[
  {
    "path": ".ameba.yml",
    "content": "# This configuration file was generated by `ameba --gen-config`\n# on 2025-06-02 13:57:07 UTC using Ameba version 1.7.0-dev.\n# The point is for the user to remove these configuration records\n# one by one as the reported problems are removed from the code base.\n#\n# For more details on any individual rule, run `ameba --only RuleName`.\n\n# Indicators for comment annotations:\n#\n# * `Disabled`: The rule is disabled because it does not seem useful for this repo (or in general).\n# * `BUG`: A bug in ameba prevents using this rule either entirely or for specific files.\n# * `FIXME`: The rule seems useful, but requires some effort to resolve. That's deferred for later.\n# * `TODO`: The rule might be useful, but we need to investigate whether we want to use it or not.\n\nVersion: \"1.7.0-dev\"\n\n# Documentation\n# =========================\n\n# Disabled: What's the point in alerting about existing TODOs in code?\nDocumentation/DocumentationAdmonition:\n  Enabled: false\n\n# Lint\n# =========================\n\nLint/DebugCalls:\n  Excluded:\n  # Samples may legitimately use DebugCalls\n  - samples/**/*\n  # Explicit tests\n  - spec/std/pp_spec.cr\n\nLint/DebuggerStatement:\n  Excluded:\n  # Explicit tests\n  - spec/debug/**/*\n\n# Disabled: `else nil` can be useful to explicitly show the consequence of the else branch\nLint/ElseNil:\n  Enabled: false\n\n# Disabled: We have an explicit CI job for testing the formatter (both latest\n# and head). No reason to run it through ameba.\nLint/Formatting:\n  Enabled: false\n\nLint/LiteralInCondition:\n  Excluded:\n  # Samples may legitimately use literals in conditions\n  - samples/**/*\n\nLint/LiteralInInterpolation:\n  Excluded:\n  - spec/std/random_spec.cr # BUG: https://github.com/crystal-ameba/ameba/issues/611\n\nLint/LiteralsComparison:\n  Excluded:\n  # Explicit tests for case equality on literals\n  - spec/std/{array,char,int,number,regex,string,symbol,tuple,uint}_spec.cr\n\n# TODO: Investigate if some `not_nil!` calls can be avoided.\nLint/NotNil:\n  Enabled: false\n\n# BUG: https://github.com/crystal-ameba/ameba/issues/605\nLint/SpecFocus:\n  Enabled: false\n\nLint/RandZero:\n  Excluded:\n  # Explicit tests\n  - spec/std/random_spec.cr\n\n# FIXME: Resolve shadowing.\nLint/ShadowingOuterLocalVar:\n  Enabled: false\n\nLint/SpecFilename:\n  Excluded:\n  - spec/compiler/data/**/*\n  - spec/compiler/lexer/lexer_objects/strings.cr\n  - spec/debug/**/*\n  - spec/llvm-ir/**/*\n  - spec/std/float_printer/ryu_printf_test_cases.cr\n\n# BUG: https://github.com/crystal-ameba/ameba/issues/612\nLint/TopLevelOperatorDefinition:\n  Enabled: false\n\n# Disabled: We have an explicit CI job for `typos`. No reason to run it through\n# ameba.\nLint/Typos:\n  Enabled: false\n\n# TODO: Investigate unused arguments.\nLint/UnusedArgument:\n  Enabled: false\n\n# TODO: Investigate unused block arguments.\nLint/UnusedBlockArgument:\n  Enabled: false\n\nLint/UselessAssign:\n  # BUG: https://github.com/crystal-ameba/ameba/issues/447\n  # This setting is to avoid false positives from the common use of type\n  # declarations in macro arguments.\n  ExcludeTypeDeclarations: true\n  Excluded:\n    - spec/debug/**/*\n    - samples/mt_gc_test.cr\n    # BUG: https://github.com/crystal-ameba/ameba/issues/624\n    - spec/std/concurrent/select_spec.cr\n    # Generators assign top-level variables to pass to ECR templates\n    - scripts/generate_*\n    # BUG: https://github.com/crystal-ameba/ameba/issues/623\n    - scripts/github-changelog.cr\n\n# Metrics\n# =========================\n\n# Disabled: Lot's of violations. Complexity is very individual.\nMetrics/CyclomaticComplexity:\n  Enabled: false\n\n# Naming\n# =========================\n# All disabled. There are many violations and some of the rules are questionable.\n# TODO: Consider enabling some of these rules.\n\nNaming/AccessorMethodName:\n  Enabled: false\n\nNaming/BinaryOperatorParameterName:\n  Enabled: false\n\nNaming/BlockParameterName:\n  Enabled: false\n\n# Disabled: All violations follow the spelling of identifiers in upstream\n# projects, e.g. for lib bindings.\nNaming/ConstantNames:\n  Enabled: false\n\nNaming/MethodNames:\n  Enabled: false\n\nNaming/PredicateName:\n  Enabled: false\n\nNaming/QueryBoolMethods:\n  Enabled: false\n\nNaming/RescuedExceptionsVariableName:\n  Enabled: false\n\nNaming/TypeNames:\n  Enabled: false\n\nNaming/VariableNames:\n  Enabled: false\n\n# Performance\n# =========================\n\nPerformance/AnyInsteadOfEmpty:\n  Excluded:\n  # These specs explicitly test `#any?` implementations\n  - spec/std/bit_array_spec.cr\n  - spec/std/enumerable_spec.cr\n  - spec/std/hash_spec.cr\n\n# Style\n# =========================\n# All disabled. There are many violations and some of the rules are questionable.\n# TODO: Consider enabling some of these rules.\n\nStyle/HeredocEscape:\n  Enabled: false\n\nStyle/HeredocIndent:\n  Enabled: false\n\nStyle/MultilineCurlyBlock:\n  Enabled: false\n\n# Disabled: This rule seems too strict when any negation inside a complex condition is\n# considered a violation. https://github.com/crystal-ameba/ameba/issues/621\nStyle/NegatedConditionsInUnless:\n  Enabled: false\n\n# BUG: https://github.com/crystal-ameba/ameba/issues/614\nStyle/ParenthesesAroundCondition:\n  Enabled: false\n\nStyle/PercentLiteralDelimiters:\n  Enabled: false\n\nStyle/RedundantNext:\n  Enabled: false\n\nStyle/RedundantReturn:\n  Enabled: false\n\nStyle/RedundantSelf:\n  Enabled: false\n\nStyle/VerboseBlock:\n  Enabled: false\n\nStyle/WhileTrue:\n  Enabled: false\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\n\nparameters:\n  distribution-scripts-repo:\n    description: \"Git url https://github.com/crystal-lang/distribution-scripts/\"\n    type: string\n    default: \"https://github.com/crystal-lang/distribution-scripts.git\"\n  distribution-scripts-version:\n    description: \"Git ref for version of https://github.com/crystal-lang/distribution-scripts/\"\n    type: string\n    default: \"4205934e01733848a559a2908a14dcb16be7f38f\"\n  previous_crystal_base_url:\n    description: \"Prefix for URLs to Crystal bootstrap compiler\"\n    type: string\n    default: \"https://github.com/crystal-lang/crystal/releases/download/1.19.1/crystal-1.19.1-1\"\n\ndefaults:\n  environment: &env\n    TRAVIS_BRANCH: $CIRCLE_BRANCH\n    TRAVIS_PULL_REQUEST: $CI_PULL_REQUEST || \"false\"\n  steps: &ci_steps\n    - checkout\n    - run: bin/ci prepare_system\n    - run: echo 'export CURRENT_TAG=\"$CIRCLE_TAG\"' >> $BASH_ENV\n    - run: bin/ci prepare_build\n    - run:\n        command: bin/ci build\n        no_output_timeout: 30m\n    - run:\n        when: always\n        command: |\n          mkdir -p ~/test-results/spec\n          cp .junit/*.xml ~/test-results/spec/\n    - store_test_results:\n        path: ~/test-results\n    - store_artifacts:\n        path: ~/test-results/spec\n\njobs:\n  test_linux:\n    machine:\n      image: default\n    environment:\n      <<: *env\n      TRAVIS_OS_NAME: linux\n      ARCH: x86_64\n      ARCH_CMD: linux64\n    resource_class: large\n    steps:\n      - checkout\n      - run: bin/ci prepare_system\n      - run: echo 'export CURRENT_TAG=\"$CIRCLE_TAG\"' >> $BASH_ENV\n      - run: bin/ci prepare_build\n      - run:\n          command: bin/ci build\n          no_output_timeout: 30m\n      - run:\n          when: always\n          command: |\n            mkdir -p ~/test-results/spec\n            cp .junit/*.xml ~/test-results/spec/\n      - store_test_results:\n          path: ~/test-results\n      - store_artifacts:\n          path: ~/test-results/spec\n      - persist_to_workspace:\n          root: .\n          paths:\n            - docs\n\n  test_alpine:\n    machine:\n      image: default\n    environment:\n      <<: *env\n      TRAVIS_OS_NAME: linux\n      ARCH: x86_64-musl\n      ARCH_CMD: linux64\n    resource_class: large\n    steps: *ci_steps\n\n  test_darwin:\n    macos:\n      xcode: 16.4.0\n    environment:\n      <<: *env\n      TRAVIS_OS_NAME: osx\n      LLVM_CONFIG: /usr/local/opt/llvm/bin/llvm-config\n    steps:\n      - restore_cache:\n          keys:\n            - brew-cache-v1\n      - checkout\n      - run: bin/ci prepare_system\n      - run: echo 'export PKG_CONFIG_PATH=\"$PKG_CONFIG_PATH:/usr/local/opt/openssl@1.1/lib/pkgconfig\"' >> $BASH_ENV\n      - run: echo 'export CURRENT_TAG=\"$CIRCLE_TAG\"' >> $BASH_ENV\n      - run: bin/ci prepare_build\n      - run:\n          command: bin/ci build\n          no_output_timeout: 30m\n      - run:\n          when: always\n          command: |\n            mkdir -p ~/test-results/spec\n            cp .junit/*.xml ~/test-results/spec/\n      - store_test_results:\n          path: ~/test-results\n      - store_artifacts:\n          path: ~/test-results/spec\n      - save_cache:\n          key: brew-cache-v1\n          paths:\n            - /usr/local/Homebrew\n            - ~/Library/Caches/Homebrew/downloads\n\n  test_preview_mt:\n    machine:\n      image: default\n    resource_class: large\n    environment:\n      <<: *env\n      TRAVIS_OS_NAME: linux\n      ARCH: x86_64\n      ARCH_CMD: linux64\n    steps:\n      - checkout\n      - run: bin/ci prepare_system\n      - run: echo 'export CURRENT_TAG=\"$CIRCLE_TAG\"' >> $BASH_ENV\n      - run: bin/ci prepare_build\n      - run: bin/ci with_build_env 'make crystal'\n      - run:\n          command: bin/ci with_build_env 'CRYSTAL_WORKERS=4 make std_spec threads=1 FLAGS=\"-D preview_mt\" junit_output=.junit/std_spec.xml'\n          no_output_timeout: 30m\n      - run:\n          when: always\n          command: |\n            mkdir -p ~/test-results/spec\n            cp .junit/*.xml ~/test-results/spec/\n      - store_test_results:\n          path: ~/test-results\n      - store_artifacts:\n          path: ~/test-results/spec\n\n  prepare_common:\n    docker:\n      - image: docker:stable-git\n    steps:\n      # checkout specific distribution-scripts version to perform releases and nightly\n      - run: |\n          git clone << pipeline.parameters.distribution-scripts-repo >> ~/distribution-scripts\n          cd ~/distribution-scripts\n          git checkout << pipeline.parameters.distribution-scripts-version >>\n      # persist relevant information for build process\n      - run: |\n          cd ~/distribution-scripts\n          touch build.env\n          echo \"export DOCKER_REPOSITORY=crystallang/crystal\" >> build.env\n\n          # What to build\n          echo \"export CRYSTAL_SHA1=$CIRCLE_SHA1\" >> build.env\n\n          # Which previous version use\n          echo \"export PREVIOUS_CRYSTAL_RELEASE_DARWIN_TARGZ=<< pipeline.parameters.previous_crystal_base_url >>-darwin-universal.tar.gz\" >> build.env\n\n          cat build.env\n      - persist_to_workspace:\n          root: ../\n          paths:\n            - distribution-scripts\n\n  # prepare build for tagged releases\n  prepare_tagged:\n    docker:\n      - image: docker:stable-git\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run: |\n          cd /tmp/workspace/distribution-scripts\n\n          # How to brand it\n          echo \"export CRYSTAL_VERSION=$CIRCLE_TAG\" >> build.env\n          echo \"export DOCKER_TAG=$CIRCLE_TAG\" >> build.env\n\n          # Snapcraft configuration\n          echo \"export SNAP_GRADE=stable\" >> build.env\n          echo \"export SNAP_CHANNEL=edge\" >> build.env\n\n          cat build.env\n      - persist_to_workspace:\n          root: /tmp/workspace\n          paths:\n            - distribution-scripts\n\n  # prepare build for nightly releases\n  prepare_nightly:\n    docker:\n      - image: docker:stable-git\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - checkout\n      - run: |\n          # We need CRYSTAL_VERSION in prepare_nightly to use src/VERSION so we publish them as x.y.z-dev in apt/rpm\n          #\n          # How to brand it\n          echo \"export CRYSTAL_VERSION=$(cat src/VERSION)\" >> /tmp/workspace/distribution-scripts/build.env\n          #\n          # TODO: We might want to do that on docker images also to support updates on multiple development versions the same date.\n      - run: |\n          cd /tmp/workspace/distribution-scripts\n\n          echo \"export DOCKER_TAG=nightly\" >> build.env\n\n          # Build from working directory (needed for omnibus and when version does not match branch/tag)\n          echo \"export FORCE_GIT_TAGGED=0\" >> build.env\n\n          # Snapcraft configuration\n          echo \"export SNAP_GRADE=devel\" >> build.env\n          echo \"export SNAP_CHANNEL=edge\" >> build.env\n\n          cat build.env\n      - persist_to_workspace:\n          root: /tmp/workspace\n          paths:\n            - distribution-scripts\n\n  # prepare build for manual triggered releases like ci branches, maintenance releases, etc.\n  prepare_maintenance:\n    docker:\n      - image: docker:stable-git\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run: |\n          cd /tmp/workspace/distribution-scripts\n\n          # The version is based on the branch name.\n          VERSION=$CIRCLE_BRANCH\n\n          # We need to sanitize it because there are restrictions on some places\n          # where the version is use (Mac pkg names, snap branch).\n          VERSION=${VERSION/release\\//}\n          VERSION=${VERSION//_/-}\n          VERSION=${VERSION//\\//-}-dev\n\n          export VERSION\n          echo \"export CRYSTAL_VERSION=$VERSION\" >> build.env\n          echo \"export DOCKER_TAG=$VERSION\" >> build.env\n\n          # Build from working directory (needed for omnibus and when version does not match branch/tag)\n          echo \"export FORCE_GIT_TAGGED=0\" >> build.env\n\n          # Snapcraft configuration\n          echo \"export SNAP_GRADE=devel\" >> build.env\n          echo \"export SNAP_CHANNEL=edge/$VERSION\" >> build.env\n\n          cat build.env\n      - persist_to_workspace:\n          root: /tmp/workspace\n          paths:\n            - distribution-scripts\n\n  dist_linux:\n    parameters:\n      arch:\n        type: string\n    machine:\n      image: default\n    resource_class: << parameters.arch >>\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run:\n          no_output_timeout: 20m\n          command: |\n            cd /tmp/workspace/distribution-scripts\n            source build.env\n            export PREVIOUS_CRYSTAL_RELEASE_LINUX64_TARGZ=\"<< pipeline.parameters.previous_crystal_base_url >>-linux-$(uname -m).tar.gz\"\n            cd linux\n            make all64 release=true\n      - store_artifacts:\n          path: /tmp/workspace/distribution-scripts/linux/build\n          destination: build\n      - persist_to_workspace:\n          root: /tmp/workspace/distribution-scripts/linux/\n          paths:\n            - build\n\n  dist_darwin:\n    macos:\n      xcode: 16.4.0\n    shell: /bin/bash --login -eo pipefail\n    steps:\n      - restore_cache:\n          keys:\n            - brew-cache-v1\n      - run:\n          name: Setup environment\n          command: |\n            brew unlink python@2 || true\n\n            brew install ruby@3 libffi pkgconfig libtool automake\n\n            sudo mkdir -p /opt/crystal\n            sudo chown $(whoami) /opt/crystal/\n            sudo mkdir -p /var/cache\n            sudo chown $(whoami) /var/cache\n      - attach_workspace:\n          at: /tmp/workspace\n      - run:\n          no_output_timeout: 40m\n          command: |\n            cd /tmp/workspace/distribution-scripts\n            source build.env\n            cd omnibus\n            ruby --version\n            gem install bundler -v \"$(grep -A 1 \"BUNDLED WITH\" Gemfile.lock | tail -n 1)\"\n            bundle check || bundle install --binstubs\n            cd ../darwin\n            make\n      - store_artifacts:\n          path: /tmp/workspace/distribution-scripts/darwin/build\n          destination: build\n      - persist_to_workspace:\n          root: /tmp/workspace/distribution-scripts/darwin/\n          paths:\n            - build\n\n  push_obs_nightly:\n    docker:\n      - image: crystallang/osc\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run:\n          command: |\n            cd /tmp/workspace/distribution-scripts\n            source build.env\n\n            packages/obs-setup.sh\n            packages/obs-push.sh devel:languages:crystal:nightly ${CRYSTAL_VERSION%-*} $(date '+%Y%m%d') $CRYSTAL_SHA1 \\\n                /tmp/workspace/build/crystal-*-linux-x86_64.tar.gz \\\n                /tmp/workspace/build/crystal-*-docs.tar.gz\n\n  dist_docker:\n    machine:\n      image: default\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run:\n          name: Enable buildx for multiarch builds\n          command: docker buildx create --use\n      - run:\n          name: Build and push multiarch images\n          command: |\n            cd /tmp/workspace/distribution-scripts\n            source build.env\n            cd docker\n            docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}\n            make all TARBALLS=/tmp/workspace/build/ VERSION=${CRYSTAL_VERSION} PUSH=1\n      - run:\n          name: Smoke tests\n          command: |\n            cd /tmp/workspace/distribution-scripts\n            source build.env\n            cd docker\n            make smoke-all VERSION=${CRYSTAL_VERSION}\n\n  dist_snap:\n    machine:\n      image: \"ubuntu-2204:current\"\n    environment:\n      SNAPCRAFT_BUILD_ENVIRONMENT: host\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run:\n          command: |\n            sudo apt-get update\n            sudo apt-get install -y make snapd\n            sudo snap install snapcraft --classic\n            cd /tmp/workspace/distribution-scripts\n            source build.env\n            cd snapcraft/\n            sudo -E make ARCH=amd64 CRYSTAL_TARBALL=/tmp/workspace/build/crystal-$CRYSTAL_VERSION-1-linux-x86_64.tar.gz  GRADE=$SNAP_GRADE\n            sudo -E make ARCH=arm64 CRYSTAL_TARBALL=/tmp/workspace/build/crystal-$CRYSTAL_VERSION-1-linux-aarch64.tar.gz GRADE=$SNAP_GRADE\n            sudo -E make push CHANNEL=$SNAP_CHANNEL\n\n  dist_docs:\n    machine:\n      image: default\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run: |\n          cd /tmp/workspace/distribution-scripts\n          source build.env\n          cd docs\n          make CRYSTAL_DOCKER_IMAGE=${DOCKER_REPOSITORY}:${DOCKER_TAG}-build\n      - store_artifacts:\n          path: /tmp/workspace/distribution-scripts/docs/build\n          destination: build\n      - persist_to_workspace:\n          root: /tmp/workspace/distribution-scripts/docs/\n          paths:\n            - build\n\n  publish_nightly_artifacts:\n    docker:\n      - image: manastech/s3cmd:2.2-alpine\n    environment:\n      <<: *env\n      AWS_ACCESS_KEY_ID: AKIA2EEIIRCJDEDGK6MQ\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - run: |\n          mkdir -p /tmp/upload\n          cd /tmp/workspace/build\n          cp crystal-*-darwin-universal.tar.gz /tmp/upload/crystal-nightly-darwin-universal.tar.gz\n          cp crystal-*-linux-x86_64.tar.gz /tmp/upload/crystal-nightly-linux-x86_64.tar.gz\n          cp crystal-*-linux-aarch64.tar.gz /tmp/upload/crystal-nightly-linux-aarch64.tar.gz\n      - run: s3cmd put --recursive /tmp/upload/* s3://artifacts.crystal-lang.org/dist/\n\n  dist_artifacts:\n    docker:\n      - image: buildpack-deps:xenial\n    steps:\n      - attach_workspace:\n          at: /tmp/workspace\n      - store_artifacts:\n          path: /tmp/workspace/build\n          destination: dist_packages\n\nworkflows:\n  version: 2\n  release:\n    jobs:\n      - test_linux:\n          filters: &release\n            branches:\n              only:\n                - /release\\/.+/\n                - /.*\\bci\\b.*/\n            tags:\n              only: /.*/\n      - test_alpine:\n          filters: *release\n      # - test_darwin: # See https://github.com/crystal-lang/crystal/pull/9763\n      #     filters: *release\n      - test_preview_mt:\n          filters: *release\n      - prepare_common:\n          filters: *release\n      - prepare_maintenance:\n          filters: &maintenance\n            branches:\n              only:\n                - /release\\/.+/\n                - /.*\\bci\\b.*/\n          requires:\n            - prepare_common\n      - prepare_tagged:\n          filters: &tagged\n            branches:\n              ignore: /.*/\n            tags:\n              only: /.*/\n          requires:\n            - prepare_common\n      - dist_linux:\n          matrix:\n            parameters:\n              arch: [large, arm.medium]\n          filters: *release\n          requires:\n            - prepare_maintenance\n            - prepare_tagged\n      - dist_darwin:\n          filters: *release\n          requires:\n            - prepare_maintenance\n            - prepare_tagged\n      - dist_docker:\n          filters: *release\n          requires:\n            - dist_linux\n      - dist_snap:\n          filters: *release\n          requires:\n            - dist_linux\n      - dist_docs:\n          filters: *release\n          requires:\n            - dist_docker\n      - dist_artifacts:\n          filters: *release\n          requires:\n            - dist_linux\n            - dist_darwin\n            - dist_docs\n\n  nightly_release:\n    triggers:\n      - schedule:\n          cron: \"0 0 * * *\"\n          filters:\n            branches:\n              only:\n                - master\n    jobs:\n      - test_linux\n      - test_alpine\n      # - test_darwin # See https://github.com/crystal-lang/crystal/pull/9763\n      - test_preview_mt\n      - prepare_common\n      - prepare_nightly:\n          requires:\n            - prepare_common\n      - dist_linux:\n          matrix:\n            parameters:\n              arch: [large, arm.medium]\n          requires:\n            - prepare_nightly\n      - dist_darwin:\n          requires:\n            - prepare_nightly\n      - push_obs_nightly:\n          requires:\n            - dist_linux\n            - dist_docs\n      - dist_docker:\n          requires:\n            - dist_linux\n      - dist_snap:\n          requires:\n            - dist_linux\n      - dist_docs:\n          requires:\n            - dist_docker\n      - publish_nightly_artifacts:\n          requires:\n            - dist_linux\n            - dist_darwin\n      - dist_artifacts:\n          requires:\n            - dist_linux\n            - dist_darwin\n            - dist_docs\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*.{cr,ecr}]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".gitattributes",
    "content": "## Sources\n\n*.cr text eol=lf\n\n## Sourced-in dependencies\n\nlib/** linguist-vendored\n\n## Generated files\n\n# produced by scripts/generate_windows_zone_names.cr\nsrc/crystal/system/win32/zone_names.cr linguist-generated\n# produced by scripts/generate_object_properties.cr\nsrc/object/properties.cr\n# produced by scripts/generate_html_entities.cr\nsrc/html/entities.cr linguist-generated\n# produced by scripts/generate_ssl_server_defaults.cr\nsrc/openssl/ssl/defaults.cr linguist-generated\n# produced by scripts/generate_grapheme_properties.cr\nsrc/string/grapheme/properties.cr linguist-generated\n# produced by scripts/generate_unicode_data.cr\nsrc/unicode/data.cr linguist-generated\n# produced by scripts/generate_glob_specs.cr\nspec/std/file/match-fast-glob_spec.cr\n# produced by scripts/generate_grapheme_break_specs.cr\nspec/std/string/grapheme_break_spec.cr linguist-generated\n# produced by spec/generate_wasm32_spec.sh\nspec/wasm32_std_spec.cr linguist-generated\n\n# devenv\n.devenv.flake.nix -merge linguist-generated\ndevenv.lock -merge linguist-generated\n\n## Syntax highlighting\n\nMakefile.win linguist-language=makefile\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: \"\\U0001F41B Bug Report\"\nabout: I want to report a bug.\nlabels: kind:bug\ntype: Bug\n---\n\n# Bug Report\n\nMake sure to review these points before submitting issues - thank you!\n\n- Make sure a similar issue does not exist yet: use the search box, search engine and look at [Stack Overflow](https://stackoverflow.com/questions/tagged/crystal-lang).\n- **Include reproducible code**: we should be able to paste it into an editor, compile and run it and get the same error as you. Otherwise it's impossible for us to reproduce the bug.\n- Don't **only** use `play.crystal-lang.org` or `carc.in`: code might be lost and the issue will remain incomplete. Write code in the issue itself.\n- Reduce code, if possible, to the minimum size that reproduces the bug.\n- If all of the above is impossible due to a large project, create a branch that reproduces the bug and point us to it.\n- Include Crystal compiler version (`crystal -v`) and OS. If possible, try to see if the bug still reproduces on master.\n\n---\n\nAdd a :+1: [reaction] to [issues you find important].\n\n[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/\n[issues you find important]:\n    https://github.com/crystal-lang/crystal/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: \"\\U00002753 Crystal Community Forum\"\n    url: https://forum.crystal-lang.org/c/help-support\n    about: Questions about installing, using Crystal and any related issues.\n  - name: \"\\U0001F4AC Crystal Community Chat\"\n    url: https://gitter.im/crystal-lang/crystal\n    about: \"Get in touch with the community, ask for help and talk about Crystal. (IRC: #crystal-lang on libera.chat)\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/discussion.md",
    "content": "---\nname: \"\\U0001F914 Language Improvement Discussion\"\nabout: I want to start a new discussion about improving the language.\nlabels: status:discussion\n---\n\n# Discussion\n\n- What aspect of the language would you like to see improved?\n- What are the reasons?\n- Include practical examples to illustrate your points.\n- Optionally add one (or more) proposals to improve the current situation.\n\n---\n\nAdd a :+1: [reaction] to [issues you find important].\n\n[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/\n[issues you find important]:\n    https://github.com/crystal-lang/crystal/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: \"\\U0001F680 Feature Request\"\nabout: I want to propose a new language feature.\nlabels: kind:feature\ntype: Feature\n---\n\n# Feature Request\n\n- Is your feature request related to a problem? Please describe clearly and concisely what is it.\n- Describe the feature you would like, optionally illustrated by examples, and how it will solve the above problem.\n- Describe considered alternative solutions, and the reasons why you have not proposed them as a solution here.\n- Does it break backward compatibility, if yes then what's the migration path?\n\nIn case this proposal includes a substantial change to the language, we ask you to go through an [RFC process](https://github.com/crystal-lang/rfcs).\n\nThe best place to start an open discussion about potential changes is the [Crystal forum](https://forum.crystal-lang.org/c/crystal-contrib/6).\n\n---\n\nAdd a :+1: [reaction] to [issues you find important].\n\n[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/\n[issues you find important]: https://github.com/crystal-lang/crystal/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/pull_request_template.md",
    "content": "# Pull Request\n\nWe thank you for helping improve Crystal. In order to ease the reviewing process, we invite you to read the [guidelines](https://github.com/crystal-lang/crystal/blob/master/CONTRIBUTING.md#making-good-pull-requests) and ask you to consider the following points before submitting a PR:\n\n1. We prefer to discuss the underlying issue _prior_ to discussing the code. Therefore, we kindly ask you to refer to an existing issue, or an existing discussion in a public space with members of the Core Team (forum, Gitter, Discord, ...). In few cases, we acknowledge that this might not be necessary, for instance when refactoring code or small bug fixes. In this case, the PR must include the same information an issue would have: a clear explanation of the issue, reproducible code, etc.\n\n2. Focus the PR to the referred issue, and restraint from adding unrelated changes/additions. We do welcome another PR if you fixed another issue.\n\n3. If your change is big, consider breaking it into several smaller PRs. In general, the smaller the change, the quicker we can review it.\n\n4. ⚠️ Please do not amend previous commits and force push to the PR branch. This makes reviews much harder because reference to previous state is hidden.\n\n---\n\nAdd a :+1: [reaction] to [pull requests you find important].\n\n[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/\n[pull requests you find important]:\n    https://github.com/crystal-lang/crystal/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc\n"
  },
  {
    "path": ".github/actionlint.yaml",
    "content": "self-hosted-runner:\n  # Labels of self-hosted runner in array of strings.\n  labels:\n    - runs-on\n    - runner=*\n    - family=*\n    - ram=*\n    - windows-11-arm # actionlint doesn't understand this label yet\n\n# Configuration variables in array of strings defined in your repository or\n# organization. `null` means disabling configuration variables check.\n# Empty array means no configuration variable is allowed.\nconfig-variables: null\n\n# Configuration for file paths. The keys are glob patterns to match to file\n# paths relative to the repository root. The values are the configurations for\n# the file paths. Note that the path separator is always '/'.\n# The following configurations are available.\n#\n# \"ignore\" is an array of regular expression patterns. Matched error messages\n# are ignored. This is similar to the \"-ignore\" command line option.\npaths:\n#  .github/workflows/**/*.yml:\n#    ignore: []\n"
  },
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:recommended\"\n  ],\n  \"separateMajorMinor\": false,\n  \"packageRules\": [\n    {\n      \"matchDatasources\": [\n        \"docker\"\n      ],\n      \"enabled\": false\n    },\n    {\n      \"groupName\": \"GH Actions\",\n      \"matchManagers\": [\n        \"github-actions\"\n      ],\n      \"schedule\": [\n        \"after 5am and before 8am on Wednesday\"\n      ]\n    }\n  ],\n  \"labels\": [\n    \"topic:infrastructure/ci\"\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/aarch64.yml",
    "content": "name: AArch64 CI\n\non: [push, pull_request]\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\njobs:\n  aarch64-musl-build:\n    runs-on: [runs-on, runner=2cpu-linux-arm64, \"run-id=${{ github.run_id }}\"]\n    if: github.repository == 'crystal-lang/crystal'\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Build Crystal\n        uses: docker://crystallang/crystal:1.13.2-alpine-84codes-build\n        with:\n          args: make crystal\n      - name: Upload Crystal executable\n        uses: actions/upload-artifact@v7\n        with:\n          name: crystal-aarch64-musl\n          path: |\n            .build/crystal\n            src/llvm/ext/llvm_ext.o\n  aarch64-musl-test-stdlib:\n    needs: aarch64-musl-build\n    runs-on: [runs-on, runner=4cpu-linux-arm64, \"family=m7g\", ram=16, \"run-id=${{ github.run_id }}\"]\n    if: github.repository == 'crystal-lang/crystal'\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal-aarch64-musl\n      - name: Mark downloaded compiler as executable\n        run: chmod +x .build/crystal\n      - name: Run stdlib specs\n        uses: docker://crystallang/crystal:1.13.2-alpine-84codes-build\n        with:\n          args: make std_spec\n  aarch64-musl-test-compiler:\n    needs: aarch64-musl-build\n    runs-on: [runs-on, runner=4cpu-linux-arm64, \"family=m7g\", ram=16, \"run-id=${{ github.run_id }}\"]\n    if: github.repository == 'crystal-lang/crystal'\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal-aarch64-musl\n      - name: Mark downloaded compiler as executable\n        run: chmod +x .build/crystal\n      - name: Run compiler specs\n        uses: docker://crystallang/crystal:1.13.2-alpine-84codes-build\n        with:\n          args: make primitives_spec compiler_spec FLAGS=-Dwithout_ffi\n  aarch64-gnu-build:\n    runs-on: [runs-on, runner=2cpu-linux-arm64, \"run-id=${{ github.run_id }}\"]\n    if: github.repository == 'crystal-lang/crystal'\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Build Crystal\n        uses: docker://crystallang/crystal:1.13.2-ubuntu-84codes-build\n        with:\n          args: make crystal\n      - name: Upload Crystal executable\n        uses: actions/upload-artifact@v7\n        with:\n          name: crystal-aarch64-gnu\n          path: |\n            .build/crystal\n            src/llvm/ext/llvm_ext.o\n  aarch64-gnu-test-stdlib:\n    needs: aarch64-gnu-build\n    runs-on: [runs-on, runner=4cpu-linux-arm64, \"family=m7g\", ram=16, \"run-id=${{ github.run_id }}\"]\n    if: github.repository == 'crystal-lang/crystal'\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal-aarch64-gnu\n      - name: Mark downloaded compiler as executable\n        run: chmod +x .build/crystal\n      - name: Run stdlib specs\n        uses: docker://crystallang/crystal:1.13.2-ubuntu-84codes-build\n        with:\n          args: make std_spec\n  aarch64-gnu-test-compiler:\n    needs: aarch64-gnu-build\n    runs-on: [runs-on, runner=4cpu-linux-arm64, \"family=m7g\", ram=16, \"run-id=${{ github.run_id }}\"]\n    if: github.repository == 'crystal-lang/crystal'\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal-aarch64-gnu\n      - name: Mark downloaded compiler as executable\n        run: chmod +x .build/crystal\n      - name: Run compiler specs\n        uses: docker://crystallang/crystal:1.13.2-ubuntu-84codes-build\n        with:\n          args: make primitives_spec compiler_spec\n"
  },
  {
    "path": ".github/workflows/backport.yml",
    "content": "# WARNING:\n# When extending this action, be aware that $GITHUB_TOKEN allows write access to\n# the GitHub repository. This means that it should not evaluate user input in a\n# way that allows code injection.\n\nname: Backport\n\non:\n  pull_request_target:\n    types: [closed, labeled]\n    branches: [master, release/*]\n\npermissions:\n  contents: write # so it can comment\n  pull-requests: write # so it can create pull requests\n\njobs:\n  backport:\n    name: Backport Pull Request\n    if: github.repository_owner == 'crystal-lang' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}\n          token: ${{ secrets.BACKPORT_ACTION_GITHUB_PAT }}\n          persist-credentials: false\n\n      - name: Create backport PR\n        uses: korthout/backport-action@4aaf0e03a94ff0a619c9a511b61aeb42adea5b02 # v4.2.0\n        with:\n          github_token: ${{ secrets.BACKPORT_ACTION_GITHUB_PAT }}\n          # Config README: https://github.com/korthout/backport-action#backport-action\n          copy_labels_pattern: '^(breaking-change|security|topic:.*|kind:.*|platform:.*)$'\n          copy_milestone: true\n          pull_description: |-\n            Automated backport of #${pull_number} to `${target_branch}`, triggered by a label.\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Docs\n\non:\n  push:\n    branches:\n      - master\n\npermissions: {}\n\nenv:\n  TRAVIS_OS_NAME: linux\n\njobs:\n  deploy_api_docs:\n    if: github.repository_owner == 'crystal-lang'\n    env:\n      ARCH: x86_64\n      ARCH_CMD: linux64\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: bin/ci prepare_build\n\n      - name: Build docs\n        run: bin/ci with_build_env 'make crystal docs threads=1'\n\n      - name: Set revision\n        run: echo \"$GITHUB_SHA\" > ./docs/revision.txt\n\n      - name: Configure AWS Credentials\n        uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: us-east-1\n\n      - name: Deploy API docs to S3\n        run: |\n          aws s3 sync ./docs s3://crystal-api/api/master --delete\n"
  },
  {
    "path": ".github/workflows/forward-compatibility.yml",
    "content": "name: Forward Compatibility\n\non:\n  push:\n    paths:\n      - .github/workflows/forward-compatibility.yml\n  schedule:\n  - cron: '0 3 * * *'\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  TRAVIS_OS_NAME: linux\n  SPEC_SPLIT_DOTS: 160\n\njobs:\n  # Test against *each* supported Crystal version for forward compatibility.\n  #\n  # This workflow runs on a nightly schedule on `master`.\n  # It can also be dispatched manually when necessary.\n  #\n  # We run a basic version of this test on every commit which covers only the earliest and latest supported versions in `linux.yml#x86_64-gnu-test`.\n  x86_64-gnu-test-forward_compatibility:\n    env:\n      ARCH: x86_64\n      ARCH_CMD: linux64\n      CRYSTAL_BOOTSTRAP_VERSION: ${{ matrix.crystal_bootstrap_version }}\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        crystal_bootstrap_version: [1.7.3, 1.8.2, 1.9.2, 1.10.1, 1.11.2, 1.12.2, 1.13.3, 1.14.1, 1.15.1, 1.16.3, 1.17.1, 1.18.2]\n        env: [{}]\n        include:\n          # libffi is only available starting from the 1.2.2 build images\n          # pcre2 is only available starting from the 1.7.0 build images\n          - crystal_bootstrap_version: 1.1.1\n            env:\n              FLAGS: -Dwithout_ffi\n              USE_PCRE1: true\n          - crystal_bootstrap_version: 1.2.2\n            env:\n              USE_PCRE1: true\n          - crystal_bootstrap_version: 1.3.2\n            env:\n              USE_PCRE1: true\n          - crystal_bootstrap_version: 1.4.1\n            env:\n              USE_PCRE1: true\n          - crystal_bootstrap_version: 1.5.1\n            env:\n              USE_PCRE1: true\n          - crystal_bootstrap_version: 1.6.2\n            env:\n              USE_PCRE1: true\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: bin/ci prepare_build\n\n      - name: Test\n        run: bin/ci build\n        env: ${{ matrix.env }}\n"
  },
  {
    "path": ".github/workflows/interpreter.yml",
    "content": "name: Interpreter Test\n\non: [push, pull_request]\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  SPEC_SPLIT_DOTS: 160\n\njobs:\n  test-interpreter_spec:\n    runs-on: ubuntu-24.04\n    container:\n      image: crystallang/crystal:1.19.1-build\n    name: \"Test Interpreter\"\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Test interpreter_spec\n        run: make interpreter_spec junit_output=.junit/interpreter_spec.xml\n\n  build-interpreter:\n    runs-on: ubuntu-24.04\n    container:\n      image: crystallang/crystal:1.19.1-build\n    name: Build interpreter\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Build compiler\n        run: make interpreter=1 release=1\n\n      - name: Upload compiler artifact\n        uses: actions/upload-artifact@v7\n        with:\n          name: crystal-interpreter\n          path: |\n            .build/crystal\n\n  test-interpreter-std_spec:\n    needs: build-interpreter\n    runs-on: ubuntu-24.04\n    container:\n      image: crystallang/crystal:1.19.1-build\n    strategy:\n      fail-fast: false\n      matrix:\n        part: [0, 1, 2, 3]\n    name: \"Test std_spec with interpreter (${{ matrix.part }})\"\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Download compiler artifact\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal-interpreter\n          path: .build/\n\n      - name: Mark downloaded compiler as executable\n        run: chmod +x .build/crystal\n\n      - name: Run std_spec with interpreter\n        run: SPEC_SPLIT=\"${{ matrix.part }}%4\" bin/crystal i spec/std_spec.cr -- --junit_output .junit/interpreter-std_spec.${{ matrix.part }}.xml\n\n  test-interpreter-primitives_spec:\n    needs: build-interpreter\n    runs-on: ubuntu-24.04\n    container:\n      image: crystallang/crystal:1.19.1-build\n    name: \"Test primitives_spec with interpreter\"\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Download compiler artifact\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal-interpreter\n          path: .build/\n\n      - name: Mark downloaded compiler as executable\n        run: chmod +x .build/crystal\n\n      - name: Run primitives_spec with interpreter\n        run: bin/crystal i spec/primitives_spec.cr -- --junit_output .junit/interpreter-primitives_spec.xml\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\npermissions: {}\n\njobs:\n  prek:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download source\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n          fetch-depth: 0\n      - uses: cachix/install-nix-action@1ca7d21a94afc7c957383a2d217460d980de4934 # v31\n        with:\n          nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/e99215c1a21cab2a995a3d01fe20d5714addea27.tar.gz # nixpkgs-unstable@2026-03-06 (with devenv 2.0)\n      - uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: devenv\n      - name: Install devenv.sh\n        run: nix profile install nixpkgs#devenv\n      - name: \"Download target branch: ${{ github.base_ref || 'master' }}\"\n        run: git fetch origin \"${TARGET_BRANCH}\"\n      - name: Check whether to run full test on all files\n        id: test_full_run\n        if: ${{ github.event_name != 'workflow_dispatch' }}\n        run: |\n          # Trigger full run if devenv.lock or this workflow file changed\n          if git diff --quiet \"origin/${TARGET_BRANCH}\" HEAD -- devenv.lock .github/workflows/lint.yml; then\n            echo \"test_full_run=false\" >> \"$GITHUB_OUTPUT\"\n          else\n            echo \"test_full_run=true\" >> \"$GITHUB_OUTPUT\"\n          fi\n      - name: Run prek on all files\n        id: run_full\n        run: prek run --all-files\n        shell: devenv --profile lint shell bash -- -e {0}\n        if: ${{ github.event_name == 'workflow_dispatch' || steps.test_full_run.outputs.test_full_run == 'true' }}\n      - name: Run prek hooks for changes against target branch (${{ github.base_ref || 'master' }})\n        run: prek run --from-ref \"origin/${TARGET_BRANCH}\" --to-ref HEAD\n        shell: devenv --profile lint shell bash -- -e {0}\n        if: \"${{ steps.run_full.outcome == 'skipped' }}\"\n    env:\n      TARGET_BRANCH: \"${{ github.base_ref || 'master' }}\"\n"
  },
  {
    "path": ".github/workflows/linux.yml",
    "content": "name: Linux CI\n\non: [push, pull_request]\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  TRAVIS_OS_NAME: linux\n  SPEC_SPLIT_DOTS: 160\n\njobs:\n  # Test against latest release and the earliest supported Crystal version for forward compatibility.\n  #\n  # A more advanced test which runs against *each* supported version is available in `forward-compatibility.yml` and\n  # runs regularly on a nightly schedule.\n  test:\n    env:\n      ARCH: ${{ matrix.arch }}\n      ARCH_CMD: linux64\n    runs-on: ${{ case(startsWith(matrix.arch, 'aarch64'), 'ubuntu-24.04-arm', 'ubuntu-latest') }}\n    strategy:\n      fail-fast: false\n      matrix:\n        arch:\n          - x86_64\n          - x86_64-musl\n          - aarch64\n          - aarch64-musl\n        env: [{}]\n        include:\n          - arch: x86_64\n            env:\n              CRYSTAL_BOOTSTRAP_VERSION: 1.0.0\n              # libffi is only available starting from the 1.2.2 build images\n              FLAGS: \"-Dwithout_ffi\"\n              # pcre2 is only available starting from the 1.7.0 build images\n              USE_PCRE1: true\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: bin/ci prepare_build\n\n      - name: Test\n        run: bin/ci build\n        env: ${{ matrix.env }}\n\n  test-std:\n    env:\n      ARCH: ${{ matrix.arch }}\n      ARCH_CMD: linux64\n    runs-on: ${{ case(startsWith(matrix.arch, 'aarch64'), 'ubuntu-24.04-arm', 'ubuntu-latest') }}\n    strategy:\n      fail-fast: false\n      matrix:\n        arch:\n          - x86_64\n          - aarch64\n        opts:\n          - flags: \"-Dexecution_context -Dpreview_mt\" # execution_context: concurrent\n            workers: 1\n          - flags: \"-Dexecution_context -Dpreview_mt\" # execution_context: parallel\n            workers: 4\n        include:\n          - arch: x86_64\n            opts:\n              flags: \"-Devloop=libevent\"\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: |\n          bin/ci prepare_build\n          bin/ci with_build_env crystal version\n\n      - name: Test\n        run: bin/ci with_build_env 'CRYSTAL_WORKERS=${{ matrix.opts.workers || 1 }} make std_spec threads=1 FLAGS=\"${{ matrix.opts.flags }}\"'\n\n  check_format:\n    env:\n      ARCH: x86_64\n      ARCH_CMD: linux64\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: bin/ci prepare_build\n\n      - name: Check Format\n        run: bin/ci format\n\n  lint_spellcheck:\n    name: Spell Check with Typos\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout Actions Repository\n      uses: actions/checkout@v6\n      with:\n        persist-credentials: false\n\n    - name: Spell Check Repo\n      uses: crate-ci/typos@631208b7aac2daa8b707f55e7331f9112b0e062d # v1.44.0\n      env:\n        CLICOLOR: 1\n\n  lint_ameba:\n    runs-on: ubuntu-latest\n    container: ghcr.io/crystal-ameba/ameba:sha-d861f4ed0f904e0ecdad11c26f98e968f7d95afa # 1.7.0-dev\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - run: ameba\n"
  },
  {
    "path": ".github/workflows/llvm.yml",
    "content": "name: LLVM CI\n\non:\n  push:\n    paths:\n      - 'src/compiler/crystal/codegen/**'\n      - 'src/llvm/**'\n      - 'spec/std/llvm/**'\n      - 'spec/llvm-ir/**'\n      - 'spec/compiler/codegen/**'\n      - '.github/workflows/llvm.yml'\n  pull_request:\n    paths:\n      - 'src/compiler/crystal/codegen/**'\n      - 'src/llvm/**'\n      - 'spec/std/llvm/**'\n      - 'spec/llvm-ir/**'\n      - 'spec/compiler/codegen/**'\n      - '.github/workflows/llvm.yml'\n  schedule:\n    - cron: '0 3 * * *'\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  SPEC_SPLIT_DOTS: 160\n\njobs:\n  llvm_test:\n    runs-on: ${{ matrix.runs-on }}\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - {llvm_version: 13, runs-on: ubuntu-22.04, codename: jammy}\n          - {llvm_version: 14, runs-on: ubuntu-22.04, codename: jammy}\n          - {llvm_version: 15, runs-on: ubuntu-22.04, codename: jammy}\n          - {llvm_version: 16, runs-on: ubuntu-22.04, codename: jammy}\n          - {llvm_version: 17, runs-on: ubuntu-24.04, codename: noble}\n          - {llvm_version: 18, runs-on: ubuntu-24.04, codename: noble}\n          - {llvm_version: 19, runs-on: ubuntu-24.04, codename: noble}\n          - {llvm_version: 20, runs-on: ubuntu-24.04, codename: noble}\n          - {llvm_version: 21, runs-on: ubuntu-24.04, codename: noble}\n          - {llvm_version: 22, runs-on: ubuntu-24.04, codename: noble}\n          - {runs-on: ubuntu-24.04, codename: noble}\n    name: \"LLVM ${{ matrix.llvm_version || 'Nightly' }}\"\n    steps:\n      - name: Checkout Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install LLVM ${{ matrix.llvm_version || 'Nightly' }}\n        run: |\n          sudo apt remove 'llvm-*' 'libllvm*'\n          wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc\n          sudo apt-add-repository -y \\\n            deb \\\n            http://apt.llvm.org/${{ matrix.codename }}/ \\\n            llvm-toolchain-${{ matrix.codename }}${{ matrix.llvm_version && format('-{0}', matrix.llvm_version) || '' }} \\\n            main\n          sudo apt install -y llvm${{ matrix.llvm_version && format('-{0}', matrix.llvm_version) || '' }}-dev lld\n\n      - name: Install Crystal\n        uses: crystal-lang/install-crystal@v1\n        with:\n          crystal: \"1.19.1\"\n\n      - name: Build libllvm_ext\n        run: make -B deps\n\n      - name: Test compiler_spec\n        run: make compiler_spec junit_output=.junit/compiler_spec.xml\n\n      - name: Integration test\n        run: make crystal std_spec threads=1 junit_output=.junit/std_spec.xml\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: macOS CI\n\non: [push, pull_request]\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  SPEC_SPLIT_DOTS: 160\n  CI_NIX_SHELL: true\n\njobs:\n  darwin-test:\n    runs-on: ${{ matrix.runs-on }}\n    name: \"${{ matrix.runs-on }} (${{ matrix.arch }})\"\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n        - runs-on: macos-15-intel\n          arch: x86_64-darwin\n        - runs-on: macos-14\n          arch: aarch64-darwin\n        - runs-on: macos-15\n          arch: aarch64-darwin\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - uses: cachix/install-nix-action@1ca7d21a94afc7c957383a2d217460d980de4934 # v31\n        with:\n          extra_nix_config: |\n            experimental-features = nix-command\n\n      - uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: crystal-ci\n          signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: bin/ci prepare_build\n\n      - name: Test\n        run: bin/ci build\n\n      - name: Test interpreter\n        run: bin/ci with_build_env 'make interpreter_spec'\n\n  test-std:\n    runs-on: macos-15\n    strategy:\n      fail-fast: false\n      matrix:\n        opts:\n          - flags: \"-Dexecution_context -Dpreview_mt\" # execution_context: concurrent\n            workers: 1\n          - flags: \"-Dexecution_context -Dpreview_mt\" # execution_context: parallel\n            workers: 4\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - uses: cachix/install-nix-action@1ca7d21a94afc7c957383a2d217460d980de4934 # v31\n        with:\n          extra_nix_config: |\n            experimental-features = nix-command\n\n      - uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: crystal-ci\n          signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'\n\n      - name: Prepare System\n        run: bin/ci prepare_system\n\n      - name: Prepare Build\n        run: bin/ci prepare_build\n\n      - name: Test\n        run: bin/ci with_build_env 'CRYSTAL_WORKERS=${{ matrix.opts.workers || 1 }} make std_spec threads=1 FLAGS=\"${{ matrix.opts.flags }}\"'\n"
  },
  {
    "path": ".github/workflows/mingw-w64-steps.yml",
    "content": "name: MinGW-w64 CI / Build\r\n\r\non:\r\n  workflow_call:\r\n    inputs:\r\n      arch:\r\n        required: true\r\n        type: string\r\n      runs-on:\r\n        required: true\r\n        type: string\r\n      msystem:\r\n        required: true\r\n        type: string\r\n      mingw-package-prefix:\r\n        required: true\r\n        type: string\r\n\r\npermissions: {}\r\n\r\njobs:\r\n  build:\r\n    runs-on: ${{ inputs.runs-on }}\r\n    steps:\r\n      - name: Setup MSYS2\r\n        id: msys2\r\n        uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2.30.0\r\n        with:\r\n          msystem: ${{ inputs.msystem }}\r\n          update: true\r\n          install: >-\r\n            git\r\n            make\r\n            ${{ inputs.mingw-package-prefix }}-pkgconf\r\n            ${{ inputs.mingw-package-prefix }}-llvm\r\n            ${{ inputs.mingw-package-prefix }}-crystal\r\n\r\n      - name: Disable CRLF line ending substitution\r\n        run: |\r\n          git config --global core.autocrlf false\r\n\r\n      - name: Download Crystal source\r\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\r\n\r\n      - name: Build Crystal\r\n        shell: msys2 {0}\r\n        run: make crystal interpreter=1 release=1\r\n\r\n      - name: Package Crystal\r\n        shell: msys2 {0}\r\n        run: make install install_dlls deref_symlinks=1 PREFIX=\"$(pwd)/crystal\"\r\n\r\n      - name: Download shards release\r\n        uses: actions/checkout@v6\r\n        with:\r\n          repository: crystal-lang/shards\r\n          ref: v0.20.0\n          path: shards\n          persist-credentials: false\r\n\r\n      - name: Build shards release\r\n        shell: msys2 {0}\r\n        working-directory: ./shards\r\n        run: make CRYSTAL=$(pwd)/../crystal/bin/crystal SHARDS=false release=1\r\n\r\n      - name: Package Shards\r\n        shell: msys2 {0}\r\n        working-directory: ./shards\r\n        run: |\r\n          make install PREFIX=\"$(pwd)/../crystal\" SHARDS=false\r\n          # FIXME: remove after crystal-lang/shards#668\r\n          ldd bin/shards.exe | grep -iv ' => /c/windows/system32' | sed 's/.* => //; s/ (.*//' | xargs -t -i /usr/bin/install -m 0755 '{}' \"$(pwd)/../crystal/bin/\"\r\n\r\n      - name: Upload Crystal executable\r\n        uses: actions/upload-artifact@v7\r\n        with:\r\n          name: ${{ inputs.arch }}-mingw-w64-crystal\r\n          path: crystal\r\n\r\n  test-stdlib:\r\n    runs-on: ${{ inputs.runs-on }}\r\n    steps:\r\n      - name: Setup MSYS2\r\n        id: msys2\r\n        uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2.30.0\r\n        with:\r\n          msystem: ${{ inputs.msystem }}\r\n          update: true\r\n          install: >-\r\n            git\r\n            make\r\n            ${{ inputs.mingw-package-prefix }}-pkgconf\r\n            ${{ inputs.mingw-package-prefix }}-llvm\r\n            ${{ inputs.mingw-package-prefix }}-crystal\r\n\r\n      - name: Disable CRLF line ending substitution\r\n        run: |\r\n          git config --global core.autocrlf false\r\n\r\n      - name: Download Crystal source\r\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\r\n\r\n      - name: Run stdlib specs\r\n        shell: msys2 {0}\r\n        run: |\r\n          export CRYSTAL_SPEC_COMPILER_BIN=\"$(which crystal.exe)\"\r\n          make std_spec\r\n\r\n  test-compiler:\r\n    runs-on: ${{ inputs.runs-on }}\r\n    needs: [build]\r\n    steps:\r\n      - name: Setup MSYS2\r\n        id: msys2\r\n        uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2.30.0\r\n        with:\r\n          msystem: ${{ inputs.msystem }}\r\n          update: true\r\n          install: >-\r\n            git\r\n            make\r\n            ${{ inputs.mingw-package-prefix }}-pkgconf\r\n            ${{ inputs.mingw-package-prefix }}-llvm\r\n            ${{ inputs.mingw-package-prefix }}-crystal\r\n\r\n      - name: Disable CRLF line ending substitution\r\n        run: |\r\n          git config --global core.autocrlf false\r\n\r\n      - name: Download Crystal source\r\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\r\n\r\n      - name: Download Crystal executable\r\n        uses: actions/download-artifact@v8\r\n        with:\r\n          name: ${{ inputs.arch }}-mingw-w64-crystal\r\n          path: crystal\r\n\r\n      - name: Copy compiler build into place\r\n        run: mkdir .build/ && cp crystal/bin/crystal.exe .build/\r\n\r\n      - name: Run compiler specs\r\n        shell: msys2 {0}\r\n        run: |\r\n          export CRYSTAL_SPEC_COMPILER_BIN=\"$(which crystal.exe)\"\r\n          make compiler_spec\r\n\r\n      - name: Run interpreter specs\r\n        shell: msys2 {0}\r\n        run: |\r\n          export CRYSTAL_SPEC_COMPILER_BIN=\"$(which crystal.exe)\"\r\n          make interpreter_spec\r\n\r\n      - name: Run primitives specs\r\n        shell: msys2 {0}\r\n        run: |\r\n          export CRYSTAL_SPEC_COMPILER_BIN=\"$(which crystal.exe)\"\r\n          make -o .build/crystal.exe primitives_spec # we know the compiler is fresh; do not rebuild it here\r\n        env:\r\n          SPEC_FLAGS: --tag=~external_commands # skip exec_external_command spec because it doesn't work with this setup\r\n"
  },
  {
    "path": ".github/workflows/mingw-w64.yml",
    "content": "name: MinGW-w64 CI\r\n\r\non: [push, pull_request]\r\n\r\npermissions: {}\r\n\r\nconcurrency:\r\n  group: ${{ github.workflow }}-${{ github.ref }}\r\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\r\n\r\nenv:\r\n  SPEC_SPLIT_DOTS: 160\r\n\r\njobs:\r\n  x86_64-mingw-w64:\r\n    name: UCRT64\r\n    uses: ./.github/workflows/mingw-w64-steps.yml\r\n    with:\r\n      arch: x86_64\r\n      runs-on: windows-2025\r\n      msystem: UCRT64\r\n      mingw-package-prefix: mingw-w64-ucrt-x86_64\r\n\r\n  aarch64-mingw-w64:\r\n    name: CLANGARM64\r\n    uses: ./.github/workflows/mingw-w64-steps.yml\r\n    with:\r\n      arch: aarch64\r\n      runs-on: windows-11-arm\r\n      msystem: CLANGARM64\r\n      mingw-package-prefix: mingw-w64-clang-aarch64\r\n"
  },
  {
    "path": ".github/workflows/openssl.yml",
    "content": "name: OpenSSL CI\n\non:\n  push:\n    paths:\n      - 'src/openssl/**'\n      - 'spec/std/digest/**'\n      - 'spec/std/openssl/**'\n      - '.github/workflows/openssl.yml'\n  pull_request:\n    paths:\n      - 'src/openssl/**'\n      - 'spec/std/digest/**'\n      - 'spec/std/openssl/**'\n      - '.github/workflows/openssl.yml'\n  schedule:\n    - cron: '0 3 * * *'\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\njobs:\n  libssl_test:\n    runs-on: ubuntu-latest\n    name: \"${{ matrix.pkg }}\"\n    container: crystallang/crystal:1.19.1-alpine\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - pkg: \"openssl1.1-compat-dev=~1.1.1\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.18/community\n          - pkg: \"openssl-dev=~3.0\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.17/main\n          - pkg: \"openssl-dev=~3.3\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.20/main\n          - pkg: \"openssl-dev\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/edge/main\n          - pkg: \"libressl-dev=~3.4\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.15/community\n          - pkg: \"libressl-dev=~3.5\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.16/community\n          - pkg: \"libressl-dev=~3.8\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.20/community\n          - pkg: \"libressl-dev\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/edge/community\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Uninstall openssl and conflicts\n        run: apk del openssl-dev openssl-libs-static libxml2-static\n      - name: Install ${{ matrix.pkg }}\n        run: apk add \"${{ matrix.pkg }}\" --repository=${{ matrix.repository }}\n      - name: Print LibSSL version\n        run: bin/crystal eval 'require \"openssl\"; p! LibSSL::OPENSSL_VERSION, LibSSL::LIBRESSL_VERSION'\n      - name: Run OpenSSL specs\n        run: bin/crystal spec --order=random spec/std/openssl/\n"
  },
  {
    "path": ".github/workflows/regex-engine.yml",
    "content": "name: Regex Engine CI\n\non:\n  push:\n    paths:\n      - 'src/regex.cr'\n      - 'src/regex/**'\n      - 'spec/std/regex_spec.cr'\n      - 'spec/std/regex/**'\n      - '.github/workflows/regex-engine.yml'\n  pull_request:\n    paths:\n      - 'src/regex.cr'\n      - 'src/regex/**'\n      - 'spec/std/regex_spec.cr'\n      - 'spec/std/regex/**'\n      - '.github/workflows/regex-engine.yml'\n  schedule:\n    - cron: '0 3 * * *'\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\njobs:\n  pcre:\n    runs-on: ubuntu-latest\n    name: \"PCRE\"\n    container: crystallang/crystal:1.19.1-alpine\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Remove PCRE2\n        run: apk del pcre2-dev\n      - name: Assert using PCRE\n        run: bin/crystal eval 'abort unless Regex::Engine == Regex::PCRE'\n      - name: Show PCRE config\n        run: bin/crystal scripts/print_regex_config.cr\n      - name: Run Regex specs\n        run: bin/crystal spec --order=random spec/std/regex*\n  pcre2:\n    runs-on: ubuntu-latest\n    name: \"PCRE2\"\n    container: crystallang/crystal:1.19.1-alpine\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Assert using PCRE2\n        run: bin/crystal eval 'abort unless Regex::Engine == Regex::PCRE2'\n      - name: Assert select PCRE\n        run: bin/crystal eval -Duse_pcre 'abort unless Regex::Engine == Regex::PCRE'\n      - name: Show PCRE2 config\n        run: bin/crystal scripts/print_regex_config.cr\n      - name: Run Regex specs\n        run: bin/crystal spec --order=random spec/std/regex*\n"
  },
  {
    "path": ".github/workflows/smoke.yml",
    "content": "# These jobs are smoke tests for platforms where we don't run full tests.\n# They ensure that std_spec, compiler_spec and the compiler itself at least\n# compile for the target with --cross-compile. But the binaries are not linked\n# and executed. So this does not validate correct behaviour.\n#\n# The list of supported platforms is extracted from the lib_c bindings:\n#\n# ```terminal-session\n# $ find src/lib_c -maxdepth 1 -mindepth 1 -type d -printf '%P\\n' | sort\n# aarch64-android\n# aarch64-darwin\n# aarch64-linux-gnu\n# aarch64-linux-musl\n# arm-linux-gnueabihf\n# i386-linux-gnu\n# i386-linux-musl\n# wasm32-wasi\n# x86_64-darwin\n# x86_64-dragonfly\n# x86_64-freebsd\n# x86_64-linux-gnu\n# x86_64-linux-musl\n# x86_64-netbsd\n# x86_64-openbsd\n# x86_64-solaris\n# x86_64-windows-msvc\n# ```\n#\n# Platforms for which we currently run full tests are excluded from this workflow.\nname: Smoke tests\n\non:\n  push:\n    paths:\n      - 'src/lib_c/**'\n      - 'src/crystal/system/**'\n      - '.github/workflows/smoke.yml'\n  pull_request:\n    paths:\n      - 'src/lib_c/**'\n      - 'src/crystal/system/**'\n      - '.github/workflows/smoke.yml'\n  schedule:\n    - cron: '0 3 * * *'\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  TRAVIS_OS_NAME: linux\n  ARCH: x86_64\n  ARCH_CMD: linux64\n\njobs:\n  smoke-test:\n    name: ${{ matrix.target }}\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      max-parallel: 2\n      matrix:\n        target:\n          - aarch64-linux-android\n          - arm-linux-gnueabihf\n          - i386-linux-gnu\n          - i386-linux-musl\n          - x86_64-dragonfly\n          - x86_64-freebsd\n          - x86_64-netbsd\n          - x86_64-openbsd\n          - x86_64-solaris\n\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Build fresh compiler\n        run: bin/ci with_build_env make\n\n      - name: Run smoke test\n        run: bin/ci with_build_env make simple_smoke_test target=${{ matrix.target }}\n"
  },
  {
    "path": ".github/workflows/update-devenv.yml",
    "content": "name: Update devenv\n\non:\n  schedule:\n    - cron: \"0 3 * * 2\" # Weekly on Tuesdays\n  push:\n    branches:\n      - master\n    paths:\n      - '.github/workflows/update-devenv.yml'\n  workflow_dispatch:\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  update-devenv:\n    if: github.repository == 'crystal-lang/crystal'\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Download source\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          ref: master\n          persist-credentials: false\n\n      - uses: cachix/install-nix-action@1ca7d21a94afc7c957383a2d217460d980de4934 # v31\n\n      - uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: devenv\n\n      - name: Install devenv.sh\n        run: nix profile add nixpkgs#devenv\n\n      - name: Update devenv.lock\n        run: devenv update\n\n      - name: Check for changes\n        id: changes\n        run: |\n          git add devenv.lock\n          if git diff --cached --quiet; then\n            echo \"changed=false\" >>\"$GITHUB_OUTPUT\"\n          else\n            echo \"changed=true\" >>\"$GITHUB_OUTPUT\"\n          fi\n\n      - name: Create PR\n        if: steps.changes.outputs.changed == 'true'\n        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8\n        with:\n          commit-message: \"Update `devenv.lock`\"\n          title: \"Update `devenv.lock`\"\n          body: \"\"\n          labels: \"topic:infrastructure\"\n          branch: infra/update-devenv\n          delete-branch: true\n"
  },
  {
    "path": ".github/workflows/wasm32.yml",
    "content": "name: WebAssembly CI\n\non: [push, pull_request]\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  SPEC_SPLIT_DOTS: 160\n\njobs:\n  wasm32-test:\n    runs-on: ubuntu-24.04\n    container: crystallang/crystal:1.19.1-build\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install wasmtime\n        uses: mwilliamson/setup-wasmtime-action@bf814d7d8fc3c3a77dfe114bd9fb8a2c575f6ad6 #v2\n        with:\n          wasmtime-version: \"2.0.0\"\n\n      - name: Install LLVM\n        run: |\n          apt-get update\n          apt-get remove -y 'llvm-*' 'libllvm*'\n          apt-get install -y curl software-properties-common\n          wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc\n          apt-add-repository -y deb http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main\n          apt-get install -y llvm-18-dev lld-18\n          ln -s \"$(which wasm-ld-18)\" /usr/bin/wasm-ld\n\n      - name: Download wasm32 libs\n        run: |\n          mkdir wasm32-wasi-libs\n          curl -LO https://github.com/lbguilherme/wasm-libs/releases/download/0.0.3/wasm32-wasi-libs.tar.gz\n          echo \"cd36f319f8f9f9cd08f723d10e6ec2b92f2e44d3ce3b20344b8041386d85c261  wasm32-wasi-libs.tar.gz\" | sha256sum -c -\n          tar -f wasm32-wasi-libs.tar.gz -C wasm32-wasi-libs -xz\n          rm wasm32-wasi-libs.tar.gz\n\n      - name: Build spec/wasm32_std_spec.cr\n        run: bin/crystal build spec/wasm32_std_spec.cr -o wasm32_std_spec.wasm --target wasm32-wasi -Dwithout_iconv -Dwithout_openssl\n        env:\n          CRYSTAL_LIBRARY_PATH: ${{ github.workspace }}/wasm32-wasi-libs\n\n      - name: Run wasm32_std_spec.wasm\n        run: |\n          wasmtime run wasm32_std_spec.wasm\n"
  },
  {
    "path": ".github/workflows/win.yml",
    "content": "name: Windows CI\n\non: [push, pull_request, workflow_dispatch]\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\nenv:\n  SPEC_SPLIT_DOTS: 160\n  CI_LLVM_VERSION: \"20.1.7\"\n  CI_LLVM_TARGETS: \"X86,AArch64\"\n  CI_LLVM_LDFLAGS: \"psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib ws2_32.lib ntdll.lib\"\n\njobs:\n  x86_64-windows-libs:\n    runs-on: windows-2025\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Enable Developer Command Prompt\n        uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Cache libraries\n        id: cache-libs\n        uses: actions/cache@v5\n        with:\n          path: | # openssl and llvm take much longer to build so they are cached separately\n            libs/pcre.lib\n            libs/pcre2-8.lib\n            libs/iconv.lib\n            libs/gc.lib\n            libs/ffi.lib\n            libs/z.lib\n            libs/mpir.lib\n            libs/yaml.lib\n            libs/xml2.lib\n            libs/libxml_VERSION\n          key: win-libs-${{ hashFiles('.github/workflows/win.yml', 'etc/win-ci/*.ps1') }}-msvc\n      - name: Set up Cygwin\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6\n        with:\n          packages: make\n          install-dir: C:\\cygwin64\n          add-to-path: false\n      - name: Build libgc\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-gc.ps1 -BuildTree deps\\gc -Version 8.2.8 -AtomicOpsVersion 7.8.2\n      - name: Build libpcre\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-pcre.ps1 -BuildTree deps\\pcre -Version 8.45\n      - name: Build libpcre2\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-pcre2.ps1 -BuildTree deps\\pcre2 -Version 10.45\n      - name: Build libiconv\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-iconv.ps1 -BuildTree deps\\iconv -Version 1.18\n      - name: Build libffi\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-ffi.ps1 -BuildTree deps\\ffi -Version 3.5.1\n      - name: Build zlib\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-z.ps1 -BuildTree deps\\z -Version 1.3.1\n      - name: Build mpir\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-mpir.ps1 -BuildTree deps\\mpir\n      - name: Build libyaml\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-yaml.ps1 -BuildTree deps\\yaml -Version 0.2.5\n      - name: Build libxml2\n        if: steps.cache-libs.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-xml2.ps1 -BuildTree deps\\xml2 -Version 2.13.8\n\n      - name: Cache OpenSSL\n        id: cache-openssl\n        uses: actions/cache@v5\n        with:\n          path: |\n            libs/crypto.lib\n            libs/ssl.lib\n            libs/openssl_VERSION\n          key: win-openssl-libs-3.4.1-${{ hashFiles('etc/win-ci/build-openssl.ps1') }}-msvc\n      - name: Set up NASM\n        if: steps.cache-openssl.outputs.cache-hit != 'true'\n        uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2\n      - name: Build OpenSSL\n        if: steps.cache-openssl.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-openssl.ps1 -BuildTree deps\\openssl -Version 3.5.0\n\n  x86_64-windows-dlls:\n    runs-on: windows-2025\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Enable Developer Command Prompt\n        uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Cache libraries\n        id: cache-dlls\n        uses: actions/cache@v5\n        with:\n          path: | # openssl and llvm take much longer to build so they are cached separately\n            libs/pcre-dynamic.lib\n            libs/pcre2-8-dynamic.lib\n            libs/iconv-dynamic.lib\n            libs/gc-dynamic.lib\n            libs/ffi-dynamic.lib\n            libs/z-dynamic.lib\n            libs/mpir-dynamic.lib\n            libs/yaml-dynamic.lib\n            libs/xml2-dynamic.lib\n            dlls/pcre.dll\n            dlls/pcre2-8.dll\n            dlls/iconv-2.dll\n            dlls/gc.dll\n            dlls/libffi.dll\n            dlls/zlib1.dll\n            dlls/mpir.dll\n            dlls/yaml.dll\n            dlls/libxml2.dll\n          key: win-dlls-${{ hashFiles('.github/workflows/win.yml', 'etc/win-ci/*.ps1') }}-msvc\n      - name: Set up Cygwin\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6\n        with:\n          packages: make\n          install-dir: C:\\cygwin64\n          add-to-path: false\n      - name: Build libgc\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-gc.ps1 -BuildTree deps\\gc -Version 8.2.8 -AtomicOpsVersion 7.8.2 -Dynamic\n      - name: Build libpcre\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-pcre.ps1 -BuildTree deps\\pcre -Version 8.45 -Dynamic\n      - name: Build libpcre2\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-pcre2.ps1 -BuildTree deps\\pcre2 -Version 10.45 -Dynamic\n      - name: Build libiconv\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-iconv.ps1 -BuildTree deps\\iconv -Version 1.18 -Dynamic\n      - name: Build libffi\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-ffi.ps1 -BuildTree deps\\ffi -Version 3.4.7 -Dynamic\n      - name: Build zlib\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-z.ps1 -BuildTree deps\\z -Version 1.3.1 -Dynamic\n      - name: Build mpir\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-mpir.ps1 -BuildTree deps\\mpir -Dynamic\n      - name: Build libyaml\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-yaml.ps1 -BuildTree deps\\yaml -Version 0.2.5 -Dynamic\n      - name: Build libxml2\n        if: steps.cache-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-xml2.ps1 -BuildTree deps\\xml2 -Version 2.13.6 -Dynamic\n\n      - name: Cache OpenSSL\n        id: cache-openssl-dlls\n        uses: actions/cache@v5\n        with:\n          path: |\n            libs/crypto-dynamic.lib\n            libs/ssl-dynamic.lib\n            dlls/libcrypto-3-x64.dll\n            dlls/libssl-3-x64.dll\n          key: win-openssl-dlls-3.4.1-${{ hashFiles('etc/win-ci/build-openssl.ps1') }}-msvc\n      - name: Set up NASM\n        if: steps.cache-openssl-dlls.outputs.cache-hit != 'true'\n        uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2\n      - name: Build OpenSSL\n        if: steps.cache-openssl-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-openssl.ps1 -BuildTree deps\\openssl -Version 3.4.1 -Dynamic\n\n  x86_64-windows-llvm-dlls:\n    runs-on: windows-2025\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Enable Developer Command Prompt\n        uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Cache LLVM\n        id: cache-llvm-dlls\n        uses: actions/cache@v5\n        with:\n          path: |\n            libs/llvm_VERSION\n            libs/llvm-dynamic.lib\n            dlls/LLVM-C.dll\n          key: llvm-dlls-${{ env.CI_LLVM_VERSION }}-${{ hashFiles('etc/win-ci/build-llvm.ps1') }}-msvc\n      - name: Build LLVM\n        if: steps.cache-llvm-dlls.outputs.cache-hit != 'true'\n        run: .\\etc\\win-ci\\build-llvm.ps1 -BuildTree deps\\llvm -Version ${{ env.CI_LLVM_VERSION }} -TargetsToBuild ${{ env.CI_LLVM_TARGETS }} -Dynamic\n\n  x86_64-windows-release:\n    needs: [x86_64-windows-libs, x86_64-windows-dlls, x86_64-windows-llvm-dlls]\n    uses: ./.github/workflows/win_build_portable.yml\n    with:\n      release: true\n      llvm_version: \"20.1.7\"\n      llvm_targets: \"X86,AArch64\"\n      llvm_ldflags: \"psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib ws2_32.lib ntdll.lib\"\n\n  x86_64-windows-test:\n    runs-on: windows-2025\n    needs: [x86_64-windows-release]\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Enable Developer Command Prompt\n        uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal\n          path: build\n\n      - name: Set up environment\n        run: |\n          Add-Content $env:GITHUB_PATH \"$(pwd)\\build\"\n          Add-Content $env:GITHUB_ENV \"CRYSTAL_SPEC_COMPILER_BIN=$(pwd)\\build\\crystal.exe\"\n          Add-Content $env:GITHUB_ENV \"LLVM_VERSION=${{ env.CI_LLVM_VERSION }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_TARGETS=${{ env.CI_LLVM_TARGETS }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_LDFLAGS=${{ env.CI_LLVM_LDFLAGS }}\"\n\n      - name: Run stdlib specs\n        run: make -f Makefile.win std_spec\n\n      - name: Run compiler specs\n        run: make -f Makefile.win compiler_spec\n\n      - name: Run interpreter specs\n        run: make -f Makefile.win interpreter_spec\n\n      - name: Run primitives specs\n        run: make -f Makefile.win -o .build\\crystal.exe primitives_spec # we know the compiler is fresh; do not rebuild it here\n\n      - name: Build samples\n        run: make -f Makefile.win samples\n\n  test-std:\n    runs-on: windows-2025\n    needs: [x86_64-windows-release]\n    strategy:\n      fail-fast: false\n      matrix:\n        opts:\n          - flags: \"-Dexecution_context -Dpreview_mt\" # execution_context: concurrent\n            workers: 1\n          - flags: \"-Dexecution_context -Dpreview_mt\" # execution_context: parallel\n            workers: 4\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: git config --global core.autocrlf false\n\n      - name: Enable Developer Command Prompt\n        uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal\n          path: build\n\n      - name: Set up environment\n        run: |\n          Add-Content $env:GITHUB_PATH \"$(pwd)\\build\"\n          Add-Content $env:GITHUB_ENV \"CRYSTAL_SPEC_COMPILER_BIN=$(pwd)\\build\\crystal.exe\"\n          Add-Content $env:GITHUB_ENV \"LLVM_VERSION=${{ env.CI_LLVM_VERSION }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_TARGETS=${{ env.CI_LLVM_TARGETS }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_LDFLAGS=${{ env.CI_LLVM_LDFLAGS }}\"\n\n      - name: Test\n        run: make -f Makefile.win std_spec FLAGS=\"${{ matrix.opts.flags }}\"\n        env:\n          CRYSTAL_WORKERS: ${{ matrix.opts.workers || 1 }}\n\n  x86_64-windows-test-interpreter:\n    runs-on: windows-2025\n    needs: [x86_64-windows-release]\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal\n          path: build\n\n      - name: Set up environment\n        run: |\n          Add-Content $env:GITHUB_PATH \"$(pwd)\\build\"\n          Add-Content $env:GITHUB_ENV \"CRYSTAL_SPEC_COMPILER_BIN=$(pwd)\\build\\crystal.exe\"\n          Add-Content $env:GITHUB_ENV \"LLVM_VERSION=${{ env.CI_LLVM_VERSION }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_TARGETS=${{ env.CI_LLVM_TARGETS }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_LDFLAGS=${{ env.CI_LLVM_LDFLAGS }}\"\n\n      - name: Run stdlib specs with interpreter\n        run: bin\\crystal i spec\\std_spec.cr\n\n      - name: Run primitives specs with interpreter\n        run: bin\\crystal i spec\\primitives_spec.cr\n\n  x86_64-windows-installer:\n    if: github.repository_owner == 'crystal-lang' && (startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/ci/'))\n    runs-on: windows-2025\n    needs: [x86_64-windows-release]\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Download Crystal executable\n        uses: actions/download-artifact@v8\n        with:\n          name: crystal\n          path: etc/win-ci/portable\n\n      - name: Set up Inno Setup\n        run: |\n          Invoke-WebRequest -Uri https://jrsoftware.org/download.php/is.exe -OutFile C:\\is.exe\n          C:\\is.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART\n\n      - name: Set up environment\n        run: |\n          Add-Content $env:GITHUB_PATH \"$(pwd)\\etc\\win-ci\\portable\"\n          Add-Content $env:GITHUB_ENV \"LLVM_VERSION=${{ env.CI_LLVM_VERSION }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_TARGETS=${{ env.CI_LLVM_TARGETS }}\"\n          Add-Content $env:GITHUB_ENV \"LLVM_LDFLAGS=${{ env.CI_LLVM_LDFLAGS }}\"\n\n      - name: Build docs\n        run: make -f Makefile.win install_docs prefix=etc\\win-ci\\portable\n\n      - name: Build installer\n        working-directory: ./etc/win-ci\n        run: |\n          & \"C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe\" crystal.iss\n\n      - name: Upload Crystal installer\n        uses: actions/upload-artifact@v7\n        with:\n          name: crystal-installer\n          path: etc/win-ci/Output/crystal-setup.exe\n"
  },
  {
    "path": ".github/workflows/win_build_portable.yml",
    "content": "name: Windows CI / Build Portable Package\n\non:\n  workflow_call:\n    inputs:\n      release:\n        required: true\n        type: boolean\n      llvm_version:\n        required: true\n        type: string\n      llvm_targets:\n        required: true\n        type: string\n      llvm_ldflags:\n        required: true\n        type: string\n\npermissions: {}\n\njobs:\n  build:\n    runs-on: windows-2025\n    steps:\n      - name: Disable CRLF line ending substitution\n        run: |\n          git config --global core.autocrlf false\n\n      - name: Enable Developer Command Prompt\n        uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0\n\n      - name: Install Crystal\n        uses: crystal-lang/install-crystal@v1\n        id: install-crystal\n        with:\n          crystal: \"1.19.1\"\n\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Restore libraries\n        uses: actions/cache/restore@v5\n        with:\n          path: |\n            libs/pcre.lib\n            libs/pcre2-8.lib\n            libs/iconv.lib\n            libs/gc.lib\n            libs/ffi.lib\n            libs/z.lib\n            libs/mpir.lib\n            libs/yaml.lib\n            libs/xml2.lib\n            libs/libxml_VERSION\n          key: win-libs-${{ hashFiles('.github/workflows/win.yml', 'etc/win-ci/*.ps1') }}-msvc\n          fail-on-cache-miss: true\n      - name: Restore OpenSSL\n        uses: actions/cache/restore@v5\n        with:\n          path: |\n            libs/crypto.lib\n            libs/ssl.lib\n            libs/openssl_VERSION\n          key: win-openssl-libs-3.4.1-${{ hashFiles('etc/win-ci/build-openssl.ps1') }}-msvc\n          fail-on-cache-miss: true\n      - name: Restore DLLs\n        uses: actions/cache/restore@v5\n        with:\n          path: |\n            libs/pcre-dynamic.lib\n            libs/pcre2-8-dynamic.lib\n            libs/iconv-dynamic.lib\n            libs/gc-dynamic.lib\n            libs/ffi-dynamic.lib\n            libs/z-dynamic.lib\n            libs/mpir-dynamic.lib\n            libs/yaml-dynamic.lib\n            libs/xml2-dynamic.lib\n            dlls/pcre.dll\n            dlls/pcre2-8.dll\n            dlls/iconv-2.dll\n            dlls/gc.dll\n            dlls/libffi.dll\n            dlls/zlib1.dll\n            dlls/mpir.dll\n            dlls/yaml.dll\n            dlls/libxml2.dll\n          key: win-dlls-${{ hashFiles('.github/workflows/win.yml', 'etc/win-ci/*.ps1') }}-msvc\n          fail-on-cache-miss: true\n      - name: Restore OpenSSL DLLs\n        uses: actions/cache/restore@v5\n        with:\n          path: |\n            libs/crypto-dynamic.lib\n            libs/ssl-dynamic.lib\n            dlls/libcrypto-3-x64.dll\n            dlls/libssl-3-x64.dll\n          key: win-openssl-dlls-3.4.1-${{ hashFiles('etc/win-ci/build-openssl.ps1') }}-msvc\n          fail-on-cache-miss: true\n      - name: Restore LLVM DLLs\n        uses: actions/cache/restore@v5\n        with:\n          path: |\n            libs/llvm_VERSION\n            libs/llvm-dynamic.lib\n            dlls/LLVM-C.dll\n          key: llvm-dlls-${{ inputs.llvm_version }}-${{ hashFiles('etc/win-ci/build-llvm.ps1') }}-msvc\n          fail-on-cache-miss: true\n\n      - name: Set up environment\n        run: |\n          Add-Content $env:GITHUB_ENV \"CRYSTAL_LIBRARY_PATH=$(pwd)\\libs\"\n          Add-Content $env:GITHUB_ENV \"LLVM_VERSION=$env:INPUTS_LLVM_VERSION\"\n          Add-Content $env:GITHUB_ENV \"LLVM_TARGETS=$env:INPUTS_LLVM_TARGETS\"\n          Add-Content $env:GITHUB_ENV \"LLVM_LDFLAGS=$env:INPUTS_LLVM_LDFLAGS\"\n        env:\n          INPUTS_LLVM_VERSION: ${{ inputs.llvm_version }}\n          INPUTS_LLVM_TARGETS: ${{ inputs.llvm_targets }}\n          INPUTS_LLVM_LDFLAGS: ${{ inputs.llvm_ldflags }}\n\n      - name: Build LLVM extensions\n        run: make -f Makefile.win deps\n\n      - name: Build Crystal\n        run: |\n          bin/crystal.bat env\n          make -f Makefile.win -B ${{ inputs.release && 'release=1' || '' }} interpreter=1\n\n      - name: Download shards release\n        uses: actions/checkout@v6\n        with:\n          repository: crystal-lang/shards\n          ref: v0.20.0\n          path: shards\n          persist-credentials: false\n\n      - name: Build shards release\n        working-directory: ./shards\n        run: make -f Makefile.win ${{ inputs.release && 'release=1' || '' }}\n\n      - name: Gather Crystal binaries\n        run: |\n          make -f Makefile.win install prefix=crystal\n          mkdir crystal/lib\n          cp shards/bin/shards.exe crystal/\n          cp shards/bin/shards.pdb crystal/\n          cp libs/* crystal/lib/\n          cp dlls/* crystal/\n          cp README.md crystal/\n\n      - name: Upload Crystal binaries\n        uses: actions/upload-artifact@v7\n        with:\n          name: crystal\n          path: crystal\n"
  },
  {
    "path": ".github/workflows/xml.yml",
    "content": "name: XML CI\n\non:\n  push:\n    paths:\n      - 'spec/std/xml/**'\n      - 'src/xml/**'\n      - '.github/workflows/xml.yml'\n  pull_request:\n    paths:\n      - 'spec/std/xml/**'\n      - 'src/xml/**'\n      - '.github/workflows/xml.yml'\n  schedule:\n    - cron: '0 3 * * *'\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}\n\njobs:\n  libssl_test:\n    runs-on: ubuntu-latest\n    name: \"${{ matrix.pkg }}\"\n    container: crystallang/crystal:1.19.1-alpine\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - pkg: \"libxml2-dev=~2.9\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.16/main\n          - pkg: \"libxml2-dev=~2.12\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.20/main\n          - pkg: \"libxml2-dev=~2.13\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/v3.22/main\n          - pkg: \"libxml2-dev\"\n            repository: http://dl-cdn.alpinelinux.org/alpine/edge/main\n    steps:\n      - name: Download Crystal source\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Uninstall libxml2 and conflicts\n        run: apk del libxml2-dev\n      - name: Install ${{ matrix.pkg }}\n        run: apk add \"${{ matrix.pkg }}\" --repository=${{ matrix.repository }}\n      - name: Print LibXML2 version\n        run: bin/crystal eval 'require \"xml\"; p! XML.libxml2_version'\n      - name: Run XML specs\n        run: bin/crystal spec --order=random spec/std/xml/\n"
  },
  {
    "path": ".github/zizmor.yml",
    "content": "rules:\n  dangerous-triggers:\n    ignore:\n      - backport.yml\n  unpinned-uses:\n    config:\n      policies:\n        actions/*: ref-pin\n        crystal-lang/*: ref-pin\n"
  },
  {
    "path": ".gitignore",
    "content": "# Environment configuration\n/Makefile.local\n/Makefile.win.local\n.env\n.env.*\n.envrc\n.envrc.*\n\n# Build artifacts\n*.o\n*.ll\n*.s\n*.obj\n*.dwo\n*.dwarf\n*.pdb\n*.exe\n/.build/\n/samples/.build/\n/docs/\n/man/*.gz\n\n# CI\n/coverage/\n/tmp/\n/.junit/\n\n# Devenv\n.devenv*\ndevenv.local.nix\ndevenv.local.yaml\n\n# direnv\n.direnv\n\n# pre-commit\n.pre-commit-config.yaml\n"
  },
  {
    "path": ".mailmap",
    "content": "Ary Borenszweig <asterite@gmail.com> <aborenszweig@manas.com.ar>\nAry Borenszweig <asterite@gmail.com> <ary@esperanto.org.ar>\nAry Borenszweig <asterite@gmail.com> <asterite@gmail.com>\nBrian J. Cardiff <bcardiff@gmail.com> <bcardiff@manas.com.ar>\nBrian J. Cardiff <bcardiff@gmail.com> <bcardiff@manas.tech>\nGeorge Dietrich <dev@dietrich.pub> <yomoejoe@gmail.com>\nGeorge Dietrich <dev@dietrich.pub> <george@dietrich.app>\nGustavo Giráldez <ggiraldez@manas.tech> <ggiraldez@manas.com.ar>\nJohannes Müller <straightshoota@gmail.com> <johannes.mueller@smj-fulda.org>\nJuan Wajnerman <juan.wajnerman@gmail.com> <jwajnerman@manas.com.ar>\nJuan Wajnerman <juan.wajnerman@gmail.com> <waj@manas.com.ar>\nJuan Wajnerman <juan.wajnerman@gmail.com> <waj@manas.tech>\nJulien Reichardt <git@jrei.ch> <mi@jrei.ch>\nMartin Verzilli <mverzilli@manas.tech> <mverzilli@manas.com.ar>\nMatías García Isaía <mgarcia@manas.tech> <mgarcia@manas.com.ar>\nMatías García Isaía <mgarcia@manas.tech> <mgarcia@manas.tech>\nStephanie Hobbs <steph@rx14.co.uk> <chris@rx14.co.uk>\n"
  },
  {
    "path": ".markdownlint.yaml",
    "content": "default: true\n\nheading-style: \"setext\"\n\nno-inline-html: false\nline-length: false\n\nol-prefix:\n  style: \"ordered\"\nul-style:\n  style: \"dash\"\nul-indent:\n  indent: 2\n\nno-blanks-blockquote: false\n\nemphasis-style:\n  style: \"underscore\"\nstrong-style:\n  style: \"asterisk\"\n\nno-duplicate-heading:\n  siblings_only: true\nno-bare-urls: false\n\nfenced-code-language: false\ncommands-show-output: false\n\nfirst-line-h1:\n  front_matter_title: \"^\\\\s*(page_)?title\\\\s*[:=]\"\n  exclude:\n    .github/PULL_REQUEST_TEMPLATE/pull_request_template.md\n\nno-alt-text: false\n\nhard_tab:\n  code_blocks: false\n"
  },
  {
    "path": ".markdownlintignore",
    "content": "/lib/\n"
  },
  {
    "path": ".well-known/funding-manifest-urls",
    "content": "https://crystal-lang.org/funding.json\n"
  },
  {
    "path": "Brewfile",
    "content": "brew \"gmp\"\nbrew \"libevent\"\nbrew \"pcre\"\nbrew \"pkg-config\"\nbrew \"openssl@1.1\"\nbrew \"llvm@11\", link: true, conflicts_with: [\"python@2\"]\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nFor information on prior releases, refer to the changelogs in [./doc/changelogs](./doc/changelogs/README.md)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project maintainers in private or by sending an email to [crystal@manas.tech](mailto:crystal@manas.tech). All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Crystal\n\nSo you've decided to contribute to Crystal. Excellent!\n\n## Using the issue tracker\n\nThe [issue tracker](https://github.com/crystal-lang/crystal/issues) is the heart of Crystal's work. Use it for bugs, questions, proposals and feature requests.\n\nPlease always **open a new issue before sending a pull request** if you want to add a new feature to Crystal, unless it is a minor fix, and wait until someone from the core team approves it before you actually start working on it. Otherwise, you risk having the pull request rejected, and the effort implementing it goes to waste. And if you start working on an implementation for an issue, please **let everyone know in the comments** so someone else does not start working on the same thing.\n\nRegardless of the kind of issue, please make sure to look for similar existing issues before posting; otherwise, your issue may be flagged as `duplicated` and closed in favour of the original one. Also, once you open a new issue, please make sure to honour the items listed in the issue template.\n\nIf you open a question, remember to close the issue once you are satisfied with the answer and you think\nthere's no more room for discussion. We'll anyway close the issue after some days.\n\nIf something is missing from the language it might be that it's not yet implemented or that it was purposely left out. If in doubt, just ask.\n\nSubstantial changes go through an [RFC process](https://github.com/crystal-lang/rfcs).\n\nThe best place to start an open discussion about potential changes is the [Crystal forum](https://forum.crystal-lang.org/c/crystal-contrib/6).\n\n### What's needed right now\n\nYou can find a list of tasks that we consider suitable for a first time contribution with\nthe [good first issue label](https://github.com/crystal-lang/crystal/contribute).\n\nAs you feel more confident, you can keep an eye out for open issues with the following labels:\n\n- [`community:to-research`](https://github.com/crystal-lang/crystal/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Acommunity%3Ato-research): Help needed on **researching and investigating** the issue at hand; could be from going through an RFC to figure out how something _should_ be working, to go through details on a C-library we'd like to bind.\n- [`community:to-design`](https://github.com/crystal-lang/crystal/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Acommunity%3Ato-design): As an issue has been accepted, we are looking for **ideas on how it could be implemented**, this is, a high-level design for the feature at hand.\n- [`community:to-implement`](https://github.com/crystal-lang/crystal/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Acommunity%3Ato-implement): After a design has been agreed upon, the remaining task is to actually **code** it and send a PR!\n- [`community:to-document`](https://github.com/crystal-lang/crystal/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Acommunity%3Ato-document): Similar to the one above, but this one is for those awesome devs that are happy to **contribute with documentation** instead of just code.\n\nFurthermore, these are the most important general topics in need right now, so if you are interested open an issue to start working on it:\n\n- Documenting the language\n- Documenting the standard library\n- Adding missing bits of the standard library, and/or improving its performance\n\n### Labels\n\nIssue tracker labels are sorted by category: community, kind, pr, status and topic.\n\n#### Community\n\nThese are the issues where help from the community is most welcome. See above for a description on `newcomer`, `to-research`, `to-design`, `to-implement` and `to-document`.\n\nLabel `in-progress` is used to signal that someone from the community is already working on the issue (since GitHub does not allow for a non-team member to be _assigned_ to an issue).\n\nThe label `shard-idea` refers to a feature proposal that, albeit good, is better suited as a separate shard rather than as part of the core library; so if you are looking for a shard of your own to start working on, these issues are good starting points.\n\n#### Kind\n\nThe most basic category is the kind of the issue: `bug`, `feature` and `question` speak for themselves, while `refactor` is left for changes that do not actually introduce a new a feature, and are not fixing something that is broken, but rather clean up the code (or documentation!).\n\n#### PR\n\nPull-request only labels, used to signal that a pull request `needs-review` by a core team member, or that is still `wip` (work in progress).\n\n#### Topic\n\nTopic encompasses the broad aspect of the language that the issue refers to: could be performance, the compiler, the type system, the code formatter, concurrency, and quite a large etc.\n\n#### Status\n\nStatus labels attempt to capture the lifecycle of an issue:\n\n- A detailed proposal on a feature is marked as `draft`, while a more general argument is usually labelled as `discussion` until a consensus is achieved.\n\n- An issue is `accepted` when it describes a feature or bugfix that a core team member has agreed to have added to the language, so as soon as a design is discussed (if needed), it's safe to start working on a pull request.\n\n- Bug reports are marked as `needs-more-info`, where the author is requested to provide the info required; note that the issue may be closed after some time if it is not supplied.\n\n- Issues that are batched in an epic to be worked on as a group are typically marked as `deferred`, while low-prio issues or tasks far away in the roadmap are marked as `someday`.\n\n- Closed issues are marked as `implemented`, `invalid`, `duplicate` or `wontfix`, depending on their resolution.\n\n## Contributing\n\n### The documentation\n\nThe language reference is available at <https://crystal-lang.org/reference/>.\nSee the repository at [crystal-lang/crystal-book](https://github.com/crystal-lang/crystal-book) for contributing to it.\n\nThe [standard library documentation](https://crystal-lang.org/api/) is on the code itself, in this repository.\nThe [`master` version](https://crystal-lang.org/api/master/) is updated with every push to the master branch.\nIt uses a subset of [Markdown](http://daringfireball.net/projects/markdown/). You can [use Ruby as a source\nof inspiration](https://twitter.com/yukihiro_matz/status/549317901002342400) whenever applicable. To generate\nthe docs execute `make docs`. Please follow the guidelines described in our\n[language documentation](https://crystal-lang.org/reference/conventions/documenting_code.html), like the use of the third person.\n\nAdditionally, all official documentation can be found on [the Crystal website](https://crystal-lang.org/docs/).\n\n### The standard library\n\n1. [Fork](https://github.com/crystal-lang/crystal/fork) and checkout the\n   repository <https://github.com/crystal-lang/crystal/>\n\nOnce in the cloned directory, and once you [installed Crystal](https://crystal-lang.org/install/),\nyou can execute `bin/crystal` instead of `crystal`. This is a wrapper that will use the cloned repository\nas the standard library. Otherwise the barebones `crystal` executable uses the standard library that comes in\nyour installation.\n\nNext, make changes to the standard library, making sure you also provide corresponding specs. To run\nthe specs for the standard library, run `make std_spec`. To run a particular spec: `bin/crystal spec spec/std/array_spec.cr`.\nYou can use `make help` for a list of available make targets.\n\nNote: at this point you might get long compile error that include \"library not found for: ...\". This means\nyou are [missing some libraries](https://github.com/crystal-lang/crystal/wiki/All-required-libraries).\n\nMake sure that your changes follow the recommended [Coding Style](https://crystal-lang.org/reference/conventions/coding_style.html).\nYou can run `crystal tool format` to automate this.\n\nThen push your changes and create a pull request.\n\n### The compiler itself\n\nIf you want to add/change something in the compiler,\nthe first thing you will need to do is to [install the compiler](https://crystal-lang.org/install/).\n\nOnce you have a compiler up and running, check that executing `crystal` on the command line prints its usage.\nNow you can setup your environment to compile Crystal itself, which is itself written in Crystal.\n\nThe compiler needs [LLVM](https://llvm.org) and some other libraries. See [list of all required libraries](https://github.com/crystal-lang/crystal/wiki/All-required-libraries).\n\nExecuting `make crystal` builds the compiler into `.build/compiler` and you can run it using the wrapper script at `bin/crystal`.\nThe script sets up the proper environment variables that the compiler can find the standard library source files in `src/`.\n\n`make compiler_spec` runs the compiler specs. `make std_spec` runs the standard library specs.\n`make primitives_spec` runs the specs for primitive methods with an up-to-date Crystal compiler.\nYou can use `make help` for a list of available make targets.\n\n## This guide\n\nIf this guide is not clear and it needs improvements, please send pull requests against it. Thanks! :-)\n\n## Making good pull requests\n\nThe commit history should consist of commits that transform the codebase from one state into another one, motivated by something that\nshould change, be it a bugfix, a new feature or some ground work to support a new feature, like changing an existing API or introducing\na new isolated class that is later used in the same pull request.\n\nReview history should be preserved in a pull request. If you need to push a change to an open pull request (for example\nbecause specs broke and required a fix, or for applying a review suggestion) these changes should be added as individual\nfixup commits. Please do not amend previous commits and force push to the PR branch. This makes reviews much harder\nbecause reference to previous state is hidden.\n\nIf changes introduced to `master` branch result in conflicts, it should be merged with a merge commit (`git fetch upstream/master; git merge upstream/master`).\n\n### Minimum requirements\n\n1. Describe reasons and result of the change in the pull request comment.\n2. Do not force push to a pull request. The development history should be easily traceable.\n3. Any change to a public API requires appropriate documentation: params (and particularly interesting combinations of them if the method is complex), results, interesting, self-contained examples.\n4. Any change to behaviour needs to be reflected and validated with specs.\n5. Any change affecting the compiler or performance-critical features in the standard library\n   should be checked with benchmarks how it affects performance.\n\n### Reviews\n\nReviews are conducted by community members to validate a contribution and ensure quality standards are met.\nApprovals from Core Team members are required for accepting a pull requests. Other community members are encouraged to do reviews as well. Leave suggestions for improvements or approve a change when it looks good to you.\n\n1. Make sure the [formal minimum requirements](#minimum-requirements) are met, for the change itself and the PR. Cross check with the referenced issue(s).\n2. Check if CI is successful. If not, try to figure out what's wrong and add a comment about it. If a failure seems unrelated, maintainers can try to rerun the job.\n3. Leave inline comments when you want to request changes or ask for clarification. Suggestions are often understood as requirements, so make it clear if a proposal is optional or you're just asking for feedback.\n\n### Accepting a Pull Request\n\nThe process of accepting a pull request entails the following check list:\n\n1. At least two approvals by Core Team members; one approval if the author is a Core Team member. Only approvals based on the most recent code version count (ignoring minor changes like fixing a typo).\n2. There are no outstanding questions nor requested changes in the pull request and associated issues.\n3. Title and description appropriately represent the final state of the change.\n4. Proper labels are applied (usually at least a `topic:` and `kind:` label).\n5. Change is based on a fairly recent commit of the `master` branch. When in doubt, merge `master` and wait for CI.\n6. CI is green.\n\nWhen these conditions are met, a Core Team member can mark the pull request as accepted by adding it to the current development milestone.\nThis signals that the PR is scheduled to be merged soon and gives another chance for final reviews.\n\nThe current [development milestone](https://github.com/crystal-lang/crystal/milestones) is typically the milestone for the next release.\nDuring the freeze period of a release, feature enhancements are added to the milestone of the next release.\nFreeze periods are announced on the community forums and usually span two weeks before the scheduled date of a minor release.\n\n### Merge Queue\n\nThe current [development milestone](https://github.com/crystal-lang/crystal/milestones) serves as a merge queue. Open pull requests on that milestone\nare eligible for being merged.\n\nPending pull requests should usually stay in the queue for at least one full business day, allowing other reviewers to take a final look at it.\nThis wait time can be extended, for example for big changes or when there was a lot of recent activity in the discussion.\nUrgent bug and regression fixes can skip the line.\n\nIf reasonable objection or questions arise while waiting for merge, the pull request must be removed from the milestone until they are resolved.\n\nIt's good practice to have a single maintainer responsible for operating the merge queue.\n\n### Merging\n\nBefore merging, make sure the pull request has been on the merge queue for some time (usually 1+ business days) and there has not been any\nmore recent discussion that questions the current state of the change.\nIf conditions are met, the pull request can finally be merged. Use squash merge to not pollute the version history of the main branch with\ndetails of the pull request process. For non-trivial changes, the merge commit should contain a short description.\n\n### For maintainers with push access\n\n1. Do not directly commit to the `master` branch. Always create a feature branch and pull request.\n2. Feature branches should typically be created in your fork. The main repo should only contain essential branches.\n   - CI changes affecting circle CI only run for branches on the main repo. They should be prefixed `ci/` to trigger a maintenance release.\n   - Long-running feature branches that accept contributions must be pushed to the main repo in order to allow PRs targeting that branch.\n\n## Git pre-commit hook\n\nCode submitted to this repository should be formatted according to `crystal tool format`.\nA pre-commit hook can be installed into the local git repo to ensure the formatter validates every commit: <https://github.com/crystal-lang/crystal/blob/master/scripts/git/pre-commit>\n\nInstall the pre-commit hook:\n\n```sh\nln -s scripts/git/pre-commit .git/hooks\n```\n\n## Code of Conduct\n\nPlease note that this project is released with a [Contributor Code of Conduct][ccoc].\nBy participating in this project you agree to abide by its terms.\n\n[ccoc]: https://github.com/crystal-lang/crystal/blob/master/CODE_OF_CONDUCT.md\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\n----------------------------------------------------------------------------\n\nRuntime Library Exception to the Apache 2.0 License:\n\n   As an exception, if you use this Software to compile your source code\n   and portions of this Software are embedded into the binary product as a\n   result, you may redistribute such product without providing attribution\n   as would otherwise be required by Sections 4(a), 4(b) and 4(d) of the\n   License.\n"
  },
  {
    "path": "LICENSES/Apache-2.0.txt",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\n     (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and\n\n     (b) You must cause any modified files to carry prominent notices stating that You changed the files; and\n\n     (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\n     (d) If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\n\n     You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!)  The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSES/MIT.txt",
    "content": "MIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial\nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO\nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "LICENSES/Swift-exception.txt",
    "content": "### Runtime Library Exception to the Apache 2.0 License: ###\n\nAs an exception, if you use this Software to compile your source code and\nportions of this Software are embedded into the binary product as a result,\nyou may redistribute such product without providing attribution as would\notherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\n"
  },
  {
    "path": "Makefile",
    "content": "all:\n\n-include Makefile.local # for optional local options e.g. threads\n\n# Recipes for this Makefile\n\n## Build the compiler\n##   $ make\n## Build the compiler with progress output\n##   $ make progress=1\n## Clean up built files then build the compiler\n##   $ make clean crystal\n## Build the compiler in release mode\n##   $ make crystal release=1 interpreter=1\n## Run tests\n##   $ make test\n## Run stdlib tests\n##   $ make std_spec\n## Run compiler tests\n##   $ make compiler_spec\n## Run generators (Unicode, SSL config, ...)\n##   $ make -B generate_data\n\nCRYSTAL ?= crystal## which previous crystal compiler use\n\nrelease ?=        ## Compile in release mode\nstats ?=          ## Enable statistics output\nprogress ?=       ## Enable progress output\nthreads ?=        ## Maximum number of threads to use\ndebug ?=          ## Add symbolic debug info\nverbose ?=        ## Run specs in verbose mode\njunit_output ?=   ## Path to output junit results\nstatic ?=         ## Enable static linking\ntarget ?=         ## Cross-compilation target\ninterpreter ?=    ## Enable interpreter feature\ncheck ?=          ## Enable only check when running format\norder ?=random    ## Enable order for spec execution (values: \"default\" | \"random\" | seed number)\nderef_symlinks ?= ## Dereference symbolic links for `make install`\ndocs_sanitizer ?= ## Enable sanitization for documentation generation\nsequential_codegen ?=$(if $(filter 0,$(supports_preview_mt)),true,)## Enforce sequential codegen in compiler builds. Base compiler before Crystal 1.8 cannot build with `-Dpreview_mt -Dexecution_context`\n\nO := .build\nSOURCES := $(shell find src -name '*.cr')\nSPEC_SOURCES := $(shell find spec -name '*.cr')\nMAN1PAGES := $(patsubst doc/man/%.adoc,man/%.1,$(wildcard doc/man/*.adoc))\noverride FLAGS += -D strict_multi_assign -D preview_overload_order $(if $(release),--release )$(if $(stats),--stats )$(if $(progress),--progress )$(if $(threads),--threads $(threads) )$(if $(debug),-d )$(if $(static),--static )$(if $(LDFLAGS),--link-flags=\"$(LDFLAGS)\" )$(if $(target),--cross-compile --target $(target) )\n# NOTE: USE_PCRE1 is only used for testing compatibility with legacy environments that don't provide libpcre2.\n# Newly built compilers should never be distributed with libpcre to ensure syntax consistency.\noverride COMPILER_FLAGS += $(if $(interpreter),,-Dwithout_interpreter )$(if $(docs_sanitizer),,-Dwithout_libxml2 ) -Dwithout_openssl -Dwithout_zlib$(if $(sequential_codegen),, -Dpreview_mt -Dexecution_context) $(if $(USE_PCRE1),-Duse_pcre,-Duse_pcre2)\nSPEC_WARNINGS_OFF := --exclude-warnings spec/std --exclude-warnings spec/compiler --exclude-warnings spec/primitives --exclude-warnings src/float/printer --exclude-warnings src/random.cr\noverride SPEC_FLAGS += $(if $(verbose),-v )$(if $(junit_output),--junit_output $(junit_output) )$(if $(order),--order=$(order) )\nCRYSTAL_CONFIG_LIBRARY_PATH := '$$ORIGIN/../lib/crystal'\nifndef CRYSTAL_CONFIG_BUILD_COMMIT\n\tCRYSTAL_CONFIG_BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null)\nendif\nCRYSTAL_CONFIG_PATH := '$$ORIGIN/../share/crystal/src'\nifndef BASE_CRYSTAL_VERSION\n\tBASE_CRYSTAL_VERSION := $(shell $(CRYSTAL) env CRYSTAL_VERSION)\nendif\nifndef CRYSTAL_VERSION\n\tCRYSTAL_VERSION := $(shell cat src/VERSION)\nendif\nifndef SOURCE_DATE_EPOCH\n\tSOURCE_DATE_EPOCH := $(shell (cat src/SOURCE_DATE_EPOCH || (git show -s --format=%ct HEAD || stat -c \"%Y\" Makefile ||stat -f \"%m\" Makefile)) 2> /dev/null)\nendif\ncheck_lld := command -v ld.lld >/dev/null && case \"$$(uname -s)\" in MINGW32*|MINGW64*|Linux) echo 1;; esac\nifeq ($(shell $(check_lld)),1)\n  EXPORT_CC ?= CC=\"$(CC) -fuse-ld=lld\"\nendif\noverride EXPORTS += \\\n  CRYSTAL_CONFIG_BUILD_COMMIT=\"$(CRYSTAL_CONFIG_BUILD_COMMIT)\" \\\n\tCRYSTAL_CONFIG_PATH=$(CRYSTAL_CONFIG_PATH) \\\n\tSOURCE_DATE_EPOCH=\"$(SOURCE_DATE_EPOCH)\"\noverride EXPORTS_BUILD += \\\n\t$(EXPORT_CC) \\\n\tCRYSTAL_CONFIG_LIBRARY_PATH=$(CRYSTAL_CONFIG_LIBRARY_PATH)\nSHELL = sh\n\nifeq ($(LLVM_VERSION),)\n\tifndef LLVM_CONFIG\n  \tLLVM_CONFIG := $(shell src/llvm/ext/find-llvm-config.sh)\n\tendif\n\tLLVM_VERSION := $(if $(LLVM_CONFIG),$(shell \"$(LLVM_CONFIG)\" --version 2> /dev/null))\nendif\n\n# Crystal versions before 1.8 cannot build a functional compiler with `-Dpreview_mt` (https://github.com/crystal-lang/crystal/pull/16380)\nsupports_preview_mt := $(if $(filter 1.8.0,$(shell printf \"%s\\n%s\" \"1.8.0\" \"$(BASE_CRYSTAL_VERSION)\" | sort -V | tail -n1)),0,1)\n\nLLVM_EXT_DIR = src/llvm/ext\nLLVM_EXT_OBJ = $(LLVM_EXT_DIR)/llvm_ext.o\nCXXFLAGS += $(if $(debug),-g -O0)\n\n# MSYS2 support (native Windows should use `Makefile.win` instead)\nifeq ($(OS),Windows_NT)\n  EXE := .exe\n  WINDOWS := 1\nelse\n  EXE :=\n  WINDOWS :=\nendif\nCRYSTAL_BIN := crystal$(EXE)\n\nDESTDIR ?=\nPREFIX ?= /usr/local\nBINDIR ?= $(PREFIX)/bin\nLIBDIR ?= $(PREFIX)/lib\nDATADIR ?= $(PREFIX)/share\nDOCDIR ?= $(DATADIR)/doc/crystal\nMANDIR ?= $(DATADIR)/man\nINSTALL ?= /usr/bin/install\n\nifeq ($(or $(TERM),$(TERM),dumb),dumb)\n  colorize = $(shell printf \"%s\" \"$1\" >&2)\nelse\n  colorize = $(shell printf \"\\033[33m%s\\033[0m\\n\" \"$1\" >&2)\nendif\n\nDEPS = $(LLVM_EXT_OBJ)\nifneq ($(LLVM_VERSION),)\n  ifeq ($(shell test $(firstword $(subst ., ,$(LLVM_VERSION))) -ge 18; echo $$?),0)\n    DEPS =\n  endif\nendif\n\ncheck_llvm_config = $(eval \\\n\tcheck_llvm_config := $(if $(LLVM_VERSION),\\\n\t\t$(call colorize,Using $(or $(LLVM_CONFIG),externally configured LLVM) [version=$(LLVM_VERSION)]),\\\n\t\t$(error \"Could not locate compatible llvm-config, make sure it is installed and in your PATH, or set LLVM_VERSION / LLVM_CONFIG. Compatible versions: $(shell cat src/llvm/ext/llvm-versions.txt)))\\\n\t)\n\n.PHONY: all\nall: crystal ## Build all files (currently crystal only) [default]\n\n.PHONY: test\ntest: spec ## Run tests\n\n.PHONY: spec\nspec: std_spec primitives_spec compiler_spec\n\n.PHONY: std_spec\nstd_spec: $(O)/std_spec$(EXE) ## Run standard library specs\n\t$(O)/std_spec$(EXE) $(SPEC_FLAGS)\n\n.PHONY: compiler_spec\ncompiler_spec: $(O)/compiler_spec$(EXE) ## Run compiler specs\n\t$(O)/compiler_spec$(EXE) $(SPEC_FLAGS)\n\n.PHONY: primitives_spec\nprimitives_spec: $(O)/primitives_spec$(EXE) ## Run primitives specs\n\t$(O)/primitives_spec$(EXE) $(SPEC_FLAGS)\n\n.PHONY: interpreter_spec\ninterpreter_spec: $(O)/interpreter_spec$(EXE) ## Run interpreter specs\n\t$(O)/interpreter_spec$(EXE) $(SPEC_FLAGS)\n\n.PHONY: simple_smoke_test\nsimple_smoke_test: ## Build std specs as a smoke test\nsimple_smoke_test: $(O)/std_spec$(EXE)\n\n.PHONY: smoke_test\nsmoke_test: ## Build std specs, compiler specs and compiler as a smoke test\nsmoke_test: $(O)/std_spec$(EXE) $(O)/compiler_spec$(EXE) $(O)/$(CRYSTAL_BIN)\n\nSHELLCHECK_SOURCES := $(wildcard **/*.sh) $(wildcard **/*.bash) bin/crystal bin/ci bin/check-compiler-flag scripts/git/pre-commit\n\n.PHONY: lint-shellcheck\nlint-shellcheck:\n\tshellcheck --severity=warning $(SHELLCHECK_SOURCES)\n\n.PHONY: all_spec\nall_spec: $(O)/all_spec$(EXE) ## Run all specs (note: this builds a huge program; `test` recipe builds individual binaries and is recommended for reduced resource usage)\n\t$(O)/all_spec$(EXE) $(SPEC_FLAGS)\n\n.PHONY: samples\nsamples: ## Build example programs\n\t$(MAKE) -C samples\n\n.PHONY: docs\ndocs: ## Generate standard library documentation\n\t$(call check_llvm_config)\n\t./bin/crystal docs src/docs_main.cr $(DOCS_OPTIONS) --project-name=Crystal --project-version=$(CRYSTAL_VERSION) --source-refname=$(CRYSTAL_CONFIG_BUILD_COMMIT)\n\tcp -R -P -p doc/ docs/\n\n.PHONY: crystal\ncrystal: $(O)/$(CRYSTAL_BIN) ## Build the compiler\n\n.PHONY: deps llvm_ext\ndeps: $(DEPS) ## Build dependencies\nllvm_ext: $(LLVM_EXT_OBJ)\n\n.PHONY: format\nformat: ## Format sources\n\t./bin/crystal tool format$(if $(check), --check) src spec samples scripts\n\n.PHONY: generate_data\ngenerate_data: ## Run generator scripts for Unicode, SSL config, ...\n\t$(MAKE) -B -f scripts/generate_data.mk\n\n.PHONY: install\ninstall: $(O)/$(CRYSTAL_BIN) man/crystal.1.gz ## Install the compiler at DESTDIR\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(BINDIR)/\"\n\t$(INSTALL) -m 0755 \"$(O)/$(CRYSTAL_BIN)\" \"$(DESTDIR)$(BINDIR)/$(CRYSTAL_BIN)\"\n\n\t$(INSTALL) -d -m 0755 $(DESTDIR)$(DATADIR)/crystal\n\tcp -R -p $(if $(deref_symlinks),-L,-P) src \"$(DESTDIR)$(DATADIR)/crystal/src\"\n\trm -rf \"$(DESTDIR)$(DATADIR)/crystal/$(LLVM_EXT_OBJ)\" # Don't install llvm_ext.o\n\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(MANDIR)/man1/\"\n\t$(INSTALL) -m 644 man/crystal.1.gz \"$(DESTDIR)$(MANDIR)/man1/crystal.1.gz\"\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(DATADIR)/licenses/crystal/\"\n\t$(INSTALL) -m 644 LICENSE \"$(DESTDIR)$(DATADIR)/licenses/crystal/LICENSE\"\n\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(DATADIR)/bash-completion/completions/\"\n\t$(INSTALL) -m 644 etc/completion.bash \"$(DESTDIR)$(DATADIR)/bash-completion/completions/crystal\"\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(DATADIR)/zsh/site-functions/\"\n\t$(INSTALL) -m 644 etc/completion.zsh \"$(DESTDIR)$(DATADIR)/zsh/site-functions/_crystal\"\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(DATADIR)/fish/vendor_completions.d/\"\n\t$(INSTALL) -m 644 etc/completion.fish \"$(DESTDIR)$(DATADIR)/fish/vendor_completions.d/crystal.fish\"\n\nifeq ($(WINDOWS),1)\n.PHONY: install_dlls\ninstall_dlls: $(O)/$(CRYSTAL_BIN) ## Install the compiler's dependent DLLs at DESTDIR (Windows only)\n\t$(INSTALL) -d -m 0755 \"$(DESTDIR)$(BINDIR)/\"\n\t@ldd $(O)/$(CRYSTAL_BIN) | grep -iv ' => /c/windows/system32' | sed 's/.* => //; s/ (.*//' | xargs -t -i $(INSTALL) -m 0755 '{}' \"$(DESTDIR)$(BINDIR)/\"\nendif\n\n.PHONY: uninstall\nuninstall: ## Uninstall the compiler from DESTDIR\n\trm -f \"$(DESTDIR)$(BINDIR)/$(CRYSTAL_BIN)\"\n\n\trm -rf \"$(DESTDIR)$(DATADIR)/crystal/src\"\n\n\trm -f \"$(DESTDIR)$(MANDIR)/man1/crystal.1.gz\"\n\trm -f \"$(DESTDIR)$(DATADIR)/licenses/crystal/LICENSE\"\n\n\trm -f \"$(DESTDIR)$(DATADIR)/bash-completion/completions/crystal\"\n\trm -f \"$(DESTDIR)$(DATADIR)/zsh/site-functions/_crystal\"\n\trm -f \"$(DESTDIR)$(DATADIR)/fish/vendor_completions.d/crystal.fish\"\n\n.PHONY: install_docs\ninstall_docs: docs ## Install docs at DESTDIR\n\t$(INSTALL) -d -m 0755 $(DESTDIR)$(DOCDIR)\n\n\tcp -R -P -p docs \"$(DESTDIR)$(DOCDIR)/docs\"\n\tcp -R -P -p samples \"$(DESTDIR)$(DOCDIR)/examples\"\n\n.PHONY: uninstall_docs\nuninstall_docs: ## Uninstall docs from DESTDIR\n\trm -rf \"$(DESTDIR)$(DOCDIR)/docs\"\n\trm -rf \"$(DESTDIR)$(DOCDIR)/examples\"\n\n$(O)/all_spec$(EXE): $(DEPS) $(SOURCES) $(SPEC_SOURCES)\n\t$(call check_llvm_config)\n\t@mkdir -p $(O)\n\t$(EXPORT_CC) $(EXPORTS) ./bin/crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/all_spec.cr\n\n$(O)/std_spec$(EXE): $(DEPS) $(SOURCES) $(SPEC_SOURCES)\n\t$(call check_llvm_config)\n\t@mkdir -p $(O)\n\t$(EXPORT_CC) ./bin/crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/std_spec.cr\n\n$(O)/compiler_spec$(EXE): $(DEPS) $(SOURCES) $(SPEC_SOURCES)\n\t$(call check_llvm_config)\n\t@mkdir -p $(O)\n\t$(EXPORT_CC) $(EXPORTS) ./bin/crystal build $(FLAGS) $(COMPILER_FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/compiler_spec.cr --release\n\n$(O)/primitives_spec$(EXE): $(O)/$(CRYSTAL_BIN) $(DEPS) $(SOURCES) $(SPEC_SOURCES)\n\t@mkdir -p $(O)\n\t$(EXPORT_CC) ./bin/crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/primitives_spec.cr\n\n$(O)/interpreter_spec$(EXE): $(DEPS) $(SOURCES) $(SPEC_SOURCES)\n\t$(eval interpreter=1)\n\t@mkdir -p $(O)\n\t$(EXPORT_CC) ./bin/crystal build $(FLAGS) $(COMPILER_FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/compiler/interpreter_spec.cr\n\n$(O)/$(CRYSTAL_BIN): $(DEPS) $(SOURCES)\n\t$(call check_llvm_config)\n\t@mkdir -p $(O)\n\t$(EXPORTS) $(EXPORTS_BUILD) ./bin/crystal build $(FLAGS) $(COMPILER_FLAGS) -o $(if $(WINDOWS),$(O)/crystal-next.exe,$@) src/compiler/crystal.cr\n\t@# NOTE: on MSYS2 it is not possible to overwrite a running program, so the compiler must be first built with\n\t@# a different filename and then moved to the final destination.\n\t$(if $(WINDOWS),mv $(O)/crystal-next.exe $@)\n\n$(LLVM_EXT_OBJ): $(LLVM_EXT_DIR)/llvm_ext.cc\n\t$(call check_llvm_config)\n\t$(CXX) -c $(CXXFLAGS) -o $@ $< $(if $(LLVM_CONFIG),$(shell $(LLVM_CONFIG) --cxxflags))\n\nman/: $(MAN1PAGES)\n\nman/%.gz: man/%\n\tgzip -c -9 $< > $@\n\nman/%.1: doc/man/%.adoc\n\tSOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) asciidoctor -a crystal_version=$(CRYSTAL_VERSION) $< -b manpage -o $@\n\n.PHONY: clean\nclean: clean_crystal ## Clean up built directories and files\n\trm -rf $(LLVM_EXT_OBJ)\n\trm -rf man/*.gz\n\n.PHONY: clean_crystal\nclean_crystal: ## Clean up crystal built files\n\trm -rf $(O)\n\trm -rf ./docs\n\n.PHONY: clean_cache\nclean_cache: ## Clean up CRYSTAL_CACHE_DIR files\n\trm -rf $(shell ./bin/crystal env CRYSTAL_CACHE_DIR)\n\n.PHONY: help\nhelp: ## Show this help\n\t@echo\n\t@printf '\\033[34mtargets:\\033[0m\\n'\n\t@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\\\n\t\tsort |\\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\t@echo\n\t@printf '\\033[34moptional variables:\\033[0m\\n'\n\t@grep -hE '^[a-zA-Z_-]+ \\?=.*?## .*$$' $(MAKEFILE_LIST) |\\\n\t\tsort |\\\n\t\tawk 'BEGIN {FS = \" \\\\?=.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\t@echo\n\t@printf '\\033[34mrecipes:\\033[0m\\n'\n\t@grep -hE '^##.*$$' $(MAKEFILE_LIST) |\\\n\t\tawk 'BEGIN {FS = \"## \"}; /^## [a-zA-Z_-]/ {printf \"  \\033[36m%s\\033[0m\\n\", $$2}; /^##  / {printf \"  %s\\n\", $$2}'\n"
  },
  {
    "path": "Makefile.win",
    "content": "all:\r\n\r\n-include Makefile.win.local # for optional local options e.g. threads\r\n\r\n# Recipes for this Makefile\r\n\r\n## Build the compiler\r\n##   $ make -f Makefile.win\r\n## Build the compiler with progress output\r\n##   $ make -f Makefile.win progress=1\r\n## Clean up built files then build the compiler\r\n##   $ make -f Makefile.win clean crystal\r\n## Build the compiler in release mode\r\n##   $ make -f Makefile.win crystal release=1 interpreter=1\r\n## Run tests\r\n##   $ make -f Makefile.win test\r\n## Run stdlib tests\r\n##   $ make -f Makefile.win std_spec\r\n## Run compiler tests\r\n##   $ make -f Makefile.win compiler_spec\r\n## Run generators (Unicode, SSL config, ...)\r\n##   $ make -B generate_data\r\n\r\nCRYSTAL ?= crystal ## which previous crystal compiler use\r\n\r\nrelease ?=        ## Compile in release mode\r\nstats ?=          ## Enable statistics output\r\nprogress ?=       ## Enable progress output\r\nthreads ?=        ## Maximum number of threads to use\r\ndebug ?=          ## Add symbolic debug info\r\nverbose ?=        ## Run specs in verbose mode\r\njunit_output ?=   ## Path to output junit results\r\nstatic ?=         ## Enable static linking\r\ntarget ?=         ## Cross-compilation target\r\ninterpreter ?=    ## Enable interpreter feature\r\ncheck ?=          ## Enable only check when running format\r\norder ?=random    ## Enable order for spec execution (values: \"default\" | \"random\" | seed number)\r\ndocs_sanitizer ?= ## Enable sanitization for documentation generation\r\nsequential_codegen ?= ## Enforce sequential codegen in compiler builds. Base compiler before Crystal 1.8 cannot build with `-Dpreview_mt`\r\n\r\nMAKEFLAGS += --no-builtin-rules\r\n.SUFFIXES:\r\n\r\nSHELL := cmd.exe\r\nCXX := cl.exe\r\nRC := rc.exe\r\n\r\nGLOB = $(shell dir $1 /B /S)\r\nMKDIR = if not exist $1 mkdir $1\r\nCP = copy /B /Y $1 $2\r\nCPDIR = robocopy /E /NJH /NJS /NS /NC /NP $1 $2 & if %%ERRORLEVEL%% GEQ 8 exit /B 1\r\nINSTALL = copy /B /Y $1 $2\r\nINSTALLDIR = robocopy /E /NJH /NJS /NS /NC /NP $1 $2 & if %%ERRORLEVEL%% GEQ 8 exit /B 1\r\nMV = move /Y $1 $2\r\nRM = if exist $1 del /F /Q $1\r\nRMDIR = if exist $1 rd /S /Q $1\r\n\r\nO := .build\r\nSOURCES := $(call GLOB,src\\\\*.cr)\r\nSPEC_SOURCES := $(call GLOB,spec\\\\*.cr)\r\noverride FLAGS += -D strict_multi_assign -D preview_overload_order $(if $(release),--release )$(if $(stats),--stats )$(if $(progress),--progress )$(if $(threads),--threads $(threads) )$(if $(debug),-d )$(if $(static),--static )$(if $(LDFLAGS),--link-flags=\"$(LDFLAGS)\" )$(if $(target),--cross-compile --target $(target) )\r\n# NOTE: USE_PCRE1 is only used for testing compatibility with legacy environments that don't provide libpcre2.\r\n# Newly built compilers should never be distributed with libpcre to ensure syntax consistency.\r\noverride COMPILER_FLAGS += $(if $(interpreter),,-Dwithout_interpreter )$(if $(docs_sanitizer),,-Dwithout_libxml2 ) -Dwithout_openssl -Dwithout_zlib$(if $(sequential_codegen),, -Dpreview_mt -Dexecution_context) $(if $(USE_PCRE1),-Duse_pcre,-Duse_pcre2)\r\nSPEC_WARNINGS_OFF := --exclude-warnings spec\\std --exclude-warnings spec\\compiler --exclude-warnings spec\\primitives --exclude-warnings src\\float\\printer\r\nSPEC_FLAGS := $(if $(verbose),-v )$(if $(junit_output),--junit_output $(junit_output) )$(if $(order),--order=$(order) )\r\nCRYSTAL_CONFIG_LIBRARY_PATH := $$ORIGIN\\lib\r\nCRYSTAL_CONFIG_BUILD_COMMIT := $(shell git rev-parse --short HEAD)\r\nCRYSTAL_CONFIG_PATH := $$ORIGIN\\src\r\nifndef CRYSTAL_VERSION\r\n\tCRYSTAL_VERSION := $(shell type src\\VERSION)\r\nendif\r\nifndef SOURCE_DATE_EPOCH\r\n\tSOURCE_DATE_EPOCH := $(or $(shell type src\\SOURCE_DATE_EPOCH 2>NUL),$(shell git show -s --format=%ct HEAD))\r\nendif\r\nexport_vars = $(eval export CRYSTAL_CONFIG_BUILD_COMMIT CRYSTAL_CONFIG_PATH SOURCE_DATE_EPOCH)\r\nexport_build_vars = $(eval export CRYSTAL_CONFIG_LIBRARY_PATH)\r\n\r\nifeq ($(LLVM_VERSION),)\r\n  LLVM_CONFIG ?=\r\n  LLVM_VERSION := $(if $(LLVM_CONFIG),$(shell \"$(LLVM_CONFIG)\" --version))\r\nendif\r\n\r\nLLVM_EXT_DIR = src\\llvm\\ext\r\nLLVM_EXT_OBJ = $(LLVM_EXT_DIR)\\llvm_ext.obj\r\nCXXFLAGS += $(if $(static),$(if $(debug),/MTd /Od ,/MT ),$(if $(debug),/MDd /Od ,/MD ))\r\n\r\nprefix ?= $(or $(ProgramW6432),$(ProgramFiles))\\crystal\r\nBINDIR ?= $(prefix)\r\nLIBDIR ?= $(prefix)\\lib\r\nSRCDIR ?= $(prefix)\\src\r\nDATADIR ?= $(prefix)\r\n\r\ncolorize = $(info $1)\r\n\r\nDEPS = $(LLVM_EXT_OBJ)\r\nifneq ($(LLVM_VERSION),)\r\n  ifeq ($(shell if $(firstword $(subst ., ,$(LLVM_VERSION))) GEQ 18 echo 0),0)\r\n    DEPS =\r\n  endif\r\nendif\r\n\r\ncheck_llvm_config = $(eval \\\r\n\tcheck_llvm_config := $(if $(LLVM_VERSION),\\\r\n\t\t$(call colorize,Using $(or $(LLVM_CONFIG),externally configured LLVM) [version=$(LLVM_VERSION)]),\\\r\n\t\t$(error \"Could not locate compatible llvm-config, make sure it is installed and in your PATH, or set LLVM_VERSION / LLVM_CONFIG. Compatible versions: $(shell type src\\llvm\\ext\\llvm-versions.txt)))\\\r\n\t)\r\n\r\n.PHONY: all\r\nall: crystal ## Build all files (currently crystal only) [default]\r\n\r\n.PHONY: test\r\ntest: spec ## Run tests\r\n\r\n.PHONY: spec\r\nspec: std_spec primitives_spec compiler_spec\r\n\r\n.PHONY: std_spec\r\nstd_spec: $(O)\\std_spec.exe ## Run standard library specs\r\n\t$(O)\\std_spec $(SPEC_FLAGS)\r\n\r\n.PHONY: compiler_spec\r\ncompiler_spec: $(O)\\compiler_spec.exe ## Run compiler specs\r\n\t$(O)\\compiler_spec $(SPEC_FLAGS)\r\n\r\n.PHONY: primitives_spec\r\nprimitives_spec: $(O)\\primitives_spec.exe ## Run primitives specs\r\n\t$(O)\\primitives_spec $(SPEC_FLAGS)\r\n\r\n.PHONY: interpreter_spec\r\ninterpreter_spec: $(O)\\interpreter_spec ## Run interpreter specs\r\n\t$(O)\\interpreter_spec $(SPEC_FLAGS)\r\n\r\n.PHONY: smoke_test\r\nsmoke_test: ## Build specs as a smoke test\r\nsmoke_test: $(O)\\std_spec.exe $(O)\\compiler_spec.exe $(O)\\crystal.exe\r\n\r\n.PHONY: all_spec\r\nall_spec: $(O)\\all_spec.exe ## Run all specs (note: this builds a huge program; `test` recipe builds individual binaries and is recommended for reduced resource usage)\r\n\t$(O)\\all_spec $(SPEC_FLAGS)\r\n\r\n.PHONY: samples\r\nsamples: ## Build example programs\r\n\t$(MAKE) -C samples -f $(MAKEFILE_LIST)\r\n\r\n.PHONY: docs\r\ndocs: ## Generate standard library documentation\r\n\t$(call check_llvm_config)\r\n\t.\\bin\\crystal docs src\\docs_main.cr $(DOCS_OPTIONS) --project-name=Crystal --project-version=$(CRYSTAL_VERSION) --source-refname=$(CRYSTAL_CONFIG_BUILD_COMMIT)\r\n\r\n.PHONY: crystal\r\ncrystal: $(O)\\crystal.exe ## Build the compiler\r\n\r\n.PHONY: deps llvm_ext\r\ndeps: $(DEPS) ## Build dependencies\r\nllvm_ext: $(LLVM_EXT_OBJ)\r\n\r\n.PHONY: format\r\nformat: ## Format sources\r\n\t.\\bin\\crystal tool format$(if $(check), --check) src spec samples scripts\r\n\r\n.PHONY: generate_data\r\ngenerate_data: ## Run generator scripts for Unicode, SSL config, ...\r\n\t$(MAKE) -B -f scripts/generate_data.mk\r\n\r\n.PHONY: install\r\ninstall: $(O)\\crystal.exe ## Install the compiler at prefix\r\n\t$(call MKDIR,\"$(BINDIR)\")\r\n\t$(call INSTALL,\"$(O)\\crystal.exe\",\"$(BINDIR)\\crystal.exe\")\r\n\t$(call INSTALL,\"$(O)\\crystal.pdb\",\"$(BINDIR)\\crystal.pdb\")\r\n\r\n\t$(call MKDIR,\"$(DATADIR)\")\r\n\t$(call INSTALLDIR,src,\"$(DATADIR)\\src\")\r\n\t$(call RM,\"$(DATADIR)\\$(LLVM_EXT_OBJ)\")\r\n\r\n\t$(call INSTALL,LICENSE,\"$(DATADIR)\\LICENSE.txt\")\r\n\r\n.PHONY: uninstall\r\nuninstall: ## Uninstall the compiler from prefix\r\n\t$(call RM,\"$(DATADIR)\\LICENSE.txt\")\r\n\r\n\t$(call RMDIR,\"$(DATADIR)\\src\")\r\n\r\n\t$(call RM,\"$(BINDIR)\\crystal.exe\")\r\n\t$(call RM,\"$(BINDIR)\\crystal.pdb\")\r\n\r\n.PHONY: install_docs\r\ninstall_docs: docs ## Install docs at prefix\r\n\t$(call MKDIR,\"$(DATADIR)\")\r\n\t$(call INSTALLDIR,docs,\"$(DATADIR)\\docs\")\r\n\t$(call INSTALLDIR,samples,\"$(DATADIR)\\examples\")\r\n\r\n.PHONY: uninstall_docs\r\nuninstall_docs: ## Uninstall docs from prefix\r\n\t$(call RMDIR,\"$(DATADIR)\\docs\")\r\n\t$(call RMDIR,\"$(DATADIR)\\examples\")\r\n\r\n$(O)\\all_spec.exe: $(DEPS) $(SOURCES) $(SPEC_SOURCES)\r\n\t$(call check_llvm_config)\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t$(call export_vars)\r\n\t.\\bin\\crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o \"$@\" spec\\all_spec.cr\r\n\r\n$(O)\\std_spec.exe: $(DEPS) $(SOURCES) $(SPEC_SOURCES)\r\n\t$(call check_llvm_config)\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t.\\bin\\crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o \"$@\" spec\\std_spec.cr\r\n\r\n$(O)\\compiler_spec.exe: $(DEPS) $(SOURCES) $(SPEC_SOURCES)\r\n\t$(call check_llvm_config)\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t$(call export_vars)\r\n\t.\\bin\\crystal build $(FLAGS) $(COMPILER_FLAGS) $(SPEC_WARNINGS_OFF) -o \"$@\" spec\\compiler_spec.cr --release\r\n\r\n$(O)\\primitives_spec.exe: $(O)\\crystal.exe $(DEPS) $(SOURCES) $(SPEC_SOURCES)\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t.\\bin\\crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o \"$@\" spec\\primitives_spec.cr\r\n\r\n$(O)\\interpreter_spec: $(DEPS) $(SOURCES) $(SPEC_SOURCES)\r\n\t$(eval interpreter=1)\r\n\t@$(call MKDIR, \"$(O)\")\r\n\t.\\bin\\crystal build $(FLAGS) $(COMPILER_FLAGS)  $(SPEC_WARNINGS_OFF) -o $@ spec\\compiler\\interpreter_spec.cr\r\n\r\n$(O)\\crystal.exe: $(DEPS) $(SOURCES) $(O)\\crystal.res\r\n\t$(call check_llvm_config)\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t$(call export_vars)\r\n\t$(call export_build_vars)\r\n\t.\\bin\\crystal build $(FLAGS) $(COMPILER_FLAGS)  -o \"$(O)\\crystal-next.exe\" src\\compiler\\crystal.cr --link-flags=/PDBALTPATH:crystal.pdb \"--link-flags=$(realpath $(O)\\crystal.res)\"\r\n\t$(call MV,\"$(O)\\crystal-next.exe\",\"$@\")\r\n\t$(call MV,\"$(O)\\crystal-next.pdb\",\"$(O)\\crystal.pdb\")\r\n\r\n$(O)\\crystal.res: $(O)\\crystal.rc\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t$(RC) /nologo /Fo \"$@\" \"$<\"\r\n\r\n$(O)\\crystal.rc: $(MAKEFILE_LIST)\r\n\t@$(call MKDIR,\"$(O)\")\r\n\t$(MAKE) -s -f $(MAKEFILE_LIST) rc > \"$@\"\r\n\r\n$(LLVM_EXT_OBJ): $(LLVM_EXT_DIR)\\llvm_ext.cc\r\n\t$(call check_llvm_config)\r\n\t@rem Disable some harmless warnings:\r\n\t@rem - warning C4244: 'initializing': conversion from '_Ty' to '_Ty2', possible loss of data\r\n\t@rem - warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc\r\n\t@rem - warning C4624: '...': destructor was implicitly defined as deleted\r\n\t$(CXX) /nologo /c $(CXXFLAGS) /WX /wd4244 /wd4624 /wd4530 \"/Fo$@\" \"$<\" $(if $(LLVM_CONFIG),$(shell $(LLVM_CONFIG) --cxxflags))\r\n\r\n.PHONY: clean\r\nclean: clean_crystal ## Clean up built directories and files\r\n\t$(call RM,\"$(LLVM_EXT_OBJ)\")\r\n\r\n.PHONY: clean_crystal\r\nclean_crystal: ## Clean up crystal built files\r\n\t$(call RMDIR,\"$(O)\")\r\n\t$(call RMDIR,docs)\r\n\r\n.PHONY: clean_cache\r\nclean_cache: ## Clean up CRYSTAL_CACHE_DIR files\r\n\t$(call RMDIR,\"$(shell .\\bin\\crystal env CRYSTAL_CACHE_DIR)\")\r\n\r\n.PHONY: rc\r\nrc: ## Write compiler resource script to standard output\r\n\t$(eval comma := ,)\r\n\t$(eval ver_comma := $(subst .,$(comma),$(subst -dev,,$(CRYSTAL_VERSION))))\r\n\t$(eval source_date := $(shell git show -s --format=%cs HEAD))\r\n\t$(eval source_year := $(firstword $(subst -, ,$(source_date))))\r\n\t$(eval ver_full := $(CRYSTAL_VERSION)$(if $(CRYSTAL_CONFIG_BUILD_COMMIT), [$(CRYSTAL_CONFIG_BUILD_COMMIT)])$(if $(source_date), ($(source_date))))\r\n\t@setlocal EnableDelayedExpansion &\\\r\n\techo #include ^<windows.h^>&\\\r\n\techo.&\\\r\n\tset \"_dir=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))\" &\\\r\n\techo 1 ICON ^\"!_dir:/=\\\\!src\\\\compiler\\\\crystal\\\\tools\\\\playground\\\\public\\\\favicon.ico^\"&\\\r\n\techo.&\\\r\n\techo VS_VERSION_INFO VERSIONINFO&\\\r\n\techo     PRODUCTVERSION $(ver_comma),0&\\\r\n\techo     FILEVERSION    $(ver_comma),1&\\\r\n\techo     FILEFLAGSMASK  VS_FFI_FILEFLAGSMASK&\\\r\n\techo     FILEFLAGS      $(if $(findstring -dev,$(CRYSTAL_VERSION)),VS_FF_PRERELEASE,0)&\\\r\n\techo     FILEOS         VOS_NT_WINDOWS32&\\\r\n\techo     FILETYPE       VFT_APP&\\\r\n\techo     FILESUBTYPE    VFT2_UNKNOWN&\\\r\n\techo BEGIN&\\\r\n\techo     BLOCK \"StringFileInfo\"&\\\r\n\techo     BEGIN&\\\r\n\techo         BLOCK \"040904B0\"&\\\r\n\techo         BEGIN&\\\r\n\techo             VALUE \"ProductName\",      \"Crystal\"&\\\r\n\techo             VALUE \"ProductVersion\",   \"$(ver_full)\"&\\\r\n\techo             VALUE \"FileVersion\",      \"$(ver_full)\"&\\\r\n\techo             VALUE \"FileDescription\",  \"Crystal$(if $(LLVM_CONFIG), $(shell $(LLVM_CONFIG) --host-target))\"&\\\r\n\techo             VALUE \"Comments\",         \"$(if $(LLVM_VERSION),LLVM $(LLVM_VERSION))\"&\\\r\n\techo             VALUE \"InternalName\",     \"crystal.exe\"&\\\r\n\techo             VALUE \"OriginalFilename\", \"crystal.exe\"&\\\r\n\techo             VALUE \"CompanyName\",      \"Manas Technology Solutions\"&\\\r\n\techo             VALUE \"LegalCopyright\",   \"Copyright 2012-$(source_year) Manas Technology Solutions\"&\\\r\n\techo         END&\\\r\n\techo     END&\\\r\n\techo     BLOCK \"VarFileInfo\"&\\\r\n\techo     BEGIN&\\\r\n\techo         VALUE \"Translation\", 0x0409, 0x04B0&\\\r\n\techo     END&\\\r\n\techo END\r\n\r\n.PHONY: help\r\nhelp: ## Show this help\r\n\t@setlocal EnableDelayedExpansion &\\\r\n\techo. &\\\r\n\techo targets: &\\\r\n\t(for /F \"usebackq tokens=1* delims=:\" %%g in ($(MAKEFILE_LIST)) do (\\\r\n\t\tif not \"%%h\" == \"\" (\\\r\n\t\t\tset \"_line=%%g                \" &\\\r\n\t\t\tset \"_rest=%%h\" &\\\r\n\t\t\tset \"_comment=!_rest:* ## =!\" &\\\r\n\t\t\tif not \"!_comment!\" == \"!_rest!\"\\\r\n\t\t\t\tif \"!_line:_rest=!\" == \"!_line!\"\\\r\n\t\t\t\t\techo   !_line:~0,16!!_comment!\\\r\n\t\t)\\\r\n\t)) &\\\r\n\techo. &\\\r\n\techo optional variables: &\\\r\n\t(for /F \"usebackq tokens=1,3 delims=?#\" %%g in ($(MAKEFILE_LIST)) do (\\\r\n\t\tif not \"%%h\" == \"\" (\\\r\n\t\t\tset \"_var=%%g              \" &\\\r\n\t\t\techo   !_var:~0,14! %%h\\\r\n\t\t)\\\r\n\t)) &\\\r\n\techo. &\\\r\n\techo recipes: &\\\r\n\t(for /F \"usebackq tokens=* delims=\" %%g in ($(MAKEFILE_LIST)) do (\\\r\n\t\tset \"_line=%%g\" &\\\r\n\t\tif \"!_line:~0,7!\" == \"##   $$ \" (\\\r\n\t\t\techo !_name! &\\\r\n\t\t\techo  !_line:~2!\\\r\n\t\t) else if \"!_line:~0,3!\" == \"## \"\\\r\n\t\t\tset \"_name=  !_line:~3!\"\\\r\n\t))\r\n"
  },
  {
    "path": "NOTICE.md",
    "content": "# Crystal Programming Language\n\nCopyright 2012-2026 Manas Technology Solutions.\n\nThis product includes software developed at Manas Technology Solutions (<https://manas.tech/>).\n\nApache License v2.0 with Swift exception applies to all works unless specified\notherwise:\n\nPlease see [REUSE.toml](REUSE.toml) and [LICENSE](LICENSE) for additional\ncopyright and licensing information.\n\n- This repository includes vendored libraries (shards) in `/lib/` which have\n  their own licenses. See [REUSE.toml](REUSE.toml) for details.\n- Crystal playground includes vendored libraries with their own licenses. See\n  [src/compiler/crystal/tools/playground/public/vendor/REUSE.toml](src/compiler/crystal/tools/playground/public/vendor/REUSE.toml)\n  for details.\n\n## External libraries information\n\nCrystal compiler links the following libraries, which have their own license:\n\n- [LLVM][] - [Apache-2.0 with LLVM exceptions][]\n- [PCRE or PCRE2][] - [BSD-3][]\n- [libevent2][] - [BSD-3][]\n- [libiconv][] - [LGPLv3][]\n- [bdwgc][] - [MIT][]\n\nCrystal compiler calls the following tools as external process on compiling, which have their own license:\n\n- [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) - [GPLv3]\n\nCrystal standard library uses the following libraries, which have their own licenses:\n\n- [LLVM][] - [Apache-2.0 with LLVM exceptions][]\n- [PCRE or PCRE2][] - [BSD-3][]\n- [libevent2][] - [BSD-3][]\n- [libiconv][] - [LGPLv3][]\n- [bdwgc][] - [MIT][]\n- [Zlib][] - [Zlib][Zlib-license]\n- [OpenSSL][] - [Apache-2.0][]\n- [Libxml2][] - [MIT][]\n- [LibYAML][] - [MIT][]\n- [readline][] - [GPLv3][]\n- [GMP][] - [LGPLv3][]\n\n<!-- licenses -->\n[Apache-2.0]: https://www.openssl.org/source/apache-license-2.0.txt\n[Apache-2.0 with LLVM exceptions]: https://raw.githubusercontent.com/llvm/llvm-project/main/llvm/LICENSE.TXT\n[BSD-3]: https://opensource.org/licenses/BSD-3-Clause\n[GPLv3]: https://www.gnu.org/licenses/gpl-3.0.en.html\n[LGPLv3]: https://www.gnu.org/licenses/lgpl-3.0.en.html\n[MIT]: https://opensource.org/licenses/MIT\n[Zlib-license]: https://opensource.org/licenses/Zlib\n<!-- libraries -->\n[bdwgc]: http://www.hboehm.info/gc/\n[GMP]: https://gmplib.org/\n[libevent2]: http://libevent.org/\n[libiconv]: https://www.gnu.org/software/libiconv/\n[Libxml2]: http://xmlsoft.org/\n[LibYAML]: http://pyyaml.org/wiki/LibYAML\n[LLVM]: http://llvm.org/\n[OpenSSL]: https://www.openssl.org/\n[PCRE or PCRE2]: http://pcre.org/\n[readline]: https://tiswww.case.edu/php/chet/readline/rltop.html\n[Zlib]: http://www.zlib.net/\n"
  },
  {
    "path": "README.md",
    "content": "# Crystal\n\n[![Linux CI Build Status](https://github.com/crystal-lang/crystal/workflows/Linux%20CI/badge.svg)](https://github.com/crystal-lang/crystal/actions?query=workflow%3A%22Linux+CI%22+event%3Apush+branch%3Amaster)\n[![macOS CI Build Status](https://github.com/crystal-lang/crystal/workflows/macOS%20CI/badge.svg)](https://github.com/crystal-lang/crystal/actions?query=workflow%3A%22macOS+CI%22+event%3Apush+branch%3Amaster)\n[![AArch64 CI Build Status](https://github.com/crystal-lang/crystal/workflows/AArch64%20CI/badge.svg)](https://github.com/crystal-lang/crystal/actions?query=workflow%3A%22AArch64+CI%22+event%3Apush+branch%3Amaster)\n[![Windows CI Build Status](https://github.com/crystal-lang/crystal/workflows/Windows%20CI/badge.svg)](https://github.com/crystal-lang/crystal/actions?query=workflow%3A%22Windows+CI%22+event%3Apush+branch%3Amaster)\n[![CircleCI Build Status](https://circleci.com/gh/crystal-lang/crystal/tree/master.svg?style=shield)](https://circleci.com/gh/crystal-lang/crystal)\n[![Join the chat at https://gitter.im/crystal-lang/crystal](https://badges.gitter.im/crystal-lang/crystal.svg)](https://gitter.im/crystal-lang/crystal)\n[![Code Triagers Badge](https://www.codetriage.com/crystal-lang/crystal/badges/users.svg)](https://www.codetriage.com/crystal-lang/crystal)\n\n---\n\n[![Crystal - Born and raised at Manas](doc/assets/crystal-born-and-raised.svg)](https://manas.tech/)\n\nCrystal is a programming language with the following goals:\n\n- Have a syntax similar to Ruby (but compatibility with it is not a goal)\n- Statically type-checked but without having to specify the type of variables or method arguments.\n- Be able to call C code by writing bindings to it in Crystal.\n- Have compile-time evaluation and generation of code, to avoid boilerplate code.\n- Compile to efficient native code.\n\n## Why?\n\nWe love Ruby's efficiency for writing code.\n\nWe love C's efficiency for running code.\n\nWe want the best of both worlds.\n\nWe want the compiler to understand what we mean without having to specify types everywhere.\n\nWe want full OOP.\n\nOh, and we don't want to write C code to make the code run faster.\n\n## Project Status\n\nWithin a major version, language features won't be removed or changed in any way that could prevent a Crystal program written with that version from compiling and working. The built-in standard library might be enriched, but it will always be done with backwards compatibility in mind.\n\nDevelopment of the Crystal language is possible thanks to the community's effort and the continued support of [84codes](https://www.84codes.com/) and every other [sponsor](https://crystal-lang.org/sponsors).\n\n## Installing\n\n[Follow these installation instructions](https://crystal-lang.org/install)\n\n## Try it online\n\n[play.crystal-lang.org](https://play.crystal-lang.org/)\n\n## Documentation\n\n- [Language Reference](http://crystal-lang.org/reference)\n- [Standard library API](https://crystal-lang.org/api)\n- [Roadmap](https://github.com/crystal-lang/crystal/wiki/Roadmap)\n\n## Community\n\nHave any questions or suggestions? Ask on the [Crystal Forum](https://forum.crystal-lang.org), on our [Gitter channel](https://gitter.im/crystal-lang/crystal) or IRC channel [#crystal-lang](https://web.libera.chat/#crystal-lang) at irc.libera.chat, or on Stack Overflow under the [crystal-lang](http://stackoverflow.com/questions/tagged/crystal-lang) tag. There is also an archived [Google Group](https://groups.google.com/forum/?fromgroups#!forum/crystal-lang).\n\n## Contributing\n\nThe Crystal repository is hosted at [crystal-lang/crystal](https://github.com/crystal-lang/crystal) on GitHub.\n\nRead the general [Contributing guide](https://github.com/crystal-lang/crystal/blob/master/CONTRIBUTING.md), and then:\n\n1. Fork it (<https://github.com/crystal-lang/crystal/fork>)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n"
  },
  {
    "path": "REUSE.toml",
    "content": "version = 1\n\n# All original code in this repository is licensed under Apache 2.0 with Swift\n# exception.\n[[annotations]]\npath = [\"**\"]\nSPDX-FileCopyrightText = \"Copyright 2012-2026 Manas Technology Solutions.\"\nSPDX-License-Identifier = \"Apache-2.0 WITH Swift-exception\"\n\n# Unset license information for vendored libraries (\"shards\"). They either\n# provide their own information or there's a specific section in this file.\n[[annotations]]\npath = [\"lib/*/**\"]\n\n[[annotations]]\npath = \"lib/markd/**\"\nSPDX-FileCopyrightText = \"Copyright (c) 2017-present icyleaf\"\nSPDX-License-Identifier = \"MIT\"\n\n[[annotations]]\npath = \"lib/reply/**\"\nSPDX-FileCopyrightText = \"Copyright (c) 2022 I3oris <boris31.resch@gmail.com>\"\nSPDX-License-Identifier = \"MIT\"\n\n[[annotations]]\npath = \"lib/sanitize/**\"\nSPDX-FileCopyrightText = \"Copyright (c) 2020 Johannes Müller <straightshoota@gmail.com>\"\nSPDX-License-Identifier = \"Apache-2.0\"\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Reporting a Vulnerability\n\nShould you find any security-related issue, please _do not share them openly_ in\nthe issue tracker. We have a dedicated mailbox where we keep track of them:\n\n<security@manas.tech>\n\nYou can encrypt your message via [Keybase](https://keybase.io/encrypt) (set\nrecipient `crystal`) or with our [PGP key](https://crystal-lang.org/community/crystal-pgp-key.txt)\n(fingerprint `5995 C83C D754 BE44 8164 1929 0961 7FD3 7CC0 6B54`)\nalso available on [Keybase server](https://keybase.io/crystal/pgp_keys.asc).\n"
  },
  {
    "path": "UPGRADING.md",
    "content": "# Upgrading\n\nThis guide provides instructions for upgrading Crystal from one release to the next.\nUpgrades must be run sequentially, meaning you should not skip minor/major releases while upgrading.\n\nCrystal commits to a backwards-compatibility guarantee that code should continue\nto work with all future minor releases of the same major release series.\n\nStill, even bug fixes introduce changes that may break existing code in some edge cases.\nWe're only listing the most relevant changes here that could have a relevant impact.\n\nThe [changelogs](./doc/changelogs/README.md) contain more information about all changes in\na specific release.\n\n## Crystal 1.13\n\n- `CRYSTAL_LIBRARY_RPATH` and the `preview_win32_delay_load` feature flag have\n  been removed. Individual DLLs can be explicitly delay-loaded with the MSVC\n  toolchain by using `/DELAYLOAD` as a linker flag. Similarly RPATH can be added\n  with GCC or Clang toolchains by adding `-Wl,-rpath`.\n\n## Crystal 1.9\n\n- The implementation of the comparison operator `#<=>` between `Big*` (`BigDecimal`,\n  `BigFloat`, `BigInt`, `BigRational`) and `Float` (`Float32`, `Float64`) number types\n  is now nilable. When invoking these comparisons, `Nil` values must be handled.\n"
  },
  {
    "path": "_typos.toml",
    "content": "[default.extend-words]\nflate = \"flate\"\n\n[default.extend-identifiers]\nACCES = \"ACCES\"\nba = \"ba\"\nba2 = \"ba2\"\nbui = \"bui\"\ncategory_Nd = \"category_Nd\"\nDW_AT_endianity = \"DW_AT_endianity\"\nEXTA = \"EXTA\"\nGC_get_thr_restart_signal = \"GC_get_thr_restart_signal\"\nget_thr_restart_signal = \"get_thr_restart_signal\"\nIPPROTO_ND = \"IPPROTO_ND\"\nIST = \"IST\"\niTolen = \"iTolen\"\niy = \"iy\"\nlarg = \"larg\"\nNd = \"Nd\"\nnumer = \"numer\"\nOLT = \"OLT\"\npendings = \"pendings\"\nRELA = \"RELA\"\nRPC_S_CALL_FAILED_DNE = \"RPC_S_CALL_FAILED_DNE\"\nSEH = \"SEH\"\nsetup_seh_handler = \"setup_seh_handler\"\nusri4_parms = \"usri4_parms\"\nmsg_controllen = \"msg_controllen\"\n__crystal_sigfault_handler = \"__crystal_sigfault_handler\"\nTunkShif = \"TunkShif\"\n\n[default]\nextend-ignore-re = [\n  # numeric literals\n  '0x[0-9a-fA-F_\\.\\+]+([fiu](8|16|32|64|128))?',\n  '\\\\u\\{[0-9a-fA-F]+\\}',\n  # proper names\n  'FLE Standard Time',\n  'Universally Unique IDentifier',\n  # constants\n  'ERROR_\\w+',\n  'EVP_CIPH.*',\n  # fixed test values\n  'FOO|/Fo',\n  'rCVZVOThsIa97pEDOxvGu',\n  '\\w*AAAAAAAA\\w*',\n  # several string specs\n  '\"(Fo-ur|thi|abd|alo|tro|tring|ue)\"',\n  '\"(aGFo|hel|Hel|thr|noe|Noe|BaR|fo|FO)', '(hel|Hel|worl|Worl|fo)\"',\n  '/fo|/FO', \"'fo\", \"/\\\\(fo\",\n  'tr‸ue|fo‸o',\n  \"\\\"[^\\\"]+\\\"\\\\.to_slice\\\\s*=> [\\\"']\\\\\\\\u\", # src/html/entities.cr\n  \" ([a-zA-Z.]{8} ){4}->\", # src/bit_array.cr\n  \"[A-z]+[0-9]+\", # words including a number are likely some kind of identifier\n  \"sha256[:-][0-9A-z=]+\", # shell.nix\n]\n\n[files]\nextend-exclude = [\n  \".git/**\",\n  \"lib/**\",\n  \"man/**\",\n  \"spec/compiler/semantic/did_you_mean_spec.cr\",\n  \"spec/std/string_scanner_spec.cr\",\n  \"spec/std/data/**\",\n  \"spec/std/string/grapheme_break_spec.cr\",\n  \"src/compiler/crystal/tools/playground/public/vendor/\",\n]\n"
  },
  {
    "path": "bin/check-compiler-flag",
    "content": "#!/bin/sh\n\n# Use `./bin/check-compiler-flag FLAG` to check the behaviour of the given compiler flag.\n# This is useful if `crystal build` is affected when using `-DFLAG` option.\n#\n# This script will\n#   * run the compiler_spec using the flag\n#   * build the compiler to have the flag available (in case the flag is introduced in this version)\n#   * run the std_spec and primitives_spec with and without the flag\n#   * check the compiler_spec built with and without the flag (eg: the specs build with the flag can generate a compiler without the flag)\n#   * build a 2nd generation of the compiler using the flag\n#   * repeat the checks for the spec suites with and without the flag\n\nset -eux\n\nCOMPILER_FLAG=$1\n\n# test compiler_specs\nmake clean_cache clean_crystal\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) CRYSTAL_SPEC_COMPILER_FLAGS=\"$COMPILER_FLAG\" make compiler_spec\n\n# first gen compiler\nmake clean_cache clean_crystal crystal\n./bin/crystal --version\nmd5 .build/crystal\n\n# run specs\nmake clean_cache std_spec\nmake clean_cache std_spec FLAGS=\"-D$COMPILER_FLAG\"\nmake clean_cache primitives_spec\nmake clean_cache primitives_spec FLAGS=\"-D$COMPILER_FLAG\"\n\n# test compiler_specs\nmake clean_cache\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) CRYSTAL_SPEC_COMPILER_FLAGS=\"$COMPILER_FLAG\" make compiler_spec FLAGS=\"-D$COMPILER_FLAG\"\nmake clean_cache\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) make compiler_spec FLAGS=\"-D$COMPILER_FLAG\"\nmake clean_cache\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) make compiler_spec\n\n# building 2nd gen compiler\nmake clean_crystal clean_cache crystal # first\nmd5 .build/crystal\ntouch src/compiler/crystal.cr\nsleep 2\nmake clean_cache crystal FLAGS=\"-D$COMPILER_FLAG\" # second\nmd5 .build/crystal\n\n# run specs\nmake clean_cache std_spec\nmake clean_cache std_spec FLAGS=\"-D$COMPILER_FLAG\"\nmake clean_cache primitives_spec\nmake clean_cache primitives_spec FLAGS=\"-D$COMPILER_FLAG\"\n\n# run compiler specs\nmake clean_cache\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) CRYSTAL_SPEC_COMPILER_FLAGS=\"$COMPILER_FLAG\" make compiler_spec FLAGS=\"-D$COMPILER_FLAG\"\nmake clean_cache\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) make compiler_spec FLAGS=\"-D$COMPILER_FLAG\"\nmake clean_cache\nCRYSTAL_LIBRARY_PATH=$(./bin/crystal env CRYSTAL_LIBRARY_PATH) make compiler_spec\n"
  },
  {
    "path": "bin/ci",
    "content": "#!/bin/sh\n\nfail() {\n  echo \"${*}\" >&2\n  exit 1\n}\n\non_tag() {\n  if [ -n \"$CURRENT_TAG\" ]; then\n    echo \"${*}\"\n    eval \"${*}\"\n    return $?\n  else\n    return 0\n  fi\n}\n\nfail_on_error() {\n  \"${@}\"\n\n  exit=$?\n  if [ \"$exit\" -ne \"0\" ]; then\n    fail \"${*} exited with $exit\"\n  fi\n\n  return 0\n}\n\nverify_environment() {\n  if [ -z \"$TRAVIS_OS_NAME\" ]; then\n    fail \"\\$TRAVIS_OS_NAME is not set or empty.\"\n  fi\n}\n\nverify_linux_environment() {\n  if [ -z \"$ARCH\" ]; then\n    fail \"\\$ARCH is not set or empty.\"\n  fi\n\n  if [ -z \"$ARCH_CMD\" ]; then\n    fail \"\\$ARCH_CMD is not set or empty.\"\n  fi\n}\n\non_os() {\n  os=\"$1\"\n  shift\n\n  if [ -z \"$CI_NIX_SHELL\" ]; then\n    verify_environment\n\n    if [ \"$TRAVIS_OS_NAME\" = \"$os\" ]; then\n      echo \"${*}\"\n      eval \"${*}\"\n      return $?\n    else\n      return 0\n    fi\n  else\n    return 0\n  fi\n}\n\non_linux() {\n  fail_on_error on_os \"linux\" \"${*}\"\n}\n\non_osx() {\n  fail_on_error on_os \"osx\" \"${*}\"\n}\n\non_nix_shell_eval() {\n  if [ -n \"$CI_NIX_SHELL\" ]; then\n    echo \"${*}\"\n    eval \"${*}\"\n    return $?\n  else\n    return 0\n  fi\n}\n\non_nix_shell() {\n  fail_on_error on_nix_shell_eval \"${*}\"\n}\n\non_github() {\n  if [ \"$GITHUB_ACTIONS\" = \"true\" ]; then\n    eval \"${*}\"\n    return $?\n  else\n    return 0\n  fi\n}\n\nprepare_system() {\n  on_linux 'echo '\"'\"'{\"ipv6\":true, \"fixed-cidr-v6\":\"2001:db8:1::/64\"}'\"'\"' | sudo tee /etc/docker/daemon.json'\n  on_linux sudo service docker restart\n}\n\nbuild() {\n  with_build_env 'bin/crystal scripts/print_regex_config.cr'\n  with_build_env 'make std_spec clean threads=1 junit_output=.junit/std_spec.xml'\n\n  case $ARCH in\n    i386)\n      with_build_env 'make crystal threads=1'\n      with_build_env 'SPEC_SPLIT=\"0%4\" make std_spec threads=1 junit_output=.junit/std_spec.0.xml'\n      with_build_env 'SPEC_SPLIT=\"1%4\" make std_spec threads=1 junit_output=.junit/std_spec.1.xml'\n      with_build_env 'SPEC_SPLIT=\"2%4\" make std_spec threads=1 junit_output=.junit/std_spec.2.xml'\n      with_build_env 'SPEC_SPLIT=\"3%4\" make std_spec threads=1 junit_output=.junit/std_spec.3.xml'\n\n      parts=16\n      i=0\n      while [ $i -lt $parts ]; do\n        with_build_env \"CRYSTAL_SPEC_COMPILER_THREADS=1 SPEC_SPLIT=\\\"$i%$parts\\\" make compiler_spec threads=1 junit_output=.junit/compiler_spec.$i.xml\"\n        i=$((i + 1))\n      done\n\n      with_build_env 'make primitives_spec threads=1 junit_output=.junit/primitives_spec.xml'\n      with_build_env 'make docs threads=1'\n      ;;\n    *)\n      with_build_env 'make crystal primitives_spec std_spec compiler_spec docs threads=1 junit_output=.junit/spec.xml DOCS_OPTIONS=\"--json-config-url=/api/versions.json --canonical-base-url=https://crystal-lang.org/api/latest/\"'\n      ;;\n  esac\n\n  with_build_env 'make samples'\n  with_build_env 'CRYSTAL_OPTS=--debug make samples'\n}\n\nformat() {\n  with_build_env 'make clean crystal format threads=1 check=1'\n}\n\nprepare_build() {\n  on_linux verify_linux_environment\n\n  on_osx curl -L https://github.com/crystal-lang/crystal/releases/download/1.19.1/crystal-1.19.1-1-darwin-universal.tar.gz -o ~/crystal.tar.gz\n  on_osx 'pushd ~;gunzip -c ~/crystal.tar.gz | tar xopf -;mv crystal-1.19.1-1 crystal;popd'\n\n  # These commands may take a few minutes to run due to the large size of the repositories.\n  # This restriction has been made on GitHub's request because updating shallow\n  # clones is an extremely expensive operation due to the tree layout and\n  # traffic of Homebrew/homebrew-core and Homebrew/homebrew-cask. We don't do\n  # this for you automatically to avoid repeatedly performing an expensive\n  # unshallow operation in CI systems (which should instead be fixed to not use\n  # shallow clones). Sorry for the inconvenience!\n  on_osx git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow\n  on_osx git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow\n\n  on_osx brew update --preinstall\n  on_osx brew bundle --no-lock\n\n  # Install a recent bash version for nix-shell.\n  # macos ships with an ancient one.\n  if [ \"$(uname)\" = \"Darwin\" ]; then\n    on_nix_shell \"brew install bash\"\n  fi\n  # initialize nix environment\n  on_nix_shell nix-shell\n\n  # Note: brew link --force might show:\n  #   Warning: Refusing to link macOS-provided software: llvm\n  #\n  #   If you need to have llvm first in your PATH run:\n  #     echo 'export PATH=\"/usr/local/opt/llvm/bin:$PATH\"' >> ~/.bash_profile\n  #\n  # This is added in the .circleci/config.yml\n\n  on_tag verify_version\n}\n\nverify_version() {\n  # If building a tag, check it matches with file\n  FILE_VERSION=$(cat ./src/VERSION)\n\n  if [ \"$FILE_VERSION\" != \"$CURRENT_TAG\" ]\n  then\n    fail \"VERSION ($FILE_VERSION) does not match GIT TAG ($CURRENT_TAG)\"\n  fi\n}\n\nwith_build_env() {\n  command=\"$1\"\n  on_github \"echo '::group::$1'\"\n\n  # Ensure non GMT timezone\n  export TZ=\"America/New_York\"\n\n  on_linux verify_linux_environment\n\n  export DOCKER_TEST_PREFIX=\"${DOCKER_TEST_PREFIX:=\"crystallang/crystal:${CRYSTAL_BOOTSTRAP_VERSION:-1.19.1}\"}\"\n\n  case $ARCH in\n    aarch64|x86_64)\n      export DOCKER_TEST_IMAGE=\"$DOCKER_TEST_PREFIX-build\"\n      ;;\n    aarch64-musl|x86_64-musl)\n      export DOCKER_TEST_IMAGE=\"$DOCKER_TEST_PREFIX-alpine-build\"\n      ;;\n    i386)\n      export DOCKER_TEST_IMAGE=\"$DOCKER_TEST_PREFIX-i386-build\"\n      ;;\n  esac\n\n  on_linux docker run \\\n    --rm -t \\\n    -u \"$(id -u)\" \\\n    -v \"$PWD\":/mnt \\\n    -v /etc/passwd:/etc/passwd \\\n    -v /etc/group:/etc/group \\\n    -w /mnt \\\n    -e CRYSTAL_CACHE_DIR=\"/tmp/crystal\" \\\n    -e SPEC_SPLIT_DOTS \\\n    -e USE_PCRE1 \\\n    \"$DOCKER_TEST_IMAGE\" \\\n    \"$ARCH_CMD\" /bin/sh -c \"'$command'\"\n\n  on_osx sudo systemsetup -settimezone $TZ\n  on_osx PATH=\"~/crystal/bin:\\$PATH\" \\\n    CRYSTAL_LIBRARY_PATH=\"~/crystal/embedded/lib\" \\\n    CRYSTAL_CACHE_DIR=\"/tmp/crystal\" \\\n    /bin/sh -c \"'$command'\"\n\n  # shellcheck disable=SC2086\n  on_nix_shell nix-shell --pure $CI_NIX_SHELL_ARGS --run \"'TZ=$TZ $command'\"\n\n  on_github echo \"::endgroup::\"\n}\n\nusage() {\n  cat <<EOF\nbin/ci [-h|--help] command [parameter ...]\n\nHelper script to prepare and run the testsuite on Travis CI.\n\nCommands:\n  prepare_system          setup any necessaries repositories etc.\n  prepare_build           download and extract any dependencies needed for the build\n  build                   run specs, build crystal, build samples, build the docs\n  format                  build crystal, run format check\n  with_build_env command  run command in the build environment\n  help                    display this\n\nEOF\n}\n\ncommand=\"$1\"\nshift\ncase $command in\n  prepare_system)\n    prepare_system\n    ;;\n  prepare_build)\n    prepare_build\n    ;;\n  with_build_env)\n    target_command=\"${*}\"\n    with_build_env \"$target_command\"\n    ;;\n  build)\n    build\n    ;;\n  format)\n    format\n    ;;\n  -h|--help|help)\n    usage\n    ;;\n  *)\n    if [ -n \"$command\" ]; then\n      fail \"Unknown command $command\"\n    else\n      usage\n      exit 1\n    fi\n    ;;\nesac\n"
  },
  {
    "path": "bin/crystal",
    "content": "#!/bin/sh\n\n################## https://github.com/mkropat/sh-realpath #####################\n#\n# The MIT License (MIT)\n#\n# Copyright (c) 2014 Michael Kropat\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n# THE SOFTWARE.\n\nrealpath() {\n    canonicalize_path \"$(resolve_symlinks \"$1\")\"\n}\n\nresolve_symlinks() {\n    _resolve_symlinks \"$1\"\n}\n\n_resolve_symlinks() (\n    _assert_no_path_cycles \"$@\" || return\n\n    if path=$(readlink -- \"$1\"); then\n        dir_context=$(dirname -- \"$1\")\n        _resolve_symlinks \"$(_prepend_dir_context_if_necessary \"$dir_context\" \"$path\")\" \"$@\"\n    else\n        printf '%s\\n' \"$1\"\n    fi\n)\n\n_prepend_dir_context_if_necessary() {\n    if [ \"$1\" = . ]; then\n        printf '%s\\n' \"$2\"\n    else\n        _prepend_path_if_relative \"$1\" \"$2\"\n    fi\n}\n\n_prepend_path_if_relative() {\n    case \"$2\" in\n        /* ) printf '%s\\n' \"$2\" ;;\n         * ) printf '%s\\n' \"$1/$2\" ;;\n    esac\n}\n\n_assert_no_path_cycles() (\n    target=$1\n    shift\n\n    for path in \"$@\"; do\n        if [ \"$path\" = \"$target\" ]; then\n            return 1\n        fi\n    done\n)\n\ncanonicalize_path() {\n    if [ -d \"$1\" ]; then\n        _canonicalize_dir_path \"$1\"\n    else\n        _canonicalize_file_path \"$1\"\n    fi\n}\n\n_canonicalize_dir_path() {\n    { cd \"$1\" 2>/dev/null && pwd -P; }\n}\n\n_canonicalize_file_path() (\n    dir=$(dirname -- \"$1\")\n    file=$(basename -- \"$1\")\n    { cd \"$dir\" 2>/dev/null >/dev/null && printf '%s/%s\\n' \"$(pwd -P)\" \"$file\"; }\n)\n\n##############################################################################\n\n# Based on http://stackoverflow.com/q/370047/641451\nremove_path_item() (\n  path=\"$1\"\n\n  printf \"%s\" \"$path\" | awk -v item=\"$2\" -v RS=: -v ORS=: '$0 != item' | sed 's/:$//'\n)\n\n##############################################################################\n\n__has_colors() (\n  num_colors=$(tput colors 2>/dev/null)\n\n  if [ -n \"$num_colors\" ] && [ \"$num_colors\" -gt 2 ]; then\n    return 0\n  else\n    return 1\n  fi\n)\n__error_msg() {\n  if __has_colors; then\n    # bold red coloring\n    printf '%b\\n' \"\\033[31;1m$*\\033[0m\" 1>&2\n  else\n    printf '%b\\n' \"$@\" 1>&2\n  fi\n}\n__warning_msg() {\n  if __has_colors; then\n    # brown coloring\n    printf '%b\\n' \"\\033[33m$*\\033[0m\" 1>&2\n  else\n    printf '%b\\n' \"$@\" 1>&2\n  fi\n}\n\n\nSCRIPT_PATH=\"$(realpath \"$0\")\"\nSCRIPT_ROOT=\"$(dirname \"$SCRIPT_PATH\")\"\nCRYSTAL_ROOT=\"$(dirname \"$SCRIPT_ROOT\")\"\nCRYSTAL_DIR=\"$CRYSTAL_ROOT/.build\"\n\nexport CRYSTAL_PATH=\"${CRYSTAL_PATH:-./lib:$CRYSTAL_ROOT/src}\"\nif [ -n \"${CRYSTAL_PATH##*\"$CRYSTAL_ROOT\"/src*}\" ]; then\n  __warning_msg \"CRYSTAL_PATH env variable does not contain $CRYSTAL_ROOT/src\"\nfi\n\nexport CRYSTAL_HAS_WRAPPER=true\n: \"${CRYSTAL=crystal}\"\n\nPARENT_CRYSTAL=\"$CRYSTAL\"\n\n# check if the parent crystal command is a path that refers to this script\nif [ -z \"${PARENT_CRYSTAL##*/*}\" ] && [ \"$(realpath \"$PARENT_CRYSTAL\")\" = \"$SCRIPT_PATH\" ]; then\n  # ignore it and use `crystal` as parent compiler command\n  PARENT_CRYSTAL=\"crystal\"\nfi\n\n# check if the parent crystal command refers to this script\nif [ \"$(realpath \"$(command -v \"$PARENT_CRYSTAL\" 2> /dev/null)\" 2> /dev/null)\" = \"$SCRIPT_PATH\" ]; then\n  # remove the path to this script from PATH\n  NEW_PATH=\"$(remove_path_item \"$(remove_path_item \"$PATH\" \"$SCRIPT_ROOT\")\" \"bin\")\"\n  # if the PATH did not change it will lead to an infinite recursion => display error\n  if [ \"$NEW_PATH\" = \"$PATH\" ]; then\n    __error_msg 'Could not remove the script bin/crystal from the PATH. Remove it by hand or set the CRYSTAL env variable'\n    exit 1\n  fi\n  PARENT_CRYSTAL=$(PATH=$NEW_PATH command -v \"$PARENT_CRYSTAL\" 2> /dev/null)\n  if [ \"$(realpath \"$PARENT_CRYSTAL\" 2> /dev/null)\" = \"$SCRIPT_PATH\" ]; then\n    __error_msg 'Could not remove the script bin/crystal from the PATH. Remove it by hand or set the CRYSTAL env variable'\n    exit 1\n  fi\nfi\n\ncommand -v \"$PARENT_CRYSTAL\" > /dev/null 2> /dev/null\nPARENT_CRYSTAL_EXISTS=$(test !$?)\nif ($PARENT_CRYSTAL_EXISTS); then\n  if [ -z \"$CRYSTAL_CONFIG_LIBRARY_PATH\" ] || [ -z \"$CRYSTAL_LIBRARY_PATH\" ]; then\n    CRYSTAL_INSTALLED_LIBRARY_PATH=\"$($PARENT_CRYSTAL env CRYSTAL_LIBRARY_PATH 2> /dev/null || echo \"\")\"\n    export CRYSTAL_LIBRARY_PATH=\"${CRYSTAL_LIBRARY_PATH:-$CRYSTAL_INSTALLED_LIBRARY_PATH}\"\n    export CRYSTAL_CONFIG_LIBRARY_PATH=\"${CRYSTAL_CONFIG_LIBRARY_PATH:-$CRYSTAL_INSTALLED_LIBRARY_PATH}\"\n  fi\nfi\n\n# CRYSTAL_PATH has all symlinks resolved. In order to avoid issues with duplicate file\n# paths when the working directory is a symlink, we cd into the current directory\n# with symlinks resolved as well (see https://github.com/crystal-lang/crystal/issues/12969).\ncd \"$(realpath \"$(pwd)\")\" || exit\n\ncase \"$(uname -s)\" in\n  CYGWIN*|MSYS_NT*|MINGW32_NT*|MINGW64_NT*)\n    CRYSTAL_BIN=\"crystal.exe\"\n    ;;\n  *)\n    CRYSTAL_BIN=\"crystal\"\n    ;;\nesac\n\n# CRYSTAL_EXEC_PATH determines the location of the `crystal` program for external\n# compiler commands.\nCRYSTAL_EXEC_PATH=\"$SCRIPT_ROOT\"\nexport CRYSTAL_EXEC_PATH\n\nif [ -x \"$CRYSTAL_DIR/${CRYSTAL_BIN}\" ]; then\n  __warning_msg \"Using compiled compiler at ${CRYSTAL_DIR#\"$PWD/\"}/${CRYSTAL_BIN}\"\n  exec \"$CRYSTAL_DIR/${CRYSTAL_BIN}\" \"$@\"\nelif (! $PARENT_CRYSTAL_EXISTS); then\n  __error_msg 'You need to have a crystal executable in your path! or set CRYSTAL env variable'\n  exit 1\nelse\n  exec \"$PARENT_CRYSTAL\" \"$@\"\nfi\n"
  },
  {
    "path": "bin/crystal.bat",
    "content": "@echo off\r\npowershell.exe -NoProfile -ExecutionPolicy Bypass -Command \"%~dp0crystal.ps1\" --%% %*\r\n"
  },
  {
    "path": "bin/crystal.ps1",
    "content": "param(\r\n    [Parameter(Position = 0, ValueFromRemainingArguments)] [string[]] $CrystalArgs\r\n)\r\n\r\n# https://www.powershellgallery.com/packages/PowerGit/0.6.1/Content/Functions%5CResolve-RealPath.ps1\r\nfunction Resolve-RealPath {\r\n    <#\r\n        .SYNOPSIS\r\n        Implementation of Unix realpath().\r\n\r\n        .PARAMETER Path\r\n        Must exist\r\n    #>\r\n    [CmdletBinding()]\r\n    [OutputType([string])]\r\n    param(\r\n        [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]\r\n        [Alias('FullName')]\r\n        [string] $Path\r\n    )\r\n\r\n    if ($PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows) {\r\n        return [IO.Path]::GetFullPath($Path)\r\n    }\r\n\r\n    [string[]] $parts = ($Path.TrimStart([IO.Path]::DirectorySeparatorChar).Split([IO.Path]::DirectorySeparatorChar))\r\n    [string] $realPath = ''\r\n    foreach ($part in $parts) {\r\n        $realPath += [string] ([IO.Path]::DirectorySeparatorChar + $part)\r\n        $item = Get-Item $realPath\r\n        if ($item.Target) {\r\n            $realPath = $item.Target\r\n        }\r\n    }\r\n    $realPath\r\n}\r\n\r\n# adopted from https://stackoverflow.com/a/15669365\r\nfunction Write-StdErr {\r\n<#\r\n.SYNOPSIS\r\nWrites text to stderr when running in a regular console window,\r\nto the host''s error stream otherwise.\r\n\r\n.DESCRIPTION\r\nWriting to true stderr allows you to write a well-behaved CLI\r\nas a PS script that can be invoked from a batch file, for instance.\r\n\r\nNote that PS by default sends ALL its streams to *stdout* when invoked from\r\ncmd.exe.\r\n#>\r\n    param(\r\n        [Parameter(Mandatory)] [string] $Line,\r\n        $ForegroundColor\r\n    )\r\n    if ($Host.Name -eq 'ConsoleHost') {\r\n        if ($ForegroundColor) {\r\n            [Console]::ForegroundColor = $ForegroundColor\r\n        }\r\n        [Console]::Error.WriteLine($Line)\r\n        if ($ForegroundColor) {\r\n            [Console]::ResetColor()\r\n        }\r\n    } else {\r\n        [void] $host.ui.WriteErrorLine($Line)\r\n    }\r\n}\r\n\r\n# https://stackoverflow.com/a/43030126\r\nfunction Invoke-WithEnvironment {\r\n<#\r\n.SYNOPSIS\r\nInvokes commands with a temporarily modified environment.\r\n\r\n.DESCRIPTION\r\nModifies environment variables temporarily based on a hashtable of values,\r\ninvokes the specified script block, then restores the previous environment.\r\n\r\n.PARAMETER Environment\r\nA hashtable that defines the temporary environment-variable values.\r\nAssign $null to (temporarily) remove an environment variable that is\r\ncurrently set.\r\n\r\n.PARAMETER ScriptBlock\r\nThe command(s) to execute with the temporarily modified environment.\r\n\r\n.EXAMPLE\r\n> Invoke-WithEnvironment @{ PORT=8080 } { node index.js }\r\n\r\nRuns node with environment variable PORT temporarily set to 8080, with its\r\nprevious value, if any \r\n#>\r\n    param(\r\n        [Parameter(Mandatory)] [System.Collections.IDictionary] $Environment,\r\n        [Parameter(Mandatory)] [scriptblock] $ScriptBlock\r\n    )\r\n    # Modify the environment based on the hashtable and save the original \r\n    # one for later restoration.\r\n    $htOrgEnv = @{}\r\n    foreach ($kv in $Environment.GetEnumerator()) {\r\n        $htOrgEnv[$kv.Key] = (Get-Item -EA SilentlyContinue \"env:$($kv.Key)\").Value\r\n        Set-Item \"env:$($kv.Key)\" $kv.Value\r\n    }\r\n    # Invoke the script block\r\n    try {\r\n        & $ScriptBlock\r\n    } finally {\r\n        # Restore the original environment.\r\n        foreach ($kv in $Environment.GetEnumerator()) {\r\n            # Note: setting an environment var. to $null or '' *removes* it.\r\n            Set-Item \"env:$($kv.Key)\" $htOrgEnv[$kv.Key]\r\n        }\r\n    }\r\n}\r\n\r\n# Code ported from:\r\n# https://source.dot.net/#System.Diagnostics.Process/System/Diagnostics/ProcessStartInfo.cs\r\n# https://source.dot.net/#System.Diagnostics.Process/PasteArguments.cs\r\n# Licensed to the .NET Foundation under one or more agreements.\r\n# The .NET Foundation licenses this file to you under the MIT license.\r\nfunction Append-Argument {\r\n    param(\r\n        [Parameter(Mandatory)] [Text.StringBuilder] $Builder,\r\n        [Parameter(Mandatory)] [AllowEmptyString()] [string] $Arg\r\n    )\r\n\r\n    if (-not $Builder.Length -eq 0) {\r\n        [void]$Builder.Append(' ')\r\n    }\r\n\r\n    # Parsing rules for non-argv[0] arguments:\r\n    #   - Backslash is a normal character except followed by a quote.\r\n    #   - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote\r\n    #   - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote\r\n    #   - Parsing stops at first whitespace outside of quoted region.\r\n    #   - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode.\r\n    if ((-not $Builder.Length -eq 0) -and (ContainsNoWhitespaceOrQuotes $Arg)) {\r\n        # Simple case - no quoting or changes needed.\r\n        [void]$Builder.Append($Arg)\r\n    } else {\r\n        [void]$Builder.Append('\"')\r\n        $idx = 0\r\n        while ($idx -lt $Arg.Length) {\r\n            $c = $Arg[$idx++]\r\n            if ($c -eq '\\') {\r\n                $numBackSlash = 1\r\n                while (($idx -lt $Arg.Length) -and ($Arg[$idx] -eq '\\')) {\r\n                    $idx++\r\n                    $numBackslash++\r\n                }\r\n\r\n                if ($idx -eq $Arg.Length) {\r\n                    # We'll emit an end quote after this so must double the number of backslashes.\r\n                    [void]$Builder.Append('\\', $numBackSlash * 2)\r\n                } elseif ($Arg[$idx] -eq '\"') {\r\n                    # Backslashes will be followed by a quote. Must double the number of backslashes.\r\n                    [void]$Builder.Append('\\', $numBackSlash * 2 + 1)\r\n                    [void]$Builder.Append('\"')\r\n                    $idx++\r\n                } else {\r\n                    # Backslash will not be followed by a quote, so emit as normal characters.\r\n                    [void]$Builder.Append('\\', $numBackSlash)\r\n                }\r\n\r\n                continue\r\n            }\r\n\r\n            if ($c -eq '\"') {\r\n                # Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed\r\n                # by another quote (which parses differently pre-2008 vs. post-2008.)\r\n                [void]$Builder.Append('\\')\r\n                [void]$Builder.Append('\"')\r\n                continue\r\n            }\r\n\r\n            [void]$Builder.Append($c)\r\n        }\r\n\r\n        [void]$Builder.Append('\"')\r\n    }\r\n}\r\n\r\nfunction ContainsNoWhitespaceOrQuotes {\r\n    param(\r\n        [Parameter(Mandatory)] [AllowEmptyString()] [string] $s\r\n    )\r\n\r\n    for ($i = 0; $i -lt $s.Length; $i += 1) {\r\n        $c = $s[$i]\r\n        if ([char]::IsWhiteSpace($c) -or ($c -eq '\"')) {\r\n            return $False\r\n        }\r\n    }\r\n\r\n    $True\r\n}\r\n\r\nfunction Build-Arguments {\r\n    param(\r\n        [Parameter(Mandatory)] [AllowEmptyString()] [AllowNull()] [string[]] $Args\r\n    )\r\n\r\n    if ($Args.count -eq 0) { return '' }\r\n\r\n    $Builder = [Text.StringBuilder]::new()\r\n    $Args | Foreach-Object { Append-Argument $Builder $_ }\r\n    $Builder.ToString()\r\n}\r\n\r\nfunction Exec-Process {\r\n    param(\r\n        [Parameter(Mandatory)] [string] $Path,\r\n        [Parameter(Mandatory)] [AllowEmptyString()] [AllowNull()] [string[]] $Args\r\n    )\r\n\r\n    if ($Args[0] -eq '--%') {\r\n        $null, $Args = $Args\r\n\r\n        if ($Args) {\r\n            $Process = Start-Process $Path -Argument $Args -NoNewWindow -PassThru\r\n        } else {\r\n            $Process = Start-Process $Path -NoNewWindow -PassThru\r\n        }\r\n    } else {\r\n        # `Start-Process` simply joins all arguments without escaping and passes it to `ProcessStartInfo.Arguments`;\r\n        # here we replicate the logic for `ProcessStartInfo.ArgumentList` on .NET 5 and above\r\n        # See also: https://github.com/PowerShell/PowerShell/issues/14747\r\n        # (note that we must nonetheless implement it by ourselves if we support Windows Powershell 5.1)\r\n        $EscapedArgs = Build-Arguments $Args\r\n        if ($EscapedArgs) {\r\n            $Process = Start-Process $Path -ArgumentList $EscapedArgs -NoNewWindow -PassThru\r\n        } else {\r\n            $Process = Start-Process $Path -NoNewWindow -PassThru\r\n        }\r\n    }\r\n\r\n    # workaround to obtain the exit status properly: https://stackoverflow.com/a/23797762\r\n    $hnd = $Process.Handle\r\n    Wait-Process -Id $Process.Id\r\n\r\n    Exit $Process.ExitCode\r\n}\r\n\r\n$ScriptPath = Resolve-RealPath $PSCommandPath\r\n$ScriptRoot = Split-Path -Path $ScriptPath -Parent\r\n$CrystalRoot = Split-Path -Path $ScriptRoot -Parent\r\n$CrystalDir = \"$CrystalRoot\\.build\"\r\n\r\nInvoke-WithEnvironment @{\r\n    CRYSTAL_PATH = if ($env:CRYSTAL_PATH) { $env:CRYSTAL_PATH } else { \"lib;$CrystalRoot\\src\" }\r\n    CRYSTAL_HAS_WRAPPER = \"true\"\r\n    CRYSTAL = if ($env:CRYSTAL) { $env:CRYSTAL } else { \"crystal\" }\r\n    CRYSTAL_CONFIG_LIBRARY_PATH = $env:CRYSTAL_CONFIG_LIBRARY_PATH\r\n    CRYSTAL_LIBRARY_PATH = $env:CRYSTAL_LIBRARY_PATH\r\n} {\r\n    if (!$env:CRYSTAL_PATH.Contains(\"$CrystalRoot\\src\")) {\r\n        Write-StdErr \"CRYSTAL_PATH env variable does not contain $CrystalRoot\\src\" -ForegroundColor DarkYellow\r\n    }\r\n\r\n    if (!$env:CRYSTAL_CONFIG_LIBRARY_PATH -or !$env:CRYSTAL_LIBRARY_PATH) {\r\n        Invoke-WithEnvironment @{ PATH = $env:PATH.Split(';') -ne $ScriptRoot -ne \"bin\" -join ';' } {\r\n            $CrystalInstalled = Get-Command \"crystal\" -CommandType ExternalScript, Application -ErrorAction SilentlyContinue\r\n            $CrystalInstalledLibraryPath = if ($CrystalInstalled) { crystal env CRYSTAL_LIBRARY_PATH } else { $null }\r\n            if (!$env:CRYSTAL_CONFIG_LIBRARY_PATH) { $env:CRYSTAL_CONFIG_LIBRARY_PATH = $CrystalInstalledLibraryPath }\r\n            if (!$env:CRYSTAL_LIBRARY_PATH) { $env:CRYSTAL_LIBRARY_PATH = $CrystalInstalledLibraryPath }\r\n        }\r\n    }\r\n\r\n    if (Test-Path -Path \"$CrystalDir/crystal.exe\" -PathType Leaf) {\r\n        Write-StdErr \"Using compiled compiler at $($CrystalDir.Replace($pwd, \".\"))\\crystal.exe\" -ForegroundColor DarkYellow\r\n        Exec-Process \"$CrystalDir/crystal.exe\" $CrystalArgs\r\n    } else {\r\n        $CrystalCmd = Get-Command $env:CRYSTAL -CommandType ExternalScript, Application -ErrorAction SilentlyContinue\r\n        if (!$CrystalCmd) {\r\n            Write-StdErr 'You need to have a crystal executable in your path! or set CRYSTAL env variable' -ForegroundColor Red\r\n            Exit 1\r\n        } else {\r\n            $CrystalInstalledDir = Split-Path -Path $CrystalCmd.Path -Parent\r\n            if ($CrystalInstalledDir -eq $ScriptRoot -or $CrystalInstalledDir -eq \"bin\") {\r\n                Invoke-WithEnvironment @{ PATH = $env:PATH.Split(';') -ne $ScriptRoot -ne \"bin\" -join ';' } {\r\n                    Exec-Process $ScriptPath $CrystalArgs\r\n                }\r\n            } else {\r\n                Exec-Process $CrystalCmd.Path $CrystalArgs\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "devenv.nix",
    "content": "{ pkgs, lib, config, inputs, ... }:\n\n{\n  dotenv.enable = true;\n\n  git-hooks.hooks = {\n    actionlint.enable = true;\n    ameba = {\n      enable = true;\n      name = \"Ameba\";\n      entry = \"${pkgs.ameba}/bin/ameba --fix\";\n      files = \"\\\\.cr$\";\n      excludes = [\"^lib/\"];\n      pass_filenames = true;\n    };\n    check-toml.enable = true;\n    check-vcs-permalinks.enable = true;\n    circleci.enable = true;\n    crystal.enable = true;\n    makefile_both = {\n      enable = true;\n      name = \"Change both Makefile and Makefile.win\";\n      entry = ''${pkgs.runtimeShell} -c 'test \"$#\" -ne 1 || (echo \"Changes only in $@\" && false)' --'';\n      files = \"^Makefile(\\.win)?$\";\n      pass_filenames = true;\n    };\n    markdownlint.enable = true;\n    shellcheck = {\n      enable = true;\n      excludes = [\n        \".*\\.zsh$\"\n      ];\n    };\n    typos.enable = true;\n    zizmor.enable = true;\n  };\n\n  profiles = {\n    lint.module = {\n      # More expensive hooks that we don't want to execute on every commit all the time\n      git-hooks.hooks = {\n        # reuse always runs on all files in the repo which takes some time.\n        # Violations are very rare, so a longer feedback loop doesn't matter much.\n        reuse.enable = true;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "devenv.yaml",
    "content": "inputs:\n  git-hooks:\n    url: github:cachix/git-hooks.nix\n  nixpkgs:\n    url: github:cachix/devenv-nixpkgs/rolling\n"
  },
  {
    "path": "doc/changelogs/README.md",
    "content": "# Changelog\n\n## Historical changelogs\n\n- [pre-1.0](pre-1.0.md)\n- [1.0 series](v1.0.md)\n- [1.1 series](v1.1.md)\n- [1.2 series](v1.2.md)\n- [1.3 series](v1.3.md)\n- [1.4 series](v1.4.md)\n- [1.5 series](v1.5.md)\n- [1.6 series](v1.6.md)\n- [1.7 series](v1.7.md)\n- [1.8 series](v1.8.md)\n- [1.9 series](v1.9.md)\n- [1.10 series](v1.10.md)\n- [1.11 series](v1.11.md)\n- [1.12 series](v1.12.md)\n- [1.13 series](v1.13.md)\n- [1.14 series](v1.14.md)\n- [1.15 series](v1.15.md)\n- [1.16 series](v1.16.md)\n- [1.17 series](v1.17.md)\n- [1.18 series](v1.18.md)\n- [1.19 series](v1.19.md)\n"
  },
  {
    "path": "doc/changelogs/pre-1.0.md",
    "content": "# Changelog pre 1.0\n\n## 0.36.1 (2021-02-02)\n\n### Standard library\n\n- Fix `Enum.from_value?` for flag enum with non `Int32` base type. ([#10303](https://github.com/crystal-lang/crystal/pull/10303), thanks @straight-shoota)\n\n#### Text\n\n- Don't raise on `String.new` with `null` pointer and bytesize 0. ([#10308](https://github.com/crystal-lang/crystal/pull/10308), thanks @asterite)\n\n#### Collections\n\n- Explicitly return a `Hash` in `Hash#dup` and `Hash#clone` (reverts [#9871](https://github.com/crystal-lang/crystal/pull/9871)). ([#10331](https://github.com/crystal-lang/crystal/pull/10331), thanks @straight-shoota)\n\n#### Serialization\n\n- XML: fix deprecation warning. ([#10335](https://github.com/crystal-lang/crystal/pull/10335), thanks @bcardiff)\n\n#### Runtime\n\n- Eager load DWARF only if `CRYSTAL_LOAD_DWARF=1`. ([#10326](https://github.com/crystal-lang/crystal/pull/10326), thanks @bcardiff)\n\n### Compiler\n\n- **(performance)** Initialize right-away constants in a separate function. ([#10334](https://github.com/crystal-lang/crystal/pull/10334), thanks @asterite)\n- Fix incorrect casting between different union types. ([#10333](https://github.com/crystal-lang/crystal/pull/10333), thanks @asterite)\n- Fix a formatting error in the \"missing argument\" error. ([#10325](https://github.com/crystal-lang/crystal/pull/10325), thanks @BlobCodes)\n- Fix while condition assignment check for Not. ([#10347](https://github.com/crystal-lang/crystal/pull/10347), thanks @asterite)\n- Allow operators and setters-like macros names back. ([#10338](https://github.com/crystal-lang/crystal/pull/10338), thanks @asterite)\n\n#### Language semantics\n\n- Fix type check not considering virtual types. ([#10304](https://github.com/crystal-lang/crystal/pull/10304), thanks @asterite)\n- Use path lookup when looking up type for auto-cast match. ([#10318](https://github.com/crystal-lang/crystal/pull/10318), thanks @asterite)\n\n## 0.36.0 (2021-01-26)\n\n### Language changes\n\n- **(breaking-change)** Reject annotations on ivars defined in base class. ([#9502](https://github.com/crystal-lang/crystal/pull/9502), thanks @waj)\n- **(breaking-change)** Make `**` be right associative. ([#9684](https://github.com/crystal-lang/crystal/pull/9684), thanks @asterite)\n\n#### Macros\n\n- **(breaking-change)** Deprecate `TypeNode#has_attribute?` in favor of `#annotation`. ([#9950](https://github.com/crystal-lang/crystal/pull/9950), thanks @HertzDevil)\n- Support heredoc literal in macro. ([#9467](https://github.com/crystal-lang/crystal/pull/9467), thanks @MakeNowJust)\n- Support `%Q`, `%i`, `%w`, and `%x` literals in macros. ([#9811](https://github.com/crystal-lang/crystal/pull/9811), thanks @toddsundsted)\n- Allow executing multi-assignments in macros. ([#9440](https://github.com/crystal-lang/crystal/pull/9440), thanks @MakeNowJust)\n\n### Standard library\n\n- **(breaking-change)** Drop deprecated `CRC32`, `Adler32` top-level. ([#9530](https://github.com/crystal-lang/crystal/pull/9530), thanks @bcardiff)\n- **(breaking-change)** Drop deprecated `Flate`, `Gzip`, `Zip`, `Zlib` top-level. ([#9529](https://github.com/crystal-lang/crystal/pull/9529), thanks @bcardiff)\n- **(breaking-change)** Drop deprecated `with_color` top-level method. ([#9531](https://github.com/crystal-lang/crystal/pull/9531), thanks @bcardiff)\n- **(breaking-change)** Make `SemanticVersion` parsing more strict. ([#9868](https://github.com/crystal-lang/crystal/pull/9868), thanks @hugopl)\n- Respect explicitly provided type vars for `Tuple.new` and `NamedTuple.new`. ([#10047](https://github.com/crystal-lang/crystal/pull/10047), thanks @HertzDevil)\n- Fix `OptionParser` to handle sub-commands with hyphen. ([#9465](https://github.com/crystal-lang/crystal/pull/9465), thanks @erdnaxeli)\n- Fix `OptionParser` to not call handler if value is given to none value handler. ([#9603](https://github.com/crystal-lang/crystal/pull/9603), thanks @MakeNowJust)\n- Make `def_equals` compare first by reference. ([#9650](https://github.com/crystal-lang/crystal/pull/9650), thanks @waj)\n- Remove unnecessary `def ==(other)` fallbacks. ([#9571](https://github.com/crystal-lang/crystal/pull/9571), thanks @MakeNowJust)\n- Fix `OptionParser` indentation of option descriptions. ([#10183](https://github.com/crystal-lang/crystal/pull/10183), thanks @wonderix)\n- Add `#hash` to `Log::Metadata`, `Path`, `SemanticVersion`, `Socket::Address`, `URI::Params`, and `UUID`. ([#10119](https://github.com/crystal-lang/crystal/pull/10119), thanks @straight-shoota)\n- Refactor several `case` statements with exhaustive `case`/`in`. ([#9656](https://github.com/crystal-lang/crystal/pull/9656), thanks @Sija)\n- Remove `Kernel` mentions in docs. ([#9549](https://github.com/crystal-lang/crystal/pull/9549), thanks @toddsundsted)\n- Improve docs for `Object#dup`. ([#10053](https://github.com/crystal-lang/crystal/pull/10053), thanks @straight-shoota)\n- Fix example codes in multiple places. ([#9818](https://github.com/crystal-lang/crystal/pull/9818), thanks @maiha)\n- Fix typos, misspelling and styling. ([#9636](https://github.com/crystal-lang/crystal/pull/9636), [#9638](https://github.com/crystal-lang/crystal/pull/9638), [#9561](https://github.com/crystal-lang/crystal/pull/9561), [#9786](https://github.com/crystal-lang/crystal/pull/9786), [#9840](https://github.com/crystal-lang/crystal/pull/9840), [#9844](https://github.com/crystal-lang/crystal/pull/9844), [#9854](https://github.com/crystal-lang/crystal/pull/9854), [#9869](https://github.com/crystal-lang/crystal/pull/9869), [#10068](https://github.com/crystal-lang/crystal/pull/10068), [#10123](https://github.com/crystal-lang/crystal/pull/10123), [#10102](https://github.com/crystal-lang/crystal/pull/10102), [#10116](https://github.com/crystal-lang/crystal/pull/10116), [#10229](https://github.com/crystal-lang/crystal/pull/10229), [#10252](https://github.com/crystal-lang/crystal/pull/10252), thanks @kubo, @m-o-e, @mgomes, @philipp-classen, @dukeraphaelng, @camreeves, @docelic, @ilmanzo, @Sija, @pxeger, @oprypin)\n\n#### Macros\n\n- Fix documentation for `#[]=` macro methods. ([#10025](https://github.com/crystal-lang/crystal/pull/10025), thanks @HertzDevil)\n\n#### Numeric\n\n- **(breaking-change)** Move `Complex#exp`, `Complex#log`, etc. to `Math.exp`, `Math.log` overloads. ([#9739](https://github.com/crystal-lang/crystal/pull/9739), thanks @cristian-lsdb)\n- **(performance)** Use unchecked arithmetics in `Int#times`. ([#9511](https://github.com/crystal-lang/crystal/pull/9511), thanks @waj)\n- Check for overflow when extending signed integers to unsigned integers. ([#10000](https://github.com/crystal-lang/crystal/pull/10000), thanks @HertzDevil)\n- Fix `sprintf` for zero left padding a negative number. ([#9725](https://github.com/crystal-lang/crystal/pull/9725), thanks @asterite)\n- Fix `BigInt#to_64` and `BigInt#hash`. ([#10121](https://github.com/crystal-lang/crystal/pull/10121), thanks @oprypin)\n- Fix `BigDecimal#to_s` for numbers between `-1` and `0`. ([#9897](https://github.com/crystal-lang/crystal/pull/9897), thanks @dukeraphaelng)\n- Fix `Number#step`. ([#10130](https://github.com/crystal-lang/crystal/pull/10130), [#10295](https://github.com/crystal-lang/crystal/pull/10295), thanks @straight-shoota, @bcardiff)\n- Make `Complex` unary plus returns `self`. ([#9719](https://github.com/crystal-lang/crystal/pull/9719), thanks @asterite)\n- Improve `Int#lcm` and `Int#gcd`. ([#9999](https://github.com/crystal-lang/crystal/pull/9999), thanks @HertzDevil)\n- Add `Math::TAU`. ([#10179](https://github.com/crystal-lang/crystal/pull/10179), thanks @j8r)\n- Add wrapped unary negation to unsigned integer types to allow `&- x` expressions. ([#9947](https://github.com/crystal-lang/crystal/pull/9947), thanks @HertzDevil)\n- Improve documentation of mathematical functions. ([#9994](https://github.com/crystal-lang/crystal/pull/9994), thanks @HertzDevil)\n\n#### Text\n\n- **(breaking-change)** Fix `String#index` not working well for broken UTF-8 sequences. ([#9713](https://github.com/crystal-lang/crystal/pull/9713), thanks @asterite)\n- **(performance)** `String`: Don't materialize `Regex` `match[0]` if not needed. ([#9615](https://github.com/crystal-lang/crystal/pull/9615), thanks @asterite)\n- Fix `String#rindex` default offset for matching empty regex correctly. ([#9690](https://github.com/crystal-lang/crystal/pull/9690), thanks @MakeNowJust)\n- Add various `String#delete_at` methods. ([#9398](https://github.com/crystal-lang/crystal/pull/9398), thanks @asterite)\n- Raise if passing `null` Pointer to `String`. ([#9653](https://github.com/crystal-lang/crystal/pull/9653), thanks @jgaskins)\n- Add `Regex#capture_count`. ([#9746](https://github.com/crystal-lang/crystal/pull/9746), thanks @Sija)\n- Add `Regex::MatchData#[]` overloading to `Range`. ([#10076](https://github.com/crystal-lang/crystal/pull/10076), thanks @MakeNowJust)\n- Fix duplicates in `String` documentation. ([#9987](https://github.com/crystal-lang/crystal/pull/9987), thanks @Nicolab)\n- Add docs for substitution to `Kernel.sprintf`. ([#10227](https://github.com/crystal-lang/crystal/pull/10227), thanks @straight-shoota)\n\n#### Collections\n\n- **(breaking-change)** Make `Hash#reject!`, `Hash#select!`, and `Hash#compact!` consistent with `Array` and return `self`. ([#9904](https://github.com/crystal-lang/crystal/pull/9904), thanks @caspiano)\n- **(breaking-change)** Deprecate `Hash#delete_if` in favor of `Hash#reject!`, add `Dequeue#reject!` and `Dequeue#select!`. ([#9878](https://github.com/crystal-lang/crystal/pull/9878), thanks @caspiano)\n- **(breaking-change)** Change `Set#delete` to return Bool. ([#9590](https://github.com/crystal-lang/crystal/pull/9590), thanks @j8r)\n- **(breaking-change)** Drop deprecated `Enumerable#grep`. ([#9711](https://github.com/crystal-lang/crystal/pull/9711), thanks @j8r)\n- **(breaking-change)** Remove `Hash#key_index` . ([#10016](https://github.com/crystal-lang/crystal/pull/10016), thanks @asterite)\n- **(breaking-change)** Deprecate `StaticArray#[]=(value)` in favor of `StaticArray#fill`. ([#10027](https://github.com/crystal-lang/crystal/pull/10027), thanks @HertzDevil)\n- **(breaking-change)** Rename `Set#{sub,super}set?` to `{sub,super}set_of?`. ([#10187](https://github.com/crystal-lang/crystal/pull/10187), thanks @straight-shoota)\n- **(performance)** Optimize `Array#shift` and `Array#unshift`. ([#10081](https://github.com/crystal-lang/crystal/pull/10081), thanks @asterite)\n- **(performance)** `Array#rotate` optimization for small arrays. ([#8516](https://github.com/crystal-lang/crystal/pull/8516), thanks @wontruefree)\n- ~~Fix `Hash#dup` and `Hash#clone` to return correct type for subclasses. ([#9871](https://github.com/crystal-lang/crystal/pull/9871), thanks @vlazar)~~ (Reverted by [#10331](https://github.com/crystal-lang/crystal/pull/10331) in 0.36.1)\n- Fix `Iterator#cons_pair` return type. ([#9788](https://github.com/crystal-lang/crystal/pull/9788), thanks @asterite)\n- Fix key type restriction of `Hash#merge` block. ([#9495](https://github.com/crystal-lang/crystal/pull/9495), thanks @MakeNowJust)\n- Fix `Range#step`. ([#10203](https://github.com/crystal-lang/crystal/pull/10203), thanks @straight-shoota)\n- Let `Range#step` delegate to `begin.step` and allow float based ranges to be stepped. ([#10209](https://github.com/crystal-lang/crystal/pull/10209), thanks @straight-shoota)\n- Fix `Indexable#join` with `IO`. ([#10152](https://github.com/crystal-lang/crystal/pull/10152), thanks @straight-shoota)\n- Fix `Flatten` for value-type iterators. ([#10096](https://github.com/crystal-lang/crystal/pull/10096), thanks @f-fr)\n- Fix `Indexable.range_to_index_and_count` to not raise `IndexError`. ([#10191](https://github.com/crystal-lang/crystal/pull/10191), thanks @straight-shoota)\n- Handle recursive structures in `def_clone`, `Hash`, `Array`, `Dequeue`. ([#9800](https://github.com/crystal-lang/crystal/pull/9800), thanks @asterite)\n- Add `BitArray#dup`. ([#9550](https://github.com/crystal-lang/crystal/pull/9550), thanks @andremedeiros)\n- Allow `Iterator#zip` to take multiple `Iterator` arguments. ([#9944](https://github.com/crystal-lang/crystal/pull/9944), thanks @HertzDevil)\n- Add `Iterator#with_object(obj, &)`. ([#9956](https://github.com/crystal-lang/crystal/pull/9956), thanks @HertzDevil)\n- Always clear unused bits of `BitArray`s. ([#10008](https://github.com/crystal-lang/crystal/pull/10008), thanks @HertzDevil)\n- Update `Enumerable#sum(initial, &)` docs. ([#9860](https://github.com/crystal-lang/crystal/pull/9860), thanks @jage)\n- Add `Array#unsafe_build`. ([#10092](https://github.com/crystal-lang/crystal/pull/10092), thanks @straight-shoota)\n- Add identity methods for `#sum` and `#product`. ([#10151](https://github.com/crystal-lang/crystal/pull/10151), thanks @straight-shoota)\n- Move `combinations`, `permutations`, etc., from `Array` to `Indexable`. ([#10235](https://github.com/crystal-lang/crystal/pull/10235), thanks @wonderix)\n- Move sampling methods to `Enumerable`. ([#10129](https://github.com/crystal-lang/crystal/pull/10129), thanks @HertzDevil)\n- Use `Set#add` in `Set#add` documentation. ([#9441](https://github.com/crystal-lang/crystal/pull/9441), thanks @hugopl)\n- Improve `Hash#values_at` docs. ([#9955](https://github.com/crystal-lang/crystal/pull/9955), thanks @j8r)\n\n#### Serialization\n\n- **(breaking-change)** Drop deprecated `JSON.mapping`. ([#9527](https://github.com/crystal-lang/crystal/pull/9527), thanks @bcardiff)\n- **(breaking-change)** Drop deprecated `YAML.mapping`. ([#9526](https://github.com/crystal-lang/crystal/pull/9526), thanks @bcardiff)\n- Fix flag enum deserialization from `Int64`. ([#10145](https://github.com/crystal-lang/crystal/pull/10145), thanks @straight-shoota)\n- Add support for selective JSON and YAML serialization. ([#9567](https://github.com/crystal-lang/crystal/pull/9567), thanks @stakach)\n- YAML: correctly serialize `infinity` and `NaN`. ([#9780](https://github.com/crystal-lang/crystal/pull/9780), thanks @asterite)\n- YAML: allow using discriminator in contained types. ([#9851](https://github.com/crystal-lang/crystal/pull/9851), thanks @asterite)\n- Support more literal types in `JSON::Serializable.use_json_discriminator` macro. ([#9222](https://github.com/crystal-lang/crystal/pull/9222), thanks @voximity)\n- Support more literal types in `YAML::Serializable.use_yaml_discriminator` macro. ([#10149](https://github.com/crystal-lang/crystal/pull/10149), thanks @straight-shoota)\n- Add `JSON` serialization to big number types. ([#9898](https://github.com/crystal-lang/crystal/pull/9898), [#10054](https://github.com/crystal-lang/crystal/pull/10054), thanks @dukeraphaelng, @Sija)\n- Handle namespaced defaults in `JSON::Serializable` and `YAML::Serializable`. ([#9733](https://github.com/crystal-lang/crystal/pull/9733), thanks @jgaskins)\n- Standardize YAML error location. ([#9273](https://github.com/crystal-lang/crystal/pull/9273), thanks @straight-shoota)\n- Update `YAML::Field(converter)` argument docs. ([#9557](https://github.com/crystal-lang/crystal/pull/9557), thanks @dscottboggs)\n- Use `content` instead of `value` when inspect `XML::Attribute`. ([#9592](https://github.com/crystal-lang/crystal/pull/9592), thanks @asterite)\n- Let `JSON::PullParser#on_key` yield self and return value. ([#10028](https://github.com/crystal-lang/crystal/pull/10028), thanks @straight-shoota)\n- Cleanup `YAML`/`JSON` `Any#dig` methods. ([#9415](https://github.com/crystal-lang/crystal/pull/9415), thanks @j8r)\n- Document `JSON::PullParser`. ([#9983](https://github.com/crystal-lang/crystal/pull/9983), thanks @erdnaxeli)\n- Clarify Serialization Converter requirements and examples. ([#10202](https://github.com/crystal-lang/crystal/pull/10202), thanks @Daniel-Worrall)\n\n#### Time\n\n- **(breaking-change)** Drop deprecated `Time::Span.new` variants. ([#10051](https://github.com/crystal-lang/crystal/pull/10051), thanks @Sija)\n- **(breaking-change)** Deprecate `Time::Span#duration`, use `Time::Span#abs`. ([#10144](https://github.com/crystal-lang/crystal/pull/10144), thanks @straight-shoota)\n- Add `%Z` time format directive. ([#10141](https://github.com/crystal-lang/crystal/pull/10141), thanks @straight-shoota)\n- Improve handling edge cases for `Time::Location#load`. ([#10140](https://github.com/crystal-lang/crystal/pull/10140), thanks @straight-shoota)\n- Enable pending specs. ([#10093](https://github.com/crystal-lang/crystal/pull/10093), thanks @straight-shoota)\n- Improve `Time::Span` arithmetic specs. ([#10143](https://github.com/crystal-lang/crystal/pull/10143), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Make `File.size` and `FileInfo#size` be `Int64` instead of `UInt64`. ([#10015](https://github.com/crystal-lang/crystal/pull/10015), thanks @asterite)\n- **(breaking-change)** Fix `FileUtils.cp_r` when destination is a directory. ([#10180](https://github.com/crystal-lang/crystal/pull/10180), thanks @wonderix)\n- Enable large-file support on i386-linux-gnu. ([#9478](https://github.com/crystal-lang/crystal/pull/9478), thanks @kubo)\n- Fix glob not following symdir directories. ([#9910](https://github.com/crystal-lang/crystal/pull/9910), thanks @straight-shoota)\n- Windows: allow creating symlinks without admin rights. ([#9767](https://github.com/crystal-lang/crystal/pull/9767), thanks @oprypin)\n- Windows: allow touching directories. ([#10284](https://github.com/crystal-lang/crystal/pull/10284), thanks @straight-shoota)\n- Fix `Dir.glob` when `readdir` doesn't report file type. ([#9877](https://github.com/crystal-lang/crystal/pull/9877), thanks @straight-shoota)\n- Add `Path#stem`. ([#10037](https://github.com/crystal-lang/crystal/pull/10037), thanks @straight-shoota)\n- Move `File#fsync`, `File#flock_exclusive`, `File#flock_shared`, and `File#flock_unlock` methods to `IO::FileDescriptor`. ([#9794](https://github.com/crystal-lang/crystal/pull/9794), thanks @naqvis)\n- Call `IO#flush` on builder finish methods. ([#9321](https://github.com/crystal-lang/crystal/pull/9321), thanks @straight-shoota)\n- Support using `Path` in `MIME` and `File::Error` APIs. ([#10034](https://github.com/crystal-lang/crystal/pull/10034), thanks @Blacksmoke16)\n- Windows: enable `FileUtils` specs. ([#9902](https://github.com/crystal-lang/crystal/pull/9902), thanks @oprypin)\n\n#### Networking\n\n- **(breaking-change)** Rename `HTTP::Params` to `URI::Params`. ([#10098](https://github.com/crystal-lang/crystal/pull/10098), thanks @straight-shoota)\n- **(breaking-change)** Rename `URI#full_path` to `URI#request_target`. ([#10099](https://github.com/crystal-lang/crystal/pull/10099), thanks @straight-shoota)\n- **(breaking-change)** `URI::Params#[]=` replaces all values. ([#9605](https://github.com/crystal-lang/crystal/pull/9605), thanks @asterite)\n- Fix type of `HTTP::Server::Response#closed?` to `Bool`. ([#9489](https://github.com/crystal-lang/crystal/pull/9489), thanks @straight-shoota)\n- Fix `Socket#accept` to obey `read_timeout`. ([#9538](https://github.com/crystal-lang/crystal/pull/9538), thanks @waj)\n- Fix `Socket` to allow datagram over unix sockets. ([#9838](https://github.com/crystal-lang/crystal/pull/9838), thanks @bcardiff)\n- Fix `HTTP::Headers#==` and `HTTP::Headers#hash`. ([#10186](https://github.com/crystal-lang/crystal/pull/10186), thanks @straight-shoota)\n- Make `HTTP::StaticFileHandler` serve pre-gzipped content. ([#9626](https://github.com/crystal-lang/crystal/pull/9626), thanks @waj)\n- Delete `Content-Encoding` and `Content-Length` headers after decompression in `HTTP::Client::Response`. ([#5212](https://github.com/crystal-lang/crystal/pull/5212), thanks @maiha)\n- Delay setup of `HTTP::CompressHandler` until content is written. ([#9625](https://github.com/crystal-lang/crystal/pull/9625), thanks @waj)\n- Allow `HTTP::Client` to work with any IO. ([#9543](https://github.com/crystal-lang/crystal/pull/9543), thanks @waj)\n- Make `HTTP::Server` don't override `content-length` if already set. ([#9726](https://github.com/crystal-lang/crystal/pull/9726), thanks @asterite)\n- Do not share underlying hash between `URI::Param`s. ([#9641](https://github.com/crystal-lang/crystal/pull/9641), thanks @jgaskins)\n- Add `HTTP::Cookie::SameSite::None`. ([#9262](https://github.com/crystal-lang/crystal/pull/9262), thanks @j8r)\n- Add `HTTP::Client` logging and basic instrumentation. ([#9756](https://github.com/crystal-lang/crystal/pull/9756), thanks @bcardiff)\n- Add `HTTP::Request#hostname`+ utils. ([#10029](https://github.com/crystal-lang/crystal/pull/10029), thanks @straight-shoota)\n- Add `URI#authority`. ([#10100](https://github.com/crystal-lang/crystal/pull/10100), thanks @straight-shoota)\n- Add `Socket::IPAddress#private?`. ([#9517](https://github.com/crystal-lang/crystal/pull/9517), thanks @jgillich)\n- Make `Socket::IpAddress` a bit more type-friendly. ([#9528](https://github.com/crystal-lang/crystal/pull/9528), thanks @asterite)\n- Make specs pass in non-IPv6 environments. ([#9438](https://github.com/crystal-lang/crystal/pull/9438), thanks @jhass)\n- Make specs pending instead of failing in no multi-cast environments. ([#9566](https://github.com/crystal-lang/crystal/pull/9566), thanks @jhass)\n- Remove invalid optimization on `Encoding`. ([#9724](https://github.com/crystal-lang/crystal/pull/9724), thanks @asterite)\n- Drop `RemoteAddressType` private alias. ([#9777](https://github.com/crystal-lang/crystal/pull/9777), thanks @bcardiff)\n- Add documentation for `HTTP::WebSocket`. ([#9761](https://github.com/crystal-lang/crystal/pull/9761), [#9778](https://github.com/crystal-lang/crystal/pull/9778), thanks @ibraheemdev, @bcardiff)\n- Fix `HTTP` docs. ([#9612](https://github.com/crystal-lang/crystal/pull/9612), [#9627](https://github.com/crystal-lang/crystal/pull/9627), [#9717](https://github.com/crystal-lang/crystal/pull/9717), thanks @RX14, @asterite, @3n-k1)\n- Fix reference to TCP protocol in docs. ([#9457](https://github.com/crystal-lang/crystal/pull/9457), thanks @dprobinson)\n\n#### Logging\n\n- **(breaking-change)** Drop deprecated `Logger`. ([#9525](https://github.com/crystal-lang/crystal/pull/9525), thanks @bcardiff)\n- **(performance)** Add logging dispatcher to defer entry dispatch from current fiber. ([#9432](https://github.com/crystal-lang/crystal/pull/9432), thanks @waj)\n- Take timestamp as an argument in `Log::Entry` initializer. ([#9570](https://github.com/crystal-lang/crystal/pull/9570), thanks @Sija)\n- Improve docs regarding `Log.setup`. ([#9497](https://github.com/crystal-lang/crystal/pull/9497), [#9559](https://github.com/crystal-lang/crystal/pull/9559), thanks @caspiano, @jjlorenzo)\n\n#### Crypto\n\n- **(security)** Fix `\"force-peer\"` `verify_mode` setup. ([#9668](https://github.com/crystal-lang/crystal/pull/9668), thanks @PhilAtWysdom)\n- **(security)** Force secure renegotiation on server to prevent Secure Client-Initiated Renegotiation vulnerability attack. ([#9815](https://github.com/crystal-lang/crystal/pull/9815), thanks @bcardiff)\n- **(breaking-change)** Refactor `Digest` and introduce `Digest::MD5`, `Digest::SHA1`, `Digest::SHA256`, `Digest::SHA512` backed by openssl. ([#9864](https://github.com/crystal-lang/crystal/pull/9864), thanks @bcardiff)\n- Fix overflows in MD5 and SHA1. ([#9781](https://github.com/crystal-lang/crystal/pull/9781), thanks @bcardiff)\n- Add `OpenSSL::SSL::Context#set_modern_ciphers` and related methods to set ciphers and cipher suites in a more portable fashion. ([#9814](https://github.com/crystal-lang/crystal/pull/9814), [#10298](https://github.com/crystal-lang/crystal/pull/10298), thanks @bcardiff)\n- Add `OpenSSL::SSL::Context#security_level`. ([#9831](https://github.com/crystal-lang/crystal/pull/9831), thanks @bcardiff)\n- `OpenSSL::digest` add require for `OpenSSL::Error`. ([#10173](https://github.com/crystal-lang/crystal/pull/10173), thanks @wonderix)\n\n#### Concurrency\n\n- **(breaking-change)** Hide Channel internal implementation methods. ([#9564](https://github.com/crystal-lang/crystal/pull/9564), thanks @j8r)\n- `Channel#close` returns `true` unless the channel was already closed. ([#9443](https://github.com/crystal-lang/crystal/pull/9443), thanks @waj)\n- Support splat expressions inside `spawn` macro. ([#10234](https://github.com/crystal-lang/crystal/pull/10234), thanks @HertzDevil)\n- Remove incorrect note about negative timeout not triggering. ([#10131](https://github.com/crystal-lang/crystal/pull/10131), thanks @straight-shoota)\n\n#### System\n\n- Fix `Process.find_executable` to check the found path is executable and add Windows support. ([#9365](https://github.com/crystal-lang/crystal/pull/9365), thanks @oprypin)\n- Add `Process.parse_arguments` and fix `CRYSTAL_OPTS` parsing. ([#9518](https://github.com/crystal-lang/crystal/pull/9518), thanks @MakeNowJust)\n- Port to NetBSD. ([#9360](https://github.com/crystal-lang/crystal/pull/9360), thanks @niacat)\n- Add note about raising `IO::Error` to `Process` methods. ([#9894](https://github.com/crystal-lang/crystal/pull/9894), thanks @straight-shoota)\n- Handle errors in `User`/`Group` `from_name?`/`from_id?` as not found. ([#10182](https://github.com/crystal-lang/crystal/pull/10182), thanks @wonderix)\n- define the `SC_PAGESIZE` constant on more platforms. ([#9821](https://github.com/crystal-lang/crystal/pull/9821), thanks @carlhoerberg)\n\n#### Runtime\n\n- Fix bug with passing many args then a struct in Win64 C lib ABI. ([#9520](https://github.com/crystal-lang/crystal/pull/9520), thanks @oprypin)\n- Fix C ABI for AArch64. ([#9430](https://github.com/crystal-lang/crystal/pull/9430), thanks @jhass)\n- Disable LLVM Global Isel. ([#9401](https://github.com/crystal-lang/crystal/pull/9401), [#9562](https://github.com/crystal-lang/crystal/pull/9562), thanks @jhass, @bcardiff)\n- Print original exception after failing to raise it. ([#9220](https://github.com/crystal-lang/crystal/pull/9220), thanks @jhass)\n- Use Dwarf information on `Exception::CallStack.print_frame` and crash stacktraces. ([#9792](https://github.com/crystal-lang/crystal/pull/9792), [#9852](https://github.com/crystal-lang/crystal/pull/9852), thanks @bcardiff)\n- `MachO`: Handle missing `LC_UUID`. ([#9706](https://github.com/crystal-lang/crystal/pull/9706), thanks @bcardiff)\n- Allow `WeakRef` to compile with `-Dgc_none`. ([#9806](https://github.com/crystal-lang/crystal/pull/9806), thanks @bcardiff)\n- Fix example in `Crystal.main` docs. ([#9736](https://github.com/crystal-lang/crystal/pull/9736), thanks @hugopl)\n- Add a C ABI spec for accepting a large struct - failing on Win32, AArch64. ([#9534](https://github.com/crystal-lang/crystal/pull/9534), thanks @oprypin)\n- Don't relativize paths outside of current directory in backtrace. ([#10177](https://github.com/crystal-lang/crystal/pull/10177), thanks @Sija)\n- Avoid loading DWARF if env var `CRYSTAL_LOAD_DWARF=0`. ([#10261](https://github.com/crystal-lang/crystal/pull/10261), thanks @bcardiff)\n- Fix DWARF loading in FreeBSD. ([#10259](https://github.com/crystal-lang/crystal/pull/10259), thanks @bcardiff)\n- Print unhandled exceptions from main routine with `#inspect_with_backtrace`. ([#10086](https://github.com/crystal-lang/crystal/pull/10086), thanks @straight-shoota)\n\n#### Spec\n\n- **(breaking-change)** Add support for custom failure messages in expectations. ([#10127](https://github.com/crystal-lang/crystal/pull/10127), [#10289](https://github.com/crystal-lang/crystal/pull/10289), thanks @Fryguy, @straight-shoota)\n- Allow absolute file paths in `crystal spec` CLI. ([#9951](https://github.com/crystal-lang/crystal/pull/9951), thanks @KevinSjoberg)\n- Allow `--link-flags` to be used. ([#6243](https://github.com/crystal-lang/crystal/pull/6243), thanks @bcardiff)\n- Improve duration display in `spec --profile`. ([#10044](https://github.com/crystal-lang/crystal/pull/10044), thanks @straight-shoota)\n- Add exception handler in spec `at_exit`. ([#10106](https://github.com/crystal-lang/crystal/pull/10106), thanks @straight-shoota)\n\n### Compiler\n\n- **(performance)** Don't use init check for constants that are declared before read. ([#9801](https://github.com/crystal-lang/crystal/pull/9801), thanks @asterite)\n- **(performance)** Don't use init check for class vars initialized before read. ([#9995](https://github.com/crystal-lang/crystal/pull/9995), thanks @asterite)\n- **(performance)** Don't use init flag for string constants. ([#9808](https://github.com/crystal-lang/crystal/pull/9808), thanks @asterite)\n- Run side effects for class var with constant initializer. ([#10010](https://github.com/crystal-lang/crystal/pull/10010), thanks @asterite)\n- Fix `VaList` and disable `va_arg` for AArch64. ([#9422](https://github.com/crystal-lang/crystal/pull/9422), thanks @jhass)\n- Fix cache corrupted when compilation is cancelled. ([#9558](https://github.com/crystal-lang/crystal/pull/9558), thanks @waj)\n- Consider `select` as an opening keyword in macros. ([#9624](https://github.com/crystal-lang/crystal/pull/9624), thanks @asterite)\n- Use function attribute `frame-pointer`. ([#9361](https://github.com/crystal-lang/crystal/pull/9361), thanks @kubo39)\n- Make missing `__crystal_raise_overflow` error more clear. ([#9686](https://github.com/crystal-lang/crystal/pull/9686), thanks @3n-k1)\n- Forbid calls with both block arg and block. ([#10026](https://github.com/crystal-lang/crystal/pull/10026), thanks @HertzDevil)\n- Add method name to frozen type error. ([#10057](https://github.com/crystal-lang/crystal/pull/10057), thanks @straight-shoota)\n- Use file lock to avoid clashes between compilations. ([#10050](https://github.com/crystal-lang/crystal/pull/10050), thanks @asterite)\n- Add a couple of missing end locations to some nodes. ([#10012](https://github.com/crystal-lang/crystal/pull/10012), thanks @asterite)\n- Don't visit enum member expanded by macro twice. ([#10105](https://github.com/crystal-lang/crystal/pull/10105), thanks @asterite)\n- Fix crash when matching arguments against splat restriction after positional parameters. ([#10172](https://github.com/crystal-lang/crystal/pull/10172), thanks @HertzDevil)\n- Fix parsing of `do end` blocks inside parenthesis. ([#10189](https://github.com/crystal-lang/crystal/pull/10189), [#10207](https://github.com/crystal-lang/crystal/pull/10207), thanks @straight-shoota, @Sija)\n- Fix regex parsing after then in `case/when`. ([#10274](https://github.com/crystal-lang/crystal/pull/10274), thanks @asterite)\n- Fix error message for non-exhaustive case statements of 3 or more `Bool`/`Enum` values. ([#10194](https://github.com/crystal-lang/crystal/pull/10194), thanks @HertzDevil)\n- Forbid reopening generic types with different type var splats. ([#10167](https://github.com/crystal-lang/crystal/pull/10167), thanks @HertzDevil)\n- Apply stricter checks to macro names. ([#10069](https://github.com/crystal-lang/crystal/pull/10069), thanks @HertzDevil)\n- Reword error message when including/inheriting generic type without type vars. ([#10206](https://github.com/crystal-lang/crystal/pull/10206), thanks @HertzDevil)\n- Print a message pointing to the online playground if compiled without it. ([#9622](https://github.com/crystal-lang/crystal/pull/9622), thanks @deiv)\n- Handle typedef type restriction against alias type correctly. ([#9490](https://github.com/crystal-lang/crystal/pull/9490), thanks @MakeNowJust)\n- Initial support LLVM 11.0 with known issue regarding optimizations [#10220](https://github.com/crystal-lang/crystal/issues/10220). ([#9829](https://github.com/crystal-lang/crystal/pull/9829), [#10293](https://github.com/crystal-lang/crystal/pull/10293), thanks @bcardiff)\n- Add support for `--mcpu native`. ([#10264](https://github.com/crystal-lang/crystal/pull/10264), [#10276](https://github.com/crystal-lang/crystal/pull/10276), thanks @BlobCodes, @bcardiff)\n- Fix `Process.run` uses in compiler to handle exec failure. ([#9893](https://github.com/crystal-lang/crystal/pull/9893), [#9911](https://github.com/crystal-lang/crystal/pull/9911), [#9913](https://github.com/crystal-lang/crystal/pull/9913), thanks @straight-shoota, @bcardiff, @oprypin)\n- Fix `Command#report_warnings` to gracefully handle missing `Compiler#program`. ([#9866](https://github.com/crystal-lang/crystal/pull/9866), thanks @straight-shoota)\n- Add `Program#host_compiler`. ([#9920](https://github.com/crystal-lang/crystal/pull/9920), thanks @straight-shoota)\n- Add `Crystal::Path#name_size` implementation. ([#9380](https://github.com/crystal-lang/crystal/pull/9380), thanks @straight-shoota)\n- Refactor helper methods in `call_error.cr`. ([#9376](https://github.com/crystal-lang/crystal/pull/9376), thanks @straight-shoota)\n- Delegate def error location to implementing type. ([#10159](https://github.com/crystal-lang/crystal/pull/10159), thanks @straight-shoota)\n- Refactor `Crystal::Exception` to `Crystal::CodeError`. ([#10197](https://github.com/crystal-lang/crystal/pull/10197), thanks @straight-shoota)\n- Refactor `check_single_def_error_message`. ([#10196](https://github.com/crystal-lang/crystal/pull/10196), thanks @straight-shoota)\n- Code clean-ups. ([#9468](https://github.com/crystal-lang/crystal/pull/9468), thanks @MakeNowJust)\n- Refactors for compiler specs. ([#9379](https://github.com/crystal-lang/crystal/pull/9379), thanks @straight-shoota)\n- Windows CI: Fix and enable compiler specs. ([#9348](https://github.com/crystal-lang/crystal/pull/9348), [#9560](https://github.com/crystal-lang/crystal/pull/9560), thanks @oprypin)\n\n#### Language semantics\n\n- **(breaking-change)** Handle union restrictions with free vars properly. ([#10267](https://github.com/crystal-lang/crystal/pull/10267), thanks @HertzDevil)\n- **(breaking-change)** Disallow keywords as block argument name. ([#9704](https://github.com/crystal-lang/crystal/pull/9704), thanks @asterite)\n- **(breaking-change)** Fix `a-b -c` incorrectly parsed as `a - (b - c)`. ([#9652](https://github.com/crystal-lang/crystal/pull/9652), [#9884](https://github.com/crystal-lang/crystal/pull/9884), thanks @asterite, @bcardiff)\n- **(breaking-change)** Check abstract def implementations with splats, default values and keyword arguments. ([#9585](https://github.com/crystal-lang/crystal/pull/9585), thanks @waj)\n- **(breaking-change)** Make abstract def return type warning an error. ([#9810](https://github.com/crystal-lang/crystal/pull/9810), thanks @bcardiff)\n- **(breaking-change)** Error when abstract def implementation adds a type restriction. ([#9634](https://github.com/crystal-lang/crystal/pull/9634), thanks @waj)\n- **(breaking-change)** Don't interpret Proc typedefs as aliases. ([#10254](https://github.com/crystal-lang/crystal/pull/10254), thanks @HertzDevil)\n- **(breaking-change)** Remove the special logic of union of pointer and `nil`. ([#9872](https://github.com/crystal-lang/crystal/pull/9872), thanks @asterite)\n- Check abstract def implementations with double splats. ([#9633](https://github.com/crystal-lang/crystal/pull/9633), thanks @waj)\n- Make inherited hook work through generic instances. ([#9701](https://github.com/crystal-lang/crystal/pull/9701), thanks @asterite)\n- Attach doc comment to annotation in macro expansion. ([#9630](https://github.com/crystal-lang/crystal/pull/9630), thanks @MakeNowJust)\n- Marks else branch of exhaustive case as unreachable, improving resulting type. ([#9659](https://github.com/crystal-lang/crystal/pull/9659), thanks @Sija)\n- Make `Pointer(T)#value=` stricter for generic arguments. ([#10224](https://github.com/crystal-lang/crystal/pull/10224), thanks @asterite)\n- Extend type filtering of conditional clauses to arbitrary logical connectives. ([#10147](https://github.com/crystal-lang/crystal/pull/10147), thanks @HertzDevil)\n- Support NamedTuple instance restrictions in redefinition checks. ([#10245](https://github.com/crystal-lang/crystal/pull/10245), thanks @HertzDevil)\n- Respect block arg restriction when given block has a splat parameter. ([#10242](https://github.com/crystal-lang/crystal/pull/10242), thanks @HertzDevil)\n- Make overloads without double splat more specialized than overloads with one. ([#10185](https://github.com/crystal-lang/crystal/pull/10185), thanks @HertzDevil)\n- Support type splats inside explicit Unions in type restrictions. ([#10174](https://github.com/crystal-lang/crystal/pull/10174), thanks @HertzDevil)\n- Fix splats on generic arguments. ([#9859](https://github.com/crystal-lang/crystal/pull/9859), thanks @asterite)\n- Correctly compute line number after macro escape. ([#9858](https://github.com/crystal-lang/crystal/pull/9858), thanks @asterite)\n- Fix bug for C structs assigned to a type. ([#9743](https://github.com/crystal-lang/crystal/pull/9743), thanks @matthewmcgarvey)\n- Promote C variadic args as needed. ([#9747](https://github.com/crystal-lang/crystal/pull/9747), thanks @asterite)\n- Fix parsing of `def self./` and `def self.%`. ([#9721](https://github.com/crystal-lang/crystal/pull/9721), thanks @asterite)\n- Fix `ASTNode#to_s` for parenthesized expression in block. ([#9629](https://github.com/crystal-lang/crystal/pull/9629), thanks @MakeNowJust)\n- Don't form a closure on `typeof(@ivar)`. ([#9723](https://github.com/crystal-lang/crystal/pull/9723), thanks @asterite)\n- Add a few missing `expanded.transform self`. ([#9506](https://github.com/crystal-lang/crystal/pull/9506), thanks @asterite)\n- Treat `super` as special only if it doesn't have a receiver (i.e.: allow `super` user defined methods). ([#10011](https://github.com/crystal-lang/crystal/pull/10011), thanks @asterite)\n- Implement auto-casting in a better way. ([#9501](https://github.com/crystal-lang/crystal/pull/9501), thanks @asterite)\n- Try literal type first when autocasting to unions types. ([#9610](https://github.com/crystal-lang/crystal/pull/9610), thanks @asterite)\n- Correctly pass named arguments for `previous_def` and `super`. ([#9834](https://github.com/crystal-lang/crystal/pull/9834), thanks @asterite)\n- Turn proc pointer into proc literal in some cases to closure needed variables. ([#9824](https://github.com/crystal-lang/crystal/pull/9824), thanks @asterite)\n- Fix proc of self causing multi-dispatch. ([#9972](https://github.com/crystal-lang/crystal/pull/9972), thanks @asterite)\n- Fixes and improvements to closured variables. ([#9986](https://github.com/crystal-lang/crystal/pull/9986), thanks @asterite)\n- Don't merge proc types but allow assigning them if they are compatible. ([#9971](https://github.com/crystal-lang/crystal/pull/9971), thanks @asterite)\n- Reset nilable vars inside block method. ([#10091](https://github.com/crystal-lang/crystal/pull/10091), thanks @asterite)\n- Reset free vars before attempting each overload match. ([#10271](https://github.com/crystal-lang/crystal/pull/10271), thanks @HertzDevil)\n- Let `offsetof` support `Tuples`. ([#10218](https://github.com/crystal-lang/crystal/pull/10218), thanks @hugopl)\n\n### Tools\n\n- Fetch git config correctly. ([#9640](https://github.com/crystal-lang/crystal/pull/9640), thanks @dorianmariefr)\n\n#### Formatter\n\n- Avoid adding a newline after comment before end. ([#9722](https://github.com/crystal-lang/crystal/pull/9722), thanks @asterite)\n- Apply string pieces combination even if heredoc has no indent. ([#9475](https://github.com/crystal-lang/crystal/pull/9475), thanks @MakeNowJust)\n- Correctly format named arguments like `foo:bar`. ([#9740](https://github.com/crystal-lang/crystal/pull/9740), thanks @asterite)\n- Handle comment before do in a separate line. ([#9762](https://github.com/crystal-lang/crystal/pull/9762), thanks @asterite)\n- Fix indent for the first comment of `select when`/`else`. ([#10002](https://github.com/crystal-lang/crystal/pull/10002), thanks @MakeNowJust)\n- Reduce redundant newlines at end of `begin ... end`. ([#9741](https://github.com/crystal-lang/crystal/pull/9741), thanks @MakeNowJust)\n- Let formatter normalize crystal language tag for code blocks in doc comments. ([#10156](https://github.com/crystal-lang/crystal/pull/10156), thanks @straight-shoota)\n- Fix indentation of trailing comma in call with a block. ([#10223](https://github.com/crystal-lang/crystal/pull/10223), thanks @MakeNowJust)\n\n#### Doc generator\n\n- Linkification: refactor, fix edge cases and add specs. ([#9817](https://github.com/crystal-lang/crystal/pull/9817), thanks @oprypin)\n- Exclude types from docs who are in nodoc namespaces. ([#9819](https://github.com/crystal-lang/crystal/pull/9819), thanks @Blacksmoke16)\n- Fix link generation for lib type reference. ([#9931](https://github.com/crystal-lang/crystal/pull/9931), thanks @straight-shoota)\n- Correctly render underscores in code blocks. ([#9822](https://github.com/crystal-lang/crystal/pull/9822), thanks @Blacksmoke16)\n- Fix some HTML in docs generator. ([#9918](https://github.com/crystal-lang/crystal/pull/9918), thanks @straight-shoota)\n- Correctly handle broken source code on syntax highlighting. ([#9416](https://github.com/crystal-lang/crystal/pull/9416), thanks @MakeNowJust)\n- Re-introduce canonical URL for docs generator. ([#9917](https://github.com/crystal-lang/crystal/pull/9917), thanks @straight-shoota)\n- Expose `\"location\"` and `\"args_html\"` in doc JSON. ([#10122](https://github.com/crystal-lang/crystal/pull/10122), [#10200](https://github.com/crystal-lang/crystal/pull/10200), thanks @oprypin)\n- Expose `\"aliased_html\"` in doc JSON, make `\"aliased\"` field nullable. ([#10117](https://github.com/crystal-lang/crystal/pull/10117), thanks @oprypin)\n- Support linking to section headings in same tab. ([#9826](https://github.com/crystal-lang/crystal/pull/9826), thanks @Blacksmoke16)\n- Do not highlight `undef` as a keyword, it's not. ([#10216](https://github.com/crystal-lang/crystal/pull/10216), thanks @rhysd)\n\n### Others\n\n- CI improvements and housekeeping. ([#9507](https://github.com/crystal-lang/crystal/pull/9507), [#9513](https://github.com/crystal-lang/crystal/pull/9513), [#9512](https://github.com/crystal-lang/crystal/pull/9512), [#9515](https://github.com/crystal-lang/crystal/pull/9515), [#9514](https://github.com/crystal-lang/crystal/pull/9514), [#9609](https://github.com/crystal-lang/crystal/pull/9609), [#9642](https://github.com/crystal-lang/crystal/pull/9642), [#9758](https://github.com/crystal-lang/crystal/pull/9758), [#9763](https://github.com/crystal-lang/crystal/pull/9763), [#9850](https://github.com/crystal-lang/crystal/pull/9850), [#9906](https://github.com/crystal-lang/crystal/pull/9906), [#9907](https://github.com/crystal-lang/crystal/pull/9907), [#9912](https://github.com/crystal-lang/crystal/pull/9912), [#10078](https://github.com/crystal-lang/crystal/pull/10078), [#10217](https://github.com/crystal-lang/crystal/pull/10217), [#10255](https://github.com/crystal-lang/crystal/pull/10255), thanks @jhass, @bcardiff, @waj, @oprypin, @j8r, @Sija)\n- Add timeout to individual specs on multi-threading. ([#9865](https://github.com/crystal-lang/crystal/pull/9865), thanks @bcardiff)\n- Initial AArch64 CI. ([#9508](https://github.com/crystal-lang/crystal/pull/9508), [#9582](https://github.com/crystal-lang/crystal/pull/9582), thanks @jhass, @waj)\n- Update distribution-scripts and use LLVM 10 on Linux packages. ([#9710](https://github.com/crystal-lang/crystal/pull/9710), thanks @bcardiff)\n- Update distribution-scripts and publish nightly packages to bintray. ([#9663](https://github.com/crystal-lang/crystal/pull/9663), thanks @bcardiff)\n- Update distribution-scripts to use Shards v0.13.0. ([#10280](https://github.com/crystal-lang/crystal/pull/10280), thanks @bcardiff)\n- Initial Nix development environment. Use Nix for OSX CI instead of homebrew. ([#9727](https://github.com/crystal-lang/crystal/pull/9727), [#9776](https://github.com/crystal-lang/crystal/pull/9776), thanks @bcardiff)\n- Prevent recursive call in `bin/crystal` wrapper. ([#9505](https://github.com/crystal-lang/crystal/pull/9505), thanks @jhass)\n- Allow overriding `CRYSTAL_PATH` in `bin/crystal` wrapper . ([#9632](https://github.com/crystal-lang/crystal/pull/9632), thanks @bcardiff)\n- Makefile: support changing the current crystal via env var and argument. ([#9471](https://github.com/crystal-lang/crystal/pull/9471), thanks @bcardiff)\n- Makefile: improve supported LLVM versions message. ([#10221](https://github.com/crystal-lang/crystal/pull/10221), [#10269](https://github.com/crystal-lang/crystal/pull/10269), thanks @rdp, @straight-shoota)\n- Update CONTRIBUTING.md. ([#9448](https://github.com/crystal-lang/crystal/pull/9448), [#10249](https://github.com/crystal-lang/crystal/pull/10249), [#10250](https://github.com/crystal-lang/crystal/pull/10250), thanks @wontruefree, @sanks64, @straight-shoota)\n- Refactor `RedBlackTree` sample to use an enum instead of symbols. ([#10233](https://github.com/crystal-lang/crystal/pull/10233), thanks @Sija)\n- Add security policy. ([#9984](https://github.com/crystal-lang/crystal/pull/9984), thanks @straight-shoota)\n- Use `name: nightly` in docs-versions.sh for HEAD. ([#9915](https://github.com/crystal-lang/crystal/pull/9915), thanks @straight-shoota)\n- Use canonical base url for generating docs. ([#10007](https://github.com/crystal-lang/crystal/pull/10007), thanks @straight-shoota)\n- Remove Vagrantfile. ([#10033](https://github.com/crystal-lang/crystal/pull/10033), thanks @straight-shoota)\n- Update NOTICE's copyright year to 2021. ([#10166](https://github.com/crystal-lang/crystal/pull/10166), thanks @matiasgarciaisaia)\n\n## 0.35.1 (2020-06-19)\n\n### Standard library\n\n#### Collections\n\n- Remove `Hash#each` type restriction to allow working with splats. ([#9456](https://github.com/crystal-lang/crystal/pull/9456), thanks @bcardiff)\n\n#### Networking\n\n- Revert `IO#write` changes in 0.35.0 and let it return Nil. ([#9469](https://github.com/crystal-lang/crystal/pull/9469), thanks @bcardiff)\n- Avoid leaking logging context in HTTP request handlers. ([#9494](https://github.com/crystal-lang/crystal/pull/9494), thanks @Blacksmoke16)\n\n#### Crypto\n\n- Use less strict cipher compatibility for OpenSSL client context. ([#9459](https://github.com/crystal-lang/crystal/pull/9459), thanks @straight-shoota)\n- Fix `Digest::Base` block argument type restrictions. ([#9500](https://github.com/crystal-lang/crystal/pull/9500), thanks @straight-shoota)\n\n#### Logging\n\n- Fix `Log.context.set` docs for hash based data. ([#9470](https://github.com/crystal-lang/crystal/pull/9470), thanks @bcardiff)\n\n### Compiler\n\n- Show warnings even if there are errors. ([#9461](https://github.com/crystal-lang/crystal/pull/9461), thanks @asterite)\n- Fix parsing of `{foo: X, typeof: Y}` type. ([#9453](https://github.com/crystal-lang/crystal/pull/9453), thanks @MakeNowJust)\n- Fix parsing of proc in hash `of` key type. ([#9458](https://github.com/crystal-lang/crystal/pull/9458), thanks @MakeNowJust)\n- Revert debug level information changes in specs to fix 32 bits builds. ([#9466](https://github.com/crystal-lang/crystal/pull/9466), thanks @bcardiff)\n\n### Others\n\n- CI improvements and housekeeping. ([#9455](https://github.com/crystal-lang/crystal/pull/9455), thanks @bcardiff)\n- Code formatting. ([#9482](https://github.com/crystal-lang/crystal/pull/9482), thanks @MakeNowJust)\n\n## 0.35.0 (2020-06-09)\n\n### Language changes\n\n- **(breaking-change)** Let `case when` be non-exhaustive, introduce `case in` as exhaustive. ([#9258](https://github.com/crystal-lang/crystal/pull/9258), [#9045](https://github.com/crystal-lang/crystal/pull/9045), thanks @asterite)\n- Allow `->@ivar.foo` and `->@@cvar.foo` expressions. ([#9268](https://github.com/crystal-lang/crystal/pull/9268), thanks @MakeNowJust)\n\n#### Macros\n\n- Allow executing OpAssign (`+=`, `||=`, etc.) inside macros. ([#9409](https://github.com/crystal-lang/crystal/pull/9409), thanks @asterite)\n\n### Standard library\n\n- **(breaking-change)** Refactor to standardize on first argument for methods receiving `IO`. ([#9134](https://github.com/crystal-lang/crystal/pull/9134), [#9289](https://github.com/crystal-lang/crystal/pull/9289), [#9303](https://github.com/crystal-lang/crystal/pull/9303), [#9318](https://github.com/crystal-lang/crystal/pull/9318), thanks @straight-shoota, @bcardiff, @oprypin)\n- **(breaking-change)** Cleanup Digest and OpenSSL::Digest. ([#8426](https://github.com/crystal-lang/crystal/pull/8426), thanks @didactic-drunk)\n- Fix `Enum#to_s` for private enum. ([#9126](https://github.com/crystal-lang/crystal/pull/9126), thanks @straight-shoota)\n- Refactor `Benchmark::IPS::Entry` to use `UInt64` in `bytes_per_op`. ([#9081](https://github.com/crystal-lang/crystal/pull/9081), thanks @jhass)\n- Add `Experimental` annotation and doc label. ([#9244](https://github.com/crystal-lang/crystal/pull/9244), thanks @bcardiff)\n- Add subcommands to `OptionParser`. ([#9009](https://github.com/crystal-lang/crystal/pull/9009), [#9133](https://github.com/crystal-lang/crystal/pull/9133), thanks @RX14, @Sija)\n- Make `NamedTuple#sorted_keys` public. ([#9263](https://github.com/crystal-lang/crystal/pull/9263), thanks @waj)\n- Fix example codes in multiple places. ([#9203](https://github.com/crystal-lang/crystal/pull/9203), thanks @maiha)\n\n#### Macros\n\n- **(breaking-change)** Remove top-level `assert_responds_to` macro. ([#9085](https://github.com/crystal-lang/crystal/pull/9085), thanks @bcardiff)\n- **(breaking-change)** Drop top-level `parallel` macro. ([#9097](https://github.com/crystal-lang/crystal/pull/9097), thanks @bcardiff)\n- Fix lazy property not forwarding annotations. ([#9140](https://github.com/crystal-lang/crystal/pull/9140), thanks @asterite)\n- Add `host_flag?` macro method, not affected by cross-compilation. ([#9049](https://github.com/crystal-lang/crystal/pull/9049), thanks @oprypin)\n- Add `.each` and `.each_with_index` to various macro types. ([#9120](https://github.com/crystal-lang/crystal/pull/9120), thanks @Blacksmoke16)\n- Add `StringLiteral#titleize` macro method. ([#9269](https://github.com/crystal-lang/crystal/pull/9269), thanks @MakeNowJust)\n- Add `TypeNode` methods to check what \"type\" the node is. ([#9270](https://github.com/crystal-lang/crystal/pull/9270), thanks @Blacksmoke16)\n- Fix support `TypeNode.name(generic_args: false)` for generic instances. ([#9224](https://github.com/crystal-lang/crystal/pull/9224), thanks @Blacksmoke16)\n\n#### Numeric\n\n- **(breaking-change)** Add `Int#digits`, reverse `BigInt#digits` result. ([#9383](https://github.com/crystal-lang/crystal/pull/9383), thanks @asterite)\n- Fix overflow checking for operations with mixed sign. ([#9403](https://github.com/crystal-lang/crystal/pull/9403), thanks @waj)\n- Add `BigInt#factorial` using GMP. ([#9132](https://github.com/crystal-lang/crystal/pull/9132), thanks @peheje)\n\n#### Text\n\n- Add `String#titleize`. ([#9204](https://github.com/crystal-lang/crystal/pull/9204), thanks @hugopl)\n- Add `Regex#matches?` and `String#matches?`. ([#8989](https://github.com/crystal-lang/crystal/pull/8989), thanks @MakeNowJust)\n- Add `IO` overloads to various `String` case methods. ([#9236](https://github.com/crystal-lang/crystal/pull/9236), thanks @Blacksmoke16)\n- Improve docs examples regarding `Regex::MatchData`. ([#9010](https://github.com/crystal-lang/crystal/pull/9010), thanks @MakeNowJust)\n- Improve docs on `String` methods. ([#8447](https://github.com/crystal-lang/crystal/pull/8447), thanks @jan-zajic)\n\n#### Collections\n\n- **(breaking-change)** Add `Enumerable#first` with fallback block. ([#8999](https://github.com/crystal-lang/crystal/pull/8999), thanks @MakeNowJust)\n- Fix `Array#delete_at` bug with negative start index. ([#9399](https://github.com/crystal-lang/crystal/pull/9399), thanks @asterite)\n- Fix `Enumerable#{zip,zip?}` when self is an `Iterator`. ([#9330](https://github.com/crystal-lang/crystal/pull/9330), thanks @mneumann)\n- Make `Range#each` and `Range#reverse_each` work better with end/begin-less values. ([#9325](https://github.com/crystal-lang/crystal/pull/9325), thanks @asterite)\n- Improve docs on `Hash`. ([#8887](https://github.com/crystal-lang/crystal/pull/8887), thanks @rdp)\n\n#### Serialization\n\n- **(breaking-change)** Deprecate `JSON.mapping` and `YAML.mapping`. ([#9272](https://github.com/crystal-lang/crystal/pull/9272), thanks @straight-shoota)\n- **(breaking-change)** Make `INI` a module. ([#9408](https://github.com/crystal-lang/crystal/pull/9408), thanks @j8r)\n- Fix integration between `record` macro and `JSON::Serializable`/`YAML::Serializable` regarding default values. ([#9063](https://github.com/crystal-lang/crystal/pull/9063), thanks @Blacksmoke16)\n- Fix `XML.parse` invalid mem access in multi-thread. ([#9098](https://github.com/crystal-lang/crystal/pull/9098), thanks @bcardiff, @asterite)\n- Fix double string escape in `XML::Node#content=`. ([#9300](https://github.com/crystal-lang/crystal/pull/9300), thanks @straight-shoota)\n- Improve xpath regarding namespaces. ([#9288](https://github.com/crystal-lang/crystal/pull/9288), thanks @asterite)\n- Escape CDATA end sequences. ([#9230](https://github.com/crystal-lang/crystal/pull/9230), thanks @Blacksmoke16)\n- Add `JSON` and `YAML` serialization to `Path`. ([#9156](https://github.com/crystal-lang/crystal/pull/9156), thanks @straight-shoota)\n- Specify pkg-config name for `libyaml`. ([#9426](https://github.com/crystal-lang/crystal/pull/9426), thanks @jhass)\n- Specify pkg-config name for `libxml2`. ([#9436](https://github.com/crystal-lang/crystal/pull/9436), thanks @Blacksmoke16)\n- Make YAML specs robust against libyaml 0.2.5. ([#9427](https://github.com/crystal-lang/crystal/pull/9427), thanks @jhass)\n\n#### Time\n\n- **(breaking-change)** Support different number of fraction digits for RFC3339 time format. ([#9283](https://github.com/crystal-lang/crystal/pull/9283), thanks @waj)\n- Fix parsing AM/PM hours. ([#9334](https://github.com/crystal-lang/crystal/pull/9334), thanks @straight-shoota)\n- Improve `File.utime` precision from second to 100-nanosecond on Windows. ([#9344](https://github.com/crystal-lang/crystal/pull/9344), thanks @kubo)\n\n#### Files\n\n- **(breaking-change)** Move `Flate`, `Gzip`, `Zip`, `Zlib` to `Compress`. ([#8886](https://github.com/crystal-lang/crystal/pull/8886), thanks @bcardiff)\n- **(breaking-change)** Cleanup `File` & `FileUtils`. ([#9175](https://github.com/crystal-lang/crystal/pull/9175), thanks @bcardiff)\n- Fix realpath on macOS 10.15 (Catalina). ([#9296](https://github.com/crystal-lang/crystal/pull/9296), thanks @waj)\n- Fix `File#pos`, `File#seek` and `File#truncate` over 2G on Windows. ([#9015](https://github.com/crystal-lang/crystal/pull/9015), thanks @kubo)\n- Fix `File.rename` to overwrite the destination file on Windows, like elsewhere. ([#9038](https://github.com/crystal-lang/crystal/pull/9038), thanks @oprypin)\n- Fix `File`'s specs and related exception types on Windows. ([#9037](https://github.com/crystal-lang/crystal/pull/9037), thanks @oprypin)\n- Add support for `Path` arguments to multiple methods. ([#9153](https://github.com/crystal-lang/crystal/pull/9153), thanks @straight-shoota)\n- Add `Path#each_part` iterator. ([#9138](https://github.com/crystal-lang/crystal/pull/9138), thanks @straight-shoota)\n- Add `Path#relative_to`. ([#9169](https://github.com/crystal-lang/crystal/pull/9169), thanks @straight-shoota)\n- Add support for `Path` pattern to `Dir.glob`. ([#9420](https://github.com/crystal-lang/crystal/pull/9420), thanks @straight-shoota)\n- Implement `File#fsync` on Windows. ([#9257](https://github.com/crystal-lang/crystal/pull/9257), thanks @kubo)\n- Refactor `Path` regarding empty and `.`. ([#9137](https://github.com/crystal-lang/crystal/pull/9137), thanks @straight-shoota)\n\n#### Networking\n\n- **(breaking-change)** Make `IO#skip`, `IO#write` returns the number of bytes it skipped/written as `Int64`. ([#9233](https://github.com/crystal-lang/crystal/pull/9233), [#9363](https://github.com/crystal-lang/crystal/pull/9363), thanks @bcardiff)\n- **(breaking-change)** Improve error handling and logging in `HTTP::Server`. ([#9115](https://github.com/crystal-lang/crystal/pull/9115), [#9034](https://github.com/crystal-lang/crystal/pull/9034), thanks @waj, @straight-shoota)\n- **(breaking-change)** Change `HTTP::Request#remote_address` type to `Socket::Address?`. ([#9210](https://github.com/crystal-lang/crystal/pull/9210), thanks @waj)\n- Fix `flush` methods to always flush underlying `IO`. ([#9320](https://github.com/crystal-lang/crystal/pull/9320), thanks @straight-shoota)\n- Fix `HTTP::Server` sporadic failure in SSL handshake. ([#9177](https://github.com/crystal-lang/crystal/pull/9177), thanks @waj)\n- `WebSocket` shouldn't reply with same close code. ([#9313](https://github.com/crystal-lang/crystal/pull/9313), thanks @waj)\n- Ignore response body during `WebSocket` handshake. ([#9418](https://github.com/crystal-lang/crystal/pull/9418), thanks @waj)\n- Treat cookies which expire in this instant as expired. ([#9061](https://github.com/crystal-lang/crystal/pull/9061), thanks @RX14)\n- Set `sync` or `flush_on_newline` for standard I/O on Windows. ([#9207](https://github.com/crystal-lang/crystal/pull/9207), thanks @kubo)\n- Prefer HTTP basic authentication in OAuth2 client. ([#9127](https://github.com/crystal-lang/crystal/pull/9127), thanks @crush-157)\n- Defer request upgrade in `HTTP::Server` (aka: WebSockets). ([#9243](https://github.com/crystal-lang/crystal/pull/9243), thanks @waj)\n- Improve `URI::Punycode`, `HTTP::WebSocketHandler`, `HTTP::Status` documentation. ([#9068](https://github.com/crystal-lang/crystal/pull/9068), [#9130](https://github.com/crystal-lang/crystal/pull/9130), [#9180](https://github.com/crystal-lang/crystal/pull/9180), thanks @Blacksmoke16, @dscottboggs, @wontruefree)\n- Remove `HTTP::Params::Builder#to_s`, use underlying `IO` directly. ([#9319](https://github.com/crystal-lang/crystal/pull/9319), thanks @straight-shoota)\n- Fixed some regular failing specs in multi-thread mode. ([#9412](https://github.com/crystal-lang/crystal/pull/9412), thanks @bcardiff)\n\n#### Crypto\n\n- **(security)** Update SSL server secure defaults. ([#9026](https://github.com/crystal-lang/crystal/pull/9026), thanks @straight-shoota)\n- Add LibSSL `NO_TLS_V1_3` option. ([#9350](https://github.com/crystal-lang/crystal/pull/9350), thanks @lun-4)\n\n#### Logging\n\n- **(breaking-change)** Rename `Log::Severity::Warning` to `Warn`. Drop `Verbose`. Add `Trace` and `Notice`. ([#9293](https://github.com/crystal-lang/crystal/pull/9293), [#9107](https://github.com/crystal-lang/crystal/pull/9107), [#9316](https://github.com/crystal-lang/crystal/pull/9316), thanks @bcardiff, @paulcsmith)\n- **(breaking-change)** Allow local data on entries via `Log::Metadata` and redesign `Log::Context`. ([#9118](https://github.com/crystal-lang/crystal/pull/9118), [#9227](https://github.com/crystal-lang/crystal/pull/9227), [#9150](https://github.com/crystal-lang/crystal/pull/9150), [#9157](https://github.com/crystal-lang/crystal/pull/9157), thanks @bcardiff, @waj)\n- **(breaking-change)** Split top-level `Log::Metadata` from `Log::Metadata::Value`, drop immutability via clone, improve performance. ([#9295](https://github.com/crystal-lang/crystal/pull/9295), thanks @bcardiff)\n- **(breaking-change)** Rework `Log.setup_from_env` and defaults. ([#9145](https://github.com/crystal-lang/crystal/pull/9145), [#9240](https://github.com/crystal-lang/crystal/pull/9240),  thanks @bcardiff)\n- Add `Log.capture` spec helper. ([#9201](https://github.com/crystal-lang/crystal/pull/9201), thanks @bcardiff)\n- Redesign `Log::Formatter`. ([#9211](https://github.com/crystal-lang/crystal/pull/9211), thanks @waj)\n- Add `to_json` for `Log::Context`. ([#9101](https://github.com/crystal-lang/crystal/pull/9101), thanks @paulcsmith)\n- Add `Log::IOBackend#new` with `formatter` named argument. ([#9105](https://github.com/crystal-lang/crystal/pull/9105), [#9434](https://github.com/crystal-lang/crystal/pull/9434), thanks @paulcsmith, @bcardiff)\n- Allow `nil` as context raw values. ([#9121](https://github.com/crystal-lang/crystal/pull/9121), thanks @bcardiff)\n- Add missing `Log#with_context`. ([#9058](https://github.com/crystal-lang/crystal/pull/9058), thanks @bcardiff)\n- Fix types referred in documentation. ([#9117](https://github.com/crystal-lang/crystal/pull/9117), thanks @bcardiff)\n- Allow override context within logging calls. ([#9146](https://github.com/crystal-lang/crystal/pull/9146), thanks @bcardiff)\n- Check severity before backend. ([#9400](https://github.com/crystal-lang/crystal/pull/9400), thanks @asterite)\n\n#### Concurrency\n\n- **(breaking-change)** Drop `Concurrent::Future` and top-level methods `delay`, `future`, `lazy`. Use [crystal-community/future.cr](https://github.com/crystal-community/future.cr). ([#9093](https://github.com/crystal-lang/crystal/pull/9093), thanks @bcardiff)\n\n#### System\n\n- **(breaking-change)** Deprecate `Process#kill`, use `Process#signal`. ([#9006](https://github.com/crystal-lang/crystal/pull/9006), thanks @oprypin, @jan-zajic)\n- `Process` raises `IO::Error` (or subclasses). ([#9340](https://github.com/crystal-lang/crystal/pull/9340), thanks @waj)\n- Add `Process.quote` and fix shell usages in the compiler. ([#9043](https://github.com/crystal-lang/crystal/pull/9043), [#9369](https://github.com/crystal-lang/crystal/pull/9369), thanks @oprypin, @bcardiff)\n- Implement `Process` support on Windows. ([#9047](https://github.com/crystal-lang/crystal/pull/9047), [#9021](https://github.com/crystal-lang/crystal/pull/9021), [#9122](https://github.com/crystal-lang/crystal/pull/9122), [#9112](https://github.com/crystal-lang/crystal/pull/9112), [#9149](https://github.com/crystal-lang/crystal/pull/9149), [#9310](https://github.com/crystal-lang/crystal/pull/9310), thanks @oprypin, @RX14, @kubo, @jan-zajic)\n- Fix compile-time checking of `dup3`/`clock_gettime` methods definition. ([#9407](https://github.com/crystal-lang/crystal/pull/9407), thanks @asterite)\n- Use `Int64` as portable `Process.pid` type. ([#9019](https://github.com/crystal-lang/crystal/pull/9019), thanks @oprypin)\n\n#### Runtime\n\n- **(breaking-change)** Deprecate top-level `fork`. ([#9136](https://github.com/crystal-lang/crystal/pull/9136), thanks @bcardiff)\n- **(breaking-change)** Move `Debug` to `Crystal` namespace. ([#9176](https://github.com/crystal-lang/crystal/pull/9176), thanks @bcardiff)\n- Fix segfaults when static linking with musl. ([#9238](https://github.com/crystal-lang/crystal/pull/9238), thanks @waj)\n- Allow calling `at_exit` inside `at_exit`. ([#9388](https://github.com/crystal-lang/crystal/pull/9388), thanks @asterite)\n- Rework DWARF loading and fix empty backtraces in musl. ([#9267](https://github.com/crystal-lang/crystal/pull/9267), thanks @waj)\n- Add DragonFly(BSD) support. ([#9178](https://github.com/crystal-lang/crystal/pull/9178), thanks @mneumann)\n- Add `Crystal::System::Process` to split out system-specific implementations. ([#9035](https://github.com/crystal-lang/crystal/pull/9035), thanks @oprypin)\n- Move internal `CallStack` to `Exception::CallStack`. ([#9076](https://github.com/crystal-lang/crystal/pull/9076), thanks @bcardiff)\n- Specify pkg-config name for `libevent`. ([#9395](https://github.com/crystal-lang/crystal/pull/9395), thanks @jhass)\n\n#### Spec\n\n- Reference global Spec in `be_a` macro. ([#9066](https://github.com/crystal-lang/crystal/pull/9066), thanks @asterite)\n- Add `-h` short flag to spec runner. ([#9164](https://github.com/crystal-lang/crystal/pull/9164), thanks @straight-shoota)\n- Fix `crystal spec` file paths on Windows. ([#9234](https://github.com/crystal-lang/crystal/pull/9234), thanks @oprypin)\n- Refactor spec hooks. ([#9090](https://github.com/crystal-lang/crystal/pull/9090), thanks @straight-shoota)\n\n### Compiler\n\n- **(breaking-change)** Improve compiler single-file run syntax to make it shebang-friendly `#!`. ([#9171](https://github.com/crystal-lang/crystal/pull/9171), thanks @RX14)\n- **(breaking-change)** Use `Process.quote` for `crystal env` output. ([#9428](https://github.com/crystal-lang/crystal/pull/9428), thanks @MakeNowJust)\n- **(breaking-change)** Simplify `Link` annotation handling. ([#8972](https://github.com/crystal-lang/crystal/pull/8972), thanks @RX14)\n- Fix parsing of `foo:\"bar\"` inside call or named tuple. ([#9033](https://github.com/crystal-lang/crystal/pull/9033), thanks @asterite)\n- Fix parsing of anonymous splat and block arg. ([#9113](https://github.com/crystal-lang/crystal/pull/9113), thanks @MakeNowJust)\n- Fix parsing of `unless` inside macro. ([#9024](https://github.com/crystal-lang/crystal/pull/9024), [#9167](https://github.com/crystal-lang/crystal/pull/9167), thanks @MakeNowJust)\n- Fix parsing of `\\` (backslash + space) inside regex literal to ` ` (space). ([#9079](https://github.com/crystal-lang/crystal/pull/9079), thanks @MakeNowJust)\n- Fix parsing of ambiguous '+' and '-'. ([#9194](https://github.com/crystal-lang/crystal/pull/9194), thanks @max-codeware)\n- Fix parsing of capitalized named argument. ([#9232](https://github.com/crystal-lang/crystal/pull/9232), thanks @asterite)\n- Fix parsing of `{[] of Foo, self.foo}` expressions. ([#9329](https://github.com/crystal-lang/crystal/pull/9329), thanks @MakeNowJust)\n- Fix cast fun function pointer to Proc. ([#9287](https://github.com/crystal-lang/crystal/pull/9287), thanks @asterite)\n- Make compiler warn on deprecated macros. ([#9343](https://github.com/crystal-lang/crystal/pull/9343), thanks @bcardiff)\n- Basic support for Win64 C lib ABI. ([#9387](https://github.com/crystal-lang/crystal/pull/9387), thanks @oprypin)\n- Make the compiler able to run on Windows and compile itself. ([#9054](https://github.com/crystal-lang/crystal/pull/9054), [#9062](https://github.com/crystal-lang/crystal/pull/9062), [#9095](https://github.com/crystal-lang/crystal/pull/9095), [#9106](https://github.com/crystal-lang/crystal/pull/9106), [#9307](https://github.com/crystal-lang/crystal/pull/9307), thanks @oprypin, @Sija)\n- Add docs regarding `CRYSTAL_OPTS`. ([#9018](https://github.com/crystal-lang/crystal/pull/9018), thanks @straight-shoota)\n- Remove `Process.run(\"which\")` from compiler. ([#9141](https://github.com/crystal-lang/crystal/pull/9141), thanks @straight-shoota)\n- Refactor type parser. ([#9208](https://github.com/crystal-lang/crystal/pull/9208), thanks @MakeNowJust)\n- Refactor & clean-up in compiler. ([#8781](https://github.com/crystal-lang/crystal/pull/8781), [#9195](https://github.com/crystal-lang/crystal/pull/9195), thanks @rhysd, @straight-shoota)\n- Refactor `CrystalPath::Error`. ([#9359](https://github.com/crystal-lang/crystal/pull/9359), thanks @straight-shoota)\n- Refactor and improvements on spec_helper. ([#9367](https://github.com/crystal-lang/crystal/pull/9367), [#9059](https://github.com/crystal-lang/crystal/pull/9059), [#9393](https://github.com/crystal-lang/crystal/pull/9393), [#9351](https://github.com/crystal-lang/crystal/pull/9351), [#9402](https://github.com/crystal-lang/crystal/pull/9402), thanks @straight-shoota, @jhass, @oprypin)\n- Split general ABI specs from x86_64-specific ones, run on every platform. ([#9384](https://github.com/crystal-lang/crystal/pull/9384), thanks @oprypin)\n\n#### Language semantics\n\n- Fix `RegexLiteral#to_s` output when first character of literal is whitespace. ([#9017](https://github.com/crystal-lang/crystal/pull/9017), thanks @MakeNowJust)\n- Fix autocasting in multidispatch. ([#9004](https://github.com/crystal-lang/crystal/pull/9004), thanks @asterite)\n- Fix propagation of annotations into other scopes. ([#9125](https://github.com/crystal-lang/crystal/pull/9125), thanks @asterite)\n- Fix yield computation inside macro code. ([#9324](https://github.com/crystal-lang/crystal/pull/9324), thanks @asterite)\n- Fix incorrect type generated with `as?` when type is a union. ([#9417](https://github.com/crystal-lang/crystal/pull/9417), [#9435](https://github.com/crystal-lang/crystal/pull/9435), thanks @asterite)\n- Don't duplicate instance var in inherited generic type. ([#9433](https://github.com/crystal-lang/crystal/pull/9433), thanks @asterite)\n- Ensure `type_vars` works for generic modules. ([#9161](https://github.com/crystal-lang/crystal/pull/9161), thanks @toddsundsted)\n- Make autocasting work in default values against unions. ([#9366](https://github.com/crystal-lang/crystal/pull/9366), thanks @asterite)\n- Skip no closure check for non-Crystal procs. ([#9248](https://github.com/crystal-lang/crystal/pull/9248), thanks @jhass)\n\n#### Debugger\n\n- Improve debugging support. ([#8538](https://github.com/crystal-lang/crystal/pull/8538), thanks @skuznetsov)\n- Move recent additions to `DIBuilder` to `LLVMExt`. ([#9114](https://github.com/crystal-lang/crystal/pull/9114), thanks @bcardiff)\n\n### Tools\n\n#### Formatter\n\n- Fix formatting of regex after some comments. ([#9109](https://github.com/crystal-lang/crystal/pull/9109), thanks @MakeNowJust)\n- Fix formatting of `&.!`. ([#9391](https://github.com/crystal-lang/crystal/pull/9391), thanks @MakeNowJust)\n- Avoid crash on heredoc with interpolations. ([#9382](https://github.com/crystal-lang/crystal/pull/9382), thanks @MakeNowJust)\n- Refactor: code clean-up. ([#9231](https://github.com/crystal-lang/crystal/pull/9231), thanks @MakeNowJust)\n\n#### Doc generator\n\n- Fix links to methods with `String` default values. ([#9200](https://github.com/crystal-lang/crystal/pull/9200), thanks @bcardiff)\n- Fix syntax highlighting of heredoc. ([#9396](https://github.com/crystal-lang/crystal/pull/9396), thanks @MakeNowJust)\n- Correctly attach docs before annotations to following types. ([#9332](https://github.com/crystal-lang/crystal/pull/9332), thanks @asterite)\n- Allow annotations and `:ditto:` in macro. ([#9341](https://github.com/crystal-lang/crystal/pull/9341), thanks @bcardiff)\n- Add project name and version to API docs. ([#8792](https://github.com/crystal-lang/crystal/pull/8792), thanks @straight-shoota)\n- Add version selector to API docs. ([#9074](https://github.com/crystal-lang/crystal/pull/9074), [#9187](https://github.com/crystal-lang/crystal/pull/9187), [#9250](https://github.com/crystal-lang/crystal/pull/9250), [#9252](https://github.com/crystal-lang/crystal/pull/9252), [#9254](https://github.com/crystal-lang/crystal/pull/9254), thanks @straight-shoota, @bcardiff)\n- Show input type path instead of full qualified path on generic. ([#9302](https://github.com/crystal-lang/crystal/pull/9302), thanks @MakeNowJust)\n- Remove README link in API docs. ([#9082](https://github.com/crystal-lang/crystal/pull/9082), thanks @straight-shoota)\n- Remove special handling for version tags in docs generator. ([#9083](https://github.com/crystal-lang/crystal/pull/9083), thanks @straight-shoota)\n- Refactor `is_crystal_repo` based on project name. ([#9070](https://github.com/crystal-lang/crystal/pull/9070), thanks @straight-shoota)\n- Refactor `Docs::Generator` source link generation. ([#9119](https://github.com/crystal-lang/crystal/pull/9119), [#9305](https://github.com/crystal-lang/crystal/pull/9305), thanks @straight-shoota)\n\n#### Playground\n\n- Allow building compiler without 'playground', to avoid dependency on sockets. ([#9031](https://github.com/crystal-lang/crystal/pull/9031), thanks @oprypin)\n- Add support to jquery version 3. ([#9028](https://github.com/crystal-lang/crystal/pull/9028), thanks @deiv)\n\n### Others\n\n- CI improvements and housekeeping. ([#9012](https://github.com/crystal-lang/crystal/pull/9012), [#9129](https://github.com/crystal-lang/crystal/pull/9129), [#9242](https://github.com/crystal-lang/crystal/pull/9242), [#9370](https://github.com/crystal-lang/crystal/pull/9370), thanks @bcardiff)\n- Update to Shards 0.11.1. ([#9446](https://github.com/crystal-lang/crystal/pull/9446), thanks @bcardiff)\n- Tidy up Makefile and crystal env output. ([#9423](https://github.com/crystal-lang/crystal/pull/9423), thanks @bcardiff)\n- Always include `lib` directory in the `CRYSTAL_PATH`. ([#9315](https://github.com/crystal-lang/crystal/pull/9315), thanks @waj)\n- Use `SOURCE_DATE_EPOCH` only to determine compiler date. ([#9088](https://github.com/crystal-lang/crystal/pull/9088), thanks @straight-shoota)\n- Win CI: Bootstrap Crystal, build things on Windows, publish the binary. ([#9123](https://github.com/crystal-lang/crystal/pull/9123), [#9155](https://github.com/crystal-lang/crystal/pull/9155), [#9144](https://github.com/crystal-lang/crystal/pull/9144), [#9346](https://github.com/crystal-lang/crystal/pull/9346), thanks @oprypin)\n- Regenerate implementation tool sample. ([#9003](https://github.com/crystal-lang/crystal/pull/9003), thanks @nulty)\n- Avoid requiring non std-lib spec spec_helper. ([#9294](https://github.com/crystal-lang/crystal/pull/9294), thanks @bcardiff)\n- Improve grammar and fix typos. ([#9087](https://github.com/crystal-lang/crystal/pull/9087), [#9212](https://github.com/crystal-lang/crystal/pull/9212), [#9368](https://github.com/crystal-lang/crystal/pull/9368), thanks @MakeNowJust, @j8r)\n- Hide internal functions in docs. ([#9410](https://github.com/crystal-lang/crystal/pull/9410), thanks @bcardiff)\n- Advertise full `crystal spec` command for running a particular spec. ([#9103](https://github.com/crystal-lang/crystal/pull/9103), thanks @paulcsmith)\n- Update README. ([#9225](https://github.com/crystal-lang/crystal/pull/9225), [#9163](https://github.com/crystal-lang/crystal/pull/9163), thanks @danimiba, @straight-shoota)\n- Fix LICENSE and add NOTICE file. ([#3903](https://github.com/crystal-lang/crystal/pull/3903), thanks @MakeNowJust)\n\n## 0.34.0 (2020-04-06)\n\n### Language changes\n\n- **(breaking-change)** Exhaustive `case` expression check added, for now it produces warnings. ([#8424](https://github.com/crystal-lang/crystal/pull/8424), [#8962](https://github.com/crystal-lang/crystal/pull/8962), thanks @asterite, @Sija)\n- Let `Proc(T)` be used as a `Proc(Nil)`. ([#8969](https://github.com/crystal-lang/crystal/pull/8969), [#8970](https://github.com/crystal-lang/crystal/pull/8970), thanks @asterite)\n\n### Standard library\n\n- **(breaking-change)** Replace `Errno`, `WinError`, `IO::Timeout` with `RuntimeError`, `IO::TimeoutError`, `IO::Error`, `File::Error`, `Socket::Error`, and subclasses. ([#8885](https://github.com/crystal-lang/crystal/pull/8885), thanks @waj)\n- **(breaking-change)** Replace `Logger` module in favor of `Log` module. ([#8847](https://github.com/crystal-lang/crystal/pull/8847), [#8976](https://github.com/crystal-lang/crystal/pull/8976), thanks @bcardiff)\n- **(breaking-change)** Move `Adler32` and `CRC32` to `Digest`. ([#8881](https://github.com/crystal-lang/crystal/pull/8881), thanks @bcardiff)\n- **(breaking-change)** Remove `DL` module. ([#8882](https://github.com/crystal-lang/crystal/pull/8882), thanks @bcardiff)\n- Enable more win32 specs. ([#8683](https://github.com/crystal-lang/crystal/pull/8683), [#8822](https://github.com/crystal-lang/crystal/pull/8822), thanks @straight-shoota)\n- Make `SemanticVersion::Prerelease` comparable. ([#8991](https://github.com/crystal-lang/crystal/pull/8991), thanks @MakeNowJust)\n- Use `flag?(:i386)` instead of obsolete `flag?(:i686)`. ([#8863](https://github.com/crystal-lang/crystal/pull/8863), thanks @bcardiff)\n- Remove Windows workaround using `vsnprintf`. ([#8942](https://github.com/crystal-lang/crystal/pull/8942), thanks @oprypin)\n- Fixed docs broken link to ruby's prettyprint source. ([#8915](https://github.com/crystal-lang/crystal/pull/8915), thanks @matthin)\n- Update `OptionParser` example to exit on `--help`. ([#8927](https://github.com/crystal-lang/crystal/pull/8927), thanks @vlazar)\n\n#### Numeric\n\n- Fixed `Float32#to_s` corner-case. ([#8838](https://github.com/crystal-lang/crystal/pull/8838), thanks @toddsundsted)\n- Fixed make `Big*#to_u` raise on negative values. ([#8826](https://github.com/crystal-lang/crystal/pull/8826), thanks @Sija)\n- Fixed `BigDecimal#to_big_i` regression. ([#8790](https://github.com/crystal-lang/crystal/pull/8790), thanks @Sija)\n- Add `Complex#round`. ([#8819](https://github.com/crystal-lang/crystal/pull/8819), thanks @miketheman)\n- Add `Int#bit_length` and `BigInt#bit_length`. ([#8924](https://github.com/crystal-lang/crystal/pull/8924), [#8931](https://github.com/crystal-lang/crystal/pull/,8931), thanks @asterite)\n- Fixed docs of `Int#gcd`. ([#8894](https://github.com/crystal-lang/crystal/pull/8894), thanks @nard-tech)\n\n#### Text\n\n- **(breaking-change)** Deprecate top-level `with_color` in favor of `Colorize.with`. ([#8892](https://github.com/crystal-lang/crystal/pull/8892), [#8958](https://github.com/crystal-lang/crystal/pull/8958), thanks @bcardiff, @oprypin)\n- Add overloads for `String#ljust`, `String#rjust` and `String#center` that take an `IO`. ([#8923](https://github.com/crystal-lang/crystal/pull/8923), thanks @asterite)\n- Add missing `Regex#hash` and `Regex::MatchData#hash`. ([#8986](https://github.com/crystal-lang/crystal/pull/8986), thanks @MakeNowJust)\n- Upgrade Unicode to 13.0.0. ([#8906](https://github.com/crystal-lang/crystal/pull/8906), thanks @Blacksmoke16)\n- Revert deprecation of `String#codepoint_at`. ([#8902](https://github.com/crystal-lang/crystal/pull/8902), thanks @vlazar)\n- Move `Iconv` to `Crystal` namespace. ([#8890](https://github.com/crystal-lang/crystal/pull/8890), thanks @bcardiff)\n\n#### Collections\n\n- Fixed make `Range#size` raise on an open range. ([#8829](https://github.com/crystal-lang/crystal/pull/8829), thanks @Sija)\n- Add `Enumerable#empty?`. ([#8960](https://github.com/crystal-lang/crystal/pull/8960), thanks @Sija)\n- **(performance)** Optimized Implementation of `Array#fill` for zero Values. ([#8903](https://github.com/crystal-lang/crystal/pull/8903), thanks @toddsundsted)\n- Refactor `Reflect` to an `Enumerable` private definition. ([#8884](https://github.com/crystal-lang/crystal/pull/8884), thanks @bcardiff)\n\n#### Serialization\n\n- **(breaking-change)** Rename `YAML::Builder.new` with block to `YAML::Builder.build`. ([#8896](https://github.com/crystal-lang/crystal/pull/8896), thanks @straight-shoota)\n- Add `XML.build_fragment`. ([#8813](https://github.com/crystal-lang/crystal/pull/8813), thanks @straight-shoota)\n- Add `CSV#rewind`. ([#8912](https://github.com/crystal-lang/crystal/pull/8912), thanks @asterite)\n- Add `Deque#from_json` and `Deque#to_json`. ([#8850](https://github.com/crystal-lang/crystal/pull/8850), thanks @carlhoerberg)\n- Call to `IO#flush` on `CSV`, `INI`, `JSON`, `XML`, and `YAML` builders. ([#8876](https://github.com/crystal-lang/crystal/pull/8876), thanks @asterite)\n- Add docs to `Object.from_yaml`. ([#8800](https://github.com/crystal-lang/crystal/pull/8800), thanks @wowinter13)\n\n#### Time\n\n- **(breaking-change)** Improve `Time::Span` initialization API with mandatory named arguments. ([#8257](https://github.com/crystal-lang/crystal/pull/8257), [#8857](https://github.com/crystal-lang/crystal/pull/8857), thanks @dnamsons, @bcardiff)\n- Add `Time::Span#total_microseconds`. ([#8966](https://github.com/crystal-lang/crystal/pull/8966), thanks @vlazar)\n\n#### Files\n\n- Fixed multi-thread race condition by setting `fd` to `-1` on closed `File`/`Socket`. ([#8873](https://github.com/crystal-lang/crystal/pull/8873), thanks @bcardiff)\n- Fixed `File.dirname` with unicode chars. ([#8911](https://github.com/crystal-lang/crystal/pull/8911), thanks @asterite)\n- Add `IO::Buffered#flush_on_newline` back and set it to true for non-tty. ([#8935](https://github.com/crystal-lang/crystal/pull/8935), thanks @asterite)\n- Forward missing methods of `IO::Hexdump` to underlying `IO`. ([#8908](https://github.com/crystal-lang/crystal/pull/8908), thanks @carlhoerberg)\n\n#### Networking\n\n- **(breaking-change)** Correctly support WebSocket close codes. ([#8975](https://github.com/crystal-lang/crystal/pull/8975), [#8981](https://github.com/crystal-lang/crystal/pull/8981), thanks @RX14, @Sija)\n- Make `HTTP::Client` return empty `body_io` if content-length is zero. ([#8503](https://github.com/crystal-lang/crystal/pull/8503), thanks @asterite)\n- Fixed `UDP` specs in the case of a local firewall. ([#8817](https://github.com/crystal-lang/crystal/pull/8817), thanks @RX14)\n- Fixed `MIME` spec examples to not collide with actual registry. ([#8795](https://github.com/crystal-lang/crystal/pull/8795), thanks @straight-shoota)\n- Fixed `UNIXServer`, and `HTTP::WebSocket` specs to ensure server is accepting before closing. ([#8755](https://github.com/crystal-lang/crystal/pull/8755), [#8879](https://github.com/crystal-lang/crystal/pull/8879), thanks @bcardiff)\n- Add type annotation to `tls` argument in `HTTP`. ([#8678](https://github.com/crystal-lang/crystal/pull/8678), thanks @j8r)\n- Add `Location` to `HTTP::Request` common header names. ([#8992](https://github.com/crystal-lang/crystal/pull/8992), thanks @mamantoha)\n\n#### Concurrency\n\n- Add docs on `Future` regarding exceptions. ([#8860](https://github.com/crystal-lang/crystal/pull/8860), thanks @rdp)\n- Disable occasionally failing `Thread` specs on musl. ([#8801](https://github.com/crystal-lang/crystal/pull/8801), thanks @straight-shoota)\n\n#### System\n\n- Fixed typo on `src/signal.cr`. ([#8805](https://github.com/crystal-lang/crystal/pull/8805), thanks @lbguilherme)\n\n#### Runtime\n\n- Fixed exceptions not being inspectable when running binary from PATH. ([#8807](https://github.com/crystal-lang/crystal/pull/8807), thanks @willhbr)\n- Move `AtExitHandlers` to `Crystal` namespace. ([#8883](https://github.com/crystal-lang/crystal/pull/8883), thanks @bcardiff)\n\n### Compiler\n\n- **(breaking-change)** Drop `disable_overflow` compiler flag. ([#8772](https://github.com/crystal-lang/crystal/pull/8772), thanks @Sija)\n- Fixed url in \"can't infer block return type\" error message. ([#8869](https://github.com/crystal-lang/crystal/pull/8869), thanks @nilium)\n- Fixed typo in math interpreter error message. ([#8941](https://github.com/crystal-lang/crystal/pull/8941), thanks @j8r)\n- Use `CRYSTAL_OPTS` environment variable as default compiler options. ([#8900](https://github.com/crystal-lang/crystal/pull/8900), thanks @bcardiff)\n- Avoid using the default `--exclude-warnings` value if some is specified. ([#8899](https://github.com/crystal-lang/crystal/pull/8899), thanks @bcardiff)\n- Honor `LIBRARY_PATH` as default library path, and allow linking with no explicit `/usr/lib:/usr/local/lib` paths. ([#8948](https://github.com/crystal-lang/crystal/pull/8948), thanks @bcardiff)\n- Fix Windows LLVM globals codegen in non-single-module mode. ([#8978](https://github.com/crystal-lang/crystal/pull/8978), thanks @oprypin)\n- Add support for LLVM 10. ([#8940](https://github.com/crystal-lang/crystal/pull/8940), thanks @RX14)\n- Remove redundant calls to `Object.to_s` in interpolation in compiler's code. ([#8947](https://github.com/crystal-lang/crystal/pull/8947), thanks @veelenga)\n\n#### Language semantics\n\n- Type as `NoReturn` if calling method on abstract class with no concrete subclasses. ([#8870](https://github.com/crystal-lang/crystal/pull/8870), thanks @asterite)\n\n### Tools\n\n- Add `crystal init` name validation. ([#8737](https://github.com/crystal-lang/crystal/pull/8737), thanks @straight-shoota)\n\n#### Doc generator\n\n- Show warnings on docs command. ([#8880](https://github.com/crystal-lang/crystal/pull/8880), thanks @bcardiff)\n\n### Others\n\n- CI improvements and housekeeping. ([#8804](https://github.com/crystal-lang/crystal/pull/8804), [#8811](https://github.com/crystal-lang/crystal/pull/8811), [#8982](https://github.com/crystal-lang/crystal/pull/8982), thanks @bcardiff, @oprypin)\n- Update to Shards 0.10.0. ([#8988](https://github.com/crystal-lang/crystal/pull/8988), thanks @bcardiff)\n- Fix `pretty_json` sample. ([#8816](https://github.com/crystal-lang/crystal/pull/8816), thanks @asterite)\n- Fix typos throughout the codebase. ([#8971](https://github.com/crystal-lang/crystal/pull/8971), thanks @Sija)\n\n## 0.33.0 (2020-02-14)\n\n### Language changes\n\n- Allow `timeout` in select statements. ([#8506](https://github.com/crystal-lang/crystal/pull/8506), [#8705](https://github.com/crystal-lang/crystal/pull/8705), thanks @bcardiff, @firejox, @vlazar)\n\n#### Macros\n\n- Add `TypeNode#name(generic_args : BoolLiteral)` to return `TypeNode`'s name with or without type vars. ([#8483](https://github.com/crystal-lang/crystal/pull/8483), thanks @Blacksmoke16)\n\n### Standard library\n\n- **(breaking-change)** Remove several previously deprecated methods and modules: `PartialComparable`, `Crypto::Bcrypt::Password#==`, `HTTP::Server::Response#respond_with_error`, `JSON::PullParser::Kind#==`, `Symbol#==(JSON::PullParser::Kind)`, `JSON::Token#type`, `String#at`, `Time.new`, `Time.now`, `Time.utc_now`, `URI.escape`, `URI.unescape`. ([#8646](https://github.com/crystal-lang/crystal/pull/8646), [#8596](https://github.com/crystal-lang/crystal/pull/8596), thanks @bcardiff, @Blacksmoke16)\n- Fixed docs wording. ([#8606](https://github.com/crystal-lang/crystal/pull/8606), [#8784](https://github.com/crystal-lang/crystal/pull/8784), thanks @fxn)\n- Add `Object#in?`. ([#8720](https://github.com/crystal-lang/crystal/pull/8720), [#8723](https://github.com/crystal-lang/crystal/pull/8723), thanks @Sija)\n- Allow to create an enum from a symbol. ([#8634](https://github.com/crystal-lang/crystal/pull/8634), thanks @bew)\n- Add `VaList#next` for getting the next element in a variadic argument list. ([#8535](https://github.com/crystal-lang/crystal/pull/8535), [#8688](https://github.com/crystal-lang/crystal/pull/8688), thanks @ffwff, @RX14)\n- Refactor `ARGF` implementation. ([#8593](https://github.com/crystal-lang/crystal/pull/8593), thanks @arcage)\n- Fixed specs of `Colorize` on dumb terminal. ([#8673](https://github.com/crystal-lang/crystal/pull/8673), thanks @oprypin)\n- Fixed some specs on Win32. ([#8670](https://github.com/crystal-lang/crystal/pull/8670), thanks @straight-shoota)\n\n#### Numeric\n\n- Add `BigInt#unsafe_shr`. ([#8763](https://github.com/crystal-lang/crystal/pull/8763), thanks @asterite)\n- Refactor `Float#fdiv` to use binary primitive. ([#8662](https://github.com/crystal-lang/crystal/pull/8662), thanks @bcardiff)\n\n#### Text\n\n- Fixed `\\u0000` wrongly added on `String#sub(Hash)` replaces last char. ([#8644](https://github.com/crystal-lang/crystal/pull/8644), thanks @mimame)\n\n#### Collections\n\n- Fixed `Enumerable#zip` to work with union types. ([#8621](https://github.com/crystal-lang/crystal/pull/8621), thanks @asterite)\n- Fixed docs regarding `Hash`'s `initial_capacity`. ([#8569](https://github.com/crystal-lang/crystal/pull/8569), thanks @r00ster91)\n\n#### Serialization\n\n- Improved JSON deserialization into union types. ([#8689](https://github.com/crystal-lang/crystal/pull/8689), thanks @KimBurgess)\n- Fixed expected error message in libxml2 error spec. ([#8699](https://github.com/crystal-lang/crystal/pull/8699), thanks @straight-shoota)\n- Fixed `JSON::PullParser` overflow handling. ([#8698](https://github.com/crystal-lang/crystal/pull/8698), thanks @KimBurgess)\n- Fixed `JSON::Any#dig?`/`YAML::Any#dig?` on non-structure values. ([#8745](https://github.com/crystal-lang/crystal/pull/8745), thanks @Sija)\n\n#### Time\n\n- Fixed `Time#shift` over date boundaries with zone offset. ([#8742](https://github.com/crystal-lang/crystal/pull/8742), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Deprecate `File::Info#owner`, and `File::Info#group`; use `owner_id`, and `group_id`. ([#8007](https://github.com/crystal-lang/crystal/pull/8007), thanks @j8r)\n- Fixed `Path.new` receiving `Path` as first argument. ([#8753](https://github.com/crystal-lang/crystal/pull/8753), thanks @straight-shoota)\n- Fixed `File.size` and `File.info` to work with `Path` parameters. ([#8625](https://github.com/crystal-lang/crystal/pull/8625), thanks @snluu)\n- Fixed `Path` specs when `ENV[\"HOME\"]` is unset. ([#8667](https://github.com/crystal-lang/crystal/pull/8667), thanks @straight-shoota)\n- Refactor `Dir.mkdir_p` to use `Path#each_parent` and make it work on Win32. ([#8668](https://github.com/crystal-lang/crystal/pull/8668), thanks @straight-shoota)\n- Fixed `IO::MultiWriter` specs to close file before reading/deleting it. ([#8674](https://github.com/crystal-lang/crystal/pull/8674), thanks @oprypin)\n\n#### Networking\n\n- Fixed invalid call to libevent and race conditions on closed `IO` when resuming readable/writable event. ([#8707](https://github.com/crystal-lang/crystal/pull/8707), [#8733](https://github.com/crystal-lang/crystal/pull/8733), thanks @bcardiff)\n- Fixed unexpected EOF in terminated SSL connection. ([#8540](https://github.com/crystal-lang/crystal/pull/8540), thanks @rdp)\n- Fixed `HTTP::Cookie` to support `Int64` max-age values. ([#8759](https://github.com/crystal-lang/crystal/pull/8759), thanks @asterite)\n- Improve error message for `getaddrinfo` failure. ([#8498](https://github.com/crystal-lang/crystal/pull/8498), thanks @rdp)\n- Make `IO::SysCall#wait_readable` and `IO::SysCall#wait_writable` public, yet `:nodoc:`. ([#7366](https://github.com/crystal-lang/crystal/pull/7366), thanks @stakach)\n- Refactor `StaticFileHandler` to use `Path`. ([#8672](https://github.com/crystal-lang/crystal/pull/8672), thanks @straight-shoota)\n- Remove fixed date in spec. ([#8640](https://github.com/crystal-lang/crystal/pull/8640), thanks @bcardiff)\n- Remove non-portable error message in `TCPServer` spec. ([#8702](https://github.com/crystal-lang/crystal/pull/8702), thanks @straight-shoota)\n\n#### Crypto\n\n- Add `Crypto::Bcrypt::Password` check for invalid hash value. ([#6467](https://github.com/crystal-lang/crystal/pull/6467), thanks @miketheman)\n- Improve documentation for `Random::Secure`. ([#8484](https://github.com/crystal-lang/crystal/pull/8484), thanks @straight-shoota)\n\n#### Concurrency\n\n- Fixed `Future(Nil)` when the block raises. ([#8650](https://github.com/crystal-lang/crystal/pull/8650), thanks @lbguilherme)\n- Fixed `IO` closing in multi-thread mode. ([#8733](https://github.com/crystal-lang/crystal/pull/8733), thanks @bcardiff)\n- Fixed some regular failing specs in multi-thread mode. ([#8592](https://github.com/crystal-lang/crystal/pull/8592), [#8643](https://github.com/crystal-lang/crystal/pull/8643), [#8724](https://github.com/crystal-lang/crystal/pull/8724), [#8761](https://github.com/crystal-lang/crystal/pull/8761), thanks @bcardiff)\n- Add docs to `Fiber`. ([#8739](https://github.com/crystal-lang/crystal/pull/8739), thanks @straight-shoota)\n\n#### System\n\n- Enable `system` module for Win32 in prelude. ([#8661](https://github.com/crystal-lang/crystal/pull/8661), thanks @straight-shoota)\n- Handle exceptions raised at `__crystal_sigfault_handler`. ([#8743](https://github.com/crystal-lang/crystal/pull/8743), thanks @waj)\n\n#### Runtime\n\n- Fixed wrongly collected exception object by the GC. Ensure `LibUnwind::Exception` struct is not atomic. ([#8728](https://github.com/crystal-lang/crystal/pull/8728), thanks @waj)\n- Fixed reporting of non-statement rows in DWARF backtrace. ([#8499](https://github.com/crystal-lang/crystal/pull/8499), thanks @rdp)\n- Add top level exception handler. ([#8735](https://github.com/crystal-lang/crystal/pull/8735), [#8791](https://github.com/crystal-lang/crystal/pull/8791), thanks @waj)\n- Try to open stdio in non-blocking mode. ([#8787](https://github.com/crystal-lang/crystal/pull/8787), thanks @waj)\n- Allow `Crystal::System.print_error` to use `printf` like format. ([#8786](https://github.com/crystal-lang/crystal/pull/8786), thanks @bcardiff)\n\n#### Spec\n\n- **(breaking-change)** Remove previously deprecated spec method `assert`. ([#8767](https://github.com/crystal-lang/crystal/pull/8767), thanks @Blacksmoke16)\n- `Spec::JUnitFormatter` output and options enhancements. ([#8599](https://github.com/crystal-lang/crystal/pull/8599), [#8692](https://github.com/crystal-lang/crystal/pull/8692), thanks @Sija, @bcardiff)\n\n### Compiler\n\n- **(breaking-change)** Drop support for previously deprecated comma separators in enums and other cleanups. ([#8657](https://github.com/crystal-lang/crystal/pull/8657), thanks @bcardiff)\n- **(breaking-change)** Drop uppercase F32 and F64 float number suffixes. ([#8782](https://github.com/crystal-lang/crystal/pull/8782), thanks @rhysd)\n- Fixed memory corruption issues by using LLVM's `memset` and `memcpy` that matches target machine. ([#8746](https://github.com/crystal-lang/crystal/pull/8746), thanks @bcardiff)\n- Fixed ICE when trying to add type inside annotation. ([#8628](https://github.com/crystal-lang/crystal/pull/8628), thanks @asterite)\n- Fixed ICE on `typeof` in an unused block. ([#8695](https://github.com/crystal-lang/crystal/pull/8695), thanks @asterite)\n- Fixed ICE in case of wrong target triple. ([#8710](https://github.com/crystal-lang/crystal/pull/8710), thanks @Sija)\n- Fixed ICE when raising a macro exception with empty message. ([#8654](https://github.com/crystal-lang/crystal/pull/8654), thanks @jan-zajic)\n- Fixed parser bug macro with \"eenum\" in it. ([#8760](https://github.com/crystal-lang/crystal/pull/8760), thanks @asterite)\n- Change `CRYSTAL_PATH` to allow shards to override std-lib. ([#8752](https://github.com/crystal-lang/crystal/pull/8752), thanks @bcardiff)\n\n#### Language semantics\n\n- Fixed missing virtualization of `Proc` pointer. ([#8757](https://github.com/crystal-lang/crystal/pull/8757), thanks @asterite)\n- Fixed type of vars after `begin`/`rescue` if all `rescue` are unreachable. ([#8758](https://github.com/crystal-lang/crystal/pull/8758), thanks @asterite)\n- Fixed visibility propagation to macro expansions in all cases. ([#8762](https://github.com/crystal-lang/crystal/pull/8762), [#8796](https://github.com/crystal-lang/crystal/pull/8796), thanks @asterite)\n\n### Tools\n\n- Update `crystal init` to handle `.`. ([#8681](https://github.com/crystal-lang/crystal/pull/8681), thanks @jethrodaniel)\n\n#### Formatter\n\n- Fixed indent after comment inside indexer. ([#8627](https://github.com/crystal-lang/crystal/pull/8627), thanks @asterite)\n- Fixed indent of comments at the end of a proc literal. ([#8778](https://github.com/crystal-lang/crystal/pull/8778), thanks @asterite)\n- Fixed crash when formatting comment after macro. ([#8697](https://github.com/crystal-lang/crystal/pull/8697), thanks @asterite)\n- Fixed crash when formatting `exp.!`. ([#8768](https://github.com/crystal-lang/crystal/pull/8768), thanks @asterite)\n- Removes unnecessary escape sequences. ([#8619](https://github.com/crystal-lang/crystal/pull/8619), thanks @RX14)\n\n#### Doc generator\n\n- **(breaking-change)** Deprecate `ditto` and `nodoc` in favor of `:ditto:` and `:nodoc:`. ([#6362](https://github.com/crystal-lang/crystal/pull/6362), thanks @j8r)\n- Skip creation of `docs/` dir when not needed. ([#8718](https://github.com/crystal-lang/crystal/pull/8718), thanks @Sija)\n\n### Others\n\n- CI improvements and housekeeping. ([#8580](https://github.com/crystal-lang/crystal/pull/8580), [#8597](https://github.com/crystal-lang/crystal/pull/8597), [#8679](https://github.com/crystal-lang/crystal/pull/8679), [#8779](https://github.com/crystal-lang/crystal/pull/8779), thanks @bcardiff, @j8r)\n- Add Windows CI using GitHub Actions. ([#8676](https://github.com/crystal-lang/crystal/pull/8676), thanks @oprypin)\n- Add Alpine CI using CircleCI. ([#7420](https://github.com/crystal-lang/crystal/pull/7420), thanks @straight-shoota)\n- Build Alpine Docker images. ([#8708](https://github.com/crystal-lang/crystal/pull/8708), thanks @straight-shoota)\n- Allow `Makefile` to use `lld` if present (Linux only). ([#8641](https://github.com/crystal-lang/crystal/pull/8641), thanks @bcardiff)\n- Simplify script to determine installed LLVM version. ([#8605](https://github.com/crystal-lang/crystal/pull/8605), thanks @j8r)\n- Add CircleCI test summaries. ([#8617](https://github.com/crystal-lang/crystal/pull/8617), thanks @Sija)\n- Add helper scripts to identify working std-lib specs on Win32. ([#8664](https://github.com/crystal-lang/crystal/pull/8664), thanks @straight-shoota)\n\n## 0.32.1 (2019-12-18)\n\n### Standard library\n\n#### Collections\n\n- Fixed docs of `Enumerable#each_cons_pair` and `Iterator#cons_pair`. ([#8585](https://github.com/crystal-lang/crystal/pull/8585), thanks @arcage)\n\n#### Networking\n\n- Fixed `HTTP::WebSocket`'s `on_close` callback is called for all errors. ([#8552](https://github.com/crystal-lang/crystal/pull/8552), thanks @stakach)\n- Fixed sporadic failure in specs with OpenSSL 1.1+. ([#8582](https://github.com/crystal-lang/crystal/pull/8582), thanks @rdp)\n\n### Compiler\n\n#### Language semantics\n\n- Combine contiguous string literals before string interpolation. ([#8581](https://github.com/crystal-lang/crystal/pull/8581), thanks @asterite)\n\n## 0.32.0 (2019-12-11)\n\n### Language changes\n\n- Allow boolean negation to be written also as a regular method call `expr.!`. ([#8445](https://github.com/crystal-lang/crystal/pull/8445), thanks @jan-zajic)\n\n#### Macros\n\n- Add `TypeNode#class_vars` to list class variables of a type in a macro. ([#8405](https://github.com/crystal-lang/crystal/pull/8405), thanks @jan-zajic)\n- Add `TypeNode#includers` to get an array of types a module is directly included in. ([#8133](https://github.com/crystal-lang/crystal/pull/8133), thanks @Blacksmoke16)\n- Add `ArrayLiteral#map_with_index` and `TupleLiteral#map_with_index`. ([#8049](https://github.com/crystal-lang/crystal/pull/8049), thanks @Blacksmoke16)\n- Add docs for `ArrayLiteral#reduce`. ([#8379](https://github.com/crystal-lang/crystal/pull/8379), thanks @jan-zajic)\n- Add `lower:` named argument to `StringLiteral#camelcase`. ([#8429](https://github.com/crystal-lang/crystal/pull/8429), thanks @Blacksmoke16)\n\n### Standard library\n\n- **(breaking-change)** Remove `Readline` from std-lib. It's now available as a shard at [crystal-lang/crystal-readline](https://www.github.com/crystal-lang/crystal-readline) ([#8364](https://github.com/crystal-lang/crystal/pull/8364), thanks @ftarulla)\n- Move `Number#clamp` to `Comparable#clamp`. ([#8522](https://github.com/crystal-lang/crystal/pull/8522), thanks @wontruefree)\n- Allow `abort` without arguments. ([#8214](https://github.com/crystal-lang/crystal/pull/8214), thanks @dbackeus)\n- Improve error message for not-nil assertion in getters. ([#8200](https://github.com/crystal-lang/crystal/pull/8200), [#8296](https://github.com/crystal-lang/crystal/pull/8296), thanks @icy-arctic-fox)\n- Add `Enum.valid?`. ([#5716](https://github.com/crystal-lang/crystal/pull/5716), thanks @MakeNowJust)\n- Disable colored output if `TERM=dumb`. ([#8271](https://github.com/crystal-lang/crystal/pull/8271), thanks @ilanpillemer)\n- Documentation improvements. ([#7656](https://github.com/crystal-lang/crystal/pull/7656), [#8337](https://github.com/crystal-lang/crystal/pull/8337), [#8446](https://github.com/crystal-lang/crystal/pull/8446), thanks @r00ster91, @vlazar, @cserb)\n- Add docs for pseudo methods. ([#8327](https://github.com/crystal-lang/crystal/pull/8327), [#8491](https://github.com/crystal-lang/crystal/pull/8491), thanks @straight-shoota)\n- Code cleanups. ([#8270](https://github.com/crystal-lang/crystal/pull/8270), [#8368](https://github.com/crystal-lang/crystal/pull/8368), [#8404](https://github.com/crystal-lang/crystal/pull/8404), thanks @asterite, @vlazar, @arcage)\n\n#### Numeric\n\n- Fixed `%` and `Int#remainder` edge case of min int value against `-1`. ([#8321](https://github.com/crystal-lang/crystal/pull/8321), thanks @asterite)\n- Fixed `Int#gcd` types edge case and improve performance. ([#7996](https://github.com/crystal-lang/crystal/pull/7996), [#8419](https://github.com/crystal-lang/crystal/pull/8419), thanks @yxhuvud, @j8r)\n- Add `Int#bits` for accessing bit ranges. ([#8165](https://github.com/crystal-lang/crystal/pull/8165), thanks @stakach)\n- Allow `Number#round` with `UInt` argument. ([#8361](https://github.com/crystal-lang/crystal/pull/8361), thanks @igor-alexandrov)\n\n#### Text\n\n- **(breaking-change)** Implement string interpolation as a call to `String.interpolation`. ([#8400](https://github.com/crystal-lang/crystal/pull/8400), thanks @asterite)\n- **(breaking-change)** Deprecate `String#codepoint_at`, use `char_at(index).ord`. ([#8475](https://github.com/crystal-lang/crystal/pull/8475), thanks @vlazar)\n- Fixed encoding specs for musl iconv. ([#8525](https://github.com/crystal-lang/crystal/pull/8525), thanks @straight-shoota)\n- Add `String#presence`. ([#8345](https://github.com/crystal-lang/crystal/pull/8345), [#8508](https://github.com/crystal-lang/crystal/pull/8508), thanks @igor-alexandrov, @Sija)\n- Add `String#center`. ([#8557](https://github.com/crystal-lang/crystal/pull/8557), thanks @hutou)\n- **(performance)** Refactor `String#to_utf16` optimizing for ascii-only. ([#8526](https://github.com/crystal-lang/crystal/pull/8526), thanks @straight-shoota)\n- Add docs in `Levenshtein` module. ([#8386](https://github.com/crystal-lang/crystal/pull/8386), thanks @katafrakt)\n- Add docs to `Regex::Options`. ([#8448](https://github.com/crystal-lang/crystal/pull/8448), thanks @jan-zajic)\n\n#### Collections\n\n- **(breaking-change)** Deprecate `Enumerable#grep`, use `Enumerable#select`. ([#8452](https://github.com/crystal-lang/crystal/pull/8452), thanks @j8r)\n- Fixed `Enumerable#minmax`, `#min`, `#max` for partially comparable values. ([#8490](https://github.com/crystal-lang/crystal/pull/8490), thanks @TedTran2019)\n- Fixed `Hash#rehash`. ([#8450](https://github.com/crystal-lang/crystal/pull/8450), thanks @asterite)\n- Fixed `Array` range assignment index out of bounds. ([#8347](https://github.com/crystal-lang/crystal/pull/8347), thanks @asterite)\n- Fixed endless ranged support for `String#[]?` and `Array#[]?`. ([#8567](https://github.com/crystal-lang/crystal/pull/8567), thanks @KarthikMAM)\n- Add `Hash#compare_by_identity` and `Set#compare_by_identity`. ([#8451](https://github.com/crystal-lang/crystal/pull/8451), thanks @asterite)\n- Add `Enumerable#each_cons_pair` and `Iterator#cons_pair` yielding a tuple. ([#8332](https://github.com/crystal-lang/crystal/pull/8332), thanks @straight-shoota)\n- Add `offset` argument to all `map_with_index` methods. ([#8264](https://github.com/crystal-lang/crystal/pull/8264), thanks @asterite)\n- **(performance)** Optimized version of `Tuple#to_a`. ([#8265](https://github.com/crystal-lang/crystal/pull/8265), thanks @asterite)\n- Add docs to `Hash.merge!(other : Hash, &)`. ([#8380](https://github.com/crystal-lang/crystal/pull/8380), thanks @jan-zajic)\n- Add docs to `Hash.select`. ([#8391](https://github.com/crystal-lang/crystal/pull/8391), thanks @jan-zajic)\n- Add docs and specs to `Enumerable.reduce`. ([#8378](https://github.com/crystal-lang/crystal/pull/8378), thanks @jan-zajic)\n\n#### Serialization\n\n- **(breaking-change)** Make `XML::Reader#expand` raise, introduce `XML::Reader#expand?` for former behavior. ([#8186](https://github.com/crystal-lang/crystal/pull/8186), thanks @Blacksmoke16)\n- Allow `JSON.mapping` & `YAML.mapping` converter attribute to be applied to `Array` and `Hash`. ([#8156](https://github.com/crystal-lang/crystal/pull/8156), thanks @rodrigopinto)\n- Add `use_json_discriminator` and `use_yaml_discriminator` to choose type based on property value. ([#8406](https://github.com/crystal-lang/crystal/pull/8406), thanks @asterite)\n- Remove return type `self` restriction from `Object.from_json` and `Object.from_yaml`. ([#8489](https://github.com/crystal-lang/crystal/pull/8489), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Remove expand home (`~`) by default in `File.expand_path` and `Path#expand`, now opt-in argument. ([#7903](https://github.com/crystal-lang/crystal/pull/7903), thanks @didactic-drunk)\n- Fixed bugs in `Path` regarding `#dirname`, `#each_part`, `#each_parent`. ([#8415](https://github.com/crystal-lang/crystal/pull/8415), thanks @jan-zajic)\n- Fixed `GZip::Reader` and `GZip::Writer` to handle large data sizes. ([#8421](https://github.com/crystal-lang/crystal/pull/8421), thanks @straight-shoota)\n- Fixed `File::Info#same_file?` by providing access to 64 bit inode numbers. ([#8355](https://github.com/crystal-lang/crystal/pull/8355), thanks @didactic-drunk)\n\n#### Networking\n\n- Fixed `HTTP::Response#mime_type` returns `nil` on empty `Content-Type` header. ([#8464](https://github.com/crystal-lang/crystal/pull/8464), thanks @Sija)\n- Fixed handling of unidirectional SSL servers hang. ([#8481](https://github.com/crystal-lang/crystal/pull/8481), thanks @rdp)\n- Add `HTTP::Client#write_timeout`. ([#8507](https://github.com/crystal-lang/crystal/pull/8507), thanks @Sija)\n- Updated mime type of `.js` files to `text/javascript` and include `image/webp`. ([#8342](https://github.com/crystal-lang/crystal/pull/8342), thanks @mamantoha)\n- Refactor websocket protocol GUID string. ([#8339](https://github.com/crystal-lang/crystal/pull/8339), thanks @vlazar)\n\n#### Crypto\n\n- **(breaking-change)** Enforce single-line results of `OpenSSL::DigestBase#base64digest` via `Base64.strict_encode`. ([#8215](https://github.com/crystal-lang/crystal/pull/8215), thanks @j8r)\n\n#### Concurrency\n\n- Fixed `Channel` successful sent and raise behavior. ([#8284](https://github.com/crystal-lang/crystal/pull/8284), thanks @firejox)\n- Fixed `Channel#close` to be thread-safe. ([#8249](https://github.com/crystal-lang/crystal/pull/8249), thanks @firejox)\n- Fixed `select` with `receive?` and closed channels. ([#8304](https://github.com/crystal-lang/crystal/pull/8304), thanks @bcardiff)\n- Faster `Mutex` implementation and policy checks. ([#8295](https://github.com/crystal-lang/crystal/pull/8295), [#8563](https://github.com/crystal-lang/crystal/pull/8563), thanks @waj, @ysbaddaden)\n- **(performance)** Channel internals refactor and optimize. ([#8322](https://github.com/crystal-lang/crystal/pull/8322), [#8497](https://github.com/crystal-lang/crystal/pull/8497), thanks @firejox, @Sija)\n- Add docs to `Channel#send` and `Channel#close`. ([#8356](https://github.com/crystal-lang/crystal/pull/8356), thanks @lbarasti)\n- Fixed `Thread#gc_thread_handler` for Windows support. ([#8519](https://github.com/crystal-lang/crystal/pull/8519), thanks @straight-shoota)\n\n#### System\n\n- Don't close pipes used for signal handlers in multi-thread mode. ([#8465](https://github.com/crystal-lang/crystal/pull/8465), thanks @waj)\n- Fixed thread initialization on OpenBSD. ([#8293](https://github.com/crystal-lang/crystal/pull/8293), thanks @wmoxam)\n- Implement fibers for win32. ([#7995](https://github.com/crystal-lang/crystal/pull/7995), [#8513](https://github.com/crystal-lang/crystal/pull/8513), thanks @straight-shoota, @firejox)\n\n#### Runtime\n\n- Fixed fiber initialization on `-Dgc_none -Dpreview_mt`. ([#8280](https://github.com/crystal-lang/crystal/pull/8280), thanks @bcardiff)\n- Add GC profiling stats and warning bindings. ([#8281](https://github.com/crystal-lang/crystal/pull/8281), thanks @bcardiff, @benoist)\n- Refactor `callstack_spec`. ([#8308](https://github.com/crystal-lang/crystal/pull/8308), [#8395](https://github.com/crystal-lang/crystal/pull/8395), thanks @straight-shoota, @Sija)\n\n#### Spec\n\n- Fixed `--fail-fast` behaviour. ([#8453](https://github.com/crystal-lang/crystal/pull/8453), thanks @asterite)\n- Add before, after, and around hooks. ([#8302](https://github.com/crystal-lang/crystal/pull/8302), thanks @asterite)\n- Restrict the type returned by `should_not be_nil` and others. ([#8412](https://github.com/crystal-lang/crystal/pull/8412), thanks @asterite)\n- Add ability to randomize specs via `--order random|<seed-value>`. ([#8310](https://github.com/crystal-lang/crystal/pull/8310), thanks @Fryguy)\n- Add specs for `Spec` filters. ([#8242](https://github.com/crystal-lang/crystal/pull/8242), thanks @Fryguy)\n- Add ability to tag specs. ([#8068](https://github.com/crystal-lang/crystal/pull/8068), thanks @Fryguy)\n\n### Compiler\n\n- Fixed musl libc detection (Alpine 3.10 regression bug). ([#8330](https://github.com/crystal-lang/crystal/pull/8330), thanks @straight-shoota)\n- Fixed pragmas handling in macros. ([#8256](https://github.com/crystal-lang/crystal/pull/8256), thanks @asterite)\n- Fixed parser crash for 'alias Foo?'. ([#8282](https://github.com/crystal-lang/crystal/pull/8282), thanks @oprypin)\n- Fixed parser error on newline before closing parenthesis. ([#8320](https://github.com/crystal-lang/crystal/pull/8320), thanks @MakeNowJust)\n- Fixed generic subtypes edge cases triggering `no target defs` error. ([#8417](https://github.com/crystal-lang/crystal/pull/8417), thanks @asterite)\n- Fixed cleanup of local vars reachable by macros. ([#8529](https://github.com/crystal-lang/crystal/pull/8529), thanks @asterite)\n- Add support for LLVM 9. ([#8358](https://github.com/crystal-lang/crystal/pull/8358), thanks @RX14)\n- Add `--mcmodel` option to compiler. ([#8363](https://github.com/crystal-lang/crystal/pull/8363), thanks @ffwff)\n- Disallow `instance_sizeof` on union. ([#8399](https://github.com/crystal-lang/crystal/pull/8399), thanks @asterite)\n- Add mention to `crystal <command> --help` in help. ([#3628](https://github.com/crystal-lang/crystal/pull/3628), thanks @rdp)\n- Improve error message when a filename is misspelled. ([#8500](https://github.com/crystal-lang/crystal/pull/8500), thanks @rdp)\n- Show full path of locally compiled Crystal. ([#8486](https://github.com/crystal-lang/crystal/pull/8486), thanks @rdp)\n- Code cleanups. ([#8460](https://github.com/crystal-lang/crystal/pull/8460), thanks @veelenga)\n\n#### Language semantics\n\n- Fixed method lookup priority when type alias of union is used. ([#8258](https://github.com/crystal-lang/crystal/pull/8258), thanks @asterite)\n- Fixed visibility modifiers in virtual types. ([#8562](https://github.com/crystal-lang/crystal/pull/8562), thanks @asterite)\n- Fixed `sizeof(Bool)`. ([#8273](https://github.com/crystal-lang/crystal/pull/8273), thanks @asterite)\n\n### Tools\n\n#### Formatter\n\n- Fixed indent in arguments. ([#8315](https://github.com/crystal-lang/crystal/pull/8315), thanks @MakeNowJust)\n- Fixed crash related to parenthesis on generic types. ([#8501](https://github.com/crystal-lang/crystal/pull/8501), thanks @asterite)\n\n#### Doc generator\n\n- Fixed underscore type restriction in doc generator. ([#8331](https://github.com/crystal-lang/crystal/pull/8331), thanks @straight-shoota)\n- Correctly attach docs through multiple macro invocations. ([#8502](https://github.com/crystal-lang/crystal/pull/8502), thanks @asterite)\n- Allow constants to use `:ditto:`. ([#8389](https://github.com/crystal-lang/crystal/pull/8389), thanks @Blacksmoke16)\n- Add sitemap generator. ([#8348](https://github.com/crystal-lang/crystal/pull/8348), thanks @straight-shoota)\n- Add documentation for pseudo-methods: `!`, `as`, `nil?`, etc.. ([#8327](https://github.com/crystal-lang/crystal/pull/8327), [#8371](https://github.com/crystal-lang/crystal/pull/8371), thanks @straight-shoota)\n- Add clickable anchor icon next to headings. ([#8344](https://github.com/crystal-lang/crystal/pull/8344), thanks @Blacksmoke16)\n- Use `&` instead of `&block` for signature of yielding method. ([#8394](https://github.com/crystal-lang/crystal/pull/8394), thanks @j8r)\n\n#### Playground\n\n- Do not collapse whitespaces in playground sidebar. ([#8528](https://github.com/crystal-lang/crystal/pull/8528), thanks @hugopl)\n\n### Others\n\n- CI improvements and housekeeping. ([#8210](https://github.com/crystal-lang/crystal/pull/8210), [#8251](https://github.com/crystal-lang/crystal/pull/8251), [#8283](https://github.com/crystal-lang/crystal/pull/8283), [#8439](https://github.com/crystal-lang/crystal/pull/8439), [#8510](https://github.com/crystal-lang/crystal/pull/8510), thanks @bcardiff)\n- Update base docker images to `bionic` and LLVM 8.0. ([#8442](https://github.com/crystal-lang/crystal/pull/8442), thanks @bcardiff)\n- Repository clean-up. ([#8312](https://github.com/crystal-lang/crystal/pull/8312), [#8397](https://github.com/crystal-lang/crystal/pull/8397), thanks @bcardiff, @straight-shoota)\n\n## 0.31.1 (2019-09-30)\n\n### Standard library\n\n#### Numeric\n\n- Fixed overflow in `Random::Secure`. ([#8224](https://github.com/crystal-lang/crystal/pull/8224), thanks @oprypin)\n\n#### Networking\n\n- Workaround `IO::Evented#evented_write` invalid `IndexError` error. ([#8239](https://github.com/crystal-lang/crystal/pull/8239), thanks @bcardiff)\n\n#### Concurrency\n\n- Use bdw-gc upstream patch for green threads support. ([#8225](https://github.com/crystal-lang/crystal/pull/8225), thanks @bcardiff)\n- Refactor `Channel` to use records instead of tuples. ([#8227](https://github.com/crystal-lang/crystal/pull/8227), thanks @asterite)\n\n#### Spec\n\n- Add `before_suite` and `after_suite` hooks. ([#8238](https://github.com/crystal-lang/crystal/pull/8238), thanks @asterite)\n\n### Compiler\n\n- Fix debug location information when emitting main code from module. ([#8234](https://github.com/crystal-lang/crystal/pull/8234), thanks @asterite)\n\n#### Language semantics\n\n- Use virtual type for `uninitialized`. ([#8221](https://github.com/crystal-lang/crystal/pull/8221), thanks @asterite)\n\n## 0.31.0 (2019-09-23)\n\n### Language changes\n\n- Allow non-captured block args with type restriction using `& : T -> U`. ([#8117](https://github.com/crystal-lang/crystal/pull/8117), thanks @asterite)\n\n#### Macros\n\n- Ensure `@type` is devirtualized inside macros. ([#8149](https://github.com/crystal-lang/crystal/pull/8149), thanks @asterite)\n\n### Standard library\n\n- **(breaking-change)** Remove `Markdown` from the std-lib. ([#8115](https://github.com/crystal-lang/crystal/pull/8115), thanks @asterite)\n- **(breaking-change)** Deprecate `OptionParser#parse!`, use `OptionParser#parse`. ([#8041](https://github.com/crystal-lang/crystal/pull/8041), thanks @didactic-drunk)\n- Fix example codes in multiple places. ([#8194](https://github.com/crystal-lang/crystal/pull/8194), thanks @maiha)\n\n#### Numeric\n\n- **(breaking-change)** Enable overflow by default. ([#8170](https://github.com/crystal-lang/crystal/pull/8170), thanks @bcardiff)\n- **(breaking-change)** Make `/` the arithmetic division for all types. ([#8120](https://github.com/crystal-lang/crystal/pull/8120), thanks @bcardiff)\n- Add `BigDecimal#**` and `BigRational#**` (pow operator). ([#7860](https://github.com/crystal-lang/crystal/pull/7860), thanks @jwbuiter)\n- Avoid overflow exception in `Number#round(digits, base)`. ([#8204](https://github.com/crystal-lang/crystal/pull/8204), thanks @bcardiff)\n- Refactor `Int#divisible_by?` for clarity. ([#8045](https://github.com/crystal-lang/crystal/pull/8045), thanks @yxhuvud)\n\n#### Text\n\n- **(performance)** Minor `String#lchop?` ASCII-only optimization. ([#8052](https://github.com/crystal-lang/crystal/pull/8052), thanks @r00ster91)\n\n#### Collections\n\n- **(performance)** Array optimizations for small number of elements. ([#8048](https://github.com/crystal-lang/crystal/pull/8048), thanks @asterite)\n- **(performance)** Optimize `Array#*`. ([#8087](https://github.com/crystal-lang/crystal/pull/8087), thanks @asterite)\n- **(performance)** Hash now uses an open addressing algorithm. ([#8017](https://github.com/crystal-lang/crystal/pull/8017), [#8182](https://github.com/crystal-lang/crystal/pull/8182), thanks @asterite)\n- **(performance)** Optimize `Hash#to_a`, `Hash#keys` and `Hash#values`. ([#8042](https://github.com/crystal-lang/crystal/pull/8042), thanks @asterite)\n- **(performance)** Add `Hash#put` and optimize `Set#add?`. ([#8116](https://github.com/crystal-lang/crystal/pull/8116), thanks @asterite)\n- Fixed `Slice#==` for some generic instantiations, add `Slice#<=>`. ([#8074](https://github.com/crystal-lang/crystal/pull/8074), thanks @asterite)\n- Add docs on idempotence and methods involving eager evaluation in `Iterator`. ([#8053](https://github.com/crystal-lang/crystal/pull/8053), thanks @KimBurgess)\n- Add `Set#+`. ([#8121](https://github.com/crystal-lang/crystal/pull/8121), thanks @sam0x17)\n- Refactor `Hash` to use integer division instead of float division. ([#8104](https://github.com/crystal-lang/crystal/pull/8104), thanks @asterite)\n\n#### Serialization\n\n- **(breaking-change)** Rename `XML::Type` to `XML::Node::Type`, introduce `XML::Reader::Type`. ([#8134](https://github.com/crystal-lang/crystal/pull/8134), thanks @asterite)\n- Fixed JSON and YAML parsing of `NamedTuple` with nilable fields. ([#8109](https://github.com/crystal-lang/crystal/pull/8109), thanks @asterite)\n- Fixed YAML to emit unicode characters as such. ([#8132](https://github.com/crystal-lang/crystal/pull/8132), thanks @asterite)\n- Fixed INI generation of empty sections. ([#8106](https://github.com/crystal-lang/crystal/pull/8106), thanks @j8r)\n\n#### Files\n\n- **(performance)** Optimize `Path#join` by precomputing capacity if possible. ([#8078](https://github.com/crystal-lang/crystal/pull/8078), thanks @asterite)\n- **(performance)** Optimize `Path#join` for the case of joining one single part. ([#8082](https://github.com/crystal-lang/crystal/pull/8082), thanks @asterite)\n- **(performance)** Optimize `Dir.glob`. ([#8081](https://github.com/crystal-lang/crystal/pull/8081), thanks @asterite)\n- Fixed `File.basename` off-by-one corner-case. ([#8119](https://github.com/crystal-lang/crystal/pull/8119), thanks @ysbaddaden)\n- Fixed unneeded evaluation of `Path.home` on `Path.expand`. ([#8128](https://github.com/crystal-lang/crystal/pull/8128), thanks @asterite)\n- Fixed `Zip::Writer` STORED compression. ([#8142](https://github.com/crystal-lang/crystal/pull/8142), thanks @asterite)\n- Fixed missing check on `ARGF` if read_count is zero. ([#8177](https://github.com/crystal-lang/crystal/pull/8177), thanks @Blacksmoke16)\n\n#### Networking\n\n- **(breaking-change)** Replace `HTTP::Server::Response#respond_with_error` with `#respond_with_status`. ([#6988](https://github.com/crystal-lang/crystal/pull/6988), thanks @straight-shoota)\n- **(breaking-change)** Handle too long URIs and too large header fields in `HTTP::Request.from_io` and remove `HTTP::Request::BadRequest`. ([#8013](https://github.com/crystal-lang/crystal/pull/8013), thanks @straight-shoota)\n- Fixed memory leak from `SSL_new` if `ssl_accept` fails. ([#8088](https://github.com/crystal-lang/crystal/pull/8088), thanks @rdp)\n- Fixed WebSocket ipv6 hostname connection. ([#8066](https://github.com/crystal-lang/crystal/pull/8066), thanks @MrSorcus)\n- Add `URI#query_params` method. ([#8090](https://github.com/crystal-lang/crystal/pull/8090), thanks @rodrigopinto)\n- Add `URI#resolve` and `URI#relativize`. ([#7716](https://github.com/crystal-lang/crystal/pull/7716), thanks @straight-shoota)\n- Add `#clear`, `#delete`, and `#size` methods to `HTTP::Cookies`. ([#8107](https://github.com/crystal-lang/crystal/pull/8107), thanks @sam0x17)\n- Refactor `http/server_spec`. ([#8056](https://github.com/crystal-lang/crystal/pull/8056), thanks @straight-shoota)\n- Refactor UDP specs to use random port. ([#8139](https://github.com/crystal-lang/crystal/pull/8139), thanks @waj)\n\n#### Concurrency\n\n- Multithreading. ([#8112](https://github.com/crystal-lang/crystal/pull/8112), thanks @waj)\n- Delay releasing of fiber stack in multi-thread mode. ([#8138](https://github.com/crystal-lang/crystal/pull/8138), thanks @waj)\n- Make `Crystal::Scheduler.init_workers` block until workers are ready. ([#8145](https://github.com/crystal-lang/crystal/pull/8145), thanks @bcardiff)\n- Make `Crystal::ThreadLocalValue` thread-safe. ([#8168](https://github.com/crystal-lang/crystal/pull/8168), thanks @waj)\n- Let `exec_recursive` use a thread-local data structure. ([#8146](https://github.com/crystal-lang/crystal/pull/8146), thanks @asterite)\n- Add explicit return types for some channel methods. ([#8161](https://github.com/crystal-lang/crystal/pull/8161), thanks @Blacksmoke16)\n- Remove the dedicated fiber to run the event loop. ([#8173](https://github.com/crystal-lang/crystal/pull/8173), thanks @waj)\n- Fix corruption of thread linked list. ([#8196](https://github.com/crystal-lang/crystal/pull/8196), thanks @waj)\n- Workaround compile on win32 until fibers is implemented. ([#8195](https://github.com/crystal-lang/crystal/pull/8195), thanks @straight-shoota)\n\n#### System\n\n- Increase precision of `Process.times`. ([#8097](https://github.com/crystal-lang/crystal/pull/8097), thanks @jgaskins)\n\n#### Spec\n\n- **(breaking-change)** Add support for `focus`. ([#8125](https://github.com/crystal-lang/crystal/pull/8125), [#8178](https://github.com/crystal-lang/crystal/pull/8178), [#8208](https://github.com/crystal-lang/crystal/pull/8208), thanks @asterite, @straight-shoota, @bcardiff)\n\n### Compiler\n\n- Fixed ICE on declarations inside fun. ([#8076](https://github.com/crystal-lang/crystal/pull/8076), thanks @asterite)\n- Fixed missing `name_location` of some calls. ([#8192](https://github.com/crystal-lang/crystal/pull/8192), thanks @asterite)\n- Activate compiler warnings by default. ([#8171](https://github.com/crystal-lang/crystal/pull/8171), thanks @bcardiff)\n- Improve return type mismatch error. ([#8203](https://github.com/crystal-lang/crystal/pull/8203), thanks @asterite)\n- Improve `for` expression error. ([#7641](https://github.com/crystal-lang/crystal/pull/7641), thanks @r00ster91)\n\n#### Language semantics\n\n- Fixed abstract def check regarding generic ancestor lookup. ([#8098](https://github.com/crystal-lang/crystal/pull/8098), thanks @asterite)\n- Fixed missing virtualization of type arguments in `Proc` types. ([#8159](https://github.com/crystal-lang/crystal/pull/8159), thanks @asterite)\n- Fixed incorrect typing after exception handler. ([#8037](https://github.com/crystal-lang/crystal/pull/8037), thanks @asterite)\n- Fixed behaviour when a yield node can't be typed. ([#8101](https://github.com/crystal-lang/crystal/pull/8101), thanks @asterite)\n- Fixed `offsetof` on reference types. ([#8137](https://github.com/crystal-lang/crystal/pull/8137), thanks @mcr431)\n- Allow rescue var to be closured. ([#8143](https://github.com/crystal-lang/crystal/pull/8143), thanks @asterite)\n- Refactor class var and constant initialization. ([#8067](https://github.com/crystal-lang/crystal/pull/8067), [#8091](https://github.com/crystal-lang/crystal/pull/8091), thanks @waj)\n- Add runtime check for recursive initialization of class variables and constants. ([#8172](https://github.com/crystal-lang/crystal/pull/8172), thanks @waj)\n\n### Tools\n\n#### Doc generator\n\n- Fixed link to constructors of another class. ([#8110](https://github.com/crystal-lang/crystal/pull/8110), thanks @asterite)\n- Enable docs from previous def and/or ancestors to be inherited. ([#6989](https://github.com/crystal-lang/crystal/pull/6989), thanks @asterite)\n\n### Others\n\n- Update CI to use 0.30.1. ([#8032](https://github.com/crystal-lang/crystal/pull/8032), thanks @bcardiff)\n- Use LLVM 8.0 for Linux official packages. ([#8155](https://github.com/crystal-lang/crystal/pull/8155), thanks @bcardiff, @RX14)\n- Update dependencies of the build process. ([#8205](https://github.com/crystal-lang/crystal/pull/8205), thanks @bcardiff)\n- Code cleanups. ([#8033](https://github.com/crystal-lang/crystal/pull/8033), thanks @straight-shoota)\n\n## 0.30.1 (2019-08-12)\n\n### Standard library\n\n#### Numeric\n\n- Fixed `Number#humanize` digits. ([#8027](https://github.com/crystal-lang/crystal/pull/8027), thanks @straight-shoota)\n\n#### Networking\n\n- Fixed TCP socket leaking after failed SSL connect in `HTTP::Client#socket`. ([#8025](https://github.com/crystal-lang/crystal/pull/8025), thanks @straight-shoota)\n- Honor normalized header names for HTTP requests. ([#8061](https://github.com/crystal-lang/crystal/pull/8061), thanks @asterite)\n\n#### Concurrency\n\n- Don't resume fibers directly from event loop callbacks (or support for libevent 2.1.11). ([#8058](https://github.com/crystal-lang/crystal/pull/8058), thanks @waj)\n\n### Compiler\n\n- Fixed `sizeof(Nil)` and other empty types. ([#8040](https://github.com/crystal-lang/crystal/pull/8040), thanks @asterite)\n- Avoid internal globals of type i128 or u128. (or workaround [a llvm 128 bits bug](https://bugs.llvm.org/show_bug.cgi?id=42932)). ([#8063](https://github.com/crystal-lang/crystal/pull/8063), thanks @bcardiff, @asterite)\n\n#### Language semantics\n\n- Consider abstract method implementation in supertype for abstract method checks. ([#8035](https://github.com/crystal-lang/crystal/pull/8035), thanks @asterite)\n\n### Tools\n\n#### Formatter\n\n- Handle consecutive macro literals when subformatting. ([#8034](https://github.com/crystal-lang/crystal/pull/8034), thanks @asterite)\n- Fixed crash when formatting syntax error inside macro. ([#8055](https://github.com/crystal-lang/crystal/pull/8055), thanks @asterite)\n\n### Others\n\n- Use LLVM 6.0.1 for darwin official packages. ([#7994](https://github.com/crystal-lang/crystal/pull/7994), thanks @bcardiff)\n- Split std_specs in 32 bits CI. ([#8065](https://github.com/crystal-lang/crystal/pull/8065), thanks @bcardiff)\n\n## 0.30.0 (2019-08-01)\n\n### Language changes\n\n- **(breaking-change)** Enforce abstract methods return types. ([#7956](https://github.com/crystal-lang/crystal/pull/7956), [#7999](https://github.com/crystal-lang/crystal/pull/7999), [#8010](https://github.com/crystal-lang/crystal/pull/8010), thanks @asterite)\n- **(breaking-change)** Don't allow ranges to span across lines. ([#7888](https://github.com/crystal-lang/crystal/pull/7888), thanks @oprypin)\n\n#### Macros\n\n- Add `args`/`named_args` macro methods to `Annotations`. ([#7694](https://github.com/crystal-lang/crystal/pull/7694), thanks @Blacksmoke16)\n- Unify `resolve` and `types` macro methods API for `Type` and `Path` for convenience. ([#7970](https://github.com/crystal-lang/crystal/pull/7970), thanks @asterite)\n\n### Standard library\n\n- **(breaking-change)** Remove `UUID#to_slice` in favor of `UUID#bytes` to fix dangling pointer issues. ([#7901](https://github.com/crystal-lang/crystal/pull/7901), thanks @ysbaddaden)\n- **(performance)** Improve `Box` of reference types. ([#8016](https://github.com/crystal-lang/crystal/pull/8016), thanks @waj)\n- Fixed initial seed of `Random::ISAAC`. ([#7977](https://github.com/crystal-lang/crystal/pull/7977), thanks @asterite)\n- Fixed mem intrinsics for aarch64. ([#7983](https://github.com/crystal-lang/crystal/pull/7983), thanks @drujensen)\n- Add `Benchmark.memory`. ([#7835](https://github.com/crystal-lang/crystal/pull/7835), thanks @r00ster91)\n- Allow setting default capacity for `StringPool`. ([#7899](https://github.com/crystal-lang/crystal/pull/7899), thanks @carlhoerberg)\n- Add type restrictions to `INI`. ([#7831](https://github.com/crystal-lang/crystal/pull/7831), thanks @j8r)\n- Fixed `Logger` docs. ([#7898](https://github.com/crystal-lang/crystal/pull/7898), thanks @dprobinson)\n- Fix example codes in multiple places. ([#8003](https://github.com/crystal-lang/crystal/pull/8003), thanks @maiha)\n\n#### Numeric\n\n- Fixed incorrect `Int#%` overflow. ([#7980](https://github.com/crystal-lang/crystal/pull/7980), thanks @asterite)\n- Fixed inconsistency between `Float#to_s` and `BigFloat#to_s`, always show `.0` for whole numbers. ([#7982](https://github.com/crystal-lang/crystal/pull/7982), thanks @Lasvad)\n\n#### Text\n\n- Fixed unicode alternate ranges generation. ([#7924](https://github.com/crystal-lang/crystal/pull/7924), thanks @asterite)\n\n#### Collections\n\n- Add `Enumerable#tally`. ([#7921](https://github.com/crystal-lang/crystal/pull/7921), thanks @kachick)\n- Add `Enumerable#reduce?` overload with not initial value. ([#7941](https://github.com/crystal-lang/crystal/pull/7941), thanks @miketheman)\n- Fix specs of `Enumerable#min_by?`. ([#7919](https://github.com/crystal-lang/crystal/pull/7919), thanks @kachick)\n\n#### Serialization\n\n- **(breaking-change)** JSON: use enums instead of symbols. ([#7966](https://github.com/crystal-lang/crystal/pull/7966), thanks @asterite)\n- Fixed YAML deserialization of String in a union type. ([#7938](https://github.com/crystal-lang/crystal/pull/7938), thanks @asterite)\n- Validate element names in `XML::Builder`. ([#7965](https://github.com/crystal-lang/crystal/pull/7965), thanks @Blacksmoke16)\n- Allow numeric keys in JSON (ie: `Hash(Int32, String).from_json`). ([#7944](https://github.com/crystal-lang/crystal/pull/7944), thanks @asterite)\n- Add `alias`/`merge` methods to `YAML::Builder` and `YAML::Nodes::Builder`. ([#7949](https://github.com/crystal-lang/crystal/pull/7949), thanks @Blacksmoke16)\n\n#### Files\n\n- Adds `File.readlink` to match `File.symlink`. ([#7858](https://github.com/crystal-lang/crystal/pull/7858), thanks @didactic-drunk)\n\n#### Networking\n\n- **(breaking-change)** Improve URL encoding. `URI.escape` and `URI.unescape` are renamed to `URI.encode_www_form` and `URI.decode_www_form`. Add `URI.encode` and `URI.decode`. ([#7997](https://github.com/crystal-lang/crystal/pull/7997), [#8021](https://github.com/crystal-lang/crystal/pull/8021), thanks @straight-shoota, @bcardiff)\n- **(performance)** HTTP protocol parsing optimizations. ([#8002](https://github.com/crystal-lang/crystal/pull/8002), [#8009](https://github.com/crystal-lang/crystal/pull/8009), thanks @asterite)\n- Fixed `HTTP::Server` response double-close. ([#7908](https://github.com/crystal-lang/crystal/pull/7908), thanks @asterite)\n- Enforce `HTTP::Client` host argument is just a host. ([#7958](https://github.com/crystal-lang/crystal/pull/7958), thanks @asterite)\n- Allow `HTTP::Params.encode` to encode an arrays of values for a key. ([#7862](https://github.com/crystal-lang/crystal/pull/7862), thanks @rodrigopinto)\n- Forward `read_timeout`/`write_timeout` in ssl socket to underlying socket. ([#7820](https://github.com/crystal-lang/crystal/pull/7820), thanks @carlhoerberg)\n- Natively support [Same-site Cookies](https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1.1). ([#7864](https://github.com/crystal-lang/crystal/pull/7864), thanks @Blacksmoke16)\n- Allow setting buffer size for `IO::Buffered`. ([#7930](https://github.com/crystal-lang/crystal/pull/7930), thanks @carlhoerberg)\n\n#### Crypto\n\n- Require openssl algorithm in pkcs5. ([#7985](https://github.com/crystal-lang/crystal/pull/7985), thanks @will)\n- Fixed cipher expectation in `OpenSSL::SSL::Socket` spec. ([#7871](https://github.com/crystal-lang/crystal/pull/7871), thanks @j8r)\n\n#### Concurrency\n\n- Fixed `sysconf` call on OpenBSD. ([#7879](https://github.com/crystal-lang/crystal/pull/7879), thanks @jcs)\n\n#### System\n\n- Introduce `System::User` and `System::Group`. ([#7725](https://github.com/crystal-lang/crystal/pull/7725), thanks @woodruffw, @chris-huxtable)\n- Add docs for `Process::Status.exit_status` (#7873). ([#8014](https://github.com/crystal-lang/crystal/pull/8014), thanks @UlisseMini)\n\n### Compiler\n\n- Fixed codegen of `pointer.as(Nil)`. ([#8019](https://github.com/crystal-lang/crystal/pull/8019), thanks @asterite)\n- Fixed edge cases in parser and stringifier. ([#7886](https://github.com/crystal-lang/crystal/pull/7886), thanks @oprypin)\n- Fixed `concrete_types` for virtual metaclass and modules. ([#7951](https://github.com/crystal-lang/crystal/pull/7951), thanks @bcardiff)\n- Fixed incorrect `remove_indirection` in `TypeDefType`. ([#7971](https://github.com/crystal-lang/crystal/pull/7971), thanks @bcardiff)\n- Fixed missing `CRYSTAL_SPEC_COMPILER_FLAGS` usage in some more specs. ([774768](https://github.com/crystal-lang/crystal/commit/77476800836eb47c8d783e2259bf21c2992f2041), thanks @bcardiff)\n- Revamp compile error formatting & output. ([#7748](https://github.com/crystal-lang/crystal/pull/7748), thanks @martimatix)\n- Add support for LLVM 8. ([#7987](https://github.com/crystal-lang/crystal/pull/7987), thanks @bcardiff)\n- Add support for LLVM 7. ([#7986](https://github.com/crystal-lang/crystal/pull/7986), thanks @bcardiff, @waj, @foutrelis, @wmoxam)\n- Add debug log helper function for codegen. ([#7935](https://github.com/crystal-lang/crystal/pull/7935), [#7937](https://github.com/crystal-lang/crystal/pull/7937), thanks @bcardiff)\n- Refactor codegen of unions. ([#7940](https://github.com/crystal-lang/crystal/pull/7940), thanks @bcardiff)\n- Move `LLVMId` from `CodeGenVisitor` to `Program`. ([#7973](https://github.com/crystal-lang/crystal/pull/7973), thanks @bcardiff)\n- Minor additions and refactors on for LLVM codegen. ([#7972](https://github.com/crystal-lang/crystal/pull/7972), thanks @bcardiff)\n- Add `bin/check-compiler-flag` helper script. Add `make clean_cache`. ([da3892](https://github.com/crystal-lang/crystal/commit/da38927f3a00f1e6e5ea86b96ca669533f0aa438), thanks @bcardiff)\n\n#### Language semantics\n\n- Fixed generic metaclass argument expansion. ([#7916](https://github.com/crystal-lang/crystal/pull/7916), thanks @asterite)\n- Fixed top-level private const not being scoped. ([#7907](https://github.com/crystal-lang/crystal/pull/7907), thanks @asterite)\n- Fixed enum overflow when declaring members. ([#7881](https://github.com/crystal-lang/crystal/pull/7881), thanks @asterite)\n- Fixed annotation lookup on generic types. ([#7891](https://github.com/crystal-lang/crystal/pull/7891), thanks @asterite)\n\n### Tools\n\n#### Formatter\n\n- Format top-level inline macros. ([#7889](https://github.com/crystal-lang/crystal/pull/7889), [#7992](https://github.com/crystal-lang/crystal/pull/7992), thanks @asterite)\n\n#### Doc generator\n\n- Allow rendering tags on methods without any docs. ([#7952](https://github.com/crystal-lang/crystal/pull/7952), thanks @Blacksmoke16)\n\n### Others\n\n- Update CI to use 0.29.0. ([#7863](https://github.com/crystal-lang/crystal/pull/7863), thanks @bcardiff)\n- Automated snap publishing. ([#7893](https://github.com/crystal-lang/crystal/pull/7893), thanks @bcardiff)\n- ~~Use LLVM 6.0.1 for darwin official packages.~~ ([#7994](https://github.com/crystal-lang/crystal/pull/7994), thanks @bcardiff)\n\n## 0.29.0 (2019-06-05)\n\n### Standard library\n\n- Fix example codes in multiple places. ([#7718](https://github.com/crystal-lang/crystal/pull/7718), thanks @maiha)\n\n#### Macros\n\n- Fix inheritance support of `record` macro. ([#7811](https://github.com/crystal-lang/crystal/pull/7811), thanks @asterite)\n- Omit quotes in `puts` macro output. ([#7734](https://github.com/crystal-lang/crystal/pull/7734), thanks @asterite)\n\n#### Numeric\n\n- **(performance)** Optimize `String#to_u` methods for the case of a negative number. ([#7446](https://github.com/crystal-lang/crystal/pull/7446), thanks @r00ster91)\n\n#### Text\n\n- **(breaking-change)** Deprecate `String#at`, use `String#char_at`. ([#7633](https://github.com/crystal-lang/crystal/pull/7633), thanks @j8r)\n- **(breaking-change)** Change `String#to_i` to parse octals with prefix `0o` (but not `0` by default). ([#7691](https://github.com/crystal-lang/crystal/pull/7691), thanks @icy-arctic-fox)\n- **(breaking-change)** Restrict some `String#to_i` arguments to be `Bool`. ([#7436](https://github.com/crystal-lang/crystal/pull/7436), thanks @j8r)\n- Add `downcase` option to `String#camelcase`. ([#7717](https://github.com/crystal-lang/crystal/pull/7717), thanks @wontruefree)\n- Add support for unicode 12.0.0. ([#7721](https://github.com/crystal-lang/crystal/pull/7721), thanks @Blacksmoke16)\n- Fix `Unicode` not showing up in the API docs. ([#7720](https://github.com/crystal-lang/crystal/pull/7720), thanks @r00ster91)\n\n#### Collections\n\n- **(breaking-change)** Remove `Slice#pointer`. ([#7581](https://github.com/crystal-lang/crystal/pull/7581), thanks @Maroo-b)\n- Add sort methods to `Slice`. ([#7597](https://github.com/crystal-lang/crystal/pull/7597), thanks @Maroo-b)\n- Add `Slice#[]?`. ([#7701](https://github.com/crystal-lang/crystal/pull/7701), thanks @Sija)\n- Improve docs for `Slice#[]`. ([#7780](https://github.com/crystal-lang/crystal/pull/7780), thanks @Sija)\n\n#### Serialization\n\n- YAML: let String handle numbers too. ([#7809](https://github.com/crystal-lang/crystal/pull/7809), thanks @asterite)\n\n#### Time\n\n- Fix time format RFC 3339 to not include offset seconds. ([#7492](https://github.com/crystal-lang/crystal/pull/7492), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Rename `File::DEVNULL` to `File::NULL`. ([#7778](https://github.com/crystal-lang/crystal/pull/7778), thanks @r00ster91)\n- Fix handling of files starting with `~` in `Path#expand`. ([#7768](https://github.com/crystal-lang/crystal/pull/7768), thanks @byroot)\n- Fix `Dir.glob(match_hidden: false)` not hiding hidden files properly. ([#7774](https://github.com/crystal-lang/crystal/pull/7774), thanks @ayazhafiz)\n\n#### Networking\n\n- **(breaking-change)** Let `IO#copy` return `UInt64`. ([#7660](https://github.com/crystal-lang/crystal/pull/7660), thanks @asterite)\n- Add support for UDP multicast. ([#7423](https://github.com/crystal-lang/crystal/pull/7423), thanks @stakach)\n- Add missing requires to `openssl.cr`. ([#7803](https://github.com/crystal-lang/crystal/pull/7803), thanks @RX14)\n- Add `IO::MultiWriter#flush`. ([#7765](https://github.com/crystal-lang/crystal/pull/7765), thanks @mamantoha)\n- Add `OpenSSL::SSL::Socket#cipher` and `#tls_version`. ([#7445](https://github.com/crystal-lang/crystal/pull/7445), thanks @carlhoerberg)\n- Improve `URI#normalize`. ([#7635](https://github.com/crystal-lang/crystal/pull/7635), thanks @straight-shoota)\n- Improve documentation of some `URI` methods. ([#7796](https://github.com/crystal-lang/crystal/pull/7796), thanks @r00ster91)\n- Refactor `StaticFileHandler` specs for `Last-Modified` header. ([#7640](https://github.com/crystal-lang/crystal/pull/7640), thanks @straight-shoota)\n- Refactor compression usage in handler specs. ([#7819](https://github.com/crystal-lang/crystal/pull/7819), thanks @asterite)\n\n#### Crypto\n\n- **(breaking-change)** Rename `Crypto::Bcrypt::Password#==` to `#verify`. ([#7790](https://github.com/crystal-lang/crystal/pull/7790), thanks @asterite)\n\n#### Concurrency\n\n- Add docs for `Channel`. ([#7673](https://github.com/crystal-lang/crystal/pull/7673), thanks @j8r)\n\n### Compiler\n\n- **(breaking-change)** Fix require relative path resolution. ([#7758](https://github.com/crystal-lang/crystal/pull/7758), thanks @asterite)\n- **(breaking-change)** Disallow '!' or '?' at the end of the LHS in an assignment. ([#7582](https://github.com/crystal-lang/crystal/pull/7582), thanks @Maroo-b)\n- Allow running compiler_specs with specific flags. ([#7837](https://github.com/crystal-lang/crystal/pull/7837), thanks @bcardiff)\n- Fix extend from generic types. ([#7812](https://github.com/crystal-lang/crystal/pull/7812), thanks @asterite)\n- Don't virtualize types in `Union(...)` and keep more accurate type information. ([#7815](https://github.com/crystal-lang/crystal/pull/7815), thanks @asterite)\n- Do not generate debug metadata for arguments of naked functions. ([#7775](https://github.com/crystal-lang/crystal/pull/7775), thanks @eyusupov)\n- Detect deprecation on initialize methods and methods with named args. ([#7724](https://github.com/crystal-lang/crystal/pull/7724), thanks @bcardiff)\n- Fix track of AST nodes location. ([#7827](https://github.com/crystal-lang/crystal/pull/7827), thanks @asterite)\n- Fix `offsetof` not being usable with macros. ([#7703](https://github.com/crystal-lang/crystal/pull/7703), thanks @malte-v)\n- Allow parsing of `call &.@ivar`. ([#7754](https://github.com/crystal-lang/crystal/pull/7754), thanks @asterite)\n- Fix `Def#to_s` with `**options` and `&block`. ([#7854](https://github.com/crystal-lang/crystal/pull/7854), thanks @MakeNowJust)\n- Check `pointerof` inner expression for errors. ([#7755](https://github.com/crystal-lang/crystal/pull/7755), thanks @asterite)\n- Fix some error messages. ([#7833](https://github.com/crystal-lang/crystal/pull/7833), thanks @asterite)\n- Improve wording of `pointerof(self)` parser error. ([#7542](https://github.com/crystal-lang/crystal/pull/7542), thanks @r00ster91)\n- Fix typo. ([#7828](https://github.com/crystal-lang/crystal/pull/7828), thanks @RX14)\n\n#### Language semantics\n\n- **(breaking-change)** Fix new/initialize lookup regarding modules. ([#7818](https://github.com/crystal-lang/crystal/pull/7818), thanks @asterite)\n- **(breaking-change)**  Don't precompute `sizeof` on abstract structs and modules. ([#7801](https://github.com/crystal-lang/crystal/pull/7801), thanks @asterite)\n- Consider macro calls in `@ivar` initializer. ([#7750](https://github.com/crystal-lang/crystal/pull/7750), thanks @asterite)\n- Give precedence to `T.class` over `Class` in method lookup. ([#7759](https://github.com/crystal-lang/crystal/pull/7759), thanks @asterite)\n- Honor enum base type on non-default values. ([#7776](https://github.com/crystal-lang/crystal/pull/7776), thanks @asterite)\n- Avoid lookup of private def defined inside macro. ([#7733](https://github.com/crystal-lang/crystal/pull/7733), thanks @asterite)\n- Improve type flow of var in `if` with `&&`. ([#7785](https://github.com/crystal-lang/crystal/pull/7785), thanks @asterite)\n- Fix handling of `NoReturn` in `if`. ([#7792](https://github.com/crystal-lang/crystal/pull/7792), thanks @asterite)\n- Improve edge issues with `while` and `rescue`. ([#7806](https://github.com/crystal-lang/crystal/pull/7806), thanks @asterite)\n- Improve error on macro call in proc pointer. ([#7757](https://github.com/crystal-lang/crystal/pull/7757), thanks @asterite)\n- Fix error on named args forwarding. ([#7756](https://github.com/crystal-lang/crystal/pull/7756), thanks @asterite)\n- Check `NoReturn` type in named args. ([#7761](https://github.com/crystal-lang/crystal/pull/7761), thanks @asterite)\n- Fix internal handling of unbound/abstract generic types. ([#7781](https://github.com/crystal-lang/crystal/pull/7781), thanks @asterite)\n- Fix wrong cast to unbound generic type. ([#7793](https://github.com/crystal-lang/crystal/pull/7793), thanks @asterite)\n- Fix subclass observer to handle edge case call over generic types. ([#7735](https://github.com/crystal-lang/crystal/pull/7735), thanks @asterite)\n- Fix edge case of abstract struct with one subclass. ([#7787](https://github.com/crystal-lang/crystal/pull/7787), thanks @asterite)\n- Make automatic cast work with `with ... yield`. ([#7746](https://github.com/crystal-lang/crystal/pull/7746), thanks @asterite)\n\n### Tools\n\n- Allow to lookup class and module implementations. ([#7742](https://github.com/crystal-lang/crystal/pull/7742), thanks @MakeNowJust)\n- Refactor old duplicated 'def contains_target'. ([#7739](https://github.com/crystal-lang/crystal/pull/7739), thanks @MakeNowJust)\n\n#### Formatter\n\n- Don't produce unnecessary newline before named args following heredoc. ([#7695](https://github.com/crystal-lang/crystal/pull/7695), thanks @MakeNowJust)\n- Fix formatting of multiline call arguments. ([#7745](https://github.com/crystal-lang/crystal/pull/7745), thanks @MakeNowJust)\n- Fix formatting of annotations with newlines and spaces. ([#7744](https://github.com/crystal-lang/crystal/pull/7744), thanks @MakeNowJust)\n- Refactor code to format &.[]. ([#7699](https://github.com/crystal-lang/crystal/pull/7699), thanks @MakeNowJust)\n\n### Others\n\n- CI improvements and housekeeping. ([#7705](https://github.com/crystal-lang/crystal/pull/7705), [#7852](https://github.com/crystal-lang/crystal/pull/7852), thanks @bcardiff)\n- Move VERSION inside ./src. ([#7804](https://github.com/crystal-lang/crystal/pull/7804), thanks @bcardiff)\n\n## 0.28.0 (2019-04-17)\n\n### Language changes\n\n- **(breaking-change)** Enum declaration members can no longer be separated by a space, only by a newline, `;` or `,`, the latter being deprecated and reformatted to a newline. ([#7607](https://github.com/crystal-lang/crystal/pull/7607), [#7618](https://github.com/crystal-lang/crystal/pull/7618), thanks @asterite, and @j8r)\n- Add begin-less and end-less ranges: `array[5..]`.  ([#7179](https://github.com/crystal-lang/crystal/pull/7179), thanks @asterite)\n- Add `offsetof(Type, @ivar)` expression. ([#7589](https://github.com/crystal-lang/crystal/pull/7589), thanks @malte-v)\n\n#### Macros\n\n- Add `Type#annotations` to list all annotations and not just the last of each kind. ([#7326](https://github.com/crystal-lang/crystal/pull/7326), thanks @Blacksmoke16)\n- Add `ArrayLiteral#sort_by` macro method. ([#3947](https://github.com/crystal-lang/crystal/pull/3947), thanks @jreinert)\n\n### Standard library\n\n- **(breaking-change)** Allow creating `None` enum flag with `Enum.from_value`. ([#6516](https://github.com/crystal-lang/crystal/pull/6516), thanks @bew)\n- **(breaking-change)** Add deprecation message to `PartialComparable`. Its behaviour has been fully integrated into `Comparable`. ([#7664](https://github.com/crystal-lang/crystal/pull/7664), thanks @straight-shoota)\n- **(performance)** Optimize dwarf line numbers decoding. ([#7413](https://github.com/crystal-lang/crystal/pull/7413), thanks @asterite)\n- Fix `Signal::CHLD.reset` not clearing previous handler. ([#7409](https://github.com/crystal-lang/crystal/pull/7409), thanks @asterite)\n- Add lazy versions of `Object.getter?` and `Object.property?` macros. ([#7322](https://github.com/crystal-lang/crystal/pull/7322), thanks @Sija)\n- Allow returning other values than `-1`, `0` and `1` by `Comparable#<=>`. ([#7277](https://github.com/crystal-lang/crystal/pull/7277), thanks @r00ster91)\n- Add missing `require` statements to samples in the API docs. ([#7564](https://github.com/crystal-lang/crystal/pull/7564), thanks @Maroo-b)\n- Fix example codes in multiple places. ([#7569](https://github.com/crystal-lang/crystal/pull/7569), thanks @maiha)\n- Add documentation for `@[Flags]` and `@[Link]` annotations. ([#7665](https://github.com/crystal-lang/crystal/pull/7665), thanks @bcardiff)\n- Add documentation for `Bool`. ([#7651](https://github.com/crystal-lang/crystal/pull/7651), thanks @wontruefree)\n- Refactor to avoid usage of the thread-local `$errno` GLIBC_PRIVATE symbol. ([#7496](https://github.com/crystal-lang/crystal/pull/7496), thanks @felixvf)\n- Refactor to have similar signatures across the stdlib for `#to_s` and `#inspect`. ([#7528](https://github.com/crystal-lang/crystal/pull/7528), thanks @wontruefree)\n\n#### Numeric\n\n- **(breaking-change)** Add deprecation message to `Int#/`. It will return a `Float` in `0.29.0`. Use `Int#//` for integer division. ([#7639](https://github.com/crystal-lang/crystal/pull/7639), thanks @bcardiff)\n- Change `Number#inspect` to not show the type suffixes. ([#7525](https://github.com/crystal-lang/crystal/pull/7525), thanks @asterite)\n- Add `Int#leading_zeros_count` and `Int#trailing_zeros_count`. ([#7520](https://github.com/crystal-lang/crystal/pull/7520), thanks @Sija)\n- Add `Big*#//`, `BigInt#&`-ops and missing `#floor`, `#ceil`, `#trunc`. ([#7638](https://github.com/crystal-lang/crystal/pull/7638), thanks @bcardiff)\n- Improve `OverflowError` message. ([#7375](https://github.com/crystal-lang/crystal/pull/7375), thanks @r00ster91)\n\n#### Text\n\n- **(performance)** Optimize `String#compare` in case of ASCII only. ([#7352](https://github.com/crystal-lang/crystal/pull/7352), thanks @r00ster91)\n- Add methods for human-readable formatting of numbers: `Number#format`, `Number#humanize`, and `Int#humanize_bytes`. ([#6314](https://github.com/crystal-lang/crystal/pull/6314), thanks @straight-shoota)\n- Add `String#rchop?` and `String#lchop?`. ([#7328](https://github.com/crystal-lang/crystal/pull/7328), thanks @j8r)\n- Add `options` argument to `String#camelcase` and `String#underscore`. ([#7374](https://github.com/crystal-lang/crystal/pull/7374), thanks @r00ster91)\n- Add docs to `Unicode::CaseOptions`. ([#7513](https://github.com/crystal-lang/crystal/pull/7513), thanks @r00ster91)\n- Improve specs and docs for `String#each_line` and `IO#each_line`. ([#7419](https://github.com/crystal-lang/crystal/pull/7419), thanks @straight-shoota)\n\n#### Collections\n\n- **(breaking-change)** Let `Array#sort` only use `<=>`, and let `<=>` return `nil` for partial comparability. ([#6611](https://github.com/crystal-lang/crystal/pull/6611), thanks @asterite)\n- **(breaking-change)** Drop `Iterator#rewind`. Implement `#cycle` by storing elements in an array. ([#7440](https://github.com/crystal-lang/crystal/pull/7440), thanks @asterite)\n- **(performance)** Add `Enumerable#each_cons` support for `Deque` as a reuse buffer. ([#7233](https://github.com/crystal-lang/crystal/pull/7233), thanks @yxhuvud)\n- **(performance)** Change `Range#bsearch` `/ 2` to `>> 1` for faster performance. ([#7531](https://github.com/crystal-lang/crystal/pull/7531), thanks @Fryguy)\n- Fix `Slice#clone` for non-primitive types and deep copy. ([#7591](https://github.com/crystal-lang/crystal/pull/7591), thanks @straight-shoota)\n- Move `Indexable#zip` and `Indexable#zip?` to `Enumerable` and make it work with any number of `Indexable` or `Iterable` or `Iterator`. ([#7453](https://github.com/crystal-lang/crystal/pull/7453), thanks @asterite)\n- Add `Slice#[](Range)`. ([#7439](https://github.com/crystal-lang/crystal/pull/7439), thanks @asterite)\n- Add nillable range fetching `#[]?(Range)` to `Array` and `String`. ([#7338](https://github.com/crystal-lang/crystal/pull/7338), thanks @j8r)\n- Add `Set#add?`. ([#7495](https://github.com/crystal-lang/crystal/pull/7495), thanks @Sija)\n- Improve documentation of `Hash` regarding ordering of items. ([#7594](https://github.com/crystal-lang/crystal/pull/7594), thanks @straight-shoota)\n\n#### Serialization\n\n- **(breaking-change)** Change return type of `YAML#libyaml_version` to `SemanticVersion`. ([#7555](https://github.com/crystal-lang/crystal/pull/7555), thanks @asterite)\n- Fix support for libxml2 2.9.9. ([#7477](https://github.com/crystal-lang/crystal/pull/7477), thanks @asterite)\n- Fix support for libyaml 0.2.2. ([#7555](https://github.com/crystal-lang/crystal/pull/7555), thanks @asterite)\n- Add `BigDecimal.from_yaml`. ([#7398](https://github.com/crystal-lang/crystal/pull/7398), thanks @Sija)\n\n#### Time\n\n- **(breaking-change)** Rename `Time` constructors. Deprecate `Time.now` to encourage usage  `Time.utc` or `Time.local` ([#5346](https://github.com/crystal-lang/crystal/pull/5346), [#7586](https://github.com/crystal-lang/crystal/pull/7586), thanks @straight-shoota)\n- **(breaking-change)** Rename `Time#add_span` to `Time#shift` for changing a time instance by calendar units and handle other units. ([#6598](https://github.com/crystal-lang/crystal/pull/6598), thanks @straight-shoota)\n- **(breaking-change)** Change `Time#date` to return a `Tuple` of `{year, month, day}`. Use `Time#at_beginning_of_day` if a `Time` instance is wanted. ([#5822](https://github.com/crystal-lang/crystal/pull/5822), thanks @straight-shoota)\n- Fix Windows monotonic time bug. ([#7377](https://github.com/crystal-lang/crystal/pull/7377), thanks @domgetter)\n- Refactor `Time` methods. ([#6581](https://github.com/crystal-lang/crystal/pull/6581), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Remove `IO#flush_on_newline` and only `sync` on `STDOUT`/`STDIN`/`STDERR` when they are TTY devices. ([#7470](https://github.com/crystal-lang/crystal/pull/7470), thanks @asterite)\n- Add `Path` type. ([#5635](https://github.com/crystal-lang/crystal/pull/5635), thanks @straight-shoota)\n\n#### Networking\n\n- **(breaking-change)** Move `HTTP::Multipart` to `MIME::Multipart`. ([#7085](https://github.com/crystal-lang/crystal/pull/7085), thanks @m1lt0n)\n- **(breaking-change)** Stop parsing JSON in OAuth2 errors. ([#7467](https://github.com/crystal-lang/crystal/pull/7467), thanks @asterite)\n- **(breaking-change)** Fix `RequestProcessor` connection reuse logic. ([#7055](https://github.com/crystal-lang/crystal/pull/7055), thanks @straight-shoota)\n- **(breaking-change)** Replace `HTTP.default_status_message_for(Int)` with `HTTP::Status.new(Int).description`. ([#7247](https://github.com/crystal-lang/crystal/pull/7247), thanks @dwightwatson)\n- **(breaking-change)** Fix issues in `URI` implementation. `URI#opaque` method is merged into `URI#path`, which no longer returns `Nil`. `#parse`/`#to_s` normalization and default port handling has changed. ([#6323](https://github.com/crystal-lang/crystal/pull/6323), thanks @straight-shoota)\n- Fix write buffering in OpenSSL sockets. ([#7460](https://github.com/crystal-lang/crystal/pull/7460), thanks @carlhoerberg)\n- Fix leaks in `HTTP::Server` `#bind_*` and specs. ([#7197](https://github.com/crystal-lang/crystal/pull/7197), thanks @straight-shoota)\n- Add `HTTP::Request#remote_address`. ([#7610](https://github.com/crystal-lang/crystal/pull/7610), thanks @asterite)\n- Add `HTTP::Status` and `Response#status`. ([#7247](https://github.com/crystal-lang/crystal/pull/7247), [#7682](https://github.com/crystal-lang/crystal/pull/7682), thanks @dwightwatson, and @bcardiff)\n- Add support for OAuth 2.0 resource owner password credentials grant type. ([#7424](https://github.com/crystal-lang/crystal/pull/7424), thanks @Blacksmoke16)\n- Add support for IIS date format in cookies. ([#7405](https://github.com/crystal-lang/crystal/pull/7405), thanks @Sija)\n- Allow calls to `IO::Syscall#wait_readable` and `IO::Syscall#wait_writable`. ([#7366](https://github.com/crystal-lang/crystal/pull/7366), thanks @stakach)\n\n- Fix spec of `HTTP::Client` to not write server response after timeout. ([#7402](https://github.com/crystal-lang/crystal/pull/7402), thanks @asterite)\n- Fix spec of `TCP::Server` for musl. ([#7484](https://github.com/crystal-lang/crystal/pull/7484), thanks @straight-shoota)\n\n#### Crypto\n\n- **(breaking-change)** Use `OpenSSL::Algorithm` instead of symbols for `digest`/`hexdigest`. Expose LibCrypt's `PKCS5_PBKDF2_HMAC`. ([#7264](https://github.com/crystal-lang/crystal/pull/7264), thanks @mniak)\n\n#### Concurrency\n\n- Add multi-threading ready GC when compiling with `-D preview_mt`. ([#7546](https://github.com/crystal-lang/crystal/pull/7546), thanks @bcardiff, @waj, and @ysbaddaden)\n- Ship patched bdw-gc for multi-threading support. ([#7622](https://github.com/crystal-lang/crystal/pull/7622), thanks @bcardiff, and @ysbaddaden)\n- Refactor to extract `Fiber::StackPool` from `Fiber`. ([#7417](https://github.com/crystal-lang/crystal/pull/7417), thanks @ysbaddaden)\n- Refactor `IO::Syscall` as `IO::Evented`. ([#7505](https://github.com/crystal-lang/crystal/pull/7505), thanks @ysbaddaden)\n\n#### System\n\n- Add command and args to `execvp` error message. ([#7511](https://github.com/crystal-lang/crystal/pull/7511), thanks @straight-shoota)\n- Refactor signals handling in a separate fiber. ([#7469](https://github.com/crystal-lang/crystal/pull/7469), thanks @asterite)\n\n#### Spec\n\n- Improve how running specs are cancelled upon `CTRL+C`. ([#7426](https://github.com/crystal-lang/crystal/pull/7426), thanks @asterite)\n- Allow `pending` and `it` to accept constants. ([#7646](https://github.com/crystal-lang/crystal/pull/7646), thanks @straight-shoota)\n\n### Compiler\n\n- **(performance)** Avoid fork and spawn when `--threads=1`. ([#7397](https://github.com/crystal-lang/crystal/pull/7397), thanks @asterite)\n- Fix exception type thrown on missing require. ([#7386](https://github.com/crystal-lang/crystal/pull/7386), thanks @asterite)\n- Fix ICE when assigning a constant inside a multi-assign. ([#7468](https://github.com/crystal-lang/crystal/pull/7468), thanks @asterite)\n- Fix parsing and behaviour of `->foo.[]` and other operators . ([#7334](https://github.com/crystal-lang/crystal/pull/7334), thanks @asterite)\n- Fix parsing bug in `asm` with 3 colons and a variable. ([#7627](https://github.com/crystal-lang/crystal/pull/7627), thanks @r00ster91)\n- Opt-in detection of calls to `@[Deprecated]` methods. ([#7596](https://github.com/crystal-lang/crystal/pull/7596), [#7626](https://github.com/crystal-lang/crystal/pull/7626), [#7661](https://github.com/crystal-lang/crystal/pull/7661), thanks @bcardiff)\n- Add `CRYSTAL_LIBRARY_PATH` for lookup static libraries. ([#7562](https://github.com/crystal-lang/crystal/pull/7562), thanks @bcardiff)\n- Improve error messages by adding the scope (and `with ... yield` scope, if any) on undefined method error. ([#7384](https://github.com/crystal-lang/crystal/pull/7384), thanks @asterite)\n- Suggest `next` when trying to break from captured block . ([#7406](https://github.com/crystal-lang/crystal/pull/7406), thanks @r00ster91)\n- Add detection of linux environment in compiler config. ([#7479](https://github.com/crystal-lang/crystal/pull/7479), thanks @straight-shoota)\n- Pending leftovers to support `//` and `&`-ops in multiple places. ([#7628](https://github.com/crystal-lang/crystal/pull/7628), thanks @bcardiff)\n- Refactor `Crystal::Config.version` to use `read_file` macro. ([#7081](https://github.com/crystal-lang/crystal/pull/7081), thanks @Sija)\n- Rewrite macro spec without executing a shell command. ([#6962](https://github.com/crystal-lang/crystal/pull/6962), thanks @asterite)\n- Fix typo in internals. ([#7592](https://github.com/crystal-lang/crystal/pull/7592), thanks @toshokan)\n\n#### Language semantics\n\n- Fix issues with `as`, `as?` and unions and empty types. ([#7475](https://github.com/crystal-lang/crystal/pull/7475), thanks @asterite)\n- Fix method lookup when restrictions of instantiated and non-instantiated generic types are used. ([#7537](https://github.com/crystal-lang/crystal/pull/7537), thanks @bew)\n- Fix method lookup when free vars and explicit types are used. ([#7536](https://github.com/crystal-lang/crystal/pull/7536), [#7580](https://github.com/crystal-lang/crystal/pull/7580), thanks @bew)\n- When declaring a `protected initialize`, define a protected `new`. ([#7510](https://github.com/crystal-lang/crystal/pull/7510), thanks @asterite)\n- Fix named args type matching. ([#7529](https://github.com/crystal-lang/crystal/pull/7529), thanks @asterite)\n- Merge procs with the same arguments type and `Nil | T` return type to `Nil` return type. ([#7527](https://github.com/crystal-lang/crystal/pull/7527), thanks @asterite)\n- Fix passing recursive alias to proc. ([#7568](https://github.com/crystal-lang/crystal/pull/7568), thanks @asterite)\n\n### Tools\n\n- Suggest the user to run the formatter in `travis.yml`. ([#7138](https://github.com/crystal-lang/crystal/pull/7138), thanks @KCErb)\n\n#### Formatter\n\n- Fix formatting of `1\\n.as(Int32)`. ([#7347](https://github.com/crystal-lang/crystal/pull/7347), thanks @asterite)\n- Fix formatting of nested array elements. ([#7450](https://github.com/crystal-lang/crystal/pull/7450), thanks @MakeNowJust)\n- Fix formatting of comments and enums. ([#7605](https://github.com/crystal-lang/crystal/pull/7605), thanks @asterite)\n- Fix CLI handling of absolute paths input. ([#7560](https://github.com/crystal-lang/crystal/pull/7560), thanks @RX14)\n\n#### Doc generator\n\n- Don't include private constants. ([#7575](https://github.com/crystal-lang/crystal/pull/7575), thanks @r00ster91)\n- Include Crystal built-in constants. ([#7623](https://github.com/crystal-lang/crystal/pull/7623), thanks @bcardiff)\n- Add compile-time flag to docs generator. ([#6668](https://github.com/crystal-lang/crystal/pull/6668), [#7438](https://github.com/crystal-lang/crystal/pull/7438), thanks @straight-shoota)\n- Display deprecated label when `@[Deprecated]` is used. ([#7653](https://github.com/crystal-lang/crystal/pull/7653), thanks @bcardiff)\n\n#### Playground\n\n- Change the font-weight used for better readability. ([#7552](https://github.com/crystal-lang/crystal/pull/7552), thanks @Maroo-b)\n\n### Others\n\n- CI improvements and housekeeping. ([#7359](https://github.com/crystal-lang/crystal/pull/7359), [#7381](https://github.com/crystal-lang/crystal/pull/7381), [#7388](https://github.com/crystal-lang/crystal/pull/7388), [#7387](https://github.com/crystal-lang/crystal/pull/7387), [#7390](https://github.com/crystal-lang/crystal/pull/7390), [#7622](https://github.com/crystal-lang/crystal/pull/7622), thanks @bcardiff)\n- Smoke test linux 64 bits package using docker image recent build. ([#7389](https://github.com/crystal-lang/crystal/pull/7389), thanks @bcardiff)\n- Mention git pre-commit hook in `CONTRIBUTING.md`. ([#7617](https://github.com/crystal-lang/crystal/pull/7617), thanks @straight-shoota)\n- Fix misspellings throughout the codebase. ([#7361](https://github.com/crystal-lang/crystal/pull/7361), thanks @Sija)\n- Use chars instead of strings throughout the codebase. ([#6237](https://github.com/crystal-lang/crystal/pull/6237), thanks @r00ster91)\n- Fix GC finalization warning in `Thread` specs. ([#7403](https://github.com/crystal-lang/crystal/pull/7403), thanks @asterite)\n- Remove generated docs from linux packages. ([#7519](https://github.com/crystal-lang/crystal/issues/7519), thanks @straight-shoota)\n\n## 0.27.2 (2019-02-05)\n\n### Standard library\n\n- Fixed integer overflow in main thread stack base detection. ([#7373](https://github.com/crystal-lang/crystal/pull/7373), thanks @ysbaddaden)\n\n#### Networking\n\n- Fixes TLS exception during shutdown. ([#7372](https://github.com/crystal-lang/crystal/pull/7372), thanks @bcardiff)\n- Fixed `HTTP::Client` support exception on missing Content-Type. ([#7371](https://github.com/crystal-lang/crystal/pull/7371), thanks @bew)\n\n## 0.27.1 (2019-01-30)\n\n### Language changes\n\n- Allow trailing commas inside tuple types. ([#7182](https://github.com/crystal-lang/crystal/pull/7182), thanks @asterite)\n\n### Standard library\n\n- **(performance)** Optimize generating `UUID` from `String`. ([#7030](https://github.com/crystal-lang/crystal/pull/7030), thanks @jgaskins)\n- **(performance)** Improve `SemanticVersion` operations. ([#7234](https://github.com/crystal-lang/crystal/pull/7234), thanks @j8r)\n- Fixed markdown inline code parsing. ([#7090](https://github.com/crystal-lang/crystal/pull/7090), thanks @MakeNowJust)\n- Fixed inappropriate uses of `Time.now`. ([#7155](https://github.com/crystal-lang/crystal/pull/7155), thanks @straight-shoota)\n- Make `Nil#not_nil!` raise `NilAssertionError`. ([#7330](https://github.com/crystal-lang/crystal/pull/7330), thanks @r00ster91)\n- Add SemanticVersion to API docs. ([#7003](https://github.com/crystal-lang/crystal/pull/7003), thanks @Blacksmoke16)\n- Add docs to discourage the use of `Bool#to_unsafe` other than for C bindings. ([#7320](https://github.com/crystal-lang/crystal/pull/7320), thanks @oprypin)\n- Refactor `#to_s` to be independent of the `name` method. ([#7295](https://github.com/crystal-lang/crystal/pull/7295), thanks @asterite)\n\n#### Macros\n\n- Fixed docs of `ArrayLiteral#unshift`. ([#7127](https://github.com/crystal-lang/crystal/pull/7127), thanks @Blacksmoke16)\n- Fixed `Annotation#[]` to accept `String` and `Symbol` as keys. ([#7153](https://github.com/crystal-lang/crystal/pull/7153), thanks @MakeNowJust)\n- Fixed `NamedTupleLiteral#[]` to raise a compile error for invalid key type. ([#7158](https://github.com/crystal-lang/crystal/pull/7158), thanks @MakeNowJust)\n- Fixed `getter`/`property` macros to work properly with `Bool` types. ([#7313](https://github.com/crystal-lang/crystal/pull/7313), thanks @Sija)\n- Add `read_file` macro method. ([#6967](https://github.com/crystal-lang/crystal/pull/6967), [#7094](https://github.com/crystal-lang/crystal/pull/7094), thanks @Sija, @woodruffw)\n- Add `StringLiteral#count`. ([#7239](https://github.com/crystal-lang/crystal/pull/7239), thanks @Blacksmoke16)\n\n#### Numeric\n\n- Fixed scale issues when dividing `BigDecimal`. ([#7218](https://github.com/crystal-lang/crystal/pull/7218), thanks @Sija)\n- Allow underscores in the `String` passed to `Big*` constructors. ([#7107](https://github.com/crystal-lang/crystal/pull/7107), thanks @Sija)\n- Add conversion methods and docs to `Complex`. ([#5440](https://github.com/crystal-lang/crystal/pull/5440), thanks @Sija)\n- Add specs for `Int128`, `UInt128`. ([#7173](https://github.com/crystal-lang/crystal/pull/7173), thanks @bcardiff)\n- Add unsafe number ops `value.to_X!`/`T.new!`/`Int#&**`. ([#7226](https://github.com/crystal-lang/crystal/pull/7226), thanks @bcardiff)\n- Add overflow detection with preview opt-in. ([#7206](https://github.com/crystal-lang/crystal/pull/7206), thanks @bcardiff)\n\n#### Text\n\n- Fixed `ECR` location error reported. ([#7137](https://github.com/crystal-lang/crystal/pull/7137), thanks @MakeNowJust)\n- Add docs to ECR. ([#7121](https://github.com/crystal-lang/crystal/pull/7121), thanks @KCErb)\n- Refactor `String#to_i` to avoid future overflow. ([#7172](https://github.com/crystal-lang/crystal/pull/7172), thanks @bcardiff)\n\n#### Collections\n\n- Fixed docs example in `Hash#from`. ([#7210](https://github.com/crystal-lang/crystal/pull/7210), thanks @r00ster91)\n- Fixed docs links of `Enumerable#chunks` and `Iterator#chunk`. ([#6941](https://github.com/crystal-lang/crystal/pull/6941), thanks @r00ster91)\n- Remove implicit null skip from `Hash` to `JSON` serialization. ([#7053](https://github.com/crystal-lang/crystal/pull/7053), thanks @MakeNowJust)\n- Add `Iterator#slice_after`. ([#7146](https://github.com/crystal-lang/crystal/pull/7146), thanks @asterite)\n- Add `Iterator#slice_before`. ([#7152](https://github.com/crystal-lang/crystal/pull/7152), thanks @asterite)\n- Add `Iteratory#slice_when` and `Iterator#chunk_while`. ([#7159](https://github.com/crystal-lang/crystal/pull/7159), thanks @asterite)\n- Add `Enumerable#to_h(&block)`. ([#7150](https://github.com/crystal-lang/crystal/pull/7150), thanks @Sija)\n- Add `Enumerable#one?`. ([#7166](https://github.com/crystal-lang/crystal/pull/7166), thanks @asterite)\n- Add several `Enumerable`, `Iterator` and `Array` overloads that accept a pattern. ([#7174](https://github.com/crystal-lang/crystal/pull/7174), thanks @asterite)\n- Add docs to hash constructors. ([#6923](https://github.com/crystal-lang/crystal/pull/6923), thanks @KCErb)\n\n#### Serialization\n\n- Add conversion between JSON and YAML. ([#7232](https://github.com/crystal-lang/crystal/pull/7232), thanks @straight-shoota)\n- Standardize `#as_T`/`#as_T?` methods between `JSON::Any`/`YAML::Any`. ([#6556](https://github.com/crystal-lang/crystal/pull/6556), thanks @j8r)\n- Add `Set#from_yaml`. ([#6310](https://github.com/crystal-lang/crystal/pull/6310), thanks @kostya)\n\n#### Time\n\n- Fixed `Time::Span` initializer and `sleep` for big seconds. ([#7221](https://github.com/crystal-lang/crystal/pull/7221), thanks @asterite)\n- Fixed docs to show proper use of parse. ([#7035](https://github.com/crystal-lang/crystal/pull/7035), thanks @jwoertink)\n- Add missing `Float#weeks` method similar to `Int#weeks`. ([#7165](https://github.com/crystal-lang/crystal/pull/7165), thanks @vlazar)\n\n#### Files\n\n- Fix `mkstemps` support on aarch64. ([#7300](https://github.com/crystal-lang/crystal/pull/7300), thanks @silmanduin66)\n- Validate LibC error codes in specs involving Errno errors. ([#7087](https://github.com/crystal-lang/crystal/pull/7087), thanks @straight-shoota)\n- Add microsecond precision to `System::File.utime` (Unix). ([#7156](https://github.com/crystal-lang/crystal/pull/7156), thanks @straight-shoota)\n- Add missing tempfile cleanup in specs. ([#7250](https://github.com/crystal-lang/crystal/pull/7250), thanks @bcardiff)\n- Add docs for file open modes. ([#6664](https://github.com/crystal-lang/crystal/pull/6664), thanks @r00ster91)\n\n#### Networking\n\n- Fixed `HTTP::Client` edge case of exception during in TLS initialization. ([#7123](https://github.com/crystal-lang/crystal/pull/7123), thanks @asterite)\n- Fixed `OpenSSL::SSL::Error.new` to not raise `Errno`. ([#7068](https://github.com/crystal-lang/crystal/pull/7068), thanks @straight-shoota)\n- Fixed `URI` encoding in `StaticFileHandler::DirectoryListing`. ([#7072](https://github.com/crystal-lang/crystal/pull/7072), thanks @Sija)\n- Add MIME registry. ([#5765](https://github.com/crystal-lang/crystal/pull/5765), [#7079](https://github.com/crystal-lang/crystal/pull/7079), [#7080](https://github.com/crystal-lang/crystal/pull/7080), thanks @straight-shoota, @Sija)\n- Add `MIME::MediaType` for parsing mime media types. ([#7077](https://github.com/crystal-lang/crystal/pull/7077), thanks @straight-shoota)\n- Add support for 100-continue in `HTTP::Server::Response`. ([#6912](https://github.com/crystal-lang/crystal/pull/6912), thanks @jreinert)\n- Add support for creating sockets from raw file descriptors. ([#6894](https://github.com/crystal-lang/crystal/pull/6894), thanks @myfreeweb)\n- Add SNI support for OpenSSL. ([#7291](https://github.com/crystal-lang/crystal/pull/7291), thanks @bararchy)\n- Improve `HTTP::Server` docs. ([#7251](https://github.com/crystal-lang/crystal/pull/7251), thanks @straight-shoota)\n- Refactor `OpenSSL` specs to reduce chances of failing. ([#7202](https://github.com/crystal-lang/crystal/pull/7202), thanks @bcardiff)\n\n#### Crypto\n\n- Add `OpenSSL::Cipher#authenticated?` to see if the cipher supports aead. ([#7223](https://github.com/crystal-lang/crystal/pull/7223), thanks @danielwestendorf)\n\n#### System\n\n- Fixed inline ASM when compiling for ARM. ([#7041](https://github.com/crystal-lang/crystal/pull/7041), thanks @omarroth)\n- Implement `Crystal::System` for Win32. ([#6972](https://github.com/crystal-lang/crystal/pull/6972), thanks @markrjr)\n- Add `Errno#errno_message` getter. ([#6702](https://github.com/crystal-lang/crystal/pull/6702), thanks @r00ster91)\n\n#### Spec\n\n- Detect nesting `it` and `pending` at run-time. ([#7297](https://github.com/crystal-lang/crystal/pull/7297), thanks @MakeNowJust)\n\n### Compiler\n\n- Fixed how `LLVM::Type.const_int` emit `Int128` literals. ([#7135](https://github.com/crystal-lang/crystal/pull/7135), thanks @bcardiff)\n- Fixed ICE related to named tuples. ([#7163](https://github.com/crystal-lang/crystal/pull/7163), thanks @asterite)\n- Fixed automatic casting for private top-level methods. ([#7310](https://github.com/crystal-lang/crystal/pull/7310), thanks @asterite)\n- Give proper error if defining initialize inside enum, allow `Enum.new`. ([#7266](https://github.com/crystal-lang/crystal/pull/7266), thanks @asterite)\n- Give proper error when trying to access instance variable of union type. ([#7194](https://github.com/crystal-lang/crystal/pull/7194), thanks @asterite)\n- Give proper error when trying to instantiate Module. ([#6735](https://github.com/crystal-lang/crystal/pull/6735), thanks @r00ster91)\n- Give proper error related to named arguments. ([#7288](https://github.com/crystal-lang/crystal/pull/7288), thanks @asterite)\n- Parse required comma between block args. ([#7343](https://github.com/crystal-lang/crystal/pull/7343), thanks @asterite)\n- Improve inference in recursion that involves blocks. ([#7161](https://github.com/crystal-lang/crystal/pull/7161), thanks @asterite)\n- Add locations to all expanded macro arguments. ([#7008](https://github.com/crystal-lang/crystal/pull/7008), thanks @MakeNowJust)\n- Turn a not compiler specific error while requiring into ICE. ([#7208](https://github.com/crystal-lang/crystal/pull/7208), thanks @MakeNowJust)\n- Remove old `nil?` error on pointer types. ([#7180](https://github.com/crystal-lang/crystal/pull/7180), thanks @asterite)\n- Improve too big tuple and named tuple error message. ([#7131](https://github.com/crystal-lang/crystal/pull/7131), thanks @r00ster91)\n- Workaround buggy offset debug info values. ([#7335](https://github.com/crystal-lang/crystal/pull/7335), thanks @bcardiff)\n- Refactor extract helper methods to emit `Float32`, `Float64` values. ([#7134](https://github.com/crystal-lang/crystal/pull/7134), thanks @bcardiff)\n- Refactor filename resolution logic out of `interpret_run`. ([#7051](https://github.com/crystal-lang/crystal/pull/7051), thanks @Sija)\n- Refactor internals regarding overflow. ([#7262](https://github.com/crystal-lang/crystal/pull/7262), thanks @bcardiff)\n- Refactor `Crystal::Codegen::Target` and consolidate triple handling. ([#7282](https://github.com/crystal-lang/crystal/pull/7282), [#7317](https://github.com/crystal-lang/crystal/pull/7317), thanks @RX14, @bcardiff)\n\n### Tools\n\n- Update README template. ([#7118](https://github.com/crystal-lang/crystal/pull/7118), thanks @mamantoha)\n- Capitalise Crystal in CLI output. ([#7224](https://github.com/crystal-lang/crystal/pull/7224), thanks @dwightwatson)\n\n#### Formatter\n\n- Fixed formatting of multiline literal elements. ([#7048](https://github.com/crystal-lang/crystal/pull/7048), thanks @MakeNowJust)\n- Fixed formatting of heredoc with interpolations. ([#7184](https://github.com/crystal-lang/crystal/pull/7184), thanks @MakeNowJust)\n- Fixed prevent conflict between nested tuple types and macro expressions. ([#7097](https://github.com/crystal-lang/crystal/pull/7097), thanks @MakeNowJust)\n- Fixed format when `typeof` appears inside generic type. ([#7176](https://github.com/crystal-lang/crystal/pull/7176), thanks @asterite)\n- Fixed format of newline after `&.foo` in call. ([#7240](https://github.com/crystal-lang/crystal/pull/7240), thanks @MakeNowJust)\n- Honor same behaviour for single or multiple file arguments. ([#7144](https://github.com/crystal-lang/crystal/pull/7144), thanks @straight-shoota)\n- Refactor remove quotes from overflow symbols in formatter spec. ([#6968](https://github.com/crystal-lang/crystal/pull/6968), thanks @r00ster91)\n- Major rework of `crystal tool format` command. ([#7257](https://github.com/crystal-lang/crystal/pull/7257), thanks @MakeNowJust)\n\n#### Doc generator\n\n- **(security)** Prevent XSS via args. ([#7056](https://github.com/crystal-lang/crystal/pull/7056), thanks @MakeNowJust)\n- Fixed generation of toplevel. ([#7063](https://github.com/crystal-lang/crystal/pull/7063), thanks @MakeNowJust)\n- Fixed display of double splat and block arg. ([#7029](https://github.com/crystal-lang/crystal/pull/7029), [#7031](https://github.com/crystal-lang/crystal/pull/7031), thanks @MakeNowJust)\n- Fixed keep trailing spaces in macros. ([#7099](https://github.com/crystal-lang/crystal/pull/7099), thanks @MakeNowJust)\n- Fixed avoid showing subtypes of aliased type. ([#7124](https://github.com/crystal-lang/crystal/pull/7124), thanks @asterite)\n- Fixed style of methods when hovering. ([#7022](https://github.com/crystal-lang/crystal/pull/7022), thanks @r00ster91)\n- Fixed duplicate `source_link` field. ([#7033](https://github.com/crystal-lang/crystal/pull/7033), thanks @bcardiff)\n- Fixed missing keywords in `Doc::Highlighter`. ([#7054](https://github.com/crystal-lang/crystal/pull/7054), thanks @MakeNowJust)\n- Add `--format` option to docs command. ([#6982](https://github.com/crystal-lang/crystal/pull/6982), thanks @mniak)\n\n### Others\n\n- CI improvements and housekeeping. ([#7018](https://github.com/crystal-lang/crystal/pull/7018), [#7043](https://github.com/crystal-lang/crystal/pull/7043), [#7133](https://github.com/crystal-lang/crystal/pull/7133), [#7139](https://github.com/crystal-lang/crystal/pull/7139), [#7230](https://github.com/crystal-lang/crystal/pull/7230), [#7227](https://github.com/crystal-lang/crystal/pull/7227), [#7263](https://github.com/crystal-lang/crystal/pull/7263), thanks @bcardiff)\n- CI split formatting check. ([#7228](https://github.com/crystal-lang/crystal/pull/7228), thanks @bcardiff)\n- Depend on standard variable to let the user define the build date. ([#7186](https://github.com/crystal-lang/crystal/pull/7186), thanks @eli-schwartz)\n- Reorganize community section in README, add forum. ([#7235](https://github.com/crystal-lang/crystal/pull/7235), thanks @straight-shoota)\n- Fixed docs grammar and typos. ([#7034](https://github.com/crystal-lang/crystal/pull/7034), [#7242](https://github.com/crystal-lang/crystal/pull/7242), [#7331](https://github.com/crystal-lang/crystal/pull/7331), thanks @r00ster91, @girng)\n- Improve samples. ([#6454](https://github.com/crystal-lang/crystal/pull/6454), thanks @r00ster91)\n- Fixed 0.27.0 CHANGELOG. ([#7024](https://github.com/crystal-lang/crystal/pull/7024), thanks @arcage)\n- Update ISSUE_TEMPLATE to include forum. ([#7301](https://github.com/crystal-lang/crystal/pull/7301), thanks @straight-shoota)\n- Update LICENSE's copyright year. ([#7246](https://github.com/crystal-lang/crystal/pull/7246), thanks @matiasgarciaisaia)\n\n## 0.27.0 (2018-11-01)\n\n### Language changes\n\n- **(breaking-change)** Disallow comma after newline in argument list. ([#6514](https://github.com/crystal-lang/crystal/pull/6514), thanks @asterite)\n\n#### Macros\n\n- Add `Generic#resolve` and `Generic#resolve?` macro methods. ([#6617](https://github.com/crystal-lang/crystal/pull/6617), thanks @asterite)\n\n### Standard library\n\n- Fixed `v1`, `v2`, `v3`, `v4`, `v5` methods of `UUID`. ([#6952](https://github.com/crystal-lang/crystal/pull/6952), thanks @r00ster91)\n- Fixed multiple docs typos and phrasing in multiple places. ([#6778](https://github.com/crystal-lang/crystal/pull/6778), [#6963](https://github.com/crystal-lang/crystal/pull/6963), thanks @r00ster91)\n- Fixes `Pointer`/`UInt` subtraction. ([#6994](https://github.com/crystal-lang/crystal/pull/6994), thanks @damaxwell)\n- Add stack overflow detection. ([#6928](https://github.com/crystal-lang/crystal/pull/6928), [#6995](https://github.com/crystal-lang/crystal/pull/6995), thanks @damaxwell)\n- Add caller file and line to `Nil#not_nil!`. ([#6712](https://github.com/crystal-lang/crystal/pull/6712), thanks @yeeunmariakim)\n- Restrict `Enum#parse`/`Enum#parse?` to `String` arguments. ([#6654](https://github.com/crystal-lang/crystal/pull/6654), thanks @vladfaust)\n- Refactor and unify printing exceptions from within fibers. ([#6594](https://github.com/crystal-lang/crystal/pull/6594), thanks @Sija)\n- Improve docs on properties generated by `property?`. ([#6682](https://github.com/crystal-lang/crystal/pull/6682), thanks @epergo)\n- Add docs to top level namespace constants. ([#6971](https://github.com/crystal-lang/crystal/pull/6971), thanks @r00ster91)\n\n#### Macros\n\n- Fix typos in `StringLiteral#gsub` and `#tr` errors. ([#6925](https://github.com/crystal-lang/crystal/pull/6925), thanks @r00ster91)\n\n#### Numeric\n\n- **(breaking-change)** Disallow `rand` with zero value. ([#6686](https://github.com/crystal-lang/crystal/pull/6686), thanks @oprypin)\n- **(breaking-change)** Let `==` and `!=` compare the values instead of bits when dealing with signed vs unsigned integers. ([#6689](https://github.com/crystal-lang/crystal/pull/6689), thanks @asterite)\n- Fixed `Int#downto` with unsigned int. ([#6678](https://github.com/crystal-lang/crystal/pull/6678), thanks @gmarcais)\n- Add wrapping arithmetics operators `&+` `&-` `&*`. ([#6890](https://github.com/crystal-lang/crystal/pull/6890), thanks @bcardiff)\n- Add floor divisions operator `Int#//` and `Float#//`. ([#6891](https://github.com/crystal-lang/crystal/pull/6891), thanks @bcardiff)\n- Add random support for `BigInt`. ([#6687](https://github.com/crystal-lang/crystal/pull/6687), thanks @oprypin)\n- Add docs related to `Float::Printer::*`. ([#5438](https://github.com/crystal-lang/crystal/pull/5438), thanks @Sija)\n\n#### Text\n\n- Add `String::Builder#chomp!` returns self. ([#6583](https://github.com/crystal-lang/crystal/pull/6583), thanks @Sija)\n- Add `:default` to colorize and document `ColorRGB`, `Color256`. ([#6427](https://github.com/crystal-lang/crystal/pull/6427), thanks @r00ster91)\n- Add `String::Formatter` support for `c` flag and improve docs. ([#6758](https://github.com/crystal-lang/crystal/pull/6758), thanks @r00ster91)\n\n#### Collections\n\n- **(breaking-change)** Replace `Indexable#at` with `#fetch`. Remove `Hash#fetch(key)` as alias of `Hash#[]`. ([#6296](https://github.com/crystal-lang/crystal/pull/6296), thanks @AlexWayfer)\n- Add `Hash/Indexable#dig/dig?`. ([#6719](https://github.com/crystal-lang/crystal/pull/6719), thanks @Sija)\n- Add `Iterator.chain` to chain array of iterators. ([#6570](https://github.com/crystal-lang/crystal/pull/6570), thanks @xqyww123)\n- Add `NamedTuple#to_h` over empty tuples. ([#6628](https://github.com/crystal-lang/crystal/pull/6628), thanks @icyleaf)\n- Optimize `Indexable#join` when all elements are strings. ([#6635](https://github.com/crystal-lang/crystal/pull/6635), thanks @asterite)\n- Optimize `Array#skip`. ([#6946](https://github.com/crystal-lang/crystal/pull/6946), thanks @asterite)\n\n#### Serialization\n\n- Fixed `YAML::Schema::FailSafe.parse` and `parse_all`. ([#6790](https://github.com/crystal-lang/crystal/pull/6790), thanks @r00ster91)\n- Fixed order of `xmlns` and prefix in `XML::Builder#namespace`. ([#6743](https://github.com/crystal-lang/crystal/pull/6743), thanks @yeeunmariakim)\n- Fixed `CSV.build` quoting of `Char` and `Symbol`. ([#6904](https://github.com/crystal-lang/crystal/pull/6904), thanks @maiha)\n- Fixed docs for `JSON::Serializable`. ([#6950](https://github.com/crystal-lang/crystal/pull/6950), thanks @Heaven31415)\n- Add `XML::Attributes#delete`. ([#6910](https://github.com/crystal-lang/crystal/pull/6910), thanks @joenas)\n- Add ability to quote values always in `CSV.build`. ([#6723](https://github.com/crystal-lang/crystal/pull/6723), thanks @maiha)\n- Refactor how empty properties are handled in `JSON::Serializable` and `YAML::Serializable`. ([#6539](https://github.com/crystal-lang/crystal/pull/6539), thanks @r00ster91)\n\n#### Time\n\n- **(breaking-change)** Rename `Time#epoch` to `Time#to_unix`. Also `#epoch_ms` to `#to_unix_ms`, and `#epoch_f` to `#to_unix_f`. ([#6662](https://github.com/crystal-lang/crystal/pull/6662), thanks @straight-shoota)\n- Fixed spec for `Time::Location.load_local` with `TZ=nil`. ([#6740](https://github.com/crystal-lang/crystal/pull/6740), thanks @straight-shoota)\n- Add support for ISO calendar week to `Time`. ([#6681](https://github.com/crystal-lang/crystal/pull/6681), thanks @straight-shoota)\n- Add `Time::Format` support for `%G`, `%g`, `%V`. ([#6681](https://github.com/crystal-lang/crystal/pull/6681), thanks @straight-shoota)\n- Add `Time::Location` loader support for Windows. ([#6363](https://github.com/crystal-lang/crystal/pull/6363), thanks @straight-shoota)\n- Add `Time#to_local_in` to change time zone while keeping wall clock. ([#6572](https://github.com/crystal-lang/crystal/pull/6572), thanks @straight-shoota)\n- Add `Time::UNIX_EPOCH` and drop private `UNIX_SECONDS` constant. ([#6908](https://github.com/crystal-lang/crystal/pull/6908), thanks @j8r)\n- Change `Time::DayOfWeek` to ISO ordinal numbering based on `Monday = 1`. ([#6555](https://github.com/crystal-lang/crystal/pull/6555), thanks @straight-shoota)\n- Refactor time specs. ([#6574](https://github.com/crystal-lang/crystal/pull/6574), thanks @straight-shoota)\n- Add docs for singular method aliases, add `Int#microsecond` alias. ([#6297](https://github.com/crystal-lang/crystal/pull/6297), thanks @Sija)\n\n#### Files\n\n- **(breaking-change)** Remove `Tempfile`. Use `File.tempfile` or `File.tempname`. ([#6485](https://github.com/crystal-lang/crystal/pull/6485), thanks @straight-shoota)\n- Fixed missing closed status check of FDs when creating a subprocess. ([#6641](https://github.com/crystal-lang/crystal/pull/6641), thanks @Timbus)\n- Fixed `ChecksumReader.write` error message. ([#6889](https://github.com/crystal-lang/crystal/pull/6889), thanks @r00ster91)\n- Add `File#delete`, `Dir#tempdir` and improve `File` docs. ([#6485](https://github.com/crystal-lang/crystal/pull/6485), thanks @straight-shoota)\n- Add `File#fsync` to flush all data written into the file to the disk device. ([#6793](https://github.com/crystal-lang/crystal/pull/6793), thanks @carlhoerberg)\n- Add `DEVNULL` to docs. ([#6642](https://github.com/crystal-lang/crystal/pull/6642), thanks @r00ster91)\n- Improve checks for FreeBSD version due to breaking API changes. ([#6629](https://github.com/crystal-lang/crystal/pull/6629), thanks @myfreeweb)\n- Improve performance of `Zlib::Reader`, `Gzip::Reader` and `Flate::Reader` by including `IO::Buffered`. ([#6916](https://github.com/crystal-lang/crystal/pull/6916), thanks @asterite)\n- Refactor `Crystal::System::FileDescriptor` to use `@fd` ivar directly. ([#6703](https://github.com/crystal-lang/crystal/pull/6703), thanks @straight-shoota)\n- Refactor `{Zlib,Gzip,Flate}::Reader#unbuffered_rewind` to use `check_open`. ([#6958](https://github.com/crystal-lang/crystal/pull/6958), thanks @Sija)\n\n#### Networking\n\n- **(breaking-change)** Remove deprecated alias `HTTP::Server#bind_ssl`. Use `HTTP::Server#bind_tls`. ([#6699](https://github.com/crystal-lang/crystal/pull/6699), thanks @straight-shoota)\n- Add `Socket::Address#pretty_print` and `#inspect`. ([#6704](https://github.com/crystal-lang/crystal/pull/6704), thanks @straight-shoota)\n- Add `Socket::IPAddress` loopback, unspecified and broadcast methods/constants. ([#6710](https://github.com/crystal-lang/crystal/pull/6710), thanks @straight-shoota)\n- Fixed `Socket#reuse_port?` if `SO_REUSEPORT` is not supported. ([#6706](https://github.com/crystal-lang/crystal/pull/6706), thanks @straight-shoota)\n- Fixed `TCPServer` handling of `reuse_port`. ([#6940](https://github.com/crystal-lang/crystal/pull/6940), thanks @RX14)\n- Add docs to demonstrate parameters for `HTTP::Client`. ([#5145](https://github.com/crystal-lang/crystal/pull/5145), thanks @HCLarsen)\n- Add docs examples to `Socket::Server#accept`. ([#6705](https://github.com/crystal-lang/crystal/pull/6705), thanks @straight-shoota)\n- Refactor `socket_spec.cr` into separate files. ([#6700](https://github.com/crystal-lang/crystal/pull/6700), thanks @straight-shoota)\n- Refactor specs of `HTTP::Client` to remove inheritance for test server. ([#6909](https://github.com/crystal-lang/crystal/pull/6909), thanks @straight-shoota)\n- Improve specs for `HTTP::Server#close`. ([#5958](https://github.com/crystal-lang/crystal/pull/5958), thanks @straight-shoota)\n- Improve specs for socket. ([#6711](https://github.com/crystal-lang/crystal/pull/6711), thanks @straight-shoota)\n\n#### Crypto\n\n- Fixed OpenSSL bindings to work with LibreSSL. ([#6917](https://github.com/crystal-lang/crystal/pull/6917), thanks @LVMBDV)\n- Add support for OpenSSL 1.1.1. ([#6738](https://github.com/crystal-lang/crystal/pull/6738), thanks @ysbaddaden)\n\n#### Concurrency\n\n- Improve POSIX threads integration regarding locking, error and resource management. ([#6944](https://github.com/crystal-lang/crystal/pull/6944), thanks @ysbaddaden)\n- Remove unintended public methods from `Channel`. ([#6714](https://github.com/crystal-lang/crystal/pull/6714), thanks @asterite)\n- Refactor `Fiber`/`Scheduler` to isolate responsibilities. ([#6897](https://github.com/crystal-lang/crystal/pull/6897), thanks @ysbaddaden)\n- Refactor specs that relied on `Fiber.yield` behavior. ([#6953](https://github.com/crystal-lang/crystal/pull/6953), thanks @ysbaddaden)\n\n#### System\n\n- Fixed fork and signal child handlers. ([#6426](https://github.com/crystal-lang/crystal/pull/6426), thanks @ysbaddaden)\n- Use blocking `IO` on a TTY if it can't be reopened. ([#6660](https://github.com/crystal-lang/crystal/pull/6660), thanks @Timbus)\n- Refactor `Process` in preparation for Windows support. ([#6744](https://github.com/crystal-lang/crystal/pull/6744), thanks @RX14)\n\n#### Spec\n\n- Allow `pending` to be used without blocks. ([#6732](https://github.com/crystal-lang/crystal/pull/6732), thanks @tswicegood)\n- Add `be_empty` expectation. ([#6614](https://github.com/crystal-lang/crystal/pull/6614), thanks @mamantoha)\n- Add specs for expectation methods. ([#6512](https://github.com/crystal-lang/crystal/pull/6512), thanks @rodrigopinto)\n\n### Compiler\n\n- Fixed don't \"ambiguous match\" if there's an exact match. ([#6618](https://github.com/crystal-lang/crystal/pull/6618), thanks @asterite)\n- Fixed allow annotations inside enums. ([#6713](https://github.com/crystal-lang/crystal/pull/6713), thanks @asterite)\n- Fixed `super` inside macros will honor arguments. ([#6638](https://github.com/crystal-lang/crystal/pull/6638), thanks @asterite)\n- Fixed guessed ivar type from splat arguments. ([#6648](https://github.com/crystal-lang/crystal/pull/6648), thanks @MakeNowJust)\n- Fixed `ASTNode#to_s` of non-unary operator call without argument. ([#6538](https://github.com/crystal-lang/crystal/pull/6538), thanks @MakeNowJust)\n- Fixed `ASTNode#to_s` for multiline macro expression. ([#6666](https://github.com/crystal-lang/crystal/pull/6666), thanks @MakeNowJust)\n- Fixed `ASTNode#to_s` for `{% verbatim do %} ... {% end %}`. ([#6665](https://github.com/crystal-lang/crystal/pull/6665), thanks @MakeNowJust)\n- Fixed empty case statement normalization. ([#6915](https://github.com/crystal-lang/crystal/pull/6915), thanks @straight-shoota)\n- Fixed codegen of tuple elements with unreachable elements. ([#6659](https://github.com/crystal-lang/crystal/pull/6659), thanks @MakeNowJust)\n- Fixed parsing of `//` corner cases. ([#6927](https://github.com/crystal-lang/crystal/pull/6927), thanks @bcardiff)\n- Fixed recursive block expansion check for non `ProcNotation` restriction. ([#6932](https://github.com/crystal-lang/crystal/pull/6932), thanks @MakeNowJust)\n- Fixed corner case of expressions not typed on main phase but typed on cleanup phase. ([#6720](https://github.com/crystal-lang/crystal/pull/6720), thanks @MakeNowJust)\n- Improve error traces regarding `return`, `next` and `break`. ([#6633](https://github.com/crystal-lang/crystal/pull/6633), thanks @asterite)\n- Add resolve generics typenodes in macros. ([#6617](https://github.com/crystal-lang/crystal/pull/6617), thanks @asterite)\n- Add support for multiple output values in inline asm. ([#6680](https://github.com/crystal-lang/crystal/pull/6680), thanks @RX14)\n- Improve parsing of `asm` operands. ([#6688](https://github.com/crystal-lang/crystal/pull/6688), thanks @RX14)\n- Refactor rescue block codegen for Windows. ([#6649](https://github.com/crystal-lang/crystal/pull/6649), thanks @RX14)\n\n### Tools\n\n- Improve installation section in README template. ([#6914](https://github.com/crystal-lang/crystal/pull/6914), [#6942](https://github.com/crystal-lang/crystal/pull/6942), thanks @r00ster91)\n- Improve contributors section in README template. ([#7005](https://github.com/crystal-lang/crystal/pull/7005), thanks @r00ster91)\n\n#### Formatter\n\n- Fixed formatting of `{% verbatim do %} ... {% end %}` outside macro. ([#6667](https://github.com/crystal-lang/crystal/pull/6667), thanks @MakeNowJust)\n- Fixed formatting of `//` corner cases. ([#6927](https://github.com/crystal-lang/crystal/pull/6927), thanks @bcardiff)\n- Improve formatting of `asm` operands. ([#6688](https://github.com/crystal-lang/crystal/pull/6688), thanks @RX14)\n\n#### Doc generator\n\n- Add support for comments after `:nodoc:` marker. ([#6627](https://github.com/crystal-lang/crystal/pull/6627), thanks @Sija)\n- Fixed browser performance issue with blur filter. ([#6764](https://github.com/crystal-lang/crystal/pull/6764), thanks @girng)\n- Accessibility improvement in search field. ([#6926](https://github.com/crystal-lang/crystal/pull/6926), thanks @jodylecompte)\n\n### Others\n\n- CI improvements and housekeeping. ([#6658](https://github.com/crystal-lang/crystal/pull/6658), [#6739](https://github.com/crystal-lang/crystal/pull/6739), [#6930](https://github.com/crystal-lang/crystal/pull/6930), thanks @bcardiff, @RX14)\n- Add `VERSION` file and support for specifying the build commit. ([#6966](https://github.com/crystal-lang/crystal/pull/6966), thanks @bcardiff)\n- Add support for specifying the build date. ([#6788](https://github.com/crystal-lang/crystal/pull/6788), thanks @peterhoeg)\n- Update Contributing section in `README.md`. ([#6911](https://github.com/crystal-lang/crystal/pull/6911), thanks @r00ster91)\n\n## 0.26.1 (2018-08-27)\n\n### Language changes\n\n- **(breaking-change)** Make `self` to be eager evaluated when including modules. ([#6557](https://github.com/crystal-lang/crystal/pull/6557), thanks @bcardiff)\n\n#### Macros\n\n- Add `accepts_block?` macro method to `Def`. ([#6604](https://github.com/crystal-lang/crystal/pull/6604), thanks @willhbr)\n\n### Standard library\n\n#### Macros\n\n- Fixed `Object#def_hash` can receive symbols. ([#6531](https://github.com/crystal-lang/crystal/pull/6531), thanks @Sija)\n\n#### Collections\n\n- Add `Hash#transform_keys` and `Hash#transform_values`. ([#4385](https://github.com/crystal-lang/crystal/pull/4385), thanks @deepj)\n\n#### Serialization\n\n- Fixed `JSON::Serializable` and `YAML::Serializable` clashing with custom initializers. ([#6458](https://github.com/crystal-lang/crystal/pull/6458), thanks @kostya)\n\n#### Time\n\n- Fixed docs for `Time::Format`. ([#6578](https://github.com/crystal-lang/crystal/pull/6578), thanks @straight-shoota)\n\n#### Files\n\n- Fixed zlib handling of buffer error. ([#6610](https://github.com/crystal-lang/crystal/pull/6610), thanks @asterite)\n\n#### Networking\n\n- **(deprecate)** `HTTP::Server#bind_ssl` in favor of `HTTP::Server#bind_tls`. ([#6551](https://github.com/crystal-lang/crystal/pull/6551), thanks @bcardiff)\n- Add tls scheme to `HTTP::Server#bind`. ([#6533](https://github.com/crystal-lang/crystal/pull/6533), thanks @straight-shoota)\n- Fixed `HTTP::Server` crash with self-signed certificate. ([#6590](https://github.com/crystal-lang/crystal/pull/6590), thanks @bcardiff)\n- Refactor `HTTP::Server` specs to use free ports. ([#6530](https://github.com/crystal-lang/crystal/pull/6530), thanks @straight-shoota)\n\n#### System\n\n- Improve `STDIN`/`STDOUT`/`STDERR` handling to avoid breaking other programs. ([#6518](https://github.com/crystal-lang/crystal/pull/6518), thanks @Timbus)\n\n#### Spec\n\n- Fixed `DotFormatter` to flush after every spec. ([#6562](https://github.com/crystal-lang/crystal/pull/6562), thanks @asterite)\n- Add support for Windows. ([#6497](https://github.com/crystal-lang/crystal/pull/6497), thanks @RX14)\n\n### Compiler\n\n- Fixed evaluate yield expressions in macros. ([#6587](https://github.com/crystal-lang/crystal/pull/6587), thanks @asterite)\n- Fixed presence check of named argument via external name. ([#6560](https://github.com/crystal-lang/crystal/pull/6560), thanks @asterite)\n- Fixed parser error on `break when`. ([#6509](https://github.com/crystal-lang/crystal/pull/6509), thanks @asterite)\n- Fixed `~` methods are now able to be called as `foo.~`. ([#6541](https://github.com/crystal-lang/crystal/pull/6541), thanks @MakeNowJust)\n- Fixed parsing newline after macro control expression. ([#6607](https://github.com/crystal-lang/crystal/pull/6607), thanks @asterite)\n- Refactor use enum instead of hardcoded string values for emit kinds. ([#6515](https://github.com/crystal-lang/crystal/pull/6515), thanks @bew)\n\n### Tools\n\n#### Formatter\n\n- Fixed formatting of newline before `&.method` in call. ([#6535](https://github.com/crystal-lang/crystal/pull/6535), thanks @MakeNowJust)\n- Fixed formatting of empty heredoc. ([#6567](https://github.com/crystal-lang/crystal/pull/6567), thanks @MakeNowJust)\n- Fixed formatting of string literal in interpolation. ([#6568](https://github.com/crystal-lang/crystal/pull/6568), thanks @MakeNowJust)\n- Fixed formatting of comments in case when. ([#6595](https://github.com/crystal-lang/crystal/pull/6595), thanks @asterite)\n\n#### Doc generator\n\n- Add Menlo font family and fix ordering. ([#6602](https://github.com/crystal-lang/crystal/pull/6602), thanks @slice)\n\n#### Playground\n\n- Fixed internal link. ([#6596](https://github.com/crystal-lang/crystal/pull/6596), thanks @omarroth)\n\n### Others\n\n- CI improvements and housekeeping. ([#6550](https://github.com/crystal-lang/crystal/pull/6550), [#6612](https://github.com/crystal-lang/crystal/pull/6612), thanks @bcardiff)\n- Add `pkg-config` as Linux package dependency. ([distribution-scripts#16](https://github.com/crystal-lang/distribution-scripts/pull/16), thanks @bcardiff)\n\n## 0.26.0 (2018-08-09)\n\n### Language changes\n\n- **(breaking-change)** Revert do not collapse unions for sibling types. ([#6351](https://github.com/crystal-lang/crystal/pull/6351), thanks @asterite)\n- **(breaking-change)** Constant lookup context in macro is now lexical. ([#5354](https://github.com/crystal-lang/crystal/pull/5354), thanks @MakeNowJust)\n- **(breaking-change)** Evaluate instance var initializers at the metaclass level (ie: disallow using `self`). ([#6414](https://github.com/crystal-lang/crystal/pull/6414), thanks @asterite)\n- **(breaking-change)** Add `//` operator parsing. NB: No behaviour is assigned to this operator yet. ([#6470](https://github.com/crystal-lang/crystal/pull/6470), thanks @bcardiff)\n- Add `&+` `&-` `&*` `&**` operators parsing. NB: No behaviour is assigned to these operators yet. ([#6329](https://github.com/crystal-lang/crystal/pull/6329), thanks @bcardiff)\n- Add support for empty `case` without `when`. ([#6367](https://github.com/crystal-lang/crystal/pull/6367), thanks @straight-shoota)\n\n#### Macros\n\n- Add `pp!` and `p!` macro methods. ([#6374](https://github.com/crystal-lang/crystal/pull/6374), [#6476](https://github.com/crystal-lang/crystal/pull/6476), thanks @straight-shoota)\n\n### Standard library\n\n- Fix docs for `Pointer`. ([#6494](https://github.com/crystal-lang/crystal/pull/6494), thanks @fxn)\n- Fix docs of `UUID` enums. ([#6496](https://github.com/crystal-lang/crystal/pull/6496), thanks @r00ster91)\n\n#### Numeric\n\n- Fixed `Random#rand(Range(Float, Float))` to return `Float`. ([#6445](https://github.com/crystal-lang/crystal/pull/6445), thanks @straight-shoota)\n- Add docs of big module overloads. ([#6336](https://github.com/crystal-lang/crystal/pull/6336), thanks @laginha87)\n\n#### Text\n\n- **(breaking-change)** `String#from_utf16(pointer : Pointer(UInt16))` returns now `{String, Pointer(UInt16)}`. ([#6333](https://github.com/crystal-lang/crystal/pull/6333), thanks @straight-shoota)\n- Add support for unicode 11.0.0. ([#6505](https://github.com/crystal-lang/crystal/pull/6505), thanks @asterite)\n- Add an optional argument to `String#check_no_null_byte` to customize error message. ([#6333](https://github.com/crystal-lang/crystal/pull/6333), thanks @straight-shoota)\n- Add `ECR.render` for rendering directly as `String`. ([#6371](https://github.com/crystal-lang/crystal/pull/6371), thanks @straight-shoota)\n- Fix docs for `Char` ([#6487](https://github.com/crystal-lang/crystal/pull/6487), thanks @r00ster91)\n\n#### Collections\n\n- Add docs for `StaticArray`. ([#6404](https://github.com/crystal-lang/crystal/pull/6404), [#6488](https://github.com/crystal-lang/crystal/pull/6488), thanks @straight-shoota, @r00ster91, @hinrik)\n- Refactor `Array#concat`. ([#6493](https://github.com/crystal-lang/crystal/pull/6493), thanks @fxn)\n\n#### Serialization\n\n- **(breaking-change)** Add a maximum nesting level to prevent stack overflow on `YAML::Builder` and `JSON::Builder`. ([#6322](https://github.com/crystal-lang/crystal/pull/6322), thanks @asterite)\n- Fixed compatibility for libyaml 0.2.1 regarding document end marker `...`. ([#6287](https://github.com/crystal-lang/crystal/pull/6287), thanks @straight-shoota)\n- Add methods and options for pull parsing or hybrid parsing to `XML::Reader`. ([#5740](https://github.com/crystal-lang/crystal/pull/5740), [#6332](https://github.com/crystal-lang/crystal/pull/6332), thanks @felixbuenemann)\n- Fixed docs for `JSON::Any`, `JSON::Serialization` and `YAML::Serialization`. ([#6460](https://github.com/crystal-lang/crystal/pull/6460), [#6491](https://github.com/crystal-lang/crystal/pull/6491), thanks @delef, @bmulvihill)\n\n#### Time\n\n- **(breaking-change)** Make location a required argument for `Time.parse`. ([#6369](https://github.com/crystal-lang/crystal/pull/6369), thanks @straight-shoota)\n- Add `Time.parse!`, `Time.parse_utc`, `Time.parse_local`. ([#6369](https://github.com/crystal-lang/crystal/pull/6369), thanks @straight-shoota)\n- Fix docs comment missing ([#6387](https://github.com/crystal-lang/crystal/pull/6387), thanks @faustinoaq)\n\n#### Files\n\n- **(breaking-change)** Remove `File.each_line` method that returns an iterator. Use `IO#each_line`. ([#6301](https://github.com/crystal-lang/crystal/pull/6301), thanks @asterite)\n- Fixed `File.join` when path separator is a component argument. ([#6328](https://github.com/crystal-lang/crystal/pull/6328), thanks @icyleaf)\n- Fixed `Dir.glob` can now list broken symlinks. ([#6466](https://github.com/crystal-lang/crystal/pull/6466), thanks @straight-shoota)\n- Add `File` and `Dir` support for Windows. ([#5623](https://github.com/crystal-lang/crystal/pull/5623), thanks @RX14)\n\n#### Networking\n\n- **(breaking-change)** Drop `HTTP::Server#tls` in favor of `HTTP::Server#bind_ssl`. ([#5960](https://github.com/crystal-lang/crystal/pull/5960), thanks @straight-shoota)\n- **(breaking-change)** Rename alias `HTTP::Handler::Proc` to `HTTP::Handler::HandlerProc`. ([#6453](https://github.com/crystal-lang/crystal/pull/6453), thanks @jwoertink)\n- Fixed `Socket#accept?` base implementation. ([#6277](https://github.com/crystal-lang/crystal/pull/6277), thanks @ysbaddaden)\n- Fixed performance issue due to unbuffered `IO` read. `IO#sync` only affect writes, introduce `IO#read_buffering?`. ([#6304](https://github.com/crystal-lang/crystal/pull/6304), [#6474](https://github.com/crystal-lang/crystal/pull/6474), thanks @asterite, @bcardiff)\n- Fixed handling of closed state in `HTTP::Server::Response`. ([#6477](https://github.com/crystal-lang/crystal/pull/6477), thanks @straight-shoota)\n- Fixed change encoding name comparison to be case insensitive for UTF-8. ([#6355](https://github.com/crystal-lang/crystal/pull/6355), thanks @asterite)\n- Fixed support for quoted charset value in HTTP. ([#6354](https://github.com/crystal-lang/crystal/pull/6354), thanks @asterite)\n- Fixed docs regarding udp example on `Socket::Addrinfo`. ([#6388](https://github.com/crystal-lang/crystal/pull/6388), thanks @faustinoaq)\n- Fixed `HTTP::Client` will set `connection: close` header on one-shot requests. ([#6410](https://github.com/crystal-lang/crystal/pull/6410), thanks @asterite)\n- Fixed `OpenSSL::Digest` for multibyte strings. ([#6471](https://github.com/crystal-lang/crystal/pull/6471), thanks @RX14)\n- Fixed missing `Host` header when using `HTTP::Client#exec`. ([#6481](https://github.com/crystal-lang/crystal/pull/6481), thanks @straight-shoota)\n- Add `HTTP::Server#bind(URI|String)` that infers protocol from scheme. ([#6500](https://github.com/crystal-lang/crystal/pull/6500), thanks @straight-shoota)\n- Add `HTTP::Params.new` and `HTTP::Params#empty?`. ([#6241](https://github.com/crystal-lang/crystal/pull/6241), thanks @icyleaf)\n- Add support for multiple Etags in `If-None-Match` header for `HTTP::Request` and `HTTP::StaticFileHandler`. ([#6219](https://github.com/crystal-lang/crystal/pull/6219), thanks @straight-shoota)\n- Add IDNs normalization to punycode in `OpenSSL::SSL::Socket`. ([#6306](https://github.com/crystal-lang/crystal/pull/6306), thanks @paulkass)\n- Add `application/wasm` to the default MIME types of `HTTP::StaticFileHandler`. ([#6377](https://github.com/crystal-lang/crystal/pull/6377), thanks @MakeNowJust)\n- Add `URI#absolute?` and `URI#relative?`. ([#6311](https://github.com/crystal-lang/crystal/pull/6311), thanks @mamantoha)\n\n#### Crypto\n\n- Fixed `Crypto::Bcrypt::Password#==` was hiding `Reference#==(other)`. ([#6356](https://github.com/crystal-lang/crystal/pull/6356), thanks @straight-shoota)\n\n#### Concurrency\n\n- Fixed `Atomic#swap` with reference types. ([#6428](https://github.com/crystal-lang/crystal/pull/6428), thanks @Exilor)\n\n#### System\n\n- Fixed raise `Errno` if `Process.new` fails to exec. ([#6501](https://github.com/crystal-lang/crystal/pull/6501), thanks @straight-shoota, @lbguilherme)\n- Add support for `WinError` UTF-16 string messages. ([#6442](https://github.com/crystal-lang/crystal/pull/6442), thanks @straight-shoota)\n- Refactor platform specifics from `ENV` to `Crystal::System::Env` and implement for Windows. ([#6333](https://github.com/crystal-lang/crystal/pull/6333), [#6499](https://github.com/crystal-lang/crystal/pull/6499), thanks @straight-shoota)\n\n#### Spec\n\n- Add [TAP](https://testanything.org/) formatter to spec suite. ([#6286](https://github.com/crystal-lang/crystal/pull/6286), thanks @straight-shoota)\n\n### Compiler\n\n- Fixed named arguments expansion from double splat clash with local variable names. ([#6378](https://github.com/crystal-lang/crystal/pull/6378), thanks @asterite)\n- Fixed auto assigned ivars arguments expansions when clash with keywords. ([#6379](https://github.com/crystal-lang/crystal/pull/6379), thanks @asterite)\n- Fixed resulting type of union of tuple metaclasses. ([#6342](https://github.com/crystal-lang/crystal/pull/6342), thanks @asterite)\n- Fixed ICE when using unbound type parameter inside generic type. ([#6292](https://github.com/crystal-lang/crystal/pull/6292), thanks @asterite)\n- Fixed ICE when using unions of metaclasses. ([#6307](https://github.com/crystal-lang/crystal/pull/6307), thanks @asterite)\n- Fixed ICE related to literal type guessing and generic types hierarchy. ([#6341](https://github.com/crystal-lang/crystal/pull/6341), thanks @asterite)\n- Fixed ICE related to `not` and inlinable values. ([#6452](https://github.com/crystal-lang/crystal/pull/6452), thanks @asterite)\n- Fixed rebind variables type in while condition after analyzing its body. ([#6295](https://github.com/crystal-lang/crystal/pull/6295), thanks @asterite)\n- Fixed corner cases regarding automatic casts and method instantiation. ([#6284](https://github.com/crystal-lang/crystal/pull/6284), thanks @asterite)\n- Fixed parsing of `\\A` (and others) inside `%r{...}` inside macros. ([#6282](https://github.com/crystal-lang/crystal/pull/6282), thanks @asterite)\n- Fixed parsing of of named tuple inside generic type arguments. ([#6413](https://github.com/crystal-lang/crystal/pull/6413), thanks @asterite)\n- Fixed disallow cast from module class to virtual metaclass. ([#6320](https://github.com/crystal-lang/crystal/pull/6320), thanks @asterite)\n- Fixed disallow `return` inside a constant's value. ([#6347](https://github.com/crystal-lang/crystal/pull/6347), thanks @asterite)\n- Fixed debug info for closured self. ([#6346](https://github.com/crystal-lang/crystal/pull/6346), thanks @asterite)\n- Fixed parsing error of newline before closing macro. ([#6382](https://github.com/crystal-lang/crystal/pull/6382), thanks @asterite)\n- Fixed missing error if constant has `NoReturn` type. ([#6411](https://github.com/crystal-lang/crystal/pull/6411), thanks @asterite)\n- Fixed give proper error when doing sizeof uninstantiated generic type. ([#6418](https://github.com/crystal-lang/crystal/pull/6418), thanks @asterite)\n- Fixed private aliases at top-level are now considered private. ([#6432](https://github.com/crystal-lang/crystal/pull/6432), thanks @asterite)\n- Fixed setters with multiple arguments as now disallowed. ([#6324](https://github.com/crystal-lang/crystal/pull/6324), thanks @maxfierke)\n- Fixed type var that resolves to number in restriction didn't work. ([#6504](https://github.com/crystal-lang/crystal/pull/6504), thanks @asterite)\n- Add support for class variables in generic classes. ([#6348](https://github.com/crystal-lang/crystal/pull/6348), thanks @asterite)\n- Add support for exception handling in Windows (SEH). ([#6419](https://github.com/crystal-lang/crystal/pull/6419), thanks @RX14)\n- Refactor codegen of binary operators. ([#6330](https://github.com/crystal-lang/crystal/pull/6330), thanks @bcardiff)\n- Refactor use `JSON::Serializable` instead of `JSON.mapping`. ([#6308](https://github.com/crystal-lang/crystal/pull/6308), thanks @kostya)\n- Refactor `Crystal::Call#check_visibility` and extract type methods. ([#6484](https://github.com/crystal-lang/crystal/pull/6484), thanks @asterite, @bcardiff)\n- Change how metaclasses are shown. Use `Foo.class` instead of `Foo:Class`. ([#6439](https://github.com/crystal-lang/crystal/pull/6439), thanks @RX14)\n\n### Tools\n\n- Flatten project structure created by `crystal init`. ([#6317](https://github.com/crystal-lang/crystal/pull/6317), thanks @straight-shoota)\n\n#### Formatter\n\n- Fixed formatting of `{ {1}.foo, ...}` like expressions. ([#6300](https://github.com/crystal-lang/crystal/pull/6300), thanks @asterite)\n- Fixed formatting of `when` with numbers. Use right alignment only if all are number literals. ([#6392](https://github.com/crystal-lang/crystal/pull/6392), thanks @MakeNowJust)\n- Fixed formatting of comment in case's else. ([#6393](https://github.com/crystal-lang/crystal/pull/6393), thanks @MakeNowJust)\n- Fixed code fence when language is not crystal will not be formatted. ([#6424](https://github.com/crystal-lang/crystal/pull/6424), thanks @asterite)\n\n#### Doc generator\n\n- Add line numbers at link when there are duplicated filenames in \"Defined in:\" section. ([#6280](https://github.com/crystal-lang/crystal/pull/6280), [#6489](https://github.com/crystal-lang/crystal/pull/6489), thanks @r00ster91)\n- Fix docs navigator not scrolling into open type on page load. ([#6420](https://github.com/crystal-lang/crystal/pull/6420), thanks @soanvig)\n\n### Others\n\n- Fixed `system_spec` does no longer emit errors messages on BSD platforms. ([#6289](https://github.com/crystal-lang/crystal/pull/6289), thanks @jcs)\n- Fixed compilation issue when running spec against compiler and std together. ([#6312](https://github.com/crystal-lang/crystal/pull/6312), thanks @straight-shoota)\n- Add support for LLVM 6.0. ([#6381](https://github.com/crystal-lang/crystal/pull/6381), [#6380](https://github.com/crystal-lang/crystal/pull/6380), [#6383](https://github.com/crystal-lang/crystal/pull/6383), thanks @felixbuenemann)\n- CI improvements and housekeeping. ([#6313](https://github.com/crystal-lang/crystal/pull/6313), [#6337](https://github.com/crystal-lang/crystal/pull/6337), [#6407](https://github.com/crystal-lang/crystal/pull/6407), [#6408](https://github.com/crystal-lang/crystal/pull/6408), [#6315](https://github.com/crystal-lang/crystal/pull/6315), thanks @bcardiff, @MakeNowJust, @r00ster91, @maiha)\n\n## 0.25.1 (2018-06-27)\n\n### Standard library\n\n#### Macros\n\n- Fixed `Object.delegate` is now able to be used with `[]=` methods. ([#6178](https://github.com/crystal-lang/crystal/pull/6178), thanks @straight-shoota)\n- Fixed `p!` `pp!` are now able to be used with tuples. ([#6244](https://github.com/crystal-lang/crystal/pull/6244), thanks @bcardiff)\n- Add `#copy_with` method to structs generated by `record` macro. ([#5736](https://github.com/crystal-lang/crystal/pull/5736), thanks @chris-baynes)\n- Add docs for `ArrayLiteral#push` and `#unshift`. ([#6232](https://github.com/crystal-lang/crystal/pull/6232), thanks @MakeNowJust)\n\n#### Collections\n\n- Add docs for `Indexable#zip` and `#zip?` methods. ([#5734](https://github.com/crystal-lang/crystal/pull/5734), thanks @rodrigopinto)\n\n#### Serialization\n\n- Add `#dup` and `#clone` for `JSON::Any` and `YAML::Any`. ([6266](https://github.com/crystal-lang/crystal/pull/6266), thanks @asterite)\n- Add docs example of nesting mappings to `YAML.builder`. ([#6097](https://github.com/crystal-lang/crystal/pull/6097), thanks @kalinon)\n\n#### Time\n\n- Fixed docs regarding formatting and parsing `Time`. ([#6208](https://github.com/crystal-lang/crystal/pull/6208), [#6214](https://github.com/crystal-lang/crystal/pull/6214), thanks @r00ster91 and @straight-shoota)\n- Fixed `Time` internals for future Windows support. ([#6181](https://github.com/crystal-lang/crystal/pull/6181), thanks @RX14)\n- Add `Time::Span#microseconds`, `Int#microseconds` and `Float#microseconds`. ([#6272](https://github.com/crystal-lang/crystal/pull/6272), thanks @asterite)\n- Add specs. ([#6174](https://github.com/crystal-lang/crystal/pull/6174), thanks @straight-shoota)\n\n#### Files\n\n- Fixed `File.extname` edge case. ([#6234](https://github.com/crystal-lang/crystal/pull/6234), thanks @bcardiff)\n- Fixed `FileInfo#flags` return value. ([#6248](https://github.com/crystal-lang/crystal/pull/6248), thanks @fgimian)\n\n#### Networking\n\n- Fixed `IO#write(slice : Bytes)` won't write information if slice is empty. ([#6269](https://github.com/crystal-lang/crystal/pull/6269), thanks @asterite)\n- Fixed docs regarding `HTTP::Server#bind_tcp` method. ([#6179](https://github.com/crystal-lang/crystal/pull/6179), [#6233](https://github.com/crystal-lang/crystal/pull/6233), thanks @straight-shoota and @MakeNowJust)\n- Add Etag support in `HTTP::StaticFileHandler`. ([#6145](https://github.com/crystal-lang/crystal/pull/6145), thanks @emq)\n\n#### Misc\n\n- Fixed `mmap` usage on OpenBSD 6.3+. ([#6250](https://github.com/crystal-lang/crystal/pull/6250), thanks @jcs)\n- Fixed `big/big_int`, `big/big_float`, etc are now able to be included directly. ([#6267](https://github.com/crystal-lang/crystal/pull/6267), thanks @asterite)\n- Refactor dependency in `Crystal::Hasher` to avoid load order issues. ([#6184](https://github.com/crystal-lang/crystal/pull/6184), thanks @ysbaddaden)\n\n### Compiler\n\n- Fixed a leakage of unbounded generic type variable and show error. ([#6128](https://github.com/crystal-lang/crystal/pull/6128), thanks @asterite)\n- Fixed error message when lookup of library fails and lib's name contains non-alpha chars. ([#6187](https://github.com/crystal-lang/crystal/pull/6187), thanks @oprypin)\n- Fixed integer kind deduction for very large negative numbers. ([#6182](https://github.com/crystal-lang/crystal/pull/6182), thanks @rGradeStd)\n- Refactor specs tempfiles and data files usage in favor of portability ([#5951](https://github.com/crystal-lang/crystal/pull/5951), thanks @straight-shoota)\n- Improve formatting and information in some compiler error messages. ([#6261](https://github.com/crystal-lang/crystal/pull/6261), thanks @RX14)\n\n### Tools\n\n#### Formatter\n\n- Fixed crash when semicolon after block paren were present. ([#6192](https://github.com/crystal-lang/crystal/pull/6192), thanks @MakeNowJust)\n- Fixed invalid code produced when heredoc and comma were present. ([#6222](https://github.com/crystal-lang/crystal/pull/6222), thanks @straight-shoota and @MakeNowJust)\n- Fixed crash when one-liner `begin`/`rescue` were present. ([#6274](https://github.com/crystal-lang/crystal/pull/6274), thanks @asterite)\n\n#### Doc generator\n\n- Fixed JSON export that prevent jumping to constant. ([#6218](https://github.com/crystal-lang/crystal/pull/6218), thanks @straight-shoota)\n- Fixed crash when virtual types were reached. ([#6246](https://github.com/crystal-lang/crystal/pull/6246), thanks @bcardiff)\n\n### Misc\n\n- CI improvements and housekeeping. ([#6193](https://github.com/crystal-lang/crystal/pull/6193), [#6211](https://github.com/crystal-lang/crystal/pull/6211), [#6209](https://github.com/crystal-lang/crystal/pull/6209), [#6221](https://github.com/crystal-lang/crystal/pull/6221), [#6260](https://github.com/crystal-lang/crystal/pull/6260), thanks @bcardiff, @kostya and @r00ster91)\n- Update man page. ([#6259](https://github.com/crystal-lang/crystal/pull/6259), thanks @docelic)\n\n## 0.25.0 (2018-06-11)\n\n### New features and breaking changes\n\n- **(breaking-change)** Time zones has been added to `Time`. ([#5324](https://github.com/crystal-lang/crystal/pull/5324), [#5819](https://github.com/crystal-lang/crystal/pull/5819), thanks @straight-shoota)\n- **(breaking-change)** Drop `HTTP.rfc1123_date` in favor of `HTTP.format_time` and add time format implementations for ISO-8601, RFC-3339, and RFC-2822. ([#5123](https://github.com/crystal-lang/crystal/pull/5123), thanks @straight-shoota)\n- **(breaking-change)** `crystal deps` is removed, use `shards`. ([#5544](https://github.com/crystal-lang/crystal/pull/5544), thanks @asterite)\n- **(breaking-change)** `Hash#key` was renamed as `Hash#key_for`. ([#5444](https://github.com/crystal-lang/crystal/pull/5444), thanks @marksiemers)\n- **(breaking-change)** `JSON::Any` and `YAML::Any` have been re-implemented solving some inconsistencies and avoiding the usage of recursive aliases (`JSON::Type` and `YAML::Type` have been removed). ([#5183](https://github.com/crystal-lang/crystal/pull/5183), thanks @asterite)\n- **(breaking-change)** Multiple heredocs can be used as arguments and methods can be invoked writing them in the initial delimiter, also empty heredocs are now supported. ([#5578](https://github.com/crystal-lang/crystal/pull/5578), [#5602](https://github.com/crystal-lang/crystal/pull/5602), [#6048](https://github.com/crystal-lang/crystal/pull/6048), thanks @asterite and @MakeNowJust)\n- **(breaking-change)** Refactor signal handlers and avoid closing pipe at exit. ([#5730](https://github.com/crystal-lang/crystal/pull/5730), thanks @ysbaddaden)\n- **(breaking-change)** Improve behaviour of `File.join` with empty path component. ([#5915](https://github.com/crystal-lang/crystal/pull/5915), thanks @straight-shoota)\n- **(breaking-change)** Drop `Colorize#push` in favor of `Colorize#surround` and allow nested calls across the stack. ([#4196](https://github.com/crystal-lang/crystal/pull/4196), thanks @MakeNowJust)\n- **(breaking-change)** `File.stat` was renamed to `File.info` and a more portable API was implemented. ([#5584](https://github.com/crystal-lang/crystal/pull/5584), [#6161](https://github.com/crystal-lang/crystal/pull/6161), thanks @RX14 and @bcardiff)\n- **(breaking-change)** Refactor `HTTP::Server` to bind to multiple addresses. ([#5776](https://github.com/crystal-lang/crystal/pull/5776), [#5959](https://github.com/crystal-lang/crystal/pull/5959), thanks @straight-shoota)\n- **(breaking-change)** Remove block argument from `loop`. ([#6026](https://github.com/crystal-lang/crystal/pull/6026), thanks @asterite)\n- **(breaking-change)** Do not collapse unions for sibling types. ([#6024](https://github.com/crystal-lang/crystal/pull/6024), thanks @asterite)\n- **(breaking-change)** Disallow `typeof` in type restrictions. ([#5192](https://github.com/crystal-lang/crystal/pull/5192), thanks @asterite)\n- **(breaking-change)** Perform unbuffered read when `IO::Buffered#sync = true`. ([#5849](https://github.com/crystal-lang/crystal/pull/5849), thanks @RX14)\n- **(breaking-change)** Drop `when _` support. ([#6150](https://github.com/crystal-lang/crystal/pull/6150), thanks @MakeNowJust)\n- **(breaking-change)** The `DivisionByZero` exception was renamed to `DivisionByZeroError`. ([#5395](https://github.com/crystal-lang/crystal/pull/5395), thanks @sdogruyol)\n- A bootstrap Windows port has been added to the standard library. It's not usable for real programs yet. ([#5339](https://github.com/crystal-lang/crystal/pull/5339), [#5484](https://github.com/crystal-lang/crystal/pull/5484), [#5448](https://github.com/crystal-lang/crystal/pull/5448), thanks @RX14)\n- Add automatic casts on literals arguments for numbers and enums. ([#6074](https://github.com/crystal-lang/crystal/pull/6074), thanks @asterite)\n- Add user defined annotations. ([#6063](https://github.com/crystal-lang/crystal/pull/6063), [#6084](https://github.com/crystal-lang/crystal/pull/6084), [#6106](https://github.com/crystal-lang/crystal/pull/6106), thanks @asterite)\n- Add macro verbatim blocks to avoid nested macros. ([#6108](https://github.com/crystal-lang/crystal/pull/6108), thanks @asterite)\n- Allow namespaced expressions to define constants eg: `Foo::Bar = 1`. ([#5883](https://github.com/crystal-lang/crystal/pull/5883), thanks @bew)\n- Allow trailing `=` in symbol literals. ([#5969](https://github.com/crystal-lang/crystal/pull/5969), thanks @straight-shoota)\n- Allow redefining `None` to `0` for `@[Flags]` enum. ([#6160](https://github.com/crystal-lang/crystal/pull/6160), thanks @bew)\n- Suggest possible solutions to failing requires. ([#5487](https://github.com/crystal-lang/crystal/pull/5487), thanks @RX14)\n- Allow pointers of external C library global variables. ([#4845](https://github.com/crystal-lang/crystal/pull/4845), thanks @larubujo)\n- Decouple pretty-printing (`pp`) and showing the expression (`!`): `p`, `pp`, `p!`, `pp!`. ([#6044](https://github.com/crystal-lang/crystal/pull/6044), thanks @asterite)\n- Add ivars default value reflection in macros. ([#5974](https://github.com/crystal-lang/crystal/pull/5974), thanks @asterite)\n- Add argless overload to `Number#round` to rounds to the nearest whole number. ([#5397](https://github.com/crystal-lang/crystal/pull/5397), thanks @Sija)\n- Add `Int#bits_set?` to easily check that certain bits are set. ([#5619](https://github.com/crystal-lang/crystal/pull/5619), thanks @RX14)\n- Add `Float32` and `Float64` constants. ([#4787](https://github.com/crystal-lang/crystal/pull/4787), thanks @konovod)\n- Add allocated bytes per operation in `Benchmark.ips`. ([#5522](https://github.com/crystal-lang/crystal/pull/5522), thanks @asterite)\n- Add `String#to_utf16` and `String.from_utf16`. ([#5541](https://github.com/crystal-lang/crystal/pull/5541), [#5579](https://github.com/crystal-lang/crystal/pull/5579), [#5583](https://github.com/crystal-lang/crystal/pull/5583) thanks @asterite, @RX14 and @straight-shoota)\n- Add `String#starts_with?(re: Regex)`. ([#5485](https://github.com/crystal-lang/crystal/pull/5485), thanks @MakeNowJust)\n- Add `Regex.needs_escape?`. ([#5962](https://github.com/crystal-lang/crystal/pull/5962), thanks @Sija)\n- Add `Hash#last_key` and `Hash#last_value`. ([#5760](https://github.com/crystal-lang/crystal/pull/5760), thanks @j8r)\n- Add no-copy iteration to `Indexable`. ([#4584](https://github.com/crystal-lang/crystal/pull/4584), thanks @cjgajard)\n- Add `Time#at_{beginning,end}_of_second` ([#6167](https://github.com/crystal-lang/crystal/pull/6167), thanks @straight-shoota)\n- Add `IO::Stapled` to combine two unidirectional `IO`s into a single bidirectional one. ([#6017](https://github.com/crystal-lang/crystal/pull/6017), thanks @straight-shoota)\n- Add context to errors in `JSON.mapping` generated code. ([#5932](https://github.com/crystal-lang/crystal/pull/5932), thanks @straight-shoota)\n- Add `JSON::Serializable` and `YAML::Serializable` attribute powered mappings. ([#6082](https://github.com/crystal-lang/crystal/pull/6082), thanks @kostya)\n- Add `mode` param to `File.write`. ([#5754](https://github.com/crystal-lang/crystal/pull/5754), thanks @woodruffw)\n- Add Punycode/IDNA support and integrate with DNS lookup. ([#2543](https://github.com/crystal-lang/crystal/pull/2543), thanks @MakeNowJust)\n- Add `HTTP::Client#options` method. ([#5824](https://github.com/crystal-lang/crystal/pull/5824), thanks @mamantoha)\n- Add support for `Last-Modified` and other cache improvements to `HTTP::StaticFileHandler`. ([#2470](https://github.com/crystal-lang/crystal/pull/2470), [#5607](https://github.com/crystal-lang/crystal/pull/5607), thanks @bebac and @straight-shoota)\n- Add operations and improvements related to `BigDecimal` and `BigFloat`. ([#5437](https://github.com/crystal-lang/crystal/pull/5437), [#5390](https://github.com/crystal-lang/crystal/pull/5390), [#5589](https://github.com/crystal-lang/crystal/pull/5589), [#5582](https://github.com/crystal-lang/crystal/pull/5582), [#5638](https://github.com/crystal-lang/crystal/pull/5638), [#5675](https://github.com/crystal-lang/crystal/pull/5675), thanks @Sija and @mjago)\n- Add `BigDecimal` and `UUID` JSON support. ([#5525](https://github.com/crystal-lang/crystal/pull/5525), [#5551](https://github.com/crystal-lang/crystal/pull/5551), thanks @lukeasrodgers and @lachlan)\n- Add missing `UUID#inspect`. ([#5574](https://github.com/crystal-lang/crystal/pull/5574), thanks @ngsankha)\n- Add `Logger` configuration in initializer. ([#5618](https://github.com/crystal-lang/crystal/pull/5618), thanks @Sija)\n- Add custom separators in `CSV.build`. ([#5998](https://github.com/crystal-lang/crystal/pull/5998), [#6008](https://github.com/crystal-lang/crystal/pull/6008) thanks @Sija)\n- Add `INI.build` to emit `INI` files. ([#5298](https://github.com/crystal-lang/crystal/pull/5298), thanks @j8r)\n- Add `Process.chroot`. ([#5577](https://github.com/crystal-lang/crystal/pull/5577), thanks @chris-huxtable)\n- Add `Tempfile.tempname` to create likely nonexisting filenames. ([#5360](https://github.com/crystal-lang/crystal/pull/5360), thanks @woodruffw)\n- Add `FileUtils#ln`, `ln_s`, and `ln_sf`. ([#5421](https://github.com/crystal-lang/crystal/pull/5421), thanks @woodruffw)\n- Add support 8bit and true color to `Colorize`. ([#5902](https://github.com/crystal-lang/crystal/pull/5902), thanks @MakeNowJust)\n- Add comparison operators between classes. ([#5645](https://github.com/crystal-lang/crystal/pull/5645), thanks @asterite)\n- Add exception cause in backtrace. ([#5833](https://github.com/crystal-lang/crystal/pull/5833), thanks @RX14)\n- Add unhandled exception as argument in `at_exit`. ([#5906](https://github.com/crystal-lang/crystal/pull/5906), thanks @MakeNowJust)\n- Add support to target aarch64-linux-musl. ([#5861](https://github.com/crystal-lang/crystal/pull/5861), thanks @jirutka)\n- Add `#clear` method to `ArrayLiteral`/`HashLiteral` for macros. ([#5265](https://github.com/crystal-lang/crystal/pull/5265), thanks @Sija)\n- Add `Bool#to_unsafe` for C bindings. ([#5465](https://github.com/crystal-lang/crystal/pull/5465), thanks @woodruffw)\n- Spec: Add expectations `starts_with`, `ends_with`. ([#5881](https://github.com/crystal-lang/crystal/pull/5881), thanks @kostya)\n- Formatter: Add `--include` and `--exclude` options to restrict directories. ([#4635](https://github.com/crystal-lang/crystal/pull/4635), thanks @straight-shoota)\n- Documentation generator: improved navigation, searching, rendering and SEO. ([#5229](https://github.com/crystal-lang/crystal/pull/5229), [#5795](https://github.com/crystal-lang/crystal/pull/5795), [#5990](https://github.com/crystal-lang/crystal/pull/5990), [#5657](https://github.com/crystal-lang/crystal/pull/5657), [#6073](https://github.com/crystal-lang/crystal/pull/6073), thanks @straight-shoota, @Sija and @j8r)\n- Playground: Add button in playground to run formatter. ([#3652](https://github.com/crystal-lang/crystal/pull/3652), thanks @samueleaton)\n\n### Standard library bugs fixed\n\n- Fixed `String#sub` handling of negative indexes. ([#5491](https://github.com/crystal-lang/crystal/pull/5491), thanks @MakeNowJust)\n- Fixed `String#gsub` in non-ascii strings. ([#5350](https://github.com/crystal-lang/crystal/pull/5350), thanks @straight-shoota)\n- Fixed `String#dump` for UTF-8 characters higher than `\\uFFFF`. ([#5668](https://github.com/crystal-lang/crystal/pull/5668), thanks @straight-shoota)\n- Fixed `String#tr` edge case optimization bug. ([#5913](https://github.com/crystal-lang/crystal/pull/5913), thanks @MakeNowJust)\n- Fixed `String#rindex` when called with `Regex`. ([#5594](https://github.com/crystal-lang/crystal/pull/5594), thanks @straight-shoota)\n- Fixed `Time::Span` precision loss and boundary check. ([#5563](https://github.com/crystal-lang/crystal/pull/5563), [#5786](https://github.com/crystal-lang/crystal/pull/5786), thanks @petoem and @straight-shoota)\n- `Array#sample` was fixed to use the provided random number generator (instead of the default) in all cases. ([#5419](https://github.com/crystal-lang/crystal/pull/5419), thanks @c910335)\n- Add short-circuit logic in `Deque#rotate!` for singleton and empty queues. ([#5399](https://github.com/crystal-lang/crystal/pull/5399), thanks @willcosgrove)\n- `Slice#reverse!` was optimised to be up to 43% faster. ([#5401](https://github.com/crystal-lang/crystal/pull/5401), thanks @larubujo)\n- Fixed `Regex#inspect` when escaping was needed. ([#5841](https://github.com/crystal-lang/crystal/pull/5841), thanks @MakeNowJust)\n- Fixed `JSON.mapping` now generates type restriction on getters. ([#5935](https://github.com/crystal-lang/crystal/pull/5935), thanks @Daniel-Worrall)\n- Fixed `JSON.mapping` documentation regarding unions. ([#5483](https://github.com/crystal-lang/crystal/pull/5483), thanks @RX14)\n- Fixed `JSON.mapping` and `YAML.mapping` to allow `properties` property. ([#5180](https://github.com/crystal-lang/crystal/pull/5180), [#5352](https://github.com/crystal-lang/crystal/pull/5352), thanks @maxpowa and @Sija)\n- Fixed `YAML` int and float parsing. ([#5699](https://github.com/crystal-lang/crystal/pull/5699), [#5774](https://github.com/crystal-lang/crystal/pull/5774), thanks @straight-shoota)\n- Fixed WebSocket handshake validation. ([#5327](https://github.com/crystal-lang/crystal/pull/5327), [#6027](https://github.com/crystal-lang/crystal/pull/6027) thanks @straight-shoota)\n- Fixed `HTTP::Client` is able to use ipv6 addresses. ([#6147](https://github.com/crystal-lang/crystal/pull/6147), thanks @bcardiff)\n- Fixed handling some invalid responses in `HTTP::Client`. ([#5630](https://github.com/crystal-lang/crystal/pull/5630), thanks @straight-shoota)\n- Fixed `HTTP::ChunkedContent` will raise on unterminated content. ([#5928](https://github.com/crystal-lang/crystal/pull/5928), [#5943](https://github.com/crystal-lang/crystal/pull/5943), thanks @straight-shoota)\n- `URI#to_s` now handles default ports for lots of schemes. ([#5233](https://github.com/crystal-lang/crystal/pull/5233), thanks @lachlan)\n- `HTTP::Cookies` is able to deal with spaces in cookies. ([#5408](https://github.com/crystal-lang/crystal/pull/5408), thanks @bararchy)\n- Fixed MIME type of SVG images in `HTTP::StaticFileHandler`. ([#5605](https://github.com/crystal-lang/crystal/pull/5605), thanks @damianham)\n- Fixed URI encoding in `StaticFileHandler#redirect_to`. ([#5628](https://github.com/crystal-lang/crystal/pull/5628), thanks @straight-shoota)\n- Fixed `before_request` callbacks to be executed right before writing the request in `HTTP::Client`. ([#5626](https://github.com/crystal-lang/crystal/pull/5626), thanks @asterite)\n- `Dir.glob` was re-implemented with performance improvements and edge cases fixed. ([#5179](https://github.com/crystal-lang/crystal/pull/5179), thanks @straight-shoota)\n- Fixed `File.extname` edge case for '.' in path with no extension. ([#5790](https://github.com/crystal-lang/crystal/pull/5790), thanks @codyjb)\n- Some ECDHE curves were incorrectly disabled in `OpenSSL` clients, this has been fixed. ([#5494](https://github.com/crystal-lang/crystal/pull/5494), thanks @jhass)\n- Fixed allow bcrypt passwords up to 71 bytes. ([#5356](https://github.com/crystal-lang/crystal/pull/5356), thanks @ysbaddaden)\n- Unhandled exceptions occurring inside `Process.fork` now print their backtrace correctly. ([#5431](https://github.com/crystal-lang/crystal/pull/5431), thanks @RX14)\n- Fixed `Zip` no longer modifies deflate signature. ([#5376](https://github.com/crystal-lang/crystal/pull/5376), thanks @luislavena)\n- Fixed `INI` parser edge cases and performance improvements. ([#5442](https://github.com/crystal-lang/crystal/pull/5442), [#5718](https://github.com/crystal-lang/crystal/pull/5718) thanks @woodruffw, @j8r)\n- Fixed initialization of `LibXML`. ([#5587](https://github.com/crystal-lang/crystal/pull/5587), thanks @lbguilherme)\n- Some finalizers were missing for example when the object where cloned. ([#5367](https://github.com/crystal-lang/crystal/pull/5367), thanks @alexbatalov)\n- Fixed segfault handler initialization regarding `sa_mask`. ([#5677](https://github.com/crystal-lang/crystal/pull/5677) thanks @ysbaddaden)\n- Fixed missing reference symbol in ARM. ([#5640](https://github.com/crystal-lang/crystal/pull/5640), thanks @blankoworld)\n- Fixed detect LLVM 5.0 by `llvm-config-5.0` command. ([#5531](https://github.com/crystal-lang/crystal/pull/5531), thanks @Vexatos)\n- Restore STDIN|OUT|ERR blocking state on exit. ([#5802](https://github.com/crystal-lang/crystal/pull/5802), thanks @bew)\n- Fixed multiple `at_exit` handlers chaining. ([#5413](https://github.com/crystal-lang/crystal/pull/5413), thanks @bew)\n- Fixed senders were not notified when channels were closed. ([#5880](https://github.com/crystal-lang/crystal/pull/5880), thanks @carlhoerberg)\n- Fixed forward unhandled exception to caller in `parallel` macro. ([#5726](https://github.com/crystal-lang/crystal/pull/5726), thanks @lipanski)\n- Fixed Markdown parsing of code fences appearing on the same line. ([#5606](https://github.com/crystal-lang/crystal/pull/5606), thanks @oprypin)\n- Fixed OpenSSL bindings to recognize LibreSSL. ([#5676](https://github.com/crystal-lang/crystal/pull/5676), [#6062](https://github.com/crystal-lang/crystal/pull/6062), [#5949](https://github.com/crystal-lang/crystal/pull/5949), [#5973](https://github.com/crystal-lang/crystal/pull/5973) thanks @LVMBDV and @RX14)\n- Fixed path value in to `UNIXSocket` created by `UNIXServer`. ([#5869](https://github.com/crystal-lang/crystal/pull/5869), thanks @straight-shoota)\n- Fixed `Object.delegate` over setters. ([#5964](https://github.com/crystal-lang/crystal/pull/5964), thanks @straight-shoota)\n- Fixed `pp` will now use the same width on every line. ([#5978](https://github.com/crystal-lang/crystal/pull/5978), thanks @MakeNowJust)\n- Fixes missing stdarg.cr for i686-linux-musl. ([#6120](https://github.com/crystal-lang/crystal/pull/6120), thanks @bcardiff)\n- Spec: Fixed junit spec formatter to emit the correct XML. ([#5463](https://github.com/crystal-lang/crystal/pull/5463), thanks @hanneskaeufler)\n\n### Compiler bugs fixed\n\n- Fixed enum generated values when a member has value 0. ([#5954](https://github.com/crystal-lang/crystal/pull/5954), thanks @bew)\n- Fixed compiler issue when previous compilation was interrupted. ([#5585](https://github.com/crystal-lang/crystal/pull/5585), thanks @asterite)\n- Fixed compiler error with an empty `ensure` block. ([#5396](https://github.com/crystal-lang/crystal/pull/5396), thanks @MakeNowJust)\n- Fixed parsing regex in default arguments. ([#5481](https://github.com/crystal-lang/crystal/pull/5481), thanks @MakeNowJust)\n- Fixed parsing error of regex literal after open parenthesis. ([#5453](https://github.com/crystal-lang/crystal/pull/5453), thanks @MakeNowJust)\n- Fixed parsing of empty array with blank. ([#6107](https://github.com/crystal-lang/crystal/pull/6107), thanks @asterite)\n- Static libraries are now found correctly when using the `--static` compiler flag. ([#5385](https://github.com/crystal-lang/crystal/pull/5385), thanks @jreinert)\n- Improve error messages for unterminated literals. ([#5409](https://github.com/crystal-lang/crystal/pull/5409), thanks @straight-shoota)\n- Fixed `ProcNotation` and `ProcLiteral` introspection in macros. ([#5206](https://github.com/crystal-lang/crystal/pull/5206), thanks @javanut13)\n- Cross compilation honors `--emit` and avoid generating `bc_flags` in current directory. ([#5521](https://github.com/crystal-lang/crystal/pull/5521), thanks @asterite)\n- Fixed compiler error with integer constants as generic arguments. ([#5532](https://github.com/crystal-lang/crystal/pull/5532), thanks @asterite)\n- Fixed compiler error with self as base class. ([#5534](https://github.com/crystal-lang/crystal/pull/5534), thanks @asterite)\n- Fixed macro expansion when mutating the argument. ([#5247](https://github.com/crystal-lang/crystal/pull/5247), thanks @MakeNowJust)\n- Fixed macro expansion edge cases. ([#5680](https://github.com/crystal-lang/crystal/pull/5680), [#5842](https://github.com/crystal-lang/crystal/pull/5842), [#6163](https://github.com/crystal-lang/crystal/pull/6163), thanks @asterite, @MakeNowJust and @splattael)\n- Fixed macro overload on named args. ([#5808](https://github.com/crystal-lang/crystal/pull/5808), thanks @bew)\n- Fixed macro numeric types used in interpreter. ([#5972](https://github.com/crystal-lang/crystal/pull/5972), thanks @straight-shoota)\n- Fixed missing debug locations in several places. ([#5597](https://github.com/crystal-lang/crystal/pull/5597), thanks @asterite)\n- Fixed missing information in AST nodes needed for macro expansion. ([#5454](https://github.com/crystal-lang/crystal/pull/5454), thanks @MakeNowJust)\n- Fixed multiline error messages in emitted by `ASTNode#raise` macro method. ([#5670](https://github.com/crystal-lang/crystal/pull/5670), thanks @asterite)\n- Fixed nested delimiters and escaped whitespace in string/symbol array literals. ([#5667](https://github.com/crystal-lang/crystal/pull/5667), thanks @straight-shoota)\n- Fixed custom array/hash-like literals in nested modules. ([#5685](https://github.com/crystal-lang/crystal/pull/5685), thanks @asterite)\n- Fixed usage of static array in C externs. ([#5690](https://github.com/crystal-lang/crystal/pull/5690), thanks @asterite)\n- Fixed `spawn` over expression with receivers. ([#5781](https://github.com/crystal-lang/crystal/pull/5781), thanks @straight-shoota)\n- Fixed prevent heredoc inside interpolation. ([#5648](https://github.com/crystal-lang/crystal/pull/5648), thanks @MakeNowJust)\n- Fixed parsing error when a newline follows block arg. ([#5737](https://github.com/crystal-lang/crystal/pull/5737), thanks @bew)\n- Fixed parsing error when macro argument is followed by a newline. ([#6046](https://github.com/crystal-lang/crystal/pull/6046), thanks @asterite)\n- Fixed compiler error messages wording. ([#5887](https://github.com/crystal-lang/crystal/pull/5887), thanks @r00ster91)\n- Fixed recursion issues in `method_added` macro hook. ([#5159](https://github.com/crystal-lang/crystal/pull/5159), thanks @MakeNowJust)\n- Fixed avoid using type of updated argument for type inference. ([#5166](https://github.com/crystal-lang/crystal/pull/5166), thanks @MakeNowJust)\n- Fixed parsing error message on unbalanced end brace in macros. ([#5420](https://github.com/crystal-lang/crystal/pull/5420), thanks @MakeNowJust)\n- Fixed parsing error message on keywords are used as arguments. ([#5930](https://github.com/crystal-lang/crystal/pull/5930), [#6052](https://github.com/crystal-lang/crystal/pull/6052), thanks @MakeNowJust and @esse)\n- Fixed parsing error message on missing comma for named tuples. ([#5981](https://github.com/crystal-lang/crystal/pull/5981), thanks @MakeNowJust)\n- Fixed missing handling of `cond` node in visitor. ([#6032](https://github.com/crystal-lang/crystal/pull/6032), thanks @veelenga)\n- Fixed cli when `--threads` has invalid value. ([#6039](https://github.com/crystal-lang/crystal/pull/6039), thanks @r00ster91)\n- Fixed private methods can now be called with explicit `self` receiver. ([#6075](https://github.com/crystal-lang/crystal/pull/6075), thanks @MakeNowJust)\n- Fixed missing some missing rules of initializer in initializers macro methods. ([#6077](https://github.com/crystal-lang/crystal/pull/6077), thanks @asterite)\n- Fixed regression bug related to unreachable code. ([#6045](https://github.com/crystal-lang/crystal/pull/6045), thanks @asterite)\n\n### Tools bugs fixed\n\n- Several `crystal init` and template improvements. ([#5475](https://github.com/crystal-lang/crystal/pull/5475), [#5355](https://github.com/crystal-lang/crystal/pull/5355), [#4691](https://github.com/crystal-lang/crystal/pull/4691), [#5788](https://github.com/crystal-lang/crystal/pull/5788), [#5644](https://github.com/crystal-lang/crystal/pull/5644), [#6031](https://github.com/crystal-lang/crystal/pull/6031) thanks @woodruffw, @faustinoaq, @bew, @kostya and @MakeNowJust)\n- Formatter: improve formatting of method call arguments with trailing comments. ([#5492](https://github.com/crystal-lang/crystal/pull/5492), thanks @MakeNowJust)\n- Formatter: fix formatting of multiline statements. ([#5234](https://github.com/crystal-lang/crystal/pull/5234), [#5901](https://github.com/crystal-lang/crystal/pull/5901), [#6013](https://github.com/crystal-lang/crystal/pull/6013) thanks @MakeNowJust)\n- Formatter: fix formatting of multi assignment. ([#5452](https://github.com/crystal-lang/crystal/pull/5452), thanks @MakeNowJust)\n- Formatter: fix formatting of backslash ending statements. ([#5194](https://github.com/crystal-lang/crystal/pull/5194), thanks @asterite)\n- Formatter: fix formatting of `.[]` methods. ([#5424](https://github.com/crystal-lang/crystal/pull/5424), thanks @MakeNowJust)\n- Formatter: fix formatting of statements with comments. ([#5655](https://github.com/crystal-lang/crystal/pull/5655), [#5893](https://github.com/crystal-lang/crystal/pull/5893), [#5909](https://github.com/crystal-lang/crystal/pull/5909), thanks @MakeNowJust)\n- Formatter: fix formatting of nested `begin`/`end`. ([#5922](https://github.com/crystal-lang/crystal/pull/5922), thanks @MakeNowJust)\n- Formatter: fix formatting of trailing comma with block calls. ([#5855](https://github.com/crystal-lang/crystal/pull/5855), thanks @MakeNowJust)\n- Formatter: fix formatting of ending expression after heredoc. ([#6127](https://github.com/crystal-lang/crystal/pull/6127), thanks @asterite)\n- Documentation generator: references to nested types in markdown are now correctly parsed. ([#5308](https://github.com/crystal-lang/crystal/pull/5308), thanks @straight-shoota)\n- Documentation generator: fix leftovers regarding default old `doc` directory. ([#5406](https://github.com/crystal-lang/crystal/pull/5406), thanks @GloverDonovan)\n- Documentation generator: avoid failing on non git directory. ([#3700](https://github.com/crystal-lang/crystal/pull/3700), thanks @MakeNowJust)\n- `Crystal::Doc::Highlighter` has specs now ([#5368](https://github.com/crystal-lang/crystal/pull/5368), thanks @MakeNowJust)\n- Playground: can now be run with HTTPS. ([#5527](https://github.com/crystal-lang/crystal/pull/5527), thanks @opiation)\n- Playground: Pretty-print objects in inspector. ([#4601](https://github.com/crystal-lang/crystal/pull/4601), thanks @jgaskins)\n\n### Misc\n\n- The platform-specific parts of `File` and `IO::FileDescriptor` were moved to `Crystal::System`, as part of preparation for the Windows port. ([#5333](https://github.com/crystal-lang/crystal/pull/5333), [#5553](https://github.com/crystal-lang/crystal/pull/5553), [#5622](https://github.com/crystal-lang/crystal/pull/5622) thanks @RX14)\n- The platform-specific parts of `Dir` were moved to `Crystal::System`, as part of preparation for the Windows port. ([#5447](https://github.com/crystal-lang/crystal/pull/5447), thanks @RX14)\n- Incremental contributions regarding Windows support. ([#5422](https://github.com/crystal-lang/crystal/pull/5422), [#5524](https://github.com/crystal-lang/crystal/pull/5524), [#5533](https://github.com/crystal-lang/crystal/pull/5533), [#5538](https://github.com/crystal-lang/crystal/pull/5538), [#5539](https://github.com/crystal-lang/crystal/pull/5539), [#5580](https://github.com/crystal-lang/crystal/pull/5580), [#5947](https://github.com/crystal-lang/crystal/pull/5947) thanks @RX14 and @straight-shoota)\n- The build on OpenBSD was fixed. ([#5387](https://github.com/crystal-lang/crystal/pull/5387), thanks @wmoxam)\n- Add support for FreeBSD 12 (64-bit inodes). ([#5199](https://github.com/crystal-lang/crystal/pull/5199), thanks @myfreeweb)\n- Scripts and makefiles now depend on `sh` instead of `bash` for greater portability. ([#5468](https://github.com/crystal-lang/crystal/pull/5468), thanks @j8r)\n- Honor `LDFLAGS` and `EXTRA_FLAGS` in `Makefile`. ([#5423](https://github.com/crystal-lang/crystal/pull/5423), [#5860](https://github.com/crystal-lang/crystal/pull/5860), thanks @trofi, @jirutka)\n- Improve message on link failure. ([#5486](https://github.com/crystal-lang/crystal/pull/5486), [#5603](https://github.com/crystal-lang/crystal/pull/5603), thanks @RX14 and @waj)\n- Improve `String#to_json` when chars don't need escaping. ([#5456](https://github.com/crystal-lang/crystal/pull/5456), thanks @larubujo)\n- Improve `Time#add_span` when arguments are zero. ([#5787](https://github.com/crystal-lang/crystal/pull/5787), thanks @straight-shoota)\n- Improve `String#pretty_print` to output by splitting newline. ([#5750](https://github.com/crystal-lang/crystal/pull/5750), thanks @MakeNowJust)\n- Add `\\a` escape sequence. ([#5864](https://github.com/crystal-lang/crystal/pull/5864), thanks @r00ster91)\n- Several miscellaneous minor code cleanups and refactors. ([#5499](https://github.com/crystal-lang/crystal/pull/5499), [#5502](https://github.com/crystal-lang/crystal/pull/5502), [#5507](https://github.com/crystal-lang/crystal/pull/5507), [#5516](https://github.com/crystal-lang/crystal/pull/5516), [#4915](https://github.com/crystal-lang/crystal/pull/4915), [#5526](https://github.com/crystal-lang/crystal/pull/5526), [#5529](https://github.com/crystal-lang/crystal/pull/5529), [#5535](https://github.com/crystal-lang/crystal/pull/5535), [#5537](https://github.com/crystal-lang/crystal/pull/5537), [#5540](https://github.com/crystal-lang/crystal/pull/5540), [#5435](https://github.com/crystal-lang/crystal/pull/5435), [#5520](https://github.com/crystal-lang/crystal/pull/5520), [#5530](https://github.com/crystal-lang/crystal/pull/5530), [#5547](https://github.com/crystal-lang/crystal/pull/5547), [#5543](https://github.com/crystal-lang/crystal/pull/5543), [#5561](https://github.com/crystal-lang/crystal/pull/5561), [#5599](https://github.com/crystal-lang/crystal/pull/5599), [#5493](https://github.com/crystal-lang/crystal/pull/5493), [#5546](https://github.com/crystal-lang/crystal/pull/5546), [#5624](https://github.com/crystal-lang/crystal/pull/5624), [#5701](https://github.com/crystal-lang/crystal/pull/5701), [#5733](https://github.com/crystal-lang/crystal/pull/5733), [#5646](https://github.com/crystal-lang/crystal/pull/5646), [#5729](https://github.com/crystal-lang/crystal/pull/5729), [#5791](https://github.com/crystal-lang/crystal/pull/5791), [#5859](https://github.com/crystal-lang/crystal/pull/5859), [#5882](https://github.com/crystal-lang/crystal/pull/5882), [#5899](https://github.com/crystal-lang/crystal/pull/5899), [#5918](https://github.com/crystal-lang/crystal/pull/5918), [#5896](https://github.com/crystal-lang/crystal/pull/5896), [#5810](https://github.com/crystal-lang/crystal/pull/5810), [#5575](https://github.com/crystal-lang/crystal/pull/5575), [#5785](https://github.com/crystal-lang/crystal/pull/5785), [#5866](https://github.com/crystal-lang/crystal/pull/5866), [#5816](https://github.com/crystal-lang/crystal/pull/5816), [#5945](https://github.com/crystal-lang/crystal/pull/5945), [#5963](https://github.com/crystal-lang/crystal/pull/5963), [#5968](https://github.com/crystal-lang/crystal/pull/5968), [#5977](https://github.com/crystal-lang/crystal/pull/5977), [#6004](https://github.com/crystal-lang/crystal/pull/6004), [#5794](https://github.com/crystal-lang/crystal/pull/5794), [#5858](https://github.com/crystal-lang/crystal/pull/5858), [#6033](https://github.com/crystal-lang/crystal/pull/6033), [#6036](https://github.com/crystal-lang/crystal/pull/6036), [#6079](https://github.com/crystal-lang/crystal/pull/6079), [#6111](https://github.com/crystal-lang/crystal/pull/6111), [#6118](https://github.com/crystal-lang/crystal/pull/6118), [#6141](https://github.com/crystal-lang/crystal/pull/6141), [#6142](https://github.com/crystal-lang/crystal/pull/6142), [#5380](https://github.com/crystal-lang/crystal/pull/5380), [#6071](https://github.com/crystal-lang/crystal/pull/6071), thanks @chastell, @lachlan, @bew, @RX14, @sdogruyol, @MakeNowJust, @Sija, @noriyotcp, @asterite, @splattael, @straight-shoota, @r00ster91, @jirutka, @paulcsmith, @rab, @esse, @carlhoerberg, @chris-huxtable, @luislavena)\n- Several documentation fixes and additions. ([#5425](https://github.com/crystal-lang/crystal/pull/5425), [#5682](https://github.com/crystal-lang/crystal/pull/5682), [#5779](https://github.com/crystal-lang/crystal/pull/5779), [#5576](https://github.com/crystal-lang/crystal/pull/5576), [#5806](https://github.com/crystal-lang/crystal/pull/5806), [#5817](https://github.com/crystal-lang/crystal/pull/5817), [#5873](https://github.com/crystal-lang/crystal/pull/5873), [#5878](https://github.com/crystal-lang/crystal/pull/5878), [#5637](https://github.com/crystal-lang/crystal/pull/5637), [#5885](https://github.com/crystal-lang/crystal/pull/5885), [#5884](https://github.com/crystal-lang/crystal/pull/5884), [#5728](https://github.com/crystal-lang/crystal/pull/5728), [#5917](https://github.com/crystal-lang/crystal/pull/5917), [#5912](https://github.com/crystal-lang/crystal/pull/5912), [#5894](https://github.com/crystal-lang/crystal/pull/5894), [#5933](https://github.com/crystal-lang/crystal/pull/5933), [#5809](https://github.com/crystal-lang/crystal/pull/5809), [#5936](https://github.com/crystal-lang/crystal/pull/5936), [#5908](https://github.com/crystal-lang/crystal/pull/5908), [#5851](https://github.com/crystal-lang/crystal/pull/5851), [#5378](https://github.com/crystal-lang/crystal/pull/5378), [#5914](https://github.com/crystal-lang/crystal/pull/5914), [#5967](https://github.com/crystal-lang/crystal/pull/5967), [#5993](https://github.com/crystal-lang/crystal/pull/5993), [#3482](https://github.com/crystal-lang/crystal/pull/3482), [#5946](https://github.com/crystal-lang/crystal/pull/5946), [#6095](https://github.com/crystal-lang/crystal/pull/6095), [#6117](https://github.com/crystal-lang/crystal/pull/6117), [#6131](https://github.com/crystal-lang/crystal/pull/6131), [#6162](https://github.com/crystal-lang/crystal/pull/6162), thanks @MakeNowJust, @straight-shoota, @vendethiel, @bew, @Heaven31415, @marksiemers, @Willamin, @r00ster91, @maiha, @Givralix, @docelic, @CaDs, @esse, @igneus, @masukomi)\n- CI housekeeping and including 32 bits automated builds. ([#5796](https://github.com/crystal-lang/crystal/pull/5796), [#5804](https://github.com/crystal-lang/crystal/pull/5804), [#5837](https://github.com/crystal-lang/crystal/pull/5837), [#6015](https://github.com/crystal-lang/crystal/pull/6015), [#6165](https://github.com/crystal-lang/crystal/pull/6165), thanks @bcardiff, @bew and @Sija)\n- Sync docs in master to [https://crystal-lang.org/api/master](https://crystal-lang.org/api/master). ([#5941](https://github.com/crystal-lang/crystal/pull/5941), thanks @bcardiff)\n- Enable the large heap configuration for libgc. ([#5839](https://github.com/crystal-lang/crystal/pull/5839), thanks @RX14)\n- Improve Ctrl-C handling of spec. ([#5719](https://github.com/crystal-lang/crystal/pull/5719), thanks @MakeNowJust)\n- Playground: Update to codemirror 5.38.0. ([#6166](https://github.com/crystal-lang/crystal/pull/6166), thanks @bcardiff)\n\n## 0.24.2 (2018-03-08)\n\n- Fixed an `Index out of bounds` raised during `at_exit` ([#5224](https://github.com/crystal-lang/crystal/issues/5224), [#5565](https://github.com/crystal-lang/crystal/issues/5565), thanks @ysbaddaden)\n- Re-add `Dir#each` so it complies with `Enumerable` ([#5458](https://github.com/crystal-lang/crystal/issues/5458), thanks @bcardiff)\n- Fixed `SSL::Context` bug verifying certificates ([#5266](https://github.com/crystal-lang/crystal/issues/5266), [#5601](https://github.com/crystal-lang/crystal/issues/5601), thanks @waj)\n- Fixed UUID documentation that was missing ([#5478](https://github.com/crystal-lang/crystal/issues/5478), [#5542](https://github.com/crystal-lang/crystal/issues/5542), thanks @asterite)\n- Fixed a bug with single expressions in parenthesis ([#5482](https://github.com/crystal-lang/crystal/issues/5482), [#5511](https://github.com/crystal-lang/crystal/issues/5511), [#5513](https://github.com/crystal-lang/crystal/issues/5513), thanks @MakeNowJust)\n- Fixed `skip_file` macro docs ([#5488](https://github.com/crystal-lang/crystal/issues/5488), thanks @straight-shoota)\n- Fixed CI `build` script's `LIBRARY_PATH` ([#5457](https://github.com/crystal-lang/crystal/issues/5457), [#5461](https://github.com/crystal-lang/crystal/issues/5461), thanks @bcardiff)\n- Fixed formatter bug with upper-cased `fun` names ([#5432](https://github.com/crystal-lang/crystal/issues/5432), [#5434](https://github.com/crystal-lang/crystal/issues/5434), thanks @bew)\n\n## 0.24.1 (2017-12-23)\n\n### New features\n\n- Add ThinLTO support for faster release builds in LLVM 4.0 and above. ([#4367](https://github.com/crystal-lang/crystal/issues/4367), thanks @bcardiff)\n- **(breaking-change)** Add `UUID` type. `Random::Secure.uuid` has been replaced with `UUID.random`. ([#4453](https://github.com/crystal-lang/crystal/issues/4453), thanks @wontruefree)\n- Add a `BigDecimal` class for arbitrary precision, exact, decimal numbers. ([#4876](https://github.com/crystal-lang/crystal/issues/4876) and [#5255](https://github.com/crystal-lang/crystal/issues/5255), thanks @vegai and @Sija)\n- Allow `Set` to work as a case condition, which matches when the case variable is inside the set. ([#5269](https://github.com/crystal-lang/crystal/issues/5269), thanks @MakeNowJust)\n- **(breaking-change)** Change `Time::Format` codes to allow more robust options for parsing sub-second precision times. ([#5317](https://github.com/crystal-lang/crystal/issues/5317), thanks @bcardiff)\n- Add `Time.utc`, an alias of `Time.new` which shortens creating UTC times. ([#5321](https://github.com/crystal-lang/crystal/issues/5321), thanks @straight-shoota)\n- Add custom extension support to `Tempfile`. ([#5264](https://github.com/crystal-lang/crystal/issues/5264), thanks @jreinert)\n- Add `reduce` method to `TupleLiteral` and `ArrayLiteral` when using macros. ([#5294](https://github.com/crystal-lang/crystal/issues/5294), thanks @javanut13)\n- Export a JSON representation of the documentation in the generated output. ([#4746](https://github.com/crystal-lang/crystal/issues/4746) and [#5228](https://github.com/crystal-lang/crystal/issues/5228), thanks @straight-shoota)\n- Make `gc/none` garbage collection compile again and allow it to be enabled using `-Dgc_none` compiler flag. ([#5314](https://github.com/crystal-lang/crystal/issues/5314), thanks @ysbaddaden)\n\n### Standard library bugs fixed\n\n- Make `String#[]` unable to read out-of-bounds when the string ends in a unicode character. ([#5257](https://github.com/crystal-lang/crystal/issues/5257), thanks @Papierkorb)\n- Fix incorrect parsing of long JSON floating point values. ([#5323](https://github.com/crystal-lang/crystal/issues/5323), thanks @benoist)\n- Replace the default hash function with one resistant to hash DoS. ([#5146](https://github.com/crystal-lang/crystal/issues/5146), thanks @funny-falcon)\n- Ensure equal numbers always have the same hashcode. ([#5276](https://github.com/crystal-lang/crystal/issues/5276), thanks @akzhan)\n- Fix struct equality when two structs descend from the same abstract struct. ([#5254](https://github.com/crystal-lang/crystal/issues/5254), thanks @hinrik)\n- Fix `URI#full_path` not to append a `?` unless the query params are nonempty. ([#5340](https://github.com/crystal-lang/crystal/issues/5340), thanks @paulcsmith)\n- Fix `HTTP::Params.parse` to parse `&&` correctly. ([#5274](https://github.com/crystal-lang/crystal/issues/5274), thanks @akiicat)\n- Disallow null bytes in `ENV` keys and values. ([#5216](https://github.com/crystal-lang/crystal/issues/5216), thanks @Papierkorb)\n- Disallow null bytes in `XML::Node` names and content. ([#5200](https://github.com/crystal-lang/crystal/issues/5200), thanks @RX14)\n- Fix `IO#blocking=` on OpenBSD. ([#5283](https://github.com/crystal-lang/crystal/issues/5283), thanks @wmoxam)\n- Fix linking programs in OpenBSD. ([#5282](https://github.com/crystal-lang/crystal/issues/5282), thanks @wmoxam)\n\n### Compiler bugs fixed\n\n- Stop incorrectly finding top-level methods when searching for a `super` method. ([#5202](https://github.com/crystal-lang/crystal/issues/5202), thanks @lbguilherme)\n- Fix parsing regex literals starting with a `;` directly after a call (ex `p /;/`). ([#5208](https://github.com/crystal-lang/crystal/issues/5208), thanks @MakeNowJust)\n- Correct a case where `Expressions#to_s` could produce invalid output, causing macro expansion to fail. ([#5226](https://github.com/crystal-lang/crystal/issues/5226), thanks @asterite)\n- Give error instead of crashing when `self` is used at the top level. ([#5227](https://github.com/crystal-lang/crystal/issues/5227), thanks @MakeNowJust)\n- Give error instead of crashing when using `instance_sizeof` on a generic type without providing it's type arguments. ([#5209](https://github.com/crystal-lang/crystal/issues/5209), thanks @lbguilherme)\n- Fix parsing calls when short block syntax (`&.foo`) is followed by a newline. ([#5237](https://github.com/crystal-lang/crystal/issues/5237), thanks @MakeNowJust)\n- Give error instead of crashing when an unterminated string array literal (`%w()`) sits at the end of a file. ([#5241](https://github.com/crystal-lang/crystal/issues/5241), thanks @asterite)\n- Give error when attempting to use macro yield (`{{yield}}`) outside a macro. ([#5307](https://github.com/crystal-lang/crystal/issues/5307), thanks @MakeNowJust)\n- Fix error related to generic inheritance. ([#5284](https://github.com/crystal-lang/crystal/issues/5284), thanks @MakeNowJust)\n- Fix compiler crash when using recursive alias and generics. ([#5330](https://github.com/crystal-lang/crystal/issues/5330), thanks @MakeNowJust)\n- Fix parsing `foo(+1)` as `foo + 1` instead of `foo(1)` where `foo` was a local variable. ([#5336](https://github.com/crystal-lang/crystal/issues/5336), thanks @MakeNowJust)\n- Documentation generator: Keep quoted symbol literals quoted when syntax highlighting code blocks in documentation output. ([#5238](https://github.com/crystal-lang/crystal/issues/5238), thanks @MakeNowJust)\n- Documentation generator: Keep the original delimiter used when syntax highlighting string array literals. ([#5297](https://github.com/crystal-lang/crystal/issues/5297), thanks @MakeNowJust)\n- Documentation generator: Fix XSS vulnerability when syntax highlighting string array literals. ([#5259](https://github.com/crystal-lang/crystal/issues/5259), thanks @MakeNowJust)\n- Formatter: fix indentation of the last comment in a `begin`/`end` block. ([#5198](https://github.com/crystal-lang/crystal/issues/5198), thanks @MakeNowJust)\n- Formatter: fix formatting parentheses with multiple lines in. ([#5268](https://github.com/crystal-lang/crystal/issues/5268), thanks @MakeNowJust)\n- Formatter: fix formatting `$1?`. ([#5313](https://github.com/crystal-lang/crystal/issues/5313), thanks @MakeNowJust)\n- Formatter: ensure to insert a space between `{` and `%` characters to avoid forming `{%` macros. ([#5278](https://github.com/crystal-lang/crystal/issues/5278), thanks @MakeNowJust)\n\n### Misc\n\n- Fix `Makefile`, CI, and gitignore to use the new documentation path after [#4937](https://github.com/crystal-lang/crystal/issues/4937). ([#5217](https://github.com/crystal-lang/crystal/issues/5217), thanks @straight-shoota)\n- Miscellaneous code cleanups. ([#5318](https://github.com/crystal-lang/crystal/issues/5318), [#5341](https://github.com/crystal-lang/crystal/issues/5341) and [#5366](https://github.com/crystal-lang/crystal/issues/5366), thanks @bew and @mig-hub)\n- Documentation fixes. ([#5253](https://github.com/crystal-lang/crystal/issues/5253), [#5296](https://github.com/crystal-lang/crystal/issues/5296), [#5300](https://github.com/crystal-lang/crystal/issues/5300) and [#5322](https://github.com/crystal-lang/crystal/issues/5322), thanks @arcage, @icyleaf, @straight-shoota and @bew)\n- Fix the in-repository changelog to include 0.24.0. ([#5331](https://github.com/crystal-lang/crystal/pull/5331), thanks @sdogruyol)\n\n## 0.24.0 (2017-10-30)\n\n- **(breaking-change)** HTTP::Client#post_form is now HTTP::Client.post(form: ...)\n- **(breaking-change)** Array#reject!, Array#compact! and Array#select! now return self ([#5154](https://github.com/crystal-lang/crystal/pull/5154))\n- **(breaking-change)** Remove the possibility to require big_int, big_float or big_rational individually: use require \"big\" instead ([#5121](https://github.com/crystal-lang/crystal/pull/5121))\n- **(breaking-change)** Spec: remove expect_raises without type argument ([#5096](https://github.com/crystal-lang/crystal/pull/5096))\n- **(breaking-change)** IO is now a class, no longer a module ([#4901](https://github.com/crystal-lang/crystal/pull/4901))\n- **(breaking-change)** Time constructors now have nanosecond and kind as named argument ([#5072](https://github.com/crystal-lang/crystal/pull/5072))\n- **(breaking-change)** Removed XML.escape. Use HTML.escape instead ([#5046](https://github.com/crystal-lang/crystal/pull/5046))\n- **(breaking-change)** Removed macro def ([#5040](https://github.com/crystal-lang/crystal/pull/5040))\n- **(breaking-change)** SecureRandom is now Random::Secure ([#4894](https://github.com/crystal-lang/crystal/pull/4894))\n- **(breaking-change)** HTML.escape now only escapes &<>\"' ([#5012](https://github.com/crystal-lang/crystal/pull/5012))\n- **(breaking-change)** To define a custom hash method you must now define hash(hasher) ([#4946](https://github.com/crystal-lang/crystal/pull/4946))\n- **(breaking-change)** Flate::Reader.new(&block) and Flate::Writer.new(&block) now use the name open ([#4887](https://github.com/crystal-lang/crystal/pull4887/))\n- **(breaking-change)** Use an Enum for Process stdio redirections ([#4445](https://github.com/crystal-lang/crystal/pull/4445))\n- **(breaking-change)** Remove '$0' special syntax\n- **(breaking-change)** Remove bare array creation from multi assign (a = 1, 2, 3) ([#4824](https://github.com/crystal-lang/crystal/pull/4824))\n- **(breaking-change)** Rename skip macro method to skip_file ([#4709](https://github.com/crystal-lang/crystal/pull/4709))\n- **(breaking-change)** StaticArray#map and Slice#map now return their same type instead of Array ([#5124](https://github.com/crystal-lang/crystal/pull/5124))\n- **(breaking-change)** Tuple#map_with_index now returns a Tuple. ([#5086](https://github.com/crystal-lang/crystal/pull/5086))\n- Packages built with LLVM 3.9.1. They should (hopefully) fix [#4719](https://github.com/crystal-lang/crystal/issues/4719)\n- Syntax: Allow flat rescue/ensure/else block in do/end block ([#5114](https://github.com/crystal-lang/crystal/pull/5114))\n- Syntax: fun names and lib function calls can now start with Uppercase\n- Macros: Using an alias in macros will now automatically resolve it to is aliased type ([#4995](https://github.com/crystal-lang/crystal/pull/4995))\n- Macros: The flags bits32 and bits64 are now automatically defined in macros\n- The YAML module has now full support for the 1.1 core schema with additional types, and properly supports aliases and merge keys ([#5007](https://github.com/crystal-lang/crystal/pull/5007))\n- Add --output option to crystal docs ([#4937](https://github.com/crystal-lang/crystal/pull/4937))\n- Add Time#days_in_year: it returns the no of days in a given year ([#5163](https://github.com/crystal-lang/crystal/pull/5163))\n- Add Time.monotonic to return monotonic clock ([#5108](https://github.com/crystal-lang/crystal/pull/5108))\n- Add remove_empty option to many String#split overloads\n- Add Math.sqrt overloads for Bigs ([#5113](https://github.com/crystal-lang/crystal/pull/5113))\n- Add --stdin-filename to crystal command to compile source from STDIN ([#4571](https://github.com/crystal-lang/crystal/pull/4571))\n- Add Crystal.main to more easily redefine the main of a program ([#4998](https://github.com/crystal-lang/crystal/pull/4998))\n- Add Tuple.types that returns a tuple of types ([#4962](https://github.com/crystal-lang/crystal/pull/4962))\n- Add NamedTuple.types that returns a named tuple of types ([#4962](https://github.com/crystal-lang/crystal/pull/4962))\n- Add NamedTuple#merge(other : NamedTuple) ([#4688](https://github.com/crystal-lang/crystal/pull/4688))\n- Add YAML and JSON.mapping presence: true option ([#4843](https://github.com/crystal-lang/crystal/pull/4843))\n- Add Dir.each_child(&block) ([#4811](https://github.com/crystal-lang/crystal/pull/4811))\n- Add Dir.children ([#4808](https://github.com/crystal-lang/crystal/pull/4808))\n- HTML.unescape now supports all HTML5 named entities ([#5064](https://github.com/crystal-lang/crystal/pull/5064))\n- Regex now supports duplicated named captures ([#5061](https://github.com/crystal-lang/crystal/pull/5061))\n- rand(0) is now valid and returns 0\n- Tuple#[] now supports a negative index ([#4735](https://github.com/crystal-lang/crystal/pull/4735))\n- JSON::Builder#field now accepts non-scalar values ([#4706](https://github.com/crystal-lang/crystal/pull/4706))\n- Number#inspect now shows the number type\n- Some additions to Big arithmetics ([#4653](https://github.com/crystal-lang/crystal/pull/4653))\n- Increase the precision of Time and Time::Span to nanoseconds ([#5022](https://github.com/crystal-lang/crystal/pull/5022))\n- Upgrade Unicode to 10.0.0 ([#5122](https://github.com/crystal-lang/crystal/pull/5122))\n- Support LLVM 5.0 ([#4821](https://github.com/crystal-lang/crystal/pull/4821))\n- [Lots of bugs fixed](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.24.0)\n\n## 0.23.1 (2017-07-01)\n\n- Added `Random::PCG32` generator (See [#4536](https://github.com/crystal-lang/crystal/issues/4536), thanks @konovod)\n- WebSocket should compare \"Upgrade\" header value with case insensitive (See [#4617](https://github.com/crystal-lang/crystal/issues/4617), thanks @MakeNowJust)\n- Fixed macro lookup from included module (See [#4639](https://github.com/crystal-lang/crystal/issues/4639), thanks @asterite)\n- Explained \"crystal tool expand\" in crystal(1) man page (See [#4643](https://github.com/crystal-lang/crystal/issues/4643), thanks @MakeNowJust)\n- Explained how to detect end of file in `IO` (See [#4661](https://github.com/crystal-lang/crystal/issues/4661), thanks @oprypin)\n\n## 0.23.0 (2017-06-27)\n\n- **(breaking-change)** `Logger#formatter` takes a `Severity` instead of a `String` (See [#4355](https://github.com/crystal-lang/crystal/issues/4355), [#4369](https://github.com/crystal-lang/crystal/issues/4369), thanks @Sija)\n- **(breaking-change)** Removed `IO.select` (See [#4392](https://github.com/crystal-lang/crystal/issues/4392), thanks @RX14)\n- Added `Crystal::System::Random` namespace (See [#4450](https://github.com/crystal-lang/crystal/issues/4450), thanks @ysbaddaden)\n- Added `Path#resolve?` macro method (See [#4370](https://github.com/crystal-lang/crystal/issues/4370), [#4408](https://github.com/crystal-lang/crystal/issues/4408), thanks @RX14)\n- Added range methods to `BitArray` (See [#4397](https://github.com/crystal-lang/crystal/issues/4397), [#3968](https://github.com/crystal-lang/crystal/issues/3968), thanks @RX14)\n- Added some well-known HTTP Status messages (See [#4419](https://github.com/crystal-lang/crystal/issues/4419), thanks @akzhan)\n- Added compiler progress indicator (See [#4182](https://github.com/crystal-lang/crystal/issues/4182), thanks @RX14)\n- Added `System.cpu_cores` (See [#4449](https://github.com/crystal-lang/crystal/issues/4449), [#4226](https://github.com/crystal-lang/crystal/issues/4226), thanks @miketheman)\n- Added `separator` and `quote_char` to `CSV#each_row` (See [#4448](https://github.com/crystal-lang/crystal/issues/4448), thanks @timsu)\n- Added `map_with_index!` to `Pointer`, `Array` and `StaticArray` (See [#4456](https://github.com/crystal-lang/crystal/issues/4456), [#3356](https://github.com/crystal-lang/crystal/issues/3356), [#3354](https://github.com/crystal-lang/crystal/issues/3354), thanks @Nephos)\n- Added `headers` parameter to `HTTP::WebSocket` constructors (See [#4227](https://github.com/crystal-lang/crystal/issues/4227), [#4222](https://github.com/crystal-lang/crystal/issues/4222), thanks @adamtrilling)\n- Added `unlink` to `XML::Node` (See [#4515](https://github.com/crystal-lang/crystal/issues/4515), [#4331](https://github.com/crystal-lang/crystal/issues/4331), thanks @RX14 and @MrSorcus)\n- Added `Math.frexp` (See [#4560](https://github.com/crystal-lang/crystal/issues/4560), thanks @akzhan)\n- Added `Regex::MatchData` support for negative indexes (See [#4566](https://github.com/crystal-lang/crystal/issues/4566), thanks @MakeNowJust)\n- Added `captures`, `named_captures`, `to_a` and `to_h` to `Regex::MatchData` (See [#3783](https://github.com/crystal-lang/crystal/issues/3783), thanks @MakeNowJust)\n- Added `|` as a string delimiter to allow `q|string|` syntax (See [#3467](https://github.com/crystal-lang/crystal/issues/3467), thanks @RX14)\n- Added support for Windows linker (See [#4491](https://github.com/crystal-lang/crystal/issues/4491), thanks @RX14)\n- Added llvm operand bundle def and catch pad/ret/switch in order to support Windows SEH (See [#4501](https://github.com/crystal-lang/crystal/issues/4501), thanks @bcardiff)\n- Added `Float::Printer` based on Grisu3 to speed up float to string conversion (See [#4333](https://github.com/crystal-lang/crystal/issues/4333), thanks @will)\n- Added `Object.unsafe_as` to unsafely reinterpret the bytes of an object as being of another `type` (See [#4333](https://github.com/crystal-lang/crystal/issues/4333), thanks @asterite)\n- Added `.downcase(Unicode::CaseOptions::Fold)` option which convert strings to casefolded strings for caseless matching (See [#4512](https://github.com/crystal-lang/crystal/issues/4512), thanks @akzhan)\n- Added `OpenSSL::DigestIO` to wrap an IO while calculating a digest (See [#4260](https://github.com/crystal-lang/crystal/issues/4260), thanks @spalladino)\n- Added `zero?` to numbers and time spans (See [#4026](https://github.com/crystal-lang/crystal/issues/4026), thanks @jellymann)\n- Added `TypeNode#has_method?` method (See [#4474](https://github.com/crystal-lang/crystal/issues/4474), thanks @Sija)\n- `Regex::MatchData#size` renamed to `#group_size` (See [#4565](https://github.com/crystal-lang/crystal/issues/4565), thanks @MakeNowJust)\n- `HTTP::StaticFileHandler` can disable directory listing (See [#4403](https://github.com/crystal-lang/crystal/issues/4403), [#4398](https://github.com/crystal-lang/crystal/issues/4398), thanks @joaodiogocosta)\n- `bin/crystal` now uses `/bin/sh` instead of `/bin/bash` (See [#3809](https://github.com/crystal-lang/crystal/issues/3809), [#4410](https://github.com/crystal-lang/crystal/issues/4410), thanks @TheLonelyGhost)\n- `crystal init` generates a `.editorconfig` file (See [#4422](https://github.com/crystal-lang/crystal/issues/4422), [#297](https://github.com/crystal-lang/crystal/issues/297), thanks @akzhan)\n- `man` page for `crystal` command (See [#2989](https://github.com/crystal-lang/crystal/issues/2989), [#1291](https://github.com/crystal-lang/crystal/issues/1291), thanks @dread-uo)\n- Re-raising an exception doesn't overwrite its callstack (See [#4487](https://github.com/crystal-lang/crystal/issues/4487), [#4482](https://github.com/crystal-lang/crystal/issues/4482), thanks @akzhan)\n- MD5 and SHA1 documentation clearly states they are not cryptographically secure anymore (See [#4426](https://github.com/crystal-lang/crystal/issues/4426), thanks @RX14)\n- Documentation about constructor methods now rendered separately (See [#4216](https://github.com/crystal-lang/crystal/issues/4216), thanks @Sija)\n- Turn `Random::System` into a module (See [#4542](https://github.com/crystal-lang/crystal/issues/4542), thanks @oprypin)\n- `Regex::MatchData` pretty printed (See [#4574](https://github.com/crystal-lang/crystal/issues/4574), thanks @MakeNowJust)\n- `String.underscore` treats digits as downcase or upcase characters depending previous characters (See [#4280](https://github.com/crystal-lang/crystal/issues/4280), thanks @MakeNowJust)\n- Refactor time platform specific implementation (See [#4502](https://github.com/crystal-lang/crystal/issues/4502), thanks @bcardiff)\n- Fixed Crystal not reusing .o files across builds (See [#4336](https://github.com/crystal-lang/crystal/issues/4336))\n- Fixed `SomeClass.class.is_a?(SomeConst)` causing an \"already had enclosing call\" exception (See [#4364](https://github.com/crystal-lang/crystal/issues/4364), [#4390](https://github.com/crystal-lang/crystal/issues/4390), thanks @rockwyc992)\n- Fixed `HTTP::Params.parse` query string with two `=` gave wrong result (See [#4388](https://github.com/crystal-lang/crystal/issues/4388), [#4389](https://github.com/crystal-lang/crystal/issues/4389), thanks @akiicat)\n- Fixed `Class.class.is_a?(Class.class.class.class.class)` 🎉 (See [#4375](https://github.com/crystal-lang/crystal/issues/4375), [#4374](https://github.com/crystal-lang/crystal/issues/4374), thanks @rockwyc992)\n- Fixed select hanging when sending before receive (See [#3862](https://github.com/crystal-lang/crystal/issues/3862), [#3899](https://github.com/crystal-lang/crystal/issues/3899), thanks @kostya)\n- Fixed \"Unknown key in access token json: id_token\" error in OAuth2 client (See [#4437](https://github.com/crystal-lang/crystal/issues/4437))\n- Fixed macro lookup conflicting with method lookup when including on top level (See [#236](https://github.com/crystal-lang/crystal/issues/236))\n- Fixed Vagrant images (See [#4510](https://github.com/crystal-lang/crystal/issues/4510), [#4508](https://github.com/crystal-lang/crystal/issues/4508), thanks @Val)\n- Fixed `IO::FileDescriptor#seek` from current position (See [#4558](https://github.com/crystal-lang/crystal/issues/4558), thanks @ysbaddaden)\n- Fixed `IO::Memory#gets_to_end` to consume the `IO` (See [#4415](https://github.com/crystal-lang/crystal/issues/4415), thanks @jhass)\n- Fixed setting of XML attributes (See [#4562](https://github.com/crystal-lang/crystal/issues/4562), thanks @asterite)\n- Fixed \"SSL_shutdown: Operation now in progress\" error by retrying (See [#3168](https://github.com/crystal-lang/crystal/issues/3168), thanks @akzhan)\n- Fixed WebSocket negotiation (See [#4386](https://github.com/crystal-lang/crystal/issues/4386), thanks @RX14)\n\n## 0.22.0 (2017-04-20)\n\n- **(breaking-change)** Removed `Process.new(pid)` is now private (See [#4197](https://github.com/crystal-lang/crystal/issues/4197))\n- **(breaking-change)** IO#peek now returns an empty slice on EOF (See [#4240](https://github.com/crystal-lang/crystal/issues/4240), [#4261](https://github.com/crystal-lang/crystal/issues/4261))\n- **(breaking-change)** Rename `WeakRef#target` to `WeakRef#value` (See [#4293](https://github.com/crystal-lang/crystal/issues/4293))\n- **(breaking-change)** Rename `HTTP::Params.from_hash` to `HTTP::Params.encode` (See [#4205](https://github.com/crystal-lang/crystal/issues/4205))\n- **(breaking-change)** `'\\\"'` is now invalid, use `'\"'` (See [#4309](https://github.com/crystal-lang/crystal/issues/4309))\n- Improved backtrace function names are now read from DWARF sections (See [#3958](https://github.com/crystal-lang/crystal/issues/3958), thanks @ysbaddaden)\n- Improved sigfaults and exceptions are printed to STDERR (See [#4163](https://github.com/crystal-lang/crystal/issues/4163), thanks @Sija)\n- Improved SSL Sockets are now buffered (See [#4248](https://github.com/crystal-lang/crystal/issues/4248))\n- Improved type inference on loops (See [#4242](https://github.com/crystal-lang/crystal/issues/4242), [#4243](https://github.com/crystal-lang/crystal/issues/4243))\n- Improved `pp` and `p`, the printed value is returned (See [#4285](https://github.com/crystal-lang/crystal/issues/4285), [#4283](https://github.com/crystal-lang/crystal/issues/4283), thanks @MakeNowJust)\n- Added support for OpenSSL 1.1.0 (See [#4215](https://github.com/crystal-lang/crystal/issues/4215), [#4230](https://github.com/crystal-lang/crystal/issues/4230), thanks @ysbaddaden)\n- Added `SecureRandom#random_bytes(Bytes)` (See [#4191](https://github.com/crystal-lang/crystal/issues/4191), thanks @konovod)\n- Added setting and deleting of attributes on `XML::Node` (See [#3902](https://github.com/crystal-lang/crystal/issues/3902), thanks @bmmcginty)\n- Added `File.touch` and `FileUtils.touch` methods (See [#4069](https://github.com/crystal-lang/crystal/issues/4069), thanks @Sija)\n- Added `#values_at` for `CSV` (See [#4157](https://github.com/crystal-lang/crystal/issues/4157), thanks @need47)\n- Added `Time#clone` (See [#4174](https://github.com/crystal-lang/crystal/issues/4174), thanks @Sija)\n- Added `ancestors` macro method (See [#3875](https://github.com/crystal-lang/crystal/issues/3875), thanks @david50407)\n- Added `skip` macro method ([#4237](https://github.com/crystal-lang/crystal/issues/4237), thanks @mverzilli)\n- Added `Colorize.on_tty_only!` for easier toggling (See [#4075](https://github.com/crystal-lang/crystal/issues/4075), [#4271](https://github.com/crystal-lang/crystal/issues/4271), thanks @MakeNowJust)\n- Added `WebSocket#on_binary` to receive binary messages (See [#2774](https://github.com/crystal-lang/crystal/issues/2774), thanks @lbguilherme)\n- Fixed `Iterator.of` stops iterating when `Iterator.stop` is returned (See [#4208](https://github.com/crystal-lang/crystal/issues/4208))\n- Fixed `String#insert` for non-ascii Char (See [#4164](https://github.com/crystal-lang/crystal/issues/4164), thanks @Papierkorb)\n- Fixed `File.link` now creates a hard link ([#4116](https://github.com/crystal-lang/crystal/issues/4116), thanks @KCreate)\n- Fixed error message for `#to_h` over empty `NamedTuple` (See [#4076](https://github.com/crystal-lang/crystal/issues/4076), thanks @karlseguin)\n- Fixed `NamedTuple#to_h` does no longer call to value's `#clone` (See [#4203](https://github.com/crystal-lang/crystal/issues/4203))\n- Fixed `Math#gamma` and `Math#lgamma` (See [#4229](https://github.com/crystal-lang/crystal/issues/4229), thanks @KCreate)\n- Fixed `TCPSocket` creation for 0 port for Mac OSX (See [#4177](https://github.com/crystal-lang/crystal/issues/4177), thanks @will)\n- Fixed repo name extraction from git remote in doc tool (See [#4132](https://github.com/crystal-lang/crystal/issues/4132), thanks @Sija)\n- Fixed `self` resolution when including a generic module (See [#3972](https://github.com/crystal-lang/crystal/issues/3972), thanks @MakeNowJust)\n- Fixed debug information was missing in some cases (See [#4166](https://github.com/crystal-lang/crystal/issues/4166), [#4202](https://github.com/crystal-lang/crystal/issues/4202), [#4254](https://github.com/crystal-lang/crystal/issues/4254))\n- Fixed use generic ARM architecture target triple for all ARM architectures (See [#4167](https://github.com/crystal-lang/crystal/issues/4167), thanks @ysbaddaden)\n- Fixed macro run arguments escaping\n- Fixed zsh completion (See [#4284](https://github.com/crystal-lang/crystal/issues/4284), thanks @veelenga)\n- Fixed honor `--no-color` option in spec (See [#4306](https://github.com/crystal-lang/crystal/issues/4306), thanks @luislavena)\n- [Some bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.22.0)\n\n## 0.21.1 (2017-03-06)\n\n- Improved lookup of abstract def implementors (see [#4052](https://github.com/crystal-lang/crystal/issues/4052))\n- Improved allocation of objects without pointer instance variables using `malloc_atomic` (see [#4081](https://github.com/crystal-lang/crystal/issues/4081))\n- Added `crystal --version` reports also the LLVM version (see [#4095](https://github.com/crystal-lang/crystal/issues/4095), thanks @matiasgarciaisaia)\n- Fixed instance variables initializers corner cases (see [#3988](https://github.com/crystal-lang/crystal/issues/3988))\n- Fixed `crystal play` was broken (see [#4061](https://github.com/crystal-lang/crystal/issues/4061))\n- Fixed `Atomic` can be set to `nil` (see [#4062](https://github.com/crystal-lang/crystal/issues/4062))\n- Fixed `GZip::Header` extra byte (see [#4068](https://github.com/crystal-lang/crystal/issues/4068), thanks @crisward)\n- Fixed `ASTNode#to_s` for `Attribute` (see [#4098](https://github.com/crystal-lang/crystal/issues/4098), thanks @olbat)\n- [Some bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.21.1)\n\n## 0.21.0 (2017-02-20)\n\n- **(breaking-change)** The compiler now reuses previous macro run compilations so `{{ run(...) }}` is only re-run if the code changes\n- **(breaking-change)** Spec: `assert { ... }` is now `it { ... }` (thanks @TheLonelyGhost)\n- **(breaking-change)** Renamed `Set#merge!` to `Set#concat`\n- **(breaking-change)** `Zlib` was split into `Flate`, `Gzip` and `Zlib` ([bda40f](https://github.com/crystal-lang/crystal/commit/bda40f))\n- **(breaking-change)** `Crypto::MD5` is now `Digest::MD5`\n- **(breaking-change)** `String#chop` is now `String#rchop`\n- **(breaking-change)** `String#to_slice` now returns a read-only Slice\n- **(breaking-change)** `String` can now hold invalid UTF-8 byte sequences, and they produce a unicode replacement character when traversed\n- **(breaking-change)** Removed `String#lchomp`. Use `String#lchop`\n- **(breaking-change)** Octal escapes inside strings incorrectly produced a codepoint value instead of a byte value\n- **(breaking-change)** Removed octal escape from char literals\n- Fixed compiler performance regression related to cached files ([f69e37e](https://github.com/crystal-lang/crystal/commit/f69e37e))\n- Added `\\xHH` escape sequence in string literals\n- `Char::Reader` can now traverse a string backwards\n- `Enum#to_s` now uses pipes instead of commas for flag enums\n- `IO#read_string` is now encoding-aware\n- `OAuth2::Client` now sends `application/json` Accept header, and considers the `expires_in` access token property as optional\n- `Slice` can now be read-only\n- `TCPServer` no longer set SO_REUSEPORT to true by default\n- Added `HTTP::Multipart` and `HTTP::FormData` (thanks @RX14)\n- Added `File::Stat#pipe?`\n- Added `File.utime`\n- Added `IO#peek`\n- Added `String#strip(arg)`, `String#lstrip(arg)`, `String#rstrip(arg)`\n- Added `String#lchop`, `String#lchop(prefix)`, `String#rchop` and `String#rchop(suffix)`\n- Added `String#hexbytes` and `String#hexbytes?`\n- Added `String#scrub` and `String#valid_encoding?`\n- Added `include?` macro method for StringLiteral, SymbolLiteral and MacroId (thanks @karlseguin)\n- Added \"view source\" links to GitLab (thanks @ezrast)\n- Updated CONTRIBUTING.md guidelines\n- [Some bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.21.0)\n\n## 0.20.5 (2017-01-20)\n\n- Improved performance in `String#index`, `String#rindex` due to Rabin-Karp algorithm (thanks @MakeNowJust).\n- Improved performance in `Crypto::Bcrypt` (see [#3880](https://github.com/crystal-lang/crystal/issues/3880), thanks @ysbaddaden).\n- `expect_raises` returns raised exception (thanks @kostya).\n- Line numbers debug information is always generated (see [#3831](https://github.com/crystal-lang/crystal/issues/3831), thanks @ysbaddaden).\n- Added `Zip::File`, `Zip::Reader` and `Zip::Writer`. Native readers for zip files that delegate compression to existing zlib module.\n- Added `Hash#delete` with block (see [#3856](https://github.com/crystal-lang/crystal/issues/3856), thanks @bmulvihill).\n- Added `String#[](char : Char)` (see [#3855](https://github.com/crystal-lang/crystal/issues/3855), thanks @Sija).\n- Added `crystal tool expand` to expand macro call in a given location (see [#3732](https://github.com/crystal-lang/crystal/issues/3732), thanks @MakeNowJust).\n- Fixed `crystal play` is able to show compilation errors again.\n- `crystal doc` recognizes `crystal-lang/crystal` in any remote (thanks @MaxLap).\n- [Some bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.20.5)\n\n## 0.20.4 (2017-01-06)\n\n- **(breaking change)** A type that wants to convert itself to JSON now must override `to_json(builder : JSON::Builder)` instead of `to_json(io : IO)`. The same is true for custom JSON converters. If you are using `JSON.mapping` then your code will continue to work without changes.\n- **(breaking change)** Defining a `finalize` method on a struct now gives a compile error\n- **(breaking change)** Default argument types now must match their restriction, if any (for example `def foo(x : Int32 = nil)` will now fail to compile if `foo` is invoked without arguments) (thanks @MakeNowJust)\n- **(breaking change)** `each` methods now return `Nil`\n- **(breaking change)** `IO#skip(bytes)` will now raise if there aren't at least the given amount of bytes in the `IO` (previously it would work well if there were less bytes, and it would hang if there were more)\n- **(breaking change)** `MemoryIO` was removed (use `IO::Memory` instead)\n- **(breaking change)** `Number#step` now requires named arguments, `to` and `by`, to avoid argument order confusion\n- **(breaking change)** `YAML::Emitter` was renamed to `YAML::Builder`, and some of its methods were also renamed\n- **(breaking change)** `XML::Node#[]` now always returns a `String` (previously it could also return `Nil`, which was incorrect)\n- **(breaking change)** `XML::Node#content` now returns an empty `String` when no content is available\n- `HTTP::Client` now automatically reconnects on a dropped keep-alive connection\n- `with ... yield` now works well with `method_missing`\n- Class variables can now be used in generic types (all generic instances share the same variable, and subclasses get their own copy, as usual)\n- Added support for LLVM 4 (thanks @ysbaddaden)\n- Added `Enum.each` and `Enum#each` (thanks @ysbaddaden)\n- Added `Hash#compact` and `Hash#compact!` (thanks @MakeNowJust)\n- Added `IO#read_string(bytesize)`\n- Added `IO#skip_to_end`\n- Added `Iterator#flat_map` (thanks @MakeNowJust)\n- Added `JSON.build` and `JSON::Builder`\n- Added `NamedTuple#has_key?(String)` (thanks @Sija)\n- Added `p(NamedTuple)` (thanks @splattael)\n- Added `Regex::MatchData#==` (thanks @MakeNowJust)\n- Added `String#sub(Regex, NamedTuple)` (thanks @maiha)\n- Added `XML.build` and `XML::Builder`\n- Lots of improvements and applied consistencies to doc comments (thanks @Sija and @maiha)\n- [Some bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.20.4)\n\n### 0.20.3 (2016-12-23)\n\n- **(breaking change)** `IO#gets`, `IO#each_line`, `String#lines`, `String#each_line`, etc. now chomp lines by default. You can pass `chomp: false` to prevent automatic chomping. Note that `chomp` is `true` by default for argless `IO#gets` (read line) but `false` if args are given.\n- **(breaking change)** `HTTP::Handler` is now a module instead of a class (thanks @andrewhamon)\n- **(breaking change)** Free variables now must be specified with `forall`, a single uppercase letter will not work anymore\n- **(breaking change)** The `libs` directory is no longer in the default CRYSTAL_PATH, use `lib` (running `crystal deps` should fix this)\n- Optimized compile times, specially on linux\n- `private` can now be used with macros inside types (thanks @MakeNowJust)\n- CLI: the `-s`/`--stats` option now also shows execution time (thanks @MakeNowJust)\n- CLI: added `-t`/`--time` to show execution time (thanks @MakeNowJust)\n- `Socket` now allows any family/type/protocol association, [and many other improvements](https://github.com/crystal-lang/crystal/pull/3750) (thanks @ysbaddaden)\n- YAML: an `IO` can now be passed to `from_yaml` (thanks @MakeNowJust)\n- Added `class_getter`, `class_setter`, `class_property`, etc. (thanks @Sija)\n- Added `String#lchomp` (thanks @Sija)\n- Added `IO#read_fully?`\n- Added `Iterator#flatten` (thanks @MakeNowJust)\n- Added `HTTP::WebSocket#ping`, `pong`, `on_ping`, `on_pong`, and now a ping message is automatically replied with a pong message (thanks @Sija)\n- Added `File#empty?` and `Dir#empty?` (thanks @dylandrop)\n- Added `Time::Span#/(Time::Span)` (thanks @RX14)\n- Added `String#split` versions that accept a block (thanks @splattael)\n- Added `URI#normalize` and `normalize!` (thanks @taylorfinnell)\n- Added `reuse` optional argument to many `Array`, `Enumerable` and `Iterable` methods that allow you to reuse the yielded/return array for better performance and less memory footprint\n- The `:debug` flag is now present when compiled with `--debug`, useful for doing `flag?(:debug)` in macros (thanks @luislavena)\n- [Many bug fixes and performance improvements](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.20.3)\n\n### 0.20.1 (2016-12-05)\n\n- **(breaking change)** `Set#merge` as renamed to `Set#merge!`\n- **(breaking change)** `Slice.new(size)` no longer works with non primitive integers and floats\n- **(breaking change)** The macro method `argify` was renamed to `splat`\n- Added pretty printing. The methods `p` and `pp` now use it. To get the old behaviour use `puts obj.inspect`\n- Added `ArrayLiteral#[]=`, `TypeNode#constant`, `TypeNode#overrides?` and `HashLiteral#double_splat` in macros\n- Added a `finished` macro hook that runs at the end of the program\n- Added support for declaring the type of a local variable\n- Added `Slice.empty`\n- Flags enums now have a `none?` method\n- `IO::ByteFormat` has now methods to encode/decode to/from a `Slice`\n- Spec: the line number passed to run a specific `it` block can now be inside any line of that block\n- The `CallConvention` attribute can now also be applied to a `lib` declaration, and all `fun`s inside it will inherit it\n- The `method_missing` hook can now define a method, useful for specifying block arguments\n- Support double splat in macros (`{{**...}}`)\n- [Some bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.20.1)\n\n### 0.20.0 (2016-11-22)\n\n- **(breaking change)** Removed `ifdef` from the language\n- **(breaking change)** Removed `PointerIO`\n- **(breaking change)** The `body` property of `HTTP::Request` is now an `IO?` (previously it was `String`). Use `request.body.try(&.gets_to_end)`  if you need the entire body as a String.\n- **(breaking change)** `MemoryIO` has been renamed to `IO::Memory`. The old name can still be used but will produce a compile-time warning. `MemoryIO` will be removed immediately after 0.20.0.\n- **(breaking change)** `Char#digit?` was split into `Char#ascii_number?` and `Char#number?`. The old name is still available and will produce a compile-time warning, but will be removed immediately after 0.20.0.\n- **(breaking change)** `Char#alpha?` was split into `Char#ascii_letter?` and `Char#letter?`. The old name is still available and will produce a compile-time warning, but will be removed immediately after 0.20.0.\n- **(breaking change)** The `Iterable` module is now generic\n- Many `String` and `Char` methods are now unicode-aware, for example `String#downcase`, `String#upcase`, `Char#downcase`, `Char#upcase`, `Char#whitespace?`, etc.\n- Added support for HTTP client and server streaming.\n- Added support for ARM (thanks @ysbaddaden)\n- Added support for AArch64 (thanks @ysbaddaden)\n- Added support for LLVM 3.9 (thanks @ysbaddaden)\n- Added `__END_LINE__` magic constant in method default arguments: will be the last line of a call (if the call has a block, it will be the last line of that block)\n- Added `@def` inside macros that takes the value of the current method\n- API docs have a nicer style now, and notes like TODO and DEPRECATED are better highlighted (thanks @samueleaton)\n- Slight improvement to debugging support (thanks @ggiraldez)\n- Line numbers in backtraces (linux only for now) (thanks @ysbaddaden)\n- Added iteration times to `Benchmark.ips` (thanks @RX14)\n- Allow `HTTP::Client` block initializer to be used when passing an URI (thanks @philnash)\n- `JSON.mapping` and `YAML.mapping` getter/setter generation can now be controlled (thanks @zatherz)\n- `Time` is now serializable to JSON and YAML using ISO 8601 date-time format\n- Added `IO::MultiWriter` (thanks @RX14)\n- Added `String#index(Regex)` and `String#rindex(Regex)` (thanks @zatherz)\n- Added `String#partition` and `String#rpartition` (thanks @johnjansen)\n- Added `FileUtils.cd`, `FileUtils.mkdir`, `FileUtils.mkdir_p`, `FileUtils.mv`, `FileUtils.pwd`, `FileUtils.rm`, `FileUtils.rm_r`, `FileUtils.rmdir` (thanks @ghivert)\n- Added `JSON::Builder#raw_field` (thanks @kostya)\n- Added `Enumerable#chunks` and `Iterator#chunk` (thanks @kostya)\n- Added `Iterator#with_index`\n- Several enhancements to the Random module: now works for any integer type and avoids overflows (thanks @BlaXpirit)\n- Optimized `Array#sort` by using introsort (thanks @c910335)\n- [Several bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.20.0)\n\n### 0.19.4  (2016-10-07)\n\n- Added support for OpenBSD (thanks @wmoxam and @ysbaddaden)\n- More iconv fixes for FreeBSD (thanks @ysbaddaden)\n- Changed how `require` works for the upcoming `shards` release (this is backwards compatible). See https://github.com/crystal-lang/crystal/pull/2788\n- Added `Atomic` and exposed all LLVM atomic instructions to Crystal (needed to implemented multiple-thread support)\n- Added `Process.executable_path` (thanks @kostya, @whereami and @ysbaddaden)\n- Added `HTML.unescape` (thanks @dukex)\n- Added `Char#+(Int)` and `Char#-(Int)`\n- [A few bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.19.4)\n\n### 0.19.3  (2016-09-30)\n\n- `crystal eval` now accepts some flags like `--stats`, `--release` and `--help`\n- Added `File.chown` and `File.chmod` (thanks @ysbaddaden)\n- Added `Time::Span.zero` (useful for doing `sum`) (thanks @RX14)\n- Added docs to `OAuth` and `OAuth2`\n- [Several bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.19.3)\n\n### 0.19.2  (2016-09-16)\n\n- Generic type variables no longer need to be single-letter names (for example `class Gen(Foo)` is now possible)\n- Added syntax to denote free variables: `def foo(x : T) forall T`. The old rule of single-letter name still applies but will be removed in the future.\n- Removed the restriction that top-level types and constants can't have single-letter names\n- Added `@[Extern]` attribute to mark regular Crystal structs as being able to be used in C bindings\n- Faster `Char#to_s` when it's ASCII: this improves the performance of JSON and CSV parsing\n- `crystal spec`: allow passing `--release` and other options\n- `crystal spec`: allow running all specs in a given directory\n- `crystal playground`: support custom workbook resources (thanks @bcardiff)\n- `crystal playground`: standard output now understands ANSI colors (thanks @bcardiff)\n- Added many more macro methods to traverse AST nodes (thanks @BlaXpirit)\n- Error messages no longer include a type trace by default, pass `--error-trace` to show the full trace (the trace is often useless and makes it harder to understand error messages)\n- [Several bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.19.2)\n\n### 0.19.1  (2016-09-09)\n\n- Types (class, module, etc.) can now be marked as `private`.\n- Added `WeakRef`  (thanks @bcardiff)\n- [Several bug fixes](https://github.com/crystal-lang/crystal/issues?q=is%3Aclosed+milestone%3A0.19.1)\n\n### 0.19.0  (2016-09-02)\n\n- **(breaking change)** Added `select` keyword\n- **(breaking change)** Removed $global variables. Use @@class variables instead.\n- **(breaking change)** Heredoc now ends when the matching identifier is found, either followed by a space or by a non-identifier\n- **(breaking change)** Assignment to a local variable inside an assignment to that same variable is now an error\n- **(breaking change)** Type names like `T`, `T1`, `U`, etc., are now disallowed at the top level, to avoid conflicts with free variables\n- **(breaking change)** Type lookup (`Foo::Bar::Baz`) had some incorrect behaviour that now is fixed. This can break existing code that relied on this incorrect behaviour. The fix is to fully qualify types (`::Foo::Bar::Baz`)\n- **(breaking change)** In relationships like `class Bar < Foo(Baz)` and `include Moo(Baz)`, all of `Foo`, `Moo` and `Baz` must be defined before that point (this was not always the case in previous versions)\n- **(breaking change)** Removed the deprecated syntax `x as T`\n- **(breaking change)** Removed block form of `String#match`\n- **(breaking change)** Removed `IO#read_nonblock`\n- **(breaking change)** `Int#/` now performs floored division. Use `Int#tdiv` for truncated division (see their docs to learn the difference)\n- Added support for LLVM 3.8 (thanks @omarroth)\n- `||` now does type filtering\n- Generic inheritance should now work well, and (instantiated) generic modules can now be used as the type of instance variables\n- `NamedTuple` can now be accessed with strings too (thanks @jhass)\n- `Base64` can now encode and decode directly to an `IO` (thanks @kostya)\n- `BigInt` now uses GMP implementation of gcd and lcm (thanks @endSly)\n- `ECR` now supports removing leading and trailing whitespace (`<%-`, `-%>`)\n- `HTTP::Request#path` now never returns `nil`: it fallbacks to `\"/\"` (thanks @jhass)\n- `String#tr(..., \"\")` is now the same as `String#delete`\n- `tool hierarchy` now supports `--format json` (thanks @bmulvihill)\n- Added `Char#ascii?`\n- Added `Class#nilable?` and `Union#nilable?`\n- Added `Hash#has_value?` (thanks @kachick)\n- Added `IO::Sized` and `IO::Delimited` (thanks @RX14)\n- Added `IO::Hexdump` (thanks @ysbaddaden)\n- Added `IO#noecho` and `IO#noecho!` (thanks @jhass)\n- Added `Logger.new(nil)` to create a null logger\n- Added `OptionParser#missing_option` and `OptionParser#invalid_option` (thanks @jhass)\n- Added `Process.exists?`, `Process#exists?` and `Process#terminated?` (thanks @jhass)\n- Added `Process.exec` (thanks @jhass)\n- Added `Slice#copy_to`, `Slice#copy_from`, `Slice#move_to` and `Slice#move_from` (thanks @RX14)\n- Added `URI#==` and `URI#hash` (thanks @timcraft)\n- Added `YAML#parse(IO)`\n- Added `Indexable` module that `Array`, `Slice`, `Tuple` and `StaticArray` include\n- Added `indent` parameter to `to_pretty_json`\n- Added lazy form of `getter` and `property` macros\n- Added macro methods to access an ASTNode's location\n- Unified String and Char to integer/float conversion API (thanks @jhass)\n- [Lots of bug fixes](https://github.com/crystal-lang/crystal/milestone/5?closed=1)\n\n### 0.18.7 (2016-07-03)\n\n- The `compile` command was renamed back to `build`. The `compile` command is deprecated and will be removed in a future version\n- Fibers now can be spawned with a name\n- ECR macros can now be required with just `require \"ecr\"`\n- [Several bugs fixes and enhancements](https://github.com/crystal-lang/crystal/issues?q=milestone%3A0.18.7+is%3Aclosed)\n\n### 0.18.6 (2016-06-28)\n\n- `T?` is now parsed as `Union(T, Nil)` outside the type grammar\n- Added `String#sub` overloads for replacing an index or range with a char or string\n- [Several bugs fixes](https://github.com/crystal-lang/crystal/issues?q=milestone%3A0.18.6+is%3Aclosed)\n\n### 0.18.5 (2016-06-27)\n\n- Added `OpenSSL::SSL::Socket#alpn_protocol`\n- Added `IO#copy(src, desc, limit)` (thanks @jreinert)\n- Added `TypeNode#instance` macro method\n- [Several bugs fixes](https://github.com/crystal-lang/crystal/issues?q=milestone%3A0.18.5+is%3Aclosed)\n\n### 0.18.4 (2016-06-21)\n\n- Fixed [#2887](https://github.com/crystal-lang/crystal/issues/2887)\n- Fix broken specs\n\n### 0.18.3 (2016-06-21)\n\n- `TypeNode`: added `<`, `<=`, `>` and `>=` macro methods\n- [Several bugs fixes](https://github.com/crystal-lang/crystal/issues?q=milestone%3A0.18.3+is%3Aclosed)\n\n### 0.18.2 (2016-06-16)\n\n- Fixed building Crystal from the source tarball\n\n### 0.18.1 (2016-06-16)\n\n- Spec: passing `--profile` shows the slowest 10 specs (thanks @mperham)\n- Added `StringLiteral#>` and `StringLiteral#<` in macros\n- [Several bugs fixes](https://github.com/crystal-lang/crystal/issues?q=milestone%3A0.18.1+is%3Aclosed)\n\n### 0.18.0 (2016-06-14)\n\n- **(breaking change)** `IniFile` was renamed to `INI`, and its method `load` renamed to `parse`\n- **(breaking change)** `Process.getpgid` was renamed to `Process.pgid`\n- **(breaking change)** An `Exception`'s backtrace is now set when it's raised, not when it's created: it's `backtrace` method raises if it's not set, and there's `backtrace?` to get it as a nilable array\n- **(breaking change)** `dup` is now correctly implemented in all types. `clone` is not defined by default, but some types in the standard library do. Also check `Object#def_clone`\n- **(breaking change)** the `method_missing` macro only accepts a single argument: a `Call` now. The form that accepted 3 arguments was removed.\n- **(breaking change)** the `delegate` macro must now be used like `delegate method1, method2, ..., methodN, to: object`\n- **(breaking change)** `Hash#each_with_index` and `Hash#each_with_object` now yield a tuple (pair) and an index, because `Hash` is now `Enumerable`. Use `do |(key, value), index|` for this.\n- **(breaking change)** `{\"foo\": 1}` denotes a named tuple literal now, not a hash literal. Use `{\"foo\" => 1}` instead. This also applies to, for example `HTTP::Headers{...}`\n- **(breaking change)** Extra block arguments now give a compile-time error. This means that methods that yield more than once, one time with N arguments and another time with M arguments, with N < M, will always give an error. To fix this, add M - N `nil` fillers on the yield side (this makes it more explicit that `nil` was intended to be a block argument value)\n- **(breaking change)** `OpenSSL::SSL::Context` and `OpenSSL::SSL::Socket` can no longer be used directly anymore. Use their respective subclasses `OpenSSL::SSL::Context::Client`,\n  with `OpenSSL::SSL::Socket::Client`, `OpenSSL::SSL::Context::Server` with `OpenSSL::SSL::Socket::Server`.\n- **(breaking change)** TLS server and client sockets now use sane defaults, including support for hostname verification for client sockets, used by default in `HTTP::Client`.\n- **(breaking change)** The `ssl` option was renamed to `tls` in `HTTP::Client`, `HTTP::Server`, `HTTP::WebSocket`, `OAuth::Consumer`, `OAuth::Signature` and `OAuth2::AccessToken`.\n- The `dns_timeout` setting in a few classes like `HTTP::Client` and `TCPSocket` is now ignored until a next version supports a non-blocking `getaddrinfo` equivalent\n- `OpenSSL::SSL::Socket::Client` supports server name indication now.\n- The `build` command was renamed to `compile`. The `build` command is deprecated and will be removed in a future version\n- The `--cross-compile` flag no longer takes arguments, use `--target` and `-D`\n- Added a `Union` type that represents the type of a union, which can have class methods\n- Methods, procs and lib functions that are marked as returning `Void` now return `Nil`\n- Methods that are marked as returning `Nil` are not checked for a correct return type, they always return `nil` now\n- When `as` fails at runtime it now includes which type couldn't be cast\n- Macros can now be used inside `lib` and `enum` declarations\n- Macros can now be declared inside enums\n- Macro calls can now be used as enum values\n- Generic types can now include a splatted type variable. This already existed in the language (`Tuple(*T)`, `Proc(*T)`) but there was no syntax to define such types.\n- Class variables are now inherited (only their type, not their value). They are now similar to Ruby class instance variables.\n- Splats in `yield` can now be used\n- Splat in block arguments can now be used.\n- Added block auto-unpacking: if a method yields a tuple and a block specifies more then one block argument, the tuple is unpacked to these arguments\n- String literals are now allowed as external method arguments, to match named tuples and named arguments\n- `sizeof` and `instance_sizeof` can now be used as generic type arguments (mostly useful combined with `StaticArray`)\n- `Hash`, `HTTP::Headers`, `HTTP::Params` and `ENV` now include the `Enumerable` module\n- `Proc` is now `Proc(*T, R)`\n- `Tuple(*T).new` and `NamedTuple(**T).new` now correctly match the given `T` ([#1828](https://github.com/crystal-lang/crystal/issues/1828))\n- `Float64#to_s` now produces an ever more accurate output\n- `JSON` parsing now correctly handle floats with many digits\n- `JSON.mapping` and `YAML.mapping` now also accept named arguments in addition to a hash literal or named tuple literal\n- `Int#chr` now raises if the integer is out of a char's range. The old non-raising behaviour is now in `Int#unsafe_chr`.\n- The output of `pp x` is now `x # => ...` instead of `x = ...`\n- The output of the `debug()` macro method now tries to format the code (pass `false` to disable this)\n- Added `JSON` and `YAML` parsing and mapping for unions\n- Added `FileUtils.cp_r` (thanks @Dreauw)\n- Added `Tuple.from` and `NamedTuple.from`  (thanks @jhass)\n- Added `XML.escape` (thanks @juanedi)\n- Added `HTTP::Server::Response#respond_with_error` (thanks @jhass)\n- Added `TCPServer#accept?`\n- Added optional `base` argument to `Char#digit?` and `Char#hex?` (thanks @mirek)\n- Added `flag?` macro method, similar to using `ifdef`. `ifdef` is deprecated and will be removed in a future version.\n- Added `YAML::PullParser#read_raw`\n- Added `Proc#partial`\n- Added `Socket.ip?(str)` to validate IPv4 and IPv6 addresses\n- Added `Bytes` as an alias of `Slice(UInt8)`\n- Added `RangeLiteral` macro methods: `begin`, `end`, `excludes_end?`, `map` and `to_a`\n- Added `ArrayLiteral#[range]` and `ArrayLiteral#[from, to]` in macros (applicable for `TupleLiteral` too)\n- Added `Generic` macro methods: `name`, `type_vars`, `named_args`\n- Spec: added JUnit formatter output (thanks @juanedi)\n- The `tls` option in `HTTP::Client` can now take a `OpenSSL::SSL::Context::Client` in addition to `true`.\n- `HTTP::LogHandler` logs exceptions now (thanks @jhass)\n- `HTTP::ErrorHandler` does not tell the client which exception occurred by default (can be enabled with a `verbose` flag) (thanks @jhass)\n- Several bug fixes\n\n### 0.17.4 (2016-05-26)\n\n- Added string literals without interpolations nor escapes: `%q{...}` and `<<-'HEREDOC'`. Also added `%Q{...}` with the same meaning as `%{...}`.\n- A method that uses `@type` inside a macro expression is now automatically detected as being a `macro def`\n- `Float64#to_s` now produces a more accurate output\n- Added `Crystal::VERSION` and other compiler-metadata constants\n- Added `Object.from_json(string_or_io, root)` and a `root` option to `JSON.mapping`\n- Added `System.hostname` (thanks @miketheman)\n- The `property`, `getter` and `setter` macros now also accept assignments (`property x = 0`)\n- The `record` macro now also accepts assignments (`record Point, x = 0, y = 0`)\n- Comparison in macros between `MacroId` and `StringLiteral` or `SymbolLiteral` now work as expected (compares the `id` representation)\n- Some bug fixes\n\n### 0.17.3 (2016-05-20)\n\n- Fixed: multiple macro runs executions didn't work well ([#2624](https://github.com/crystal-lang/crystal/issues/2624))\n- Fixed incorrect formatting of underscore in unpacked block arguments\n- Fixed wrong codegen for global variable assignment in type declaration ([#2619](https://github.com/crystal-lang/crystal/issues/2619))\n- Fixed initialize default arguments where evaluated at the class scope ([#731](https://github.com/crystal-lang/crystal/issues/731))\n- The type guesser can now infer a block type from `def initialize(&@block)`\n- Allow type restriction in double splat argument (similar to restriction in single splat)\n- Allow splat restriction in splat argument (useful for `Tuple.new`)\n- Allow double splat restriction in double splat argument (useful for `NamedTuple.new`)\n\n### 0.17.2 (2016-05-18)\n\n- Fixed crash when using pointerof of constant\n\n### 0.17.1 (2016-05-18)\n\n- Constants and class vars are no longer initialized before \"main\". Now their initialization order goes along with \"main\", similar to how it works in Ruby (much more intuitive)\n- Added syntax for unpacking block arguments: `foo { |(x, y)| ... }`\n- Added `NamedTupleLiteral#map` and `HashLiteral#map` in macros (thanks @jhass)\n- Fixed wrong codegen for tuples/named tuples merge with pass-by-value types\n- Formatter: fixed incorrect format for named tuple type\n\n### 0.17.0 (2016-05-17)\n\n- **(breaking change)** Macro defs are now parsed like regular methods. Enclose the body with `{% begin %} .. {% end %}` if you needed that behaviour\n- **(breaking change)** A union of two tuples of the same size results in a tuple with the unions of the types in each position. This only affects code that later tested a tuple's type with `is_a?`, for example `tuple.is_a?({Int32, String})`\n- **(breaking change)** Method arguments have now a different semantic. This only affects methods that had a splat argument followed by other arguments.\n- **(breaking change)** The syntax `{foo: 1, bar: 2}` now denotes a `NamedTuple`, not a `Hash` with symbol as keys. Use `{:foo => 1, :bar => 2}` instead\n- The syntax `exp as Type` is now deprecated and will be removed in the next version. Use `crystal tool format` to automatically upgrade your code\n- The compiler now gives an error when trying to define a method named `!`, `is_a?`, `responds_to?`, `nil?`, `as` or `as?`\n- Added the `NamedTuple` type\n- Added double splatting\n- Added external argument names\n- Macro defs return type is no longer mandatory\n- Added `as?`: similar to `as`, but returns `nil` when the type doesn't match\n- Added `Number::Primitive` alias\n- Added `Tuple#+(Tuple)`\n- Added `ArrayLiteral#+(ArrayLiteral)` in macros\n- `Crypto::MD5` now allows `Slice(UInt8)` and a block form (thanks @will)\n- Added docs for XML (thanks @Hamdiakoguz)\n- Many bug fixes\n\n### 0.16.0 (2016-05-05)\n\n- **(breaking change)** Instance, class and global variables types must be told to the compiler, [either explicitly or through a series of syntactic rules](http://crystal-lang.org/docs/syntax_and_semantics/type_inference.html)\n- **(breaking change)** Non-abstract structs cannot be inherited anymore (abstract structs can), check the [docs](http://crystal-lang.org/docs/syntax_and_semantics/structs.html) to know why. In many cases you can use modules instead.\n- **(breaking change)** Class variables are now initialized at the beginning of the program (before \"main\"), make sure to read the docs about [class variables](http://crystal-lang.org/docs/syntax_and_semantics/class_variables.html) and [main](http://crystal-lang.org/docs/syntax_and_semantics/the_program.html)\n- **(breaking change)** Constants are now initialized at the beginning of the program (before \"main\"), make sure to read the docs about [constants](http://crystal-lang.org/docs/syntax_and_semantics/constants.html) and [main](http://crystal-lang.org/docs/syntax_and_semantics/the_program.html)\n- **(breaking change)** When doing `crystal program.cr arg1 arg2 arg3`, `arg1`, `arg2` and `arg3` are considered arguments to pass to the program (not the compiler). Use `crystal run program.cr arg1 ...` to consider `arg1` a file to include in the compilation.\n- **(breaking change)** `Int#**(Int)` now returns an integer, and raises if the argument is negative. Use a float base or exponent for negative exponents to work.\n- **(breaking change)** `Slice#to_s` and `StaticArray#to_s` now include their type name in the output\n- Support for FreeBSD and musl libc has landed (thanks @ysbaddaden)\n- The `.crystal` directory is now created at `$HOME/.cache/crystal` or `$HOME/.crystal` (or others similar), with a fallback to the current directory\n- `crystal doc` and `crystal tool hierarchy` are now much faster. Additionally, the hierarchy tool shows types for generic types, and doesn't show instantiations anymore (wasn't very useful)\n- `!` now does type filtering (for example you can do `!x || x.bar`, assuming `x` can be `nil` and the non-nil type responds to `bar`)\n- Named arguments can now match any argument, even if they don't have a default value. Make sure to read the [docs](http://crystal-lang.org/docs/syntax_and_semantics/default_and_named_arguments.html)\n- The `as` operator can now be written as a method: `exp.as(Type)` in addition to `exp as Type`. The old syntax will be removed in a few releases.\n- Added `@x : Int32 = 1` syntax (declaration + initialization)\n- `new`/`initialize` logic now works more as one would expect\n- Added `BigRational` (thanks @will)\n- Added `BigFloat` (thanks @Exilor)\n- Added `String#insert`\n- Added `Time::EpochConverter` and `Time::EpochMillisConverter`\n- Added `%s` (unix epoch) directive to `Time::Format`\n- `Time` now honours Dayling Saving and `ENV[\"TZ\"]`\n- Added `HTTP::Server::Response#cookies` (thanks @jhass)\n- Added `Array#bsearch`, `Array#bsearch_index` and `Range#bsearch` (thanks @MakeNowJust)\n- Added `Range#reverse_each` iterator (thanks @omninonsense)\n- `JSON::Any`: added `as_...?` methods (thanks @DougEverly)\n- `JSON::Any` is now `Enumerable`\n- `YAML::Any` is now `Enumerable`\n- Added `JSON.parse_raw` that returns a `JSON::Type`\n- `JSON::PullParser`: added `#read_raw` to read a JSON value as a raw string (useful for delayed parsing). Also added `String::RawConverter` to be used with `JSON.mapping`.\n- `JSON` and `YAML`: enums, `BigInt` and `BigFloat` are now serializable\n- `ENV`: allow passing `nil` as a value to delete an environment variable\n- `Hash`: allow `Array | Tuple` arguments for `#select`, `#select!`, `#reject` and `#reject!`\n- `Crypto::Subtle.constant_time_compare` now returns `Bool`, and it can compare two strings in addition to two slices (thanks @skunkworker)\n- `HTTP::Server`: reset port zero after listening (thanks @splattael)\n- Added `File#each_line` iterator\n- Added `Number.slice`, `Number.static_array`, `Slice.[]` and `StaticArray.[]` to easily create slices and static arrays\n- Added `Slice#hexdump` (thanks @will)\n- Added `Enumerable#product` (thanks @dkhofer)\n- Fix: disallow using `out` with `Void*` pointers\n- Fixed bug in `XML::Node#namespace_scopes` (thanks @Hamdiakoguz)\n- Added docs for `INIFile` (thanks @EvanHahn)\n- Lots of bug fixes\n\n### 0.15.0 (2016-03-31)\n\n- **(breaking change)** `!` has now its meaning hardcoded in the language. If you defined it for a type it won't be invoked as a method anymore.\n- **(breaking change)** `nil?` has now its meaning hardcoded in the language. If you defined it for a type it won't be invoked as a method anymore.\n- **(breaking change)** `typeof` is now disallowed in `alias` declarations\n- Added `crystal tool format --check` to check that source code is properly formatted\n- `crystal play` (playground) added workbooks support, as well as improvements and stabilizations\n- Added `Tempfile.dirname` (thanks @DougEverly)\n- Added `Path#resolve` method in macros\n- `{{...}}` arguments to a macro call are now expanded before macro invocation ([#2392](https://github.com/crystal-lang/crystal/issues/2392))\n- Special variables (`$~` and `$?`) are now accessible after being defined in blocks ([#2194](https://github.com/crystal-lang/crystal/issues/2194))\n- Some bugs and regressions fixed\n\n### 0.14.2 (2016-03-22)\n\n- Fixed regression with formatter ([#2348](https://github.com/crystal-lang/crystal/issues/2348))\n- Fixed regression with block return types ([#2347](https://github.com/crystal-lang/crystal/issues/2347))\n- Fixed regression with openssl (https://github.com/crystal-lang/crystal/commit/78c12caf2366b01f949046e78ad4dab65d0d80d4)\n\n### 0.14.1 (2016-03-21)\n\n- Fixed some regressions in the formatter\n\n### 0.14.0 (2016-03-21)\n\n- **(breaking change)** The syntax of a method argument with a default value and a type restriction is now `def foo(arg : Type = default_value)`. The old `def foo(arg = default_value : Type)` was removed.\n- **(breaking change)** `Enumerable#take(n)` and `Iterator#take(n)` were renamed to `first(n)`\n- **(breaking change)** `Socket#addr` and `Socket#peeraddr` were renamed to `local_address` and `remote_address` respectively\n- **(breaking change)** Removed `Comparable#between?(a, z)`. Use `a <= x <= z` instead\n- **(breaking change)** `HTTP::WebSocketHandler` callbacks can now access the `HTTP::Context`. If you had a forwarding method to it you'll need to update it. See [#2313](https://github.com/crystal-lang/crystal/issues/2313).\n- New command `crystal play` that opens a playground for you to play in the browser :-) (thanks @bcardiff)\n- New command `crystal env` that prints environment information\n- `Spec`: you can now run multiple files with specified line numbers, as in `crystal spec file1.cr:10 file2.cr:20 ...`\n- Initial support for musl-libc (thanks @ysbaddaden)\n- Added `FileUtils.cp` (thanks @Dreauw)\n- Added `Array#first(n)` and `Array#last(n)` (thanks @greyblake)\n- Added `WebSocket#close` and properly handle disconnections\n- Added `UDPSocket#send` and `UDPSocket#receive` (thanks @tatey)\n- Added `Char#uppercase?` and `Char#lowercase?` (thanks @MaloJaffre`)\n- Added `sync_close` property to `OpenSSL::SSL::Socket`, `Zlib::Inflate` and `Zlib::Deflate`\n- Added `XML::Node#encoding` and `XML::Node#version`\n- Added `HTTP::Client::Response#success?` (thanks @marceloboeira)\n- Added `StaticArray#shuffle!(random)` (thanks @Nesqwik)\n- Added `Splat#exp` method in macros\n- Added fiber-safe `Mutex`\n- All `Int` types (except `BigInt`) can now be used in `JSON` and `YAML` mappings (thanks @marceloboeira)\n- Instance variable declarations/initializations now correctly work in generic classes and modules\n- Lots of bug fixes\n\n### 0.13.0 (2016-03-07)\n\n- **(breaking change)** `Matrix` was moved to a separate shard: [https://github.com/Exilor/matrix](https://github.com/Exilor/Matrix)\n- The syntax of a method argument with a default value and a type restriction is now `def foo(arg : Type = default_value)`. Run `crystal tool format` to automatically upgrade existing code to this new syntax. The old `def foo(arg = default_value : Type)` syntax will be removed in a next release.\n- Special handling of `case` with a tuple literal. See [#2258](https://github.com/crystal-lang/crystal/pull/2258).\n- Keywords can now be used for variable declaration, so `property end : Time` works as expected.\n- Comparison of signed vs. unsigned integers now always give a correct result\n- Allow declaring instance variables in non-generic module types (`module Moo; @x : Int32; end`)\n- Allow initializing instance variables in non-generic module types (`module Moo; @x = 1; end`)\n- `Spec`: allow setting multiple output formatters (thanks @marceloboeira)\n- `StringScanner`: improved performance\n- Added `foo.[0] = 1` and `foo.[0]` as valid syntax, similar to the one in `&.` blocks (thanks @MakeNowJust)\n- `CSV`: allow separate and quote characters different than comma and double quote (thanks @jreinert)\n- `YAML`: support merge operator (`<<`) (thanks @jreinert)\n- Allow redefining primitive methods like `Int32#+(other : Int32)`\n- Allow defining macros with operator names like `[]`\n- `Levenshtein`: improved performance (thanks @tcrouch)\n- `HTTP::Client`: fixed incorrect parsing of chunked body\n- `HTTP::Client`: added a constructor with an `URI` argument (thanks @plukevdh)\n- `String`: `sub` and `gsub` now understand backreferences (thanks @bjmllr)\n- `Random`: added `Random#rand(Float64)` and `Random#rand(Range(Float, Float))` (thanks @AlexWayfer)\n- `HTML`: `HTML.escape` includes more characters (thanks @Ryuuzakis)\n- Added `TypeNode.class` method in macros (thanks @waterlink)\n- `run` inside macros now also work with absolute paths (useful when used with `__DIR__`)\n- Added docs for `Math` and `StaticArray` (thanks @Zavydiel, @HeleneMyr)\n- Many bug fixes and some micro-optimizations\n\n### 0.12.0 (2016-02-16)\n\n- **(breaking change)** When used with a type declaration, the macros `property`, `getter`, `setter`, etc., declare instance variables with those types.\n- **(breaking change)** `JSON.mapping` and `YAML.mapping` declare instance variables with the given types.\n- **(breaking change)** `YAML.load` was renamed to `YAML.parse`, and it now returns a `YAML::Any`.\n- **(breaking change)** `embed_ecr` and `ecr_file` were renamed to `ECR.embed` and `ECR.def_to_s` (the old methods now produce a warning and will be removed in the next release).\n- Added encoding support: `IO#set_encoding`, `String#encode`, and `HTTP::Client` charset check.\n- Segmentation faults are now trapped and shown in a more friendlier way.\n- The `record` macro can now accept type declarations (for example `record Point, x : Int32, y : Int32`)\n- Added `Iterator#step` (thanks @jhass)\n- `Array#push` and `Array#unshift` can now accept multiple values and add the elements in an efficient way (thanks @arktisklada)\n- Added `default` option to `JSON.mapping` (thanks @kostya)\n- Added `default` option to `YAML.mapping` (thanks @jreinert)\n- Allow doing `case foo; when Foo.class` (and `Foo(T)` and `Foo(T).class`) in case expressions.\n- Added `Class#|` so a union type can be expresses as `Int32 | Char` in regular code.\n- Added `File.real_path` (thanks @jreinert)\n- Added `dns_timeout` for `HTTP::Client` (thanks @kostya)\n- Added dynamic width precision to `sprintf` (thanks @gtramontina)\n- `Markdown` now supports blockquotes and 1 level of list nesting (thanks @SebastianSzturo)\n- `p` now accepts multiple arguments\n- Many bug fixes and some optimizations\n\n### 0.11.1 (2016-01-25)\n\n- Fixed [#2050](https://github.com/crystal-lang/crystal/issues/2050), [#2054](https://github.com/crystal-lang/crystal/issues/2054), [#2057](https://github.com/crystal-lang/crystal/issues/2057), [#2059](https://github.com/crystal-lang/crystal/issues/2059), [#2064](https://github.com/crystal-lang/crystal/issues/2064)\n- Fixed bug: HTTP::Server::Response headers weren't cleared after each request\n- Formatter would incorrectly change `property x :: Int32` to `property x = uninitialized Int32`\n\n### 0.11.0 (2016-01-23)\n\n- **(breaking change)** Syntax for type declarations changed from `var :: Type` to `var : Type`. The old syntax is still allowed but will be deprecated in the next version (run `crystal tool format` to automatically fix this)\n- **(breaking change)** Syntax for uninitialized variables, which used to be `var :: Type`, is now `var = uninitialized Type`. The old syntax is still allowed but will be deprecated in the next version (run `crystal tool format` to automatically fix this)\n- **(breaking change)** `HTTP::Server` refactor to support streaming. Check the [docs](http://crystal-lang.org/api/HTTP/Server.html) of `HTTP::Server` for upgrade instructions\n- **(breaking change)** Renamed `HTTP::WebSocketSession` to `HTTP::WebSocket`.\n- **(breaking change)** Heredocs now remove indentations according to the indentation of the closing identifier (thanks @rhysd)\n- **(breaking change)** Renamed `Enumerable#inject` to `Enumerable#reduce`\n- **(breaking change)** `next` and `return` semantic inside captured block has been swapped ([#420](https://github.com/crystal-lang/crystal/issues/420))\n- Fibers context switch is now faster, done with inline assembly. `libpcl` is no longer used\n- Allow annotating the type of class and global variables\n- Support comments in ECR (thanks @ysbaddaden)\n- Security improvements to `HTTP::StaticFileHandler` (thanks @MakeNowJust)\n- Moved `seek`, `tell`, `pos` and `pos=` from `File` to `IO::FileDescriptor` (affects `Tempfile`)\n- `URI.parse` is now faster (thanks @will)\n- Many bug fixes, some really old ones involving issues with order of declaration\n\n### 0.10.2 (2016-01-13)\n\n- Fixed Directory Traversal Vulnerability in HTTP::StaticFileHandler (thanks @MakeNowJust)\n\n### 0.10.1 (2016-01-08)\n\n- Added `Int#popcount` (thanks @rmosolgo)\n- Added `@[Naked]` attribute for omitting a method's prelude\n- Check that abstract methods are implemented by subtypes\n- Some bug fixes\n\n### 0.10.0 (2015-12-23)\n\n- **(breaking change)** `def` arguments must always be enclosed in parentheses\n- **(breaking change)** A space is now required before and after def return type restriction\n- **(breaking change)** Renamed `Dir.working_dir` to `Dir.current`\n- **(breaking change)** Moved `HTML::Builder` to [its own shard](https://github.com/crystal-lang/html_builder)\n- **(breaking change)** `String#split` now always keeps all results (never drops trailing empty strings)\n- **(breaking change)** Removed `Array#buffer`, `StaticArray#buffer` and `Slice#buffer`. Use `to_unsafe` instead (so unsafe usages are easier to spot)\n- **(breaking change)** Removed `String#cstr`. Use `to_unsafe` instead (so unsafe usages are easier to spot)\n- Optimized Range#sum (thanks @MakeNowJust)\n- Allow forward declarations for lib external vars\n- Added `Int#to_s(base)` for `base = 62` (thanks @jhass)\n- `JSON.parse` now returns `JSON::Any`, which allows traversal of JSON objects with less casts\n- Added `OpenSSL::PKCS5` (thanks @benoist)\n- MemoryIO can now be created to read/write from a Slice(UInt8). In this mode MemoryIO can't be expanded, and can optionally be written. And when creating a MemoryIO from a String, it's non-resizeable and read-only.\n- Added `Object#!~` (the opposite of `=~`)\n- `at_exit` now receives that exit status code in the block (thanks @MakeNowJust)\n- Allow using `Set` in JSON mappings (thanks @benoist)\n- Added `File.executable?`, `File.readable?` and `File.writeable?` (thanks @mverzilli)\n- `Array#sort_by` and `Array#sort_by!` now use a [Schwartzian transform](https://en.wikipedia.org/wiki/Schwartzian_transform) (thanks @radarek)\n- Added `Array#each_permutation`, `Array#each_combination` and `Array#each_repeated_combination` iterators\n- Added optional _random_ argument to `Array#sample` and `Array#shuffle`\n- The `delegate` macro can now delegate multiple methods to an object (thanks @elthariel)\n- Added basic YAML generation (thanks @porras)\n\n### 0.9.1 (2015-10-30)\n\n- Docs search now finds nested entries (thanks @adlerhsieh)\n- Many corrections and changes to the formatter, for better consistency and less obtrusion.\n- Added `OpenSSL::Cipher` and `OpenSSL::Digest` (thanks @benoist)\n- Added `Char#+(String)` (thanks @hangyas)\n- Added `Hash#key` and `Hash#key?` (thanks @adlerhsieh)\n- Added `Time::Span#*` and `Time::Span#/` (thanks @jbaum98)\n- Added `Slice#reverse_each` (thanks @omninonsense)\n- Added docs for `Random` and `Tempfile` (thanks @adlerhsieh)\n- Fixed some bugs.\n\n### 0.9.0 (2015-10-16)\n\n- **(breaking change)** The `CGI` module's functionality has been moved to `URI` and `HTTP::Params`\n- **(breaking change)** `IO#read()` is now `IO#gets_to_end`. Removed `IO#read(count)`, added `IO#skip(count)`\n- **(breaking change)** `json_mapping` is now `JSON.mapping`. `yaml_mapping` is now `YAML.mapping`\n- **(breaking change)** `StringIO` is now `MemoryIO`\n- Added `crystal tool format` that automatically formats your code\n- `protected` methods can now be invoked between types inside a same namespace\n- Removed `curses`, you can use `https://github.com/jreinert/ncurses-crystal`\n- `to_unsafe` and numeric conversions are now also automatically performed in C struct and union fields\n- Added `{% begin %} ... {% end %}` as an alternative to `{% if true %} ... {% end %}`\n- Added `~!` operator\n- Added debug metadata for char, float, bool and enums. Also for classes and structs (experimental)\n- `Dir.glob` now works well with recursive patterns like `**` (thanks @pgkos)\n- Added `read_timeout` and `connect_timeout` to `HTTP::Client` (thanks @benoist)\n- Added `Zlib` (thanks @datanoise and @bcardiff)\n- Added `HTTP::DeflateHandler` (thanks @bcardiff)\n- Added `ENV#fetch` (thanks @tristil)\n- `Hash#new` now accepts an initialize capacity argument\n- `HTTP::Request` provides access and mutation of `query`, `path` and `query_params` (thanks @waterlink)\n- Added `XML::Node#content=` and `#name=`\n- Allow passing handlers and a block to an `HTTP::Server` (thanks @RX14)\n- `crystal init` now tries to use your github username if available (thanks @jreinert)\n- Added `Hash#select`, `Hash#reject` and their bang variant, and `Hash#each_with_object` (thanks @devdazed)\n- Added `Hash#select(*keys)` and `Hash#reject(*keys)` and their bang variant (thanks @sdogruyol)\n- Added `Set#-`, `Set#^`, and `Set#subtract` (thanks @js-ojus)\n- Allow running specs without colors (thanks @rhysd)\n- Added `TypeNode#has_constant?` and `TypeNode#type_vars` in macros (thanks @jreinert)\n- Added `String#compare` that allows case insensitive comparisons\n- Added `File#truncate` (thanks @porras)\n- `CSV` is now a class for iterating rows, optionally with headers access\n- Allow setting multiple `before_request` callbacks to an `HTTP::Client`\n- Added `Dir.cd(&block)` (thanks @rhysd)\n- Added `Class#cast` (thanks @will)\n- Fixes and additions to WebSocket, like the possibility of streaming data (thanks @jreinert)\n- Added `SemanticVersion` class (thanks @technorama)\n- `loop` now yields a counter\n- Added `Array#[]=(index, count, value)` and `Array#[]=(range, value)`\n- Added argless `sleep`\n- `IO#write(slice)` now writes the full slice or raises on error\n- Added some docs for ECR, Markdown, Hash, File, Time, Time::Span, Colorize, String, SecureRandom, YAML (thanks @adlerhsieh, @chdorner, @vjdhama, @rmosolgo)\n- Many bug fixes\n\n### 0.8.0 (2015-09-19)\n\n- **(breaking change)** Renamed a couple of types: `ChannelClosed` -> `Channel::ClosedError`,\n  `UnbufferedChannel` -> `Channel::Unbuffered`, `BufferedChannel` -> `Channel::Buffered`,\n  `DayOfWeek` -> `Time::DayOfWeek`, `MonthSpan` -> `Time::MonthSpan`, `TimeSpan` -> `Time::Span`,\n  `TimeFormat` -> `Time::Format`, `EmptyEnumerable` -> `Enumerable::EmptyError`, `SocketError` -> `Socket::Error`,\n  `MatchData` -> `Regex::MatchData`, `SignedInt` -> `Int::Signed`, `UnsignedInt` -> `Int::Unsigned`,\n  `FileDescriptorIO` -> `IO::FileDescriptor`, `BufferedIO` -> `IO::Buffered`, `CharReader` -> `Char::Reader`,\n  `PointerAppender` -> `Pointer::Appender`.\n- **(breaking change)** All places that raised `DomainError` raise `ArgumentError` now.\n- **(breaking change)** Renamed `Type.cast` to `Type.new` (for example, `Int32.new` instead of `Int32.cast`)\n- **(breaking change)** Removed all macro instance variables except `@type`\n- **(breaking change)** Removed `undef`\n- **(breaking change)** Removed `length()` and `count()` methods from collections. The only method for this is now `size`.\n- **(breaking change)** Removed the ability to invoke methods on a union class\n- Improved debugger support\n- `crystal deps` now delegates to [shards](https://github.com/ysbaddaden/shards). Removed `Projecfile` support.\n- Automatically convert numeric types when invoking C functions\n- Automatically define questions methods for enum members\n- Support quotes inside quoted symbols (thanks @wolflee)\n- Allow marking `initialize` as private\n- Added `method_added` macro hook (thanks @MakeNowJust)\n- Added `ArrayLiteral#includes?(obj)` in macros\n- Added `ASTNode#symbolize` in macros (thanks @kirbyfan64)\n- Added experimental `yaml_mapping`\n- Added nilable variants to `Enumerable#max`, `Enumerable#min`, and others (thanks @technorama)\n- Added `Iterator#flatten` (thanks @jpellerin)\n- Added setting a read timeout to `HTTP::Client` (thanks @benoist)\n- Added `Array#delete_at(index, count)` and `Array#delete_at(range)` (thanks @tebakane)\n- Added `HTTP::Cookies` (thanks @netfeed)\n- Added `Tuple#reverse` (thanks @jhass)\n- Added `Number#clamp` (thanks @technorama)\n- Added several socket options (thanks @technorama)\n- Added `WebSocket.open` (thanks @kumpelblase2)\n- Added `Enum.flags` macro\n- Added support for sending chunked content in HTTP server (thanks @bcardiff)\n- Added `future`, `lazy` and `delay` concurrency methods (thanks @technorama)\n- `fork` now returns a `Process` (thanks @technorama)\n- Documented `Set`, and added a couple of methods (thanks @will)\n- Nicer formatting in `Benchmark.ips`, and interactive mode (thanks @will)\n- The `-f` format output is now honored in compiler errors (thanks @kirbyfan64)\n- Fixed an ambiguity with the `crystal build` command (thanks @MakeNowJust)\n- Cast exceptions now raise `TypeCastError` instead of `Exception` (thanks @will)\n- Many bugs fixes\n\n### 0.7.7 (2015-09-05)\n\n- **(breaking change)** Reimplemented `Process.run` to allow configuring input, output and error, as well as behaving well regarding non-blocking IO (thanks @technorama)\n- **(breaking change)** Removed the `alias_method` macro.\n- **(breaking change)** Disallow declaring defs, classes and other declarations \"dynamically\" (for example inside an `if`... this of course didn't work, but incorrectly compiled).\n- **(breaking change)** `require` is now only allowed at the top-level, never inside other types or methods.\n- **(breaking change)** Removed `Nil#to_i`\n- **(breaking change)** Changed API of `Channel#select` toward a thread-safe one.\n- **(breaking change)** The two methods that IO must implement are now `read(slice : Slice(UInt8))` and `write(slice : Slice(UInt8))`.\n- New beautiful, searchable and more functional API docs. Thanks @rosylilly for the initial design, and @BlaxPirit for some improvements.\n- CLI: Moved `browser`, `hierarchy` and `types` to `crystal tool ...`\n- Added `crystal tool context` and `crystal tool implementations` for IDEs (thanks @bcardiff!!)\n- `Int#>>(amount)` and `Int#<<(amount)` now give zero when `amount` is greater than the number of bits of the integer representation.\n- Added `\\%` escape sequence inside macros.\n- Added aliases for the many C types (thanks @BlaxPirit)\n- Added `Iterator#in_groups_of` (thanks @PragTob)\n- Added optional `offset` argument to `Hash#each_with_index` (thanks @sergey-kucher)\n- Added `Array#combinations`, `Array#each_combination`, `Array#repeated_combinations`, `Array#each_repeated_combination`, `Array#repeated_permutations`, `Array#each_repeated_permutation`, `Array.product` and `Array.each_product` (thanks @kostya)\n- Added `Array#rotate` and `Array#rotate!` (thanks @kostya)\n- Added `MatchData#pre_match` and `MatchData#post_match` (thanks @bjmllr)\n- Added `Array#flatten`\n- Added `Range.reverse_each`, along with `Int#pred` and `Char#pred` (thanks @BlaxPirit)\n- Added `XML.parse_html` (thanks @ryanworl)\n- Added `ENV.keys` and`ENV.values` (thanks @will)\n- Added `StaticArray==(other : StaticArray)` (thanks @tatey)\n- Added `String#sub` in many variants (thanks @jhass)\n- Added `Readline.bind_key`, `Readline.unbind_key`, `Readline.done` and `Readline.done=` (thanks @daphee)\n- Added `Hash#all?`, `Hash#any?` and `Hash#inject` (thanks @jreinert)\n- Added `File#pos` and `File#pos=`\n- Added `Enum.from_value` and `Enum.from_value?`\n- Added `Deque` (thanks @BlaxPirit)\n- Added lots of methods to `StringScanner`, and documented it, making it usable (thanks @will)\n- `StringIO` now quacks like a `File`.\n- Allow sending masked data through a `WebSocket`, and sending long data (thanks @kumpelblase2)\n- `File.new` now accepts an optional `perm` argument (thanks @technorama)\n- `FileDescriptorIO` now has configurable read/write timeouts (thanks @technorama)\n- Signal handling is more robust and allows any kind of code (thanks @technorama)\n- Correctly handle `WebSocket` close packet (thanks @bebac)\n- Correctly implement `seek` and `tell` in buffered IOs (thanks @lbguilherme)\n- Allow setting several options on sockets (thanks @technorama)\n- Some improvements to `crystal init` for the \"app\" case (thanks @krisleech)\n- `sleep` and IO timeouts can receive `TimeSpan` as arguments (thanks @BlaxPirit)\n- Handle `HTTP::Response` without content-length (thanks @lbguilherme)\n- Added docs for OptionParser, ENV, Regex, Enumerable, Iterator and some Array methods (thanks @porras, @will, @bjmllr, @PragTob, @decioferreira)\n- Lots of bug fixes and small improvements\n\n### 0.7.6 (2015-08-13)\n\n- **(breaking change)** removed support for trailing `while`/`until` ([read this](https://github.com/crystal-lang/crystal/wiki/FAQ#why-trailing-whileuntil-is-not-supported-unlike-ruby))\n- **(breaking change)** Renamed `Enumerable#drop` to `Enumerable#skip`\n- **(breaking change)** Renamed `Time.at` to `Time.epoch`, and `Time#to_i` and `Time#to_f` to `Time#epoch` and `Time#epoch_f`\n- **(breaking change)** `inherited` macro now runs before a class' body\n- Renamed `--no-build` flag to `--no-codegen`\n- Allow interpolations in heredocs (thanks @jessedoyle)\n- Allow hash substitutions in `String#%` and `sprintf` (thanks @zamith)\n- Added `SecureRandom.base64`, `SecureRandom.urlsafe_base64` and `SecureRandom.uuid` (thanks @ysbaddaden)\n- Added `File.link`, `File.symlink` and `File.symlink?` (thanks @ysbaddaden)\n- Added `Enumerable#in_groups_of` (thanks @jalyna)\n- Added `Array#zip?` (thanks @yui-knk)\n- Added `Array#permutations` and `Array#each_permutation` (thanks @jalyna and @kostya)\n- Added `IO#gets(limit : Int)` and `IO#gets(delimiter : Char, limit : Int)`\n- Added `Iterator#compact_map`, `Iterator#take_while` and `Iterator#skip_while` (thanks @PragTob)\n- Added `StringLiteral#to_i` macro method\n- Added `Crypto::Bcrypt` (thanks @akaufmann)\n- Added `Time.epoch_ms` and `Time#epoch_ms`\n- Added `BitArray#toggle` and `BitArray#invert` (thanks @will)\n- Fixed `IO#reopen` swapped semantic (thanks @technorama)\n- Many bug fixes and improvements\n\n### 0.7.5 (2015-07-30)\n\n- **(breaking change)** `0` is not a prefix for octal numbers anymore. Use `0o`\n- **(breaking change)** Renamed `MissingKey` to `KeyError`\n- **(breaking change)** Renamed `IndexOutOfBounds` to `IndexError`\n- Fixed all exception-handling related bugs.\n- Allow nested and multiline ternary expressions (thanks @daviswahl)\n- Allow assigning to `_` (underscore), give error when trying to read from it\n- Macros can now also receive the following nodes: `And`, `Or`, `Case`, `RangeLiteral` and `StringInterpolation`. `And` and `Or` have `left` and `right` methods.\n- Added `-e` option to `hierarchy` command to filter types by a regex\n- Added `-v` as an alias of `--version`\n- Added `-h` as an alias of `--help`\n- Added `Array#transpose` (thanks @rhysd)\n- Added `Benchmark#ips` (thanks @will)\n- Added `Hash#merge(&block)` and `Hash#merge!(&block)` (thanks @yui-knk)\n- Added `Hash#invert` (thanks @yui-knk)\n- Added `Bool#^` (thanks @yui-knk)\n- Added `Enumerable#drop`, `Enumerable#drop_while` and `Enumerable#take_while` (thanks @PragTob)\n- Added `Enumerable#none?` (thanks @yui-knk)\n- Added `Set#subset?`, `Set#superset?` and `Set#intersects?` (thanks @yui-knk)\n- Added `Set#new(Enumerable)` (thanks @yui-knk)\n- Added `String#succ` (thanks @porras and @Exilor)\n- Added `Array#*` (thanks @porras)\n- Added `Char#===(Int)` and `Int#===(Char)` (thanks @will)\n- Added `StringLiteral#camelcase` and `StringLiteral#underscore` in macros\n- Added `Expressions#expressions` in macros\n- Added `Cast#obj` and `Cast#to` in macros\n- Added `ASTNode#class_name` in macros (thanks @yui-knk)\n- Added `Array#push`/`Array#<<` and `Array#unshift` in macros (thanks @ysbaddaden)\n- Added `Def#visibility` in macros (thanks @ysbaddaden)\n- Added `String#codepoints` and `String#each_codepoint` (thanks @jhass)\n- `Char#to_i(base)` now supports bases from 2 to 36\n- `Set#|` now correctly accepts a set of a possible different type (thanks @yui-knk)\n- Flush `STDERR` on exit (thanks @jbbarth)\n- `HTTP::Client` methods accept an optional block, which will yield an `HTTP::Response` with a non-nil `body_io` property to consume the response's IO\n- Document `URI`, `UDPSocket` (thanks @davydovanton)\n- Improved `URI` class (thanks @will)\n- Define `$~` in `String#gsub` and `String#scan`\n- Define `$?` in `Process.run`\n- Lots of bug fixes and small improvements\n\n### 0.7.4 (2015-06-23)\n\n- Added Float module and remainder (thanks @wmoxam)\n- Show elapsed time in HTTP::LogHandler (thanks @zamith for the suggestion)\n- Added `0o` as a prefix for octal numbers (thanks @asb)\n- Allow spaces before the closing tag of a heredoc (thanks @zamith)\n- `String#split(Regex)` now includes captures in the results\n- Added `union?`, `union_types` and `type_params` in macro methods\n- Improved `MatchData#to_s` to show named groups (thanks @will)\n- Optimized Base64 encode/decode (thanks @kostya)\n- Added basic docs for spec (thanks @PragTob)\n- Added docs for Benchmark (thanks @daneb)\n- Added `ARGF`\n- Non-matching regex captures now return `nil` instead of an empty string (thanks @will)\n- Added `$1?`, `$2?`, etc., as a nilable alternative to `$1`, `$2`, etc.\n- Added user, password, fragment and opaque to URI (thanks @will)\n- `HTTP::Client.exec` now honors user/password info from URI\n- Set default user agent in `HTTP::Client`\n- Added `String#chop`\n- Fixed `crystal deps` behaviour with empty git repositories (thanks @tkrajcar)\n- Optimized `HTTP::Headers` and `HTTP::Request` parsing.\n- `FileDescriptorIO` (superclass of `File` and `Socket`) has now buffering capabilities (use `sync=` and `sync?` to turn on/off). That means there's no need to use `BufferedIO` for these classes anymore.\n- Allow `pointerof` with class and global variables, and also `foo.@bar` access\n- Optimized fibers performance.\n- Added inline assembly support.\n- The `.crystal` cache dir is now configurable with an ENV variable (thanks @jhass)\n- Generic type variables names can now also be a single letter followed by a digit.\n\n### 0.7.3 (2015-06-07)\n\n- Added `Tuple.from_json` and `Tuple.to_json`\n- The `method_missing` macro now accepts a 1 argument variant that is a Call node. The 3 arguments variant will be deprecated.\n- Flush STDOUT at program exit (fixes `print` not showing any output)\n- Added `Time#to_utc` and `Time#to_local` (thanks @datanoise)\n- Time comparison is now correct when comparing local vs. utc times\n- Support timezone offsets in Time parsing and formatting\n- Added `IO#gets(delimiter : String)`\n- Added `String#chomp(Char)` and `String#chomp(String)`\n- Allow invoking `debug()` inside a macro to see what's being generated.\n- `IO#puts` and `IO#print` now receive a splat (thanks @rhysd)\n- Added `Process.kill` and `Process.getpgid` (thanks @barachy)\n- `Signal` is now an enum. Use it like `Signal::INT.trap { ... }` instead of `Signal.trap(Signal::INT) { ... }`\n- Added `CSV.each_row` (both in block and iterator forms)\n- Important fixes to non-blocking IO logic.\n\n### 0.7.2 (2015-05-26)\n\n- Improved performance of Regex\n- Fixed lexing of octal characters and strings (thanks @rhysd)\n- Time.parse can return UTC times (thanks @will)\n- Handle dashes in `crystal init` (thanks @niftyn8)\n- Generic type variables can now only be single letters (T, U, A, B, etc.)\n- Support `%x` and `%X` in `sprintf` (thanks @yyyc514)\n- Optimized `Int#to_s` (thanks @yyyc514)\n- Added `upcase` option to `Int#to_s`, and use downcase by default.\n- Improved `String#to_i` and fixed the many variants (`to_i8`, `to_u64`, etc.)\n- Added `Time.at` (thanks @jeromegn)\n- Added `Int#upto`, `Int#downto`, `Int#to` iterators.\n- Added `Iterator#cons` and `Enumerable#each_cons` (thanks @porras)\n- Added `Iterator.of`, `Iterator#chain` and `Iterator#tap`.\n- Allow top-level `private macro` (similar to top-level `private def`)\n- Optimized `BufferedIO` writing performance and memory usage.\n- Added `Channel#close`, `Channel#closed?`, `Channel#receive?` and allow them to send/receive nil values (thanks @datanoise).\n- Fixed `Process#run` after introducing non-blocking IO (thanks @will)\n- `Tuple#map` now returns a `Tuple` (previously it returned an `Array`)\n- `Tuple#class` now returns a proper `Class` (previously it returned a `Tuple` of classes)\n- Lots of bug fixes.\n\n### 0.7.1 (2015-04-30)\n\n- Fixed [#597](https://github.com/crystal-lang/crystal/issues/597).\n- Fixed [#599](https://github.com/crystal-lang/crystal/issues/599).\n\n### 0.7.0 (2015-04-30)\n\n- Crystal has evented IO by default. Added `spawn` and `Channel`.\n- Correctly support the X86_64 and X86 ABIs. Now bindings to C APIs that pass and return structs works perfectly fine.\n- Added `crystal init` to quickly create a skeleton library or application (thanks @waterlink)\n- Added `--emit` flag to the compiler. Now you can easily see the generated LLVM IR, LLVM bitcode, assembly and object files.\n- Added `--no-color` flag to suppress color output, useful for editor tools.\n- Added macro vars: `%var` and `%var{x, y}` create uniqely named variables inside macros.\n- Added [typed splats](https://github.com/crystal-lang/crystal/issues/291).\n- Added `Iterator` and many methods that return iterators, like `Array#each`, `Hash#each`, `Int#times`, `Int#step`, `String#each_char`, etc.\n- Added `sprintf` and improved `String#%` to support floats and float formatting.\n- Added more variants of `String#gsub`.\n- Added `Pointer#clear` and use it to clear an `Array`'s values when doing `pop` and other shrinking methods.\n- Added `BigInt#to_s(base)`, `BigInt::cast` and bit operators (thanks @Exilor)\n- Allow invoking methods on a union class as long as all types in the union have it.\n- Allow specifying a def's return type. The compiler checks the return type only for that def for now (not for subclasses overriding the method). The return type appears in the documentation.\n- Allow constants and computed constants for a StaticArray length.\n- Allow class vars in enums.\n- Allow private and protected defs in enums.\n- Allow reopening a `lib` and adding more `@[Link]` attributes to it, even allowing duplicated attributes.\n- Allow getting a function pointer to a lib fun without specifying its types (i.e. `->LibC.getenv`)\n- Allow specifying `ditto` for a doc comment to reuse the previous comment.\n- Changed the semantic of `%`: previously it meant `remainder`, not it means `modulo`, similar to Ruby and Python. Added `Int#remainder`.\n- `#to_s` and `#inspect` now work for a union class.\n- Spec: added global `before_each` and `after_each` hooks, which will simplify the use of mocking libraries like [timecop.cr](https://github.com/waterlink/timecop.cr) and [webmock.cr](https://github.com/manastech/webmock.cr).\n- `Range(T)` is now `Range(B, E)` again (much more flexible).\n- Improved Regex performance.\n- Better XML support.\n- Support LLVM 3.6.\n- Exception class is now shown on unhandled exceptions\n- The following types are now disallowed in generics (for now): Object, Value, Reference, Number, Int and Float.\n- Lots of bug fixes, enhancements and optimizations.\n\n### 0.6.1 (2015-03-04)\n\n- The `class` method now works in all cases. You can now compare classes with `==` and ask their `hash` value.\n- Block variables can now shadow local variables.\n- `Range(B, E)` is now `Range(T)`.\n- Added `Number::[]`. Now you can do `Int64[1, 2, 3]` instead of `[1_i64, 2_i64, 3_u64]`.\n- Better detection of nilable instance variables, and better error messages too.\n- Added `Crypto::Blowfish` (thanks @akaufmann)\n- Added `Matrix` (thanks @Exilor)\n- Added `CallConvention` attribute for `fun`s.\n- Macros: added `constants` so you can inspect a type's constants at compile time.\n- Macros: added `methods`, which lists a type's methods (without including supertypes).\n- Macros: added `has_attribute?` for enum types, so you can check if an enum has the Flags attribute on it.\n- Many more small additions and bug fixes.\n\n### 0.6.0 (2015-02-12)\n\n- Same as 0.5.10\n\n### 0.5.10 (transitional) (2015-02-12)\n\n- **Note**: This release makes core, breaking changes to the language, and doesn't work out of the box with its accompanying standard library. Use 0.6.0 instead.\n- Improved error messages related to nilable instance variables.\n- The magic variables `$~` and `$?` are now method-local and concurrent-safe.\n- `Tuple` is now correctly considered a struct\n- `Pointer` is now correctly considered a struct\n- Renamed `Function` to `Proc`\n\n### 0.5.9 (2015-02-07)\n\n- `Random` is now a module, with static methods that default to the `Random::MT19937` class.\n- Added `Random::ISAAC` engine (thanks @ysbaddaden!)\n- Added `String#size` (thanks @zamith!)\n- Added `limit` to all `String#split` variants (thanks @jhass!)\n- Raising inside a Thread is now rescued and re-raised on join (thanks @jhass!)\n- Added `path` option to Projectfile for `crystal deps` (thanks @naps62!)\n- Many fixes towards making Crystal work on linux 32 bits.\n- Huge refactors, additions and improvements for sockets: Socket, IPSocket, TCPSocket, TCPServer, UDPSocket, UNIXSocket, UNIXServer (thanks @ysbaddaden!)\n- Allow regex with empty spaces in various places.\n- Added `HTML.escape(String)` (thanks @naps62!)\n- Added support for `%w[...]`, `%w{...}`, `%w<...>` as alternatives to `%w(...)`. Same goes for `%i(...)` (thanks @zamith!)\n- Added `Enumerable#min_of`, `Enumerable#max_of` and `Enumerable#minmax_of`, `Enumerable#to_h`, `Dir.chdir` and `Number#fdiv` (thanks @jhass!)\n- Added `String#match`, `String#[]`, `String#[]?` and `MatchData#[]?` related to regexes (thanks @jhass!)\n- Allow `T::Bar` when T is a generic type argument.\n- Added `subclasses` and `all_subclasses` in macros.\n- Now you can invoke `to_s` and `inspect` on C structs and unions, making debugging C bindings much easier!\n- Added `#to_f` and `#to_i` to `Time` and `TimeSpan` (thanks @epitron!)\n- Added `IO.select` (thanks @jhass!)\n- Now you can use `ifdef` inside C structs and unions.\n- Added `include` inside C structs, to include other struct fields (useful for composition and avoiding an explicit indirection).\n- Added `Char#in_set?`, `String#count`, `String#delete` and `String#squeeze` (thanks @jhass!)\n- Added `-D flag` option to the compiler to set compile-time flags to use in `ifdef`.\n- More support for forward declarations inside C libs.\n- Rewritten some `Function` primitives in Crystal itself, and added methods for obtaining the pointer and closure data, as well as for recreating a function from these.\n- Added a `Logger` class (thanks @ysbaddaden!)\n- Lots of bugs fixed.\n\n### 0.5.8 (2015-01-16)\n\n- Added `Random` and `Random::MT19937` (Mersenne Twister) classes (thanks @rhysd).\n- Docs: removed automatic linking. To link to classes and methods surround with backticks.\n- Fixed [#328](https://github.com/crystal-lang/crystal/issues/328): `!=` bug.\n\n### 0.5.7 (2015-01-02)\n\n- Fixed: `doc` command had some hardcoded paths and didn't work\n- Added: `private def` at the top-level of a file is only available inside that file\n\n### 0.5.6 (2014-31-12)\n\n- Added a `crystal doc` command to automatically generate documentation for a project using [Markdown](http://daringfireball.net/projects/markdown/) syntax. The style is still ugly but it's quite functional. Now we only need to start documenting things :-)\n- Removed the old `@:` attribute syntax.\n- Fixed [#311](https://github.com/crystal-lang/crystal/issues/311): Issues with invoking lib functions in other ways (thanks @scidom).\n- Fixed [#314](https://github.com/crystal-lang/crystal/issues/314): NoReturn information is not lazy.\n- Fixed [#317](https://github.com/crystal-lang/crystal/issues/317): Fixes in UTF-8 encoding/decoding (thanks @yous).\n- Fixed [#319](https://github.com/crystal-lang/crystal/issues/319): Unexpected EOF (thanks @Exilor).\n- `{{yield}}` inside macros now preserve the yielded node location, leading to much better error messages.\n- Added `Float#nan?`, `Float#infinite?` and `Float#finite?`.\n- Many other bug fixes and improvements.\n\n### 0.5.5 (2014-12-12)\n\n- Removed `src` and crystal compiler `libs` directory from CRYSTAL_PATH.\n- Several bug fixes.\n\n### 0.5.4 (2014-12-04)\n\n- **(breaking change)** `require \"foo\"` always looks up in `CRYSTAL_PATH`. `require \"./foo\"` looks up relative to the requiring file.\n- **(breaking change)** Renamed `Json` to `JSON`, `Xml` to `XML` and `Yaml` to `YAML` to follow [a convention](https://github.com/crystal-lang/crystal/issues/279).\n- **(breaking change)** To use HTTP types do, for example, `require \"http/client\"` instead of the old `require \"net/http\"`.\n- Added `alias_method` macro (thanks @Exilor and @jtomschroeder).\n- Added some `Complex` number methods and many math methods, refactors and specs (thanks @scidom).\n- Inheriting generic classes is now possible.\n- Creating arrays of generic types (i.e.: `[] of Thread`) is now possible.\n- Allow using an alias in a block type (i.e.: `alias F = Int32 ->`, `&block : F`).\n- `json_mapping` macro supports a simpler syntax: `json_mapping({key1: Type1, key2: Type2})`.\n- Spec: added `be_a(type)` matcher.\n- Spec: added `be > ...` and similar matchers for `>=`, `<` and `<=`.\n- Added `File::file?` and `File::directory?`.\n- CSV parser can parse from String or IO.\n- When invoking the compiler like this: `crystal foo.cr -o foo` the `build` command is assumed instead of `run`.\n- Added short symbol notation for methods that are operators (i.e. `:+`, `:*`, `:[]`, etc.).\n- Added `TimeSpan#ago`, `TimeSpan#from_now`, `MonthSpan#ago` and `MonthSpan#from_now`.\n\n### 0.5.3 (2014-11-06)\n\n- Spec: when a `should` or `should_not` fail, the filename and line number, including the source's line, is included in the error message.\n- Spec: added `-l` switch to be able to run a spec defined in a line.\n- Added `crystal spec file:line`\n- Properties (property, setter, getter) can now be restricted to a type with the syntax `property name :: Type`.\n- Enums can be used outside `lib`. They inherit `Enum`, can have methods and can be marked with @[Flags].\n- Removed the distinction between `lib` enums and regular enums.\n- Fixed: it was incorrectly possible to define `class`, `def`, etc. inside a call block.\n- The syntax for specifying the base type of an enum, `enum Name < BaseType` has been deprecated. Use `enum Name : BaseType`.\n- Added `Array#<=>` and make it comparable to other arrays.\n\n### 0.5.2 (2014-11-04)\n\n- New command line interface to the compiler (`crystal build ...`, `crystal run ...`, `crystal spec`, etc.). The default is to compiler and run a program.\n- `crystal eval` without arguments reads from standard input.\n- Added preliminar `crystal deps` command.\n- `__FILE__`, `__DIR__` and `__LINE__`, when used as def default arguments, resolve to the caller location (similar to [D](http://dlang.org/traits.html#specialkeywords) and [Swift](https://developer.apple.com/swift/blog/?id=15))\n- Allow `as` to determine a type even if the casted value doesn't have a type yet.\n- Added `is_a?` in macros. The check is against an [AST node](https://github.com/crystal-lang/crystal/blob/master/src/compiler/crystal/syntax/ast.cr) name. For example `node.is_a?(HashLiteral)`.\n- Added `emit_null` property to `json_mapping`.\n- Added `converter` property to `json_mapping`.\n- Added `pp` in macros.\n- Added `to_pretty_json`.\n- Added really basic `CSV.parse`.\n- Added `Regex.escape`.\n- Added `String#scan`.\n- Added `-e` switch to spec, to run specs that match a pattern.\n- Added `--fail-fast` switch to spec.\n- Added `HTTPClient#basic_auth`.\n- Added `DeclareVar`, `Def` and `Arg` macro methods.\n- Added `Time` and `TimeSpan` structs. `TimeWithZone` will come later.\n- Added `Array#fill` (thanks @Exilor).\n- Added `Array#uniq`.\n- Optimized `File.read_lines`.\n- Allow any expression inside `{% ... %}` so that you can interpret code without outputting the result.\n- Allow `\\` at the end of a line.\n- Allow using `if` and `unless` inside macro expressions.\n- Allow marking a `fun/def` as `@[Raises]` (useful when a function can potentially raise from a callback).\n- Allow procs are now considered `@[Raises]`.\n- `OAuth2::Client` supports getting an access token via authorization code or refresh token.\n- Consecutive string literals are automatically concatenated by the parser as long as there is a `\\` with a newline between them.\n- Many bug fixes.\n\n### 0.5.1 (2014-10-16)\n\n- Added [json_mapping](https://github.com/crystal-lang/crystal/blob/master/spec/std/json/mapping_spec.cr) macro.\n- Added [Signal](https://github.com/crystal-lang/crystal/blob/master/src/signal.cr) module.\n- Added [Tempfile](https://github.com/crystal-lang/crystal/blob/master/src/tempfile.cr) class.\n- Enhanced [HTTP::Client](https://github.com/crystal-lang/crystal/blob/master/src/net/http/client/client.cr).\n- Added [OAuth::Consumer](https://github.com/crystal-lang/crystal/blob/master/libs/oauth/consumer.cr).\n- Added [OAuth2::Client](https://github.com/crystal-lang/crystal/blob/master/libs/oauth2/client.cr).\n- Added [OpenSSL::HMAC](https://github.com/crystal-lang/crystal/blob/master/libs/openssl/hmac.cr).\n- Added [SecureRandom](https://github.com/crystal-lang/crystal/blob/master/src/secure_random.cr).\n- New syntax for array/hash-like classes. For example: `Set {1, 2, 3}` and `HTTP::Headers {\"content-type\": \"text/plain\"}`. These just create the type and use `<<` or `[]=`.\n- Optimized Json parsing performance.\n- Added a [CSV builder](https://github.com/crystal-lang/crystal/blob/0.5.1//src/csv.cr#L13).\n- XML reader can [parse from an IO](https://github.com/crystal-lang/crystal/blob/0.5.1//src/xml/reader.cr#L10).\n- Added `Dir::glob` and `Dir::Entries` (thanks @jhass)\n- Allow `ensure` as an expression suffix.\n- Fixed [#219](https://github.com/crystal-lang/crystal/issues/219): Proc type is not inferred when passing to library fun and the return type doesn't match.\n- Fixed [#224](https://github.com/crystal-lang/crystal/issues/224): Class#new doesn't pass a block.\n- Fixed [#225](https://github.com/crystal-lang/crystal/issues/225): ICE when comparing void to something.\n- Fixed [#227](https://github.com/crystal-lang/crystal/issues/227): Nested captured block looses scope and crashes compiler.\n- Fixed [#228](https://github.com/crystal-lang/crystal/issues/228): Macro expansion doesn't retain symbol escaping as needed.\n- Fixed [#229](https://github.com/crystal-lang/crystal/issues/229): Can't change block context if defined within module context.\n- Fixed [#230](https://github.com/crystal-lang/crystal/issues/230): Type interference breaks equality operator.\n- Fixed [#233](https://github.com/crystal-lang/crystal/issues/233): Incorrect `no block given` message with new.\n- Other bug fixes.\n\n### 0.5.0 (2014-09-24)\n\n- String overhaul, and optimizations\n\n### 0.4.5 (2014-09-24)\n\n- Define backtick (`) for command execution.\n- Allow string literals as keys in hash literals: `{\"foo\": \"bar\"} # :: Hash(String, String)`\n- Allow `ifdef` as a suffix.\n- Integer division by zero raises a `DivisionByZero` exception.\n- Link attributes are now only processed if a lib function is used.\n- Removed the `type Name : Type` syntax (use `type Name = Type` instead).\n- Removed the `lib Lib(\"libname\"); end` syntax. Use `@[Link]` attribute instead.\n- Fixed some `require` issues.\n- String representation includes length.\n- Upgraded to LLVM 3.5.\n\n### 0.4.4 (2014-09-17)\n\n- Fixed [#193](https://github.com/crystal-lang/crystal/issues/193): allow initializing an enum value with another's one.\n- The `record` macro is now variadic, so instead of `record Vec3, [x, y, z]` write `record Vec3, x, y, z`.\n- The `def_equals`, `def_hash` and `def_equals_and_hash` macros are now variadic.\n- The `property`, `getter` and `setter` macros are now variadic.\n- All String methods are now UTF-8 aware.\n- `String#length` returns the number of characters, while `String#bytesize` return the number of bytes (previously `length` returned the number of bytes and `bytesize` didn't exist).\n- `String#[](index)` now returns a `Char` instead of an `UInt8`, where index is counted in characters. There's also `String#byte_at(index)`.\n- Removed the `\\x` escape sequence in char and string literals. Use `\\u` instead.\n- `initialize` methods are now protected.\n- Added `IO#gets_to_end`.\n- Added backticks (`...`) and `%x(...)` for command execution.\n- Added `%r(...)` for regular expression literals.\n- Allow interpolations in regular expression literals.\n- Compiling with `--release` sets a `release` flag that you can test with `ifdef`.\n- Allow passing splats to C functions\n- A C type can now be declared like `type Name = Type` (`type Name : Type` will be deprecated).\n- Now a C struct/union type can be created with named arguments.\n- New attributes syntax: `@[Attr(...)`] instead of `@:Attr`. The old syntax will be deprecated in a future release.\n- New link syntax for C libs: `@[Link(\"name\")]` (uses `name` as `pkg-config name` if available or `-lname` instead), `@[Link(ldflags: \"...\")]` to pass raw flags to the linker, `@[Link(\"name\", static: true)]` to try to find a static library first, and `@[Link(framework: \"AppKit\")]` (for Mac OSX).\n- Added an `exec` method to execute shell commands. Added the `system` and `backtick` similar to Ruby ones.\n- Added `be_truthy` and `be_falsey` spec matchers. Added `Array#zip` without a block. (thanks @mjgpy3)\n- Added `getter?` and `property?` macros to create methods that end with `?`.\n- Added a `CGI` module.\n- The compiler now only depends on `cc` for compiling (removed dependency to `llc`, `opt`, `llvm-dis` and `clang`).\n- Added `IO#tty?`.\n- Some bug fixes.\n\n### 0.4.3 (2014-08-14)\n\n- Reverted a commit that introduced random crashes.\n\n### 0.4.2 (2014-08-13)\n\n- Fixed [#187](https://github.com/crystal-lang/crystal/issues/185): mixing `yield` and `block.call` crashes the compiler.\n- Added `\\u` unicode escape sequences inside strings and chars (similar to Ruby). `\\x` will be deprecated as it can generate strings with invalid UTF-8 byte sequences.\n- Added `String#chars`.\n- Fixed: splats weren't working in `initialize`.\n- Added the `private` and `protected` visibility modifiers, with the same semantics as Ruby. The difference is that you must place them before a `def` or a macro call.\n- Some bug fixes.\n\n### 0.4.1 (2014-08-09)\n\n- Fixed [#185](https://github.com/crystal-lang/crystal/issues/185): `-e` flag stopped working.\n- Added a `@length` compile-time variable available inside tuples that allows to do loop unrolling.\n- Some bug fixes.\n\n### 0.4.0 (2014-08-08)\n\n- Support splats in macros.\n- Support splats in defs and calls.\n- Added named arguments.\n- Renamed the `make_named_tuple` macro to `record`.\n- Added `def_equals`, `def_hash` and `def_equals_and_hash` macros to generate them from a list of fields.\n- Added `Slice(T)`, which is a struct having a pointer and a length. Use this in IO for a safe API.\n- Some `StaticArray` fixes and enhancements.\n\n### 0.3.5 (2014-07-29)\n\n- **(breaking change)** Removed the special `->` operator for pointers of structs/unions: instead of `foo->bar` use `foo.value.bar`; instead of `foo->bar = 1` use `foo.value.bar = 1`.\n- Added `colorize` file that provides methods to easily output bash colors.\n- Now you can use modules as generic type arguments (for example, do `x = [] of IO`).\n- Added SSL sockets. Now HTTP::Server implements HTTPS.\n- Macros have access to constants and types.\n- Allow iterating a range in macros with `for`.\n- Use cpu cycle counter to initialize random.\n- `method_missing` now works in generic types.\n- Fixed [#154](https://github.com/crystal-lang/crystal/issues/154): bug, constants are initialized before global variables.\n- Fixed [#168](https://github.com/crystal-lang/crystal/issues/168): incorrect type inference of instance variables if not assigned in superclass.\n- Fixed [#169](https://github.com/crystal-lang/crystal/issues/169): `responds_to?` wasn't working with generic types.\n- Fixed [#171](https://github.com/crystal-lang/crystal/issues/171): ensure blocks are not executed if the rescue block returns from a def.\n- Fixed [#175](https://github.com/crystal-lang/crystal/issues/175): invalid code generated when using with/yield with structs.\n- Fixed some parser issues and other small issues.\n- Allow forward struct/union declarations in libs.\n- Added `String#replace(Regex, String)`\n- Added a `Box(T)` class, useful for boxing value types to pass them to C as `Void*`.\n\n### 0.3.4 (2014-07-21)\n\n- Fixed [#165](https://github.com/crystal-lang/crystal/issues/165): restrictions with generic types didn't work for hierarchy types.\n- Allow using a single underscore in restrictions, useful for matching against an n-tuple or an n-function where you don't care about the types (e.g.: `def foo(x : {_, _})`.\n- Added a `generate_hash` macro that generates a `hash` methods based on some AST nodes.\n- Added very basic `previous_def`: similar to `super`, but uses the previous definition of a method. Useful to decorate existing methods (similar to `alias_method_chain`). For now the method's type restrictions must match for a previous definition to be found.\n- Made the compiler a bit faster\n- Added `env` in macros, to fetch an environment value. Returns a StringLiteral if found or NilLiteral if not.\n- Make `return 1, 2` be the same as `return {1, 2}`. Same goes with `break` and `next`.\n- Added `Pointer#as_enumerable(size : Int)` to create an `Enumerable` from a Pointer with an associated size, with zero overhead. Some methods removed from `Pointer`: `each`, `map`, `to_a`, `index`.\n- Added `StaticArray::new`, `StaticArray::new(value)`, `StaticArray::new(&block)`, `StaticArray#shuffle!` and `StaticArray#map!`.\n- Faster `Char#to_s(io : IO)`\n\n### 0.3.3 (2014-07-14)\n\n- Allow implicit conversion to C types by defining a `to_unsafe` method. This removed the hardcoded rule for converting a `String` to `UInt8*` and also allows passing an `Array(T)` to an argument expecting `Pointer(T)`.\n- Fixed `.is_a?(Class)` not working ([#162](https://github.com/crystal-lang/crystal/issues/162))\n- Attributes are now associated to AST nodes in the semantic pass, not during parsing. This allows macros to generate attributes that will be attached to subsequent expressions.\n- **(breaking change)** Make ENV#[] raise on missing key, and added ENV#[]?\n- **(breaking change)** Macro defs are now written like `macro def name(args) : ReturnType` instead of `def name(args) : ReturnType`, which was a bit confusing.\n\n### 0.3.2 (2014-07-10)\n\n- Integer literals without a suffix are inferred to be Int32, Int64 or UInt64 depending on their value.\n- Check that integer literals fit into their types.\n- Put back `Int#to_s(radix : Int)` (was renamed to `to_s_in_base` in the previous release) by also specifying a restriction in `Int#to_s(io : IO)`.\n- Added `expect_raises` macros in specs\n\n### 0.3.1 (2014-07-09)\n\n- **(breaking change)** Replaced `@name` inside macros with `@class_name`.\n- **(breaking change)** Instance variables inside macros now don't have the `@` symbols in their names.\n\n### 0.3.0 (2014-07-08)\n\n- Added `Array#each_index`\n- Optimized `String#*` for the case when the string has length one.\n- Use `GC.malloc_atomic` for String and String::Buffer (as they don't contain internal pointers.)\n- Added a `PointerAppender` struct to easily append to a `Pointer` while counting at the same time (thanks @kostya for the idea).\n- Added a `Base64` module (thanks @kostya)\n- Allow default arguments in macros\n- Allow invoking `new` on a function type. For example: `alias F = Int32 -> Int32; f = F.new { |x| x + 1 }; f.call(2) #=> 3`.\n- Allow omitting function argument types when invoking C functions that accept functions as arguments.\n- Renamed `@name` to `@class_name` inside macros. `@name` will be deprecated in the next version.\n- Added IO#read_fully\n- Macro hooks: `inherited`, `included` and `extended`\n- `method_missing` macro\n- Added `{{ raise ... }}` inside macros to issue a compile error.\n- Started JSON serialization and deserialization\n- Now `at_exit` handlers are run when you invoke `exit`\n- Methods can be marked as abstract\n- New convention for `to_s` and `inspect`: you must override them receiving an IO object\n- StringBuilder and StringBuffer have been replaced by StringIO\n\n### 0.2.0 (2014-06-24)\n\n- Removed icr (a REPL): it is abandoned for the moment because it was done in a hacky, non-reliable way\n- Added very basic `String#underscore` and `String#camelcase`.\n- The parser generates string literals out of strings with interpolated string literals. For example, `\"#{__DIR__}/foo\"` is interpolated at compile time and generates a string literal with the full path, since `__DIR__` is just a (special) string literal.\n- **(breaking change)** Now macro nodes are always pasted as is. If you want to generate an id use `{{var.id}}`.\n\n    Previously, a code like this:\n\n    ```ruby\n    macro foo(name)\n      def {{name}}; end\n    end\n\n    foo :hello\n    foo \"hello\"\n    foo hello\n    ```\n\n    generated this:\n\n    ```ruby\n    def hello; end\n    def hello; end\n    def hello; end\n    ```\n\n    With this change, it generates this:\n\n    ```ruby\n    def :hello; end\n    def \"hello\"; end\n    def hello; end\n    ```\n\n    Now, to get an identifier out of a symbol literal, string literal or a name, use id:\n\n    ```ruby\n    macro foo(name)\n      def {{name.id}}; end\n    end\n    ```\n\n    Although it's longer to type, the implicit \"id\" call was sometimes confusing. Explicit is better than implicit.\n\n    Invoking `id` on any other kind of node has no effect on the pasted result.\n- Allow escaping curly braces inside macros with `\\{`. This allows defining macros that, when expanded, can contain other macro expressions.\n- Added a special comment-like pragma to change the lexer's filename, line number and column number.\n\n    ```ruby\n    # foo.cr\n    a = 1\n    #<loc:\"bar.cr\",12,24>b = 2\n    c = 3\n    ```\n\n    In the previous example, `b = 2` (and the rest of the file) is considered as being parsed from file `bar.cr` at line 12, column 24.\n\n- Added a special `run` call inside macros. This compiles and executes another Crystal program and pastes its output into the current program.\n\n    As an example, consider this program:\n\n    ```ruby\n    # foo.cr\n    {{ run(\"my_program\", 1, 2, 3) }}\n    ```\n\n    Compiling `foo.cr` will, at compile-time, compile `my_program.cr` and execute it with arguments `1 2 3`. The output of that execution is pasted into `foo.cr` at that location.\n- Added ECR (Embedded Crystal) support. This is implemented using the special `run` macro call.\n\n    A small example:\n\n    ```ruby\n    # template.ecr\n    Hello <%= @msg %>\n    ```\n\n    ```ruby\n    # foo.cr\n    require \"ecr/macros\"\n\n    class HelloView\n      def initialize(@msg)\n      end\n\n      # This generates a to_s method with the contents of template.ecr\n      ecr_file \"template.ecr\"\n    end\n\n    view = HelloView.new \"world!\"\n    view.to_s #=> \"Hello world!\"\n    ```\n\n    The nice thing about this is that, using the `#<loc...>` pragma for specifying the lexer's location, if you have a syntax/semantic error in the template the error points to the template :-)\n\n### 0.1.0 (2014-06-18)\n\n- First official release\n"
  },
  {
    "path": "doc/changelogs/v1.0.md",
    "content": "# Changelog 1.0\n\n## [1.0.0] - 2021-03-22\n\n[1.0.0]: https://github.com/crystal-lang/crystal/releases/1.0.0\n\n### Language changes\n\n- Support `Tuple#[](Range)` with compile-time range literals. ([#10379](https://github.com/crystal-lang/crystal/pull/10379), thanks @HertzDevil)\n\n#### Macros\n\n- Don't use named argument key names as parameters for `method_missing` calls. ([#10388](https://github.com/crystal-lang/crystal/pull/10388), thanks @HertzDevil)\n\n### Standard library\n\n- **(breaking-change)** Drop deprecated definitions. ([#10386](https://github.com/crystal-lang/crystal/pull/10386), thanks @bcardiff)\n- Fix example codes in multiple places. ([#10505](https://github.com/crystal-lang/crystal/pull/10505), thanks @maiha)\n\n#### Macros\n\n- **(breaking-change)** Always add explicit return types in getter/property macros. ([#10405](https://github.com/crystal-lang/crystal/pull/10405), thanks @Sija)\n\n#### Numeric\n\n- **(breaking-change)** Change default rounding mode to `TIES_EVEN`. ([#10508](https://github.com/crystal-lang/crystal/pull/10508), thanks @straight-shoota)\n- Fix downcasting float infinity. ([#10420](https://github.com/crystal-lang/crystal/pull/10420), thanks @straight-shoota)\n- Fix `String#to_f` out of range behaviour. ([#10425](https://github.com/crystal-lang/crystal/pull/10425), thanks @straight-shoota)\n- Implement rounding mode for `Number#round`. ([#10413](https://github.com/crystal-lang/crystal/pull/10413), [#10360](https://github.com/crystal-lang/crystal/pull/10360), [#10479](https://github.com/crystal-lang/crystal/pull/10479), thanks @straight-shoota)\n\n#### Text\n\n- Add missing unicode whitespace support to `String` methods. ([#10367](https://github.com/crystal-lang/crystal/pull/10367), thanks @straight-shoota)\n\n#### Collections\n\n- Fix `Range#==` to ignore generic type arguments. ([#10309](https://github.com/crystal-lang/crystal/pull/10309), thanks @straight-shoota)\n- Make `Enumerable#flat_map`, `Iterator#flat_map` work with mixed element types. ([#10329](https://github.com/crystal-lang/crystal/pull/10329), thanks @HertzDevil)\n- Remove duplicated `sort` related specs. ([#10208](https://github.com/crystal-lang/crystal/pull/10208), thanks @MakeNowJust)\n- Fix docs regarding `Set#each` return type. ([#10477](https://github.com/crystal-lang/crystal/pull/10477), thanks @kachick)\n- Fix docs examples regarding `Set#*set_of?`. ([#10285](https://github.com/crystal-lang/crystal/pull/10285), thanks @oddp)\n- Fix expectation on set specs. ([#10482](https://github.com/crystal-lang/crystal/pull/10482), thanks @kachick)\n\n#### Serialization\n\n- **(breaking-change)** Serialize `Enum` to underscored `String` by default. ([#10431](https://github.com/crystal-lang/crystal/pull/10431), thanks @straight-shoota, @caspiano)\n- **(breaking-change)** Use class instead of struct for types in XML module. ([#10436](https://github.com/crystal-lang/crystal/pull/10436), thanks @hugopl)\n- Add `YAML::Nodes::Node#kind`. ([#10432](https://github.com/crystal-lang/crystal/pull/10432), thanks @straight-shoota)\n\n#### Files\n\n- Let `IO::Memory` not be writable with read-only `Slice`. ([#10391](https://github.com/crystal-lang/crystal/pull/10391), thanks @straight-shoota)\n- Allow `Int64` values within `IO#read_at`. ([#10356](https://github.com/crystal-lang/crystal/pull/10356), thanks @Blacksmoke16)\n- Add `IO::Sized#remaining=(value)` to reuse an existing instance. ([#10520](https://github.com/crystal-lang/crystal/pull/10520), thanks @jgaskins)\n\n#### Networking\n\n- **(security)** Remove Cookie Name Decoding. ([#10442](https://github.com/crystal-lang/crystal/pull/10442), thanks @security-curious)\n- **(breaking-change)** Remove implicit en-/decoding for cookie values. ([#10485](https://github.com/crystal-lang/crystal/pull/10485), thanks @straight-shoota)\n- **(breaking-change)** Split `HTTP::Cookies.from_headers` into separate methods for server/client. ([#10486](https://github.com/crystal-lang/crystal/pull/10486), thanks @straight-shoota)\n- **(performance)** Minor performance improvements to `HTTP::Cookies`. ([#10488](https://github.com/crystal-lang/crystal/pull/10488), thanks @straight-shoota)\n- Respect subclasses when constructing `HTTP::Client` from class methods. ([#10375](https://github.com/crystal-lang/crystal/pull/10375), thanks @oprypin)\n- Make the `content-length` header more RFC compliant. ([#10353](https://github.com/crystal-lang/crystal/pull/10353), thanks @Blacksmoke16)\n- Fix `#respond_with_status` when headers written or closed. ([#10415](https://github.com/crystal-lang/crystal/pull/10415), thanks @straight-shoota)\n- Fix `Cookie#==` to take all ivars into account. ([#10487](https://github.com/crystal-lang/crystal/pull/10487), thanks @straight-shoota)\n- Remove implicit `path=/` from `HTTP::Cookie`. ([#10491](https://github.com/crystal-lang/crystal/pull/10491), thanks @straight-shoota)\n- Add `HTTP::Request#local_address`. ([#10385](https://github.com/crystal-lang/crystal/pull/10385), thanks @carlhoerberg)\n\n#### Logging\n\n- Close `AsyncDispatcher` on `#finalize`. ([#10390](https://github.com/crystal-lang/crystal/pull/10390), thanks @straight-shoota)\n\n#### System\n\n- Fix `Process.parse_argument` behavior against a quote in a word. ([#10337](https://github.com/crystal-lang/crystal/pull/10337), thanks @MakeNowJust)\n- Add aarch64 support for macOS/darwin targets. ([#10348](https://github.com/crystal-lang/crystal/pull/10348), thanks @maxfierke, @RomainFranceschini)\n- Add `LibC::MAP_ANONYMOUS` to x86_64-darwin to match other platforms. ([#10398](https://github.com/crystal-lang/crystal/pull/10398), thanks @sourgrasses)\n\n#### Runtime\n\n- Improve error message for ELF reader on uninitialized runtime. ([#10282](https://github.com/crystal-lang/crystal/pull/10282), thanks @straight-shoota)\n\n### Compiler\n\n- **(breaking-change)** Disallow surrogate halves in escape sequences of string and character literals, use `\\x` for arbitrary binary values. ([#10443](https://github.com/crystal-lang/crystal/pull/10443), thanks @HertzDevil)\n- Fix ICE when exhaustive in-clause calls pseudo-method. ([#10382](https://github.com/crystal-lang/crystal/pull/10382), thanks @HertzDevil)\n- Fix ICE when parsing `foo.%` calls. ([#10351](https://github.com/crystal-lang/crystal/pull/10351), thanks @MakeNowJust)\n- Fix edge cases for symbol quoting rules. ([#10389](https://github.com/crystal-lang/crystal/pull/10389), thanks @HertzDevil)\n- Support closured vars inside `Const` initializer. ([#10478](https://github.com/crystal-lang/crystal/pull/10478), thanks @RX14)\n- Documentation grammar fix. ([#10369](https://github.com/crystal-lang/crystal/pull/10369), thanks @szTheory)\n\n#### Language semantics\n\n- Don't fail on untyped `is_a?`. ([#10320](https://github.com/crystal-lang/crystal/pull/10320), thanks @asterite)\n- Fix named arguments in `super` and `previous_def` calls. ([#10400](https://github.com/crystal-lang/crystal/pull/10400), thanks @HertzDevil)\n- Fix assignments in array literals. ([#10009](https://github.com/crystal-lang/crystal/pull/10009), thanks @straight-shoota)\n- Consider type var splats in generic type restrictions. ([#10168](https://github.com/crystal-lang/crystal/pull/10168), thanks @HertzDevil)\n- Align `Proc.new(&block)`'s behaviour with other captured blocks. ([#10263](https://github.com/crystal-lang/crystal/pull/10263), thanks @HertzDevil)\n- Don't merge `NamedTuple` metaclasses through instance types. ([#10501](https://github.com/crystal-lang/crystal/pull/10501), thanks @HertzDevil)\n- Access instantiations of `NamedTuple` and other generics uniformly. ([#10401](https://github.com/crystal-lang/crystal/pull/10401), thanks @HertzDevil)\n- Improve error message for auto-cast error at Var assign. ([#10327](https://github.com/crystal-lang/crystal/pull/10327), thanks @straight-shoota)\n- Exclude abstract defs from \"no overload matches\" errors. ([#10483](https://github.com/crystal-lang/crystal/pull/10483), thanks @HertzDevil)\n- Support splats inside tuple literals in type names. ([#10430](https://github.com/crystal-lang/crystal/pull/10430), thanks @HertzDevil)\n- Accept pointer instance types on falsey conditional branches. ([#10464](https://github.com/crystal-lang/crystal/pull/10464), thanks @HertzDevil)\n- Match named arguments by external parameter names when checking overload cover. ([#10530](https://github.com/crystal-lang/crystal/pull/10530), thanks @HertzDevil)\n\n#### Doc generator\n\n- Detect source locations in more situations. ([#10439](https://github.com/crystal-lang/crystal/pull/10439), thanks @oprypin)\n\n### Others\n\n- CI improvements and housekeeping. ([#10299](https://github.com/crystal-lang/crystal/pull/10299), [#10340](https://github.com/crystal-lang/crystal/pull/10340), [#10476](https://github.com/crystal-lang/crystal/pull/10476), [#10480](https://github.com/crystal-lang/crystal/pull/10480), thanks @bcardiff, @Sija, @straight-shoota)\n- Update distribution-scripts to use Shards v0.14.1. ([#10494](https://github.com/crystal-lang/crystal/pull/10494), thanks @bcardiff)\n- Add GitHub issue templates. ([#8934](https://github.com/crystal-lang/crystal/pull/8934), thanks @j8r)\n- Add LLVM 11.1 to the list of supported versions. ([#10523](https://github.com/crystal-lang/crystal/pull/10523), thanks @Sija)\n- Fix SDL examples crashes. ([#10470](https://github.com/crystal-lang/crystal/pull/10470), thanks @megatux)\n"
  },
  {
    "path": "doc/changelogs/v1.1.md",
    "content": "# Changelog 1.1\n\n## [1.1.1] - 2021-07-26\n\n[1.1.1]: https://github.com/crystal-lang/crystal/releases/1.1.1\n\n### Language changes\n\n- Revert name of top-level module to `main` ([#10993](https://github.com/crystal-lang/crystal/pull/10993), thanks @beta-ziliani)\n\n### Standard Library\n\n- Fix missing required args for `Socket::Addrinfo::Error.new` ([#10960](https://github.com/crystal-lang/crystal/pull/10960), thanks @straight-shoota)\n- Fix disable unnecessary spec on win32 ([#10971](https://github.com/crystal-lang/crystal/pull/10971), thanks @straight-shoota)\n- Remove incorrect type restrictions on index methods with offset ([#10972](https://github.com/crystal-lang/crystal/pull/10972), thanks @straight-shoota)\n- Fix: documentation of `#step` in `Number` and `Char` ([#10966](https://github.com/crystal-lang/crystal/pull/10966), [#11006](https://github.com/crystal-lang/crystal/pull/11006), thanks @beta-ziliani and @straight-shoota)\n\n### Compiler\n\n- Fix parsing macro body with escaped backslash in literal ([#10995](https://github.com/crystal-lang/crystal/pull/10995), thanks @straight-shoota)\n\n### Other\n\n- Updating aarch64 actions to use 1.0.0 images ([#10976](https://github.com/crystal-lang/crystal/pull/10976), thanks @beta-ziliani)\n\n## [1.1.0] - 2021-07-14\n\n[1.1.0]: https://github.com/crystal-lang/crystal/releases/1.1.0\n\n### Language changes\n\n- Support splat expansions inside tuple and array literals. ([#10429](https://github.com/crystal-lang/crystal/pull/10429), thanks @HertzDevil)\n- Support breaks with values inside `while` expressions. ([#10566](https://github.com/crystal-lang/crystal/pull/10566), thanks @HertzDevil)\n\n#### Macros\n\n- Add `@top_level` to access the top-level scope in macros. ([#10682](https://github.com/crystal-lang/crystal/pull/10682), thanks @beta-ziliani)\n- Fix: preserve integer sizes in `NumberLiteral#int_bin_op`. ([#10713](https://github.com/crystal-lang/crystal/pull/10713), thanks @collidedscope)\n- Add `NumberLiteral#to_number`. ([#10802](https://github.com/crystal-lang/crystal/pull/10802), thanks @straight-shoota)\n- **(breaking-change)** Add `Crystal::Macros::Path#global?` deprecating the old `Crystal::Macros::Path#global`. ([#10812](https://github.com/crystal-lang/crystal/pull/10812), thanks @HertzDevil)\n- Minor fixes to docs of `UnaryExpression` macro nodes. ([#10816](https://github.com/crystal-lang/crystal/pull/10816), thanks @HertzDevil)\n- Add macro method `ASTNode#nil?`. ([#10850](https://github.com/crystal-lang/crystal/pull/10850), [#10616](https://github.com/crystal-lang/crystal/pull/10616), thanks @straight-shoota)\n\n### Standard library\n\n#### Global changes\n\n##### Windows support\n\n- Port `Socket::Address` to win32 . ([#10610](https://github.com/crystal-lang/crystal/pull/10610), thanks @straight-shoota)\n- Port `Socket::Addrinfo` to win32. ([#10650](https://github.com/crystal-lang/crystal/pull/10650), thanks @straight-shoota)\n- Extract system-specifics from Socket. ([#10706](https://github.com/crystal-lang/crystal/pull/10706), thanks @straight-shoota)\n- Make `WinError` portable and add it to prelude. ([#10725](https://github.com/crystal-lang/crystal/pull/10725), thanks @straight-shoota)\n- Improve portability of `SystemError`. ([#10726](https://github.com/crystal-lang/crystal/pull/10726), thanks @straight-shoota)\n- Refactor `Socket::Addrinfo::Error` based on `os_error`. ([#10761](https://github.com/crystal-lang/crystal/pull/10761), thanks @straight-shoota)\n- Add `WinError.wsa_value` and specs for `WinError`. ([#10762](https://github.com/crystal-lang/crystal/pull/10762), thanks @straight-shoota)\n- Add specs for `Errno`. ([#10763](https://github.com/crystal-lang/crystal/pull/10763), thanks @straight-shoota)\n- Refactor: Move win32 libc bindings from `winbase.cr` to appropriate files. ([#10771](https://github.com/crystal-lang/crystal/pull/10771), thanks @straight-shoota)\n- Refactor: Change protocol socket fd to `Socket::Handle`. ([#10772](https://github.com/crystal-lang/crystal/pull/10772), thanks @straight-shoota)\n- Fix `Socket::Connect` error in addrinfo inherit `os_error`. ([#10782](https://github.com/crystal-lang/crystal/pull/10782), thanks @straight-shoota)\n- Reorganize some win32 libc bindings ([#10776](https://github.com/crystal-lang/crystal/pull/10776), thanks @straight-shoota)\n\n##### Type annotations\n\n- Add type restriction to private `Process` constructor. ([#7040](https://github.com/crystal-lang/crystal/pull/7040), thanks @z64)\n- Add various return type restrictions (thanks @oprypin, @straight-shoota, and @caspiano):\n    [#10578](https://github.com/crystal-lang/crystal/pull/10578), [#10579](https://github.com/crystal-lang/crystal/pull/10579),\n    [#10580](https://github.com/crystal-lang/crystal/pull/10580), [#10581](https://github.com/crystal-lang/crystal/pull/10581),\n    [#10582](https://github.com/crystal-lang/crystal/pull/10582), [#10583](https://github.com/crystal-lang/crystal/pull/10583),\n    [#10584](https://github.com/crystal-lang/crystal/pull/10584), [#10585](https://github.com/crystal-lang/crystal/pull/10585),\n    [#10586](https://github.com/crystal-lang/crystal/pull/10586), [#10587](https://github.com/crystal-lang/crystal/pull/10587),\n    [#10588](https://github.com/crystal-lang/crystal/pull/10588), [#10849](https://github.com/crystal-lang/crystal/pull/10849),\n    [#10856](https://github.com/crystal-lang/crystal/pull/10856), [#10857](https://github.com/crystal-lang/crystal/pull/10857),\n    [#10858](https://github.com/crystal-lang/crystal/pull/10858), [#10905](https://github.com/crystal-lang/crystal/pull/10905)\n- Add type restrictions for splat-less overloads of some methods. ([#10594](https://github.com/crystal-lang/crystal/pull/10594), thanks @HertzDevil)\n\n#### Numeric\n\n- Add `Number.new` overload for `String`. ([#10422](https://github.com/crystal-lang/crystal/pull/10422), thanks @Blacksmoke16)\n- Fix `Math.pw2ceil` for zero and 64-bit integers. ([#10555](https://github.com/crystal-lang/crystal/pull/10555), thanks @straight-shoota)\n- Add `#positive?` & `#negative?` to `Number` and `Time::Span`. ([#10601](https://github.com/crystal-lang/crystal/pull/10601), thanks @Blacksmoke16)\n- Fix imprecise `Number#significant` algorithm. ([#10615](https://github.com/crystal-lang/crystal/pull/10615), thanks @straight-shoota)\n- Add `BigFloat`'s rounding modes. ([#10618](https://github.com/crystal-lang/crystal/pull/10618), thanks @HertzDevil)\n- Fix handling of arithmetic overflow in `BigDecimal#div`. ([#10628](https://github.com/crystal-lang/crystal/pull/10628), thanks @kellydanma)\n- Clarify behaviour of unsafe `Float`-to-number conversions. ([#10631](https://github.com/crystal-lang/crystal/pull/10631), thanks @HertzDevil)\n- Fix return type restriction for `Number#humanize` overload. ([#10633](https://github.com/crystal-lang/crystal/pull/10633), thanks @HertzDevil)\n- Fix `printf` float with many digits. ([#10719](https://github.com/crystal-lang/crystal/pull/10719), thanks @straight-shoota)\n- Add `BigDecimal`'s missing rounding modes. ([#10798](https://github.com/crystal-lang/crystal/pull/10798), thanks @HertzDevil)\n- Add support for using big rational `#**` with unsigned ints. ([#10887](https://github.com/crystal-lang/crystal/pull/10887), thanks @stakach)\n- Add overflow detection to `BigFloat#to_i64` and `#to_u64`. ([#10630](https://github.com/crystal-lang/crystal/pull/10630), thanks @HertzDevil)\n\n#### Text\n\n- **(performance)** Optimize `Levenshtein.distance`. ([#8324](https://github.com/crystal-lang/crystal/pull/8324), thanks @r00ster91)\n- Refactor: add private `Slice#hexdump(io : IO)` overload. ([#10496](https://github.com/crystal-lang/crystal/pull/10496), thanks @HertzDevil)\n- Restrict `MatchData#begin` and `#end` to `Int32`. ([#10656](https://github.com/crystal-lang/crystal/pull/10656), thanks @straight-shoota)\n- Refactor: remove `#check_needs_resize` from `IO::Memory`, `String::Builder`. ([#10732](https://github.com/crystal-lang/crystal/pull/10732), thanks @straight-shoota)\n- Fix `Base64#encode`, exclude last 3 bytes from bswap. ([#10752](https://github.com/crystal-lang/crystal/pull/10752), thanks @kostya)\n- Refactor: avoid union type in `Char::Reader#decode_char_at`. ([#10758](https://github.com/crystal-lang/crystal/pull/10758), thanks @asterite)\n\n#### Collections\n\n- Add sub/superset checking methods to `Hash`. ([#7500](https://github.com/crystal-lang/crystal/pull/7500), thanks @Sija)\n- Improve documentation of `Array#[](Range)`. ([#10243](https://github.com/crystal-lang/crystal/pull/10243), thanks @straight-shoota)\n- Add `Steppable` module as generalized `Number#step`. ([#10279](https://github.com/crystal-lang/crystal/pull/10279), thanks @straight-shoota)\n- Add docs for `#map_with_index`. ([#10512](https://github.com/crystal-lang/crystal/pull/10512), thanks @wontruefree)\n- Add `Array#truncate`. ([#10712](https://github.com/crystal-lang/crystal/pull/10712), thanks @HertzDevil)\n- Fix: Always copy `Hash`'s default block on `#dup` and `#clone`. ([#10744](https://github.com/crystal-lang/crystal/pull/10744), thanks @HertzDevil)\n- Apply `Array#push`'s resizing heuristic to `#unshift`. ([#10750](https://github.com/crystal-lang/crystal/pull/10750), thanks @HertzDevil)\n- Refactor index / count normalization in range-like methods. ([#10753](https://github.com/crystal-lang/crystal/pull/10753), thanks @HertzDevil)\n- Add methods for cumulative folding and prefix sums. ([#10789](https://github.com/crystal-lang/crystal/pull/10789), thanks @HertzDevil)\n- Fix: Pass read-only flag to peeked slice in `IO::Memory`. ([#10891](https://github.com/crystal-lang/crystal/pull/10891), thanks @z64)\n\n#### Crypto\n\n- Add methods for getting peer certificates and signatures in `OpenSSL`. ([#8005](https://github.com/crystal-lang/crystal/pull/8005), thanks @will)\n- Add docs for `OpenSSL::Cipher`. ([#9934](https://github.com/crystal-lang/crystal/pull/9934), thanks @sol-vin)\n- Fix format of `src/openssl/cipher.cr`. ([#10705](https://github.com/crystal-lang/crystal/pull/10705), thanks @straight-shoota)\n- Refine documentation for `Random#urlsafe_base64`. ([#10724](https://github.com/crystal-lang/crystal/pull/10724), thanks @straight-shoota)\n- Fix ssl context required for `add_x509_verify_flags`. ([#10756](https://github.com/crystal-lang/crystal/pull/10756), thanks @stakach)\n\n#### Time\n\n- Improve error handling for `load_localtime` on unix. ([#10654](https://github.com/crystal-lang/crystal/pull/10654), thanks @straight-shoota)\n- Fix broken call to `Time#to_s`. ([#10778](https://github.com/crystal-lang/crystal/pull/10778), thanks @straight-shoota)\n- Fix `Time#shift` cover date boundaries with zone offset. ([#10871](https://github.com/crystal-lang/crystal/pull/10871), thanks @straight-shoota)\n\n#### Files\n\n- Fix and unify documentation for `puts`. ([#10614](https://github.com/crystal-lang/crystal/pull/10614), thanks @straight-shoota)\n- Fix `Path#sibling` return type. ([#10655](https://github.com/crystal-lang/crystal/pull/10655), thanks @Sija)\n- Add `Path` in `FileUtils`'s methods to match the interfaces it's wrapping. ([#10747](https://github.com/crystal-lang/crystal/pull/10747), thanks @yb66)\n- Fix `FileDescriptor#pos` return `Int64` on armv6 ([#10845](https://github.com/crystal-lang/crystal/pull/10845), thanks @straight-shoota)\n\n#### Fibers\n\n- Clarify documentation on `Path#join` and `#==`. ([#10455](https://github.com/crystal-lang/crystal/pull/10455), thanks @straight-shoota)\n\n#### Networking\n\n- Add an example middleware for `remote_address`. ([#10408](https://github.com/crystal-lang/crystal/pull/10408), thanks @oprypin)\n- Add `OAuth2::Client#http_client`. ([#10452](https://github.com/crystal-lang/crystal/pull/10452), thanks @straight-shoota)\n- Fix undefined constant error for `http/params`. ([#10537](https://github.com/crystal-lang/crystal/pull/10537), thanks @stakach)\n- Fix looping forever at 100% CPU if socket is closed. ([#10658](https://github.com/crystal-lang/crystal/pull/10658), thanks @didactic-drunk)\n- Fix documentation of `HTTP::Cookies#[]=` empty path. ([#10669](https://github.com/crystal-lang/crystal/pull/10669), thanks @straight-shoota)\n- Fix handling of `EAI_SYSTEM` for `getaddrinfo`. ([#10757](https://github.com/crystal-lang/crystal/pull/10757), thanks @straight-shoota)\n- **(performance)** Cache `socket.local_address` and `socket.remote_address`. ([#10765](https://github.com/crystal-lang/crystal/pull/10765), thanks @lbguilherme)\n- Fix: `IO::ARGF#read` should always return `i32`. ([#10828](https://github.com/crystal-lang/crystal/pull/10828), thanks @stakach)\n- Fix `HTTP::Cookie` parse quoted cookie value. ([#10853](https://github.com/crystal-lang/crystal/pull/10853), thanks @straight-shoota)\n- Add `Socket::Addrinfo#inspect` ([#10775](https://github.com/crystal-lang/crystal/pull/10775), thanks @straight-shoota)\n\n#### System\n\n- Fix sentence structure in `process.cr`. ([#9259](https://github.com/crystal-lang/crystal/pull/9259), thanks @matthewmcgarvey)\n\n#### Runtime\n\n- Implement segfault handler in Crystal. ([#10463](https://github.com/crystal-lang/crystal/pull/10463), thanks @maxfierke)\n- Improve documentation for `Pointer.malloc` and `GC` methods. ([#10644](https://github.com/crystal-lang/crystal/pull/10644), thanks @straight-shoota)\n- Add links to literal types in the language reference. ([#10827](https://github.com/crystal-lang/crystal/pull/10827), thanks @straight-shoota)\n\n#### Serialization\n\n- Add docs for some json methods. ([#10257](https://github.com/crystal-lang/crystal/pull/10257), thanks @rdp)\n- Add `UUID.from_json_object_key?`. ([#10517](https://github.com/crystal-lang/crystal/pull/10517), thanks @kalinon)\n- Fix `JSON::Lexer`'s UTF-16 escape sequence parsing. ([#10450](https://github.com/crystal-lang/crystal/pull/10450), thanks @HertzDevil)\n- Fix `YAML::Serializable.use_yaml_discriminator` with typed enum. ([#10460](https://github.com/crystal-lang/crystal/pull/10460), thanks @straight-shoota)\n- Fix YAML to not parse empty string as `nil`. ([#10608](https://github.com/crystal-lang/crystal/pull/10608), thanks @straight-shoota)\n- Add `UUID` to yaml parsing. ([#10715](https://github.com/crystal-lang/crystal/pull/10715), thanks @kalinon)\n- Fix double flushing json/yaml builders. ([#10716](https://github.com/crystal-lang/crystal/pull/10716), thanks @matthewmcgarvey)\n\n#### Specs\n\n- Add spec helper `it_iterates` for iteration methods. ([#10158](https://github.com/crystal-lang/crystal/pull/10158), [#10797](https://github.com/crystal-lang/crystal/pull/10797), thanks @straight-shoota)\n- Add usage instructions for spec runner to compiler. ([#10046](https://github.com/crystal-lang/crystal/pull/10046), thanks @straight-shoota)\n- Fix: Handle invalid option errors on `crystal spec`. ([#10787](https://github.com/crystal-lang/crystal/pull/10787), thanks @hugopl)\n- Include `spec/**` in docs_main. ([#10863](https://github.com/crystal-lang/crystal/pull/10863), thanks @straight-shoota)\n\n### Compiler\n\n- Add support for type var splats inside `Tuple` during generic parameter substitution. ([#10232](https://github.com/crystal-lang/crystal/pull/10232), thanks @HertzDevil)\n- Fix: consider free vars in parameters of abstract def implementations before existing types, in particular fixing the creation of empty types. ([#10503](https://github.com/crystal-lang/crystal/pull/10503), thanks @HertzDevil)\n- Replace `Crystal::Type#covariant?` with `#implements?` ([#10507](https://github.com/crystal-lang/crystal/pull/10507), thanks @HertzDevil)\n- Fix error message when default parameter value doesn't match non-type restriction. ([#10515](https://github.com/crystal-lang/crystal/pull/10515), thanks @HertzDevil)\n- Fix type restriction logic for generic module instances. ([#10519](https://github.com/crystal-lang/crystal/pull/10519), thanks @HertzDevil)\n- Fix logic for subclass restricted against uninstantiated nested generic superclass. ([#10522](https://github.com/crystal-lang/crystal/pull/10522), [#10560](https://github.com/crystal-lang/crystal/pull/10560), thanks @HertzDevil)\n- Fix: eliminate extraneous types in certain non-commutative unions. ([#10527](https://github.com/crystal-lang/crystal/pull/10527), thanks @HertzDevil)\n- Fix: exclude variables' final types inside `while true` if re-assigned before first break. ([#10538](https://github.com/crystal-lang/crystal/pull/10538), thanks @HertzDevil)\n- Make `Pointer(T)#value=` even stricter for generic arguments. ([#10553](https://github.com/crystal-lang/crystal/pull/10553), thanks @HertzDevil)\n- Fix body locations for def nodes that have default args . ([#10619](https://github.com/crystal-lang/crystal/pull/10619), thanks @oprypin)\n- Fix call nodes' location after transforming its splats. ([#10620](https://github.com/crystal-lang/crystal/pull/10620), thanks @oprypin)\n- Fix `check_type_allowed_as_proc_argument` to show the type name. ([#10688](https://github.com/crystal-lang/crystal/pull/10688), thanks @straight-shoota)\n- Add free variables to \"no overload matches\" errors. ([#10692](https://github.com/crystal-lang/crystal/pull/10692), thanks @HertzDevil)\n- Fix: make virtual unbound types also unbound. ([#10704](https://github.com/crystal-lang/crystal/pull/10704), thanks @HertzDevil)\n- Fix: run instance variable initializers on instantiated generic superclasses only. ([#10729](https://github.com/crystal-lang/crystal/pull/10729), thanks @HertzDevil)\n- Fix: allow `previous_def` to init superclass's non-nilable ivars. ([#10733](https://github.com/crystal-lang/crystal/pull/10733), thanks @HertzDevil)\n- Fix: Use only last sub-expression of `Expressions` nodes for conditional type filters. ([#10738](https://github.com/crystal-lang/crystal/pull/10738), thanks @HertzDevil)\n- Fix: Don't compute type filters inside `typeof`'s argument. ([#10739](https://github.com/crystal-lang/crystal/pull/10739), thanks @HertzDevil)\n- Fix: Devirtualize types in `TypeNode#==(other : TypeNode)` and `#!=`. ([#10742](https://github.com/crystal-lang/crystal/pull/10742), thanks @HertzDevil)\n- Fix exit types of variables assigned inside `while` conditions. ([#10759](https://github.com/crystal-lang/crystal/pull/10759), thanks @HertzDevil)\n- Fix logic for `responds_to?` of generic module instances. ([#10760](https://github.com/crystal-lang/crystal/pull/10760), thanks @HertzDevil)\n- Add support for accessing a common value of a union type. ([#10770](https://github.com/crystal-lang/crystal/pull/10770), thanks @asterite)\n- Fix subtype relation when generic type variable is a virtual abstract struct. ([#10779](https://github.com/crystal-lang/crystal/pull/10779), thanks @HertzDevil)\n- Fix array literals consisting entirely of splat expansions. ([#10792](https://github.com/crystal-lang/crystal/pull/10792), thanks @HertzDevil)\n- Fix parsing macro literal containing char literal. ([#10799](https://github.com/crystal-lang/crystal/pull/10799), thanks @straight-shoota)\n- Refactor: Use type instead of `is_a?` in filters. ([#10815](https://github.com/crystal-lang/crystal/pull/10815), thanks @caspiano)\n- Expand named macro expression arguments before outer macro call expansion. ([#10819](https://github.com/crystal-lang/crystal/pull/10819), thanks @HertzDevil)\n- Be more strict about printing operator calls as short forms. ([#10825](https://github.com/crystal-lang/crystal/pull/10825), thanks @HertzDevil)\n- Fix union logic between metaclasses of uninstantiated generic classes in same hierarchy. ([#10832](https://github.com/crystal-lang/crystal/pull/10832), thanks @HertzDevil)\n- Fix uninstantiated generic classes casting to themselves. ([#10883](https://github.com/crystal-lang/crystal/pull/10883), thanks @HertzDevil)\n- Allow underscore in block return type even if the type can't be computed ([#10933](https://github.com/crystal-lang/crystal/pull/10933), thanks @asterite)\n- Fix parser identifies call with named args as var ([#10842](https://github.com/crystal-lang/crystal/pull/10842), thanks @straight-shoota)\n\n### Tools\n\n#### Formatter\n\n- Fix: allow trailing space in parenthesized unions. ([#10595](https://github.com/crystal-lang/crystal/pull/10595), thanks @HertzDevil)\n- Fix: don't consume newline after endless range literals. ([#10596](https://github.com/crystal-lang/crystal/pull/10596), thanks @HertzDevil)\n- Fix indentation of heredocs relative to delimiter. ([#10806](https://github.com/crystal-lang/crystal/pull/10806), thanks @HertzDevil)\n- Fix heredoc indent with outer indent. ([#10867](https://github.com/crystal-lang/crystal/pull/10867), thanks @straight-shoota)\n\n#### Doc generator\n\n- Fix escaping of argument lists in doc generator, expose JSON. ([#10109](https://github.com/crystal-lang/crystal/pull/10109), [#10821](https://github.com/crystal-lang/crystal/pull/10821), thanks @oprypin and @Sija)\n- Print named generic type arguments of type restrictions in docs. ([#10424](https://github.com/crystal-lang/crystal/pull/10424), thanks @HertzDevil)\n- Fix: respect overload order between methods. ([#10609](https://github.com/crystal-lang/crystal/pull/10609), thanks @HertzDevil)\n- Fix `PropagateDocVisitor` visit macro def. ([#10634](https://github.com/crystal-lang/crystal/pull/10634), thanks @straight-shoota)\n- Fix: remove superclass from `ASTNode` in API docs. ([#10664](https://github.com/crystal-lang/crystal/pull/10664), thanks @beta-ziliani)\n- **(breaking-change)** Remove deprecated `ditto` doc directive. ([#10755](https://github.com/crystal-lang/crystal/pull/10755), thanks @caspiano)\n  (Note that it was scheduled for removal since 0.34)\n- Fix: Restrict macro types' ancestors to `ASTNode`. ([#10722](https://github.com/crystal-lang/crystal/pull/10722), thanks @HertzDevil)\n- Fix docs generator search use `html_id`. ([#10875](https://github.com/crystal-lang/crystal/pull/10875), thanks @straight-shoota)\n- Fix `--sitemap-priority`, `--sitemap-changefreq`. ([#10906](https://github.com/crystal-lang/crystal/pull/10906), thanks @HertzDevil)\n\n### Others\n\n- CI: Update to use 1.0.0. ([#10533](https://github.com/crystal-lang/crystal/pull/10533), thanks @bcardiff)\n- Bump distribution-scripts. ([#10639](https://github.com/crystal-lang/crystal/pull/10639), [#10673](https://github.com/crystal-lang/crystal/pull/10673), [#10754](https://github.com/crystal-lang/crystal/pull/10754), thanks @straight-shoota and @bcardiff)\n- Fix contribution instructions. ([#10558](https://github.com/crystal-lang/crystal/pull/10558), thanks @straight-shoota)\n- Remove `.dockerignore`. ([#10642](https://github.com/crystal-lang/crystal/pull/10642), thanks @miry)\n- Add section about pull requests to the contributing guide. ([#10683](https://github.com/crystal-lang/crystal/pull/10683), thanks @straight-shoota)\n- Publish nightly builds to OBS. ([#10684](https://github.com/crystal-lang/crystal/pull/10684), thanks @straight-shoota)\n- Remove broken travis.yml config from `crystal init`. ([#10800](https://github.com/crystal-lang/crystal/pull/10800), thanks @straight-shoota)\n- Disable broken `test_darwin` job on circleci. ([#10823](https://github.com/crystal-lang/crystal/pull/10823), thanks @straight-shoota)\n- Update distribution-scripts for shards 0.15.0. ([#10862](https://github.com/crystal-lang/crystal/pull/10862), thanks @straight-shoota)\n- Add smoke tests for platforms where we don't run full tests ([#10848](https://github.com/crystal-lang/crystal/pull/10848), thanks @straight-shoota)\n"
  },
  {
    "path": "doc/changelogs/v1.10.md",
    "content": "# Changelog 1.10\n\n## [1.10.1] (2023-10-13)\n\n[1.10.1]: https://github.com/crystal-lang/crystal/releases/1.10.1\n\n### Bugfixes\n\n#### stdlib\n\n- `IO#gets` should have same result regardless of `#peek` availability ([#13882](https://github.com/crystal-lang/crystal/pull/13882), thanks @compumike)\n- Support Android API levels 24 - 27 ([#13884](https://github.com/crystal-lang/crystal/pull/13884), thanks @HertzDevil)\n\n### Infrastructure\n\n- _(ci)_ Fix `win.yml` ([#13876](https://github.com/crystal-lang/crystal/pull/13876), thanks @straight-shoota)\n\n## [1.10.0] (2023-10-09)\n\n[1.10.0]: https://github.com/crystal-lang/crystal/releases/1.10.0\n\n### Features\n\n#### lang\n\n- Add unlimited block unpacking ([#11597](https://github.com/crystal-lang/crystal/pull/11597), thanks @asterite)\n\n#### stdlib\n\n- Add more `Colorize::Mode` flags ([#13745](https://github.com/crystal-lang/crystal/pull/13745), thanks @HertzDevil)\n- _(collection)_ Add `Hash#put_if_absent` ([#13590](https://github.com/crystal-lang/crystal/pull/13590), thanks @HertzDevil)\n- _(collection)_ Add `Set#rehash` ([#13630](https://github.com/crystal-lang/crystal/pull/13630), thanks @HertzDevil)\n- _(collection)_ Add yield `key` in `Hash#transform_values` and `value` in `#transform_keys` ([#13608](https://github.com/crystal-lang/crystal/pull/13608), thanks @baseballlover723)\n- _(crypto)_ Upgrade SSL defaults to Mozilla guidelines version 5.7 ([#13685](https://github.com/crystal-lang/crystal/pull/13685), thanks @straight-shoota)\n- _(crypto)_ **[security]** Allow OpenSSL clients to choose cipher ([#13695](https://github.com/crystal-lang/crystal/pull/13695), thanks @carlhoerberg)\n- _(files)_ Add `File#rename` ([#13640](https://github.com/crystal-lang/crystal/pull/13640), thanks @carlhoerberg)\n- _(llvm)_ Support LLVM 17 ([#13782](https://github.com/crystal-lang/crystal/pull/13782), thanks @HertzDevil)\n- _(networking)_ Add overloads for `URI::Params.encode` with `IO` parameter ([#13798](https://github.com/crystal-lang/crystal/pull/13798), thanks @jwoertink)\n- _(numeric)_ Add `Complex#to_i128`, `Complex#to_u128` ([#13838](https://github.com/crystal-lang/crystal/pull/13838), thanks @HertzDevil)\n- _(runtime)_ Add additional fields to `GC:ProfStats` ([#13734](https://github.com/crystal-lang/crystal/pull/13734), thanks @carlhoerberg)\n- _(serialization)_ Support YAML deserialization of 128-bit integers ([#13834](https://github.com/crystal-lang/crystal/pull/13834), thanks @HertzDevil)\n- _(serialization)_ Support 128-bit integers in `JSON::PullParser#read?` ([#13837](https://github.com/crystal-lang/crystal/pull/13837), thanks @HertzDevil)\n- _(specs)_ **[breaking]** Change spec runner to exit with failure for `focus: true` ([#13653](https://github.com/crystal-lang/crystal/pull/13653), thanks @straight-shoota)\n- _(text)_ Add `String#byte_index(Char)` ([#13819](https://github.com/crystal-lang/crystal/pull/13819), thanks @funny-falcon)\n- _(time)_ Support Android's system timezone database ([#13666](https://github.com/crystal-lang/crystal/pull/13666), thanks @HertzDevil)\n\n#### compiler\n\n- Experimental: Add `Slice.literal` for numeric slice constants ([#13716](https://github.com/crystal-lang/crystal/pull/13716), thanks @HertzDevil)\n\n#### tools\n\n- Add `tool unreachable` ([#13783](https://github.com/crystal-lang/crystal/pull/13783), thanks @straight-shoota)\n- _(dependencies)_ Add `crystal tool dependencies` ([#13631](https://github.com/crystal-lang/crystal/pull/13631), thanks @straight-shoota)\n- _(docs-generator)_ Add CSS for tables ([#13822](https://github.com/crystal-lang/crystal/pull/13822), thanks @nobodywasishere)\n- _(hierarchy)_ Support generic types in `crystal tool hierarchy` ([#13715](https://github.com/crystal-lang/crystal/pull/13715), thanks @HertzDevil)\n- _(playground)_ Update octicons to v19.5.0 ([#13738](https://github.com/crystal-lang/crystal/pull/13738), thanks @GeopJr)\n\n### Bugfixes\n\n#### lang\n\n- _(macros)_ Fix missing normalization of macro expressions (and others) ([#13709](https://github.com/crystal-lang/crystal/pull/13709), thanks @asterite)\n- _(macros)_ Fix block parameter unpacking inside macros ([#13813](https://github.com/crystal-lang/crystal/pull/13813), thanks @HertzDevil)\n\n#### stdlib\n\n- _(collection)_ **[breaking]** Mark the return type of methods such as `Slice#copy_to` as `Nil` ([#13774](https://github.com/crystal-lang/crystal/pull/13774), thanks @erdian718)\n- _(files)_ Change `IO::Buffered#peek`'s return type to `Bytes` ([#13863](https://github.com/crystal-lang/crystal/pull/13863), thanks @HertzDevil)\n- _(llvm)_ Chop git suffix from `LibLLVM::VERSION` ([#13699](https://github.com/crystal-lang/crystal/pull/13699), thanks @HOMODELUNA)\n- _(macros)_ Do not add trailing `+` in `TypeNode#id` for virtual types ([#13708](https://github.com/crystal-lang/crystal/pull/13708), thanks @HertzDevil)\n- _(numeric)_ Fix `BigDecimal#round` for large digit counts in base 10 ([#13811](https://github.com/crystal-lang/crystal/pull/13811), thanks @HertzDevil)\n- _(serialization)_ Set encoding in `XML.parse_html` explicitly to UTF-8 ([#13705](https://github.com/crystal-lang/crystal/pull/13705), thanks @straight-shoota)\n- _(serialization)_ Fix error message when parsing unknown JSON enum value ([#13728](https://github.com/crystal-lang/crystal/pull/13728), thanks @willhbr)\n- _(serialization)_ Fix YAML scalar type validation error message ([#13771](https://github.com/crystal-lang/crystal/pull/13771), thanks @MistressRemilia)\n- _(serialization)_ Fix incorrect overflow in `UInt64.from_yaml` ([#13829](https://github.com/crystal-lang/crystal/pull/13829), thanks @HertzDevil)\n- _(system)_ Fix `Process.new` with nilable chdir parameter on Windows ([#13768](https://github.com/crystal-lang/crystal/pull/13768), thanks @straight-shoota)\n- _(system)_ Fix typo in unistd.cr ([#13850](https://github.com/crystal-lang/crystal/pull/13850), thanks @kojix2)\n- _(text)_ Fix `Char::Reader#each` bounds check after block ([#13817](https://github.com/crystal-lang/crystal/pull/13817), thanks @straight-shoota)\n- _(text)_ Minor fixup for `HTML.decode_codepoint` ([#13843](https://github.com/crystal-lang/crystal/pull/13843), thanks @straight-shoota)\n\n#### compiler\n\n- **[breaking]** Remove double `.cr.cr` extension in `require` path lookup ([#13749](https://github.com/crystal-lang/crystal/pull/13749), thanks @straight-shoota)\n- _(parser)_ Fix end location for `FunDef` ([#13789](https://github.com/crystal-lang/crystal/pull/13789), thanks @straight-shoota)\n- _(semantic)_ Fix lookup scope for `@[Primitive]` def's return type ([#13658](https://github.com/crystal-lang/crystal/pull/13658), thanks @HertzDevil)\n- _(semantic)_ Fix typo in call_error.cr ([#13764](https://github.com/crystal-lang/crystal/pull/13764), thanks @kojix2)\n\n#### tools\n\n- _(docs-generator)_ Fix octicon-link icon color on dark mode ([#13670](https://github.com/crystal-lang/crystal/pull/13670), thanks @GeopJr)\n- _(docs-generator)_ Allow word breaks between module names in docs ([#13827](https://github.com/crystal-lang/crystal/pull/13827), thanks @nobodywasishere)\n- _(docs-generator)_ Fix docs dark mode dropdown background on blink ([#13840](https://github.com/crystal-lang/crystal/pull/13840), thanks @GeopJr)\n- _(init)_ Fix shard crystal version in `crystal init` ([#13730](https://github.com/crystal-lang/crystal/pull/13730), thanks @xendk)\n- _(hierarchy)_: Fix byte sizes for `Proc`s inside extern structs ([#13711](https://github.com/crystal-lang/crystal/pull/13711), thanks @HertzDevil)\n\n### Performance\n\n#### stdlib\n\n- Optimize `IO::Delimited` ([#11242](https://github.com/crystal-lang/crystal/pull/11242), thanks @asterite)\n- _(crypto)_ Use `IO::DEFAULT_BUFFER_SIZE` in `Digest#update` ([#13635](https://github.com/crystal-lang/crystal/pull/13635), thanks @carlhoerberg)\n- _(crypto)_ Fix memory leak in `OpenSSL::SSL::Socket#peer_certificate` ([#13785](https://github.com/crystal-lang/crystal/pull/13785), thanks @compumike)\n- _(files)_ Optimize `IO#read_string(0)` ([#13732](https://github.com/crystal-lang/crystal/pull/13732), thanks @jgaskins)\n- _(files)_ Avoid double file buffering ([#13780](https://github.com/crystal-lang/crystal/pull/13780), thanks @carlhoerberg)\n- _(llvm)_ Refactor `LLVM.default_target_triple` to avoid regex ([#13659](https://github.com/crystal-lang/crystal/pull/13659), thanks @straight-shoota)\n- _(numeric)_ Pre-allocate Dragonbox cache array ([#13649](https://github.com/crystal-lang/crystal/pull/13649), thanks @HertzDevil)\n- _(runtime)_ Avoid realloc callstack array when unwinding ([#13781](https://github.com/crystal-lang/crystal/pull/13781), thanks @carlhoerberg)\n- _(time)_ Optimize the constructors of `Time::Span` ([#13807](https://github.com/crystal-lang/crystal/pull/13807), thanks @erdian718)\n\n### Refactor\n\n#### stdlib\n\n- Do not use nilable `Pointer`s ([#13710](https://github.com/crystal-lang/crystal/pull/13710), thanks @HertzDevil)\n- _(collection)_ Use `Set(T)` instead of `Hash(T, Bool)` ([#13611](https://github.com/crystal-lang/crystal/pull/13611), thanks @HertzDevil)\n- _(concurrency)_ Use `Fiber.inactive` inside `Fiber#run`'s `ensure` block ([#13701](https://github.com/crystal-lang/crystal/pull/13701), thanks @HertzDevil)\n- _(crypto)_ Use `JSON::Serializable` in `scripts/generate_ssl_server_defaults.cr` ([#13667](https://github.com/crystal-lang/crystal/pull/13667), thanks @HertzDevil)\n- _(crypto)_ Refactor narrow OpenSSL requires for digest implementations ([#13818](https://github.com/crystal-lang/crystal/pull/13818), thanks @straight-shoota)\n- _(networking)_ **[deprecation]** Add types to `HTTP::StaticFileHandler` ([#13778](https://github.com/crystal-lang/crystal/pull/13778), thanks @jkthorne)\n\n#### compiler\n\n- Restrict some boolean properties to `Bool` in the compiler ([#13614](https://github.com/crystal-lang/crystal/pull/13614), thanks @HertzDevil)\n\n### Documentation\n\n#### stdlib\n\n- _(crypto)_ Fix docs for `Digest::SHA512` ([#13796](https://github.com/crystal-lang/crystal/pull/13796), thanks @jgaskins)\n- _(files)_ Document `Dir#mkdir`, `Dir#exists?` ([#13795](https://github.com/crystal-lang/crystal/pull/13795), thanks @jkthorne)\n- _(networking)_ Add documentation for `HTTP::Headers#add` ([#13762](https://github.com/crystal-lang/crystal/pull/13762), thanks @jkthorne)\n- _(text)_ Fix typo in regex.cr ([#13751](https://github.com/crystal-lang/crystal/pull/13751), thanks @beta-ziliani)\n\n### Specs\n\n#### stdlib\n\n- _(numeric)_ Update specs for `Int::Primitive.from_json` ([#13835](https://github.com/crystal-lang/crystal/pull/13835), thanks @HertzDevil)\n- _(numeric)_ Remove overflowing `Float#to_u!` interpreter primitive specs ([#13737](https://github.com/crystal-lang/crystal/pull/13737), thanks @HertzDevil)\n- _(time)_ Clear `Time::Location` cache before `.load_android` specs ([#13718](https://github.com/crystal-lang/crystal/pull/13718), thanks @HertzDevil)\n\n### Infrastructure\n\n- Update previous Crystal release - 1.9.2 ([#13650](https://github.com/crystal-lang/crystal/pull/13650), thanks @straight-shoota)\n- Update distribution-scripts ([#13776](https://github.com/crystal-lang/crystal/pull/13776), thanks @straight-shoota)\n- make: Add `generate_data` target for running generator scripts ([#13700](https://github.com/crystal-lang/crystal/pull/13700), thanks @straight-shoota)\n- Add shell completions for `clear_cache` ([#13636](https://github.com/crystal-lang/crystal/pull/13636), thanks @straight-shoota)\n- New changelog format ([#13662](https://github.com/crystal-lang/crystal/pull/13662), thanks @straight-shoota)\n- Detect developer mode in Windows installer ([#13681](https://github.com/crystal-lang/crystal/pull/13681), thanks @HertzDevil)\n- Update PGP key link ([#13754](https://github.com/crystal-lang/crystal/pull/13754), thanks @syeopite)\n- Fix log format in update-distribution-scripts.sh ([#13777](https://github.com/crystal-lang/crystal/pull/13777), thanks @straight-shoota)\n- _(ci)_ Trigger windows release jobs on tag ([#13683](https://github.com/crystal-lang/crystal/pull/13683), thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#13748](https://github.com/crystal-lang/crystal/pull/13748), thanks @renovate)\n- _(ci)_ Refactor `crystal_bootstrap_version` ([#13845](https://github.com/crystal-lang/crystal/pull/13845), thanks @straight-shoota)\n"
  },
  {
    "path": "doc/changelogs/v1.11.md",
    "content": "# Changelog 1.11\n\n## [1.11.2] (2024-01-18)\n\n[1.11.2]: https://github.com/crystal-lang/crystal/releases/1.11.2\n\n### Bugfixes\n\n#### stdlib\n\n- _(files)_ Fix missing `cause` parameter from `IO::Error#initialize` ([#14242](https://github.com/crystal-lang/crystal/pull/14242), thanks @straight-shoota)\n- _(runtime)_ Always use `%p` for pointers in `Crystal::System.print_error` ([#14186](https://github.com/crystal-lang/crystal/pull/14186), thanks @HertzDevil)\n- _(runtime)_ Fixup for always use `%p` for pointers in `Crystal::System.print_error` ([#14221](https://github.com/crystal-lang/crystal/pull/14221), thanks @HertzDevil)\n\n### Infrastructure\n\n- Changelog for 1.11.2 ([#14249](https://github.com/crystal-lang/crystal/pull/14249), thanks @straight-shoota)\n\n## [1.11.1] (2024-01-11)\n\n[1.11.1]: https://github.com/crystal-lang/crystal/releases/1.11.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(crypto)_ Revert \"Fix OpenSSL error handling for EOF (support for OpenSSL 3.2) (#14169)\" ([#14217](https://github.com/crystal-lang/crystal/pull/14217), thanks @straight-shoota)\n\n#### compiler\n\n- _(interpreter)_ Remove pkg-config name for libgc as workaround for interpreter loader ([#14198](https://github.com/crystal-lang/crystal/pull/14198), thanks @straight-shoota)\n- _(semantic)_ Revert \"Add `ReferenceStorage` for manual allocation of references (#14106)\" ([#14207](https://github.com/crystal-lang/crystal/pull/14207), thanks @straight-shoota)\n\n### Infrastructure\n\n- Changelog for 1.11.1 ([#14208](https://github.com/crystal-lang/crystal/pull/14208), thanks @straight-shoota)\n- Bump VERSION to 1.11.1-dev ([#14197](https://github.com/crystal-lang/crystal/pull/14197), thanks @straight-shoota)\n\n## [1.11.0] (2024-01-08)\n\n[1.11.0]: https://github.com/crystal-lang/crystal/releases/1.11.0\n\n### Features\n\n#### lang\n\n- **[breaking]** Support `alignof` and `instance_alignof` ([#14087](https://github.com/crystal-lang/crystal/pull/14087), thanks @HertzDevil)\n- _(annotations)_ Support `dll` parameter in `@[Link]` ([#14131](https://github.com/crystal-lang/crystal/pull/14131), thanks @HertzDevil)\n- _(macros)_ Expose macro `Call` context via new `@caller` macro ivar ([#14048](https://github.com/crystal-lang/crystal/pull/14048), thanks @Blacksmoke16)\n\n#### stdlib\n\n- _(collection)_ Add `Enumerable#present?` ([#13866](https://github.com/crystal-lang/crystal/pull/13866), thanks @straight-shoota)\n- _(collection)_ Add `Enumerable#each_step` and `Iterable#each_step` ([#13610](https://github.com/crystal-lang/crystal/pull/13610), thanks @baseballlover723)\n- _(collection)_ Add `Enumerable(T)#to_set(& : T -> U) : Set(U) forall U` ([#12654](https://github.com/crystal-lang/crystal/pull/12654), thanks @caspiano)\n- _(collection)_ Add `Enumerable(T)#to_a(& : T -> U) forall U` ([#12653](https://github.com/crystal-lang/crystal/pull/12653), thanks @caspiano)\n- _(files)_ Add `IO::Error#target` ([#13865](https://github.com/crystal-lang/crystal/pull/13865), thanks @straight-shoota)\n- _(llvm)_ Add `LLVM::OperandBundleDef#dispose` ([#14095](https://github.com/crystal-lang/crystal/pull/14095), thanks @HertzDevil)\n- _(llvm)_ Windows: Use local configuration for LLVM when linking dynamically ([#14101](https://github.com/crystal-lang/crystal/pull/14101), thanks @HertzDevil)\n- _(macros)_ Add `CharLiteral#ord` ([#13910](https://github.com/crystal-lang/crystal/pull/13910), thanks @refi64)\n- _(macros)_ Add macro methods for `MacroIf` and `MacroFor` nodes ([#13902](https://github.com/crystal-lang/crystal/pull/13902), thanks @sbsoftware)\n- _(macros)_ Expose doc comments on `ASTNode` when generating docs ([#14050](https://github.com/crystal-lang/crystal/pull/14050), thanks @Blacksmoke16)\n- _(macros)_ Add macro methods for `ModuleDef` ([#14063](https://github.com/crystal-lang/crystal/pull/14063), thanks @HertzDevil)\n- _(macros)_ Add macro methods for `Include` and `Extend` ([#14064](https://github.com/crystal-lang/crystal/pull/14064), thanks @HertzDevil)\n- _(macros)_ Add macro methods for `ClassDef`, `EnumDef`, `AnnotationDef` ([#14072](https://github.com/crystal-lang/crystal/pull/14072), thanks @HertzDevil)\n- _(numeric)_ Implement `BigRational`'s rounding modes ([#13871](https://github.com/crystal-lang/crystal/pull/13871), thanks @HertzDevil)\n- _(numeric)_ Support full exponent range in `BigFloat#**(BigInt)` ([#13881](https://github.com/crystal-lang/crystal/pull/13881), thanks @HertzDevil)\n- _(numeric)_ Add `Math.fma` ([#13934](https://github.com/crystal-lang/crystal/pull/13934), thanks @HertzDevil)\n- _(numeric)_ Add `Number#integer?` ([#13936](https://github.com/crystal-lang/crystal/pull/13936), thanks @HertzDevil)\n- _(numeric)_ Publish `Int::Primitive#abs_unsigned` and `#neg_signed` ([#13938](https://github.com/crystal-lang/crystal/pull/13938), thanks @HertzDevil)\n- _(numeric)_ Add `Int::Primitive#to_signed`, `#to_signed!`, `#to_unsigned`, `#to_unsigned!` ([#13960](https://github.com/crystal-lang/crystal/pull/13960), thanks @HertzDevil)\n- _(numeric)_ Support `BigFloat#**` for all `Int::Primitive` arguments ([#13971](https://github.com/crystal-lang/crystal/pull/13971), thanks @HertzDevil)\n- _(numeric)_ Add `Float32::MIN_SUBNORMAL` and `Float64::MIN_SUBNORMAL` ([#13961](https://github.com/crystal-lang/crystal/pull/13961), thanks @HertzDevil)\n- _(numeric)_ Add `Float::Primitive.parse_hexfloat`, `.parse_hexfloat?`, `#to_hexfloat` ([#14027](https://github.com/crystal-lang/crystal/pull/14027), thanks @HertzDevil)\n- _(numeric)_ Implement `sprintf \"%f\"` in Crystal using Ryu Printf ([#14067](https://github.com/crystal-lang/crystal/pull/14067), thanks @HertzDevil)\n- _(numeric)_ Implement `sprintf \"%e\"` in Crystal ([#14084](https://github.com/crystal-lang/crystal/pull/14084), thanks @HertzDevil)\n- _(numeric)_ Implement `sprintf \"%a\"` in Crystal ([#14102](https://github.com/crystal-lang/crystal/pull/14102), thanks @HertzDevil)\n- _(numeric)_ Implement `sprintf \"%g\"` in Crystal ([#14123](https://github.com/crystal-lang/crystal/pull/14123), thanks @HertzDevil)\n- _(runtime)_ Add `Crystal::HOST_TRIPLE` and `TARGET_TRIPLE` ([#13823](https://github.com/crystal-lang/crystal/pull/13823), thanks @HertzDevil)\n- _(runtime)_ **[experimental]** Add `Reference.pre_initialize` and `.unsafe_construct` ([#14108](https://github.com/crystal-lang/crystal/pull/14108), thanks @HertzDevil)\n- _(runtime)_ **[experimental]** Add `ReferenceStorage` for manual allocation of references ([#14106](https://github.com/crystal-lang/crystal/pull/14106), thanks @HertzDevil)\n- _(serialization)_ Fix `StaticArray#to_json` ([#14104](https://github.com/crystal-lang/crystal/pull/14104), thanks @Vendicated)\n- _(specs)_ Add `crystal spec --dry-run` ([#13804](https://github.com/crystal-lang/crystal/pull/13804), thanks @nobodywasishere)\n- _(specs)_ Add `crystal spec --list-tags` ([#13616](https://github.com/crystal-lang/crystal/pull/13616), thanks @baseballlover723)\n- _(system)_ Respect Windows `Path` directory separators in `File.match?` ([#13912](https://github.com/crystal-lang/crystal/pull/13912), thanks @HertzDevil)\n- _(text)_ Support Unicode 15.1.0 ([#13812](https://github.com/crystal-lang/crystal/pull/13812), thanks @HertzDevil)\n- _(text)_ Add `UUID.v1`, `.v2`, `.v3`, `.v4`, `.v5` ([#13693](https://github.com/crystal-lang/crystal/pull/13693), thanks @threez)\n- _(text)_ Add `String` and `Char` patterns to `StringScanner` ([#13806](https://github.com/crystal-lang/crystal/pull/13806), thanks @funny-falcon)\n- _(text)_ Add `EOL`constant (End-Of-Line) ([#11303](https://github.com/crystal-lang/crystal/pull/11303), thanks @postmodern)\n- _(text)_ Add `Char::Reader#current_char?`, `#next_char?`, `#previous_char?` ([#14012](https://github.com/crystal-lang/crystal/pull/14012), thanks @HertzDevil)\n- _(text)_ Add `String#matches_full?` ([#13968](https://github.com/crystal-lang/crystal/pull/13968), thanks @straight-shoota)\n- _(text)_ Change `Regex::MatchData#to_s` to return matched substring ([#14115](https://github.com/crystal-lang/crystal/pull/14115), thanks @Vendicated)\n\n#### compiler\n\n- _(codegen)_ Add incremental optimization levels ([#13464](https://github.com/crystal-lang/crystal/pull/13464), thanks @kostya)\n- _(debugger)_ Support debug information for 64-bit or unsigned enums ([#14081](https://github.com/crystal-lang/crystal/pull/14081), thanks @HertzDevil)\n- _(interpreter)_ Support `instance_sizeof(T)` in the interpreter ([#14031](https://github.com/crystal-lang/crystal/pull/14031), thanks @HertzDevil)\n- _(interpreter)_ Support `-dynamic.lib` in Windows interpreter ([#14143](https://github.com/crystal-lang/crystal/pull/14143), thanks @HertzDevil)\n- _(interpreter)_ Support absolute paths in `CRYSTAL_INTERPRETER_LOADER_INFO` ([#14147](https://github.com/crystal-lang/crystal/pull/14147), thanks @HertzDevil)\n- _(interpreter)_ Add `Crystal::Repl#parse_and_interpret` ([#14138](https://github.com/crystal-lang/crystal/pull/14138), thanks @bcardiff)\n- _(semantic)_ Change short_reference for top-level methods to `::foo` ([#14071](https://github.com/crystal-lang/crystal/pull/14071), thanks @keshavbiswa)\n\n#### tools\n\n- _(docs-generator)_ Expose inherited macros in generated API docs ([#13810](https://github.com/crystal-lang/crystal/pull/13810), thanks @Blacksmoke16)\n- _(docs-generator)_ Order macros below class methods in generated docs ([#14024](https://github.com/crystal-lang/crystal/pull/14024), thanks @Blacksmoke16)\n- _(formatter)_ Do not remove trailing comma from multi-line macro/def parameters (not yet enabled) ([#14075](https://github.com/crystal-lang/crystal/pull/14075), thanks @Blacksmoke16)\n- _(unreachable)_ Add `--check` flag to `crystal tool unreachable` ([#13930](https://github.com/crystal-lang/crystal/pull/13930), thanks @straight-shoota)\n- _(unreachable)_ Add annotations to output of `crystal tool unreachable` ([#13927](https://github.com/crystal-lang/crystal/pull/13927), thanks @straight-shoota)\n- _(unreachable)_ Print relative paths in `crystal tool unreachable` ([#13929](https://github.com/crystal-lang/crystal/pull/13929), thanks @straight-shoota)\n- _(unreachable)_ Add CSV output format to `crystal tool unreachable` ([#13926](https://github.com/crystal-lang/crystal/pull/13926), thanks @straight-shoota)\n- _(unreachable)_ Add `--tallies` option to `crystal tool unreachable` ([#13969](https://github.com/crystal-lang/crystal/pull/13969), thanks @straight-shoota)\n\n### Bugfixes\n\n#### stdlib\n\n- Fix `Box(T?)` crashing on `nil` ([#13893](https://github.com/crystal-lang/crystal/pull/13893), thanks @HertzDevil)\n- Fix typos in src ([#14053](https://github.com/crystal-lang/crystal/pull/14053), thanks @kojix2)\n- _(collection)_ Fix `Indexable#each_repeated_combination(n)` when `n > size` ([#14092](https://github.com/crystal-lang/crystal/pull/14092), thanks @HertzDevil)\n- _(concurrency)_ Make `Process#wait` asynchronous on Windows ([#13908](https://github.com/crystal-lang/crystal/pull/13908), thanks @HertzDevil)\n- _(concurrency)_ Fix math overflow after spawning `Int32::MAX + 1` fibers ([#14096](https://github.com/crystal-lang/crystal/pull/14096), thanks @ysbaddaden)\n- _(concurrency)_ Fix `can't resume a running fiber` ([#14128](https://github.com/crystal-lang/crystal/pull/14128), thanks @ysbaddaden)\n- _(crypto)_ Fix OpenSSL error handling for EOF (support for OpenSSL 3.2) ([#14169](https://github.com/crystal-lang/crystal/pull/14169), thanks @straight-shoota)\n- _(files)_ Fix `Globber.constant_entry?` matching patterns ([#13955](https://github.com/crystal-lang/crystal/pull/13955), thanks @GeopJr)\n- _(files)_ Fix `String::Buffer` and `IO::Memory` capacity to grow beyond 1GB ([#13989](https://github.com/crystal-lang/crystal/pull/13989), thanks @straight-shoota)\n- _(llvm)_ Fix a typo ([#13914](https://github.com/crystal-lang/crystal/pull/13914), thanks @kojix2)\n- _(numeric)_ Make `String#to_f(whitespace: false)` work with infinity and NaN ([#13875](https://github.com/crystal-lang/crystal/pull/13875), thanks @HertzDevil)\n- _(numeric)_ Use `LibGMP::SI` and `UI` for size checks, not `Long` and `ULong` ([#13874](https://github.com/crystal-lang/crystal/pull/13874), thanks @HertzDevil)\n- _(numeric)_ Fix integral part extraction in `Number#format` ([#14061](https://github.com/crystal-lang/crystal/pull/14061), thanks @HertzDevil)\n- _(numeric)_ Fix out-of-bounds access in `Int128::MIN.to_s(base: 2)` ([#14119](https://github.com/crystal-lang/crystal/pull/14119), thanks @HertzDevil)\n- _(numeric)_ Avoid double rounding in `Float#format` for nonnegative `decimal_place` ([#14129](https://github.com/crystal-lang/crystal/pull/14129), thanks @HertzDevil)\n- _(runtime)_ Avoid `@[ThreadLocal]` on Android ([#14025](https://github.com/crystal-lang/crystal/pull/14025), thanks @HertzDevil)\n- _(runtime)_ Never use string interpolation in `Crystal::System.print_error` ([#14114](https://github.com/crystal-lang/crystal/pull/14114), thanks @HertzDevil)\n- _(runtime)_ Fix segfault with next boehm gc (after v8.2.4) ([#14130](https://github.com/crystal-lang/crystal/pull/14130), thanks @ysbaddaden)\n- _(specs)_ Skip spec execution on error exit ([#13986](https://github.com/crystal-lang/crystal/pull/13986), thanks @straight-shoota)\n- _(system)_ Fix `FileUtils.ln_sf` to override special file types ([#13896](https://github.com/crystal-lang/crystal/pull/13896), thanks @straight-shoota)\n- _(system)_ Fix `Process.exists?` throwing errors on EPERM ([#13911](https://github.com/crystal-lang/crystal/pull/13911), thanks @refi64)\n- _(system)_ Fix portable shell command arguments in `Process#prepare_args` ([#13942](https://github.com/crystal-lang/crystal/pull/13942), thanks @GeopJr)\n- _(system)_ Windows: Do not close process handle in `Process#close` ([#13997](https://github.com/crystal-lang/crystal/pull/13997), thanks @HertzDevil)\n- _(system)_ Windows: clear `Crystal::System::Process#@completion_key` after use ([#14068](https://github.com/crystal-lang/crystal/pull/14068), thanks @HertzDevil)\n- _(system)_ Fix UTF-8 console input on Windows ([#13758](https://github.com/crystal-lang/crystal/pull/13758), thanks @erdian718)\n- _(text)_ Fix invalid UTF-8 handling in `Char::Reader#previous_char` ([#14013](https://github.com/crystal-lang/crystal/pull/14013), thanks @HertzDevil)\n- _(text)_ Fix `options` parameter for `String#split`, `#scan` ([#14183](https://github.com/crystal-lang/crystal/pull/14183), thanks @straight-shoota)\n- _(time)_ Fix time span overflow on `Int#milliseconds` and `Int#microseconds` ([#14105](https://github.com/crystal-lang/crystal/pull/14105), thanks @bcardiff)\n\n#### compiler\n\n- _(cli)_ Remove unnecessary file check for CLI arguments ([#13853](https://github.com/crystal-lang/crystal/pull/13853), thanks @straight-shoota)\n- _(cli)_ Check for invalid integers in compiler's CLI ([#13959](https://github.com/crystal-lang/crystal/pull/13959), thanks @HertzDevil)\n- _(cli)_ Fix compiler error message for invalid source file ([#14157](https://github.com/crystal-lang/crystal/pull/14157), thanks @straight-shoota)\n- _(codegen)_ Fix a typo in compiler source ([#14054](https://github.com/crystal-lang/crystal/pull/14054), thanks @kojix2)\n- _(codegen)_ Fix codegen error when discarding `is_a?` or `responds_to?`'s result ([#14148](https://github.com/crystal-lang/crystal/pull/14148), thanks @HertzDevil)\n- _(interpreter)_ Fix element alignment of `Tuple` and `NamedTuple` casts ([#14040](https://github.com/crystal-lang/crystal/pull/14040), thanks @HertzDevil)\n- _(interpreter)_ `Crystal::Loader`: Skip second linker member on Windows if absent ([#14111](https://github.com/crystal-lang/crystal/pull/14111), thanks @HertzDevil)\n- _(parser)_ Support `%r` and `%x` when not followed by delimiter start ([#13933](https://github.com/crystal-lang/crystal/pull/13933), thanks @HertzDevil)\n- _(parser)_ Fix location of global `Path` nodes in certain constructs ([#13932](https://github.com/crystal-lang/crystal/pull/13932), thanks @HertzDevil)\n- _(parser)_ Fix `ToSVisitor` for expanded string interpolation in backticks ([#13943](https://github.com/crystal-lang/crystal/pull/13943), thanks @straight-shoota)\n- _(parser)_ Fix location for \"invalid trailing comma in call\" errors ([#13964](https://github.com/crystal-lang/crystal/pull/13964), thanks @HertzDevil)\n- _(semantic)_ Fix check for file type ([#13760](https://github.com/crystal-lang/crystal/pull/13760), thanks @straight-shoota)\n- _(semantic)_ Fix private type definitions with namespaced `Path`s ([#13931](https://github.com/crystal-lang/crystal/pull/13931), thanks @HertzDevil)\n- _(semantic)_ Fix missing param count in compilation error message ([#13985](https://github.com/crystal-lang/crystal/pull/13985), thanks @koffeinfrei)\n- _(semantic)_ Fix `ReadInstanceVar` on typedefs ([#14044](https://github.com/crystal-lang/crystal/pull/14044), thanks @HertzDevil)\n- _(semantic)_ Fix global `Path` lookup inside macro when def has free variables ([#14073](https://github.com/crystal-lang/crystal/pull/14073), thanks @HertzDevil)\n- _(semantic)_ Add location information to implicit flag enum members ([#14127](https://github.com/crystal-lang/crystal/pull/14127), thanks @Blacksmoke16)\n\n#### tools\n\n- _(docs-generator)_ Fix `crystal docs` check `File.exists?` for `shard.yml` ([#13937](https://github.com/crystal-lang/crystal/pull/13937), thanks @straight-shoota)\n- _(docs-generator)_ Fix version sorting in API docs ([#13994](https://github.com/crystal-lang/crystal/pull/13994), thanks @m-o-e)\n- _(docs-generator)_ Strip whitespace in doc comment before determining summary line ([#14049](https://github.com/crystal-lang/crystal/pull/14049), thanks @Blacksmoke16)\n- _(docs-generator)_ Skip `Crystal::Macros` unless generating docs ([#13970](https://github.com/crystal-lang/crystal/pull/13970), thanks @straight-shoota)\n- _(init)_ Fix tool init error message when target exists but not a dir ([#13869](https://github.com/crystal-lang/crystal/pull/13869), thanks @straight-shoota)\n- _(unreachable)_ Fix infinite recursion of expanded nodes in `UnreachableVisitor` ([#13922](https://github.com/crystal-lang/crystal/pull/13922), thanks @straight-shoota)\n\n### Chores\n\n#### lang\n\n- _(macros)_ **[deprecation]** Deprecate the splat operators in macro expressions ([#13939](https://github.com/crystal-lang/crystal/pull/13939), thanks @HertzDevil)\n\n#### stdlib\n\n- _(llvm)_ **[deprecation]** Deprecate `LLVM.start_multithreaded` and `.stop_multithreaded` ([#13949](https://github.com/crystal-lang/crystal/pull/13949), thanks @HertzDevil)\n\n### Performance\n\n#### stdlib\n\n- _(concurrency)_ Skip indirections in `Crystal::Scheduler` ([#14098](https://github.com/crystal-lang/crystal/pull/14098), thanks @ysbaddaden)\n- _(numeric)_ Optimize `BigInt#&`, `#|`, `#^` with `Int::Primitive` arguments ([#14006](https://github.com/crystal-lang/crystal/pull/14006), thanks @HertzDevil)\n- _(numeric)_ Optimize `BigInt#bit` ([#13980](https://github.com/crystal-lang/crystal/pull/13980), thanks @HertzDevil)\n- _(numeric)_ Use `#trailing_zeros_count` in `Int#gcd` ([#14069](https://github.com/crystal-lang/crystal/pull/14069), thanks @HertzDevil)\n- _(serialization)_ Optimize `JSON::Builder#string` with byte-based algorithm ([#13915](https://github.com/crystal-lang/crystal/pull/13915), thanks @straight-shoota)\n- _(serialization)_ Improve performance of `JSON::Builder#string` with direct stringification ([#13950](https://github.com/crystal-lang/crystal/pull/13950), thanks @straight-shoota)\n- _(text)_ Refactor `HTML.unescape` in native Crystal ([#13844](https://github.com/crystal-lang/crystal/pull/13844), thanks @straight-shoota)\n- _(text)_ Refactor some uses of the blockless `String#split` ([#14001](https://github.com/crystal-lang/crystal/pull/14001), thanks @HertzDevil)\n\n### Refactor\n\n#### stdlib\n\n- _(concurrency)_ Add `Crystal::System::Thread` ([#13814](https://github.com/crystal-lang/crystal/pull/13814), thanks @HertzDevil)\n- _(concurrency)_ Move `Thread#set_current_thread` to `Fiber` ([#14099](https://github.com/crystal-lang/crystal/pull/14099), thanks @ysbaddaden)\n- _(files)_ Use `IO.copy` in `IO#gets_to_end` ([#13990](https://github.com/crystal-lang/crystal/pull/13990), thanks @straight-shoota)\n- _(files)_ Do not use `pointerof(Path)` in the standard library ([#14144](https://github.com/crystal-lang/crystal/pull/14144), thanks @HertzDevil)\n- _(llvm)_ **[deprecation]** Remove `LLVMExtSetCurrentDebugLocation` from `llvm_ext.cc` for LLVM 9+ ([#13965](https://github.com/crystal-lang/crystal/pull/13965), thanks @HertzDevil)\n- _(llvm)_ Replace some deprecated LLVM bindings ([#13953](https://github.com/crystal-lang/crystal/pull/13953), thanks @HertzDevil)\n- _(llvm)_ Split `LibLLVM` by C headers ([#13948](https://github.com/crystal-lang/crystal/pull/13948), thanks @HertzDevil)\n- _(llvm)_ Support `LLVMSetTargetMachineGlobalISel` from LLVM 18 ([#14079](https://github.com/crystal-lang/crystal/pull/14079), thanks @HertzDevil)\n- _(llvm)_ Support the operand bundle API from LLVM 18 ([#14082](https://github.com/crystal-lang/crystal/pull/14082), thanks @HertzDevil)\n- _(numeric)_ Simplify `String::Formatter` when Ryu Printf is available ([#14132](https://github.com/crystal-lang/crystal/pull/14132), thanks @HertzDevil)\n- _(runtime)_ Implement most of `Crystal::System.print_error` in native Crystal ([#14116](https://github.com/crystal-lang/crystal/pull/14116), thanks @HertzDevil)\n- _(text)_ Drop `Char::Reader#@end` ([#13920](https://github.com/crystal-lang/crystal/pull/13920), thanks @straight-shoota)\n- _(text)_ Generate `src/html/entities.cr` automatically ([#13998](https://github.com/crystal-lang/crystal/pull/13998), thanks @HertzDevil)\n- _(time)_ Refactor leap year to use `divisible_by?` ([#13982](https://github.com/crystal-lang/crystal/pull/13982), thanks @meatball133)\n\n#### compiler\n\n- Remove relative path to vendored shards `markd` and `reply` ([#13992](https://github.com/crystal-lang/crystal/pull/13992), thanks @nobodywasishere)\n- _(cli)_ Generalize allowed values for compiler CLI `--format` option ([#13940](https://github.com/crystal-lang/crystal/pull/13940), thanks @straight-shoota)\n- _(parser)_ Use `Char#to_i?` in lexer ([#13841](https://github.com/crystal-lang/crystal/pull/13841), thanks @straight-shoota)\n\n#### tools\n\n- _(unreachable)_ Refactor `UnreachablePresenter` ([#13941](https://github.com/crystal-lang/crystal/pull/13941), thanks @straight-shoota)\n\n### Documentation\n\n#### lang\n\n- _(macros)_ Add reference to book how merging macro expansion and call docs ([#14139](https://github.com/crystal-lang/crystal/pull/14139), thanks @Blacksmoke16)\n\n#### stdlib\n\n- _(collection)_ Fix documentation of `Hash#put_if_absent` ([#13898](https://github.com/crystal-lang/crystal/pull/13898), thanks @ilmanzo)\n- _(collection)_ Improve docs on initial/default values passed to `Array.new` and `Hash.new` ([#13962](https://github.com/crystal-lang/crystal/pull/13962), thanks @straight-shoota)\n- _(collection)_ Improve docs for `Iterator` step-by-step iteration ([#13967](https://github.com/crystal-lang/crystal/pull/13967), thanks @straight-shoota)\n- _(macros)_ Document `Crystal::Macros::MagicConstant` ([#14070](https://github.com/crystal-lang/crystal/pull/14070), thanks @HertzDevil)\n- _(serialization)_ Add docs and explicit type restriction for indent parameter of `JSON.build` ([#14140](https://github.com/crystal-lang/crystal/pull/14140), thanks @syeopite)\n- _(text)_ Add note about `Char::Reader`'s value semantics ([#14008](https://github.com/crystal-lang/crystal/pull/14008), thanks @HertzDevil)\n- _(text)_ Fix documentation for `String#index!` ([#14038](https://github.com/crystal-lang/crystal/pull/14038), thanks @gettalong)\n\n#### compiler\n\n- _(cli)_ Add optimization levels to manpage ([#14162](https://github.com/crystal-lang/crystal/pull/14162), thanks @straight-shoota)\n- _(cli)_ Add `unreachable` options to manpage ([#14164](https://github.com/crystal-lang/crystal/pull/14164), thanks @straight-shoota)\n- _(cli)_ Fix formatting in manpage ([#14163](https://github.com/crystal-lang/crystal/pull/14163), thanks @straight-shoota)\n\n### Specs\n\n#### stdlib\n\n- Add `pending_wasm32` ([#14086](https://github.com/crystal-lang/crystal/pull/14086), thanks @HertzDevil)\n- _(concurrency)_ Workaround regular timeouts in `HTTP::Server` specs with MT ([#14097](https://github.com/crystal-lang/crystal/pull/14097), thanks @ysbaddaden)\n- _(files)_ Fix `File::AccessDeniedError` expectations in `File` specs ([#14029](https://github.com/crystal-lang/crystal/pull/14029), thanks @HertzDevil)\n- _(text)_ Refactor specs for `HTML.unescape` ([#13842](https://github.com/crystal-lang/crystal/pull/13842), thanks @straight-shoota)\n- _(text)_ Fix spec for `String#encode` and `String.new` on DragonFlyBSD ([#13944](https://github.com/crystal-lang/crystal/pull/13944), thanks @GeopJr)\n\n#### compiler\n\n- _(codegen)_ Remove `LLVMExtCreateMCJITCompilerForModule` from `llvm_ext.cc` ([#13966](https://github.com/crystal-lang/crystal/pull/13966), thanks @HertzDevil)\n- _(interpreter)_ Disable `mkfifo` spec for interpreter ([#14051](https://github.com/crystal-lang/crystal/pull/14051), thanks @HertzDevil)\n- _(interpreter)_ Fix interpreter specs on Windows ([#14145](https://github.com/crystal-lang/crystal/pull/14145), thanks @HertzDevil)\n\n#### tools\n\n- _(docs-generator)_ Use `top_level_semantic` in doc spec instead of `semantic` ([#9352](https://github.com/crystal-lang/crystal/pull/9352), thanks @makenowjust)\n\n### Infrastructure\n\n- Changelog for 1.11.0 ([#14158](https://github.com/crystal-lang/crystal/pull/14158), thanks @straight-shoota)\n- Update previous Crystal release - 1.10.0 ([#13878](https://github.com/crystal-lang/crystal/pull/13878), thanks @straight-shoota)\n- Allow to specify git fork of distribution-scripts in CI ([#13976](https://github.com/crystal-lang/crystal/pull/13976), thanks @miry)\n- Extract `generate_data` to separate Makefile ([#14015](https://github.com/crystal-lang/crystal/pull/14015), thanks @straight-shoota)\n- Windows: Run specs in random order by default ([#14041](https://github.com/crystal-lang/crystal/pull/14041), thanks @HertzDevil)\n- Update shards 0.17.4 ([#14133](https://github.com/crystal-lang/crystal/pull/14133), thanks @straight-shoota)\n- Update distribution-scripts ([#14136](https://github.com/crystal-lang/crystal/pull/14136), thanks @straight-shoota)\n- Update GH Actions to v4 ([#14120](https://github.com/crystal-lang/crystal/pull/14120), thanks @renovate)\n- Embed logo image into repository and upgrade to SVG ([#14137](https://github.com/crystal-lang/crystal/pull/14137), thanks @straight-shoota)\n- Improvements for `github-changelog` script ([#14160](https://github.com/crystal-lang/crystal/pull/14160), thanks @straight-shoota)\n- Add `scripts/generate_llvm_version_info.cr` ([#14112](https://github.com/crystal-lang/crystal/pull/14112), thanks @HertzDevil)\n- Fix `make clean` to remove zipped manpages ([#14135](https://github.com/crystal-lang/crystal/pull/14135), thanks @straight-shoota)\n- Make `scripts/*.cr` all executable ([#13999](https://github.com/crystal-lang/crystal/pull/13999), thanks @HertzDevil)\n- Reformat changelog release headings ([#13663](https://github.com/crystal-lang/crystal/pull/13663), thanks @straight-shoota)\n- Merge `samples/.gitignore` into `.gitignore` ([#14134](https://github.com/crystal-lang/crystal/pull/14134), thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#13801](https://github.com/crystal-lang/crystal/pull/13801), thanks @renovate)\n- _(ci)_ Update LLVM patch version to LLVM 17.0.6 ([#14080](https://github.com/crystal-lang/crystal/pull/14080), thanks @straight-shoota)\n- _(ci)_ Configure Renovate Bot to add label `topic:infrastructure/ci` on PRs ([#14166](https://github.com/crystal-lang/crystal/pull/14166), thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#14165](https://github.com/crystal-lang/crystal/pull/14165), thanks @renovate)\n- _(ci)_ Distribute LLVM DLLs on Windows CI ([#14110](https://github.com/crystal-lang/crystal/pull/14110), thanks @HertzDevil)\n- _(ci)_ Use `CMAKE_MSVC_RUNTIME_LIBRARY` flag in win.yml ([#13900](https://github.com/crystal-lang/crystal/pull/13900), thanks @HertzDevil)\n"
  },
  {
    "path": "doc/changelogs/v1.12.md",
    "content": "# Changelog 1.12\n\n## [1.12.2] (2024-05-31)\n\n_Patch release with a bug fix necessary for support of latest libgc._\n\n[1.12.2]: https://github.com/crystal-lang/crystal/releases/1.12.2\n\n### Bugfixes\n\n#### stdlib\n\n- _(runtime)_ Don't allocate in `Fiber.unsafe_each` and `Thread.unsafe_each` ([#14635], thanks @ysbaddaden)\n\n[#14635]: https://github.com/crystal-lang/crystal/pull/14635\n\n### Infrastructure\n\n- Changelog for 1.12.2 ([#14640], thanks @straight-shoota)\n\n[#14640]: https://github.com/crystal-lang/crystal/pull/14640\n\n## [1.12.1] (2024-04-11)\n\n[1.12.1]: https://github.com/crystal-lang/crystal/releases/1.12.1\n\n### Bugfixes\n\n#### tools\n\n- _(formatter)_ **[regression]** Fix formatter with whitespace before closing parenthesis ([#14471], thanks @straight-shoota)\n\n[#14471]: https://github.com/crystal-lang/crystal/pull/14471\n\n### Infrastructure\n\n- Changelog for 1.12.1 ([#14472], thanks @straight-shoota)\n\n[#14472]: https://github.com/crystal-lang/crystal/pull/14472\n\n## [1.12.0] (2024-04-09)\n\n[1.12.0]: https://github.com/crystal-lang/crystal/releases/1.12.0\n\n### Features\n\n#### lang\n\n- Allow multiple parameters and blocks for operators ending in `=` ([#14159], thanks @HertzDevil)\n\n[#14159]: https://github.com/crystal-lang/crystal/pull/14159\n\n#### stdlib\n\n- _(concurrency)_ MT: reduce interleaved backtraces in spawn unhandled exceptions ([#14220], thanks @ysbaddaden)\n- _(concurrency)_ Fix: opening/reading from fifo/chardev files are blocking the thread ([#14255], thanks @ysbaddaden)\n- _(concurrency)_ Fix: Atomics and Locks (ARM, AArch64, X86) ([#14293], thanks @ysbaddaden)\n- _(files)_ Add `IO::FileDescriptor::Handle` ([#14390], thanks @straight-shoota)\n- _(macros)_ Add macro methods for lib-related nodes ([#14218], thanks @HertzDevil)\n- _(macros)_ Add macro methods for `Primitive` ([#14263], thanks @HertzDevil)\n- _(macros)_ Add macro methods for `TypeOf` ([#14262], thanks @HertzDevil)\n- _(macros)_ Add macro methods for `Alias` ([#14261], thanks @HertzDevil)\n- _(macros)_ Add macro methods for `Asm` and `AsmOperand` ([#14268], thanks @HertzDevil)\n- _(macros)_ Relax `delegate`'s setter detection ([#14282], thanks @HertzDevil)\n- _(numeric)_ Add `BigRational#%`, `#tdiv`, `#remainder` ([#14306], thanks @HertzDevil)\n- _(runtime)_ **[experimental]** Add `ReferenceStorage` for manual allocation of references ([#14270], thanks @HertzDevil)\n- _(runtime)_ Add MSVC invalid parameter handler ([#14313], thanks @HertzDevil)\n- _(system)_ Add `Signal#trap_handler?` ([#14126], thanks @stakach)\n- _(system)_ Thread: set name ([#14257], thanks @ysbaddaden)\n- _(system)_ Add `Process.on_terminate` ([#13694], thanks @stakach)\n- _(time)_ Add support for `Etc/UTC` time zone identifier without tzdb ([#14185], thanks @femto)\n\n[#14220]: https://github.com/crystal-lang/crystal/pull/14220\n[#14255]: https://github.com/crystal-lang/crystal/pull/14255\n[#14293]: https://github.com/crystal-lang/crystal/pull/14293\n[#14390]: https://github.com/crystal-lang/crystal/pull/14390\n[#14218]: https://github.com/crystal-lang/crystal/pull/14218\n[#14263]: https://github.com/crystal-lang/crystal/pull/14263\n[#14262]: https://github.com/crystal-lang/crystal/pull/14262\n[#14261]: https://github.com/crystal-lang/crystal/pull/14261\n[#14268]: https://github.com/crystal-lang/crystal/pull/14268\n[#14282]: https://github.com/crystal-lang/crystal/pull/14282\n[#14306]: https://github.com/crystal-lang/crystal/pull/14306\n[#14270]: https://github.com/crystal-lang/crystal/pull/14270\n[#14313]: https://github.com/crystal-lang/crystal/pull/14313\n[#14126]: https://github.com/crystal-lang/crystal/pull/14126\n[#14257]: https://github.com/crystal-lang/crystal/pull/14257\n[#13694]: https://github.com/crystal-lang/crystal/pull/13694\n[#14185]: https://github.com/crystal-lang/crystal/pull/14185\n\n#### compiler\n\n- Add `CRYSTAL_CONFIG_CC` compiler config ([#14318], thanks @straight-shoota)\n- _(cli)_ Respect `NO_COLOR` in the compiler ([#14260], thanks @HertzDevil)\n- _(cli)_ Respect `--static` on Windows ([#14292], thanks @HertzDevil)\n- _(cli)_ Allow `--single-module` and `--threads` for `eval` and `spec` ([#14341], thanks @HertzDevil)\n- _(codegen)_ Add `--frame-pointers` to control preservation of frame pointers ([#13860], thanks @refi64)\n- _(codegen)_ x86-64 Solaris / illumos support ([#14343], thanks @HertzDevil)\n- _(interpreter)_ Support `@[Link]`'s DLL search order in the interpreter on Windows ([#14146], thanks @HertzDevil)\n- _(interpreter)_ Automatically detect MSVC tools on Windows interpreter ([#14391], thanks @HertzDevil)\n- _(parser)_ Allow calling `#[]=` with a block using method syntax ([#14161], thanks @HertzDevil)\n- _(semantic)_ Change short_reference for top-level macros to `::foo` ([#14203], thanks @femto)\n\n[#14318]: https://github.com/crystal-lang/crystal/pull/14318\n[#14260]: https://github.com/crystal-lang/crystal/pull/14260\n[#14292]: https://github.com/crystal-lang/crystal/pull/14292\n[#14341]: https://github.com/crystal-lang/crystal/pull/14341\n[#13860]: https://github.com/crystal-lang/crystal/pull/13860\n[#14343]: https://github.com/crystal-lang/crystal/pull/14343\n[#14146]: https://github.com/crystal-lang/crystal/pull/14146\n[#14391]: https://github.com/crystal-lang/crystal/pull/14391\n[#14161]: https://github.com/crystal-lang/crystal/pull/14161\n[#14203]: https://github.com/crystal-lang/crystal/pull/14203\n\n#### tools\n\n- Add `crystal tool flags` ([#14234], thanks @straight-shoota)\n- _(formatter)_ Add more whitespace around `ProcLiteral`s ([#14209], thanks @HertzDevil)\n\n[#14234]: https://github.com/crystal-lang/crystal/pull/14234\n[#14209]: https://github.com/crystal-lang/crystal/pull/14209\n\n### Bugfixes\n\n#### lang\n\n- _(macros)_ Remove extra newline in top-level `FunDef`'s string representation ([#14212], thanks @HertzDevil)\n- _(macros)_ Remove `T*` and `T[N]` macro interpolation behavior inside libs ([#14215], thanks @HertzDevil)\n\n[#14212]: https://github.com/crystal-lang/crystal/pull/14212\n[#14215]: https://github.com/crystal-lang/crystal/pull/14215\n\n#### stdlib\n\n- _(collection)_ Fix `Hash#update` when default block also adds given key ([#14417], thanks @HertzDevil)\n- _(collection)_ Fix `Hash#put_if_absent` putting duplicate keys ([#14427], thanks @HertzDevil)\n- _(concurrency)_ Reserve stack space on non-main threads for crash recovery on Windows ([#14187], thanks @HertzDevil)\n- _(concurrency)_ Add memory barrier to `Mutex#unlock` on aarch64 ([#14272], thanks @jgaskins)\n- _(concurrency)_ init schedulers before we spawn fibers ([#14339], thanks @ysbaddaden)\n- _(files)_ Make `FileUtils.mv` work across filesystems on Windows ([#14320], thanks @HertzDevil)\n- _(llvm)_ Use correct string size for `LLVM::Type#inline_asm` ([#14265], thanks @HertzDevil)\n- _(llvm)_ Fix System V ABI for packed structs with misaligned fields ([#14324], thanks @HertzDevil)\n- _(networking)_ OpenSSL 3.x reports unexpected EOF as SSL error ([#14219], thanks @ysbaddaden)\n- _(numeric)_ Make equality between `Complex` and other numbers exact ([#14309], thanks @HertzDevil)\n- _(numeric)_ Fix `#hash` for the `Big*` number types ([#14308], thanks @HertzDevil)\n- _(runtime)_ Do not allocate memory in the segmentation fault signal handler ([#14327], thanks @HertzDevil)\n- _(runtime)_ Fix crash stack trace decoding on macOS ([#14335], thanks @HertzDevil)\n- _(runtime)_ `Crystal::RWLock` should be a struct ([#14345], thanks @ysbaddaden)\n- _(runtime)_ Fix `min_by?` in IOCP event loop `#run_once` ([#14394], thanks @straight-shoota)\n- _(serialization)_ `XML::Reader`: Disallow attributes containing null bytes ([#14193], thanks @HertzDevil)\n- _(serialization)_ Always call `LibXML.xmlInitParser` when requiring XML libraries ([#14191], thanks @HertzDevil)\n- _(system)_ Fix macro `Crystal::LIBRARY_PATH.split` when cross-compiling ([#14330], thanks @HertzDevil)\n- _(system)_ Add `SA_RESTART` flag to sigaction syscall ([#14351], thanks @ysbaddaden)\n- _(text)_ Add `Nil` return type restriction to `String::Formatter#consume_substitution` ([#14430], thanks @straight-shoota)\n\n[#14417]: https://github.com/crystal-lang/crystal/pull/14417\n[#14427]: https://github.com/crystal-lang/crystal/pull/14427\n[#14187]: https://github.com/crystal-lang/crystal/pull/14187\n[#14272]: https://github.com/crystal-lang/crystal/pull/14272\n[#14339]: https://github.com/crystal-lang/crystal/pull/14339\n[#14320]: https://github.com/crystal-lang/crystal/pull/14320\n[#14265]: https://github.com/crystal-lang/crystal/pull/14265\n[#14324]: https://github.com/crystal-lang/crystal/pull/14324\n[#14219]: https://github.com/crystal-lang/crystal/pull/14219\n[#14309]: https://github.com/crystal-lang/crystal/pull/14309\n[#14308]: https://github.com/crystal-lang/crystal/pull/14308\n[#14327]: https://github.com/crystal-lang/crystal/pull/14327\n[#14335]: https://github.com/crystal-lang/crystal/pull/14335\n[#14345]: https://github.com/crystal-lang/crystal/pull/14345\n[#14394]: https://github.com/crystal-lang/crystal/pull/14394\n[#14193]: https://github.com/crystal-lang/crystal/pull/14193\n[#14191]: https://github.com/crystal-lang/crystal/pull/14191\n[#14330]: https://github.com/crystal-lang/crystal/pull/14330\n[#14351]: https://github.com/crystal-lang/crystal/pull/14351\n[#14430]: https://github.com/crystal-lang/crystal/pull/14430\n\n#### compiler\n\n- _(cli)_ `build --no-codegen` output file name error ([#14239], thanks @apainintheneck)\n- _(codegen)_ Do not handle inline assembly with `\"intel\"` flag as AT&T syntax ([#14264], thanks @HertzDevil)\n- _(codegen)_ **[breaking]** Respect alignments above `alignof(Void*)` inside union values ([#14279], thanks @HertzDevil)\n- _(codegen)_ Fix stack corruption in union-to-union casts ([#14289], thanks @HertzDevil)\n- _(codegen)_ Don't copy DLL to output directory if file already exists ([#14315], thanks @HertzDevil)\n- _(codegen)_ Fix `Proc#call` that takes and returns large extern structs by value ([#14323], thanks @HertzDevil)\n- _(codegen)_ Never discard ivar initializer inside `.allocate` and `.pre_initialize` ([#14337], thanks @HertzDevil)\n- _(codegen)_ Use separate names for constant and class variable internals ([#14445], thanks @HertzDevil)\n- _(interpreter)_ fix fiber's resumable property ([#14252], thanks @ysbaddaden)\n- _(interpreter)_ Ensure all constants only have one initializer in the interpreter ([#14381], thanks @HertzDevil)\n- _(interpreter)_ Handle NaN comparisons in the interpreter ([#14441], thanks @HertzDevil)\n- _(interpreter)_ Check `UInt16#to_u8` for overflow in the interpreter ([#14436], thanks @HertzDevil)\n- _(interpreter)_ Fix interpreter internal overflow for `UInt128#to_f32` and `#to_f32!` ([#14437], thanks @HertzDevil)\n- _(parser)_ Fix name locations of `FunDef` and `External` nodes ([#14267], thanks @HertzDevil)\n- _(parser)_ Fix end locations of `Alias` nodes ([#14271], thanks @HertzDevil)\n\n[#14239]: https://github.com/crystal-lang/crystal/pull/14239\n[#14264]: https://github.com/crystal-lang/crystal/pull/14264\n[#14279]: https://github.com/crystal-lang/crystal/pull/14279\n[#14289]: https://github.com/crystal-lang/crystal/pull/14289\n[#14315]: https://github.com/crystal-lang/crystal/pull/14315\n[#14323]: https://github.com/crystal-lang/crystal/pull/14323\n[#14337]: https://github.com/crystal-lang/crystal/pull/14337\n[#14445]: https://github.com/crystal-lang/crystal/pull/14445\n[#14252]: https://github.com/crystal-lang/crystal/pull/14252\n[#14381]: https://github.com/crystal-lang/crystal/pull/14381\n[#14441]: https://github.com/crystal-lang/crystal/pull/14441\n[#14436]: https://github.com/crystal-lang/crystal/pull/14436\n[#14437]: https://github.com/crystal-lang/crystal/pull/14437\n[#14267]: https://github.com/crystal-lang/crystal/pull/14267\n[#14271]: https://github.com/crystal-lang/crystal/pull/14271\n\n#### tools\n\n- _(formatter)_ Fix format for `asm` with comments ([#14278], thanks @straight-shoota)\n- _(formatter)_ Fix formatter for white space in `a.[b]` syntax ([#14346], thanks @straight-shoota)\n- _(formatter)_ Fix formatter on call without parentheses followed by doc comment ([#14354], thanks @straight-shoota)\n- _(formatter)_ Do not remove the whitespace in `foo ()` when formatting ([#14439], thanks @HertzDevil)\n\n[#14278]: https://github.com/crystal-lang/crystal/pull/14278\n[#14346]: https://github.com/crystal-lang/crystal/pull/14346\n[#14354]: https://github.com/crystal-lang/crystal/pull/14354\n[#14439]: https://github.com/crystal-lang/crystal/pull/14439\n\n### Chores\n\n#### stdlib\n\n- _(concurrency)_ **[deprecation]** Drop flag `openbsd6.2` ([#14233], thanks @straight-shoota)\n- _(files)_ **[deprecation]** Deprecate timeout setters with `Number` arguments ([#14372], thanks @straight-shoota)\n\n[#14233]: https://github.com/crystal-lang/crystal/pull/14233\n[#14372]: https://github.com/crystal-lang/crystal/pull/14372\n\n#### compiler\n\n- _(codegen)_ Drop pinning Dwarf version 2 for android ([#14243], thanks @straight-shoota)\n\n[#14243]: https://github.com/crystal-lang/crystal/pull/14243\n\n### Performance\n\n#### stdlib\n\n- _(collection)_ Optimize hash lookup in `Enumerable#group_by` ([#14235], thanks @straight-shoota)\n- _(concurrency)_ Use per-scheduler stack pools (let's recycle) ([#14100], thanks @ysbaddaden)\n\n[#14235]: https://github.com/crystal-lang/crystal/pull/14235\n[#14100]: https://github.com/crystal-lang/crystal/pull/14100\n\n#### compiler\n\n- _(codegen)_ on demand distribution to forked processes ([#14273], thanks @ysbaddaden)\n- _(interpreter)_ Use `Fiber::StackPool` in the interpreter ([#14395], thanks @HertzDevil)\n\n[#14273]: https://github.com/crystal-lang/crystal/pull/14273\n[#14395]: https://github.com/crystal-lang/crystal/pull/14395\n\n### Refactor\n\n#### stdlib\n\n- _(files)_ Replace some Microsoft C runtime funs with Win32 equivalents ([#14316], thanks @HertzDevil)\n- _(files)_ Move timeout properties to `Socket` and `IO::FileDescriptor` ([#14367], thanks @straight-shoota)\n- _(files)_ Add type restrictions to `#unbuffered_*` implementations ([#14382], thanks @straight-shoota)\n- _(networking)_ Refactor `HTTP::Client` timeout ivars to `Time::Span` ([#14371], thanks @straight-shoota)\n- _(networking)_ Refactor `Socket#system_receive` to return `Address` ([#14384], thanks @straight-shoota)\n- _(networking)_ Refactor `#system_connect` without yield ([#14383], thanks @straight-shoota)\n- _(numeric)_ Add `Crystal::Hasher.reduce_num` and `#number` ([#14304], thanks @HertzDevil)\n- _(runtime)_ Refactor and add comments to IOCP `#run_once` ([#14380], thanks @straight-shoota)\n- _(specs)_ **[deprecation]** Move most of spec runner's state into `Spec::CLI` ([#14170], thanks @HertzDevil)\n- _(specs)_ Add `Spec::Formatter#should_print_summary?` ([#14397], thanks @HertzDevil)\n\n[#14316]: https://github.com/crystal-lang/crystal/pull/14316\n[#14367]: https://github.com/crystal-lang/crystal/pull/14367\n[#14382]: https://github.com/crystal-lang/crystal/pull/14382\n[#14371]: https://github.com/crystal-lang/crystal/pull/14371\n[#14384]: https://github.com/crystal-lang/crystal/pull/14384\n[#14383]: https://github.com/crystal-lang/crystal/pull/14383\n[#14304]: https://github.com/crystal-lang/crystal/pull/14304\n[#14380]: https://github.com/crystal-lang/crystal/pull/14380\n[#14170]: https://github.com/crystal-lang/crystal/pull/14170\n[#14397]: https://github.com/crystal-lang/crystal/pull/14397\n\n#### compiler\n\n- Ensure `Crystal::Visitor#visit` returns `Bool` ([#14266], thanks @HertzDevil)\n- _(parser)_ Add `Token::Kind#unary_operator?` ([#14342], thanks @straight-shoota)\n- _(parser)_ Add `Lexer#wants_def_or_macro_name` ([#14352], thanks @straight-shoota)\n\n[#14266]: https://github.com/crystal-lang/crystal/pull/14266\n[#14342]: https://github.com/crystal-lang/crystal/pull/14342\n[#14352]: https://github.com/crystal-lang/crystal/pull/14352\n\n### Documentation\n\n#### stdlib\n\n- _(collection)_ Fix docs `:inherit:` pragma for `Indexable#first` ([#14296], thanks @lachlan)\n- _(collection)_ Fix `Hash.new(initial_capacity, &block)` doc to use relevant example ([#14429], thanks @lachlan)\n- _(crypto)_ Improve OpenSSL module documentation ([#14410], thanks @summer-alice)\n- _(numeric)_ Enhance docs for `Int#downto` ([#14176], thanks @jkthorne)\n- _(runtime)_ Document builtin constants ([#14276], thanks @straight-shoota)\n- _(runtime)_ Fix `Pointer#+(offset: Int64)` doc parameter name typo ([#14428], thanks @lachlan)\n- _(runtime)_ Improve documentation for `at_exit` handler conditions ([#14426], thanks @straight-shoota)\n- _(system)_ Fix typo in Signal docs ([#14400], thanks @joshrickard)\n- _(text)_ Fix `Colorize.enabled?`'s documentation ([#14258], thanks @HertzDevil)\n\n[#14296]: https://github.com/crystal-lang/crystal/pull/14296\n[#14429]: https://github.com/crystal-lang/crystal/pull/14429\n[#14410]: https://github.com/crystal-lang/crystal/pull/14410\n[#14176]: https://github.com/crystal-lang/crystal/pull/14176\n[#14276]: https://github.com/crystal-lang/crystal/pull/14276\n[#14428]: https://github.com/crystal-lang/crystal/pull/14428\n[#14426]: https://github.com/crystal-lang/crystal/pull/14426\n[#14400]: https://github.com/crystal-lang/crystal/pull/14400\n[#14258]: https://github.com/crystal-lang/crystal/pull/14258\n\n### Specs\n\n#### stdlib\n\n- Fix spelling in `spec/std/uri/params_spec.cr` ([#14302], thanks @jbampton)\n- _(files)_ Refactor expectations with `SpecChannelStatus` to be explicit ([#14378], thanks @straight-shoota)\n- _(files)_ Move some `IO::FileDescriptor` specs to the correct file ([#14431], thanks @HertzDevil)\n- _(system)_ Always preserve the environment for specs that modify `ENV` ([#14211], thanks @HertzDevil)\n- _(system)_ Ensure Windows time zone specs request `SeTimeZonePrivilege` properly ([#14297], thanks @HertzDevil)\n- _(text)_ Add single source of UTF-8 test sequences for specs ([#14433], thanks @HertzDevil)\n- _(time)_ Fix requires for `time/time_spec.cr` and `time/format_spec.cr` ([#14385], thanks @HertzDevil)\n\n[#14302]: https://github.com/crystal-lang/crystal/pull/14302\n[#14378]: https://github.com/crystal-lang/crystal/pull/14378\n[#14431]: https://github.com/crystal-lang/crystal/pull/14431\n[#14211]: https://github.com/crystal-lang/crystal/pull/14211\n[#14297]: https://github.com/crystal-lang/crystal/pull/14297\n[#14433]: https://github.com/crystal-lang/crystal/pull/14433\n[#14385]: https://github.com/crystal-lang/crystal/pull/14385\n\n#### compiler\n\n- Remove the prelude from some compiler specs ([#14336], thanks @HertzDevil)\n- _(interpreter)_ Fix: don't run thread specs with the interpreter ([#14287], thanks @ysbaddaden)\n- _(interpreter)_ Add `pending_interpreted` ([#14386], thanks @HertzDevil)\n- _(interpreter)_ Remove `spec/interpreter_std_spec.cr` ([#14399], thanks @HertzDevil)\n- _(semantic)_ Enable `@[Primitive(:va_arg)]` semantic spec on Windows ([#14338], thanks @HertzDevil)\n\n[#14336]: https://github.com/crystal-lang/crystal/pull/14336\n[#14287]: https://github.com/crystal-lang/crystal/pull/14287\n[#14386]: https://github.com/crystal-lang/crystal/pull/14386\n[#14399]: https://github.com/crystal-lang/crystal/pull/14399\n[#14338]: https://github.com/crystal-lang/crystal/pull/14338\n\n### Infrastructure\n\n- Changelog for 1.12.0 ([#14232], thanks @straight-shoota)\n- Remove filtering of already mentioned PRs ([#14229], thanks @straight-shoota)\n- Mention RFC process in contribution instructions ([#14291], thanks @straight-shoota)\n- Drop Nikola sponsor mention from Readme ([#14290], thanks @straight-shoota)\n- Enhance changelog script to pull milestone info from GitHub ([#14230], thanks @straight-shoota)\n- Add `shard.yml` ([#14365], thanks @straight-shoota)\n- Update vendored dependencies ([#14373], thanks @straight-shoota)\n- Fix `Milestone` JSON bindings in `github-changelog` helper ([#14404], thanks @straight-shoota)\n- Make repository configurable for reusable `github-changelog` ([#14407], thanks @straight-shoota)\n- Use link refs for PR links in changelog ([#14406], thanks @straight-shoota)\n- Implement pagination for GitHub API in `github-changelog` helper ([#14412], thanks @straight-shoota)\n- Add `scripts/update-changelog.sh` ([#14231], thanks @straight-shoota)\n- Update distribution-scripts ([#14457], thanks @straight-shoota)\n- Change some line endings from CRLF to LF ([#14299], thanks @HertzDevil)\n- Update copyright year in NOTICE.md ([#14329], thanks @HertzDevil)\n- Install system dependencies in the Windows GUI installer ([#14328], thanks @HertzDevil)\n- Skip building `llvm_ext.cc` on LLVM 18 or above ([#14357], thanks @HertzDevil)\n- _(ci)_ Update previous Crystal release 1.11.0 ([#14189], thanks @straight-shoota)\n- _(ci)_ Update previous Crystal release 1.11.1 ([#14224], thanks @straight-shoota)\n- _(ci)_ Update previous Crystal release - 1.11.2 ([#14251], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#14246], thanks @renovate)\n- _(ci)_ Upgrade `resource_class` for `test_preview_mt` ([#14274], thanks @straight-shoota)\n- _(ci)_ Upgrade from old machine images approaching EOL ([#14275], thanks @straight-shoota)\n- _(ci)_ Update Windows library versions ([#14355], thanks @HertzDevil)\n- _(ci)_ Update cachix/install-nix-action action to v26 ([#14375], thanks @renovate)\n- _(ci)_ Update shards 0.18.0 ([#14411], thanks @straight-shoota)\n- _(ci)_ Support LLVM 18.1 ([#14277], thanks @HertzDevil)\n- _(ci)_ Use `Makefile.win` for Shards on Windows CI ([#14414], thanks @HertzDevil)\n\n[#14232]: https://github.com/crystal-lang/crystal/pull/14232\n[#14229]: https://github.com/crystal-lang/crystal/pull/14229\n[#14291]: https://github.com/crystal-lang/crystal/pull/14291\n[#14290]: https://github.com/crystal-lang/crystal/pull/14290\n[#14230]: https://github.com/crystal-lang/crystal/pull/14230\n[#14365]: https://github.com/crystal-lang/crystal/pull/14365\n[#14373]: https://github.com/crystal-lang/crystal/pull/14373\n[#14404]: https://github.com/crystal-lang/crystal/pull/14404\n[#14407]: https://github.com/crystal-lang/crystal/pull/14407\n[#14406]: https://github.com/crystal-lang/crystal/pull/14406\n[#14412]: https://github.com/crystal-lang/crystal/pull/14412\n[#14231]: https://github.com/crystal-lang/crystal/pull/14231\n[#14457]: https://github.com/crystal-lang/crystal/pull/14457\n[#14299]: https://github.com/crystal-lang/crystal/pull/14299\n[#14329]: https://github.com/crystal-lang/crystal/pull/14329\n[#14328]: https://github.com/crystal-lang/crystal/pull/14328\n[#14357]: https://github.com/crystal-lang/crystal/pull/14357\n[#14189]: https://github.com/crystal-lang/crystal/pull/14189\n[#14224]: https://github.com/crystal-lang/crystal/pull/14224\n[#14251]: https://github.com/crystal-lang/crystal/pull/14251\n[#14246]: https://github.com/crystal-lang/crystal/pull/14246\n[#14274]: https://github.com/crystal-lang/crystal/pull/14274\n[#14275]: https://github.com/crystal-lang/crystal/pull/14275\n[#14355]: https://github.com/crystal-lang/crystal/pull/14355\n[#14375]: https://github.com/crystal-lang/crystal/pull/14375\n[#14411]: https://github.com/crystal-lang/crystal/pull/14411\n[#14277]: https://github.com/crystal-lang/crystal/pull/14277\n[#14414]: https://github.com/crystal-lang/crystal/pull/14414\n"
  },
  {
    "path": "doc/changelogs/v1.13.md",
    "content": "# Changelog 1.13\n\n## [1.13.3] (2024-09-18)\n\n[1.13.3]: https://github.com/crystal-lang/crystal/releases/1.13.3\n\n### Bugfixes\n\n#### stdlib\n\n- **[regression]** Fix use global paths in macro bodies ([#14965], thanks @straight-shoota)\n- _(system)_ **[regression]** Fix `Process.exec` stream redirection on Windows ([#14986], thanks @HertzDevil)\n- _(text)_ **[regression]** Fix `String#index` and `#rindex` for `Char::REPLACEMENT` ([#14937], thanks @HertzDevil)\n\n[#14965]: https://github.com/crystal-lang/crystal/pull/14965\n[#14986]: https://github.com/crystal-lang/crystal/pull/14986\n[#14937]: https://github.com/crystal-lang/crystal/pull/14937\n\n### Infrastructure\n\n- Changelog for 1.13.3 ([#14991], thanks @straight-shoota)\n- _(ci)_ Enable runners from `runs-on.com` for Aarch64 CI ([#15007], thanks @straight-shoota)\n\n[#14991]: https://github.com/crystal-lang/crystal/pull/14991\n[#15007]: https://github.com/crystal-lang/crystal/pull/15007\n\n## [1.13.2] (2024-08-20)\n\n[1.13.2]: https://github.com/crystal-lang/crystal/releases/1.13.2\n\n### Bugfixes\n\n#### stdlib\n\n- _(collection)_ Fix explicitly clear deleted `Hash::Entry` ([#14862], thanks @HertzDevil)\n\n[#14862]: https://github.com/crystal-lang/crystal/pull/14862\n\n#### compiler\n\n- _(codegen)_ Fix `ReferenceStorage(T)` atomic if `T` has no inner pointers ([#14845], thanks @HertzDevil)\n- _(codegen)_ Fix misaligned store in `Bool` to union upcasts ([#14906], thanks @HertzDevil)\n- _(interpreter)_ Fix misaligned stack access in the interpreter ([#14843], thanks @HertzDevil)\n\n[#14845]: https://github.com/crystal-lang/crystal/pull/14845\n[#14906]: https://github.com/crystal-lang/crystal/pull/14906\n[#14843]: https://github.com/crystal-lang/crystal/pull/14843\n\n### Infrastructure\n\n- Changelog for 1.13.2 ([#14914], thanks @straight-shoota)\n\n[#14914]: https://github.com/crystal-lang/crystal/pull/14914\n\n## [1.13.1] (2024-07-12)\n\n[1.13.1]: https://github.com/crystal-lang/crystal/releases/1.13.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(serialization)_ **[regression]** Revert \"Optimize JSON parsing a bit\" ([#14804], thanks @straight-shoota)\n\n[#14804]: https://github.com/crystal-lang/crystal/pull/14804\n\n### Infrastructure\n\n- Changelog for 1.13.1 ([#14806], thanks @straight-shoota)\n\n[#14806]: https://github.com/crystal-lang/crystal/pull/14806\n\n## [1.13.0] (2024-07-09)\n\n[1.13.0]: https://github.com/crystal-lang/crystal/releases/1.13.0\n\n### Features\n\n#### lang\n\n- Allow rescuing exceptions that include a module ([#14553], thanks @Blacksmoke16)\n- _(macros)_ Allow assignment to `_` inside macro expressions ([#14452], thanks @HertzDevil)\n\n[#14553]: https://github.com/crystal-lang/crystal/pull/14553\n[#14452]: https://github.com/crystal-lang/crystal/pull/14452\n\n#### stdlib\n\n- _(collection)_ Add `Array#insert_all` ([#14486], thanks @summer-alice)\n- _(collection)_ Improve compile time error for `#sort(&block : T, T -> U)` ([#14693], thanks @beta-ziliani)\n- _(concurrency)_ Add `WaitGroup` synchronization primitive ([#14167], thanks @ysbaddaden)\n- _(concurrency)_ Allow `Atomic`s of pointer types ([#14401], thanks @HertzDevil)\n- _(concurrency)_ Add `Thread.new` yields itself ([#14543], thanks @ysbaddaden)\n- _(concurrency)_ Add support for `Atomic(Bool)` ([#14532], thanks @ysbaddaden)\n- _(concurrency)_ Add `Thread.sleep(Time::Span)` ([#14715], thanks @ysbaddaden)\n- _(files)_ Implement `IO#tty?` in Win32 ([#14421], thanks @HertzDevil)\n- _(files)_ Implement `File.readable?` and `.writable?` in Win32 ([#14420], thanks @HertzDevil)\n- _(files)_ Make `File.readable?` and `.writable?` follow symlinks on Windows ([#14514], thanks @HertzDevil)\n- _(llvm)_ Add some missing `LLVM::Context` bindings ([#14612], thanks @ysbaddaden)\n- _(llvm)_ Do not strip the macOS target triple ([#14466], thanks @hovsater)\n- _(log)_ Support `UInt32` and `UInt64` in Log Context ([#14459], thanks @toddsundsted)\n- _(macros)_ Add AST node methods for macro-related nodes ([#14492], thanks @HertzDevil)\n- _(macros)_ Support short blocks in `#[]` operator call's macro interpolation ([#14523], thanks @HertzDevil)\n- _(macros)_ Add macro methods for `Select` ([#14600], thanks @HertzDevil)\n- _(macros)_ Add `TypeNode#private?`, `#public?` and `#visibility` ([#11696], thanks @Hadeweka)\n- _(macros)_ Add `StringLiteral#to_utf16` ([#14676], thanks @ysbaddaden)\n- _(networking)_ Relax type restriction of handlers in `HTTP::Server.new` to `Indexable(HTTP::Handler)` ([#14413], thanks @hugopl)\n- _(networking)_ **[security]** OpenSSL: don't change default cipher suites ([#14655], thanks @ysbaddaden)\n- _(networking)_ Allow parsing cookies with space in the value ([#14455], thanks @anton7c3)\n- _(runtime)_ Add `EventLoop::Socket` module ([#14643], thanks @straight-shoota)\n- _(runtime)_ Add `EventLoop::FileDescriptor` module ([#14639], thanks @straight-shoota)\n- _(runtime)_ Add `Crystal::Tracing` for runtime tracing ([#14659], thanks @ysbaddaden)\n- _(system)_ **[security]** **[breaking]** Disable implicit execution of batch files on Windows ([#14557], thanks @straight-shoota)\n- _(system)_ Add `EventLoop#run(blocking)` and `EventLoop#interrupt` ([#14568], thanks @ysbaddaden)\n- _(system)_ Add `Crystal::System::Time.ticks` ([#14620], thanks @ysbaddaden)\n- _(system)_ Add `Crystal::System::Thread.current_thread?`, `#scheduler?` ([#14660], thanks @ysbaddaden)\n- _(system)_ Protect fork/exec on targets that don't support atomic CLOEXEC ([#14674], thanks @ysbaddaden)\n- _(system)_ Add `Crystal::System.panic` ([#14733], thanks @ysbaddaden)\n- _(text)_ Add `Regex::MatchOptions` overload for `String#index!` ([#14462], thanks @HertzDevil)\n- _(text)_ Add `StringPool#get?` ([#14508], thanks @HertzDevil)\n- _(text)_ Add `pkg_config` names for `pcre2` and `pcre` ([#14584], thanks @straight-shoota)\n- _(text)_ Add UUID v7 ([#14732], thanks @jgaskins)\n- _(time)_ Add `Time::Error` ([#14743], thanks @Blacksmoke16)\n\n[#14486]: https://github.com/crystal-lang/crystal/pull/14486\n[#14693]: https://github.com/crystal-lang/crystal/pull/14693\n[#14167]: https://github.com/crystal-lang/crystal/pull/14167\n[#14401]: https://github.com/crystal-lang/crystal/pull/14401\n[#14543]: https://github.com/crystal-lang/crystal/pull/14543\n[#14532]: https://github.com/crystal-lang/crystal/pull/14532\n[#14715]: https://github.com/crystal-lang/crystal/pull/14715\n[#14421]: https://github.com/crystal-lang/crystal/pull/14421\n[#14420]: https://github.com/crystal-lang/crystal/pull/14420\n[#14514]: https://github.com/crystal-lang/crystal/pull/14514\n[#14612]: https://github.com/crystal-lang/crystal/pull/14612\n[#14466]: https://github.com/crystal-lang/crystal/pull/14466\n[#14459]: https://github.com/crystal-lang/crystal/pull/14459\n[#14492]: https://github.com/crystal-lang/crystal/pull/14492\n[#14523]: https://github.com/crystal-lang/crystal/pull/14523\n[#14600]: https://github.com/crystal-lang/crystal/pull/14600\n[#11696]: https://github.com/crystal-lang/crystal/pull/11696\n[#14676]: https://github.com/crystal-lang/crystal/pull/14676\n[#14413]: https://github.com/crystal-lang/crystal/pull/14413\n[#14655]: https://github.com/crystal-lang/crystal/pull/14655\n[#14455]: https://github.com/crystal-lang/crystal/pull/14455\n[#14643]: https://github.com/crystal-lang/crystal/pull/14643\n[#14639]: https://github.com/crystal-lang/crystal/pull/14639\n[#14659]: https://github.com/crystal-lang/crystal/pull/14659\n[#14557]: https://github.com/crystal-lang/crystal/pull/14557\n[#14568]: https://github.com/crystal-lang/crystal/pull/14568\n[#14620]: https://github.com/crystal-lang/crystal/pull/14620\n[#14660]: https://github.com/crystal-lang/crystal/pull/14660\n[#14674]: https://github.com/crystal-lang/crystal/pull/14674\n[#14733]: https://github.com/crystal-lang/crystal/pull/14733\n[#14462]: https://github.com/crystal-lang/crystal/pull/14462\n[#14508]: https://github.com/crystal-lang/crystal/pull/14508\n[#14584]: https://github.com/crystal-lang/crystal/pull/14584\n[#14732]: https://github.com/crystal-lang/crystal/pull/14732\n[#14743]: https://github.com/crystal-lang/crystal/pull/14743\n\n#### compiler\n\n- _(codegen)_ Add compiler flags `-Os` and `-Oz` to optimize binary size ([#14463], thanks @ysbaddaden)\n- _(codegen)_ Add compiler support for AVR architecture (Arduino) ([#14393], thanks @ysbaddaden)\n\n[#14463]: https://github.com/crystal-lang/crystal/pull/14463\n[#14393]: https://github.com/crystal-lang/crystal/pull/14393\n\n#### tools\n\n- _(formatter)_ Allow new formatter styles for trailing comma and whitespace around proc literal ([#14726], thanks @Blacksmoke16)\n\n[#14726]: https://github.com/crystal-lang/crystal/pull/14726\n\n### Bugfixes\n\n#### lang\n\n- _(macros)_ Fix parsing of non-trailing `if` bodies inside macro expressions ([#14505], thanks @HertzDevil)\n- _(macros)_ Drop parentheses around `->` inside certain comma-separated lists ([#14506], thanks @HertzDevil)\n- _(macros)_ Fix indentation of `Select` nodes' macro interpolation ([#14510], thanks @HertzDevil)\n- _(macros)_ Fix indentation of parenthesized `Expressions#to_s` ([#14511], thanks @HertzDevil)\n\n[#14505]: https://github.com/crystal-lang/crystal/pull/14505\n[#14506]: https://github.com/crystal-lang/crystal/pull/14506\n[#14510]: https://github.com/crystal-lang/crystal/pull/14510\n[#14511]: https://github.com/crystal-lang/crystal/pull/14511\n\n#### stdlib\n\n- _(collection)_ **[regression]** Ensure `Enumerable#to_a` and `Enumerable#tally` properly retain return type of `T` ([#14447], thanks @Blacksmoke16)\n- _(collection)_ **[breaking]** Never raise `IndexError` in `#[]?(Range)` ([#14444], thanks @HertzDevil)\n- _(collection)_ **[breaking]** Fix `Set#to_a(&)` ([#14519], thanks @meatball133)\n- _(collection)_ Fix `Hash#rehash` to reset `@first` ([#14606], thanks @straight-shoota)\n- _(collection)_ Fix macro interpolation in `NamedTuple#from` ([#14790], thanks @HertzDevil)\n- _(collection)_ **[regression]** Fix regression with `NamedTuple.new` when using key with a hyphen ([#14785], thanks @Blacksmoke16)\n- _(files)_ Allow `#fsync` and `#flock_*` on `IO::FileDescriptor` ([#14432], thanks @HertzDevil)\n- _(files)_ Make `IO::FileDescriptor#tty?` return false for NUL on Windows ([#14509], thanks @HertzDevil)\n- _(files)_ Fix blockless `IO::FileDescriptor` echo and raw mode methods ([#14529], thanks @HertzDevil)\n- _(files)_ Remove target path's forward slashes in `File.symlink` on Windows ([#14522], thanks @HertzDevil)\n- _(files)_ Fix `IO#same_content?` accepting prefix on second stream ([#14664], thanks @straight-shoota)\n- _(files)_ Fix overflow in `File#read_at` for large offsets on Windows ([#14708], thanks @HertzDevil)\n- _(files)_ Fix `IO::FileDescriptor.new` for closed fd ([#14697], thanks @straight-shoota)\n- _(files)_ Fix `IO::Delimited` reading into limited slice with peek ([#14772], thanks @straight-shoota)\n- _(files)_ Fix `Compress::Gzip` extra field ([#14550], thanks @kojix2)\n- _(log)_ delete source from builder cache when finalize ([#14475], thanks @ysbaddaden)\n- _(networking)_ Fix `Socket#close` error handling on Windows ([#14517], thanks @HertzDevil)\n- _(networking)_ Set UTF-8 charset on directory listing in `HTTP::StaticFileHandler` ([#14546], thanks @alexkutsan)\n- _(networking)_ Don't pass socket file descriptors to subprocesses on Unix (`SOCK_CLOEXEC`) ([#14632], thanks @carlhoerberg)\n- _(networking)_ **[security]** OpenSSL: don't set default ECDH curve ([#14656], thanks @ysbaddaden)\n- _(networking)_ **[security]** OpenSSL: deprecate Mozilla's TLS Server recommendation ([#14657], thanks @ysbaddaden)\n- _(networking)_ use `SOCK_CLOEXEC` with `FD_CLOEXEC` fallback ([#14672], thanks @ysbaddaden)\n- _(networking)_ **[regression]** Fix regression on `Socket#connect` timeout type restriction ([#14755], thanks @straight-shoota)\n- _(networking)_ Drop default timeout for `Socket#connect` on Windows ([#14756], thanks @straight-shoota)\n- _(networking)_ don't hardcode alpn protocol byte size (OpenSSL) ([#14769], thanks @ysbaddaden)\n- _(numeric)_ Fix `BigRational#format` ([#14525], thanks @meatball133)\n- _(numeric)_ **[regression]** Restore leading zero in exponent for `printf(\"%e\")` and `printf(\"%g\")` ([#14695], thanks @straight-shoota)\n- _(runtime)_ Fix enable docs for builtin constants ([#14571], thanks @straight-shoota)\n- _(runtime)_ Fix `GC.malloc` for `gc_none` to clear memory ([#14746], thanks @straight-shoota)\n- _(serialization)_ Fix JSON discriminator for Bool `false` value ([#14779], thanks @dammer)\n- _(specs)_ Fix relative file paths in spec output ([#14725], thanks @straight-shoota)\n- _(system)_ Fix using `System.retry_with_buffer` with stack buffer ([#14615], thanks @straight-shoota)\n- _(system)_ Harmonize close on exec for `Socket` & `FileDescriptor` on Windows ([#14634], thanks @ysbaddaden)\n- _(system)_ Use `dup3` and `pipe2` to set `O_CLOEXEC` when available ([#14673], thanks @ysbaddaden)\n- _(system)_ Fix calls to `retry_with_buffer` when big buffer is necessary ([#14622], thanks @BlobCodes)\n- _(system)_ Fix `Process.run` with closed IO ([#14698], thanks @straight-shoota)\n- _(system)_ Prefer `strerror_r` over `strerror` for thread-safe errno ([#14764], thanks @ysbaddaden)\n- _(text)_ Make `String#sub` raise `IndexError` if index is equal to size ([#14458], thanks @HertzDevil)\n- _(text)_ Fix `libpcre2` version detection not working for `-RC{N}` versions ([#14478], thanks @Frityet)\n- _(text)_ Fix `Regex#inspect` with non-literal-compatible options ([#14575], thanks @straight-shoota)\n- _(text)_ Fix ECR escape sequences containing `-` ([#14739], thanks @HertzDevil)\n\n[#14447]: https://github.com/crystal-lang/crystal/pull/14447\n[#14444]: https://github.com/crystal-lang/crystal/pull/14444\n[#14519]: https://github.com/crystal-lang/crystal/pull/14519\n[#14606]: https://github.com/crystal-lang/crystal/pull/14606\n[#14790]: https://github.com/crystal-lang/crystal/pull/14790\n[#14785]: https://github.com/crystal-lang/crystal/pull/14785\n[#14432]: https://github.com/crystal-lang/crystal/pull/14432\n[#14509]: https://github.com/crystal-lang/crystal/pull/14509\n[#14529]: https://github.com/crystal-lang/crystal/pull/14529\n[#14522]: https://github.com/crystal-lang/crystal/pull/14522\n[#14664]: https://github.com/crystal-lang/crystal/pull/14664\n[#14708]: https://github.com/crystal-lang/crystal/pull/14708\n[#14697]: https://github.com/crystal-lang/crystal/pull/14697\n[#14772]: https://github.com/crystal-lang/crystal/pull/14772\n[#14550]: https://github.com/crystal-lang/crystal/pull/14550\n[#14475]: https://github.com/crystal-lang/crystal/pull/14475\n[#14517]: https://github.com/crystal-lang/crystal/pull/14517\n[#14546]: https://github.com/crystal-lang/crystal/pull/14546\n[#14632]: https://github.com/crystal-lang/crystal/pull/14632\n[#14656]: https://github.com/crystal-lang/crystal/pull/14656\n[#14657]: https://github.com/crystal-lang/crystal/pull/14657\n[#14672]: https://github.com/crystal-lang/crystal/pull/14672\n[#14755]: https://github.com/crystal-lang/crystal/pull/14755\n[#14756]: https://github.com/crystal-lang/crystal/pull/14756\n[#14769]: https://github.com/crystal-lang/crystal/pull/14769\n[#14525]: https://github.com/crystal-lang/crystal/pull/14525\n[#14695]: https://github.com/crystal-lang/crystal/pull/14695\n[#14571]: https://github.com/crystal-lang/crystal/pull/14571\n[#14746]: https://github.com/crystal-lang/crystal/pull/14746\n[#14779]: https://github.com/crystal-lang/crystal/pull/14779\n[#14725]: https://github.com/crystal-lang/crystal/pull/14725\n[#14615]: https://github.com/crystal-lang/crystal/pull/14615\n[#14634]: https://github.com/crystal-lang/crystal/pull/14634\n[#14673]: https://github.com/crystal-lang/crystal/pull/14673\n[#14622]: https://github.com/crystal-lang/crystal/pull/14622\n[#14698]: https://github.com/crystal-lang/crystal/pull/14698\n[#14764]: https://github.com/crystal-lang/crystal/pull/14764\n[#14458]: https://github.com/crystal-lang/crystal/pull/14458\n[#14478]: https://github.com/crystal-lang/crystal/pull/14478\n[#14575]: https://github.com/crystal-lang/crystal/pull/14575\n[#14739]: https://github.com/crystal-lang/crystal/pull/14739\n\n#### compiler\n\n- _(codegen)_ Fix create new `target_machine` for every program ([#14694], thanks @straight-shoota)\n- _(codegen)_ Make `ReferenceStorage(T)` non-atomic if `T` is non-atomic ([#14730], thanks @HertzDevil)\n- _(codegen)_ Detect and error on failed codegen process ([#14762], thanks @ysbaddaden)\n- _(codegen)_ Fix stats and progress issues in codegen ([#14763], thanks @ysbaddaden)\n- _(debugger)_ Fix LLDB `crystal_formatters.py` for Python 3 ([#14665], thanks @zw963)\n- _(parser)_ Disallow assignments to calls with parentheses ([#14527], thanks @HertzDevil)\n- _(parser)_ Fix parser validate UTF-8 on first input byte  ([#14750], thanks @straight-shoota)\n- _(semantic)_ Fix type def reopening type from parent namespace ([#11208], thanks @straight-shoota)\n- _(semantic)_ Fix `Crystal::Path#to_macro_id` for global path ([#14490], thanks @straight-shoota)\n- _(semantic)_ Fix `Class#===` on metaclass ([#11162], thanks @makenowjust)\n\n[#14694]: https://github.com/crystal-lang/crystal/pull/14694\n[#14730]: https://github.com/crystal-lang/crystal/pull/14730\n[#14762]: https://github.com/crystal-lang/crystal/pull/14762\n[#14763]: https://github.com/crystal-lang/crystal/pull/14763\n[#14665]: https://github.com/crystal-lang/crystal/pull/14665\n[#14527]: https://github.com/crystal-lang/crystal/pull/14527\n[#14750]: https://github.com/crystal-lang/crystal/pull/14750\n[#11208]: https://github.com/crystal-lang/crystal/pull/11208\n[#14490]: https://github.com/crystal-lang/crystal/pull/14490\n[#11162]: https://github.com/crystal-lang/crystal/pull/11162\n\n#### tools\n\n- _(docs-generator)_ Fix generate docs for builtins `HOST_TRIPLE` and `TARGET_TRIPLE` ([#14570], thanks @straight-shoota)\n- _(docs-generator)_ Decode URI component for search functionality in docs ([#14645], thanks @nobodywasishere)\n- _(formatter)_ Fix formatting for short block inside `#[]` operator call ([#14526], thanks @HertzDevil)\n- _(formatter)_ Fix formatter to skip trailing comma for single-line parameters ([#14713], thanks @Blacksmoke16)\n\n[#14570]: https://github.com/crystal-lang/crystal/pull/14570\n[#14645]: https://github.com/crystal-lang/crystal/pull/14645\n[#14526]: https://github.com/crystal-lang/crystal/pull/14526\n[#14713]: https://github.com/crystal-lang/crystal/pull/14713\n\n### Chores\n\n#### stdlib\n\n- _(collection)_ Drop obsolete workaround in `Range#reverse_each` ([#14709], thanks @yxhuvud)\n- _(concurrency)_ Add `WaitGroup` to `docs_main.cr` ([#14624], thanks @straight-shoota)\n- _(files)_ **[deprecation]** Move `File.readable?`, `.writable?`, `.executable?` to `File::Info` ([#14484], thanks @straight-shoota)\n- _(networking)_ Drop `Crystal::System::Socket#system_send` ([#14637], thanks @straight-shoota)\n- _(networking)_ Add type restriction `host : String` in `TCPSocket` and `Addrinfo` ([#14703], thanks @straight-shoota)\n- _(runtime)_ Fix abstract def parameter name in `LibEvent::EventLoop#send_to` ([#14658], thanks @straight-shoota)\n- _(runtime)_ **[breaking]** Drop unused methods in `IO::Evented` ([#14666], thanks @straight-shoota)\n- _(runtime)_ Drop `IO::Overlapped` ([#14704], thanks @straight-shoota)\n\n[#14709]: https://github.com/crystal-lang/crystal/pull/14709\n[#14624]: https://github.com/crystal-lang/crystal/pull/14624\n[#14484]: https://github.com/crystal-lang/crystal/pull/14484\n[#14637]: https://github.com/crystal-lang/crystal/pull/14637\n[#14703]: https://github.com/crystal-lang/crystal/pull/14703\n[#14658]: https://github.com/crystal-lang/crystal/pull/14658\n[#14666]: https://github.com/crystal-lang/crystal/pull/14666\n[#14704]: https://github.com/crystal-lang/crystal/pull/14704\n\n#### compiler\n\n- _(codegen)_ **[breaking]** Remove `CRYSTAL_LIBRARY_RPATH` and delay-load helper ([#14598], thanks @HertzDevil)\n\n[#14598]: https://github.com/crystal-lang/crystal/pull/14598\n\n### Performance\n\n#### stdlib\n\n- _(collection)_ Optimize `Hash` for repeated removals and insertions ([#14539], thanks @HertzDevil)\n- _(runtime)_ Remove unnecessary explicit memory barriers on ARM ([#14567], thanks @ysbaddaden)\n- _(serialization)_ Optimize JSON parsing a bit ([#14366], thanks @asterite)\n- _(text)_ Use wrapping arithmetic for `Int::Primitive#unsafe_chr` ([#14443], thanks @HertzDevil)\n- _(text)_ Optimize `String#index(Char)` and `#rindex(Char)` for invalid UTF-8 ([#14461], thanks @HertzDevil)\n- _(text)_ Optimize `String#to_utf16` ([#14671], thanks @straight-shoota)\n\n[#14539]: https://github.com/crystal-lang/crystal/pull/14539\n[#14567]: https://github.com/crystal-lang/crystal/pull/14567\n[#14366]: https://github.com/crystal-lang/crystal/pull/14366\n[#14443]: https://github.com/crystal-lang/crystal/pull/14443\n[#14461]: https://github.com/crystal-lang/crystal/pull/14461\n[#14671]: https://github.com/crystal-lang/crystal/pull/14671\n\n### Refactor\n\n#### stdlib\n\n- Remove unnecessary calls to `#unsafe_as(UInt64)` etc. ([#14686], thanks @straight-shoota)\n- _(crypto)_ Replace calls to `StaticArray(UInt8, 1)#unsafe_as(UInt8)` ([#14685], thanks @straight-shoota)\n- _(files)_ Remove calls to `LibC._setmode` ([#14419], thanks @HertzDevil)\n- _(files)_ Use file handles directly instead of C file descriptors on Win32 ([#14501], thanks @HertzDevil)\n- _(files)_ Refactor win32 `System::FileDescriptor#unbuffered_{read,write}` ([#14607], thanks @straight-shoota)\n- _(files)_ Extract `#system_read` and `#system_write` for `FileDescriptor` and `Socket` ([#14626], thanks @straight-shoota)\n- _(files)_ Drop unused slice parameters of `#evented_*` methods ([#14627], thanks @straight-shoota)\n- _(networking)_ Refactor use `IO#read_byte` instead of `#read_char` in `HTTP::ChunkedContent` ([#14548], thanks @straight-shoota)\n- _(runtime)_ `Thread` owns its current fiber (instead of `Crystal::Scheduler`) ([#14554], thanks @ysbaddaden)\n- _(runtime)_ Use `Fiber#enqueue` ([#14561], thanks @ysbaddaden)\n- _(runtime)_ Add `Crystal::EventLoop.current` ([#14559], thanks @ysbaddaden)\n- _(runtime)_ Add `Fiber.suspend` ([#14560], thanks @ysbaddaden)\n- _(runtime)_ Remove `OverlappedOperation#synchronous` ([#14663], thanks @straight-shoota)\n- _(runtime)_ Rename `Crystal::Iocp` to `Crystal::IOCP` ([#14662], thanks @straight-shoota)\n- _(runtime)_ Unify `EventLoop.create` ([#14661], thanks @straight-shoota)\n- _(serialization)_ Replace `type` declarations for void pointers with `alias` in `libxml2` ([#14494], thanks @straight-shoota)\n- _(serialization)_ Refactor `JSON::Any#size` to use two branches instead of three ([#14533], thanks @meatball133)\n- _(specs)_ Move `Spec` context state into `Spec::CLI` ([#14259], thanks @HertzDevil)\n- _(system)_ Update `WinError#to_errno` ([#14515], thanks @HertzDevil)\n- _(system)_ Rename `Crystal::System.print_error(fmt, *args, &)` to `printf` ([#14617], thanks @ysbaddaden)\n- _(system)_ Refactor `Crystal::System.retry_with_buffer` calling `#to_slice` ([#14614], thanks @straight-shoota)\n- _(system)_ Extract system implementation of `UNIXSocket.pair` as `Crystal::System::Socket.socketpair` ([#14675], thanks @ysbaddaden)\n- _(system)_ Remove calls to `Pointer.new(Int)` ([#14683], thanks @straight-shoota)\n- _(system)_ Cleanup for `IOCP::OverlappedOperation` ([#14723], thanks @straight-shoota)\n- _(system)_ Refactor `IOCP::OverlappedOperation` internalize `handle` and `wait_for_completion` ([#14724], thanks @straight-shoota)\n\n[#14686]: https://github.com/crystal-lang/crystal/pull/14686\n[#14685]: https://github.com/crystal-lang/crystal/pull/14685\n[#14419]: https://github.com/crystal-lang/crystal/pull/14419\n[#14501]: https://github.com/crystal-lang/crystal/pull/14501\n[#14607]: https://github.com/crystal-lang/crystal/pull/14607\n[#14626]: https://github.com/crystal-lang/crystal/pull/14626\n[#14627]: https://github.com/crystal-lang/crystal/pull/14627\n[#14548]: https://github.com/crystal-lang/crystal/pull/14548\n[#14554]: https://github.com/crystal-lang/crystal/pull/14554\n[#14561]: https://github.com/crystal-lang/crystal/pull/14561\n[#14559]: https://github.com/crystal-lang/crystal/pull/14559\n[#14560]: https://github.com/crystal-lang/crystal/pull/14560\n[#14663]: https://github.com/crystal-lang/crystal/pull/14663\n[#14662]: https://github.com/crystal-lang/crystal/pull/14662\n[#14661]: https://github.com/crystal-lang/crystal/pull/14661\n[#14494]: https://github.com/crystal-lang/crystal/pull/14494\n[#14533]: https://github.com/crystal-lang/crystal/pull/14533\n[#14259]: https://github.com/crystal-lang/crystal/pull/14259\n[#14515]: https://github.com/crystal-lang/crystal/pull/14515\n[#14617]: https://github.com/crystal-lang/crystal/pull/14617\n[#14614]: https://github.com/crystal-lang/crystal/pull/14614\n[#14675]: https://github.com/crystal-lang/crystal/pull/14675\n[#14683]: https://github.com/crystal-lang/crystal/pull/14683\n[#14723]: https://github.com/crystal-lang/crystal/pull/14723\n[#14724]: https://github.com/crystal-lang/crystal/pull/14724\n\n#### compiler\n\n- _(codegen)_ Add `Program#size_t` and `Target#size_bit_width` ([#14442], thanks @ysbaddaden)\n- _(parser)_ Replace `Crystal::Select::When` with `Crystal::When` ([#14497], thanks @HertzDevil)\n\n[#14442]: https://github.com/crystal-lang/crystal/pull/14442\n[#14497]: https://github.com/crystal-lang/crystal/pull/14497\n\n### Documentation\n\n#### stdlib\n\n- _(collection)_ Fix doc for `Set#proper_superset_of?` ([#14516], thanks @meatball133)\n- _(collection)_ Fix result formatting in code example for `Indexable#[]?` ([#14721], thanks @meatball133)\n- _(concurrency)_ Fix example for `WeakRef` by removing `ref.value` call ([#10846], thanks @hugopl)\n- _(networking)_ Improve API docs for `Socket#send` ([#14638], thanks @straight-shoota)\n- _(networking)_ Add documentation for `HTTP::WebSocket#stream` ([#14537], thanks @meatball133)\n- _(numeric)_ Add documentation for complex methods inside Number ([#14538], thanks @meatball133)\n- _(runtime)_ Add documentation for standard streams blocking behaviour ([#14577], thanks @straight-shoota)\n- _(serialization)_ Fix docs for `CSV::Builder#row(&)` ([#14736], thanks @philipp-classen)\n- _(system)_ **[breaking]** Undocument `IO::Evented` ([#14749], thanks @straight-shoota)\n- _(system)_ Fix code example for `Process.on_terminate` ([#14798], thanks @philipp-classen)\n- _(text)_ Fix docs for `Char#ascii_number?` stating wrong minimum base ([#14521], thanks @meatball133)\n- _(text)_ Enhance documentation for regex options `NO_UTF_CHECK` ([#14542], thanks @straight-shoota)\n\n[#14516]: https://github.com/crystal-lang/crystal/pull/14516\n[#14721]: https://github.com/crystal-lang/crystal/pull/14721\n[#10846]: https://github.com/crystal-lang/crystal/pull/10846\n[#14638]: https://github.com/crystal-lang/crystal/pull/14638\n[#14537]: https://github.com/crystal-lang/crystal/pull/14537\n[#14538]: https://github.com/crystal-lang/crystal/pull/14538\n[#14577]: https://github.com/crystal-lang/crystal/pull/14577\n[#14736]: https://github.com/crystal-lang/crystal/pull/14736\n[#14749]: https://github.com/crystal-lang/crystal/pull/14749\n[#14798]: https://github.com/crystal-lang/crystal/pull/14798\n[#14521]: https://github.com/crystal-lang/crystal/pull/14521\n[#14542]: https://github.com/crystal-lang/crystal/pull/14542\n\n### Specs\n\n#### stdlib\n\n- Remove incorrect uses of `describe` ([#14757], thanks @HertzDevil)\n- _(files)_ Add spec for `Compress::Gzip::Writer` with `extra` ([#14788], thanks @straight-shoota)\n- _(runtime)_ Add specs for `Pointer::Appender` ([#14719], thanks @straight-shoota)\n- _(text)_ Add test helper for `normalize/regex_spec` ([#14545], thanks @straight-shoota)\n\n[#14757]: https://github.com/crystal-lang/crystal/pull/14757\n[#14788]: https://github.com/crystal-lang/crystal/pull/14788\n[#14719]: https://github.com/crystal-lang/crystal/pull/14719\n[#14545]: https://github.com/crystal-lang/crystal/pull/14545\n\n### Infrastructure\n\n- Changelog for 1.13.0 ([#14712], thanks @straight-shoota)\n- Update previous Crystal release 1.12.1 ([#14480], thanks @straight-shoota)\n- Highlight regression bugfixes in changelog ([#14474], thanks @straight-shoota)\n- Write release version to `src/VERSION` in `update-changelog` and `release-update` ([#14547], thanks @straight-shoota)\n- Fix `shell.nix` use `llvmPackages.bintools` with wrapper for rpath config ([#14583], thanks @straight-shoota)\n- Add `src/SOURCE_DATE_EPOCH` to release tree ([#14574], thanks @straight-shoota)\n- Update distribution-scripts ([#14562], thanks @straight-shoota)\n- Update distribution-scripts ([#14594], thanks @straight-shoota)\n- Use `boehmgc` package from nixpkgs in `shell.nix` ([#14591], thanks @straight-shoota)\n- Simplify LLVM dependency in `shell.nix` ([#14590], thanks @straight-shoota)\n- Fix nixpkgs `pkg-config` name in `shell.nix` ([#14593], thanks @straight-shoota)\n- Update previous Crystal release 1.12.2 ([#14647], thanks @straight-shoota)\n- Update distribution-scripts ([#14648], thanks @straight-shoota)\n- Update distribution-scripts ([#14714], thanks @straight-shoota)\n- Update distribution-scripts ([#14776], thanks @straight-shoota)\n- Fix changelog generator increase topic priority for `infrastructure` ([#14781], thanks @straight-shoota)\n- Remove `SetShouldExit` in Powershell wrapper ([#13769], thanks @straight-shoota)\n- _(ci)_ Run `primitives_spec` with the interpreter on CI ([#14438], thanks @HertzDevil)\n- _(ci)_ Add LLVM 18 to LLVM CI ([#14565], thanks @HertzDevil)\n- _(ci)_ Update to Ruby 3 in macOS circleCI runner ([#14777], thanks @straight-shoota)\n- _(ci)_ Distribute `shards.pdb` on Windows ([#14415], thanks @HertzDevil)\n- _(ci)_ Drop Windows CI workaround for 1.12.0-dev ([#14483], thanks @HertzDevil)\n\n[#14712]: https://github.com/crystal-lang/crystal/pull/14712\n[#14480]: https://github.com/crystal-lang/crystal/pull/14480\n[#14474]: https://github.com/crystal-lang/crystal/pull/14474\n[#14547]: https://github.com/crystal-lang/crystal/pull/14547\n[#14583]: https://github.com/crystal-lang/crystal/pull/14583\n[#14574]: https://github.com/crystal-lang/crystal/pull/14574\n[#14562]: https://github.com/crystal-lang/crystal/pull/14562\n[#14594]: https://github.com/crystal-lang/crystal/pull/14594\n[#14591]: https://github.com/crystal-lang/crystal/pull/14591\n[#14590]: https://github.com/crystal-lang/crystal/pull/14590\n[#14593]: https://github.com/crystal-lang/crystal/pull/14593\n[#14647]: https://github.com/crystal-lang/crystal/pull/14647\n[#14648]: https://github.com/crystal-lang/crystal/pull/14648\n[#14714]: https://github.com/crystal-lang/crystal/pull/14714\n[#14776]: https://github.com/crystal-lang/crystal/pull/14776\n[#14781]: https://github.com/crystal-lang/crystal/pull/14781\n[#13769]: https://github.com/crystal-lang/crystal/pull/13769\n[#14438]: https://github.com/crystal-lang/crystal/pull/14438\n[#14565]: https://github.com/crystal-lang/crystal/pull/14565\n[#14777]: https://github.com/crystal-lang/crystal/pull/14777\n[#14415]: https://github.com/crystal-lang/crystal/pull/14415\n[#14483]: https://github.com/crystal-lang/crystal/pull/14483\n"
  },
  {
    "path": "doc/changelogs/v1.14.md",
    "content": "# Changelog 1.14\n\n## [1.14.1] (2025-01-08)\n\n[1.14.1]: https://github.com/crystal-lang/crystal/releases/1.14.1\n\n### Bugfixes\n\n#### tools\n\n- _(formatter)_ Handle trailing comma with multiple parameters on the same line ([#15097], thanks @Blacksmoke16)\n\n[#15097]: https://github.com/crystal-lang/crystal/pull/15097\n\n### Infrastructure\n\n- Changelog for 1.14.1 ([#15323], thanks @straight-shoota)\n- _(ci)_ Update XCode 15.3.0 in circleci ([#15327], thanks @straight-shoota)\n\n[#15323]: https://github.com/crystal-lang/crystal/pull/15323\n[#15327]: https://github.com/crystal-lang/crystal/pull/15327\n\n## [1.14.0] (2024-10-09)\n\n[1.14.0]: https://github.com/crystal-lang/crystal/releases/1.14.0\n\n### Features\n\n#### lang\n\n- Allow `^` in constant numeric expressions ([#14951], thanks @HertzDevil)\n\n[#14951]: https://github.com/crystal-lang/crystal/pull/14951\n\n#### stdlib\n\n- Add support for Windows on aarch64 ([#14911], thanks @HertzDevil)\n- _(collection)_ **[breaking]** Add support for negative start index in `Slice#[start, count]` ([#14778], thanks @ysbaddaden)\n- _(collection)_ Add `Slice#same?` ([#14728], thanks @straight-shoota)\n- _(concurrency)_ Add `WaitGroup.wait` and `WaitGroup#spawn` ([#14837], thanks @jgaskins)\n- _(concurrency)_ Open non-blocking regular files as overlapped on Windows ([#14921], thanks @HertzDevil)\n- _(concurrency)_ Support non-blocking `File#read` and `#write` on Windows ([#14940], thanks @HertzDevil)\n- _(concurrency)_ Support non-blocking `File#read_at` on Windows ([#14958], thanks @HertzDevil)\n- _(concurrency)_ Support non-blocking `Process.run` standard streams on Windows ([#14941], thanks @HertzDevil)\n- _(concurrency)_ Support `IO::FileDescriptor#flock_*` on non-blocking files on Windows ([#14943], thanks @HertzDevil)\n- _(concurrency)_ Emulate non-blocking `STDIN` console on Windows ([#14947], thanks @HertzDevil)\n- _(concurrency)_ Async DNS resolution on Windows ([#14979], thanks @HertzDevil)\n- _(crypto)_ Update `LibCrypto` bindings for LibreSSL 3.5+ ([#14872], thanks @straight-shoota)\n- _(llvm)_ Expose LLVM instruction builder for `neg` and `fneg` ([#14774], thanks @JarnaChao09)\n- _(llvm)_ **[experimental]** Add minimal LLVM OrcV2 bindings ([#14887], thanks @HertzDevil)\n- _(llvm)_ Add `LLVM::Builder#finalize` ([#14892], thanks @JarnaChao09)\n- _(llvm)_ Support LLVM 19.1 ([#14842], thanks @HertzDevil)\n- _(macros)_ Add `Crystal::Macros::TypeNode#has_inner_pointers?` ([#14847], thanks @HertzDevil)\n- _(macros)_ Add `HashLiteral#has_key?` and `NamedTupleLiteral#has_key?` ([#14890], thanks @kamil-gwozdz)\n- _(numeric)_ Implement floating-point manipulation functions for `BigFloat` ([#11007], thanks @HertzDevil)\n- _(runtime)_ Stop & start the world (undocumented API) ([#14729], thanks @ysbaddaden)\n- _(runtime)_ Add `Pointer::Appender#to_slice` ([#14874], thanks @straight-shoota)\n- _(serialization)_ Add `URI.from_json_object_key?` and `URI#to_json_object_key` ([#14834], thanks @nobodywasishere)\n- _(serialization)_ Add `URI::Params::Serializable` ([#14684], thanks @Blacksmoke16)\n- _(system)_ Enable full backtrace for exception in process spawn ([#14796], thanks @straight-shoota)\n- _(system)_ Implement `System::User` on Windows ([#14933], thanks @HertzDevil)\n- _(system)_ Implement `System::Group` on Windows ([#14945], thanks @HertzDevil)\n- _(system)_ Add methods to `Crystal::EventLoop` ([#14977], thanks @ysbaddaden)\n- _(text)_ Add `underscore_to_space` option to `String#titleize` ([#14822], thanks @Blacksmoke16)\n- _(text)_ Support Unicode 16.0.0 ([#14997], thanks @HertzDevil)\n\n[#14911]: https://github.com/crystal-lang/crystal/pull/14911\n[#14778]: https://github.com/crystal-lang/crystal/pull/14778\n[#14728]: https://github.com/crystal-lang/crystal/pull/14728\n[#14837]: https://github.com/crystal-lang/crystal/pull/14837\n[#14921]: https://github.com/crystal-lang/crystal/pull/14921\n[#14940]: https://github.com/crystal-lang/crystal/pull/14940\n[#14958]: https://github.com/crystal-lang/crystal/pull/14958\n[#14941]: https://github.com/crystal-lang/crystal/pull/14941\n[#14943]: https://github.com/crystal-lang/crystal/pull/14943\n[#14947]: https://github.com/crystal-lang/crystal/pull/14947\n[#14979]: https://github.com/crystal-lang/crystal/pull/14979\n[#14872]: https://github.com/crystal-lang/crystal/pull/14872\n[#14774]: https://github.com/crystal-lang/crystal/pull/14774\n[#14887]: https://github.com/crystal-lang/crystal/pull/14887\n[#14892]: https://github.com/crystal-lang/crystal/pull/14892\n[#14842]: https://github.com/crystal-lang/crystal/pull/14842\n[#14847]: https://github.com/crystal-lang/crystal/pull/14847\n[#14890]: https://github.com/crystal-lang/crystal/pull/14890\n[#11007]: https://github.com/crystal-lang/crystal/pull/11007\n[#14729]: https://github.com/crystal-lang/crystal/pull/14729\n[#14874]: https://github.com/crystal-lang/crystal/pull/14874\n[#14834]: https://github.com/crystal-lang/crystal/pull/14834\n[#14684]: https://github.com/crystal-lang/crystal/pull/14684\n[#14796]: https://github.com/crystal-lang/crystal/pull/14796\n[#14933]: https://github.com/crystal-lang/crystal/pull/14933\n[#14945]: https://github.com/crystal-lang/crystal/pull/14945\n[#14977]: https://github.com/crystal-lang/crystal/pull/14977\n[#14822]: https://github.com/crystal-lang/crystal/pull/14822\n[#14997]: https://github.com/crystal-lang/crystal/pull/14997\n\n#### compiler\n\n- _(cli)_ Adds initial support for external commands ([#14953], thanks @bcardiff)\n- _(interpreter)_ Add `Crystal::Repl::Value#runtime_type` ([#14156], thanks @bcardiff)\n- _(interpreter)_ Implement `Reference.pre_initialize` in the interpreter ([#14968], thanks @HertzDevil)\n- _(interpreter)_ Enable the interpreter on Windows ([#14964], thanks @HertzDevil)\n\n[#14953]: https://github.com/crystal-lang/crystal/pull/14953\n[#14156]: https://github.com/crystal-lang/crystal/pull/14156\n[#14968]: https://github.com/crystal-lang/crystal/pull/14968\n[#14964]: https://github.com/crystal-lang/crystal/pull/14964\n\n### Bugfixes\n\n#### lang\n\n- Fix `Slice.literal` for multiple calls with identical signature ([#15009], thanks @HertzDevil)\n- _(macros)_ Add location info to some `MacroIf` nodes ([#14885], thanks @Blacksmoke16)\n\n[#15009]: https://github.com/crystal-lang/crystal/pull/15009\n[#14885]: https://github.com/crystal-lang/crystal/pull/14885\n\n#### stdlib\n\n- _(collection)_ Fix `Range#size` return type to `Int32` ([#14588], thanks @straight-shoota)\n- _(concurrency)_ Update `DeallocationStack` for Windows context switch ([#15032], thanks @HertzDevil)\n- _(concurrency)_ Fix race condition in `pthread_create` handle initialization ([#15043], thanks @HertzDevil)\n- _(files)_ **[regression]** Fix `File#truncate` and `#lock` for Win32 append-mode files ([#14706], thanks @HertzDevil)\n- _(files)_ **[breaking]** Avoid flush in finalizers for `Socket` and `IO::FileDescriptor` ([#14882], thanks @straight-shoota)\n- _(files)_ Make `IO::Buffered#buffer_size=` idempotent ([#14855], thanks @jgaskins)\n- _(macros)_ Implement `#sort_by` inside macros using `Enumerable#sort_by` ([#14895], thanks @HertzDevil)\n- _(macros)_ Fix internal error when calling `#is_a?` on `External` nodes ([#14918], thanks @HertzDevil)\n- _(networking)_ Use correct timeout for `Socket#connect` on Windows ([#14961], thanks @HertzDevil)\n- _(numeric)_ Fix handle empty string in `String#to_f(whitespace: false)` ([#14902], thanks @Blacksmoke16)\n- _(numeric)_ Fix exponent wrapping in `Math.frexp(BigFloat)` for very large values ([#14971], thanks @HertzDevil)\n- _(numeric)_ Fix exponent overflow in `BigFloat#to_s` for very large values ([#14982], thanks @HertzDevil)\n- _(numeric)_ Add missing `@[Link(dll:)]` annotation to MPIR ([#15003], thanks @HertzDevil)\n- _(runtime)_ Add missing return type of `LibC.VirtualQuery` ([#15036], thanks @HertzDevil)\n- _(runtime)_ Fix main stack top detection on musl-libc ([#15047], thanks @HertzDevil)\n- _(serialization)_ **[breaking]** Remove `XML::Error.errors` ([#14936], thanks @straight-shoota)\n- _(specs)_ **[regression]** Fix `Expectations::Be` for module type ([#14926], thanks @straight-shoota)\n- _(system)_ Fix return type restriction for `ENV.fetch` ([#14919], thanks @straight-shoota)\n- _(system)_ `#file_descriptor_close` should set `@closed` (UNIX) ([#14973], thanks @ysbaddaden)\n- _(system)_ reinit event loop first after fork (UNIX) ([#14975], thanks @ysbaddaden)\n- _(text)_ Fix avoid linking `libpcre` when unused ([#14891], thanks @kojix2)\n- _(text)_ Add type restriction to `String#byte_index` `offset` parameter ([#14981], thanks @straight-shoota)\n\n[#14588]: https://github.com/crystal-lang/crystal/pull/14588\n[#15032]: https://github.com/crystal-lang/crystal/pull/15032\n[#15043]: https://github.com/crystal-lang/crystal/pull/15043\n[#14706]: https://github.com/crystal-lang/crystal/pull/14706\n[#14882]: https://github.com/crystal-lang/crystal/pull/14882\n[#14855]: https://github.com/crystal-lang/crystal/pull/14855\n[#14895]: https://github.com/crystal-lang/crystal/pull/14895\n[#14918]: https://github.com/crystal-lang/crystal/pull/14918\n[#14961]: https://github.com/crystal-lang/crystal/pull/14961\n[#14902]: https://github.com/crystal-lang/crystal/pull/14902\n[#14971]: https://github.com/crystal-lang/crystal/pull/14971\n[#14982]: https://github.com/crystal-lang/crystal/pull/14982\n[#15003]: https://github.com/crystal-lang/crystal/pull/15003\n[#15036]: https://github.com/crystal-lang/crystal/pull/15036\n[#15047]: https://github.com/crystal-lang/crystal/pull/15047\n[#14936]: https://github.com/crystal-lang/crystal/pull/14936\n[#14926]: https://github.com/crystal-lang/crystal/pull/14926\n[#14919]: https://github.com/crystal-lang/crystal/pull/14919\n[#14973]: https://github.com/crystal-lang/crystal/pull/14973\n[#14975]: https://github.com/crystal-lang/crystal/pull/14975\n[#14891]: https://github.com/crystal-lang/crystal/pull/14891\n[#14981]: https://github.com/crystal-lang/crystal/pull/14981\n\n#### compiler\n\n- _(cli)_ Add error handling for linker flag sub commands ([#14932], thanks @straight-shoota)\n- _(codegen)_ Allow returning `Proc`s from top-level funs ([#14917], thanks @HertzDevil)\n- _(codegen)_ Fix CRT static-dynamic linking conflict in specs with C sources ([#14970], thanks @HertzDevil)\n- _(interpreter)_ Fix Linux `getrandom` failure in interpreted code ([#15035], thanks @HertzDevil)\n- _(interpreter)_ Fix undefined behavior in interpreter mixed union upcast ([#15042], thanks @HertzDevil)\n- _(semantic)_ Fix `TopLevelVisitor` adding existing `ClassDef` type to current scope ([#15067], thanks @straight-shoota)\n\n[#14932]: https://github.com/crystal-lang/crystal/pull/14932\n[#14917]: https://github.com/crystal-lang/crystal/pull/14917\n[#14970]: https://github.com/crystal-lang/crystal/pull/14970\n[#15035]: https://github.com/crystal-lang/crystal/pull/15035\n[#15042]: https://github.com/crystal-lang/crystal/pull/15042\n[#15067]: https://github.com/crystal-lang/crystal/pull/15067\n\n#### tools\n\n- _(dependencies)_ Fix `crystal tool dependencies` format flat ([#14927], thanks @straight-shoota)\n- _(dependencies)_ Fix `crystal tool dependencies` filters for Windows paths ([#14928], thanks @straight-shoota)\n- _(docs-generator)_ Fix doc comment above annotation with macro expansion ([#14849], thanks @Blacksmoke16)\n- _(unreachable)_ Fix `crystal tool unreachable` & co visiting circular hierarchies ([#15065], thanks @straight-shoota)\n\n[#14927]: https://github.com/crystal-lang/crystal/pull/14927\n[#14928]: https://github.com/crystal-lang/crystal/pull/14928\n[#14849]: https://github.com/crystal-lang/crystal/pull/14849\n[#15065]: https://github.com/crystal-lang/crystal/pull/15065\n\n### Chores\n\n#### stdlib\n\n- **[deprecation]** Use `Time::Span` in `Benchmark.ips` ([#14805], thanks @HertzDevil)\n- **[deprecation]** Deprecate `::sleep(Number)` ([#14962], thanks @HertzDevil)\n- _(runtime)_ **[deprecation]** Deprecate `Pointer.new(Int)` ([#14875], thanks @straight-shoota)\n\n[#14805]: https://github.com/crystal-lang/crystal/pull/14805\n[#14962]: https://github.com/crystal-lang/crystal/pull/14962\n[#14875]: https://github.com/crystal-lang/crystal/pull/14875\n\n#### compiler\n\n- _(interpreter)_ Remove TODO in `Crystal::Loader` on Windows ([#14988], thanks @HertzDevil)\n- _(interpreter:repl)_ Update REPLy version ([#14950], thanks @HertzDevil)\n\n[#14988]: https://github.com/crystal-lang/crystal/pull/14988\n[#14950]: https://github.com/crystal-lang/crystal/pull/14950\n\n### Performance\n\n#### stdlib\n\n- _(collection)_ Always use unstable sort for simple types ([#14825], thanks @HertzDevil)\n- _(collection)_ Optimize `Hash#transform_{keys,values}` ([#14502], thanks @jgaskins)\n- _(numeric)_ Optimize arithmetic between `BigFloat` and integers ([#14944], thanks @HertzDevil)\n- _(runtime)_ **[regression]** Cache `Exception::CallStack.empty` to avoid repeat `Array` allocation ([#15025], thanks @straight-shoota)\n\n[#14825]: https://github.com/crystal-lang/crystal/pull/14825\n[#14502]: https://github.com/crystal-lang/crystal/pull/14502\n[#14944]: https://github.com/crystal-lang/crystal/pull/14944\n[#15025]: https://github.com/crystal-lang/crystal/pull/15025\n\n#### compiler\n\n- Avoid unwinding the stack on hot path in method call lookups ([#15002], thanks @ggiraldez)\n- _(codegen)_ Reduce calls to `Crystal::Type#remove_indirection` in module dispatch ([#14992], thanks @HertzDevil)\n- _(codegen)_ Compiler: enable parallel codegen with MT ([#14748], thanks @ysbaddaden)\n\n[#15002]: https://github.com/crystal-lang/crystal/pull/15002\n[#14992]: https://github.com/crystal-lang/crystal/pull/14992\n[#14748]: https://github.com/crystal-lang/crystal/pull/14748\n\n### Refactor\n\n#### stdlib\n\n- _(concurrency)_ Extract `select` from `src/channel.cr` ([#14912], thanks @straight-shoota)\n- _(concurrency)_ Make `Crystal::IOCP::OverlappedOperation` abstract ([#14987], thanks @HertzDevil)\n- _(files)_ Move `#evented_read`, `#evented_write` into `Crystal::LibEvent::EventLoop` ([#14883], thanks @straight-shoota)\n- _(networking)_ Simplify `Socket::Addrinfo.getaddrinfo(&)` ([#14956], thanks @HertzDevil)\n- _(networking)_ Add `Crystal::System::Addrinfo` ([#14957], thanks @HertzDevil)\n- _(runtime)_ Add `Exception::CallStack.empty` ([#15017], thanks @straight-shoota)\n- _(system)_ Refactor cancellation of `IOCP::OverlappedOperation` ([#14754], thanks @straight-shoota)\n- _(system)_ Include `Crystal::System::Group` instead of extending it ([#14930], thanks @HertzDevil)\n- _(system)_ Include `Crystal::System::User` instead of extending it ([#14929], thanks @HertzDevil)\n- _(system)_ Fix: `Crystal::SpinLock` doesn't need to be allocated on the HEAP ([#14972], thanks @ysbaddaden)\n- _(system)_ Don't involve evloop after fork in `System::Process.spawn` (UNIX) ([#14974], thanks @ysbaddaden)\n- _(system)_ Refactor `EventLoop` interface for sleeps & select timeouts ([#14980], thanks @ysbaddaden)\n\n[#14912]: https://github.com/crystal-lang/crystal/pull/14912\n[#14987]: https://github.com/crystal-lang/crystal/pull/14987\n[#14883]: https://github.com/crystal-lang/crystal/pull/14883\n[#14956]: https://github.com/crystal-lang/crystal/pull/14956\n[#14957]: https://github.com/crystal-lang/crystal/pull/14957\n[#15017]: https://github.com/crystal-lang/crystal/pull/15017\n[#14754]: https://github.com/crystal-lang/crystal/pull/14754\n[#14930]: https://github.com/crystal-lang/crystal/pull/14930\n[#14929]: https://github.com/crystal-lang/crystal/pull/14929\n[#14972]: https://github.com/crystal-lang/crystal/pull/14972\n[#14974]: https://github.com/crystal-lang/crystal/pull/14974\n[#14980]: https://github.com/crystal-lang/crystal/pull/14980\n\n#### compiler\n\n- _(codegen)_ Compiler: refactor codegen ([#14760], thanks @ysbaddaden)\n- _(interpreter)_ Refactor interpreter stack code to avoid duplicate macro expansion ([#14876], thanks @straight-shoota)\n\n[#14760]: https://github.com/crystal-lang/crystal/pull/14760\n[#14876]: https://github.com/crystal-lang/crystal/pull/14876\n\n### Documentation\n\n#### stdlib\n\n- _(collection)_ **[breaking]** Hide `Hash::Entry` from public API docs ([#14881], thanks @Blacksmoke16)\n- _(collection)_ Fix typos in docs for `Set` and `Hash` ([#14889], thanks @philipp-classen)\n- _(llvm)_ Add `@[Experimental]` to `LLVM::DIBuilder` ([#14854], thanks @HertzDevil)\n- _(networking)_ Add documentation about synchronous DNS resolution ([#15027], thanks @straight-shoota)\n- _(networking)_ Add `uri/json` to `docs_main` ([#15069], thanks @straight-shoota)\n- _(runtime)_ Add docs about `Pointer`'s alignment requirement ([#14853], thanks @HertzDevil)\n- _(runtime)_ Reword `Pointer#memcmp`'s documentation ([#14818], thanks @HertzDevil)\n- _(runtime)_ Add documentation for `NoReturn` and `Void` ([#14817], thanks @nobodywasishere)\n\n[#14881]: https://github.com/crystal-lang/crystal/pull/14881\n[#14889]: https://github.com/crystal-lang/crystal/pull/14889\n[#14854]: https://github.com/crystal-lang/crystal/pull/14854\n[#15027]: https://github.com/crystal-lang/crystal/pull/15027\n[#15069]: https://github.com/crystal-lang/crystal/pull/15069\n[#14853]: https://github.com/crystal-lang/crystal/pull/14853\n[#14818]: https://github.com/crystal-lang/crystal/pull/14818\n[#14817]: https://github.com/crystal-lang/crystal/pull/14817\n\n### Specs\n\n#### stdlib\n\n- Remove some uses of deprecated overloads in standard library specs ([#14963], thanks @HertzDevil)\n- _(collection)_ Disable `Tuple#to_static_array` spec on AArch64 ([#14844], thanks @HertzDevil)\n- _(serialization)_ Add JSON parsing UTF-8 spec ([#14823], thanks @Blacksmoke16)\n- _(text)_ Add specs for `String#index`, `#rindex` search for `Char::REPLACEMENT` ([#14946], thanks @straight-shoota)\n\n[#14963]: https://github.com/crystal-lang/crystal/pull/14963\n[#14844]: https://github.com/crystal-lang/crystal/pull/14844\n[#14823]: https://github.com/crystal-lang/crystal/pull/14823\n[#14946]: https://github.com/crystal-lang/crystal/pull/14946\n\n#### compiler\n\n- _(codegen)_ Support return types in codegen specs ([#14888], thanks @HertzDevil)\n- _(codegen)_ Fix codegen spec for `ProcPointer` of virtual type ([#14903], thanks @HertzDevil)\n- _(codegen)_ Support LLVM OrcV2 codegen specs ([#14886], thanks @HertzDevil)\n- _(codegen)_ Don't spawn subprocess if codegen spec uses flags but not the prelude ([#14904], thanks @HertzDevil)\n\n[#14888]: https://github.com/crystal-lang/crystal/pull/14888\n[#14903]: https://github.com/crystal-lang/crystal/pull/14903\n[#14886]: https://github.com/crystal-lang/crystal/pull/14886\n[#14904]: https://github.com/crystal-lang/crystal/pull/14904\n\n### Infrastructure\n\n- Changelog for 1.14.0 ([#14969], thanks @straight-shoota)\n- Update previous Crystal release 1.13.1 ([#14810], thanks @straight-shoota)\n- Refactor GitHub changelog generator print special infra ([#14795], thanks @straight-shoota)\n- Update distribution-scripts ([#14877], thanks @straight-shoota)\n- Update version in `shard.yml` ([#14909], thanks @straight-shoota)\n- Merge `release/1.13`@1.13.2 ([#14924], thanks @straight-shoota)\n- Update previous Crystal release 1.13.2 ([#14925], thanks @straight-shoota)\n- Merge `release/1.13`@1.13.3 ([#15012], thanks @straight-shoota)\n- Update previous Crystal release 1.13.3 ([#15016], thanks @straight-shoota)\n- **[regression]** Fix `SOURCE_DATE_EPOCH` in `Makefile.win` ([#14922], thanks @HertzDevil)\n- _(ci)_ Update actions/checkout action to v4 - autoclosed ([#14896], thanks @renovate)\n- _(ci)_ Update LLVM 18 for `wasm32-test` ([#14821], thanks @straight-shoota)\n- _(ci)_ Pin package repos for OpenSSL packages ([#14831], thanks @straight-shoota)\n- _(ci)_ Upgrade XCode 15.4.0 ([#14794], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#14535], thanks @renovate)\n- _(ci)_ Add test for OpenSSL 3.3 ([#14873], thanks @straight-shoota)\n- _(ci)_ Update GitHub runner to `macos-14` ([#14833], thanks @straight-shoota)\n- _(ci)_ Refactor SSL workflow with job matrix ([#14899], thanks @straight-shoota)\n- _(ci)_ Drop the non-release Windows compiler artifact ([#15000], thanks @HertzDevil)\n- _(ci)_ Fix compiler artifact name in WindowsCI ([#15021], thanks @straight-shoota)\n- _(ci)_ Restrict CI runners from runs-on to 8GB ([#15030], thanks @straight-shoota)\n- _(ci)_ Increase memory for stdlib CI runners from runs-on to 16GB ([#15044], thanks @straight-shoota)\n- _(ci)_ Use Cygwin to build libiconv on Windows CI ([#14999], thanks @HertzDevil)\n- _(ci)_ Use our own `libffi` repository on Windows CI ([#14998], thanks @HertzDevil)\n\n[#14969]: https://github.com/crystal-lang/crystal/pull/14969\n[#14810]: https://github.com/crystal-lang/crystal/pull/14810\n[#14795]: https://github.com/crystal-lang/crystal/pull/14795\n[#14877]: https://github.com/crystal-lang/crystal/pull/14877\n[#14909]: https://github.com/crystal-lang/crystal/pull/14909\n[#14924]: https://github.com/crystal-lang/crystal/pull/14924\n[#14925]: https://github.com/crystal-lang/crystal/pull/14925\n[#15012]: https://github.com/crystal-lang/crystal/pull/15012\n[#15016]: https://github.com/crystal-lang/crystal/pull/15016\n[#14922]: https://github.com/crystal-lang/crystal/pull/14922\n[#14896]: https://github.com/crystal-lang/crystal/pull/14896\n[#14821]: https://github.com/crystal-lang/crystal/pull/14821\n[#14831]: https://github.com/crystal-lang/crystal/pull/14831\n[#14794]: https://github.com/crystal-lang/crystal/pull/14794\n[#14535]: https://github.com/crystal-lang/crystal/pull/14535\n[#14873]: https://github.com/crystal-lang/crystal/pull/14873\n[#14833]: https://github.com/crystal-lang/crystal/pull/14833\n[#14899]: https://github.com/crystal-lang/crystal/pull/14899\n[#15000]: https://github.com/crystal-lang/crystal/pull/15000\n[#15021]: https://github.com/crystal-lang/crystal/pull/15021\n[#15030]: https://github.com/crystal-lang/crystal/pull/15030\n[#15044]: https://github.com/crystal-lang/crystal/pull/15044\n[#14999]: https://github.com/crystal-lang/crystal/pull/14999\n[#14998]: https://github.com/crystal-lang/crystal/pull/14998\n"
  },
  {
    "path": "doc/changelogs/v1.15.md",
    "content": "# Changelog 1.15\n\n## [1.15.1] (2025-02-04)\n\n[1.15.1]: https://github.com/crystal-lang/crystal/releases/1.15.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(networking)_ Disable directory path redirect when `directory_listing=false` ([#15393], thanks @straight-shoota)\n- _(runtime)_ **[regression]** abstract `EventLoop::Polling#system_add` invalid signature ([#15380], backported from [#15358], thanks @straight-shoota)\n- _(system)_ **[regression]** Fix GC `sig_suspend`, `sig_resume` for `gc_none` ([#15382], backported from [#15349], thanks @ysbaddaden)\n\n[#15393]: https://github.com/crystal-lang/crystal/pull/15393\n[#15380]: https://github.com/crystal-lang/crystal/pull/15380\n[#15358]: https://github.com/crystal-lang/crystal/pull/15358\n[#15382]: https://github.com/crystal-lang/crystal/pull/15382\n[#15349]: https://github.com/crystal-lang/crystal/pull/15349\n\n### Documentation\n\n#### stdlib\n\n- _(system)_ Fix code example in `Process::Status#exit_code` docs ([#15381], backported from [#15351], thanks @zw963)\n\n[#15381]: https://github.com/crystal-lang/crystal/pull/15381\n[#15351]: https://github.com/crystal-lang/crystal/pull/15351\n\n### Infrastructure\n\n- Changelog for 1.15.1 ([#15406], thanks @straight-shoota)\n- Update distribution-scripts ([#15385], backported from [#15368], thanks @straight-shoota)\n- Update distribution-scripts ([#15388], thanks @straight-shoota)\n- _(ci)_ Add build shards to `mingw-w64` workflow ([#15344], thanks @straight-shoota)\n- _(ci)_ Update shards 0.19.1 ([#15384], backported from [#15366], thanks @straight-shoota)\n- _(ci)_ Add check for shards binary in `test_dist_linux_on_docker` ([#15394], thanks @straight-shoota)\n\n[#15406]: https://github.com/crystal-lang/crystal/pull/15406\n[#15385]: https://github.com/crystal-lang/crystal/pull/15385\n[#15368]: https://github.com/crystal-lang/crystal/pull/15368\n[#15388]: https://github.com/crystal-lang/crystal/pull/15388\n[#15344]: https://github.com/crystal-lang/crystal/pull/15344\n[#15384]: https://github.com/crystal-lang/crystal/pull/15384\n[#15366]: https://github.com/crystal-lang/crystal/pull/15366\n[#15394]: https://github.com/crystal-lang/crystal/pull/15394\n\n## [1.15.0] (2025-01-09)\n\n[1.15.0]: https://github.com/crystal-lang/crystal/releases/1.15.0\n\n### Breaking changes\n\n#### lang\n\n- Allow constants to start with non-ascii uppercase and titlecase ([#15148], thanks @nanobowers)\n\n[#15148]: https://github.com/crystal-lang/crystal/pull/15148\n\n### Features\n\n#### lang\n\n- _(macros)_ Crystal `Not` operators do not need parens for stringification ([#15292], thanks @Blacksmoke16)\n- _(macros)_ Add `MacroIf#is_unless?` AST node method ([#15304], thanks @Blacksmoke16)\n\n[#15292]: https://github.com/crystal-lang/crystal/pull/15292\n[#15304]: https://github.com/crystal-lang/crystal/pull/15304\n\n#### stdlib\n\n- _(collection)_ Add `Iterator(T).empty` ([#15039], thanks @spuun)\n- _(collection)_ Add `Enumerable#find_value` ([#14893], thanks @jgaskins)\n- _(concurrency)_ Implement the ARM64 Windows context switch ([#15155], thanks @HertzDevil)\n- _(concurrency)_ Add optional `name` parameter forward to `WaitGroup#spawn` ([#15189], thanks @spuun)\n- _(crypto)_ Enable bindings for functions in LibreSSL ([#15177], thanks @straight-shoota)\n- _(log)_ Add `Log` overloads for logging exceptions without giving a block ([#15257], thanks @lachlan)\n- _(networking)_ Better handle explicit chunked encoding responses ([#15092], thanks @Blacksmoke16)\n- _(networking)_ Support OpenSSL on MSYS2 ([#15111], thanks @HertzDevil)\n- _(networking)_ Add `Socket::Address.from` without `addrlen` ([#15060], thanks @mamantoha)\n- _(networking)_ Add stringification for `HTTP::Cookie` ([#15240], thanks @straight-shoota)\n- _(networking)_ Add stringification for `HTTP::Cookies` ([#15246], thanks @straight-shoota)\n- _(networking)_ Add `HTTP::Cookie#expire` ([#14819], thanks @a-alhusaini)\n- _(numeric)_ Implement `fast_float` for `String#to_f` ([#15195], thanks @HertzDevil)\n- _(runtime)_ Support call stacks for MinGW-w64 builds ([#15117], thanks @HertzDevil)\n- _(runtime)_ Support MSYS2's CLANGARM64 environment on ARM64 Windows ([#15159], thanks @HertzDevil)\n- _(runtime)_ Improve `Crystal::Tracing` ([#15297], thanks @ysbaddaden)\n- _(runtime)_ Add `Thread#internal_name=` ([#15298], thanks @ysbaddaden)\n- _(runtime)_ Add `Thread::LinkedList#each` to safely iterate lists ([#15300], thanks @ysbaddaden)\n- _(system)_ Add `Process::Status#exit_code?` ([#15247], thanks @straight-shoota)\n- _(system)_ Add `Process::Status#abnormal_exit?` ([#15266], thanks @straight-shoota)\n- _(system)_ Improve `Process::Status#to_s` for abnormal exits on Windows ([#15283], thanks @straight-shoota)\n- _(system)_ Add `Process::Status#exit_signal?` ([#15284], thanks @straight-shoota)\n- _(system)_ Change `Process::Status#to_s` to hex format on Windows ([#15285], thanks @straight-shoota)\n- _(system)_ Add `Process::Status#system_exit_status` ([#15296], thanks @straight-shoota)\n- _(text)_ Add `Regex::CompileOptions::MULTILINE_ONLY` ([#14870], thanks @ralsina)\n- _(text)_ Add type restrictions to Levenshtein ([#15168], thanks @beta-ziliani)\n- _(text)_ Add `unit_separator` to `Int#humanize` and `#humanize_bytes` ([#15176], thanks @CTC97)\n- _(text)_ Add `String#byte_index(Regex)` ([#15248], thanks @Zeljko-Predjeskovic)\n- _(text)_ Add `Colorize::Object#ansi_escape` ([#15113], thanks @devnote-dev)\n\n[#15039]: https://github.com/crystal-lang/crystal/pull/15039\n[#14893]: https://github.com/crystal-lang/crystal/pull/14893\n[#15155]: https://github.com/crystal-lang/crystal/pull/15155\n[#15189]: https://github.com/crystal-lang/crystal/pull/15189\n[#15177]: https://github.com/crystal-lang/crystal/pull/15177\n[#15257]: https://github.com/crystal-lang/crystal/pull/15257\n[#15092]: https://github.com/crystal-lang/crystal/pull/15092\n[#15111]: https://github.com/crystal-lang/crystal/pull/15111\n[#15060]: https://github.com/crystal-lang/crystal/pull/15060\n[#15240]: https://github.com/crystal-lang/crystal/pull/15240\n[#15246]: https://github.com/crystal-lang/crystal/pull/15246\n[#14819]: https://github.com/crystal-lang/crystal/pull/14819\n[#15195]: https://github.com/crystal-lang/crystal/pull/15195\n[#15117]: https://github.com/crystal-lang/crystal/pull/15117\n[#15159]: https://github.com/crystal-lang/crystal/pull/15159\n[#15297]: https://github.com/crystal-lang/crystal/pull/15297\n[#15298]: https://github.com/crystal-lang/crystal/pull/15298\n[#15300]: https://github.com/crystal-lang/crystal/pull/15300\n[#15247]: https://github.com/crystal-lang/crystal/pull/15247\n[#15266]: https://github.com/crystal-lang/crystal/pull/15266\n[#15283]: https://github.com/crystal-lang/crystal/pull/15283\n[#15284]: https://github.com/crystal-lang/crystal/pull/15284\n[#15285]: https://github.com/crystal-lang/crystal/pull/15285\n[#15296]: https://github.com/crystal-lang/crystal/pull/15296\n[#14870]: https://github.com/crystal-lang/crystal/pull/14870\n[#15168]: https://github.com/crystal-lang/crystal/pull/15168\n[#15176]: https://github.com/crystal-lang/crystal/pull/15176\n[#15248]: https://github.com/crystal-lang/crystal/pull/15248\n[#15113]: https://github.com/crystal-lang/crystal/pull/15113\n\n#### compiler\n\n- Basic MinGW-w64 cross-compilation support ([#15070], [#15219], thanks @HertzDevil, @BlobCodes)\n- _(cli)_ Support building from a MinGW-w64-based compiler ([#15077], thanks @HertzDevil)\n- _(codegen)_ Add indirect branch tracking ([#15122], thanks @ysbaddaden)\n- _(codegen)_ Emit position dependent code for embedded targets ([#15174], thanks @RX14)\n- _(interpreter)_ Support \"long format\" DLL import libraries ([#15119], thanks @HertzDevil)\n- _(interpreter)_ Add `cc`'s search paths to Unix dynamic library loader ([#15127], thanks @HertzDevil)\n- _(interpreter)_ Basic MinGW-w64-based interpreter support ([#15140], thanks @HertzDevil)\n- _(parser)_ Add `ECR::Lexer::SyntaxException` with location info ([#15222], thanks @nobodywasishere)\n\n[#15070]: https://github.com/crystal-lang/crystal/pull/15070\n[#15219]: https://github.com/crystal-lang/crystal/pull/15219\n[#15077]: https://github.com/crystal-lang/crystal/pull/15077\n[#15122]: https://github.com/crystal-lang/crystal/pull/15122\n[#15174]: https://github.com/crystal-lang/crystal/pull/15174\n[#15119]: https://github.com/crystal-lang/crystal/pull/15119\n[#15127]: https://github.com/crystal-lang/crystal/pull/15127\n[#15140]: https://github.com/crystal-lang/crystal/pull/15140\n[#15222]: https://github.com/crystal-lang/crystal/pull/15222\n\n#### tools\n\n- _(formatter)_ Enable pending formatter features ([#14718], thanks @Blacksmoke16)\n- _(unreachable)_ Implement `codecov` format for `unreachable` tool ([#15059], thanks @Blacksmoke16)\n\n[#14718]: https://github.com/crystal-lang/crystal/pull/14718\n[#15059]: https://github.com/crystal-lang/crystal/pull/15059\n\n### Bugfixes\n\n#### lang\n\n- _(macros)_ Add location information to more MacroIf related nodes ([#15100], thanks @Blacksmoke16)\n\n[#15100]: https://github.com/crystal-lang/crystal/pull/15100\n\n#### stdlib\n\n- LibC bindings and std specs on NetBSD 10 ([#15115], thanks @ysbaddaden)\n- _(files)_ Treat `WinError::ERROR_DIRECTORY` as an error for non-existent files ([#15114], thanks @HertzDevil)\n- _(files)_ Replace handle atomically in `IO::FileDescriptor#close` on Windows ([#15165], thanks @HertzDevil)\n- _(llvm)_ Fix `find-llvm-config` to ignore `LLVM_CONFIG`'s escape sequences ([#15076], thanks @HertzDevil)\n- _(log)_ **[regression]** Fix `Log` to emit with `exception` even if block outputs `nil` ([#15253], thanks @lachlan)\n- _(macros)_ Avoid identifier naming collision in `getter`, `setter`, and `property` macros ([#15239], thanks @jgaskins)\n- _(networking)_ **[regression]** Fix `UNIXSocket#receive` ([#15107], thanks @straight-shoota)\n- _(numeric)_ Fix `Complex#/` edge cases ([#15086], thanks @HertzDevil)\n- _(numeric)_ Fix `Number#humanize` printing of `(-)Infinity` and `NaN` ([#15090], thanks @lachlan)\n- _(runtime)_ Fix Deadlock with parallel stop-world/fork calls in MT ([#15096], thanks @ysbaddaden)\n- _(runtime)_ **[regression]** Protect constant initializers with mutex on Windows ([#15134], thanks @HertzDevil)\n- _(runtime)_ use `uninitialized LibC::SigsetT` ([#15144], thanks @straight-shoota)\n- _(runtime)_ Fix static linking when using MinGW-w64 ([#15167], thanks @HertzDevil)\n- _(runtime)_ register GC callbacks inside `GC.init` ([#15278], thanks @ysbaddaden)\n- _(runtime)_ Cleanup nodes in `Thread::LinkedList(T)#delete` ([#15295], thanks @ysbaddaden)\n- _(runtime)_ Make `Crystal::EventLoop#remove(io)` a class method ([#15282], thanks @ysbaddaden)\n- _(system)_ Raise on abnormal exit in `Process::Status#exit_code` ([#15241], thanks @straight-shoota)\n- _(system)_ Fix `Process::Status` for unknown signals ([#15280], thanks @straight-shoota)\n- _(system)_ Fix error handling for `LibC.clock_gettime(CLOCK_MONOTONIC)` calls ([#15309], thanks @compumike)\n- _(text)_ Fix libiconv build on Windows ([#15095], thanks @HertzDevil)\n- _(text)_ Change `sprintf \"%c\"` to support only `Char` and `Int::Primitive` ([#15142], thanks @nanobowers)\n- _(time)_ Fix proper error handling for early end in `HTTP_DATE` parser ([#15232], thanks @straight-shoota)\n\n[#15115]: https://github.com/crystal-lang/crystal/pull/15115\n[#15114]: https://github.com/crystal-lang/crystal/pull/15114\n[#15165]: https://github.com/crystal-lang/crystal/pull/15165\n[#15076]: https://github.com/crystal-lang/crystal/pull/15076\n[#15253]: https://github.com/crystal-lang/crystal/pull/15253\n[#15239]: https://github.com/crystal-lang/crystal/pull/15239\n[#15107]: https://github.com/crystal-lang/crystal/pull/15107\n[#15086]: https://github.com/crystal-lang/crystal/pull/15086\n[#15090]: https://github.com/crystal-lang/crystal/pull/15090\n[#15096]: https://github.com/crystal-lang/crystal/pull/15096\n[#15134]: https://github.com/crystal-lang/crystal/pull/15134\n[#15144]: https://github.com/crystal-lang/crystal/pull/15144\n[#15167]: https://github.com/crystal-lang/crystal/pull/15167\n[#15278]: https://github.com/crystal-lang/crystal/pull/15278\n[#15295]: https://github.com/crystal-lang/crystal/pull/15295\n[#15282]: https://github.com/crystal-lang/crystal/pull/15282\n[#15241]: https://github.com/crystal-lang/crystal/pull/15241\n[#15280]: https://github.com/crystal-lang/crystal/pull/15280\n[#15309]: https://github.com/crystal-lang/crystal/pull/15309\n[#15095]: https://github.com/crystal-lang/crystal/pull/15095\n[#15142]: https://github.com/crystal-lang/crystal/pull/15142\n[#15232]: https://github.com/crystal-lang/crystal/pull/15232\n\n#### compiler\n\n- OpenBSD: fix integration and broken specs ([#15118], thanks @ysbaddaden)\n- _(interpreter)_ setup signal handlers in interpreted code ([#14766], [#15178], thanks @ysbaddaden, @straight-shoota)\n- _(parser)_ Fix `SyntaxHighlighter` delimiter state ([#15104], thanks @straight-shoota)\n- _(parser)_ Disallow weird assignments ([#14815], thanks @FnControlOption)\n\n[#15118]: https://github.com/crystal-lang/crystal/pull/15118\n[#14766]: https://github.com/crystal-lang/crystal/pull/14766\n[#15178]: https://github.com/crystal-lang/crystal/pull/15178\n[#15104]: https://github.com/crystal-lang/crystal/pull/15104\n[#14815]: https://github.com/crystal-lang/crystal/pull/14815\n\n#### tools\n\n- Improve man and shell completion for tools ([#15082], thanks @Blacksmoke16)\n- _(docs-generator)_ Fix first doc comment inside macro yield ([#15050], thanks @RX14)\n- _(implementations)_ Fix `tool implementations` to handle gracefully a def with missing location ([#15273], thanks @straight-shoota)\n\n[#15082]: https://github.com/crystal-lang/crystal/pull/15082\n[#15050]: https://github.com/crystal-lang/crystal/pull/15050\n[#15273]: https://github.com/crystal-lang/crystal/pull/15273\n\n### Chores\n\n#### stdlib\n\n- Fix various typos ([#15080], thanks @kojix2)\n- _(runtime)_ Make `Enum` an abstract struct ([#15274], thanks @straight-shoota)\n- _(system)_ **[deprecation]** Deprecate `Process::Status#exit_status` ([#8647], thanks @jwoertink)\n- _(system)_ Redefine `Process::Status#normal_exit?` on Windows ([#15255], [#15267], thanks @straight-shoota)\n- _(system)_ **[breaking]** Redefine `Process::Status#signal_exit?` ([#15289], thanks @straight-shoota)\n\n[#15080]: https://github.com/crystal-lang/crystal/pull/15080\n[#15274]: https://github.com/crystal-lang/crystal/pull/15274\n[#8647]: https://github.com/crystal-lang/crystal/pull/8647\n[#15255]: https://github.com/crystal-lang/crystal/pull/15255\n[#15267]: https://github.com/crystal-lang/crystal/pull/15267\n[#15289]: https://github.com/crystal-lang/crystal/pull/15289\n\n#### compiler\n\n- _(codegen)_ Link i128 constants internally if possible ([#15217], thanks @BlobCodes)\n- _(parser)_ Add location to `RegexLiteral` ([#15235], thanks @straight-shoota)\n\n[#15217]: https://github.com/crystal-lang/crystal/pull/15217\n[#15235]: https://github.com/crystal-lang/crystal/pull/15235\n\n### Performance\n\n#### stdlib\n\n- _(collection)_ Optimize `Slice#<=>` and `#==` with reference check ([#15234], thanks @straight-shoota)\n- _(concurrency)_ Do not over-commit fiber stacks on Windows ([#15037], thanks @HertzDevil)\n- _(text)_ Pre-compute `String` size after `#chomp()` if possible ([#15153], thanks @HertzDevil)\n- _(text)_ Optimize `String#rchop?()` ([#15175], thanks @HertzDevil)\n- _(text)_ Optimize `String#==` taking character size into account ([#15233], thanks @straight-shoota)\n\n[#15234]: https://github.com/crystal-lang/crystal/pull/15234\n[#15037]: https://github.com/crystal-lang/crystal/pull/15037\n[#15153]: https://github.com/crystal-lang/crystal/pull/15153\n[#15175]: https://github.com/crystal-lang/crystal/pull/15175\n[#15233]: https://github.com/crystal-lang/crystal/pull/15233\n\n#### compiler\n\n- _(semantic)_ Inline `ASTNode` bindings dependencies and observers ([#15098], thanks @ggiraldez)\n\n[#15098]: https://github.com/crystal-lang/crystal/pull/15098\n\n### Refactor\n\n#### stdlib\n\n- Use Win32 heap functions with `-Dgc_none` ([#15173], thanks @HertzDevil)\n- _(collection)_ Refactor `Enumerable#map` to delegate to `#map_with_index` ([#15210], thanks @straight-shoota)\n- _(concurrency)_ Drop `Crystal::FiberChannel` ([#15245], thanks @ysbaddaden)\n- _(runtime)_ Refactor uses of `LibC.dladdr` inside `Exception::CallStack` ([#15108], thanks @HertzDevil)\n- _(runtime)_ Introduce `Crystal::EventLoop` namespace ([#15226], thanks @ysbaddaden)\n- _(runtime)_ Change `libevent` event loop to wait forever when blocking ([#15243], thanks @ysbaddaden)\n- _(runtime)_ Refactor the IOCP event loop (timers, ...) ([#15238], thanks @ysbaddaden)\n- _(runtime)_ Explicit exit from main ([#15299], thanks @ysbaddaden)\n- _(serialization)_ Use per-thread libxml2 global state on all platforms ([#15121], thanks @HertzDevil)\n- _(system)_ Assume `getrandom` on Linux ([#15040], thanks @ysbaddaden)\n- _(system)_ Refactor Lifetime Event Loop ([#14996], [#15205], [#15206], [#15215], [#15301], thanks @ysbaddaden)\n- _(system)_ Refactor use of `Process::Status#exit_code` to `#exit_code?` ([#15254], thanks @straight-shoota)\n- _(system)_ Refactor simplify `Process::Status#exit_reason` on Unix ([#15288], thanks @straight-shoota)\n\n[#15173]: https://github.com/crystal-lang/crystal/pull/15173\n[#15210]: https://github.com/crystal-lang/crystal/pull/15210\n[#15245]: https://github.com/crystal-lang/crystal/pull/15245\n[#15108]: https://github.com/crystal-lang/crystal/pull/15108\n[#15226]: https://github.com/crystal-lang/crystal/pull/15226\n[#15243]: https://github.com/crystal-lang/crystal/pull/15243\n[#15238]: https://github.com/crystal-lang/crystal/pull/15238\n[#15299]: https://github.com/crystal-lang/crystal/pull/15299\n[#15121]: https://github.com/crystal-lang/crystal/pull/15121\n[#15040]: https://github.com/crystal-lang/crystal/pull/15040\n[#14996]: https://github.com/crystal-lang/crystal/pull/14996\n[#15205]: https://github.com/crystal-lang/crystal/pull/15205\n[#15206]: https://github.com/crystal-lang/crystal/pull/15206\n[#15215]: https://github.com/crystal-lang/crystal/pull/15215\n[#15301]: https://github.com/crystal-lang/crystal/pull/15301\n[#15254]: https://github.com/crystal-lang/crystal/pull/15254\n[#15288]: https://github.com/crystal-lang/crystal/pull/15288\n\n#### compiler\n\n- _(semantic)_ Replace uses of `AliasType#types?` by `Type#lookup_name` ([#15068], thanks @straight-shoota)\n\n[#15068]: https://github.com/crystal-lang/crystal/pull/15068\n\n### Documentation\n\n#### stdlib\n\n- Add docs for lib bindings with supported library versions ([#14900], [#15198], thanks @straight-shoota)\n- _(concurrency)_ Make `Fiber.timeout` and `.cancel_timeout` nodoc ([#15184], thanks @straight-shoota)\n- _(concurrency)_ Update example code for `::spawn` with `WaitGroup` ([#15191], thanks @BigBoyBarney)\n- _(numeric)_ Clarify behavior of `strict` for `String`-to-number conversions ([#15199], thanks @HertzDevil)\n- _(runtime)_ Make `Box` constructor and `object` getter nodoc ([#15136], thanks @straight-shoota)\n- _(runtime)_ Fix `EventLoop` docs for `Socket` `read`, `write` ([#15194], thanks @straight-shoota)\n- _(system)_ Add example for `Dir.glob` ([#15171], thanks @BigBoyBarney)\n- _(system)_ Adjust definition of `ExitReason::Aborted` ([#15256], thanks @straight-shoota)\n- _(text)_ Improve docs for `String#rindex!` ([#15132], thanks @BigBoyBarney)\n- _(text)_ Add note about locale-dependent system error messages ([#15196], thanks @HertzDevil)\n\n[#14900]: https://github.com/crystal-lang/crystal/pull/14900\n[#15198]: https://github.com/crystal-lang/crystal/pull/15198\n[#15184]: https://github.com/crystal-lang/crystal/pull/15184\n[#15191]: https://github.com/crystal-lang/crystal/pull/15191\n[#15199]: https://github.com/crystal-lang/crystal/pull/15199\n[#15136]: https://github.com/crystal-lang/crystal/pull/15136\n[#15194]: https://github.com/crystal-lang/crystal/pull/15194\n[#15171]: https://github.com/crystal-lang/crystal/pull/15171\n[#15256]: https://github.com/crystal-lang/crystal/pull/15256\n[#15132]: https://github.com/crystal-lang/crystal/pull/15132\n[#15196]: https://github.com/crystal-lang/crystal/pull/15196\n\n### Specs\n\n#### stdlib\n\n- Fix failing specs on FreeBSD ([#15093], thanks @ysbaddaden)\n- Disable specs that break on MinGW-w64 ([#15116], thanks @HertzDevil)\n- _(networking)_ DragonFlyBSD: std specs fixes + pending ([#15152], thanks @ysbaddaden)\n- _(networking)_ Close some dangling sockets in specs ([#15163], thanks @HertzDevil)\n- _(networking)_ Update specs to run with IPv6 support disabled ([#15046], thanks @Blacksmoke16)\n- _(networking)_ Add specs for invalid special characters in `Cookie` ([#15244], thanks @straight-shoota)\n- _(system)_ Improve `System::User` specs on Windows ([#15156], thanks @HertzDevil)\n- _(system)_ Make `cmd.exe` drop `%PROCESSOR_ARCHITECTURE%` in `Process` specs ([#15158], thanks @HertzDevil)\n- _(system)_ Add specs for signal exit ([#15229], thanks @straight-shoota)\n\n[#15093]: https://github.com/crystal-lang/crystal/pull/15093\n[#15116]: https://github.com/crystal-lang/crystal/pull/15116\n[#15152]: https://github.com/crystal-lang/crystal/pull/15152\n[#15163]: https://github.com/crystal-lang/crystal/pull/15163\n[#15046]: https://github.com/crystal-lang/crystal/pull/15046\n[#15244]: https://github.com/crystal-lang/crystal/pull/15244\n[#15156]: https://github.com/crystal-lang/crystal/pull/15156\n[#15158]: https://github.com/crystal-lang/crystal/pull/15158\n[#15229]: https://github.com/crystal-lang/crystal/pull/15229\n\n#### compiler\n\n- _(cli)_ Remove the entire compiler code base from `external_command_spec` ([#15208], thanks @straight-shoota)\n- _(interpreter)_ **[regression]** Fix `Crystal::Loader.default_search_paths` spec for macOS ([#15135], thanks @HertzDevil)\n\n[#15208]: https://github.com/crystal-lang/crystal/pull/15208\n[#15135]: https://github.com/crystal-lang/crystal/pull/15135\n\n#### tools\n\n- Use empty prelude for compiler tools specs ([#15272], thanks @straight-shoota)\n- _(docs-generator)_ Allow skipping compiler tool specs that require Git ([#15125], thanks @HertzDevil)\n\n[#15272]: https://github.com/crystal-lang/crystal/pull/15272\n[#15125]: https://github.com/crystal-lang/crystal/pull/15125\n\n### Infrastructure\n\n- Changelog for 1.15.0 ([#15277], thanks @straight-shoota)\n- Update previous Crystal release 1.14.0 ([#15071], thanks @straight-shoota)\n- Fix remove trailing whitespace from CRYSTAL definition ([#15131], thanks @straight-shoota)\n- Make utilities posix compatible ([#15139], thanks @nanobowers)\n- Update `shell.nix` to `nixpkgs-24.05` and LLVM 18 ([#14651], thanks @straight-shoota)\n- Makefile: Allow custom extensions for exports and spec flags ([#15099], thanks @straight-shoota)\n- Merge changelog entries for fixups with main PR ([#15207], thanks @straight-shoota)\n- Update link to good first issues ([#15250], thanks @BigBoyBarney)\n- Update distribution-scripts ([#15291], thanks @straight-shoota)\n- Bump NOTICE copyright year ([#15318], thanks @straight-shoota)\n- Merge `release/1.14`@1.14.1 ([#15329], thanks @straight-shoota)\n- Update distribution-scripts ([#15332], thanks @straight-shoota)\n- Make `bin/crystal` work on MSYS2 ([#15094], thanks @HertzDevil)\n- Make `Makefile` work on MSYS2 ([#15102], thanks @HertzDevil)\n- Support `.exe` file extension in `Makefile` on MSYS2 ([#15123], thanks @HertzDevil)\n- Support dereferencing symlinks in `make install` ([#15138], thanks @HertzDevil)\n- _(ci)_ Extract `deploy_api_docs` job into its own Workflow ([#15022], thanks @straight-shoota)\n- _(ci)_ Remove pin for ancient nix version ([#15150], thanks @straight-shoota)\n- _(ci)_ Migrate renovate config ([#15151], thanks @renovate)\n- _(ci)_ Update GH Actions ([#15052], thanks @renovate)\n- _(ci)_ Update msys2/setup-msys2 action to v2.26.0 ([#15265], thanks @renovate)\n- _(ci)_ Update shards 0.19.0 ([#15290], thanks @straight-shoota)\n- _(ci)_ **[security]** Restrict GitHub token permissions of CI workflows ([#15087], thanks @HertzDevil)\n- _(ci)_ Do not link against `DbgHelp` for MinGW-w64 CI build ([#15160], thanks @HertzDevil)\n- _(ci)_ Use MSYS2's upstream LLVM version on MinGW-w64 CI ([#15197], thanks @HertzDevil)\n- _(ci)_ Add CI workflow for cross-compiling Crystal on MSYS2 ([#15110], thanks @HertzDevil)\n- _(ci)_ Add MinGW-w64 CI workflow for stdlib and compiler specs ([#15124], thanks @HertzDevil)\n- _(ci)_ Make MinGW-w64 build artifact a full installation ([#15204], thanks @HertzDevil)\n- _(ci)_ Use official Apt repositories for LLVM CI ([#15103], thanks @HertzDevil)\n- _(ci)_ Drop LLVM Apt installer script on WebAssembly CI ([#15109], thanks @HertzDevil)\n- _(ci)_ Run interpreter specs on Windows CI ([#15141], thanks @HertzDevil)\n\n[#15277]: https://github.com/crystal-lang/crystal/pull/15277\n[#15071]: https://github.com/crystal-lang/crystal/pull/15071\n[#15131]: https://github.com/crystal-lang/crystal/pull/15131\n[#15139]: https://github.com/crystal-lang/crystal/pull/15139\n[#14651]: https://github.com/crystal-lang/crystal/pull/14651\n[#15099]: https://github.com/crystal-lang/crystal/pull/15099\n[#15207]: https://github.com/crystal-lang/crystal/pull/15207\n[#15250]: https://github.com/crystal-lang/crystal/pull/15250\n[#15291]: https://github.com/crystal-lang/crystal/pull/15291\n[#15318]: https://github.com/crystal-lang/crystal/pull/15318\n[#15329]: https://github.com/crystal-lang/crystal/pull/15329\n[#15332]: https://github.com/crystal-lang/crystal/pull/15332\n[#15094]: https://github.com/crystal-lang/crystal/pull/15094\n[#15102]: https://github.com/crystal-lang/crystal/pull/15102\n[#15123]: https://github.com/crystal-lang/crystal/pull/15123\n[#15138]: https://github.com/crystal-lang/crystal/pull/15138\n[#15022]: https://github.com/crystal-lang/crystal/pull/15022\n[#15150]: https://github.com/crystal-lang/crystal/pull/15150\n[#15151]: https://github.com/crystal-lang/crystal/pull/15151\n[#15052]: https://github.com/crystal-lang/crystal/pull/15052\n[#15265]: https://github.com/crystal-lang/crystal/pull/15265\n[#15290]: https://github.com/crystal-lang/crystal/pull/15290\n[#15087]: https://github.com/crystal-lang/crystal/pull/15087\n[#15160]: https://github.com/crystal-lang/crystal/pull/15160\n[#15197]: https://github.com/crystal-lang/crystal/pull/15197\n[#15110]: https://github.com/crystal-lang/crystal/pull/15110\n[#15124]: https://github.com/crystal-lang/crystal/pull/15124\n[#15204]: https://github.com/crystal-lang/crystal/pull/15204\n[#15103]: https://github.com/crystal-lang/crystal/pull/15103\n[#15109]: https://github.com/crystal-lang/crystal/pull/15109\n[#15141]: https://github.com/crystal-lang/crystal/pull/15141\n"
  },
  {
    "path": "doc/changelogs/v1.16.md",
    "content": "# Changelog 1.16\n\n## [1.16.3] (2025-05-12)\n\n[1.16.3]: https://github.com/crystal-lang/crystal/releases/1.16.3\n\n### Bugfixes\n\n#### stdlib\n\n- _(runtime)_ Fix `Crystal::EventLoop::LibEvent` and `FiberExecutionContext`  integration ([#15759], backported from [#15743], thanks @ysbaddaden)\n\n[#15759]: https://github.com/crystal-lang/crystal/pull/15759\n[#15743]: https://github.com/crystal-lang/crystal/pull/15743\n\n#### compiler\n\n- _(codegen)_ Add fallback if `__crystal_raise_cast_failed` is missing ([#15769], backported from [#15762], thanks @HertzDevil)\n- _(semantic)_ **[regression]** Remove type binding on `T` for `Pointer(T)#value=` ([#15757], backported from [#15751], thanks @HertzDevil)\n\n[#15769]: https://github.com/crystal-lang/crystal/pull/15769\n[#15762]: https://github.com/crystal-lang/crystal/pull/15762\n[#15757]: https://github.com/crystal-lang/crystal/pull/15757\n[#15751]: https://github.com/crystal-lang/crystal/pull/15751\n\n### Infrastructure\n\n- Changelog for 1.16.3 ([#15758], thanks @straight-shoota)\n\n[#15758]: https://github.com/crystal-lang/crystal/pull/15758\n\n## [1.16.2] (2025-04-29)\n\n[1.16.2]: https://github.com/crystal-lang/crystal/releases/1.16.2\n\n### Bugfixes\n\n#### stdlib\n\n- _(numeric)_ Fix show `unit_separator` in `#humanize_bytes` with empty prefix ([#15717], backported from [#15683], thanks @straight-shoota)\n- _(runtime)_ `CRYSTAL_LOAD_DEBUG_INFO=1` fails with `-Dexecution_context` ([#15715], backported from [#15704], thanks @ysbaddaden)\n- _(runtime)_ Fix `-Dtracing` raises math overflows on fiber sleep ([#15725], backported from [#15722], thanks @ysbaddaden)\n- _(runtime)_ Fix `Fiber::ExecutionContext::Isolated#wait` must suspend fiber ([#15723], backported from [#15720], thanks @ysbaddaden)\n- _(runtime)_ Fix run win32 console reader in bare thread ([#15726], backported from [#15724], thanks @ysbaddaden)\n\n[#15717]: https://github.com/crystal-lang/crystal/pull/15717\n[#15683]: https://github.com/crystal-lang/crystal/pull/15683\n[#15715]: https://github.com/crystal-lang/crystal/pull/15715\n[#15704]: https://github.com/crystal-lang/crystal/pull/15704\n[#15725]: https://github.com/crystal-lang/crystal/pull/15725\n[#15722]: https://github.com/crystal-lang/crystal/pull/15722\n[#15723]: https://github.com/crystal-lang/crystal/pull/15723\n[#15720]: https://github.com/crystal-lang/crystal/pull/15720\n[#15726]: https://github.com/crystal-lang/crystal/pull/15726\n[#15724]: https://github.com/crystal-lang/crystal/pull/15724\n\n#### compiler\n\n- _(semantic)_ Do not add `ReferenceStorage` to `Value`'s subclasses twice ([#15718], backported from [#15706], thanks @HertzDevil)\n\n[#15718]: https://github.com/crystal-lang/crystal/pull/15718\n[#15706]: https://github.com/crystal-lang/crystal/pull/15706\n\n### Refactor\n\n#### compiler\n\n- _(codegen)_ Add `__crystal_raise_cast_failed` for non-interpreted code ([#15712], backported from [#15708], thanks @HertzDevil)\n\n[#15712]: https://github.com/crystal-lang/crystal/pull/15712\n[#15708]: https://github.com/crystal-lang/crystal/pull/15708\n\n### Infrastructure\n\n- Changelog for 1.16.2 ([#15716], thanks @straight-shoota)\n- _(ci)_ Fix package shards on MinGW ([#15719], thanks @straight-shoota)\n- _(ci)_ Only set up Cygwin on Windows CI if truly required ([#15713], backported from [#15661], thanks @HertzDevil)\n\n[#15716]: https://github.com/crystal-lang/crystal/pull/15716\n[#15719]: https://github.com/crystal-lang/crystal/pull/15719\n[#15713]: https://github.com/crystal-lang/crystal/pull/15713\n[#15661]: https://github.com/crystal-lang/crystal/pull/15661\n\n## [1.16.1] (2025-04-16)\n\n[1.16.1]: https://github.com/crystal-lang/crystal/releases/1.16.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(runtime)_ Correctly transfer FD ownership in polling event loop ([#15650], thanks @ysbaddaden)\n- _(runtime)_ Fix error message when `PollDescriptor` can't transfer fd ([#15663], thanks @ysbaddaden)\n- _(runtime)_ Fix `libgc` pkg-config name for version discovery ([#15636], thanks @straight-shoota)\n- _(serialization)_ **[regression]** Fix link `bcrypt` with `libxml2` on Windows ([#15651], thanks @straight-shoota)\n\n[#15650]: https://github.com/crystal-lang/crystal/pull/15650\n[#15663]: https://github.com/crystal-lang/crystal/pull/15663\n[#15636]: https://github.com/crystal-lang/crystal/pull/15636\n[#15651]: https://github.com/crystal-lang/crystal/pull/15651\n\n#### compiler\n\n- _(cli)_ **[regression]** Fix `crystal eval` read from stdin ([#15655], thanks @straight-shoota)\n\n[#15655]: https://github.com/crystal-lang/crystal/pull/15655\n\n### Documentation\n\n#### stdlib\n\n- _(runtime)_ Enable docs for `ExecutionContext` ([#15644], thanks @straight-shoota)\n- _(runtime)_ Fix mark method overrides on `ExecutionContext` as `:nodoc:` ([#15659], thanks @ysbaddaden)\n- _(runtime)_ Update docs for `Fiber::ExecutionContext.default_workers_count` ([#15664], thanks @ysbaddaden)\n- _(runtime)_ Enhance documentation for `ExecutionContext` ([#15665], thanks @straight-shoota)\n\n[#15644]: https://github.com/crystal-lang/crystal/pull/15644\n[#15659]: https://github.com/crystal-lang/crystal/pull/15659\n[#15664]: https://github.com/crystal-lang/crystal/pull/15664\n[#15665]: https://github.com/crystal-lang/crystal/pull/15665\n\n### Infrastructure\n\n- Changelog for 1.16.1 ([#15666], thanks @straight-shoota)\n\n[#15666]: https://github.com/crystal-lang/crystal/pull/15666\n\n## [1.16.0] (2025-04-09)\n\n[1.16.0]: https://github.com/crystal-lang/crystal/releases/1.16.0\n\n### Features\n\n#### lang\n\n- Support `Slice.literal` in the interpreter ([#15531], thanks @HertzDevil)\n- Support `Slice.literal` with inferred element type ([#15529], thanks @HertzDevil)\n- _(macros)_ Error on `TypeNode#instance_vars`, `#has_inner_pointers?` macros in top-level scope ([#15293], thanks @straight-shoota)\n- _(macros)_ Support `sizeof` and `alignof` inside macros for stable types ([#15497], thanks @HertzDevil)\n\n[#15531]: https://github.com/crystal-lang/crystal/pull/15531\n[#15529]: https://github.com/crystal-lang/crystal/pull/15529\n[#15293]: https://github.com/crystal-lang/crystal/pull/15293\n[#15497]: https://github.com/crystal-lang/crystal/pull/15497\n\n#### stdlib\n\n- Fix `Box(Pointer).box` to not allocate pointer storage on the heap ([#15562], thanks @ysbaddaden)\n- _(collection)_ Add `Indexable#find` and `#find!` ([#15552], [#15589], thanks @punteek, @Sija)\n- _(llvm)_ Add `LLVM.version` ([#15354], thanks @straight-shoota)\n- _(llvm)_ Support LLVM 20 ([#15412], [#15418], thanks @HertzDevil, @straight-shoota)\n- _(llvm)_ Add `LLVM.init_native_target` and `LLVM.init_all_targets` ([#15466], thanks @HertzDevil)\n- _(llvm)_ Support `$LLVM_VERSION`, `$LLVM_TARGETS`, and `$LLVM_LDFLAGS` ([#15091], thanks @HertzDevil)\n- _(llvm)_ Add `LLVM::CodeModel::Tiny` ([#15608], thanks @HertzDevil)\n- _(macros)_ Implement `StringLiteral#scan` ([#15398], thanks @homonoidian)\n- _(networking)_ Add `Path` as possible argument type to `UNIXSocket` and `UNIXServer` ([#15260], thanks @BigBoyBarney)\n- _(networking)_ Add `Cookies#==` ([#15463], thanks @straight-shoota)\n- _(runtime)_ Add `EventLoop#wait_readable`, `#wait_writable` methods methods ([#15376], thanks @ysbaddaden)\n- _(runtime)_ Initialize `Fiber` with an explicit stack ([#15409], thanks @ysbaddaden)\n- _(runtime)_ Add fiber queues for execution context schedulers ([#15345], thanks @ysbaddaden)\n- _(runtime)_ RFC 2: Skeleton for ExecutionContext  ([#15350], [#15596], thanks @ysbaddaden)\n- _(runtime)_ RFC 2: Add `Fiber::ExecutionContext::SingleThreaded` scheduler ([#15511], thanks @ysbaddaden)\n- _(runtime)_ RFC 2: Add `Fiber::ExecutionContext::Isolated` ([#15513], thanks @ysbaddaden)\n- _(runtime)_ RFC 2: Add `Fiber::ExecutionContext::Monitor` ([#15599], thanks @ysbaddaden)\n- _(runtime)_ RFC 2: Add `Fiber::ExecutionContext::MultiThreaded` ([#15517], thanks @ysbaddaden)\n- _(serialization)_ Add `Union.from_json_object_key?` ([#15411], thanks @straight-shoota)\n- _(system)_ Add `Process::Status#description` ([#15468], thanks @straight-shoota)\n- _(text)_ Add `IO` overloads to `Char#upcase`, `#downcase`, `#titlecase` ([#15508], thanks @HertzDevil)\n- _(text)_ **[breaking]** New algorithm for `File.match?` ([#15607], thanks @straight-shoota)\n\n[#15562]: https://github.com/crystal-lang/crystal/pull/15562\n[#15552]: https://github.com/crystal-lang/crystal/pull/15552\n[#15589]: https://github.com/crystal-lang/crystal/pull/15589\n[#15354]: https://github.com/crystal-lang/crystal/pull/15354\n[#15412]: https://github.com/crystal-lang/crystal/pull/15412\n[#15418]: https://github.com/crystal-lang/crystal/pull/15418\n[#15466]: https://github.com/crystal-lang/crystal/pull/15466\n[#15091]: https://github.com/crystal-lang/crystal/pull/15091\n[#15608]: https://github.com/crystal-lang/crystal/pull/15608\n[#15398]: https://github.com/crystal-lang/crystal/pull/15398\n[#15260]: https://github.com/crystal-lang/crystal/pull/15260\n[#15463]: https://github.com/crystal-lang/crystal/pull/15463\n[#15376]: https://github.com/crystal-lang/crystal/pull/15376\n[#15409]: https://github.com/crystal-lang/crystal/pull/15409\n[#15345]: https://github.com/crystal-lang/crystal/pull/15345\n[#15350]: https://github.com/crystal-lang/crystal/pull/15350\n[#15596]: https://github.com/crystal-lang/crystal/pull/15596\n[#15511]: https://github.com/crystal-lang/crystal/pull/15511\n[#15513]: https://github.com/crystal-lang/crystal/pull/15513\n[#15599]: https://github.com/crystal-lang/crystal/pull/15599\n[#15517]: https://github.com/crystal-lang/crystal/pull/15517\n[#15411]: https://github.com/crystal-lang/crystal/pull/15411\n[#15468]: https://github.com/crystal-lang/crystal/pull/15468\n[#15508]: https://github.com/crystal-lang/crystal/pull/15508\n[#15607]: https://github.com/crystal-lang/crystal/pull/15607\n\n#### compiler\n\n- _(cli)_ Support `--output` long option in `crystal build` ([#15519], thanks @HertzDevil)\n- _(cli)_ Support directory name in `--output` CLI option ([#15471], thanks @straight-shoota)\n- _(cli)_ **[breaking]** Add compiler path to `$PATH` and `$CRYSTAL_EXEC_PATH` for subcommands ([#15186], thanks @straight-shoota)\n- _(cli)_ Respect `--mcpu=help` in the compiler ([#15595], thanks @HertzDevil)\n- _(cli)_ Add `CRYSTAL_EXEC_PATH` to `crystal env` [followup #15186] ([#15632], thanks @straight-shoota)\n- _(codegen)_ Set linkage of `__crystal_*` funs to internal ([#15439], thanks @ysbaddaden)\n- _(codegen)_ Add function name to `CRYSTAL_DEBUG_CODEGEN` log helper ([#15506], thanks @HertzDevil)\n- _(parser)_ Handle properly stringifying multiline macro expressions ([#15305], thanks @Blacksmoke16)\n- _(parser)_ **[breaking]** Check that def, macro, and block parameters don't end with `?` or `!` ([#12197], thanks @potomak)\n\n[#15519]: https://github.com/crystal-lang/crystal/pull/15519\n[#15471]: https://github.com/crystal-lang/crystal/pull/15471\n[#15186]: https://github.com/crystal-lang/crystal/pull/15186\n[#15595]: https://github.com/crystal-lang/crystal/pull/15595\n[#15632]: https://github.com/crystal-lang/crystal/pull/15632\n[#15439]: https://github.com/crystal-lang/crystal/pull/15439\n[#15506]: https://github.com/crystal-lang/crystal/pull/15506\n[#15305]: https://github.com/crystal-lang/crystal/pull/15305\n[#12197]: https://github.com/crystal-lang/crystal/pull/12197\n\n#### tools\n\n- _(docs-generator)_ Add docs to enum member helper methods ([#15379], thanks @nobodywasishere)\n- _(docs-generator)_ Add `:showdoc:` directive for `private` and `protected` objects (RFC #0011) ([#15337], thanks @nobodywasishere)\n- _(docs-generator)_ Add documentation support for `lib`, `fun`, `union`, `cstruct`, `external`, and `type` (RFC #0011) ([#15447], thanks @nobodywasishere)\n\n[#15379]: https://github.com/crystal-lang/crystal/pull/15379\n[#15337]: https://github.com/crystal-lang/crystal/pull/15337\n[#15447]: https://github.com/crystal-lang/crystal/pull/15447\n\n### Bugfixes\n\n#### stdlib\n\n- _(collection)_ Fix hash `@indices` can grow larger than `Int32::MAX` bytes ([#15347], thanks @ysbaddaden)\n- _(collection)_ Fix `Tuple#to_a(&)` for arbitrary block output type ([#15431], thanks @straight-shoota)\n- _(collection)_ Fix `Range#size` for unsigned edge cases ([#14978], thanks @straight-shoota)\n- _(collection)_ **[breaking]** Fix the return type of `Enumerable#sum`, `#product` for union elements ([#15314], thanks @rvprasad)\n- _(concurrency)_ Fix `Reference#exec_recursive`, `#exec_recursive_clone` to be fiber aware ([#15361], thanks @ysbaddaden)\n- _(concurrency)_ RFC 2: MT safe fiber context switch on ARM ([#15582], thanks @ysbaddaden)\n- _(crypto)_ Fix argument type for `EVP_CIPHER_get_flags` ([#15392], thanks @miry)\n- _(files)_ Never remove UNC share name in `Path#dirname` ([#15583], thanks @HertzDevil)\n- _(files)_ Fix `File.exists?` for special devices on Windows ([#15587], thanks @HertzDevil)\n- _(llvm)_ Fix LLVM version detection for `-rc1` ([#15410], thanks @HertzDevil)\n- _(networking)_ **[breaking]** Fix parsing HTTP resource string that looks like absolute URL ([#15499], thanks @straight-shoota)\n- _(runtime)_ Fix `pkg_config` name for `libgc` bindings on FreeBSD ([#15532], thanks @straight-shoota)\n- _(runtime)_ RFC 2: MT safe fiber context switch on AArch64 ([#15581], thanks @ysbaddaden)\n- _(runtime)_ Add thread safety to `at_exit` ([#15598], thanks @ysbaddaden)\n- _(runtime)_ Remove top-level calls to `LibGC.has_method?` for backwards compat ([#15635], thanks @straight-shoota)\n- _(serialization)_ Fix `Union.from_yaml` to prioritize `String` for quoted scalar ([#15405], thanks @straight-shoota)\n- _(system)_ signal handler mustn't depend on the event loop ([#15325], thanks @ysbaddaden)\n- _(system)_ Corrects Windows lib lookup in case-sensitive OSes ([#15362], thanks @luislavena)\n- _(system)_ Fix permissions application in `File.copy` ([#15520], thanks @straight-shoota)\n- _(system)_ **[security]** Strip periods, spaces for batch file filtering on Windows ([#15573], thanks @GeopJr)\n- _(system)_ Extend Windows `Process` completion key's lifetime ([#15597], thanks @HertzDevil)\n\n[#15347]: https://github.com/crystal-lang/crystal/pull/15347\n[#15431]: https://github.com/crystal-lang/crystal/pull/15431\n[#14978]: https://github.com/crystal-lang/crystal/pull/14978\n[#15314]: https://github.com/crystal-lang/crystal/pull/15314\n[#15361]: https://github.com/crystal-lang/crystal/pull/15361\n[#15582]: https://github.com/crystal-lang/crystal/pull/15582\n[#15392]: https://github.com/crystal-lang/crystal/pull/15392\n[#15583]: https://github.com/crystal-lang/crystal/pull/15583\n[#15587]: https://github.com/crystal-lang/crystal/pull/15587\n[#15410]: https://github.com/crystal-lang/crystal/pull/15410\n[#15499]: https://github.com/crystal-lang/crystal/pull/15499\n[#15532]: https://github.com/crystal-lang/crystal/pull/15532\n[#15581]: https://github.com/crystal-lang/crystal/pull/15581\n[#15598]: https://github.com/crystal-lang/crystal/pull/15598\n[#15635]: https://github.com/crystal-lang/crystal/pull/15635\n[#15405]: https://github.com/crystal-lang/crystal/pull/15405\n[#15325]: https://github.com/crystal-lang/crystal/pull/15325\n[#15362]: https://github.com/crystal-lang/crystal/pull/15362\n[#15520]: https://github.com/crystal-lang/crystal/pull/15520\n[#15573]: https://github.com/crystal-lang/crystal/pull/15573\n[#15597]: https://github.com/crystal-lang/crystal/pull/15597\n\n#### compiler\n\n- _(cli)_ Fix query runtime version of LLVM ([#15355], thanks @straight-shoota)\n- _(cli)_ Fix handling of double dashes `--` in crystal `eval` command ([#15477], thanks @kojix2)\n- _(codegen)_ don't set external linkage when `@[NoInline]` is specified ([#15424], thanks @ysbaddaden)\n- _(codegen)_ Allow multiple redefinitions of the same top-level fun ([#15553], thanks @HertzDevil)\n- _(codegen)_ Respect `$MACOSX_DEPLOYMENT_TARGET` on macOS hosts ([#15603], thanks @HertzDevil)\n- _(interpreter)_ Fix `pkg_config` name for `libffi` bindings ([#15533], thanks @straight-shoota)\n- _(parser)_ Lexer: Fix location of token after line continuation ([#15454], thanks @FnControlOption)\n- _(parser)_ Add locations to `When` nodes ([#15481], thanks @Sija)\n- _(parser)_ Fix end location of call with block arg and no parentheses ([#15502], thanks @FnControlOption)\n- _(parser)_ Fix location of `MacroExpression` nodes ([#15524], thanks @Sija)\n- _(parser)_ Reject invalid operator names for implicit object calls ([#15526], thanks @nobodywasishere)\n\n[#15355]: https://github.com/crystal-lang/crystal/pull/15355\n[#15477]: https://github.com/crystal-lang/crystal/pull/15477\n[#15424]: https://github.com/crystal-lang/crystal/pull/15424\n[#15553]: https://github.com/crystal-lang/crystal/pull/15553\n[#15603]: https://github.com/crystal-lang/crystal/pull/15603\n[#15533]: https://github.com/crystal-lang/crystal/pull/15533\n[#15454]: https://github.com/crystal-lang/crystal/pull/15454\n[#15481]: https://github.com/crystal-lang/crystal/pull/15481\n[#15502]: https://github.com/crystal-lang/crystal/pull/15502\n[#15524]: https://github.com/crystal-lang/crystal/pull/15524\n[#15526]: https://github.com/crystal-lang/crystal/pull/15526\n\n#### tools\n\n- _(formatter)_ Add uninitialized variables to formatter variable scopes ([#15578], thanks @HertzDevil)\n\n[#15578]: https://github.com/crystal-lang/crystal/pull/15578\n\n### Chores\n\n#### stdlib\n\n- _(llvm)_ **[breaking]** Remove the `LibLLVM::IS_*` constants ([#15464], thanks @HertzDevil)\n\n[#15464]: https://github.com/crystal-lang/crystal/pull/15464\n\n#### compiler\n\n- _(interpreter:repl)_ Update REPLy version ([#15328], thanks @oprypin)\n\n[#15328]: https://github.com/crystal-lang/crystal/pull/15328\n\n### Performance\n\n#### stdlib\n\n- _(files)_ Optimize `Path#drive`, `#root`, and `#anchor` ([#15584], thanks @HertzDevil)\n- _(files)_ Optimize `Path#relative_to?` ([#15594], thanks @HertzDevil)\n- _(runtime)_ Shrink `Crystal::System.print_error`'s output size ([#15490], thanks @HertzDevil)\n\n[#15584]: https://github.com/crystal-lang/crystal/pull/15584\n[#15594]: https://github.com/crystal-lang/crystal/pull/15594\n[#15490]: https://github.com/crystal-lang/crystal/pull/15490\n\n#### compiler\n\n- _(codegen)_ Replace inline type IDs with global constants in LLVM IR ([#15485], [#15505], thanks @HertzDevil)\n- _(codegen)_ Do not load `Path` call receiver if known to be pure load ([#15488], thanks @HertzDevil)\n- _(codegen)_ Only refer to LLVM symbol table in calls to `Symbol#to_s` ([#15486], thanks @HertzDevil)\n- _(debugger)_ Read all DWARF abbreviations tables in a single pass ([#15515], thanks @HertzDevil)\n- _(debugger)_ Use binary search to search DWARF line numbers ([#15539], thanks @HertzDevil)\n- _(debugger)_ Remove `op_index` and `end_sequence` from `Crystal::DWARF::LineNumbers::Row` ([#15538], thanks @HertzDevil)\n\n[#15485]: https://github.com/crystal-lang/crystal/pull/15485\n[#15505]: https://github.com/crystal-lang/crystal/pull/15505\n[#15488]: https://github.com/crystal-lang/crystal/pull/15488\n[#15486]: https://github.com/crystal-lang/crystal/pull/15486\n[#15515]: https://github.com/crystal-lang/crystal/pull/15515\n[#15539]: https://github.com/crystal-lang/crystal/pull/15539\n[#15538]: https://github.com/crystal-lang/crystal/pull/15538\n\n### Refactor\n\n#### stdlib\n\n- Use splat parameter to put `Tuple`s in large `Array` constants ([#15495], thanks @HertzDevil)\n- _(collection)_ Simplify `Enumerable#to_a` ([#15432], thanks @straight-shoota)\n- _(concurrency)_ Use `Crystal::PointerLinkedList` instead of `Deque` in `Mutex` ([#15330], thanks @ysbaddaden)\n- _(concurrency)_ Add fiber safety to crystal/once ([#15370], thanks @ysbaddaden)\n- _(concurrency)_ ARM: reduce duplication in fiber context switch ([#15585], thanks @ysbaddaden)\n- _(files)_ Add nanosecond precision to `File.utime` on Unix ([#15335], thanks @ysbaddaden)\n- _(llvm)_ **[deprecation]** Make `LLVM::ABI` internal ([#15559], thanks @HertzDevil)\n- _(llvm)_ Only initialize `LLVM::Attribute`'s class variables on demand ([#15534], thanks @HertzDevil)\n- _(macros)_ Generate Object getter/property macros to remove duplications ([#15386], thanks @ysbaddaden)\n- _(networking)_ Refactor extract `HTTP::Cookies` to its own file ([#15500], [#15618], thanks @straight-shoota)\n- _(runtime)_ Add `Crystal.print_buffered(io)` and `Crystal.print_error_buffered` ([#15343], thanks @ysbaddaden)\n- _(runtime)_ Explicit init of Thread and Fiber class variables ([#15369], thanks @ysbaddaden)\n- _(runtime)_ Add `Crystal.once_init` replacing `__crystal_once_init` ([#15371], thanks @ysbaddaden)\n- _(runtime)_ Move shadow space reservation to x86_64 makecontext ([#15434], thanks @ysbaddaden)\n- _(runtime)_ Add `Crystal::EventLoop#sleep(duration)` method ([#15564], thanks @ysbaddaden)\n- _(system)_ Extract `File.match?` to separate source file ([#15574], thanks @straight-shoota)\n\n[#15495]: https://github.com/crystal-lang/crystal/pull/15495\n[#15432]: https://github.com/crystal-lang/crystal/pull/15432\n[#15330]: https://github.com/crystal-lang/crystal/pull/15330\n[#15370]: https://github.com/crystal-lang/crystal/pull/15370\n[#15585]: https://github.com/crystal-lang/crystal/pull/15585\n[#15335]: https://github.com/crystal-lang/crystal/pull/15335\n[#15559]: https://github.com/crystal-lang/crystal/pull/15559\n[#15534]: https://github.com/crystal-lang/crystal/pull/15534\n[#15386]: https://github.com/crystal-lang/crystal/pull/15386\n[#15500]: https://github.com/crystal-lang/crystal/pull/15500\n[#15618]: https://github.com/crystal-lang/crystal/pull/15618\n[#15343]: https://github.com/crystal-lang/crystal/pull/15343\n[#15369]: https://github.com/crystal-lang/crystal/pull/15369\n[#15371]: https://github.com/crystal-lang/crystal/pull/15371\n[#15434]: https://github.com/crystal-lang/crystal/pull/15434\n[#15564]: https://github.com/crystal-lang/crystal/pull/15564\n[#15574]: https://github.com/crystal-lang/crystal/pull/15574\n\n#### compiler\n\n- _(codegen)_ Rework initialization of constants & class variables ([#15333], thanks @ysbaddaden)\n- _(codegen)_ Remove unnecessary calls to `Crystal::CodeGenVisitor#union_type_and_value_pointer` ([#15491], thanks @HertzDevil)\n- _(parser)_ Simplify `Call.new` convenience overloads ([#15427], thanks @straight-shoota)\n- _(parser)_ Add `Call.new` constructor overload without `obj` parameter ([#15441], thanks @straight-shoota)\n- _(semantic)_ Extract `regex_value` helper for macro methods ([#15435], thanks @straight-shoota)\n\n[#15333]: https://github.com/crystal-lang/crystal/pull/15333\n[#15491]: https://github.com/crystal-lang/crystal/pull/15491\n[#15427]: https://github.com/crystal-lang/crystal/pull/15427\n[#15441]: https://github.com/crystal-lang/crystal/pull/15441\n[#15435]: https://github.com/crystal-lang/crystal/pull/15435\n\n### Documentation\n\n#### lang\n\n- Document `alignof` and `instance_alignof` ([#15576], thanks @HertzDevil)\n- _(macros)_ Document macro `sizeof` and `alignof` [followup #15497] ([#15575], thanks @HertzDevil)\n\n[#15576]: https://github.com/crystal-lang/crystal/pull/15576\n[#15575]: https://github.com/crystal-lang/crystal/pull/15575\n\n#### stdlib\n\n- Fix `Colorize::ObjectExtensions#colorize(r, g, b)` comment ([#15521], thanks @Sija)\n- Rework docs for `getter`, `setter` and `property` macros ([#15428], thanks @ysbaddaden)\n- Add missing files for API docs ([#15622], thanks @straight-shoota)\n- _(runtime)_ Document `::debugger` ([#15579], thanks @HertzDevil)\n\n[#15521]: https://github.com/crystal-lang/crystal/pull/15521\n[#15428]: https://github.com/crystal-lang/crystal/pull/15428\n[#15622]: https://github.com/crystal-lang/crystal/pull/15622\n[#15579]: https://github.com/crystal-lang/crystal/pull/15579\n\n#### compiler\n\n- _(cli)_ Convert `crystal.1` manpage to asciidoc ([#15478], thanks @straight-shoota)\n- _(cli)_ Split combined manpage into individual ones for each command ([#15509], thanks @straight-shoota)\n- _(cli)_ Document environment variable `CRYSTAL_EXEC_PATH` [followup #15186] ([#15631], thanks @straight-shoota)\n\n[#15478]: https://github.com/crystal-lang/crystal/pull/15478\n[#15509]: https://github.com/crystal-lang/crystal/pull/15509\n[#15631]: https://github.com/crystal-lang/crystal/pull/15631\n\n#### other\n\n- Add sample fibonacci numbers ([#15550], thanks @666hwll)\n\n[#15550]: https://github.com/crystal-lang/crystal/pull/15550\n\n### Specs\n\n#### stdlib\n\n- Fix invalid returns in class getter's lazy evaluation blocks ([#15364], thanks @ysbaddaden)\n- _(specs)_ Add specs for `File.match?` ([#15348], thanks @straight-shoota)\n- _(text)_ Add specs for `File.match?` from fast-glob ([#15604], thanks @straight-shoota)\n- _(text)_ Add specs for `File.match?` with multibyte characters ([#15601], thanks @straight-shoota)\n\n[#15364]: https://github.com/crystal-lang/crystal/pull/15364\n[#15348]: https://github.com/crystal-lang/crystal/pull/15348\n[#15604]: https://github.com/crystal-lang/crystal/pull/15604\n[#15601]: https://github.com/crystal-lang/crystal/pull/15601\n\n#### compiler\n\n- _(parser)_ Add specs for block association in nested calls ([#15461], thanks @straight-shoota)\n\n[#15461]: https://github.com/crystal-lang/crystal/pull/15461\n\n### Infrastructure\n\n- Changelog for 1.16.0 ([#15602], thanks @straight-shoota)\n- Update previous Crystal release 1.15.0 ([#15339], thanks @straight-shoota)\n- Make: Fix `make uninstall` to remove fish completion ([#15367], thanks @straight-shoota)\n- Merge `release/1.15`@1.15.1 ([#15422], thanks @straight-shoota)\n- Fix: Remove reverted PR from changelog for 1.15.1 ([#15415], thanks @straight-shoota)\n- Update previous release: 1.15.1 ([#15417], thanks @straight-shoota)\n- Add backports to changelog generator ([#15413], thanks @straight-shoota)\n- Makefile: Expand `DESTDIR` outside of prefixed dir variables ([#15444], thanks @straight-shoota)\n- Add git mailmap ([#15396], thanks @straight-shoota)\n- Rename `find-llvm-config` to `find-llvm-config.sh` ([#15448], thanks @straight-shoota)\n- Makefile: Remove `crystal` from `DATADIR` ([#15467], thanks @straight-shoota)\n- Add `scripts/update-shards.sh` ([#15462], thanks @straight-shoota)\n- Enhance `.gitignore` ([#15469], thanks @straight-shoota)\n- Introduce shellcheck to lint shell scripts ([#15169], thanks @straight-shoota)\n- Trim `CHANGELOG.md` ([#15627], thanks @straight-shoota)\n- Update `scripts/generate_llvm_version_info.cr` ([#15465], thanks @HertzDevil)\n- _(ci)_ Fix shards packaging for mingw-w64 ([#15451], thanks @straight-shoota)\n- _(ci)_ Add workflow for backporting PRs to release branches ([#15372], [#15378], thanks @straight-shoota)\n- _(ci)_ Update cygwin/cygwin-install-action action to v5 ([#15346], thanks @renovate)\n- _(ci)_ Extract forward compatibility checks and run on nightly schedule ([#15437], thanks @straight-shoota)\n- _(ci)_ Use MSYS2 Crystal package for `mingw-w64` workflow ([#15453], [#15476], thanks @HertzDevil, @straight-shoota)\n- _(ci)_ Filter runs of LLVM Test workflow ([#15458], thanks @straight-shoota)\n- _(ci)_ Filter runs of regex engine workflow ([#15460], thanks @straight-shoota)\n- _(ci)_ Filter runs of OpenSSL Test workflow ([#15459], thanks @straight-shoota)\n- _(ci)_ Filter runs of Smoke Test workflow ([#15457], thanks @straight-shoota)\n- _(ci)_ Introduce actionlint to lint GitHub Actions workflows ([#15449], thanks @straight-shoota)\n- _(ci)_ Fix MinGW-W64 workflow to run compiler tests with fresh compiler ([#15522], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#15525], thanks @renovate)\n- _(ci)_ Update GH Actions ([#15551], thanks @renovate)\n- _(ci)_ Update library versions for MSVC CI ([#15554], thanks @HertzDevil)\n- _(ci)_ Increase memory for `aarch64-*-test-compiler` runners to 16GB ([#15572], thanks @straight-shoota)\n- _(ci)_ Add AArch64 Linux workflow using GitHub's runner ([#15600], thanks @HertzDevil)\n\n[#15602]: https://github.com/crystal-lang/crystal/pull/15602\n[#15339]: https://github.com/crystal-lang/crystal/pull/15339\n[#15367]: https://github.com/crystal-lang/crystal/pull/15367\n[#15422]: https://github.com/crystal-lang/crystal/pull/15422\n[#15415]: https://github.com/crystal-lang/crystal/pull/15415\n[#15417]: https://github.com/crystal-lang/crystal/pull/15417\n[#15413]: https://github.com/crystal-lang/crystal/pull/15413\n[#15444]: https://github.com/crystal-lang/crystal/pull/15444\n[#15396]: https://github.com/crystal-lang/crystal/pull/15396\n[#15448]: https://github.com/crystal-lang/crystal/pull/15448\n[#15467]: https://github.com/crystal-lang/crystal/pull/15467\n[#15462]: https://github.com/crystal-lang/crystal/pull/15462\n[#15469]: https://github.com/crystal-lang/crystal/pull/15469\n[#15169]: https://github.com/crystal-lang/crystal/pull/15169\n[#15627]: https://github.com/crystal-lang/crystal/pull/15627\n[#15465]: https://github.com/crystal-lang/crystal/pull/15465\n[#15451]: https://github.com/crystal-lang/crystal/pull/15451\n[#15372]: https://github.com/crystal-lang/crystal/pull/15372\n[#15378]: https://github.com/crystal-lang/crystal/pull/15378\n[#15346]: https://github.com/crystal-lang/crystal/pull/15346\n[#15437]: https://github.com/crystal-lang/crystal/pull/15437\n[#15453]: https://github.com/crystal-lang/crystal/pull/15453\n[#15476]: https://github.com/crystal-lang/crystal/pull/15476\n[#15458]: https://github.com/crystal-lang/crystal/pull/15458\n[#15460]: https://github.com/crystal-lang/crystal/pull/15460\n[#15459]: https://github.com/crystal-lang/crystal/pull/15459\n[#15457]: https://github.com/crystal-lang/crystal/pull/15457\n[#15449]: https://github.com/crystal-lang/crystal/pull/15449\n[#15522]: https://github.com/crystal-lang/crystal/pull/15522\n[#15525]: https://github.com/crystal-lang/crystal/pull/15525\n[#15551]: https://github.com/crystal-lang/crystal/pull/15551\n[#15554]: https://github.com/crystal-lang/crystal/pull/15554\n[#15572]: https://github.com/crystal-lang/crystal/pull/15572\n[#15600]: https://github.com/crystal-lang/crystal/pull/15600\n"
  },
  {
    "path": "doc/changelogs/v1.17.md",
    "content": "# Changelog 1.17\n\n## [1.17.1] (2025-07-22)\n\n[1.17.1]: https://github.com/crystal-lang/crystal/releases/1.17.1\n\n### Bugfixes\n\n#### tools\n\n- _(docs-generator)_ **[regression]** Revert \"Handle doc locations when file is in parent directory\" ([#15996], thanks @Blacksmoke16)\n\n[#15996]: https://github.com/crystal-lang/crystal/pull/15996\n\n### Infrastructure\n\n- Changelog for 1.17.1 ([#16009], thanks @straight-shoota)\n\n[#16009]: https://github.com/crystal-lang/crystal/pull/16009\n\n## [1.17.0] (2025-07-16)\n\n[1.17.0]: https://github.com/crystal-lang/crystal/releases/1.17.0\n\n### Breaking changes\n\n#### stdlib\n\n- Make `Colorize.on_tty_only!` the default behavior ([#15881], thanks @HertzDevil)\n- _(concurrency)_ Rename execution contexts + improve their docs ([#15936], thanks @ysbaddaden)\n- _(files)_ Refactor `IO.pipe` blocking mode ([#15823], thanks @ysbaddaden)\n- _(files)_ Fix: set `IO::Stapled.pipe` blocking args to nil, as per `IO.pipe` ([#15925], thanks @ysbaddaden)\n- _(networking)_ Refactor `Socket` blocking mode ([#15804], thanks @ysbaddaden)\n- _(runtime)_ Let the event loop decide the blocking mode of `File` ([#15930], thanks @ysbaddaden)\n- _(serialization)_ Fix: libxml manual memory management ([#15906], [#15933], thanks @ysbaddaden)\n- _(system)_ Turn `SystemError.from_errno` into a macro ([#15874], thanks @straight-shoota)\n\n[#15881]: https://github.com/crystal-lang/crystal/pull/15881\n[#15936]: https://github.com/crystal-lang/crystal/pull/15936\n[#15823]: https://github.com/crystal-lang/crystal/pull/15823\n[#15925]: https://github.com/crystal-lang/crystal/pull/15925\n[#15804]: https://github.com/crystal-lang/crystal/pull/15804\n[#15930]: https://github.com/crystal-lang/crystal/pull/15930\n[#15906]: https://github.com/crystal-lang/crystal/pull/15906\n[#15933]: https://github.com/crystal-lang/crystal/pull/15933\n[#15874]: https://github.com/crystal-lang/crystal/pull/15874\n\n### Features\n\n#### lang\n\n- _(macros)_ Handle properly stringifying single line blocks ([#15568], thanks @Blacksmoke16)\n- _(macros)_ Handle properly stringifying multiline named tuple literals ([#15566], thanks @Blacksmoke16)\n- _(macros)_ Handle properly stringifying multiline calls ([#15691], thanks @Blacksmoke16)\n- _(macros)_ Handle significant whitespace before a blocks body ([#15692], thanks @Blacksmoke16)\n- _(macros)_ Support `{% if ...; end; ... %}` macro expressions ([#15917], thanks @HertzDevil)\n- _(macros)_ Support `{% elsif %}` when stringifying `MacroIf` nodes ([#15928], thanks @HertzDevil)\n\n[#15568]: https://github.com/crystal-lang/crystal/pull/15568\n[#15566]: https://github.com/crystal-lang/crystal/pull/15566\n[#15691]: https://github.com/crystal-lang/crystal/pull/15691\n[#15692]: https://github.com/crystal-lang/crystal/pull/15692\n[#15917]: https://github.com/crystal-lang/crystal/pull/15917\n[#15928]: https://github.com/crystal-lang/crystal/pull/15928\n\n#### stdlib\n\n- Add `Colorize.default_enabled?` ([#15912], thanks @HertzDevil)\n- **[experimental]** Add `Struct.pre_initialize` ([#15896], thanks @HertzDevil)\n- _(files)_ Support Windows local device paths in `Path` ([#15590], thanks @HertzDevil)\n- _(llvm)_ Support LLVM 21.0 (development branch) ([#15771], thanks @HertzDevil)\n- _(networking)_ Extract `WebSocket#do_ping`, `#do_close` helper methods for overrides ([#15545], thanks @luislavena)\n- _(networking)_ Add support for IPv6 scoped addresses (RFC4007) ([#15263], thanks @foxxx0)\n- _(networking)_ Expose `HTTP::Request#uri` ([#15816], thanks @syeopite)\n- _(numeric)_ Add `BigRational#to_i` ([#15809], thanks @HertzDevil)\n- _(numeric)_ Add `Float::Primitive#sign_bit` ([#15830], thanks @HertzDevil)\n- _(runtime)_ Add explicit `Crystal::EventLoop#reopened(FileDescriptor)` hook ([#15640], thanks @ysbaddaden)\n- _(runtime)_ Add `Crystal::EventLoop::FileDescriptor#open` ([#15750], thanks @ysbaddaden)\n- _(runtime)_ Add `Thread::Local(T)` ([#15616], thanks @ysbaddaden)\n- _(serialization)_ Add `XML.libxml2_version` ([#15623], thanks @straight-shoota)\n- _(serialization)_ Add `YAML::Builder#start_document(*, implicit_start_indicator)` ([#15835], thanks @straight-shoota)\n- _(serialization)_ Support pretty printing of `XML` types ([#15833], thanks @HertzDevil)\n- _(serialization)_ Expose error message from libyaml on emitter errors ([#15841], thanks @straight-shoota)\n- _(serialization)_ Add `Path.from_json_object_key` ([#15877], thanks @jneen)\n- _(serialization)_ Add `Time::Location#{to,from}_{json,yaml}` ([#15939], thanks @Sija)\n- _(serialization)_ Extract `XML::Document` from `XML::Node` ([#15920], thanks @ysbaddaden)\n- _(text)_ **[experimental]** Add `Crystal::System.wstr_literal` on Windows ([#15747], thanks @HertzDevil)\n- _(text)_ Add `String#ensure_suffix` and `String#ensure_prefix` ([#15782], thanks @MatheusRich)\n- _(text)_ Add `truncate_at_null` parameter to `String.new(Bytes)` and `.from_utf16` ([#15887], thanks @HertzDevil)\n- _(time)_ Add `Time.month_week_date` ([#15620], thanks @HertzDevil)\n- _(time)_ Improve the TZif database file parser ([#15825], thanks @HertzDevil)\n- _(time)_ Support POSIX TZ environment variable strings ([#15792], thanks @HertzDevil)\n- _(time)_ Improve whitespace handling in `Time::Format` ([#15890], thanks @HertzDevil)\n- _(time)_ Support Windows system time zone transitions in all years ([#15891], thanks @HertzDevil)\n- _(time)_ Support POSIX TZ strings in TZif databases ([#15863], thanks @HertzDevil)\n\n[#15912]: https://github.com/crystal-lang/crystal/pull/15912\n[#15896]: https://github.com/crystal-lang/crystal/pull/15896\n[#15590]: https://github.com/crystal-lang/crystal/pull/15590\n[#15771]: https://github.com/crystal-lang/crystal/pull/15771\n[#15545]: https://github.com/crystal-lang/crystal/pull/15545\n[#15263]: https://github.com/crystal-lang/crystal/pull/15263\n[#15816]: https://github.com/crystal-lang/crystal/pull/15816\n[#15809]: https://github.com/crystal-lang/crystal/pull/15809\n[#15830]: https://github.com/crystal-lang/crystal/pull/15830\n[#15640]: https://github.com/crystal-lang/crystal/pull/15640\n[#15750]: https://github.com/crystal-lang/crystal/pull/15750\n[#15616]: https://github.com/crystal-lang/crystal/pull/15616\n[#15623]: https://github.com/crystal-lang/crystal/pull/15623\n[#15835]: https://github.com/crystal-lang/crystal/pull/15835\n[#15833]: https://github.com/crystal-lang/crystal/pull/15833\n[#15841]: https://github.com/crystal-lang/crystal/pull/15841\n[#15877]: https://github.com/crystal-lang/crystal/pull/15877\n[#15939]: https://github.com/crystal-lang/crystal/pull/15939\n[#15920]: https://github.com/crystal-lang/crystal/pull/15920\n[#15747]: https://github.com/crystal-lang/crystal/pull/15747\n[#15782]: https://github.com/crystal-lang/crystal/pull/15782\n[#15887]: https://github.com/crystal-lang/crystal/pull/15887\n[#15620]: https://github.com/crystal-lang/crystal/pull/15620\n[#15825]: https://github.com/crystal-lang/crystal/pull/15825\n[#15792]: https://github.com/crystal-lang/crystal/pull/15792\n[#15890]: https://github.com/crystal-lang/crystal/pull/15890\n[#15891]: https://github.com/crystal-lang/crystal/pull/15891\n[#15863]: https://github.com/crystal-lang/crystal/pull/15863\n\n#### compiler\n\n- _(cli)_ Support `--x86-asm-syntax` for emitting Intel style assembly ([#15612], thanks @HertzDevil)\n- _(debugger)_ Support debug info of 128-bit enum members ([#15770], thanks @HertzDevil)\n- _(parser)_ More robust trailing expressions newline implementation ([#15614], thanks @Blacksmoke16)\n- _(parser)_ Handle properly stringifying multiline (boolean) expressions ([#15709], thanks @Blacksmoke16)\n- _(parser)_ Stringify `MacroIf` `unless` nodes properly ([#15919], thanks @HertzDevil)\n- _(parser)_ Support `elsif` when stringifying `If` nodes ([#15918], thanks @HertzDevil)\n- _(parser)_ Add location info to `MacroVar` nodes ([#15947], thanks @Blacksmoke16)\n- _(semantic)_ Improve error message for `pointerof` ([#15876], thanks @straight-shoota)\n\n[#15612]: https://github.com/crystal-lang/crystal/pull/15612\n[#15770]: https://github.com/crystal-lang/crystal/pull/15770\n[#15614]: https://github.com/crystal-lang/crystal/pull/15614\n[#15709]: https://github.com/crystal-lang/crystal/pull/15709\n[#15919]: https://github.com/crystal-lang/crystal/pull/15919\n[#15918]: https://github.com/crystal-lang/crystal/pull/15918\n[#15947]: https://github.com/crystal-lang/crystal/pull/15947\n[#15876]: https://github.com/crystal-lang/crystal/pull/15876\n\n#### tools\n\n- Macro code coverage tool ([#15738], thanks @Blacksmoke16)\n- _(docs-generator)_ Limit paragraph `max-width` in API docs ([#15672], thanks @straight-shoota)\n\n[#15738]: https://github.com/crystal-lang/crystal/pull/15738\n[#15672]: https://github.com/crystal-lang/crystal/pull/15672\n\n### Bugfixes\n\n#### lang\n\n- **[experimental]** Do not use private linkage for slice literal buffers ([#15746], thanks @HertzDevil)\n\n[#15746]: https://github.com/crystal-lang/crystal/pull/15746\n\n#### stdlib\n\n- Require `NO_COLOR` to be non-empty ([#15880], thanks @HertzDevil)\n- _(benchmark)_ Use `UInt64` to track iteration count during warm-up calculation in `Benchmark::IPS` ([#15780], thanks @syeopite)\n- _(collection)_ Fix `Array#|` for different item types ([#15756], thanks @straight-shoota)\n- _(concurrency)_ Fix calling `Fiber::ExecutionContext#enqueue` from bare `Thread` ([#15767], thanks @ysbaddaden)\n- _(concurrency)_ Simplify `Crystal::System::Fiber::RESERVED_STACK_SIZE` initializer on Windows ([#15820], thanks @HertzDevil)\n- _(concurrency)_ Do not print adjacent nodes in `Thread::LinkedList#inspect` ([#15829], thanks @HertzDevil)\n- _(files)_ Fix async append to file in IOCP ([#15681], thanks @ysbaddaden)\n- _(numeric)_ **[regression]** Fix `BigFloat#format` not compiling ([#15796], thanks @HertzDevil)\n- _(numeric)_ Never output exponent in `BigDecimal#format` ([#15795], thanks @HertzDevil)\n- _(numeric)_ Preserve precision when passing `BigDecimal` or `BigFloat` to `sprintf` `%i` ([#15808], thanks @HertzDevil)\n- _(numeric)_ Fix `Float32#abs` for signed zeros ([#15814], thanks @HertzDevil)\n- _(numeric)_ Ensure unary `Float32#-` and `Float64#-` flip sign bit ([#15857], thanks @HertzDevil)\n- _(runtime)_ reopen async `File` passed to `Process.exec` and `.run` (win32) ([#15703], thanks @ysbaddaden)\n- _(runtime)_ raise on manual fiber resume from sleep ([#15744], thanks @ysbaddaden)\n- _(runtime)_ race condition in `Fiber::ExecutionContext::Isolated#wait` ([#15872], thanks @ysbaddaden)\n- _(runtime)_ Prevent leaking memory when `exec_recursive`'s block raises ([#15893], thanks @straight-shoota)\n- _(runtime)_ thread specs must test `Thread`, not `Fiber::ExecutionContext::Isolated` ([#15909], thanks @ysbaddaden)\n- _(system)_ Fix `Path#relative_to` for non-normalized anchor ([#15737], thanks @straight-shoota)\n- _(system)_ **[regression]** Skip `src/termios.cr` on Windows ([#15852], thanks @HertzDevil)\n- _(system)_ Suspend Windows processes until job objects are set up ([#15850], thanks @HertzDevil)\n- _(time)_ Fix `Time::Location::InvalidTZDataError` dropping default message ([#15824], thanks @HertzDevil)\n- _(time)_ Fix IANA time zone names for Windows system time zones ([#15914], thanks @HertzDevil)\n\n[#15880]: https://github.com/crystal-lang/crystal/pull/15880\n[#15780]: https://github.com/crystal-lang/crystal/pull/15780\n[#15756]: https://github.com/crystal-lang/crystal/pull/15756\n[#15767]: https://github.com/crystal-lang/crystal/pull/15767\n[#15820]: https://github.com/crystal-lang/crystal/pull/15820\n[#15829]: https://github.com/crystal-lang/crystal/pull/15829\n[#15681]: https://github.com/crystal-lang/crystal/pull/15681\n[#15796]: https://github.com/crystal-lang/crystal/pull/15796\n[#15795]: https://github.com/crystal-lang/crystal/pull/15795\n[#15808]: https://github.com/crystal-lang/crystal/pull/15808\n[#15814]: https://github.com/crystal-lang/crystal/pull/15814\n[#15857]: https://github.com/crystal-lang/crystal/pull/15857\n[#15703]: https://github.com/crystal-lang/crystal/pull/15703\n[#15744]: https://github.com/crystal-lang/crystal/pull/15744\n[#15872]: https://github.com/crystal-lang/crystal/pull/15872\n[#15893]: https://github.com/crystal-lang/crystal/pull/15893\n[#15909]: https://github.com/crystal-lang/crystal/pull/15909\n[#15737]: https://github.com/crystal-lang/crystal/pull/15737\n[#15852]: https://github.com/crystal-lang/crystal/pull/15852\n[#15850]: https://github.com/crystal-lang/crystal/pull/15850\n[#15824]: https://github.com/crystal-lang/crystal/pull/15824\n[#15914]: https://github.com/crystal-lang/crystal/pull/15914\n\n#### compiler\n\n- _(codegen)_ Add file name to file-private virtual types during codegen ([#15897], thanks @HertzDevil)\n- _(codegen)_ Clear debug location of top-level implicit return ([#15972], thanks @HertzDevil)\n- _(codegen)_ Add file name to file-private generic instance metaclass types during codegen ([#15974], thanks @HertzDevil)\n- _(codegen)_ Add debug locations to metaclass nodes and `typeof` ([#15971], thanks @HertzDevil)\n- _(interpreter)_ Fix interpreter guard clauses for signal handling ([#15892], thanks @straight-shoota)\n- _(parser)_ Add end locations for `Case`, `Asm`, and `Select` ([#15452], thanks @FnControlOption)\n- _(parser)_ **[regression]** Fix stringification of `Not` as call receiver ([#15801], thanks @Blacksmoke16)\n- _(semantic)_ Fix cleanup of one-to-many assignment with untyped RHS ([#15755], thanks @HertzDevil)\n- _(semantic)_ Do not consider type in `Crystal::Var#==` ([#15884], thanks @HertzDevil)\n\n[#15897]: https://github.com/crystal-lang/crystal/pull/15897\n[#15972]: https://github.com/crystal-lang/crystal/pull/15972\n[#15974]: https://github.com/crystal-lang/crystal/pull/15974\n[#15971]: https://github.com/crystal-lang/crystal/pull/15971\n[#15892]: https://github.com/crystal-lang/crystal/pull/15892\n[#15452]: https://github.com/crystal-lang/crystal/pull/15452\n[#15801]: https://github.com/crystal-lang/crystal/pull/15801\n[#15755]: https://github.com/crystal-lang/crystal/pull/15755\n[#15884]: https://github.com/crystal-lang/crystal/pull/15884\n\n#### tools\n\n- _(docs-generator)_ Handle doc locations when file is in parent directory ([#15898], thanks @Blacksmoke16)\n- _(macro-code-coverage)_ Better handle coverage reporting of `MacroIf` nodes with _only_ `MacroLiteral` bodies ([#15938], thanks @Blacksmoke16)\n\n[#15898]: https://github.com/crystal-lang/crystal/pull/15898\n[#15938]: https://github.com/crystal-lang/crystal/pull/15938\n\n### Chores\n\n#### stdlib\n\n- **[breaking]** Make `Enum.from_value` raise `ArgumentError` instead of `Exception` ([#15624], thanks @HertzDevil)\n- Fix duplicate keys in hash literals ([#15843], thanks @straight-shoota)\n- Remove unused code ([#15845], thanks @straight-shoota)\n- Remove shadowed method arguments ([#15846], thanks @straight-shoota)\n- Replace some `not_nil!` calls with bang methods ([#15847], thanks @straight-shoota)\n- Remove useless condition literal ([#15859], thanks @straight-shoota)\n- Use `be_nil` and `be_true`/`be_false` everywhere in specs ([#15867], thanks @straight-shoota)\n- Remove trailing whitespace ([#15869], thanks @straight-shoota)\n- Add trailing newlines ([#15870], thanks @straight-shoota)\n- _(collection)_ Replace literal conditions with nilable casts ([#15844], thanks @straight-shoota)\n- _(time)_ Update Windows zone names ([#15728], thanks @straight-shoota)\n- _(time)_ Update Windows zone names using local database ([#15837], thanks @HertzDevil)\n\n[#15624]: https://github.com/crystal-lang/crystal/pull/15624\n[#15843]: https://github.com/crystal-lang/crystal/pull/15843\n[#15845]: https://github.com/crystal-lang/crystal/pull/15845\n[#15846]: https://github.com/crystal-lang/crystal/pull/15846\n[#15847]: https://github.com/crystal-lang/crystal/pull/15847\n[#15859]: https://github.com/crystal-lang/crystal/pull/15859\n[#15867]: https://github.com/crystal-lang/crystal/pull/15867\n[#15869]: https://github.com/crystal-lang/crystal/pull/15869\n[#15870]: https://github.com/crystal-lang/crystal/pull/15870\n[#15844]: https://github.com/crystal-lang/crystal/pull/15844\n[#15728]: https://github.com/crystal-lang/crystal/pull/15728\n[#15837]: https://github.com/crystal-lang/crystal/pull/15837\n\n#### compiler\n\n- Replace `is_a?` calls with convenient alternatives ([#15860], thanks @straight-shoota)\n\n[#15860]: https://github.com/crystal-lang/crystal/pull/15860\n\n#### other\n\n- Fix typos and add `typos` integration ([#15873], thanks @straight-shoota)\n\n[#15873]: https://github.com/crystal-lang/crystal/pull/15873\n\n### Performance\n\n#### lang\n\n- **[experimental]** Optimize slice literal codegen on LLVM 21 ([#15803], thanks @HertzDevil)\n\n[#15803]: https://github.com/crystal-lang/crystal/pull/15803\n\n#### stdlib\n\n- _(collection)_ Optimize `Indexable#find` ([#15674], thanks @straight-shoota)\n- _(numeric)_ Add specialized implementations for `Float#infinite?` and `#nan?` ([#15813], thanks @HertzDevil)\n\n[#15674]: https://github.com/crystal-lang/crystal/pull/15674\n[#15813]: https://github.com/crystal-lang/crystal/pull/15813\n\n#### compiler\n\n- Apply performance improvement suggestions from ameba ([#15839], thanks @straight-shoota)\n- _(codegen)_ Simplify codegen of mixed-type checked integer addition and subtraction ([#15878], thanks @HertzDevil)\n\n[#15839]: https://github.com/crystal-lang/crystal/pull/15839\n[#15878]: https://github.com/crystal-lang/crystal/pull/15878\n\n### Refactor\n\n#### stdlib\n\n- _(collection)_ Make `offset` a required parameter in `Indexable#find` ([#15671], thanks @straight-shoota)\n- _(crypto)_ Add extra `Digest.update` overloads for `Bytes` ([#15736], thanks @straight-shoota)\n- _(crypto)_ **[experimental]** Use `Slice` literals in `Crypto::Bcrypt` ([#15781], thanks @HertzDevil)\n- _(files)_ Ask system to decide non-blocking `IO::FileDescriptor` (win32) ([#15753], thanks @ysbaddaden)\n- _(files)_ `Crystal::EventLoop::FileDescriptor#open` now sets the non/blocking flag ([#15754], thanks @ysbaddaden)\n- _(networking)_ Use relative requires for `http/` files ([#15675], thanks @straight-shoota)\n- _(networking)_ Split `StaticFileHandler#call` into structured components ([#15678], thanks @straight-shoota)\n- _(numeric)_ **[experimental]** Use `Slice.literal` for `fast_float` when supported ([#15667], thanks @HertzDevil)\n- _(numeric)_ Combine the decimal number printing implementations ([#15815], thanks @HertzDevil)\n- _(runtime)_ Extract bindings for LibC errno to `src/lib_c/` ([#15565], thanks @ysbaddaden)\n- _(runtime)_ Extract `Exception::CallStack.decode_backtrace_frame` helper ([#15615], thanks @ysbaddaden)\n- _(runtime)_ Let `Crystal::EventLoop#close` do the actual close (not just cleanup) ([#15641], thanks @ysbaddaden)\n- _(serialization)_ Replace deprecated `LibXML.xmlGcMemSetup` with `.xmlMemSetup` ([#15626], thanks @straight-shoota)\n- _(serialization)_ XML: modernize API when available & workaround issues with legacy versions ([#15899], thanks @ysbaddaden)\n- _(specs)_ Support arbitrary `IO`s in `Spec::CLI` ([#15882], thanks @HertzDevil)\n- _(specs)_ Replace some lookup hashes in `Spec` with exhaustive cases ([#15879], thanks @HertzDevil)\n- _(text)_ **[experimental]** Use slice literals for `String::CHAR_TO_DIGIT` and `CHAR_TO_DIGIT62` ([#15745], thanks @HertzDevil)\n- _(text)_ Replace some uses of `String#%` with justification methods ([#15821], thanks @HertzDevil)\n- _(text)_ Avoid calling `chars.size` on `String`s ([#15822], thanks @HertzDevil)\n- _(time)_ Move most POSIX TZ string functionality to a module ([#15866], thanks @HertzDevil)\n\n[#15671]: https://github.com/crystal-lang/crystal/pull/15671\n[#15736]: https://github.com/crystal-lang/crystal/pull/15736\n[#15781]: https://github.com/crystal-lang/crystal/pull/15781\n[#15753]: https://github.com/crystal-lang/crystal/pull/15753\n[#15754]: https://github.com/crystal-lang/crystal/pull/15754\n[#15675]: https://github.com/crystal-lang/crystal/pull/15675\n[#15678]: https://github.com/crystal-lang/crystal/pull/15678\n[#15667]: https://github.com/crystal-lang/crystal/pull/15667\n[#15815]: https://github.com/crystal-lang/crystal/pull/15815\n[#15565]: https://github.com/crystal-lang/crystal/pull/15565\n[#15615]: https://github.com/crystal-lang/crystal/pull/15615\n[#15641]: https://github.com/crystal-lang/crystal/pull/15641\n[#15626]: https://github.com/crystal-lang/crystal/pull/15626\n[#15899]: https://github.com/crystal-lang/crystal/pull/15899\n[#15882]: https://github.com/crystal-lang/crystal/pull/15882\n[#15879]: https://github.com/crystal-lang/crystal/pull/15879\n[#15745]: https://github.com/crystal-lang/crystal/pull/15745\n[#15821]: https://github.com/crystal-lang/crystal/pull/15821\n[#15822]: https://github.com/crystal-lang/crystal/pull/15822\n[#15866]: https://github.com/crystal-lang/crystal/pull/15866\n\n#### compiler\n\n- _(codegen)_ Replace type-id function with lookup table ([#15904], thanks @BlobCodes)\n\n[#15904]: https://github.com/crystal-lang/crystal/pull/15904\n\n### Documentation\n\n#### stdlib\n\n- _(crypto)_ Add type restrictions to `Digest` ([#15696], thanks @Vici37)\n- _(crypto)_ Add documentation for `Crypto::Bcrypt::Password#to_s` ([#15935], thanks @hugopl)\n- _(crypto)_ Improve docs for `OpenSSL::Cipher` ([#15964], thanks @Fijxu)\n- _(macros)_ **[experimental]** Document `Crystal::Macros::StringLiteral#to_utf16` ([#15748], thanks @HertzDevil)\n- _(runtime)_ Document `GC::Stats` properties ([#15676], thanks @ysbaddaden)\n- _(runtime)_ Add links to language specification in docs for pseudo methods ([#15864], thanks @straight-shoota)\n- _(runtime)_ Fix unclosed codeblock in `__crystal_pseudo_alignof` docs ([#15945], thanks @syeopite)\n- _(serialization)_ Add type restrictions to `CSV` ([#15695], thanks @Vici37)\n- _(specs)_ Clarify docs in regards to what `be_nil` expectation does ([#15954], thanks @Blacksmoke16)\n- _(system)_ Add type restrictions to `Dir` ([#15697], thanks @Vici37)\n- _(system)_ Improve docs for `Socket` and `IO::FileDescriptor` handles ([#15977], thanks @straight-shoota)\n- _(text)_ Improve docs for `String#lines` and `#each_line` ([#15894], thanks @straight-shoota)\n\n[#15696]: https://github.com/crystal-lang/crystal/pull/15696\n[#15935]: https://github.com/crystal-lang/crystal/pull/15935\n[#15964]: https://github.com/crystal-lang/crystal/pull/15964\n[#15748]: https://github.com/crystal-lang/crystal/pull/15748\n[#15676]: https://github.com/crystal-lang/crystal/pull/15676\n[#15864]: https://github.com/crystal-lang/crystal/pull/15864\n[#15945]: https://github.com/crystal-lang/crystal/pull/15945\n[#15695]: https://github.com/crystal-lang/crystal/pull/15695\n[#15954]: https://github.com/crystal-lang/crystal/pull/15954\n[#15697]: https://github.com/crystal-lang/crystal/pull/15697\n[#15977]: https://github.com/crystal-lang/crystal/pull/15977\n[#15894]: https://github.com/crystal-lang/crystal/pull/15894\n\n#### compiler\n\n- _(parser)_ Improve examples for the syntax highlighter documentation ([#15699], thanks @tamdaz)\n\n[#15699]: https://github.com/crystal-lang/crystal/pull/15699\n\n### Specs\n\n#### stdlib\n\n- Drop `to_a` in expectations with `Slice` ([#15735], thanks @straight-shoota)\n- _(crypto)_ Unroll test data in specs for `crypto/subtle` ([#15702], thanks @straight-shoota)\n- _(networking)_ Add test for `HTTP::Request` with resource string `//` ([#15546], thanks @miry)\n- _(networking)_ Pick TCP and UDP local ports differently in socket specs ([#15828], thanks @HertzDevil)\n- _(serialization)_ Remove forgotten debug `puts` call ([#15942], thanks @Sija)\n- _(text)_ Simplify specs for string comparison ([#15868], thanks @straight-shoota)\n\n[#15735]: https://github.com/crystal-lang/crystal/pull/15735\n[#15702]: https://github.com/crystal-lang/crystal/pull/15702\n[#15546]: https://github.com/crystal-lang/crystal/pull/15546\n[#15828]: https://github.com/crystal-lang/crystal/pull/15828\n[#15942]: https://github.com/crystal-lang/crystal/pull/15942\n[#15868]: https://github.com/crystal-lang/crystal/pull/15868\n\n#### compiler\n\n- _(interpreter)_ Enable interpreter integration test for XML ([#15628], thanks @straight-shoota)\n- _(parser)_ Cleanup parser specs ([#15446], thanks @FnControlOption)\n\n[#15628]: https://github.com/crystal-lang/crystal/pull/15628\n[#15446]: https://github.com/crystal-lang/crystal/pull/15446\n\n#### tools\n\n- _(formatter)_ Fix formatter specs with string interpolation ([#15842], thanks @straight-shoota)\n\n[#15842]: https://github.com/crystal-lang/crystal/pull/15842\n\n### Infrastructure\n\n- Changelog for 1.17.0 ([#15900], [#15983], thanks @straight-shoota)\n- Update previous Crystal release 1.16.1 ([#15649], thanks @straight-shoota)\n- Update `release-update` script: Truncate CHANGELOG ([#15679], thanks @straight-shoota)\n- Merge `release/1.16` into master ([#15729], thanks @straight-shoota)\n- Simplify `docs_main.cr` ([#15621], thanks @straight-shoota)\n- Update previous Crystal release 1.16.2 ([#15730], thanks @straight-shoota)\n- Fix order of title clean steps in github-changelog helper ([#15727], thanks @straight-shoota)\n- Fix `scripts/release-update.sh` idempotent previous CHANGELOG entry ([#15731], thanks @straight-shoota)\n- Merge `release/1.16`@1.16.3 into master ([#15774], thanks @straight-shoota)\n- Update previous Crystal release 1.16.3 ([#15773], thanks @straight-shoota)\n- Makefile: Fix target location for `install_docs` ([#15853], thanks @straight-shoota)\n- Add ameba ([#15875], thanks @straight-shoota)\n- Allow-list Crystal's funding.json from the project ([#15969], thanks @matiasgarciaisaia)\n- Allow `LLVM_VERSION` override inside `Makefile` ([#15765], thanks @HertzDevil)\n- Add build script for `spec/std/data/zoneinfo.zip` ([#15831], thanks @HertzDevil)\n- _(ci)_ Update GH Actions ([#15668], thanks @renovate)\n- _(ci)_ Add `XML CI` workflow ([#15923], thanks @straight-shoota)\n- _(ci)_ Update typos 1.34.0 ([#15950], thanks @straight-shoota)\n- _(ci)_ Update korthout/backport-action action to v3.2.1 ([#15949], thanks @renovate)\n- _(ci)_ Update cygwin/cygwin-install-action action to v6 ([#15965], thanks @renovate)\n- _(ci)_ Drop the static LLVM libraries on Windows MSVC CI ([#15797], thanks @HertzDevil)\n- _(ci)_ Set up Inno Setup explicitly on MSVC CI ([#15851], [#15861], thanks @HertzDevil)\n- _(ci)_ Update library versions for MSVC CI ([#15921], thanks @HertzDevil)\n- _(ci)_ Add CI workflow for MinGW-w64 ARM64 builds ([#15794], thanks @HertzDevil)\n- _(ci)_ **[regression]** Use `CMAKE_MSVC_RUNTIME_LIBRARY` for the MSVC PCRE2 static library ([#15802], thanks @HertzDevil)\n\n[#15900]: https://github.com/crystal-lang/crystal/pull/15900\n[#15983]: https://github.com/crystal-lang/crystal/pull/15983\n[#15649]: https://github.com/crystal-lang/crystal/pull/15649\n[#15679]: https://github.com/crystal-lang/crystal/pull/15679\n[#15729]: https://github.com/crystal-lang/crystal/pull/15729\n[#15621]: https://github.com/crystal-lang/crystal/pull/15621\n[#15730]: https://github.com/crystal-lang/crystal/pull/15730\n[#15727]: https://github.com/crystal-lang/crystal/pull/15727\n[#15731]: https://github.com/crystal-lang/crystal/pull/15731\n[#15774]: https://github.com/crystal-lang/crystal/pull/15774\n[#15773]: https://github.com/crystal-lang/crystal/pull/15773\n[#15853]: https://github.com/crystal-lang/crystal/pull/15853\n[#15875]: https://github.com/crystal-lang/crystal/pull/15875\n[#15969]: https://github.com/crystal-lang/crystal/pull/15969\n[#15765]: https://github.com/crystal-lang/crystal/pull/15765\n[#15831]: https://github.com/crystal-lang/crystal/pull/15831\n[#15668]: https://github.com/crystal-lang/crystal/pull/15668\n[#15923]: https://github.com/crystal-lang/crystal/pull/15923\n[#15950]: https://github.com/crystal-lang/crystal/pull/15950\n[#15949]: https://github.com/crystal-lang/crystal/pull/15949\n[#15965]: https://github.com/crystal-lang/crystal/pull/15965\n[#15797]: https://github.com/crystal-lang/crystal/pull/15797\n[#15851]: https://github.com/crystal-lang/crystal/pull/15851\n[#15861]: https://github.com/crystal-lang/crystal/pull/15861\n[#15921]: https://github.com/crystal-lang/crystal/pull/15921\n[#15794]: https://github.com/crystal-lang/crystal/pull/15794\n[#15802]: https://github.com/crystal-lang/crystal/pull/15802\n"
  },
  {
    "path": "doc/changelogs/v1.18.md",
    "content": "# Changelog 1.18\n\n## [1.18.2] (2025-10-21)\n\n[1.18.2]: https://github.com/crystal-lang/crystal/releases/1.18.2\n\n### Bugfixes\n\n#### stdlib\n\n- _(files)_ **[regression]** Revert type restriction for `IO#read_bytes` ([#16231], thanks @straight-shoota)\n- _(runtime)_ Fix `Fiber::ExecutionContext.default_workers_count` to avoid taking into account unsupported values ([#16227], thanks @Sija)\n\n[#16231]: https://github.com/crystal-lang/crystal/pull/16231\n[#16227]: https://github.com/crystal-lang/crystal/pull/16227\n\n### Infrastructure\n\n- Changelog for 1.18.2 ([#16243], thanks @matiasgarciaisaia)\n\n[#16243]: https://github.com/crystal-lang/crystal/pull/16243\n\n## [1.18.1] (2025-10-17)\n\n[1.18.1]: https://github.com/crystal-lang/crystal/releases/1.18.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(networking)_ **[regression]** Revert adding type restrictions on `HTTP::WebSocket#send` etc. ([#16218], thanks @straight-shoota)\n\n[#16218]: https://github.com/crystal-lang/crystal/pull/16218\n\n#### compiler\n\n- _(parser)_ **[regression]** Fix allow space after base type in enum def ([#16217], thanks @straight-shoota)\n\n[#16217]: https://github.com/crystal-lang/crystal/pull/16217\n\n### Infrastructure\n\n- Changelog for 1.18.1 ([#16224], thanks @matiasgarciaisaia)\n\n[#16224]: https://github.com/crystal-lang/crystal/pull/16224\n\n## [1.18.0] (2025-10-14)\n\n[1.18.0]: https://github.com/crystal-lang/crystal/releases/1.18.0\n\n### Features\n\n#### lang\n\n- Support `ProcPointer`s of lib funs with parameter types ([#16089], thanks @HertzDevil)\n- _(annotations)_ Print deprecation warning on types and aliases ([#15962], thanks @ysbaddaden)\n- _(annotations)_ Print deprecation warnings on deprecated method argument ([#15999], thanks @ysbaddaden)\n- _(macros)_ **[breaking]** Expand empty `(Named)TupleLiteral` to `(Named)Tuple.new` instead of `{}` ([#16108], thanks @spuun)\n- _(macros)_ Add `ArrayLiteral#*`, `StringLiteral#*` and `TupleLiteral#*` ([#16154], [#16206], thanks @jneen, @ysbaddaden)\n\n[#16089]: https://github.com/crystal-lang/crystal/pull/16089\n[#15962]: https://github.com/crystal-lang/crystal/pull/15962\n[#15999]: https://github.com/crystal-lang/crystal/pull/15999\n[#16108]: https://github.com/crystal-lang/crystal/pull/16108\n[#16154]: https://github.com/crystal-lang/crystal/pull/16154\n[#16206]: https://github.com/crystal-lang/crystal/pull/16206\n\n#### stdlib\n\n- Add `summary_width` and `summary_indent` to `OptionParser` ([#15326], thanks @kojix2)\n- _(collection)_ Add `Set#select!` and `#reject!` ([#16060], thanks @HertzDevil)\n- _(concurrency)_ Speed up `Parallel::Scheduler#quick_dequeue?` for `max=1` ([#15961], thanks @ysbaddaden)\n- _(concurrency)_ Default execution context is now parallel (MT:1) ([#16136], thanks @ysbaddaden)\n- _(files)_ **[deprecation]** Add `.set_blocking` to `Socket` and `IO::FileDescriptor` and deprecate `#blocking` property ([#16033], [#16129], thanks @ysbaddaden)\n- _(llvm)_ Support LLVM 21.1 and 22.0 ([#16062], [#16198], thanks @HertzDevil, @straight-shoota)\n- _(macros)_ Add `NumberLiteral#zero?` ([#10248], thanks @Sija)\n- _(macros)_ Add `thread_local` macro ([#16173], thanks @ysbaddaden)\n- _(networking)_ Fix `URI#host=` to wrap IPv6 address in brackets ([#16164], thanks @stakach)\n- _(runtime)_ Lazily instantiate the event loop of isolated execution contexts ([#16063], thanks @ysbaddaden)\n- _(runtime)_ Add `Fiber::ExecutionContext::Parallel#resize` ([#15956], thanks @ysbaddaden)\n- _(runtime)_ Add `Crystal::System.effective_cpu_count` ([#16148], thanks @ysbaddaden)\n- _(runtime)_ Improve `Fiber::ExecutionContext.default_workers_count` ([#16149], thanks @ysbaddaden)\n- _(serialization)_ Add `Time::Location.from_json_object_key` ([#15957], thanks @Sija)\n- _(serialization)_ Resolve YAML aliases in `YAML::Any` ([#15941], thanks @willhbr)\n- _(serialization)_ Embed libxml2 version number on Windows MSVC ([#16104], thanks @HertzDevil)\n- _(serialization)_ Add `JSON::Any` wrapper around `JSON::Any#inspect` output ([#15979], thanks @jneen)\n- _(specs)_ Add `with_tempdir` spec helper ([#16005], thanks @straight-shoota)\n- _(system)_ Add `File.readlink?` ([#16004], thanks @straight-shoota)\n- _(text)_ Add `SemanticVersion.valid?` & `SemanticVersion.parse?` ([#15051], thanks @devnote-dev)\n- _(text)_ Add `String.additive_identity` ([#15998], thanks @straight-shoota)\n- _(text)_ Use proper ANSI reset codes in `Colorize` ([#16052], thanks @Blacksmoke16)\n- _(text)_ Update Unicode to 17.0.0 ([#16160], thanks @HertzDevil)\n- _(time)_ Use canonical IANA name for the local Windows system time zone ([#15967], thanks @HertzDevil)\n- _(time)_ Load `Location.local` by symlink name ([#16002], [#16022], thanks @straight-shoota)\n- _(time)_ Add `Time::Location.load?` ([#16121], thanks @straight-shoota)\n- _(time)_ Format `Time#inspect` with Internet Extended Date/Time Format ([#16039], thanks @straight-shoota)\n\n[#15326]: https://github.com/crystal-lang/crystal/pull/15326\n[#16060]: https://github.com/crystal-lang/crystal/pull/16060\n[#15961]: https://github.com/crystal-lang/crystal/pull/15961\n[#16136]: https://github.com/crystal-lang/crystal/pull/16136\n[#16033]: https://github.com/crystal-lang/crystal/pull/16033\n[#16129]: https://github.com/crystal-lang/crystal/pull/16129\n[#16062]: https://github.com/crystal-lang/crystal/pull/16062\n[#16198]: https://github.com/crystal-lang/crystal/pull/16198\n[#10248]: https://github.com/crystal-lang/crystal/pull/10248\n[#16173]: https://github.com/crystal-lang/crystal/pull/16173\n[#16164]: https://github.com/crystal-lang/crystal/pull/16164\n[#16063]: https://github.com/crystal-lang/crystal/pull/16063\n[#15956]: https://github.com/crystal-lang/crystal/pull/15956\n[#16148]: https://github.com/crystal-lang/crystal/pull/16148\n[#16149]: https://github.com/crystal-lang/crystal/pull/16149\n[#15957]: https://github.com/crystal-lang/crystal/pull/15957\n[#15941]: https://github.com/crystal-lang/crystal/pull/15941\n[#16104]: https://github.com/crystal-lang/crystal/pull/16104\n[#15979]: https://github.com/crystal-lang/crystal/pull/15979\n[#16005]: https://github.com/crystal-lang/crystal/pull/16005\n[#16004]: https://github.com/crystal-lang/crystal/pull/16004\n[#15051]: https://github.com/crystal-lang/crystal/pull/15051\n[#15998]: https://github.com/crystal-lang/crystal/pull/15998\n[#16052]: https://github.com/crystal-lang/crystal/pull/16052\n[#16160]: https://github.com/crystal-lang/crystal/pull/16160\n[#15967]: https://github.com/crystal-lang/crystal/pull/15967\n[#16002]: https://github.com/crystal-lang/crystal/pull/16002\n[#16022]: https://github.com/crystal-lang/crystal/pull/16022\n[#16121]: https://github.com/crystal-lang/crystal/pull/16121\n[#16039]: https://github.com/crystal-lang/crystal/pull/16039\n\n#### compiler\n\n- _(cli)_ Add the ability to dump type information to a JSON file ([#16027], thanks @HertzDevil)\n- _(interpreter)_ Support `Proc.new(Void*, Void*)` in the interpreter ([#16044], thanks @HertzDevil)\n- _(interpreter:repl)_ Fully exit the process on `exit!` from REPL ([#16171], thanks @jneen)\n- _(semantic)_ Resolve types when guessing return type from class method overloads ([#16118], thanks @HertzDevil)\n- _(semantic)_ Guess instance variable types from global method calls ([#16119], thanks @HertzDevil)\n\n[#16027]: https://github.com/crystal-lang/crystal/pull/16027\n[#16044]: https://github.com/crystal-lang/crystal/pull/16044\n[#16171]: https://github.com/crystal-lang/crystal/pull/16171\n[#16118]: https://github.com/crystal-lang/crystal/pull/16118\n[#16119]: https://github.com/crystal-lang/crystal/pull/16119\n\n#### tools\n\n- _(docs-generator)_ Add support for deprecated parameters in doc generator ([#16012], thanks @straight-shoota)\n- _(hierarchy)_ Show extern union types in hierarchy tool ([#16026], thanks @HertzDevil)\n\n[#16012]: https://github.com/crystal-lang/crystal/pull/16012\n[#16026]: https://github.com/crystal-lang/crystal/pull/16026\n\n### Bugfixes\n\n#### stdlib\n\n- _(files)_ Fix `fcntl` reference for `fds[1]` in socketpair setup ([#16072], thanks @kojix2)\n- _(log)_ Make crystal log resilient to empty LOG_LEVEL env var ([#15963], thanks @anaPerezGhiglia)\n- _(networking)_ Preserve query params in `StaticFileHandler` redirects ([#15789], thanks @syeopite)\n- _(networking)_ Fix `StaticFileHandler` to return 404 on file error ([#16025], [#16077], thanks @straight-shoota)\n- _(networking)_ Run `before_request` callback in `HTTP::Client` only once ([#16064], thanks @straight-shoota)\n- _(networking)_ Fix `HTTP::Request#query=` typing ([#16143], thanks @Blacksmoke16)\n- _(runtime)_ `Fiber::ExecutionContext::Parallel::Scheduler#tick` must be unsigned ([#16155], thanks @ysbaddaden)\n- _(serialization)_ Fix add missing `#==` overloads for `Log::Metadata::Value` and `YAML::Any` ([#15732], thanks @straight-shoota)\n- _(serialization)_ Fix pointer access bug in `XML::NodeSet` ([#16055], thanks @toddsundsted)\n- _(serialization)_ Remove `NOERROR` from LibXML default options ([#16103], thanks @straight-shoota)\n- _(serialization)_ Move `*::Serializable`'s private constructors into `macro included` hook ([#16147], thanks @HertzDevil)\n- _(serialization)_ Correctly reference global JSON/YAML modules ([#16161], [#16169], thanks @Sija, @straight-shoota)\n- _(serialization)_ Fix element type inference in `YAML::ArrayConverter.from_yaml` ([#16166], thanks @HertzDevil)\n- _(system)_ Fix return type of `system_close_on_exec=` on Windows ([#16095], thanks @straight-shoota)\n- _(time)_ Fix time zone identifier `America/Argentina/Buenos_Aires` ([#16078], thanks @straight-shoota)\n- _(time)_ Fix `Time#at_beginning_of_week`,`#at_end_of_week` to respect local timezone ([#16113], thanks @straight-shoota)\n\n[#16072]: https://github.com/crystal-lang/crystal/pull/16072\n[#15963]: https://github.com/crystal-lang/crystal/pull/15963\n[#15789]: https://github.com/crystal-lang/crystal/pull/15789\n[#16025]: https://github.com/crystal-lang/crystal/pull/16025\n[#16077]: https://github.com/crystal-lang/crystal/pull/16077\n[#16064]: https://github.com/crystal-lang/crystal/pull/16064\n[#16143]: https://github.com/crystal-lang/crystal/pull/16143\n[#16155]: https://github.com/crystal-lang/crystal/pull/16155\n[#15732]: https://github.com/crystal-lang/crystal/pull/15732\n[#16055]: https://github.com/crystal-lang/crystal/pull/16055\n[#16103]: https://github.com/crystal-lang/crystal/pull/16103\n[#16147]: https://github.com/crystal-lang/crystal/pull/16147\n[#16161]: https://github.com/crystal-lang/crystal/pull/16161\n[#16169]: https://github.com/crystal-lang/crystal/pull/16169\n[#16166]: https://github.com/crystal-lang/crystal/pull/16166\n[#16095]: https://github.com/crystal-lang/crystal/pull/16095\n[#16078]: https://github.com/crystal-lang/crystal/pull/16078\n[#16113]: https://github.com/crystal-lang/crystal/pull/16113\n\n#### compiler\n\n- _(codegen)_ Never generate assignments to a block's underscore parameters ([#16057], thanks @HertzDevil)\n- _(codegen)_ Fix `@[Primitive]` codegen for typedefs ([#16110], thanks @HertzDevil)\n- _(interpreter)_ never generate assignments to a block's underscore parameters ([#16058], thanks @HertzDevil)\n- _(interpreter)_ Add `writer.close_on_finalize = false` for signal pipe ([#16167], thanks @straight-shoota)\n- _(interpreter:repl)_ Continue REPL prompt if input consists entirely of annotations ([#16045], thanks @HertzDevil)\n- _(parser)_ Disallow unterminated escaped heredoc without trailing newline ([#16046], thanks @HertzDevil)\n- _(parser)_ Require space, semicolon, or newline after class/module/etc. header ([#13375], thanks @FnControlOption)\n- _(parser)_ Fix parsing `ReadInstanceVar` in short block syntax ([#16099], thanks @nobodywasishere)\n- _(semantic)_ Pass through variable types unchanged in a while loop ([#15980], thanks @HertzDevil)\n- _(semantic)_ deprecation warning for (expanded) deprecated def ([#15997], thanks @ysbaddaden)\n- _(semantic)_ Copy annotations in  `Crystal::Arg#copy_without_location` ([#16008], thanks @ysbaddaden)\n- _(semantic)_ Copy annotations in `Crystal::Def#expand_default_arguments` ([#16007], thanks @ysbaddaden)\n- _(semantic)_ Copy annotations in `Crystal::Def#expand_new_default_arguments` ([#16013], thanks @ysbaddaden)\n- _(semantic)_ Fix error message for `StaticArray` with non-integer generic argument `N` ([#16037], thanks @straight-shoota)\n- _(semantic)_ Resolve bound type parameters from generic superclass during path lookup ([#10839], thanks @HertzDevil)\n- _(semantic)_ **[regression]** Ensure hash literals are evaluated from left to right ([#16124], thanks @HertzDevil)\n\n[#16057]: https://github.com/crystal-lang/crystal/pull/16057\n[#16110]: https://github.com/crystal-lang/crystal/pull/16110\n[#16058]: https://github.com/crystal-lang/crystal/pull/16058\n[#16167]: https://github.com/crystal-lang/crystal/pull/16167\n[#16045]: https://github.com/crystal-lang/crystal/pull/16045\n[#16046]: https://github.com/crystal-lang/crystal/pull/16046\n[#13375]: https://github.com/crystal-lang/crystal/pull/13375\n[#16099]: https://github.com/crystal-lang/crystal/pull/16099\n[#15980]: https://github.com/crystal-lang/crystal/pull/15980\n[#15997]: https://github.com/crystal-lang/crystal/pull/15997\n[#16008]: https://github.com/crystal-lang/crystal/pull/16008\n[#16007]: https://github.com/crystal-lang/crystal/pull/16007\n[#16013]: https://github.com/crystal-lang/crystal/pull/16013\n[#16037]: https://github.com/crystal-lang/crystal/pull/16037\n[#10839]: https://github.com/crystal-lang/crystal/pull/10839\n[#16124]: https://github.com/crystal-lang/crystal/pull/16124\n\n### Chores\n\n#### stdlib\n\n- Drop `Thread::Local(T)` ([#16179], thanks @ysbaddaden)\n- _(concurrency)_ **[deprecation]** Deprecate `Atomic::Flag` ([#15805], thanks @ysbaddaden)\n- _(files)_ **[deprecation]** Deprecate the `blocking` parameter of `File`, `Socket` and `IO::FileDescriptor` constructors ([#16034], [#16043], [#16047], thanks @ysbaddaden, @Blacksmoke16)\n- _(numeric)_ **[deprecation]** Deprecate `Float::Printer::IEEE` ([#16050], thanks @HertzDevil)\n- _(system)_ **[deprecation]** Deprecate `Process::Status#exit_signal` ([#16003], thanks @straight-shoota)\n\n[#16179]: https://github.com/crystal-lang/crystal/pull/16179\n[#15805]: https://github.com/crystal-lang/crystal/pull/15805\n[#16034]: https://github.com/crystal-lang/crystal/pull/16034\n[#16043]: https://github.com/crystal-lang/crystal/pull/16043\n[#16047]: https://github.com/crystal-lang/crystal/pull/16047\n[#16050]: https://github.com/crystal-lang/crystal/pull/16050\n[#16003]: https://github.com/crystal-lang/crystal/pull/16003\n\n### Performance\n\n#### lang\n\n- Optimize `Enum.parse?`, avoiding allocations ([#15927], [#16192], thanks @jgaskins, @straight-shoota)\n\n[#15927]: https://github.com/crystal-lang/crystal/pull/15927\n[#16192]: https://github.com/crystal-lang/crystal/pull/16192\n\n#### stdlib\n\n- _(numeric)_ Do not use equality checks in `Int#upto` and `#downto` ([#16076], thanks @HertzDevil)\n- _(system)_ Simplify buffer for `File.readlink` ([#16021], thanks @straight-shoota)\n- _(time)_ Optimize `Time#to_s` ([#16042], thanks @straight-shoota)\n\n[#16076]: https://github.com/crystal-lang/crystal/pull/16076\n[#16021]: https://github.com/crystal-lang/crystal/pull/16021\n[#16042]: https://github.com/crystal-lang/crystal/pull/16042\n\n#### compiler\n\n- Inline `Crystal.check_type_can_be_stored` ([#16130], thanks @HertzDevil)\n- _(codegen)_ Group temporary variables by file name: compound array assignments ([#16122], thanks @HertzDevil)\n\n[#16130]: https://github.com/crystal-lang/crystal/pull/16130\n[#16122]: https://github.com/crystal-lang/crystal/pull/16122\n\n### Refactor\n\n#### stdlib\n\n- _(concurrency)_ Refactor redundant `begin ... end` blocks ([#16011], thanks @straight-shoota)\n- _(networking)_ Reorder implementation of `HTTP::Cookies#<<` and `[]=` ([#16107], thanks @straight-shoota)\n- _(runtime)_ Remove nilable pointers in `Crystal::PointerPairingHeap` ([#15973], thanks @HertzDevil)\n- _(runtime)_ Remove nilable pointer in `Crystal::EventLoop::IOCP#@timer_packet` ([#15975], thanks @HertzDevil)\n- _(runtime)_ Remove minimum in `Fiber::ExecutionContext::Parallel` ([#15946], thanks @ysbaddaden)\n- _(runtime)_ Pass `fd` implicitly to `System::FileDescriptor` and `System::Socket` ([#16137], [#16183], thanks @ysbaddaden)\n- _(runtime)_ Drop custom implementation of `Fiber::ExecutionContext::Concurrent` ([#16135], thanks @ysbaddaden)\n- _(specs)_ Keep own colorization state in `Spec::CLI` ([#15926], thanks @HertzDevil)\n- _(text)_ Use `ensure_suffix` instead of manually checking for suffixes ([#15858], thanks @MatheusRich)\n- _(time)_ Remove the old Windows time zone name table ([#16006], thanks @HertzDevil)\n\n[#16011]: https://github.com/crystal-lang/crystal/pull/16011\n[#16107]: https://github.com/crystal-lang/crystal/pull/16107\n[#15973]: https://github.com/crystal-lang/crystal/pull/15973\n[#15975]: https://github.com/crystal-lang/crystal/pull/15975\n[#15946]: https://github.com/crystal-lang/crystal/pull/15946\n[#16137]: https://github.com/crystal-lang/crystal/pull/16137\n[#16183]: https://github.com/crystal-lang/crystal/pull/16183\n[#16135]: https://github.com/crystal-lang/crystal/pull/16135\n[#15926]: https://github.com/crystal-lang/crystal/pull/15926\n[#15858]: https://github.com/crystal-lang/crystal/pull/15858\n[#16006]: https://github.com/crystal-lang/crystal/pull/16006\n\n#### compiler\n\n- _(codegen)_ Refactor `Compiler#must_compile?` to clarify the rules. ([#16056], thanks @kojix2)\n- _(parser)_ Add `#has_any_args?` method for `ASTNode`s ([#16115], thanks @straight-shoota)\n\n[#16056]: https://github.com/crystal-lang/crystal/pull/16056\n[#16115]: https://github.com/crystal-lang/crystal/pull/16115\n\n#### tools\n\n- _(formatter)_ Simplify control flow in formatter for `Call` nodes ([#16170], thanks @straight-shoota)\n\n[#16170]: https://github.com/crystal-lang/crystal/pull/16170\n\n#### other\n\n- Refactor `unless ... else` ([#16010], thanks @straight-shoota)\n- Removed unused variables ([#16014], thanks @straight-shoota)\n\n[#16010]: https://github.com/crystal-lang/crystal/pull/16010\n[#16014]: https://github.com/crystal-lang/crystal/pull/16014\n\n### Documentation\n\n#### lang\n\n- _(annotations)_ Enhance documentation of `Deprecated` annotation ([#16195], thanks @straight-shoota)\n\n[#16195]: https://github.com/crystal-lang/crystal/pull/16195\n\n#### stdlib\n\n- _(benchmark)_ Add type restrictions to benchmark directory ([#15688], thanks @Vici37)\n- _(concurrency)_ Improve docs for `Channel#close` ([#15910], thanks @anaPerezGhiglia)\n- _(crypto)_ Fix doc example for `Crypto::BCrypt.new(String, String, Int)` ([#15931], thanks @hugopl)\n- _(crypto)_ Add type restrictions to crypto directory ([#15694], thanks @Vici37)\n- _(files)_ Add type restrictions to mime ([#15834], thanks @Vici37)\n- _(files)_ Fix `IO::TimeoutError` documentation use of deprecated `read_timeout=` ([#16073], thanks @lachlan)\n- _(files)_ Add type restrictions to io ([#15698], thanks @Vici37)\n- _(llvm)_ Deprecate each `LLVM::ABI::*` classes ([#15989], thanks @ysbaddaden)\n- _(log)_ Add type restrictions to Log directory ([#15777], thanks @Vici37)\n- _(networking)_ Add type restrictions to Oauth directory ([#15687], thanks @Vici37)\n- _(networking)_ Add type restrictions to http ([#15710], thanks @Vici37)\n- _(numeric)_ Add type restrictions to big ([#15689], thanks @Vici37)\n- _(runtime)_ Fix typo in `Object` docs ([#16035], thanks @plambert)\n- _(runtime)_ Tweak docs for `Fiber::ExecutionContext` ([#16196], thanks @ysbaddaden)\n- _(serialization)_ Add type restrictions to json ([#15840], [#16142], thanks @Vici37, @Sija)\n- _(serialization)_ Add note about default constructor in `*::Serializable` ([#16080], thanks @HertzDevil)\n- _(system)_ Add type restrictions to `args` parameter in `Process` ([#16031], thanks @BigBoyBarney)\n- _(system)_ Add missing `File::NotFoundError` exception documentation for `File.open` ([#15826], thanks @Fijxu)\n- _(text)_ Fix return type definition of `String#match_full!` method ([#16001], thanks @Sija)\n- _(time)_ Update links to ISO-8601 to point to an archived version of the page ([#15985], thanks @nobodywasishere)\n- _(time)_ Improve docs for `Time#to_local_in` ([#16041], thanks @straight-shoota)\n\n[#15688]: https://github.com/crystal-lang/crystal/pull/15688\n[#15910]: https://github.com/crystal-lang/crystal/pull/15910\n[#15931]: https://github.com/crystal-lang/crystal/pull/15931\n[#15694]: https://github.com/crystal-lang/crystal/pull/15694\n[#15834]: https://github.com/crystal-lang/crystal/pull/15834\n[#16073]: https://github.com/crystal-lang/crystal/pull/16073\n[#15698]: https://github.com/crystal-lang/crystal/pull/15698\n[#15989]: https://github.com/crystal-lang/crystal/pull/15989\n[#15777]: https://github.com/crystal-lang/crystal/pull/15777\n[#15687]: https://github.com/crystal-lang/crystal/pull/15687\n[#15710]: https://github.com/crystal-lang/crystal/pull/15710\n[#15689]: https://github.com/crystal-lang/crystal/pull/15689\n[#16035]: https://github.com/crystal-lang/crystal/pull/16035\n[#16196]: https://github.com/crystal-lang/crystal/pull/16196\n[#15840]: https://github.com/crystal-lang/crystal/pull/15840\n[#16142]: https://github.com/crystal-lang/crystal/pull/16142\n[#16080]: https://github.com/crystal-lang/crystal/pull/16080\n[#16031]: https://github.com/crystal-lang/crystal/pull/16031\n[#15826]: https://github.com/crystal-lang/crystal/pull/15826\n[#16001]: https://github.com/crystal-lang/crystal/pull/16001\n[#15985]: https://github.com/crystal-lang/crystal/pull/15985\n[#16041]: https://github.com/crystal-lang/crystal/pull/16041\n\n### Specs\n\n#### stdlib\n\n- _(networking)_ Disable UDP multicast spec on macOS ([#15990], thanks @straight-shoota)\n- _(networking)_ Extract specs for `HTTP::Cookies` to their own file ([#16106], thanks @straight-shoota)\n- _(networking)_ Overhaul `HTTP::Cookies` specs ([#16117], thanks @straight-shoota)\n- _(networking)_ Fix `UDPSocket` broadcast spec to not use `connect` ([#16165], thanks @ysbaddaden)\n- _(runtime)_ Disable flaky spec for `Fiber::ExecutionContext::GlobalQueue` on macOS ([#16146], thanks @ysbaddaden)\n- _(system)_ Enable specs for `close_on_exec` on Windows ([#14716], thanks @straight-shoota)\n\n[#15990]: https://github.com/crystal-lang/crystal/pull/15990\n[#16106]: https://github.com/crystal-lang/crystal/pull/16106\n[#16117]: https://github.com/crystal-lang/crystal/pull/16117\n[#16165]: https://github.com/crystal-lang/crystal/pull/16165\n[#16146]: https://github.com/crystal-lang/crystal/pull/16146\n[#14716]: https://github.com/crystal-lang/crystal/pull/14716\n\n#### compiler\n\n- Use `<<-CRYSTAL` in compiler specs consistently ([#16083], thanks @HertzDevil)\n- Style the remaining multi-line compiler specs using heredocs ([#16125], thanks @HertzDevil)\n- _(codegen)_ Style multi-line codegen specs using heredocs ([#16081], thanks @HertzDevil)\n- _(codegen)_ Style `test_c` codegen specs using heredocs ([#16082], thanks @HertzDevil)\n- _(semantic)_ Style multi-line `assert_type` specs using heredocs ([#16088], thanks @HertzDevil)\n- _(semantic)_ Style multi-line `assert_error` specs using heredocs ([#16093], thanks @HertzDevil)\n- _(semantic)_ Move `crystal_path_spec` fixtures to `spec/compiler/data` ([#16086], thanks @straight-shoota)\n\n[#16083]: https://github.com/crystal-lang/crystal/pull/16083\n[#16125]: https://github.com/crystal-lang/crystal/pull/16125\n[#16081]: https://github.com/crystal-lang/crystal/pull/16081\n[#16082]: https://github.com/crystal-lang/crystal/pull/16082\n[#16088]: https://github.com/crystal-lang/crystal/pull/16088\n[#16093]: https://github.com/crystal-lang/crystal/pull/16093\n[#16086]: https://github.com/crystal-lang/crystal/pull/16086\n\n#### tools\n\n- _(formatter)_ Add specs for `x.[](y)` syntax ([#16109], thanks @straight-shoota)\n\n[#16109]: https://github.com/crystal-lang/crystal/pull/16109\n\n### Infrastructure\n\n- Changelog for 1.18.0 ([#16153], thanks @straight-shoota)\n- Update previous Crystal release 1.17.0 ([#15988], thanks @straight-shoota)\n- Support debug builds for the MSVC Boehm GC libraries ([#15968], thanks @HertzDevil)\n- Fix funding.json well-known file name ([#16000], thanks @matiasgarciaisaia)\n- Lint the Bash auto-completion script ([#15993], thanks @HertzDevil)\n- Merge `release/1.17`@`1.17.1` into `master` ([#16017], thanks @straight-shoota)\n- Update previous Crystal release 1.17.1 ([#16016], thanks @straight-shoota)\n- Add new types to GitHub issue templates ([#15811], thanks @straight-shoota)\n- Disable ameba rule `Lint/Formatting` ([#16015], thanks @straight-shoota)\n- Add `REUSE.toml` ([#15992], thanks @straight-shoota)\n- Default to LLVM 16 in `shell.nix` ([#16023], thanks @ysbaddaden)\n- Avoid updating `forward-compatibility.yml` on release update for patch releases ([#16019], thanks @straight-shoota)\n- Disable `Lint/LiteralsComparison` in more spec files ([#16087], thanks @straight-shoota)\n- Fix typo in Makefile comment ([#16126], thanks @kojix2)\n- Fix duplicate `--error-trace` option in man page ([#16133], thanks @kojix2)\n- Makefile: Skip grisu3 float printer deprecations in `std_spec` ([#16185], thanks @ysbaddaden)\n- Fix changelog format for Markdown linter ([#16188], thanks @straight-shoota)\n- _(ci)_ Add `fail-fast: false` for strategy CI jobs ([#15960], thanks @straight-shoota)\n- _(ci)_ Add tests for latest OpenSSL and LibreSSL in Alpine edge ([#15812], thanks @straight-shoota)\n- _(ci)_ Use MSYS2 Crystal package for ARM64 Windows CI ([#15991], thanks @HertzDevil)\n- _(ci)_ Add macos-15 runner ([#15982], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#16067], thanks @renovate)\n- _(ci)_ Update GH Actions ([#16084], thanks @renovate)\n- _(ci)_ Update crate-ci/typos action to v1.35.5 ([#16102], thanks @renovate)\n- _(ci)_ Update GH Actions ([#16131], thanks @renovate)\n- _(ci)_ Update deprecated `macos-13` to `macos-15-intel` ([#16197], thanks @straight-shoota)\n- _(ci)_ Trigger LLVM CI when codegen files are changed ([#16116], thanks @HertzDevil)\n- _(ci)_ Do not use D drive on MSVC CI ([#15986], thanks @HertzDevil)\n\n[#16153]: https://github.com/crystal-lang/crystal/pull/16153\n[#15988]: https://github.com/crystal-lang/crystal/pull/15988\n[#15968]: https://github.com/crystal-lang/crystal/pull/15968\n[#16000]: https://github.com/crystal-lang/crystal/pull/16000\n[#15993]: https://github.com/crystal-lang/crystal/pull/15993\n[#16017]: https://github.com/crystal-lang/crystal/pull/16017\n[#16016]: https://github.com/crystal-lang/crystal/pull/16016\n[#15811]: https://github.com/crystal-lang/crystal/pull/15811\n[#16015]: https://github.com/crystal-lang/crystal/pull/16015\n[#15992]: https://github.com/crystal-lang/crystal/pull/15992\n[#16023]: https://github.com/crystal-lang/crystal/pull/16023\n[#16019]: https://github.com/crystal-lang/crystal/pull/16019\n[#16087]: https://github.com/crystal-lang/crystal/pull/16087\n[#16126]: https://github.com/crystal-lang/crystal/pull/16126\n[#16133]: https://github.com/crystal-lang/crystal/pull/16133\n[#16185]: https://github.com/crystal-lang/crystal/pull/16185\n[#16188]: https://github.com/crystal-lang/crystal/pull/16188\n[#15960]: https://github.com/crystal-lang/crystal/pull/15960\n[#15812]: https://github.com/crystal-lang/crystal/pull/15812\n[#15991]: https://github.com/crystal-lang/crystal/pull/15991\n[#15982]: https://github.com/crystal-lang/crystal/pull/15982\n[#16067]: https://github.com/crystal-lang/crystal/pull/16067\n[#16084]: https://github.com/crystal-lang/crystal/pull/16084\n[#16102]: https://github.com/crystal-lang/crystal/pull/16102\n[#16131]: https://github.com/crystal-lang/crystal/pull/16131\n[#16197]: https://github.com/crystal-lang/crystal/pull/16197\n[#16116]: https://github.com/crystal-lang/crystal/pull/16116\n[#15986]: https://github.com/crystal-lang/crystal/pull/15986\n"
  },
  {
    "path": "doc/changelogs/v1.19.md",
    "content": "# Changelog 1.19\n\n## [1.19.1] (2026-01-20)\n\n[1.19.1]: https://github.com/crystal-lang/crystal/releases/1.19.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(concurrency)_ **[regression]** Fix kqueue timer duration calculation ([#16581], thanks @skuznetsov)\n- _(concurrency)_ **[regression]** time calculations in IOCP and Monitor thread ([#16583], thanks @ysbaddaden)\n\n[#16581]: https://github.com/crystal-lang/crystal/pull/16581\n[#16583]: https://github.com/crystal-lang/crystal/pull/16583\n\n### Infrastructure\n\n- Changelog for 1.19.1 ([#16590], thanks @ysbaddaden)\n\n[#16590]: https://github.com/crystal-lang/crystal/pull/16589\n\n## [1.19.0] (2026-01-14)\n\n[1.19.0]: https://github.com/crystal-lang/crystal/releases/1.19.0\n\n### Breaking changes\n\n#### stdlib\n\n- _(crypto)_ Require OpenSSL 1.1.1+ or LibreSSL 3+ ([#16480], thanks @ysbaddaden)\n\n[#16480]: https://github.com/crystal-lang/crystal/pull/16480\n\n### Features\n\n#### lang\n\n- _(macros)_ **[breaking]** Add compiler flag values ([#16310], thanks @straight-shoota)\n- _(macros)_ Add yielding variant of `StringLiteral#gsub` ([#16378], thanks @Blacksmoke16)\n- _(macros)_ Support `StringLiteral#split(RegexLiteral)` ([#16423], thanks @HertzDevil)\n- _(macros)_ Add `StringLiteral#match` ([#16464], thanks @HertzDevil)\n- _(macros)_ Make all overloads of `ArrayLiteral#[]` return `nil` on out of bounds ([#16453], thanks @HertzDevil)\n\n[#16310]: https://github.com/crystal-lang/crystal/pull/16310\n[#16378]: https://github.com/crystal-lang/crystal/pull/16378\n[#16423]: https://github.com/crystal-lang/crystal/pull/16423\n[#16464]: https://github.com/crystal-lang/crystal/pull/16464\n[#16453]: https://github.com/crystal-lang/crystal/pull/16453\n\n#### stdlib\n\n- _(collection)_ Add `NamedTuple#reverse_merge` ([#16229], thanks @andrykonchin)\n- _(collection)_ Pad `Hash#inspect`, `Tuple#inspect` before `{` from first element ([#16245], thanks @andrykonchin)\n- _(collection)_ Add `Set#map!` ([#16271], thanks @andrykonchin)\n- _(collection)_ Add `Hash#transform_keys!` ([#16280], thanks @andrykonchin)\n- _(collection)_ Enhance error message for `Hash#[]` when key is wrong type for default block ([#16442], thanks @Blacksmoke16)\n- _(concurrency)_ Add `Sync::Mutex` and `Sync::RWLock` ([#16399], thanks @ysbaddaden)\n- _(concurrency)_ Add `Sync::ConditionVariable` ([#16440], thanks @ysbaddaden)\n- _(concurrency)_ Import `Sync::Exclusive` and `Sync::Shared` ([#16487], thanks @ysbaddaden)\n- _(crypto)_ Add `OpenSSL::SSL::Context::Server#on_server_name` for SNI ([#16452], [#16525], thanks @carlhoerberg, @straight-shoota)\n- _(networking)_ Loosen type restrictions in `StaticFileHandler` helper methods from `File` to `IO` ([#16238], thanks @andrykonchin)\n- _(networking)_ Add `IPSocket#ipv6_only` ([#16347], thanks @stakach)\n- _(networking)_ Expose `flags` hint for `getaddrinfo` ([#16528], thanks @stakach)\n- _(numeric)_ Add `Int.from_digits` as inverse of `Int#digits` ([#16237], [#16566], thanks @andrykonchin, @ysbaddaden)\n- _(numeric)_ Add `BigInt.from_digits` ([#16259], thanks @HertzDevil)\n- _(numeric)_ Add `Int#tdivmod` ([#16258], thanks @andrykonchin)\n- _(runtime)_ Add `Proc#[]` as alias to `#call` ([#16220], thanks @andrykonchin)\n- _(runtime)_ Add `#unshift`, `#pop` and `#pop?` to `Crystal::PointerLinkedList` ([#16287], thanks @ysbaddaden)\n- _(runtime)_ Add `Random.next_bool` and `.next_int` ([#16297], thanks @ysbaddaden)\n- _(runtime)_ Add `Random#split` and `#split_internal` API for splittable PRNGs ([#16342], [#16495], thanks @ysbaddaden)\n- _(runtime)_ Add `Pointer#fill` ([#16338], thanks @straight-shoota)\n- _(runtime)_ Add `Crystal::PointerLinkedList#first?` ([#16400], thanks @ysbaddaden)\n- _(runtime)_ Ensure single reader and writer to system fd on Unix ([#16209], thanks @ysbaddaden)\n- _(runtime)_ Protect `Box.unbox` from dereferencing null pointer ([#16514], thanks @straight-shoota)\n- _(runtime)_ Register execution context schedulers with the event loop ([#16519], thanks @ysbaddaden)\n- _(runtime)_ Add `Fiber::ExecutionContext::Scheduler.current?` ([#16521], thanks @ysbaddaden)\n- _(runtime)_ Move execution context event loop lock to each event loop ([#16520], thanks @ysbaddaden)\n- _(serialization)_ Support deserialization of YAML anchors of value types ([#16186], thanks @HertzDevil)\n- _(serialization)_ Add end locations to scalars and aliases in `YAML::Nodes.parse` ([#16187], thanks @HertzDevil)\n- _(serialization)_ Set `JSON::SerializableError#attribute` when appropriate ([#16158], thanks @spuun)\n- _(serialization)_ Support large JSON files ([#16211], thanks @RX14)\n- _(serialization)_ Add `YAML::Nodes.parse_all` ([#16247], thanks @HertzDevil)\n- _(specs)_ Rescale execution context in spec runner with `CRYSTAL_WORKERS` ([#16444], [#16471], thanks @straight-shoota, @ysbaddaden)\n- _(system)_ Add `Process.debugger_present?` for Windows and Linux ([#16248], thanks @HertzDevil)\n- _(system)_ Implement `execvpe_impl` ([#16322], [#16344], thanks @straight-shoota)\n- _(system)_ Add `::exit(Process::Status)` ([#16436], thanks @straight-shoota)\n- _(system)_ Standardize system error codes for `File::Error` ([#16024], thanks @straight-shoota)\n- _(system)_ Add `Path#relative?` ([#16473], thanks @Sija)\n- _(text)_ PCRE2: use thread local for jit stack and match data ([#16175], thanks @ysbaddaden)\n- _(text)_ Support 0X, 0O, 0B prefixes in string to integer conversion ([#16226], thanks @andrykonchin)\n- _(text)_ Add `String#each_line` parameter `remove_empty` ([#16232], thanks @andrykonchin)\n- _(time)_ Add `weeks` parameter to `Time::Span.new` ([#16208], thanks @Sija)\n- _(time)_ Treat GMT as a legacy alias of UTC ([#16292], thanks @straight-shoota)\n- _(time)_ Add `/etc/zoneinfo` to zoneinfo lookup paths ([#16463], thanks @straight-shoota)\n- _(time)_ Add support for `$TZDIR` ([#16466], thanks @straight-shoota)\n- _(time)_ Add `Time::Instant` ([#16490], thanks @straight-shoota)\n- _(time)_ **[breaking]** Adjust monotonic clocks to include suspended time with precision ([#16516], thanks @straight-shoota)\n\n[#16229]: https://github.com/crystal-lang/crystal/pull/16229\n[#16245]: https://github.com/crystal-lang/crystal/pull/16245\n[#16271]: https://github.com/crystal-lang/crystal/pull/16271\n[#16280]: https://github.com/crystal-lang/crystal/pull/16280\n[#16442]: https://github.com/crystal-lang/crystal/pull/16442\n[#16399]: https://github.com/crystal-lang/crystal/pull/16399\n[#16440]: https://github.com/crystal-lang/crystal/pull/16440\n[#16487]: https://github.com/crystal-lang/crystal/pull/16487\n[#16452]: https://github.com/crystal-lang/crystal/pull/16452\n[#16525]: https://github.com/crystal-lang/crystal/pull/16525\n[#16238]: https://github.com/crystal-lang/crystal/pull/16238\n[#16347]: https://github.com/crystal-lang/crystal/pull/16347\n[#16528]: https://github.com/crystal-lang/crystal/pull/16528\n[#16237]: https://github.com/crystal-lang/crystal/pull/16237\n[#16566]: https://github.com/crystal-lang/crystal/pull/16566\n[#16259]: https://github.com/crystal-lang/crystal/pull/16259\n[#16258]: https://github.com/crystal-lang/crystal/pull/16258\n[#16220]: https://github.com/crystal-lang/crystal/pull/16220\n[#16287]: https://github.com/crystal-lang/crystal/pull/16287\n[#16297]: https://github.com/crystal-lang/crystal/pull/16297\n[#16342]: https://github.com/crystal-lang/crystal/pull/16342\n[#16495]: https://github.com/crystal-lang/crystal/pull/16495\n[#16338]: https://github.com/crystal-lang/crystal/pull/16338\n[#16400]: https://github.com/crystal-lang/crystal/pull/16400\n[#16209]: https://github.com/crystal-lang/crystal/pull/16209\n[#16514]: https://github.com/crystal-lang/crystal/pull/16514\n[#16519]: https://github.com/crystal-lang/crystal/pull/16519\n[#16521]: https://github.com/crystal-lang/crystal/pull/16521\n[#16520]: https://github.com/crystal-lang/crystal/pull/16520\n[#16186]: https://github.com/crystal-lang/crystal/pull/16186\n[#16187]: https://github.com/crystal-lang/crystal/pull/16187\n[#16158]: https://github.com/crystal-lang/crystal/pull/16158\n[#16211]: https://github.com/crystal-lang/crystal/pull/16211\n[#16247]: https://github.com/crystal-lang/crystal/pull/16247\n[#16444]: https://github.com/crystal-lang/crystal/pull/16444\n[#16471]: https://github.com/crystal-lang/crystal/pull/16471\n[#16248]: https://github.com/crystal-lang/crystal/pull/16248\n[#16322]: https://github.com/crystal-lang/crystal/pull/16322\n[#16344]: https://github.com/crystal-lang/crystal/pull/16344\n[#16436]: https://github.com/crystal-lang/crystal/pull/16436\n[#16024]: https://github.com/crystal-lang/crystal/pull/16024\n[#16473]: https://github.com/crystal-lang/crystal/pull/16473\n[#16175]: https://github.com/crystal-lang/crystal/pull/16175\n[#16226]: https://github.com/crystal-lang/crystal/pull/16226\n[#16232]: https://github.com/crystal-lang/crystal/pull/16232\n[#16208]: https://github.com/crystal-lang/crystal/pull/16208\n[#16292]: https://github.com/crystal-lang/crystal/pull/16292\n[#16463]: https://github.com/crystal-lang/crystal/pull/16463\n[#16466]: https://github.com/crystal-lang/crystal/pull/16466\n[#16490]: https://github.com/crystal-lang/crystal/pull/16490\n[#16516]: https://github.com/crystal-lang/crystal/pull/16516\n\n#### compiler\n\n- _(codegen)_ Build compiler with `-Dexecution_context` ([#16447], [#16502], thanks @ysbaddaden, @straight-shoota)\n- _(interpreter)_ Support `->LibX.fun_name` in the interpreter ([#16194], thanks @ysbaddaden)\n- _(semantic)_ Add error message to `CrystalPath::NotFoundError` ([#16365], thanks @willhbr)\n- _(semantic)_ Retain original location for errors in `included`, `extended` hooks ([#13261], thanks @Blacksmoke16)\n\n[#16447]: https://github.com/crystal-lang/crystal/pull/16447\n[#16502]: https://github.com/crystal-lang/crystal/pull/16502\n[#16194]: https://github.com/crystal-lang/crystal/pull/16194\n[#16365]: https://github.com/crystal-lang/crystal/pull/16365\n[#13261]: https://github.com/crystal-lang/crystal/pull/13261\n\n#### tools\n\n- _(docs-generator)_ Add optional sanitizer to docs generator ([#14646], [#16251], thanks @nobodywasishere, @straight-shoota)\n\n[#14646]: https://github.com/crystal-lang/crystal/pull/14646\n[#16251]: https://github.com/crystal-lang/crystal/pull/16251\n\n### Bugfixes\n\n#### lang\n\n- _(macros)_ Fix nested sigil delimiter parsing inside macros ([#16266], thanks @HertzDevil)\n\n[#16266]: https://github.com/crystal-lang/crystal/pull/16266\n\n#### stdlib\n\n- Fix `OptionParser` subcommand help to respect custom `summary_indent` ([#16334], thanks @kojix2)\n- _(collection)_ Fix `Hash` methods to retain `compare_by_identity` flag ([#16356], thanks @andrykonchin)\n- _(collection)_ Fix Hash methods and retaining default value ([#16374], thanks @andrykonchin)\n- _(files)_ Fix condition for no-op `lock_write` to work without sockets ([#16304], thanks @straight-shoota)\n- _(networking)_ Fix `HTTP::Cookie` parsing trailing semicolons ([#16328], thanks @alexkutsan)\n- _(networking)_ **[breaking]** Make `#flush` in `WebSocket#stream` a no-op to not send wrongly frames ([#16539], thanks @spuun)\n- _(runtime)_ **[deprecation]** Add thread safety to default random ([#16174], [#16568], thanks @ysbaddaden)\n- _(runtime)_ default execution context is `Parallel` ([#16367], thanks @ysbaddaden)\n- _(runtime)_ `Crystal::PointerLinkedList#each` stops iterating when deleting head ([#16401], thanks @ysbaddaden)\n- _(runtime)_ closing system fd is thread unsafe ([#16289], thanks @ysbaddaden)\n- _(runtime)_ `Crystal::System::Process#rwlock` with Crystal < 1.7 (UNIX) ([#16482], thanks @ysbaddaden)\n- _(runtime)_ urandom initialization isn't thread safe + refactor ([#16479], thanks @ysbaddaden)\n- _(runtime)_ execution context queue stress tests failures ([#16472], thanks @ysbaddaden)\n- _(runtime)_ don't use `Time.monotonic` in `Fiber::ExecutionContext::Monitor` ([#16500], thanks @ysbaddaden)\n- _(runtime)_ thread safety of `Exception::Callstack` ([#16504], thanks @ysbaddaden)\n- _(runtime)_ actually clear memory using gc_none on unix ([#16562], thanks @BlobCodes)\n- _(serialization)_ memory leak in `XML.parse` and `XML.parse_html` methods ([#16414], thanks @ysbaddaden)\n- _(serialization)_ memory leak in `XML::Document#finalize` ([#16418], thanks @toddsundsted)\n- _(serialization)_ memory leak in `XML::Node#content=` ([#16419], thanks @toddsundsted)\n- _(serialization)_ Fix use after unlink in `XML::Node` ([#16432], thanks @toddsundsted)\n- _(specs)_ Resolve inconsistent use of `#inspect` in `expect_raises` ([#16265], [#16375], thanks @andrykonchin, @straight-shoota)\n- _(system)_ Create `argv` before `fork` ([#16286], [#16321], thanks @straight-shoota)\n- _(system)_ Pass `envp` to `execvpe` ([#16340], thanks @straight-shoota)\n- _(system)_ Move `make_envp` before `fork` ([#16351], thanks @straight-shoota)\n- _(system)_ Replace `Dir.cd` with a non-raising alternative in pre-exec ([#16352], [#16369], thanks @straight-shoota)\n- _(system)_ Fix reset directory if `Process.exec` fails ([#16383], thanks @straight-shoota)\n- _(system)_ Fix reorder `Process.lock_write` outside of `.block_signals` ([#16465], thanks @straight-shoota)\n- _(system)_ Disable process cancellation during `fork` ([#16446], thanks @straight-shoota)\n\n[#16334]: https://github.com/crystal-lang/crystal/pull/16334\n[#16356]: https://github.com/crystal-lang/crystal/pull/16356\n[#16374]: https://github.com/crystal-lang/crystal/pull/16374\n[#16304]: https://github.com/crystal-lang/crystal/pull/16304\n[#16328]: https://github.com/crystal-lang/crystal/pull/16328\n[#16539]: https://github.com/crystal-lang/crystal/pull/16539\n[#16174]: https://github.com/crystal-lang/crystal/pull/16174\n[#16568]: https://github.com/crystal-lang/crystal/pull/16568\n[#16367]: https://github.com/crystal-lang/crystal/pull/16367\n[#16401]: https://github.com/crystal-lang/crystal/pull/16401\n[#16289]: https://github.com/crystal-lang/crystal/pull/16289\n[#16482]: https://github.com/crystal-lang/crystal/pull/16482\n[#16479]: https://github.com/crystal-lang/crystal/pull/16479\n[#16472]: https://github.com/crystal-lang/crystal/pull/16472\n[#16500]: https://github.com/crystal-lang/crystal/pull/16500\n[#16504]: https://github.com/crystal-lang/crystal/pull/16504\n[#16562]: https://github.com/crystal-lang/crystal/pull/16562\n[#16414]: https://github.com/crystal-lang/crystal/pull/16414\n[#16418]: https://github.com/crystal-lang/crystal/pull/16418\n[#16419]: https://github.com/crystal-lang/crystal/pull/16419\n[#16432]: https://github.com/crystal-lang/crystal/pull/16432\n[#16265]: https://github.com/crystal-lang/crystal/pull/16265\n[#16375]: https://github.com/crystal-lang/crystal/pull/16375\n[#16286]: https://github.com/crystal-lang/crystal/pull/16286\n[#16321]: https://github.com/crystal-lang/crystal/pull/16321\n[#16340]: https://github.com/crystal-lang/crystal/pull/16340\n[#16351]: https://github.com/crystal-lang/crystal/pull/16351\n[#16352]: https://github.com/crystal-lang/crystal/pull/16352\n[#16369]: https://github.com/crystal-lang/crystal/pull/16369\n[#16383]: https://github.com/crystal-lang/crystal/pull/16383\n[#16465]: https://github.com/crystal-lang/crystal/pull/16465\n[#16446]: https://github.com/crystal-lang/crystal/pull/16446\n\n#### compiler\n\n- _(cli)_ chore: correct progress step count to 14 ([#16269], thanks @miry)\n- _(codegen)_ Fix System V ABI for arrays of packed structs with misaligned fields ([#16314], thanks @HertzDevil)\n- _(debugger)_ Fix debug info for closured variables ([#16393], thanks @HertzDevil)\n- _(interpreter)_ interpreter handles `self` in inlined method with arguments ([#16307], thanks @cyangle)\n- _(interpreter)_ interpreter `typeof` should return concrete type ([#16379], thanks @cyangle)\n- _(interpreter)_ Fix variable shadowing bug in interpreter ([#16335], thanks @cyangle)\n- _(interpreter)_ interpreter musn't reuse dead fiber stacks ([#16518], thanks @ysbaddaden)\n- _(parser)_ Fix internal error if multi-assign RHS has splats ([#16182], thanks @HertzDevil)\n- _(parser)_ Fix regex delimiter detection in syntax highlighter ([#16394], thanks @HertzDevil)\n- _(parser)_ Merge adjacent StringLiterals before yielding ([#16427], thanks @Blacksmoke16)\n- _(parser)_ Fix `Call#end_location` w/ named arguments off-by-one error ([#16542], thanks @Sija)\n- _(parser)_ Fix incorrect location for parenthesized union AST nodes ([#16552], thanks @Sija)\n- _(semantic)_ Fix instantiation of abstract generic structs in virtual type lookup ([#16513], thanks @Blacksmoke16)\n- _(semantic)_ Fix variables assigned inside `&&` conditions with method calls incorrectly got `Nil` added to their type ([#16512], thanks @Blacksmoke16)\n\n[#16269]: https://github.com/crystal-lang/crystal/pull/16269\n[#16314]: https://github.com/crystal-lang/crystal/pull/16314\n[#16393]: https://github.com/crystal-lang/crystal/pull/16393\n[#16307]: https://github.com/crystal-lang/crystal/pull/16307\n[#16379]: https://github.com/crystal-lang/crystal/pull/16379\n[#16335]: https://github.com/crystal-lang/crystal/pull/16335\n[#16518]: https://github.com/crystal-lang/crystal/pull/16518\n[#16182]: https://github.com/crystal-lang/crystal/pull/16182\n[#16394]: https://github.com/crystal-lang/crystal/pull/16394\n[#16427]: https://github.com/crystal-lang/crystal/pull/16427\n[#16542]: https://github.com/crystal-lang/crystal/pull/16542\n[#16552]: https://github.com/crystal-lang/crystal/pull/16552\n[#16513]: https://github.com/crystal-lang/crystal/pull/16513\n[#16512]: https://github.com/crystal-lang/crystal/pull/16512\n\n#### tools\n\n- _(docs-generator)_ Fix doc generation when nesting multiple `:inherit:` directives ([#16443], thanks @Blacksmoke16)\n- _(docs-generator)_ Fix some doc inconsistencies for macros ([#16561], thanks @Blacksmoke16)\n- _(formatter)_ Fix incorrect formatting of multi-line macro expression with comment as first line ([#16429], thanks @Blacksmoke16)\n- _(formatter)_ Add multi-line formatting support to `Generic` formatter visitor ([#16430], thanks @Blacksmoke16)\n\n[#16443]: https://github.com/crystal-lang/crystal/pull/16443\n[#16561]: https://github.com/crystal-lang/crystal/pull/16561\n[#16429]: https://github.com/crystal-lang/crystal/pull/16429\n[#16430]: https://github.com/crystal-lang/crystal/pull/16430\n\n### Chores\n\n#### lang\n\n- _(macros)_ **[deprecation]** Deprecate single-letter macro fresh variables with indices ([#16267], thanks @HertzDevil)\n- _(macros)_ **[deprecation]** Deprecate macro fresh variables with constant names ([#16293], thanks @HertzDevil)\n\n[#16267]: https://github.com/crystal-lang/crystal/pull/16267\n[#16293]: https://github.com/crystal-lang/crystal/pull/16293\n\n#### stdlib\n\n- _(macros)_ **[deprecation]** Deprecate `StringLiteral#split(ASTNode)` for non-separator arguments ([#16439], thanks @HertzDevil)\n- _(time)_ **[deprecation]** Deprecate `Time#inspect(io, *, with_nanoseconds)` ([#16416], thanks @straight-shoota)\n- _(time)_ **[deprecation]** Deprecate `Time.monotonic` ([#16545], thanks @straight-shoota)\n\n[#16439]: https://github.com/crystal-lang/crystal/pull/16439\n[#16416]: https://github.com/crystal-lang/crystal/pull/16416\n[#16545]: https://github.com/crystal-lang/crystal/pull/16545\n\n#### compiler\n\n- _(cli)_ Error when trying to build aarch64 with LLVM 12 and below ([#15018], thanks @straight-shoota)\n\n[#15018]: https://github.com/crystal-lang/crystal/pull/15018\n\n#### other\n\n- Update copyright year ([#16550], thanks @HertzDevil)\n- Remove redundant `begin`/`end` blocks ([#16554], thanks @straight-shoota)\n\n[#16550]: https://github.com/crystal-lang/crystal/pull/16550\n[#16554]: https://github.com/crystal-lang/crystal/pull/16554\n\n### Performance\n\n#### stdlib\n\n- Avoid calling `times.map` ([#16422], thanks @HertzDevil)\n- _(runtime)_ Skip initialization of `Pointer.malloc` with zero value ([#16333], thanks @straight-shoota)\n- _(runtime)_ Call `Pointer.malloc(size, value)` in `Slice.new(size, value)` ([#16358], thanks @straight-shoota)\n\n[#16422]: https://github.com/crystal-lang/crystal/pull/16422\n[#16333]: https://github.com/crystal-lang/crystal/pull/16333\n[#16358]: https://github.com/crystal-lang/crystal/pull/16358\n\n#### compiler\n\n- Group temporary variables by file name: splats ([#16242], thanks @HertzDevil)\n- _(codegen)_ **[regression]** Only define the type name table in the main LLVM module ([#16260], thanks @HertzDevil)\n- _(codegen)_ Allow closures to use atomic allocation ([#16360], thanks @HertzDevil)\n\n[#16242]: https://github.com/crystal-lang/crystal/pull/16242\n[#16260]: https://github.com/crystal-lang/crystal/pull/16260\n[#16360]: https://github.com/crystal-lang/crystal/pull/16360\n\n### Refactor\n\n#### stdlib\n\n- Refactor flag and value parsing into a separate method ([#16300], thanks @straight-shoota)\n- _(cli)_ Refactor `OptionParser#parse` ([#16233], thanks @kojix2)\n- _(cli)_ Simplify `OptionParser#handle_flag` with guard clauses ([#16309], thanks @kojix2)\n- _(files)_ Fix: don't flush twice in `File#truncate` (UNIX) ([#16395], thanks @ysbaddaden)\n- _(llvm)_ simplify target initialization and support more targets ([#16437], thanks @ysbaddaden)\n- _(log)_ `Log::Metadata` should put parent entries first on extend (like `Hash#merge`) ([#16098], thanks @spuun)\n- _(networking)_ Split `HTTP::Headers#get(Key)` into undocumented overload ([#16283], thanks @straight-shoota)\n- _(networking)_ Remove internal type `OAuth::Params` ([#16319], thanks @AnandRaj2224)\n- _(runtime)_ Refactor `Crystal::DWARF::LineNumbers::Sequence` ([#16214], thanks @HertzDevil)\n- _(runtime)_ Extract `Crystal::EventLoop#shutdown` from `#close` ([#16288], [#16366], thanks @ysbaddaden)\n- _(runtime)_ Prefer `Random::Secure.random_bytes` ([#16298], thanks @ysbaddaden)\n- _(runtime)_ Set default `random` arg to `nil` instead of `Random::DEFAULT` ([#16299], thanks @ysbaddaden)\n- _(runtime)_ Drop `EventLoop#after_fork_before_exec` ([#16332], thanks @straight-shoota)\n- _(runtime)_ Cleanup node on `Crystal::PointerLinkedList#delete` ([#16398], thanks @ysbaddaden)\n- _(runtime)_ Add `Fiber::Stack#size` ([#16420], thanks @ysbaddaden)\n- _(runtime)_ Fix: `new_thread` spec helper must return isolated context (not thread) ([#16421], thanks @ysbaddaden)\n- _(runtime)_ Fix: always use getrandom on Linux and Android >= 28 ([#16478], thanks @ysbaddaden)\n- _(system)_ Extract `Crystal::System::Env.each_pointer` on Unix ([#16200], thanks @straight-shoota)\n- _(system)_ Refactor internal `Crystal::System::Process#fork` on UNIX ([#16191], [#16373], thanks @ysbaddaden, @straight-shoota)\n- _(system)_ Use `execvpe` when available ([#16294], [#16311], thanks @straight-shoota)\n- _(system)_ Add `Env.make_envp` ([#16320], [#16384], thanks @straight-shoota)\n- _(system)_ Fix pre-exec for closed file descriptor ([#16359], thanks @straight-shoota)\n- _(system)_ Move `prepare_args` into system implementation internals ([#16362], thanks @straight-shoota)\n- _(system)_ Extract `unix/spawn.cr` as a separate file ([#16388], thanks @straight-shoota)\n- _(system)_ Extract internal `Process.block_signals` helper ([#16402], thanks @straight-shoota)\n- _(system)_ Rename target `aarch64-android` to `aarch64-linux-android` ([#16409], thanks @straight-shoota)\n- _(text)_ Simplify `String#byte_slice(Int)` and `String#byte_slice?(Int)` ([#16235], thanks @andrykonchin)\n- _(time)_ Use `clock_gettime` on darwin ([#16492], thanks @straight-shoota)\n- _(time)_ Add `Crystal::System::Time.instant` ([#16506], thanks @straight-shoota)\n- _(time)_ Replace `Time.monotonic` with `Time.instant` [follow-up #16490] ([#16498], thanks @straight-shoota)\n- _(time)_ remove extraneous method definition for `Time::Span#sign` ([#16553], thanks @plambert)\n\n[#16300]: https://github.com/crystal-lang/crystal/pull/16300\n[#16233]: https://github.com/crystal-lang/crystal/pull/16233\n[#16309]: https://github.com/crystal-lang/crystal/pull/16309\n[#16395]: https://github.com/crystal-lang/crystal/pull/16395\n[#16437]: https://github.com/crystal-lang/crystal/pull/16437\n[#16098]: https://github.com/crystal-lang/crystal/pull/16098\n[#16283]: https://github.com/crystal-lang/crystal/pull/16283\n[#16319]: https://github.com/crystal-lang/crystal/pull/16319\n[#16214]: https://github.com/crystal-lang/crystal/pull/16214\n[#16288]: https://github.com/crystal-lang/crystal/pull/16288\n[#16366]: https://github.com/crystal-lang/crystal/pull/16366\n[#16298]: https://github.com/crystal-lang/crystal/pull/16298\n[#16299]: https://github.com/crystal-lang/crystal/pull/16299\n[#16332]: https://github.com/crystal-lang/crystal/pull/16332\n[#16398]: https://github.com/crystal-lang/crystal/pull/16398\n[#16420]: https://github.com/crystal-lang/crystal/pull/16420\n[#16421]: https://github.com/crystal-lang/crystal/pull/16421\n[#16478]: https://github.com/crystal-lang/crystal/pull/16478\n[#16200]: https://github.com/crystal-lang/crystal/pull/16200\n[#16191]: https://github.com/crystal-lang/crystal/pull/16191\n[#16373]: https://github.com/crystal-lang/crystal/pull/16373\n[#16294]: https://github.com/crystal-lang/crystal/pull/16294\n[#16311]: https://github.com/crystal-lang/crystal/pull/16311\n[#16320]: https://github.com/crystal-lang/crystal/pull/16320\n[#16384]: https://github.com/crystal-lang/crystal/pull/16384\n[#16359]: https://github.com/crystal-lang/crystal/pull/16359\n[#16362]: https://github.com/crystal-lang/crystal/pull/16362\n[#16388]: https://github.com/crystal-lang/crystal/pull/16388\n[#16402]: https://github.com/crystal-lang/crystal/pull/16402\n[#16409]: https://github.com/crystal-lang/crystal/pull/16409\n[#16235]: https://github.com/crystal-lang/crystal/pull/16235\n[#16492]: https://github.com/crystal-lang/crystal/pull/16492\n[#16506]: https://github.com/crystal-lang/crystal/pull/16506\n[#16498]: https://github.com/crystal-lang/crystal/pull/16498\n[#16553]: https://github.com/crystal-lang/crystal/pull/16553\n\n### Documentation\n\n#### lang\n\n- _(annotations)_ Fix `@[Deprecated]` doc comment ([#16302], thanks @jgaskins)\n\n[#16302]: https://github.com/crystal-lang/crystal/pull/16302\n\n#### stdlib\n\n- _(collection)_ Clarify `Set`'s enumeration order ([#16274], thanks @HertzDevil)\n- _(concurrency)_ Add docs for `Sync` namespace ([#16565], thanks @ysbaddaden)\n- _(crypto)_ Remove outdated performance hint in `Bcrypt` docs ([#16536], thanks @BlobCodes)\n- _(macros)_ Fix invalid runtime types in macro docs ([#16534], thanks @BlobCodes)\n- _(networking)_ Add type restrictions to `OAuth::Consumer#get_authorize_uri` ([#16285], thanks @straight-shoota)\n- _(numeric)_ Improve docs for `Int` to mention `Int128` and `UInt128` ([#16529], thanks @HCLarsen)\n- _(runtime)_ Use `to_slice` for presentation in `Pointer` doc examples ([#16345], thanks @straight-shoota)\n- _(system)_ Add type restrictions to process ([#16065], thanks @Vici37)\n- _(text)_ Document `String#split(Regex)`'s capture group behavior ([#16207], thanks @HertzDevil)\n- _(text)_ Add type restrictions to regex directory ([#16066], thanks @Vici37)\n\n[#16274]: https://github.com/crystal-lang/crystal/pull/16274\n[#16565]: https://github.com/crystal-lang/crystal/pull/16565\n[#16536]: https://github.com/crystal-lang/crystal/pull/16536\n[#16534]: https://github.com/crystal-lang/crystal/pull/16534\n[#16285]: https://github.com/crystal-lang/crystal/pull/16285\n[#16529]: https://github.com/crystal-lang/crystal/pull/16529\n[#16345]: https://github.com/crystal-lang/crystal/pull/16345\n[#16065]: https://github.com/crystal-lang/crystal/pull/16065\n[#16207]: https://github.com/crystal-lang/crystal/pull/16207\n[#16066]: https://github.com/crystal-lang/crystal/pull/16066\n\n### Specs\n\n#### lang\n\n- _(macros)_ Enhance specs for `flag?` macro ([#16336], thanks @straight-shoota)\n\n[#16336]: https://github.com/crystal-lang/crystal/pull/16336\n\n#### stdlib\n\n- _(collection)_ Add specs for `Slice.new` ([#16424], thanks @straight-shoota)\n- _(concurrency)_ Fix thread name expectation with parallel execution context ([#16517], thanks @straight-shoota)\n- _(crypto)_ Fix: remove 1 second sleep in openssl/ssl/server spec ([#16454], thanks @ysbaddaden)\n- _(files)_ Add specs for `IO#read_bytes` with converter ([#16250], thanks @straight-shoota)\n- _(networking)_ Fix TCP specs to accept `EAI_NODATA` instead of `EAI_NONAME` for unresolvable hostname ([#16496], thanks @straight-shoota)\n- _(system)_ Add specs for `Process.run` ([#16306], [#16325], thanks @straight-shoota)\n- _(time)_ Update zoneinfo to TZDB version 2025c ([#16501], thanks @straight-shoota)\n\n[#16424]: https://github.com/crystal-lang/crystal/pull/16424\n[#16517]: https://github.com/crystal-lang/crystal/pull/16517\n[#16454]: https://github.com/crystal-lang/crystal/pull/16454\n[#16250]: https://github.com/crystal-lang/crystal/pull/16250\n[#16496]: https://github.com/crystal-lang/crystal/pull/16496\n[#16306]: https://github.com/crystal-lang/crystal/pull/16306\n[#16325]: https://github.com/crystal-lang/crystal/pull/16325\n[#16501]: https://github.com/crystal-lang/crystal/pull/16501\n\n#### compiler\n\n- _(semantic)_ Drop `assert_expand_second` and `assert_expand_third` helpers ([#16244], thanks @HertzDevil)\n\n[#16244]: https://github.com/crystal-lang/crystal/pull/16244\n\n### Infrastructure\n\n- Changelog for 1.19.0 ([#16510], thanks @ysbaddaden)\n- Update previous Crystal release 1.18.1 ([#16212], thanks @matiasgarciaisaia)\n- Fix shellcheck violations ([#16221], thanks @straight-shoota)\n- Fix markdownlint violations ([#16222], [#16252], thanks @straight-shoota)\n- Merge `release/1.18`@`1.18.2` into `master` ([#16246], thanks @straight-shoota)\n- Enable ameba rule `Lint/SpecFilename` ([#16223], thanks @straight-shoota)\n- Encourage +1 reactions on issues and PRs for prioritization ([#16241], thanks @straight-shoota)\n- Update previous Crystal release 1.18.2 ([#16249], thanks @straight-shoota)\n- Add `devenv` ([#16263], thanks @straight-shoota)\n- Add `ameba` to `git-hooks` ([#16276], [#16295], thanks @straight-shoota)\n- Add `devenv` profile `lint` ([#16291], thanks @straight-shoota)\n- Update distribution-scripts ([#16301], thanks @straight-shoota)\n- Makefile: Extract variable `COMPILER_FLAGS` ([#16349], [#16372], thanks @straight-shoota)\n- Fix `shell.nix` on Linux ([#16346], thanks @straight-shoota)\n- Build compiler with `-Dpreview_mt` ([#16380], thanks @straight-shoota)\n- Update `devenv.lock` ([#16386], thanks @github-actions)\n- Update `devenv.lock` ([#16408], thanks @github-actions)\n- Drop committed `.envrc` ([#16462], thanks @straight-shoota)\n- Add git-hook to ensure changing both `Makefile` and `Makefile.win` at the same time ([#16503], thanks @straight-shoota)\n- Makefile: Use simply expanded variables to avoid costly duplicate evaluation ([#16509], thanks @straight-shoota)\n- Fix `scripts/update-shards.sh` ([#16524], thanks @straight-shoota)\n- Update distribution-scripts ([#16530], thanks @straight-shoota)\n- Update shards 0.20.0 ([#16523], thanks @straight-shoota)\n- Update typos 1.38.1 ([#16219], thanks @straight-shoota)\n- Build snap arm64 target + drop publish_snap target ([#16491], thanks @ysbaddaden)\n- _(ci)_ Update darwin jobs in circleci to `m4pro.medium` resource class ([#16389], thanks @straight-shoota)\n- _(ci)_ Update xcode to 26.0.1 on circleci ([#16201], thanks @straight-shoota)\n- _(ci)_ Update korthout/backport-action action to v3.4.1 ([#16215], thanks @renovate)\n- _(ci)_ **[security]** Pin GitHub action uses to commit hash ([#16253], thanks @straight-shoota)\n- _(ci)_ Add lint workflow running `pre-commit` ([#16275], [#16296], thanks @straight-shoota)\n- _(ci)_ Fix issues in GHA workflows ([#16282], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#16290], thanks @renovate)\n- _(ci)_ Update crate-ci/typos action to v1.39.0 ([#16326], thanks @renovate)\n- _(ci)_ Refactor matrix configuration in Linux workflow ([#16331], thanks @straight-shoota)\n- _(ci)_ Reduce smoke tests to building only `std_spec` ([#16337], thanks @straight-shoota)\n- _(ci)_ Merge gnu and musl tests into a single matrix ([#16341], thanks @straight-shoota)\n- _(ci)_ Fix `pull_request` trigger for `smoke` workflow ([#16343], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#16385], thanks @renovate)\n- _(ci)_ Add workflow `update-devenv` ([#16387], thanks @straight-shoota)\n- _(ci)_ Update GH Actions ([#16434], thanks @renovate)\n- _(ci)_ Update distribution-scripts ([#16411], thanks @straight-shoota)\n- _(ci)_ Run smoke tests on docker images ([#16441], thanks @straight-shoota)\n- _(ci)_ Update actions/checkout digest to 8e8c483 ([#16474], thanks @renovate)\n- _(ci)_ Remove `test_dist_linux_on_docker` job ([#16410], thanks @straight-shoota)\n- _(ci)_ Push docker images directly to registry ([#16488], thanks @straight-shoota)\n- _(ci)_ Enable multiarch docker builds ([#16493], thanks @straight-shoota)\n- _(ci)_ Run multi-threading test job with execution context ([#16339], thanks @straight-shoota)\n- _(ci)_ build linux aarch64 tarballs ([#16330], thanks @ysbaddaden)\n\n[#16510]: https://github.com/crystal-lang/crystal/pull/16510\n[#16212]: https://github.com/crystal-lang/crystal/pull/16212\n[#16221]: https://github.com/crystal-lang/crystal/pull/16221\n[#16222]: https://github.com/crystal-lang/crystal/pull/16222\n[#16252]: https://github.com/crystal-lang/crystal/pull/16252\n[#16246]: https://github.com/crystal-lang/crystal/pull/16246\n[#16223]: https://github.com/crystal-lang/crystal/pull/16223\n[#16241]: https://github.com/crystal-lang/crystal/pull/16241\n[#16249]: https://github.com/crystal-lang/crystal/pull/16249\n[#16263]: https://github.com/crystal-lang/crystal/pull/16263\n[#16276]: https://github.com/crystal-lang/crystal/pull/16276\n[#16295]: https://github.com/crystal-lang/crystal/pull/16295\n[#16291]: https://github.com/crystal-lang/crystal/pull/16291\n[#16301]: https://github.com/crystal-lang/crystal/pull/16301\n[#16349]: https://github.com/crystal-lang/crystal/pull/16349\n[#16372]: https://github.com/crystal-lang/crystal/pull/16372\n[#16346]: https://github.com/crystal-lang/crystal/pull/16346\n[#16380]: https://github.com/crystal-lang/crystal/pull/16380\n[#16386]: https://github.com/crystal-lang/crystal/pull/16386\n[#16408]: https://github.com/crystal-lang/crystal/pull/16408\n[#16462]: https://github.com/crystal-lang/crystal/pull/16462\n[#16503]: https://github.com/crystal-lang/crystal/pull/16503\n[#16509]: https://github.com/crystal-lang/crystal/pull/16509\n[#16524]: https://github.com/crystal-lang/crystal/pull/16524\n[#16530]: https://github.com/crystal-lang/crystal/pull/16530\n[#16523]: https://github.com/crystal-lang/crystal/pull/16523\n[#16219]: https://github.com/crystal-lang/crystal/pull/16219\n[#16491]: https://github.com/crystal-lang/crystal/pull/16491\n[#16389]: https://github.com/crystal-lang/crystal/pull/16389\n[#16201]: https://github.com/crystal-lang/crystal/pull/16201\n[#16215]: https://github.com/crystal-lang/crystal/pull/16215\n[#16253]: https://github.com/crystal-lang/crystal/pull/16253\n[#16275]: https://github.com/crystal-lang/crystal/pull/16275\n[#16296]: https://github.com/crystal-lang/crystal/pull/16296\n[#16282]: https://github.com/crystal-lang/crystal/pull/16282\n[#16290]: https://github.com/crystal-lang/crystal/pull/16290\n[#16326]: https://github.com/crystal-lang/crystal/pull/16326\n[#16331]: https://github.com/crystal-lang/crystal/pull/16331\n[#16337]: https://github.com/crystal-lang/crystal/pull/16337\n[#16341]: https://github.com/crystal-lang/crystal/pull/16341\n[#16343]: https://github.com/crystal-lang/crystal/pull/16343\n[#16385]: https://github.com/crystal-lang/crystal/pull/16385\n[#16387]: https://github.com/crystal-lang/crystal/pull/16387\n[#16434]: https://github.com/crystal-lang/crystal/pull/16434\n[#16411]: https://github.com/crystal-lang/crystal/pull/16411\n[#16441]: https://github.com/crystal-lang/crystal/pull/16441\n[#16474]: https://github.com/crystal-lang/crystal/pull/16474\n[#16410]: https://github.com/crystal-lang/crystal/pull/16410\n[#16488]: https://github.com/crystal-lang/crystal/pull/16488\n[#16493]: https://github.com/crystal-lang/crystal/pull/16493\n[#16339]: https://github.com/crystal-lang/crystal/pull/16339\n[#16330]: https://github.com/crystal-lang/crystal/pull/16330\n"
  },
  {
    "path": "doc/changelogs/v1.2.md",
    "content": "# Changelog 1.2\n\n## [1.2.2] - 2021-11-10\n\n[1.2.2]: https://github.com/crystal-lang/crystal/releases/1.2.2\n\n### Compiler\n\n- x86_64 ABI: pass structs indirectly if there are no more available registers ([#11344](https://github.com/crystal-lang/crystal/pull/11344), thanks @ggiraldez)\n- Add parentheses around type name for metaclasses of unions ([#11315](https://github.com/crystal-lang/crystal/pull/11315), thanks @HertzDevil)\n- **(regression)** Restrict virtual metaclasses to themselves against `Class` ([#11377](https://github.com/crystal-lang/crystal/pull/11377), thanks @HertzDevil)\n- **(regression)** Add fallback for union debug type if current debug file is not set ([#11390](https://github.com/crystal-lang/crystal/pull/11390), thanks @maxfierke)\n- **(regression)** Add missing debug locations to constant / class variable read calls ([#11417](https://github.com/crystal-lang/crystal/pull/11417), thanks @HertzDevil)\n\n### Standard Library\n\n#### Collection\n\n- Fix `BitArray#toggle` when toggling empty subrange ([#11381](https://github.com/crystal-lang/crystal/pull/11381), thanks @HertzDevil)\n\n#### Crypto\n\n- Update for OpenSSL 3.0.0 ([#11360](https://github.com/crystal-lang/crystal/pull/11360), thanks @straight-shoota)\n- Restore libressl support and add CI for that ([#11400](https://github.com/crystal-lang/crystal/pull/11400), thanks @straight-shoota)\n- Replace lib version comparisons by functional feature checks ([#11374](https://github.com/crystal-lang/crystal/pull/11374), thanks @straight-shoota)\n\n#### Runtime\n\n- Add support for DWARF 5 ([#11399](https://github.com/crystal-lang/crystal/pull/11399), thanks @straight-shoota)\n- Retrieve filename of shared libs, use in stacktraces ([#11408](https://github.com/crystal-lang/crystal/pull/11408), thanks @rdp)\n\n### Other\n\n- [CI] Fix enable nix-command as experimental feature ([#11398](https://github.com/crystal-lang/crystal/pull/11398), thanks @straight-shoota)\n- [CI] Fix OpenSSL 3 apk package name ([#11418](https://github.com/crystal-lang/crystal/pull/11418), thanks @straight-shoota)\n- Update distribution-scripts ([#11404](https://github.com/crystal-lang/crystal/pull/11404), thanks @straight-shoota)\n- [CI] Fix pcre download URL ([#11422](https://github.com/crystal-lang/crystal/pull/11422), thanks @straight-shoota)\n\n## [1.2.1] - 2021-10-21\n\n[1.2.1]: https://github.com/crystal-lang/crystal/releases/1.2.1\n\n### Compiler\n\n- Adding location to the Path returned by the literal expander for regex ([#11334](https://github.com/crystal-lang/crystal/pull/11334), thanks @beta-ziliani)\n\n### Standard Library\n\n- Add support for LLVM 13 ([#11302](https://github.com/crystal-lang/crystal/pull/11302), thanks @maxfierke)\n\n#### Runtime\n\n- Move the `:nodoc:` flags to the right place to hide the `__mul*` functions. ([#11326](https://github.com/crystal-lang/crystal/pull/11326), thanks @wyhaines)\n\n### Tools\n\n- Update markd subtree to v0.4.2 ([#11338](https://github.com/crystal-lang/crystal/pull/11338), thanks @straight-shoota)\n\n## [1.2.0] - 2021-10-13\n\n[1.2.0]: https://github.com/crystal-lang/crystal/releases/1.2.0\n\n### Compiler\n\n- Fix variance checks between generic instances for `Proc#call` and abstract defs. ([#10899](https://github.com/crystal-lang/crystal/pull/10899), thanks @HertzDevil)\n- Fix `proc_spec` forcing normal compilation instead of JIT ([#10964](https://github.com/crystal-lang/crystal/pull/10964), thanks @straight-shoota)\n- Fix `ProcNotation#to_s` remove whitespace for nil output type ([#10935](https://github.com/crystal-lang/crystal/pull/10935), thanks @straight-shoota)\n- Compiler: carry FileModule information inside Block ([#11039](https://github.com/crystal-lang/crystal/pull/11039), thanks @asterite)\n- Splat values correctly inside return/break/next statements ([#10193](https://github.com/crystal-lang/crystal/pull/10193), thanks @HertzDevil)\n- Handle already stripped column numbers in compiler exceptions ([#11008](https://github.com/crystal-lang/crystal/pull/11008), thanks @pyrsmk)\n- Substitute unbound type parameters in virtual metaclass types ([#11067](https://github.com/crystal-lang/crystal/pull/11067), thanks @HertzDevil)\n- Improve detection of instance variables in extended modules ([#10554](https://github.com/crystal-lang/crystal/pull/10554), thanks @HertzDevil)\n- Don't compute instance variable initializers on unbound generic instances ([#11000](https://github.com/crystal-lang/crystal/pull/11000), thanks @HertzDevil)\n- Syntax errors for invalid 128-bit integer literals ([#10975](https://github.com/crystal-lang/crystal/pull/10975), thanks @rymiel)\n- Support auto-splatting in captured block literals ([#10251](https://github.com/crystal-lang/crystal/pull/10251), thanks @HertzDevil)\n- Detect cyclic includes between generic modules ([#10529](https://github.com/crystal-lang/crystal/pull/10529), thanks @HertzDevil)\n- Add stricter checks for arguments to macro methods on AST nodes ([#10498](https://github.com/crystal-lang/crystal/pull/10498), thanks @HertzDevil)\n- Compiler: fix `is_a?` for virtual metaclass types ([#11121](https://github.com/crystal-lang/crystal/pull/11121), thanks @asterite)\n- Fix edge cases with unicode method names ([#10978](https://github.com/crystal-lang/crystal/pull/10978), thanks @HertzDevil)\n- Don't emit debug info for unused variable declarations ([#10957](https://github.com/crystal-lang/crystal/pull/10957), thanks @HertzDevil)\n- Fix `Call.def_full_name` print full block parameter ([#10915](https://github.com/crystal-lang/crystal/pull/10915), thanks @straight-shoota)\n- Allow union types to be unbound ([#11166](https://github.com/crystal-lang/crystal/pull/11166), thanks @HertzDevil)\n- Make `typeof` start a nested lexical scope ([#10796](https://github.com/crystal-lang/crystal/pull/10796), thanks @HertzDevil)\n- Fix edge case for intersection between virtual metaclasses ([#11185](https://github.com/crystal-lang/crystal/pull/11185), thanks @HertzDevil)\n- Compiler: don't trigger \"already had enclosing call\" for same object ([#11202](https://github.com/crystal-lang/crystal/pull/11202), thanks @asterite)\n- Properly handle indirect arguments for external C functions ([#11189](https://github.com/crystal-lang/crystal/pull/11189), thanks @ggiraldez)\n- Fix resolve generic argument in block output type restriction mismatch ([#11186](https://github.com/crystal-lang/crystal/pull/11186), thanks @straight-shoota)\n- Secure array slicing when expanding macro for stack trace ([#11109](https://github.com/crystal-lang/crystal/pull/11109), thanks @pyrsmk)\n- Fix debug locations for `Proc` pointers ([#11243](https://github.com/crystal-lang/crystal/pull/11243), thanks @HertzDevil)\n- Allow assignments from generic instance metaclasses to virtual metaclasses ([#11250](https://github.com/crystal-lang/crystal/pull/11250), thanks @HertzDevil)\n- Refactor `CrystalPath#find_in_path_relative_to_dir` for readability ([#10876](https://github.com/crystal-lang/crystal/pull/10876), [#10990](https://github.com/crystal-lang/crystal/pull/10990), [#10988](https://github.com/crystal-lang/crystal/pull/10988), thanks @straight-shoota)\n- Allow constants and instance / class variables as receivers for setter proc pointers ([#10741](https://github.com/crystal-lang/crystal/pull/10741), thanks @HertzDevil)\n- Do not use globals for regex ([#10951](https://github.com/crystal-lang/crystal/pull/10951), thanks @asterite)\n- Define type filtering through an intersection operation ([#10781](https://github.com/crystal-lang/crystal/pull/10781), thanks @HertzDevil)\n- Fix no overflow check when primitive int converts to same type ([#11003](https://github.com/crystal-lang/crystal/pull/11003), thanks @HertzDevil)\n- Attach debug locations to generated internal LLVM functions ([#10934](https://github.com/crystal-lang/crystal/pull/10934), thanks @HertzDevil)\n- Add helpful error message for invalid number literal like '.42' ([#4665](https://github.com/crystal-lang/crystal/pull/4665), thanks @MakeNowJust)\n- Add `CrystalPath.expand_paths`, expand relative to compiler path ([#11030](https://github.com/crystal-lang/crystal/pull/11030), thanks @straight-shoota)\n- Clarify usage of \"arguments\" and \"parameters\" in error messages ([#10378](https://github.com/crystal-lang/crystal/pull/10378), thanks @HertzDevil)\n- **(performance)** Don't generate type IDs for formal generic instances ([#11167](https://github.com/crystal-lang/crystal/pull/11167), thanks @HertzDevil)\n- **(performance)** Don't generate unique type IDs for virtual metaclasses ([#11188](https://github.com/crystal-lang/crystal/pull/11188), thanks @HertzDevil)\n- Add an environment variable for dumping type IDs ([#11168](https://github.com/crystal-lang/crystal/pull/11168), thanks @HertzDevil)\n- Allow underscores in macro `for`'s loop variables ([#11141](https://github.com/crystal-lang/crystal/pull/11141), thanks @HertzDevil)\n- **(performance)** Compiler: cache cleanup transformer ([#11197](https://github.com/crystal-lang/crystal/pull/11197), thanks @asterite)\n- Avoid needless union in `LLVM::ABI::AArch64#homogeneous_aggregate?` ([#11199](https://github.com/crystal-lang/crystal/pull/11199), thanks @asterite)\n- Removing ThinLTO support ([#11194](https://github.com/crystal-lang/crystal/pull/11194), thanks @beta-ziliani)\n- Error if abstract def implementation is inherited from supertype ([#11056](https://github.com/crystal-lang/crystal/pull/11056), thanks @straight-shoota)\n- **(performance)** Add `inject_primitives: false` to macro_spec ([#11269](https://github.com/crystal-lang/crystal/pull/11269), thanks @straight-shoota)\n- Primitive annotations for interpreter ([#11147](https://github.com/crystal-lang/crystal/pull/11147), thanks @asterite)\n- Support generic module instances in `TypeNode#includers` ([#11116](https://github.com/crystal-lang/crystal/pull/11116), thanks @HertzDevil)\n- Reject hash literals with mixed syntax ([#11154](https://github.com/crystal-lang/crystal/pull/11154), thanks @MakeNowJust)\n\n### Language\n\n- Make `.as?(NoReturn)` always return `nil` ([#10896](https://github.com/crystal-lang/crystal/pull/10896), thanks @HertzDevil)\n- Compiler: make `is_a?(union)` work correctly for virtual types ([#11176](https://github.com/crystal-lang/crystal/pull/11176), thanks @asterite)\n- Adjust docs for `Crystal::Macros::HashLiteral#[]` ([#10930](https://github.com/crystal-lang/crystal/pull/10930), thanks @kevinsjoberg)\n- Fix path lookup when ancestor finds type with same name as current scope ([#10901](https://github.com/crystal-lang/crystal/pull/10901), thanks @HertzDevil)\n- Fix several compile-time operations on generic instance metaclasses ([#11101](https://github.com/crystal-lang/crystal/pull/11101), thanks @HertzDevil)\n- Make `#is_a?` in macros respect the AST node hierarchy ([#11062](https://github.com/crystal-lang/crystal/pull/11062), thanks @HertzDevil)\n- Add docs to string methods in `SymbolLiteral` and `MacroId` ([#9298](https://github.com/crystal-lang/crystal/pull/9298), thanks @MakeNowJust)\n- Add clarification about when `instance_vars` can be called ([#11171](https://github.com/crystal-lang/crystal/pull/11171), thanks @willhbr)\n- Add `file_exists?` macro method ([#10540](https://github.com/crystal-lang/crystal/pull/10540), thanks @Sija)\n\n### Standard Library\n\n- **(breaking-change)** Change nonsense return types to Nil: uncategorized ([#10625](https://github.com/crystal-lang/crystal/pull/10625), thanks @oprypin)\n- **(breaking-change)** Change nonsense return types to Nil in formatter classes ([#10623](https://github.com/crystal-lang/crystal/pull/10623), thanks @oprypin)\n- Add base64 to prelude ([#11050](https://github.com/crystal-lang/crystal/pull/11050), thanks @straight-shoota)\n- Remove calls to deprecated `SystemError.from_winerror` ([#11220](https://github.com/crystal-lang/crystal/pull/11220), thanks @straight-shoota)\n- Add support for LLVM 12 ([#10873](https://github.com/crystal-lang/crystal/pull/10873), [#11178](https://github.com/crystal-lang/crystal/pull/11178), thanks @maxfierke, @Blacksmoke16)\n- Examples: fix (2021-09) ([#11234](https://github.com/crystal-lang/crystal/pull/11234), thanks @maiha)\n- Don't use `:nodoc:` when overriding public methods ([#11096](https://github.com/crystal-lang/crystal/pull/11096), thanks @HertzDevil)\n- Add internal registry implementation for win32 ([#11137](https://github.com/crystal-lang/crystal/pull/11137), thanks @straight-shoota)\n\n#### Collection\n\n- **(breaking-change)** Move `Array#product` to `Indexable#cartesian_product` ([#10013](https://github.com/crystal-lang/crystal/pull/10013), thanks @HertzDevil)\n- Disallow `Slice(T).new(Int)` where `T` is a union of primitive number types ([#10982](https://github.com/crystal-lang/crystal/pull/10982), thanks @HertzDevil)\n- Make `Array#transpose`, `Enumerable#reject`, `Enumerable#to_h` work with tuples ([#10445](https://github.com/crystal-lang/crystal/pull/10445), thanks @HertzDevil)\n- Fix `Enumerable#each` block return type ([#10928](https://github.com/crystal-lang/crystal/pull/10928), thanks @straight-shoota)\n- Fix key type for empty `NamedTuple` be `Symbol` ([#10942](https://github.com/crystal-lang/crystal/pull/10942), thanks @caspiano)\n- Fix overflow in `BitArray#[](Int, Int)` for sizes between 33 and 64 ([#10809](https://github.com/crystal-lang/crystal/pull/10809), thanks @HertzDevil)\n- Fix `Range#step` for non-integer `Steppable` types ([#11130](https://github.com/crystal-lang/crystal/pull/11130), thanks @straight-shoota)\n- **(performance)** Construct an array literal in `NamedTuple#map` ([#10950](https://github.com/crystal-lang/crystal/pull/10950), thanks @caspiano)\n- Add `Slice#fill` ([#10924](https://github.com/crystal-lang/crystal/pull/10924), thanks @HertzDevil)\n- Add range overloads for `BitArray#toggle` ([#10743](https://github.com/crystal-lang/crystal/pull/10743), thanks @HertzDevil)\n- Add stable sort implementation to `Slice`, `Array` and `Indexable::Mutable` ([#10163](https://github.com/crystal-lang/crystal/pull/10163), [#11029](https://github.com/crystal-lang/crystal/pull/11029), [#11254](https://github.com/crystal-lang/crystal/pull/11254), thanks @MakeNowJust, thanks @straight-shoota)\n- Allow `Enumerable(T)#reduce`'s return type to differ from `T` ([#11065](https://github.com/crystal-lang/crystal/pull/11065), thanks @HertzDevil)\n- Implement `Enumerable#tally_by` ([#10922](https://github.com/crystal-lang/crystal/pull/10922), thanks @caspiano)\n- Add the `Indexable::Mutable(T)` module ([#11059](https://github.com/crystal-lang/crystal/pull/11059), thanks @HertzDevil)\n- Remove restriction of bsearch block output type ([#11212](https://github.com/crystal-lang/crystal/pull/11212), thanks @straight-shoota)\n- Add and improve type restrictions of block arguments ([#10467](https://github.com/crystal-lang/crystal/pull/10467), [#11246](https://github.com/crystal-lang/crystal/pull/11246), [#11267](https://github.com/crystal-lang/crystal/pull/11267, [#11308](https://github.com/crystal-lang/crystal/pull/11308), thanks @caspiano, thanks @straight-shoota, thanks @HertzDevil, thanks @beta-ziliani, thanks @caspiano)\n- **(performance)** Optimize `#rotate!` ([#11198](https://github.com/crystal-lang/crystal/pull/11198), thanks @HertzDevil)\n\n#### Concurrency\n\n- Fix Documentation of `Fiber.timeout` ([#11271](https://github.com/crystal-lang/crystal/pull/11271), thanks @toddsundsted)\n- **(performance)** `Scheduler#reschedule`: Shortcut lookup for current fiber. ([#11156](https://github.com/crystal-lang/crystal/pull/11156), thanks @yxhuvud)\n- Add sleep support to win32 event loop ([#10605](https://github.com/crystal-lang/crystal/pull/10605), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Change nonsense return types to Nil in IO-related methods ([#10621](https://github.com/crystal-lang/crystal/pull/10621), thanks @oprypin)\n- Fix `File.match?` to accept `Path` type as `path` argument ([#11075](https://github.com/crystal-lang/crystal/pull/11075), thanks @fishnibble)\n- Add `FileUtils` method specs with `String` and `Path` arguments ([#10987](https://github.com/crystal-lang/crystal/pull/10987), thanks @straight-shoota)\n- Make `IO#read_char`'s default behaviour UTF-8-strict ([#10446](https://github.com/crystal-lang/crystal/pull/10446), thanks @HertzDevil)\n- Fix glob with multiple recurse patterns ([#10813](https://github.com/crystal-lang/crystal/pull/10813), thanks @straight-shoota)\n- IO: fix bug in `gets` without peek involving `\\r` and limit ([#11241](https://github.com/crystal-lang/crystal/pull/11241), thanks @asterite)\n- Make `FileUtils.mv` work across filesystems ([#10783](https://github.com/crystal-lang/crystal/pull/10783), thanks @naqvis)\n- **(performance)** Improve performance of `Path#dirname` and `Path#extension` ([#11001](https://github.com/crystal-lang/crystal/pull/11001), thanks @BlobCodes)\n\n#### Networking\n\n- **(breaking-change)** Change nonsense return types to `Nil` in HTTP-related methods and `Log` ([#10624](https://github.com/crystal-lang/crystal/pull/10624), thanks @oprypin)\n- Fix trailing `rescue` syntax ([#11083](https://github.com/crystal-lang/crystal/pull/11083), thanks @straight-shoota)\n- Fix spec for `HTTP::Params` can't run on its own ([#11128](https://github.com/crystal-lang/crystal/pull/11128), thanks @asterite)\n- Fix parsing cookie `Domain` attribute with leading dot ([#11098](https://github.com/crystal-lang/crystal/pull/11098), thanks @mamantoha)\n- Rescue `OpenSSL::SSL::Error` in `HTTP::Server#handle_client` ([#11146](https://github.com/crystal-lang/crystal/pull/11146), thanks @straight-shoota)\n- Fix `TCPSocket` constructors ([#11049](https://github.com/crystal-lang/crystal/pull/11049), thanks @straight-shoota)\n- Support basic auth from `URI` in websockets ([#10854](https://github.com/crystal-lang/crystal/pull/10854), thanks @willhbr)\n- Tag std specs that need network access ([#11048](https://github.com/crystal-lang/crystal/pull/11048), thanks @toshokan)\n- Proper handling of `max-age` and `expires` for cookies ([#10564](https://github.com/crystal-lang/crystal/pull/10564), thanks @straight-shoota, @watzon)\n- Retry `HTTP::Client` requests once if io is closed ([#11088](https://github.com/crystal-lang/crystal/pull/11088), thanks @carlhoerberg)\n- Implement `Socket` for win32 ([#10784](https://github.com/crystal-lang/crystal/pull/10784), thanks @straight-shoota)\n- Add `URI.encode_path` and deprecate `URI.encode` ([#11248](https://github.com/crystal-lang/crystal/pull/11248), thanks @straight-shoota)\n\n#### Numeric\n\n- **(breaking-change)** Refine type restriction of `Math.frexp(BigFloat)` ([#10998](https://github.com/crystal-lang/crystal/pull/10998), thanks @straight-shoota)\n- Fix `BigInt#to_s` emitting null bytes for certain values ([#11063](https://github.com/crystal-lang/crystal/pull/11063), thanks @HertzDevil)\n- Fix `Float#humanize` for values outside `1e-4...1e15` ([#10881](https://github.com/crystal-lang/crystal/pull/10881), thanks @straight-shoota)\n- Add type restrictions and fix return types of `BigFloat#to_x` methods ([#10996](https://github.com/crystal-lang/crystal/pull/10996), thanks @straight-shoota)\n- Add integer square root ([#10549](https://github.com/crystal-lang/crystal/pull/10549), thanks @kimburgess)\n- Add negative exponential support to BigDecimal ([#10892](https://github.com/crystal-lang/crystal/pull/10892), thanks @stakach)\n- Add `#next_float` and `#prev_float` to `Float32` and `Float64` ([#10908](https://github.com/crystal-lang/crystal/pull/10908), thanks @HertzDevil)\n- Add precision parameter to `Int#to_s` ([#10926](https://github.com/crystal-lang/crystal/pull/10926), thanks @HertzDevil)\n- **(performance)** Improve Int parsing performance ([#11093](https://github.com/crystal-lang/crystal/pull/11093), thanks @BlobCodes)\n- Implement `Int128` compiler-rt methods ([#11206](https://github.com/crystal-lang/crystal/pull/11206), thanks @BlobCodes)\n- Fix `BigDecimal` operations with floats ([#10874](https://github.com/crystal-lang/crystal/pull/10874), thanks @stakach)\n- Add `String#to_(u/i)128(?)` methods ([#11245](https://github.com/crystal-lang/crystal/pull/11245), thanks @BlobCodes)\n\n#### Runtime\n\n- Extract `libunwind` from callstack ([#11205](https://github.com/crystal-lang/crystal/pull/11205), thanks @straight-shoota)\n\n#### Serialization\n\n- **(breaking-change)** Change nonsense return types to `Nil`: JSON and YAML ([#10622](https://github.com/crystal-lang/crystal/pull/10622), thanks @oprypin)\n- **(breaking-change)** Add type restriction and conversion to `YAML::PullParser#location` ([#10997](https://github.com/crystal-lang/crystal/pull/10997), thanks @straight-shoota)\n- Allow EOF IO passed to `JSON::PullParser.new` ([#10864](https://github.com/crystal-lang/crystal/pull/10864), thanks @Blacksmoke16)\n- Quote the named tuple's keys on deserialization ([#10919](https://github.com/crystal-lang/crystal/pull/10919), thanks @Blacksmoke16)\n- Refactor `JSON::PullParser#consume_number` to use stdlib number parsing ([#10447](https://github.com/crystal-lang/crystal/pull/10447), thanks @straight-shoota)\n- XML Namespace improvements ([#11072](https://github.com/crystal-lang/crystal/pull/11072), thanks @Blacksmoke16)\n- Add JSON/YAML serialization to `URI` ([#10404](https://github.com/crystal-lang/crystal/pull/10404), thanks @straight-shoota)\n\n#### Specs\n\n- Add missing require in `iterator_spec` ([#11148](https://github.com/crystal-lang/crystal/pull/11148), thanks @asterite)\n- Add missing requires to run a couple of specs standalone ([#11152](https://github.com/crystal-lang/crystal/pull/11152), thanks @asterite)\n- Allow `describe` without requiring an argument ([#10974](https://github.com/crystal-lang/crystal/pull/10974), thanks @straight-shoota)\n\n#### System\n\n- SystemError: Fix inconsistent signature. ([#11002](https://github.com/crystal-lang/crystal/pull/11002), thanks @yxhuvud)\n\n#### Text\n\n- **(breaking-change)** Deprecate `String#unsafe_byte_at` ([#10559](https://github.com/crystal-lang/crystal/pull/10559), thanks @straight-shoota)\n- **(breaking-change)** Rename `IO#write_utf8` to `#write_string`. ([#11051](https://github.com/crystal-lang/crystal/pull/11051), thanks @straight-shoota)\n- Use `#write_string` instead of `#write` whenever writing strings to unknown `IO`s ([#11011](https://github.com/crystal-lang/crystal/pull/11011), thanks @HertzDevil)\n- Don't use `#write_byte` whenever writing ASCII characters to unknown `IO`s ([#11124](https://github.com/crystal-lang/crystal/pull/11124), thanks @HertzDevil)\n- Make `Int#chr` reject surrogate halves ([#10451](https://github.com/crystal-lang/crystal/pull/10451), thanks @HertzDevil)\n- CSV: don't eagerly check next char after newline ([#11174](https://github.com/crystal-lang/crystal/pull/11174), thanks @asterite)\n- Fix link on regex.cr ([#11204](https://github.com/crystal-lang/crystal/pull/11204), thanks @gemmaro)\n- Disallow non-UTF-8 encoding settings for `String::Builder` ([#11025](https://github.com/crystal-lang/crystal/pull/11025), thanks @HertzDevil)\n- Unicode: update to version 14.0.0 ([#11215](https://github.com/crystal-lang/crystal/pull/11215), thanks @Blacksmoke16)\n\n### Tools\n\n- Formatter: Handle `(-> )` correctly ([#10945](https://github.com/crystal-lang/crystal/pull/10945), thanks @HertzDevil)\n- Use [markd](https://github.com/icyleaf/markd) for markdown rendering in the compiler ([#11040](https://github.com/crystal-lang/crystal/pull/11040), thanks @straight-shoota)\n- Formatter: Handle leading tuple literals in multi-expression `return`/`break`/`next` properly ([#10597](https://github.com/crystal-lang/crystal/pull/10597), thanks @HertzDevil)\n- Include parent headings in anchor links ([#9839](https://github.com/crystal-lang/crystal/pull/9839), thanks @Blacksmoke16)\n- Fix formatting nested multiline array and tuple ([#11153](https://github.com/crystal-lang/crystal/pull/11153), thanks @MakeNowJust)\n- `crystal init`: Improve transformation of project name with hyphens ([#11170](https://github.com/crystal-lang/crystal/pull/11170), thanks @Kanezoh)\n- Fix formatting generic types with suffix ([#11187](https://github.com/crystal-lang/crystal/pull/11187), thanks @MakeNowJust)\n- Make `WARNING` an admonition keyword ([#10898](https://github.com/crystal-lang/crystal/pull/10898), thanks @HertzDevil)\n- Refactor hierarchy printers ([#10791](https://github.com/crystal-lang/crystal/pull/10791), thanks @HertzDevil)\n\n### Other\n\n- Fix typos ([#11045](https://github.com/crystal-lang/crystal/pull/11045), [#11163](https://github.com/crystal-lang/crystal/pull/11163), [#11138](https://github.com/crystal-lang/crystal/pull/11138), hanks @toshokan, thanks @MakeNowJust, thanks @schmijos)\n- Update readme to point to IRC channel on libera.chat ([#11024](https://github.com/crystal-lang/crystal/pull/11024), thanks @jhass)\n- [CI] Update ruby-install ([#11276](https://github.com/crystal-lang/crystal/pull/11276), thanks @straight-shoota)\n- [CI] Remove `test_linux_32` and add smoke test for 32-bit gnu ([#11127](https://github.com/crystal-lang/crystal/pull/11127), thanks @straight-shoota)\n- [CI] Remove obsolete `package_build` workflow ([#11240](https://github.com/crystal-lang/crystal/pull/11240), thanks @straight-shoota)\n- [CI] Add build matrix with 1.0.0 and 1.1.1 ([#11278](https://github.com/crystal-lang/crystal/pull/11278), thanks @straight-shoota)\n- [CI] Update aarch64.yml ([#11160](https://github.com/crystal-lang/crystal/pull/11160), thanks @beta-ziliani)\n- [CI] Update distribution-scripts (universal darwin & demote alpine to 3.12)  ([#11228](https://github.com/crystal-lang/crystal/pull/11228), thanks @bcardiff)\n- Update shards 0.16.0 ([#11292](https://github.com/crystal-lang/crystal/pull/11292), thanks @straight-shoota)\n- Update previous release Crystal 1.1.0 ([#10955](https://github.com/crystal-lang/crystal/pull/10955), thanks @straight-shoota)\n- Merge changelog entry for 1.1.1 ([#11028](https://github.com/crystal-lang/crystal/pull/11028), thanks @straight-shoota)\n- Update previous release Crystal 1.1.1 ([#11053](https://github.com/crystal-lang/crystal/pull/11053), thanks @straight-shoota)\n- PR template ([#10894](https://github.com/crystal-lang/crystal/pull/10894), thanks @beta-ziliani)\n- Add github-changelog script ([#11155](https://github.com/crystal-lang/crystal/pull/11155), thanks @straight-shoota)\n- Add `make install` ([#10878](https://github.com/crystal-lang/crystal/pull/10878), thanks @straight-shoota)\n- [CI] Sanitize version from branch name ([#11294](https://github.com/crystal-lang/crystal/pull/11294), thanks @straight-shoota)\n- Update libgc to 8.2.0 ([#11293](https://github.com/crystal-lang/crystal/pull/11293), thanks @straight-shoota)\n- [CI] Unify `maintenance_release` and `tagged_release` workflows ([#11273](https://github.com/crystal-lang/crystal/pull/11273), thanks @straight-shoota)\n- [CI] Update distribution-scripts (make install) ([#11307](https://github.com/crystal-lang/crystal/pull/11307), thanks @straight-shoota)\n- [CI] Enable publish docker images on tagged release ([#11309](https://github.com/crystal-lang/crystal/pull/11309), thanks @straight-shoota)\n- [CI] Update distribution-scripts (fix for libgc in alpine Docker image) ([#11310](https://github.com/crystal-lang/crystal/pull/11310), thanks @straight-shoota)\n- [CI] Pin macOS runner to 10.15 ([#11282](https://github.com/crystal-lang/crystal/pull/11282), thanks @straight-shoota)\n- [CI] Fix `push_obs_nightly` ([#11301](https://github.com/crystal-lang/crystal/pull/11301), thanks @straight-shoota)\n- [CI] Update distribution-scripts ([#11285](https://github.com/crystal-lang/crystal/pull/11285), thanks @straight-shoota)\n- [CI] Remove i386 builds ([#11287](https://github.com/crystal-lang/crystal/pull/11287), thanks @straight-shoota)\n"
  },
  {
    "path": "doc/changelogs/v1.3.md",
    "content": "# Changelog 1.3\n\n## [1.3.2] - 2022-01-18\n\n[1.3.2]: https://github.com/crystal-lang/crystal/releases/1.3.2\n\n### Standard Library\n\n#### Text\n\n- Fix buffer overflow in `String#index` ([#11747](https://github.com/crystal-lang/crystal/pull/11747), thanks @asterite, @straight-shoota)\n\n## [1.3.1] - 2022-01-13\n\n[1.3.1]: https://github.com/crystal-lang/crystal/releases/1.3.1\n\n### Standard Library\n\n- Remove useless variable declarations in trailing position ([#11704](https://github.com/crystal-lang/crystal/pull/11704), thanks @HertzDevil)\n\n#### Crypto\n\n- Fix for missing `BIO_*` functions in OpenSSL < 1.1.0 ([#11736](https://github.com/crystal-lang/crystal/pull/11736), thanks @daliborfilus)\n\n#### Runtime\n\n- Remove string allocation from `GC_set_warn_proc` ([#11729](https://github.com/crystal-lang/crystal/pull/11729), thanks @straight-shoota)\n\n### Tools\n\n- Doc generator: Fix escape HTML in code span ([#11686](https://github.com/crystal-lang/crystal/pull/11686), thanks @straight-shoota)\n- Fix formatter error for `ProcLiteral`s with `Union` return type ([#11709](https://github.com/crystal-lang/crystal/pull/11709), thanks @HertzDevil)\n\n### Other\n\n- Fix typos ([#11725](https://github.com/crystal-lang/crystal/pull/11725), thanks @kianmeng)\n\n## [1.3.0] - 2022-01-06\n\n[1.3.0]: https://github.com/crystal-lang/crystal/releases/1.3.0\n\n### Compiler\n\n- Refer to `T.class` as \"metaclass\" in error messages, not \"class\" ([#11378](https://github.com/crystal-lang/crystal/pull/11378), thanks @HertzDevil)\n- Create `Reason` enum for exhaustive case in nil-reason check ([#11449](https://github.com/crystal-lang/crystal/pull/11449), thanks @rymiel)\n- Improve cache directory behaviour on Windows ([#11436](https://github.com/crystal-lang/crystal/pull/11436), thanks @HertzDevil)\n- Automatically detect MSVC tools on Windows via `vswhere` ([#11496](https://github.com/crystal-lang/crystal/pull/11496), thanks @HertzDevil)\n- Clean up .pdb files for temporary executables on MSVC ([#11553](https://github.com/crystal-lang/crystal/pull/11553), thanks @HertzDevil)\n- Disable incremental linking on MSVC ([#11552](https://github.com/crystal-lang/crystal/pull/11552), thanks @HertzDevil)\n- Allow multiple `--emit` compiler options to stack ([#11556](https://github.com/crystal-lang/crystal/pull/11556), thanks @HertzDevil)\n- Refactor some type restrictions in the compiler ([#11531](https://github.com/crystal-lang/crystal/pull/11531), thanks @straight-shoota)\n- Detect `cl.exe`'s path for compiler specs requiring a C compiler ([#11560](https://github.com/crystal-lang/crystal/pull/11560), thanks @HertzDevil)\n- Increase default stack size on MSVC to 8 MB ([#11569](https://github.com/crystal-lang/crystal/pull/11569), thanks @HertzDevil)\n- Resolve compiler wildcard require ([#11562](https://github.com/crystal-lang/crystal/pull/11562), thanks @straight-shoota)\n- Compiler: use enums instead of symbols in various places ([#11607](https://github.com/crystal-lang/crystal/pull/11607), thanks @HertzDevil)\n\n#### Codegen\n\n- Disable specs for `StaticArray#sort_by` on broken targets ([#11359](https://github.com/crystal-lang/crystal/pull/11359), thanks @straight-shoota)\n- Fix link flag behaviour on Windows MSVC ([#11424](https://github.com/crystal-lang/crystal/pull/11424), thanks @HertzDevil)\n- Attach debug locations to splat expansions inside array-like literals ([#11655](https://github.com/crystal-lang/crystal/pull/11655), thanks @HertzDevil)\n- Use full name for private types' class variables during codegen ([#11651](https://github.com/crystal-lang/crystal/pull/11651), thanks @HertzDevil)\n- Fix codegen when instantiating class methods of typedefs ([#11636](https://github.com/crystal-lang/crystal/pull/11636), thanks @HertzDevil)\n- Add minimal load-time DLL support on Windows, support `dllimport` storage class ([#11573](https://github.com/crystal-lang/crystal/pull/11573), thanks @HertzDevil)\n\n#### Debugger\n\n- Attach debug locations to auto-generated `initialize` methods ([#11313](https://github.com/crystal-lang/crystal/pull/11313), thanks @HertzDevil)\n- Fix debug location for `~check_proc_is_not_closure` ([#11311](https://github.com/crystal-lang/crystal/pull/11311), thanks @HertzDevil)\n\n#### Interpreter\n\n- `crystal i`, a Crystal interpreter ([#11159](https://github.com/crystal-lang/crystal/pull/11159), thanks @asterite)\n- Implement FFI bindings ([#11475](https://github.com/crystal-lang/crystal/pull/11475), thanks @straight-shoota)\n- Add `Crystal::Loader` ([#11434](https://github.com/crystal-lang/crystal/pull/11434), [#11662](https://github.com/crystal-lang/crystal/pull/11662), thanks @straight-shoota, @HertzDevil)\n- Mark `bswap32` intrinsic with interpreter primitive annotation ([#11582](https://github.com/crystal-lang/crystal/pull/11582), thanks @rymiel)\n- Split interpreter specs into separate files ([#11578](https://github.com/crystal-lang/crystal/pull/11578), thanks @straight-shoota)\n- Workaround for GC issues in interpreter specs ([#11634](https://github.com/crystal-lang/crystal/pull/11634), thanks @straight-shoota)\n\n#### Parser\n\n- Parser: allow keyword as named argument inside macros ([#10377](https://github.com/crystal-lang/crystal/pull/10377), thanks @asterite)\n- Parser: add missing end location to `IsA` node ([#11351](https://github.com/crystal-lang/crystal/pull/11351), thanks @FnControlOption)\n- Fix node locations for `ProcLiteral`s with parameters ([#11365](https://github.com/crystal-lang/crystal/pull/11365), thanks @HertzDevil)\n- Fix parser error with named argument `end` in macro body ([#11463](https://github.com/crystal-lang/crystal/pull/11463), thanks @straight-shoota)\n- Report syntax error for too-long bin/hex/oct integer literals ([#11447](https://github.com/crystal-lang/crystal/pull/11447), thanks @oprypin)\n- [lexer] Correctly increase nesting for escaped macro `unless` ([#11440](https://github.com/crystal-lang/crystal/pull/11440), thanks @rymiel)\n- Show proper syntax errors in some edge cases in the parser ([#11446](https://github.com/crystal-lang/crystal/pull/11446), thanks @oprypin)\n- Fix parse `yield` with parenthesis ([#11469](https://github.com/crystal-lang/crystal/pull/11469), thanks @straight-shoota)\n- Lexer number parsing refactor ([#11211](https://github.com/crystal-lang/crystal/pull/11211), thanks @BlobCodes)\n- Allow underscores after a leading zero in `String#to_i` (regression fix) ([#11672](https://github.com/crystal-lang/crystal/pull/11672), thanks @BlobCodes)\n- Fix no comma before short block in `ToSVisitor` ([#11677](https://github.com/crystal-lang/crystal/pull/11677), thanks @homonoidian)\n- Unify format of \"unexpected token\" error ([#11473](https://github.com/crystal-lang/crystal/pull/11473), thanks @straight-shoota)\n- Implement lexer int128 support ([#11571](https://github.com/crystal-lang/crystal/pull/11571), thanks @BlobCodes)\n\n#### Semantic\n\n- Show proper owner for `Class`'s methods in error messages ([#10590](https://github.com/crystal-lang/crystal/pull/10590), thanks @HertzDevil)\n- Be more strict about `ProcNotation` variable declarations ([#11372](https://github.com/crystal-lang/crystal/pull/11372), thanks @HertzDevil)\n- Allow metaclass parameters in `Proc` literals and pointers ([#11367](https://github.com/crystal-lang/crystal/pull/11367), thanks @HertzDevil)\n- Fix top-level multi-assign splat variable not working in macros ([#11600](https://github.com/crystal-lang/crystal/pull/11600), thanks @HertzDevil)\n- Replace `semantic` with `assert_no_errors` in compiler specs whenever possible ([#11288](https://github.com/crystal-lang/crystal/pull/11288), thanks @HertzDevil)\n- Make `inject_primitives = false` default for semantic specs  ([#11297](https://github.com/crystal-lang/crystal/pull/11297), thanks @HertzDevil)\n- Add spec for #8428 ([#10073](https://github.com/crystal-lang/crystal/pull/10073), thanks @docelic)\n- Remove and resolve spurious cast and its associated FIXME ([#11455](https://github.com/crystal-lang/crystal/pull/11455), thanks @rymiel)\n- Add pending spec for recursive abstract struct ([#11470](https://github.com/crystal-lang/crystal/pull/11470), thanks @HertzDevil)\n\n### Language\n\n- **(breaking-change)** Require elements in 1-to-n assignments to match targets exactly ([#11145](https://github.com/crystal-lang/crystal/pull/11145), thanks @HertzDevil)\n- **(breaking-change)** Require right-hand side of one-to-many assignments to be `Indexable` ([#11545](https://github.com/crystal-lang/crystal/pull/11545), thanks @HertzDevil)\n- Support splats on left-hand sides of multiple assignment expressions ([#10410](https://github.com/crystal-lang/crystal/pull/10410), thanks @HertzDevil)\n- Make all AST nodes immutable through container-returning methods ([#11397](https://github.com/crystal-lang/crystal/pull/11397), thanks @HertzDevil)\n- Add auto upcast for integer and float values ([#11431](https://github.com/crystal-lang/crystal/pull/11431), [#11529](https://github.com/crystal-lang/crystal/pull/11529), thanks @asterite, @beta-ziliani)\n\n### Standard Library\n\n- Fix `Process::INITIAL_PWD` for non-existent path ([#10525](https://github.com/crystal-lang/crystal/pull/10525), thanks @straight-shoota)\n- Resolve some TODOs ([#11369](https://github.com/crystal-lang/crystal/pull/11369), thanks @straight-shoota)\n- Refactor some target flag uses ([#11466](https://github.com/crystal-lang/crystal/pull/11466), thanks @straight-shoota)\n- Use `Slice(UInt8)#fill` in the standard library ([#11468](https://github.com/crystal-lang/crystal/pull/11468), thanks @HertzDevil)\n- Update `spec/win32_std_spec.cr` ([#11432](https://github.com/crystal-lang/crystal/pull/11432), [#11637](https://github.com/crystal-lang/crystal/pull/11637), thanks @HertzDevil)\n- Use strings instead of symbols in `#finalize` specs ([#11619](https://github.com/crystal-lang/crystal/pull/11619), thanks @HertzDevil)\n- Fix `Enum.parse` to handle case-sensitive member names ([#11659](https://github.com/crystal-lang/crystal/pull/11659), thanks @straight-shoota)\n- Improve docs for `Object#not_nil!` ([#11661](https://github.com/crystal-lang/crystal/pull/11661), thanks @straight-shoota)\n\n#### Collection\n\n- **(breaking-change)** Always use `start` as parameter in subrange-accepting methods ([#11350](https://github.com/crystal-lang/crystal/pull/11350), thanks @HertzDevil)\n- **(breaking-change)** Refactor `Indexable::Mutable#fill`'s overloads ([#11368](https://github.com/crystal-lang/crystal/pull/11368), thanks @HertzDevil)\n- Add sorting methods to `StaticArray` ([#10889](https://github.com/crystal-lang/crystal/pull/10889), thanks @HertzDevil)\n- Add spaceship operator to `StaticArray` ([#11364](https://github.com/crystal-lang/crystal/pull/11364), thanks @henrikac)\n- **(performance)** Optimize `BitArray#reverse!` ([#11363](https://github.com/crystal-lang/crystal/pull/11363), thanks @HertzDevil)\n- **(performance)** Grow large arrays more slowly ([#11482](https://github.com/crystal-lang/crystal/pull/11482), thanks @mgomes)\n- Fix docs for `Indexable::Mutable#map!` ([#11349](https://github.com/crystal-lang/crystal/pull/11349), thanks @HertzDevil)\n- Add `Slice#unsafe_slice_of`, `#to_unsafe_bytes` ([#11379](https://github.com/crystal-lang/crystal/pull/11379), thanks @HertzDevil)\n- **(performance)** Avoid reallocation in `Enumerable#each_cons` and `Iterator#cons`'s default reused array ([#10384](https://github.com/crystal-lang/crystal/pull/10384), thanks @HertzDevil)\n- Fix `Array#unshift` for large arrays ([#11656](https://github.com/crystal-lang/crystal/pull/11656), thanks @HertzDevil)\n\n#### Crypto\n\n- Support OpenSSL on Windows ([#11477](https://github.com/crystal-lang/crystal/pull/11477), thanks @HertzDevil)\n- Encode OpenSSL version on Windows ([#11516](https://github.com/crystal-lang/crystal/pull/11516), thanks @HertzDevil)\n- Add docs to `Crypto::Bcrypt` ([#9647](https://github.com/crystal-lang/crystal/pull/9647), thanks @j8r)\n- Fix `getrandom` for interpreter ([#11624](https://github.com/crystal-lang/crystal/pull/11624), thanks @straight-shoota)\n- **(performance)** Use more efficient method to split `UInt32` to bytes in `Crypto::Blowfish` ([#11594](https://github.com/crystal-lang/crystal/pull/11594), thanks @BlobCodes)\n\n#### Files\n\n- Add bindings to `__xstat`, `__fxstat` and `__lxstat` for x86_64-linux-gnu ([#11361](https://github.com/crystal-lang/crystal/pull/11361), [#11536](https://github.com/crystal-lang/crystal/pull/11536), thanks @straight-shoota)\n- Fix `IO::Memory#to_s` appending to itself ([#11643](https://github.com/crystal-lang/crystal/pull/11643), thanks @straight-shoota)\n\n#### LLVM\n\n- Fix `LLVMExtDIBuilderCreateArrayType` argument `alignInBits` should be `UInt64` ([#11644](https://github.com/crystal-lang/crystal/pull/11644), thanks @lbguilherme)\n\n#### Log\n\n- Add `Log.with_context` with kwargs ([#11517](https://github.com/crystal-lang/crystal/pull/11517), thanks @caspiano)\n- Refactor `Log::BroadcastBackend#single_backend?` ([#11530](https://github.com/crystal-lang/crystal/pull/11530), thanks @straight-shoota)\n\n#### Macros\n\n- Add macro methods for `Return`, `Break`, `Next`, `Yield`, and exception handlers ([#10822](https://github.com/crystal-lang/crystal/pull/10822), thanks @HertzDevil)\n- Add `Crystal::Macros::ProcNotation#resolve` and `#resolve?` ([#11373](https://github.com/crystal-lang/crystal/pull/11373), thanks @HertzDevil)\n- Support explicit return types in `ProcLiteral`s ([#11402](https://github.com/crystal-lang/crystal/pull/11402), thanks @HertzDevil)\n- Add several missing `ASTNode` macro methods ([#10811](https://github.com/crystal-lang/crystal/pull/10811), thanks @HertzDevil)\n- Allow incomplete range arguments for `#[](Range)` macro methods ([#11380](https://github.com/crystal-lang/crystal/pull/11380), thanks @HertzDevil)\n- Add macro methods for `Metaclass` nodes ([#11375](https://github.com/crystal-lang/crystal/pull/11375), thanks @HertzDevil)\n\n#### Networking\n\n- Datagram support for `UNIXServer` ([#11426](https://github.com/crystal-lang/crystal/pull/11426), thanks @carlhoerberg)\n- Fix `WebSocket#stream` flushing for not exactly buffer size, add specs ([#11299](https://github.com/crystal-lang/crystal/pull/11299), thanks @will)\n- Fix flag for UNIX-like OS ([#11382](https://github.com/crystal-lang/crystal/pull/11382), thanks @straight-shoota)\n- Add more `check_headers` to `HTTP::Server::Response` ([#11253](https://github.com/crystal-lang/crystal/pull/11253), thanks @straight-shoota)\n- Enable `LogHandler` address for win32 ([#11465](https://github.com/crystal-lang/crystal/pull/11465), thanks @straight-shoota)\n- Enable two specs to run on all platforms ([#11467](https://github.com/crystal-lang/crystal/pull/11467), thanks @straight-shoota)\n- `TCPServer`: explain how to get an ephemeral port ([#11407](https://github.com/crystal-lang/crystal/pull/11407), thanks @rdp)\n- Fix `HTTP::Server::Response#close` when replaced output syncs close ([#11631](https://github.com/crystal-lang/crystal/pull/11631), thanks @straight-shoota)\n\n#### Numeric\n\n- **(breaking-change)** Fix `Random.rand(max : Float32)` return `Float32` ([#9946](https://github.com/crystal-lang/crystal/pull/9946), thanks @j8r)\n- Fix `Math` linking errors on Windows MSVC ([#11435](https://github.com/crystal-lang/crystal/pull/11435), thanks @HertzDevil)\n- Implement compiler-rt `__multi3` for arm ([#11499](https://github.com/crystal-lang/crystal/pull/11499), thanks @straight-shoota)\n- Use MPIR for `Big*` numbers on Windows ([#11412](https://github.com/crystal-lang/crystal/pull/11412), thanks @HertzDevil)\n- Add `BigRational#to_big_r` ([#11462](https://github.com/crystal-lang/crystal/pull/11462), thanks @HertzDevil)\n- Move specs for arithmetic primitives to `primitives_spec` ([#11298](https://github.com/crystal-lang/crystal/pull/11298), thanks @HertzDevil)\n- Implement compiler-rt's 128-bit integer conversions to/from floats ([#11437](https://github.com/crystal-lang/crystal/pull/11437), thanks @HertzDevil)\n- Fix `Number.significant` to return `0` as is, not as `Float64` ([#11321](https://github.com/crystal-lang/crystal/pull/11321), thanks @Sija)\n- Fix inequality for floating-point NaNs ([#11229](https://github.com/crystal-lang/crystal/pull/11229), thanks @HertzDevil)\n- Add workaround for 128-bit integer division/modulo on Windows ([#11551](https://github.com/crystal-lang/crystal/pull/11551), thanks @HertzDevil)\n- Reject near-boundary and NaN values for `Float`-to-`Int` conversions ([#11230](https://github.com/crystal-lang/crystal/pull/11230), thanks @HertzDevil)\n\n#### Runtime\n\n- GC/Boehm: Silence GC warnings about big allocations. ([#11289](https://github.com/crystal-lang/crystal/pull/11289), thanks @yxhuvud)\n- Disable impossible spec on win32, previously marked as pending ([#11451](https://github.com/crystal-lang/crystal/pull/11451), thanks @straight-shoota)\n- Support call stacks on Windows ([#11461](https://github.com/crystal-lang/crystal/pull/11461), thanks @HertzDevil)\n- Make Windows PDB lookup relative to running executable ([#11493](https://github.com/crystal-lang/crystal/pull/11493), thanks @HertzDevil)\n\n#### Serialization\n\n- Parses JSON `UInt64` numbers. ([#11395](https://github.com/crystal-lang/crystal/pull/11395), thanks @hugopl)\n- Fix `YAML::Any` deserialize with alias ([#11532](https://github.com/crystal-lang/crystal/pull/11532), thanks @straight-shoota)\n\n#### Specs\n\n- Use enums instead of symbols for `Spec`-related types ([#11585](https://github.com/crystal-lang/crystal/pull/11585), thanks @HertzDevil)\n\n#### System\n\n- Add native Linux syscall interface ([#10777](https://github.com/crystal-lang/crystal/pull/10777), thanks @lbguilherme)\n- Implement `Path.home` on Windows ([#11503](https://github.com/crystal-lang/crystal/pull/11503), thanks @HertzDevil)\n- Support `~\\` for Windows paths in `Path#expand` and `File.expand_path` ([#11559](https://github.com/crystal-lang/crystal/pull/11559), thanks @HertzDevil)\n- Support non-ASCII command-line arguments on Windows ([#11564](https://github.com/crystal-lang/crystal/pull/11564), thanks @HertzDevil)\n- Enable `kernel_spec.cr` on Windows CI ([#11554](https://github.com/crystal-lang/crystal/pull/11554), thanks @HertzDevil)\n- Fix `getrandom` syscall was blocking and didn't had proper error checking ([#11460](https://github.com/crystal-lang/crystal/pull/11460), thanks @lbguilherme)\n\n#### Text\n\n- Regex: use `PCRE_UCP` ([#11265](https://github.com/crystal-lang/crystal/pull/11265), thanks @asterite)\n- Add missing `it` in `UUID` spec ([#11353](https://github.com/crystal-lang/crystal/pull/11353), thanks @darkstego)\n- Add `Char#unicode_escape` and fix `#dump` and `#inspect` format ([#11421](https://github.com/crystal-lang/crystal/pull/11421), thanks @straight-shoota)\n- Fix `Char#letter?` to include all letter categories ([#11474](https://github.com/crystal-lang/crystal/pull/11474), thanks @straight-shoota)\n- Pass JIT Compile flag to `study` ([#11325](https://github.com/crystal-lang/crystal/pull/11325), thanks @Blacksmoke16)\n- Add Comparison operator to UUID ([#11352](https://github.com/crystal-lang/crystal/pull/11352), thanks @darkstego)\n- Add `Char#printable?` ([#11429](https://github.com/crystal-lang/crystal/pull/11429), thanks @straight-shoota)\n- Fix `String#inspect` and `Char#inspect` escape all non-printable characters ([#11452](https://github.com/crystal-lang/crystal/pull/11452), [#11626](https://github.com/crystal-lang/crystal/pull/11626), thanks @straight-shoota)\n- Support custom encodings on Windows through GNU libiconv ([#11480](https://github.com/crystal-lang/crystal/pull/11480), thanks @HertzDevil)\n- **(breaking-change)** Change `Regex#name_table` to return `Hash(Int32, String)` ([#11539](https://github.com/crystal-lang/crystal/pull/11539), thanks @straight-shoota)\n- Fix skip surrogates in `Char#succ` and `#pred` ([#11506](https://github.com/crystal-lang/crystal/pull/11506), thanks @straight-shoota)\n- **(performance)** Improve Base64 decoding performance ([#11094](https://github.com/crystal-lang/crystal/pull/11094), thanks @BlobCodes)\n- Refactor syntax highlighter and add ANSI escape code highlighter for console ([#11366](https://github.com/crystal-lang/crystal/pull/11366), thanks @straight-shoota)\n- Fix UTF-8 console input/output on Windows ([#11557](https://github.com/crystal-lang/crystal/pull/11557), thanks @HertzDevil)\n- Implement Unicode grapheme clusters ([#11472](https://github.com/crystal-lang/crystal/pull/11472), [#11611](https://github.com/crystal-lang/crystal/pull/11611), thanks @straight-shoota)\n- **(breaking-change)** Fix `Char#ascii_control?` restrict to ASCII characters ([#11510](https://github.com/crystal-lang/crystal/pull/11510), thanks @straight-shoota)\n- **(performance)** Performance: specify string sizes in advance ([#11592](https://github.com/crystal-lang/crystal/pull/11592), thanks @BlobCodes)\n- **(performance)** Improve performance of `Char#to_s` ([#11593](https://github.com/crystal-lang/crystal/pull/11593), thanks @BlobCodes)\n- Add docs to `Colorize` ([#11664](https://github.com/crystal-lang/crystal/pull/11664), thanks @straight-shoota)\n- Support ANSI escape sequence output on more Windows consoles ([#11622](https://github.com/crystal-lang/crystal/pull/11622), thanks @HertzDevil)\n\n### Tools\n\n- [docs] Fix ditto with additional lines ([#11336](https://github.com/crystal-lang/crystal/pull/11336), thanks @straight-shoota)\n- [docs] Compact some JSON fields for search ([#11438](https://github.com/crystal-lang/crystal/pull/11438), thanks @rymiel)\n- [docs] Add 404.html page ([#11428](https://github.com/crystal-lang/crystal/pull/11428), thanks @straight-shoota)\n- [docs] Improve search input a11y for generated docs ([#11604](https://github.com/crystal-lang/crystal/pull/11604), thanks @chances)\n- [docs] use `shard.yml` version when no git tag present ([#11232](https://github.com/crystal-lang/crystal/pull/11232), thanks @superhawk610)\n- [formatter] Fix weird interactions with comments near indentation ([#11441](https://github.com/crystal-lang/crystal/pull/11441), thanks @rymiel)\n- [formatter] fix extra newline after comment in case else ([#11448](https://github.com/crystal-lang/crystal/pull/11448), thanks @rymiel)\n- [formatter] Fix space between call name and parenthesized arg ([#11523](https://github.com/crystal-lang/crystal/pull/11523), thanks @straight-shoota)\n- [playground] Refactor `PlaygroundPage` resources list ([#11608](https://github.com/crystal-lang/crystal/pull/11608), thanks @straight-shoota)\n\n### Other\n\n- Update previous Crystal release - 1.2.2 ([#11430](https://github.com/crystal-lang/crystal/pull/11430), thanks @straight-shoota)\n- Prepare 1.3.0-dev ([#11317](https://github.com/crystal-lang/crystal/pull/11317), thanks @straight-shoota)\n- [CI] Fix `test_dist_linux_on_docker` ([#11512](https://github.com/crystal-lang/crystal/pull/11512), thanks @straight-shoota)\n- Improve compiler spec helpers for macro methods ([#11139](https://github.com/crystal-lang/crystal/pull/11139), thanks @HertzDevil)\n- Add Makefile to build samples ([#11419](https://github.com/crystal-lang/crystal/pull/11419), thanks @straight-shoota)\n- Verify downloads' hashes in Windows CI ([#11423](https://github.com/crystal-lang/crystal/pull/11423), thanks @matiasgarciaisaia)\n- Make the Windows compiler artifact more portable ([#11494](https://github.com/crystal-lang/crystal/pull/11494), thanks @HertzDevil)\n- Allow compiler executable under test to be overridden ([#11457](https://github.com/crystal-lang/crystal/pull/11457), thanks @HertzDevil)\n- Fix CI rules for building libiconv on Windows ([#11504](https://github.com/crystal-lang/crystal/pull/11504), thanks @HertzDevil)\n- Update license template ([#11498](https://github.com/crystal-lang/crystal/pull/11498), thanks @taupiqueur)\n- Pin alpine repo for ssl libs to 3.15 ([#11500](https://github.com/crystal-lang/crystal/pull/11500), thanks @straight-shoota)\n- Don't generate PDB for MPIR on Windows ([#11521](https://github.com/crystal-lang/crystal/pull/11521), thanks @HertzDevil)\n- [Makefile] Check for `LLVM_CONFIG` only when LLVM is used ([#11519](https://github.com/crystal-lang/crystal/pull/11519), thanks @straight-shoota)\n- Update distribution-scripts ([#11514](https://github.com/crystal-lang/crystal/pull/11514), [#11515](https://github.com/crystal-lang/crystal/pull/11515), thanks @straight-shoota)\n- Add commit hash to Windows builds ([#11538](https://github.com/crystal-lang/crystal/pull/11538), thanks @HertzDevil)\n- Support BuildTools and other VS variants in vswhere detection ([#11534](https://github.com/crystal-lang/crystal/pull/11534), thanks @neatorobito)\n- Define `LIBXML_STATIC` when building xml2.lib on Windows ([#11574](https://github.com/crystal-lang/crystal/pull/11574), thanks @HertzDevil)\n- Improve texts in `README.md` ([#11587](https://github.com/crystal-lang/crystal/pull/11587), thanks @athix)\n- Include `shards` with Windows build artifacts ([#11543](https://github.com/crystal-lang/crystal/pull/11543), thanks @neatorobito)\n- [CI] Remove `libatomic_ops` ([#11598](https://github.com/crystal-lang/crystal/pull/11598), thanks @straight-shoota)\n- Update NOTICE Copyright year to 2022 ([#11679](https://github.com/crystal-lang/crystal/pull/11679), thanks @matiasgarciaisaia)\n"
  },
  {
    "path": "doc/changelogs/v1.4.md",
    "content": "# Changelog 1.4\n\n## [1.4.1] - 2022-04-22\n\n[1.4.1]: https://github.com/crystal-lang/crystal/releases/1.4.1\n\n### Standard Library\n\n#### Collection\n\n- Avoid compile-time error on empty `NamedTuple`s. ([#12007](https://github.com/crystal-lang/crystal/pull/12007), thanks @I3oris)\n\n#### Files\n\n- Add missing fun def for `__xstat` ([#11985](https://github.com/crystal-lang/crystal/pull/11985), thanks @straight-shoota)\n\n#### Runtime\n\n- Add `pthread` link annotations in lib bindings ([#12013](https://github.com/crystal-lang/crystal/pull/12013), thanks @straight-shoota)\n- Fix GC typedefs on Windows ([#11963](https://github.com/crystal-lang/crystal/pull/11963), thanks @HertzDevil)\n\n### Compiler\n\n#### Semantic\n\n- Compiler: remove duplicate instance vars once we know them all ([#11995](https://github.com/crystal-lang/crystal/pull/11995), thanks @asterite)\n\n## [1.4.0] - 2022-04-06\n\n[1.4.0]: https://github.com/crystal-lang/crystal/releases/1.4.0\n\n### Language\n\n- Add support for `Int128` in codegen and macros ([#11576](https://github.com/crystal-lang/crystal/pull/11576), thanks @BlobCodes)\n- Support `ProcPointer`s with global path and top-level method references ([#11777](https://github.com/crystal-lang/crystal/pull/11777), thanks @HertzDevil)\n- Fix documentation for macros `system` and `run` ([#11740](https://github.com/crystal-lang/crystal/pull/11740), thanks @lbguilherme)\n- Experimental: better type inference for ivars/cvars ([#11812](https://github.com/crystal-lang/crystal/pull/11812), thanks @asterite)\n- Support `@[Deprecated]` on constants ([#11680](https://github.com/crystal-lang/crystal/pull/11680), thanks @HertzDevil)\n\n### Standard Library\n\n- Fix compiler flags with optional arg eating following flags ([#11201](https://github.com/crystal-lang/crystal/pull/11201), thanks @yb66)\n- Support GNU style optional arguments in `OptionParser` ([#11546](https://github.com/crystal-lang/crystal/pull/11546), thanks @HertzDevil)\n- Remove some unnecessary link annotations ([#11563](https://github.com/crystal-lang/crystal/pull/11563), thanks @straight-shoota)\n- Remove useless assignments ([#11774](https://github.com/crystal-lang/crystal/pull/11774), thanks @IgorPolyakov)\n- Use \"truthy\" and \"falsey\" in more places in the documentation ([#11784](https://github.com/crystal-lang/crystal/pull/11784), thanks @HertzDevil)\n- Add missing code blocks for `self` in documentation ([#11718](https://github.com/crystal-lang/crystal/pull/11718), thanks @HertzDevil)\n- Add support for LLVM 14.0 ([#11905](https://github.com/crystal-lang/crystal/pull/11905), thanks @HertzDevil)\n- Fix code examples in doc comments (2022-03) ([#11927](https://github.com/crystal-lang/crystal/pull/11927), thanks @maiha)\n\n#### Collection\n\n- Remove `Iterator.of(Iterator.stop)` from implementations ([#11613](https://github.com/crystal-lang/crystal/pull/11613), thanks @asterite)\n- Add allow `Enumerable` arguments for `Hash#select` and `#reject` ([#11750](https://github.com/crystal-lang/crystal/pull/11750), thanks @mamantoha)\n- Add docs for `Hash#reject!` ([#11691](https://github.com/crystal-lang/crystal/pull/11691), thanks @wontruefree)\n- Add raising method variants `Enumerable#find!` and `#index!` ([#11566](https://github.com/crystal-lang/crystal/pull/11566), thanks @yxhuvud)\n- **(performance)** Optimize block-less overloads of `BitArray#fill` ([#11450](https://github.com/crystal-lang/crystal/pull/11450), thanks @HertzDevil)\n- Adds docs for `Array#replace` ([#11682](https://github.com/crystal-lang/crystal/pull/11682), thanks @wontruefree)\n- **(performance)** Optimize `BitArray`'s counting methods ([#11591](https://github.com/crystal-lang/crystal/pull/11591), thanks @HertzDevil)\n- Add some return types to Array, Hash, Set and String ([#11822](https://github.com/crystal-lang/crystal/pull/11822), thanks @asterite)\n- Add `Hash#update` ([#11881](https://github.com/crystal-lang/crystal/pull/11881), thanks @ftarulla)\n- Allow `Bytes[]` to construct an empty `Bytes` ([#11897](https://github.com/crystal-lang/crystal/pull/11897), thanks @HertzDevil)\n- Improve `BitArray`'s constructors ([#11898](https://github.com/crystal-lang/crystal/pull/11898), thanks @HertzDevil)\n- Add overload to `Enumerable#tally` and `#tally_by` accepting a hash ([#11815](https://github.com/crystal-lang/crystal/pull/11815), thanks @mamantoha)\n\n#### Crypto\n\n- Add support for Bcrypt algorithm version `2b` ([#11595](https://github.com/crystal-lang/crystal/pull/11595), thanks @docelic)\n\n#### Files\n\n- Fix race condition in `chown` ([#11885](https://github.com/crystal-lang/crystal/pull/11885), thanks @didactic-drunk)\n- Add docs for `Dir#each_child` ([#11688](https://github.com/crystal-lang/crystal/pull/11688), thanks @wontruefree)\n- Add docs for `Dir#path` ([#11689](https://github.com/crystal-lang/crystal/pull/11689), thanks @wontruefree)\n- Support read-write + binary file modes in `File.open` ([#11817](https://github.com/crystal-lang/crystal/pull/11817), thanks @HertzDevil)\n- Add docs for `Dir#entries` ([#11701](https://github.com/crystal-lang/crystal/pull/11701), thanks @wontruefree)\n- Add `IO#getb_to_end` ([#11830](https://github.com/crystal-lang/crystal/pull/11830), thanks @HertzDevil)\n- Fix `IO::FileDescriptor#pos` giving incorrect position after write ([#10865](https://github.com/crystal-lang/crystal/pull/10865), thanks @didactic-drunk)\n- Remove reference to binary file mode in `File.open` ([#11824](https://github.com/crystal-lang/crystal/pull/11824), thanks @HertzDevil)\n\n#### Macros\n\n- Add `#parse_type` ([#11126](https://github.com/crystal-lang/crystal/pull/11126), thanks @Blacksmoke16)\n\n#### Networking\n\n- **(performance)** Optimize `URI.decode` ([#11741](https://github.com/crystal-lang/crystal/pull/11741), thanks @asterite)\n- Fix `address_spec` expectation for Windows Server 2022 ([#11794](https://github.com/crystal-lang/crystal/pull/11794), thanks @straight-shoota)\n- Add `space_to_plus` option in `URI::Params` everywhere ([#11821](https://github.com/crystal-lang/crystal/pull/11821), thanks @asterite)\n- Improve `URI::Params#inspect` to use hash-like literal ([#11880](https://github.com/crystal-lang/crystal/pull/11880), thanks @straight-shoota)\n- Use enums instead of symbols for `MIME::Multipart` and `HTTP::FormData` ([#11617](https://github.com/crystal-lang/crystal/pull/11617), thanks @HertzDevil)\n\n#### Numeric\n\n- **(breaking-change)** Fix: Hide `BigDecimal::ZERO` and `BigDecimal::TEN` ([#11820](https://github.com/crystal-lang/crystal/pull/11820), thanks @lbguilherme)\n- **(breaking-change)** Add support for scientific notation in `BigFloat#to_s` ([#10632](https://github.com/crystal-lang/crystal/pull/10632), thanks @HertzDevil)\n- Fix: Inspect string in error message for number parsing ([#11883](https://github.com/crystal-lang/crystal/pull/11883), thanks @straight-shoota)\n- Add docs for `Complex#zero?` ([#11697](https://github.com/crystal-lang/crystal/pull/11697), thanks @wontruefree)\n- Fix E notation parsing in `BigDecimal` ([#9577](https://github.com/crystal-lang/crystal/pull/9577), thanks @stevegeek)\n- **(performance)** Optimize Integer decoding from bytes ([#11796](https://github.com/crystal-lang/crystal/pull/11796), thanks @carlhoerberg)\n\n#### Runtime\n\n- Fix interpreter when shared library `pthread` is missing ([#11807](https://github.com/crystal-lang/crystal/pull/11807), thanks @straight-shoota)\n- **(performance)** Implement `Intrinsics.pause` for aarch64 ([#11742](https://github.com/crystal-lang/crystal/pull/11742), thanks @lbguilherme, @jgaskins)\n- Add crash handler on Windows ([#11570](https://github.com/crystal-lang/crystal/pull/11570), thanks @HertzDevil)\n- Add specs for `NamedTuple#from` ([#11816](https://github.com/crystal-lang/crystal/pull/11816), thanks @straight-shoota)\n- Improve error for incompatible generic arguments for `WeakRef` ([#11911](https://github.com/crystal-lang/crystal/pull/11911), thanks @straight-shoota)\n- Add the wasm entrypoint defined in Crystal ([#11936](https://github.com/crystal-lang/crystal/pull/11936), thanks @lbguilherme)\n\n#### Serialization\n\n- Allow passing instance method or conditional expressions to option `ignore_serialize` on `JSON::Field` ([#11804](https://github.com/crystal-lang/crystal/pull/11804), thanks @cyangle)\n- Implement `Iterator.from_json` and `#to_json` ([#10437](https://github.com/crystal-lang/crystal/pull/10437), thanks @wonderix)\n\n#### Specs\n\n- Add `file` and `line` arguments to `it_iterates` ([#11628](https://github.com/crystal-lang/crystal/pull/11628), thanks @straight-shoota)\n- Remove duplicate word in documentation ([#11797](https://github.com/crystal-lang/crystal/pull/11797), thanks @samueleaton)\n\n#### System\n\n- **(breaking-change)** **(security)** Fix character mappings for Windows path conversions ([#11847](https://github.com/crystal-lang/crystal/pull/11847), thanks @straight-shoota)\n- Add fallback for `Path.home` on Unix ([#11544](https://github.com/crystal-lang/crystal/pull/11544), thanks @HertzDevil)\n- Relax `ENV.fetch(key, &)`'s block restriction ([#11779](https://github.com/crystal-lang/crystal/pull/11779), thanks @HertzDevil)\n- Refactor target clauses for system implementations ([#11813](https://github.com/crystal-lang/crystal/pull/11813), thanks @straight-shoota)\n- Fix `Path` support for UNC shares ([#11827](https://github.com/crystal-lang/crystal/pull/11827), thanks @straight-shoota)\n- Fix regression for Linux older than 3.17: properly check that `getrandom` is available ([#11953](https://github.com/crystal-lang/crystal/pull/11953), thanks @lbguilherme)\n\n#### Text\n\n- Fix ensure PCRE JIT mode is available before running spec ([#11533](https://github.com/crystal-lang/crystal/pull/11533), thanks @Blacksmoke16)\n- Add more `Colorize` overloads and fix docs ([#11832](https://github.com/crystal-lang/crystal/pull/11832), thanks @asterite)\n- Refactor `Colorize::Mode` enum ([#11663](https://github.com/crystal-lang/crystal/pull/11663), thanks @straight-shoota)\n- Add some docs for `UUID` ([#11683](https://github.com/crystal-lang/crystal/pull/11683), thanks @wontruefree)\n- Add docs for `UUID#urn` ([#11693](https://github.com/crystal-lang/crystal/pull/11693), thanks @wontruefree)\n- Add docs for `String#[]` ([#11690](https://github.com/crystal-lang/crystal/pull/11690), thanks @wontruefree)\n- Allow explicit usage of `libiconv` ([#11876](https://github.com/crystal-lang/crystal/pull/11876), thanks @luislavena)\n- **(breaking-change)** Fix: Honour encoding in `IO::Memory#to_s` ([#11875](https://github.com/crystal-lang/crystal/pull/11875), thanks @straight-shoota)\n- Add `self` return type to `UUID` constructor methods ([#10539](https://github.com/crystal-lang/crystal/pull/10539), thanks @straight-shoota)\n- Fix infinite loop for certain `StringPool` initial capacities ([#11929](https://github.com/crystal-lang/crystal/pull/11929), thanks @HertzDevil)\n\n#### Time\n\n- Add examples to `Time::Format` methods ([#11713](https://github.com/crystal-lang/crystal/pull/11713), thanks @ThunderKey)\n- Support day of year (`%j`) in `Time` parsers ([#11791](https://github.com/crystal-lang/crystal/pull/11791), thanks @HertzDevil)\n\n### Compiler\n\n- Hello WebAssembly! (MVP implementation) ([#10870](https://github.com/crystal-lang/crystal/pull/10870), thanks @lbguilherme)\n- Fix compiler specs git integration for configurable default branch ([#11754](https://github.com/crystal-lang/crystal/pull/11754), thanks @yxhuvud)\n- `Crystal::ToSVisitor`: Remove `decorate_*` methods ([#11724](https://github.com/crystal-lang/crystal/pull/11724), thanks @HertzDevil)\n- Use fewer symbols in the compiler source ([#11625](https://github.com/crystal-lang/crystal/pull/11625), thanks @HertzDevil)\n- Add support for `--m*` flags to `crystal spec` and `crystal eval` ([#11767](https://github.com/crystal-lang/crystal/pull/11767), thanks @yxhuvud)\n- Add local compiler wrapper script for Windows ([#11524](https://github.com/crystal-lang/crystal/pull/11524), thanks @HertzDevil)\n- Add `Crystal::Program#check_deprecation` ([#11684](https://github.com/crystal-lang/crystal/pull/11684), thanks @HertzDevil)\n- Refactor symbols for primitive number kinds to enums ([#11621](https://github.com/crystal-lang/crystal/pull/11621), thanks @HertzDevil)\n- Remove useless assignments II ([#11843](https://github.com/crystal-lang/crystal/pull/11843), thanks @IgorPolyakov)\n- Limit the number of rendered overloads on signature mismatch ([#10890](https://github.com/crystal-lang/crystal/pull/10890), thanks @caspiano)\n- Support \"can't infer type parameter\" error for uninstantiated generic modules ([#11904](https://github.com/crystal-lang/crystal/pull/11904), thanks @HertzDevil)\n- Fix: Accept only option flags in `CRYSTAL_OPTS` for build commands ([#11922](https://github.com/crystal-lang/crystal/pull/11922), thanks  @HertzDevil, @beta-ziliani)\n- Evaluate `LibLLVM::IS_LT_*` during macro expansion time ([#11913](https://github.com/crystal-lang/crystal/pull/11913), thanks @HertzDevil)\n- Fix incorrect var type inside nested exception handler ([#11928](https://github.com/crystal-lang/crystal/pull/11928), thanks @asterite)\n- Fix: Look up return type in defining type ([#11962](https://github.com/crystal-lang/crystal/pull/11962), thanks @asterite)\n\n#### Codegen\n\n- **(performance)** Codegen: Do not always request value for `Proc#call` ([#11675](https://github.com/crystal-lang/crystal/pull/11675), thanks @HertzDevil)\n\n#### Debugger\n\n- Fix debug location of inlined `Proc#call` body ([#11676](https://github.com/crystal-lang/crystal/pull/11676), thanks @HertzDevil)\n\n#### Generics\n\n- Resolve non-type free variables in return type restrictions ([#11861](https://github.com/crystal-lang/crystal/pull/11861), thanks @HertzDevil)\n- Fix recursive `pointerof` detection with generic splat type variables ([#11811](https://github.com/crystal-lang/crystal/pull/11811), thanks @HertzDevil)\n\n#### Interpreter\n\n- Fix for Crystal interpreter crash ([#11717](https://github.com/crystal-lang/crystal/pull/11717), thanks @wmoxam)\n- Interpreter: support `Tuple#[]` with range literals ([#11783](https://github.com/crystal-lang/crystal/pull/11783), thanks @HertzDevil)\n- Interpreter: Promote arguments of variadic function calls ([#11795](https://github.com/crystal-lang/crystal/pull/11795), thanks @straight-shoota)\n- Check if libraries are present using `dlopen` ([#11852](https://github.com/crystal-lang/crystal/pull/11852), thanks @FnControlOption)\n- Use `Crystal::Loader` in the interpreter ([#11579](https://github.com/crystal-lang/crystal/pull/11579), thanks @straight-shoota)\n- Improve `Crystal::Loader` errors ([#11860](https://github.com/crystal-lang/crystal/pull/11860), thanks @straight-shoota)\n- Enable interpreter integration specs for `YAML` ([#11801](https://github.com/crystal-lang/crystal/pull/11801), thanks @straight-shoota)\n\n#### Parser\n\n- Fix parser error with semicolon + newline in parenthesized `Expressions` ([#11769](https://github.com/crystal-lang/crystal/pull/11769), thanks @straight-shoota)\n- Fix comment indentation in `ASTNode#to_s` ([#11851](https://github.com/crystal-lang/crystal/pull/11851), thanks @FnControlOption)\n- Add locations for `else`, `ensure`, `end` keywords ([#11476](https://github.com/crystal-lang/crystal/pull/11476), thanks @FnControlOption)\n- Add parser support to handle CRLF the same as LF ([#11810](https://github.com/crystal-lang/crystal/pull/11810), thanks @asterite)\n- Lexer: use `Crystal::Token::Kind` enum instead of symbols ([#11616](https://github.com/crystal-lang/crystal/pull/11616), thanks @HertzDevil)\n- Support `Generic` nodes with no type variables ([#11906](https://github.com/crystal-lang/crystal/pull/11906), thanks @HertzDevil)\n\n#### Semantic\n\n- **(breaking-change)** Drop `skip_abstract_def_check` flag support ([#9217](https://github.com/crystal-lang/crystal/pull/9217), thanks @makenowjust)\n- Add error when instance variable is inherited from module and supertype ([#11768](https://github.com/crystal-lang/crystal/pull/11768), thanks @straight-shoota)\n- Check file-private types for abstract defs and recursive structs ([#11838](https://github.com/crystal-lang/crystal/pull/11838), thanks @HertzDevil)\n- Fix: Prevent eager `instance_sizeof` on structs ([#11856](https://github.com/crystal-lang/crystal/pull/11856), thanks @mattrberry)\n- Fix: Do not consider global `Path` in def parameter restriction as free variable ([#11862](https://github.com/crystal-lang/crystal/pull/11862), thanks @HertzDevil)\n\n### Tools\n\n- Do not inherit from `Hash` in the compiler ([#11707](https://github.com/crystal-lang/crystal/pull/11707), thanks @HertzDevil)\n- Use `OptionParser` in `crystal env` ([#11720](https://github.com/crystal-lang/crystal/pull/11720), thanks @HertzDevil)\n\n#### Playground\n\n- Replace PNG icon with optimized SVG for playground ([#7616](https://github.com/crystal-lang/crystal/pull/7616), thanks @straight-shoota)\n\n### Other\n\n- Update previous Crystal release - 1.3.2 ([#11715](https://github.com/crystal-lang/crystal/pull/11715), thanks @straight-shoota)\n- Add `scripts/release-update.sh` ([#11716](https://github.com/crystal-lang/crystal/pull/11716), thanks @straight-shoota)\n- [Makefile] Honour `$CC` in `EXPORT_CC` ([#11548](https://github.com/crystal-lang/crystal/pull/11548), thanks @straight-shoota)\n- Fix typo in GHA aarch64 config ([#11793](https://github.com/crystal-lang/crystal/pull/11793), thanks @straight-shoota)\n- [CI] Test against LLVM 13 ([#11343](https://github.com/crystal-lang/crystal/pull/11343), thanks @straight-shoota)\n- [CI] Use parameters in circleci config ([#11714](https://github.com/crystal-lang/crystal/pull/11714), thanks @straight-shoota)\n- Refactor `etc/completion.bash` ([#11719](https://github.com/crystal-lang/crystal/pull/11719), thanks @HertzDevil)\n- [CI] Renaming jobs to be `arch-os-job` or `arch-build-job` ([#11207](https://github.com/crystal-lang/crystal/pull/11207), thanks @beta-ziliani)\n- Improve documentation for review and merge processes ([#11800](https://github.com/crystal-lang/crystal/pull/11800), thanks @straight-shoota)\n- Improve section ordering in `scripts/github-changelog.cr` ([#11770](https://github.com/crystal-lang/crystal/pull/11770), thanks @straight-shoota)\n- Upload build artifacts to S3 bucket ([#11902](https://github.com/crystal-lang/crystal/pull/11902), thanks @matiasgarciaisaia, @straight-shoota)\n- Fix `make install` for BSD ([#11758](https://github.com/crystal-lang/crystal/pull/11758), thanks @straight-shoota)\n- Fix typo ([#11939](https://github.com/crystal-lang/crystal/pull/11939), thanks @taupiqueur)\n- [CI] Update to shards 0.17.0 ([#11930](https://github.com/crystal-lang/crystal/pull/11930), thanks @straight-shoota)\n- Use `be_empty` expectations in more specs ([#11937](https://github.com/crystal-lang/crystal/pull/11937), thanks @HertzDevil)\n- [CI] Update distribution-scripts ([#11969](https://github.com/crystal-lang/crystal/pull/11969), thanks @straight-shoota)\n"
  },
  {
    "path": "doc/changelogs/v1.5.md",
    "content": "# Changelog 1.5\n\n## [1.5.1] - 2022-09-07\n\n[1.5.1]: https://github.com/crystal-lang/crystal/releases/1.5.1\n\n### Standard Library\n\n- Fix `Class#nilable?` for recursive unions and root types ([#12353](https://github.com/crystal-lang/crystal/pull/12353), thanks @HertzDevil)\n\n#### Numeric\n\n- Fix `Float#abs` and `Number#format` for `-0.0` ([#12424](https://github.com/crystal-lang/crystal/pull/12424), thanks @straight-shoota)\n\n#### Text\n\n- Fix null dereference when passing empty slice to `Base64.encode` ([#12377](https://github.com/crystal-lang/crystal/pull/12377), thanks @dscottboggs)\n\n### Compiler\n\n- Do not check abstract def parameter names on abstract types and modules ([#12434](https://github.com/crystal-lang/crystal/pull/12434), thanks @HertzDevil)\n\n#### Codegen\n\n- Compiler/codegen: reset `@needs_value` ([#12444](https://github.com/crystal-lang/crystal/pull/12444), thanks @asterite)\n- Fix `homogeneous_aggregate?` check for aarch64 types ([#12445](https://github.com/crystal-lang/crystal/pull/12445), thanks @mattrberry)\n\n#### Semantic\n\n- Compiler: don't eagerly check cast type ([#12272](https://github.com/crystal-lang/crystal/pull/12272), thanks @asterite)\n- Fix type restriction augmenter for `Union(*T)` and similar ([#12438](https://github.com/crystal-lang/crystal/pull/12438), thanks @asterite)\n\n### Tools\n\n#### Formatter\n\n- Formatter: Fix assign followed by comment ([#12319](https://github.com/crystal-lang/crystal/pull/12319), thanks @straight-shoota)\n- Handle formatting annotated method parameters ([#12446](https://github.com/crystal-lang/crystal/pull/12446), thanks @Blacksmoke16)\n\n### Other\n\n- Update distribution-scripts ([#12359](https://github.com/crystal-lang/crystal/pull/12359), thanks @straight-shoota)\n- Update distribution-scripts ([#12333](https://github.com/crystal-lang/crystal/pull/12333), thanks @straight-shoota)\n- [CI] Bumping xcode to 13.4.1 ([#12264](https://github.com/crystal-lang/crystal/pull/12264), thanks @beta-ziliani)\n\n## [1.5.0] - 2022-07-06\n\n[1.5.0]: https://github.com/crystal-lang/crystal/releases/1.5.0\n\n### Language\n\n- **(breaking-change)** Warn on positional parameter mismatches for abstract def implementations ([#11915](https://github.com/crystal-lang/crystal/pull/11915), [#12167](https://github.com/crystal-lang/crystal/pull/12167), thanks @HertzDevil)\n- Fix `\\{{...}}` syntax in macro inside comments ([#12175](https://github.com/crystal-lang/crystal/pull/12175), thanks @asterite)\n- Let constant tuple indexers work with constants ([#12012](https://github.com/crystal-lang/crystal/pull/12012), thanks @asterite)\n- Refactor restriction mechanism for autocasting ([#12014](https://github.com/crystal-lang/crystal/pull/12014), thanks @HertzDevil)\n- Support unions in `Crystal::Macros::ASTNode#is_a?` ([#12086](https://github.com/crystal-lang/crystal/pull/12086), thanks @HertzDevil)\n- Experimental: restriction augmenter ([#12103](https://github.com/crystal-lang/crystal/pull/12103), [#12136](https://github.com/crystal-lang/crystal/pull/12136), [#12143](https://github.com/crystal-lang/crystal/pull/12143), [#12130](https://github.com/crystal-lang/crystal/pull/12130), thanks @asterite)\n- Method/macro parameter annotation support ([#12044](https://github.com/crystal-lang/crystal/pull/12044), thanks @Blacksmoke16)\n\n### Standard Library\n\n- Support `Path` for `chdir` arg in `Process` methods ([#11932](https://github.com/crystal-lang/crystal/pull/11932), thanks @caspiano)\n- Add docs for `Enum#value` ([#11947](https://github.com/crystal-lang/crystal/pull/11947), thanks @lbguilherme)\n- Fix positional parameter warnings in specs ([#12158](https://github.com/crystal-lang/crystal/pull/12158), thanks @straight-shoota)\n- Use more specific expectations in specs ([#11951](https://github.com/crystal-lang/crystal/pull/11951), thanks @HertzDevil)\n- Use `contain` expectations in more specs ([#11950](https://github.com/crystal-lang/crystal/pull/11950), thanks @HertzDevil)\n\n#### Collection\n\n- Fix `Hash#reject!` for non-equality key ([#10511](https://github.com/crystal-lang/crystal/pull/10511), thanks @straight-shoota)\n- Introduce `Tuple.element_type` and `NamedTuple.element_type` ([#12011](https://github.com/crystal-lang/crystal/pull/12011), thanks @HertzDevil)\n- Rename \"take\" to \"first\" ([#11988](https://github.com/crystal-lang/crystal/pull/11988), thanks @jmdyck)\n- Add spec for `Array#-` with different generic type arguments ([#12049](https://github.com/crystal-lang/crystal/pull/12049), thanks @straight-shoota)\n\n#### Concurrency\n\n- Windows: Always use `GC_set_stackbottom` on Windows ([#12186](https://github.com/crystal-lang/crystal/pull/12186), thanks @HertzDevil)\n- Windows: Event loop based on IOCP ([#12149](https://github.com/crystal-lang/crystal/pull/12149), thanks @straight-shoota, @wonderix, @yxhuvud, @HertzDevil)\n- Use enum instead of symbol for `Atomic` primitives ([#11583](https://github.com/crystal-lang/crystal/pull/11583), thanks @HertzDevil)\n- Allow `Enumerable(Channel)` parameter for  `Channel.send_first`, `.receive_first` ([#12101](https://github.com/crystal-lang/crystal/pull/12101), thanks @carlhoerberg)\n\n#### Crypto\n\n- **(performance)** Add faster `Digest#hexfinal` ([#9292](https://github.com/crystal-lang/crystal/pull/9292), thanks @didactic-drunk)\n- Handle OpenSSL 3.0 KTLS ctrl calls ([#12034](https://github.com/crystal-lang/crystal/pull/12034), thanks @1player)\n\n#### Files\n\n- Fix `Path#join(Enumerable)` ([#12032](https://github.com/crystal-lang/crystal/pull/12032), thanks @straight-shoota)\n- Fix `Path#join` to convert argument path to base kind ([#12033](https://github.com/crystal-lang/crystal/pull/12033), thanks @straight-shoota)\n- Fix `Dir.glob` with multi components after recursive wildcard ([#12057](https://github.com/crystal-lang/crystal/pull/12057), thanks @straight-shoota)\n- Add `File#delete?` and `Dir#delete?` ([#11887](https://github.com/crystal-lang/crystal/pull/11887), thanks @didactic-drunk)\n- Accept `Path` arguments in `Compress::Zip` ([#11925](https://github.com/crystal-lang/crystal/pull/11925), thanks @didactic-drunk)\n- Update file.cr ([#12024](https://github.com/crystal-lang/crystal/pull/12024), thanks @rdp)\n- Add `File#chown` and `#chmod` ([#11886](https://github.com/crystal-lang/crystal/pull/11886), thanks @didactic-drunk)\n\n#### Log\n\n- Change `Log` emitters to not emit event when block output is `nil` ([#12000](https://github.com/crystal-lang/crystal/pull/12000), thanks @robacarp)\n\n#### Networking\n\n- Enable more networking specs on Windows ([#12176](https://github.com/crystal-lang/crystal/pull/12176), thanks @HertzDevil)\n- Add specs for Windows directory separators in `StaticFileHandler` paths ([#11884](https://github.com/crystal-lang/crystal/pull/11884), thanks @straight-shoota)\n- Add property `HTTP::Server::Response#status_message` ([#10416](https://github.com/crystal-lang/crystal/pull/10416), thanks @straight-shoota)\n\n#### Numeric\n\n- Fix `Complex.multiplicative_identity` ([#12051](https://github.com/crystal-lang/crystal/pull/12051), thanks @I3oris)\n- Add docs for `Float`, `BigFloat` rounding methods ([#12004](https://github.com/crystal-lang/crystal/pull/12004), thanks @marksiemers)\n- Implement rt builtins `__ashlti3`, `__ashrti3` and `__lshrti3` for wasm32 ([#11948](https://github.com/crystal-lang/crystal/pull/11948), thanks @lbguilherme)\n\n#### Specs\n\n- Align `Spec::Be`, `BeClose` failure message to other messages ([#11946](https://github.com/crystal-lang/crystal/pull/11946), thanks @jgaskins)\n\n#### System\n\n- **(security)** Fix check for null byte in `File#tempfile` args ([#12076](https://github.com/crystal-lang/crystal/pull/12076), thanks @straight-shoota)\n- Add missing `SC_PAGESIZE` constant for `aarch64-darwin` ([#12037](https://github.com/crystal-lang/crystal/pull/12037), thanks @carlhoerberg)\n- Docs: Add more prominent note about path traversal in `File.tempfile` ([#12077](https://github.com/crystal-lang/crystal/pull/12077), thanks @straight-shoota)\n- Support `Enumerable` as argument to `File.join` ([#12102](https://github.com/crystal-lang/crystal/pull/12102), thanks @caspiano)\n\n#### Runtime\n\n- Mention `#value` explicitly in `Pointer` overview. ([#12184](https://github.com/crystal-lang/crystal/pull/12184), thanks @elebow)\n\n#### Text\n\n- Add specs for `String#char_bytesize_at` ([#11872](https://github.com/crystal-lang/crystal/pull/11872), thanks @straight-shoota)\n- Flush shift state for `String#encode` ([#11993](https://github.com/crystal-lang/crystal/pull/11993), thanks @HertzDevil)\n- Handle invalid bytes in single-byte optimizable `String`s correctly ([#12108](https://github.com/crystal-lang/crystal/pull/12108), thanks @HertzDevil)\n- Fix: Don't stop on null byte in `String#%` ([#12125](https://github.com/crystal-lang/crystal/pull/12125), thanks @asterite)\n- Add `UUID.parse?` ([#11998](https://github.com/crystal-lang/crystal/pull/11998), thanks @jgaskins)\n\n#### Time\n\n- Fix: Better error message for `Time.parse!` when end of input is reached ([#12124](https://github.com/crystal-lang/crystal/pull/12124), thanks @asterite)\n\n### Compiler\n\n- Clean up compiler warning specs ([#11916](https://github.com/crystal-lang/crystal/pull/11916), thanks @HertzDevil)\n- Add support for `NO_COLOR` to `Colorize` ([#11984](https://github.com/crystal-lang/crystal/pull/11984), thanks @didactic-drunk)\n- **(performance)** Use LLVM's new pass manager when possible ([#12116](https://github.com/crystal-lang/crystal/pull/12116), thanks @asterite)\n\n#### Macros\n\n- Document `Crystal::Macros::Self` and `Underscore` ([#12085](https://github.com/crystal-lang/crystal/pull/12085), thanks @HertzDevil)\n\n#### Generics\n\n- Allow the empty instantiation `NamedTuple()` ([#12009](https://github.com/crystal-lang/crystal/pull/12009), thanks @HertzDevil)\n\n#### Interpreter\n\n- Add missing `EXPORT` in interpreter spec ([#12201](https://github.com/crystal-lang/crystal/pull/12201), thanks @HertzDevil)\n- Handle escaping exceptions in pry ([#12211](https://github.com/crystal-lang/crystal/pull/12211), thanks @asterite)\n- Allow some options, and colorize whereami ([#12198](https://github.com/crystal-lang/crystal/pull/12198), thanks @asterite)\n- Fix instance var offset of virtual struct ([#12189](https://github.com/crystal-lang/crystal/pull/12189), thanks @asterite)\n- Handle explicit return when method type is Nil ([#12179](https://github.com/crystal-lang/crystal/pull/12179), thanks @asterite)\n- `Crystal::Loader`: don't check if file exists, leave that to dlopen ([#12207](https://github.com/crystal-lang/crystal/pull/12207), thanks @asterite)\n- Fix call receiver by value from VirtualType struct ([#12194](https://github.com/crystal-lang/crystal/pull/12194), thanks @asterite)\n- Clear finished hooks after interpreting ([#12174](https://github.com/crystal-lang/crystal/pull/12174), thanks @asterite)\n- Fix FFI bindings for libffi >= 3.4 ([#12192](https://github.com/crystal-lang/crystal/pull/12192), thanks @straight-shoota)\n- Fix `.class` for modules and unions ([#12205](https://github.com/crystal-lang/crystal/pull/12205), thanks @asterite)\n- Implement `Crystal::Loader` for MSVC ([#12140](https://github.com/crystal-lang/crystal/pull/12140), thanks @HertzDevil)\n- Fix: cast from virtual metaclass to union ([#12163](https://github.com/crystal-lang/crystal/pull/12163), thanks @asterite)\n- Allow inspect vars when inside a block ([#12165](https://github.com/crystal-lang/crystal/pull/12165), thanks @asterite)\n- Let pry see closured vars ([#12169](https://github.com/crystal-lang/crystal/pull/12169), thanks @asterite)\n- Fix caller ([#12182](https://github.com/crystal-lang/crystal/pull/12182), thanks @asterite)\n- Apply shell expansion in ldflags ([#12094](https://github.com/crystal-lang/crystal/pull/12094), thanks @mdwagner)\n- Fix expression value of constant assignment in interpreter ([#12016](https://github.com/crystal-lang/crystal/pull/12016), thanks @beta-ziliani)\n- Fix: Don't link `librt` and `libdl` on GNU systems ([#12038](https://github.com/crystal-lang/crystal/pull/12038), thanks @1player)\n\n#### Parser\n\n- **(breaking-change)** Disallow empty parameter and argument names ([#11971](https://github.com/crystal-lang/crystal/pull/11971), thanks @HertzDevil)\n- Disallow duplicate free variables in defs ([#11965](https://github.com/crystal-lang/crystal/pull/11965), thanks @HertzDevil)\n- Disallow duplicate `fun` parameter names ([#11967](https://github.com/crystal-lang/crystal/pull/11967), thanks @HertzDevil)\n- Remove redundant check for EOF on `Crystal::Parser` ([#12067](https://github.com/crystal-lang/crystal/pull/12067), thanks @lbguilherme)\n\n#### Semantic\n\n- Compiler: don't check ivar read forms a closure in `exp.@x` ([#12183](https://github.com/crystal-lang/crystal/pull/12183), thanks @asterite)\n- Compiler: raise when allocating an abstract virtual type ([#12141](https://github.com/crystal-lang/crystal/pull/12141), thanks @asterite)\n- Compiler: don't use `with_scope` if Call has a receiver ([#12138](https://github.com/crystal-lang/crystal/pull/12138), thanks @asterite)\n- Compiler: fix proc return type restriction for `Proc(...)` ([#12129](https://github.com/crystal-lang/crystal/pull/12129), thanks @asterite)\n- Compiler: simpler way to compute `Def#raises?` ([#12121](https://github.com/crystal-lang/crystal/pull/12121), thanks @asterite)\n- Remove unused `ASTNode#unbind_all` ([#12120](https://github.com/crystal-lang/crystal/pull/12120), thanks @asterite)\n\n#### Debugger\n\n- Improve the LLDB spec driver script ([#12119](https://github.com/crystal-lang/crystal/pull/12119), thanks @HertzDevil)\n\n### Tools\n\n#### Docs-generator\n\n- [Docs] Adjust method sort order to sort all operators first ([#12104](https://github.com/crystal-lang/crystal/pull/12104), thanks @straight-shoota)\n\n#### Formatter\n\n- Fix formatter lib-fun declaration with newlines ([#12071](https://github.com/crystal-lang/crystal/pull/12071), thanks @ftarulla)\n- Fix formatter alias-def with no-space before equals ([#12073](https://github.com/crystal-lang/crystal/pull/12073), thanks @ftarulla)\n- Fix formatter for parenthesized arg after space ([#11972](https://github.com/crystal-lang/crystal/pull/11972), thanks @straight-shoota)\n\n#### Playground\n\n- Playground: fix `modalContentDom` typo ([#12188](https://github.com/crystal-lang/crystal/pull/12188), thanks @HertzDevil)\n- Fix: Unset executable bit from js/css files in playground ([#12053](https://github.com/crystal-lang/crystal/pull/12053), thanks @carlhoerberg)\n\n### Other\n\n- [CI] Add build compiler step to smoke tests ([#11814](https://github.com/crystal-lang/crystal/pull/11814), thanks @straight-shoota)\n- Add Makefile for Windows ([#11773](https://github.com/crystal-lang/crystal/pull/11773), thanks @HertzDevil)\n- [CI] Update distribution-scripts ([#12155](https://github.com/crystal-lang/crystal/pull/12155), thanks @straight-shoota)\n- [CI] Add `update-distributions-scripts.sh` ([#12156](https://github.com/crystal-lang/crystal/pull/12156), thanks @straight-shoota)\n- [CI] Ignore `pax_global_header` on Windows CI ([#12173](https://github.com/crystal-lang/crystal/pull/12173), thanks @HertzDevil)\n- [CI] Invalidate cached libraries on new MSVC release ([#12064](https://github.com/crystal-lang/crystal/pull/12064), thanks @HertzDevil)\n- Fix spelling ([#12040](https://github.com/crystal-lang/crystal/pull/12040), thanks @jsoref)\n- Update previous Crystal release - 1.4.1 ([#12029](https://github.com/crystal-lang/crystal/pull/12029), thanks @straight-shoota)\n- [CI] Pin version of ubuntu base image for circleci jobs ([#12030](https://github.com/crystal-lang/crystal/pull/12030), thanks @straight-shoota)\n- Samples: avoid `Symbol` variables ([#11923](https://github.com/crystal-lang/crystal/pull/11923), thanks @HertzDevil)\n"
  },
  {
    "path": "doc/changelogs/v1.6.md",
    "content": "# Changelog 1.6\n\n## [1.6.2] - 2022-11-03\n\n[1.6.2]: https://github.com/crystal-lang/crystal/releases/1.6.2\n\n### Language\n\n- Fix `VirtualMetaclassType#implements?` to ignore base type ([#12632](https://github.com/crystal-lang/crystal/pull/12632), thanks @straight-shoota)\n\n### Compiler\n\n- Compiler: handle yield expressions without a type ([#12679](https://github.com/crystal-lang/crystal/pull/12679), thanks @asterite)\n- Partial revert \"Compiler: refactor and slightly optimize merging two types (#12436)\" ([#12709](https://github.com/crystal-lang/crystal/pull/12709), thanks @caspiano)\n\n#### Semantic\n\n- Compiler: ignore type filters when accepting cast for `obj` and `to` ([#12668](https://github.com/crystal-lang/crystal/pull/12668), thanks @asterite)\n\n### Other\n\n- **(security)** CI: Update to OpenSSL 3.0.7 for bundled lib on Windows ([#12712](https://github.com/crystal-lang/crystal/pull/12712), thanks @beta-ziliani)\n\n## [1.6.1] - 2022-10-21\n\n[1.6.1]: https://github.com/crystal-lang/crystal/releases/1.6.1\n\n### Compiler\n\n#### Interpreter\n\n- Interpreter (repl): migrate types even if their size remains the same ([#12581](https://github.com/crystal-lang/crystal/pull/12581), thanks @asterite)\n- Unbreak the interpreter on FreeBSD ([#12600](https://github.com/crystal-lang/crystal/pull/12600), thanks @dmgk)\n- Fix FFI specs on release builds ([#12601](https://github.com/crystal-lang/crystal/pull/12601), thanks @HertzDevil)\n- Adding welcome message to the interpreter ([#12511](https://github.com/crystal-lang/crystal/pull/12511), thanks @beta-ziliani)\n\n#### Semantic\n\n- Treat single splats with same restriction as equivalent ([#12584](https://github.com/crystal-lang/crystal/pull/12584), thanks @HertzDevil)\n\n### Tools\n\n#### Formatter\n\n- Formatter: escape backslashes in macro literals when subformatting ([#12582](https://github.com/crystal-lang/crystal/pull/12582), thanks @asterite)\n\n#### Playground\n\n- Fix origin validation in playground server for localhost ([#12599](https://github.com/crystal-lang/crystal/pull/12599), thanks @straight-shoota)\n\n### Other\n\n- Fix doc typos in `Socket::IPAddress` ([#12583](https://github.com/crystal-lang/crystal/pull/12583), thanks @Blacksmoke16)\n- Fix building Wasm32 on Crystal 1.6 (Regression) ([#12580](https://github.com/crystal-lang/crystal/pull/12580), thanks @lbguilherme)\n- Bump version to 1.6.1-dev ([#12588](https://github.com/crystal-lang/crystal/pull/12588), thanks @straight-shoota)\n- Disable failing specs on Windows CI ([#12585](https://github.com/crystal-lang/crystal/pull/12585), thanks @HertzDevil)\n- Detect `llvm-configXX` while building compiler ([#12602](https://github.com/crystal-lang/crystal/pull/12602), thanks @HertzDevil)\n\n## [1.6.0] - 2022-10-06\n\n[1.6.0]: https://github.com/crystal-lang/crystal/releases/1.6.0\n\n### Language\n\n- Add 'wasm_import_module' option to the `@[Link]` annotation ([#11935](https://github.com/crystal-lang/crystal/pull/11935), thanks @lbguilherme)\n\n### Standard Library\n\n- Use `GC.malloc_atomic` with `GC.realloc`, not `Pointer#realloc` ([#12391](https://github.com/crystal-lang/crystal/pull/12391), thanks @HertzDevil)\n- Improve syntax highlighter ([#12409](https://github.com/crystal-lang/crystal/pull/12409), thanks @I3oris)\n- Enable miscellaneous parts of the standard library on Windows ([#12281](https://github.com/crystal-lang/crystal/pull/12281), thanks @HertzDevil)\n- Use interpreter to run std spec tests ([#12355](https://github.com/crystal-lang/crystal/pull/12355), thanks @cyangle)\n- Remove most uses of `Symbol` variables in standard library specs ([#12462](https://github.com/crystal-lang/crystal/pull/12462), thanks @HertzDevil)\n- Use `@[::Primitive]` and `@[::Flags]` where necessary ([#11900](https://github.com/crystal-lang/crystal/pull/11900), thanks @HertzDevil)\n- Document how to change base type of an enum ([#9803](https://github.com/crystal-lang/crystal/pull/9803), thanks @Blacksmoke16)\n- Spec: bump and document timeouts in interpreted mode ([#12430](https://github.com/crystal-lang/crystal/pull/12430), thanks @asterite)\n\n#### Collection\n\n- Refactor and improve `NamedTuple` deserialization from JSON and YAML ([#12008](https://github.com/crystal-lang/crystal/pull/12008), thanks @HertzDevil)\n- **(performance)** Optimize `BitArray#tally(hash)` ([#11909](https://github.com/crystal-lang/crystal/pull/11909), thanks @HertzDevil)\n- Use `Slice#unsafe_slice_of` and `#to_unsafe_bytes` in the standard library and compiler ([#12280](https://github.com/crystal-lang/crystal/pull/12280), thanks @HertzDevil)\n- **(performance)** Optimize block-less overloads of `BitArray#index` and `#rindex` ([#12087](https://github.com/crystal-lang/crystal/pull/12087), thanks @HertzDevil)\n- Support tuple metaclass indexers with non-literal arguments ([#11834](https://github.com/crystal-lang/crystal/pull/11834), thanks @HertzDevil)\n- Add `Indexable#index!` overloads with `offset` parameter ([#12089](https://github.com/crystal-lang/crystal/pull/12089), thanks @HertzDevil)\n\n#### Concurrency\n\n- Fix fiber clean loop on Windows ([#12300](https://github.com/crystal-lang/crystal/pull/12300), thanks @HertzDevil)\n- Enable `Mutex` on Windows ([#12213](https://github.com/crystal-lang/crystal/pull/12213), thanks @HertzDevil)\n\n#### Crypto\n\n- Add support for Bcrypt algorithm version 2y ([#12447](https://github.com/crystal-lang/crystal/pull/12447), thanks @docelic)\n- Allow using `U/Int128` in `Random` ([#11977](https://github.com/crystal-lang/crystal/pull/11977), thanks @BlobCodes)\n\n#### Files\n\n- **(breaking-change)** Define `#system_echo` and `#system_raw` on all systems ([#12352](https://github.com/crystal-lang/crystal/pull/12352), thanks @HertzDevil)\n- **(breaking-change)** Do not expose `Crystal::System::FileInfo` through `File::Info` ([#12385](https://github.com/crystal-lang/crystal/pull/12385), thanks @HertzDevil)\n- Fix `IO.pipe` spec on FreeBSD ([#12324](https://github.com/crystal-lang/crystal/pull/12324), thanks @dmgk)\n- Fix docs error for `File.match?` `**` globbing pattern. ([#12343](https://github.com/crystal-lang/crystal/pull/12343), thanks @zw963)\n- Add `Dir#info` ([#11991](https://github.com/crystal-lang/crystal/pull/11991), thanks @didactic-drunk)\n- Implement `IO::FileDescriptor`'s console methods on Windows ([#12294](https://github.com/crystal-lang/crystal/pull/12294), thanks @HertzDevil)\n- Fix typo: `LibC::DT_LINK` -> `DT_LNK` ([#11954](https://github.com/crystal-lang/crystal/pull/11954), thanks @HertzDevil)\n- Document `IO::FileDescriptor#info` ([#12384](https://github.com/crystal-lang/crystal/pull/12384), thanks @HertzDevil)\n- **(performance)** Introduce `IO::DEFAULT_BUFFER_SIZE` ([#12507](https://github.com/crystal-lang/crystal/pull/12507), thanks @straight-shoota)\n- Add support for `IO::FileDescriptor` staying open on finalize ([#12367](https://github.com/crystal-lang/crystal/pull/12367), thanks @refi64)\n\n#### Macros\n\n- Enhance `record` documentation ([#12334](https://github.com/crystal-lang/crystal/pull/12334), thanks @straight-shoota)\n\n#### Networking\n\n- Add `Socket::IPAddress.valid?` ([#12489](https://github.com/crystal-lang/crystal/pull/12489), [#10492](https://github.com/crystal-lang/crystal/pull/10492), thanks @straight-shoota)\n- Fix `HTTP::Client#exec` to abort retry when client was closed ([#12465](https://github.com/crystal-lang/crystal/pull/12465), thanks @straight-shoota)\n- Fix specs with side effects ([#12539](https://github.com/crystal-lang/crystal/pull/12539), thanks @straight-shoota)\n- Fix `HTTP::Client` implicit compression with retry ([#12536](https://github.com/crystal-lang/crystal/pull/12536), thanks @straight-shoota)\n- `HTTP::StaticFileHandler`: Reduce max stat calls from 6 to 2 ([#12310](https://github.com/crystal-lang/crystal/pull/12310), thanks @didactic-drunk)\n- Add warning about concurrent requests in `HTTP::Client` ([#12527](https://github.com/crystal-lang/crystal/pull/12527), thanks @straight-shoota)\n\n#### Numeric\n\n- Add full integer support to `sprintf` and `String#%` ([#10973](https://github.com/crystal-lang/crystal/pull/10973), thanks @HertzDevil)\n- Make `Float#to_s` ignore NaN sign bit ([#12399](https://github.com/crystal-lang/crystal/pull/12399), thanks @HertzDevil)\n- Make `sprintf` and `String#%` ignore NaN sign bit ([#12400](https://github.com/crystal-lang/crystal/pull/12400), thanks @HertzDevil)\n- Fix `Complex#to_s` imaginary component sign for certain values ([#12244](https://github.com/crystal-lang/crystal/pull/12244), thanks @HertzDevil)\n- More accurate definition of `Complex#sign` ([#12242](https://github.com/crystal-lang/crystal/pull/12242), thanks @HertzDevil)\n- Fix overflow for `rand(Range(Int, Int))` when signed span is too large ([#12545](https://github.com/crystal-lang/crystal/pull/12545), thanks @HertzDevil)\n- **(performance)** Add `#rotate_left` and `#rotate_right` for primitive integers ([#12307](https://github.com/crystal-lang/crystal/pull/12307), thanks @HertzDevil)\n- **(performance)** Optimize `BigDecimal#div` for inexact divisions ([#10803](https://github.com/crystal-lang/crystal/pull/10803), thanks @HertzDevil)\n- Implement the Dragonbox algorithm for `Float#to_s` ([#10913](https://github.com/crystal-lang/crystal/pull/10913), thanks @HertzDevil)\n- Add `U/Int128` to `isqrt` spec ([#11976](https://github.com/crystal-lang/crystal/pull/11976), thanks @BlobCodes)\n\n#### Runtime\n\n- Fix: Parse DWARF5 Data16 values ([#12497](https://github.com/crystal-lang/crystal/pull/12497), thanks @stakach)\n- macOS: Fix call stack when executable path contains symlinks ([#12504](https://github.com/crystal-lang/crystal/pull/12504), thanks @HertzDevil)\n- WASM: Add support for `wasi-sdk 16`: don't rely on `__original_main` ([#12450](https://github.com/crystal-lang/crystal/pull/12450), thanks @lbguilherme)\n\n#### Serialization\n\n- Fix YAML serialization class name ambiguity ([#12537](https://github.com/crystal-lang/crystal/pull/12537), thanks @hugopl)\n- Allow non-type converter instances in `ArrayConverter` and `HashValueConverter` ([#10638](https://github.com/crystal-lang/crystal/pull/10638), thanks @HertzDevil)\n- Document `after_initialize` method for `yaml` and `json` serializers ([#12530](https://github.com/crystal-lang/crystal/pull/12530), thanks @analogsalad)\n\n#### System\n\n- Add missing fields to `LibC::Passwd` on FreeBSD ([#12315](https://github.com/crystal-lang/crystal/pull/12315), thanks @dmgk)\n- Add platform-specific variants of `Process.parse_arguments` ([#12278](https://github.com/crystal-lang/crystal/pull/12278), thanks @HertzDevil)\n- Make `Dir.current` respect `$PWD` ([#12471](https://github.com/crystal-lang/crystal/pull/12471), thanks @straight-shoota)\n\n#### Text\n\n- Fix `String` shift state specs on FreeBSD ([#12339](https://github.com/crystal-lang/crystal/pull/12339), thanks @dmgk)\n- Disallow mixing of sequential and named `sprintf` parameters ([#12402](https://github.com/crystal-lang/crystal/pull/12402), thanks @HertzDevil)\n- Fix `Colorize` doc example ([#12492](https://github.com/crystal-lang/crystal/pull/12492), thanks @zw963)\n- **(performance)** Optimize `String#downcase` and `String#upcase` for single byte optimizable case ([#12389](https://github.com/crystal-lang/crystal/pull/12389), thanks @asterite)\n- **(performance)** Optimize `String#valid_encoding?` ([#12145](https://github.com/crystal-lang/crystal/pull/12145), thanks @HertzDevil)\n- Implement `String#unicode_normalize` and `String#unicode_normalized?` ([#11226](https://github.com/crystal-lang/crystal/pull/11226), thanks @HertzDevil)\n- Support parameter numbers in `sprintf` ([#12448](https://github.com/crystal-lang/crystal/pull/12448), thanks @HertzDevil)\n- Use `LibC.malloc` instead of `GC.malloc` for LibPCRE allocations ([#12456](https://github.com/crystal-lang/crystal/pull/12456), thanks @lbguilherme)\n- Unicode: Update to version 15.0.0 ([#12479](https://github.com/crystal-lang/crystal/pull/12479), thanks @HertzDevil)\n- Avoid free call in interpreted mode ([#12496](https://github.com/crystal-lang/crystal/pull/12496), thanks @straight-shoota)\n\n### Compiler\n\n- Improve recursive splat expansion detection ([#11790](https://github.com/crystal-lang/crystal/pull/11790), thanks @asterite)\n- Compiler: fix `#to_s` for empty parameters of lib funs ([#12368](https://github.com/crystal-lang/crystal/pull/12368), thanks @HertzDevil)\n- Compiler: transform `Proc(*T, Void)` to `Proc(*T, Nil)` ([#12388](https://github.com/crystal-lang/crystal/pull/12388), thanks @asterite)\n- Compiler: indent `begin` `Expression`s that are direct node children ([#12362](https://github.com/crystal-lang/crystal/pull/12362), thanks @HertzDevil)\n- Compiler: add missing location to node on literal expander for array ([#12403](https://github.com/crystal-lang/crystal/pull/12403), thanks @asterite)\n- Compiler: a generic class type can also be reference-like ([#12347](https://github.com/crystal-lang/crystal/pull/12347), thanks @asterite)\n- Hoist complex element expressions outside container literals ([#12366](https://github.com/crystal-lang/crystal/pull/12366), thanks @HertzDevil)\n- **(performance)** Compiler: bind to tuple, not array ([#12423](https://github.com/crystal-lang/crystal/pull/12423), thanks @asterite)\n- Use `Path.new(string)` instead of `Path.new([string])` ([#12419](https://github.com/crystal-lang/crystal/pull/12419), thanks @asterite)\n- Decouple warning detection from program instances ([#12293](https://github.com/crystal-lang/crystal/pull/12293), thanks @HertzDevil)\n- **(performance)** Compiler: only have `freeze_type` in select AST nodes ([#12428](https://github.com/crystal-lang/crystal/pull/12428), thanks @asterite)\n- Correctly display codegen when cross-compiling ([#12414](https://github.com/crystal-lang/crystal/pull/12414), thanks @luislavena)\n- Compiler: simplify some calls ([#12417](https://github.com/crystal-lang/crystal/pull/12417), thanks @asterite)\n- **(performance)** Compiler: optimizations in `merge_if_vars` ([#12432](https://github.com/crystal-lang/crystal/pull/12432), [#12433](https://github.com/crystal-lang/crystal/pull/12433), thanks @asterite)\n- Compiler refactor: extract `type_from_dependencies` ([#12437](https://github.com/crystal-lang/crystal/pull/12437), thanks @asterite)\n- **(performance)** Compiler: refactor and slightly optimize merging two types ([#12436](https://github.com/crystal-lang/crystal/pull/12436), thanks @asterite)\n- **(performance)** Compiler optimization: don't create call for hook unless needed ([#12452](https://github.com/crystal-lang/crystal/pull/12452), thanks @asterite)\n- **(performance)** CrystalPath: Cache `Dir.current` to avoid thousands of allocations ([#12455](https://github.com/crystal-lang/crystal/pull/12455), thanks @yxhuvud)\n- Better call error messages ([#12469](https://github.com/crystal-lang/crystal/pull/12469), thanks @asterite)\n- **(performance)** Compiler optimization: avoid intermediate array when matching call arg types ([#12485](https://github.com/crystal-lang/crystal/pull/12485), thanks @asterite)\n\n#### Codegen\n\n- Codegen: fix how unions are represented to not miss bytes ([#12551](https://github.com/crystal-lang/crystal/pull/12551), thanks @asterite)\n- Fix alignment typo in compiler comments ([#12564](https://github.com/crystal-lang/crystal/pull/12564), thanks @mdwagner)\n- Remove redundant code from x86_64 abi ([#12443](https://github.com/crystal-lang/crystal/pull/12443), thanks @mattrberry)\n- Codegen: use var pointer for `out` instead of an extra variable ([#10952](https://github.com/crystal-lang/crystal/pull/10952), thanks @asterite)\n\n#### Debugger\n\n- Basic GDB formatter support ([#12209](https://github.com/crystal-lang/crystal/pull/12209), thanks @HertzDevil)\n- Add Visual Studio formatters for `String`, `Array`, and `Hash` ([#12212](https://github.com/crystal-lang/crystal/pull/12212), thanks @HertzDevil)\n\n#### Interpreter\n\n- Interpreter: handle the case of a def's body with no type ([#12220](https://github.com/crystal-lang/crystal/pull/12220), thanks @asterite)\n- Interpreter: simplify ivar initialization ([#12222](https://github.com/crystal-lang/crystal/pull/12222), thanks @asterite)\n- Interpreter: fix autocasting in multidispatch ([#12223](https://github.com/crystal-lang/crystal/pull/12223), thanks @asterite)\n- Interpreter: handle `next` inside captured block ([#12237](https://github.com/crystal-lang/crystal/pull/12237), thanks @asterite)\n- Interpreter: fix `crystal_type_id` for virtual metaclass type ([#12246](https://github.com/crystal-lang/crystal/pull/12246), thanks @asterite)\n- Interpreter: handle yield with splat combined with tuple unpacking ([#12247](https://github.com/crystal-lang/crystal/pull/12247), thanks @asterite)\n- Interpreter: handle inlined call that returns self for structs ([#12259](https://github.com/crystal-lang/crystal/pull/12259), thanks @asterite)\n- Interpreter: implement `Int128`/`UInt128` intrinsics ([#12258](https://github.com/crystal-lang/crystal/pull/12258), thanks @asterite)\n- Interpreter: fix some conversion primitives ([#12257](https://github.com/crystal-lang/crystal/pull/12257), thanks @asterite)\n- Interpreter: don't override special vars inside block ([#12251](https://github.com/crystal-lang/crystal/pull/12251), thanks @asterite)\n- Interpreter: add missing cast from tuple to other tuple inside union ([#12249](https://github.com/crystal-lang/crystal/pull/12249), thanks @asterite)\n- Interpreter: allow declaring local vars during a pry session ([#12180](https://github.com/crystal-lang/crystal/pull/12180), thanks @asterite)\n- Interpreter: handle bitreverse intrinsics ([#12273](https://github.com/crystal-lang/crystal/pull/12273), thanks @asterite)\n- Interpreter: cache methods with captured block ([#12285](https://github.com/crystal-lang/crystal/pull/12285), thanks @asterite)\n- Interpreter: missing downcast from `MixedUnionType` to `NilableProcType` ([#12286](https://github.com/crystal-lang/crystal/pull/12286), thanks @asterite)\n- Interpreter: fix `with ... yield` with extra arguments ([#12301](https://github.com/crystal-lang/crystal/pull/12301), thanks @asterite)\n- Interpreter: consider nodes without a type as `NoReturn` ([#12275](https://github.com/crystal-lang/crystal/pull/12275), thanks @asterite)\n- Interpreter: take `with ... yield` scope into account for args bytesize ([#12317](https://github.com/crystal-lang/crystal/pull/12317), thanks @asterite)\n- Fix loader spec on FreeBSD ([#12323](https://github.com/crystal-lang/crystal/pull/12323), thanks @dmgk)\n- Interpreter: inline ivar access for virtual call with a single child ([#12321](https://github.com/crystal-lang/crystal/pull/12321), thanks @asterite)\n- Interpreter: fix `as?` when there's no resulting type ([#12328](https://github.com/crystal-lang/crystal/pull/12328), thanks @asterite)\n- Interpreter: handle missing closured struct self ([#12345](https://github.com/crystal-lang/crystal/pull/12345), thanks @asterite)\n- Interpreter: use `non_nilable_type` in NilableCast ([#12348](https://github.com/crystal-lang/crystal/pull/12348), thanks @asterite)\n- Interpreter: implement mixed union cast with compatible tuple types ([#12349](https://github.com/crystal-lang/crystal/pull/12349), thanks @asterite)\n- Interpreter: fix missing `upcast_distinct` from `A+` to `B` (`Crystal::VirtualType` to `Crystal::NonGenericClassType`) ([#12374](https://github.com/crystal-lang/crystal/pull/12374), thanks @asterite)\n- Interpreter: discard tuple and named tuple ([#12387](https://github.com/crystal-lang/crystal/pull/12387), thanks @asterite)\n- Interpreter: cast proc call arguments to proc arg types ([#12375](https://github.com/crystal-lang/crystal/pull/12375), thanks @asterite)\n- Interpreter: set correct scope for class var initializer ([#12441](https://github.com/crystal-lang/crystal/pull/12441), thanks @asterite)\n- Interpreter (repl): use new `MainVisitor` each time we need to interpret code ([#12512](https://github.com/crystal-lang/crystal/pull/12512), thanks @asterite)\n- Interpreter: allow inspecting block vars without affecting program ([#12520](https://github.com/crystal-lang/crystal/pull/12520), thanks @asterite)\n- Interpreter: check upcast in nilable cast ([#12533](https://github.com/crystal-lang/crystal/pull/12533), thanks @asterite)\n- Interpreter: implement variable autocast ([#12563](https://github.com/crystal-lang/crystal/pull/12563), thanks @asterite)\n- Interpreter: handle missing upcast from `GenericClassInstanceMetaclassType` to `VirtualMetaclassType` ([#12562](https://github.com/crystal-lang/crystal/pull/12562), thanks @asterite)\n- Interpreter: let local vars be seen by macros in repl and pry ([#12240](https://github.com/crystal-lang/crystal/pull/12240), thanks @asterite)\n- Interpreter: handle local variable type declaration ([#12239](https://github.com/crystal-lang/crystal/pull/12239), thanks @asterite)\n- Support libffi on Windows ([#12200](https://github.com/crystal-lang/crystal/pull/12200), thanks @HertzDevil)\n- Add `$CRYSTAL_INTERPRETER_LOADER_INFO` to show loaded libraries ([#12221](https://github.com/crystal-lang/crystal/pull/12221), thanks @straight-shoota)\n- Interpreter: node override ([#12287](https://github.com/crystal-lang/crystal/pull/12287), thanks @asterite)\n- Interpreter: introduce a `Prompt` type ([#12288](https://github.com/crystal-lang/crystal/pull/12288), thanks @asterite)\n- Interpreter: missing `i += 1` ([#12381](https://github.com/crystal-lang/crystal/pull/12381), thanks @asterite)\n- Support building the interpreter on Windows ([#12397](https://github.com/crystal-lang/crystal/pull/12397), thanks @HertzDevil)\n- Don't exit in interpreter spec and change type from `Nil` to `NoReturn` in `FixMissingTypes` ([#12230](https://github.com/crystal-lang/crystal/pull/12230), thanks @asterite)\n- Interpreter: fix multidispatch with captured block ([#12236](https://github.com/crystal-lang/crystal/pull/12236), thanks @asterite)\n- Interpreter: don't change compiled mode logic ([#12252](https://github.com/crystal-lang/crystal/pull/12252), thanks @asterite)\n- Wait more in `HTTP::Server` specs in interpreted mode ([#12420](https://github.com/crystal-lang/crystal/pull/12420), thanks @asterite)\n\n#### Parser\n\n- Lexer: fix index out of bounds when scanning numbers ([#12482](https://github.com/crystal-lang/crystal/pull/12482), thanks @asterite)\n- Fix parser to never create doc from trailing comment ([#11268](https://github.com/crystal-lang/crystal/pull/11268), thanks @straight-shoota)\n- Parser: declare local vars of indirect type declarations in call args ([#11983](https://github.com/crystal-lang/crystal/pull/11983), thanks @asterite)\n- Remove redundant conditional ([#12196](https://github.com/crystal-lang/crystal/pull/12196), thanks @potomak)\n- Warn on suffix-less integer literals outside `Int64`'s range ([#12427](https://github.com/crystal-lang/crystal/pull/12427), thanks @HertzDevil)\n- Use enum instead of symbols for keywords in the lexer ([#11871](https://github.com/crystal-lang/crystal/pull/11871), thanks @HertzDevil)\n- Parser: Rename `arg*` to `param*` ([#12235](https://github.com/crystal-lang/crystal/pull/12235), thanks @potomak)\n- Fix test cases ([#12508](https://github.com/crystal-lang/crystal/pull/12508), thanks @potomak)\n\n#### Semantic\n\n- **(breaking-change)** Allow `Union` restrictions to be ordered before all other restrictions ([#12335](https://github.com/crystal-lang/crystal/pull/12335), thanks @HertzDevil)\n- **(breaking-change)** Use more robust ordering between def overloads ([#10711](https://github.com/crystal-lang/crystal/pull/10711), thanks @HertzDevil)\n- Fix: Instance vars should not be allowed on `Class`, `Tuple`, `NamedTuple`, `Enum`, `Pointer` , `Proc`, `StaticArray` and `Union`. ([#12160](https://github.com/crystal-lang/crystal/pull/12160), thanks @I3oris)\n- Compiler and interpreter: fix `is_a?` from virtual metaclass to generic metaclass ([#12306](https://github.com/crystal-lang/crystal/pull/12306), thanks @asterite)\n- Compiler: fix type descendent for union metaclass ([#12308](https://github.com/crystal-lang/crystal/pull/12308), thanks @asterite)\n- Compiler: fix `is_a?` from generic class against generic class instance type ([#12312](https://github.com/crystal-lang/crystal/pull/12312), thanks @asterite)\n- Fix `self` in restrictions when instantiating macro def in subtypes ([#10954](https://github.com/crystal-lang/crystal/pull/10954), thanks @HertzDevil)\n- Never resolve free variables as types during overload ordering ([#11973](https://github.com/crystal-lang/crystal/pull/11973), thanks @HertzDevil)\n- Use instantiated type as `self` when inferring instance variable types ([#12466](https://github.com/crystal-lang/crystal/pull/12466), thanks @HertzDevil)\n- Fix restriction comparison between `Metaclass` and `Path` ([#12523](https://github.com/crystal-lang/crystal/pull/12523), thanks @HertzDevil)\n- **(performance)** Compiler: don't always use Array for node dependencies and observers ([#12405](https://github.com/crystal-lang/crystal/pull/12405), thanks @asterite)\n- Compiler: better error message for symbol against enum ([#12478](https://github.com/crystal-lang/crystal/pull/12478), thanks @asterite)\n\n### Tools\n\n#### Docs-generator\n\n- Fix docs generator search constants id ([#12262](https://github.com/crystal-lang/crystal/pull/12262), thanks @GeopJr)\n\n#### Formatter\n\n- Formatter: format comment after select ([#12506](https://github.com/crystal-lang/crystal/pull/12506), thanks @asterite)\n- Formatter: try to format macros that don't interpolate content ([#12378](https://github.com/crystal-lang/crystal/pull/12378), thanks @asterite)\n\n#### Playground\n\n- Playground: Fix pass bound hostname to run sessions ([#12356](https://github.com/crystal-lang/crystal/pull/12356), thanks @orangeSi)\n- Don't show stacktrace when playground port is already in use. ([#11844](https://github.com/crystal-lang/crystal/pull/11844), thanks @hugopl)\n- Indent playground code using spaces ([#12231](https://github.com/crystal-lang/crystal/pull/12231), thanks @potomak)\n\n### Other\n\n- `bin/crystal`: Ensure `sh` compatibility ([#12486](https://github.com/crystal-lang/crystal/pull/12486), thanks @HertzDevil)\n- bumping version 1.6.0-dev ([#12263](https://github.com/crystal-lang/crystal/pull/12263), thanks @beta-ziliani)\n- updating CI to 1.5.0 ([#12260](https://github.com/crystal-lang/crystal/pull/12260), thanks @beta-ziliani)\n- Add fish shell completion ([#12026](https://github.com/crystal-lang/crystal/pull/12026), thanks @TunkShif)\n- Execute `compopt` only when it's present ([#12248](https://github.com/crystal-lang/crystal/pull/12248), thanks @potomak)\n- Use `Makefile.win` and wrapper script on Windows CI ([#12344](https://github.com/crystal-lang/crystal/pull/12344), thanks @HertzDevil)\n- [Makefile] Add format target ([#11420](https://github.com/crystal-lang/crystal/pull/11420), thanks @straight-shoota)\n- Update contact section of CODE of CONDUCT ([#9219](https://github.com/crystal-lang/crystal/pull/9219), thanks @paulcsmith)\n- Update nixpkgs 22.05 and LLVM 11 ([#12498](https://github.com/crystal-lang/crystal/pull/12498), thanks @straight-shoota)\n- [Makefile] Use `EXPORT_CC` for `make crystal` ([#11760](https://github.com/crystal-lang/crystal/pull/11760), thanks @straight-shoota)\n- Update distribution-scripts ([#12502](https://github.com/crystal-lang/crystal/pull/12502), [#12555](https://github.com/crystal-lang/crystal/pull/12555), thanks @straight-shoota)\n- Fix and enhance `scripts/update-distribution-scripts.sh` ([#12503](https://github.com/crystal-lang/crystal/pull/12503), thanks @straight-shoota)\n- [CI] Upgrade GitHub Actions to macos-11 ([#12500](https://github.com/crystal-lang/crystal/pull/12500), thanks @straight-shoota)\n- Add icon and metadata to Windows Crystal compiler binary ([#12494](https://github.com/crystal-lang/crystal/pull/12494), thanks @HertzDevil)\n- Remove `spec/win32_std_spec.cr` and `spec/generate_windows_spec.sh` ([#12282](https://github.com/crystal-lang/crystal/pull/12282), [#12549](https://github.com/crystal-lang/crystal/pull/12549), thanks @HertzDevil and @straight-shoota)\n"
  },
  {
    "path": "doc/changelogs/v1.7.md",
    "content": "# Changelog 1.7\n\n## [1.7.3] - 2023-03-07\n\n[1.7.3]: https://github.com/crystal-lang/crystal/releases/1.7.3\n\n### Standard Library\n\n#### Text\n\n- Do not use `@[ThreadLocal]` for PCRE2's JIT stack ([#13056](https://github.com/crystal-lang/crystal/pull/13056), thanks @HertzDevil)\n- Fix `libpcre2` bindings with arch-dependent types (`SizeT`) ([#13088](https://github.com/crystal-lang/crystal/pull/13088), thanks @straight-shoota)\n- Fix `libpcre2` bindings function pointers ([#13090](https://github.com/crystal-lang/crystal/pull/13090), thanks @straight-shoota)\n- Fix PCRE2 do not allocate JIT stack if unavailable ([#13100](https://github.com/crystal-lang/crystal/pull/13100), thanks @straight-shoota)\n- Backport PCRE2 fixes to 1.7 ([#13136](https://github.com/crystal-lang/crystal/pull/13136), thanks @straight-shoota)\n- Fix `MatchData#[]` named capture with identical prefix ([#13147](https://github.com/crystal-lang/crystal/pull/13147), thanks @straight-shoota)\n- Fix `Regex::Option` behaviour for unnamed members ([#13155](https://github.com/crystal-lang/crystal/pull/13155), thanks @straight-shoota)\n- **(performance)** Improve PCRE2 match performance for JIT and interpreted ([#13146](https://github.com/crystal-lang/crystal/pull/13146), thanks @straight-shoota)\n\n### Compiler\n\n#### Generics\n\n- Explicitly treat unbound type vars in generic class methods as free variables ([#13125](https://github.com/crystal-lang/crystal/pull/13125), thanks @HertzDevil)\n\n### Other\n\n- [CI] Fix add PCRE2 to GHA cache for win job ([#13089](https://github.com/crystal-lang/crystal/pull/13089), thanks @straight-shoota)\n- [CI] Pin `use_pcre` in build environments where PCRE2 is not yet available ([#13102](https://github.com/crystal-lang/crystal/pull/13102), thanks @straight-shoota)\n\n## [1.7.2] - 2023-01-23\n\n[1.7.2]: https://github.com/crystal-lang/crystal/releases/1.7.2\n\n### Standard Library\n\n#### Runtime\n\n- Fix: Add `Nil` return type restrictions to `load_debug_info` ([#12992](https://github.com/crystal-lang/crystal/pull/12992), thanks @straight-shoota)\n\n### Compiler\n\n#### Codegen\n\n- Add error handling to compiler when linker is unavailable ([#12899](https://github.com/crystal-lang/crystal/pull/12899), thanks @straight-shoota)\n\n#### Parser\n\n- Revert \"Parser: Fix restrict grammar for name and supertype in type def (#12622)\" ([#12977](https://github.com/crystal-lang/crystal/pull/12977), thanks @straight-shoota)\n\n### Other\n\n- Update `VERSION` to `1.7.2-dev` ([#12993](https://github.com/crystal-lang/crystal/pull/12993), thanks @straight-shoota)\n\n## [1.7.1] - 2023-01-17\n\n[1.7.1]: https://github.com/crystal-lang/crystal/releases/1.7.1\n\n### Tools\n\n#### Playground\n\n- Fix baked-in path in playground to resolve at runtime ([#12948](https://github.com/crystal-lang/crystal/pull/12948), thanks @straight-shoota)\n\n### Other\n\n- Update `VERSION` to 1.7.1-dev ([#12950](https://github.com/crystal-lang/crystal/pull/12950), thanks @straight-shoota)\n\n## [1.7.0] - 2023-01-09\n\n[1.7.0]: https://github.com/crystal-lang/crystal/releases/1.7.0\n\n### Language\n\n- Add lib functions earlier so that they are visible in top-level macros ([#12848](https://github.com/crystal-lang/crystal/pull/12848), thanks @asterite)\n\n### Standard Library\n\n- Improve `Benchmark` docs ([#12782](https://github.com/crystal-lang/crystal/pull/12782), thanks @r00ster91, @straight-shoota)\n- Improve documentation for `Object#to_s` and `#inspect` ([#9974](https://github.com/crystal-lang/crystal/pull/9974), thanks @straight-shoota)\n- Add methods to manipulate semantic versions ([#12834](https://github.com/crystal-lang/crystal/pull/12834), thanks @gabriel-ss)\n- Add types to methods with defaults ([#12837](https://github.com/crystal-lang/crystal/pull/12837), thanks @caspiano)\n- examples: fix (2022-10) ([#12665](https://github.com/crystal-lang/crystal/pull/12665), thanks @maiha)\n- Fix documentation for `Pointer#move_to` ([#12677](https://github.com/crystal-lang/crystal/pull/12677), thanks @TheEEs)\n- **(performance)** Eliminate `nil` from many predicate methods ([#12702](https://github.com/crystal-lang/crystal/pull/12702), thanks @HertzDevil)\n- examples: fix (2022-12) ([#12870](https://github.com/crystal-lang/crystal/pull/12870), thanks @maiha)\n\n#### Collection\n\n- Fix missed elements in `Hash#select!(keys : Enumerable)` ([#12739](https://github.com/crystal-lang/crystal/pull/12739), thanks @caspiano)\n- Add missing docs for `Indexable` combinations methods ([#10548](https://github.com/crystal-lang/crystal/pull/10548), thanks @keidax)\n- **(performance)** Optimize `Range#sample(n)` for integers and floats ([#12535](https://github.com/crystal-lang/crystal/pull/12535), thanks @straight-shoota)\n- Add `Iterable#each_cons_pair` ([#12726](https://github.com/crystal-lang/crystal/pull/12726), thanks @caspiano)\n- Add links to equivalent `Iterator` methods in `Iterable` ([#12727](https://github.com/crystal-lang/crystal/pull/12727), thanks @caspiano)\n- **(performance)** Optimize `Hash#select(Enumerable)` and `#merge!(Hash, &)` ([#12737](https://github.com/crystal-lang/crystal/pull/12737), thanks @HertzDevil)\n- Add `Indexable#rindex!` method variant ([#12759](https://github.com/crystal-lang/crystal/pull/12759), thanks @Sija)\n- **(performance)** Use mutating collection methods ([#12644](https://github.com/crystal-lang/crystal/pull/12644), thanks @caspiano)\n- Fix `Enum#to_s` for flag enum containing named and unnamed members ([#12895](https://github.com/crystal-lang/crystal/pull/12895), thanks @straight-shoota)\n\n#### Concurrency\n\n- Allow the EventLoop implementation to be detected at runtime ([#12656](https://github.com/crystal-lang/crystal/pull/12656), thanks @lbguilherme)\n- **(performance)** Optimize uniqueness filter in `Channel.select_impl` ([#12814](https://github.com/crystal-lang/crystal/pull/12814), thanks @straight-shoota)\n- Implement multithreading primitives on Windows ([#11647](https://github.com/crystal-lang/crystal/pull/11647), thanks @HertzDevil)\n\n#### Crypto\n\n- **(breaking-change)** Implement `Digest` class in `Digest::CRC32` and `Digest::Adler32` ([#11535](https://github.com/crystal-lang/crystal/pull/11535), thanks @BlobCodes)\n- Fix `OpenSSL::SSL::Context::Client#alpn_protocol=` ([#12724](https://github.com/crystal-lang/crystal/pull/12724), thanks @jaclarke)\n- Fix `HTTP::Client` certificate validation error on FQDN (host with trailing dot) ([#12778](https://github.com/crystal-lang/crystal/pull/12778), thanks @compumike)\n- Enable `arc4random(3)` on all supported BSDs and macOS/Darwin ([#12608](https://github.com/crystal-lang/crystal/pull/12608), thanks @dmgk)\n\n#### Files\n\n- Fix: Read `UInt` in zip directory header ([#12822](https://github.com/crystal-lang/crystal/pull/12822), thanks @pbrumm)\n- Add `File.executable?` for Windows ([#9677](https://github.com/crystal-lang/crystal/pull/9677), thanks @nof1000)\n\n#### Macros\n\n- Fix `TypeNode#nilable?` for root types ([#12354](https://github.com/crystal-lang/crystal/pull/12354), thanks @HertzDevil)\n- Add argless `#annotations` overload ([#9326](https://github.com/crystal-lang/crystal/pull/9326), thanks @Blacksmoke16)\n- Add specs for addition between `ArrayLiteral` and `TupleLiteral` ([#12639](https://github.com/crystal-lang/crystal/pull/12639), thanks @caspiano)\n- Add `ArrayLiteral#-(other)` and `TupleLiteral#-(other)` ([#12646](https://github.com/crystal-lang/crystal/pull/12646), [#12916](https://github.com/crystal-lang/crystal/pull/12916) thanks @caspiano, @straight-shoota)\n\n#### Networking\n\n- **(breaking-change)** Add `HTTP::Headers#serialize` ([#12765](https://github.com/crystal-lang/crystal/pull/12765), thanks @straight-shoota)\n- Ensure `HTTP::Client` closes response when breaking out of block ([#12749](https://github.com/crystal-lang/crystal/pull/12749), thanks @straight-shoota)\n- Add `HTTP::Server::Response#redirect` ([#12526](https://github.com/crystal-lang/crystal/pull/12526), thanks @straight-shoota)\n- **(performance)** Websocket: write masked data to temporary buffer before sending it ([#12613](https://github.com/crystal-lang/crystal/pull/12613), thanks @asterite)\n- Validate cookie name prefixes ([#10648](https://github.com/crystal-lang/crystal/pull/10648), thanks @Blacksmoke16)\n- `IPAddress#loopback?` should consider `::ffff:127.0.0.1/104` loopback too ([#12783](https://github.com/crystal-lang/crystal/pull/12783), thanks @carlhoerberg)\n\n#### Numeric\n\n- Support new SI prefixes in `Number#humanize` ([#12761](https://github.com/crystal-lang/crystal/pull/12761), thanks @HertzDevil)\n- Fix `BigInt#%` for unsigned integers ([#12773](https://github.com/crystal-lang/crystal/pull/12773), thanks @straight-shoota)\n- [WASM] Add missing `__powisf2` and `__powidf2` compiler-rt functions ([#12569](https://github.com/crystal-lang/crystal/pull/12569), thanks @lbguilherme)\n- Add docs for `Int#downto` ([#12468](https://github.com/crystal-lang/crystal/pull/12468), thanks @yb66)\n- Upgrade the Dragonbox algorithm ([#12767](https://github.com/crystal-lang/crystal/pull/12767), thanks @HertzDevil)\n- Support scientific notation in `BigDecimal#to_s` ([#10805](https://github.com/crystal-lang/crystal/pull/10805), thanks @HertzDevil)\n- Add `#bit_reverse` and `#byte_swap` for primitive integers ([#12865](https://github.com/crystal-lang/crystal/pull/12865), thanks @HertzDevil)\n- Fix Number comparison operator docs ([#12880](https://github.com/crystal-lang/crystal/pull/12880), thanks @fdocr)\n\n#### Runtime\n\n- `Exception::CallStack`: avoid allocations in `LibC.dl_iterate_phdr` ([#12625](https://github.com/crystal-lang/crystal/pull/12625), thanks @dmgk)\n- Fix explicit type conversion to u64 for `GC::Stats` ([#12779](https://github.com/crystal-lang/crystal/pull/12779), thanks @straight-shoota)\n- Add custom `message` parameter to `#not_nil!` ([#12797](https://github.com/crystal-lang/crystal/pull/12797), thanks @straight-shoota)\n- Refactor specs for `Enum#to_s` using `assert_prints` ([#12882](https://github.com/crystal-lang/crystal/pull/12882), thanks @straight-shoota)\n\n#### Serialization\n\n- **(performance)** Leverage `GC.malloc_atomic` for XML ([#12692](https://github.com/crystal-lang/crystal/pull/12692), thanks @HertzDevil)\n- Refactor libXML error handling to remove global state ([#12663](https://github.com/crystal-lang/crystal/pull/12663), [#12795](https://github.com/crystal-lang/crystal/pull/12795), thanks @straight-shoota)\n- Use qualified type reference `YAML::Any` ([#12688](https://github.com/crystal-lang/crystal/pull/12688), thanks @zw963)\n- Automatically cast Int to Float for `{JSON,YAML}::Any#as_f` ([#12835](https://github.com/crystal-lang/crystal/pull/12835), thanks @compumike)\n\n#### Specs\n\n- Print seed info at start and end of spec output ([#12755](https://github.com/crystal-lang/crystal/pull/12755), thanks @straight-shoota)\n\n#### System\n\n- **(breaking)** Rename `File.real_path` to `.realpath` ([#12552](https://github.com/crystal-lang/crystal/pull/12552), thanks @straight-shoota)\n- **(breaking-change)** Drop FreeBSD 11 compatibility code ([#12612](https://github.com/crystal-lang/crystal/pull/12612), thanks @dmgk)\n- Trap when trying to raise wasm32 exceptions ([#12572](https://github.com/crystal-lang/crystal/pull/12572), thanks @lbguilherme)\n- Use single helper method to pass UTF-16 strings to Windows ([#12695](https://github.com/crystal-lang/crystal/pull/12695), [#12747](https://github.com/crystal-lang/crystal/pull/12747), thanks @HertzDevil, @straight-shoota)\n- Implement `flock_*` fiber-aware, without blocking the thread ([#12861](https://github.com/crystal-lang/crystal/pull/12861), [#12728](https://github.com/crystal-lang/crystal/pull/12728), thanks @straight-shoota)\n- Implement `flock_*` for Win32 ([#12766](https://github.com/crystal-lang/crystal/pull/12766), thanks @straight-shoota)\n- Add docs to `ENV#has_key?` ([#12781](https://github.com/crystal-lang/crystal/pull/12781), thanks @straight-shoota)\n- Improve specs by removing absolute path references ([#12776](https://github.com/crystal-lang/crystal/pull/12776), thanks @straight-shoota)\n- Update FreeBSD LibC types ([#12651](https://github.com/crystal-lang/crystal/pull/12651), thanks @dmgk)\n- Organize `Process` specs ([#12889](https://github.com/crystal-lang/crystal/pull/12889), thanks @straight-shoota)\n- Add tests for `Process::Status` ([#12881](https://github.com/crystal-lang/crystal/pull/12881), thanks @straight-shoota)\n\n#### Text\n\n- Raise `IndexError` on unmatched subpattern for `MatchData#begin` and `#end` ([#12810](https://github.com/crystal-lang/crystal/pull/12810), thanks @straight-shoota)\n- Swap documentation for `String#split` array and block versions ([#12808](https://github.com/crystal-lang/crystal/pull/12808), thanks @hugopl)\n- Add `String#index/rindex!` methods ([#12730](https://github.com/crystal-lang/crystal/pull/12730), thanks @Sija)\n- Re-organize and enhance specs for `Regex` and `Regex::MatchData` ([#12788](https://github.com/crystal-lang/crystal/pull/12788), [#12789](https://github.com/crystal-lang/crystal/pull/12789), thanks @straight-shoota)\n- Add missing positive spec for `Regex#match` with option ([#12804](https://github.com/crystal-lang/crystal/pull/12804), thanks @straight-shoota)\n- Replace `if !blank?` with `unless blank?` ([#12800](https://github.com/crystal-lang/crystal/pull/12800), thanks @vlazar)\n- Add references between String equality, comparison methods ([#10531](https://github.com/crystal-lang/crystal/pull/10531), thanks @straight-shoota)\n- Extract internal Regex API for PCRE backend ([#12802](https://github.com/crystal-lang/crystal/pull/12802), thanks @straight-shoota)\n- Implement `Regex` engine on PCRE2 ([#12856](https://github.com/crystal-lang/crystal/pull/12856), [#12866](https://github.com/crystal-lang/crystal/pull/12866), [#12847](https://github.com/crystal-lang/crystal/pull/12847), thanks @straight-shoota, thanks @HertzDevil)\n- Add missing overloads for `String#byte_slice` ([#12809](https://github.com/crystal-lang/crystal/pull/12809), thanks @straight-shoota)\n\n### Compiler\n\n- Improve error message when there are extra types ([#12734](https://github.com/crystal-lang/crystal/pull/12734), thanks @asterite)\n- Handle triples without libc ([#12594](https://github.com/crystal-lang/crystal/pull/12594), thanks @GeopJr)\n- Remove unused `Program#cache_dir` property ([#12669](https://github.com/crystal-lang/crystal/pull/12669), thanks @straight-shoota)\n- Fix: Unwrap nested errors in error handler for `Crystal::Error` ([#12888](https://github.com/crystal-lang/crystal/pull/12888), thanks @straight-shoota)\n\n#### Codegen\n\n- Add missing specs for `->var.foo` semantics with assignments ([#9419](https://github.com/crystal-lang/crystal/pull/9419), thanks @makenowjust)\n- Use `File#flock_exclusive` on win32 in compiler ([#12876](https://github.com/crystal-lang/crystal/pull/12876), thanks @straight-shoota)\n\n#### Generics\n\n- Redefine defs when constant and number in generic arguments are equal ([#12785](https://github.com/crystal-lang/crystal/pull/12785), thanks @HertzDevil)\n- Fix restriction of numeral generic argument against non-free variable `Path` ([#12784](https://github.com/crystal-lang/crystal/pull/12784), thanks @HertzDevil)\n\n#### Interpreter\n\n- Interpreter: fix class var initializer that needs an upcast ([#12635](https://github.com/crystal-lang/crystal/pull/12635), thanks @asterite)\n- Reverting #12405 Compiler: don't always use Array for node dependencies and observers  ([#12849](https://github.com/crystal-lang/crystal/pull/12849), thanks @beta-ziliani)\n- Match Nix loader errors in compiler spec ([#12852](https://github.com/crystal-lang/crystal/pull/12852), thanks @bcardiff)\n- Interpreter reply ([#12738](https://github.com/crystal-lang/crystal/pull/12738), thanks @I3oris)\n\n#### Parser\n\n- **(breaking-change)** Parser: Fix restrict grammar for name and supertype in type def ([#12622](https://github.com/crystal-lang/crystal/pull/12622), thanks @caspiano)\n- Lexer: fix global capture vars ending with zero, e.g. `$10?` ([#12701](https://github.com/crystal-lang/crystal/pull/12701), thanks @FnControlOption)\n- Lexer: allow regex after CRLF ([#12713](https://github.com/crystal-lang/crystal/pull/12713), thanks @FnControlOption)\n- Assignment to global regex match data is not allowed ([#12714](https://github.com/crystal-lang/crystal/pull/12714), thanks @caspiano)\n- Error when declaring a constant within another constant declaration ([#12566](https://github.com/crystal-lang/crystal/pull/12566), thanks @caspiano)\n- Fix calls with do-end blocks within index operators ([#12824](https://github.com/crystal-lang/crystal/pull/12824), thanks @caspiano)\n- Remove oct/bin floating point literals ([#12687](https://github.com/crystal-lang/crystal/pull/12687), thanks @BlobCodes)\n- Parser: fix wrong/missing locations of various AST nodes ([#11798](https://github.com/crystal-lang/crystal/pull/11798), thanks @FnControlOption)\n- Refactor: use helper method instead of duplicate code in lexer ([#12590](https://github.com/crystal-lang/crystal/pull/12590), thanks @straight-shoota)\n- Simplify sequential character checks in Crystal lexer ([#12699](https://github.com/crystal-lang/crystal/pull/12699), thanks @caspiano)\n- Lexer: delete redundant `scan_ident` calls ([#12691](https://github.com/crystal-lang/crystal/pull/12691), thanks @FnControlOption)\n- Rename `Def#yields` to `Def#block_arity` ([#12833](https://github.com/crystal-lang/crystal/pull/12833), thanks @straight-shoota)\n- Fix warning on space before colon with anonymous block arg ([#12869](https://github.com/crystal-lang/crystal/pull/12869), thanks @straight-shoota)\n- Warn on missing space before colon in type declaration/restriction ([#12740](https://github.com/crystal-lang/crystal/pull/12740), thanks @straight-shoota)\n\n#### Semantic\n\n- Fix: Do not merge union types in truthy filter ([#12752](https://github.com/crystal-lang/crystal/pull/12752), thanks @straight-shoota)\n- Fix crash when using `sizeof`, `instance_sizeof`, or `offsetof` as a type arg ([#12577](https://github.com/crystal-lang/crystal/pull/12577), thanks @keidax)\n- Resolve type of free variable on block return type mismatch ([#12754](https://github.com/crystal-lang/crystal/pull/12754), thanks @caspiano)\n- Order `_` after any other `Path` when comparing overloads ([#12855](https://github.com/crystal-lang/crystal/pull/12855), thanks @HertzDevil)\n- [Experimental] Compiler: try to solve string interpolation exps at compile time ([#12524](https://github.com/crystal-lang/crystal/pull/12524), thanks @asterite)\n- Support `@[Deprecated]` on `annotation` ([#12557](https://github.com/crystal-lang/crystal/pull/12557), thanks @caspiano)\n- Add more specific error message for uninstantiated proc type ([#11219](https://github.com/crystal-lang/crystal/pull/11219), thanks @straight-shoota)\n- Add specs for `system` macro method ([#12885](https://github.com/crystal-lang/crystal/pull/12885), thanks @straight-shoota)\n\n### Tools\n\n#### Docs-generator\n\n- Fix range literals causing method lookups in docs generator  ([#12680](https://github.com/crystal-lang/crystal/pull/12680), thanks @caspiano)\n- Fix method lookup for single char class names ([#12683](https://github.com/crystal-lang/crystal/pull/12683), thanks @caspiano)\n\n#### Formatter\n\n- Formatter: document stdin filename argument (`-`) ([#12620](https://github.com/crystal-lang/crystal/pull/12620), thanks @caspiano)\n\n### Other\n\n#### Infrastructure\n\n- [CI] Drop Alpine libreSSL 3.1 test ([#12641](https://github.com/crystal-lang/crystal/pull/12641), thanks @straight-shoota)\n- Bump version to 1.7.0-dev ([#12640](https://github.com/crystal-lang/crystal/pull/12640), thanks @straight-shoota)\n- [CI] Update GHA actions ([#12501](https://github.com/crystal-lang/crystal/pull/12501), thanks @straight-shoota)\n- Opt in to new overload ordering behavior in Makefile ([#12703](https://github.com/crystal-lang/crystal/pull/12703), thanks @HertzDevil)\n- Merge release 1.6.2 into master ([#12719](https://github.com/crystal-lang/crystal/pull/12719), thanks @beta-ziliani)\n- Configure Renovate ([#12678](https://github.com/crystal-lang/crystal/pull/12678), thanks @renovate)\n- [CI] Add version pin for ilammy/msvc-dev-cmd in windows CI ([#12746](https://github.com/crystal-lang/crystal/pull/12746), thanks @straight-shoota)\n- [CI] Update dependencies for windows CI ([#12745](https://github.com/crystal-lang/crystal/pull/12745), thanks @straight-shoota)\n- Update GH Actions ([#12742](https://github.com/crystal-lang/crystal/pull/12742), thanks @renovate)\n- [CI] Run specs in random order by default ([#12541](https://github.com/crystal-lang/crystal/pull/12541), thanks @straight-shoota)\n- Update `shell.nix` for newer LLVM versions and aarch64-darwin ([#12591](https://github.com/crystal-lang/crystal/pull/12591), thanks @HertzDevil)\n- Update previous Crystal release - 1.6.2 ([#12750](https://github.com/crystal-lang/crystal/pull/12750), thanks @straight-shoota)\n- [CI] Update PCRE 8.45 for Windows CI ([#12762](https://github.com/crystal-lang/crystal/pull/12762), thanks @HertzDevil)\n- Add WebAssembly specs ([#12571](https://github.com/crystal-lang/crystal/pull/12571), thanks @lbguilherme)\n- Update actions/checkout action to v3 ([#12805](https://github.com/crystal-lang/crystal/pull/12805), thanks @renovate)\n- Enable multithreading specs on Windows CI ([#12843](https://github.com/crystal-lang/crystal/pull/12843), thanks @HertzDevil)\n- [CI] Update mwilliamson/setup-wasmtime-action action to v2 ([#12864](https://github.com/crystal-lang/crystal/pull/12864), thanks @renovate)\n- [CI] Update distribution-scripts ([#12891](https://github.com/crystal-lang/crystal/pull/12891), thanks @straight-shoota)\n- [CI] Update shards 0.17.2 ([#12875](https://github.com/crystal-lang/crystal/pull/12875), thanks @straight-shoota)\n- Rotate breached credentials in CircleCI ([#12902](https://github.com/crystal-lang/crystal/pull/12902), thanks @matiasgarciaisaia)\n- Update `NOTICE.md` ([#12901](https://github.com/crystal-lang/crystal/pull/12901), thanks @HertzDevil)\n- Split pre-1.0 changelog ([#12898](https://github.com/crystal-lang/crystal/pull/12898), thanks @straight-shoota)\n\n#### Code Improvements\n\n- Style: Remove redundant begin blocks ([#12638](https://github.com/crystal-lang/crystal/pull/12638), thanks @caspiano)\n- Lint: Fix variable name casing ([#12674](https://github.com/crystal-lang/crystal/pull/12674), thanks @Sija)\n- Lint: Remove comparisons with boolean literals ([#12673](https://github.com/crystal-lang/crystal/pull/12673), thanks @Sija)\n- Lint: Use `Object#in?` instead of multiple comparisons ([#12675](https://github.com/crystal-lang/crystal/pull/12675), thanks @Sija)\n- Lint: Remove useless assignments ([#12648](https://github.com/crystal-lang/crystal/pull/12648), thanks @Sija)\n- Use `Object#in?` in place of multiple comparisons ([#12700](https://github.com/crystal-lang/crystal/pull/12700), thanks @caspiano)\n- Style: Remove explicit returns from the codebase ([#12637](https://github.com/crystal-lang/crystal/pull/12637), thanks @caspiano)\n- Lint: Use `Enumerable#find!/#index!` variants ([#12686](https://github.com/crystal-lang/crystal/pull/12686), thanks @Sija)\n- Style: Use short block notation for simple one-liners ([#12676](https://github.com/crystal-lang/crystal/pull/12676), thanks @Sija)\n- Couple of ameba lint issues fixed ([#12685](https://github.com/crystal-lang/crystal/pull/12685), thanks @Sija)\n- Use context-specific heredoc deliminators ([#12816](https://github.com/crystal-lang/crystal/pull/12816), thanks @straight-shoota)\n"
  },
  {
    "path": "doc/changelogs/v1.8.md",
    "content": "# Changelog 1.8\n\n## [1.8.2] - 2023-05-08\n\n[1.8.2]: https://github.com/crystal-lang/crystal/releases/1.8.2\n\n### Standard Library\n\n#### Collection\n\n- Fix codegen bug with `Iterator::ChainIterator` ([#13412](https://github.com/crystal-lang/crystal/pull/13412), thanks @straight-shoota)\n\n#### Log\n\n- Fix `Log::Metadata#dup` crash with 2+ entries ([#13369](https://github.com/crystal-lang/crystal/pull/13369), thanks @HertzDevil)\n\n#### Serialization\n\n- Fixup for `JSON::Serializable` on certain recursively defined types ([#13430](https://github.com/crystal-lang/crystal/pull/13430), thanks @kostya)\n\n#### Text\n\n- Fix `String#scan` with empty `Regex` match at multibyte char ([#13387](https://github.com/crystal-lang/crystal/pull/13387), thanks @HertzDevil)\n- **(performance)** Check subject UTF-8 validity just once for `String#gsub`, `#scan`, `#split` ([#13406](https://github.com/crystal-lang/crystal/pull/13406), thanks @HertzDevil)\n\n### Compiler\n\n#### Codegen\n\n- Always use 0 for offset of `StaticArray`'s `@buffer` ([#13319](https://github.com/crystal-lang/crystal/pull/13319), thanks @HertzDevil)\n\n### Other\n\n- Backport bugfixes to release/1.8 for release 1.8.2 ([#3435](https://github.com/crystal-lang/crystal/pull/13435), thanks @straight-shoota)\n\n## [1.8.1] - 2023-04-20\n\n[1.8.1]: https://github.com/crystal-lang/crystal/releases/1.8.1\n\n### Standard Library\n\n#### Serialization\n\n- Fix `JSON::Serializable` on certain recursively defined types ([#13344](https://github.com/crystal-lang/crystal/pull/13344), thanks @HertzDevil)\n\n#### Text\n\n- Fix `String#gsub` with empty match at multibyte char ([#13342](https://github.com/crystal-lang/crystal/pull/13342), thanks @straight-shoota)\n- Fix PCRE2 `Regex` with more than 127 named capture groups ([#13349](https://github.com/crystal-lang/crystal/pull/13349), thanks @HertzDevil)\n\n## [1.8.0] - 2023-04-14\n\n[1.8.0]: https://github.com/crystal-lang/crystal/releases/1.8.0\n\n### Language\n\n- The compiler uses PCRE2 to validate regex literals ([#13084](https://github.com/crystal-lang/crystal/pull/13084), thanks @straight-shoota)\n- Fill docs for `TupleLiteral` ([#12927](https://github.com/crystal-lang/crystal/pull/12927), thanks @straight-shoota)\n- Allow namespaced `Path`s as type names for `lib` ([#12903](https://github.com/crystal-lang/crystal/pull/12903), thanks @HertzDevil)\n\n### Standard Library\n\n- Fix `SyntaxHighlighter::HTML` to escape identifier values ([#13212](https://github.com/crystal-lang/crystal/pull/13212), thanks @straight-shoota)\n- Add workaround for `Value#not_nil!` copying the receiver ([#13264](https://github.com/crystal-lang/crystal/pull/13264), thanks @HertzDevil)\n- Fix `Pointer#copy_to` overflow on unsigned size and different target type ([#13269](https://github.com/crystal-lang/crystal/pull/13269), thanks @HertzDevil)\n- Docs: Added note about imports where necessary ([#13026](https://github.com/crystal-lang/crystal/pull/13026), [#13066](https://github.com/crystal-lang/crystal/pull/13066), thanks @Tamnac, @straight-shoota)\n- Suppress compiler output in `compile_file` spec helper ([#13228](https://github.com/crystal-lang/crystal/pull/13228), thanks @straight-shoota)\n- Define equality for `Process::Status` and `OAuth::RequestToken` ([#13014](https://github.com/crystal-lang/crystal/pull/13014), thanks @HertzDevil)\n- Fix some Linux glibc bindings ([#13242](https://github.com/crystal-lang/crystal/pull/13242), [#13249](https://github.com/crystal-lang/crystal/pull/13249), thanks @ysbaddaden, @HertzDevil)\n\n#### Collection\n\n- **(breaking-change)** Fix `Enum#includes?` to require all bits set ([#13229](https://github.com/crystal-lang/crystal/pull/13229), thanks @straight-shoota)\n- **(breaking-change)** Deprecate `Enum.flags` ([#12900](https://github.com/crystal-lang/crystal/pull/12900), thanks @straight-shoota)\n- **(breaking-change)** Remove compile-time error for `Range#size`, `#each`, `#sample` ([#13278](https://github.com/crystal-lang/crystal/pull/13278), thanks @straight-shoota)\n- **(breaking-change)** Docs: Require all `Indexable`s to be stable ([#13061](https://github.com/crystal-lang/crystal/pull/13061), thanks @HertzDevil)\n- Add `Enum.[]` convenience constructor ([#12900](https://github.com/crystal-lang/crystal/pull/12900), thanks @straight-shoota)\n- Rename internal `Iterator::Slice` type to not conflict with `::Slice` ([#12983](https://github.com/crystal-lang/crystal/pull/12983), thanks @Blacksmoke16)\n- Fix `Array#replace` on shifted arrays ([#13256](https://github.com/crystal-lang/crystal/pull/13256), thanks @HertzDevil)\n- Add `Tuple#to_static_array` ([#12930](https://github.com/crystal-lang/crystal/pull/12930), thanks @straight-shoota)\n- Add `Enum#inspect` ([#13004](https://github.com/crystal-lang/crystal/pull/13004), thanks @straight-shoota)\n- Add `Slice#+(Slice)` and `Slice.join` ([#12081](https://github.com/crystal-lang/crystal/pull/12081), thanks @HertzDevil)\n- Add `Enumerable#min(count)` and `#max(count)` ([#13057](https://github.com/crystal-lang/crystal/pull/13057), thanks @nthiad)\n- Fix `Array(T)#[]=(Int, Int, Array(T))` on shifted arrays ([#13275](https://github.com/crystal-lang/crystal/pull/13275), thanks @HertzDevil)\n\n#### Concurrency\n\n- Fix: Make sure to dup `Array` in `Channel.select_impl` ([#12827](https://github.com/crystal-lang/crystal/pull/12827), [#12962](https://github.com/crystal-lang/crystal/pull/12962), thanks @straight-shoota)\n- Add memory barriers on lock/unlock of SpinLock ([#13050](https://github.com/crystal-lang/crystal/pull/13050), thanks @bcardiff)\n- **(performance)** Avoid `Array` allocation in `Channel.select(Tuple)` ([#12960](https://github.com/crystal-lang/crystal/pull/12960), thanks @straight-shoota)\n\n#### Files\n\n- **(breaking-change)** Deprecate `Termios` ([#12940](https://github.com/crystal-lang/crystal/pull/12940), thanks @HertzDevil)\n- **(breaking-change)** Windows: make `File.delete` remove symlink directories, not `Dir.delete` ([#13224](https://github.com/crystal-lang/crystal/pull/13224), thanks @HertzDevil)\n- Leverage `fileapi` for opening files on windows ([#13178](https://github.com/crystal-lang/crystal/pull/13178), thanks @Blacksmoke16)\n- Windows: fix error condition when `File.open` fails ([#13235](https://github.com/crystal-lang/crystal/pull/13235), thanks @HertzDevil)\n- Skip eacces spec for superuser ([#13227](https://github.com/crystal-lang/crystal/pull/13227), thanks @straight-shoota)\n- Improve `File.symlink` on Windows ([#13141](https://github.com/crystal-lang/crystal/pull/13141), thanks @HertzDevil)\n- Implement `File.readlink` on Windows ([#13195](https://github.com/crystal-lang/crystal/pull/13195), thanks @HertzDevil)\n\n#### LLVM\n\n- **(breaking-change)** Drop support for LLVM < 8 ([#12906](https://github.com/crystal-lang/crystal/pull/12906), thanks @straight-shoota)\n- **(breaking-change)** Support LLVM 15 ([#13173](https://github.com/crystal-lang/crystal/pull/13173), thanks @HertzDevil)\n- Error when `find-llvm-config` is unsuccessful ([#13045](https://github.com/crystal-lang/crystal/pull/13045), thanks @straight-shoota)\n- Remove `LibLLVM.has_constant?(:AttributeRef)` checks ([#13162](https://github.com/crystal-lang/crystal/pull/13162), thanks @HertzDevil)\n- Refactor `LLVM::Attribute#each_kind` to use `Enum#each` ([#13234](https://github.com/crystal-lang/crystal/pull/13234), thanks @straight-shoota)\n\n#### Networking\n\n- Fix socket specs when network not available ([#12961](https://github.com/crystal-lang/crystal/pull/12961), thanks @straight-shoota)\n- Fix wrong default address when binding sockets ([#13006](https://github.com/crystal-lang/crystal/pull/13006), thanks @etra0)\n- Clarify WebSocket documentation ([#13096](https://github.com/crystal-lang/crystal/pull/13096), thanks @j8r)\n- Add `Socket::IPAddress#link_local?` ([#13204](https://github.com/crystal-lang/crystal/pull/13204), thanks @GeopJr)\n- Clean up `back\\slash.txt` in `HTTP::StaticFileHandler` specs ([#12984](https://github.com/crystal-lang/crystal/pull/12984), thanks @HertzDevil)\n- Add `MIME::Multipart.parse(HTTP::Client::Response, &)` ([#12890](https://github.com/crystal-lang/crystal/pull/12890), thanks @straight-shoota)\n- Replace `LibC.ntohs` and `htons` with native code ([#13027](https://github.com/crystal-lang/crystal/pull/13027), thanks @HertzDevil)\n- Add `OAuth2::Client#make_token_request` returning HTTP response ([#12921](https://github.com/crystal-lang/crystal/pull/12921), thanks @cyangle)\n- Use exhaustive case in `HTTP::WebSocket#run` ([#13097](https://github.com/crystal-lang/crystal/pull/13097), thanks @j8r)\n- Increase time drift for `HTTP::StaticFileHandler`'s gzip check ([#13138](https://github.com/crystal-lang/crystal/pull/13138), thanks @HertzDevil)\n- OpenSSL: use Windows' system root certificate store ([#13187](https://github.com/crystal-lang/crystal/pull/13187), thanks @HertzDevil)\n- Handle `Range` requests in `HTTP::StaticFileHandler` ([#12886](https://github.com/crystal-lang/crystal/pull/12886), thanks @jgaskins, @straight-shoota)\n- Skip hostname spec if `hostname` command fails ([#12987](https://github.com/crystal-lang/crystal/pull/12987), thanks @Blacksmoke16)\n- Fix `Socket#tty?` to `false` on Windows ([#13175](https://github.com/crystal-lang/crystal/pull/13175), thanks @Blacksmoke16)\n- Fix `HTTP::Server::Response#reset` for `status_message` ([#13282](https://github.com/crystal-lang/crystal/pull/13282), thanks @straight-shoota)\n\n#### Numeric\n\n- Define `Math.pw2ceil` for all integer types ([#13127](https://github.com/crystal-lang/crystal/pull/13127), thanks @HertzDevil)\n- Workaround for more `Int128`-and-float methods on Windows with LLVM 14+ ([#13218](https://github.com/crystal-lang/crystal/pull/13218), thanks @HertzDevil)\n- Fix `Int128`-and-float conversion overflow checks on Windows LLVM 14+ ([#13222](https://github.com/crystal-lang/crystal/pull/13222), thanks @HertzDevil)\n- Add `Char.to_i128` and `.to_u128` ([#12958](https://github.com/crystal-lang/crystal/pull/12958), thanks @meatball133)\n- Docs: Add references to `Number` collection convenience constructors ([#13020](https://github.com/crystal-lang/crystal/pull/13020), thanks @straight-shoota)\n- Docs: Fix examples for `#byte_swap` with different int types ([#13154](https://github.com/crystal-lang/crystal/pull/13154), [#13180](https://github.com/crystal-lang/crystal/pull/13180), thanks @pan, @Blacksmoke16)\n- Make `BigRational.new(BigFloat)` exact ([#13295](https://github.com/crystal-lang/crystal/pull/13295), thanks @HertzDevil)\n\n#### Runtime\n\n- Increase timeout for slow specs ([#13043](https://github.com/crystal-lang/crystal/pull/13043), thanks @straight-shoota)\n- Use `Crystal::System.print_error` instead of `LibC.printf` ([#13161](https://github.com/crystal-lang/crystal/pull/13161), thanks @HertzDevil)\n- Windows: detect stack overflows on non-main `Fiber`s ([#13220](https://github.com/crystal-lang/crystal/pull/13220), thanks @HertzDevil)\n- Add missing require for `Crystal::ThreadLocalValue` ([#13092](https://github.com/crystal-lang/crystal/pull/13092), thanks @Sija)\n\n#### Serialization\n\n- Remove obsolete error handling in `XPathContext` ([#13038](https://github.com/crystal-lang/crystal/pull/13038), thanks @straight-shoota)\n- Fix JSON, YAML `use_*_discriminator` for recursive `Serializable::Strict` types ([#13238](https://github.com/crystal-lang/crystal/pull/13238), thanks @HertzDevil)\n- Add more specs for `YAML::Any#[]` and `#[]?` ([#11646](https://github.com/crystal-lang/crystal/pull/11646), thanks @straight-shoota)\n- Add `from_json` for 128-bit integers ([#13041](https://github.com/crystal-lang/crystal/pull/13041), thanks @straight-shoota)\n- Reduce JSON, YAML serializable test types ([#13042](https://github.com/crystal-lang/crystal/pull/13042), thanks @straight-shoota)\n\n#### Specs\n\n- Format spec results with pretty inspect ([#11635](https://github.com/crystal-lang/crystal/pull/11635), thanks @JamesGood626)\n- Spec: Add `--color` option to spec runner ([#12932](https://github.com/crystal-lang/crystal/pull/12932), thanks @straight-shoota)\n- Add `Spec::Item#all_tags` ([#12915](https://github.com/crystal-lang/crystal/pull/12915), thanks @compumike)\n\n#### System\n\n- **(breaking-change)** Add full stub for Windows signals ([#13131](https://github.com/crystal-lang/crystal/pull/13131), thanks @HertzDevil)\n- **(breaking-change)** Deprecate and internalize `Process.fork` ([#12934](https://github.com/crystal-lang/crystal/pull/12934), thanks @straight-shoota)\n- Fix `Process` spec to wait on started processes ([#12941](https://github.com/crystal-lang/crystal/pull/12941), thanks @straight-shoota)\n- Drop privileges in chroot spec ([#13226](https://github.com/crystal-lang/crystal/pull/13226), thanks @straight-shoota)\n- Drop deprecated `from_winerror` overload for `flock_*` ([#13039](https://github.com/crystal-lang/crystal/pull/13039), thanks @HertzDevil)\n- Add `Process.on_interrupt` ([#13034](https://github.com/crystal-lang/crystal/pull/13034), thanks @HertzDevil)\n- Add `Process::Status#to_s` and `#inspect` ([#13044](https://github.com/crystal-lang/crystal/pull/13044), thanks @straight-shoota)\n- Add `graceful` parameter to `Process#terminate` ([#13070](https://github.com/crystal-lang/crystal/pull/13070), thanks @HertzDevil)\n- Add `Process::ExitReason` and `Process::Status#exit_reason` ([#13052](https://github.com/crystal-lang/crystal/pull/13052), thanks @HertzDevil)\n- Implement `File.tempfile` in Crystal ([#12111](https://github.com/crystal-lang/crystal/pull/12111), thanks @straight-shoota)\n- `System::User#name`: Fall back to `#username` if unavailable ([#13137](https://github.com/crystal-lang/crystal/pull/13137), thanks @HertzDevil)\n- Implement `Process.ppid` on Windows ([#13140](https://github.com/crystal-lang/crystal/pull/13140), thanks @HertzDevil)\n- AArch64 Android support ([#13065](https://github.com/crystal-lang/crystal/pull/13065), thanks @HertzDevil)\n- Windows 7 support ([#11505](https://github.com/crystal-lang/crystal/pull/11505), thanks @konovod)\n\n#### Text\n\n- **(breaking-change)** Fix PCRE crashing on invalid UTF-8 ([#13240](https://github.com/crystal-lang/crystal/pull/13240), [#13311](https://github.com/crystal-lang/crystal/pull/13311), [#13313](https://github.com/crystal-lang/crystal/pull/13313), thanks @straight-shoota)\n- **(breaking-change)** Switch default regex engine to PCRE2 ([#12978](https://github.com/crystal-lang/crystal/pull/12978), thanks @straight-shoota)\n- **(breaking-change)** Add more members to `Regex::Options` ([#13223](https://github.com/crystal-lang/crystal/pull/13223), thanks @straight-shoota)\n- **(breaking-change)** Add `Regex::MatchOptions` ([#13248](https://github.com/crystal-lang/crystal/pull/13248), thanks @straight-shoota)\n- Fix PCRE2 implementation and tests ([#13105](https://github.com/crystal-lang/crystal/pull/13105), thanks @straight-shoota)\n- Remove pending spec for `Path#drive` with IPv6 UNC host names ([#13190](https://github.com/crystal-lang/crystal/pull/13190), thanks @HertzDevil)\n- Remove `Regex::PCRE2#finalize` redefinition ([#13309](https://github.com/crystal-lang/crystal/pull/13309), thanks @HertzDevil)\n- Clarify behavior of strings with invalid UTF-8 byte sequences ([#13314](https://github.com/crystal-lang/crystal/pull/13314), thanks @HertzDevil)\n- Refer to PCRE2 in `Regex`'s summary ([#13318](https://github.com/crystal-lang/crystal/pull/13318), thanks @HertzDevil)\n\n### Compiler\n\n- Escape filenames when running `crystal spec` with multiple files ([#12929](https://github.com/crystal-lang/crystal/pull/12929), thanks @HertzDevil)\n- Handle ARM64 MSVC paths when cross-compiling on Windows ([#13073](https://github.com/crystal-lang/crystal/pull/13073), thanks @HertzDevil)\n- Use relative paths to vendored shards\" ([#13315](https://github.com/crystal-lang/crystal/pull/13315), thanks @straight-shoota)\n\n#### Debugger\n\n- Always use 0 for offsets of lib / extern union members ([#13305](https://github.com/crystal-lang/crystal/pull/13305), thanks @HertzDevil)\n\n#### Codegen\n\n- **(breaking-change)** Support LLVM 15 ([#13173](https://github.com/crystal-lang/crystal/pull/13173), thanks @HertzDevil)\n- Remove obsolete functions from `llvm_ext.cc` ([#13177](https://github.com/crystal-lang/crystal/pull/13177), thanks @HertzDevil)\n\n#### Generics\n\n- Fix type names for generic instances with empty splat type vars ([#13189](https://github.com/crystal-lang/crystal/pull/13189), thanks @HertzDevil)\n\n#### Interpreter\n\n- Fix: Interpreter `value_to_bool` for module, generic module and generic module metaclass ([#12920](https://github.com/crystal-lang/crystal/pull/12920), thanks @asterite)\n- Fix redundant cast in interpreter ([#12996](https://github.com/crystal-lang/crystal/pull/12996), thanks @asterite)\n- Dynamic library loader: search in `-L` directories before default ones ([#13069](https://github.com/crystal-lang/crystal/pull/13069), thanks @HertzDevil)\n- Simplify expectation of loader spec error messages ([#12858](https://github.com/crystal-lang/crystal/pull/12858), thanks @straight-shoota)\n- Add support for 128-bit literals in the interpreter ([#12859](https://github.com/crystal-lang/crystal/pull/12859), thanks @straight-shoota)\n- Fix interpreter `value_to_bool` for `NoReturn` ([#13290](https://github.com/crystal-lang/crystal/pull/13290), thanks @straight-shoota)\n\n#### Parser\n\n- Fix `x @y` and `x @@y` in def parameters when `y` is reserved ([#12922](https://github.com/crystal-lang/crystal/pull/12922), thanks @HertzDevil)\n- Disallow empty exponents in number literals ([#12910](https://github.com/crystal-lang/crystal/pull/12910), thanks @HertzDevil)\n- Stricter checks for multiple assignment syntax ([#12919](https://github.com/crystal-lang/crystal/pull/12919), thanks @HertzDevil)\n\n#### Semantic\n\n- Compiler: type declaration with initial value gets the value's type ([#13025](https://github.com/crystal-lang/crystal/pull/13025), thanks @asterite)\n- Stricter checks for enum definitions ([#12945](https://github.com/crystal-lang/crystal/pull/12945), thanks @HertzDevil)\n- Fix error handling in macro system method when execution fails ([#12893](https://github.com/crystal-lang/crystal/pull/12893), thanks @straight-shoota)\n- Add comment for `LiteralExpander` `select` ([#12926](https://github.com/crystal-lang/crystal/pull/12926), thanks @straight-shoota)\n- Improve locations of some AST nodes ([#12933](https://github.com/crystal-lang/crystal/pull/12933), thanks @straight-shoota)\n- Refactor `SemanticVisitor` tighter `rescue` scope in `Require` visitor ([#12887](https://github.com/crystal-lang/crystal/pull/12887), thanks @straight-shoota)\n- Add specs for regex literal expansion ([#13253](https://github.com/crystal-lang/crystal/pull/13253), thanks @straight-shoota)\n\n### Tools\n\n- Fix Crystal tool cursor parsing for filenames containing `:` ([#13129](https://github.com/crystal-lang/crystal/pull/13129), thanks @HertzDevil)\n\n#### Formatter\n\n- Formatter: fix end indent after comment inside begin ([#12994](https://github.com/crystal-lang/crystal/pull/12994), thanks @asterite)\n- Parser: remove obsolete handling of `else` inside lib struct ([#13028](https://github.com/crystal-lang/crystal/pull/13028), thanks @HertzDevil)\n- Fix formatter empty array literal with comment on extra line ([#12907](https://github.com/crystal-lang/crystal/pull/12907), thanks @straight-shoota)\n- Fix formatter comment on extra line at end of method args ([#12908](https://github.com/crystal-lang/crystal/pull/12908), thanks @straight-shoota)\n- Fix formatter not merge consecutive but separated comment lines ([#12909](https://github.com/crystal-lang/crystal/pull/12909), thanks @straight-shoota)\n- Formatter: add `(&)` to param-less yielding defs before comment line ([#13126](https://github.com/crystal-lang/crystal/pull/13126), thanks @HertzDevil)\n- Formatter: add `&` to yielding methods without a block parameter ([#12951](https://github.com/crystal-lang/crystal/pull/12951), thanks @HertzDevil)\n- Formatter: Add feature flag for `method_signature_yield` ([#13215](https://github.com/crystal-lang/crystal/pull/13215), thanks @straight-shoota)\n- Macro interpolation: add `&` to yielding `Def`s without a block parameter ([#12952](https://github.com/crystal-lang/crystal/pull/12952), thanks @HertzDevil)\n\n### Infrastructure\n\n- Fix `bin/crystal` print no error message when `crystal` is missing ([#12981](https://github.com/crystal-lang/crystal/pull/12981), thanks @straight-shoota)\n- Prevent infinitely recursive wrapper script ([#11712](https://github.com/crystal-lang/crystal/pull/11712), thanks @ThunderKey)\n- Changelog helper: Report error from HTTP request ([#13011](https://github.com/crystal-lang/crystal/pull/13011), thanks @straight-shoota)\n- Fix wrapper script to handle `CRYSTAL` variable pointing to itself ([#13032](https://github.com/crystal-lang/crystal/pull/13032), thanks @straight-shoota)\n- Propagate exit code correctly in Windows wrapper batch script ([#13048](https://github.com/crystal-lang/crystal/pull/13048), thanks @HertzDevil)\n- Remove `__declspec(dllimport)` from Windows libiconv build ([#13219](https://github.com/crystal-lang/crystal/pull/13219), thanks @HertzDevil)\n- Update previous Crystal release - 1.7.0 ([#12925](https://github.com/crystal-lang/crystal/pull/12925), thanks @straight-shoota)\n- [CI] Remove `verbose=1` in `test_llvm` ([#12931](https://github.com/crystal-lang/crystal/pull/12931), thanks @straight-shoota)\n- Missing quotes in Wrapper Script ([#12955](https://github.com/crystal-lang/crystal/pull/12955), thanks @stellarpower)\n- Makefile: refactor test recipe ([#12979](https://github.com/crystal-lang/crystal/pull/12979), thanks @straight-shoota)\n- Merge release branch for 1.7 into master ([#12998](https://github.com/crystal-lang/crystal/pull/12998), thanks @straight-shoota)\n- Update previous Crystal release - 1.7.2 ([#13001](https://github.com/crystal-lang/crystal/pull/13001), thanks @straight-shoota)\n- Update distribution-scripts ([#13051](https://github.com/crystal-lang/crystal/pull/13051), [#13068](https://github.com/crystal-lang/crystal/pull/13068), [#13188](https://github.com/crystal-lang/crystal/pull/13188), [#13213](https://github.com/crystal-lang/crystal/pull/13213), [#13298](https://github.com/crystal-lang/crystal/pull/13298), thanks @straight-shoota)\n- [CI] Use Ubuntu 22.04 base image for LLVM tests ([#13035](https://github.com/crystal-lang/crystal/pull/13035), thanks @straight-shoota)\n- Add instructions for other repos to pre-commit hook ([#10535](https://github.com/crystal-lang/crystal/pull/10535), thanks @straight-shoota)\n- Makefile: Add `./scripts` to `format` recipe ([#13064](https://github.com/crystal-lang/crystal/pull/13064), thanks @straight-shoota)\n- Crystal wrapper script enhancements ([#12959](https://github.com/crystal-lang/crystal/pull/12959), thanks @j8r)\n- Fix sed command in `scripts/update-distribution-scripts.cr` ([#13071](https://github.com/crystal-lang/crystal/pull/13071), thanks @straight-shoota)\n- Update GH Actions ([#13075](https://github.com/crystal-lang/crystal/pull/13075), [#13132](https://github.com/crystal-lang/crystal/pull/13132), thanks @renovate)\n- CI: Enable testing with `libpcre2` on wasm32 ([#13109](https://github.com/crystal-lang/crystal/pull/13109), thanks @lbguilherme)\n- Build the compiler with PCRE2 ([#13084](https://github.com/crystal-lang/crystal/pull/13084), [#13133](https://github.com/crystal-lang/crystal/pull/13133), thanks @straight-shoota)\n- Prefer matching `llvm-config` in  `find-llvm-config` ([#13087](https://github.com/crystal-lang/crystal/pull/13087), thanks @straight-shoota)\n- **(performance)** Run compiler specs in release mode ([#13122](https://github.com/crystal-lang/crystal/pull/13122), thanks @straight-shoota)\n- [CI] Increase `no_output_timeout` on circleci ([#13151](https://github.com/crystal-lang/crystal/pull/13151), thanks @straight-shoota)\n- Update NOTICE.md ([#13159](https://github.com/crystal-lang/crystal/pull/13159), thanks @HertzDevil)\n- Merge `release/1.7`@1.7.3 ([#13168](https://github.com/crystal-lang/crystal/pull/13168), thanks @straight-shoota)\n- [CI] Cancel in-progress jobs when another commit is pushed ([#13179](https://github.com/crystal-lang/crystal/pull/13179), thanks @Blacksmoke16)\n- Mute shell comments in Makefile ([#13201](https://github.com/crystal-lang/crystal/pull/13201), thanks @straight-shoota)\n- Update previous Crystal release - 1.7.3 ([#13167](https://github.com/crystal-lang/crystal/pull/13167), thanks @straight-shoota)\n- [CI] Remove cross-compilation on Windows ([#13207](https://github.com/crystal-lang/crystal/pull/13207), thanks @straight-shoota)\n- [CI] Increase `no_output_timeout` on circleci (cont.) ([#13185](https://github.com/crystal-lang/crystal/pull/13185), thanks @straight-shoota)\n- [CI] Update Windows job to LLVM 15 ([#13208](https://github.com/crystal-lang/crystal/pull/13208), thanks @straight-shoota)\n- Clean up `.gitignore` ([#13241](https://github.com/crystal-lang/crystal/pull/13241), thanks @straight-shoota)\n- [CI] Extract LLVM tests in separate workflow ([#13246](https://github.com/crystal-lang/crystal/pull/13246), thanks @straight-shoota)\n- [CI] Extract interpreter workflow and split `std_spec` execution ([#13267](https://github.com/crystal-lang/crystal/pull/13267), thanks @straight-shoota)\n- Avoid `test.cr` in root of repo conflicting with parser warning specs ([#13259](https://github.com/crystal-lang/crystal/pull/13259), thanks @Blacksmoke16)\n- Fix `bin/crystal` in symlink working directory ([#13281](https://github.com/crystal-lang/crystal/pull/13281), thanks @straight-shoota)\n- Fix `bin/crystal` when no global `crystal` command is installed ([#13286](https://github.com/crystal-lang/crystal/pull/13286), thanks @straight-shoota)\n- Makefile: Add `interpreter_spec` ([#13251](https://github.com/crystal-lang/crystal/pull/13251), thanks @straight-shoota)\n- Makefile: Add `all` target as default before including `Makefile.local` ([#13276](https://github.com/crystal-lang/crystal/pull/13276), thanks @straight-shoota)\n- Update shards 0.17.3 ([#13296](https://github.com/crystal-lang/crystal/pull/13296), thanks @straight-shoota)\n\n### Other\n\n- Do not match expectations outside specs ([#13079](https://github.com/crystal-lang/crystal/pull/13079), thanks @HertzDevil)\n- Enable or fix specs that already work on Windows ([#13186](https://github.com/crystal-lang/crystal/pull/13186), thanks @HertzDevil)\n"
  },
  {
    "path": "doc/changelogs/v1.9.md",
    "content": "# Changelog 1.9\n\n## [1.9.2] - 2023-07-19\n\n[1.9.2]: https://github.com/crystal-lang/crystal/releases/1.9.2\n\n### Bugfixes\n\n#### stdlib\n\n- _(runtime)_ Revert \"Add default interrupt handlers\" ([#13673](https://github.com/crystal-lang/crystal/pull/13673), thanks @straight-shoota)\n\n## [1.9.1] - 2023-07-17\n\n[1.9.1]: https://github.com/crystal-lang/crystal/releases/1.9.1\n\n### Bugfixes\n\n#### stdlib\n\n- _(serialization)_ Fix `Serializable` with converter parsing `null` value ([#13656](https://github.com/crystal-lang/crystal/pull/13656), thanks @straight-shoota)\n\n#### compiler\n\n- _(codegen)_ Fix generated cc command for cross compile ([#13661](https://github.com/crystal-lang/crystal/pull/13661), thanks @fnordfish)\n\n## [1.9.0] - 2023-07-11\n\n[1.9.0]: https://github.com/crystal-lang/crystal/releases/1.9.0\n\n### Breaking changes\n\n#### stdlib\n\n- _(numeric)_ Handle NaNs when comparing `Big*` numbers against `Float` ([#13293](https://github.com/crystal-lang/crystal/pull/13293), [#13294](https://github.com/crystal-lang/crystal/pull/13294), [#13350](https://github.com/crystal-lang/crystal/pull/13350), [#13554](https://github.com/crystal-lang/crystal/pull/13554), thanks @HertzDevil)\n- _(llvm)_ Remove most `LLVM::DIBuilder` functions from `llvm_ext.cc` ([#13448](https://github.com/crystal-lang/crystal/pull/13448), thanks @HertzDevil)\n\n### Features\n\n#### lang\n\n- _(macros)_ Add `warning` macro ([#13262](https://github.com/crystal-lang/crystal/pull/13262), thanks @Blacksmoke16)\n- _(macros)_ Add `print` macro ([#13336](https://github.com/crystal-lang/crystal/pull/13336), thanks @jkthorne)\n\n#### stdlib\n\n- _(collection)_ Add `Enumerable#in_slices_of` ([#13108](https://github.com/crystal-lang/crystal/pull/13108), thanks @pricelessrabbit)\n- _(collection)_ Add support for dash separator to `Enum.parse` ([#13508](https://github.com/crystal-lang/crystal/pull/13508), thanks @straight-shoota)\n- _(collection)_ Add `Enum#to_i128` and `#to_u128` ([#13576](https://github.com/crystal-lang/crystal/pull/13576), thanks @meatball133)\n- _(collection)_ Add `Enumerable#partition` overload with type filter ([#13572](https://github.com/crystal-lang/crystal/pull/13572), thanks @baseballlover723)\n- _(concurrency)_ Support asynchronous `IO.pipe` on Windows ([#13362](https://github.com/crystal-lang/crystal/pull/13362), thanks @HertzDevil)\n- _(files)_ **[deprecation]** Add `File::MatchOptions` to control `Dir.glob`'s behavior ([#13550](https://github.com/crystal-lang/crystal/pull/13550), thanks @HertzDevil)\n- _(networking)_ Implement `Socket#reuse_port` on Windows ([#13326](https://github.com/crystal-lang/crystal/pull/13326), thanks @stakach)\n- _(networking)_ Add multicast support to `UDPSocket` on Windows ([#13325](https://github.com/crystal-lang/crystal/pull/13325), thanks @stakach)\n- _(networking)_ HTTP Server should allow custom concurrency models ([#13428](https://github.com/crystal-lang/crystal/pull/13428), thanks @stakach)\n- _(networking)_ Add `Socket::IPaddress.v4`, `.v6`, `.v4_mapped_v6` ([#13422](https://github.com/crystal-lang/crystal/pull/13422), thanks @HertzDevil)\n- _(networking)_ Add `URI::Params#merge`, `#merge!` and `URI#update_query_params` ([#13415](https://github.com/crystal-lang/crystal/pull/13415), thanks @skinnyjames)\n- _(networking)_ Support Unix sockets on Windows ([#13493](https://github.com/crystal-lang/crystal/pull/13493), thanks @HertzDevil)\n- _(networking)_ Add `HTTP::Request#form_params` ([#13418](https://github.com/crystal-lang/crystal/pull/13418), thanks @threez)\n- _(numeric)_ Add `BigDecimal#%` ([#13255](https://github.com/crystal-lang/crystal/pull/13255), thanks @MattAlp)\n- _(numeric)_ Improve conversions from `BigInt` to `Int::Primitive` ([#13562](https://github.com/crystal-lang/crystal/pull/13562), thanks @HertzDevil)\n- _(runtime)_ Print error if unable to delay-load DLL on Windows ([#13475](https://github.com/crystal-lang/crystal/pull/13475), thanks @HertzDevil)\n- _(runtime)_ Add default interrupt handlers ([#13568](https://github.com/crystal-lang/crystal/pull/13568), thanks @straight-shoota) ⚠️ This was reverted in 1.9.2\n- _(serialization)_ Add `ignore_serialize` for `YAML::Serializable` ([#13556](https://github.com/crystal-lang/crystal/pull/13556), thanks @meatball133)\n- _(specs)_ Add a testcase line number to the output of JUnitFormatter ([#13468](https://github.com/crystal-lang/crystal/pull/13468), thanks @nobodywasishere)\n- _(specs)_ Publish the `assert_prints` spec helper ([#13599](https://github.com/crystal-lang/crystal/pull/13599), thanks @HertzDevil)\n- _(system)_ Implement `Process.exec` on Windows ([#13374](https://github.com/crystal-lang/crystal/pull/13374), thanks @HertzDevil)\n- _(system)_ Add `File::BadExecutableError` ([#13491](https://github.com/crystal-lang/crystal/pull/13491), thanks @HertzDevil)\n- _(text)_ Add inspection of Regex options support ([#13354](https://github.com/crystal-lang/crystal/pull/13354), thanks @straight-shoota)\n- _(text)_ Add `Regex.literal` ([#13339](https://github.com/crystal-lang/crystal/pull/13339), thanks @straight-shoota)\n- _(text)_ Implement `#match!` for Regex ([#13285](https://github.com/crystal-lang/crystal/pull/13285), thanks @devnote-dev)\n- _(text)_ Add parameters for `Regex::MatchOptions` to matching methods ([#13353](https://github.com/crystal-lang/crystal/pull/13353), thanks @straight-shoota)\n- _(text)_ Add `Char#titlecase` for correct mixed-case transformations ([#13539](https://github.com/crystal-lang/crystal/pull/13539), thanks @HertzDevil)\n- _(time)_ Add `start_day` parameter to `Time#at_beginning_of_week` ([#13446](https://github.com/crystal-lang/crystal/pull/13446), thanks @DanielGilchrist)\n- _(time)_ Map IANA time zone identifiers to Windows time zones ([#13517](https://github.com/crystal-lang/crystal/pull/13517), thanks @HertzDevil)\n- _(time)_ Add `Time.unix_ns` and `#to_unix_ns` ([#13359](https://github.com/crystal-lang/crystal/pull/13359), thanks @garymardell)\n\n#### compiler\n\n- Add message about non-release mode to `crystal --version` ([#13254](https://github.com/crystal-lang/crystal/pull/13254), thanks @will)\n- Respect `%CC%` on Windows ([#13376](https://github.com/crystal-lang/crystal/pull/13376), thanks @HertzDevil)\n- Support DLL delay-loading on Windows ([#13436](https://github.com/crystal-lang/crystal/pull/13436), thanks @HertzDevil)\n- Support `-static` and `-dynamic` `.lib` suffixes on Windows ([#13473](https://github.com/crystal-lang/crystal/pull/13473), [#13645](https://github.com/crystal-lang/crystal/pull/13645), thanks @HertzDevil)\n- Make compiler aware of output extension when building programs ([#13370](https://github.com/crystal-lang/crystal/pull/13370), thanks @HertzDevil)\n- Support `CRYSTAL_LIBRARY_RPATH` for adding dynamic library lookup paths ([#13499](https://github.com/crystal-lang/crystal/pull/13499), thanks @HertzDevil)\n- Add compiler command `crystal clear_cache` ([#13553](https://github.com/crystal-lang/crystal/pull/13553), thanks @baseballlover723)\n- _(codegen)_ Support LLVM 16 ([#13181](https://github.com/crystal-lang/crystal/pull/13181), thanks @HertzDevil)\n- _(semantic)_ Correctly ignore nested deprecation warnings ([#13513](https://github.com/crystal-lang/crystal/pull/13513), thanks @straight-shoota)\n\n#### tools\n\n- _(docs-generator)_ Add dark mode to docs ([#13512](https://github.com/crystal-lang/crystal/pull/13512), thanks @GeopJr)\n- _(docs-generator)_ Add mobile support to docs ([#13515](https://github.com/crystal-lang/crystal/pull/13515), thanks @GeopJr)\n- _(formatter)_ **[security]** Formatter: escape bi-directional control characters within strings ([#13067](https://github.com/crystal-lang/crystal/pull/13067), thanks @HertzDevil)\n\n### Bugfixes\n\n#### stdlib\n\n- _(collection)_ Fix `Array#flatten` to discard `Iterator::Stop` ([#13388](https://github.com/crystal-lang/crystal/pull/13388), thanks @straight-shoota)\n- _(collection)_ Fix return type of `Iterator#chunk` and `Enumerable#chunks` without `Drop` ([#13506](https://github.com/crystal-lang/crystal/pull/13506), thanks @straight-shoota)\n- _(collection)_ Fix `Iterator#with_index(offset)` with non-`Int32` `offset` ([#13612](https://github.com/crystal-lang/crystal/pull/13612), thanks @HertzDevil)\n- _(concurrency)_ Fix `preview_mt` infinite loop on Windows ([#13419](https://github.com/crystal-lang/crystal/pull/13419), thanks @HertzDevil)\n- _(concurrency)_ Fix `Atomic#max` and `#min` for signed enums ([#13524](https://github.com/crystal-lang/crystal/pull/13524), thanks @HertzDevil)\n- _(concurrency)_ Fix timeout events getting lost on Windows ([#13525](https://github.com/crystal-lang/crystal/pull/13525), thanks @HertzDevil)\n- _(concurrency)_ Support `Atomic(T)#compare_and_set` when `T` is a reference union ([#13565](https://github.com/crystal-lang/crystal/pull/13565), thanks @HertzDevil)\n- _(files)_ Fix `Dir#info` on Windows ([#13395](https://github.com/crystal-lang/crystal/pull/13395), thanks @HertzDevil)\n- _(files)_ Windows: open standard streams in binary mode ([#13397](https://github.com/crystal-lang/crystal/pull/13397), thanks @HertzDevil)\n- _(files)_ Fix `File.info(File::NULL)` on Windows ([#13421](https://github.com/crystal-lang/crystal/pull/13421), thanks @HertzDevil)\n- _(files)_ Allow `File.delete` to remove read-only files on Windows ([#13462](https://github.com/crystal-lang/crystal/pull/13462), thanks @HertzDevil)\n- _(files)_ Make `fcntl` defined on all platforms ([#13495](https://github.com/crystal-lang/crystal/pull/13495), thanks @HertzDevil)\n- _(files)_ Allow `Dir.delete` to remove read-only directories on Windows ([#13626](https://github.com/crystal-lang/crystal/pull/13626), thanks @HertzDevil)\n- _(files)_ Use current directory's root for `Dir.glob(\"/...\")` on Windows ([#13628](https://github.com/crystal-lang/crystal/pull/13628), thanks @HertzDevil)\n- _(llvm)_ Fix `LLVM.default_target_triple` to normalize aarch64 darwin target ([#13597](https://github.com/crystal-lang/crystal/pull/13597), thanks @straight-shoota)\n- _(log)_ Fix `Log::Builder` append `BroadcastBackend` to itself ([#13405](https://github.com/crystal-lang/crystal/pull/13405), thanks @straight-shoota)\n- _(macros)_ Fix error message for calling `record` macro with kwargs ([#13367](https://github.com/crystal-lang/crystal/pull/13367), thanks @a-alhusaini)\n- _(networking)_ Remove double URL escape in `HTTP::Server::Response.redirect` ([#13321](https://github.com/crystal-lang/crystal/pull/13321), thanks @threez)\n- _(networking)_ Fix WebSocket capitalization in docs ([#13331](https://github.com/crystal-lang/crystal/pull/13331), thanks @joshrickard)\n- _(networking)_ Fix `TCPSocket#tcp_keepalive_idle` on Windows ([#13364](https://github.com/crystal-lang/crystal/pull/13364), thanks @HertzDevil)\n- _(networking)_ Fix client-side `TCPSocket#remote_address` on Windows ([#13363](https://github.com/crystal-lang/crystal/pull/13363), thanks @HertzDevil)\n- _(networking)_ Parse IP addresses in Crystal instead of using `LibC.inet_pton` ([#13463](https://github.com/crystal-lang/crystal/pull/13463), thanks @HertzDevil)\n- _(networking)_ Windows: do not set `SO_EXCLUSIVEADDRUSE` if `SO_REUSEADDR` already present ([#13477](https://github.com/crystal-lang/crystal/pull/13477), thanks @HertzDevil)\n- _(networking)_ Implement `Socket::IPAddress#to_s` with Crystal instead of `LibC.inet_ntop` ([#13483](https://github.com/crystal-lang/crystal/pull/13483), thanks @HertzDevil)\n- _(networking)_ Ensure `Socket` checks `WinError.wsa_value` on Windows, not `Errno.value` ([#13494](https://github.com/crystal-lang/crystal/pull/13494), thanks @HertzDevil)\n- _(numeric)_ Disallow creating `Big*` numbers from infinity or NaN ([#13351](https://github.com/crystal-lang/crystal/pull/13351), thanks @HertzDevil)\n- _(numeric)_ Fix `LibM.hypotf` and `ldexpf` link errors on Windows ([#13485](https://github.com/crystal-lang/crystal/pull/13485), thanks @HertzDevil)\n- _(numeric)_ Make comparisons between `BigRational` and `BigFloat` exact ([#13538](https://github.com/crystal-lang/crystal/pull/13538), thanks @HertzDevil)\n- _(runtime)_ Fix size of type_id in `Object.set_crystal_type_id` ([#13338](https://github.com/crystal-lang/crystal/pull/13338), thanks @straight-shoota)\n- _(runtime)_ Allow `/SUBSYSTEM:WINDOWS` on Windows ([#13332](https://github.com/crystal-lang/crystal/pull/13332), thanks @HertzDevil)\n- _(runtime)_ Use correct format strings for crash stack traces ([#13408](https://github.com/crystal-lang/crystal/pull/13408), thanks @HertzDevil)\n- _(serialization)_ Fix handling of quoted boolean values in `YAML::Any` ([#13546](https://github.com/crystal-lang/crystal/pull/13546), thanks @willhbr)\n- _(serialization)_ Fix ambiguous call with untyped int literal in `{JSON,YAML}::Any.new` ([#13618](https://github.com/crystal-lang/crystal/pull/13618), thanks @straight-shoota)\n- _(system)_ Fix for Process: ensure chdir is a string ([#13503](https://github.com/crystal-lang/crystal/pull/13503), thanks @devnote-dev)\n- _(system)_ Windows: drop internal environment variables from `ENV` ([#13570](https://github.com/crystal-lang/crystal/pull/13570), thanks @HertzDevil)\n- _(text)_ Fix `String#underscore` with multi-character downcasing ([#13540](https://github.com/crystal-lang/crystal/pull/13540), thanks @HertzDevil)\n- _(text)_ Do not attempt downcasing first when case-folding a `Char` ([#13542](https://github.com/crystal-lang/crystal/pull/13542), thanks @HertzDevil)\n- _(text)_ Handle case folding in `String#compare` correctly ([#13532](https://github.com/crystal-lang/crystal/pull/13532), thanks @HertzDevil)\n- _(time)_ Update list of Windows time zones ([#13501](https://github.com/crystal-lang/crystal/pull/13501), thanks @HertzDevil)\n- _(time)_ Fix local timezones without DST on Windows ([#13516](https://github.com/crystal-lang/crystal/pull/13516), thanks @HertzDevil)\n- _(time)_ Fix leap second handling for timezone information files ([#13600](https://github.com/crystal-lang/crystal/pull/13600), thanks @HertzDevil)\n\n#### compiler\n\n- More accurate macro errors ([#13260](https://github.com/crystal-lang/crystal/pull/13260), thanks @Blacksmoke16)\n- Do not drop `/LIBPATH` from Windows linker command ([#13530](https://github.com/crystal-lang/crystal/pull/13530), thanks @HertzDevil)\n- Fix instantiated method signatures in error traces ([#13580](https://github.com/crystal-lang/crystal/pull/13580), thanks @HertzDevil)\n- Place `--emit` files back in current directory when running source ([#13604](https://github.com/crystal-lang/crystal/pull/13604), thanks @HertzDevil)\n- _(generics)_ Fix free variable matching of bound numeric values ([#13606](https://github.com/crystal-lang/crystal/pull/13606), thanks @HertzDevil)\n- _(parser)_ Don't skip the token immediately after `lib` name ([#13407](https://github.com/crystal-lang/crystal/pull/13407), thanks @FnControlOption)\n- _(parser)_ Allow newline after hash rocket ([#13460](https://github.com/crystal-lang/crystal/pull/13460), thanks @FnControlOption)\n- _(parser)_ Add missing locations of various AST nodes ([#13452](https://github.com/crystal-lang/crystal/pull/13452), thanks @FnControlOption)\n- _(parser)_ Fix AST location of call name in operator assignment ([#13456](https://github.com/crystal-lang/crystal/pull/13456), thanks @FnControlOption)\n\n#### tools\n\n- Display `Bool`'s size as 1 byte in `crystal tool hierarchy`, not 0 ([#13588](https://github.com/crystal-lang/crystal/pull/13588), thanks @HertzDevil)\n\n### Performance\n\n#### stdlib\n\n- _(collection)_ Optimize `Array#concat(Indexable)` ([#13280](https://github.com/crystal-lang/crystal/pull/13280), thanks @HertzDevil)\n- _(collection)_ Optimize `Deque#concat(Indexable)` ([#13283](https://github.com/crystal-lang/crystal/pull/13283), thanks @HertzDevil)\n- _(concurrency)_ Support synchronous socket operations on Windows ([#13414](https://github.com/crystal-lang/crystal/pull/13414), thanks @HertzDevil)\n- _(numeric)_ Optimize `BigInt.new(Int::Primitive)` ([#13303](https://github.com/crystal-lang/crystal/pull/13303), thanks @HertzDevil)\n- _(numeric)_ Optimize `BigRational#<=>(Int)` ([#13555](https://github.com/crystal-lang/crystal/pull/13555), thanks @HertzDevil)\n- _(text)_ Improve `HTML.escape(string, io)` performance ([#13139](https://github.com/crystal-lang/crystal/pull/13139), thanks @BlobCodes)\n- _(text)_ Refactor `String.ends_with?` to use `MatchOptions::ENDANCHORED` ([#13551](https://github.com/crystal-lang/crystal/pull/13551), thanks @straight-shoota)\n\n#### tools\n\n- _(docs-generator)_ Optimize `Doc::Method#compute_doc_info` to avoid duplicate regex ([#13324](https://github.com/crystal-lang/crystal/pull/13324), thanks @straight-shoota)\n\n### Refactor\n\n#### stdlib\n\n- Use sentence case for all standard library exceptions ([#13400](https://github.com/crystal-lang/crystal/pull/13400), thanks @HertzDevil)\n- _(collection)_ Refactor code for `Deque` buffer resizing ([#13257](https://github.com/crystal-lang/crystal/pull/13257), thanks @HertzDevil)\n- _(concurrency)_ Clean up unused code for Windows event loop ([#13424](https://github.com/crystal-lang/crystal/pull/13424), thanks @HertzDevil)\n- _(files)_ Do not reopen current file in `File#utime` on Windows ([#13625](https://github.com/crystal-lang/crystal/pull/13625), thanks @HertzDevil)\n- _(files)_ Do not reopen current file in `File#chmod` on Windows ([#13627](https://github.com/crystal-lang/crystal/pull/13627), thanks @HertzDevil)\n- _(llvm)_ **[deprecation]** Deprecate `LLVM::Module#write_bitcode_with_summary_to_file` ([#13488](https://github.com/crystal-lang/crystal/pull/13488), thanks @HertzDevil)\n- _(llvm)_ **[deprecation]** Deprecate LLVM's legacy pass manager ([#13579](https://github.com/crystal-lang/crystal/pull/13579), thanks @HertzDevil)\n- _(llvm)_ Remove two outdated LLVM fun bindings ([#13438](https://github.com/crystal-lang/crystal/pull/13438), thanks @HertzDevil)\n- _(llvm)_ Split `LLVM::Builder` overloads that don't take an operand bundle ([#13564](https://github.com/crystal-lang/crystal/pull/13564), thanks @HertzDevil)\n- _(networking)_ Move more `Socket` methods to `Crystal::System::Socket` ([#13346](https://github.com/crystal-lang/crystal/pull/13346), thanks @HertzDevil)\n- _(numeric)_ Use `Int#bit_length` instead of `Math.log2` followed by `#to_i` ([#13440](https://github.com/crystal-lang/crystal/pull/13440), thanks @HertzDevil)\n- _(numeric)_ Use GMP's functions for `Float`-to-`BigRational` conversion ([#13352](https://github.com/crystal-lang/crystal/pull/13352), thanks @HertzDevil)\n- _(serialization)_ Simplify implementation of `Serializable#initialize` ([#13433](https://github.com/crystal-lang/crystal/pull/13433), thanks @straight-shoota)\n- _(serialization)_ Use per-thread libxml2 global state on Windows ([#13486](https://github.com/crystal-lang/crystal/pull/13486), thanks @HertzDevil)\n- _(text)_ Refactor String header layout reflection ([#13335](https://github.com/crystal-lang/crystal/pull/13335), thanks @straight-shoota)\n- _(text)_ Refactor symbol quoting into `Symbol.quote_for_named_argument` ([#13595](https://github.com/crystal-lang/crystal/pull/13595), thanks @straight-shoota)\n\n#### compiler\n\n- _(parser)_ Crystal lexer cleanup ([#13270](https://github.com/crystal-lang/crystal/pull/13270), thanks @FnControlOption)\n- _(parser)_ Don't use symbols in `Crystal::Lexer#check_macro_opening_keyword` ([#13277](https://github.com/crystal-lang/crystal/pull/13277), thanks @HertzDevil)\n\n### Documentation\n\n#### stdlib\n\n- _(concurrency)_ Fix operators in `Atomic#add`, `#sub`, `#max`, `#min` ([#13523](https://github.com/crystal-lang/crystal/pull/13523), thanks @HertzDevil)\n- _(concurrency)_ Hide `Crystal::LibEvent` from public docs ([#13624](https://github.com/crystal-lang/crystal/pull/13624), thanks @HertzDevil)\n- _(macros)_ Fix doc for return type of `Crystal::Macros::Case#else` ([#13385](https://github.com/crystal-lang/crystal/pull/13385), thanks @HertzDevil)\n- _(system)_ Reference `Process.executable_path` at `PROGRAM_NAME` ([#13434](https://github.com/crystal-lang/crystal/pull/13434), thanks @straight-shoota)\n- _(text)_ Add note about graphemes in `String#reverse` ([#13536](https://github.com/crystal-lang/crystal/pull/13536), thanks @noraj)\n\n#### compiler\n\n- Add manual entry for `clear_cache` command ([#13621](https://github.com/crystal-lang/crystal/pull/13621), thanks @straight-shoota)\n\n#### other\n\n- Implemented ',' command in brainfuck sample program ([#13559](https://github.com/crystal-lang/crystal/pull/13559), thanks @ZeroPlayerRodent)\n\n### Specs\n\n#### stdlib\n\n- _(files)_ Fix `IO::FileDescriptor`'s `STDIN` mode spec ([#13365](https://github.com/crystal-lang/crystal/pull/13365), thanks @HertzDevil)\n- _(files)_ Fix file permission specs on Windows ([#13465](https://github.com/crystal-lang/crystal/pull/13465), thanks @HertzDevil)\n- _(files)_ Add `slow` tag to stdlib specs that compile a program ([#13498](https://github.com/crystal-lang/crystal/pull/13498), thanks @straight-shoota)\n- _(serialization)_ Refactor JSON, YAML specs for #13337 for simplicity ([#13358](https://github.com/crystal-lang/crystal/pull/13358), thanks @straight-shoota)\n- _(system)_ Disable `Process.pgid` spec on Windows ([#13476](https://github.com/crystal-lang/crystal/pull/13476), thanks @HertzDevil)\n- _(text)_ Reorder and enhance specs for `String.new(&)` ([#13333](https://github.com/crystal-lang/crystal/pull/13333), thanks @straight-shoota)\n- _(text)_ Remove incorrect `CRYSTAL` in comments ([#13500](https://github.com/crystal-lang/crystal/pull/13500), thanks @HertzDevil)\n- _(time)_ Skip `Time::Location.load_local` spec if unable to change time zone ([#13355](https://github.com/crystal-lang/crystal/pull/13355), thanks @HertzDevil)\n\n#### compiler\n\n- _(interpreter)_ Regenerate `spec/interpreter_std_spec.cr` ([#13310](https://github.com/crystal-lang/crystal/pull/13310), thanks @cyangle)\n\n### Infrastructure\n\n- Update changelog with previous Crystal releases ([#13322](https://github.com/crystal-lang/crystal/pull/13322), [#13373](https://github.com/crystal-lang/crystal/pull/13373), [#13450](https://github.com/crystal-lang/crystal/pull/13450), thanks @straight-shoota)\n- Merge `release/1.8` ([#13361](https://github.com/crystal-lang/crystal/pull/13361), [#13449](https://github.com/crystal-lang/crystal/pull/13449), thanks @straight-shoota)\n- PR template: adding a line about force-pushes ([#12794](https://github.com/crystal-lang/crystal/pull/12794), thanks @beta-ziliani)\n- Less verbose output in `Makefile.win` ([#13383](https://github.com/crystal-lang/crystal/pull/13383), thanks @HertzDevil)\n- Update distribution-scripts ([#13457](https://github.com/crystal-lang/crystal/pull/13457), thanks @Blacksmoke16)\n- Add `.gitattributes` to repository ([#13479](https://github.com/crystal-lang/crystal/pull/13479), thanks @HertzDevil)\n- Update `shell.nix` to nixpkgs-23.05 ([#13571](https://github.com/crystal-lang/crystal/pull/13571), thanks @HertzDevil)\n- Document `target` variable in Makefiles ([#13384](https://github.com/crystal-lang/crystal/pull/13384), thanks @HertzDevil)\n- Fix `bin\\crystal.ps1` writing to standard error stream ([#13372](https://github.com/crystal-lang/crystal/pull/13372), thanks @HertzDevil)\n- Avoid calling realpath of parent crystal in wrapper script ([#13596](https://github.com/crystal-lang/crystal/pull/13596), thanks @straight-shoota)\n- _(ci)_ Show PCRE/PCRE2 configuration on CI ([#13307](https://github.com/crystal-lang/crystal/pull/13307), thanks @HertzDevil)\n- _(ci)_ Update cachix/install-nix-action action ([#13531](https://github.com/crystal-lang/crystal/pull/13531), [#13586](https://github.com/crystal-lang/crystal/pull/13586), thanks @renovate)\n- _(ci)_ Restrict Windows CI jobs for installer packages to release branches ([#13623](https://github.com/crystal-lang/crystal/pull/13623), thanks @HertzDevil)\n- _(ci)_ Build samples on Windows CI ([#13334](https://github.com/crystal-lang/crystal/pull/13334), thanks @HertzDevil)\n- _(ci)_ Do not cancel in progress CI jobs for master branch ([#13393](https://github.com/crystal-lang/crystal/pull/13393), thanks @Blacksmoke16)\n- _(ci)_ Upgrade Windows CI to LLVM 16 ([#13469](https://github.com/crystal-lang/crystal/pull/13469), thanks @HertzDevil)\n- _(ci)_ Distribute DLLs and import libraries on Windows ([#13543](https://github.com/crystal-lang/crystal/pull/13543), thanks @HertzDevil)\n- _(ci)_ Build Windows portable and installer packages on CI ([#13578](https://github.com/crystal-lang/crystal/pull/13578), thanks @HertzDevil)\n- _(ci)_ Split Windows library build scripts from CI ([#13478](https://github.com/crystal-lang/crystal/pull/13478), thanks @HertzDevil)\n"
  },
  {
    "path": "doc/man/crystal-build.adoc",
    "content": "= crystal-build(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-build - Compile a Crystal program\n\n== Synopsis\n*crystal build* [options] [programfile] [--] [arguments]\n\n== Options\n\n*--cross-compile*::\nGenerate an object file for cross compilation and prints the command to build the executable.\tThe object file should be copied\nto the target system and the printed command should be executed\nthere. This flag mainly exists for porting the compiler to new\nplatforms, where possible run the compiler on the target platform\ndirectly.\n*-d*, *--debug*::\nGenerate the output with symbolic debug symbols.  These are read\nwhen debugging the built program with tools like lldb, gdb, valgrind etc. and provide mappings to the original source code for\nthose tools.\n*--no-debug*::\nGenerate the output without any symbolic debug symbols.\n*-D* _FLAG_, *--define* _FLAG_::\nDefine a compile-time flag. This is useful to conditionally define types, methods, or commands based on flags available at compile time. The default flags are from the target triple given\nwith *--target-triple*  or the hosts default, if none is given.\n*--emit* [asm|llvm-bc|llvm-ir|obj]::\nComma separated list of types of output for the compiler to emit.\nYou can use this to see the generated LLVM IR, LLVM bitcode, assembly, and object files.\n*--x86-asm-syntax* [att|intel]::\nSelect the assembly dialect for *--emit=asm*. The default is `att`, which stands\nfor the AT&T syntax supported by tools like the GNU Assembler. `intel` selects\nthe Intel syntax which is preferred for Windows tools.\n*--frame-pointers* [auto|always|non-leaf]::\nControl the preservation of frame pointers. The default value,\n`--frame-pointers=auto`, will preserve frame pointers on debug\nbuilds and try to omit them on release builds (certain platforms\nrequire them to stay enabled). `--frame-pointers=always` will always preserve them, and non-leaf will only force their preservation on non-leaf functions.\n*-f* text|json, *--format* text|json::\nFormat of output. Defaults to text. The json format can be used\nto get a more parser-friendly output.\n*--error-trace*::\nShow full error trace. Disabled by default, as the full trace\nusually makes error messages less readable and may not always deliver\nrelevant information.\n*--ll*:: \t Dump LLVM assembly file to output directory.\n*--link-flags* _FLAGS_::\nPass additional flags to the linker. Though you can specify those\nflags on the source code, this is useful for passing environment\nspecific information directly to the linker, like non-standard\nlibrary paths or names. For more information on specifying linker\nflags on source, you can read the \"C bindings\" section of the\ndocumentation available on the official web site.\n*--mcpu* _CPU_::\nSpecify a specific CPU to generate code for. This will pass a\n-mcpu flag to LLVM, and is only intended to be used for cross-\ncompilation. For a list of available CPUs, pass --mcpu help\nwhen building any Crystal source code.  Passing --mcpu native\nwill pass the host CPU name to tune performance for the host.\n*--mattr* _CPU_::\nOverride or control specific attributes of the target, such as\nwhether SIMD operations are enabled or not. The default set of\nattributes is set by the current CPU. This will pass a -mattr\nflag to LLVM, and is only intended to be used for cross-compilation. For a list of available attributes, invoke \"llvm-as <\n/dev/null | llc -march=xyz -mattr=help\".\n*--mcmodel* default|kernel|tiny|small|medium|large::\nSpecifies a specific code model to generate code for. This will\npass a --code-model flag to LLVM.\n*--no-color*::\nDisable colored output.\n*--no-codegen*::\nDon't do code generation, just parse the file.\n*-o* _FILE_, *--output* _FILE_::\nSpecify output path. If a directory, the filename is derived from the first source file (default: current directory)\n*--prelude*::\nSpecify prelude to use. The default one initializes the garbage\ncollector. You can also use --prelude=empty to use no preludes.\nThis can be useful for checking code generation for a specific\nsource code file.\n*-O* _LEVEL_::  Optimization mode: 0 (default), 1, 2, 3. See *OPTIMIZATIONS* for\ndetails.\n*--release*::\nCompile in release mode. Equivalent to *-O3 --single-module*\n*-s*, *--stats*::\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler process.\n*-p*, *--progress*::\nPrint statistics about the progress for the current build.\n*-t*, *--time*::\nPrint statistics about the execution time.\n*--single-module*::\nGenerate a single LLVM module.  By default, one LLVM module is\ncreated for each type in a program.  *--release* implies this option.\n*--threads* _NUM_::\nMaximum number of threads to use for code generation. The default\nis 8 threads.\n*--target* _TRIPLE_::\nEnable target triple; intended to use for cross-compilation. See\nllvm documentation for more information about target triple.\n*--verbose*::\nDisplay the commands executed by the system.\n*--static*::\t Create a statically linked executable.\n*--stdin-filename* _FILENAME_::\nSource file name to be read from STDIN.\n"
  },
  {
    "path": "doc/man/crystal-docs.adoc",
    "content": "= crystal-docs(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-doc - Generate API docs for Crystal code\n\n== Synopsis\n*crystal build* [options] [programfile] [--] [arguments]\n\n== Description\n\nGenerate documentation from comments using a subset of markdown. The output\nis saved in html format on the created docs/ folder. More information about\ndocumentation conventions can be found at <https://crystal-lang.org/docs/conventions/documenting_code.html>.\n\n== Options\n\n*--project-name* _NAME_::\nSet the project name. The default value is extracted from\nshard.yml if available.\n+\nIn case no default can be found, this option is mandatory.\n*--project-version* _VERSION_::\nSet the project version. The default value is extracted from current git commit or shard.yml if available.\n+\nIn case no default can be found, this option is mandatory.\n*--json-config-url* _URL_::\nSet the URL pointing to a config file (used for discovering versions).\n*--source-refname* _REFNAME_::\nSet source refname (e.g. git tag, commit hash). The default value\nis extracted from current git commit if available.\n+\nIf this option is missing and can't be automatically determined,\nthe generator can't produce source code links.\n*--source-url-pattern* _URL_::\nSet URL pattern for source code links. The default value is extracted from git remotes (\"origin\" or first one) if available and\nthe provider's URL pattern is recognized.\n+\nSupported replacement tags:\n+\n--\n*%{refname}*::  commit reference\n*%{path}*::     path to source file inside the repository\n*%{filename}*::\n  basename of the source file\n*%{line}*::     line number\n--\n+\nIf this option is missing and can't be automatically determined,\nthe generator can't produce source code links.\n*-o* _DIR_, *--output* _DIR_::\nSet the output directory (default: ./docs).\n*-b* _URL_, **--canonical-base-url** _URL_::\nIndicate the preferred URL with rel=\"canonical\" link element.\n*-b* _URL_, *--sitemap-base-url* _URL_::\nSet the sitemap base URL. Sitemap will only be generated when\nthis option is set.\n*--sitemap-priority* _PRIO_::\nSet the priority assigned to sitemap entries (default: 1.0).\n*--sitemap-changefreq* _FREQ_::\nSet the changefreq assigned to sitemap entries (default: never).\n"
  },
  {
    "path": "doc/man/crystal-env.adoc",
    "content": "= crystal-env(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-env - Print environment variables for the Crystal compiler\n\n== Synopsis\n*crystal env* [variables]\n\n== Description\n\nPrint Crystal-specific environment variables in a format compatible with\nshell scripts. If one or more variable names are given as arguments, it\nprints only the value of each named variable on its own line.\n\nVariables:\n\n*CRYSTAL_CACHE_DIR*::\nPlease see ENVIRONMENT VARIABLES.\n\n*CRYSTAL_EXEC_PATH*::\nPlease see ENVIRONMENT VARIABLES.\n\n*CRYSTAL_LIBRARY_PATH*::\nPlease see ENVIRONMENT VARIABLES.\n\n*CRYSTAL_PATH*::\nPlease see ENVIRONMENT VARIABLES.\n\n*CRYSTAL_VERSION*::\nContains Crystal version.\n\n== Informative Variables\n\nThese variables expose information about the Crystal compiler and cannot be configured externally.\n\n=== CRYSTAL_VERSION\n\nContains Crystal version.\n"
  },
  {
    "path": "doc/man/crystal-eval.adoc",
    "content": "= crystal-eval(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-eval - Evaluate a crystal program\n\n== Synopsis\n*crystal eval* [options] [source]\n\n== Description\n\nEvaluate code from arguments or, if no arguments are passed, from the standard input. Useful for experiments.\n\n== Options\n\n*-d*, *--debug*::\nGenerate the output with symbolic debug symbols.  These are read\nwhen debugging the built program with tools like lldb, gdb, valgrind etc. and provide mappings to the original source code for\nthose tools.\n*--no-debug*::\nGenerate the output without any symbolic debug symbols.\n*-D* _FLAG_, *--define* _FLAG_::\nDefine a compile-time flag. This is useful to conditionally define types, methods, or commands based on flags available at compile time. The default flags are from the target triple given\nwith --target-triple or the hosts default, if none is given.\n*--error-trace*::\nShow full error trace.\n*-O* _LEVEL_::\t Optimization mode: 0 (default), 1, 2, 3. See *OPTIMIZATIONS* for details.\n*--release*::\nCompile in release mode. Equivalent to *-O3 --single-module*\n*-s*, *--stats*::\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler\nprocess.\n*-p*, *--progress*::\nPrint statistics about the progress for the current build.\n*-t*, *--time*::\nPrint statistics about the execution time.\n*--no-color*::\nDisable colored output.\n"
  },
  {
    "path": "doc/man/crystal-init.adoc",
    "content": "= crystal-init(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-init - Create a a new Crystal project\n\n== Synopsis\n*crystal init* TYPE [DIR | NAME DIR]\n\n== Description\n\nTYPE is one of:\n\n*lib*\t Creates a library skeleton +\n*app*\t Creates an application skeleton\n\nThis initializes the lib/app project folder as a git repository, with a license file, a README file, a shard.yml for use with shards (the Crystal dependency manager), a .gitignore file, and src and spec folders.\n\nDIR  - directory where project will be generated\n\nNAME - name of project to be generated (default: basename of DIR)\n\n== Options\n\n*-f, --force*:: Force overwrite existing files.\n*-s, --skip-existing*:: Skip existing files.\n"
  },
  {
    "path": "doc/man/crystal-play.adoc",
    "content": "= crystal-play(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-play - Run the Crystal playground\n\n== Synopsis\n*crystal play* [options] [file]\n\n== Description\n\nStarts the *crystal* playground server on port 8080, by default.\n\n== Options\n\n*-p* _PORT_, *--port* _PORT_::\nRun the playground on the specified port. Default is 8080.\n*-b* _HOST_, *--binding* _HOST_::\nBind the playground to the specified IP.\n*-v*, *--verbose*::\nDisplay detailed information of the executed code.\n"
  },
  {
    "path": "doc/man/crystal-run.adoc",
    "content": "= crystal-run(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-run - Compile and run a Crystal program\n\n== Synopsis\n*crystal run* [options] [programfile] [--] [arguments]\n\n== Description\n\nThe default command. Compile and run program.\n\n== Options\n\nSame as the build options.\n"
  },
  {
    "path": "doc/man/crystal-spec.adoc",
    "content": "= crystal-spec(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-spec - Compile and run Crystal spec files\n\n== Synopsis\n*crystal spec* [options] [files]\n\n== Options\n\n*-d*, *--debug*::\nGenerate the output with symbolic debug symbols.  These are read\nwhen debugging the built program with tools like lldb, gdb, valgrind etc. and provide mappings to the original source code for\nthose tools.\n*--no-debug*::\nGenerate the output without any symbolic debug symbols.\n*-D* _FLAG_, *--define* _FLAG_::\nDefine a compile-time flag. This is useful to conditionally define types, methods, or commands based on flags available at compile time. The default flags are from the target triple given\nwith *--target-triple*  or the hosts default, if none is given.\n*--error-trace*::\nShow full error trace.\n*-O* _LEVEL_::\t Optimization mode: 0 (default), 1, 2, 3. See *OPTIMIZATIONS* for details.\n*--release*::\nCompile in release mode. Equivalent to *-O3 --single-module*\n*-s*, *--stats*::\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler\nprocess.\n*-p*, *--progress*::\nPrint statistics about the progress for the current build.\n*-t*, *--time*::\nPrint statistics about the execution time.\n*--no-color*::\nDisable colored output.\n"
  },
  {
    "path": "doc/man/crystal-tool-dependencies.adoc",
    "content": "= crystal-tool-dependencies(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-tool-dependencies - Show tree of required source files.\n\n== Synopsis\n*crystal tool dependencies* [options] [programfile]\n\n== Options\n\n*-D* _FLAG_, *--define*=_FLAG_::\n  Define a compile-time flag. This is useful to con    ditionally define types, methods, or commands based\n  on flags available at compile time. The default\n  flags are from the target triple given with *--tar*     get-triple or the hosts default, if none is given.\n*-f* _FORMAT_, *--format*=_FORMAT_::\n  Output format 'tree' (default), 'flat', 'dot', or\n  'mermaid'.\n*-i* _PATH_, *--include*=_PATH_::\n  Include path in output.\n*-e* _PATH_, *--exclude*=_PATH_::\n  Exclude path in output.\n*--error-trace*::\n  Show full error trace.\n*--prelude*::\n  Specify prelude to use. The default one initializes\n  the garbage collector. You can also use *--pre*     lude=empty to use no preludes. This can be useful\n  for checking code generation for a specific source\n  code file.\n*--verbose*::\n  Show skipped and heads of filtered paths\n"
  },
  {
    "path": "doc/man/crystal-tool-format.adoc",
    "content": "= crystal-tool-format(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-tool-format - Format Crystal source files\n\n== Synopsis\n*crystal tool format* [options] [source...]\n\n== Description\n\nFormat project, directories and/or files with the coding\nstyle used in the standard library.\n\n== Options\n\n*--check*:: Only check conformity but do not apply any changes.\n"
  },
  {
    "path": "doc/man/crystal-tool-macro_code_coverage.adoc",
    "content": "= crystal-tool-macro_code_coverage(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-tool-macro_code_coverage - generate a macro code coverage report.\n\n== Synopsis\n*crystal tool macro_code_coverage* [options] [programfile]\n\n== Description\n\nGenerate and output a macro code coverage report to STDERR.\nAny exception raised while computing the report is written to STDOUT.\n\n== Options\n\n*-D* _FLAG_, *--define*=_FLAG_::\n  Define a compile-time flag. This is useful to con    ditionally define types, methods, or commands based\n  on flags available at compile time. The default\n  flags are from the target triple given with *--tar*     get-triple or the hosts default, if none is given.\n*-f* _FORMAT_, *--format*=_FORMAT_::\n  Output format 'codecov' (default).\n*-i* _PATH_, *--include*=_PATH_::\n  Include path in output.\n*-e* _PATH_, *--exclude*=_PATH_::\n  Exclude path in output (default: lib).\n*--error-trace*::\n  Show full error trace.\n*--prelude*::\n  Specify prelude to use. The default one initializes\n  the garbage collector. You can also use *--pre*     lude=empty to use no preludes. This can be useful\n  for checking code generation for a specific source\n  code file.\n*-s*, *--stats*::\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler\nprocess.\n*-p*, *--progress*::\nPrint statistics about the progress for the current build.\n*-t*, *--time*::\nPrint statistics about the execution time.\n*--no-color*::\nDisable colored output.\n"
  },
  {
    "path": "doc/man/crystal-tool-unreachable.adoc",
    "content": "= crystal-tool-unreachable(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal-tool-unreachable - Identify methods that are never called\n\n== Synopsis\n*crystal tool unreachable* [options] [source...]\n\n== Description\n\nShow methods that are never called. The text output is a list\nof lines with columns separated by tab.\n\nOutput fields:\n\n*count*::\t sum of all calls to this method (only with *--tallies* option; otherwise skipped)\n*location*::\t pathname, line and column, all separated by colon name\n*lines*::\t length of the def in lines annotations\n\n== Options\n\n*-D* _FLAG_, *--define*=_FLAG_::\n  Define a compile-time flag. This is useful to con    ditionally define types,\n  methods, or commands based on flags available at compile time. The default\n  flags are from the target triple given with *--target-triple* or the hosts\n  default, if none is given.\n*-f* _FORMAT_, *--format*=_FORMAT_::\n  Output format 'text' (default), 'json', 'codecov', or 'csv'.\n*--tallies*::\n  Print reachable methods and their call counts as well.\n*--check*::    Exit with error if there is any unreachable code.\n*-i* _PATH_, *--include*=_PATH_::\n  Include path in output.\n*-e* _PATH_, *--exclude*=_PATH_::\n  Exclude path in output (default: lib).\n*--error-trace*::\n  Show full error trace.\n*--prelude*::\n  Specify prelude to use. The default one initializes the garbage collector. You\n  can also use *--prelude=empty* to use no preludes. This can be useful for\n  checking code generation for a specific source code file.\n"
  },
  {
    "path": "doc/man/crystal.adoc",
    "content": "= crystal(1)\n:doctype: manpage\n:date: {localdate}\n:crystal_version: {crystal_version}\n:man manual: Crystal Compiler Command Line Reference Guide\n:man source: crystal {crystal_version}\n\n== Name\ncrystal - compiler for the Crystal language\n\n== Synopsis\n*crystal* command [switches] programfile -- [arguments]\n\n== Description\nCrystal is a statically type-checked programming language. It was created with the\nbeauty of Ruby and the performance of C in mind.\n\n== Usage\nYou can compile and run a program by invoking the compiler with a single filename:\n\n```shell\ncrystal some_program.cr\n```\n\nCrystal files usually end with the .cr extension, though this is not mandatory.\n\nAlternatively you can use the run command:\n\n```shell\ncrystal run some_program.cr\n```\n\nTo create an executable use the build command:\n\n```shell\ncrystal build some_program.cr\n```\n\nThis will create an executable named \"some_program\".\n\nNote that by default the generated executables are not fully optimized.  To turn optimizations on, use the *--release*  flag:\n\n```shell\ncrystal build --release some_program.cr\n```\n\nMake sure to always use *--release*  for production-ready executables and when performing benchmarks.\n\nThe optimizations are not turned on by default because the compile times are much\nfaster without them and the performance of the program is still pretty good without\nthem, so it allows to use the *crystal* command almost to be used as if it was an interpreter.\n\n== Crystal Commands\n\n=== Main commands\n\n*crystal-init(1)*:: Create a a new Crystal project\n\n*crystal-build(1)*:: Compile a Crystal program\n\n*crystal-docs(1)*:: Generate API docs for Crystal code\n\n*crystal-env(1)*:: Print environment variables for the Crystal compiler\n\n*crystal-eval(1)*:: Evaluate a crystal program\n\n*crystal-play(1)*:: Run the Crystal playground\n\n*crystal-run(1)*:: Compile and run a Crystal program\n\n*crystal-spec(1)*:: Compile and run Crystal spec files\n\n*crystal-clear_cache*::\n\nClear the compiler cache (located at 'CRYSTAL_CACHE_DIR').\n\n*crystal-help*::\n\nShow help. Option *--help*  or *-h*  can also be added to each command for command-specific\nhelp.\n\n*crystal-version*::\n\nShow version.\n\n=== Tools\n\n*crystal-tool-context*:: Show context for given location\n\n*crystal-tool-dependencies(1)*:: Show tree of required source files\n\n*crystal-tool-expand*::  Show macro expansion for given location\n\n*crystal-tool-flags*::   Print all macro 'flag?' values\n\n*crystal-tool-format(1)*:: Format Crystal source files\n\n*crystal-tool-hierarchy*::\nShow hierarchy of types from file. Also show class and struct\nmembers, with type and size. Types can be filtered with a\nregex by using the *-e* flag.\n\n*crystal-tool-implementations*::\nShow implementations for a given call. Use *--cursor*  to specify the cursor position. The format for the cursor position\nis file:line:column.\n\n*crystal-tool-macro_code_coverage*::  Generate a macro code coverage report.\n\n*crystal-tool-types*::  Show type of main variables of file.\n\n*crystal-tool-unreachable(1)*:: Identify methods that are never called\n\n== Optimizations\nThe optimization level specifies the codegen effort for producing optimal code.  It's\na trade-off between compilation performance (decreasing per optimization level) and\nruntime performance (increasing per optimization level).\n\nProduction builds should usually have the highest optimization level.  Best results\nare achieved with *--release*  which also implies *--single-module*\n\n*-O0*::       No optimization (default)\n*-O1*::       Low optimization\n*-O2*::       Middle optimization\n*-O3*::       High optimization\n*-Os*::        Middle optimization with focus on file size\n*-Oz*::        Middle optimization aggressively focused on file size\n\n== Environment Variables\n\n=== CRYSTAL_CACHE_DIR\nDefines path where Crystal caches partial compilation results for faster\nsubsequent builds. This path is also used to temporarily store executables\nwhen Crystal programs are run with '*crystal* run' rather than '*crystal*\nbuild'.\n\n=== CRYSTAL_EXEC_PATH\nDetermines the path where *crystal* looks for external sub-commands.\n\n=== CRYSTAL_LIBRARY_PATH\nDefines paths where Crystal searches for (binary) libraries. Multiple paths\ncan be separated by \":\".\t These paths are passed to the linker as `-L`\nflags.\n\nThe pattern '$ORIGIN' at the start of the path expands to the directory\nwhere the compiler binary is located. For example, '$ORIGIN/../lib/crystal'\nresolves the standard library path relative to the compiler location in a\ngeneric way, independent of the absolute paths (assuming the relative location is correct).\n\n=== CRYSTAL_PATH\nDefines paths where Crystal searches for required source files. Multiple\npaths can be separated by \":\".\n\nThe pattern '$ORIGIN' at the start of the path expands to the directory\nwhere the compiler binary is located. For example, '$ORIGIN/../share/crystal/src' resolves the standard library path relative to the compiler location in a generic way, independent of the absolute paths (assuming the relative location is correct).\n\n=== CRYSTAL_OPTS\nDefines options for the Crystal compiler to be used besides the command\nline arguments. The syntax is identical to the command line arguments. This\nis handy when using Crystal in build setups, for example 'CRYSTAL_OPTS=--debug make build'.\n\n== Seealso\n\n*shards*(1)\n\n<https://crystal-lang.org/>\t\t\t   The official web site.\n\n<https://github.com/crystal-lang/crystal> \t   Official Repository.\n"
  },
  {
    "path": "etc/completion.bash",
    "content": "# Bash completion for \"crystal\" command.\n# Written by Sergey Potapov <blake131313@gmail.com>.\n\n# Return list of options, that match $pattern\n_crystal_compgen_options(){\n    local options=$1\n    local pattern=$2\n\n    while IFS=$' \\n' read -r line; do\n        COMPREPLY+=(\"$line\")\n    done < <(compgen -W \"${options}\" -- \"${pattern}\")\n}\n\n# Return list of crystal sources or directories, that match $pattern\n_crystal_compgen_sources(){\n    local pattern=$1\n\n    type compopt &> /dev/null && compopt -o filenames\n    while IFS=$'\\n' read -r line; do\n        COMPREPLY+=(\"$line\")\n    done < <(compgen -f -o plusdirs -X '!*.cr' -- \"${pattern}\")\n}\n\n# Return list of files or directories, that match $pattern (the default action)\n_crystal_compgen_files(){\n    local pattern=$1\n\n    type compopt &> /dev/null && compopt -o filenames\n    while IFS=$'\\n' read -r line; do\n        COMPREPLY+=(\"$line\")\n    done < <(compgen -o default -- \"${pattern}\")\n}\n\n_crystal()\n{\n    local IFS=$' \\n'\n    local program=${COMP_WORDS[0]}\n    local cmd=${COMP_WORDS[1]}\n    local cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    local prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n    COMPREPLY=()\n\n    commands=\"init build clear_cache docs eval play run spec tool help version --help --version\"\n\n    case \"${cmd}\" in\n        init)\n            if [[ \"${prev}\" == \"init\" ]] ; then\n                local opts=\"app lib\"\n                _crystal_compgen_options \"${opts}\" \"${cur}\"\n            else\n                _crystal_compgen_files \"${cur}\"\n            fi\n            ;;\n        build)\n            if [[ \"${cur}\" == -* ]] ; then\n                local opts=\"--cross-compile --debug --emit --error-on-warnings --exclude-warnings --ll --link-flags --mcpu --no-color --no-codegen --output --prelude --release --single-module --threads --target --verbose --warnings --x86-asm-syntax --help\"\n                _crystal_compgen_options \"${opts}\" \"${cur}\"\n            else\n                _crystal_compgen_sources \"${cur}\"\n            fi\n            ;;\n        run)\n            if [[ \"${cur}\" == -* ]] ; then\n                local opts=\"--debug --define --emit --error-on-warnings --exclude-warnings --format --help --ll --link-flags --mcpu --no-color --no-codegen --output --prelude --release --stats --single-module --threads --verbose --warnings --x86-asm-syntax\"\n                _crystal_compgen_options \"${opts}\" \"${cur}\"\n            else\n                _crystal_compgen_sources \"${cur}\"\n            fi\n            ;;\n        tool)\n            if [[ \"${cur}\" == -* ]] ; then\n                local opts=\"--no-color --prelude --define --format --cursor\"\n                _crystal_compgen_options \"${opts}\" \"${cur}\"\n            else\n                if [[ \"${prev}\" == \"tool\" ]] ; then\n                    local subcommands=\"context dependencies expand flags format hierarchy implementations macro_code_coverage types unreachable\"\n                    _crystal_compgen_options \"${subcommands}\" \"${cur}\"\n                else\n                    _crystal_compgen_sources \"${cur}\"\n                fi\n            fi\n            ;;\n        play)\n            if [[ ${cur} == -* ]] ; then\n                local opts=\"--port --binding --verbose --help\"\n                _crystal_compgen_options \"${opts}\" \"${cur}\"\n            else\n                _crystal_compgen_sources \"${cur}\"\n            fi\n            ;;\n        clear_cache|docs|eval|spec|version|help)\n            # These commands do not accept any options nor subcommands\n            _crystal_compgen_files \"${cur}\"\n            ;;\n        *)\n            # When any of subcommands matches directly\n            if [[ \"${prev}\" == \"${program}\" && $(compgen -W \"${commands}\" -- \"${cur}\") ]] ; then\n                _crystal_compgen_options \"${commands}\" \"${cur}\"\n            else\n                _crystal_compgen_sources \"${cur}\"\n            fi\n    esac\n    return 0\n}\n\ncomplete -F _crystal crystal\n"
  },
  {
    "path": "etc/completion.fish",
    "content": "set -l crystal_commands init build clear_cache docs env eval i interactive play run spec tool help version\nset -l tool_subcommands context dependencies expand flags format hierarchy implementations types unreachable\n\ncomplete -c crystal -s h -l help -d \"Show help\" -x\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"init\" -d \"Generate a new project\"\ncomplete -c crystal -f -n \"__fish_seen_subcommand_from init\" -a \"lib\" -d \"Creates a library skeleton\"\ncomplete -c crystal -f -n \"__fish_seen_subcommand_from init\" -a \"app\" -d \"Creates an application skeleton\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"build\" -d \"Build an executable\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l cross-compile -d \"cross-compile\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s d -l debug -d \"Add full symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l no-debug -d \"Skip any symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l emit -d \"Comma separated list of types of output for the compiler to emit\" -a \"asm obj llvm-bc llvm-ir\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l x86-asm-syntax -d \"X86 dialect for --emit=asm: att (default), intel\" -a \"att intel\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l ll -d \"Dump ll to Crystal's cache directory\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l link-flags -d \"Additional flags to pass to the linker\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l mcpu -d \"Target specific cpu type\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l mattr -d \"Target specific features\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l mcmodel -d \"Target specific code model\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l warnings -d \"Which warnings detect. (default: all)\" -a \"all none\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l error-on-warnings -d \"Treat warnings as errors\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l exclude-warnings -d \"Exclude warnings from path (default: lib)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l no-codegen -d \"Don't do code generation\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s o -l output -d \"Output filename\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l release -d \"Compile in release mode\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l single-module -d \"Generate a single LLVM module\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l threads -d \"Maximum number of threads to use\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l target -d \"Target triple\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l verbose -d \"Display executed commands\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l static -d \"Link statically\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from build\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"clear_cache\" -d \"clear the compiler cache\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"docs\" -d \"generate documentation\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l project-name -d \"Set project name\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l project-version -d \"Set project version\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l source-refname -d \"Set source refname (e.g. git tag, commit hash)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l source-url-pattern -d \"Set URL pattern for source code links\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s o -l output -d \"Set the output directory (default: ./docs)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s f -l format -d \"Set the output format (default: html)\" -a \"html json\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l json-config-url -d \"Set the URL pointing to a config file (used for discovering versions)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l canonical-base-url -d \"Indicate the preferred URL with rel=\"canonical\" link element\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s b -l sitemap-base-url -d \"Set the sitemap base URL and generates sitemap\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l sitemap-priority -d \"Set the sitemap priority (default: 1.0)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l sitemap-changefreq -d \"Set the sitemap changefreq (default: never) \"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l warnings -d \"Which warnings detect (default: all)\" -a \"all none\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l error-on-warnings -d \"Treat warnings as errors\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from docs\" -l exclude-warnings -d \"Exclude warnings from path (default: lib)\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"env\" -d \"print Crystal environment information\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"eval\" -d \"eval code from args or standard input\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -s d -l debug -d \"Add full symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l no-debug -d \"Skip any symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l release -d \"Compile in release mode\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l mcpu -d \"Target specific cpu type\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l mattr -d \"Target specific features\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l mcmodel -d \"Target specific code model\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l warnings -d \"Which warnings detect. (default: all)\" -a \"all none\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l error-on-warnings -d \"Treat warnings as errors\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from eval\" -l exclude-warnings -d \"Exclude warnings from path (default: lib)\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"i interactive\" -d \"starts interactive Crystal\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"play\" -d \"starts Crystal playground server\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from play\" -s p -l port -d \"Runs the playground on the specified port\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from play\" -s b -l binding -d \"Binds the playground to the specified IP\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from play\" -s v -l verbose -d \"Display detailed information of executed code\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"run\" -d \"build and run program\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s d -l debug -d \"Add full symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l no-debug -d \"Skip any symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l emit -d \"Comma separated list of types of output for the compiler to emit\" -a \"asm obj llvm-bc llvm-ir\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l x86-asm-syntax -d \"X86 dialect for --emit=asm: att (default), intel\" -a \"att intel\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l ll -d \"Dump ll to Crystal's cache directory\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l link-flags -d \"Additional flags to pass to the linker\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l mcpu -d \"Target specific cpu type\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l mattr -d \"Target specific features\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l mcmodel -d \"Target specific code model\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l warnings -d \"Which warnings detect. (default: all)\" -a \"all none\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l error-on-warnings -d \"Treat warnings as errors\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l exclude-warnings -d \"Exclude warnings from path (default: lib)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l no-codegen -d \"Don't do code generation\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s o -l output -d \"Output filename\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l release -d \"Compile in release mode\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l single-module -d \"Generate a single LLVM module\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l threads -d \"Maximum number of threads to use\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l verbose -d \"Display executed commands\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l static -d \"Link statically\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from run\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"spec\" -d \"build and run specs\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -s d -l debug -d \"Add full symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l no-debug -d \"Skip any symbolic debug info\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l release -d \"Compile in release mode\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l mcpu -d \"Target specific cpu type\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l mattr -d \"Target specific features\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l mcmodel -d \"Target specific code model\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l warnings -d \"Which warnings detect. (default: all)\" -a \"all none\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l error-on-warnings -d \"Treat warnings as errors\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l exclude-warnings -d \"Exclude warnings from path (default: lib)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from spec\" -l link-flags -d \"Additional flags to pass to the linker\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"tool\" -d \"run a tool\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"context\" -d \"show context for given location\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s c -l cursor -d \"Cursor location with LOC as path/to/file.cr:line:column\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"dependencies\" -d \"show tree of required source files\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s i -l include -d \"Include path in output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s e -l exclude -d \"Exclude path in output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s f -l format -d \"Output format 'tree' (default), 'flat', 'dot', or 'mermaid'.\" -a \"tree flat dot mermaid\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from context\" -s t -l time -d \"Enable execution time output\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"expand\" -d \"show macro expansion for given location\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -s c -l cursor -d \"Cursor location with LOC as path/to/file.cr:line:column\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from expand\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"flags\" -d \"print all macro 'flag?' values\" -x\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"format\" -d \"format project, directories and/or files\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from format\" -l check -d \"Checks that formatting code produces no changes\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from format\" -s i -l include -d \"Include path\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from format\" -s e -l exclude -d \"Exclude path (default: lib)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from format\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from format\" -l show-backtrace -d \"Show backtrace on a bug (used only for debugging)\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"hierarchy\" -d \"show type hierarchy\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -s e -d \"Filter types by NAME regex\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from hierarchy\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"implementations\" -d \"show implementations for given call in location\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -s c -l cursor -d \"Cursor location with LOC as path/to/file.cr:line:column\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from implementations\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"unreachable\" -d \"show methods that are never called\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s f -l format -d \"Output format text (default), json, csv, codecov\" -a \"text json csv codecov\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -l tallies -d \"Print reachable methods and their call counts as well\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -l check -d \"Exits with error if there is any unreachable code\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s i -l include -d \"Include path\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s e -l exclude -d \"Exclude path (default: lib)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from unreachable\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"macro_code_coverage\" -d \"generate a macro code coverage report\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s f -l format -d \"Output format codecov (default)\" -a \"codecov\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s i -l include -d \"Include path\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s e -l exclude -d \"Exclude path (default: lib)\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from macro_code_coverage\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands\" -a \"types\" -d \"show type of main variables\" -x\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -s D -l define -d \"Define a compile-time flag\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -s f -l format -d \"Output format text (default) or json\" -a \"text json\" -f\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -l error-trace -d \"Show full error trace\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -l no-color -d \"Disable colored output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -l prelude -d \"Use given file as prelude\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -s s -l stats -d \"Enable statistics output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -s p -l progress -d \"Enable progress output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -s t -l time -d \"Enable execution time output\"\ncomplete -c crystal -n \"__fish_seen_subcommand_from types\" -l stdin-filename -d \"Source file name to be read from STDIN\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"help\" -d \"show help\"\n\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -a \"version\" -d \"show version\"\ncomplete -c crystal -n \"not __fish_seen_subcommand_from $crystal_commands\" -s v -l version -d \"show version\" -x\n"
  },
  {
    "path": "etc/completion.zsh",
    "content": "#compdef crystal\n\n_crystal() {\n\n_crystal_commands() {\n  local -a commands\n  commands=(\n    \"init:generate new crystal project\"\n    \"build:build an executable\"\n    \"clear_cache:clear the compiler cache\"\n    \"docs:generate documentation\"\n    \"env:print Crystal environment information\"\n    \"eval:eval code from args or standard input\"\n    \"play:starts playground server\"\n    \"run:build and run program\"\n    \"spec:build and run specs (in spec directory)\"\n    \"tool:run a tool\"\n    \"help:show help\"\n    \"version:show version\"\n  )\n  _describe -t commands 'Crystal command' commands\n}\n\nlocal -a help_args; help_args=(\n  '(-h --help)'{-h,--help}'[show help]'\n)\n\nlocal -a version_args; version_args=(\n  '(-v --version)'{-v,--version}'[show version]' \\\n)\n\nlocal -a no_color_args; no_color_args=(\n  '(--no-color)--no-color[disable colored output]'\n)\n\nlocal -a prelude_args; prelude_args=(\n  '(--prelude)--prelude[use given file as prelude]'\n)\n\nlocal -a exec_args; exec_args=(\n  '(\\*)'{-D+,--define=}'[define a compile-time flag]:' \\\n  '(--error-trace)--error-trace[show full error trace]' \\\n  '(-s --stats)'{-s,--stats}'[enable statistics output]' \\\n  '(-t --time)'{-t,--time}'[enable execution time output]' \\\n  '(-p --progress)'{-p,--progress}'[enable progress output]'\n)\n\nlocal -a format_args; format_args=(\n  '(-f --format)'{-f,--format}'[output format text (default) or json]:'\n)\n\nlocal -a debug_args; debug_args=(\n  '(-d --debug)'{-d,--debug}'[add full symbolic debug info]' \\\n  '(--no-debug)--no-debug[skip any symbolic debug info]'\n)\n\nlocal -a release_args; release_args=(\n  '(--release)--release[compile in release mode]'\n)\n\nlocal -a cursor_args; cursor_args=(\n  '(-c --cursor)'{-c,--cursor}'[cursor location with LOC as path/to/file.cr:line:column]:LOC'\n)\n\nlocal -a include_exclude_args; include_exclude_args=(\n  '(-i --include)'{-i,--include}'[Include path in output]' \\\n  '(-i --exclude)'{-i,--exclude}'[Exclude path in output]'\n)\n\nlocal -a stdin_filename_args; stdin_filename_args=(\n  '(--stdin-filename)--stdin-filename[source file name to be read from STDIN]'\n)\n\nlocal -a programfile; programfile='*:Crystal File:_files -g \"*.cr(.)\"'\n\n# TODO make 'emit' allow completion with more than one\nlocal -a shared_run_build; shared_run_build=(\n  $programfile \\\n  $help_args \\\n  $no_color_args \\\n  $prelude_args \\\n  $format_args \\\n  $exec_args \\\n  $debug_args \\\n  $release_args \\\n  '(--emit)--emit[comma separated list of types of output for the compiler to emit]:foo:(asm llvm-bc llvm-ir obj)' \\\n  '(--x86-asm-syntax)--x86-asm-syntax[X86 dialect for --emit-asm: att (default), intel]:' \\\n  \"(--ll)--ll[dump ll to Crystal's cache directory ]\" \\\n  '(--link-flags)--link-flags[additional flags to pass to the linker]:' \\\n  '(--mcpu)--mcpu[target specific cpu type]:' \\\n  '(--mattr)--mattr[target specific features]:' \\\n  \"(--no-codegen)--no-codegen[don't do code generation]\" \\\n  '(-o --output)'{-o,--output}'[output filename]:' \\\n  '(--single-module)--single-module[generate a single llvm module]' \\\n  '(--threads)--threads[maximum number of threads to use]' \\\n  '(--verbose)--verbose[display executed commands]' \\\n)\n\n# TODO add help text for name and dir\n_crystal-init() {\n  _arguments \\\n    '1:type:(lib app)' \\\n    && ret=0\n}\n\n_crystal-build() {\n  _arguments \\\n    $shared_run_build \\\n    '(--cross-compile)--cross-compile[cross-compile FLAGS]:' \\\n    '(--target)--target[target triple]:' \\\n    && ret=0\n}\n\n_crystal-env() {\n  _arguments \\\n    '(--help)--help[prints help]' \\\n    '1:type:(CRYSTAL_CACHE_DIR CRYSTAL_PATH CRYSTAL_VERSION CRYSTAL_OPTS)' \\\n    && ret=0\n}\n\n_crystal-eval() {\n  _arguments \\\n    $help_args \\\n    $no_color_args \\\n    $exec_args \\\n    $debug_args \\\n    $release_args \\\n    && ret=0\n}\n\n_crystal-play() {\n  _arguments \\\n    $programfile \\\n    '(--port)--port[PORT]:' \\\n    '(--binding)--binding[HOST]:' \\\n    '(--verbose)--verbose[display detailed information of executed code]' \\\n    '(-h --help)'{-h,--help}'[show help]' \\\n    && ret=0\n}\n\n_crystal-run() {\n  _arguments \\\n    $shared_run_build \\\n    && ret=0\n}\n\n_crystal-spec() {\n  _arguments \\\n    $programfile \\\n    $help_args \\\n    $no_color_args \\\n    $exec_args \\\n    $debug_args \\\n    $release_args \\\n    && ret=0\n}\n\n_crystal-tool() {\n  local curcontext=\"$curcontext\" state line\n  typeset -A opt_args\n\n  _arguments -C \\\n    ':command:->command' \\\n    '*::options:->options'\n\n  case $state in\n    (command)\n      local -a commands\n\n      commands=(\n        \"context:show context for given location\"\n        \"dependencies:show tree of required source files\"\n        \"expand:show macro expansion for given location\"\n        \"flags:print all macro 'flag?' values\"\n        \"format:format project, directories and/or files\"\n        \"hierarchy:show type hierarchy\"\n        \"implementations:show implementations for given call in location\"\n        \"macro_code_coverage:generate a macro code coverage report\"\n        \"types:show type of main variables\"\n        \"unreachable:show methods that are never called\"\n      )\n\n      _describe -t commands 'Crystal tool command' commands\n\n      _arguments $help_args\n    ;;\n\n    (options)\n      case $line[1] in\n        (context)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $format_args \\\n            $prelude_args \\\n            $stdin_filename_args \\\n            $cursor_args\n        ;;\n\n        (dependencies)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            '(-f --format)'{-f,--format}'[output format 'tree' (default), 'flat', 'dot', or 'mermaid']:' \\\n            $prelude_args \\\n            $stdin_filename_args \\\n            $include_exclude_args\n        ;;\n\n        (expand)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $format_args \\\n            $prelude_args \\\n            $stdin_filename_args \\\n            $cursor_args\n        ;;\n\n        (flags)\n          _arguments \\\n            $programfile \\\n            $no_color_args \\\n            $help_args\n        ;;\n\n        (format)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $include_exclude_args \\\n            '(--check)--check[checks that formatting code produces no changes]' \\\n            '(--show-backtrace)--show-backtrace[show backtrace on a bug (used only for debugging)]'\n        ;;\n\n        (hierarchy)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $format_args \\\n            $prelude_args \\\n            $stdin_filename_args \\\n            '(-e)-e[filter types by NAME regex]:'\n        ;;\n\n        (implementations)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $format_args \\\n            $prelude_args \\\n            $cursor_args \\\n            $stdin_filename_args\n        ;;\n\n        (unreachable)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $include_exclude_args \\\n            '(-f --format)'{-f,--format}'[output format: text (default), json, csv, codecov]:' \\\n            $prelude_args \\\n            '(--check)--check[exits with error if there is any unreachable code]' \\\n            '(--tallies)--tallies[print reachable methods and their call counts as well]' \\\n            $stdin_filename_args\n        ;;\n\n        (macro_code_coverage)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $include_exclude_args \\\n            '(-f --format)'{-f,--format}'[output format: codecov (default)]:' \\\n            $prelude_args \\\n            $stdin_filename_args\n        ;;\n\n        (types)\n          _arguments \\\n            $programfile \\\n            $help_args \\\n            $no_color_args \\\n            $exec_args \\\n            $format_args \\\n            $prelude_args \\\n            $stdin_filename_args\n        ;;\n      esac\n    ;;\n  esac\n}\n\nlocal curcontext=$curcontext ret=1\ndeclare -A opt_args\n_arguments -C \\\n  $help_args \\\n  $version_args \\\n  '1:sub-command: _alternative \"subcommands:sub command:_crystal_commands\" \"files:file:_files -g \\*.cr\\(-.\\)\"' \\\n  '*::arg:->cmd' && ret=0\ncase $state in\n  (cmd)\n    if (( $CURRENT == 1 )); then\n    else\n      curcontext=\"${curcontext%:*:*}:crystal-$words[1]:\"\n      if ! _call_function ret _crystal-$words[1] ; then\n          _default && ret=0\n      fi\n      return ret\n    fi\n    ;;\nesac\n}\n_crystal\n"
  },
  {
    "path": "etc/gdb/crystal_formatters.py",
    "content": "import gdb\n\nclass CrystalStringPrinter:\n    def __init__(self, val):\n        self.val = val\n\n    def to_string(self):\n        bytesize = self.val['bytesize']\n        buf = gdb.selected_inferior().read_memory(self.val['c'].address, bytesize)\n        return buf.tobytes().decode('utf-8', errors='backslashreplace')\n\n    def display_hint(self):\n        return 'string'\n\nclass CrystalArrayPrinter:\n    def __init__(self, val):\n        self.val = val\n\n    def to_string(self):\n        type = self.val.type\n        if type.code == gdb.TYPE_CODE_PTR:\n            type = type.target()\n        return str(type)\n\n    def children(self):\n        for i in range(int(self.val['size'])):\n            yield str(i), (self.val['buffer'] + i).dereference()\n\n    def display_hint(self):\n        return 'array'\n\nclass CrystalReferenceSubPrinter:\n    def __init__(self, name, cls):\n        self.name = name\n        self.enabled = True\n        self.cls = cls\n\n    def recognize(self, val):\n        type = val.type\n        if type.code == gdb.TYPE_CODE_PTR:\n            type = type.target()\n        typename = type.name\n        if typename is not None:\n            if typename == self.name:\n                return self.cls(val)\n            if typename.startswith(self.name) and typename[len(self.name)] == '(':\n                return self.cls(val)\n\nclass CrystalPrettyPrinter(gdb.printing.PrettyPrinter):\n    def __init__(self):\n        super(CrystalPrettyPrinter, self).__init__(\"CrystalStdlib\", [])\n        self.subprinters.append(CrystalReferenceSubPrinter(\"String\", CrystalStringPrinter))\n        self.subprinters.append(CrystalReferenceSubPrinter(\"Array\", CrystalArrayPrinter))\n\n    def __call__(self, val):\n        for subprinter in self.subprinters:\n            if subprinter.enabled:\n                instance = subprinter.recognize(val)\n                if instance is not None:\n                    return instance\n\ngdb.printing.register_pretty_printer(\n    gdb.current_objfile(),\n    CrystalPrettyPrinter(),\n    replace=True)\n"
  },
  {
    "path": "etc/lldb/crystal_formatters.py",
    "content": "import lldb\n\nclass CrystalArraySyntheticProvider:\n    def __init__(self, valobj, internal_dict):\n        self.valobj = valobj\n        self.buffer = None\n        self.size = 0\n\n    def update(self):\n        if self.valobj.type.is_pointer:\n            self.valobj = self.valobj.Dereference()\n        self.size = int(self.valobj.child[0].value)\n        self.type = self.valobj.type\n        self.buffer = self.valobj.child[3]\n\n    def num_children(self):\n        size = 0 if self.size is None else self.size\n        return size\n\n    def get_child_index(self, name):\n        try:\n            return int(name.lstrip('[').rstrip(']'))\n        except:\n            return -1\n\n    def get_child_at_index(self,index):\n        if index >= self.size:\n            return None\n        try:\n            elementType = self.buffer.type.GetPointeeType()\n            offset = elementType.size * index\n            return self.buffer.CreateChildAtOffset('[' + str(index) + ']', offset, elementType)\n        except Exception as e:\n            print('Got exception %s' % (str(e)))\n            return None\n\ndef findType(name, module):\n    cachedTypes = module.GetTypes()\n    for idx in range(cachedTypes.GetSize()):\n        type = cachedTypes.GetTypeAtIndex(idx)\n        if type.name == name:\n            return type\n    return None\n\n\ndef CrystalString_SummaryProvider(value, dict):\n    error = lldb.SBError()\n    if value.TypeIsPointerType():\n        value = value.Dereference()\n    process = value.GetTarget().GetProcess()\n    byteSize = int(value.child[0].value)\n    len = int(value.child[1].value)\n    len = byteSize or len\n    strAddr = value.child[2].load_addr\n    val = process.ReadCStringFromMemory(strAddr, len + 1, error)\n    return '\"%s\"' % val\n\n\ndef __lldb_init_module(debugger, dict):\n    debugger.HandleCommand(r'type synthetic add -l crystal_formatters.CrystalArraySyntheticProvider -x \"^Array\\(.+\\)(\\s*\\**)?\" -w Crystal')\n    debugger.HandleCommand(r'type summary add -F crystal_formatters.CrystalString_SummaryProvider -x \"^(String|\\(String \\| Nil\\))(\\s*\\**)?$\" -w Crystal')\n    debugger.HandleCommand(r'type category enable Crystal')\n"
  },
  {
    "path": "etc/msvc/crystal.natvis",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<AutoVisualizer xmlns=\"http://schemas.microsoft.com/vstudio/debugger/natvis/2010\">\r\n    <Type Name=\"String\">\r\n        <DisplayString>{&amp;c,[bytesize]s8}</DisplayString>\r\n        <StringView>&amp;c,[bytesize]s8</StringView>\r\n    </Type>\r\n\r\n    <Type Name=\"Array(*)\">\r\n        <DisplayString>{{ size={size} }}</DisplayString>\r\n        <Expand>\r\n            <Item Name=\"[root_buffer]\">buffer - offset_to_buffer</Item>\r\n            <Item Name=\"[remaining_capacity]\">capacity - offset_to_buffer</Item>\r\n            <ArrayItems>\r\n                <Size>size</Size>\r\n                <ValuePointer>buffer</ValuePointer>\r\n            </ArrayItems>\r\n        </Expand>\r\n    </Type>\r\n\r\n    <Type Name=\"Hash(*,*)\">\r\n        <DisplayString>{{ size={size} }}</DisplayString>\r\n        <Expand>\r\n            <CustomListItems MaxItemsPerView=\"5000\">\r\n                <Variable Name=\"i\" InitialValue=\"first\"/>\r\n                <Loop>\r\n                    <Break Condition=\"i &gt;= size + deleted_count\"/>\r\n                    <If Condition=\"entries[i].hash != 0\">\r\n                        <Item>entries[i]</Item>\r\n                    </If>\r\n                    <Exec>i += 1</Exec>\r\n                </Loop>\r\n            </CustomListItems>\r\n        </Expand>\r\n    </Type>\r\n</AutoVisualizer>\r\n"
  },
  {
    "path": "etc/win-ci/build-ffi.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/crystal-lang/libffi.git -Ref v$Version\r\n\r\nRun-InDirectory $BuildTree {\r\n    $args = \"-DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_SHARED_LIBS=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --config Release\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build libffi\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\Release\\libffi.lib libs\\ffi-dynamic.lib\r\n    mv -Force $BuildTree\\Release\\libffi.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\Release\\libffi.lib libs\\ffi.lib\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-gc.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [Parameter(Mandatory)] [string] $AtomicOpsVersion,\r\n    [ValidateSet(\"Release\", \"Debug\")] [string] $Config = \"Release\",\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/ivmai/bdwgc.git -Ref v$Version\r\nSetup-Git -Path $BuildTree\\libatomic_ops -Url https://github.com/ivmai/libatomic_ops.git -Ref v$AtomicOpsVersion\r\n\r\nRun-InDirectory $BuildTree {\r\n    $args = \"-Dbuild_cord=OFF -Denable_large_config=ON -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_SHARED_LIBS=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded $args\"\r\n    }\r\n    if ($Config -eq \"Debug\") {\r\n        $args = \"-DCMAKE_SHARED_LINKER_FLAGS=/PDBALTPATH:gc.pdb -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded -DCMAKE_POLICY_DEFAULT_CMP0141=NEW $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --config $Config\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build libgc\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\$Config\\gc.lib libs\\gc-dynamic.lib\r\n    mv -Force $BuildTree\\$Config\\gc.dll dlls\\\r\n    if ($Config -eq \"Debug\") {\r\n        mv -Force $BuildTree\\$Config\\gc.pdb dlls\\\r\n    }\r\n} else {\r\n    mv -Force $BuildTree\\$Config\\gc.lib libs\\\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-iconv.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nInvoke-WebRequest \"https://ftp.gnu.org/pub/gnu/libiconv/libiconv-${Version}.tar.gz\" -OutFile libiconv.tar.gz\r\ntar -xzf libiconv.tar.gz\r\nmv libiconv-* $BuildTree\r\nrm libiconv.tar.gz\r\n\r\nRun-InDirectory $BuildTree {\r\n    $env:CHERE_INVOKING = 1\r\n    [System.IO.File]::WriteAllText(\"src\\Makefile.in\", [System.IO.File]::ReadAllText(\"src\\Makefile.in\").Replace(\"chmod 777 .\", \"true\"))\r\n\r\n    & 'C:\\cygwin64\\bin\\bash.exe' --login \"$PSScriptRoot\\cygwin-build-iconv.sh\" \"$(if ($Dynamic) { 1 })\"\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build libiconv\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\iconv\\lib\\iconv.dll.lib libs\\iconv-dynamic.lib\r\n    mv -Force $BuildTree\\iconv\\bin\\iconv-2.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\iconv\\lib\\iconv.lib libs\\\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-llvm.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [Parameter(Mandatory)] [string[]] $TargetsToBuild,\r\n    [switch] $Dynamic\r\n)\r\n\r\nif (-not $Dynamic) {\r\n    Write-Host \"Error: Building LLVM as a static library is not supported yet\" -ForegroundColor Red\r\n    Exit 1\r\n}\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/llvm/llvm-project.git -Ref llvmorg-$Version\r\n\r\nRun-InDirectory $BuildTree\\build {\r\n    $args = \"-Thost=x64 -DLLVM_TARGETS_TO_BUILD=$($TargetsToBuild -join ';') -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_ENABLE_ZSTD=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL $args\"\r\n    } else {\r\n        $args = \"-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DLLVM_BUILD_LLVM_C_DYLIB=OFF $args\"\r\n    }\r\n    & $cmake ..\\llvm $args.split(' ')\r\n    & $cmake --build . --config Release --target llvm-config --target LLVM-C\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build LLVM\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\build\\Release\\lib\\LLVM-C.lib libs\\llvm-dynamic.lib\r\n    mv -Force $BuildTree\\build\\Release\\bin\\LLVM-C.dll dlls\\\r\n} else {\r\n    # TODO (probably never)\r\n}\r\n\r\nAdd-Content libs\\llvm_VERSION $(& \"$BuildTree\\build\\Release\\bin\\llvm-config.exe\" --version)\r\nAdd-Content libs\\llvm_VERSION $(& \"$BuildTree\\build\\Release\\bin\\llvm-config.exe\" --targets-built)\r\nAdd-Content libs\\llvm_VERSION $(& \"$BuildTree\\build\\Release\\bin\\llvm-config.exe\" --system-libs)\r\n"
  },
  {
    "path": "etc/win-ci/build-mpir.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/BrianGladman/mpir.git -Ref dc82b0475dea84d5338356e49176c40be03a5bdf # master@{2023-02-10}\r\n\r\nRun-InDirectory $BuildTree {\r\n    $vsVersion = \"vs$((& \"${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\vswhere.exe\" -property displayName) -replace '.*\\b\\d\\d(\\d\\d)\\b.*', '$1')\"\r\n\r\n    echo '<Project>\r\n        <PropertyGroup>\r\n            <ForceImportAfterCppTargets>$(MsbuildThisFileDirectory)\\Override.props</ForceImportAfterCppTargets>\r\n        </PropertyGroup>\r\n    </Project>' > 'msvc\\Directory.Build.props'\r\n\r\n    echo '<Project>\r\n        <ItemDefinitionGroup>\r\n            <ClCompile>\r\n                <DebugInformationFormat>None</DebugInformationFormat>\r\n                <WholeProgramOptimization>false</WholeProgramOptimization>\r\n            </ClCompile>\r\n            <Link>\r\n                <GenerateDebugInformation>false</GenerateDebugInformation>\r\n            </Link>\r\n        </ItemDefinitionGroup>\r\n    </Project>' > 'msvc\\Override.props'\r\n\r\n    if ($Dynamic) {\r\n        MSBuild.exe /p:Platform=x64 /p:Configuration=Release \"msvc\\$vsVersion\\dll_mpir_gc\\dll_mpir_gc.vcxproj\"\r\n    } else {\r\n        MSBuild.exe /p:Platform=x64 /p:Configuration=Release \"msvc\\$vsVersion\\lib_mpir_gc\\lib_mpir_gc.vcxproj\"\r\n    }\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build MPIR\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\dll\\x64\\Release\\mpir.lib libs\\mpir-dynamic.lib\r\n    mv -Force $BuildTree\\dll\\x64\\Release\\mpir.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\lib\\x64\\Release\\mpir.lib libs\\\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-openssl.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/openssl/openssl -Ref openssl-$Version\r\n\r\nRun-InDirectory $BuildTree {\r\n    Replace-Text Configurations\\10-main.conf '/Zi /Fdossl_static.pdb' ''\r\n    Replace-Text Configurations\\10-main.conf '\"/nologo /debug\"' '\"/nologo /debug:none\"'\r\n\r\n    if ($Dynamic) {\r\n        perl Configure VC-WIN64A no-tests\r\n    } else {\r\n        perl Configure VC-WIN64A /MT -static no-tests\r\n    }\r\n    nmake\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build OpenSSL\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    $major = $Version -replace '\\..*', ''\r\n    mv -Force $BuildTree\\libcrypto.lib libs\\crypto-dynamic.lib\r\n    mv -Force $BuildTree\\libssl.lib libs\\ssl-dynamic.lib\r\n    mv -Force $BuildTree\\libcrypto-$major-x64.dll dlls\\\r\n    mv -Force $BuildTree\\libssl-$major-x64.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\libcrypto.lib libs\\crypto.lib\r\n    mv -Force $BuildTree\\libssl.lib libs\\ssl.lib\r\n}\r\n[IO.File]::WriteAllLines(\"libs\\openssl_VERSION\", $Version)\r\n"
  },
  {
    "path": "etc/win-ci/build-pcre.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\nfunction Find-7Zip {\r\n    $Path = Get-Command \"7z\" -CommandType Application -TotalCount 1 -ErrorAction SilentlyContinue\r\n    if ($Path) { return $Path.Path }\r\n\r\n    $Path = \"$env:ProgramFiles\\7-Zip\\7z.exe\"\r\n    if (Test-Path -Path $Path -PathType Leaf) { return $Path }\r\n\r\n    $Path = \"${env:ProgramFiles(x86)}\\7-Zip\\7z.exe\"\r\n    if (Test-Path -Path $Path -PathType Leaf) { return $Path }\r\n\r\n    Write-Host \"Error: Cannot locate 7-Zip executable\" -ForegroundColor Red\r\n    Exit 1\r\n}\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nInvoke-WebRequest https://cs.stanford.edu/pub/exim/pcre/pcre-$Version.zip -OutFile pcre.zip\r\n& (Find-7Zip) x pcre.zip\r\nmv pcre-* $BuildTree\r\nrm pcre.zip\r\n\r\nRun-InDirectory $BuildTree {\r\n    $args = \"-DPCRE_BUILD_PCREGREP=OFF -DPCRE_BUILD_TESTS=OFF -DPCRE_BUILD_PCRECPP=OFF -DPCRE_SUPPORT_JIT=ON -DPCRE_SUPPORT_UNICODE_PROPERTIES=ON -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_SHARED_LIBS=OFF -DPCRE_STATIC_RUNTIME=ON $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --config Release\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build PCRE\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\Release\\pcre.lib libs\\pcre-dynamic.lib\r\n    mv -Force $BuildTree\\Release\\pcre.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\Release\\pcre.lib libs\\\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-pcre2.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/PCRE2Project/pcre2.git -Ref pcre2-$Version\r\n\r\nRun-InDirectory $BuildTree {\r\n    & $git submodule update --init\r\n\r\n    $args = \"-DPCRE2_BUILD_PCRE2GREP=OFF -DPCRE2_BUILD_TESTS=OFF -DPCRE2_SUPPORT_UNICODE=ON -DPCRE2_SUPPORT_JIT=ON -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_STATIC_LIBS=OFF -DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --config Release\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build PCRE2\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\Release\\pcre2-8.lib libs\\pcre2-8-dynamic.lib\r\n    mv -Force $BuildTree\\Release\\pcre2-8.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\Release\\pcre2-8-static.lib libs\\pcre2-8.lib\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-xml2.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://gitlab.gnome.org/GNOME/libxml2.git -Ref v$Version\r\n\r\nRun-InDirectory $BuildTree {\r\n    $args = \"-DLIBXML2_WITH_TESTS=OFF -DLIBXML2_WITH_PROGRAMS=OFF -DLIBXML2_WITH_HTTP=OFF -DLIBXML2_WITH_FTP=OFF -DLIBXML2_WITH_ICONV=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_ZLIB=OFF -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_SHARED_LIBS=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --config Release\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build libxml2\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\Release\\libxml2.lib libs\\xml2-dynamic.lib\r\n    mv -Force $BuildTree\\Release\\libxml2.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\Release\\libxml2s.lib libs\\xml2.lib\r\n}\r\n[IO.File]::WriteAllLines(\"libs\\libxml_VERSION\", $Version)\r\n"
  },
  {
    "path": "etc/win-ci/build-yaml.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/yaml/libyaml.git -Ref $Version\r\n\r\nRun-InDirectory $BuildTree {\r\n    $args = \"-DBUILD_TESTING=OFF -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_SHARED_LIBS=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --config Release\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build libyaml\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\Release\\yaml.lib libs\\yaml-dynamic.lib\r\n    mv -Force $BuildTree\\Release\\yaml.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\Release\\yaml.lib libs\\\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/build-z.ps1",
    "content": "param(\r\n    [Parameter(Mandatory)] [string] $BuildTree,\r\n    [Parameter(Mandatory)] [string] $Version,\r\n    [switch] $Dynamic\r\n)\r\n\r\n. \"$(Split-Path -Parent $MyInvocation.MyCommand.Path)\\setup.ps1\"\r\n\r\n[void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force)\r\nSetup-Git -Path $BuildTree -Url https://github.com/madler/zlib.git -Ref v$Version\r\n\r\nRun-InDirectory $BuildTree {\r\n    $args = \"-DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF\"\r\n    if ($Dynamic) {\r\n        $args = \"-DBUILD_SHARED_LIBS=ON $args\"\r\n    } else {\r\n        $args = \"-DBUILD_SHARED_LIBS=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded $args\"\r\n    }\r\n    & $cmake . $args.split(' ')\r\n    & $cmake --build . --target $(if ($Dynamic) { 'zlib' } else { 'zlibstatic' }) --config Release\r\n    if (-not $?) {\r\n        Write-Host \"Error: Failed to build zlib\" -ForegroundColor Red\r\n        Exit 1\r\n    }\r\n}\r\n\r\nif ($Dynamic) {\r\n    mv -Force $BuildTree\\Release\\zlib.lib libs\\z-dynamic.lib\r\n    mv -Force $BuildTree\\Release\\zlib1.dll dlls\\\r\n} else {\r\n    mv -Force $BuildTree\\Release\\zlibstatic.lib libs\\z.lib\r\n}\r\n"
  },
  {
    "path": "etc/win-ci/crystal.iss",
    "content": "#define MyAppName \"Crystal\"\r\n#define VersionFile FileOpen(\"portable\\src\\VERSION\")\r\n#define MyAppVersion FileRead(VersionFile)\r\n#define MyAppVersionNum StringChange(MyAppVersion, \"-dev\", \"\")\r\n#expr FileClose(VersionFile)\r\n#define MyAppPublisher \"Manas Technology Solutions\"\r\n#define MyAppURL \"https://crystal-lang.org/\"\r\n#define MyAppExeName \"crystal.exe\"\r\n#define MyAppCopyright GetFileCopyright(\"portable\\\" + MyAppExeName)\r\n#define MyAppAssocName MyAppName + \" Source File\"\r\n#define MyAppAssocExt \".cr\"\r\n#define MyAppAssocKey StringChange(MyAppAssocName, \" \", \"\") + MyAppAssocExt\r\n\r\n#define MajorNum 0\r\n#define MinorNum 0\r\n#expr UnpackVersionComponents(StrToVersion(MyAppVersionNum), MajorNum, MinorNum, null, null)\r\n\r\n[Setup]\r\nAppId={{7C307DDF-447E-46C5-BB3B-47A6F652D7C8}\r\nAppName={#MyAppName} x86_64-windows-msvc\r\nAppVersion={#MyAppVersion}\r\nAppCopyright={#MyAppCopyright}\r\nVersionInfoVersion={#MyAppVersionNum}\r\nAppPublisher={#MyAppPublisher}\r\nAppPublisherURL={#MyAppURL}\r\nAppSupportURL={#MyAppURL}\r\nAppUpdatesURL={#MyAppURL}\r\nArchitecturesAllowed=x64\r\nArchitecturesInstallIn64BitMode=x64\r\nDefaultDirName={autopf}\\{#MyAppName}\r\nOutputBaseFilename=crystal-setup\r\nLicenseFile=portable\\LICENSE.txt\r\n\r\nChangesEnvironment=yes\r\nChangesAssociations=yes\r\n\r\nDefaultGroupName={#MyAppName} {#MyAppVersion}\r\nAllowNoIcons=yes\r\n\r\nPrivilegesRequired=lowest\r\nPrivilegesRequiredOverridesAllowed=dialog\r\n\r\nCompression=lzma\r\nSolidCompression=yes\r\n\r\nWizardStyle=modern\r\nWizardImageFile=crystal.bmp\r\nWizardSmallImageFile=crystal_small.bmp\r\nDisableWelcomePage=no\r\n\r\nSetupMutex={#MyAppName}SetupMutex\r\n\r\n[Languages]\r\nName: \"english\"; MessagesFile: \"compiler:Default.isl\"\r\n\r\n[Types]\r\nName: \"full\"; Description: \"Full installation\"\r\nName: \"minimal\"; Description: \"Minimal installation\"\r\nName: \"custom\"; Description: \"Custom installation\"; Flags: iscustom\r\n\r\n[Components]\r\nName: \"main\"; Description: \"Crystal compiler\"; Types: full minimal custom; Flags: fixed\r\nName: \"shards\"; Description: \"Shards dependency manager\"; Types: full\r\nName: \"pdb\"; Description: \"Debug symbols\"; Types: full\r\nName: \"samples\"; Description: \"Sample programs\"; Types: full\r\nName: \"docs\"; Description: \"Offline standard library documentation\"; Types: full\r\n\r\n[Tasks]\r\nName: addtopath; Description: \"Add Crystal's directory to the &PATH environment variable\"; Flags: checkedonce\r\nName: association; Description: \"{cm:AssocFileExtension,Crystal,.cr}\"; Flags: unchecked\r\n\r\n[Files]\r\nSource: \"portable\\{#MyAppExeName}\"; DestDir: \"{app}\"; Flags: ignoreversion; Components: main\r\nSource: \"portable\\lib\\*\"; DestDir: \"{app}\\lib\"; Flags: ignoreversion; Components: main\r\nSource: \"portable\\*.dll\"; DestDir: \"{app}\"; Flags: ignoreversion; Components: main\r\nSource: \"portable\\src\\*\"; DestDir: \"{app}\\src\"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: main\r\nSource: \"portable\\README.md\"; DestDir: \"{app}\"; Flags: ignoreversion isreadme; Components: main\r\nSource: \"portable\\LICENSE.txt\"; DestDir: \"{app}\"; Flags: ignoreversion; Components: main\r\n\r\nSource: \"portable\\shards.exe\"; DestDir: \"{app}\"; Flags: ignoreversion; Components: shards\r\n\r\nSource: \"portable\\crystal.pdb\"; DestDir: \"{app}\"; Flags: ignoreversion; Components: pdb\r\nSource: \"portable\\shards.pdb\"; DestDir: \"{app}\"; Flags: ignoreversion; Components: pdb and shards\r\n\r\nSource: \"portable\\examples\\*\"; DestDir: \"{app}\\examples\"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: samples\r\n\r\nSource: \"portable\\docs\\*\"; DestDir: \"{app}\\docs\"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: docs\r\n\r\n[UninstallDelete]\r\nType: filesandordirs; Name: \"{localappdata}\\crystal\\cache\"\r\n\r\n[Registry]\r\nRoot: HKA; Subkey: \"Software\\Classes\\{#MyAppAssocExt}\"; ValueType: string; ValueName: \"PerceivedType\"; ValueData: \"text\"; Flags: uninsdeletevalue\r\nRoot: HKA; Subkey: \"Software\\Classes\\{#MyAppAssocExt}\\OpenWithProgids\"; ValueType: string; ValueName: \"{#MyAppAssocKey}\"; ValueData: \"\"; Flags: uninsdeletevalue\r\n\r\nRoot: HKA; Subkey: \"Software\\Classes\\{#MyAppAssocKey}\"; ValueType: string; ValueName: \"\"; ValueData: \"{#MyAppAssocName}\"; Flags: uninsdeletekey\r\nRoot: HKA; Subkey: \"Software\\Classes\\{#MyAppAssocKey}\\DefaultIcon\"; ValueType: string; ValueName: \"\"; ValueData: \"{app}\\{#MyAppExeName},0\"; Flags: uninsdeletekey\r\nRoot: HKA; Subkey: \"Software\\Classes\\{#MyAppAssocKey}\\shell\\open\\command\"; ValueType: string; ValueName: \"\"; ValueData: \"\"\"{app}\\{#MyAppExeName}\"\" \"\"%1\"\"\"; Flags: uninsdeletekey\r\n\r\nRoot: HKA; Subkey: \"Software\\Classes\\Applications\\{#MyAppExeName}\\DefaultIcon\"; ValueType: string; ValueName: \"\"; ValueData: \"{app}\\{#MyAppExeName},0\"; Flags: uninsdeletekey\r\nRoot: HKA; Subkey: \"Software\\Classes\\Applications\\{#MyAppExeName}\\SupportedTypes\"; ValueType: string; ValueName: \".cr\"; ValueData: \"\"; Flags: uninsdeletekey\r\n\r\n[Icons]\r\nName: \"{group}\\Crystal Book\"; Filename: \"https://crystal-lang.org/reference/{#MajorNum}.{#MinorNum}/index.html\"\r\nName: \"{group}\\Crystal Standard Library API\"; Filename: \"{app}\\docs\\index.html\"; Components: docs\r\nName: \"{group}\\Crystal Standard Library API\"; Filename: \"https://crystal-lang.org/api/{#MyAppVersion}/index.html\"; Components: not docs\r\nName: \"{group}\\Official Website\"; Filename: \"https://crystal-lang.org/\"\r\nName: \"{group}\\GitHub Repository\"; Filename: \"https://github.com/crystal-lang/crystal\"\r\n\r\n[Code]\r\nconst CLSID_SetupConfiguration = '{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}';\r\nconst Win10SDK64 = 'SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0';\r\nconst Win10SDK32 = 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0';\r\n\r\ntype\r\n  ISetupInstance = interface(IUnknown) '{B41463C3-8866-43B5-BC33-2B0676F7F42E}'\r\n    procedure GetInstanceId;\r\n    procedure GetInstallDate;\r\n    procedure GetInstallationName;\r\n    function GetInstallationPath(out installationPath: WideString): HResult;\r\n    procedure GetInstallationVersion;\r\n    procedure GetDisplayName;\r\n    procedure GetDescription;\r\n    procedure ResolvePath;\r\n  end;\r\n\r\n  IEnumSetupInstances = interface(IUnknown) '{6380BCFF-41D3-4B2E-8B2E-BF8A6810C848}'\r\n    function Next(celt: DWord; out rgelt: ISetupInstance; out pceltFetched: DWord): HResult;\r\n    procedure Skip;\r\n    procedure Reset;\r\n    procedure Clone;\r\n  end;\r\n\r\n  ISetupConfiguration = interface(IUnknown) '{42843719-DB4C-46C2-8E7C-64F1816EFD5B}'\r\n    function EnumInstances(out enumInstances: IEnumSetupInstances): HResult;\r\n  end;\r\n\r\n  IClassFactory = interface(IUnknown) '{00000001-0000-0000-C000-000000000046}'\r\n    function CreateInstance(unkOuter: IUnknown; riid: TGUID; out object: IUnknown): HResult;\r\n    procedure LockServer;\r\n  end;\r\n\r\nfunction HasMSVC: Boolean;\r\nvar\r\n  config: ISetupConfiguration;\r\n  enumSetup: IEnumSetupInstances;\r\n  count: DWord;\r\n  setup: ISetupInstance;\r\n  setupPath: WideString;\r\n  msvcVersion: AnsiString;\r\n  hresult: HResult;\r\nbegin\r\n  result := False;\r\n\r\n  try\r\n    config := ISetupConfiguration(CreateComObject(StringToGuid(CLSID_SetupConfiguration)));\r\n  except\r\n    exit;\r\n  end;\r\n\r\n  OleCheck(config.EnumInstances(enumSetup));\r\n  While True do\r\n  begin\r\n    hresult := enumSetup.Next(1, setup, count);\r\n    OleCheck(hresult);\r\n    if hresult = 1 then\r\n      Break;\r\n    hresult := setup.GetInstallationPath(setupPath);\r\n    if hresult <> 0 then\r\n      Continue;\r\n    if not LoadStringFromFile(setupPath + '\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt', msvcVersion) then\r\n      Continue;\r\n    if not FileExists(setupPath + '\\VC\\Tools\\MSVC\\' + TrimRight(msvcVersion) + '\\bin\\Hostx64\\x64\\cl.exe') then\r\n      Continue;\r\n    Log('MSVC location: ' + setupPath + '\\VC\\Tools\\MSVC\\' + TrimRight(msvcVersion));\r\n    result := True;\r\n    exit;\r\n  end;\r\nend;\r\n\r\nfunction HasWinSDKAt(const rootKey: Integer; const subKey: String): Boolean;\r\nvar\r\n  installationFolder: String;\r\n  productVersion: String;\r\nbegin\r\n  result := False;\r\n  if RegQueryStringValue(rootKey, subKey, 'InstallationFolder', installationFolder) then\r\n    if RegQueryStringValue(rootKey, subKey, 'ProductVersion', productVersion) then\r\n      if FileExists(installationFolder + '\\Include\\' + productVersion + '.0\\um\\winsdkver.h') then\r\n      begin\r\n        Log('Windows SDK location: ' + installationFolder + '\\Lib\\' + productVersion);\r\n        result := True;\r\n      end;\r\nend;\r\n\r\nfunction HasWinSDK: Boolean;\r\nbegin\r\n  result := HasWinSDKAt(HKEY_LOCAL_MACHINE, Win10SDK64) or\r\n    HasWinSDKAt(HKEY_LOCAL_MACHINE, Win10SDK32) or\r\n    HasWinSDKAt(HKEY_CURRENT_USER, Win10SDK64) or\r\n    HasWinSDKAt(HKEY_CURRENT_USER, Win10SDK32);\r\nend;\r\n\r\nfunction HasVCRedist: Boolean;\r\nvar\r\n  regValue: Cardinal;\r\nbegin\r\n  result := False;\r\n  if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64', 'Installed', regValue) then\r\n    if regValue <> 0 then\r\n      result := True;\r\nend;\r\n\r\n{ Adopted from https://stackoverflow.com/a/46609047 }\r\nprocedure EnvAddPath(Path: string; IsSystem: Boolean);\r\nvar\r\n  Paths: string;\r\n  Status: Boolean;\r\nbegin\r\n  if IsSystem then\r\n    Status := RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'Path', Paths)\r\n  else\r\n    Status := RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Paths);\r\n\r\n  if not Status then\r\n    Paths := '';\r\n\r\n  if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then\r\n    exit;\r\n\r\n  Paths := Paths + ';' + Path + ';';\r\n\r\n  if IsSystem then\r\n    RegWriteStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'Path', Paths)\r\n  else\r\n    RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Paths);\r\nend;\r\n\r\nprocedure EnvRemovePath(Path: string; IsSystem: Boolean);\r\nvar\r\n  Paths: string;\r\n  Status: Boolean;\r\n  P: Integer;\r\nbegin\r\n  if IsSystem then\r\n    Status := RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'Path', Paths)\r\n  else\r\n    Status := RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Paths);\r\n\r\n  if not Status then\r\n    exit;\r\n\r\n  P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');\r\n  if P = 0 then\r\n    exit;\r\n\r\n  Delete(Paths, P - 1, Length(Path) + 1);\r\n\r\n  if IsSystem then\r\n    RegWriteStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'Path', Paths)\r\n  else\r\n    RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Paths);\r\nend;\r\n\r\nfunction GetUninstallString(): String;\r\nvar\r\n  sUnInstPath: String;\r\n  sUnInstallString: String;\r\nbegin\r\n  sUnInstPath := ExpandConstant('Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{#emit SetupSetting(\"AppId\")}_is1');\r\n  sUnInstallString := '';\r\n  if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then\r\n    RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);\r\n  result := sUnInstallString;\r\nend;\r\n\r\nprocedure InitializeWizard;\r\nvar\r\n  updatingPage: TOutputMsgWizardPage;\r\nbegin\r\n  if GetUninstallString() <> '' then\r\n    updatingPage := CreateOutputMsgPage(\r\n      wpSelectTasks,\r\n      'Pre-Install Checks',\r\n      'A previous Crystal installation already exists.',\r\n      'Setup has detected a previous installation of Crystal; it will be uninstalled before the new version is installed. ' +\r\n      'This ensures that requiring Crystal files will not pick up any leftover files from the previous version.'#13#13 +\r\n      'To use multiple Crystal installations side-by-side, the portable packages for the extra versions must be downloaded manually.');\r\nend;\r\n\r\nprocedure CurStepChanged(CurStep: TSetupStep);\r\nvar\r\n  uninstallString: String;\r\n  exitCode: Integer;\r\nbegin\r\n  case CurStep of\r\n  ssInstall:\r\n    begin\r\n      uninstallString := GetUninstallString();\r\n      if uninstallString <> '' then\r\n        if not Exec(RemoveQuotes(uninstallString), '/VERYSILENT /NORESTART /SUPPRESSMSGBOXES', '', SW_HIDE, ewWaitUntilTerminated, exitCode) then\r\n        begin\r\n          SuppressibleMsgBox('Failed to remove the previous Crystal installation. Setup will now exit.', mbCriticalError, MB_OK, IDOK);\r\n          Abort;\r\n        end;\r\n    end;\r\n  ssPostInstall:\r\n    begin\r\n      if (not HasVCRedist) and (IDYES = SuppressibleTaskDialogMsgBox(\r\n        'Install Visual C++ Redistributable',\r\n        'Setup is unable to detect a copy of the Visual C++ 2015 Redistributable (x64) or newer on this machine. ' +\r\n          'The runtime libraries are needed by dynamically linked executables, including the compiler itself. ' +\r\n          'If you select \"Agree\", the installer will proceed to download: '#13#10#13#10 +\r\n          'https://aka.ms/vs/17/release/vc_redist.x64.exe'#13#10#13#10 +\r\n          'and then run:'#13#10#13#10 +\r\n          'VC_redist.x64.exe /passive /norestart'#13#10#13#10 +\r\n          'Would you like to install the Visual C++ Redistributable now?',\r\n        mbInformation, MB_YESNO, ['Agree', 'Decline'], IDYES, IDYES)) then\r\n      begin\r\n        DownloadTemporaryFile('https://aka.ms/vs/17/release/vc_redist.x64.exe', 'VC_redist.x64.exe', '', nil);\r\n        if not Exec(ExpandConstant('{tmp}\\VC_redist.x64.exe'), '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, exitCode) then\r\n          SuppressibleMsgBox('Failed to install the Visual C++ Redistributable.', mbError, MB_OK, IDOK);\r\n      end;\r\n\r\n      if (not HasMSVC) and (IDYES = SuppressibleTaskDialogMsgBox(\r\n        'Install MSVC Build Tools',\r\n        'Setup is unable to detect a copy of the Build Tools for Visual Studio 2017 or newer on this machine. ' +\r\n          'The MSVC Build Tools are required to link Crystal programs into Windows executables. ' +\r\n          'If you select \"Agree\", the installer will proceed to download: '#13#10#13#10 +\r\n          'https://aka.ms/vs/17/release/vs_BuildTools.exe'#13#10#13#10 +\r\n          'and then run:'#13#10#13#10 +\r\n          'vs_BuildTools.exe --passive --norestart --wait --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64'#13#10#13#10 +\r\n          'Would you like to install the MSVC Build Tools now? ' +\r\n          'Note that you may modify your installation later using the Visual Studio Installer.',\r\n        mbInformation, MB_YESNO, ['Agree', 'Decline'], IDYES, IDYES)) then\r\n      begin\r\n        DownloadTemporaryFile('https://aka.ms/vs/17/release/vs_BuildTools.exe', 'vs_BuildTools.exe', '', nil);\r\n        if not Exec(\r\n          ExpandConstant('{tmp}\\vs_BuildTools.exe'),\r\n          '--passive --norestart --wait --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64',\r\n          '', SW_SHOW, ewWaitUntilTerminated, exitCode\r\n        ) then\r\n          SuppressibleMsgBox('Failed to install the Build Tools for Visual Studio.', mbError, MB_OK, IDOK);\r\n      end;\r\n\r\n      if (not HasWinSDK) and (IDYES = SuppressibleTaskDialogMsgBox(\r\n        'Install Windows SDK',\r\n        'Setup is unable to detect a copy of the Windows 10 SDK or newer on this machine. ' +\r\n          'The Crystal runtime and standard library rely on the Win32 libraries to interface with the Windows system. ' +\r\n          'If you select \"Agree\", the installer will proceed to download: '#13#10#13#10 +\r\n          'https://aka.ms/vs/17/release/vs_BuildTools.exe'#13#10#13#10 +\r\n          'and then run:'#13#10#13#10 +\r\n          'vs_BuildTools.exe --passive --norestart --wait --add Microsoft.VisualStudio.Component.Windows11SDK.22621'#13#10#13#10 +\r\n          'Would you like to install the Windows SDK now? ' +\r\n          'Note that you may modify your installation later using the Visual Studio Installer.',\r\n        mbInformation, MB_YESNO, ['Agree', 'Decline'], IDYES, IDYES)) then\r\n      begin\r\n        DownloadTemporaryFile('https://aka.ms/vs/17/release/vs_BuildTools.exe', 'vs_BuildTools.exe', '', nil);\r\n        if not Exec(\r\n          ExpandConstant('{tmp}\\vs_BuildTools.exe'),\r\n          '--passive --norestart --wait --add Microsoft.VisualStudio.Component.Windows11SDK.22621',\r\n          '', SW_SHOW, ewWaitUntilTerminated, exitCode\r\n        ) then\r\n          SuppressibleMsgBox('Failed to install the Windows SDK.', mbError, MB_OK, IDOK);\r\n      end;\r\n\r\n      if WizardIsTaskSelected('addtopath') then\r\n        EnvAddPath(ExpandConstant('{app}'), IsAdminInstallMode());\r\n    end;\r\n  end;\r\nend;\r\n\r\nprocedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);\r\nbegin\r\n  if CurUninstallStep = usPostUninstall then\r\n    EnvRemovePath(ExpandConstant('{app}'), IsAdminInstallMode());\r\nend;\r\n"
  },
  {
    "path": "etc/win-ci/cygwin-build-iconv.sh",
    "content": "#!/bin/sh\n# shellcheck disable=SC2155\n\nset -e\n\nDynamic=$1\n\nexport PATH=\"$(pwd)/build-aux:$PATH\"\nexport CC=\"$(pwd)/build-aux/compile cl -nologo\"\nexport CXX=\"$(pwd)/build-aux/compile cl -nologo\"\nexport AR=\"$(pwd)/build-aux/ar-lib lib\"\nexport LD=\"link\"\nexport NM=\"dumpbin -symbols\"\nexport STRIP=\":\"\nexport RANLIB=\":\"\nif [ -n \"$Dynamic\" ]; then\n  export CFLAGS=\"-MD\"\n  export CXXFLAGS=\"-MD\"\n  enable_shared=yes\n  enable_static=no\nelse\n  export CFLAGS=\"-MT\"\n  export CXXFLAGS=\"-MT\"\n  enable_shared=no\n  enable_static=yes\n  # GNU libiconv appears to define `BUILDING_DLL` unconditionally, so the static\n  # library contains `/EXPORT` directives that make any executable also export\n  # the iconv symbols, which we don't want\n  find . '(' -name '*.h' -or -name '*.h.build.in' ')' -print0 | xargs -0 -I{} sed -i 's/__declspec(dllexport)//' '{}'\nfi\nexport CPPFLAGS=\"-O2 -D_WIN32_WINNT=_WIN32_WINNT_WIN7 -I$(pwd)/iconv/include\"\nexport LDFLAGS=\"-L$(pwd)/iconv/lib\"\n\n./configure --host=x86_64-w64-mingw32 --prefix=\"$(pwd)/iconv\" --enable-shared=\"${enable_shared}\" --enable-static=\"${enable_static}\"\nmake\nmake install\n"
  },
  {
    "path": "etc/win-ci/setup.ps1",
    "content": "function Run-InDirectory {\r\n    param(\r\n        [Parameter(Mandatory)] [string] $Path,\r\n        [Parameter(Mandatory)] [scriptblock] $ScriptBlock\r\n    )\r\n\r\n    [void](New-Item -Name $Path -ItemType Directory -Force)\r\n    Push-Location $Path\r\n    [Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath\r\n    try { & $ScriptBlock } finally {\r\n        Pop-Location\r\n        [Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath\r\n    }\r\n}\r\n\r\nfunction Find-Git {\r\n    $Path = Get-Command \"git\" -CommandType Application -TotalCount 1 -ErrorAction SilentlyContinue\r\n    if ($Path) { return $Path.Path }\r\n\r\n    $Path = \"$env:ProgramFiles\\Git\\cmd\\git.exe\"\r\n    if (Test-Path -Path $Path -PathType Leaf) { return $Path }\r\n\r\n    Write-Host \"Error: Cannot locate Git executable\" -ForegroundColor Red\r\n    Exit 1\r\n}\r\n\r\nfunction Find-CMake {\r\n    $Path = Get-Command \"cmake\" -CommandType Application -TotalCount 1 -ErrorAction SilentlyContinue\r\n    if ($Path) { return $Path.Path }\r\n\r\n    $Path = \"$env:ProgramFiles\\CMake\\bin\\cmake.exe\"\r\n    if (Test-Path -Path $Path -PathType Leaf) { return $Path }\r\n\r\n    Write-Host \"Error: Cannot locate CMake executable\" -ForegroundColor Red\r\n    Exit 1\r\n}\r\n\r\nfunction Setup-Git {\r\n    param(\r\n        [Parameter(Mandatory)] [string] $Path,\r\n        [Parameter(Mandatory)] [string] $Url,\r\n        [string] $Ref = $null\r\n    )\r\n\r\n    if (-not (Test-Path $Path)) {\r\n        $args = \"clone\", \"--config\", \"core.autocrlf=false\", $Url, $Path\r\n        Write-Host \"$git $args\" -ForegroundColor Cyan\r\n        & $git $args\r\n        if (-not $?) {\r\n            Write-Host \"Error: Failed to clone Git repository\" -ForegroundColor Red\r\n            Exit 1\r\n        }\r\n    }\r\n\r\n    if ($Ref) {\r\n        Run-InDirectory $Path {\r\n            Write-Host \"$git checkout $Ref\" -ForegroundColor Cyan\r\n            & $git checkout $Ref\r\n        }\r\n    }\r\n}\r\n\r\nfunction Replace-Text {\r\n    param(\r\n        [Parameter(Mandatory)] [string] $Path,\r\n        [Parameter(Mandatory)] [string] $Pattern,\r\n        [Parameter(Mandatory)] [AllowEmptyString()] [string] $Replacement\r\n    )\r\n\r\n    $content = [System.IO.File]::ReadAllText($Path).Replace($Pattern, $Replacement)\r\n    [System.IO.File]::WriteAllText($Path, $content)\r\n}\r\n\r\n$git = Find-Git\r\n$cmake = Find-CMake\r\n\r\n[void](New-Item -Name libs -ItemType Directory -Force)\r\n[void](New-Item -Name dlls -ItemType Directory -Force)\r\n"
  },
  {
    "path": "lib/.shards.info",
    "content": "---\nversion: 1.0\nshards:\n  markd:\n    git: https://github.com/icyleaf/markd.git\n    version: 0.5.0\n  reply:\n    git: https://github.com/i3oris/reply.git\n    version: 0.3.1+git.commit.13f7eba083f138dd063c68b859c8e315f44fb523\n  sanitize:\n    git: https://github.com/straight-shoota/sanitize.git\n    version: 0.1.0+git.commit.75c141b619c77956e88f557149566cd28876398b\n"
  },
  {
    "path": "man/crystal.1",
    "content": "'\\\" t\n.\\\"     Title: crystal\n.\\\"    Author: [see the \"AUTHOR(S)\" section]\n.\\\" Generator: Asciidoctor 2.0.23\n.\\\"      Date: 2025-02-14\n.\\\"    Manual: Crystal Compiler Command Line Reference Guide\n.\\\"    Source: crystal 1.16.0-dev\n.\\\"  Language: English\n.\\\"\n.TH \"CRYSTAL\" \"1\" \"2025-02-14\" \"crystal 1.16.0\\-dev\" \"Crystal Compiler Command Line Reference Guide\"\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.ss \\n[.ss] 0\n.nh\n.ad l\n.de URL\n\\fI\\\\$2\\fP <\\\\$1>\\\\$3\n..\n.als MTO URL\n.if \\n[.g] \\{\\\n.  mso www.tmac\n.  am URL\n.    ad l\n.  .\n.  am MTO\n.    ad l\n.  .\n.  LINKSTYLE blue R < >\n.\\}\n.SH \"NAME\"\ncrystal \\- compiler for the Crystal language\n.SH \"SYNOPSIS\"\n.sp\n\\fBcrystal\\fP command [switches] programfile \\(em [arguments]\n.SH \"DESCRIPTION\"\n.sp\nCrystal is a statically type\\-checked programming language. It was created with the\nbeauty of Ruby and the performance of C in mind.\n.SH \"USAGE\"\n.sp\nYou can compile and run a program by invoking the compiler with a single filename:\n.sp\n.if n .RS 4\n.nf\n.fam C\ncrystal some_program.cr\n.fam\n.fi\n.if n .RE\n.sp\nCrystal files usually end with the .cr extension, though this is not mandatory.\n.sp\nAlternatively you can use the run command:\n.sp\n.if n .RS 4\n.nf\n.fam C\ncrystal run some_program.cr\n.fam\n.fi\n.if n .RE\n.sp\nTo create an executable use the build command:\n.sp\n.if n .RS 4\n.nf\n.fam C\ncrystal build some_program.cr\n.fam\n.fi\n.if n .RE\n.sp\nThis will create an executable named \"some_program\".\n.sp\nNote that by default the generated executables are not fully optimized.  To turn optimizations on, use the \\fB\\-\\-release\\fP  flag:\n.sp\n.if n .RS 4\n.nf\n.fam C\ncrystal build \\-\\-release some_program.cr\n.fam\n.fi\n.if n .RE\n.sp\nMake sure to always use \\fB\\-\\-release\\fP  for production\\-ready executables and when performing benchmarks.\n.sp\nThe optimizations are not turned on by default because the compile times are much\nfaster without them and the performance of the program is still pretty good without\nthem, so it allows to use the \\fBcrystal\\fP command almost to be used as if it was an interpreter.\n.SH \"OPTIONS\"\n.sp\nThe \\fBcrystal\\fP command accepts the following options\n.SS \"init\"\n.sp\n\\fBinit\\fP TYPE [DIR | NAME DIR]\n.sp\nGenerates a new Crystal project.\n.sp\nTYPE is one of:\n.sp\n\\fBlib\\fP\t Creates a library skeleton\n.br\n\\fBapp\\fP\t Creates an application skeleton\n.sp\nThis initializes the lib/app project folder as a git repository, with a license file, a README file, a shard.yml for use with shards (the Crystal dependency manager), a .gitignore file, and src and spec folders.\n.sp\nDIR  \\- directory where project will be generated\n.sp\nNAME \\- name of project to be generated (default: basename of DIR)\n.sp\nOptions:\n.sp\n\\fB\\-f, \\-\\-force\\fP\n.RS 4\nForce overwrite existing files.\n.RE\n.sp\n\\fB\\-s, \\-\\-skip\\-existing\\fP\n.RS 4\nSkip existing files.\n.RE\n.SS \"build\"\n.sp\n\\fBbuild\\fP [options] [programfile] [\\-\\-] [arguments]\n.sp\nCompile program.\n.sp\nOptions:\n.sp\n\\fB\\-\\-cross\\-compile\\fP\n.RS 4\nGenerate an object file for cross compilation and prints the command to build the executable.\tThe object file should be copied\nto the target system and the printed command should be executed\nthere. This flag mainly exists for porting the compiler to new\nplatforms, where possible run the compiler on the target platform\ndirectly.\n.RE\n.sp\n\\fB\\-d\\fP, \\fB\\-\\-debug\\fP\n.RS 4\nGenerate the output with symbolic debug symbols.  These are read\nwhen debugging the built program with tools like lldb, gdb, valgrind etc. and provide mappings to the original source code for\nthose tools.\n.RE\n.sp\n\\fB\\-\\-no\\-debug\\fP\n.RS 4\nGenerate the output without any symbolic debug symbols.\n.RE\n.sp\n\\fB\\-D\\fP \\fIFLAG\\fP, \\fB\\-\\-define\\fP \\fIFLAG\\fP\n.RS 4\nDefine a compile\\-time flag. This is useful to conditionally define types, methods, or commands based on flags available at compile time. The default flags are from the target triple given\nwith \\fB\\-\\-target\\-triple\\fP  or the hosts default, if none is given.\n.RE\n.sp\n\\fB\\-\\-emit\\fP [asm|llvm\\-bc|llvm\\-ir|obj]\n.RS 4\nComma separated list of types of output for the compiler to emit.\nYou can use this to see the generated LLVM IR, LLVM bitcode, assembly, and object files.\n.RE\n.sp\n\\fB\\-\\-x86\\-asm\\-syntax\\fP [att|intel]\n.RS 4\nSelect the assembly dialect for \\fB\\-\\-emit=asm\\fP. The default is \\f(CRatt\\fP, which stands\nfor the AT&T syntax supported by tools like the GNU Assembler. \\f(CRintel\\fP selects\nthe Intel syntax which is preferred for Windows tools.\n.RE\n.sp\n\\fB\\-\\-frame\\-pointers\\fP [auto|always|non\\-leaf]\n.RS 4\nControl the preservation of frame pointers. The default value,\n\\f(CR\\-\\-frame\\-pointers=auto\\fP, will preserve frame pointers on debug\nbuilds and try to omit them on release builds (certain platforms\nrequire them to stay enabled). \\f(CR\\-\\-frame\\-pointers=always\\fP will always preserve them, and non\\-leaf will only force their preservation on non\\-leaf functions.\n.RE\n.sp\n\\fB\\-f\\fP text|json, \\fB\\-\\-format\\fP text|json\n.RS 4\nFormat of output. Defaults to text. The json format can be used\nto get a more parser\\-friendly output.\n.RE\n.sp\n\\fB\\-\\-error\\-trace\\fP\n.RS 4\nShow full error trace. Disabled by default, as the full trace\nusually makes error messages less readable and may not always deliver\nrelevant information.\n.RE\n.sp\n\\fB\\-\\-ll\\fP\n.RS 4\nDump LLVM assembly file to output directory.\n.RE\n.sp\n\\fB\\-\\-link\\-flags\\fP \\fIFLAGS\\fP\n.RS 4\nPass additional flags to the linker. Though you can specify those\nflags on the source code, this is useful for passing environment\nspecific information directly to the linker, like non\\-standard\nlibrary paths or names. For more information on specifying linker\nflags on source, you can read the \"C bindings\" section of the\ndocumentation available on the official web site.\n.RE\n.sp\n\\fB\\-\\-mcpu\\fP \\fICPU\\fP\n.RS 4\nSpecify a specific CPU to generate code for. This will pass a\n\\-mcpu flag to LLVM, and is only intended to be used for cross\\-\ncompilation. For a list of available CPUs, pass \\-\\-mcpu help\nwhen building any Crystal source code.  Passing \\-\\-mcpu native\nwill pass the host CPU name to tune performance for the host.\n.RE\n.sp\n\\fB\\-\\-mattr\\fP \\fICPU\\fP\n.RS 4\nOverride or control specific attributes of the target, such as\nwhether SIMD operations are enabled or not. The default set of\nattributes is set by the current CPU. This will pass a \\-mattr\nflag to LLVM, and is only intended to be used for cross\\-compilation. For a list of available attributes, invoke \"llvm\\-as <\n/dev/null | llc \\-march=xyz \\-mattr=help\".\n.RE\n.sp\n\\fB\\-\\-mcmodel\\fP default|kernel|tiny|small|medium|large\n.RS 4\nSpecifies a specific code model to generate code for. This will\npass a \\-\\-code\\-model flag to LLVM.\n.RE\n.sp\n\\fB\\-\\-no\\-color\\fP\n.RS 4\nDisable colored output.\n.RE\n.sp\n\\fB\\-\\-no\\-codegen\\fP\n.RS 4\nDon\\(cqt do code generation, just parse the file.\n.RE\n.sp\n\\fB\\-o\\fP\n.RS 4\nSpecify filename of output.\n.RE\n.sp\n\\fB\\-\\-prelude\\fP\n.RS 4\nSpecify prelude to use. The default one initializes the garbage\ncollector. You can also use \\-\\-prelude=empty to use no preludes.\nThis can be useful for checking code generation for a specific\nsource code file.\n.RE\n.sp\n\\fB\\-O\\fP \\fILEVEL\\fP\n.RS 4\nOptimization mode: 0 (default), 1, 2, 3. See \\fBOPTIMIZATIONS\\fP for\ndetails.\n.RE\n.sp\n\\fB\\-\\-release\\fP\n.RS 4\nCompile in release mode. Equivalent to \\fB\\-O3 \\-\\-single\\-module\\fP\n.RE\n.sp\n\\fB\\-s\\fP, \\fB\\-\\-stats\\fP\n.RS 4\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler process.\n.RE\n.sp\n\\fB\\-p\\fP, \\fB\\-\\-progress\\fP\n.RS 4\nPrint statistics about the progress for the current build.\n.RE\n.sp\n\\fB\\-t\\fP, \\fB\\-\\-time\\fP\n.RS 4\nPrint statistics about the execution time.\n.RE\n.sp\n\\fB\\-\\-single\\-module\\fP\n.RS 4\nGenerate a single LLVM module.  By default, one LLVM module is\ncreated for each type in a program.  \\fB\\-\\-release\\fP implies this option.\n.RE\n.sp\n\\fB\\-\\-threads\\fP \\fINUM\\fP\n.RS 4\nMaximum number of threads to use for code generation. The default\nis 8 threads.\n.RE\n.sp\n\\fB\\-\\-target\\fP \\fITRIPLE\\fP\n.RS 4\nEnable target triple; intended to use for cross\\-compilation. See\nllvm documentation for more information about target triple.\n.RE\n.sp\n\\fB\\-\\-verbose\\fP\n.RS 4\nDisplay the commands executed by the system.\n.RE\n.sp\n\\fB\\-\\-static\\fP\n.RS 4\nCreate a statically linked executable.\n.RE\n.sp\n\\fB\\-\\-stdin\\-filename\\fP \\fIFILENAME\\fP\n.RS 4\nSource file name to be read from STDIN.\n.RE\n.SS \"docs\"\n.sp\nGenerate documentation from comments using a subset of markdown. The output\nis saved in html format on the created docs/ folder. More information about\ndocumentation conventions can be found at \\c\n.URL \"https://crystal\\-lang.org/docs/conventions/documenting_code.html\" \"\" \".\"\n.sp\nOptions:\n.sp\n\\fB\\-\\-project\\-name\\fP \\fINAME\\fP\n.RS 4\nSet the project name. The default value is extracted from\nshard.yml if available.\n.sp\nIn case no default can be found, this option is mandatory.\n.RE\n.sp\n\\fB\\-\\-project\\-version\\fP \\fIVERSION\\fP\n.RS 4\nSet the project version. The default value is extracted from current git commit or shard.yml if available.\n.sp\nIn case no default can be found, this option is mandatory.\n.RE\n.sp\n\\fB\\-\\-json\\-config\\-url\\fP \\fIURL\\fP\n.RS 4\nSet the URL pointing to a config file (used for discovering versions).\n.RE\n.sp\n\\fB\\-\\-source\\-refname\\fP \\fIREFNAME\\fP\n.RS 4\nSet source refname (e.g. git tag, commit hash). The default value\nis extracted from current git commit if available.\n.sp\nIf this option is missing and can\\(cqt be automatically determined,\nthe generator can\\(cqt produce source code links.\n.RE\n.sp\n\\fB\\-\\-source\\-url\\-pattern\\fP \\fIURL\\fP\n.RS 4\nSet URL pattern for source code links. The default value is extracted from git remotes (\"origin\" or first one) if available and\nthe provider\\(cqs URL pattern is recognized.\n.sp\nSupported replacement tags:\n.sp\n\\fB%{refname}\\fP\n.RS 4\ncommit reference\n.RE\n.sp\n\\fB%{path}\\fP\n.RS 4\npath to source file inside the repository\n.RE\n.sp\n\\fB%{filename}\\fP\n.RS 4\nbasename of the source file\n.RE\n.sp\n\\fB%{line}\\fP\n.RS 4\nline number\n.RE\n.sp\nIf this option is missing and can\\(cqt be automatically determined,\nthe generator can\\(cqt produce source code links.\n.RE\n.sp\n\\fB\\-o\\fP \\fIDIR\\fP, \\fB\\-\\-output\\fP \\fIDIR\\fP\n.RS 4\nSet the output directory (default: ./docs).\n.RE\n.sp\n\\fB\\-b\\fP \\fIURL\\fP, \\fB\\-\\-canonical\\-base\\-url\\fP \\fIURL\\fP\n.RS 4\nIndicate the preferred URL with rel=\"canonical\" link element.\n.RE\n.sp\n\\fB\\-b\\fP \\fIURL\\fP, \\fB\\-\\-sitemap\\-base\\-url\\fP \\fIURL\\fP\n.RS 4\nSet the sitemap base URL. Sitemap will only be generated when\nthis option is set.\n.RE\n.sp\n\\fB\\-\\-sitemap\\-priority\\fP \\fIPRIO\\fP\n.RS 4\nSet the priority assigned to sitemap entries (default: 1.0).\n.RE\n.sp\n\\fB\\-\\-sitemap\\-changefreq\\fP \\fIFREQ\\fP\n.RS 4\nSet the changefreq assigned to sitemap entries (default: never).\n.RE\n.SS \"env\"\n.sp\n\\fBenv\\fP [variables]\n.sp\nPrint Crystal\\-specific environment variables in a format compatible with\nshell scripts. If one or more variable names are given as arguments, it\nprints only the value of each named variable on its own line.\n.sp\nVariables:\n.sp\n\\fBCRYSTAL_CACHE_DIR\\fP\n.RS 4\nPlease see ENVIRONMENT VARIABLES.\n.RE\n.sp\n\\fBCRYSTAL_EXEC_PATH\\fP\n.RS 4\nPlease see ENVIRONMENT VARIABLES.\n.RE\n.sp\n\\fBCRYSTAL_LIBRARY_PATH\\fP\n.RS 4\nPlease see ENVIRONMENT VARIABLES.\n.RE\n.sp\n\\fBCRYSTAL_PATH\\fP\n.RS 4\nPlease see ENVIRONMENT VARIABLES.\n.RE\n.sp\n\\fBCRYSTAL_VERSION\\fP\n.RS 4\nContains Crystal version.\n.RE\n.SS \"eval\"\n.sp\n\\fBeval\\fP [options] [source]\n.sp\nEvaluate code from arguments or, if no arguments are passed, from the standard input. Useful for experiments.\n.sp\nOptions:\n.sp\n\\fB\\-d\\fP, \\fB\\-\\-debug\\fP\n.RS 4\nGenerate the output with symbolic debug symbols.  These are read\nwhen debugging the built program with tools like lldb, gdb, valgrind etc. and provide mappings to the original source code for\nthose tools.\n.RE\n.sp\n\\fB\\-\\-no\\-debug\\fP\n.RS 4\nGenerate the output without any symbolic debug symbols.\n.RE\n.sp\n\\fB\\-D\\fP \\fIFLAG\\fP, \\fB\\-\\-define\\fP \\fIFLAG\\fP\n.RS 4\nDefine a compile\\-time flag. This is useful to conditionally define types, methods, or commands based on flags available at compile time. The default flags are from the target triple given\nwith \\-\\-target\\-triple or the hosts default, if none is given.\n.RE\n.sp\n\\fB\\-\\-error\\-trace\\fP\n.RS 4\nShow full error trace.\n.RE\n.sp\n\\fB\\-O\\fP \\fILEVEL\\fP\n.RS 4\nOptimization mode: 0 (default), 1, 2, 3. See \\fBOPTIMIZATIONS\\fP for details.\n.RE\n.sp\n\\fB\\-\\-release\\fP\n.RS 4\nCompile in release mode. Equivalent to \\fB\\-O3 \\-\\-single\\-module\\fP\n.RE\n.sp\n\\fB\\-s\\fP, \\fB\\-\\-stats\\fP\n.RS 4\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler\nprocess.\n.RE\n.sp\n\\fB\\-p\\fP, \\fB\\-\\-progress\\fP\n.RS 4\nPrint statistics about the progress for the current build.\n.RE\n.sp\n\\fB\\-t\\fP, \\fB\\-\\-time\\fP\n.RS 4\nPrint statistics about the execution time.\n.RE\n.sp\n\\fB\\-\\-no\\-color\\fP\n.RS 4\nDisable colored output.\n.RE\n.SS \"play\"\n.sp\n\\fBplay\\fP [options] [file]\n.sp\nStarts the \\fBcrystal\\fP playground server on port 8080, by default.\n.sp\nOptions:\n.sp\n\\fB\\-p\\fP \\fIPORT\\fP, \\fB\\-\\-port\\fP \\fIPORT\\fP\n.RS 4\nRun the playground on the specified port. Default is 8080.\n.RE\n.sp\n\\fB\\-b\\fP \\fIHOST\\fP, \\fB\\-\\-binding\\fP \\fIHOST\\fP\n.RS 4\nBind the playground to the specified IP.\n.RE\n.sp\n\\fB\\-v\\fP, \\fB\\-\\-verbose\\fP\n.RS 4\nDisplay detailed information of the executed code.\n.RE\n.SS \"run\"\n.sp\n\\fBrun\\fP [options] [programfile] [\\-\\-] [arguments]\n.sp\nThe default command. Compile and run program.\n.sp\nOptions: Same as the build options.\n.SS \"spec\"\n.sp\nspec [options] [files]\n.sp\nCompile and run specs (in spec directory).\n.sp\nOptions:\n.sp\n\\fB\\-d\\fP, \\fB\\-\\-debug\\fP\n.RS 4\nGenerate the output with symbolic debug symbols.  These are read\nwhen debugging the built program with tools like lldb, gdb, valgrind etc. and provide mappings to the original source code for\nthose tools.\n.RE\n.sp\n\\fB\\-\\-no\\-debug\\fP\n.RS 4\nGenerate the output without any symbolic debug symbols.\n.RE\n.sp\n\\fB\\-D\\fP \\fIFLAG\\fP, \\fB\\-\\-define\\fP \\fIFLAG\\fP\n.RS 4\nDefine a compile\\-time flag. This is useful to conditionally define types, methods, or commands based on flags available at compile time. The default flags are from the target triple given\nwith \\fB\\-\\-target\\-triple\\fP  or the hosts default, if none is given.\n.RE\n.sp\n\\fB\\-\\-error\\-trace\\fP\n.RS 4\nShow full error trace.\n.RE\n.sp\n\\fB\\-O\\fP \\fILEVEL\\fP\n.RS 4\nOptimization mode: 0 (default), 1, 2, 3. See \\fBOPTIMIZATIONS\\fP for details.\n.RE\n.sp\n\\fB\\-\\-release\\fP\n.RS 4\nCompile in release mode. Equivalent to \\fB\\-O3 \\-\\-single\\-module\\fP\n.RE\n.sp\n\\fB\\-s\\fP, \\fB\\-\\-stats\\fP\n.RS 4\nPrint statistics about the different compiler stages for the current build. Output time and used memory for each compiler\nprocess.\n.RE\n.sp\n\\fB\\-p\\fP, \\fB\\-\\-progress\\fP\n.RS 4\nPrint statistics about the progress for the current build.\n.RE\n.sp\n\\fB\\-t\\fP, \\fB\\-\\-time\\fP\n.RS 4\nPrint statistics about the execution time.\n.RE\n.sp\n\\fB\\-\\-no\\-color\\fP\n.RS 4\nDisable colored output.\n.RE\n.SS \"tool\"\n.sp\n\\fBtool\\fP [tool] [switches] [programfile] [\\-\\-] [arguments]\n.sp\nRun a tool. The available tools are: context, dependencies, expand, flags,\nformat, hierarchy, implementations, types, and unreachable.\n.sp\nTools:\n.sp\n\\fBcontext\\fP\n.RS 4\nShow context for given location.\n.RE\n.sp\n\\fBdependencies\\fP\n.RS 4\nShow tree of required source files.\n.sp\nOptions:\n.sp\n\\fB\\-D\\fP \\fIFLAG\\fP, \\fB\\-\\-define\\fP=\\fIFLAG\\fP\n.RS 4\nDefine a compile\\-time flag. This is useful to con    ditionally define types, methods, or commands based\non flags available at compile time. The default\nflags are from the target triple given with \\fB\\-\\-tar\\fP     get\\-triple or the hosts default, if none is given.\n.RE\n.sp\n\\fB\\-f\\fP \\fIFORMAT\\fP, \\fB\\-\\-format\\fP=\\fIFORMAT\\fP\n.RS 4\nOutput format \\*(Aqtree\\*(Aq (default), \\*(Aqflat\\*(Aq, \\*(Aqdot\\*(Aq, or\n\\*(Aqmermaid\\*(Aq.\n.RE\n.sp\n\\fB\\-i\\fP \\fIPATH\\fP, \\fB\\-\\-include\\fP=\\fIPATH\\fP\n.RS 4\nInclude path in output.\n.RE\n.sp\n\\fB\\-e\\fP \\fIPATH\\fP, \\fB\\-\\-exclude\\fP=\\fIPATH\\fP\n.RS 4\nExclude path in output.\n.RE\n.sp\n\\fB\\-\\-error\\-trace\\fP\n.RS 4\nShow full error trace.\n.RE\n.sp\n\\fB\\-\\-prelude\\fP\n.RS 4\nSpecify prelude to use. The default one initializes\nthe garbage collector. You can also use \\fB\\-\\-pre\\fP     lude=empty to use no preludes. This can be useful\nfor checking code generation for a specific source\ncode file.\n.RE\n.sp\n\\fB\\-\\-verbose\\fP\n.RS 4\nShow skipped and heads of filtered paths\n.RE\n.RE\n.sp\n\\fBexpand\\fP\n.RS 4\nShow macro expansion for given location.\n.RE\n.sp\n\\fBflags\\fP\n.RS 4\nPrint all macro \\*(Aqflag?\\*(Aq values\n.RE\n.sp\n\\fBformat\\fP\n.RS 4\nFormat project, directories and/or files with the coding\nstyle used in the standard library. You can use the\n\\fB\\-\\-checkflag\\fP  to check whether the formatter would make any\nchanges.\n.RE\n.sp\n\\fBhierarchy\\fP\n.RS 4\nShow hierarchy of types from file. Also show class and struct\nmembers, with type and size. Types can be filtered with a\nregex by using the \\fB\\-e\\fP flag.\n.RE\n.sp\n\\fBimplementations\\fP\n.RS 4\nShow implementations for a given call. Use \\fB\\-\\-cursor\\fP  to specify the cursor position. The format for the cursor position\nis file:line:column.\n.RE\n.sp\n\\fBtypes\\fP\n.RS 4\nShow type of main variables of file.\n.RE\n.sp\n\\fBunreachable\\fP\n.RS 4\nShow methods that are never called. The text output is a list\nof lines with columns separated by tab.\n.sp\nOutput fields:\n.sp\n\\fBcount\\fP\n.RS 4\nsum of all calls to this method (only with\n\\fB\\-\\-tallies\\fP  option; otherwise skipped)\n.RE\n.sp\n\\fBlocation\\fP\n.RS 4\npathname, line and column, all separated by colon\nname\n.RE\n.sp\n\\fBlines\\fP\n.RS 4\nlength of the def in lines\nannotations\n.RE\n.sp\nOptions:\n.sp\n\\fB\\-D\\fP \\fIFLAG\\fP, \\fB\\-\\-define\\fP=\\fIFLAG\\fP\n.RS 4\nDefine a compile\\-time flag. This is useful to con    ditionally define types,\nmethods, or commands based on flags available at compile time. The default\nflags are from the target triple given with \\fB\\-\\-target\\-triple\\fP or the hosts\ndefault, if none is given.\n.RE\n.sp\n\\fB\\-f\\fP \\fIFORMAT\\fP, \\fB\\-\\-format\\fP=\\fIFORMAT\\fP\n.RS 4\nOutput format \\*(Aqtext\\*(Aq (default), \\*(Aqjson\\*(Aq, \\*(Aqcodecov\\*(Aq, or \\*(Aqcsv\\*(Aq.\n.RE\n.sp\n\\fB\\-\\-tallies\\fP\n.RS 4\nPrint reachable methods and their call counts as well.\n.RE\n.sp\n\\fB\\-\\-check\\fP\n.RS 4\nExit with error if there is any unreachable code.\n.RE\n.sp\n\\fB\\-i\\fP \\fIPATH\\fP, \\fB\\-\\-include\\fP=\\fIPATH\\fP\n.RS 4\nInclude path in output.\n.RE\n.sp\n\\fB\\-e\\fP \\fIPATH\\fP, \\fB\\-\\-exclude\\fP=\\fIPATH\\fP\n.RS 4\nExclude path in output (default: lib).\n.RE\n.sp\n\\fB\\-\\-error\\-trace\\fP\n.RS 4\nShow full error trace.\n.RE\n.sp\n\\fB\\-\\-prelude\\fP\n.RS 4\nSpecify prelude to use. The default one initializes the garbage collector. You\ncan also use \\fB\\-\\-prelude=empty\\fP to use no preludes. This can be useful for\nchecking code generation for a specific source code file.\n.RE\n.RE\n.SS \"clear_cache\"\n.sp\nClear the compiler cache (located at \\*(AqCRYSTAL_CACHE_DIR\\*(Aq).\n.SS \"help\"\n.sp\nShow help. Option \\fB\\-\\-help\\fP  or \\fB\\-h\\fP  can also be added to each command for command\\-specific\nhelp.\n.SS \"version\"\n.sp\nShow version.\n.SH \"OPTIMIZATIONS\"\n.sp\nThe optimization level specifies the codegen effort for producing optimal code.  It\\(cqs\na trade\\-off between compilation performance (decreasing per optimization level) and\nruntime performance (increasing per optimization level).\n.sp\nProduction builds should usually have the highest optimization level.  Best results\nare achieved with \\fB\\-\\-release\\fP  which also implies \\fB\\-\\-single\\-module\\fP\n.sp\n\\fB\\-O0\\fP\n.RS 4\nNo optimization (default)\n.RE\n.sp\n\\fB\\-O1\\fP\n.RS 4\nLow optimization\n.RE\n.sp\n\\fB\\-O2\\fP\n.RS 4\nMiddle optimization\n.RE\n.sp\n\\fB\\-O3\\fP\n.RS 4\nHigh optimization\n.RE\n.sp\n\\fB\\-Os\\fP\n.RS 4\nMiddle optimization with focus on file size\n.RE\n.sp\n\\fB\\-Oz\\fP\n.RS 4\nMiddle optimization aggressively focused on file size\n.RE\n.SH \"ENVIRONMENT VARIABLES\"\n.SS \"CRYSTAL_CACHE_DIR\"\n.sp\nDefines path where Crystal caches partial compilation results for faster\nsubsequent builds. This path is also used to temporarily store executables\nwhen Crystal programs are run with \\*(Aq\\fBcrystal\\fP run\\*(Aq rather than \\*(Aq\\fBcrystal\\fP\nbuild\\*(Aq.\n.SS \"CRYSTAL_EXEC_PATH\"\n.sp\nDetermines the path where crystal looks for external sub-commands.\n.SS \"CRYSTAL_LIBRARY_PATH\"\n.sp\nDefines paths where Crystal searches for (binary) libraries. Multiple paths\ncan be separated by \":\".\t These paths are passed to the linker as \\f(CR\\-L\\fP\nflags.\n.sp\nThe pattern \\*(Aq$ORIGIN\\*(Aq at the start of the path expands to the directory\nwhere the compiler binary is located. For example, \\*(Aq$ORIGIN/../lib/crystal\\*(Aq\nresolves the standard library path relative to the compiler location in a\ngeneric way, independent of the absolute paths (assuming the relative location is correct).\n.SS \"CRYSTAL_PATH\"\n.sp\nDefines paths where Crystal searches for required source files. Multiple\npaths can be separated by \":\".\n.sp\nThe pattern \\*(Aq$ORIGIN\\*(Aq at the start of the path expands to the directory\nwhere the compiler binary is located. For example, \\*(Aq$ORIGIN/../share/crystal/src\\*(Aq resolves the standard library path relative to the compiler location in a generic way, independent of the absolute paths (assuming the relative location is correct).\n.SS \"CRYSTAL_OPTS\"\n.sp\nDefines options for the Crystal compiler to be used besides the command\nline arguments. The syntax is identical to the command line arguments. This\nis handy when using Crystal in build setups, for example \\*(AqCRYSTAL_OPTS=\\-\\-debug make build\\*(Aq.\n.SH \"SEEALSO\"\n.sp\n\\fBshards\\fP(1)\n.sp\n.URL \"https://crystal\\-lang.org/\" \"\" \"\"\nThe official web site.\n.sp\n.URL \"https://github.com/crystal\\-lang/crystal\" \"\" \"\"\nOfficial Repository.\n"
  },
  {
    "path": "samples/2048.cr",
    "content": "# Based on 2048 by Gabriele Cirulli - gabrielecirulli.github.io/2048\n\nrequire \"colorize\"\n\nenum Action\n  Up\n  Down\n  Left\n  Right\n  Escape\n  Unknown\nend\n\nmodule Screen\n  TILES = {\n        0 => {Colorize::ColorANSI::White, nil},\n        2 => {Colorize::ColorANSI::Black, Colorize::ColorANSI::White},\n        4 => {Colorize::ColorANSI::Blue, Colorize::ColorANSI::White},\n        8 => {Colorize::ColorANSI::Black, Colorize::ColorANSI::Yellow},\n       16 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Red},\n       32 => {Colorize::ColorANSI::Black, Colorize::ColorANSI::Red},\n       64 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Magenta},\n      128 => {Colorize::ColorANSI::Red, Colorize::ColorANSI::Yellow},\n      256 => {Colorize::ColorANSI::Magenta, Colorize::ColorANSI::Yellow},\n      512 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Yellow},\n     1024 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Yellow},\n     2048 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Yellow},\n     4096 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Black},\n     8192 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Black},\n    16384 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Black},\n    32768 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Black},\n    65536 => {Colorize::ColorANSI::White, Colorize::ColorANSI::Black},\n  }\n\n  def self.colorize_for(tile, &)\n    fg_color, bg_color = TILES[tile]\n    color = Colorize.with.fore(fg_color)\n    color = color.back(bg_color) if bg_color\n    color.surround do\n      yield\n    end\n  end\n\n  def self.clear\n    print \"\\e[2J\\e[1;1H\"\n  end\n\n  def self.read_keypress\n    STDIN.raw do |io|\n      buffer = Bytes.new(3)\n      bytes_read = io.read(buffer)\n      return Action::Unknown if bytes_read == 0\n      input = String.new(buffer[0, bytes_read])\n\n      case input\n      when \"\\e[A\", \"w\"\n        Action::Up\n      when \"\\e[B\", \"s\"\n        Action::Down\n      when \"\\e[C\", \"d\"\n        Action::Right\n      when \"\\e[D\", \"a\"\n        Action::Left\n      when \"\\e\", \"\\u{3}\", \"q\", \"Q\"\n        Action::Escape\n      else\n        Action::Unknown\n      end\n    end\n  end\nend\n\nclass Drawer\n  INNER_CELL_WIDTH  = 16\n  INNER_CELL_HEIGHT =  6\n\n  def initialize\n    @n = 0\n    @grid = [] of Array(String)\n    @current_row = [] of String\n    @content_line = false\n  end\n\n  def set_current_row(row)\n    @current_row = row\n  end\n\n  def draw(grid)\n    @grid = grid\n    @n = @grid.size\n\n    Screen.clear\n    box\n  end\n\n  def box\n    top_border\n\n    (@n - 1).times do |row|\n      tile row\n      mid_border\n    end\n\n    tile @n - 1\n\n    bottom_border\n  end\n\n  def tile(row)\n    set_current_row @grid[row]\n\n    INNER_CELL_HEIGHT.times do |i|\n      if i == (@n / 2) + 1\n        content_line\n      else\n        space_line\n      end\n    end\n\n    set_current_row [] of String\n  end\n\n  def space_line\n    line '│', \" \", '│', '│'\n  end\n\n  def content_line\n    @content_line = true\n    space_line\n    @content_line = false\n  end\n\n  def top_border\n    line '┌', \"─\", '┬', '┐'\n  end\n\n  def mid_border\n    line '├', \"─\", '┼', '┤'\n  end\n\n  def bottom_border\n    line '└', \"─\", '┴', '┘'\n  end\n\n  def line(left, fill, inner, right)\n    print left\n\n    (@n - 1).times do |cell|\n      cell_line fill, cell\n\n      print inner\n    end\n\n    cell_line fill, @n - 1\n\n    puts right\n  end\n\n  def cell_line(fill, cell)\n    content = @current_row.fetch(cell) { \"empty\" }\n    tile_value = (content == \"empty\" ? 0 : (content.to_i? || 0)).to_i\n    content = \"\" if !@content_line || content == \"empty\"\n\n    fill_size = INNER_CELL_WIDTH // 2\n    fill_size -= content.size // 2\n    fill_size -= 2\n\n    print fill\n\n    Screen.colorize_for(tile_value) do\n      print fill*fill_size\n      print content\n      print fill*fill_size\n      print fill if content.size % 2 == 0\n    end\n    print fill\n  end\nend\n\nclass Game\n  def initialize\n    @drawer = Drawer.new\n    @grid = [\n      [nil, nil, nil, nil] of Int32?,\n      [nil, nil, nil, nil] of Int32?,\n      [nil, nil, nil, nil] of Int32?,\n      [nil, nil, nil, nil] of Int32?,\n    ]\n\n    insert_tile\n    insert_tile\n  end\n\n  def run\n    draw\n\n    until won? || lost?\n      if execute_action read_action\n        insert_tile\n        draw\n      end\n    end\n\n    if won?\n      end_game \"You won!\"\n    elsif lost?\n      end_game \"You lost!\"\n    else\n      raise \"Game loop quit unexpectedly\"\n    end\n  end\n\n  def draw\n    @drawer.draw drawable_grid\n  end\n\n  def drawable_grid\n    @grid.map &.map(&.to_s)\n  end\n\n  def read_action\n    Screen.read_keypress\n  end\n\n  def insert_tile\n    value = rand > 0.8 ? 4 : 2\n\n    empty_cells = @grid.sum(&.count &.nil?)\n\n    fill_cell = empty_cells > 1 ? rand(empty_cells - 1) + 1 : 1\n\n    empty_cell_count = 0\n\n    each_cell_with_index do |tile, row, col|\n      empty_cell_count += 1 unless tile\n\n      if empty_cell_count == fill_cell\n        @grid[row][col] = value\n        return\n      end\n    end\n  end\n\n  def each_cell_with_index(&)\n    0.upto(@grid.size - 1) do |row|\n      0.upto(@grid.size - 1) do |col|\n        yield @grid[row][col], row, col\n      end\n    end\n  end\n\n  def execute_action(action)\n    case action\n    in .up?, .down?, .left?, .right?\n      if can_move_in? action\n        shift_grid action\n        true\n      else\n        false\n      end\n    in .escape?\n      end_game \"Bye\"\n    in .unknown?\n      false # ignore\n    end\n  end\n\n  def shift_grid(direction)\n    drow, dcol = offsets_for direction\n    shift_tiles_to_empty_cells direction, drow, dcol\n    merge_tiles direction, drow, dcol\n    shift_tiles_to_empty_cells direction, drow, dcol\n  end\n\n  def shift_tiles_to_empty_cells(direction, drow, dcol)\n    modified = true\n    while modified\n      modified = false\n      movable_tiles(direction, drow, dcol) do |tile, row, col|\n        unless @grid[row + drow][col + dcol]\n          @grid[row + drow][col + dcol] = tile\n          @grid[row][col] = nil\n          modified = true\n        end\n      end\n    end\n  end\n\n  def merge_tiles(direction, drow, dcol)\n    movable_tiles(direction, drow, dcol) do |tile, row, col|\n      if @grid[row + drow][col + dcol] == tile\n        @grid[row][col] = nil\n        @grid[row + drow][col + dcol] = tile*2\n      end\n    end\n  end\n\n  def movable_tiles(direction, drow, dcol, &)\n    max = @grid.size - 1\n    from_row, to_row, from_column, to_column =\n      case direction\n      when .up?, .left?\n        {0, max, 0, max}\n      when .down?, .right?\n        {max, 0, max, 0}\n      else\n        raise ArgumentError.new \"Unknown direction #{direction}\"\n      end\n    from_row.to(to_row) do |row|\n      from_column.to(to_column) do |col|\n        tile = @grid[row][col]\n        if tile && !to_border?(direction, row, col, drow, dcol)\n          yield tile, row, col\n        end\n      end\n    end\n  end\n\n  def can_move_in?(direction)\n    drow, dcol = offsets_for direction\n\n    movable_tiles(direction, drow, dcol) do |tile, row, col|\n      target_tile = @grid[row + drow][col + dcol]\n      return true if !target_tile || target_tile == tile\n    end\n\n    false\n  end\n\n  def offsets_for(direction)\n    drow = dcol = 0\n\n    case direction\n    when .up?\n      drow = -1\n    when .down?\n      drow = 1\n    when .left?\n      dcol = -1\n    when .right?\n      dcol = 1\n    else\n      raise ArgumentError.new \"Unknown direction #{direction}\"\n    end\n\n    {drow, dcol}\n  end\n\n  def to_border?(direction, row, col, drow, dcol)\n    case direction\n    when .up?\n      row + drow < 0\n    when .down?\n      row + drow >= @grid.size\n    when .left?\n      col + dcol < 0\n    when .right?\n      col + dcol >= @grid.size\n    else\n      false\n    end\n  end\n\n  def won?\n    @grid.any? &.any?(&.==(2048))\n  end\n\n  def lost?\n    !can_move?\n  end\n\n  def can_move?\n    can_move_in?(Action::Up) || can_move_in?(Action::Down) ||\n      can_move_in?(Action::Left) || can_move_in?(Action::Right)\n  end\n\n  def end_game(msg)\n    puts msg\n    exit\n  end\nend\n\nat_exit { STDIN.cooked! }\nGame.new.run\n"
  },
  {
    "path": "samples/Makefile",
    "content": "CRYSTAL := ../bin/crystal## Crystal compiler to use\nO := .build## Output directory\n\nBUILDABLE_SOURCES := $(wildcard *.cr llvm/*.cr compiler/*.cr)\nNONLINK_SOURCES := $(wildcard sdl/*.cr)\n\nBUILDABLE_BINARIES := $(patsubst %.cr,$(O)/%,$(BUILDABLE_SOURCES))\nNONLINK_BINARIES := $(patsubst %.cr,$(O)/%.o,$(NONLINK_SOURCES))\n\n.PHONY: all\nall: build\n\n.PHONY: build\nbuild: $(BUILDABLE_BINARIES) $(NONLINK_BINARIES) ## Build sample binaries\n\n$(O)/%: %.cr\n\tmkdir -p $(shell dirname $@)\n\t$(CRYSTAL) build $< -o $@\n\n$(O)/%.o: %.cr\n\tmkdir -p $(shell dirname $@)\n\t$(CRYSTAL) build --cross-compile $< -o $(patsubst %.o,%,$@)\n\n.PHONY: clean\nclean: ## Remove build artifacts\n\trm -rf $(O)\n\n.PHONY: help\nhelp: ## Show this help\n\t@echo\n\t@printf '\\033[34mtargets:\\033[0m\\n'\n\t@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\\\n\t\tsort |\\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\t@echo\n\t@printf '\\033[34moptional variables:\\033[0m\\n'\n\t@grep -hE '^[a-zA-Z_-]+ \\?=.*?## .*$$' $(MAKEFILE_LIST) |\\\n\t\tsort |\\\n\t\tawk 'BEGIN {FS = \" \\\\?=.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\t@echo\n\t@printf '\\033[34mrecipes:\\033[0m\\n'\n\t@grep -hE '^##.*$$' $(MAKEFILE_LIST) |\\\n\t\tawk 'BEGIN {FS = \"## \"}; /^## [a-zA-Z_-]/ {printf \"  \\033[36m%s\\033[0m\\n\", $$2}; /^##  / {printf \"  %s\\n\", $$2}'\n"
  },
  {
    "path": "samples/Makefile.win",
    "content": "all:\r\n\r\nMAKEFLAGS += --no-builtin-rules\r\n.SUFFIXES:\r\n\r\nSHELL := cmd.exe\r\n\r\nMKDIR = if not exist $1 mkdir $1\r\nRMDIR = if exist $1 rd /S /Q $1\r\n\r\nCRYSTAL := ..\\bin\\crystal.bat# Crystal compiler to use\r\nO := .build# Output directory\r\n\r\nBUILDABLE_SOURCES := $(wildcard *.cr llvm/*.cr compiler/*.cr)\r\nNONLINK_SOURCES := $(wildcard sdl/*.cr)\r\n\r\nBUILDABLE_BINARIES := $(patsubst %.cr,$(O)/%.exe,$(BUILDABLE_SOURCES))\r\nNONLINK_BINARIES := $(patsubst %.cr,$(O)/%.obj,$(NONLINK_SOURCES))\r\n\r\n.PHONY: all\r\nall: build\r\n\r\n.PHONY: build\r\nbuild: $(BUILDABLE_BINARIES) $(NONLINK_BINARIES) ## Build sample binaries\r\n\r\n$(O)/%.exe: %.cr\r\n\t$(call MKDIR,\"$(dir $@)\")\r\n\t$(CRYSTAL) build \"$<\" -o \"$@\"\r\n\r\n$(O)/%.obj: %.cr\r\n\t$(call MKDIR,\"$(dir $@)\")\r\n\t$(CRYSTAL) build --cross-compile \"$<\" -o \"$(patsubst %.obj,%,$@)\"\r\n\r\n.PHONY: clean\r\nclean: ## Remove build artifacts\r\n\t$(call RMDIR,\"$(O)\")\r\n\r\n.PHONY: help\r\nhelp: ## Show this help\r\n\t@setlocal EnableDelayedExpansion &\\\r\n\techo. &\\\r\n\techo targets: &\\\r\n\t(for /F \"usebackq tokens=1* delims=:\" %%g in ($(MAKEFILE_LIST)) do (\\\r\n\t\tif not \"%%h\" == \"\" (\\\r\n\t\t\tset \"_line=%%g                \" &\\\r\n\t\t\tset \"_rest=%%h\" &\\\r\n\t\t\tset \"_comment=!_rest:* ## =!\" &\\\r\n\t\t\tif not \"!_comment!\" == \"!_rest!\"\\\r\n\t\t\t\tif \"!_line:_rest=!\" == \"!_line!\"\\\r\n\t\t\t\t\techo   !_line:~0,16!!_comment!\\\r\n\t\t)\\\r\n\t)) &\\\r\n\techo. &\\\r\n\techo optional variables: &\\\r\n\t(for /F \"usebackq tokens=1,3 delims=?#\" %%g in ($(MAKEFILE_LIST)) do (\\\r\n\t\tif not \"%%h\" == \"\" (\\\r\n\t\t\tset \"_var=%%g              \" &\\\r\n\t\t\techo   !_var:~0,14! %%h\\\r\n\t\t)\\\r\n\t)) &\\\r\n\techo. &\\\r\n\techo recipes: &\\\r\n\t(for /F \"usebackq tokens=* delims=\" %%g in ($(MAKEFILE_LIST)) do (\\\r\n\t\tset \"_line=%%g\" &\\\r\n\t\tif \"!_line:~0,7!\" == \"##   $$ \" (\\\r\n\t\t\techo !_name! &\\\r\n\t\t\techo  !_line:~2!\\\r\n\t\t) else if \"!_line:~0,3!\" == \"## \"\\\r\n\t\t\tset \"_name=  !_line:~3!\"\\\r\n\t))\r\n"
  },
  {
    "path": "samples/binary-trees.cr",
    "content": "# Copied with little modifications from: https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/binarytrees-yarv-1.html\n\nclass Node\n  def initialize(@a : Node?, @b : Int32, @c : Node?)\n  end\n\n  property :a\n  property :b\n  property :c\nend\n\ndef item_check(tree)\n  tree = tree.not_nil!\n  return tree.b if tree.a.nil?\n  tree.b + item_check(tree.a) - item_check(tree.c)\nend\n\ndef bottom_up_tree(item, depth)\n  return Node.new(nil, item, nil) unless depth > 0\n  item_item = 2 * item\n  depth -= 1\n  Node.new(bottom_up_tree(item_item - 1, depth), item, bottom_up_tree(item_item, depth))\nend\n\nmax_depth = (ARGV[0]? || 15).to_i\nmin_depth = 4\n\nmax_depth = min_depth + 2 if min_depth + 2 > max_depth\n\nstretch_depth = max_depth + 1\nstretch_tree = bottom_up_tree(0, stretch_depth)\n\nputs \"stretch tree of depth #{stretch_depth}\\t check: #{item_check(stretch_tree)}\"\n\nlong_lived_tree = bottom_up_tree(0, max_depth)\n\nmin_depth.step(to: max_depth + 1, by: 2) do |depth|\n  iterations = 2**(max_depth - depth + min_depth)\n\n  check = 0\n\n  (1..iterations).each do |i|\n    temp_tree = bottom_up_tree(i, depth)\n    check += item_check(temp_tree)\n\n    temp_tree = bottom_up_tree(-i, depth)\n    check += item_check(temp_tree)\n  end\n\n  puts \"#{iterations * 2}\\t trees of depth #{depth}\\t check: #{check}\"\nend\n\nputs \"long lived tree of depth #{max_depth}\\t check: #{item_check(long_lived_tree)}\"\n"
  },
  {
    "path": "samples/brainfuck.cr",
    "content": "# Brainf*ck interpreter\n\nstruct Tape\n  def initialize\n    @tape = [0]\n    @pos = 0\n  end\n\n  def get\n    @tape[@pos]\n  end\n\n  def inc\n    @tape[@pos] += 1\n  end\n\n  def dec\n    @tape[@pos] -= 1\n  end\n\n  def advance\n    @pos += 1\n    @tape << 0 if @tape.size <= @pos\n  end\n\n  def devance\n    @pos -= 1\n    raise \"pos should be > 0\" if @pos < 0\n  end\n\n  def set(value)\n    @tape[@pos] = value\n  end\nend\n\nclass Program\n  def initialize(@chars : Array(Char), @bracket_map : Hash(Int32, Int32))\n  end\n\n  def run\n    tape = Tape.new\n    pc = 0\n    while pc < @chars.size\n      case @chars[pc]\n      when '>'; tape.advance\n      when '<'; tape.devance\n      when '+'; tape.inc\n      when '-'; tape.dec\n      when '.'; print tape.get.chr\n      when ','; STDIN.read_byte.try { |byte| tape.set byte.to_i }\n      when '['; pc = @bracket_map[pc] if tape.get == 0\n      when ']'; pc = @bracket_map[pc] if tape.get != 0\n      else # skip\n      end\n      pc += 1\n    end\n  end\n\n  def self.parse(text)\n    parsed = [] of Char\n    bracket_map = {} of Int32 => Int32\n    leftstack = [] of Int32\n    pc = 0\n    text.each_char do |char|\n      if \"[]<>+-,.\".includes?(char)\n        parsed << char\n        if char == '['\n          leftstack << pc\n        elsif char == ']' && !leftstack.empty?\n          left = leftstack.pop\n          right = pc\n          bracket_map[left] = right\n          bracket_map[right] = left\n        end\n        pc += 1\n      end\n    end\n\n    Program.new(parsed, bracket_map)\n  end\nend\n\ntext = if ARGV.size > 0\n         File.read(ARGV[0])\n       else\n         <<-PROGRAM\n         Benchmark brainf*ck program\n         >++[<+++++++++++++>-]<[[>+>+<<-]>[<+>-]++++++++\n         [>++++++++<-]>.[-]<<>++++++++++[>++++++++++[>++\n         ++++++++[>++++++++++[>++++++++++[>++++++++++[>+\n         +++++++++[-]<-]<-]<-]<-]<-]<-]<-]++++++++++.\n         PROGRAM\n       end\n\nProgram.parse(text).run\n"
  },
  {
    "path": "samples/channel_primes.cr",
    "content": "# Ported from Go sample from this page: https://web.archive.org/web/20160602135806/http://dancallahan.info/journal/go-concurrency/#How+do+channels+and+goroutines+work+together?\n\ndef generate(chan)\n  i = 2\n  loop do\n    chan.send(i)\n    i += 1\n  end\nend\n\ndef filter(in_chan, out_chan, prime)\n  loop do\n    i = in_chan.receive\n    if i % prime != 0\n      out_chan.send(i)\n    end\n  end\nend\n\nch = Channel(Int32).new\nspawn generate(ch)\n\n100.times do\n  prime = ch.receive\n  puts prime\n  ch1 = Channel(Int32).new\n  spawn filter(ch, ch1, prime)\n  ch = ch1\nend\n"
  },
  {
    "path": "samples/channel_select.cr",
    "content": "def generator(n : T) forall T\n  channel = Channel(T).new\n  spawn do\n    loop do\n      sleep n.seconds\n      channel.send n\n    end\n  end\n  channel\nend\n\nch1 = generator(1)\nch2 = generator(1.5)\nch3 = generator(5)\n\nloop do\n  select\n  when int = ch1.receive\n    puts \"Int: #{int}\"\n  when float = ch2.receive\n    puts \"Float: #{float}\"\n  when ch3.receive\n    break\n  end\nend\n"
  },
  {
    "path": "samples/compiler/formatter_example.cr",
    "content": "# This is a small sample on how to use a Crystal's\n# formatter programmatically.\n\n# Use `require \"compiler/crystal/formatter\"` in your programs\nrequire \"../../src/compiler/crystal/formatter\"\n\nsource = \"[ 1 , 2 , 3].map { | x |  x.to_s  }\"\nresult = Crystal.format(source)\nputs result\n"
  },
  {
    "path": "samples/compiler/transformer_example.cr",
    "content": "# This is a small sample on how to use a Crystal::Transformer\n# to transform source code.\n#\n# Here we transform all number literals with their char\n# equivalent using `chr`.\n\n# Use `require \"compiler/crystal/syntax\"` in your programs\nrequire \"../../src/compiler/crystal/syntax\"\n\nclass Charify < Crystal::Transformer\n  def transform(node : Crystal::NumberLiteral)\n    Crystal::CharLiteral.new(node.value.to_i.chr)\n  end\nend\n\nnodes = Crystal::Parser.parse(\"hello(99, 114, 121, 115, 116, 97, 108)\")\nputs nodes.transform(Charify.new)\n"
  },
  {
    "path": "samples/compiler/visitor_example.cr",
    "content": "# This is a small sample on how to use a Crystal::Visitor\n# to traverse an AST.\n#\n# Here we count the number of NumberLiterals in a program.\n\n# Use `require \"compiler/crystal/syntax\"` in your programs\nrequire \"../../src/compiler/crystal/syntax\"\n\nclass Counter < Crystal::Visitor\n  getter count\n\n  def initialize\n    @count = 0\n  end\n\n  def visit(node : Crystal::NumberLiteral)\n    @count += 1\n    false\n  end\n\n  def visit(node : Crystal::ASTNode)\n    # true: we want to the visitor to visit node's children\n    true\n  end\nend\n\nnodes = Crystal::Parser.parse(\"hello(99, 114, 121, 115, 116, 97, 108)\")\n\ncounter = Counter.new\nnodes.accept counter\nputs counter.count\n"
  },
  {
    "path": "samples/conway.cr",
    "content": "# Ported from http://arthurtw.github.io/2015/01/12/quick-comparison-nim-vs-rust.html\n\nstruct ANSI\n  def initialize(@io : IO)\n  end\n\n  def clear\n    @io << \"\\e[2J\"\n  end\n\n  def pos(x, y)\n    @io << \"\\e[\" << x << ';' << y << 'H'\n  end\nend\n\nclass IO\n  def ansi\n    ANSI.new self\n  end\nend\n\nstruct ConwayMap\n  WIDTH  = 40\n  HEIGHT = 30\n\n  include Math\n\n  @map : Array(Array(Bool))\n\n  def initialize(pattern)\n    @map = Array.new(HEIGHT) { Array.new(WIDTH, false) }\n\n    ix = min WIDTH, pattern.max_of(&.size)\n    iy = min HEIGHT, pattern.size\n    dx = (WIDTH - ix) // 2\n    dy = (HEIGHT - iy) // 2\n\n    iy.times do |y|\n      ix.times do |x|\n        if x < pattern[y].size && !pattern[y][x].whitespace?\n          @map[y + dy][x + dx] = true\n        end\n      end\n    end\n  end\n\n  def next\n    old_map = @map.clone\n\n    HEIGHT.times do |i|\n      WIDTH.times do |j|\n        nlive = 0\n\n        max(i - 1, 0).upto(min(i + 1, HEIGHT - 1)) do |i2|\n          max(j - 1, 0).upto(min(j + 1, WIDTH - 1)) do |j2|\n            nlive += 1 if old_map[i2][j2] && (i2 != i || j2 != j)\n          end\n        end\n\n        if @map[i][j]\n          @map[i][j] = 2 <= nlive <= 3\n        else\n          @map[i][j] = nlive == 3\n        end\n      end\n    end\n  end\n\n  def to_s(io)\n    io.ansi.clear\n    io.ansi.pos 1, 1\n    @map.each do |row|\n      row.each do |cell|\n        io << (cell ? \"()\" : \". \")\n      end\n      io.puts\n    end\n  end\nend\n\nPAUSE         = 20.milliseconds\nDEFAULT_COUNT = 300\nINITIAL_MAP   = [\n  \"                        1           \",\n  \"                      1 1           \",\n  \"            11      11            11\",\n  \"           1   1    11            11\",\n  \"11        1     1   11              \",\n  \"11        1   1 11    1 1           \",\n  \"          1     1       1           \",\n  \"           1   1                    \",\n  \"            11                      \",\n]\n\nmap = ConwayMap.new INITIAL_MAP\n\nspawn { gets; exit }\n\n1.upto(DEFAULT_COUNT) do |i|\n  puts map\n  puts \"n = #{i}\\tPress ENTER to exit\"\n  sleep PAUSE\n  map.next\nend\n"
  },
  {
    "path": "samples/degree_days.cr",
    "content": "# Copied with little modifications from: https://github.com/rubinius/rubinius-benchmark/blob/master/real_world/bench_degree_days.rb\n\nclass DegreeDays\n  def initialize(@daily_temperatures : Array(Array(Int32)), @options = {} of String => Float64)\n  end\n\n  property :daily_temperatures\n\n  def calculate\n    heating = 0.0\n    cooling = 0.0\n    heating_days = 0.0\n    cooling_days = 0.0\n\n    daily_temperatures.each do |day|\n      heating_today = heating_day(day)\n      cooling_today = cooling_day(day)\n\n      if heating_today\n        heating_days += 1\n        heating += heating_today\n      end\n\n      if cooling_today\n        cooling_days += 1\n        cooling += cooling_today\n      end\n    end\n\n    {\n      \"heating\"      => heating,\n      \"cooling\"      => cooling,\n      \"heating_days\" => heating_days,\n      \"cooling_days\" => cooling_days,\n    }\n  end\n\n  private def sum(ary)\n    ary.reduce(0) { |a, i| a + i }\n  end\n\n  private def avg(ary)\n    sum(ary).to_f / ary.size.to_f\n  end\n\n  private def heating_day(temps)\n    heat = avg temps.map { |temp| heating_degree(temp) }\n    (heat > heating_threshold) ? heat : nil\n  end\n\n  private def cooling_day(temps)\n    cool = avg temps.map { |temp| cooling_degree(temp) }\n    (cool > cooling_threshold) ? cool : nil\n  end\n\n  private def heating_degree(temp)\n    deg = base_temperature - (temp + heating_insulation)\n    {deg, 0}.max\n  end\n\n  private def cooling_degree(temp)\n    deg = (temp - cooling_insulation) - base_temperature\n    {deg, 0}.max\n  end\n\n  private def base_temperature\n    @options[\"base_temperature\"]? || 65.0\n  end\n\n  private def heating_insulation\n    @options[\"heating_insulation\"]? || insulation_factor || 3\n  end\n\n  private def cooling_insulation\n    @options[\"cooling_insulation\"]? || insulation_factor || 0\n  end\n\n  private def insulation_factor\n    @options[\"insulation_factor\"]?\n  end\n\n  private def heating_threshold\n    @options[\"heating_threshold\"]? || threshold || 6\n  end\n\n  private def cooling_threshold\n    @options[\"cooling_threshold\"]? || threshold || 3\n  end\n\n  private def threshold\n    @options[\"threshold\"]?\n  end\nend\n\n(ARGV[0]? || 300).to_i.times do |i|\n  days_in_year = 365\n  hours_in_day = 24\n\n  hot_day = Array.new(hours_in_day, 92)\n  cold_day = Array.new(hours_in_day, 37)\n\n  # 182 hot days + 183 cold days\n  temperatures = Array.new((days_in_year / 2.0).floor.to_i, hot_day) +\n                 Array.new((days_in_year / 2.0).ceil.to_i, cold_day)\n\n  degree_days = DegreeDays.new(temperatures)\n  res = degree_days.calculate\n  p res if i == 0\nend\n"
  },
  {
    "path": "samples/egrep.cr",
    "content": "if ARGV.empty?\n  abort \"Usage: cat somefile | egrep 'some'\"\nend\n\nregex = Regex.new(ARGV[0])\nwhile str = STDIN.gets\n  STDOUT.print(str) if str =~ regex\nend\n"
  },
  {
    "path": "samples/fannkuch-redux.cr",
    "content": "# Copied with little modifications from: https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/fannkuchredux-yarv-1.html\n\ndef fannkuch(n)\n  sign, maxflips, sum = 1, 0, 0\n\n  w = [0].concat((1..n).to_a)\n  q = w.dup\n  s = w.dup\n\n  while true\n    # Copy and flip.\n    q1 = w[1] # Cache 1st element.\n    if q1 != 1\n      q = w.dup\n      flips = 1\n      while true\n        qq = q[q1]\n        if qq == 1 # ... until 1st element is 1.\n          sum = sum + sign * flips\n          maxflips = flips if flips > maxflips # New maximum?\n          break\n        end\n        q[q1] = q1\n        if q1 >= 4\n          i, j = 2, q1 - 1\n\n          while true\n            q.swap i, j\n            i = i + 1\n            j = j - 1\n            break if !(i < j)\n          end\n        end\n        q1 = qq\n        flips = flips + 1\n      end\n    end\n    # Permute.\n    if sign == 1\n      # Rotate 1<-2.\n      w.swap 1, 2\n      sign = -1\n    else\n      # Rotate 1<-2 and 1<-2<-3.\n      w.swap 2, 3\n\n      sign = 1\n      3.upto(n) do |ki|\n        unless s[ki] == 1\n          s[ki] = s[ki] - 1\n          break\n        end\n\n        return [sum, maxflips] if ki == n # Out of permutations.\n\n        s[ki] = ki\n        # Rotate 1<-...<-i+1.\n        t = w[1]\n        1.upto(ki) do |kj|\n          w[kj] = w[kj + 1]\n        end\n        w[ki + 1] = t\n      end\n    end\n  end\nend\n\nn = (ARGV[0]? || 10).to_i\nsum, flips = fannkuch(n)\nputs \"#{sum}\\nPfannkuchen(#{n}) = #{flips}\"\n"
  },
  {
    "path": "samples/fibonacci.cr",
    "content": "def fibonacci(n : Int32) : Int32\n  return 0 if n < 0\n  return n if n <= 1\n\n  fibonacci(n - 1) + fibonacci(n - 2)\nend\n\nputs \"First ten Fibonacci numbers:\"\n(0..9).each do |n|\n  puts \"fibonacci(#{n}) = #{fibonacci(n)}\"\nend\n"
  },
  {
    "path": "samples/havlak.cr",
    "content": "# Havlak benchmark: https://code.google.com/p/multi-language-bench/\n# Crystal Implementation (translated from Python version)\n\n# Intel i5 2.5GHz\n# crystal: 26.5s 359Mb\n# c++:     28.2s 150Mb\n# java:    31.5s 909Mb\n# scala:   66.8s 316Mb\n# go:      67.7s 456Mb\n# python:  958.4s 713Mb\n\nclass BasicBlock\n  def initialize(@name : Int32)\n    @in_edges = [] of BasicBlock\n    @out_edges = [] of BasicBlock\n  end\n\n  property :in_edges\n  property :out_edges\n\n  def to_s(io)\n    io << \"BB#\"\n    io << @name\n  end\nend\n\nstruct BasicBlockEdge\n  @from : BasicBlock\n  @to : BasicBlock\n\n  def initialize(cfg, from_name, to_name)\n    @from = cfg.create_node(from_name)\n    @to = cfg.create_node(to_name)\n    @from.out_edges << @to\n    @to.in_edges << @from\n  end\n\n  def self.add(cfg, from_name, to_name)\n    edge = new(cfg, from_name, to_name)\n    cfg.add_edge(edge)\n  end\nend\n\nclass CFG\n  def initialize\n    @basic_block_map = {} of Int32 => BasicBlock\n    @edge_list = [] of BasicBlockEdge\n  end\n\n  property start_node : BasicBlock?\n  property :basic_block_map\n\n  def create_node(name)\n    node = @basic_block_map.put_if_absent(name) { BasicBlock.new(name) }\n    @start_node ||= node\n    node\n  end\n\n  def add_edge(edge)\n    @edge_list << edge\n  end\n\n  def num_nodes\n    @basic_block_map.size\n  end\nend\n\nclass SimpleLoop\n  def initialize\n    @basic_blocks = Set(BasicBlock).new\n    @children = Set(SimpleLoop).new\n    @parent = nil\n    @header = nil\n\n    @root = false\n    @reducible = true\n    @counter = 0\n    @nesting_level = 0\n    @depth_level = 0\n  end\n\n  property :counter\n  property? :reducible\n  property? :root\n  getter :parent\n  property :depth_level\n  property :children\n  getter :nesting_level\n\n  def add_node(bb)\n    @basic_blocks.add(bb)\n  end\n\n  def add_child_loop(l)\n    @children.add(l)\n  end\n\n  def parent=(parent : SimpleLoop)\n    @parent = parent\n    parent.add_child_loop(self)\n  end\n\n  def header=(bb : BasicBlock)\n    @basic_blocks.add(bb)\n    @header = bb\n  end\n\n  def nesting_level=(level)\n    @nesting_level = level\n    @root = true if level == 0\n  end\nend\n\nclass LSG\n  @@loop_counter = 0\n\n  @root : SimpleLoop\n\n  def initialize\n    @loops = [] of SimpleLoop\n    @root = create_new_loop\n    @root.nesting_level = 0\n    add_loop(@root)\n  end\n\n  def create_new_loop\n    s = SimpleLoop.new\n    s.counter = @@loop_counter += 1\n    s\n  end\n\n  def add_loop(l)\n    @loops << l\n  end\n\n  def calculate_nesting_level\n    @loops.each do |liter|\n      liter.parent = @root if !liter.root? && liter.parent == nil\n    end\n\n    calculate_nesting_level_rec(@root, 0)\n  end\n\n  def calculate_nesting_level_rec(l, depth)\n    l.depth_level = depth\n    l.children.each do |liter|\n      calculate_nesting_level_rec(liter, depth + 1)\n      l.nesting_level = Math.max(l.nesting_level, 1 + liter.nesting_level)\n    end\n  end\n\n  def num_loops\n    @loops.size\n  end\nend\n\nclass UnionFindNode\n  def initialize\n    @parent = nil\n    @bb = nil\n    @l = nil\n    @dfs_number = 0\n  end\n\n  def init_node(bb, dfs_number)\n    @parent = self\n    @bb = bb\n    @dfs_number = dfs_number\n  end\n\n  property bb : BasicBlock?\n  property parent : self?\n  property dfs_number : Int32\n  property l : SimpleLoop?\n\n  def find_set\n    node_list = [] of UnionFindNode\n\n    node = self\n    while node != node.parent\n      parent = node.parent.not_nil!\n      node_list << node if parent != parent.parent\n      node = parent\n    end\n\n    node_list.each { |iter| iter.parent = node.parent }\n\n    node\n  end\n\n  def union(union_find_node)\n    @parent = union_find_node\n  end\nend\n\nclass HavlakLoopFinder\n  BB_TOP         = 0 # uninitialized\n  BB_NONHEADER   = 1 # a regular BB\n  BB_REDUCIBLE   = 2 # reducible loop\n  BB_SELF        = 3 # single BB loop\n  BB_IRREDUCIBLE = 4 # irreducible loop\n  BB_DEAD        = 5 # a dead BB\n  BB_LAST        = 6 # Sentinel\n\n  # Marker for uninitialized nodes.\n  UNVISITED = -1\n\n  # Safeguard against pathologic algorithm behavior.\n  MAXNONBACKPREDS = (32 * 1024)\n\n  def initialize(@cfg : CFG, @lsg : LSG)\n  end\n\n  def ancestor?(w, v, last)\n    w <= v <= last[w]\n  end\n\n  def dfs(current_node, nodes, number, last, current)\n    nodes[current].init_node(current_node, current)\n    number[current_node] = current\n\n    lastid = current\n    current_node.out_edges.each do |target|\n      if number[target] == UNVISITED\n        lastid = dfs(target, nodes, number, last, lastid + 1)\n      end\n    end\n\n    last[number[current_node]] = lastid\n    lastid\n  end\n\n  def find_loops\n    start_node = @cfg.start_node\n    return 0 unless start_node\n    size = @cfg.num_nodes\n\n    non_back_preds = Array.new(size) { Set(Int32).new }\n    back_preds = Array.new(size) { Array(Int32).new }\n    number = {} of BasicBlock => Int32\n    header = Array.new(size, 0)\n    types = Array.new(size, 0)\n    last = Array.new(size, 0)\n    nodes = Array.new(size) { UnionFindNode.new }\n\n    # Step a:\n    #   - initialize all nodes as unvisited.\n    #   - depth-first traversal and numbering.\n    #   - unreached BB's are marked as dead.\n    #\n    @cfg.basic_block_map.each_value { |v| number[v] = UNVISITED }\n    dfs(start_node, nodes, number, last, 0)\n\n    # Step b:\n    #   - iterate over all nodes.\n    #\n    #   A backedge comes from a descendant in the DFS tree, and non-backedges\n    #   from non-descendants (following Tarjan).\n    #\n    #   - check incoming edges 'v' and add them to either\n    #     - the list of backedges (back_preds) or\n    #     - the list of non-backedges (non_back_preds)\n    #\n    size.times do |w|\n      header[w] = 0\n      types[w] = BB_NONHEADER\n\n      node_w = nodes[w].bb\n      if node_w\n        node_w.in_edges.each do |node_v|\n          v = number[node_v]\n          if v != UNVISITED\n            if ancestor?(w, v, last)\n              back_preds[w] << v\n            else\n              non_back_preds[w].add(v)\n            end\n          end\n        end\n      else\n        types[w] = BB_DEAD\n      end\n    end\n\n    # Start node is root of all other loops.\n    header[0] = 0\n\n    # Step c:\n    #\n    # The outer loop, unchanged from Tarjan. It does nothing except\n    # for those nodes which are the destinations of backedges.\n    # For a header node w, we chase backward from the sources of the\n    # backedges adding nodes to the set P, representing the body of\n    # the loop headed by w.\n    #\n    # By running through the nodes in reverse of the DFST preorder,\n    # we ensure that inner loop headers will be processed before the\n    # headers for surrounding loops.\n    #\n    (size - 1).downto(0) do |w|\n      # this is 'P' in Havlak's paper\n      node_pool = [] of UnionFindNode\n\n      node_w = nodes[w].bb\n      if node_w # dead BB\n\n        # Step d:\n        back_preds[w].each do |v|\n          if v != w\n            node_pool << nodes[v].find_set\n          else\n            types[w] = BB_SELF\n          end\n        end\n\n        # Copy node_pool to work_list.\n        #\n        work_list = node_pool.dup\n\n        types[w] = BB_REDUCIBLE if node_pool.size != 0\n\n        # work the list...\n        #\n        while !work_list.empty?\n          x = work_list.shift\n\n          # Step e:\n          #\n          # Step e represents the main difference from Tarjan's method.\n          # Chasing upwards from the sources of a node w's backedges. If\n          # there is a node y' that is not a descendant of w, w is marked\n          # the header of an irreducible loop, there is another entry\n          # into this loop that avoids w.\n          #\n\n          # The algorithm has degenerated. Break and\n          # return in this case.\n          #\n          non_back_size = non_back_preds[x.dfs_number].size\n          return 0 if non_back_size > MAXNONBACKPREDS\n\n          non_back_preds[x.dfs_number].each do |iter|\n            y = nodes[iter]\n            ydash = y.find_set\n\n            if !ancestor?(w, ydash.dfs_number, last)\n              types[w] = BB_IRREDUCIBLE\n              non_back_preds[w].add(ydash.dfs_number)\n            else\n              if ydash.dfs_number != w && !node_pool.includes?(ydash)\n                work_list << ydash\n                node_pool << ydash\n              end\n            end\n          end\n        end\n\n        # Collapse/Unionize nodes in a SCC to a single node\n        # For every SCC found, create a loop descriptor and link it in.\n        #\n        if (node_pool.size > 0) || (types[w] == BB_SELF)\n          l = @lsg.create_new_loop\n\n          l.header = node_w\n          l.reducible = types[w] != BB_IRREDUCIBLE\n\n          # At this point, one can set attributes to the loop, such as:\n          #\n          # the bottom node:\n          #    iter  = back_preds(w).begin();\n          #    loop bottom is: nodes(iter).node;\n          #\n          # the number of backedges:\n          #    back_preds(w).size()\n          #\n          # whether this loop is reducible:\n          #    types(w) != BB_IRREDUCIBLE\n          #\n          nodes[w].l = l\n\n          node_pool.each do |node|\n            # Add nodes to loop descriptor.\n            header[node.dfs_number] = w\n            node.union(nodes[w])\n\n            # Nested loops are not added, but linked together.\n            if node_l = node.l\n              node_l.parent = l\n            else\n              l.add_node(node.bb.not_nil!)\n            end\n          end\n\n          @lsg.add_loop(l)\n        end\n      end\n    end\n\n    @lsg.num_loops\n  end\nend\n\ndef build_diamond(start)\n  bb0 = start\n  BasicBlockEdge.add(TOP_CFG, bb0, bb0 + 1)\n  BasicBlockEdge.add(TOP_CFG, bb0, bb0 + 2)\n  BasicBlockEdge.add(TOP_CFG, bb0 + 1, bb0 + 3)\n  BasicBlockEdge.add(TOP_CFG, bb0 + 2, bb0 + 3)\n  bb0 + 3\nend\n\ndef build_connect(_start, _end)\n  BasicBlockEdge.add(TOP_CFG, _start, _end)\nend\n\ndef build_straight(start, n)\n  n.times do |i|\n    build_connect(start + i, start + i + 1)\n  end\n  start + n\nend\n\ndef build_base_loop(from)\n  header = build_straight(from, 1)\n  diamond1 = build_diamond(header)\n  d11 = build_straight(diamond1, 1)\n  diamond2 = build_diamond(d11)\n  footer = build_straight(diamond2, 1)\n  build_connect(diamond2, d11)\n  build_connect(diamond1, header)\n\n  build_connect(footer, from)\n  build_straight(footer, 1)\nend\n\nTOP_CFG = CFG.new\n\nputs \"Welcome to LoopTesterApp, Crystal edition\"\n\nputs \"Constructing Simple CFG...\"\n\nTOP_CFG.create_node(0) # top\nbuild_base_loop(0)\nTOP_CFG.create_node(1) # bottom\nbuild_connect(0, 2)\n\n# execute loop recognition 15000 times to force compilation\nputs \"15000 dummy loops\"\n15000.times do\n  HavlakLoopFinder.new(TOP_CFG, LSG.new).find_loops\nend\n\nputs \"Constructing CFG...\"\nn = 2\n\n10.times do |parlooptrees|\n  TOP_CFG.create_node(n + 1)\n  build_connect(2, n + 1)\n  n = n + 1\n  100.times do |i|\n    top = n\n    n = build_straight(n, 1)\n    25.times { n = build_base_loop(n) }\n\n    bottom = build_straight(n, 1)\n    build_connect(n, top)\n    n = bottom\n  end\n\n  build_connect(n, 1)\nend\n\nputs \"Performing Loop Recognition\\n1 Iteration\"\nloops = HavlakLoopFinder.new(TOP_CFG, LSG.new).find_loops\n\nputs \"Another 50 iterations...\"\n\nsum = 0\n50.times do |i|\n  print '.'\n  sum += HavlakLoopFinder.new(TOP_CFG, LSG.new).find_loops\nend\n\nputs \"\\nFound #{loops} loops (including artificial root node) (#{sum})\\n\"\n"
  },
  {
    "path": "samples/http_server.cr",
    "content": "# A very basic HTTP server\nrequire \"http/server\"\n\nserver = HTTP::Server.new do |context|\n  context.response.content_type = \"text/plain\"\n  context.response.print \"Hello world!\"\nend\n\naddress = server.bind_tcp 8080\nputs \"Listening on http://#{address}\"\nserver.listen\n"
  },
  {
    "path": "samples/impl.cr",
    "content": "class Foo\n  property lorem : Int32?\n\n  def foo\n    1\n  end\nend\n\nclass Bar\n  def foo\n    2\n  end\nend\n\ndef bar(o)\n  while false\n    o.foo\n  end\nend\n\ndef baz(o)\n  o.foo\nend\n\nputs bar(Foo.new)\nputs bar(Bar.new)\nputs baz(Foo.new)\n\nFoo.new.lorem\n\n# ./crystal tool implementations samples/impl.cr --cursor samples/impl.cr:17:8\n#\n# 2 implementations found\n# .../samples/impl.cr:4:3\n# .../samples/impl.cr:10:3\n\n# ./crystal tool implementations samples/impl.cr --cursor samples/impl.cr:22:5\n#\n# 1 implementation found\n# .../samples/impl.cr:4:3\n\n# ./crystal tool implementations samples/impl.cr --cursor samples/impl.cr:25:7\n#\n# 1 implementation found\n# .../samples/impl.cr:15:1\n\n# ./crystal tool implementations samples/impl.cr --cursor samples/impl.cr:27:3\n#\n# 1 implementation found\n# .../share/crystal/src/kernel.cr:369:1\n\n# ./crystal tool implementations samples/impl.cr --cursor samples/impl.cr:29:9\n#\n# 1 implementation found\n# .../samples/impl.cr:2:3\n#  ~> macro property: expanded macro: macro_139913700784656:636:13\n"
  },
  {
    "path": "samples/llvm/brainfuck.cr",
    "content": "# Ported from https://github.com/Wilfred/Brainfrack/blob/5a2f613f9e82bfd57be687aa6a67aca15d3d9861/llvm/compiler.cpp\n\nrequire \"llvm\"\n\nNUM_CELLS          = 30000\nCELL_SIZE_IN_BYTES =     1\n\nabstract class Instruction\n  abstract def compile(program, bb)\nend\n\nclass Increment < Instruction\n  def initialize(@amount : Int32)\n  end\n\n  def compile(program, bb)\n    builder = program.builder\n    builder.position_at_end bb\n\n    cell_index = builder.load program.ctx.int32, program.cell_index_ptr, \"cell_index\"\n    current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, \"current_cell_ptr\"\n\n    cell_val = builder.load program.cell_type, current_cell_ptr, \"cell_value\"\n    increment_amount = program.cell_type.const_int(@amount)\n    new_cell_val = builder.add cell_val, increment_amount, \"cell_value\"\n    builder.store new_cell_val, current_cell_ptr\n\n    bb\n  end\nend\n\nclass DataIncrement < Instruction\n  def initialize(@amount : Int32)\n  end\n\n  def compile(program, bb)\n    builder = program.builder\n    builder.position_at_end bb\n\n    cell_index = builder.load program.ctx.int32, program.cell_index_ptr, \"cell_index\"\n    increment_amount = program.ctx.int32.const_int(@amount)\n    new_cell_index = builder.add cell_index, increment_amount, \"new_cell_index\"\n\n    builder.store new_cell_index, program.cell_index_ptr\n\n    bb\n  end\nend\n\nclass Read < Instruction\n  def compile(program, bb)\n    builder = program.builder\n    builder.position_at_end bb\n\n    cell_index = builder.load program.ctx.int32, program.cell_index_ptr, \"cell_index\"\n    current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, \"current_cell_ptr\"\n\n    input_char = program.call_c_function \"getchar\", name: \"input_char\"\n    input_byte = builder.trunc input_char, program.ctx.int8, \"input_byte\"\n    builder.store input_byte, current_cell_ptr\n\n    bb\n  end\nend\n\nclass Write < Instruction\n  def compile(program, bb)\n    builder = program.builder\n    builder.position_at_end bb\n\n    cell_index = builder.load program.ctx.int32, program.cell_index_ptr, \"cell_index\"\n    current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, \"current_cell_ptr\"\n\n    cell_val = builder.load program.cell_type, current_cell_ptr, \"cell_value\"\n    cell_val_as_char = builder.sext cell_val, program.ctx.int32, \"cell_val_as_char\"\n\n    program.call_c_function \"putchar\", cell_val_as_char\n\n    bb\n  end\nend\n\nclass Loop < Instruction\n  def initialize(@body : Array(Instruction))\n  end\n\n  def compile(program, bb)\n    builder = program.builder\n    func = program.func\n\n    loop_header = func.basic_blocks.append \"loop_header\"\n\n    builder.position_at_end bb\n    builder.br loop_header\n\n    loop_body_block = func.basic_blocks.append \"loop_body\"\n    loop_after = func.basic_blocks.append \"loop_after\"\n\n    builder.position_at_end loop_header\n    cell_index = builder.load program.ctx.int32, program.cell_index_ptr, \"cell_index\"\n    current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, \"current_cell_ptr\"\n    cell_val = builder.load program.cell_type, current_cell_ptr, \"cell_value\"\n    zero = program.cell_type.const_int(0)\n    cell_val_is_zero = builder.icmp LLVM::IntPredicate::EQ, cell_val, zero\n\n    builder.cond cell_val_is_zero, loop_after, loop_body_block\n\n    @body.each do |instruction|\n      loop_body_block = instruction.compile(program, loop_body_block)\n    end\n\n    builder.position_at_end loop_body_block\n    builder.br loop_header\n\n    loop_after\n  end\nend\n\nclass Program\n  getter mod : LLVM::Module\n  getter ctx : LLVM::Context\n  getter builder : LLVM::Builder\n  getter instructions\n  getter cell_type : LLVM::Type\n  getter! cells_ptr : LLVM::Value\n  getter! cell_index_ptr : LLVM::Value\n  getter! func : LLVM::Function\n\n  @func_types = {} of String => LLVM::Type\n\n  def initialize(@instructions : Array(Instruction))\n    @ctx = LLVM::Context.new\n    @mod = @ctx.new_module(\"brainfuck\")\n    @builder = @ctx.new_builder\n\n    @cell_type = @ctx.int(CELL_SIZE_IN_BYTES * 8)\n  end\n\n  def self.new(source : String)\n    new source.chars\n  end\n\n  def self.new(source : Array(Char))\n    new parse(source, 0, source.size)\n  end\n\n  def self.parse(source, from, to)\n    program = [] of Instruction\n    i = from\n    while i < to\n      case source[i]\n      when '+'\n        program << Increment.new(1)\n      when '-'\n        program << Increment.new(-1)\n      when '>'\n        program << DataIncrement.new(1)\n      when '<'\n        program << DataIncrement.new(-1)\n      when ','\n        program << Read.new\n      when '.'\n        program << Write.new\n      when '['\n        matching_close_index = find_matching_close(source, i)\n        unless matching_close_index\n          abort \"Unmatched '[' at position #{i}\"\n        end\n        program << Loop.new(parse(source, i + 1, matching_close_index))\n        i = matching_close_index\n      when ']'\n        abort \"Unmatched ']' at position #{i}\"\n      else\n        # skip\n      end\n      i += 1\n    end\n    program\n  end\n\n  def self.find_matching_close(source, open_index)\n    open_count = 0\n    (open_index...source.size).each do |i|\n      case source[i]\n      when '['\n        open_count += 1\n      when ']'\n        open_count -= 1\n      end\n\n      if open_count == 0\n        return i\n      end\n    end\n    nil\n  end\n\n  def compile\n    declare_c_functions mod\n    @func = create_main mod\n    bb = func.basic_blocks.append \"entry\"\n    add_cells_init mod, bb\n    instructions.each do |instruction|\n      bb = instruction.compile(self, bb)\n    end\n    add_cells_cleanup mod, bb\n    mod\n  end\n\n  def declare_c_functions(mod)\n    declare_c_function mod, \"calloc\", [@ctx.int32, @ctx.int32], @ctx.void_pointer\n    declare_c_function mod, \"free\", [@ctx.void_pointer], @ctx.void\n    declare_c_function mod, \"putchar\", [@ctx.int32], @ctx.int32\n    declare_c_function mod, \"getchar\", ([] of LLVM::Type), @ctx.int32\n  end\n\n  def declare_c_function(mod, name, param_types, return_type)\n    func_type = LLVM::Type.function(param_types, return_type)\n    @func_types[name] = func_type\n    mod.functions.add name, func_type\n  end\n\n  def call_c_function(func_name, args = [] of LLVM::Value, name = \"\")\n    func = mod.functions[func_name]\n    @builder.call @func_types[func_name], func, args, name\n  end\n\n  def create_main(mod)\n    main = mod.functions.add \"main\", ([] of LLVM::Type), @ctx.int32\n    main.linkage = LLVM::Linkage::External\n    main\n  end\n\n  def add_cells_init(mod, bb)\n    builder.position_at_end bb\n\n    call_args = [@ctx.int32.const_int(NUM_CELLS), @ctx.int32.const_int(CELL_SIZE_IN_BYTES)]\n    @cells_ptr = call_c_function \"calloc\", call_args, \"cells\"\n\n    @cell_index_ptr = builder.alloca @ctx.int32, \"cell_index_ptr\"\n    zero = @ctx.int32.const_int(0)\n    builder.store zero, cell_index_ptr\n  end\n\n  def add_cells_cleanup(mod, bb)\n    builder.position_at_end bb\n\n    call_c_function \"free\", cells_ptr\n\n    zero = @ctx.int32.const_int(0)\n    builder.ret zero\n  end\nend\n\ndef get_output_name(filename)\n  if filename.ends_with?(\".bf\")\n    \"#{filename[0..filename.size - 4]}.ll\"\n  else\n    \"#{filename}.ll\"\n  end\nend\n\nfilename = ARGV.first?\nunless filename\n  abort \"Missing filename\"\nend\n\nunless File.file?(filename)\n  abort \"#{filename} is not a file\"\nend\n\nsource = File.read(filename)\nprogram = Program.new(source)\nmod = program.compile\n\noutput_name = get_output_name(filename)\nFile.open(output_name, \"w\") do |file|\n  mod.to_s(file)\nend\n"
  },
  {
    "path": "samples/mandelbrot.cr",
    "content": "def print_density(d)\n  if d > 8\n    print ' '\n  elsif d > 4\n    print '.'\n  elsif d > 2\n    print '*'\n  else\n    print '+'\n  end\nend\n\ndef mandelconverger(real, imag, iters, creal, cimag)\n  if iters > 255 || real*real + imag*imag >= 4\n    iters\n  else\n    mandelconverger real*real - imag*imag + creal, 2*real*imag + cimag, iters + 1, creal, cimag\n  end\nend\n\ndef mandelconverge(real, imag)\n  mandelconverger real, imag, 0, real, imag\nend\n\ndef mandelhelp(xmin, xmax, xstep, ymin, ymax, ystep)\n  ymin.step(to: ymax, by: ystep) do |y|\n    xmin.step(to: xmax, by: xstep) do |x|\n      print_density mandelconverge(x, y)\n    end\n    puts\n  end\nend\n\ndef mandel(realstart, imagstart, realmag, imagmag)\n  mandelhelp realstart, realstart + realmag*78, realmag, imagstart, imagstart + imagmag*40, imagmag\nend\n\nmandel -2.3, -1.3, 0.05, 0.07\n"
  },
  {
    "path": "samples/mandelbrot2.cr",
    "content": "require \"complex\"\n\ndef mandelbrot(a)\n  Iterator.of(a).first(100).reduce(a) { |z, c| z*z + c }\nend\n\n(1.0).step(to: -1, by: -0.05) do |y|\n  (-2.0).step(to: 0.5, by: 0.0315) do |x|\n    print mandelbrot(x + y.i).abs < 2 ? '*' : ' '\n  end\n  puts\nend\n"
  },
  {
    "path": "samples/matmul.cr",
    "content": "# Copied with little modifications from: https://github.com/attractivechaos/plb/blob/master/matmul/matmul_v1.rb\n\ndef matmul(a, b)\n  m = a.size\n  n = a[0].size\n  p = b[0].size\n  # transpose\n  b2 = Array.new(n) { Array.new(p, 0.0) }\n  (0...n).each do |i|\n    (0...p).each do |j|\n      b2[j][i] = b[i][j]\n    end\n  end\n  # multiplication\n  c = Array.new(m) { Array.new(p, 0.0) }\n  (0...m).each do |i|\n    (0...p).each do |j|\n      s = 0.0\n      ai, b2j = a[i], b2[j]\n      (0...n).each do |k|\n        s += ai[k] * b2j[k]\n      end\n      c[i][j] = s\n    end\n  end\n  c\nend\n\ndef matgen(n)\n  tmp = 1.0 / n / n\n  a = Array.new(n) { Array.new(n, 0.0) }\n  (0...n).each do |i|\n    (0...n).each do |j|\n      a[i][j] = tmp * (i - j) * (i + j)\n    end\n  end\n  a\nend\n\nn = (ARGV[0]? || 500).to_i\nn = n // 2 * 2\na = matgen(n)\nb = matgen(n)\nc = matmul(a, b)\nputs c[n // 2][n // 2]\n"
  },
  {
    "path": "samples/meteor.cr",
    "content": "# Translated from: https://gitlab.redox-os.org/redox-os/rust/blob/a59de37e99060162a2674e3ff45409ac73595c0e/src/test/bench/shootout-meteor.rs\n\nalias Masks = Array(Array(Array(UInt64)))\nalias Point = Tuple(Int32, Int32)\n\nclass MyIterator(T)\n  include Enumerable(T)\n\n  def initialize(@data : T, &@block : T -> T)\n  end\n\n  def each(&)\n    while true\n      yield @data\n      @data = @block.call(@data)\n    end\n  end\nend\n\ndef bo(offset) # bit offset\n  1_u64 << offset\nend\n\ndef bm(mask, offset) # bit mask\n  mask & bo(offset)\nend\n\ndef transform(piece, all)\n  i = MyIterator.new piece, &.map { |(y, x)| {x + y, -y} }\n  rots = i.first(all ? 6 : 3)\n  res = rots.flat_map do |cur_piece|\n    i2 = MyIterator.new cur_piece, &.map { |(y, x)| {x, y} }\n    i2.first(2)\n  end\n\n  res.map do |cur_piece|\n    dy, dx = cur_piece.min\n    cur_piece.map do |(y, x)|\n      {y - dy, x - dx}\n    end\n  end\nend\n\ndef mask(dy, dx, id, p)\n  m = bo(50 + id)\n  p.each do |(y, x)|\n    x2 = x + dx + (y + (dy % 2)) // 2\n    return if x2 < 0 || x2 > 4\n    y2 = y + dy\n    return if y2 < 0 || y2 > 9\n    m |= bo(y2 * 5 + x2)\n  end\n  m\nend\n\nPIECES = [\n  [{0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 3}],\n  [{0, 0}, {0, 2}, {0, 3}, {1, 0}, {1, 1}],\n  [{0, 0}, {0, 1}, {0, 2}, {1, 2}, {2, 1}],\n  [{0, 0}, {0, 1}, {0, 2}, {1, 1}, {2, 1}],\n  [{0, 0}, {0, 2}, {1, 0}, {1, 1}, {2, 1}],\n  [{0, 0}, {0, 1}, {0, 2}, {1, 1}, {1, 2}],\n  [{0, 0}, {0, 1}, {1, 1}, {1, 2}, {2, 1}],\n  [{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 2}],\n  [{0, 0}, {0, 1}, {0, 2}, {1, 2}, {1, 3}],\n  [{0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 2}],\n]\n\ndef make_masks\n  res = Masks.new\n  PIECES.each_with_index do |p, id|\n    trans = transform(p, id != 3)\n    cur_piece = [] of Array(UInt64)\n    10.times do |dy|\n      5.times do |dx|\n        cur_piece << trans.compact_map { |t| mask(dy, dx, id, t) }\n      end\n    end\n    res << cur_piece\n  end\n  res\nend\n\ndef board_unfeasible?(board : UInt64, masks : Masks)\n  coverable = board\n\n  (0...50).select { |i| bm(board, i) == 0 }.each do |i|\n    masks.each_with_index do |pos_masks, cur_id|\n      next if bm(board, 50 + cur_id) != 0\n      pos_masks[i].each do |cur_m|\n        coverable |= cur_m if cur_m & board == 0\n      end\n    end\n    return true if bm(coverable, i) == 0\n  end\n\n  coverable != (bo(60) - 1)\nend\n\ndef filter_masks(masks : Masks)\n  masks.map do |p|\n    p.map do |p2|\n      p2.select do |m|\n        !board_unfeasible?(m, masks)\n      end\n    end\n  end\nend\n\ndef get_id(m : UInt64)\n  10.times do |id|\n    return id.to_u8 if bm(m, id + 50) != 0\n  end\n  raise \"Does not have a valid identifier\"\nend\n\ndef to_utf8(raw_sol)\n  String.new(50) do |buf|\n    raw_sol.each do |m|\n      id = get_id(m)\n      50.times do |i|\n        buf[i] = '0'.ord.to_u8 + id if bm(m, i) != 0\n      end\n    end\n    {50, 50}\n  end\nend\n\ndef print_sol(str)\n  i = 0\n  str.each_byte do |c|\n    puts if i % 5 == 0\n    print ' ' if (i + 5) % 10 == 0\n    print ' '\n    print c.chr\n    i += 1\n  end\n  puts\nend\n\nclass SolutionNode\n  def initialize(@x : UInt64, @prev : SolutionNode?)\n  end\n\n  getter :x\n  getter :prev\n\n  def each(&)\n    yield @x\n    p = prev\n    while y = p\n      yield y.x\n      p = p.prev\n    end\n  end\nend\n\nclass Meteor\n  def initialize(@masks : Masks, @stop_after : Int32)\n    @nb = 0\n    @min = \"9\" * 50\n    @max = \"0\" * 50\n  end\n\n  property min : String\n  property max : String\n  property :nb\n\n  def handle_sol(cur)\n    @nb += 2\n    sol1 = to_utf8(cur)\n    sol2 = sol1.reverse\n    @min = {sol1, sol2, @min}.min\n    @max = {sol1, sol2, @max}.max\n    nb < @stop_after\n  end\n\n  def search(board, i, cur = nil)\n    while bm(board, i) != 0 && (i < 50)\n      i += 1\n    end\n\n    return handle_sol(cur) if i >= 50 && cur\n\n    (0...10).each do |id|\n      if bm(board, id + 50) == 0\n        @masks[id][i].each do |m|\n          if board & m == 0\n            return false if !search(board | m, i + 1, SolutionNode.new(m, cur))\n          end\n        end\n      end\n    end\n\n    true\n  end\nend\n\nstop_after = (ARGV[0]? || 2098).to_i\nmasks = filter_masks(make_masks)\ndata = Meteor.new(masks, stop_after)\ndata.search(0_u64, 0)\nputs \"#{data.nb} solutions found\"\nprint_sol(data.min)\nprint_sol(data.max)\nputs\n"
  },
  {
    "path": "samples/mt_gc_test.cr",
    "content": "# Experiment to stress the GC with multi-threading.\n# The experiment manually allocates multiple times a queue of fibers\n# on a list of worker threads. The main thread does the orchestration.\n# The fibers will grow and shrink their stack size by doing recursion.\n# In each level of the stack an amount of dummy objects are allocated.\n# These objects will be released by the GC eventually.\n\nrequire \"option_parser\"\nrequire \"benchmark\"\n\nlib LibC\n  fun fflush(b : Void*)\nend\n\nclass Foo\n  @@collections = Atomic(Int32).new(0)\n\n  @data = StaticArray(Int32, 8).new(0)\n\n  def finalize\n    @@collections.add(1)\n  end\n\n  def self.collections\n    @@collections.get\n  end\nend\n\nclass Context\n  enum State\n    Wait\n    Run\n  end\n\n  property expected_depth : Int32 = 0\n  @worker_fibers = Array(Fiber).new(0)\n\n  def initialize(@fibers : Int32, @threads : Int32, @log : Bool)\n    @fibers_reached = Atomic(Int32).new(0)\n    @threads_reached = Atomic(Int32).new(0)\n    @fiber_depths = Array(Int32).new(@fibers, 0)\n    @pending_fibers_queues = Array(Array(Fiber)).new(@threads) { Array(Fiber).new }\n    @threads_state = Atomic(State).new(State::Wait)\n\n    # Create worker fibers but do not start them yet.\n    # Each fiber will try to reach the `expected_depth` value\n    # by increasing or decreasing its callstack.\n    @fibers.times do |index|\n      @worker_fibers << Fiber.new(\"f:#{index}\") do\n        Context.fiber_run(self, index, 1)\n      end\n    end\n\n    # Create worker threads.\n    # they will perform operations when `@threads_state == :run`\n    # otherwise they will remain in a tight busy loop.\n    # See Context#create_thread\n    @threads.times { |index| create_thread(index) }\n  end\n\n  def self.fiber_run(context, fiber_index, depth)\n    context.set_fiber_depth(fiber_index, depth)\n\n    # allocate a bunch of objects in the stack\n    # some should be released fast\n    10.times do\n      foo = Foo.new\n    end\n    foo = Foo.new\n\n    # increase/decrease stack depending on the expected_depth\n    # when reached, notify and yield control\n    while true\n      if context.expected_depth < depth\n        return\n      elsif context.expected_depth > depth\n        fiber_run(context, fiber_index, depth + 1)\n      else\n        context.notify_depth_reached\n        context.yield\n      end\n    end\n  end\n\n  def set_fiber_depth(index, depth)\n    @fiber_depths[index] = depth\n  end\n\n  def run_until_depth(phase, depth)\n    # make all fibers reach a specific depth\n    log \"#{phase}: expected_depth: #{depth}\"\n\n    @expected_depth = depth\n    @fibers_reached.set(0)\n    @threads_reached.set(0)\n\n    # allocate fibers on each thread queue.\n    @pending_fibers_queues.each &.clear\n    @worker_fibers.dup.shuffle!.each_with_index do |f, index|\n      @pending_fibers_queues[index % @threads] << f\n    end\n\n    @threads_state.set(State::Run)\n\n    # spin wait for all fibers to finish\n    while @fibers_reached.get < @fibers\n    end\n    log \"All fibers_reached!\"\n\n    @threads_state.set(State::Wait)\n\n    # spin wait for threads to finish the round\n    while @threads_reached.get < @threads\n    end\n    log \"All threads_reached!\"\n  end\n\n  def notify_depth_reached\n    @fibers_reached.add(1)\n  end\n\n  def yield\n    Thread.current.main_fiber.resume\n  end\n\n  def pick_and_resume_fiber(queue_index)\n    fiber = @pending_fibers_queues[queue_index].shift?\n\n    if fiber\n      fiber.resume\n      true\n    else\n      false\n    end\n  end\n\n  def gc_stats\n    log \"GC.stats: #{GC.stats}\"\n    log \"Foo.collections: #{Foo.collections}\"\n  end\n\n  def create_thread(queue_index)\n    Thread.new do\n      # this loop will iterate once per #run_until_depth\n      while true\n        # wait for queues to be ready\n        while @threads_state.get != State::Run\n        end\n\n        # consume the queue until empty\n        while pick_and_resume_fiber(queue_index)\n        end\n\n        # wait for all worker threads to finish\n        while @threads_state.get != State::Wait\n        end\n\n        # sync all workers threads end of loop\n        @threads_reached.add(1)\n      end\n    end\n  end\n\n  def log(s)\n    if @log\n      t = Thread.current.to_s rescue \"Unknown\"\n      f = Fiber.current.to_s rescue \"Unknown\"\n      LibC.printf(\"%s::%s >>> %s\\n\", t, f, s)\n      LibC.fflush(nil)\n    end\n    s\n  end\nend\n\ndef run(threads_num, fibers_num, loops_num, log)\n  # Specify the number of fibers and threads to use\n  context = Context.new(fibers: fibers_num, threads: threads_num, log: log)\n\n  (1..loops_num).each do |i|\n    context.run_until_depth \"Phase #{i}.1\", 40\n    context.run_until_depth \"Phase #{i}.2\", 5\n\n    context.gc_stats\n\n    context.run_until_depth \"Phase #{i}.3\", 50\n    context.run_until_depth \"Phase #{i}.4\", 5\n\n    context.gc_stats\n\n    context.run_until_depth \"Phase #{i}.5\", 10\n\n    context.gc_stats\n  end\n\n  context.log \"Done\"\nend\n\nenum Mode\n  Run\n  Ips\n  Measure\nend\n\nthreads_num = 4\nfibers_num = 1_000\nloops_num = 20\nmode : Mode = :run\n\nOptionParser.parse do |parser|\n  parser.on(\"-i\", \"--ips\", \"Benchmark with ips\") { mode = :ips }\n  parser.on(\"-m\", \"--measure\", \"Benchmark with measure\") { mode = :measure }\n  parser.on(\"-f FIBERS\", \"--fibers=FIBERS\", \"Specifies the number of fibers\") { |v| fibers_num = v.to_i }\n  parser.on(\"-t THREADS\", \"--threads=THREADS\", \"Specifies the number of threads\") { |v| threads_num = v.to_i }\n  parser.on(\"-l LOOPS\", \"--loops=LOOPS\", \"Specifies the number of loops\") { |v| loops_num = v.to_i }\n  parser.on(\"-h\", \"--help\", \"Show this help\") {\n    puts parser\n    exit(0)\n  }\n  parser.invalid_option do |flag|\n    STDERR.puts \"ERROR: #{flag} is not a valid option.\"\n    STDERR.puts parser\n    exit(1)\n  end\nend\n\ncase mode\nwhen .run?\n  run(threads_num, fibers_num, loops_num, true)\nwhen .ips?\n  Benchmark.ips do |x|\n    x.report(\"run\") { run(threads_num, fibers_num, loops_num, false) }\n  end\nwhen .measure?\n  puts Benchmark.measure { run(threads_num, fibers_num, loops_num, false) }\nend\n"
  },
  {
    "path": "samples/nbodies.cr",
    "content": "# Copied with little modifications from: https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/nbody-yarv-2.html\n\nSOLAR_MASS    = 4 * Math::PI**2\nDAYS_PER_YEAR = 365.24\n\nclass Planet\n  property x : Float64\n  property y : Float64\n  property z : Float64\n  property vx : Float64\n  property vy : Float64\n  property vz : Float64\n  property mass : Float64\n\n  def initialize(@x, @y, @z, vx, vy, vz, mass)\n    @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR\n    @mass = mass * SOLAR_MASS\n  end\n\n  def move_from_i(bodies, nbodies, dt, i)\n    while i < nbodies\n      b2 = bodies[i]\n      dx = @x - b2.x\n      dy = @y - b2.y\n      dz = @z - b2.z\n\n      distance = Math.sqrt(dx * dx + dy * dy + dz * dz)\n      mag = dt / (distance * distance * distance)\n      b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag\n\n      @vx -= dx * b2_mass_mag\n      @vy -= dy * b2_mass_mag\n      @vz -= dz * b2_mass_mag\n      b2.vx += dx * b_mass_mag\n      b2.vy += dy * b_mass_mag\n      b2.vz += dz * b_mass_mag\n      i += 1\n    end\n\n    @x += dt * @vx\n    @y += dt * @vy\n    @z += dt * @vz\n  end\nend\n\ndef energy(bodies)\n  e = 0.0\n  nbodies = bodies.size\n\n  0.upto(nbodies - 1) do |i|\n    b = bodies[i]\n    e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz)\n    (i + 1).upto(nbodies - 1) do |j|\n      b2 = bodies[j]\n      dx = b.x - b2.x\n      dy = b.y - b2.y\n      dz = b.z - b2.z\n      distance = Math.sqrt(dx * dx + dy * dy + dz * dz)\n      e -= (b.mass * b2.mass) / distance\n    end\n  end\n  e\nend\n\ndef offset_momentum(bodies)\n  px, py, pz = 0.0, 0.0, 0.0\n\n  bodies.each do |b|\n    m = b.mass\n    px += b.vx * m\n    py += b.vy * m\n    pz += b.vz * m\n  end\n\n  b = bodies[0]\n  b.vx = -px / SOLAR_MASS\n  b.vy = -py / SOLAR_MASS\n  b.vz = -pz / SOLAR_MASS\nend\n\nBODIES = [\n  # sun\n  Planet.new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0),\n\n  # jupiter\n  Planet.new(\n    4.84143144246472090e+00,\n    -1.16032004402742839e+00,\n    -1.03622044471123109e-01,\n    1.66007664274403694e-03,\n    7.69901118419740425e-03,\n    -6.90460016972063023e-05,\n    9.54791938424326609e-04),\n\n  # saturn\n  Planet.new(\n    8.34336671824457987e+00,\n    4.12479856412430479e+00,\n    -4.03523417114321381e-01,\n    -2.76742510726862411e-03,\n    4.99852801234917238e-03,\n    2.30417297573763929e-05,\n    2.85885980666130812e-04),\n\n  # uranus\n  Planet.new(\n    1.28943695621391310e+01,\n    -1.51111514016986312e+01,\n    -2.23307578892655734e-01,\n    2.96460137564761618e-03,\n    2.37847173959480950e-03,\n    -2.96589568540237556e-05,\n    4.36624404335156298e-05),\n\n  # neptune\n  Planet.new(\n    1.53796971148509165e+01,\n    -2.59193146099879641e+01,\n    1.79258772950371181e-01,\n    2.68067772490389322e-03,\n    1.62824170038242295e-03,\n    -9.51592254519715870e-05,\n    5.15138902046611451e-05),\n]\n\nif ARGV.size != 1\n  abort \"Usage: nbodies n\"\nend\n\nn = ARGV[0].to_i\n\noffset_momentum(BODIES)\n\nputs energy(BODIES)\n\nnbodies = BODIES.size\ndt = 0.01\n\nn.times do\n  i = 0\n  while i < nbodies\n    b = BODIES[i]\n    b.move_from_i(BODIES, nbodies, dt, i + 1)\n    i += 1\n  end\nend\n\nputs energy(BODIES)\n"
  },
  {
    "path": "samples/neural_net.cr",
    "content": "# Copied with little modifications from: https://github.com/jruby/rubybench/blob/master/time/bench_neural_net.rb\n\nclass Synapse\n  property weight : Float64\n  property prev_weight : Float64\n  property :source_neuron\n  property :dest_neuron\n\n  def initialize(@source_neuron : Neuron, @dest_neuron : Neuron)\n    @prev_weight = @weight = rand * 2 - 1\n  end\nend\n\nclass Neuron\n  LEARNING_RATE = 1.0\n  MOMENTUM      = 0.3\n\n  property :synapses_in\n  property :synapses_out\n  property threshold : Float64\n  property prev_threshold : Float64\n  property :error\n  property :output\n\n  def initialize\n    @prev_threshold = @threshold = rand * 2 - 1\n    @synapses_in = [] of Synapse\n    @synapses_out = [] of Synapse\n    @output = 0.0\n    @error = 0.0\n  end\n\n  def calculate_output\n    activation = synapses_in.reduce(0.0) do |sum, synapse|\n      sum + synapse.weight * synapse.source_neuron.output\n    end\n    activation -= threshold\n\n    @output = 1.0 / (1.0 + Math.exp(-activation))\n  end\n\n  def derivative\n    output * (1 - output)\n  end\n\n  def output_train(rate, target)\n    @error = (target - output) * derivative\n    update_weights(rate)\n  end\n\n  def hidden_train(rate)\n    @error = synapses_out.reduce(0.0) do |sum, synapse|\n      sum + synapse.prev_weight * synapse.dest_neuron.error\n    end * derivative\n    update_weights(rate)\n  end\n\n  def update_weights(rate)\n    synapses_in.each do |synapse|\n      temp_weight = synapse.weight\n      synapse.weight += (rate * LEARNING_RATE * error * synapse.source_neuron.output) + (MOMENTUM * (synapse.weight - synapse.prev_weight))\n      synapse.prev_weight = temp_weight\n    end\n    temp_threshold = threshold\n    @threshold += (rate * LEARNING_RATE * error * -1) + (MOMENTUM * (threshold - prev_threshold))\n    @prev_threshold = temp_threshold\n  end\nend\n\nclass NeuralNetwork\n  @input_layer : Array(Neuron)\n  @hidden_layer : Array(Neuron)\n  @output_layer : Array(Neuron)\n\n  def initialize(inputs, hidden, outputs)\n    @input_layer = (1..inputs).map { Neuron.new }\n    @hidden_layer = (1..hidden).map { Neuron.new }\n    @output_layer = (1..outputs).map { Neuron.new }\n\n    @input_layer.each_cartesian(@hidden_layer) do |source, dest|\n      synapse = Synapse.new(source, dest)\n      source.synapses_out << synapse\n      dest.synapses_in << synapse\n    end\n    @hidden_layer.each_cartesian(@output_layer) do |source, dest|\n      synapse = Synapse.new(source, dest)\n      source.synapses_out << synapse\n      dest.synapses_in << synapse\n    end\n  end\n\n  def train(inputs, targets)\n    feed_forward(inputs)\n\n    @output_layer.zip(targets) do |neuron, target|\n      neuron.output_train(0.3, target)\n    end\n    @hidden_layer.each do |neuron|\n      neuron.hidden_train(0.3)\n    end\n  end\n\n  def feed_forward(inputs)\n    @input_layer.zip(inputs) do |neuron, input|\n      neuron.output = input.to_f64\n    end\n    @hidden_layer.each do |neuron|\n      neuron.calculate_output if neuron\n    end\n    @output_layer.each do |neuron|\n      neuron.calculate_output if neuron\n    end\n  end\n\n  def current_outputs\n    @output_layer.map do |neuron|\n      neuron.output\n    end\n  end\nend\n\n(ARGV[0]? || 5).to_i.times do\n  xor = NeuralNetwork.new(2, 10, 1)\n\n  10000.times do\n    xor.train([0, 0], [0])\n    xor.train([1, 0], [1])\n    xor.train([0, 1], [1])\n    xor.train([1, 1], [0])\n  end\n\n  xor.feed_forward([0, 0])\n  puts xor.current_outputs\n  xor.feed_forward([0, 1])\n  puts xor.current_outputs\n  xor.feed_forward([1, 0])\n  puts xor.current_outputs\n  xor.feed_forward([1, 1])\n  puts xor.current_outputs\nend\n"
  },
  {
    "path": "samples/noise.cr",
    "content": "# Perlin noise benchmark: https://github.com/nsf/pnoise\n\nrecord Vec2, x : Float64, y : Float64\n\ndef lerp(a, b, v)\n  a * (1.0 - v) + b * v\nend\n\ndef smooth(v)\n  v * v * (3.0 - 2.0 * v)\nend\n\ndef random_gradient\n  v = rand * Math::PI * 2.0\n  Vec2.new(Math.cos(v), Math.sin(v))\nend\n\ndef gradient(orig, grad, p)\n  sp = Vec2.new(p.x - orig.x, p.y - orig.y)\n  grad.x * sp.x + grad.y * sp.y\nend\n\nstruct Noise2DContext\n  def initialize\n    @rgradients = StaticArray(Vec2, 256).new { random_gradient }\n    @permutations = StaticArray(Int32, 256).new { |i| i }\n    @permutations.shuffle!\n  end\n\n  def get_gradient(x, y)\n    idx = @permutations[x & 255] + @permutations[y & 255]\n    @rgradients[idx & 255]\n  end\n\n  def get_gradients(x, y)\n    x0f = x.floor\n    y0f = y.floor\n    x0 = x0f.to_i\n    y0 = y0f.to_i\n    x1 = x0 + 1\n    y1 = y0 + 1\n\n    {\n      {\n        get_gradient(x0, y0),\n        get_gradient(x1, y0),\n        get_gradient(x0, y1),\n        get_gradient(x1, y1),\n      },\n      {\n        Vec2.new(x0f + 0.0, y0f + 0.0),\n        Vec2.new(x0f + 1.0, y0f + 0.0),\n        Vec2.new(x0f + 0.0, y0f + 1.0),\n        Vec2.new(x0f + 1.0, y0f + 1.0),\n      },\n    }\n  end\n\n  def get(x, y)\n    p = Vec2.new(x, y)\n    gradients, origins = get_gradients(x, y)\n    v0 = gradient(origins[0], gradients[0], p)\n    v1 = gradient(origins[1], gradients[1], p)\n    v2 = gradient(origins[2], gradients[2], p)\n    v3 = gradient(origins[3], gradients[3], p)\n    fx = smooth(x - origins[0].x)\n    vx0 = lerp(v0, v1, fx)\n    vx1 = lerp(v2, v3, fx)\n    fy = smooth(y - origins[0].y)\n    lerp(vx0, vx1, fy)\n  end\nend\n\nsymbols = [' ', '░', '▒', '▓', '█', '█']\npixels = Array.new(256) { Array.new(256, 0.0) }\n\nn2d = Noise2DContext.new\n\n100.times do |i|\n  256.times do |y|\n    256.times do |x|\n      v = n2d.get(x * 0.1, (y + (i * 128)) * 0.1) * 0.5 + 0.5\n      pixels[y][x] = v\n    end\n  end\nend\n\n256.times do |y|\n  256.times do |x|\n    v = pixels[y][x]\n    print(symbols[(v / 0.2).to_i])\n  end\n  puts\nend\n"
  },
  {
    "path": "samples/pig.cr",
    "content": "# Translated from Go: http://golang.org/doc/codewalk/functions/\n\nWin            = 100 # The winning score in a game of Pig\nGamesPerSeries =  10 # The number of games per series to simulate\n\n# A score includes scores accumulated in previous turns for each player,\n# as well as the points scored by the current player in this turn.\nrecord Score,\n  player : Int32,\n  opponent : Int32,\n  this_turn : Int32\n\n# roll returns the {result, turn_is_over} outcome of simulating a die roll.\n# If the roll value is 1, then this_turn score is abandoned, and the players'\n# roles swap.  Otherwise, the roll value is added to this_turn.\ndef roll(s)\n  outcome = rand 1..6\n  if outcome == 1\n    {Score.new(s.opponent, s.player, 0), true}\n  else\n    {Score.new(s.player, s.opponent, outcome + s.this_turn), false}\n  end\nend\n\n# stay returns the {result, turn_is_over} outcome of staying.\n# this_turn score is added to the player's score, and the players' roles swap.\ndef stay(s)\n  {Score.new(s.opponent, s.player + s.this_turn, 0), true}\nend\n\n# stay_at_k returns a strategy that rolls until this_turn is at least k, then stays.\ndef stay_at_k(k)\n  ->(s : Score) do\n    if s.this_turn >= k\n      ->stay(Score)\n    else\n      ->roll(Score)\n    end\n  end\nend\n\n# play simulates a Pig game and returns the winner (0 or 1).\ndef play(strategy0, strategy1)\n  strategies = {strategy0, strategy1}\n  s = Score.new(0, 0, 0)\n  turn_is_over = false\n  current_player = rand(2)\n  while s.player + s.this_turn < Win\n    action = strategies[current_player].call(s)\n    s, turn_is_over = action.call(s)\n    if turn_is_over\n      current_player = (current_player + 1) % 2\n    end\n  end\n  current_player\nend\n\n# roundRobin simulates a series of games between every pair of strategies.\ndef round_robin(strategies)\n  wins = Array.new(strategies.size, 0)\n  (0...strategies.size).each do |i|\n    (i + 1...strategies.size).each do |j|\n      (0...GamesPerSeries).each do |k|\n        winner = play strategies[i], strategies[j]\n        if winner == 0\n          wins[i] += 1\n        else\n          wins[j] += 1\n        end\n      end\n    end\n  end\n\n  games_per_strategy = GamesPerSeries * (strategies.size - 1)\n  {wins, games_per_strategy}\nend\n\n# ratio_string takes a list of integer values and returns a string that lists\n# each value and its percentage of the sum of all values.\n# e.g., ratios(1, 2, 3) = \"1/6 (16.7%), 2/6 (33.3%), 3/6 (50.0%)\"\ndef ratio_string(vals)\n  total = vals.sum\n  vals.map do |val|\n    pct = ((100 * val.to_f / total.to_f) * 10).to_i / 10.0\n    \"#{val}/#{total} %#{pct}\"\n  end.join \", \"\nend\n\nstrategies = Array.new(Win) { |k| stay_at_k(k + 1) }\nwins, games = round_robin strategies\n\nstrategies.each_with_index do |strategy, k|\n  puts \"Wins, losses staying at k = #{k + 1}: #{ratio_string({wins[k], games - wins[k]})}\"\nend\n"
  },
  {
    "path": "samples/pretty_json.cr",
    "content": "# JSON pretty printer\n# ~~~~~~~~~~~~~~~~~~~\n#\n# Reads JSON from STDIN and outputs it formatted and colored to STDOUT.\n#\n# Usage: echo '[1, {\"two\": \"three\"}, false]' | pretty_json\n\nrequire \"json\"\nrequire \"colorize\"\n\nclass PrettyPrinter\n  def initialize(@input : IO, @output : IO)\n    @pull = JSON::PullParser.new @input\n    @indent = 0\n  end\n\n  def print\n    read_any\n  end\n\n  def read_any\n    case @pull.kind\n    when .null?\n      Colorize.with.bold.surround(@output) do\n        @pull.read_null.to_json(@output)\n      end\n    when .bool?\n      Colorize.with.light_blue.surround(@output) do\n        @pull.read_bool.to_json(@output)\n      end\n    when .int?\n      Colorize.with.red.surround(@output) do\n        @pull.read_int.to_json(@output)\n      end\n    when .float?\n      Colorize.with.red.surround(@output) do\n        @pull.read_float.to_json(@output)\n      end\n    when .string?\n      Colorize.with.yellow.surround(@output) do\n        @pull.read_string.to_json(@output)\n      end\n    when .begin_array?\n      read_array\n    when .begin_object?\n      read_object\n    when .eof?\n      # We are done\n    when .end_array?, .end_object?\n      raise \"Bug: Shouldn't happen\"\n    end\n  end\n\n  def read_array\n    print \"[\\n\"\n    @indent += 1\n    i = 0\n    @pull.read_array do\n      if i > 0\n        print ','\n        print '\\n' if @indent > 0\n      end\n      print_indent\n      read_any\n      i += 1\n    end\n    @indent -= 1\n    print '\\n'\n    print_indent\n    print ']'\n  end\n\n  def read_object\n    print \"{\\n\"\n    @indent += 1\n    i = 0\n    @pull.read_object do |key|\n      if i > 0\n        print ','\n        print '\\n' if @indent > 0\n      end\n      print_indent\n      Colorize.with.cyan.surround(@output) do\n        key.to_json(@output)\n      end\n      print \": \"\n      read_any\n      i += 1\n    end\n    @indent -= 1\n    print '\\n'\n    print_indent\n    print '}'\n  end\n\n  def print_indent\n    @indent.times { @output << \"  \" }\n  end\n\n  def print(value)\n    @output << value\n  end\nend\n\nprinter = PrettyPrinter.new(STDIN, STDOUT)\nprinter.print\nSTDOUT.puts\n"
  },
  {
    "path": "samples/quine.cr",
    "content": "s = \"s = @; puts s.sub(\\\"@\\\", s.dump)\"; puts s.sub(\"@\", s.dump)\n"
  },
  {
    "path": "samples/red_black_tree.cr",
    "content": "# Copied with little modifications from: https://github.com/rubinius/rubinius-benchmark/blob/cf4a2468f46d23cc300815afabc8150609383d6c/real_world/bench_red_black_tree.rb\n\nclass RedBlackTree\n  class Node\n    enum Color\n      Red\n      Black\n    end\n\n    property color : Color\n    property key : Int32\n    property! :left\n    property! :right\n    property! parent : self\n\n    def initialize(@key, @color : Color = :red)\n      @left = @right = @parent = NilNode.instance\n    end\n\n    def black?\n      color.black?\n    end\n\n    def red?\n      color.red?\n    end\n\n    def nil_node?\n      false\n    end\n  end\n\n  class NilNode < Node\n    def self.instance\n      @@instance ||= RedBlackTree::NilNode.new\n    end\n\n    def initialize\n      @key = 0\n      @color = :black\n      @left = @right = @parent = self\n    end\n\n    def nil_node?\n      true\n    end\n  end\n\n  property root : Node\n  property :size\n\n  def initialize\n    @root = NilNode.instance\n    @size = 0\n  end\n\n  def add(key)\n    insert(Node.new(key))\n  end\n\n  def insert(x)\n    insert_helper(x)\n\n    x.color = :red\n    while x != root && x.parent.red?\n      if x.parent == x.parent.parent.left\n        y = x.parent.parent.right\n        if !y.nil_node? && y.red?\n          x.parent.color = :black\n          y.color = :black\n          x.parent.parent.color = :red\n          x = x.parent.parent\n        else\n          if x == x.parent.right\n            x = x.parent\n            left_rotate(x)\n          end\n          x.parent.color = :black\n          x.parent.parent.color = :red\n          right_rotate(x.parent.parent)\n        end\n      else\n        y = x.parent.parent.left\n        if !y.nil_node? && y.red?\n          x.parent.color = :black\n          y.color = :black\n          x.parent.parent.color = :red\n          x = x.parent.parent\n        else\n          if x == x.parent.left\n            x = x.parent\n            right_rotate(x)\n          end\n          x.parent.color = :black\n          x.parent.parent.color = :red\n          left_rotate(x.parent.parent)\n        end\n      end\n    end\n    root.color = :black\n  end\n\n  def <<(x)\n    insert(x)\n  end\n\n  def delete(z)\n    y = (z.left.nil_node? || z.right.nil_node?) ? z : successor(z)\n    x = y.left.nil_node? ? y.right : y.left\n    x.parent = y.parent\n\n    if y.parent.nil_node?\n      self.root = x\n    else\n      if y == y.parent.left\n        y.parent.left = x\n      else\n        y.parent.right = x\n      end\n    end\n\n    z.key = y.key if y != z\n\n    if y.black?\n      delete_fixup(x)\n    end\n\n    self.size -= 1\n    y\n  end\n\n  def minimum(x = root)\n    while !x.left.nil_node?\n      x = x.left\n    end\n    x\n  end\n\n  def maximum(x = root)\n    while !x.right.nil_node?\n      x = x.right\n    end\n    x\n  end\n\n  def successor(x)\n    if !x.right.nil_node?\n      return minimum(x.right)\n    end\n    y = x.parent\n    while !y.nil_node? && x == y.right\n      x = y\n      y = y.parent\n    end\n    y\n  end\n\n  def predecessor(x)\n    if !x.left.nil_node?\n      return maximum(x.left)\n    end\n    y = x.parent\n    while !y.nil_node? && x == y.left\n      x = y\n      y = y.parent\n    end\n    y\n  end\n\n  def inorder_walk(&)\n    x = self.minimum\n    while !x.nil_node?\n      yield x.key\n      x = successor(x)\n    end\n  end\n\n  def each(x = root, &)\n    inorder_walk(x) { |k| yield k }\n  end\n\n  def reverse_inorder_walk(&)\n    x = self.maximum\n    while !x.nil_node?\n      yield x.key\n      x = predecessor(x)\n    end\n  end\n\n  def reverse_each(x = root, &)\n    reverse_inorder_walk(x) { |k| yield k }\n  end\n\n  def search(key, x = root)\n    while !x.nil_node? && x.key != key\n      x = (key < x.key) ? x.left : x.right\n    end\n    x\n  end\n\n  def empty?\n    self.root.nil_node?\n  end\n\n  def black_height(x = root)\n    height = 0\n    while !x.nil_node?\n      x = x.left\n      height += 1 if x.nil_node? || x.black?\n    end\n    height\n  end\n\n  private def left_rotate(x)\n    raise \"x.right is nil!\" if x.right.nil_node?\n    y = x.right\n    x.right = y.left\n    y.left.parent = x if !y.left.nil_node?\n    y.parent = x.parent\n    if x.parent.nil_node?\n      self.root = y\n    else\n      if x == x.parent.left\n        x.parent.left = y\n      else\n        x.parent.right = y\n      end\n    end\n    y.left = x\n    x.parent = y\n  end\n\n  private def right_rotate(x)\n    raise \"x.left is nil!\" if x.left.nil_node?\n    y = x.left\n    x.left = y.right\n    y.right.parent = x if !y.right.nil_node?\n    y.parent = x.parent\n    if x.parent.nil_node?\n      self.root = y\n    else\n      if x == x.parent.left\n        x.parent.left = y\n      else\n        x.parent.right = y\n      end\n    end\n    y.right = x\n    x.parent = y\n  end\n\n  private def insert_helper(z)\n    y = NilNode.instance\n    x = root\n    while !x.nil_node?\n      y = x\n      x = (z.key < x.key) ? x.left : x.right\n    end\n    z.parent = y\n    if y.nil_node?\n      self.root = z\n    else\n      z.key < y.key ? y.left = z : y.right = z\n    end\n    self.size += 1\n  end\n\n  private def delete_fixup(x)\n    while x != root && x.black?\n      if x == x.parent.left\n        w = x.parent.right\n        if w.red?\n          w.color = :black\n          x.parent.color = :red\n          left_rotate(x.parent)\n          w = x.parent.right\n        end\n        if w.left.black? && w.right.black?\n          w.color = :red\n          x = x.parent\n        else\n          if w.right.black?\n            w.left.color = :black\n            w.color = :red\n            right_rotate(w)\n            w = x.parent.right\n          end\n          w.color = x.parent.color\n          x.parent.color = :black\n          w.right.color = :black\n          left_rotate(x.parent)\n          x = root\n        end\n      else\n        w = x.parent.left\n        if w.red?\n          w.color = :black\n          x.parent.color = :red\n          right_rotate(x.parent)\n          w = x.parent.left\n        end\n        if w.right.black? && w.left.black?\n          w.color = :red\n          x = x.parent\n        else\n          if w.left.black?\n            w.right.color = :black\n            w.color = :red\n            left_rotate(w)\n            w = x.parent.left\n          end\n          w.color = x.parent.color\n          x.parent.color = :black\n          w.left.color = :black\n          right_rotate(x.parent)\n          x = root\n        end\n      end\n    end\n    x.color = :black\n  end\nend\n\nclass RedBlackTreeRunner\n  property :tree\n\n  def initialize(n = 10_000)\n    @n = n\n\n    random = Random.new(1234) # repeatable random seq\n    @a1 = Array(Int32).new(n) { random.rand(99_999) }\n\n    random = Random.new(4321) # repeatable random seq\n    @a2 = Array(Int32).new(n) { random.rand(99_999) }\n\n    @tree = RedBlackTree.new\n  end\n\n  def run_delete\n    @tree = RedBlackTree.new\n    @n.times { |i| @tree.add(i) }\n    @n.times { @tree.delete(@tree.root) }\n    tree.size\n  end\n\n  def run_add\n    @tree = RedBlackTree.new\n    @a1.each { |e| @tree.add(e) }\n    tree.size\n  end\n\n  def run_search\n    s = c = 0\n    @a2.each { |e| c += 1; s += @tree.search(e).key % 3 }\n    [s, c]\n  end\n\n  def run_inorder_walk\n    s = 0\n    c = 0\n    @tree.inorder_walk { |key| c += 1; s += key % 3 }\n    [s, c]\n  end\n\n  def run_reverse_inorder_walk\n    s = 0\n    c = 0\n    @tree.reverse_inorder_walk { |key| c += 1; s += key % 3 }\n    [s, c]\n  end\n\n  def run_min\n    s = 0\n    @n.times { s += @tree.minimum.key }\n    s\n  end\n\n  def run_max\n    s = 0_u64\n    @n.times { s += @tree.maximum.key }\n    s\n  end\nend\n\ndef bench(name, n = 1, &)\n  start = Time.monotonic\n  print \"#{name}: \"\n  res = nil\n  n.times do\n    res = yield\n  end\n\n  puts \"#{Time.monotonic - start}, res: #{res}\"\nend\n\nstart = Time.monotonic\nb = RedBlackTreeRunner.new 100_000\nbench(\"delete\", 10) { b.run_delete }\nbench(\"add\", 10) { b.run_add }\nbench(\"search\", 10) { b.run_search }\nbench(\"walk\", 100) { b.run_inorder_walk }\nbench(\"reverse_walk\", 100) { b.run_reverse_inorder_walk }\nbench(\"min\", 100) { b.run_min }\nbench(\"max\", 100) { b.run_max }\n\nputs \"summary time: #{Time.monotonic - start}\"\n"
  },
  {
    "path": "samples/sdl/fire.cr",
    "content": "require \"./sdl/sdl\"\n\nclass Point\n  MAX_LIFE  = 50\n  HALF_LIFE = MAX_LIFE / 2\n\n  property x : Float64\n  property y : Float64\n  property angle : Float64\n  property speed : Float64\n  property color_pattern : ColorPattern\n  getter :life\n\n  def initialize(x, y, angle, speed, color_pattern)\n    @x = x.to_f64\n    @y = y.to_f64\n    @angle = angle.to_f64\n    @speed = speed.to_f64\n    @color_pattern = color_pattern\n    @life = MAX_LIFE\n  end\n\n  def dead?\n    @life <= 0\n  end\n\n  def revive\n    @life = MAX_LIFE\n  end\n\n  def die_a_little\n    @life -= 1\n  end\n\n  def color\n    @color_pattern.interpolate(@life)\n  end\n\n  def advance(screen)\n    @x += @speed * Math.cos(@angle)\n    @y -= @speed * Math.sin(@angle)\n\n    if @x <= 0\n      @angle = Math::PI - @angle\n      @x = 0_f64\n    end\n\n    if @x >= screen.width - 1\n      @angle = Math::PI - @angle\n      @x = (screen.width - 1).to_f64\n    end\n\n    if @y <= 0\n      @angle = 2 * Math::PI - @angle\n      @y = 0_f64\n    end\n\n    if @y >= screen.height - 1\n      @angle = 2 * Math::PI - @angle\n      @y = (screen.height - 1).to_f64\n    end\n  end\nend\n\nclass MainPoint < Point\n  COUNT          = 4\n  MAX_TAIL_ANGLE = Math::PI / 3\n  TAIL_SPEED     = 0.05\n\n  enum Direction\n    Plus\n    Minus\n  end\n\n  @tail_angle : Float64\n  @tail_direction : Direction\n\n  def initialize(x, y, angle, speed, color_pattern)\n    super\n    @tail_angle = MAX_TAIL_ANGLE\n    @tail_direction = :minus\n    @color_pattern = color_pattern\n  end\n\n  def turn_left\n    @angle += 0.05\n  end\n\n  def turn_right\n    @angle -= 0.05\n  end\n\n  def speed_up\n    @speed += 0.02\n    @speed = 20_f64 if @speed >= 20\n  end\n\n  def speed_down\n    @speed -= 0.02\n    @speed = 0_f64 if @speed <= 0\n  end\n\n  def color\n    @color_pattern.main\n  end\n\n  def emit_tail_points(points)\n    points.make(@x, @y, Math::PI + @angle + @tail_angle, @speed, @color_pattern.child)\n    points.make(@x, @y, Math::PI + @angle - @tail_angle, @speed, @color_pattern.child)\n\n    if @tail_direction.plus?\n      @tail_angle += TAIL_SPEED\n      if @tail_angle >= MAX_TAIL_ANGLE\n        @tail_angle = MAX_TAIL_ANGLE\n        @tail_direction = :minus\n      end\n    else\n      @tail_angle -= TAIL_SPEED\n      if @tail_angle <= -MAX_TAIL_ANGLE\n        @tail_angle = -MAX_TAIL_ANGLE\n        @tail_direction = :plus\n      end\n    end\n  end\nend\n\nabstract class ColorPattern\n  def child\n    self\n  end\n\n  def interpolate_half(life)\n    (life * 255.0 / Point::HALF_LIFE).to_i\n  end\n\n  def interpolate_max(life)\n    (life * 255.0 / Point::MAX_LIFE).to_i\n  end\n\n  def make_color(r, g, b)\n    (b << 24) + (g << 16) + (r << 8)\n  end\nend\n\nclass YellowColorPattern < ColorPattern\n  def main\n    0x00FFFF00\n  end\n\n  def interpolate(life)\n    if life > Point::HALF_LIFE\n      r, g, b = 255, interpolate_half(life), 0\n    else\n      r, g, b = interpolate_half(life), 0, 0\n    end\n    make_color r, g, b\n  end\nend\n\nclass CyanColorPattern < ColorPattern\n  def main\n    0xFFFF0000\n  end\n\n  def interpolate(life)\n    if life > Point::HALF_LIFE\n      r, g, b = 0, 255, interpolate_half(life)\n    else\n      r, g, b = 0, interpolate_half(life), 0\n    end\n    make_color r, g, b\n  end\nend\n\nclass MagentaColorPattern < ColorPattern\n  def main\n    0xFF00FF00\n  end\n\n  def interpolate(life)\n    if life > Point::HALF_LIFE\n      r, g, b = interpolate_half(life), 0, 255\n    else\n      r, g, b = 0, 0, interpolate_half(life)\n    end\n    make_color r, g, b\n  end\nend\n\nclass RainbowColorPattern < ColorPattern\n  def initialize(@patterns : Array(ColorPattern))\n    @index = 0.0\n  end\n\n  def main\n    main = @patterns[@index.to_i].main\n    @index += 0.05\n    @index = 0.0 if @index.to_i >= @patterns.size\n    main\n  end\n\n  def child\n    @patterns[@index.to_i]\n  end\n\n  def interpolate(life)\n    raise \"Shouldn't reach here\"\n  end\nend\n\nclass Points\n  MAX = Point::MAX_LIFE * MainPoint::COUNT * 2\n\n  def initialize\n    @points = Array(Point).new(MAX)\n  end\n\n  def make(x, y, angle, speed, color_pattern)\n    @points.each do |point|\n      if point.dead?\n        point.x = x\n        point.y = y\n        point.angle = angle\n        point.speed = speed\n        point.color_pattern = color_pattern\n        point.revive\n        return\n      end\n    end\n\n    if @points.size < MAX\n      @points << Point.new(x, y, angle, speed, color_pattern)\n    end\n  end\n\n  def each(&)\n    @points.each do |point|\n      yield point unless point.dead?\n    end\n  end\nend\n\nrecord Rectangle, x : Int32, y : Int32 do\n  def contains?(p)\n    contains? p.x, p.y\n  end\n\n  def contains?(x, y)\n    @x <= x && x < @x + 10 && @y <= y && y < @y + 10\n  end\nend\n\ndef parse_rectangles(filename)\n  unless File.exists?(filename)\n    raise \"File does not exist: #{filename}\"\n  end\n\n  rects = [] of Rectangle\n  lines = File.read(filename)\n  lines = lines.split('\\n').map(&.rstrip)\n  lines.each_with_index do |line, y|\n    x = 0\n    line.each_char do |c|\n      if c != ' '\n        rects << Rectangle.new(x * 10, y * 10)\n      end\n      x += 1\n    end\n  end\n  rects\nend\n\nclass Screen\n  @rects : Array(Rectangle)\n\n  def initialize(@surface : SDL::Surface)\n    @background = Array(UInt32).new(surface.width * surface.height, 0_u32)\n    @rects = parse_rectangles(\"#{__DIR__}/fire.txt\")\n  end\n\n  def width\n    @surface.width\n  end\n\n  def height\n    @surface.height\n  end\n\n  def put_pixel(point, color)\n    color = color.to_u32!\n    offset = @surface.offset(point.x, point.y)\n    @surface[offset] = color\n\n    background_intensity = intensity(@background[offset])\n    color_intensity = intensity(color)\n\n    if color_intensity >= background_intensity && @rects.any?(&.contains?(point))\n      @background[offset] = color\n    end\n  end\n\n  def put_background(point)\n    offset = @surface.offset(point.x, point.y)\n    @surface[offset] = @background[offset]\n  end\n\n  def intensity(color)\n    b = (color >> 24) % 256\n    g = (color >> 16) % 256\n    r = (color >> 8) % 256\n    r + g + b\n  end\nend\n\ndef finish(start, frames)\n  ms = SDL.ticks - start\n  puts \"#{frames} frames in #{ms} ms\"\n  puts \"Average FPS: #{frames / (ms * 0.001)}\"\n  SDL.quit\n  exit\nend\n\nwidth = 640\nheight = 480\npoint_count = ARGV.size > 0 ? ARGV[0].to_i : 4\n\nyellow = YellowColorPattern.new\nmagenta = MagentaColorPattern.new\ncyan = CyanColorPattern.new\nrainbow = RainbowColorPattern.new [yellow, magenta, cyan]\n\nmain_points = [] of MainPoint\nmain_points << MainPoint.new(50, 50, -Math::PI / 8, 1.4, yellow)\nmain_points << MainPoint.new(width - 50, height - 50, Math::PI - Math::PI / 8, 1.4, magenta) if point_count >= 2\nmain_points << MainPoint.new(width - 50, 50, Math::PI + Math::PI / 8, 1.4, cyan) if point_count >= 3\nmain_points << MainPoint.new(50, height - 50, Math::PI / 8, 1.4, rainbow) if point_count >= 4\n\npoints = Points.new\n\nSDL.init\nSDL.hide_cursor\n\nsurface = SDL.set_video_mode width, height, 32, LibSDL::DOUBLEBUF | LibSDL::HWSURFACE | LibSDL::ASYNCBLIT | LibSDL::FULLSCREEN\nscreen = Screen.new(surface)\n\nframes = 0_u32\nstart = SDL.ticks\n\nturn_left = Array.new(MainPoint::COUNT, false)\nturn_right = Array.new(MainPoint::COUNT, false)\nspeed_up = Array.new(MainPoint::COUNT, false)\nspeed_down = Array.new(MainPoint::COUNT, false)\n\nwhile true\n  SDL.poll_events do |event|\n    case event.type\n    when LibSDL::QUIT\n      finish start, frames\n    when LibSDL::KEYDOWN\n      case event.key.key_sym.sym\n      when LibSDL::Key::ESCAPE, LibSDL::Key::Q\n        finish start, frames\n      when LibSDL::Key::LEFT\n        turn_left[0] = true\n      when LibSDL::Key::RIGHT\n        turn_right[0] = true\n      when LibSDL::Key::UP\n        speed_up[0] = true\n      when LibSDL::Key::DOWN\n        speed_down[0] = true\n      when LibSDL::Key::W\n        speed_up[1] = true\n      when LibSDL::Key::A\n        turn_left[1] = true\n      when LibSDL::Key::S\n        speed_down[1] = true\n      when LibSDL::Key::D\n        turn_right[1] = true\n      when LibSDL::Key::T\n        speed_up[2] = true\n      when LibSDL::Key::F\n        turn_left[2] = true\n      when LibSDL::Key::G\n        speed_down[2] = true\n      when LibSDL::Key::H\n        turn_right[2] = true\n      when LibSDL::Key::I\n        speed_up[3] = true\n      when LibSDL::Key::J\n        turn_left[3] = true\n      when LibSDL::Key::K\n        speed_down[3] = true\n      when LibSDL::Key::L\n        turn_right[3] = true\n      end\n    when LibSDL::KEYUP\n      case event.key.key_sym.sym\n      when LibSDL::Key::LEFT\n        turn_left[0] = false\n      when LibSDL::Key::RIGHT\n        turn_right[0] = false\n      when LibSDL::Key::UP\n        speed_up[0] = false\n      when LibSDL::Key::DOWN\n        speed_down[0] = false\n      when LibSDL::Key::W\n        speed_up[1] = false\n      when LibSDL::Key::A\n        turn_left[1] = false\n      when LibSDL::Key::S\n        speed_down[1] = false\n      when LibSDL::Key::D\n        turn_right[1] = false\n      when LibSDL::Key::T\n        speed_up[2] = false\n      when LibSDL::Key::F\n        turn_left[2] = false\n      when LibSDL::Key::G\n        speed_down[2] = false\n      when LibSDL::Key::H\n        turn_right[2] = false\n      when LibSDL::Key::I\n        speed_up[3] = false\n      when LibSDL::Key::J\n        turn_left[3] = false\n      when LibSDL::Key::K\n        speed_down[3] = false\n      when LibSDL::Key::L\n        turn_right[3] = false\n      end\n    end\n  end\n\n  surface.lock\n\n  points.each do |point|\n    screen.put_background(point)\n    point.die_a_little\n  end\n\n  main_points.each do |main_point|\n    screen.put_background(main_point)\n  end\n\n  main_points.each_with_index do |main_point, i|\n    main_point.turn_left if turn_left[i]\n    main_point.turn_right if turn_right[i]\n    main_point.speed_up if speed_up[i]\n    main_point.speed_down if speed_down[i]\n    main_point.advance(screen)\n  end\n\n  points.each do |point|\n    point.advance(screen)\n  end\n\n  points.each do |point|\n    screen.put_pixel(point, point.color)\n  end\n\n  main_points.each do |main_point|\n    screen.put_pixel(main_point, main_point.color)\n  end\n\n  main_points.each do |main_point|\n    main_point.emit_tail_points(points)\n  end\n\n  surface.unlock\n  surface.flip\n\n  frames += 1\nend\n"
  },
  {
    "path": "samples/sdl/fire.txt",
    "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n         xxxxxx  xxxxx   xx  xx  xxxxxx  xxxxxx  xxxxxx  xx\n         xxxxxx  xxxxxx  xx  xx  xxxxxx  xxxxxx  xxxxxx  xx\n         xx      xx  xx  xx  xx  xx        xx    xx  xx  xx\n         xx      xx  xx  xxxxxx  xx        xx    xx  xx  xx\n         xx      xxxxxx  xxxxxx  xxxxxx    xx    xxxxxx  xx\n         xx      xxxxx     xx    xxxxxx    xx    xxxxxx  xx\n         xx      xx xx     xx        xx    xx    xx  xx  xx\n         xx      xx  xx    xx        xx    xx    xx  xx  xx\n         xxxxxx  xx  xx    xx    xxxxxx    xx    xx  xx  xxxxxx\n         xxxxxx  xx  xx    xx    xxxxxx    xx    xx  xx  xxxxxx\n"
  },
  {
    "path": "samples/sdl/raytracer.cr",
    "content": "# Ported from Nimrod: https://gist.github.com/AdrianV/5774141\n\nrequire \"./sdl/sdl\"\n\nWIDTH     = 1280\nHEIGHT    =  720\nFOV       = 45.0\nMAX_DEPTH =    6\n\nstruct Vec3\n  getter :x\n  getter :y\n  getter :z\n\n  def initialize\n    @x, @y, @z = 0.0, 0.0, 0.0\n  end\n\n  def initialize(value)\n    @x, @y, @z = value, value, value\n  end\n\n  def initialize(@x, @y, @z)\n  end\n\n  {% for op in %w(+ - * /) %}\n    def {{op.id}}(other : Vec3)\n      Vec3.new(@x {{op.id}} other.x, @y {{op.id}} other.y, @z {{op.id}} other.z)\n    end\n\n    def {{op.id}}(other : Float)\n      Vec3.new(@x {{op.id}} other, @y {{op.id}} other, @z {{op.id}} other)\n    end\n  {% end %}\n\n  def -\n    Vec3.new(-@x, -@y, -@z)\n  end\n\n  def dot(other)\n    @x * other.x + @y * other.y + @z * other.z\n  end\n\n  def magnitude\n    Math.sqrt(dot(self))\n  end\n\n  def normalize\n    m = magnitude\n    Vec3.new(@x / m, @y / m, @z / m)\n  end\nend\n\nrecord Ray, start : Vec3, dir : Vec3\n\nclass Sphere\n  getter :color\n  getter :reflection\n  getter :transparency\n\n  def initialize(@center : Vec3, @radius : Float64, @color : Vec3, @reflection = 0.0, @transparency = 0.0)\n  end\n\n  def intersects?(ray)\n    vl = @center - ray.start\n    a = vl.dot(ray.dir)\n    return false if a < 0\n\n    b2 = vl.dot(vl) - a * a\n    r2 = @radius * @radius\n    return false if b2 > r2\n\n    true\n  end\n\n  def intersect(ray, distance)\n    vl = @center - ray.start\n    a = vl.dot(ray.dir)\n    return nil if a < 0\n\n    b2 = vl.dot(vl) - a * a\n    r2 = @radius * @radius\n    return nil if b2 > r2\n\n    c = Math.sqrt(r2 - b2)\n    near = a - c\n    far = a + c\n    near < 0 ? far : near\n  end\n\n  def normalize(v)\n    (v - @center).normalize\n  end\nend\n\nrecord Light, position : Vec3, color : Vec3\nrecord Scene, objects : Array(Sphere), lights : Array(Light)\n\ndef trace(ray, scene, depth)\n  nearest = 1e9\n  obj = nil\n  result = Vec3.new\n\n  scene.objects.each do |o|\n    distance = 1e9\n    if (distance = o.intersect(ray, distance)) && distance < nearest\n      nearest = distance\n      obj = o\n    end\n  end\n\n  if obj\n    point_of_hit = ray.dir * nearest\n    point_of_hit += ray.start\n    normal = obj.normalize(point_of_hit)\n    inside = false\n    dot_normal_ray = normal.dot(ray.dir)\n    if dot_normal_ray > 0\n      inside = true\n      normal = -normal\n      dot_normal_ray = -dot_normal_ray\n    end\n\n    reflection_ratio = obj.reflection\n    norm_e5 = normal * 1.0e-5\n\n    scene.lights.each do |lgt|\n      light_direction = (lgt.position - point_of_hit).normalize\n      r = Ray.new(point_of_hit + norm_e5, light_direction)\n\n      # go through the scene check whether we're blocked from the lights\n      blocked = scene.objects.any? &.intersects? r\n\n      unless blocked\n        temp = lgt.color\n        temp *= Math.max(0.0, normal.dot(light_direction))\n        temp *= obj.color\n        temp *= (1.0 - reflection_ratio)\n        result += temp\n      end\n    end\n\n    facing = Math.max(0.0, -dot_normal_ray)\n    fresneleffect = reflection_ratio + (1.0 - reflection_ratio) * ((1.0 - facing) ** 5.0)\n\n    # compute reflection\n    if depth < MAX_DEPTH && reflection_ratio > 0\n      reflection_direction = ray.dir - normal * 2.0 * dot_normal_ray\n      reflection = trace(Ray.new(point_of_hit + norm_e5, reflection_direction), scene, depth + 1)\n      result += reflection * fresneleffect\n    end\n\n    # compute refraction\n    if depth < MAX_DEPTH && (obj.transparency > 0.0)\n      ior = 1.5\n      ce = ray.dir.dot(normal) * -1.0\n      ior = inside ? 1.0 / ior : ior\n      eta = 1.0 / ior\n      gf = (ray.dir + normal * ce) * eta\n      sin_t1_2 = 1.0 - ce * ce\n      sin_t2_2 = sin_t1_2 * (eta * eta)\n      if sin_t2_2 < 1.0\n        gc = normal * Math.sqrt(1 - sin_t2_2)\n        refraction_direction = gf - gc\n        refraction = trace(Ray.new(point_of_hit - normal * 1.0e-4, refraction_direction),\n          scene, depth + 1)\n        result += refraction * (1.0 - fresneleffect) * obj.transparency\n      end\n    end\n  end\n\n  result\nend\n\ndef render(scene, surface)\n  surface.lock\n\n  eye = Vec3.new\n  h = Math.tan(FOV / 360.0 * 2.0 * Math::PI / 2.0) * 2.0\n  ww = surface.width.to_f64\n  hh = surface.height.to_f64\n  w = h * ww / hh\n\n  i = 0\n  HEIGHT.times do |y|\n    yy = y.to_f64\n    WIDTH.times do |x|\n      xx = x.to_f64\n      dir = Vec3.new((xx - ww / 2.0) / ww * w,\n        (hh / 2.0 - yy) / hh * h,\n        -1.0).normalize\n      pixel = trace(Ray.new(eye, dir), scene, 0.0)\n      r = Math.min(255, (pixel.x * 255.0).round.to_i)\n      g = Math.min(255, (pixel.y * 255.0).round.to_i)\n      b = Math.min(255, (pixel.z * 255.0).round.to_i)\n      surface[i] = (b << 24) + (g << 16) + (r << 8)\n      i += 1\n    end\n  end\n\n  surface.unlock\n  surface.update_rect 0, 0, 0, 0\nend\n\nProcess.on_interrupt { exit }\n\nscene = Scene.new(\n  [\n    Sphere.new(Vec3.new(0.0, -10002.0, -20.0), 10000.0, Vec3.new(0.8, 0.8, 0.8)),\n    Sphere.new(Vec3.new(0.0, 2.0, -20.0), 4.0, Vec3.new(0.8, 0.5, 0.5), 0.5),\n    Sphere.new(Vec3.new(5.0, 0.0, -15.0), 2.0, Vec3.new(0.3, 0.8, 0.8), 0.2),\n    Sphere.new(Vec3.new(-5.0, 0.0, -15.0), 2.0, Vec3.new(0.3, 0.5, 0.8), 0.2),\n    Sphere.new(Vec3.new(-2.0, -1.0, -10.0), 1.0, Vec3.new(0.1, 0.1, 0.1), 0.1, 0.8),\n  ],\n  [\n    Light.new(Vec3.new(-10.0, 20.0, 30.0), Vec3.new(2.0, 2.0, 2.0)),\n  ]\n)\n\nSDL.init\nSDL.hide_cursor\nsurface = SDL.set_video_mode WIDTH, HEIGHT, 32, LibSDL::DOUBLEBUF | LibSDL::HWSURFACE | LibSDL::ASYNCBLIT\n\nfirst = true\n\nbegin\n  while true\n    SDL.poll_events do |event|\n      if event.type.in?(LibSDL::QUIT, LibSDL::KEYDOWN)\n        SDL.quit\n        exit\n      end\n    end\n\n    if first\n      start = SDL.ticks\n      render scene, surface\n      ms = SDL.ticks - start\n      puts \"Rendered in #{ms} ms\"\n      first = false\n    end\n  end\nensure\n  SDL.quit\nend\n"
  },
  {
    "path": "samples/sdl/sdl/lib_sdl.cr",
    "content": "{% if flag?(:darwin) %}\n  @[Link(\"SDL\")]\n  @[Link(\"SDLMain\")]\n  @[Link(framework: \"Cocoa\")]\n{% else %}\n  @[Link(\"SDL\")]\n{% end %}\n\nlib LibSDL\n  INIT_TIMER       = 0x00000001_u32\n  INIT_AUDIO       = 0x00000010_u32\n  INIT_VIDEO       = 0x00000020_u32\n  INIT_CDROM       = 0x00000100_u32\n  INIT_JOYSTICK    = 0x00000200_u32\n  INIT_NOPARACHUTE = 0x00100000_u32\n  INIT_EVENTTHREAD = 0x01000000_u32\n  INIT_EVERYTHING  = 0x0000FFFF_u32\n\n  SWSURFACE = 0x00000000_u32\n  HWSURFACE = 0x00000001_u32\n  ASYNCBLIT = 0x00000004_u32\n\n  ANYFORMAT  = 0x10000000_u32\n  HWPALETTE  = 0x20000000_u32\n  DOUBLEBUF  = 0x40000000_u32\n  FULLSCREEN = 0x80000000_u32\n  OPENGL     = 0x00000002_u32\n  OPENGLBLIT = 0x0000000A_u32\n  RESIZABLE  = 0x00000010_u32\n  NOFRAME    = 0x00000020_u32\n\n  NOEVENT         =  0_u8\n  ACTIVEEVENT     =  1_u8\n  KEYDOWN         =  2_u8\n  KEYUP           =  3_u8\n  MOUSEMOTION     =  4_u8\n  MOUSEBUTTONDOWN =  5_u8\n  MOUSEBUTTONUP   =  6_u8\n  JOYAXISMOTION   =  7_u8\n  JOYBALLMOTION   =  8_u8\n  JOYHATMOTION    =  9_u8\n  JOYBUTTONDOWN   = 10_u8\n  JOYBUTTONUP     = 11_u8\n  QUIT            = 12_u8\n  SYSWMEVENT      = 13_u8\n  EVENT_RESERVEDA = 14_u8\n  EVENT_RESERVEDB = 15_u8\n  VIDEORESIZE     = 16_u8\n  VIDEOEXPOSE     = 17_u8\n  EVENT_RESERVED2 = 18_u8\n  EVENT_RESERVED3 = 19_u8\n  EVENT_RESERVED4 = 20_u8\n  EVENT_RESERVED5 = 21_u8\n  EVENT_RESERVED6 = 22_u8\n  EVENT_RESERVED7 = 23_u8\n  USEREVENT       = 24_u8\n  NUMEVENTS       = 32_u8\n\n  HWACCEL     = 0x00000100_u32\n  SRCCOLORKEY = 0x00001000_u32\n  RLEACCELOK  = 0x00002000_u32\n  RLEACCEL    = 0x00004000_u32\n  SRCALPHA    = 0x00010000_u32\n  PREALLOC    = 0x01000000_u32\n\n  DISABLE = 0\n  ENABLE  = 1\n\n  struct Color\n    r, g, b, unused : UInt8\n  end\n\n  struct Rect\n    x, y : Int16\n    w, h : UInt16\n  end\n\n  struct Surface\n    flags : UInt32\n    format : Void* # TODO\n    w, h : Int32\n    pitch : UInt16\n    pixels : Void*\n    # TODO\n  end\n\n  enum Key\n    ESCAPE =  27\n    A      =  97\n    B      =  98\n    C      =  99\n    D      = 100\n    E      = 101\n    F      = 102\n    G      = 103\n    H      = 104\n    I      = 105\n    J      = 106\n    K      = 107\n    L      = 108\n    M      = 109\n    N      = 110\n    O      = 111\n    P      = 112\n    Q      = 113\n    R      = 114\n    S      = 115\n    T      = 116\n    U      = 117\n    V      = 118\n    W      = 119\n    X      = 120\n    Y      = 121\n    Z      = 122\n    UP     = 273\n    DOWN   = 274\n    RIGHT  = 275\n    LEFT   = 276\n  end\n\n  struct KeySym\n    scan_code : UInt8\n    sym : Key\n    # TODO\n  end\n\n  struct KeyboardEvent\n    type : UInt8\n    which : UInt8\n    state : UInt8\n    key_sym : KeySym\n  end\n\n  union Event\n    type : UInt8\n    key : KeyboardEvent\n  end\n\n  fun init = SDL_Init(flags : UInt32) : Int32\n  fun get_error = SDL_GetError : UInt8*\n  fun quit = SDL_Quit : Void\n  fun set_video_mode = SDL_SetVideoMode(width : Int32, height : Int32, bpp : Int32, flags : UInt32) : Surface*\n  fun delay = SDL_Delay(ms : UInt32) : Void\n  fun poll_event = SDL_PollEvent(event : Event*) : Int32\n  fun wait_event = SDL_WaitEvent(event : Event*) : Int32\n  fun lock_surface = SDL_LockSurface(surface : Surface*) : Int32\n  fun unlock_surface = SDL_UnlockSurface(surface : Surface*) : Void\n  fun update_rect = SDL_UpdateRect(screen : Surface*, x : Int32, y : Int32, w : Int32, h : Int32) : Void\n  fun show_cursor = SDL_ShowCursor(toggle : Int32) : Int32\n  fun get_ticks = SDL_GetTicks : UInt32\n  fun flip = SDL_Flip(screen : Surface*) : Int32\nend\n"
  },
  {
    "path": "samples/sdl/sdl/sdl.cr",
    "content": "require \"./*\"\n\nmodule SDL\n  def self.init(flags = LibSDL::INIT_EVERYTHING)\n    if LibSDL.init(flags) != 0\n      raise \"Can't initialize SDL: #{error}\"\n    end\n  end\n\n  def self.set_video_mode(width, height, bpp, flags)\n    surface = LibSDL.set_video_mode(width, height, bpp, flags)\n    if surface.null?\n      raise \"Can't set SDL video mode: #{error}\"\n    end\n    Surface.new(surface, width, height, bpp)\n  end\n\n  def self.show_cursor\n    LibSDL.show_cursor LibSDL::ENABLE\n  end\n\n  def self.hide_cursor\n    LibSDL.show_cursor LibSDL::DISABLE\n  end\n\n  def self.error\n    String.new LibSDL.get_error\n  end\n\n  def self.ticks\n    LibSDL.get_ticks\n  end\n\n  def self.quit\n    LibSDL.quit\n  end\n\n  def self.poll_events(&)\n    while LibSDL.poll_event(out event) == 1\n      yield event\n    end\n  end\nend\n"
  },
  {
    "path": "samples/sdl/sdl/surface.cr",
    "content": "class SDL::Surface\n  getter :surface\n  getter :width\n  getter :height\n  getter :bpp\n\n  def initialize(@surface : LibSDL::Surface*, @width : Int32, @height : Int32, @bpp : Int32)\n  end\n\n  def lock\n    LibSDL.lock_surface @surface\n  end\n\n  def unlock\n    LibSDL.unlock_surface @surface\n  end\n\n  def update_rect(x, y, w, h)\n    LibSDL.update_rect @surface, x, y, w, h\n  end\n\n  def flip\n    LibSDL.flip @surface\n  end\n\n  def []=(offset, color)\n    (@surface.value.pixels.as(UInt32*))[offset] = color.to_u32!\n  end\n\n  def []=(x, y, color)\n    self[y.to_i32 * @width + x.to_i32] = color\n  end\n\n  def offset(x, y)\n    x.to_i32 + (y.to_i32 * @width)\n  end\nend\n"
  },
  {
    "path": "samples/sdl/tv.cr",
    "content": "require \"./sdl/sdl\"\n\nclass ColorMaker\n  enum State\n    BlueUp\n    BlueDown\n    GreenUp\n    GreenDown\n    RedUp\n    RedDown\n  end\n\n  @state : State\n\n  def initialize(@delay : Int32)\n    @r = 0\n    @g = 255\n    @b = 0\n    @time = 0\n    @state = :blue_up\n  end\n\n  def next\n    @time += 1\n    if @time == @delay\n      next_state\n      @time = 0\n    end\n  end\n\n  def next_state\n    case @state\n    when .green_up?\n      @g += 1\n      @state = :red_down if @g == 255\n    when .red_down?\n      @r -= 1\n      @state = :blue_up if @r == 0\n    when .blue_up?\n      @b += 1\n      @state = :green_down if @b == 255\n    when .green_down?\n      @g -= 1\n      @state = :red_up if @g == 0\n    when .red_up?\n      @r += 1\n      @state = :blue_down if @r == 255\n    when .blue_down?\n      @b -= 1\n      @state = :green_up if @b == 0\n    end\n  end\n\n  def black_color\n    make_alpha_color(0.25)\n  end\n\n  def dark_color\n    make_alpha_color(0.5)\n  end\n\n  def light_color\n    make_alpha_color(1.0)\n  end\n\n  def make_alpha_color(multiplier)\n    rand = Random.next_int\n    r = ((rand >> 16) % 256).to_i\n    g = ((rand >> 8) % 256).to_i\n    b = (rand % 256).to_i\n    r = saturate_color(r, @r, multiplier)\n    g = saturate_color(g, @g, multiplier)\n    b = saturate_color(b, @b, multiplier)\n    make_color r, g, b, 0\n  end\n\n  def saturate_color(random, component, multiplier)\n    Math.min(random, (component * multiplier).to_i)\n  end\n\n  def make_color(r, g, b, a)\n    (b << 24) + (g << 16) + (r << 8) + a\n  end\nend\n\nclass Rectangle\n  def initialize(@x : Int32, @y : Int32, @light : Bool)\n  end\n\n  def light?\n    @light\n  end\n\n  def contains?(x, y)\n    @x == x && @y == y\n  end\nend\n\ndef parse_rectangles\n  rects = [] of Rectangle\n  lines = File.read(\"#{__DIR__}/tv.txt\").split('\\n').map(&.rstrip)\n  lines.each_with_index do |line, y|\n    x = 0\n    line.each_char do |c|\n      if c == 'x'\n        rects << Rectangle.new(x, y, true)\n      elsif c == '.'\n        rects << Rectangle.new(x, y, false)\n      end\n      x += 1\n    end\n  end\n  rects\nend\n\nwidth = 640\nheight = 480\n\ndelay = ARGV.size > 1 ? ARGV[1].to_i : 1\n\nSDL.init\nSDL.show_cursor\n\nsurface = SDL.set_video_mode width, height, 32, LibSDL::DOUBLEBUF | LibSDL::HWSURFACE | LibSDL::ASYNCBLIT | LibSDL::FULLSCREEN\n\nframes = 0_u32\nstart = SDL.ticks\n\ncolor_maker = ColorMaker.new(delay)\nrects = parse_rectangles\nputs \"Rects: #{rects.size}\"\n\nbegin\n  while true\n    SDL.poll_events do |event|\n      if event.type.in?(LibSDL::QUIT, LibSDL::KEYDOWN)\n        ms = SDL.ticks - start\n        puts \"#{frames} frames in #{ms} ms\"\n        puts \"Average FPS: #{frames / (ms * 0.001)}\"\n        SDL.quit\n        exit\n      end\n    end\n\n    surface.lock\n\n    (height // 10).times do |h|\n      (width // 10).times do |w|\n        rect = rects.find(&.contains?(w, h))\n        10.times do |y|\n          10.times do |x|\n            surface[x + 10 * w, y + 10 * h] = rect ? (rect.light? ? color_maker.light_color : color_maker.dark_color) : color_maker.black_color\n          end\n        end\n      end\n    end\n\n    color_maker.next\n\n    surface.unlock\n    surface.flip\n\n    frames += 1\n  end\nensure\n  SDL.quit\nend\n"
  },
  {
    "path": "samples/sdl/tv.txt",
    "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n      xxxxx   xxxx    x   x   xxxxx   xxxxx   xxxxx   x\n      x.....  x...x   x.  x.  x.....   .x...  x...x.  x.\n      x.      x.  x.  x.  x.  x.        x.    x.  x.  x.\n      x.      x.  x.  x.  x.  x.        x.    x.  x.  x.\n      x.      xxxx     x x .  xxxxx     x.    xxxxx.  x.\n      x.      x...x     x .    ...x.    x.    x...x.  x.\n      x.      x.  x.    x.        x.    x.    x.  x.  x.\n      x.      x.  x.    x.        x.    x.    x.  x.  x.\n      xxxxx   x.  x.    x.    xxxxx.    x.    x.  x.  xxxxx\n       .....   .   .     .     .....     .     .   .   .....\n"
  },
  {
    "path": "samples/sieve.cr",
    "content": "# Compute prime numbers up to 100 with the Sieve of Eratosthenes\nmax = 100\n\nsieve = Array.new(max + 1, true)\nsieve[0] = false\nsieve[1] = false\n\n2.step(to: Math.sqrt(max)) do |i|\n  if sieve[i]\n    (i * i).step(to: max, by: i) do |j|\n      sieve[j] = false\n    end\n  end\nend\n\nsieve.each_with_index do |prime, number|\n  if prime\n    puts number\n  end\nend\n"
  },
  {
    "path": "samples/spectral-norm.cr",
    "content": "# Copied with little modifications from: https://github.com/wmoxam/Ruby-Benchmarks-Game/blob/master/benchmarks/spectral-norm.rb\n\ndef eval_A(i, j)\n  1.0_f64 / ((i + j) * (i + j + 1.0) / 2.0 + i + 1.0)\nend\n\ndef eval_A_times_u(u)\n  (0...u.size).map do |i|\n    v = 0.0_f64\n    (0...u.size).each do |j|\n      v += eval_A(i, j) * u[j]\n    end\n    v\n  end\nend\n\ndef eval_At_times_u(u)\n  (0...u.size).map do |i|\n    v = 0.0_f64\n    (0...u.size).each do |j|\n      v += eval_A(j, i) * u[j]\n    end\n    v\n  end\nend\n\ndef eval_AtA_times_u(u)\n  eval_At_times_u(eval_A_times_u(u))\nend\n\nn = (ARGV[0]? || 1000).to_i\nu = Array.new(n, 1.0_f64)\nv = Array.new(n, 1.0_f64)\n10.times do\n  v = eval_AtA_times_u(u)\n  u = eval_AtA_times_u(v)\nend\nvBv = vv = 0.0_f64\n(0...n).each do |i|\n  vBv += u[i] * v[i]\n  vv += v[i] * v[i]\nend\nputs Math.sqrt(vBv / vv)\n"
  },
  {
    "path": "samples/sudoku.cr",
    "content": "# Copied with little modifications from: https://github.com/attractivechaos/plb/blob/master/sudoku/sudoku_v1.rb\n\ndef sd_genmat\n  mr = Array.new(324) { [] of Int32 }\n  mc = Array.new(729) { [] of Int32 }\n  r = 0\n  (0...9).each do |i|\n    (0...9).each do |j|\n      (0...9).each do |k|\n        mc[r] = [9 * i + j, (i // 3 * 3 + j // 3) * 9 + k + 81, 9 * i + k + 162, 9 * j + k + 243]\n        r += 1\n      end\n    end\n  end\n  (0...729).each do |r2|\n    (0...4).each do |c2|\n      mr[mc[r2][c2]].push(r2)\n    end\n  end\n  {mr, mc}\nend\n\ndef sd_update(mr, mc, sr, sc, r, v)\n  min, min_c = 10, 0\n  (0...4).each do |c2|\n    if v > 0\n      sc[mc[r][c2]] += 128\n    else\n      sc[mc[r][c2]] -= 128\n    end\n  end\n  (0...4).each do |c2|\n    c = mc[r][c2]\n    if v > 0\n      (0...9).each do |r2|\n        rr = mr[c][r2]\n        sr[rr] += +1\n        if sr[rr] == 1\n          p = mc[rr]\n          sc[p[0]] -= 1; sc[p[1]] -= 1; sc[p[2]] -= 1; sc[p[3]] -= 1\n          if sc[p[0]] < min\n            min, min_c = sc[p[0]], p[0]\n          end\n          if sc[p[1]] < min\n            min, min_c = sc[p[1]], p[1]\n          end\n          if sc[p[2]] < min\n            min, min_c = sc[p[2]], p[2]\n          end\n          if sc[p[3]] < min\n            min, min_c = sc[p[3]], p[3]\n          end\n        end\n      end\n    else\n      (0...9).each do |r2|\n        rr = mr[c][r2]\n        sr[rr] -= 1\n        if sr[rr] == 0\n          p = mc[rr]\n          sc[p[0]] += 1; sc[p[1]] += 1; sc[p[2]] += 1; sc[p[3]] += 1\n        end\n      end\n    end\n  end\n  {min, min_c}\nend\n\ndef sd_solve(mr, mc, s)\n  ret = [] of Array(Int32)\n  sr, sc, hints = Array.new(729, 0), Array.new(324, 9), 0\n  (0...81).each do |i|\n    a = ('1' <= s[i] <= '9') ? s[i].ord - 49 : -1\n    if a >= 0\n      sd_update(mr, mc, sr, sc, i * 9 + a, 1)\n      hints += 1\n    end\n  end\n  cr, cc = Array.new(81, -1), Array.new(81, 0)\n  i, min, dir = 0, 10, 1\n  loop do\n    while i >= 0 && i < 81 - hints\n      if dir == 1\n        if min > 1\n          (0...324).each do |c|\n            if sc[c] < min\n              min, cc[i] = sc[c], c\n              break if min < 2\n            end\n          end\n        end\n        if min == 0 || min == 10\n          cr[i], dir, i = -1, -1, i - 1\n        end\n      end\n      c = cc[i]\n      if dir == -1 && cr[i] >= 0\n        sd_update(mr, mc, sr, sc, mr[c][cr[i]], -1)\n      end\n      r2_ = 9\n      (cr[i] + 1...9).each do |r2|\n        if sr[mr[c][r2]] == 0\n          r2_ = r2\n          break\n        end\n      end\n      if r2_ < 9\n        min, cc[i + 1] = sd_update(mr, mc, sr, sc, mr[c][r2_], 1)\n        cr[i], dir, i = r2_, 1, i + 1\n      else\n        cr[i], dir, i = -1, -1, i - 1\n      end\n    end\n    break if i < 0\n    o = [] of Int32\n    (0...81).each { |j| o.push((s[j].ord - 49).to_i32) }\n    (0...i).each do |j|\n      r = mr[cc[j]][cr[j]]\n      o[r // 9] = r % 9 + 1\n    end\n    ret.push(o)\n    i, dir = i - 1, -1\n  end\n  ret\nend\n\nsudoku = <<-SUDOKU\n..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 # near worst case for brute-force solver (wiki)\n.......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... # gsf's sudoku q1 (Platinum Blonde)\n.2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. # (Cheese)\n........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ # (Fata Morgana)\n12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 # (Red Dwarf)\n1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 # (Easter Monster)\n.......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... # Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5)\n12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8\n..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5.\n1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6.\n..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7..\n....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 # dukuso's suexrat9 (top 1)\n7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5...........\n3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6.....\n........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 # dukuso's suexratt (top 1)\n.......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... # first 2 from sudoku17\n.......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6..\n1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1\n.....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67.....\nSUDOKU\n\ndef solve_all(sudoku)\n  mr, mc = sd_genmat()\n  sudoku.split('\\n').compact_map do |line|\n    if line.size >= 81\n      ret = sd_solve(mr, mc, line)\n      ret.map(&.join)\n    end\n  end\nend\n\n10.times do |i|\n  res = solve_all(sudoku)\n  res.each { |str| puts str[0] } if i == 0\nend\n"
  },
  {
    "path": "samples/tcp_client.cr",
    "content": "require \"socket\"\n\n# goes with tcp_server.cr\n\nsocket = TCPSocket.new \"127.0.0.1\", 9000\n10.times do |i|\n  socket.puts i\n  puts \"Server response: #{socket.gets}\"\n  sleep 0.5.seconds\nend\n"
  },
  {
    "path": "samples/tcp_server.cr",
    "content": "require \"socket\"\n\n# goes with tcp_client.cr\n\ndef process(client)\n  client_addr = client.remote_address\n  puts \"#{client_addr} connected\"\n\n  while msg = client.read_line\n    puts \"#{client_addr} msg '#{msg}'\"\n    client.puts msg\n  end\nrescue IO::EOFError\n  puts \"#{client_addr} disconnected\"\nensure\n  client.close\nend\n\nserver = TCPServer.new \"127.0.0.1\", 9000\nputs \"Listening on 127.0.0.1:9000\"\nloop { spawn process(server.accept) }\n"
  },
  {
    "path": "samples/text_raytracer.cr",
    "content": "# Ported from Rust from https://gist.github.com/joshmarinacci/c84d0979e100d107f685\n\nrecord Vector, x : Float64, y : Float64, z : Float64 do\n  def scale(s)\n    Vector.new(x * s, y * s, z * s)\n  end\n\n  def +(other)\n    Vector.new(x + other.x, y + other.y, z + other.z)\n  end\n\n  def -(other)\n    Vector.new(x - other.x, y - other.y, z - other.z)\n  end\n\n  def dot(other)\n    x*other.x + y*other.y + z*other.z\n  end\n\n  def magnitude\n    Math.sqrt self.dot(self)\n  end\n\n  def normalize\n    scale(1.0 / magnitude)\n  end\nend\n\nrecord Ray, orig : Vector, dir : Vector\n\nrecord Color, r : Float64, g : Float64, b : Float64 do\n  def scale(s)\n    Color.new(r * s, g * s, b * s)\n  end\n\n  def +(other)\n    Color.new(r + other.r, g + other.g, b + other.b)\n  end\nend\n\nrecord Sphere, center : Vector, radius : Float64, color : Color do\n  def get_normal(pt)\n    (pt - center).normalize\n  end\nend\n\nrecord Light, position : Vector, color : Color\n\nrecord Hit, obj : Sphere, value : Float64\n\nWHITE = Color.new(1.0, 1.0, 1.0)\nRED   = Color.new(1.0, 0.0, 0.0)\nGREEN = Color.new(0.0, 1.0, 0.0)\nBLUE  = Color.new(0.0, 0.0, 1.0)\n\nLIGHT1 = Light.new(Vector.new(0.7, -1.0, 1.7), WHITE)\n\ndef shade_pixel(ray, obj, tval)\n  pi = ray.orig + ray.dir.scale(tval)\n  color = diffuse_shading pi, obj, LIGHT1\n  col = (color.r + color.g + color.b) / 3.0\n  (col * 6.0).to_i\nend\n\ndef intersect_sphere(ray, center, radius)\n  l = center - ray.orig\n  tca = l.dot(ray.dir)\n  if tca < 0.0\n    return nil\n  end\n\n  d2 = l.dot(l) - tca*tca\n  r2 = radius*radius\n  if d2 > r2\n    return nil\n  end\n\n  thc = Math.sqrt(r2 - d2)\n  t0 = tca - thc\n  # t1 = tca + thc\n  if t0 > 10_000\n    return nil\n  end\n\n  t0\nend\n\ndef clamp(x, a, b)\n  return a if x < a\n  return b if x > b\n  x\nend\n\ndef diffuse_shading(pi, obj, light)\n  n = obj.get_normal(pi)\n  lam1 = (light.position - pi).normalize.dot(n)\n  lam2 = clamp lam1, 0.0, 1.0\n  light.color.scale(lam2*0.5) + obj.color.scale(0.3)\nend\n\nputs \"Hello, worlds!\"\n\nlut = %w(. - + * X M)\nw = 20 * 4\nh = 10 * 4\n\nscene = [\n  Sphere.new(Vector.new(-1.0, 0.0, 3.0), 0.3, RED),\n  Sphere.new(Vector.new(0.0, 0.0, 3.0), 0.8, GREEN),\n  Sphere.new(Vector.new(1.0, 0.0, 3.0), 0.4, BLUE),\n]\n\n(0...h).each do |j|\n  puts \"--\"\n  (0...w).each do |i|\n    fw, fi, fj, fh = w.to_f, i.to_f, j.to_f, h.to_f\n\n    ray = Ray.new(\n      Vector.new(0.0, 0.0, 0.0),\n      Vector.new((fi - fw/2.0)/fw, (fj - fh/2.0)/fh, 1.0).normalize\n    )\n\n    hit = nil\n\n    scene.each do |obj|\n      ret = intersect_sphere(ray, obj.center, obj.radius)\n      if ret\n        hit = Hit.new obj, ret\n      end\n    end\n\n    if hit\n      pixel = lut[shade_pixel(ray, hit.obj, hit.value)]\n    else\n      pixel = ' '\n    end\n\n    print pixel\n  end\nend\n\nputs \"we are done!\"\n"
  },
  {
    "path": "samples/tree.cr",
    "content": "class Node\n  @left : self?\n  @right : self?\n\n  def initialize(@value : Char)\n  end\n\n  def add(x)\n    if x < @value\n      if left = @left\n        left.add(x)\n      else\n        @left = Node.new(x)\n      end\n    else\n      if right = @right\n        right.add(x)\n      else\n        @right = Node.new(x)\n      end\n    end\n  end\n\n  def print\n    @left.try &.print\n    print @value\n    @right.try &.print\n  end\nend\n\nroot = Node.new('$')\n\"crystalrocks!\".each_char do |c|\n  root.add c\nend\n\nroot.print # => !$accklorrssty\nputs\n"
  },
  {
    "path": "samples/wordcount.cr",
    "content": "# Ported from http://arthurtw.github.io/2015/01/12/quick-comparison-nim-vs-rust.html\n\nrequire \"option_parser\"\n\ndef do_work(in_filenames, output_filename, ignore_case)\n  if in_filenames.empty?\n    in_files = [STDIN]\n  else\n    in_files = in_filenames.map { |name| File.open(name, \"r\") }\n  end\n\n  if output_filename\n    out_file = File.open(output_filename, \"w\")\n  else\n    out_file = STDOUT\n  end\n\n  counts = Hash(String, Int32).new(0)\n\n  in_files.each do |in_file|\n    in_file.each_line do |line|\n      line = line.downcase if ignore_case\n      line.scan(/\\w+/) do |match|\n        counts[match[0]] += 1\n      end\n    end\n  end\n\n  entries = counts.to_a.sort_by! &.[0]\n  entries.each do |(word, count)|\n    out_file.puts \"#{count}\\t#{word}\"\n  end\nend\n\noutput_filename = nil\nignore_case = false\n\nOptionParser.parse do |opts|\n  opts.banner = \"Usage: wordcount [OPTIONS] [FILES]\"\n  opts.on(\"-o NAME\", \"set output filename\") do |filename|\n    output_filename = filename\n  end\n  opts.on(\"-i\", \"--ignore-case\", \"ignore case\") do\n    ignore_case = true\n  end\n  opts.on(\"-h\", \"--help\", \"print this help menu\") do\n    puts opts\n  end\nend\n\nin_filenames = ARGV\n\ndo_work in_filenames, output_filename, ignore_case\n"
  },
  {
    "path": "scripts/docs-versions.sh",
    "content": "#! /usr/bin/env sh\n\ngit tag --list | \\\ngrep -v -E '0\\.1?[0-9]\\.' | \\\ngrep '^[0-9]' | \\\nsort -rV | \\\nawk '\n  BEGIN {\n    print \"{\"\n    print \"  \\\"versions\\\": [\"\n    printf \"    {\\\"name\\\": \\\"nightly\\\", \\\"url\\\": \\\"/api/master/\\\", \\\"released\\\": false}\"\n  }\n\n  {\n    printf \",\\n    {\\\"name\\\": \\\"\" $1 \"\\\", \\\"url\\\": \\\"/api/\" $1 \"/\\\"}\"\n  }\n\n  END {\n    print \"\\n  ]\"\n    print \"}\"\n  }\n'\n"
  },
  {
    "path": "scripts/generate_data.mk",
    "content": "\n## Run all data generators\n##   $ make -f scripts/generate_data.mk\n## Generate time zone database for stdlib specs\n##   $ make zoneinfo -f scripts/generate_data.mk\n\nTZDB_VERSION := 2025c\n\nifeq ($(OS),Windows_NT)\n  BIN_CRYSTAL=bin\\crystal\nelse\n  BIN_CRYSTAL=bin/crystal\nendif\n\n.PHONY: all\nall: zoneinfo ## Run all generators\n\t$(BIN_CRYSTAL) run scripts/generate_grapheme_break_specs.cr\n\t$(BIN_CRYSTAL) run scripts/generate_grapheme_properties.cr\n\t$(BIN_CRYSTAL) run scripts/generate_ssl_server_defaults.cr\n\t$(BIN_CRYSTAL) run scripts/generate_unicode_data.cr\n\t$(BIN_CRYSTAL) run scripts/generate_windows_zone_names.cr\n\t$(BIN_CRYSTAL) run scripts/generate_html_entities.cr\n\n.PHONY: zoneinfo\nzoneinfo: spec/std/data/zoneinfo.zip ## Generate time zone database for stdlib specs\n\nspec/std/data/zoneinfo.zip:\n\twget -O- https://data.iana.org/time-zones/tzdb-latest.tar.lz | tar -x --lzip\n\t$(MAKE) -C 'tzdb-$(TZDB_VERSION)' zones REDO=posix_only DESTDIR=stage TZDIR=/ ZFLAGS='-b fat'\n\trm -f spec/std/data/zoneinfo.zip\n\tcd 'tzdb-$(TZDB_VERSION)/stage' && zip -0 -r ../../spec/std/data/zoneinfo.zip .\n\n.PHONY: help\nhelp: ## Show this help\n\t@echo\n\t@printf '\\033[34mtargets:\\033[0m\\n'\n\t@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\\\n\t\tsort |\\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\t@echo\n\t@printf '\\033[34moptional variables:\\033[0m\\n'\n\t@grep -hE '^[a-zA-Z_-]+ \\?=.*?## .*$$' $(MAKEFILE_LIST) |\\\n\t\tsort |\\\n\t\tawk 'BEGIN {FS = \" \\\\?=.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\t@echo\n\t@printf '\\033[34mrecipes:\\033[0m\\n'\n\t@grep -hE '^##.*$$' $(MAKEFILE_LIST) |\\\n\t\tawk 'BEGIN {FS = \"## \"}; /^## [a-zA-Z_-]/ {printf \"  \\033[36m%s\\033[0m\\n\", $$2}; /^##  / {printf \"  %s\\n\", $$2}'\n"
  },
  {
    "path": "scripts/generate_glob_specs.sh",
    "content": "#!/bin/env bash\n\n# This tool generates the tests in spec/std/file/match-fast-glob_spec.cr\n# from https://github.com/oxc-project/fast-glob/blob/main/tests/test.rs\n# converted to Crystal\n\nset -euo pipefail\n\ntarget=spec/std/file/match-fast-glob_spec.cr\nURL=\"https://raw.githubusercontent.com/oxc-project/fast-glob/refs/heads/main/tests/test.rs\"\n\ncurl -L \"$URL\" |\n(\n  echo '\n# This file was automatically generated by running:\n#\n#   scripts/generate_glob_specs.cr\n#\n# DO NOT EDIT\n'\n\necho \"\n# These tests are autogenerated from $URL\n# They are are  collection of tests from bash and micromatch\n# https://github.com/micromatch/picomatch/blob/master/test/bash.js.\n\"\n\n  echo '\nrequire \"spec\"\n\nprivate def assert_file_matches(pattern, path : String, *, file = __FILE__, line = __LINE__)\n  File.match?(pattern, path).should be_true, file: file, line: line\n  File.match?(pattern, Path.posix(path)).should be_true, file: file, line: line\n  File.match?(pattern, Path.posix(path).to_windows(mappings: false)).should be_true, file: file, line: line\nend\n\nprivate def refute_file_matches(pattern, path : String, *, file = __FILE__, line = __LINE__)\n  File.match?(pattern, path).should be_false, file: file, line: line\n  File.match?(pattern, Path.posix(path)).should be_false, file: file, line: line\n  File.match?(pattern, Path.posix(path).to_windows(mappings: false)).should be_false, file: file, line: line\nend\n'\n\n  echo 'describe \"File .match? bash tests\" do'\n\n  sed -E '1,5d' |\n  sed '/let patterns/,/#\\[test\\]/d; s/fn not_paired_braces/end\\n\\nfn not_paired_braces/' | # drop complex patterns implementation\n  sed '/fn fuzz_tests/,$d' | # drop complex fuzz tests implementation\n  sed '/^\\s*#\\[/d' |\n  sed -E 's/fn (.*)\\(\\) \\{/it \"\\1\" do/' |\n  sed -E 's/\\}$/end/' |\n  sed -E 's|^\\s*//|#|' |\n  sed -E 's/assert!\\(!glob_match\\(\"(.*)\", \"(.*)\"\\)\\);/refute_file_matches \"\\1\", \"\\2\"/' |\n  sed -E 's/assert!\\(glob_match\\(\"(.*)\", \"(.*)\"\\)\\);/assert_file_matches \"\\1\", \"\\2\"/' |\n  # multiline assertions:\n  sed -E 's/assert!\\(glob_match\\(/assert_file_matches(/' |\n  sed -E 's/assert!\\(!glob_match\\(/refute_file_matches(/' |\n  sed -E 's/\\)\\);/)/' |\n  sed -E 's/let //' |\n  sed -E 's/it \"negation\"/pending \"negation\"/' | # File.match? currently does not support negated patterns\n  sed -E '/it \"generic_input\"/,/end$/d' # Generic input tests are specific to Rust types\n\n  echo 'end'\n) > \"$target\"\n\ncrystal tool format \"$target\"\n"
  },
  {
    "path": "scripts/generate_grapheme_break_specs.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This script generates the file spec/std/string/graphemes_break_spec.cr\n# that contains test cases for Unicode grapheme clusters based on the default\n# Grapheme_Cluster_Break Test\n\n# http://www.unicode.org/Public/x.y.z/ucd/auxiliary/GraphemeBreakTest.txt\n\nrequire \"http/client\"\nrequire \"../src/compiler/crystal/formatter\"\n\nUCD_ROOT = \"http://www.unicode.org/Public/#{Unicode::VERSION}/ucd/\"\n\nurl = \"#{UCD_ROOT}auxiliary/GraphemeBreakTest.txt\"\n\npath = \"#{__DIR__}/../spec/std/string/grapheme_break_spec.cr\"\n\ndef string_or_char(string)\n  if string.size == 1\n    string[0]\n  else\n    string\n  end\nend\n\nFile.open(path, \"w\") do |file|\n  file.puts <<-CRYSTAL\n    # This file was automatically generated by running:\n    #\n    #   scripts/generate_grapheme_break_spec.cr\n    #\n    # See https://www.unicode.org/license.html for the Unicode license agreement.\n    # DO NOT EDIT\n\n    require \"./spec_helper\"\n\n    describe \"String#each_grapheme\" do\n    CRYSTAL\n  HTTP::Client.get(url).body.each_line do |line|\n    next if line.starts_with?('#')\n\n    format, _, comment = line.partition('#')\n\n    # TODO: implement grapheme boundary rule GB9c in UAX29\n    pending = comment.includes?(\"[9.3]\")\n\n    graphemes = [] of String | Char\n    string = String.build do |io|\n      grapheme = String::Builder.new\n      format.split.in_groups_of(2) do |ary|\n        operator, codepoint = ary\n        break if codepoint.nil?\n        char = codepoint.to_i(16).chr\n        io << char\n        case operator\n        when \"÷\"\n          unless grapheme.empty?\n            graphemes << string_or_char(grapheme.to_s)\n          end\n          grapheme = String::Builder.new\n        when \"×\"\n        else raise \"Unexpected operator #{operator.inspect}\"\n        end\n        grapheme << char\n      end\n      graphemes << string_or_char(grapheme.to_s)\n    end\n\n    file.puts \"  #{%(pending \"GB9c\" { ) if pending} it_iterates_graphemes #{string.dump}, [#{graphemes.join(\", \", &.dump)}] #{\" }\" if pending} # #{comment}\"\n  end\n  file.puts \"end\"\nend\n\n`crystal tool format #{path}`\n"
  },
  {
    "path": "scripts/generate_grapheme_properties.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This script generates the file src/string/grapheme/properties.cr\n# that contains compact representations of the GraphemeBreakProperty.txt and emoji-data.txt\n# file from the unicode specification.\n\nrequire \"http/client\"\nrequire \"ecr\"\n\nrecord RRange, low : Int32, high : Int32, prop : String\n\nUCD_ROOT = \"http://www.unicode.org/Public/#{Unicode::VERSION}/ucd/\"\n\ndef shapeup(arr)\n  i = 0\n  to_del = Array(Int32).new\n  while i < arr.size - 1\n    if arr[i].high + 1 == arr[i + 1].low\n      low = arr[i].low\n      to_del << i\n      arr.delete_at(i)\n      arr[i] = RRange.new(low, arr[i].high, arr[i].prop)\n      i -= 1\n    end\n    i += 1\n  end\n  arr\nend\n\ndef parse_graphemes_data(body)\n  result = Hash(String, Array(RRange)).new\n  body.each_line do |line|\n    next unless line = line.strip.presence\n    next if line.starts_with?('#')\n    parts = line.split(';')\n    next unless parts.size >= 2\n\n    fields = parts.first.strip.split(\"..\")\n    f1 = fields.first.to_i(16)\n    f2 = fields.size > 1 ? fields[1].to_i(16) : f1\n\n    prop = parts[1].split('#').first.strip.gsub('_', \"\")\n\n    (result[prop] ||= Array(RRange).new) << RRange.new(f1, f2, prop)\n  end\n  result.transform_values { |v| shapeup(v) }\nend\n\ndef parse_emoji(body)\n  emoji = Array(RRange).new\n  body.each_line do |line|\n    next unless line = line.strip.presence\n    next if line.starts_with?('#')\n    next unless line.includes?(\"; Extended_Pictographic\")\n\n    data = line.split.first.split(';')\n    fields = data.first.split(\"..\")\n    f1 = fields.first.to_i(16)\n    f2 = fields.size > 1 ? fields[1].to_i(16) : f1\n    next if f2 < 0xFF\n    emoji << RRange.new(f1, f2, \"ExtendedPictographic\")\n  end\n  shapeup(emoji)\nend\n\nbody = HTTP::Client.get(\"#{UCD_ROOT}auxiliary/GraphemeBreakProperty.txt\").body\nprops = parse_graphemes_data(body)\n\nbody = HTTP::Client.get(\"#{UCD_ROOT}emoji/emoji-data.txt\").body\nprops[\"ExtendedPictographic\"] = parse_emoji(body)\n\nprops_data = props.values.flatten.sort! { |a, b| a.low <=> b.low }\n\npath = \"#{__DIR__}/../src/string/grapheme/properties.cr\"\nFile.open(path, \"w\") do |file|\n  ECR.embed \"#{__DIR__}/grapheme_properties.ecr\", file\nend\n\n`crystal tool format #{path}`\n"
  },
  {
    "path": "scripts/generate_html_entities.cr",
    "content": "#! /usr/bin/env crystal\n\nrequire \"http\"\nrequire \"json\"\nrequire \"ecr\"\n\nrecord Entity, characters : String, codepoints : Array(Int32) do\n  include JSON::Serializable\n  include JSON::Serializable::Strict\nend\n\nsingle_char_entities = [] of {String, Entity}\ndouble_char_entities = [] of {String, Entity}\n\nHTTP::Client.get(\"https://html.spec.whatwg.org/entities.json\") do |res|\n  Hash(String, Entity).from_json(res.body_io).each do |name, entity|\n    name = name.rchop(';').lchop?('&') || raise \"Entity does not begin with &\"\n\n    entities =\n      case entity.codepoints.size\n      when 1; single_char_entities\n      when 2; double_char_entities\n      else    raise \"Unknown entity codepoint size\"\n      end\n\n    entities << {name, entity}\n  end\nend\n\nsingle_char_entities.uniq!(&.first).sort_by!(&.first)\ndouble_char_entities.uniq!(&.first).sort_by!(&.first)\n\nmax_entity_name_size = {\n  single_char_entities.max_of { |name, _| name.size },\n  double_char_entities.max_of { |name, _| name.size },\n}.max\n\npath = \"#{__DIR__}/../src/html/entities.cr\"\nFile.open(path, \"w\") do |file|\n  ECR.embed \"#{__DIR__}/html_entities.ecr\", file\nend\n\n`crystal tool format #{path}`\n"
  },
  {
    "path": "scripts/generate_llvm_version_info.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This script generates the `lib/llvm_VERSION` file from LLVM-C.dll, needed for\n# dynamically linking against LLVM on Windows. This is only needed when using an\n# LLVM installation different from the one bundled with Crystal.\n\nrequire \"c/libloaderapi\"\nrequire \"llvm/lib_llvm/config\"\n\ndef find_dll_in_env_path\n  ENV[\"PATH\"]?.try &.split(Process::PATH_DELIMITER, remove_empty: true) do |path|\n    dll_path = File.join(path, \"LLVM-C.dll\")\n    return dll_path if File.exists?(File.join(path, \"LLVM-C.dll\"))\n  end\nend\n\nunless dll_fname = ARGV.shift? || find_dll_in_env_path\n  abort \"Error: Cannot locate LLVM-C.dll, pass its absolute path as a command-line argument or ensure it is available in the PATH environment variable\"\nend\n\nunless dll = LibC.LoadLibraryExW(dll_fname.check_no_null_byte.to_utf16, nil, 0)\n  abort \"Error: Failed to load DLL at #{dll_fname}\"\nend\n\nbegin\n  unless llvm_get_version = LibC.GetProcAddress(dll, \"LLVMGetVersion\")\n    abort \"Error: Failed to resolve LLVMGetVersion\"\n  end\n\n  llvm_get_version = Proc(LibC::UInt*, LibC::UInt*, LibC::UInt*, Nil).new(llvm_get_version, Pointer(Void).null)\n  major = uninitialized LibC::UInt\n  minor = uninitialized LibC::UInt\n  patch = uninitialized LibC::UInt\n  llvm_get_version.call(pointerof(major), pointerof(minor), pointerof(patch))\n\n  targets_built = LibLLVM::ALL_TARGETS.select do |target|\n    LibC.GetProcAddress(dll, \"LLVMInitialize#{target}Target\") && LibC.GetProcAddress(dll, \"LLVMInitialize#{target}TargetInfo\")\n  end\n\n  # The list of required system libraries are hardcoded in:\n  # https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/CMakeLists.txt\n  # There is no way to infer them from `dumpbin /dependents` alone, because that\n  # command lists DLLs only, whereas some of these libraries are purely static.\n  system_libs = %w(psapi shell32 ole32 uuid advapi32)\n  # https://github.com/llvm/llvm-project/commit/a5ffabce98a4b2e9d69009fa3e60f2b154100860\n  system_libs << \"ws2_32\" if {major, minor, patch} >= {18, 0, 0}\n  # https://github.com/llvm/llvm-project/commit/cb7690af09b95bb944baf1b5a9ffb18f86c12130\n  system_libs << \"ntdll\" if {major, minor, patch} >= {19, 0, 0}\n\n  puts \"#{major}.#{minor}.#{patch}\"\n  puts targets_built.join(' ')\n  puts system_libs.join(' ', &.+ \".lib\")\nensure\n  LibC.FreeLibrary(dll)\nend\n"
  },
  {
    "path": "scripts/generate_object_properties.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This script generates the `src/object/properties.cr` file with the whole set\n# of `[class_](getter|setter|property)[?!]` macros to avoid duplicating the\n# implementations. Having an external script avoids the runtime cost of having\n# macros generating AST calls to other macros that must expanded again.\n\nstruct Generator\n  def initialize(@file : File, @macro_prefix : String, @method_prefix : String, @var_prefix : String, @doc_prefix : String)\n  end\n\n  def puts\n    @file.puts\n  end\n\n  def puts(message)\n    @file.puts(message)\n  end\n\n  def def_vars\n    def_vars do\n      <<-TEXT\n              {% if block %}\n                #{@var_prefix}{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n              {% else %}\n                #{@var_prefix}{{name}}\n              {% end %}\n      TEXT\n    end\n  end\n\n  def def_vars_no_macro_block\n    def_vars { \"#{@var_prefix}{{name}}\" }\n  end\n\n  def def_vars(&)\n    <<-TEXT\n          {% if name.is_a?(TypeDeclaration) %}\n            {% var_name = name.var.id %}\n            {% type = name.type %}\n            #{yield.lstrip}\n          {% elsif name.is_a?(Assign) %}\n            {% var_name = name.target %}\n            {% type = nil %}\n            #{@var_prefix}{{name}}\n          {% else %}\n            {% var_name = name.id %}\n            {% type = nil %}\n          {% end %}\n\n    TEXT\n  end\n\n  def def_vars!\n    <<-TEXT\n          {% if name.is_a?(TypeDeclaration) %}\n            {% var_name = name.var.id %}\n            {% type = name.type %}\n            #{@var_prefix}{{name}}?\n          {% else %}\n            {% var_name = name.id %}\n            {% type = nil %}\n          {% end %}\n\n    TEXT\n  end\n\n  def def_getter(suffix = \"\")\n    <<-TEXT\n          def #{@method_prefix}{{var_name}}#{suffix} {% if type %} : {{type}} {% end %}\n            {% if block %}\n              if (%value = #{@var_prefix}{{var_name}}).nil?\n                #{@var_prefix}{{var_name}} = {{yield}}\n              else\n                %value\n              end\n            {% else %}\n              #{@var_prefix}{{var_name}}\n            {% end %}\n          end\n\n    TEXT\n  end\n\n  def def_getter!\n    <<-TEXT\n          def #{@method_prefix}{{var_name}}? {% if type %} : {{type}}? {% end %}\n            #{@var_prefix}{{var_name}}\n          end\n\n          def #{@method_prefix}{{var_name}} {% if type %} : {{type}} {% end %}\n            if (%value = #{@var_prefix}{{var_name}}).nil?\n              ::raise ::NilAssertionError.new(\"{{@type.id}}{{#{@doc_prefix.inspect}.id}}{{var_name}} cannot be nil\")\n            else\n              %value\n            end\n          end\n\n    TEXT\n  end\n\n  def def_setter\n    <<-TEXT\n          def #{@method_prefix}{{var_name}}=(#{@var_prefix}{{var_name}}{% if type %} : {{type}} {% end %})\n          end\n\n    TEXT\n  end\n\n  def gen_property_macros\n    puts <<-TEXT\n      # Generates both `#{@macro_prefix}getter` and `#{@macro_prefix}setter`\n      # methods to access instance variables.\n      #\n      # Refer to the aforementioned macros for details.\n      macro #{@macro_prefix}property(*names, &block)\n        {% for name in names %}\n    #{def_vars}\n    #{def_getter}\n    #{def_setter}\n        {% end %}\n      end\n\n      # Generates both `#{@macro_prefix}getter?` and `#{@macro_prefix}setter`\n      # methods to access instance variables.\n      #\n      # Refer to the aforementioned macros for details.\n      macro #{@macro_prefix}property?(*names, &block)\n        {% for name in names %}\n    #{def_vars}\n    #{def_getter \"?\"}\n    #{def_setter}\n        {% end %}\n      end\n\n      # Generates both `#{@macro_prefix}getter!` and `#{@macro_prefix}setter`\n      # methods to access instance variables.\n      #\n      # Refer to the aforementioned macros for details.\n      macro #{@macro_prefix}property!(*names)\n        {% for name in names %}\n    #{def_vars!}\n    #{def_getter!}\n    #{def_setter}\n        {% end %}\n      end\n    TEXT\n  end\nend\n\ndirectory = File.expand_path(\"../src/object\", __DIR__)\nDir.mkdir(directory) unless Dir.exists?(directory)\n\noutput = File.join(directory, \"properties.cr\")\nFile.open(output, \"w\") do |f|\n  f.puts \"# This file was automatically generated by running:\"\n  f.puts \"#\"\n  f.puts \"#   scripts/generate_object_properties.cr\"\n  f.puts \"#\"\n  f.puts \"# DO NOT EDIT\"\n  f.puts\n  f.puts \"class Object\"\n\n  g = Generator.new(f, \"\", \"\", \"@\", \"#\")\n\n  f.puts <<-TEXT\n    # Defines getter methods to access instance variables.\n    #\n    # Refer to [Getters](#getters) for details.\n    macro getter(*names, &block)\n      {% for name in names %}\n  #{g.def_vars}\n  #{g.def_getter}\n      {% end %}\n    end\n\n    # Identical to `getter` but defines query methods.\n    #\n    # For example, writing:\n    #\n    # ```\n    # class Robot\n    #   getter? working\n    # end\n    # ```\n    #\n    # Is equivalent to writing:\n    #\n    # ```\n    # class Robot\n    #   def working?\n    #     @working\n    #   end\n    # end\n    # ```\n    #\n    # Refer to [Getters](#getters) for general details.\n    macro getter?(*names, &block)\n      {% for name in names %}\n  #{g.def_vars}\n  #{g.def_getter \"?\"}\n      {% end %}\n    end\n\n    # Similar to `getter` but defines both raise-on-nil methods as well as query\n    # methods that return a nilable value.\n    #\n    # If a type is specified, then it will become a nilable type (union of the\n    # type and `Nil`). Unlike the other `getter` methods the value is always\n    # initialized to `nil`. There are no initial value or lazy initialization.\n    #\n    # For example, writing:\n    #\n    # ```\n    # class Robot\n    #   getter! name : String\n    # end\n    # ```\n    #\n    # Is equivalent to writing:\n    #\n    # ```\n    # class Robot\n    #   @name : String?\n    #\n    #   def name? : String?\n    #     @name\n    #   end\n    #\n    #   def name : String\n    #     @name.not_nil!(\"Robot#name cannot be nil\")\n    #   end\n    # end\n    # ```\n    #\n    # Refer to [Getters](#getters) for general details.\n    macro getter!(*names)\n      {% for name in names %}\n  #{g.def_vars!}\n  #{g.def_getter!}\n      {% end %}\n    end\n\n    # Generates setter methods to set instance variables.\n    #\n    # Refer to [Setters](#setters) for general details.\n    macro setter(*names)\n      {% for name in names %}\n  #{g.def_vars_no_macro_block}\n  #{g.def_setter}\n      {% end %}\n    end\n  TEXT\n\n  g.gen_property_macros\n\n  g = Generator.new(f, \"class_\", \"self.\", \"@@\", \".\")\n\n  f.puts <<-TEXT\n    # Defines getter methods to access class variables.\n    #\n    # For example, writing:\n    #\n    # ```\n    # class Robot\n    #   class_getter backend\n    # end\n    # ```\n    #\n    # Is equivalent to writing:\n    #\n    # ```\n    # class Robot\n    #   def self.backend\n    #     @@backend\n    #   end\n    # end\n    # ```\n    #\n    # Refer to [Getters](#getters) for details.\n    macro class_getter(*names, &block)\n      {% for name in names %}\n  #{g.def_vars}\n  #{g.def_getter}\n      {% end %}\n    end\n\n    # Identical to `class_getter` but defines query methods.\n    #\n    # For example, writing:\n    #\n    # ```\n    # class Robot\n    #   class_getter? backend\n    # end\n    # ```\n    #\n    # Is equivalent to writing:\n    #\n    # ```\n    # class Robot\n    #   def self.backend?\n    #     @@backend\n    #   end\n    # end\n    # ```\n    #\n    # Refer to [Getters](#getters) for general details.\n    macro class_getter?(*names, &block)\n      {% for name in names %}\n  #{g.def_vars}\n  #{g.def_getter \"?\"}\n      {% end %}\n    end\n\n    # Similar to `class_getter` but defines both raise-on-nil methods as well as\n    # query methods that return a nilable value.\n    #\n    # If a type is specified, then it will become a nilable type (union of the\n    # type and `Nil`). Unlike with `class_getter` the value is always initialized\n    # to `nil`. There are no initial value or lazy initialization.\n    #\n    # For example, writing:\n    #\n    # ```\n    # class Robot\n    #   class_getter! backend : String\n    # end\n    # ```\n    #\n    # Is equivalent to writing:\n    #\n    # ```\n    # class Robot\n    #   @@backend : String?\n    #\n    #   def self.backend? : String?\n    #     @@backend\n    #   end\n    #\n    #   def backend : String\n    #     @@backend.not_nil!(\"Robot.backend cannot be nil\")\n    #   end\n    # end\n    # ```\n    #\n    # Refer to [Getters](#getters) for general details.\n    macro class_getter!(*names)\n      {% for name in names %}\n  #{g.def_vars!}\n  #{g.def_getter!}\n      {% end %}\n    end\n\n    # Generates setter methods to set class variables.\n    #\n    # For example, writing:\n    #\n    # ```\n    # class Robot\n    #   class_setter factories\n    # end\n    # ```\n    #\n    # Is equivalent to writing:\n    #\n    # ```\n    # class Robot\n    #   @@factories\n    #\n    #   def self.factories=(@@factories)\n    #   end\n    # end\n    # ```\n    #\n    # Refer to [Setters](#setters) for general details.\n    macro class_setter(*names)\n      {% for name in names %}\n  #{g.def_vars_no_macro_block}\n  #{g.def_setter}\n      {% end %}\n    end\n  TEXT\n\n  g.gen_property_macros\n\n  f.puts \"end\"\nend\n"
  },
  {
    "path": "scripts/generate_ssl_server_defaults.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This helper fetches the Mozilla recommendations for default TLS ciphers\n# (https://wiki.mozilla.org/Security/Server_Side_TLS) and automatically places\n# them in src/openssl/ssl/defaults.cr\n\nrequire \"http\"\nrequire \"json\"\n\nstruct Configuration\n  include JSON::Serializable\n\n  getter oldest_clients : Array(String)\n  getter ciphersuites : Array(String)\n  @[JSON::Field(root: \"openssl\")]\n  getter ciphers : Array(String)\nend\n\nstruct Guidelines\n  include JSON::Serializable\n\n  @[JSON::Field(converter: String::RawConverter)]\n  getter version : String\n  getter href : String\n  getter configurations : Hash(String, Configuration)\nend\n\nurl = ARGV.shift? || \"https://ssl-config.mozilla.org/guidelines/latest.json\"\nDEFAULTS_FILE = File.expand_path(\"../src/openssl/ssl/defaults.cr\", __DIR__)\n\nguidelines = Guidelines.from_json(HTTP::Client.get(url).body)\ndisabled_ciphers = %w(!RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS)\n\nFile.open(DEFAULTS_FILE, \"w\") do |file|\n  file.puts <<-CRYSTAL\n  # THIS FILE WAS AUTOMATICALLY GENERATED BY scripts/#{File.basename(__FILE__)}\n  # on #{Time.utc}.\n\n  abstract class OpenSSL::SSL::Context\n  CRYSTAL\n\n  guidelines.configurations.join(file, '\\n') do |(level, configuration)|\n    clients = configuration.oldest_clients\n    ciphersuites = configuration.ciphersuites\n    ciphers = configuration.ciphers\n    all_ciphers = ciphersuites + ciphers + disabled_ciphers\n\n    file.puts <<-CRYSTAL\n      # The list of secure ciphers on **#{level}** compatibility level as per Mozilla\n      # recommendations.\n      #\n      # The oldest clients supported by this configuration are:\n      # * #{clients.join(\"\\n  # * \")}\n      #\n      # This list represents version #{guidelines.version} of the #{level} configuration\n      # available at #{guidelines.href}.\n      #\n      # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n      @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n      CIPHERS_#{level.upcase} = \"#{all_ciphers.join(\":\")}\"\n\n      # The list of secure ciphersuites on **#{level}** compatibility level as per Mozilla\n      # recommendations.\n      #\n      # The oldest clients supported by this configuration are:\n      # * #{clients.join(\"\\n  # * \")}\n      #\n      # This list represents version #{guidelines.version} of the #{level} configuration\n      # available at #{guidelines.href}.\n      #\n      # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n      @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n      CIPHER_SUITES_#{level.upcase} = \"#{ciphersuites.join(\":\")}\"\n    CRYSTAL\n  end\n  file.puts \"end\"\nend\n"
  },
  {
    "path": "scripts/generate_unicode_data.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This script generates the file src/unicode/data.cr\n# that contains compact representations of the UnicodeData.txt\n# file from the unicode specification.\n\nrequire \"http/client\"\nrequire \"ecr\"\nrequire \"../src/compiler/crystal/formatter\"\n\nUCD_ROOT = \"http://www.unicode.org/Public/#{Unicode::VERSION}/ucd/\"\n\nenum DecompositionType\n  None\n  Canonical\n  Compatibility\nend\n\n# Each entry in UnicodeData.txt\n# (some info is missing but we don't use it yet)\nrecord Entry,\n  codepoint : Int32,\n  name : String,\n  general_category : String,\n  decomposition_type : DecompositionType,\n  decomposition_mapping : Array(Int32)?,\n  upcase : Int32?,\n  downcase : Int32?,\n  casefold : Int32?\n\nrecord SpecialCase,\n  codepoint : Int32,\n  value : Array(Int32)\n\nrecord CaseRange, low : Int32, high : Int32, delta : Int32\nrecord AlternateRange, low : Int32, high : Int32\nrecord Stride, low : Int32, high : Int32, stride : Int32\nrecord CanonicalCombiningClassRange, low : Int32, high : Int32, ccc : UInt8\nrecord QuickCheckRange, low : Int32, high : Int32, result : Unicode::QuickCheckResult\n\ndef case_ranges(entries, &block)\n  ranges = [] of CaseRange\n  first_codepoint = nil\n  last_codepoint = nil\n  first_match = nil\n  last_match = nil\n  entries.each do |entry|\n    codepoint = entry.codepoint\n    match = yield entry\n    if match\n      if last_codepoint == codepoint - 1 && last_match == match - 1\n        # Continue streak\n      else\n        if last_codepoint && last_match\n          ranges << CaseRange.new(first_codepoint.not_nil!, last_codepoint, first_match.not_nil! - first_codepoint.not_nil!)\n        end\n        first_codepoint = codepoint\n        first_match = match\n      end\n    else\n      if last_codepoint && last_match\n        ranges << CaseRange.new(first_codepoint.not_nil!, last_codepoint, first_match.not_nil! - first_codepoint.not_nil!)\n      end\n    end\n\n    last_codepoint = codepoint\n    last_match = match\n  end\n  ranges\nend\n\ndef alternate_ranges(ranges)\n  alternate = [] of AlternateRange\n\n  first_codepoint = nil\n  last_codepoint = nil\n\n  ranges.each do |range|\n    codepoint = range.low\n    if last_codepoint == codepoint - 2\n      # Continue streak\n    else\n      if first_codepoint\n        alternate << new_alternate_range(first_codepoint, last_codepoint)\n      end\n      first_codepoint = codepoint\n    end\n\n    last_codepoint = codepoint\n  end\n\n  if first_codepoint\n    alternate << new_alternate_range(first_codepoint, last_codepoint)\n  end\n\n  alternate\nend\n\ndef new_alternate_range(first_codepoint, last_codepoint)\n  # The last codepoint is the one for the uppercase letter and we\n  # need to also consider the next codepoint for the lowercase one.\n  AlternateRange.new(first_codepoint, last_codepoint.not_nil! + 1)\nend\n\ndef strides(entries, targets, &)\n  strides = [] of Stride\n\n  entries = entries.select { |entry| targets.includes?(yield entry) }\n\n  first_entry = nil\n  last_entry = nil\n  stride = nil\n\n  entries.each do |entry|\n    if first_entry\n      if last_entry\n        current_stride = entry.codepoint - last_entry.codepoint\n        if current_stride == stride\n          # Continue stride\n        else\n          if first_entry == last_entry\n            stride = current_stride\n          else\n            stride = 1 if first_entry.name.ends_with?(\"First>\") && last_entry.name.ends_with?(\"Last>\")\n            strides << Stride.new(first_entry.codepoint, last_entry.codepoint, stride.not_nil!)\n            first_entry = entry\n            stride = nil\n          end\n        end\n      end\n    else\n      first_entry = entry\n    end\n\n    last_entry = entry\n  end\n\n  if first_entry && last_entry\n    if stride\n      stride = 1 if first_entry.name.ends_with?(\"First>\") && last_entry.name.ends_with?(\"Last>\")\n      strides << Stride.new(first_entry.codepoint, last_entry.codepoint, stride)\n    else\n      strides << Stride.new(first_entry.codepoint, last_entry.codepoint, 1)\n    end\n  end\n\n  strides\nend\n\nentries = [] of Entry\nspecial_cases_downcase = [] of SpecialCase\nspecial_cases_titlecase = [] of SpecialCase\nspecial_cases_upcase = [] of SpecialCase\nspecial_cases_casefold = [] of SpecialCase\ncasefold_mapping = Hash(Int32, Int32).new\ncanonical_combining_classes = [] of CanonicalCombiningClassRange\nfull_composition_exclusions = Set(Int32).new\nquick_checks = Unicode::NormalizationForm.values.to_h { |kind| {kind, Array(QuickCheckRange).new} }\n\nurl = \"#{UCD_ROOT}CaseFolding.txt\"\nbody = HTTP::Client.get(url).body\nbody.each_line do |line|\n  line = line.strip\n  next if line.empty?\n  next if line.starts_with?('#')\n\n  pieces = line.split(';')\n  codepoint = pieces[0].to_i(16)\n  status = pieces[1].strip[0]\n  casefold = pieces[2].split.map(&.to_i(16))\n  next if status != 'C' && status != 'F' # casefold uses full case folding (C and F)\n  if casefold.size == 1\n    casefold_mapping[codepoint] = casefold[0]\n    casefold = nil\n  end\n  if casefold\n    while casefold.size < 3\n      casefold << 0\n    end\n    special_cases_casefold << SpecialCase.new(codepoint, casefold)\n  end\nend\n\nurl = \"#{UCD_ROOT}UnicodeData.txt\"\nbody = HTTP::Client.get(url).body\nbody.each_line do |line|\n  line = line.strip\n  next if line.empty?\n\n  pieces = line.split(';')\n  codepoint = pieces[0].to_i(16)\n  name = pieces[1]\n  general_category = pieces[2]\n  # don't read CanonicalCombiningClass here; the derived properties file has\n  # exact ranges\n  decomposition = pieces[5]\n  if decomposition.starts_with?('<')\n    decomposition_mapping = decomposition.partition(\"> \")[2].split.map(&.to_i(16))\n    decomposition_type = DecompositionType::Compatibility\n  else\n    decomposition_mapping = decomposition.presence.try &.split.map(&.to_i(16))\n    decomposition_type = decomposition_mapping.nil? ? DecompositionType::None : DecompositionType::Canonical\n  end\n  upcase = pieces[12].to_i?(16)\n  downcase = pieces[13].to_i?(16)\n  titlecase = pieces[14].to_i?(16)\n  casefold = casefold_mapping[codepoint]?\n  entries << Entry.new(\n    codepoint: codepoint,\n    name: name,\n    general_category: general_category,\n    decomposition_type: decomposition_type,\n    decomposition_mapping: decomposition_mapping,\n    upcase: upcase,\n    downcase: downcase,\n    casefold: casefold,\n  )\n  if titlecase && titlecase != upcase\n    special_cases_titlecase << SpecialCase.new(codepoint, [titlecase, 0, 0])\n  end\nend\n\nurl = \"#{UCD_ROOT}SpecialCasing.txt\"\nbody = HTTP::Client.get(url).body\nbody.each_line do |line|\n  line = line.strip\n  next if line.empty?\n  break if line.starts_with?(\"# Conditional Mappings\")\n  next if line.starts_with?('#')\n\n  pieces = line.split(';')\n  codepoint = pieces[0].to_i(16)\n\n  downcase = pieces[1].split.map(&.to_i(16))\n  if downcase.size > 1\n    while downcase.size < 3\n      downcase << 0\n    end\n    special_cases_downcase << SpecialCase.new(codepoint, downcase)\n  end\n\n  upcase = pieces[3].split.map(&.to_i(16))\n  if upcase.size > 1\n    while upcase.size < 3\n      upcase << 0\n    end\n    special_cases_upcase << SpecialCase.new(codepoint, upcase)\n  end\n\n  titlecase = pieces[2].split.map(&.to_i(16))\n  if titlecase.size > 1\n    while titlecase.size < 3\n      titlecase << 0\n    end\n    special_cases_titlecase << SpecialCase.new(codepoint, titlecase)\n  end\nend\n\nurl = \"#{UCD_ROOT}extracted/DerivedCombiningClass.txt\"\nbody = HTTP::Client.get(url).body\nbody.each_line do |line|\n  line = line.strip\n\n  if m = line.match(/^([0-9A-F]+)(?:\\.\\.([0-9A-F]+))?\\s*;\\s*(\\d+)/)\n    ccc = m[3].to_u8\n    next if ccc == 0\n    low = m[1].to_i(16)\n    high = m[2]?.try(&.to_i(16)) || low\n    canonical_combining_classes << CanonicalCombiningClassRange.new(low, high, ccc)\n  end\nend\n\nurl = \"#{UCD_ROOT}DerivedNormalizationProps.txt\"\nbody = HTTP::Client.get(url).body\nbody.each_line do |line|\n  line = line.strip\n  break if line.starts_with?(\"# Derived Property: Expands_On_NFD\")\n\n  if m = line.match(/^([0-9A-F]+)(?:\\.\\.([0-9A-F]+))?\\s*;\\s*Full_Composition_Exclusion/)\n    low = m[1].to_i(16)\n    high = m[2]?.try(&.to_i(16)) || low\n    (low..high).each { |codepoint| full_composition_exclusions << codepoint }\n  elsif m = line.match(/^([0-9A-F]+)(?:\\.\\.([0-9A-F]+))?\\s*;\\s*(NFC|NFD|NFKC|NFKD)_QC\\s*;\\s*(N|M)/)\n    low = m[1].to_i(16)\n    high = m[2]?.try(&.to_i(16)) || low\n    quick_check = quick_checks[Unicode::NormalizationForm.parse(m[3])]\n    result = m[4] == \"M\" ? Unicode::QuickCheckResult::Maybe : Unicode::QuickCheckResult::No\n    quick_check << QuickCheckRange.new(low, high, result)\n  end\nend\n\ndowncase_ranges = case_ranges entries, &.downcase\ndowncase_one_ranges, downcase_ranges = downcase_ranges.partition { |r| r.delta == 1 }\n\nupcase_ranges = case_ranges entries, &.upcase\nupcase_ranges.select! { |r| r.delta != -1 }\n\nalternate_ranges = alternate_ranges(downcase_one_ranges)\n\nspecial_cases_downcase.sort_by! &.codepoint\nspecial_cases_upcase.sort_by! &.codepoint\nspecial_cases_titlecase.reject! &.in?(special_cases_upcase)\nspecial_cases_titlecase.sort_by! &.codepoint\n\ncasefold_ranges = case_ranges entries, &.casefold\n\nall_strides = {} of String => Array(Stride)\ncategories = %w(Lu Ll Lt Lm Lo Mn Mc Me Nd Nl No Zs Zl Zp Cc Cf Cs Co Cn)\n\ncategories.each do |category|\n  all_strides[category] = strides entries, category, &.general_category\nend\n\ncanonical_combining_classes.sort_by! &.low\n\ncanonical_decompositions = entries.compact_map do |entry|\n  next unless entry.decomposition_type.canonical?\n  mapping = entry.decomposition_mapping.not_nil!\n  raise \"BUG: Mapping longer than 2 codepoints\" unless mapping.size <= 2\n  {entry.codepoint, mapping[0], mapping[1]? || 0}\nend\n\n# Instead of storing the codepoints for each compatibility decomposition as an\n# individual `Array`, we store all of them in a single `Array` and refer to its\n# subsequences using index and count.\ncompatibility_decomposition_data = [] of Int32\ncompatibility_decompositions = entries.compact_map do |entry|\n  next unless entry.decomposition_type.compatibility?\n  mapping = entry.decomposition_mapping.not_nil!\n\n  # We try to reuse any existing subsequences in the table that match this\n  # entry's decomposition mapping. This reduces the table size by over 40%,\n  # mainly due to singleton decompositions. It can be further optimized by\n  # solving the shortest common superstring problem.\n  index = (0..compatibility_decomposition_data.size - mapping.size).find do |i|\n    (0...mapping.size).all? do |j|\n      mapping[j] == compatibility_decomposition_data[i + j]\n    end\n  end\n  unless index\n    index = compatibility_decomposition_data.size\n    compatibility_decomposition_data.concat(mapping)\n  end\n\n  {entry.codepoint, index, mapping.size}\nend\n\ncanonical_compositions = canonical_decompositions.compact_map do |codepoint, first, second|\n  next if second == 0 || full_composition_exclusions.includes?(codepoint)\n  {(first.to_i64 << 21) | second, codepoint}\nend\n\nquick_checks.each_value &.sort_by! &.low\n\noutput = ECR.render \"#{__DIR__}/unicode_data.ecr\"\noutput = Crystal.format(output)\nFile.write(\"#{__DIR__}/../src/unicode/data.cr\", output)\n"
  },
  {
    "path": "scripts/generate_windows_zone_names.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This script generates the file src/crystal/system/win32/zone_names.cr\n# that contains mappings for windows time zone names based on the values\n# found in https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml\n\nrequire \"http/client\"\nrequire \"xml\"\nrequire \"../src/compiler/crystal/formatter\"\nrequire \"ecr\"\n\n# CLDR-18479 Update CLDR data to TZDB 2025b. (#4593)\nWINDOWS_ZONE_NAMES_SOURCE = \"https://raw.githubusercontent.com/unicode-org/cldr/f8369ba0795c79f3bac8eb89967eea359f77835e/common/supplemental/windowsZones.xml\"\nTARGET_FILE               = File.join(__DIR__, \"..\", \"src\", \"crystal\", \"system\", \"win32\", \"zone_names.cr\")\nZONEINFO_ZIP              = File.join(__DIR__, \"..\", \"spec\", \"std\", \"data\", \"zoneinfo.zip\")\n\nresponse = HTTP::Client.get(WINDOWS_ZONE_NAMES_SOURCE)\nxml = XML.parse(response.body)\nnodes = xml.xpath_nodes(\"/supplementalData/windowsZones/mapTimezones/mapZone\")\nentries = nodes.flat_map do |node|\n  windows_name = node[\"other\"]\n  territory = node[\"territory\"]\n  node[\"type\"].split(' ', remove_empty: true).map do |tzdata_name|\n    {tzdata_name, territory, windows_name}\n  end\nend.sort!\n\niana_to_windows_items = entries.compact_map do |tzdata_name, territory, windows_name|\n  location = Time::Location.load_from_dir_or_zip(tzdata_name, ZONEINFO_ZIP)\n  next unless location\n\n  time = Time.local(location).at_beginning_of_year\n  zone1 = time.zone\n  zone2 = (time + 6.months).zone\n\n  # southern hemisphere\n  if zone1.offset > zone2.offset\n    zone1, zone2 = zone2, zone1\n  end\n\n  {tzdata_name, windows_name, zone1.name, zone2.name}\nend.uniq!\n\nwindows_to_iana_items = entries.compact_map do |tzdata_name, territory, windows_name|\n  {windows_name, tzdata_name} if territory == \"001\"\nend.uniq!\n\nsource = ECR.render \"#{__DIR__}/windows_zone_names.ecr\"\nsource = Crystal.format(source)\nFile.write(TARGET_FILE, source)\n"
  },
  {
    "path": "scripts/git/pre-commit",
    "content": "#! /bin/sh\n#\n# This script ensures Crystal code is correctly formatted before committing it.\n# It won't apply any format changes automatically.\n#\n# Only staged files (the ones to be committed) are being processed, but each file is checked\n# entirely as it is stored on disc, even parts that are not staged.\n#\n# To use this script, install it in the local git repository.\n#\n# `curl -sL https://github.com/crystal-lang/crystal/raw/master/scripts/git/pre-commit > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit`.\n#\n# Alternatively, in the Crystal repo you can directly link it: `ln -s scripts/git/pre-commit .git/hooks`.\n#\n# Called by \"git commit\" with no arguments. The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n\nchanged_cr_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\\.cr$')\n\n[ -z \"$changed_cr_files\" ] && exit 0\n\nif [ -x bin/crystal ]; then\n  # use bin/crystal wrapper when available to run local compiler build\n  # shellcheck disable=SC2086\n  exec bin/crystal tool format --check $changed_cr_files >&2\nelse\n  # shellcheck disable=SC2086\n  exec crystal tool format --check $changed_cr_files >&2\nfi\n"
  },
  {
    "path": "scripts/github-changelog.cr",
    "content": "#! /usr/bin/env crystal\n\n# This helper queries merged pull requests for a given milestone from the GitHub API\n# and creates formatted changelog entries.\n#\n# Entries are grouped by topic (based on topic labels) and ordered by merge date.\n# Some annotations are automatically added based on labels.\n#\n# Usage:\n#\n#   scripts/github-changelog.cr <milestone>\n#\n# Environment variables:\n#   GITHUB_TOKEN: Access token for the GitHub API (required)\nrequire \"http/client\"\nrequire \"json\"\n\nabort \"Missing GITHUB_TOKEN env variable\" unless ENV[\"GITHUB_TOKEN\"]?\napi_token = ENV[\"GITHUB_TOKEN\"]\n\ncase ARGV.size\nwhen 0\n  abort \"Missing <milestone> argument\"\nwhen 1\n  repository = \"crystal-lang/crystal\"\n  milestone = ARGV.first\nwhen 2\n  repository = ARGV[0]\n  milestone = ARGV[1]\nelse\n  abort \"Too many arguments. Usage:\\n  #{PROGRAM_NAME} [<GH repo ref>] <milestone>\"\nend\n\ndef query_prs(api_token, repository, milestone : String, cursor : String?)\n  query = <<-GRAPHQL\n    query($milestone: String, $owner: String!, $repository: String!, $cursor: String) {\n      repository(owner: $owner, name: $repository) {\n        milestones(query: $milestone, first: 1) {\n          nodes {\n            closedAt\n            description\n            dueOn\n            title\n            pullRequests(first: 100, after: $cursor) {\n              nodes {\n                number\n                title\n                mergedAt\n                permalink\n                author {\n                  login\n                }\n                labels(first: 10) {\n                  nodes {\n                    name\n                  }\n                }\n              }\n              pageInfo {\n                endCursor\n                hasNextPage\n              }\n            }\n          }\n        }\n      }\n    }\n    GRAPHQL\n\n  owner, _, name = repository.partition(\"/\")\n  variables = {\n    owner:      owner,\n    repository: name,\n    milestone:  milestone,\n    cursor:     cursor,\n  }\n\n  response = HTTP::Client.post(\"https://api.github.com/graphql\",\n    body: {query: query, variables: variables}.to_json,\n    headers: HTTP::Headers{\n      \"Authorization\" => \"bearer #{api_token}\",\n    }\n  )\n  unless response.success?\n    abort \"GitHub API response: #{response.status}\\n#{response.body}\"\n  end\n\n  response\nend\n\nmodule LabelNameConverter\n  def self.from_json(pull : JSON::PullParser)\n    pull.on_key! \"name\" do\n      String.new(pull)\n    end\n  end\nend\n\nrecord Milestone,\n  closed_at : Time?,\n  description : String?,\n  due_on : Time?,\n  title : String,\n  pull_requests : Array(PullRequest) do\n  include JSON::Serializable\n\n  @[JSON::Field(key: \"dueOn\")]\n  @due_on : Time?\n\n  @[JSON::Field(key: \"closedAt\")]\n  @closed_at : Time?\n\n  @[JSON::Field(key: \"pullRequests\", root: \"nodes\")]\n  @pull_requests : Array(PullRequest)\n\n  def release_date\n    closed_at || due_on\n  end\nend\n\nrecord PullRequest,\n  number : Int32,\n  title : String,\n  merged_at : Time?,\n  permalink : String,\n  author : String?,\n  labels : Array(String) do\n  include JSON::Serializable\n  include Comparable(self)\n\n  @[JSON::Field(key: \"mergedAt\")]\n  @merged_at : Time?\n\n  @[JSON::Field(root: \"login\")]\n  @author : String?\n\n  @[JSON::Field(root: \"nodes\", converter: JSON::ArrayConverter(LabelNameConverter))]\n  @labels : Array(String)\n\n  def link_ref(io)\n    io << \"[#\" << number << \"]\"\n  end\n\n  def <=>(other : self)\n    sort_tuple <=> other.sort_tuple\n  end\n\n  def sort_tuple\n    {\n      type || \"\",\n      topic || [] of String,\n      deprecated? ? 0 : 1,\n      merged_at || Time.unix(0),\n    }\n  end\n\n  def infra_sort_tuple\n    {\n      topic || [] of String,\n      type || \"\",\n      deprecated? ? 0 : 1,\n      merged_at || Time.unix(0),\n    }\n  end\n\n  def primary_topic\n    topic.try(&.[0]?) || \"other\"\n  end\n\n  def sub_topic\n    topic.try(&.[1..].join(\":\").presence)\n  end\n\n  def topic\n    topics.fetch(0) do\n      STDERR.puts \"Missing topic for ##{number}\"\n      nil\n    end\n  end\n\n  def topics\n    topics = labels.compact_map { |label|\n      label.lchop?(\"topic:\").try(&.split(/:|\\//))\n    }\n    topics.reject! &.[0].==(\"multithreading\")\n\n    topics.sort_by! { |parts|\n      topic_priority = case parts[0]\n                       when \"infrastructure\" then 3\n                       when \"tools\"          then 2\n                       when \"lang\"           then 1\n                       else                       0\n                       end\n      {-topic_priority, parts[0]}\n    }\n  end\n\n  def deprecated?\n    labels.includes?(\"deprecation\")\n  end\n\n  def breaking?\n    labels.includes?(\"kind:breaking\")\n  end\n\n  def regression?\n    labels.includes?(\"kind:regression\")\n  end\n\n  def experimental?\n    labels.includes?(\"experimental\")\n  end\n\n  def feature?\n    labels.includes?(\"kind:feature\")\n  end\n\n  def fix?\n    labels.includes?(\"kind:bug\")\n  end\n\n  def chore?\n    labels.includes?(\"kind:chore\")\n  end\n\n  def refactor?\n    labels.includes?(\"kind:refactor\")\n  end\n\n  def docs?\n    labels.includes?(\"kind:docs\")\n  end\n\n  def specs?\n    labels.includes?(\"kind:specs\")\n  end\n\n  def performance?\n    labels.includes?(\"performance\")\n  end\n\n  def infra?\n    labels.any?(&.starts_with?(\"topic:infrastructure\"))\n  end\n\n  def type\n    case\n    when feature?     then \"feature\"\n    when docs?        then \"docs\"\n    when specs?       then \"specs\"\n    when fix?         then \"fix\"\n    when chore?       then \"chore\"\n    when performance? then \"performance\"\n    when refactor?    then \"refactor\"\n    else                   nil\n    end\n  end\n\n  def section\n    case\n    when breaking? then \"breaking\"\n    when infra?    then \"infra\"\n    else                type || \"\"\n    end\n  end\n\n  def fixup?\n    md = title.match(/\\[fixup #(.\\d+)/) || return\n    md[1]?.try(&.to_i)\n  end\n\n  def clean_title\n    title.sub(/\\s*\\[Backport [^\\]]+\\]\\s*/, \"\").sub(/^\\[?(?:#{type}|#{sub_topic})(?::|\\]:?) /i, \"\")\n  end\n\n  def backported?\n    labels.any?(&.starts_with?(\"backport\"))\n  end\n\n  def backport?\n    title.includes?(\"[Backport \")\n  end\nend\n\ndef query_milestone(api_token, repository, number)\n  cursor = nil\n  milestone = nil\n\n  while true\n    response = query_prs(api_token, repository, number, cursor)\n\n    parser = JSON::PullParser.new(response.body)\n    m = parser.on_key! \"data\" do\n      parser.on_key! \"repository\" do\n        parser.on_key! \"milestones\" do\n          parser.on_key! \"nodes\" do\n            parser.read_begin_array\n            Milestone.new(parser)\n          ensure\n            parser.read_end_array\n          end\n        end\n      end\n    end\n\n    if milestone\n      milestone.pull_requests.concat m.pull_requests\n    else\n      milestone = m\n    end\n\n    json = JSON.parse(response.body)\n    page_info = json.dig(\"data\", \"repository\", \"milestones\", \"nodes\", 0, \"pullRequests\", \"pageInfo\")\n    break unless page_info[\"hasNextPage\"].as_bool\n\n    cursor = page_info[\"endCursor\"].as_s\n  end\n\n  milestone\nend\n\nmilestone = query_milestone(api_token, repository, milestone)\n\nclass ChangelogEntry\n  getter pull_requests : Array(PullRequest)\n  property backported_from : PullRequest?\n\n  def initialize(pr : PullRequest)\n    @pull_requests = [pr]\n  end\n\n  def pr\n    pull_requests[0]\n  end\n\n  def to_s(io : IO)\n    if sub_topic = pr.sub_topic\n      io << \"_(\" << sub_topic << \")_ \"\n    end\n    if pr.labels.includes?(\"security\")\n      io << \"**[security]** \"\n    end\n    if pr.labels.includes?(\"breaking-change\")\n      io << \"**[breaking]** \"\n    end\n    if pr.regression?\n      io << \"**[regression]** \"\n    end\n    if pr.experimental?\n      io << \"**[experimental]** \"\n    end\n    if pr.deprecated?\n      io << \"**[deprecation]** \"\n    end\n    io << pr.clean_title\n\n    io << \" (\"\n    pull_requests.join(io, \", \") do |pr|\n      pr.link_ref(io)\n    end\n\n    if backported_from = self.backported_from\n      io << \", backported from \"\n      backported_from.link_ref(io)\n    end\n\n    authors = collect_authors\n    if authors.present?\n      io << \", thanks \"\n      authors.join(io, \", \") do |author|\n        io << \"@\" << author\n      end\n    end\n    io << \")\"\n  end\n\n  def collect_authors\n    authors = [] of String\n\n    if backported_from = self.backported_from\n      if author = backported_from.author\n        authors << author\n      end\n    end\n\n    pull_requests.each_with_index do |pr, i|\n      next if backported_from && i.zero?\n\n      author = pr.author || next\n      authors << author unless authors.includes?(author)\n    end\n\n    authors\n  end\n\n  def print_ref_labels(io)\n    pull_requests.each { |pr| print_ref_label(io, pr) }\n    backported_from.try { |pr| print_ref_label(io, pr) }\n  end\n\n  def print_ref_label(io, pr)\n    pr.link_ref(io)\n    io << \": \" << pr.permalink\n    io.puts\n  end\nend\n\nentries = milestone.pull_requests.compact_map do |pr|\n  ChangelogEntry.new(pr) unless pr.fixup? || pr.backported?\nend\n\nmilestone.pull_requests.each do |pr|\n  parent_number = pr.fixup? || next\n\n  parent_entry = entries.find { |entry| entry.pr.number == parent_number }\n  if parent_entry\n    parent_entry.pull_requests << pr\n  else\n    STDERR.puts \"Unresolved fixup: ##{parent_number} for: #{pr.title} (##{pr.number})\"\n  end\nend\n\nmilestone.pull_requests.each do |pr|\n  next unless pr.backported?\n\n  backport = entries.find { |entry| entry.pr.backport? && entry.pr.clean_title == pr.clean_title }\n  if backport\n    backport.backported_from = pr\n  else\n    STDERR.puts \"Unresolved backport: #{pr.clean_title.inspect} (##{pr.number})\"\n  end\nend\n\nsections = entries.group_by(&.pr.section)\n\nSECTION_TITLES = {\n  \"breaking\"    => \"Breaking changes\",\n  \"feature\"     => \"Features\",\n  \"fix\"         => \"Bugfixes\",\n  \"chore\"       => \"Chores\",\n  \"performance\" => \"Performance\",\n  \"refactor\"    => \"Refactor\",\n  \"docs\"        => \"Documentation\",\n  \"specs\"       => \"Specs\",\n  \"infra\"       => \"Infrastructure\",\n  \"\"            => \"other\",\n}\n\nTOPIC_ORDER = %w[lang stdlib compiler tools other]\n\nputs \"## [#{milestone.title}] (#{milestone.release_date.try(&.to_s(\"%F\")) || \"unreleased\"})\"\nif description = milestone.description.presence\n  puts\n  puts description\nend\nputs\nputs \"[#{milestone.title}]: https://github.com/#{repository}/releases/#{milestone.title}\"\nputs\n\ndef print_entries(entries)\n  entries.each do |entry|\n    puts \"- #{entry}\"\n  end\n  puts\n\n  entries.each(&.print_ref_labels(STDOUT))\nend\n\nSECTION_TITLES.each do |id, title|\n  entries = sections[id]? || next\n  puts \"### #{title}\"\n  puts\n\n  if id == \"infra\"\n    entries.sort_by!(&.pr.infra_sort_tuple)\n    print_entries entries\n  else\n    topics = entries.group_by(&.pr.primary_topic)\n\n    topic_titles = topics.keys.sort_by! { |k| TOPIC_ORDER.index(k) || Int32::MAX }\n\n    topic_titles.each do |topic_title|\n      topic_entries = topics[topic_title]? || next\n\n      puts \"#### #{topic_title}\"\n      puts\n\n      topic_entries.sort_by!(&.pr)\n      print_entries topic_entries\n    end\n  end\nend\n"
  },
  {
    "path": "scripts/grapheme_properties.ecr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_grapheme_properties.cr\n#\n# DO NOT EDIT\n\nstruct String::Grapheme\n  # :nodoc:\n  #\n  # The Grapheme Cluster Break Property values\n  # http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Break_Property_Values\n  enum Property\n    Start\n    Any\n    <%- props.keys.each do |key| -%>\n          <%= key %>\n    <%- end -%>\n\n    ExtendedPlusZeroWidth\n\n    # returns the Unicode property value (see `Codepoints` constants below)\n    # of the given code point\n    def self.from(char : Char)\n      r = char.ord\n      # run a binary search\n      f = 0\n      t = Grapheme.codepoints.size\n      while t > f\n        mid = (f + t) // 2\n        cp = Grapheme.codepoints[mid]\n        if r < cp[0]\n          t = mid\n          next\n        end\n        if r > cp[1]\n          f = mid + 1\n          next\n        end\n        return cp[2]\n      end\n      Property::Any\n    end\n  end\n\n  # Maps code point ranges to their properties. In the context of this package,\n  # any code point that is not contained may map to `Property::Any`. The code point\n  # ranges in this slice are numerically sorted.\n  #\n  # These ranges were taken from\n  # http://www.unicode.org/Public/<%= Unicode::VERSION %>/ucd/auxiliary/GraphemeBreakProperty.txt\n  # as well as\n  # http://www.unicode.org/Public/<%= Unicode::VERSION %>/ucd/emoji/emoji-data.txt\n  # (\"Extended_Pictographic\" only). See\n  # https://www.unicode.org/license.html for the Unicode license agreement.\n  @@codepoints : Array(Tuple(Int32, Int32, Property))?\n\n  # :nodoc:\n  protected def self.codepoints\n    @@codepoints||= begin\n      data = Array(Tuple(Int32, Int32, Property)).new(<%= props_data.size %>)\n      <%- props_data.each do |range| -%>\n        put(data, <%= sprintf(\"0x%04X\", range.low) %>, <%= sprintf(\"0x%04X\", range.high) %>, Property::<%= range.prop %>)\n      <%- end -%>\n      data\n    end\n  end\n\n  private def self.put(array : Array, *values) : Nil\n    array << values\n  end\nend\n"
  },
  {
    "path": "scripts/html_entities.ecr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_html_entities.cr\n#\n# DO NOT EDIT\n\nmodule HTML\n  # :nodoc:\n  SINGLE_CHAR_ENTITIES = {\n    <%- single_char_entities.each do |name, entity| -%>\n      <%= name.inspect %>.to_slice => '\\u{<%= \"%06X\" % entity.codepoints[0] %>}',\n    <%- end -%>\n  } of Bytes => Char\n\n  # :nodoc:\n  DOUBLE_CHAR_ENTITIES = {\n    <%- double_char_entities.each do |name, entity| -%>\n      <%= name.inspect %>.to_slice => \"\\u{<%= \"%04X\" % entity.codepoints[0] %>}\\u{<%= \"%04X\" % entity.codepoints[1] %>}\",\n    <%- end -%>\n  } of Bytes => String\n\n  # :nodoc:\n  MAX_ENTITY_NAME_SIZE = <%= max_entity_name_size %>\nend\n"
  },
  {
    "path": "scripts/print_regex_config.cr",
    "content": "#! /usr/bin/env crystal\n\n{% if Regex::Engine.resolve.name == \"Regex::PCRE2\" %}\n  enum LibPCRE2::BSR : UInt32\n    UNICODE = 1\n    ANYCRLF = 2\n  end\n\n  @[Flags]\n  enum LibPCRE2::COMPILED_WIDTHS : UInt32\n    U8\n    U16\n    U32\n    Unused\n  end\n\n  enum LibPCRE2::NEWLINE : UInt32\n    CR      = 1\n    LF      = 2\n    CRLF    = 3\n    ANY     = 4\n    ANYCRLF = 5\n    NUL     = 6\n  end\n\n  def config(kind : UInt32.class, what)\n    where = uninitialized UInt32\n    LibPCRE2.config(what, pointerof(where))\n    where\n  end\n\n  def config(kind : Bool.class, what)\n    config(UInt32, what) != 0\n  end\n\n  def config(kind : String.class, what)\n    len = LibPCRE2.config(what, nil)\n    if len > 0\n      where = Bytes.new(len - 1)\n      LibPCRE2.config(what, where)\n      ret = String.new(where)\n    end\n    ret.inspect\n  end\n\n  def config(kind : Enum.class, what)\n    kind.new(config(UInt32, what))\n  end\n\n  puts <<-EOS\n  Using PCRE2 #{config(String, LibPCRE2::CONFIG_VERSION)}\n  * PCRE2_CONFIG_BSR:               #{config(LibPCRE2::BSR, LibPCRE2::CONFIG_BSR)}\n  * PCRE2_CONFIG_COMPILED_WIDTHS:   #{config(LibPCRE2::COMPILED_WIDTHS, LibPCRE2::CONFIG_COMPILED_WIDTHS)}\n  * PCRE2_CONFIG_DEPTHLIMIT:        #{config(UInt32, LibPCRE2::CONFIG_DEPTHLIMIT)}\n  * PCRE2_CONFIG_HEAPLIMIT:         #{config(UInt32, LibPCRE2::CONFIG_HEAPLIMIT)}\n  * PCRE2_CONFIG_JIT:               #{config(Bool, LibPCRE2::CONFIG_JIT)}\n  * PCRE2_CONFIG_JITTARGET:         #{config(String, LibPCRE2::CONFIG_JITTARGET)}\n  * PCRE2_CONFIG_LINKSIZE:          #{config(UInt32, LibPCRE2::CONFIG_LINKSIZE)}\n  * PCRE2_CONFIG_MATCHLIMIT:        #{config(UInt32, LibPCRE2::CONFIG_MATCHLIMIT)}\n  * PCRE2_CONFIG_NEVER_BACKSLASH_C: #{config(Bool, LibPCRE2::CONFIG_NEVER_BACKSLASH_C)}\n  * PCRE2_CONFIG_NEWLINE:           #{config(LibPCRE2::NEWLINE, LibPCRE2::CONFIG_NEWLINE)}\n  * PCRE2_CONFIG_PARENSLIMIT:       #{config(UInt32, LibPCRE2::CONFIG_PARENSLIMIT)}\n  * PCRE2_CONFIG_UNICODE:           #{config(Bool, LibPCRE2::CONFIG_UNICODE)}\n  * PCRE2_CONFIG_UNICODE_VERSION:   #{config(String, LibPCRE2::CONFIG_UNICODE_VERSION)}\n  EOS\n{% else %}\n  enum LibPCRE::BSR : LibC::Int\n    UNICODE = 0\n    ANYCRLF = 1\n  end\n\n  enum LibPCRE::NEWLINE : LibC::Int\n    CR      = 0x000d\n    LF      = 0x000a\n    CRLF    = 0x0d0a\n    ANYCRLF =     -2\n    ANY     =     -1\n  end\n\n  lib LibPCRE\n    CONFIG_UTF8                   =  0\n    CONFIG_NEWLINE                =  1\n    CONFIG_LINK_SIZE              =  2\n    CONFIG_POSIX_MALLOC_THRESHOLD =  3\n    CONFIG_MATCH_LIMIT            =  4\n    CONFIG_STACKRECURSE           =  5\n    CONFIG_UNICODE_PROPERTIES     =  6\n    CONFIG_MATCH_LIMIT_RECURSION  =  7\n    CONFIG_BSR                    =  8\n    CONFIG_UTF16                  = 10\n    CONFIG_JITTARGET              = 11\n    CONFIG_UTF32                  = 12\n    CONFIG_PARENS_LIMIT           = 13\n  end\n\n  def config(kind : LibC::Int.class, what)\n    where = uninitialized LibC::Int\n    LibPCRE.config(what, pointerof(where))\n    where\n  end\n\n  def config(kind : LibC::ULong.class, what)\n    where = uninitialized LibC::ULong\n    LibPCRE.config(what, pointerof(where))\n    where\n  end\n\n  def config(kind : Bool.class, what)\n    config(LibC::Int, what) != 0\n  end\n\n  def config(kind : String.class, what)\n    where = uninitialized LibC::Char*\n    LibPCRE.config(what, pointerof(where))\n    (where ? String.new(where) : nil).inspect\n  end\n\n  def config(kind : Enum.class, what)\n    kind.new(config(LibC::Int, what))\n  end\n\n  puts <<-EOS\n  Using PCRE #{String.new(LibPCRE.version).inspect}\n  * PCRE_CONFIG_BSR:                    #{config(LibPCRE::BSR, LibPCRE::CONFIG_BSR)}\n  * PCRE_CONFIG_JIT:                    #{config(Bool, LibPCRE::CONFIG_JIT)}\n  * PCRE_CONFIG_JITTARGET:              #{config(String, LibPCRE::CONFIG_JITTARGET)}\n  * PCRE_CONFIG_LINK_SIZE:              #{config(LibC::Int, LibPCRE::CONFIG_LINK_SIZE)}\n  * PCRE_CONFIG_PARENS_LIMIT:           #{config(LibC::ULong, LibPCRE::CONFIG_PARENS_LIMIT)}\n  * PCRE_CONFIG_MATCH_LIMIT:            #{config(LibC::ULong, LibPCRE::CONFIG_MATCH_LIMIT)}\n  * PCRE_CONFIG_MATCH_LIMIT_RECURSION:  #{config(LibC::ULong, LibPCRE::CONFIG_MATCH_LIMIT_RECURSION)}\n  * PCRE_CONFIG_NEWLINE:                #{config(LibPCRE::NEWLINE, LibPCRE::CONFIG_NEWLINE)}\n  * PCRE_CONFIG_POSIX_MALLOC_THRESHOLD: #{config(LibC::Int, LibPCRE::CONFIG_POSIX_MALLOC_THRESHOLD)}\n  * PCRE_CONFIG_STACKRECURSE:           #{config(Bool, LibPCRE::CONFIG_STACKRECURSE)}\n  * PCRE_CONFIG_UTF16:                  #{config(Bool, LibPCRE::CONFIG_UTF16)}\n  * PCRE_CONFIG_UTF32:                  #{config(Bool, LibPCRE::CONFIG_UTF32)}\n  * PCRE_CONFIG_UTF8:                   #{config(Bool, LibPCRE::CONFIG_UTF8)}\n  * PCRE_CONFIG_UNICODE_PROPERTIES:     #{config(Bool, LibPCRE::CONFIG_UNICODE_PROPERTIES)}\n  EOS\n{% end %}\n"
  },
  {
    "path": "scripts/release-update.sh",
    "content": "#!/usr/bin/env sh\n#\n# This helper updates all references to the previous Crystal release as bootstrap version with a new release.\n#\n# Usage:\n#\n#    scripts/release-update.sh 1.3.0\n#\n# See Crystal release checklist: https://github.com/crystal-lang/distribution-scripts/blob/master/processes/crystal-release.md#post-release\nset -eu\n\nCRYSTAL_VERSION=$1\n\n# Write dev version for next minor release into src/VERSION\nminor_branch=\"${CRYSTAL_VERSION%.*}\"\nnext_minor=\"$((${minor_branch#*.} + 1))\"\necho \"${CRYSTAL_VERSION%%.*}.${next_minor}.0-dev\" > src/VERSION\n\n# Update shard.yml\nsed -i -E \"s/version: .*/version: $(cat src/VERSION)/\" shard.yml\n\n# Remove SOURCE_DATE_EPOCH (only used in source tree of a release)\nrm -f src/SOURCE_DATE_EPOCH\n\n# Edit PREVIOUS_CRYSTAL_BASE_URL in .circleci/config.yml\nsed -i -E \"s|[0-9.]+/crystal-[0-9.]+-[0-9]|$CRYSTAL_VERSION/crystal-$CRYSTAL_VERSION-1|g\" .circleci/config.yml\n\n# Edit DOCKER_TEST_PREFIX in bin/ci\nsed -i -E \"s|crystallang/crystal:[0-9.]+|crystallang/crystal:$CRYSTAL_VERSION|\" bin/ci\n\n# Edit prepare_build on_osx download package and folder\nsed -i -E \"s|[0-9.]+/crystal-[0-9.]+-[0-9]|$CRYSTAL_VERSION/crystal-$CRYSTAL_VERSION-1|g\" bin/ci\nsed -i -E \"s|crystal-[0-9.]+-[0-9]|crystal-$CRYSTAL_VERSION-1|g\" bin/ci\n\n# Edit .github/workflows/*.yml to point to docker image\n# Update the patch version of the latest entry if same minor version to have only one item per minor version\nprevious_release=$(grep -o -P '(?<=crystal_bootstrap_version: ).*(?= # LATEST RELEASE)' .github/workflows/linux.yml)\nsed -i -E \"s/crystal_bootstrap_version: .+ # LATEST RELEASE/crystal_bootstrap_version: $CRYSTAL_VERSION # LATEST RELEASE/\" .github/workflows/linux.yml\n\nif [ \"${minor_branch}\" != \"${previous_release%.*}\" ]; then\n  sed -i -E \"/crystal_bootstrap_version:/ s/(, ${previous_release%.*}\\.[0-9]*)?\\]\\$/, $previous_release]/\" .github/workflows/forward-compatibility.yml\nfi\n\nsed -i -E \"s|crystallang/crystal:[0-9.]+|crystallang/crystal:$CRYSTAL_VERSION|g\" .github/workflows/*.yml\n\n# Edit .github/workflows/*.yml to update version for install-crystal action\nsed -i -E \"s|crystal: \\\"[0-9.]+\\\"|crystal: \\\"$CRYSTAL_VERSION\\\"|g\" .github/workflows/*.yml\n\n# Edit shell.nix latestCrystalBinary using nix-prefetch-url --unpack <url>\ndarwin_url=\"https://github.com/crystal-lang/crystal/releases/download/$CRYSTAL_VERSION/crystal-$CRYSTAL_VERSION-1-darwin-universal.tar.gz\"\ndarwin_sha=$(nix-prefetch-url --unpack \"$darwin_url\")\n\nsed -i -E \"s|https://github.com/crystal-lang/crystal/releases/download/[0-9.]+/crystal-[0-9.]+-[0-9]-darwin-universal.tar.gz|$darwin_url|\" shell.nix\nsed -i -E \"/darwin-universal\\.tar\\.gz/ {n;s|sha256:[^\\\"]+|sha256:$darwin_sha|}\" shell.nix\n\nlinux_url=\"https://github.com/crystal-lang/crystal/releases/download/$CRYSTAL_VERSION/crystal-$CRYSTAL_VERSION-1-linux-x86_64.tar.gz\"\nlinux_sha=$(nix-prefetch-url --unpack \"$linux_url\")\n\nsed -i -E \"s|https://github.com/crystal-lang/crystal/releases/download/[0-9.]+/crystal-[0-9.]+-[0-9]-linux-x86_64.tar.gz|$linux_url|\" shell.nix\nsed -i -E \"/linux-x86_64\\.tar\\.gz/ {n;s|sha256:[^\\\"]+|sha256:$linux_sha|}\" shell.nix\n"
  },
  {
    "path": "scripts/test_ssl_server.cr",
    "content": "#! /usr/bin/env crystal\n#\n# This helper runs a default `HTTP::Server` instance and checks its behaviour\n# using [testssl.sh](https://testssl.sh/).\n# testssl.sh is a tool for validating TLS implementations.\n\nrequire \"http\"\nrequire \"../spec/support/ssl\"\n\n# This is needed for the ssl_context_pair helper\ndef datapath(*components)\n  File.join(\"spec\", \"std\", \"data\", *components)\nend\n\nserver = HTTP::Server.new do |context|\n  context.response.content_type = \"text/plain\"\n  context.response.print \"Hello world!\"\nend\nserver_context, _client_context = ssl_context_pair\naddress = server.bind_tls \"0.0.0.0\", 0, server_context\n\nputs \"== Starting HTTP server at #{address}\"\n\nspawn do\n  puts \"== Running testssl.sh\"\n  puts \"This may take some time...\"\n\n  Process.run(\"testssl.sh\", %w(--parallel --nodns none --color 2) << address.to_s,\n    output: :inherit, error: :inherit)\n\n  server.close\nend\n\nserver.listen\n"
  },
  {
    "path": "scripts/unicode_data.ecr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_unicode_data.cr\n#\n# DO NOT EDIT\n\nmodule Unicode\n  # Most case conversions map a range to another range.\n  # Here we store: {from, to, delta}\n  private class_getter upcase_ranges : Array({Int32, Int32, Int32}) do\n    data = Array({Int32, Int32, Int32}).new(<%= upcase_ranges.size %>)\n    <%- upcase_ranges.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>, <%= range.delta %>)\n    <%- end -%>\n    data\n  end\n\n  # Most case conversions map a range to another range.\n  # Here we store: {from, to, delta}\n  private class_getter downcase_ranges : Array({Int32, Int32, Int32}) do\n    data = Array({Int32, Int32, Int32}).new(<%= downcase_ranges.size %>)\n    <%- downcase_ranges.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>, <%= range.delta %>)\n    <%- end -%>\n    data\n  end\n\n  # Other case conversions run in an alternated range\n  # of uppercase/lowercase transformations\n  # Here we store {from, to}\n  private class_getter alternate_ranges : Array({Int32, Int32}) do\n    data = Array({Int32, Int32}).new(<%= alternate_ranges.size %>)\n    <%- alternate_ranges.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>)\n    <%- end -%>\n    data\n  end\n\n  # We store categories as consecutive strides {from, to, stride}\n  #\n  # For example, in this case:\n  #\n  #   {1, 10, 1}\n  #   {11, 15, 2}\n  #\n  # The values are: 1..10, 11, 13, 15\n\n  <%- all_strides.each do |category, strides| -%>\n    private class_getter category_<%= category %> : Array({Int32, Int32, Int32}) do\n      data = Array({Int32, Int32, Int32}).new(<%= strides.size %>)\n      <%- strides.each do |stride| -%>\n        put(data, <%= stride.low %>, <%= stride.high %>, <%= stride.stride %>)\n      <%- end -%>\n      data\n    end\n  <%- end %>\n\n  # Most casefold conversions map a range to another range.\n  # Here we store: {from, to, delta}\n  private class_getter casefold_ranges : Array({Int32, Int32, Int32}) do\n    data = Array({Int32, Int32, Int32}).new(<%= casefold_ranges.size %>)\n    <%- casefold_ranges.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>, <%= range.delta %>)\n    <%- end -%>\n    data\n  end\n\n  # Special downcase transformation that involve mapping a codepoint\n  # to multiple codepoints. The maximum transformation is always 3\n  # codepoints, so we store them all as 3 codepoints and 0 means end.\n  private class_getter special_cases_downcase : Hash(Int32, {Int32, Int32, Int32}) do\n    data = Hash(Int32, {Int32, Int32, Int32}).new(initial_capacity: <%= special_cases_downcase.size %>)\n    <%- special_cases_downcase.each do |a_case| -%>\n      put(data, <%= a_case.codepoint %>, <%= a_case.value.join(\", \") %>)\n    <%- end %>\n    data\n  end\n\n  # Special upcase transformation that involve mapping a codepoint\n  # to multiple codepoints. The maximum transformation is always 3\n  # codepoints, so we store them all as 3 codepoints and 0 means end.\n  private class_getter special_cases_upcase : Hash(Int32, {Int32, Int32, Int32}) do\n    data = Hash(Int32, {Int32, Int32, Int32}).new(initial_capacity: <%= special_cases_upcase.size %>)\n    <%- special_cases_upcase.each do |a_case| -%>\n      put(data, <%= a_case.codepoint %>, <%= a_case.value.join(\", \") %>)\n    <%- end %>\n    data\n  end\n\n  # Titlecase transformation that differs from the uppercase transformation.\n  # The maximum transformation is always 3 codepoints, so we store them all as 3\n  # codepoints and 0 means end.\n  private class_getter special_cases_titlecase : Hash(Int32, {Int32, Int32, Int32}) do\n    data = Hash(Int32, {Int32, Int32, Int32}).new(initial_capacity: <%= special_cases_titlecase.size %>)\n    <%- special_cases_titlecase.each do |a_case| -%>\n      put(data, <%= a_case.codepoint %>, <%= a_case.value.join(\", \") %>)\n    <%- end %>\n    data\n  end\n\n  # Fold case transformation that involve mapping a codepoint\n  # to multiple codepoints. The maximum transformation is always 3\n  # codepoints, so we store them all as 3 codepoints and 0 means end.\n  private class_getter fold_cases : Hash(Int32, {Int32, Int32, Int32}) do\n    data = Hash(Int32, {Int32, Int32, Int32}).new(initial_capacity: <%= special_cases_casefold.size %>)\n    <%- special_cases_casefold.each do |a_case| -%>\n      put(data, <%= a_case.codepoint %>, <%= a_case.value.join(\", \") %>)\n    <%- end -%>\n    data\n  end\n\n  # Canonical combining classes. Only non-zero entries are stored. Unicode\n  # guarantees that all class values are within `0..254`.\n  # Here we store: {from, to, class}\n  private class_getter canonical_combining_classes : Array({Int32, Int32, UInt8}) do\n    data = Array({Int32, Int32, UInt8}).new(<%= canonical_combining_classes.size %>)\n    <%- canonical_combining_classes.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>, <%= range.ccc %>_u8)\n    <%- end -%>\n    data\n  end\n\n  # Canonical decomposition mappings, excluding Hangul syllables. The maximum\n  # transformation is always 2 codepoints, so we store them all as 2 codepoints\n  # and 0 means end.\n  private class_getter canonical_decompositions : Hash(Int32, {Int32, Int32}) do\n    data = Hash(Int32, {Int32, Int32}).new(initial_capacity: <%= canonical_decompositions.size %>)\n    <%- canonical_decompositions.each do |decomp| -%>\n      put(data, <%= decomp.join(\", \") %>)\n    <%- end -%>\n    data\n  end\n\n  # Codepoints for compatibility decomposition mappings.\n  private class_getter compatibility_decomposition_data : Array(Int32) do\n    data = Array(Int32).new(<%= compatibility_decomposition_data.size %>)\n    <%- compatibility_decomposition_data.each do |codepoint| -%>\n      put(data, <%= codepoint %>)\n    <%- end -%>\n    data\n  end\n\n  # Compatibility decomposition mappings, represented as subsequences of\n  # `compatibility_decomposition_data`. The maximum transformation is 18\n  # codepoints.\n  # Here we store: codepoint => {index, count}\n  private class_getter compatibility_decompositions : Hash(Int32, {Int32, Int32}) do\n    data = Hash(Int32, {Int32, Int32}).new(initial_capacity: <%= compatibility_decompositions.size %>)\n    <%- compatibility_decompositions.each do |codepoint, index, count| -%>\n      put(data, <%= codepoint %>, <%= index %>, <%= count %>)\n    <%- end -%>\n    data\n  end\n\n  # Reverse mapping of the canonical decompositions, excluding the full\n  # composition exclusions.\n  # Here we store: (first << 21 | second) => codepoint\n  private class_getter canonical_compositions : Hash(Int64, Int32) do\n    data = Hash(Int64, Int32).new(initial_capacity: <%= canonical_compositions.size %>)\n    <%- canonical_compositions.each do |first_second, codepoint| -%>\n      put(data, <%= first_second %>_i64, <%= codepoint %>)\n    <%- end -%>\n    data\n  end\n\n  # Used to quickly determine whether a codepoint may appear under Normalization\n  # Form C (yes if absent in this table).\n  # Here we store: {low, high, result (no or maybe)}\n  private class_getter nfc_quick_check : Array({Int32, Int32, QuickCheckResult}) do\n    <%- quick_check = quick_checks[Unicode::NormalizationForm::NFC] -%>\n    data = Array({Int32, Int32, QuickCheckResult}).new(<%= quick_check.size %>)\n    <%- quick_check.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>, QuickCheckResult::<%= range.result %>)\n    <%- end -%>\n    data\n  end\n\n  # Used to quickly determine whether a codepoint may appear under Normalization\n  # Form KC (yes if absent in this table).\n  # Here we store: {low, high, result (no or maybe)}\n  private class_getter nfkc_quick_check : Array({Int32, Int32, QuickCheckResult}) do\n    <%- quick_check = quick_checks[Unicode::NormalizationForm::NFKC] -%>\n    data = Array({Int32, Int32, QuickCheckResult}).new(<%= quick_check.size %>)\n    <%- quick_check.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>, QuickCheckResult::<%= range.result %>)\n    <%- end -%>\n    data\n  end\n\n  # Used to quickly determine whether a codepoint may appear under Normalization\n  # Form D (yes if absent in this table). There are no \"maybe\" values;\n  # codepoints contained here may not appear under NFD.\n  # Here we store: {low, high}\n  private class_getter nfd_quick_check : Array({Int32, Int32}) do\n    <%- quick_check = quick_checks[Unicode::NormalizationForm::NFD] -%>\n    data = Array({Int32, Int32}).new(<%= quick_check.size %>)\n    <%- quick_check.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>)\n    <%- end -%>\n    data\n  end\n\n  # Used to quickly determine whether a codepoint may appear under Normalization\n  # Form KD (yes if absent in this table). There are no \"maybe\" values;\n  # codepoints contained here may not appear under NFKD.\n  # Here we store: {low, high}\n  private class_getter nfkd_quick_check : Array({Int32, Int32}) do\n    <%- quick_check = quick_checks[Unicode::NormalizationForm::NFKD] -%>\n    data = Array({Int32, Int32}).new(<%= quick_check.size %>)\n    <%- quick_check.each do |range| -%>\n      put(data, <%= range.low %>, <%= range.high %>)\n    <%- end -%>\n    data\n  end\n\n  # TODO: this is needed to avoid generating lots of allocas\n  # in LLVM, which makes LLVM really slow. The compiler should\n  # try to avoid/reuse temporary allocas.\n  # Explanation: https://github.com/crystal-lang/crystal/issues/4516#issuecomment-306226171\n  private def self.put(array : Array, value) : Nil\n    array << value\n  end\n\n  private def self.put(array : Array, *values) : Nil\n    array << values\n  end\n\n  private def self.put(hash : Hash, key, value) : Nil\n    hash[key] = value\n  end\n\n  private def self.put(hash : Hash, key, *values) : Nil\n    hash[key] = values\n  end\nend\n"
  },
  {
    "path": "scripts/update-changelog.sh",
    "content": "#! /bin/sh\n\n# This script automates generating changelog with `scripts/github-changelog.cr`,\n# editing it into `doc/changelog/v${VERSION%.*}.md` and pushing it to a\n# `changelog/$VERSION` branch.\n#\n# It reads the current (dev-)version from `src/VERSION` and generates the\n# changelog entries for all PRs from the respective GitHub milestone via\n# `scripts/github-changelog.cr`.\n# The section is then inserted into the changelog file, overwriting any previous\n# content for this milestone.\n# Finally, the changes are committed and pushed to `changelog/$VERSION`.\n# If the changelog section is *new*, also creates a draft PR for this branch.\n#\n# Usage:\n#\n#   scripts/update-changelog.sh\n#\n# Requirements:\n#\n#   - scripts/github-changelog.cr\n#   - git\n#   - grep\n#   - sed\n#\n# Environment variables:\n#   GITHUB_TOKEN: Access token for the GitHub API (required)\n\nset -eu\n\nVERSION=${1:-$(cat src/VERSION)}\nVERSION=${VERSION%-dev}\n\nbase_branch=$(git rev-parse --abbrev-ref HEAD)\nbranch=\"changelog/$VERSION\"\ncurrent_changelog=\"CHANGELOG.$VERSION.md\"\n\necho \"Generating $current_changelog...\"\nscripts/github-changelog.cr \"$VERSION\" > \"$current_changelog\"\n\necho \"Switching to branch $branch\"\ngit switch \"$branch\" 2>/dev/null || git switch -c \"$branch\";\n\n# Write release version into src/VERSION\necho \"${VERSION}\" > src/VERSION\ngit add src/VERSION\n\n# Update shard.yml\nsed -i -E \"s/version: .*/version: ${VERSION}/\" shard.yml\ngit add shard.yml\n\n# Write release date into src/SOURCE_DATE_EPOCH\nrelease_date=$(head -n1 \"$current_changelog\" | grep -o -P '(?<=\\()[^)]+')\ndate --utc --date=\"${release_date}\" +%s > src/SOURCE_DATE_EPOCH\ngit add src/SOURCE_DATE_EPOCH\n\nchangelog_path=\"doc/changelogs/v${VERSION%.*}.md\"\n\nif [ ! -f \"$changelog_path\" ]; then\n  echo \"Creating new changelog file $changelog_path\"\n  printf \"# Changelog %s\\n\\n\" \"${VERSION%.*}\" > \"$changelog_path\"\n\n  printf '%s [%s series](./v%s.md)\\n' \"-\" \"${VERSION%.*}\" \"${VERSION%.*}\" >> \"doc/changelogs/README.md\"\n  git add \"doc/changelogs/README.md\"\nfi\n\nif grep --silent -E \"^## \\[$VERSION\\]\" \"$changelog_path\"; then\n  echo \"Replacing section in $changelog_path\"\n  sed -i -E \"/^## \\[$VERSION\\]/,/^## /{\n\n    /^## \\[$VERSION\\]/s/.*/cat $current_changelog/e; /^## /!d\n  }\" \"$changelog_path\"\n\n  git add \"$changelog_path\"\n  git commit -m \"Update changelog for $VERSION\"\n  echo git push\nelse\n  echo \"Adding new section to $changelog_path\"\n\n  sed -i -E \"2r $current_changelog\" \"$changelog_path\"\n\n  git add \"$changelog_path\"\n  git commit -m \"Add changelog for $VERSION\"\n  echo git push -u upstream \"$branch\"\n\n  echo gh pr create --draft --base \"$base_branch\" \\\n    --body \"Preview: https://github.com/crystal-lang/crystal/blob/$branch/$changelog_path.md\" \\\n    --label \"topic:infrastructure\" -t \"Changelog for $VERSION\" --milestone \"$VERSION\"\nfi\n"
  },
  {
    "path": "scripts/update-distribution-scripts.sh",
    "content": "#!/usr/bin/env sh\n#\n# This helper updates the reference of [distribution-scripts](https://github.com/crystal-lang/distribution-scripts),\n# pushes the change to GitHub and creates a pull request.\n#\n# Usage:\n#\n#    scripts/update-distribution_scripts.sh [REF [BRANCH]]\n#\n# Parameters:\n# * REF: Git commit SHA in distribution-scripts (default: HEAD)\n# * BRANCH: Branch name for CI branch in crystal (default: ci/update-distribution-scripts)\n#\n# Requirements:\n# * packages: git gh sed\n# * Working directory should be in a checked out work tree of `crystal-lang/crystal`.\n#\n# * The default value for reference is the current HEAD of https://github.com/crystal-lang/distribution-scripts.\n\nset -eu\n\nDISTRIBUTION_SCRIPTS_WORK_DIR=${DISTRIBUTION_SCRIPTS_WORK_DIR:-../distribution-scripts/.git}\nGIT_DS=\"git --git-dir=$DISTRIBUTION_SCRIPTS_WORK_DIR\"\n\n$GIT_DS fetch origin master\n\nif [ \"${1:-\"HEAD\"}\" = \"HEAD\" ]; then\n  reference=$($GIT_DS rev-list origin/master | head -1)\nelse\n  reference=${1}\nfi\n\nbranch=\"${2:-\"ci/update-distribution-scripts\"}\"\n\ngit switch -C \"$branch\" master\n\nold_reference=$(sed -n \"/distribution-scripts-version:/{n;n;n;p}\" .circleci/config.yml | grep -o -P '(?<=default: \")[^\"]+')\necho \"$old_reference\"..\"$reference\"\n\nsed -i -E \"/distribution-scripts-version:/{n;n;n;s/default: \\\".*\\\"/default: \\\"$reference\\\"/}\" .circleci/config.yml\n\ngit add .circleci/config.yml\n\nmessage=\"Updates \\`distribution-scripts\\` dependency to https://github.com/crystal-lang/distribution-scripts/commit/$reference\"\nlog=$($GIT_DS log \"$old_reference\"..\"$reference\" --format=\"%s\" | sed \"s/.*(/crystal-lang\\/distribution-scripts/;s/^/* /;s/)$//\")\nmessage=$(printf \"%s\\n\\nThis includes the following changes:\\n\\n%s\" \"$message\" \"$log\")\n\ngit commit -m \"Update distribution-scripts\" -m \"$message\"\n\ngit show\n\ngit push -u upstream \"$branch\"\n\n# Confirm creating pull request\necho \"Create pull request for branch $branch? [y/N]\"\nread -r REPLY\n\nif [ \"$REPLY\" = \"y\" ]; then\n  gh pr create -R crystal-lang/crystal --fill --label \"topic:infrastructure\" --assignee \"@me\"\nfi\n"
  },
  {
    "path": "scripts/update-shards.sh",
    "content": "#!/usr/bin/env sh\n\n# Update shards release.\n#\n# Usage:\n#\n#    scripts/update-shards.sh [<version>]\n#\n# This helper script pulls the latest Shards release from GitHub and updates all\n# references to the shards release in this repository.\n#\n# See Crystal release checklist: https://github.com/crystal-lang/distribution-scripts/blob/master/processes/shards-release.md#post-release\n\nset -eu\n\nSHARDS_VERSION=${1:-}\nif [ -z \"$SHARDS_VERSION\" ]; then\n  # fetch latest release from GitHub\n  SHARDS_VERSION=$(gh release view --repo crystal-lang/shards --json tagName --jq .tagName | cut -c 2-)\nfi\n\n# Update shards ref in mingw64 and win-msvc build actions\nsed -i \"/repository: crystal-lang\\/shards/{n;s/ref: .*/ref: v${SHARDS_VERSION}/}\" .github/workflows/mingw-w64-steps.yml .github/workflows/win_build_portable.yml\n"
  },
  {
    "path": "scripts/windows_zone_names.ecr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_windows_zone_names.cr\n#\n# DO NOT EDIT\n\nmodule Crystal::System::Time\n  # These mappings from IANA to Windows time zone names and tzdata abbreviations\n  # are based on\n  # <%= WINDOWS_ZONE_NAMES_SOURCE %>\n  private class_getter iana_to_windows : Hash(String, {String, String, String}) do\n    data = Hash(String, {String, String, String}).new(initial_capacity: <%= iana_to_windows_items.size %>)\n    <%- iana_to_windows_items.each do |tzdata_name, windows_name, zone1, zone2| -%>\n      put(data, <%= tzdata_name.inspect %>, <%= windows_name.inspect %>, <%= zone1.inspect %>, <%= zone2.inspect %>)\n    <%- end -%>\n    data\n  end\n\n  # These canonical mappings from Windows to IANA time zone names, used for the\n  # local time zone, are based on\n  # <%= WINDOWS_ZONE_NAMES_SOURCE %>\n  private class_getter windows_to_iana : Hash(String, String) do\n    data = Hash(String, String).new(initial_capacity: <%= windows_to_iana_items.size %>)\n    <%- windows_to_iana_items.each do |windows_name, tzdata_name| -%>\n      put(data, <%= windows_name.inspect %>, <%= tzdata_name.inspect %>)\n    <%- end -%>\n    data\n  end\n\n  # TODO: this is needed to avoid generating lots of allocas\n  # in LLVM, which makes LLVM really slow. The compiler should\n  # try to avoid/reuse temporary allocas.\n  # Explanation: https://github.com/crystal-lang/crystal/issues/4516#issuecomment-306226171\n  private def self.put(hash : Hash, key, value) : Nil\n    hash[key] = value\n  end\n\n  private def self.put(hash : Hash, key, *values) : Nil\n    hash[key] = values\n  end\nend\n"
  },
  {
    "path": "shard.yml",
    "content": "name: crystal\nversion: 1.20.0-dev\n\nauthors:\n  - Crystal Core Team <crystal@manas.tech>\n\ndescription: |\n  The Crystal standard library and compiler.\n\ncrystal: \">= 1.0\"\n\ndependencies:\n  markd:\n    github: icyleaf/markd\n  reply:\n    github: I3oris/reply\n    commit: 13f7eba083f138dd063c68b859c8e315f44fb523\n  sanitize:\n    github: straight-shoota/sanitize\n    commit: 75c141b619c77956e88f557149566cd28876398b\n\nlicense: Apache-2.0 WITH Swift-exception\n\nrepository: https://github.com/crystal-lang/crystal\nhomepage: https://crystal-lang.org/\ndocumentation: https://crystal-lang.org/docs\n"
  },
  {
    "path": "shell.nix",
    "content": "# This nix-shell script can be used to get a complete development environment\n# for the Crystal compiler.\n#\n# You can choose which llvm version use and, on Linux, choose to use musl.\n#\n# $ nix-shell --pure\n# $ nix-shell --pure --arg llvm 10\n# $ nix-shell --pure --arg llvm 10 --arg musl true\n# $ nix-shell --pure --arg llvm 9\n# $ nix-shell --pure --arg llvm 9 --argstr system i686-linux\n# ...\n# $ nix-shell --pure --arg llvm 6\n#\n# If needed, you can use https://app.cachix.org/cache/crystal-ci to avoid building\n# packages that are not available in Nix directly. This is mostly useful for musl.\n#\n# $ nix-env -iA cachix -f https://cachix.org/api/v1/install\n# $ cachix use crystal-ci\n# $ nix-shell --pure --arg musl true\n#\n\n{llvm ? 16, musl ? false, system ? builtins.currentSystem}:\n\nlet\n  nixpkgs = import (builtins.fetchTarball {\n    name = \"nixpkgs-23.05\";\n    url = \"https://github.com/NixOS/nixpkgs/archive/23.05.tar.gz\";\n    sha256 = \"10wn0l08j9lgqcw8177nh2ljrnxdrpri7bp0g7nvrsn9rkawvlbf\";\n  }) {\n    inherit system;\n  };\n\n  pkgs = if musl then nixpkgs.pkgsMusl else nixpkgs;\n  llvmPackages = pkgs.\"llvmPackages_${toString llvm}\";\n\n  genericBinary = { url, sha256 }:\n    pkgs.stdenv.mkDerivation rec {\n      name = \"crystal-binary\";\n      src = builtins.fetchTarball { inherit url sha256; };\n\n      # Extract only the compiler binary\n      installPhase = ''\n        mkdir -p $out/bin\n\n        if [ -f \"${src}/embedded/bin/crystal\" ]; then\n          # Darwin packages use embedded/bin/crystal\n          cp ${src}/embedded/bin/crystal $out/bin/\n        elif [ -f \"${src}/lib/crystal/bin/crystal\" ]; then\n          # Older Linux packages use lib/crystal/bin/crystal\n          cp ${src}/lib/crystal/bin/crystal $out/bin/\n        elif [ -f \"${src}/bin/crystal\" ]; then\n          # Linux packages use bin/crystal\n          cp ${src}/bin/crystal $out/bin/\n        fi\n      '';\n    };\n\n  # Hashes obtained using `nix-prefetch-url --unpack <url>`\n  latestCrystalBinary = genericBinary ({\n    x86_64-darwin = {\n      url = \"https://github.com/crystal-lang/crystal/releases/download/1.19.1/crystal-1.19.1-1-darwin-universal.tar.gz\";\n      sha256 = \"sha256:1w2ph2sz91pddkpc6nxbgpbx4gvml481g2gr1a5325w7hk523d7v\";\n    };\n\n    aarch64-darwin = {\n      url = \"https://github.com/crystal-lang/crystal/releases/download/1.19.1/crystal-1.19.1-1-darwin-universal.tar.gz\";\n      sha256 = \"sha256:1w2ph2sz91pddkpc6nxbgpbx4gvml481g2gr1a5325w7hk523d7v\";\n    };\n\n    x86_64-linux = {\n      url = \"https://github.com/crystal-lang/crystal/releases/download/1.19.1/crystal-1.19.1-1-linux-x86_64.tar.gz\";\n      sha256 = \"sha256:0vh0m9dppjwv0af40sz8y2y7dm1ysrm2z3va1cgn4g9f9p0wnfcq\";\n    };\n\n    aarch64-linux = {\n      url = \"https://github.com/crystal-lang/crystal/releases/download/1.19.1/crystal-1.19.1-1-linux-aarch64.tar.gz\";\n      sha256 = \"sha256:1c4xgwcxanss5scx4aj814qjb7amwbhbrv8izgfvfq73d8b7azng\";\n    };\n  }.${pkgs.stdenv.system});\n\n  boehmgc = pkgs.boehmgc.override {\n    enableLargeConfig = true;\n  };\n\n  stdLibDeps = with pkgs; [\n      boehmgc gmp libevent libiconv libxml2 libyaml openssl pcre2 zlib\n    ] ++ lib.optionals stdenv.isDarwin [ libiconv ];\n\n  tools = [ pkgs.hostname pkgs.git llvmPackages.bintools ] ++ pkgs.lib.optional (!llvmPackages.lldb.meta.broken) llvmPackages.lldb;\nin\n\npkgs.stdenv.mkDerivation rec {\n  name = \"crystal-dev\";\n\n  buildInputs = tools ++ stdLibDeps ++ [\n    latestCrystalBinary\n    pkgs.pkg-config\n    llvmPackages.libllvm\n    pkgs.libffi\n  ];\n\n  LLVM_CONFIG = \"${llvmPackages.libllvm.dev}/bin/llvm-config\";\n\n  MACOSX_DEPLOYMENT_TARGET = \"10.11\";\n}\n"
  },
  {
    "path": "spec/all_spec.cr",
    "content": "require \"./compiler_spec\"\nrequire \"./std_spec\"\nrequire \"./primitives_spec\"\n"
  },
  {
    "path": "spec/compiler/codegen/abi/aarch64_spec.cr",
    "content": "require \"spec\"\nrequire \"llvm\"\nrequire \"compiler/crystal/codegen/abi/aarch64\"\n\n{% if LibLLVM::BUILT_TARGETS.includes?(:aarch64) %}\n  LLVM.init_aarch64\n{% end %}\n\nprivate def abi\n  triple = \"aarch64-unknown-linux-gnu\"\n  target = LLVM::Target.from_triple(triple)\n  machine = target.create_target_machine(triple)\n  machine.enable_global_isel = false\n  Crystal::ABI::AArch64.new(machine)\nend\n\nprivate def test(msg, &block : Crystal::ABI, LLVM::Context ->)\n  it msg do\n    abi = abi()\n    ctx = LLVM::Context.new\n    block.call(abi, ctx)\n  end\nend\n\nclass Crystal::ABI\n  describe AArch64 do\n    {% if LibLLVM::BUILT_TARGETS.includes?(:aarch64) %}\n      describe \"align\" do\n        test \"for integer\" do |abi, ctx|\n          abi.align(ctx.int1).should be_a(::Int32)\n          abi.align(ctx.int1).should eq(1)\n          abi.align(ctx.int8).should eq(1)\n          abi.align(ctx.int16).should eq(2)\n          abi.align(ctx.int32).should eq(4)\n          abi.align(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.align(ctx.int8.pointer).should eq(8)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.align(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.align(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64])).should eq(8)\n          abi.align(ctx.struct([ctx.int8, ctx.int16])).should eq(2)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(1)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.align(ctx.int16.array(10)).should eq(2)\n        end\n      end\n\n      describe \"size\" do\n        test \"for integer\" do |abi, ctx|\n          abi.size(ctx.int1).should be_a(::Int32)\n          abi.size(ctx.int1).should eq(1)\n          abi.size(ctx.int8).should eq(1)\n          abi.size(ctx.int16).should eq(2)\n          abi.size(ctx.int32).should eq(4)\n          abi.size(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.size(ctx.int8.pointer).should eq(8)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.size(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.size(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64])).should eq(16)\n          abi.size(ctx.struct([ctx.int16, ctx.int8])).should eq(4)\n          abi.size(ctx.struct([ctx.int32, ctx.int8, ctx.int8])).should eq(8)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(12)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.size(ctx.int16.array(10)).should eq(20)\n        end\n      end\n\n      describe \"abi_info\" do\n        test \"does with primitives\" do |abi, ctx|\n          arg_types = [ctx.int32, ctx.int64]\n          return_type = ctx.int8\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(2)\n\n          info.arg_types[0].should eq(ArgType.direct(ctx.int32))\n          info.arg_types[1].should eq(ArgType.direct(ctx.int64))\n          info.return_type.should eq(ArgType.direct(ctx.int8))\n        end\n\n        test \"does with structs less than 64 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int8, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.int32))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.int32))\n        end\n\n        test \"does with structs between 64 and 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.int64.array(2)))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.int64.array(2)))\n        end\n\n        test \"does with structs larger than 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int64, ctx.int8])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, nil))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with homogeneous structs\" do |abi, ctx|\n          str = ctx.struct([ctx.float, ctx.float, ctx.float, ctx.float])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, ctx.float.array(4)))\n          info.return_type.should eq(ArgType.direct(str, ctx.float.array(4)))\n        end\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/abi/arm_spec.cr",
    "content": "require \"spec\"\nrequire \"llvm\"\nrequire \"compiler/crystal/codegen/abi/arm\"\n\n{% if LibLLVM::BUILT_TARGETS.includes?(:arm) %}\n  LLVM.init_arm\n{% end %}\n\nprivate def abi\n  triple = \"arm-unknown-linux-gnueabihf\"\n  target = LLVM::Target.from_triple(triple)\n  machine = target.create_target_machine(triple)\n  machine.enable_global_isel = false\n  Crystal::ABI::ARM.new(machine)\nend\n\nprivate def test(msg, &block : Crystal::ABI, LLVM::Context ->)\n  it msg do\n    abi = abi()\n    ctx = LLVM::Context.new\n    block.call(abi, ctx)\n  end\nend\n\nclass Crystal::ABI\n  describe ARM do\n    {% if LibLLVM::BUILT_TARGETS.includes?(:arm) %}\n      describe \"align\" do\n        test \"for integer\" do |abi, ctx|\n          abi.align(ctx.int1).should be_a(::Int32)\n          abi.align(ctx.int1).should eq(1)\n          abi.align(ctx.int8).should eq(1)\n          abi.align(ctx.int16).should eq(2)\n          abi.align(ctx.int32).should eq(4)\n          abi.align(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.align(ctx.int8.pointer).should eq(4)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.align(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.align(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64])).should eq(8)\n          abi.align(ctx.struct([ctx.int8, ctx.int16])).should eq(2)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(1)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.align(ctx.int16.array(10)).should eq(2)\n        end\n      end\n\n      describe \"size\" do\n        test \"for integer\" do |abi, ctx|\n          abi.size(ctx.int1).should be_a(::Int32)\n          abi.size(ctx.int1).should eq(1)\n          abi.size(ctx.int8).should eq(1)\n          abi.size(ctx.int16).should eq(2)\n          abi.size(ctx.int32).should eq(4)\n          abi.size(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.size(ctx.int8.pointer).should eq(4)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.size(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.size(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64])).should eq(16)\n          abi.size(ctx.struct([ctx.int16, ctx.int8])).should eq(4)\n          abi.size(ctx.struct([ctx.int32, ctx.int8, ctx.int8])).should eq(8)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(12)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.size(ctx.int16.array(10)).should eq(20)\n        end\n      end\n\n      describe \"abi_info\" do\n        test \"does with primitives\" do |abi, ctx|\n          arg_types = [ctx.int32, ctx.int64]\n          return_type = ctx.int8\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(2)\n\n          info.arg_types[0].should eq(ArgType.direct(ctx.int32))\n          info.arg_types[1].should eq(ArgType.direct(ctx.int64))\n          info.return_type.should eq(ArgType.direct(ctx.int8))\n        end\n\n        test \"does with structs less than 64 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int8, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.int32.array(1)))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.int32))\n        end\n\n        test \"does with structs between 64 and 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.int64.array(2)))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with structs larger than 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int64, ctx.int8])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.int64.array(3)))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/abi/avr_spec.cr",
    "content": "require \"spec\"\nrequire \"llvm\"\nrequire \"compiler/crystal/codegen/abi/avr\"\n\n{% if LibLLVM::BUILT_TARGETS.includes?(:avr) %}\n  LLVM.init_avr\n{% end %}\n\nprivate def abi\n  triple = \"avr-unknown-unknown-atmega328p\"\n  target = LLVM::Target.from_triple(triple)\n  machine = target.create_target_machine(triple)\n  machine.enable_global_isel = false\n  Crystal::ABI::AVR.new(machine)\nend\n\nprivate def test(msg, &block : Crystal::ABI, LLVM::Context ->)\n  it msg do\n    abi = abi()\n    ctx = LLVM::Context.new\n    block.call(abi, ctx)\n  end\nend\n\nclass Crystal::ABI\n  describe AVR do\n    {% if LibLLVM::BUILT_TARGETS.includes?(:avr) %}\n      describe \"align\" do\n        test \"for integer\" do |abi, ctx|\n          abi.align(ctx.int1).should be_a(::Int32)\n          abi.align(ctx.int1).should eq(1)\n          abi.align(ctx.int8).should eq(1)\n          abi.align(ctx.int16).should eq(1)\n          abi.align(ctx.int32).should eq(1)\n          abi.align(ctx.int64).should eq(1)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.align(ctx.int8.pointer).should eq(1)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.align(ctx.float).should eq(1)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.align(ctx.double).should eq(1)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64])).should eq(1)\n          abi.align(ctx.struct([ctx.int8, ctx.int16])).should eq(1)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(1)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.align(ctx.int16.array(10)).should eq(1)\n        end\n      end\n\n      describe \"size\" do\n        test \"for integer\" do |abi, ctx|\n          abi.size(ctx.int1).should be_a(::Int32)\n          abi.size(ctx.int1).should eq(1)\n          abi.size(ctx.int8).should eq(1)\n          abi.size(ctx.int16).should eq(2)\n          abi.size(ctx.int32).should eq(4)\n          abi.size(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.size(ctx.int8.pointer).should eq(2)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.size(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.size(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64])).should eq(12)\n          abi.size(ctx.struct([ctx.int16, ctx.int8])).should eq(3)\n          abi.size(ctx.struct([ctx.int32, ctx.int8, ctx.int8])).should eq(6)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int8], packed: true)).should eq(5)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.size(ctx.int16.array(10)).should eq(20)\n        end\n      end\n\n      describe \"abi_info\" do\n        {% for bits in [1, 8, 16, 32, 64] %}\n          test \"int{{bits}}\" do |abi, ctx|\n            arg_type = ArgType.direct(ctx.int{{bits}})\n            info = abi.abi_info([ctx.int{{bits}}], ctx.int{{bits}}, true, ctx)\n            info.arg_types.size.should eq(1)\n            info.arg_types[0].should eq(arg_type)\n            info.arg_types[0].kind.should eq(Crystal::ABI::ArgKind::Direct)\n            info.return_type.should eq(arg_type)\n            info.return_type.kind.should eq(Crystal::ABI::ArgKind::Direct)\n          end\n        {% end %}\n\n        test \"float\" do |abi, ctx|\n          arg_type = ArgType.direct(ctx.float)\n          info = abi.abi_info([ctx.float], ctx.float, true, ctx)\n          info.arg_types.size.should eq(1)\n          info.arg_types[0].should eq(arg_type)\n          info.arg_types[0].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.return_type.should eq(arg_type)\n          info.return_type.kind.should eq(Crystal::ABI::ArgKind::Direct)\n        end\n\n        test \"double\" do |abi, ctx|\n          arg_type = ArgType.direct(ctx.double)\n          info = abi.abi_info([ctx.double], ctx.double, true, ctx)\n          info.arg_types.size.should eq(1)\n          info.arg_types[0].should eq(arg_type)\n          info.arg_types[0].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.return_type.should eq(arg_type)\n          info.return_type.kind.should eq(Crystal::ABI::ArgKind::Direct)\n        end\n\n        test \"multiple arguments\" do |abi, ctx|\n          args = Array.new(9) { ctx.int16 }\n          info = abi.abi_info(args, ctx.int8, false, ctx)\n          info.arg_types.size.should eq(9)\n          info.arg_types.each(&.kind.should eq(Crystal::ABI::ArgKind::Direct))\n        end\n\n        test \"multiple arguments above registers\" do |abi, ctx|\n          args = Array.new(5) { ctx.int32 }\n          info = abi.abi_info(args, ctx.int8, false, ctx)\n          info.arg_types.size.should eq(5)\n          info.arg_types[0].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[1].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[2].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[3].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[4].kind.should eq(Crystal::ABI::ArgKind::Indirect)\n        end\n\n        test \"struct args within 18 bytes\" do |abi, ctx|\n          args = [\n            ctx.int8,                           # rounded to 2 bytes\n            ctx.struct([ctx.int32, ctx.int32]), # 8 bytes\n            ctx.struct([ctx.int32, ctx.int32]), # 8 bytes\n          ]\n          info = abi.abi_info(args, ctx.void, false, ctx)\n          info.arg_types.size.should eq(3)\n          info.arg_types[0].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[1].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[2].kind.should eq(Crystal::ABI::ArgKind::Direct)\n        end\n\n        test \"struct args over 18 bytes\" do |abi, ctx|\n          args = [\n            ctx.int32,                          # 4 bytes\n            ctx.struct([ctx.int32, ctx.int32]), # 8 bytes\n            ctx.struct([ctx.int32, ctx.int32]), # 8 bytes\n          ]\n          info = abi.abi_info(args, ctx.void, false, ctx)\n          info.arg_types.size.should eq(3)\n          info.arg_types[0].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[1].kind.should eq(Crystal::ABI::ArgKind::Direct)\n          info.arg_types[2].kind.should eq(Crystal::ABI::ArgKind::Indirect)\n        end\n\n        test \"returns struct within 8 bytes\" do |abi, ctx|\n          rty = ctx.struct([ctx.int32, ctx.int32])\n          info = abi.abi_info([] of LLVM::Type, rty, true, ctx)\n          info.return_type.kind.should eq(Crystal::ABI::ArgKind::Direct)\n        end\n\n        test \"returns struct over 8 bytes\" do |abi, ctx|\n          rty = ctx.struct([ctx.int32, ctx.int32, ctx.int8])\n          info = abi.abi_info([] of LLVM::Type, rty, true, ctx)\n          info.return_type.kind.should eq(Crystal::ABI::ArgKind::Indirect)\n        end\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/abi/x86_64_spec.cr",
    "content": "require \"spec\"\nrequire \"llvm\"\nrequire \"compiler/crystal/codegen/abi/x86_64\"\nrequire \"compiler/crystal/codegen/abi/x86_win64\"\n\n{% if LibLLVM::BUILT_TARGETS.includes?(:x86) %}\n  LLVM.init_x86\n{% end %}\n\nprivate def abi(win64 = false)\n  triple = win64 ? \"x86_64-windows-msvc\" : LLVM.default_target_triple.gsub(/^(.+?)-/, \"x86_64-\")\n  target = LLVM::Target.from_triple(triple)\n  machine = target.create_target_machine(triple)\n  machine.enable_global_isel = false\n  win64 ? Crystal::ABI::X86_Win64.new(machine) : Crystal::ABI::X86_64.new(machine)\nend\n\nprivate def test(msg, *, win64 = false, file = __FILE__, line = __LINE__, &block : Crystal::ABI, LLVM::Context ->)\n  it msg, file: file, line: line do\n    abi = abi(win64)\n    ctx = LLVM::Context.new\n    block.call(abi, ctx)\n  end\nend\n\nclass Crystal::ABI\n  describe X86_64 do\n    {% if LibLLVM::BUILT_TARGETS.includes?(:x86) %}\n      describe \"align\" do\n        test \"for integer\" do |abi, ctx|\n          abi.align(ctx.int1).should be_a(::Int32)\n          abi.align(ctx.int1).should eq(1)\n          abi.align(ctx.int8).should eq(1)\n          abi.align(ctx.int16).should eq(2)\n          abi.align(ctx.int32).should eq(4)\n          abi.align(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.align(ctx.int8.pointer).should eq(8)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.align(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.align(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64])).should eq(8)\n          abi.align(ctx.struct([ctx.int8, ctx.int16])).should eq(2)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(1)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.align(ctx.int16.array(10)).should eq(2)\n        end\n      end\n\n      describe \"size\" do\n        test \"for integer\" do |abi, ctx|\n          abi.size(ctx.int1).should be_a(::Int32)\n          abi.size(ctx.int1).should eq(1)\n          abi.size(ctx.int8).should eq(1)\n          abi.size(ctx.int16).should eq(2)\n          abi.size(ctx.int32).should eq(4)\n          abi.size(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.size(ctx.int8.pointer).should eq(8)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.size(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.size(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64])).should eq(16)\n          abi.size(ctx.struct([ctx.int16, ctx.int8])).should eq(4)\n          abi.size(ctx.struct([ctx.int32, ctx.int8, ctx.int8])).should eq(8)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(12)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.size(ctx.int16.array(10)).should eq(20)\n        end\n      end\n\n      describe \"abi_info\" do\n        test \"does with primitives\" do |abi, ctx|\n          arg_types = [ctx.int32, ctx.int64]\n          return_type = ctx.int8\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(2)\n\n          info.arg_types[0].should eq(ArgType.direct(ctx.int32))\n          info.arg_types[1].should eq(ArgType.direct(ctx.int64))\n          info.return_type.should eq(ArgType.direct(ctx.int8))\n        end\n\n        test \"does with structs less than 64 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int8, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))\n        end\n\n        test \"does with structs between 64 and 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64, ctx.int64])))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64, ctx.int64])))\n        end\n\n        test \"does with structs larger than 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int64, ctx.int8])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with non-packed struct containing packed struct with unaligned fields\" do |abi, ctx|\n          inner = ctx.struct([ctx.int16, ctx.int8], packed: true)\n          str = ctx.struct([ctx.int8, inner])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with non-packed struct containing single-element array of packed struct with unaligned fields\" do |abi, ctx|\n          inner = ctx.struct([ctx.int16, ctx.int8], packed: true).array(1)\n          str = ctx.struct([ctx.int8, inner])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n      end\n    {% end %}\n  end\n\n  describe X86_Win64 do\n    {% if LibLLVM::BUILT_TARGETS.includes?(:x86) %}\n      describe \"abi_info\" do\n        test \"does with structs between 64 and 128 bits\", win64: true do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with structs larger than 128 bits\", win64: true do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int64, ctx.int8])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with packed struct containing unaligned fields (#9873)\" do |abi, ctx|\n          str = ctx.struct([ctx.int8, ctx.int16], packed: true)\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with packed struct not containing unaligned fields\" do |abi, ctx|\n          str = ctx.struct([ctx.int16, ctx.int8], packed: true)\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))\n        end\n\n        test \"does with array of packed struct containing unaligned fields (#16312)\" do |abi, ctx|\n          str = ctx.struct([ctx.int8, ctx.int16], packed: true).array(1)\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n\n          str = ctx.struct([ctx.int8, ctx.int16], packed: true).array(2)\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n\n          str = ctx.struct([ctx.int16, ctx.int8], packed: true).array(2)\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n\n        test \"does with array of packed struct not containing unaligned fields (#16312)\" do |abi, ctx|\n          str = ctx.struct([ctx.int16, ctx.int8], packed: true).array(1)\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))\n          info.return_type.should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))\n        end\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/abi/x86_spec.cr",
    "content": "{% skip_file if flag?(:win32) %} # 32-bit windows is not supported\n\nrequire \"spec\"\nrequire \"llvm\"\nrequire \"compiler/crystal/codegen/abi/x86\"\n\n{% if LibLLVM::BUILT_TARGETS.includes?(:x86) %}\n  LLVM.init_x86\n{% end %}\n\nprivate def abi\n  triple = {% if flag?(:darwin) %}\n             \"i686-unknown-linux-gnu\"\n           {% else %}\n             LLVM.default_target_triple.gsub(/^(.+?)-/, \"i686-\")\n           {% end %}\n  target = LLVM::Target.from_triple(triple)\n  machine = target.create_target_machine(triple)\n  machine.enable_global_isel = false\n  Crystal::ABI::X86.new(machine)\nend\n\nprivate def test(msg, &block : Crystal::ABI, LLVM::Context ->)\n  it msg do\n    abi = abi()\n    ctx = LLVM::Context.new\n    block.call(abi, ctx)\n  end\nend\n\nclass Crystal::ABI\n  describe X86 do\n    {% if LibLLVM::BUILT_TARGETS.includes?(:x86) %}\n      describe \"align\" do\n        test \"for integer\" do |abi, ctx|\n          abi.align(ctx.int1).should be_a(::Int32)\n          abi.align(ctx.int1).should eq(1)\n          abi.align(ctx.int8).should eq(1)\n          abi.align(ctx.int16).should eq(2)\n          abi.align(ctx.int32).should eq(4)\n          abi.align(ctx.int64).should eq(4)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.align(ctx.int8.pointer).should eq(4)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.align(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.align(ctx.double).should eq(4)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64])).should eq(4)\n          abi.align(ctx.struct([ctx.int8, ctx.int16])).should eq(2)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.align(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(1)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.align(ctx.int16.array(10)).should eq(2)\n        end\n      end\n\n      describe \"size\" do\n        test \"for integer\" do |abi, ctx|\n          abi.size(ctx.int1).should be_a(::Int32)\n          abi.size(ctx.int1).should eq(1)\n          abi.size(ctx.int8).should eq(1)\n          abi.size(ctx.int16).should eq(2)\n          abi.size(ctx.int32).should eq(4)\n          abi.size(ctx.int64).should eq(8)\n        end\n\n        test \"for pointer\" do |abi, ctx|\n          abi.size(ctx.int8.pointer).should eq(4)\n        end\n\n        test \"for float\" do |abi, ctx|\n          abi.size(ctx.float).should eq(4)\n        end\n\n        test \"for double\" do |abi, ctx|\n          abi.size(ctx.double).should eq(8)\n        end\n\n        test \"for struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64])).should eq(12)\n          abi.size(ctx.struct([ctx.int16, ctx.int8])).should eq(4)\n          abi.size(ctx.struct([ctx.int32, ctx.int8, ctx.int8])).should eq(8)\n        end\n\n        test \"for packed struct\" do |abi, ctx|\n          abi.size(ctx.struct([ctx.int32, ctx.int64], packed: true)).should eq(12)\n        end\n\n        test \"for array\" do |abi, ctx|\n          abi.size(ctx.int16.array(10)).should eq(20)\n        end\n      end\n\n      describe \"abi_info\" do\n        test \"does with primitives\" do |abi, ctx|\n          arg_types = [ctx.int32, ctx.int64]\n          return_type = ctx.int8\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(2)\n\n          info.arg_types[0].should eq(ArgType.direct(ctx.int32))\n          info.arg_types[1].should eq(ArgType.direct(ctx.int64))\n          info.return_type.should eq(ArgType.direct(ctx.int8))\n        end\n\n        test \"does with structs less than 64 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int8, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, attr: LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, attr: LLVM::Attribute::StructRet))\n        end\n\n        test \"does with structs between 64 and 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int16])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, attr: LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, attr: LLVM::Attribute::StructRet))\n        end\n\n        test \"does with structs between 64 and 128 bits\" do |abi, ctx|\n          str = ctx.struct([ctx.int64, ctx.int64, ctx.int8])\n          arg_types = [str]\n          return_type = str\n\n          info = abi.abi_info(arg_types, return_type, true, ctx)\n          info.arg_types.size.should eq(1)\n\n          info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))\n          info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))\n        end\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/alias_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: alias\" do\n  it \"invokes methods on empty array of recursive alias (1)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"\")\n      require \"prelude\"\n\n      alias Alias = Array(Alias)\n\n      a = [] of Alias\n      b = a.map(&.to_s).join\n      CRYSTAL\n  end\n\n  it \"invokes methods on empty array of recursive alias (2)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"\")\n      require \"prelude\"\n\n      alias Alias = Nil | Array(Alias)\n\n      a = [] of Alias\n      b = a.map(&.to_s).join\n      CRYSTAL\n  end\n\n  it \"invokes methods on empty array of recursive alias (3)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"\")\n      require \"prelude\"\n\n      alias Alias = Nil | Array(Alias)\n\n      a = [] of Alias\n      b = a.map(&.to_s).join\n      CRYSTAL\n  end\n\n  it \"casts to recursive alias\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Bar(T)\n      end\n\n      alias Foo = Int32 | Bar(Foo)\n\n      a = 1.as(Foo)\n      b = a.as(Int32)\n      b\n      CRYSTAL\n  end\n\n  it \"casts to recursive alias\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Bar(T)\n        def self.new(&block : -> T)\n        end\n\n        def to_i!\n          0\n        end\n      end\n\n      alias Foo = Int32 | Bar(Foo)\n\n      def foo(n)\n        if n == 0\n          1\n        else\n          foo(n &- 1).as(Foo)\n        end\n      end\n\n      foo(2).to_i!\n      CRYSTAL\n  end\n\n  it \"doesn't break with alias for link attributes\" do\n    result = semantic(<<-CRYSTAL)\n      alias Foo = Int32\n\n      module Moo\n        alias Bar = Foo\n        alias Foo = Moo\n      end\n      CRYSTAL\n    result.program.link_annotations\n  end\n\n  it \"doesn't crash on cast to as recursive alias (#639)\" do\n    codegen(<<-CRYSTAL)\n      class Foo(T)\n      end\n\n      alias Type = Int32 | Foo(Type)\n\n      Foo(Type).new\n\n      ptr = Pointer(Type).malloc(1_u64)\n      ptr.value = 1.as(Type)\n      ptr.value = 1\n      CRYSTAL\n  end\n\n  it \"lazily solves aliases (#1346)\" do\n    run(<<-CRYSTAL)\n      struct Proc\n        def self.new(&block : self)\n          block\n        end\n      end\n\n      class Session; end\n\n      alias CmdHandler = Proc(Session, Int32)\n\n      class Session\n        def foo\n          1\n        end\n      end\n\n      class SmtpSession < Session\n        def foo\n          2\n        end\n      end\n\n      cmd = CmdHandler.new { |s| s.foo }\n      cmd.call(SmtpSession.new)\n      CRYSTAL\n  end\n\n  it \"codegens cast to alias that includes bool\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      alias Foo = Bool | Array(Foo)\n\n      a = false.as(Foo)\n      if a\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"overloads alias against generic (1) (#3261)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(2)\n      class Foo(T)\n      end\n\n      alias FooString = Foo(String)\n\n      def take(foo : Foo(String))\n        1\n      end\n\n      def take(foo : FooString)\n        2\n      end\n\n      take(Foo(String).new)\n      CRYSTAL\n  end\n\n  it \"overloads alias against generic (2) (#3261)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(1)\n      class Foo(T)\n      end\n\n      alias FooString = Foo(String)\n\n      def take(foo : FooString)\n        2\n      end\n\n      def take(foo : Foo(String))\n        1\n      end\n\n      take(Foo(String).new)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/and_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: and\" do\n  it \"codegens and with bool false and false\" do\n    run(\"false && false\", Bool).should be_false\n  end\n\n  it \"codegens and with bool false and true\" do\n    run(\"false && true\", Bool).should be_false\n  end\n\n  it \"codegens and with bool true and true\" do\n    run(\"true && true\", Bool).should be_true\n  end\n\n  it \"codegens and with bool true and false\" do\n    run(\"true && false\", Bool).should be_false\n  end\n\n  it \"codegens and with bool and int 1\" do\n    run(\"struct Bool; def to_i!; 0; end; end; (false && 2).to_i!\", Int32).should eq(0)\n  end\n\n  it \"codegens and with bool and int 2\" do\n    run(\"struct Bool; def to_i!; 0; end; end; (true && 2).to_i!\", Int32).should eq(2)\n  end\n\n  it \"codegens and with primitive type other than bool\" do\n    run(\"1 && 2\", Int32).should eq(2)\n  end\n\n  it \"codegens and with primitive type other than bool with union\" do\n    run(\"(1 && 1.5).to_f\", Float64).should eq(1.5)\n  end\n\n  it \"codegens and with primitive type other than bool\" do\n    run(<<-CRYSTAL, Int32).should eq(0)\n      struct Nil; def to_i!; 0; end; end\n      (nil && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with nilable as left node 1\" do\n    run(<<-CRYSTAL, Int32).should eq(0)\n      struct Nil; def to_i!; 0; end; end\n      class Object; def to_i!; -1; end; end\n      a = Reference.new\n      a = nil\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with nilable as left node 2\" do\n    run(<<-CRYSTAL, Int32).should eq(2)\n      class Object; def to_i!; -1; end; end\n      a = nil\n      a = Reference.new\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with non-false union as left node\" do\n    run(<<-CRYSTAL, Int32).should eq(2)\n      a = 1.5\n      a = 1\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with nil union as left node 1\" do\n    run(<<-CRYSTAL, Int32).should eq(2)\n      require \"nil\"\n      a = nil\n      a = 1\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with nil union as left node 2\" do\n    run(<<-CRYSTAL, Int32).should eq(0)\n      struct Nil; def to_i!; 0; end; end\n      a = 1\n      a = nil\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 1\" do\n    run(<<-CRYSTAL, Int32).should eq(2)\n      struct Bool; def to_i!; 0; end; end\n      a = false\n      a = 1\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 2\" do\n    run(<<-CRYSTAL, Int32).should eq(0)\n      struct Bool; def to_i!; 0; end; end\n      a = 1\n      a = false\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 3\" do\n    run(<<-CRYSTAL, Int32).should eq(2)\n      struct Bool; def to_i!; 0; end; end\n      a = 1\n      a = true\n      (a && 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 1\" do\n    run(<<-CRYSTAL, Int32).should eq(3)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = false\n      a = nil\n      a = 2\n      (a && 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 2\" do\n    run(<<-CRYSTAL, Int32).should eq(1)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = nil\n      a = 2\n      a = false\n      (a && 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 3\" do\n    run(<<-CRYSTAL, Int32).should eq(3)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = nil\n      a = 2\n      a = true\n      (a && 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens and with bool union as left node 4\" do\n    run(<<-CRYSTAL, Int32).should eq(0)\n      struct Nil; def to_i!; 0; end; end\n      struct Bool; def to_i!; 1; end; end\n      a = 2\n      a = true\n      a = nil\n      (a && 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens assign in right node, after must be nilable\" do\n    run(<<-CRYSTAL, Bool).should be_true\n      a = 1 == 2 && (b = Reference.new)\n      b.nil?\n      CRYSTAL\n  end\n\n  it \"codegens assign in right node, inside if must not be nil\" do\n    run(<<-CRYSTAL, Int32).should eq(1)\n      struct Nil; end\n      class Foo; def foo; 1; end; end\n\n      if 1 == 1 && (b = Foo.new)\n        b.foo\n      else\n        0\n      end\n      CRYSTAL\n  end\n\n  it \"codegens assign in right node, after if must be nilable\" do\n    run(<<-CRYSTAL, Bool).should be_true\n      if 1 == 2 && (b = Reference.new)\n      end\n      b.nil?\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/array_literal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: array literal spec\" do\n  it \"creates custom non-generic array\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Custom\n        def initialize\n          @value = 0\n        end\n\n        def <<(element)\n          @value &+= element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      custom = Custom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom generic array\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Custom(T)\n        def initialize\n          @value = 0\n        end\n\n        def <<(element : T)\n          @value &+= element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      custom = Custom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom generic array with type var\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Custom(T)\n        def initialize\n          @value = 0\n        end\n\n        def <<(element : T)\n          @value &+= element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      custom = Custom(Int32) {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom generic array via alias\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Custom(T)\n        def initialize\n          @value = 0\n        end\n\n        def <<(element : T)\n          @value &+= element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      alias MyCustom = Custom\n\n      custom = MyCustom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom generic array via alias (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Custom(T)\n        def initialize\n          @value = 0\n        end\n\n        def <<(element : T)\n          @value &+= element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      alias MyCustom = Custom(Int32)\n\n      custom = MyCustom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom non-generic array in nested module\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo::Custom\n        def initialize\n          @value = 0\n        end\n\n        def <<(element)\n          @value &+= element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      custom = Foo::Custom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom non-generic array in module\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      module Moo\n        class Custom\n          def initialize\n            @value = 0\n          end\n\n          def <<(element)\n            @value &+= element\n          end\n\n          def value\n            @value\n          end\n        end\n      end\n\n      custom = Moo::Custom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom generic array in module (#5684)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      module Moo\n        class Custom(T)\n          def initialize\n            @value = 0\n          end\n\n          def <<(element : T)\n            @value &+= element\n          end\n\n          def value\n            @value\n          end\n        end\n      end\n\n      custom = Moo::Custom {1, 2, 3}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom non-generic array, with splats\" do\n    run(<<-CRYSTAL).to_i.should eq(123456)\n      #{enumerable_element_type}\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def first\n          0\n        end\n\n        def each\n          yield @x\n          yield @x &+ 1\n        end\n      end\n\n      class Custom\n        def initialize\n          @value = 0\n        end\n\n        def <<(element)\n          @value = @value &* 10 &+ element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      custom = Custom {1, *Foo.new(2), 4, *Foo.new(5)}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates custom generic array, with splats\" do\n    run(<<-CRYSTAL).to_i.should eq(123456)\n      #{enumerable_element_type}\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def first\n          0\n        end\n\n        def each\n          yield @x\n          yield @x &+ 1\n        end\n      end\n\n      class Custom(T)\n        def initialize\n          @value = 0\n        end\n\n        def <<(element : T)\n          @value = @value &* 10 &+ element\n        end\n\n        def value\n          @value\n        end\n      end\n\n      custom = Custom {1, *Foo.new(2), 4, *Foo.new(5)}\n      custom.value\n      CRYSTAL\n  end\n\n  it \"creates typed array\" do\n    run(\"require \\\"prelude\\\"; typeof([1, 2] of Int8)\").to_string.should eq(\"Array(Int8)\")\n  end\n\n  it \"assignment in array literal works\" do\n    run(\"require \\\"prelude\\\"; [a = 1]; a\").to_i.should eq(1)\n  end\n\n  it \"assignment in array-like literal works\" do\n    run(\"require \\\"prelude\\\"; Array(Int32){a = 1}; a\").to_i.should eq(1)\n  end\nend\n\nprivate def enumerable_element_type\n  %(\n    module Enumerable(T)\n      def self.element_type(x)\n        x.each { |elem| return elem }\n        ret = uninitialized NoReturn\n        ret\n      end\n    end\n  )\nend\n"
  },
  {
    "path": "spec/compiler/codegen/asm_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: asm\" do\n  # TODO: arm asm tests\n  {% if flag?(:i386) || flag?(:x86_64) %}\n    it \"passes correct string length to LLVM\" do\n      run <<-CRYSTAL\n        asm(\"// 😂😂\n        nop\n        nop\")\n        CRYSTAL\n    end\n\n    it \"codegens without inputs\" do\n      run(<<-CRYSTAL).to_i.should eq(1234)\n        dst = uninitialized Int32\n        asm(\"mov $$1234, $0\" : \"=r\"(dst))\n        dst\n        CRYSTAL\n    end\n\n    it \"codegens with two outputs\" do\n      run(<<-CRYSTAL).to_i.should eq(0x12345678)\n        dst1 = uninitialized Int32\n        dst2 = uninitialized Int32\n        asm(\"\n          mov $$0x1234, $0\n          mov $$0x5678, $1\" : \"=r\"(dst1), \"=r\"(dst2))\n        (dst1.unsafe_shl(16)) | dst2\n        CRYSTAL\n    end\n\n    it \"codegens with one input\" do\n      run(<<-CRYSTAL).to_i.should eq(1234)\n        src = 1234\n        dst = uninitialized Int32\n        asm(\"mov $1, $0\" : \"=r\"(dst) : \"r\"(src))\n        dst\n        CRYSTAL\n    end\n\n    it \"codegens with two inputs\" do\n      run(<<-CRYSTAL).to_i.should eq(42)\n        c = uninitialized Int32\n        a = 20\n        b = 22\n        asm(\n          \"add $2, $0\"\n             : \"=r\"(c)\n             : \"0\"(a), \"r\"(b)\n          )\n        c\n        CRYSTAL\n    end\n\n    it \"codegens with intel dialect\" do\n      run(<<-CRYSTAL).to_i.should eq(1234)\n        dst = uninitialized Int32\n        asm(\"mov dword ptr [$0], 1234\" :: \"r\"(pointerof(dst)) :: \"intel\")\n        dst\n        CRYSTAL\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/compiler/codegen/automatic_cast_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: automatic cast\" do\n  it \"casts literal integer (Int32 -> Int64)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      def foo(x : Int64)\n        x\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int64 -> Int32, ok)\" do\n    run(<<-CRYSTAL).to_i.should eq(2147483647)\n      def foo(x : Int32)\n        x\n      end\n\n      foo(2147483647_i64)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int32 -> Float32)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      def foo(x : Float32)\n        x\n      end\n\n      foo(12345).to_i!\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int32 -> Float64)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      def foo(x : Float64)\n        x\n      end\n\n      foo(12345).to_i!\n      CRYSTAL\n  end\n\n  it \"casts literal float (Float32 -> Float64)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      def foo(x : Float64)\n        x\n      end\n\n      foo(12345.0_f32).to_i!\n      CRYSTAL\n  end\n\n  it \"casts literal float (Float64 -> Float32)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      def foo(x : Float32)\n        x\n      end\n\n      foo(12345.0).to_i!\n      CRYSTAL\n  end\n\n  it \"casts symbol literal to enum\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      :four\n\n      enum Foo\n        One\n        Two\n        Three\n      end\n\n      def foo(x : Foo)\n        x\n      end\n\n      foo(:three)\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in ivar assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        @x : Int64\n\n        def initialize\n          @x = 10\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Symbol to Enum in ivar assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      enum E\n        One\n        Two\n        Three\n      end\n\n      class Foo\n        @x : E\n\n        def initialize\n          @x = :three\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in cvar assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        @@x : Int64 = 0_i64\n\n        def self.x\n          @@x = 10\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in lvar assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      x : Int64\n      x = 123\n      x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in ivar type declaration\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        @x : Int64 = 10\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Symbol to Enum in ivar type declaration\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      class Foo\n        @x : Color = :blue\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in cvar type declaration\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        @@x : Int64 = 10\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 -> Int64 in arg restriction\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def foo(x : Int64 = 123)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in ivar type declaration in generic\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo(T)\n        @x : T = 10\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int64).new.x\n      CRYSTAL\n  end\n\n  it \"does multidispatch with automatic casting (1) (#8217)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo(mode : Int64, x : Int32)\n        10\n      end\n      def foo(mode : Int64, x : String)\n        20\n      end\n      foo(1, 1 || \"a\")\n      CRYSTAL\n  end\n\n  it \"does multidispatch with automatic casting (2) (#8217)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      def foo(mode : Int64, x : Int32)\n        10\n      end\n      def foo(mode : Int64, x : String)\n        20\n      end\n      foo(1, \"a\" || 1)\n      CRYSTAL\n  end\n\n  it \"does multidispatch with automatic casting (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def foo(x : UInt8)\n          2\n        end\n      end\n\n      class Baz < Foo\n        def foo(x : UInt8)\n          3\n        end\n      end\n\n      Bar.new.as(Foo).foo(1)\n      CRYSTAL\n  end\n\n  it \"doesn't autocast number on union (#8655)\" do\n    run(<<-CRYSTAL).to_i.should eq(255)\n      def foo(x : UInt8 | Int32, y : Float64)\n        x\n      end\n\n      foo(255, 60)\n      CRYSTAL\n  end\n\n  it \"casts integer variable to larger type (#9565)\" do\n    run(<<-CRYSTAL).to_i64.should eq(123)\n      def foo(x : Int64)\n        x\n      end\n\n      x = 123\n      foo(x)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/block_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: block\" do\n  it \"generate inline\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"passes yield arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield 1\n      end\n\n      foo do |x|\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"pass arguments to yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      def foo(a)\n        yield a\n      end\n\n      foo(3) do |x|\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"pass self to yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      struct Int\n        def foo\n          yield self\n        end\n      end\n\n      3.foo do |x|\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"pass self and arguments to yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      struct Int\n        def foo(i)\n          yield self, i\n        end\n      end\n\n      3.foo(2) do |x, i|\n        x &+ i\n      end\n      CRYSTAL\n  end\n\n  it \"allows access to local variables\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield\n      end\n\n      x = 1\n      foo do\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"can access instance vars from yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize\n          @x = 1\n        end\n        def foo\n          yield @x\n        end\n      end\n\n      Foo.new.foo do |x|\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"can set instance vars from yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def foo\n          @x = yield\n        end\n        def value\n          @x\n        end\n      end\n\n      a = Foo.new\n      a.foo { 2 }\n      a.value\n      CRYSTAL\n  end\n\n  it \"can use instance methods from yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          yield value\n        end\n        def value\n          1\n        end\n      end\n\n      Foo.new.foo { |x| x &+ 1 }\n      CRYSTAL\n  end\n\n  it \"can call methods from block when yielder is an instance method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          yield\n        end\n      end\n\n      def bar\n        1\n      end\n\n      Foo.new.foo { bar }\n      CRYSTAL\n  end\n\n  it \"nested yields\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def bar\n        yield\n      end\n\n      def foo\n        bar { yield }\n      end\n\n      a = foo { 1 }\n      CRYSTAL\n  end\n\n  it \"assigns yield to argument\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(x)\n        yield\n        x = 1\n      end\n\n      foo(1) { 1 }\n      CRYSTAL\n  end\n\n  it \"can use global constant\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      FOO = 1\n      def foo\n        yield\n        FOO\n      end\n      foo { }\n      CRYSTAL\n  end\n\n  it \"return from yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield\n        return 1\n      end\n\n      foo { }\n      2\n      CRYSTAL\n  end\n\n  it \"return from block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      def bar\n        foo { return 1 }\n        2\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"return from yielder function (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n        return 1 if true\n        return 2\n      end\n\n      def bar\n        foo {}\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"union value of yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n        a = 1.1\n        a = 1\n        a\n      end\n\n      foo {}.to_i!\n      CRYSTAL\n  end\n\n  it \"allow return from function called from yielder function\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        return 2\n      end\n\n      def bar\n        yield\n        foo\n        1\n      end\n\n      bar {}\n      CRYSTAL\n  end\n\n  it \"\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n        true ? return 1 : return 1.1\n      end\n\n      foo {}.to_i!\n      CRYSTAL\n  end\n\n  it \"return from block that always returns from function that always yields inside if block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def bar\n        yield\n        2\n      end\n\n      def foo\n        if true\n          bar { return 1 }\n        else\n          0\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"return from block that always returns from function that conditionally yields\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def bar\n        if true\n          yield\n        end\n      end\n\n      def foo\n        bar { return 1 }\n        2\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"call block from dispatch\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def bar(y)\n        yield y\n      end\n\n      def foo\n        x = 1.1\n        x = 1\n        bar(x) { |z| z }\n      end\n\n      foo.to_i!\n      CRYSTAL\n  end\n\n  it \"call block from dispatch and use local vars\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      require \"prelude\"\n\n      def bar(y)\n        yield y\n      end\n\n      def foo\n        total = 0\n        x = 1.5\n        bar(x) { |z| total += z }\n        x = 1\n        bar(x) { |z| total += z }\n        x = 1.5\n        bar(x) { |z| total += z }\n        total\n      end\n\n      foo.to_i\n      CRYSTAL\n  end\n\n  it \"break without value returns nil\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"nil\"\n      require \"value\"\n\n      def foo\n        yield\n        1\n      end\n\n      x = foo do\n        break if 1 == 1\n      end\n\n      x.nil?\n      CRYSTAL\n  end\n\n  it \"break block with yielder inside while\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      require \"prelude\"\n      a = 0\n      10.times do\n        a += 1\n        break if a > 5\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"break from block returns from yielder\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n        yield\n      end\n\n      a = 0\n      foo { a &+= 1; break }\n      a\n      CRYSTAL\n  end\n\n  it \"break from block with value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        while true\n          yield\n          a = 3\n        end\n      end\n\n      foo do\n        break 1\n      end\n      CRYSTAL\n  end\n\n  it \"returns from block with value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        while true\n          yield\n          a = 3\n        end\n      end\n\n      def bar\n        foo do\n          return 1\n        end\n      end\n\n      bar.to_i\n      CRYSTAL\n  end\n\n  it \"doesn't codegen after while that always yields and breaks\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        while true\n          yield\n        end\n        1\n      end\n\n      foo do\n        break 2\n      end\n      CRYSTAL\n  end\n\n  it \"break from block with value\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      require \"prelude\"\n      10.times { break 20 }\n      CRYSTAL\n  end\n\n  it \"doesn't codegen call if arg yields and always breaks\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"nil\"\n\n      def foo\n        1 &+ yield\n      end\n\n      foo { break 2 }.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens nested return\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def bar\n        yield\n        a = 1\n      end\n\n      def foo\n        bar { yield }\n      end\n\n      def z\n        foo { return 2 }\n      end\n\n      z\n      CRYSTAL\n  end\n\n  it \"codegens nested break\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def bar\n        yield\n        a = 1\n      end\n\n      def foo\n        bar { yield }\n      end\n\n      foo { break 2 }\n      CRYSTAL\n  end\n\n  it \"codegens call with block with call with arg that yields\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def bar\n        yield\n        a = 2\n      end\n\n      def foo\n        bar { 1 &+ yield }\n      end\n\n      foo { break 3 }\n      CRYSTAL\n  end\n\n  it \"can break without value from yielder that returns nilable (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def foo\n        yield\n        \"\"\n      end\n\n      a = foo do\n        break\n      end\n\n      a.nil?\n      CRYSTAL\n  end\n\n  it \"can break without value from yielder that returns nilable (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def foo\n        yield\n        \"\"\n      end\n\n      a = foo do\n        break nil\n      end\n\n      a.nil?\n      CRYSTAL\n  end\n\n  it \"break with value from yielder that returns a nilable\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      def foo\n        yield\n        \"\"\n      end\n\n      a = foo do\n        break if false\n        break \"\"\n      end\n\n      a.nil?\n      CRYSTAL\n  end\n\n  it \"can use self inside a block called from dispatch\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      struct Nil; def to_i!; 0; end; end\n\n      class Foo\n        def do; yield; end\n      end\n      class Bar < Foo\n      end\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      struct Int\n        def foo\n          x = Foo.new\n          x = Bar.new\n          x.do { Global.x = self }\n        end\n      end\n\n      123.foo\n      Global.x.to_i!\n      CRYSTAL\n  end\n\n  it \"return from block called from dispatch\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def do; yield; end\n      end\n      class Bar < Foo\n      end\n\n      def foo\n        x = Foo.new\n        x = Bar.new\n        x.do { return 1 }\n        0\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"breaks from while in function called from block\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield\n      end\n\n      def bar\n        while true\n          break 1\n        end\n        2\n      end\n\n      foo do\n        bar\n      end\n      CRYSTAL\n  end\n\n  it \"allows modifying yielded value (with literal)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield 1\n      end\n\n      foo { |x| x = 2; x }\n      CRYSTAL\n  end\n\n  it \"allows modifying yielded value (with variable)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        a = 1\n        yield a\n        a\n      end\n\n      foo { |x| x = 2; x }\n      CRYSTAL\n  end\n\n  it \"it yields nil from another call\" do\n    run(<<-CRYSTAL)\n      require \"bool\"\n\n      def foo(key, default)\n        foo(key) { default }\n      end\n\n      def foo(key)\n        if !(true)\n          return yield key\n        end\n        yield key\n      end\n\n      foo(1, nil)\n      CRYSTAL\n  end\n\n  it \"allows yield from dispatch call\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(x : Value)\n        yield 1\n      end\n\n      def foo(x : Int)\n        yield 2\n      end\n\n      def bar\n        a = 1; a = 1.1\n        foo(a) do |i|\n          yield i\n        end\n      end\n\n      x = 0\n      bar { |i| x = i }\n      x\n      CRYSTAL\n  end\n\n  it \"block with nilable type\" do\n    run(<<-CRYSTAL)\n      class Foo\n        def foo\n          yield 1\n        end\n      end\n\n      class Bar\n        def foo\n          yield 2\n          Reference.new\n        end\n      end\n\n      a = Foo.new || Bar.new\n      a.foo {}\n      CRYSTAL\n  end\n\n  it \"block with nilable type 2\" do\n    run(<<-CRYSTAL)\n      class Foo\n        def foo\n          yield 1\n          nil\n        end\n      end\n\n      class Bar\n        def foo\n          yield 2\n          Reference.new\n        end\n      end\n\n      a = Foo.new || Bar.new\n      a.foo {}\n      CRYSTAL\n  end\n\n  it \"codegens block with nilable type with return (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def foo\n        if yield\n          return Reference.new\n        end\n        nil\n      end\n\n      foo { false }.nil?\n      CRYSTAL\n  end\n\n  it \"codegens block with nilable type with return (2)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      def foo\n        if yield\n          return nil\n        end\n        Reference.new\n      end\n\n      foo { false }.nil?\n      CRYSTAL\n  end\n\n  it \"codegens block with union with return\" do\n    run(<<-CRYSTAL)\n      def foo\n        yield\n\n        return 1 if 1 == 2\n\n        nil\n      end\n\n      x = foo { }\n      1\n      CRYSTAL\n  end\n\n  it \"codegens if with call with block (ssa issue)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def bar\n        yield\n      end\n\n      def foo\n        if 1 == 2\n          bar do\n            x = 1\n          end\n        else\n          3\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens block with return and yield and no return\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo(key)\n        foo(key) { LibC.exit }\n      end\n\n      def foo(key)\n        if 1 == 1\n          return 2\n        end\n        yield\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"codegens while/break inside block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      foo do\n        while true\n          break\n        end\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"codegens block with union arg (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield 1 || 1.5\n      end\n\n      foo { |x| x }.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens block with union arg (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Number\n        def abs\n          self\n        end\n      end\n\n      class Foo(T)\n        def initialize(x : T)\n          @x = x\n        end\n\n        def each\n          yield @x\n        end\n      end\n\n      a = Foo.new(1) || Foo.new(1.5)\n      a.each do |x|\n        x.abs\n      end.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens block with virtual type arg\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Var(T)\n        def initialize(x : T)\n          @x = x\n        end\n\n        def each\n          yield @x\n        end\n      end\n\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def bar\n          2\n        end\n      end\n\n      a = Var.new(Foo.new) || Var.new(Bar.new)\n      a.each do |x|\n        x.bar\n      end\n      CRYSTAL\n  end\n\n  it \"codegens call with blocks of different type without args\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      foo { 1.1 }\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"codegens dispatch with block and break (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def each\n          yield @x\n        end\n      end\n\n      n = 0\n      f = Foo.new(1) || Foo.new(1.5)\n      f.each do |x|\n        break if x > 2\n        n += x\n      end\n      n.to_i\n      CRYSTAL\n  end\n\n  it \"codegens dispatch with block and break (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      a = [1, 2, 3] || [1.5]\n      n = 0\n      a.each do |x|\n        break if x > 2\n        n += x\n      end\n      n.to_i\n      CRYSTAL\n  end\n\n  it \"codegens block call when argument type changes\" do\n    run(<<-CRYSTAL)\n      def foo(x)\n        while 1 == 2\n          x = 1.5\n          yield\n        end\n      end\n\n      foo(1) do\n      end\n      CRYSTAL\n  end\n\n  it \"returns void when called with block\" do\n    run(<<-CRYSTAL)\n      fun foo : Void\n      end\n\n      def bar\n        yield\n        foo\n      end\n\n      bar {}\n      CRYSTAL\n  end\n\n  it \"executes yield expression if no arg is given for block\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        a = 1\n        yield (a = 2)\n        a\n      end\n\n      foo { }\n      CRYSTAL\n  end\n\n  it \"codegens bug with block and arg and var\" do\n    run(<<-CRYSTAL).to_i.should eq(65)\n      def foo\n        yield 1\n      end\n\n      foo { |a| x = a }\n\n      foo do\n        a = 'A'\n        a.ord\n      end\n      CRYSTAL\n  end\n\n  it \"allows using var as block arg with outer var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield 'a'\n      end\n\n      a = foo do |a|\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"allows initialize with yield (#224)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = yield 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new do |a|\n        a &+ 1\n      end\n      foo.x\n      CRYSTAL\n  end\n\n  it \"uses block inside array literal (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        yield 1\n      end\n\n      ary = [foo { |x| x.abs }]\n      ary[0]\n      CRYSTAL\n  end\n\n  it \"codegens method invocation on a object of a captured block with a type that was never instantiated\" do\n    codegen(<<-CRYSTAL)\n      class Bar\n        def initialize(@bar : NoReturn)\n        end\n\n        def bar\n          @bar\n        end\n\n        def baz(x)\n        end\n      end\n\n      def foo(&block : Bar ->)\n        block\n      end\n\n      def method(bar)\n        bar.bar\n      end\n\n      foo do |bar|\n        bar.baz method(bar).baz\n      end\n      CRYSTAL\n  end\n\n  it \"codegens method invocation on a object of a captured block with a type that was never instantiated (2)\" do\n    codegen(<<-CRYSTAL)\n      class Bar\n        def initialize(@bar : NoReturn)\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      def foo(&block : Bar ->)\n        block\n      end\n\n      def method(bar)\n        bar.bar\n      end\n\n      def baz(x)\n      end\n\n      foo do |bar|\n        baz method(bar).baz\n      end\n      CRYSTAL\n  end\n\n  it \"codegens bug with yield not_nil! that is never not nil\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      class Object\n        def not_nil!\n          self\n        end\n      end\n\n      struct Nil\n        def not_nil!\n          LibC.exit(1)\n        end\n\n        def to_i!\n          0\n        end\n      end\n\n      def foo\n        key = nil\n        if 1 == 2\n          yield key.not_nil!\n        end\n        yield 1\n      end\n\n      extra = nil\n\n      foo do |key|\n        if 1 == 1\n          extra = 1\n          extra &+ key\n        end\n      end\n\n      extra.to_i!\n      CRYSTAL\n  end\n\n  it \"uses block var with same name as local var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield \"hello\"\n      end\n\n      a = 1\n      foo do |a|\n        a\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't crash on untyped array to_s\" do\n    run(<<-CRYSTAL).to_string.should eq(\"[]\")\n      require \"prelude\"\n\n      class Bar(T)\n      end\n\n      class Foo(K)\n        @foo : Nil\n\n        def foo\n          Array(typeof(yield @foo.not_nil!)).new\n        end\n      end\n\n      Foo(Int32).new.foo { |k| k + 1 }.to_s\n      CRYSTAL\n  end\n\n  it \"codegens block which always breaks but never enters (#494)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo\n        while 1 == 2\n          yield\n        end\n        3\n      end\n\n      foo do\n        break 10\n      end\n      CRYSTAL\n  end\n\n  it \"codegens block bug with conditional next and unconditional break (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      def foo\n        yield 1\n        yield 2\n        yield 3\n      end\n\n      a = 0\n      foo do |x|\n        a &+= x\n        next if true\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"codegens block bug with conditional next and unconditional break (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      def foo\n        yield 1\n        yield 2\n        yield 3\n      end\n\n      a = 0\n      foo do |x|\n        a &+= x\n        next if 1 == 1\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"codegens block bug with conditional next and unconditional break (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        a = 1234\n        a = yield 1\n        Global.x = a\n        a\n      end\n\n      foo do |x|\n        next x if 1 == 1\n        break 0\n      end\n      Global.x\n      CRYSTAL\n  end\n\n  it \"codegens block bug with conditional next and unconditional break (4)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        bar(yield 1)\n      end\n\n      def bar(x)\n        Global.x = x\n      end\n\n      foo do |x|\n        next x if 1 == 1\n        break 0\n      end\n      Global.x\n      CRYSTAL\n  end\n\n  it \"returns from proc literal\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      foo = ->{\n        if 1 == 1\n          return 10\n        end\n\n        20\n      }\n\n      foo.call\n      CRYSTAL\n  end\n\n  it \"does next from captured block\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo(&block : -> T) forall T\n        block\n      end\n\n      f = foo do\n        if 1 == 1\n          next 10\n        end\n\n        next 20\n      end\n\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens captured block with next inside yielded block (#2097)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def foo\n        yield\n      end\n\n      def bar(&block : -> Int32)\n        block\n      end\n\n      foo do\n        block = bar do\n          next 123\n        end\n        block.call\n      end\n      CRYSTAL\n  end\n\n  it \"codegens captured block that returns union, but proc only returns a single type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      def run_callbacks(&block : -> Int32 | String)\n        block.call\n      end\n\n      f = run_callbacks { \"foo\" }\n      if f.is_a?(String)\n        f\n      else\n        \"oops\"\n      end\n      CRYSTAL\n  end\n\n  it \"yields inside yield (#682)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      def foo\n        yield(1, (yield 3))\n      end\n\n      a = 0\n      foo do |x|\n        a &+= x\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"yields splat\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      def foo\n        tup = {1, 2, 3}\n        yield *tup\n      end\n\n      foo do |x, y, z|\n        x &+ y &+ z\n      end\n      CRYSTAL\n  end\n\n  it \"yields more exps than block arg, through splat\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield *{1, 2}\n      end\n\n      foo do |x|\n        x\n      end\n      CRYSTAL\n  end\n\n  it \"uses splat in block argument\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      def foo\n        yield 1, 2, 3\n      end\n\n      foo do |*args|\n        args[0] &+ args[1] &+ args[2]\n      end\n      CRYSTAL\n  end\n\n  it \"uses splat in block argument, many args\" do\n    run(<<-CRYSTAL).to_i.should eq(((((1 + 2) * 3) - 4) * 5) - 6)\n      def foo\n        yield 1, 2, 3, 4, 5, 6\n      end\n\n      foo do |x, y, *z, w|\n        ((((x &+ y) &* z[0]) &- z[1]) &* z[2]) &- w\n      end\n      CRYSTAL\n  end\n\n  it \"uses block splat argument with union types\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo\n        yield 1\n        yield 2.5\n      end\n\n      total = 0\n      foo do |*args|\n        total &+= args[0].to_i!\n      end\n      total\n      CRYSTAL\n  end\n\n  it \"works if block has both splat and non-splat underscore parameters\" do\n    run(<<-CRYSTAL, Int32).should eq(34)\n      def foo(&)\n        yield 1, 2, 4, 8, 16, 32\n      end\n\n      foo do |_, a, *_, b|\n        a &+ b\n      end\n      CRYSTAL\n  end\n\n  it \"works if block has both splat parameter and multiple non-splat underscore parameters\" do\n    run(<<-CRYSTAL, Int32).should eq(40)\n      def foo(&)\n        yield 1, 2, 4, 8, \"\", 32\n      end\n\n      foo do |_, *a, _, b|\n         a[2] &+ b\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple\" do\n    run(<<-CRYSTAL).to_i.should eq((1 + 2) * 4)\n      def foo\n        tup = {1, 2, 4}\n        yield tup\n      end\n\n      foo do |x, y, z|\n        (x &+ y) &* z\n      end\n      CRYSTAL\n  end\n\n  it \"unpacks tuple but doesn't override local variables\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo\n        yield({10, 20}, {30, 40})\n      end\n\n      x = 1\n      y = 2\n      z = 3\n      w = 4\n      foo do |(x, y), (z, w)|\n      end\n      x &+ y &+ z &+ w\n      CRYSTAL\n  end\n\n  it \"codegens block with multiple underscores (#3054)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo(&block : Int32, Int32 -> Int32)\n        block.call(1, 2)\n      end\n\n      foo do |_, _|\n        3\n      end\n      CRYSTAL\n  end\n\n  it \"breaks in var assignment (#3364)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def foo\n        yield\n        456\n      end\n\n      foo do\n        a = nil || break 123\n      end\n      CRYSTAL\n  end\n\n  it \"nexts in var assignment (#3364)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def foo\n        yield\n      end\n\n      foo do\n        a = nil || next 123\n      end\n      CRYSTAL\n  end\n\n  it \"dispatches with captured and non-captured block (#3969)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def fn(x : Int32, &block)\n        x\n      end\n\n      def fn(x : Char, &block : -> Int32)\n        block.call\n      end\n\n      a = fn(1 || 'a') { 2 }\n      b = fn('a' || 1) { 2 }\n      a &+ b\n      CRYSTAL\n  end\n\n  it \"codegens block with repeated underscore and different types (#4711)\" do\n    codegen(<<-CRYSTAL)\n      def foo\n        yield('0', 0)\n      end\n      foo{|_, _| }\n      CRYSTAL\n  end\n\n  it \"doesn't crash on yield exp without a type (#8100)\" do\n    codegen(<<-CRYSTAL)\n      def foo\n        x = 1\n        if x.is_a?(Char)\n          yield x\n        else\n          yield x\n        end\n      end\n\n      foo { |x| }\n      CRYSTAL\n  end\n\n  it \"clears nilable var before inlining block method (#10087)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @@x = 0\n\n        def self.bar\n          i = 0\n          while i < 2\n            yield i\n            i &+= 1\n          end\n          @@x\n        end\n\n        def self.foo(x)\n          if x == 0\n            bug = \"Hello\"\n          end\n\n          if bug\n            @@x &+= 1\n          end\n\n          yield\n        end\n\n      end\n\n      Foo.bar do |z|\n        Foo.foo(z) { }\n      end\n      CRYSTAL\n  end\n\n  it \"(bug) doesn't set needs_value to true on every yield (#12442)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        if true\n          yield\n        end\n\n        1\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash if yield exp has no type (#12670)\" do\n    codegen(<<-CRYSTAL)\n      def foo : String?\n      end\n\n      def bar\n        while res = foo\n          yield res\n        end\n      end\n\n      bar do |res|\n        res\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/c_abi/c_abi_spec.cr",
    "content": "require \"../../../spec_helper\"\n\ndescribe \"Code gen: C ABI\" do\n  it \"passes struct less than 64 bits (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n      struct s {\n        char x;\n        short y;\n      };\n\n      int foo(struct s a) {\n        return a.x + a.y;\n      }\n      C\n      lib LibFoo\n        struct Struct\n          x : Int8\n          y : Int16\n        end\n\n        fun foo(s : Struct) : Int32\n      end\n\n      s = LibFoo::Struct.new x: 1_i8, y: 2_i16\n      LibFoo.foo(s)\n      CRYSTAL\n  end\n\n  it \"passes struct between 64 and 128 bits (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n      struct s {\n        long long x;\n        short y;\n      };\n\n      long long foo(struct s a) {\n        return a.x + a.y;\n      }\n      C\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int16\n        end\n\n        fun foo(s : Struct) : Int64\n      end\n\n      s = LibFoo::Struct.new x: 1_i64, y: 2_i16\n      LibFoo.foo(s)\n      CRYSTAL\n  end\n\n  it \"passes struct bigger than 128 bits (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(6))\n      struct s {\n        long long x;\n        long long y;\n        char z;\n      };\n\n      long long foo(struct s a) {\n        return a.x + a.y + a.z;\n      }\n      C\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int64\n          z : Int8\n        end\n\n        fun foo(s : Struct) : Int64\n      end\n\n      s = LibFoo::Struct.new x: 1_i64, y: 2_i64, z: 3_i8\n      LibFoo.foo(s)\n      CRYSTAL\n  end\n\n  it \"passes struct after many other args (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_string.should eq(\"28\"))\n      struct s {\n        long long x, y;\n      };\n\n      long long foo(long long a, long long b, long long c, long long d, long long e, struct s v) {\n        return a + b + c + d + e + v.x + v.y;\n      }\n      C\n      lib LibFoo\n        struct S\n          x : Int64\n          y : Int64\n        end\n\n        fun foo(a : Int64, b : Int64, c : Int64, d : Int64, e : Int64, v : S) : Int64\n      end\n\n      v = LibFoo::S.new(x: 6, y: 7)\n      LibFoo.foo(1, 2, 3, 4, 5, v)\n      CRYSTAL\n  end\n\n  it \"passes struct after many other args when returning a large struct (sret return type)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_string.should eq(\"[6, 7, 10]\"))\n      struct s {\n        long long x, y;\n      };\n      struct t {\n        long long x, y, z;\n      };\n\n      struct t foo(long long a, long long b, long long c, long long d, struct s v) {\n        return (struct t){ v.x, v.y, a + b + c + d };\n      }\n      C\n      lib LibFoo\n        struct S\n          x : Int64\n          y : Int64\n        end\n        struct T\n          x : Int64\n          y : Int64\n          z : Int64\n        end\n\n        fun foo(a : Int64, b : Int64, c : Int64, d : Int64, v : S) : T\n      end\n\n      v = LibFoo::S.new(x: 6, y: 7)\n      w = LibFoo.foo(1, 2, 3, 4, v)\n      [w.x, w.y, w.z]\n      CRYSTAL\n  end\n\n  it \"returns struct less than 64 bits (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n      struct s {\n        char x;\n        short y;\n      };\n\n      struct s foo() {\n        struct s a = {1, 2};\n        return a;\n      }\n      C\n      lib LibFoo\n        struct Struct\n          x : Int8\n          y : Int16\n        end\n\n        fun foo : Struct\n      end\n\n      str = LibFoo.foo\n      str.x.to_i + str.y.to_i\n      CRYSTAL\n  end\n\n  it \"returns struct between 64 and 128 bits (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n      struct s {\n        long long x;\n        short y;\n      };\n\n      struct s foo() {\n        struct s a = {1, 2};\n        return a;\n      }\n      C\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int16\n        end\n\n        fun foo : Struct\n      end\n\n      str = LibFoo.foo\n      (str.x + str.y).to_i32\n      CRYSTAL\n  end\n\n  it \"returns struct bigger than 128 bits with sret\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(6))\n      struct s {\n        long long x;\n        long long y;\n        char z;\n      };\n\n      struct s foo(int z) {\n        struct s a = {1, 2, z};\n        return a;\n      }\n      C\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int64\n          z : Int8\n        end\n\n        fun foo(w : Int32) : Struct\n      end\n\n      str = LibFoo.foo(3)\n      (str.x + str.y + str.z).to_i32\n      CRYSTAL\n  end\n\n  it \"accepts large struct in a callback (for real)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_string.should eq(\"6\"))\n      struct s {\n          long long x, y, z;\n      };\n\n      void ccaller(void (*func)(struct s)) {\n          struct s v = {1, 2, 3};\n          func(v);\n      }\n      C\n      lib LibFoo\n        struct S\n          x : Int64\n          y : Int64\n          z : Int64\n        end\n\n        fun ccaller(func : (S) ->)\n      end\n\n      module Global\n        class_property x = 0i64\n      end\n\n      fun callback(v : LibFoo::S)\n        Global.x = v.x &+ v.y &+ v.z\n      end\n\n      LibFoo.ccaller(->callback)\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"promotes variadic args (float to double)\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_f64.should eq(1.0))\n      #include <stdarg.h>\n\n      double foo(int n, ...) {\n        va_list args;\n        va_start(args, n);\n        return va_arg(args, double);\n      }\n      C\n      lib LibFoo\n        fun foo(n : Int32, ...) : Float64\n      end\n\n      LibFoo.foo(1, 1.0_f32)\n      CRYSTAL\n  end\n\n  [{\"i8\", -123},\n   {\"u8\", 255},\n   {\"i16\", -123},\n   {\"u16\", 65535},\n  ].each do |int_kind, int_value|\n    it \"promotes variadic args (#{int_kind} to i32) (#9742)\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(int_value))\n        #include <stdarg.h>\n\n        int foo(int n, ...) {\n          va_list args;\n          va_start(args, n);\n          return va_arg(args, int);\n        }\n        C\n        lib LibFoo\n          fun foo(n : Int32, ...) : Int32\n        end\n\n        LibFoo.foo(1, #{int_value}_#{int_kind})\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/c_abi/c_abi_x86_64_spec.cr",
    "content": "{% skip_file unless flag?(:x86_64) && !flag?(:win32) %}\nrequire \"../../../spec_helper\"\n\ndescribe \"Code gen: C ABI x86_64\" do\n  it \"passes struct less than 64 bits as { i64 }\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int8\n          y : Int16\n        end\n\n        fun foo(s : Struct)\n      end\n\n      s = LibFoo::Struct.new\n      LibFoo.foo(s)\n      CRYSTAL\n    str = mod.to_s\n    str.should contain(\"call void @foo({ i64 }\")\n    str.should contain(\"declare void @foo({ i64 })\")\n  end\n\n  it \"passes struct less than 64 bits as { i64 } in varargs\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int8\n          y : Int16\n        end\n\n        fun foo(...)\n      end\n\n      s = LibFoo::Struct.new\n      LibFoo.foo(s)\n      CRYSTAL\n    str = mod.to_s\n    str.should contain(\"call void (...)\")\n  end\n\n  it \"passes struct between 64 and 128 bits as { i64, i64 }\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int16\n        end\n\n        fun foo(s : Struct)\n      end\n\n      s = LibFoo::Struct.new\n      LibFoo.foo(s)\n      CRYSTAL\n    str = mod.to_s\n    str.should contain(\"call void @foo({ i64, i64 }\")\n    str.should contain(\"declare void @foo({ i64, i64 })\")\n  end\n\n  it \"passes struct between 64 and 128 bits as { i64, i64 } (with multiple modules/contexts)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int16\n        end\n\n        fun foo(s : Struct)\n      end\n\n      module Moo\n        def self.moo\n          s = LibFoo::Struct.new\n          LibFoo.foo(s)\n        end\n      end\n\n      Moo.moo\n      CRYSTAL\n  end\n\n  it \"passes struct bigger than128 bits with byval\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int64\n          z : Int8\n        end\n\n        fun foo(s : Struct)\n      end\n\n      s = LibFoo::Struct.new\n      LibFoo.foo(s)\n      CRYSTAL\n    str = mod.to_s\n    str.scan(/byval/).size.should eq(2)\n  end\n\n  it \"returns struct less than 64 bits as { i64 }\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int8\n          y : Int16\n        end\n\n        fun foo : Struct\n      end\n\n      str = LibFoo.foo\n      CRYSTAL\n    str = mod.to_s\n    str.should contain(\"call { i64 } @foo()\")\n    str.should contain(\"declare { i64 } @foo()\")\n  end\n\n  it \"returns struct between 64 and 128 bits as { i64, i64 }\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int16\n        end\n\n        fun foo : Struct\n      end\n\n      str = LibFoo.foo\n      CRYSTAL\n    str = mod.to_s\n    str.should contain(\"call { i64, i64 } @foo()\")\n    str.should contain(\"declare { i64, i64 } @foo()\")\n  end\n\n  it \"returns struct bigger than 128 bits with sret\" do\n    mod = codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct Struct\n          x : Int64\n          y : Int64\n          z : Int8\n        end\n\n        fun foo(w : Int32) : Struct\n      end\n\n      str = LibFoo.foo(1)\n      CRYSTAL\n    str = mod.to_s\n    str.scan(/sret/).size.should eq(2)\n\n    if LibLLVM::IS_LT_120\n      str.should contain(\"sret, i32\") # sret goes as first argument\n    else\n      str.should contain(\"sret(%\\\"struct.LibFoo::Struct\\\") %0, i32\") # sret goes as first argument\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/c_enum_spec.cr",
    "content": "require \"../../spec_helper\"\n\nCodeGenCEnumString = \"lib LibFoo; enum Bar; X; Y; Z = 10; W; end end\"\n\ndescribe \"Code gen: c enum\" do\n  it \"codegens enum value\" do\n    run(\"#{CodeGenCEnumString}; LibFoo::Bar::X\").to_i.should eq(0)\n  end\n\n  it \"codegens enum value 2\" do\n    run(\"#{CodeGenCEnumString}; LibFoo::Bar::Y\").to_i.should eq(1)\n  end\n\n  it \"codegens enum value 3\" do\n    run(\"#{CodeGenCEnumString}; LibFoo::Bar::Z\").to_i.should eq(10)\n  end\n\n  it \"codegens enum value 4\" do\n    run(\"#{CodeGenCEnumString}; LibFoo::Bar::W\").to_i.should eq(11)\n  end\n\n  [\n    {\"+1\", 1},\n    {\"-1\", -1},\n    {\"~1\", -2},\n    {\"1 + 2\", 3},\n    {\"3 - 2\", 1},\n    {\"3 * 2\", 6},\n    {\"1 &+ 2\", 3},\n    {\"3 &- 2\", 1},\n    {\"3 &* 2\", 6},\n    # {\"10 / 2\", 5}, # MathInterpreter only works with Integer and 10 / 2 : Float\n    {\"10 // 2\", 5},\n    {\"1 << 3\", 8},\n    {\"100 >> 3\", 12},\n    {\"10 & 3\", 2},\n    {\"10 | 3\", 11},\n    {\"10 ^ 3\", 9},\n    {\"(1 + 2) * 3\", 9},\n    {\"10 % 3\", 1},\n  ].each do |(code, expected)|\n    it \"codegens enum with #{code} \" do\n      run(<<-CRYSTAL).to_i.should eq(expected)\n        lib LibFoo\n          enum Bar\n            X = #{code}\n          end\n        end\n\n        LibFoo::Bar::X\n        CRYSTAL\n    end\n  end\n\n  it \"codegens enum that refers to another enum constant\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      lib LibFoo\n        enum Bar\n          A = 1\n          B = A + 1\n          C = B + 1\n        end\n      end\n\n      LibFoo::Bar::C\n      CRYSTAL\n  end\n\n  it \"codegens enum that refers to another constant\" do\n    run(<<-CRYSTAL).to_i.should eq(12)\n      lib LibFoo\n        X = 10\n        enum Bar\n          A = X\n          B = A + 1\n          C = B + 1\n        end\n      end\n\n      LibFoo::Bar::C\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/c_struct_spec.cr",
    "content": "require \"../../spec_helper\"\n\nCodeGenStructString = \"lib LibFoo; struct Bar; x : Int32; y : Float32; end; end\"\n\ndescribe \"Code gen: struct\" do\n  it \"codegens struct property default value\" do\n    run(\"#{CodeGenStructString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x\").to_i.should eq(0)\n  end\n\n  it \"codegens struct property setter\" do\n    run(\"#{CodeGenStructString}; bar = LibFoo::Bar.new; bar.y = 2.5_f32; bar.y\").to_f32.should eq(2.5)\n  end\n\n  it \"codegens struct property setter via pointer\" do\n    run(\"#{CodeGenStructString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.y = 2.5_f32; bar.value.y\").to_f32.should eq(2.5)\n  end\n\n  it \"codegens struct property setter via pointer\" do\n    run(\"#{CodeGenStructString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.y = 2.5_f32; bar.value.y\").to_f32.should eq(2.5)\n  end\n\n  it \"codegens set struct value with constant\" do\n    run(\"#{CodeGenStructString}; CONST = 1; bar = LibFoo::Bar.new; bar.x = CONST; bar.x\").to_i.should eq(1)\n  end\n\n  it \"codegens union inside struct\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibFoo\n        union Bar\n          x : Int32\n          y : Int64\n        end\n\n        struct Baz\n          lala : Bar\n        end\n      end\n\n      a = Pointer(LibFoo::Baz).malloc(1_u64)\n      a.value.lala.x = 10\n      a.value.lala.x\n      CRYSTAL\n  end\n\n  it \"codegens struct get inside struct\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      lib LibC\n        struct Bar\n          y : Int32\n        end\n\n        struct Foo\n          x : Int32\n          bar : Bar\n        end\n      end\n\n      foo = Pointer(LibC::Foo).malloc(1_u64)\n      (foo.as(Int32*) + 1_i64).value = 2\n\n      foo.value.bar.y\n      CRYSTAL\n  end\n\n  it \"codegens struct set inside struct\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      lib LibC\n        struct Bar\n          y : Int32\n        end\n\n        struct Foo\n          x : Int32\n          bar : Bar\n        end\n      end\n\n      foo = Pointer(LibC::Foo).malloc(1_u64)\n      bar = LibC::Bar.new\n      bar.y = 2\n      foo.value.bar = bar\n\n      foo.value.bar.y\n      CRYSTAL\n  end\n\n  it \"codegens pointer malloc of struct\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibC\n        struct Foo\n          x : Int32\n        end\n      end\n\n      p = Pointer(LibC::Foo).malloc(1_u64)\n      p.value.x = 1\n      p.value.x\n      CRYSTAL\n  end\n\n  it \"passes struct to method (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibC\n        struct Foo\n          x : Int32\n        end\n      end\n\n      def foo(f)\n        f.x = 2\n        f\n      end\n\n      f1 = LibC::Foo.new\n      f1.x = 1\n\n      f2 = foo(f1)\n\n      f1.x\n      CRYSTAL\n  end\n\n  it \"passes struct to method (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      lib LibC\n        struct Foo\n          x : Int32\n        end\n      end\n\n      def foo(f)\n        f.x = 2\n        f\n      end\n\n      f1 = LibC::Foo.new\n      f1.x = 1\n\n      f2 = foo(f1)\n      f2.x\n      CRYSTAL\n  end\n\n  it \"codegens struct access with -> and then .\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      lib LibC\n        struct ScalarEvent\n          x : Int32\n        end\n\n        union EventData\n          scalar : ScalarEvent\n        end\n\n        struct Event\n          data : EventData\n        end\n      end\n\n      e = Pointer(LibC::Event).malloc(1_u64)\n      e.value.data.scalar.x\n      CRYSTAL\n  end\n\n  it \"yields struct via ->\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      lib LibC\n        struct ScalarEvent\n          x : Int32\n        end\n\n        union EventData\n          scalar : ScalarEvent\n        end\n\n        struct Event\n          data : EventData\n        end\n      end\n\n      def foo\n        e = Pointer(LibC::Event).malloc(1_u64)\n        yield e.value.data\n      end\n\n      foo do |data|\n        data.scalar.x\n      end\n      CRYSTAL\n  end\n\n  it \"codegens assign struct to union\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      lib LibFoo\n        struct Coco\n          x : Int32\n        end\n      end\n\n      x = LibFoo::Coco.new\n      c = x || 0\n      c.is_a?(LibFoo::Coco)\n      CRYSTAL\n  end\n\n  it \"codegens passing pointerof(struct) to fun\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibC\n        struct Foo\n          a : Int32\n        end\n      end\n\n      fun foo(x : LibC::Foo*) : Int32\n        x.value.a\n      end\n\n      f = LibC::Foo.new\n      f.a = 1\n\n      foo pointerof(f)\n      CRYSTAL\n  end\n\n  it \"builds struct setter with fun type (1)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        struct Foo\n          x : ->\n        end\n      end\n\n      foo = LibC::Foo.new\n      foo.x = -> { }\n      CRYSTAL\n  end\n\n  it \"builds struct setter with fun type (2)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        struct Foo\n          x : ->\n        end\n      end\n\n      foo = Pointer(LibC::Foo).malloc(1)\n      foo.value.x = -> { }\n      CRYSTAL\n  end\n\n  it \"allows using named arguments for new\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      lib LibC\n        struct Point\n          x, y : Int32\n        end\n      end\n\n      point = LibC::Point.new x: 1, y: 2\n      point.x &+ point.y\n      CRYSTAL\n  end\n\n  it \"does to_s\" do\n    run(<<-CRYSTAL).to_string.should eq(\"LibFoo::Point(@x=1, @y=2)\")\n      require \"prelude\"\n\n      lib LibFoo\n        struct Point\n          x, y : Int32\n        end\n      end\n\n      point = LibFoo::Point.new x: 1, y: 2\n      point.to_s\n      CRYSTAL\n  end\n\n  it \"can access instance var from the outside (#1092)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      f = LibFoo::Foo.new x: 123\n      f.@x\n      CRYSTAL\n  end\n\n  it \"automatically converts numeric type in struct field assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = 123_u8\n      foo.x\n      CRYSTAL\n  end\n\n  it \"automatically converts numeric union type in struct field assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(57)\n      lib LibFoo\n        struct Foo\n          x : Int8\n        end\n      end\n\n      a = 12345 || 12346_u16\n\n      foo = LibFoo::Foo.new\n      foo.x = a\n      foo.x\n      CRYSTAL\n  end\n\n  it \"automatically converts nil to pointer\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      lib LibFoo\n        struct Foo\n          x : Int32*\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Pointer(Int32).new(1234_u64)\n      foo.x = nil\n      foo.x.address\n      CRYSTAL\n  end\n\n  it \"automatically converts by invoking to_unsafe\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      class Foo\n        def to_unsafe\n          123\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"sets instance var to proc\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      lib LibFoo\n        struct Foo\n          x : Int32 -> Int32\n        end\n      end\n\n      struct LibFoo::Foo\n        def set(f)\n          @x = f\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.set(->(x : Int32) { x + 1 })\n      foo.x.call(1)\n      CRYSTAL\n  end\n\n  it \"can access member of uninitialized struct behind type (#8774)\" do\n    run(<<-CRYSTAL)\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n\n        type FooT = Foo\n      end\n\n      foo = uninitialized LibFoo::FooT\n      foo.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/c_union_spec.cr",
    "content": "require \"../../spec_helper\"\n\nCodeGenUnionString = \"lib LibFoo; union Bar; x : Int32; y : Int64; z : Float32; end; end\"\n\ndescribe \"Code gen: c union\" do\n  it \"codegens union property default value\" do\n    run(\"#{CodeGenUnionString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x\").to_i.should eq(0)\n  end\n\n  it \"codegens union property default value 2\" do\n    run(\"#{CodeGenUnionString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.z\").to_f32.should eq(0)\n  end\n\n  it \"codegens union property setter 1\" do\n    run(\"#{CodeGenUnionString}; bar = LibFoo::Bar.new; bar.x = 42; bar.x\").to_i.should eq(42)\n  end\n\n  it \"codegens union property setter 2\" do\n    run(\"#{CodeGenUnionString}; bar = LibFoo::Bar.new; bar.z = 42.0_f32; bar.z\").to_f32.should eq(42.0)\n  end\n\n  it \"codegens union property setter 1 via pointer\" do\n    run(\"#{CodeGenUnionString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x = 42; bar.value.x\").to_i.should eq(42)\n  end\n\n  it \"codegens union property setter 2 via pointer\" do\n    run(\"#{CodeGenUnionString}; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.z = 42.0_f32; bar.value.z\").to_f32.should eq(42.0)\n  end\n\n  it \"codegens struct inside union\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibFoo\n        struct Baz\n          lele : Int64\n          lala : Int32\n        end\n\n        union Bar\n          x : Int32\n          y : Int64\n          z : Baz\n        end\n      end\n\n      a = Pointer(LibFoo::Bar).malloc(1_u64)\n      a.value.z = LibFoo::Baz.new\n      a.value.z.lala = 10\n      a.value.z.lala\n      CRYSTAL\n  end\n\n  it \"codegens assign c union to union\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibFoo\n        union Bar\n          x : Int32\n        end\n      end\n\n      bar = LibFoo::Bar.new\n      bar.x = 10\n      x = bar || nil\n      if x\n        x.x\n      else\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"builds union setter with fun type\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        union Foo\n          x : ->\n        end\n      end\n\n      foo = LibC::Foo.new\n      foo.x = -> { }\n      CRYSTAL\n  end\n\n  it \"does to_s\" do\n    run(<<-CRYSTAL).to_string.should eq(\"LibNVG::Color(@array=0)\")\n      require \"prelude\"\n\n      lib LibNVG\n        union Color\n          array: Int32\n        end\n      end\n\n      color = LibNVG::Color.new\n      color.to_s\n      CRYSTAL\n  end\n\n  it \"automatically converts numeric type in field assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(57)\n      lib LibFoo\n        union Foo\n          x : Int8\n        end\n      end\n\n      a = 12345\n\n      foo = LibFoo::Foo.new\n      foo.x = a\n      foo.x\n      CRYSTAL\n  end\n\n  it \"automatically converts numeric union type in field assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(57)\n      lib LibFoo\n        union Foo\n          x : Int8\n        end\n      end\n\n      a = 12345 || 12346_u16\n\n      foo = LibFoo::Foo.new\n      foo.x = a\n      foo.x\n      CRYSTAL\n  end\n\n  it \"automatically converts by invoking to_unsafe\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      lib LibFoo\n        union Foo\n          x : Int32\n        end\n      end\n\n      class Foo\n        def to_unsafe\n          123\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"aligns to the member with biggest align requirements\" do\n    run(<<-CRYSTAL).to_i.should eq(0x5858)\n      lib LibFoo\n        union Foo\n          bytes : UInt8[4]\n          short : UInt16\n        end\n\n        struct Bar\n          a : Int8\n          b : Foo\n        end\n      end\n\n      class String\n        def to_unsafe\n          pointerof(@c)\n        end\n      end\n\n      str = \"00XX0\"\n      foo = str.to_unsafe.as(LibFoo::Bar*)\n      foo.value.b.short.to_i\n      CRYSTAL\n  end\n\n  it \"fills union type to the max size\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      lib LibFoo\n        union Foo\n          bytes : UInt8[4]\n          short : UInt16\n        end\n\n        struct Bar\n          a : Int8\n          b : Foo\n        end\n      end\n\n      sizeof(LibFoo::Bar)\n      CRYSTAL\n  end\n\n  it \"reads union instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      lib LibFoo\n        union Foo\n          char : Char\n          int : Int32\n        end\n      end\n\n      struct LibFoo::Foo\n        def read_int\n          @int\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.int = 42\n      foo.read_int\n      CRYSTAL\n  end\n\n  it \"moves unions around correctly (#12550)\" do\n    run(<<-CRYSTAL).to_i.should eq((1..6).sum)\n      require \"prelude\"\n\n      lib Lib\n        struct Foo\n          x : UInt8\n          y : UInt16\n        end\n\n        union Bar\n          foo : Foo\n          padding : UInt8[6] # larger than `Foo`\n        end\n      end\n\n      def foo\n        a = uninitialized Lib::Bar\n        a.padding.fill { |i| 1_u8 + i }\n        a\n      end\n\n      foo.padding.sum\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/case_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: case\" do\n  it \"codegens case with one condition\" do\n    run(\"require \\\"prelude\\\"; case 1; when 1; 2; else; 3; end\").to_i.should eq(2)\n  end\n\n  it \"codegens case with two conditions\" do\n    run(\"require \\\"prelude\\\"; case 1; when 0, 1; 2; else; 3; end\").to_i.should eq(2)\n  end\n\n  it \"codegens case with else\" do\n    run(\"require \\\"prelude\\\"; case 1; when 0; 2; else; 3; end\").to_i.should eq(3)\n  end\n\n  it \"codegens case without whens but else\" do\n    run(\"require \\\"prelude\\\"; case 1; else; 2; end\").to_i.should eq(2)\n  end\n\n  it \"codegens case that always returns\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n      def foo\n        if true\n          case 0\n          when 1; return 2\n          else return 3\n          end\n        end\n        4\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens case when cond is a call\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      def foo\n        1\n      end\n\n      case foo\n      when 2\n        1\n      when 1\n        2\n      else\n        3\n      end\n      CRYSTAL\n  end\n\n  it \"codegens case with class\" do\n    run(<<-CRYSTAL).to_i.should eq(-1)\n      struct Nil; def to_i!; 0; end; end\n\n      struct Int32\n        def foo\n          self\n        end\n      end\n\n      a = -1 || 'a'\n      case a\n      when Int32\n        a.foo\n      when Char\n        a.ord\n      end.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens value-less case\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      case\n      when 1 == 2\n        1\n      when 2 == 2\n        2\n      else\n        3\n      end\n      CRYSTAL\n  end\n\n  it \"codegens case when constant bug (#1028)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Nil\n        def ===(other)\n          self.is_a?(Reference) && other.is_a?(Reference)\n        end\n      end\n\n      CONST = nil\n      case nil\n      when CONST\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"does case when with metaclass\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar\n        def self.bar\n          2\n        end\n      end\n\n      foobar = Bar || Foo\n      case foobar\n      when Foo.class\n        foobar.foo\n      when Bar.class\n        foobar.bar\n      else\n        3\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/cast_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: cast\" do\n  it \"allows casting object to pointer and back\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      f = Foo.new(1)\n      p = f.as(Void*)\n      f = p.as(Foo)\n      f.x\n      CRYSTAL\n  end\n\n  it \"casts from int to int\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1\n      b = a.as(Int32)\n      b.abs\n      CRYSTAL\n  end\n\n  it \"casts from union to single type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1 || 'a'\n      b = a.as(Int32)\n      b.abs\n      CRYSTAL\n  end\n\n  it \"casts from union to single type raises TypeCastError\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      a = 1 || 'a'\n      begin\n        a.as(Char)\n        false\n      rescue ex\n        ex.message.not_nil!.includes?(\"Cast from Int32 to Char failed\") && (ex.class == TypeCastError)\n      end\n      CRYSTAL\n  end\n\n  it \"casts from union to another union\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1 || 1.5 || 'a'\n      b = a.as(Int32 | Float64)\n      b.abs.to_i\n      CRYSTAL\n  end\n\n  it \"casts from union to another union raises TypeCastError\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      a = 1 || 1.5 || 'a'\n      begin\n        a.as(Float64 | Char)\n        false\n      rescue ex\n        ex.message.not_nil!.includes?(\"Cast from Int32 to (Char | Float64) failed\") && (ex.class == TypeCastError)\n      end\n      CRYSTAL\n  end\n\n  it \"upcasts from union to union with different alignment\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1 || 2_i64\n      a.as(Int32 | Int64 | Int128)\n      CRYSTAL\n  end\n\n  it \"downcasts from union to union with different alignment\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1 || 2_i64 || 3_i128\n      a.as(Int32 | Int64)\n      CRYSTAL\n  end\n\n  it \"sidecasts from union to union with different alignment\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1 || 2_i64\n      a.as(Int32 | Int128)\n      CRYSTAL\n  end\n\n  it \"doesn't corrupt stack when downcasting union to union with different alignment (#14285)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      struct Time2\n        def initialize(@seconds : Int64)\n          @nanoseconds = uninitialized UInt32[3]\n        end\n\n        def <(other : Time2) : Bool\n          @seconds < other.@seconds\n        end\n      end\n\n      class Constraints::Range\n        def initialize(@min : Int128 | Time2 | Nil)\n        end\n      end\n\n      def validate(value : Time2, constraint) : Bool\n        min = constraint.@min\n        if min.is_a?(Time2?)\n          if min\n            if value < min\n              return false\n            end\n          end\n        end\n        true\n      end\n\n      value = Time2.new(123)\n      constraint = Constraints::Range.new(Time2.new(45))\n      validate(value, constraint)\n      CRYSTAL\n  end\n\n  it \"casts from virtual to single type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class CastSpecFoo\n      end\n\n      class CastSpecBar < CastSpecFoo\n        def bar\n          1\n        end\n      end\n\n      class CastSpecBaz < CastSpecBar\n      end\n\n      a = CastSpecBar.new || CastSpecFoo.new || CastSpecBaz.new\n      b = a.as(CastSpecBar)\n      b.bar\n      CRYSTAL\n  end\n\n  it \"casts from virtual to single type raises TypeCastError\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      class CastSpecFoo\n      end\n\n      class CastSpecBar < CastSpecFoo\n        def bar\n          1\n        end\n      end\n\n      class CastSpecBaz < CastSpecBar\n      end\n\n      a = CastSpecBar.new || CastSpecFoo.new || CastSpecBaz.new\n      begin\n        a.as(CastSpecBaz)\n        false\n      rescue ex\n        ex.message.not_nil!.includes?(\"Cast from CastSpecBar to CastSpecBaz failed\") && (ex.class == TypeCastError)\n      end\n      CRYSTAL\n  end\n\n  it \"casts from pointer to pointer\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 1_i64\n      pointerof(a).as(Int32*).value\n      CRYSTAL\n  end\n\n  it \"casts to module\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      module CastSpecMoo\n        def moo\n          2\n        end\n      end\n\n      class CastSpecFoo\n      end\n\n      class CastSpecBar < CastSpecFoo\n        include CastSpecMoo\n\n        def bar\n          1\n        end\n      end\n\n      class CastSpecBaz < CastSpecBar\n      end\n\n      class CastSpecBan < CastSpecFoo\n        include CastSpecMoo\n      end\n\n      a = CastSpecBar.new || CastSpecFoo.new || CastSpecBaz.new || CastSpecBan.new\n      m = a.as(CastSpecMoo)\n      m.moo\n      CRYSTAL\n  end\n\n  it \"casts from nilable to nil\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      a = 1 == 2 ? Reference.new : nil\n      c = a.as(Nil)\n      c == nil\n      CRYSTAL\n  end\n\n  it \"casts from nilable to nil raises TypeCastError\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      a = 1 == 1 ? Reference.new : nil\n      begin\n        a.as(Nil)\n        false\n      rescue ex\n        ex.message.not_nil!.includes?(\"Cast from Reference to Nil failed\") && (ex.class == TypeCastError)\n      end\n      CRYSTAL\n  end\n\n  it \"casts to base class making it virtual\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          1.5\n        end\n      end\n\n      bar = Bar.new\n      x = bar.as(Foo).foo\n      x.to_i!\n      CRYSTAL\n  end\n\n  it \"casts to bigger union\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      x = 1.5.as(Int32 | Float64)\n      x.to_i!\n      CRYSTAL\n  end\n\n  it \"allows casting nil to Void*\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      nil.as(Void*).address\n      CRYSTAL\n  end\n\n  it \"allows casting nilable type to Void* (1)\" do\n    run(<<-CRYSTAL).to_i.should_not eq(0)\n      a = 1 == 1 ? Reference.new : nil\n      a.as(Void*).address\n      CRYSTAL\n  end\n\n  it \"allows casting nilable type to Void* (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      a = 1 == 2 ? Reference.new : nil\n      a.as(Void*).address\n      CRYSTAL\n  end\n\n  it \"allows casting nilable type to Void* (3)\" do\n    run(<<-CRYSTAL).to_i.should_not eq(0)\n      class Foo\n      end\n      a = 1 == 1 ? Reference.new : (1 == 2 ? Foo.new : nil)\n      a.as(Void*).address\n      CRYSTAL\n  end\n\n  it \"casts (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n      (1 || 1.1).as(Int32)\n      123\n      CRYSTAL\n  end\n\n  it \"can cast from Void* to virtual type (#3014)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      abstract class Foo\n        abstract def hi\n      end\n\n      class Bar < Foo\n        def hi\n          42\n        end\n      end\n\n      Bar.new.as(Void*).as(Foo).hi\n      CRYSTAL\n  end\n\n  it \"upcasts from non-generic to generic\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo(T)\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo(Int32)\n        def foo\n          2\n        end\n      end\n\n      Bar.new.as(Foo(Int32)).foo\n      CRYSTAL\n  end\n\n  it \"upcasts type to virtual (#3304)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      Foo.new.as(Foo).foo\n      CRYSTAL\n  end\n\n  it \"upcasts type to virtual (2) (#3304)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      class Gen(T)\n        def self.cast(x)\n          x.as(T)\n        end\n      end\n\n      Gen(Foo).cast(Foo.new).foo\n      CRYSTAL\n  end\n\n  it \"casts with block var that changes type (#3341)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Object\n        def try\n          yield self\n        end\n      end\n\n      class Foo\n      end\n\n      x = Foo.new.as(Int32 | Foo)\n      x.try &.as(Foo)\n      CRYSTAL\n  end\n\n  it \"casts between union types, where union has a tuple type (#3377)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      v = 1 || true || 1.0\n      (v || {v}).as(Bool | Float64)\n      CRYSTAL\n  end\n\n  it \"codegens class method when type id is available but not a virtual type (#3490)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"A\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Super\n      end\n\n      module Mixin\n      end\n\n      class A < Super\n        include Mixin\n      end\n\n      class B < Super\n        include Mixin\n      end\n\n      a = A.new.as(Super)\n      if a.is_a?(Mixin)\n        a.class.name\n      else\n        \"Nope\"\n      end\n      CRYSTAL\n  end\n\n  it \"casts from nilable type to virtual type (#3512)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      foo = 1 == 2 ? nil : Foo.new\n      x = foo.as(Foo)\n      x.foo\n      CRYSTAL\n  end\n\n  it \"can cast to metaclass (#11121)\" do\n    run(<<-CRYSTAL)\n      class A\n      end\n\n      class B < A\n      end\n\n      A.as(A.class)\n      CRYSTAL\n  end\n\n  it \"cast virtual metaclass type to nilable virtual instance type (#12628)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      abstract struct Base\n      end\n\n      struct Impl < Base\n      end\n\n      Base.as(Base | Base.class).as?(Base | Impl).nil?\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/class_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: class\" do\n  it \"codegens call to same instance\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n\n        def bar\n          foo\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"codegens instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def initialize(@coco : Int32)\n        end\n        def coco\n          @coco\n        end\n      end\n\n      f = Foo.new(2)\n      g = Foo.new(40)\n      f.coco &+ g.coco\n      CRYSTAL\n  end\n\n  it \"codegens recursive type\" do\n    run(<<-CRYSTAL)\n      class Foo\n        def next=(@next : Foo)\n        end\n      end\n\n      f = Foo.new\n      f.next = f\n      CRYSTAL\n  end\n\n  it \"codegens method call of instance var\" do\n    run(<<-CRYSTAL).to_f64.should eq(1.0)\n      class List\n        def initialize\n          @last = 0\n        end\n\n        def foo\n          @last = 1\n          @last.to_f\n        end\n      end\n\n      l = List.new\n      l.foo\n      CRYSTAL\n  end\n\n  it \"codegens new which calls initialize\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(value : Int32)\n          @value = value\n        end\n\n        def value\n          @value\n        end\n      end\n\n      f = Foo.new 1\n      f.value\n      CRYSTAL\n  end\n\n  it \"codegens method from another method without obj and accesses instance vars\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          bar\n        end\n\n        def bar\n          @a = 1\n        end\n      end\n\n      f = Foo.new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"codegens virtual call that calls another method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          foo2\n        end\n\n        def foo2\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens virtual method of generic class\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Object\n        def foo\n          bar\n        end\n\n        def bar\n          'a'\n        end\n      end\n\n      class Foo(T)\n        def bar\n          1\n        end\n      end\n\n      Foo(Int32).new.foo.to_i!\n      CRYSTAL\n  end\n\n  it \"changes instance variable in method (ssa bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize\n          @var = 0\n        end\n\n        def foo\n          @var = 1\n          bar\n          @var\n        end\n\n        def bar\n          @var = 2\n        end\n      end\n\n      foo = Foo.new\n      foo.foo\n      CRYSTAL\n  end\n\n  # it \"gets object_id of class\" do\n  #   program = Program.new\n  #   program.run(\"Reference.object_id\").to_i.should eq(program.reference.metaclass.type_id)\n  # end\n\n  it \"calls method on Class class\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Class\n        def foo\n          1\n        end\n      end\n\n      class Foo\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"uses number type var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo(T)\n        def self.foo\n          T\n        end\n      end\n\n      Foo(1).foo\n      CRYSTAL\n  end\n\n  it \"calls class method without self\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def self.coco\n          1\n        end\n\n        a = coco\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"calls class method without self (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.coco\n          lala\n        end\n\n        def self.lala\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.lala\n          2\n        end\n\n        a = coco\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"assigns type to reference union type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Bar)\n        end\n        def x=(@x : Baz); end\n      end\n\n      class Bar; end\n      class Baz; end\n\n      f = Foo.new(Bar.new)\n      f.x = Baz.new\n      1\n      CRYSTAL\n  end\n\n  it \"does to_s for class\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Reference\")\n      require \"prelude\"\n\n      Reference.to_s\n      CRYSTAL\n  end\n\n  it \"allows fixing an instance variable's type\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n        @x : Bool\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(true).x\n      CRYSTAL\n  end\n\n  it \"codegens initialize with instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x : Nil\n\n        def initialize\n          @x\n        end\n      end\n\n      Foo.new\n      1\n      CRYSTAL\n  end\n\n  it \"reads other instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.@x\n      CRYSTAL\n  end\n\n  it \"reads a virtual type instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Foo.new(1) || Bar.new(2)\n      foo.@x\n      CRYSTAL\n  end\n\n  it \"reads a union type instance var (reference union, first type)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def initialize(@y : Int32, @x : Bool)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(10)\n      bar = Bar.new(2, true)\n      union = foo || bar\n      var = union.@x\n      if var.is_a?(Int32)\n        var\n      else\n        20\n      end\n      CRYSTAL\n  end\n\n  it \"reads a union type instance var (reference union, second type)\" do\n    run(<<-CRYSTAL).to_i.should eq('a'.ord)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def initialize(@y : Int32, @x : Char)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(10)\n      bar = Bar.new(2, 'a')\n      union = bar || foo\n      var = union.@x\n      if var.is_a?(Char)\n        var\n      else\n        'b'\n      end\n      CRYSTAL\n  end\n\n  it \"reads a union type instance var (mixed union, first type)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def initialize(@y : Int32, @x : Bool)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(10)\n      bar = Bar.new(2, true)\n      union = foo || bar\n      var = union.@x\n      if var.is_a?(Int32)\n        var\n      else\n        20\n      end\n      CRYSTAL\n  end\n\n  it \"reads a union type instance var (mixed union, second type)\" do\n    run(<<-CRYSTAL).to_i.should eq('a'.ord)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def initialize(@y : Int32, @x : Char)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(10)\n      bar = Bar.new(2, 'a')\n      union = bar || foo\n      var = union.@x\n      if var.is_a?(Char)\n        var\n      else\n        'b'\n      end\n      CRYSTAL\n  end\n\n  it \"never considers read instance var as closure (#12181)\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        @x = 1\n      end\n\n      def bug\n        ->{\n          Foo.new.@x\n        }\n      end\n\n      bug\n      CRYSTAL\n  end\n\n  it \"runs with nilable instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      class Bar\n        def initialize\n        end\n\n        def initialize(@x : Int32?)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      bar = Bar.new\n      bar.x.to_i!\n      CRYSTAL\n  end\n\n  it \"runs with nil instance var when inheriting\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      class Foo\n        @x : Int32?\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @x = nil\n        end\n      end\n\n      bar = Bar.new\n      bar.x.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens bug #168\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x : Foo?\n\n        def foo\n          x = @x\n          if x\n            x.foo\n          else\n            1\n          end\n        end\n      end\n\n      class Bar < Foo\n        def initialize(@x)\n        end\n      end\n\n      Bar.new(Foo.new).foo\n      CRYSTAL\n  end\n\n  it \"allows initializing var with constant\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        A = 1\n        @x = A\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"codegens class method\" do\n    codegen(<<-CRYSTAL)\n      Int32.class\n      CRYSTAL\n  end\n\n  it \"codegens virtual class method\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      (Foo.new || Bar.new).class\n      CRYSTAL\n  end\n\n  it \"allows using self in class scope\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def self.foo\n          1\n        end\n\n        @@x = self.foo.as(Int32)\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"allows using self in class scope\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo\n        def self.foo\n          1\n        end\n\n        @@x = self.as(Foo.class)\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x.foo\n      CRYSTAL\n  end\n\n  it \"makes .class always be a virtual type even if no subclasses\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n      end\n\n      p = Pointer(Foo.class).malloc(1_u64)\n\n      class Bar < Foo\n        p.value = self\n      end\n      CRYSTAL\n  end\n\n  it \"does to_s for virtual metaclass type (1)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      require \"prelude\"\n\n      class Foo; end\n      class Bar < Foo; end\n      class Baz < Foo; end\n\n      a = Foo || Bar || Baz\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"does to_s for virtual metaclass type (2)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar\")\n      require \"prelude\"\n\n      class Foo; end\n      class Bar < Foo; end\n      class Baz < Foo; end\n\n      a = Bar || Foo || Baz\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"does to_s for virtual metaclass type (3)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Baz\")\n      require \"prelude\"\n\n      class Foo; end\n      class Bar < Foo; end\n      class Baz < Foo; end\n\n      a = Baz || Bar || Foo\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"does not combine module metaclass types with same name but different file scopes (#15503)\" do\n    run(<<-CRYSTAL, Int32, filename: \"foo.cr\").should eq(11)\n      module Foo\n        def self.foo\n          1\n        end\n      end\n\n      alias Bar = Foo\n\n      {% Bar %} # forces immediate resolution of `Bar`\n\n      private module Foo\n        def self.foo\n          10\n        end\n      end\n\n      module Baz\n        def self.foo\n          100\n        end\n      end\n\n      def foo(x)\n        x.foo\n      end\n\n      foo(Foo || Baz) &+ foo(Bar || Baz)\n      CRYSTAL\n  end\n\n  it \"does not combine virtual types with same name but different file scopes\" do\n    run(<<-CRYSTAL, Int32, filename: \"foo.cr\").should eq(101)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar1 < Foo\n        def foo\n          10\n        end\n      end\n\n      alias Fred = Foo\n      {% Fred %} # forces immediate resolution of `Foo`\n\n      private class Foo\n        def foo\n          100\n        end\n      end\n\n      private class Bar2 < Foo\n        def foo\n          1000\n        end\n      end\n\n      Fred.new.as(Fred).foo &+ Foo.new.as(Foo).foo\n      CRYSTAL\n  end\n\n  it \"does not combine virtual metaclass types with same name but different file scopes\" do\n    run(<<-CRYSTAL, Int32, filename: \"foo.cr\").should eq(101)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar1 < Foo\n        def self.foo\n          10\n        end\n      end\n\n      alias Fred = Foo\n      {% Fred %} # forces immediate resolution of `Foo`\n\n      private class Foo\n        def self.foo\n          100\n        end\n      end\n\n      private class Bar2 < Foo\n        def self.foo\n          1000\n        end\n      end\n\n      Fred.as(Fred.class).foo &+ Foo.as(Foo.class).foo\n      CRYSTAL\n  end\n\n  it \"does not combine generic virtual metaclass types with same name but different file scopes\" do\n    run(<<-CRYSTAL, Int32, filename: \"foo.cr\").should eq(11)\n      module Foo\n        def self.foo\n          1\n        end\n      end\n\n      alias Bar = Foo\n\n      {% Bar %} # forces immediate resolution of `Bar`\n\n      private module Foo\n        def self.foo\n          10\n        end\n      end\n\n      abstract class Base\n      end\n\n      class Gen(T) < Base\n        def self.x\n          T.foo\n        end\n      end\n\n      Gen(Foo).as(Base.class).x &+ Gen(Bar).as(Base.class).x\n      CRYSTAL\n  end\n\n  it \"does not combine generic module metaclass types with same name but different file scopes\" do\n    run(<<-CRYSTAL, Int32, filename: \"foo.cr\").should eq(11)\n      module Foo\n        def self.foo\n          1\n        end\n      end\n\n      alias Bar = Foo\n\n      {% Bar %} # forces immediate resolution of `Bar`\n\n      private module Foo\n        def self.foo\n          10\n        end\n      end\n\n      module Gen(T)\n        def self.x\n          T.foo\n        end\n      end\n\n      (Gen(Foo) || Gen(Bar)).x &+ (Gen(Bar) || Gen(Foo)).x\n      CRYSTAL\n  end\n\n  it \"builds generic class bug\" do\n    codegen(<<-CRYSTAL)\n      abstract class Base\n        def initialize\n          @value = 1\n        end\n      end\n\n      class Foo(T) < Base\n        @target : Nil\n\n        def foo\n          @target\n        end\n      end\n\n      class Bar < Base\n        def foo\n        end\n      end\n\n      ex = Foo(Int32).new || Bar.new\n      ex.foo\n      CRYSTAL\n  end\n\n  it \"resolves type declaration when accessing instance var (#348)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        type Foo = Int64[8]\n      end\n\n      class Bar\n        def initialize\n          @foo = uninitialized LibC::Foo\n        end\n      end\n\n      Bar.new.inspect\n      CRYSTAL\n  end\n\n  it \"gets class of virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      f = Bar.new || Foo.new\n      f.class.foo\n      CRYSTAL\n  end\n\n  it \"notifies superclass recursively on inheritance (#576)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Qux\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n\n        def foo\n          name\n        end\n      end\n\n      class Foo\n      end\n\n      ptr = Pointer(Foo.class).malloc(1_u64)\n      ptr.value = Foo\n      ptr.value.foo\n\n      class Bar < Foo; end\n      ptr.value = Bar\n      ptr.value.foo\n\n      class Baz < Bar; end\n      ptr.value = Baz\n      ptr.value.foo\n\n      class Qux < Baz; end\n      ptr.value = Qux\n      ptr.value.foo\n      CRYSTAL\n  end\n\n  it \"works with array in variable initializer in non-generic type (#855)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      require \"prelude\"\n\n      class Foo\n        @ary = [1, 2, 3]\n\n        def sum\n          @ary.sum\n        end\n      end\n\n      Foo.new.sum\n      CRYSTAL\n  end\n\n  it \"works with array in variable initializer in generic type (#855)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      require \"prelude\"\n\n      class Foo(T)\n        @ary = [1, 2, 3]\n\n        def sum\n          @ary.sum\n        end\n      end\n\n      Foo(Int32).new.sum\n      CRYSTAL\n  end\n\n  it \"doesn't crash on instance variable assigned a proc, and never instantiated (#923)\" do\n    codegen(<<-CRYSTAL)\n      class Klass\n        def self.f(arg)\n        end\n\n        @a : Proc(String, Nil) = ->f(String)\n      end\n      CRYSTAL\n  end\n\n  it \"does to_s on class\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Class\")\n      require \"prelude\"\n\n      class Foo\n      end\n\n      Foo.class.to_s\n      CRYSTAL\n  end\n\n  it \"invokes class method inside instance method (#1119)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class Class\n        def bar\n          123\n        end\n      end\n\n      class Foo\n        def test\n          Foo.class\n        end\n      end\n\n      x = Foo.new.test\n      x.bar\n      CRYSTAL\n  end\n\n  it \"codegens method of class union including Int (#1476)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Class\n        def foo\n          1\n        end\n      end\n\n      x = Int || Int32\n      x.foo\n      CRYSTAL\n  end\n\n  it \"can use a Main class (#1628)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Main\n        def self.foo\n          1\n        end\n      end\n\n      Main.foo\n      CRYSTAL\n  end\n\n  it \"codegens singleton (#718)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Hello\")\n      class Singleton\n        @@instance = new\n\n        def initialize\n          @msg = \"Hello\"\n        end\n\n        def msg\n          @msg\n        end\n\n        def self.get_instance\n          @@instance\n        end\n      end\n\n      Singleton.get_instance.msg\n      CRYSTAL\n  end\n\n  it \"doesn't crash if not using undefined instance variable in superclass\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      foo = Bar.new(42)\n      foo.x\n      CRYSTAL\n  end\n\n  it \"codegens virtual metaclass union bug (#2597)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Foo1 < Foo\n        def self.foo\n          2\n        end\n      end\n\n      class Foo2 < Foo\n        def self.foo\n          3\n        end\n      end\n\n      class Bar\n        @foo : Foo.class\n\n        def initialize\n          @foo = if 1 == 1\n                   Foo1\n                 elsif 1 == 2\n                   Foo2\n                 else\n                   Foo\n                 end\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Bar.new.foo.foo\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #1216\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        def initialize(@ivar : Int32)\n          meth\n        end\n\n        def meth\n          r = self.class.new(5)\n          r.@ivar\n        end\n      end\n\n      Foo.new(6)\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #1216 with pointerof\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        def initialize(@ivar : Int32)\n          meth\n        end\n\n        def meth\n          r = self.class.new(5)\n          pointerof(r.@ivar)\n        end\n      end\n\n      Foo.new(6)\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #1216 (reduced)\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        def foo\n          crash.foo\n        end\n      end\n\n      def crash\n        x = Foo.allocate\n        x.foo\n        x\n      end\n\n      crash\n      CRYSTAL\n  end\n\n  it \"doesn't crash on abstract class never instantiated (#2840)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      abstract class Foo\n      end\n\n      if 1 == 2\n        true\n      else\n        Pointer(Foo).malloc(1_u64).value.foo\n      end\n      CRYSTAL\n  end\n\n  it \"can assign virtual metaclass to virtual metaclass (#3007)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      class Baz < Bar\n        def self.foo\n          3\n        end\n      end\n\n      class Gen(T)\n        def initialize(x : T)\n        end\n      end\n\n      ptr = Pointer(Foo.class).malloc(1_u64)\n      ptr.value = Bar || Baz\n      ptr.value.foo\n      CRYSTAL\n  end\n\n  it \"transfers initializer from module to generic class\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      module Moo\n        @x = 123\n\n        def x\n          @x\n        end\n      end\n\n      class Foo(T)\n        include Moo\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"transfers initializer from generic module to non-generic class\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      module Moo(T)\n        @x = 123\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"transfers initializer from generic module to generic class\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      module Moo(T)\n        @x = 123\n\n        def x\n          @x\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"doesn't skip false initializers (#3272)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      class Parent\n        @foo = true\n\n        def foo\n          @foo\n        end\n      end\n\n      class Child < Parent\n        @foo = false\n      end\n\n      Child.new.foo ? 10 : 20\n      CRYSTAL\n  end\n\n  it \"doesn't skip zero initializers (#3272)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      class Parent\n        @foo = 123\n\n        def foo\n          @foo\n        end\n      end\n\n      class Child < Parent\n        @foo = 0\n      end\n\n      Child.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens virtual generic class instance metaclass (#3819)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      module Core\n      end\n\n      class Base(T)\n        include Core\n      end\n\n      class Foo < Base(String)\n      end\n\n      class Bar < Base(Int32)\n      end\n\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      Foo.new.as(Core).class.name\n      CRYSTAL\n  end\n\n  it \"codegens class with recursive tuple to class (#4520)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(1)\n      class Foo\n        @foo : {Foo, Foo}?\n\n        def initialize(@x : Int32)\n        end\n\n        def foo=(@foo)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.foo = {Foo.new(2), Foo.new(3)}\n      foo.x\n      CRYSTAL\n  end\n\n  it \"runs instance variable initializer at the class level\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @x : Int32 = bar\n\n        def self.bar\n          42\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"runs instance variable initializer at the class level, for generic type\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo(T)\n        @x : T = bar\n\n        def self.bar\n          42\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  pending \"codegens assignment of generic metaclasses (1) (#10394)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(T)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo(T); end\n      class Bar(T) < Foo(T); end\n\n      x = Foo\n      x = Bar\n      x.name\n      CRYSTAL\n  end\n\n  pending \"codegens assignment of generic metaclasses (2) (#10394)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(Int32)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo(T); end\n      class Bar(T) < Foo(T); end\n\n      x = Foo\n      x = Bar(Int32)\n      x.name\n      CRYSTAL\n  end\n\n  it \"codegens assignment of generic metaclasses (3) (#10394)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(Int32)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo(T); end\n      class Bar(T) < Foo(T); end\n\n      x = Foo(Int32)\n      x = Bar(Int32)\n      x.name\n      CRYSTAL\n  end\n\n  it \"codegens assignment of generic metaclasses (4) (#10394)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(Int32)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo(T); end\n      class Bar(T) < Foo(T); end\n\n      x = Foo(String)\n      x = Bar(Int32)\n      x.name\n      CRYSTAL\n  end\n\n  it \"codegens assignment of generic metaclasses, base is non-generic (1) (#10394)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(T)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo; end\n      class Bar(T) < Foo; end\n\n      x = Foo\n      x = Bar\n      x.name\n      CRYSTAL\n  end\n\n  it \"codegens assignment of generic metaclasses, base is non-generic (2) (#10394)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(Int32)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo; end\n      class Bar(T) < Foo; end\n\n      x = Foo\n      x = Bar(Int32)\n      x.name\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/class_var_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: class var\" do\n  it \"codegens class var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @@foo = 1\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"codegens class var as nil\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil; def to_i; 0; end; end\n\n      class Foo\n        @@foo = nil\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo.to_i\n      CRYSTAL\n  end\n\n  it \"codegens class var inside instance method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @@foo = 1\n\n        def foo\n          @@foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens class var as nil if assigned for the first time inside method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil; def to_i!; 0; end; end\n\n      class Foo\n        def self.foo\n          @@foo = 1\n          @@foo\n        end\n      end\n\n      Foo.foo.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens class var inside module\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Foo\n        @@foo = 1\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"accesses class var from proc literal\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @@a = 1\n\n        def self.foo\n          ->{ @@a }.call\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"reads class var before initializing it (hoisting)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      x = Foo.var\n\n      class Foo\n        @@var = 42\n\n        def self.var\n          @@var\n        end\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"uses var in class var initializer\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo\n        @@var : Int32\n        @@var = begin\n          a = class_method\n          a &+ 3\n        end\n\n        def self.var\n          @@var\n        end\n\n        def self.class_method\n          1 &+ 2\n        end\n      end\n\n      Foo.var\n      CRYSTAL\n  end\n\n  it \"reads simple class var before another complex one\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @@var2 : Int32\n        @@var2 = @@var &+ 1\n\n        @@var = 41\n\n        def self.var2\n          @@var2\n        end\n      end\n\n      Foo.var2\n      CRYSTAL\n  end\n\n  it \"initializes class var of union with single type\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @@var : Int32 | String\n        @@var = 42\n\n        def self.var\n          @@var\n        end\n      end\n\n      var = Foo.var\n      if var.is_a?(Int32)\n        var\n      else\n        0\n      end\n      CRYSTAL\n  end\n\n  it \"initializes class var with array literal\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      class Foo\n        @@var = [1, 2, 4]\n\n        def self.var\n          @@var\n        end\n      end\n\n      Foo.var.size\n      CRYSTAL\n  end\n\n  it \"codegens second class var initializer\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @@var = 1\n        @@var = 2\n\n        def self.var\n          @@var\n        end\n      end\n\n      Foo.var\n      CRYSTAL\n  end\n\n  it \"initializes dependent constant before class var\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      def foo\n        a = 1\n        b = 2\n        a &+ b\n      end\n\n      CONST = foo()\n\n      class Foo\n        @@foo : Int32\n        @@foo = CONST\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"declares and initializes\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @@x : Int32 = 42\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't use nilable type for initializer\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @@foo : Int32?\n        @@foo = 42\n\n        @@bar : Int32?\n        @@bar = @@foo\n\n        def self.bar\n          @@bar\n        end\n      end\n\n      Foo.bar || 10\n      CRYSTAL\n  end\n\n  it \"codegens class var with begin and vars\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        @@foo : Int32\n        @@foo = begin\n          a = 1\n          b = 2\n          a &+ b\n        end\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"codegens class var with type declaration begin and vars\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        @@foo : Int32 = begin\n          a = 1\n          b = 2\n          a &+ b\n        end\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"codegens class var with nilable reference type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hello\")\n      class Foo\n        @@foo : String? = nil\n\n        def self.foo\n          @@foo ||= \"hello\"\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"initializes class var the moment it reaches it\" do\n    run(<<-CRYSTAL).to_string.should eq(\"BAR\")\n      require \"prelude\"\n\n      ENV[\"FOO\"] = \"BAR\"\n\n      class Foo\n        @@x = ENV[\"FOO\"]\n\n        def self.x\n          @@x\n        end\n      end\n\n      w = Foo.x\n      z = Foo.x\n      z\n      CRYSTAL\n  end\n\n  it \"gets pointerof class var\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      z = Foo.foo\n\n      class Foo\n        @@foo = 10\n\n        def self.foo\n          pointerof(@@foo).value\n        end\n      end\n\n      z\n      CRYSTAL\n  end\n\n  it \"gets pointerof class var complex constant\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      z = Foo.foo\n\n      class Foo\n        @@foo : Int32\n        @@foo = begin\n          a = 10\n          a\n        end\n\n        def self.foo\n          pointerof(@@foo).value\n        end\n      end\n\n      z\n      CRYSTAL\n  end\n\n  it \"doesn't inherit class var value in subclass\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @@var = 1\n\n        def self.var\n          @@var\n        end\n\n        def self.var=(@@var)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Foo.var = 2\n\n      Bar.var\n      CRYSTAL\n  end\n\n  it \"doesn't inherit class var value in module\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Moo\n        @@var = 1\n\n        def var\n          @@var\n        end\n\n        def self.var=(@@var)\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Moo.var = 2\n\n      Foo.new.var\n      CRYSTAL\n  end\n\n  it \"reads class var from virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @@var = 1\n\n        def self.var=(@@var)\n        end\n\n        def self.var\n          @@var\n        end\n\n        def var\n          @@var\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.var = 2\n\n      ptr = Pointer(Foo).malloc(1_u64)\n      ptr.value = Bar.new\n      ptr.value.var\n      CRYSTAL\n  end\n\n  it \"reads class var from virtual type metaclass\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @@var = 1\n\n        def self.var=(@@var)\n        end\n\n        def self.var\n          @@var\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.var = 2\n\n      ptr = Pointer(Foo.class).malloc(1_u64)\n      ptr.value = Bar\n      ptr.value.var\n      CRYSTAL\n  end\n\n  it \"writes class var from virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @@var = 1\n\n        def self.var=(@@var)\n        end\n\n        def self.var\n          @@var\n        end\n\n        def var=(@@var)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      ptr = Pointer(Foo).malloc(1_u64)\n      ptr.value = Bar.new\n      ptr.value.var = 2\n\n      Bar.var\n      CRYSTAL\n  end\n\n  it \"declares var as uninitialized and initializes it unsafely\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        @@x = uninitialized Int32\n        @@x = Foo.bar\n\n        def self.bar\n          if 1 == 2\n            @@x\n          else\n            10\n          end\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't crash with pointerof from another module\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo\n        @@x : Int32?\n        @@x = 1\n\n        def self.x\n          pointerof(@@x).value\n        end\n      end\n\n      class Bar\n        def self.bar\n          Foo.x\n        end\n      end\n\n      Bar.bar\n      CRYSTAL\n  end\n\n  it \"codegens generic class with class var\" do\n    run(<<-CRYSTAL).to_i.should eq(1 + 1 + 10 + 20)\n      class Foo(T)\n        @@bar = 1\n\n        def bar\n          @@bar\n        end\n\n        def bar=(@@bar)\n        end\n      end\n\n      f1 = Foo(Int32).new\n      f2 = Foo(String).new\n\n      a = f1.bar\n      b = f2.bar\n      f1.bar = 10\n      c = f2.bar\n      f2.bar = 20\n      d = f1.bar\n      a &+ b &+ c &+ d\n      CRYSTAL\n  end\n\n  it \"inline initialization of simple class var\" do\n    mod = codegen(<<-CRYSTAL)\n      class Foo\n        @@x = 1\n      end\n      CRYSTAL\n\n    mod.to_s.should_not contain(\"x:init\")\n  end\n\n  it \"doesn't error if class var shares name with const (#7865)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"asdfgh\")\n      require \"prelude\"\n\n      class Pattern\n        @@A = \"asdf\"\n        A = \"\\#{@@A}gh\"\n      end\n\n      Pattern::A\n      CRYSTAL\n  end\n\n  it \"catch infinite loop in class var initializer\" do\n    run(<<-CRYSTAL).to_string.should eq(\"error: Recursion while initializing class variables and/or constants\")\n      require \"prelude\"\n\n      module Crystal\n        def self.main_user_code(argc : Int32, argv : UInt8**)\n          LibCrystalMain.__crystal_main(argc, argv)\n        rescue ex\n          print \"error: \\#{ex.message}\"\n        end\n      end\n\n      class Foo\n        @@x : Int32 = init\n\n        def self.init\n          @@x + 1\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      nil\n      CRYSTAL\n  end\n\n  it \"runs class var side effects (#8862)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      require \"prelude\"\n\n      class Foo\n        @@x = 0\n\n        def self.set\n          @@x = 3\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      a = Hello.value\n\n      class Hello\n        @@value : Int32 = begin\n          Foo.set\n          1 &+ 2\n        end\n\n        def self.value\n          @@value\n        end\n      end\n\n      a &+ Foo.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/closure_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: closure\" do\n  it \"codegens simple closure at global scope\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1\n      foo = ->{ a }\n      foo.call\n      CRYSTAL\n  end\n\n  it \"codegens simple closure in function\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        a = 1\n        ->{ a }\n      end\n\n      foo.call\n      CRYSTAL\n  end\n\n  it \"codegens simple closure in function with argument\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(a)\n        ->{ a }\n      end\n\n      foo(1).call\n      CRYSTAL\n  end\n\n  it \"codegens simple closure in block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      f = foo do\n        x = 1\n        -> { x }\n      end\n\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closured nested in block\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo\n        yield\n      end\n\n      a = 1\n      f = foo do\n        b = 2\n        -> { a &+ b }\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closured nested in block with a call with a closure with same names\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      def foo\n        a = 3\n        f = -> { a }\n        yield f.call\n      end\n\n      a = 1\n      f = foo do |x|\n        -> { a &+ x }\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with block that declares same var\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo\n        a = 1\n        yield a\n      end\n\n      f = foo do |x|\n        a = 2\n        -> { a &+ x }\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with def that has an if\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield 1 if 1\n        yield 2\n      end\n\n      f = foo do |x|\n        -> { x }\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens multiple nested blocks\" do\n    run(<<-CRYSTAL).to_i.should eq(9)\n      def foo\n        yield 1\n        yield 2\n        yield 3\n      end\n\n      a = 1\n      f = foo do |x|\n        b = 1\n        foo do |y|\n          c = 1\n          -> { a &+ b &+ c &+ x &+ y }\n        end\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with nested context without new closured vars\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      a = 1\n      f = foo do\n        -> { a }\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with nested context without new closured vars\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield\n      end\n\n      def bar\n        yield\n      end\n\n      a = 1\n      f = foo do\n        b = 1\n        bar do\n          -> { a &+ b }\n        end\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with nested context without new closured vars but with block arg\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        yield\n      end\n\n      def bar\n        yield 3\n      end\n\n      a = 1\n      f = foo do\n        b = 1\n        bar do |x|\n          x\n          -> { a &+ b }\n        end\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"unifies types of closured var\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      a = 1\n      f = -> { a }\n      a = 2.5\n      f.call.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens closure with block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      a = 1\n      ->{ foo { a } }.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with self and var\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          a = 2\n          ->{ self.x &+ a }\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(1).foo.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with implicit self and var\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          a = 2\n          ->{ x &+ a }\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(1).foo.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with instance var and var\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          a = 2\n          ->{ @x &+ a }\n        end\n      end\n\n      Foo.new(1).foo.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          ->{ @x }\n        end\n      end\n\n      Foo.new(1).foo.call\n      CRYSTAL\n  end\n\n  it \"codegens closure with instance var and block\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def bar\n        yield\n      end\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          bar do\n            a = 2\n            ->{ @x &+ a }\n          end\n        end\n      end\n\n      Foo.new(1).foo.call\n      CRYSTAL\n  end\n\n  it \"codegen closure in instance method without self closured\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          ->(a : Int32) { a }\n        end\n      end\n\n      Foo.new.foo.call(1)\n      CRYSTAL\n  end\n\n  it \"codegens closure inside initialize inside block with self\" do\n    run(<<-CRYSTAL)\n      def foo\n        yield\n      end\n\n      class Foo\n        def initialize\n          -> { self }\n        end\n      end\n\n      foo do\n        Foo.new\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't free closure memory (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1249975000_i64)\n      require \"prelude\"\n\n      def foo\n        i = 0\n        while i < 50_000\n          yield i\n          i += 1\n        end\n      end\n\n      funcs = [] of -> Int32\n\n      foo do |x|\n        funcs.push(->{ x })\n      end\n\n      a = 0_i64\n      funcs.each do |func|\n        a += func.call\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"codegens nested closure\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1\n      ->{ ->{ a } }.call.call\n      CRYSTAL\n  end\n\n  it \"codegens super nested closure\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1\n      ->{ ->{ -> { -> { a } } } }.call.call.call.call\n      CRYSTAL\n  end\n\n  it \"codegens nested closure with block (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      a = 1\n      ->{ foo { ->{ a } } }.call.call\n      CRYSTAL\n  end\n\n  it \"codegens nested closure with block (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      a = 1\n      ->{ ->{ foo { a } } }.call.call\n      CRYSTAL\n  end\n\n  it \"codegens nested closure with nested closured variable\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      a = 1\n      ->{\n        b = 2\n        ->{ a &+ b }\n      }.call.call\n      CRYSTAL\n  end\n\n  it \"codegens super nested closure with nested closured variable\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo\n        yield 4\n      end\n\n      a = 1\n      ->{\n        b = 2\n        ->{\n          -> {\n            -> {\n              c = 3\n              foo do |d|\n                -> {\n                  a &+ b &+ c &+ d\n                }\n              end\n            }\n          }\n        }\n      }.call.call.call.call.call\n      CRYSTAL\n  end\n\n  it \"codegens proc literal with struct\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      f = ->(foo : Foo) { foo.x }\n\n      obj = Foo.new(2)\n      f.call(obj)\n      CRYSTAL\n  end\n\n  it \"codegens closure with struct\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      a = 1\n      f = ->(foo : Foo) {\n        foo.x &+ a\n      }\n\n      obj = Foo.new(2)\n      f.call(obj)\n      CRYSTAL\n  end\n\n  it \"codegens closure with self and arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo(x)\n          @x &+ x\n        end\n\n        def bar\n          ->foo(Int32)\n        end\n      end\n\n      f = Foo.new(1).bar\n      f.call(2)\n      CRYSTAL\n  end\n\n  it \"codegens nested closure that mentions var in both contexts\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1\n      f = ->{\n        a\n        -> { a }\n      }\n      f.call.call\n      CRYSTAL\n  end\n\n  it \"transforms block to proc literal\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(&block : Int32 -> Int32)\n        block.call(1)\n      end\n\n      a = 1\n      foo do |x|\n        x &+ a\n      end\n      CRYSTAL\n  end\n\n  it \"transforms block to proc literal with free var\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo(&block : Int32 -> U) forall U\n        block\n      end\n\n      a = 1\n      g = foo { |x| x &+ a }\n      h = foo { |x| x.to_f + a }\n      (g.call(3) + h.call(5)).to_i!\n      CRYSTAL\n  end\n\n  it \"allows passing block as proc literal to new and to initialize\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize(&block : Int32 -> Float64)\n          @block = block\n        end\n\n        def block\n          @block\n        end\n      end\n\n      a = 1\n      foo = Foo.new { |x| x.to_f! + a }\n      foo.block.call(1).to_i!\n      CRYSTAL\n  end\n\n  it \"allows giving less block args when transforming block to proc literal\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(&block : Int32 -> U) forall U\n        block.call(1)\n      end\n\n      a = 1\n      v = foo do\n        1.5 + a\n      end\n      v.to_i!\n      CRYSTAL\n  end\n\n  it \"allows passing proc literal to def that captures block with &\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(&block : Int32 -> Int32)\n        block.call(1)\n      end\n\n      a = 1\n      f = ->(x : Int32) { x &+ a }\n      foo &f\n      CRYSTAL\n  end\n\n  it \"allows mixing yield and block.call\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo(&block : Int32 ->)\n        yield 1\n        block.call 2\n      end\n\n      a = 0\n      foo { |x| a &+= x }\n      a\n      CRYSTAL\n  end\n\n  it \"closures struct self\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          ->{ @x }\n        end\n      end\n\n      Foo.new(1).foo.call\n      CRYSTAL\n  end\n\n  it \"doesn't form a closure if invoking class method\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      require \"prelude\"\n\n      class Foo\n        def self.foo\n          ->{ bar }.closure?\n        end\n\n        def self.bar\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"doesn't form a closure if invoking class method with self\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      require \"prelude\"\n\n      class Foo\n        def self.foo\n          ->{ self.bar }.closure?\n        end\n\n        def self.bar\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"captures block and accesses local variable (#2050)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def capture(&block)\n        block\n      end\n\n      coco = 1\n      capture do\n        coco\n      end\n      coco\n      CRYSTAL\n  end\n\n  it \"codegens closured self in block (#3388)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n\n        def foo\n          yield\n          ->{ self }\n        end\n      end\n\n      foo = Foo.new(42)\n      foo2 = foo.foo { }\n      foo2.call.x\n      CRYSTAL\n  end\n\n  it \"doesn't incorrectly consider local as closured (#4948)\" do\n    codegen(<<-CRYSTAL)\n      arg = 1\n\n      f1 = ->{\n        # Here 'local' isn't to be confused with\n        # the outer closured 'local'\n        local = 1\n        local &+ arg\n      }\n\n      arg = 2\n\n      local = 4_i64\n      f2 = ->{ local.to_i! }\n\n      f1.call &+ f2.call\n      CRYSTAL\n  end\n\n  it \"ensures it can raise from the closure check\" do\n    expect_raises(Exception, \"::raise must be of NoReturn return type!\") do\n      codegen(<<-CRYSTAL)\n        def raise(m : String)\n        end\n\n        fun a(a : -> Int32)\n        end\n\n        value = 1\n        p = ->{ value }\n        a(p)\n        CRYSTAL\n    end\n  end\n\n  it \"allows passing an external function along\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n\n      lib LibA\n        fun a(a : Void* -> Void*)\n      end\n\n      fun b(a : Void* -> Void*)\n        LibA.a(a)\n      end\n      CRYSTAL\n  end\n\n  it \"allows passing an external function along (2)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        struct S\n          callback : ->\n        end\n      end\n\n      s = LibFoo::S.new\n      s.callback = nil\n      CRYSTAL\n  end\n\n  it \"uses atomic allocation for closure data if closured variables have no inner pointers\" do\n    run(<<-CRYSTAL, Nil)\n      fun __crystal_malloc64(size : UInt64) : Void*\n        Pointer(Void).new(0_u64)\n      end\n\n      struct Foo\n        @a = 1.2\n        @b = true\n      end\n\n      x = 0\n      y = Foo.new\n      -> { {x, y} }\n      nil\n      CRYSTAL\n  end\n\n  it \"uses non-atomic allocation for closure data if closured variables contain inner pointers\" do\n    run(<<-CRYSTAL, Nil)\n      fun __crystal_malloc_atomic64(size : UInt64) : Void*\n        Pointer(Void).new(0_u64)\n      end\n\n      class Foo\n        @x : Foo?\n\n        def foo\n          -> { @x }\n        end\n      end\n\n      x = Foo.new\n      -> { x }\n\n      y = Pointer(Void).new(0_u64)\n      -> { y }\n\n      x.foo\n      nil\n      CRYSTAL\n  end\n\n  it \"uses non-atomic allocation for nested closure data\" do\n    # nested closures always contain a pointer to their parent closures\n    run(<<-CRYSTAL, Bool).should be_false\n      module Closure\n        @@atomic = uninitialized Int32\n\n        def self.atomic\n          pointerof(@@atomic).as(Void*)\n        end\n      end\n\n      fun __crystal_malloc_atomic64(size : UInt64) : Void*\n        Closure.atomic\n      end\n\n      struct Proc\n        def closure_data\n          func = self\n          ptr = pointerof(func).as({Void*, Void*}*)\n          ptr.value[1]\n        end\n      end\n\n      x = 0\n      fn = -> do\n        y = 0\n        -> { x &+ y }\n      end\n\n      fn.call.closure_data.address == Closure.atomic.address\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/const_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: const\" do\n  it \"define a constant\" do\n    run(\"CONST = 1; CONST\").to_i.should eq(1)\n  end\n\n  it \"support nested constant\" do\n    run(\"class Foo; A = 1; end; Foo::A\").to_i.should eq(1)\n  end\n\n  it \"support constant inside a def\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        A = 1\n\n        def foo\n          A\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"finds nearest constant first\" do\n    run(<<-CRYSTAL).to_f32.should eq(2.5)\n      CONST = 1\n\n      class Foo\n        CONST = 2.5_f32\n\n        def foo\n          CONST\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows constants with same name\" do\n    run(<<-CRYSTAL).to_f32.should eq(2.5)\n      CONST = 1\n\n      class Foo\n        CONST = 2.5_f32\n\n        def foo\n          CONST\n        end\n      end\n\n      CONST\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"constants with expression\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      CONST = 1 + 1\n      CONST\n      CRYSTAL\n  end\n\n  it \"finds global constant\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      CONST = 1\n\n      class Foo\n        def foo\n          CONST\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"define a constant in lib\" do\n    run(\"lib LibFoo; A = 1; end; LibFoo::A\").to_i.should eq(1)\n  end\n\n  it \"invokes block in const\" do\n    run(\"require \\\"prelude\\\"; CONST = [\\\"1\\\"].map { |x| x.to_i }; CONST[0]\").to_i.should eq(1)\n  end\n\n  it \"declare constants in right order\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      CONST1 = 1 + 1\n      CONST2 = true ? CONST1 : 0\n      CONST2\n      CRYSTAL\n  end\n\n  it \"uses correct types lookup\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Moo\n        class B\n          def foo\n            1\n          end\n        end\n\n        C = B.new;\n      end\n\n      def foo\n        Moo::C.foo\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens variable assignment in const\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      CONST = begin\n            f = Foo.new(1)\n            f\n          end\n\n      def foo\n        CONST.x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"declaring var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      BAR = begin\n        a = 1\n        while 1 == 2\n          b = 2\n        end\n        a\n      end\n      class Foo\n        def compile\n          BAR\n        end\n      end\n\n      Foo.new.compile\n      CRYSTAL\n  end\n\n  it \"initialize const that might raise an exception\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n      CONST = (raise \"OH NO\" if 1 == 2)\n\n      def doit\n        CONST\n      rescue\n      end\n\n      doit.nil?\n      CRYSTAL\n  end\n\n  it \"allows implicit self in constant, called from another class (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo\n        def self.foo\n          1\n        end\n\n        A = foo\n      end\n\n      class Bar\n        def bar\n          Foo::A\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"codegens two consts with same variable name\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      CONST1 = begin\n            a = 1\n          end\n\n      CONST2 = begin\n            a = 2.3\n          end\n\n      (CONST1 + CONST2).to_i\n      CRYSTAL\n  end\n\n  it \"works with variable declared inside if\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      require \"prelude\"\n\n      FOO = begin\n        if 1 == 2\n          x = 3\n        else\n          x = 4\n        end\n        x\n      end\n      FOO\n      CRYSTAL\n  end\n\n  it \"codegens constant that refers to another constant that is a struct\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      struct Foo\n        X = Foo.new(1)\n        Y = X\n\n        def initialize(@value : Int32)\n        end\n\n        def value\n          @value\n        end\n      end\n\n      Foo::Y.value\n      CRYSTAL\n  end\n\n  it \"codegens constant that is declared later because of virtual dispatch\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Base\n        def base\n        end\n      end\n\n      class Base2 < Base\n        def base\n        end\n      end\n\n      b = Base.new || Base2.new\n      b.base\n\n      class MyBase < Base\n        CONST = 1\n\n        def base\n          CONST\n        end\n      end\n\n      MyBase.new.base\n      CRYSTAL\n  end\n\n  it \"doesn't crash if constant is used, but class is never instantiated (#1106)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Foo\n        BAR = 1 || 2\n\n        def foo\n          BAR\n        end\n      end\n\n      ->(x : Foo) { x.foo }\n      CRYSTAL\n  end\n\n  it \"uses const before declaring it (hoisting)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      x = CONST\n\n      CONST = foo\n\n      def foo\n        a = 1\n        b = 2\n        a &+ b\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"uses const before declaring it in another module\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      def foo\n        a = 1\n        b = 2\n        a + b\n      end\n\n      class Foo\n        def self.foo\n          CONST\n        end\n      end\n\n      x = Foo.foo\n\n      CONST = foo\n\n      x\n      CRYSTAL\n  end\n\n  it \"initializes simple const\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      FOO = 10\n      FOO\n      CRYSTAL\n  end\n\n  it \"initializes simple const via another const\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      BAR = 10\n      FOO = BAR\n      FOO\n      CRYSTAL\n  end\n\n  it \"initializes ARGC_UNSAFE\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      ARGC_UNSAFE\n      CRYSTAL\n  end\n\n  it \"gets pointerof constant\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      z = pointerof(FOO).value\n      FOO = 10\n      z\n      CRYSTAL\n  end\n\n  it \"gets pointerof complex constant\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      z = pointerof(FOO).value\n      FOO = begin\n        a = 10\n        a\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"gets pointerof constant inside class\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      class Foo\n        BAR = 42\n\n        @z : Int32\n\n        def initialize\n          @z = pointerof(BAR).value\n        end\n\n        def z\n          @z\n        end\n      end\n\n      Foo.new.z\n      CRYSTAL\n  end\n\n  it \"inlines simple const\" do\n    mod = codegen(<<-CRYSTAL)\n      CONST = 1\n      CONST\n      CRYSTAL\n\n    mod.to_s.should_not contain(\"CONST\")\n  end\n\n  it \"inlines enum value\" do\n    mod = codegen(<<-CRYSTAL)\n      enum Foo\n        CONST\n      end\n\n      Foo::CONST\n      CRYSTAL\n\n    mod.to_s.should_not contain(\"CONST\")\n  end\n\n  it \"inlines const with math\" do\n    mod = codegen(<<-CRYSTAL)\n      struct Int32\n        def //(other)\n          self\n        end\n      end\n\n      CONST = (((1 + 2) * 3 &+ 1 &* 3 &- 2) // 2) + 42000\n      CONST\n      CRYSTAL\n    mod.to_s.should_not contain(\"CONST\")\n    mod.to_s.should contain(\"42005\")\n  end\n\n  it \"inlines const referencing another const\" do\n    mod = codegen(<<-CRYSTAL)\n      OTHER = 1\n\n      CONST = OTHER\n      CONST\n      CRYSTAL\n\n    mod.to_s.should_not contain(\"CONST\")\n    mod.to_s.should_not contain(\"OTHER\")\n  end\n\n  it \"inlines bool const\" do\n    mod = codegen(<<-CRYSTAL)\n      CONST = true\n      CONST\n      CRYSTAL\n\n    mod.to_s.should_not contain(\"CONST\")\n  end\n\n  it \"inlines char const\" do\n    mod = codegen(<<-CRYSTAL)\n      CONST = 'a'\n      CONST\n      CRYSTAL\n\n    mod.to_s.should_not contain(\"CONST\")\n  end\n\n  it \"synchronizes initialization of constants\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      def foo\n        v1, v2 = 1, 1\n        rand(100000..10000000).times do\n          v1, v2 = v2, v1 &+ v2\n        end\n        v2\n      end\n\n      ch = Channel(Int32).new\n\n      10.times do\n        spawn do\n          ch.send X\n        end\n      end\n\n      X = foo\n\n      def test(ch)\n        expected = X\n\n        10.times do\n          if ch.receive != expected\n            return false\n          end\n        end\n\n        true\n      end\n\n      test(ch)\n      CRYSTAL\n  end\n\n  it \"runs const side effects (#8862)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      require \"prelude\"\n\n      class Foo\n        @@x = 0\n\n        def self.set\n          @@x = 3\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      a = HELLO\n\n      HELLO = begin\n        Foo.set\n        1 &+ 2\n      end\n\n      a &+ Foo.x\n      CRYSTAL\n  end\n\n  it \"supports closured vars inside initializers (#10474)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def bar\n          3\n        end\n      end\n\n      def func(&block : -> Int32)\n        block.call\n      end\n\n      CONST = begin\n        foo = Foo.new\n        func do\n          foo.bar\n        end\n      end\n\n      CONST\n      CRYSTAL\n  end\n\n  it \"supports storing function returning nil\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def foo\n        \"foo\"\n        nil\n      end\n\n      CONST = foo\n      CONST.nil?\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/debug_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: debug\" do\n  it \"codegens abstract struct (#3578)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      abstract struct Base\n      end\n\n      struct Foo < Base\n      end\n\n      struct Bar < Base\n      end\n\n      x = Foo.new || Bar.new\n      CRYSTAL\n  end\n\n  it \"codegens lib union (#7335)\" do\n    codegen <<-CRYSTAL, debug: Crystal::Debug::All\n      lib Foo\n        union Bar\n          a : Int32\n          b : Int16\n          c : Int8\n        end\n      end\n\n      x = Foo::Bar.new\n      CRYSTAL\n  end\n\n  it \"codegens extern union (#7335)\" do\n    codegen <<-CRYSTAL, debug: Crystal::Debug::All\n      @[Extern(union: true)]\n      struct Foo\n        @a = uninitialized Int32\n        @b = uninitialized Int16\n        @c = uninitialized Int8\n      end\n\n      x = Foo.new\n      CRYSTAL\n  end\n\n  it \"inlines instance var access through getter in debug mode\" do\n    run(<<-CRYSTAL, debug: Crystal::Debug::All, filename: \"foo.cr\").to_i.should eq(2)\n      struct Bar\n        @x = 1\n\n        def set\n          @x = 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        @bar = Bar.new\n\n        def set\n          bar.set\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      foo = Foo.new\n      foo.set\n      foo.bar.x\n      CRYSTAL\n  end\n\n  it \"codegens correct debug info for untyped expression (#4007 and #4008)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      require \"prelude\"\n\n      int = 3\n      case int\n      when 0\n          puts 0\n      when 1, 2, Int32\n          puts \"1 | 2 | Int32\"\n      else\n          puts int\n      end\n      CRYSTAL\n  end\n\n  it \"codegens correct debug info for new with custom allocate (#3945)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      class Foo\n        def initialize\n        end\n\n        def self.allocate\n          Pointer(UInt8).malloc(1_u64).as(self)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"correctly restores debug location after fun change (#4254)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      require \"prelude\"\n\n      class Foo\n        def self.one\n          TWO.two { three }\n          self\n        end\n\n        def self.three\n          1 + 2\n        end\n\n        def two(&block)\n          block\n        end\n      end\n\n      ONE = Foo.one\n      TWO = Foo.new\n\n      ONE.three\n      CRYSTAL\n  end\n\n  it \"has correct debug location after constant initialization in call with block (#4719)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      require \"prelude\"\n\n      fun __crystal_malloc_atomic(size : UInt32) : Void*\n        x = uninitialized Void*\n        x\n      end\n\n      class Foo\n      end\n\n      class Bar\n        def initialize\n          yield\n        end\n      end\n\n      A = Foo.new\n\n      Bar.new { }\n\n      A\n      CRYSTAL\n  end\n\n  it \"has debug info in closure inside if (#5593)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      def foo\n        if true && true\n          yield 1\n        end\n      end\n\n      def bar(&block)\n        block\n      end\n\n      foo do |i|\n        bar do\n          i\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't emit incorrect debug info for closured self\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      def foo(&block : Int32 ->)\n        block.call(1)\n      end\n\n      class Foo\n        def bar\n          foo do\n            self\n          end\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"doesn't emit debug info for unused variable declarations (#9882)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      x : Int32\n      CRYSTAL\n  end\n\n  it \"stores and restores debug location after jumping to main (#6920)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      require \"prelude\"\n\n      Module.method\n\n      module Module\n        def self.value\n          1 &+ 2\n        end\n\n        @@x : Int32 = value\n\n        def self.method\n          @@x\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"stores and restores debug location after jumping to main (2)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      module Foo\n        @@x : Int32 = begin\n          y = 1\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"stores and restores debug location after jumping to main (3)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      def raise(exception)\n        x = uninitialized NoReturn\n        x\n      end\n\n      lib LibFoo\n        $foo : ->\n      end\n\n      LibFoo.foo = ->{ }\n      CRYSTAL\n  end\n\n  it \"doesn't fail on constant read calls (#11416)\" do\n    codegen(<<-CRYSTAL, debug: Crystal::Debug::All)\n      require \"prelude\"\n\n      class Foo\n        def foo\n        end\n      end\n\n      def a_foo\n        Foo.new\n      end\n\n      THE_FOO.foo\n\n      THE_FOO = a_foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on splat expansions inside array-like literals\" do\n    run(<<-CRYSTAL, debug: Crystal::Debug::All).to_i.should eq(123)\n      require \"prelude\"\n\n      class Foo\n        def each\n          yield 1\n          yield 2\n          yield 3\n        end\n      end\n\n      class Bar\n        @bar = 0\n\n        def <<(value)\n          @bar = @bar &* 10 &+ value\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      x = Foo.new\n      y = Bar{*x}\n      y.bar\n      CRYSTAL\n  end\n\n  {% unless LibLLVM::IS_LT_210 %}\n    it \"supports 128-bit enumerators\" do\n      codegen(<<-CRYSTAL, debug: Crystal::Debug::All).to_s.should contain(%(!DIEnumerator(name: \"X\", value: 1002003004005006007008009)))\n        enum Foo : Int128\n          X = 1002003004005006007008009_i128\n        end\n\n        x = Foo::X\n        CRYSTAL\n    end\n  {% end %}\n\n  it \"doesn't fail if no top-level code follows discarded class var initializer (#15970)\" do\n    codegen <<-CRYSTAL, debug: Crystal::Debug::All\n      module Foo\n        @@x = 1\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't fail if class var initializer is followed by metaclass (#15970)\" do\n    codegen <<-CRYSTAL, debug: Crystal::Debug::All\n      module Foo\n        @@x = 1\n      end\n\n      Int32\n      CRYSTAL\n  end\n\n  it \"doesn't fail if Proc self is closured (#16382)\" do\n    codegen <<-CRYSTAL, debug: Crystal::Debug::All\n      struct Proc\n        def partial\n          -> do\n            self\n          end\n        end\n      end\n\n      -> { }.partial.call\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/def_default_value_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: def with default value\" do\n  it \"codegens def with one default value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(x = 1)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens def new with one default value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x = 1)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"considers first the one with more arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(x, y = 1)\n        1\n      end\n\n      def foo(x, y : String)\n        2\n      end\n\n      foo 1, \"hello\"\n      CRYSTAL\n  end\n\n  it \"considers first the one with a restriction\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(x : String, y = \"\")\n        1\n      end\n\n      def foo(x, y)\n        2\n      end\n\n      foo \"hello\"\n      CRYSTAL\n  end\n\n  it \"doesn't mix types of instance vars with initialize and new\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize(x = 1)\n          @x = x\n        end\n\n        def self.new(x : String)\n          new(1)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new\n      Foo.new(1)\n      Foo.new(\"hello\").x\n      CRYSTAL\n  end\n\n  it \"resolves expanded call to current type, not to virtual type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x = 1)\n          2\n        end\n      end\n\n      class Bar < Foo\n        def foo(x)\n          'a'\n        end\n      end\n\n      bar = Bar.new\n      bar.foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/def_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: def\" do\n  it \"codegens empty def\" do\n    run(\"def foo; end; foo\")\n  end\n\n  it \"codegens call without args\" do\n    run(\"def foo; 1; end; 2; foo\").to_i.should eq(1)\n  end\n\n  it \"call functions defined in any order\" do\n    run(\"def foo; bar; end; def bar; 1; end; foo\").to_i.should eq(1)\n  end\n\n  it \"codegens call with args\" do\n    run(\"def foo(x); x; end; foo 1\").to_i.should eq(1)\n  end\n\n  it \"call external function 'putchar'\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      lib LibC\n        fun putchar(c : Char) : Char\n      end\n      LibC.putchar '\\\\0'\n      CRYSTAL\n  end\n\n  it \"uses self\" do\n    run(\"struct Int; def foo; self &+ 1; end; end; 3.foo\").to_i.should eq(4)\n  end\n\n  it \"uses var after external\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibC\n        fun putchar(c : Char) : Char\n      end\n\n      a = 1\n      LibC.putchar '\\\\0'\n      a\n      CRYSTAL\n  end\n\n  it \"allows to change argument values\" do\n    run(\"def foo(x); x = 1; x; end; foo(2)\").to_i.should eq(1)\n  end\n\n  it \"runs empty def\" do\n    run(\"def foo; end; foo\")\n  end\n\n  it \"builds infinite recursive function\" do\n    codegen \"def foo; foo; end; foo\"\n  end\n\n  it \"unifies all calls to same def\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def raise(msg)\n        nil\n      end\n\n      class Hash2\n        def initialize\n          @buckets = [[1]]\n        end\n\n        def []=(key, value)\n          bucket.push value\n        end\n\n        def [](key)\n          bucket[0]\n        end\n\n        def bucket\n          @buckets[0]\n        end\n      end\n\n      hash = Hash2.new\n      hash[1] = 2\n      hash[1]\n      CRYSTAL\n  end\n\n  it \"codegens recursive type with union\" do\n    run(<<-CRYSTAL)\n      class Foo\n        @next : Foo?\n\n        def next=(n)\n          @next = n\n        end\n\n        def next\n          @next\n        end\n      end\n\n      a = Foo.allocate\n      a.next = Foo.allocate\n      a = a.next\n      CRYSTAL\n  end\n\n  it \"codegens with related types\" do\n    run(<<-CRYSTAL)\n      class Foo\n        @next : Foo | Bar | Nil\n\n        def next=(n)\n          @next = n\n        end\n\n        def next\n          @next\n        end\n      end\n\n      class Bar\n        @next : Foo | Bar | Nil\n\n        def next=(n)\n          @next = n\n        end\n\n        def next\n          @next\n        end\n      end\n\n      def foo(x, y)\n        if n = x.next\n          n.next = y\n        end\n      end\n\n      a = Foo.allocate\n      a.next = Bar.allocate\n\n      foo(a, Bar.allocate)\n\n      c = Foo.allocate\n      c.next = Bar.allocate\n\n      foo(c, c.next)\n      CRYSTAL\n  end\n\n  it \"codegens and doesn't break if obj is int and there's a mutation\" do\n    run(<<-CRYSTAL)\n      require \"prelude\"\n\n      struct Int\n        def baz(x)\n        end\n      end\n\n      elems = [1]\n      elems[0].baz [1]\n      CRYSTAL\n  end\n\n  it \"codegens with and without default arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      def foo(x = 1)\n        x &+ 1\n      end\n\n      foo(2) &+ foo\n      CRYSTAL\n  end\n\n  it \"codegens with and without many default arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(40)\n      def foo(x = 1, y = 2, z = 3)\n        x &+ y &+ z\n      end\n\n      foo &+ foo(9) &+ foo(3, 4) &+ foo(6, 3, 1)\n      CRYSTAL\n  end\n\n  it \"codegens with interesting default argument\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      class Foo\n        def foo(x = self.bar)\n          x &+ 1\n        end\n\n        def bar\n          1\n        end\n      end\n\n      f = Foo.new\n\n      f.foo(2) &+ f.foo\n      CRYSTAL\n  end\n\n  it \"codegens dispatch on static method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def Object.foo(x)\n        1\n      end\n\n      a = 1\n      a = 1.5\n      Object.foo(a)\n      CRYSTAL\n  end\n\n  it \"use target def type as return type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        if false\n          return 0\n        end\n      end\n\n      foo.nil? ? 1 : 0\n      CRYSTAL\n  end\n\n  it \"codegens recursive nasty code\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        def initialize(x)\n        end\n      end\n\n      class Bar\n        def initialize(x)\n        end\n      end\n\n      class Box\n        @elem : Foo | Bar | Nil\n\n        def set(elem)\n          @elem = elem\n        end\n\n        def get\n          @elem\n        end\n      end\n\n      def foo\n        exps = Box.new\n        sub = foo\n        t = Foo.new(sub) || Bar.new(sub)\n        exps.set t\n        exps.get || 1\n      end\n\n      false && foo\n      CRYSTAL\n  end\n\n  it \"looks up matches in super classes and merges them with subclasses\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo(other)\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo(other : Int)\n          2\n        end\n      end\n\n      bar1 = Bar.new\n      bar1.foo(1 || 1.5)\n      CRYSTAL\n  end\n\n  it \"codegens def which changes type of arg\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      def foo(x)\n        while x >= 0\n          x = -0.5\n        end\n        x\n      end\n\n      foo(2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens return nil when nilable type (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def foo\n        return if 1 == 1\n        Reference.new\n      end\n\n      foo.nil?\n      CRYSTAL\n  end\n\n  it \"codegens return nil when nilable type (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def foo\n        return nil if 1 == 1\n        Reference.new\n      end\n\n      foo.nil?\n      CRYSTAL\n  end\n\n  it \"codegens dispatch with nilable reference union type\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil; def object_id; 0_u64; end; end\n      class Foo; end\n      class Bar; end\n\n      f = 1 == 1 ? nil : (Foo.new || Bar.new)\n      f.object_id\n      CRYSTAL\n  end\n\n  it \"codegens dispatch without obj, bug 1\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def coco(x : Int32)\n        2\n      end\n\n      def coco(x)\n        3\n      end\n\n      class Foo\n        def foo\n          coco(1 || nil)\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens dispatch without obj, bug 1\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def coco(x : Int32)\n        2\n      end\n\n      def coco(x)\n        3\n      end\n\n      class Foo\n        def foo\n          coco(1 || nil)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      (Foo.new || Bar.new).foo\n      CRYSTAL\n  end\n\n  it \"codegens dispatch with single def when discarding unallocated ones (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      class Bar\n        def bar\n          2\n        end\n      end\n\n      foo = 1 == 1 ? Foo.new : Pointer(Int32).new(0_u64).as(Bar)\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"codegens dispatch with single def when discarding unallocated ones (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      class Bar\n      end\n\n      def something(x : Foo)\n        1\n      end\n\n      def something(x : Bar)\n        2\n      end\n\n      foo = 1 == 1 ? Foo.new : Pointer(Int32).new(0_u64).as(Bar)\n      something(foo)\n      CRYSTAL\n  end\n\n  it \"codegens bug #119\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      require \"prelude\"\n\n      x = {} of String => Hash(String, String)\n      x.has_key?(\"a\")\n      CRYSTAL\n  end\n\n  it \"puts union before single type in matches preferences\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      abstract class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      def foo(x : Foo)\n        2\n      end\n\n      def foo(x : Bar | Baz)\n        1\n      end\n\n      node = Baz.new || Bar.new\n      foo(node)\n      CRYSTAL\n  end\n\n  it \"dispatches on virtual type implementing generic module (related to bug #165)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Moo(T)\n        def moo\n          1\n        end\n      end\n\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        include Moo(Int32)\n      end\n\n      class Baz < Foo\n      end\n\n      def method(x : Moo(Int32))\n        x.moo\n      end\n\n      def method(x : Baz)\n        2\n      end\n\n      foo = Bar.new || Baz.new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"fixes #230: include original owner in mangled def\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Base\n        def some(other : self)\n          false\n        end\n\n        def some(other)\n          false\n        end\n      end\n\n      class Foo(T) < Base\n        def some(other : Foo)\n          true\n        end\n      end\n\n      a = Foo(Int32).new\n      b = Foo(Int32).new || Foo(Int32 | Nil).new || true\n      a.some(b)\n\n      c = Foo(Int32).new\n      c.some(c)\n      CRYSTAL\n  end\n\n  it \"doesn't crash on private def as last expression\" do\n    codegen(<<-CRYSTAL)\n      private def foo\n      end\n      CRYSTAL\n  end\n\n  it \"uses previous argument in default value (#1062)\" do\n    run(<<-CRYSTAL).to_i.should eq(123 * 2 + 456)\n      def foo(x = 123, y = x &+ 456)\n        x &+ y\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"can match N type argument of static array (#1203)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def fn(a : StaticArray(T, N)) forall T, N\n        N\n      end\n\n      n = uninitialized StaticArray(Int32, 10)\n      fn(n)\n      CRYSTAL\n  end\n\n  it \"uses dispatch call type for phi (#3529)\" do\n    codegen(<<-CRYSTAL, inject_primitives: false)\n      def foo(x : Int32)\n        yield\n        1.0\n      end\n\n      def foo(x : Int64)\n        yield\n        1.0\n      end\n\n      foo(1 || 1_i64) do\n        break\n      end\n      CRYSTAL\n  end\n\n  it \"codegens union to union assignment of mutable arg (#3691)\" do\n    codegen(<<-CRYSTAL)\n      def foo(arg)\n        arg = \"\"\n      end\n\n      foo(1 || true)\n      CRYSTAL\n  end\n\n  it \"codegens yield with destructing tuple having unreachable element\" do\n    codegen(<<-CRYSTAL)\n      def foo\n        yield({1, while true; end})\n      end\n\n      foo { |a, b| }\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/double_splat_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: double splat\" do\n  it \"double splats named argument into arguments (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(32 - 10)\n      def foo(x, y)\n        x &- y\n      end\n\n      tup = {x: 32, y: 10}\n      foo **tup\n      CRYSTAL\n  end\n\n  it \"double splats named argument into arguments (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(32 - 10)\n      def foo(x, y)\n        x &- y\n      end\n\n      tup = {y: 10, x: 32}\n      foo **tup\n      CRYSTAL\n  end\n\n  it \"double splats named argument with positional arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(1000 - 20*30)\n      def foo(x, y, z)\n        x &- y &* z\n      end\n\n      tup = {y: 20, z: 30}\n      foo 1000, **tup\n      CRYSTAL\n  end\n\n  it \"double splats named argument with named args (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1000 - 20*30)\n      def foo(x, y, z)\n        x &- y &* z\n      end\n\n      tup = {x: 1000, z: 30}\n      foo **tup, y: 20\n      CRYSTAL\n  end\n\n  it \"double splats named argument with named args (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1000 - 20*30)\n      def foo(x, y, z)\n        x &- y &* z\n      end\n\n      tup = {z: 30}\n      foo **tup, x: 1000, y: 20\n      CRYSTAL\n  end\n\n  it \"double splats twice \" do\n    run(<<-CRYSTAL).to_i.should eq((1000 - 20*30) * 40)\n      def foo(x, y, z, w)\n        (x &- y &* z) &* w\n      end\n\n      tup1 = {x: 1000, z: 30}\n      tup2 = {y: 20, w: 40}\n      foo **tup2, **tup1\n      CRYSTAL\n  end\n\n  it \"matches double splat on method with named args\" do\n    run(<<-CRYSTAL).to_i.should eq(7)\n      def foo(**options)\n        options[:x] &- options[:y]\n      end\n\n      foo x: 10, y: 3\n      CRYSTAL\n  end\n\n  it \"matches double splat on method with named args and regular args\" do\n    run(<<-CRYSTAL).to_i.should eq(1000 - 20*30)\n      def foo(x, **args)\n        x &- args[:y] &* args[:z]\n      end\n\n      foo y: 20, z: 30, x: 1000\n      CRYSTAL\n  end\n\n  it \"matches double splat with regular splat\" do\n    run(<<-CRYSTAL).to_i.should eq((1000 - 20*30) * 40)\n      def foo(*args, **options)\n        (args[0] &- args[1] &* options[:z]) &* options[:w]\n      end\n\n      foo 1000, 20, z: 30, w: 40\n      CRYSTAL\n  end\n\n  it \"evaluates double splat argument just once (#2677)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def data\n        Global.x &+= 1\n        {x: Global.x, y: Global.x, z: Global.x}\n      end\n\n      def test(x, y, z)\n      end\n\n      test(**data)\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"removes literal types in all matches (#6239)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(y : Float64)\n        y.to_i!\n      end\n\n      def bar(x : Float64, **args)\n        foo(**args)\n      end\n\n      bar(x: 1, y: 2.0)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/enum_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: enum\" do\n  it \"codegens enum\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      enum Foo\n        A = 1\n      end\n\n      Foo::A\n      CRYSTAL\n  end\n\n  it \"codegens enum without explicit value\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      enum Foo\n        A\n        B\n        C\n      end\n\n      Foo::C\n      CRYSTAL\n  end\n\n  it \"codegens enum value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      enum Foo\n        A = 1\n      end\n\n      Foo::A.value\n      CRYSTAL\n  end\n\n  it \"creates enum from value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      enum Foo\n        A\n        B\n      end\n\n      Foo.new(1).value\n      CRYSTAL\n  end\n\n  it \"codegens enum bitflags (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      @[Flags]\n      enum Foo\n        A\n      end\n\n      Foo::A\n      CRYSTAL\n  end\n\n  it \"codegens enum bitflags (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      @[Flags]\n      enum Foo\n        A\n        B\n      end\n\n      Foo::B\n      CRYSTAL\n  end\n\n  it \"codegens enum bitflags (4)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      @[Flags]\n      enum Foo\n        A\n        B\n        C\n      end\n\n      Foo::C\n      CRYSTAL\n  end\n\n  it \"codegens enum bitflags None\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      @[Flags]\n      enum Foo\n        A\n      end\n\n      Foo::None\n      CRYSTAL\n  end\n\n  it \"codegens enum bitflags All\" do\n    run(<<-CRYSTAL).to_i.should eq(1 + 2 + 4)\n      @[Flags]\n      enum Foo\n        A\n        B\n        C\n      end\n\n      Foo::All\n      CRYSTAL\n  end\n\n  it \"codegens enum None redefined\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib Lib\n        @[Flags]\n        enum Foo\n          A\n          None = 10\n        end\n      end\n\n      Lib::Foo::None\n      CRYSTAL\n  end\n\n  it \"codegens enum All redefined\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib Lib\n        @[Flags]\n        enum Foo\n          A\n          All = 10\n        end\n      end\n\n      Lib::Foo::All\n      CRYSTAL\n  end\n\n  it \"allows class vars in enum\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      enum Foo\n        A\n\n        @@class_var = 1\n\n        def self.class_var\n          @@class_var\n        end\n      end\n\n      Foo.class_var\n      CRYSTAL\n  end\n\n  it \"automatically defines question method for each enum member (false case)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      struct Enum\n        def ==(other : self)\n          value == other.value\n        end\n      end\n\n      enum Day\n        SomeMonday\n        SomeTuesday\n      end\n\n      day = Day::SomeTuesday\n      day.some_monday?\n      CRYSTAL\n  end\n\n  it \"automatically defines question method for each enum member (true case)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      struct Enum\n        def ==(other : self)\n          value == other.value\n        end\n      end\n\n      enum Day\n        SomeMonday\n        SomeTuesday\n      end\n\n      day = Day::SomeTuesday\n      day.some_tuesday?\n      CRYSTAL\n  end\n\n  it \"automatically defines question method for each enum member (flags, false case)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      struct Enum\n        def includes?(other : self)\n          (value & other.value) != 0\n        end\n      end\n\n      @[Flags]\n      enum Day\n        SomeMonday\n        SomeTuesday\n        SomeWednesday\n      end\n\n      day = Day.new(3)\n      day.some_wednesday?\n      CRYSTAL\n  end\n\n  it \"automatically defines question method for each enum member (flags, true case)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      struct Enum\n        def includes?(other : self)\n          (value & other.value) != 0\n        end\n      end\n\n      @[Flags]\n      enum Day\n        SomeMonday\n        SomeTuesday\n        SomeWednesday\n      end\n\n      day = Day.new(3)\n      day.some_tuesday?\n      CRYSTAL\n  end\n\n  it \"does ~ at compile time for enum member\" do\n    run(<<-CRYSTAL).to_i.should eq(~1)\n      enum Foo\n        Bar = ~1\n      end\n\n      Foo::Bar.value\n      CRYSTAL\n  end\n\n  it \"uses enum value before declaration (hoisting)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      x = Bar.bar\n\n      enum Foo\n        A = 1\n      end\n\n      class Bar\n        def self.bar\n          Foo::A\n        end\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"casts All value to base type\" do\n    run(<<-CRYSTAL).to_i.should eq(-1073741824)\n      @[Flags]\n      enum Foo\n        A = 1 << 30\n        B = 1 << 31\n      end\n\n      Foo::All.value\n      CRYSTAL\n  end\n\n  it \"can use macro calls inside enum value (#424)\" do\n    run(<<-CRYSTAL).to_i.should eq(30)\n      enum Foo\n        macro bar\n          10 + 20\n        end\n\n        A = bar\n      end\n\n      Foo::A.value\n      CRYSTAL\n  end\n\n  it \"can use macro calls inside enum value, macro defined outside enum (#424)\" do\n    run(<<-CRYSTAL).to_i.should eq(30)\n      macro bar\n        10 + 20\n      end\n\n      enum Foo\n        A = bar\n      end\n\n      Foo::A.value\n      CRYSTAL\n  end\n\n  it \"can use macro calls inside enum value, with receiver (#424)\" do\n    run(<<-CRYSTAL).to_i.should eq(30)\n      module Moo\n        macro bar\n          10 + 20\n        end\n      end\n\n      enum Foo\n        A = Moo.bar\n      end\n\n      Foo::A.value\n      CRYSTAL\n  end\n\n  it \"adds a none? method to flags enum\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      @[Flags]\n      enum Foo\n        A\n        B\n      end\n\n      x = 0\n      x &+= 1 if Foo::None.none?\n      x &+= 2 if Foo::A.none?\n      x\n      CRYSTAL\n  end\n\n  it \"can redefine Enum.new and use previous_def\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      enum Foo\n        FOO = 1\n        BAR = 2\n\n        def self.new(x : Int32)\n          previous_def(2)\n        end\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"can define flags enum : UInt64 with more than 32 values (#7268)\" do\n    run(<<-CRYSTAL).to_u64.should eq(1_u64 << 32)\n      @[Flags]\n      enum Foo : UInt64\n        #{Array.new(33) { |i| \"V#{i + 1}\" }.join \"\\n\"}\n      end\n\n      Foo::V33.value\n      CRYSTAL\n  end\n\n  it \"can define flags enum : UInt128 with 128 values\" do\n    run(<<-CRYSTAL).to_u64.should eq(1_u64 << 63)\n      @[Flags]\n      enum Foo : UInt128\n        #{Array.new(128) { |i| \"V#{i + 1}\" }.join \"\\n\"}\n      end\n\n      Foo::V64.value.to_u64!\n      CRYSTAL\n  end\n\n  it \"can define flags enum : UInt128 with compile-time interpreted values\" do\n    run(<<-CRYSTAL).to_u64.should eq(1 << 6)\n      enum Foo : UInt128\n        A = 1_u128 << 6\n        B = 1_u128 << 20\n        C = 1_u128 << 60\n      end\n\n      Foo::A.value.to_u64!\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/exception_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: exception\" do\n  it \"codegens rescue specific leaf exception\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo < Exception\n      end\n\n      def foo\n        raise Foo.new\n      end\n\n      def bar(x)\n        1\n      end\n\n      begin\n        foo\n        2\n      rescue ex : Foo\n        bar(ex)\n      end\n      CRYSTAL\n  end\n\n  it \"codegens exception handler with return\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        begin\n          return 1\n        ensure\n          1 + 2\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does ensure after rescue which returns (#171)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        raise \"foo\"\n      rescue\n        Global.x += 1\n        return\n      ensure\n        Global.x += 1\n      end\n\n      foo\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"executes body if nothing raised (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      y = 1\n      x = begin\n            2\n          rescue\n            y = 10\n          end\n      x + y\n      CRYSTAL\n  end\n\n  it \"executes rescue if something is raised conditionally\" do\n    run(<<-CRYSTAL).to_i.should eq(8)\n      require \"prelude\"\n\n      y = 1\n      x = 1\n\n      x = begin\n            y == 1 ? raise(\"Oh no!\") : nil\n            y = 10\n          rescue\n            y = 4\n          end\n      x + y\n      CRYSTAL\n  end\n\n  it \"executes rescue if something is raised unconditionally\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      require \"prelude\"\n\n      y = 1\n      x = 1\n      x = begin\n            raise \"Oh no!\"\n            y = 10\n          rescue\n            y = 3\n          end\n      x + y\n      CRYSTAL\n  end\n\n  it \"can result into union (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      x = begin\n            1\n          rescue\n            2.1\n          end\n      x.to_i\n      CRYSTAL\n  end\n\n  it \"can result into union (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      y = begin\n            1 > 0 ? raise(\"Oh no!\") : 0\n          rescue\n            2.1\n          end\n      y.to_i\n      CRYSTAL\n  end\n\n  it \"handles nested exceptions\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      a = 0\n      b = begin\n            begin\n              raise \"Oh no!\"\n            rescue\n              a = 1\n              raise \"Boom!\"\n            end\n          rescue\n            2\n          end\n\n      a + b\n      CRYSTAL\n  end\n\n  it \"executes ensure when no exception is raised (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      a = 0\n      b = begin\n            a = 1\n          rescue\n            a = 3\n          ensure\n            a = 10\n          end\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure when no exception is raised (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 0\n      b = begin\n            a = 1\n          rescue\n            a = 3\n          ensure\n            a = 10\n          end\n      b\n      CRYSTAL\n  end\n\n  it \"executes ensure when exception is raised (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      a = 0\n      b = begin\n            a = 1\n            raise \"Oh no!\"\n          rescue\n            a = 3\n          ensure\n            a = 2\n          end\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure when exception is raised (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      a = 0\n      b = begin\n            a = 1\n            raise \"Oh no!\"\n          rescue\n            a = 3\n          ensure\n            a = 2\n          end\n      b\n      CRYSTAL\n  end\n\n  it \"executes ensure when exception is unhandled (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n\n      a = 0\n      b = begin\n            begin\n              a = 1\n              raise \"Oh no!\"\n            rescue Ex1\n              a = 2\n            ensure\n              a = 3\n            end\n          rescue\n            4\n          end\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure when exception is unhandled (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n\n      a = 0\n      b = begin\n            begin\n              a = 1\n              raise \"Oh no!\"\n            rescue Ex1\n              a = 2\n            ensure\n              a = 3\n            end\n          rescue\n            4\n          end\n      b\n      CRYSTAL\n  end\n\n  it \"ensure without rescue\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 0\n      begin\n        begin\n          raise \"Oh no!\"\n        ensure\n          a = 1\n        end\n      rescue\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure when the main block returns\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      require \"prelude\"\n\n      struct Nil; def to_i; 0; end; end\n\n      def foo(x)\n        begin\n          return 0 if 1 == 1\n        ensure\n          x.value = 1\n        end\n      end\n\n      x = 0\n      foo(pointerof(x)).to_i\n      CRYSTAL\n  end\n\n  it \"executes ensure when the main block returns\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo(x)\n        begin\n          return 0 if 1 == 1\n        ensure\n          x.value = 1\n        end\n      end\n\n      x = 0\n      foo(pointerof(x))\n      x\n      CRYSTAL\n  end\n\n  it \"executes ensure when the main block yields and returns\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo2(x)\n        begin\n          yield\n        ensure\n          x.value = 1\n        end\n      end\n\n      def bar2(y)\n        foo2(y) do\n          return if 1 == 1\n        end\n      end\n\n      x = 0\n      bar2(pointerof(x))\n      x\n      CRYSTAL\n  end\n\n  it \"rescues with type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n\n      a = begin\n            raise Ex2.new\n          rescue Ex1\n            1\n          rescue Ex2\n            2\n          end\n\n      a\n      CRYSTAL\n  end\n\n  it \"rescues with types defaults to generic rescue\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n\n      a = begin\n            raise \"Oh no!\"\n          rescue Ex1\n            1\n          rescue Ex2\n            2\n          rescue\n            3\n          end\n\n      a\n      CRYSTAL\n  end\n\n  it \"handles exception in outer block (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n\n      p = 0\n      x = begin\n            begin\n              raise Ex1.new\n            rescue Ex2\n              p = 1\n              1\n            end\n          rescue\n            2\n          end\n\n      x\n      CRYSTAL\n  end\n\n  it \"handles exception in outer block (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n\n      p = 0\n      x = begin\n            begin\n              raise Ex1.new\n            rescue Ex2\n              p = 1\n              1\n            end\n          rescue\n            2\n          end\n\n      p\n      CRYSTAL\n  end\n\n  it \"handles subclass\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n      class Ex3 < Ex1; end\n\n      x = 0\n      begin\n        raise Ex3.new\n      rescue Ex1\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"handle multiple exception types (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n\n      x = 0\n      begin\n        raise Ex2.new\n      rescue Ex1 | Ex2\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"handle multiple exception types (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n      class Ex2 < Exception; end\n\n      x = 0\n      begin\n        raise Ex1.new\n      rescue Ex1 | Ex2\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"receives exception object\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Ex1\")\n      require \"prelude\"\n\n      class Ex1 < Exception\n        def to_s(io)\n          io << \"Ex1\"\n        end\n      end\n\n      x = \"\"\n      begin\n        raise Ex1.new\n      rescue ex\n        x = ex.to_s\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"executes else if no exception is raised (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      x = 1\n      y = begin\n          rescue ex\n            x = 2\n          else\n            x = 3\n          end\n      x\n      CRYSTAL\n  end\n\n  it \"executes else if no exception is raised (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      x = 1\n      y = begin\n          rescue ex\n            x = 2\n          else\n            x = 3\n          end\n      y\n      CRYSTAL\n  end\n\n  it \"doesn't execute else if exception is raised (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n\n      x = 1\n      y = begin\n            raise Ex1.new\n          rescue ex\n            x = 2\n          else\n            x = 3\n          end\n      x\n      CRYSTAL\n  end\n\n  it \"doesn't execute else if exception is raised (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n\n      x = 1\n      y = begin\n            raise Ex1.new\n          rescue ex\n            x = 2\n          else\n            x = 3\n          end\n      y\n      CRYSTAL\n  end\n\n  it \"doesn't execute else if exception is raised conditionally (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n\n      x = 1\n      y = begin\n            raise Ex1.new if 1 == 1\n          rescue ex\n            x = 2\n          else\n            x = 3\n          end\n      x\n      CRYSTAL\n  end\n\n  it \"doesn't execute else if exception is raised conditionally (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Ex1 < Exception; end\n\n      x = 1\n      y = begin\n            raise Ex1.new if 1 == 1\n          rescue ex\n            x = 2\n          else\n            x = 3\n          end\n      y\n      CRYSTAL\n  end\n\n  it \"handle exception raised by proc literal\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      x = 0\n      f = -> { raise \"Foo\" if 1 == 1 }\n      begin\n        f.call\n      rescue\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"codegens issue #118 (1)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      begin\n        raise \"hey\"\n        n = 3\n      ensure\n        p n\n      end\n      CRYSTAL\n  end\n\n  it \"codegens issue #118 (2)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      n = nil\n      begin\n        raise \"hey\"\n        n = 3\n      ensure\n        p n\n      end\n      CRYSTAL\n  end\n\n  it \"captures exception thrown from proc\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      def foo\n        ->{ raise \"OH NO\" }.call\n      end\n\n      a = 1\n      begin\n        foo\n      rescue\n        a = 2\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"uses exception after rescue\" do\n    run(<<-CRYSTAL).to_string.should eq(\"OH NO\")\n      require \"prelude\"\n\n      begin\n        raise \"OH NO\"\n      rescue ex\n      end\n      ex.not_nil!.message\n      CRYSTAL\n  end\n\n  it \"doesn't codegen duplicated ensure if unreachable (#709)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          exit if 1 == 2\n        end\n      end\n\n      begin\n        begin\n          while true\n          end\n        ensure\n          Foo.new.object_id\n        end\n      ensure\n      end\n      CRYSTAL\n  end\n\n  it \"executes ensure when raising inside rescue\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      a = 1\n\n      begin\n        begin\n          raise \"OH NO\"\n        rescue\n          raise \"LALA\"\n        ensure\n          a = 2\n        end\n      rescue\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure of break inside while inside body\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      a = 0\n      while true\n        begin\n          break\n        ensure\n          a = 123\n        end\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure of break inside while inside body with nested handlers\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      a = 0\n      b = 0\n      begin\n        while true\n          begin\n            break\n          ensure\n            a += 1\n          end\n        end\n        b = a\n      ensure\n        a += 1\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"executes ensure of break inside while inside body with block\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      require \"prelude\"\n\n      class Global\n        @@a = 0\n        @@b = 0\n\n        def self.a=(@@a)\n        end\n\n        def self.a\n          @@a\n        end\n\n        def self.b=(@@b)\n        end\n\n        def self.b\n          @@b\n        end\n      end\n\n      def bar\n        begin\n          yield\n        ensure\n          Global.a = 1\n        end\n      end\n\n      bar do\n        while true\n          break\n        end\n        Global.b = Global.a\n      end\n\n      Global.b\n      CRYSTAL\n  end\n\n  it \"executes ensure of break inside while inside rescue\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      a = 0\n      while true\n        begin\n          raise \"OH NO\"\n        rescue\n          break\n        ensure\n          a = 123\n        end\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure of break inside while inside else\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      a = 0\n      while true\n        begin\n        rescue\n        else\n          break\n        ensure\n          a = 123\n        end\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure of next inside while inside body\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      a = 0\n      continue = true\n      while continue\n        continue = false\n        begin\n          next\n        ensure\n          a = 123\n        end\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"executes return inside rescue, executing ensure\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        begin\n          begin\n            raise \"foo\"\n          rescue\n            Global.x += 1\n            return\n          end\n        ensure\n          Global.x += 1\n        end\n      end\n\n      foo\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"executes ensure from return until target\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        yield\n        return\n      end\n\n      a = 0\n\n      begin\n        foo {}\n      ensure\n        a += 1\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"executes ensure from return until target\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        begin\n          yield\n        ensure\n          Global.x += 1\n        end\n      end\n\n      def bar\n        begin\n          foo do\n            return\n          end\n        ensure\n          Global.x += 1\n        end\n      end\n\n      bar\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"executes ensure of next inside block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        yield\n      end\n\n      a = 0\n      b = 0\n\n      begin\n        foo do\n          begin\n            next\n          ensure\n            a += 1\n          end\n        end\n        b = a\n      ensure\n        a += 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"executes ensure of next inside block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Global\n        @@a = 0\n        @@b = 0\n\n        def self.a=(@@a)\n        end\n\n        def self.a\n          @@a\n        end\n\n        def self.b=(@@b)\n        end\n\n        def self.b\n          @@b\n        end\n      end\n\n      def foo\n        begin\n          yield\n          Global.b = Global.a\n        ensure\n          Global.a += 1\n        end\n      end\n\n      begin\n        foo do\n          begin\n            next\n          ensure\n            Global.a += 1\n          end\n        end\n      ensure\n        Global.a += 1\n      end\n\n      Global.b\n      CRYSTAL\n  end\n\n  it \"executes ensure of break inside block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def foo\n        yield\n      end\n\n      a = 0\n      b = 0\n\n      begin\n        foo do\n          begin\n            break\n          ensure\n            a += 1\n          end\n        end\n        b = a\n      ensure\n        a += 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"executes ensure of calling method when doing break inside block (#1233)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        yield\n      ensure\n        Global.x = 123\n      end\n\n      foo do\n        break\n      end\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"propagates raise status (#2074)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Foo\n        @var : Var?\n\n        def method1\n          method2\n        end\n\n        def method2\n          if var = @var\n            var.method3\n          end\n        end\n\n        def var=(@var)\n        end\n      end\n\n      class Var\n        def method3\n          raise \"OH NO\"\n        end\n      end\n\n      # method1 isn't marked as raise because @var's type isn't known yet\n      Foo.new.method1\n\n      foo = Foo.new\n      # This causes method2 to recompute, but method1 doesn't get notified\n      # that it might now raise\n      foo.var = Var.new\n      a = 1\n      begin\n        foo.method1\n      rescue ex\n        a = 2\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #1988\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      begin\n        x = 42\n      rescue\n      end\n\n      if x.is_a?(Int32)\n        x\n      else\n        21\n      end\n      CRYSTAL\n  end\n\n  it \"runs #2441\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      require \"prelude\"\n\n      while true\n        begin\n          raise \"foo\"\n        rescue ex\n          break\n        end\n      end\n\n      ex.not_nil!.message.to_s\n      CRYSTAL\n  end\n\n  it \"can rescue TypeCastError (#2607)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      begin\n        (1 || \"foo\").as(String)\n        2\n      rescue e : TypeCastError\n        42\n      rescue e : Exception\n        0\n      end\n      CRYSTAL\n  end\n\n  it \"can use argument in rescue (#2844)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      require \"prelude\"\n\n      def foo(exe)\n        begin\n          raise exe\n        rescue exe\n          exe\n        end\n      end\n\n      ex = Exception.new(\"foo\")\n      ex = foo(ex)\n      ex.message.not_nil!\n      CRYSTAL\n  end\n\n  it \"can use argument in rescue, with a different type (1) (#2844)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      require \"prelude\"\n\n      def foo(exe)\n        begin\n          raise Exception.new(\"foo\") if 1 == 1\n          exe\n        rescue exe\n          exe\n        end\n      end\n\n      ex = foo(1).as(Exception)\n      ex.message.not_nil!\n      CRYSTAL\n  end\n\n  it \"can use argument in rescue, with a different type (2) (#2844)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      def foo(exe)\n        begin\n          raise Exception.new(\"foo\") if 1 == 2\n          exe\n        rescue exe\n          exe\n        end\n      end\n\n      foo(10).as(Int32)\n      CRYSTAL\n  end\n\n  it \"runs NoReturn ensure (#3082)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      begin\n        print 1\n        raise \"OH NO\"\n        print 0\n      rescue\n        print 2\n      ensure\n        print 3\n        exit\n        print 4\n      end\n      print 5\n      CRYSTAL\n  end\n\n  it \"catches exception thrown by as inside method (#4030)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"good\")\n      require \"prelude\"\n\n      def foo\n        a = 1 || \"\"\n        a.as(String)\n      end\n\n      begin\n        foo\n        \"bad\"\n      rescue ex : TypeCastError\n        \"good\"\n      end\n      CRYSTAL\n  end\n\n  it \"types parenthesized expression (#5511)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      require \"prelude\"\n\n      begin\n        ((raise \"foo\").bar).baz\n      rescue ex\n        ex.message\n      end\n      CRYSTAL\n  end\n\n  it \"codegens return from rescue with value\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      require \"prelude\"\n\n      def foo\n        begin\n          raise \"foo\"\n        rescue\n          return 5\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"closures rescue variable (#8141)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      def invoke(&block)\n        block.call\n      end\n\n      ex = nil\n\n      invoke do\n        begin\n        rescue ex\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"handles rescuing module type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo; end\n\n      class Ex1 < Exception\n        include Foo\n      end\n\n      x = 0\n      begin\n        raise Ex1.new\n      rescue Foo\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"handles rescuing union between module type and class type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo; end\n\n      abstract class BaseError < Exception; end\n      class Ex2 < BaseError; end\n\n      class Ex1 < BaseError\n        include Foo\n      end\n\n      x = 0\n      begin\n        raise Ex1.new\n      rescue Foo | BaseError\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"handles rescuing union between module types\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo; end\n      module Bar; end\n\n      class Ex1 < Exception\n        include Foo\n      end\n\n      class Ex2 < Exception\n        include Bar\n      end\n\n      x = 0\n      begin\n        raise Ex1.new\n      rescue Foo | Bar\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"does not rescue just any module\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      module Foo; end\n      module Bar; end\n\n      class Ex < Exception\n        include Foo\n      end\n\n      x = 0\n      begin\n        begin\n          raise Ex.new(\"oh no\")\n        rescue Bar\n          x = 1\n        end\n      rescue ex\n        x = 2\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"rescues a valid union\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo; end\n      module Bar; end\n\n      class Ex < Exception\n        include Foo\n      end\n\n      x = 0\n      begin\n        raise Ex.new(\"oh no\")\n      rescue Union(Foo, Bar)\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"rescues a valid nested union\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo; end\n      module Bar; end\n      module Baz; end\n\n      class Ex < Exception\n        include Foo\n      end\n\n      x = 0\n      begin\n        raise Ex.new(\"oh no\")\n      rescue Union(Baz, Union(Foo, Bar))\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"does not rescue just any union\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      module Foo; end\n      module Bar; end\n      module Baz; end\n\n      class Ex < Exception\n        include Foo\n      end\n\n      x = 0\n      begin\n        raise Ex.new(\"oh no\")\n      rescue Union(Bar, Baz)\n        x = 1\n      rescue\n        x = 2\n      end\n      x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/experimental_spec.cr",
    "content": "require \"../spec_helper\"\n\ndescribe \"Code gen: experimental\" do\n  it \"compiles with no argument\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      @[Experimental]\n      def foo\n      end\n\n      2\n      CRYSTAL\n  end\n\n  it \"compiles with single string argument\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      @[Experimental(\"lorem ipsum\")]\n      def foo\n      end\n\n      2\n      CRYSTAL\n  end\n\n  it \"errors if invalid argument type\" do\n    assert_error <<-CRYSTAL, \"first argument must be a String\"\n      @[Experimental(42)]\n      def foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if too many arguments\" do\n    assert_error <<-CRYSTAL, \"wrong number of experimental annotation arguments (given 2, expected 1)\"\n      @[Experimental(\"lorem ipsum\", \"extra arg\")]\n      def foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if missing link arguments\" do\n    assert_error <<-CRYSTAL, \"too many named arguments (given 1, expected maximum 0)\"\n      @[Experimental(invalid: \"lorem ipsum\")]\n      def foo\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/extern_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: extern struct\" do\n  it \"declares extern struct with no constructor\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      @[Extern]\n      struct Foo\n        @x = uninitialized Int32\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares extern struct with no constructor, assigns var\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      @[Extern]\n      struct Foo\n        @x = uninitialized Int32\n\n        def x=(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x = 10\n      foo.x\n      CRYSTAL\n  end\n\n  it \"declares extern union with no constructor\" do\n    run(<<-CRYSTAL).to_i.should eq(1069547520)\n      @[Extern(union: true)]\n      struct Foo\n        @x = uninitialized Int32\n        @y = uninitialized Float32\n\n        def x=(@x)\n        end\n\n        def x\n          @x\n        end\n\n        def y=(@y)\n        end\n      end\n\n      foo = Foo.new\n      foo.x = 1\n      foo.y = 1.5_f32\n      foo.x\n      CRYSTAL\n  end\n\n  it \"declares extern struct, sets and gets instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      @[Extern]\n      struct Foo\n        @y = uninitialized Float64\n        @x = uninitialized Int32\n\n        def foo\n          @x = 42\n          @x\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"declares extern union, sets and gets instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1069547520)\n      @[Extern(union: true)]\n      struct Foo\n        @x = uninitialized Int32\n        @y = uninitialized Float32\n\n        def foo\n          @x = 1\n          @y = 1.5_f32\n          @x\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"sets callback on extern struct\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      @[Extern]\n      struct Foo\n        @x = uninitialized -> Int32\n\n        def set\n          @x = ->{ 42 }\n        end\n\n        def get\n          @x.call\n        end\n      end\n\n      foo = Foo.new\n      foo.set\n      foo.get\n      CRYSTAL\n  end\n\n  it \"sets callback on extern union\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      @[Extern(union: true)]\n      struct Foo\n        @y = uninitialized Float64\n        @x = uninitialized -> Int32\n\n        def set\n          @x = ->{ 42 }\n        end\n\n        def get\n          @x.call\n        end\n      end\n\n      foo = Foo.new\n      foo.set\n      foo.get\n      CRYSTAL\n  end\n\n  it \"codegens extern proc call twice (#4982)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      @[Extern]\n      struct Data\n        def initialize(@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      f = ->(data : Data) { data.foo }\n\n      x = f.call(Data.new(1))\n      y = f.call(Data.new(2))\n\n      x &+ y\n      CRYSTAL\n  end\n\n  it \"codegens proc that takes and returns large extern struct by value\" do\n    run(<<-CRYSTAL).to_i.should eq(149)\n      @[Extern]\n      struct Foo\n        @unused = uninitialized Int64\n\n        def initialize(@x : Int32, @y : Int32, @z : Int32)\n        end\n      end\n\n      f = ->(foo : Foo) {\n        Foo.new(foo.@x, foo.@y &* 2, foo.@z &* 3)\n      }\n\n      foo = f.call(Foo.new(100, 20, 3))\n      foo.@x &+ foo.@y &+ foo.@z\n      CRYSTAL\n  end\n\n  # These specs *should* also work for 32 bits, but for now we'll\n  # make sure they work in 64 bits (they probably work in 32 bits too,\n  # it's just that the specs need to be a bit different)\n  {% if flag?(:x86_64) || flag?(:aarch64) %}\n    it \"codegens proc that takes an extern struct with C ABI\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n        struct Struct {\n          int x;\n          int y;\n        };\n\n        void foo(struct Struct (*callback)(struct Struct)) {\n          struct Struct s;\n          s.x = 1;\n          s.y = 2;\n          callback(s);\n        }\n        C\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n          end\n\n          alias Callback = Struct ->\n\n          fun foo(callback : Callback) : LibMylib::Struct\n        end\n\n        class Global\n          @@x = 0\n          @@y = 0\n\n          def self.x=(@@x)\n          end\n\n          def self.y=(@@y)\n          end\n\n          def self.x\n            @@x\n          end\n\n          def self.y\n            @@y\n          end\n        end\n\n        LibMylib.foo(->(s) {\n          Global.x = s.x\n          Global.y = s.y\n        })\n\n        Global.x &+ Global.y\n        CRYSTAL\n    end\n\n    it \"codegens proc that takes an extern struct with C ABI (2)\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(33))\n        struct Struct {\n          int x;\n          int y;\n        };\n\n        void foo(struct Struct (*callback)(int, struct Struct, int)) {\n          struct Struct s;\n          s.x = 1;\n          s.y = 2;\n          callback(10, s, 20);\n        }\n        C\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n          end\n\n          alias Callback = Int32, Struct, Int32 ->\n\n          fun foo(callback : Callback) : LibMylib::Struct\n        end\n\n        class Global\n          @@x = 0\n          @@y = 0\n\n          def self.x=(@@x)\n          end\n\n          def self.y=(@@y)\n          end\n\n          def self.x\n            @@x\n          end\n\n          def self.y\n            @@y\n          end\n        end\n\n        LibMylib.foo(->(x, s, y) {\n          Global.x = s.x &+ x\n          Global.y = s.y &+ y\n        })\n\n        Global.x &+ Global.y\n        CRYSTAL\n    end\n\n    it \"codegens proc that takes an extern struct with C ABI, callback returns nil\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n        struct Struct {\n          int x;\n          int y;\n        };\n\n        void foo(void (*callback)(struct Struct)) {\n          struct Struct s;\n          s.x = 1;\n          s.y = 2;\n          callback(s);\n        }\n        C\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n          end\n\n          alias Callback = Struct ->\n\n          fun foo(callback : Callback) : LibMylib::Struct\n        end\n\n        class Global\n          @@x = 0\n          @@y = 0\n\n          def self.x=(@@x)\n          end\n\n          def self.y=(@@y)\n          end\n\n          def self.x\n            @@x\n          end\n\n          def self.y\n            @@y\n          end\n        end\n\n        LibMylib.foo(->(s) {\n          Global.x = s.x\n          Global.y = s.y\n          nil\n        })\n\n        Global.x &+ Global.y\n        CRYSTAL\n    end\n\n    it \"codegens proc that takes and returns an extern struct with C ABI\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(303))\n        struct Struct {\n          int x;\n          int y;\n        };\n\n        struct Struct foo(struct Struct (*callback)(struct Struct)) {\n          struct Struct s;\n          s.x = 1;\n          s.y = 2;\n          return callback(s);\n        }\n        C\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n          end\n\n          alias Callback = Struct -> Struct\n\n          fun foo(callback : Callback) : LibMylib::Struct\n        end\n\n        class Global\n          @@x = 0\n          @@y = 0\n\n          def self.x=(@@x)\n          end\n\n          def self.y=(@@y)\n          end\n\n          def self.x\n            @@x\n          end\n\n          def self.y\n            @@y\n          end\n        end\n\n        s2 = LibMylib.foo(->(s) {\n          Global.x = s.x\n          Global.y = s.y\n          s.x = 100\n          s.y = 200\n          s\n        })\n\n        Global.x &+ Global.y &+ s2.x &+ s2.y\n        CRYSTAL\n    end\n\n    it \"codegens proc that takes and returns an extern struct with C ABI\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(30))\n        struct Struct {\n          int x;\n          int y;\n        };\n\n        struct Struct foo(struct Struct (*callback)(int, int)) {\n          return callback(10, 20);\n        }\n        C\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n          end\n\n          alias Callback = Int32, Int32 -> Struct\n\n          fun foo(callback : Callback) : LibMylib::Struct\n        end\n\n        s2 = LibMylib.foo(->(x, y) {\n          s = LibMylib::Struct.new\n          s.x = x\n          s.y = y\n          s\n        })\n\n        s2.x &+ s2.y\n        CRYSTAL\n    end\n\n    it \"codegens proc that takes and returns an extern struct with sret\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(12))\n        struct Struct {\n          long long x;\n          long long y;\n          long long z;\n        };\n\n        struct Struct foo(struct Struct (*callback)(struct Struct)) {\n          struct Struct s;\n          s.x = 1;\n          s.y = 2;\n          s.z = 3;\n          return callback(s);\n        }\n        C\n        lib LibMylib\n          struct Struct\n            x : Int64\n            y : Int64\n            z : Int64\n          end\n\n          alias Callback = Struct -> Struct\n\n          fun foo(callback : Callback) : LibMylib::Struct\n        end\n\n        class Global\n          @@x = 0\n\n          def self.x=(@@x)\n          end\n\n          def self.x\n            @@x\n          end\n        end\n\n        s2 = LibMylib.foo(->(s) {\n          Global.x &+= s.x\n          Global.x &+= s.y\n          Global.x &+= s.z\n          s\n        })\n\n        Global.x &+= s2.x\n        Global.x &+= s2.y\n        Global.x &+= s2.z\n        Global.x.to_i32\n        CRYSTAL\n    end\n\n    it \"doesn't crash with proc with extern struct that's a closure\" do\n      codegen(<<-CRYSTAL)\n        lib LibMylib\n          struct Struct\n            x : Int64\n            y : Int64\n            z : Int64\n          end\n        end\n\n        a = 1\n        f = ->(s : LibMylib::Struct) {\n          a\n        }\n\n        s = LibMylib::Struct.new\n        f.call(s)\n        CRYSTAL\n    end\n\n    it \"invokes proc with extern struct\" do\n      run(<<-CRYSTAL).to_i.should eq(30)\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n          end\n        end\n\n        class Global\n          @@x = 0\n\n          def self.x=(@@x)\n          end\n\n          def self.x\n            @@x\n          end\n        end\n\n        f = ->(s : LibMylib::Struct) {\n          Global.x &+= s.x\n          Global.x &+= s.y\n        }\n\n        s = LibMylib::Struct.new\n        s.x = 10\n        s.y = 20\n        f.call(s)\n\n        Global.x\n        CRYSTAL\n    end\n\n    it \"invokes proc with extern struct with sret\" do\n      run(<<-CRYSTAL).to_i.should eq(15)\n        lib LibMylib\n          struct Struct\n            x : Int32\n            y : Int32\n            z : Int32\n            w : Int32\n            a : Int32\n          end\n        end\n\n        f = ->{\n          s = LibMylib::Struct.new\n          s.x = 1\n          s.y = 2\n          s.z = 3\n          s.w = 4\n          s.a = 5\n          s\n        }\n\n        s = f.call\n        s.x &+ s.y &+ s.z &+ s.w &+ s.a\n        CRYSTAL\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/compiler/codegen/fun_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: fun\" do\n  it \"sets external linkage by default\" do\n    mod = codegen(<<-CRYSTAL, inject_primitives: false, single_module: false)\n    fun foo; end\n    fun __crystal_foo; end\n    CRYSTAL\n    mod.functions[\"foo\"].linkage.should eq(LLVM::Linkage::External)\n    mod.functions[\"__crystal_foo\"].linkage.should eq(LLVM::Linkage::External)\n  end\n\n  it \"sets internal linkage to __crystal_ funs when compiling to single module\" do\n    mod = codegen(<<-CRYSTAL, inject_primitives: false, single_module: true)\n    fun foo; end\n    fun __crystal_foo; end\n    CRYSTAL\n    mod.functions[\"foo\"].linkage.should eq(LLVM::Linkage::External)\n    mod.functions[\"__crystal_foo\"].linkage.should eq(LLVM::Linkage::Internal)\n  end\n\n  it \"defines same fun 3 or more times (#15523)\" do\n    run(<<-CRYSTAL, Int32).should eq(3)\n      fun foo : Int32\n        1\n      end\n\n      fun foo : Int32\n        2\n      end\n\n      fun foo : Int32\n        3\n      end\n\n      foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/generic_class_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: generic class type\" do\n  it \"codegens inherited generic class instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def x\n          @x &+ 1\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new(1).x\n      CRYSTAL\n  end\n\n  it \"instantiates generic class with default argument in initialize (#394)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo(T)\n        def initialize(@x = 1)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x &+ 1\n      CRYSTAL\n  end\n\n  it \"allows initializing instance variable (#665)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class SomeType(T)\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      SomeType(Char).new.x\n      CRYSTAL\n  end\n\n  it \"allows initializing instance variable in inherited generic type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo(T)\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        @y = 2\n      end\n\n      Bar(Char).new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var with virtual T (#1675)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Generic(T)\n        def initialize\n          @value = uninitialized T\n        end\n\n        def value=(@value)\n        end\n\n        def value\n          @value\n        end\n      end\n\n      generic = Generic(Foo).new\n      generic.value = Foo.new\n      generic.value.foo\n      CRYSTAL\n  end\n\n  it \"runs generic instance var initializers in superclass's metaclass context (#4753)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Bar(T)\n        def x\n          {% if T == Int32 %} 1 {% else %} 2 {% end %}\n        end\n      end\n\n      class FooBase(T)\n        @bar = Bar(T).new\n\n        def bar\n          @bar\n        end\n      end\n\n      class Foo(T) < FooBase(T)\n      end\n\n      Foo(Int32).new.bar.x\n      CRYSTAL\n  end\n\n  it \"runs generic instance var initializers in superclass's metaclass context (2) (#6482)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Bar(T)\n        def x\n          {% if T == FooBase(Int32) %} 1 {% else %} 2 {% end %}\n        end\n      end\n\n      class FooBase(T)\n        @bar = Bar(FooBase(T)).new\n\n        def bar\n          @bar\n        end\n      end\n\n      class Foo(T) < FooBase(T)\n      end\n\n      Foo(Int32).new.bar.x\n      CRYSTAL\n  end\n\n  it \"doesn't run generic instance var initializers in formal superclass's context (#4753)\" do\n    run(<<-CRYSTAL).to_i.should eq(7)\n      class Foo(T)\n        @foo = T.new\n\n        def foo\n          @foo\n        end\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz\n        def baz\n          7\n        end\n      end\n\n      Bar(Baz).new.foo.baz\n      CRYSTAL\n  end\n\n  it \"codegens static array size after instantiating\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct StaticArray(T, N)\n        def size\n          N\n        end\n      end\n\n      alias Foo = Int32[3]\n\n      x = uninitialized Int32[3]\n      x.size\n      CRYSTAL\n  end\n\n  it \"inherited instance var initialize from generic to concrete (#2128)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo(T)\n        @x = 42\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"inherited instance var initialize from generic to generic to concrete (#2128)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo(T)\n        @x = 10\n\n        def x\n          @x\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        @y = 32\n\n        def y\n          @y\n        end\n      end\n\n      class Baz < Bar(Int32)\n      end\n\n      baz = Baz.new\n      baz.x &+ baz.y\n      CRYSTAL\n  end\n\n  it \"invokes super in generic class (#2354)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Global\n        @@x = 1\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        def foo\n          Global.x = 2\n        end\n      end\n\n      class Bar(T) < Foo\n        def foo\n          super\n        end\n      end\n\n      b = Bar(Int32).new\n      b.foo\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"uses big integer as generic type argument (#2353)\" do\n    run(<<-CRYSTAL).to_u64.should eq(2374623294237463578)\n      require \"prelude\"\n\n      MIN_RANGE = -2374623294237463578\n      MAX_RANGE = -MIN_RANGE\n\n      class Hello(T)\n        def self.t\n          T\n        end\n      end\n\n      Hello(MAX_RANGE).t\n      CRYSTAL\n  end\n\n  it \"doesn't use virtual + in type arguments (#2839)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Gen(Foo)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Gen(T)\n      end\n\n      Gen(Foo).name\n      CRYSTAL\n  end\n\n  it \"doesn't use virtual + in type arguments for Tuple (#2839)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Tuple(Foo)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Gen(T)\n      end\n\n      Tuple(Foo).name\n      CRYSTAL\n  end\n\n  it \"doesn't use virtual + in type arguments for NamedTuple (#2839)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"NamedTuple(x: Foo)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Gen(T)\n      end\n\n      NamedTuple(x: Foo).name\n      CRYSTAL\n  end\n\n  it \"codegens virtual generic metaclass macro method call\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar(Int32)\")\n      class Class\n        def name : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      Bar(Int32).new.as(Foo(Int32)).class.name\n      CRYSTAL\n  end\n\n  it \"recomputes two calls that look the same due to generic type being instantiated (#7728)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hello\")\n      require \"prelude\"\n\n      abstract class Base\n      end\n\n      class Gen(T) < Base\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      def foo(gen)\n        gen.x\n        gen.x\n      end\n\n      foo(Gen.new(1) || Gen.new(1.5))\n      foo(Gen.new(true) || Gen.new(1_u8))\n      foo(Gen.new(\"hello\") || Gen.new('z')).as(String)\n      CRYSTAL\n  end\n\n  it \"doesn't consider abstract types for including types (#7200)\" do\n    codegen(<<-CRYSTAL)\n      module Moo\n      end\n\n      abstract class Foo(T)\n        include Moo\n\n        def foo\n          bar\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        def bar\n        end\n      end\n\n      Bar(Int32).new.as(Moo).foo\n      CRYSTAL\n  end\n\n  it \"doesn't consider abstract generic instantiation when restricting type (#5190)\" do\n    codegen(<<-CRYSTAL)\n      abstract class Foo(E)\n        abstract def foo\n      end\n\n      abstract class Bar(E) < Foo(E)\n      end\n\n      class Baz(E) < Bar(E)\n        def foo\n        end\n      end\n\n      ptr = Pointer(Foo(String)).malloc(1_u64)\n\n      Baz(String).new\n\n      x = ptr.value\n      if x.is_a?(Bar)\n        x.foo\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash on generic type restriction with initially no subtypes (#8411)\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n      end\n\n      class Baz(T) < Foo\n        def baz\n        end\n      end\n\n      def x(z)\n      end\n\n      f = uninitialized Foo\n      if f.is_a?(Baz)\n        x(f.baz)\n      end\n\n      Baz(Int32).new\n      CRYSTAL\n  end\n\n  it \"doesn't crash on generic type restriction with no subtypes (#7583)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Baz(T) < Foo\n        def baz\n        end\n      end\n\n      def x(z)\n      end\n\n      f = uninitialized Foo\n      if f.is_a?(Baz)\n        x(f.baz)\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't override guessed instance var in generic type if already declared in superclass (#9431)\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        @x = 0\n      end\n\n      class Bar(T) < Foo\n        @x = 0\n      end\n\n      class Baz < Bar(Int32)\n        @valid = true\n      end\n\n      Baz.new\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, def context (#10834)\" do\n    run(<<-CRYSTAL, Int32).should eq(7)\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        def t\n          T\n        end\n      end\n\n      Bar(7).new.t\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, metaclass context\" do\n    run(<<-CRYSTAL, Int32).should eq(7)\n      struct Int32\n        def self.new(x : Int32)\n          x\n        end\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        @x = T.new(7)\n      end\n\n      Bar(Int32).new.@x\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, macro context\" do\n    run(<<-CRYSTAL, Int32).should eq(1)\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        def t\n          {{ T == 7 ? 1 : 2 }}\n        end\n      end\n\n      Bar(7).new.t\n      CRYSTAL\n  end\n\n  it \"codegens compile-time interpreted generic int128\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      require \"prelude\"\n\n      CONST = 1_i128 + 2_i128\n      class Foo(T)\n        def initialize()\n        end\n\n        def t_incr\n          T + 1\n        end\n      end\n\n      class Bar < Foo(CONST)\n      end\n\n      Bar.new.t_incr\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/hash_literal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: hash literal spec\" do\n  it \"creates custom non-generic hash\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      class Custom\n        def initialize\n          @keys = 0\n          @values = 0\n        end\n\n        def []=(key, value)\n          @keys &+= key\n          @values &+= value\n        end\n\n        def keys\n          @keys\n        end\n\n        def values\n          @values\n        end\n      end\n\n      custom = Custom {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"creates custom generic hash\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      class Custom(K, V)\n        def initialize\n          @keys = 0\n          @values = 0\n        end\n\n        def []=(key, value)\n          @keys &+= key\n          @values &+= value\n        end\n\n        def keys\n          @keys\n        end\n\n        def values\n          @values\n        end\n      end\n\n      custom = Custom {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"creates custom generic hash with type vars\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      class Custom(K, V)\n        def initialize\n          @keys = 0\n          @values = 0\n        end\n\n        def []=(key, value)\n          @keys &+= key\n          @values &+= value\n        end\n\n        def keys\n          @keys\n        end\n\n        def values\n          @values\n        end\n      end\n\n      custom = Custom(Int32, Int32) {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"creates custom generic hash via alias (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      class Custom(K, V)\n        def initialize\n          @keys = 0\n          @values = 0\n        end\n\n        def []=(key, value)\n          @keys &+= key\n          @values &+= value\n        end\n\n        def keys\n          @keys\n        end\n\n        def values\n          @values\n        end\n      end\n\n      alias MyCustom = Custom\n\n      custom = MyCustom {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"creates custom generic hash via alias (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      class Custom(K, V)\n        def initialize\n          @keys = 0\n          @values = 0\n        end\n\n        def []=(key, value)\n          @keys &+= key\n          @values &+= value\n        end\n\n        def keys\n          @keys\n        end\n\n        def values\n          @values\n        end\n      end\n\n      alias MyCustom = Custom(Int32, Int32)\n\n      custom = MyCustom {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"doesn't crash on hash literal with proc pointer (#646)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      def blah\n        1\n      end\n\n      b = {\"a\" => ->blah}\n      b[\"a\"].call\n      CRYSTAL\n  end\n\n  it \"creates custom non-generic hash in module\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      module Moo\n        class Custom\n          def initialize\n            @keys = 0\n            @values = 0\n          end\n\n          def []=(key, value)\n            @keys &+= key\n            @values &+= value\n          end\n\n          def keys\n            @keys\n          end\n\n          def values\n            @values\n          end\n        end\n      end\n\n      custom = Moo::Custom {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"creates custom generic hash in module (#5684)\" do\n    run(<<-CRYSTAL).to_i.should eq(90)\n      module Moo\n        class Custom(K, V)\n          def initialize\n            @keys = 0\n            @values = 0\n          end\n\n          def []=(key, value)\n            @keys &+= key\n            @values &+= value\n          end\n\n          def keys\n            @keys\n          end\n\n          def values\n            @values\n          end\n        end\n      end\n\n      custom = Moo::Custom {1 => 10, 2 => 20}\n      custom.keys &* custom.values\n      CRYSTAL\n  end\n\n  it \"assignment in hash literal works\" do\n    run(\"require \\\"prelude\\\"; {k = 1 => v = 2}; k + v\").to_i.should eq(3)\n  end\n\n  it \"assignment in hash-like literal works\" do\n    run(\"require \\\"prelude\\\"; Hash(Int32, Int32){k = 1 => v = 2}; k + v\").to_i.should eq(3)\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/hooks_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: hooks\" do\n  it \"does inherited macro\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        macro inherited\n          @@x = 1\n\n          def self.x\n            @@x\n          end\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.x\n      CRYSTAL\n  end\n\n  it \"does included macro\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Foo\n        macro included\n          @@x = 1\n\n          def self.x\n            @@x\n          end\n        end\n      end\n\n      class Bar\n        include Foo\n      end\n\n      Bar.x\n      CRYSTAL\n  end\n\n  it \"does extended macro\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Foo\n        macro extended\n          @@x = 1\n\n          def self.x\n            @@x\n          end\n        end\n      end\n\n      class Bar\n        extend Foo\n      end\n\n      Bar.x\n      CRYSTAL\n  end\n\n  it \"does added method macro\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        macro method_added(d)\n          Global.x = 1\n        end\n\n        def foo; end\n      end\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"does inherited macro recursively\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        macro inherited\n          Global.x &+= 1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Bar\n      end\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"does inherited macro before class body\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      class Global\n        @@x = 123\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        macro inherited\n          @@y : Int32 = Global.x\n\n          def self.y\n            @@y\n          end\n        end\n      end\n\n      class Bar < Foo\n        Global.x &+= 1\n      end\n\n      Bar.y\n      CRYSTAL\n  end\n\n  it \"does finished\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      class Foo\n        A = [1]\n\n        macro finished\n          {% A[0] = A[0] + 1 %}\n        end\n\n        macro finished\n          {% A[0] = A[0] * 2 %}\n        end\n\n        macro finished\n          def self.foo\n            {{ A[0] }}\n          end\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"fixes empty types in hooks (#3946)\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun exit(x : Int32) : NoReturn\n      end\n\n      def bar(x)\n      end\n\n      module Moo\n        def foo\n          bar(moo)\n        end\n      end\n\n      class Moo1\n        include Moo\n\n        def moo\n          0\n        end\n      end\n\n      class Moo2\n        include Moo\n\n        def moo\n          LibC.exit(1)\n        end\n      end\n\n      class Foo\n        macro inherited\n          io = uninitialized Moo\n          io.foo\n        end\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/if_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: if\" do\n  it \"codegens if without an else with true\" do\n    run(\"a = 1; if true; a = 2; end; a\").to_i.should eq(2)\n  end\n\n  it \"codegens if without an else with false\" do\n    run(\"a = 1; if false; a = 2; end; a\").to_i.should eq(1)\n  end\n\n  it \"codegens if with an else with false\" do\n    run(\"a = 1; if false; a = 2; else; a = 3; end; a\").to_i.should eq(3)\n  end\n\n  it \"codegens if with an else with true\" do\n    run(\"a = 1; if true; a = 2; else; a = 3; end; a\").to_i.should eq(2)\n  end\n\n  it \"codegens if inside def without an else with true\" do\n    run(\"def foo; a = 1; if true; a = 2; end; a; end; foo\").to_i.should eq(2)\n  end\n\n  it \"codegen if inside if\" do\n    run(\"a = 1; if false; a = 1; elsif false; a = 2; else; a = 3; end; a\").to_i.should eq(3)\n  end\n\n  it \"codegens if value from then\" do\n    run(\"if true; 1; else 2; end\").to_i.should eq(1)\n  end\n\n  it \"codegens if with union\" do\n    run(\"a = if true; 2.5_f32; else; 1; end; a.to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codes if with two whiles\" do\n    run(\"if true; while false; end; else; while false; end; end\")\n  end\n\n  it \"codegens if with int\" do\n    run(\"if 1; 2; else 3; end\").to_i.should eq(2)\n  end\n\n  it \"codegens if with nil\" do\n    run(\"require \\\"nil\\\"; if nil; 2; else 3; end\").to_i.should eq(3)\n  end\n\n  it \"codegens if of nilable type in then\" do\n    run(\"if false; nil; else; \\\"foo\\\"; end\").to_string.should eq(\"foo\")\n  end\n\n  it \"codegens if of nilable type in then 2\" do\n    run(\"if 1 == 2; nil; else; \\\"foo\\\"; end\").to_string.should eq(\"foo\")\n  end\n\n  it \"codegens if of nilable type in else\" do\n    run(\"if true; \\\"foo\\\"; else; nil; end\").to_string.should eq(\"foo\")\n  end\n\n  it \"codegens if of nilable type in else 3\" do\n    run(\"if 1 == 1; \\\"foo\\\"; else; nil; end\").to_string.should eq(\"foo\")\n  end\n\n  it \"codegens if with return and no else\" do\n    run(\"def foo; if true; return 1; end; 2; end; foo\").to_i.should eq(1)\n  end\n\n  it \"codegens if with return in both branches\" do\n    run(\"def foo; if true; return 1; else; return 2; end; end; foo\").to_i.should eq(1)\n  end\n\n  it \"codegen if with nested if that returns\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        if true\n          if true\n            return 1\n          else\n            return 2\n          end\n        end\n        0\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegen if with union type and then without type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        if true\n          return 1\n        else\n          1 || 1.1\n        end\n        return 0\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegen if with union type and else without type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        if false\n          1 || 1.1\n        else\n          return 1\n        end\n        return 0\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens if with virtual\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.new || Bar.new\n      if f\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens nested if with var (ssa bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      foo = 1\n      if 1 == 2\n        if 1 == 2\n          foo = 2\n        else\n          foo = 3\n        end\n      end\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens if with nested if that raises\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      struct Nil; def to_i; 0; end; end\n\n      block = 1 || nil\n      if 1 == 2\n        if block\n          raise \"Oh no\"\n        end\n      else\n        block\n      end.to_i\n      CRYSTAL\n  end\n\n  it \"codegens if with return in else preserves type filter\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      def foo\n        x = 1 || nil\n        if x\n        else\n          return 0\n        end\n\n        x + 1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens bug #1729\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      n = true ? 3 : 3.2\n      z = if n.is_a?(Float64) || false\n        0\n      else\n        n\n      end\n      z.to_i!\n      CRYSTAL\n  end\n\n  {% if flag?(:bits64) %}\n    it \"codegens if with pointer 0x100000000 pointer\" do\n      run(<<-CRYSTAL).to_i.should eq(1)\n        ptr = Pointer(Void).new(0x100000000_u64)\n        if ptr\n          1\n        else\n          2\n        end\n        CRYSTAL\n    end\n  {% end %}\n\n  it \"doesn't crash with if !var using var in else\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      foo = nil\n      if !foo\n        1\n      else\n        foo\n      end\n      1\n      CRYSTAL\n  end\n\n  it \"doesn't crash with if !is_a? using var in then\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      foo = 1\n      if !foo.is_a?(Int32)\n        foo\n      else\n        1\n      end\n      1\n      CRYSTAL\n  end\n\n  it \"restricts with || always falsey\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      t = 1\n      if t.is_a?(String) || t.is_a?(String)\n        t\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"considers or truthy/falsey right\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      t = 1 || 'a'\n      if t.is_a?(Char) || t.is_a?(Char)\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens #3104\" do\n    codegen(<<-CRYSTAL)\n      def foo\n        yield\n      end\n\n      x = typeof(nil && 1)\n      foo do\n        if x\n        end\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"doesn't generate truthy if branch if doesn't need value (bug)\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n      end\n\n      x = nil\n      if x\n        nil\n      else\n        if 2 == 2\n          Foo.new\n        else\n          \"\"\n        end\n      end\n      1\n      CRYSTAL\n  end\n\n  it \"doesn't crash no NoReturn var (true left cond) (#1823)\" do\n    codegen(<<-CRYSTAL)\n      def foo\n        arg = nil\n        if true || arg.nil?\n          return\n        end\n\n        arg\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't crash no NoReturn var (non-true left cond) (#1823)\" do\n    codegen(<<-CRYSTAL)\n      def foo\n        arg = nil\n        if 1 == 2 || arg.nil?\n          return\n        end\n\n        arg\n      end\n\n      foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/is_a_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: is_a?\" do\n  it \"codegens is_a? true for simple type\" do\n    run(\"1.is_a?(Int)\").to_b.should be_true\n  end\n\n  it \"codegens is_a? false for simple type\" do\n    run(\"1.is_a?(Bool)\").to_b.should be_false\n  end\n\n  it \"codegens is_a? with union gives true\" do\n    run(\"(1 == 1 ? 1 : 'a').is_a?(Int)\").to_b.should be_true\n  end\n\n  it \"codegens is_a? with union gives false\" do\n    run(\"(1 == 1 ? 1 : 'a').is_a?(Char)\").to_b.should be_false\n  end\n\n  it \"codegens is_a? with union gives false\" do\n    run(\"(1 == 1 ? 1 : 'a').is_a?(Float)\").to_b.should be_false\n  end\n\n  it \"codegens is_a? with union gives true\" do\n    run(\"(1 == 1 ? 1 : 'a').is_a?(Object)\").to_b.should be_true\n  end\n\n  it \"codegens is_a? with nilable gives true\" do\n    run(\"(1 == 1 ? nil : Reference.new).is_a?(Nil)\").to_b.should be_true\n  end\n\n  it \"codegens is_a? with nilable gives false because other type 1\" do\n    run(\"(1 == 1 ? nil : Reference.new).is_a?(Reference)\").to_b.should be_false\n  end\n\n  it \"codegens is_a? with nilable gives false because other type 2\" do\n    run(\"(1 == 2 ? nil : Reference.new).is_a?(Reference)\").to_b.should be_true\n  end\n\n  it \"codegens is_a? with nilable gives false because no type\" do\n    run(\"(1 == 2 ? nil : Reference.new).is_a?(String)\").to_b.should be_false\n  end\n\n  it \"codegens is_a? with nilable gives false because no type\" do\n    run(\"1.is_a?(Object)\").to_b.should be_true\n  end\n\n  it \"doesn't error if result is discarded (#14113)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      (Foo.new || \"\").is_a?(Foo)\n      1\n      CRYSTAL\n  end\n\n  it \"evaluate method on filtered type\" do\n    run(\"a = 1; a = 'a'; if a.is_a?(Char); a.ord; else; 0; end\").to_i.chr.should eq('a')\n  end\n\n  it \"evaluate method on filtered type nilable type not-nil\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      a = nil\n      a = Foo.new\n      if a.is_a?(Foo)\n        a.foo\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"evaluate method on filtered type nilable type nil\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def foo\n          1\n        end\n      end\n\n      class Foo\n      end\n\n      a = Foo.new\n      a = nil\n      if a.is_a?(Nil)\n        a.foo\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"evaluates method on filtered union type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize(x : Int32)\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      a = 1\n      a = Foo.new(2)\n\n      if a.is_a?(Reference)\n        a.x\n      else\n        0\n      end\n      CRYSTAL\n  end\n\n  it \"evaluates method on filtered union type 2\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize(x : Int32)\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def initialize(x : Int32)\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      a = 1\n      a = Foo.new(2)\n      a = Bar.new(3)\n\n      if a.is_a?(Reference)\n        a.x\n      else\n        0\n      end\n      CRYSTAL\n  end\n\n  it \"evaluates method on filtered union type 3\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      require \"prelude\"\n      a = 1\n      a = [1.1]\n      a = [5]\n\n      if a.is_a?(Enumerable)\n        a[0]\n      else\n        0\n      end.to_i\n      CRYSTAL\n  end\n\n  it \"codegens when is_a? is always false but properties are used\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      require \"prelude\"\n\n      class Foo\n        def obj; 1 end\n      end\n\n      foo = 1\n      foo.is_a?(Foo) && foo.obj && foo.obj\n      CRYSTAL\n  end\n\n  it \"codegens is_a? on right side of and\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def bar\n          true\n        end\n      end\n\n      foo = Foo.new || nil\n      if 1 == 1 && foo.is_a?(Foo) && foo.bar\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens is_a? with virtual\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Bar.new || Foo.new\n      foo.is_a?(Bar) ? 1 : 2\n      CRYSTAL\n  end\n\n  it \"codegens is_a? with virtual and nil\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.new || Bar.new || nil\n      f.is_a?(Foo) ? 1 : 2\n      CRYSTAL\n  end\n\n  it \"codegens is_a? with virtual and module\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Bar\n      end\n\n      abstract class FooBase2\n      end\n\n      abstract class FooBase < FooBase2\n        include Bar\n      end\n\n      class Foo < FooBase\n      end\n\n      class Foo2 < FooBase2\n      end\n\n      f = Foo.new || Foo2.new\n      f.is_a?(Bar)\n      CRYSTAL\n  end\n\n  it \"restricts simple type with union\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      a = 1\n      if a.is_a?(Int32 | Char)\n        a &+ 1\n      else\n        0\n      end\n      CRYSTAL\n  end\n\n  it \"restricts union with union\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct Char\n        def &+(other : Int32)\n          other\n        end\n      end\n\n      struct Bool\n        def foo\n          2\n        end\n      end\n\n      a = 1 || 'a' || false\n      if a.is_a?(Int32 | Char)\n        a &+ 2\n      else\n        a.foo\n      end\n      CRYSTAL\n  end\n\n  it \"codegens is_a? with a Const does comparison and gives true\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n      CONST = 1\n      1.is_a?(CONST)\n      CRYSTAL\n  end\n\n  it \"codegens is_a? with a Const does comparison and gives false\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      require \"prelude\"\n      CONST = 1\n      2.is_a?(CONST)\n      CRYSTAL\n  end\n\n  it \"gives false if generic type doesn't match exactly\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo(T)\n      end\n\n      foo = Foo(Int32 | Float64).new\n      foo.is_a?(Foo(Int32)) ? 1 : 2\n      CRYSTAL\n  end\n\n  it \"does is_a? with more strict virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      f = Bar.new || Foo.new\n      if f.is_a?(Bar)\n        f.foo\n      else\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"codegens is_a? casts union to nilable\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo; end\n\n      var = \"hello\" || Foo.new || nil\n      if var.is_a?(Foo | Nil)\n        var2 = var\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens is_a? casts union to nilable in method\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo; end\n\n      def foo(var)\n        if var.is_a?(Foo | Nil)\n          var2 = var\n          1\n        else\n          2\n        end\n      end\n\n      var = \"hello\" || Foo.new || nil\n      foo(var)\n      CRYSTAL\n  end\n\n  it \"codegens is_a? from virtual type to module\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Moo\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n\n      class Baz < Foo\n        include Moo\n      end\n\n      f = Bar.new || Baz.new\n      if f.is_a?(Moo)\n        g = f\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens is_a? from nilable reference union type to nil\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n      end\n\n      class Bar\n      end\n\n      a = Foo.new || Bar.new || nil\n      if a.is_a?(Nil)\n        b = a\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens is_a? from nilable reference union type to type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      class Bar\n      end\n\n      a = Foo.new || Bar.new || nil\n      if a.is_a?(Foo)\n        b = a\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"says false for value.is_a?(Class)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      1.is_a?(Class)\n      CRYSTAL\n  end\n\n  it \"restricts type in else but lazily\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(1)\n      x = foo.x\n      if x.is_a?(Int32)\n        z = x &+ 1\n      else\n        z = x.foo_bar\n      end\n\n      z\n      CRYSTAL\n  end\n\n  it \"works with inherited generic class against an instantiation\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      bar = Bar.new\n      bar.is_a?(Foo(Int32))\n      CRYSTAL\n  end\n\n  it \"doesn't work with inherited generic class against an instantiation (2)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Class1\n      end\n\n      class Class2 < Class1\n      end\n\n      class Foo(T)\n      end\n\n      class Bar < Foo(Class2)\n      end\n\n      bar = Bar.new\n      bar.is_a?(Foo(Class1))\n      CRYSTAL\n  end\n\n  it \"works with inherited generic class against an instantiation (3)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      bar = Bar.new\n      bar.is_a?(Foo(Float32))\n      CRYSTAL\n  end\n\n  it \"doesn't type merge (1) (#548)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Base; end\n      class Base1 < Base; end\n      class Base2 < Base; end\n      class Base3 < Base; end\n\n      Base3.new.is_a?(Base1 | Base2)\n      CRYSTAL\n  end\n\n  it \"doesn't type merge (2) (#548)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Base; end\n      class Base1 < Base; end\n      class Base2 < Base; end\n      class Base3 < Base; end\n\n      Base1.new.is_a?(Base1 | Base2) && Base2.new.is_a?(Base1 | Base2)\n      CRYSTAL\n  end\n\n  it \"doesn't skip assignment when used in combination with .is_a? (true case, then) (#1121)\" do\n    run(<<-CRYSTAL).to_i.should eq(124)\n      a = 123\n      if (b = a).is_a?(Int32)\n        b &+ 1\n      else\n        a\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't skip assignment when used in combination with .is_a? (true case, else) (#1121)\" do\n    run(<<-CRYSTAL).to_i.should eq(125)\n      a = 123\n      if (b = a).is_a?(Int32)\n        a &+ 2\n      else\n        b\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't skip assignment when used in combination with .is_a? (false case) (#1121)\" do\n    run(<<-CRYSTAL).to_i.should eq(124)\n      a = 123\n      if (b = a).is_a?(Char)\n        b\n      else\n        b &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't skip assignment when used in combination with .is_a? and && (#1121)\" do\n    run(<<-CRYSTAL).to_i.should eq(124)\n      a = 123\n      if (1 == 1) && (b = a).is_a?(Char)\n        b\n      else\n        a\n      end\n      b ? b &+ 1 : 0\n      CRYSTAL\n  end\n\n  it \"transforms then if condition is always truthy\" do\n    run(<<-CRYSTAL).to_i.should eq(456)\n      def foo\n        123 && 456\n      end\n\n      if 1.is_a?(Int32)\n        foo\n      else\n        999\n      end\n      CRYSTAL\n  end\n\n  it \"transforms else if condition is always falsey\" do\n    run(<<-CRYSTAL).to_i.should eq(456)\n      def foo\n        123 && 456\n      end\n\n      if 1.is_a?(Char)\n        999\n      else\n        foo\n      end\n      CRYSTAL\n  end\n\n  it \"resets truthy state after visiting nodes (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      a = 123\n      if !1.is_a?(Int32)\n        a = 456\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"does is_a? with generic class metaclass\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo(T)\n      end\n\n      Foo(Int32).is_a?(Foo.class)\n      CRYSTAL\n  end\n\n  it \"says false for GenericChild(Base).is_a?(GenericBase(Child)) (#1294)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Base\n      end\n\n      class Child < Base\n      end\n\n      class GenericBase(T)\n      end\n\n      class GenericChild(T) < GenericBase(T)\n      end\n\n      GenericChild(Base).new.is_a?(GenericBase(Child))\n      CRYSTAL\n  end\n\n  it \"does is_a?/responds_to? twice (#1451)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      a = 1 == 2 ? 1 : false\n      if a.is_a?(Int32) && a.is_a?(Int32)\n        3\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"does is_a? with && and true condition\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      a = 1 == 1 ? 1 : false\n      if a.is_a?(Int32) && 1 == 1\n        3\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"does is_a? for union of module and type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Moo\n        def moo\n          2\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar\n        include Moo\n      end\n\n      def foo(io)\n        if io.is_a?(Moo)\n          io.moo\n        else\n          3\n        end\n      end\n\n      io = Foo.new.as(Moo) || 1\n      foo(io)\n      CRYSTAL\n  end\n\n  it \"does is_a? for virtual generic instance type against generic\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      def foo(x : Bar)\n      end\n\n      Bar(Int32).new.as(Foo(Int32)).is_a?(Bar) ? 2 : 3\n      CRYSTAL\n  end\n\n  it \"does is_a?(generic type) for nested generic inheritance (1) (#9660)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true\n      class Cxx\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz < Bar(Cxx)\n      end\n\n      Baz.new.is_a?(Foo)\n      CRYSTAL\n  end\n\n  it \"does is_a?(generic type) for nested generic inheritance (2)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true\n      class Cxx\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz(T) < Bar(T)\n      end\n\n      Baz(Cxx).new.is_a?(Foo)\n      CRYSTAL\n  end\n\n  it \"does is_a?(generic type) for nested generic inheritance, through upcast (1)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true\n      class Cxx\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz < Bar(Cxx)\n      end\n\n      Baz.new.as(Foo(Cxx)).is_a?(Bar)\n      CRYSTAL\n  end\n\n  it \"does is_a?(generic type) for nested generic inheritance, through upcast (2)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true\n      class Cxx\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz(T) < Bar(T)\n      end\n\n      Baz(Cxx).new.as(Foo(Cxx)).is_a?(Bar)\n      CRYSTAL\n  end\n\n  it \"doesn't consider generic type to be a generic type of a recursive alias (#3524)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Gen(T)\n      end\n\n      alias Type = Int32 | Gen(Type)\n      a = Gen(Int32).new\n      a.is_a?(Type)\n      CRYSTAL\n  end\n\n  it \"codegens untyped var (#4009)\" do\n    codegen(<<-CRYSTAL)\n      i = 1\n      1 || i.is_a?(Int32) ? \"\" : i\n      CRYSTAL\n  end\n\n  it \"visits 1.to_s twice, may trigger enclosing_call (#4364)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      B = String\n      1.to_s.is_a?(B)\n      CRYSTAL\n  end\n\n  it \"says true for Class.is_a?(Class.class) (#4374)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      Class.is_a?(Class.class)\n      CRYSTAL\n  end\n\n  it \"says true for Class.is_a?(Class.class.class) (#4374)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      Class.is_a?(Class.class.class)\n      CRYSTAL\n  end\n\n  it \"passes is_a? with generic module type on virtual type (#10302)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Mod(T)\n      end\n\n      abstract struct Sup\n        include Mod(Sup)\n      end\n\n      struct Sub < Sup\n      end\n\n      Sub.new.is_a?(Mod(Sup))\n      CRYSTAL\n  end\n\n  it \"restricts metaclass against virtual metaclass type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class A\n      end\n\n      class B < A\n      end\n\n      x = B || A\n      if x.is_a?(B.class)\n        1\n      elsif x.is_a?(A.class)\n        2\n      else\n        3\n      end\n      CRYSTAL\n  end\n\n  it \"restricts virtual metaclass against virtual metaclass type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class A\n      end\n\n      class B < A\n      end\n\n      class C < B\n      end\n\n      x = B || A\n      if x.is_a?(B.class)\n        1\n      elsif x.is_a?(A.class)\n        2\n      else\n        3\n      end\n      CRYSTAL\n  end\n\n  it \"does is_a? with union type, don't resolve to virtual type (#10244)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class A\n      end\n\n      class B < A\n      end\n\n      class C < A\n      end\n\n      class D < A\n      end\n\n      x = D.new || C.new\n      x.is_a?(B | C)\n      CRYSTAL\n  end\n\n  it \"does is_a? with union type as Union(X, Y), don't resolve to virtual type (#10244)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class A\n      end\n\n      class B < A\n      end\n\n      class C < A\n      end\n\n      class D < A\n      end\n\n      x = D.new || C.new\n      x.is_a?(Union(B, C))\n      CRYSTAL\n  end\n\n  it \"restricts union metaclass to metaclass (#12295)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      x = true ? Union(String | Int32) : String\n      if x.is_a?(String.class)\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"does is_a? for generic type against generic class instance type (#12304)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      class A\n      end\n\n      class B(T) < A\n      end\n\n      a = B(Int32).new.as(A)\n      b = a.as(B)\n\n      b.is_a?(B(Int32))\n      CRYSTAL\n  end\n\n  it \"virtual metaclass type is not virtual instance type (#12628)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      abstract struct Base\n      end\n\n      struct Impl < Base\n      end\n\n      Base.as(Base | Base.class).is_a?(Base | Impl)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/lib_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: lib\" do\n  pending \"codegens lib var set and get\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibC\n        $errno : Int32\n      end\n\n      LibC.errno = 1\n      LibC.errno\n      CRYSTAL\n  end\n\n  it \"call to void function\" do\n    run(<<-CRYSTAL)\n      lib LibC\n        fun srand(x : UInt32) : Void\n      end\n\n      def foo\n        LibC.srand(0_u32)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"allows passing type to LibC if it has a converter with to_unsafe\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun foo(x : Int32) : Int32\n      end\n\n      class Foo\n        def to_unsafe\n          1\n        end\n      end\n\n      LibC.foo Foo.new\n      CRYSTAL\n  end\n\n  it \"allows passing type to LibC if it has a converter with to_unsafe (bug)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        fun foo(x : UInt8*)\n      end\n\n      def foo\n        yield 1\n      end\n\n      LibC.foo(foo &.to_s)\n      CRYSTAL\n  end\n\n  it \"allows setting/getting external variable as function pointer\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        $x : ->\n      end\n\n      LibC.x = ->{}\n      LibC.x.call\n      CRYSTAL\n  end\n\n  it \"can use enum as fun argument\" do\n    codegen(<<-CRYSTAL)\n      enum Foo\n        A\n      end\n\n      lib LibC\n        fun foo(x : Foo)\n      end\n\n      LibC.foo(Foo::A)\n      CRYSTAL\n  end\n\n  it \"can use enum as fun return\" do\n    codegen(<<-CRYSTAL)\n      enum Foo\n        A\n      end\n\n      lib LibC\n        fun foo : Foo\n      end\n\n      LibC.foo\n      CRYSTAL\n  end\n\n  it \"can use tuple as fun return\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3))\n      struct s {\n        int x;\n        int y;\n      };\n\n      struct s foo() {\n        struct s a = {1, 2};\n        return a;\n      }\n      C\n      lib LibFoo\n        fun foo : {Int32, Int32}\n      end\n\n      tuple = LibFoo.foo\n      tuple[0] + tuple[1]\n      CRYSTAL\n  end\n\n  it \"get fun field from struct (#672)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      lib Moo\n        struct Type\n          func : (Type*) -> Int32\n        end\n      end\n\n      p = Pointer(Moo::Type).malloc(1)\n      p.value.func = -> (t: Moo::Type*) { 10 }\n      p.value.func.call(p)\n      CRYSTAL\n  end\n\n  it \"get fun field from union (#672)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      lib Moo\n        union Type\n          func : (Type*) -> Int32\n        end\n      end\n\n      p = Pointer(Moo::Type).malloc(1)\n      p.value.func = -> (t: Moo::Type*) { 10 }\n      p.value.func.call(p)\n      CRYSTAL\n  end\n\n  it \"refers to lib type (#960)\" do\n    codegen(<<-CRYSTAL)\n      lib Thing\n      end\n\n      Thing\n      CRYSTAL\n  end\n\n  it \"allows invoking out with underscore \" do\n    codegen(<<-CRYSTAL)\n      lib Lib\n        fun foo(x : Int32*) : Float64\n      end\n\n      Lib.foo out _\n      CRYSTAL\n  end\n\n  it \"passes int as another float type in literal\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo(x : Int32)\n      end\n\n      LibFoo.foo 1234.5\n      CRYSTAL\n  end\n\n  it \"passes nil to varargs (#1570)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo(...)\n      end\n\n      LibFoo.foo(nil)\n      CRYSTAL\n  end\n\n  it \"casts C fun to Crystal proc when accessing instance var (#2515)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibFoo\n        struct Some\n          x : ->\n        end\n      end\n\n      LibFoo::Some.new.to_s\n      CRYSTAL\n  end\n\n  it \"doesn't crash when casting -1 to UInt32 (#3594)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo(x : UInt32) : Nil\n      end\n\n      LibFoo.foo(-1)\n      CRYSTAL\n  end\n\n  it \"doesn't crash with nil and varargs (#4414)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo(Void*, ...)\n      end\n\n      x = nil\n      LibFoo.foo(x)\n      CRYSTAL\n  end\n\n  it \"uses static array in lib extern (#5688)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        $x : Int32[10]\n      end\n\n      LibFoo.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/macro_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: macro\" do\n  it \"expands macro\" do\n    run(\"macro foo; 1 &+ 2; end; foo\").to_i.should eq(3)\n  end\n\n  it \"expands macro with arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro foo(n)\n        {{n}} &+ 2\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"expands macro that invokes another macro\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro foo\n        def x\n          1 &+ 2\n        end\n      end\n\n      macro bar\n        foo\n      end\n\n      bar\n      x\n      CRYSTAL\n  end\n\n  it \"expands macro defined in class\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        macro foo\n          def bar\n            1\n          end\n        end\n\n        foo\n      end\n\n      foo = Foo.new\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"expands macro defined in base class\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Object\n        macro foo\n          def bar\n            1\n          end\n        end\n      end\n\n      class Foo\n        foo\n      end\n\n      foo = Foo.new\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"expands inline macro\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = {{ 1 }}\n      a\n      CRYSTAL\n  end\n\n  it \"expands inline macro for\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      a = 0\n      {% for i in [1, 2, 3] %}\n        a &+= {{i}}\n      {% end %}\n      a\n      CRYSTAL\n  end\n\n  it \"expands inline macro if (true)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 0\n      {% if 1 == 1 %}\n        a &+= 1\n      {% end %}\n      a\n      CRYSTAL\n  end\n\n  it \"expands inline macro if (false)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      a = 0\n      {% if 1 == 2 %}\n        a &+= 1\n      {% end %}\n      a\n      CRYSTAL\n  end\n\n  it \"finds macro in class\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        macro foo\n          1 &+ 2\n        end\n\n        def bar\n          foo\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"expands def macro\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def bar_baz\n        1\n      end\n\n      def foo : Int32\n        {% begin %}\n          bar_{{ \"baz\".id }}\n        {% end %}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"expands def macro with var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo : Int32\n          {{ @type }}\n          a = {{ 1 }}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"expands def macro with @type.instance_vars\" do\n    run(<<-CRYSTAL).to_string.should eq(\"x\")\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def to_s : String\n          {{ @type.instance_vars.first.stringify }}\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.to_s\n      CRYSTAL\n  end\n\n  it \"expands def macro with @type.instance_vars with subclass\" do\n    run(<<-CRYSTAL).to_string.should eq(\"y\")\n      class Reference\n        def to_s : String\n          {{ @type.instance_vars.last.stringify }}\n        end\n      end\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(@x : Int32, @y : Int32)\n        end\n      end\n\n      Bar.new(1, 2).to_s\n      CRYSTAL\n  end\n\n  it \"expands def macro with @type.instance_vars with virtual\" do\n    run(<<-CRYSTAL).to_string.should eq(\"y\")\n      class Reference\n        def to_s : String\n          {{ @type.instance_vars.last.stringify }}\n        end\n      end\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(@x : Int32, @y : Int32)\n        end\n      end\n\n      (Bar.new(1, 2) || Foo.new(1)).to_s\n      CRYSTAL\n  end\n\n  it \"expands def macro with @type.name\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def to_s : String\n          {{@type.name.stringify}}\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.to_s\n      CRYSTAL\n  end\n\n  it \"expands macro and resolves type correctly\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo : Int32\n          {{ @type }}\n          1\n        end\n      end\n\n      class Bar < Foo\n        Int32 = 2\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"expands def macro with @type.name with virtual\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar\")\n      class Reference\n        def to_s : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      (Bar.new || Foo.new).to_s\n      CRYSTAL\n  end\n\n  it \"expands def macro with @type.name with virtual (2)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      class Reference\n        def to_s : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      (Foo.new || Bar.new).to_s\n      CRYSTAL\n  end\n\n  it \"allows overriding macro definition when redefining base class\" do\n    run(<<-CRYSTAL).to_string.should eq(\"OH NO\")\n      class Foo\n        def inspect : String\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Foo\n        def inspect\n          \"OH NO\"\n        end\n      end\n\n      Bar.new.inspect\n      CRYSTAL\n  end\n\n  it \"uses invocation context\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      macro foo\n        def bar\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Foo\n        foo\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"allows macro with default arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def bar\n        2\n      end\n\n      macro foo(x, y = :bar)\n        {{x}} &+ {{y.id}}\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"expands def macro with instance var and method call (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      class Foo\n        @name : Int32?\n\n        def foo : Int32\n          {{ @type }}\n          name = 1\n          @name = name\n        end\n      end\n\n      Foo.new.foo.to_i!\n      CRYSTAL\n  end\n\n  it \"expands @type.name in virtual metaclass (1)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      class Class\n        def to_s : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      p = Pointer(Foo.class).malloc(1_u64)\n      p.value = Bar\n      p.value = Foo\n      p.value.to_s\n      CRYSTAL\n  end\n\n  it \"expands @type.name in virtual metaclass (2)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar\")\n      class Class\n        def to_s : String\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      p = Pointer(Foo.class).malloc(1_u64)\n      p.value = Foo\n      p.value = Bar\n      p.value.to_s\n      CRYSTAL\n  end\n\n  it \"doesn't skip abstract classes when defining macro methods\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Object\n        def foo : Int32\n          {{ @type }}\n          1\n        end\n      end\n\n      class Type\n      end\n\n      class ModuleType < Type\n        def foo\n          2\n        end\n      end\n\n      class Type1 < ModuleType\n      end\n\n      class Type2 < Type\n      end\n\n      t = Type1.new || Type2.new\n      t.foo\n      CRYSTAL\n  end\n\n  it \"doesn't reuse macro nodes (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Float\n        def &+(other)\n          self + other\n        end\n      end\n\n      def foo(x)\n        {% for y in [1, 2] %}\n          x &+ 1\n        {% end %}\n      end\n\n      foo 1\n      foo(1.5).to_i!\n      CRYSTAL\n  end\n\n  it \"can use constants\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      CONST = 1\n      {{ CONST }}\n      CRYSTAL\n  end\n\n  it \"can refer to types\" do\n    run(<<-CRYSTAL).to_string.should eq(\"y\")\n      class Foo\n        def initialize(@x : Int32, @y : Int32)\n        end\n\n        def foo : String\n          {{ @type }}\n          {{ Foo.instance_vars.last.name.stringify }}\n        end\n\n      end\n\n      Foo.new(1, 2).foo\n      CRYSTAL\n  end\n\n  it \"runs macro with splat\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro foo(*args)\n        {{args.size}}\n      end\n\n      foo 1, 1, 1\n      CRYSTAL\n  end\n\n  it \"runs macro with arg and splat\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro foo(name, *args)\n        {{args.size}}\n      end\n\n      foo bar, 1, 1, 1\n      CRYSTAL\n  end\n\n  it \"expands macro that yields\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo\n        {% for i in 0 .. 2 %}\n          yield {{i}}\n        {% end %}\n      end\n\n      a = 0\n      foo do |x|\n        a &+= x\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"can refer to abstract (1)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo\n      end\n\n      {{ Foo.abstract? }}\n      CRYSTAL\n  end\n\n  it \"can refer to abstract (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      abstract class Foo\n      end\n\n      {{ Foo.abstract? }}\n      CRYSTAL\n  end\n\n  it \"can refer to @type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      class Foo\n        def foo : String\n          {{@type.name.stringify}}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"can refer to union (1)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      {{Int32.union?}}\n      CRYSTAL\n  end\n\n  it \"can refer to union (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n        def initialize\n          @x = 1; @x = 1.1\n        end\n        def foo\n          {{ @type.instance_vars.first.type.union? }}\n        end\n      end\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"can iterate union types\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Float64-Int32\")\n      class Foo\n        def initialize\n          @x = 1; @x = 1.1\n        end\n        def foo\n          {{ @type.instance_vars.first.type.union_types.map(&.name).sort.join(\"-\") }}\n        end\n      end\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"can access type variables\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Int32\")\n      class Foo(T)\n        def foo\n          {{ @type.type_vars.first.name.stringify }}\n        end\n      end\n      Foo(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"can access type variables of a module\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Int32\")\n      module Foo(T)\n        def self.foo\n          {{ @type.type_vars.first.name.stringify }}\n        end\n      end\n      Foo(Int32).foo\n      CRYSTAL\n  end\n\n  it \"can access type variables that are not types\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo(T)\n        def foo\n          {{ @type.type_vars.first.is_a?(NumberLiteral) }}\n        end\n      end\n      Foo(1).new.foo\n      CRYSTAL\n  end\n\n  it \"can access type variables of a tuple\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Int32\")\n      struct Tuple\n        def foo\n          {{ @type.type_vars.first.name.stringify }}\n        end\n      end\n      {1, 2, 3}.foo\n      CRYSTAL\n  end\n\n  it \"can access type variables of a generic type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"T-K\")\n      class Foo(T, K)\n        def self.foo : String\n          {{ @type.type_vars.map(&.stringify).join(\"-\") }}\n        end\n      end\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"receives &block\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      macro foo(&block)\n        bar {{block}}\n      end\n\n      def bar\n        yield 1\n      end\n\n      foo do |x|\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"executes with named arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro foo(x = 1)\n        {{x}} &+ 1\n      end\n\n      foo x: 2\n      CRYSTAL\n  end\n\n  it \"gets correct class name when there are classes in the middle\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Qux\")\n      class Foo\n        def class_desc : String\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Bar\n      end\n\n      class Qux < Bar\n      end\n\n      a = Pointer(Foo).malloc(1_u64)\n      a.value = Qux.new\n      a.value.class_desc\n      CRYSTAL\n  end\n\n  it \"transforms hooks (bug)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      module GC\n        def self.add_finalizer(object : T)\n          object.responds_to?(:finalize)\n        end\n      end\n\n      abstract class Foo\n        ALL = Pointer(Foo).malloc(1_u64)\n\n        macro inherited\n          ALL.value = new\n        end\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n  end\n\n  it \"executes subclasses\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar-Baz\")\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      class Qux < Baz\n      end\n\n      {{ Foo.subclasses.map(&.name).join(\"-\") }}\n      CRYSTAL\n  end\n\n  it \"executes all_subclasses\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar-Baz\")\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Bar\n      end\n\n      {{ Foo.all_subclasses.map(&.name).join(\"-\") }}\n      CRYSTAL\n  end\n\n  it \"gets enum members with @type.constants\" do\n    run(<<-CRYSTAL).to_i.should eq(0 + 1 + 2)\n      enum Color\n        Red\n        Green\n        Blue\n\n        def self.red\n          {{@type.constants[0]}}\n        end\n\n        def self.green\n          {{@type.constants[1]}}\n        end\n\n        def self.blue\n          {{@type.constants[2]}}\n        end\n      end\n\n      Color.red.value &+ Color.green.value &+ Color.blue.value\n      CRYSTAL\n  end\n\n  it \"gets enum members as constants\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Green\")\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      {{Color.constants[1].stringify}}\n      CRYSTAL\n  end\n\n  it \"says that enum has Flags annotation\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      @[Flags]\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      {{Color.annotation(Flags) ? true : false}}\n      CRYSTAL\n  end\n\n  it \"says that enum doesn't have Flags annotation\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      {{Color.annotation(Flags) ? true : false}}\n      CRYSTAL\n  end\n\n  it \"gets methods\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bar\")\n      class Foo\n        def bar\n          1\n        end\n\n        def first_method_name : String\n          {{ @type.methods.map(&.name.stringify).first }}\n        end\n      end\n\n      Foo.new.first_method_name\n      CRYSTAL\n  end\n\n  it \"copies base macro def to sub-subtype even after it was copied to a subtype (#448)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Baz\")\n      class Object\n        def class_name : String\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Foo\n        @@children : Pointer(Foo)\n        @@children = Pointer(Foo).malloc(1_u64)\n\n        def self.children\n          @@children\n        end\n      end\n\n      Foo.children.value = Foo.new\n      Foo.children.value.class_name\n\n      class Bar < Foo; end\n\n      Foo.children.value = Bar.new\n      Foo.children.value.class_name\n\n      class Baz < Bar; end\n      Foo.children.value = Baz.new\n      Foo.children.value.class_name\n      CRYSTAL\n  end\n\n  it \"recalculates method when virtual metaclass type is added\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Test, RunnableTest\")\n      require \"prelude\"\n\n      class Global\n        @@x = [] of String\n        @@runnables = [] of Runnable.class\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n\n        def self.runnables\n          @@runnables\n        end\n      end\n\n      def run\n        Global.runnables.each &.run\n      end\n\n      class Runnable\n      end\n\n      class Runnable\n        macro inherited\n          Global.runnables << self\n        end\n\n        def self.run : Nil\n          Global.x << {{@type.name.stringify}}\n          nil\n        end\n      end\n\n      class Test < Runnable\n      end\n\n      run\n      Global.x.clear\n\n      class RunnableTest < Test\n      end\n\n      run\n      Global.x.join(\", \")\n      CRYSTAL\n  end\n\n  it \"correctly recomputes call (bug)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Baz\")\n      class Object\n        def in_object\n          in_class(1)\n        end\n      end\n\n      class Class\n        def in_class(x)\n          bar\n        end\n\n        def bar : String\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      f = Bar.new || Foo.new\n      f.class.in_object\n\n      class Baz < Foo\n      end\n\n      f2 = Baz.new || Foo.new\n      f2.class.in_object\n      CRYSTAL\n  end\n\n  it \"doesn't override local variable when using macro variable\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      macro foo(x)\n        %a = {{x}}\n        %a\n      end\n\n      a = 1\n      foo(2)\n      foo(3)\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't override local variable when using macro variable (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(26)\n      macro foo(x)\n        %a = {{x}} &+ 10\n        %a\n      end\n\n      a = 1\n      z = foo(2)\n      w = foo(3)\n      a &+ z &+ w\n      CRYSTAL\n  end\n\n  it \"uses indexed macro variable\" do\n    run(<<-CRYSTAL).to_i.should eq(4 + 5 + 6 + 40 + 50 + 60)\n      macro foo(*elems)\n        {% for elem, i in elems %}\n          %var{i} = {{elem}}\n        {% end %}\n\n        %total = 0\n        {% for elem, i in elems %}\n          %total &+= %var{i}\n        {% end %}\n        %total\n      end\n\n      z = 0\n      z &+= foo 4, 5, 6\n      z &+= foo 40, 50, 60\n      z\n      CRYSTAL\n  end\n\n  it \"uses indexed macro variable with many keys\" do\n    run(<<-CRYSTAL).to_i.should eq(4 + 5 + 6)\n      macro foo(*elems)\n        {% for elem, i in elems %}\n          %var{elem, i} = {{elem}}\n        {% end %}\n\n        %total = 0\n        {% for elem, i in elems %}\n          %total &+= %var{elem, i}\n        {% end %}\n        %total\n      end\n\n      z = foo 4, 5, 6\n      z\n      CRYSTAL\n  end\n\n  it \"codegens macro def with splat (#496)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo\n        def bar(*args) : Int32\n          {{ @type }}\n          args[0] &+ args[1] &+ args[2]\n        end\n      end\n\n      Foo.new.bar(1, 2, 3)\n      CRYSTAL\n  end\n\n  it \"codegens macro def with default arg (similar to #496)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def bar(foo = 1) : Int32\n          {{ @type }}\n          foo &+ 2\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"expands macro with default arg and splat (#784)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"5\")\n      macro some_macro(a=5, *args)\n        {{a.stringify}}\n      end\n\n      some_macro\n      CRYSTAL\n  end\n\n  it \"expands macro with default arg and splat (2) (#784)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"1\")\n      macro some_macro(a=5, *args)\n        {{a.stringify}}\n      end\n\n      some_macro 1, 2, 3, 4\n      CRYSTAL\n  end\n\n  it \"expands macro with default arg and splat (3) (#784)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro some_macro(a=5, *args)\n        {{args.size}}\n      end\n\n      some_macro 1, 2, 3, 4\n      CRYSTAL\n  end\n\n  it \"checks if macro expansion returns (#821)\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(123)\n      macro pass\n        return 123\n      end\n\n      def me\n        pass\n        nil\n      end\n\n      me || 0\n      CRYSTAL\n  end\n\n  it \"passes #826\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      macro foo\n        macro bar\n          {{yield}}\n        end\n      end\n\n      foo do\n        123\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"declares constant in macro (#838)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      macro foo\n        {{yield}}\n      end\n\n      foo do\n        CONST = 123\n      end\n\n      CONST\n      CRYSTAL\n  end\n\n  it \"errors if dynamic constant assignment after macro expansion\" do\n    assert_error <<-CRYSTAL, \"dynamic constant assignment. Constants can only be declared at the top level or inside other types.\"\n      macro foo\n        X = 123\n      end\n\n      def bar\n        foo\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"finds macro from virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class Foo\n        macro foo\n          123\n        end\n\n        def bar\n          foo\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      a = Pointer(Foo).malloc(1_u64)\n      a.value = Foo.new\n      a.value.bar\n      CRYSTAL\n  end\n\n  it \"expands macro with escaped quotes (#895)\" do\n    run(<<-CRYSTAL).to_string.should eq(%(hello\"))\n      macro foo(x)\n        \"{{x}}\\\\\"\"\n      end\n\n      foo hello\n      CRYSTAL\n  end\n\n  it \"expands macro def with return (#1040)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class Foo\n        def a : Int32\n          {{ @type }}\n          return 123\n        end\n      end\n\n      Foo.new.a\n      CRYSTAL\n  end\n\n  it \"fixes empty types of macro expansions (#1379)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      macro lala(exp)\n        {{exp}}\n      end\n\n      def foo\n        bar do\n          return 123\n        end\n      end\n\n      def bar\n        return yield\n      end\n\n      lala foo\n      CRYSTAL\n  end\n\n  it \"expands macro as class method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        macro bar\n          1\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"expands macro as class method and accesses @type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      class Foo\n        macro bar\n          {{@type.stringify}}\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"codegens macro with comment (bug) (#1396)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      macro my_macro\n        # {{ 1 }}\n        {{ 1 }}\n      end\n\n      my_macro\n      CRYSTAL\n  end\n\n  it \"correctly resolves constant inside block in macro def\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def foo\n        yield\n      end\n\n      class Foo\n        Const = 123\n\n        def self.bar : Int32\n          {{ @type }}\n          foo { Const }\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"can access free variables\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Int32\")\n      def foo(x : T) forall T\n        {{ T.stringify }}\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"types macro expansion bug (#1734)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo : Int32\n          {{ @type }}\n          1 || 2\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      x = true ? Foo.new : Bar.new\n      x.foo\n      CRYSTAL\n  end\n\n  it \"expands Path with resolve method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      CONST = 1\n\n      macro id(path)\n        {{path.resolve}}\n      end\n\n      id(CONST)\n      CRYSTAL\n  end\n\n  it \"can use macro inside array literal\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      macro foo\n        42\n      end\n\n      ary = [foo]\n      ary[0]\n      CRYSTAL\n  end\n\n  it \"can use macro inside hash literal\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      macro foo\n        42\n      end\n\n      hash = {foo => foo}\n      hash[foo]\n      CRYSTAL\n  end\n\n  it \"executes with named arguments for positional arg (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      macro foo(x)\n        {{x}} &+ 1\n      end\n\n      foo x: 2\n      CRYSTAL\n  end\n\n  it \"executes with named arguments for positional arg (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      macro foo(x, y)\n        {{x}} &+ {{y}} &+ 1\n      end\n\n      foo x: 2, y: 3\n      CRYSTAL\n  end\n\n  it \"executes with named arguments for positional arg (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class String\n        def bytesize\n          @bytesize\n        end\n      end\n\n      macro foo(x, y)\n        {{x}} &+ {{y}}.bytesize &+ 1\n      end\n\n      foo y: \"foo\", x: 2\n      CRYSTAL\n  end\n\n  it \"stringifies type without virtual marker\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo_m : Int32\n          {{ @type }}.foo\n        end\n\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      (Bar.new || Foo.new).foo_m\n      CRYSTAL\n  end\n\n  it \"uses tuple T in method with free vars\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Tuple\n        def foo(x : U) forall U\n          {{T.size}}\n        end\n      end\n\n      {1, 3}.foo(1)\n      CRYSTAL\n  end\n\n  it \"implicitly marks method as macro def when using @type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar\")\n      class Foo\n        def method\n          {{@type.stringify}}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.as(Foo).method\n      CRYSTAL\n  end\n\n  it \"doesn't replace %s in string (#2178)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hello %s\")\n      {% begin %}\n        \"hello %s\"\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"doesn't replace %q() (#2178)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hello\")\n      {% begin %}\n        %q(hello)\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"replaces %s inside string inside interpolation (#2178)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hello world\")\n      require \"prelude\"\n\n      {% begin %}\n        %a = \"world\"\n        \"hello \\#{ %a }\"\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"replaces %s inside string inside interpolation, with braces (#2178)\" do\n    run(<<-CRYSTAL).to_string.should eq(%(hello [{\"world\", \"world\"}, \"world\"]))\n      require \"prelude\"\n\n      {% begin %}\n        %a = \"world\"\n        \"hello \\#{ [{ %a, %a }, %a] }\"\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"retains original yield expression (#2923)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hi\")\n      macro foo\n        def bar(baz)\n          {{yield}}\n        end\n      end\n\n      foo do\n        baz\n      end\n\n      bar(\"hi\")\n      CRYSTAL\n  end\n\n  it \"surrounds {{yield}} with begin/end\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      macro foo\n        a = {{yield}}\n      end\n\n      a = 0\n      foo do\n        1\n        2\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"initializes instance var in macro\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(1)\n      class Foo\n        {% begin %}\n          @x = 1\n        {% end %}\n      end\n\n      Foo.new.@x\n      CRYSTAL\n  end\n\n  it \"initializes class var in macro\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(1)\n      class Foo\n        {% begin %}\n          @@x = 1\n        {% end %}\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"expands @def in inline macro\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      def foo\n        {{@def.name.stringify}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"expands @def in macro\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bar\")\n      macro foo\n        {{@def.name.stringify}}\n      end\n\n      def bar\n        foo\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"gets constant\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        Bar = 42\n      end\n\n      {{ Foo.constant(\"Bar\") }}\n      CRYSTAL\n  end\n\n  it \"determines if overrides (false)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      {{ Bar.overrides?(Foo, \"foo\") }}\n      CRYSTAL\n  end\n\n  it \"determines if overrides (true)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      {{ Bar.overrides?(Foo, \"foo\") }}\n      CRYSTAL\n  end\n\n  it \"determines if overrides, through another class (true)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      class Baz < Bar\n      end\n\n      {{ Baz.overrides?(Foo, \"foo\") }}\n      CRYSTAL\n  end\n\n  it \"determines if overrides, through module (true)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      module Moo\n        def foo\n          2\n        end\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n\n      class Baz < Bar\n      end\n\n      {{ Baz.overrides?(Foo, \"foo\") }}\n      CRYSTAL\n  end\n\n  it \"determines if overrides, with macro method (false)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo\n        def foo\n          {{ @type }}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      (Foo.new || Bar.new).foo\n\n      def x\n        {{ Bar.overrides?(Foo, \"foo\") }}\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"determines if method exists (true)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n        def foo\n          42\n        end\n      end\n\n      {{ Foo.has_method?(:foo) }}\n      CRYSTAL\n  end\n\n  it \"determines if method exists (false)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo\n        def foo\n          42\n        end\n      end\n\n      {{ Foo.has_method?(:bar) }}\n      CRYSTAL\n  end\n\n  it \"forwards file location\" do\n    run(<<-CRYSTAL, filename: \"bar.cr\").to_string.should eq(\"bar.cr\")\n      macro foo\n        bar\n      end\n\n      macro bar(file = __FILE__)\n        {{file}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"forwards dir location\" do\n    run(<<-CRYSTAL, filename: \"somedir/bar.cr\").to_string.should eq(\"somedir\")\n      macro foo\n        bar\n      end\n\n      macro bar(dir = __DIR__)\n        {{dir}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"forwards line number\" do\n    run(<<-CRYSTAL, filename: \"somedir/bar.cr\", inject_primitives: false).to_i.should eq(9)\n      macro foo\n        bar\n      end\n\n      macro bar(line = __LINE__)\n        {{line}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"keeps line number with no block\" do\n    run(<<-CRYSTAL, filename: \"somedir/bar.cr\", inject_primitives: false).to_i.should eq(6)\n      macro foo\n        {{ yield }}\n        __LINE__\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"keeps line number with a block\" do\n    run(<<-CRYSTAL, filename: \"somedir/bar.cr\", inject_primitives: false).to_i.should eq(6)\n      macro foo\n        {{ yield }}\n        __LINE__\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"resolves alias in macro\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      alias Foo = Int32 | String\n\n      {{ Foo.union_types.size }}\n      CRYSTAL\n  end\n\n  it \"gets default value of instance variable\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x = 1\n\n        def default\n          {{@type.instance_vars.first.default_value}}\n        end\n      end\n\n      Foo.new.default\n      CRYSTAL\n  end\n\n  it \"gets default value of instance variable of generic type\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      struct Int32\n        def self.foo\n          10\n        end\n      end\n\n      class Foo(T)\n        @x : T = T.foo\n\n        def default\n          {{@type.instance_vars.first.default_value}}\n        end\n      end\n\n      Foo(Int32).new.default\n      CRYSTAL\n  end\n\n  it \"gets default value of instance variable of inherited type that also includes module\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      module Moo\n        @moo = 10\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          {{ @type.instance_vars.first.default_value }}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"determines if variable has default value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x = 1\n        @y : Int32\n\n        def initialize(@y)\n        end\n\n        def defaults\n          {\n            {{ @type.instance_vars.find { |i| i.name == \"x\" }.has_default_value? }},\n            {{ @type.instance_vars.find { |i| i.name == \"y\" }.has_default_value? }},\n          }\n        end\n      end\n\n      x, y = Foo.new(2).defaults\n      a = 0\n      a &+= 1 if x\n      a &+= 2 if y\n      a\n      CRYSTAL\n  end\n\n  it \"expands macro with op assign inside assign (#5568)\" do\n    run(<<-CRYSTAL).to_string.chomp.should eq(\"2\")\n      require \"prelude\"\n\n      macro expand\n        {{ yield }}\n      end\n\n      def foo\n        {:foo => 1}\n      end\n\n      expand do\n        x = foo[:foo] += 1\n        puts x\n      end\n      CRYSTAL\n  end\n\n  it \"devirtualizes @type\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      class Foo\n        def foo\n          {{@type.id.stringify}}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      (Foo.new || Bar.new).foo\n      CRYSTAL\n  end\n\n  it \"keeps heredoc contents inside macro\" do\n    run(<<-CRYSTAL).to_string.should eq(\"  %foo\")\n      macro foo\n        <<-FOO\n          %foo\n        FOO\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"keeps heredoc contents with interpolation inside macro\" do\n    run(<<-CRYSTAL).to_string.should eq(\"  42\")\n      require \"prelude\"\n\n      macro foo\n        %foo = 42\n        <<-FOO\n          \\#{ %foo }\n        FOO\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"access to the program with @top_level\" do\n    run(<<-CRYSTAL).to_string.should eq(\"main\")\n      class Foo\n        def bar\n          {{@top_level.name.stringify}}\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"responds correctly to has_constant? with @top_level\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      FOO = 1\n      class Foo\n        def bar\n          {{@top_level.has_constant?(\"FOO\")}}\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"does block unpacking inside macro expression (#13707)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      {% begin %}\n        {%\n          data = [{1, 2}, {3, 4}]\n          value = 0\n          data.each do |(k, v)|\n            value += k\n            value += v\n          end\n        %}\n        {{ value }}\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"accepts compile-time flags\" do\n    run(\"{{ flag?(:foo) ? 1 : 0 }}\", flags: %w(foo)).to_i.should eq(1)\n    run(\"{{ flag?(:foo) ? 1 : 0 }}\", Int32, flags: %w(foo)).should eq(1)\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/magic_constants_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: magic constants\" do\n  it \"does __LINE__\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(5)\n      def foo(x = __LINE__)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __FILE__\" do\n    run(<<-CRYSTAL, filename: \"/foo/bar/baz.cr\").to_string.should eq(\"/foo/bar/baz.cr\")\n      def foo(x = __FILE__)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __DIR__\" do\n    run(<<-CRYSTAL, filename: \"/foo/bar/baz.cr\").to_string.should eq(\"/foo/bar\")\n      def foo(x = __DIR__)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __LINE__ with dispatch\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(10)\n      def foo(z : Int32, x = __LINE__)\n        x\n      end\n\n      def foo(z : String)\n        1\n      end\n\n      a = 1 || \"hello\"\n      foo(a)\n      CRYSTAL\n  end\n\n  it \"does __LINE__ when specifying one default arg with __FILE__\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(5)\n      def foo(x, file = __FILE__, line = __LINE__)\n        line\n      end\n\n      foo 1, \"hello\"\n      CRYSTAL\n  end\n\n  it \"does __LINE__ when specifying one normal default arg\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(27)\n      require \"primitives\"\n\n      def foo(x, z = 10, line = __LINE__)\n        z &+ line\n      end\n\n      foo 1, 20\n      CRYSTAL\n  end\n\n  it \"does __LINE__ when specifying one middle argument\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(27)\n      require \"primitives\"\n\n      def foo(x, line = __LINE__, z = 1)\n        z &+ line\n      end\n\n      foo 1, z: 20\n      CRYSTAL\n  end\n\n  it \"does __LINE__ in macro\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(5)\n      macro foo(line = __LINE__)\n        {{line}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __FILE__ in macro\" do\n    run(<<-CRYSTAL, filename: \"/foo/bar/baz.cr\").to_string.should eq(\"/foo/bar/baz.cr\")\n      macro foo(file = __FILE__)\n        {{file}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __DIR__ in macro\" do\n    run(<<-CRYSTAL, filename: \"/foo/bar/baz.cr\").to_string.should eq(\"/foo/bar\")\n      macro foo(dir = __DIR__)\n        {{dir}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __END_LINE__ without block\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(5)\n      def foo(x = __END_LINE__)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __END_LINE__ with block\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(8)\n      def foo(x = __END_LINE__)\n        yield\n        x\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"does __END_LINE__ in macro without block\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(5)\n      macro foo(line = __END_LINE__)\n        {{line}}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"does __END_LINE__ in macro with block\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(7)\n      macro foo(line = __END_LINE__)\n        {{line}}\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/method_missing_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: method_missing\" do\n  it \"does method_missing macro without args\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo_something\n          1\n        end\n\n        macro method_missing(call)\n          {{call.name.id}}_something\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with args\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo\n        macro method_missing(call)\n          {{call.args.join(\" &+ \").id}}\n        end\n      end\n\n      Foo.new.foo(1, 2, 3)\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with block\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo\n        def foo_something\n          yield 1\n          yield 2\n          yield 3\n        end\n\n        macro method_missing(call)\n          {{call.name.id}}_something {{call.block}}\n        end\n      end\n\n      a = 0\n      Foo.new.foo do |x|\n        a &+= x\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with block but not using it\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def foo_something\n          1 &+ 2\n        end\n\n        macro method_missing(call)\n          {{call.name.id}}_something {{call.block}}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (1)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foococo\")\n      class Foo\n        macro method_missing(call)\n          \"{{@type.name.id}}{{call.name.id}}\"\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Foo.new || Bar.new\n      foo.coco\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (2)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Barcoco\")\n      class Foo\n        macro method_missing(call)\n          \"{{@type.name.id}}{{call.name.id}}\"\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Bar.new || Foo.new\n      foo.coco\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def lala\n          1\n        end\n\n        macro method_missing(call)\n          2\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Bar.new || Foo.new\n      foo.lala\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (4)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        macro method_missing(call)\n          1\n        end\n      end\n\n      class Bar < Foo\n        macro method_missing(call)\n          2\n        end\n      end\n\n      foo = Bar.new || Foo.new\n      foo.lala\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (5)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        macro method_missing(call)\n          1\n        end\n      end\n\n      class Bar < Foo\n        macro method_missing(call)\n          2\n        end\n      end\n\n      class Baz < Bar\n        macro method_missing(call)\n          3\n        end\n      end\n\n      foo = Baz.new || Bar.new || Foo.new\n      foo.lala\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (6)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        macro method_missing(call)\n          2\n        end\n      end\n\n      class Baz < Bar\n        def lala\n          3\n        end\n      end\n\n      foo = Bar.new || Baz.new\n      foo.lala\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (7)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        macro method_missing(call)\n          2\n        end\n      end\n\n      class Baz < Bar\n        def lala\n          3\n        end\n      end\n\n      foo = Baz.new || Bar.new\n      foo.lala\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with virtual type (8)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar\")\n      class Foo\n        macro method_missing(call)\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Foo.new\n      foo.coco\n\n      bar = Bar.new\n      bar.coco\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with module involved\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Moo\n        def lala\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n\n        macro method_missing(call)\n          2\n        end\n      end\n\n      Foo.new.lala\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with top level method involved\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def lala\n        1\n      end\n\n      class Foo\n        macro method_missing(call)\n          2\n        end\n\n        def bar\n          lala\n        end\n      end\n\n      foo = Foo.new\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with included module\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Foo\")\n      module Moo\n        macro method_missing(call)\n          {{@type.name.stringify}}\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.coco\n      CRYSTAL\n  end\n\n  it \"does method_missing with assignment (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        macro method_missing(call)\n          x = {{call.args[0]}}\n          x\n        end\n      end\n\n      foo = Foo.new\n      foo.bar(1)\n      CRYSTAL\n  end\n\n  it \"does method_missing with assignment (2) (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      class Foo\n        @x : Int32?\n\n        macro method_missing(call)\n          @x = {{call.args[0]}}\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.bar(1).to_i!\n      CRYSTAL\n  end\n\n  it \"does method_missing macro without args (with call)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo_something\n          1\n        end\n\n        macro method_missing(call)\n          {{call.name.id}}_something\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"does method_missing macro with args (with call)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo\n        macro method_missing(call)\n          {{call.args.join(\" &+ \").id}}\n        end\n      end\n\n      Foo.new.foo(1, 2, 3)\n      CRYSTAL\n  end\n\n  it \"forwards\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Wrapped\n        def foo(x, y, z)\n          x &+ y &+ z\n        end\n      end\n\n      class Foo\n        def initialize(@wrapped : Wrapped)\n        end\n\n        macro method_missing(call)\n          @wrapped.{{call}}\n        end\n      end\n\n      Foo.new(Wrapped.new).foo(1, 2, 3)\n      CRYSTAL\n  end\n\n  it \"does method_missing generating method\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bar\")\n      class Foo\n        macro method_missing(call)\n          def {{call.name}}\n            {{call.name.stringify}}\n          end\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"works with named arguments (#3654)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class A\n        macro method_missing(call)\n          {{call.named_args[0].value}} &+\n            {{call.named_args[1].value}}\n        end\n      end\n\n      a = A.new\n      a.b(x: 1, y: 2)\n      CRYSTAL\n  end\n\n  it \"works with named arguments that aren't legal variable names (#10381)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class A\n        macro method_missing(call)\n          {{call.named_args[0].value}} &+\n            {{call.named_args[1].value}}\n        end\n      end\n\n      a = A.new\n      a.b(\"@x\": 1, Y: 2)\n      CRYSTAL\n  end\n\n  it \"finds method_missing with 'with ... yield'\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        macro method_missing(call)\n          @{{call.name.id}}\n        end\n      end\n\n      def bar\n        foo = Foo.new(10)\n        with foo yield\n      end\n\n      bar do\n        x\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/module_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: module\" do\n  it \"codegens pointer of module with method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"codegens pointer of module with method with two including types\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        include Moo\n\n        def foo\n          2\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo.new\n      p.value = Bar.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"codegens pointer of module with method with two including types with one struct\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Foo\n      end\n\n      class Bar\n        include Foo\n\n        def foo\n          1\n        end\n      end\n\n      struct Coco\n        include Foo\n\n        def foo\n          2\n        end\n      end\n\n      p = Pointer(Foo).malloc(1_u64)\n      p.value = Bar.new\n      p.value = Coco.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"codegens pointer of module with method with two including types with one struct (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Foo\n      end\n\n      class Bar\n        include Foo\n\n        def foo\n          1\n        end\n      end\n\n      struct Coco\n        include Foo\n\n        def foo\n          2\n        end\n      end\n\n      p = Pointer(Foo).malloc(1_u64)\n      p.value = Bar.new\n      p.value = Coco.new\n      x = p.value\n      x.foo\n      CRYSTAL\n  end\n\n  it \"codegens pointer of module and pass value to method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Foo\n      end\n\n      class Bar\n        include Foo\n\n        def foo\n          1\n        end\n      end\n\n      def foo(x)\n        x.foo\n      end\n\n      p = Pointer(Foo).malloc(1_u64)\n      p.value = Bar.new\n      foo p.value\n      CRYSTAL\n  end\n\n  it \"codegens pointer of module with block\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      struct Pointer\n        def each\n          yield value\n        end\n      end\n\n      a = Pointer(Moo).malloc(1_u64)\n      a.value = Foo.new\n      x = nil\n      a.each do |io|\n        x = io\n      end\n      x.not_nil!.foo\n      CRYSTAL\n  end\n\n  it \"codegens module with virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Bar.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"declares proc with module type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      module Moo\n        def moo\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar\n        include Moo\n      end\n\n      foo = ->(x : Moo) { x.moo }\n      foo.call(Bar.new)\n      CRYSTAL\n  end\n\n  it \"declares proc with module type and invoke it with two different types that return themselves\" do\n    codegen(<<-CRYSTAL)\n      module Moo\n        def moo\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      struct Bar\n        include Moo\n      end\n\n      foo = ->(x : Moo) { x }\n      foo.call(Foo.new)\n      foo.call(Bar.new)\n      CRYSTAL\n  end\n\n  it \"codegens proc of a module that was never included\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      module Moo\n      end\n\n      ->(x : Moo) { x.foo }\n      1\n      CRYSTAL\n  end\n\n  it \"codegens proc of module when generic type includes it\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      module Moo\n      end\n\n      class Foo(T)\n        include Moo\n\n        def foo\n          3\n        end\n      end\n\n      z = ->(x : Moo) { x.foo }\n      z.call(Foo(Int32).new)\n      CRYSTAL\n  end\n\n  it \"invokes method on yielded module that has no instances (#1079)\" do\n    run(<<-CRYSTAL).to_i.should eq(456)\n      require \"prelude\"\n\n      module Mod\n      end\n\n      def foo\n        ptr = Pointer(Mod).malloc(1_u64)\n        yield ptr.value\n        123\n      rescue\n        456\n      end\n\n      foo { |x| x.coco }\n      CRYSTAL\n  end\n\n  it \"expands modules to its including types (#1916)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Reference\n        def method(other : Reference)\n          1\n        end\n\n        def method(other)\n          2\n        end\n      end\n\n      module Moo\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar\n        include Moo\n      end\n\n      x = Foo.new\n      y = x.as(Moo)\n\n      x.method(y)\n      CRYSTAL\n  end\n\n  it \"expands modules to its including types (2) (#1916)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Reference\n        def method(other : Reference)\n          1\n        end\n\n        def method(other)\n          2\n        end\n      end\n\n      module Moo\n      end\n\n      module Moo::Sub\n        include Moo\n      end\n\n      class File2\n        include Moo::Sub\n      end\n\n      file = File2.new\n      file2 = file.as(Moo)\n\n      file.method(file2)\n      CRYSTAL\n  end\n\n  it \"expands modules to its including types (3) (#1916)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Object\n        def method(other : Reference)\n          1\n        end\n\n        def method(other)\n          2\n        end\n      end\n\n      module Moo\n      end\n\n      class Foo\n        include Moo\n      end\n\n      struct Bar\n        include Moo\n      end\n\n      x = Bar.new\n      y = x.as(Moo)\n\n      x.method(y)\n      CRYSTAL\n  end\n\n  it \"codegens cast to module with class and struct to nilable module\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      module Moo\n        def bar\n          10\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      struct Bar\n        include Moo\n      end\n\n      def moo\n        (Foo.new || Bar.new).as(Moo)\n      end\n\n      moo = moo()\n      nilable = moo || nil\n      if nilable\n        nilable.bar\n      else\n        20\n      end\n      CRYSTAL\n  end\n\n  it \"codegens cast to module that includes bool\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Moo\n      end\n\n      struct Bool\n        include Moo\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new\n      a = false.as(Moo)\n      if a\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"declares and includes generic module, in macros T is a tuple literal\" do\n    run(<<-CRYSTAL).to_string.should eq(\"TupleLiteral\")\n      module Moo(*T)\n        def t\n          {{T.class_name}}\n        end\n      end\n\n      class Foo\n        include Moo(Int32, Char)\n      end\n\n      Foo.new.t\n      CRYSTAL\n  end\n\n  it \"can instantiate generic module\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      struct Int32\n        def self.foo\n          10\n        end\n      end\n\n      module Foo(T)\n        def self.foo\n          T.foo\n        end\n      end\n\n      Foo(Int32).foo\n      CRYSTAL\n  end\n\n  it \"can use generic module as instance variable type\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      module Moo(T)\n        def foo\n          1\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n\n      class Bar\n        include Moo(Int32)\n\n        def foo\n          2\n        end\n      end\n\n      class Mooer\n        def initialize(@moo : Moo(Int32))\n        end\n\n        def moo\n          @moo.foo\n        end\n      end\n\n      mooer = Mooer.new(Foo.new)\n      x = mooer.moo\n\n      mooer = Mooer.new(Bar.new)\n      y = mooer.moo\n\n      x &+ y\n      CRYSTAL\n  end\n\n  it \"can use generic module as instance variable type (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      module Moo(T)\n        def foo\n          1\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n\n      class Bar(T)\n        include Moo(T)\n\n        def foo\n          2\n        end\n      end\n\n      class Mooer\n        def initialize(@moo : Moo(Int32))\n        end\n\n        def moo\n          @moo.foo\n        end\n      end\n\n      mooer = Mooer.new(Foo(Int32).new)\n      x = mooer.moo\n\n      mooer = Mooer.new(Bar(Int32).new)\n      y = mooer.moo\n\n      x &+ y\n      CRYSTAL\n  end\n\n  it \"casts to union of module that is included in other module (#3323)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      module Moo\n        def moo\n          0\n        end\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      class Foo\n        include Moo2\n      end\n\n      class Bar < Foo\n        def moo\n          10\n        end\n      end\n\n      struct Baz\n        include Moo\n      end\n\n      bar = Bar.new.as(Int32 | Moo)\n      bar.as(Moo).moo\n      CRYSTAL\n  end\n\n  it \"casts to union of generic module that is included in other module (#3323)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n\n      module Moo(T)\n        def moo\n          0\n        end\n      end\n\n      module Moo2(T)\n        include Moo(T)\n      end\n\n      class Foo\n        include Moo2(Char)\n      end\n\n      class Bar < Foo\n        def moo\n          10\n        end\n      end\n\n      struct Baz\n        include Moo(Char)\n      end\n\n      bar = Bar.new.as(Int32 | Moo(Char))\n      bar.as(Moo(Char)).moo\n      CRYSTAL\n  end\n\n  it \"codegens dispatch of union with module (#3647)\" do\n    run(<<-CRYSTAL).to_i.should eq(234)\n      module Moo\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : Int32)\n        1\n      end\n\n      def foo(x)\n        234\n      end\n\n      m = Bar.new.as(Moo)\n      a = m || 1\n      foo(a)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/multi_assign_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: multi assign\" do\n  it \"supports n to n assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      a, b, c = 1, 2, 3\n      a &* 100 &+ b &* 10 &+ c\n      CRYSTAL\n  end\n\n  context \"without strict_multi_assign\" do\n    it \"supports 1 to n assignment\" do\n      run(<<-CRYSTAL).to_i.should eq(123)\n        class Foo\n          def [](index)\n            index &+ 1\n          end\n        end\n\n        a, b, c = Foo.new\n        a &* 100 &+ b &* 10 &+ c\n        CRYSTAL\n    end\n\n    it \"doesn't raise if value size in 1 to n assignment doesn't match target count\" do\n      run(<<-CRYSTAL).to_i.should eq(4)\n        require \"prelude\"\n\n        begin\n          a, b = [1, 2, 3]\n          4\n        rescue ex : Exception\n          raise ex unless ex.message == \"Multiple assignment count mismatch\"\n          5\n        end\n        CRYSTAL\n    end\n  end\n\n  context \"strict_multi_assign\" do\n    it \"supports 1 to n assignment\" do\n      run(<<-CRYSTAL, flags: %w(strict_multi_assign)).to_i.should eq(123)\n        require \"prelude\"\n\n        class Foo\n          include Indexable(Int32)\n\n          def unsafe_fetch(index)\n            index &+ 1\n          end\n\n          def size\n            3\n          end\n        end\n\n        a, b, c = Foo.new\n        a &* 100 &+ b &* 10 &+ c\n        CRYSTAL\n    end\n\n    it \"raises if value size in 1 to n assignment doesn't match target count\" do\n      run(<<-CRYSTAL, flags: %w(strict_multi_assign)).to_i.should eq(5)\n        require \"prelude\"\n\n        begin\n          a, b = [1, 2, 3]\n          4\n        rescue ex : Exception\n          raise ex unless ex.message == \"Multiple assignment count mismatch\"\n          5\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"supports m to n assignment, with splat on left-hand side (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      #{tuple_new}\n\n      a, *b, c = 1, 2, 3, 4, 5\n      a &* 10000 &+ b[0] &* 1000 &+ b[1] &* 100 &+ b[2] &* 10 &+ c\n      CRYSTAL\n  end\n\n  it \"supports m to n assignment, with splat on left-hand side (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      #{tuple_new}\n\n      *a, b, c = 1, 2, 3, 4, 5\n      a[0] &* 10000 &+ a[1] &* 1000 &+ a[2] &* 100 &+ b &* 10 &+ c\n      CRYSTAL\n  end\n\n  it \"supports m to n assignment, with splat on left-hand side (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      #{tuple_new}\n\n      a, b, *c = 1, 2, 3, 4, 5\n      a &* 10000 &+ b &* 1000 &+ c[0] &* 100 &+ c[1] &* 10 &+ c[2]\n      CRYSTAL\n  end\n\n  it \"supports m to n assignment, splat is empty tuple (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{tuple_new}\n\n      _, *x, _ = 1, 2\n      x.is_a?(Tuple())\n      CRYSTAL\n  end\n\n  it \"supports m to n assignment, splat is empty tuple (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{tuple_new}\n\n      *x, _, _ = 1, 2\n      x.is_a?(Tuple())\n      CRYSTAL\n  end\n\n  it \"supports m to n assignment, splat is empty tuple (3)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{tuple_new}\n\n      _, _, *x = 1, 2\n      x.is_a?(Tuple())\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, with splat on left-hand side (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      require \"prelude\"\n\n      a, *b, c = {1, 2, 3, 4, 5}\n      a &* 10000 &+ b[0] &* 1000 &+ b[1] &* 100 &+ b[2] &* 10 &+ c\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, with splat on left-hand side (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      #{range_new}\n      #{include_indexable}\n\n      *a, b, c = {1, 2, 3, 4, 5}\n      a[0] &* 10000 &+ a[1] &* 1000 &+ a[2] &* 100 &+ b &* 10 &+ c\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, with splat on left-hand side (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(12345)\n      #{range_new}\n      #{include_indexable}\n\n      a, b, *c = {1, 2, 3, 4, 5}\n      a &* 10000 &+ b &* 1000 &+ c[0] &* 100 &+ c[1] &* 10 &+ c[2]\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, splat is empty (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      _, *x, _ = {1, 2}\n      x.is_a?(Tuple())\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, splat is empty (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{tuple_new}\n      #{range_new}\n      #{include_indexable}\n\n      *x, _, _ = {1, 2}\n      x.is_a?(Tuple())\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, splat is empty (3)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{tuple_new}\n      #{range_new}\n      #{include_indexable}\n\n      _, _, *x = {1, 2}\n      x.is_a?(Tuple())\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, raises if too short\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      begin\n        a, *b, c = [1]\n        false\n      rescue ex : IndexError\n        ex.message == \"Multiple assignment count mismatch\"\n      end\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, raises if out of bounds (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      begin\n        *a, b, c = [1]\n        false\n      rescue ex : IndexError\n        true\n      end\n      CRYSTAL\n  end\n\n  it \"supports 1 to n assignment, raises if out of bounds (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      begin\n        a, b, *c = [1]\n        false\n      rescue ex : IndexError\n        true\n      end\n      CRYSTAL\n  end\nend\n\nprivate def tuple_new\n  <<-CRYSTAL\n    struct Tuple\n      def self.new(*args)\n        args\n      end\n    end\n    CRYSTAL\nend\n\nprivate def range_new\n  <<-CRYSTAL\n    struct Range(B, E)\n      def initialize(@begin : B, @end : E, @exclusive : Bool = false)\n      end\n    end\n    CRYSTAL\nend\n\nprivate def include_indexable\n  <<-CRYSTAL\n    struct Tuple(*T)\n      include Indexable(Union(*T))\n    end\n    CRYSTAL\nend\n"
  },
  {
    "path": "spec/compiler/codegen/named_args_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: named args\" do\n  it \"calls with named arg\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo(y = 2)\n        y\n      end\n\n      foo y: 10\n      CRYSTAL\n  end\n\n  it \"calls with named arg and other args\" do\n    run(<<-CRYSTAL).to_i.should eq(13)\n      def foo(x, y = 2, z = 3)\n        x &+ y &+ z\n      end\n\n      foo 1, z: 10\n      CRYSTAL\n  end\n\n  it \"calls with named arg as object method\" do\n    run(<<-CRYSTAL).to_i.should eq(13)\n      class Foo\n        def foo(x, y = 2, z = 3)\n          x &+ y &+ z\n        end\n      end\n\n      Foo.new.foo 1, z: 10\n      CRYSTAL\n  end\n\n  it \"calls twice with different types\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      struct Int32\n        def &+(other : Float)\n          self + other\n        end\n      end\n\n      def add(x, y = 1)\n        x &+ y\n      end\n\n      value = 0\n      value &+= add(1, y: 2)\n      value &+= add(1, y: 1.3)\n      value.to_i!\n      CRYSTAL\n  end\n\n  it \"calls new with named arg\" do\n    run(<<-CRYSTAL).to_i.should eq(13)\n      class Foo\n        @value : Int32\n\n        def initialize(x, y = 2, z = 3)\n          @value = x &+ y &+ z\n        end\n\n        def value\n          @value\n        end\n      end\n\n      Foo.new(1, z: 10).value\n      CRYSTAL\n  end\n\n  it \"uses named args in dispatch\" do\n    run(<<-CRYSTAL).to_i.should eq(22)\n      class Foo\n        def foo(x, z = 2)\n          x &+ z &+ 1\n        end\n      end\n\n      class Bar\n        def foo(x, z = 2)\n          x &+ z\n        end\n      end\n\n      a = Foo.new || Bar.new\n      a.foo 1, z: 20\n      CRYSTAL\n  end\n\n  it \"sends one regular argument as named argument\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo(x)\n        x\n      end\n\n      foo x: 42\n      CRYSTAL\n  end\n\n  it \"sends two regular arguments as named arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo(x, y)\n        x &+ y\n      end\n\n      foo x: 10, y: 32\n      CRYSTAL\n  end\n\n  it \"sends two regular arguments as named arguments in inverted position (1)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      def foo(x, y)\n        x\n      end\n\n      foo y: 42, x: \"foo\"\n      CRYSTAL\n  end\n\n  it \"sends two regular arguments as named arguments in inverted position (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo(x, y)\n        y\n      end\n\n      foo y: 42, x: \"foo\"\n      CRYSTAL\n  end\n\n  it \"overloads based on required named args\" do\n    run(<<-CRYSTAL).to_i.should eq(10 + 20 + 30*40)\n      def foo(x, *, y)\n        x &+ y\n      end\n\n      def foo(x, *, z)\n        x &* z\n      end\n\n      a = foo(10, y: 20)\n      b = foo(30, z: 40)\n      a &+ b\n      CRYSTAL\n  end\n\n  it \"overloads based on required named args, with restrictions\" do\n    run(<<-CRYSTAL).to_i.should eq(10 + 20 + 30*40)\n      def foo(x, *, z : Int32)\n        x &+ z\n      end\n\n      def foo(x, *, z : Float64)\n        x &* z.to_i!\n      end\n\n      a = foo(10, z: 20)\n      b = foo(30, z: 40.0)\n      a &+ b\n      CRYSTAL\n  end\n\n  it \"uses bare splat in new (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def initialize(*, y = 22)\n          @y = y\n        end\n\n        def y\n          @y\n        end\n      end\n\n      v1 = Foo.new.y\n      v2 = Foo.new(y: 20).y\n      v1 &+ v2\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/named_tuple_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: named tuple\" do\n  it \"codegens tuple index\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      t = {x: 42, y: 'a'}\n      t[:x]\n      CRYSTAL\n  end\n\n  it \"codegens tuple index another order\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      t = {y: 'a', x: 42}\n      t[:x]\n      CRYSTAL\n  end\n\n  it \"codegens tuple nilable index (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      t = {x: 42, y: 'a'}\n      t[:x]? || 84\n      CRYSTAL\n  end\n\n  it \"codegens tuple nilable index (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      t = {x: 'a', y: 42}\n      t[:y]? || 84\n      CRYSTAL\n  end\n\n  it \"codegens tuple nilable index (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(84)\n      t = {x: 'a', y: 42}\n      t[:z]? || 84\n      CRYSTAL\n  end\n\n  it \"passes named tuple to def\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo(t)\n        t[:x]\n      end\n\n      foo({y: 'a', x: 42})\n      CRYSTAL\n  end\n\n  it \"gets size at compile time\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct NamedTuple\n        def my_size\n          {{ T.size }}\n        end\n      end\n\n      {x: 10, y: 20}.my_size\n      CRYSTAL\n  end\n\n  it \"gets keys at compile time (1)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"x\")\n      struct NamedTuple\n        def keys\n          {{ T.keys.map(&.stringify)[0] }}\n        end\n      end\n\n      {x: 10, y: 2}.keys\n      CRYSTAL\n  end\n\n  it \"gets keys at compile time (2)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"y\")\n      struct NamedTuple\n        def keys\n          {{ T.keys.map(&.stringify)[1] }}\n        end\n      end\n\n      {x: 10, y: 2}.keys\n      CRYSTAL\n  end\n\n  it \"doesn't crash when overload doesn't match\" do\n    codegen(<<-CRYSTAL)\n      struct NamedTuple\n        def foo(other : self)\n        end\n\n        def foo(other)\n        end\n      end\n\n      tup1 = {a: 1}\n      tup2 = {b: 1}\n      tup1.foo(tup2)\n      CRYSTAL\n  end\n\n  it \"assigns named tuple to compatible named tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      ptr = Pointer({x: Int32, y: String}).malloc(1_u64)\n\n      # Here the compiler should reorder the values to match\n      # the type inside the pointer\n      ptr.value = {y: \"hello\", x: 42}\n\n      ptr.value[:x]\n      CRYSTAL\n  end\n\n  it \"upcasts named tuple inside compatible named tuple\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Bar\")\n      def foo\n        if 1 == 2\n          {name: \"Foo\", age: 20}\n        else\n          # Here the compiler should reorder the values to match\n          # those of the tuple above\n          {age: 40, name: \"Bar\"}\n        end\n      end\n\n      foo[:name]\n      CRYSTAL\n  end\n\n  it \"assigns named tuple union to compatible named tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      tup1 = {x: 1, y: \"foo\"}\n      tup2 = {x: 3}\n      tup3 = {y: \"bar\", x: 42}\n\n      ptr = Pointer(typeof(tup1, tup2, tup3)).malloc(1_u64)\n\n      # Here the compiler should reorder the values\n      # inside tup3 to match the order of tup1\n      ptr.value = tup3\n\n      ptr.value[:x]\n      CRYSTAL\n  end\n\n  it \"upcasts named tuple union to compatible named tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo\n        if 1 == 2\n          {x: 1, y: \"foo\"} || {x: 3}\n        else\n          {y: \"bar\", x: 42}\n        end\n      end\n\n      foo[:x]\n      CRYSTAL\n  end\n\n  it \"assigns named tuple inside union to union with compatible named tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      tup1 = {x: 21, y: \"foo\"}\n      tup2 = {x: 3}\n\n      union1 = tup1 || tup2\n\n      tup3 = {y: \"bar\", x: 42}\n      tup4 = {x: 4}\n\n      union2 = tup3 || tup4\n\n      ptr = Pointer(typeof(union1, union2)).malloc(1_u64)\n\n      # Here the compiler should reorder the values inside\n      # tup3 inside union2 to match the order of tup1\n      ptr.value = union2\n\n      ptr.value[:x]\n      CRYSTAL\n  end\n\n  it \"upcasts named tuple inside union to union with compatible named tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo\n        if 1 == 2\n          tup1 = {x: 21, y: \"foo\"}\n          tup2 = {x: 3}\n          union1 = tup1 || tup2\n          union1\n        else\n          tup3 = {y: \"bar\", x: 42}\n          tup4 = {x: 4}\n          union2 = tup3 || tup4\n\n          # Here the compiler should reorder the values inside\n          # tup3 inside union2 to match the order of tup1\n          union2\n        end\n      end\n\n      foo[:x]\n      CRYSTAL\n  end\n\n  it \"allows named tuple covariance\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n       class Obj\n         def initialize\n           @tuple = {foo: Foo.new}\n         end\n\n         def tuple=(@tuple)\n         end\n\n         def tuple\n           @tuple\n         end\n       end\n\n       class Foo\n         def bar\n           21\n         end\n       end\n\n       class Bar < Foo\n         def bar\n           42\n         end\n       end\n\n       obj = Obj.new\n       obj.tuple = {foo: Bar.new}\n       obj.tuple[:foo].bar\n       CRYSTAL\n  end\n\n  it \"merges two named tuple types with same keys but different types (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n       def foo\n         if 1 == 2\n           {x: \"foo\", y: 10}\n         else\n           {y: nil, x: \"foo\"}\n         end\n       end\n\n       val = foo[:y]\n       val || 20\n       CRYSTAL\n  end\n\n  it \"merges two named tuple types with same keys but different types (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n       def foo\n         if 1 == 1\n           {x: \"foo\", y: 10}\n         else\n           {y: nil, x: \"foo\"}\n         end\n       end\n\n       val = foo[:y]\n       val || 20\n       CRYSTAL\n  end\n\n  it \"codegens union of tuple of float with tuple of tuple of float\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      a = {x: 1.5}\n      b = {x: {22.0, 20.0} }\n      c = b || a\n      v = c[:x]\n      if v.is_a?(Float64)\n        10\n      else\n        v[0].to_i! &+ v[1].to_i!\n      end\n      CRYSTAL\n  end\n\n  it \"provides T as a named tuple literal\" do\n    run(<<-CRYSTAL).to_string.should eq(\"NamedTupleLiteral\")\n      struct NamedTuple\n        def self.foo\n          {{ T.class_name }}\n        end\n      end\n      NamedTuple(x: Nil, y: Int32).foo\n      CRYSTAL\n  end\n\n  it \"assigns two same-size named tuple types to a same var (#3132)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      t = {x: true}\n      t\n      t = {x: 2}\n      t[:x]\n      CRYSTAL\n  end\n\n  it \"downcasts union inside tuple to value (#3907)\" do\n    codegen(<<-CRYSTAL)\n      struct Foo\n      end\n\n      foo = Foo.new\n\n      x = {a: 0, b: foo}\n      z = x[:a]\n      x = {a: 0, b: z}\n      CRYSTAL\n  end\n\n  it \"accesses T and creates instance from it\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct NamedTuple\n        def named_args\n          T\n        end\n      end\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      t = {a: Foo.new(1)}\n      f = t.named_args[:a].new(2)\n      f.x\n      CRYSTAL\n  end\n\n  it \"does to_s for NamedTuple class\" do\n    run(<<-CRYSTAL).to_string.should eq(%(NamedTuple(a: Int32, \"b c\": String, \"+\": Char)))\n      require \"prelude\"\n\n      NamedTuple(a: Int32, \"b c\": String, \"+\": Char).to_s\n      CRYSTAL\n  end\n\n  it \"doesn't error if NamedTuple includes a non-generic module (#10380)\" do\n    codegen(<<-CRYSTAL)\n      module Foo\n      end\n\n      struct NamedTuple\n        include Foo\n      end\n\n      x = uninitialized Foo\n      x = {a: 1}\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/new_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: new\" do\n  it \"codegens instance method with allocate\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def coco\n          1\n        end\n      end\n\n      Foo.allocate.coco\n      CRYSTAL\n  end\n\n  it \"codegens instance method with new and instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize\n          @coco = 2\n        end\n\n        def coco\n          @coco = 1\n          @coco\n        end\n      end\n\n      f = Foo.new\n      f.coco\n      CRYSTAL\n  end\n\n  it \"codegens instance method with new\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def coco\n          1\n        end\n      end\n\n      Foo.new.coco\n      CRYSTAL\n  end\n\n  it \"can create Reference\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      Reference.new.object_id == 0\n      CRYSTAL\n  end\n\n  it \"inherits initialize\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new(42).x\n      CRYSTAL\n  end\n\n  it \"inherits initialize for generic type\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo(T)\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        def x\n          @x\n        end\n      end\n\n      Bar(Int32).new(42).x\n      CRYSTAL\n  end\n\n  it \"overloads new and initialize, 1 (#2489)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class String\n        def size\n          10\n        end\n      end\n\n      class Foo\n        def initialize(@foo : Int32)\n        end\n\n        def self.new(bar) : self\n          new bar.size\n        end\n\n        def self.new : self\n          new \"foo\"\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"overloads new and initialize, 2 (#2489)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        def initialize(@foo : Int32)\n        end\n      end\n\n      class Bar < Foo\n        def self.new(foo : Int32) : self\n          Global.x = foo &+ 1\n          super\n        end\n      end\n\n      Bar.new(5)\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"overloads new and initialize, 3 (#2489)\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        def initialize(@foo : Int32)\n        end\n\n        def self.new(foo : Int32) : self\n          Global.x = foo &+ 1\n          previous_def\n        end\n      end\n\n      Foo.new(5)\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"defines new for module\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      module Moo\n        @x : Int32\n\n        def initialize(x : Int32)\n          @x = x &+ 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new(41).x\n      CRYSTAL\n  end\n\n  it \"finds super in deep hierarchy\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Bar\n      end\n\n      class Qux < Baz\n        def initialize\n          super(42)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Qux.new.x\n      CRYSTAL\n  end\n\n  it \"finds new in superclass if no initialize is defined (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def self.new\n          42\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"finds new in superclass if no initialize is defined (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        def self.new\n          42\n        end\n      end\n\n      class Bar < Foo\n        def self.new(x)\n          x\n        end\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"finds new in superclass for Enum\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Enum\n        def self.new(x : String)\n          new(1)\n        end\n      end\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      color = Color.new(\"foo\")\n      color.value\n      CRYSTAL\n  end\n\n  it \"can create Tuple with Tuple.new\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      require \"prelude\"\n\n      Tuple.new.size\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (1) (#731)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @x : Int32\n\n        def initialize(@x = bar)\n        end\n\n        def x\n          @x\n        end\n\n        def bar\n          42\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (2) (#731)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @x : Int32\n\n        def initialize(@x = bar, @y = 2)\n        end\n\n        def x\n          @x\n        end\n\n        def y\n          @y\n        end\n\n        def bar\n          20\n        end\n      end\n\n      foo = Foo.new(y: 22)\n      foo.x &+ foo.y\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (3) (#731)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @x : Int32\n\n        def initialize(@x = bar)\n          yield 10, 12\n        end\n\n        def x\n          @x\n        end\n\n        def bar\n          20\n        end\n      end\n\n      total = 0\n      foo = Foo.new do |a, b|\n        total &+= a\n        total &+= b\n      end\n      total &+= foo.x\n      total\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (4) (#731)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @x : Int32\n\n        def initialize(@x = bar, &@block : -> Int32)\n        end\n\n        def x\n          @x\n        end\n\n        def bar\n          22\n        end\n\n        def block\n          @block\n        end\n      end\n\n      foo = Foo.new do\n        20\n      end\n      foo.x &+ foo.block.call\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/next_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: next\" do\n  it \"codegens next\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        yield\n      end\n\n      foo do\n        next 1\n      end\n      CRYSTAL\n  end\n\n  it \"codegens next conditionally\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      def foo\n        yield 1\n        yield 2\n        yield 3\n        yield 4\n      end\n\n      a = 0\n      foo do |i|\n        next if i.unsafe_mod(2) == 0\n        a &+= i\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"codegens next conditionally with int type (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(100)\n      def foo\n        x = 0\n        x &+= yield 1\n        x &+= yield 2\n        x &+= yield 3\n        x &+= yield 4\n        x\n      end\n\n      foo do |i|\n        if i == 1\n          next 10\n        elsif i == 2\n          next 20\n        elsif i == 3\n          next 30\n        end\n        40\n      end\n      CRYSTAL\n  end\n\n  it \"codegens next with break (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      def foo\n        yield 1\n      end\n\n      foo do |i|\n        if i == 1\n          break 20\n        else\n          next 10\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"codegens next with break (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(40)\n      def foo\n        a = 0\n        a &+= yield 1\n        a &+= yield 2\n        a\n      end\n\n      foo do |i|\n        if i == 1\n          next 10\n        elsif i == 3\n          break 20\n        end\n        30\n      end\n      CRYSTAL\n  end\n\n  it \"codegens next with break (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      def foo\n        a = 0\n        a &+= yield 1\n        a &+= yield 2\n        a\n      end\n\n      foo do |i|\n        if i == 1\n          next 10\n        elsif i == 2\n          break 20\n        end\n        30\n      end\n      CRYSTAL\n  end\n\n  it \"codegens next with while inside block\" do\n    run(<<-CRYSTAL).to_i.should eq(30)\n      def foo\n        a = 0\n        a &+= yield 4\n        a &+= yield 5\n        a\n      end\n\n      foo do |i|\n        a = 0\n        b = 0\n        while a < 4\n          a &+= 1\n          next if a.unsafe_mod(2) == 0\n          b &+= a\n        end\n        if b == i\n          next 10\n        end\n        20\n      end\n      CRYSTAL\n  end\n\n  it \"codegens next without expressions\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil; def to_i!; 0; end; end\n\n      def foo\n        yield\n      end\n\n      foo do\n        if 1 == 1\n          1\n        else\n          next\n        end\n      end.to_i!\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/nilable_cast_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: nilable cast\" do\n  it \"does nilable cast (true)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      x = 42 || \"hello\"\n      y = x.as?(Int32)\n      y || 84\n      CRYSTAL\n  end\n\n  it \"does nilable cast (false)\" do\n    run(<<-CRYSTAL).to_i.should eq(84)\n      x = \"hello\" || 42\n      y = x.as?(Int32)\n      y || 84\n      CRYSTAL\n  end\n\n  it \"does nilable cast (always true)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      x = 42\n      y = x.as?(Int32)\n      y || 84\n      CRYSTAL\n  end\n\n  it \"does upcast\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def bar\n          2\n        end\n      end\n\n      foo = Bar.new.as?(Foo)\n      if foo\n        foo.bar\n      else\n        3\n      end\n      CRYSTAL\n  end\n\n  it \"does cast to nil (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      x = 1\n      y = x.as?(Nil)\n      y ? 2 : 3\n      CRYSTAL\n  end\n\n  it \"does cast to nil (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      x = nil\n      y = x.as?(Nil)\n      y ? 2 : 3\n      CRYSTAL\n  end\n\n  it \"types as? with wrong type (#2775)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      x = 1.as?(String)\n      x ? 10 : 20\n      CRYSTAL\n  end\n\n  it \"codegens with NoReturn\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo\n        LibC.exit.as?(Int32)\n        10\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"upcasts type to virtual (#3304)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      f = Foo.new.as?(Foo)\n      f ? f.foo : 10\n      CRYSTAL\n  end\n\n  it \"upcasts type to virtual (2) (#3304)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      class Gen(T)\n        def self.cast(x)\n          x.as?(T)\n        end\n      end\n\n      f = Gen(Foo).cast(Foo.new)\n      f ? f.foo : 10\n      CRYSTAL\n  end\n\n  it \"casts with block var that changes type (#3341)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Object\n        def try\n          yield self\n        end\n      end\n\n      class Foo\n      end\n\n      x = Foo.new.as(Int32 | Foo)\n      x.try &.as?(Foo)\n      CRYSTAL\n  end\n\n  it \"casts union type to nilable type (#9342)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      struct Nil\n        def foo\n          0\n        end\n      end\n\n      class Gen(T)\n        def initialize(@value : Int32)\n        end\n\n        def foo\n          @value\n        end\n      end\n\n      a = Gen(String).new(10) || Gen(Int32).new(20)\n      a.as?(Gen).foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/no_return_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: no return\" do\n  it \"codegens if with NoReturn on then and union on else\" do\n    run(\"lib LibC; fun exit(c : Int32) : NoReturn; end; (if 1 == 2; LibC.exit(1); else; 1 || 2.5; end).to_i!\").to_i.should eq(1)\n  end\n\n  it \"codegens Pointer(NoReturn).malloc\" do\n    run(\"Pointer(NoReturn).malloc(1_u64); 1\").to_i.should eq(1)\n  end\n\n  it \"codegens if with no return and variable used afterwards\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        fun exit2 : NoReturn\n      end\n\n      if (a = LibC.exit2) && a.size == 3\n      end\n      CRYSTAL\n  end\n\n  it \"codegen types exception handler as NoReturn if ensure is NoReturn\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      lib LibC\n        fun foo : NoReturn\n      end\n\n      begin\n        1\n      ensure\n        LibC.foo\n      end\n      CRYSTAL\n  end\n\n  it \"codegens no return variable declaration (#1508)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      foo = uninitialized NoReturn\n      1\n      CRYSTAL\n  end\n\n  it \"codegens no return instance variable declaration (#1508)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize\n          @foo = uninitialized NoReturn\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"codegens call with no return because of falsey if (#3661)\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      def bar(x)\n        x\n      end\n\n      def foo\n        bar(yield 1)\n      end\n\n      foo do |x|\n        LibC.exit(0) unless false\n      end\n      CRYSTAL\n  end\n\n  it \"codegens untyped typeof (#5105)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      typeof(raise(\"\").foo)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/not_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: not\" do\n  it \"codegens not number\" do\n    run(\"!1\").to_b.should be_false\n  end\n\n  it \"codegens not true\" do\n    run(\"!true\").to_b.should be_false\n  end\n\n  it \"codegens not false\" do\n    run(\"!false\").to_b.should be_true\n  end\n\n  it \"codegens not nil\" do\n    run(\"!nil\").to_b.should be_true\n  end\n\n  it \"codegens not nilable type (true)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n      end\n\n      a = 1 == 2 ? Foo.new : nil\n      !a\n      CRYSTAL\n  end\n\n  it \"codegens not nilable type (false)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo\n      end\n\n      a = 1 == 1 ? Foo.new : nil\n      !a\n      CRYSTAL\n  end\n\n  it \"codegens not pointer (true)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      !Pointer(Int32).new(0_u64)\n      CRYSTAL\n  end\n\n  it \"codegens not pointer (false)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      !Pointer(Int32).new(1_u64)\n      CRYSTAL\n  end\n\n  it \"doesn't crash\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      a = 1\n      !a.is_a?(String) && !a\n      CRYSTAL\n  end\n\n  it \"codegens not with inlinable value (#6451)\" do\n    codegen(<<-CRYSTAL)\n      class Test\n        def test\n          false\n        end\n      end\n\n      !Test.new.test\n      nil\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/offsetof_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: offsetof\" do\n  it \"returns offset allowing manual access of first struct field\" do\n    code = \"struct Foo; @x = 42; def x; @x; end; end;\n            f = Foo.new\n            (pointerof(f).as(Void*) + offsetof(Foo, @x).to_i64()).as(Int32*).value == f.x\"\n\n    run(code).to_b.should be_true\n  end\n\n  it \"returns offset allowing manual access of struct field that isn't first\" do\n    code = \"struct Foo; @x = 1; @y = 42; def x; @x; end; def y; @y; end; end;\n            f = Foo.new\n            (pointerof(f).as(Void*) + offsetof(Foo, @y).to_i64()).as(Int32*).value == f.y\"\n\n    run(code).to_b.should be_true\n  end\n\n  it \"returns offset allowing manual access of first class field\" do\n    code = \"class Bar; @x = 42; def x; @x; end; end;\n            b = Bar.new\n            (b.as(Void*) + offsetof(Bar, @x).to_i64()).as(Int32*).value == b.x\"\n\n    run(code).to_b.should be_true\n  end\n\n  it \"returns offset allowing manual access of class field that isn't first\" do\n    code = \"class Bar; @x = 1; @y = 42; def x; @x; end; def y; @y; end; end;\n            b = Bar.new\n            (b.as(Void*) + offsetof(Bar, @y).to_i64()).as(Int32*).value == b.y\"\n\n    run(code).to_b.should be_true\n  end\n\n  it \"returns offset allowing manual access of tuple items\" do\n    code = \"foo = {1, 2_i8, 3}\n            (pointerof(foo).as(Void*) + offsetof({Int32,Int8,Int32}, 2).to_i64).as(Int32*).value == 3\"\n\n    run(code).to_b.should be_true\n  end\n\n  it \"returns offset of extern union\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      @[Extern(union: true)]\n      struct Foo\n        @x = 1.0_f32\n        @y = uninitialized UInt32\n\n        def y\n          @y\n        end\n      end\n\n      f = Foo.new\n      (pointerof(f).as(Void*) + offsetof(Foo, @y).to_i64).as(UInt32*).value == f.y\n      CRYSTAL\n  end\n\n  it \"returns offset of `StaticArray#@buffer`\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      x = uninitialized Int32[4]\n      pointerof(x.@buffer).value = 12345\n      (pointerof(x).as(Void*) + offsetof(Int32[4], @buffer).to_i64).as(Int32*).value == x.@buffer\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/op_assign_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: op assign\" do\n  it \"evaluates exps once (#3398)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@value = 0\n\n        def self.value\n          @@value\n        end\n\n        def self.value=(@@value)\n        end\n      end\n\n      class Foo\n        def bar=(bar)\n        end\n\n        def bar\n          0\n        end\n      end\n\n      def foo\n        Global.value &+= 1\n        Foo.new\n      end\n\n      foo.bar &+= 2\n\n      Global.value\n      CRYSTAL\n  end\n\n  it \"evaluates exps once, [] (#3398)\" do\n    run(<<-CRYSTAL).to_i.should eq(11)\n      class Global\n        @@value = 0\n\n        def self.value\n          @@value\n        end\n\n        def self.value=(@@value)\n        end\n      end\n\n      class Foo\n        def [](v)\n          0\n        end\n\n        def []=(k, v)\n        end\n      end\n\n      def foo\n        Global.value &+= 1\n        Foo.new\n      end\n\n      def bar\n        Global.value &+= 10\n        0\n      end\n\n      foo[bar] &+= 2\n\n      Global.value\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/or_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: or\" do\n  it \"codegens or with bool false and false\" do\n    run(\"false || false\").to_b.should be_false\n  end\n\n  it \"codegens or with bool false and true\" do\n    run(\"false || true\").to_b.should be_true\n  end\n\n  it \"codegens or with bool true and true\" do\n    run(\"true || true\").to_b.should be_true\n  end\n\n  it \"codegens or with bool true and false\" do\n    run(\"true || false\").to_b.should be_true\n  end\n\n  it \"codegens or with bool and int 1\" do\n    run(\"struct Bool; def to_i!; 0; end; end; (false || 2).to_i!\").to_i.should eq(2)\n  end\n\n  it \"codegens or with bool and int 2\" do\n    run(\"struct Bool; def to_i!; 0; end; end; (true || 2).to_i!\").to_i.should eq(0)\n  end\n\n  it \"codegens or with primitive type other than bool\" do\n    run(\"1 || 2\").to_i.should eq(1)\n  end\n\n  it \"codegens or with primitive type other than bool with union\" do\n    run(\"(1 || 1.5).to_f\").to_f64.should eq(1)\n  end\n\n  it \"codegens or with primitive type other than bool\" do\n    run(\"require \\\"nil\\\"; (nil || 2).to_i!\").to_i.should eq(2)\n  end\n\n  it \"codegens or with nilable as left node 1\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"nil\"\n      class Object; def to_i!; -1; end; end\n      a = Reference.new\n      a = nil\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with nilable as left node 2\" do\n    run(<<-CRYSTAL).to_i.should eq(-1)\n      class Object; def to_i!; -1; end; end\n      a = nil\n      a = Reference.new\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with non-false union as left node\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1.5\n      a = 1\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with nil union as left node 1\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"nil\"\n      a = nil\n      a = 1\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with nil union as left node 2\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"nil\"\n      a = 1\n      a = nil\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 1\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Bool; def to_i!; 0; end; end\n      a = false\n      a = 1\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 2\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Bool; def to_i!; 0; end; end\n      a = 1\n      a = false\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 3\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Bool; def to_i!; 0; end; end\n      a = 1\n      a = true\n      (a || 2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 1\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = false\n      a = nil\n      a = 2\n      (a || 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 2\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = nil\n      a = 2\n      a = false\n      (a || 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 3\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = nil\n      a = 2\n      a = true\n      (a || 3).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens or with bool union as left node 4\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"nil\"\n      struct Bool; def to_i!; 1; end; end\n      a = 2\n      a = true\n      a = nil\n      (a || 3).to_i!\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/pointer_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: pointer\" do\n  it \"get pointer and value of it\" do\n    run(\"a = 1; b = pointerof(a); b.value\").to_i.should eq(1)\n  end\n\n  it \"get pointer of instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        def initialize(value : Int32)\n          @value = value\n        end\n\n        def value_ptr\n          pointerof(@value)\n        end\n      end\n\n      foo = Foo.new(10)\n      value_ptr = foo.value_ptr\n      value_ptr.value\n      CRYSTAL\n  end\n\n  it \"set pointer value\" do\n    run(\"a = 1; b = pointerof(a); b.value = 2; a\").to_i.should eq(2)\n  end\n\n  it \"get value of pointer to union\" do\n    run(\"a = 1.1; a = 1; b = pointerof(a); b.value.to_i!\").to_i.should eq(1)\n  end\n\n  it \"sets value of pointer to union\" do\n    run(\"p = Pointer(Int32|Float64).malloc(1_u64); a = 1; a = 2.5; p.value = a; p.value.to_i!\").to_i.should eq(2)\n  end\n\n  it \"increments pointer\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize\n          @a = 1\n          @b = 2\n        end\n        def value\n          p = pointerof(@a)\n          p += 1_i64\n          p.value\n        end\n      end\n      Foo.new.value\n      CRYSTAL\n  end\n\n  it \"codegens malloc\" do\n    run(\"p = Pointer(Int32).malloc(10_u64); p.value = 1; p.value &+ 1_i64\").to_i.should eq(2)\n  end\n\n  it \"codegens realloc\" do\n    run(\"p = Pointer(Int32).malloc(10_u64); p.value = 1; x = p.realloc(20_u64); x.value &+ 1_i64\").to_i.should eq(2)\n  end\n\n  it \"codegens pointer cast\" do\n    run(\"a = 1_i64; pointerof(a).as(Int32*).value\").to_i.should eq(1)\n  end\n\n  it \"codegens pointer cast to Nil (#8015)\" do\n    run(\"a = 1_i64; pointerof(a).as(Nil).nil? ? 3 : 7\").to_i.should eq(3)\n  end\n\n  it \"codegens pointer as if condition\" do\n    run(\"a = 0; pointerof(a) ? 1 : 2\").to_i.should eq(1)\n  end\n\n  it \"codegens null pointer as if condition\" do\n    run(\"Pointer(Int32).new(0_u64) ? 1 : 2\").to_i.should eq(2)\n  end\n\n  it \"gets pointer of instance variable in virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize\n          @a = 1\n        end\n\n        def foo\n          pointerof(@a)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Foo.new || Bar.new\n      x = foo.foo\n      x.value\n      CRYSTAL\n  end\n\n  it \"sets value of pointer to struct\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n      lib LibC\n        struct Color\n          r, g, b, a : UInt8\n        end\n      end\n\n      color = Pointer(LibC::Color).malloc(1_u64)\n      color.value.r = 10_u8\n\n      color2 = Pointer(LibC::Color).malloc(1_u64)\n      color2.value.r = 20_u8\n\n      color.value = color2.value\n\n      color.value.r\n      CRYSTAL\n  end\n\n  it \"changes through var and reads from pointer\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      x = 1\n      px = pointerof(x)\n      x = 2\n      px.value\n      CRYSTAL\n  end\n\n  it \"creates pointer by address\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      x = Pointer(Int32).new(123_u64)\n      x.address\n      CRYSTAL\n  end\n\n  it \"calculates pointer diff\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      x = 1\n      (pointerof(x) + 1_i64) - pointerof(x)\n      CRYSTAL\n  end\n\n  it \"can dereference pointer to func\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo; 1; end\n      x = ->foo\n      y = pointerof(x)\n      y.value.call\n      CRYSTAL\n  end\n\n  it \"gets pointer of argument that is never assigned to\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(x)\n        pointerof(x)\n      end\n\n      foo(1)\n      1\n      CRYSTAL\n  end\n\n  it \"codegens nilable pointer type (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      p = Pointer(Int32).malloc(1_u64)\n      p.value = 3\n      a = 1 == 2 ? nil : p\n      if a\n        a.value\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"codegens nilable pointer type (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      p = Pointer(Int32).malloc(1_u64)\n      p.value = 3\n      a = 1 == 1 ? nil : p\n      if a\n        a.value\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"codegens nilable pointer type dispatch (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo(x : Pointer)\n        x.value\n      end\n\n      def foo(x : Nil)\n        0\n      end\n\n      p = Pointer(Int32).malloc(1_u64)\n      p.value = 3\n      a = 1 == 1 ? p : nil\n      foo(a)\n      CRYSTAL\n  end\n\n  it \"codegens nilable pointer type dispatch (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      def foo(x : Pointer)\n        x.value\n      end\n\n      def foo(x : Nil)\n        0\n      end\n\n      p = Pointer(Int32).malloc(1_u64)\n      p.value = 3\n      a = 1 == 1 ? nil : p\n      foo(a)\n      CRYSTAL\n  end\n\n  it \"assigns nil and pointer to nilable pointer type\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize\n        end\n\n        def x=(@x : Int32*?)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      p = Pointer(Int32).malloc(1_u64)\n      p.value = 3\n\n      foo = Foo.new\n      foo.x = nil\n      foo.x = p\n      z = foo.x\n      if z\n        p.value\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"gets pointer to constant\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n      FOO = 1\n      pointerof(FOO).value\n      CRYSTAL\n  end\n\n  it \"passes pointer of pointer to method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(x)\n        x.value.value\n      end\n\n      p = Pointer(Pointer(Int32)).malloc(1_u64)\n      p.value = Pointer(Int32).malloc(1_u64)\n      p.value.value = 1\n      foo p\n      CRYSTAL\n  end\n\n  it \"codegens pointer as if condition inside union (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      ptr = Pointer(Int32).new(0_u64) || Pointer(Float64).new(0_u64)\n      if ptr\n        1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"codegens pointer as if condition inside union (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(30)\n      if 1 == 1\n        ptr = Pointer(Int32).new(0_u64)\n      else\n        ptr = 10\n      end\n      ptr ? 20 : 30\n      CRYSTAL\n  end\n\n  it \"can use typedef pointer value get and set (#630)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        type MyObj = Int32*\n        fun foo : MyObj\n      end\n\n      LibFoo.foo.value\n      LibFoo.foo.value = 1\n      CRYSTAL\n  end\n\n  it \"does pointerof class variable\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @@a = 1\n\n        def self.a_ptr\n          pointerof(@@a)\n        end\n\n        def self.a\n          @@a\n        end\n      end\n\n      Foo.a_ptr.value = 2\n      Foo.a\n      CRYSTAL\n  end\n\n  it \"does pointerof class variable with class\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Bar\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        @@a = Bar.new(1)\n\n        def self.a_ptr\n          pointerof(@@a)\n        end\n\n        def self.a\n          @@a\n        end\n      end\n\n      Foo.a_ptr.value = Bar.new(2)\n      Foo.a.x\n      CRYSTAL\n  end\n\n  it \"does pointerof read variable\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      pointerof(foo.@x).value = 123\n      foo.x\n      CRYSTAL\n  end\n\n  it \"can assign nil to void pointer\" do\n    codegen(<<-CRYSTAL)\n      ptr = Pointer(Void).malloc(1_u64)\n      ptr.value = ptr.value\n      CRYSTAL\n  end\n\n  it \"can pass any pointer to something expecting void* in lib call\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo(x : Void*) : Float64\n      end\n\n      LibFoo.foo(Pointer(Int32).malloc(1_u64))\n      CRYSTAL\n  end\n\n  it \"can pass any pointer to something expecting void* in lib call, with to_unsafe\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo(x : Void*) : Float64\n      end\n\n      class Foo\n        def to_unsafe\n          Pointer(Int32).malloc(1_u64)\n        end\n      end\n\n      LibFoo.foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"uses correct llvm module for typedef metaclass (#2877)\" do\n    run(<<-CRYSTAL)\n      lib LibFoo\n        type Foo = Void*\n        type Bar = Void*\n      end\n\n      class Class\n        def foo\n          foo(1)\n        end\n\n        def foo(x)\n        end\n      end\n\n      struct Pointer\n        def foo\n          T.foo\n        end\n      end\n\n      foo = uninitialized LibFoo::Foo*\n      bar = uninitialized LibFoo::Bar*\n      foo.foo\n      bar.foo\n      1\n      CRYSTAL\n  end\n\n  it \"passes arguments correctly for typedef metaclass (#8544)\" do\n    run <<-CRYSTAL\n      lib LibFoo\n        type Foo = Void*\n      end\n\n      class Class\n        def foo(x)\n          x\n        end\n      end\n\n      x = 1\n      LibFoo::Foo.foo(x)\n      Pointer(Void).foo(x)\n      CRYSTAL\n  end\n\n  it \"generates correct code for Pointer.malloc(0) (#2905)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize(@value : Int32)\n        end\n\n        def value\n          @value\n        end\n      end\n\n      foo = Foo.new(3)\n      Pointer(Int32 | UInt8[9]).malloc(0_u64)\n      foo.value\n      CRYSTAL\n  end\n\n  it \"compares pointers through typedef\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Comparable(T)\n        def ==(other : T)\n          (self <=> other) == 0\n        end\n      end\n\n      struct Pointer(T)\n        include Comparable(Pointer)\n\n        def <=>(other : Pointer)\n          0\n        end\n      end\n\n      lib LibFoo\n        type Ptr = Void*\n      end\n\n      ptr = Pointer(Void).malloc(1_u64).as(LibFoo::Ptr)\n      ptr == ptr\n      CRYSTAL\n  end\n\n  # FIXME: `$external_var` implies __declspec(dllimport), but we only have an\n  # object file, so MinGW-w64 fails linking (actually MSVC also emits an\n  # LNK4217 linker warning)\n  {% unless flag?(:win32) && flag?(:gnu) %}\n    it \"takes pointerof lib external var\" do\n      test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(111))\n        int external_var = 0;\n        C\n        lib LibFoo\n          $external_var : Int32\n        end\n\n        LibFoo.external_var = 1\n\n        ptr = pointerof(LibFoo.external_var)\n        x = ptr.value\n\n        ptr.value = 10\n        y = ptr.value\n\n        ptr.value = 100\n        z = LibFoo.external_var\n\n        x + y + z\n        CRYSTAL\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/compiler/codegen/previous_def_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"codegen: previous_def\" do\n  it \"codegens previous def\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        1\n      end\n\n      def foo\n        previous_def &+ 1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens previous def when inside fun and forwards args\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      def foo(z)\n        z &+ 1\n      end\n\n      def foo(z)\n        ->(x : Int32) { x &+ previous_def }\n      end\n\n      x = foo(2)\n      x.call(3)\n      CRYSTAL\n  end\n\n  it \"codegens previous def when inside fun with self\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def bar\n          @x\n        end\n      end\n\n      class Foo\n        def bar\n          x = ->{ previous_def }\n        end\n      end\n\n      Foo.new.bar.call\n      CRYSTAL\n  end\n\n  it \"correctly passes named arguments\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      def foo(x, *args, other = 1)\n        other\n      end\n\n      def foo(x, *args, other = 1)\n        previous_def\n      end\n\n      foo(1, 2, 3, other: 4)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/primitives_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: primitives\" do\n  it \"codegens bool\" do\n    run(\"true\").to_b.should be_true\n    run(\"false\").to_b.should be_false\n  end\n\n  it \"codegens int\" do\n    run(\"1\").to_i.should eq(1)\n  end\n\n  it \"codegens long\" do\n    run(\"1_i64\").to_i.should eq(1)\n  end\n\n  it \"codegens int128\" do\n    # LLVM's JIT doesn't seem to support 128\n    # bit integers well regarding GenericValue\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      1_i128.to_i\n      CRYSTAL\n  end\n\n  it \"codegens uint128\" do\n    # LLVM's JIT doesn't seem to support 128\n    # bit integers well regarding GenericValue\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      1_u128.to_i\n      CRYSTAL\n  end\n\n  it \"codegens char\" do\n    run(\"'a'\").to_i.should eq('a'.ord)\n  end\n\n  it \"codegens char ord\" do\n    run(\"'a'.ord\").to_i.should eq('a'.ord)\n  end\n\n  it \"codegens f32\" do\n    run(\"2.5_f32\").to_f32.should eq(2.5_f32)\n  end\n\n  it \"codegens f64\" do\n    run(\"2.5_f64\").to_f64.should eq(2.5_f64)\n  end\n\n  it \"codegens string\" do\n    run(%(\"foo\")).to_string.should eq(\"foo\")\n  end\n\n  describe \"arithmetic primitives\" do\n    # more detailed tests are done through the `primitives_spec` suite on a new\n    # generation of the compiler\n\n    it \"codegens 1 + 2\" do\n      run(%(require \"prelude\"; 1 + 2)).to_i.should eq(3)\n    end\n\n    it \"codegens 1 &+ 2\" do\n      run(%(1 &+ 2)).to_i.should eq(3)\n    end\n\n    it \"codegens 1 - 2\" do\n      run(%(require \"prelude\"; 1 - 2)).to_i.should eq(-1)\n    end\n\n    it \"codegens 1 &- 2\" do\n      run(%(1 &- 2)).to_i.should eq(-1)\n    end\n\n    it \"codegens 2 * 3\" do\n      run(%(require \"prelude\"; 2 * 3)).to_i.should eq(6)\n    end\n\n    it \"codegens 2 &* 3\" do\n      run(%(2 &* 3)).to_i.should eq(6)\n    end\n\n    it \"codegens 8.unsafe_div 3\" do\n      run(%(8.unsafe_div 3)).to_i.should eq(2)\n    end\n\n    it \"codegens 8.unsafe_mod 3\" do\n      run(%(10.unsafe_mod 3)).to_i.should eq(1)\n    end\n\n    it \"codegens 16.unsafe_shr 2\" do\n      run(%(16.unsafe_shr 2)).to_i.should eq(4)\n    end\n\n    it \"codegens 16.unsafe_shl 2\" do\n      run(%(16.unsafe_shl 2)).to_i.should eq(64)\n    end\n\n    it \"codegens 1.to_i16!\" do\n      run(\"1.to_i16!\").to_i.should eq(1)\n    end\n\n    it \"codegens 1.to_i16\" do\n      run(%(require \"prelude\"; 1.to_i16)).to_i.should eq(1)\n    end\n\n    it \"codegens 1.to_f!\" do\n      run(\"1.to_f!\").to_f64.should eq(1.0)\n    end\n\n    it \"codegens 1.to_f\" do\n      run(%(require \"prelude\"; 1.to_f)).to_f64.should eq(1.0)\n    end\n\n    it \"skips bounds checking when to_i produces same type\" do\n      run(\"1.to_i32\").to_i.should eq(1)\n    end\n  end\n\n  it \"defined method that calls primitive (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Int64\n        def foo\n          to_u64!\n        end\n      end\n\n      a = 1_i64\n      a.foo.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens __LINE__\" do\n    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(2)\n\n      __LINE__\n      CRYSTAL\n  end\n\n  it \"codegens crystal_type_id with union type\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.allocate || Bar.allocate\n      f.crystal_type_id == Foo.allocate.crystal_type_id\n      CRYSTAL\n  end\n\n  it \"doesn't treat `(1 == 1) == true` as `1 == 1 == true` (#328)\" do\n    run(\"(1 == 1) == true\").to_b.should be_true\n  end\n\n  it \"passes issue #328\" do\n    run(\"((1 == 1) != (2 == 2))\").to_b.should be_false\n  end\n\n  pending \"codegens pointer of int\" do\n    run(<<-CRYSTAL).to_i.should eq(5)\n      ptr = Pointer(Int).malloc(1_u64)\n      ptr.value = 1\n      ptr.value = 2_u8\n      ptr.value = 3_u16\n      ptr.value = 4_u32\n      (ptr.value + 1).to_i32\n      CRYSTAL\n  end\n\n  pending \"sums two numbers out of an [] of Number\" do\n    run(<<-CRYSTAL).to_f32.should eq(2.5)\n      p = Pointer(Number).malloc(2_u64)\n      p.value = 1\n      (p + 1_i64).value = 1.5\n\n      (p.value + (p + 1_i64).value).to_f32\n      CRYSTAL\n  end\n\n  it \"codegens crystal_type_id for class\" do\n    codegen(%(String.crystal_type_id))\n  end\n\n  it \"can invoke cast on primitive typedef (#614)\" do\n    codegen(<<-CRYSTAL)\n      lib Test\n        type K = Int32\n        fun foo : K\n      end\n\n      Test.foo.to_i!\n      CRYSTAL\n  end\n\n  it \"can invoke binary on primitive typedef (#614)\" do\n    codegen(<<-CRYSTAL)\n      lib Test\n        type K = Int32\n        fun foo : K\n      end\n\n      Test.foo &+ 1\n      CRYSTAL\n  end\n\n  it \"can invoke binary on primitive typedef (2) (#16097)\" do\n    codegen(<<-CRYSTAL)\n      lib Test\n        type K = Int32\n        fun foo : K\n      end\n\n      Test.foo == Test.foo\n      CRYSTAL\n  end\n\n  it \"can invoke pointer primitives on typedef\" do\n    codegen(<<-CRYSTAL)\n      lib Test\n        type K = Void*\n        fun foo : K\n      end\n\n      Test.foo + 1\n      Test.foo - Test.foo\n      Test.foo.realloc(1)\n      CRYSTAL\n  end\n\n  it \"can invoke struct setter on primitive typedef\" do\n    codegen(<<-CRYSTAL)\n      lib Test\n        struct Foo\n          x : Int32\n        end\n\n        type K = Foo\n        fun foo : K\n      end\n\n      Test.foo.x = 1\n      CRYSTAL\n  end\n\n  pending \"can invoke proc call on primitive typedef\" do\n    codegen(<<-CRYSTAL)\n      lib Test\n        type K = ->\n        fun foo : K\n      end\n\n      Test.foo.call\n      CRYSTAL\n  end\n\n  it \"allows redefining a primitive method\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      struct Int32\n        def *(other : Int32)\n          42\n        end\n      end\n\n      1 * 2\n      CRYSTAL\n  end\n\n  it \"doesn't optimize away call whose obj is not passed as self (#2226)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo\n        Global.x = 2\n        3\n      end\n\n      foo.class.crystal_type_id\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"uses built-in llvm function that returns a tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      lib Intrinsics\n        fun sadd_i32_with_overflow = \"llvm.sadd.with.overflow.i32\"(a : Int32, b : Int32) : {Int32, Bool}\n      end\n\n      x, o = Intrinsics.sadd_i32_with_overflow(1, 2)\n      x\n      CRYSTAL\n  end\n\n  it \"gets crystal class instance type id\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n      end\n\n      Foo.new.crystal_type_id == Foo.crystal_instance_type_id\n      CRYSTAL\n  end\n\n  describe \"va_arg\" do\n    # On Windows and AArch64 llvm's va_arg instruction works incorrectly.\n    {% unless flag?(:win32) || flag?(:aarch64) %}\n      it \"uses llvm's va_arg instruction\" do\n        mod = codegen(<<-CRYSTAL)\n          struct VaList\n            @[Primitive(:va_arg)]\n            def next(type)\n            end\n          end\n\n          list = VaList.new\n          list.next(Int32)\n          CRYSTAL\n        type = {% if LibLLVM::IS_LT_150 %} \"%VaList*\" {% else %} \"ptr\" {% end %}\n        str = mod.to_s\n        str.should contain(\"va_arg #{type} %list\")\n      end\n\n      it \"works with C code\" do\n        test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(6))\n          extern int foo_f(int,...);\n          int foo() {\n            return foo_f(3,1,2,3);\n          }\n          C\n          lib LibFoo\n            fun foo() : LibC::Int\n          end\n\n          fun foo_f(count : Int32, ...) : LibC::Int\n            sum = 0\n            VaList.open do |list|\n              count.times do |i|\n                sum += list.next(Int32)\n              end\n            end\n            sum\n          end\n\n          LibFoo.foo\n          CRYSTAL\n      end\n    {% end %}\n  end\n\n  describe \"atomicrmw\" do\n    it \"codegens atomicrmw with enums\" do\n      run(<<-CRYSTAL).to_i.should eq(3)\n        enum RMWBinOp\n          Add = 1\n        end\n\n        enum Ordering\n          SequentiallyConsistent = 7\n        end\n\n        @[Primitive(:atomicrmw)]\n        def atomicrmw(op : RMWBinOp, ptr : Int32*, val : Int32, ordering : Ordering, singlethread : Bool) : Int32\n        end\n\n        x = 1\n        atomicrmw(:add, pointerof(x), 2, :sequentially_consistent, false)\n        x\n        CRYSTAL\n    end\n\n    it \"codegens atomicrmw with enums\" do\n      run(<<-CRYSTAL).to_i.should eq(3)\n        enum RMWBinOp\n          Add = 1\n        end\n\n        enum Ordering\n          SequentiallyConsistent = 7\n        end\n\n        @[Primitive(:atomicrmw)]\n        def atomicrmw(op : RMWBinOp, ptr : Int32*, val : Int32, ordering : Ordering, singlethread : Bool) : Int32\n        end\n\n        x = 1\n        atomicrmw(RMWBinOp::Add, pointerof(x), 2, Ordering::SequentiallyConsistent, false)\n        x\n        CRYSTAL\n    end\n\n    # TODO: remove once support for 1.4 is dropped\n    it \"codegens atomicrmw with symbols\" do\n      run(<<-CRYSTAL).to_i.should eq(3)\n        @[Primitive(:atomicrmw)]\n        def atomicrmw(op : Symbol, ptr : Int32*, val : Int32, ordering : Symbol, singlethread : Bool) : Int32\n        end\n\n        x = 1\n        atomicrmw(:add, pointerof(x), 2, :sequentially_consistent, false)\n        x\n        CRYSTAL\n    end\n  end\n\n  it \"allows @[Primitive] on method that has body\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hello\")\n      module Moo\n        @[Primitive(:symbol_to_s)]\n        def self.symbol_to_s(symbol : Symbol) : String\n          untyped\n        end\n      end\n\n      Moo.symbol_to_s(:hello)\n      CRYSTAL\n  end\n\n  it \"allows @[Primitive] on fun declarations\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibFoo\n        @[Primitive(:enum_value)]\n        fun enum_value(x : Int32) : Int32\n      end\n\n      LibFoo.enum_value(1)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/private_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: private\" do\n  it \"codegens private def in same file\" do\n    compile(<<-CRYSTAL)\n      private def foo\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens overloaded private def in same file\" do\n    compile(<<-CRYSTAL)\n      private def foo(x : Int32)\n        1\n      end\n\n      private def foo(x : Char)\n        2\n      end\n\n      a = 3 || 'a'\n      foo a\n      CRYSTAL\n  end\n\n  it \"codegens class var of private type with same name as public type (#11620)\" do\n    compile(<<-CRYSTAL, <<-CRYSTAL)\n      module Foo\n        @@x = true\n      end\n    CRYSTAL\n      private module Foo\n        @@x = 1\n      end\n    CRYSTAL\n  end\n\n  it \"codegens class vars of private types with same name (#11620)\" do\n    compile(<<-CRYSTAL, <<-CRYSTAL)\n      private module Foo\n        @@x = true\n      end\n    CRYSTAL\n      private module Foo\n        @@x = 1\n      end\n    CRYSTAL\n  end\n\n  it \"doesn't include filename for private types\" do\n    run(<<-CRYSTAL, filename: \"foo\").to_string.should eq(\"Foo\")\n      private class Foo\n        def foo\n          {{@type.stringify}}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/proc_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: proc\" do\n  it \"call simple proc literal\" do\n    run(\"x = -> { 1 }; x.call\").to_i.should eq(1)\n  end\n\n  it \"call proc literal with arguments\" do\n    run(\"f = ->(x : Int32) { x &+ 1 }; f.call(41)\").to_i.should eq(42)\n  end\n\n  it \"call proc literal with return type\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      f = -> : Int32 | Float64 { 1 }\n      x = f.call\n      x.is_a?(Int32) && x == 1\n      CRYSTAL\n  end\n\n  it \"call proc pointer\" do\n    run(\"def foo; 1; end; x = ->foo; x.call\").to_i.should eq(1)\n  end\n\n  it \"call proc pointer with args\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo(x, y)\n        x &+ y\n      end\n\n      f = ->foo(Int32, Int32)\n      f.call(1, 2)\n      CRYSTAL\n  end\n\n  it \"call proc pointer of instance method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def coco\n          @x\n        end\n      end\n\n      foo = Foo.new\n      f = ->foo.coco\n      f.call\n      CRYSTAL\n  end\n\n  it \"call proc pointer of instance method that raises\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n      class Foo\n        def coco\n          raise \"foo\"\n        end\n      end\n\n      foo = Foo.new\n      f = ->foo.coco\n      f.call rescue 1\n      CRYSTAL\n  end\n\n  it \"codegens proc with another var\" do\n    run(<<-CRYSTAL)\n      def foo(x)\n        bar(x, -> {})\n      end\n\n      def bar(x, proc)\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"codegens proc that returns a virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def coco; 1; end\n      end\n\n      class Bar < Foo\n        def coco; 2; end\n      end\n\n      x = -> { Foo.new || Bar.new }\n      x.call.coco\n      CRYSTAL\n  end\n\n  it \"codegens proc that accepts a union and is called with a single type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Float\n        def &+(other)\n          self + other\n        end\n      end\n\n      f = ->(x : Int32 | Float64) { x &+ 1 }\n      f.call(1).to_i!\n      CRYSTAL\n  end\n\n  it \"makes sure that proc pointer is transformed after type inference\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Bar\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        def on_something\n          Bar.new(1)\n        end\n      end\n\n      def _on_(p : Foo*)\n        p.value.on_something.x\n      end\n\n      c = ->_on_(Foo*)\n      a = Foo.new\n      c.call(pointerof(a))\n      CRYSTAL\n  end\n\n  it \"binds function pointer to associated call\" do\n    run(<<-CRYSTAL).to_i.should eq(12)\n      class Foo\n        def initialize(@e : Int32)\n        end\n\n        def on_something\n          @e\n        end\n      end\n\n      def _on_(p : Foo*)\n        p.value.on_something\n      end\n\n      c = ->_on_(Foo*)\n      a = Foo.new(12)\n      a.on_something\n\n      c.call(pointerof(a))\n      CRYSTAL\n  end\n\n  it \"call simple proc literal with return\" do\n    run(\"x = -> { return 1 }; x.call\").to_i.should eq(1)\n  end\n\n  it \"calls proc pointer with union (passed by value) arg\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Number\n        def abs; self; end\n      end\n\n      f = ->(x : Int32 | Float64) { x.abs }\n      f.call(1 || 1.5).to_i!\n      CRYSTAL\n  end\n\n  it \"allows passing proc type to C automatically\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      lib LibC\n        fun qsort(base : Void*, nel : LibC::SizeT, width : LibC::SizeT, callback : (Void*, Void* -> Int32))\n      end\n\n      ary = [3, 1, 4, 2]\n      LibC.qsort(ary.to_unsafe.as(Void*), LibC::SizeT.new(ary.size), LibC::SizeT.new(sizeof(Int32)), ->(a : Void*, b : Void*) {\n        a = a.as(Int32*)\n        b = b.as(Int32*)\n        a.value <=> b.value\n      })\n      ary[0]\n      CRYSTAL\n  end\n\n  it \"allows proc pointer where self is a class\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def self.bla\n          1\n        end\n      end\n\n      f = ->Foo.bla\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens proc literal hard type inference (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo\n        def initialize(@x : NoReturn)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      def foo(s)\n        Foo.new(s.x)\n      end\n\n      def bar\n        ->(s : Foo) { ->foo(Foo) }\n      end\n\n      bar\n\n      1\n      CRYSTAL\n  end\n\n  it \"automatically casts proc that returns something to proc that returns void\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def foo(x : ->)\n        x.call\n      end\n\n      foo ->{ Global.x = 1 }\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"allows proc type of enum type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibFoo\n        enum MyEnum\n          X = 1\n        end\n      end\n\n      ->(x : LibFoo::MyEnum) {\n        x\n      }.call(LibFoo::MyEnum::X)\n      CRYSTAL\n  end\n\n  it \"allows proc type of enum type with base type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      lib LibFoo\n        enum MyEnum : UInt16\n          X = 1\n        end\n      end\n\n      ->(x : LibFoo::MyEnum) {\n        x\n      }.call(LibFoo::MyEnum::X)\n      CRYSTAL\n  end\n\n  it \"codegens nilable proc type (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      a = 1 == 2 ? nil : ->{ 3 }\n      if a\n        a.call\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"codegens nilable proc type (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      a = 1 == 1 ? nil : ->{ 3 }\n      if a\n        a.call\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"codegens nilable proc type dispatch (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo(x : -> U) forall U\n        x.call\n      end\n\n      def foo(x : Nil)\n        0\n      end\n\n      a = 1 == 1 ? (->{ 3 }) : nil\n      foo(a)\n      CRYSTAL\n  end\n\n  it \"codegens nilable proc type dispatch (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      def foo(x : -> U) forall U\n        x.call\n      end\n\n      def foo(x : Nil)\n        0\n      end\n\n      a = 1 == 1 ? nil : ->{ 3 }\n      foo(a)\n      CRYSTAL\n  end\n\n  it \"builds proc type from fun\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun foo : ->\n      end\n\n      x = LibC.foo\n      x.call\n      CRYSTAL\n  end\n\n  it \"builds nilable proc type from fun\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun foo : (->)?\n      end\n\n      x = LibC.foo\n      if x\n        x.call\n      end\n      CRYSTAL\n  end\n\n  it \"assigns nil and proc to nilable proc type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize\n        end\n\n        def x=(@x : (-> Int32)?)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x = nil\n      foo.x = -> { 1 }\n      z = foo.x\n      if z\n        z.call\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"allows invoking proc literal with smaller type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      f = ->(x : Int32 | Nil) {\n        x\n      }\n      f.call(1).to_i!\n      CRYSTAL\n  end\n\n  it \"does new on proc type\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct Proc\n        def self.new(&block : self)\n          block\n        end\n      end\n\n      alias Func = Int32 -> Int32\n\n      a = 2\n      f = Func.new { |x| x &+ a }\n      f.call(1)\n      CRYSTAL\n  end\n\n  it \"allows invoking a function with a subtype\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def x\n          1\n        end\n      end\n\n      class Bar < Foo\n        def x\n          2\n        end\n      end\n\n      f = ->(foo : Foo) { foo.x }\n      f.call Bar.new\n      CRYSTAL\n  end\n\n  it \"allows invoking a function with a subtype when defined as block spec\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def x\n          1\n        end\n      end\n\n      class Bar < Foo\n        def x\n          2\n        end\n      end\n\n      def func(&block : Foo -> U) forall U\n        block\n      end\n\n      f = func { |foo| foo.x }\n      f.call Bar.new\n      CRYSTAL\n  end\n\n  it \"allows redefining fun\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      fun foo : Int32\n        1\n      end\n\n      fun foo : Int32\n        2\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"passes block to another function (bug: mangling of both methods was the same)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(&block : ->)\n        foo(block)\n      end\n\n      def foo(block)\n        1\n      end\n\n      foo { }\n      CRYSTAL\n  end\n\n  it \"codegens proc with union type that returns itself\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1 || 1.5\n\n      foo = ->(x : Int32 | Float64) { x }\n      foo.call(a)\n      foo.call(a).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens issue with missing byval in proc literal inside struct\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bar\")\n      require \"prelude\"\n\n      struct Params\n        def foo\n          params = [] of {String}\n          params << {\"foo\"}\n          params << {\"bar\"}\n          params.sort! { |x, y| x[0] <=> y[0] }\n          params[0][0]\n        end\n      end\n\n      Params.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens proc that references struct (bug)\" do\n    run(<<-CRYSTAL).to_i.should_not eq(42)\n      class Ref\n      end\n\n      class Context\n        def initialize\n          @x = Ref.new\n        end\n\n        def run\n          @x.object_id\n        end\n\n        def it(&block)\n          block.call\n        end\n      end\n\n      struct Foo\n        def initialize\n          @x = 0\n          @y = 0\n          @z = 42\n          @w = 0\n        end\n      end\n\n      context = Context.new\n      context.it do\n        Foo.new\n      end\n      context.run\n      CRYSTAL\n  end\n\n  it \"codegens captured block that returns tuple\" do\n    codegen(<<-CRYSTAL)\n      def foo(&block)\n        block\n      end\n\n      block = foo do\n        {0, 0, 42, 0}\n      end\n      block.call\n      CRYSTAL\n  end\n\n  it \"allows using proc arg name shadowing local variable\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1\n      f = ->(a : String) { }\n      a\n      CRYSTAL\n  end\n\n  it \"codegens proc that accepts array of type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      def foo(&block : Array(Foo) -> Foo)\n        block\n      end\n\n      block = foo { |elems| Bar.new }\n      elems = [Bar.new, Foo.new]\n      bar = block.call elems\n      bar.foo\n      CRYSTAL\n  end\n\n  it \"gets proc to lib fun (#504)\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun bar\n      end\n\n      ->LibFoo.bar\n      CRYSTAL\n  end\n\n  it \"gets proc to lib fun with parameter types\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(8))\n      #include <stdint.h>\n\n      int32_t foo(int32_t x, int32_t y) {\n        return x + y;\n      }\n      C\n      lib LibFoo\n        fun foo(x : Int32, y : Int32) : Int32\n      end\n\n      fn = ->LibFoo.foo(Int32, Int32)\n      fn.call(3, 5)\n      CRYSTAL\n  end\n\n  it \"gets proc to lib fun with compatible parameter types\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(0x1235))\n      void *foo(void *x) {\n        return (void *)((char *)x + 1);\n      }\n      C\n      lib LibFoo\n        fun foo(x : Void*) : Void*\n      end\n\n      x = Pointer(UInt8).new(0x1234)\n      fn = ->LibFoo.foo(UInt8*)\n      fn.call(x).address\n      CRYSTAL\n  end\n\n  it \"gets proc to lib fun with compatible `#to_unsafe` type\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(42))\n      #include <stdint.h>\n\n      int32_t foo(int32_t x) {\n        return x * 2;\n      }\n      C\n      lib LibFoo\n        fun foo(x : Int32) : Int32\n      end\n\n      class Foo\n        def to_unsafe\n          21\n        end\n      end\n\n      fn = ->LibFoo.foo(Foo)\n      fn.call(Foo.new)\n      CRYSTAL\n  end\n\n  it \"gets proc to variadic lib fun with parameter types\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(1110))\n      #include <stdarg.h>\n      #include <stdint.h>\n\n      int32_t foo(int32_t n, ...) {\n        va_list args;\n        va_start(args, n);\n        int32_t sum = 0;\n        for (; n > 0; --n)\n          sum += va_arg(args, int32_t);\n        return sum;\n      }\n      C\n      lib LibFoo\n        fun foo(n : Int32, ...) : Int32\n      end\n\n      fn = ->LibFoo.foo(Int32, Int32, Int32, Int32)\n      fn.call(3, 10, 100, 1000)\n      CRYSTAL\n  end\n\n  it \"gets same pointer from proc pointers to lib fun with compatible types\" do\n    test_c(<<-C, <<-CRYSTAL, &.to_b.should be_true)\n      void foo(void *x) {\n      }\n      C\n      lib LibFoo\n        fun foo(x : Void*)\n      end\n\n      a = ->LibFoo.foo\n      b = ->LibFoo.foo(Void*)\n      c = ->LibFoo.foo(UInt8*)\n      a.pointer == b.pointer && a.pointer == c.pointer\n      CRYSTAL\n  end\n\n  it \"codegens proc to implicit self in constant (#647)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      module Foo\n        def self.blah\n          1\n        end\n        H = ->{ blah }\n      end\n\n      Foo::H.call\n      CRYSTAL\n  end\n\n  it \"passes proc as &->expr to method that yields\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def foo\n        yield\n      end\n\n      foo &->{ 123 }\n      CRYSTAL\n  end\n\n  it \"mangles strings in such a way they don't conflict with funs (#1006)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      a = :foo\n\n      fun foo : Int32\n        123\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"gets proc pointer using virtual type (#1337)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      def foo(a : Foo)\n        a.foo\n      end\n\n      bar = ->foo(Foo)\n      bar.call(Bar.new)\n      CRYSTAL\n  end\n\n  it \"uses alias of proc with virtual type (#1347)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      class Class1\n        def foo\n          1\n        end\n      end\n\n      class Class2 < Class1\n        def foo\n          2\n        end\n      end\n\n      module Foo\n        alias Callback = Class1 ->\n        @@callbacks = Hash(String, Callback).new\n        def self.add(name, &block : Callback)\n          @@callbacks[name] = block\n        end\n\n        def self.call\n          @@callbacks.each_value(&.call(Class2.new))\n        end\n      end\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.add(\"foo\") do |a|\n        Global.x = a.foo\n      end\n\n      Foo.call\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #2196\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      x = 42\n      z = if x.is_a?(Int32)\n        x\n      else\n        y = x\n        ->{ y }\n      end\n      z.is_a?(Int32) ? z : 0\n      CRYSTAL\n  end\n\n  it \"accesses T in macros as a TupleLiteral\" do\n    run(<<-CRYSTAL).to_string.should eq(\"TupleLiteral\")\n      struct Proc\n        def t\n          {{ T.class_name }}\n        end\n      end\n\n      ->(x : Int32) { 'a' }.t\n      CRYSTAL\n  end\n\n  it \"codegens proc in instance var initialize (#3016)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @f : -> Int32 = ->foo\n\n        def self.foo\n          42\n        end\n      end\n\n      Foo.new.@f.call\n      CRYSTAL\n  end\n\n  it \"codegens proc of generic type\" do\n    codegen(<<-CRYSTAL)\n      class Gen(T)\n      end\n\n      class Foo < Gen(Int32)\n      end\n\n      f = ->(x : Gen(Int32)) {}\n      f.call(Foo.new)\n      CRYSTAL\n  end\n\n  it \"executes proc pointer on primitive\" do\n    run(<<-CRYSTAL).to_i.should eq(21)\n      a = 1\n      f = ->a.&+(Int32)\n      f.call(20)\n      CRYSTAL\n  end\n\n  it \"can pass Proc(T) to Proc(Nil) in type restriction (#8964)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(x : Proc(Nil))\n        x\n      end\n\n      a = 1\n      proc = foo(->{ a = 2 })\n      proc.call\n      a\n      CRYSTAL\n  end\n\n  it \"can assign proc that returns anything to proc that returns nil (#3655)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        @block : -> Nil\n\n        def initialize(@block)\n        end\n\n        def call\n          @block.call\n        end\n      end\n\n      a = 1\n      block = ->{ a = 2 }\n\n      Foo.new(block).call\n\n      a\n      CRYSTAL\n  end\n\n  it \"can assign proc that returns anything to proc that returns nil, using union type (#3655)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        @block : -> Nil\n\n        def initialize(@block)\n        end\n\n        def call\n          @block.call\n        end\n      end\n\n      a = 1\n      block1 = ->{ a = 2 }\n      block2 = ->{ a = 3; nil }\n\n      Foo.new(block2 || block1).call\n\n      a\n      CRYSTAL\n  end\n\n  it \"calls function pointer\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n\n      fun foo(f : Int32 -> Int32) : Int32\n        f.call(1)\n      end\n\n      foo(->(x : Int32) { x &+ 1 })\n      CRYSTAL\n  end\n\n  it \"casts from function pointer to proc\" do\n    codegen(<<-CRYSTAL)\n      fun a(a : Void* -> Void*)\n        Pointer(Proc((Void* -> Void*), Void*)).new(0_u64).value.call(a)\n      end\n      CRYSTAL\n  end\n\n  it \"takes pointerof function pointer\" do\n    codegen(<<-CRYSTAL)\n      fun a(a : Void* -> Void*)\n        pointerof(a).value.call(Pointer(Void).new(0_u64))\n      end\n      CRYSTAL\n  end\n\n  it \"returns proc as function pointer inside top-level fun (#14691)\" do\n    run(<<-CRYSTAL, Int32).should eq(8)\n      def raise(msg)\n        while true\n        end\n      end\n\n      fun add : Int32, Int32 -> Int32\n        ->(x : Int32, y : Int32) { x &+ y }\n      end\n\n      add.call(3, 5)\n      CRYSTAL\n  end\n\n  it \"returns ProcPointer inside top-level fun (#14691)\" do\n    run(<<-CRYSTAL, Int32).should eq(8)\n      def raise(msg)\n        while true\n        end\n      end\n\n      fun foo(x : Int32) : Int32\n        x &+ 5\n      end\n\n      fun bar : Int32 -> Int32\n        ->foo(Int32)\n      end\n\n      bar.call(3)\n      CRYSTAL\n  end\n\n  it \"raises if returning closure from top-level fun (#14691)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n\n      @[Raises]\n      fun foo(x : Int32) : -> Int32\n        -> { x }\n      end\n\n      begin\n        foo(1)\n      rescue\n        true\n      else\n        false\n      end\n      CRYSTAL\n  end\n\n  it \"closures var on ->var.call (#8584)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def bar(x)\n        x\n      end\n\n      struct Foo\n        def initialize\n          @value = 1\n        end\n\n        def value\n          bar(@value)\n          @value\n        end\n      end\n\n      def get_proc_a\n        foo = Foo.new\n        ->foo.value\n      end\n\n      def get_proc_b\n        foo = Foo.new\n        ->{ foo.value }\n      end\n\n      proc_a = get_proc_a\n      proc_b = get_proc_b\n      proc_b.call\n      proc_a.call\n      CRYSTAL\n  end\n\n  it \"saves receiver value of proc pointer `->var.foo`\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      var = Foo.new(1)\n      proc = ->var.foo\n      var = Foo.new(2)\n      proc.call\n      CRYSTAL\n  end\n\n  it \"saves receiver value of proc pointer `->@ivar.foo`\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      class Test\n        @ivar = Foo.new(1)\n\n        def test\n          proc = ->@ivar.foo\n          @ivar = Foo.new(2)\n          proc.call\n        end\n      end\n\n      Test.new.test\n      CRYSTAL\n  end\n\n  it \"saves receiver value of proc pointer `->@@cvar.foo`\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      class Foo\n        def initialize(@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      class Test\n        @@cvar = Foo.new(1)\n\n        def self.test\n          proc = ->@@cvar.foo\n          @@cvar = Foo.new(2)\n          proc.call\n        end\n      end\n\n      Test.test\n      CRYSTAL\n  end\n\n  it \"doesn't crash when taking a proc pointer to a virtual type (#9823)\" do\n    run(<<-CRYSTAL, Proc(Int32, Int32, Int32))\n      abstract struct Parent\n        abstract def work(a : Int32, b : Int32)\n\n        def get\n          ->work(Int32, Int32)\n        end\n      end\n\n      struct Child1 < Parent\n        def work(a : Int32, b : Int32)\n          a &+ b\n        end\n      end\n\n      struct Child2 < Parent\n        def work(a : Int32, b : Int32)\n          a &- b\n        end\n      end\n\n      Child1.new.as(Parent).get\n      CRYSTAL\n  end\n\n  it \"doesn't crash when taking a proc pointer that multidispatches on the top-level (#3822)\" do\n    run(<<-CRYSTAL)\n      class Foo\n        def initialize(@proc : Proc(Bar, Nil))\n        end\n      end\n\n      module Bar\n      end\n\n      class Baz\n        include Bar\n      end\n\n      def test(bar : Bar)\n        if bar.is_a? Baz\n          test bar\n        end\n      end\n\n      def test(baz : Baz)\n      end\n\n      Foo.new(->test(Bar))\n      CRYSTAL\n  end\n\n  it \"doesn't crash when taking a proc pointer that multidispatches on a module (#3822)\" do\n    run(<<-CRYSTAL)\n      class Foo\n        def initialize(@proc : Proc(Bar, Nil))\n        end\n      end\n\n      module Bar\n      end\n\n      class Baz\n        include Bar\n      end\n\n      module Moo\n        def self.test(bar : Bar)\n          if bar.is_a? Baz\n            test bar\n          end\n        end\n\n        def self.test(baz : Baz)\n        end\n      end\n\n      Foo.new(->Moo.test(Bar))\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/regex_literal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: regex literal spec\" do\n  it \"works in a class variable (#10951)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      require \"prelude\"\n      class Foo\n        @@regex = /whatever/\n\n        def self.check_regex\n          @@regex == /whatever/\n        end\n      end\n      Foo.check_regex\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/responds_to_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: responds_to?\" do\n  it \"codegens responds_to? true for simple type\" do\n    run(\"1.responds_to?(:\\\"+\\\")\").to_b.should be_true\n  end\n\n  it \"codegens responds_to? false for simple type\" do\n    run(\"1.responds_to?(:foo)\").to_b.should be_false\n  end\n\n  it \"codegens responds_to? with union gives true\" do\n    run(\"(1 == 1 ? 1 : 'a').responds_to?(:\\\"+\\\")\").to_b.should be_true\n  end\n\n  it \"codegens responds_to? with union gives false\" do\n    run(\"(1 == 1 ? 1 : 'a').responds_to?(:\\\"foo\\\")\").to_b.should be_false\n  end\n\n  it \"codegens responds_to? with nilable gives true\" do\n    run(\"struct Nil; def foo; end; end; (1 == 1 ? nil : Reference.new).responds_to?(:foo)\").to_b.should be_true\n  end\n\n  it \"codegens responds_to? with nilable gives false because other type 1\" do\n    run(\"(1 == 1 ? nil : Reference.new).responds_to?(:foo)\").to_b.should be_false\n  end\n\n  it \"codegens responds_to? with nilable gives false because other type 2\" do\n    run(\"class Reference; def foo; end; end; (1 == 2 ? nil : Reference.new).responds_to?(:foo)\").to_b.should be_true\n  end\n\n  it \"codegens responds_to? with generic class (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo(T)\n        def foo\n        end\n      end\n\n      Foo(Int32).new.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"codegens responds_to? with generic class (2)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo(T)\n        def foo\n        end\n      end\n\n      Foo(Int32).new.responds_to?(:bar)\n      CRYSTAL\n  end\n\n  it \"doesn't error if result is discarded (#14113)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n        end\n      end\n\n      (Foo.new || \"\").responds_to?(:foo)\n      1\n      CRYSTAL\n  end\n\n  it \"works with virtual type\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      foo = Bar.new || Foo.new\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with two virtual types\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar2 < Bar\n      end\n\n      class Other\n      end\n\n      class Sub < Other\n        def foo\n          3\n        end\n      end\n\n      class Sub2 < Sub\n        def foo\n          4\n        end\n      end\n\n      foo = Sub2.new || Bar.new || Bar2.new || Sub.new || Sub2.new\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with virtual class type (1) (#1926)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo\n      end\n\n      class Bar < Foo\n        def self.foo\n          1\n        end\n      end\n\n      foo = Bar || Foo\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with virtual class type (2) (#1926)\" do\n    run(<<-CRYSTAL).to_b.should be_false\n      class Foo\n      end\n\n      class Bar < Foo\n        def self.foo\n          1\n        end\n      end\n\n      foo = Foo || Bar\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with generic virtual superclass (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n\n        def foo\n          1\n        end\n      end\n\n      foo = Bar.new.as(Foo(Int32))\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with generic virtual superclass (2)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n\n        def foo\n          1\n        end\n      end\n\n      foo = Bar(Int32).new.as(Foo(Int32))\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with module\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      ptr = Pointer(Moo).malloc(1_u64)\n      ptr.value = Bar.new\n      ptr.value = Foo.new\n\n      moo = ptr.value\n      moo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with generic virtual module (1)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Foo(T)\n      end\n\n      class Bar\n        include Foo(Int32)\n\n        def foo\n          1\n        end\n      end\n\n      foo = Bar.new.as(Foo(Int32))\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"works with generic virtual module (2) (#8334)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Foo(T)\n      end\n\n      class Bar(T)\n        include Foo(T)\n\n        def foo\n          1\n        end\n      end\n\n      foo = Bar(Int32).new.as(Foo(Int32))\n      foo.responds_to?(:foo)\n      CRYSTAL\n  end\n\n  it \"does for generic instance type metaclass (#4353)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class MyGeneric(T)\n        def self.hallo\n          1\n        end\n      end\n\n      MyGeneric(String).responds_to? :hallo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/return_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: return\" do\n  it \"codegens return\" do\n    run(\"def foo; return 1; end; foo\").to_i.should eq(1)\n  end\n\n  it \"codegens return followed by another expression\" do\n    run(\"def foo; return 1; 2; end; foo\").to_i.should eq(1)\n  end\n\n  it \"codegens return inside if\" do\n    run(\"def foo; if 1 == 1; return 1; end; 2; end; foo\").to_i.should eq(1)\n  end\n\n  it \"return from function with union type\" do\n    run(\"struct Char; def to_i!; 2; end; end; def foo; return 1 if 1 == 1; 'a'; end; foo.to_i!\").to_i.should eq(1)\n  end\n\n  it \"return union\" do\n    run(\"struct Char; def to_i!; 2; end; end; def foo; 1 == 2 ? return 1 : return 'a'; end; foo.to_i!\").to_i.should eq(2)\n  end\n\n  it \"return from function with nilable type\" do\n    run(%(def foo; return Reference.new if 1 == 1; end; foo.nil?)).to_b.should be_false\n  end\n\n  it \"return from function with nilable type 2\" do\n    run(%(def foo; return Reference.new if 1 == 1; end; foo.nil?)).to_b.should be_false\n  end\n\n  it \"returns empty from function\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil; def to_i!; 0; end; end\n      def foo(x)\n        return if x == 1\n        1\n      end\n\n      foo(2).to_i!\n      CRYSTAL\n  end\n\n  it \"codegens bug with return if true\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      def bar\n        return if true\n        1\n      end\n\n      bar.is_a?(Nil)\n      CRYSTAL\n  end\n\n  it \"codegens assign with if with two returns\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def test\n        a = 1 ? return 2 : return 3\n      end\n\n      test\n      CRYSTAL\n  end\n\n  it \"doesn't crash when method returns nil and can be inlined\" do\n    codegen(<<-CRYSTAL)\n      def foo : Nil\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"returns in var assignment (#3364)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      def bar\n        a = nil || return 123\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"forms a tuple from multiple return values\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo\n        return 5, 3\n      end\n\n      v = foo\n      v[0] &- v[1]\n      CRYSTAL\n  end\n\n  it \"flattens splats inside multiple return values\" do\n    run(<<-CRYSTAL).to_i.should eq(18)\n      def foo\n        return 1, *{3, 9}, 27\n      end\n\n      v = foo\n      v[3] &- v[2]\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/sizeof_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: sizeof\" do\n  it \"gets sizeof int\" do\n    run(\"sizeof(Int32)\").to_i.should eq(4)\n  end\n\n  it \"gets sizeof struct\" do\n    run(<<-CRYSTAL).to_i.should eq(12)\n      struct Foo\n        def initialize(@x : Int32, @y : Int32, @z : Int32)\n        end\n      end\n\n      Foo.new(1, 2, 3)\n\n      sizeof(Foo)\n      CRYSTAL\n  end\n\n  it \"gets sizeof class\" do\n    # A class is represented as a pointer to its data\n    run(<<-CRYSTAL).to_i.should eq(sizeof(Void*))\n      class Foo\n        def initialize(@x : Int32, @y : Int32, @z : Int32)\n        end\n      end\n\n      Foo.new(1, 2, 3)\n\n      sizeof(Foo)\n      CRYSTAL\n  end\n\n  it \"gets sizeof union\" do\n    size = run(<<-CRYSTAL).to_i\n      sizeof(Int32 | Float64)\n      CRYSTAL\n\n    # This union is represented as:\n    #\n    #   struct {\n    #      4 bytes, # for the type id\n    #      8 bytes, # for the largest size between Int32 and Float64\n    #   }\n    #\n    # But in 64 bits structs are aligned to 8 bytes, so it'll actually\n    # be struct { 8 bytes, 8 bytes }.\n    #\n    # In 32 bits structs are aligned to 4 bytes, so it remains the same.\n    {% if flag?(:bits64) %}\n      size.should eq(16)\n    {% else %}\n      size.should eq(12)\n    {% end %}\n  end\n\n  it \"gets instance_sizeof class\" do\n    run(<<-CRYSTAL).to_i.should eq(16)\n      class Foo\n        def initialize(@x : Int32, @y : Int32, @z : Int32)\n        end\n      end\n\n      Foo.new(1, 2, 3)\n\n      instance_sizeof(Foo)\n      CRYSTAL\n  end\n\n  it \"gets instance_sizeof a generic type with type vars\" do\n    run(<<-CRYSTAL).to_i.should eq(8)\n      class Foo(T)\n        def initialize(@x : T)\n        end\n      end\n\n      instance_sizeof(Foo(Int32))\n      CRYSTAL\n  end\n\n  it \"gets sizeof Void\" do\n    # Same as the size of a byte, because doing\n    # `Pointer(Void).malloc` must work like `Pointer(UInt8).malloc`\n    run(\"sizeof(Void)\").to_i.should eq(1)\n  end\n\n  it \"gets sizeof NoReturn\" do\n    # NoReturn can't hold anything\n    run(\"sizeof(NoReturn)\").to_i.should eq(0)\n  end\n\n  it \"gets sizeof Nil (#7644)\" do\n    # Nil can't hold anything\n    run(\"sizeof(Nil)\").to_i.should eq(0)\n  end\n\n  it \"gets sizeof Bool (#8272)\" do\n    run(\"sizeof(Bool)\").to_i.should eq(1)\n  end\n\n  it \"can use sizeof in type argument (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      struct StaticArray\n        def size\n          N\n        end\n      end\n\n      x = uninitialized UInt8[sizeof(Int32)]\n      x.size\n      CRYSTAL\n  end\n\n  it \"can use sizeof in type argument (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(8)\n      struct StaticArray\n        def size\n          N\n        end\n      end\n\n      x = uninitialized UInt8[sizeof(Float64)]\n      x.size\n      CRYSTAL\n  end\n\n  it \"can use sizeof of virtual type\" do\n    size = run(<<-CRYSTAL).to_i\n      class Foo\n        @x = 1\n      end\n\n      class Bar < Foo\n        @y = 2\n      end\n\n      foo = Bar.new.as(Foo)\n      sizeof(typeof(foo))\n      CRYSTAL\n\n    {% if flag?(:bits64) %}\n      size.should eq(8)\n    {% else %}\n      size.should eq(4)\n    {% end %}\n  end\n\n  it \"can use instance_sizeof of virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(12)\n      class Foo\n        @x = 1\n      end\n\n      class Bar < Foo\n        @y = 2\n      end\n\n      class Baz < Bar\n        @z = 2\n      end\n\n      bar = Baz.new.as(Bar)\n      instance_sizeof(typeof(bar))\n      CRYSTAL\n  end\n\n  it \"can use instance_sizeof in type argument\" do\n    run(<<-CRYSTAL).to_i.should eq(12)\n      struct StaticArray\n        def size\n          N\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = 1\n          @y = 1\n        end\n      end\n\n      x = uninitialized UInt8[instance_sizeof(Foo)]\n      x.size\n      CRYSTAL\n  end\n\n  it \"returns correct sizeof for abstract struct (#4319)\" do\n    size = run(<<-CRYSTAL).to_i\n        abstract struct Entry\n        end\n\n        struct FooEntry < Entry\n          def initialize\n            @uid = \"\"\n          end\n        end\n\n        struct BarEntry < Entry\n          def initialize\n            @uid = \"\"\n          end\n        end\n\n        sizeof(Entry)\n        CRYSTAL\n\n    size.should eq(16)\n  end\n\n  it \"doesn't precompute sizeof of abstract struct (#7741)\" do\n    run(<<-CRYSTAL).to_i.should eq(16)\n      abstract struct Base\n      end\n\n      struct Foo(T) < Base\n        def initialize(@x : T)\n        end\n      end\n\n      z = sizeof(Base)\n\n      Foo({Int32, Int32, Int32, Int32})\n\n      z\n      CRYSTAL\n  end\n\n  it \"doesn't precompute sizeof of module (#7741)\" do\n    run(<<-CRYSTAL).to_i.should eq(16)\n      module Base\n      end\n\n      struct Foo(T)\n        include Base\n\n        def initialize(@x : T)\n        end\n      end\n\n      z = sizeof(Base)\n\n      Foo({Int32, Int32, Int32, Int32})\n\n      z\n      CRYSTAL\n  end\n\n  describe \"alignof\" do\n    it \"gets alignof primitive types\" do\n      run(\"alignof(Int32)\").to_i.should eq(4)\n      run(\"alignof(Void)\").to_i.should eq(1)\n      run(\"alignof(NoReturn)\").to_i.should eq(1)\n      run(\"alignof(Nil)\").to_i.should eq(1)\n      run(\"alignof(Bool)\").to_i.should eq(1)\n    end\n\n    it \"gets alignof struct\" do\n      run(<<-CRYSTAL).to_i.should eq(4)\n        struct Foo\n          def initialize(@x : Int8, @y : Int32, @z : Int16)\n          end\n        end\n\n        Foo.new(1, 2, 3)\n\n        alignof(Foo)\n        CRYSTAL\n    end\n\n    it \"gets alignof class\" do\n      # pointer size and alignment should be identical\n      run(<<-CRYSTAL).to_i.should eq(sizeof(Void*))\n        class Foo\n          def initialize(@x : Int8, @y : Int32, @z : Int16)\n          end\n        end\n\n        Foo.new(1, 2, 3)\n\n        alignof(Foo)\n        CRYSTAL\n    end\n\n    it \"gets alignof union\" do\n      run(\"alignof(Int32 | Int8)\").to_i.should eq(8)\n      run(\"alignof(Int32 | Int64)\").to_i.should eq(8)\n    end\n\n    it \"alignof mixed union is not less than alignof its variant types\" do\n      # NOTE: `alignof(Int128) == 16` is not guaranteed\n      run(\"alignof(Int32 | Int128) >= alignof(Int128)\").to_b.should be_true\n    end\n  end\n\n  describe \"instance_alignof\" do\n    it \"gets instance_alignof class\" do\n      run(<<-CRYSTAL).to_i.should eq(4)\n        class Foo\n          def initialize(@x : Int8, @y : Int32, @z : Int16)\n          end\n        end\n\n        Foo.new(1, 2, 3)\n\n        instance_alignof(Foo)\n        CRYSTAL\n\n      run(<<-CRYSTAL).to_i.should eq(8)\n        class Foo\n          def initialize(@x : Int8, @y : Int64, @z : Int16)\n          end\n        end\n\n        Foo.new(1, 2, 3)\n\n        instance_alignof(Foo)\n        CRYSTAL\n\n      run(<<-CRYSTAL).to_i.should eq(4)\n        class Foo\n        end\n\n        Foo.new\n\n        instance_alignof(Foo)\n        CRYSTAL\n    end\n\n    it \"gets instance_alignof a generic type with type vars\" do\n      run(<<-CRYSTAL).to_i.should eq(4)\n        class Foo(T)\n          def initialize(@x : T)\n          end\n        end\n\n        instance_alignof(Foo(Int32))\n        CRYSTAL\n\n      run(<<-CRYSTAL).to_i.should eq(8)\n        class Foo(T)\n          def initialize(@x : T)\n          end\n        end\n\n        instance_alignof(Foo(Int64))\n        CRYSTAL\n\n      run(<<-CRYSTAL).to_i.should eq(4)\n        class Foo(T)\n          def initialize(@x : T)\n          end\n        end\n\n        instance_alignof(Foo(Int8))\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/special_vars_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: special vars\" do\n  [\"$~\", \"$?\"].each do |name|\n    it \"codegens #{name}\" do\n      run(<<-CRYSTAL).to_string.should eq(\"hey\")\n        class Object; def not_nil!; self; end; end\n\n        def foo(z)\n          #{name} = \"hey\"\n        end\n\n        foo(2)\n        #{name}\n        CRYSTAL\n    end\n\n    it \"codegens #{name} with nilable (1)\" do\n      run(<<-CRYSTAL).to_string.should eq(\"ouch\")\n        require \"prelude\"\n\n        def foo\n          if 1 == 2\n            #{name} = \"foo\"\n          end\n        end\n\n        foo\n\n        begin\n          #{name}\n        rescue ex\n          \"ouch\"\n        end\n        CRYSTAL\n    end\n\n    it \"codegens #{name} with nilable (2)\" do\n      run(<<-CRYSTAL).to_string.should eq(\"foo\")\n        require \"prelude\"\n\n        def foo\n          if 1 == 1\n            #{name} = \"foo\"\n          end\n        end\n\n        foo\n\n        begin\n          #{name}\n        rescue ex\n          \"ouch\"\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"codegens $~ two levels\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hey\")\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $? = \"hey\"\n      end\n\n      def bar\n        $? = foo\n        $?\n      end\n\n      bar\n      $?\n      CRYSTAL\n  end\n\n  it \"works lazily\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bar\")\n      require \"prelude\"\n\n      class Foo\n        getter string\n\n        def initialize(@string : String)\n        end\n      end\n\n      def bar(&block : Foo -> _)\n        block\n      end\n\n      block = bar do |foo|\n        case foo.string\n        when /foo-(.+)/\n          $1\n        else\n          \"baz\"\n        end\n      end\n      block.call(Foo.new(\"foo-bar\"))\n      CRYSTAL\n  end\n\n  it \"codegens in block\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hey\")\n      require \"prelude\"\n\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $~ = \"hey\"\n        yield\n      end\n\n      a = nil\n      foo do\n        a = $~\n      end\n      a.not_nil!\n      CRYSTAL\n  end\n\n  it \"codegens in block with nested block\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hey\")\n      require \"prelude\"\n\n      class Object; def not_nil!; self; end; end\n\n      def bar\n        yield\n      end\n\n      def foo\n        bar do\n          $~ = \"hey\"\n          yield\n        end\n      end\n\n      a = nil\n      foo do\n        a = $~\n      end\n      a.not_nil!\n      CRYSTAL\n  end\n\n  it \"codegens after block\" do\n    run(<<-CRYSTAL).to_string.should eq(\"hey\")\n      require \"prelude\"\n\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $~ = \"hey\"\n        yield\n      end\n\n      a = nil\n      foo {}\n      $~\n      CRYSTAL\n  end\n\n  it \"codegens after block 2\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bye\")\n      class Object; def not_nil!; self; end; end\n\n      def baz\n        $~ = \"bye\"\n      end\n\n      def foo\n        baz\n        yield\n        $~\n      end\n\n      foo do\n      end\n      CRYSTAL\n  end\n\n  it \"codegens with default argument\" do\n    run(<<-CRYSTAL).to_string.should eq(\"bye\")\n      class Object; def not_nil!; self; end; end\n\n      def baz(x = 1)\n        $~ = \"bye\"\n      end\n\n      baz\n      $~\n      CRYSTAL\n  end\n\n  it \"preserves special vars in macro expansion with call with default arguments (#824)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"yes\")\n      class Object; def not_nil!; self; end; end\n\n      def bar(x = 0)\n        $~ = \"yes\"\n      end\n\n      macro foo\n        bar\n        $~\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"allows with primitive\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $~ = 123\n      end\n\n      foo\n\n      v = $~\n      v || 456\n      CRYSTAL\n  end\n\n  it \"allows with struct\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class Object; def not_nil!; self; end; end\n\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      def foo\n        $~ = Foo.new(123)\n      end\n\n      foo\n\n      v = $~\n      if v\n        v.x\n      else\n        456\n      end\n      CRYSTAL\n  end\n\n  it \"preserves special vars if initialized inside block (#2194)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"foo\")\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $~ = \"foo\"\n      end\n\n      def bar\n        yield\n      end\n\n      bar do\n        foo\n      end\n\n      v = $~\n      if v\n        v\n      else\n        \"bar\"\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/splat_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: splat\" do\n  it \"splats\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct Tuple\n        def size; {{T.size}}; end\n      end\n\n      def foo(*args)\n        args.size\n      end\n\n      foo 1, 1, 1\n      CRYSTAL\n  end\n\n  it \"splats with another arg\" do\n    run(<<-CRYSTAL).to_i.should eq(12)\n      struct Tuple\n        def size; {{T.size}}; end\n      end\n\n      def foo(x, *args)\n        x &+ args.size\n      end\n\n      foo 10, 1, 1\n      CRYSTAL\n  end\n\n  it \"splats on call\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      def foo(x, y)\n        x &+ y\n      end\n\n      tuple = {1, 2}\n      foo *tuple\n      CRYSTAL\n  end\n\n  it \"splats without args\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Tuple\n        def size; {{T.size}}; end\n      end\n\n      def foo(*args)\n        args.size\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"splats with default value\" do\n    run(<<-CRYSTAL).to_i.should eq(100)\n      struct Tuple\n        def size; {{T.size}}; end\n      end\n\n      def foo(x = 100, *args)\n        x &+ args.size\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"splats with default value (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(110)\n      struct Tuple\n        def size; {{T.size}}; end\n      end\n\n      def foo(x, y = 100, *args)\n        x &+ y &+ args.size\n      end\n\n      foo 10\n      CRYSTAL\n  end\n\n  it \"splats with default value (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(32)\n      struct Tuple\n        def size; {{T.size}}; end\n      end\n\n      def foo(x, y = 100, *args)\n        x &+ y &+ args.size\n      end\n\n      foo 10, 20, 30, 40\n      CRYSTAL\n  end\n\n  it \"splats in initialize\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        @x : Int32\n        @y : Int32\n\n        def initialize(*args)\n          @x, @y = args\n        end\n\n        def x\n          @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      foo = Foo.new 1, 2\n      foo.x &+ foo.y\n      CRYSTAL\n  end\n\n  it \"does #2407\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      def some\n        yield(1 || (LibC.exit(1); \"\"))\n      end\n\n      def foo(*objects)\n        bar *objects\n      end\n\n      def bar(objects)\n      end\n\n      some do |value|\n        foo value\n      end\n      CRYSTAL\n  end\n\n  it \"evaluates splat argument just once (#2677)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      def data\n        Global.x &+= 1\n        {Global.x, Global.x, Global.x}\n      end\n\n      def test(x, y, z)\n        x &+ y &+ z\n      end\n\n      v = test(*data)\n\n      Global.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/ssa_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: ssa\" do\n  it \"codegens a redefined var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      a = 1.5\n      a = 1\n      a\n      CRYSTAL\n  end\n\n  it \"codegens a redefined var inside method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo\n        a = 1.5\n        a = 1\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"codegens a redefined var inside method with argument\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def foo(a)\n        a = 1\n        a\n      end\n\n      foo 1.5\n      CRYSTAL\n  end\n\n  it \"codegens declaration of var inside then when false\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      if 1 == 2\n        b = 2\n      end\n      b.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens declaration of var inside then when true\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      if 1 == 1\n        b = 2\n      end\n      b.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens a var that is re-assigned in a block\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      struct Char\n        def to_i!\n          10\n        end\n      end\n\n      def foo\n        yield\n      end\n\n      a = 1\n      foo do\n        a = 'a'\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens a var that is re-assigned in a block (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      struct Char\n        def to_i!\n          10\n        end\n      end\n\n      a = 1\n      while a.to_i! == 1\n        a = 'a'\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens a var that is re-assigned in a block (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Char\n        def to_i!\n          10\n        end\n      end\n\n      a = 1\n      while 1 == 2\n        a = 'a'\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens a var that is declared in a block (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      while 1 == 2\n        a = 1\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens a var that is declared in a block (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      b = 1\n      while b == 1\n        a = 1\n        b = 2\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens ssa bug with if/else on var\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      a = 1 || nil\n      if a && false\n        b = 2\n      elsif a\n        b = 3\n      else\n        b = 4\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"codegens ssa bug (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      index = nil\n      if index\n        a = index\n        1\n      else\n        if 1 == 1\n          index = 1\n        else\n          1\n        end\n        a = index\n        1\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens ssa bug (2)\" do\n    # This shows a bug where a block variable (coconio in this case)\n    # wasn't reset to nil before each block iteration.\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def to_i!\n          0\n        end\n      end\n\n      def foo\n        i = 1\n        while i <= 3\n          yield i\n          i &+= 1\n        end\n      end\n\n      a = 0\n      foo do |x|\n        coconio = x if x == 1\n        a &+= coconio.to_i!\n      end\n      a\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/struct_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: struct\" do\n  it \"creates structs\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n      end\n\n      f = Foo.allocate\n      1\n      CRYSTAL\n  end\n\n  it \"creates structs with instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      f = Foo.new(1)\n      f.x\n      CRYSTAL\n  end\n\n  it \"assigning a struct makes a copy (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n\n        def x=(@x)\n        end\n      end\n\n      f = Foo.new(1)\n\n      g = f\n      g.x = 2\n\n      g.x\n      CRYSTAL\n  end\n\n  it \"assigning a struct makes a copy (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n\n        def x=(@x)\n        end\n      end\n\n      f = Foo.new(1)\n\n      g = f\n      g.x = 2\n\n      f.x\n      CRYSTAL\n  end\n\n  it \"passes a struct as a parameter makes a copy\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n\n        def x=(@x)\n        end\n      end\n\n      def foo(f)\n        f.x = 2\n      end\n\n      f = Foo.new(1)\n\n      foo(f)\n\n      f.x\n      CRYSTAL\n  end\n\n  it \"passes a generic struct as a parameter makes a copy\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo(T)\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n\n        def x=(@x)\n        end\n      end\n\n      def foo(f)\n        f.x = 2\n      end\n\n      f = Foo(Int32).new(1)\n\n      foo(f)\n\n      f.x\n      CRYSTAL\n  end\n\n  it \"returns struct as a copy\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n\n        def x=(@x)\n        end\n      end\n\n      def foo(f)\n        f.x = 2\n        f\n      end\n\n      f = Foo.new(1)\n\n      g = foo(f)\n      g.x\n      CRYSTAL\n  end\n\n  it \"creates struct in def\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      def foo\n        Foo.new(1)\n      end\n\n      foo.x\n      CRYSTAL\n  end\n\n  it \"declares const struct\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      FOO = Foo.new(1)\n\n      FOO.x\n      CRYSTAL\n  end\n\n  it \"uses struct in if\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      FOO = Foo.new(1)\n\n      if 1 == 2\n        foo = Foo.new(1)\n      else\n        foo = FOO\n      end\n      foo.x\n      CRYSTAL\n  end\n\n  it \"uses nilable struct\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n      end\n\n      f = Foo.new || nil\n      f.nil? ? 1 : 2\n      CRYSTAL\n  end\n\n  it \"returns self\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n        def initialize(@x)\n        end\n\n        def foo\n          @x = 2\n          return self\n        end\n\n        def x\n          @x\n        end\n      end\n\n      f = Foo.new(1)\n      g = f.foo\n      g.x\n      CRYSTAL\n  end\n\n  it \"returns self with block\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n        def initialize(@x)\n        end\n\n        def foo\n          @x = 2\n          yield 1\n          self\n        end\n\n        def x\n          @x\n        end\n      end\n\n      f = Foo.new(1)\n      g = f.foo { }\n      g.x\n      CRYSTAL\n  end\n\n  it \"does phi of struct\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      x = if 1 == 2\n            Foo.new(2)\n          else\n            Foo.new(1)\n          end\n      x.x\n      CRYSTAL\n  end\n\n  it \"allows assigning to struct argument (bug)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Foo\n        def bar\n          2\n        end\n      end\n\n      def foo(x)\n        x = x.bar\n      end\n\n      foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"codegens struct assigned to underscore (#1842)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      struct Foo\n        def initialize\n          @value = 123\n        end\n\n        def value\n          @value\n        end\n      end\n\n      def foo\n        _ = Foo.new\n      end\n\n      foo.value\n      CRYSTAL\n  end\n\n  it \"codegens virtual struct\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      struct Baz < Foo\n        def initialize\n          @x = 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Bar.new || Baz.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"codegens virtual struct with pointer\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      struct Baz < Foo\n        def initialize\n          @x = 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      ptr = Pointer(Foo).malloc(1_u64)\n      ptr.value = Baz.new\n      ptr.value = Bar.new\n      ptr.value.x\n      CRYSTAL\n  end\n\n  it \"codegens virtual struct metaclass (#2551) (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      abstract struct Foo\n        def initialize\n          @x = 21\n        end\n\n        def x\n          a = @x\n          a\n        end\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n      end\n\n      struct Baz < Foo\n      end\n\n      Bar.new.as(Foo).x\n      CRYSTAL\n  end\n\n  it \"codegens virtual struct metaclass (#2551) (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      abstract struct Foo\n        def initialize\n          @x = 21\n        end\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n      end\n\n      struct Baz < Foo\n      end\n\n      Bar.new.as(Foo).@x\n      CRYSTAL\n  end\n\n  it \"codegens virtual struct metaclass (#2551) (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      abstract struct Foo\n        def initialize\n          @x = 21\n        end\n\n        def x\n          @x\n        end\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n      end\n\n      struct Baz < Foo\n      end\n\n      Bar.new.as(Foo).x\n      CRYSTAL\n  end\n\n  it \"codegens virtual struct metaclass (#2551) (4)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      require \"prelude\"\n\n      abstract struct Foo\n        def initialize\n          @x = 21\n        end\n\n        def x\n          a = @x\n          a\n        end\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n      end\n\n      struct Baz < Foo\n      end\n\n      (Bar || Baz).new.x\n      CRYSTAL\n  end\n\n  it \"mutates a  virtual struct\" do\n    run(<<-CRYSTAL).to_i.should eq(84)\n      abstract struct Foo\n        def initialize\n          @x = 21\n        end\n\n        def x=(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n      end\n\n      struct Baz < Foo\n      end\n\n      foo = Bar.new.as(Foo)\n      foo.x = 84\n      foo.x\n      CRYSTAL\n  end\n\n  it \"codegens virtual structs union (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n\n        def x\n          @x\n        end\n      end\n\n      abstract struct Foo2\n      end\n\n      struct Bar2 < Foo2\n        def initialize\n          @x = 84\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Bar.new.as(Foo)\n      foo2 = Bar2.new.as(Foo2)\n\n      f = foo || foo2\n      f.x\n      CRYSTAL\n  end\n\n  it \"codegens virtual structs union (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(84)\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n        def initialize\n          @x = 42\n        end\n\n        def x\n          @x\n        end\n      end\n\n      abstract struct Foo2\n      end\n\n      struct Bar2 < Foo2\n        def initialize\n          @x = 84\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Bar.new.as(Foo)\n      foo2 = Bar2.new.as(Foo2)\n\n      f = foo2 || foo\n      f.x\n      CRYSTAL\n  end\n\n  it \"can cast virtual struct to specific struct\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n       require \"prelude\"\n\n       abstract struct Foo\n       end\n\n       struct Bar < Foo\n         def foo\n           1\n         end\n       end\n\n       struct Baz < Foo\n         def foo\n           2\n         end\n       end\n\n       x = Bar.new || Baz.new\n       x.as(Bar).foo\n       CRYSTAL\n  end\n\n  it \"casts virtual struct to base type, only one subclass (#2885)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"1\")\n      abstract struct Entry\n        def initialize(@uid : String, @country : String)\n        end\n\n        def uid\n          @uid\n        end\n      end\n\n      struct MyEntry < Entry\n      end\n\n      entry = MyEntry.new(\"1\", \"GER\")\n      entry.as(Entry).uid\n      CRYSTAL\n  end\n\n  it \"can call new on abstract struct with single child (#7309)\" do\n    codegen(<<-CRYSTAL, inject_primitives: false)\n      require \"prelude\"\n\n      abstract struct Foo\n        @x = 1\n      end\n\n      struct A < Foo\n        @y = 2\n      end\n\n      A.as(Foo.class).new\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/super_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: super\" do\n  it \"codegens super without arguments\" do\n    run(\"class Foo; def foo; 1; end; end; class Bar < Foo; def foo; super; end; end; Bar.new.foo\").to_i.should eq(1)\n  end\n\n  it \"codegens super without arguments but parent has arguments\" do\n    run(\"class Foo; def foo(x); x &+ 1; end; end; class Bar < Foo; def foo(x); super; end; end; Bar.new.foo(1)\").to_i.should eq(2)\n  end\n\n  it \"codegens super without arguments and instance variable\" do\n    run(\"class Foo; def foo; @x = 1; end; end; class Bar < Foo; def foo; super; end; end; Bar.new.foo\").to_i.should eq(1)\n  end\n\n  it \"codegens super that calls subclass method\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          bar\n        end\n\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n\n        def bar\n          2\n        end\n      end\n\n      b = Bar.new\n      b.foo\n      CRYSTAL\n  end\n\n  it \"codegens super that calls subclass method 2\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          self.bar\n        end\n\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n\n        def bar\n          2\n        end\n      end\n\n      b = Bar.new\n      b.foo\n      CRYSTAL\n  end\n\n  it \"codegens super that calls subclass method 3\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          self.bar\n        end\n\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n\n        def bar\n          2\n        end\n      end\n\n      b = Foo.new || Bar.new\n      b.foo\n      CRYSTAL\n  end\n\n  it \"codegens super that calls subclass method 4\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          self.bar\n        end\n\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n\n        def bar\n          2\n        end\n      end\n\n      b = Bar.new || Foo.new\n      b.foo\n      CRYSTAL\n  end\n\n  it \"codegens super that calls subclass method 5\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Mod\n        def add_def\n          another\n        end\n      end\n\n      abstract class ClassType\n        include Mod\n\n        def add_def\n          super\n        end\n      end\n\n      class NonGenericClassType < ClassType\n      end\n\n      class PrimitiveType < ClassType\n        def another\n          2\n        end\n      end\n\n      class IntegerType < PrimitiveType\n        def another\n          3\n        end\n      end\n\n      c = PrimitiveType.new || IntegerType.new\n      c.add_def\n      CRYSTAL\n  end\n\n  it \"codegens super that calls subclass method 6\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      module Mod\n        def add_def\n          another\n        end\n      end\n\n      abstract class ClassType\n        include Mod\n\n        def add_def\n          super\n        end\n      end\n\n      class NonGenericClassType < ClassType\n      end\n\n      class PrimitiveType < ClassType\n        def another\n          2\n        end\n      end\n\n      class IntegerType < PrimitiveType\n        def another\n          3\n        end\n      end\n\n      c = IntegerType.new || PrimitiveType.new\n      c.add_def\n      CRYSTAL\n  end\n\n  it \"codegens super inside closure\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          ->{ super }\n        end\n      end\n\n      f = Bar.new(1).foo\n      f.call\n      CRYSTAL\n  end\n\n  it \"codegens super inside closure forwarding args\" do\n    run(<<-CRYSTAL).to_i.should eq(6)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo(z)\n          z &+ @x\n        end\n      end\n\n      class Bar < Foo\n        def foo(z)\n          ->(x : Int32) { x &+ super }\n        end\n      end\n\n      f = Bar.new(1).foo(2)\n      f.call(3)\n      CRYSTAL\n  end\n\n  it \"build super on generic class (bug)\" do\n    codegen(<<-CRYSTAL)\n      class Base\n        def foo(x)\n          1.5\n        end\n      end\n\n      class Foo(T) < Base\n        def foo\n          super(1)\n        end\n      end\n\n      Foo(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"calls super in module method (#556)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Parent\n        def a\n          1\n        end\n      end\n\n      module Mod\n        def a\n          super\n        end\n      end\n\n      class Child < Parent\n        include Mod\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"calls super in generic module method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Parent\n        def a\n          1\n        end\n      end\n\n      module Mod(T)\n        def a\n          super\n        end\n      end\n\n      class Child < Parent\n        include Mod(Int32)\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"does super in virtual type including module\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      module Bar\n        def bar\n          123\n        end\n      end\n\n      module Foo\n        include Bar\n\n        def bar\n          super\n        end\n      end\n\n      class Base\n        include Foo\n      end\n\n      class Child < Base\n      end\n\n      (Base.new || Child.new).bar\n      CRYSTAL\n  end\n\n  it \"doesn't invoke super twice in inherited generic types (#942)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      abstract class Foo\n      end\n\n      class Bar(T) < Foo\n        def initialize\n            Global.x &+= 1\n            super\n        end\n      end\n\n      class Baz(T) < Bar(T)\n      end\n\n      Baz(Int8).new\n\n      Global.x\n      CRYSTAL\n  end\n\n  it \"calls super in metaclass (#1522)\" do\n    # We include the prelude so this is codegened for real, because that's where the issue lies\n    run(<<-CRYSTAL).to_i.should eq(5)\n      require \"prelude\"\n\n      class Global\n        @@x = 0\n\n        def self.x=(@@x)\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Base\n        def self.foo\n          Global.x += 1\n        end\n      end\n\n      class One < Base\n        def self.foo\n          Global.x += 3\n          super\n        end\n      end\n\n      Base.foo\n      One.foo\n      CRYSTAL\n  end\n\n  it \"calls super with dispatch (#2318)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def foo(x : Int32)\n          x\n        end\n\n        def foo(x : Float64)\n          x\n        end\n      end\n\n      class Bar < Foo\n        def foo(obj)\n          super(obj)\n        end\n      end\n\n      z = Bar.new.foo(3 || 2.5)\n      z.to_i!\n      CRYSTAL\n  end\n\n  it \"calls super from virtual metaclass type (#2841)\" do\n    run(<<-CRYSTAL)\n      abstract class Foo\n        def self.bar(x : Bool)\n          x\n        end\n      end\n\n      class Bar < Foo\n        def self.bar(x : Bool)\n          super\n        end\n      end\n\n      class Baz < Foo\n        def self.bar(x : Bool)\n          super\n        end\n      end\n\n      (Foo || Bar).bar(true)\n      CRYSTAL\n  end\n\n  it \"calls super on an object (#10004)\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @foo = 42\n\n        def super\n          @foo\n        end\n\n      end\n\n      Foo.new.super\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/target_spec.cr",
    "content": "require \"spec\"\nrequire \"compiler/requires\"\n\nprivate alias Target = Crystal::Codegen::Target\n\ndescribe Crystal::Codegen::Target do\n  it \"parses incomplete triples\" do\n    target = Target.new(\"x86_64-linux-gnu\")\n    target.to_s.should eq(\"x86_64-unknown-linux-gnu\")\n    target.pointer_bit_width.should eq(64)\n    target.linux?.should be_true\n    target.unix?.should be_true\n    target.gnu?.should be_true\n  end\n\n  it \"normalizes triples\" do\n    Target.new(\"i686-unknown-linux-gnu\").to_s.should eq(\"i386-unknown-linux-gnu\")\n    Target.new(\"amd64-unknown-openbsd\").to_s.should eq(\"x86_64-unknown-openbsd\")\n    Target.new(\"arm64-apple-darwin20.2.0\").to_s.should eq(\"aarch64-apple-darwin20.2.0\")\n    Target.new(\"x86_64-suse-linux\").to_s.should eq(\"x86_64-suse-linux-gnu\")\n  end\n\n  it \"parses freebsd version\" do\n    Target.new(\"x86_64-unknown-linux-gnu\").freebsd_version.should be_nil\n    Target.new(\"x86_64-unknown-freebsd8.0\").freebsd_version.should eq(8)\n    Target.new(\"x86_64-unknown-freebsd11.0\").freebsd_version.should eq(11)\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/thread_local_spec.cr",
    "content": "{% skip_file if flag?(:openbsd) || (flag?(:win32) && flag?(:gnu)) %}\n\nrequire \"../../spec_helper\"\n\ndescribe \"Codegen: thread local\" do\n  it \"works with class variables\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n    require \"prelude\"\n\n    class Foo\n      @[ThreadLocal]\n      @@var = 123\n\n      def self.var\n        @@var\n      end\n\n      def self.var=(@@var)\n      end\n    end\n\n    Thread.new { Foo.var = 456 }.join\n\n    Foo.var\n    CRYSTAL\n  end\n\n  it \"works with class variable in main thread\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n    require \"prelude\"\n\n    class Foo\n      @[ThreadLocal]\n      @@a = 123\n\n      def self.a\n        @@a\n      end\n    end\n\n    Foo.a\n    CRYSTAL\n  end\n\n  it \"compiles with class variable referenced from initializer\" do\n    run(<<-CRYSTAL)\n    require \"prelude\"\n\n    class Foo\n      @[ThreadLocal]\n      @@x : Foo?\n      @@x = nil\n\n      def self.x\n        @@x ||= new\n      end\n\n      def initialize\n        Foo.x\n      end\n    end\n\n    0\n    CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/tuple_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: tuple\" do\n  it \"codegens tuple [0]\" do\n    run(\"{1, true}[0]\").to_i.should eq(1)\n  end\n\n  it \"codegens tuple [1]\" do\n    run(\"{1, true}[1]\").to_b.should be_true\n  end\n\n  it \"codegens tuple [1] (2)\" do\n    run(\"{true, 3}[1]\").to_i.should eq(3)\n  end\n\n  it \"codegens tuple [0]?\" do\n    run(\"{42, 'a'}[0]? || 84\").to_i.should eq(42)\n  end\n\n  it \"codegens tuple [1]?\" do\n    run(\"{'a', 42}[1]? || 84\").to_i.should eq(42)\n  end\n\n  it \"codegens tuple [2]?\" do\n    run(\"{'a', 42}[2]? || 84\").to_i.should eq(84)\n  end\n\n  it \"codegens tuple metaclass [0]\" do\n    run(\"Tuple(Int32, Char)[0].is_a?(Int32.class)\").to_b.should be_true\n  end\n\n  it \"codegens tuple metaclass [1]\" do\n    run(\"Tuple(Int32, Char)[1].is_a?(Char.class)\").to_b.should be_true\n  end\n\n  it \"codegens tuple metaclass [2]?\" do\n    run(\"Tuple(Int32, Char)[2]?.nil?\").to_b.should be_true\n  end\n\n  it \"codegens tuple [0..0]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[0..0]\n      val.is_a?(Tuple(Int32)) && val[0] == 1\n      CRYSTAL\n  end\n\n  it \"codegens tuple [0..1]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[0..1]\n      val.is_a?(Tuple(Int32, Bool)) && val[0] == 1 && val[1] == true\n      CRYSTAL\n  end\n\n  it \"codegens tuple [0..2]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[0..2]\n      val.is_a?(Tuple(Int32, Bool)) && val[0] == 1&& val[1] == true\n      CRYSTAL\n  end\n\n  it \"codegens tuple [1..1]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[1..1]\n      val.is_a?(Tuple(Bool)) && val[0] == true\n      CRYSTAL\n  end\n\n  it \"codegens tuple [1..0]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      def empty(*args)\n        args\n      end\n\n      {1, true}[1..0].is_a?(typeof(empty))\n      CRYSTAL\n  end\n\n  it \"codegens tuple [2..2]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      def empty(*args)\n        args\n      end\n\n      {1, true}[2..2].is_a?(typeof(empty))\n      CRYSTAL\n  end\n\n  it \"codegens tuple [0..0]?\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[0..0]?\n      val.is_a?(Tuple(Int32)) && val[0] == 1\n      CRYSTAL\n  end\n\n  it \"codegens tuple [0..1]?\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[0..1]?\n      val.is_a?(Tuple(Int32, Bool)) && val[0] == 1 && val[1] == true\n      CRYSTAL\n  end\n\n  it \"codegens tuple [0..2]?\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[0..2]?\n      val.is_a?(Tuple(Int32, Bool)) && val[0] == 1&& val[1] == true\n      CRYSTAL\n  end\n\n  it \"codegens tuple [1..1]?\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      val = {1, true}[1..1]?\n      val.is_a?(Tuple(Bool)) && val[0] == true\n      CRYSTAL\n  end\n\n  it \"codegens tuple [1..0]?\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      def empty(*args)\n        args\n      end\n\n      {1, true}[1..0]?.is_a?(typeof(empty))\n      CRYSTAL\n  end\n\n  it \"codegens tuple [2..2]?\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      def empty(*args)\n        args\n      end\n\n      {1, true}[2..2]?.is_a?(typeof(empty))\n      CRYSTAL\n  end\n\n  it \"codegens tuple [3..2]?\" do\n    run(\"#{range_new}; {1, true}[3..2]?.nil?\").to_b.should be_true\n  end\n\n  it \"codegens tuple [-3..2]?\" do\n    run(\"#{range_new}; {1, true}[-3..2]?.nil?\").to_b.should be_true\n  end\n\n  it \"codegens tuple metaclass [0..0]\" do\n    run(\"#{range_new}; Tuple(Int32, Char)[0..0].is_a?(Tuple(Int32).class)\").to_b.should be_true\n  end\n\n  it \"codegens tuple metaclass [0..1]\" do\n    run(\"#{range_new}; Tuple(Int32, Char)[0..1].is_a?(Tuple(Int32, Char).class)\").to_b.should be_true\n  end\n\n  it \"codegens tuple metaclass [1..0]\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      #{range_new}\n\n      def empty(*args)\n        args.class\n      end\n\n      Tuple(Int32, Char)[1..0].is_a?(typeof(empty))\n      CRYSTAL\n  end\n\n  it \"codegens tuple metaclass [3..2]?\" do\n    run(\"#{range_new}; Tuple(Int32, Char)[3..2]?.nil?\").to_b.should be_true\n  end\n\n  it \"codegens splats inside tuples\" do\n    run(<<-CRYSTAL).to_i.should eq(2 + 4 + 32 + 128)\n      x = {1, *{2, 4}, 8, *{16, 32, 64}, 128}\n      x[1] &+ x[2] &+ x[5] &+ x[7]\n      CRYSTAL\n  end\n\n  it \"passed tuple to def\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      def foo(t)\n        t[1]\n      end\n\n      foo({1, 2, 3})\n      CRYSTAL\n  end\n\n  it \"accesses T and creates instance from it\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Tuple\n        def type_args\n          T\n        end\n      end\n\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      t = {Foo.new(1)}\n      f = t.type_args[0].new(2)\n      f.x\n      CRYSTAL\n  end\n\n  it \"allows malloc pointer of tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct Pointer\n        def self.malloc(size : Int)\n          malloc(size.to_u64!)\n        end\n      end\n\n      def foo(x : T) forall T\n        p = Pointer(T).malloc(1)\n        p.value = x\n        p\n      end\n\n      p = foo({1, 2})\n      p.value[0] &+ p.value[1]\n      CRYSTAL\n  end\n\n  it \"codegens tuple union (bug because union size was computed incorrectly)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n      x = 1 == 1 ? {1, 1, 1} : {1}\n      i = 2\n      x[i]\n      CRYSTAL\n  end\n\n  it \"codegens tuple class\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n      end\n\n      foo = Foo.new(1)\n      bar = Bar.new\n\n      tuple = {foo, bar}\n      tuple_class = tuple.class\n      foo_class = tuple_class[0]\n      foo2 = foo_class.new(2)\n      foo2.x\n      CRYSTAL\n  end\n\n  it \"gets size at compile time\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      struct Tuple\n        def my_size\n          {{ T.size }}\n        end\n      end\n\n      {1, 1}.my_size\n      CRYSTAL\n  end\n\n  it \"allows tuple covariance\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n       class Obj\n         def initialize\n           @tuple = {Foo.new}\n         end\n\n         def tuple=(@tuple)\n         end\n\n         def tuple\n           @tuple\n         end\n       end\n\n       class Foo\n         def bar\n           21\n         end\n       end\n\n       class Bar < Foo\n         def bar\n           42\n         end\n       end\n\n       obj = Obj.new\n       obj.tuple = {Bar.new}\n       obj.tuple[0].bar\n       CRYSTAL\n  end\n\n  it \"merges two tuple types of same size (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(20)\n       def foo\n         if 1 == 2\n           {\"foo\", 10}\n         else\n           {\"foo\", nil}\n         end\n       end\n\n       val = foo[1]\n       val || 20\n       CRYSTAL\n  end\n\n  it \"merges two tuple types of same size (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n       def foo\n         if 1 == 1\n           {\"foo\", 10}\n         else\n           {\"foo\", nil}\n         end\n       end\n\n       val = foo[1]\n       val || 20\n       CRYSTAL\n  end\n\n  it \"assigns tuple to compatible tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      ptr = Pointer({Int32 | String, Bool | Char}).malloc(1_u64)\n\n      # Here the compiler should cast each value\n      ptr.value = {42, 'x'}\n\n      val = ptr.value[0]\n      val.as?(Int32) || 10\n      CRYSTAL\n  end\n\n  it \"upcasts tuple inside compatible tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo\n        if 1 == 2\n          {\"hello\", false}\n        else\n          {42, 'x'}\n        end\n      end\n\n      val = foo[0]\n      val.as?(Int32) || 10\n      CRYSTAL\n  end\n\n  it \"assigns tuple union to compatible tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      tup1 = {\"hello\", false}\n      tup2 = {3}\n      tup3 = {42, 'x'}\n\n      ptr = Pointer(typeof(tup1, tup2, tup3)).malloc(1_u64)\n      ptr.value = tup3\n      val = ptr.value[0]\n      val.as?(Int32) || 10\n      CRYSTAL\n  end\n\n  it \"upcasts tuple union to compatible tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo\n        if 1 == 2\n          {\"hello\", false} || {3}\n        else\n          {42, 'x'}\n        end\n      end\n\n      val = foo[0]\n      val.as?(Int32) || 10\n      CRYSTAL\n  end\n\n  it \"assigns tuple inside union to union with compatible tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      tup1 = {\"hello\", false}\n      tup2 = {3}\n\n      union1 = tup1 || tup2\n\n      tup3 = {42, 'x'}\n      tup4 = {4}\n\n      union2 = tup3 || tup4\n\n      ptr = Pointer(typeof(union1, union2)).malloc(1_u64)\n      ptr.value = union2\n      val = ptr.value[0]\n      val.as?(Int32) || 10\n      CRYSTAL\n  end\n\n  it \"upcasts tuple inside union to union with compatible tuple\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      def foo\n        if 1 == 2\n          tup1 = {\"hello\", false}\n          tup2 = {3}\n          union1 = tup1 || tup2\n          union1\n        else\n          tup3 = {42, 'x'}\n          tup4 = {4}\n          union2 = tup3 || tup4\n          union2\n        end\n      end\n\n      val = foo[0]\n      val.as?(Int32) || 10\n      CRYSTAL\n  end\n\n  it \"codegens union of tuple of float with tuple of tuple of float\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      a = {1.5}\n      b = { {22.0, 20.0} }\n      c = b || a\n      v = c[0]\n      if v.is_a?(Float64)\n        10\n      else\n        v[0].to_i! &+ v[1].to_i!\n      end\n      CRYSTAL\n  end\n\n  it \"provides T as a tuple literal\" do\n    run(<<-CRYSTAL).to_string.should eq(\"TupleLiteral\")\n      struct Tuple\n        def self.foo\n          {{ T.class_name }}\n        end\n      end\n      Tuple(Nil, Int32).foo\n      CRYSTAL\n  end\n\n  it \"passes empty tuple and empty named tuple to a method (#2852)\" do\n    codegen(<<-CRYSTAL)\n      def foo(*binds)\n        baz(binds)\n      end\n\n      def bar(**binds)\n        baz(binds)\n      end\n\n      def baz(binds)\n        binds\n      end\n\n      foo\n      bar\n      CRYSTAL\n  end\n\n  it \"assigns two same-size tuple types to a same var (#3132)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      t = {true}\n      t\n      t = {2}\n      t[0]\n      CRYSTAL\n  end\n\n  it \"downcasts union to mixed tuple type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      t = {1} || 2 || {true}\n      t = {1}\n      t[0]\n      CRYSTAL\n  end\n\n  it \"downcasts union to mixed union with mixed tuple types\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      t = {1} || 2 || {true}\n      t = {1} || 2\n      t.as(Tuple)[0]\n      CRYSTAL\n  end\n\n  it \"downcasts union inside tuple to value (#3907)\" do\n    codegen(<<-CRYSTAL)\n      struct Foo\n      end\n\n      foo = Foo.new\n\n      x = {0, foo}\n      z = x[0]\n      x = {0, z}\n      CRYSTAL\n  end\nend\n\nprivate def range_new\n  %(\n    struct Range(B, E)\n      def initialize(@begin : B, @end : E, @exclusive : Bool = false)\n      end\n    end\n  )\nend\n"
  },
  {
    "path": "spec/compiler/codegen/type_declaration_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: type declaration\" do\n  it \"codegens initialize instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"codegens initialize instance var of superclass\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"codegens initialize instance var with var declaration\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        @x : Int32 = begin\n          a = 1\n          a\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares and initializes\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      class Foo\n        @x : Int32 = 42\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares and initializes var\" do\n    run(<<-CRYSTAL).to_i.should eq(42)\n      a : Int32 = 42\n      a\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/uninitialized_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: uninitialized\" do\n  it \"codegens declare var and read it\" do\n    run(\"a = uninitialized Int32; a\")\n  end\n\n  it \"codegens declare var and changes it\" do\n    run(\"a = uninitialized Int32; while a != 10; a = 10; end; a\").to_i.should eq(10)\n  end\n\n  it \"codegens declare instance var\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      class Foo\n        def initialize\n          @x = uninitialized Int32\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"codegens declare instance var with static array type\" do\n    run(<<-CRYSTAL)\n      class Foo\n        def initialize\n          @x = uninitialized Int32[4]\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      nil\n      CRYSTAL\n  end\n\n  it \"doesn't break on inherited declared var (#390)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n        def initialize\n          @x = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @x = uninitialized Int32\n          @x = 1\n          @y = 2\n        end\n\n        def x\n          @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      bar = Bar.new\n      bar.x &+ bar.y\n      CRYSTAL\n  end\n\n  it \"works inside while/begin/rescue (bug inside #759)\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      a = 3\n      while 1\n        begin\n          buf = uninitialized Int32\n          buf + 1\n          break if a == 3\n        rescue\n        end\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"works with uninitialized NoReturn (#3314)\" do\n    codegen(<<-CRYSTAL, inject_primitives: false)\n      def foo\n        x = uninitialized NoReturn\n        if 1\n          x = yield\n        end\n        x\n      end\n\n      def bar\n        foo { return }\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"codegens value (#3641)\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      x = y = uninitialized Int32\n      x == y\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/union_type_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: union type\" do\n  it \"codegens union type when obj is union and no args\" do\n    run(\"a = 1; a = 2.5_f32; a.to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codegens union type when obj is union and arg is union\" do\n    run(\"a = 1; a = 1.5_f32; (a + a).to_f\").to_f64.should eq(3)\n  end\n\n  it \"codegens union type when obj is not union but arg is\" do\n    run(\"a = 1; b = 2; b = 1.5_f32; (a + b).to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codegens union type when obj union but arg is not\" do\n    run(\"a = 1; b = 2; b = 1.5_f32; (b + a).to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codegens union type when no obj\" do\n    run(\"def foo(x); x; end; a = 1; a = 2.5_f32; foo(a).to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codegens union type when no obj and restrictions\" do\n    run(\"def foo(x : Int); 1.5; end; def foo(x : Float); 2.5; end; a = 1; a = 3.5_f32; foo(a).to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codegens union type as return value\" do\n    run(\"def foo; a = 1; a = 2.5_f32; a; end; foo.to_f\").to_f64.should eq(2.5)\n  end\n\n  it \"codegens union type for instance var\" do\n    run(<<-CRYSTAL).to_f64.should eq(3)\n      struct Float\n        def &+(other)\n          self + other\n        end\n      end\n\n      struct Int32\n        def &+(other : Float)\n          self + other\n        end\n      end\n\n      class Foo\n        @value : Int32 | Float32\n\n        def initialize(value)\n          @value = value\n        end\n        def value=(@value); end\n        def value; @value; end\n      end\n\n      f = Foo.new(1)\n      f.value = 1.5_f32\n      (f.value &+ f.value).to_f\n      CRYSTAL\n  end\n\n  it \"codegens if with same nested union\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      if true\n        if true\n          1\n        else\n          2.5_f32\n        end\n      else\n        if true\n          1\n        else\n          2.5_f32\n        end\n      end.to_i!\n      CRYSTAL\n  end\n\n  it \"assigns union to union\" do\n    run(<<-CRYSTAL).to_i.should eq(97)\n      require \"prelude\"\n\n      struct Nil; def to_i; 0; end; end\n\n      struct Char\n        def to_i\n          ord\n        end\n      end\n\n      class Foo\n        @x : Int32 | Char | Nil\n\n        def foo(x)\n          @x = x\n          @x = @x || 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      f = Foo.new\n      f.foo 1\n      f.foo 'a'\n      f.x.to_i\n      CRYSTAL\n  end\n\n  it \"assigns union to larger union\" do\n    run(<<-CRYSTAL).to_string.should eq(\"d\")\n      require \"prelude\"\n      a = 1\n      a = 1.1_f32\n      b = \"c\"\n      b = 'd'\n      a = b\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"assigns union to larger union when source is nilable 1\" do\n    value = run(<<-CRYSTAL).to_string\n      require \"prelude\"\n      a = 1\n      b = nil\n      b = Reference.new\n      a = b\n      a.to_s\n      CRYSTAL\n    value.should contain(\"Reference\")\n  end\n\n  it \"assigns union to larger union when source is nilable 2\" do\n    run(<<-CRYSTAL).to_string.should eq(\"\")\n      require \"prelude\"\n      a = 1\n      b = Reference.new\n      b = nil\n      a = b\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"dispatch call to object method on nilable\" do\n    run(<<-CRYSTAL)\n      require \"prelude\"\n      class Foo\n      end\n\n      a = nil\n      a = Foo.new\n      a.nil?\n      CRYSTAL\n  end\n\n  it \"sorts restrictions when there are unions\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Middle\n      end\n\n      class Top < Middle\n      end\n\n      class Another1\n      end\n\n      class Another2\n      end\n\n      def type_id(type : Another2)\n        1\n      end\n\n      def type_id(y : Top)\n        2\n      end\n\n      def type_id(y : Middle | Another1)\n        3\n      end\n\n      def type_id(y)\n        4\n      end\n\n      t = Top.new || Another1.new\n      type_id t\n      CRYSTAL\n  end\n\n  it \"codegens union to_s\" do\n    str = run(<<-CRYSTAL).to_string\n      require \"prelude\"\n\n      def foo(x : T) forall T\n        T.to_s\n      end\n\n      a = 1 || 1.5\n      foo(a)\n      CRYSTAL\n    str.in?(\"(Int32 | Float64)\", \"(Float64 | Int32)\").should be_true\n  end\n\n  it \"provides T as a tuple literal\" do\n    run(<<-CRYSTAL).to_string.should eq(\"TupleLiteral\")\n      struct Union\n        def self.foo\n          {{ T.class_name }}\n        end\n      end\n      Union(Nil, Int32).foo\n      CRYSTAL\n  end\n\n  it \"respects union payload alignment when upcasting Bool (#14898)\" do\n    mod = codegen(<<-CRYSTAL)\n      x = uninitialized Bool | UInt8[64]\n      x = true\n      CRYSTAL\n\n    str = mod.to_s\n    {% if LibLLVM::IS_LT_150 %}\n      str.should match(/store i512 1, i512\\* %\\d+, align 8/)\n    {% else %}\n      str.should match(/store i512 1, ptr %\\d+, align 8/)\n    {% end %}\n\n    # an i512 store defaults to 16-byte alignment, which is undefined behavior\n    # as it overestimates the actual alignment of `x`'s data field (x86 in\n    # particular segfaults on misaligned 16-byte stores)\n    str.should_not contain(\"align 16\")\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/until_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: until\" do\n  it \"codegens until\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      a = 1\n      until a == 10\n        a = a &+ 1\n      end\n      a\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/var_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: var\" do\n  it \"codegens var\" do\n    run(\"a = 1; 1.5; a\").to_i.should eq(1)\n  end\n\n  it \"codegens var with type declaration\" do\n    run(\"a = (b : Int32 = 1); a\").to_i.should eq(1)\n  end\n\n  it \"codegens ivar assignment when not-nil type filter applies\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          if @a\n            x = @a\n          end\n          @a = 2\n        end\n      end\n\n      foo = Foo.new\n      foo.foo\n      CRYSTAL\n  end\n\n  it \"codegens bug with instance vars and ssa\" do\n    run(<<-CRYSTAL).to_i.should eq(-1)\n      class Foo\n        def initialize\n          @angle = 0\n        end\n\n        def foo\n          if 1 == 2\n            @angle &+= 1\n          else\n            @angle &-= 1\n          end\n        end\n      end\n\n      f = Foo.new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"codegens bug with var, while, if, break and ssa\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      a = 1\n      a = 2\n\n      while 1 == 1\n        if 1 == 2\n          a = 3\n        else\n          break\n        end\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"codegens bug with union of int, nil and string (1): assigning nil to union must fill all zeros\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def foo\n          1\n        end\n      end\n\n      class String\n        def foo\n          2\n        end\n      end\n\n      x = 80\n      if true\n        x = nil\n      else\n        x = \"a\"\n      end\n      x.foo\n      CRYSTAL\n  end\n\n  it \"codegens bug with union of int, nil and string (2): assigning nil to union must fill all zeros\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil\n        def foo\n          1\n        end\n      end\n\n      class String\n        def foo\n          2\n        end\n      end\n\n      x = 443\n      if true\n        x = nil\n      else\n        x = \"a\"\n      end\n      x.foo\n      CRYSTAL\n  end\n\n  it \"codegens assignment that can never be reached\" do\n    codegen(<<-CRYSTAL)\n      if 1 == 1 && (x = nil)\n        z = x\n      end\n      CRYSTAL\n  end\n\n  it \"works with typeof with assignment (#828)\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      class String; def to_i!; 0; end; end\n\n      a = 123\n      typeof(a = \"hello\")\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"assigns to underscore\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      _ = (b = 2)\n      b\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/virtual_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: virtual type\" do\n  it \"call base method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def coco\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      a = Foo.new\n      a = Bar.new\n      a.coco\n      CRYSTAL\n  end\n\n  it \"call overwritten method\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def coco\n          1\n        end\n      end\n\n      class Bar < Foo\n        def coco\n          2\n        end\n      end\n\n      a = Foo.new\n      a = Bar.new\n      a.coco\n      CRYSTAL\n  end\n\n  it \"call base overwritten method\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def coco\n          1\n        end\n      end\n\n      class Bar < Foo\n        def coco\n          2\n        end\n      end\n\n      a = Bar.new\n      a = Foo.new\n      a.coco\n      CRYSTAL\n  end\n\n  it \"dispatch call with virtual type argument\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def coco(x : Bar)\n        1\n      end\n\n      def coco(x)\n        2\n      end\n\n      a = Bar.new\n      a = Foo.new\n      coco(a)\n      CRYSTAL\n  end\n\n  it \"can belong to union\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo; 1; end\n      end\n      class Bar < Foo; end\n      class Baz\n        def foo; 2; end\n      end\n\n      x = Foo.new\n      x = Bar.new\n      x = Baz.new\n      x.foo\n      CRYSTAL\n  end\n\n  it \"lookup instance variables in parent types\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize\n          @x = 1\n        end\n        def foo\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          @x &+ 1\n        end\n      end\n\n      a = Bar.new || Foo.new\n      a.foo\n      CRYSTAL\n  end\n\n  it \"assign instance variable in virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          @x = 1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.new || Bar.new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"codegens non-virtual call that calls virtual call to another virtual call\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          foo2\n        end\n\n        def foo2\n          1\n        end\n      end\n\n      class Bar < Foo\n        def bar\n          foo\n        end\n      end\n\n      bar = Bar.new\n      bar.bar\n      CRYSTAL\n  end\n\n  it \"casts virtual type to base virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Object\n        def bar\n          1\n        end\n      end\n\n      class Foo\n        def foo\n          bar\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.new || Bar.new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"codegens call to Object#to_s from virtual type\" do\n    run(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = Foo.new || Bar.new\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"codegens call to Object#to_s from nilable type\" do\n    run(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Foo\n      end\n\n      a = nil || Foo.new\n      a.to_s\n      CRYSTAL\n  end\n\n  it \"codegens virtual call with explicit self\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          self.bar\n        end\n\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.new || Bar.new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"codegens virtual call with explicit self and nilable type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          self.bar\n        end\n\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      struct Nil\n        def foo\n          2\n        end\n      end\n\n      f = Bar.new || nil\n      f.foo.to_i!\n      CRYSTAL\n  end\n\n  it \"initializes ivars to nil even if object never instantiated\" do\n    run(<<-CRYSTAL)\n      require \"prelude\"\n\n      class Ref\n      end\n\n      class Foo\n        def foo\n          bar self\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n        def initialize\n          @x = Ref.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      def bar(x)\n      end\n\n      def bar(x : Baz)\n        x.x.to_s\n      end\n\n      f = Foo.new || Bar.new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"doesn't lookup in Value+ when virtual type is Object+\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      class Object\n        def foo\n          !nil?\n        end\n      end\n\n      class Foo\n      end\n\n      a = Foo.new\n      a.foo\n      CRYSTAL\n  end\n\n  it \"correctly dispatch call with block when the obj is a virtual type\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def each\n          yield self\n        end\n\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      a = Foo.new\n      a = Bar.new\n\n      y = 0\n      a.each {|x| y = x.foo}\n      y\n      CRYSTAL\n  end\n\n  it \"dispatch call with nilable virtual arg\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x)\n        1\n      end\n\n      def foo(x : Bar)\n        2\n      end\n\n      def lala\n        1 == 2 ? nil : Foo.new || Bar.new\n      end\n\n      x = lala\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"calls class method 1\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      (Foo.new || Bar.new).class.foo\n      CRYSTAL\n  end\n\n  it \"calls class method 2\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      (Bar.new || Foo.new).class.foo\n      CRYSTAL\n  end\n\n  it \"calls class method 3\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Base\n        def self.foo\n          1\n        end\n      end\n\n      class Foo < Base\n      end\n\n      class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      (Foo.new || Base.new).class.foo\n      CRYSTAL\n  end\n\n  it \"dispatches on virtual metaclass (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def self.coco\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.coco\n          2\n        end\n      end\n\n      some_long_var = Foo || Bar\n      some_long_var.coco\n      CRYSTAL\n  end\n\n  it \"dispatches on virtual metaclass (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.coco\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.coco\n          2\n        end\n      end\n\n      some_long_var = Bar || Foo\n      some_long_var.coco\n      CRYSTAL\n  end\n\n  it \"dispatches on virtual metaclass (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.coco\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.coco\n          2\n        end\n      end\n\n      class Baz < Bar\n      end\n\n      some_long_var = Baz || Foo\n      some_long_var.coco\n      CRYSTAL\n  end\n\n  it \"codegens new for simple type, then for virtual\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      x = Foo.new(1)\n      y = (Foo || Bar).new(1)\n      y.x\n      CRYSTAL\n  end\n\n  it \"codegens new twice for virtual\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      y = (Foo || Bar).new(1)\n      y = (Foo || Bar).new(1)\n      y.x\n      CRYSTAL\n  end\n\n  it \"codegens allocate for virtual type with custom new\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.new\n          allocate\n        end\n\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      foo = (Bar || Foo).new\n      foo.foo\n      CRYSTAL\n  end\n\n  it \"returns type with virtual type def type\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      def foo\n        return Foo.new if 1 == 1\n        Bar.new\n      end\n\n      foo.foo\n      CRYSTAL\n  end\n\n  it \"casts virtual type to union\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      class Baz < Foo\n        def foo\n          3\n        end\n      end\n\n      def x(f : Bar | Baz)\n        f.foo\n      end\n\n      def x(f)\n        0\n      end\n\n      f = Baz.new || Bar.new\n      x(f)\n      CRYSTAL\n  end\n\n  it \"casts union to virtual\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      module Moo\n      end\n\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n\n      class Baz < Foo\n        include Moo\n      end\n\n      def foo(x : Moo)\n        p = Pointer(Foo).malloc(1_u64)\n        p.value = x\n        p.value.object_id\n      end\n\n      def foo(x)\n        0_u64\n      end\n\n      reference = Bar.new || Baz.new\n      reference.object_id == foo(reference)\n      CRYSTAL\n  end\n\n  it \"codegens virtual method of abstract metaclass\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      abstract class Bar < Foo\n        def self.foo\n          2\n        end\n      end\n\n      class Baz < Foo\n        def self.foo\n          3\n        end\n      end\n\n      (Bar || Foo || Baz).foo\n      CRYSTAL\n  end\n\n  it \"codegens new for virtual class with one type\" do\n    run(<<-CRYSTAL).to_i.should eq(123)\n      require \"prelude\"\n\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          123\n        end\n      end\n\n      p = Pointer(Foo.class).malloc(1_u64)\n      p.value = Bar\n      p.value.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens new for virtual class with two types\" do\n    run(<<-CRYSTAL).to_i.should eq(456)\n      require \"prelude\"\n\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          123\n        end\n      end\n\n      class Baz < Foo\n        def foo\n          456\n        end\n      end\n\n      p = Pointer(Foo.class).malloc(1_u64)\n      p.value = Bar\n      p.value = Baz\n      p.value.new.foo\n      CRYSTAL\n  end\n\n  it \"codegens new for new on virtual abstract class (#3835)\" do\n    run(<<-CRYSTAL).to_string.should eq(\"Can't instantiate abstract class Foo\")\n      require \"prelude\"\n\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          123\n        end\n      end\n\n      begin\n        (Foo || Bar).new\n        \"\"\n      rescue ex\n        ex.message.not_nil!\n      end\n      CRYSTAL\n  end\n\n  it \"casts metaclass union type to virtual metaclass type (#6298)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def self.x\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.x\n          2\n        end\n      end\n\n      class Baz < Foo\n        def self.x\n          3\n        end\n      end\n\n      class Moo\n        def initialize(@foo : Foo.class)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      klass = Bar || Baz\n      Moo.new(klass).foo.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/void_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Code gen: void\" do\n  it \"codegens void assignment\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      fun foo : Void\n      end\n\n      a = foo\n      a\n      1\n      CRYSTAL\n  end\n\n  it \"codegens void assignment in case\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      fun foo : Void\n      end\n\n      def bar\n        case 1\n        when 1\n          foo\n        when 2\n          raise \"oh no\"\n        else\n        end\n      end\n\n      bar\n      1\n      CRYSTAL\n  end\n\n  it \"codegens void assignment in case with local variable\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      require \"prelude\"\n\n      fun foo : Void\n      end\n\n      def bar\n        case 1\n        when 1\n          a = 1\n          foo\n        when 2\n          raise \"oh no\"\n        else\n        end\n      end\n\n      bar\n      1\n      CRYSTAL\n  end\n\n  it \"codegens unreachable code\" do\n    run(<<-CRYSTAL)\n      a = nil\n      if a\n        b = a.foo\n      end\n      CRYSTAL\n  end\n\n  it \"codegens no return assignment\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      a = LibC.exit\n      a\n      CRYSTAL\n  end\n\n  it \"allows passing void as argument to method\" do\n    codegen(<<-CRYSTAL)\n      lib LibC\n        fun foo\n      end\n\n      def bar(x)\n      end\n\n      def baz\n        LibC.foo\n      end\n\n      bar(baz)\n      CRYSTAL\n  end\n\n  it \"returns void from nil functions, doesn't crash when passing value\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      def baz(x)\n        1\n      end\n\n      struct Nil\n        def bar\n          baz(self)\n        end\n      end\n\n      def foo\n        1\n        nil\n      end\n\n      foo.bar\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/while_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Codegen: while\" do\n  it \"codegens def with while\" do\n    run(\"def foo; while false; 1; end; end; foo\")\n  end\n\n  it \"codegens while with false\" do\n    run(\"a = 1; while false; a = 2; end; a\").to_i.should eq(1)\n  end\n\n  it \"codegens while with non-false condition\" do\n    run(\"a = 1; while a < 10; a = a &+ 1; end; a\").to_i.should eq(10)\n  end\n\n  it \"break without value\" do\n    run(\"a = 0; while a < 10; a &+= 1; break; end; a\").to_i.should eq(1)\n  end\n\n  it \"conditional break without value\" do\n    run(\"a = 0; while a < 10; a &+= 1; break if a > 5; end; a\").to_i.should eq(6)\n  end\n\n  it \"break with value\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      struct Nil; def to_i!; 0; end; end\n\n      a = 0\n      b = while a < 10\n        a &+= 1\n        break a &+ 3\n      end\n      b.to_i!\n      CRYSTAL\n  end\n\n  it \"conditional break with value\" do\n    run(<<-CRYSTAL).to_i.should eq(9)\n      struct Nil; def to_i!; 0; end; end\n\n      a = 0\n      b = while a < 10\n        a &+= 1\n        break a &+ 3 if a > 5\n      end\n      b.to_i!\n      CRYSTAL\n  end\n\n  it \"break with value, condition fails\" do\n    run(<<-CRYSTAL).to_b.should be_true\n      a = while 1 == 2\n        break 1\n      end\n      a.nil?\n      CRYSTAL\n  end\n\n  it \"endless break with value\" do\n    run(<<-CRYSTAL).to_i.should eq(4)\n      a = 0\n      while true\n        a &+= 1\n        break a &+ 3\n      end\n      CRYSTAL\n  end\n\n  it \"endless conditional break with value\" do\n    run(<<-CRYSTAL).to_i.should eq(9)\n      a = 0\n      while true\n        a &+= 1\n        break a &+ 3 if a > 5\n      end\n      CRYSTAL\n  end\n\n  it \"codegens endless while\" do\n    codegen \"while true; end\"\n  end\n\n  it \"codegens while with declared var 1\" do\n    run(<<-CRYSTAL).to_i.should eq(0)\n      struct Nil; def to_i!; 0; end; end\n\n      while 1 == 2\n        a = 2\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens while with declared var 2\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      struct Nil; def to_i!; 0; end; end\n\n      while 1 == 1\n        a = 2\n        if 1 == 1\n          a = 3\n          break\n        end\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"codegens while with declared var 3\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Nil; def to_i!; 0; end; end\n\n      while 1 == 1\n        a = 1\n        if a\n          break\n        else\n          2\n        end\n      end\n      a.to_i!\n      CRYSTAL\n  end\n\n  it \"skip block with next\" do\n    run(<<-CRYSTAL).to_i.should eq(25)\n      i = 0\n      x = 0\n\n      while i < 10\n        i &+= 1\n        next if i.unsafe_mod(2) == 0\n        x &+= i\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"doesn't crash on a = NoReturn\" do\n    codegen(<<-CRYSTAL)\n      lib LibFoo\n        fun foo : NoReturn\n      end\n\n      while a = LibFoo.foo\n        a\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #2767\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      x = 'x'\n      while 1 == 2\n        if true\n          x = (LibC.exit(0); 1)\n        end\n      end\n      x\n      10\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #2767 (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      x = 'x'\n      while 1 == 2\n        x = LibC.exit(0).as(Int32)\n      end\n      x\n      10\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #2767 (3)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      x = 'x'\n      while 1 == 2\n        if true\n          x = if true\n            LibC.exit(0)\n          else\n            3\n          end\n        end\n      end\n      x\n      10\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #2767 (4)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      x = 'x'\n      while 1 == 2\n        if true\n          x = (LibC.exit(0); 1)\n        end\n        y = x\n        z = y\n        x = z\n      end\n      x\n      10\n      CRYSTAL\n  end\n\n  it \"doesn't crash on while true begin break rescue (#7786)\" do\n    codegen(<<-CRYSTAL)\n      require \"prelude\"\n\n      while true\n        begin\n          foo = 1\n          break\n        rescue\n        end\n      end\n      foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/codegen/yield_with_scope_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: yield with scope\" do\n  it \"uses scope in global method\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n      def foo; with 1 yield; end\n\n      foo do\n        succ\n      end\n      CRYSTAL\n  end\n\n  it \"uses scope in instance method\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      require \"prelude\"\n      def foo; with 1 yield; end\n\n      class Foo\n        def test\n          foo do\n            succ\n          end\n        end\n\n        def succ\n          10\n        end\n      end\n\n      Foo.new.test\n      CRYSTAL\n  end\n\n  it \"it uses self for instance method\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      require \"prelude\"\n      def foo; with 1 yield; end\n\n      class Foo\n        def test\n          foo do\n            self.succ\n          end\n        end\n\n        def succ\n          10\n        end\n      end\n\n      Foo.new.test\n      CRYSTAL\n  end\n\n  it \"it invokes global method inside block of yield scope\" do\n    run(<<-CRYSTAL).to_i.should eq(3)\n      require \"prelude\"\n\n      def foo\n        with -1 yield\n      end\n\n      def plus_two(x)\n        x + 2\n      end\n\n      foo do\n        plus_two abs\n      end\n      CRYSTAL\n  end\n\n  it \"generate right code when yielding struct as scope\" do\n    run(<<-CRYSTAL).to_i.should eq(1)\n      struct Foo\n        def bar; end\n      end\n\n      def foo\n        with Foo.new yield\n        1\n      end\n\n      foo { bar }\n      CRYSTAL\n  end\n\n  it \"doesn't explode if specifying &block but never using it (#181)\" do\n    codegen(<<-CRYSTAL)\n      class Foo\n        def a(&block)\n          with self yield\n        end\n        def aa\n        end\n      end\n      a = Foo.new\n      a.a { aa }\n      a.a { aa }\n      CRYSTAL\n  end\n\n  it \"uses instance variable of enclosing scope\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          with self yield\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = 1\n        end\n\n        def bar\n          Foo.new.foo do\n            @x &+ 1\n          end\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"uses method of enclosing scope\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          with self yield\n        end\n      end\n\n      class Bar\n        def bar\n          Foo.new.foo do\n            baz &+ 1\n          end\n        end\n\n        def baz\n          1\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"uses method of with object\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def foo\n          with self yield\n        end\n\n        def coco\n          @x &+ 1\n        end\n      end\n\n      class Bar\n        def bar\n          Foo.new.foo do\n            coco\n          end\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"yields with dispatch (#2171) (1)\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      class Foo\n        def method(x : Int32)\n          10\n        end\n\n        def method(x : Float64)\n          20\n        end\n      end\n\n      def foo\n        with Foo.new yield\n      end\n\n      foo do\n        method(1 || 1.5)\n      end\n      CRYSTAL\n  end\n\n  it \"yields virtual type (#2171) (2)\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def method\n          1\n        end\n      end\n\n      class Bar < Foo\n        def method\n          2\n        end\n      end\n\n      def foo\n        with (Bar.new || Foo.new) yield\n      end\n\n      foo { method }\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/compiler_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\n\ndescribe \"Compiler\" do\n  it \"has a valid version\" do\n    SemanticVersion.parse(Crystal::Config.version)\n  end\n\n  it \"compiles a file\" do\n    with_temp_executable \"compiler_spec_output\" do |path|\n      Crystal::Command.run [\"build\"].concat(program_flags_options).concat([compiler_datapath(\"compiler_sample\"), \"-o\", path])\n\n      File.exists?(path).should be_true\n\n      `#{Process.quote(path)}`.should eq(\"Hello!\")\n    end\n  end\n\n  it \"runs subcommand in preference to a filename \" do\n    Dir.cd compiler_datapath do\n      with_temp_executable \"compiler_spec_output\" do |path|\n        Crystal::Command.run [\"build\"].concat(program_flags_options).concat([\"compiler_sample\", \"-o\", path])\n\n        File.exists?(path).should be_true\n\n        `#{Process.quote(path)}`.should eq(\"Hello!\")\n      end\n    end\n  end\n\n  it \"treats all arguments post-filename as program arguments\" do\n    with_tempfile \"args_test\" do |path|\n      Process.run(ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"]? || \"bin/crystal\", [File.join(compiler_datapath, \"args_test\"), \"-Dother_flag\", \"--\", \"bar\", path])\n\n      File.read(path).should eq(<<-FILE)\n        [\"-Dother_flag\", \"--\", \"bar\"]\n        {other_flag: false}\n        FILE\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/config_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\n\ndescribe Crystal::Config do\n  it \".host_target\" do\n    {% begin %}\n      {% host_triple = Crystal.constant(\"HOST_TRIPLE\") || Crystal::DESCRIPTION.lines[-1].gsub(/^Default target: /, \"\") %}\n      Crystal::Config.host_target.should eq Crystal::Codegen::Target.new({{ host_triple }})\n    {% end %}\n  end\n\n  {% if flag?(:linux) %}\n    it \".linux_runtime_libc\" do\n      Crystal::Config.linux_runtime_libc.should eq {{ flag?(:musl) ? \"musl\" : \"gnu\" }}\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/compiler/crystal/commands/clear_cache_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nclass Crystal::CacheDir\n  class_setter instance\n\n  def initialize(@dir)\n    Dir.mkdir_p(dir)\n  end\nend\n\ndescribe Crystal::Command do\n  describe \"clear_cache\" do\n    around_each do |example|\n      old_cache_dir = CacheDir.instance\n      temp_dir_name = File.tempname\n      begin\n        CacheDir.instance = CacheDir.new(temp_dir_name)\n        example.run\n      ensure\n        FileUtils.rm_rf(temp_dir_name)\n        CacheDir.instance = old_cache_dir\n      end\n    end\n\n    it \"clears any cached compiler files\" do\n      file_path = File.tempname(dir: CacheDir.instance.dir)\n      Dir.mkdir_p(File.dirname(file_path))\n      File.touch(file_path)\n      File.exists?(file_path).should be_true\n\n      Crystal::Command.run([\"clear_cache\"])\n\n      File.exists?(file_path).should be_false\n      File.exists?(CacheDir.instance.dir).should be_false\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/context_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def processed_context_visitor(code, cursor_location)\n  compiler = Compiler.new\n  compiler.no_codegen = true\n  compiler.prelude = \"empty\"\n  result = compiler.compile(Compiler::Source.new(\".\", code), \"fake-no-build\")\n\n  visitor = ContextVisitor.new(cursor_location)\n  process_result = visitor.process(result)\n\n  {visitor, process_result}\nend\n\nprivate def run_context_tool(code, &)\n  cursor_location = nil\n\n  code.lines.each_with_index do |line, line_number_0|\n    if column_number = line.index('‸')\n      cursor_location = Location.new(\".\", line_number_0 + 1, column_number + 1)\n    end\n  end\n\n  code = code.delete('‸')\n\n  if cursor_location\n    _, result = processed_context_visitor(code, cursor_location)\n\n    yield result\n  else\n    raise \"no cursor found in spec\"\n  end\nend\n\nprivate def assert_context_keys(code, *variables)\n  run_context_tool(code) do |result|\n    result.contexts.should_not be_nil\n    result.contexts.not_nil!.each do |context|\n      context.keys.should eq(variables.to_a)\n    end\n  end\nend\n\nprivate def assert_context_includes(code, variable, var_types)\n  run_context_tool(code) do |result|\n    result.contexts.should_not be_nil\n    result.contexts.not_nil!.map(&.[variable].to_s).should eq(var_types)\n  end\nend\n\n# References\n#\n#   ‸ marks location of the cursor to use\n#\ndescribe \"context\" do\n  it \"includes args\" do\n    assert_context_includes %(\n      def foo(a)\n        ‸\n        1\n      end\n\n      foo(1i64)\n    ), \"a\", [\"Int64\"]\n  end\n\n  it \"consider different instances of def\" do\n    assert_context_includes %(\n      def foo(a)\n        ‸\n        1\n      end\n\n      foo(1i64)\n      foo(\"foo\")\n    ), \"a\", [\"Int64\", \"String\"]\n  end\n\n  it \"includes assignments\" do\n    assert_context_includes %(\n      def foo(a)\n        b = a\n        ‸\n        1\n      end\n\n      foo(1i64)\n      foo(\"foo\")\n    ), \"b\", [\"Int64\", \"String\"]\n  end\n\n  it \"includes block args\" do\n    assert_context_includes %(\n      def bar(x)\n        yield x\n      end\n\n      def foo(a)\n        bar a do |b|\n          ‸\n          1\n        end\n        1\n      end\n\n      foo(1i64)\n      foo(\"foo\")\n    ), \"b\", [\"Int64\", \"String\"]\n  end\n\n  it \"includes top level vars\" do\n    assert_context_includes %(\n      a = 0i64\n      ‸\n      1\n    ), \"a\", [\"Int64\"]\n  end\n\n  it \"includes last call\" do\n    assert_context_includes %(\n      class Foo\n        def lorem\n          @lorem\n        end\n\n        def initialize(@lorem : Int64)\n        end\n      end\n\n      def foo(f)\n      end\n\n      f = Foo.new(1i64)\n\n      foo f.lo‸rem\n      1\n    ), \"f.lorem\", [\"Int64\"]\n  end\n\n  it \"does not includes temp variables\" do\n    assert_context_keys %(\n      a = 0i64\n      ‸\n      1\n    ), \"a\"\n  end\n\n  it \"does includes regex special variables\" do\n    assert_context_keys %(\n      def match\n        $~ = \"match\"\n      end\n\n      def foo\n        s = \"foo\"\n        match\n        ‸\n        0\n      end\n\n      foo\n    ), \"s\", \"$~\"\n  end\n\n  it \"does includes self on classes\" do\n    assert_context_includes %(\n      class Foo\n        def foo\n          ‸\n          0\n        end\n      end\n\n      f = Foo.new\n      f.foo\n      0\n    ), \"self\", [\"Foo\"]\n  end\n\n  it \"does includes args, instance vars, local variables and expressions on instance methods\" do\n    assert_context_keys %(\n      class Foo\n        def foo(the_arg)\n          @ivar = 2\n          the_arg.fo‸o(self)\n          0\n        end\n      end\n\n      f = Foo.new\n      f.foo(Foo.new)\n      0\n    ), \"self\", \"@ivar\", \"the_arg\", \"the_arg.foo(self)\"\n  end\n\n  it \"can handle union types\" do\n    assert_context_includes %(\n    a = 1_i64.as(Int64 | String)\n    ‸\n    0\n    ), \"a\", [\"(Int64 | String)\"]\n  end\n\n  it \"can display text output\" do\n    run_context_tool(%(\n    a = 1_i64.as(Int64 | String)\n    ‸\n    0\n    )) do |result|\n      String::Builder.build do |io|\n        result.to_text(io)\n      end.should eq %(1 possible context found\n\n| Expr | Type           |\n-------------------------\n| a    | Int64 | String |\n)\n    end\n  end\n\n  it \"can display json output\" do\n    run_context_tool(%(\n    a = 1_i64.as(Int64 | String)\n    ‸\n    0\n    )) do |result|\n      String::Builder.build do |io|\n        result.to_json(io)\n      end.should eq %({\"status\":\"ok\",\"message\":\"1 possible context found\",\"contexts\":[{\"a\":\"Int64 | String\"}]})\n    end\n  end\n\n  it \"can get context of empty def\" do\n    assert_context_includes %(\n    def foo(a)\n      ‸\n    end\n\n    foo(0i64)\n    ), \"a\", [\"Int64\"]\n  end\n\n  it \"can get context of empty yielded block\" do\n    assert_context_includes %(\n    def it_like\n      yield\n    end\n\n    it_like do\n      a = 1i64‸\n    end\n    ), \"a\", [\"Int64\"]\n  end\n\n  it \"can get context of yielded block\" do\n    assert_context_keys %(\n    def foo(a)\n      b = a + 1\n      ‸\n      yield b\n    end\n\n    foo 1 do |x|\n    end\n    ), \"a\", \"b\"\n  end\n\n  it \"can get context of nested yielded block\" do\n    assert_context_keys %(\n    def foo(a)\n      b = a + 1\n      ‸\n      yield b\n    end\n\n    def bar\n      foo 1 do |x|\n        yield x\n      end\n    end\n\n    bar do |y|\n    end\n    ), \"a\", \"b\"\n  end\n\n  it \"can get context inside a module\" do\n    assert_context_includes %(\n    module Foo\n      class Bar\n        def bar(o)\n          ‸\n        end\n      end\n    end\n\n    Foo::Bar.new.bar(\"foo\")\n    ), \"o\", [\"String\"]\n  end\n\n  it \"can get context inside class methods\" do\n    assert_context_includes %(\n    class Bar\n      def self.bar(o)\n        ‸\n      end\n    end\n\n    Bar.bar(\"foo\")\n    ), \"o\", [\"String\"]\n  end\n\n  it \"can get context inside initialize\" do\n    assert_context_keys %(\n    class Bar\n      def initialize(@ivar : String)\n        ‸\n      end\n    end\n\n    Bar.new(\"s\")\n    ), \"self\", \"@ivar\", \"ivar\"\n  end\n\n  it \"can get context in generic class\" do\n    assert_context_keys %(\n    class Foo(T, S)\n      def foo(a)\n        ‸\n      end\n    end\n\n    Foo(String, Char).new.foo(1)\n    ), \"T\", \"S\", \"self\", \"a\"\n\n    assert_context_includes %(\n    class Foo(T, S)\n      def foo(a)\n        ‸\n      end\n    end\n\n    Foo(String, Char).new.foo(1)\n    ), \"T\", [\"String\"]\n  end\n\n  it \"can get context in contained class' class method\" do\n    assert_context_keys %(\n    module Baz\n      class Bar(T)\n        class Foo\n          def self.bar_foo(a)\n            ‸\n          end\n        end\n      end\n    end\n\n    Baz::Bar::Foo.bar_foo(1)\n    ), \"self\", \"a\"\n  end\n\n  it \"use type filters from is_a?\" do\n    assert_context_includes %(\n    def foo(c)\n      if c.is_a?(String)\n        ‸\n      end\n    end\n\n    foo(1 < 0 ? nil : \"s\")\n    ), \"c\", [\"String\"]\n  end\n\n  it \"use type filters from if var\" do\n    assert_context_includes %(\n    def foo(c)\n      if c\n        ‸\n      end\n    end\n\n    foo(1 < 0 ? nil : \"s\")\n    ), \"c\", [\"String\"]\n  end\n\n  it \"can get context in file private method\" do\n    assert_context_keys %(\n    private def foo(a)\n      ‸\n    end\n\n    foo 100\n    ), \"a\"\n  end\n\n  it \"can get context in file private module\" do\n    assert_context_keys %(\n    private module Foo\n      def self.foo(a)\n        ‸\n      end\n    end\n\n    Foo.foo 100\n    ), \"self\", \"a\"\n  end\n\n  it \"can't get context from uncalled method\" do\n    run_context_tool %(\n    def foo(value)\n      ‸\n    end\n    ) do |result|\n      result.status.should eq(\"failed\")\n      result.message.should match(/never called/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/directives_spec.cr",
    "content": "require \"../../../spec_helper\"\n\ndescribe Crystal::Doc::Generator do\n  context \":nodoc:\" do\n    it \"hides documentation from being generated for methods\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # :nodoc:\n          #\n          # Some docs\n          def foo\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").should be_nil\n    end\n\n    it \"hides documentation from being generated for classes\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        # :nodoc:\n        class Foo\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      generator.must_include?(program.types[\"Foo\"]).should be_false\n    end\n  end\n\n  context \":showdoc:\" do\n    it \"shows documentation for private methods\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # :showdoc:\n          #\n          # Some docs\n          private def foo\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      a_def = generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").not_nil!\n      a_def.doc.should eq(\"Some docs\")\n      a_def.visibility.should eq(\"private\")\n    end\n\n    it \"shows documentation for private macros\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # :showdoc:\n          #\n          # Some docs\n          private macro foo\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      a_macro = generator.type(program.types[\"Foo\"]).lookup_macro(\"foo\").not_nil!\n      a_macro.doc.should eq(\"Some docs\")\n      a_macro.visibility.should eq(\"private\")\n    end\n\n    it \"shows documentation for nested objects if a lib is marked with :showdoc:\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        # :showdoc:\n        lib Foo\n          # docs for `foo`\n          fun foo\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n\n      generator.must_include?(program.types[\"Foo\"]).should be_true\n      generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").should_not be_nil\n    end\n\n    it \"does not include documentation for methods within a :nodoc: namespace\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        # :nodoc:\n        class Foo\n          # :showdoc:\n          #\n          # Some docs\n          private def foo\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n\n      # If namespace isn't included, don't need to check if the method is included\n      generator.must_include?(program.types[\"Foo\"]).should be_false\n    end\n\n    it \"does not include documentation for private and protected methods and objects in a :showdoc: namespace\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        # :showdoc:\n        class Foo\n          # Some docs for `foo`\n          private def foo\n          end\n\n          # Some docs for `bar`\n          protected def bar\n          end\n\n          # Some docs for `Baz`\n          private class Baz\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n\n      generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").should be_nil\n      generator.type(program.types[\"Foo\"]).lookup_method(\"bar\").should be_nil\n\n      generator.must_include?(generator.type(program.types[\"Foo\"]).lookup_path(\"Baz\")).should be_false\n    end\n\n    it \"does not include documentation for a :showdoc: fun inside a lib not marked with :showdoc:\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        lib Foo\n          # :showdoc:\n          fun foo\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n\n      generator.must_include?(program.types[\"Foo\"]).should be_false\n    end\n\n    it \"doesn't show a method marked :nodoc: within a :showdoc: namespace\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        # :showdoc:\n        class Foo\n          # :nodoc:\n          # Some docs for `foo`\n          def foo\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").should be_nil\n    end\n\n    it \"doesn't show a fun marked :nodoc: within a :showdoc: lib\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        # :showdoc:\n        lib Foo\n          # :nodoc:\n          # Some docs for `foo`\n          fun foo\n\n          fun bar\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").should be_nil\n      generator.type(program.types[\"Foo\"]).lookup_method(\"bar\").should_not be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/doc_renderer_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def assert_code_link(obj, before, after = before)\n  renderer = Doc::MarkdDocRenderer.new(obj, Markd::Options.new)\n  renderer.expand_code_links(before).should eq(after)\nend\n\nprivate def it_renders(context, input, output, file = __FILE__, line = __LINE__)\n  it \"renders #{input.inspect}\", file, line do\n    c = context\n    c ||= begin\n      program = Program.new\n      generator = Doc::Generator.new(program, [\"\"])\n      generator.type(program)\n    end\n    options = Markd::Options.new\n    document = Markd::Parser.parse(input, options)\n    renderer = Doc::MarkdDocRenderer.new(c, options)\n\n    renderer.render(document).chomp.should eq(output), file: file, line: line\n  end\nend\n\ndescribe Doc::MarkdDocRenderer do\n  describe \"expand_code_links\" do\n    program = semantic(<<-CRYSTAL, wants_doc: true).program\n      class Base\n        def foo\n        end\n        def bar\n        end\n        def self.baz\n        end\n\n        def foo2(a, b)\n        end\n        def foo3(a, b, c)\n        end\n\n        def que?\n        end\n        def one!(one)\n        end\n\n        def <=(other)\n        end\n\n        class Nested\n          CONST = true\n\n          def foo\n          end\n        end\n      end\n\n      class Sub < Base\n        def foo\n        end\n      end\n\n      class A\n        def foo; end\n        def bar; end\n        def self.baz; end\n      end\n      CRYSTAL\n    generator = Doc::Generator.new(program, [\"\"])\n\n    base = generator.type(program.types[\"Base\"])\n    base_foo = base.lookup_method(\"foo\").not_nil!\n    sub = generator.type(program.types[\"Sub\"])\n    sub_foo = sub.lookup_method(\"foo\").not_nil!\n    nested = generator.type(program.types[\"Base\"].types[\"Nested\"])\n    nested_foo = nested.lookup_method(\"foo\").not_nil!\n    single_char_class = generator.type(program.types[\"A\"])\n    single_char_class_foo = single_char_class.lookup_method(\"foo\").not_nil!\n\n    it \"finds sibling methods\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"bar\", %(<a href=\"Base.html#bar-instance-method\">#bar</a>))\n        assert_code_link(obj, \"baz\", %(<a href=\"Base.html#baz-class-method\">.baz</a>))\n      end\n    end\n\n    it \"finds sibling methods\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"#bar\", %(<a href=\"Base.html#bar-instance-method\">#bar</a>))\n        assert_code_link(obj, \".baz\", %(<a href=\"Base.html#baz-class-method\">.baz</a>))\n      end\n    end\n\n    it \"matches methods on single-character class names\" do\n      {single_char_class, single_char_class_foo}.each do |obj|\n        assert_code_link(obj, \"#bar\", %(<a href=\"A.html#bar-instance-method\">#bar</a>))\n        assert_code_link(obj, \".baz\", %(<a href=\"A.html#baz-class-method\">.baz</a>))\n      end\n    end\n\n    it \"doesn't spuriously match range literals\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"(0..baz)\")\n        assert_code_link(obj, \"(0...baz)\")\n        assert_code_link(obj, \"0..baz\")\n        assert_code_link(obj, \"0...baz\")\n      end\n    end\n\n    it \"doesn't find substrings for methods\" do\n      assert_code_link(base_foo, \"not bar\")\n      assert_code_link(base_foo, \"bazzy\")\n    end\n\n    it \"doesn't find sibling methods of wrong type\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Wrong#bar\")\n        assert_code_link(obj, \"Wrong.bar\")\n      end\n    end\n\n    it \"doesn't find sibling methods with fake receiver\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"wrong#bar\")\n        assert_code_link(obj, \"wrong.bar\")\n      end\n    end\n\n    it \"finds sibling methods with self receiver\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"self.bar\", %(self<a href=\"Base.html#bar-instance-method\">.bar</a>))\n      end\n    end\n\n    it \"doesn't find parents' methods\" do\n      {sub, sub_foo, nested, nested_foo}.each do |obj|\n        assert_code_link(obj, \"bar\")\n        assert_code_link(obj, \"baz\")\n      end\n    end\n\n    it \"doesn't find parents' methods\" do\n      {sub, sub_foo, nested, nested_foo}.each do |obj|\n        assert_code_link(obj, \"#bar\")\n        assert_code_link(obj, \".baz\")\n      end\n    end\n\n    it \"doesn't match with different separator\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \",baz\")\n        assert_code_link(obj, \"Base:bar\", %(<a href=\"Base.html\">Base</a>:bar))\n      end\n    end\n\n    it \"finds method with args\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"foo2(a, b)\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2(a, b)</a>))\n        assert_code_link(obj, \"#foo2(a, a)\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2(a, a)</a>))\n        assert_code_link(obj, \"Base#foo2(a, a)\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">Base#foo2(a, a)</a>))\n      end\n    end\n\n    it \"finds method with zero args\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"bar()\", %(<a href=\"Base.html#bar-instance-method\">#bar()</a>))\n        assert_code_link(obj, \"#bar()\", %(<a href=\"Base.html#bar-instance-method\">#bar()</a>))\n        assert_code_link(obj, \"Base#bar()\", %(<a href=\"Base.html#bar-instance-method\">Base#bar()</a>))\n      end\n    end\n\n    it \"doesn't find method with wrong number of args\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"#foo2(a, a, a, a)\")\n        assert_code_link(obj, \"#bar(a)\")\n      end\n    end\n\n    it \"doesn't find method with wrong number of args\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Base#foo2(a)\")\n        assert_code_link(obj, \"Base#bar(a)\")\n      end\n    end\n\n    it \"finds method with unspecified args\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"foo2\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2</a>))\n        assert_code_link(obj, \"#foo2\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2</a>))\n        assert_code_link(obj, \"Base#foo2\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">Base#foo2</a>))\n      end\n    end\n\n    it \"finds method with args even with empty brackets\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"foo2()\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2()</a>))\n        assert_code_link(obj, \"#foo2()\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2()</a>))\n        assert_code_link(obj, \"Base#foo2()\", %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">Base#foo2()</a>))\n      end\n    end\n\n    it \"finds method with question mark\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"que?\", %(<a href=\"Base.html#que%3F-instance-method\">#que?</a>))\n        assert_code_link(obj, \"#que?\", %(<a href=\"Base.html#que%3F-instance-method\">#que?</a>))\n        assert_code_link(obj, \"Base#que?\", %(<a href=\"Base.html#que%3F-instance-method\">Base#que?</a>))\n      end\n    end\n\n    it \"finds method with exclamation mark\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"one!(one)\", %(<a href=\"Base.html#one%21%28one%29-instance-method\">#one!(one)</a>))\n        assert_code_link(obj, \"#one!(one)\", %(<a href=\"Base.html#one%21%28one%29-instance-method\">#one!(one)</a>))\n        assert_code_link(obj, \"Base#one!(one)\", %(<a href=\"Base.html#one%21%28one%29-instance-method\">Base#one!(one)</a>))\n      end\n    end\n\n    it \"finds operator method\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"<=(other)\", %(<a href=\"Base.html#%3C%3D%28other%29-instance-method\">#<=(other)</a>))\n        assert_code_link(obj, \"#<=(other)\", %(<a href=\"Base.html#%3C%3D%28other%29-instance-method\">#<=(other)</a>))\n        assert_code_link(obj, \"Base#<=(other)\", %(<a href=\"Base.html#%3C%3D%28other%29-instance-method\">Base#<=(other)</a>))\n      end\n    end\n\n    it \"finds operator method with unspecified args\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"<=\", %(<a href=\"Base.html#%3C%3D%28other%29-instance-method\">#<=</a>))\n        assert_code_link(obj, \"#<=\", %(<a href=\"Base.html#%3C%3D%28other%29-instance-method\">#<=</a>))\n        assert_code_link(obj, \"Base#<=\", %(<a href=\"Base.html#%3C%3D%28other%29-instance-method\">Base#<=</a>))\n      end\n    end\n\n    it \"finds methods of a type\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Base#bar\", %(<a href=\"Base.html#bar-instance-method\">Base#bar</a>))\n        assert_code_link(obj, \"Base.baz\", %(<a href=\"Base.html#baz-class-method\">Base.baz</a>))\n      end\n    end\n\n    it \"finds method of an absolute type\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"::Base::Nested#foo\", %(<a href=\"Base/Nested.html#foo-instance-method\">::Base::Nested#foo</a>))\n        assert_code_link(obj, \"::Base.baz\", %(<a href=\"Base.html#baz-class-method\">::Base.baz</a>))\n      end\n    end\n\n    pending \"doesn't find wrong kind of sibling methods\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \".bar\")\n        assert_code_link(obj, \"#baz\")\n      end\n    end\n\n    pending \"doesn't find wrong kind of methods\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Base.bar\")\n        assert_code_link(obj, \"Base#baz\")\n      end\n    end\n\n    it \"finds multiple methods with brackets\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"#foo2(a, a) and Base#foo3(a,b,  c)\",\n          %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">#foo2(a, a)</a> and <a href=\"Base.html#foo3%28a%2Cb%2Cc%29-instance-method\">Base#foo3(a,b,  c)</a>))\n      end\n    end\n\n    it \"finds types from base\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Base and Sub and Nested\",\n          %(<a href=\"Base.html\">Base</a> and <a href=\"Sub.html\">Sub</a> and <a href=\"Base/Nested.html\">Nested</a>))\n      end\n    end\n\n    it \"finds types from nested\" do\n      {nested, nested_foo}.each do |obj|\n        assert_code_link(obj, \"Base and Sub and Nested\",\n          %(<a href=\"../Base.html\">Base</a> and <a href=\"../Sub.html\">Sub</a> and <a href=\"../Base/Nested.html\">Nested</a>))\n      end\n    end\n\n    it \"finds constant\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Nested::CONST\", %(<a href=\"Base/Nested.html#CONST\">Nested::CONST</a>))\n      end\n    end\n\n    it \"finds nested type\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Base::Nested\", %(<a href=\"Base/Nested.html\">Base::Nested</a>))\n      end\n    end\n\n    it \"finds absolute type\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"::Base::Nested\",\n          %(<a href=\"Base/Nested.html\">::Base::Nested</a>))\n      end\n    end\n\n    it \"doesn't find wrong absolute type\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"::Nested\")\n      end\n    end\n\n    it \"doesn't find type not at word boundary\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"aBase\")\n      end\n    end\n\n    it \"finds multiple kinds of things\" do\n      {base, base_foo}.each do |obj|\n        assert_code_link(obj, \"Base#foo2(a, a) and #foo3 and Base\",\n          %(<a href=\"Base.html#foo2%28a%2Cb%29-instance-method\">Base#foo2(a, a)</a> and <a href=\"Base.html#foo3%28a%2Cb%2Cc%29-instance-method\">#foo3</a> and <a href=\"Base.html\">Base</a>))\n      end\n    end\n\n    it \"does not break when referencing lib type (#9928)\" do\n      program = semantic(\"lib LibFoo; BAR = 0; end\", wants_doc: true).program\n      generator = Doc::Generator.new(program, [\"\"])\n\n      # TODO: There should not be a link to LibFoo::Bar in the first place\n      # because LibFoo is undocumented\n      assert_code_link(generator.type(program), \"LibFoo::BAR\", %(<a href=\"LibFoo.html#BAR\">LibFoo::BAR</a>))\n    end\n  end\n\n  describe \"renders code blocks\" do\n    it_renders nil, \"```crystal\\nHello\\nWorld\\n```\", %(<pre><code class=\"language-crystal\"><span class=\"t\">Hello</span>\\n<span class=\"t\">World</span></code></pre>)\n    it_renders nil, \"```cr\\nHello\\nWorld\\n```\", %(<pre><code class=\"language-crystal\"><span class=\"t\">Hello</span>\\n<span class=\"t\">World</span></code></pre>)\n    it_renders nil, \"```\\nHello\\nWorld\\n```\", %(<pre><code class=\"language-crystal\"><span class=\"t\">Hello</span>\\n<span class=\"t\">World</span></code></pre>)\n  end\n\n  describe \"renders code spans\" do\n    it_renders nil, \"`<style>`\", %(<p><code>&lt;style&gt;</code></p>)\n  end\n\n  describe \"renders links\" do\n    it_renders nil, \"[foo](http://example.com/foo)\", %(<p><a href=\"http://example.com/foo\">foo</a></p>)\n\n    program = semantic(\"class Foo; end\", wants_doc: true).program\n    it_renders Doc::Generator.new(program, [\"\"]).type(program), \"[`Foo`](http://example.com/foo)\", %(<p><a href=\"http://example.com/foo\"><code>Foo</code></a></p>)\n\n    it_renders nil, %([filter](https://docs.celestine.dev/Celestine/Meta/Context.html#filter(&block:Celestine::Filter-%3ECelestine::Filter)-instance-method)),\n      %(<p><a href=\"https://docs.celestine.dev/Celestine/Meta/Context.html#filter(&amp;block:Celestine::Filter-%3ECelestine::Filter)-instance-method\">filter</a></p>)\n  end\n\n  describe \"renders headline\" do\n    it_renders nil, \"## Foo Bar\", <<-HTML\n    <h2><a id=\"foo-bar\" class=\"anchor\" href=\"#foo-bar\">  <svg class=\"octicon-link\" aria-hidden=\"true\">\n        <use href=\"#octicon-link\"/>\n      </svg>\n    </a>Foo Bar</h2>\n    HTML\n\n    it_renders nil, \"## Foo Bar\\n### Sub\\n## Bar Baz\\n### Sub\", <<-HTML\n    <h2><a id=\"foo-bar\" class=\"anchor\" href=\"#foo-bar\">  <svg class=\"octicon-link\" aria-hidden=\"true\">\n        <use href=\"#octicon-link\"/>\n      </svg>\n    </a>Foo Bar</h2>\n    <h3><a id=\"sub\" class=\"anchor\" href=\"#sub\">\\n  <svg class=\"octicon-link\" aria-hidden=\"true\">\n        <use href=\"#octicon-link\"/>\n      </svg>\n    </a>Sub</h3>\n    <h2><a id=\"bar-baz\" class=\"anchor\" href=\"#bar-baz\">\\n  <svg class=\"octicon-link\" aria-hidden=\"true\">\n        <use href=\"#octicon-link\"/>\n      </svg>\n    </a>Bar Baz</h2>\n    <h3><a id=\"sub-1\" class=\"anchor\" href=\"#sub-1\">\\n  <svg class=\"octicon-link\" aria-hidden=\"true\">\n        <use href=\"#octicon-link\"/>\n      </svg>\n    </a>Sub</h3>\n    HTML\n  end\n\n  {% if !flag?(:without_libxml2) %}\n    describe \"renders html with sanitization\" do\n      it_renders nil, %(<h1 align=\"center\">Foo</h1>), %(<h1>Foo</h1>)\n      it_renders nil, %(<script>alert(\"hello world\")</script>), %()\n      it_renders nil, %(<p style=\"font-size: 100px\">example text</p></div>), %(<p>example text</p>)\n\n      it_renders nil, %(```crystal\\n# <script>alert(\"hello world\")</script>\\n```),\n        %(<pre><code class=\"language-crystal\"><span class=\"c\"># &lt;script&gt;alert(&quot;hello world&quot;)&lt;/script&gt;</span></code></pre>)\n    end\n\n    describe \"still renders tables despite sanitization\" do\n      table_mkdn = <<-HTML\n        <table>\n          <tr>\n            <th>column 1</th>\n            <th>column 2</th>\n          </tr>\n          <tr>\n            <td>data 1</td>\n            <td>data 2</td>\n          </tr>\n          <tr>\n            <td>data 3</td>\n            <td>data 4</td>\n          </tr>\n        </table>\n      HTML\n\n      it_renders nil, table_mkdn, table_mkdn\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/generator_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate ANNOTATION_COLORS = {\"Deprecated\" => \"red\", \"Experimental\" => \"lime\"}\n\ndescribe Doc::Generator do\n  describe \"#must_include_toplevel?\" do\n    it \"returns false if program has nothing\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      generator.must_include_toplevel?(doc_type).should be_false\n    end\n\n    it \"returns true if program has constant\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      constant = Const.new program, program, \"Foo\", 1.int32\n      constant.add_location Location.new \"foo\", 1, 1\n      program.types[constant.name] = constant\n\n      generator.must_include_toplevel?(doc_type).should be_true\n    end\n\n    it \"returns false if program has constant which is defined in other place\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      constant = Const.new program, program, \"Foo\", 1.int32\n      constant.add_location Location.new \"bar\", 1, 1\n      program.types[constant.name] = constant\n\n      generator.must_include_toplevel?(doc_type).should be_false\n    end\n\n    it \"returns true if program has macro\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\"\n      a_macro.location = Location.new \"foo\", 1, 1\n      program.add_macro a_macro\n\n      generator.must_include_toplevel?(doc_type).should be_true\n    end\n\n    it \"returns false if program has macro which is defined in other place\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\"\n      a_macro.location = Location.new \"bar\", 1, 1\n      program.add_macro a_macro\n\n      generator.must_include_toplevel?(doc_type).should be_false\n    end\n\n    it \"returns true if program has method\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.location = Location.new \"foo\", 1, 1\n      program.add_def a_def\n\n      generator.must_include_toplevel?(doc_type).should be_true\n    end\n\n    it \"returns false if program has method which is defined in other place\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.location = Location.new \"bar\", 1, 1\n      program.add_def a_def\n\n      generator.must_include_toplevel?(doc_type).should be_false\n    end\n  end\n\n  describe \"#collect_constants\" do\n    it \"returns empty array when constants are private\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\"foo\"]\n      doc_type = Doc::Type.new generator, program\n\n      constant = Const.new program, program, \"Foo\", 1.int32\n      constant.private = true\n      constant.add_location Location.new \"foo\", 1, 1\n      program.types[constant.name] = constant\n\n      generator.collect_constants(doc_type).should be_empty\n    end\n  end\n\n  describe \"#formatted_summary\" do\n    ANNOTATION_COLORS.each do |ann, color|\n      describe \"with a #{ann} annotation, and no docs\" do\n        it \"should generate just the #{ann} tag\" do\n          program = Program.new\n          generator = Doc::Generator.new program, [\".\"]\n          doc_type = Doc::Type.new generator, program\n\n          a_def = Def.new \"foo\"\n          a_def.add_annotation(program.types[ann].as(Crystal::AnnotationType), Annotation.new(Crystal::Path.new(ann), [\"lorem ipsum\".string] of ASTNode))\n          doc_method = Doc::Method.new generator, doc_type, a_def, false\n          doc_method.formatted_summary.should eq %(<p><span class=\"flag #{color}\">#{ann.upcase}</span>  lorem ipsum</p>)\n        end\n      end\n\n      describe \"with a #{ann} annotation, and docs\" do\n        it \"should generate both the docs and #{ann} tag\" do\n          program = Program.new\n          generator = Doc::Generator.new program, [\".\"]\n          doc_type = Doc::Type.new generator, program\n\n          a_def = Def.new \"foo\"\n          a_def.doc = \"Some Method\"\n          a_def.add_annotation(program.types[ann].as(Crystal::AnnotationType), Annotation.new(Crystal::Path.new(ann), [\"lorem ipsum\".string] of ASTNode))\n          doc_method = Doc::Method.new generator, doc_type, a_def, false\n          doc_method.formatted_summary.should eq %(<p>Some Method</p>\\n<p><span class=\"flag #{color}\">#{ann.upcase}</span>  lorem ipsum</p>)\n        end\n      end\n    end\n\n    describe \"with no annotation, and no docs\" do\n      it \"should generate nothing\" do\n        program = Program.new\n        generator = Doc::Generator.new program, [\".\"]\n        doc_type = Doc::Type.new generator, program\n\n        a_def = Def.new \"foo\"\n        doc_method = Doc::Method.new generator, doc_type, a_def, false\n        doc_method.formatted_summary.should be_nil\n      end\n    end\n\n    it \"should generate the first sentence\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.doc = \"Some Method.  Longer description\"\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      doc_method.formatted_summary.should eq %(<p>Some Method.</p>)\n    end\n\n    it \"should generate the first line\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.doc = \"Some Method\\n\\nMore Data\"\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      doc_method.formatted_summary.should eq %(<p>Some Method</p>)\n    end\n\n    it \"should exclude whitespace before the summary line\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.doc = \" \\n\\nSome Method\\n\\nMore Data\"\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      doc_method.formatted_summary.should eq %(<p>Some Method</p>)\n    end\n  end\n\n  describe \"#formatted_doc\" do\n    ANNOTATION_COLORS.each do |ann, color|\n      describe \"with a #{ann} annotation, and no docs\" do\n        it \"should generate just the #{ann} tag\" do\n          program = Program.new\n          generator = Doc::Generator.new program, [\".\"]\n          doc_type = Doc::Type.new generator, program\n\n          a_def = Def.new \"foo\"\n          a_def.add_annotation(program.types[ann].as(Crystal::AnnotationType), Annotation.new(Crystal::Path.new(ann), [\"lorem ipsum\".string] of ASTNode))\n          doc_method = Doc::Method.new generator, doc_type, a_def, false\n          doc_method.formatted_doc.should eq %(<p><span class=\"flag #{color}\">#{ann.upcase}</span>  lorem ipsum</p>)\n        end\n      end\n\n      describe \"with a #{ann} annotation, and docs\" do\n        it \"should generate both the docs and #{ann} tag\" do\n          program = Program.new\n          generator = Doc::Generator.new program, [\".\"]\n          doc_type = Doc::Type.new generator, program\n\n          a_def = Def.new \"foo\"\n          a_def.doc = \"Some Method\"\n          a_def.add_annotation(program.types[ann].as(Crystal::AnnotationType), Annotation.new(Crystal::Path.new(ann), [\"lorem ipsum\".string] of ASTNode))\n          doc_method = Doc::Method.new generator, doc_type, a_def, false\n          doc_method.formatted_doc.should eq %(<p>Some Method</p>\\n<p><span class=\"flag #{color}\">#{ann.upcase}</span>  lorem ipsum</p>)\n        end\n      end\n\n      describe \"with alias\" do\n        it \"should generate the #{ann} tag\" do\n          program = Program.new\n          generator = Doc::Generator.new program, [\".\"]\n\n          alias_type = AliasType.new(program, program, \"Foo\", Crystal::Path.new(\"Bar\"))\n          alias_type.add_annotation(program.types[ann].as(Crystal::AnnotationType), Annotation.new(Crystal::Path.new(ann), [\"lorem ipsum\".string] of ASTNode))\n          doc_type = Doc::Type.new generator, alias_type\n          doc_type.formatted_doc.should eq %(<p><span class=\"flag #{color}\">#{ann.upcase}</span>  lorem ipsum</p>)\n        end\n      end\n\n      describe \"with #{ann} annotation in parameter\" do\n        it \"should generate the #{ann} tag\" do\n          program = Program.new\n          generator = Doc::Generator.new program, [\".\"]\n          doc_type = Doc::Type.new generator, program\n\n          a_def = Def.new \"foo\"\n          a_def.doc = \"Some Method\"\n          arg = Arg.new(\"bar\")\n          arg.add_annotation(program.types[ann].as(Crystal::AnnotationType), Annotation.new(Crystal::Path.new(ann), [\"lorem ipsum\".string] of ASTNode))\n          a_def.args << arg\n          doc_method = Doc::Method.new generator, doc_type, a_def, false\n          doc_method.formatted_doc.should eq %(<p>Some Method</p>\\n<p><span class=\"flag #{color}\">#{ann.upcase} parameter <code>bar</code></span>  lorem ipsum</p>)\n        end\n      end\n    end\n\n    describe \"with no annotation, and no docs\" do\n      it \"should generate nothing\" do\n        program = Program.new\n        generator = Doc::Generator.new program, [\".\"]\n        doc_type = Doc::Type.new generator, program\n\n        a_def = Def.new \"foo\"\n        doc_method = Doc::Method.new generator, doc_type, a_def, false\n        doc_method.formatted_doc.should be_nil\n      end\n    end\n\n    it \"should generate the full document\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.doc = \"Some Method.  Longer description\"\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      doc_method.formatted_doc.should eq %(<p>Some Method.  Longer description</p>)\n    end\n\n    it \"should generate the full document\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\"\n      a_def.doc = \"Some Method\\n\\nMore Data\"\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      doc_method.formatted_doc.should eq %(<p>Some Method</p>\\n<p>More Data</p>)\n    end\n  end\n\n  describe \"crystal repo\" do\n    it \"inserts pseudo methods\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n      generator.project_info.name = \"Crystal\"\n\n      pseudo_def = Def.new \"__crystal_pseudo_typeof\"\n      pseudo_def.doc = \"Foo\"\n      doc_method = Doc::Method.new generator, doc_type, pseudo_def, false\n      doc_method.name.should eq \"typeof\"\n      doc_method.doc.not_nil!.should contain %(NOTE: This is a pseudo-method)\n\n      regular_def = Def.new \"pseudo_bar\"\n      regular_def.doc = \"Foo\"\n      doc_method = Doc::Method.new generator, doc_type, regular_def, false\n      doc_method.name.should eq \"pseudo_bar\"\n      doc_method.doc.not_nil!.should_not contain %(NOTE: This is a pseudo-method)\n    end\n  end\n\n  it \"generates sitemap\" do\n    program = Program.new\n    generator = Doc::Generator.new program, [\".\"]\n    doc_type = Doc::Type.new generator, program\n\n    Doc::SitemapTemplate.new([doc_type], \"http://example.com/api/1.0\", \"0.8\", \"monthly\").to_s.should eq <<-XML\n      <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n      <urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n        <url>\n          <loc>http://example.com/api/1.0/toplevel.html</loc>\n          <priority>0.8</priority>\n          <changefreq>monthly</changefreq>\n        </url>\n      </urlset>\n\n      XML\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/macro_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def assert_args_to_s(item, to_s_output, to_html_output = to_s_output, *, file = __FILE__, line = __LINE__)\n  item.args_to_s.should eq(to_s_output), file: file, line: line\n  item.args_to_html.should eq(to_html_output), file: file, line: line\nend\n\ndescribe Doc::Macro do\n  describe \"args_to_s\" do\n    it \"shows simple args\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg, \"bar\".arg]\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(foo, bar)\")\n    end\n\n    it \"shows splat arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg], splat_index: 0\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(*foo)\")\n    end\n\n    it \"shows simple arg and splat arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg, \"bar\".arg], splat_index: 1\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(foo, *bar)\")\n    end\n\n    it \"shows double splat arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", double_splat: \"foo\".arg\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(**foo)\")\n    end\n\n    it \"shows double splat arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", double_splat: \"foo\".arg\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(**foo)\")\n    end\n\n    it \"shows simple arg and double splat arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg], double_splat: \"bar\".arg\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(foo, **bar)\")\n    end\n\n    it \"shows block arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", block_arg: \"foo\".arg\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(&foo)\")\n    end\n\n    it \"shows simple arg and block arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg], block_arg: \"bar\".arg\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(foo, &bar)\")\n    end\n\n    it \"shows external name of arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg(external_name: \"bar\")]\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, \"(bar foo)\")\n    end\n\n    it \"shows external name of arg with quotes and escaping\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg(external_name: \"<<-< uouo fish life\")]\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro,\n        %((\"<<-< uouo fish life\" foo)),\n        \"(&quot;&lt;&lt;-&lt; uouo fish life&quot; foo)\")\n    end\n\n    it \"shows default value with highlighting\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_macro = Macro.new \"foo\", [\"foo\".arg(default_value: 1.int32)]\n      doc_macro = Doc::Macro.new generator, doc_type, a_macro\n      assert_args_to_s(doc_macro, %((foo = 1)), %((foo = <span class=\"n\">1</span>)))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/method_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def assert_args_to_s(item, to_s_output, to_html_output = to_s_output, *, file = __FILE__, line = __LINE__)\n  item.args_to_s.should eq(to_s_output), file: file, line: line\n  item.args_to_html.should eq(to_html_output), file: file, line: line\nend\n\ndescribe Doc::Method do\n  describe \"args_to_s\" do\n    it \"shows simple args\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg, \"bar\".arg]\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(foo, bar)\")\n    end\n\n    it \"shows splat args\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg], splat_index: 0\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(*foo)\")\n    end\n\n    it \"shows underscore restriction\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg(restriction: Crystal::Underscore.new)], splat_index: 0\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(*foo : _)\")\n    end\n\n    it \"shows double splat args\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", double_splat: \"foo\".arg\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(**foo)\")\n    end\n\n    it \"shows block args\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", block_arg: \"foo\".arg\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(&foo)\")\n    end\n\n    it \"shows block args with underscore\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", block_arg: \"foo\".arg(restriction: Crystal::ProcNotation.new(([Crystal::Underscore.new] of Crystal::ASTNode), Crystal::Underscore.new))\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(&foo : _ -> _)\")\n    end\n\n    it \"shows block args if a def has `yield`\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", block_arity: 1\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(&)\")\n    end\n\n    it \"shows return type restriction\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", return_type: \"Foo\".path\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \" : Foo\")\n    end\n\n    it \"shows args and return type restriction\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg], return_type: \"Foo\".path\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(foo) : Foo\")\n    end\n\n    it \"shows external name of arg\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg(external_name: \"bar\")]\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, \"(bar foo)\")\n    end\n\n    it \"shows external name of arg with quotes and escaping\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg(external_name: \"<<-< uouo fish life\")]\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method,\n        %((\"<<-< uouo fish life\" foo)),\n        \"(&quot;&lt;&lt;-&lt; uouo fish life&quot; foo)\")\n    end\n\n    it \"shows typeof restriction of arg with highlighting\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg(restriction: TypeOf.new([1.int32] of ASTNode))]\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method,\n        %((foo : typeof(1))),\n        %((foo : <span class=\"k\">typeof</span>(<span class=\"n\">1</span>))))\n    end\n\n    it \"shows default value of arg with highlighting\" do\n      program = Program.new\n      generator = Doc::Generator.new program, [\".\"]\n      doc_type = Doc::Type.new generator, program\n\n      a_def = Def.new \"foo\", [\"foo\".arg(default_value: 1.int32)]\n      doc_method = Doc::Method.new generator, doc_type, a_def, false\n      assert_args_to_s(doc_method, %((foo = 1)), %((foo = <span class=\"n\">1</span>)))\n    end\n  end\n\n  describe \"doc\" do\n    it \"gets doc from underlying method\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Some docs\")\n      method.doc_copied_from.should be_nil\n    end\n\n    it \"trailing comment is not a doc comment\" do\n      program = semantic(<<-CRYSTAL, inject_primitives: false, wants_doc: true).program\n        nil # trailing comment\n        def foo\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program).lookup_class_method(\"foo\").not_nil!\n      method.doc.should be_nil\n    end\n\n    it \"trailing comment is not part of a doc comment\" do\n      program = semantic(<<-CRYSTAL, inject_primitives: false, wants_doc: true).program\n        nil # trailing comment\n        # doc comment\n        def foo\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program).lookup_class_method(\"foo\").not_nil!\n      method.doc.should eq(\"doc comment\")\n    end\n\n    it \"inherits doc from ancestor (no extra comment)\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n        end\n\n        class Bar < Foo\n          def foo\n            super\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Bar\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Some docs\")\n      method.doc_copied_from.should eq(generator.type(program.types[\"Foo\"]))\n    end\n\n    it \"inherits doc from previous def (no extra comment)\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n\n          def foo\n            previous_def\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Foo\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Some docs\")\n      method.doc_copied_from.should be_nil\n    end\n\n    it \"inherits doc from ancestor's previous def (no extra comment)\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n\n          def foo\n            previous_def\n          end\n        end\n\n        class Bar < Foo\n          def foo\n            super\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Bar\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Some docs\")\n      method.doc_copied_from.should eq(generator.type(program.types[\"Foo\"]))\n    end\n\n    it \"inherits doc from ancestor (use :inherit:)\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n        end\n\n        class Bar < Foo\n          # :inherit:\n          def foo\n            super\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Bar\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Some docs\")\n      method.doc_copied_from.should be_nil\n    end\n\n    it \"inherits doc from ancestor (use :inherit: plus more content)\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n        end\n\n        class Bar < Foo\n          # Before\n          #\n          # :inherit:\n          #\n          # After\n          def foo\n            super\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Bar\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Before\\n\\nSome docs\\n\\nAfter\")\n      method.doc_copied_from.should be_nil\n    end\n\n    it \"inherits doc from ancestor through chained :inherit:\" do\n      program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n        class Foo\n          # Some docs\n          def foo\n          end\n        end\n\n        class Bar < Foo\n          # :inherit:\n          def foo\n            super\n          end\n        end\n\n        class Baz < Bar\n          # :inherit:\n          def foo\n            super\n          end\n        end\n        CRYSTAL\n      generator = Doc::Generator.new program, [\"\"]\n      method = generator.type(program.types[\"Baz\"]).lookup_method(\"foo\").not_nil!\n      method.doc.should eq(\"Some docs\")\n      method.doc_copied_from.should be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/project_info_spec.cr",
    "content": "require \"../../../../spec_helper\"\nrequire \"../../../../support/tempfile\"\n\nprivate alias ProjectInfo = Crystal::Doc::ProjectInfo\n\nprivate def run_git(command)\n  Process.run(%(git -c user.email=\"\" -c user.name=\"spec\" #{command}), shell: true)\nrescue IO::Error\n  pending! \"Git is not available\"\nend\n\nprivate def assert_with_defaults(initial, expected, *, file = __FILE__, line = __LINE__)\n  initial.fill_with_defaults\n  initial.should eq(expected), file: file, line: line\nend\n\ndescribe Crystal::Doc::ProjectInfo do\n  around_each do |example|\n    with_tempdir(\"docs-project\") do\n      example.run\n    end\n  end\n\n  describe \"#fill_with_defaults\" do\n    it \"empty folder\" do\n      assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(nil, nil, refname: nil))\n      assert_with_defaults(ProjectInfo.new(\"foo\", \"1.0\"), ProjectInfo.new(\"foo\", \"1.0\", refname: nil))\n    end\n\n    context \"with shard.yml\" do\n      before_each do\n        File.write(\"shard.yml\", \"name: foo\\nversion: 1.0\")\n      end\n\n      it \"git missing\" do\n        Crystal::Git.executable = \"git-missing-executable\"\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"1.0\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(nil, \"2.0\"), ProjectInfo.new(\"foo\", \"2.0\", refname: nil))\n      ensure\n        Crystal::Git.executable = \"git\"\n      end\n\n      it \"not in a git folder\" do\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"1.0\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(nil, \"2.0\"), ProjectInfo.new(\"foo\", \"2.0\", refname: nil))\n      end\n\n      it \"git but no commit\" do\n        run_git \"init\"\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"1.0\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(nil, \"2.0\"), ProjectInfo.new(\"foo\", \"2.0\", refname: nil))\n      end\n\n      it \"git tagged version\" do\n        run_git \"init\"\n        run_git \"add shard.yml\"\n        run_git \"commit -m \\\"Initial commit\\\" --no-gpg-sign\"\n        run_git \"tag v3.0\"\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"3.0\", refname: \"v3.0\"))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: \"v3.0\"))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\", refname: \"12345\"), ProjectInfo.new(\"bar\", \"2.0\", refname: \"12345\"))\n      end\n\n      it \"git tagged version dirty\" do\n        run_git \"init\"\n        run_git \"add shard.yml\"\n        run_git \"commit -m \\\"Initial commit\\\" --no-gpg-sign\"\n        run_git \"tag v3.0\"\n        File.write(\"shard.yml\", \"\\n\", mode: \"a\")\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"3.0-dev\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(nil, \"1.1\"), ProjectInfo.new(\"foo\", \"1.1\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: nil))\n      end\n\n      it \"git untracked file doesn't prevent detection\" do\n        run_git \"init\"\n        run_git \"add shard.yml\"\n        run_git \"commit -m \\\"Initial commit\\\" --no-gpg-sign\"\n        run_git \"tag v3.0\"\n        File.write(\"foo.txt\", \"bar\")\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"3.0\", refname: \"v3.0\"))\n      end\n\n      it \"git non-tagged commit\" do\n        run_git \"init\"\n        run_git \"checkout -B master\"\n        run_git \"add shard.yml\"\n        run_git \"commit -m \\\"Initial commit\\\" --no-gpg-sign\"\n        commit_sha = `git rev-parse HEAD`.chomp\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"master\", refname: commit_sha))\n        assert_with_defaults(ProjectInfo.new(nil, \"1.1\"), ProjectInfo.new(\"foo\", \"1.1\", refname: commit_sha))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: commit_sha))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\", refname: \"12345\"), ProjectInfo.new(\"bar\", \"2.0\", refname: \"12345\"))\n      end\n\n      it \"git non-tagged commit dirty\" do\n        run_git \"init\"\n        run_git \"checkout -B master\"\n        run_git \"add shard.yml\"\n        run_git \"commit -m \\\"Initial commit\\\" --no-gpg-sign\"\n        File.write(\"shard.yml\", \"\\n\", mode: \"a\")\n\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"master-dev\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(nil, \"1.1\"), ProjectInfo.new(\"foo\", \"1.1\", refname: nil))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: nil))\n      end\n\n      it \"git with remote\" do\n        run_git \"init\"\n        run_git \"remote add origin git@github.com:foo/bar\"\n\n        url_pattern = \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n        assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(\"foo\", \"1.0\", refname: nil, source_url_pattern: url_pattern))\n        assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: nil, source_url_pattern: url_pattern))\n        assert_with_defaults(ProjectInfo.new(nil, \"2.0\"), ProjectInfo.new(\"foo\", \"2.0\", refname: nil, source_url_pattern: url_pattern))\n        assert_with_defaults(ProjectInfo.new(nil, \"2.0\", source_url_pattern: \"foo_bar\"), ProjectInfo.new(\"foo\", \"2.0\", refname: nil, source_url_pattern: \"foo_bar\"))\n      end\n    end\n\n    it \"no shard.yml, but git tagged version\" do\n      File.write(\"foo.txt\", \"bar\")\n      run_git \"init\"\n      run_git \"add foo.txt\"\n      run_git \"commit -m \\\"Remove shard.yml\\\" --no-gpg-sign\"\n      run_git \"tag v4.0\"\n\n      assert_with_defaults(ProjectInfo.new(nil, nil), ProjectInfo.new(nil, \"4.0\", refname: \"v4.0\"))\n      assert_with_defaults(ProjectInfo.new(\"foo\", nil), ProjectInfo.new(\"foo\", \"4.0\", refname: \"v4.0\"))\n      assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\"), ProjectInfo.new(\"bar\", \"2.0\", refname: \"v4.0\"))\n      assert_with_defaults(ProjectInfo.new(\"bar\", \"2.0\", refname: \"12345\"), ProjectInfo.new(\"bar\", \"2.0\", refname: \"12345\"))\n    end\n  end\n\n  it \".find_git_version\" do\n    # Non-git directory\n    ProjectInfo.find_git_version.should be_nil\n\n    # Empty git directory\n    run_git \"init\"\n    run_git \"checkout -B master\"\n    ProjectInfo.find_git_version.should be_nil\n\n    # Non-tagged commit\n    File.write(\"file.txt\", \"foo\")\n    run_git \"add file.txt\"\n    run_git \"commit -m \\\"Initial commit\\\" --no-gpg-sign\"\n    ProjectInfo.find_git_version.should eq \"master\"\n\n    # Other branch\n    run_git \"checkout -b foo\"\n    ProjectInfo.find_git_version.should eq \"foo\"\n\n    # Non-tagged commit, dirty workdir\n    run_git \"checkout master\"\n    File.write(\"file.txt\", \"bar\")\n    ProjectInfo.find_git_version.should eq \"master-dev\"\n\n    run_git \"checkout -- .\"\n\n    # Tagged commit\n    run_git \"tag v0.1.0\"\n    ProjectInfo.find_git_version.should eq \"0.1.0\"\n\n    # Tagged commit, dirty workdir\n    File.write(\"file.txt\", \"bar\")\n    ProjectInfo.find_git_version.should eq \"0.1.0-dev\"\n\n    # Tagged commit, dirty index\n    run_git \"add file.txt\"\n    ProjectInfo.find_git_version.should eq \"0.1.0-dev\"\n\n    run_git \"reset --hard v0.1.0\"\n    ProjectInfo.find_git_version.should eq \"0.1.0\"\n\n    # Multiple tags\n    run_git \"tag v0.2.0\"\n    ProjectInfo.find_git_version.should eq \"0.1.0\"\n  end\n\n  describe \".git_remote\" do\n    it \"no git workdir\" do\n      ProjectInfo.git_remote.should be_nil\n    end\n\n    it \"no remote\" do\n      run_git \"init\"\n      ProjectInfo.git_remote.should be_nil\n    end\n\n    it \"simple origin\" do\n      run_git \"init\"\n      run_git \"remote add origin https://example.com/foo.git\"\n      ProjectInfo.git_remote.should eq \"https://example.com/foo.git\"\n    end\n\n    it \"origin plus other\" do\n      run_git \"init\"\n      run_git \"remote add bar https://example.com/bar.git\"\n      run_git \"remote add origin https://example.com/foo.git\"\n      run_git \"remote add baz https://example.com/baz.git\"\n      `git remote -v`\n      ProjectInfo.git_remote.should eq \"https://example.com/foo.git\"\n    end\n\n    it \"no origin remote\" do\n      run_git \"init\"\n      run_git \"remote add bar https://example.com/bar.git\"\n      run_git \"remote add baz https://example.com/baz.git\"\n      `git remote -v`\n      ProjectInfo.git_remote.should eq \"https://example.com/bar.git\"\n    end\n  end\n\n  describe \".read_shard_properties\" do\n    it \"no shard.yml\" do\n      ProjectInfo.read_shard_properties.should eq({nil, nil})\n    end\n\n    it \"without name and version properties\" do\n      File.write(\"shard.yml\", \"foo: bar\\n\")\n      ProjectInfo.read_shard_properties.should eq({nil, nil})\n    end\n\n    it \"empty properties\" do\n      File.write(\"shard.yml\", \"name: \\nversion: \")\n      ProjectInfo.read_shard_properties.should eq({nil, nil})\n    end\n\n    it \"indented properties\" do\n      File.write(\"shard.yml\", \"  name: bar\\n  version: 1.0\")\n      ProjectInfo.read_shard_properties.should eq({nil, nil})\n    end\n\n    it \"only name\" do\n      File.write(\"shard.yml\", \"name: bar\\n\")\n      ProjectInfo.read_shard_properties.should eq({\"bar\", nil})\n    end\n\n    it \"name and version\" do\n      File.write(\"shard.yml\", \"name: bar\\nversion: 1.0\")\n      ProjectInfo.read_shard_properties.should eq({\"bar\", \"1.0\"})\n    end\n\n    it \"duplicate properties uses first one\" do\n      File.write(\"shard.yml\", \"name: bar\\nversion: 1.0\\nname: foo\\nversion: foo\")\n      ProjectInfo.read_shard_properties.should eq({\"bar\", \"1.0\"})\n    end\n\n    it \"strip whitespace\" do\n      File.write(\"shard.yml\", \"name: bar  \\nversion: 1.0  \")\n      ProjectInfo.read_shard_properties.should eq({\"bar\", \"1.0\"})\n    end\n\n    it \"strip quotes\" do\n      File.write(\"shard.yml\", \"name: 'bar'\\nversion: '1.0'\")\n      ProjectInfo.read_shard_properties.should eq({\"bar\", \"1.0\"})\n    end\n\n    it \"ignores comments\" do\n      File.write(\"shard.yml\", \"name: bar # comment\\nversion: 1.0 # comment\")\n      ProjectInfo.read_shard_properties.should eq({\"bar\", \"1.0\"})\n\n      File.write(\"shard.yml\", \"name: # comment\\nversion: # comment\")\n      ProjectInfo.read_shard_properties.should eq({nil, nil})\n    end\n  end\n\n  it \".find_source_url_pattern\" do\n    ProjectInfo.find_source_url_pattern(\"no a uri\").should be_nil\n    ProjectInfo.find_source_url_pattern(\"git@example.com:foo/bar\").should be_nil\n    ProjectInfo.find_source_url_pattern(\"http://example.com/foo/bar\").should be_nil\n\n    ProjectInfo.find_source_url_pattern(\"git@github.com:foo/bar/\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"git@github.com:foo/bar.git\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n\n    ProjectInfo.find_source_url_pattern(\"git@github.com:foo/bar\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://github.com/foo/bar\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"https://github.com/foo/bar\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://www.github.com/foo/bar\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"https://www.github.com/foo/bar\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n\n    ProjectInfo.find_source_url_pattern(\"https://github.com/foo/bar.git\").should eq \"https://github.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"https://github.com/foo/bar.cr\").should eq \"https://github.com/foo/bar.cr/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"https://github.com/foo/bar.cr.git\").should eq \"https://github.com/foo/bar.cr/blob/%{refname}/%{path}#L%{line}\"\n\n    ProjectInfo.find_source_url_pattern(\"git@gitlab.com:foo/bar\").should eq \"https://gitlab.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://gitlab.com/foo/bar\").should eq \"https://gitlab.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://gitlab.com/foo/bar.git\").should eq \"https://gitlab.com/foo/bar/blob/%{refname}/%{path}#L%{line}\"\n\n    ProjectInfo.find_source_url_pattern(\"git@bitbucket.com:foo/bar\").should eq \"https://bitbucket.com/foo/bar/src/%{refname}/%{path}#%{filename}-%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://bitbucket.com/foo/bar\").should eq \"https://bitbucket.com/foo/bar/src/%{refname}/%{path}#%{filename}-%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://bitbucket.com/foo/bar.git\").should eq \"https://bitbucket.com/foo/bar/src/%{refname}/%{path}#%{filename}-%{line}\"\n\n    ProjectInfo.find_source_url_pattern(\"git@git.sr.ht:~foo/bar\").should eq \"https://git.sr.ht/~foo/bar/tree/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://git.sr.ht/~foo/bar\").should eq \"https://git.sr.ht/~foo/bar/tree/%{refname}/%{path}#L%{line}\"\n    ProjectInfo.find_source_url_pattern(\"http://git.sr.ht/~foo/bar.git\").should eq \"https://git.sr.ht/~foo/bar.git/tree/%{refname}/%{path}#L%{line}\"\n  end\n\n  describe \"#source_url\" do\n    it \"fails if refname is missing\" do\n      location = Crystal::Doc::RelativeLocation.new(\"foo/bar.baz\", 42)\n      info = ProjectInfo.new(\"test\", \"v1.0\", refname: nil, source_url_pattern: \"http://git.example.com/test.git/src/%{refname}/%{path}#L%{line}\")\n      info.source_url(location).should be_nil\n    end\n\n    it \"fails if pattern is missing\" do\n      location = Crystal::Doc::RelativeLocation.new(\"foo/bar.baz\", 42)\n      info = ProjectInfo.new(\"test\", \"v1.0\", refname: \"master\")\n      info.source_url(location).should be_nil\n    end\n\n    it \"builds url\" do\n      info = ProjectInfo.new(\"test\", \"v1.0\", refname: \"master\", source_url_pattern: \"http://git.example.com/test.git/src/%{refname}/%{path}#L%{line}\")\n      location = Crystal::Doc::RelativeLocation.new(\"foo/bar.baz\", 42)\n      info.source_url(location).should eq \"http://git.example.com/test.git/src/master/foo/bar.baz#L42\"\n    end\n\n    it \"returns nil for empty pattern\" do\n      info = ProjectInfo.new(\"test\", \"v1.0\", refname: \"master\", source_url_pattern: \"\")\n      location = Crystal::Doc::RelativeLocation.new(\"foo/bar.baz\", 42)\n      info.source_url(location).should be_nil\n    end\n\n    it \"fails if pattern is missing\" do\n      location = Crystal::Doc::RelativeLocation.new(\"foo/bar.baz\", 42)\n      info = ProjectInfo.new(\"test\", \"v1.0\")\n      info.refname = \"master\"\n      info.source_url(location).should be_nil\n    end\n\n    it \"builds url\" do\n      info = ProjectInfo.new(\"test\", \"v1.0\")\n      info.refname = \"master\"\n      info.source_url_pattern = \"http://git.example.com/test.git/src/%{refname}/%{path}#L%{line}\"\n      location = Crystal::Doc::RelativeLocation.new(\"foo/bar.baz\", 42)\n      info.source_url(location).should eq \"http://git.example.com/test.git/src/master/foo/bar.baz#L42\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc/type_spec.cr",
    "content": "require \"../../../spec_helper\"\n\ndescribe Doc::Type do\n  it \"doesn't show types for alias type\" do\n    program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n      class Foo\n        class Bar\n        end\n      end\n\n      alias Alias = Foo\n\n      Alias\n      CRYSTAL\n\n    # Set locations to types relative to the included dir\n    # so they are included by the doc generator\n    foo_bar_type = program.types[\"Foo\"].types[\"Bar\"]\n    foo_bar_type.add_location(Location.new(\"./foo.cr\", 1, 1))\n\n    alias_type = program.types[\"Alias\"]\n    alias_type.add_location(Location.new(\"./foo.cr\", 1, 1))\n\n    generator = Doc::Generator.new program, [\".\"]\n\n    doc_alias_type = generator.type(alias_type)\n    doc_alias_type.types.size.should eq(0)\n  end\n\n  it \"finds construct when searching class method (#8095)\" do\n    program = top_level_semantic(<<-CRYSTAL, wants_doc: true).program\n      class Foo\n        def initialize(x)\n        end\n      end\n      CRYSTAL\n\n    generator = Doc::Generator.new program, [\"\"]\n    foo = generator.type(program.types[\"Foo\"])\n    foo.lookup_class_method(\"new\").should_not be_nil\n    foo.lookup_class_method(\"new\", 1).should_not be_nil\n  end\n\n  describe \"#node_to_html\" do\n    it \"shows relative path\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          class Bar\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      foo = generator.type(program.types[\"Foo\"])\n      foo.node_to_html(\"Bar\".path).should eq(%(<a href=\"Foo/Bar.html\">Bar</a>))\n    end\n\n    it \"shows relative generic\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          class Bar(T)\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      foo = generator.type(program.types[\"Foo\"])\n      foo.node_to_html(Generic.new(\"Bar\".path, [\"Foo\".path] of ASTNode)).should eq(%(<a href=\"Foo/Bar.html\">Bar</a>(<a href=\"Foo.html\">Foo</a>)))\n    end\n\n    it \"shows generic path with necessary colons\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          class Foo\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      foo = generator.type(program.types[\"Foo\"])\n      foo.node_to_html(\"Foo\".path(global: true)).should eq(%(<a href=\"Foo.html\">::Foo</a>))\n    end\n\n    it \"shows generic path with unnecessary colons\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          class Bar\n          end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      foo = generator.type(program.types[\"Foo\"])\n      foo.node_to_html(\"Foo\".path(global: true)).should eq(%(<a href=\"Foo.html\">Foo</a>))\n    end\n\n    it \"shows tuples\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n        end\n\n        class Bar\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      foo = generator.type(program.types[\"Foo\"])\n      node = Generic.new(\"Tuple\".path(global: true), [\"Foo\".path, \"Bar\".path] of ASTNode)\n      foo.node_to_html(node).should eq(%(Tuple(<a href=\"Foo.html\">Foo</a>, <a href=\"Bar.html\">Bar</a>)))\n    end\n\n    it \"shows named tuples\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n        end\n\n        class Bar\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      foo = generator.type(program.types[\"Foo\"])\n      node = Generic.new(\"NamedTuple\".path(global: true), [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"Foo\".path), NamedArgument.new(\"y\", \"Bar\".path)])\n      foo.node_to_html(node).should eq(%(NamedTuple(x: <a href=\"Foo.html\">Foo</a>, y: <a href=\"Bar.html\">Bar</a>)))\n    end\n  end\n\n  it \"ASTNode has no superclass\" do\n    program = semantic(<<-CRYSTAL).program\n      module Crystal\n        module Macros\n          class ASTNode\n          end\n          class Arg < ASTNode\n          end\n        end\n      end\n      CRYSTAL\n\n    generator = Doc::Generator.new program, [\"\"]\n    macros_module = program.types[\"Crystal\"].types[\"Macros\"]\n    astnode = generator.type(macros_module.types[\"ASTNode\"])\n    astnode.superclass.should be_nil\n    # Sanity check: subclasses of ASTNode has the right superclass\n    generator.type(macros_module.types[\"Arg\"]).superclass.should eq(astnode)\n  end\n\n  it \"ASTNode has no ancestors\" do\n    program = semantic(<<-CRYSTAL).program\n      module Crystal\n        module Macros\n          class ASTNode\n          end\n          class Arg < ASTNode\n          end\n        end\n      end\n      CRYSTAL\n\n    generator = Doc::Generator.new program, [\"\"]\n    macros_module = program.types[\"Crystal\"].types[\"Macros\"]\n    astnode = generator.type(macros_module.types[\"ASTNode\"])\n    astnode.ancestors.should be_empty\n    # Sanity check: subclasses of ASTNode has the right ancestors\n    generator.type(macros_module.types[\"Arg\"]).ancestors.should eq([astnode])\n  end\n\n  describe \"#instance_methods\" do\n    it \"sorts operators first\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          def foo; end\n          def ~; end\n          def +; end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      type = generator.type(program.types[\"Foo\"])\n      type.instance_methods.map(&.name).should eq [\"+\", \"~\", \"foo\"]\n    end\n  end\n\n  describe \"#class_methods\" do\n    it \"sorts operators first\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          def self.foo; end\n          def self.~; end\n          def self.+; end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      type = generator.type(program.types[\"Foo\"])\n      type.class_methods.map(&.name).should eq [\"+\", \"~\", \"foo\"]\n    end\n  end\n\n  describe \"#macros\" do\n    it \"sorts operators first\" do\n      program = semantic(<<-CRYSTAL).program\n        class Foo\n          macro foo; end\n          macro ~; end\n          macro +; end\n        end\n        CRYSTAL\n\n      generator = Doc::Generator.new program, [\"\"]\n      type = generator.type(program.types[\"Foo\"])\n      type.macros.map(&.name).should eq [\"+\", \"~\", \"foo\"]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/doc_spec.cr",
    "content": "require \"../../../spec_helper\"\n\ndescribe Crystal::Doc::Generator do\n  describe \".anchor_link\" do\n    it \"generates the correct anchor link\" do\n      Crystal::Doc.anchor_link(\"anchor\").should eq(\n        <<-HTML\n        <a id=\"anchor\" class=\"anchor\" href=\"#anchor\">\n          <svg class=\"octicon-link\" aria-hidden=\"true\">\n            <use href=\"#octicon-link\"/>\n          </svg>\n        </a>\n        HTML\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/expand_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def processed_expand_visitor(code, cursor_location)\n  compiler = Compiler.new\n  compiler.no_codegen = true\n  compiler.no_cleanup = true\n  compiler.wants_doc = true\n  compiler.prelude = \"empty\"\n  result = compiler.compile(Compiler::Source.new(\".\", code), \"fake-no-build\")\n\n  visitor = ExpandVisitor.new(cursor_location)\n  process_result = visitor.process(result)\n\n  {visitor, process_result}\nend\n\nprivate def run_expand_tool(code, &)\n  cursor_location = nil\n\n  code.lines.each_with_index do |line, line_number_0|\n    if column_number = line.index('‸')\n      cursor_location = Location.new(\".\", line_number_0 + 1, column_number + 1)\n    end\n  end\n\n  code = code.delete('‸')\n\n  if cursor_location\n    _, result = processed_expand_visitor(code, cursor_location)\n\n    yield result\n  else\n    raise \"no cursor found in spec\"\n  end\nend\n\nprivate def expansion_to_a(expansion)\n  [expansion.original_source].concat(expansion.expanded_sources)\nend\n\nprivate def assert_expand(code, expected_result)\n  assert_expand(code, expected_result) { }\nend\n\nprivate def assert_expand(code, expected_result, &)\n  run_expand_tool code do |result|\n    result.status.should eq(\"ok\")\n    result.message.should eq(\"#{expected_result.size} expansion#{expected_result.size >= 2 ? \"s\" : \"\"} found\")\n    result.expansions.not_nil!.zip(expected_result) do |expansion, expected_result|\n      expansion_to_a(expansion).zip(expected_result) do |result, expected|\n        result.should eq(expected)\n      end\n    end\n\n    yield result\n  end\nend\n\nprivate def assert_expand_simple(code, expanded, original = code.delete('‸'))\n  assert_expand_simple(code, expanded, original) { }\nend\n\nprivate def assert_expand_simple(code, expanded, original = code.delete('‸'), &)\n  assert_expand(code, [[original, expanded]]) { |result| yield result.expansions.not_nil![0] }\nend\n\nprivate def assert_expand_fail(code, message = \"no expansion found\")\n  run_expand_tool code do |result|\n    result.status.should eq(\"failed\")\n    result.message.should eq(message)\n  end\nend\n\ndescribe \"expand\" do\n  it \"expands macro expression {{ ... }}\" do\n    code = \"‸{{ 1 + 2 }}\"\n\n    assert_expand_simple code, \"3\"\n  end\n\n  it \"expands macro expression {{ ... }} with cursor inside it\" do\n    code = \"{{ 1 ‸+ 2 }}\"\n\n    assert_expand_simple code, \"3\"\n  end\n\n  it \"expands macro expression {{ ... }} with cursor end of it\" do\n    code = \"{{ 1 + 2 }‸}\"\n\n    assert_expand_simple code, \"3\"\n  end\n\n  it \"expands macro expression {% ... %}\" do\n    code = %(‸{% \"test\" %})\n\n    assert_expand_simple code, \"\"\n  end\n\n  it \"expands macro expression {% ... %} with cursor at end of it\" do\n    code = %({% \"test\" ‸%})\n\n    assert_expand_simple code, \"\"\n  end\n\n  it \"expands macro control {% if %}\" do\n    code = <<-CRYSTAL\n    {%‸ if 1 == 1 %}\n      true\n    {% end %}\n    CRYSTAL\n\n    assert_expand_simple code, \"true\"\n  end\n\n  it \"expands macro control {% if %} with cursor inside it\" do\n    code = <<-CRYSTAL\n    {% if 1 == 1 %}\n      tr‸ue\n    {% end %}\n    CRYSTAL\n\n    assert_expand_simple code, \"true\"\n  end\n\n  it \"expands macro control {% if %} with cursor at end of it\" do\n    code = <<-CRYSTAL\n    {% if 1 == 1 %}\n      true\n    {% end ‸%}\n    CRYSTAL\n\n    assert_expand_simple code, \"true\"\n  end\n\n  it \"expands macro control {% if %} with indent\" do\n    code = <<-CRYSTAL\n    begin\n      {% if 1 == 1 %}\n        t‸rue\n      {% end %}\n    end\n    CRYSTAL\n\n    original = <<-CRYSTAL\n    {% if 1 == 1 %}\n      true\n    {% end %}\n    CRYSTAL\n\n    assert_expand_simple code, original: original, expanded: \"true\"\n  end\n\n  it \"expands macro control {% for %}\" do\n    code = <<-CRYSTAL\n    {% f‸or x in 1..3 %}\n      {{ x }}\n    {% end %}\n    CRYSTAL\n\n    assert_expand_simple code, \"1\\n\\n2\\n\\n3\\n\"\n  end\n\n  it \"expands macro control {% for %} with cursor inside it\" do\n    code = <<-CRYSTAL\n    {% for x in 1..3 %}\n     ‸ {{ x }}\n    {% end %}\n    CRYSTAL\n\n    assert_expand_simple code, \"1\\n\\n2\\n\\n3\\n\"\n  end\n\n  it \"expands macro control {% for %} with cursor at end of it\" do\n    code = <<-CRYSTAL\n    {% for x in 1..3 %}\n      {{ x }}\n    ‸{% end %}\n    CRYSTAL\n\n    assert_expand_simple code, \"1\\n\\n2\\n\\n3\\n\"\n  end\n\n  it \"expands macro control {% for %} with indent\" do\n    code = <<-CRYSTAL\n    begin\n      {% f‸or x in 1..3 %}\n        {{ x }}\n      {% end %}\n    end\n    CRYSTAL\n\n    original = <<-CRYSTAL\n    {% for x in 1..3 %}\n      {{ x }}\n    {% end %}\n    CRYSTAL\n\n    assert_expand_simple code, original: original, expanded: \"1\\n\\n2\\n\\n3\\n\"\n  end\n\n  it \"expands simple macro\" do\n    code = <<-CRYSTAL\n    macro foo\n      1\n    end\n\n    ‸foo\n    CRYSTAL\n\n    assert_expand_simple code, original: \"foo\", expanded: \"1\" do |expansion|\n      expansion.expanded_macros.size.should eq(1)\n      macros = expansion.expanded_macros[0]\n      macros.size.should eq(1)\n\n      a_macro = macros[0]\n      a_macro[:name].should eq(\"foo\")\n      a_macro[:implementation].filename.should eq(\".\")\n      a_macro[:implementation].line.should eq(1)\n      a_macro[:implementation].column.should eq(1)\n    end\n  end\n\n  it \"expands simple macro with cursor inside it\" do\n    code = <<-CRYSTAL\n    macro foo\n      1\n    end\n\n    f‸oo\n    CRYSTAL\n\n    assert_expand_simple code, original: \"foo\", expanded: \"1\"\n  end\n\n  it \"expands simple macro with cursor at end of it\" do\n    code = <<-CRYSTAL\n    macro foo\n      1\n    end\n\n    fo‸o\n    CRYSTAL\n\n    assert_expand_simple code, original: \"foo\", expanded: \"1\"\n  end\n\n  it \"expands complex macro\" do\n    code = <<-CRYSTAL\n    macro foo\n      {% if true %}\n        \"if true\"\n      {% end %}\n      {% for x in %w(1 2 3) %}\n        {{ x }}\n      {% end %}\n    end\n\n    ‸foo\n    CRYSTAL\n\n    assert_expand_simple code, original: \"foo\", expanded: %(\"if true\"\\n\\n\\n\"1\"\\n\\n\"2\"\\n\\n\"3\"\\n)\n  end\n\n  it \"expands macros with 2 level\" do\n    code = <<-CRYSTAL\n    macro foo\n      :foo\n    end\n\n    macro bar\n      foo\n      :bar\n    end\n\n    b‸ar\n    CRYSTAL\n\n    assert_expand code, [[\"bar\", \"foo\\n:bar\\n\", \":foo\\n:bar\\n\"]] do |result|\n      expansion = result.expansions.not_nil![0]\n\n      macros = expansion.expanded_macros\n      macros.size.should eq(2)\n      macros[0].size.should eq(1)\n      macros[1].size.should eq(1)\n\n      macro1 = macros[0][0]\n      macro1[:name].should eq(\"bar\")\n      macro1[:implementation].filename.should eq(\".\")\n      macro1[:implementation].line.should eq(5)\n      macro1[:implementation].column.should eq(1)\n\n      macro2 = macros[1][0]\n      macro2[:name].should eq(\"foo\")\n      macro2[:implementation].filename.should eq(\".\")\n      macro2[:implementation].line.should eq(1)\n      macro2[:implementation].column.should eq(1)\n    end\n  end\n\n  it \"expands macros with 3 level\" do\n    code = <<-CRYSTAL\n    macro foo\n      :foo\n    end\n\n    macro bar\n      foo\n      :bar\n    end\n\n    macro baz\n      foo\n      bar\n      :baz\n    end\n\n    ba‸z\n    CRYSTAL\n\n    assert_expand code, [[\"baz\", \"foo\\nbar\\n:baz\\n\", \":foo\\nfoo\\n:bar\\n:baz\\n\", \":foo\\n:foo\\n:bar\\n:baz\\n\"]] do |result|\n      expansion = result.expansions.not_nil![0]\n\n      macros = expansion.expanded_macros\n      macros.size.should eq(3)\n      macros[0].size.should eq(1)\n      macros[1].size.should eq(2)\n      macros[2].size.should eq(1)\n\n      macro1 = macros[0][0]\n      macro1[:name].should eq(\"baz\")\n      macro1[:implementation].filename.should eq(\".\")\n      macro1[:implementation].line.should eq(10)\n      macro1[:implementation].column.should eq(1)\n\n      macro2 = macros[1][0]\n      macro2[:name].should eq(\"foo\")\n      macro2[:implementation].filename.should eq(\".\")\n      macro2[:implementation].line.should eq(1)\n      macro2[:implementation].column.should eq(1)\n\n      macro3 = macros[1][1]\n      macro3[:name].should eq(\"bar\")\n      macro3[:implementation].filename.should eq(\".\")\n      macro3[:implementation].line.should eq(5)\n      macro3[:implementation].column.should eq(1)\n\n      macro4 = macros[2][0]\n      macro4[:name].should eq(\"foo\")\n      macro4[:implementation].filename.should eq(\".\")\n      macro4[:implementation].line.should eq(1)\n      macro4[:implementation].column.should eq(1)\n    end\n  end\n\n  it \"expands macro of module\" do\n    code = <<-CRYSTAL\n    module Foo\n      macro foo\n        :Foo\n        :foo\n      end\n    end\n\n    Foo.f‸oo\n    CRYSTAL\n\n    assert_expand_simple code, original: \"Foo.foo\", expanded: \":Foo\\n:foo\\n\" do |expansion|\n      expansion.expanded_macros.size.should eq(1)\n      macros = expansion.expanded_macros[0]\n      macros.size.should eq(1)\n\n      a_macro = macros[0]\n      a_macro[:name].should eq(\"Foo.foo\")\n      a_macro[:implementation].filename.should eq(\".\")\n      a_macro[:implementation].line.should eq(2)\n      a_macro[:implementation].column.should eq(3)\n    end\n  end\n\n  it \"expands macro of module with cursor at module name\" do\n    code = <<-CRYSTAL\n    module Foo\n      macro foo\n        :Foo\n        :foo\n      end\n    end\n\n    F‸oo.foo\n    CRYSTAL\n\n    assert_expand_simple code, original: \"Foo.foo\", expanded: \":Foo\\n:foo\\n\"\n  end\n\n  it \"expands macro of module with cursor at dot\" do\n    code = <<-CRYSTAL\n    module Foo\n      macro foo\n        :Foo\n        :foo\n      end\n    end\n\n    Foo‸.foo\n    CRYSTAL\n\n    assert_expand_simple code, original: \"Foo.foo\", expanded: \":Foo\\n:foo\\n\"\n  end\n\n  it \"expands macro of module inside module\" do\n    code = <<-CRYSTAL\n    module Foo\n      macro foo\n        :Foo\n        :foo\n      end\n\n      f‸oo\n    end\n    CRYSTAL\n\n    assert_expand_simple code, original: \"foo\", expanded: \":Foo\\n:foo\\n\"\n  end\n\n  %w(module class struct enum lib).each do |keyword|\n    it \"expands macro expression inside #{keyword}\" do\n      code = <<-CRYSTAL\n      #{keyword} Foo\n        ‸{{ \"Foo = 1\".id }}\n      end\n      CRYSTAL\n\n      assert_expand_simple code, original: %({{ \"Foo = 1\".id }}), expanded: \"Foo = 1\"\n    end\n\n    it \"expands macro expression inside private #{keyword}\" do\n      code = <<-CRYSTAL\n      private #{keyword} Foo\n        ‸{{ \"Foo = 1\".id }}\n      end\n      CRYSTAL\n\n      assert_expand_simple code, original: %({{ \"Foo = 1\".id }}), expanded: \"Foo = 1\"\n    end\n\n    unless keyword == \"lib\"\n      it \"expands macro expression inside def of private #{keyword}\" do\n        code = <<-CRYSTAL\n        private #{keyword} Foo\n          Foo = 1\n          def self.foo\n            {{ :‸foo }}\n          end\n        end\n\n        Foo.foo\n        CRYSTAL\n\n        assert_expand_simple code, original: \"{{ :foo }}\", expanded: \":foo\"\n      end\n    end\n  end\n\n  %w(struct union).each do |keyword|\n    it \"expands macro expression inside C #{keyword}\" do\n      code = <<-CRYSTAL\n      lib Foo\n        #{keyword} Foo\n          ‸{{ \"x : Int32\".id }}\n        end\n      end\n      CRYSTAL\n\n      assert_expand_simple code, original: %({{ \"x : Int32\".id }}), expanded: \"x : Int32\"\n    end\n\n    it \"expands macro expression inside C #{keyword} of private lib\" do\n      code = <<-CRYSTAL\n      private lib Foo\n        #{keyword} Foo\n          ‸{{ \"x : Int32\".id }}\n        end\n      end\n      CRYSTAL\n\n      assert_expand_simple code, original: %({{ \"x : Int32\".id }}), expanded: \"x : Int32\"\n    end\n  end\n\n  [\"\", \"private \"].each do |prefix|\n    it \"expands macro expression inside #{prefix}def\" do\n      code = <<-CRYSTAL\n      #{prefix}def foo(x : T) forall T\n        ‸{{ T }}\n      end\n\n      foo 1\n      foo \"bar\"\n      CRYSTAL\n\n      assert_expand code, [\n        [\"{{ T }}\", \"Int32\"],\n        [\"{{ T }}\", \"String\"],\n      ]\n    end\n\n    it \"expands macro expression inside def of #{prefix}module\" do\n      code = <<-CRYSTAL\n      #{prefix}module Foo(T)\n        def self.foo\n          {{ ‸T }}\n        end\n      end\n\n      Foo(Int32).foo\n      Foo(String).foo\n      Foo(1).foo\n      CRYSTAL\n\n      assert_expand code, [\n        [\"{{ T }}\", \"Int32\"],\n        [\"{{ T }}\", \"String\"],\n        [\"{{ T }}\", \"1\"],\n      ]\n    end\n\n    it \"expands macro expression inside def of nested #{prefix}module\" do\n      code = <<-CRYSTAL\n      #{prefix}module Foo\n        #{prefix}module Bar(T)\n          def self.foo\n            {{ ‸T }}\n          end\n        end\n\n        Bar(Int32).foo\n        Bar(String).foo\n        Bar(1).foo\n      end\n      CRYSTAL\n\n      assert_expand code, [\n        [\"{{ T }}\", \"Int32\"],\n        [\"{{ T }}\", \"String\"],\n        [\"{{ T }}\", \"1\"],\n      ]\n    end\n  end\n\n  it \"expands macro expression inside fun\" do\n    code = <<-CRYSTAL\n    fun foo\n      {{ :foo‸ }}\n    end\n    CRYSTAL\n\n    assert_expand_simple code, original: \"{{ :foo }}\", expanded: \":foo\"\n  end\n\n  it \"doesn't expand macro expression\" do\n    code = <<-CRYSTAL\n    {{ 1 + 2 }}\n    ‸\n    CRYSTAL\n\n    assert_expand_fail code\n  end\n\n  it \"doesn't expand macro expression with cursor out of end\" do\n    code = <<-CRYSTAL\n    {{ 1 + 2 }}‸\n    CRYSTAL\n\n    assert_expand_fail code\n  end\n\n  it \"doesn't expand macro expression\" do\n    code = <<-CRYSTAL\n    ‸  {{ 1 + 2 }}\n    CRYSTAL\n\n    assert_expand_fail code\n  end\n\n  it \"doesn't expand normal call\" do\n    code = <<-CRYSTAL\n    def foo\n      1\n    end\n\n    ‸foo\n    CRYSTAL\n\n    assert_expand_fail code, \"no expansion found: foo may not be a macro\"\n  end\n\n  it \"expands macro with doc\" do\n    code = <<-CRYSTAL\n    macro foo(x)\n      # string of {{ x }}\n      def {{ x }}_str\n        {{ x.stringify }}\n      end\n      # symbol of {{ x }}\n      def {{ x }}_sym\n        {{ x.symbolize }}\n      end\n    end\n\n    ‸foo(hello)\n    CRYSTAL\n\n    expanded = <<-CRYSTAL\n    # string of hello\n    def hello_str\n      \"hello\"\n    end\n\n    # symbol of hello\n    def hello_sym\n      :hello\n    end\n    CRYSTAL\n\n    assert_expand_simple code, original: \"foo(hello)\", expanded: expanded + '\\n'\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/flags_spec.cr",
    "content": "require \"../../../spec_helper\"\ninclude Crystal\n\nprivate def parse_flags(source)\n  Crystal::Command::FlagsVisitor.new.tap do |visitor|\n    Parser.parse(source).accept(visitor)\n  end\nend\n\ndescribe Crystal::Command::FlagsVisitor do\n  it \"different flags\" do\n    visitor = parse_flags <<-CRYSTAL\n      {%\n        flag?(:foo)\n        flag?(\"bar\")\n        flag?(1)\n        flag?(true)\n      %}\n      CRYSTAL\n    visitor.flag_names.should eq %w[1 bar foo true]\n  end\n\n  it \"unique flags\" do\n    visitor = parse_flags <<-CRYSTAL\n      {%\n        flag?(:foo)\n        flag?(\"foo\")\n        flag?(:foo)\n      %}\n      CRYSTAL\n    visitor.flag_names.should eq %w[foo]\n  end\n\n  it \"only macro\" do\n    visitor = parse_flags <<-CRYSTAL\n      flag?(:flag)\n      f.flag?(:foo)\n      F.flag?(:bar)\n      {% f.flag?(:baz) %}\n      {% f.flag?(:qux, other: true) %}\n      CRYSTAL\n    visitor.flag_names.should eq %w[]\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/format_spec.cr",
    "content": "{% skip_file if flag?(:bits32) %}\n\nrequire \"spec\"\nrequire \"compiler/crystal/formatter\"\nrequire \"compiler/crystal/command/format\"\nrequire \"../../../support/tempfile\"\n\nprivate class BuggyFormatCommand < Crystal::Command::FormatCommand\n  def format(filename, source)\n    raise \"format command test\"\n  end\nend\n\ndescribe Crystal::Command::FormatCommand do\n  it \"formats stdin\" do\n    stdin = IO::Memory.new \"if true\\n1\\nend\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    format_command = Crystal::Command::FormatCommand.new([\"-\"], stdin: stdin, stdout: stdout, stderr: stderr)\n    format_command.run\n    format_command.status_code.should eq(0)\n    stdout.to_s.should eq(\"if true\\n  1\\nend\\n\")\n    stderr.to_s.should be_empty\n  end\n\n  it \"formats stdin (formatted)\" do\n    stdin = IO::Memory.new \"if true\\n  1\\nend\\n\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    format_command = Crystal::Command::FormatCommand.new([\"-\"], stdin: stdin, stdout: stdout, stderr: stderr)\n    format_command.run\n    format_command.status_code.should eq(0)\n    stdout.to_s.should eq(\"if true\\n  1\\nend\\n\")\n    stderr.to_s.should be_empty\n  end\n\n  it \"formats stdin (syntax error)\" do\n    stdin = IO::Memory.new \"if\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    format_command = Crystal::Command::FormatCommand.new([\"-\"], stdin: stdin, stdout: stdout, stderr: stderr)\n    format_command.run\n    format_command.status_code.should eq(1)\n    stdout.to_s.should be_empty\n    stderr.to_s.should contain(\"syntax error in 'STDIN:1:3': unexpected token: EOF\")\n  end\n\n  it \"formats stdin (invalid byte sequence error)\" do\n    stdin = IO::Memory.new \"\\xfe\\xff\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    format_command = Crystal::Command::FormatCommand.new([\"-\"], stdin: stdin, stdout: stdout, stderr: stderr)\n    format_command.run\n    format_command.status_code.should eq(1)\n    stdout.to_s.should be_empty\n    stderr.to_s.should contain(\"file 'STDIN' is not a valid Crystal source file: Unexpected byte 0xfe at position 0, malformed UTF-8\")\n  end\n\n  it \"formats stdin (bug)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    format_command = BuggyFormatCommand.new([\"-\"], stdin: stdin, stdout: stdout, stderr: stderr)\n    format_command.run\n    format_command.status_code.should eq(1)\n    stdout.to_s.should be_empty\n    stderr.to_s.should contain(\"there's a bug formatting 'STDIN', to show more information, please run:\\n\\n  $ crystal tool format --show-backtrace -\")\n  end\n\n  it \"formats stdin (bug + show-backtrace)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    format_command = BuggyFormatCommand.new([\"-\"], show_backtrace: true, stdin: stdin, stdout: stdout, stderr: stderr)\n    format_command.run\n    format_command.status_code.should eq(1)\n    stdout.to_s.should be_empty\n    stderr.to_s.should contain(\"format command test\")\n    stderr.to_s.should contain(\"couldn't format 'STDIN', please report a bug including the contents of it: https://github.com/crystal-lang/crystal/issues\")\n  end\n\n  it \"formats files\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"format.cr\", \"if true\\n1\\nend\"\n      File.write \"not_format.cr\", \"if true\\n  1\\nend\\n\"\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(0)\n      stdout.to_s.should contain(\"Format #{Path[\".\", \"format.cr\"]}\")\n      stdout.to_s.should_not contain(\"Format #{Path[\".\", \"not_format.cr\"]}\")\n      stderr.to_s.should be_empty\n\n      File.read(\"format.cr\").should eq(\"if true\\n  1\\nend\\n\")\n    end\n  end\n\n  it \"formats files (dir)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      Dir.mkdir \"dir\"\n      File.write \"format.cr\", \"if true\\n1\\nend\"\n      File.write \"not_format.cr\", \"if true\\n  1\\nend\\n\"\n      File.write File.join(\"dir\", \"format.cr\"), \"if true\\n1\\nend\"\n      File.write File.join(\"dir\", \"not_format.cr\"), \"if true\\n  1\\nend\\n\"\n\n      format_command = Crystal::Command::FormatCommand.new([\"dir\"], color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(0)\n      stdout.to_s.should contain(\"Format #{Path[\".\", \"dir\", \"format.cr\"]}\")\n      stdout.to_s.should_not contain(\"Format #{Path[\".\", \"dir\", \"not_format.cr\"]}\")\n      stderr.to_s.should be_empty\n\n      {stdout, stderr}.each &.clear\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(0)\n      stdout.to_s.should contain(\"Format #{Path[\".\", \"format.cr\"]}\")\n      stdout.to_s.should_not contain(\"Format #{Path[\".\", \"not_format.cr\"]}\")\n      stdout.to_s.should_not contain(\"Format #{Path[\".\", \"dir\", \"format.cr\"]}\")\n      stdout.to_s.should_not contain(\"Format #{Path[\".\", \"dir\", \"not_format.cr\"]}\")\n      stderr.to_s.should be_empty\n\n      File.read(\"format.cr\").should eq(\"if true\\n  1\\nend\\n\")\n      File.read(File.join(\"dir\", \"format.cr\")).should eq(\"if true\\n  1\\nend\\n\")\n    end\n  end\n\n  it \"formats files (error)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"format.cr\", \"if true\\n1\\nend\"\n      File.write \"syntax_error.cr\", \"if\"\n      File.write \"invalid_byte_sequence_error.cr\", \"\\xfe\\xff\"\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(1)\n      stdout.to_s.should contain(\"Format #{Path[\".\", \"format.cr\"]}\")\n      stderr.to_s.should contain(\"syntax error in '#{Path[\".\", \"syntax_error.cr\"]}:1:3': unexpected token: EOF\")\n      stderr.to_s.should contain(\"file '#{Path[\".\", \"invalid_byte_sequence_error.cr\"]}' is not a valid Crystal source file: Unexpected byte 0xfe at position 0, malformed UTF-8\")\n\n      File.read(\"format.cr\").should eq(\"if true\\n  1\\nend\\n\")\n    end\n  end\n\n  it \"formats files (bug)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"empty.cr\", \"\"\n\n      format_command = BuggyFormatCommand.new([] of String, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(1)\n      stderr.to_s.should contain(\"there's a bug formatting '#{Path[\".\", \"empty.cr\"]}', to show more information, please run:\\n\\n  $ crystal tool format --show-backtrace '#{Path[\".\", \"empty.cr\"]}'\")\n    end\n  end\n\n  it \"formats files (bug + show-stacktrace)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"empty.cr\", \"\"\n\n      format_command = BuggyFormatCommand.new([] of String, show_backtrace: true, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(1)\n      stderr.to_s.should contain(\"format command test\")\n      stderr.to_s.should contain(\"couldn't format '#{Path[\".\", \"empty.cr\"]}', please report a bug including the contents of it: https://github.com/crystal-lang/crystal/issues\")\n    end\n  end\n\n  it \"checks files format\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"format.cr\", \"if true\\n1\\nend\"\n      File.write \"not_format.cr\", \"if true\\n  1\\nend\\n\"\n      File.write \"syntax_error.cr\", \"if\"\n      File.write \"invalid_byte_sequence_error.cr\", \"\\xfe\\xff\"\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, check: true, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(1)\n      stdout.to_s.should be_empty\n      stderr.to_s.should_not contain(\"not_format.cr\")\n      stderr.to_s.should contain(\"formatting '#{Path[\".\", \"format.cr\"]}' produced changes\")\n      stderr.to_s.should contain(\"syntax error in '#{Path[\".\", \"syntax_error.cr\"]}:1:3': unexpected token: EOF\")\n      stderr.to_s.should contain(\"file '#{Path[\".\", \"invalid_byte_sequence_error.cr\"]}' is not a valid Crystal source file: Unexpected byte 0xfe at position 0, malformed UTF-8\")\n    end\n  end\n\n  it \"checks files format (ok)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"format1.cr\", \"if true\\n  1\\nend\\n\"\n      File.write \"format2.cr\", \"if true\\n  2\\nend\\n\"\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, check: true, color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(0)\n      stdout.to_s.should be_empty\n      stderr.to_s.should be_empty\n    end\n  end\n\n  it \"checks files format (excludes)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"format.cr\", \"if true\\n1\\nend\"\n      File.write \"not_format.cr\", \"if true\\n  1\\nend\\n\"\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, check: true, excludes: [\"format.cr\"], color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(0)\n      stdout.to_s.should be_empty\n      stderr.to_s.should be_empty\n    end\n  end\n\n  it \"checks files format (excludes + includes)\" do\n    stdin = IO::Memory.new \"\"\n    stdout = IO::Memory.new\n    stderr = IO::Memory.new\n\n    with_tempdir do\n      File.write \"format.cr\", \"if true\\n1\\nend\"\n      File.write \"not_format.cr\", \"if true\\n  1\\nend\\n\"\n\n      format_command = Crystal::Command::FormatCommand.new([] of String, check: true, excludes: [\"format.cr\"], includes: [\"format.cr\"], color: false, stdin: stdin, stdout: stdout, stderr: stderr)\n      format_command.run\n      format_command.status_code.should eq(1)\n      stdout.to_s.should be_empty\n      stderr.to_s.should contain(\"formatting '#{Path[\".\", \"format.cr\"]}' produced changes\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/hierarchy_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def assert_text_hierarchy(source, filter, expected, *, file = __FILE__, line = __LINE__)\n  program = semantic(source).program\n  output = String.build { |io| Crystal.print_hierarchy(program, io, filter, \"text\") }\n  output.should eq(expected), file: file, line: line\nend\n\nprivate def assert_json_hierarchy(source, filter, expected, *, file = __FILE__, line = __LINE__)\n  program = semantic(source).program\n  output = String.build { |io| Crystal.print_hierarchy(program, io, filter, \"json\") }\n  JSON.parse(output).should eq(JSON.parse(expected)), file: file, line: line\nend\n\ndescribe Crystal::TextHierarchyPrinter do\n  it \"works\" do\n    assert_text_hierarchy <<-CRYSTAL, \"ar$\", <<-EOS\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n      - class Object (4 bytes)\n        |\n        +- class Reference (4 bytes)\n           |\n           +- class Foo (4 bytes)\n              |\n              +- class Bar (4 bytes)\\n\n      EOS\n  end\n\n  it \"shows correct size for Bool member\" do\n    assert_text_hierarchy <<-CRYSTAL, \"Foo\", <<-EOS\n      struct Foo\n        @x = true\n      end\n      CRYSTAL\n      - class Object (4 bytes)\n        |\n        +- struct Value (0 bytes)\n           |\n           +- struct Struct (0 bytes)\n              |\n              +- struct Foo (1 bytes)\n                     @x : Bool (1 bytes)\\n\n      EOS\n  end\n\n  it \"shows correct size for members with bound types\" do\n    assert_text_hierarchy <<-CRYSTAL, \"Foo\", <<-EOS\n      struct Bar1(T)\n        @x = uninitialized T\n      end\n\n      class Bar2(T)\n        @x = uninitialized T\n      end\n\n      module Bar3(T)\n        struct I(T)\n          include Bar3(T)\n\n          @x = uninitialized T\n        end\n      end\n\n      module Bar4(T)\n        class I(T)\n          include Bar3(T)\n\n          @x = uninitialized T\n        end\n      end\n\n      class Foo(T)\n        @a = uninitialized T*\n        @b = uninitialized T\n        @c = uninitialized T[4]\n        @d = uninitialized Int32[T]\n        @e = uninitialized T ->\n        @f = uninitialized T?\n        @g = uninitialized {T}\n        @h = uninitialized {x: T}\n        @i = uninitialized Bar1(T)\n        @j = uninitialized Bar2(T)\n        @k = uninitialized Bar3(T)\n        @l = uninitialized Bar4(T)\n      end\n      CRYSTAL\n      - class Object (4 bytes)\n        |\n        +- class Reference (4 bytes)\n           |\n           +- class Foo(T)\n                  @a : Pointer(T)            ( 8 bytes)\n                  @b : T\n                  @c : StaticArray(T, 4)\n                  @d : StaticArray(Int32, T)\n                  @e : Proc(T, Nil)          (16 bytes)\n                  @f : (T | Nil)\n                  @g : Tuple(T)\n                  @h : NamedTuple(x: T)\n                  @i : Bar1(T)\n                  @j : Bar2(T)               ( 8 bytes)\n                  @k : Bar3(T)\n                  @l : Bar4(T)               ( 8 bytes)\\n\n      EOS\n  end\n\n  it \"shows correct total size of generic class if known\" do\n    assert_text_hierarchy <<-CRYSTAL, \"Foo\", <<-EOS\n      class Bar1(T)\n        @x = uninitialized T\n      end\n\n      class Bar2(T)\n        @x = uninitialized T\n      end\n\n      class Foo(T)\n        @a = uninitialized T*\n        @b : Bar1(T) | Bar2(T)?\n        @c = uninitialized T*[6]\n        @d = uninitialized Int64\n      end\n      CRYSTAL\n      - class Object (4 bytes)\n        |\n        +- class Reference (4 bytes)\n           |\n           +- class Foo(T) (80 bytes)\n                  @a : Pointer(T)                 ( 8 bytes)\n                  @b : (Bar1(T) | Bar2(T) | Nil)  ( 8 bytes)\n                  @c : StaticArray(Pointer(T), 6) (48 bytes)\n                  @d : Int64                      ( 8 bytes)\\n\n      EOS\n  end\n\n  it \"shows correct size for Proc inside extern struct\" do\n    assert_text_hierarchy <<-CRYSTAL, \"Foo\", <<-EOS\n      @[Extern]\n      struct Foo\n        @x = uninitialized ->\n      end\n\n      lib Bar\n        struct Foo\n          x : Int32 -> Int32\n        end\n      end\n      CRYSTAL\n      - class Object (4 bytes)\n        |\n        +- struct Value (0 bytes)\n           |\n           +- struct Struct (0 bytes)\n              |\n              +- struct Bar::Foo (8 bytes)\n              |      @x : Proc(Int32, Int32) (8 bytes)\n              |\n              +- struct Foo (8 bytes)\n                     @x : Proc(Nil) (8 bytes)\\n\n      EOS\n  end\n\n  it \"shows extern unions\" do\n    assert_text_hierarchy <<-CRYSTAL, \"Foo\", <<-EOS\n      lib Lib\n        union Foo\n          x : Int32\n          y : Float64\n        end\n      end\n      CRYSTAL\n      - class Object (4 bytes)\n        |\n        +- struct Value (0 bytes)\n           |\n           +- struct Struct (0 bytes)\n              |\n              +- union Lib::Foo (8 bytes)\n                     @x : Int32   (4 bytes)\n                     @y : Float64 (8 bytes)\\n\n      EOS\n  end\nend\n\ndescribe Crystal::JSONHierarchyPrinter do\n  it \"works\" do\n    assert_json_hierarchy <<-CRYSTAL, \"ar$\", <<-JSON\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n      {\n        \"name\": \"Object\",\n        \"kind\": \"class\",\n        \"size_in_bytes\": 4,\n        \"sub_types\": [\n          {\n            \"name\": \"Reference\",\n            \"kind\": \"class\",\n            \"size_in_bytes\": 4,\n            \"sub_types\": [\n              {\n                \"name\": \"Foo\",\n                \"kind\": \"class\",\n                \"size_in_bytes\": 4,\n                \"sub_types\": [\n                  {\n                    \"name\": \"Bar\",\n                    \"kind\": \"class\",\n                    \"size_in_bytes\": 4,\n                    \"sub_types\": []\n                  }\n                ]\n              }\n            ]\n          }\n        ]\n      }\n      JSON\n  end\n\n  it \"shows extern unions\" do\n    assert_json_hierarchy <<-CRYSTAL, \"Foo\", <<-JSON\n      lib Lib\n        union Foo\n          x : Int32\n          y : Float64\n        end\n      end\n      CRYSTAL\n      {\n        \"name\": \"Object\",\n        \"kind\": \"class\",\n        \"size_in_bytes\": 4,\n        \"sub_types\": [\n          {\n            \"name\": \"Value\",\n            \"kind\": \"struct\",\n            \"size_in_bytes\": 0,\n            \"sub_types\": [\n              {\n                \"name\": \"Struct\",\n                \"kind\": \"struct\",\n                \"size_in_bytes\": 0,\n                \"sub_types\": [\n                  {\n                    \"name\": \"Lib::Foo\",\n                    \"kind\": \"union\",\n                    \"size_in_bytes\": 8,\n                    \"instance_vars\": [\n                      {\n                        \"name\": \"@x\",\n                        \"type\": \"Int32\",\n                        \"size_in_bytes\": 4\n                      },\n                      {\n                        \"name\": \"@y\",\n                        \"type\": \"Float64\",\n                        \"size_in_bytes\": 8\n                      }\n                    ],\n                    \"sub_types\": []\n                  }\n                ]\n              }\n            ]\n          }\n        ]\n      }\n      JSON\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/implementations_spec.cr",
    "content": "require \"../../../spec_helper\"\n\nprivate def processed_implementation_visitor(code, cursor_location)\n  compiler = Compiler.new\n  compiler.no_codegen = true\n  compiler.prelude = \"empty\"\n  result = compiler.compile(Compiler::Source.new(\".\", code), \"fake-no-build\")\n\n  visitor = ImplementationsVisitor.new(cursor_location)\n  process_result = visitor.process(result)\n\n  {visitor, process_result}\nend\n\nprivate def assert_implementations(code)\n  cursor_location = nil\n  expected_locations = [] of Location\n\n  code.lines.each_with_index do |line, line_number_0|\n    if column_number = line.index('‸')\n      cursor_location = Location.new(\".\", line_number_0 + 1, column_number + 1)\n    end\n\n    if column_number = line.index('༓')\n      expected_locations << Location.new(\".\", line_number_0 + 1, column_number + 1)\n    end\n  end\n\n  code = code.delete &.in?('‸', '༓')\n\n  if cursor_location\n    _, result = processed_implementation_visitor(code, cursor_location)\n\n    result_locations = result.implementations.not_nil!.map do |e|\n      Location.new(e.filename.not_nil!, e.line.not_nil!, e.column.not_nil!).to_s\n    end.sort!\n\n    result_locations.should eq(expected_locations.map(&.to_s))\n  else\n    raise \"no cursor found in spec\"\n  end\nend\n\n# References\n#\n#   ༓ marks the expected implementations to be found\n#   ‸ marks the method call which implementations wants to be found\n#\ndescribe \"implementations\" do\n  it \"find top level method calls\" do\n    assert_implementations %(\n      ༓def foo\n        1\n      end\n\n      f‸oo\n    )\n  end\n\n  it \"find implementors of different classes\" do\n    assert_implementations %(\n      class Foo\n        ༓def foo\n        end\n      end\n\n      class Bar\n        ༓def foo\n        end\n      end\n\n      def bar(o)\n        o.f‸oo\n      end\n\n      bar(Foo.new)\n      bar(Bar.new)\n    )\n  end\n\n  it \"find implementors of classes that are only used\" do\n    assert_implementations %(\n      class Foo\n        ༓def foo\n        end\n      end\n\n      class Bar\n        def foo\n        end\n      end\n\n      def bar(o)\n        o.f‸oo\n      end\n\n      bar(Foo.new)\n      Bar.new\n    )\n  end\n\n  it \"find method calls inside while\" do\n    assert_implementations %(\n      ༓def foo\n        1\n      end\n\n      while false\n        f‸oo\n      end\n    )\n  end\n\n  it \"find method calls inside while cond\" do\n    assert_implementations %(\n      ༓def foo\n        1\n      end\n\n      while f‸oo\n      end\n    )\n  end\n\n  it \"find method calls inside if\" do\n    assert_implementations %(\n      ༓def foo\n        1\n      end\n\n      if f‸oo\n      end\n    )\n  end\n\n  it \"find method calls inside trailing if\" do\n    assert_implementations %(\n      ༓def foo\n        1\n      end\n\n      2 if f‸oo\n    )\n  end\n\n  it \"find method calls inside rescue\" do\n    assert_implementations %(\n      ༓def foo\n        1\n      end\n\n      begin\n      rescue\n        f‸oo\n      end\n    )\n  end\n\n  it \"find implementation from macro expansions\" do\n    assert_implementations %(\n      macro foo\n        def bar\n        end\n      end\n\n      macro baz\n        foo\n      end\n\n      ༓baz\n      b‸ar\n    )\n  end\n\n  it \"find full trace for macro expansions\" do\n    _, result = processed_implementation_visitor(%(\n      macro foo\n        def bar\n        end\n      end\n\n      macro baz\n        foo\n      end\n\n      baz\n      bar\n    ), Location.new(\".\", 12, 9))\n\n    result.implementations.should_not be_nil\n    impls = result.implementations.not_nil!\n    impls.size.should eq(1)\n\n    impls[0].line.should eq(11) # location of baz\n    impls[0].column.should eq(7)\n    impls[0].filename.should eq(\".\")\n\n    impls[0].expands.should_not be_nil\n    exp = impls[0].expands.not_nil!\n    exp.line.should eq(8) # location of foo call in macro baz\n    exp.column.should eq(9)\n    exp.macro.should eq(\"baz\")\n    exp.filename.should eq(\".\")\n\n    exp.expands.should_not be_nil\n    exp = exp.expands.not_nil!\n    exp.line.should eq(3) # location of def bar in macro foo\n    exp.column.should eq(9)\n    exp.macro.should eq(\"foo\")\n    exp.filename.should eq(\".\")\n  end\n\n  it \"can display text output\" do\n    _, result = processed_implementation_visitor(%(\n      macro foo\n        def bar\n        end\n      end\n\n      macro baz\n        foo\n      end\n\n      baz\n      bar\n    ), Location.new(\".\", 12, 9))\n\n    String::Builder.build do |io|\n      result.to_text(io)\n    end.should eq %(1 implementation found\n.:11:7\n ~> macro baz: .:8:9\n ~> macro foo: .:3:9\n)\n  end\n\n  it \"can display json output\" do\n    _, result = processed_implementation_visitor(%(\n      macro foo\n        def bar\n        end\n      end\n\n      macro baz\n        foo\n      end\n\n      baz\n      bar\n    ), Location.new(\".\", 12, 9))\n\n    String::Builder.build do |io|\n      result.to_json(io)\n    end.should eq %({\"status\":\"ok\",\"message\":\"1 implementation found\",\"implementations\":[{\"line\":11,\"column\":7,\"filename\":\".\",\"expands\":{\"line\":8,\"column\":9,\"filename\":\".\",\"macro\":\"baz\",\"expands\":{\"line\":3,\"column\":9,\"filename\":\".\",\"macro\":\"foo\"}}}]})\n  end\n\n  it \"find implementation in class methods\" do\n    assert_implementations %(\n    ༓def foo\n    end\n\n    class Bar\n      def self.bar\n        f‸oo\n      end\n    end\n\n    Bar.bar)\n  end\n\n  it \"find implementation in generic class\" do\n    assert_implementations %(\n    class Foo\n      ༓def self.foo\n      end\n    end\n\n    class Baz\n      ༓def self.foo\n      end\n    end\n\n    class Bar(T)\n      def bar\n        T.f‸oo\n      end\n    end\n\n    Bar(Foo).new.bar\n    Bar(Baz).new.bar\n    )\n  end\n\n  it \"find implementation in generic class methods\" do\n    assert_implementations %(\n    ༓def foo\n    end\n\n    class Bar(T)\n      def self.bar\n        f‸oo\n      end\n    end\n\n    Bar(Nil).bar\n    )\n  end\n\n  it \"find implementation inside a module class\" do\n    assert_implementations %(\n    ༓def foo\n    end\n\n    module Baz\n      class Bar(T)\n        def self.bar\n          f‸oo\n        end\n      end\n    end\n\n    Baz::Bar(Nil).bar\n    )\n  end\n\n  it \"find implementation inside contained class' class method\" do\n    assert_implementations %(\n    ༓def foo\n\n    end\n\n    class Bar(T)\n      class Foo\n        def self.bar_foo\n          f‸oo\n        end\n      end\n    end\n\n    Bar::Foo.bar_foo\n    )\n  end\n\n  it \"find implementation inside contained file private method\" do\n    assert_implementations %(\n    private ༓def foo\n    end\n\n    private def bar\n      f‸oo\n    end\n\n    bar\n    )\n  end\n\n  it \"find implementation inside contained file private class' class method\" do\n    assert_implementations %(\n    private ༓def foo\n    end\n\n    private class Bar\n      def self.bar\n        f‸oo\n      end\n    end\n\n    Bar.bar\n    )\n  end\n\n  it \"find class implementation\" do\n    assert_implementations %(\n    ༓class Foo\n    end\n\n    F‸oo\n    )\n  end\n\n  it \"find open class implementation\" do\n    assert_implementations %(\n    ༓class Foo\n      def foo\n      end\n    end\n\n    ༓class Foo\n      def bar\n      end\n    end\n\n    F‸oo\n    )\n  end\n\n  it \"find struct implementation\" do\n    assert_implementations %(\n    ༓struct Foo\n    end\n\n    F‸oo\n    )\n  end\n\n  it \"find module implementation\" do\n    assert_implementations %(\n    ༓module Foo\n    end\n\n    F‸oo\n    )\n  end\n\n  it \"find enum implementation\" do\n    assert_implementations %(\n    ༓enum Foo\n      Foo\n    end\n\n    F‸oo\n    )\n  end\n\n  it \"find enum value implementation\" do\n    assert_implementations %(\n    enum Foo\n      ༓Foo\n    end\n\n    Foo::F‸oo\n    )\n  end\n\n  it \"find alias implementation\" do\n    assert_implementations %(\n    class Foo\n    end\n\n    ༓alias Bar = Foo\n\n    B‸ar\n    )\n  end\n\n  it \"find class defined by macro\" do\n    assert_implementations %(\n    macro foo\n      class Foo\n      end\n    end\n\n    ༓foo\n\n    F‸oo\n    )\n  end\n\n  it \"find class inside method\" do\n    assert_implementations %(\n    ༓class Foo\n    end\n\n    def foo\n      F‸oo\n    end\n\n    foo\n    )\n  end\n\n  it \"find const implementation\" do\n    assert_implementations %(\n    ༓Foo = 42\n\n    F‸oo\n    )\n  end\n\n  it \"find implementation on def with no location\" do\n    _, result = processed_implementation_visitor <<-CRYSTAL, Location.new(\".\", 5, 5)\n      enum Foo\n        FOO\n      end\n\n      Foo.new(42)\n      CRYSTAL\n\n    result.implementations.not_nil!.map do |e|\n      Location.new(e.filename, e.line, e.column).to_s\n    end.should eq [\"<unknown>:0:0\"]\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/init_spec.cr",
    "content": "require \"compiler/crystal/syntax\"\nrequire \"compiler/crystal/config\"\nrequire \"compiler/crystal/tools/init\"\nrequire \"file_utils\"\nrequire \"ini\"\nrequire \"spec\"\nrequire \"yaml\"\nrequire \"../../../support/tempfile\"\nrequire \"../../../support/env\"\nrequire \"../../../support/win32\"\n\nprivate def exec_init(project_name, project_dir = nil, type = \"lib\", force = false, skip_existing = false)\n  args = [type, project_name]\n  args << project_dir if project_dir\n  args << \"--force\" if force\n  args << \"--skip-existing\" if skip_existing\n\n  config = Crystal::Init.parse_args(args)\n  config.silent = true\n  Crystal::Init::InitProject.new(config).run\nend\n\nprivate def with_file(name, &)\n  yield File.read(name)\nend\n\nprivate def run_init_project(skeleton_type, name, author, email, github_name, dir = name)\n  Crystal::Init::InitProject.new(\n    Crystal::Init::Config.new(skeleton_type, name, dir, author, email, github_name, true)\n  ).run\nend\n\nprivate def git_available?\n  Process.run(Crystal::Git.executable).success?\nrescue IO::Error\n  false\nend\n\nmodule Crystal\n  describe Init::InitProject do\n    it \"correctly uses git config\" do\n      pending! \"Git is not available\" unless git_available?\n\n      with_tempdir(\"git-config\") do\n        File.write(\".gitconfig\", <<-INI)\n        [user]\n          email = dorian@dorianmarie.fr\n          name = Dorian Marié\n        INI\n\n        with_env(\"GIT_CONFIG\": \"#{FileUtils.pwd}/.gitconfig\") do\n          exec_init(\"example\", \"example\", \"app\")\n        end\n\n        with_file \"example/LICENSE\" do |file|\n          file.should contain(\"Dorian Marié\")\n        end\n      end\n    end\n\n    it \"has proper contents\" do\n      with_tempdir(\"proper-contents\") do\n        run_init_project(\"lib\", \"example\", \"John Smith\", \"john@smith.com\", \"jsmith\")\n        run_init_project(\"app\", \"example_app\", \"John Smith\", \"john@smith.com\", \"jsmith\")\n        run_init_project(\"app\", \"num-followed-hyphen-1\", \"John Smith\", \"john@smith.com\", \"jsmith\")\n        run_init_project(\"lib\", \"example-lib\", \"John Smith\", \"john@smith.com\", \"jsmith\")\n        run_init_project(\"lib\", \"camel_example-camel_lib\", \"John Smith\", \"john@smith.com\", \"jsmith\")\n        run_init_project(\"lib\", \"example\", \"John Smith\", \"john@smith.com\", \"jsmith\", dir: \"other-example-directory\")\n\n        with_file \"example-lib/src/example-lib.cr\" do |file|\n          file.should contain(\"Example::Lib\")\n        end\n\n        with_file \"camel_example-camel_lib/src/camel_example-camel_lib.cr\" do |file|\n          file.should contain(\"CamelExample::CamelLib\")\n        end\n\n        with_file \"num-followed-hyphen-1/src/num-followed-hyphen-1.cr\" do |file|\n          file.should contain(\"Num::Followed::Hyphen1\")\n        end\n\n        with_file \"example/.gitignore\" do |gitignore|\n          gitignore.should contain(\"/docs/\")\n          gitignore.should contain(\"/.shards/\")\n          gitignore.should contain(\"/shard.lock\")\n          gitignore.should contain(\"/lib/\")\n        end\n\n        with_file \"example_app/.gitignore\" do |gitignore|\n          gitignore.should contain(\"/docs/\")\n          gitignore.should contain(\"/.shards/\")\n          gitignore.should_not contain(\"/shard.lock\")\n          gitignore.should contain(\"/lib/\")\n        end\n\n        {\"example\", \"example_app\", \"example-lib\", \"camel_example-camel_lib\"}.each do |name|\n          with_file \"#{name}/.editorconfig\" do |editorconfig|\n            parsed = INI.parse(editorconfig)\n            parsed[\"\"][\"root\"].should eq(\"true\")\n            cr_ext = parsed[\"*.cr\"]\n            cr_ext[\"charset\"].should eq(\"utf-8\")\n            cr_ext[\"end_of_line\"].should eq(\"lf\")\n            cr_ext[\"insert_final_newline\"].should eq(\"true\")\n            cr_ext[\"indent_style\"].should eq(\"space\")\n            cr_ext[\"indent_size\"].should eq(\"2\")\n            cr_ext[\"trim_trailing_whitespace\"].should eq(\"true\")\n          end\n        end\n\n        with_file \"example/LICENSE\" do |license|\n          license.should match %r{Copyright \\(c\\) \\d+ John Smith}\n        end\n\n        with_file \"example/README.md\" do |readme|\n          readme.should contain(\"# example\")\n\n          readme.should contain(%{1. Add the dependency to your `shard.yml`:})\n          readme.should contain(<<-MARKDOWN\n\n           ```yaml\n           dependencies:\n             example:\n               github: jsmith/example\n           ```\n\n        MARKDOWN\n          )\n          readme.should contain(%{2. Run `shards install`})\n          readme.should contain(%{TODO: Write a description here})\n          readme.should_not contain(%{TODO: Write installation instructions here})\n          readme.should contain(%{require \"example\"})\n          readme.should contain(%{1. Fork it (<https://github.com/jsmith/example/fork>)})\n          readme.should contain(%{[John Smith](https://github.com/jsmith) - creator and maintainer})\n        end\n\n        with_file \"example_app/README.md\" do |readme|\n          readme.should contain(\"# example\")\n\n          readme.should contain(%{TODO: Write a description here})\n\n          readme.should_not contain(%{1. Add the dependency to your `shard.yml`:})\n          readme.should_not contain(<<-MARKDOWN\n\n           ```yaml\n           dependencies:\n             example:\n               github: jsmith/example\n           ```\n\n        MARKDOWN\n          )\n          readme.should_not contain(%{2. Run `shards install`})\n          readme.should contain(%{TODO: Write installation instructions here})\n          readme.should_not contain(%{require \"example\"})\n          readme.should contain(%{1. Fork it (<https://github.com/jsmith/example_app/fork>)})\n          readme.should contain(%{[John Smith](https://github.com/jsmith) - creator and maintainer})\n        end\n\n        with_file \"example/shard.yml\" do |shard_yml|\n          parsed = YAML.parse(shard_yml)\n          parsed[\"name\"].should eq(\"example\")\n          parsed[\"version\"].should eq(\"0.1.0\")\n          parsed[\"authors\"].should eq([\"John Smith <john@smith.com>\"])\n          parsed[\"license\"].should eq(\"MIT\")\n          parsed[\"crystal\"].should eq(\">= #{Crystal::Config.version}\")\n          parsed[\"targets\"]?.should be_nil\n        end\n\n        with_file \"example_app/shard.yml\" do |shard_yml|\n          parsed = YAML.parse(shard_yml)\n          parsed[\"targets\"].should eq({\"example_app\" => {\"main\" => \"src/example_app.cr\"}})\n        end\n\n        with_file \"example/src/example.cr\" do |example|\n          example.should eq(<<-CRYSTAL\n        # TODO: Write documentation for `Example`\n        module Example\n          VERSION = \"0.1.0\"\n\n          # TODO: Put your code here\n        end\n\n        CRYSTAL\n          )\n        end\n\n        with_file \"example/spec/spec_helper.cr\" do |example|\n          example.should eq(<<-CRYSTAL\n        require \"spec\"\n        require \"../src/example\"\n\n        CRYSTAL\n          )\n        end\n\n        with_file \"example/spec/example_spec.cr\" do |example|\n          example.should eq(<<-CRYSTAL\n        require \"./spec_helper\"\n\n        describe Example do\n          # TODO: Write tests\n\n          it \"works\" do\n            false.should eq(true)\n          end\n        end\n\n        CRYSTAL\n          )\n        end\n\n        if git_available?\n          with_file \"example/.git/config\" { }\n\n          with_file \"other-example-directory/.git/config\" { }\n        end\n      end\n    end\n  end\n\n  describe \"Init invocation\" do\n    it \"produces valid yaml file\" do\n      with_tempdir(\"valid-yaml\") do\n        exec_init(\"example\", \"example\", \"app\")\n\n        with_file \"example/shard.yml\" do |file|\n          YAML.parse(file)\n        end\n      end\n    end\n\n    it \"prints error if a file is already present\" do\n      with_tempdir(\"already-present\") do\n        existing_file = \"existing-file\"\n        File.touch(existing_file)\n        expect_raises(Crystal::Init::Error, \"#{existing_file.inspect} is a file\") do\n          exec_init(existing_file)\n        end\n      end\n    end\n\n    it \"honors the custom set directory name\" do\n      with_tempdir(\"directory-name\") do\n        project_name = \"my_project\"\n        project_dir = \"project_dir\"\n\n        Dir.mkdir(project_name)\n        File.write(\"README.md\", \"content before init\")\n\n        exec_init(project_name, project_dir)\n\n        File.read(\"README.md\").should eq(\"content before init\")\n        File.exists?(File.join(project_dir, \"README.md\")).should be_true\n      end\n    end\n\n    it \"errors if files will be overwritten by a generated file\" do\n      with_tempdir(\"generated-file\") do\n        File.write(\"README.md\", \"content before init\")\n\n        ex = expect_raises(Crystal::Init::FilesConflictError) do\n          exec_init(\"my_lib\", \".\")\n        end\n        ex.conflicting_files.should contain(\"README.md\")\n\n        File.read(\"README.md\").should eq(\"content before init\")\n        File.exists?(\"LICENSE\").should_not be_true\n      end\n    end\n\n    it \"doesn't error if files will be overwritten by a generated file and --force is used\" do\n      with_tempdir(\"generated-force\") do\n        File.write(\"README.md\", \"content before init\")\n        File.exists?(\"README.md\").should be_true\n\n        exec_init(\"my_lib\", \".\", force: true)\n\n        File.read(\"README.md\").should_not eq(\"content before init\")\n        File.exists?(\"LICENSE\").should be_true\n      end\n    end\n\n    it \"doesn't error when asked to skip existing files\" do\n      with_tempdir(\"skip-existing\") do\n        File.write(\"README.md\", \"content before init\")\n\n        exec_init(\"my_lib\", \".\", skip_existing: true)\n\n        File.read(\"README.md\").should eq(\"content before init\")\n        File.exists?(\"LICENSE\").should be_true\n      end\n    end\n  end\n\n  describe \".parse_args\" do\n    it \"DIR\" do\n      config = Crystal::Init.parse_args([\"lib\", \"foo\"])\n      config.name.should eq \"foo\"\n      config.dir.should eq \"foo\"\n      config.expanded_dir.should eq ::Path[Dir.current, \"foo\"]\n    end\n\n    it \"DIR with path\" do\n      path = ::Path[\"foo\", \"bar\"].to_s\n      config = Crystal::Init.parse_args([\"lib\", path])\n      config.name.should eq \"bar\"\n      config.dir.should eq path\n      config.expanded_dir.should eq ::Path[Dir.current, \"foo\", \"bar\"]\n    end\n\n    it \"DIR (relative to home)\" do\n      path = ::Path[\"~\", \"foo\"].to_s\n      config = Crystal::Init.parse_args([\"lib\", path])\n      config.name.should eq \"foo\"\n      config.dir.should eq path\n      config.expanded_dir.should eq ::Path.home.join(\"foo\")\n    end\n\n    it \"DIR (absolute)\" do\n      path = ::Path[::Path[Dir.current].anchor.to_s, \"foo\"].to_s\n      config = Crystal::Init.parse_args([\"lib\", path])\n      config.name.should eq \"foo\"\n      config.dir.should eq path\n      config.expanded_dir.should eq ::Path[path]\n    end\n\n    it \"DIR = .\" do\n      with_tempdir(\"dir-dot\") do\n        config = Crystal::Init.parse_args([\"lib\", \".\"])\n        config.name.should eq File.basename(Dir.current)\n        config.dir.should eq \".\"\n        config.expanded_dir.should eq ::Path[Dir.current]\n      end\n    end\n\n    it \"NAME DIR\" do\n      config = Crystal::Init.parse_args([\"lib\", \"foo\", \"foo-shard\"])\n      config.name.should eq \"foo\"\n      config.dir.should eq \"foo-shard\"\n      config.expanded_dir.should eq ::Path[Dir.current, \"foo-shard\"]\n    end\n  end\n\n  describe \".validate_name\" do\n    it \"empty\" do\n      expect_raises Crystal::Init::Error, \"NAME must not be empty\" do\n        Crystal::Init.validate_name(\"\")\n      end\n    end\n    it \"length\" do\n      Crystal::Init.validate_name(\"a\" * 50)\n      expect_raises Crystal::Init::Error, \"NAME must not be longer than 50 characters\" do\n        Crystal::Init.validate_name(\"a\" * 51)\n      end\n    end\n    it \"uppercase\" do\n      expect_raises Crystal::Init::Error, \"NAME should be all lower cased\" do\n        Crystal::Init.validate_name(\"Foo\")\n      end\n    end\n    it \"digits\" do\n      Crystal::Init.validate_name(\"i18n\")\n      expect_raises Crystal::Init::Error, \"NAME must start with a letter\" do\n        Crystal::Init.validate_name(\"4u\")\n      end\n    end\n    it \"dashes\" do\n      Crystal::Init.validate_name(\"foo-bar\")\n      expect_raises Crystal::Init::Error, \"NAME must start with a letter\" do\n        Crystal::Init.validate_name(\"-foo\")\n      end\n      expect_raises Crystal::Init::Error, \"NAME must not have consecutive dashes\" do\n        Crystal::Init.validate_name(\"foo--bar\")\n      end\n    end\n    it \"underscores\" do\n      Crystal::Init.validate_name(\"foo_bar\")\n      expect_raises Crystal::Init::Error, \"NAME must start with a letter\" do\n        Crystal::Init.validate_name(\"_foo\")\n      end\n      expect_raises Crystal::Init::Error, \"NAME must not have consecutive underscores\" do\n        Crystal::Init.validate_name(\"foo__bar\")\n      end\n    end\n    it \"invalid character\" do\n      expect_raises Crystal::Init::Error, \"NAME must only contain alphanumerical characters, underscores or dashes\" do\n        Crystal::Init.validate_name(\"foo bar\")\n      end\n      expect_raises Crystal::Init::Error, \"NAME must only contain alphanumerical characters, underscores or dashes\" do\n        Crystal::Init.validate_name(\"foo\\abar\")\n      end\n      Crystal::Init.validate_name(\"grüß-gott\")\n    end\n  end\n\n  describe \"View#module_name\" do\n    it \"namespace is divided by hyphen\" do\n      Crystal::Init::View.module_name(\"my-proj-name\").should eq \"My::Proj::Name\"\n    end\n    it \"hyphen followed by non-ascii letter is replaced by its character\" do\n      Crystal::Init::View.module_name(\"my-proj-1\").should eq \"My::Proj1\"\n    end\n    it \"underscore is ignored\" do\n      Crystal::Init::View.module_name(\"my-proj_name\").should eq \"My::ProjName\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/macro_code_coverage_spec.cr",
    "content": "require \"../../../spec_helper\"\ninclude Crystal\n\nprivate def assert_coverage(code, expected_coverage, *, expected_error : String? = nil, focus : Bool = false, spec_file = __FILE__, spec_line = __LINE__)\n  it focus: focus, file: spec_file, line: spec_line do\n    processor = MacroCoverageProcessor.new\n\n    compiler = Compiler.new\n    compiler.prelude = \"empty\"\n    compiler.no_codegen = true\n    compiler.compile_configure_program(Compiler::Source.new(\".\", code), \"fake-no-build\") do |program|\n      processor.configure program\n    end\n\n    processor.excludes << Path[Dir.current].to_posix.to_s\n    processor.includes << \".\"\n\n    hits = processor.compute_coverage\n\n    unless hits = hits[\".\"]?\n      fail \"Failed to generate coverage\", file: spec_file, line: spec_line\n    end\n\n    coverage_exception = processor.coverage_interrupt_exception\n\n    if expected_error\n      err = coverage_exception.should_not be_nil, file: spec_file, line: spec_line\n      err.inspect_with_backtrace.should contain(expected_error), file: spec_file, line: spec_line\n    else\n      coverage_exception.should be_nil, file: spec_file, line: spec_line\n    end\n\n    hits.should eq(expected_coverage), file: spec_file, line: spec_line\n  end\nend\n\ndescribe \"macro_code_coverage\" do\n  assert_coverage <<-'CRYSTAL', {1 => 1}\n    {{ \"foo\" }}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 1}, expected_error: \"undefined macro method 'NumberLiteral#sdfds'\"\n    {%\n      a = 1\n      b = 2.sdfds\n      c = 3\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {6 => 1, 7 => 1, 9 => \"2/2\", 11 => 1, 12 => 1}, expected_error: \"Class 'Foo' is missing its name.\"\n    annotation Name; end\n\n    @[Name]\n    class Foo\n      def self.default_name\n        {% begin %}\n          {% if ann = @type.annotation Name %}\n            {%\n              name = (ann[0] || ann[:name])\n\n              unless name\n                ann.raise \"Class '#{@type}' is missing its name.\"\n              end\n            %}\n          {% end %}\n        {% end %}\n      end\n    end\n\n    Foo.default_name\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/2\"}, expected_error: \"oh noes im an error\"\n    {{ true ? raise(\"oh noes im an error\") : 0 }}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/2\"}\n    {{ true ? 1 : 0 }}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => \"1/2\"}\n    {% begin %}\n      {{true ? 1 : 0}} + {{2}}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/3\"}\n    {{ true ? 1 : x == 2 ? 2 : 3 }}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/3\"}\n    macro test(x)\n    {{ x == 1 ? 1 : x == 2 ? 2 : 3 }}\n    end\n\n    test(1)\n    test(2)\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"3/3\"}\n    macro test(x)\n    {{ x == 1 ? 1 : x == 2 ? 2 : 3 }}\n    end\n\n    test(1)\n    test(2)\n    test(3)\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"3/3\"}\n    macro test(x)\n      {{ x == 1 ? 1 : x == 2 ? 2 : 3 }}\n    end\n\n    test(1)\n    test(2)\n    test(3)\n    test(4)\n    CRYSTAL\n\n  # 1/2 since the raise would prevent the 2nd execution\n  assert_coverage <<-'CRYSTAL', {2 => \"1/2\"}, expected_error: \"oh noes im an error\"\n    macro test(x)\n      {{ 1 == x ? raise(\"oh noes im an error\") : 0 }}\n    end\n\n    test(1)\n    test(2)\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/2\"}, expected_error: \"oh noes im an error\"\n    macro test(x)\n      {{ 1 == x ? raise(\"oh noes im an error\") : 0 }}\n    end\n\n    test(2)\n    test(1)\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/2\"}\n    {% tags = (tags = (1 + 1)) ? tags : nil %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => \"1/2\", 3 => 3}\n    {% for type in [1, 2, 3] %}\n      {% tags = (tags = type) ? tags : nil %}\n      {% tags %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/2\"}\n    {% if true %}1{% else %}0{% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/3\"}\n    {% if false %}1{% elsif false %}2{% else %}3{% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/6\"}\n    {% if false %}{% if false %}1{% else %}2{% end %}{% elsif false %}3{% else %}{% if false %}4{% elsif false %}5{% else %}6{% end %}{% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/6\"}\n    {% if false; if false; 1; else 2; end; elsif false; 3; else; if false; 4; elsif false; 5; else 6; end; end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => \"1/5\"}\n    {% unless false; if false; 1; else 2; end; else; if false; 4; elsif false; 5; else 6; end; end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1}, expected_error: \"oh noes im an error\"\n    {% raise \"oh noes im an error\" %}\n    {{ 2 }}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => \"1/2\", 3 => 1}\n    {% 1 %}\n    {% 2 if false %}\n    {% 3 %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => \"1/2\", 3 => 1}\n    {% 1 %}\n    {% 2 if true %}\n    {% 3 %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => \"1/2\", 3 => 1}\n    {% 1 %}\n    {% 2 unless true %}\n    {% 3 %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => \"1/2\", 3 => 1}\n    {% 1 %}\n    {% 2 unless false %}\n    {% 3 %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"1/2\"}\n    macro test(v)\n      {{3 if v == 1}}\n    end\n\n    test 0\n    test 2\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/2\"}\n    macro test(v)\n      {{3 if v == 1}}\n    end\n\n    test 2\n    test 1\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/2\"}\n    macro test(v)\n      {{3 if v == 1}}\n    end\n\n    test 1\n    test 2\n    test 1\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => \"1/2\"}, expected_error: \"oh noes im an error\"\n    {%\n      if true\n        raise \"oh noes im an error\" if Int32 <= Number\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => \"1/2\"}\n    macro finished\n      {% verbatim do %}\n        {%\n          if true\n            a = 1 if true\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 1, 7 => 0}, expected_error: \"oh noes im an error\"\n    macro finished\n      {% verbatim do %}\n        {%\n          if true\n            raise \"oh noes im an error\"\n          else\n            123\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 0, 4 => 1}\n    {% unless true %}\n      {{0}}\n    {% else %}\n      {{1}}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 1, 4 => 0}\n    {% unless false %}\n      {{0}}\n    {% else %}\n      {{1}}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 1}\n    {%\n      a, b, c = {1, 2, 3}\n\n      a + b + c\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 2, 6 => 1, 10 => 1}\n    macro test(&)\n      {{yield}}\n    end\n\n    test do\n      {{2 + 1}}\n    end\n\n    test do\n      {{9 + 12}}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 2, 3 => \"1/2\", 4 => 2, 8 => 0, 13 => 0}\n    macro test(&)\n      {{ 1 + 1 }}\n      {{yield if false}}\n      {{ 2 + 2 }}\n    end\n\n    test do\n      {{2 + 1}}\n      {{1 + 2}}\n    end\n\n    test do\n      {{4 + 5}}\n      {{5 + 4}}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 2, 6 => 0, 8 => 2}\n    {% begin %}\n      {% for v in {1, 2, 3} %}\n        {% if v == 2 %}\n          {{v * 2}}\n        {% elsif v > 5 %}\n          {{v * 5}}\n        {% else %}\n          {{v}}\n        {% end %}\n      {% end %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 1, 4 => 2, 5 => 2, 7 => 2}\n    {% begin %}\n      {% for v in [1, 2] %}\n        {%\n          0 + (10 * 10)\n          20 * 20\n        %}\n        {% 30 * 30 %}\n      {% end %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 1, 4 => \"1/2\", 5 => 2, 7 => 2}\n    {% begin %}\n      {% for v in [1, 2] %}\n        {%\n          0 + (10 * 10) if false\n          20 * 20\n        %}\n        {% 30 * 30 %}\n      {% end %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          10 * 10\n          10 * 20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 3 => 0}\n    {% if false %}\n      # foo\n      {% 1 + 1 %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 1, 3 => 2, 4 => 1, 6 => 1}\n    {% begin %}\n      {% for vals in [[] of Int32, [1]] %}\n        {% if vals.empty? %}\n          {{1 + 1}}\n        {% else %}\n          {{2 + 2}}\n        {% end %}\n      {% end %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {3 => 1, 6 => 1, 7 => 1}\n    macro finished\n      {% verbatim do %}\n        {% 10 * 10 %}\n\n        {%\n          20 * 20\n          30 * 30\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 7 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          10 * 10\n\n          # Foo\n          10 * 20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 8 => 1, 10 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          10 * 10\n\n          # Foo\n\n          10 * 20\n\n          10 * 10\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 8 => 1, 9 => 1, 13 => 1, 16 => 1, 17 => 1, 22 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n          # Foo\n\n          20\n          30\n\n          # Bar\n\n          40\n        %}\n        {%\n          50\n          60\n        %}\n\n\n        {%\n          70\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 3, 6 => 1, 7 => 2, 8 => 1, 10 => 1, 13 => 3}\n    macro finished\n      {% verbatim do %}\n        {%\n          [0, 1, 2].each do |val|\n            str = if val >= 2\n                    \"greater or equal to 2\"\n                  elsif val == 1\n                    \"equals 1\"\n                  else\n                    \"other\"\n                  end\n\n            \"Got: \" + str\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          data = {__nil: nil}\n\n          data[\"foo\"] = {\n            id: 1, active: true,\n            name: \"foo\".upcase,\n            pie: 3.14,\n          }\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 1, 7 => 1, 8 => 1, 9 => 1, 10 => 1, 11 => 1, 12 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          data = {__nil: nil}\n          num = 4\n\n          data[\"foo\"] = {\n            var: num,\n            hash_literal: {} of Nil => Nil,\n            named_tuple_literal: {id: 10},\n            array_literal: [] of Nil,\n            tuple_literal: {1, 2, 3},\n          }\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {3 => 1}\n    macro finished\n      {% verbatim do %}\n        {% [1, 2, 3].find(&.+.==(2)) %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {3 => 1, 4 => 0}\n    macro finished\n      {% verbatim do %}\n        {% if false %}\n          {% raise \"Oh noes\" %}\n        {% end %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 7 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          if true\n            # Some comment\n            # Another comment\n            10\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 7 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          unless false\n            # Some comment\n            # Another comment\n            10\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 5 => 1}\n    {%\n      if true\n        # Some comment\n        # Another comment\n        10\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 5 => 1}\n    {%\n      unless false\n        # Some comment\n        # Another comment\n        10\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => \"1/2\"}\n    macro finished\n      {% verbatim do %}\n        {%\n          pp 1 if false\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 0}\n    macro test(v)\n      {% if v > 1 %}\n        {%\n          pp v.stringify\n        %}\n      {% end %}\n    end\n\n    test 1\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 0}\n    macro test(v)\n      {% if v > 1 %}\n        {%\n          val = v.stringify\n\n          pp val\n        %}\n      {% end %}\n    end\n\n    test 1\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 1, 6 => 1, 9 => 1, 10 => 1, 13 => 0}\n    macro test(v)\n      {% if v > 1 %}\n        {%\n          val = v.stringify\n\n          val = \"foo\"\n        %}\n\n        {% if v == 2 %}\n          {{v}}\n        {% else %}\n          {%\n            pp v * 2\n          %}\n        {% end %}\n      {% end %}\n    end\n\n    test 2\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 0}\n    {% for val in [] of Nil %}\n      {% pp 1 %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 0}\n    {% for val in {} of Nil => Nil %}\n      {% pp 1 %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {1 => 1, 2 => 0}\n    {% for val in (0...0) %}\n      {% pp 1 %}\n    {% end %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 0}\n    {%\n      ([] of Nil).each do |v|\n        pp v\n        pp 123\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 0}\n    {%\n      ([] of Nil).each do |(a, b, c)|\n        pp v\n        pp 123\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 0}\n    {%\n      ({} of Nil => Nil).each do |v|\n        pp v\n        pp 123\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 0}\n    {%\n      (0...0).each do |v|\n        pp v\n        pp 123\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 0}\n    {%\n      ([] of Nil).map do |v|\n        pp v\n        pp 123\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 0}\n    {%\n      ([] of Nil).find do |v|\n        v > 1\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"1/3\"}\n    {%\n      v = false || true || false\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/2\"}\n    {%\n      true && false\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"1/3\"}\n    {%\n      v = 1 || 2 || raise \"Oh noes\"\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"1/3\"}\n    macro test(one, two, three)\n      {{one || two || three}}\n    end\n\n    test true, false, false\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/3\"}\n    macro test(one, two, three)\n      {{one || two || three}}\n    end\n\n    test true, false, false\n    test false, true, false\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"3/3\"}\n    macro test(one, two, three)\n      {{one || two || three}}\n    end\n\n    test true, false, false\n    test false, true, false\n    test false, false, true\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"3/3\"}\n    {%\n      v = 1 && 2 && 3\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"3/3\"}, expected_error: \"oh noes im an error\"\n    {%\n      v = 1 && 2 && raise \"oh noes im an error\"\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 6 => 1, 10 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          ({\"a\" => \"b\"} of Nil => Nil).each do |k, v|\n            # stuff and things\n            k + v\n\n            # foo bar\n\n            k + v\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 6 => 1, 7 => 0, 11 => 1, 12 => 1, 15 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          a = nil\n\n          if false\n            a = 1\n\n            # Scalar value\n          else\n            a = 4\n            b = 5\n          end\n\n          a\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {5 => 1, 6 => 1, 7 => 1, 8 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          {\n            type:     String,\n            services: [4, 1, 12]\n              .sort_by { |v| v }\n              .map { |v| v },\n          }\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 1, 6 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n        val = \"foo\"\n                .strip\n                .strip.strip\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 6 => 1, 7 => 0, 9 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          if true &&\n              (\n                true ||\n                false\n              )\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 1, 6 => 0, 8 => 1, 9 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          if (\n              true ||\n              false\n            ) &&\n            true\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 5 => 1, 6 => 1, 8 => 0, 9 => 0}\n    macro finished\n      {% verbatim do %}\n        {%\n          if (\n              false ||\n              false\n            ) &&\n            true\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => \"1/2\", 5 => 1, 6 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          if (true || false) &&\n            true\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => \"2/2\", 5 => 0, 6 => 0}\n    macro finished\n      {% verbatim do %}\n        {%\n          if (true && false) &&\n            true\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 6 => 1, 7 => 0, 9 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          if (type = Number) &&\n              (\n                Int32 <= type ||\n                false\n              )\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {4 => 1, 6 => 1, 7 => 0, 9 => 1}\n    macro finished\n      {% verbatim do %}\n        {%\n          if (type = Number) &&\n              (\n                Int32 <= type ||\n                (type < Int && Int8 <= Number)\n              )\n            1\n          end\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 0, 7 => 0}\n    {%\n      if (type = nil) &&\n          (\n            Int32 <= type ||\n            (type < Int && Int8 <= Number)\n          )\n        id\n      end\n    %}\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 3 => 1, 5 => 0}\n    def default(type : T.class) forall T\n      {% if T.nilable? %}\n        {% 0 %}\n      {% else %}\n        {% 1 %}\n      {% end %}\n    end\n\n    default Int32?\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 2, 3 => 1, 5 => 1}\n    def default(type : T.class) forall T\n      {% if T.nilable? %}\n        {% 0 %}\n      {% else %}\n        {% 1 %}\n      {% end %}\n    end\n\n    default Int32\n    default Int32?\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 0}\n    def default(type : T.class) forall T\n      {% if T.nilable? %}\n        0\n      {% else %}\n        1\n      {% end %}\n    end\n\n    default Int32?\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 1, 4 => 1}\n    def default(type : T.class) forall T\n      {% if T.nilable? %}\n        0\n      {% else %}\n        1\n      {% end %}\n    end\n\n    default Int32\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 2, 4 => 1}\n    def default(type : T.class) forall T\n      {% if T.nilable? %}\n        0\n      {% else %}\n        1\n      {% end %}\n    end\n\n    default Int32\n    default Int32?\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => \"2/2\"}\n    def default(type : T.class) forall T\n      {% if T.nilable? %} 0 {% else %} 1 {% end %}\n    end\n\n    default Int32\n    default Int32?\n    CRYSTAL\n\n  assert_coverage <<-'CRYSTAL', {2 => 2, 3 => 1, 5 => 1}\n    macro test(val)\n      {% if val == 1 %}\n        %a = {{val.id}}\n      {% else %}\n        %a = {{val.id}}\n      {% end %}\n    end\n\n    test 1\n    test 2\n    CRYSTAL\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/playground_spec.cr",
    "content": "{% skip_file if flag?(:without_playground) %}\n\nrequire \"../../../spec_helper\"\n\nprivate def instrument(source)\n  ast = Parser.new(source).parse\n  instrumented = Playground::AgentInstrumentorTransformer.transform ast\n  instrumented.to_s\nend\n\nprivate def assert_agent(source, expected, *, file : String = __FILE__, line : Int32 = __LINE__)\n  # parse/to_s expected so block syntax and spaces do not bother\n  expected = Parser.new(expected).parse.to_s\n\n  instrument(source).should contain(expected), file: file, line: line\n\n  # whatever case should work before it should work with appended lines\n  instrument(\"#{source}\\n1\\n\").should contain(expected)\nend\n\nprivate def assert_agent_eq(source, expected)\n  # parse/to_s expected so block syntax and spaces do not bother\n  expected = Parser.new(expected).parse.to_s\n  instrument(source).should eq(expected)\nend\n\nclass Playground::Agent\n  @ws : HTTP::WebSocket | TestAgent::FakeSocket\nend\n\nprivate class TestAgent < Playground::Agent\n  class FakeSocket\n    property message\n\n    def send(@message : String)\n    end\n  end\n\n  def initialize(url, @tag : Int32)\n    @ws = @fake_socket = FakeSocket.new\n  end\n\n  def last_message\n    @fake_socket.message\n  end\nend\n\ndescribe Playground::Agent do\n  it \"should send json messages and return inspected value\" do\n    agent = TestAgent.new(\".\", 32)\n    agent.i(1) { 5 }.should eq(5)\n    agent.last_message.should eq(%({\"tag\":32,\"type\":\"value\",\"line\":1,\"value\":\"5\",\"html_value\":\"5\",\"value_type\":\"Int32\"}))\n    x, y = 3, 4\n    agent.i(1, [\"x\", \"y\"]) { {x, y} }.should eq({3, 4})\n    agent.last_message.should eq(%({\"tag\":32,\"type\":\"value\",\"line\":1,\"value\":\"{3, 4}\",\"html_value\":\"{3, 4}\",\"value_type\":\"Tuple(Int32, Int32)\",\"data\":{\"x\":\"3\",\"y\":\"4\"}}))\n  end\nend\n\ndescribe Playground::AgentInstrumentorTransformer do\n  it \"instrument literals\" do\n    assert_agent %(nil), %(_p.i(1) { nil })\n    assert_agent %(5), %(_p.i(1) { 5 })\n    assert_agent %(5.0), %(_p.i(1) { 5.0 })\n    assert_agent %(\"lorem\"), %(_p.i(1) { \"lorem\" })\n    assert_agent %(true), %(_p.i(1) { true })\n    assert_agent %('c'), %(_p.i(1) { 'c' })\n    assert_agent %(:foo), %(_p.i(1) { :foo })\n    assert_agent %([1, 2]), %(_p.i(1) { [1, 2] })\n    assert_agent %({} of Int32 => Int32), %(_p.i(1) { {} of Int32 => Int32 })\n    assert_agent %(/a/), %(_p.i(1) { /a/ })\n  end\n\n  it \"instrument literals with expression names\" do\n    assert_agent %({1, 2}), %(_p.i(1, [\"1\", \"2\"]) { {1, 2} })\n    assert_agent %({x, x + y}), %(_p.i(1, [\"x\", \"x + y\"]) { {x, x + y} })\n    assert_agent %(a = {x, x + y}), %(a = _p.i(1, [\"x\", \"x + y\"]) { {x, x + y} })\n  end\n\n  it \"instrument single variables expressions\" do\n    assert_agent %(x), %(_p.i(1) { x })\n  end\n\n  it \"instrument string interpolations\" do\n    assert_agent %(\"lorem \\#{a} \\#{b}\"), %(_p.i(1) { \"lorem \\#{a} \\#{b}\" })\n  end\n\n  it \"instrument assignments in the rhs\" do\n    assert_agent %(a = 4), %(a = _p.i(1) { 4 })\n  end\n\n  it \"do not instrument constants assignments\" do\n    assert_agent %(A = 4), %(A = 4)\n  end\n\n  it \"instrument not expressions\" do\n    assert_agent %(!true), %(_p.i(1) { !true })\n  end\n\n  it \"instrument binary expressions\" do\n    assert_agent %(a && b), %(_p.i(1) { a && b })\n    assert_agent %(a || b), %(_p.i(1) { a || b })\n  end\n\n  it \"instrument chained comparisons (#4663)\" do\n    assert_agent %(1 <= 2 <= 3), %(_p.i(1) { 1 <= 2 <= 3 })\n  end\n\n  it \"instrument unary expressions\" do\n    assert_agent %(pointerof(x)), %(_p.i(1) { pointerof(x) })\n  end\n\n  it \"instrument is_a? expressions\" do\n    assert_agent %(x.is_a?(Foo)), %(_p.i(1) { x.is_a?(Foo) })\n  end\n\n  it \"instrument ivar with obj\" do\n    assert_agent %(x.@foo), %(_p.i(1) { x.@foo })\n  end\n\n  it \"instrument multi assignments in the rhs\" do\n    assert_agent %(a, b = t), %(a, b = _p.i(1) { t })\n    assert_agent %(a, b = d, f), %(a, b = _p.i(1, [\"d\", \"f\"]) { {d, f} })\n    assert_agent %(a, b = {d, f}), %(a, b = _p.i(1, [\"d\", \"f\"]) { {d, f} })\n  end\n\n  it \"instrument puts with args\" do\n    assert_agent %(puts 3), %(puts(_p.i(1) { 3 }))\n    assert_agent %(puts a, 2, b), %(puts(*_p.i(1, [\"a\", \"2\", \"b\"]) { {a, 2, b} }))\n    assert_agent %(puts *{3}), %(puts(*_p.i(1, [\"3\"]) { {3} }))\n    assert_agent %(puts *{3,a}), %(puts(*_p.i(1, [\"3\", \"a\"]) { {3,a} }))\n    assert_agent_eq %(puts), %(puts)\n  end\n\n  it \"instrument print with args\" do\n    assert_agent %(print 3), %(print(_p.i(1) { 3 }))\n    assert_agent %(print a, 2, b), %(print(*_p.i(1, [\"a\", \"2\", \"b\"]) { {a, 2, b} }))\n    assert_agent_eq %(print), %(print)\n  end\n\n  it \"instrument single statement def\" do\n    assert_agent %(\n    def foo\n      4\n    end), <<-CRYSTAL\n    def foo\n      _p.i(3) { 4 }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument single statement var def\" do\n    assert_agent %(\n    def foo(x)\n      x\n    end), <<-CRYSTAL\n    def foo(x)\n      _p.i(3) { x }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument multi statement def\" do\n    assert_agent %(\n    def foo\n      2\n      6\n    end), <<-CRYSTAL\n    def foo\n      _p.i(3) { 2 }\n      _p.i(4) { 6 }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument returns inside def\" do\n    assert_agent %(\n    def foo\n      return 4\n    end), <<-CRYSTAL\n    def foo\n      return _p.i(3) { 4 }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument class defs\" do\n    assert_agent %(\n    class Foo\n      def initialize\n        @x = 3\n      end\n      def bar(x)\n        x = x + x\n        x\n      end\n      def self.bar(x, y)\n        x+y\n      end\n    end), <<-CRYSTAL\n    class Foo\n      def initialize\n        @x = _p.i(4) { 3 }.as(typeof(3))\n      end\n      def bar(x)\n        x = _p.i(7) { x + x }\n        _p.i(8) { x }\n      end\n      def self.bar(x, y)\n        _p.i(11) { x + y }\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"instrument instance variable and class variables reads and writes\" do\n    assert_agent %(\n    class Foo\n      def initialize\n        @x = 3\n        @@x = 4\n      end\n      def bar\n        @x\n      end\n      def self.bar\n        @@x\n      end\n    end), <<-CRYSTAL\n    class Foo\n      def initialize\n        @x = _p.i(4) { 3 }.as(typeof(3))\n        @@x = _p.i(5) { 4 }.as(typeof(4))\n      end\n      def bar\n        _p.i(8) { @x }\n      end\n      def self.bar\n        _p.i(11) { @@x }\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"do not instrument class initializing arguments\" do\n    assert_agent %(\n    class Foo\n      def initialize(@x, @y)\n        @z = @x + @y\n      end\n    end\n    ), <<-CRYSTAL\n    class Foo\n      def initialize(x, y)\n        @x = x\n        @y = y\n        @z = _p.i(4) { @x + @y }.as(typeof(@x + @y))\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"allow visibility modifiers\" do\n    assert_agent %(\n    class Foo\n      private def bar\n        1\n      end\n      protected def self.bar\n        2\n      end\n    end), <<-CRYSTAL\n    class Foo\n      private def bar\n        _p.i(4) { 1 }\n      end\n      protected def self.bar\n        _p.i(7) { 2 }\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"do not instrument macro calls in class\" do\n    assert_agent %(\n    class Foo\n      property foo\n    end), <<-CRYSTAL\n    class Foo\n      property foo\n    end\n    CRYSTAL\n  end\n\n  it \"instrument nested class defs\" do\n    assert_agent %(\n    class Bar\n      class Foo\n        def initialize\n          @x = 3\n        end\n      end\n    end), <<-CRYSTAL\n    class Bar\n      class Foo\n        def initialize\n          @x = _p.i(5) { 3 }.as(typeof(3))\n        end\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"do not instrument records class\" do\n    assert_agent %(\n    record Foo, x, y\n    ), <<-CRYSTAL\n    record Foo, x, y\n    CRYSTAL\n  end\n\n  it \"do not instrument top level macro calls\" do\n    assert_agent(<<-CRYSTAL, <<-CRYSTAL)\n    macro bar\n      def foo\n        4\n      end\n    end\n    bar\n    foo\n    CRYSTAL\n    macro bar\n      def foo\n        4\n      end\n    end\n    bar\n    _p.i(7) { foo }\n    CRYSTAL\n  end\n\n  it \"do not instrument class/module declared macro\" do\n    assert_agent(<<-CRYSTAL, <<-CRYSTAL)\n    module Bar\n      macro bar\n        4\n      end\n    end\n\n    class Foo\n      include Bar\n      def foo\n        bar\n        8\n      end\n    end\n    CRYSTAL\n    module Bar\n      macro bar\n        4\n      end\n    end\n\n    class Foo\n      include Bar\n      def foo\n        bar\n        _p.i(11) { 8 }\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"instrument inside modules\" do\n    assert_agent %(\n    module Bar\n      class Baz\n        class Foo\n          def initialize\n            @x = 3\n          end\n        end\n      end\n    end), <<-CRYSTAL\n    module Bar\n      class Baz\n        class Foo\n          def initialize\n            @x = _p.i(6) { 3 }.as(typeof(3))\n          end\n        end\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"instrument if statement\" do\n    assert_agent %(\n    if a\n      b\n    else\n      c\n    end\n    ), <<-CRYSTAL\n    if a\n      _p.i(3) { b }\n    else\n      _p.i(5) { c }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument unless statement\" do\n    assert_agent %(\n    unless a\n      b\n    else\n      c\n    end\n    ), <<-CRYSTAL\n    unless a\n      _p.i(3) { b }\n    else\n      _p.i(5) { c }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument while statement\" do\n    assert_agent %(\n    while a\n      b\n      c\n    end\n    ), <<-CRYSTAL\n    while a\n      _p.i(3) { b }\n      _p.i(4) { c }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument case statement\" do\n    # mind multi cond cases and non-cond cases before instrumenting single-cond cases\n    assert_agent %(\n    case a\n    when 0\n      b\n    when 1\n      c\n    else\n      d\n    end\n    ), <<-CRYSTAL\n    case a\n    when 0\n      _p.i(4) { b }\n    when 1\n      _p.i(6) { c }\n    else\n      _p.i(8) { d }\n    end\n    CRYSTAL\n  end\n\n  it \"instrument blocks and single yields\" do\n    assert_agent %(\n    def foo(x)\n      yield x\n    end\n    foo do |a|\n      a\n    end\n    ), <<-CRYSTAL\n    def foo(x)\n      yield _p.i(3) { x }\n    end\n    _p.i(5) do\n      foo do |a|\n        _p.i(6) { a }\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"instrument blocks and but non multi yields\" do\n    assert_agent %(\n    def foo(x)\n      yield x, 1\n    end\n    foo do |a, i|\n      a\n    end\n    ), <<-CRYSTAL\n    def foo(x)\n      yield x, 1\n    end\n    _p.i(5) do\n      foo do |a, i|\n        _p.i(6) { a }\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"instrument nested blocks unless in same line\" do\n    assert_agent %(\n    a = foo do\n      'a'\n      bar do\n        'b'\n      end\n      baz { 'c' }\n    end\n    ), <<-CRYSTAL\n    a = _p.i(2) do\n      foo do\n        _p.i(3) { 'a' }\n        _p.i(4) do\n          bar do\n            _p.i(5) { 'b' }\n          end\n        end\n        _p.i(7) do baz do 'c' end end\n      end\n    end\n    CRYSTAL\n  end\n\n  it \"instrument typeof\" do\n    assert_agent %(typeof(5)), %(_p.i(1) { typeof(5) })\n  end\n\n  it \"instrument exceptions\" do\n    assert_agent %(\n    begin\n      raise \"The exception\"\n    rescue ex : String\n      1\n    rescue\n      0\n    else\n      2\n    ensure\n      3\n    end\n    def foo(x)\n      raise \"Other\"\n    rescue\n      0\n    end\n    ), <<-CRYSTAL\n    begin\n      raise(_p.i(3) { \"The exception\" })\n    rescue ex : String\n      _p.i(5) { 1 }\n    rescue\n      _p.i(7) { 0 }\n    else\n      _p.i(9) { 2 }\n    ensure\n      _p.i(11) { 3 }\n    end\n    def foo(x)\n      begin\n        raise(_p.i(14) { \"Other\" })\n      rescue\n        _p.i(16) { 0 }\n      end\n    end\n    CRYSTAL\n  end\nend\n\nprivate def assert_compile(source)\n  sources = Playground::Session.instrument_and_prelude(\"\", \"\", 0, source)\n  compiler = Compiler.new\n  compiler.no_codegen = true\n  compiler.compile sources, \"fake-no-build\"\nend\n\ndescribe Playground::Session do\n  it { assert_compile %(puts \"1\") }\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/repl_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\n\nrequire \"../../../spec_helper\"\n\nprivate def success_value(result : Crystal::Repl::EvalResult) : Crystal::Repl::Value\n  result.warnings.infos.should be_empty\n  result.value.should_not be_nil\nend\n\ndescribe Crystal::Repl do\n  it \"can parse and evaluate snippets\" do\n    repl = Crystal::Repl.new\n    repl.prelude = \"primitives\"\n    repl.load_prelude\n\n    success_value(repl.parse_and_interpret(\"1 + 2\")).value.should eq(3)\n    success_value(repl.parse_and_interpret(\"def foo; 1 + 2; end\")).value.should be_nil\n    success_value(repl.parse_and_interpret(\"foo\")).value.should eq(3)\n  end\n\n  describe \"can return static and runtime type information for\" do\n    it \"Non Union\" do\n      repl = Crystal::Repl.new\n      repl.prelude = \"primitives\"\n      repl.load_prelude\n\n      repl_value = success_value(repl.parse_and_interpret(\"1\"))\n      repl_value.type.to_s.should eq(\"Int32\")\n      repl_value.runtime_type.to_s.should eq(\"Int32\")\n    end\n\n    it \"MixedUnionType\" do\n      repl = Crystal::Repl.new\n      repl.prelude = \"primitives\"\n      repl.load_prelude\n\n      repl_value = success_value(repl.parse_and_interpret(\"1 || \\\"a\\\"\"))\n      repl_value.type.to_s.should eq(\"(Int32 | String)\")\n      repl_value.runtime_type.to_s.should eq(\"Int32\")\n    end\n\n    it \"UnionType\" do\n      repl = Crystal::Repl.new\n      repl.prelude = \"primitives\"\n      repl.load_prelude\n\n      repl_value = success_value(repl.parse_and_interpret(\"true || 1\"))\n      repl_value.type.to_s.should eq(\"(Bool | Int32)\")\n      repl_value.runtime_type.to_s.should eq(\"Bool\")\n    end\n\n    it \"VirtualType\" do\n      repl = Crystal::Repl.new\n      repl.prelude = \"primitives\"\n      repl.load_prelude\n\n      repl.parse_and_interpret <<-CRYSTAL\n        class Foo\n        end\n\n        class Bar < Foo\n        end\n      CRYSTAL\n      repl_value = success_value(repl.parse_and_interpret(\"Bar.new || Foo.new\"))\n      repl_value.type.to_s.should eq(\"Foo+\") # Maybe should Foo to match typeof\n      repl_value.runtime_type.to_s.should eq(\"Bar\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/table_print_spec.cr",
    "content": "require \"spec\"\nrequire \"compiler/crystal/tools/table_print\"\n\nprivate def assert_table(expected, &)\n  actual = String::Builder.build do |builder|\n    Crystal::TablePrint.new(builder).build do |tp|\n      with tp yield\n    end\n  end\n\n  actual.should eq(expected[1..-1])\nend\n\ndescribe Crystal::TablePrint do\n  it \"single cell\" do\n    assert_table %(\n| A |) do\n      row do\n        cell \"A\"\n      end\n    end\n  end\n\n  it \"single row with separator\" do\n    assert_table %(\n| A | B |) do\n      row do\n        cell \"A\"\n        cell \"B\"\n      end\n    end\n  end\n\n  it \"multiple rows with separator\" do\n    assert_table %(\n| A | B |\n| C | D |) do\n      row do\n        cell \"A\"\n        cell \"B\"\n      end\n      row do\n        cell \"C\"\n        cell \"D\"\n      end\n    end\n  end\n\n  it \"rows with horizontal separators\" do\n    assert_table %(\n| A | B |\n---------\n| C | D |) do\n      row do\n        cell \"A\"\n        cell \"B\"\n      end\n      separator\n      row do\n        cell \"C\"\n        cell \"D\"\n      end\n    end\n  end\n\n  it \"aligns columns borders\" do\n    assert_table %(\n| A   | Foo |\n-------------\n| Bar | D   |) do\n      row do\n        cell \"A\"\n        cell \"Foo\"\n      end\n      separator\n      row do\n        cell \"Bar\"\n        cell \"D\"\n      end\n    end\n  end\n\n  it \"aligns cell content\" do\n    assert_table %(\n|  A  | Fooo |\n--------------\n| Bar |    D |) do\n      row do\n        cell \"A\", align: :center\n        cell \"Fooo\"\n      end\n      separator\n      row do\n        cell \"Bar\"\n        cell \"D\", align: :right\n      end\n    end\n  end\n\n  it \"colspan a cell that fits the available size\" do\n    assert_table %(\n|   A   |\n| B | C |) do\n      row do\n        cell \"A\", align: :center, colspan: 2\n      end\n      row do\n        cell \"B\"\n        cell \"C\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/tools/unreachable_spec.cr",
    "content": "require \"../../../spec_helper\"\ninclude Crystal\n\ndef processed_unreachable_visitor(code)\n  compiler = Compiler.new\n  compiler.prelude = \"empty\"\n  compiler.no_codegen = true\n  result = compiler.compile(Compiler::Source.new(\".\", code), \"fake-no-build\")\n\n  visitor = UnreachableVisitor.new\n  visitor.excludes << Path[Dir.current].to_posix.to_s\n  visitor.includes << \".\"\n\n  visitor.process(result)\nend\n\nprivate def assert_unreachable(code, file = __FILE__, line = __LINE__)\n  expected = Hash(String, Int32).new\n\n  code.lines.each_with_index do |line, line_number_0|\n    if match = line.match(/༓(?:\\[(\\d+)\\])?/)\n      location = Location.new(\".\", line_number_0 + 1, match.begin + 1)\n      expected[location.to_s] = (match[1]? || 0).to_i\n    end\n  end\n\n  code = code.gsub(/༓(?:\\[(\\d+)\\])?/, \"\")\n\n  tallies = processed_unreachable_visitor(code)\n\n  processed_results = [] of {Location, Int32}\n  tallies.each do |k, v|\n    location = k.location.not_nil!\n    next unless v.zero? || expected.has_key?(location.to_s)\n    processed_results << {location, v}\n  end\n\n  processed_results = processed_results.sort_by! do |loc, v|\n    {loc.filename.as(String), loc.line_number, loc.column_number}\n  end.map { |k, v| {k.to_s, v} }\n\n  processed_results.should eq(expected.to_a), file: file, line: line\nend\n\n# References\n#\n#   ༓ marks the expected unreachable code to be found\n#   ༓[n] marks the expected code to be called n times\n#\ndescribe \"unreachable\" do\n  it \"finds top level methods\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n        1\n      end\n\n      def bar\n        2\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"finds instance methods\" do\n    assert_unreachable <<-CRYSTAL\n      class Foo\n        ༓def foo\n          1\n        end\n\n        def bar\n          2\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"finds class methods\" do\n    assert_unreachable <<-CRYSTAL\n      class Foo\n        ༓def self.foo\n          1\n        end\n\n        def self.bar\n          2\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"finds instance methods in nested types\" do\n    assert_unreachable <<-CRYSTAL\n      module Mod\n        class Foo\n          ༓def foo\n            1\n          end\n\n          def bar\n            2\n          end\n        end\n      end\n\n      Mod::Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"handles circular hierarchy references (#14034)\" do\n    assert_unreachable <<-CRYSTAL\n      class Foo\n        alias Bar = Foo\n      end\n      CRYSTAL\n  end\n\n  it \"finds initializer\" do\n    assert_unreachable <<-CRYSTAL\n      class Foo\n        ༓def initialize\n        end\n      end\n\n      class Bar\n        def initialize\n        end\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"finds method with free variable\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo(u : U) forall U\n      end\n\n      def bar(u : U) forall U\n      end\n\n      bar(1)\n      CRYSTAL\n  end\n\n  it \"finds yielding methods\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n        yield\n      end\n\n      def bar\n        yield\n      end\n\n      bar {}\n      CRYSTAL\n  end\n\n  it \"finds method called from block\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      def bar\n      end\n\n      def baz\n        yield\n      end\n\n      baz do\n        bar\n      end\n      CRYSTAL\n  end\n\n  it \"finds method called from proc\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      def bar\n      end\n\n      def baz(&proc : ->)\n        proc.call\n      end\n\n      baz do\n        bar\n      end\n      CRYSTAL\n  end\n\n  it \"finds methods with proc parameter\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo(&proc : ->)\n        proc.call\n      end\n\n      def bar(&proc : ->)\n        proc.call\n      end\n\n      bar {}\n      CRYSTAL\n  end\n\n  it \"finds shadowed method\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      ༓def foo\n      end\n\n      ༓def bar\n      end\n\n      def bar\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"finds method with `previous_def`\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      ༓def foo\n        previous_def\n      end\n\n      def bar\n      end\n\n      def bar\n        previous_def\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"finds methods called from reachable code\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def qux_foo\n      end\n\n      ༓def foo\n        qux_foo\n      end\n\n      def qux_bar\n      end\n\n      def bar\n        qux_bar\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"finds method with `super`\" do\n    assert_unreachable <<-CRYSTAL\n      class Foo\n        ༓def foo\n        end\n\n        def bar\n        end\n      end\n\n      class Qux < Foo\n        ༓def foo\n          super\n        end\n\n        def bar\n          super\n        end\n      end\n\n      Qux.new.bar\n      CRYSTAL\n  end\n\n  it \"finds methods in generic type\" do\n    assert_unreachable <<-CRYSTAL\n      class Foo(T)\n        ༓def foo\n          1\n        end\n\n        def bar\n          2\n        end\n      end\n\n      Foo(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"finds method in abstract type\" do\n    assert_unreachable <<-CRYSTAL\n      abstract class Foo\n        ༓def foo\n        end\n\n        def bar\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      Baz.new.bar\n      CRYSTAL\n  end\n\n  # TODO: Should abstract Foo#bar be reported as well?\n  it \"finds abstract method\" do\n    assert_unreachable <<-CRYSTAL\n      abstract class Foo\n        abstract def foo\n\n        abstract def bar\n      end\n\n      class Baz < Foo\n        ༓def foo\n        end\n\n        def bar\n        end\n      end\n\n      Baz.new.bar\n      CRYSTAL\n  end\n\n  it \"finds virtual method\" do\n    assert_unreachable <<-CRYSTAL\n      abstract class Foo\n        ༓def foo\n        end\n\n        def bar\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      class Qux < Foo\n        ༓def foo\n        end\n\n        def bar\n        end\n      end\n\n      Baz.new.as(Baz | Qux).bar\n      CRYSTAL\n  end\n\n  it \"ignores autogenerated enum predicates\" do\n    assert_unreachable <<-CRYSTAL\n      enum Foo\n        BAR\n        BAZ\n\n        ༓def foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"finds method called from instance variable initializer\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      def bar\n        1\n      end\n\n      class Foo\n        @status = Bar.new\n        @other : Int32 = bar\n      end\n\n      class Bar\n        def initialize\n        end\n      end\n\n      Foo.new\n    CRYSTAL\n  end\n\n  it \"finds method called from expanded macro\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      def bar\n      end\n\n      macro bar_macro\n        bar\n      end\n\n      def go(&block)\n        block.call\n      end\n\n      go { bar_macro }\n      CRYSTAL\n  end\n\n  it \"finds method called from expanded macro expression\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n      end\n\n      def bar\n      end\n\n      {% begin %}\n        bar\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"tallies calls\" do\n    assert_unreachable <<-CRYSTAL\n      ༓def foo\n        1\n      end\n\n      ༓[2]def bar\n        2\n      end\n\n      ༓[1]def baz\n        bar\n      end\n\n      bar\n      baz\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal/types_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate def assert_type_to_s(expected, &)\n  p = Program.new\n  t = with p yield p\n  t.to_s.should eq(expected)\nend\n\ndescribe \"types to_s of\" do\n  it \"does for type contained in generic class\" do\n    result = semantic(<<-CRYSTAL)\n      class Bar(T)\n        class Foo\n        end\n      end\n      CRYSTAL\n    result.program.types[\"Bar\"].types[\"Foo\"].to_s.should eq(\"Bar::Foo\")\n  end\n\n  it \"does for type contained in generic module\" do\n    result = semantic(<<-CRYSTAL)\n      module Bar(T)\n        class Foo\n        end\n      end\n      CRYSTAL\n    result.program.types[\"Bar\"].types[\"Foo\"].to_s.should eq(\"Bar::Foo\")\n  end\n\n  it \"non-instantiated array\" do\n    assert_type_to_s \"Array(T)\" { array }\n  end\n\n  it \"array of simple types\" do\n    assert_type_to_s \"Array(Int32)\" { array_of(int32) }\n  end\n\n  it \"union of simple types\" do\n    assert_type_to_s \"(Int32 | String)\" { union_of(string, int32) }\n  end\n\n  it \"named tuple\" do\n    assert_type_to_s %(NamedTuple(a: Int32, \"b c\": String, \"+\": Char)) { named_tuple_of({\"a\" => int32, \"b c\" => string, \"+\" => char}) }\n  end\n\n  it \"nilable reference type\" do\n    assert_type_to_s \"(String | Nil)\" { nilable string }\n  end\n\n  it \"nilable value type\" do\n    assert_type_to_s \"(Int32 | Nil)\" { nilable int32 }\n  end\n\n  it \"nilable type with more than two elements, Nil at the end\" do\n    assert_type_to_s \"(Int32 | String | Nil)\" { union_of(string, int32, nil_type) }\n  end\n\n  describe \"union types\" do\n    describe \"should not have extra parens\" do\n      it \"in arrays\" do\n        assert_type_to_s \"Array(Int32 | String)\" { array_of(union_of(string, int32)) }\n      end\n\n      it \"in pointers\" do\n        assert_type_to_s \"Pointer(Int32 | String)\" { pointer_of(union_of(string, int32)) }\n      end\n\n      it \"in tuples\" do\n        assert_type_to_s \"Tuple(String, Int32 | String)\" { tuple_of [string, union_of(string, int32)] }\n      end\n    end\n\n    describe \"should have parens\" do\n      it \"as return type\" do\n        assert_type_to_s \"Proc((Int32 | String))\" { proc_of union_of(string, int32) }\n      end\n\n      it \"as arg type\" do\n        assert_type_to_s \"Proc((Int32 | String), Int32)\" { proc_of union_of(string, int32), int32 }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/crystal_path/crystal_path_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/env\"\nrequire \"spec/helpers/iterate\"\n\nprivate def assert_finds(search, results, relative_to = nil, path = File.expand_path(compiler_datapath(\"crystal_path\")), file = __FILE__, line = __LINE__)\n  it \"finds #{search.inspect}\", file, line do\n    results = results.map { |result| ::Path[path, result].normalize.to_s }\n    Dir.cd(File.expand_path(compiler_datapath(\"crystal_path\"))) do\n      crystal_path = Crystal::CrystalPath.new([path])\n      matches = crystal_path.find search, relative_to: relative_to\n      matches.should eq(results), file: file, line: line\n    end\n  end\nend\n\nprivate def assert_doesnt_find(search, relative_to = nil, path = File.expand_path(compiler_datapath(\"crystal_path\")), expected_relative_to = nil, file = __FILE__, line = __LINE__)\n  it \"doesn't finds #{search.inspect}\", file, line do\n    Dir.cd(File.expand_path(compiler_datapath(\"crystal_path\"))) do\n      crystal_path = Crystal::CrystalPath.new([path])\n      error = expect_raises Crystal::CrystalPath::NotFoundError do\n        crystal_path.find search, relative_to: relative_to\n      end\n      error.relative_to.should eq(expected_relative_to), file: file, line: line\n      error.filename.should eq(search), file: file, line: line\n    end\n  end\nend\n\ndescribe Crystal::CrystalPath do\n  assert_finds \"test_files/file_one.cr\", [\"test_files/file_one.cr\"]\n  assert_finds \"test_files/file_one\", [\"test_files/file_one.cr\"]\n  assert_finds \"test_files/*\", [\n    \"test_files/file_one.cr\",\n    \"test_files/file_two.cr\",\n  ]\n  assert_finds \"test_files/**\", [\n    \"test_files/file_one.cr\",\n    \"test_files/file_two.cr\",\n    \"test_files/src/file_three.cr\",\n    \"test_files/src/test_files.cr\",\n    \"test_files/src/test_files/file_four.cr\",\n    \"test_files/src/test_files/another/another.cr\",\n    \"test_files/src/yet_another/yet_another.cr\",\n    \"test_files/test_folder/file_three.cr\",\n    \"test_files/test_folder/test_folder.cr\",\n  ]\n  assert_finds \"./file_two.cr\", relative_to: \"test_files/file_one.cr\", results: [\n    \"test_files/file_two.cr\",\n  ]\n  assert_finds \"./test_folder/file_three.cr\", relative_to: \"test_files/file_one.cr\", results: [\n    \"test_files/test_folder/file_three.cr\",\n  ]\n  assert_finds \"./test_folder/*\", relative_to: \"test_files/file_one.cr\", results: [\n    \"test_files/test_folder/file_three.cr\",\n    \"test_files/test_folder/test_folder.cr\",\n  ]\n  assert_finds \"../**\", relative_to: \"test_files/test_folder/file_three.cr\", results: [\n    \"test_files/file_one.cr\",\n    \"test_files/file_two.cr\",\n    \"test_files/src/file_three.cr\",\n    \"test_files/src/test_files.cr\",\n    \"test_files/src/test_files/file_four.cr\",\n    \"test_files/src/test_files/another/another.cr\",\n    \"test_files/src/yet_another/yet_another.cr\",\n    \"test_files/test_folder/file_three.cr\",\n    \"test_files/test_folder/test_folder.cr\",\n  ]\n  assert_finds \"../test_folder\", relative_to: \"test_files/test_folder/file_three.cr\", results: [\n    \"test_files/test_folder/test_folder.cr\",\n  ]\n  assert_finds \"foo.cr\", results: [\n    \"foo.cr/foo.cr\",\n  ]\n\n  # For `require \"foo\"`:\n  # 1. foo.cr (to find something in the standard library)\n  assert_finds \"simple\", [\"simple.cr\"]\n  # 2. foo/src/foo.cr (to find something in a shard)\n  assert_finds \"test_files\", [\"test_files/src/test_files.cr\"]\n\n  # For `require \"foo/bar\"`:\n  # 1. foo/bar.cr (to find something in the standard library)\n  assert_finds \"test_files/file_one\", [\"test_files/file_one.cr\"]\n  # 2. foo/src/bar.cr (to find something in a shard, non-namespaced structure)\n  assert_finds \"test_files/file_three\", [\"test_files/src/file_three.cr\"]\n  # 3. foo/src/foo/bar.cr (to find something in a shard, namespaced structure)\n  assert_finds \"test_files/file_four\", [\"test_files/src/test_files/file_four.cr\"]\n\n  # Nested searches\n  # a/1. foo.cr (to find something in the standard library (nested))\n  assert_finds \"other_test_files\", [\"other_test_files/other_test_files.cr\"]\n  # b/2. foo/src/bar/bar.cr (to find something in a shard, non-namespaced structure, nested)\n  assert_finds \"test_files/yet_another\", [\"test_files/src/yet_another/yet_another.cr\"]\n  # b/3. foo/src/foo/bar/bar.cr (to find something in a shard, namespaced structure, nested)\n  assert_finds \"test_files/another\", [\"test_files/src/test_files/another/another.cr\"]\n\n  assert_doesnt_find \"file_two.cr\"\n  assert_doesnt_find \"test_folder/file_three.cr\"\n  assert_doesnt_find \"test_folder/*\", relative_to: Path[__DIR__, \"test_files\", \"file_one.cr\"].to_s, expected_relative_to: Path[__DIR__, \"test_files\"].to_s\n  assert_doesnt_find \"test_files/missing_file.cr\"\n  assert_doesnt_find __FILE__[1..-1], path: \":\"\n\n  # Don't find in CRYSTAL_PATH if the path is relative (#4742)\n  assert_doesnt_find \"./crystal_path_spec\", relative_to: Path[\"test_files\", \"file_one.cr\"].to_s, expected_relative_to: Path[\"test_files\"].to_s\n  assert_doesnt_find \"./crystal_path_spec.cr\", relative_to: Path[\"test_files\", \"file_one.cr\"].to_s, expected_relative_to: Path[\"test_files\"].to_s\n  assert_doesnt_find \"../crystal_path/test_files/file_one\"\n\n  # Don't find relative filenames in src or shards\n  assert_doesnt_find \"../../src/file_three\", relative_to: Path[\"test_files\", \"test_folder\", \"test_folder.cr\"].to_s, expected_relative_to: Path[\"test_files\", \"test_folder\"].to_s\n\n  describe \"#each_file_expansion\" do\n    path = Crystal::CrystalPath.new\n\n    it \"foo.cr\" do\n      assert_iterates_yielding [\n        \"x/foo.cr\",\n        \"x/foo.cr/foo.cr\",\n        \"x/foo.cr/src/foo.cr\",\n      ], path.each_file_expansion(\"foo.cr\", \"x\")\n    end\n\n    it \"foo.cr/bar\" do\n      assert_iterates_yielding [\n        \"x/foo.cr/bar.cr\",\n        \"x/foo.cr/src/bar.cr\",\n        \"x/foo.cr/src/foo.cr/bar.cr\",\n        \"x/foo.cr/bar/bar.cr\",\n        \"x/foo.cr/src/bar/bar.cr\",\n        \"x/foo.cr/src/foo.cr/bar/bar.cr\",\n      ], path.each_file_expansion(\"foo.cr/bar\", \"x\")\n    end\n\n    it \"foo.cr/bar.cr\" do\n      assert_iterates_yielding [\n        \"x/foo.cr/bar.cr\",\n        \"x/foo.cr/src/bar.cr\",\n        \"x/foo.cr/src/foo.cr/bar.cr\",\n        \"x/foo.cr/bar.cr/bar.cr\",\n        \"x/foo.cr/src/bar.cr/bar.cr\",\n        \"x/foo.cr/src/foo.cr/bar.cr/bar.cr\",\n      ], path.each_file_expansion(\"foo.cr/bar.cr\", \"x\")\n    end\n\n    it \"foo\" do\n      assert_iterates_yielding [\n        \"x/foo.cr\",\n        \"x/foo/foo.cr\",\n        \"x/foo/src/foo.cr\",\n      ], path.each_file_expansion(\"foo\", \"x\")\n    end\n\n    it \"./foo\" do\n      assert_iterates_yielding [\n        \"x/./foo.cr\",\n        \"x/./foo/foo.cr\",\n      ], path.each_file_expansion(\"./foo\", \"x\")\n    end\n\n    it \"./foo.cr\" do\n      assert_iterates_yielding [\n        \"x/./foo.cr\",\n        \"x/./foo.cr/foo.cr\",\n      ], path.each_file_expansion(\"./foo.cr\", \"x\")\n    end\n\n    it \"foo/bar\" do\n      assert_iterates_yielding [\n        \"x/foo/bar.cr\",\n        \"x/foo/src/bar.cr\",\n        \"x/foo/src/foo/bar.cr\",\n        \"x/foo/bar/bar.cr\",\n        \"x/foo/src/bar/bar.cr\",\n        \"x/foo/src/foo/bar/bar.cr\",\n      ], path.each_file_expansion(\"foo/bar\", \"x\")\n    end\n\n    it \"./foo/bar\" do\n      assert_iterates_yielding [\n        \"x/./foo/bar.cr\",\n        \"x/./foo/bar/bar.cr\",\n      ], path.each_file_expansion(\"./foo/bar\", \"x\")\n    end\n\n    it \"foo/bar/baz\" do\n      assert_iterates_yielding [\n        \"x/foo/bar/baz.cr\",\n        \"x/foo/src/bar/baz.cr\",\n        \"x/foo/src/foo/bar/baz.cr\",\n        \"x/foo/bar/baz/baz.cr\",\n        \"x/foo/src/bar/baz/bar/baz.cr\",\n        \"x/foo/src/foo/bar/baz/bar/baz.cr\",\n      ], path.each_file_expansion(\"foo/bar/baz\", \"x\")\n    end\n\n    it \"./foo/bar/baz\" do\n      assert_iterates_yielding [\n        \"x/./foo/bar/baz.cr\",\n        \"x/./foo/bar/baz/baz.cr\",\n      ], path.each_file_expansion(\"./foo/bar/baz\", \"x\")\n    end\n  end\n\n  it \"includes 'lib' by default\" do\n    with_env(\"CRYSTAL_PATH\": nil) do\n      crystal_path = Crystal::CrystalPath.new\n      crystal_path.entries[0].should eq(\"lib\")\n    end\n  end\n\n  it \"overrides path with environment variable\" do\n    with_env(\"CRYSTAL_PATH\": \"foo#{Process::PATH_DELIMITER}bar\") do\n      crystal_path = Crystal::CrystalPath.new\n      crystal_path.entries.should eq(%w(foo bar))\n    end\n  end\n\n  it \".expand_paths\" do\n    paths = [\"$ORIGIN/../foo\"]\n    Crystal::CrystalPath.expand_paths(paths, \"/usr/bin/\")\n    paths.should eq [\"/usr/bin/../foo\"]\n    paths = [\"./$ORIGIN/../foo\"]\n    Crystal::CrystalPath.expand_paths(paths, \"/usr/bin/\")\n    paths.should eq [\"./$ORIGIN/../foo\"]\n    paths = [\"$ORIGINfoo\"]\n    Crystal::CrystalPath.expand_paths(paths, \"/usr/bin/\")\n    paths.should eq [\"$ORIGINfoo\"]\n    paths = [\"lib\", \"$ORIGIN/../foo\"]\n    Crystal::CrystalPath.expand_paths(paths, \"/usr/bin/\")\n    paths.should eq [\"lib\", \"/usr/bin/../foo\"]\n\n    paths = [\"$ORIGIN/../foo\"]\n    expect_raises(Exception, \"Missing executable path to expand $ORIGIN path\") do\n      Crystal::CrystalPath.expand_paths(paths, nil)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/data/args_test",
    "content": "# Last argument is file path to write\ntest_path = ARGV.pop\nFile.open(test_path, \"w\") do |f|\n  ARGV.inspect(f)\n  f.puts\n  {other_flag: {{flag?(:other_flag)}}}.inspect(f)\nend\n"
  },
  {
    "path": "spec/compiler/data/build",
    "content": "puts \"this file is never compiled (#1412)\"\n"
  },
  {
    "path": "spec/compiler/data/compiler_sample",
    "content": "class Foo\n  def self.foo\n    print \"He\"\n  end\nend\n\n\nclass FOO\n  def self.foo\n    print \"llo!\"\n  end\nend\n\nFoo.foo\nFOO.foo\n"
  },
  {
    "path": "spec/compiler/data/crystal_path/foo.cr/foo.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/other_test_files/other_test_files.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/simple.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/file_one.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/file_two.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/src/file_three.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/src/test_files/another/another.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/src/test_files/file_four.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/src/test_files.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/src/yet_another/yet_another.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/test_folder/file_three.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/test_folder/not_a_crystal_file.txt",
    "content": ""
  },
  {
    "path": "spec/compiler/data/crystal_path/test_files/test_folder/test_folder.cr",
    "content": ""
  },
  {
    "path": "spec/compiler/data/ffi/sum.c",
    "content": "#include <stdarg.h>\n#include <stdint.h>\n#include \"../visibility.h\"\n\n// all the integral return types must be at least as large as the register size\n// to avoid integer promotion by FFI!\n\nEXPORT int64_t answer()\n{\n    return 42;\n}\n\nEXPORT int64_t sum(int32_t a, int32_t b, int32_t c)\n{\n    return a + b + c;\n}\n\nEXPORT void sum_primitive_types(\n    uint8_t a, int8_t b,\n    uint16_t c, int16_t d,\n    uint32_t e, int32_t f,\n    uint64_t g, int64_t h,\n    float i, double j,\n    int64_t *k)\n{\n    *k = a + b + c + d + e + f + g + h + (int64_t)i + (int64_t)j + *k;\n}\n\nstruct test_struct\n{\n    int8_t b;\n    int16_t s;\n    int32_t i;\n    int64_t j;\n    float f;\n    double d;\n    void *p;\n};\n\nEXPORT int64_t sum_struct(struct test_struct s)\n{\n    int64_t *p = (int64_t *)s.p;\n    *p = s.b + s.s + s.i + s.j + s.f + s.d + *p;\n    return *p;\n}\n\nEXPORT int64_t sum_array(int32_t ary[4])\n{\n    int64_t sum = 0;\n    for (int32_t i = 0; i < 4; i++)\n    {\n        sum += ary[i];\n    }\n    return sum;\n}\n\nEXPORT int64_t sum_variadic(int32_t count, ...)\n{\n    va_list ap;\n    int32_t j;\n    int64_t sum = 0;\n\n    va_start(ap, count); /* Requires the last fixed parameter (to get the address) */\n    for (j = 0; j < count; j++)\n    {\n        sum += va_arg(ap, int32_t); /* Increments ap to the next argument. */\n    }\n    va_end(ap);\n\n    return sum;\n}\n\nEXPORT struct test_struct make_struct(int8_t b, int16_t s, int32_t i, int64_t j, float f, double d, void *p)\n{\n    struct test_struct t;\n    t.b = b;\n    t.s = s;\n    t.i = i;\n    t.j = j;\n    t.f = f;\n    t.d = d;\n    t.p = p;\n    return t;\n}\n"
  },
  {
    "path": "spec/compiler/data/interpreter/sum.c",
    "content": "#include <stdarg.h>\n#include \"../visibility.h\"\n\nEXPORT float sum_float(int count, ...) {\n  va_list args;\n  float total = 0;\n\n  va_start(args, count);\n  for (int i = 0; i < count; i++) {\n    total += va_arg(args, double);\n  }\n  va_end(args);\n\n  return total;\n}\n\nEXPORT long sum_int(int count, ...) {\n  va_list args;\n  long total = 0;\n\n  va_start(args, count);\n  for (int i = 0; i < count; i++) {\n    total += va_arg(args, long);\n  }\n  va_end(args);\n\n  return total;\n}\n\nEXPORT int simple_sum_int(int a, int b) {\n  return a + b;\n}\n"
  },
  {
    "path": "spec/compiler/data/loader/bar.c",
    "content": "#include \"../visibility.h\"\n\nLOCAL int foo() {\n  return 42;\n}\n\nEXPORT int bar() {\n  return foo() + 100;\n}\n"
  },
  {
    "path": "spec/compiler/data/loader/foo.c",
    "content": "#include \"../visibility.h\"\n\nEXPORT int foo() {\n  return 12;\n}\n"
  },
  {
    "path": "spec/compiler/data/loader/foo2.c",
    "content": "#include \"../visibility.h\"\n\nLOCAL int a() {\n  return 41;\n}\n\nEXPORT int foo() {\n  return 42;\n}\n"
  },
  {
    "path": "spec/compiler/data/visibility.h",
    "content": "#ifdef _WIN32\n  #define EXPORT __declspec(dllexport)\n  #define LOCAL\n#else\n  #define EXPORT __attribute__ ((visibility (\"default\")))\n  #define LOCAL __attribute__ ((visibility (\"hidden\")))\n#endif\n"
  },
  {
    "path": "spec/compiler/ffi/ffi_spec.cr",
    "content": "{% skip_file if flag?(:without_ffi) || flag?(:wasm32) %}\n{% skip_file unless flag?(:unix) || flag?(:win32) %}\n\nrequire \"../spec_helper\"\nrequire \"compiler/crystal/ffi\"\nrequire \"compiler/crystal/loader\"\nrequire \"../loader/spec_helper\"\nrequire \"../../support/env\"\n\n# all the integral return types must be at least as large as the register size\n# to avoid integer promotion by FFI!\n\n@[Extern]\nprivate record TestStruct,\n  b : Int8,\n  s : Int16,\n  i : Int32,\n  j : Int64,\n  f : Float32,\n  d : Float64,\n  p : Pointer(Void)\n\nprivate def dll_search_paths\n  {% if flag?(:msvc) %}\n    [SPEC_CRYSTAL_LOADER_LIB_PATH]\n  {% else %}\n    nil\n  {% end %}\nend\n\n{% if flag?(:unix) || (flag?(:win32) && flag?(:gnu)) %}\n  class Crystal::Loader\n    def self.new(search_paths : Array(String), *, dll_search_paths : Nil)\n      new(search_paths)\n    end\n  end\n{% end %}\n\ndescribe Crystal::FFI::CallInterface do\n  around_all do |example|\n    FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n    build_c_dynlib(compiler_datapath(\"ffi\", \"sum.c\"))\n\n    {% if flag?(:win32) && flag?(:gnu) %}\n      with_env({\"PATH\" => \"#{SPEC_CRYSTAL_LOADER_LIB_PATH}#{Process::PATH_DELIMITER}#{ENV[\"PATH\"]}\"}) do\n        example.run\n      end\n    {% else %}\n      example.run\n    {% end %}\n\n    FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n  end\n\n  describe \".new\" do\n    it \"simple call\" do\n      call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [] of Crystal::FFI::Type\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"answer\")\n      return_value = 0_i64\n      call_interface.call(function_pointer, Pointer(Pointer(Void)).null, pointerof(return_value).as(Void*))\n      return_value.should eq 42_i64\n    ensure\n      loader.try &.close_all\n    end\n\n    it \"with args\" do\n      call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [\n        Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32,\n      ] of Crystal::FFI::Type\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"sum\")\n\n      return_value = 0_i64\n      args = Int32[1, 3, 5]\n      arg_pointers = StaticArray(Pointer(Void), 3).new { |i| (args.to_unsafe + i).as(Void*) }\n      call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))\n      return_value.should eq 9_i64\n    ensure\n      loader.try &.close_all\n    end\n\n    it \"all primitive arg types\" do\n      call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.void, [\n        Crystal::FFI::Type.uint8, Crystal::FFI::Type.sint8,\n        Crystal::FFI::Type.uint16, Crystal::FFI::Type.sint16,\n        Crystal::FFI::Type.uint32, Crystal::FFI::Type.sint32,\n        Crystal::FFI::Type.uint64, Crystal::FFI::Type.sint64,\n        Crystal::FFI::Type.float, Crystal::FFI::Type.double,\n        Crystal::FFI::Type.pointer,\n      ] of Crystal::FFI::Type\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"sum_primitive_types\")\n\n      pointer_value = 11_i64\n      arg_pointers = StaticArray[\n        Pointer(UInt8).malloc(1, 1).as(Void*),\n        Pointer(Int8).malloc(1, 2).as(Void*),\n        Pointer(UInt16).malloc(1, 3).as(Void*),\n        Pointer(Int16).malloc(1, 4).as(Void*),\n        Pointer(UInt32).malloc(1, 5).as(Void*),\n        Pointer(Int32).malloc(1, 6).as(Void*),\n        Pointer(UInt64).malloc(1, 7).as(Void*),\n        Pointer(Int64).malloc(1, 8).as(Void*),\n        Pointer(Float32).malloc(1, 9.0).as(Void*),\n        Pointer(Float64).malloc(1, 10.0).as(Void*),\n        Pointer(Int64*).malloc(1, pointerof(pointer_value)).as(Void*),\n        Pointer(Void).null,\n      ]\n\n      call_interface.call(function_pointer, arg_pointers.to_unsafe, Pointer(Void).null)\n      pointer_value.should eq 66\n    ensure\n      loader.try &.close_all\n    end\n\n    it \"make struct\" do\n      struct_fields = [\n        Crystal::FFI::Type.sint8,\n        Crystal::FFI::Type.sint16,\n        Crystal::FFI::Type.sint32,\n        Crystal::FFI::Type.sint64,\n        Crystal::FFI::Type.float,\n        Crystal::FFI::Type.double,\n        Crystal::FFI::Type.pointer,\n      ]\n      call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.struct(struct_fields), struct_fields\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"make_struct\")\n\n      pointer_value = \"foobar\"\n      arg_pointers = StaticArray[\n        Pointer(Int8).malloc(1, 2).as(Void*),\n        Pointer(Int16).malloc(1, 4).as(Void*),\n        Pointer(Int32).malloc(1, 6).as(Void*),\n        Pointer(Int64).malloc(1, 8).as(Void*),\n        Pointer(Float32).malloc(1, 9.0).as(Void*),\n        Pointer(Float64).malloc(1, 10.0).as(Void*),\n        Pointer(UInt8*).malloc(1, pointer_value.to_unsafe).as(Void*),\n        Pointer(Void).null,\n      ]\n\n      return_value = uninitialized TestStruct\n      call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))\n      return_value.should eq TestStruct.new b: 2, s: 4, i: 6, j: 8, f: 9.0, d: 10.0, p: pointer_value.to_unsafe.as(Void*)\n    ensure\n      loader.try &.close_all\n    end\n\n    it \"sum struct\" do\n      call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [\n        Crystal::FFI::Type.struct([\n          Crystal::FFI::Type.sint8,\n          Crystal::FFI::Type.sint16,\n          Crystal::FFI::Type.sint32,\n          Crystal::FFI::Type.sint64,\n          Crystal::FFI::Type.float,\n          Crystal::FFI::Type.double,\n          Crystal::FFI::Type.pointer,\n        ]),\n      ] of Crystal::FFI::Type\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"sum_struct\")\n\n      pointer_value = 11_i64\n      arg_pointers = StaticArray[\n        Pointer(TestStruct).malloc(1, TestStruct.new(\n          b: 2,\n          s: 4,\n          i: 6,\n          j: 8,\n          f: 9.0,\n          d: 10.0,\n          p: pointerof(pointer_value).as(Void*)\n        )).as(Void*),\n        Pointer(Void).null,\n      ]\n\n      return_value = 0_i64\n      call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))\n      return_value.should eq 50_i64\n      pointer_value.should eq 50_i64\n    ensure\n      loader.try &.close_all\n    end\n\n    # passing C array by value is not supported everywhere\n    {% unless flag?(:win32) %}\n      it \"array\" do\n        call_interface = Crystal::FFI::CallInterface.new Crystal::FFI::Type.sint64, [\n          Crystal::FFI::Type.struct([\n            Crystal::FFI::Type.sint32,\n            Crystal::FFI::Type.sint32,\n            Crystal::FFI::Type.sint32,\n            Crystal::FFI::Type.sint32,\n          ]),\n        ] of Crystal::FFI::Type\n\n        loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n        loader.load_library \"sum\"\n        function_pointer = loader.find_symbol(\"sum_array\")\n\n        return_value = 0_i64\n\n        ary = [1, 2, 3, 4]\n\n        arg_pointers = StaticArray[\n          Pointer.malloc(1, ary.to_unsafe).as(Void*),\n          Pointer(Void).null,\n        ]\n\n        call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))\n        return_value.should eq 10_i64\n      ensure\n        loader.try &.close_all\n      end\n    {% end %}\n  end\n\n  describe \".variadic\" do\n    it \"basic\" do\n      call_interface = Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32, Crystal::FFI::Type.sint32] of Crystal::FFI::Type, 1\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"sum_variadic\")\n\n      return_value = 0_i64\n      args = Int32[3, 1, 3, 5]\n      arg_pointers = StaticArray(Pointer(Void), 4).new { |i| (args.to_unsafe + i).as(Void*) }\n      call_interface.call(function_pointer, arg_pointers.to_unsafe, pointerof(return_value).as(Void*))\n      return_value.should eq 9_i64\n    ensure\n      loader.try &.close_all\n    end\n\n    it \"zero varargs\" do\n      call_interface = Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [Crystal::FFI::Type.sint32] of Crystal::FFI::Type, 1\n\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: dll_search_paths)\n      loader.load_library \"sum\"\n      function_pointer = loader.find_symbol(\"sum_variadic\")\n\n      return_value = 1_i64\n      count = 0_i32\n      arg_pointer = pointerof(count).as(Void*)\n      call_interface.call(function_pointer, pointerof(arg_pointer), pointerof(return_value).as(Void*))\n      return_value.should eq 0_i64\n    ensure\n      loader.try &.close_all\n    end\n\n    it \"validates args size\" do\n      expect_raises Exception, \"invalid value for fixed_args\" do\n        Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [] of Crystal::FFI::Type, 1\n      end\n      expect_raises Exception, \"invalid value for fixed_args\" do\n        Crystal::FFI::CallInterface.variadic Crystal::FFI::Type.sint64, [] of Crystal::FFI::Type, -1\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/formatter/formatter_spec.cr",
    "content": "require \"spec\"\nrequire \"../../../src/compiler/crystal/formatter\"\n\nprivate def assert_format(input, output = input, strict = false, flags = nil, file = __FILE__, line = __LINE__, focus = false)\n  it \"formats #{input.inspect}\", file, line, focus: focus do\n    output = \"#{output}\\n\" unless strict\n    result = Crystal.format(input, flags: flags)\n    unless result == output\n      message = <<-ERROR\n        Expected\n\n        ~~~\n        #{input}\n        ~~~\n\n        to format to:\n\n        ~~~\n        #{output}\n        ~~~\n\n        but got:\n\n        ~~~\n        #{result}\n        ~~~\n\n          assert_format #{input.inspect}, #{result.chomp.inspect}\n        ERROR\n\n      fail message, file: file, line: line\n    end\n\n    # Check idempotency\n    result2 = Crystal.format(result, flags: flags)\n    unless result == result2\n      fail \"Idempotency failed:\\nBefore: #{result.inspect}\\nAfter:  #{result2.inspect}\", file: file, line: line\n    end\n  end\nend\n\ndescribe Crystal::Formatter do\n  assert_format \"\", \"\", strict: true\n\n  assert_format \"nil\"\n\n  assert_format \"true\"\n  assert_format \"false\"\n\n  assert_format \"'\\\\n'\"\n  assert_format \"'a'\"\n  assert_format \"'\\\\u{0123}'\"\n\n  assert_format \":foo\"\n  assert_format \":\\\"foo\\\"\"\n\n  assert_format \"()\"\n  assert_format \"(())\"\n\n  assert_format \"1\"\n  assert_format \"1   ;    2\", \"1; 2\"\n  assert_format \"1   ;\\n    2\", \"1\\n2\"\n  assert_format \"1\\n\\n2\", \"1\\n\\n2\"\n  assert_format \"1\\n\\n\\n2\", \"1\\n\\n2\"\n  assert_format \"1_234\", \"1_234\"\n  assert_format \"0x1234_u32\", \"0x1234_u32\"\n  assert_format \"0_u64\", \"0_u64\"\n  assert_format \"0u64\", \"0u64\"\n  assert_format \"0i64\", \"0i64\"\n\n  assert_format \"   1\", \"1\"\n  assert_format \"\\n\\n1\", \"1\"\n  assert_format \"\\n# hello\\n1\", \"# hello\\n1\"\n  assert_format \"\\n# hello\\n\\n1\", \"# hello\\n\\n1\"\n  assert_format \"\\n# hello\\n\\n\\n1\", \"# hello\\n\\n1\"\n  assert_format \"\\n   # hello\\n\\n1\", \"# hello\\n\\n1\"\n\n  assert_format %(\"hello\")\n  assert_format %(%(hello))\n  assert_format %(%<hello>)\n  assert_format %(%[hello])\n  assert_format %(%{hello})\n  assert_format %(\"hel\\\\nlo\")\n  assert_format %(\"hel\\nlo\")\n\n  assert_format \"[] of Foo\"\n  assert_format \"[\\n]   of   \\n   Foo  \", \"[] of Foo\"\n  assert_format \"[1, 2, 3]\"\n  assert_format \"[1, 2, 3] of Foo\"\n  assert_format \"  [   1  ,    2  ,    3  ]  \", \"[1, 2, 3]\"\n  assert_format \"[1, 2, 3,  ]\", \"[1, 2, 3]\"\n  assert_format \"[1,\\n2,\\n3]\", \"[1,\\n 2,\\n 3]\"\n  assert_format \"[1,\\n2,\\n3\\n]\", \"[1,\\n 2,\\n 3,\\n]\"\n  assert_format \"[\\n1,\\n2,\\n3]\", \"[\\n  1,\\n  2,\\n  3,\\n]\"\n  assert_format \"[\\n  [\\n    1,\\n  ], [\\n    2,\\n  ], [\\n    3,\\n  ],\\n]\"\n  assert_format \"[\\n  {\\n    1 => 2,\\n  }, {\\n    3 => 4,\\n  }, {\\n    5 => 6,\\n  },\\n]\"\n  assert_format \"if 1\\n[   1  ,    2  ,    3  ]\\nend\", \"if 1\\n  [1, 2, 3]\\nend\"\n  assert_format \"    [   1,   \\n   2   ,   \\n   3   ]   \", \"[1,\\n 2,\\n 3]\"\n  assert_format \"Set { 1 , 2 }\", \"Set{1, 2}\"\n  assert_format \"[\\n1,\\n\\n2]\", \"[\\n  1,\\n\\n  2,\\n]\"\n  assert_format \"[ # foo\\n  1,\\n]\"\n  assert_format \"Set{ # foo\\n  1,\\n}\"\n  assert_format \"begin\\n  array[\\n    0 # Zero\\n  ]\\nend\"\n  assert_format \"begin\\n  array[\\n    0, # Zero\\n  ]\\nend\"\n  assert_format \"[\\n  # foo\\n] of String\"\n  assert_format \"[\\n# foo\\n] of String\", \"[\\n  # foo\\n] of String\"\n\n  assert_format \"{1, 2, 3}\"\n  assert_format \"{ {1, 2, 3} }\"\n  assert_format \"{ {1 => 2} }\"\n  assert_format \"{ {1, 2, 3} => 4 }\"\n  assert_format \"{ {foo: 2} }\"\n  assert_format \"{ # foo\\n  1,\\n}\"\n\n  assert_format \"{ * 1 * 2,\\n*\\n3, 4 }\", \"{*1 * 2,\\n *3, 4}\"\n  assert_format \"[ * [ * [ 1 ] ], *    \\n[ 2] ]\", \"[*[*[1]], *[2]]\"\n\n  assert_format \"{  } of  A   =>   B\", \"{} of A => B\"\n  assert_format \"{ 1   =>   2 }\", \"{1 => 2}\"\n  assert_format \"{ 1   =>   2 ,   3  =>  4 }\", \"{1 => 2, 3 => 4}\"\n  assert_format \"{ 1   =>   2 ,\\n   3  =>  4 }\", \"{1 => 2,\\n 3 => 4}\"\n  assert_format \"{\\n1   =>   2 ,\\n   3  =>  4 }\", \"{\\n  1 => 2,\\n  3 => 4,\\n}\"\n  assert_format \"{ foo:  1 }\", \"{foo: 1}\"\n  assert_format \"{ \\\"foo\\\":  1 }\", \"{\\\"foo\\\": 1}\"\n  assert_format \"{ \\\"foo\\\" =>  1 }\", \"{\\\"foo\\\" => 1}\"\n  assert_format \"{ 1   =>   2 ,\\n\\n   3  =>  4 }\", \"{1 => 2,\\n\\n 3 => 4}\"\n  assert_format \"foo({\\nbar: 1,\\n})\", \"foo({\\n  bar: 1,\\n})\"\n  assert_format \"{ # foo\\n  1 => 2,\\n}\"\n\n  assert_format \"Foo\"\n  assert_format \"Foo:: Bar\", \"Foo::Bar\"\n  assert_format \"Foo:: Bar\", \"Foo::Bar\"\n  assert_format \"::Foo:: Bar\", \"::Foo::Bar\"\n  assert_format \"Foo(  )\", \"Foo()\"\n  assert_format \"Foo( A , 1 )\", \"Foo(A, 1)\"\n  assert_format \"Foo( x:  Int32  )\", \"Foo(x: Int32)\"\n  assert_format \"Foo( x:  Int32  ,  y: Float64 )\", \"Foo(x: Int32, y: Float64)\"\n  assert_format \"Foo(  * T, { * A  ,*\\n  B } )\", \"Foo(*T, {*A, *B})\"\n  assert_format \"Foo( Bar(  ) )\", \"Foo(Bar())\"\n\n  assert_format \"NamedTuple(a: Int32,)\", \"NamedTuple(a: Int32)\"\n  assert_format \"NamedTuple(\\n  a: Int32,\\n)\"\n  assert_format \"NamedTuple(\\n  a: Int32,)\", \"NamedTuple(\\n  a: Int32,\\n)\"\n  assert_format \"class Foo\\n  NamedTuple(\\n    a: Int32,\\n  )\\nend\"\n\n  # Multi-line generic type with inline comments (issue #11328)\n  assert_format \"Tuple(\\n  Int32, # first\\n  String, # second\\n)\", \"Tuple(\\n  Int32,  # first\\n  String, # second\\n)\"\n  assert_format \"Tuple(\\n  Int32, # first\\n  String, # second\\n  Bool,\\n)\", \"Tuple(\\n  Int32,  # first\\n  String, # second\\n  Bool,\\n)\"\n  assert_format \"Tuple(\\n  Int32, # first\\n  String # adds trailing comma\\n)\", \"Tuple(\\n  Int32,  # first\\n  String, # adds trailing comma\\n)\"\n  assert_format \"Hash(\\n  String, # key\\n  Int32, # value\\n)\", \"Hash(\\n  String, # key\\n  Int32,  # value\\n)\"\n  assert_format \"Tuple(\\n  Int32,\\n  String\\n)\", \"Tuple(\\n  Int32,\\n  String,\\n)\"\n\n  assert_format \"::Tuple(T)\"\n  assert_format \"::NamedTuple(T)\"\n  assert_format \"::Pointer(T)\"\n  assert_format \"::StaticArray(T)\"\n\n  assert_format \"Tuple()\"\n  assert_format \"::Tuple()\"\n  assert_format \"NamedTuple()\"\n  assert_format \"::NamedTuple()\"\n\n  %w(if unless).each do |keyword|\n    assert_format \"#{keyword} a\\n2\\nend\", \"#{keyword} a\\n  2\\nend\"\n    assert_format \"#{keyword} a\\n2\\n3\\nend\", \"#{keyword} a\\n  2\\n  3\\nend\"\n    assert_format \"#{keyword} a\\n2\\nelse\\nend\", \"#{keyword} a\\n  2\\nelse\\nend\"\n    assert_format \"#{keyword} a\\nelse\\n2\\nend\", \"#{keyword} a\\nelse\\n  2\\nend\"\n    assert_format \"#{keyword} a\\n2\\nelse\\n3\\nend\", \"#{keyword} a\\n  2\\nelse\\n  3\\nend\"\n    assert_format \"#{keyword} a\\n2\\n3\\nelse\\n4\\n5\\nend\", \"#{keyword} a\\n  2\\n  3\\nelse\\n  4\\n  5\\nend\"\n    assert_format \"#{keyword} a\\n#{keyword} b\\n3\\nelse\\n4\\nend\\nend\", \"#{keyword} a\\n  #{keyword} b\\n    3\\n  else\\n    4\\n  end\\nend\"\n    assert_format \"#{keyword} a\\n#{keyword} b\\nelse\\n4\\nend\\nend\", \"#{keyword} a\\n  #{keyword} b\\n  else\\n    4\\n  end\\nend\"\n    assert_format \"#{keyword} a\\n    # hello\\n 2\\nend\", \"#{keyword} a\\n  # hello\\n  2\\nend\"\n    assert_format \"#{keyword} a\\n2; 3\\nelse\\n3\\nend\", \"#{keyword} a\\n  2; 3\\nelse\\n  3\\nend\"\n  end\n\n  assert_format \"if 1\\n2\\nelsif\\n3\\n4\\nend\", \"if 1\\n  2\\nelsif 3\\n  4\\nend\"\n  assert_format \"if 1\\n2\\nelsif\\n3\\n4\\nelsif 5\\n6\\nend\", \"if 1\\n  2\\nelsif 3\\n  4\\nelsif 5\\n  6\\nend\"\n  assert_format \"if 1\\n2\\nelsif\\n3\\n4\\nelse\\n6\\nend\", \"if 1\\n  2\\nelsif 3\\n  4\\nelse\\n  6\\nend\"\n\n  assert_format \"{% if 1 %}\\n  2\\n{% end %}\\ndef foo\\nend\"\n\n  assert_format \"if 1\\n2\\nend\\nif 3\\nend\", \"if 1\\n  2\\nend\\nif 3\\nend\"\n  assert_format \"if 1\\nelse\\n2\\nend\\n3\", \"if 1\\nelse\\n  2\\nend\\n3\"\n\n  assert_format \"1 ? 2 : 3\"\n  assert_format \"1 ?\\n  2    :   \\n 3\", \"1 ? 2 : 3\"\n\n  assert_format \"1   if   2\", \"1 if 2\"\n  assert_format \"1   unless   2\", \"1 unless 2\"\n\n  assert_format \"[] of Int32\\n1\"\n\n  assert_format \"(1)\"\n  assert_format \"  (  1;  2;   3  )  \", \"(1; 2; 3)\"\n  assert_format \"(\\n  a = 1\\n  a\\n)\"\n  assert_format \"begin; 1; end\", \"begin\\n  1\\nend\"\n  assert_format \"begin\\n1\\n2\\n3\\nend\", \"begin\\n  1\\n  2\\n  3\\nend\"\n  assert_format \"begin\\n1 ? 2 : 3\\nend\", \"begin\\n  1 ? 2 : 3\\nend\"\n  assert_format \"begin\\n  begin\\n\\n  end\\nend\"\n  assert_format \"begin\\n  ()\\nend\"\n\n  assert_format \"def   foo  \\n  end\", \"def foo\\nend\"\n  assert_format \"def foo\\n1\\nend\", \"def foo\\n  1\\nend\"\n  assert_format \"def foo\\n\\n1\\n\\nend\", \"def foo\\n  1\\nend\"\n  assert_format \"def foo()\\n1\\nend\", \"def foo\\n  1\\nend\"\n  assert_format \"def foo   (   )   \\n1\\nend\", \"def foo\\n  1\\nend\"\n  assert_format \"def self . foo\\nend\", \"def self.foo\\nend\"\n  assert_format \"def   foo (  x )  \\n  end\", \"def foo(x)\\nend\"\n  assert_format \"def   foo (  x , y )  \\n  end\", \"def foo(x, y)\\nend\"\n  assert_format \"def   foo (  x , y , )  \\n  end\", \"def foo(x, y)\\nend\"\n  assert_format \"def   foo (  x , y ,\\n)  \\n  end\", \"def foo(x, y)\\nend\"\n  assert_format \"def   foo (  x ,\\n y )  \\n  end\", \"def foo(x,\\n        y)\\nend\"\n  assert_format \"def   foo (\\nx ,\\n y )  \\n  end\", \"def foo(\\n  x,\\n  y,\\n)\\nend\"\n  assert_format \"class Foo\\ndef   foo (\\nx ,\\n y )  \\n  end\\nend\", \"class Foo\\n  def foo(\\n    x,\\n    y,\\n  )\\n  end\\nend\"\n  assert_format \"def   foo (  @x)  \\n  end\", \"def foo(@x)\\nend\"\n  assert_format \"def   foo (  @x, @y)  \\n  end\", \"def foo(@x, @y)\\nend\"\n  assert_format \"def   foo (  @@x)  \\n  end\", \"def foo(@@x)\\nend\"\n  assert_format \"def   foo (  &@block)  \\n  end\", \"def foo(&@block)\\nend\"\n  assert_format \"def   foo (  @select)  \\n  end\", \"def foo(@select)\\nend\"\n  assert_format \"def   foo (  @@select)  \\n  end\", \"def foo(@@select)\\nend\"\n  assert_format \"def   foo (  bar  @select)  \\n  end\", \"def foo(bar @select)\\nend\"\n  assert_format \"def   foo (  bar  @@select)  \\n  end\", \"def foo(bar @@select)\\nend\"\n  assert_format \"def foo(a, &@b)\\nend\"\n  assert_format \"def   foo (  x  =   1 )  \\n  end\", \"def foo(x = 1)\\nend\"\n  assert_format \"def   foo (  x  :  Int32 )  \\n  end\", \"def foo(x : Int32)\\nend\"\n  assert_format \"def   foo (  x  :  self )  \\n  end\", \"def foo(x : self)\\nend\"\n  assert_format \"def   foo (  x  :  Foo.class )  \\n  end\", \"def foo(x : Foo.class)\\nend\"\n  assert_format \"def   foo (  x  :   Int32  =  1 )  \\n  end\", \"def foo(x : Int32 = 1)\\nend\"\n  assert_format \"abstract  def   foo  \\n  1\", \"abstract def foo\\n\\n1\"\n  assert_format \"def foo( & block )\\nend\", \"def foo(&block)\\nend\"\n  assert_format \"def foo( & )\\nend\", \"def foo(&)\\nend\"\n  assert_format \"def foo( & \\n )\\nend\", \"def foo(&)\\nend\"\n  assert_format \"def foo( x , & block )\\nend\", \"def foo(x, &block)\\nend\"\n  assert_format \"def foo( x , & block  : Int32 )\\nend\", \"def foo(x, &block : Int32)\\nend\"\n  assert_format \"def foo( x , & block  : Int32 ->)\\nend\", \"def foo(x, &block : Int32 ->)\\nend\"\n  assert_format \"def foo( x , & block  : Int32->Float64)\\nend\", \"def foo(x, &block : Int32 -> Float64)\\nend\"\n  assert_format \"def foo( x , & block  :   ->)\\nend\", \"def foo(x, &block : ->)\\nend\"\n  assert_format \"def foo( x , & : Int32 )\\nend\", \"def foo(x, & : Int32)\\nend\"\n  assert_format \"def foo(&: Int32)\\nend\", \"def foo(& : Int32)\\nend\"\n  assert_format \"def foo(&block: Int32)\\nend\", \"def foo(&block : Int32)\\nend\"\n  assert_format \"def foo( x , * y )\\nend\", \"def foo(x, *y)\\nend\"\n  assert_format \"class Bar\\nprotected def foo(x)\\na=b(c)\\nend\\nend\", \"class Bar\\n  protected def foo(x)\\n    a = b(c)\\n  end\\nend\"\n  assert_format \"def foo=(x)\\nend\"\n  assert_format \"def +(x)\\nend\"\n  assert_format \"def   foo  :  Int32 \\n  end\", \"def foo : Int32\\nend\"\n  assert_format \"def   foo ( x )  :  Int32 \\n  end\", \"def foo(x) : Int32\\nend\"\n  assert_format \"def foo: Int32\\nend\", \"def foo : Int32\\nend\"\n  assert_format \"def %(x)\\n  1\\nend\"\n  assert_format \"def //(x)\\n  1\\nend\"\n  assert_format \"def `(x)\\n  1\\nend\"\n  assert_format \"def /(x)\\n  1\\nend\"\n  assert_format \"def foo(x : X)  forall   X ,   Y; end\", \"def foo(x : X) forall X, Y; end\"\n  assert_format \"def foo(x)\\n  self // x\\nend\"\n  assert_format \"def foo(x)\\n  case self // x\\n  when 2\\n    3\\n  end\\nend\"\n  assert_format \"def foo(x)\\n  case 1\\n  when self // 2\\n    3\\n  end\\nend\"\n  assert_format \"def foo(x)\\n  case //\\n  when //\\n    3\\n  end\\nend\"\n  assert_format \"foo self // 1\"\n  assert_format \"foo(self // 1)\"\n  assert_format \"foo x, self // 1\"\n  assert_format \"{x => self // 1}\"\n  assert_format \"foo(//, //)\"\n  assert_format \"foo(a: 1 // 2)\"\n  assert_format \"foo(a: //)\"\n  assert_format \"foo(a: //, b: //)\"\n\n  assert_format \"def foo(a : T) forall T \\n  #\\nend\", \"def foo(a : T) forall T\\n  #\\nend\"\n  assert_format \"def foo(a : T, b : U) forall T, U\\n  #\\nend\", \"def foo(a : T, b : U) forall T, U\\n  #\\nend\"\n  assert_format \"def foo(a : T, b : U) forall T, U         #\\n  #\\nend\", \"def foo(a : T, b : U) forall T, U #\\n  #\\nend\"\n  assert_format \"def foo(a : T) forall T\\n  #\\n\\nend\", \"def foo(a : T) forall T\\n  #\\nend\"\n  assert_format \"def foo(a : T) forall T\\n  #\\n\\n\\nend\", \"def foo(a : T) forall T\\n  #\\nend\"\n  assert_format \"def foo\\n  1\\n  #\\nrescue\\nend\"\n  assert_format \"def foo\\n  1 #\\nrescue\\nend\"\n  assert_format \"def foo\\n  1 #\\nrescue\\nend\"\n  assert_format \"def foo\\n  1\\n  #\\n\\n\\nrescue\\nend\", \"def foo\\n  1\\n  #\\nrescue\\nend\"\n\n  assert_format \"def foo(@[MyAnn] v); end\"\n  assert_format \"def foo(@[MyAnn] &); end\"\n  assert_format \"def foo(@[MyAnn] &block); end\"\n  assert_format \"def foo(@[MyAnn] & : String -> Nil); end\"\n  assert_format \"def foo(  @[MyAnn]  v  ); end\", \"def foo(@[MyAnn] v); end\"\n  assert_format \"def foo(@[AnnOne] @[AnnTwo] v); end\"\n  assert_format \"def foo(@[AnnOne]   @[AnnTwo] v); end\", \"def foo(@[AnnOne] @[AnnTwo] v); end\"\n  assert_format \"def foo(@[AnnOne]   @[AnnTwo]   &  ); end\", \"def foo(@[AnnOne] @[AnnTwo] &); end\"\n  assert_format \"def foo(@[AnnOne]   @[AnnTwo]   &block : Int32 ->  ); end\", \"def foo(@[AnnOne] @[AnnTwo] &block : Int32 ->); end\"\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn] bar,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    foo,\n    @[MyAnn] &block\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    foo,\n    @[MyAnn]\n    &block\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    foo,\n\n    @[MyAnn]\n    &block\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    foo,\n\n    @[MyAnn]\n    @[MyAnn]\n    & : Nil -> Nil\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n  def foo(\n     @[MyAnn]   bar\n  ); end\n  CRYSTAL\n  def foo(\n    @[MyAnn] bar,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    bar,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n    @[MyAnn] baz,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n\n    @[MyAnn] baz,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n  def foo(\n     @[MyAnn]\n   bar\n  ); end\n  CRYSTAL\n  def foo(\n    @[MyAnn]\n    bar,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n  def foo(\n     @[MyAnn]\n   bar\n  ); end\n  CRYSTAL\n  def foo(\n    @[MyAnn]\n    bar,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n    @[MyAnn] @[MyAnn] baz,\n    @[MyAnn]\n    @[MyAnn]\n    biz,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n\n    @[MyAnn] @[MyAnn] baz,\n\n    @[MyAnn]\n    @[MyAnn]\n    biz,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n\n    @[MyAnn]  @[MyAnn]  baz,\n\n    @[MyAnn]\n\n    @[MyAnn]\n\n    biz\n  ); end\n  CRYSTAL\n  def foo(\n    @[MyAnn]\n    @[MyAnn]\n    bar,\n\n    @[MyAnn] @[MyAnn] baz,\n\n    @[MyAnn]\n    @[MyAnn]\n    biz,\n  ); end\n  CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    module M\n      @[MyAnn(\n        1\n\n      )]\n    end\n    CRYSTAL\n    module M\n      @[MyAnn(\n        1\n      )]\n    end\n    CRYSTAL\n\n  assert_format \"loop do\\n  1\\nrescue\\n  2\\nend\"\n  assert_format \"loop do\\n  1\\n  loop do\\n    2\\n  rescue\\n    3\\n  end\\n  4\\nend\"\n\n  assert_format \"foo\"\n  assert_format \"foo()\"\n  assert_format \"foo(  )\", \"foo()\"\n  assert_format \"foo  1\", \"foo 1\"\n  assert_format \"foo  1  ,   2\", \"foo 1, 2\"\n  assert_format \"foo(  1  ,   2 )\", \"foo(1, 2)\"\n\n  assert_format \"foo . bar\", \"foo.bar\"\n  assert_format \"foo . bar()\", \"foo.bar\"\n  assert_format \"foo . bar( x , y )\", \"foo.bar(x, y)\"\n  assert_format \"foo do  \\n x \\n end\", \"foo do\\n  x\\nend\"\n  assert_format \"foo do  | x | \\n x \\n end\", \"foo do |x|\\n  x\\nend\"\n  assert_format \"foo do  | x , y | \\n x \\n end\", \"foo do |x, y|\\n  x\\nend\"\n  assert_format \"if 1\\nfoo do  | x , y | \\n x \\n end\\nend\", \"if 1\\n  foo do |x, y|\\n    x\\n  end\\nend\"\n  assert_format \"foo do   # hello\\nend\", \"foo do # hello\\nend\"\n  assert_format \"foo{}\", \"foo { }\"\n  assert_format \"foo{|x| x}\", \"foo { |x| x }\"\n  assert_format \"foo{|x|\\n x}\", \"foo { |x|\\n  x\\n}\"\n  assert_format \"foo   &.bar\", \"foo &.bar\"\n  assert_format \"foo   &.bar( 1 , 2 )\", \"foo &.bar(1, 2)\"\n  assert_format \"foo.bar  &.baz( 1 , 2 )\", \"foo.bar &.baz(1, 2)\"\n  assert_format \"foo   &.bar\", \"foo &.bar\"\n  assert_format \"foo   &.==(2)\", \"foo &.==(2)\"\n  assert_format \"foo   &.>=(2)\", \"foo &.>=(2)\"\n  assert_format \"join io, &.inspect\"\n  assert_format \"foo . bar  =  1\", \"foo.bar = 1\"\n  assert_format \"foo  x:  1\", \"foo x: 1\"\n  assert_format \"foo  x:  1,  y:  2\", \"foo x: 1, y: 2\"\n  assert_format \"foo a , b ,  x:  1\", \"foo a, b, x: 1\"\n  assert_format \"foo a , *b\", \"foo a, *b\"\n  assert_format \"foo a , **b\", \"foo a, **b\"\n  assert_format \"foo   &bar\", \"foo &bar\"\n  assert_format \"foo 1 ,  &bar\", \"foo 1, &bar\"\n  assert_format \"foo(&.bar)\"\n  assert_format \"foo.bar(&.baz)\"\n  assert_format \"foo(1, &.bar)\"\n  assert_format \"foo(1,\\n  &.bar)\"\n  assert_format \"foo(1, # foo\\n  &.bar)\"\n  assert_format \"::foo(1, 2)\"\n  assert_format \"args.any? &.name.baz\"\n  assert_format \"foo(\\n  1, 2)\", \"foo(\\n  1, 2)\"\n  assert_format \"foo(\\n1,\\n 2  \\n)\", \"foo(\\n  1,\\n  2\\n)\"\n  assert_format \"foo(\\n1,\\n\\n 2  \\n)\", \"foo(\\n  1,\\n\\n  2\\n)\"\n  assert_format \"foo(\\n  1,\\n  # 2,\\n  3,\\n)\"\n  assert_format \"foo(\\n  1,\\n  # 2,\\n  # 3,\\n)\"\n  assert_format \"foo 1,\\n2\", \"foo 1,\\n  2\"\n  assert_format \"foo 1, a: 1,\\nb: 2,\\nc: 3\", \"foo 1, a: 1,\\n  b: 2,\\n  c: 3\"\n  assert_format \"foo 1,\\na: 1,\\nb: 2,\\nc: 3\", \"foo 1,\\n  a: 1,\\n  b: 2,\\n  c: 3\"\n  assert_format \"foo bar:baz, qux:other\", \"foo bar: baz, qux: other\"\n  assert_format \"foo(\\n  1, 2, &block)\", \"foo(\\n  1, 2, &block)\"\n  assert_format \"foo(\\n  1, 2,\\n&block)\", \"foo(\\n  1, 2,\\n  &block)\"\n  assert_format \"foo(\\n  1,\\n  2\\n) do\\n  1\\nend\"\n  assert_format \"foo 1, a: 1,\\nb: 2,\\nc: 3,\\n&block\", \"foo 1, a: 1,\\n  b: 2,\\n  c: 3,\\n  &block\"\n  assert_format \"foo 1, do\\n2\\nend\", \"foo 1 do\\n  2\\nend\"\n  assert_format \"a.b &.[c]?\\n1\"\n  assert_format \"a.b &.[c]\\n1\"\n  assert_format \"foo(1, 2,)\", \"foo(1, 2)\"\n  assert_format \"foo(1, 2,\\n)\", \"foo(1, 2)\"\n  assert_format \"foo(1,\\n2,\\n)\", \"foo(1,\\n  2,\\n)\"\n  assert_format \"foo(out x)\", \"foo(out x)\"\n  assert_format \"foo(\\n  1,\\n  a: 1,\\n  b: 2,\\n)\"\n  assert_format \"foo(1, ) { }\", \"foo(1) { }\"\n  assert_format \"foo(1, ) do\\nend\", \"foo(1) do\\nend\"\n  assert_format \"foo {;1}\", \"foo { 1 }\"\n  assert_format \"foo {;;1}\", \"foo { 1 }\"\n  assert_format \"foo.%(bar)\"\n  assert_format \"foo.% bar\"\n  assert_format \"foo.bar(&.%(baz))\"\n  assert_format \"foo.bar(&.% baz)\"\n  assert_format \"if 1\\n  foo(\\n    bar\\n    # comment\\n  )\\nend\"\n  assert_format \"if 1\\n  foo(\\n    bar,\\n    # comment\\n  )\\nend\"\n\n  assert_format \"foo.bar\\n.baz\", \"foo.bar\\n  .baz\"\n  assert_format \"foo.bar.baz\\n.qux\", \"foo.bar.baz\\n  .qux\"\n  assert_format \"foo\\n.bar\\n.baz\", \"foo\\n  .bar\\n  .baz\"\n\n  assert_format \"foo.\\nbar\", \"foo\\n  .bar\"\n\n  assert_format \"foo   &.is_a?(T)\", \"foo &.is_a?(T)\"\n  assert_format \"foo   &.responds_to?(:foo)\", \"foo &.responds_to?(:foo)\"\n\n  assert_format \"foo(\\n  1,\\n  &.foo\\n)\"\n\n  %w(return break next yield).each do |keyword|\n    assert_format keyword\n    assert_format \"#{keyword}( 1 )\", \"#{keyword}(1)\"\n    assert_format \"#{keyword}  1\", \"#{keyword} 1\"\n    assert_format \"#{keyword}( 1 , 2 )\", \"#{keyword}(1, 2)\"\n    assert_format \"#{keyword}  1 ,  2\", \"#{keyword} 1, 2\"\n    assert_format \"#{keyword}  *1\", \"#{keyword} *1\"\n    assert_format \"#{keyword}  1  , *2\", \"#{keyword} 1, *2\"\n    assert_format \"#{keyword}  *1  ,2\", \"#{keyword} *1, 2\"\n    assert_format \"#{keyword}  *1  , *2\", \"#{keyword} *1, *2\"\n    assert_format \"#{keyword}( *1  , *2 )\", \"#{keyword}(*1, *2)\"\n\n    unless keyword == \"yield\"\n      assert_format \"#{keyword} { 1 ,  2 }\", \"#{keyword} {1, 2}\"\n      assert_format \"#{keyword} {1, 2}, 3\"\n      assert_format \"#{keyword} 1, {2, 3}\"\n      assert_format \"#{keyword} {1, 2}, {3, 4}\"\n      assert_format \"#{keyword} { {1, 2}, {3, 4} }\"\n      assert_format \"#{keyword} { {1, 2}, {3, 4} }, 5\"\n    end\n  end\n\n  assert_format \"yield 1\\n2\", \"yield 1\\n2\"\n  assert_format \"yield 1 , \\n2\", \"yield 1,\\n  2\"\n  assert_format \"yield(1 , \\n2)\", \"yield(1,\\n  2)\"\n  assert_format \"yield(\\n1 , \\n2)\", \"yield(\\n  1,\\n  2)\"\n\n  assert_format \"with foo yield bar\"\n\n  context \"adds `&` to yielding methods that don't have a block parameter (#8764)\" do\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo\n        yield\n      end\n      CRYSTAL\n      def foo(&)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo()\n        yield\n      end\n      CRYSTAL\n      def foo(&)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n      )\n        yield\n      end\n      CRYSTAL\n      def foo(&)\n        yield\n      end\n      CRYSTAL\n\n    # #13091\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo # bar\n        yield\n      end\n      CRYSTAL\n      def foo(&) # bar\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(x)\n        yield\n      end\n      CRYSTAL\n      def foo(x, &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(x ,)\n        yield\n      end\n      CRYSTAL\n      def foo(x, &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(x,\n      y)\n        yield\n      end\n      CRYSTAL\n      def foo(x,\n              y, &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(x,\n      y,)\n        yield\n      end\n      CRYSTAL\n      def foo(x,\n              y, &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(x\n      )\n        yield\n      end\n      CRYSTAL\n      def foo(x,\n              &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(x,\n      )\n        yield\n      end\n      CRYSTAL\n      def foo(x,\n              &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n      x)\n        yield\n      end\n      CRYSTAL\n      def foo(\n        x, &\n      )\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n      x, y)\n        yield\n      end\n      CRYSTAL\n      def foo(\n        x, y, &\n      )\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n      x,\n      y)\n        yield\n      end\n      CRYSTAL\n      def foo(\n        x,\n        y, &\n      )\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n      x,\n      )\n        yield\n      end\n      CRYSTAL\n      def foo(\n        x,\n        &\n      )\n        yield\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(a, **b)\n        yield\n      end\n      CRYSTAL\n      def foo(a, **b, &)\n        yield\n      end\n      CRYSTAL\n\n    assert_format \"macro f\\n yield\\n {{ yield }}\\nend\"\n  end\n\n  assert_format <<-CRYSTAL\n    def foo(\n      a,\n      b,\n    )\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    def foo(\n      a, b,\n    )\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    macro foo(\n      a,\n      *b,\n    )\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    macro foo(\n      a,\n      **b,\n    )\n    end\n    CRYSTAL\n\n  context \"adds trailing comma to def multi-line normal, splat, and double splat parameters\" do\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      macro foo(\n        a,\n        b\n      )\n      end\n      CRYSTAL\n      macro foo(\n        a,\n        b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      macro foo(\n        a,\n        *b\n      )\n      end\n      CRYSTAL\n      macro foo(\n        a,\n        *b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      fun foo(\n        a : Int32,\n        b : Int32\n      )\n      end\n      CRYSTAL\n      fun foo(\n        a : Int32,\n        b : Int32,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      fun foo(\n        a : Int32,\n        ...\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a,\n        b\n      )\n      end\n      CRYSTAL\n      def foo(\n        a,\n        b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a : Int32,\n        b : Int32\n      )\n      end\n      CRYSTAL\n      def foo(\n        a : Int32,\n        b : Int32,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a : Int32,\n        b : Int32 = 1\n      )\n      end\n      CRYSTAL\n      def foo(\n        a : Int32,\n        b : Int32 = 1,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a,\n        b c\n      )\n      end\n      CRYSTAL\n      def foo(\n        a,\n        b c,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a,\n        @[Ann] b\n      )\n      end\n      CRYSTAL\n      def foo(\n        a,\n        @[Ann] b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a,\n        @[Ann]\n        b\n      )\n      end\n      CRYSTAL\n      def foo(\n        a,\n        @[Ann]\n        b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a, b\n      )\n      end\n      CRYSTAL\n      def foo(\n        a, b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a, b,\n        c, d\n      )\n      end\n      CRYSTAL\n      def foo(\n        a, b,\n        c, d,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a, # Foo\n        b # Bar\n      )\n      end\n      CRYSTAL\n      def foo(\n        a, # Foo\n        b, # Bar\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a,\n        *b\n      )\n      end\n      CRYSTAL\n      def foo(\n        a,\n        *b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL, <<-CRYSTAL\n      def foo(\n        a,\n        **b\n      )\n      end\n      CRYSTAL\n      def foo(\n        a,\n        **b,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(\n        a,\n        &block\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(\n        a,\n      )\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a)\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a, b)\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a, *args)\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a, *args, &block)\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a, **kwargs)\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a, **kwargs, &block)\n      end\n      CRYSTAL\n\n    assert_format <<-CRYSTAL\n      def foo(a, &block)\n      end\n      CRYSTAL\n  end\n\n  assert_format \"1   +   2\", \"1 + 2\"\n  assert_format \"1   &+   2\", \"1 &+ 2\"\n  assert_format \"1   >   2\", \"1 > 2\"\n  assert_format \"1   *   2\", \"1 * 2\"\n  assert_format \"1*2\", \"1*2\"\n  assert_format \"1/2\", \"1/2\"\n  assert_format \"1 / 2\", \"1 / 2\"\n  assert_format \"10/a\", \"10/a\"\n  assert_format \"10 / a\", \"10 / a\"\n  assert_format \"1 // 2\", \"1 // 2\"\n  assert_format \"10//a\", \"10//a\"\n  assert_format \"10 // a\", \"10 // a\"\n  assert_format \"10**a\", \"10**a\"\n  assert_format \"10 ** a\", \"10 ** a\"\n  assert_format %(\" \" * 2)\n  assert_format \"foo.bar / 2\\n\", \"foo.bar / 2\"\n\n  assert_format \"! 1\", \"!1\"\n  assert_format \"- 1\", \"-1\"\n  assert_format \"~ 1\", \"~1\"\n  assert_format \"+ 1\", \"+1\"\n  assert_format \"&- 1\", \"&-1\"\n  assert_format \"&+ 1\", \"&+1\"\n  assert_format \"a-1\", \"a - 1\"\n  assert_format \"a+1\", \"a + 1\"\n  assert_format \"a&-1\", \"a &- 1\"\n  assert_format \"a&+1\", \"a &+ 1\"\n  assert_format \"1 + \\n2\", \"1 +\\n  2\"\n  assert_format \"1 +  # foo\\n2\", \"1 + # foo\\n  2\"\n  assert_format \"a = 1 +  #    foo\\n2\", \"a = 1 + #    foo\\n    2\"\n  assert_format \"1+2*3\", \"1 + 2*3\"\n  assert_format \"1&+2&*3\", \"1 &+ 2 &* 3\"\n\n  assert_format \"foo(1 + \\n2)\", \"foo(1 +\\n    2)\"\n  assert_format \"foo(1 &+ \\n2)\", \"foo(1 &+\\n    2)\"\n\n  assert_format \"foo(1 &- 2)\"\n\n  assert_format \"foo[]\", \"foo[]\"\n  assert_format \"foo[ 1 , 2 ]\", \"foo[1, 2]\"\n  assert_format \"foo[ 1,  2 ]?\", \"foo[1, 2]?\"\n  assert_format \"foo[] =1\", \"foo[] = 1\"\n  assert_format \"foo[ 1 , 2 ]   =3\", \"foo[1, 2] = 3\"\n\n  assert_format \"foo.[]\"\n  assert_format \"foo.[ 1 , 2 ]\", \"foo.[1, 2]\"\n  assert_format \"foo.[ 1,  2 ]?\", \"foo.[1, 2]?\"\n  assert_format \"foo.[] =1\", \"foo.[] = 1\"\n  assert_format \"foo.[ 1 , 2 ]   =3\", \"foo.[1, 2] = 3\"\n\n  assert_format \"foo.[]()\", \"foo.[]\"\n  assert_format \"foo.[]( 1 , 2 )\", \"foo.[](1, 2)\"\n  assert_format \"foo.[]?( 1,  2 )\", \"foo.[]?(1, 2)\"\n  assert_format \"foo.[] =(1)\", \"foo.[] = (1)\"\n  assert_format \"foo.[]=( 1 , 2 , 3 )\", \"foo.[]=(1, 2, 3)\"\n\n  assert_format \"1  ||  2\", \"1 || 2\"\n  assert_format \"a  ||  b\", \"a || b\"\n  assert_format \"1  &&  2\", \"1 && 2\"\n  assert_format \"1 &&\\n2\", \"1 &&\\n  2\"\n  assert_format \"1 &&\\n2 &&\\n3\", \"1 &&\\n  2 &&\\n  3\"\n  assert_format \"1 && # foo\\n  2 &&\\n  3\"\n  assert_format \"if 0\\n1 &&\\n2 &&\\n3\\nend\", \"if 0\\n  1 &&\\n    2 &&\\n    3\\nend\"\n  assert_format \"if 1 &&\\n2 &&\\n3\\n4\\nend\", \"if 1 &&\\n   2 &&\\n   3\\n  4\\nend\"\n  assert_format \"if 1 &&\\n   (2 || 3)\\n  1\\nelse\\n  2\\nend\"\n  assert_format \"while 1 &&\\n2 &&\\n3\\n4\\nend\", \"while 1 &&\\n      2 &&\\n      3\\n  4\\nend\"\n\n  assert_format \"def foo(x =  __FILE__ )\\nend\", \"def foo(x = __FILE__)\\nend\"\n\n  assert_format \"a=1\", \"a = 1\"\n\n  assert_format \"while 1\\n2\\nend\", \"while 1\\n  2\\nend\"\n  assert_format \"until 1\\n2\\nend\", \"until 1\\n  2\\nend\"\n\n  assert_format \"a = begin\\n1\\n2\\nend\", \"a = begin\\n  1\\n  2\\nend\"\n  assert_format \"a = if 1\\n2\\n3\\nend\", \"a = if 1\\n      2\\n      3\\n    end\"\n  assert_format \"a = if 1\\n2\\nelse\\n3\\nend\", \"a = if 1\\n      2\\n    else\\n      3\\n    end\"\n  assert_format \"a = if 1\\n2\\nelsif 3\\n4\\nend\", \"a = if 1\\n      2\\n    elsif 3\\n      4\\n    end\"\n  assert_format \"a = [\\n1,\\n2]\", \"a = [\\n  1,\\n  2,\\n]\"\n  assert_format \"a = while 1\\n2\\nend\", \"a = while 1\\n  2\\nend\"\n  assert_format \"a = case 1\\nwhen 2\\n3\\nend\", \"a = case 1\\n    when 2\\n      3\\n    end\"\n  assert_format \"a = case 1\\nwhen 2\\n3\\nelse\\n4\\nend\", \"a = case 1\\n    when 2\\n      3\\n    else\\n      4\\n    end\"\n  assert_format \"a = \\nif 1\\n2\\nend\", \"a =\\n  if 1\\n    2\\n  end\"\n  assert_format \"a, b = \\nif 1\\n2\\nend\", \"a, b =\\n  if 1\\n    2\\n  end\"\n  assert_format \"a = b = 1\\na, b =\\n  b, a\"\n  assert_format \"a = # foo\\n  bar(1)\"\n  assert_format \"a = \\\\\\n  # foo\\n  bar(1)\"\n  assert_format \"a = \\\\\\n  # foo\\n  nil\"\n\n  assert_format %(require   \"foo\"), %(require \"foo\")\n\n  assert_format \"private   getter   foo\", \"private getter foo\"\n\n  assert_format %(\"foo \\#{ 1  +  2 }\"), %(\"foo \\#{1 + 2}\")\n  assert_format %(\"foo \\#{ 1 } \\#{ __DIR__ }\"), %(\"foo \\#{1} \\#{__DIR__}\")\n  assert_format %(\"foo \\#{ __DIR__ }\"), %(\"foo \\#{__DIR__}\")\n  assert_format \"__FILE__\", \"__FILE__\"\n  assert_format \"__DIR__\", \"__DIR__\"\n  assert_format \"__LINE__\", \"__LINE__\"\n\n  assert_format %q(\"\\\\\\\"\\#\\a\\b\\n\\r\\t\\v\\f\\e\")\n  assert_format %q(\"\\a\\c\\b\\d\"), %q(\"\\ac\\bd\")\n  assert_format %q(\"\\\\\\\"\\#\\a\\b\\n\\r\\t#{foo}\\v\\f\\e\")\n  assert_format %q(\"\\a\\c#{foo}\\b\\d\"), %q(\"\\ac#{foo}\\bd\")\n\n  assert_format %(\"\\#{foo = 1\\n}\"), %(\"\\#{foo = 1}\")\n  assert_format %(\"\\#{\\n  foo = 1\\n}\")\n  assert_format %(\"\\#{\\n  foo = 1}\"), %(\"\\#{\\n  foo = 1\\n}\")\n  assert_format %(\"\\#{ # foo\\n  foo = 1\\n}\")\n  assert_format %(\"\\#{\"foo\"}\")\n  assert_format %(\"\\#{\"\\#{foo}\"}\")\n  assert_format %(\"foo\\#{\"bar\"} Baz \\#{\"qux\"} \")\n  assert_format %(\"1\\#{\"4\\#{\"\\#{\"2\"}\"}3\"}3\\#{__DIR__}4\\#{5}6\")\n  assert_format %(\"1\\#{\"\\#{\"2\"}\"}3\\#{\"4\"}5\")\n\n  assert_format \"%w(one   two  three)\", \"%w(one two three)\"\n  assert_format \"%i(one   two  three)\", \"%i(one two three)\"\n  assert_format \"%w{one(   two(  three)}\", \"%w{one( two( three)}\"\n  assert_format \"%i{one(   two(  three)}\", \"%i{one( two( three)}\"\n\n  assert_format \"/foo/\"\n  assert_format \"/foo/imx\"\n  assert_format \"/foo \\#{ bar }/\", \"/foo \\#{bar}/\"\n  assert_format \"%r(foo \\#{ bar })\", \"%r(foo \\#{bar})\"\n  assert_format \"foo(/ /)\"\n  assert_format \"foo(1, / /)\"\n  assert_format \"/ /\"\n  assert_format \"begin\\n  / /\\nend\"\n  assert_format \"a = / /\"\n  assert_format \"1 == / /\"\n  assert_format \"if / /\\nend\"\n  assert_format \"while / /\\nend\"\n  assert_format \"[/ /, / /]\"\n  assert_format \"{/ / => / /, / / => / /}\"\n  assert_format \"case / /\\nwhen / /, /x/\\n  / /\\nend\"\n  assert_format \"case / /\\nwhen /x/, / /\\n  / /\\nend\"\n  assert_format \"/\\#{1}/imx\"\n\n  assert_format \"`foo`\"\n  assert_format \"`foo \\#{ bar }`\", \"`foo \\#{bar}`\"\n  assert_format \"%x(foo \\#{ bar })\", \"%x(foo \\#{bar})\"\n\n  assert_format \"module   Moo \\n\\n 1  \\n\\nend\", \"module Moo\\n  1\\nend\"\n  assert_format \"class   Foo \\n\\n 1  \\n\\nend\", \"class Foo\\n  1\\nend\"\n  assert_format \"struct   Foo \\n\\n 1  \\n\\nend\", \"struct Foo\\n  1\\nend\"\n  assert_format \"class   Foo  < \\n  Bar \\n\\n 1  \\n\\nend\", \"class Foo < Bar\\n  1\\nend\"\n  assert_format \"module Moo ( T )\\nend\", \"module Moo(T)\\nend\"\n  assert_format \"class Foo ( T )\\nend\", \"class Foo(T)\\nend\"\n  assert_format \"class Foo ( *T, U )\\nend\", \"class Foo(*T, U)\\nend\"\n  assert_format \"abstract  class Foo\\nend\", \"abstract class Foo\\nend\"\n  assert_format \"class Foo;end\", \"class Foo; end\"\n  assert_format \"class Foo; 1; end\", \"class Foo\\n  1\\nend\"\n  assert_format \"module Foo;end\", \"module Foo; end\"\n  assert_format \"module Foo; 1; end\", \"module Foo\\n  1\\nend\"\n  assert_format \"module Foo ( U, *T ); 1; end\", \"module Foo(U, *T)\\n  1\\nend\"\n  assert_format \"enum Foo;end\", \"enum Foo; end\"\n  assert_format \"enum Foo; A = 1; end\", \"enum Foo\\n  A = 1\\nend\"\n\n  assert_format \"@a\", \"@a\"\n  assert_format \"@@a\", \"@@a\"\n  assert_format \"$~\", \"$~\"\n  assert_format \"$~.bar\", \"$~.bar\"\n  assert_format \"$~ = 1\", \"$~ = 1\"\n  assert_format \"$?\", \"$?\"\n  assert_format \"$?.bar\", \"$?.bar\"\n  assert_format \"$? = 1\", \"$? = 1\"\n  assert_format \"$1\", \"$1\"\n  assert_format \"$1.bar\", \"$1.bar\"\n  assert_format \"$0\", \"$0\"\n  assert_format \"$0.bar\", \"$0.bar\"\n  assert_format \"$1?\"\n\n  assert_format \"foo . is_a? ( Bar )\", \"foo.is_a?(Bar)\"\n  assert_format \"foo . responds_to?( :bar )\", \"foo.responds_to?(:bar)\"\n  assert_format \"foo . is_a? Bar\", \"foo.is_a? Bar\"\n  assert_format \"foo . responds_to? :bar\", \"foo.responds_to? :bar\"\n  assert_format \"foo.responds_to? :bar\\n1\"\n\n  assert_format \"include  Foo\", \"include Foo\"\n  assert_format \"extend  Foo\", \"extend Foo\"\n\n  assert_format \"x  :   Int32\", \"x : Int32\"\n  assert_format \"x  :   Int32*\", \"x : Int32*\"\n  assert_format \"x  :   Int32**\", \"x : Int32**\"\n  assert_format \"x  :   A  |  B\", \"x : A | B\"\n  assert_format \"x  :   A?\", \"x : A?\"\n  assert_format \"x  :   Int32[ 8 ]\", \"x : Int32[8]\"\n  assert_format \"x  :   (A | B)\", \"x : (A | B)\"\n  assert_format \"x  :   (A -> B)\", \"x : (A -> B)\"\n  assert_format \"x  :   (A -> )\", \"x : (A ->)\"\n  assert_format \"x  :   (A -> B)?\", \"x : (A -> B)?\"\n  assert_format \"x  :   {A, B}\", \"x : {A, B}\"\n  assert_format \"x : { {A, B}, {C, D} }\"\n  assert_format \"x : {A, B, }\", \"x : {A, B}\"\n  assert_format \"x: Int32\", \"x : Int32\"\n  assert_format \"class Foo\\n@x  : Int32\\nend\", \"class Foo\\n  @x : Int32\\nend\"\n  assert_format \"class Foo\\n@x  :  Int32\\nend\", \"class Foo\\n  @x : Int32\\nend\"\n  assert_format \"class Foo\\nx = 1\\nend\", \"class Foo\\n  x = 1\\nend\"\n  assert_format \"x  =   uninitialized   Int32\", \"x = uninitialized Int32\"\n  assert_format \"x  :   Int32  =   1\", \"x : Int32 = 1\"\n\n  assert_format \"def foo\\n@x  :  Int32\\nend\", \"def foo\\n  @x : Int32\\nend\"\n  assert_format \"def foo\\n@x   =  uninitialized   Int32\\nend\", \"def foo\\n  @x = uninitialized Int32\\nend\"\n\n  assert_format \"x = 1\\nx    +=   1\", \"x = 1\\nx += 1\"\n  assert_format \"x[ y ] += 1\", \"x[y] += 1\"\n  assert_format \"@x   ||=   1\", \"@x ||= 1\"\n  assert_format \"@x   &&=   1\", \"@x &&= 1\"\n  assert_format \"@x[ 1 ]   ||=   2\", \"@x[1] ||= 2\"\n  assert_format \"@x[ 1 ]   &&=   2\", \"@x[1] &&= 2\"\n  assert_format \"@x[ 1 ]   +=   2\", \"@x[1] += 2\"\n  assert_format \"foo.bar   +=   2\", \"foo.bar += 2\"\n  assert_format \"a[b] ||= c\"\n\n  assert_format \"case  1 \\n when 2 \\n 3 \\n end\", \"case 1\\nwhen 2\\n  3\\nend\"\n  assert_format \"case  1 \\n when 2 \\n 3 \\n else \\n 4 \\n end\", \"case 1\\nwhen 2\\n  3\\nelse\\n  4\\nend\"\n  assert_format \"case  1 \\n when 2 , 3 \\n 4 \\n end\", \"case 1\\nwhen 2, 3\\n  4\\nend\"\n  assert_format \"case  1 \\n when 2 ,\\n 3 \\n 4 \\n end\", \"case 1\\nwhen 2,\\n     3\\n  4\\nend\"\n  assert_format \"case  1 \\n when 2 ; 3 \\n end\", \"case 1\\nwhen 2; 3\\nend\"\n  assert_format \"case  1 \\n when 2 ;\\n 3 \\n end\", \"case 1\\nwhen 2\\n  3\\nend\"\n  assert_format \"case  1 \\n when 2 ; 3 \\n when 4 ; 5\\nend\", \"case 1\\nwhen 2; 3\\nwhen 4; 5\\nend\"\n  assert_format \"case  1 \\n when 2 then 3 \\n end\", \"case 1\\nwhen 2 then 3\\nend\"\n  assert_format \"case  1 \\n when 2 then \\n 3 \\n end\", \"case 1\\nwhen 2\\n  3\\nend\"\n  assert_format \"case  1 \\n when 2 \\n 3 \\n when 4 \\n 5 \\n end\", \"case 1\\nwhen 2\\n  3\\nwhen 4\\n  5\\nend\"\n  assert_format \"if 1\\ncase 1\\nwhen 2\\n3\\nend\\nend\", \"if 1\\n  case 1\\n  when 2\\n    3\\n  end\\nend\"\n  assert_format \"case  1 \\n when  .foo? \\n 3 \\n end\", \"case 1\\nwhen .foo?\\n  3\\nend\"\n  assert_format \"case 1\\nwhen 1 then\\n2\\nwhen 3\\n4\\nend\", \"case 1\\nwhen 1\\n  2\\nwhen 3\\n  4\\nend\"\n  assert_format \"case  1 \\n when 2 \\n 3 \\n else 4 \\n end\", \"case 1\\nwhen 2\\n  3\\nelse 4\\nend\"\n  assert_format \"case 1\\nwhen 1, # 1\\n     2, # 2\\n     3  # 3\\n  1\\nend\"\n  assert_format \"a = case 1\\n    when 1, # 1\\n         2, # 2\\n         3  # 3\\n      1\\n    end\"\n  assert_format \"a = 1\\ncase\\nwhen 2\\nelse\\n  a /= 3\\nend\"\n  assert_format \"case 1\\nend\"\n  assert_format \"case 1\\nelse\\n  2\\nend\"\n  assert_format \"case\\nend\"\n  assert_format \"case 1\\nend\"\n  assert_format \"case\\nend\"\n  assert_format \"case\\nelse\\n  1\\nend\"\n\n  assert_format \"case  1 \\n in Int32 \\n 3 \\n end\", \"case 1\\nin Int32\\n  3\\nend\"\n\n  assert_format <<-CRYSTAL\n    case 0\n    when 0 then 1; 2\n    # Comments\n    end\n    CRYSTAL\n\n  assert_format \"select   \\n when  foo \\n 2 \\n end\", \"select\\nwhen foo\\n  2\\nend\"\n  assert_format \"select   \\n when  foo \\n 2 \\n when bar \\n 3 \\n end\", \"select\\nwhen foo\\n  2\\nwhen bar\\n  3\\nend\"\n  assert_format \"select   \\n when  foo  then  2 \\n end\", \"select\\nwhen foo then 2\\nend\"\n  assert_format \"select   \\n when  foo  ;  2 \\n end\", \"select\\nwhen foo; 2\\nend\"\n  assert_format \"select   \\n when  foo \\n 2 \\n else \\n 3 \\n end\", \"select\\nwhen foo\\n  2\\nelse\\n  3\\nend\"\n  assert_format \"def foo\\nselect   \\n when  foo \\n 2 \\n else \\n 3 \\nend\\nend\", \"def foo\\n  select\\n  when foo\\n    2\\n  else\\n    3\\n  end\\nend\"\n  assert_format \"select\\nwhen foo\\n  # foo\\n  # bar\\nelse\\n  # foo\\n  # bar\\nend\"\n  assert_format \"select\\nwhen foo # foo\\n  # bar\\nelse # foo\\n  # bar\\nend\"\n  assert_format \"begin\\n  select\\n  when foo\\n    # foo\\n    # bar\\n  else\\n    # foo\\n    # bar\\n  end\\nend\"\n\n  assert_format \"foo.@bar\"\n\n  assert_format \"@[Foo]\"\n  assert_format \"@[Foo()]\", \"@[Foo]\"\n  assert_format \"@[Foo( 1, 2 )]\", \"@[Foo(1, 2)]\"\n  assert_format \"@[Foo( 1, 2, foo: 3 )]\", \"@[Foo(1, 2, foo: 3)]\"\n  assert_format \"@[Foo]\\ndef foo\\nend\"\n  assert_format \"@[Foo(\\n  1,\\n)]\"\n  assert_format \"@[Foo::Bar]\"\n  assert_format \"@[::Foo::Bar]\"\n\n  assert_format \"1.as   Int32\", \"1.as Int32\"\n  assert_format \"foo.bar. as   Int32\", \"foo.bar.as Int32\"\n  assert_format \"1\\n.as(Int32)\", \"1\\n  .as(Int32)\"\n\n  assert_format \"1.as?   Int32\", \"1.as? Int32\"\n  assert_format \"foo.bar. as?   Int32\", \"foo.bar.as? Int32\"\n  assert_format \"1\\n.as?(Int32)\", \"1\\n  .as?(Int32)\"\n\n  assert_format \"1 .. 2\", \"1..2\"\n  assert_format \"1 ... 2\", \"1...2\"\n  assert_format \"(1 .. )\", \"(1..)\"\n  assert_format \" .. 2\", \"..2\"\n\n  assert_format \"1..\\n2\"\n  assert_format \"1\\n..\"\n  assert_format \"1\\n..2\"\n  assert_format \"...\\n2\"\n  assert_format \"1\\n..\\n2\"\n\n  assert_format \"typeof( 1, 2, 3 )\", \"typeof(1, 2, 3)\"\n  assert_format \"sizeof( Int32 )\", \"sizeof(Int32)\"\n  assert_format \"instance_sizeof( Int32 )\", \"instance_sizeof(Int32)\"\n  assert_format \"alignof( Int32 )\", \"alignof(Int32)\"\n  assert_format \"instance_alignof( Int32 )\", \"instance_alignof(Int32)\"\n  assert_format \"offsetof( String, @length )\", \"offsetof(String, @length)\"\n  assert_format \"pointerof( @a )\", \"pointerof(@a)\"\n\n  assert_format \"_ = 1\"\n  assert_format \"あ.い = 1\"\n\n  assert_format \"a , b  = 1  ,  2\", \"a, b = 1, 2\"\n  assert_format \"a[1] , b[2] = 1  ,  2\", \"a[1], b[2] = 1, 2\"\n  assert_format \" * a = 1 \", \"*a = 1\"\n  assert_format \" _ , *_ ,\\na.foo  ,a.bar  =  1  ,  2,3\", \"_, *_, a.foo, a.bar = 1, 2, 3\"\n  assert_format \"あ.い, う.え.お = 1, 2\"\n\n  assert_format \"begin\\n1\\nensure\\n2\\nend\", \"begin\\n  1\\nensure\\n  2\\nend\"\n  assert_format \"begin\\n1\\nrescue\\n3\\nensure\\n2\\nend\", \"begin\\n  1\\nrescue\\n  3\\nensure\\n  2\\nend\"\n  assert_format \"begin\\n1\\nrescue   ex\\n3\\nend\", \"begin\\n  1\\nrescue ex\\n  3\\nend\"\n  assert_format \"begin\\n1\\nrescue   ex   :   Int32 \\n3\\nend\", \"begin\\n  1\\nrescue ex : Int32\\n  3\\nend\"\n  assert_format \"begin\\n1\\nrescue   ex   :   Int32  |  Float64  \\n3\\nend\", \"begin\\n  1\\nrescue ex : Int32 | Float64\\n  3\\nend\"\n  assert_format \"begin\\n1\\nrescue   ex\\n3\\nelse\\n4\\nend\", \"begin\\n  1\\nrescue ex\\n  3\\nelse\\n  4\\nend\"\n  assert_format \"begin\\n1\\nrescue   Int32 \\n3\\nend\", \"begin\\n  1\\nrescue Int32\\n  3\\nend\"\n  assert_format \"if 1\\nbegin\\n2\\nensure\\n3\\nend\\nend\", \"if 1\\n  begin\\n    2\\n  ensure\\n    3\\n  end\\nend\"\n  assert_format \"1 rescue 2\"\n  assert_format \"1 ensure 2\"\n  assert_format \"begin\\n  call\\n  # comment\\nrescue\\n  call\\n  # comment\\nelse\\n  call\\n  # comment\\nensure\\n  call\\n  # comment\\nend\"\n\n  assert_format \"def foo\\n1\\nrescue\\n2\\nend\", \"def foo\\n  1\\nrescue\\n  2\\nend\"\n  assert_format \"def foo\\n1\\nensure\\n2\\nend\", \"def foo\\n  1\\nensure\\n  2\\nend\"\n  assert_format \"class Foo\\ndef foo\\n1\\nensure\\n2\\nend\\nend\", \"class Foo\\n  def foo\\n    1\\n  ensure\\n    2\\n  end\\nend\"\n  assert_format \"def run\\n\\nrescue\\n  2\\n  3\\nend\"\n\n  assert_format \"def foo(@x)\\n\\nrescue\\nend\"\n\n  assert_format \"macro foo\\nend\"\n  assert_format \"macro foo=(x)\\nend\"\n  assert_format \"macro []=(x, y)\\nend\"\n  assert_format \"macro foo()\\nend\", \"macro foo\\nend\"\n  assert_format \"macro foo( x , y )\\nend\", \"macro foo(x, y)\\nend\"\n  assert_format \"macro foo( x  =   1, y  =  2,  &block)\\nend\", \"macro foo(x = 1, y = 2, &block)\\nend\"\n  assert_format \"macro foo\\n  1 + 2\\nend\"\n  assert_format \"macro foo\\n  if 1\\n 1 + 2\\n end\\nend\", \"macro foo\\n  if 1\\n    1 + 2\\n  end\\nend\"\n  assert_format \"macro foo\\n  {{1 + 2}}\\nend\", \"macro foo\\n  {{1 + 2}}\\nend\"\n  assert_format \"macro foo\\n  {{ 1 + 2 }}\\nend\", \"macro foo\\n  {{ 1 + 2 }}\\nend\"\n  assert_format \"macro foo\\n  {% 1 + 2 %}\\nend\", \"macro foo\\n  {% 1 + 2 %}\\nend\"\n  assert_format \"macro foo\\n  {{ 1 + 2 }}\\\\\\nend\", \"macro foo\\n  {{ 1 + 2 }}\\\\\\nend\"\n  assert_format \"macro foo\\n  {{ 1 + 2 }}\\\\\\n 1\\n end\", \"macro foo\\n  {{ 1 + 2 }}\\\\\\n 1\\n end\"\n  assert_format \"macro foo\\n  {%1 + 2%}\\\\\\nend\", \"macro foo\\n  {% 1 + 2 %}\\\\\\nend\"\n  assert_format \"macro foo\\n  {% if 1 %} 2 {% end %}\\nend\"\n  assert_format \"macro foo\\n  {% unless 1 %} 2 {% end %}\\nend\"\n  assert_format \"macro foo\\n  {% if 1 %} 2 {% else %} 3 {% end %}\\nend\"\n  assert_format \"macro foo\\n  {% if 1 %}\\\\ 2 {% else %}\\\\ 3 {% end %}\\\\\\nend\"\n  assert_format \"macro foo\\n  {% for x in y %} 2 {% end %}\\nend\"\n  assert_format \"macro foo\\n  {% for x in y %}\\\\ 2 {% end %}\\\\\\nend\"\n  assert_format \"macro foo\\n  %foo\\nend\"\n  assert_format \"macro foo\\n  %foo{x.id+2}\\nend\", \"macro foo\\n  %foo{x.id + 2}\\nend\"\n  assert_format \"macro foo\\n  %foo{x,y}\\nend\", \"macro foo\\n  %foo{x, y}\\nend\"\n  assert_format \"def foo : Int32\\n  1\\nend\"\n  assert_format \"class Foo\\n  macro foo\\n    1\\n  end\\nend\"\n  assert_format \"   {{ 1 + 2 }}\", \"{{ 1 + 2 }}\"\n  assert_format \"  {% for x in y %} 2 {% end %}\", \"{% for x in y %} 2 {% end %}\"\n  assert_format \"  {% if 1 %} 2 {% end %}\", \"{% if 1 %} 2 {% end %}\"\n  assert_format \"  {% if 1 %} {% if 2 %} 2 {% end %} {% end %}\", \"{% if 1 %} {% if 2 %} 2 {% end %} {% end %}\"\n  assert_format \"if 1\\n  {% if 2 %} {% end %}\\nend\"\n  assert_format \"if 1\\n  {% for x in y %} {% end %}\\nend\"\n  assert_format \"if 1\\n  {{1 + 2}}\\nend\"\n  assert_format \"def foo : self | Nil\\n  nil\\nend\"\n  assert_format \"macro foo(x)\\n  {% if 1 %} 2 {% end %}\\nend\"\n  assert_format \"macro foo()\\n  {% if 1 %} 2 {% end %}\\nend\", \"macro foo\\n  {% if 1 %} 2 {% end %}\\nend\"\n  assert_format \"macro flags\\n  {% if 1 %}\\\\\\n  {% end %}\\\\\\nend\"\n  assert_format \"macro flags\\n  {% if 1 %}\\\\\\n 1 {% else %}\\\\\\n {% end %}\\\\\\nend\"\n  assert_format \"macro flags\\n  {% if 1 %}{{1}}a{{2}}{% end %}\\\\\\nend\"\n  assert_format \"  {% begin %} 2 {% end %}\", \"{% begin %} 2 {% end %}\"\n  assert_format \"macro foo\\n  \\\\{\\nend\"\n  assert_format \"macro foo\\n  {% if 1 %} 2 {% elsif 3 %} 4 {% else %} 5 {% end %}\\nend\"\n  assert_format \"macro [](x)\\nend\"\n  assert_format \"macro foo\\n  {% if true %}if true{% end %}\\n  {% if true %}end{% end %}\\nend\"\n  assert_format \"macro foo\\n    1  +  2 \\n    end\", \"macro foo\\n  1 + 2\\nend\"\n  assert_format \"class Foo\\n macro foo\\n    1  +  2 \\n    end\\n end\", \"class Foo\\n  macro foo\\n    1 + 2\\n  end\\nend\"\n  assert_format \"macro foo\\n    def   bar  \\n  end \\n    end\", \"macro foo\\n  def bar\\n  end\\nend\"\n\n  assert_format \"def foo\\na = bar do\\n1\\nend\\nend\", \"def foo\\n  a = bar do\\n    1\\n  end\\nend\"\n  assert_format \"def foo\\nend\\ndef bar\\nend\", \"def foo\\nend\\n\\ndef bar\\nend\"\n  assert_format \"private def foo\\nend\\nprivate def bar\\nend\", \"private def foo\\nend\\n\\nprivate def bar\\nend\"\n  assert_format \"a = 1\\ndef bar\\nend\", \"a = 1\\n\\ndef bar\\nend\"\n  assert_format \"def foo\\nend\\n\\n\\n\\ndef bar\\nend\", \"def foo\\nend\\n\\ndef bar\\nend\"\n  assert_format \"def foo\\nend;def bar\\nend\", \"def foo\\nend\\n\\ndef bar\\nend\"\n  assert_format \"class Foo\\nend\\nclass Bar\\nend\", \"class Foo\\nend\\n\\nclass Bar\\nend\"\n\n  assert_format \"alias  Foo  =   Bar\", \"alias Foo = Bar\"\n  assert_format \"alias  Foo::Bar  =   Baz\", \"alias Foo::Bar = Baz\"\n  assert_format \"alias A = (B)\"\n  assert_format \"alias A = (B) -> C\"\n  assert_format \"alias Foo=Bar\", \"alias Foo = Bar\"\n  assert_format \"alias Foo= Bar\", \"alias Foo = Bar\"\n  assert_format \"alias Foo =Bar\", \"alias Foo = Bar\"\n  assert_format \"alias Foo::Bar=Baz\", \"alias Foo::Bar = Baz\"\n  assert_format \"alias Foo::Bar= Baz\", \"alias Foo::Bar = Baz\"\n  assert_format \"alias Foo::Bar =Baz\", \"alias Foo::Bar = Baz\"\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    alias Foo=\n    Bar\n    CRYSTAL\n    alias Foo = Bar\n    CRYSTAL\n  assert_format \"lib Foo\\nend\"\n  assert_format \"lib Foo\\ntype  Foo  =   Bar\\nend\", \"lib Foo\\n  type Foo = Bar\\nend\"\n  assert_format \"lib Foo\\nfun foo\\nend\", \"lib Foo\\n  fun foo\\nend\"\n  assert_format \"lib Foo\\n  fun Bar\\nend\"\n  assert_format \"lib Foo\\n  fun bar = Bar\\nend\"\n  assert_format \"lib Foo\\n  fun Foo = Bar\\nend\"\n  assert_format \"lib Foo\\nfun foo  :  Int32\\nend\", \"lib Foo\\n  fun foo : Int32\\nend\"\n  assert_format \"lib Foo\\nfun foo()  :  Int32\\nend\", \"lib Foo\\n  fun foo : Int32\\nend\"\n  assert_format \"lib Foo\\nfun foo ()  :  Int32\\nend\", \"lib Foo\\n  fun foo : Int32\\nend\"\n  assert_format \"lib Foo\\nfun foo(x   :   Int32, y   :   Float64)  :  Int32\\nend\", \"lib Foo\\n  fun foo(x : Int32, y : Float64) : Int32\\nend\"\n  assert_format \"lib Foo\\nfun foo(x : Int32,\\ny : Float64) : Int32\\nend\", \"lib Foo\\n  fun foo(x : Int32,\\n          y : Float64) : Int32\\nend\"\n  assert_format \"lib Foo\\nfun foo( ... )  :  Int32\\nend\", \"lib Foo\\n  fun foo(...) : Int32\\nend\"\n  assert_format \"lib Foo\\nfun foo(x : Int32, ... )  :  Int32\\nend\", \"lib Foo\\n  fun foo(x : Int32, ...) : Int32\\nend\"\n  assert_format \"lib Foo\\n  fun foo(Int32) : Int32\\nend\"\n  assert_format \"fun foo(x : Int32) : Int32\\n  1\\nend\"\n  assert_format \"fun foo(\\n  x : Int32,\\n  ...\\n) : Int32\\n  1\\nend\"\n  assert_format <<-CRYSTAL\n    lib Foo\n      fun foo = bar(Int32) : Int32\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL\n    lib Foo\n      fun foo =\n        bar : Void\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    lib Foo\n      fun foo =\n\n\n        bar : Void\n    end\n    CRYSTAL\n    lib Foo\n      fun foo =\n        bar : Void\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL\n    lib Foo\n      fun foo =\n        bar(Int32) : Int32\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    lib Foo\n      fun foo =\n\n\n        bar(Int32) : Int32\n    end\n    CRYSTAL\n    lib Foo\n      fun foo =\n        bar(Int32) : Int32\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    lib Foo\n      fun foo =\n        bar(Int32,\n        Int32) : Int32\n    end\n    CRYSTAL\n    lib Foo\n      fun foo =\n        bar(Int32,\n            Int32) : Int32\n    end\n    CRYSTAL\n  assert_format \"lib Foo\\n  fun foo = bar(Int32) : Int32\\nend\"\n  assert_format <<-CRYSTAL\n    lib Foo\n      fun foo = \"bar\"(Int32) : Int32\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL\n    lib Foo\n      fun foo =\n        \"bar\"(Int32) : Int32\n    end\n    CRYSTAL\n  assert_format <<-CRYSTAL\n    lib Foo\n      fun foo =\n        \"bar\"(Int32) : Int32\n      # comment\n    end\n    CRYSTAL\n  assert_format \"lib Foo\\n  $foo  :  Int32 \\nend\", \"lib Foo\\n  $foo : Int32\\nend\"\n  assert_format \"lib Foo\\n  $foo = hello  :  Int32 \\nend\", \"lib Foo\\n  $foo = hello : Int32\\nend\"\n  assert_format \"lib Foo\\nalias  Foo  =  Bar -> \\n$a : Int32\\nend\", \"lib Foo\\n  alias Foo = Bar ->\\n  $a : Int32\\nend\"\n  assert_format \"lib Foo\\nstruct Foo\\nend\\nend\", \"lib Foo\\n  struct Foo\\n  end\\nend\"\n  assert_format \"lib Foo\\nstruct Foo\\nx  :  Int32\\nend\\nend\", \"lib Foo\\n  struct Foo\\n    x : Int32\\n  end\\nend\"\n  assert_format \"lib Foo\\nstruct Foo\\nx  :  Int32\\ny : Float64\\nend\\nend\", \"lib Foo\\n  struct Foo\\n    x : Int32\\n    y : Float64\\n  end\\nend\"\n  assert_format \"lib Foo\\nstruct Foo\\nx  ,  y  :  Int32\\nend\\nend\", \"lib Foo\\n  struct Foo\\n    x, y : Int32\\n  end\\nend\"\n  assert_format \"lib Foo\\nstruct Foo\\nx  ,  y  , z :  Int32\\nend\\nend\", \"lib Foo\\n  struct Foo\\n    x, y, z : Int32\\n  end\\nend\"\n  assert_format \"lib Foo\\nunion Foo\\nend\\nend\", \"lib Foo\\n  union Foo\\n  end\\nend\"\n\n  assert_format \"SomeLib.UppercasedFunCall\"\n  assert_format \"SomeLib.UppercasedFunCall 1, 2\"\n\n  assert_format \"enum Foo\\nend\"\n  assert_format \"enum Foo\\nA  \\nend\", \"enum Foo\\n  A\\nend\"\n  assert_format \"enum Foo\\nA = 1\\nend\", \"enum Foo\\n  A = 1\\nend\"\n  assert_format \"enum Foo : Int32\\nA = 1\\nend\", \"enum Foo : Int32\\n  A = 1\\nend\"\n  assert_format \"enum Foo : Int32\\nA = 1\\ndef foo\\n1\\nend\\nend\", \"enum Foo : Int32\\n  A = 1\\n\\n  def foo\\n    1\\n  end\\nend\"\n  assert_format \"lib Bar\\n  enum Foo\\n  end\\nend\"\n  assert_format \"lib Bar\\n  enum Foo\\n    A\\n  end\\nend\"\n  assert_format \"lib Bar\\n  enum Foo\\n    A = 1\\n  end\\nend\"\n\n  assert_format \"lib Foo::Bar\\nend\"\n\n  %w(foo foo= foo? foo!).each do |method|\n    assert_format \"->#{method}\"\n    assert_format \"foo = 1\\n->foo.#{method}\"\n    assert_format \"->Foo.#{method}\"\n    assert_format \"->@foo.#{method}\"\n    assert_format \"->@@foo.#{method}\"\n    assert_format \"-> :: #{method}\", \"->::#{method}\"\n    assert_format \"-> :: Foo . #{method}\", \"->::Foo.#{method}\"\n  end\n\n  assert_format \"foo = 1\\n->foo.bar(Int32)\"\n  assert_format \"foo = 1\\n->foo.bar(Int32*)\"\n  assert_format \"foo = 1\\n->foo.bar=(Int32)\"\n  assert_format \"foo = 1\\n->foo.[](Int32)\"\n  assert_format \"foo = 1\\n->foo.[]=(Int32)\"\n\n  assert_format \"->{ x }\", \"-> { x }\"\n  assert_format \"->{\\nx\\n}\", \"-> {\\n  x\\n}\"\n  assert_format \"->do\\nx\\nend\", \"-> do\\n  x\\nend\"\n  assert_format \"->( ){ x }\", \"-> { x }\"\n  assert_format \"->() do x end\", \"-> do x end\"\n  assert_format \"->( x , y )   { x }\", \"->(x, y) { x }\"\n  assert_format \"->( x : Int32 , y )   { x }\", \"->(x : Int32, y) { x }\"\n  assert_format \"->{ x }\", \"-> { x }\"\n\n  # #13232\n  assert_format \"->{}\", \"-> { }\"\n  assert_format \"->(){}\", \"-> { }\"\n  assert_format \"->{1}\", \"-> { 1 }\"\n  assert_format \"->(x : Int32) {}\", \"->(x : Int32) { }\"\n  assert_format \"-> : Int32 {}\", \"-> : Int32 { }\"\n  assert_format \"->do\\nend\", \"-> do\\nend\"\n  assert_format \"-> : Int32 {}\", \"-> : Int32 { }\"\n\n  # Allows whitespace around proc literal, but doesn't enforce them\n  assert_format \"-> { }\"\n  assert_format \"-> { 1 }\"\n  assert_format \"->(x : Int32) { }\"\n  assert_format \"-> : Int32 { }\"\n  assert_format \"-> do\\nend\"\n\n  assert_format \"-> : Int32 { }\"\n  assert_format \"-> : Int32 | String { 1 }\"\n  assert_format \"-> : Array(Int32) {}\", \"-> : Array(Int32) { }\"\n  assert_format \"-> : Int32? {}\", \"-> : Int32? { }\"\n  assert_format \"-> : Int32* {}\", \"-> : Int32* { }\"\n  assert_format \"-> : Int32[1] {}\", \"-> : Int32[1] { }\"\n  assert_format \"-> : {Int32, String} {}\", \"-> : {Int32, String} { }\"\n  assert_format \"-> : {Int32} { String }\"\n  assert_format \"-> : {x: Int32, y: String} {}\", \"-> : {x: Int32, y: String} { }\"\n  assert_format \"->\\n:\\nInt32\\n{\\n}\", \"-> : Int32 {\\n}\"\n  assert_format \"->( x )\\n:\\nInt32 { }\", \"->(x) : Int32 { }\"\n  assert_format \"->: Int32 do\\nx\\nend\", \"-> : Int32 do\\n  x\\nend\"\n\n  {:+, :-, :*, :/, :^, :>>, :<<, :|, :&, :&+, :&-, :&*, :&**}.each do |sym|\n    assert_format \":#{sym}\"\n  end\n\n  assert_format \":\\\"foo bar\\\"\"\n\n  assert_format %(\"foo\" \\\\\\n \"bar\"), %(\"foo\" \\\\\\n\"bar\")\n  assert_format %(\"foo\" \\\\\\n \"bar\" \\\\\\n \"baz\"), %(\"foo\" \\\\\\n\"bar\" \\\\\\n\"baz\")\n  assert_format %(\"foo \\#{bar}\" \\\\\\n \"baz\"), %(\"foo \\#{bar}\" \\\\\\n\"baz\")\n\n  assert_format %(asm(\"nop\"))\n  assert_format %(asm(\\n\"nop\"\\n)), %(asm(\\n  \"nop\"\\n))\n  assert_format %(asm(\"nop\" : : )), %(asm(\"nop\"))\n  assert_format %(asm(\"nop\" :: )), %(asm(\"nop\"))\n  assert_format %(asm(\"nop\" :: \"r\"(0))), %(asm(\"nop\" :: \"r\"(0)))\n  assert_format %(asm(\"nop\" : \"a\"(0) )), %(asm(\"nop\" : \"a\"(0)))\n  assert_format %(asm(\"nop\" : \"a\"(0), \"b\"(1) )), %(asm(\"nop\" : \"a\"(0), \"b\"(1)))\n  assert_format %(asm(\"nop\" : \"a\"(0) : \"b\"(1) )), %(asm(\"nop\" : \"a\"(0) : \"b\"(1)))\n  assert_format %(asm(\"nop\" : \"a\"(0) : \"b\"(1), \"c\"(2) )), %(asm(\"nop\" : \"a\"(0) : \"b\"(1), \"c\"(2)))\n  assert_format %(asm(\"nop\" : \"a\"(0)\\n: \"b\"(1), \"c\"(2) )), %(asm(\"nop\" : \"a\"(0)\\n          : \"b\"(1), \"c\"(2)))\n  assert_format %(asm(\"nop\" : \"a\"(0), \"b\"(1)\\n: \"c\"(2), \"d\"(3) )), %(asm(\"nop\" : \"a\"(0), \"b\"(1)\\n          : \"c\"(2), \"d\"(3)))\n  assert_format %(asm(\"nop\" : \"a\"(0),\\n\"b\"(1)\\n: \"c\"(2), \"d\"(3) )), %(asm(\"nop\" : \"a\"(0),\\n            \"b\"(1)\\n          : \"c\"(2), \"d\"(3)))\n  assert_format %(asm(\"nop\" : \"a\"(0)\\n: \"b\"(1),\\n\"c\"(2) )), %(asm(\"nop\" : \"a\"(0)\\n          : \"b\"(1),\\n            \"c\"(2)))\n  assert_format %(asm(\\n\"nop\" : \"a\"(0), \"b\"(1) )), %(asm(\\n  \"nop\" : \"a\"(0), \"b\"(1)\\n))\n  assert_format %(asm(\"nop\"\\n: \"a\"(0) )), %(asm(\"nop\"\\n        : \"a\"(0)))\n  assert_format %(asm(\"nop\" ::: \"eax\" )), %(asm(\"nop\" ::: \"eax\"))\n  assert_format %(asm(\"nop\" ::: \"eax\" ,  \"ebx\" )), %(asm(\"nop\" ::: \"eax\", \"ebx\"))\n  assert_format %(asm(\"nop\" :::: \"volatile\" )), %(asm(\"nop\" :::: \"volatile\"))\n  assert_format %(asm(\"nop\" :::: \"volatile\"  , \"alignstack\"  ,  \"intel\"   )), %(asm(\"nop\" :::: \"volatile\", \"alignstack\", \"intel\"))\n  assert_format %(asm(\"nop\" ::: \"eax\" ,  \"ebx\" :   \"volatile\"  ,  \"alignstack\" )), %(asm(\"nop\" ::: \"eax\", \"ebx\" : \"volatile\", \"alignstack\"))\n  assert_format %(asm(\"a\" : \"b\"(c) : \"d\"(e) :: \"volatile\"))\n  assert_format %(asm(\"a\" : \"b\"(1), \"c\"(2) : \"d\"(3) : : \"volatile\")), %(asm(\"a\" : \"b\"(1), \"c\"(2) : \"d\"(3) :: \"volatile\"))\n\n  assert_format %(asm(\"a\" : \"b\"(c)\\n)), %(asm(\"a\" : \"b\"(c)))\n  assert_format %(asm(\"a\" :: \"d\"(e)\\n)), %(asm(\"a\" :: \"d\"(e)))\n  assert_format %(asm(\"a\" ::: \"f\"\\n)), %(asm(\"a\" ::: \"f\"))\n  assert_format %(asm(\"a\" :::: \"volatile\"\\n)), %(asm(\"a\" :::: \"volatile\"))\n  assert_format %(asm(\"a\" : : : : \"volatile\")), %(asm(\"a\" :::: \"volatile\"))\n  assert_format %(asm(\"a\" :: : : \"volatile\")), %(asm(\"a\" :::: \"volatile\"))\n  assert_format %(asm(\"a\" : :: : \"volatile\")), %(asm(\"a\" :::: \"volatile\"))\n  assert_format %(asm(\"a\" : : :: \"volatile\")), %(asm(\"a\" :::: \"volatile\"))\n  assert_format %(asm(\"a\" : \"b\"(c) : \"d\"(e)\\n        : \"f\"))\n  assert_format %(asm(\"a\" : \"b\"(c) : \"d\"(e)\\n        : \"f\",\\n          \"g\"))\n  assert_format %(asm(\"a\" ::: \"a\"\\n        : \"volatile\",\\n          \"intel\"))\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    asm(\n    # foo\n    \"nop\"\n    # bar\n    )\n    CRYSTAL\n    asm(\n      # foo\n      \"nop\"\n      # bar\n    )\n    CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    asm(\n      # the assembly template string, following the\n      # syntax for LLVM's integrated assembler\n      \"nop\" :               # output operands\n    \"=r\"(foo), \"=r\"(bar) : # input operands\n    \"r\"(1), \"r\"(baz) :     # names of clobbered registers\n    \"eax\", \"memory\" :      # optional flags, corresponding to the LLVM IR\n      # sideeffect / alignstack / inteldialect / unwind attributes\n    \"volatile\", \"alignstack\", \"intel\", \"unwind\"\n    )\n    CRYSTAL\n    asm(\n      # the assembly template string, following the\n      # syntax for LLVM's integrated assembler\n      \"nop\" :                # output operands\n      \"=r\"(foo), \"=r\"(bar) : # input operands\n      \"r\"(1), \"r\"(baz) :     # names of clobbered registers\n      \"eax\", \"memory\" :      # optional flags, corresponding to the LLVM IR\n      # sideeffect / alignstack / inteldialect / unwind attributes\n      \"volatile\", \"alignstack\", \"intel\", \"unwind\"\n    )\n    CRYSTAL\n\n  assert_format \"1 # foo\\n1234 # bar\", \"1    # foo\\n1234 # bar\"\n  assert_format \"1234 # foo\\n1 # bar\", \"1234 # foo\\n1    # bar\"\n  assert_format \"1#foo\", \"1 # foo\"\n  assert_format \"1 # foo\\n1234 # bar\\n\\n10 # bar\", \"1    # foo\\n1234 # bar\\n\\n10 # bar\"\n  assert_format \"# foo\\na = 1 # bar\"\n  assert_format \"#### ###\"\n  assert_format \"#######\"\n  assert_format \"x\\n# foo\\n\\n# bar\"\n\n  assert_format \"A = 1\\nFOO = 2\\n\\nEX = 3\", \"A   = 1\\nFOO = 2\\n\\nEX = 3\"\n  assert_format \"FOO = 2\\nA = 1\", \"FOO = 2\\nA   = 1\"\n  assert_format \"FOO = 2 + 3\\nA = 1 - 10\", \"FOO = 2 + 3\\nA   = 1 - 10\"\n  assert_format \"private FOO = 2\\nprivate A = 1\", \"private FOO = 2\\nprivate A   = 1\"\n  assert_format \"enum Baz\\nA = 1\\nFOO = 2\\n\\nEX = 3\\nend\", \"enum Baz\\n  A   = 1\\n  FOO = 2\\n\\n  EX = 3\\nend\"\n  assert_format \"enum Baz\\nA = 1\\nFOO\\n\\nEX = 3\\nend\", \"enum Baz\\n  A   = 1\\n  FOO\\n\\n  EX = 3\\nend\"\n\n  assert_format \"1   # foo\", \"1 # foo\"\n  assert_format \"1  # foo\\n2  # bar\", \"1 # foo\\n2 # bar\"\n  assert_format \"1  #foo  \\n2  #bar\", \"1 # foo\\n2 # bar\"\n  assert_format \"if 1\\n2  # foo\\nend\", \"if 1\\n  2 # foo\\nend\"\n  assert_format \"if 1\\nelse\\n2  # foo\\nend\", \"if 1\\nelse\\n  2 # foo\\nend\"\n  assert_format \"if # some comment\\n 2 # another\\n 3 # final \\n end # end \", \"if  # some comment\\n2   # another\\n  3 # final\\nend # end\"\n  assert_format \"while 1\\n2  # foo\\nend\", \"while 1\\n  2 # foo\\nend\"\n  assert_format \"def foo\\n2  # foo\\nend\", \"def foo\\n  2 # foo\\nend\"\n  assert_format \"if 1\\n# nothing\\nend\", \"if 1\\n  # nothing\\nend\"\n  assert_format \"if 1\\nelse\\n# nothing\\nend\", \"if 1\\nelse\\n  # nothing\\nend\"\n  assert_format \"if 1 # foo\\n2\\nend\", \"if 1 # foo\\n  2\\nend\"\n  assert_format \"if 1  # foo\\nend\", \"if 1 # foo\\nend\"\n  assert_format \"while 1  # foo\\nend\", \"while 1 # foo\\nend\"\n  assert_format \"while 1\\n# nothing\\nend\", \"while 1\\n  # nothing\\nend\"\n  assert_format \"class Foo  # foo\\nend\", \"class Foo # foo\\nend\"\n  assert_format \"class Foo\\n# nothing\\nend\", \"class Foo\\n  # nothing\\nend\"\n  assert_format \"module Foo  # foo\\nend\", \"module Foo # foo\\nend\"\n  assert_format \"module Foo\\n# nothing\\nend\", \"module Foo\\n  # nothing\\nend\"\n  assert_format \"case 1 # foo\\nwhen 2\\nend\", \"case 1 # foo\\nwhen 2\\nend\"\n  assert_format \"def foo\\n# hello\\n1\\nend\", \"def foo\\n  # hello\\n  1\\nend\"\n  assert_format \"struct Foo(T)\\n# bar\\n1\\nend\", \"struct Foo(T)\\n  # bar\\n  1\\nend\"\n  assert_format \"struct Foo\\n  # bar\\n  # baz\\n1\\nend\", \"struct Foo\\n  # bar\\n  # baz\\n  1\\nend\"\n  assert_format \"(size - 1).downto(0) do |i|\\n  yield @buffer[i]\\nend\"\n  assert_format \"(a).b { }\\nc\"\n  assert_format \"begin\\n  a\\nend.b { }\\nc\"\n  assert_format \"if a\\n  b &c\\nend\"\n  assert_format \"foo (1).bar\"\n  assert_format \"foo a: 1\\nb\"\n  assert_format \"if 1\\n2 && 3\\nend\", \"if 1\\n  2 && 3\\nend\"\n  assert_format \"if 1\\n  node.is_a?(T)\\nend\"\n  assert_format \"case 1\\nwhen 2\\n#comment\\nend\", \"case 1\\nwhen 2\\n  # comment\\nend\"\n  assert_format \"case 1\\nwhen 2\\n\\n#comment\\nend\", \"case 1\\nwhen 2\\n  # comment\\nend\"\n  assert_format \"1 if 2\\n# foo\", \"1 if 2\\n# foo\"\n  assert_format \"1 if 2\\n# foo\\n3\"\n  assert_format \"1\\n2\\n# foo\"\n  assert_format \"1\\n2  \\n  # foo\", \"1\\n2\\n# foo\"\n  assert_format \"if 1\\n2\\n3\\n# foo\\nend\", \"if 1\\n  2\\n  3\\n  # foo\\nend\"\n  assert_format \"def foo\\n1\\n2\\n# foo\\nend\", \"def foo\\n  1\\n  2\\n  # foo\\nend\"\n  assert_format \"if 1\\nif 2\\n3 # foo\\nend\\nend\", \"if 1\\n  if 2\\n    3 # foo\\n  end\\nend\"\n  assert_format \"class Foo\\n1\\n\\n# foo\\nend\", \"class Foo\\n  1\\n\\n  # foo\\nend\"\n  assert_format \"module Foo\\n1\\n\\n# foo\\nend\", \"module Foo\\n  1\\n\\n  # foo\\nend\"\n  assert_format \"if 1\\n1\\n\\n# foo\\nend\", \"if 1\\n  1\\n\\n  # foo\\nend\"\n  assert_format \"while true\\n1\\n\\n# foo\\nend\", \"while true\\n  1\\n\\n  # foo\\nend\"\n  assert_format \"def foo\\nend\\n\\ndef bar\\nend\\n\\n# foo\"\n  assert_format \"1 && (\\n  2 || 3\\n)\"\n  assert_format \"class Foo\\n  def foo\\n    # nothing\\n  end\\nend\"\n  assert_format \"while 1 # foo\\n  # bar\\n  2\\nend\", \"while 1 # foo\\n  # bar\\n  2\\nend\"\n  assert_format \"foo(\\n # foo\\n1,\\n\\n # bar\\n2,  \\n)\", \"foo(\\n  # foo\\n  1,\\n\\n  # bar\\n  2,\\n)\"\n  assert_format \"foo do;\\n1; end\", \"foo do\\n  1\\nend\"\n  assert_format \"if 1;\\n2; end\", \"if 1\\n  2\\nend\"\n  assert_format \"while 1;\\n2; end\", \"while 1\\n  2\\nend\"\n  assert_format \"if 1;\\n2;\\nelse;\\n3;\\nend\", \"if 1\\n  2\\nelse\\n  3\\nend\"\n  assert_format \"if 1;\\n2;\\nelsif 3;\\n4;\\nend\", \"if 1\\n  2\\nelsif 3\\n  4\\nend\"\n  assert_format \"def foo\\n  1\\n  2\\nrescue IO\\n  1\\nend\"\n  assert_format \"def execute\\n  begin\\n    1\\n  ensure\\n    2\\n  end\\n  3\\nend\"\n  assert_format \"foo.bar=(2)\\n1\"\n  assert_format \"inner &.color=(@color)\\n1\"\n  assert_format \"ary.size = (1).to_i\"\n  assert_format \"b &.[c].d\"\n  assert_format \"b &.[c]?.d\"\n  assert_format \"a &.b[c]?\"\n  assert_format \"+ a + d\", \"+a + d\"\n  assert_format \"  ((1) + 2)\", \"((1) + 2)\"\n  assert_format \"if 1\\n  ((1) + 2)\\nend\"\n\n  assert_format \"def   foo(x   :  self ?) \\n  end\", \"def foo(x : self?)\\nend\"\n  assert_format \"def foo(x : (self)?)\\nend\"\n\n  assert_format \"  macro foo\\n  end\\n\\n  :+\", \"macro foo\\nend\\n\\n:+\"\n  assert_format \"[\\n1, # a\\n2, # b\\n 3 # c\\n]\", \"[\\n  1, # a\\n  2, # b\\n  3, # c\\n]\"\n  assert_format \"[\\n  a() # b\\n]\", \"[\\n  a(), # b\\n]\"\n  assert_format \"[\\n  a(), # b\\n]\", \"[\\n  a(), # b\\n]\"\n  assert_format \"[\\n  a(),\\n]\", \"[\\n  a(),\\n]\"\n  assert_format \"if 1\\n[\\n  a() # b\\n]\\nend\", \"if 1\\n  [\\n    a(), # b\\n  ]\\nend\"\n  assert_format \"foo(\\n# x\\n1,\\n\\n# y\\nz: 2\\n)\", \"foo(\\n  # x\\n  1,\\n\\n  # y\\n  z: 2\\n)\"\n  assert_format \"foo(\\n# x\\n1,\\n\\n# y\\nz: 2,\\n\\n# a\\nb: 3)\", \"foo(\\n  # x\\n  1,\\n\\n  # y\\n  z: 2,\\n\\n  # a\\n  b: 3)\"\n  assert_format \"foo(\\n 1, # hola\\n2, # chau\\n )\", \"foo(\\n  1, # hola\\n  2, # chau\\n)\"\n  assert_format \"foo (1)\", \"foo(1)\"\n  assert_format \"foo (1), 2\"\n  assert_format \"foo (1; 2)\"\n  assert_format \"foo ((1) ? 2 : 3)\", \"foo((1) ? 2 : 3)\"\n  assert_format \"foo((1..3))\"\n  assert_format \"foo ()\"\n  assert_format \"foo ( )\", \"foo ()\"\n  assert_format \"def foo(\\n\\n#foo\\nx,\\n\\n#bar\\nz\\n)\\nend\", \"def foo(\\n  # foo\\n  x,\\n\\n  # bar\\n  z,\\n)\\nend\"\n  assert_format \"def foo(\\nx, #foo\\nz #bar\\n)\\nend\", \"def foo(\\n  x, # foo\\n  z, # bar\\n)\\nend\"\n  assert_format \"a = 1;;; b = 2\", \"a = 1; b = 2\"\n  assert_format \"a = 1\\n;\\nb = 2\", \"a = 1\\nb = 2\"\n  assert_format \"foo do\\n  # bar\\nend\"\n  assert_format \"abstract def foo\\nabstract def bar\"\n  assert_format \"if 1\\n  ->{ 1 }\\nend\", \"if 1\\n  -> { 1 }\\nend\"\n  assert_format \"foo.bar do\\n  baz\\n    .b\\nend\"\n  assert_format \"coco.lala\\nfoo\\n  .bar\"\n  assert_format \"foo.bar = \\n1\", \"foo.bar =\\n  1\"\n  assert_format \"foo.bar += \\n1\", \"foo.bar +=\\n  1\"\n  assert_format \"->{}\", \"-> { }\"\n  assert_format \"foo &.[a] = 1\"\n  assert_format \"[\\n  # foo\\n  1,\\n\\n  # bar\\n  2,\\n]\"\n  assert_format \"[c.x]\\n  .foo\"\n  assert_format \"foo([\\n  1,\\n  2,\\n  3,\\n])\"\n  assert_format \"bar = foo([\\n  1,\\n  2,\\n  3,\\n])\"\n  assert_format \"foo({\\n  1 => 2,\\n  3 => 4,\\n  5 => 6,\\n})\"\n  assert_format \"bar = foo({\\n        1 => 2,\\n        3 => 4,\\n        5 => 6,\\n      })\", \"bar = foo({\\n  1 => 2,\\n  3 => 4,\\n  5 => 6,\\n})\"\n  assert_format \"foo(->{\\n  1 + 2\\n})\", \"foo(-> {\\n  1 + 2\\n})\"\n  assert_format \"bar = foo(->{\\n  1 + 2\\n})\", \"bar = foo(-> {\\n  1 + 2\\n})\"\n  assert_format \"foo(->do\\n  1 + 2\\nend)\", \"foo(-> do\\n  1 + 2\\nend)\"\n  assert_format \"bar = foo(->do\\n  1 + 2\\nend)\", \"bar = foo(-> do\\n  1 + 2\\nend)\"\n  assert_format \"bar = foo(->{\\n  1 + 2\\n})\", \"bar = foo(-> {\\n  1 + 2\\n})\"\n  assert_format \"case 1\\nwhen 2\\n  3\\n  # foo\\nelse\\n  4\\n  # bar\\nend\"\n  assert_format \"1 #=> 2\", \"1 # => 2\"\n  assert_format \"1 #=>2\", \"1 # => 2\"\n  assert_format \"foo(\\n  [\\n    1,\\n    2,\\n  ],\\n  [\\n    3,\\n    4,\\n  ]\\n)\"\n  assert_format \"%w(\\n  one two\\n  three four\\n)\"\n  assert_format \"a = %w(\\n  one two\\n  three four\\n)\"\n  assert_format \"foo &.bar do\\n  1 + 2\\nend\"\n  assert_format \"a = foo &.bar do\\n  1 + 2\\nend\"\n  assert_format \"foo(bar([\\n  1,\\n]))\"\n  assert_format \"a = foo(bar([\\n  1,\\n]))\"\n  assert_format \"foo(baz1 do\\nend)\"\n  assert_format \"a = foo(baz1 do\\nend)\"\n  assert_format \"foo(bar(baz3 do\\nend))\"\n  assert_format \"a = foo(bar(baz3 do\\nend))\"\n  assert_format \"foo(bar(\\n  1,\\n  2,\\n))\"\n  assert_format \"a = foo(bar(\\n  1,\\n  2,\\n))\"\n  # assert_format \"a = foo(bar([\\n          1,\\n          2,\\n        ]),\\n        3,\\n       )\"\n  assert_format \"foo(1, 2, {\\n  foo: 1,\\n  bar: 2,\\n})\"\n  assert_format \"a = foo(1, 2, {\\n  foo: 1,\\n  bar: 2,\\n})\"\n  assert_format \"foo([\\n  1,\\n  bar do\\n  end,\\n  [\\n    2,\\n  ],\\n])\"\n  assert_format \"foo(bar(\\n  1,\\n  baz(\\n    2,\\n    3,\\n  )\\n))\"\n  assert_format \"foo(bar(\\n  1,\\n  baz(2,\\n      3,\\n     )\\n))\", \"foo(bar(\\n  1,\\n  baz(2,\\n    3,\\n  )\\n))\"\n  assert_format \"foo({\\n  1 => 2,\\n  3 => {\\n    4 => 5,\\n  },\\n})\"\n  assert_format \"foo([\\n  1, 2,\\n  3, 4,\\n])\"\n  assert_format \"foo(baz(x, y) do\\n  1 + 2\\nend)\"\n  assert_format \"case 1\\nwhen \\\"foo\\\"     ; 3\\nwhen \\\"lalalala\\\"; 4\\nelse             5\\nend\"\n  assert_format \"case 1\\nwhen \\\"foo\\\"      then 3\\nwhen \\\"lalalala\\\" then 4\\nelse                 5\\nend\"\n  assert_format \"case 1        # foo\\nwhen 2 then 3 # bar\\nwhen 4 then 5 # baz\\nelse        6 # zzz\\nend\"\n  assert_format \"case 1\\nwhen 8     then 1\\nwhen 16    then 2\\nwhen 256   then 3\\nwhen 'a'   then 5\\nwhen \\\"foo\\\" then 6\\nelse            4\\nend\"\n  assert_format \"case 1\\nwhen 1      then 1\\nwhen 123    then 2\\nwhen 1..123 then 3\\nelse             4\\nend\"\n  assert_format \"macro bar\\n  1\\nend\\n\\ncase 1\\nwhen  2 then 3\\nwhen 45 then 6\\nend\"\n  assert_format \"{\\n         1 => 2,\\n        10 => 30,\\n        30 => 40,\\n  \\\"foobar\\\" => 50,\\n  \\\"coco\\\"   => 60,\\n}\"\n  assert_format \"{1 => 2, 3 => 4}\\n{5234234 => 234098234, 7 => 8}\"\n  assert_format \"{\\n    1 => 2, 3 => 4,\\n  567 => 8910,\\n}\", \"{\\n  1 => 2, 3 => 4,\\n  567 => 8910,\\n}\"\n  assert_format \"{\\n  foo:    1,\\n  b:      2,\\n  barbaz: 3,\\n}\"\n  assert_format \"{\\n  a:   1,\\n  foo: bar,\\n}\"\n  assert_format \"%(\\n1\\n)\\n\\n{\\n    1 => 2,\\n  234 => 5,\\n}\"\n  assert_format \"class Actor\\n  macro inherited\\nend\\nend\\n\", \"class Actor\\n  macro inherited\\n  end\\nend\"\n  assert_format \"class Actor\\n  macro inherited\\n\\nend\\nend\\n\", \"class Actor\\n  macro inherited\\n  end\\nend\"\n  assert_format \"{\\n  \\\"foo\\\":    1,\\n  \\\"babraz\\\": 2,\\n}\"\n  assert_format \"def foo\\n  ((((((((((((((((0_u64\\n    ) | ptr[0]) << 8\\n    ) | ptr[1]) << 8\\n    ) | ptr[2]) << 8\\n    ) | ptr[3]) << 8\\n    ) | ptr[4]) << 8\\n    ) | ptr[5]) << 8\\n    ) | ptr[6]) << 8\\n    ) | ptr[7])\\nend\"\n  assert_format \"yield (1).foo\"\n  assert_format \"module Ton\\n  macro foo\\n    class {{name.id}}\\n    end\\n  end\\nend\"\n  assert_format \"a = 1\\na ||= begin\\n  1\\nend\"\n  assert_format \"if 1\\n  return foo(\\n    1,\\n    2,\\n  )\\nend\"\n  assert_format \"1\\nyield\\n2\"\n  assert_format \"if 1\\n  [\\n    1,\\n  ].none?\\nend\"\n  assert_format \"# foo\\ndef foo\\nend\\n# bar\\ndef bar\\nend\", \"# foo\\ndef foo\\nend\\n\\n# bar\\ndef bar\\nend\"\n  assert_format \"<<-FOO\\n1\\nFOO\\n\\n{\\n   1 => 2,\\n  10 => 3,\\n}\"\n  assert_format \"p = Foo[1, 2, 3,\\n        4, 5, 6,\\n       ]\", \"p = Foo[1, 2, 3,\\n  4, 5, 6,\\n]\"\n  assert_format \"p = Foo[\\n  1, 2, 3,\\n  4, 5, 6\\n]\\n\", \"p = Foo[\\n  1, 2, 3,\\n  4, 5, 6,\\n]\"\n  assert_format \"[1, 2,\\n  3, 4]\\n\", \"[1, 2,\\n 3, 4]\"\n  assert_format \"{1 => 2,\\n  3 => 4, # lala\\n}\\n\", \"{1 => 2,\\n 3 => 4, # lala\\n}\"\n  assert_format \"A = 10\\nFOO = 123\\nBARBAZ = 1234\\n\", \"A      =   10\\nFOO    =  123\\nBARBAZ = 1234\"\n  assert_format \"enum Foo\\n  A      =   10\\n  FOO    =  123\\n  BARBAZ = 1234\\nend\\n\", \"enum Foo\\n  A      =   10\\n  FOO    =  123\\n  BARBAZ = 1234\\nend\"\n  assert_format \"1\\n# hello\\n\\n\\n\", \"1\\n# hello\"\n  assert_format \"def foo\\n  a = 1; # foo\\n  a = 2; # bar\\nend\\n\", \"def foo\\n  a = 1 # foo\\n  a = 2 # bar\\nend\"\n  assert_format \"# Hello\\n#\\n# ```\\n# puts 1+2 # bye\\n# 1+2 # hello\\n#\\n# 1+2\\n# ```\\n\\n# ```\\n# puts 1+2\\n\\n# ```\\n# puts 1+2\\n\\n# Hola\\n#\\n#     1+2\\n#     foo do\\n#     3+4\\n#     end\\n\\n# Hey\\n#\\n#     1+2\\n#     foo do\\n#     3+4\\n#     end\\n#\\n# ```\\n# 1+2\\n# ```\\n#\\n#     1+2\\n#\\n# Bye\\n\", \"# Hello\\n#\\n# ```\\n# puts 1 + 2 # bye\\n# 1 + 2      # hello\\n#\\n# 1 + 2\\n# ```\\n\\n# ```\\n# puts 1+2\\n\\n# ```\\n# puts 1+2\\n\\n# Hola\\n#\\n#     1+2\\n#     foo do\\n#     3+4\\n#     end\\n\\n# Hey\\n#\\n#     1+2\\n#     foo do\\n#     3+4\\n#     end\\n#\\n# ```\\n# 1 + 2\\n# ```\\n#\\n#     1+2\\n#\\n# Bye\"\n  assert_format \"# Hello\\n#\\n# ```cr\\n#   1\\n# ```\\n# Bye\", \"# Hello\\n#\\n# ```\\n# 1\\n# ```\\n# Bye\"\n  assert_format \"# Hello\\n#\\n# ```crystal\\n#   1\\n# ```\\n# Bye\", \"# Hello\\n#\\n# ```\\n# 1\\n# ```\\n# Bye\"\n  assert_format \"macro foo\\n  {% for value, i in values %}\\\\\\n    {% if true %}\\\\\\n    {% end %}\\\\\\n    {{ 1 }}/\\n  {% end %}\\\\\\nend\\n\\n{\\n  1 => 2,\\n  1234 => 5,\\n}\\n\", \"macro foo\\n  {% for value, i in values %}\\\\\\n    {% if true %}\\\\\\n    {% end %}\\\\\\n    {{ 1 }}/\\n  {% end %}\\\\\\nend\\n\\n{\\n     1 => 2,\\n  1234 => 5,\\n}\"\n  assert_format \"a = \\\"\\n\\\"\\n1    # 1\\n12 # 2\\n\", \"a = \\\"\\n\\\"\\n1  # 1\\n12 # 2\"\n  assert_format \"enum Foo\\n  A;   B;   C\\nend\\n\", \"enum Foo\\n  A; B; C\\nend\"\n  assert_format \"# ```\\n# macro foo\\n#   1\\n# end\\n# ```\\n\", \"# ```\\n# macro foo\\n#   1\\n# end\\n# ```\"\n  assert_format \"class Foo\\n  # ```\\n  # 1\\n  # ```\\nend\\n\", \"class Foo\\n  # ```\\n  # 1\\n  # ```\\nend\"\n  assert_format \"# Here is the doc of a method, and contains an example:\\n#\\n# ```\\n# result = foo\\n#\\n# puts result\\n# ```\\ndef foo\\n  # ...\\nend\\n\", \"# Here is the doc of a method, and contains an example:\\n#\\n# ```\\n# result = foo\\n#\\n# puts result\\n# ```\\ndef foo\\n  # ...\\nend\"\n  assert_format \"foo(\\n  a: 1,\\n  b: 2,\\n  )\\n\", \"foo(\\n  a: 1,\\n  b: 2,\\n)\"\n  assert_format \"  case 1\\n  when 2\\n    3\\n  else #:newline, :eof\\n    1 if 2\\n    return 3\\n  end\\n\", \"case 1\\nwhen 2\\n  3\\nelse # :newline, :eof\\n  1 if 2\\n  return 3\\nend\"\n  assert_format \"a = 1 if 1 == 2 ||\\n  3 == 4\\n\", \"a = 1 if 1 == 2 ||\\n         3 == 4\"\n  assert_format \"{ A: 1 }\\n\", \"{A: 1}\"\n  assert_format \"class Foo\\n  enum Bar\\n  A; B; C;\\n  D; E; F\\nend\\nend\\n\", \"class Foo\\n  enum Bar\\n    A; B; C\\n    D; E; F\\n  end\\nend\"\n  assert_format \"x.is_a? T\\n3\\n\", \"x.is_a? T\\n3\"\n  assert_format \"a = begin\\n  1\\nend\\n\\na =\\nbegin\\n  1\\nend\\n\\na = if 1\\n  2\\nend\\n\\nb = 1\\nb ||= begin\\n  2\\nend\\n\\nb ||= if 1\\n  2\\nend\\n\\nb += if 1\\n  2\\nend\\n\\nb +=\\nif 1\\n  2\\nend\\n\\na, b = begin\\n  1\\nend\\n\\na, b =\\nbegin\\n  1\\nend\\n\\nc[x] = begin\\n  2\\nend\\n\\nc[x] =\\nbegin\\n  2\\nend\\n\\nc[x] = if 1\\n  2\\nend\\n\\nc[x] ||= begin 1\\n  2\\nend\\n\\nc[x] ||= if 1\\n  2\\nend\\n\\nc[x] += if 1\\n  2\\nend\\n\\nc[x] += begin 1\\n  2\\nend\\n\\nc[x] +=\\nbegin\\n  1\\n  2\\nend\\n\\nfoo.bar = begin\\nend\\n\\nfoo.bar =\\nbegin\\nend\\n\\nfoo.bar = if\\n  2\\nend\\n\\nfoo.bar += begin\\n  2\\nend\\n\\nfoo.bar += if\\n  2\\nend\\n\\n\", \"a = begin\\n  1\\nend\\n\\na =\\n  begin\\n    1\\n  end\\n\\na = if 1\\n      2\\n    end\\n\\nb = 1\\nb ||= begin\\n  2\\nend\\n\\nb ||= if 1\\n        2\\n      end\\n\\nb += if 1\\n       2\\n     end\\n\\nb +=\\n  if 1\\n    2\\n  end\\n\\na, b = begin\\n  1\\nend\\n\\na, b =\\n  begin\\n    1\\n  end\\n\\nc[x] = begin\\n  2\\nend\\n\\nc[x] =\\n  begin\\n    2\\n  end\\n\\nc[x] = if 1\\n         2\\n       end\\n\\nc[x] ||= begin\\n  1\\n  2\\nend\\n\\nc[x] ||= if 1\\n           2\\n         end\\n\\nc[x] += if 1\\n          2\\n        end\\n\\nc[x] += begin\\n  1\\n  2\\nend\\n\\nc[x] +=\\n  begin\\n    1\\n    2\\n  end\\n\\nfoo.bar = begin\\n\\nend\\n\\nfoo.bar =\\n  begin\\n\\n  end\\n\\nfoo.bar = if 2\\n          end\\n\\nfoo.bar += begin\\n  2\\nend\\n\\nfoo.bar += if 2\\n           end\"\n  assert_format \"module Foo\\n  1 # bar\\nend\\n\\nmodule Foo\\n  1\\n  # bar\\nend\\n\\nmodule Foo\\n  1\\n\\n  # bar\\nend\\n\\nmodule Foo\\n  1\\n  2\\n  # bar\\nend\\n\\nmodule Foo\\n  1\\n  2\\n\\n  # bar\\nend\\n\\nif 1\\n  1\\n  # bar\\nend\\n\\nif 1\\n  1\\n\\n  # bar\\nend\\n\\n1\\n2\\n# foo\\n\\n1\\n2\\n\\n# foo\\n\", \"module Foo\\n  1 # bar\\nend\\n\\nmodule Foo\\n  1\\n  # bar\\nend\\n\\nmodule Foo\\n  1\\n\\n  # bar\\nend\\n\\nmodule Foo\\n  1\\n  2\\n  # bar\\nend\\n\\nmodule Foo\\n  1\\n  2\\n\\n  # bar\\nend\\n\\nif 1\\n  1\\n  # bar\\nend\\n\\nif 1\\n  1\\n\\n  # bar\\nend\\n\\n1\\n2\\n# foo\\n\\n1\\n2\\n\\n# foo\"\n  assert_format \"begin\\n  #hola\\n  1\\nend\\n\", \"begin\\n  # hola\\n  1\\nend\"\n  assert_format \"begin\\nend\\n\\n# a\\n\", \"begin\\n\\nend\\n\\n# a\"\n  assert_format \"begin\\n  1\\nend\\n\\n1\\n\", \"begin\\n  1\\nend\\n\\n1\"\n  assert_format \"{\\n  \\\"a\\\" => 1, \\\"b\\\" => 2,\\n  \\\"foo\\\" => 3, \\\"bar\\\" => 4,\\n  \\\"coconio\\\" => 5, \\\"lala\\\" => 6,\\n}\\n\", \"{\\n  \\\"a\\\" => 1, \\\"b\\\" => 2,\\n  \\\"foo\\\" => 3, \\\"bar\\\" => 4,\\n  \\\"coconio\\\" => 5, \\\"lala\\\" => 6,\\n}\"\n  assert_format \"if 1\\n  foo(\\n    1,\\n    2 # lala\\n    )\\nend\\n\", \"if 1\\n  foo(\\n    1,\\n    2 # lala\\n  )\\nend\"\n  assert_format \"case foo\\nwhen 1\\n  # A\\nelse\\n# B\\nend\\n\", \"case foo\\nwhen 1\\n  # A\\nelse\\n  # B\\nend\"\n  assert_format \"return 1\\n# end\"\n  assert_format \"case\\n# hello\\nwhen 1\\n  2\\nend\"\n  assert_format \"case 1\\nwhen 2 # a\\n  # b\\nend\"\n  assert_format \"case 1\\nelse # foo\\n  # bar\\nend\"\n\n  assert_format \"{} of A => B\\n{} of Foo => Bar\"\n\n  assert_format \"<<-HTML\\n  \\#{1}x\\n  HTML\"\n  assert_format \"<<-HTML\\n  \\#{1}x\\n  y\\n  HTML\"\n  assert_format \"<<-HTML\\n  \\#{1}x\\n  y\\n  z\\n  HTML\"\n  assert_format %(<<-HTML\\n  \\#{\"foo\"}\\n  HTML)\n  assert_format %(<<-HTML\\n  \\#{__FILE__}\\n  HTML)\n  assert_format %(<<-HTML\\n  \\#{\"fo\\#{\"o\"}\"}\\n  HTML)\n  assert_format %(<<-HTML\\n  \\#{\"foo\"}\\#{1}\\n  HTML)\n  assert_format %(<<-HTML\\n  foo\\n  \\#{\"foo\"}\\n  HTML)\n  assert_format %(<<-HTML\\n  \\#{\"foo\"}\\n  \\#{\"bar\"}\\n  HTML)\n\n  assert_format \"  <<-HTML\\n   foo\\n  HTML\", \"<<-HTML\\n foo\\nHTML\"\n  assert_format \"  <<-HTML\\n   \\#{1}\\n  HTML\", \"<<-HTML\\n \\#{1}\\nHTML\"\n  assert_format \"  <<-HTML\\n  \\#{1} \\#{2}\\n  HTML\", \"<<-HTML\\n\\#{1} \\#{2}\\nHTML\"\n  assert_format \"  <<-HTML\\n  foo\\nHTML\", \"<<-HTML\\n  foo\\nHTML\"\n\n  assert_format \"<<-HTML\\n  hello \\n  HTML\"\n  assert_format \"<<-HTML\\n  hello \\n  world   \\n  HTML\"\n  assert_format \"  <<-HTML\\n    hello \\n    world   \\n    HTML\", \"<<-HTML\\n  hello \\n  world   \\n  HTML\"\n\n  assert_format \"x, y = <<-FOO, <<-BAR\\n  hello\\n  FOO\\n  world\\n  BAR\"\n  assert_format \"x, y, z = <<-FOO, <<-BAR, <<-BAZ\\n  hello\\n  FOO\\n  world\\n  BAR\\n  qux\\nBAZ\"\n\n  assert_format \"<<-FOO\\nFOO\"\n\n  assert_format %(<<-FOO\\n\\#{\"foo\"}\\nFOO)\n  assert_format %(<<-FOO\\n\\#{\"foo\"}bar\\nFOO)\n  assert_format %(<<-FOO\\nbar\\#{\"foo\"}\\nFOO)\n  assert_format %(<<-FOO\\nbar\\#{\"foo\"}bar\\nFOO)\n  assert_format %(<<-FOO\\nfoo\\n\\#{\"foo\"}\\nFOO)\n  assert_format %(<<-FOO\\nfoo\\n\\#{1}\\nFOO)\n\n  assert_format \"#!shebang\\n1 + 2\"\n\n  assert_format \"   {{\\n1 + 2 }}\", \"{{\\n  1 + 2\\n}}\"\n  assert_format \"   {{\\n1 + 2\\n   }}\", \"{{\\n  1 + 2\\n}}\"\n  assert_format \"   {%\\na = 1 %}\", \"{%\\n  a = 1\\n%}\"\n  assert_format \"   {%\\na = 1\\n   %}\", \"{%\\n  a = 1\\n%}\"\n\n  # Multi-line macro expression with comment as first line (issue #14450)\n  assert_format \"{%\\n  # comment\\n  a = 1\\n%}\"\n  assert_format \"{%\\n  # comment 1\\n  # comment 2\\n  a = 1\\n%}\"\n  assert_format \"{{\\n  # comment\\n  a + 1\\n}}\"\n\n  assert_format \"macro foo\\n  {{\\n1 + 2 }}\\nend\", \"macro foo\\n  {{\\n    1 + 2\\n  }}\\nend\"\n  assert_format \"macro foo\\n  def bar\\n    {{\\n      1 + 2\\n    }}\\n  end\\nend\"\n  assert_format \"foo &.[]\"\n  assert_format \"foo &.[](1, 2)\"\n  assert_format \"foo &.[](  1, 2  )\", \"foo &.[](1, 2)\"\n  assert_format \"foo &.[]?\"\n  assert_format \"foo &.[]?(1, 2)\"\n  assert_format \"foo &.[]?(  1, 2  )\", \"foo &.[]?(1, 2)\"\n  assert_format \"foo &.[]=(1, 2)\"\n  assert_format \"foo &.[]=(  1, 2  )\", \"foo &.[]=(1, 2)\"\n\n  assert_format \"foo &.@bar\"\n  assert_format \"foo(&.@bar)\"\n\n  assert_format \"foo[&.bar]\"\n  assert_format \"foo[1, &.bar]\"\n  assert_format \"foo[x: 1, &.bar]\"\n  assert_format \"foo[&.bar]?\"\n  assert_format \"foo[1, &.bar]?\"\n  assert_format \"foo[x: 1, &.bar]?\"\n  assert_format \"foo[&.bar] = 1\"\n  assert_format \"foo[1, &.bar] = 1\"\n  assert_format \"foo[x: 1, &.bar] = 1\"\n  assert_format \"foo[&.bar] ||= 1\"\n  assert_format \"foo[1, &.bar] ||= 1\"\n  assert_format \"foo[x: 1, &.bar] ||= 1\"\n\n  assert_format \"foo.[]\"\n  assert_format \"foo.[1]\"\n  assert_format \"foo.[] = 1\"\n  assert_format \"foo.[1, 2] = 3\"\n\n  %w(<= == >= != []= ===).each do |operator|\n    assert_format \"1.#{operator} { 3 }\"\n    assert_format \"1.#{operator}() { 3 }\"\n    assert_format \"1.#{operator}(2) { 3 }\"\n    assert_format \"1.#{operator} do\\nend\"\n  end\n\n  assert_format \"@foo : Int32 # comment\\n\\ndef foo\\nend\"\n  assert_format \"getter foo # comment\\n\\ndef foo\\nend\"\n  assert_format \"getter foo : Int32 # comment\\n\\ndef foo\\nend\"\n\n  assert_format \"a &.b.as C\", \"a &.b.as C\"\n  assert_format \"a &.b.c.as C\", \"a &.b.c.as C\"\n  assert_format \"a(&.b.c.as C)\", \"a(&.b.c.as C)\"\n\n  assert_format \"a &.b.as(C)\"\n  assert_format \"a &.b.c.as(C)\"\n  assert_format \"a(&.b.c.as(C))\"\n\n  assert_format \"foo : self?\"\n  assert_format \"foo : self? | A\"\n  assert_format \"foo : (self)?\"\n\n  assert_format \"foo : (A) | D\"\n  assert_format \"foo : (F(A)) | D\"\n  assert_format \"foo : (   A  |  B   )\", \"foo : (A | B)\"\n\n  # #11179\n  assert_format \"foo : Pointer(Foo)*\"\n  assert_format \"foo : Foo*****\"\n  assert_format \"foo : Foo * * * * *\", \"foo : Foo*****\"\n  assert_format \"foo : StaticArray(Foo, 12)[34]\"\n\n  assert_format \"def   foo(x   :  (A | B)) \\n  end\", \"def foo(x : (A | B))\\nend\"\n  assert_format \"foo : (String -> String?) | (String)\"\n  assert_format \"foo : (Array(String)?) | String\"\n  assert_format \"foo : (String -> Array(String)?) | (String -> Array(String)) | Nil\"\n  assert_format \"module Readline\\n  @@completion_proc : (String -> Array(String)?) | (String -> Array(String)) | Nil\\nend\"\n  assert_format \"alias A = (B(C, (C | D)) | E)\"\n  assert_format \"alias A = ((B(C | D) | E) | F)\"\n  assert_format \"alias A = ({A, (B)})\"\n  assert_format \"alias A = (   A  |  B   )\", \"alias A = (A | B)\"\n\n  assert_format \"foo : A(B)\\nbar : C\"\n  assert_format \"foo : (A -> B)\\nbar : C\"\n  assert_format \"def foo(x : A(B), y)\\nend\"\n  assert_format \"alias X = (A, B) ->\\nbar : C\"\n  assert_format \"def foo : A(B)\\n  nil\\nend\"\n  assert_format \"def foo : (A, B) ->\\n  nil\\nend\"\n  assert_format \"def foo : (A | B(C))\\n  nil\\nend\"\n  assert_format \"def foo : A | B(C)\\n  nil\\nend\"\n  assert_format \"def foo(x : (   A  |  B   )) : (   A  |  B   )\\nend\", \"def foo(x : (A | B)) : (A | B)\\nend\"\n\n  describe \"ProcNotation\" do\n    assert_format \"G_(A ->)\"\n    assert_format \"G_((A ->))\"\n    assert_format \"G_((A) ->)\"\n\n    assert_format \"G_(A, B -> R)\"\n    pending { assert_format \"G_((A), B -> R)\" }\n    assert_format \"G_((A, B -> R))\"\n    assert_format \"G_((A, B) -> R)\"\n    pending { assert_format \"G_(((A), B) -> R)\" }\n    pending { assert_format \"G_((((A), B) -> R))\" }\n\n    assert_format \"G_((A, B ->) | S)\"\n\n    assert_format \"G_(A, (B -> R))\"\n    assert_format \"G_(A, ->)\"\n    assert_format \"G_(A, (->))\"\n    pending { assert_format \"G_(A, () ->)\" } # #16741\n    assert_format \"G_(A, -> R)\"\n    assert_format \"G_(-> R)\"\n    pending { assert_format \"G_(() -> R)\" } # #16741\n\n    assert_format \"G_(A -> R | S)\"\n    assert_format \"G_((A -> R | S))\"\n    assert_format \"G_((A) -> R | S)\"\n\n    assert_format \"G_((A -> R) | S)\"\n\n    assert_format \"G_(A | B -> R)\"\n    assert_format \"G_((A | B) -> C)\"\n    assert_format \"G_(A | (B -> C))\"\n  end\n\n  assert_format \"foo &.bar.is_a?(Baz)\"\n  assert_format \"foo &.bar.responds_to?(:baz)\"\n\n  assert_format \"foo &.nil?\"\n  assert_format \"foo &.bar.nil?\"\n  assert_format \"foo &.nil?()\", \"foo &.nil?\"\n  assert_format \"foo &.bar.nil?()\", \"foo &.bar.nil?\"\n\n  assert_format \"1 if nil?\\na.b + c\"\n\n  assert_format \"foo(<<-X,\\na\\nX\\n  1)\"\n  assert_format \"def bar\\n  foo(<<-X,\\n  a\\n  X\\n    1)\\nend\"\n  assert_format %(run(\"a\", 1))\n\n  assert_format \"foo.bar # comment\\n  .baz\"\n  assert_format \"foo.bar(1) # comment\\n  .baz\"\n\n  assert_format \"foo[bar.baz]\\n  .qux\"\n\n  assert_format \"bla.select(&.all?{ |x| x } )\", \"bla.select(&.all? { |x| x })\"\n  assert_format \"def foo\\n  <<-FOO\\n  foo \\#{1}\\n  FOO\\nend\"\n\n  assert_format \"@x : A(B | C)?\"\n\n  assert_format \"page= <<-HTML\\n  foo\\nHTML\", \"page = <<-HTML\\n  foo\\nHTML\"\n  assert_format \"page= <<-HTML\\n  \\#{1}foo\\nHTML\", \"page = <<-HTML\\n  \\#{1}foo\\nHTML\"\n\n  assert_format \"self.as(Int32)\"\n  assert_format \"foo.as ( Int32* )\", \"foo.as(Int32*)\"\n  assert_format \"foo.as   Int32*\", \"foo.as Int32*\"\n  assert_format \"foo.as(T).bar\"\n  assert_format \"foo &.as(T)\"\n  assert_format \"foo &.bar.as(T)\"\n  assert_format \"foo &.as(T).bar\"\n  assert_format \"foo &.as?(T).bar\"\n  assert_format \"foo &.is_a?(T).bar\"\n  assert_format \"foo &.responds_to?(:foo).bar\"\n\n  assert_format \"foo.as? ( Int32* )\", \"foo.as?(Int32*)\"\n  assert_format \"foo.as?   Int32*\", \"foo.as? Int32*\"\n  assert_format \"foo.as?(T).bar\"\n  assert_format \"foo &.as?(T)\"\n  assert_format \"foo &.bar.as?(T)\"\n\n  assert_format \"def foo(x, *, z)\\nend\"\n  assert_format \"macro foo(x, *, z)\\nend\"\n\n  assert_format \"def foo(x, *, y, **z)\\nend\"\n  assert_format \"def foo(**z)\\nend\"\n  assert_format \"def foo(*y, **z)\\nend\"\n  assert_format \"def foo(**z, &block)\\nend\"\n  assert_format \"def foo(x, **z)\\nend\"\n  assert_format \"def foo(x, **z, &block)\\nend\"\n  assert_format \"def foo(**z : Foo)\\nend\"\n\n  assert_format \"def foo(x y)\\nend\"\n  assert_format \"def foo(x @y)\\nend\"\n  assert_format \"def foo(x @@y)\\nend\"\n\n  assert_format \" Array( {x:  Int32,   y:  String } )\", \"Array({x: Int32, y: String})\"\n\n  assert_format \"foo { | a, ( b , c ) | a + b + c }\", \"foo { |a, (b, c)| a + b + c }\"\n  assert_format \"foo { | a, ( b , c, ), | a + b + c }\", \"foo { |a, (b, c)| a + b + c }\"\n  assert_format \"foo { | a, ( _ , c ) | a + c }\", \"foo { |a, (_, c)| a + c }\"\n  assert_format \"foo { | a, ( b , (c, d) ) | a + b + c }\", \"foo { |a, (b, (c, d))| a + b + c }\"\n  assert_format \"foo { | ( a, *b , c ) | a }\", \"foo { |(a, *b, c)| a }\"\n\n  assert_format \"def foo\\n  {{@type}}\\nend\"\n\n  assert_format \"[\\n  1, # foo\\n  3,\\n]\"\n  assert_format \"[\\n  1, 2, # foo\\n  3,\\n]\"\n  assert_format \"[\\n  1, 2, # foo\\n  3, 4,\\n]\"\n  assert_format \"foo { |x, *y| }\"\n\n  assert_format %(foo \"bar\": 1, \"baz qux\": 2)\n  assert_format %(foo(\"bar\": 1, \"baz qux\": 2))\n\n  assert_format %(Foo(\"bar\": Int32, \"baz qux\": Float64))\n  assert_format %(x : {\"foo bar\": Int32})\n\n  assert_format %(def foo(\"bar baz\" qux)\\nend)\n  assert_format \"{ {{FOO}}, nil}\", \"{ {{FOO}}, nil }\"\n  assert_format \"{ {% begin %}1{% end %}, nil }\"\n  assert_format \"{ {% for x in 1..2 %}3{% end %}, nil }\"\n  assert_format \"{ %() }\"\n  assert_format \"{ %w() }\"\n  assert_format \"{ {1}.foo, 2 }\"\n\n  assert_format \"String?\"\n  assert_format \"String???\"\n  assert_format \"Foo::Bar?\"\n  assert_format \"Foo::Bar(T, U?)?\"\n  assert_format \"Union(Foo::Bar?, Baz?, Qux(T, U?))\"\n\n  assert_format \"lib Foo\\n  {% if 1 %}\\n    fun foo\\n  {% end %}\\nend\\n\\nmacro bar\\n  1\\nend\"\n\n  assert_format \"x : Int32 |\\nString\", \"x : Int32 |\\n    String\"\n\n  assert_format %(foo(\"bar\" \\\\\\n\"baz\")), %(foo(\"bar\" \\\\\\n    \"baz\"))\n  assert_format %(foo(\"b\\#{1}\" \\\\\\n\"baz\")), %(foo(\"b\\#{1}\" \\\\\\n    \"baz\"))\n\n  assert_format \"foo(A |\\nB |\\nC)\", \"foo(A |\\n    B |\\n    C)\"\n  assert_format \"def foo\\n  case x\\n  # z\\n  when 1\\n  end\\nend\"\n  assert_format \"foo { |x| (x).a }\"\n  assert_format \"def foo(\\n  &block\\n)\\nend\"\n  assert_format \"def foo(a,\\n        &block)\\nend\"\n  assert_format \"def foo(\\n  a,\\n  &block\\n)\\nend\"\n  assert_format \"def foo(a,\\n        *b)\\nend\"\n  assert_format \"def foo(a, # comment\\n        *b)\\nend\", \"def foo(a, # comment\\n        *b)\\nend\"\n  assert_format \"def foo(a,\\n        **b)\\nend\"\n  assert_format \"def foo(\\n  **a\\n)\\n  1\\nend\", \"def foo(\\n  **a,\\n)\\n  1\\nend\"\n  assert_format \"def foo(**a,)\\n  1\\nend\", \"def foo(**a)\\n  1\\nend\"\n  assert_format \"def foo(\\n  **a # comment\\n)\\n  1\\nend\", \"def foo(\\n  **a, # comment\\n)\\n  1\\nend\"\n  assert_format \"def foo(\\n  **a\\n  # comment\\n)\\n  1\\nend\", \"def foo(\\n  **a,\\n  # comment\\n)\\n  1\\nend\"\n  assert_format \"def foo(\\n  **a\\n\\n  # comment\\n)\\n  1\\nend\", \"def foo(\\n  **a,\\n\\n  # comment\\n)\\n  1\\nend\"\n  assert_format \"def foo(**b, # comment\\n        &block)\\nend\"\n  assert_format \"def foo(a, **b, # comment\\n        &block)\\nend\"\n\n  assert_format \"1 +\\n  # foo\\n  2\"\n  assert_format \"1 +\\n  # foo\\n  2\"\n  assert_format \"1 ||\\n  # foo\\n  2\"\n  assert_format \"foo(1 ||\\n    # foo\\n    2)\"\n\n  assert_format \"x = a do\\n  1 ||\\n    2\\nend\"\n  assert_format \"case 1\\nwhen a; 2\\nelse; b\\nend\", \"case 1\\nwhen a; 2\\nelse    b\\nend\"\n  assert_format \"case 1\\nwhen a; 2\\nelse; ; b\\nend\", \"case 1\\nwhen a; 2\\nelse    b\\nend\"\n\n  assert_format \"as Foo\"\n  assert_format \"as? Foo\"\n  assert_format \"is_a? Foo\"\n  assert_format \"responds_to? :foo\"\n  assert_format \"nil?\"\n\n  assert_format \"Union(Int32, String)?\"\n\n  assert_format \"<<-HEREDOC\\n  \\#{foo}\\n  H\\#{bar}\\n  HEREDOC\"\n  assert_format \"foo[a, b: 2]\"\n\n  assert_format \"def a\\n  {\\n    1, # x\\n    # y\\n  }\\nend\"\n  assert_format \"def a\\n  [\\n    1, # x\\n    # y\\n  ]\\nend\"\n  assert_format \"def a\\n  b(\\n    1, # x\\n    # y\\n  )\\nend\"\n  assert_format \"def a\\n  b(\\n    1, # x\\n    # y\\n    2\\n  )\\nend\"\n  assert_format \"def a\\n  b(\\n    a: 1, # x\\n    # y\\n    b: 2\\n  )\\nend\"\n  assert_format \"def a\\n  b(\\n    1, # x\\n    # y\\n    a: 1, # x\\n    # y\\n    b: 2 # z\\n  )\\nend\"\n\n  assert_format \"def foo(a, **b : Int32)\\nend\"\n\n  assert_format \"foo\\n  \\nbar\", \"foo\\n\\nbar\"\n\n  assert_format \"\\\"\\\" + <<-END\\n  bar\\n  END\"\n\n  assert_format \"1 + \\\\\\n2\", \"1 + \\\\\\n  2\"\n  assert_format \"1 + \\\\\\n2 + \\\\\\n3\", \"1 + \\\\\\n  2 + \\\\\\n  3\"\n  assert_format \"1 + \\\\\\n2\\n3\", \"1 + \\\\\\n  2\\n3\"\n  assert_format \"1 \\\\\\n+ 2\", \"1 \\\\\\n  + 2\"\n  assert_format \"foo \\\\\\nbar\", \"foo \\\\\\n  bar\"\n  assert_format \"1 \\\\\\nif 2\", \"1 \\\\\\n  if 2\"\n  assert_format \"1 \\\\\\nrescue 2\", \"1 \\\\\\n  rescue 2\"\n  assert_format \"1 \\\\\\nensure 2\", \"1 \\\\\\n  ensure 2\"\n  assert_format \"foo bar, \\\\\\nbaz\", \"foo bar,\\n  baz\"\n  assert_format \"x 1, \\\\\\n  2\", \"x 1,\\n  2\"\n  assert_format \"begin\\n  1 + \\\\\\n    2\\n  3\\nend\"\n  assert_format \"begin\\n  1 \\\\\\n    + 2\\n  3\\nend\"\n  assert_format \"foo \\\\\\n  1,\\n  2\"\n  assert_format \"foo \\\\\\n  foo: 1,\\n  bar: 2\"\n  assert_format \"foo \\\\\\n  1,\\n  2\\n\\nbar \\\\\\n  foo: 1,\\n  bar: 2\"\n\n  assert_format \"alias X = ((Y, Z) ->)\"\n\n  assert_format \"def x(@y = ->(z) {})\\nend\", \"def x(@y = ->(z) { })\\nend\"\n\n  assert_format \"class X; annotation  FooAnnotation  ;  end ; end\", \"class X\\n  annotation FooAnnotation; end\\nend\"\n  assert_format \"class X\\n annotation  FooAnnotation  \\n  end \\n end\", \"class X\\n  annotation FooAnnotation\\n  end\\nend\"\n\n  assert_format \"macro foo\\n{% verbatim do %}1 + 2{% end %}\\nend\"\n  assert_format \"{% verbatim do %}{{1}} + {{2}}{% end %}\"\n  assert_format \"foo({% verbatim do %}{{1}} + {{2}}{% end %})\"\n\n  assert_format \"{% foo <<-X\\nbar\\nX\\n%}\"\n  assert_format \"foo do\\n  {% foo <<-X\\n  bar\\n  X\\n  %}\\nend\"\n  assert_format \"{{ foo <<-X\\nbar\\nX\\n}}\"\n  assert_format \"foo do\\n  {{ foo <<-X\\n  bar\\n  X\\n  }}\\nend\"\n  assert_format \"[foo <<-X\\nbar\\nX\\n]\"\n  assert_format \"foo do\\n  [foo <<-X\\n  bar\\n  X\\n  ]\\nend\"\n  assert_format \"{1 => foo <<-X\\nbar\\nX\\n}\"\n  assert_format \"foo do\\n  {1 => foo <<-X\\n  bar\\n  X\\n  }\\nend\"\n  assert_format \"bar do\\n  foo <<-X\\n  bar\\n  X\\nend\"\n  assert_format \"foo do\\n  bar do\\n    foo <<-X\\n    bar\\n    X\\n  end\\nend\"\n  assert_format \"call(foo <<-X\\nbar\\nX\\n)\"\n  assert_format \"bar do\\n  call(foo <<-X\\n  bar\\n  X\\n  )\\nend\"\n\n  assert_format \"[\\n  <<-EOF,\\n  foo\\n  EOF\\n]\"\n  assert_format \"[\\n  <<-EOF,\\n  foo\\n  EOF\\n  <<-BAR,\\n  bar\\n  BAR\\n]\"\n  assert_format \"Hash{\\n  foo => <<-EOF,\\n  foo\\n  EOF\\n}\"\n  assert_format \"Hash{\\n  foo => <<-EOF,\\n  foo\\n  EOF\\n  bar => <<-BAR,\\n  bar\\n  BAR\\n}\"\n  assert_format \"Hash{\\n  foo => <<-EOF\\n  foo\\n  EOF\\n}\"\n  assert_format \"{\\n  <<-KEY => 1,\\n  key\\n  KEY\\n}\"\n\n  # #10734\n  assert_format \" <<-EOF\\n 1\\nEOF\", \"<<-EOF\\n 1\\nEOF\"\n  assert_format \"  <<-EOF\\n   1\\n EOF\", \"<<-EOF\\n  1\\nEOF\"\n  assert_format \"x =  <<-EOF\\n 1\\nEOF\", \"x = <<-EOF\\n 1\\nEOF\"\n  assert_format \"  <<-EOF\\n 1\\n  2\\n EOF\", \"<<-EOF\\n1\\n 2\\nEOF\"\n\n  # #10735\n  assert_format \"{\\n  variables => true,\\n  query     => <<-HEREDOC,\\n    foo\\n  HEREDOC\\n}\"\n  assert_format \"{\\n  variables => true,\\n  query     => <<-HEREDOC,\\n    foo\\n  HEREDOC\\n  foo => true,\\n}\"\n  assert_format \"{\\n  query     => <<-HEREDOC,\\n    foo\\n  HEREDOC\\n}\", \"{\\n  query => <<-HEREDOC,\\n    foo\\n  HEREDOC\\n}\"\n  assert_format \"begin\\n  query = <<-HEREDOC\\n    foo\\n  HEREDOC\\nend\"\n\n  assert_format \"begin 0[1] rescue 2 end\"\n  assert_format \"begin\\n 0[1] rescue 2 end\", \"begin 0[1] rescue 2 end\"\n\n  assert_format \"{%\\n  if 1\\n    2\\n  end\\n%}\"\n\n  assert_format <<-CRYSTAL\n    # ```text\n    #  1  +  2\n    # ```\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    # ```text\n    # 1 + 2\n    # ```\n    #\n    # ```\n    # 3 + 4\n    # ```\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    X(typeof(begin\n      e.is_a?(Y) ? 1 : 2\n    end))\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    X(typeof(begin\n      e.is_a?(Y)\n    end))\n    CRYSTAL\n\n  # Keep trailing spaces in macros.\n  assert_format(\n    \"macro foo\\n\" +\n    \"  <<-FOO\\n\" +\n    \"    hello  \\n\" +\n    \"  FOO\\n\" +\n    \"end\"\n  )\n  assert_format(\n    \"{% verbatim do %}\\n\" +\n    \"  <<-FOO\\n\" +\n    \"    hello  \\n\" +\n    \"  FOO\\n\" +\n    \"{% end %}\"\n  )\n  assert_format(\n    \"{% if true %}\\n\" +\n    \"  <<-FOO\\n\" +\n    \"    hello  \\n\" +\n    \"  FOO\\n\" +\n    \"{% end %}\"\n  )\n  assert_format(\n    \"{% for a in %w() %}\\n\" +\n    \"  <<-FOO\\n\" +\n    \"    hello  \\n\" +\n    \"  FOO\\n\" +\n    \"{% end %}\"\n  )\n  assert_format(\n    \"macro foo\\n\" +\n    \"  {{x}}\" +\n    \"  <<-FOO\\n\" +\n    \"    hello  \\n\" +\n    \"  FOO\\n\" +\n    \"end\"\n  )\n\n  # But remove trailing space in macro expression.\n  assert_format(\n    \"macro foo\\n\" +\n    \"  1  \\n\" +\n    \"  {{  \\n\" +\n    \"    42  \\n\" +\n    \"  }}  \\n\" +\n    \"  2  \\n\" +\n    \"end\",\n    \"macro foo\\n\" +\n    \"  1  \\n\" +\n    \"  {{\\n\" +\n    \"    42\\n\" +\n    \"  }}  \\n\" +\n    \"  2  \\n\" +\n    \"end\"\n  )\n\n  # #7443\n  assert_format \"long_variable_name = [{\\n  :foo => 1,\\n}, {\\n  :bar => 2,\\n}]\"\n  assert_format \"long_variable_name = [\\n  {\\n    :foo => 1,\\n  }, {\\n    :bar => 2,\\n  },\\n]\"\n  assert_format \"long_variable_name = [\\n  {\\n    :foo => 1,\\n  },\\n  {\\n    :bar => 2,\\n  },\\n]\"\n  assert_format \"long_variable_name = [1, 2, 3,\\n                      4, 5, 6]\"\n  assert_format \"long_variable_name = [1, 2, 3, # foo\\n                      4, 5, 6]\"\n\n  # #7599\n  assert_format \"def foo # bar\\n  # baz\\nend\"\n  assert_format \"def foo(x) # bar\\n  # baz\\nend\"\n  assert_format \"def foo(x) : Int32 # bar\\n  # baz\\nend\"\n  assert_format \"def foo(x) forall T # bar\\n  # baz\\nend\"\n\n  # #7608\n  assert_format \"enum E\\n  A # hello\\n  B # hello;  C # hello\\nend\"\n\n  # #7631\n  assert_format \"x.try &.[] 123\"\n  assert_format \"x.try &.[]= 123, 456\"\n\n  # #7684\n  assert_format \"foo(\\n  <<-HERE,\\n  hello\\n  HERE\\n  1,\\n)\"\n  assert_format \"foo(\\n  <<-HERE,\\n  hello\\n  HERE\\n  foo: 1,\\n)\"\n  assert_format \"foo(\\n  <<-HERE,\\n  hello\\n  HERE\\n  # foo\\n  foo: 1,\\n)\"\n\n  # #7614\n  assert_format \"@[ Foo ]\\ndef foo\\nend\", \"@[Foo]\\ndef foo\\nend\"\n  assert_format \"@[ Foo(foo: 1) ]\\ndef foo\\nend\", \"@[Foo(foo: 1)]\\ndef foo\\nend\"\n  assert_format \"@[Foo(\\n  foo: 1\\n)]\\ndef foo\\nend\"\n  assert_format \"@[Foo(\\n  foo: 1,\\n)]\\ndef foo\\nend\"\n\n  # #7550\n  assert_format \"foo\\n  .bar(\\n    1\\n  )\"\n  assert_format \"foo\\n  .bar\\n  .baz(\\n    1\\n  )\"\n  assert_format \"foo.bar\\n  .baz(\\n    1\\n  )\"\n\n  assert_format <<-CRYSTAL,\n    def foo\n      {% if flag?(:foo) %}\n        foo  +  bar\n      {% else %}\n        baz  +  qux\n      {% end %}\n    end\n    CRYSTAL\n    <<-CRYSTAL\n    def foo\n      {% if flag?(:foo) %}\n        foo + bar\n      {% else %}\n        baz + qux\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL,\n    def foo\n      {% for x in y %}\n        foo  +  bar\n      {% end %}\n    end\n    CRYSTAL\n    <<-CRYSTAL\n    def foo\n      {% for x in y %}\n        foo + bar\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL,\n    x = {% if flag?(:foo) %}\n          foo  +  bar\n        {% else %}\n          baz  +  qux\n        {% end %}\n    CRYSTAL\n    <<-CRYSTAL\n    x = {% if flag?(:foo) %}\n          foo + bar\n        {% else %}\n          baz + qux\n        {% end %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {% if flag?(:freebsd) %}\n      1 + 2\n    {% end %}\n\n    case x\n    when 1234 then 1\n    else           x\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {% if z %}\n      1\n    {% end %}\n\n    def foo\n      z =\n        123 + # foo\n          4   # bar\n\n      1\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    lib LibFoo\n      {% begin %}\n        fun foo : Int32\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    lib LibFoo\n      struct Bar\n        {% begin %}\n          x : Int32\n        {% end %}\n      end\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    enum Foo\n      {% begin %}\n        A\n        B\n        C\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    a = 1\n    b, c = 2, 3\n    {% begin %}\n      a |= 1\n      b |= 2\n      c |= 3\n    {% end %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    lib LibFoo\n      {% begin %}\n        fun x = y(Int32)\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {% begin %}\n      \"\n        foo\"\n    {% end %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL,\n    {% if z %}\n      class   Foo\n      end\n    {% end %}\n    CRYSTAL\n    <<-CRYSTAL\n    {% if z %}\n      class Foo\n      end\n    {% end %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {% if true %}\n      # x\n    {% end %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {% if true %}\n      # x\n      # y\n    {% end %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {% if true %}\n      # x\n      #\n    {% end %}\n\n    # ```\n    # x\n    # ```\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    def foo(x)\n      {% if true %}\n        x = x + 2\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    def foo(x)\n      {% if true %}\n        # comment\n        Foo = 1\n        B   = 2\n      {% end %}\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    def foo(x)\n      {% if true %}\n        \\\\{% if true %}\n          x = 1\n        \\\\{% else %}\n          x = 2\n        \\\\{% end %}\n        \\\\{% for x in y %}\n          x = 1\n        \\\\{% end %}\n        \\\\{{x}}\n        \\\\{% x %}\n      {% end %}\n    end\n    CRYSTAL\n\n  it \"gives proper line number in syntax error inside macro\" do\n    source = <<-CRYSTAL\n      a = 1\n      b = 2\n\n      {% begin %}\n        c |= 3\n      {% end %}\n    CRYSTAL\n\n    ex = expect_raises(Crystal::SyntaxException) do\n      Crystal.format(source)\n    end\n    ex.line_number.should eq(5)\n  end\n\n  # #8197\n  assert_format <<-CRYSTAL\n    foo\n      .foo1(bar\n        .bar1\n        .bar2)\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    foo.foo1(\n      bar\n        .bar1\n        .bar2)\n    CRYSTAL\n\n  assert_format \"[] of (Array(T))\"\n  assert_format \"[] of (((Array(T))))\"\n\n  assert_format <<-CRYSTAL\n    macro foo # bar\n      baz\n    end\n    CRYSTAL\n\n  assert_format \"a.!\"\n  assert_format \"a &.!\"\n  assert_format \"a &.a.!\"\n  assert_format \"a &.!.!\"\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    ->{\n      # first comment\n      puts \"hi\"\n      # second comment\n    }\n    CRYSTAL\n    -> {\n      # first comment\n      puts \"hi\"\n      # second comment\n    }\n    CRYSTAL\n\n  # #9014\n  assert_format <<-CRYSTAL\n    {%\n      unless true\n        1\n      end\n    %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {%\n      unless true\n        1\n      else\n        2\n      end\n    %}\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    {%\n      if true\n        1\n      else\n        2\n      end\n    %}\n    CRYSTAL\n\n  # #4626\n  assert_format <<-CRYSTAL\n    1 # foo\n    / 1 /\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    1 # foo\n    / \\#{1} /\n    CRYSTAL\n\n  assert_format <<-CRYSTAL,\n    def foo\n      # Comment\n\n\n    end\n    CRYSTAL\n    <<-CRYSTAL\n    def foo\n      # Comment\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL,\n    def foo\n      1\n      # Comment\n\n\n    end\n    CRYSTAL\n    <<-CRYSTAL\n    def foo\n      1\n      # Comment\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    def foo\n      1\n    end\n\n    # Comment\n\n    def bar\n      2\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    require \"foo\"\n\n    @x : Int32\n\n    class Bar\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    x = <<-FOO\n      hello\n      FOO\n\n    def bar\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    begin\n      1\n      # Comment\n\n\n    end\n    CRYSTAL\n    begin\n      1\n      # Comment\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL, <<-CRYSTAL\n    begin\n      # Comment\n\n\n    end\n    CRYSTAL\n    begin\n      # Comment\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    foo 1, # comment\n      do\n      end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    foo 1, # comment\n      # bar\n      do\n      end\n    CRYSTAL\n\n  # #10190\n  assert_format <<-CRYSTAL\n    foo(\n      1,\n    ) do\n      2\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    foo(\n      1,\n    ) {\n      2\n    }\n    CRYSTAL\n\n  # #11079\n  assert_format <<-CRYSTAL\n    foo = [1, [2,\n               3],\n           4]\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    foo = {1, {2,\n               3},\n           4}\n    CRYSTAL\n\n  # #10817\n  assert_format <<-CRYSTAL\n    def func # comment\n      (1 + 2) / 3\n    end\n    CRYSTAL\n\n  # #10943\n  assert_format <<-CRYSTAL\n    foo do # a\n      # b\n      bar\n    end\n    CRYSTAL\n\n  # #10499\n  assert_format <<-CRYSTAL\n    case nil\n    else nil; nil # comment\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    case nil\n    else nil; nil\n    # comment\n    end\n    CRYSTAL\n\n  # #12493\n  assert_format <<-CRYSTAL\n    select\n    # when foo\n    when bar\n      break\n    end\n    CRYSTAL\n\n  assert_format <<-CRYSTAL\n    select # some comment\n    when bar\n      break\n    end\n    CRYSTAL\n\n  # #12378\n  assert_format <<-CRYSTAL\n    macro foo\n      macro bar\n        \\\\{% begin %}\n          \\\\\\\\{% puts %}\n        \\\\{% end %}\n      end\n    end\n    CRYSTAL\n\n  # #12964\n  assert_format <<-CRYSTAL\n    begin\n      begin\n        a\n        # b\n      end\n    end\n    CRYSTAL\n\n  it do\n    expect_raises(Crystal::SyntaxException) do\n      Crystal.format <<-CRYSTAL\n        lib A\n          struct B\n            {% begin %}\n              x : Int32\n              else\n            {% end %}\n          end\n        end\n        CRYSTAL\n    end\n  end\n\n  # #14256\n  assert_format <<-CRYSTAL\n    foo bar # comment\n\n    # doc\n    def baz; end\n    CRYSTAL\n\n  # 15180\n  assert_format <<-CRYSTAL\n    x = uninitialized Foo\n    {% begin %}\n      x = foo(x)\n    {% end %}\n    CRYSTAL\n\n  # CVE-2021-42574\n  describe \"Unicode bi-directional control characters\" do\n    ['\\u202A', '\\u202B', '\\u202C', '\\u202D', '\\u202E', '\\u2066', '\\u2067', '\\u2068', '\\u2069'].each do |char|\n      assert_format %(\"#{char}\"), %(\"#{char.unicode_escape}\")\n      assert_format %(\"\\\\c#{char}\"), %(\"c#{char.unicode_escape}\")\n      assert_format %(\"#{char}\\#{1}\"), %(\"#{char.unicode_escape}\\#{1}\")\n      assert_format %(\"\\\\c#{char}\\#{1}\"), %(\"c#{char.unicode_escape}\\#{1}\")\n      assert_format %(%(#{char})), %(%(#{char.unicode_escape}))\n      assert_format %(%Q(#{char})), %(%Q(#{char.unicode_escape}))\n      assert_format %(%Q(#{char}\\#{1})), %(%Q(#{char.unicode_escape}\\#{1}))\n      assert_format %(<<-EOS\\n#{char}\\nEOS), %(<<-EOS\\n#{char.unicode_escape}\\nEOS)\n      assert_format %(<<-EOS\\n#{char}\\#{1}\\nEOS), %(<<-EOS\\n#{char.unicode_escape}\\#{1}\\nEOS)\n      assert_format %(def foo(\"#{char}\" x)\\nend), %(def foo(\"#{char.unicode_escape}\" x)\\nend)\n      assert_format %(foo(\"#{char}\": 1)), %(foo(\"#{char.unicode_escape}\": 1))\n      assert_format %(NamedTuple(\"#{char}\": Int32)), %(NamedTuple(\"#{char.unicode_escape}\": Int32))\n      assert_format %({\"#{char}\": 1}), %({\"#{char.unicode_escape}\": 1})\n\n      # the following contexts do not accept escape sequences, escaping these\n      # control characters would alter the meaning of the source code\n      assert_format %(/#{char}/)\n      assert_format %(%r(#{char}))\n      assert_format %(%q(#{char}))\n      assert_format %(%w(#{char}))\n      assert_format %(%i(#{char}))\n      assert_format %(/#{char}\\#{1}/)\n      assert_format %(%r(#{char}\\#{1}))\n      assert_format %(<<-'EOS'\\n#{char}\\nEOS)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/autocast_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"autocast\" do\n    it \"autocasts symbol to enum\" do\n      interpret(<<-CRYSTAL).should eq(1)\n          enum Color\n            Red\n            Green\n            Blue\n          end\n\n          def foo(x : Color)\n            x\n          end\n\n          c = foo :green\n          c.value\n        CRYSTAL\n    end\n\n    it \"autocasts number literal to integer\" do\n      interpret(<<-CRYSTAL).should eq(12)\n          def foo(x : UInt8)\n            x\n          end\n\n          foo(12)\n        CRYSTAL\n    end\n\n    it \"autocasts number literal to float\" do\n      interpret(<<-CRYSTAL).should eq(12.0)\n          def foo(x : Float64)\n            x\n          end\n\n          foo(12)\n        CRYSTAL\n    end\n\n    it \"autocasts symbol to enum in multidispatch (#11782)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        enum Color\n          Red\n          Green\n          Blue\n        end\n\n        class Foo\n          def foo(x : Color)\n            x\n          end\n        end\n\n        class Bar\n          def foo(x : Color)\n            x\n          end\n        end\n\n        (Foo.new || Bar.new).foo(:green).value\n        CRYSTAL\n    end\n\n    it \"autocasts int in multidispatch\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class Foo\n          def foo(x : Int64)\n            x\n          end\n        end\n\n        class Bar\n          def foo(x : Int64)\n            x\n          end\n        end\n\n        (Foo.new || Bar.new).foo(1)\n        CRYSTAL\n    end\n\n    it \"autocasts symbol to enum in ivar initializer (#12216)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n          enum Color\n            Red\n            Green\n            Blue\n          end\n\n          class Foo\n            @color : Color = :blue\n\n            def color\n              @color\n            end\n          end\n\n          foo = Foo.new\n          foo.color.value\n        CRYSTAL\n    end\n\n    it \"autocasts integer var to integer (#12560)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo(x : Int64)\n          x\n        end\n\n        x = 1_i32\n        foo(x)\n        CRYSTAL\n    end\n\n    it \"autocasts integer var to float (#12560)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo(x : Float64)\n          x\n        end\n\n        x = 1_i32\n        foo(x)\n        CRYSTAL\n    end\n\n    it \"autocasts float32 var to float64 (#12560)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo(x : Float64)\n          x\n        end\n\n        x = 1.0_f32\n        foo(x)\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/blocks_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"blocks\" do\n    it \"interprets simplest block\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo\n          yield\n        end\n\n        a = 0\n        foo do\n          a += 1\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets block with multiple yields\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        def foo\n          yield\n          yield\n        end\n\n        a = 0\n        foo do\n          a += 1\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield return value\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo\n          yield\n        end\n\n        z = foo do\n          1\n        end\n        z\n      CRYSTAL\n    end\n\n    it \"interprets yield inside another block\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo\n          bar do\n            yield\n          end\n        end\n\n        def bar\n          yield\n        end\n\n        a = 0\n        foo do\n          a += 1\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield inside def with arguments\" do\n      interpret(<<-CRYSTAL).should eq(18)\n        def foo(x)\n          a = yield\n          a + x\n        end\n\n        a = foo(10) do\n          8\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield expression\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        def foo\n          yield 1\n        end\n\n        a = 1\n        foo do |x|\n          a += x\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield expressions\" do\n      interpret(<<-CRYSTAL).should eq(2 + 2*3 + 4*5)\n        def foo\n          yield 3, 4, 5\n        end\n\n        a = 2\n        foo do |x, y, z|\n          a += a * x + y * z\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"discards yield expression\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        def foo\n          yield 1\n        end\n\n        a = 2\n        foo do\n          a = 3\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"yields different values to form a union\" do\n      interpret(<<-CRYSTAL).should eq(5)\n        def foo\n          yield 1\n          yield 'a'\n        end\n\n        a = 2\n        foo do |x|\n          a +=\n            case x\n            in Int32\n              1\n            in Char\n              2\n            end\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"returns from block\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def foo\n          baz do\n            yield\n          end\n        end\n\n        def baz\n          yield\n        end\n\n        def bar\n          foo do\n            foo do\n              return 42\n            end\n          end\n\n          1\n        end\n\n        bar\n      CRYSTAL\n    end\n\n    it \"interprets next inside block\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        def foo\n          yield\n        end\n\n        a = 0\n        foo do\n          if a == 0\n            next 10\n          end\n          20\n        end\n      CRYSTAL\n    end\n\n    it \"interprets next inside block (union, through next)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        def foo\n          yield\n        end\n\n        a = 0\n        x = foo do\n          if a == 0\n            next 10\n          end\n          'a'\n        end\n\n        if x.is_a?(Int32)\n          x\n        else\n          20\n        end\n      CRYSTAL\n    end\n\n    it \"interprets next inside block (union, through normal exit)\" do\n      interpret(<<-CRYSTAL).should eq('a')\n        def foo\n          yield\n        end\n\n        a = 0\n        x = foo do\n          if a == 1\n            next 10\n          end\n          'a'\n        end\n\n        if x.is_a?(Char)\n          x\n        else\n          'b'\n        end\n      CRYSTAL\n    end\n\n    it \"interprets break inside block\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        def baz\n          yield\n        end\n\n        def foo\n          baz do\n            w = yield\n            w + 100\n          end\n        end\n\n        a = 0\n        foo do\n          if a == 0\n            break 20\n          end\n          20\n        end\n      CRYSTAL\n    end\n\n    it \"interprets break inside block (union, through break)\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        def foo\n          yield\n          'a'\n        end\n\n        a = 0\n        w = foo do\n          if a == 0\n            break 20\n          end\n          20\n        end\n        if w.is_a?(Int32)\n          w\n        else\n          30\n        end\n      CRYSTAL\n    end\n\n    it \"interprets break inside block (union, through normal flow)\" do\n      interpret(<<-CRYSTAL).should eq('a')\n        def foo\n          yield\n          'a'\n        end\n\n        a = 0\n        w = foo do\n          if a == 1\n            break 20\n          end\n          20\n        end\n        if w.is_a?(Char)\n          w\n        else\n          'b'\n        end\n      CRYSTAL\n    end\n\n    it \"interprets break inside block (union, through return)\" do\n      interpret(<<-CRYSTAL).should eq('a')\n        def foo\n          yield\n          return 'a'\n        end\n\n        a = 0\n        w = foo do\n          if a == 1\n            break 20\n          end\n          20\n        end\n        if w.is_a?(Char)\n          w\n        else\n          'b'\n        end\n      CRYSTAL\n    end\n\n    it \"interprets block with args that conflict with a local var\" do\n      interpret(<<-CRYSTAL).should eq(201)\n        def foo\n          yield 1\n        end\n\n        a = 200\n        x = 0\n\n        foo do |a|\n          x += a\n        end\n\n        x + a\n      CRYSTAL\n    end\n\n    it \"interprets block with args that conflict with a local var\" do\n      interpret(<<-CRYSTAL).should eq(216)\n        def foo\n          yield 1\n        end\n\n        def bar\n          yield 2\n        end\n\n        def baz\n          yield 3, 4, 5\n        end\n\n        # a: 0, 8\n        a = 200\n\n        # x: 8, 16\n        x = 0\n\n        # a: 16, 24\n        foo do |a|\n          x += a\n\n          # a: 24, 32\n          bar do |a|\n            x += a\n          end\n\n          # a: 24, 32\n          # b: 32, 40\n          # c: 40, 48\n          baz do |a, b, c|\n            x += a\n            x += b\n            x += c\n          end\n\n          x += a\n        end\n        x + a\n      CRYSTAL\n    end\n\n    it \"clears block local variables when calling block\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        def foo\n          yield 1\n        end\n\n        def bar\n          a = 1\n\n          foo do |b|\n            x = 1\n          end\n\n          foo do |b|\n            if a == 0 || b == 0\n              x = 10\n            end\n\n            return x\n          end\n        end\n\n        z = bar\n        if z.is_a?(Nil)\n          20\n        else\n          z\n        end\n        CRYSTAL\n    end\n\n    it \"clears block local variables when calling block (2)\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        def foo\n          yield\n        end\n\n        a = 0\n\n        foo do\n          x = 1\n        end\n\n        foo do\n          if 1 == 2\n            x = 1\n          end\n          a = x\n        end\n\n        if a\n          a\n        else\n          20\n        end\n        CRYSTAL\n    end\n\n    it \"captures non-closure block\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def capture(&block : Int32 -> Int32)\n          block\n        end\n\n        # This variable is needed in the test because it's also\n        # part of the block, even though it's not closured (it's in node.def.vars)\n        a = 100\n        b = capture { |x| x + 1 }\n        b.call(41)\n      CRYSTAL\n    end\n\n    it \"casts yield expression to block var type (not block arg type)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def foo\n          yield 42\n        end\n\n        def bar\n          foo do |x|\n            yield x\n            x = nil\n          end\n        end\n\n        a = 0\n        bar { |z| a = z }\n        a\n      CRYSTAL\n    end\n\n    it \"interprets with ... yield\" do\n      interpret(<<-CRYSTAL).should eq(31)\n        struct Int32\n          def plus(x : Int32)\n            self + x\n          end\n        end\n\n        def foo\n          with 10 yield 20\n        end\n\n        foo do |x|\n          1 + (plus x)\n        end\n      CRYSTAL\n    end\n\n    it \"interprets with ... yield with struct\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Foo\n          def initialize\n            @x = 1\n          end\n\n          def inc\n            @x += 1\n          end\n\n          def x\n            @x\n          end\n        end\n\n        def foo\n          with Foo.new yield\n        end\n\n        foo do\n          inc\n          x\n        end\n      CRYSTAL\n    end\n\n    it \"interprets with ... yield with extra arguments (#12296)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class Object\n          def itself\n            self\n          end\n        end\n\n        def build\n          with 1 yield 2\n        end\n\n        build do |t|\n          itself\n        end\n      CRYSTAL\n    end\n\n    it \"counts with ... yield scope in block args bytesize (#12316)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Object\n          def itself\n            self\n          end\n        end\n\n        def foo\n          bar(21, with 10 yield 8)\n        end\n\n        def bar(x, y)\n          x &* y\n        end\n\n        foo do |x|\n          itself &- x\n        end\n      CRYSTAL\n    end\n\n    it \"interprets yield with splat (1)\" do\n      interpret(<<-CRYSTAL).should eq((2 - 3) * 4)\n        def foo\n          t = {2, 3, 4}\n          yield *t\n        end\n\n        a = 0\n        foo do |x1, x2, x3|\n          a = (x1 - x2) * x3\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield with splat (2)\" do\n      interpret(<<-CRYSTAL).should eq((((1 - 2) * 3) - 4) * 5)\n        def foo\n          t = {2, 3, 4}\n          yield 1, *t, 5\n        end\n\n        a = 0\n        foo do |x1, x2, x3, x4, x5|\n          a = (((x1 - x2) * x3) - x4) * x5\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield with splat, less block arguments\" do\n      interpret(<<-CRYSTAL).should eq(2 - 3)\n        def foo\n          t = {2, 3, 4}\n          yield *t\n        end\n\n        a = 0\n        foo do |x1, x2|\n          a = x1 - x2\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets block with splat\" do\n      interpret(<<-CRYSTAL).should eq((((1 - 2) * 3) - 4) * 5)\n        def foo\n          yield 1, 2, 3, 4, 5\n        end\n\n        a = 0\n        foo do |x1, *x, x5|\n          a = (((x1 - x[0]) * x[1]) - x[2]) * x5\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield with splat, block with splat\" do\n      interpret(<<-CRYSTAL).should eq((((1 - 2) * 3) - 4) * 5)\n        def foo\n          t = {1, 2, 3}\n          yield *t, 4, 5\n        end\n\n        a = 0\n        foo do |x1, *x, x5|\n          a = (((x1 - x[0]) * x[1]) - x[2]) * x5\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets yield with splat, block with splat (#12227)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo\n          yield *{ {3, 2} }\n        end\n\n        foo do |x, y|\n          x &- y\n        end\n      CRYSTAL\n    end\n\n    it \"considers block arg without type as having NoReturn type (#12270)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def bar\n          if ptr = nil\n            yield ptr\n          else\n            42\n          end\n        end\n\n        def foo\n          bar do |obj|\n            obj\n          end\n        end\n\n        foo\n      CRYSTAL\n    end\n\n    it \"considers block arg without type as having NoReturn type (2) (#12270)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def bar\n          if ptr = nil\n            yield ptr\n          else\n            42\n          end\n        end\n\n        def foo\n          bar do |obj|\n            return obj\n          end\n        end\n\n        foo\n      CRYSTAL\n    end\n\n    it \"caches method with captured block (#12276)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def execute(x, &block : -> Int32)\n          if x\n            execute(false) do\n              block.call\n            end\n          else\n            yield\n          end\n        end\n\n        execute(true) do\n          42\n        end\n      CRYSTAL\n    end\n\n    it \"interprets underscore parameters corresponding to yield arguments with different types (#13474)\" do\n      interpret(<<-CRYSTAL).should eq(9)\n        def foo(&)\n          yield 1, 2, \"4\", 8\n        end\n\n        foo do |a, _, _, b|\n          a &+ b\n        end\n        CRYSTAL\n    end\n\n    it \"interprets block with both splat and non-splat underscore parameter (#13474)\" do\n      interpret(<<-CRYSTAL).should eq(34)\n        def foo(&)\n          yield 1, 2, 4, 8, 16, 32\n        end\n\n        foo do |_, a, *_, b|\n          a &+ b\n        end\n        CRYSTAL\n    end\n\n    it \"interprets underscore parameters corresponding to yield arguments with different types + tuple unpacking (#13474)\" do\n      interpret(<<-CRYSTAL).should eq(9)\n        def foo(&)\n          yield({1, 2, \"4\", 8})\n        end\n\n        foo do |a, _, _, b|\n          a &+ b\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/bugs_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"bugs\" do\n    it \"doesn't pass self to top-level method\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        struct Int32\n          def foo(x)\n            self\n          end\n        end\n\n        def value\n          1\n        end\n\n        module Moo\n          def self.moo\n            1.foo(value)\n          end\n        end\n\n        Moo.moo\n      CRYSTAL\n    end\n\n    it \"doesn't pass self to top-level method (FileNode)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        enum Color\n          Red\n          Green\n          Blue\n        end\n\n        class Object\n          def should(expectation)\n            self\n          end\n        end\n\n        def eq(value)\n          value\n        end\n\n        private def t(type : Color)\n          type\n        end\n\n        other = 2\n        e = Color::Green.should eq(t :green)\n        e.value\n      CRYSTAL\n    end\n\n    it \"breaks from current block, not from outer block\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        def twice\n          # index: 1, block_caller: 0\n\n          yield\n          yield\n        end\n\n        def bar\n          # index: 4, block_caller: 3\n          yield\n        end\n\n        def foo\n          # index: 3, block_caller: 2\n          bar do\n            # index: 5, block_caller: 2\n            yield\n          end\n        end\n\n        # index: 0\n\n        x = 0\n\n        twice do\n          # index: 2\n          x += 1\n          foo do\n            # index: 6\n\n            # parent frame has block_caller: 2,\n            # that's where we have to go to\n            break\n          end\n        end\n\n        x\n      CRYSTAL\n    end\n\n    it \"doesn't incorrectly consider a non-closure as closure\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"false\")\n        c = 0\n        ->{\n          c\n          ->{}.closure?\n        }.call\n      CRYSTAL\n    end\n\n    it \"doesn't override local variable value with block var with the same name\" do\n      interpret(<<-CRYSTAL).should eq(0)\n        def block\n          yield 1\n        end\n\n        def block2\n          yield 10\n        end\n\n        def foo\n          block do |i|\n          end\n\n          i = 0\n          block2 do |x|\n            i\n          end\n        end\n\n        foo\n      CRYSTAL\n    end\n\n    it \"does leading zeros\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"8\")\n        0_i8.leading_zeros_count\n      CRYSTAL\n    end\n\n    it \"does multidispatch on virtual struct\" do\n      interpret(<<-CRYSTAL).should be_true\n        abstract struct Base\n        end\n\n        struct Foo < Base\n          @x : Int32 | Char\n\n          def initialize\n            @x = 0\n          end\n\n          def foo\n            @x.is_a?(Int32)\n          end\n        end\n\n        struct Bar < Base\n          def foo\n            false\n          end\n        end\n\n        address = Foo.new.as(Base)\n        address.foo\n      CRYSTAL\n    end\n\n    it \"correctly puts virtual metaclass type in union\" do\n      interpret(<<-CRYSTAL).should eq(\"Bar\")\n        abstract struct Foo\n        end\n\n        struct Bar < Foo\n        end\n\n        struct Baz < Foo\n        end\n\n        class Class\n          def name : String\n            {{ @type.name.stringify }}\n          end\n        end\n\n        foo = Bar.new.as(Foo)\n        foo2 = foo || nil\n        foo2.class.name\n      CRYSTAL\n    end\n\n    it \"does multidispatch on virtual struct union nil\" do\n      interpret(<<-CRYSTAL).should be_true\n        abstract struct Foo\n          @value = 1\n        end\n\n        struct Bar < Foo\n        end\n\n        struct Baz < Foo\n        end\n\n        class Object\n          def itself\n            a = 1\n            self\n          end\n        end\n\n        foo = Bar.new.as(Foo)\n        bar = (foo || nil).itself\n        bar.is_a?(Bar)\n     CRYSTAL\n    end\n\n    it \"handles self in inlined method with arguments (#16210)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"hello\"))\n        class Foo\n          property x : String?\n\n          def initialize(@x : String? = nil)\n          end\n        end\n\n        foo = Foo.new(\"hello\")\n        foo.x.not_nil!(\"test\")\n      CRYSTAL\n    end\n\n    it \"returns concrete type with typeof (#16377)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"true\")\n        class Foo\n        end\n\n        class Bar < Foo\n        end\n\n        foo = Bar.new.as(Foo)\n        typeof(foo) == Foo\n      CRYSTAL\n    end\n\n    it \"looks up local vars in parent scopes after looking up local vars in current scope and closured scope (#15489)\" do\n      interpret(<<-CRYSTAL).should eq(\"parser\")\n        def capture(&block)\n          block\n        end\n\n        def scoped(&)\n          yield 1\n        end\n\n        scoped do |parser|\n          capture do\n            parser\n          end\n          parser # Error: BUG: missing downcast_distinct from String to Int32 (Crystal::NonGenericClassType to Crystal::IntegerType)\n        end\n\n        parser = \"parser\"\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/calls_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"calls\" do\n    it \"calls a top-level method without arguments and no local vars\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        def foo\n          1 + 2\n        end\n\n        foo\n        CRYSTAL\n    end\n\n    it \"calls a top-level method without arguments but with local vars\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        def foo\n          x = 1\n          y = 2\n          x + y\n        end\n\n        x = foo\n        x\n        CRYSTAL\n    end\n\n    it \"calls a top-level method with two arguments\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        def foo(x, y)\n          x + y\n        end\n\n        x = foo(1, 2)\n        x\n        CRYSTAL\n    end\n\n    it \"interprets call with default values\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        def foo(x = 1, y = 2)\n          x + y\n        end\n\n        foo\n        CRYSTAL\n    end\n\n    it \"interprets call with named arguments\" do\n      interpret(<<-CRYSTAL).should eq(-15)\n        def foo(x, y)\n          x - y\n        end\n\n        foo(y: 25, x: 10)\n        CRYSTAL\n    end\n\n    it \"interprets self for primitive types\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Int32\n          def foo\n            self\n          end\n        end\n\n        42.foo\n        CRYSTAL\n    end\n\n    it \"interprets explicit self call for primitive types\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Int32\n          def foo\n            self.bar\n          end\n\n          def bar\n            self\n          end\n        end\n\n        42.foo\n        CRYSTAL\n    end\n\n    it \"interprets implicit self call for pointer\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        struct Pointer(T)\n          def plus1\n            self + 1_i64\n          end\n        end\n\n        ptr = Pointer(UInt8).malloc(1_u64)\n        ptr2 = ptr.plus1\n        (ptr2 - ptr)\n        CRYSTAL\n    end\n\n    it \"interprets call with if\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        def foo\n          1 == 1 ? 2 : 3\n        end\n\n        foo\n        CRYSTAL\n    end\n\n    it \"does call with struct as obj\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        struct Foo\n          def initialize(@x : Int64)\n          end\n\n          def itself\n            self\n          end\n\n          def x\n            @x + 2_i64\n          end\n        end\n\n        def foo\n          Foo.new(1_i64)\n        end\n\n        foo.x\n      CRYSTAL\n    end\n\n    it \"does call with struct as obj (2)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Foo\n          def two\n            2\n          end\n        end\n\n        Foo.new.two\n      CRYSTAL\n    end\n\n    it \"does call on instance var that's a struct, from a class\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        class Foo\n          def initialize\n            @x = 0_i64\n            @y = 0_i64\n            @z = 0_i64\n            @bar = Bar.new(2)\n          end\n\n          def foo\n            @bar.mutate\n            @bar.x\n          end\n        end\n\n        struct Bar\n          def initialize(@x : Int32)\n          end\n\n          def mutate\n            @x = 10\n          end\n\n          def x\n            @x\n          end\n        end\n\n        Foo.new.foo\n      CRYSTAL\n    end\n\n    it \"does call on instance var that's a struct, from a struct\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 0_i64\n            @y = 0_i64\n            @z = 0_i64\n            @bar = Bar.new(2)\n          end\n\n          def foo\n            @bar.mutate\n            @bar.x\n          end\n        end\n\n        struct Bar\n          def initialize(@x : Int32)\n          end\n\n          def mutate\n            @x = 10\n          end\n\n          def x\n            @x\n          end\n        end\n\n        Foo.new.foo\n      CRYSTAL\n    end\n\n    it \"discards call with struct as obj\" do\n      interpret(<<-CRYSTAL).should eq(4)\n        struct Foo\n          def initialize(@x : Int64)\n          end\n\n          def itself\n            self\n          end\n\n          def x\n            @x + 2_i64\n          end\n        end\n\n        def foo\n          Foo.new(1_i64)\n        end\n\n        foo.x\n        4\n      CRYSTAL\n    end\n\n    it \"does call on constant that's a struct, takes a pointer to instance var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        CONST = Foo.new\n        CONST.to_unsafe.value\n      CRYSTAL\n    end\n\n    it \"does call on constant that's a struct, takes a pointer to instance var, inside if\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        CONST = Foo.new\n        c = (1 == 1 ? CONST : CONST).to_unsafe\n        c.value\n      CRYSTAL\n    end\n\n    it \"does call on var that's a struct, takes a pointer to instance var, inside if\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        a = Foo.new\n        c = (1 == 1 ? a : a).to_unsafe\n        c.value\n      CRYSTAL\n    end\n\n    it \"does call on ivar that's a struct, takes a pointer to instance var, inside if\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        struct Bar\n          def initialize\n            @foo = Foo.new\n          end\n\n          def do_it\n            c = (1 == 1 ? @foo : @foo).to_unsafe\n            c.value\n          end\n        end\n\n        Bar.new.do_it\n      CRYSTAL\n    end\n\n    it \"does call on self that's a struct, takes a pointer to instance var, inside if\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n\n          def do_it\n            c = (1 == 1 ? self : self).to_unsafe\n            c.value\n          end\n        end\n\n        Foo.new.do_it\n      CRYSTAL\n    end\n\n    it \"does call on Pointer#value that's a struct, takes a pointer to instance var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        foo = Foo.new\n        ptr = pointerof(foo)\n        c = ptr.value.to_unsafe\n        c.value\n      CRYSTAL\n    end\n\n    it \"does call on read instance var that's a struct, takes a pointer to instance var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 42\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        class Bar\n          def initialize(@foo : Foo)\n          end\n        end\n\n        foo = Foo.new\n        bar = Bar.new(foo)\n        c = bar.@foo.to_unsafe\n        c.value\n      CRYSTAL\n    end\n\n    it \"does ReadInstanceVar with wants_struct_pointer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 1\n            @y = 10\n            @bar = Bar.new\n          end\n        end\n\n        struct Bar\n          def initialize\n            @x = 1\n            @y = 2\n            @z = 42\n          end\n\n          def to_unsafe\n            pointerof(@z)\n          end\n        end\n\n        entry = Pointer(Foo).malloc(1)\n        entry.value = Foo.new\n        ptr = entry.value.@bar.to_unsafe\n        ptr.value\n      CRYSTAL\n    end\n\n    it \"does Assign var with wants_struct_pointer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Bar\n          def initialize\n            @x = 1\n            @y = 2\n            @z = 42\n          end\n\n          def to_unsafe\n            pointerof(@z)\n          end\n        end\n\n        bar = Bar.new\n        ptr = (x = bar).to_unsafe\n        ptr.value\n      CRYSTAL\n    end\n\n    it \"does Assign instance var with wants_struct_pointer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Bar\n          def initialize\n            @x = 1\n            @y = 2\n            @z = 42\n          end\n\n          def to_unsafe\n            pointerof(@z)\n          end\n        end\n\n        class Foo\n          @x : Bar?\n\n          def foo\n            bar = Bar.new\n            ptr = (@x = bar).to_unsafe\n            ptr.value\n          end\n        end\n\n        Foo.new.foo\n      CRYSTAL\n    end\n\n    it \"does Assign class var with wants_struct_pointer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Bar\n          def initialize\n            @x = 1\n            @y = 2\n            @z = 42\n          end\n\n          def to_unsafe\n            pointerof(@z)\n          end\n        end\n\n        class Foo\n          @@x : Bar?\n\n          def foo\n            bar = Bar.new\n            ptr = (@@x = bar).to_unsafe\n            ptr.value\n          end\n        end\n\n        Foo.new.foo\n      CRYSTAL\n    end\n\n    it \"inlines method that just reads an instance var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 1\n            @y = 10\n            @bar = Bar.new\n          end\n\n          def bar\n            @bar\n          end\n        end\n\n        struct Bar\n          def initialize\n            @x = 1\n            @y = 2\n            @z = 42\n          end\n\n          def to_unsafe\n            pointerof(@z)\n          end\n        end\n\n        entry = Pointer(Foo).malloc(1)\n        entry.value = Foo.new\n        ptr = entry.value.bar.to_unsafe\n        ptr.value\n      CRYSTAL\n    end\n\n    it \"inlines method that just reads an instance var, but produces side effects of args\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize\n            @x = 1\n            @y = 10\n            @bar = Bar.new\n          end\n\n          def bar(x)\n            @bar\n          end\n        end\n\n        struct Bar\n          def initialize\n            @x = 1\n            @y = 2\n            @z = 32\n          end\n\n          def to_unsafe\n            pointerof(@z)\n          end\n        end\n\n        entry = Pointer(Foo).malloc(1)\n        entry.value = Foo.new\n        a = 1\n        ptr = entry.value.bar(a = 10).to_unsafe\n        ptr.value + a\n      CRYSTAL\n    end\n\n    it \"inlines method that just reads an instance var (2)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        abstract class Abstract\n        end\n\n        class Concrete < Abstract\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        original = Concrete.new(2).as(Abstract)\n        original.x\n      CRYSTAL\n    end\n\n    it \"puts struct pointer after tuple indexer\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        struct Point\n          def initialize(@x : Int64)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        a = Point.new(1_u64)\n        t = {a}\n        t[0].x\n      CRYSTAL\n    end\n\n    it \"mutates call argument\" do\n      interpret(<<-CRYSTAL).should eq(9000)\n        def foo(x)\n          if 1 == 0\n            x = \"hello\"\n          end\n\n          if x.is_a?(Int32)\n            x\n          else\n            10\n          end\n        end\n\n        foo 9000\n      CRYSTAL\n    end\n\n    it \"inlines call that returns self\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 0\n          end\n\n          def mutate\n            @x = 10\n          end\n\n          def x\n            @x\n          end\n\n          def mutate_itself\n            itself.mutate\n          end\n\n          def itself\n            self\n          end\n        end\n\n        foo = Foo.new\n        foo.mutate_itself\n        foo.x\n      CRYSTAL\n    end\n\n    it \"inlines call that returns self (2)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 0\n          end\n\n          def mutate\n            @x = 10\n          end\n\n          def x\n            @x\n          end\n\n          def mutate_itself\n            self.itself.mutate\n          end\n\n          def itself\n            self\n          end\n        end\n\n        foo = Foo.new\n        foo.mutate_itself\n        foo.x\n      CRYSTAL\n    end\n\n    it \"mutates through pointer (1)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 0\n          end\n\n          def ptr\n            pointerof(@x)\n          end\n\n          def mutate\n            @x = 10\n            self\n          end\n\n          def x\n            @x\n          end\n        end\n\n        def foo\n          Foo.allocate\n        end\n\n        foo.mutate.ptr.value\n      CRYSTAL\n    end\n\n    it \"mutates through pointer (2)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 0\n          end\n\n          def ptr\n            pointerof(@x)\n          end\n\n          def mutate\n            @x = 10\n            self\n          end\n\n          def x\n            @x\n          end\n        end\n\n        def foo\n          Foo.allocate\n        end\n\n        x = foo.mutate.ptr\n        x.value\n      CRYSTAL\n    end\n\n    it \"mutates through pointer (3)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 0\n          end\n\n          def mutate\n            @x = 10\n          end\n\n          def x\n            @x\n          end\n        end\n\n        ptr = Pointer(Foo).malloc(1_u64)\n        ptr.value = Foo.new\n        ptr.value.mutate\n        ptr.value.x\n      CRYSTAL\n    end\n\n    it \"mutates through read instance var\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @bar = Bar.new\n          end\n\n          def bar\n            @bar\n          end\n        end\n\n        struct Bar\n          def initialize\n            @z = 0\n          end\n\n          def z=(@z)\n          end\n\n          def z\n            @z\n          end\n        end\n\n        foo = Foo.new\n        foo.@bar.z = 10\n        foo.bar.z\n      CRYSTAL\n    end\n\n    it \"mutates through inlined instance var with receiver\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @bar = Bar.new\n          end\n\n          def bar\n            @bar\n          end\n        end\n\n        struct Bar\n          def initialize\n            @z = 0\n          end\n\n          def z=(@z)\n          end\n\n          def z\n            @z\n          end\n        end\n\n        foo = Foo.new\n        foo.bar.z = 10\n        foo.bar.z\n      CRYSTAL\n    end\n\n    it \"mutates through inlined instance var without receiver\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @bar = Bar.new\n          end\n\n          def bar\n            @bar\n          end\n\n          def mutate\n            bar.z = 10\n          end\n        end\n\n        struct Bar\n          def initialize\n            @z = 0\n          end\n\n          def z=(@z)\n          end\n\n          def z\n            @z\n          end\n        end\n\n        foo = Foo.new\n        foo.mutate\n        foo.bar.z\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/casts_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"casts\" do\n    it \"casts from reference to pointer and back\" do\n      interpret(<<-CRYSTAL).should eq(\"hello\")\n        x = \"hello\"\n        p = x.as(UInt8*)\n        y = p.as(String)\n        y\n      CRYSTAL\n    end\n\n    it \"casts from reference to nilable reference\" do\n      interpret(<<-CRYSTAL).should eq(\"hello\")\n        x = \"hello\"\n        y = x.as(String | Nil)\n        if y\n          y\n        else\n          \"bye\"\n        end\n      CRYSTAL\n    end\n\n    it \"casts from mixed union type to another mixed union type for caller\" do\n      interpret(<<-CRYSTAL).should be_true\n        a = 1 == 1 ? 1 : (1 == 1 ? 20_i16 : nil)\n        if a\n          a < 2\n        else\n          false\n        end\n      CRYSTAL\n    end\n\n    it \"casts from nilable type to mixed union type\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        ascii = true\n        delimiter = 1 == 1 ? nil : \"foo\"\n\n        if ascii && delimiter\n          1\n        else\n          2\n        end\n        CRYSTAL\n    end\n\n    it \"casts from nilable type to mixed union type (2)\" do\n      interpret(<<-CRYSTAL).should be_true\n        y = 1 == 1 ? \"a\" : nil\n        x = true\n        x = y\n        x.is_a?(String)\n      CRYSTAL\n    end\n\n    it \"casts from mixed union type to primitive type\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"2\")\n        x = 1 == 1 ? 2 : nil\n        x.as(Int32)\n      CRYSTAL\n    end\n\n    it \"casts nilable from mixed union type to primitive type (non-nil case)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        x = 1 == 1 ? 2 : nil\n        y = x.as?(Int32)\n        y ? y : 20\n      CRYSTAL\n    end\n\n    it \"casts nilable from mixed union type to primitive type (nil case)\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        x = 1 == 1 ? nil : 2\n        y = x.as?(Int32)\n        y ? y : 20\n      CRYSTAL\n    end\n\n    it \"upcasts between tuple types\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq((1 + 'a'.ord).to_s)\n        a =\n          if 1 == 1\n            {1, 'a'}\n          else\n            {true, 3}\n          end\n\n        a[0].as(Int32) + a[1].as(Char).ord\n      CRYSTAL\n    end\n\n    it \"upcasts between tuple types, respects alignment (#14036)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"123\")\n        a = {100, 20, 3}.as({Int32 | Int64, Int32, Int32})\n        a[0].as(Int32) + a[1] + a[2]\n      CRYSTAL\n    end\n\n    it \"upcasts between named tuple types, same order\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq((1 + 'a'.ord).to_s)\n        a =\n          if 1 == 1\n            {a: 1, b: 'a'}\n          else\n            {a: true, b: 3}\n          end\n\n        a[:a].as(Int32) + a[:b].as(Char).ord\n      CRYSTAL\n    end\n\n    it \"upcasts between named tuple types, different order\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq((1 + 'a'.ord).to_s)\n        a =\n          if 1 == 1\n            {a: 1, b: 'a'}\n          else\n            {b:3, a: true}\n          end\n\n        a[:a].as(Int32) + a[:b].as(Char).ord\n      CRYSTAL\n    end\n\n    it \"upcasts between named tuple types, respects alignment (#14036)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"123\")\n        a = {x: 100, y: 20_i64, z: 3}.as({y: Int64, z: Int32, x: Int32})\n        a[:x] + a[:y] + a[:z]\n      CRYSTAL\n    end\n\n    it \"upcasts to module type\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        module Moo\n        end\n\n        class Foo\n          include Moo\n\n          def foo\n            1\n          end\n        end\n\n        class Bar\n          include Moo\n\n          def foo\n            2\n          end\n        end\n\n        moo = (1 == 1 ? Foo.new : Bar.new).as(Moo)\n        if moo.is_a?(Foo)\n          moo.foo\n        else\n          10\n        end\n      CRYSTAL\n    end\n\n    it \"upcasts virtual type to union\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def foo\n            1\n          end\n        end\n\n        class Bar < Foo\n          def foo\n            2\n          end\n        end\n\n        foo = 1 == 1 ? Bar.new : Foo.new\n        a = 1 == 1 ? foo : 10\n        if a.is_a?(Foo)\n          a.foo\n        else\n          20\n        end\n      CRYSTAL\n    end\n\n    it \"casts nil to Void*\" do\n      interpret(<<-CRYSTAL).should eq(0)\n        module Moo\n          def self.moo(r)\n            r.as(Void*)\n          end\n        end\n\n        Moo.moo(nil).address\n      CRYSTAL\n    end\n\n    it \"does is_a? with virtual metaclass\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class A\n          def self.a\n            2\n          end\n        end\n\n        class B < A\n          def self.b\n            1\n          end\n        end\n\n        class C < B\n        end\n\n        x = B || A\n        if x.is_a?(B.class)\n          x.b\n        elsif x.is_a?(A.class)\n          x.a\n        else\n          0\n        end\n        CRYSTAL\n    end\n\n    it \"discards cast\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"10\")\n        x = 1 || 'a'\n        x.as(Int32)\n        10\n      CRYSTAL\n    end\n\n    it \"raises when as fails\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").to_s.should contain(\"Cast from Int32 to Char failed\")\n        x = 1 || 'a'\n        begin\n          x.as(Char)\n          \"\"\n        rescue ex : TypeCastError\n          ex.message.not_nil!\n        end\n      CRYSTAL\n    end\n\n    it \"casts to filtered type, not type in as(...)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"1\")\n        ({1} || 2).as(Tuple)[0]\n      CRYSTAL\n    end\n\n    it \"does is_a? with virtual type (struct)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        abstract struct Foo\n        end\n\n        struct Bar < Foo\n          def initialize(@x : Int32)\n          end\n\n          def bar\n            @x\n          end\n        end\n\n        struct Baz < Foo\n          def initialize(@x : Int32)\n          end\n\n          def baz\n            @x\n          end\n        end\n\n        a = (Bar.new(10) || Baz.new(20)).as(Foo)\n        case a\n        when Bar\n          a.bar\n        when Baz\n          a.baz\n        else\n          0\n        end\n        CRYSTAL\n    end\n\n    it \"puts virtual metaclass into union (#12162)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"ActionA\"))\n        class Action\n        end\n\n        class ActionA < Action\n        end\n\n        class ActionB < Action\n        end\n\n        x = ActionA || ActionB\n        y = x || Nil\n        y.to_s\n        CRYSTAL\n    end\n\n    it \"puts tuple type inside union of different tuple type (#12243)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"{180}\"))\n        class A\n          def initialize(@x : {Char | Int32}?)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        x = A.new({180}).x\n        x.to_s\n      CRYSTAL\n    end\n\n    it \"puts named tuple type inside union of different named tuple type (#12243)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"{v: 180}\"))\n        class A\n          def initialize(@x : {v: Char | Int32}?)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        x = A.new({v: 180}).x\n        x.to_s\n      CRYSTAL\n    end\n\n    it \"casts from mixed union type to nilable proc type (#12283)\" do\n      interpret(<<-CRYSTAL).should eq(\"b\")\n        message = ->{ \"b\" }.as(String | Proc(String) | Nil)\n        if message.is_a?(String)\n          \"a\"\n        elsif message.is_a?(Proc(String))\n          message.call\n        else\n          \"c\"\n        end\n      CRYSTAL\n    end\n\n    it \"does as? with no resulting type (#12327)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        if nil.as?(Int32)\n          0\n        else\n          42\n        end\n        CRYSTAL\n    end\n\n    it \"does as? with no resulting type, not from nil (#12327)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        if 1.as?(String)\n          0\n        else\n          42\n        end\n        CRYSTAL\n    end\n\n    it \"does as? with a type that can't match (#12346)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        abstract class A\n        end\n\n        class B < A\n        end\n\n        class C < A\n        end\n\n        a = B.new || C.new\n        a.as?(B | Int32) ? 1 : 2\n        CRYSTAL\n    end\n\n    it \"upcasts mixed union with tuple to mixed union with compatible tuple (1) (#12331)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class Foo\n          def initialize(@tuple : Tuple(Int32?) | Tuple(Int32, Int32))\n          end\n\n          def tuple\n            @tuple\n          end\n        end\n\n        a = {1} || {1, 1}\n        foo = Foo.new(a)\n        tuple = foo.tuple\n        if tuple.is_a?(Tuple(Int32?))\n          value = tuple[0]\n          if value\n            value\n          else\n            2\n          end\n        else\n          3\n        end\n      CRYSTAL\n    end\n\n    it \"upcasts mixed union with tuple to mixed union with compatible tuple (2) (#12331)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def initialize(@tuple : Tuple(Int32?) | Tuple(Int32, Int32))\n          end\n\n          def tuple\n            @tuple\n          end\n        end\n\n        a = {nil} || {1, 1}\n        foo = Foo.new(a)\n        tuple = foo.tuple\n        if tuple.is_a?(Tuple(Int32?))\n          value = tuple[0]\n          if value\n            value\n          else\n            2\n          end\n        else\n          3\n        end\n      CRYSTAL\n    end\n\n    it \"upcasts mixed union with tuple to mixed union with compatible tuple (3) (#12331)\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Foo\n          def initialize(@tuple : Tuple(Int32?) | Tuple(Int32, Int32))\n          end\n\n          def tuple\n            @tuple\n          end\n        end\n\n        a = {1, 1} || {1}\n        foo = Foo.new(a)\n        tuple = foo.tuple\n        if tuple.is_a?(Tuple(Int32?))\n          value = tuple[0]\n          if value\n            value\n          else\n            2\n          end\n        else\n          3\n        end\n      CRYSTAL\n    end\n\n    it \"upcasts in nilable cast (#12532)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Nil\n          def foo\n            0\n          end\n        end\n\n        module A\n          def foo\n            1\n          end\n        end\n\n        class B\n          include A\n\n          def foo\n            2\n          end\n        end\n\n        class C\n          include A\n        end\n\n        B.new.as?(A).foo\n        CRYSTAL\n    end\n\n    it \"upcasts GenericClassInstanceMetaclassType to VirtualMetaclassType\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def self.foo; 1; end\n        end\n\n        class Gen(T) < Foo\n          def self.foo; 2; end\n        end\n\n        Gen(Int32).as(Foo.class).foo\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/class_vars_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"class vars\" do\n    it \"interprets class var without initializer\" do\n      interpret(<<-CRYSTAL).should eq(41)\n        class Foo\n          @@x : Int32?\n\n          def set\n            @@x = 41\n          end\n\n          def get\n            @@x\n          end\n        end\n\n        foo = Foo.new\n\n        a = 0\n\n        x = foo.get\n        a += 1 if x\n\n        foo.set\n\n        x = foo.get\n        a += x if x\n\n        a\n      CRYSTAL\n    end\n\n    it \"interprets class var with initializer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Foo\n          @@x = 10\n\n          def set\n            @@x = 32\n          end\n\n          def get\n            @@x\n          end\n        end\n\n        foo = Foo.new\n\n        a = 0\n\n        x = foo.get\n        a += x if x\n\n        foo.set\n\n        x = foo.get\n        a += x if x\n\n        a\n      CRYSTAL\n    end\n\n    it \"interprets class var for virtual type\" do\n      interpret(<<-CRYSTAL).should eq(30)\n        class Foo\n          @@x = 1\n\n          def set(@@x)\n          end\n\n          def get\n            @@x\n          end\n        end\n\n        class Bar < Foo\n        end\n\n        foo = Foo.new\n        bar = Bar.new\n\n        foobar = foo || bar\n        foobar.set(10)\n\n        barfoo = bar || foo\n        barfoo.set(20)\n\n        a = 0\n        a += foobar.get\n        a += barfoo.get\n        a\n      CRYSTAL\n    end\n\n    it \"interprets class var for virtual metaclass type\" do\n      interpret(<<-CRYSTAL).should eq(30)\n        class Foo\n          @@x = 1\n\n          def self.set(@@x)\n          end\n\n          def self.get\n            @@x\n          end\n        end\n\n        class Bar < Foo\n        end\n\n        foo = Foo\n        bar = Bar\n\n        foobar = foo || bar\n        foobar.set(10)\n\n        barfoo = bar || foo\n        barfoo.set(20)\n\n        a = 0\n        a += foobar.get\n        a += barfoo.get\n        a\n      CRYSTAL\n    end\n\n    it \"finds self in class var initializer (#12439)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Foo\n          @@value : Int32 = self.int\n\n          def self.value\n            @@value\n          end\n\n          def self.int\n            42\n          end\n        end\n\n        Foo.value\n      CRYSTAL\n    end\n\n    it \"does class var initializer with union (#12633)\" do\n      interpret(<<-CRYSTAL).should eq(\"hello\")\n        class MyClass\n          @@a : String | Int32 = \"hello\"\n\n          def self.a\n            @@a\n          end\n        end\n\n        x = MyClass.a\n        case x\n        in String\n          x\n        in Int32\n          \"bye\"\n        end\n        CRYSTAL\n    end\n\n    it \"reads class var initializer with union (#12633)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class MyClass\n          @@a : Char | Int32 = 1\n\n          def self.foo(a)\n            @@a = a\n            b = @@a\n            case b\n            in Char\n              3\n            in Int32\n              b\n            end\n          end\n        end\n\n        MyClass.foo(2)\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/classes_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"classes\" do\n    it \"does allocate, set instance var and get instance var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Foo\n          @x = 0\n\n          def x=(@x)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.allocate\n        foo.x = 42\n        foo.x\n      CRYSTAL\n    end\n\n    it \"does constructor\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Foo\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.new(42)\n        foo.x\n      CRYSTAL\n    end\n\n    it \"interprets read instance var\" do\n      interpret(%(x = \"hello\".@c)).should eq('h'.ord)\n    end\n\n    it \"discards allocate\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Foo\n        end\n\n        Foo.allocate\n        3\n      CRYSTAL\n    end\n\n    it \"calls implicit class self method\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        class Foo\n          def initialize\n            @x = 10\n          end\n\n          def foo\n            bar\n          end\n\n          def bar\n            @x\n          end\n        end\n\n        foo = Foo.new\n        foo.foo\n      CRYSTAL\n    end\n\n    it \"calls explicit struct self method\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 10\n          end\n\n          def foo\n            self.bar\n          end\n\n          def bar\n            @x\n          end\n        end\n\n        foo = Foo.new\n        foo.foo\n      CRYSTAL\n    end\n\n    it \"calls implicit struct self method\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize\n            @x = 10\n          end\n\n          def foo\n            bar\n          end\n\n          def bar\n            @x\n          end\n        end\n\n        foo = Foo.new\n        foo.foo\n      CRYSTAL\n    end\n\n    it \"does object_id\" do\n      interpret(<<-CRYSTAL).should be_true\n        class Foo\n        end\n\n        foo = Foo.allocate\n        object_id = foo.object_id\n        address = foo.as(Void*).address\n        object_id == address\n      CRYSTAL\n    end\n  end\n\n  it \"inlines instance var access from virtual type with a single type (#39520)\" do\n    interpret(<<-CRYSTAL).should eq(1)\n        struct Int32\n          def foo\n            1\n          end\n        end\n\n        struct Char\n          def foo\n            2\n          end\n        end\n\n        abstract class Expression\n        end\n\n        class ValueExpression < Expression\n          def initialize\n            @value = 1 || 'a'\n          end\n\n          def value\n            @value\n          end\n        end\n\n        expression = ValueExpression.new.as(Expression)\n        expression.value.foo\n      CRYSTAL\n  end\n\n  it \"downcasts virtual type to its only type (#12351)\" do\n    interpret(<<-CRYSTAL).should eq(1)\n      abstract class A\n      end\n\n      class B < A\n        def x\n          1\n        end\n      end\n\n      def foo(b : B)\n        b = 1\n      end\n\n      b = B.new.as(A)\n      foo(b)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/closures_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"closures\" do\n    it \"does closure without args that captures and modifies one local variable\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          a = 0\n          proc = -> { a = 42 }\n          proc.call\n          a\n        CRYSTAL\n    end\n\n    it \"does closure without args that captures and modifies two local variables\" do\n      interpret(<<-CRYSTAL).should eq(7)\n          a = 0\n          b = 0\n          proc = ->{\n            a = 10\n            b = 3\n          }\n          proc.call\n          a - b\n        CRYSTAL\n    end\n\n    it \"does closure with two args that captures and modifies two local variables\" do\n      interpret(<<-CRYSTAL).should eq(7)\n          a = 0\n          b = 0\n          proc = ->(x : Int32, y : Int32) {\n            a = x\n            b = y\n          }\n          proc.call(10, 3)\n          a - b\n        CRYSTAL\n    end\n\n    it \"does closure and accesses it inside block\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          def foo\n            yield\n          end\n\n          a = 0\n          proc = -> { a = 42 }\n\n          x = foo do\n            proc.call\n            a\n          end\n\n          x\n        CRYSTAL\n    end\n\n    it \"does closure inside def\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          def foo\n            a = 0\n            proc = -> { a = 42 }\n            proc.call\n            a\n          end\n\n          foo\n        CRYSTAL\n    end\n\n    it \"closures def arguments\" do\n      interpret(<<-CRYSTAL).should eq((41 + 1) - (10 + 2))\n          def foo(a, b)\n            proc = -> { a += 1; b += 2 }\n            proc.call\n            a - b\n          end\n\n          foo(41, 10)\n        CRYSTAL\n    end\n\n    it \"does closure inside proc\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          proc = ->{\n            a = 0\n            proc2 = -> { a = 42 }\n            proc2.call\n            a\n          }\n\n          proc.call\n        CRYSTAL\n    end\n\n    it \"does closure inside proc, capture proc argument\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          proc = ->(a : Int32) {\n            proc2 = -> { a += 1 }\n            proc2.call\n            a\n          }\n\n          proc.call(41)\n        CRYSTAL\n    end\n\n    it \"does closure inside const\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          FOO =\n            begin\n              a = 0\n              proc = -> { a = 42 }\n              proc.call\n              a\n            end\n\n          FOO\n        CRYSTAL\n    end\n\n    it \"does closure inside class variable initializer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          class Foo\n            @@foo : Int32 =\n              begin\n                a = 0\n                proc = -> { a = 42 }\n                proc.call\n                a\n              end\n\n            def self.foo\n              @@foo\n            end\n          end\n\n          Foo.foo\n        CRYSTAL\n    end\n\n    it \"does closure inside block\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          def foo\n            yield\n          end\n\n          foo do\n            a = 0\n            proc = ->{ a = 42 }\n            proc.call\n            a\n          end\n        CRYSTAL\n    end\n\n    it \"does closure inside block, capture block arg\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          def foo\n            yield 21\n          end\n\n          foo do |a|\n            proc = ->{ a += 21 }\n            proc.call\n            a\n          end\n        CRYSTAL\n    end\n\n    it \"does nested closure inside proc\" do\n      interpret(<<-CRYSTAL).should eq(21)\n          a = 0\n\n          proc1 = ->{\n            a = 21\n            b = 10\n\n            proc2 = ->{\n              a += b + 11\n            }\n          }\n\n          proc2 = proc1.call\n\n          x = a\n\n          proc2.call\n\n          y = a\n\n          y - x\n        CRYSTAL\n    end\n\n    it \"does nested closure inside captured blocks\" do\n      interpret(<<-CRYSTAL).should eq(21)\n          def capture(&block : -> _)\n            block\n          end\n\n          a = 0\n\n          proc1 = capture do\n            a = 21\n            b = 10\n\n            proc2 = capture do\n              a += b + 11\n            end\n          end\n\n          proc2 = proc1.call\n\n          x = a\n\n          proc2.call\n\n          y = a\n\n          y - x\n        CRYSTAL\n    end\n\n    it \"does nested closure inside methods and blocks\" do\n      interpret(<<-CRYSTAL).should eq(12)\n          def foo\n            yield\n          end\n\n          a = 0\n          proc1 = ->{ a += 10 }\n\n          foo do\n            b = 1\n            proc2 = ->{ b += a + 1 }\n\n            proc1.call\n            proc2.call\n\n            b\n          end\n        CRYSTAL\n    end\n\n    it \"does closure with pointerof local var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n          a = 0\n          proc = ->do\n            ptr = pointerof(a)\n            ptr.value = 42\n          end\n          proc.call\n          a\n        CRYSTAL\n    end\n\n    it \"closures self in proc literal\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Foo\n          def initialize\n            @x = 1\n          end\n\n          def inc\n            @x += 1\n          end\n\n          def x\n            @x\n          end\n\n          def closure\n            ->{ self.inc }\n          end\n        end\n\n        foo = Foo.new\n        proc = foo.closure\n        proc.call\n        proc.call\n        foo.x\n        CRYSTAL\n    end\n\n    it \"closures self in proc literal (implicit self)\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Foo\n          def initialize\n            @x = 1\n          end\n\n          def inc\n            @x += 1\n          end\n\n          def x\n            @x\n          end\n\n          def closure\n            ->{ inc }\n          end\n        end\n\n        foo = Foo.new\n        proc = foo.closure\n        proc.call\n        proc.call\n        foo.x\n        CRYSTAL\n    end\n\n    it \"closures self and modifies instance var\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Foo\n          def initialize\n            @x = 1\n          end\n\n          def x\n            @x\n          end\n\n          def closure\n            ->{ @x += 1 }\n          end\n        end\n\n        foo = Foo.new\n        proc = foo.closure\n        proc.call\n        proc.call\n        foo.x\n        CRYSTAL\n    end\n\n    it \"closures struct and calls method on it\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Foo\n          def initialize\n            @x = 1\n          end\n\n          def inc\n            @x += 1\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.new\n        proc = ->{ foo.inc }\n        proc.call\n        foo.x\n      CRYSTAL\n    end\n\n    it \"doesn't mix local vars with closured vars\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        def foo(x)\n          yield x\n        end\n\n        foo(10) do |i|\n          ->{\n            a = i\n            foo(20) do |i|\n              i\n            end\n          }.call\n        end\n      CRYSTAL\n    end\n\n    it \"closures closured block arg\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo(&block : -> Int32)\n          ->{ block.call }.call\n        end\n\n        foo do\n          1\n        end\n      CRYSTAL\n    end\n\n    it \"closures block args after 8 bytes (the closure var)\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        def foo\n          yield({1, 2, 3})\n        end\n\n        foo do |x, y, z|\n          ->{ x + y + z }.call\n        end\n      CRYSTAL\n    end\n\n    it \"passes closured struct instance var as self\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Bar\n          def bar\n            10\n          end\n        end\n\n        class Foo\n          def initialize\n            @bar = Bar.new\n          end\n\n          def foo\n            ->{ @bar.bar }.call\n          end\n        end\n\n        Foo.new.foo\n      CRYSTAL\n    end\n\n    it \"does next inside captured block (#12226)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        def foo(&block : -> _)\n          block.call\n        end\n\n        foo do\n          next 1\n          2\n        end\n      CRYSTAL\n    end\n\n    it \"gets ivar of self closured struct (#12341)\" do\n      interpret(<<-CRYSTAL).should eq(860)\n        def closure(&block : -> Int32)\n          block\n        end\n\n        struct Foo\n          @a = 10\n          @b = 20\n          @c = 30\n          @d = 40\n\n          def foo\n            block = closure do\n              (@a &+ @b) &* @c &- @d\n            end\n            block.call\n          end\n        end\n\n        foo = Foo.new\n        foo.foo\n      CRYSTAL\n    end\n\n    it \"sets ivar of self closured struct (#12341)\" do\n      interpret(<<-CRYSTAL).should eq(1) # Yes, not 2. A closured struct's value can't change.\n        def closure(&block)\n          block\n        end\n\n        struct Foo\n          @count = 1\n\n          def foo\n            closure do\n              @count = 2\n            end\n          end\n\n          def count\n            @count\n          end\n        end\n\n        foo = Foo.new\n        foo.foo\n        foo.count\n      CRYSTAL\n    end\n\n    it \"reads self closured struct (#12341)\" do\n      interpret(<<-CRYSTAL).should eq(860)\n        def closure(&block : -> _)\n          block\n        end\n\n        struct Foo\n          @a = 10\n          @b = 20\n          @c = 30\n          @d = 40\n\n          def foo\n            closure do\n              self\n            end.call\n          end\n\n          def value\n            (@a &+ @b) &* @c &- @d\n          end\n        end\n\n        foo = Foo.new\n        bar = foo.foo\n        bar.value\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/constants_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"constants\" do\n    it \"returns nil in the assignment\" do\n      interpret(<<-CRYSTAL).should be_nil\n        A = 123\n      CRYSTAL\n    end\n\n    it \"interprets constant literal\" do\n      interpret(<<-CRYSTAL).should eq(123)\n        A = 123\n        A\n      CRYSTAL\n    end\n\n    it \"interprets complex constant\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        A = begin\n          a = 1\n          b = 2\n          a + b\n        end\n        A + A\n      CRYSTAL\n    end\n\n    it \"hoists constants\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        x = A + A\n\n        A = begin\n          a = 1\n          b = 2\n          a + b\n        end\n\n        x\n      CRYSTAL\n    end\n\n    it \"interprets self inside constant inside class\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class Foo\n          X = self.foo\n\n          def self.foo\n            bar\n          end\n\n          def self.bar\n            1\n          end\n        end\n\n        Foo::X\n      CRYSTAL\n    end\n  end\n\n  context \"magic constants\" do\n    it \"does line number\" do\n      interpret(<<-CRYSTAL).should eq(6)\n          def foo(x, line = __LINE__)\n            x + line\n          end\n\n          foo(1)\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/control_flow_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"control flow\" do\n    it \"interprets if (true literal)\" do\n      interpret(\"true ? 2 : 3\").should eq(2)\n    end\n\n    it \"interprets if (false literal)\" do\n      interpret(\"false ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets if (nil literal)\" do\n      interpret(\"nil ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets if bool (true)\" do\n      interpret(\"1 == 1 ? 2 : 3\").should eq(2)\n    end\n\n    it \"interprets if bool (false)\" do\n      interpret(\"1 == 2 ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets if (nil type)\" do\n      interpret(\"a = nil; a ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets if (int type)\" do\n      interpret(\"a = 1; a ? 2 : 3\").should eq(2)\n    end\n\n    it \"interprets if union type with bool, true\" do\n      interpret(\"a = 1 == 1 ? 1 : false; a ? 2 : 3\").should eq(2)\n    end\n\n    it \"interprets if union type with bool, false\" do\n      interpret(\"a = 1 == 2 ? 1 : false; a ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets if union type with nil, false\" do\n      interpret(\"a = 1 == 2 ? 1 : nil; a ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets if pointer, true\" do\n      interpret(\"ptr = Pointer(Int32).new(1_u64); ptr ? 2 : 3\").should eq(2)\n    end\n\n    it \"interprets if pointer, false\" do\n      interpret(\"ptr = Pointer(Int32).new(0_u64); ptr ? 2 : 3\").should eq(3)\n    end\n\n    it \"interprets unless\" do\n      interpret(\"unless 1 == 1; 2; else; 3; end\").should eq(3)\n    end\n\n    it \"discards if\" do\n      interpret(\"1 == 1 ? 2 : 3; 4\").should eq(4)\n    end\n\n    it \"interprets while\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        a = 0\n        while a < 10\n          a = a + 1\n        end\n        a\n        CRYSTAL\n    end\n\n    it \"interprets while, returns nil\" do\n      interpret(<<-CRYSTAL).should be_nil\n        a = 0\n        while a < 10\n          a = a + 1\n        end\n        CRYSTAL\n    end\n\n    it \"interprets until\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        a = 0\n        until a == 10\n          a = a + 1\n        end\n        a\n      CRYSTAL\n    end\n\n    it \"interprets break inside while\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = 0\n        while a < 10\n          a += 1\n          break if a == 3\n        end\n        a\n        CRYSTAL\n    end\n\n    it \"interprets break inside nested while\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        a = 0\n        b = 0\n        c = 0\n\n        while a < 3\n          while b < 3\n            b += 1\n            c += 1\n            break if b == 1\n          end\n\n          a += 1\n          c += 1\n          break if a == 3\n        end\n\n        c\n        CRYSTAL\n    end\n\n    it \"interprets break inside while inside block\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        def foo\n          yield\n          20\n        end\n\n        a = 0\n        foo do\n          while a < 10\n            a += 1\n            break if a == 3\n          end\n        end\n        a\n        CRYSTAL\n    end\n\n    it \"interprets break with value inside while (through break)\" do\n      interpret(<<-CRYSTAL).should eq(8)\n        a = 0\n        x = while a < 10\n          a += 1\n          break 8 if a == 3\n        end\n        x || 10\n        CRYSTAL\n    end\n\n    it \"interprets break with value inside while (through normal flow)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        a = 0\n        x = while a < 10\n          a += 1\n          break 8 if a == 20\n        end\n        x || 10\n        CRYSTAL\n    end\n\n    it \"interprets next inside while\" do\n      interpret(<<-CRYSTAL).should eq(1 + 2 + 8 + 9 + 10)\n        a = 0\n        x = 0\n        while a < 10\n          a += 1\n\n          next if 3 <= a <= 7\n\n          x += a\n        end\n        x\n        CRYSTAL\n    end\n\n    it \"interprets next inside while inside block\" do\n      interpret(<<-CRYSTAL).should eq(1 + 2 + 8 + 9 + 10)\n        def foo\n          yield\n          10\n        end\n\n        a = 0\n        x = 0\n        foo do\n          while a < 10\n            a += 1\n\n            next if 3 <= a <= 7\n\n            x += a\n          end\n        end\n        x\n        CRYSTAL\n    end\n\n    it \"discards while\" do\n      interpret(\"while 1 == 2; 3; end; 4\").should eq(4)\n    end\n\n    it \"interprets return\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        def foo(x)\n          if x == 1\n            return 2\n          end\n\n          3\n        end\n\n        foo(1)\n      CRYSTAL\n    end\n\n    it \"interprets return Nil\" do\n      interpret(<<-CRYSTAL).should be_nil\n        def foo : Nil\n          1\n        end\n\n        foo\n      CRYSTAL\n    end\n\n    it \"interprets return Nil with explicit return (#12178)\" do\n      interpret(<<-CRYSTAL).should be_nil\n        def foo : Nil\n          return 1\n        end\n\n        foo\n      CRYSTAL\n    end\n\n    it \"interprets return implicit nil and Int32\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        def foo(x)\n          if x == 1\n            return\n          end\n\n          3\n        end\n\n        z = foo(1)\n        if z.is_a?(Int32)\n          z\n        else\n          10\n        end\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/enum_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"enum\" do\n    it \"does enum value\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        enum Color\n          Red\n          Green\n          Blue\n        end\n\n        Color::Blue.value\n      CRYSTAL\n    end\n\n    it \"does enum new\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        enum Color\n          Red\n          Green\n          Blue\n        end\n\n        blue = Color.new(2)\n        blue.value\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/exceptions_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"exception handling\" do\n    it \"does ensure without rescue/raise\" do\n      interpret(<<-CRYSTAL).should eq(12)\n        x = 1\n        y =\n          begin\n            10\n          ensure\n            x = 2\n          end\n        x + y\n      CRYSTAL\n    end\n\n    it \"does rescue when nothing is raised\" do\n      interpret(<<-CRYSTAL).should eq(1)\n          a = begin\n            1\n          rescue\n            'a'\n          end\n\n          if a.is_a?(Int32)\n            a\n          else\n            10\n          end\n        CRYSTAL\n    end\n\n    it \"raises and rescues anything\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"2\")\n          a = begin\n            if 1 == 1\n              raise \"OH NO\"\n            else\n              'a'\n            end\n          rescue\n            2\n          end\n\n          if a.is_a?(Int32)\n            a\n          else\n            10\n          end\n        CRYSTAL\n    end\n\n    it \"raises and rescues anything, does ensure when an exception is rescued\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"3\")\n          a = 0\n          b = 0\n\n          begin\n            raise \"OH NO\"\n          rescue\n            a = 1\n          ensure\n            b = 2\n          end\n\n          a + b\n        CRYSTAL\n    end\n\n    it \"raises and rescues specific exception type\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"2\")\n          class Ex1 < Exception; end\n          class Ex2 < Exception; end\n\n          a = 0\n\n          begin\n            raise Ex2.new\n          rescue Ex1\n            a = 1\n          rescue Ex2\n            a = 2\n          end\n\n          a\n        CRYSTAL\n    end\n\n    it \"captures exception in variable\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"10\")\n          class Ex1 < Exception\n            getter value\n\n            def initialize(@value : Int32)\n            end\n          end\n\n          a = 0\n\n          begin\n            raise Ex1.new(10)\n          rescue ex : Ex1\n            a = ex.value\n          end\n\n          a\n        CRYSTAL\n    end\n\n    it \"executes ensure when exception is raised in body\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"10\")\n          a = 0\n\n          begin\n            begin\n              raise \"OH NO\"\n            ensure\n              a = 10\n            end\n          rescue\n          end\n\n          a\n        CRYSTAL\n    end\n\n    it \"executes ensure when exception is raised in rescue\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"10\")\n          a = 0\n\n          begin\n            begin\n              raise \"OH NO\"\n            rescue\n              raise \"OOPS\"\n            ensure\n              a = 10\n            end\n          rescue\n          end\n\n          a\n        CRYSTAL\n    end\n\n    it \"does else\" do\n      interpret(<<-CRYSTAL).should eq(3)\n          a =\n            begin\n              'a'\n            rescue\n              1\n            else\n              2\n            end\n\n          a + 1\n        CRYSTAL\n    end\n\n    it \"does ensure for else\" do\n      interpret(<<-CRYSTAL).should eq(2 + ((1 * 2) + 3))\n          x = 1\n\n          a =\n            begin\n              'a'\n            rescue\n              1\n            else\n              x *= 2\n              2\n            ensure\n              x += 3\n            end\n\n          a + x\n        CRYSTAL\n    end\n\n    it \"does ensure for else when else raises\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"2\")\n          x = 1\n\n          begin\n            begin\n              1\n            rescue\n              1\n            else\n              raise \"OH NO\"\n            ensure\n              x += 1\n            end\n          rescue\n          end\n\n          x\n        CRYSTAL\n    end\n\n    it \"does ensure with explicit return\" do\n      interpret(<<-CRYSTAL).should eq(22)\n        module Global\n          @@property = 0\n\n          def self.property\n            @@property\n          end\n\n          def self.property=(@@property)\n          end\n        end\n\n        def foo\n          x = 1\n\n          begin\n            begin\n              x += 1\n              if x == 2\n                return x\n              end\n            ensure\n              Global.property = 10\n            end\n          ensure\n            Global.property *= 2\n          end\n\n          0\n        end\n\n        x = foo\n        Global.property + x\n      CRYSTAL\n    end\n\n    it \"executes ensure when returning from a block\" do\n      interpret(<<-CRYSTAL).should eq(21)\n        module Global\n          @@property = 0\n\n          def self.property\n            @@property\n          end\n\n          def self.property=(@@property)\n          end\n        end\n\n        def block\n          yield\n        ensure\n          Global.property *= 2\n        end\n\n        def foo\n          block do\n            return 1\n          ensure\n            Global.property = 10\n          end\n\n          0\n        end\n\n        x = foo\n        Global.property + x\n      CRYSTAL\n    end\n\n    it \"executes ensure when returning from a block (2)\" do\n      interpret(<<-CRYSTAL).should eq(21)\n        module Global\n          @@property = 0\n\n          def self.property\n            @@property\n          end\n\n          def self.property=(@@property)\n          end\n        end\n\n        def block\n          yield\n        ensure\n          Global.property *= 2\n        end\n\n        def another_block\n          yield\n        end\n\n        def something\n          another_block do\n            return\n          end\n        end\n\n        def foo\n          block do\n            return 1\n          ensure\n            something\n            Global.property = 10\n          end\n\n          0\n        end\n\n        x = foo\n        Global.property + x\n      CRYSTAL\n    end\n\n    it \"executes ensure when breaking from a block\" do\n      interpret(<<-CRYSTAL).should eq(18)\n        module Global\n          @@property = 0\n\n          def self.property\n            @@property\n          end\n\n          def self.property=(@@property)\n          end\n        end\n\n        def block\n          yield\n        ensure\n          Global.property *= 2\n        end\n\n        def foo\n          x = block do\n            break 1\n          ensure\n            Global.property = 10\n          end\n\n          x - 3\n        end\n\n        x = foo\n        Global.property + x\n      CRYSTAL\n    end\n\n    it \"executes ensure when returning a big value from a block\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"32405\")\n        module Global\n          @@property = 0\n\n          def self.property\n            @@property\n          end\n\n          def self.property=(@@property)\n          end\n        end\n\n        def block\n          yield\n        ensure\n          Global.property *= 2\n        end\n\n        def foo\n          block do\n            static_array = StaticArray(Int32, 255).new { |i| i }\n            return static_array\n          ensure\n            Global.property = 10\n          end\n\n          nil\n        end\n\n        x = foo\n        if x\n          Global.property + x.sum\n        else\n          0\n        end\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/extern_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"extern\" do\n    it \"interprets primitive struct_or_union_set and get (struct)\" do\n      interpret(<<-CRYSTAL).should eq(30)\n          lib LibFoo\n            struct Foo\n              x : Int32\n              y : Int32\n            end\n          end\n\n          foo = LibFoo::Foo.new\n          foo.x = 10\n          foo.y = 20\n          foo.x + foo.y\n        CRYSTAL\n    end\n\n    it \"discards primitive struct_or_union_set and get (struct)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n          lib LibFoo\n            struct Foo\n              x : Int32\n              y : Int32\n            end\n          end\n\n          foo = LibFoo::Foo.new\n          foo.y = 10\n        CRYSTAL\n    end\n\n    it \"discards primitive struct_or_union_set because it's a copy\" do\n      interpret(<<-CRYSTAL).should eq(10)\n          lib LibFoo\n            struct Foo\n              x : Int32\n              y : Int32\n            end\n          end\n\n          def copy\n            LibFoo::Foo.new\n          end\n\n          copy.y = 10\n        CRYSTAL\n    end\n\n    it \"interprets primitive struct_or_union_set and get (union)\" do\n      interpret(<<-CRYSTAL).should eq(-2045911175)\n          lib LibFoo\n            union Foo\n              a : Bool\n              x : Int64\n              y : Int32\n            end\n          end\n\n          foo = LibFoo::Foo.new\n          foo.x = 123456789012345\n          foo.y\n        CRYSTAL\n    end\n\n    it \"sets extern struct proc field\" do\n      interpret(<<-CRYSTAL).should eq(13)\n          lib LibFoo\n            struct Foo\n              proc : Int32 -> Int32\n              field : Int32\n            end\n          end\n\n          foo = LibFoo::Foo.new\n          foo.field = 10\n          foo.proc = ->(x : Int32) { x + 1 }\n          foo.proc.call(2) + foo.field\n        CRYSTAL\n    end\n\n    it \"sets struct field through pointer\" do\n      interpret(<<-CRYSTAL).should eq(20)\n          lib LibFoo\n            struct Foo\n              x : Int32\n            end\n          end\n\n          foo = LibFoo::Foo.new\n          ptr = pointerof(foo)\n          ptr.value.x = 20\n          foo.x\n        CRYSTAL\n    end\n\n    it \"does automatic C cast\" do\n      interpret(<<-CRYSTAL).should eq(1)\n          lib LibFoo\n            struct Foo\n              x : UInt8\n            end\n          end\n\n          foo = LibFoo::Foo.new\n          foo.x = 257\n          foo.x\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/integration_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"integration\" do\n    it \"does Int32#to_s\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"123456789\"))\n        123456789.to_s\n      CRYSTAL\n    end\n\n    it \"does Float64#to_s (simple)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"1.5\"))\n        1.5.to_s\n      CRYSTAL\n    end\n\n    it \"does Float64#to_s (complex)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"123456789.12345\"))\n        123456789.12345.to_s\n      CRYSTAL\n    end\n\n    it \"does Range#to_a, Array#to_s\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"[1, 2, 3, 4, 5]\"))\n        (1..5).to_a.to_s\n      CRYSTAL\n    end\n\n    it \"does some Hash methods\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"90\")\n        h = {} of Int32 => Int32\n        10.times do |i|\n          h[i] = i * 2\n        end\n        h.values.sum\n      CRYSTAL\n    end\n\n    it \"does CSV\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq((1..6).sum.to_s)\n        require \"csv\"\n\n        csv = CSV.new <<-CSV, headers: true\n          a, b, c\n          1, 2, 3\n          4, 5, 6\n        CSV\n\n        sum = 0\n        csv.each do\n          {\"a\", \"b\", \"c\"}.each do |name|\n            sum += csv[name].to_i\n          end\n        end\n        sum\n      CRYSTAL\n    end\n\n    it \"does JSON\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"6\")\n        require \"json\"\n\n        json = JSON.parse <<-JSON\n          {\"a\": [1, 2, 3]}\n          JSON\n        json.as_h[\"a\"].as_a.sum(&.as_i)\n      CRYSTAL\n    end\n\n    it \"does JSON::Serializable\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"3\")\n        require \"json\"\n\n        record Point, x : Int32, y : Int32 do\n          include JSON::Serializable\n        end\n\n        point = Point.from_json <<-JSON\n          {\"x\": 1, \"y\": 2}\n        JSON\n        point.x + point.y\n      CRYSTAL\n    end\n\n    it \"does YAML\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"6\")\n        require \"yaml\"\n\n        yaml = YAML.parse <<-YAML\n          a:\n            - 1\n            - 2\n            - 3\n          YAML\n        yaml.as_h[\"a\"].as_a.sum(&.as_i)\n      CRYSTAL\n    end\n\n    it \"does YAML::Serializable\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"3\")\n        require \"yaml\"\n\n        record Point, x : Int32, y : Int32 do\n          include YAML::Serializable\n        end\n\n        point = Point.from_yaml <<-YAML\n          x: 1\n          y: 2\n        YAML\n        point.x + point.y\n      CRYSTAL\n    end\n\n    it \"does XML\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"3\")\n        require \"xml\"\n\n        doc = XML.parse(<<-XML\n          <?xml version='1.0' encoding='UTF-8'?>\n          <people>\n            <person id=\"1\" id2=\"2\">\n              <name>John</name>\n            </person>\n          </people>\n          XML\n        )\n        attrs = doc.root.not_nil!.children[1].attributes\n        id = attrs[\"id\"].content.to_i\n        id2 = attrs[\"id2\"].content.to_i\n        id + id2\n        CRYSTAL\n    end\n\n    it \"does String#includes?\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"true\")\n        a = \"Negative array size: -1\"\n        b = \"Negative array size\"\n        a.includes?(b)\n      CRYSTAL\n    end\n\n    it \"does IO.pipe (checks that StaticArray is passed correctly to C calls)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"hello\"))\n        IO.pipe do |r, w|\n          w.puts \"hello\"\n          r.gets.not_nil!\n        end\n      CRYSTAL\n    end\n\n    it \"does caller\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\":6:5 in 'bar'\"))\n        def foo\n          bar\n        end\n\n        def bar\n          caller[0]\n        end\n\n        foo\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/is_a_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"is_a?\" do\n    it \"does is_a? from NilableType to NonGenericClassType (true)\" do\n      interpret(<<-CRYSTAL).should eq(\"hello\")\n        a = \"hello\" || nil\n        if a.is_a?(String)\n          a\n        else\n          \"bar\"\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableType to NonGenericClassType (false)\" do\n      interpret(<<-CRYSTAL).should eq(\"bar\")\n        a = 1 == 1 ? nil : \"hello\"\n        if a.is_a?(String)\n          a\n        else\n          z = a\n          \"bar\"\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableType to GenericClassInstanceType (true)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class Foo(T)\n          def initialize(@x : T)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        a = Foo.new(1) || nil\n        if a.is_a?(Foo)\n          a.x\n        else\n          2\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableType to GenericClassInstanceType (false)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo(T)\n          def initialize(@x : T)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        a = 1 == 1 ? nil : Foo.new(1)\n        if a.is_a?(Foo)\n          a.x\n        else\n          z = a\n          2\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableReferenceUnionType to NonGenericClassType (true)\" do\n      interpret(<<-CRYSTAL).should eq(\"hello\")\n        class Foo\n        end\n\n        a = 1 == 1 ? \"hello\" : (1 == 1 ? Foo.new : nil)\n        if a.is_a?(String)\n          a\n        else\n          \"bar\"\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableReferenceUnionType to NonGenericClassType (false)\" do\n      interpret(<<-CRYSTAL).should eq(\"baz\")\n        class Foo\n        end\n\n        a = 1 == 1 ? \"hello\" : (1 == 1 ? Foo.new : nil)\n        if a.is_a?(Foo)\n          \"bar\"\n        else\n          \"baz\"\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from VirtualType to NonGenericClassType (true)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def x\n            1\n          end\n        end\n\n        class Bar < Foo\n          def x\n            2\n          end\n        end\n\n        foo = Bar.new || Foo.new\n        if foo.is_a?(Bar)\n          foo.x\n        else\n          20\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from VirtualType to NonGenericClassType (false)\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        class Foo\n          def x\n            1\n          end\n        end\n\n        class Bar < Foo\n          def x\n            2\n          end\n        end\n\n        foo = Foo.new || Bar.new\n        if foo.is_a?(Bar)\n          foo.x\n        else\n          20\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableProcType to Nil\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        proc = 1 == 1 ? nil : ->{ 1 }\n        if proc.nil?\n          10\n        else\n          20\n        end\n        CRYSTAL\n    end\n\n    it \"does is_a? from NilableProcType to non-Nil\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        proc = 1 == 2 ? nil : ->{ 10 }\n        if proc.is_a?(Proc)\n          proc.call\n        else\n          20\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/lib_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\nrequire \"../loader/spec_helper\"\nrequire \"../../support/env\"\n\nprivate def ldflags\n  {% if flag?(:msvc) %}\n    \"/LIBPATH:#{SPEC_CRYSTAL_LOADER_LIB_PATH} sum.lib\"\n  {% else %}\n    \"-L#{SPEC_CRYSTAL_LOADER_LIB_PATH} -lsum\"\n  {% end %}\nend\n\nprivate def ldflags_with_backtick\n  {% if flag?(:msvc) %}\n    \"/LIBPATH:#{SPEC_CRYSTAL_LOADER_LIB_PATH} `powershell.exe -C Write-Host -NoNewline sum.lib`\"\n  {% else %}\n    \"-L#{SPEC_CRYSTAL_LOADER_LIB_PATH} -l`echo sum`\"\n  {% end %}\nend\n\ndescribe Crystal::Repl::Interpreter do\n  around_all do |example|\n    FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n    build_c_dynlib(compiler_datapath(\"interpreter\", \"sum.c\"))\n\n    {% if flag?(:win32) %}\n      with_env({\"PATH\" => \"#{SPEC_CRYSTAL_LOADER_LIB_PATH}#{Process::PATH_DELIMITER}#{ENV[\"PATH\"]}\"}) do\n        example.run\n      end\n    {% else %}\n      example.run\n    {% end %}\n\n    FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n  end\n\n  context \"variadic calls\" do\n    it \"promotes float\" do\n      interpret(<<-CRYSTAL).should eq 3.5\n        @[Link(ldflags: #{ldflags.inspect})]\n        lib LibSum\n          fun sum_float(count : Int32, ...) : Float32\n        end\n\n        LibSum.sum_float(2, 1.2_f32, 2.3_f32)\n        CRYSTAL\n    end\n\n    it \"promotes int\" do\n      interpret(<<-CRYSTAL).should eq 5\n        @[Link(ldflags: #{ldflags.inspect})]\n        lib LibSum\n          fun sum_int(count : Int32, ...) : Int32\n        end\n\n        LibSum.sum_int(2, 1_u8, 4_i16)\n        CRYSTAL\n    end\n\n    it \"promotes enum\" do\n      interpret(<<-CRYSTAL).should eq 5\n        @[Link(ldflags: #{ldflags.inspect})]\n        lib LibSum\n          fun sum_int(count : Int32, ...) : Int32\n        end\n\n        enum E : Int8\n          ONE = 1\n        end\n\n        enum F : UInt16\n          FOUR = 4\n        end\n\n        LibSum.sum_int(2, E::ONE, F::FOUR)\n        CRYSTAL\n    end\n  end\n\n  context \"command expansion\" do\n    it \"expands ldflags\" do\n      interpret(<<-CRYSTAL).should eq 4\n        @[Link(ldflags: #{ldflags_with_backtick.inspect})]\n        lib LibSum\n          fun simple_sum_int(a : Int32, b : Int32) : Int32\n        end\n\n        LibSum.simple_sum_int(2, 2)\n        CRYSTAL\n    end\n  end\n\n  context \"proc pointer\" do\n    it \"calls extern fun\" do\n      interpret(<<-CRYSTAL).should eq 6\n        @[Link(ldflags: #{ldflags_with_backtick.inspect})]\n        lib LibSum\n          fun simple_sum_int(a : Int32, b : Int32) : Int32\n        end\n\n        class Foo\n          def initialize(@method : Proc(Int32, Int32, Int32))\n          end\n\n          def call(a, b)\n            @method.call(a, b)\n          end\n        end\n\n        foo = Foo.new(->LibSum.simple_sum_int)\n        foo.call(1, 5)\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/multidispatch_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"multidispatch\" do\n    it \"does dispatch on one argument\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def foo(x : Char)\n          x.ord.to_i32\n        end\n\n        def foo(x : Int32)\n          x\n        end\n\n        a = 42 || 'a'\n        foo(a)\n      CRYSTAL\n    end\n\n    it \"does dispatch on one argument inside module with implicit self\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        module Moo\n          def self.foo(x : Char)\n            x.ord.to_i32\n          end\n\n          def self.foo(x : Int32)\n            x\n          end\n\n          def self.bar\n            a = 42 || 'a'\n            foo(a)\n          end\n        end\n\n        Moo.bar\n      CRYSTAL\n    end\n\n    it \"does dispatch on one argument inside module with explicit receiver\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        module Moo\n          def self.foo(x : Char)\n            x.ord.to_i32\n          end\n\n          def self.foo(x : Int32)\n            x\n          end\n\n          def self.bar\n          end\n        end\n\n        a = 42 || 'a'\n        Moo.foo(a)\n      CRYSTAL\n    end\n\n    it \"does dispatch on receiver type\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Char\n          def foo\n            self.ord.to_i32\n          end\n        end\n\n        struct Int32\n          def foo\n            self\n          end\n        end\n\n        a = 42 || 'a'\n        a.foo\n      CRYSTAL\n    end\n\n    it \"does dispatch on receiver type and argument type\" do\n      interpret(<<-CRYSTAL).should eq(42 + 'b'.ord)\n        struct Char\n          def foo(x : Int32)\n            self.ord.to_i32 + x\n          end\n\n          def foo(x : Char)\n            self.ord.to_i32 + x.ord.to_i32\n          end\n        end\n\n        struct Int32\n          def foo(x : Int32)\n            self + x\n          end\n\n          def foo(x : Char)\n            self + x.ord.to_i32\n          end\n        end\n\n        a = 42 || 'a'\n        b = 'b' || 43\n        a.foo(b)\n      CRYSTAL\n    end\n\n    it \"does dispatch on receiver type and argument type, multiple times\" do\n      interpret(<<-CRYSTAL).should eq(2 * (42 + 'b'.ord))\n        struct Char\n          def foo(x : Int32)\n            self.ord.to_i32 + x\n          end\n\n          def foo(x : Char)\n            self.ord.to_i32 + x.ord.to_i32\n          end\n        end\n\n        struct Int32\n          def foo(x : Int32)\n            self + x\n          end\n\n          def foo(x : Char)\n            self + x.ord.to_i32\n          end\n        end\n\n        a = 42 || 'a'\n        b = 'b' || 43\n        x = a.foo(b)\n        y = a.foo(b)\n        x + y\n      CRYSTAL\n    end\n\n    it \"does dispatch on one argument with struct receiver, and modifies it\" do\n      interpret(<<-CRYSTAL).should eq(32)\n        struct Foo\n          def initialize\n            @x = 2_i64\n          end\n\n          def foo(x : Int32)\n            v = @x + x\n            @x = 10_i64\n            v\n          end\n\n          def foo(x : Char)\n            v = @x + x.ord.to_i32\n            @x = 30_i64\n            v\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.new\n\n        a = 20 || 'a'\n        b = foo.foo(a)\n        b + foo.x\n      CRYSTAL\n    end\n\n    it \"downcasts self from union to struct (pass pointer to self)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def initialize\n            @x = 1_i64\n          end\n\n          def x\n            @x\n          end\n        end\n\n        struct Point\n          def initialize\n            @x = 2_i64\n          end\n\n          def x\n            @x\n          end\n        end\n\n        obj = Point.new || Foo.new\n        obj.x\n      CRYSTAL\n    end\n\n    it \"does dispatch on virtual type\" do\n      interpret(<<-CRYSTAL).should eq(4)\n        abstract class Foo\n          def foo\n            1\n          end\n        end\n\n        class Bar < Foo\n        end\n\n        class Baz < Foo\n          def foo\n            3\n          end\n        end\n\n        class Qux < Foo\n        end\n\n        foo = Bar.new || Baz.new\n        x = foo.foo\n\n        foo = Baz.new || Bar.new\n        y = foo.foo\n\n        x + y\n      CRYSTAL\n    end\n\n    it \"does dispatch on one argument with block\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        def foo(x : Char)\n          yield x.ord.to_i32\n        end\n\n        def foo(x : Int32)\n          yield x\n        end\n\n        a = 32 || 'a'\n        foo(a) do |x|\n          x + 10\n        end\n      CRYSTAL\n    end\n\n    it \"doesn't compile block if it's not used (no yield)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Object\n          def try\n            yield self\n          end\n        end\n\n        struct Nil\n          def try(&)\n            self\n          end\n        end\n\n        a = 1 || nil\n        b = a.try { |x| x + 1 }\n        b || 10\n      CRYSTAL\n    end\n\n    it \"does multidispatch on virtual metaclass type (1)\" do\n      interpret(<<-CRYSTAL).should eq(\"BB\")\n        class Class\n          def lt(other : T.class) : String forall T\n            {% @type %}\n            other.gt(self)\n          end\n\n          def gt(other : T.class) forall T\n            {{ @type.stringify + T.stringify }}\n          end\n        end\n\n        class A\n        end\n\n        class B < A\n        end\n\n        t = B || A\n        t.lt(t)\n      CRYSTAL\n    end\n\n    it \"does multidispatch on virtual metaclass type (2)\" do\n      interpret(<<-CRYSTAL).should eq(\"BB\")\n        class Class\n          def lt(other : T.class) : String forall T\n            {% @type %}\n            other.gt(self)\n          end\n\n          def gt(other : T.class) forall T\n            {{ @type.stringify + T.stringify }}\n          end\n        end\n\n        class A\n        end\n\n        class B < A\n        end\n\n        class C < B\n        end\n\n        t = B || A\n        t.lt(t)\n      CRYSTAL\n    end\n\n    it \"passes self as pointer when doing multidispatch\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        struct Foo\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        struct Bar\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n\n          def to_unsafe\n            pointerof(@x)\n          end\n        end\n\n        foo = Foo.new(0) || Bar.new(1)\n        foo.to_unsafe.value = 10\n        foo.x\n      CRYSTAL\n    end\n\n    it \"passes self as pointer when doing multidispatch (2)\" do\n      interpret(<<-CRYSTAL).should be_true\n        struct Tuple\n          def ==(other)\n            false\n          end\n        end\n\n        a = 1.as(Int32 | Tuple(Int64, Int64))\n        a == 1\n      CRYSTAL\n    end\n\n    it \"initialize multidispatch\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        struct Foo\n          def initialize(x : Int64)\n            initialize(x, 1 || 'a')\n          end\n\n          def initialize(@x : Int64, y : Int32)\n          end\n\n          def initialize(@x : Int64, y : Char)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        Foo.new(1_i64).x\n      CRYSTAL\n    end\n\n    it \"does multidispatch with mandatory named arguments\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        class Object\n          def foo(obj, *, file = \"\")\n            obj\n          end\n        end\n\n        (\"\" || nil).foo 1, file: \"\"\n      CRYSTAL\n    end\n\n    it \"does multidispatch with captured block (#12217)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class A\n          def then(&callback : Int32 -> Int32)\n            callback.call(70)\n          end\n        end\n\n        class B < A\n          def then(&callback : Int32 -> Int32)\n            callback.call(30)\n          end\n        end\n\n        a = A.new\n        b = B.new\n\n        a_value = (a || b).then do |x|\n          x + 3\n        end\n\n        b_value = (b || a).then do |x|\n          x + 1\n        end\n\n        a_value - b_value\n      CRYSTAL\n    end\n\n    it \"casts multidispatch argument to the def's arg type\" do\n      interpret(<<-CRYSTAL)\n        def foo(a : String) forall T\n        end\n\n        def foo(a)\n          a\n        end\n\n        foo(\"b\" || nil)\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/named_tuple_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"named tuple\" do\n    it \"interprets named tuple literal and access by known index\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        a = {a: 1, b: 2, c: 3}\n        a[:a] + a[:b] + a[:c]\n      CRYSTAL\n    end\n\n    it \"interprets named tuple metaclass indexer\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Int32\n          def self.foo\n            2\n          end\n        end\n\n        a = {a: 1, b: 'a'}\n        a.class[:a].foo\n      CRYSTAL\n    end\n\n    it \"discards named tuple (#12383)\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        1 + ({a: 1, b: 2, c: 3, d: 4}; 2)\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/pointers_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"pointers\" do\n    it \"interprets pointer set and get (int)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        ptr = Pointer(Int32).malloc(1_u64)\n        ptr.value = 10\n        ptr.value\n      CRYSTAL\n    end\n\n    it \"interprets pointer set and get (bool)\" do\n      interpret(<<-CRYSTAL).should be_true\n        ptr = Pointer(Bool).malloc(1_u64)\n        ptr.value = true\n        ptr.value\n      CRYSTAL\n    end\n\n    it \"interprets pointer set and get (clear stack)\" do\n      interpret(<<-CRYSTAL).should eq(50.unsafe_chr)\n        ptr = Pointer(UInt8).malloc(1_u64)\n        ptr.value = 50_u8\n        ptr.value.unsafe_chr\n      CRYSTAL\n    end\n\n    it \"interprets pointerof, mutates pointer, read var\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        a = 1\n        ptr = pointerof(a)\n        ptr.value = 2\n        a\n      CRYSTAL\n    end\n\n    it \"interprets pointerof, mutates var, read pointer\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        a = 1\n        ptr = pointerof(a)\n        a = 2\n        ptr.value\n      CRYSTAL\n    end\n\n    it \"interprets pointerof and mutates memory (there are more variables)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        x = 42\n        a = 1\n        ptr = pointerof(a)\n        ptr.value = 2\n        a\n      CRYSTAL\n    end\n\n    it \"pointerof instance var\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n\n          def x_ptr\n            pointerof(@x)\n          end\n        end\n\n        foo = Foo.new(1)\n        ptr = foo.x_ptr\n        ptr.value = 2\n        foo.x\n      CRYSTAL\n    end\n\n    it \"pointerof class var\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          @@x : Int32?\n\n          def self.x_ptr\n            pointerof(@@x)\n          end\n\n          def self.x\n            @@x\n          end\n        end\n\n        ptr = Foo.x_ptr\n        v = ptr.value\n        ptr.value = 2\n        x = Foo.x\n        x || 0\n      CRYSTAL\n    end\n\n    it \"pointerof read instance var\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n\n          def x_ptr\n            pointerof(@x)\n          end\n        end\n\n        foo = Foo.new(1)\n        ptr = pointerof(foo.@x)\n        ptr.value = 2\n        foo.x\n      CRYSTAL\n    end\n\n    it \"pointerof read `StaticArray#@buffer` (1)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct StaticArray(T, N)\n          def to_unsafe\n            pointerof(@buffer)\n          end\n\n          def x\n            @buffer\n          end\n        end\n\n        foo = uninitialized Int32[4]\n        foo.to_unsafe.value = 2\n        foo.x\n        CRYSTAL\n    end\n\n    it \"pointerof read `StaticArray#@buffer` (2)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        foo = uninitialized Int32[4]\n        pointerof(foo.@buffer).value = 2\n        foo.@buffer\n        CRYSTAL\n    end\n\n    it \"interprets pointer set and get (union type)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        ptr = Pointer(Int32 | Bool).malloc(1_u64)\n        ptr.value = 10\n        value = ptr.value\n        if value.is_a?(Int32)\n          value\n        else\n          20\n        end\n      CRYSTAL\n    end\n\n    it \"interprets pointer set and get (union type, setter value)\" do\n      interpret(<<-CRYSTAL).should eq(10)\n        ptr = Pointer(Int32 | Bool).malloc(1_u64)\n        ptr.value = 10\n      CRYSTAL\n    end\n\n    it \"interprets pointer new and pointer address\" do\n      interpret(<<-CRYSTAL).should eq(123_u64)\n        ptr = Pointer(Int32 | Bool).new(123_u64)\n        ptr.address\n      CRYSTAL\n    end\n\n    it \"interprets pointer diff\" do\n      interpret(<<-CRYSTAL).should eq(8_i64)\n        ptr1 = Pointer(Int32).new(133_u64)\n        ptr2 = Pointer(Int32).new(100_u64)\n        ptr1 - ptr2\n      CRYSTAL\n    end\n\n    it \"interprets pointer diff, negative\" do\n      interpret(<<-CRYSTAL).should eq(-8_i64)\n        ptr1 = Pointer(Int32).new(133_u64)\n        ptr2 = Pointer(Int32).new(100_u64)\n        ptr2 - ptr1\n      CRYSTAL\n    end\n\n    it \"discards pointer malloc\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        Pointer(Int32).malloc(1_u64)\n        1\n      CRYSTAL\n    end\n\n    it \"discards pointer get\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        ptr = Pointer(Int32).malloc(1_u64)\n        ptr.value\n        1\n      CRYSTAL\n    end\n\n    it \"discards pointer set\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        ptr = Pointer(Int32).malloc(1_u64)\n        ptr.value = 1\n      CRYSTAL\n    end\n\n    it \"discards pointer new\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        Pointer(Int32).new(1_u64)\n        1\n      CRYSTAL\n    end\n\n    it \"discards pointer diff\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        ptr1 = Pointer(Int32).new(133_u64)\n        ptr2 = Pointer(Int32).new(100_u64)\n        ptr1 - ptr2\n        1\n      CRYSTAL\n    end\n\n    it \"discards pointerof\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = 1\n        pointerof(a)\n        3\n      CRYSTAL\n    end\n\n    it \"interprets pointer add\" do\n      interpret(<<-CRYSTAL).should eq(9)\n        ptr = Pointer(Int32).new(1_u64)\n        ptr2 = ptr + 2_i64\n        ptr2.address\n      CRYSTAL\n    end\n\n    it \"discards pointer add\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        ptr = Pointer(Int32).new(1_u64)\n        ptr + 2_i64\n        3\n      CRYSTAL\n    end\n\n    it \"interprets pointer realloc\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        ptr = Pointer(Int32).malloc(1_u64)\n        ptr2 = ptr.realloc(2_u64)\n        3\n      CRYSTAL\n    end\n\n    it \"discards pointer realloc\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        ptr = Pointer(Int32).malloc(1_u64)\n        ptr.realloc(2_u64)\n        3\n      CRYSTAL\n    end\n\n    it \"interprets pointer realloc wrapper\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        struct Pointer(T)\n          def realloc(n)\n            realloc(n.to_u64)\n          end\n        end\n\n        ptr = Pointer(Int32).malloc(1_u64)\n        ptr2 = ptr.realloc(2)\n        3\n      CRYSTAL\n    end\n\n    it \"interprets nilable pointer truthiness\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        ptr = 1 == 1 ? Pointer(UInt8).malloc(1) : nil\n        if ptr\n          1\n        else\n          2\n        end\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/primitives_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\nprivate macro assert_overflows(code, file = __FILE__, line = __LINE__)\n  it \"overflows on {{code}}\", file: {{file}}, line: {{line}} do\n    interpret(%(\n      class OverflowError < Exception; end\n\n      fun __crystal_raise_overflow : NoReturn\n        raise OverflowError.new\n      end\n\n      @[Primitive(:interpreter_raise_without_backtrace)]\n      def raise(exception : Exception) : NoReturn\n      end\n\n      begin\n        a = {{code}}\n        1\n      rescue OverflowError\n        2\n      end\n    )).should eq(2)\n  end\nend\n\ndescribe Crystal::Repl::Interpreter do\n  context \"literals\" do\n    it \"interprets nil\" do\n      interpret(\"nil\").should be_nil\n    end\n\n    it \"interprets a bool (false)\" do\n      interpret(\"false\").should be_false\n    end\n\n    it \"interprets a bool (true)\" do\n      interpret(\"true\").should be_true\n    end\n\n    it \"interprets an Int8\" do\n      interpret(\"123_i8\").should eq(123_i8)\n    end\n\n    it \"interprets an UInt8\" do\n      interpret(\"145_u8\").should eq(145_u8)\n    end\n\n    it \"interprets an Int16\" do\n      interpret(\"12345_i16\").should eq(12345_i16)\n    end\n\n    it \"interprets an UInt16\" do\n      interpret(\"12389_u16\").should eq(12389_u16)\n    end\n\n    it \"interprets an Int32\" do\n      interpret(\"123456789_i32\").should eq(123456789)\n    end\n\n    it \"interprets an UInt32\" do\n      interpret(\"323456789_u32\").should eq(323456789_u32)\n    end\n\n    it \"interprets an Int64\" do\n      interpret(\"123_i64\").should eq(123_i64)\n    end\n\n    it \"interprets an UInt64\" do\n      interpret(\"123_u64\").should eq(123_u64)\n    end\n\n    it \"interprets an Int128\" do\n      interpret(\"123_i128\").should eq(123_i128)\n    end\n\n    it \"interprets an UInt128\" do\n      interpret(\"123_u128\").should eq(123_u128)\n    end\n\n    it \"interprets a Float32\" do\n      interpret(\"1.5_f32\").should eq(1.5_f32)\n    end\n\n    it \"interprets a Float64\" do\n      interpret(\"1.5\").should eq(1.5)\n    end\n\n    it \"interprets a char\" do\n      interpret(\"'a'\").should eq('a')\n    end\n\n    it \"interprets a String literal\" do\n      interpret(%(\"Hello world!\")).should eq(\"Hello world!\")\n    end\n\n    it \"uses a string pool\" do\n      interpret(<<-CRYSTAL).should be_true\n        \"a\".object_id == \"a\".object_id\n      CRYSTAL\n    end\n\n    it \"precomputes string literal length\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        \"旅\".@length\n      CRYSTAL\n    end\n  end\n\n  context \"local variables\" do\n    it \"interprets variable set\" do\n      interpret(<<-CRYSTAL).should eq(1)\n      a = 1\n      CRYSTAL\n    end\n\n    it \"interprets variable set with type restriction (#13023)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n      a : Int32 = 1\n      CRYSTAL\n    end\n\n    it \"interprets variable set and get\" do\n      interpret(<<-CRYSTAL).should eq(1)\n      a = 1\n      a\n      CRYSTAL\n    end\n\n    it \"interprets variable set and get, second local var\" do\n      interpret(<<-CRYSTAL).should eq(1)\n      x = 10\n      a = 1\n      a\n      CRYSTAL\n    end\n\n    it \"interprets variable set and get with operations\" do\n      interpret(<<-CRYSTAL).should eq(6)\n      a = 1\n      b = 2\n      c = 3\n      a + b + c\n      CRYSTAL\n    end\n\n    it \"interprets uninitialized\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = uninitialized Int32\n        a = 3\n        a\n        CRYSTAL\n    end\n\n    it \"doesn't declare variable with no type\" do\n      interpret(<<-CRYSTAL).should be_nil\n      x = nil\n      if x\n        y = x\n      end\n      CRYSTAL\n    end\n\n    it \"doesn't declare variable with no type inside method\" do\n      interpret(<<-CRYSTAL).should be_nil\n        def foo(x)\n          if x\n            y = x\n          end\n        end\n\n        foo(nil)\n      CRYSTAL\n    end\n\n    it \"assigns to underscore\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        _ = (a = 1)\n        a\n      CRYSTAL\n    end\n\n    it \"doesn't discard underscore right hand side\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        a = (_ = 1)\n        a\n      CRYSTAL\n    end\n\n    it \"interprets at the class level\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        x = 0\n\n        class Foo\n          x = self.foo\n\n          def self.foo\n            bar\n          end\n\n          def self.bar\n            1\n          end\n        end\n\n        x\n      CRYSTAL\n    end\n\n    it \"interprets local variable declaration (#12229)\" do\n      interpret(<<-CRYSTAL).should eq(1)\n      a : Int32 = 1\n      a\n      CRYSTAL\n    end\n  end\n\n  context \"conversion\" do\n    {% for target_type in %w(u8 i8 u16 i16 u32 i32 u i u64 i64 f32 f64).map(&.id) %}\n      it \"interprets Int8::MAX#to_{{target_type}}!\" do\n        interpret(\"#{Int8::MAX}_i8.to_{{target_type}}!\").should eq(Int8::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int8::MIN#to_{{target_type}}!\" do\n        interpret(\"#{Int8::MIN}_i8.to_{{target_type}}!\").should eq(Int8::MIN.to_{{target_type}}!)\n      end\n\n      it \"interprets UInt8::MAX#to_{{target_type}}!\" do\n        interpret(\"#{UInt8::MAX}_u8.to_{{target_type}}!\").should eq(UInt8::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int16::MAX#to_{{target_type}}!\" do\n        interpret(\"#{Int16::MAX}_i16.to_{{target_type}}!\").should eq(Int16::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int16::MIN#to_{{target_type}}!\" do\n        interpret(\"#{Int16::MIN}_i16.to_{{target_type}}!\").should eq(Int16::MIN.to_{{target_type}}!)\n      end\n\n      it \"interprets UInt16::MAX#to_{{target_type}}!\" do\n        interpret(\"#{UInt16::MAX}_u16.to_{{target_type}}!\").should eq(UInt16::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int32::MAX#to_{{target_type}}!\" do\n        interpret(\"#{Int32::MAX}.to_{{target_type}}!\").should eq(Int32::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int32::MIN#to_{{target_type}}!\" do\n        interpret(\"#{Int32::MIN}.to_{{target_type}}!\").should eq(Int32::MIN.to_{{target_type}}!)\n      end\n\n      it \"interprets UInt32::MAX#to_{{target_type}}!\" do\n        interpret(\"#{UInt32::MAX}_u32.to_{{target_type}}!\").should eq(UInt32::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int64::MAX#to_{{target_type}}!\" do\n        interpret(\"#{Int64::MAX}_i64.to_{{target_type}}!\").should eq(Int64::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Int64::MIN#to_{{target_type}}!\" do\n        interpret(\"#{Int64::MIN}_i64.to_{{target_type}}!\").should eq(Int64::MIN.to_{{target_type}}!)\n      end\n\n      it \"interprets UInt64::MAX#to_{{target_type}}!\" do\n        interpret(\"#{UInt64::MAX}_u64.to_{{target_type}}!\").should eq(UInt64::MAX.to_{{target_type}}!)\n      end\n\n      it \"interprets Float32#to_{{target_type}}! (positive)\" do\n        f = 23.8_f32\n        interpret(\"23.8_f32.to_{{target_type}}!\").should eq(f.to_{{target_type}}!)\n      end\n\n      it \"interprets Float64#to_{{target_type}}! (positive)\" do\n        f = 23.8_f64\n        interpret(\"23.8_f64.to_{{target_type}}!\").should eq(f.to_{{target_type}}!)\n      end\n\n      {% unless target_type.starts_with?(\"u\") %} # Do not test undefined behavior that might differ (#13736)\n        it \"interprets Float32#to_{{target_type}}! (negative)\" do\n          f = -23.8_f32\n          interpret(\"-23.8_f32.to_{{target_type}}!\").should eq(f.to_{{target_type}}!)\n        end\n\n        it \"interprets Float64#to_{{target_type}}! (negative)\" do\n          f = -23.8_f64\n          interpret(\"-23.8_f64.to_{{target_type}}!\").should eq(f.to_{{target_type}}!)\n        end\n      {% end %}\n    {% end %}\n\n    it \"interprets Char#ord\" do\n      interpret(\"'a'.ord\").should eq('a'.ord)\n    end\n\n    it \"Int32#unsafe_chr\" do\n      interpret(\"97.unsafe_chr\").should eq(97.unsafe_chr)\n    end\n\n    it \"UInt8#unsafe_chr\" do\n      interpret(\"97_u8.unsafe_chr\").should eq(97.unsafe_chr)\n    end\n\n    it \"discards conversion\" do\n      interpret(<<-CRYSTAL).should eq(3)\n      1.to_i8!\n      3\n      CRYSTAL\n    end\n\n    it \"discards conversion with local var\" do\n      interpret(<<-CRYSTAL).should eq(3)\n      x = 1\n      x.to_i8!\n      3\n      CRYSTAL\n    end\n  end\n\n  context \"overflow\" do\n    context \"+\" do\n      assert_overflows 1_u8 + 255\n      assert_overflows 1_i8 + 128\n      assert_overflows 1_u16 + 65535\n      assert_overflows 1_i16 + 32767\n      assert_overflows 1_u32 + 4294967295\n      assert_overflows 1_i32 + 2147483647\n      assert_overflows 1_u64 + 18446744073709551615u64\n      assert_overflows 1_i64 + 9223372036854775807\n    end\n\n    context \"-\" do\n      assert_overflows 1_u8 - 2\n      assert_overflows 1_i8 - 256\n      assert_overflows 1_u16 - 2\n      assert_overflows 1_i16 - 32770\n      assert_overflows 1_u32 - 2\n      assert_overflows 1_i32 - 2147483650\n      assert_overflows 1_u64 - 2\n      assert_overflows 1_i64 - 9223372036854775810u64\n    end\n\n    context \"*\" do\n      assert_overflows 10_u8 * 26\n      assert_overflows 10_i8 * 14\n      assert_overflows 10_u16 * 6600\n      assert_overflows 10_i16 * 3300\n      assert_overflows 20_u32 * 429496729\n      assert_overflows 20_i32 * 214748364\n      assert_overflows 20_u64 * 1844674407370955161\n      assert_overflows 20_i64 * 922337203685477580\n    end\n\n    context \"conversion\" do\n      assert_overflows 128_u8.to_i8\n\n      assert_overflows -1_i8.to_u8\n      assert_overflows -1_i8.to_u16\n      assert_overflows -1_i8.to_u32\n      assert_overflows -1_i8.to_u64\n\n      assert_overflows 128_u16.to_i8\n      assert_overflows 32768_u16.to_i16\n\n      assert_overflows -1_i16.to_u8\n      assert_overflows -1_i16.to_u16\n      assert_overflows -1_i16.to_u32\n      assert_overflows -1_i16.to_u64\n\n      assert_overflows 128_u32.to_i8\n      assert_overflows 32768_u32.to_i16\n      assert_overflows 2147483648_u32.to_i32\n\n      assert_overflows -1_i32.to_u8\n      assert_overflows -1_i32.to_u16\n      assert_overflows -1_i32.to_u32\n      assert_overflows -1_i32.to_u64\n\n      assert_overflows 128_u64.to_i8\n      assert_overflows 32768_u64.to_i16\n      assert_overflows 2147483648_u64.to_i32\n      assert_overflows 9223372036854775808_u64.to_i64\n\n      assert_overflows -1_i64.to_u8\n      assert_overflows -1_i64.to_u16\n      assert_overflows -1_i64.to_u32\n      assert_overflows -1_i64.to_u64\n\n      assert_overflows 256_f32.to_u8\n      assert_overflows 128_f32.to_i8\n      assert_overflows 65536_f32.to_u16\n      assert_overflows 32768_f32.to_i16\n\n      # TODO: uncomment these once they also overflow on compiled Crystal\n      # assert_overflows 4294967296_f32.to_u32\n      # assert_overflows 2147483648_f32.to_i32\n      # assert_overflows 18446744073709551616_f32.to_u64\n      # assert_overflows 9223372036854775808_f32.to_i64\n\n      assert_overflows 256_f64.to_u8\n      assert_overflows 128_f64.to_i8\n      assert_overflows 65536_f64.to_u16\n      assert_overflows 32768_f64.to_i16\n      assert_overflows 4294967296_f64.to_u32\n      assert_overflows 2147483648_f64.to_i32\n\n      # TODO: uncomment these once they also overflow on compiled Crystal\n      # assert_overflows 18446744073709551616_f64.to_u64\n      # assert_overflows 9223372036854775808_f64.to_i64\n\n      assert_overflows 1.7976931348623157e+308.to_f32\n    end\n  end\n\n  context \"math\" do\n    it \"interprets Int32 + Int32\" do\n      interpret(\"1 + 2\").should eq(3)\n    end\n\n    it \"interprets Int32 &+ Int32\" do\n      interpret(\"1 &+ 2\").should eq(3)\n    end\n\n    it \"interprets Int64 + Int64\" do\n      interpret(\"1_i64 + 2_i64\").should eq(3)\n    end\n\n    it \"interprets Int32 - Int32\" do\n      interpret(\"1 - 2\").should eq(-1)\n    end\n\n    it \"interprets Int32 &- Int32\" do\n      interpret(\"1 &- 2\").should eq(-1)\n    end\n\n    it \"interprets Int32 * Int32\" do\n      interpret(\"2 * 3\").should eq(6)\n    end\n\n    it \"interprets Int32 &* Int32\" do\n      interpret(\"2 &* 3\").should eq(6)\n    end\n\n    it \"interprets UInt64 * Int32\" do\n      interpret(\"2_u64 * 3\").should eq(6)\n    end\n\n    it \"interprets UInt8 | Int32\" do\n      interpret(\"1_u8 | 2\").should eq(3)\n    end\n\n    it \"interprets UInt64 | UInt32\" do\n      interpret(\"1_u64 | 2_u32\").should eq(3)\n    end\n\n    it \"interprets UInt32 - Int32\" do\n      interpret(\"3_u32 - 2\").should eq(1)\n    end\n\n    it \"interprets Int32 + Float64\" do\n      interpret(\"1 + 2.5\").should eq(3.5)\n    end\n\n    it \"interprets Float32 + Float64\" do\n      interpret(\"1.0_f32 + 0.0\").should eq(1.0_f32)\n    end\n\n    it \"interprets Float64 + Int32\" do\n      interpret(\"2.5 + 1\").should eq(3.5)\n    end\n\n    it \"interprets Float64 + Float64\" do\n      interpret(\"2.5 + 2.3\").should eq(4.8)\n    end\n\n    it \"interprets Float64 - Float64\" do\n      interpret(\"2.5 - 2.3\").should eq(2.5 - 2.3)\n    end\n\n    it \"interprets Float64 * Float64\" do\n      interpret(\"2.5 * 2.3\").should eq(2.5 * 2.3)\n    end\n\n    it \"interprets Int8 + Int8\" do\n      interpret(\"1_i8 + 2_i8\").should eq(3)\n    end\n\n    it \"interprets UInt64 & Int32\" do\n      interpret(\"604_u64 & 4095\").should eq(604)\n    end\n\n    it \"interprets Int128 + Int32\" do\n      interpret(\"1_i128 + 2\").should eq(3)\n    end\n\n    it \"discards math\" do\n      interpret(\"1 + 2; 4\").should eq(4)\n    end\n\n    it \"interprets Int32.unsafe_shl(Int32) with self\" do\n      interpret(<<-CRYSTAL).should eq(4)\n        struct Int32\n          def shl2\n            unsafe_shl(2)\n          end\n        end\n\n        a = 1\n        a.shl2\n        CRYSTAL\n    end\n  end\n\n  context \"comparisons\" do\n    it \"interprets Bool == Bool (false)\" do\n      interpret(\"true == false\").should be_false\n    end\n\n    it \"interprets Bool == Bool (true)\" do\n      interpret(\"true == true\").should be_true\n    end\n\n    it \"interprets Bool != Bool (false)\" do\n      interpret(\"true != true\").should be_false\n    end\n\n    it \"interprets Bool != Bool (true)\" do\n      interpret(\"true != false\").should be_true\n    end\n\n    it \"interprets Int32 < Int32\" do\n      interpret(\"1 < 2\").should be_true\n    end\n\n    it \"interprets Int32 == Int32 (true)\" do\n      interpret(\"1 == 1\").should be_true\n    end\n\n    it \"interprets Int32 == Int32 (false)\" do\n      interpret(\"1 == 2\").should be_false\n    end\n\n    it \"interprets Int32 != Int32 (true)\" do\n      interpret(\"1 != 2\").should be_true\n    end\n\n    it \"interprets Int32 != Int32 (false)\" do\n      interpret(\"1 != 1\").should be_false\n    end\n\n    it \"interprets Int32 == UInt64 (true)\" do\n      interpret(\"1 == 1_u64\").should be_true\n    end\n\n    it \"interprets Int32 == UInt64 (false)\" do\n      interpret(\"2 == 1_u64\").should be_false\n    end\n\n    it \"interprets Int32 != UInt64 (true)\" do\n      interpret(\"1 != 2_u64\").should be_true\n    end\n\n    it \"interprets Int32 != UInt64 (false)\" do\n      interpret(\"1 != 1_u64\").should be_false\n    end\n\n    it \"interprets UInt64 != Int32 (true)\" do\n      interpret(\"2_u64 != 1\").should be_true\n    end\n\n    it \"interprets UInt64 != Int32 (false)\" do\n      interpret(\"1_u64 != 1\").should be_false\n    end\n\n    it \"interprets Int128 == Int128 (false)\" do\n      interpret(\"1_i128 == 2_i128\").should be_false\n    end\n\n    it \"interprets Int128 == Int128 (true)\" do\n      interpret(\"1_i128 == 1_i128\").should be_true\n    end\n\n    it \"interprets Float32 / Int32\" do\n      interpret(\"2.5_f32 / 2\").should eq(2.5_f32 / 2)\n    end\n\n    it \"interprets Float32 / Float32\" do\n      interpret(\"2.5_f32 / 2.1_f32\").should eq(2.5_f32 / 2.1_f32)\n    end\n\n    it \"interprets Float64 / Float64\" do\n      interpret(\"2.5 / 2.1\").should eq(2.5 / 2.1)\n    end\n\n    it \"interprets Float32 fdiv Float64\" do\n      interpret(\"2.5_f32.fdiv(2.1_f64)\").should eq(2.5_f32.fdiv(2.1_f64))\n    end\n\n    it \"interprets Float64 fdiv Float32\" do\n      interpret(\"2.5_f64.fdiv(2.1_f32)\").should eq(2.5_f64.fdiv(2.1_f32))\n    end\n\n    it \"interprets Int32 == Float64 (true)\" do\n      interpret(\"1 == 1.0\").should be_true\n    end\n\n    it \"interprets Int32 == Float64 (false)\" do\n      interpret(\"1 == 1.2\").should be_false\n    end\n\n    it \"interprets Int32 > Float64 (true)\" do\n      interpret(\"2 > 1.9\").should be_true\n    end\n\n    it \"interprets Int32 > Float64 (false)\" do\n      interpret(\"2 > 2.1\").should be_false\n    end\n\n    it \"interprets UInt8 < Int32 (true, right is greater than zero)\" do\n      interpret(\"1_u8 < 2\").should be_true\n    end\n\n    it \"interprets UInt8 < Int32 (false, right is greater than zero)\" do\n      interpret(\"1_u8 < 0\").should be_false\n    end\n\n    it \"interprets UInt8 < Int32 (false, right is less than zero)\" do\n      interpret(\"1_u8 < -1\").should be_false\n    end\n\n    it \"interprets UInt64 < Int32 (true, right is greater than zero)\" do\n      interpret(\"1_u64 < 2\").should be_true\n    end\n\n    it \"interprets UInt64 < Int32 (false, right is greater than zero)\" do\n      interpret(\"1_u64 < 0\").should be_false\n    end\n\n    it \"interprets UInt64 < Int32 (false, right is less than zero)\" do\n      interpret(\"1_u64 < -1\").should be_false\n    end\n\n    it \"interprets UInt64 > UInt32 (true)\" do\n      interpret(\"1_u64 > 0_u32\").should be_true\n    end\n\n    it \"interprets UInt64 > UInt32 (false)\" do\n      interpret(\"0_u64 > 1_u32\").should be_false\n    end\n\n    it \"interprets UInt32 < Int32 (true)\" do\n      interpret(\"1_u32 < 2\").should be_true\n    end\n\n    it \"interprets UInt32 < Int32 (false)\" do\n      interpret(\"1_u32 < 1\").should be_false\n    end\n\n    it \"interprets UInt64 == Int32 (false when Int32 < 0)\" do\n      interpret(\"1_u64 == -1\").should be_false\n    end\n\n    it \"interprets UInt64 == Int32 (false when Int32 >= 0)\" do\n      interpret(\"1_u64 == 0\").should be_false\n    end\n\n    it \"interprets UInt64 == Int32 (true when Int32 >= 0)\" do\n      interpret(\"1_u64 == 1\").should be_true\n    end\n\n    it \"interprets Char == Char (false)\" do\n      interpret(\"'a' == 'b'\").should be_false\n    end\n\n    it \"interprets Char == Char (true)\" do\n      interpret(\"'a' == 'a'\").should be_true\n    end\n\n    it \"interprets Int32 < Float64\" do\n      interpret(\"1 < 2.5\").should be_true\n    end\n\n    it \"interprets Float64 < Int32\" do\n      interpret(\"1.2 < 2\").should be_true\n    end\n\n    it \"interprets Float64 < Float64\" do\n      interpret(\"1.2 < 2.3\").should be_true\n    end\n\n    it \"interprets UInt64.unsafe_mod(UInt64)\" do\n      interpret(<<-CRYSTAL).should eq(906272454103984)\n        a = 10097976637018756016_u64\n        b = 9007199254740992_u64\n        a.unsafe_mod(b)\n        CRYSTAL\n    end\n\n    it \"discards comparison\" do\n      interpret(\"1 < 2; 3\").should eq(3)\n    end\n  end\n\n  context \"logical operations\" do\n    it \"interprets not for nil\" do\n      interpret(\"!nil\").should be_true\n    end\n\n    it \"interprets not for nil type\" do\n      interpret(\"x = 1; !(x = 2; nil); x\").should eq(2)\n    end\n\n    it \"interprets not for bool true\" do\n      interpret(\"!true\").should be_false\n    end\n\n    it \"interprets not for bool false\" do\n      interpret(\"!false\").should be_true\n    end\n\n    it \"discards nil not\" do\n      interpret(\"!nil; 3\").should eq(3)\n    end\n\n    it \"discards bool not\" do\n      interpret(\"!false; 3\").should eq(3)\n    end\n\n    it \"interprets not for bool false\" do\n      interpret(\"!false\").should be_true\n    end\n\n    it \"interprets not for mixed union (nil)\" do\n      interpret(\"!(1 == 1 ? nil : 2)\").should be_true\n    end\n\n    it \"interprets not for mixed union (false)\" do\n      interpret(\"!(1 == 1 ? false : 2)\").should be_true\n    end\n\n    it \"interprets not for mixed union (true)\" do\n      interpret(\"!(1 == 1 ? true : 2)\").should be_false\n    end\n\n    it \"interprets not for mixed union (other)\" do\n      interpret(\"!(1 == 1 ? 2 : true)\").should be_false\n    end\n\n    it \"interprets not for nilable type (false)\" do\n      interpret(%(!(1 == 1 ? \"hello\" : nil))).should be_false\n    end\n\n    it \"interprets not for nilable type (true)\" do\n      interpret(%(!(1 == 1 ? nil : \"hello\"))).should be_true\n    end\n\n    it \"interprets not for nilable proc type (true)\" do\n      interpret(<<-CRYSTAL).should be_true\n        a =\n          if 1 == 1\n            nil\n          else\n            ->{ 1 }\n          end\n        !a\n        CRYSTAL\n    end\n\n    it \"interprets not for nilable proc type (false)\" do\n      interpret(<<-CRYSTAL).should be_false\n        a =\n          if 1 == 1\n            ->{ 1 }\n          else\n            nil\n          end\n        !a\n        CRYSTAL\n    end\n\n    it \"interprets not for generic class instance type\" do\n      interpret(<<-CRYSTAL).should be_false\n        class Foo(T)\n        end\n\n        foo = Foo(Int32).new\n        !foo\n        CRYSTAL\n    end\n\n    it \"interprets not for nilable type (false)\" do\n      interpret(<<-CRYSTAL).should be_false\n        class Foo\n        end\n\n        a =\n          if 1 == 1\n            \"a\"\n          elsif 1 == 1\n            Foo.new\n          else\n            nil\n          end\n        !a\n        CRYSTAL\n    end\n\n    it \"interprets not for nilable type (true)\" do\n      interpret(<<-CRYSTAL).should be_true\n        class Foo\n        end\n\n        a =\n          if 1 == 1\n            nil\n          elsif 1 == 1\n            Foo.new\n          else\n            \"a\"\n          end\n        !a\n        CRYSTAL\n    end\n\n    it \"interprets not for module (#12918)\" do\n      interpret(<<-CRYSTAL).should be_false\n        module MyModule; end\n\n        class One\n          include MyModule\n        end\n\n        !One.new.as(MyModule)\n        CRYSTAL\n    end\n\n    it \"interprets not for generic module\" do\n      interpret(<<-CRYSTAL).should be_false\n        module MyModule(T); end\n\n        class One\n          include MyModule(Int32)\n        end\n\n        !One.new.as(MyModule(Int32))\n        CRYSTAL\n    end\n\n    it \"interprets not for generic module metaclass\" do\n      interpret(<<-CRYSTAL).should be_false\n        module MyModule(T); end\n\n        !MyModule(Int32)\n        CRYSTAL\n    end\n\n    it \"interprets not for generic class instance metaclass\" do\n      interpret(<<-CRYSTAL).should be_false\n        class MyClass(T); end\n\n        !MyClass(Int32)\n        CRYSTAL\n    end\n\n    it \"does math primitive on union\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        module Test; end\n\n        a = 1\n        a.as(Int32 | Test) &+ 2\n        CRYSTAL\n    end\n\n    it \"does math convert on union\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        module Test; end\n\n        a = 1\n        a.as(Int32 | Test).to_i64!\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/procs_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"procs\" do\n    it \"interprets no args proc literal\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        proc = ->{ 40 }\n        proc.call + 2\n      CRYSTAL\n    end\n\n    it \"interprets proc literal with args\" do\n      interpret(<<-CRYSTAL).should eq(30)\n        proc = ->(x : Int32, y : Int32) { x + y }\n        proc.call(10, 20)\n      CRYSTAL\n    end\n\n    it \"interprets call inside Proc type\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Proc\n          def call2\n            call\n          end\n        end\n\n        proc = ->{ 40 }\n        proc.call2 + 2\n      CRYSTAL\n    end\n\n    it \"casts from nilable proc type to proc type\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        proc =\n          if 1 == 1\n            ->{ 42 }\n          else\n            nil\n          end\n\n        if proc\n          proc.call\n        else\n          1\n        end\n      CRYSTAL\n    end\n\n    it \"discards proc call\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        proc = ->{ 40 }\n        proc.call\n        2\n      CRYSTAL\n    end\n\n    it \"can downcast Proc(T) to Proc(Nil)\" do\n      interpret(<<-CRYSTAL)\n        class Foo\n          def initialize(@proc : ->)\n          end\n\n          def call\n            @proc.call\n          end\n        end\n\n        Foo.new(->{ 1 }).call\n        CRYSTAL\n    end\n  end\n\n  it \"casts proc call arguments to proc arg types (#12350)\" do\n    interpret(<<-CRYSTAL).should eq(42)\n      abstract struct Base\n      end\n\n      struct Foo < Base\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      struct Bar < Base\n      end\n\n      proc = ->(base : Base) {\n        if base.is_a?(Foo)\n          base.x\n        else\n          0\n        end\n      }\n\n      bar = Foo.new(42)\n      proc.call(bar)\n    CRYSTAL\n  end\n\n  it \"does call without receiver inside closure\" do\n    interpret(<<-CRYSTAL).should eq(42)\n      struct Proc\n        def foo\n          ->{\n            call\n          }\n        end\n      end\n\n      ->{ 42 }.foo.call\n    CRYSTAL\n  end\n\n  it \"calls proc primitive on union of module that has no subtypes (#12954)\" do\n    interpret(<<-CRYSTAL).should eq(42)\n      module Test\n      end\n\n      proc = ->{ 42 }\n      proc.as(Proc(Int32) | Test).call\n    CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/responds_to_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"responds_to?\" do\n    it \"does responds_to?\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Foo\n          def initialize\n            @x = 1\n          end\n\n          def foo\n            @x\n          end\n        end\n\n        class Bar\n          def initialize\n            @x = 1\n            @y = 2\n          end\n\n          def bar\n            @y\n          end\n        end\n\n        a = 0\n        foo = Foo.new || Bar.new\n        if foo.responds_to?(:foo)\n          a += foo.foo\n        end\n\n        bar = Bar.new || Foo.new\n        if bar.responds_to?(:bar)\n          a += bar.bar\n        end\n\n        a\n        CRYSTAL\n    end\n\n    it \"doesn't crash if def body ends up with no type (#12219)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"1\")\n        class Base\n          def foo\n            raise \"OH NO\"\n          end\n        end\n\n        module Moo\n          def foo\n            if self.responds_to?(:bar)\n              self.bar\n            else\n              super &- 0_i64\n            end\n          end\n        end\n\n        class Child < Base\n          include Moo\n        end\n\n        begin\n          Child.new.foo\n          0\n        rescue\n          1\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/sizeof_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"sizeof\" do\n    it \"interprets sizeof typeof\" do\n      interpret(\"sizeof(typeof(1))\").should eq(4)\n    end\n  end\n\n  context \"instance_sizeof\" do\n    it \"interprets instance_sizeof typeof\" do\n      interpret(<<-CRYSTAL).should eq(16)\n        class Foo\n          @x = 0_i64\n        end\n\n        instance_sizeof(typeof(Foo.new))\n        CRYSTAL\n    end\n  end\n\n  context \"alignof\" do\n    it \"interprets alignof typeof\" do\n      interpret(\"alignof(typeof(1))\").should eq(4)\n    end\n  end\n\n  context \"instance_alignof\" do\n    it \"interprets instance_alignof typeof\" do\n      interpret(<<-CRYSTAL).should eq(8)\n        class Foo\n          @x = 0_i64\n        end\n\n        instance_alignof(typeof(Foo.new))\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/spec_helper.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"../spec_helper\"\nrequire \"compiler/crystal/interpreter/*\"\n\ndef interpret(code, *, prelude = \"primitives\", file = __FILE__, line = __LINE__)\n  if prelude == \"primitives\"\n    context, value = interpret_with_context(code)\n    context.loader?.try &.close_all\n    value.value\n  else\n    interpret_in_separate_process(code, prelude, file: file, line: line)\n  end\nend\n\ndef interpret_with_context(code)\n  repl = Crystal::Repl.new\n  repl.prelude = \"primitives\"\n\n  value = repl.run_code(code)\n  {repl.context, value}\nend\n\n# FIXME: The following is a dirty hack to work around GC issues in interpreted programs. https://github.com/crystal-lang/crystal/issues/11602\n# In a nutshell, `interpret_in_separate_process` below calls this same process with an extra option that causes\n# the interpretation of the code from stdin, reading the output from stdout. That string is used as the result of\n# the program being tested.\nclass Spec::CLI\n  def option_parser\n    option_parser = previous_def\n    option_parser.on(\"\", \"--interpret-code PRELUDE\", \"Execute interpreted code\") do |prelude|\n      code = STDIN.gets_to_end\n\n      repl = Crystal::Repl.new\n      repl.prelude = prelude\n\n      print repl.run_code(code)\n      exit\n    end\n    option_parser\n  end\nend\n\ndef interpret_in_separate_process(code, prelude, file = __FILE__, line = __LINE__)\n  input = IO::Memory.new(code)\n  output = IO::Memory.new\n  error = IO::Memory.new\n  executable = Process.executable_path || fail \"Can't find executable path of current process\"\n  process = Process.new(executable, [\"--interpret-code\", prelude], input: input, output: output, error: error)\n\n  status = process.wait\n  unless status.success?\n    fail error.rewind.gets_to_end + output.rewind.gets_to_end, file: file, line: line\n  end\n\n  output.rewind.gets_to_end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/special_vars_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"special vars\" do\n    it \"does special var that's a reference\" do\n      interpret(<<-CRYSTAL).should eq(\"hey\")\n        class Object; def not_nil!; self; end; end\n\n        def foo(x)\n          $? = \"hey\"\n        end\n\n        foo(2)\n        $? || \"oops\"\n      CRYSTAL\n    end\n\n    it \"does special var that's a struct\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        class Object; def not_nil!; self; end; end\n\n        def foo(x)\n          $? = 3\n        end\n\n        foo(2)\n        $? || 4\n      CRYSTAL\n    end\n\n    it \"does special var that's a reference inside block\" do\n      interpret(<<-CRYSTAL).should eq(\"hey\")\n        class Object; def not_nil!; self; end; end\n\n        def bar\n          yield\n        end\n\n        def foo(x)\n          bar do\n            $? = \"hey\"\n          end\n        end\n\n        foo(2)\n        $? || \"oops\"\n      CRYSTAL\n    end\n\n    it \"does special var that's a reference when there are optional arguments\" do\n      interpret(<<-CRYSTAL).should eq(\"hey\")\n        class Object; def not_nil!; self; end; end\n\n        def foo(x = 1)\n          $? = \"hey\"\n        end\n\n        foo\n        $? || \"oops\"\n      CRYSTAL\n    end\n\n    it \"does special var that's a reference for multidispatch\" do\n      interpret(<<-CRYSTAL).should eq(\"hey\")\n        class Object; def not_nil!; self; end; end\n\n        def foo(x : Int32)\n          $? = \"hey\"\n        end\n\n        def foo(x : String)\n          $? = \"ho\"\n        end\n\n        a = 1 || \"a\"\n        foo(a)\n        $? || \"oops\"\n      CRYSTAL\n    end\n\n    it \"sets special var inside call inside block (#12250)\" do\n      interpret(<<-CRYSTAL).should eq(\"hey\")\n        class Object; def not_nil!; self; end; end\n\n        def foo\n          $? = \"hey\"\n        end\n\n        def bar\n          yield\n        end\n\n        bar { foo }\n        $? || \"oops\"\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/structs_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"structs\" do\n    it \"does allocate, set instance var and get instance var\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          @x = 0_i64\n          @y = 0_i64\n\n          def x=(@x)\n          end\n\n          def x\n            @x\n          end\n\n          def y=(@y)\n          end\n\n          def y\n            @y\n          end\n        end\n\n        foo = Foo.allocate\n        foo.x = 22_i64\n        foo.y = 20_i64\n        foo.x + foo.y\n      CRYSTAL\n    end\n\n    it \"does constructor\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.new(42)\n        foo.x\n      CRYSTAL\n    end\n\n    it \"interprets read instance var of struct\" do\n      interpret(<<-CRYSTAL).should eq(20)\n        struct Foo\n          @x = 0_i64\n          @y = 0_i64\n\n          def y=(@y)\n          end\n\n          def y\n            @y\n          end\n        end\n\n        foo = Foo.allocate\n        foo.y = 20_i64\n        foo.@y\n      CRYSTAL\n    end\n\n    it \"casts def body to def type\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        struct Foo\n          def foo\n            return nil if 1 == 2\n\n            self\n          end\n        end\n\n        value = Foo.new.foo\n        value ? 1 : 2\n      CRYSTAL\n    end\n\n    it \"discards allocate\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        struct Foo\n        end\n\n        Foo.allocate\n        3\n      CRYSTAL\n    end\n\n    it \"mutates struct inside union\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Foo\n          def initialize\n            @x = 1\n          end\n\n          def inc\n            @x += 1\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = 1 == 1 ? Foo.new : nil\n        if foo\n          foo.inc\n        end\n\n        if foo\n          foo.x\n        else\n          0\n        end\n      CRYSTAL\n    end\n\n    it \"mutates struct stored in class var\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        struct Foo\n          def initialize\n            @x = 1\n          end\n\n          def inc\n            @x += 1\n          end\n\n          def x\n            @x\n          end\n        end\n\n        module Moo\n          @@foo = Foo.new\n\n          def self.mutate\n            @@foo.inc\n          end\n\n          def self.foo\n            @@foo\n          end\n        end\n\n        before = Moo.foo.x\n        Moo.mutate\n        after = Moo.foo.x\n        before + after\n      CRYSTAL\n    end\n\n    it \"does simple class instance var initializer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Foo\n          @x = 42\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.allocate\n        foo.x\n      CRYSTAL\n    end\n\n    it \"does complex class instance var initializer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class Foo\n          @x : Int32 = begin\n            a = 20\n            b = 22\n            a + b\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.allocate\n        foo.x\n      CRYSTAL\n    end\n\n    it \"does class instance var initializer inheritance\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        module Moo\n          @z = 3\n\n          def z\n            @z\n          end\n        end\n\n        class Foo\n          include Moo\n\n          @x = 1\n\n          def x\n            @x\n          end\n        end\n\n        class Bar < Foo\n          @y = 2\n\n          def y\n            @y\n          end\n        end\n\n        bar = Bar.allocate\n        bar.x + bar.y + bar.z\n      CRYSTAL\n    end\n\n    it \"does simple struct instance var initializer\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          @x = 42\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.allocate\n        foo.x\n      CRYSTAL\n    end\n\n    it \"does call receiver by value from VirtualType abstract struct to concrete struct (#12190)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        abstract struct Base\n        end\n\n        struct A < Base\n          def initialize(@x : Int32)\n          end\n\n          def foo\n            @x\n          end\n        end\n\n        struct B < Base\n        end\n\n        v = A.new(42) || B.new\n\n        if v.is_a?(A)\n          v.foo\n        else\n          1\n        end\n      CRYSTAL\n    end\n\n    it \"does call receiver by value from VirtualType abstract struct to union\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        abstract struct Base\n        end\n\n        struct A < Base\n          def initialize(@x : Int32)\n          end\n\n          def foo\n            @x\n          end\n        end\n\n        struct B < Base\n          def initialize(@x : Int32)\n          end\n\n          def foo\n            @x\n          end\n        end\n\n        struct C < Base\n        end\n\n        v = A.new(42) || B.new(3)\n\n        if v.is_a?(A | B)\n          v.foo\n        else\n          1\n        end\n      CRYSTAL\n    end\n\n    it \"sets multiple instance vars in virtual abstract struct call (#12187)\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        abstract struct Foo\n          @x = 0\n          @y = 0\n          @z = 0\n\n          def set\n            @x = 1\n            @y = 2\n            @z = 3\n          end\n\n          def x\n            @x\n          end\n\n          def y\n            @y\n          end\n\n          def z\n            @z\n          end\n        end\n\n        struct Bar < Foo\n        end\n\n        struct Baz < Foo\n        end\n\n        f = Bar.new || Baz.new\n        f.set\n        f.x + f.y + f.z\n      CRYSTAL\n    end\n\n    it \"inlines struct method that returns self (#12253)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        struct Foo\n          def initialize(@x : Int32)\n          end\n\n          def x\n            @x\n          end\n\n          def foo\n            me\n          end\n\n          def me\n            self\n          end\n        end\n\n        a = Foo.new(42)\n        b = a.foo\n        b.x\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/symbol_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"symbol\" do\n    it \"Symbol#to_s\" do\n      interpret(<<-CRYSTAL).should eq(\"hello\")\n        x = :hello\n        x.to_s\n      CRYSTAL\n    end\n\n    it \"Symbol#to_i\" do\n      interpret(<<-CRYSTAL).should eq(0 + 1 + 2)\n        x = :hello\n        y = :bye\n        z = :foo\n        x.to_i + y.to_i + z.to_i\n      CRYSTAL\n    end\n\n    it \"symbol equality\" do\n      interpret(<<-CRYSTAL).should eq(9)\n        s1 = :foo\n        s2 = :bar\n\n        a = 0\n        a += 1 if s1 == s1\n        a += 2 if s1 == s2\n        a += 4 if s1 != s1\n        a += 8 if s1 != s2\n        a\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/tuple_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"tuple\" do\n    it \"interprets tuple literal and access by known index\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        a = {1, 2, 3}\n        a[0] + a[1] + a[2]\n      CRYSTAL\n    end\n\n    it \"interprets tuple range indexer\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        #{range_new}\n\n        a = {1, 2, 4, 8, 16}\n        b = a[1...-2]\n        b[0] + b[1]\n      CRYSTAL\n    end\n\n    it \"interprets tuple range indexer (2)\" do\n      interpret(<<-CRYSTAL).should eq(24)\n        #{range_new}\n\n        a = {1_i8, 2_i8, 4_i8, 8_i8, 16_i32}\n        b = a[3..]\n        b[1] + b[0]\n      CRYSTAL\n    end\n\n    it \"interprets tuple literal of different types (1)\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = {1, true}\n        a[0] + (a[1] ? 2 : 3)\n      CRYSTAL\n    end\n\n    it \"interprets tuple literal of different types (2)\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = {true, 1}\n        a[1] + (a[0] ? 2 : 3)\n      CRYSTAL\n    end\n\n    it \"discards tuple access\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        foo = {1, 2}\n        a = foo[0]\n        foo[1]\n        a\n      CRYSTAL\n    end\n\n    it \"interprets tuple self\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        struct Tuple\n          def itself\n            self\n          end\n        end\n\n        a = {1, 2, 3}\n        b = a.itself\n        b[0] + b[1] + b[2]\n      CRYSTAL\n    end\n\n    it \"extends sign when doing to_i32\" do\n      interpret(<<-CRYSTAL).should eq(-50)\n        t = {-50_i16}\n        exp = t[0]\n        z = exp.to_i32\n        CRYSTAL\n    end\n\n    it \"unpacks tuple in block arguments\" do\n      interpret(<<-CRYSTAL).should eq(6)\n        def foo\n          t = {1, 2, 3}\n          yield t\n        end\n\n        foo do |x, y, z|\n          x + y + z\n        end\n        CRYSTAL\n    end\n\n    it \"interprets tuple metaclass indexer\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        struct Int32\n          def self.foo\n            2\n          end\n        end\n\n        a = {1, 'a'}\n        a.class[0].foo\n      CRYSTAL\n    end\n\n    it \"interprets tuple metaclass range indexer\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        #{range_new}\n\n        struct Int32\n          def self.foo\n            1\n          end\n        end\n\n        class String\n          def self.bar\n            2\n          end\n        end\n\n        a = {true, 1, \"a\", 'a', 1.0}\n        b = a.class[1...-2]\n        b[0].foo + b[1].bar\n      CRYSTAL\n    end\n\n    it \"discards tuple (#12383)\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        1 + ({1, 2, 3, 4}; 2)\n      CRYSTAL\n    end\n\n    it \"does tuple indexer on union\" do\n      interpret(<<-CRYSTAL).should eq(1)\n        module Test; end\n\n        a = {1}\n        a.as(Tuple(Int32) | Test)[0]\n        CRYSTAL\n    end\n  end\nend\n\nprivate def range_new\n  %(\n    struct Range(B, E)\n      def initialize(@begin : B, @end : E, @exclusive : Bool = false)\n      end\n    end\n  )\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/typeof_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"typeof\" do\n    it \"interprets typeof instance type\" do\n      context, repl_value = interpret_with_context(\"typeof(1)\")\n      repl_value.value.should eq(context.program.int32.metaclass)\n    end\n\n    it \"interprets typeof metaclass type\" do\n      context, repl_value = interpret_with_context(\"typeof(Int32)\")\n      repl_value.value.should eq(context.program.class_type)\n    end\n\n    it \"interprets typeof virtual type\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"Foo\"))\n        abstract class Foo\n        end\n\n        class Bar < Foo\n        end\n\n        class Baz < Foo\n        end\n\n        foo = Baz.new.as(Foo)\n        typeof(foo).to_s\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/types_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"types\" do\n    it \"interprets path to type\" do\n      context, repl_value = interpret_with_context(\"String\")\n      repl_value.value.should eq(context.program.string.metaclass)\n    end\n\n    it \"interprets class for non-union type\" do\n      context, repl_value = interpret_with_context(\"1.class\")\n      repl_value.value.should eq(context.program.int32)\n    end\n\n    it \"discards class for non-union type\" do\n      interpret(\"1.class; 2\").should eq(2)\n    end\n\n    it \"interprets class for virtual_type type\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"Bar\"))\n          class Foo; end\n          class Bar < Foo; end\n\n          bar = Bar.new || Foo.new\n          bar.class.to_s\n        CRYSTAL\n    end\n\n    it \"interprets class for virtual_type type (struct)\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(%(\"Baz\"))\n          abstract struct Foo; end\n          struct Bar < Foo; end\n          struct Baz < Foo; end\n\n          baz = Baz.new || Bar.new\n          baz.class.to_s\n        CRYSTAL\n    end\n\n    it \"does class method on virtual metaclass casted to generic metaclass (#12302)\" do\n      interpret(<<-CRYSTAL).should eq(42)\n        class A\n          def self.foo\n            1\n          end\n        end\n\n        class B(T) < A\n          def self.foo\n            42\n          end\n        end\n\n        b = B(String).new.as(A)\n        b.class.foo\n      CRYSTAL\n    end\n\n    it \"discards class for virtual_type type\" do\n      interpret(<<-CRYSTAL).should eq(2)\n          class Foo; end\n          class Bar < Foo; end\n\n          bar = Bar.new || Foo.new\n          bar.class\n          2\n        CRYSTAL\n    end\n\n    it \"interprets class for module type (#12203)\" do\n      interpret(<<-CRYSTAL).should eq(\"A\")\n        class Class\n          def name : String\n            {{ @type.name.stringify }}\n          end\n        end\n\n        module M\n        end\n\n        class E\n          def initialize(@base : M)\n          end\n        end\n\n        abstract class P\n          include M\n        end\n\n        class A < P\n        end\n\n        e = E.new(A.new)\n        base = e.@base\n        base.class.name\n        CRYSTAL\n    end\n\n    it \"interprets crystal_type_id for nil\" do\n      interpret(\"nil.crystal_type_id\").should eq(0)\n    end\n\n    it \"interprets crystal_type_id for non-nil\" do\n      context, repl_value = interpret_with_context(\"1.crystal_type_id\")\n      repl_value.value.should eq(context.type_id(context.program.int32))\n    end\n\n    it \"interprets crystal_type_id for virtual metaclass type (#12228)\" do\n      interpret(<<-CRYSTAL).should be_true\n        class P\n        end\n\n        class A < P\n        end\n\n        p = A.as(P.class)\n        p.crystal_type_id == A.crystal_type_id\n        CRYSTAL\n    end\n\n    it \"interprets class_crystal_instance_type_id\" do\n      interpret(<<-CRYSTAL, prelude: \"prelude\").should eq(\"true\")\n        class Foo\n        end\n\n        Foo.new.crystal_type_id == Foo.crystal_instance_type_id\n        CRYSTAL\n    end\n\n    it \"discards Path\" do\n      interpret(\"String; 1\").should eq(1)\n    end\n\n    it \"discards typeof\" do\n      interpret(\"typeof(1); 1\").should eq(1)\n    end\n\n    it \"discards generic\" do\n      interpret(\"Pointer(Int32); 1\").should eq(1)\n    end\n\n    it \"discards .class\" do\n      interpret(\"1.class; 1\").should eq(1)\n    end\n\n    it \"discards crystal_type_id\" do\n      interpret(\"nil.crystal_type_id; 1\").should eq(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter/unions_spec.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./spec_helper\"\n\ndescribe Crystal::Repl::Interpreter do\n  context \"unions\" do\n    it \"put and remove from union, together with is_a? (truthy case)\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        a = 1 == 1 ? 2 : true\n        a.is_a?(Int32) ? a : 4\n        CRYSTAL\n    end\n\n    it \"put and remove from union, together with is_a? (falsey case)\" do\n      interpret(<<-CRYSTAL).should be_true\n        a = 1 == 2 ? 2 : true\n        a.is_a?(Int32) ? true : a\n        CRYSTAL\n    end\n\n    it \"returns union type\" do\n      interpret(<<-CRYSTAL).should eq('a')\n        def foo\n          if 1 == 1\n            return 'a'\n          end\n\n          3\n        end\n\n        x = foo\n        if x.is_a?(Char)\n          x\n        else\n          'b'\n        end\n        CRYSTAL\n    end\n\n    it \"returns large union type (#15041)\" do\n      interpret(<<-CRYSTAL).should eq(4_i64)\n        a = {3_i64, 4_i64} || nil\n        a.is_a?(Tuple) ? a[1] : 5_i64\n        CRYSTAL\n    end\n\n    it \"put and remove from union in local var\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = 1 == 1 ? 2 : true\n        a = 3\n        a.is_a?(Int32) ? a : 4\n        CRYSTAL\n    end\n\n    it \"put and remove from union in instance var\" do\n      interpret(<<-CRYSTAL).should eq(2)\n        class Foo\n          @x : Int32 | Char\n\n          def initialize\n            if 1 == 1\n              @x = 2\n            else\n              @x = 'a'\n            end\n          end\n\n          def x\n            @x\n          end\n        end\n\n        foo = Foo.new\n        z = foo.x\n        if z.is_a?(Int32)\n          z\n        else\n          10\n        end\n      CRYSTAL\n    end\n\n    it \"discards is_a?\" do\n      interpret(<<-CRYSTAL).should eq(3)\n        a = 1 == 1 ? 2 : true\n        a.is_a?(Int32)\n        3\n        CRYSTAL\n    end\n\n    it \"converts from NilableType to NonGenericClassType\" do\n      interpret(<<-CRYSTAL).should eq(\"a\")\n        a = 1 == 1 ? \"a\" : nil\n        a || \"b\"\n        CRYSTAL\n    end\n\n    it \"puts union inside union\" do\n      interpret(<<-CRYSTAL).should eq('a'.ord)\n        a = 'a' || 1 || true\n        case a\n        in Char\n          a.ord\n        in Int32\n          a\n        in Bool\n          20\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/interpreter_spec.cr",
    "content": "require \"./interpreter/*\"\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_comment_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def t(kind : Crystal::Token::Kind)\n  kind\nend\n\ndescribe \"Lexer comments\" do\n  it \"lexes without comments enabled\" do\n    lexer = Lexer.new(%(# Hello\\n1))\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n  end\n\n  it \"lexes with comments enabled\" do\n    lexer = Lexer.new(%(# Hello\\n1))\n    lexer.comments_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :COMMENT)\n    token.value.should eq(\"# Hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n  end\n\n  it \"lexes with comments enabled (2)\" do\n    lexer = Lexer.new(%(1 # Hello))\n    lexer.comments_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n\n    token = lexer.next_token\n    token.type.should eq(t :COMMENT)\n    token.value.should eq(\"# Hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :EOF)\n  end\n\n  it \"lexes correct number of spaces\" do\n    lexer = Lexer.new(%(1   2))\n    lexer.count_whitespace = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.value.should eq(\"   \")\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n\n    token = lexer.next_token\n    token.type.should eq(t :EOF)\n  end\nend\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_doc_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def t(kind : Crystal::Token::Kind)\n  kind\nend\n\ndescribe \"Lexer doc\" do\n  it \"lexes without doc enabled\" do\n    lexer = Lexer.new(%(1))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should be_nil\n  end\n\n  it \"lexes with doc enabled but without docs\" do\n    lexer = Lexer.new(%(1))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should be_nil\n  end\n\n  it \"lexes with doc enabled and docs\" do\n    lexer = Lexer.new(%(# hello\\n1))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should eq(\"hello\")\n  end\n\n  it \"lexes with doc enabled and docs, two line comment\" do\n    lexer = Lexer.new(%(# hello\\n# world\\n1))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"hello\\nworld\")\n  end\n\n  it \"lexes with doc enabled and docs, two line comment with leading whitespace\" do\n    lexer = Lexer.new(%(# hello\\n    # world\\n1))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.doc.should eq(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"hello\\nworld\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should eq(\"hello\\nworld\")\n  end\n\n  it \"lexes with doc enabled and docs, one line comment with two newlines and another comment\" do\n    lexer = Lexer.new(%(# hello\\n\\n    # world\\n1))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should be_nil\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.doc.should be_nil\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"world\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should eq(\"world\")\n  end\n\n  it \"resets doc after non newline or space token\" do\n    lexer = Lexer.new(%(# hello\\n1 2))\n    lexer.doc_enabled = true\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.doc.should eq(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should eq(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.doc.should be_nil\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.doc.should be_nil\n  end\nend\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_macro_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def t(kind : Crystal::Token::Kind)\n  kind\nend\n\ndescribe \"Lexer macro\" do\n  it \"lexes simple macro\" do\n    lexer = Lexer.new(%(hello end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"hello \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with expression\" do\n    lexer = Lexer.new(%(hello {{world}} end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"hello \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token_before_expression = token.dup\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"world\")\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(token_before_expression.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  [\"begin\", \"do\", \"if\", \"unless\", \"class\", \"struct\", \"module\", \"def\", \"while\", \"until\", \"case\", \"macro\", \"fun\", \"lib\", \"union\", \"annotation\", \"select\"].each do |keyword|\n    it \"lexes macro with nested #{keyword}\" do\n      lexer = Lexer.new(%(hello\\n  #{keyword} {{world}} end end))\n\n      token = lexer.next_macro_token(Token::MacroState.default, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"hello\\n  #{keyword} \")\n      token.macro_state.nest.should eq(1)\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_EXPRESSION_START)\n\n      token_before_expression = token.dup\n\n      token = lexer.next_token\n      token.type.should eq(t :IDENT)\n      token.value.should eq(\"world\")\n\n      lexer.next_token.type.should eq(t :OP_RCURLY)\n      lexer.next_token.type.should eq(t :OP_RCURLY)\n\n      token = lexer.next_macro_token(token_before_expression.macro_state, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\" \")\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"end \")\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_END)\n    end\n  end\n\n  it \"lexes macro with nested enum\" do\n    lexer = Lexer.new(%(hello enum {{world}} end end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"hello \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"enum \")\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token_before_expression = token.dup\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"world\")\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(token_before_expression.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"end \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro without nested if\" do\n    lexer = Lexer.new(%(helloif {{world}} end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"helloif \")\n    token.macro_state.nest.should eq(0)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token_before_expression = token.dup\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"world\")\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(token_before_expression.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with nested abstract def\" do\n    lexer = Lexer.new(%(hello\\n  abstract def {{world}} end end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"hello\\n  abstract def \")\n    token.macro_state.nest.should eq(0)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token_before_expression = token.dup\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"world\")\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(token_before_expression.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  {\"class\", \"struct\"}.each do |keyword|\n    it \"lexes macro with nested abstract #{keyword}\" do\n      lexer = Lexer.new(%(hello\\n  abstract #{keyword} Foo; end; end))\n\n      token = lexer.next_macro_token(Token::MacroState.default, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"hello\\n  abstract #{keyword} Foo; \")\n      token.macro_state.nest.should eq(1)\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"end; \")\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_END)\n    end\n  end\n\n  it \"reaches end\" do\n    lexer = Lexer.new(%(fail))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"fail\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :EOF)\n  end\n\n  it \"keeps correct column and line numbers\" do\n    lexer = Lexer.new(\"\\nfoo\\nbarf{{var}}\\nend\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"\\nfoo\\nbarf\")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"var\")\n    token.line_number.should eq(3)\n    token.column_number.should eq(7)\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"\\n\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with control\" do\n    lexer = Lexer.new(\"foo{% if \")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"foo\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_CONTROL_START)\n  end\n\n  it \"skips whitespace\" do\n    lexer = Lexer.new(\"   \\n    coco\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, true)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"coco\")\n  end\n\n  it \"lexes macro with embedded string\" do\n    lexer = Lexer.new(%(good \" end \" day end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(good \" end \" day ))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with embedded string and backslash\" do\n    lexer = Lexer.new(\"good \\\" end \\\\\\\" \\\" day end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"good \\\" end \\\\\\\" \\\" day \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with embedded string and expression\" do\n    lexer = Lexer.new(%(good \" end {{foo}} \" day end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(good \" end ))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    macro_state = token.macro_state\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"foo\")\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%( \" day ))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  [{\"(\", \")\"}, {\"[\", \"]\"}, {\"<\", \">\"}, {\"|\", \"|\"}].each do |(left, right)|\n    it \"lexes macro with embedded string with %#{left}\" do\n      lexer = Lexer.new(\"good %#{left} end #{right} day end\")\n\n      token = lexer.next_macro_token(Token::MacroState.default, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"good %#{left} end #{right} day \")\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_END)\n    end\n\n    it \"lexes macro with embedded string with %#{left} ignores begin\" do\n      lexer = Lexer.new(\"good %#{left} begin #{right} day end\")\n\n      token = lexer.next_macro_token(Token::MacroState.default, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"good %#{left} begin #{right} day \")\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_END)\n    end\n  end\n\n  it \"lexes macro with nested embedded string with %(\" do\n    lexer = Lexer.new(\"good %( ( ) end ) day end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"good %( ( ) end ) day \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  [\"Q\", \"q\", \"w\", \"i\", \"r\"].each do |prefix|\n    it \"lexes macro with %#{prefix}| literal\" do\n      lexer = Lexer.new(\"good %#{prefix}|begin| day end\")\n\n      token = lexer.next_macro_token(Token::MacroState.default, false)\n      token.type.should eq(t :MACRO_LITERAL)\n      token.value.should eq(\"good %#{prefix}|begin| day \")\n\n      token = lexer.next_macro_token(token.macro_state, false)\n      token.type.should eq(t :MACRO_END)\n    end\n  end\n\n  it \"lexes macro with comments\" do\n    lexer = Lexer.new(\"good # end\\n day end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"good \")\n    token.line_number.should eq(1)\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"# end\\n\")\n    token.line_number.should eq(2)\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" day \")\n    token.line_number.should eq(2)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n    token.line_number.should eq(2)\n  end\n\n  it \"lexes macro with comments and expressions\" do\n    lexer = Lexer.new(\"good # {{name}} end\\n day end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"good \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"# \")\n    token.macro_state.comment.should be_true\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token_before_expression = token.dup\n    token_before_expression.macro_state.comment.should be_true\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"name\")\n\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n    lexer.next_token.type.should eq(t :OP_RCURLY)\n\n    token = lexer.next_macro_token(token_before_expression.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" end\\n\")\n    token.macro_state.comment.should be_false\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" day \")\n    token.line_number.should eq(2)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n    token.line_number.should eq(2)\n  end\n\n  it \"lexes macro with curly escape\" do\n    lexer = Lexer.new(\"good \\\\{{world}}\\nend\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"good \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{world}}\\n\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with curly escape in comment\" do\n    lexer = Lexer.new(\"# good \\\\{{world}}\\nend\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"# good \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{world}}\\n\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with slash not followed by curly\" do\n    lexer = Lexer.new(\"# good \\\\a\\nend\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"# good \\\\a\\n\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with if as suffix\" do\n    lexer = Lexer.new(\"foo if bar end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"foo if bar \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with if as suffix after return\" do\n    lexer = Lexer.new(\"return if @end end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"return if @end \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with semicolon before end\" do\n    lexer = Lexer.new(\";end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\";\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with if after assign\" do\n    lexer = Lexer.new(\"x = if 1; 2; else; 3; end; end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"x = if 1; 2; \")\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"else; 3; \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"end; \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro var\" do\n    lexer = Lexer.new(\"x = if %var; 2; else; 3; end; end\")\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"x = if \")\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_VAR)\n    token.value.should eq(\"var\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"; 2; \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"else; 3; \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"end; \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"doesn't lex macro var if escaped\" do\n    lexer = Lexer.new(%(\" \\\\%var \" end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(\" ))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"%\")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(var \" ))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes macro with embedded char and sharp\" do\n    lexer = Lexer.new(%(good '#' day end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(good '#' day ))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_END)\n  end\n\n  it \"lexes bug #654\" do\n    lexer = Lexer.new(%(l {{op}} end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"l \")\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"op\")\n  end\n\n  it \"lexes escaped quote inside string (#895)\" do\n    lexer = Lexer.new(%(\"\\\\\"\" end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(\"\\\\\"\" ))\n  end\n\n  it \"lexes with if/end inside escaped macro (#1029)\" do\n    lexer = Lexer.new(%(\\\\{%    if true %} 2 \\\\{% end %} end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{%    if\")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, token.macro_state.beginning_of_line)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" true %} 2 \")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, token.macro_state.beginning_of_line)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{% end\")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(0)\n  end\n\n  it \"lexes with for inside escaped macro (#1029)\" do\n    lexer = Lexer.new(%(\\\\{%    for true %} 2 \\\\{% end %} end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{%    for\")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(1)\n  end\n\n  it \"lexes with unless inside escaped macro (#5664)\" do\n    lexer = Lexer.new(%(\\\\{%    unless true %} 2 \\\\{% end %} end))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{%    unless\")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, token.macro_state.beginning_of_line)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\" true %} 2 \")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(1)\n\n    token = lexer.next_macro_token(token.macro_state, token.macro_state.beginning_of_line)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"{% end\")\n    token.macro_state.beginning_of_line.should be_false\n    token.macro_state.nest.should eq(0)\n  end\n\n  it \"lexes begin end\" do\n    lexer = Lexer.new(%(begin\\nend end))\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"begin\\n\")\n\n    token = lexer.next_macro_token(token.macro_state, token.macro_state.beginning_of_line)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"end \")\n    token.line_number.should eq(2)\n  end\n\n  it \"lexes macro with string interpolation and double curly brace\" do\n    lexer = Lexer.new(%(\"\\#{{{1}}}\"))\n\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(%(\"\\#{))\n\n    token = lexer.next_macro_token(token.macro_state, false)\n    token.type.should eq(t :MACRO_EXPRESSION_START)\n  end\n\n  it \"keeps correct line number after lexes the part of keyword and newline (#4656)\" do\n    lexer = Lexer.new(%(ab\\ncd)) # 'ab' means the part of 'abstract'\n    token = lexer.next_macro_token(Token::MacroState.default, false)\n    token.type.should eq(t :MACRO_LITERAL)\n    token.value.should eq(\"ab\\ncd\")\n    lexer.line_number.should eq(2)\n  end\nend\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_objects/strings.cr",
    "content": "module LexerObjects\n  class Strings\n    @lexer : Lexer\n    @token : Token\n\n    private def t(kind : Token::Kind)\n      kind\n    end\n\n    def initialize(@lexer)\n      @token = Token.new\n    end\n\n    def string_should_be_delimited_by(expected_start, expected_end)\n      string_should_start_correctly\n      token.delimiter_state.nest.should eq(expected_start)\n      token.delimiter_state.end.should eq(expected_end)\n      token.delimiter_state.open_count.should eq(0)\n    end\n\n    def string_should_start_correctly\n      @token = lexer.next_token\n      token.type.should eq(t :DELIMITER_START)\n    end\n\n    def next_token_should_be(expected_type : Token::Kind, expected_value = nil)\n      @token = lexer.next_token\n      token.type.should eq(expected_type)\n      if expected_value\n        token.value.should eq(expected_value)\n      end\n    end\n\n    def next_unicode_tokens_should_be(expected_unicode_codes : Array)\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :STRING)\n      token.value.as(String).chars.map(&.ord).should eq(expected_unicode_codes)\n    end\n\n    def next_unicode_tokens_should_be(expected_unicode_codes)\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :STRING)\n      token.value.as(String).char_at(0).ord.should eq(expected_unicode_codes)\n    end\n\n    def next_string_token_should_be(expected_string)\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :STRING)\n      token.value.should eq(expected_string)\n    end\n\n    def next_string_token_should_be_opening\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :STRING)\n      token.value.should eq(token.delimiter_state.nest.to_s)\n      token.delimiter_state.open_count.should eq(1)\n    end\n\n    def next_string_token_should_be_closing\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :STRING)\n      token.value.should eq(token.delimiter_state.end.to_s)\n      token.delimiter_state.open_count.should eq(0)\n    end\n\n    def string_should_have_an_interpolation_of(interpolated_variable_name)\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :INTERPOLATION_START)\n\n      @token = lexer.next_token\n      token.type.should eq(t :IDENT)\n      token.value.should eq(interpolated_variable_name)\n\n      @token = lexer.next_token\n      token.type.should eq(t :OP_RCURLY)\n    end\n\n    def token_should_be_at(line = nil, column = nil)\n      token.line_number.should eq(line) if line\n      token.column_number.should eq(column) if column\n    end\n\n    def next_token_should_be_at(line = nil, column = nil)\n      @token = lexer.next_token\n      token_should_be_at(line: line, column: column)\n    end\n\n    def string_should_end_correctly(eof = true)\n      @token = lexer.next_string_token(token.delimiter_state)\n      token.type.should eq(t :DELIMITER_END)\n      if eof\n        should_have_reached_eof\n      end\n    end\n\n    def should_have_reached_eof\n      @token = lexer.next_token\n      token.type.should eq(t :EOF)\n    end\n\n    private getter :lexer, :token\n  end\nend\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def t(kind : Crystal::Token::Kind)\n  kind\nend\n\nprivate def it_lexes(string, type : Token::Kind, *, slash_is_regex : Bool? = nil)\n  it \"lexes #{string.inspect}\" do\n    lexer = Lexer.new string\n    unless (v = slash_is_regex).nil?\n      lexer.slash_is_regex = v\n    end\n    token = lexer.next_token\n    token.type.should eq(type)\n  end\nend\n\nprivate def it_lexes(string, type : Token::Kind, value)\n  it \"lexes #{string.inspect}\" do\n    lexer = Lexer.new string\n    token = lexer.next_token\n    token.type.should eq(type)\n    token.value.should eq(value)\n  end\nend\n\nprivate def it_lexes(string, type : Token::Kind, value, number_kind : NumberKind)\n  it \"lexes #{string.inspect}\" do\n    lexer = Lexer.new string\n    token = lexer.next_token\n    token.type.should eq(type)\n    token.value.should eq(value)\n    token.number_kind.should eq(number_kind)\n  end\nend\n\nprivate def it_lexes_many(values, type : Token::Kind)\n  values.each do |value|\n    it_lexes value, type, value\n  end\nend\n\nprivate def it_lexes_keywords(*keywords : Keyword)\n  keywords.each do |keyword|\n    it_lexes keyword.to_s, :IDENT, keyword\n  end\nend\n\nprivate def it_lexes_idents(idents)\n  idents.each do |ident|\n    it_lexes ident, :IDENT, ident\n  end\nend\n\nprivate def it_lexes_i32(values)\n  values.each { |value| it_lexes_number :i32, value }\nend\n\nprivate def it_lexes_i64(values)\n  values.each { |value| it_lexes_number :i64, value }\nend\n\nprivate def it_lexes_i128(values)\n  values.each { |value| it_lexes_number :i128, value }\nend\n\nprivate def it_lexes_u64(values)\n  values.each { |value| it_lexes_number :u64, value }\nend\n\nprivate def it_lexes_f32(values)\n  values.each { |value| it_lexes_number :f32, value }\nend\n\nprivate def it_lexes_f64(values)\n  values.each { |value| it_lexes_number :f64, value }\nend\n\nprivate def it_lexes_number(number_kind : NumberKind, value : Array)\n  it_lexes value[0], :NUMBER, value[1], number_kind\nend\n\nprivate def it_lexes_number(number_kind : NumberKind, value : String)\n  it_lexes value, :NUMBER, value, number_kind\nend\n\nprivate def it_lexes_char(string, value)\n  it \"lexes #{string}\" do\n    lexer = Lexer.new string\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).should eq(value)\n  end\nend\n\nprivate def it_lexes_string(string, value)\n  it \"lexes #{string}\" do\n    lexer = Lexer.new string\n    token = lexer.next_token\n    token.type.should eq(t :DELIMITER_START)\n\n    token = lexer.next_string_token(token.delimiter_state)\n    token.value.should eq(value)\n  end\nend\n\nprivate def it_lexes_operators(ops)\n  ops.each do |op|\n    it \"lexes #{op.inspect}\" do\n      lexer = Lexer.new op\n      lexer.slash_is_regex = false\n      token = lexer.next_token\n      token.type.operator?.should be_true\n      token.type.to_s.should eq(op)\n    end\n  end\nend\n\nprivate def it_lexes_const(value)\n  it_lexes value, :CONST, value\nend\n\nprivate def it_lexes_instance_var(value)\n  it_lexes value, :INSTANCE_VAR, value\nend\n\nprivate def it_lexes_class_var(value)\n  it_lexes value, :CLASS_VAR, value\nend\n\nprivate def it_lexes_globals(globals)\n  it_lexes_many globals, :GLOBAL\nend\n\nprivate def it_lexes_symbols(symbols)\n  symbols.each do |symbol|\n    value = symbol[1, symbol.size - 1]\n    value = value[1, value.size - 2] if value.starts_with?('\"')\n    it_lexes symbol, :SYMBOL, value\n  end\nend\n\nprivate def it_lexes_global_match_data_index(globals)\n  globals.each do |global|\n    it_lexes global, :GLOBAL_MATCH_DATA_INDEX, global[1, global.size - 1]\n  end\nend\n\ndescribe \"Lexer\" do\n  it_lexes \"\", :EOF\n  it_lexes \" \", :SPACE\n  it_lexes \"\\t\", :SPACE\n  it_lexes \"\\n\", :NEWLINE\n  it_lexes \"\\n\\n\\n\", :NEWLINE\n  it_lexes \"_\", :UNDERSCORE\n  it_lexes_keywords :def, :if, :else, :elsif, :end, :true, :false, :class, :module, :include,\n    :extend, :while, :until, :nil, :do, :yield, :return, :unless, :next, :break,\n    :begin, :lib, :fun, :type, :struct, :union, :enum, :macro, :out, :require,\n    :case, :when, :select, :then, :of, :abstract, :rescue, :ensure, :alias,\n    :pointerof, :sizeof, :instance_sizeof, :offsetof, :as, :typeof, :for, :in,\n    :with, :self, :super, :private, :protected, :asm, :uninitialized,\n    :annotation, :verbatim, :is_a_question, :as_question, :nil_question, :responds_to_question\n  it_lexes_idents [\"ident\", \"something\", \"with_underscores\", \"with_1\", \"foo?\", \"bar!\", \"fooBar\",\n                   \"❨╯°□°❩╯︵┻━┻\"]\n  it_lexes_idents [\"def?\", \"if?\", \"else?\", \"elsif?\", \"end?\", \"true?\", \"false?\", \"class?\", \"while?\",\n                   \"do?\", \"yield?\", \"return?\", \"unless?\", \"next?\", \"break?\", \"begin?\"]\n  it_lexes_idents [\"def!\", \"if!\", \"else!\", \"elsif!\", \"end!\", \"true!\", \"false!\", \"class!\", \"while!\",\n                   \"nil!\", \"do!\", \"yield!\", \"return!\", \"unless!\", \"next!\", \"break!\", \"begin!\"]\n  it_lexes_i32 [\"1\", [\"0i32\", \"0\"], [\"1hello\", \"1\"], \"+1\", \"-1\", \"1234\", \"+1234\", \"-1234\",\n                [\"1.foo\", \"1\"], [\"1_000\", \"1000\"], [\"100_000\", \"100000\"]]\n  it_lexes_i64 [[\"1i64\", \"1\"], [\"1_i64\", \"1\"], [\"1i64hello\", \"1\"], [\"+1_i64\", \"+1\"], [\"-1_i64\", \"-1\"]]\n  it_lexes_i128 [[\"1i128\", \"1\"], [\"1_i128\", \"1\"], [\"1i128hello\", \"1\"], [\"+1_i128\", \"+1\"], [\"-1_i128\", \"-1\"]]\n  it_lexes_f32 [[\"0f32\", \"0\"], [\"0_f32\", \"0\"], [\"1.0f32\", \"1.0\"], [\"1.0f32hello\", \"1.0\"],\n                [\"+1.0f32\", \"+1.0\"], [\"-1.0f32\", \"-1.0\"], [\"-0.0f32\", \"-0.0\"], [\"1_234.567_890_f32\", \"1234.567890\"]]\n  it_lexes_f64 [\"1.0\", [\"1.0hello\", \"1.0\"], \"+1.0\", \"-1.0\", [\"1_234.567_890\", \"1234.567890\"]]\n  it_lexes_f32 [[\"1e+23_f32\", \"1e+23\"], [\"1.2e+23_f32\", \"1.2e+23\"]]\n  it_lexes_f64 [\"1e23\", \"1e-23\", \"1e+23\", \"1.2e+23\", [\"1e23f64\", \"1e23\"], [\"1.2e+23_f64\", \"1.2e+23\"], \"0e40\", \"2e01\", [\"2_e2\", \"2e2\"], \"1E40\"]\n\n  it_lexes_number :i8, [\"1i8\", \"1\"]\n  it_lexes_number :i8, [\"1_i8\", \"1\"]\n\n  it_lexes_number :i16, [\"1i16\", \"1\"]\n  it_lexes_number :i16, [\"1_i16\", \"1\"]\n\n  it_lexes_number :i32, [\"1i32\", \"1\"]\n  it_lexes_number :i32, [\"1_i32\", \"1\"]\n\n  it_lexes_number :i64, [\"1i64\", \"1\"]\n  it_lexes_number :i64, [\"1_i64\", \"1\"]\n\n  it_lexes_number :u8, [\"1u8\", \"1\"]\n  it_lexes_number :u8, [\"1_u8\", \"1\"]\n\n  it_lexes_number :u16, [\"1u16\", \"1\"]\n  it_lexes_number :u16, [\"1_u16\", \"1\"]\n\n  it_lexes_number :u32, [\"1u32\", \"1\"]\n  it_lexes_number :u32, [\"1_u32\", \"1\"]\n\n  it_lexes_number :u64, [\"1u64\", \"1\"]\n  it_lexes_number :u64, [\"1_u64\", \"1\"]\n\n  it_lexes_number :u128, [\"1u128\", \"1\"]\n  it_lexes_number :u128, [\"1_u128\", \"1\"]\n\n  it_lexes_number :f32, [\"1f32\", \"1\"]\n  it_lexes_number :f32, [\"1.0f32\", \"1.0\"]\n\n  it_lexes_number :f64, [\"1f64\", \"1\"]\n  it_lexes_number :f64, [\"1.0f64\", \"1.0\"]\n\n  it_lexes_number :i32, [\"0b1010\", \"10\"]\n  it_lexes_number :i32, [\"+0b1010\", \"+10\"]\n  it_lexes_number :i32, [\"-0b1010\", \"-10\"]\n\n  it_lexes_number :i32, [\"0xFFFF\", \"65535\"]\n  it_lexes_number :i32, [\"0xabcdef\", \"11259375\"]\n  it_lexes_number :i32, [\"+0xFFFF\", \"+65535\"]\n  it_lexes_number :i32, [\"-0xFFFF\", \"-65535\"]\n\n  it_lexes_number :i64, [\"0x80000001\", \"2147483649\"]\n  it_lexes_number :i64, [\"-0x80000001\", \"-2147483649\"]\n  it_lexes_number :i64, [\"0xFFFFFFFF\", \"4294967295\"]\n  it_lexes_number :i64, [\"-0xFFFFFFFF\", \"-4294967295\"]\n\n  it_lexes_number :u64, [\"0xFFFF_u64\", \"65535\"]\n\n  it_lexes_i32 [[\"0o123\", \"83\"], [\"-0o123\", \"-83\"], [\"+0o123\", \"+83\"]]\n  it_lexes_f64 [[\"0.5\", \"0.5\"], [\"+0.5\", \"+0.5\"], [\"-0.5\", \"-0.5\"]]\n  it_lexes_i64 [[\"0o123_i64\", \"83\"], [\"0x1_i64\", \"1\"], [\"0b1_i64\", \"1\"]]\n\n  it_lexes_i64 [\"2147483648\", \"-2147483649\"]\n  it_lexes_i64 [[\"2147483648.foo\", \"2147483648\"]]\n  it_lexes_u64 [\"18446744073709551615\", \"14146167139683460000\", \"9223372036854775808\"]\n  it_lexes_number :u64, [\"10000000000000000000_u64\", \"10000000000000000000\"]\n\n  it_lexes_i64 [[\"0x3fffffffffffffff\", \"4611686018427387903\"]]\n  it_lexes_i64 [[\"-0x8000000000000000_i64\", \"-9223372036854775808\"]]\n  it_lexes_i64 [\"-9223372036854775808\", \"9223372036854775807\"]\n  it_lexes_u64 [[\"0xffffffffffffffff\", \"18446744073709551615\"]]\n\n  it_lexes_number :i32, [\"+0\", \"+0\"]\n  it_lexes_number :i32, [\"-0\", \"-0\"]\n\n  it_lexes_number :i32, [\"0\", \"0\"]\n  it_lexes_number :i32, [\"0_i32\", \"0\"]\n  it_lexes_number :i8, [\"0i8\", \"0\"]\n\n  it_lexes_i32 [[\"0🔮\", \"0\"], [\"12341234🔮\", \"12341234\"], [\"0x3🔮\", \"3\"]]\n  assert_syntax_error \"0b🔮\", \"numeric literal without digits\"\n\n  it_lexes_char \"'a'\", 'a'\n  it_lexes_char \"'\\\\a'\", '\\a'\n  it_lexes_char \"'\\\\b'\", '\\b'\n  it_lexes_char \"'\\\\n'\", '\\n'\n  it_lexes_char \"'\\\\t'\", '\\t'\n  it_lexes_char \"'\\\\v'\", '\\v'\n  it_lexes_char \"'\\\\f'\", '\\f'\n  it_lexes_char \"'\\\\r'\", '\\r'\n  it_lexes_char \"'\\\\0'\", '\\0'\n  it_lexes_char \"'\\\\0'\", '\\0'\n  it_lexes_char \"'\\\\''\", '\\''\n  it_lexes_char \"'\\\\\\\\'\", '\\\\'\n  assert_syntax_error \"'\", \"unterminated char literal\"\n  assert_syntax_error \"'\\\\\", \"unterminated char literal\"\n  it_lexes_operators [\"=\", \"<\", \"<=\", \">\", \">=\", \"+\", \"-\", \"*\", \"/\", \"//\", \"(\", \")\",\n                      \"==\", \"!=\", \"=~\", \"!\", \",\", \".\", \"..\", \"...\", \"&&\", \"||\",\n                      \"|\", \"{\", \"}\", \"?\", \":\", \"+=\", \"-=\", \"*=\", \"/=\", \"%=\", \"//=\", \"&=\",\n                      \"|=\", \"^=\", \"**=\", \"<<\", \">>\", \"%\", \"&\", \"|\", \"^\", \"**\", \"<<=\",\n                      \">>=\", \"~\", \"[]\", \"[]=\", \"[\", \"]\", \"::\", \"<=>\", \"=>\", \"||=\",\n                      \"&&=\", \"===\", \";\", \"->\", \"[]?\", \"{%\", \"{{\", \"%}\", \"@[\", \"!~\",\n                      \"&+\", \"&-\", \"&*\", \"&**\", \"&+=\", \"&-=\", \"&*=\"]\n  it_lexes \"!@foo\", :OP_BANG\n  it_lexes \"+@foo\", :OP_PLUS\n  it_lexes \"-@foo\", :OP_MINUS\n  it_lexes \"&+@foo\", :OP_AMP_PLUS\n  it_lexes \"&-@foo\", :OP_AMP_MINUS\n  it_lexes_const \"Foo\"\n  it_lexes_const \"ÁrvíztűrőTükörfúrógép\"\n  it_lexes_const \"ǅǈǋǲᾈᾉᾊ\"\n  it_lexes_instance_var \"@foo\"\n  it_lexes_class_var \"@@foo\"\n  it_lexes_globals [\"$foo\", \"$FOO\", \"$_foo\", \"$foo123\"]\n  it_lexes_symbols [\":foo\", \":foo!\", \":foo?\", \":foo=\", \":\\\"foo\\\"\", \":かたな\", \":+\", \":-\", \":*\", \":/\", \"://\",\n                    \":==\", \":<\", \":<=\", \":>\", \":>=\", \":!\", \":!=\", \":=~\", \":!~\", \":&\", \":|\",\n                    \":^\", \":~\", \":**\", \":>>\", \":<<\", \":%\", \":[]\", \":[]?\", \":[]=\", \":<=>\", \":===\",\n                    \":&+\", \":&-\", \":&*\", \":&**\"]\n\n  it_lexes_global_match_data_index [\"$1\", \"$10\", \"$1?\", \"$10?\", \"$23?\"]\n  assert_syntax_error \"$01\", %(unexpected token: \"1\")\n  assert_syntax_error \"$0?\"\n\n  it_lexes \"$~\", :OP_DOLLAR_TILDE\n  it_lexes \"$?\", :OP_DOLLAR_QUESTION\n\n  assert_syntax_error \"128_i8\", \"128 doesn't fit in an Int8\"\n  assert_syntax_error \"-129_i8\", \"-129 doesn't fit in an Int8\"\n  assert_syntax_error \"256_u8\", \"256 doesn't fit in an UInt8\"\n  assert_syntax_error \"-1_u8\", \"Invalid negative value -1 for UInt8\"\n\n  assert_syntax_error \"32768_i16\", \"32768 doesn't fit in an Int16\"\n  assert_syntax_error \"-32769_i16\", \"-32769 doesn't fit in an Int16\"\n  assert_syntax_error \"65536_u16\", \"65536 doesn't fit in an UInt16\"\n  assert_syntax_error \"-1_u16\", \"Invalid negative value -1 for UInt16\"\n\n  assert_syntax_error \"2147483648_i32\", \"2147483648 doesn't fit in an Int32\"\n  assert_syntax_error \"-2147483649_i32\", \"-2147483649 doesn't fit in an Int32\"\n  assert_syntax_error \"4294967296_u32\", \"4294967296 doesn't fit in an UInt32\"\n  assert_syntax_error \"-1_u32\", \"Invalid negative value -1 for UInt32\"\n\n  assert_syntax_error \"9223372036854775808_i64\", \"9223372036854775808 doesn't fit in an Int64\"\n  assert_syntax_error \"-9223372036854775809_i64\", \"-9223372036854775809 doesn't fit in an Int64\"\n  assert_syntax_error \"118446744073709551616_u64\", \"118446744073709551616 doesn't fit in an UInt64\"\n  assert_syntax_error \"18446744073709551616_u64\", \"18446744073709551616 doesn't fit in an UInt64\"\n  assert_syntax_error \"-1_u64\", \"Invalid negative value -1 for UInt64\"\n  assert_syntax_error \"-0_u64\", \"Invalid negative value -0 for UInt64\"\n  assert_syntax_error \"-0u64\", \"Invalid negative value -0 for UInt64\"\n\n  assert_syntax_error \"18446744073709551616_i32\", \"18446744073709551616 doesn't fit in an Int32\"\n  assert_syntax_error \"9999999999999999999_i32\", \"9999999999999999999 doesn't fit in an Int32\"\n\n  assert_syntax_error \"-9999999999999999999\", \"-9999999999999999999 doesn't fit in an Int64, try using the suffix i128\"\n  assert_syntax_error \"-99999999999999999999\", \"-99999999999999999999 doesn't fit in an Int64, try using the suffix i128\"\n  assert_syntax_error \"-11111111111111111111\", \"-11111111111111111111 doesn't fit in an Int64, try using the suffix i128\"\n  assert_syntax_error \"-9223372036854775809\", \"-9223372036854775809 doesn't fit in an Int64, try using the suffix i128\"\n  assert_syntax_error \"118446744073709551616\", \"118446744073709551616 doesn't fit in an UInt64, try using the suffix i128\"\n  assert_syntax_error \"18446744073709551616\", \"18446744073709551616 doesn't fit in an UInt64, try using the suffix i128\"\n\n  assert_syntax_error \"340282366920938463463374607431768211456\", \"340282366920938463463374607431768211456 doesn't fit in an UInt64\"\n  assert_syntax_error \"-170141183460469231731687303715884105729\", \"-170141183460469231731687303715884105729 doesn't fit in an Int64\"\n  assert_syntax_error \"-999999999999999999999999999999999999999\", \"-999999999999999999999999999999999999999 doesn't fit in an Int64\"\n\n  assert_syntax_error \"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\", \"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF doesn't fit in an UInt64\"\n  assert_syntax_error \"0o7777777777777777777777777777777777777777777777777\", \"0o7777777777777777777777777777777777777777777777777 doesn't fit in an UInt64\"\n  assert_syntax_error \"-0o7777777777777777777777777777777777777777777777777\", \"-0o7777777777777777777777777777777777777777777777777 doesn't fit in an Int64\"\n\n  it_lexes_number :i128, [\"9223372036854775808_i128\", \"9223372036854775808\"]\n  it_lexes_number :i128, [\"-9223372036854775809_i128\", \"-9223372036854775809\"]\n  it_lexes_number :u128, [\"118446744073709551616_u128\", \"118446744073709551616\"]\n  it_lexes_number :u128, [\"18446744073709551616_u128\", \"18446744073709551616\"]\n  it_lexes_number :i128, [\"170141183460469231731687303715884105727_i128\", \"170141183460469231731687303715884105727\"]\n  it_lexes_number :u128, [\"170141183460469231731687303715884105728_u128\", \"170141183460469231731687303715884105728\"]\n  it_lexes_number :u128, [\"340282366920938463463374607431768211455_u128\", \"340282366920938463463374607431768211455\"]\n  it_lexes_number :u128, [\"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128\", \"340282366920938463463374607431768211455\"]\n  it_lexes_number :i128, [\"-0x80000000000000000000000000000000_i128\", \"-170141183460469231731687303715884105728\"]\n  assert_syntax_error \"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\", \"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF doesn't fit in an UInt64, try using the suffix u128\"\n  assert_syntax_error \"-0x80000000000000000000000000000000\", \"-0x80000000000000000000000000000000 doesn't fit in an Int64, try using the suffix i128\"\n  assert_syntax_error \"-0x80000000000000000000000000000001\", \"-0x80000000000000000000000000000001 doesn't fit in an Int64\"\n  assert_syntax_error \"-1_u128\", \"Invalid negative value -1 for UInt128\"\n\n  assert_syntax_error \"1__1\", \"consecutive underscores in numbers aren't allowed\"\n  assert_syntax_error \"-3_\", \"trailing '_' in number\"\n  assert_syntax_error \"0b_10\", \"unexpected '_' in number\"\n  assert_syntax_error \"10e_10\", \"unexpected '_' in number\"\n  assert_syntax_error \"1_.1\", \"unexpected '_' in number\"\n  assert_syntax_error \"-0e_12\", \"unexpected '_' in number\"\n\n  assert_syntax_error \"0_12\", \"octal constants should be prefixed with 0o\"\n  assert_syntax_error \"0123\", \"octal constants should be prefixed with 0o\"\n  assert_syntax_error \"00\", \"octal constants should be prefixed with 0o\"\n  assert_syntax_error \"01_i64\", \"octal constants should be prefixed with 0o\"\n\n  assert_syntax_error \"0xFF_i8\", \"0xFF doesn't fit in an Int8\"\n  assert_syntax_error \"0o200_i8\", \"0o200 doesn't fit in an Int8\"\n  assert_syntax_error \"0b10000000_i8\", \"0b10000000 doesn't fit in an Int8\"\n\n  assert_syntax_error \"0b11_f32\", \"binary float literal is not supported\"\n  assert_syntax_error \"0o73_f64\", \"octal float literal is not supported\"\n\n  # 2**31 - 1\n  it_lexes_i32 [[\"0x7fffffff\", \"2147483647\"], [\"0o17777777777\", \"2147483647\"], [\"0b1111111111111111111111111111111\", \"2147483647\"]]\n  it_lexes_i32 [[\"0x7fffffff_i32\", \"2147483647\"], [\"0o17777777777_i32\", \"2147483647\"], [\"0b1111111111111111111111111111111_i32\", \"2147483647\"]]\n  # 2**32 - 1\n  it_lexes_i64 [[\"0xffffffff\", \"4294967295\"], [\"0o37777777777\", \"4294967295\"], [\"0b11111111111111111111111111111111\", \"4294967295\"]]\n  # 2**32\n  it_lexes_i64 [[\"0x100000000\", \"4294967296\"], [\"0o40000000000\", \"4294967296\"], [\"0b100000000000000000000000000000000\", \"4294967296\"]]\n  assert_syntax_error \"0x100000000i32\", \"0x100000000 doesn't fit in an Int32\"\n  assert_syntax_error \"0o40000000000i32\", \"0o40000000000 doesn't fit in an Int32\"\n  assert_syntax_error \"0b100000000000000000000000000000000i32\", \"0b100000000000000000000000000000000 doesn't fit in an Int32\"\n  # 2**63 - 1\n  it_lexes_i64 [[\"0x7fffffffffffffff\", \"9223372036854775807\"], [\"0o777777777777777777777\", \"9223372036854775807\"], [\"0b111111111111111111111111111111111111111111111111111111111111111\", \"9223372036854775807\"]]\n  # 2**63\n  it_lexes_u64 [[\"0x8000000000000000\", \"9223372036854775808\"], [\"0o1000000000000000000000\", \"9223372036854775808\"], [\"0b1000000000000000000000000000000000000000000000000000000000000000\", \"9223372036854775808\"]]\n  assert_syntax_error \"0x8000000000000000i64\", \"0x8000000000000000 doesn't fit in an Int64\"\n  assert_syntax_error \"0o1000000000000000000000i64\", \"0o1000000000000000000000 doesn't fit in an Int64\"\n  assert_syntax_error \"0b1000000000000000000000000000000000000000000000000000000000000000i64\", \"0b1000000000000000000000000000000000000000000000000000000000000000 doesn't fit in an Int64\"\n  # 2**64 - 1\n  it_lexes_u64 [[\"0xffff_ffff_ffff_ffff\", \"18446744073709551615\"], [\"0o177777_77777777_77777777\", \"18446744073709551615\"], [\"0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111\", \"18446744073709551615\"]]\n  it_lexes_u64 [[\"0x00ffffffffffffffff\", \"18446744073709551615\"], [\"0o001777777777777777777777\", \"18446744073709551615\"], [\"0b001111111111111111111111111111111111111111111111111111111111111111\", \"18446744073709551615\"]]\n  # 2**64\n  assert_syntax_error \"0x10000_0000_0000_0000\", \"0x10000_0000_0000_0000 doesn't fit in an UInt64, try using the suffix i128\"\n  it_lexes_number :i128, [\"0x10000_0000_0000_0000_i128\", \"18446744073709551616\"]\n  assert_syntax_error \"0x10000_0000_0000_0000_u64\", \"0x10000_0000_0000_0000 doesn't fit in an UInt64\"\n  assert_syntax_error \"0xfffffffffffffffff_u64\", \"0xfffffffffffffffff doesn't fit in an UInt64\"\n  assert_syntax_error \"0o200000_00000000_00000000_u64\", \"0o200000_00000000_00000000 doesn't fit in an UInt64\"\n  assert_syntax_error \"0o200000_00000000_00000000\", \"0o200000_00000000_00000000 doesn't fit in an UInt64, try using the suffix i128\"\n  assert_syntax_error \"0b100000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_u64\", \"0b100000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 doesn't fit in an UInt64\"\n  assert_syntax_error \"0b100000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000\", \"0b100000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 doesn't fit in an UInt64, try using the suffix i128\"\n  # Very large\n  assert_syntax_error \"0x1afafafafafafafafafafaf\", \"0x1afafafafafafafafafafaf doesn't fit in an UInt64, try using the suffix i128\"\n  assert_syntax_error \"0x1afafafafafafafafafafafu64\", \"0x1afafafafafafafafafafaf doesn't fit in an UInt64\"\n  assert_syntax_error \"0x1afafafafafafafafafafafi32\", \"0x1afafafafafafafafafafaf doesn't fit in an Int32\"\n  assert_syntax_error \"0o1234567123456712345671234567u64\", \"0o1234567123456712345671234567 doesn't fit in an UInt64\"\n  assert_syntax_error \"0o1234567123456712345671234567\", \"0o1234567123456712345671234567 doesn't fit in an UInt64, try using the suffix i128\"\n  assert_syntax_error \"0o12345671234567_12345671234567_i8\", \"0o12345671234567_12345671234567 doesn't fit in an Int8\"\n  assert_syntax_error \"0b100000000000000000000000000000000000000000000000000000000000000000\", \"0b100000000000000000000000000000000000000000000000000000000000000000 doesn't fit in an UInt64, try using the suffix i128\"\n  assert_syntax_error \"0b100000000000000000000000000000000000000000000000000000000000000000u64\", \"0b100000000000000000000000000000000000000000000000000000000000000000 doesn't fit in an UInt64\"\n\n  it_lexes_i64 [[\"0o700000000000000000000\", \"8070450532247928832\"]]\n  it_lexes_u64 [[\"0o1000000000000000000000\", \"9223372036854775808\"]]\n\n  assert_syntax_error \"4f33\", \"invalid float suffix\"\n  assert_syntax_error \"4f65\", \"invalid float suffix\"\n  assert_syntax_error \"4f22\", \"invalid float suffix\"\n  assert_syntax_error \"4i33\", \"invalid int suffix\"\n  assert_syntax_error \"4i65\", \"invalid int suffix\"\n  assert_syntax_error \"4i22\", \"invalid int suffix\"\n  assert_syntax_error \"4i3\", \"invalid int suffix\"\n  assert_syntax_error \"4i12\", \"invalid int suffix\"\n  assert_syntax_error \"4u33\", \"invalid uint suffix\"\n  assert_syntax_error \"4u65\", \"invalid uint suffix\"\n  assert_syntax_error \"4u22\", \"invalid uint suffix\"\n  assert_syntax_error \"4u3\", \"invalid uint suffix\"\n  assert_syntax_error \"4u12\", \"invalid uint suffix\"\n  # Tests for #8782\n  assert_syntax_error \"4F32\", %(unexpected token: \"F32\")\n  assert_syntax_error \"4F64\", %(unexpected token: \"F64\")\n  assert_syntax_error \"0F32\", %(unexpected token: \"F32\")\n\n  assert_syntax_error \"4.0_u32\", \"Invalid suffix u32 for decimal number\"\n  assert_syntax_error \"2e8i8\", \"Invalid suffix i8 for decimal number\"\n\n  assert_syntax_error \".42\", \".1 style number literal is not supported, put 0 before dot\"\n  assert_syntax_error \"-.42\", \".1 style number literal is not supported, put 0 before dot\"\n\n  assert_syntax_error \"2e\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2e+\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2ef32\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2e+@foo\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2e+e\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2e+f32\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2e+-2\", \"invalid decimal number exponent\"\n  assert_syntax_error \"2e+_2\", \"unexpected '_' in number\"\n\n  # Test for #11671\n  it_lexes_i32 [[\"0b0_1\", \"1\"]]\n\n  it \"lexes not instance var\" do\n    lexer = Lexer.new \"!@foo\"\n    token = lexer.next_token\n    token.type.should eq(t :OP_BANG)\n    token = lexer.next_token\n    token.type.should eq(t :INSTANCE_VAR)\n    token.value.should eq(\"@foo\")\n  end\n\n  it \"lexes space after keyword\" do\n    lexer = Lexer.new \"end 1\"\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(Keyword::END)\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n  end\n\n  it \"lexes space after char\" do\n    lexer = Lexer.new \"'a' \"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.should eq('a')\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n  end\n\n  it \"lexes comment and token\" do\n    lexer = Lexer.new \"# comment\\n=\"\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token = lexer.next_token\n    token.type.should eq(t :OP_EQ)\n  end\n\n  it \"lexes comment at the end\" do\n    lexer = Lexer.new \"# comment\"\n    token = lexer.next_token\n    token.type.should eq(t :EOF)\n  end\n\n  it \"lexes __LINE__\" do\n    lexer = Lexer.new \"__LINE__\"\n    token = lexer.next_token\n    token.type.should eq(t :MAGIC_LINE)\n  end\n\n  it \"lexes __FILE__\" do\n    lexer = Lexer.new \"__FILE__\"\n    lexer.filename = \"foo\"\n    token = lexer.next_token\n    token.type.should eq(t :MAGIC_FILE)\n  end\n\n  it \"lexes __DIR__\" do\n    lexer = Lexer.new \"__DIR__\"\n    token = lexer.next_token\n    token.type.should eq(t :MAGIC_DIR)\n  end\n\n  it \"lexes dot and ident\" do\n    lexer = Lexer.new \".read\"\n    token = lexer.next_token\n    token.type.should eq(t :OP_PERIOD)\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"read\")\n    token = lexer.next_token\n    token.type.should eq(t :EOF)\n  end\n\n  assert_syntax_error \"/foo\", \"Unterminated regular expression\"\n  assert_syntax_error \"/\\\\\", \"Unterminated regular expression\"\n  assert_syntax_error \":\\\"foo\", \"unterminated quoted symbol\"\n\n  it \"lexes utf-8 char\" do\n    lexer = Lexer.new \"'á'\"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).ord.should eq(225)\n  end\n\n  it \"lexes utf-8 multibyte char\" do\n    lexer = Lexer.new \"'日'\"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).ord.should eq(26085)\n  end\n\n  it \"doesn't raise if slash r with slash n\" do\n    lexer = Lexer.new(\"\\r\\n1\")\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n  end\n\n  it \"doesn't raise if many slash r with slash n\" do\n    lexer = Lexer.new(\"\\r\\n\\r\\n\\r\\n1\")\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n  end\n\n  assert_syntax_error \"\\r1\", \"expected '\\\\n' after '\\\\r'\"\n\n  it \"lexes char with unicode codepoint\" do\n    lexer = Lexer.new \"'\\\\uFEDA'\"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).ord.should eq(0xFEDA)\n  end\n\n  it \"lexes char with unicode codepoint and curly with zeros\" do\n    lexer = Lexer.new \"'\\\\u{0}'\"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).ord.should eq(0)\n  end\n\n  it \"lexes char with unicode codepoint and curly\" do\n    lexer = Lexer.new \"'\\\\u{A5}'\"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).ord.should eq(0xA5)\n  end\n\n  it \"lexes char with unicode codepoint and curly with six hex digits\" do\n    lexer = Lexer.new \"'\\\\u{10FFFF}'\"\n    token = lexer.next_token\n    token.type.should eq(t :CHAR)\n    token.value.as(Char).ord.should eq(0x10FFFF)\n  end\n\n  it \"lexes float then zero (bug)\" do\n    lexer = Lexer.new \"2.5 0\"\n    lexer.next_token.number_kind.should eq(NumberKind::F64)\n    lexer.next_token.type.should eq(t :SPACE)\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.number_kind.should eq(NumberKind::I32)\n  end\n\n  it \"lexes symbol with quote\" do\n    lexer = Lexer.new %(:\"\\\\\"\")\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"\\\"\")\n  end\n\n  it \"lexes symbol with backslash (#2187)\" do\n    lexer = Lexer.new %(:\"\\\\\\\\\")\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"\\\\\")\n  end\n\n  it \"lexes symbol followed by !=\" do\n    lexer = Lexer.new \":a!=:a\"\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"a\")\n    token = lexer.next_token\n    token.type.should eq(t :OP_BANG_EQ)\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"a\")\n  end\n\n  it \"lexes symbol followed by ==\" do\n    lexer = Lexer.new \":a==:a\"\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"a\")\n    token = lexer.next_token\n    token.type.should eq(t :OP_EQ_EQ)\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"a\")\n  end\n\n  it \"lexes symbol followed by ===\" do\n    lexer = Lexer.new \":a===:a\"\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"a\")\n    token = lexer.next_token\n    token.type.should eq(t :OP_EQ_EQ_EQ)\n    token = lexer.next_token\n    token.type.should eq(t :SYMBOL)\n    token.value.should eq(\"a\")\n  end\n\n  it \"lexes != after identifier (#4815)\" do\n    lexer = Lexer.new(\"some_method!=5\")\n    token = lexer.next_token\n    token.type.should eq(t :IDENT)\n    token.value.should eq(\"some_method\")\n    token = lexer.next_token\n    token.type.should eq(t :OP_BANG_EQ)\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n  end\n\n  assert_syntax_error \"'\\\\uFEDZ'\", \"expected hexadecimal character in unicode escape\"\n  assert_syntax_error \"'\\\\u{}'\", \"expected hexadecimal character in unicode escape\"\n  assert_syntax_error \"'\\\\u{110000}'\", \"invalid unicode codepoint (too large)\"\n  assert_syntax_error \"'\\\\uD800'\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \"'\\\\uDFFF'\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \"'\\\\u{D800}'\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \"'\\\\u{DFFF}'\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \":+1\", \"unexpected token\"\n\n  it \"invalid byte sequence\" do\n    expect_raises(InvalidByteSequenceError, \"Unexpected byte 0xff at position 0, malformed UTF-8\") do\n      parse \"\\xFF\"\n    end\n    expect_raises(InvalidByteSequenceError, \"Unexpected byte 0xff at position 1, malformed UTF-8\") do\n      parse \" \\xFF\"\n    end\n  end\n\n  assert_syntax_error \"'\\\\1'\", \"invalid char escape sequence\"\n\n  it_lexes_string %(\"\\\\1\"), String.new(Bytes[1])\n  it_lexes_string %(\"\\\\4\"), String.new(Bytes[4])\n  it_lexes_string %(\"\\\\10\"), String.new(Bytes[8])\n  it_lexes_string %(\"\\\\110\"), String.new(Bytes[72])\n  it_lexes_string %(\"\\\\8\"), \"8\"\n  assert_syntax_error %(\"\\\\400\"), \"octal value too big\"\n\n  it_lexes_string %(\"\\\\x12\"), String.new(Bytes[0x12])\n  it_lexes_string %(\"\\\\xFF\"), String.new(Bytes[0xFF])\n  assert_syntax_error %(\"\\\\xz\"), \"invalid hex escape\"\n  assert_syntax_error %(\"\\\\x1z\"), \"invalid hex escape\"\n\n  assert_syntax_error %(\"hi\\\\)\n\n  it \"lexes regex after \\\\n\" do\n    lexer = Lexer.new(\"\\n/=/\")\n    lexer.slash_is_regex = true\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token = lexer.next_token\n    token.type.should eq(t :DELIMITER_START)\n    token.delimiter_state.kind.should eq(Token::DelimiterKind::REGEX)\n  end\n\n  it \"lexes regex after \\\\r\\\\n\" do\n    lexer = Lexer.new(\"\\r\\n/=/\")\n    lexer.slash_is_regex = true\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token = lexer.next_token\n    token.type.should eq(t :DELIMITER_START)\n    token.delimiter_state.kind.should eq(Token::DelimiterKind::REGEX)\n  end\n\n  it \"lexes heredoc start\" do\n    lexer = Lexer.new(\"<<-EOS\\n\")\n    lexer.wants_raw = true\n    token = lexer.next_token\n    token.type.should eq(t :DELIMITER_START)\n    token.delimiter_state.kind.should eq(Token::DelimiterKind::HEREDOC)\n    token.raw.should eq \"<<-EOS\"\n  end\n\n  assert_syntax_error \"<<-123\\n456\\n123\", \"heredoc identifier starts with invalid character\"\nend\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_string_array_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def t(kind : Crystal::Token::Kind)\n  kind\nend\n\nprivate def it_should_be_valid_string_array_lexer(lexer)\n  token = lexer.next_token\n  token.type.should eq(t :STRING_ARRAY_START)\n\n  token = lexer.next_string_array_token\n  token.type.should eq(t :STRING)\n  token.value.should eq(\"one\")\n\n  token = lexer.next_string_array_token\n  token.type.should eq(t :STRING)\n  token.value.should eq(\"two\")\n\n  token = lexer.next_string_array_token\n  token.type.should eq(t :STRING_ARRAY_END)\nend\n\ndescribe \"Lexer string array\" do\n  it \"lexes simple string array\" do\n    lexer = Lexer.new(\"%w(one two)\")\n\n    it_should_be_valid_string_array_lexer(lexer)\n  end\n\n  it \"lexes string array with new line\" do\n    lexer = Lexer.new(\"%w(one \\n two)\")\n\n    token = lexer.next_token\n    token.type.should eq(t :STRING_ARRAY_START)\n\n    token = lexer.next_string_array_token\n    token.type.should eq(t :STRING)\n    token.value.should eq(\"one\")\n\n    token = lexer.next_string_array_token\n    token.type.should eq(t :STRING)\n    token.value.should eq(\"two\")\n\n    token = lexer.next_string_array_token\n    token.type.should eq(t :STRING_ARRAY_END)\n  end\n\n  it \"lexes string array with new line gives correct column for next token\" do\n    lexer = Lexer.new(\"%w(one \\n two).\")\n\n    lexer.next_token\n    lexer.next_string_array_token\n    lexer.next_string_array_token\n    lexer.next_string_array_token\n\n    token = lexer.next_token\n    token.line_number.should eq(2)\n    token.column_number.should eq(6)\n  end\n\n  context \"using { as delimiter\" do\n    it \"lexes simple string array\" do\n      lexer = Lexer.new(\"%w{one two}\")\n\n      it_should_be_valid_string_array_lexer(lexer)\n    end\n  end\n\n  context \"using [ as delimiter\" do\n    it \"lexes simple string array\" do\n      lexer = Lexer.new(\"%w[one two]\")\n\n      it_should_be_valid_string_array_lexer(lexer)\n    end\n  end\n\n  context \"using < as delimiter\" do\n    it \"lexes simple string array\" do\n      lexer = Lexer.new(\"%w<one two>\")\n\n      it_should_be_valid_string_array_lexer(lexer)\n    end\n  end\n\n  context \"using | as delimiter\" do\n    it \"lexes simple string array\" do\n      lexer = Lexer.new(\"%w|one two|\")\n\n      it_should_be_valid_string_array_lexer(lexer)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/lexer/lexer_string_spec.cr",
    "content": "require \"../../support/syntax\"\nrequire \"./lexer_objects/strings\"\n\ndescribe \"Lexer string\" do\n  it \"lexes simple string\" do\n    lexer = Lexer.new(%(\"hello\"))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('\"', '\"')\n    tester.next_string_token_should_be(\"hello\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with newline\" do\n    lexer = Lexer.new(\"\\\"hello\\\\nworld\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"hello\")\n    tester.next_string_token_should_be(\"\\n\")\n    tester.next_string_token_should_be(\"world\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with slash\" do\n    lexer = Lexer.new(\"\\\"hello\\\\\\\\world\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"hello\")\n    tester.next_string_token_should_be(\"\\\\\")\n    tester.next_string_token_should_be(\"world\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with slash quote\" do\n    lexer = Lexer.new(\"\\\"\\\\\\\"\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"\\\"\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with slash t\" do\n    lexer = Lexer.new(\"\\\"\\\\t\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"\\t\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with interpolation\" do\n    lexer = Lexer.new(\"\\\"hello \\#{world}\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"hello \")\n    tester.string_should_have_an_interpolation_of(\"world\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with numeral\" do\n    lexer = Lexer.new(\"\\\"hello#world\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"hello\")\n    tester.next_string_token_should_be(\"#\")\n    tester.next_string_token_should_be(\"world\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with literal newline\" do\n    lexer = Lexer.new(\"\\\"hello\\nworld\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"hello\")\n    tester.next_string_token_should_be(\"\\n\")\n    tester.next_string_token_should_be(\"world\")\n    tester.string_should_end_correctly\n    tester.next_token_should_be_at(line: 2, column: 7)\n  end\n\n  it \"lexes string with only newline\" do\n    lexer = Lexer.new(\"\\\"\\n\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"\\n\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes double numeral\" do\n    lexer = Lexer.new(\"\\\"##\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"#\")\n    tester.next_string_token_should_be(\"#\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with interpolation with double numeral\" do\n    lexer = Lexer.new(\"\\\"hello \\#\\#{world}\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"hello \")\n    tester.next_string_token_should_be(\"#\")\n    tester.string_should_have_an_interpolation_of(\"world\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes slash with no-escape char\" do\n    lexer = Lexer.new(\"\\\"\\\\h\\\"\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_string_token_should_be(\"h\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes simple string with %(\" do\n    lexer = Lexer.new(\"%(hello)\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('(', ')')\n    tester.next_string_token_should_be(\"hello\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes simple string with %|\" do\n    lexer = Lexer.new(\"%|hello|\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('|', '|')\n    tester.next_string_token_should_be(\"hello\")\n    tester.string_should_end_correctly\n  end\n\n  [['(', ')'], ['[', ']'], ['{', '}'], ['<', '>']].each do |(left, right)|\n    it \"lexes simple string with nested %#{left}\" do\n      lexer = Lexer.new(\"%#{left}hello #{left}world#{right}#{right}\")\n      tester = LexerObjects::Strings.new(lexer)\n\n      tester.string_should_be_delimited_by(left, right)\n      tester.next_string_token_should_be(\"hello \")\n      tester.next_string_token_should_be_opening\n      tester.next_string_token_should_be(\"world\")\n      tester.next_string_token_should_be_closing\n      tester.string_should_end_correctly\n    end\n  end\n\n  it \"lexes heredoc\" do\n    string = \"Hello, mom! I am HERE.\\nHER dress is beautiful.\\nHE is OK.\\n  HERESY\"\n    lexer = Lexer.new(\"<<-HERE\\n#{string}\\nHERE\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_token_should_be(:NEWLINE)\n    tester.next_string_token_should_be(\"Hello, mom! I am HERE.\")\n    tester.next_string_token_should_be(\"\\nHER dress is beautiful.\")\n    tester.next_string_token_should_be(\"\\nHE is OK.\")\n    tester.next_string_token_should_be(\"\\n  HERESY\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes heredoc with empty line\" do\n    lexer = Lexer.new(\"<<-XML\\nfoo\\n\\nXML\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_token_should_be(:NEWLINE)\n    tester.next_string_token_should_be(\"foo\")\n    tester.next_string_token_should_be(\"\\n\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes heredoc with \\\\r\\\\n\" do\n    lexer = Lexer.new(\"<<-XML\\r\\nfoo\\r\\n\\nXML\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_token_should_be(:NEWLINE)\n    tester.next_string_token_should_be(\"foo\")\n    tester.next_string_token_should_be(\"\\r\\n\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes heredoc with spaces before close tag\" do\n    lexer = Lexer.new(\"<<-XML\\nfoo\\n   XML\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_token_should_be(:NEWLINE)\n    tester.next_string_token_should_be(\"foo\")\n    tester.string_should_end_correctly\n  end\n\n  it \"assigns correct location after heredoc (#346)\" do\n    string = \"Hello, mom! I am HERE.\\nHER dress is beautiful.\\nHE is OK.\"\n    lexer = Lexer.new(\"<<-HERE\\n#{string}\\nHERE\\n1\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_token_should_be(:NEWLINE)\n    tester.next_string_token_should_be(\"Hello, mom! I am HERE.\")\n    tester.token_should_be_at(line: 2)\n    tester.next_string_token_should_be(\"\\nHER dress is beautiful.\")\n    tester.token_should_be_at(line: 3)\n    tester.next_string_token_should_be(\"\\nHE is OK.\")\n    tester.token_should_be_at(line: 4)\n    tester.string_should_end_correctly(false)\n    tester.next_token_should_be(:NEWLINE)\n    tester.token_should_be_at(line: 5, column: 5)\n    tester.next_token_should_be(:NUMBER)\n    tester.token_should_be_at(line: 6, column: 1)\n  end\n\n  it \"lexes interpolations in heredocs\" do\n    lexer = Lexer.new(\"<<-HERE\\n\\abc\\#{foo}\\nHERE\")\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_token_should_be(:NEWLINE)\n    tester.next_string_token_should_be(\"\\abc\")\n    tester.string_should_have_an_interpolation_of(\"foo\")\n    tester.string_should_end_correctly\n  end\n\n  it \"raises on unterminated heredoc\" do\n    lexer = Lexer.new(\"<<-HERE\\nHello\")\n    token = lexer.next_token\n    state = token.delimiter_state\n\n    expect_raises Crystal::SyntaxException, \"Unterminated heredoc\" do\n      loop do\n        token = lexer.next_string_token state\n        break if token.type.delimiter_end?\n      end\n    end\n  end\n\n  it \"raises when identifier doesn't start with a letter or number\" do\n    lexer = Lexer.new(\"<<-!!!\\\\ntest\\n!!!\\n\")\n\n    expect_raises Crystal::SyntaxException, /heredoc identifier starts with invalid character/ do\n      lexer.next_token\n    end\n  end\n\n  it \"raises on unexpected EOF while lexing heredoc\" do\n    lexer = Lexer.new(\"<<-aaa\")\n\n    expect_raises Crystal::SyntaxException, \"Unexpected EOF on heredoc identifier\" do\n      lexer.next_token\n    end\n  end\n\n  it \"lexes string with unicode codepoint\" do\n    lexer = Lexer.new \"\\\"\\\\uFEDA\\\"\"\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_unicode_tokens_should_be(0xFEDA)\n  end\n\n  it \"lexes string with unicode codepoint in curly\" do\n    lexer = Lexer.new \"\\\"\\\\u{A5}\\\"\"\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_unicode_tokens_should_be(0xA5)\n  end\n\n  it \"lexes string with unicode codepoint in curly multiple times\" do\n    lexer = Lexer.new \"\\\"\\\\u{A5 A6 10FFFF}\\\"\"\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_start_correctly\n    tester.next_unicode_tokens_should_be([0xA5, 0xA6, 0x10FFFF])\n  end\n\n  assert_syntax_error \"\\\"\\\\uFEDZ\\\"\", \"expected hexadecimal character in unicode escape\"\n  assert_syntax_error \"\\\"\\\\u{}\\\"\", \"expected hexadecimal character in unicode escape\"\n  assert_syntax_error \"\\\"\\\\u{110000}\\\"\", \"invalid unicode codepoint (too large)\"\n  assert_syntax_error \"\\\"\\\\uD800\\\"\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \"\\\"\\\\uDFFF\\\"\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \"\\\"\\\\u{D800}\\\"\", \"invalid unicode codepoint (surrogate half)\"\n  assert_syntax_error \"\\\"\\\\u{DFFF}\\\"\", \"invalid unicode codepoint (surrogate half)\"\n\n  it \"lexes backtick string\" do\n    lexer = Lexer.new(%(`hello`))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('`', '`')\n    tester.next_string_token_should_be(\"hello\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string\" do\n    lexer = Lexer.new(%(/hello/))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('/', '/')\n    tester.next_string_token_should_be(\"hello\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string with escaped slash with /.../\" do\n    lexer = Lexer.new(%(/\\\\//))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('/', '/')\n    tester.next_string_token_should_be(\"/\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string with escaped slash with %r(...)\" do\n    lexer = Lexer.new(%(%r(\\\\/)))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('(', ')')\n    tester.next_string_token_should_be(\"/\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string with escaped space with /.../\" do\n    lexer = Lexer.new(%(/\\\\ /))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('/', '/')\n    tester.next_string_token_should_be(\" \")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string with escaped space with %r(...)\" do\n    lexer = Lexer.new(%(%r(\\\\ )))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('(', ')')\n    tester.next_string_token_should_be(\" \")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string with special chars with /.../\" do\n    lexer = Lexer.new(%(/\\\\w/))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('/', '/')\n    tester.next_string_token_should_be(\"\\\\w\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes regex string with special chars with %r(...)\" do\n    lexer = Lexer.new(%(%r(\\\\w)))\n\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('(', ')')\n    tester.next_string_token_should_be(\"\\\\w\")\n    tester.string_should_end_correctly\n  end\n\n  it \"lexes string with backslash\" do\n    lexer = Lexer.new(%(\"hello \\\\\\n    world\"1))\n    tester = LexerObjects::Strings.new(lexer)\n\n    tester.string_should_be_delimited_by('\"', '\"')\n    tester.next_string_token_should_be(\"hello \")\n    tester.next_string_token_should_be(\"world\")\n    tester.string_should_end_correctly(eof: false)\n    tester.next_token_should_be(:NUMBER)\n    tester.token_should_be_at(line: 2)\n    tester.should_have_reached_eof\n  end\nend\n"
  },
  {
    "path": "spec/compiler/lexer/location_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def t(kind : Crystal::Token::Kind)\n  kind\nend\n\nprivate def assert_token_column_number(lexer, type : Token::Kind, column_number)\n  token = lexer.next_token\n  token.type.should eq(type)\n  token.column_number.should eq(column_number)\nend\n\ndescribe \"Lexer: location\" do\n  it \"stores line numbers\" do\n    lexer = Lexer.new \"1\\n2\"\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :NEWLINE)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(2)\n  end\n\n  it \"stores column numbers\" do\n    lexer = Lexer.new \"1;  ident; def;\\n4\"\n    assert_token_column_number lexer, :NUMBER, 1\n    assert_token_column_number lexer, :OP_SEMICOLON, 2\n    assert_token_column_number lexer, :SPACE, 3\n    assert_token_column_number lexer, :IDENT, 5\n    assert_token_column_number lexer, :OP_SEMICOLON, 10\n    assert_token_column_number lexer, :SPACE, 11\n    assert_token_column_number lexer, :IDENT, 12\n    assert_token_column_number lexer, :OP_SEMICOLON, 15\n    assert_token_column_number lexer, :NEWLINE, 16\n    assert_token_column_number lexer, :NUMBER, 1\n  end\n\n  it \"works with filenames containing escaped quotes\" do\n    lexer = Lexer.new %q(#<loc:\"foo-\\\"bar\\\"-baz\",12,34>1)\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(12)\n    token.column_number.should eq(34)\n    token.filename.should eq(%(foo-\"bar\"-baz))\n  end\n\n  it \"works with filenames containing escaped backslash\" do\n    lexer = Lexer.new %q(#<loc:\"foo\\\\bar\\\\baz\",12,34>1)\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(12)\n    token.column_number.should eq(34)\n    token.filename.should eq(%q(foo\\bar\\baz))\n  end\n\n  it \"overrides location with pragma\" do\n    lexer = Lexer.new %(1 + #<loc:\"foo\",12,34>2)\n    lexer.filename = \"bar\"\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(1)\n    token.column_number.should eq(1)\n    token.filename.should eq(\"bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.line_number.should eq(1)\n    token.column_number.should eq(2)\n\n    token = lexer.next_token\n    token.type.should eq(t :OP_PLUS)\n    token.line_number.should eq(1)\n    token.column_number.should eq(3)\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.line_number.should eq(1)\n    token.column_number.should eq(4)\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(12)\n    token.column_number.should eq(34)\n    token.filename.should eq(\"foo\")\n  end\n\n  it \"pushes and pops its location\" do\n    lexer = Lexer.new %(#<loc:push>#<loc:\"foo\",12,34>1 + #<loc:pop>2)\n    lexer.filename = \"bar\"\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(12)\n    token.column_number.should eq(34)\n    token.filename.should eq(\"foo\")\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.line_number.should eq(12)\n    token.column_number.should eq(35)\n\n    token = lexer.next_token\n    token.type.should eq(t :OP_PLUS)\n    token.line_number.should eq(12)\n    token.column_number.should eq(36)\n\n    token = lexer.next_token\n    token.type.should eq(t :SPACE)\n    token.line_number.should eq(12)\n    token.column_number.should eq(37)\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(1)\n    token.column_number.should eq(44)\n    token.filename.should eq(\"bar\")\n  end\n\n  it \"uses two consecutive loc pragma \" do\n    lexer = Lexer.new %(1#<loc:\"foo\",12,34>#<loc:\"foo\",56,78>2)\n    lexer.filename = \"bar\"\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(1)\n    token.column_number.should eq(1)\n    token.filename.should eq(\"bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :NUMBER)\n    token.line_number.should eq(56)\n    token.column_number.should eq(78)\n    token.filename.should eq(\"foo\")\n  end\n\n  it \"assigns correct loc location to node\" do\n    exps = Parser.parse(%[(#<loc:\"foo.txt\",2,3>1 + 2)]).as(Expressions)\n    node = exps.expressions.first\n    location = node.location.not_nil!\n    location.line_number.should eq(2)\n    location.column_number.should eq(3)\n    location.filename.should eq(\"foo.txt\")\n  end\n\n  it \"parses var/call right after loc (#491)\" do\n    exps = Parser.parse(%[(#<loc:\"foo.txt\",2,3>msg)]).as(Expressions)\n    exp = exps.expressions.first.as(Call)\n    exp.name.should eq(\"msg\")\n  end\n\n  it \"locations in different files have no order\" do\n    loc1 = Location.new(\"file1\", 1, 1)\n    loc2 = Location.new(\"file2\", 2, 2)\n\n    (loc1 < loc2).should be_false\n    (loc1 <= loc2).should be_false\n\n    (loc1 > loc2).should be_false\n    (loc1 >= loc2).should be_false\n  end\n\n  it \"locations in same files are comparable based on line\" do\n    loc1 = Location.new(\"file1\", 1, 1)\n    loc2 = Location.new(\"file1\", 2, 1)\n    loc3 = Location.new(\"file1\", 1, 1)\n    (loc1 < loc2).should be_true\n    (loc1 <= loc2).should be_true\n    (loc1 <= loc3).should be_true\n\n    (loc2 > loc1).should be_true\n    (loc2 >= loc1).should be_true\n    (loc3 >= loc1).should be_true\n\n    (loc2 < loc1).should be_false\n    (loc2 <= loc1).should be_false\n\n    (loc1 > loc2).should be_false\n    (loc1 >= loc2).should be_false\n\n    (loc3 == loc1).should be_true\n  end\n\n  it \"locations with virtual files should be comparable\" do\n    loc1 = Location.new(\"file1\", 1, 1)\n    loc2 = Location.new(VirtualFile.new(Macro.new(\"macro\", [] of Arg, Nop.new), \"\", Location.new(\"f\", 1, 1)), 2, 1)\n    (loc1 < loc2).should be_false\n    (loc2 < loc1).should be_false\n  end\n\n  describe \"Location.parse\" do\n    it \"parses location from string\" do\n      Location.parse(\"foo:1:2\").should eq(Location.new(\"foo\", 1, 2))\n      Location.parse(\"foo:bar/baz:345:6789\").should eq(Location.new(\"foo:bar/baz\", 345, 6789))\n      Location.parse(%q(C:\\foo\\bar:1:2)).should eq(Location.new(%q(C:\\foo\\bar), 1, 2))\n    end\n\n    it \"raises ArgumentError if missing colon\" do\n      expect_raises(ArgumentError, \"cursor location must be file:line:column\") { Location.parse(\"foo\") }\n      expect_raises(ArgumentError, \"cursor location must be file:line:column\") { Location.parse(\"foo:1\") }\n    end\n\n    it \"raises ArgumentError if missing part\" do\n      expect_raises(ArgumentError, \"cursor location must be file:line:column\") { Location.parse(\":1:2\") }\n      expect_raises(ArgumentError, \"cursor location must be file:line:column\") { Location.parse(\"foo::2\") }\n      expect_raises(ArgumentError, \"cursor location must be file:line:column\") { Location.parse(\"foo:1:\") }\n    end\n\n    it \"raises ArgumentError if line number is invalid\" do\n      expect_raises(ArgumentError, \"line must be a positive integer, not a\") { Location.parse(\"foo:a:2\") }\n      expect_raises(ArgumentError, \"line must be a positive integer, not 0\") { Location.parse(\"foo:0:2\") }\n      expect_raises(ArgumentError, \"line must be a positive integer, not -1\") { Location.parse(\"foo:-1:2\") }\n    end\n\n    it \"raises ArgumentError if column number is invalid\" do\n      expect_raises(ArgumentError, \"column must be a positive integer, not a\") { Location.parse(\"foo:2:a\") }\n      expect_raises(ArgumentError, \"column must be a positive integer, not 0\") { Location.parse(\"foo:2:0\") }\n      expect_raises(ArgumentError, \"column must be a positive integer, not -1\") { Location.parse(\"foo:2:-1\") }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/loader/msvc_spec.cr",
    "content": "{% skip_file if !flag?(:msvc) %}\n\nrequire \"./spec_helper\"\nrequire \"../spec_helper\"\nrequire \"../../support/env\"\nrequire \"compiler/crystal/loader\"\n\ndescribe Crystal::Loader do\n  describe \".parse\" do\n    it \"parses directory paths\" do\n      loader = Crystal::Loader.parse([%q(/LIBPATH:C:\\foo\\bar), \"/LIBPATH:baz\"], search_paths: [] of String)\n      loader.search_paths.should eq [%q(C:\\foo\\bar), \"baz\"]\n    end\n\n    it \"prepends directory paths before default search paths\" do\n      loader = Crystal::Loader.parse(%w(/LIBPATH:foo /LIBPATH:bar), search_paths: %w(baz quux))\n      loader.search_paths.should eq %w(foo bar baz quux)\n    end\n\n    it \"parses file paths\" do\n      expect_raises(Crystal::Loader::LoadError, \"cannot find foobar.lib\") do\n        Crystal::Loader.parse([\"foobar.lib\"], search_paths: [] of String)\n      end\n    end\n  end\n\n  describe \".default_search_paths\" do\n    it \"LIB\" do\n      with_env \"LIB\": \"foo;;bar\" do\n        search_paths = Crystal::Loader.default_search_paths\n        search_paths.should eq [\"foo\", \"bar\"]\n      end\n    end\n  end\n\n  describe \"dll\" do\n    before_all do\n      FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n      build_c_dynlib(compiler_datapath(\"loader\", \"foo.c\"))\n    end\n\n    after_all do\n      FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n    end\n\n    describe \"#load_file?\" do\n      it \"finds function symbol\" do\n        loader = Crystal::Loader.new([] of String, dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader.load_file?(File.join(SPEC_CRYSTAL_LOADER_LIB_PATH, Crystal::Loader.library_filename(\"foo\"))).should be_true\n        loader.find_symbol?(\"foo\").should_not be_nil\n      ensure\n        loader.close_all if loader\n      end\n    end\n\n    describe \"#load_library?\" do\n      it \"library name\" do\n        loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader.load_library?(\"foo\").should be_true\n        loader.find_symbol?(\"foo\").should_not be_nil\n      ensure\n        loader.close_all if loader\n      end\n\n      it \"full path\" do\n        loader = Crystal::Loader.new([] of String, dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader.load_library?(File.join(SPEC_CRYSTAL_LOADER_LIB_PATH, Crystal::Loader.library_filename(\"foo\"))).should be_true\n        loader.find_symbol?(\"foo\").should_not be_nil\n      ensure\n        loader.close_all if loader\n      end\n\n      it \"does not implicitly find dependencies\" do\n        build_c_dynlib(compiler_datapath(\"loader\", \"bar.c\"))\n        loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader.load_library?(\"bar\").should be_true\n        loader.find_symbol?(\"bar\").should_not be_nil\n        loader.find_symbol?(\"foo\").should be_nil\n      ensure\n        loader.close_all if loader\n      end\n\n      it \"lookup in order\" do\n        build_c_dynlib(compiler_datapath(\"loader\", \"foo2.c\"))\n\n        help_loader1 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        help_loader1.load_library?(\"foo\").should be_true\n        foo_address = help_loader1.find_symbol?(\"foo\").should_not be_nil\n\n        help_loader2 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        help_loader2.load_library?(\"foo2\").should be_true\n        foo2_address = help_loader2.find_symbol?(\"foo\").should_not be_nil\n\n        foo_address.should_not eq foo2_address\n\n        loader1 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader1.load_library(\"foo\")\n        loader1.load_library(\"foo2\")\n\n        loader1.find_symbol?(\"foo\").should eq foo_address\n\n        loader2 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader2.load_library(\"foo2\")\n        loader2.load_library(\"foo\")\n\n        loader2.find_symbol?(\"foo\").should eq foo2_address\n      ensure\n        help_loader1.try &.close_all\n        help_loader2.try &.close_all\n\n        loader1.try &.close_all\n        loader2.try &.close_all\n      end\n    end\n\n    it \"does not find global symbols\" do\n      loader = Crystal::Loader.new([] of String, dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n      loader.find_symbol?(\"__crystal_main\").should be_nil\n    end\n\n    it \"validate that lib handles are properly closed\" do\n      loader = Crystal::Loader.new([] of String, dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n      expect_raises(Crystal::Loader::LoadError, \"undefined reference to `foo'\") do\n        loader.find_symbol(\"foo\")\n      end\n    end\n  end\n\n  describe \"dll_search_paths\" do\n    it \"supports an arbitrary path different from lib search path\" do\n      with_tempfile(\"loader-dll_search_paths\") do |path|\n        FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n        FileUtils.mkdir_p(path)\n\n        build_c_dynlib(compiler_datapath(\"loader\", \"foo.c\"))\n        File.rename(File.join(SPEC_CRYSTAL_LOADER_LIB_PATH, \"foo.dll\"), File.join(path, \"foo.dll\"))\n\n        loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String, dll_search_paths: [path])\n        loader.load_library?(\"foo\").should be_true\n      ensure\n        loader.try &.close_all\n\n        FileUtils.rm_rf(path)\n        FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n      end\n    end\n\n    it \"doesn't load DLLs outside dll_search_path or Windows' default search paths\" do\n      with_tempfile(\"loader-dll_search_paths\") do |path|\n        FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n        FileUtils.mkdir_p(path)\n\n        build_c_dynlib(compiler_datapath(\"loader\", \"foo.c\"))\n        File.rename(File.join(SPEC_CRYSTAL_LOADER_LIB_PATH, \"foo.dll\"), File.join(path, \"foo.dll\"))\n\n        loader1 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String, dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n        loader1.load_library?(\"foo\").should be_false\n        loader2 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n        loader2.load_library?(\"foo\").should be_false\n      ensure\n        loader2.try &.close_all\n        loader1.try &.close_all\n\n        FileUtils.rm_rf(path)\n        FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n      end\n    end\n  end\n\n  describe \"lib suffix\" do\n    before_all do\n      FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n    end\n\n    after_all do\n      FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n    end\n\n    it \"respects -dynamic\" do\n      build_c_dynlib(compiler_datapath(\"loader\", \"foo.c\"), lib_name: \"foo-dynamic\")\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n      loader.load_library?(\"foo\").should be_true\n    ensure\n      loader.close_all if loader\n    end\n\n    it \"ignores -static\" do\n      build_c_dynlib(compiler_datapath(\"loader\", \"foo.c\"), lib_name: \"bar-static\")\n      loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH], dll_search_paths: [SPEC_CRYSTAL_LOADER_LIB_PATH])\n      loader.load_library?(\"bar\").should be_false\n    ensure\n      loader.close_all if loader\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/loader/spec_helper.cr",
    "content": "require \"spec\"\n\nSPEC_CRYSTAL_LOADER_LIB_PATH = File.join(SPEC_TEMPFILE_PATH, \"loader\")\n\ndef build_c_dynlib(c_filename, *, lib_name = nil, target_dir = SPEC_CRYSTAL_LOADER_LIB_PATH)\n  o_filename = File.join(target_dir, Crystal::Loader.library_filename(lib_name || File.basename(c_filename, \".c\")))\n\n  {% if flag?(:msvc) %}\n    o_basename = o_filename.rchop(\".lib\")\n    `#{ENV[\"CC\"]? || \"cl.exe\"} /nologo /LD #{Process.quote(c_filename)} #{Process.quote(\"/Fo#{o_basename}\")} #{Process.quote(\"/Fe#{o_basename}\")}`\n  {% elsif flag?(:win32) && flag?(:gnu) %}\n    o_basename = o_filename.rchop(\".a\")\n    `#{ENV[\"CC\"]? || \"cc\"} -shared -fvisibility=hidden #{Process.quote(c_filename)} -o #{Process.quote(o_basename + \".dll\")} #{Process.quote(\"-Wl,--out-implib,#{o_basename}.a\")}`\n  {% else %}\n    `#{ENV[\"CC\"]? || \"cc\"} -shared -fvisibility=hidden #{Process.quote(c_filename)} -o #{Process.quote(o_filename)}`\n  {% end %}\n\n  raise \"BUG: failed to compile dynamic library\" unless $?.success?\nend\n"
  },
  {
    "path": "spec/compiler/loader/unix_spec.cr",
    "content": "{% skip_file if !flag?(:unix) || flag?(:wasm32) %}\n\nrequire \"./spec_helper\"\nrequire \"../spec_helper\"\nrequire \"../../support/env\"\nrequire \"compiler/crystal/loader\"\n\ndescribe Crystal::Loader do\n  describe \".parse\" do\n    it \"parses directory paths\" do\n      loader = Crystal::Loader.parse([\"-L\", \"/foo/bar/baz\", \"--library-path\", \"qux\"], search_paths: [] of String)\n      loader.search_paths.should eq [\"/foo/bar/baz\", \"qux\"]\n    end\n\n    it \"prepends directory paths before default search paths\" do\n      loader = Crystal::Loader.parse(%w(-Lfoo -Lbar), search_paths: %w(baz quux))\n      loader.search_paths.should eq %w(foo bar baz quux)\n    end\n\n    it \"parses static\" do\n      expect_raises(Crystal::Loader::LoadError, \"static libraries are not supported by Crystal's runtime loader\") do\n        Crystal::Loader.parse([\"-static\"], search_paths: [] of String)\n      end\n    end\n\n    it \"parses library names\" do\n      expect_raises(Crystal::Loader::LoadError, \"cannot find -lfoobar\") do\n        Crystal::Loader.parse([\"-l\", \"foobar\"], search_paths: [] of String)\n      end\n      expect_raises(Crystal::Loader::LoadError, \"cannot find -lfoobar\") do\n        Crystal::Loader.parse([\"--library\", \"foobar\"], search_paths: [] of String)\n      end\n    end\n\n    it \"parses file paths\" do\n      exc = expect_raises(Crystal::Loader::LoadError, /no such file|not found|cannot open/i) do\n        Crystal::Loader.parse([\"foobar.o\"], search_paths: [] of String)\n      end\n      exc.message.should contain File.join(Dir.current, \"foobar.o\")\n      exc = expect_raises(Crystal::Loader::LoadError, /no such file|not found|cannot open/i) do\n        Crystal::Loader.parse([\"-l\", \"foo/bar.o\"], search_paths: [] of String)\n      end\n      {% if flag?(:openbsd) %}\n        exc.message.should contain \"foo/bar.o\"\n      {% else %}\n        exc.message.should contain File.join(Dir.current, \"foo\", \"bar.o\")\n      {% end %}\n    end\n  end\n\n  describe \".default_search_paths\" do\n    it \"LD_LIBRARY_PATH\" do\n      with_env \"LD_LIBRARY_PATH\": \"ld1::ld2\", \"DYLD_LIBRARY_PATH\": nil do\n        search_paths = Crystal::Loader.default_search_paths\n        {% if flag?(:darwin) %}\n          search_paths[-2..].should eq [\"/usr/lib\", \"/usr/local/lib\"]\n        {% else %}\n          search_paths[0, 2].should eq [\"ld1\", \"ld2\"]\n          {% if flag?(:android) %}\n            search_paths[-2..].should eq [\"/vendor/lib\", \"/system/lib\"]\n          {% else %}\n            search_paths[-2..].should eq [\"/lib\", \"/usr/lib\"]\n          {% end %}\n        {% end %}\n      end\n    end\n\n    it \"DYLD_LIBRARY_PATH\" do\n      with_env \"DYLD_LIBRARY_PATH\": \"ld1::ld2\", \"LD_LIBRARY_PATH\": nil do\n        search_paths = Crystal::Loader.default_search_paths\n        {% if flag?(:darwin) %}\n          search_paths[0, 2].should eq [\"ld1\", \"ld2\"]\n          search_paths[-2..].should eq [\"/usr/lib\", \"/usr/local/lib\"]\n        {% elsif flag?(:android) %}\n          search_paths[-2..].should eq [\"/vendor/lib\", \"/system/lib\"]\n        {% else %}\n          search_paths[-2..].should eq [\"/lib\", \"/usr/lib\"]\n        {% end %}\n      end\n    end\n  end\n\n  describe \".read_ld_conf\" do\n    it \"basic\" do\n      ary = [] of String\n      Crystal::Loader.read_ld_conf(ary, compiler_datapath(\"loader/ld.so/basic.conf\"))\n      ary.should eq [\"foo/bar\", \"baz/qux\"]\n    end\n\n    it \"with include\" do\n      ary = [] of String\n      Crystal::Loader.read_ld_conf(ary, compiler_datapath(\"loader/ld.so/include.conf\"))\n      ary[0].should eq \"include/before\"\n      ary[-1].should eq \"include/after\"\n      # the order between basic.conf and basic2.conf is system-dependent\n      ary[1..-2].sort.should eq [\"baz/qux\", \"foo/bar\", \"foobar\"]\n    end\n  end\n\n  describe \"dynlib\" do\n    before_all do\n      FileUtils.mkdir_p(SPEC_CRYSTAL_LOADER_LIB_PATH)\n      build_c_dynlib(compiler_datapath(\"loader\", \"foo.c\"))\n    end\n\n    after_all do\n      FileUtils.rm_rf(SPEC_CRYSTAL_LOADER_LIB_PATH)\n    end\n\n    describe \"#load_file?\" do\n      it \"finds function symbol\" do\n        loader = Crystal::Loader.new([] of String)\n        loader.load_file?(File.join(SPEC_CRYSTAL_LOADER_LIB_PATH, Crystal::Loader.library_filename(\"foo\"))).should be_true\n        loader.find_symbol?(\"foo\").should_not be_nil\n      ensure\n        loader.close_all if loader\n      end\n    end\n\n    describe \"#load_library?\" do\n      it \"library name\" do\n        loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n        loader.load_library?(\"foo\").should be_true\n        loader.find_symbol?(\"foo\").should_not be_nil\n      ensure\n        loader.close_all if loader\n      end\n\n      it \"full path\" do\n        loader = Crystal::Loader.new([] of String)\n        loader.load_library?(File.join(SPEC_CRYSTAL_LOADER_LIB_PATH, Crystal::Loader.library_filename(\"foo\"))).should be_true\n        loader.find_symbol?(\"foo\").should_not be_nil\n      ensure\n        loader.close_all if loader\n      end\n\n      {% unless flag?(:darwin) %}\n        # FIXME: bar.c doesn't compile on darwin\n        it \"does not implicitly find dependencies\" do\n          build_c_dynlib(compiler_datapath(\"loader\", \"bar.c\"))\n          loader = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n          loader.load_library?(\"bar\").should be_true\n          loader.find_symbol?(\"bar\").should_not be_nil\n          loader.find_symbol?(\"foo\").should be_nil\n        ensure\n          loader.close_all if loader\n        end\n      {% end %}\n\n      it \"lookup in order\" do\n        build_c_dynlib(compiler_datapath(\"loader\", \"foo2.c\"))\n\n        help_loader1 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n        help_loader1.load_library?(\"foo\").should be_true\n        foo_address = help_loader1.find_symbol?(\"foo\").should_not be_nil\n\n        help_loader2 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n        help_loader2.load_library?(\"foo2\").should be_true\n        foo2_address = help_loader2.find_symbol?(\"foo\").should_not be_nil\n\n        foo_address.should_not eq foo2_address\n\n        loader1 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n        loader1.load_library(\"foo\")\n        loader1.load_library(\"foo2\")\n\n        loader1.find_symbol?(\"foo\").should eq foo_address\n\n        loader2 = Crystal::Loader.new([SPEC_CRYSTAL_LOADER_LIB_PATH] of String)\n        loader2.load_library(\"foo2\")\n        loader2.load_library(\"foo\")\n\n        loader2.find_symbol?(\"foo\").should eq foo2_address\n      ensure\n        help_loader1.try &.close_all\n        help_loader2.try &.close_all\n\n        loader1.try &.close_all\n        loader2.try &.close_all\n      end\n    end\n\n    it \"does not find global symbols\" do\n      loader = Crystal::Loader.new([] of String)\n      loader.find_symbol?(\"__crystal_main\").should be_nil\n    end\n\n    it \"validate that lib handles are properly closed\" do\n      loader = Crystal::Loader.new([] of String)\n      expect_raises(Crystal::Loader::LoadError, \"undefined reference to `foo'\") do\n        loader.find_symbol(\"foo\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/macro/macro_expander_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate STABLE_ABI_TYPES = [\n  Nil, Void, Int32, Float32, Bool, Char, Symbol, Pointer(Void),           # primitive scalars\n  StaticArray(Int32, 1), Tuple(Int32), NamedTuple(x: Int32), Proc(Int32), # primitive aggregates\n  Reference, String,\n  Union(Int32, Char), Union(Int32, String), Union(Nil, String), Union(Nil, String, Regex), Union(Nil, Proc(Int32)), # unions\n]\n\nprivate UNSTABLE_ABI_TYPES = [\n  Value, Struct, Int, Float,\n  Slice, Slice(Int32), Enumerable, Enumerable(Int32), Flags,\n  StaticArray(Slice(Int32), 1), Tuple(Slice(Int32)), NamedTuple(x: Slice(Int32)),\n  Union(Slice(Int32), Nil),\n]\n\ndescribe \"MacroExpander\" do\n  it \"expands simple macro\" do\n    assert_macro \"1 + 2\", \"1 + 2\"\n  end\n\n  it \"expands macro with string substitution\" do\n    assert_macro \"{{x}}\", %(\"hello\"), {x: \"hello\".string}\n  end\n\n  it \"expands macro with symbol substitution\" do\n    assert_macro \"{{x}}\", \":hello\", {x: \"hello\".symbol}\n  end\n\n  it \"expands macro with argument-less call substitution\" do\n    assert_macro \"{{x}}\", \"hello\", {x: \"hello\".call}\n  end\n\n  it \"expands macro with boolean\" do\n    assert_macro \"{{true}}\", \"true\"\n  end\n\n  it \"expands macro with integer\" do\n    assert_macro \"{{1}}\", \"1\"\n  end\n\n  it \"expands macro with char\" do\n    assert_macro \"{{'a'}}\", \"'a'\"\n  end\n\n  it \"expands macro with string\" do\n    assert_macro %({{\"hello\"}}), %(\"hello\")\n  end\n\n  it \"expands macro with symbol\" do\n    assert_macro %({{:foo}}), %(:foo)\n  end\n\n  it \"expands macro with nil\" do\n    assert_macro %({{nil}}), %(nil)\n  end\n\n  it \"expands macro with array\" do\n    assert_macro %({{[1, 2, 3]}}), %([1, 2, 3])\n  end\n\n  it \"expands macro with hash\" do\n    assert_macro %({{{:a => 1, :b => 2}}}), \"{:a => 1, :b => 2}\"\n  end\n\n  it \"expands macro with tuple\" do\n    assert_macro %({{{1, 2, 3}}}), %({1, 2, 3})\n  end\n\n  it \"expands macro with empty tuple\" do\n    assert_macro \"{{x}}\", \"::Tuple.new\", {x: TupleLiteral.new([] of ASTNode)}\n  end\n\n  it \"expands macro with empty named tuple\" do\n    assert_macro \"{{x}}\", \"::NamedTuple.new\", {x: NamedTupleLiteral.new([] of NamedTupleLiteral::Entry)}\n  end\n\n  it \"expands macro with range\" do\n    assert_macro %({{1..3}}), %(1..3)\n  end\n\n  it \"expands macro with string interpolation\" do\n    assert_macro \"{{ \\\"hello\\#{1 == 1}world\\\" }}\", %(\"hellotrueworld\")\n  end\n\n  it \"expands macro with var substitution\" do\n    assert_macro \"{{x}}\", \"hello\", {x: \"hello\".var}\n  end\n\n  it \"expands macro with or (1)\" do\n    assert_macro \"{{x || 1}}\", \"1\", {x: NilLiteral.new}\n  end\n\n  it \"expands macro with or (2)\" do\n    assert_macro \"{{x || 1}}\", \"hello\", {x: \"hello\".var}\n  end\n\n  it \"expands macro with and (1)\" do\n    assert_macro \"{{x && 1}}\", \"nil\", {x: NilLiteral.new}\n  end\n\n  it \"expands macro with and (2)\" do\n    assert_macro \"{{x && 1}}\", \"1\", {x: \"hello\".var}\n  end\n\n  describe \"if\" do\n    it \"expands macro with if when truthy\" do\n      assert_macro \"{%if true%}hello{%end%}\", \"hello\"\n    end\n\n    it \"expands macro with if when falsey\" do\n      assert_macro \"{%if false%}hello{%end%}\", \"\"\n    end\n\n    it \"expands macro with if else when falsey\" do\n      assert_macro \"{%if false%}hello{%else%}bye{%end%}\", \"bye\"\n    end\n\n    it \"expands macro with if with nop\" do\n      assert_macro \"{%if x%}hello{%else%}bye{%end%}\", \"bye\", {x: Nop.new}\n    end\n\n    it \"expands macro with if with not\" do\n      assert_macro \"{%if !true%}hello{%else%}bye{%end%}\", \"bye\"\n    end\n  end\n\n  describe \"for\" do\n    it \"expands macro with for over array literal\" do\n      assert_macro \"{%for e in x %}{{e}}{%end%}\", \"helloworld\", {x: ArrayLiteral.new([\"hello\".var, \"world\".var] of ASTNode)}\n    end\n\n    it \"expands macro with for over array literal with index\" do\n      assert_macro \"{%for e, i in x%}{{e}}{{i}}{%end%}\", \"hello0world1\", {x: ArrayLiteral.new([\"hello\".var, \"world\".var] of ASTNode)}\n    end\n\n    it \"expands macro with for over embedded array literal\" do\n      assert_macro \"{%for e in [1, 2]%}{{e}}{%end%}\", \"12\"\n    end\n\n    it \"expands macro with for over hash literal\" do\n      assert_macro \"{%for k, v in x%}{{k}}{{v}}{%end%}\", \"acbd\", {x: HashLiteral.new([HashLiteral::Entry.new(\"a\".var, \"c\".var), HashLiteral::Entry.new(\"b\".var, \"d\".var)])}\n    end\n\n    it \"expands macro with for over hash literal with index\" do\n      assert_macro \"{%for k, v, i in x%}{{k}}{{v}}{{i}}{%end%}\", \"ac0bd1\", {x: HashLiteral.new([HashLiteral::Entry.new(\"a\".var, \"c\".var), HashLiteral::Entry.new(\"b\".var, \"d\".var)])}\n    end\n\n    it \"expands macro with for over tuple literal\" do\n      assert_macro \"{%for e, i in x%}{{e}}{{i}}{%end%}\", \"a0b1\", {x: TupleLiteral.new([\"a\".var, \"b\".var] of ASTNode)}\n    end\n\n    it \"expands macro with for over range literal\" do\n      assert_macro \"{%for e in 1..3 %}{{e}}{%end%}\", \"123\"\n    end\n\n    it \"expands macro with for over range literal, evaluating elements\" do\n      assert_macro \"{%for e in x..y %}{{e}}{%end%}\", \"3456\", {x: 3.int32, y: 6.int32}\n    end\n\n    it \"expands macro with for over range literal, evaluating elements (exclusive)\" do\n      assert_macro \"{%for e in x...y %}{{e}}{%end%}\", \"345\", {x: 3.int32, y: 6.int32}\n    end\n  end\n\n  it \"does regular if\" do\n    assert_macro %({{1 == 2 ? 3 : 4}}), \"4\"\n  end\n\n  it \"does regular unless\" do\n    assert_macro %({{unless 1 == 2; 3; else; 4; end}}), \"3\"\n  end\n\n  it \"does not expand when macro expression is {% ... %}\" do\n    assert_macro %({% 1 %}), \"\"\n  end\n\n  it \"can't use `yield` outside a macro\" do\n    assert_error %({{yield}}), \"can't use `{{yield}}` outside a macro\"\n  end\n\n  it \"outputs invisible location pragmas\" do\n    node = 42.int32\n    node.location = Location.new \"foo.cr\", 10, 20\n    assert_macro %({{node}}), \"42\", {node: node}, expected_pragmas: {\n      0 => [\n        Lexer::LocPushPragma.new,\n        Lexer::LocSetPragma.new(\"foo.cr\", 10, 20),\n      ] of Lexer::LocPragma,\n      2 => [\n        Lexer::LocPopPragma.new,\n      ] of Lexer::LocPragma,\n    }\n  end\n\n  {% for op in [\"sizeof\".id, \"alignof\".id] %}\n    describe \"{{ op }}\" do\n      {% for type in STABLE_ABI_TYPES %}\n        it \"gets {{ op }} {{ type.id }}\" do\n          # we are not interested in the actual sizes or alignments here, these\n          # values are up to the codegen spec suite\n          assert_macro %(\\{{ {{ op }}({{ type.id }}).is_a?(NumberLiteral) }}), \"true\"\n        end\n      {% end %}\n\n      it \"gets {{ op }} enum\" do\n        assert_macro(\"\\{{ {{ op }}(Foo) == {{ op }}(Int16) }}\", \"true\") do |program|\n          program.types[\"Foo\"] = EnumType.new(program, program, \"Foo\", program.int16)\n          nil\n        end\n      end\n\n      it \"gets {{ op }} alias\" do\n        assert_macro(\"\\{{ {{ op }}(Foo) == {{ op }}(Int16) }}\", \"true\") do |program|\n          program.types[\"Foo\"] = AliasType.new(program, program, \"Foo\", Crystal::Path.global(\"Int16\"))\n          nil\n        end\n      end\n\n      it \"gets {{ op }} typedef\" do\n        assert_macro(\"\\{{ {{ op }}(Foo) == {{ op }}(Int16) }}\", \"true\") do |program|\n          program.types[\"Foo\"] = TypeDefType.new(program, program, \"Foo\", program.int16)\n          nil\n        end\n      end\n\n      it \"errors with typeof\" do\n        assert_error %(\\{{ {{ op }}(typeof(1)) }})\n      end\n\n      {% for type in UNSTABLE_ABI_TYPES %}\n        it \"errors with {{ type.id }}\" do\n          assert_error %(\\{{ {{ op }}({{ type.id }}) }}), \"argument to `{{ op }}` inside macros must be a type with a stable {{ op == \"sizeof\" ? \"size\".id : \"alignment\".id }}\"\n        end\n      {% end %}\n\n      it \"errors with alias of unstable type\" do\n        assert_error <<-CRYSTAL, \"argument to `{{ op }}` inside macros must be a type with a stable {{ op == \"sizeof\" ? \"size\".id : \"alignment\".id }}\"\n          struct Foo\n          end\n\n          alias Bar = Foo\n\n          \\{{ {{ op }}(Bar) }}\n          CRYSTAL\n      end\n\n      it \"errors with typedef of unstable type\" do\n        assert_error <<-CRYSTAL, \"argument to `{{ op }}` inside macros must be a type with a stable {{ op == \"sizeof\" ? \"size\".id : \"alignment\".id }}\"\n          lib Lib\n            struct Foo\n              x : Int32\n            end\n\n            type Bar = Foo\n          end\n\n          \\{{ {{ op }}(Lib::Bar) }}\n          CRYSTAL\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/compiler/macro/macro_methods_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"../../support/env\"\n\nprivate def declare_class_var(container : ClassVarContainer, name, var_type : Type, annotations = nil)\n  var = MetaTypeVar.new(name)\n  var.owner = container\n  var.type = var_type\n  var.annotations = annotations\n  var.bind_to var\n  var.freeze_type = var_type\n  container.class_vars[name] = var\nend\n\nprivate def exit_code_command(code)\n  {% if flag?(:win32) %}\n    %(cmd.exe /c \"exit #{code}\")\n  {% else %}\n    case code\n    when 0\n      \"true\"\n    when 1\n      \"false\"\n    else\n      \"/bin/sh -c 'exit #{code}'\"\n    end\n  {% end %}\nend\n\nprivate def shell_command(command)\n  {% if flag?(:win32) %}\n    \"cmd.exe /c #{Process.quote(command)}\"\n  {% else %}\n    \"/bin/sh -c #{Process.quote(command)}\"\n  {% end %}\nend\n\nprivate def newline\n  {% if flag?(:win32) %}\n    \"\\r\\n\"\n  {% else %}\n    \"\\n\"\n  {% end %}\nend\n\nmodule Crystal\n  describe Macro do\n    describe \"node methods\" do\n      describe \"location\" do\n        location = Location.new(\"foo.cr\", 1, 2)\n\n        it \"filename\" do\n          assert_macro \"{{x.filename}}\", %(\"foo.cr\"), {x: \"hello\".string.tap { |n| n.location = location }}\n        end\n\n        it \"line_number\" do\n          assert_macro \"{{x.line_number}}\", %(1), {x: \"hello\".string.tap { |n| n.location = location }}\n        end\n\n        it \"column number\" do\n          assert_macro \"{{x.column_number}}\", %(2), {x: \"hello\".string.tap { |n| n.location = location }}\n        end\n\n        it \"end line_number\" do\n          assert_macro \"{{x.end_line_number}}\", %(1), {x: \"hello\".string.tap { |n| n.end_location = location }}\n        end\n\n        it \"end column number\" do\n          assert_macro \"{{x.end_column_number}}\", %(2), {x: \"hello\".string.tap { |n| n.end_location = location }}\n        end\n      end\n\n      describe \"stringify\" do\n        it \"expands macro with stringify call on string\" do\n          assert_macro \"{{x.stringify}}\", \"\\\"\\\\\\\"hello\\\\\\\"\\\"\", {x: \"hello\".string}\n        end\n\n        it \"expands macro with stringify call on symbol\" do\n          assert_macro \"{{x.stringify}}\", %(\":hello\"), {x: \"hello\".symbol}\n        end\n\n        it \"expands macro with stringify call on call\" do\n          assert_macro \"{{x.stringify}}\", %(\"hello\"), {x: \"hello\".call}\n        end\n\n        it \"expands macro with stringify call on number\" do\n          assert_macro \"{{x.stringify}}\", %(\"1\"), {x: 1.int32}\n        end\n      end\n\n      describe \"symbolize\" do\n        it \"expands macro with symbolize call on string\" do\n          assert_macro \"{{x.symbolize}}\", \":\\\"\\\\\\\"hello\\\\\\\"\\\"\", {x: \"hello\".string}\n        end\n\n        it \"expands macro with symbolize call on symbol\" do\n          assert_macro \"{{x.symbolize}}\", \":\\\":hello\\\"\", {x: \"hello\".symbol}\n        end\n\n        it \"expands macro with symbolize call on id\" do\n          assert_macro \"{{x.id.symbolize}}\", \":hello\", {x: \"hello\".string}\n        end\n      end\n\n      describe \"id\" do\n        it \"expands macro with id call on string\" do\n          assert_macro \"{{x.id}}\", \"hello\", {x: \"hello\".string}\n        end\n\n        it \"expands macro with id call on symbol\" do\n          assert_macro \"{{x.id}}\", \"hello\", {x: \"hello\".symbol}\n        end\n\n        it \"expands macro with id call on char\" do\n          assert_macro \"{{x.id}}\", \"є\", {x: CharLiteral.new('є')}\n        end\n\n        it \"expands macro with id call on call\" do\n          assert_macro \"{{x.id}}\", \"hello\", {x: \"hello\".call}\n        end\n\n        it \"expands macro with id call on number\" do\n          assert_macro \"{{x.id}}\", %(1), {x: 1.int32}\n        end\n\n        it \"expands macro with id call on path\" do\n          assert_macro \"{{x.id}}\", %(Foo), {x: Path.new(\"Foo\")}\n        end\n\n        it \"expands macro with id call on global path\" do\n          assert_macro \"{{x.id}}\", %(::Foo), {x: Path.new(\"Foo\", global: true)}\n        end\n      end\n\n      it \"executes == on numbers (true)\" do\n        assert_macro \"{%if 1 == 1%}hello{%else%}bye{%end%}\", \"hello\"\n      end\n\n      it \"executes == on numbers (false)\" do\n        assert_macro \"{%if 1 == 2%}hello{%else%}bye{%end%}\", \"bye\"\n      end\n\n      it \"executes != on numbers (true)\" do\n        assert_macro \"{%if 1 != 2%}hello{%else%}bye{%end%}\", \"hello\"\n      end\n\n      it \"executes != on numbers (false)\" do\n        assert_macro \"{%if 1 != 1%}hello{%else%}bye{%end%}\", \"bye\"\n      end\n\n      it \"executes == on symbols (true) (#240)\" do\n        assert_macro \"{{:foo == :foo}}\", \"true\"\n      end\n\n      it \"executes == on symbols (false) (#240)\" do\n        assert_macro \"{{:foo == :bar}}\", \"false\"\n      end\n\n      describe \"class_name\" do\n        it \"executes class_name\" do\n          assert_macro \"{{:foo.class_name}}\", \"\\\"SymbolLiteral\\\"\"\n        end\n\n        it \"executes class_name\" do\n          assert_macro \"{{x.class_name}}\", \"\\\"MacroId\\\"\", {x: MacroId.new(\"hello\")}\n        end\n\n        it \"executes class_name\" do\n          assert_macro \"{{x.class_name}}\", \"\\\"StringLiteral\\\"\", {x: \"hello\".string}\n        end\n\n        it \"executes class_name\" do\n          assert_macro \"{{x.class_name}}\", \"\\\"SymbolLiteral\\\"\", {x: \"hello\".symbol}\n        end\n\n        it \"executes class_name\" do\n          assert_macro \"{{x.class_name}}\", \"\\\"NumberLiteral\\\"\", {x: 1.int32}\n        end\n\n        it \"executes class_name\" do\n          assert_macro \"{{x.class_name}}\", \"\\\"ArrayLiteral\\\"\", {x: ArrayLiteral.new([Path.new(\"Foo\"), Path.new(\"Bar\")] of ASTNode)}\n        end\n      end\n\n      describe \"#nil?\" do\n        it \"NumberLiteral\" do\n          assert_macro \"{{ 1.nil? }}\", \"false\"\n        end\n\n        it \"NilLiteral\" do\n          assert_macro \"{{ nil.nil? }}\", \"true\"\n        end\n\n        it \"Nop\" do\n          assert_macro \"{{ x.nil? }}\", \"true\", {x: Nop.new}\n        end\n      end\n\n      describe \"#is_a?\" do\n        it \"union argument\" do\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral) }}), \"true\", {x: 1.int32}\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral) }}), \"true\", {x: \"hello\".string}\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral) }}), \"false\", {x: \"hello\".symbol}\n\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral | SymbolLiteral) }}), \"true\", {x: 1.int32}\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral | SymbolLiteral) }}), \"true\", {x: \"hello\".string}\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral | SymbolLiteral) }}), \"true\", {x: \"hello\".symbol}\n          assert_macro %({{ x.is_a?(NumberLiteral | StringLiteral | SymbolLiteral) }}), \"false\", {x: \"hello\".call}\n        end\n\n        it \"union argument, mergeable\" do\n          assert_macro %({{ x.is_a?(NumberLiteral | ASTNode) }}), \"true\", {x: 1.int32}\n          assert_macro %({{ x.is_a?(NumberLiteral | ASTNode) }}), \"true\", {x: \"hello\".string}\n        end\n\n        it \"union argument, duplicate type\" do\n          assert_macro %({{ x.is_a?(NumberLiteral | NumberLiteral) }}), \"true\", {x: 1.int32}\n          assert_macro %({{ x.is_a?(NumberLiteral | NumberLiteral) }}), \"false\", {x: \"hello\".string}\n        end\n\n        it \"union argument, contains NoReturn\" do\n          assert_macro %({{ x.is_a?(NumberLiteral | NoReturn) }}), \"true\", {x: 1.int32}\n          assert_macro %({{ x.is_a?(NumberLiteral | NoReturn) }}), \"false\", {x: \"hello\".string}\n        end\n\n        it \"union argument, undefined types\" do\n          assert_macro %({{ x.is_a?(NumberLiteral | String) }}), \"true\", {x: 1.int32}\n          assert_macro %({{ x.is_a?(NumberLiteral | String) }}), \"false\", {x: \"hello\".string}\n          assert_macro %({{ x.is_a?(Int32 | String) }}), \"false\", {x: 1.int32}\n        end\n\n        it \"union argument, unimplemented types\" do\n          assert_macro %({{ x.is_a?(ClassDef) }}), \"true\", {x: ClassDef.new(\"Foo\".path)}\n          assert_macro %({{ x.is_a?(ModuleDef) }}), \"false\", {x: ClassDef.new(\"Foo\".path)}\n        end\n      end\n\n      describe \"#doc\" do\n        it \"returns an empty string if there are no docs on the node (wants_doc = false)\" do\n          assert_macro \"{{ x.doc }}\", %(\"\"), {x: Call.new(\"some_call\")}\n        end\n\n        it \"returns the call's docs if present (wants_doc = true)\" do\n          assert_macro \"{{ x.doc }}\", %(\"Some docs\"), {x: Call.new(\"some_call\").tap { |c| c.doc = \"Some docs\" }}\n        end\n\n        it \"returns a multiline comment\" do\n          assert_macro \"{{ x.doc }}\", %(\"Some\\\\nmulti\\\\nline\\\\ndocs\"), {x: Call.new(\"some_call\").tap { |c| c.doc = \"Some\\nmulti\\nline\\ndocs\" }}\n        end\n      end\n\n      describe \"#doc_comment\" do\n        it \"returns an empty MacroId if there are no docs on the node (wants_doc = false)\" do\n          assert_macro \"{{ x.doc_comment }}\", %(), {x: Call.new(\"some_call\")}\n        end\n\n        it \"returns the call's docs if present as a MacroId (wants_doc = true)\" do\n          assert_macro \"{{ x.doc_comment }}\", %(Some docs), {x: Call.new(\"some_call\").tap { |c| c.doc = \"Some docs\" }}\n        end\n\n        it \"ensures each newline has a `#` prefix\" do\n          assert_macro \"{{ x.doc_comment }}\", %(Some\\n# multi\\n# line\\n# docs), {x: Call.new(\"some_call\").tap { |c| c.doc = \"Some\\nmulti\\nline\\ndocs\" }}\n        end\n      end\n    end\n\n    describe \"number methods\" do\n      it \"executes > (true)\" do\n        assert_macro \"{%if 2 > 1%}hello{%else%}bye{%end%}\", \"hello\"\n      end\n\n      it \"executes > (false)\" do\n        assert_macro \"{%if 2 > 3%}hello{%else%}bye{%end%}\", \"bye\"\n      end\n\n      it \"executes >= (true)\" do\n        assert_macro \"{%if 1 >= 1%}hello{%else%}bye{%end%}\", \"hello\"\n      end\n\n      it \"executes >= (false)\" do\n        assert_macro \"{%if 2 >= 3%}hello{%else%}bye{%end%}\", \"bye\"\n      end\n\n      it \"executes < (true)\" do\n        assert_macro \"{%if 1 < 2%}hello{%else%}bye{%end%}\", \"hello\"\n      end\n\n      it \"executes < (false)\" do\n        assert_macro \"{%if 3 < 2%}hello{%else%}bye{%end%}\", \"bye\"\n      end\n\n      it \"executes <= (true)\" do\n        assert_macro \"{%if 1 <= 1%}hello{%else%}bye{%end%}\", \"hello\"\n      end\n\n      it \"executes <= (false)\" do\n        assert_macro \"{%if 3 <= 2%}hello{%else%}bye{%end%}\", \"bye\"\n      end\n\n      it \"executes <=>\" do\n        assert_macro \"{{1 <=> -1}}\", \"1\"\n      end\n\n      it \"executes <=> (returns nil)\" do\n        assert_macro \"{{0.0/0.0 <=> -1}}\", \"nil\"\n      end\n\n      it \"executes +\" do\n        assert_macro \"{{1 + 2}}\", \"3\"\n      end\n\n      it \"executes + and preserves type\" do\n        assert_macro \"{{1_u64 + 2_u64}}\", \"3_u64\"\n      end\n\n      it \"executes -\" do\n        assert_macro \"{{1 - 2}}\", \"-1\"\n      end\n\n      it \"executes *\" do\n        assert_macro \"{{2 * 3}}\", \"6\"\n      end\n\n      # MathInterpreter only works with Integer and left / right : Float\n      #\n      # it \"executes /\" do\n      #   assert_macro \"{{5 / 3}}\", \"1\"\n      # end\n\n      it \"executes //\" do\n        assert_macro \"{{5 // 3}}\", \"1\"\n      end\n\n      it \"executes %\" do\n        assert_macro \"{{5 % 3}}\", \"2\"\n      end\n\n      it \"preserves integer size (#10713)\" do\n        assert_macro \"{{ 3000000000u64 % 2 }}\", \"0_u64\"\n      end\n\n      it \"executes &\" do\n        assert_macro \"{{5 & 3}}\", \"1\"\n      end\n\n      it \"executes |\" do\n        assert_macro \"{{5 | 3}}\", \"7\"\n      end\n\n      it \"executes ^\" do\n        assert_macro \"{{5 ^ 3}}\", \"6\"\n      end\n\n      it \"executes **\" do\n        assert_macro \"{{2 ** 3}}\", \"8\"\n      end\n\n      it \"executes <<\" do\n        assert_macro \"{{1 << 2}}\", \"4\"\n      end\n\n      it \"executes >>\" do\n        assert_macro \"{{4 >> 2}}\", \"1\"\n      end\n\n      it \"executes + with float\" do\n        assert_macro \"{{1.5 + 2.6}}\", \"4.1\"\n      end\n\n      it \"executes unary +\" do\n        assert_macro \"{{+3}}\", \"+3\"\n      end\n\n      it \"executes unary -\" do\n        assert_macro \"{{-(3)}}\", \"-3\"\n        assert_macro \"{{-(3_i128)}}\", \"-3_i128\"\n      end\n\n      it \"executes unary ~\" do\n        assert_macro \"{{~1}}\", \"-2\"\n      end\n\n      it \"executes kind\" do\n        assert_macro \"{{-128i8.kind}}\", \":i8\"\n        assert_macro \"{{1e-123_f32.kind}}\", \":f32\"\n        assert_macro \"{{1.0.kind}}\", \":f64\"\n        assert_macro \"{{0xde7ec7ab1e_u64.kind}}\", \":u64\"\n        assert_macro \"{{1_u128.kind}}\", \":u128\"\n        assert_macro \"{{-20i128.kind}}\", \":i128\"\n      end\n\n      it \"#to_number\" do\n        assert_macro \"{{ 4_u8.to_number }}\", \"4\"\n        assert_macro \"{{ 2147483648.to_number }}\", \"2147483648\"\n        assert_macro \"{{ 1_f32.to_number }}\", \"1.0\"\n        assert_macro \"{{ 4_u128.to_number }}\", \"4\"\n        assert_macro \"{{ -20i128.to_number }}\", \"-20\"\n      end\n\n      it \"executes math operations using U/Int128\" do\n        assert_macro \"{{18446744073709551615_u128 + 1}}\", \"18446744073709551616_u128\"\n        assert_macro \"{{18446744073709551_i128 - 1_u128}}\", \"18446744073709550_i128\"\n        assert_macro \"{{18446744073709551615_u128 * 10}}\", \"184467440737095516150_u128\"\n        assert_macro \"{{18446744073709551610_u128 // 10}}\", \"1844674407370955161_u128\"\n      end\n    end\n\n    describe \"char methods\" do\n      it \"executes ord\" do\n        assert_macro %({{'a'.ord}}), %(97)\n        assert_macro %({{'龍'.ord}}), %(40845)\n      end\n\n      it \"executes zero?\" do\n        assert_macro \"{{0.zero?}}\", \"true\"\n        assert_macro \"{{1.zero?}}\", \"false\"\n\n        assert_macro \"{{0.0.zero?}}\", \"true\"\n        assert_macro \"{{0.1.zero?}}\", \"false\"\n      end\n    end\n\n    describe \"string methods\" do\n      it \"executes string == string\" do\n        assert_macro %({{\"foo\" == \"foo\"}}), %(true)\n        assert_macro %({{\"foo\" == \"bar\"}}), %(false)\n      end\n\n      it \"executes string != string\" do\n        assert_macro %({{\"foo\" != \"foo\"}}), %(false)\n        assert_macro %({{\"foo\" != \"bar\"}}), %(true)\n      end\n\n      it \"executes string * number\" do\n        assert_macro %({{\"odelay\" * 3}}), \"\\\"odelayodelayodelay\\\"\"\n      end\n\n      describe \"#split\" do\n        it \"works without arguments\" do\n          assert_macro %({{\"1 2 3\".split}}), %([\"1\", \"2\", \"3\"] of ::String)\n        end\n\n        it \"works with string argument\" do\n          assert_macro %({{\"1-2-3\".split(\"-\")}}), %([\"1\", \"2\", \"3\"] of ::String)\n        end\n\n        it \"works with char argument\" do\n          assert_macro %({{\"1-2-3\".split('-')}}), %([\"1\", \"2\", \"3\"] of ::String)\n        end\n\n        it \"works with regex argument\" do\n          assert_macro %({{\"123-456-789\".split(/-(.)/)}}), %([\"123\", \"4\", \"56\", \"7\", \"89\"] of ::String)\n        end\n      end\n\n      it \"executes strip\" do\n        assert_macro %({{\"  hello   \".strip}}), %(\"hello\")\n      end\n\n      it \"executes downcase\" do\n        assert_macro %({{\"HELLO\".downcase}}), %(\"hello\")\n      end\n\n      it \"executes upcase\" do\n        assert_macro %({{\"hello\".upcase}}), %(\"HELLO\")\n      end\n\n      it \"executes capitalize\" do\n        assert_macro %({{\"hello\".capitalize}}), %(\"Hello\")\n      end\n\n      it \"executes chars\" do\n        assert_macro %({{x.chars}}), %(['1', '2', '3'] of ::Char), {x: StringLiteral.new(\"123\")}\n      end\n\n      it \"executes lines\" do\n        assert_macro %({{x.lines}}), %([\"1\", \"2\", \"3\"] of ::String), {x: StringLiteral.new(\"1\\n2\\n3\")}\n      end\n\n      it \"executes size\" do\n        assert_macro %({{\"hello\".size}}), \"5\"\n      end\n\n      it \"executes count\" do\n        assert_macro %({{\"aabbcc\".count('a')}}), \"2\"\n      end\n\n      it \"executes empty\" do\n        assert_macro %({{\"hello\".empty?}}), \"false\"\n      end\n\n      it \"executes [] with inclusive range\" do\n        assert_macro %({{\"hello\"[1..-2]}}), %(\"ell\")\n      end\n\n      it \"executes [] with exclusive range\" do\n        assert_macro %({{\"hello\"[1...-2]}}), %(\"el\")\n      end\n\n      it \"executes [] with computed range\" do\n        assert_macro %({{\"hello\"[[1].size..-2]}}), %(\"ell\")\n      end\n\n      it \"executes [] with incomplete range\" do\n        assert_macro %({{\"hello\"[1..]}}), %(\"ello\")\n        assert_macro %({{\"hello\"[1..nil]}}), %(\"ello\")\n        assert_macro %({{\"hello\"[...3]}}), %(\"hel\")\n        assert_macro %({{\"hello\"[nil...3]}}), %(\"hel\")\n        assert_macro %({{\"hello\"[..]}}), %(\"hello\")\n        assert_macro %({{\"hello\"[nil..nil]}}), %(\"hello\")\n      end\n\n      it \"executes string chomp\" do\n        assert_macro %({{\"hello\\n\".chomp}}), %(\"hello\")\n      end\n\n      it \"executes string starts_with? char (true)\" do\n        assert_macro %({{\"hello\".starts_with?('h')}}), %(true)\n      end\n\n      it \"executes string starts_with? char (false)\" do\n        assert_macro %({{\"hello\".starts_with?('e')}}), %(false)\n      end\n\n      it \"executes string starts_with? string (true)\" do\n        assert_macro %({{\"hello\".starts_with?(\"hel\")}}), %(true)\n      end\n\n      it \"executes string starts_with? string (false)\" do\n        assert_macro %({{\"hello\".starts_with?(\"hi\")}}), %(false)\n      end\n\n      it \"executes string ends_with? char (true)\" do\n        assert_macro %({{\"hello\".ends_with?('o')}}), %(true)\n      end\n\n      it \"executes string ends_with? char (false)\" do\n        assert_macro %({{\"hello\".ends_with?('e')}}), %(false)\n      end\n\n      it \"executes string ends_with? string (true)\" do\n        assert_macro %({{\"hello\".ends_with?(\"llo\")}}), %(true)\n      end\n\n      it \"executes string ends_with? string (false)\" do\n        assert_macro %({{\"hello\".ends_with?(\"tro\")}}), %(false)\n      end\n\n      it \"executes string + string\" do\n        assert_macro %({{\"hello\" + \" world\"}}), %(\"hello world\")\n      end\n\n      it \"executes string + char\" do\n        assert_macro %({{\"hello\" + 'w'}}), %(\"hellow\")\n      end\n\n      it \"executes string =~ (false)\" do\n        assert_macro %({{\"hello\" =~ /hei/}}), %(false)\n      end\n\n      it \"executes string =~ (true)\" do\n        assert_macro %({{\"hello\" =~ /ell/}}), %(true)\n      end\n\n      it \"executes string > string\" do\n        assert_macro %({{\"fooa\" > \"foo\"}}), %(true)\n        assert_macro %({{\"foo\" > \"fooa\"}}), %(false)\n      end\n\n      it \"executes string > macroid\" do\n        assert_macro %({{\"fooa\" > \"foo\".id}}), %(true)\n        assert_macro %({{\"foo\" > \"fooa\".id}}), %(false)\n      end\n\n      it \"executes string < string\" do\n        assert_macro %({{\"fooa\" < \"foo\"}}), %(false)\n        assert_macro %({{\"foo\" < \"fooa\"}}), %(true)\n      end\n\n      it \"executes string < macroid\" do\n        assert_macro %({{\"fooa\" < \"foo\".id}}), %(false)\n        assert_macro %({{\"foo\" < \"fooa\".id}}), %(true)\n      end\n\n      it \"executes tr\" do\n        assert_macro %({{\"hello\".tr(\"e\", \"o\")}}), %(\"hollo\")\n      end\n\n      it \"executes gsub\" do\n        assert_macro %({{\"hello\".gsub(/e|o/, \"a\")}}), %(\"halla\")\n      end\n\n      it \"executes gsub with a block\" do\n        assert_macro %q({{ \"foo bar baz\".gsub(/ba./) { \"biz\" } }}), %(\"foo biz biz\")                                                                  # No block args\n        assert_macro %q({{ \"foo bar baz\".gsub(/ba./) { |match| match.upcase } }}), %(\"foo BAR BAZ\")                                                   # full matched string\n        assert_macro %q({{ \"Name: Alice, Name: Bob\".gsub(/Name: (\\w+)/) { |full, matches| \"User(#{matches[1].id})\" } }}), %(\"User(Alice), User(Bob)\") # single capture group\n        assert_macro %q({{ \"5x10, 3x7\".gsub(/(\\d+)x(\\d+)/) { |full, matches| \"#{matches[1].to_i * matches[2].to_i}\" } }}), %(\"50, 21\")                # multiple capture groups\n        assert_macro %q({{ \"bar baz\".gsub /bar (foo)?/ { |_, matches| matches[1].nil? ? \"\" : \"BUG\" } }}), %(\"baz\")                                    # Capture group no match\n        assert_macro %q({{ \"bar\".gsub /(foo)/ { \"STR\" } }}), %(\"bar\")                                                                                 # No match at all\n      end\n\n      it \"executes match\" do\n        assert_macro %({{ \"hello world\".match(/x/) }}), %(nil)\n        assert_macro %({{ \"hello world\".match(/o.*o/) }}), %({0 => \"o wo\"} of ::Int32 | ::String => ::String | ::Nil)\n        assert_macro %({{ \"hello world\".match(/(?:(x)|e)(?<name>\\\\S+)/) }}), %({0 => \"ello\", 1 => nil, \"name\" => \"llo\"} of ::Int32 | ::String => ::String | ::Nil)\n      end\n\n      it \"executes scan\" do\n        assert_macro %({{\"Crystal\".scan(/(Cr)(?<name1>y)(st)(?<name2>al)/)}}), %([{0 => \"Crystal\", 1 => \"Cr\", \"name1\" => \"y\", 3 => \"st\", \"name2\" => \"al\"} of ::Int32 | ::String => ::String | ::Nil] of ::Hash(::Int32 | ::String, ::String | ::Nil))\n        assert_macro %({{\"Crystal\".scan(/(Cr)?(stal)/)}}), %([{0 => \"stal\", 1 => nil, 2 => \"stal\"} of ::Int32 | ::String => ::String | ::Nil] of ::Hash(::Int32 | ::String, ::String | ::Nil))\n        assert_macro %({{\"Ruby\".scan(/Crystal/)}}), %([] of ::Hash(::Int32 | ::String, ::String | ::Nil))\n      end\n\n      it \"executes camelcase\" do\n        assert_macro %({{\"foo_bar\".camelcase}}), %(\"FooBar\")\n      end\n\n      it \"executes camelcase with lower\" do\n        assert_macro %({{\"foo_bar\".camelcase(lower: true)}}), %(\"fooBar\")\n      end\n\n      it \"executes camelcase with invalid lower arg type\" do\n        assert_macro_error %({{\"foo_bar\".camelcase(lower: 99)}}), \"named argument 'lower' to StringLiteral#camelcase must be a bool, not NumberLiteral\"\n      end\n\n      it \"executes underscore\" do\n        assert_macro %({{\"FooBar\".underscore}}), %(\"foo_bar\")\n      end\n\n      it \"executes titleize\" do\n        assert_macro %({{\"hello world\".titleize}}), %(\"Hello World\")\n      end\n\n      it \"executes to_utf16\" do\n        assert_macro %({{\"hello\".to_utf16}}), \"(::Slice(::UInt16).literal(104_u16, 101_u16, 108_u16, 108_u16, 111_u16, 0_u16))[0, 5]\"\n        assert_macro %({{\"TEST 😐🐙 ±∀ の\".to_utf16}}), \"(::Slice(::UInt16).literal(84_u16, 69_u16, 83_u16, 84_u16, 32_u16, 55357_u16, 56848_u16, 55357_u16, 56345_u16, 32_u16, 177_u16, 8704_u16, 32_u16, 12398_u16, 0_u16))[0, 14]\"\n      end\n\n      it \"executes to_i\" do\n        assert_macro %({{\"1234\".to_i}}), %(1234)\n      end\n\n      it \"executes to_i(base)\" do\n        assert_macro %({{\"1234\".to_i(16)}}), %(4660)\n      end\n\n      it \"executes string includes? char (true)\" do\n        assert_macro %({{\"spice\".includes?('s')}}), %(true)\n        assert_macro %({{\"spice\".includes?('p')}}), %(true)\n        assert_macro %({{\"spice\".includes?('i')}}), %(true)\n        assert_macro %({{\"spice\".includes?('c')}}), %(true)\n        assert_macro %({{\"spice\".includes?('e')}}), %(true)\n      end\n\n      it \"executes string includes? char (false)\" do\n        assert_macro %({{\"spice\".includes?('S')}}), %(false)\n        assert_macro %({{\"spice\".includes?(' ')}}), %(false)\n        assert_macro %({{\"spice\".includes?('!')}}), %(false)\n        assert_macro %({{\"spice\".includes?('b')}}), %(false)\n      end\n\n      it \"executes string includes? string (true)\" do\n        assert_macro %({{\"spice\".includes?(\"s\")}}), %(true)\n        assert_macro %({{\"spice\".includes?(\"e\")}}), %(true)\n        assert_macro %({{\"spice\".includes?(\"sp\")}}), %(true)\n        assert_macro %({{\"spice\".includes?(\"ce\")}}), %(true)\n        assert_macro %({{\"spice\".includes?(\"pic\")}}), %(true)\n      end\n\n      it \"executes string includes? string (false)\" do\n        assert_macro %({{\"spice\".includes?(\"Spi\")}}), %(false)\n        assert_macro %({{\"spice\".includes?(\" spi\")}}), %(false)\n        assert_macro %({{\"spice\".includes?(\"ce \")}}), %(false)\n        assert_macro %({{\"spice\".includes?(\"b\")}}), %(false)\n        assert_macro %({{\"spice\".includes?(\"spice \")}}), %(false)\n      end\n    end\n\n    describe \"macro id methods\" do\n      it \"forwards methods to string\" do\n        assert_macro %({{x.ends_with?(\"llo\")}}), %(true), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.ends_with?(\"tro\")}}), %(false), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.starts_with?(\"hel\")}}), %(true), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.chomp}}), %(hello), {x: MacroId.new(\"hello\\n\")}\n        assert_macro %({{x.upcase}}), %(HELLO), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.titleize}}), %(Hello World), {x: MacroId.new(\"hello world\")}\n        assert_macro %({{x.includes?(\"el\")}}), %(true), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.includes?(\"he\")}}), %(true), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.includes?(\"EL\")}}), %(false), {x: MacroId.new(\"hello\")}\n        assert_macro %({{x.includes?(\"cat\")}}), %(false), {x: MacroId.new(\"hello\")}\n      end\n\n      it \"compares with string\" do\n        assert_macro %({{x == \"foo\"}}), %(true), {x: MacroId.new(\"foo\")}\n        assert_macro %({{\"foo\" == x}}), %(true), {x: MacroId.new(\"foo\")}\n\n        assert_macro %({{x == \"bar\"}}), %(false), {x: MacroId.new(\"foo\")}\n        assert_macro %({{\"bar\" == x}}), %(false), {x: MacroId.new(\"foo\")}\n\n        assert_macro %({{x != \"foo\"}}), %(false), {x: MacroId.new(\"foo\")}\n        assert_macro %({{\"foo\" != x}}), %(false), {x: MacroId.new(\"foo\")}\n\n        assert_macro %({{x != \"bar\"}}), %(true), {x: MacroId.new(\"foo\")}\n        assert_macro %({{\"bar\" != x}}), %(true), {x: MacroId.new(\"foo\")}\n      end\n\n      it \"compares with symbol\" do\n        assert_macro %({{x == :foo}}), %(true), {x: MacroId.new(\"foo\")}\n        assert_macro %({{:foo == x}}), %(true), {x: MacroId.new(\"foo\")}\n\n        assert_macro %({{x == :bar}}), %(false), {x: MacroId.new(\"foo\")}\n        assert_macro %({{:bar == x}}), %(false), {x: MacroId.new(\"foo\")}\n\n        assert_macro %({{x != :foo}}), %(false), {x: MacroId.new(\"foo\")}\n        assert_macro %({{:foo != x}}), %(false), {x: MacroId.new(\"foo\")}\n\n        assert_macro %({{x != :bar}}), %(true), {x: MacroId.new(\"foo\")}\n        assert_macro %({{:bar != x}}), %(true), {x: MacroId.new(\"foo\")}\n      end\n    end\n\n    describe \"symbol methods\" do\n      it \"forwards methods to string\" do\n        assert_macro %({{x.ends_with?(\"llo\")}}), %(true), {x: \"hello\".symbol}\n        assert_macro %({{x.ends_with?(\"tro\")}}), %(false), {x: \"hello\".symbol}\n        assert_macro %({{x.starts_with?(\"hel\")}}), %(true), {x: \"hello\".symbol}\n        assert_macro %({{x.chomp}}), %(:hello), {x: SymbolLiteral.new(\"hello\\n\")}\n        assert_macro %({{x.upcase}}), %(:HELLO), {x: \"hello\".symbol}\n        assert_macro %({{x.titleize}}), %(:\"Hello World\"), {x: \"hello world\".symbol}\n        assert_macro %({{x.includes?(\"el\")}}), %(true), {x: \"hello\".symbol}\n        assert_macro %({{x.includes?(\"he\")}}), %(true), {x: \"hello\".symbol}\n        assert_macro %({{x.includes?(\"EL\")}}), %(false), {x: \"hello\".symbol}\n        assert_macro %({{x.includes?(\"cat\")}}), %(false), {x: \"hello\".symbol}\n      end\n\n      it \"executes symbol == symbol\" do\n        assert_macro %({{:foo == :foo}}), %(true)\n        assert_macro %({{:foo == :bar}}), %(false)\n      end\n\n      it \"executes symbol != symbol\" do\n        assert_macro %({{:foo != :foo}}), %(false)\n        assert_macro %({{:foo != :bar}}), %(true)\n      end\n    end\n\n    describe \"and methods\" do\n      it \"executes left\" do\n        assert_macro %({{x.left}}), %(1), {x: And.new(1.int32, 2.int32)}\n      end\n\n      it \"executes right\" do\n        assert_macro %({{x.right}}), %(2), {x: And.new(1.int32, 2.int32)}\n      end\n    end\n\n    describe \"or methods\" do\n      it \"executes left\" do\n        assert_macro %({{x.left}}), %(1), {x: Or.new(1.int32, 2.int32)}\n      end\n\n      it \"executes right\" do\n        assert_macro %({{x.right}}), %(2), {x: Or.new(1.int32, 2.int32)}\n      end\n    end\n\n    describe ArrayLiteral do\n      it \"executes index 0\" do\n        assert_macro %({{[1, 2, 3][0]}}), \"1\"\n      end\n\n      it \"executes index 1\" do\n        assert_macro %({{[1, 2, 3][1]}}), \"2\"\n      end\n\n      it \"executes index out of bounds\" do\n        assert_macro %({{[1, 2, 3][3]}}), \"nil\"\n      end\n\n      it \"executes size\" do\n        assert_macro %({{[1, 2, 3].size}}), \"3\"\n      end\n\n      it \"executes empty?\" do\n        assert_macro %({{[1, 2, 3].empty?}}), \"false\"\n      end\n\n      it \"executes identify\" do\n        assert_macro %({{\"A::B\".identify}}), \"\\\"A__B\\\"\"\n        assert_macro %({{\"A\".identify}}), \"\\\"A\\\"\"\n      end\n\n      it \"executes join\" do\n        assert_macro %({{[1, 2, 3].join \", \"}}), %(\"1, 2, 3\")\n      end\n\n      it \"executes join with strings\" do\n        assert_macro %({{[\"a\", \"b\"].join \", \"}}), %(\"a, b\")\n      end\n\n      it \"executes map\" do\n        assert_macro %({{[1, 2, 3].map { |e| e == 2 }}}), \"[false, true, false]\"\n      end\n\n      it \"executes *\" do\n        assert_macro %({{[\"na\"] * 5}}), %([\"na\", \"na\", \"na\", \"na\", \"na\"])\n      end\n\n      it \"executes reduce with no initial value\" do\n        assert_macro %({{[1, 2, 3].reduce { |acc, val| acc * val }}}), \"6\"\n      end\n\n      it \"executes reduce with initial value\" do\n        assert_macro %({{[1, 2, 3].reduce(4) { |acc, val| acc * val }}}), \"24\"\n        assert_macro %({{[1, 2, 3].reduce([] of NumberLiteral) { |acc, val| acc = [val]+acc }}}), \"[3, 2, 1]\"\n      end\n\n      it \"executes map with constants\" do\n        assert_macro %({{x.map { |e| e.id }}}), \"[Foo, Bar]\", {x: ArrayLiteral.new([Path.new(\"Foo\"), Path.new(\"Bar\")] of ASTNode)}\n      end\n\n      it \"executes map with arg\" do\n        assert_macro %({{x.map { |e| e.id }}}), \"[hello]\", {x: ArrayLiteral.new([\"hello\".call] of ASTNode)}\n      end\n\n      describe \"#map_with_index\" do\n        context \"with both arguments\" do\n          it \"returns the resulting array\" do\n            assert_macro %({{[1, 2, 3].map_with_index { |e, idx| e == 2 || idx <= 1 }}}), %([true, true, false])\n          end\n        end\n\n        context \"without the index argument\" do\n          it \"returns the resulting array\" do\n            assert_macro %({{[1, 2, 3].map_with_index { |e| e }}}), %([1, 2, 3])\n          end\n        end\n\n        context \"without the element argument\" do\n          it \"returns the resulting array\" do\n            assert_macro %({{[1, 2, 3].map_with_index { |_, idx| idx }}}), %([0, 1, 2])\n          end\n        end\n\n        context \"without either argument\" do\n          it \"returns the resulting array\" do\n            assert_macro %({{[1, 2, 3].map_with_index { 7 }}}), %([7, 7, 7])\n          end\n        end\n      end\n\n      it \"#each\" do\n        assert_macro(\n          %({% begin %}{% values = [] of Nil %}{% [1, 2, 3].each { |v| values << v } %}{{values}}{% end %}),\n          %([1, 2, 3])\n        )\n      end\n\n      describe \"#each_with_index\" do\n        context \"with both arguments\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% [1, 2, 3].each_with_index { |v, idx| values << (v + idx) } %}{{values}}{% end %}),\n              %([1, 3, 5])\n            )\n          end\n        end\n\n        context \"without the index argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% [1, 2, 3].each_with_index { |v| values << v } %}{{values}}{% end %}),\n              %([1, 2, 3])\n            )\n          end\n        end\n\n        context \"without the element argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% [1, 2, 3].each_with_index { |_, idx| values << idx } %}{{values}}{% end %}),\n              %([0, 1, 2])\n            )\n          end\n        end\n\n        context \"without either argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% [1, 2, 3].each_with_index { values << 7 } %}{{values}}{% end %}),\n              %([7, 7, 7])\n            )\n          end\n        end\n      end\n\n      it \"executes select\" do\n        assert_macro %({{[1, 2, 3].select { |e| e == 1 }}}), \"[1]\"\n      end\n\n      it \"executes reject\" do\n        assert_macro %({{[1, 2, 3].reject { |e| e == 1 }}}), \"[2, 3]\"\n      end\n\n      it \"executes find (finds)\" do\n        assert_macro %({{[1, 2, 3].find { |e| e == 2 }}}), \"2\"\n      end\n\n      it \"executes find (doesn't find)\" do\n        assert_macro %({{[1, 2, 3].find { |e| e == 4 }}}), \"nil\"\n      end\n\n      it \"executes any? (true)\" do\n        assert_macro %({{[1, 2, 3].any? { |e| e == 1 }}}), \"true\"\n      end\n\n      it \"executes any? (false)\" do\n        assert_macro %({{[1, 2, 3].any? { |e| e == 4 }}}), \"false\"\n      end\n\n      it \"executes all? (true)\" do\n        assert_macro %({{[1, 1, 1].all? { |e| e == 1 }}}), \"true\"\n      end\n\n      it \"executes all? (false)\" do\n        assert_macro %({{[1, 2, 1].all? { |e| e == 1 }}}), \"false\"\n      end\n\n      it \"executes first\" do\n        assert_macro %({{[1, 2, 3].first}}), \"1\"\n      end\n\n      it \"executes last\" do\n        assert_macro %({{[1, 2, 3].last}}), \"3\"\n      end\n\n      it \"executes splat\" do\n        assert_macro %({{[1, 2, 3].splat}}), \"1, 2, 3\"\n      end\n\n      it \"executes splat with symbols and strings\" do\n        assert_macro %({{[:foo, \"hello\", 3].splat}}), %(:foo, \"hello\", 3)\n      end\n\n      it \"executes splat with splat\" do\n        assert_macro %({{*[1, 2, 3]}}), \"1, 2, 3\"\n      end\n\n      it \"executes is_a?\" do\n        assert_macro %({{[1, 2, 3].is_a?(ArrayLiteral)}}), \"true\"\n        assert_macro %({{[1, 2, 3].is_a?(ASTNode)}}), \"true\"\n        assert_macro %({{[1, 2, 3].is_a?(NumberLiteral)}}), \"false\"\n      end\n\n      it \"creates an array literal with a var\" do\n        assert_macro %({% a = [x] %}{{a[0]}}), \"1\", {x: 1.int32}\n      end\n\n      it \"executes sort with numbers\" do\n        assert_macro %({{[3, 2, 1].sort}}), \"[1, 2, 3]\"\n      end\n\n      it \"executes sort with strings\" do\n        assert_macro %({{[\"c\", \"b\", \"a\"].sort}}), %([\"a\", \"b\", \"c\"])\n      end\n\n      it \"executes sort with ids\" do\n        assert_macro %({{[\"c\".id, \"b\".id, \"a\".id].sort}}), %([a, b, c])\n      end\n\n      it \"executes sort with ids and strings\" do\n        assert_macro %({{[\"c\".id, \"b\", \"a\".id].sort}}), %([a, \"b\", c])\n      end\n\n      it \"executes sort_by\" do\n        assert_macro %({{[\"abc\", \"a\", \"ab\"].sort_by { |x| x.size }}}), %([\"a\", \"ab\", \"abc\"])\n      end\n\n      it \"calls block exactly once for each element in #sort_by\" do\n        assert_macro <<-CRYSTAL, %(5)\n          {{ (i = 0; [\"abc\", \"a\", \"ab\", \"abcde\", \"abcd\"].sort_by { i += 1 }; i) }}\n          CRYSTAL\n      end\n\n      it \"executes uniq\" do\n        assert_macro %({{[1, 1, 1, 2, 3, 1, 2, 3, 4].uniq}}), %([1, 2, 3, 4])\n      end\n\n      it \"executes unshift\" do\n        assert_macro %({% x = [1]; x.unshift(2); %}{{x}}), %([2, 1])\n      end\n\n      it \"executes push\" do\n        assert_macro %({% x = [1]; x.push(2); x << 3 %}{{x}}), %([1, 2, 3])\n      end\n\n      it \"executes includes?\" do\n        assert_macro %({{ [1, 2, 3].includes?(1) }}), %(true)\n        assert_macro %({{ [1, 2, 3].includes?(4) }}), %(false)\n      end\n\n      describe \"#+\" do\n        context \"with TupleLiteral argument\" do\n          it \"concatenates the literals into an ArrayLiteral\" do\n            assert_macro %({{ [1, 2] + {3, 4, 5} }}), %([1, 2, 3, 4, 5])\n          end\n        end\n\n        context \"with ArrayLiteral argument\" do\n          it \"concatenates the literals into an ArrayLiteral\" do\n            assert_macro %({{ [1, 2] + [3, 4, 5] }}), %([1, 2, 3, 4, 5])\n          end\n        end\n      end\n\n      describe \"#-\" do\n        context \"with TupleLiteral argument\" do\n          it \"removes the elements in RHS from LHS into an ArrayLiteral\" do\n            assert_macro %({{ [1, 2, 3, 4] - {1, 3, 5} }}), %([2, 4])\n          end\n        end\n\n        context \"with ArrayLiteral argument\" do\n          it \"removes the elements in RHS from LHS into an ArrayLiteral\" do\n            assert_macro %({{ [1, 2, 3, 4] - [1, 3, 5] }}), %([2, 4])\n          end\n        end\n      end\n\n      it \"executes [] with range\" do\n        assert_macro %({{ [1, 2, 3, 4][1...-1] }}), %([2, 3])\n      end\n\n      it \"executes [] with computed range\" do\n        assert_macro %({{ [1, 2, 3, 4][[1].size...-1] }}), %([2, 3])\n      end\n\n      it \"executes [] with incomplete range\" do\n        assert_macro %({{ [1, 2, 3, 4][1..] }}), %([2, 3, 4])\n        assert_macro %({{ [1, 2, 3, 4][1..nil] }}), %([2, 3, 4])\n        assert_macro %({{ [1, 2, 3, 4][...2] }}), %([1, 2])\n        assert_macro %({{ [1, 2, 3, 4][nil...2] }}), %([1, 2])\n        assert_macro %({{ [1, 2, 3, 4][..] }}), %([1, 2, 3, 4])\n        assert_macro %({{ [1, 2, 3, 4][nil..nil] }}), %([1, 2, 3, 4])\n      end\n\n      it \"executes [] with range, start is out of bounds\" do\n        assert_macro %({{ [1, 2, 3, 4][5..] }}), %(nil)\n        assert_macro %({{ [1, 2, 3, 4][-5..] }}), %(nil)\n      end\n\n      it \"executes [] with two numbers\" do\n        assert_macro %({{ [1, 2, 3, 4, 5][1, 3] }}), %([2, 3, 4])\n      end\n\n      it \"executes [] with two numbers, start is out of bounds\" do\n        assert_macro %({{ [1, 2, 3, 4][5, 1] }}), %(nil)\n        assert_macro %({{ [1, 2, 3, 4][-5, 4] }}), %(nil)\n      end\n\n      it \"executes []=\" do\n        assert_macro %({% a = [0]; a[0] = 2 %}{{a[0]}}), \"2\"\n      end\n\n      it \"executes of\" do\n        assert_macro %({{ x.of }}), %(Int64), {x: ArrayLiteral.new([] of ASTNode, of: Path.new(\"Int64\"))}\n      end\n\n      it \"executes of (nop)\" do\n        assert_macro %({{ [1, 2, 3].of }}), %()\n      end\n\n      it \"executes type\" do\n        assert_macro %({{ x.type }}), %(Deque), {x: ArrayLiteral.new([] of ASTNode, name: Path.new(\"Deque\"))}\n      end\n\n      it \"executes type (nop)\" do\n        assert_macro %({{ [1, 2, 3].type }}), %()\n      end\n    end\n\n    describe HashLiteral do\n      it \"executes size\" do\n        assert_macro %({{{:a => 1, :b => 3}.size}}), \"2\"\n      end\n\n      it \"executes empty?\" do\n        assert_macro %({{{:a => 1}.empty?}}), \"false\"\n      end\n\n      it \"executes []\" do\n        assert_macro %({{{:a => 1}[:a]}}), \"1\"\n      end\n\n      it \"executes [] not found\" do\n        assert_macro %({{{:a => 1}[:b]}}), \"nil\"\n      end\n\n      it \"executes keys\" do\n        assert_macro %({{{:a => 1, :b => 2}.keys}}), \"[:a, :b]\"\n      end\n\n      it \"executes values\" do\n        assert_macro %({{{:a => 1, :b => 2}.values}}), \"[1, 2]\"\n      end\n\n      it \"executes map\" do\n        assert_macro %({{{:a => 1, :b => 2}.map {|k, v| k == :a && v == 1}}}), \"[true, false]\"\n      end\n\n      it \"executes is_a?\" do\n        assert_macro %({{{:a => 1}.is_a?(HashLiteral)}}), \"true\"\n        assert_macro %({{{:a => 1}.is_a?(ASTNode)}}), \"true\"\n        assert_macro %({{{:a => 1}.is_a?(RangeLiteral)}}), \"false\"\n      end\n\n      it \"executes []=\" do\n        assert_macro %({% a = {} of Nil => Nil; a[1] = 2 %}{{a[1]}}), \"2\"\n      end\n\n      it \"creates a hash literal with a var\" do\n        assert_macro %({% a = {:a => x} %}{{a[:a]}}), \"1\", {x: 1.int32}\n      end\n\n      it \"executes to_a\" do\n        assert_macro %({{{:a => 1, :b => 3}.to_a}}), \"[{:a, 1}, {:b, 3}]\"\n      end\n\n      it \"executes of_key\" do\n        of = HashLiteral::Entry.new(Path.new(\"String\"), Path.new(\"UInt8\"))\n        assert_macro %({{ x.of_key }}), %(String), {x: HashLiteral.new([] of HashLiteral::Entry, of: of)}\n      end\n\n      it \"executes of_key (nop)\" do\n        assert_macro %({{ {'z' => 6, 'a' => 9}.of_key }}), %()\n      end\n\n      it \"executes of_value\" do\n        of = HashLiteral::Entry.new(Path.new(\"String\"), Path.new(\"UInt8\"))\n        assert_macro %({{ x.of_value }}), %(UInt8), {x: HashLiteral.new([] of HashLiteral::Entry, of: of)}\n      end\n\n      it \"executes of_value (nop)\" do\n        assert_macro %({{ {'z' => 6, 'a' => 9}.of_value }}), %()\n      end\n\n      it \"executes has_key?\" do\n        assert_macro %({{ {'z' => 6, 'a' => 9}.has_key?('z') }}), %(true)\n        assert_macro %({{ {'z' => 6, 'a' => 9}.has_key?('x') }}), %(false)\n        assert_macro %({{ {'z' => nil, 'a' => 9}.has_key?('z') }}), %(true)\n      end\n\n      it \"executes type\" do\n        assert_macro %({{ x.type }}), %(Headers), {x: HashLiteral.new([] of HashLiteral::Entry, name: Path.new(\"Headers\"))}\n      end\n\n      it \"executes type (nop)\" do\n        assert_macro %({{ {'z' => 6, 'a' => 9}.type }}), %()\n      end\n\n      it \"executes double splat\" do\n        assert_macro %({{**{1 => 2, 3 => 4}}}), \"1 => 2, 3 => 4\"\n      end\n\n      it \"executes double splat\" do\n        assert_macro %({{{1 => 2, 3 => 4}.double_splat}}), \"1 => 2, 3 => 4\"\n      end\n\n      it \"executes double splat with arg\" do\n        assert_macro %({{{1 => 2, 3 => 4}.double_splat(\", \")}}), \"1 => 2, 3 => 4, \"\n      end\n\n      describe \"#each\" do\n        context \"with both arguments\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {\"k1\" => \"v1\", \"k2\" => \"v2\"}.each { |k, v| values << {k, v} } %}{{values}}{% end %}),\n              %([{\"k1\", \"v1\"}, {\"k2\", \"v2\"}])\n            )\n          end\n        end\n\n        context \"without the value argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {\"k1\" => \"v1\", \"k2\" => \"v2\"}.each { |k| values << k } %}{{values}}{% end %}),\n              %([\"k1\", \"k2\"])\n            )\n          end\n        end\n\n        context \"without the key argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {\"k1\" => \"v1\", \"k2\" => \"v2\"}.each { |_, v| values << v } %}{{values}}{% end %}),\n              %([\"v1\", \"v2\"])\n            )\n          end\n        end\n\n        context \"without either argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {\"k1\" => \"v1\", \"k2\" => \"v2\"}.each { values << {\"k3\", \"v3\"} } %}{{values}}{% end %}),\n              %([{\"k3\", \"v3\"}, {\"k3\", \"v3\"}])\n            )\n          end\n        end\n      end\n\n      it \"executes select with block\" do\n        assert_macro %({{{:a => 1, :b => 2, :c => 3}.select { |k, v| v > 1 }}}), \"{:b => 2, :c => 3}\"\n      end\n\n      it \"executes select with keys\" do\n        assert_macro %({{{:a => 1, :b => 2, \"c\" => 3}.select(:a, \"c\")}}), \"{:a => 1, \\\"c\\\" => 3}\"\n      end\n\n      it \"executes reject with block\" do\n        assert_macro %({{{:a => 1, :b => 2, :c => 3}.reject { |k, v| v > 1 }}}), \"{:a => 1}\"\n      end\n\n      it \"executes reject with keys\" do\n        assert_macro %({{{:a => 1, :b => 2, :c => 3}.reject(:b)}}), \"{:a => 1, :c => 3}\"\n      end\n    end\n\n    describe NamedTupleLiteral do\n      it \"executes size\" do\n        assert_macro %({{{a: 1, b: 3}.size}}), \"2\"\n      end\n\n      it \"executes empty?\" do\n        assert_macro %({{{a: 1}.empty?}}), \"false\"\n      end\n\n      it \"executes []\" do\n        assert_macro %({{{a: 1}[:a]}}), \"1\"\n        assert_macro %({{{a: 1}[\"a\"]}}), \"1\"\n      end\n\n      it \"executes [] not found\" do\n        assert_macro %({{{a: 1}[:b]}}), \"nil\"\n        assert_macro %({{{a: 1}[\"b\"]}}), \"nil\"\n      end\n\n      it \"executes [] with invalid key type\" do\n        assert_macro_error %({{{a: 1}[true]}}), \"argument to [] must be a symbol or string, not BoolLiteral\"\n      end\n\n      it \"executes keys\" do\n        assert_macro %({{{a: 1, b: 2}.keys}}), \"[a, b]\"\n      end\n\n      it \"executes values\" do\n        assert_macro %({{{a: 1, b: 2}.values}}), \"[1, 2]\"\n      end\n\n      it \"executes map\" do\n        assert_macro %({{{a: 1, b: 2}.map {|k, v| k.stringify == \"a\" && v == 1}}}), \"[true, false]\"\n      end\n\n      it \"executes is_a?\" do\n        assert_macro %({{{a: 1}.is_a?(NamedTupleLiteral)}}), \"true\"\n        assert_macro %({{{a: 1}.is_a?(ASTNode)}}), \"true\"\n        assert_macro %({{{a: 1}.is_a?(RangeLiteral)}}), \"false\"\n      end\n\n      it \"executes []=\" do\n        assert_macro %({% a = {a: 1}; a[:a] = 2 %}{{a[:a]}}), \"2\"\n        assert_macro %({% a = {a: 1}; a[\"a\"] = 2 %}{{a[\"a\"]}}), \"2\"\n      end\n\n      it \"executes has_key?\" do\n        assert_macro %({{{a: 1}.has_key?(\"a\")}}), \"true\"\n        assert_macro %({{{a: 1}.has_key?(:a)}}), \"true\"\n        assert_macro %({{{a: nil}.has_key?(\"a\")}}), \"true\"\n        assert_macro %({{{a: nil}.has_key?(\"b\")}}), \"false\"\n        assert_macro_error %({{{a: 1}.has_key?(true)}}), \"expected 'NamedTupleLiteral#has_key?' first argument to be a SymbolLiteral, StringLiteral or MacroId, not BoolLiteral\"\n      end\n\n      it \"creates a named tuple literal with a var\" do\n        assert_macro %({% a = {a: x} %}{{a[:a]}}), \"1\", {x: 1.int32}\n      end\n\n      it \"executes to_a\" do\n        assert_macro %({{{a: 1, b: 3}.to_a}}), \"[{a, 1}, {b, 3}]\"\n      end\n\n      it \"executes double splat\" do\n        assert_macro %({{**{a: 1, \"foo bar\": 2, \"+\": 3}}}), %(a: 1, \"foo bar\": 2, \"+\": 3)\n      end\n\n      it \"executes double splat\" do\n        assert_macro %({{{a: 1, \"foo bar\": 2, \"+\": 3}.double_splat}}), %(a: 1, \"foo bar\": 2, \"+\": 3)\n      end\n\n      it \"executes double splat with arg\" do\n        assert_macro %({{{a: 1, \"foo bar\": 2, \"+\": 3}.double_splat(\", \")}}), %(a: 1, \"foo bar\": 2, \"+\": 3, )\n      end\n\n      describe \"#each\" do\n        context \"with both arguments\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {k1: \"v1\", k2: \"v2\"}.each { |k, v| values << {k, v} } %}{{values}}{% end %}),\n              %([{k1, \"v1\"}, {k2, \"v2\"}])\n            )\n          end\n        end\n\n        context \"without the value argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {k1: \"v1\", k2: \"v2\"}.each { |k| values << k } %}{{values}}{% end %}),\n              %([k1, k2])\n            )\n          end\n        end\n\n        context \"without the key argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {k1: \"v1\", k2: \"v2\"}.each { |_, v| values << v } %}{{values}}{% end %}),\n              %([\"v1\", \"v2\"])\n            )\n          end\n        end\n\n        context \"without either argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {k1: \"v1\", k2: \"v2\"}.each { values << {\"k3\", \"v3\"} } %}{{values}}{% end %}),\n              %([{\"k3\", \"v3\"}, {\"k3\", \"v3\"}])\n            )\n          end\n        end\n      end\n\n      it \"executes select with block\" do\n        assert_macro %({{ {a: 1, b: 2, c: 3}.select { |k, v| v > 1 } }}), \"{b: 2, c: 3}\"\n      end\n\n      it \"executes select with keys\" do\n        assert_macro %({{ {a: 1, b: 2, c: 3}.select(\"a\", \"c\") }}), \"{a: 1, c: 3}\"\n      end\n\n      it \"executes reject with block\" do\n        assert_macro %({{ {a: 1, b: 2, c: 3}.reject { |k, v| v > 1 } }}), \"{a: 1}\"\n      end\n\n      it \"executes reject with keys\" do\n        assert_macro %({{ {a: 1, b: 2, c: 3}.reject(:b) }}), \"{a: 1, c: 3}\"\n      end\n    end\n\n    describe TupleLiteral do\n      it \"executes [] with 0\" do\n        assert_macro %({{ {1, 2, 3}[0] }}), \"1\"\n      end\n\n      it \"executes [] with 1\" do\n        assert_macro %({{ {1, 2, 3}[1] }}), \"2\"\n      end\n\n      it \"executes [] out of bounds\" do\n        assert_macro %({{ {1, 2, 3}[3] }}), \"nil\"\n      end\n\n      it \"executes [] with range\" do\n        assert_macro %({{ {1, 2, 3, 4}[1...-1] }}), %({2, 3})\n      end\n\n      it \"executes [] with computed range\" do\n        assert_macro %({{ {1, 2, 3, 4}[[1].size...-1] }}), %({2, 3})\n      end\n\n      it \"executes [] with incomplete range\" do\n        assert_macro %({{ {1, 2, 3, 4}[1..] }}), %({2, 3, 4})\n        assert_macro %({{ {1, 2, 3, 4}[1..nil] }}), %({2, 3, 4})\n        assert_macro %({{ {1, 2, 3, 4}[...2] }}), %({1, 2})\n        assert_macro %({{ {1, 2, 3, 4}[nil...2] }}), %({1, 2})\n        assert_macro %({{ {1, 2, 3, 4}[..] }}), %({1, 2, 3, 4})\n        assert_macro %({{ {1, 2, 3, 4}[nil..nil] }}), %({1, 2, 3, 4})\n      end\n\n      it \"executes [] with range, start is out of bounds\" do\n        assert_macro %({{ {1, 2, 3, 4}[5..] }}), %(nil)\n        assert_macro %({{ {1, 2, 3, 4}[-5..] }}), %(nil)\n      end\n\n      it \"executes [] with two numbers\" do\n        assert_macro %({{ {1, 2, 3, 4, 5}[1, 3] }}), %({2, 3, 4})\n      end\n\n      it \"executes [] with two numbers, start is out of bounds\" do\n        assert_macro %({{ {1, 2, 3, 4}[5, 1] }}), %(nil)\n        assert_macro %({{ {1, 2, 3, 4}[-5, 4] }}), %(nil)\n      end\n\n      it \"executes size\" do\n        assert_macro %({{ {1, 2, 3}.size }}), \"3\"\n      end\n\n      it \"executes empty?\" do\n        assert_macro %({{ {1, 2, 3}.empty? }}), \"false\"\n      end\n\n      it \"executes join\" do\n        assert_macro %({{ {1, 2, 3}.join \", \" }}), %(\"1, 2, 3\")\n      end\n\n      it \"executes join with strings\" do\n        assert_macro %({{ {\"a\", \"b\"}.join \", \" }}), %(\"a, b\")\n      end\n\n      it \"executes map\" do\n        assert_macro %({{ {1, 2, 3}.map { |e| e == 2 } }}), \"{false, true, false}\"\n      end\n\n      it \"executes map with constants\" do\n        assert_macro %({{x.map { |e| e.id }}}), \"{Foo, Bar}\", {x: TupleLiteral.new([Path.new(\"Foo\"), Path.new(\"Bar\")] of ASTNode)}\n      end\n\n      it \"executes map with arg\" do\n        assert_macro %({{x.map { |e| e.id }}}), \"{hello}\", {x: TupleLiteral.new([\"hello\".call] of ASTNode)}\n      end\n\n      describe \"#map_with_index\" do\n        context \"with both arguments\" do\n          it \"returns the resulting tuple\" do\n            assert_macro %({{{1, 2, 3}.map_with_index { |e, idx| e == 2 || idx <= 1 }}}), %({true, true, false})\n          end\n        end\n\n        context \"without the index argument\" do\n          it \"returns the resulting tuple\" do\n            assert_macro %({{{1, 2, 3}.map_with_index { |e| e }}}), %({1, 2, 3})\n          end\n        end\n\n        context \"without the element argument\" do\n          it \"returns the resulting tuple\" do\n            assert_macro %({{{1, 2, 3}.map_with_index { |_, idx| idx }}}), %({0, 1, 2})\n          end\n        end\n\n        context \"without either argument\" do\n          it \"returns the resulting tuple\" do\n            assert_macro %({{{1, 2, 3}.map_with_index { 7 }}}), %({7, 7, 7})\n          end\n        end\n      end\n\n      it \"#each\" do\n        assert_macro(\n          %({% begin %}{% values = [] of Nil %}{% {1, 2, 3}.each { |v| values << v } %}{{values}}{% end %}),\n          %([1, 2, 3])\n        )\n      end\n\n      describe \"#each_with_index\" do\n        context \"with both arguments\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {1, 2, 3}.each_with_index { |v, idx| values << (v + idx) } %}{{values}}{% end %}),\n              %([1, 3, 5])\n            )\n          end\n        end\n\n        context \"without the index argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {1, 2, 3}.each_with_index { |v| values << v } %}{{values}}{% end %}),\n              %([1, 2, 3])\n            )\n          end\n        end\n\n        context \"without the element argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {1, 2, 3}.each_with_index { |_, idx| values << idx } %}{{values}}{% end %}),\n              %([0, 1, 2])\n            )\n          end\n        end\n\n        context \"without either argument\" do\n          it \"builds the correct array\" do\n            assert_macro(\n              %({% begin %}{% values = [] of Nil %}{% {1, 2, 3}.each_with_index { values << 7 } %}{{values}}{% end %}),\n              %([7, 7, 7])\n            )\n          end\n        end\n      end\n\n      it \"executes select\" do\n        assert_macro %({{ {1, 2, 3}.select { |e| e == 1 } }}), \"{1}\"\n      end\n\n      it \"executes reject\" do\n        assert_macro %({{ {1, 2, 3}.reject { |e| e == 1 } }}), \"{2, 3}\"\n      end\n\n      it \"executes find (finds)\" do\n        assert_macro %({{ {1, 2, 3}.find { |e| e == 2 } }}), \"2\"\n      end\n\n      it \"executes find (doesn't find)\" do\n        assert_macro %({{ {1, 2, 3}.find { |e| e == 4 } }}), \"nil\"\n      end\n\n      it \"executes any? (true)\" do\n        assert_macro %({{ {1, 2, 3}.any? { |e| e == 1 } }}), \"true\"\n      end\n\n      it \"executes any? (false)\" do\n        assert_macro %({{ {1, 2, 3}.any? { |e| e == 4 } }}), \"false\"\n      end\n\n      it \"executes all? (true)\" do\n        assert_macro %({{ {1, 1, 1}.all? { |e| e == 1 } }}), \"true\"\n      end\n\n      it \"executes all? (false)\" do\n        assert_macro %({{ {1, 2, 1}.all? { |e| e == 1 } }}), \"false\"\n      end\n\n      it \"executes first\" do\n        assert_macro %({{ {1, 2, 3}.first }}), \"1\"\n      end\n\n      it \"executes last\" do\n        assert_macro %({{ {1, 2, 3}.last }}), \"3\"\n      end\n\n      it \"executes splat\" do\n        assert_macro %({{ {1, 2, 3}.splat }}), \"1, 2, 3\"\n      end\n\n      it \"executes splat with arg\" do\n        assert_macro %({{ {1, 2, 3}.splat(\", \") }}), \"1, 2, 3, \"\n      end\n\n      it \"executes splat with symbols and strings\" do\n        assert_macro %({{ {:foo, \"hello\", 3}.splat }}), %(:foo, \"hello\", 3)\n      end\n\n      it \"executes splat with splat\" do\n        assert_macro %({{ *{1, 2, 3} }}), \"1, 2, 3\"\n      end\n\n      it \"executes is_a?\" do\n        assert_macro %({{ {1, 2, 3}.is_a?(TupleLiteral) }}), \"true\"\n        assert_macro %({{ {1, 2, 3}.is_a?(ASTNode) }}), \"true\"\n        assert_macro %({{ {1, 2, 3}.is_a?(ArrayLiteral) }}), \"false\"\n      end\n\n      it \"creates a tuple literal with a var\" do\n        assert_macro %({% a = {x} %}{{a[0]}}), \"1\", {x: 1.int32}\n      end\n\n      it \"executes sort with numbers\" do\n        assert_macro %({{ {3, 2, 1}.sort }}), \"{1, 2, 3}\"\n      end\n\n      it \"executes sort with strings\" do\n        assert_macro %({{ {\"c\", \"b\", \"a\"}.sort }}), %({\"a\", \"b\", \"c\"})\n      end\n\n      it \"executes sort with ids\" do\n        assert_macro %({{ {\"c\".id, \"b\".id, \"a\".id}.sort }}), %({a, b, c})\n      end\n\n      it \"executes sort with ids and strings\" do\n        assert_macro %({{ {\"c\".id, \"b\", \"a\".id}.sort }}), %({a, \"b\", c})\n      end\n\n      it \"executes uniq\" do\n        assert_macro %({{ {1, 1, 1, 2, 3, 1, 2, 3, 4}.uniq }}), %({1, 2, 3, 4})\n      end\n\n      it \"executes unshift\" do\n        assert_macro %({% x = {1}; x.unshift(2); %}{{x}}), %({2, 1})\n      end\n\n      it \"executes push\" do\n        assert_macro %({% x = {1}; x.push(2); x << 3 %}{{x}}), %({1, 2, 3})\n      end\n\n      it \"executes includes?\" do\n        assert_macro %({{ {1, 2, 3}.includes?(1) }}), %(true)\n        assert_macro %({{ {1, 2, 3}.includes?(4) }}), %(false)\n      end\n\n      describe \"#+\" do\n        context \"with TupleLiteral argument\" do\n          it \"concatenates the literals into a TupleLiteral\" do\n            assert_macro %({{ {1, 2} + {3, 4, 5} }}), %({1, 2, 3, 4, 5})\n          end\n        end\n\n        context \"with ArrayLiteral argument\" do\n          it \"concatenates the literals into a TupleLiteral\" do\n            assert_macro %({{ {1, 2} + [3, 4, 5] }}), %({1, 2, 3, 4, 5})\n          end\n        end\n      end\n\n      describe \"#-\" do\n        context \"with TupleLiteral argument\" do\n          it \"removes the elements in RHS from LHS into a TupleLiteral\" do\n            assert_macro %({{ {1, 2, 3, 4} - {1, 3, 5} }}), %({2, 4})\n          end\n        end\n\n        context \"with ArrayLiteral argument\" do\n          it \"removes the elements in RHS from LHS into a TupleLiteral\" do\n            assert_macro %({{ {1, 2, 3, 4} - [1, 3, 5] }}), %({2, 4})\n          end\n        end\n      end\n\n      it \"executes *\" do\n        assert_macro %({{ {\"na\"} * 5}}), %({\"na\", \"na\", \"na\", \"na\", \"na\"})\n      end\n    end\n\n    describe \"regex methods\" do\n      it \"executes source\" do\n        assert_macro %({{ /rëgéx/i.source }}), %(\"rëgéx\")\n      end\n\n      it \"executes options\" do\n        assert_macro %({{ //.options }}), %([] of ::Symbol)\n        assert_macro %({{ /a/i.options }}), %([:i] of ::Symbol)\n        assert_macro %({{ /re/mix.options }}), %([:i, :m, :x] of ::Symbol)\n      end\n    end\n\n    describe \"metavar methods\" do\n      it \"executes nothing\" do\n        assert_macro %({{x}}), %(foo), {x: MetaMacroVar.new(\"foo\", Program.new.int32)}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(foo), {x: MetaMacroVar.new(\"foo\", Program.new.int32)}\n      end\n\n      it \"executes id\" do\n        assert_macro %({{x.id}}), %(foo), {x: MetaMacroVar.new(\"foo\", Program.new.int32)}\n      end\n\n      it \"executes is_a?\" do\n        assert_macro %({{x.is_a?(MetaVar)}}), %(true), {x: MetaMacroVar.new(\"foo\", Program.new.int32)}\n      end\n    end\n\n    describe \"block methods\" do\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"1\", {x: Block.new(body: 1.int32)}\n      end\n\n      it \"executes args\" do\n        assert_macro %({{x.args}}), \"[x, y]\", {x: Block.new([\"x\".var, \"y\".var])}\n      end\n\n      it \"executes splat_index\" do\n        assert_macro %({{x.splat_index}}), \"1\", {x: Block.new([\"x\".var, \"y\".var], splat_index: 1)}\n        assert_macro %({{x.splat_index}}), \"nil\", {x: Block.new([\"x\".var, \"y\".var])}\n      end\n    end\n\n    describe \"expressions methods\" do\n      it \"executes expressions\" do\n        assert_macro %({{x.body.expressions[0]}}), \"some_call\", {x: Block.new(body: Expressions.new([\"some_call\".call, \"some_other_call\".call] of ASTNode))}\n      end\n    end\n\n    it \"executes assign\" do\n      assert_macro %({{a = 1}}{{a}}), \"11\"\n    end\n\n    it \"executes assign without output\" do\n      assert_macro %({% a = 1 %}{{a}}), \"1\"\n    end\n\n    describe TypeNode do\n      describe \"#includers\" do\n        it \"returns an array of types `self` is directly included in\" do\n          assert_type(<<-CRYSTAL) { tuple_of([int32, int32, int32]) }\n            module Foo\n            end\n\n            module Baz\n              module Tar\n                include Baz\n              end\n            end\n\n            abstract class Parent\n            end\n\n            module Enumt(T)\n              include Baz\n            end\n\n            class Bar < Parent\n              include Foo\n              include Baz\n            end\n\n            struct Str\n              include Enumt(String)\n              include Baz\n            end\n\n            struct Gen(T)\n              include Baz\n            end\n\n            abstract struct AStr\n              include Baz\n            end\n\n            abstract class ACla\n              include Baz\n            end\n\n            class SubT(T)\n              include Baz\n            end\n\n            class ChildT(T) < SubT(T)\n              include Enumt(T)\n            end\n\n            class Witness < ChildT(String)\n            end\n\n            {\n              {% if Baz.includers.map(&.stringify).sort == %w(ACla AStr Bar Baz::Tar Enumt(T) Gen(T) Str SubT(T)) %} 1 {% else %} 'a' {% end %},\n              {% if Enumt.includers.map(&.stringify).sort == %w(ChildT(String) ChildT(T) Str) %} 1 {% else %} 'a' {% end %},\n              {% if Enumt(String).includers.map(&.stringify).sort == %w(ChildT(String) Str) %} 1 {% else %} 'a' {% end %},\n            }\n            CRYSTAL\n        end\n      end\n\n      describe \"#name\" do\n        describe \"simple type\" do\n          it \"returns the name of the type\" do\n            assert_macro(\"{{x.name}}\", \"String\") do |program|\n              {x: TypeNode.new(program.string)}\n            end\n          end\n        end\n\n        describe \"namespaced type\" do\n          it \"should return the FQN of the type\" do\n            assert_macro(\"{{type.name}}\", \"SomeModule::SomeType\") do |program|\n              mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n\n              klass = NonGenericClassType.new(program, mod, \"SomeType\", program.reference)\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n\n        describe \"generic type\" do\n          it \"includes the generic_args of the type by default\" do\n            assert_macro(\"{{klass.name}}\", \"SomeType(A, B)\") do |program|\n              {klass: TypeNode.new(GenericClassType.new(program, program, \"SomeType\", program.object, [\"A\", \"B\"]))}\n            end\n          end\n\n          it \"includes the generic_args of the instantiated type by default\" do\n            assert_macro(\"{{Array(Int32).name}}\", \"Array(Int32)\")\n          end\n        end\n\n        describe \"generic instance\" do\n          it \"prints generic type arguments\" do\n            assert_macro(\"{{klass.name}}\", \"Foo(Int32, 3)\") do |program|\n              generic_type = GenericClassType.new(program, program, \"Foo\", program.reference, [\"T\", \"U\"])\n              {klass: TypeNode.new(generic_type.instantiate([program.int32, 3.int32] of TypeVar))}\n            end\n          end\n\n          it \"prints empty splat type var\" do\n            assert_macro(\"{{klass.name}}\", \"Foo()\") do |program|\n              generic_type = GenericClassType.new(program, program, \"Foo\", program.reference, [\"T\"])\n              generic_type.splat_index = 0\n              {klass: TypeNode.new(generic_type.instantiate([] of TypeVar))}\n            end\n          end\n\n          it \"prints multiple arguments for splat type var\" do\n            assert_macro(\"{{klass.name}}\", \"Foo(Int32, String)\") do |program|\n              generic_type = GenericClassType.new(program, program, \"Foo\", program.reference, [\"T\"])\n              generic_type.splat_index = 0\n              {klass: TypeNode.new(generic_type.instantiate([program.int32, program.string] of TypeVar))}\n            end\n          end\n\n          it \"does not print extra commas for empty splat type var (1)\" do\n            assert_macro(\"{{klass.name}}\", \"Foo(Int32)\") do |program|\n              generic_type = GenericClassType.new(program, program, \"Foo\", program.reference, [\"T\", \"U\"])\n              generic_type.splat_index = 1\n              {klass: TypeNode.new(generic_type.instantiate([program.int32] of TypeVar))}\n            end\n          end\n\n          it \"does not print extra commas for empty splat type var (2)\" do\n            assert_macro(\"{{klass.name}}\", \"Foo(Int32)\") do |program|\n              generic_type = GenericClassType.new(program, program, \"Foo\", program.reference, [\"T\", \"U\"])\n              generic_type.splat_index = 0\n              {klass: TypeNode.new(generic_type.instantiate([program.int32] of TypeVar))}\n            end\n          end\n\n          it \"does not print extra commas for empty splat type var (3)\" do\n            assert_macro(\"{{klass.name}}\", \"Foo(Int32, String)\") do |program|\n              generic_type = GenericClassType.new(program, program, \"Foo\", program.reference, [\"T\", \"U\", \"V\"])\n              generic_type.splat_index = 1\n              {klass: TypeNode.new(generic_type.instantiate([program.int32, program.string] of TypeVar))}\n            end\n          end\n        end\n\n        describe :generic_args do\n          describe true do\n            it \"includes the generic_args of the type\" do\n              assert_macro(\"{{klass.name(generic_args: true)}}\", \"SomeType(A, B)\") do |program|\n                {klass: TypeNode.new(GenericClassType.new(program, program, \"SomeType\", program.object, [\"A\", \"B\"]))}\n              end\n            end\n\n            it \"includes the generic_args of the instantiated type\" do\n              assert_macro(\"{{Array(Int32).name(generic_args: true)}}\", \"Array(Int32)\")\n            end\n          end\n\n          describe false do\n            it \"does not include the generic_args of the type\" do\n              assert_macro(\"{{klass.name(generic_args: false)}}\", \"SomeType\") do |program|\n                {klass: TypeNode.new(GenericClassType.new(program, program, \"SomeType\", program.object, [\"A\", \"B\"]))}\n              end\n            end\n\n            it \"does not include the generic_args of the instantiated type\" do\n              assert_macro(\"{{Array(Int32).name(generic_args: false)}}\", \"Array\")\n            end\n          end\n\n          describe \"with an invalid type argument\" do\n            it \"should raise the proper exception\" do\n              assert_macro_error(\"{{x.name(generic_args: 99)}}\", \"named argument 'generic_args' to TypeNode#name must be a BoolLiteral, not NumberLiteral\") do |program|\n                {x: TypeNode.new(program.string)}\n              end\n            end\n          end\n        end\n      end\n\n      describe \"#id\" do\n        it \"does not include trailing + for virtual type\" do\n          assert_macro(\"{{klass.id}}\", \"Foo\") do |program|\n            foo = NonGenericClassType.new(program, program, \"Foo\", program.reference)\n            NonGenericClassType.new(program, program, \"Bar\", foo)\n            {klass: TypeNode.new(foo.virtual_type)}\n          end\n        end\n      end\n\n      describe \"#warning\" do\n        it \"emits a warning at a specific node\" do\n          assert_warning <<-CRYSTAL, \"Oh noes\"\n            macro test(node)\n              {% node.warning \"Oh noes\" %}\n            end\n\n            test 10\n          CRYSTAL\n        end\n      end\n\n      describe \"#instance_vars\" do\n        it \"executes instance_vars\" do\n          assert_macro(\"{{x.instance_vars.map &.stringify}}\", %([\"bytesize\", \"length\", \"c\"])) do |program|\n            {x: TypeNode.new(program.string)}\n          end\n        end\n\n        it \"errors when called from top-level scope\" do\n          assert_error <<-CRYSTAL, \"`TypeNode#instance_vars` cannot be called in the top-level scope: instance vars are not yet initialized\"\n            class Foo\n            end\n            {{ Foo.instance_vars }}\n          CRYSTAL\n        end\n\n        it \"does not error when called from def scope\" do\n          assert_type <<-CRYSTAL { |program| program.string }\n            module Moo\n            end\n            def moo\n              {{ Moo.instance_vars.stringify }}\n            end\n            moo\n          CRYSTAL\n        end\n      end\n\n      it \"executes class vars\" do\n        assert_macro(\"{{x.class_vars.map &.name}}\", %([class_var])) do |program|\n          klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n          declare_class_var(klass, \"@@class_var\", program.string)\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes class vars (with inheritance)\" do\n        assert_macro(\"{{x.class_vars.map &.name}}\", %([child_class_var, base_class_var, mod_class_var])) do |program|\n          base_class = NonGenericClassType.new(program, program, \"BaseType\", program.reference)\n          declare_class_var(base_class, \"@@base_class_var\", program.string)\n          mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n          declare_class_var(mod, \"@@mod_class_var\", program.string)\n          base_class.include mod\n          child_class = NonGenericClassType.new(program, program, \"ChildType\", base_class)\n          declare_class_var(child_class, \"@@child_class_var\", program.string)\n          {x: TypeNode.new(child_class)}\n        end\n      end\n\n      it \"executes instance_vars on metaclass\" do\n        assert_macro(\"{{x.class.instance_vars.map &.stringify}}\", %([])) do |program|\n          klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n          klass.declare_instance_var(\"@var\", program.string)\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes class_vars on metaclass\" do\n        assert_macro(\"{{x.class.class_vars.map &.stringify}}\", %([])) do |program|\n          klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n          declare_class_var(klass, \"@@class_var\", program.string)\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes instance_vars on symbol type\" do\n        assert_macro(\"{{x.instance_vars.map &.stringify}}\", %([])) do |program|\n          {x: TypeNode.new(program.symbol)}\n        end\n      end\n\n      it \"executes class_vars on symbol type\" do\n        assert_macro(\"{{x.class_vars.map &.stringify}}\", %([])) do |program|\n          {x: TypeNode.new(program.symbol)}\n        end\n      end\n\n      it \"executes methods\" do\n        assert_macro(\"{{x.methods.map &.name}}\", %([foo])) do |program|\n          klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n          a_def = Def.new \"foo\"\n          klass.add_def a_def\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes class methods\" do\n        assert_macro(\"{{x.class.methods.map &.name}}\", %([allocate])) do |program|\n          klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes ancestors\" do\n        assert_macro(\"{{x.ancestors}}\", %([SomeModule, Reference, Object])) do |program|\n          mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n          klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n          klass.include mod\n\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes ancestors (with generic)\" do\n        assert_macro(\"{{x.ancestors}}\", %([SomeGenericModule(String), SomeGenericType(String), Reference, Object])) do |program|\n          generic_type = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n          generic_mod = GenericModuleType.new(program, program, \"SomeGenericModule\", [\"T\"])\n          type_var = {\"T\" => TypeNode.new(program.string)} of String => ASTNode\n          type = GenericClassInstanceType.new(program, generic_type, program.reference, type_var)\n          mod = GenericModuleInstanceType.new(program, generic_mod, type_var)\n\n          klass = NonGenericClassType.new(program, program, \"SomeType\", type)\n          klass.include mod\n\n          {x: TypeNode.new(klass)}\n        end\n      end\n\n      it \"executes superclass\" do\n        assert_macro(\"{{x.superclass}}\", %(Reference)) do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes size of tuple\" do\n        assert_macro(\"{{x.size}}\", \"2\") do |program|\n          {x: TypeNode.new(program.tuple_of([program.int32, program.string] of TypeVar))}\n        end\n      end\n\n      it \"executes size of tuple metaclass\" do\n        assert_macro(\"{{x.size}}\", \"2\") do |program|\n          {x: TypeNode.new(program.tuple_of([program.int32, program.string] of TypeVar).metaclass)}\n        end\n      end\n\n      it \"executes type_vars\" do\n        assert_macro(\"{{x.type_vars.map &.stringify}}\", %([\"A\", \"B\"])) do |program|\n          {x: TypeNode.new(GenericClassType.new(program, program, \"SomeType\", program.object, [\"A\", \"B\"]))}\n        end\n        assert_macro(\"{{x.type_vars.map &.stringify}}\", %([\"Int32\", \"String\"])) do |program|\n          generic_class = GenericClassType.new(program, program, \"SomeType\", program.object, [\"A\", \"B\"])\n          {x: TypeNode.new(generic_class.instantiate([program.int32, program.string] of TypeVar))}\n        end\n        assert_macro(\"{{x.type_vars.map &.stringify}}\", %([\"Tuple(Int32, String)\"])) do |program|\n          generic_class = GenericClassType.new(program, program, \"SomeType\", program.object, [\"T\"])\n          generic_class.splat_index = 0\n          {x: TypeNode.new(generic_class.instantiate([program.int32, program.string] of TypeVar))}\n        end\n        assert_macro(\"{{x.type_vars.map &.stringify}}\", %([\"Tuple()\"])) do |program|\n          generic_class = GenericClassType.new(program, program, \"SomeType\", program.object, [\"T\"])\n          generic_class.splat_index = 0\n          {x: TypeNode.new(generic_class.instantiate([] of TypeVar))}\n        end\n      end\n\n      it \"executes class\" do\n        assert_macro(\"{{x.class.name}}\", \"String.class\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes instance\" do\n        assert_macro(\"{{x.class.instance}}\", \"String\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes ==\" do\n        assert_macro(\"{{x == Reference}}\", \"false\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n        assert_macro(\"{{x == String}}\", \"true\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes !=\" do\n        assert_macro(\"{{x != Reference}}\", \"true\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n        assert_macro(\"{{x != String}}\", \"false\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"== and != devirtualize generic type arguments (#10730)\" do\n        assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n          class A\n          end\n\n          class B < A\n          end\n\n          module Foo(T)\n            def self.foo\n              {\n                {% if T == A %} 1 {% else %} 'a' {% end %},\n                {% if T != A %} 1 {% else %} 'a' {% end %},\n              }\n            end\n          end\n\n          Foo(A).foo\n          CRYSTAL\n      end\n\n      it \"executes <\" do\n        assert_macro(\"{{x < Reference}}\", \"true\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n        assert_macro(\"{{x < String}}\", \"false\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes <=\" do\n        assert_macro(\"{{x <= Reference}}\", \"true\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n        assert_macro(\"{{x <= String}}\", \"true\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes >\" do\n        assert_macro(\"{{x > Reference}}\", \"false\") do |program|\n          {x: TypeNode.new(program.reference)}\n        end\n        assert_macro(\"{{x > String}}\", \"true\") do |program|\n          {x: TypeNode.new(program.reference)}\n        end\n      end\n\n      it \"executes >=\" do\n        assert_macro(\"{{x >= Reference}}\", \"true\") do |program|\n          {x: TypeNode.new(program.reference)}\n        end\n        assert_macro(\"{{x >= String}}\", \"true\") do |program|\n          {x: TypeNode.new(program.reference)}\n        end\n      end\n\n      describe \"#abstract?\" do\n        it NonGenericModuleType do\n          assert_macro(\"{{type.abstract?}}\", \"false\") do |program|\n            mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n\n            {type: TypeNode.new(mod)}\n          end\n        end\n\n        it GenericModuleType do\n          assert_macro(\"{{type.abstract?}}\", \"false\") do |program|\n            generic_mod = GenericModuleType.new(program, program, \"SomeGenericModule\", [\"T\"])\n\n            {type: TypeNode.new(generic_mod)}\n          end\n        end\n\n        describe NonGenericClassType do\n          describe \"class\" do\n            it \"abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"true\") do |program|\n                klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n                klass.abstract = true\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n\n            it \"non-abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"false\") do |program|\n                klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n          end\n\n          describe \"struct\" do\n            it \"abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"true\") do |program|\n                klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n                klass.abstract = true\n                klass.struct = true\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n\n            it \"non-abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"false\") do |program|\n                klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n                klass.struct = true\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n          end\n        end\n\n        describe GenericClassType do\n          describe \"class\" do\n            it \"abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"true\") do |program|\n                klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n                klass.abstract = true\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n\n            it \"non-abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"false\") do |program|\n                klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n          end\n\n          describe \"struct\" do\n            it \"abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"true\") do |program|\n                klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n                klass.abstract = true\n                klass.struct = true\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n\n            it \"non-abstract\" do\n              assert_macro(\"{{type.abstract?}}\", \"false\") do |program|\n                klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n                klass.struct = true\n\n                {type: TypeNode.new(klass)}\n              end\n            end\n          end\n        end\n      end\n\n      describe \"#union?\" do\n        it true do\n          assert_macro(\"{{x.union?}}\", \"true\") do |program|\n            {x: TypeNode.new(program.union_of(program.string, program.nil))}\n          end\n        end\n\n        it false do\n          assert_macro(\"{{x.union?}}\", \"false\") do |program|\n            {x: TypeNode.new(program.string)}\n          end\n        end\n      end\n\n      describe \"#module?\" do\n        it NonGenericModuleType do\n          assert_macro(\"{{type.module?}}\", \"true\") do |program|\n            mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n\n            {type: TypeNode.new(mod)}\n          end\n        end\n\n        it GenericModuleType do\n          assert_macro(\"{{type.module?}}\", \"true\") do |program|\n            generic_mod = GenericModuleType.new(program, program, \"SomeGenericModule\", [\"T\"])\n\n            {type: TypeNode.new(generic_mod)}\n          end\n        end\n\n        describe NonGenericClassType do\n          it \"class\" do\n            assert_macro(\"{{type.module?}}\", \"false\") do |program|\n              klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n\n          it \"struct\" do\n            assert_macro(\"{{type.module?}}\", \"false\") do |program|\n              klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n              klass.struct = true\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n\n        describe GenericClassType do\n          it \"class\" do\n            assert_macro(\"{{type.module?}}\", \"false\") do |program|\n              klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n\n          it \"struct\" do\n            assert_macro(\"{{type.module?}}\", \"false\") do |program|\n              klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n              klass.struct = true\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n      end\n\n      describe \"#class?\" do\n        it NonGenericModuleType do\n          assert_macro(\"{{type.class?}}\", \"false\") do |program|\n            mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n\n            {type: TypeNode.new(mod)}\n          end\n        end\n\n        it GenericModuleType do\n          assert_macro(\"{{type.class?}}\", \"false\") do |program|\n            generic_mod = GenericModuleType.new(program, program, \"SomeGenericModule\", [\"T\"])\n\n            {type: TypeNode.new(generic_mod)}\n          end\n        end\n\n        describe NonGenericClassType do\n          it \"class\" do\n            assert_macro(\"{{type.class?}}\", \"true\") do |program|\n              klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n\n          it \"struct\" do\n            assert_macro(\"{{type.class?}}\", \"false\") do |program|\n              klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n              klass.struct = true\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n\n        describe GenericClassType do\n          it \"class\" do\n            assert_macro(\"{{type.class?}}\", \"true\") do |program|\n              klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n\n          it \"struct\" do\n            assert_macro(\"{{type.class?}}\", \"false\") do |program|\n              klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n              klass.struct = true\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n      end\n\n      describe \"#struct?\" do\n        it NonGenericModuleType do\n          assert_macro(\"{{type.struct?}}\", \"false\") do |program|\n            mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n\n            {type: TypeNode.new(mod)}\n          end\n        end\n\n        it GenericModuleType do\n          assert_macro(\"{{type.struct?}}\", \"false\") do |program|\n            generic_mod = GenericModuleType.new(program, program, \"SomeGenericModule\", [\"T\"])\n\n            {type: TypeNode.new(generic_mod)}\n          end\n        end\n\n        describe NonGenericClassType do\n          it \"class\" do\n            assert_macro(\"{{type.struct?}}\", \"false\") do |program|\n              klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n\n          it \"struct\" do\n            assert_macro(\"{{type.struct?}}\", \"true\") do |program|\n              klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n              klass.struct = true\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n\n        describe GenericClassType do\n          it \"class\" do\n            assert_macro(\"{{type.struct?}}\", \"false\") do |program|\n              klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n\n          it \"struct\" do\n            assert_macro(\"{{type.struct?}}\", \"true\") do |program|\n              klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n              klass.struct = true\n\n              {type: TypeNode.new(klass)}\n            end\n          end\n        end\n      end\n\n      describe \"#nilable?\" do\n        it false do\n          assert_macro(\"{{x.nilable?}}\", \"false\") do |program|\n            {x: TypeNode.new(program.string)}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"false\") do |program|\n            {x: TypeNode.new(program.union_of(program.string, program.int32))}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"false\") do |program|\n            {x: TypeNode.new(program.no_return)}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"false\") do |program|\n            {x: TypeNode.new(program.class_type)}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"false\") do |program|\n            {x: TypeNode.new(program.reference)}\n          end\n        end\n\n        it true do\n          assert_macro(\"{{x.nilable?}}\", \"true\") do |program|\n            {x: TypeNode.new(program.nil_type)}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"true\") do |program|\n            {x: TypeNode.new(program.union_of(program.string, program.nil))}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"true\") do |program|\n            {x: TypeNode.new(program.value)}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"true\") do |program|\n            {x: TypeNode.new(program.object)}\n          end\n\n          assert_macro(\"{{x.nilable?}}\", \"true\") do |program|\n            mod = NonGenericModuleType.new(program, program, \"SomeModule\")\n            program.nil_type.include mod\n            {x: TypeNode.new(mod)}\n          end\n\n          assert_type(<<-CRYSTAL) { int32 }\n            class Foo(T)\n            end\n\n            alias Bar = Foo(Bar)?\n\n            {{ Bar.nilable? ? 1 : 'a' }}\n            CRYSTAL\n        end\n      end\n\n      it \"executes resolve\" do\n        assert_macro(\"{{x.resolve}}\", \"String\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes resolve?\" do\n        assert_macro(\"{{x.resolve?}}\", \"String\") do |program|\n          {x: TypeNode.new(program.string)}\n        end\n      end\n\n      it \"executes union_types (union)\" do\n        assert_macro(\"{{x.union_types}}\", %([Bool, Int32])) do |program|\n          {x: TypeNode.new(program.union_of(program.int32, program.bool))}\n        end\n      end\n\n      it \"executes union_types (non-union)\" do\n        assert_macro(\"{{x.union_types}}\", %([Int32])) do |program|\n          {x: TypeNode.new(program.int32)}\n        end\n      end\n\n      describe \"executes private?\" do\n        it false do\n          assert_macro(\"{{x.private?}}\", \"false\") do |program|\n            klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n            {x: TypeNode.new(klass)}\n          end\n        end\n\n        it true do\n          assert_macro(\"{{x.private?}}\", \"true\") do |program|\n            klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n            klass.private = true\n\n            {x: TypeNode.new(klass)}\n          end\n        end\n      end\n\n      describe \"public?\" do\n        it false do\n          assert_macro(\"{{x.public?}}\", \"false\") do |program|\n            klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n            klass.private = true\n\n            {x: TypeNode.new(klass)}\n          end\n        end\n\n        it true do\n          assert_macro(\"{{x.public?}}\", \"true\") do |program|\n            klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n            {x: TypeNode.new(klass)}\n          end\n        end\n      end\n\n      describe \"visibility\" do\n        it :public do\n          assert_macro(\"{{x.visibility}}\", \":public\") do |program|\n            klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n\n            {x: TypeNode.new(klass)}\n          end\n        end\n\n        it :private do\n          assert_macro(\"{{x.visibility}}\", \":private\") do |program|\n            klass = GenericClassType.new(program, program, \"SomeGenericType\", program.reference, [\"T\"])\n            klass.private = true\n\n            {x: TypeNode.new(klass)}\n          end\n        end\n      end\n\n      describe \"#has_inner_pointers?\" do\n        it \"works on structs\" do\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(false)) do |program|\n            klass = NonGenericClassType.new(program, program, \"SomeType\", program.struct)\n            klass.struct = true\n            klass.declare_instance_var(\"@var\", program.int32)\n            {x: TypeNode.new(klass)}\n          end\n\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(true)) do |program|\n            klass = NonGenericClassType.new(program, program, \"SomeType\", program.struct)\n            klass.struct = true\n            klass.declare_instance_var(\"@var\", program.string)\n            {x: TypeNode.new(klass)}\n          end\n        end\n\n        it \"works on references\" do\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(true)) do |program|\n            klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n            {x: TypeNode.new(klass)}\n          end\n        end\n\n        it \"works on ReferenceStorage\" do\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(false)) do |program|\n            reference_storage = GenericReferenceStorageType.new program, program, \"ReferenceStorage\", program.struct, [\"T\"]\n            klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n            klass.declare_instance_var(\"@var\", program.int32)\n            {x: TypeNode.new(reference_storage.instantiate([klass] of TypeVar))}\n          end\n\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(true)) do |program|\n            reference_storage = GenericReferenceStorageType.new program, program, \"ReferenceStorage\", program.struct, [\"T\"]\n            klass = NonGenericClassType.new(program, program, \"SomeType\", program.reference)\n            klass.declare_instance_var(\"@var\", program.string)\n            {x: TypeNode.new(reference_storage.instantiate([klass] of TypeVar))}\n          end\n        end\n\n        it \"works on primitive values\" do\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(false)) do |program|\n            {x: TypeNode.new(program.int32)}\n          end\n\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(true)) do |program|\n            {x: TypeNode.new(program.void)}\n          end\n\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(true)) do |program|\n            {x: TypeNode.new(program.pointer_of(program.int32))}\n          end\n\n          assert_macro(\"{{x.has_inner_pointers?}}\", %(true)) do |program|\n            {x: TypeNode.new(program.proc_of(program.void))}\n          end\n        end\n\n        it \"errors when called from top-level scope\" do\n          assert_error <<-CRYSTAL, \"`TypeNode#has_inner_pointers?` cannot be called in the top-level scope: instance vars are not yet initialized\"\n            class Foo\n            end\n            {{ Foo.has_inner_pointers? }}\n          CRYSTAL\n        end\n\n        it \"does not error when called from def scope\" do\n          assert_type <<-CRYSTAL { |program| program.bool }\n            module Moo\n            end\n            def moo\n              {{ Moo.has_inner_pointers? }}\n            end\n            moo\n          CRYSTAL\n        end\n      end\n    end\n\n    describe \"type declaration methods\" do\n      it \"executes var\" do\n        assert_macro %({{x.var}}), \"some_name\", {x: TypeDeclaration.new(Var.new(\"some_name\"), Path.new(\"SomeType\"))}\n      end\n\n      it \"executes var when instance var\" do\n        assert_macro %({{x.var}}), \"@some_name\", {x: TypeDeclaration.new(InstanceVar.new(\"@some_name\"), Path.new(\"SomeType\"))}\n      end\n\n      it \"executes type\" do\n        assert_macro %({{x.type}}), \"SomeType\", {x: TypeDeclaration.new(Var.new(\"some_name\"), Path.new(\"SomeType\"))}\n      end\n\n      it \"executes value\" do\n        assert_macro %({{x.value}}), \"1\", {x: TypeDeclaration.new(Var.new(\"some_name\"), Path.new(\"SomeType\"), 1.int32)}\n      end\n    end\n\n    describe \"uninitialized var methods\" do\n      it \"executes var\" do\n        assert_macro %({{x.var}}), \"some_name\", {x: UninitializedVar.new(Var.new(\"some_name\"), Path.new(\"SomeType\"))}\n      end\n\n      it \"executes type\" do\n        assert_macro %({{x.type}}), \"SomeType\", {x: UninitializedVar.new(Var.new(\"some_name\"), Path.new(\"SomeType\"))}\n      end\n    end\n\n    describe \"proc notation methods\" do\n      it \"gets single input\" do\n        assert_macro %({{x.inputs}}), \"[SomeType]\", {x: ProcNotation.new(([Path.new(\"SomeType\")] of ASTNode), Path.new(\"SomeResult\"))}\n      end\n\n      it \"gets single output\" do\n        assert_macro %({{x.output}}), \"SomeResult\", {x: ProcNotation.new(([Path.new(\"SomeType\")] of ASTNode), Path.new(\"SomeResult\"))}\n      end\n\n      it \"gets multiple inputs\" do\n        assert_macro %({{x.inputs}}), \"[SomeType, OtherType]\", {x: ProcNotation.new([Path.new(\"SomeType\"), Path.new(\"OtherType\")] of ASTNode)}\n      end\n\n      it \"gets empty output\" do\n        assert_macro %({{x.output}}), \"nil\", {x: ProcNotation.new([Path.new(\"SomeType\")] of ASTNode)}\n      end\n\n      it \"executes resolve\" do\n        assert_macro %({{x.resolve}}), \"Proc(Int32, String)\", {x: ProcNotation.new(([Path.new(\"Int32\")] of ASTNode), Path.new(\"String\"))}\n\n        assert_macro_error(%({{x.resolve}}), \"undefined constant Foo\") do\n          {x: ProcNotation.new(([Path.new(\"Foo\")] of ASTNode))}\n        end\n\n        assert_macro_error(%({{x.resolve}}), \"undefined constant Foo\") do\n          {x: ProcNotation.new(([] of ASTNode), Path.new(\"Foo\"))}\n        end\n      end\n\n      it \"executes resolve?\" do\n        assert_macro %({{x.resolve?}}), \"Proc(Int32, String)\", {x: ProcNotation.new(([Path.new(\"Int32\")] of ASTNode), Path.new(\"String\"))}\n        assert_macro %({{x.resolve?}}), \"nil\", {x: ProcNotation.new(([Path.new(\"Foo\")] of ASTNode))}\n        assert_macro %({{x.resolve?}}), \"nil\", {x: ProcNotation.new(([] of ASTNode), Path.new(\"Foo\"))}\n      end\n    end\n\n    describe \"proc literal methods\" do\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"1\", {x: ProcLiteral.new(Def.new(\"->\", body: 1.int32))}\n      end\n\n      it \"executes args\" do\n        assert_macro %({{x.args}}), \"[z]\", {x: ProcLiteral.new(Def.new(\"->\", [Arg.new(\"z\")]))}\n      end\n\n      it \"executes return_type\" do\n        assert_macro %({{x.return_type}}), \"Int32\", {x: ProcLiteral.new(Def.new(\"->\", return_type: \"Int32\".path))}\n        assert_macro %({{x.return_type}}), \"\", {x: ProcLiteral.new(Def.new(\"->\"))}\n      end\n    end\n\n    describe \"proc pointer methods\" do\n      it \"executes obj when present\" do\n        assert_macro %({{x.obj}}), \"some_object\", {x: ProcPointer.new(Var.new(\"some_object\"), \"method\", [] of ASTNode)}\n      end\n\n      it \"executes obj when absent\" do\n        assert_macro %({{x.obj}}), \"nil\", {x: ProcPointer.new(NilLiteral.new, \"method\", [] of ASTNode)}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"method\", {x: ProcPointer.new(Var.new(\"some_object\"), \"method\", [] of ASTNode)}\n      end\n\n      it \"executes args when empty\" do\n        assert_macro %({{x.args}}), \"[]\", {x: ProcPointer.new(Var.new(\"some_object\"), \"method\", [] of ASTNode)}\n      end\n\n      it \"executes args when not empty\" do\n        assert_macro %({{x.args}}), \"[SomeType, OtherType]\", {x: ProcPointer.new(Var.new(\"some_object\"), \"method\", [Path.new(\"SomeType\"), Path.new(\"OtherType\")] of ASTNode)}\n      end\n\n      it \"executes global?\" do\n        assert_macro %({{x.global?}}), \"false\", {x: ProcPointer.new(nil, \"method\")}\n        assert_macro %({{x.global?}}), \"true\", {x: ProcPointer.new(nil, \"method\", global: true)}\n        assert_macro %({{x.global?}}), \"false\", {x: ProcPointer.new(Path.global(\"Foo\"), \"method\")}\n      end\n    end\n\n    describe \"def methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"some_def\", {x: Def.new(\"some_def\")}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"1\", {x: Def.new(\"some_def\", body: 1.int32)}\n      end\n\n      it \"executes args\" do\n        assert_macro %({{x.args}}), \"[z]\", {x: Def.new(\"some_def\", [Arg.new(\"z\")])}\n      end\n\n      it \"executes splat_index\" do\n        assert_macro %({{x.splat_index}}), \"1\", {x: Def.new(\"some_def\", [\"x\".arg, \"y\".arg], splat_index: 1)}\n        assert_macro %({{x.splat_index}}), \"nil\", {x: Def.new(\"some_def\")}\n      end\n\n      it \"executes double_splat\" do\n        assert_macro %({{x.double_splat}}), \"s\", {x: Def.new(\"some_def\", [\"x\".arg, \"y\".arg], double_splat: \"s\".arg)}\n        assert_macro %({{x.double_splat}}), \"\", {x: Def.new(\"some_def\")}\n      end\n\n      it \"executes block_arg\" do\n        assert_macro %({{x.block_arg}}), \"b\", {x: Def.new(\"some_def\", [\"x\".arg, \"y\".arg], block_arg: \"b\".arg)}\n        assert_macro %({{x.block_arg}}), \"\", {x: Def.new(\"some_def\")}\n      end\n\n      it \"executes accepts_block?\" do\n        assert_macro %({{x.accepts_block?}}), \"true\", {x: Def.new(\"some_def\", [\"x\".arg, \"y\".arg], block_arity: 1)}\n        assert_macro %({{x.accepts_block?}}), \"false\", {x: Def.new(\"some_def\")}\n      end\n\n      it \"executes return_type\" do\n        assert_macro %({{x.return_type}}), \"b\", {x: Def.new(\"some_def\", [\"x\".arg, \"y\".arg], return_type: \"b\".arg)}\n        assert_macro %({{x.return_type}}), \"\", {x: Def.new(\"some_def\")}\n      end\n\n      it \"executes free_vars\" do\n        assert_macro %({{x.free_vars}}), \"[] of ::NoReturn\", {x: Def.new(\"some_def\")}\n        assert_macro %({{x.free_vars}}), \"[T]\", {x: Def.new(\"some_def\", free_vars: %w(T))}\n        assert_macro %({{x.free_vars}}), \"[T, U, V]\", {x: Def.new(\"some_def\", free_vars: %w(T U V))}\n      end\n\n      it \"executes receiver\" do\n        assert_macro %({{x.receiver}}), \"self\", {x: Def.new(\"some_def\", receiver: Var.new(\"self\"))}\n      end\n\n      it \"executes abstract?\" do\n        assert_macro %({{x.abstract?}}), \"false\", {x: Def.new(\"some_def\")}\n        assert_macro %({{x.abstract?}}), \"true\", {x: Def.new(\"some_def\", abstract: true)}\n      end\n\n      it \"executes visibility\" do\n        assert_macro %({{x.visibility}}), \":public\", {x: Def.new(\"some_def\")}\n        assert_macro %({{x.visibility}}), \":private\", {x: Def.new(\"some_def\").tap { |d| d.visibility = Visibility::Private }}\n      end\n    end\n\n    describe External do\n      it \"executes is_a?\" do\n        assert_macro %({{x.is_a?(External)}}), \"true\", {x: External.new(\"foo\", [] of Arg, Nop.new, \"foo\")}\n        assert_macro %({{x.is_a?(Def)}}), \"true\", {x: External.new(\"foo\", [] of Arg, Nop.new, \"foo\")}\n        assert_macro %({{x.is_a?(ASTNode)}}), \"true\", {x: External.new(\"foo\", [] of Arg, Nop.new, \"foo\")}\n      end\n    end\n\n    describe Primitive do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(:abc), {x: Primitive.new(\"abc\")}\n        assert_macro %({{x.name}}), %(:\"x.y.z\"), {x: Primitive.new(\"x.y.z\")}\n      end\n    end\n\n    describe \"macro methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"some_macro\", {x: Macro.new(\"some_macro\")}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"1\", {x: Macro.new(\"some_macro\", body: 1.int32)}\n      end\n\n      it \"executes args\" do\n        assert_macro %({{x.args}}), \"[z]\", {x: Macro.new(\"some_macro\", [Arg.new(\"z\")])}\n      end\n\n      it \"executes splat_index\" do\n        assert_macro %({{x.splat_index}}), \"1\", {x: Macro.new(\"some_macro\", [\"x\".arg, \"y\".arg], splat_index: 1)}\n        assert_macro %({{x.splat_index}}), \"nil\", {x: Macro.new(\"some_macro\")}\n      end\n\n      it \"executes double_splat\" do\n        assert_macro %({{x.double_splat}}), \"s\", {x: Macro.new(\"some_macro\", [\"x\".arg, \"y\".arg], double_splat: \"s\".arg)}\n        assert_macro %({{x.double_splat}}), \"\", {x: Macro.new(\"some_macro\")}\n      end\n\n      it \"executes block_arg\" do\n        assert_macro %({{x.block_arg}}), \"b\", {x: Macro.new(\"some_macro\", [\"x\".arg, \"y\".arg], block_arg: \"b\".arg)}\n        assert_macro %({{x.block_arg}}), \"\", {x: Macro.new(\"some_macro\")}\n      end\n\n      it \"executes visibility\" do\n        assert_macro %({{x.visibility}}), \":public\", {x: Macro.new(\"some_macro\")}\n        assert_macro %({{x.visibility}}), \":private\", {x: Macro.new(\"some_macro\").tap { |d| d.visibility = Visibility::Private }}\n      end\n    end\n\n    describe MacroExpression do\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), \"nil\", {x: MacroExpression.new(NilLiteral.new)}\n      end\n\n      it \"executes output?\" do\n        assert_macro %({{x.output?}}), \"false\", {x: MacroExpression.new(NilLiteral.new, output: false)}\n        assert_macro %({{x.output?}}), \"true\", {x: MacroExpression.new(1.int32, output: true)}\n      end\n    end\n\n    describe \"macro if methods\" do\n      it \"executes cond\" do\n        assert_macro %({{x.cond}}), \"true\", {x: MacroIf.new(BoolLiteral.new(true), NilLiteral.new)}\n      end\n\n      it \"executes then\" do\n        assert_macro %({{x.then}}), \"\\\"test\\\"\", {x: MacroIf.new(BoolLiteral.new(true), StringLiteral.new(\"test\"), StringLiteral.new(\"foo\"))}\n      end\n\n      it \"executes else\" do\n        assert_macro %({{x.else}}), \"\\\"foo\\\"\", {x: MacroIf.new(BoolLiteral.new(true), StringLiteral.new(\"test\"), StringLiteral.new(\"foo\"))}\n      end\n\n      it \"executes is_unless?\" do\n        assert_macro %({{x.is_unless?}}), \"true\", {x: MacroIf.new(BoolLiteral.new(true), StringLiteral.new(\"test\"), StringLiteral.new(\"foo\"), is_unless: true)}\n        assert_macro %({{x.is_unless?}}), \"false\", {x: MacroIf.new(BoolLiteral.new(false), StringLiteral.new(\"test\"), StringLiteral.new(\"foo\"), is_unless: false)}\n      end\n    end\n\n    describe \"macro for methods\" do\n      it \"executes vars\" do\n        assert_macro %({{x.vars}}), \"[bar]\", {x: MacroFor.new([Var.new(\"bar\")], Var.new(\"foo\"), Call.new(\"puts\", [Var.new(\"bar\")] of ASTNode))}\n      end\n\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), \"foo\", {x: MacroFor.new([Var.new(\"bar\")], Var.new(\"foo\"), Call.new(\"puts\", [Var.new(\"bar\")] of ASTNode))}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"puts(bar)\", {x: MacroFor.new([Var.new(\"bar\")], Var.new(\"foo\"), Call.new(\"puts\", [Var.new(\"bar\")] of ASTNode))}\n      end\n    end\n\n    describe MacroLiteral do\n      it \"executes value\" do\n        assert_macro %({{x.value}}), \"foo(1)\", {x: MacroLiteral.new(\"foo(1)\")}\n        assert_macro %({{x.value}}), \"\", {x: MacroLiteral.new(\"\")}\n      end\n    end\n\n    describe MacroVar do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"foo\", {x: MacroVar.new(\"foo\")}\n      end\n\n      it \"executes expressions\" do\n        assert_macro %({{x.expressions}}), \"[] of ::NoReturn\", {x: MacroVar.new(\"foo\")}\n        assert_macro %({{x.expressions}}), \"[x, 1]\", {x: MacroVar.new(\"bar\", [Var.new(\"x\"), 1.int32] of ASTNode)}\n      end\n    end\n\n    describe \"unary expression methods\" do\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), \"some_call\", {x: Not.new(\"some_call\".call)}\n      end\n\n      it \"executes is_a?\" do\n        assert_macro %({{ x.is_a?(Not) }}), \"true\", {x: Not.new(\"some_call\".call)}\n        assert_macro %({{ x.is_a?(Splat) }}), \"false\", {x: Not.new(\"some_call\".call)}\n        assert_macro %({{ x.is_a?(UnaryExpression) }}), \"true\", {x: Not.new(\"some_call\".call)}\n        assert_macro %({{ x.is_a?(ASTNode) }}), \"true\", {x: Not.new(\"some_call\".call)}\n        assert_macro %({{ x.is_a?(TypeNode) }}), \"false\", {x: Not.new(\"some_call\".call)}\n      end\n    end\n\n    describe \"offsetof methods\" do\n      it \"executes type\" do\n        assert_macro %({{x.type}}), \"SomeType\", {x: OffsetOf.new(\"SomeType\".path, \"@some_ivar\".instance_var)}\n      end\n\n      it \"executes offset\" do\n        assert_macro %({{x.offset}}), \"@some_ivar\", {x: OffsetOf.new(\"SomeType\".path, \"@some_ivar\".instance_var)}\n      end\n    end\n\n    describe Include do\n      foo = Include.new(\"Foo\".path)\n      bar = Include.new(Generic.new(\"Bar\".path, [\"Int32\".path] of ASTNode))\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"Foo\", {x: foo}\n        assert_macro %({{x.name}}), \"Bar(Int32)\", {x: bar}\n      end\n    end\n\n    describe Extend do\n      foo = Extend.new(\"Foo\".path)\n      bar = Extend.new(Generic.new(\"Bar\".path, [\"Int32\".path] of ASTNode))\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"Foo\", {x: foo}\n        assert_macro %({{x.name}}), \"Bar(Int32)\", {x: bar}\n      end\n    end\n\n    describe Alias do\n      node = Alias.new(\"Foo\".path, Generic.new(Path.new([\"Bar\", \"Baz\"], global: true), [\"T\".path] of ASTNode))\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(Foo), {x: node}\n      end\n\n      it \"executes type\" do\n        assert_macro %({{x.type}}), %(::Bar::Baz(T)), {x: node}\n      end\n    end\n\n    describe \"visibility modifier methods\" do\n      node = VisibilityModifier.new(Visibility::Protected, Def.new(\"some_def\"))\n\n      it \"executes visibility\" do\n        assert_macro %({{x.visibility}}), \":protected\", {x: node}\n      end\n\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), \"def some_def\\nend\", {x: node}\n      end\n    end\n\n    describe \"is_a methods\" do\n      node = IsA.new(\"var\".var, Path.new(\"Int32\"))\n\n      it \"executes receiver\" do\n        assert_macro %({{x.receiver}}), \"var\", {x: node}\n      end\n\n      it \"executes arg\" do\n        assert_macro %({{x.arg}}), \"Int32\", {x: node}\n      end\n    end\n\n    describe \"responds_to methods\" do\n      node = RespondsTo.new(\"var\".var, \"to_i\")\n\n      it \"executes receiver\" do\n        assert_macro %({{x.receiver}}), \"var\", {x: node}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(\"to_i\"), {x: node}\n      end\n    end\n\n    describe \"metaclass methods\" do\n      node = Metaclass.new(Path.new(\"Int32\"))\n\n      it \"executes instance\" do\n        assert_macro %({{x.instance}}), \"Int32\", {x: node}\n      end\n\n      it \"executes resolve\" do\n        assert_macro %({{x.resolve}}), %(Int32.class), {x: node}\n        assert_macro %({{x.resolve}}), %(Array(T).class), {x: Metaclass.new(Path.new(\"Array\"))}\n\n        assert_macro_error(%({{x.resolve}}), \"undefined constant Foo\") do\n          {x: Metaclass.new(Path.new(\"Foo\"))}\n        end\n      end\n\n      it \"executes resolve?\" do\n        assert_macro %({{x.resolve?}}), %(Int32.class), {x: node}\n        assert_macro %({{x.resolve?}}), %(Array(T).class), {x: Metaclass.new(Path.new(\"Array\"))}\n        assert_macro %({{x.resolve?}}), %(nil), {x: Metaclass.new(Path.new(\"Foo\"))}\n      end\n    end\n\n    describe \"require methods\" do\n      it \"executes path\" do\n        assert_macro %({{x.path}}), %(\"json\"), {x: Require.new(\"json\")}\n      end\n    end\n\n    describe \"call methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"some_call\", {x: \"some_call\".call}\n      end\n\n      it \"executes args\" do\n        assert_macro %({{x.args}}), \"[1, 3]\", {x: Call.new(\"some_call\", [1.int32, 3.int32] of ASTNode)}\n      end\n\n      it \"executes receiver\" do\n        assert_macro %({{x.receiver}}), \"1\", {x: Call.new(1.int32, \"some_call\")}\n      end\n\n      it \"executes block\" do\n        assert_macro %({{x.block}}), \"do\\nend\", {x: Call.new(1.int32, \"some_call\", block: Block.new)}\n      end\n\n      it \"executes block arg\" do\n        assert_macro %({{x.block_arg}}), \"bl\", {x: Call.new(1.int32, \"some_call\", block_arg: \"bl\".arg)}\n      end\n\n      it \"executes block arg (nop)\" do\n        assert_macro %({{x.block_arg}}), \"\", {x: Call.new(1.int32, \"some_call\")}\n      end\n\n      it \"executes named args\" do\n        assert_macro %({{x.named_args}}), \"[a: 1, b: 2]\", {x: Call.new(1.int32, \"some_call\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])}\n      end\n\n      it \"executes named args name\" do\n        assert_macro %({{x.named_args[0].name}}), \"a\", {x: Call.new(1.int32, \"some_call\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])}\n      end\n\n      it \"executes named args value\" do\n        assert_macro %({{x.named_args[0].value}}), \"1\", {x: Call.new(1.int32, \"some_call\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])}\n      end\n\n      it \"executes global?\" do\n        assert_macro %({{x.global?}}), \"false\", {x: Call.new(1.int32, \"some_call\")}\n        assert_macro %({{x.global?}}), \"true\", {x: Call.new(\"some_call\", global: true)}\n      end\n    end\n\n    describe \"arg methods\" do\n      it \"executes name\" do\n        arg = \"into\".arg\n        assert_macro %({{x.name}}), \"into\", {x: arg}\n        arg.name = \"array\" # internal\n        assert_macro %({{x.name}}), \"into\", {x: arg}\n      end\n\n      it \"executes internal_name\" do\n        arg = \"into\".arg\n        assert_macro %({{x.internal_name}}), \"into\", {x: arg}\n        arg.name = \"array\"\n        assert_macro %({{x.internal_name}}), \"array\", {x: arg}\n      end\n\n      it \"executes default_value\" do\n        assert_macro %({{x.default_value}}), \"1\", {x: \"some_arg\".arg(default_value: 1.int32)}\n      end\n\n      it \"executes restriction\" do\n        assert_macro %({{x.restriction}}), \"T\", {x: \"some_arg\".arg(restriction: \"T\".path)}\n      end\n    end\n\n    describe \"cast methods\" do\n      it \"executes obj\" do\n        assert_macro %({{x.obj}}), \"x\", {x: Cast.new(\"x\".call, \"Int32\".path)}\n      end\n\n      it \"executes to\" do\n        assert_macro %({{x.to}}), \"Int32\", {x: Cast.new(\"x\".call, \"Int32\".path)}\n      end\n    end\n\n    describe \"nilable cast methods\" do\n      it \"executes obj\" do\n        assert_macro %({{x.obj}}), \"x\", {x: NilableCast.new(\"x\".call, \"Int32\".path)}\n      end\n\n      it \"executes to\" do\n        assert_macro %({{x.to}}), \"Int32\", {x: NilableCast.new(\"x\".call, \"Int32\".path)}\n      end\n    end\n\n    describe TypeOf do\n      it \"executes args\" do\n        assert_macro %({{x.args}}), \"[1, 'a', Foo]\", {x: TypeOf.new([1.int32, CharLiteral.new('a'), \"Foo\".path])}\n      end\n    end\n\n    describe \"case methods\" do\n      describe \"when\" do\n        case_node = Case.new(1.int32, [When.new([2.int32, 3.int32] of ASTNode, 4.int32)], 5.int32, exhaustive: false)\n\n        it \"executes cond\" do\n          assert_macro %({{x.cond}}), \"1\", {x: case_node}\n        end\n\n        it \"executes whens\" do\n          assert_macro %({{x.whens}}), \"[when 2, 3\\n  4\\n]\", {x: case_node}\n        end\n\n        it \"executes when conds\" do\n          assert_macro %({{x.whens[0].conds}}), \"[2, 3]\", {x: case_node}\n        end\n\n        it \"executes when body\" do\n          assert_macro %({{x.whens[0].body}}), \"4\", {x: case_node}\n        end\n\n        it \"executes when exhaustive?\" do\n          assert_macro %({{x.whens[0].exhaustive?}}), \"false\", {x: case_node}\n        end\n\n        it \"executes else\" do\n          assert_macro %({{x.else}}), \"5\", {x: case_node}\n        end\n\n        it \"executes exhaustive?\" do\n          assert_macro %({{x.exhaustive?}}), \"false\", {x: case_node}\n        end\n      end\n\n      describe \"in\" do\n        case_node = Case.new(1.int32, [When.new([2.int32, 3.int32] of ASTNode, 4.int32)], 5.int32, exhaustive: true)\n\n        it \"executes whens\" do\n          assert_macro %({{x.whens}}), \"[in 2, 3\\n  4\\n]\", {x: case_node}\n        end\n\n        it \"executes when exhaustive?\" do\n          assert_macro %({{x.whens[0].exhaustive?}}), \"true\", {x: case_node}\n        end\n\n        it \"executes exhaustive?\" do\n          assert_macro %({{x.exhaustive?}}), \"true\", {x: case_node}\n        end\n      end\n    end\n\n    describe Select do\n      it \"executes whens\" do\n        assert_macro %({{x.whens}}), \"[when foo\\n  1\\n]\", {x: Select.new([When.new(\"foo\".call, 1.int32)])}\n        assert_macro %({{x.whens}}), \"[when x = y\\n  1\\n, when bar\\n]\", {x: Select.new([When.new(Assign.new(\"x\".var, \"y\".var), 1.int32), When.new(\"bar\".call)])}\n      end\n\n      it \"executes else\" do\n        assert_macro %({{x.else}}), \"\", {x: Select.new([When.new(\"foo\".call)])}\n        assert_macro %({{x.else}}), \"1\", {x: Select.new([When.new(\"foo\".call)], 1.int32)}\n        assert_macro %({{x.else}}), \"nil\", {x: Select.new([When.new(\"foo\".call)], NilLiteral.new)}\n      end\n    end\n\n    describe \"if methods\" do\n      if_node = If.new(1.int32, 2.int32, 3.int32)\n\n      it \"executes cond\" do\n        assert_macro %({{x.cond}}), \"1\", {x: if_node}\n      end\n\n      it \"executes then\" do\n        assert_macro %({{x.then}}), \"2\", {x: if_node}\n      end\n\n      it \"executes else\" do\n        assert_macro %({{x.else}}), \"3\", {x: if_node}\n      end\n\n      it \"executes else (nop)\" do\n        assert_macro %({{x.else}}), \"\", {x: If.new(1.int32, 2.int32)}\n      end\n    end\n\n    describe \"while methods\" do\n      while_node = While.new(1.int32, 2.int32)\n\n      it \"executes cond\" do\n        assert_macro %({{x.cond}}), \"1\", {x: while_node}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"2\", {x: while_node}\n      end\n    end\n\n    describe \"control expression methods\" do\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), \"1\", {x: Break.new(1.int32)}\n        assert_macro %({{x.exp}}), \"1\", {x: Next.new(1.int32)}\n        assert_macro %({{x.exp}}), \"1\", {x: Return.new(1.int32)}\n      end\n\n      it \"executes exp (nop)\" do\n        assert_macro %({{x.exp}}), \"\", {x: Break.new}\n        assert_macro %({{x.exp}}), \"\", {x: Next.new}\n        assert_macro %({{x.exp}}), \"\", {x: Return.new}\n      end\n    end\n\n    describe \"yield methods\" do\n      it \"executes expressions\" do\n        assert_macro %({{x.expressions}}), \"[]\", {x: Yield.new}\n        assert_macro %({{x.expressions}}), \"[1]\", {x: Yield.new([1.int32] of ASTNode)}\n        assert_macro %({{x.expressions}}), \"[1, 2]\", {x: Yield.new([1.int32, 2.int32] of ASTNode)}\n      end\n\n      it \"executes scope\" do\n        assert_macro %({{x.scope}}), \"1\", {x: Yield.new(scope: 1.int32)}\n        assert_macro %({{x.scope}}), \"nil\", {x: Yield.new(scope: NilLiteral.new)}\n      end\n\n      it \"executes scope (nop)\" do\n        assert_macro %({{x.scope}}), \"\", {x: Yield.new}\n      end\n    end\n\n    describe \"exception handler methods\" do\n      # begin\n      #   1\n      # rescue ex : Int32\n      #   2\n      # rescue Char | String\n      # else\n      #   3\n      # ensure\n      #   4\n      # end\n      begin_node = ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [\"Int32\".path] of ASTNode, \"ex\"), Rescue.new(Nop.new, [\"Char\".path, \"String\".path] of ASTNode)], 3.int32, 4.int32)\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), \"1\", {x: begin_node}\n      end\n\n      it \"executes rescues\" do\n        assert_macro %({{x.rescues}}), \"[rescue ex : Int32\\n  2\\n, rescue Char | String\\n]\", {x: begin_node}\n      end\n\n      it \"executes rescue body\" do\n        assert_macro %({{x.rescues[0].body}}), \"2\", {x: begin_node}\n        assert_macro %({{x.rescues[1].body}}), \"\", {x: begin_node}\n      end\n\n      it \"executes rescue types\" do\n        assert_macro %({{x.rescues[0].types}}), \"[Int32]\", {x: begin_node}\n        assert_macro %({{x.rescues[1].types}}), \"[Char, String]\", {x: begin_node}\n        assert_macro %({{x.types}}), \"nil\", {x: Rescue.new(1.int32)}\n      end\n\n      it \"executes rescue name\" do\n        assert_macro %({{x.rescues[0].name}}), \"ex\", {x: begin_node}\n        assert_macro %({{x.rescues[1].name}}), \"\", {x: begin_node}\n      end\n\n      it \"executes else\" do\n        assert_macro %({{x.else}}), \"3\", {x: begin_node}\n      end\n\n      it \"executes else (nop)\" do\n        assert_macro %({{x.else}}), \"\", {x: ExceptionHandler.new(Nop.new)}\n      end\n\n      it \"executes ensure\" do\n        assert_macro %({{x.ensure}}), \"4\", {x: begin_node}\n      end\n\n      it \"executes ensure (nop)\" do\n        assert_macro %({{x.ensure}}), \"\", {x: ExceptionHandler.new(Nop.new)}\n      end\n    end\n\n    describe \"assign methods\" do\n      it \"executes target\" do\n        assert_macro %({{x.target}}), \"foo\", {x: Assign.new(\"foo\".var, 2.int32)}\n      end\n\n      it \"executes value\" do\n        assert_macro %({{x.value}}), \"2\", {x: Assign.new(\"foo\".var, 2.int32)}\n      end\n    end\n\n    describe \"multi_assign methods\" do\n      multi_assign_node = MultiAssign.new([\"foo\".var, \"bar\".var] of ASTNode, [2.int32, \"a\".string] of ASTNode)\n\n      it \"executes targets\" do\n        assert_macro %({{x.targets}}), %([foo, bar]), {x: multi_assign_node}\n      end\n\n      it \"executes values\" do\n        assert_macro %({{x.values}}), %([2, \"a\"]), {x: multi_assign_node}\n      end\n    end\n\n    describe \"instancevar methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(ivar), {x: InstanceVar.new(\"ivar\")}\n      end\n    end\n\n    describe \"instancevar methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(ivar), {x: InstanceVar.new(\"ivar\")}\n      end\n    end\n\n    describe \"readinstancevar methods\" do\n      it \"executes obj\" do\n        assert_macro %({{x.obj}}), %(obj), {x: ReadInstanceVar.new(\"obj\".var, \"ivar\")}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(ivar), {x: ReadInstanceVar.new(\"obj\".var, \"ivar\")}\n      end\n    end\n\n    describe \"classvar methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(cvar), {x: ClassVar.new(\"cvar\")}\n      end\n    end\n\n    describe \"global methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(gvar), {x: Global.new(\"gvar\")}\n      end\n    end\n\n    describe \"splat methods\" do\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), \"2\", {x: 2.int32.splat}\n      end\n    end\n\n    describe \"generic methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), \"Foo\", {x: Generic.new(\"Foo\".path, [\"T\".path] of ASTNode)}\n      end\n\n      it \"executes type_vars\" do\n        assert_macro %({{x.type_vars}}), \"[T, U]\", {x: Generic.new(\"Foo\".path, [\"T\".path, \"U\".path] of ASTNode)}\n        assert_macro %({{x.type_vars}}), \"[]\", {x: Generic.new(\"Foo\".path, [] of ASTNode)}\n      end\n\n      it \"executes named_args\" do\n        assert_macro %({{x.named_args}}), \"{x: U, y: V}\", {x: Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"U\".path), NamedArgument.new(\"y\", \"V\".path)])}\n      end\n\n      it \"executes resolve\" do\n        assert_macro %({{x.resolve}}), %(Array(String)), {x: Generic.new(\"Array\".path, [\"String\".path] of ASTNode)}\n\n        assert_macro_error %({{x.resolve}}), \"undefined constant Foo\", {x: Generic.new(\"Foo\".path, [\"String\".path] of ASTNode)}\n        assert_macro_error %({{x.resolve}}), \"undefined constant Foo\", {x: Generic.new(\"Array\".path, [\"Foo\".path] of ASTNode)}\n      end\n\n      it \"executes resolve?\" do\n        assert_macro %({{x.resolve?}}), %(Array(String)), {x: Generic.new(\"Array\".path, [\"String\".path] of ASTNode)}\n        assert_macro %({{x.resolve?}}), %(nil), {x: Generic.new(\"Foo\".path, [\"String\".path] of ASTNode)}\n        assert_macro %({{x.resolve?}}), %(nil), {x: Generic.new(\"Array\".path, [\"Foo\".path] of ASTNode)}\n      end\n\n      it \"executes types\" do\n        assert_macro %({{x.types}}), \"[Foo(T)]\", {x: Generic.new(\"Foo\".path, [\"T\".path] of ASTNode)}\n      end\n    end\n\n    describe \"union methods\" do\n      it \"executes types\" do\n        assert_macro %({{x.types}}), \"[Int32, String]\", {x: Crystal::Union.new([\"Int32\".path, \"String\".path] of ASTNode)}\n      end\n\n      it \"executes resolve\" do\n        assert_macro %({{x.resolve}}), \"(Int32 | String)\", {x: Crystal::Union.new([\"Int32\".path, \"String\".path] of ASTNode)}\n      end\n\n      it \"executes resolve?\" do\n        assert_macro %({{x.resolve?}}), \"(Int32 | String)\", {x: Crystal::Union.new([\"Int32\".path, \"String\".path] of ASTNode)}\n        assert_macro %({{x.resolve?}}), \"nil\", {x: Crystal::Union.new([\"Int32\".path, \"Unknown\".path] of ASTNode)}\n      end\n    end\n\n    describe RangeLiteral do\n      it \"executes begin\" do\n        assert_macro %({{x.begin}}), \"1\", {x: RangeLiteral.new(1.int32, 2.int32, true)}\n      end\n\n      it \"executes end\" do\n        assert_macro %({{x.end}}), \"2\", {x: RangeLiteral.new(1.int32, 2.int32, true)}\n      end\n\n      it \"executes excludes_end?\" do\n        assert_macro %({{x.excludes_end?}}), \"true\", {x: RangeLiteral.new(1.int32, 2.int32, true)}\n      end\n\n      it \"executes map\" do\n        assert_macro %({{x.map(&.stringify)}}), %([\"1\", \"2\", \"3\"]), {x: RangeLiteral.new(1.int32, 3.int32, false)}\n        assert_macro %({{x.map(&.stringify)}}), %([\"1\", \"2\"]), {x: RangeLiteral.new(1.int32, 3.int32, true)}\n      end\n\n      it \"executes to_a\" do\n        assert_macro %({{x.to_a}}), %([1, 2, 3]), {x: RangeLiteral.new(1.int32, 3.int32, false)}\n        assert_macro %({{x.to_a}}), %([1, 2]), {x: RangeLiteral.new(1.int32, 3.int32, true)}\n      end\n\n      it \"#each\" do\n        assert_macro(\n          %({% begin %}{% values = [] of Nil %}{% (1..3).each { |v| values << v } %}{{values}}{% end %}),\n          %([1, 2, 3])\n        )\n      end\n    end\n\n    describe \"path methods\" do\n      it \"executes names\" do\n        assert_macro %({{x.names}}), %([String]), {x: Path.new(\"String\")}\n        assert_macro %({{x.names}}), %([Foo, Bar]), {x: Path.new(\"Foo\", \"Bar\")}\n      end\n\n      it \"executes global?\" do\n        assert_macro %({{x.global?}}), %(false), {x: Path.new(\"Foo\")}\n        assert_macro %({{x.global?}}), %(true), {x: Path.new(\"Foo\", global: true)}\n      end\n\n      # TODO: remove deprecated tests\n      it \"executes global\" do\n        assert_macro %({{x.global}}), %(false), {x: Path.new(\"Foo\")}\n        assert_macro %({{x.global}}), %(true), {x: Path.new(\"Foo\", global: true)}\n      end\n\n      it \"executes resolve\" do\n        assert_macro %({{x.resolve}}), %(String), {x: Path.new(\"String\")}\n\n        assert_macro_error %({{x.resolve}}), \"undefined constant Foo\", {x: Path.new(\"Foo\")}\n      end\n\n      it \"executes resolve?\" do\n        assert_macro %({{x.resolve?}}), %(String), {x: Path.new(\"String\")}\n        assert_macro %({{x.resolve?}}), %(nil), {x: Path.new(\"Foo\")}\n      end\n\n      it \"executes types\" do\n        assert_macro %({{x.types}}), %([String]), {x: Path.new(\"String\")}\n      end\n    end\n\n    describe \"annotation methods\" do\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(Foo), {x: Annotation.new(Path.new(\"Foo\"))}\n        assert_macro %({{x.name}}), %(Foo::Bar), {x: Annotation.new(Path.new(\"Foo\", \"Bar\"))}\n      end\n\n      it \"executes [] with NumberLiteral\" do\n        assert_macro %({{x[y]}}), %(42), {\n          x: Annotation.new(Path.new(\"Foo\"), [42.int32] of ASTNode),\n          y: 0.int32,\n        }\n      end\n\n      it \"executes [] with SymbolLiteral\" do\n        assert_macro %({{x[y]}}), %(42), {\n          x: Annotation.new(Path.new(\"Foo\"), [] of ASTNode, [NamedArgument.new(\"foo\", 42.int32)]),\n          y: \"foo\".symbol,\n        }\n      end\n\n      it \"executes [] with StringLiteral\" do\n        assert_macro %({{x[y]}}), %(42), {\n          x: Annotation.new(Path.new(\"Foo\"), [] of ASTNode, [NamedArgument.new(\"foo\", 42.int32)]),\n          y: \"foo\".string,\n        }\n      end\n\n      it \"executes [] with MacroId\" do\n        assert_macro %({{x[y]}}), %(42), {\n          x: Annotation.new(Path.new(\"Foo\"), [] of ASTNode, [NamedArgument.new(\"foo\", 42.int32)]),\n          y: MacroId.new(\"foo\"),\n        }\n      end\n\n      it \"executes [] with other ASTNode, but raises an error\" do\n        assert_macro_error %({{x[y]}}), \"argument to [] must be a number, symbol or string, not BoolLiteral\", {\n          x: Annotation.new(Path.new(\"Foo\"), [] of ASTNode),\n          y: true.bool,\n        }\n      end\n    end\n\n    describe ClassDef do\n      class_def = ClassDef.new(Path.new(\"Foo\"), abstract: true, superclass: Path.new(\"Parent\"))\n      struct_def = ClassDef.new(Path.new(\"Foo\", \"Bar\", global: true), type_vars: %w(A B C D), splat_index: 2, struct: true, body: CharLiteral.new('a'))\n\n      it \"executes kind\" do\n        assert_macro %({{x.kind}}), %(class), {x: class_def}\n        assert_macro %({{x.kind}}), %(struct), {x: struct_def}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(Foo), {x: class_def}\n        assert_macro %({{x.name}}), %(::Foo::Bar(A, B, *C, D)), {x: struct_def}\n\n        assert_macro %({{x.name(generic_args: true)}}), %(Foo), {x: class_def}\n        assert_macro %({{x.name(generic_args: true)}}), %(::Foo::Bar(A, B, *C, D)), {x: struct_def}\n\n        assert_macro %({{x.name(generic_args: false)}}), %(Foo), {x: class_def}\n        assert_macro %({{x.name(generic_args: false)}}), %(::Foo::Bar), {x: struct_def}\n\n        assert_macro_error %({{x.name(generic_args: 99)}}), \"named argument 'generic_args' to ClassDef#name must be a BoolLiteral, not NumberLiteral\", {x: class_def}\n      end\n\n      it \"executes superclass\" do\n        assert_macro %({{x.superclass}}), %(Parent), {x: class_def}\n        assert_macro %({{x.superclass}}), %(Parent(*T)), {x: ClassDef.new(Path.new(\"Foo\"), superclass: Generic.new(Path.new(\"Parent\"), [Splat.new(Path.new(\"T\"))] of ASTNode))}\n        assert_macro %({{x.superclass}}), %(), {x: struct_def}\n      end\n\n      it \"executes type_vars\" do\n        assert_macro %({{x.type_vars}}), %([] of ::NoReturn), {x: class_def}\n        assert_macro %({{x.type_vars}}), %([A, B, C, D]), {x: struct_def}\n      end\n\n      it \"executes splat_index\" do\n        assert_macro %({{x.splat_index}}), %(nil), {x: class_def}\n        assert_macro %({{x.splat_index}}), %(2), {x: struct_def}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(), {x: class_def}\n        assert_macro %({{x.body}}), %('a'), {x: struct_def}\n      end\n\n      it \"executes abstract?\" do\n        assert_macro %({{x.abstract?}}), %(true), {x: class_def}\n        assert_macro %({{x.abstract?}}), %(false), {x: struct_def}\n      end\n\n      it \"executes struct?\" do\n        assert_macro %({{x.struct?}}), %(false), {x: class_def}\n        assert_macro %({{x.struct?}}), %(true), {x: struct_def}\n      end\n    end\n\n    describe ModuleDef do\n      module_def1 = ModuleDef.new(Path.new(\"Foo\"))\n      module_def2 = ModuleDef.new(Path.new(\"Foo\", \"Bar\", global: true), type_vars: %w(A B C D), splat_index: 2, body: CharLiteral.new('a'))\n\n      it \"executes kind\" do\n        assert_macro %({{x.kind}}), %(module), {x: module_def1}\n        assert_macro %({{x.kind}}), %(module), {x: module_def2}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(Foo), {x: module_def1}\n        assert_macro %({{x.name}}), %(::Foo::Bar(A, B, *C, D)), {x: module_def2}\n\n        assert_macro %({{x.name(generic_args: true)}}), %(Foo), {x: module_def1}\n        assert_macro %({{x.name(generic_args: true)}}), %(::Foo::Bar(A, B, *C, D)), {x: module_def2}\n\n        assert_macro %({{x.name(generic_args: false)}}), %(Foo), {x: module_def1}\n        assert_macro %({{x.name(generic_args: false)}}), %(::Foo::Bar), {x: module_def2}\n\n        assert_macro_error %({{x.name(generic_args: 99)}}), \"named argument 'generic_args' to ModuleDef#name must be a BoolLiteral, not NumberLiteral\", {x: module_def1}\n      end\n\n      it \"executes type_vars\" do\n        assert_macro %({{x.type_vars}}), %([] of ::NoReturn), {x: module_def1}\n        assert_macro %({{x.type_vars}}), %([A, B, C, D]), {x: module_def2}\n      end\n\n      it \"executes splat_index\" do\n        assert_macro %({{x.splat_index}}), %(nil), {x: module_def1}\n        assert_macro %({{x.splat_index}}), %(2), {x: module_def2}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(), {x: module_def1}\n        assert_macro %({{x.body}}), %('a'), {x: module_def2}\n      end\n    end\n\n    describe EnumDef do\n      enum_def = EnumDef.new(Path.new(\"Foo\", \"Bar\", global: true), [Path.new(\"X\")] of ASTNode, Path.global(\"Int32\"))\n\n      it \"executes kind\" do\n        assert_macro %({{x.kind}}), %(enum), {x: enum_def}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(::Foo::Bar), {x: enum_def}\n        assert_macro %({{x.name(generic_args: true)}}), %(::Foo::Bar), {x: enum_def}\n        assert_macro %({{x.name(generic_args: false)}}), %(::Foo::Bar), {x: enum_def}\n        assert_macro_error %({{x.name(generic_args: 99)}}), \"named argument 'generic_args' to EnumDef#name must be a BoolLiteral, not NumberLiteral\", {x: enum_def}\n      end\n\n      it \"executes base_type\" do\n        assert_macro %({{x.base_type}}), %(::Int32), {x: enum_def}\n        assert_macro %({{x.base_type}}), %(), {x: EnumDef.new(Path.new(\"Baz\"))}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(X), {x: enum_def}\n      end\n    end\n\n    describe AnnotationDef do\n      annotation_def = AnnotationDef.new(Path.new(\"Foo\", \"Bar\", global: true))\n\n      it \"executes kind\" do\n        assert_macro %({{x.kind}}), %(annotation), {x: annotation_def}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(::Foo::Bar), {x: annotation_def}\n        assert_macro %({{x.name(generic_args: true)}}), %(::Foo::Bar), {x: annotation_def}\n        assert_macro %({{x.name(generic_args: false)}}), %(::Foo::Bar), {x: annotation_def}\n        assert_macro_error %({{x.name(generic_args: 99)}}), \"named argument 'generic_args' to AnnotationDef#name must be a BoolLiteral, not NumberLiteral\", {x: annotation_def}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(), {x: annotation_def}\n      end\n    end\n\n    describe LibDef do\n      lib_def = LibDef.new(Path.new(\"Foo\", \"Bar\", global: true), FunDef.new(\"foo\"))\n\n      it \"executes kind\" do\n        assert_macro %({{x.kind}}), %(lib), {x: lib_def}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(::Foo::Bar), {x: lib_def}\n        assert_macro %({{x.name(generic_args: true)}}), %(::Foo::Bar), {x: lib_def}\n        assert_macro %({{x.name(generic_args: false)}}), %(::Foo::Bar), {x: lib_def}\n        assert_macro_error %({{x.name(generic_args: 99)}}), \"named argument 'generic_args' to LibDef#name must be a BoolLiteral, not NumberLiteral\", {x: lib_def}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(fun foo), {x: lib_def}\n      end\n    end\n\n    describe CStructOrUnionDef do\n      c_struct_def = CStructOrUnionDef.new(\"Foo\", TypeDeclaration.new(\"x\".var, \"Int\".path))\n      c_union_def = CStructOrUnionDef.new(\"Bar\", Include.new(\"Foo\".path), union: true)\n\n      it \"executes kind\" do\n        assert_macro %({{x.kind}}), %(struct), {x: c_struct_def}\n        assert_macro %({{x.kind}}), %(union), {x: c_union_def}\n      end\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(Foo), {x: c_struct_def}\n        assert_macro %({{x.name(generic_args: true)}}), %(Foo), {x: c_struct_def}\n        assert_macro %({{x.name(generic_args: false)}}), %(Foo), {x: c_struct_def}\n        assert_macro_error %({{x.name(generic_args: 99)}}), \"named argument 'generic_args' to CStructOrUnionDef#name must be a BoolLiteral, not NumberLiteral\", {x: c_struct_def}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(x : Int), {x: c_struct_def}\n        assert_macro %({{x.body}}), %(include Foo), {x: c_union_def}\n      end\n\n      it \"executes union?\" do\n        assert_macro %({{x.union?}}), %(false), {x: c_struct_def}\n        assert_macro %({{x.union?}}), %(true), {x: c_union_def}\n      end\n    end\n\n    describe FunDef do\n      lib_fun = FunDef.new(\"foo\")\n      top_level_fun = FunDef.new(\"bar\", [Arg.new(\"x\", restriction: \"Int32\".path), Arg.new(\"\", restriction: \"Char\".path)], \"Void\".path, true, 1.int32, \"y.z\")\n      top_level_fun2 = FunDef.new(\"baz\", body: Nop.new)\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(foo), {x: lib_fun}\n        assert_macro %({{x.name}}), %(bar), {x: top_level_fun}\n      end\n\n      it \"executes real_name\" do\n        assert_macro %({{x.real_name}}), %(), {x: lib_fun}\n        assert_macro %({{x.real_name}}), %(\"y.z\"), {x: top_level_fun}\n      end\n\n      it \"executes args\" do\n        assert_macro %({{x.args}}), %([]), {x: lib_fun}\n        assert_macro %({{x.args}}), %([x : Int32,  : Char]), {x: top_level_fun}\n      end\n\n      it \"executes variadic?\" do\n        assert_macro %({{x.variadic?}}), %(false), {x: lib_fun}\n        assert_macro %({{x.variadic?}}), %(true), {x: top_level_fun}\n      end\n\n      it \"executes return_type\" do\n        assert_macro %({{x.return_type}}), %(), {x: lib_fun}\n        assert_macro %({{x.return_type}}), %(Void), {x: top_level_fun}\n      end\n\n      it \"executes body\" do\n        assert_macro %({{x.body}}), %(), {x: lib_fun}\n        assert_macro %({{x.body}}), %(1), {x: top_level_fun}\n        assert_macro %({{x.body}}), %(), {x: top_level_fun2}\n      end\n\n      it \"executes has_body?\" do\n        assert_macro %({{x.has_body?}}), %(false), {x: lib_fun}\n        assert_macro %({{x.has_body?}}), %(true), {x: top_level_fun}\n        assert_macro %({{x.has_body?}}), %(true), {x: top_level_fun2}\n      end\n    end\n\n    describe TypeDef do\n      type_def = TypeDef.new(\"Foo\", Path.new(\"Bar\", \"Baz\", global: true))\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(Foo), {x: type_def}\n      end\n\n      it \"executes type\" do\n        assert_macro %({{x.type}}), %(::Bar::Baz), {x: type_def}\n      end\n    end\n\n    describe ExternalVar do\n      external_var1 = ExternalVar.new(\"foo\", Path.new(\"Bar\", \"Baz\"))\n      external_var2 = ExternalVar.new(\"X\", Generic.new(Path.global(\"Pointer\"), [\"Char\".path] of ASTNode), real_name: \"y.z\")\n\n      it \"executes name\" do\n        assert_macro %({{x.name}}), %(foo), {x: external_var1}\n        assert_macro %({{x.name}}), %(X), {x: external_var2}\n      end\n\n      it \"executes real_name\" do\n        assert_macro %({{x.real_name}}), %(), {x: external_var1}\n        assert_macro %({{x.real_name}}), %(\"y.z\"), {x: external_var2}\n      end\n\n      it \"executes type\" do\n        assert_macro %({{x.type}}), %(Bar::Baz), {x: external_var1}\n        assert_macro %({{x.type}}), %(::Pointer(Char)), {x: external_var2}\n      end\n    end\n\n    describe Asm do\n      asm1 = Asm.new(\"nop\")\n      asm2 = Asm.new(\n        text: \"foo\",\n        outputs: [AsmOperand.new(\"=r\", \"x\".var), AsmOperand.new(\"=r\", \"y\".var)],\n        inputs: [AsmOperand.new(\"i\", 1.int32), AsmOperand.new(\"r\", 2.int32)],\n        clobbers: %w(rax memory),\n        volatile: true,\n        alignstack: true,\n        intel: true,\n        can_throw: true,\n      )\n\n      it \"executes text\" do\n        assert_macro %({{x.text}}), %(\"nop\"), {x: asm1}\n        assert_macro %({{x.text}}), %(\"foo\"), {x: asm2}\n      end\n\n      it \"executes outputs\" do\n        assert_macro %({{x.outputs}}), %([] of ::NoReturn), {x: asm1}\n        assert_macro %({{x.outputs}}), %([\"=r\"(x), \"=r\"(y)]), {x: asm2}\n      end\n\n      it \"executes inputs\" do\n        assert_macro %({{x.inputs}}), %([] of ::NoReturn), {x: asm1}\n        assert_macro %({{x.inputs}}), %([\"i\"(1), \"r\"(2)]), {x: asm2}\n      end\n\n      it \"executes clobbers\" do\n        assert_macro %({{x.clobbers}}), %([] of ::NoReturn), {x: asm1}\n        assert_macro %({{x.clobbers}}), %([\"rax\", \"memory\"]), {x: asm2}\n      end\n\n      it \"executes volatile?\" do\n        assert_macro %({{x.volatile?}}), %(false), {x: asm1}\n        assert_macro %({{x.volatile?}}), %(true), {x: asm2}\n      end\n\n      it \"executes alignstack?\" do\n        assert_macro %({{x.alignstack?}}), %(false), {x: asm1}\n        assert_macro %({{x.alignstack?}}), %(true), {x: asm2}\n      end\n\n      it \"executes intel?\" do\n        assert_macro %({{x.intel?}}), %(false), {x: asm1}\n        assert_macro %({{x.intel?}}), %(true), {x: asm2}\n      end\n\n      it \"executes can_throw?\" do\n        assert_macro %({{x.can_throw?}}), %(false), {x: asm1}\n        assert_macro %({{x.can_throw?}}), %(true), {x: asm2}\n      end\n    end\n\n    describe AsmOperand do\n      asm_operand1 = AsmOperand.new(\"=r\", \"x\".var)\n      asm_operand2 = AsmOperand.new(\"i\", 1.int32)\n\n      it \"executes constraint\" do\n        assert_macro %({{x.constraint}}), %(\"=r\"), {x: asm_operand1}\n        assert_macro %({{x.constraint}}), %(\"i\"), {x: asm_operand2}\n      end\n\n      it \"executes exp\" do\n        assert_macro %({{x.exp}}), %(x), {x: asm_operand1}\n        assert_macro %({{x.exp}}), %(1), {x: asm_operand2}\n      end\n    end\n\n    describe \"env\" do\n      it \"has key\" do\n        with_env(\"FOO\": \"foo\") do\n          assert_macro %({{env(\"FOO\")}}), %(\"foo\")\n        end\n      end\n\n      it \"doesn't have key\" do\n        with_env(\"FOO\": nil) do\n          assert_macro %({{env(\"FOO\")}}), %(nil)\n        end\n      end\n    end\n\n    describe \"flag?\" do\n      it \"has simple flag\" do\n        assert_macro %({{flag?(:foo)}}), %(true), flags: \"foo\"\n      end\n\n      it \"doesn't have flag\" do\n        assert_macro %({{flag?(:foo)}}), %(false)\n      end\n\n      it \"has flag value\" do\n        assert_macro %({{flag?(:foo)}}), %(\"bar\"), flags: \"foo=bar\"\n      end\n\n      it \"has empty flag value\" do\n        assert_macro %({{flag?(:foo)}}), %(\"\"), flags: \"foo=\"\n      end\n\n      it \"uses last one of multiple values\" do\n        assert_macro %({{flag?(:foo)}}), %(\"baz\"), flags: %w[foo=bar foo=baz]\n        assert_macro %({{flag?(:foo)}}), %(\"bar\"), flags: %w[foo=baz foo=bar]\n      end\n\n      describe \"presents `name=value` as simple flag\" do\n        it \"foo=bar\" do\n          assert_macro %({{flag?(:\"foo=bar\")}}), %(true), flags: \"foo=bar\"\n        end\n\n        it \"foo=\" do\n          assert_macro %({{flag?(:foo=)}}), %(true), flags: \"foo=\"\n        end\n\n        it \"multiple values\" do\n          assert_macro %({{flag?(:\"foo=bar\")}}), %(true), flags: %w[foo=baz foo=bar]\n          assert_macro %({{flag?(:\"foo=baz\")}}), %(true), flags: %w[foo=bar foo=baz]\n          assert_macro %({{flag?(:\"foo=bar\")}}), %(true), flags: %w[foo=bar foo=baz]\n          assert_macro %({{flag?(:\"foo=baz\")}}), %(true), flags: %w[foo=baz foo=bar]\n        end\n\n        it \"multiple values and simple flag\" do\n          assert_macro %({{flag?(:\"foo=bar\")}}), %(true), flags: %w[foo=bar foo]\n          assert_macro %({{flag?(:\"foo=bar\")}}), %(true), flags: %w[foo foo=bar]\n        end\n      end\n\n      it \"uses last one of multiple values and simple\" do\n        assert_macro %({{flag?(:foo)}}), %(true), flags: %w[foo=bar foo]\n        assert_macro %({{flag?(:foo)}}), %(\"bar\"), flags: %w[foo foo=bar]\n      end\n    end\n\n    it \"compares versions\" do\n      assert_macro %({{compare_versions(\"1.10.3\", \"1.2.3\")}}), %(1)\n    end\n\n    describe \"#warning\" do\n      it \"emits a top level warning\" do\n        assert_warning <<-CRYSTAL, \"Oh noes\"\n          macro test\n            {% warning \"Oh noes\" %}\n          end\n\n          test\n        CRYSTAL\n      end\n    end\n\n    describe \"#parse_type\" do\n      it \"path\" do\n        assert_type(%[class Bar; end; {{ parse_type(\"Bar\").is_a?(Path) ? 1 : 'a'}}]) { int32 }\n        assert_type(%[class Bar; end; {{ parse_type(:Bar.id.stringify).is_a?(Path) ? 1 : 'a'}}]) { int32 }\n      end\n\n      it \"generic\" do\n        assert_type(%[class Foo(A, B); end; {{ parse_type(\"Foo(Int32, String)\").resolve.type_vars.size == 2 ? 1 : 'a' }}]) { int32 }\n      end\n\n      it \"union - |\" do\n        assert_type(%[class Foo; end; class Bar; end; {{ parse_type(\"Foo|Bar\").resolve.union_types.size == 2 ? 1 : 'a' }}]) { int32 }\n      end\n\n      it \"union - Union\" do\n        assert_type(%[class Foo; end; class Bar; end; {{ parse_type(\"Union(Foo,Bar)\").resolve.union_types.size == 2 ? 1 : 'a' }}]) { int32 }\n      end\n\n      it \"union - in generic\" do\n        assert_type(%[{{ parse_type(\"Array(Int32 | String)\").resolve.type_vars[0].union_types.size == 2 ? 1 : 'a' }}]) { int32 }\n      end\n\n      it \"proc\" do\n        assert_type(%[{{ parse_type(\"String, Int32 -> Bool\").inputs.size == 2 ? 1 : 'a' }}]) { int32 }\n        assert_type(%[{{ parse_type(\"String, Int32 -> Bool\").output.resolve == Bool ? 1 : 'a' }}]) { int32 }\n      end\n\n      it \"metaclass\" do\n        assert_type(%[{{ parse_type(\"Int32.class\").resolve == Int32.class ? 1 : 'a' }}]) { int32 }\n        assert_type(%[{{ parse_type(\"Int32\").resolve == Int32.instance ? 1 : 'a' }}]) { int32 }\n      end\n\n      it \"raises on empty string\" do\n        expect_raises(Crystal::TypeException, \"argument to parse_type cannot be an empty value\") do\n          assert_macro %({{parse_type \"\"}}), %(nil)\n        end\n      end\n\n      it \"raises on extra unparsed tokens before the type\" do\n        expect_raises(Crystal::TypeException, %(Invalid type name: \"100Foo\")) do\n          assert_macro %({{parse_type \"100Foo\" }}), %(nil)\n        end\n      end\n\n      it \"raises on extra unparsed tokens after the type\" do\n        expect_raises(Crystal::TypeException, %(Invalid type name: \"Foo(Int32)100\")) do\n          assert_macro %({{parse_type \"Foo(Int32)100\" }}), %(nil)\n        end\n      end\n\n      it \"raises on non StringLiteral arguments\" do\n        expect_raises(Crystal::TypeException, \"argument to parse_type must be a StringLiteral, not SymbolLiteral\") do\n          assert_macro %({{parse_type :Foo }}), %(nil)\n        end\n      end\n\n      it \"exposes syntax warnings\" do\n        assert_warning %({% parse_type \"Foo(0x8000_0000_0000_0000)\" %}), \"Warning: 0x8000_0000_0000_0000 doesn't fit in an Int64, try using the suffix u64 or i128\"\n      end\n    end\n\n    describe \"printing\" do\n      it \"puts\" do\n        String.build do |io|\n          assert_macro(%({% puts foo %}), \"\") do |program|\n            program.stdout = io\n            {foo: \"bar\".string}\n          end\n        end.should eq %(bar\\n)\n      end\n\n      it \"print\" do\n        String.build do |io|\n          assert_macro(%({% print foo %}), \"\") do |program|\n            program.stdout = io\n            {foo: \"bar\".string}\n          end\n        end.should eq %(bar)\n      end\n\n      it \"p\" do\n        String.build do |io|\n          assert_macro(%({% p foo %}), \"\") do |program|\n            program.stdout = io\n            {foo: \"bar\".string}\n          end\n        end.should eq %(\"bar\"\\n)\n      end\n\n      it \"p!\" do\n        String.build do |io|\n          assert_macro(\"{% p! foo %}\", \"\") do |program|\n            program.stdout = io\n            {foo: \"bar\".string}\n          end\n        end.should eq %(foo # => \"bar\"\\n)\n      end\n\n      it \"pp\" do\n        String.build do |io|\n          assert_macro(\"{% pp foo %}\", \"\") do |program|\n            program.stdout = io\n            {foo: \"bar\".string}\n          end\n        end.should eq %(\"bar\"\\n)\n      end\n\n      it \"pp!\" do\n        String.build do |io|\n          assert_macro(\"{% pp! foo %}\", \"\") do |program|\n            program.stdout = io\n            {foo: \"bar\".string}\n          end\n        end.should eq %(foo # => \"bar\"\\n)\n      end\n    end\n  end\n\n  describe \"file_exists?\" do\n    context \"with absolute path\" do\n      it \"returns true if file exists\" do\n        run(%q<\n          {{file_exists?(\"#{__DIR__}/../data/build\")}} ? 10 : 20\n          >, filename: __FILE__).to_i.should eq(10)\n      end\n\n      it \"returns false if file doesn't exist\" do\n        run(%q<\n          {{file_exists?(\"#{__DIR__}/../data/build_foo\")}} ? 10 : 20\n          >, filename: __FILE__).to_i.should eq(20)\n      end\n    end\n\n    context \"with relative path\" do\n      it \"reads file (exists)\" do\n        run(%q<\n          {{file_exists?(\"spec/compiler/data/build\")}} ? 10 : 20\n          >, filename: __FILE__).to_i.should eq(10)\n      end\n\n      it \"reads file (doesn't exist)\" do\n        run(%q<\n          {{file_exists?(\"spec/compiler/data/build_foo\")}} ? 10 : 20\n          >, filename: __FILE__).to_i.should eq(20)\n      end\n    end\n  end\n\n  describe \"read_file\" do\n    context \"with absolute path\" do\n      it \"reads file (exists)\" do\n        run(%q<\n          {{read_file(\"#{__DIR__}/../data/build\")}}\n          >, filename: __FILE__).to_string.should eq(File.read(\"#{__DIR__}/../data/build\"))\n      end\n\n      it \"reads file (doesn't exist)\" do\n        assert_error <<-CRYSTAL,\n          {{read_file(\"#{__DIR__}/../data/build_foo\")}}\n          CRYSTAL\n          \"Error opening file with mode 'r'\"\n      end\n    end\n\n    context \"with relative path\" do\n      it \"reads file (exists)\" do\n        run(%q<\n          {{read_file(\"spec/compiler/data/build\")}}\n          >, filename: __FILE__).to_string.should eq(File.read(\"spec/compiler/data/build\"))\n      end\n\n      it \"reads file (doesn't exist)\" do\n        assert_error <<-CRYSTAL,\n          {{read_file(\"spec/compiler/data/build_foo\")}}\n          CRYSTAL\n          \"Error opening file with mode 'r'\"\n      end\n    end\n  end\n\n  describe \"read_file?\" do\n    context \"with absolute path\" do\n      it \"reads file (doesn't exist)\" do\n        run(%q<\n          {{read_file?(\"#{__DIR__}/../data/build_foo\")}} ? 10 : 20\n          >, filename: __FILE__).to_i.should eq(20)\n      end\n    end\n\n    context \"with relative path\" do\n      it \"reads file (doesn't exist)\" do\n        run(%q<\n          {{read_file?(\"spec/compiler/data/build_foo\")}} ? 10 : 20\n          >, filename: __FILE__).to_i.should eq(20)\n      end\n    end\n  end\n\n  describe \".system\" do\n    it \"command does not exist\" do\n      assert_error %({{ `commanddoesnotexist` }}), \"error executing command: commanddoesnotexist\"\n    end\n\n    it \"successful command\" do\n      assert_macro %({{ `#{exit_code_command(0)}` }}), \"\"\n    end\n\n    it \"successful command with output\" do\n      assert_macro %({{ `#{shell_command(\"echo foobar\")}` }}), \"foobar#{newline}\"\n    end\n\n    it \"failing command\" do\n      assert_error %({{ `#{exit_code_command(1)}` }}), \"error executing command: #{exit_code_command(1)}, got exit status 1\"\n      assert_error %({{ `#{exit_code_command(2)}` }}), \"error executing command: #{exit_code_command(2)}, got exit status 2\"\n      assert_error %({{ `#{exit_code_command(127)}` }}), \"error executing command: #{exit_code_command(127)}, got exit status 127\"\n    end\n  end\n\n  describe \"error reporting\" do\n    it \"reports wrong number of arguments\" do\n      assert_macro_error %({{[1, 2, 3].push}}), \"wrong number of arguments for macro 'ArrayLiteral#push' (given 0, expected 1)\"\n    end\n\n    it \"reports wrong number of arguments, with optional parameters\" do\n      assert_macro_error %({{1.+(2, 3)}}), \"wrong number of arguments for macro 'NumberLiteral#+' (given 2, expected 0..1)\"\n      assert_macro_error %({{[1][]}}), \"wrong number of arguments for macro 'ArrayLiteral#[]' (given 0, expected 1..2)\"\n    end\n\n    it \"reports unexpected block\" do\n      assert_macro_error %({{[1, 2, 3].shuffle { |x| }}}), \"macro 'ArrayLiteral#shuffle' is not expected to be invoked with a block, but a block was given\"\n    end\n\n    it \"reports missing block\" do\n      assert_macro_error %({{[1, 2, 3].reduce}}), \"macro 'ArrayLiteral#reduce' is expected to be invoked with a block, but no block was given\"\n    end\n\n    it \"reports unexpected named argument\" do\n      assert_macro_error %({{\"\".starts_with?(other: \"\")}}), \"no parameter named 'other'\"\n    end\n\n    it \"reports unexpected named argument (2)\" do\n      assert_macro_error %({{\"\".camelcase(foo: \"\")}}), \"no parameter named 'foo'\"\n    end\n\n    # there are no macro methods with required named parameters\n\n    it \"uses correct name for top-level macro methods\" do\n      assert_macro_error %({{flag?}}), \"wrong number of arguments for macro '::flag?' (given 0, expected 1)\"\n    end\n  end\n\n  describe \"immutability of returned container literals (#10818)\" do\n    it \"Annotation#args\" do\n      node = Annotation.new(Path.new(\"Foo\"), [42.int32, \"a\".string] of ASTNode)\n      assert_macro %({{ (x.args << \"a\"; x.args.size) }}), \"2\", {x: node}\n    end\n\n    it \"Generic#type_vars\" do\n      node = Generic.new(\"Foo\".path, [\"Bar\".path, \"Int32\".path] of ASTNode)\n      assert_macro %({{ (x.type_vars << \"a\"; x.type_vars.size) }}), \"2\", {x: node}\n    end\n\n    it \"MultiAssign#targets\" do\n      node = MultiAssign.new([\"foo\".var, \"bar\".var] of ASTNode, [2.int32, \"a\".string] of ASTNode)\n      assert_macro %({{ (x.targets << \"a\"; x.targets.size) }}), \"2\", {x: node}\n    end\n\n    it \"MultiAssign#values\" do\n      node = MultiAssign.new([\"foo\".var, \"bar\".var] of ASTNode, [2.int32, \"a\".string] of ASTNode)\n      assert_macro %({{ (x.values << \"a\"; x.values.size) }}), \"2\", {x: node}\n    end\n\n    it \"ProcNotation#inputs\" do\n      node = ProcNotation.new([Path.new(\"SomeType\"), Path.new(\"OtherType\")] of ASTNode)\n      assert_macro %({{ (x.inputs << \"a\"; x.inputs.size) }}), \"2\", {x: node}\n    end\n\n    it \"ProcPointer#args\" do\n      node = ProcPointer.new(Var.new(\"some_object\"), \"method\", [Path.new(\"SomeType\"), Path.new(\"OtherType\")] of ASTNode)\n      assert_macro %({{ (x.args << \"a\"; x.args.size) }}), \"2\", {x: node}\n    end\n\n    it \"StringInterpolation#expressions\" do\n      node = StringInterpolation.new([\"fo\".string, 1.int32, \"o\".string] of ASTNode)\n      assert_macro %({{ (x.expressions << \"a\"; x.expressions.size) }}), \"3\", {x: node}\n    end\n\n    it \"Union#types\" do\n      node = Crystal::Union.new([\"Int32\".path, \"String\".path] of ASTNode)\n      assert_macro %({{ (x.types << \"a\"; x.types.size) }}), \"2\", {x: node}\n    end\n\n    it \"When#conds\" do\n      node = When.new([2.int32, 3.int32] of ASTNode, 4.int32)\n      assert_macro %({{ (x.conds << \"a\"; x.conds.size) }}), \"2\", {x: node}\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/and_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: and\" do\n  it \"normalizes and without variable\" do\n    assert_expand \"a && b\", \"if __temp_1 = a\\n  b\\nelse\\n  __temp_1\\nend\"\n  end\n\n  it \"normalizes and with variable on the left\" do\n    assert_expand \"a = 1; a && b\", \"if a\\n  b\\nelse\\n  a\\nend\"\n  end\n\n  it \"normalizes and with is_a? on var\" do\n    assert_expand \"a = 1; a.is_a?(Foo) && b\", \"if a.is_a?(Foo)\\n  b\\nelse\\n  a.is_a?(Foo)\\nend\"\n  end\n\n  it \"normalizes and with ! on var\" do\n    assert_expand \"a = 1; !a && b\", \"if !a\\n  b\\nelse\\n  !a\\nend\"\n  end\n\n  it \"normalizes and with ! on var.is_a?(...)\" do\n    assert_expand \"a = 1; !a.is_a?(Int32) && b\", \"if !a.is_a?(Int32)\\n  b\\nelse\\n  !a.is_a?(Int32)\\nend\"\n  end\n\n  it \"normalizes and with is_a? on exp\" do\n    assert_expand \"a = 1; 1.is_a?(Foo) && b\", \"if __temp_1 = 1.is_a?(Foo)\\n  b\\nelse\\n  __temp_1\\nend\"\n  end\n\n  it \"normalizes and with assignment\" do\n    assert_expand \"(a = 1) && b\", \"if a = 1\\n  b\\nelse\\n  a\\nend\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/array_literal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: array literal\" do\n  it \"normalizes empty with of\" do\n    assert_expand \"[] of Int\", \"::Array(Int).new\"\n  end\n\n  it \"normalizes non-empty with of\" do\n    assert_expand \"[1, 2] of Int8\", <<-CRYSTAL\n      __temp_1 = ::Array(Int8).unsafe_build(2)\n      __temp_2 = __temp_1.to_unsafe\n      __temp_2[0] = 1\n      __temp_2[1] = 2\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"normalizes non-empty without of\" do\n    assert_expand \"[1, 2]\", <<-CRYSTAL\n      __temp_1 = ::Array(typeof(1, 2)).unsafe_build(2)\n      __temp_2 = __temp_1.to_unsafe\n      __temp_2[0] = 1\n      __temp_2[1] = 2\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"normalizes non-empty with of, with splat\" do\n    assert_expand \"[1, *2, *3, 4, 5] of Int8\", <<-CRYSTAL\n      __temp_1 = ::Array(Int8).new(3)\n      __temp_1 << 1\n      __temp_1.concat(2)\n      __temp_1.concat(3)\n      __temp_1 << 4\n      __temp_1 << 5\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"normalizes non-empty without of, with splat\" do\n    assert_expand \"[1, *2, *3, 4, 5]\", <<-CRYSTAL\n      __temp_1 = ::Array(typeof(1, ::Enumerable.element_type(2), ::Enumerable.element_type(3), 4, 5)).new(3)\n      __temp_1 << 1\n      __temp_1.concat(2)\n      __temp_1.concat(3)\n      __temp_1 << 4\n      __temp_1 << 5\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"normalizes non-empty without of, with splat only\" do\n    assert_expand \"[*1]\", <<-CRYSTAL\n      __temp_1 = ::Array(typeof(::Enumerable.element_type(1))).new(0)\n      __temp_1.concat(1)\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions\" do\n    assert_expand \"[[1]]\", <<-CRYSTAL\n      __temp_1 = [1]\n      __temp_2 = ::Array(typeof(__temp_1)).unsafe_build(1)\n      __temp_3 = __temp_2.to_unsafe\n      __temp_3[0] = __temp_1\n      __temp_2\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions, with splat\" do\n    assert_expand \"[*[1]]\", <<-CRYSTAL\n      __temp_1 = [1]\n      __temp_2 = ::Array(typeof(::Enumerable.element_type(__temp_1))).new(0)\n      __temp_2.concat(__temp_1)\n      __temp_2\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions, array-like\" do\n    assert_expand_named \"Foo{[1], *[2]}\", <<-CRYSTAL\n      __temp_1 = [1]\n      __temp_2 = [2]\n      __temp_3 = Foo.new\n      __temp_3 << __temp_1\n      __temp_2.each do |__temp_4| __temp_3 << __temp_4 end\n      __temp_3\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions, array-like generic\" do\n    assert_expand_named \"Foo{[1], *[2]}\", <<-CRYSTAL, generic: \"Foo\"\n      __temp_1 = [1]\n      __temp_2 = [2]\n      __temp_3 = Foo(typeof(__temp_1, ::Enumerable.element_type(__temp_2))).new\n      __temp_3 << __temp_1\n      __temp_2.each do |__temp_4| __temp_3 << __temp_4 end\n      __temp_3\n      CRYSTAL\n  end\n\n  # TODO: add md5 to the rest of the variables\n  it \"normalizes with filename\" do\n    assert_expand_named \"Foo{x, *y}\", <<-CRYSTAL, filename: \"foo.cr\"\n      __temp_1 = x\n      __temp_2 = y\n      __temp_3 = Foo.new\n      __temp_3 << __temp_1\n      __temp_2.each do |__temp_cd6ae5dd_1| __temp_3 << __temp_cd6ae5dd_1 end\n      __temp_3\n      CRYSTAL\n\n    assert_expand_named \"Foo{x, *y}\", <<-CRYSTAL, filename: \"bar.cr\"\n      __temp_1 = x\n      __temp_2 = y\n      __temp_3 = Foo.new\n      __temp_3 << __temp_1\n      __temp_2.each do |__temp_fbcf3d84_1| __temp_3 << __temp_fbcf3d84_1 end\n      __temp_3\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/block_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: block\" do\n  it \"normalizes unpacking with empty body\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(x, y), z|\n      end\n      CRYSTAL\n      foo do |__temp_1, z|\n        x, y = __temp_1\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes unpacking with single expression body\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(x, y), z|\n        z\n      end\n      CRYSTAL\n      foo do |__temp_1, z|\n        x, y = __temp_1\n        z\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes unpacking with multiple body expressions\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(x, y), z|\n        x\n        y\n        z\n      end\n      CRYSTAL\n      foo do |__temp_1, z|\n        x, y = __temp_1\n        x\n        y\n        z\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes unpacking with underscore\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(x, _), z|\n      end\n      CRYSTAL\n      foo do |__temp_1, z|\n        x, _ = __temp_1\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes nested unpacking\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(a, (b, c))|\n        1\n      end\n      CRYSTAL\n      foo do |__temp_1|\n        a, __temp_2 = __temp_1\n        b, c = __temp_2\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes multiple nested unpackings\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(a, (b, (c, (d, e)), f))|\n        1\n      end\n      CRYSTAL\n      foo do |__temp_1|\n        a, __temp_2 = __temp_1\n        b, __temp_3, f = __temp_2\n        c, __temp_4 = __temp_3\n        d, e = __temp_4\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes unpacking with splat\" do\n    assert_normalize <<-CRYSTAL, <<-CRYSTAL\n      foo do |(x, *y, z)|\n      end\n      CRYSTAL\n      foo do |__temp_1|\n        x, *y, z = __temp_1\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/case_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: case\" do\n  it \"normalizes case with call\" do\n    assert_expand \"case x; when 1; 'b'; when 2; 'c'; else; 'd'; end\", \"__temp_1 = x\\nif 1 === __temp_1\\n  'b'\\nelsif 2 === __temp_1\\n  'c'\\nelse\\n  'd'\\nend\"\n  end\n\n  it \"normalizes case with var in cond\" do\n    assert_expand \"x = 1; case x; when 1; 'b'; end\", \"if 1 === x\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with Path to is_a?\" do\n    assert_expand \"x = 1; case x; when Foo; 'b'; end\", \"if x.is_a?(Foo)\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with generic to is_a?\" do\n    assert_expand \"x = 1; case x; when Foo(T); 'b'; end\", \"if x.is_a?(Foo(T))\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with Path.class to is_a?\" do\n    assert_expand \"x = 1; case x; when Foo.class; 'b'; end\", \"if x.is_a?(Foo.class)\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with Generic.class to is_a?\" do\n    assert_expand \"x = 1; case x; when Foo(T).class; 'b'; end\", \"if x.is_a?(Foo(T).class)\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with many expressions in when\" do\n    assert_expand \"x = 1; case x; when 1, 2; 'b'; end\", \"if (1 === x) || (2 === x)\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with implicit call\" do\n    assert_expand \"case x; when .foo(1); 2; end\", \"__temp_1 = x\\nif __temp_1.foo(1)\\n  2\\nend\"\n  end\n\n  it \"normalizes case with implicit responds_to? (#3040)\" do\n    assert_expand \"case x; when .responds_to?(:foo); 2; end\", \"__temp_1 = x\\nif __temp_1.responds_to?(:foo)\\n  2\\nend\"\n  end\n\n  it \"normalizes case with implicit is_a? (#3040)\" do\n    assert_expand \"case x; when .is_a?(T); 2; end\", \"__temp_1 = x\\nif __temp_1.is_a?(T)\\n  2\\nend\"\n  end\n\n  it \"normalizes case with implicit as (#3040)\" do\n    assert_expand \"case x; when .as(T); 2; end\", \"__temp_1 = x\\nif __temp_1.as(T)\\n  2\\nend\"\n  end\n\n  it \"normalizes case with implicit as? (#3040)\" do\n    assert_expand \"case x; when .as?(T); 2; end\", \"__temp_1 = x\\nif __temp_1.as?(T)\\n  2\\nend\"\n  end\n\n  it \"normalizes case with implicit !\" do\n    assert_expand \"case x; when .!; 2; end\", \"__temp_1 = x\\nif !__temp_1\\n  2\\nend\"\n  end\n\n  it \"normalizes case with assignment\" do\n    assert_expand \"case x = 1; when 2; 3; end\", \"x = 1\\nif 2 === x\\n  3\\nend\"\n  end\n\n  it \"normalizes case with assignment wrapped by paren\" do\n    assert_expand \"case (x = 1); when 2; 3; end\", \"x = 1\\nif 2 === x\\n  3\\nend\"\n  end\n\n  it \"normalizes case without value\" do\n    assert_expand \"case when 2; 3; when 4; 5; end\", \"if 2\\n  3\\nelsif 4\\n  5\\nend\"\n  end\n\n  it \"normalizes case without value with many expressions in when\" do\n    assert_expand \"case when 2, 9; 3; when 4; 5; end\", \"if 2 || 9\\n  3\\nelsif 4\\n  5\\nend\"\n  end\n\n  it \"normalizes case with nil to is_a?\" do\n    assert_expand \"x = 1; case x; when nil; 'b'; end\", \"if x.is_a?(::Nil)\\n  'b'\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {2, 3}; 4; end\", \"if (2 === x) && (3 === y)\\n  4\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions and types\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {Int32, Float64}; 4; end\", \"if x.is_a?(Int32) && y.is_a?(Float64)\\n  4\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions and implicit obj\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {.foo, .bar}; 4; end\", \"if x.foo && y.bar\\n  4\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions and comma\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {2, 3}, {4, 5}; 6; end\", \"if ((2 === x) && (3 === y)) || ((4 === x) && (5 === y))\\n  6\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions with underscore\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {2, _}; 4; end\", \"if 2 === x\\n  4\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions with all underscores\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {_, _}; 4; end\", \"if true\\n  4\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions with all underscores twice\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when {_, _}, {_, _}; 4; end\", \"if true\\n  4\\nend\"\n  end\n\n  it \"normalizes case with multiple expressions and non-tuple\" do\n    assert_expand \"x, y = 1, 2; case {x, y}; when 1; 4; end\", \"if 1 === {x, y}\\n  4\\nend\"\n  end\n\n  it \"normalizes case without when and else\" do\n    assert_expand \"case x; end\", \"x\\nnil\"\n  end\n\n  it \"normalizes case without when but else\" do\n    assert_expand \"case x; else; y; end\", \"x\\ny\"\n  end\n\n  it \"normalizes case without cond, when and else\" do\n    assert_expand \"case; end\", \"\"\n  end\n\n  it \"normalizes case without cond, when but else\" do\n    assert_expand \"case; else; y; end\", \"y\"\n  end\n\n  it \"normalizes case with Path.class to is_a? (in)\" do\n    assert_expand \"x = 1; case x; in Foo.class; 'b'; end\", \"if x.is_a?(Foo.class)\\n  'b'\\nelse\\n  raise \\\"unreachable\\\"\\nend\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/chained_comparisons_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: chained comparisons\" do\n  it \"normalizes one comparison with literal\" do\n    assert_normalize \"1 <= 2 <= 3\", \"1 <= 2 && 2 <= 3\"\n  end\n\n  it \"normalizes one comparison with var\" do\n    assert_normalize \"b = 1; 1 <= b <= 3\", \"b = 1\\n1 <= b && b <= 3\"\n  end\n\n  it \"normalizes one comparison with call\" do\n    assert_normalize \"1 <= b <= 3\", \"1 <= (__temp_1 = b) && __temp_1 <= 3\"\n  end\n\n  it \"normalizes two comparisons with literal\" do\n    assert_normalize \"1 <= 2 <= 3 <= 4\", \"(1 <= 2 && 2 <= 3) && 3 <= 4\"\n  end\n\n  it \"normalizes two comparisons with calls\" do\n    assert_normalize \"1 <= a <= b <= 4\", \"(1 <= (__temp_2 = a) && __temp_2 <= (__temp_1 = b)) && __temp_1 <= 4\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/def_spec.cr",
    "content": "require \"../../spec_helper\"\n\nmodule Crystal\n  describe \"Normalize: def\" do\n    it \"expands a def on request with default arguments\" do\n      a_def = parse(\"def foo(x, y = 1, z = 2); x + y + z; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 1)\n      expected = parse(\"def foo(x); y = 1; z = 2; foo(x, y, z); end\")\n      actual.should eq(expected)\n    end\n\n    it \"expands a def on request with default arguments (2)\" do\n      a_def = parse(\"def foo(x, y = 1, z = 2); x + y + z; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 2)\n      expected = parse(\"def foo(x, y); z = 2; foo(x, y, z); end\")\n      actual.should eq(expected)\n    end\n\n    it \"expands a def on request with default arguments that yields\" do\n      a_def = parse(\"def foo(x, y = 1, z = 2); yield x + y + z; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 1)\n      expected = parse(\"def foo(x); y = 1; z = 2; yield x + y + z; end\")\n      actual.should eq(expected)\n    end\n\n    it \"expands a def on request with default arguments that yields (2)\" do\n      a_def = parse(\"def foo(x, y = 1, z = 2); yield x + y + z; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 2)\n      expected = parse(\"def foo(x, y); z = 2; yield x + y + z; end\")\n      actual.should eq(expected)\n    end\n\n    it \"expands a def on request with default arguments and type restrictions\" do\n      a_def = parse(\"def foo(x, y : Int32 = 1, z : Int64 = 2i64); x + y + z; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 1)\n      expected = parse(\"def foo(x); y = 1; z = 2i64; x + y + z; end\").as(Def)\n\n      exps = expected.body.as(Expressions).expressions\n      exps[0] = AssignWithRestriction.new(exps[0].as(Assign), Path.new(\"Int32\"))\n      exps[1] = AssignWithRestriction.new(exps[1].as(Assign), Path.new(\"Int64\"))\n\n      actual.should eq(expected)\n    end\n\n    it \"expands a def on request with default arguments and type restrictions (2)\" do\n      a_def = parse(\"def foo(x, y : Int32 = 1, z : Int64 = 2i64); x + y + z; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 2)\n      expected = parse(\"def foo(x, y : Int32); z = 2i64; x + y + z; end\").as(Def)\n\n      exps = expected.body.as(Expressions).expressions\n      exps[0] = AssignWithRestriction.new(exps[0].as(Assign), Path.new(\"Int64\"))\n\n      actual.should eq(expected)\n    end\n\n    it \"expands with splat\" do\n      a_def = parse(\"def foo(*args); args; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 3)\n      expected = parse(\"def foo(__temp_1, __temp_2, __temp_3)\\n  args = {__temp_1, __temp_2, __temp_3}\\n  args\\nend\")\n      actual.should eq(expected)\n    end\n\n    it \"expands with splat with one arg before\" do\n      a_def = parse(\"def foo(x, *args); args; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 3)\n      expected = parse(\"def foo(x, __temp_1, __temp_2)\\n  args = {__temp_1, __temp_2}\\n  args\\nend\")\n      actual.should eq(expected)\n    end\n\n    it \"expands with splat and zero\" do\n      a_def = parse(\"def foo(*args); args; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 0)\n      actual.to_s.should eq(\"def foo\\n  args = ::Tuple.new\\n  args\\nend\")\n    end\n\n    it \"expands with splat and default argument\" do\n      a_def = parse(\"def foo(x = 1, *args); args; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 0)\n      actual.to_s.should eq(\"def foo\\n  x = 1\\n  args = ::Tuple.new\\n  args\\nend\")\n    end\n\n    it \"expands with named argument\" do\n      a_def = parse(\"def foo(x = 1, y = 2); x + y; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 0, [\"y\"])\n      actual.to_s.should eq(\"def foo:y(y)\\n  x = 1\\n  foo(x, y)\\nend\")\n    end\n\n    it \"expands with two named argument\" do\n      a_def = parse(\"def foo(x = 1, y = 2); x + y; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 0, [\"y\", \"x\"])\n      actual.to_s.should eq(\"def foo:y:x(y, x)\\n  foo(x, y)\\nend\")\n    end\n\n    it \"expands with two named argument and one not\" do\n      a_def = parse(\"def foo(x, y = 2, z = 3); x + y; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 1, [\"z\"])\n      actual.to_s.should eq(\"def foo:z(x, z)\\n  y = 2\\n  foo(x, y, z)\\nend\")\n    end\n\n    it \"expands with named argument and yield\" do\n      a_def = parse(\"def foo(x = 1, y = 2, &); yield x + y; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 0, [\"y\"])\n      actual.to_s.should eq(\"def foo:y(y, &)\\n  x = 1\\n  yield x + y\\nend\")\n    end\n\n    # Small optimizations: no need to create a separate def in these cases\n    it \"expands with one named arg that is the only one (1)\" do\n      a_def = parse(\"def foo(x = 1); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0, [\"x\"])\n      other_def.should be(a_def)\n    end\n\n    it \"expands with one named arg that is the only one (2)\" do\n      a_def = parse(\"def foo(x, y = 1); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 1, [\"y\"])\n      other_def.should be(a_def)\n    end\n\n    it \"expands with more named arg which come in the correct order\" do\n      a_def = parse(\"def foo(x, y = 1, z = 2); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 1, [\"y\", \"z\"])\n      other_def.should be(a_def)\n    end\n\n    it \"expands with magic constant\" do\n      a_def = parse(\"def foo(x, y = __LINE__); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 1)\n      other_def.should be(a_def)\n    end\n\n    it \"expands with magic constant specifying one when all are magic\" do\n      a_def = parse(\"def foo(x, file = __FILE__, line = __LINE__); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 2)\n      other_def.should be(a_def)\n    end\n\n    it \"expands with magic constant specifying one when not all are magic\" do\n      a_def = parse(\"def foo(x, z = 1, line = __LINE__); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 2)\n      other_def.should be(a_def)\n    end\n\n    it \"expands with magic constant with named arg\" do\n      a_def = parse(\"def foo(x, file = __FILE__, line = __LINE__); x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 1, [\"line\"])\n      other_def.to_s.should eq(\"def foo:line(x, line, file = __FILE__)\\n  foo(x, file, line)\\nend\")\n    end\n\n    it \"expands with magic constant with named arg with yield\" do\n      a_def = parse(\"def foo(x, file = __FILE__, line = __LINE__, &); yield x, file, line; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 1, [\"line\"])\n      other_def.to_s.should eq(\"def foo:line(x, line, file = __FILE__, &)\\n  yield x, file, line\\nend\")\n    end\n\n    it \"expands a def with double splat and no args\" do\n      a_def = parse(\"def foo(**options); options; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0)\n      other_def.to_s.should eq(\"def foo\\n  options = ::NamedTuple.new\\n  options\\nend\")\n    end\n\n    it \"expands a def with double splat and two named args\" do\n      a_def = parse(\"def foo(**options); options; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0, [\"x\", \"y\"])\n      other_def.to_s.should eq(\"def foo:x:y(x __temp_1, y __temp_2)\\n  options = {x: __temp_1, y: __temp_2}\\n  options\\nend\")\n    end\n\n    it \"expands a def with double splat and two named args and regular args\" do\n      a_def = parse(\"def foo(y, **options); y + options; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0, [\"x\", \"y\", \"z\"])\n      other_def.to_s.should eq(\"def foo:x:y:z(x __temp_1, y, z __temp_3)\\n  options = {x: __temp_1, z: __temp_3}\\n  y + options\\nend\")\n    end\n\n    it \"expands a def with splat and double splat\" do\n      a_def = parse(\"def foo(*args, **options); args + options; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 2, [\"x\", \"y\"])\n      other_def.to_s.should eq(\"def foo:x:y(__temp_1, __temp_2, x __temp_3, y __temp_4)\\n  args = {__temp_1, __temp_2}\\n  options = {x: __temp_3, y: __temp_4}\\n  args + options\\nend\")\n    end\n\n    it \"expands arg with default value after splat\" do\n      a_def = parse(\"def foo(*args, x = 10); args + x; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0)\n      other_def.to_s.should eq(\"def foo\\n  x = 10\\n  args = ::Tuple.new\\n  args + x\\nend\")\n    end\n\n    it \"expands default value after splat index\" do\n      a_def = parse(\"def foo(x, *y, z = 10); x + y + z; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 3)\n      other_def.to_s.should eq(\"def foo(x, __temp_1, __temp_2)\\n  z = 10\\n  y = {__temp_1, __temp_2}\\n  (x + y) + z\\nend\")\n    end\n\n    it \"uses bare *\" do\n      a_def = parse(\"def foo(x, *, y); x + y; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 1, [\"y\"])\n      other_def.to_s.should eq(\"def foo:y(x, y)\\n  x + y\\nend\")\n    end\n\n    it \"expands a def with external names (1)\" do\n      a_def = parse(\"def foo(x y); y; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 0, [\"x\"])\n      actual.should be(a_def)\n    end\n\n    it \"expands a def with external names (2)\" do\n      a_def = parse(\"def foo(x x1, y y1); x1 + y1; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0, [\"y\", \"x\"])\n      other_def.to_s.should eq(\"def foo:y:x(y y1, x x1)\\n  foo(x1, y1)\\nend\")\n    end\n\n    it \"expands a def on request with default arguments (external names)\" do\n      a_def = parse(\"def foo(x x1, y y1 = 1, z z1 = 2); x1 + y1 + z1; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 1)\n      expected = parse(\"def foo(x x1); y1 = 1; z1 = 2; foo(x1, y1, z1); end\")\n      actual.should eq(expected)\n    end\n\n    it \"expands a def on request with default arguments that yields (external names)\" do\n      a_def = parse(\"def foo(x x1, y y1 = 1, z z1 = 2); yield x1 + y1 + z1; end\").as(Def)\n      actual = a_def.expand_default_arguments(Program.new, 1)\n      expected = parse(\"def foo(x x1); y1 = 1; z1 = 2; yield x1 + y1 + z1; end\")\n      actual.should eq(expected)\n    end\n\n    it \"expands a new def with double splat and two named args and regular args\" do\n      a_def = parse(\"def new(y, **options); y + options; end\").as(Def)\n      other_def = a_def.expand_new_default_arguments(Program.new, 0, [\"x\", \"y\", \"z\"])\n      other_def.to_s.should eq(\"def new:x:y:z(x __temp_1, y __temp_2, z __temp_3)\\n  _ = allocate\\n  _.initialize(x: __temp_1, y: __temp_2, z: __temp_3)\\n  if _.responds_to?(:finalize)\\n    ::GC.add_finalizer(_)\\n  end\\n  _\\nend\")\n    end\n\n    it \"expands def with reserved external name (#6559)\" do\n      a_def = parse(\"def foo(abstract __arg0, **options); @abstract = __arg0; end\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 0, [\"abstract\"])\n      other_def.to_s.should eq(\"def foo:abstract(abstract __arg0)\\n  options = ::NamedTuple.new\\n  @abstract = __arg0\\nend\")\n    end\n\n    describe \"gives correct body location with\" do\n      {\"default arg\"                  => \"def testing(foo = 5)\",\n       \"default arg with restriction\" => \"def testing(foo : Int = 5)\",\n       \"splat arg\"                    => \"def testing(*foo : Array)\",\n       \"block instance var arg\"       => \"def testing(&@foo : ->)\",\n      }.each do |(suffix1, definition)|\n        {\"with body\"    => \"zzz = 7\\n\",\n         \"without body\" => \"\",\n        }.each do |(suffix2, body)|\n          it \"#{suffix1}, #{suffix2}\" do\n            a_def = parse(\"#{definition}\\n#{body}end\").as(Def)\n            actual = a_def.expand_default_arguments(Program.new, 1)\n\n            actual.location.should eq Location.new(\"\", line_number: 1, column_number: 1)\n            actual.body.location.should eq Location.new(\"\", line_number: 2, column_number: 1)\n          end\n        end\n      end\n    end\n\n    it \"normalizes with filename\" do\n      a_def = parse(\"def foo(*args, **options); args + options; end\", filename: \"foo.cr\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 2, [\"x\", \"y\"])\n      other_def.to_s.should eq <<-CRYSTAL\n        def foo:x:y(__temp_cd6ae5dd_1, __temp_cd6ae5dd_2, x __temp_cd6ae5dd_3, y __temp_cd6ae5dd_4)\n          args = {__temp_cd6ae5dd_1, __temp_cd6ae5dd_2}\n          options = {x: __temp_cd6ae5dd_3, y: __temp_cd6ae5dd_4}\n          args + options\n        end\n        CRYSTAL\n\n      a_def = parse(\"def foo(*args, **options); args + options; end\", filename: \"bar.cr\").as(Def)\n      other_def = a_def.expand_default_arguments(Program.new, 2, [\"x\", \"y\"])\n      other_def.to_s.should eq <<-CRYSTAL\n        def foo:x:y(__temp_fbcf3d84_1, __temp_fbcf3d84_2, x __temp_fbcf3d84_3, y __temp_fbcf3d84_4)\n          args = {__temp_fbcf3d84_1, __temp_fbcf3d84_2}\n          options = {x: __temp_fbcf3d84_3, y: __temp_fbcf3d84_4}\n          args + options\n        end\n        CRYSTAL\n    end\n\n    it \"normalizes `.new` with filename\" do\n      a_def = parse(\"def new(y, **options); end\", filename: \"foo.cr\").as(Def)\n      other_def = a_def.expand_new_default_arguments(Program.new, 0, [\"x\", \"y\", \"z\"])\n      other_def.to_s.should eq <<-CRYSTAL\n        def new:x:y:z(x __temp_cd6ae5dd_1, y __temp_cd6ae5dd_2, z __temp_cd6ae5dd_3)\n          _ = allocate\n          _.initialize(x: __temp_cd6ae5dd_1, y: __temp_cd6ae5dd_2, z: __temp_cd6ae5dd_3)\n          if _.responds_to?(:finalize)\n            ::GC.add_finalizer(_)\n          end\n          _\n        end\n        CRYSTAL\n\n      a_def = parse(\"def new(y, **options); end\", filename: \"bar.cr\").as(Def)\n      other_def = a_def.expand_new_default_arguments(Program.new, 0, [\"x\", \"y\", \"z\"])\n      other_def.to_s.should eq <<-CRYSTAL\n        def new:x:y:z(x __temp_fbcf3d84_1, y __temp_fbcf3d84_2, z __temp_fbcf3d84_3)\n          _ = allocate\n          _.initialize(x: __temp_fbcf3d84_1, y: __temp_fbcf3d84_2, z: __temp_fbcf3d84_3)\n          if _.responds_to?(:finalize)\n            ::GC.add_finalizer(_)\n          end\n          _\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/expressions_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: expressions\" do\n  it \"normalizes an empty expression with begin/end\" do\n    assert_normalize \"begin\\nend\", \"begin\\nend\"\n  end\n\n  it \"normalizes an expression\" do\n    assert_normalize \"(1 < 2).as(Bool)\", \"(1 < 2).as(Bool)\"\n  end\n\n  it \"normalizes expressions with begin/end\" do\n    assert_normalize \"begin\\n  1\\n  2\\nend\", \"begin\\n  1\\n  2\\nend\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/hash_literal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: hash literal\" do\n  it \"normalizes empty with of\" do\n    assert_expand \"{} of Int => Float\", \"::Hash(Int, Float).new\"\n  end\n\n  it \"normalizes non-empty with of\" do\n    assert_expand \"{1 => 2, 3 => 4} of Int => Float\", <<-CRYSTAL\n      __temp_1 = ::Hash(Int, Float).new\n      __temp_1[1] = 2\n      __temp_1[3] = 4\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"normalizes non-empty without of\" do\n    assert_expand \"{1 => 2, 3 => 4}\", <<-CRYSTAL\n      __temp_1 = ::Hash(typeof(1, 3), typeof(2, 4)).new\n      __temp_1[1] = 2\n      __temp_1[3] = 4\n      __temp_1\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions\" do\n    assert_expand \"{[1] => 2, 3 => [4]}\", <<-CRYSTAL\n      __temp_1 = [1]\n      __temp_2 = [4]\n      __temp_3 = ::Hash(typeof(__temp_1, 3), typeof(2, __temp_2)).new\n      __temp_3[__temp_1] = 2\n      __temp_3[3] = __temp_2\n      __temp_3\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions, hash-like\" do\n    assert_expand_named \"Foo{[1] => 2, 3 => [4]}\", <<-CRYSTAL\n      __temp_1 = [1]\n      __temp_2 = [4]\n      __temp_3 = Foo.new\n      __temp_3[__temp_1] = 2\n      __temp_3[3] = __temp_2\n      __temp_3\n      CRYSTAL\n  end\n\n  it \"hoists complex element expressions, hash-like generic\" do\n    assert_expand_named \"Foo{[1] => 2, 3 => [4]}\", <<-CRYSTAL, generic: \"Foo\"\n      __temp_1 = [1]\n      __temp_2 = [4]\n      __temp_3 = Foo(typeof(__temp_1, 3), typeof(2, __temp_2)).new\n      __temp_3[__temp_1] = 2\n      __temp_3[3] = __temp_2\n      __temp_3\n      CRYSTAL\n  end\n\n  it \"evaluates key and value expressions in correct order\" do\n    assert_expand \"{foo(1) => foo(2), foo(3) => foo(4), foo(5) => foo(6)}\", <<-CRYSTAL\n      __temp_1 = foo(1)\n      __temp_4 = foo(2)\n      __temp_2 = foo(3)\n      __temp_5 = foo(4)\n      __temp_3 = foo(5)\n      __temp_6 = foo(6)\n      __temp_7 = ::Hash(typeof(__temp_1, __temp_2, __temp_3), typeof(__temp_4, __temp_5, __temp_6)).new\n      __temp_7[__temp_1] = __temp_4\n      __temp_7[__temp_2] = __temp_5\n      __temp_7[__temp_3] = __temp_6\n      __temp_7\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/multi_assign_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: multi assign\" do\n  it \"normalizes n to n\" do\n    assert_expand \"a, b, c = 1, 2, 3\", <<-CRYSTAL\n      __temp_1 = 1\n      __temp_2 = 2\n      __temp_3 = 3\n      a = __temp_1\n      b = __temp_2\n      c = __temp_3\n      CRYSTAL\n  end\n\n  it \"normalizes n to n with []\" do\n    assert_expand \"a = 1; b = 2; a[0], b[1] = 2, 3\", <<-CRYSTAL\n      __temp_1 = 2\n      __temp_2 = 3\n      a[0] = __temp_1\n      b[1] = __temp_2\n      CRYSTAL\n  end\n\n  it \"normalizes n to n with call\" do\n    assert_expand \"a = 1; b = 2; a.foo, b.bar = 2, 3\", <<-CRYSTAL\n      __temp_1 = 2\n      __temp_2 = 3\n      a.foo = __temp_1\n      b.bar = __temp_2\n      CRYSTAL\n  end\n\n  context \"without strict_multi_assign\" do\n    it \"normalizes 1 to n\" do\n      assert_expand \"d = 1; a, b, c = d\", <<-CRYSTAL\n        __temp_1 = d\n        a = __temp_1[0]\n        b = __temp_1[1]\n        c = __temp_1[2]\n        CRYSTAL\n    end\n\n    it \"normalizes 1 to n with []\" do\n      assert_expand \"a = 1; b = 2; a[0], b[1] = 2\", <<-CRYSTAL\n        __temp_1 = 2\n        a[0] = __temp_1[0]\n        b[1] = __temp_1[1]\n        CRYSTAL\n    end\n\n    it \"normalizes 1 to n with call\" do\n      assert_expand \"a = 1; b = 2; a.foo, b.bar = 2\", <<-CRYSTAL\n        __temp_1 = 2\n        a.foo = __temp_1[0]\n        b.bar = __temp_1[1]\n        CRYSTAL\n    end\n  end\n\n  context \"strict_multi_assign\" do\n    it \"normalizes 1 to n\" do\n      assert_expand \"d = 1; a, b, c = d\", <<-CRYSTAL, flags: \"strict_multi_assign\"\n        __temp_1 = d\n        if __temp_1.size != 3\n          ::raise(::IndexError.new(\"Multiple assignment count mismatch\"))\n        end\n        a = __temp_1[0]\n        b = __temp_1[1]\n        c = __temp_1[2]\n        CRYSTAL\n    end\n\n    it \"normalizes 1 to n with []\" do\n      assert_expand \"a = 1; b = 2; a[0], b[1] = 2\", <<-CRYSTAL, flags: \"strict_multi_assign\"\n        __temp_1 = 2\n        if __temp_1.size != 2\n          ::raise(::IndexError.new(\"Multiple assignment count mismatch\"))\n        end\n        a[0] = __temp_1[0]\n        b[1] = __temp_1[1]\n        CRYSTAL\n    end\n\n    it \"normalizes 1 to n with call\" do\n      assert_expand \"a = 1; b = 2; a.foo, b.bar = 2\", <<-CRYSTAL, flags: \"strict_multi_assign\"\n        __temp_1 = 2\n        if __temp_1.size != 2\n          ::raise(::IndexError.new(\"Multiple assignment count mismatch\"))\n        end\n        a.foo = __temp_1[0]\n        b.bar = __temp_1[1]\n        CRYSTAL\n    end\n  end\n\n  it \"normalizes m to n, with splat on left-hand side, splat is empty\" do\n    assert_expand \"a = 1; b = 2; *a[0], b.foo, c = 3, 4\", <<-CRYSTAL\n      __temp_1 = ::Tuple.new\n      __temp_2 = 3\n      __temp_3 = 4\n      a[0] = __temp_1\n      b.foo = __temp_2\n      c = __temp_3\n      CRYSTAL\n  end\n\n  it \"normalizes m to n, with splat on left-hand side, splat is non-empty\" do\n    assert_expand \"a = 1; b = 2; a[0], *b.foo, c = 3, 4, 5, 6, 7\", <<-CRYSTAL\n      __temp_1 = 3\n      __temp_2 = ::Tuple.new(4, 5, 6)\n      __temp_3 = 7\n      a[0] = __temp_1\n      b.foo = __temp_2\n      c = __temp_3\n      CRYSTAL\n  end\n\n  it \"normalizes m to n, with *_ on left-hand side (1)\" do\n    assert_expand \"a, *_, b, c = 1, 2, 3, 4, 5\", <<-CRYSTAL\n      __temp_1 = 1\n      2\n      3\n      __temp_2 = 4\n      __temp_3 = 5\n      a = __temp_1\n      b = __temp_2\n      c = __temp_3\n      CRYSTAL\n  end\n\n  it \"normalizes m to n, with *_ on left-hand side (2)\" do\n    assert_expand \"*_, a, b, c = 1, 2, 3, 4, 5\", <<-CRYSTAL\n      1\n      2\n      __temp_1 = 3\n      __temp_2 = 4\n      __temp_3 = 5\n      a = __temp_1\n      b = __temp_2\n      c = __temp_3\n      CRYSTAL\n  end\n\n  it \"normalizes m to n, with *_ on left-hand side (3)\" do\n    assert_expand \"a, b, c, *_ = 1, 2, 3, 4, 5\", <<-CRYSTAL\n      __temp_1 = 1\n      __temp_2 = 2\n      __temp_3 = 3\n      4\n      5\n      a = __temp_1\n      b = __temp_2\n      c = __temp_3\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to n, with splat on left-hand side\" do\n    assert_expand \"c = 1; d = 2; a, b, *c.foo, d[0], e, f = 3\", <<-CRYSTAL\n      __temp_1 = 3\n      if __temp_1.size < 5\n        ::raise(::IndexError.new(\"Multiple assignment count mismatch\"))\n      end\n      a = __temp_1[0]\n      b = __temp_1[1]\n      c.foo = __temp_1[2..-4]\n      d[0] = __temp_1[-3]\n      e = __temp_1[-2]\n      f = __temp_1[-1]\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to n, with splat on left-hand side, splat before other targets\" do\n    assert_expand \"*a, b, c, d = 3\", <<-CRYSTAL\n      __temp_1 = 3\n      a = __temp_1[0..-4]\n      b = __temp_1[-3]\n      c = __temp_1[-2]\n      d = __temp_1[-1]\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to n, with splat on left-hand side, splat after other targets\" do\n    assert_expand \"a, b, c, *d = 3\", <<-CRYSTAL\n      __temp_1 = 3\n      a = __temp_1[0]\n      b = __temp_1[1]\n      c = __temp_1[2]\n      d = __temp_1[3..-1]\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to n, with *_ on left-hand side (1)\" do\n    assert_expand \"a, *_, b, c = 1\", <<-CRYSTAL\n      __temp_1 = 1\n      if __temp_1.size < 3\n        ::raise(::IndexError.new(\"Multiple assignment count mismatch\"))\n      end\n      a = __temp_1[0]\n      b = __temp_1[-2]\n      c = __temp_1[-1]\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to n, with *_ on left-hand side (2)\" do\n    assert_expand \"*_, a, b, c = 1\", <<-CRYSTAL\n      __temp_1 = 1\n      a = __temp_1[-3]\n      b = __temp_1[-2]\n      c = __temp_1[-1]\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to n, with *_ on left-hand side (3)\" do\n    assert_expand \"a, b, c, *_ = 1\", <<-CRYSTAL\n      __temp_1 = 1\n      a = __temp_1[0]\n      b = __temp_1[1]\n      c = __temp_1[2]\n      CRYSTAL\n  end\n\n  it \"normalizes n to splat on left-hand side\" do\n    assert_expand \"*a = 1, 2, 3, 4\", <<-CRYSTAL\n      __temp_1 = ::Tuple.new(1, 2, 3, 4)\n      a = __temp_1\n      CRYSTAL\n  end\n\n  it \"normalizes n to *_ on left-hand side\" do\n    assert_expand \"*_ = 1, 2, 3, 4\", <<-CRYSTAL\n      1\n      2\n      3\n      4\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to splat on left-hand side\" do\n    assert_expand \"*a = 1\", <<-CRYSTAL\n      __temp_1 = 1\n      a = __temp_1[0..-1]\n      CRYSTAL\n  end\n\n  it \"normalizes 1 to *_ on left-hand side\" do\n    assert_expand \"*_ = 1\", <<-CRYSTAL\n      __temp_1 = 1\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/op_assign_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: op assign\" do\n  [\"+\", \"-\", \"*\", \"&+\", \"&-\", \"&*\"].each do |op|\n    it \"normalizes var #{op}=\" do\n      node = assert_normalize \"a = 1; a #{op}= 2\", \"a = 1\\na = a #{op} 2\"\n      assert_name_location node.as(Expressions).expressions[1].as(Assign).value,\n        1, 10\n    end\n  end\n\n  it \"normalizes var ||=\" do\n    assert_normalize \"a = 1; a ||= 2\", \"a = 1\\na || (a = 2)\"\n  end\n\n  it \"normalizes var &&=\" do\n    assert_normalize \"a = 1; a &&= 2\", \"a = 1\\na && (a = 2)\"\n  end\n\n  it \"normalizes exp.value +=\" do\n    node = assert_normalize \"a.b += 1\", \"__temp_1 = a\\n__temp_1.b = __temp_1.b + 1\"\n    assert_name_location node.as(Expressions).expressions[1].as(Call).args[0],\n      1, 5\n  end\n\n  it \"normalizes exp.value ||=\" do\n    assert_normalize \"a.b ||= 1\", \"__temp_1 = a\\n__temp_1.b || (__temp_1.b = 1)\"\n  end\n\n  it \"normalizes exp.value &&=\" do\n    assert_normalize \"a.b &&= 1\", \"__temp_1 = a\\n__temp_1.b && (__temp_1.b = 1)\"\n  end\n\n  it \"normalizes var.value +=\" do\n    node = assert_normalize \"a = 1; a.b += 2\", \"a = 1\\na.b = a.b + 2\"\n    assert_name_location node.as(Expressions).expressions[1].as(Call).args[0],\n      1, 12\n  end\n\n  it \"normalizes @var.value +=\" do\n    node = assert_normalize \"@a.b += 2\", \"@a.b = @a.b + 2\"\n    assert_name_location node.as(Call).args[0],\n      1, 6\n  end\n\n  it \"normalizes @@var.value +=\" do\n    node = assert_normalize \"@@a.b += 2\", \"@@a.b = @@a.b + 2\"\n    assert_name_location node.as(Call).args[0],\n      1, 7\n  end\n\n  it \"normalizes exp[value] +=\" do\n    node = assert_normalize \"a[b, c] += 1\", \"__temp_1 = b\\n__temp_2 = c\\n__temp_3 = a\\n__temp_3[__temp_1, __temp_2] = __temp_3[__temp_1, __temp_2] + 1\"\n    assert_name_location node.as(Expressions).expressions[3].as(Call).args[2],\n      1, 9\n  end\n\n  it \"normalizes exp[value] ||=\" do\n    assert_normalize \"a[b, c] ||= 1\", \"__temp_1 = b\\n__temp_2 = c\\n__temp_3 = a\\n__temp_3[__temp_1, __temp_2]? || (__temp_3[__temp_1, __temp_2] = 1)\"\n  end\n\n  it \"normalizes exp[value] &&=\" do\n    assert_normalize \"a[b, c] &&= 1\", \"__temp_1 = b\\n__temp_2 = c\\n__temp_3 = a\\n__temp_3[__temp_1, __temp_2]? && (__temp_3[__temp_1, __temp_2] = 1)\"\n  end\n\n  it \"normalizes exp[0] +=\" do\n    node = assert_normalize \"a[0] += 1\", \"__temp_2 = a\\n__temp_2[0] = __temp_2[0] + 1\"\n    assert_name_location node.as(Expressions).expressions[1].as(Call).args[1],\n      1, 6\n  end\n\n  it \"normalizes var[0] +=\" do\n    node = assert_normalize \"a = 1; a[0] += 1\", \"a = 1\\na[0] = a[0] + 1\"\n    assert_name_location node.as(Expressions).expressions[1].as(Call).args[1],\n      1, 13\n  end\n\n  it \"normalizes @var[0] +=\" do\n    node = assert_normalize \"@a[0] += 1\", \"@a[0] = @a[0] + 1\"\n    assert_name_location node.as(Call).args[1],\n      1, 7\n  end\n\n  it \"normalizes @@var[0] +=\" do\n    node = assert_normalize \"@@a[0] += 1\", \"@@a[0] = @@a[0] + 1\"\n    assert_name_location node.as(Call).args[1],\n      1, 8\n  end\n\n  it \"normalizes with filename\" do\n    assert_normalize \"a[b, c] += 1\", <<-CRYSTAL, filename: \"foo.cr\"\n      __temp_cd6ae5dd_1 = b\n      __temp_cd6ae5dd_2 = c\n      __temp_cd6ae5dd_3 = a\n      __temp_cd6ae5dd_3[__temp_cd6ae5dd_1, __temp_cd6ae5dd_2] = __temp_cd6ae5dd_3[__temp_cd6ae5dd_1, __temp_cd6ae5dd_2] + 1\n      CRYSTAL\n\n    assert_normalize \"a[b, c] += 1\", <<-CRYSTAL, filename: \"bar.cr\"\n      __temp_fbcf3d84_1 = b\n      __temp_fbcf3d84_2 = c\n      __temp_fbcf3d84_3 = a\n      __temp_fbcf3d84_3[__temp_fbcf3d84_1, __temp_fbcf3d84_2] = __temp_fbcf3d84_3[__temp_fbcf3d84_1, __temp_fbcf3d84_2] + 1\n      CRYSTAL\n  end\nend\n\nprivate def assert_name_location(node, line_number, column_number, spec_file = __FILE__, spec_line = __LINE__)\n  node.name_location.should_not be_nil, file: spec_file, line: spec_line\n\n  name_location = node.name_location.not_nil!\n  name_location.line_number.should eq(line_number), file: spec_file, line: spec_line\n  name_location.column_number.should eq(column_number), file: spec_file, line: spec_line\nend\n"
  },
  {
    "path": "spec/compiler/normalize/or_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: or\" do\n  it \"normalizes or without variable\" do\n    assert_expand \"a || b\", \"if __temp_1 = a\\n  __temp_1\\nelse\\n  b\\nend\"\n  end\n\n  it \"normalizes or with variable on the left\" do\n    assert_expand \"a = 1; a || b\", \"if a\\n  a\\nelse\\n  b\\nend\"\n  end\n\n  it \"normalizes or with assignment on the left\" do\n    assert_expand \"(a = 1) || b\", \"if a = 1\\n  a\\nelse\\n  b\\nend\"\n  end\n\n  it \"normalizes or with is_a? on var\" do\n    assert_expand \"a = 1; a.is_a?(Foo) || b\", \"if a.is_a?(Foo)\\n  a.is_a?(Foo)\\nelse\\n  b\\nend\"\n  end\n\n  it \"normalizes or with ! on var\" do\n    assert_expand \"a = 1; !a || b\", \"if !a\\n  !a\\nelse\\n  b\\nend\"\n  end\n\n  it \"normalizes or with ! on var.is_a?(...)\" do\n    assert_expand \"a = 1; !a.is_a?(Int32) || b\", \"if !a.is_a?(Int32)\\n  !a.is_a?(Int32)\\nelse\\n  b\\nend\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/proc_pointer_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: proc pointer\" do\n  it \"normalizes proc pointer without object\" do\n    assert_expand \"->foo\", <<-CRYSTAL\n      -> do\n        foo\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer with parameters, without object\" do\n    assert_expand \"->foo(Int32, String)\", <<-CRYSTAL\n      ->(__temp_1 : Int32, __temp_2 : String) do\n        foo(__temp_1, __temp_2)\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer of global call\" do\n    assert_expand \"->::foo(Int32)\", <<-CRYSTAL\n      ->(__temp_1 : Int32) do\n        ::foo(__temp_1)\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer with const receiver\" do\n    assert_expand \"->Foo.foo(Int32)\", <<-CRYSTAL\n      ->(__temp_1 : Int32) do\n        Foo.foo(__temp_1)\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer with global const receiver\" do\n    assert_expand \"->::Foo.foo(Int32)\", <<-CRYSTAL\n      ->(__temp_1 : Int32) do\n        ::Foo.foo(__temp_1)\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer with variable receiver\" do\n    assert_expand \"foo = 1; ->foo.bar(Int32)\", <<-CRYSTAL\n      __temp_1 = foo\n      ->(__temp_2 : Int32) do\n        __temp_1.bar(__temp_2)\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer with ivar receiver\" do\n    assert_expand \"->@foo.bar(Int32)\", <<-CRYSTAL\n      __temp_1 = @foo\n      ->(__temp_2 : Int32) do\n        __temp_1.bar(__temp_2)\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes proc pointer with cvar receiver\" do\n    assert_expand \"->@@foo.bar(Int32)\", <<-CRYSTAL\n      __temp_1 = @@foo\n      ->(__temp_2 : Int32) do\n        __temp_1.bar(__temp_2)\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/range_literal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: range literal\" do\n  it \"normalizes not exclusive\" do\n    assert_expand \"1..2\", \"::Range.new(1, 2, false)\"\n  end\n\n  it \"normalizes exclusive\" do\n    assert_expand \"1...2\", \"::Range.new(1, 2, true)\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/regex_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate def assert_expand_regex_const(from : String, to, *, flags = nil, file = __FILE__, line = __LINE__)\n  from_nodes = Parser.parse(from)\n  assert_expand(from_nodes, flags: flags, file: file, line: line) do |to_nodes, program|\n    const = program.types[to_nodes.to_s].should be_a(Crystal::Const), file: file, line: line\n    const.value.to_s.should eq(to.strip), file: file, line: line\n  end\nend\n\ndescribe \"Normalize: regex literal\" do\n  describe \"StringLiteral\" do\n    it \"expands to const\" do\n      assert_expand Parser.parse(%q(/foo/)) do |to_nodes, program|\n        to_nodes.to_s.should eq \"$Regex:0\"\n      end\n    end\n\n    it \"simple\" do\n      assert_expand_regex_const %q(/foo/), <<-'CRYSTAL'\n      ::Regex.new(\"foo\", ::Regex::Options.new(0))\n      CRYSTAL\n    end\n  end\n\n  describe \"StringInterpolation\" do\n    it \"simple\" do\n      assert_expand %q(/#{\"foo\".to_s}/), <<-'CRYSTAL'\n        ::Regex.new(\"#{\"foo\".to_s}\", ::Regex::Options.new(0))\n        CRYSTAL\n    end\n  end\n\n  describe \"options\" do\n    it \"empty\" do\n      assert_expand_regex_const %q(//), <<-'CRYSTAL'\n      ::Regex.new(\"\", ::Regex::Options.new(0))\n      CRYSTAL\n    end\n    it \"i\" do\n      assert_expand_regex_const %q(//i), <<-'CRYSTAL'\n      ::Regex.new(\"\", ::Regex::Options.new(1))\n      CRYSTAL\n    end\n    it \"x\" do\n      assert_expand_regex_const %q(//x), <<-'CRYSTAL'\n      ::Regex.new(\"\", ::Regex::Options.new(8))\n      CRYSTAL\n    end\n    it \"im\" do\n      assert_expand_regex_const %q(//im), <<-'CRYSTAL'\n      ::Regex.new(\"\", ::Regex::Options.new(7))\n      CRYSTAL\n    end\n    it \"imx\" do\n      assert_expand_regex_const %q(//imx), <<-'CRYSTAL'\n      ::Regex.new(\"\", ::Regex::Options.new(15))\n      CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/return_next_break_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: return next break\" do\n  [\"return\", \"next\", \"break\"].each do |keyword|\n    it \"removes nodes after #{keyword}\" do\n      assert_normalize \"#{keyword} 1; 2\", \"#{keyword} 1\"\n    end\n  end\n\n  it \"doesn't remove after return when there's an unless\" do\n    assert_normalize \"return 1 unless 2; 3\", \"if 2\\nelse\\n  return 1\\nend\\n3\"\n  end\n\n  it \"removes nodes after if that returns in both branches\" do\n    assert_normalize \"if true; break; else; return; end; 1\", \"if true\\n  break\\nelse\\n  return\\nend\"\n  end\n\n  it \"doesn't remove nodes after if that returns in one branch\" do\n    assert_normalize \"if true; 1; else; return; end; 1\", \"if true\\n  1\\nelse\\n  return\\nend\\n1\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/select_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: case\" do\n  it \"normalizes select with call\" do\n    assert_expand \"select; when foo; body; when bar; baz; end\", <<-CRYSTAL\n      __temp_1, __temp_2 = ::Channel.select({foo_select_action, bar_select_action})\n      case __temp_1\n      when 0\n        body\n      when 1\n        baz\n      else\n        ::raise(\"BUG: invalid select index\")\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes select with assign\" do\n    assert_expand \"select; when x = foo; x + 1; end\", <<-CRYSTAL\n      __temp_1, __temp_2 = ::Channel.select({foo_select_action})\n      case __temp_1\n      when 0\n        x = __temp_2.as(typeof(foo))\n        x + 1\n      else\n        ::raise(\"BUG: invalid select index\")\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes select with else\" do\n    assert_expand \"select; when foo; body; else; baz; end\", <<-CRYSTAL\n      __temp_1, __temp_2 = ::Channel.non_blocking_select({foo_select_action})\n      case __temp_1\n      when 0\n        body\n      else\n        baz\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes select with assign and question method\" do\n    assert_expand \"select; when x = foo?; x + 1; end\", <<-CRYSTAL\n      __temp_1, __temp_2 = ::Channel.select({foo_select_action?})\n      case __temp_1\n      when 0\n        x = __temp_2.as(typeof(foo?))\n        x + 1\n      else\n        ::raise(\"BUG: invalid select index\")\n      end\n      CRYSTAL\n  end\n\n  it \"normalizes select with assign and bang method\" do\n    assert_expand \"select; when x = foo!; x + 1; end\", <<-CRYSTAL\n      __temp_1, __temp_2 = ::Channel.select({foo_select_action!})\n      case __temp_1\n      when 0\n        x = __temp_2.as(typeof(foo!))\n        x + 1\n      else\n        ::raise(\"BUG: invalid select index\")\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/string_interpolation_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: string interpolation\" do\n  it \"normalizes string interpolation\" do\n    assert_expand %(\"foo\\#{bar}baz\"), %(::String.interpolation(\"foo\", bar, \"baz\"))\n  end\n\n  it \"normalizes string interpolation with multiple lines\" do\n    assert_expand %(\"foo\\n\\#{bar}\\nbaz\\nqux\\nfox\"), %(::String.interpolation(\"foo\\\\n\", bar, \"\\\\nbaz\\\\nqux\\\\nfox\"))\n  end\n\n  it \"normalizes heredoc\" do\n    assert_normalize \"<<-FOO\\nhello\\nFOO\", %(\"hello\")\n  end\n\n  it \"replaces string constant\" do\n    result = semantic(<<-CRYSTAL)\n      def String.interpolation(*args); \"\"; end\n\n      OBJ = \"world\"\n\n      \"hello \\#{OBJ}\"\n      CRYSTAL\n    node = result.node.as(Expressions).last\n    string = node.should be_a(StringLiteral)\n    string.value.should eq(\"hello world\")\n  end\n\n  it \"replaces string constant that results from macro expansion\" do\n    result = semantic(<<-CRYSTAL)\n      def String.interpolation(*args); \"\"; end\n\n      OBJ = {% if 1 + 1 == 2 %} \"world\" {% else %} \"bug\" {% end %}\n\n      \"hello \\#{OBJ}\"\n      CRYSTAL\n    node = result.node.as(Expressions).last\n    string = node.should be_a(StringLiteral)\n    string.value.should eq(\"hello world\")\n  end\n\n  it \"replaces through multiple levels\" do\n    result = semantic(<<-CRYSTAL)\n      def String.interpolation(*args); \"\"; end\n\n      OBJ1 = \"ld\"\n      OBJ2 = \"wor\\#{OBJ1}\"\n\n      \"hello \\#{OBJ2}\"\n      CRYSTAL\n    node = result.node.as(Expressions).last\n    string = node.should be_a(StringLiteral)\n    string.value.should eq(\"hello world\")\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/unless_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: unless\" do\n  it \"normalizes unless\" do\n    assert_normalize \"unless 1; 2; end\", \"if 1\\nelse\\n  2\\nend\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/normalize/until_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Normalize: until\" do\n  it \"normalizes until\" do\n    assert_normalize \"until 1; 2; end\", \"while !1\\n  2\\nend\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/parser/parser_doc_spec.cr",
    "content": "require \"../../support/syntax\"\n\ndescribe \"Parser doc\" do\n  [\n    {\"class\", \"class Foo\\nend\"},\n    {\"abstract class\", \"abstract class Foo\\nend\"},\n    {\"struct\", \"struct Foo\\nend\"},\n    {\"module\", \"module Foo\\nend\"},\n    {\"def\", \"def foo\\nend\"},\n    {\"abstract def\", \"abstract def foo\"},\n    {\"macro\", \"macro foo\\nend\"},\n    {\"call without obj\", \"foo\"},\n    {\"fun def\", \"fun foo : Int32\\nend\"},\n    {\"enum def\", \"enum Foo\\nend\"},\n    {\"constant assign\", \"A = 1\"},\n    {\"alias\", \"alias Foo = Bar\"},\n    {\"annotation\", \"@[Some]\"},\n    {\"private def\", \"private def foo\\nend\"},\n    {\"lib def\", \"lib MyLib\\nend\"},\n  ].each do |(desc, code)|\n    it \"includes doc for #{desc}\" do\n      parser = Parser.new(<<-CRYSTAL)\n        # This is Foo.\n        # Use it well.\n        #{code}\n        CRYSTAL\n      parser.wants_doc = true\n      node = parser.parse\n      node.doc.should eq(\"This is Foo.\\nUse it well.\")\n    end\n  end\n\n  [\n    {\"type def\", \"type Foo = Bar\"},\n    {\"cstruct def\", \"struct Name\\nend\"},\n    {\"union def\", \"union Name\\nend\"},\n    {\"fun def\", \"fun name = Name\"},\n    {\"external var\", \"$errno : Int32\"},\n  ].each do |(desc, code)|\n    it \"includes doc for #{desc} inside lib def\" do\n      parser = Parser.new(<<-CRYSTAL)\n        lib MyLib\n          # This is Foo.\n          # Use it well.\n          #{code}\n        end\n        CRYSTAL\n      parser.wants_doc = true\n      node = parser.parse\n      node.as(Crystal::LibDef).body.doc.should eq(\"This is Foo.\\nUse it well.\")\n    end\n  end\n\n  it \"includes doc for cstruct fields\" do\n    parser = Parser.new(<<-CRYSTAL)\n      lib MyLib\n        struct IntOrFloat\n          # This is Foo.\n          # Use it well.\n          some_int : Int32\n          # This is Foo.\n          # Use it well.\n          some_float, other_float : Float64\n        end\n      end\n      CRYSTAL\n\n    parser.wants_doc = true\n    node = parser.parse\n    node.as(Crystal::LibDef)\n      .body.as(Crystal::CStructOrUnionDef)\n      .body.as(Crystal::Expressions)\n      .expressions.each do |exp|\n      exp.as(Crystal::TypeDeclaration)\n        .var.as(Crystal::Var)\n        .doc.should eq(\"This is Foo.\\nUse it well.\")\n    end\n  end\n\n  it \"disables doc parsing inside defs\" do\n    parser = Parser.new(<<-CRYSTAL)\n      # doc 1\n      def foo\n        # doc 2\n        bar\n      end\n\n      # doc 3\n      def baz\n      end\n      CRYSTAL\n    parser.wants_doc = true\n    nodes = parser.parse.as(Expressions)\n\n    foo = nodes[0].as(Def)\n    foo.doc.should eq(\"doc 1\")\n\n    bar = foo.body.as(Call)\n    bar.doc.should be_nil\n\n    baz = nodes[1].as(Def)\n    baz.doc.should eq(\"doc 3\")\n  end\nend\n"
  },
  {
    "path": "spec/compiler/parser/parser_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def regex(string, options = Regex::CompileOptions::None)\n  string = StringLiteral.new(string) if string.is_a?(String)\n  RegexLiteral.new(string, options)\nend\n\nprivate def command(string)\n  string = StringLiteral.new(string) if string.is_a?(String)\n  Call.new(\"`\", string)\nend\n\nprivate def string_array(*args)\n  ary = [] of ASTNode\n  ary.concat(args)\n  ary.array_of(Crystal::Path.global(\"String\"))\nend\n\nprivate def symbol_array(*args)\n  ary = [] of ASTNode\n  ary.concat(args)\n  ary.array_of(Crystal::Path.global(\"Symbol\"))\nend\n\nprivate def it_parses(string, expected_node, file = __FILE__, line = __LINE__, *, focus : Bool = false)\n  it \"parses #{string.dump}\", file, line, focus: focus do\n    parser = Parser.new(string)\n    parser.filename = \"/foo/bar/baz.cr\"\n    node = parser.parse\n\n    # If it's an Array, map it all to ASTNode (the array might be of a\n    # union that's not exactly ASTNode). Not having to write `[...] of ASTNode`\n    # simplifies testing a bit.\n    local_expected_node = expected_node\n    if local_expected_node.is_a?(Array)\n      local_expected_node = local_expected_node.map(&.as(ASTNode))\n    end\n\n    node.should eq(Expressions.from(local_expected_node))\n  end\nend\n\nprivate def it_parses_literal(literal, expectations, *, file = __FILE__, line = __LINE__)\n  # For percent literals, add additional variants. But allow them to be overridden with different expectations.\n  expectations.dup.each do |delimiter, expected_node|\n    if delimiter.starts_with?(\"%\")\n      expectations.put_if_absent(\"#{delimiter[..-2]}{\", expected_node)\n      expectations.put_if_absent(\"#{delimiter[..-2]}|\", expected_node)\n    end\n  end\n  expectations.each do |delimiter, expected_node|\n    end_delimiter = case delimiter[-1]\n                    when '[' then ']'\n                    when '{' then '}'\n                    when '(' then ')'\n                    else\n                      delimiter[-1]\n                    end\n    source = \"#{delimiter}#{literal}#{end_delimiter}\"\n    if expected_node.is_a?(String)\n      assert_syntax_error source, expected_node, metafile: file, metaline: line\n    else\n      it_parses source, expected_node, file: file, line: line\n    end\n  end\nend\n\nprivate def location_to_index(string, location)\n  index = 0\n  (location.line_number - 1).times do\n    index = string.index!('\\n', index) + 1\n  end\n  index + location.column_number - 1\nend\n\nprivate def source_between(string, loc, end_loc)\n  beginning = location_to_index(string, loc.not_nil!)\n  ending = location_to_index(string, end_loc.not_nil!)\n  string[beginning..ending]\nend\n\nprivate def node_source(string, node)\n  source_between(string, node.location, node.end_location)\nend\n\nprivate def assert_end_location(source, line_number = 1, column_number = source.size, file = __FILE__, line = __LINE__, *, focus : Bool = false)\n  it \"gets corrects end location for #{source.inspect}\", file, line, focus: focus do\n    string = \"#{source}; 1\"\n    parser = Parser.new(string)\n    node = parser.parse.as(Expressions).expressions[0]\n    node_source(string, node).should eq(source)\n    end_loc = node.end_location.not_nil!\n    end_loc.line_number.should eq(line_number)\n    end_loc.column_number.should eq(column_number)\n  end\nend\n\nprivate def assert_location(node : ASTNode, start_line_number : Int32, end_line_number : Int32) : Nil\n  location = node.location.should_not be_nil\n  location.line_number.should eq start_line_number\n  location = node.end_location.should_not be_nil\n  location.line_number.should eq end_line_number\nend\n\nmodule Crystal\n  describe \"Parser\" do\n    it_parses \"nil\", NilLiteral.new\n\n    it_parses \"true\", true.bool\n    it_parses \"false\", false.bool\n\n    it_parses \"1\", 1.int32\n    it_parses \"+1\", 1.int32\n    it_parses \"-1\", -1.int32\n\n    it_parses \"1_i64\", 1.int64\n    it_parses \"+1_i64\", 1.int64\n    it_parses \"-1_i64\", -1.int64\n\n    it_parses \"1_u128\", 1.uint128\n    it_parses \"1_i128\", 1.int128\n\n    it_parses \"1.0\", 1.0.float64\n    it_parses \"+1.0\", 1.0.float64\n    it_parses \"-1.0\", -1.0.float64\n\n    it_parses \"1.0_f32\", \"1.0\".float32\n    it_parses \"+1.0_f32\", \"+1.0\".float32\n    it_parses \"-1.0_f32\", \"-1.0\".float32\n\n    it_parses \"2.3_f32\", 2.3.float32\n\n    it_parses \"'a'\", CharLiteral.new('a')\n\n    it_parses %(\"foo\"), \"foo\".string\n    it_parses %(\"\"), \"\".string\n    it_parses %(\"hello \\\\\\n     world\"), \"hello world\".string\n    it_parses %(\"hello \\\\\\r\\n     world\"), \"hello world\".string\n\n    it_parses %(%Q{hello \\\\n world}), \"hello \\n world\".string\n    it_parses %(%q{hello \\\\n world}), \"hello \\\\n world\".string\n    it_parses %(%q{hello \\#{foo} world}), \"hello \\#{foo} world\".string\n\n    it_parses \":foo\", \"foo\".symbol\n    it_parses \":foo!\", \"foo!\".symbol\n    it_parses \":foo?\", \"foo?\".symbol\n    it_parses \":\\\"foo\\\"\", \"foo\".symbol\n    it_parses \":かたな\", \"かたな\".symbol\n    it_parses \":+\", \"+\".symbol\n    it_parses \":-\", \"-\".symbol\n    it_parses \":*\", \"*\".symbol\n    it_parses \":/\", \"/\".symbol\n    it_parses \":==\", \"==\".symbol\n    it_parses \":<\", \"<\".symbol\n    it_parses \":<=\", \"<=\".symbol\n    it_parses \":>\", \">\".symbol\n    it_parses \":>=\", \">=\".symbol\n    it_parses \":!\", \"!\".symbol\n    it_parses \":!=\", \"!=\".symbol\n    it_parses \":=~\", \"=~\".symbol\n    it_parses \":!~\", \"!~\".symbol\n    it_parses \":&\", \"&\".symbol\n    it_parses \":|\", \"|\".symbol\n    it_parses \":^\", \"^\".symbol\n    it_parses \":~\", \"~\".symbol\n    it_parses \":**\", \"**\".symbol\n    it_parses \":&**\", \"&**\".symbol\n    it_parses \":>>\", \">>\".symbol\n    it_parses \":<<\", \"<<\".symbol\n    it_parses \":%\", \"%\".symbol\n    it_parses \":[]\", \"[]\".symbol\n    it_parses \":[]?\", \"[]?\".symbol\n    it_parses \":[]=\", \"[]=\".symbol\n    it_parses \":<=>\", \"<=>\".symbol\n    it_parses \":===\", \"===\".symbol\n    it_parses \":[]=\", \"[]=\".symbol\n    it_parses \":[]?\", \"[]?\".symbol\n    it_parses %(:\"\\\\\\\\foo\"), \"\\\\foo\".symbol\n    it_parses %(:\"\\\\\"foo\"), \"\\\"foo\".symbol\n    it_parses %(:\"\\\\\"foo\\\\\"\"), \"\\\"foo\\\"\".symbol\n    it_parses %(:\"\\\\a\\\\b\\\\n\\\\r\\\\t\\\\v\\\\f\\\\e\"), \"\\a\\b\\n\\r\\t\\v\\f\\e\".symbol\n    it_parses %(:\"\\\\u{61}\"), \"a\".symbol\n    it_parses %(:\"\"), \"\".symbol\n\n    it_parses \"[1, 2]\", ([1.int32, 2.int32] of ASTNode).array\n    it_parses \"[\\n1, 2]\", ([1.int32, 2.int32] of ASTNode).array\n    it_parses \"[1,\\n 2,]\", ([1.int32, 2.int32] of ASTNode).array\n\n    it_parses \"1 + 2\", Call.new(1.int32, \"+\", 2.int32)\n    it_parses \"1 +\\n2\", Call.new(1.int32, \"+\", 2.int32)\n    it_parses \"1 +2\", Call.new(1.int32, \"+\", 2.int32)\n    it_parses \"1 -2\", Call.new(1.int32, \"-\", 2.int32)\n    it_parses \"1 +2.0\", Call.new(1.int32, \"+\", 2.float64)\n    it_parses \"1 -2.0\", Call.new(1.int32, \"-\", 2.float64)\n    it_parses \"1 +2_i64\", Call.new(1.int32, \"+\", 2.int64)\n    it_parses \"1 -2_i64\", Call.new(1.int32, \"-\", 2.int64)\n    it_parses \"1\\n+2\", [1.int32, 2.int32] of ASTNode\n    it_parses \"1;+2\", [1.int32, 2.int32] of ASTNode\n    it_parses \"1 - 2\", Call.new(1.int32, \"-\", 2.int32)\n    it_parses \"1 -\\n2\", Call.new(1.int32, \"-\", 2.int32)\n    it_parses \"1\\n-2\", [1.int32, -2.int32] of ASTNode\n    it_parses \"1;-2\", [1.int32, -2.int32] of ASTNode\n    it_parses \"1 * 2\", Call.new(1.int32, \"*\", 2.int32)\n    it_parses \"1 * -2\", Call.new(1.int32, \"*\", -2.int32)\n    it_parses \"2 * 3 + 4 * 5\", Call.new(Call.new(2.int32, \"*\", 3.int32), \"+\", Call.new(4.int32, \"*\", 5.int32))\n    it_parses \"1 / 2\", Call.new(1.int32, \"/\", 2.int32)\n    it_parses \"1 / -2\", Call.new(1.int32, \"/\", -2.int32)\n    it_parses \"2 / 3 + 4 / 5\", Call.new(Call.new(2.int32, \"/\", 3.int32), \"+\", Call.new(4.int32, \"/\", 5.int32))\n    it_parses \"2 * (3 + 4)\", Call.new(2.int32, \"*\", Expressions.new([Call.new(3.int32, \"+\", 4.int32)] of ASTNode))\n    it_parses \"a = 1; b = 2; c = 3; a-b-c\", Expressions.new([\n      Assign.new(\"a\".var, 1.int32),\n      Assign.new(\"b\".var, 2.int32),\n      Assign.new(\"c\".var, 3.int32),\n      Call.new(Call.new(\"a\".var, \"-\", \"b\".var), \"-\", \"c\".var),\n    ])\n    it_parses \"a = 1; b = 2; c = 3; a-b -c\", Expressions.new([\n      Assign.new(\"a\".var, 1.int32),\n      Assign.new(\"b\".var, 2.int32),\n      Assign.new(\"c\".var, 3.int32),\n      Call.new(Call.new(\"a\".var, \"-\", \"b\".var), \"-\", \"c\".var),\n    ])\n    it_parses \"1/2\", Call.new(1.int32, \"/\", [2.int32] of ASTNode)\n    it_parses \"1 + /foo/\", Call.new(1.int32, \"+\", regex(\"foo\"))\n    it_parses \"1+0\", Call.new(1.int32, \"+\", 0.int32)\n    it_parses \"a = 1; a /b\", [Assign.new(\"a\".var, 1.int32), Call.new(\"a\".var, \"/\", \"b\".call)]\n    it_parses \"a = 1; a/b\", [Assign.new(\"a\".var, 1.int32), Call.new(\"a\".var, \"/\", \"b\".call)]\n    it_parses \"a = 1; (a)/b\", [Assign.new(\"a\".var, 1.int32), Call.new(Expressions.new([\"a\".var] of ASTNode), \"/\", \"b\".call)]\n    it_parses \"_ = 1\", Assign.new(Underscore.new, 1.int32)\n    it_parses \"@foo/2\", Call.new(\"@foo\".instance_var, \"/\", 2.int32)\n    it_parses \"@@foo/2\", Call.new(\"@@foo\".class_var, \"/\", 2.int32)\n    it_parses \"1+2*3\", Call.new(1.int32, \"+\", Call.new(2.int32, \"*\", 3.int32))\n    it_parses \"foo[] /2\", Call.new(Call.new(\"foo\".call, \"[]\"), \"/\", 2.int32)\n    it_parses \"foo[1] /2\", Call.new(Call.new(\"foo\".call, \"[]\", 1.int32), \"/\", 2.int32)\n    it_parses \"[1] /2\", Call.new(([1.int32] of ASTNode).array, \"/\", 2.int32)\n    it_parses \"2**3**4\", Call.new(2.int32, \"**\", Call.new(3.int32, \"**\", 4.int32))\n\n    it_parses %(foo%i), Call.new(\"foo\".call, \"%\", \"i\".call)\n    it_parses %(foo%q), Call.new(\"foo\".call, \"%\", \"q\".call)\n    it_parses %(foo%Q), Call.new(\"foo\".call, \"%\", \"Q\".path)\n    it_parses %(foo%r), Call.new(\"foo\".call, \"%\", \"r\".call)\n    it_parses %(foo%x), Call.new(\"foo\".call, \"%\", \"x\".call)\n    it_parses %(foo%w), Call.new(\"foo\".call, \"%\", \"w\".call)\n\n    it_parses %(foo %i), Call.new(\"foo\".call, \"%\", \"i\".call)\n    it_parses %(foo %q), Call.new(\"foo\".call, \"%\", \"q\".call)\n    it_parses %(foo %Q), Call.new(\"foo\".call, \"%\", \"Q\".path)\n    it_parses %(foo %r), Call.new(\"foo\".call, \"%\", \"r\".call)\n    it_parses %(foo %x), Call.new(\"foo\".call, \"%\", \"x\".call)\n    it_parses %(foo %w), Call.new(\"foo\".call, \"%\", \"w\".call)\n\n    it_parses %(foo %i()), \"foo\".call(([] of ASTNode).array_of(Path.global(\"Symbol\")))\n    it_parses %(foo %q()), \"foo\".call(\"\".string)\n    it_parses %(foo %Q()), \"foo\".call(\"\".string)\n    it_parses %(foo %r()), \"foo\".call(regex(\"\"))\n    it_parses %(foo %x()), \"foo\".call(Call.new(\"`\", \"\".string))\n    it_parses %(foo %w()), \"foo\".call(([] of ASTNode).array_of(Path.global(\"String\")))\n\n    it_parses %(foo % i()), Call.new(\"foo\".call, \"%\", \"i\".call)\n    it_parses %(foo % q()), Call.new(\"foo\".call, \"%\", \"q\".call)\n    it_parses %(foo % Q()), Call.new(\"foo\".call, \"%\", Generic.new(\"Q\".path, [] of ASTNode))\n    it_parses %(foo % r()), Call.new(\"foo\".call, \"%\", \"r\".call)\n    it_parses %(foo % x()), Call.new(\"foo\".call, \"%\", \"x\".call)\n    it_parses %(foo % w()), Call.new(\"foo\".call, \"%\", \"w\".call)\n\n    it_parses \"!1\", Not.new(1.int32)\n    it_parses \"- 1\", Call.new(1.int32, \"-\")\n    it_parses \"+ 1\", Call.new(1.int32, \"+\")\n    it_parses \"~ 1\", Call.new(1.int32, \"~\")\n    it_parses \"1.~\", Call.new(1.int32, \"~\")\n    it_parses \"1.!\", Not.new(1.int32)\n    it_parses \"1 && 2\", And.new(1.int32, 2.int32)\n    it_parses \"1 || 2\", Or.new(1.int32, 2.int32)\n    it_parses \"&- 1\", Call.new(1.int32, \"&-\")\n    it_parses \"&+ 1\", Call.new(1.int32, \"&+\")\n\n    it_parses \"1 <=> 2\", Call.new(1.int32, \"<=>\", 2.int32)\n    it_parses \"1 !~ 2\", Call.new(1.int32, \"!~\", 2.int32)\n\n    it_parses \"a = 1\", Assign.new(\"a\".var, 1.int32)\n    it_parses \"a = b = 2\", Assign.new(\"a\".var, Assign.new(\"b\".var, 2.int32))\n    it_parses \"a[] = 1\", Call.new(\"a\".call, \"[]=\", 1.int32)\n    it_parses \"a.[] = 1\", Call.new(\"a\".call, \"[]=\", 1.int32)\n\n    it_parses \"a, b = 1, 2\", MultiAssign.new([\"a\".var, \"b\".var] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"a, b = 1\", MultiAssign.new([\"a\".var, \"b\".var] of ASTNode, [1.int32] of ASTNode)\n    it_parses \"_, _ = 1, 2\", MultiAssign.new([Underscore.new, Underscore.new] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"a[0], a[1] = 1, 2\", MultiAssign.new([Call.new(\"a\".call, \"[]\", 0.int32), Call.new(\"a\".call, \"[]\", 1.int32)] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"a[], a[] = 1, 2\", MultiAssign.new([Call.new(\"a\".call, \"[]\"), Call.new(\"a\".call, \"[]\")] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"a.foo, a.bar = 1, 2\", MultiAssign.new([Call.new(\"a\".call, \"foo\"), Call.new(\"a\".call, \"bar\")] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"x = 0; a, b = x += 1\", [Assign.new(\"x\".var, 0.int32), MultiAssign.new([\"a\".var, \"b\".var] of ASTNode, [OpAssign.new(\"x\".var, \"+\", 1.int32)] of ASTNode)] of ASTNode\n    it_parses \"a, b = 1, 2 if 3\", If.new(3.int32, MultiAssign.new([\"a\".var, \"b\".var] of ASTNode, [1.int32, 2.int32] of ASTNode))\n\n    it_parses \"*a = 1\", MultiAssign.new([\"a\".var.splat] of ASTNode, [1.int32] of ASTNode)\n    it_parses \"*a = 1, 2\", MultiAssign.new([\"a\".var.splat] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"*_ = 1, 2\", MultiAssign.new([Underscore.new.splat] of ASTNode, [1.int32, 2.int32] of ASTNode)\n\n    it_parses \"*a, b = 1\", MultiAssign.new([\"a\".var.splat, \"b\".var] of ASTNode, [1.int32] of ASTNode)\n    it_parses \"a, *b = 1\", MultiAssign.new([\"a\".var, \"b\".var.splat] of ASTNode, [1.int32] of ASTNode)\n    it_parses \"a, *b = 1, 2\", MultiAssign.new([\"a\".var, \"b\".var.splat] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"*a, b = 1, 2, 3, 4\", MultiAssign.new([\"a\".var.splat, \"b\".var] of ASTNode, [1.int32, 2.int32, 3.int32, 4.int32] of ASTNode)\n    it_parses \"a, b, *c = 1\", MultiAssign.new([\"a\".var, \"b\".var, \"c\".var.splat] of ASTNode, [1.int32] of ASTNode)\n    it_parses \"a, b, *c = 1, 2\", MultiAssign.new([\"a\".var, \"b\".var, \"c\".var.splat] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"_, *_, _, _ = 1, 2, 3\", MultiAssign.new([Underscore.new, Underscore.new.splat, Underscore.new, Underscore.new] of ASTNode, [1.int32, 2.int32, 3.int32] of ASTNode)\n\n    it_parses \"*a.foo, a.bar = 1\", MultiAssign.new([Call.new(\"a\".call, \"foo\").splat, Call.new(\"a\".call, \"bar\")] of ASTNode, [1.int32] of ASTNode)\n    it_parses \"a.foo, *a.bar = 1\", MultiAssign.new([Call.new(\"a\".call, \"foo\"), Call.new(\"a\".call, \"bar\").splat] of ASTNode, [1.int32] of ASTNode)\n\n    it_parses \"@a, b = 1, 2\", MultiAssign.new([\"@a\".instance_var, \"b\".var] of ASTNode, [1.int32, 2.int32] of ASTNode)\n    it_parses \"@@a, b = 1, 2\", MultiAssign.new([\"@@a\".class_var, \"b\".var] of ASTNode, [1.int32, 2.int32] of ASTNode)\n\n    it_parses \"あ.い, う.え.お = 1, 2\", MultiAssign.new([Call.new(\"あ\".call, \"い\"), Call.new(Call.new(\"う\".call, \"え\"), \"お\")] of ASTNode, [1.int32, 2.int32] of ASTNode)\n\n    assert_syntax_error \"b? = 1\", %(unexpected token: \"=\")\n    assert_syntax_error \"b! = 1\", %(unexpected token: \"=\")\n    assert_syntax_error \"a, B = 1, 2\", \"can't assign to constant in multiple assignment\"\n\n    assert_syntax_error \"1 == 2, a = 4\"\n    assert_syntax_error \"x : String, a = 4\"\n    assert_syntax_error \"b, 1 == 2, a = 4\"\n    assert_syntax_error \"a = 1, 2, 3\", \"Multiple assignment count mismatch\"\n    assert_syntax_error \"a = 1, b = 2\", \"Multiple assignment count mismatch\"\n\n    assert_syntax_error \"*a\"\n    assert_syntax_error \"*a if true\"\n    assert_syntax_error \"*a if true = 2\"\n    assert_syntax_error \"*a, 1 = 2\"\n    assert_syntax_error \"*1, a = 2\"\n    assert_syntax_error \"*a, *b = 1\", \"splat assignment already specified\"\n\n    assert_syntax_error \"*a, b, c, d = 1, 2\", \"Multiple assignment count mismatch\"\n    assert_syntax_error \"a, b, *c, d = 1, 2\", \"Multiple assignment count mismatch\"\n    assert_syntax_error \"*a, b, c, d, e = 1, 2\", \"Multiple assignment count mismatch\"\n    assert_syntax_error \"a, b, c, d, *e = 1, 2, 3\", \"Multiple assignment count mismatch\"\n\n    assert_syntax_error \"a = *1\", %(unexpected token: \"*\")\n    assert_syntax_error \"a = *1, 2\", %(unexpected token: \"*\")\n    assert_syntax_error \"a = 1, *2\", %(unexpected token: \"*\")\n    assert_syntax_error \"a, b = *1\", %(unexpected token: \"*\")\n    assert_syntax_error \"a, b = *1, 2\", %(unexpected token: \"*\")\n    assert_syntax_error \"a, b = 1, *2\", %(unexpected token: \"*\")\n    assert_syntax_error \"a, *b = *1\", %(unexpected token: \"*\")\n    assert_syntax_error \"a, *b = *1, 2\", %(unexpected token: \"*\")\n    assert_syntax_error \"a, *b = 1, *2\", %(unexpected token: \"*\")\n\n    # #11442, #12911\n    assert_syntax_error \"a, b.<=\"\n    assert_syntax_error \"*a == 1\"\n    assert_syntax_error \"*a === 1\"\n\n    assert_syntax_error \"a {}, b = 1\"\n    assert_syntax_error \"a.b {}, c = 1\"\n\n    assert_syntax_error \"a.b(), c.d = 1\"\n    assert_syntax_error \"a.b, c.d() = 1\"\n\n    assert_syntax_error \"a() = 1\"\n    assert_syntax_error \"a {} = 1\"\n    assert_syntax_error \"a.b() = 1\"\n    assert_syntax_error \"a.[]() = 1\"\n    assert_syntax_error \"a() += 1\"\n    assert_syntax_error \"a {} += 1\"\n    assert_syntax_error \"a.b() += 1\"\n    assert_syntax_error \"a.[]() += 1\"\n\n    assert_syntax_error \"a.[] 0 = 1\"\n    assert_syntax_error \"a.[] 0 += 1\"\n    assert_syntax_error \"a b: 0 = 1\"\n\n    it_parses \"def foo\\n1\\nend\", Def.new(\"foo\", body: 1.int32)\n    it_parses \"def downto(n)\\n1\\nend\", Def.new(\"downto\", [\"n\".arg], 1.int32)\n    it_parses \"def foo ; 1 ; end\", Def.new(\"foo\", body: 1.int32)\n    it_parses \"def foo; end\", Def.new(\"foo\")\n    it_parses \"def foo(var); end\", Def.new(\"foo\", [\"var\".arg])\n    it_parses \"def foo(\\nvar); end\", Def.new(\"foo\", [\"var\".arg])\n    it_parses \"def foo(\\nvar\\n); end\", Def.new(\"foo\", [\"var\".arg])\n    it_parses \"def foo(var1, var2); end\", Def.new(\"foo\", [\"var1\".arg, \"var2\".arg])\n    it_parses \"def foo; 1; 2; end\", Def.new(\"foo\", body: [1.int32, 2.int32] of ASTNode)\n    it_parses \"def foo=(value); end\", Def.new(\"foo=\", [\"value\".arg])\n    it_parses \"def foo(n); foo(n -1); end\", Def.new(\"foo\", [\"n\".arg], \"foo\".call(Call.new(\"n\".var, \"-\", 1.int32)))\n    it_parses \"def type(type); end\", Def.new(\"type\", [\"type\".arg])\n\n    # #4815\n    assert_syntax_error \"def foo!=; end\", %(unexpected token: \"!=\")\n    assert_syntax_error \"def foo?=(x); end\", %(unexpected token: \"?\")\n\n    # #5856\n    assert_syntax_error \"def foo=(a,b); end\", \"setter method 'foo=' cannot have more than one parameter\"\n    assert_syntax_error \"def foo=(a = 1, b = 2); end\", \"setter method 'foo=' cannot have more than one parameter\"\n    assert_syntax_error \"def foo=(*args); end\", \"setter method 'foo=' cannot have more than one parameter\"\n    assert_syntax_error \"def foo=(**kwargs); end\", \"setter method 'foo=' cannot have more than one parameter\"\n    assert_syntax_error \"def foo=(&block); end\", \"setter method 'foo=' cannot have a block\"\n\n    # #10397\n    describe \"multiple parameters and blocks for operators ending in `=`\" do\n      it_parses \"def <=(other, file = 1); end\", Def.new(\"<=\", [\"other\".arg, Arg.new(\"file\", 1.int32)])\n      it_parses \"def >=(other, file = 1); end\", Def.new(\">=\", [\"other\".arg, Arg.new(\"file\", 1.int32)])\n      it_parses \"def ==(other, file = 1); end\", Def.new(\"==\", [\"other\".arg, Arg.new(\"file\", 1.int32)])\n      it_parses \"def !=(other, file = 1); end\", Def.new(\"!=\", [\"other\".arg, Arg.new(\"file\", 1.int32)])\n      it_parses \"def []=(other, file = 1); end\", Def.new(\"[]=\", [\"other\".arg, Arg.new(\"file\", 1.int32)])\n      it_parses \"def ===(other, file = 1); end\", Def.new(\"===\", [\"other\".arg, Arg.new(\"file\", 1.int32)])\n\n      it_parses \"def <=(*args, **opts); end\", Def.new(\"<=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg)\n      it_parses \"def >=(*args, **opts); end\", Def.new(\">=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg)\n      it_parses \"def ==(*args, **opts); end\", Def.new(\"==\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg)\n      it_parses \"def !=(*args, **opts); end\", Def.new(\"!=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg)\n      it_parses \"def []=(*args, **opts); end\", Def.new(\"[]=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg)\n      it_parses \"def ===(*args, **opts); end\", Def.new(\"===\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg)\n\n      it_parses \"def <=(*args, **opts, &); end\", Def.new(\"<=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg, block_arg: Arg.new(\"\"), block_arity: 0)\n      it_parses \"def >=(*args, **opts, &); end\", Def.new(\">=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg, block_arg: Arg.new(\"\"), block_arity: 0)\n      it_parses \"def ==(*args, **opts, &); end\", Def.new(\"==\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg, block_arg: Arg.new(\"\"), block_arity: 0)\n      it_parses \"def !=(*args, **opts, &); end\", Def.new(\"!=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg, block_arg: Arg.new(\"\"), block_arity: 0)\n      it_parses \"def []=(*args, **opts, &); end\", Def.new(\"[]=\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg, block_arg: Arg.new(\"\"), block_arity: 0)\n      it_parses \"def ===(*args, **opts, &); end\", Def.new(\"===\", [\"args\".arg], splat_index: 0, double_splat: \"opts\".arg, block_arg: Arg.new(\"\"), block_arity: 0)\n    end\n\n    # #5895, #6042, #5997\n    %w(\n      begin nil true false yield with abstract\n      def macro require case select if unless include\n      extend class struct module enum while until return\n      next break lib fun alias pointerof sizeof\n      instance_sizeof offsetof typeof private protected asm out\n      end self in\n    ).each do |kw|\n      assert_syntax_error \"def foo(#{kw}); end\", \"cannot use '#{kw}' as a parameter name\", 1, 9\n      assert_syntax_error \"def foo(foo #{kw}); end\", \"cannot use '#{kw}' as a parameter name\", 1, 13\n      it_parses \"def foo(#{kw} foo); end\", Def.new(\"foo\", [Arg.new(\"foo\", external_name: kw.to_s)])\n      it_parses \"def foo(@#{kw}); end\", Def.new(\"foo\", [Arg.new(\"__arg0\", external_name: kw.to_s)], [Assign.new(\"@#{kw}\".instance_var, \"__arg0\".var)] of ASTNode)\n      it_parses \"def foo(@@#{kw}); end\", Def.new(\"foo\", [Arg.new(\"__arg0\", external_name: kw.to_s)], [Assign.new(\"@@#{kw}\".class_var, \"__arg0\".var)] of ASTNode)\n      it_parses \"def foo(x @#{kw}); end\", Def.new(\"foo\", [Arg.new(\"__arg0\", external_name: \"x\")], [Assign.new(\"@#{kw}\".instance_var, \"__arg0\".var)] of ASTNode)\n      it_parses \"def foo(x @@#{kw}); end\", Def.new(\"foo\", [Arg.new(\"__arg0\", external_name: \"x\")], [Assign.new(\"@@#{kw}\".class_var, \"__arg0\".var)] of ASTNode)\n\n      assert_syntax_error \"foo { |#{kw}| }\", \"cannot use '#{kw}' as a block parameter name\", 1, 8\n      assert_syntax_error \"foo { |(#{kw})| }\", \"cannot use '#{kw}' as a block parameter name\", 1, 9\n    end\n\n    # #10917\n    %w(\n      bar? bar!\n    ).each do |name|\n      assert_syntax_warning \"def foo(#{name}); end\", \"invalid parameter name: #{name}\"\n      assert_syntax_warning \"def foo(foo #{name}); end\", \"invalid parameter name: #{name}\"\n      it_parses \"def foo(#{name} foo); end\", Def.new(\"foo\", [Arg.new(\"foo\", external_name: name.to_s)])\n\n      assert_syntax_warning \"macro foo(#{name}); end\", \"invalid parameter name: #{name}\"\n      assert_syntax_warning \"macro foo(foo #{name}); end\", \"invalid parameter name: #{name}\"\n      it_parses \"macro foo(#{name} foo); end\", Macro.new(\"foo\", [Arg.new(\"foo\", external_name: name.to_s)], body: MacroLiteral.new(\" \"))\n\n      it_parses \"foo(#{name})\", Call.new(nil, \"foo\", [name.call] of ASTNode)\n      it_parses \"foo #{name}\", Call.new(nil, \"foo\", [name.call] of ASTNode)\n\n      assert_syntax_warning \"foo { |#{name}| }\", \"invalid parameter name: #{name}\"\n      assert_syntax_warning \"foo { |foo, (#{name})| }\", \"invalid parameter name: #{name}\"\n\n      assert_syntax_warning \"foo do |foo, #{name}|\\nend\", \"invalid parameter name: #{name}\"\n      assert_syntax_warning \"foo do |(#{name})|\\nend\", \"invalid parameter name: #{name}\"\n    end\n\n    it_parses \"def self.foo\\n1\\nend\", Def.new(\"foo\", body: 1.int32, receiver: \"self\".var)\n    it_parses \"def self.foo()\\n1\\nend\", Def.new(\"foo\", body: 1.int32, receiver: \"self\".var)\n    it_parses \"def self.foo=\\n1\\nend\", Def.new(\"foo=\", body: 1.int32, receiver: \"self\".var)\n    it_parses \"def self.foo=()\\n1\\nend\", Def.new(\"foo=\", body: 1.int32, receiver: \"self\".var)\n    it_parses \"def Foo.foo\\n1\\nend\", Def.new(\"foo\", body: 1.int32, receiver: \"Foo\".path)\n    it_parses \"def Foo::Bar.foo\\n1\\nend\", Def.new(\"foo\", body: 1.int32, receiver: [\"Foo\", \"Bar\"].path)\n\n    it_parses \"def foo; a; end\", Def.new(\"foo\", body: \"a\".call)\n    it_parses \"def foo(a); a; end\", Def.new(\"foo\", [\"a\".arg], \"a\".var)\n    it_parses \"def foo; a = 1; a; end\", Def.new(\"foo\", body: [Assign.new(\"a\".var, 1.int32), \"a\".var] of ASTNode)\n    it_parses \"def foo; a = 1; a {}; end\", Def.new(\"foo\", body: [Assign.new(\"a\".var, 1.int32), Call.new(\"a\", block: Block.new)] of ASTNode)\n    it_parses \"def foo; a = 1; x { a }; end\", Def.new(\"foo\", body: [Assign.new(\"a\".var, 1.int32), Call.new(\"x\", block: Block.new(body: [\"a\".var] of ASTNode))] of ASTNode)\n    it_parses \"def foo; x { |a| a }; end\", Def.new(\"foo\", body: [Call.new(\"x\", block: Block.new([\"a\".var], [\"a\".var] of ASTNode))] of ASTNode)\n    it_parses \"def foo; x { |_| 1 }; end\", Def.new(\"foo\", body: [Call.new(\"x\", block: Block.new([\"_\".var], [1.int32] of ASTNode))] of ASTNode)\n    it_parses \"def foo; x { |a, *b| b }; end\", Def.new(\"foo\", body: [Call.new(\"x\", block: Block.new([\"a\".var, \"b\".var], [\"b\".var] of ASTNode, splat_index: 1))] of ASTNode)\n    assert_syntax_error \"x { |*a, *b| }\", \"splat block parameter already specified\"\n\n    it_parses \"def foo(var = 1); end\", Def.new(\"foo\", [Arg.new(\"var\", 1.int32)])\n    it_parses \"def foo(var : Int); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: \"Int\".path)])\n    it_parses \"def foo(var : self); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: Self.new)])\n    it_parses \"def foo(var : self?); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: Crystal::Union.new([Self.new, Path.global(\"Nil\")] of ASTNode))])\n    it_parses \"def foo(var : self.class); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: Metaclass.new(Self.new))])\n    it_parses \"def foo(var : self*); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: Self.new.pointer_of)])\n    it_parses \"def foo(var : Int | Double); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: Crystal::Union.new([\"Int\".path, \"Double\".path] of ASTNode))])\n    it_parses \"def foo(var : Int?); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: Crystal::Union.new([\"Int\".path, \"Nil\".path(true)] of ASTNode))])\n    it_parses \"def foo(var : Int*); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: \"Int\".path.pointer_of)])\n    it_parses \"def foo(var : Int**); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: \"Int\".path.pointer_of.pointer_of)])\n    it_parses \"def foo(var : Int -> Double); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: ProcNotation.new([\"Int\".path] of ASTNode, \"Double\".path))])\n    it_parses \"def foo(var : Int, Float -> Double); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: ProcNotation.new([\"Int\".path, \"Float\".path] of ASTNode, \"Double\".path))])\n    it_parses \"def foo(var : (Int, Float -> Double)); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: ProcNotation.new([\"Int\".path, \"Float\".path] of ASTNode, \"Double\".path))])\n    it_parses \"def foo(var : (Int, Float) -> Double); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: ProcNotation.new([\"Int\".path, \"Float\".path] of ASTNode, \"Double\".path))])\n    it_parses \"def foo(var : () -> Double); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: ProcNotation.new([] of ASTNode, \"Double\".path))])\n    it_parses \"def foo(var : Char[256]); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: \"Char\".static_array_of(256))])\n    it_parses \"def foo(var : Char[N]); end\", Def.new(\"foo\", [Arg.new(\"var\", restriction: \"Char\".static_array_of(\"N\".path))])\n    it_parses \"def foo(var : Int32 = 1); end\", Def.new(\"foo\", [Arg.new(\"var\", 1.int32, \"Int32\".path)])\n    it_parses \"def foo(var : Int32 -> = 1); end\", Def.new(\"foo\", [Arg.new(\"var\", 1.int32, ProcNotation.new([\"Int32\".path] of ASTNode))])\n    it_parses \"def foo; yield; end\", Def.new(\"foo\", body: Yield.new, block_arity: 0)\n    it_parses \"def foo; yield 1; end\", Def.new(\"foo\", body: Yield.new([1.int32] of ASTNode), block_arity: 1)\n    it_parses \"def foo; yield 1; yield; end\", Def.new(\"foo\", body: [Yield.new([1.int32] of ASTNode), Yield.new] of ASTNode, block_arity: 1)\n    it_parses \"def foo; yield(1); end\", Def.new(\"foo\", body: [Yield.new([1.int32] of ASTNode, has_parentheses: true)] of ASTNode, block_arity: 1)\n    it_parses \"def foo(a, b = a); end\", Def.new(\"foo\", [Arg.new(\"a\"), Arg.new(\"b\", \"a\".var)])\n    it_parses \"def foo(&block); end\", Def.new(\"foo\", block_arg: Arg.new(\"block\"), block_arity: 0)\n    it_parses \"def foo(&); end\", Def.new(\"foo\", block_arg: Arg.new(\"\"), block_arity: 0)\n    it_parses \"def foo(&\\n); end\", Def.new(\"foo\", block_arg: Arg.new(\"\"), block_arity: 0)\n    it_parses \"def foo(a, &block); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\"), block_arity: 0)\n    it_parses \"def foo(a, &block : Int -> Double); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path] of ASTNode, \"Double\".path)), block_arity: 1)\n    it_parses \"def foo(a, & : Int -> Double); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"\", restriction: ProcNotation.new([\"Int\".path] of ASTNode, \"Double\".path)), block_arity: 1)\n    it_parses \"def foo(a, &block : Int, Float -> Double); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path, \"Float\".path] of ASTNode, \"Double\".path)), block_arity: 2)\n    it_parses \"def foo(a, &block : Int, self -> Double); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path, Self.new] of ASTNode, \"Double\".path)), block_arity: 2)\n    it_parses \"def foo(a, &block : -> Double); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new(nil, \"Double\".path)), block_arity: 0)\n    it_parses \"def foo(a, &block : Int -> ); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path] of ASTNode)), block_arity: 1)\n    it_parses \"def foo(a, &block : self -> self); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new([Self.new] of ASTNode, Self.new)), block_arity: 1)\n    it_parses \"def foo(a, &block : Foo); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: Path.new(\"Foo\")), block_arity: 0)\n    it_parses \"def foo; with a yield; end\", Def.new(\"foo\", body: Yield.new(scope: \"a\".call), block_arity: 1)\n    it_parses \"def foo; with a yield 1; end\", Def.new(\"foo\", body: Yield.new([1.int32] of ASTNode, \"a\".call), block_arity: 1)\n    it_parses \"def foo; a = 1; with a yield a; end\", Def.new(\"foo\", body: [Assign.new(\"a\".var, 1.int32), Yield.new([\"a\".var] of ASTNode, \"a\".var)] of ASTNode, block_arity: 1)\n    it_parses \"def foo(@var); end\", Def.new(\"foo\", [Arg.new(\"var\")], [Assign.new(\"@var\".instance_var, \"var\".var)] of ASTNode)\n    it_parses \"def foo(@var); 1; end\", Def.new(\"foo\", [Arg.new(\"var\")], [Assign.new(\"@var\".instance_var, \"var\".var), 1.int32] of ASTNode)\n    it_parses \"def foo(@var = 1); 1; end\", Def.new(\"foo\", [Arg.new(\"var\", 1.int32)], [Assign.new(\"@var\".instance_var, \"var\".var), 1.int32] of ASTNode)\n    it_parses \"def foo(@@var); end\", Def.new(\"foo\", [Arg.new(\"var\")], [Assign.new(\"@@var\".class_var, \"var\".var)] of ASTNode)\n    it_parses \"def foo(@@var); 1; end\", Def.new(\"foo\", [Arg.new(\"var\")], [Assign.new(\"@@var\".class_var, \"var\".var), 1.int32] of ASTNode)\n    it_parses \"def foo(@@var = 1); 1; end\", Def.new(\"foo\", [Arg.new(\"var\", 1.int32)], [Assign.new(\"@@var\".class_var, \"var\".var), 1.int32] of ASTNode)\n    it_parses \"def foo(&@block); end\", Def.new(\"foo\", body: Assign.new(\"@block\".instance_var, \"block\".var), block_arg: Arg.new(\"block\"), block_arity: 0)\n\n    # Defs with annotated parameters\n    it_parses \"def foo(@[Foo] var); end\", Def.new(\"foo\", [\"var\".arg(annotations: [\"Foo\".ann])])\n    it_parses \"def foo(@[Foo] outer inner); end\", Def.new(\"foo\", [\"inner\".arg(annotations: [\"Foo\".ann], external_name: \"outer\")])\n    it_parses \"def foo(@[Foo]  var); end\", Def.new(\"foo\", [\"var\".arg(annotations: [\"Foo\".ann])])\n    it_parses \"def foo(a, @[Foo] var); end\", Def.new(\"foo\", [\"a\".arg, \"var\".arg(annotations: [\"Foo\".ann])])\n    it_parses \"def foo(a, @[Foo] &block); end\", Def.new(\"foo\", [\"a\".arg], block_arg: \"block\".arg(annotations: [\"Foo\".ann]), block_arity: 0)\n    it_parses \"def foo(@[Foo] @var); end\", Def.new(\"foo\", [\"var\".arg(annotations: [\"Foo\".ann])], [Assign.new(\"@var\".instance_var, \"var\".var)] of ASTNode)\n    it_parses \"def foo(@[Foo] var : Int32); end\", Def.new(\"foo\", [\"var\".arg(restriction: \"Int32\".path, annotations: [\"Foo\".ann])])\n    it_parses \"def foo(@[Foo] @[Bar] var : Int32); end\", Def.new(\"foo\", [\"var\".arg(restriction: \"Int32\".path, annotations: [\"Foo\".ann, \"Bar\".ann])])\n    it_parses \"def foo(@[Foo] &@block); end\", Def.new(\"foo\", body: Assign.new(\"@block\".instance_var, \"block\".var), block_arg: \"block\".arg(annotations: [\"Foo\".ann]), block_arity: 0)\n    it_parses \"def foo(@[Foo] *args); end\", Def.new(\"foo\", args: [\"args\".arg(annotations: [\"Foo\".ann])], splat_index: 0)\n    it_parses \"def foo(@[Foo] **args); end\", Def.new(\"foo\", double_splat: \"args\".arg(annotations: [\"Foo\".ann]))\n    it_parses <<-CRYSTAL, Def.new(\"foo\", [\"id\".arg(restriction: \"Int32\".path, annotations: [\"Foo\".ann]), \"name\".arg(restriction: \"String\".path, annotations: [\"Bar\".ann])])\n      def foo(\n        @[Foo]\n        id : Int32,\n        @[Bar] name : String\n      ); end\n    CRYSTAL\n\n    it_parses \"def foo(\\n&block\\n); end\", Def.new(\"foo\", block_arg: Arg.new(\"block\"), block_arity: 0)\n    it_parses \"def foo(&block :\\n Int ->); end\", Def.new(\"foo\", block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path] of ASTNode)), block_arity: 1)\n    it_parses \"def foo(&block : Int ->\\n); end\", Def.new(\"foo\", block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path] of ASTNode)), block_arity: 1)\n\n    it_parses \"def foo(a, &block : *Int -> ); end\", Def.new(\"foo\", [Arg.new(\"a\")], block_arg: Arg.new(\"block\", restriction: ProcNotation.new([\"Int\".path.splat] of ASTNode)), block_arity: 1)\n\n    it_parses \"def foo(x, *args, y = 2); 1; end\", Def.new(\"foo\", args: [\"x\".arg, \"args\".arg, Arg.new(\"y\", default_value: 2.int32)], body: 1.int32, splat_index: 1)\n    it_parses \"def foo(x, *args, y = 2, w, z = 3); 1; end\", Def.new(\"foo\", args: [\"x\".arg, \"args\".arg, Arg.new(\"y\", default_value: 2.int32), \"w\".arg, Arg.new(\"z\", default_value: 3.int32)], body: 1.int32, splat_index: 1)\n    it_parses \"def foo(x, *, y); 1; end\", Def.new(\"foo\", args: [\"x\".arg, \"\".arg, \"y\".arg], body: 1.int32, splat_index: 1)\n    assert_syntax_error \"def foo(x, *); 1; end\", \"named parameters must follow bare *\"\n    it_parses \"def foo(x, *, y, &); 1; end\", Def.new(\"foo\", args: [\"x\".arg, \"\".arg, \"y\".arg], body: 1.int32, splat_index: 1, block_arg: Arg.new(\"\"), block_arity: 0)\n\n    assert_syntax_error \"def foo(var = 1 : Int32); end\", \"the syntax for a parameter with a default value V and type T is `param : T = V`\"\n    assert_syntax_error \"def foo(var = x : Int); end\", \"the syntax for a parameter with a default value V and type T is `param : T = V`\"\n\n    it_parses \"def foo(**args)\\n1\\nend\", Def.new(\"foo\", body: 1.int32, double_splat: \"args\".arg)\n    it_parses \"def foo(x, **args)\\n1\\nend\", Def.new(\"foo\", body: 1.int32, args: [\"x\".arg], double_splat: \"args\".arg)\n    it_parses \"def foo(x, **args, &block)\\n1\\nend\", Def.new(\"foo\", body: 1.int32, args: [\"x\".arg], double_splat: \"args\".arg, block_arg: \"block\".arg, block_arity: 0)\n    it_parses \"def foo(**args)\\nargs\\nend\", Def.new(\"foo\", body: \"args\".var, double_splat: \"args\".arg)\n    it_parses \"def foo(x = 1, **args)\\n1\\nend\", Def.new(\"foo\", body: 1.int32, args: [Arg.new(\"x\", default_value: 1.int32)], double_splat: \"args\".arg)\n    it_parses \"def foo(**args : Foo)\\n1\\nend\", Def.new(\"foo\", body: 1.int32, double_splat: Arg.new(\"args\", restriction: \"Foo\".path))\n    it_parses \"def foo(**args : **Foo)\\n1\\nend\", Def.new(\"foo\", body: 1.int32, double_splat: Arg.new(\"args\", restriction: DoubleSplat.new(\"Foo\".path)))\n\n    assert_syntax_error \"def foo(**args, **args2); end\", \"only block parameter is allowed after double splat\"\n    assert_syntax_error \"def foo(**args, x); end\", \"only block parameter is allowed after double splat\"\n    assert_syntax_error \"def foo(**args, *x); end\", \"only block parameter is allowed after double splat\"\n\n    it_parses \"def foo(x y); y; end\", Def.new(\"foo\", args: [Arg.new(\"y\", external_name: \"x\")], body: \"y\".var)\n    it_parses \"def foo(x @var); end\", Def.new(\"foo\", [Arg.new(\"var\", external_name: \"x\")], [Assign.new(\"@var\".instance_var, \"var\".var)] of ASTNode)\n    it_parses \"def foo(x @@var); end\", Def.new(\"foo\", [Arg.new(\"var\", external_name: \"x\")], [Assign.new(\"@@var\".class_var, \"var\".var)] of ASTNode)\n    assert_syntax_error \"def foo(_ y); y; end\"\n    assert_syntax_error \"def foo(\\\"\\\" y); y; end\", \"external parameter name cannot be empty\"\n\n    it_parses %(def foo(\"bar qux\" y); y; end), Def.new(\"foo\", args: [Arg.new(\"y\", external_name: \"bar qux\")], body: \"y\".var)\n\n    assert_syntax_error \"def foo(x x); 1; end\", \"when specified, external name must be different than internal name\"\n    assert_syntax_error \"def foo(x @x); 1; end\", \"when specified, external name must be different than internal name\"\n    assert_syntax_error \"def foo(x @@x); 1; end\", \"when specified, external name must be different than internal name\"\n\n    assert_syntax_error \"def foo(*a foo); end\"\n    assert_syntax_error \"def foo(**a foo); end\"\n    assert_syntax_error \"def foo(&a foo); end\"\n\n    it_parses \"macro foo(**args)\\n1\\nend\", Macro.new(\"foo\", body: MacroLiteral.new(\"1\\n\"), double_splat: \"args\".arg)\n\n    assert_syntax_error \"macro foo(\\\"\\\" y); end\", \"external parameter name cannot be empty\"\n\n    assert_syntax_error \"macro foo(x, *); 1; end\", \"named parameters must follow bare *\"\n    assert_syntax_error \"macro foo(**x, **y)\", \"only block parameter is allowed after double splat\"\n    assert_syntax_error \"macro foo(**x, y)\", \"only block parameter is allowed after double splat\"\n\n    it_parses \"abstract def foo\", Def.new(\"foo\", abstract: true)\n    it_parses \"abstract def foo; 1\", [Def.new(\"foo\", abstract: true), 1.int32]\n    it_parses \"abstract def foo\\n1\", [Def.new(\"foo\", abstract: true), 1.int32]\n    it_parses \"abstract def foo(x)\", Def.new(\"foo\", [\"x\".arg], abstract: true)\n\n    assert_syntax_error \"def foo var; end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"def foo var\\n end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"def foo &block ; end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"def foo &block : Int -> Double ; end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"def foo @var, &block; end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"def foo @@var, &block; end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"def foo *y; 1; end\", \"parentheses are mandatory for def parameters\"\n\n    it_parses \"def foo(x : U) forall U; end\", Def.new(\"foo\", args: [Arg.new(\"x\", restriction: \"U\".path)], free_vars: %w(U))\n    it_parses \"def foo(x : U) forall T, U; end\", Def.new(\"foo\", args: [Arg.new(\"x\", restriction: \"U\".path)], free_vars: %w(T U))\n    it_parses \"def foo(x : U) : Int32 forall T, U; end\", Def.new(\"foo\", args: [Arg.new(\"x\", restriction: \"U\".path)], return_type: \"Int32\".path, free_vars: %w(T U))\n    assert_syntax_error \"def foo(x : U) forall; end\"\n    assert_syntax_error \"def foo(x : U) forall U,; end\"\n    assert_syntax_error \"def foo(x : U) forall U, U; end\", \"duplicated free variable name: U\"\n\n    it_parses \"foo\", \"foo\".call\n    it_parses \"foo()\", \"foo\".call\n    it_parses \"foo(1)\", \"foo\".call(1.int32)\n    it_parses \"foo 1\", \"foo\".call(1.int32)\n    it_parses \"foo 1\\n\", \"foo\".call(1.int32)\n    it_parses \"foo 1;\", \"foo\".call(1.int32)\n    it_parses \"foo 1, 2\", \"foo\".call(1.int32, 2.int32)\n    it_parses \"foo (1 + 2), 3\", \"foo\".call(Expressions.new([Call.new(1.int32, \"+\", 2.int32)] of ASTNode), 3.int32)\n    it_parses \"foo(1 + 2)\", \"foo\".call(Call.new(1.int32, \"+\", 2.int32))\n    it_parses \"foo -1.0, -2.0\", \"foo\".call(-1.float64, -2.float64)\n    it_parses \"foo(\\n1)\", \"foo\".call(1.int32)\n    it_parses \"::foo\", Call.new(\"foo\", [] of ASTNode, nil, nil, nil, true)\n\n    it_parses \"foo + 1\", Call.new(\"foo\".call, \"+\", 1.int32)\n    it_parses \"foo +1\", Call.new(\"foo\", 1.int32)\n    it_parses \"foo +1.0\", Call.new(\"foo\", 1.float64)\n    it_parses \"foo +1_i64\", Call.new(\"foo\", 1.int64)\n    it_parses \"foo = 1; foo +1\", [Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\".var, \"+\", 1.int32)]\n    it_parses \"foo = 1; foo(+1)\", [Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\", 1.int32)]\n    it_parses \"foo = 1; foo -1\", [Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\".var, \"-\", 1.int32)]\n    it_parses \"foo = 1; foo(-1)\", [Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\", -1.int32)]\n    it_parses \"foo = 1; b = 2; foo -b\", [Assign.new(\"foo\".var, 1.int32), Assign.new(\"b\".var, 2.int32), Call.new(\"foo\".var, \"-\", \"b\".var)]\n    it_parses \"foo = 1; b = 2; foo +b\", [Assign.new(\"foo\".var, 1.int32), Assign.new(\"b\".var, 2.int32), Call.new(\"foo\".var, \"+\", \"b\".var)]\n    it_parses \"foo = 1; foo a: 1\", Expressions.new([Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32)])] of ASTNode)\n    it_parses \"foo = 1; foo {}\", Expressions.new([Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\", block: Block.new)] of ASTNode)\n    it_parses \"foo = 1; foo &x\", Expressions.new([Assign.new(\"foo\".var, 1.int32), Call.new(\"foo\", block_arg: \"x\".call)] of ASTNode)\n    it_parses \"def foo(x)\\n x\\nend; foo = 1; b = 2; foo -b\", [Def.new(\"foo\", [\"x\".arg], \"x\".var), Assign.new(\"foo\".var, 1.int32), Assign.new(\"b\".var, 2.int32), Call.new(\"foo\".var, \"-\", \"b\".var)]\n    it_parses \"def foo(x)\\n x\\nend; foo = 1; b = 2; foo +b\", [Def.new(\"foo\", [\"x\".arg], \"x\".var), Assign.new(\"foo\".var, 1.int32), Assign.new(\"b\".var, 2.int32), Call.new(\"foo\".var, \"+\", \"b\".var)]\n\n    it_parses \"foo(&block)\", Call.new(\"foo\", block_arg: \"block\".call)\n    it_parses \"foo &block\", Call.new(\"foo\", block_arg: \"block\".call)\n    it_parses \"a.foo &block\", Call.new(\"a\".call, \"foo\", block_arg: \"block\".call)\n    it_parses \"a.foo(&block)\", Call.new(\"a\".call, \"foo\", block_arg: \"block\".call)\n\n    it_parses \"foo(&.block)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"block\")))\n    it_parses \"foo &.block\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"block\")))\n    it_parses \"foo &./(1)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"/\", 1.int32)))\n    it_parses \"foo &.%(1)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"%\", 1.int32)))\n    it_parses \"foo &.block(1)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"block\", 1.int32)))\n    it_parses \"foo &.+(2)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"+\", 2.int32)))\n    it_parses \"foo &.bar.baz\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Call.new(Var.new(\"__arg0\"), \"bar\"), \"baz\")))\n    it_parses \"foo(&.bar.baz)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Call.new(Var.new(\"__arg0\"), \"bar\"), \"baz\")))\n    it_parses \"foo &.block[]\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Call.new(Var.new(\"__arg0\"), \"block\"), \"[]\")))\n    it_parses \"foo &.block[0]\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Call.new(Var.new(\"__arg0\"), \"block\"), \"[]\", 0.int32)))\n    it_parses \"foo &.block=(0)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"block=\", 0.int32)))\n    it_parses \"foo &.block = 0\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"block=\", 0.int32)))\n    it_parses \"foo &.block[] = 1\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Call.new(Var.new(\"__arg0\"), \"block\"), \"[]=\", 1.int32)))\n    it_parses \"foo &.block[0] = 1\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Call.new(Var.new(\"__arg0\"), \"block\"), \"[]=\", 0.int32, 1.int32)))\n    it_parses \"foo &.[]\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"[]\")))\n    it_parses \"foo &.[0]\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"[]\", 0.int32)))\n    it_parses \"foo &.[] = 1\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"[]=\", 1.int32)))\n    it_parses \"foo &.[0] = 1\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"[]=\", 0.int32, 1.int32)))\n    it_parses \"foo(&.is_a?(T))\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], IsA.new(Var.new(\"__arg0\"), \"T\".path)))\n    it_parses \"foo(&.!)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Not.new(Var.new(\"__arg0\"))))\n    it_parses \"foo(&.responds_to?(:foo))\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], RespondsTo.new(Var.new(\"__arg0\"), \"foo\")))\n    it_parses \"foo &.each {\\n}\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], Call.new(\"__arg0\".var, \"each\", block: Block.new)))\n    it_parses \"foo &.each do\\nend\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], Call.new(\"__arg0\".var, \"each\", block: Block.new)))\n    it_parses \"foo &.@bar\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], ReadInstanceVar.new(\"__arg0\".var, \"@bar\")))\n    it_parses \"foo &.@bar.baz\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], Call.new(ReadInstanceVar.new(\"__arg0\".var, \"@bar\"), \"baz\")))\n    it_parses \"foo(&.@bar.baz)\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], Call.new(ReadInstanceVar.new(\"__arg0\".var, \"@bar\"), \"baz\")))\n    it_parses \"foo &.@bar[baz]\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], Call.new(ReadInstanceVar.new(\"__arg0\".var, \"@bar\"), \"[]\", Call.new(\"baz\"))))\n    it_parses \"foo &.@bar.@baz\", Call.new(\"foo\", block: Block.new([\"__arg0\".var], ReadInstanceVar.new(ReadInstanceVar.new(\"__arg0\".var, \"@bar\"), \"@baz\")))\n\n    it_parses \"foo(&.as(T))\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Cast.new(Var.new(\"__arg0\"), \"T\".path)))\n    it_parses \"foo(&.as(T).bar)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Cast.new(Var.new(\"__arg0\"), \"T\".path), \"bar\")))\n    it_parses \"foo &.as(T)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Cast.new(Var.new(\"__arg0\"), \"T\".path)))\n    it_parses \"foo &.as(T).bar\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Cast.new(Var.new(\"__arg0\"), \"T\".path), \"bar\")))\n\n    it_parses \"foo(&.as?(T))\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], NilableCast.new(Var.new(\"__arg0\"), \"T\".path)))\n    it_parses \"foo(&.as?(T).bar)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(NilableCast.new(Var.new(\"__arg0\"), \"T\".path), \"bar\")))\n    it_parses \"foo &.as?(T)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], NilableCast.new(Var.new(\"__arg0\"), \"T\".path)))\n    it_parses \"foo &.as?(T).bar\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(NilableCast.new(Var.new(\"__arg0\"), \"T\".path), \"bar\")))\n    it_parses \"foo(\\n  &.block\\n)\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"block\")))\n\n    it_parses \"foo.[0]\", Call.new(\"foo\".call, \"[]\", 0.int32)\n    it_parses \"foo.[0] = 1\", Call.new(\"foo\".call, \"[]=\", [0.int32, 1.int32] of ASTNode)\n\n    it_parses \"foo(a: 1, b: 2)\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"foo(1, a: 1, b: 2)\", Call.new(\"foo\", [1.int32] of ASTNode, named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"foo a: 1, b: 2\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"foo 1, a: 1, b: 2\", Call.new(\"foo\", [1.int32] of ASTNode, named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"foo 1, a: 1, b: 2\\n1\", [Call.new(\"foo\", [1.int32] of ASTNode, named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)]), 1.int32]\n    it_parses \"foo(a: 1\\n)\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32)])\n    it_parses \"foo(\\na: 1,\\n)\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32)])\n\n    assert_syntax_error \"foo(\\\"\\\": 1)\", \"named argument cannot have an empty name\"\n\n    it_parses %(foo(\"foo bar\": 1, \"baz\": 2)), Call.new(\"foo\", named_args: [NamedArgument.new(\"foo bar\", 1.int32), NamedArgument.new(\"baz\", 2.int32)])\n    it_parses %(foo \"foo bar\": 1, \"baz\": 2), Call.new(\"foo\", named_args: [NamedArgument.new(\"foo bar\", 1.int32), NamedArgument.new(\"baz\", 2.int32)])\n\n    it_parses %(foo(Foo: 1, Bar: 2)), Call.new(\"foo\", named_args: [NamedArgument.new(\"Foo\", 1.int32), NamedArgument.new(\"Bar\", 2.int32)])\n\n    it_parses \"x.foo(a: 1, b: 2)\", Call.new(\"x\".call, \"foo\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"x.foo a: 1, b: 2 \", Call.new(\"x\".call, \"foo\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n\n    it_parses \"x[a: 1, b: 2]\", Call.new(\"x\".call, \"[]\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"x[a: 1, b: 2,]\", Call.new(\"x\".call, \"[]\", named_args: [NamedArgument.new(\"a\", 1.int32), NamedArgument.new(\"b\", 2.int32)])\n    it_parses \"x[{1}]\", Call.new(\"x\".call, \"[]\", TupleLiteral.new([1.int32] of ASTNode))\n    it_parses \"x[+ 1]\", Call.new(\"x\".call, \"[]\", Call.new(1.int32, \"+\"))\n\n    it_parses \"foo(a: 1, &block)\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32)], block_arg: \"block\".call)\n    it_parses \"foo a: 1, &block\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", 1.int32)], block_arg: \"block\".call)\n    it_parses \"foo a: b(1) do\\nend\", Call.new(\"foo\", named_args: [NamedArgument.new(\"a\", Call.new(\"b\", 1.int32))], block: Block.new)\n\n    it_parses \"Foo.bar x.y do\\nend\", Call.new(\"Foo\".path, \"bar\", args: [Call.new(\"x\".call, \"y\")] of ASTNode, block: Block.new)\n\n    it_parses \"x = 1; foo x do\\nend\", [Assign.new(\"x\".var, 1.int32), Call.new(\"foo\", [\"x\".var] of ASTNode, Block.new)]\n    it_parses \"x = 1; foo x { }\", [Assign.new(\"x\".var, 1.int32), Call.new(\"foo\", [Call.new(\"x\", block: Block.new)] of ASTNode)]\n    it_parses \"x = 1; foo x {\\n}\", [Assign.new(\"x\".var, 1.int32), Call.new(\"foo\", [Call.new(\"x\", block: Block.new)] of ASTNode)]\n    it_parses \"foo x do\\nend\", Call.new(\"foo\", [\"x\".call] of ASTNode, Block.new)\n    it_parses \"foo x, y do\\nend\", Call.new(\"foo\", [\"x\".call, \"y\".call] of ASTNode, Block.new)\n    it_parses \"foo(bar do\\nend)\", Call.new(\"foo\", [Call.new(\"bar\", [] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"foo(bar { })\", Call.new(\"foo\", [Call.new(\"bar\", [] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"(bar do\\nend)\", Expressions.new([Call.new(\"bar\", [] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"(bar do\\nend)\", Expressions.new([Call.new(\"bar\", [] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"(foo bar do\\nend)\", Expressions.new([Call.new(\"foo\", [\"bar\".call] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"(baz; bar do\\nend)\", Expressions.new([\"baz\".call, Call.new(\"bar\", [] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"(bar {})\", Expressions.new([Call.new(\"bar\", [] of ASTNode, Block.new)] of ASTNode)\n    it_parses \"(a;\\nb)\", Expressions.new([Call.new(\"a\"), Call.new(\"b\")] of ASTNode)\n    it_parses \"1.x; foo do\\nend\", [Call.new(1.int32, \"x\"), Call.new(\"foo\", block: Block.new)] of ASTNode\n    it_parses \"x = 1; foo.bar x do\\nend\", [Assign.new(\"x\".var, 1.int32), Call.new(\"foo\".call, \"bar\", [\"x\".var] of ASTNode, Block.new)]\n\n    describe \"block associativity\" do\n      describe \"surprise one: binds to second-to-the-right (#15303)\" do\n        it_parses \"a b c d e do; end\", Call.new(\"a\", Call.new(\"b\", Call.new(\"c\", Call.new(\"d\", Call.new(\"e\"), block: Block.new))))\n        it_parses \"a b c d e {}\", Call.new(\"a\", Call.new(\"b\", Call.new(\"c\", Call.new(\"d\", Call.new(\"e\", block: Block.new)))))\n      end\n\n      describe \"surprise two: block chains bind right-to-left starting from second-to-the-right (#15303)\" do\n        it_parses \"a b c d e do 1 end do 2 end { 3 } do 4 end\", Call.new(\"a\",\n          Call.new(\"b\",\n            Call.new(\"c\",\n              Call.new(\"d\",\n                Call.new(\"e\"),\n                block: Block.new(body: 1.int32)\n              ),\n              block: Block.new(body: 2.int32)\n            ),\n            block: Block.new(body: 3.int32)\n          ),\n          block: Block.new(body: 4.int32))\n        it_parses \"a b c d e { 1 } { 2 } do 3 end { 4 }\", Call.new(\"a\",\n          Call.new(\"b\",\n            Call.new(\"c\",\n              Call.new(\"d\",\n                Call.new(\"e\",\n                  block: Block.new(body: 1.int32)\n                ),\n                block: Block.new(body: 2.int32)\n              ),\n              block: Block.new(body: 3.int32)\n            ),\n            block: Block.new(body: 4.int32))\n        )\n      end\n\n      describe \"surprise three: arguments affect block binding (#15303)\" do\n        it_parses \"a b c d e 1, 2 do; end\", Call.new(\"a\", Call.new(\"b\", Call.new(\"c\", Call.new(\"d\", Call.new(\"e\", 1.int32, 2.int32, block: Block.new)))))\n        it_parses \"a b c d e 1, 2 {}\", Call.new(\"a\", Call.new(\"b\", Call.new(\"c\", Call.new(\"d\", Call.new(\"e\", 1.int32, 2.int32, block: Block.new)))))\n      end\n\n      describe \"surprise four: parentheses affect block binding (#15303)\" do\n        it_parses \"a 1, (2), b do end\", Call.new(\"a\", 1.int32, Expressions.new([2.int32] of ASTNode), Call.new(\"b\", block: Block.new))\n        it_parses \"a 1, (2), b {}\", Call.new(\"a\", 1.int32, Expressions.new([2.int32] of ASTNode), Call.new(\"b\", block: Block.new))\n      end\n    end\n\n    it_parses \"foo do\\n//\\nend\", Call.new(\"foo\", [] of ASTNode, Block.new(body: regex(\"\")))\n    it_parses \"foo x do\\n//\\nend\", Call.new(\"foo\", [\"x\".call] of ASTNode, Block.new(body: regex(\"\")))\n    it_parses \"foo(x) do\\n//\\nend\", Call.new(\"foo\", [\"x\".call] of ASTNode, Block.new(body: regex(\"\")))\n\n    it_parses \"foo !false\", Call.new(\"foo\", [Not.new(false.bool)] of ASTNode)\n    it_parses \"!a && b\", And.new(Not.new(\"a\".call), \"b\".call)\n\n    it_parses \"foo.bar.baz\", Call.new(Call.new(\"foo\".call, \"bar\"), \"baz\")\n    it_parses \"f.x Foo.new\", Call.new(\"f\".call, \"x\", [Call.new(\"Foo\".path, \"new\")] of ASTNode)\n    it_parses \"f.x = Foo.new\", Call.new(\"f\".call, \"x=\", [Call.new(\"Foo\".path, \"new\")] of ASTNode)\n    it_parses \"f.x = - 1\", Call.new(\"f\".call, \"x=\", [Call.new(1.int32, \"-\")] of ASTNode)\n\n    describe \"abbreviated assignment\" do\n      it_parses \"f.x += 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"+\", 2.int32)\n      it_parses \"f.x -= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"-\", 2.int32)\n      it_parses \"f.x *= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"*\", 2.int32)\n      it_parses \"f.x /= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"/\", 2.int32)\n      it_parses \"f.x //= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"//\", 2.int32)\n      it_parses \"f.x %= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"%\", 2.int32)\n      it_parses \"f.x |= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"|\", 2.int32)\n      it_parses \"f.x &= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"&\", 2.int32)\n      it_parses \"f.x ^= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"^\", 2.int32)\n      it_parses \"f.x **= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"**\", 2.int32)\n      it_parses \"f.x <<= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"<<\", 2.int32)\n      it_parses \"f.x >>= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \">>\", 2.int32)\n      it_parses \"f.x &+= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"&+\", 2.int32)\n      it_parses \"f.x &-= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"&-\", 2.int32)\n      it_parses \"f.x &*= 2\", OpAssign.new(Call.new(\"f\".call, \"x\"), \"&*\", 2.int32)\n    end\n\n    [\"/\", \"<\", \"<=\", \"==\", \"!=\", \"=~\", \"!~\", \">\", \">=\", \"+\", \"-\", \"*\", \"/\", \"~\", \"%\", \"&\", \"|\", \"^\", \"**\", \"===\"].each do |op|\n      it_parses \"def #{op}; end;\", Def.new(op)\n      it_parses \"def #{op}(); end;\", Def.new(op)\n      it_parses \"def self.#{op}; end;\", Def.new(op, receiver: \"self\".var)\n      it_parses \"def self.#{op}(); end;\", Def.new(op, receiver: \"self\".var)\n    end\n\n    [\"<<\", \"<\", \"<=\", \"==\", \">>\", \">\", \">=\", \"+\", \"-\", \"*\", \"/\", \"//\", \"%\", \"|\", \"&\", \"^\", \"**\", \"===\", \"=~\", \"!~\", \"&+\", \"&-\", \"&*\", \"&**\"].each do |op|\n      it_parses \"1 #{op} 2\", Call.new(1.int32, op, 2.int32)\n      it_parses \"n #{op} 2\", Call.new(\"n\".call, op, 2.int32)\n      it_parses \"foo(n #{op} 2)\", Call.new(\"foo\", Call.new(\"n\".call, op, 2.int32))\n      it_parses \"foo(0, n #{op} 2)\", Call.new(\"foo\", 0.int32, Call.new(\"n\".call, op, 2.int32))\n      it_parses \"foo(a: n #{op} 2)\", Call.new(\"foo\", [] of ASTNode, named_args: [NamedArgument.new(\"a\", Call.new(\"n\".call, op, 2.int32))])\n      it_parses \"foo(z: 0, a: n #{op} 2)\", Call.new(\"foo\", [] of ASTNode, named_args: [NamedArgument.new(\"z\", 0.int32), NamedArgument.new(\"a\", Call.new(\"n\".call, op, 2.int32))])\n      it_parses \"def #{op}(); end\", Def.new(op)\n\n      it_parses \"foo = 1; ->foo.#{op}(Int32)\", [Assign.new(\"foo\".var, 1.int32), ProcPointer.new(\"foo\".var, op, [\"Int32\".path] of ASTNode)]\n      it_parses \"->Foo.#{op}(Int32)\", ProcPointer.new(\"Foo\".path, op, [\"Int32\".path] of ASTNode)\n    end\n\n    [\"[]\", \"[]=\"].each do |op|\n      it_parses \"foo = 1; ->foo.#{op}(Int32)\", [Assign.new(\"foo\".var, 1.int32), ProcPointer.new(\"foo\".var, op, [\"Int32\".path] of ASTNode)]\n      it_parses \"->Foo.#{op}(Int32)\", ProcPointer.new(\"Foo\".path, op, [\"Int32\".path] of ASTNode)\n    end\n\n    [\"bar\", \"+\", \"-\", \"*\", \"/\", \"<\", \"<=\", \"==\", \">\", \">=\", \"%\", \"|\", \"&\", \"^\", \"**\", \"===\", \"=~\", \"!=\", \"[]=\", \"!~\"].each do |name|\n      it_parses \"foo.#{name}\", Call.new(\"foo\".call, name)\n      it_parses \"foo.#{name} 1, 2\", Call.new(\"foo\".call, name, 1.int32, 2.int32)\n      it_parses \"foo.#{name}(1, 2)\", Call.new(\"foo\".call, name, 1.int32, 2.int32)\n      it_parses \"foo.#{name}(1, 2) { 3 }\", Call.new(\"foo\".call, name, args: [1.int32, 2.int32] of ASTNode, block: Block.new(body: 3.int32))\n      it_parses \"foo.#{name} do end\", Call.new(\"foo\".call, name, block: Block.new)\n    end\n\n    [\"+\", \"-\", \"*\", \"/\", \"//\", \"%\", \"|\", \"&\", \"^\", \"**\", \"<<\", \">>\", \"&+\", \"&-\", \"&*\"].each do |op|\n      it_parses \"a = 1; a #{op}= 1\", [Assign.new(\"a\".var, 1.int32), OpAssign.new(\"a\".var, op, 1.int32)]\n      it_parses \"a = 1; a #{op}=\\n1\", [Assign.new(\"a\".var, 1.int32), OpAssign.new(\"a\".var, op, 1.int32)]\n      it_parses \"a.b #{op}=\\n1\", OpAssign.new(Call.new(\"a\".call, \"b\"), op, 1.int32)\n    end\n\n    it_parses \"a = 1; a &&= 1\", [Assign.new(\"a\".var, 1.int32), OpAssign.new(\"a\".var, \"&&\", 1.int32)]\n    it_parses \"a = 1; a ||= 1\", [Assign.new(\"a\".var, 1.int32), OpAssign.new(\"a\".var, \"||\", 1.int32)]\n\n    it_parses \"a = 1; a[2] &&= 3\", [Assign.new(\"a\".var, 1.int32), OpAssign.new(Call.new(\"a\".var, \"[]\", 2.int32), \"&&\", 3.int32)]\n    it_parses \"a = 1; a[2] ||= 3\", [Assign.new(\"a\".var, 1.int32), OpAssign.new(Call.new(\"a\".var, \"[]\", 2.int32), \"||\", 3.int32)]\n\n    it_parses \"if foo; 1; end\", If.new(\"foo\".call, 1.int32)\n    it_parses \"if foo\\n1\\nend\", If.new(\"foo\".call, 1.int32)\n    it_parses \"if foo; 1; else; 2; end\", If.new(\"foo\".call, 1.int32, 2.int32)\n    it_parses \"if foo\\n1\\nelse\\n2\\nend\", If.new(\"foo\".call, 1.int32, 2.int32)\n    it_parses \"if foo; 1; elsif bar; 2; else 3; end\", If.new(\"foo\".call, 1.int32, If.new(\"bar\".call, 2.int32, 3.int32))\n\n    it_parses \"include Foo\", Include.new(\"Foo\".path)\n    it_parses \"include Foo\\nif true; end\", [Include.new(\"Foo\".path), If.new(true.bool)]\n    it_parses \"extend Foo\", Extend.new(\"Foo\".path)\n    it_parses \"extend Foo\\nif true; end\", [Extend.new(\"Foo\".path), If.new(true.bool)]\n    it_parses \"extend self\", Extend.new(Self.new)\n\n    it_parses \"unless foo; 1; end\", Unless.new(\"foo\".call, 1.int32)\n    it_parses \"unless foo; 1; else; 2; end\", Unless.new(\"foo\".call, 1.int32, 2.int32)\n\n    it_parses \"class Foo; end\", ClassDef.new(\"Foo\".path)\n    it_parses \"class Foo\\nend\", ClassDef.new(\"Foo\".path)\n    it_parses \"class Foo\\ndef foo; end; end\", ClassDef.new(\"Foo\".path, [Def.new(\"foo\")] of ASTNode)\n    it_parses \"class Foo < Bar; end\", ClassDef.new(\"Foo\".path, superclass: \"Bar\".path)\n    it_parses \"class Foo(T); end\", ClassDef.new(\"Foo\".path, type_vars: [\"T\"])\n    it_parses \"class Foo(T1); end\", ClassDef.new(\"Foo\".path, type_vars: [\"T1\"])\n    it_parses \"class Foo(Type); end\", ClassDef.new(\"Foo\".path, type_vars: [\"Type\"])\n    it_parses \"abstract class Foo; end\", ClassDef.new(\"Foo\".path, abstract: true)\n    it_parses \"abstract struct Foo; end\", ClassDef.new(\"Foo\".path, abstract: true, struct: true)\n    assert_syntax_error \"class Foo(); end\", \"must specify at least one type var\"\n\n    it_parses \"class Foo < self; end\", ClassDef.new(\"Foo\".path, superclass: Self.new)\n\n    it_parses \"module Foo(*T); end\", ModuleDef.new(\"Foo\".path, type_vars: [\"T\"], splat_index: 0)\n    it_parses \"class Foo(*T); end\", ClassDef.new(\"Foo\".path, type_vars: [\"T\"], splat_index: 0)\n    it_parses \"class Foo(T, *U); end\", ClassDef.new(\"Foo\".path, type_vars: [\"T\", \"U\"], splat_index: 1)\n    assert_syntax_error \"class Foo(*T, *U); end\", \"splat type parameter already specified\"\n\n    it_parses \"x : Foo(A, *B, C)\", TypeDeclaration.new(\"x\".var, Generic.new(\"Foo\".path, [\"A\".path, \"B\".path.splat, \"C\".path] of ASTNode))\n    it_parses \"x : *T -> R\", TypeDeclaration.new(\"x\".var, ProcNotation.new([\"T\".path.splat] of ASTNode, \"R\".path))\n    it_parses \"def foo(x : *T -> R); end\", Def.new(\"foo\", args: [Arg.new(\"x\", restriction: ProcNotation.new([\"T\".path.splat] of ASTNode, \"R\".path))])\n\n    it_parses \"foo result : Int32; result\", Expressions.new([\n      Call.new(\"foo\", TypeDeclaration.new(\"result\".var, \"Int32\".path)),\n      Call.new(\"result\"),\n    ] of ASTNode)\n\n    it_parses \"foo(x: result : Int32); result\", Expressions.new([\n      Call.new(\"foo\", named_args: [NamedArgument.new(\"x\", TypeDeclaration.new(\"result\".var, \"Int32\".path))]),\n      Call.new(\"result\"),\n    ] of ASTNode)\n\n    it_parses \"foo(\n        begin\n          result : Int32 = 1\n          result\n        end\n      )\", Call.new(\"foo\", Expressions.new([\n      TypeDeclaration.new(\"result\".var, \"Int32\".path, 1.int32),\n      \"result\".var,\n    ] of ASTNode))\n\n    it_parses \"foo(x:\n        begin\n          result : Int32 = 1\n          result\n        end\n      )\", Call.new(\"foo\", named_args: [NamedArgument.new(\"x\", Expressions.new([\n      TypeDeclaration.new(\"result\".var, \"Int32\".path, 1.int32),\n      \"result\".var,\n    ] of ASTNode))])\n\n    it_parses \"struct Foo; end\", ClassDef.new(\"Foo\".path, struct: true)\n\n    it_parses \"Foo()\", Generic.new(\"Foo\".path, [] of ASTNode)\n    it_parses \"Foo(T)\", Generic.new(\"Foo\".path, [\"T\".path] of ASTNode)\n    it_parses \"Foo(T | U)\", Generic.new(\"Foo\".path, [Crystal::Union.new([\"T\".path, \"U\".path] of ASTNode)] of ASTNode)\n    it_parses \"Foo(Bar(T | U))\", Generic.new(\"Foo\".path, [Generic.new(\"Bar\".path, [Crystal::Union.new([\"T\".path, \"U\".path] of ASTNode)] of ASTNode)] of ASTNode)\n    it_parses \"Foo(Bar())\", Generic.new(\"Foo\".path, [Generic.new(\"Bar\".path, [] of ASTNode)] of ASTNode)\n    it_parses \"Foo(T?)\", Generic.new(\"Foo\".path, [Crystal::Union.new([\"T\".path, Path.global(\"Nil\")] of ASTNode)] of ASTNode)\n    it_parses \"Foo(1)\", Generic.new(\"Foo\".path, [1.int32] of ASTNode)\n    it_parses \"Foo(T, 1)\", Generic.new(\"Foo\".path, [\"T\".path, 1.int32] of ASTNode)\n    it_parses \"Foo(T, U, 1)\", Generic.new(\"Foo\".path, [\"T\".path, \"U\".path, 1.int32] of ASTNode)\n    it_parses \"Foo(T, 1, U)\", Generic.new(\"Foo\".path, [\"T\".path, 1.int32, \"U\".path] of ASTNode)\n    it_parses \"Foo(typeof(1))\", Generic.new(\"Foo\".path, [TypeOf.new([1.int32] of ASTNode)] of ASTNode)\n    it_parses \"Foo(typeof(1), typeof(2))\", Generic.new(\"Foo\".path, [TypeOf.new([1.int32] of ASTNode), TypeOf.new([2.int32] of ASTNode)] of ASTNode)\n    it_parses \"Foo({X, Y})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"Tuple\"), [\"X\".path, \"Y\".path] of ASTNode)] of ASTNode)\n    it_parses \"Foo({X, Y,})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"Tuple\"), [\"X\".path, \"Y\".path] of ASTNode)] of ASTNode)\n    it_parses \"Foo({*X, *{Y}})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"Tuple\"), [\"X\".path.splat, Generic.new(Path.global(\"Tuple\"), [\"Y\".path] of ASTNode).splat] of ASTNode)] of ASTNode)\n    it_parses \"Foo({->})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"Tuple\"), [ProcNotation.new] of ASTNode)] of ASTNode)\n    it_parses \"Foo({String, ->})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"Tuple\"), [\"String\".path, ProcNotation.new] of ASTNode)] of ASTNode)\n    it_parses \"Foo({String, ->, ->})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"Tuple\"), [\"String\".path, ProcNotation.new, ProcNotation.new] of ASTNode)] of ASTNode)\n    it_parses \"[] of {String, ->}\", ArrayLiteral.new([] of ASTNode, Generic.new(Path.global(\"Tuple\"), [\"String\".path, ProcNotation.new] of ASTNode))\n    it_parses \"x([] of Foo, Bar.new)\", Call.new(\"x\", ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(\"Bar\".path, \"new\"))\n\n    context \"calls with blocks within index operator (#12818)\" do\n      it_parses \"foo[bar { 1 }]\", Call.new(\"foo\".call, \"[]\", Call.new(\"bar\", block: Block.new(body: 1.int32)))\n      it_parses \"foo.[bar { 1 }]\", Call.new(\"foo\".call, \"[]\", Call.new(\"bar\", block: Block.new(body: 1.int32)))\n      it_parses \"foo.[](bar { 1 })\", Call.new(\"foo\".call, \"[]\", Call.new(\"bar\", block: Block.new(body: 1.int32)))\n      it_parses \"foo[bar do; 1; end]\", Call.new(\"foo\".call, \"[]\", Call.new(\"bar\", block: Block.new(body: 1.int32)))\n      it_parses \"foo.[bar do; 1; end]\", Call.new(\"foo\".call, \"[]\", Call.new(\"bar\", block: Block.new(body: 1.int32)))\n      it_parses \"foo.[](bar do; 1; end)\", Call.new(\"foo\".call, \"[]\", Call.new(\"bar\", block: Block.new(body: 1.int32)))\n    end\n\n    it_parses \"Foo(x: U)\", Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"U\".path)])\n    it_parses \"Foo(x: U, y: V)\", Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"U\".path), NamedArgument.new(\"y\", \"V\".path)])\n    it_parses \"Foo(X: U, Y: V)\", Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"X\", \"U\".path), NamedArgument.new(\"Y\", \"V\".path)])\n    assert_syntax_error \"Foo(T, x: U)\"\n    assert_syntax_error \"Foo(x: T y: U)\"\n    assert_syntax_error \"Foo(\\\"\\\": T)\", \"named argument cannot have an empty name\"\n\n    it_parses %(Foo(\"foo bar\": U)), Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"foo bar\", \"U\".path)])\n    it_parses %(Foo(\"foo\": U, \"bar\": V)), Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"foo\", \"U\".path), NamedArgument.new(\"bar\", \"V\".path)])\n\n    it_parses \"Foo({x: X})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"X\".path)])] of ASTNode)\n    it_parses \"Foo({x: X, y: Y})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"X\".path), NamedArgument.new(\"y\", \"Y\".path)])] of ASTNode)\n    it_parses \"Foo({X: X, Y: Y})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"X\", \"X\".path), NamedArgument.new(\"Y\", \"Y\".path)])] of ASTNode)\n    it_parses \"Foo(T, {x: X})\", Generic.new(\"Foo\".path, [\"T\".path, Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"X\".path)])] of ASTNode)\n    it_parses \"Foo({x: X, typeof: Y})\", Generic.new(\"Foo\".path, [Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"X\".path), NamedArgument.new(\"typeof\", \"Y\".path)])] of ASTNode)\n    assert_syntax_error \"Foo({x: X, x: Y})\", \"duplicated key: x\"\n\n    it_parses %(Foo({\"foo bar\": X})), Generic.new(\"Foo\".path, [Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"foo bar\", \"X\".path)])] of ASTNode)\n    it_parses %(Foo({\"foo\": X, \"bar\": Y})), Generic.new(\"Foo\".path, [Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: [NamedArgument.new(\"foo\", \"X\".path), NamedArgument.new(\"bar\", \"Y\".path)])] of ASTNode)\n\n    it_parses %(Foo{\"x\" => \"y\"}), HashLiteral.new([HashLiteral::Entry.new(\"x\".string, \"y\".string)], name: \"Foo\".path)\n    it_parses %(::Foo{\"x\" => \"y\"}), HashLiteral.new([HashLiteral::Entry.new(\"x\".string, \"y\".string)], name: Path.global(\"Foo\"))\n\n    it_parses \"Foo(*T)\", Generic.new(\"Foo\".path, [\"T\".path.splat] of ASTNode)\n\n    it_parses \"Foo(X, sizeof(Int32))\", Generic.new(\"Foo\".path, [\"X\".path, SizeOf.new(\"Int32\".path)] of ASTNode)\n    it_parses \"Foo(X, instance_sizeof(Int32))\", Generic.new(\"Foo\".path, [\"X\".path, InstanceSizeOf.new(\"Int32\".path)] of ASTNode)\n    it_parses \"Foo(X, alignof(Int32))\", Generic.new(\"Foo\".path, [\"X\".path, AlignOf.new(\"Int32\".path)] of ASTNode)\n    it_parses \"Foo(X, instance_alignof(Int32))\", Generic.new(\"Foo\".path, [\"X\".path, InstanceAlignOf.new(\"Int32\".path)] of ASTNode)\n    it_parses \"Foo(X, offsetof(Foo, @a))\", Generic.new(\"Foo\".path, [\"X\".path, OffsetOf.new(\"Foo\".path, \"@a\".instance_var)] of ASTNode)\n\n    it_parses \"Foo(\\n)\", Generic.new(\"Foo\".path, [] of ASTNode)\n    it_parses \"Foo(\\nT\\n)\", Generic.new(\"Foo\".path, [\"T\".path] of ASTNode)\n    it_parses \"Foo(\\nT,\\nU,\\n)\", Generic.new(\"Foo\".path, [\"T\".path, \"U\".path] of ASTNode)\n    it_parses \"Foo(\\nx:\\nT,\\ny:\\nU,\\n)\", Generic.new(\"Foo\".path, [] of ASTNode, named_args: [NamedArgument.new(\"x\", \"T\".path), NamedArgument.new(\"y\", \"U\".path)])\n\n    it_parses \"module Foo; end\", ModuleDef.new(\"Foo\".path)\n    it_parses \"module Foo\\ndef foo; end; end\", ModuleDef.new(\"Foo\".path, [Def.new(\"foo\")] of ASTNode)\n    it_parses \"module Foo(T); end\", ModuleDef.new(\"Foo\".path, type_vars: [\"T\"])\n\n    it_parses \"while true; end;\", While.new(true.bool)\n    it_parses \"while true; 1; end;\", While.new(true.bool, 1.int32)\n\n    it_parses \"until true; end;\", Until.new(true.bool)\n    it_parses \"until true; 1; end;\", Until.new(true.bool, 1.int32)\n\n    it_parses \"foo do; 1; end\", Call.new(\"foo\", block: Block.new(body: 1.int32))\n    it_parses \"foo do |a|; 1; end\", Call.new(\"foo\", block: Block.new([\"a\".var], 1.int32))\n\n    it_parses \"foo { 1 }\", Call.new(\"foo\", block: Block.new(body: 1.int32))\n    it_parses \"foo { |a| 1 }\", Call.new(\"foo\", block: Block.new([\"a\".var], 1.int32))\n    it_parses \"foo { |a, b| 1 }\", Call.new(\"foo\", block: Block.new([\"a\".var, \"b\".var], 1.int32))\n    it_parses \"foo { |a, b, | 1 }\", Call.new(\"foo\", block: Block.new([\"a\".var, \"b\".var], 1.int32))\n    it_parses \"1.foo do; 1; end\", Call.new(1.int32, \"foo\", block: Block.new(body: 1.int32))\n    it_parses \"a b() {}\", Call.new(\"a\", Call.new(\"b\", block: Block.new))\n\n    assert_syntax_error \"foo(&block) {}\"\n\n    it_parses \"foo { |a, (b, c), (d, e)| a; b; c; d; e }\", Call.new(\"foo\",\n      block: Block.new(\n        [\"a\".var, \"\".var, \"\".var],\n        Expressions.new([\n          \"a\".var,\n          \"b\".var,\n          \"c\".var,\n          \"d\".var,\n          \"e\".var,\n        ] of ASTNode),\n        unpacks: {\n          1 => Expressions.new([\"b\".var, \"c\".var] of ASTNode),\n          2 => Expressions.new([\"d\".var, \"e\".var] of ASTNode),\n        },\n      ),\n    )\n\n    it_parses \"foo { |(_, c)| c }\", Call.new(\"foo\",\n      block: Block.new([\"\".var],\n        \"c\".var,\n        unpacks: {0 => Expressions.new([Underscore.new, \"c\".var] of ASTNode)},\n      )\n    )\n\n    it_parses \"foo { |(_, c, )| c }\", Call.new(\"foo\",\n      block: Block.new([\"\".var],\n        \"c\".var,\n        unpacks: {0 => Expressions.new([Underscore.new, \"c\".var] of ASTNode)},\n      )\n    )\n\n    it_parses \"foo { |(a, (b, (c, d)))| }\", Call.new(\"foo\",\n      block: Block.new(\n        [\"\".var],\n        Nop.new,\n        unpacks: {\n          0 => Expressions.new([\n            \"a\".var,\n            Expressions.new([\n              \"b\".var,\n              Expressions.new([\n                \"c\".var,\n                \"d\".var,\n              ] of ASTNode),\n            ]),\n          ]),\n        },\n      ),\n    )\n\n    it_parses \"foo { |(a, *b, c)| }\", Call.new(\"foo\",\n      block: Block.new(\n        [\"\".var],\n        Nop.new,\n        unpacks: {\n          0 => Expressions.new([\n            \"a\".var,\n            Splat.new(\"b\".var),\n            \"c\".var,\n          ]),\n        },\n      ),\n    )\n\n    assert_syntax_error \"foo { |a b| }\", \"expecting ',' or '|', not b\"\n    assert_syntax_error \"foo { |(a b)| }\", \"expecting ',' or ')', not b\"\n\n    it_parses \"1 ? 2 : 3\", If.new(1.int32, 2.int32, 3.int32)\n    it_parses \"1 ? a : b\", If.new(1.int32, \"a\".call, \"b\".call)\n    it_parses \"1 ? a : b ? c : 3\", If.new(1.int32, \"a\".call, If.new(\"b\".call, \"c\".call, 3.int32))\n    it_parses \"a ? 1 : b ? 2 : c ? 3 : 0\", If.new(\"a\".call, 1.int32, If.new(\"b\".call, 2.int32, If.new(\"c\".call, 3.int32, 0.int32)))\n    it_parses \"a ? 1\n              : b\", If.new(\"a\".call, 1.int32, \"b\".call)\n    it_parses \"a ? 1 :\n              b ? 2 :\n              c ? 3\n              : 0\", If.new(\"a\".call, 1.int32, If.new(\"b\".call, 2.int32, If.new(\"c\".call, 3.int32, 0.int32)))\n    it_parses \"a ? 1\n              : b ? 2\n              : c ? 3\n              : 0\", If.new(\"a\".call, 1.int32, If.new(\"b\".call, 2.int32, If.new(\"c\".call, 3.int32, 0.int32)))\n    it_parses \"a ?\n              b ? b1 : b2\n              : c ? 3\n              : 0\", If.new(\"a\".call, If.new(\"b\".call, \"b1\".call, \"b2\".call), If.new(\"c\".call, 3.int32, 0.int32))\n\n    it_parses \"1 if 3\", If.new(3.int32, 1.int32)\n    it_parses \"1 unless 3\", Unless.new(3.int32, 1.int32)\n    it_parses \"r = 1; r.x += 2\", [Assign.new(\"r\".var, 1.int32), OpAssign.new(Call.new(\"r\".var, \"x\"), \"+\", 2.int32)] of ASTNode\n\n    it_parses \"foo if 3\", If.new(3.int32, \"foo\".call)\n    it_parses \"foo unless 3\", Unless.new(3.int32, \"foo\".call)\n\n    it_parses \"a = 1; a += 10 if a += 20\", [Assign.new(\"a\".var, 1.int32), If.new(OpAssign.new(\"a\".var, \"+\", 20.int32), OpAssign.new(\"a\".var, \"+\", 10.int32))]\n    it_parses \"puts a if true\", If.new(true.bool, Call.new(\"puts\", \"a\".call))\n    it_parses \"puts ::foo\", Call.new(\"puts\", Call.new(\"foo\", global: true))\n\n    it_parses \"puts __FILE__\", Call.new(\"puts\", \"/foo/bar/baz.cr\".string)\n    it_parses \"puts __DIR__\", Call.new(\"puts\", \"/foo/bar\".string)\n    it_parses \"puts __LINE__\", Call.new(\"puts\", 1.int32)\n    it_parses \"puts _\", Call.new(\"puts\", Underscore.new)\n\n    it_parses \"x = 2; foo do bar x end\", [Assign.new(\"x\".var, 2.int32), Call.new(\"foo\", block: Block.new(body: Call.new(\"bar\", \"x\".var)))] of ASTNode\n\n    { {\"break\", Break}, {\"return\", Return}, {\"next\", Next} }.each do |(keyword, klass)|\n      it_parses \"#{keyword}\", klass.new\n      it_parses \"#{keyword};\", klass.new\n      it_parses \"#{keyword} 1\", klass.new(1.int32)\n      it_parses \"#{keyword} 1, 2\", klass.new(TupleLiteral.new([1.int32, 2.int32] of ASTNode))\n      it_parses \"#{keyword} {1, 2}\", klass.new(TupleLiteral.new([1.int32, 2.int32] of ASTNode))\n      it_parses \"#{keyword} {1 => 2}\", klass.new(HashLiteral.new([HashLiteral::Entry.new(1.int32, 2.int32)]))\n      it_parses \"#{keyword} 1 if true\", If.new(true.bool, klass.new(1.int32))\n      it_parses \"#{keyword} if true\", If.new(true.bool, klass.new)\n\n      it_parses \"#{keyword} *1\", klass.new(TupleLiteral.new([1.int32.splat] of ASTNode))\n      it_parses \"#{keyword} *1, 2\", klass.new(TupleLiteral.new([1.int32.splat, 2.int32]))\n      it_parses \"#{keyword} 1, *2\", klass.new(TupleLiteral.new([1.int32, 2.int32.splat]))\n      it_parses \"#{keyword} *{1, 2}\", klass.new(TupleLiteral.new([TupleLiteral.new([1.int32, 2.int32] of ASTNode).splat] of ASTNode))\n\n      assert_syntax_error \"a = #{keyword}\", \"void value expression\"\n      assert_syntax_error \"a = 1; a += #{keyword}\", \"void value expression\"\n      assert_syntax_error \"yield #{keyword}\", \"void value expression\"\n      assert_syntax_error \"foo(#{keyword})\", \"void value expression\"\n      assert_syntax_error \"foo[#{keyword}]\", \"void value expression\"\n      assert_syntax_error \"foo[1] = #{keyword}\", \"void value expression\"\n      assert_syntax_error \"if #{keyword}; end\", \"void value expression\"\n      assert_syntax_error \"unless #{keyword}; end\", \"void value expression\"\n      assert_syntax_error \"while #{keyword}; end\", \"void value expression\"\n      assert_syntax_error \"until #{keyword}; end\", \"void value expression\"\n      assert_syntax_error \"1 if #{keyword}\", \"void value expression\"\n      assert_syntax_error \"1 unless #{keyword}\", \"void value expression\"\n      assert_syntax_error \"#{keyword}.foo\", \"void value expression\"\n      assert_syntax_error \"#{keyword}.as(Int32)\", \"void value expression\"\n      assert_syntax_error \"#{keyword}[]\", \"void value expression\"\n      assert_syntax_error \"#{keyword}[0]\", \"void value expression\"\n      assert_syntax_error \"#{keyword}[0]= 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} .. 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} ... 1\", \"void value expression\"\n      assert_syntax_error \"1 .. #{keyword}\", \"void value expression\"\n      assert_syntax_error \"1 ... #{keyword}\", \"void value expression\"\n      assert_syntax_error \"#{keyword} ? 1 : 2\", \"void value expression\"\n      assert_syntax_error \"+#{keyword}\", \"void value expression\"\n      assert_syntax_error \"#{keyword} << 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} < 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} <= 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} == 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} >> 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} > 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} >= 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} + 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} - 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} * 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} / 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} // 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} % 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} | 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} & 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} ^ 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} ** 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} === 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} &+ 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} &- 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} &* 1\", \"void value expression\"\n      assert_syntax_error \"#{keyword} &** 1\", \"void value expression\"\n      assert_syntax_error \"case #{keyword}; when 1; end; end\", \"void value expression\"\n      assert_syntax_error \"case 1; when #{keyword}; end; end\", \"void value expression\"\n    end\n\n    assert_syntax_error \"break when true\"\n\n    it_parses \"yield\", Yield.new\n    it_parses \"yield;\", Yield.new\n    it_parses \"yield 1\", Yield.new([1.int32] of ASTNode)\n    it_parses \"yield 1 if true\", If.new(true.bool, Yield.new([1.int32] of ASTNode))\n    it_parses \"yield if true\", If.new(true.bool, Yield.new)\n\n    it_parses \"Int\", \"Int\".path\n\n    it_parses \"Int[]\", Call.new(\"Int\".path, \"[]\")\n    it_parses \"def []; end\", Def.new(\"[]\")\n    it_parses \"def []?; end\", Def.new(\"[]?\")\n    it_parses \"def []=(value); end\", Def.new(\"[]=\", [\"value\".arg])\n    it_parses \"def self.[]; end\", Def.new(\"[]\", receiver: \"self\".var)\n    it_parses \"def self.[]?; end\", Def.new(\"[]?\", receiver: \"self\".var)\n\n    it_parses \"Int[8]\", Call.new(\"Int\".path, \"[]\", 8.int32)\n    it_parses \"Int[8, 4]\", Call.new(\"Int\".path, \"[]\", 8.int32, 4.int32)\n    it_parses \"Int[8, 4,]\", Call.new(\"Int\".path, \"[]\", 8.int32, 4.int32)\n    it_parses \"Int[8]?\", Call.new(\"Int\".path, \"[]?\", 8.int32)\n    it_parses \"x[0] ? 1 : 0\", If.new(Call.new(\"x\".call, \"[]\", 0.int32), 1.int32, 0.int32)\n\n    it_parses \"def [](x); end\", Def.new(\"[]\", [\"x\".arg])\n\n    it_parses \"foo[0] = 1\", Call.new(\"foo\".call, \"[]=\", 0.int32, 1.int32)\n    it_parses \"foo[0] = 1 if 2\", If.new(2.int32, Call.new(\"foo\".call, \"[]=\", 0.int32, 1.int32))\n\n    it_parses \"begin; 1; end;\", Expressions.new([1.int32] of ASTNode)\n    it_parses \"begin; 1; 2; 3; end;\", Expressions.new([1.int32, 2.int32, 3.int32] of ASTNode)\n\n    it_parses \"self\", \"self\".var\n\n    it_parses \"@foo\", \"@foo\".instance_var\n    it_parses \"@foo = 1\", Assign.new(\"@foo\".instance_var, 1.int32)\n    it_parses \"-@foo\", Call.new(\"@foo\".instance_var, \"-\")\n\n    it_parses \"var.@foo\", ReadInstanceVar.new(\"var\".call, \"@foo\")\n    it_parses \"var.@foo.@bar\", ReadInstanceVar.new(ReadInstanceVar.new(\"var\".call, \"@foo\"), \"@bar\")\n\n    it_parses \"@@foo\", \"@@foo\".class_var\n    it_parses \"@@foo = 1\", Assign.new(\"@@foo\".class_var, 1.int32)\n    it_parses \"-@@foo\", Call.new(\"@@foo\".class_var, \"-\")\n\n    it_parses \"call @foo.bar\", Call.new(\"call\", Call.new(\"@foo\".instance_var, \"bar\"))\n    it_parses \"call \\\"foo\\\"\", Call.new(\"call\", \"foo\".string)\n\n    it_parses \"def foo; end; if false; 1; else; 2; end\", [Def.new(\"foo\", [] of Arg), If.new(false.bool, 1.int32, 2.int32)]\n\n    it_parses \"A.new(\\\"x\\\", B.new(\\\"y\\\"))\", Call.new(\"A\".path, \"new\", \"x\".string, Call.new(\"B\".path, \"new\", \"y\".string))\n\n    it_parses \"foo [1]\", Call.new(\"foo\", ([1.int32] of ASTNode).array)\n    it_parses \"foo.bar [1]\", Call.new(\"foo\".call, \"bar\", ([1.int32] of ASTNode).array)\n\n    it_parses \"class Foo; end\\nwhile true; end\", [ClassDef.new(\"Foo\".path), While.new(true.bool)]\n    it_parses \"while true; end\\nif true; end\", [While.new(true.bool), If.new(true.bool)]\n    it_parses \"(1)\\nif true; end\", [Expressions.new([1.int32] of ASTNode), If.new(true.bool)]\n    it_parses \"begin\\n1\\nend\\nif true; end\", [Expressions.new([1.int32] of ASTNode), If.new(true.bool)]\n\n    it_parses \"Foo::Bar\", [\"Foo\", \"Bar\"].path\n\n    it_parses \"lib LibC\\nend\", LibDef.new(\"LibC\".path)\n    it_parses \"lib LibC\\nfun getchar\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\")] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar(...)\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", varargs: true)] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar : Int\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", return_type: \"Int\".path)] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar : (->)?\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", return_type: Crystal::Union.new([ProcNotation.new, \"Nil\".path(true)] of ASTNode))] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar(Int, Float)\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", [Arg.new(\"\", restriction: \"Int\".path), Arg.new(\"\", restriction: \"Float\".path)])] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar(a : Int, b : Float)\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", [Arg.new(\"a\", restriction: \"Int\".path), Arg.new(\"b\", restriction: \"Float\".path)])] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar(a : Int)\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", [Arg.new(\"a\", restriction: \"Int\".path)])] of ASTNode)\n    it_parses \"lib LibC\\nfun getchar(a : Int, b : Float) : Int\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", [Arg.new(\"a\", restriction: \"Int\".path), Arg.new(\"b\", restriction: \"Float\".path)], \"Int\".path)] of ASTNode)\n    it_parses \"lib LibC; fun getchar(a : Int, b : Float) : Int; end\", LibDef.new(\"LibC\".path, [FunDef.new(\"getchar\", [Arg.new(\"a\", restriction: \"Int\".path), Arg.new(\"b\", restriction: \"Float\".path)], \"Int\".path)] of ASTNode)\n    it_parses \"lib LibC; fun foo(a : Int*); end\", LibDef.new(\"LibC\".path, [FunDef.new(\"foo\", [Arg.new(\"a\", restriction: \"Int\".path.pointer_of)])] of ASTNode)\n    it_parses \"lib LibC; fun foo(a : Int**); end\", LibDef.new(\"LibC\".path, [FunDef.new(\"foo\", [Arg.new(\"a\", restriction: \"Int\".path.pointer_of.pointer_of)])] of ASTNode)\n    it_parses \"lib LibC; fun foo : Int*; end\", LibDef.new(\"LibC\".path, [FunDef.new(\"foo\", return_type: \"Int\".path.pointer_of)] of ASTNode)\n    it_parses \"lib LibC; fun foo : Int**; end\", LibDef.new(\"LibC\".path, [FunDef.new(\"foo\", return_type: \"Int\".path.pointer_of.pointer_of)] of ASTNode)\n    it_parses \"lib LibC; fun foo(a : ::B, ::C -> ::D); end\", LibDef.new(\"LibC\".path, [FunDef.new(\"foo\", [Arg.new(\"a\", restriction: ProcNotation.new([Path.global(\"B\"), Path.global(\"C\")] of ASTNode, Path.global(\"D\")))])] of ASTNode)\n    it_parses \"lib LibC; type A = B; end\", LibDef.new(\"LibC\".path, [TypeDef.new(\"A\", \"B\".path)] of ASTNode)\n    it_parses \"lib LibC; type A = B*; end\", LibDef.new(\"LibC\".path, [TypeDef.new(\"A\", \"B\".path.pointer_of)] of ASTNode)\n    it_parses \"lib LibC; type A = B**; end\", LibDef.new(\"LibC\".path, [TypeDef.new(\"A\", \"B\".path.pointer_of.pointer_of)] of ASTNode)\n    it_parses \"lib LibC; type A = B.class; end\", LibDef.new(\"LibC\".path, [TypeDef.new(\"A\", Metaclass.new(\"B\".path))] of ASTNode)\n    it_parses \"lib LibC; struct Foo; end end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\")] of ASTNode)\n    it_parses \"lib LibC; struct Foo; x : Int; y : Float; end end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\", [TypeDeclaration.new(\"x\".var, \"Int\".path), TypeDeclaration.new(\"y\".var, \"Float\".path)] of ASTNode)] of ASTNode)\n    it_parses \"lib LibC; struct Foo; x : Int*; end end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\", Expressions.from(TypeDeclaration.new(\"x\".var, \"Int\".path.pointer_of)))] of ASTNode)\n    it_parses \"lib LibC; struct Foo; x : Int**; end end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\", Expressions.from(TypeDeclaration.new(\"x\".var, \"Int\".path.pointer_of.pointer_of)))] of ASTNode)\n    it_parses \"lib LibC; struct Foo; x, y, z : Int; end end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\", [TypeDeclaration.new(\"x\".var, \"Int\".path), TypeDeclaration.new(\"y\".var, \"Int\".path), TypeDeclaration.new(\"z\".var, \"Int\".path)] of ASTNode)] of ASTNode)\n    it_parses \"lib LibC; union Foo; end end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\", union: true)] of ASTNode)\n    it_parses \"lib LibC; enum Foo; A\\nB; C\\nD = 1; end end\", LibDef.new(\"LibC\".path, [EnumDef.new(\"Foo\".path, [Arg.new(\"A\"), Arg.new(\"B\"), Arg.new(\"C\"), Arg.new(\"D\", 1.int32)] of ASTNode)] of ASTNode)\n    it_parses \"lib LibC; enum Foo; A = 1; B; end end\", LibDef.new(\"LibC\".path, [EnumDef.new(\"Foo\".path, [Arg.new(\"A\", 1.int32), Arg.new(\"B\")] of ASTNode)] of ASTNode)\n    it_parses \"lib LibC; Foo = 1; end\", LibDef.new(\"LibC\".path, [Assign.new(\"Foo\".path, 1.int32)] of ASTNode)\n    it_parses \"lib LibC\\nfun getch = GetChar\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"getch\", real_name: \"GetChar\")] of ASTNode)\n    it_parses %(lib LibC\\nfun getch = \"get.char\"\\nend), LibDef.new(\"LibC\".path, [FunDef.new(\"getch\", real_name: \"get.char\")] of ASTNode)\n    it_parses %(lib LibC\\nfun getch = \"get.char\" : Int32\\nend), LibDef.new(\"LibC\".path, [FunDef.new(\"getch\", return_type: \"Int32\".path, real_name: \"get.char\")] of ASTNode)\n    it_parses %(lib LibC\\nfun getch = \"get.char\"(x : Int32)\\nend), LibDef.new(\"LibC\".path, [FunDef.new(\"getch\", [Arg.new(\"x\", restriction: \"Int32\".path)], real_name: \"get.char\")] of ASTNode)\n    it_parses \"lib LibC\\n$errno : Int32\\n$errno2 : Int32\\nend\", LibDef.new(\"LibC\".path, [ExternalVar.new(\"errno\", \"Int32\".path), ExternalVar.new(\"errno2\", \"Int32\".path)] of ASTNode)\n    it_parses \"lib LibC\\n$errno : B, C -> D\\nend\", LibDef.new(\"LibC\".path, [ExternalVar.new(\"errno\", ProcNotation.new([\"B\".path, \"C\".path] of ASTNode, \"D\".path))] of ASTNode)\n    it_parses \"lib LibC\\n$errno = Foo : Int32\\nend\", LibDef.new(\"LibC\".path, [ExternalVar.new(\"errno\", \"Int32\".path, \"Foo\")] of ASTNode)\n    it_parses \"lib LibC\\nalias Foo = Bar\\nend\", LibDef.new(\"LibC\".path, [Alias.new(\"Foo\".path, \"Bar\".path)] of ASTNode)\n    it_parses \"lib LibC; struct Foo; include Bar; end; end\", LibDef.new(\"LibC\".path, [CStructOrUnionDef.new(\"Foo\", Include.new(\"Bar\".path))] of ASTNode)\n\n    it_parses \"lib LibC\\nfun SomeFun\\nend\", LibDef.new(\"LibC\".path, [FunDef.new(\"SomeFun\")] of ASTNode)\n\n    it_parses \"lib Foo::Bar\\nend\", LibDef.new(Path.new(\"Foo\", \"Bar\"))\n\n    it_parses \"fun foo(x : Int32) : Int64\\nx\\nend\", FunDef.new(\"foo\", [Arg.new(\"x\", restriction: \"Int32\".path)], \"Int64\".path, body: \"x\".var)\n    assert_syntax_error \"fun foo(Int32); end\", \"top-level fun parameter must have a name\"\n    assert_syntax_error \"fun Foo : Int64\\nend\"\n\n    it_parses \"lib LibC; {{ 1 }}; end\", LibDef.new(\"LibC\".path, body: [MacroExpression.new(1.int32)] of ASTNode)\n    it_parses \"lib LibC; {% if 1 %}2{% end %}; end\", LibDef.new(\"LibC\".path, body: [MacroIf.new(1.int32, MacroLiteral.new(\"2\"))] of ASTNode)\n\n    it_parses \"lib LibC; struct Foo; {{ 1 }}; end; end\", LibDef.new(\"LibC\".path, body: CStructOrUnionDef.new(\"Foo\", Expressions.from([MacroExpression.new(1.int32)] of ASTNode)))\n    it_parses \"lib LibC; struct Foo; {% if 1 %}2{% end %}; end; end\", LibDef.new(\"LibC\".path, body: CStructOrUnionDef.new(\"Foo\", Expressions.from([MacroIf.new(1.int32, MacroLiteral.new(\"2\"))] of ASTNode)))\n\n    it_parses \"1 .. 2\", RangeLiteral.new(1.int32, 2.int32, false)\n    it_parses \"1 ... 2\", RangeLiteral.new(1.int32, 2.int32, true)\n    it_parses \"(1 .. )\", Expressions.new([RangeLiteral.new(1.int32, Nop.new, false)] of ASTNode)\n    it_parses \"(1 ... )\", Expressions.new([RangeLiteral.new(1.int32, Nop.new, true)] of ASTNode)\n    it_parses \"foo(1.., 2)\", Call.new(\"foo\", [RangeLiteral.new(1.int32, Nop.new, false), 2.int32] of ASTNode)\n    it_parses \"1..;\", RangeLiteral.new(1.int32, Nop.new, false)\n    it_parses \"1..\\n2..\", Expressions.new([RangeLiteral.new(1.int32, Nop.new, false), RangeLiteral.new(2.int32, Nop.new, false)] of ASTNode)\n    it_parses \"{1.. => 2};\", HashLiteral.new([HashLiteral::Entry.new(RangeLiteral.new(1.int32, Nop.new, false), 2.int32)])\n    it_parses \"..2\", RangeLiteral.new(Nop.new, 2.int32, false)\n    it_parses \"...2\", RangeLiteral.new(Nop.new, 2.int32, true)\n    it_parses \"foo..2\", RangeLiteral.new(\"foo\".call, 2.int32, false)\n    it_parses \"foo ..2\", RangeLiteral.new(\"foo\".call, 2.int32, false)\n    it_parses \"foo(..2)\", Call.new(\"foo\", RangeLiteral.new(Nop.new, 2.int32, false))\n    it_parses \"x[..2]\", Call.new(\"x\".call, \"[]\", RangeLiteral.new(Nop.new, 2.int32, false))\n    it_parses \"x[1, ..2]\", Call.new(\"x\".call, \"[]\", [1.int32, RangeLiteral.new(Nop.new, 2.int32, false)] of ASTNode)\n    it_parses \"{..2}\", TupleLiteral.new([RangeLiteral.new(Nop.new, 2.int32, false)] of ASTNode)\n    it_parses \"[..2]\", ArrayLiteral.new([RangeLiteral.new(Nop.new, 2.int32, false)] of ASTNode)\n\n    it_parses \"A = 1\", Assign.new(\"A\".path, 1.int32)\n\n    it_parses \"puts %w(one two)\", Call.new(\"puts\", ([\"one\".string, \"two\".string] of ASTNode).array_of(Path.global(\"String\")))\n    it_parses \"puts %w{one two}\", Call.new(\"puts\", ([\"one\".string, \"two\".string] of ASTNode).array_of(Path.global(\"String\")))\n    it_parses \"puts %i(one two)\", Call.new(\"puts\", ([\"one\".symbol, \"two\".symbol] of ASTNode).array_of(Path.global(\"Symbol\")))\n    it_parses \"puts {{1}}\", Call.new(\"puts\", MacroExpression.new(1.int32))\n    it_parses \"puts {{\\n1\\n}}\", Call.new(\"puts\", MacroExpression.new(1.int32))\n    it_parses \"puts {{*1}}\", Call.new(\"puts\", MacroExpression.new(1.int32.splat))\n    it_parses \"puts {{**1}}\", Call.new(\"puts\", MacroExpression.new(DoubleSplat.new(1.int32)))\n    it_parses \"{{a = 1 if 2}}\", MacroExpression.new(If.new(2.int32, Assign.new(\"a\".var, 1.int32)))\n    it_parses \"{% a = 1 %}\", MacroExpression.new(Assign.new(\"a\".var, 1.int32), output: false)\n    it_parses \"{%\\na = 1\\n%}\", MacroExpression.new(Assign.new(\"a\".var, 1.int32), output: false)\n    it_parses \"{% a = 1 if 2 %}\", MacroExpression.new(If.new(2.int32, Assign.new(\"a\".var, 1.int32)), output: false)\n    it_parses \"{% if 1; 2; end %}\", MacroExpression.new(If.new(1.int32, 2.int32), output: false)\n    it_parses \"{%\\nif 1; 2; end\\n%}\", MacroExpression.new(If.new(1.int32, 2.int32), output: false)\n    it_parses \"{% if 1\\n  x\\nend %}\", MacroExpression.new(If.new(1.int32, \"x\".var), output: false)\n    it_parses \"{% x if 1 %}\", MacroExpression.new(If.new(1.int32, \"x\".var), output: false)\n    it_parses \"{% unless 1; 2; end %}\", MacroExpression.new(Unless.new(1.int32, 2.int32, Nop.new), output: false)\n    it_parses \"{% unless 1; 2; else 3; end %}\", MacroExpression.new(Unless.new(1.int32, 2.int32, 3.int32), output: false)\n    it_parses \"{% unless 1\\n  x\\nend %}\", MacroExpression.new(Unless.new(1.int32, \"x\".var), output: false)\n    it_parses \"{% x unless 1 %}\", MacroExpression.new(Unless.new(1.int32, \"x\".var), output: false)\n    it_parses \"{%\\n1\\n2\\n3\\n%}\", MacroExpression.new(Expressions.new([1.int32, 2.int32, 3.int32] of ASTNode), output: false)\n\n    assert_syntax_error \"{% unless 1; 2; elsif 3; 4; end %}\"\n    assert_syntax_error \"{% unless 1 %} 2 {% elsif 3 %} 3 {% end %}\"\n\n    it_parses \"{% if 1; 2; end; %}\", MacroExpression.new(If.new(1.int32, 2.int32), output: false)\n    it_parses \"{% if 1; 2; end; 3 %}\", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{%\\nif 1; 2; end; 3\\n%}\", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{% 2 if 1; 3 %}\", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{%\\n2 if 1; 3\\n%}\", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{% if 1; 2; elsif 3; 4; else; 5; end; 6 %}\", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32, If.new(3.int32, 4.int32, 5.int32)), 6.int32]), output: false)\n\n    it_parses \"{% unless 1; 2; end; %}\", MacroExpression.new(Unless.new(1.int32, 2.int32), output: false)\n    it_parses \"{% unless 1; 2; end; 3 %}\", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{%\\nunless 1; 2; end; 3\\n%}\", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{% 2 unless 1; 3 %}\", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)\n    it_parses \"{%\\n2 unless 1; 3\\n%}\", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)\n\n    it_parses \"{{ 1 // 2 }}\", MacroExpression.new(Expressions.from([Call.new(1.int32, \"//\", 2.int32)] of ASTNode))\n    it_parses \"{{ //.options }}\", MacroExpression.new(Expressions.from([Call.new(RegexLiteral.new(StringLiteral.new(\"\")), \"options\")] of ASTNode))\n\n    it_parses \"[] of Int\", ([] of ASTNode).array_of(\"Int\".path)\n    it_parses \"[1, 2] of Int\", ([1.int32, 2.int32] of ASTNode).array_of(\"Int\".path)\n\n    it_parses \"::A::B\", Path.global([\"A\", \"B\"])\n\n    assert_syntax_error \"$foo\", \"$global_variables are not supported, use @@class_variables instead\"\n\n    it_parses \"macro foo;end\", Macro.new(\"foo\", [] of Arg, Expressions.new)\n    it_parses \"macro [];end\", Macro.new(\"[]\", [] of Arg, Expressions.new)\n    it_parses %(macro foo; 1 + 2; end), Macro.new(\"foo\", [] of Arg, Expressions.from([\" 1 + 2; \".macro_literal] of ASTNode))\n    it_parses %(macro foo(x); 1 + 2; end), Macro.new(\"foo\", ([Arg.new(\"x\")]), Expressions.from([\" 1 + 2; \".macro_literal] of ASTNode))\n    it_parses %(macro foo(x)\\n 1 + 2; end), Macro.new(\"foo\", ([Arg.new(\"x\")]), Expressions.from([\" 1 + 2; \".macro_literal] of ASTNode))\n    it_parses \"macro foo; 1 + 2 {{foo}} 3 + 4; end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\" 1 + 2 \".macro_literal, MacroExpression.new(\"foo\".var), \" 3 + 4; \".macro_literal] of ASTNode))\n    it_parses \"macro foo; 1 + 2 {{ foo }} 3 + 4; end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\" 1 + 2 \".macro_literal, MacroExpression.new(\"foo\".var), \" 3 + 4; \".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% for x in y %}body{% end %}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroFor.new([\"x\".var], \"y\".var, \"body\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% for x, y in z %}body{% end %}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroFor.new([\"x\".var, \"y\".var], \"z\".var, \"body\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% if x %}body{% end %}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroIf.new(\"x\".var, \"body\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% if x %}body{% else %}body2{%end%}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroIf.new(\"x\".var, \"body\".macro_literal, \"body2\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% if x %}body{% elsif y %}body2{%end%}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroIf.new(\"x\".var, \"body\".macro_literal, MacroIf.new(\"y\".var, \"body2\".macro_literal)), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% if x %}body{% elsif y %}body2{% else %}body3{%end%}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroIf.new(\"x\".var, \"body\".macro_literal, MacroIf.new(\"y\".var, \"body2\".macro_literal, \"body3\".macro_literal)), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% unless x %}body{% end %}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroIf.new(\"x\".var, Nop.new, \"body\".macro_literal, is_unless: true), \"baz;\".macro_literal] of ASTNode))\n\n    it_parses \"macro foo;bar{% for x in y %}\\\\  \\n   body{% end %}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroFor.new([\"x\".var], \"y\".var, \"body\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo;bar{% for x in y %}\\\\  \\n   body{% end %}\\\\   baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroFor.new([\"x\".var], \"y\".var, \"body\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n    it_parses \"macro foo; 1 + 2 {{foo}}\\\\ 3 + 4; end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\" 1 + 2 \".macro_literal, MacroExpression.new(\"foo\".var), \"3 + 4; \".macro_literal] of ASTNode))\n\n    it_parses \"macro foo(\\na = 0\\n)\\nend\", Macro.new(\"foo\", [Arg.new(\"a\", default_value: 0.int32)], Expressions.new)\n\n    it_parses \"macro foo;{% verbatim do %}1{% foo %}2{% end %};end\", Macro.new(\"foo\", [] of Arg, Expressions.from([MacroVerbatim.new(Expressions.from([\"1\".macro_literal, MacroExpression.new(\"foo\".var, false), \"2\".macro_literal] of ASTNode)), \";\".macro_literal] of ASTNode))\n\n    it_parses \"macro foo\\n{%\\nif 1\\n2\\nelse\\n3\\nend\\n%}end\", Macro.new(\"foo\", body: MacroExpression.new(If.new(1.int32, 2.int32, 3.int32), output: false))\n\n    it_parses \"macro foo\\neenum\\nend\", Macro.new(\"foo\", body: Expressions.from([\"eenum\\n\".macro_literal] of ASTNode))\n    it_parses \"macro foo\\n'\\\\''\\nend\", Macro.new(\"foo\", body: Expressions.from([\"'\\\\''\\n\".macro_literal] of ASTNode))\n    it_parses \"macro foo\\n'\\\\\\\\'\\nend\", Macro.new(\"foo\", body: Expressions.from([\"'\\\\\\\\'\\n\".macro_literal] of ASTNode))\n    it_parses %(macro foo\\n\"\\\\'\"\\nend), Macro.new(\"foo\", body: Expressions.from([%(\"\\\\'\"\\n).macro_literal] of ASTNode))\n    it_parses %(macro foo\\n\"\\\\\\\\\"\\nend), Macro.new(\"foo\", body: Expressions.from([%(\"\\\\\\\\\"\\n).macro_literal] of ASTNode))\n\n    it_parses \"macro foo;bar(end: 1);end\", Macro.new(\"foo\", body: Expressions.from([\"bar(\".macro_literal, \"end: 1);\".macro_literal] of ASTNode))\n    it_parses \"def foo;bar(end: 1);end\", Def.new(\"foo\", body: Expressions.from([Call.new(\"bar\", named_args: [NamedArgument.new(\"end\", 1.int32)])] of ASTNode))\n\n    # Macros with annotated parameters\n    it_parses \"macro foo(@[Foo] var);end\", Macro.new(\"foo\", [\"var\".arg(annotations: [\"Foo\".ann])], Expressions.new)\n    it_parses \"macro foo(@[Foo] outer inner);end\", Macro.new(\"foo\", [\"inner\".arg(annotations: [\"Foo\".ann], external_name: \"outer\")], Expressions.new)\n    it_parses \"macro foo(@[Foo]  var);end\", Macro.new(\"foo\", [\"var\".arg(annotations: [\"Foo\".ann])], Expressions.new)\n    it_parses \"macro foo(a, @[Foo] var);end\", Macro.new(\"foo\", [\"a\".arg, \"var\".arg(annotations: [\"Foo\".ann])], Expressions.new)\n    it_parses \"macro foo(a, @[Foo] &block);end\", Macro.new(\"foo\", [\"a\".arg], Expressions.new, block_arg: \"block\".arg(annotations: [\"Foo\".ann]))\n    it_parses \"macro foo(@[Foo] *args);end\", Macro.new(\"foo\", [\"args\".arg(annotations: [\"Foo\".ann])], Expressions.new, splat_index: 0)\n    it_parses \"macro foo(@[Foo] **args);end\", Macro.new(\"foo\", body: Expressions.new, double_splat: \"args\".arg(annotations: [\"Foo\".ann]))\n    it_parses <<-CRYSTAL, Macro.new(\"foo\", [\"id\".arg(annotations: [\"Foo\".ann]), \"name\".arg(annotations: [\"Bar\".ann])], Expressions.new)\n      macro foo(\n        @[Foo]\n        id,\n        @[Bar] name\n      );end\n    CRYSTAL\n\n    assert_syntax_error \"macro foo; {% foo = 1 }; end\"\n    assert_syntax_error \"macro def foo : String; 1; end\"\n\n    it_parses \"macro foo=;end\", Macro.new(\"foo=\", body: Expressions.new)\n    assert_syntax_error \"macro Foo;end\", \"macro can't have a receiver\"\n    assert_syntax_error \"macro foo.bar;end\", \"macro can't have a receiver\"\n    assert_syntax_error \"macro Foo.bar;end\", \"macro can't have a receiver\"\n    assert_syntax_error \"macro foo&&;end\"\n    assert_syntax_error \"macro foo\"\n\n    describe \"operator as macro name\" do\n      it_parses \"macro `;end\", Macro.new(\"`\", body: Expressions.new)\n      it_parses \"macro <<;end\", Macro.new(\"<<\", body: Expressions.new)\n      it_parses \"macro <;end\", Macro.new(\"<\", body: Expressions.new)\n      it_parses \"macro <=;end\", Macro.new(\"<=\", body: Expressions.new)\n      it_parses \"macro ==;end\", Macro.new(\"==\", body: Expressions.new)\n      it_parses \"macro ===;end\", Macro.new(\"===\", body: Expressions.new)\n      it_parses \"macro !=;end\", Macro.new(\"!=\", body: Expressions.new)\n      it_parses \"macro =~;end\", Macro.new(\"=~\", body: Expressions.new)\n      it_parses \"macro !~;end\", Macro.new(\"!~\", body: Expressions.new)\n      it_parses \"macro >>;end\", Macro.new(\">>\", body: Expressions.new)\n      it_parses \"macro >;end\", Macro.new(\">\", body: Expressions.new)\n      it_parses \"macro >=;end\", Macro.new(\">=\", body: Expressions.new)\n      it_parses \"macro +;end\", Macro.new(\"+\", body: Expressions.new)\n      it_parses \"macro -;end\", Macro.new(\"-\", body: Expressions.new)\n      it_parses \"macro *;end\", Macro.new(\"*\", body: Expressions.new)\n      it_parses \"macro /;end\", Macro.new(\"/\", body: Expressions.new)\n      it_parses \"macro //;end\", Macro.new(\"//\", body: Expressions.new)\n      it_parses \"macro ~;end\", Macro.new(\"~\", body: Expressions.new)\n      it_parses \"macro %;end\", Macro.new(\"%\", body: Expressions.new)\n      it_parses \"macro &;end\", Macro.new(\"&\", body: Expressions.new)\n      it_parses \"macro |;end\", Macro.new(\"|\", body: Expressions.new)\n      it_parses \"macro ^;end\", Macro.new(\"^\", body: Expressions.new)\n      it_parses \"macro **;end\", Macro.new(\"**\", body: Expressions.new)\n      it_parses \"macro []?;end\", Macro.new(\"[]?\", body: Expressions.new)\n      it_parses \"macro []=;end\", Macro.new(\"[]=\", body: Expressions.new)\n      it_parses \"macro <=>;end\", Macro.new(\"<=>\", body: Expressions.new)\n      it_parses \"macro &+;end\", Macro.new(\"&+\", body: Expressions.new)\n      it_parses \"macro &-;end\", Macro.new(\"&-\", body: Expressions.new)\n      it_parses \"macro &*;end\", Macro.new(\"&*\", body: Expressions.new)\n      it_parses \"macro &**;end\", Macro.new(\"&**\", body: Expressions.new)\n    end\n\n    assert_syntax_error \"macro !;end\", \"'!' is a pseudo-method and can't be redefined\"\n\n    it_parses \"def foo;{{@type}};end\", Def.new(\"foo\", body: Expressions.from([MacroExpression.new(\"@type\".instance_var)] of ASTNode), macro_def: true)\n\n    it_parses \"macro foo;bar{% begin %}body{% end %}baz;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"bar\".macro_literal, MacroIf.new(true.bool, \"body\".macro_literal), \"baz;\".macro_literal] of ASTNode))\n\n    it_parses \"macro x\\n%{}\\nend\", Macro.new(\"x\", body: MacroLiteral.new(\"%{}\\n\"))\n\n    it_parses \"def foo : Int32\\n1\\nend\", Def.new(\"foo\", body: 1.int32, return_type: \"Int32\".path)\n    it_parses \"def foo(x) : Int32\\n1\\nend\", Def.new(\"foo\", args: [\"x\".arg], body: 1.int32, return_type: \"Int32\".path)\n\n    it_parses \"abstract def foo : Int32\", Def.new(\"foo\", return_type: \"Int32\".path, abstract: true)\n    it_parses \"abstract def foo(x) : Int32\", Def.new(\"foo\", args: [\"x\".arg], return_type: \"Int32\".path, abstract: true)\n\n    it_parses \"{% for x in y %}body{% end %}\", MacroFor.new([\"x\".var], \"y\".var, \"body\".macro_literal)\n    it_parses \"{% for _, x, _ in y %}body{% end %}\", MacroFor.new([\"_\".var, \"x\".var, \"_\".var], \"y\".var, \"body\".macro_literal)\n    it_parses \"{% if x %}body{% end %}\", MacroIf.new(\"x\".var, \"body\".macro_literal)\n    it_parses \"{% begin %}{% if true %}if true{% end %}\\n{% if true %}end{% end %}{% end %}\", MacroIf.new(true.bool, [MacroIf.new(true.bool, \"if true\".macro_literal), \"\\n\".macro_literal, MacroIf.new(true.bool, \"end\".macro_literal)] of ASTNode)\n    it_parses \"{{ foo }}\", MacroExpression.new(\"foo\".var)\n\n    it_parses \"macro foo;%var;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([MacroVar.new(\"var\"), MacroLiteral.new(\";\")] of ASTNode))\n    it_parses \"macro foo;%var{1, x} = hello;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([MacroVar.new(\"var\", [1.int32, \"x\".var] of ASTNode), MacroLiteral.new(\" = hello;\")] of ASTNode))\n\n    # #4087\n    describe \"suffix `if`/`unless` in macros after macro var\" do\n      it_parses \"macro foo;%var if true;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([MacroVar.new(\"var\"), \" if true;\".macro_literal] of ASTNode))\n      it_parses \"macro foo;var if true;end\", Macro.new(\"foo\", [] of Arg, \"var if true;\".macro_literal)\n      it_parses \"macro foo;if %var;true;end;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"if \".macro_literal, MacroVar.new(\"var\"), \";true;\".macro_literal, \"end;\".macro_literal] of ASTNode))\n      it_parses \"macro foo;if var;true;end;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"if var;true;\".macro_literal, \"end;\".macro_literal] of ASTNode))\n\n      it_parses \"macro foo;%var unless true;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([MacroVar.new(\"var\"), \" unless true;\".macro_literal] of ASTNode))\n      it_parses \"macro foo;var unless true;end\", Macro.new(\"foo\", [] of Arg, \"var unless true;\".macro_literal)\n      it_parses \"macro foo;unless %var;true;end;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"unless \".macro_literal, MacroVar.new(\"var\"), \";true;\".macro_literal, \"end;\".macro_literal] of ASTNode))\n      it_parses \"macro foo;unless var;true;end;end\", Macro.new(\"foo\", [] of Arg, Expressions.from([\"unless var;true;\".macro_literal, \"end;\".macro_literal] of ASTNode))\n    end\n\n    {'i', 'q', 'r', 'w', 'x', 'Q'}.each do |ch|\n      it_parses \"macro foo;%#{ch}[#{ch}];end\", Macro.new(\"foo\", [] of Arg, \"%#{ch}[#{ch}];\".macro_literal)\n    end\n\n    it_parses \"a = 1; pointerof(a)\", [Assign.new(\"a\".var, 1.int32), PointerOf.new(\"a\".var)]\n    it_parses \"pointerof(@a)\", PointerOf.new(\"@a\".instance_var)\n    it_parses \"a = 1; pointerof(a)\", [Assign.new(\"a\".var, 1.int32), PointerOf.new(\"a\".var)]\n    it_parses \"pointerof(@a)\", PointerOf.new(\"@a\".instance_var)\n\n    it_parses \"sizeof(X)\", SizeOf.new(\"X\".path)\n    it_parses \"instance_sizeof(X)\", InstanceSizeOf.new(\"X\".path)\n    it_parses \"alignof(X)\", AlignOf.new(\"X\".path)\n    it_parses \"instance_alignof(X)\", InstanceAlignOf.new(\"X\".path)\n    it_parses \"offsetof(X, @a)\", OffsetOf.new(\"X\".path, \"@a\".instance_var)\n    it_parses \"offsetof(X, 1)\", OffsetOf.new(\"X\".path, 1.int32)\n    assert_syntax_error \"offsetof(X, 1.0)\", \"expecting an integer offset, not '1.0'\"\n    assert_syntax_error \"offsetof(X, 'c')\", \"expecting an instance variable or a integer offset, not 'c'\"\n\n    it_parses \"foo.is_a?(Const)\", IsA.new(\"foo\".call, \"Const\".path)\n    it_parses \"foo.is_a?(Foo | Bar)\", IsA.new(\"foo\".call, Crystal::Union.new([\"Foo\".path, \"Bar\".path] of ASTNode))\n    it_parses \"foo.is_a? Const\", IsA.new(\"foo\".call, \"Const\".path)\n    it_parses \"foo.responds_to?(:foo)\", RespondsTo.new(\"foo\".call, \"foo\")\n    it_parses \"foo.responds_to? :foo\", RespondsTo.new(\"foo\".call, \"foo\")\n    it_parses \"if foo.responds_to? :foo\\nx = 1\\nend\", If.new(RespondsTo.new(\"foo\".call, \"foo\"), Assign.new(\"x\".var, 1.int32))\n\n    it_parses \"is_a?(Const)\", IsA.new(\"self\".var, \"Const\".path)\n    it_parses \"responds_to?(:foo)\", RespondsTo.new(\"self\".var, \"foo\")\n    it_parses \"nil?\", IsA.new(\"self\".var, Path.global(\"Nil\"), nil_check: true)\n    it_parses \"nil?(  )\", IsA.new(\"self\".var, Path.global(\"Nil\"), nil_check: true)\n\n    it_parses \"foo.nil?\", IsA.new(\"foo\".call, Path.global(\"Nil\"), nil_check: true)\n    it_parses \"foo.nil?(  )\", IsA.new(\"foo\".call, Path.global(\"Nil\"), nil_check: true)\n\n    it_parses \"foo &.nil?\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], IsA.new(Var.new(\"__arg0\"), Path.global(\"Nil\"), nil_check: true)))\n    it_parses \"foo &.baz.qux do\\nend\", Call.new(\"foo\",\n      block: Block.new([\"__arg0\".var],\n        Call.new(Call.new(\"__arg0\".var, \"baz\"), \"qux\", block: Block.new)\n      )\n    )\n\n    it_parses \"{{ foo.nil? }}\", MacroExpression.new(Call.new(Var.new(\"foo\"), \"nil?\"))\n    it_parses \"{{ foo &.nil? }}\", MacroExpression.new(Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Call.new(Var.new(\"__arg0\"), \"nil?\"))))\n    it_parses \"{{ foo.nil?(foo) }}\", MacroExpression.new(Call.new(Var.new(\"foo\"), \"nil?\", [Var.new(\"foo\")] of ASTNode))\n    it_parses \"{{ nil?(foo) }}\", MacroExpression.new(Call.new(\"nil?\", [Var.new(\"foo\")] of ASTNode))\n\n    it_parses \"foo.!\", Not.new(\"foo\".call)\n    it_parses \"foo.!.!\", Not.new(Not.new(\"foo\".call))\n    it_parses \"foo.!(  )\", Not.new(\"foo\".call)\n\n    it_parses \"foo &.!\", Call.new(\"foo\", block: Block.new([Var.new(\"__arg0\")], Not.new(Var.new(\"__arg0\"))))\n\n    # multiline pseudo methods (#8318)\n    it_parses \"sizeof(\\n  Int32\\n)\", SizeOf.new(Path.new(\"Int32\"))\n    it_parses \"instance_sizeof(\\n  Int32\\n)\", InstanceSizeOf.new(Path.new(\"Int32\"))\n    it_parses \"alignof(\\n  Int32\\n)\", AlignOf.new(Path.new(\"Int32\"))\n    it_parses \"instance_alignof(\\n  Int32\\n)\", InstanceAlignOf.new(Path.new(\"Int32\"))\n    it_parses \"typeof(\\n  1\\n)\", TypeOf.new([1.int32] of ASTNode)\n    it_parses \"offsetof(\\n  Foo,\\n  @foo\\n)\", OffsetOf.new(Path.new(\"Foo\"), InstanceVar.new(\"@foo\"))\n    it_parses \"pointerof(\\n  foo\\n)\", PointerOf.new(\"foo\".call)\n    it_parses \"1.as(\\n  Int32\\n)\", Cast.new(1.int32, Path.new(\"Int32\"))\n    it_parses \"1.as?(\\n  Int32\\n)\", NilableCast.new(1.int32, Path.new(\"Int32\"))\n    it_parses \"1.is_a?(\\n  Int32\\n)\", IsA.new(1.int32, Path.new(\"Int32\"))\n    it_parses \"1.responds_to?(\\n  :foo\\n)\", RespondsTo.new(1.int32, \"foo\")\n    it_parses \"1.nil?(\\n)\", IsA.new(1.int32, Path.global(\"Nil\"), nil_check: true)\n    it_parses \"1.!(\\n)\", Not.new(1.int32)\n\n    it_parses \"/foo/\", regex(\"foo\")\n    it_parses \"/foo/i\", regex(\"foo\", Regex::CompileOptions::IGNORE_CASE)\n    it_parses \"/foo/m\", regex(\"foo\", Regex::CompileOptions::MULTILINE)\n    it_parses \"/foo/x\", regex(\"foo\", Regex::CompileOptions::EXTENDED)\n    it_parses \"/foo/imximx\", regex(\"foo\", Regex::CompileOptions::IGNORE_CASE | Regex::CompileOptions::MULTILINE | Regex::CompileOptions::EXTENDED)\n    it_parses \"/fo\\\\so/\", regex(\"fo\\\\so\")\n    it_parses \"/fo\\#{1}o/\", RegexLiteral.new(StringInterpolation.new([\"fo\".string, 1.int32, \"o\".string] of ASTNode))\n    it_parses \"/(fo\\#{\\\"bar\\\"}\\#{1}o)/\", RegexLiteral.new(StringInterpolation.new([\"(fo\".string, \"bar\".string, 1.int32, \"o)\".string] of ASTNode))\n    it_parses \"%r(foo(bar))\", regex(\"foo(bar)\")\n    it_parses \"/ /\", regex(\" \")\n    it_parses \"/=/\", regex(\"=\")\n    it_parses \"/ hi /\", regex(\" hi \")\n    it_parses \"self / number\", Call.new(\"self\".var, \"/\", \"number\".call)\n    it_parses \"a == / /\", Call.new(\"a\".call, \"==\", regex(\" \"))\n    it_parses \"/ /\", regex(\" \")\n    it_parses \"/ /; / /\", [regex(\" \"), regex(\" \")] of ASTNode\n    it_parses \"/ /\\n/ /\", [regex(\" \"), regex(\" \")] of ASTNode\n    it_parses \"a = / /\", Assign.new(\"a\".var, regex(\" \"))\n    it_parses \"(/ /)\", Expressions.new([regex(\" \")] of ASTNode)\n    it_parses \"a = /=/\", Assign.new(\"a\".var, regex(\"=\"))\n    it_parses \"a; if / /; / /; elsif / /; / /; end\", [\"a\".call, If.new(regex(\" \"), regex(\" \"), If.new(regex(\" \"), regex(\" \")))]\n    it_parses \"a; if / /\\n/ /\\nelsif / /\\n/ /\\nend\", [\"a\".call, If.new(regex(\" \"), regex(\" \"), If.new(regex(\" \"), regex(\" \")))]\n    it_parses \"a; unless / /; / /; else; / /; end\", [\"a\".call, Unless.new(regex(\" \"), regex(\" \"), regex(\" \"))]\n    it_parses \"a\\nunless / /\\n/ /\\nelse\\n/ /\\nend\", [\"a\".call, Unless.new(regex(\" \"), regex(\" \"), regex(\" \"))]\n    it_parses \"a\\nwhile / /; / /; end\", [\"a\".call, While.new(regex(\" \"), regex(\" \"))]\n    it_parses \"a\\nwhile / /\\n/ /\\nend\", [\"a\".call, While.new(regex(\" \"), regex(\" \"))]\n    it_parses \"[/ /, / /]\", ArrayLiteral.new([regex(\" \"), regex(\" \")] of ASTNode)\n    it_parses \"{/ / => / /, / / => / /}\", HashLiteral.new([HashLiteral::Entry.new(regex(\" \"), regex(\" \")), HashLiteral::Entry.new(regex(\" \"), regex(\" \"))])\n    it_parses \"{/ /, / /}\", TupleLiteral.new([regex(\" \"), regex(\" \")] of ASTNode)\n    it_parses \"begin; / /; end\", Expressions.new([regex(\" \")] of ASTNode)\n    it_parses \"begin\\n/ /\\nend\", Expressions.new([regex(\" \")] of ASTNode)\n    it_parses \"/\\\\//\", regex(\"/\")\n    it_parses \"/\\\\ /\", regex(\" \")\n    it_parses \"%r(/)\", regex(\"/\")\n    it_parses \"%r(\\\\/)\", regex(\"/\")\n    it_parses \"%r(\\\\ )\", regex(\" \")\n    it_parses \"a()/3\", Call.new(\"a\".call, \"/\", 3.int32)\n    it_parses \"a() /3\", Call.new(\"a\".call, \"/\", 3.int32)\n    it_parses \"a.b() /3\", Call.new(Call.new(\"a\".call, \"b\"), \"/\", 3.int32)\n    it_parses \"def foo(x = / /); end\", Def.new(\"foo\", [Arg.new(\"x\", regex(\" \"))])\n    it_parses \"begin 1 end / 2\", Call.new(Expressions.new([1.int32] of ASTNode), \"/\", 2.int32)\n\n    it_parses \"1 =~ 2\", Call.new(1.int32, \"=~\", 2.int32)\n    it_parses \"1.=~(2)\", Call.new(1.int32, \"=~\", 2.int32)\n    it_parses \"def =~; end\", Def.new(\"=~\", [] of Arg)\n\n    describe \"global regex match data\" do\n      it_parses \"$~\", Global.new(\"$~\")\n      it_parses \"$~.foo\", Call.new(Global.new(\"$~\"), \"foo\")\n      it_parses \"$0\", Call.new(Global.new(\"$~\"), \"[]\", 0.int32)\n      it_parses \"$1\", Call.new(Global.new(\"$~\"), \"[]\", 1.int32)\n      it_parses \"$1?\", Call.new(Global.new(\"$~\"), \"[]?\", 1.int32)\n      it_parses \"foo $1\", Call.new(\"foo\", Call.new(Global.new(\"$~\"), \"[]\", 1.int32))\n      it_parses \"$~ = 1\", Assign.new(\"$~\".var, 1.int32)\n\n      assert_syntax_error \"$0 = 1\", \"global match data cannot be assigned to\"\n      assert_syntax_error \"$0, $1 = [1, 2]\", \"global match data cannot be assigned to\"\n      assert_syntax_error \"$0, a = {1, 2}\", \"global match data cannot be assigned to\"\n\n      assert_syntax_error \"$2147483648\"\n      assert_syntax_error \"$99999999999999999999999?\", \"Index $99999999999999999999999 doesn't fit in an Int32\"\n\n      it_parses \"$?\", Global.new(\"$?\")\n      it_parses \"$?.foo\", Call.new(Global.new(\"$?\"), \"foo\")\n      it_parses \"foo $?\", Call.new(\"foo\", Global.new(\"$?\"))\n      it_parses \"$? = 1\", Assign.new(\"$?\".var, 1.int32)\n    end\n\n    it_parses \"foo /a/\", Call.new(\"foo\", regex(\"a\"))\n    it_parses \"foo(/a/)\", Call.new(\"foo\", regex(\"a\"))\n    it_parses \"foo(//)\", Call.new(\"foo\", regex(\"\"))\n    it_parses \"foo(regex: //)\", Call.new(\"foo\", [] of ASTNode, named_args: [NamedArgument.new(\"regex\", regex(\"\"))])\n\n    it_parses \"foo(/ /)\", Call.new(\"foo\", regex(\" \"))\n    it_parses \"foo(/ /, / /)\", Call.new(\"foo\", [regex(\" \"), regex(\" \")] of ASTNode)\n    it_parses \"foo a, / /\", Call.new(\"foo\", [\"a\".call, regex(\" \")] of ASTNode)\n    it_parses \"foo /;/\", Call.new(\"foo\", regex(\";\"))\n\n    it_parses \"foo out x; x\", [Call.new(\"foo\", Out.new(\"x\".var)), \"x\".var]\n    it_parses \"foo(out x); x\", [Call.new(\"foo\", Out.new(\"x\".var)), \"x\".var]\n    it_parses \"foo out @x; @x\", [Call.new(\"foo\", Out.new(\"@x\".instance_var)), \"@x\".instance_var]\n    it_parses \"foo(out @x); @x\", [Call.new(\"foo\", Out.new(\"@x\".instance_var)), \"@x\".instance_var]\n    it_parses \"foo out _\", Call.new(\"foo\", Out.new(Underscore.new))\n    it_parses \"foo z: out x; x\", [Call.new(\"foo\", named_args: [NamedArgument.new(\"z\", Out.new(\"x\".var))]), \"x\".var]\n\n    it_parses \"{1 => 2, 3 => 4}\", HashLiteral.new([HashLiteral::Entry.new(1.int32, 2.int32), HashLiteral::Entry.new(3.int32, 4.int32)])\n    it_parses \"{1 =>\\n2, 3 =>\\n4}\", HashLiteral.new([HashLiteral::Entry.new(1.int32, 2.int32), HashLiteral::Entry.new(3.int32, 4.int32)])\n    it_parses %({A::B => 1, C::D => 2}), HashLiteral.new([HashLiteral::Entry.new(Path.new(\"A\", \"B\"), 1.int32), HashLiteral::Entry.new(Path.new(\"C\", \"D\"), 2.int32)])\n    assert_syntax_error %({\"foo\" => 1, \"bar\": 2}), \"can't use 'key: value' syntax in a hash literal\"\n\n    it_parses \"{a: 1}\", NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"a\", 1.int32)])\n    it_parses \"{a: 1, b: 2}\", NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"a\", 1.int32), NamedTupleLiteral::Entry.new(\"b\", 2.int32)])\n    it_parses \"{A: 1, B: 2}\", NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"A\", 1.int32), NamedTupleLiteral::Entry.new(\"B\", 2.int32)])\n\n    it_parses %({\"foo\": 1}), NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"foo\", 1.int32)])\n    it_parses %({\"foo\": 1, \"bar\": 2}), NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"foo\", 1.int32), NamedTupleLiteral::Entry.new(\"bar\", 2.int32)])\n\n    assert_syntax_error \"{\\\"\\\": 1}\", \"named tuple name cannot be empty\"\n    assert_syntax_error \"{a: 1, \\\"\\\": 2}\", \"named tuple name cannot be empty\"\n    assert_syntax_error \"{a: 1, a: 2}\", \"duplicated key: a\"\n    assert_syntax_error \"{a[0]: 1}\", \"expecting token '=>', not ':'\"\n    assert_syntax_error \"{a[]: 1}\", \"expecting token '=>', not ':'\"\n\n    it_parses \"{} of Int => Double\", HashLiteral.new([] of HashLiteral::Entry, of: HashLiteral::Entry.new(\"Int\".path, \"Double\".path))\n    it_parses \"{} of Int32 -> Int32 => Int32\", HashLiteral.new([] of HashLiteral::Entry, of: HashLiteral::Entry.new(ProcNotation.new([\"Int32\".path] of ASTNode, \"Int32\".path), \"Int32\".path))\n\n    it_parses \"require \\\"foo\\\"\", Require.new(\"foo\")\n    it_parses \"require \\\"foo\\\"; [1]\", [Require.new(\"foo\"), ([1.int32] of ASTNode).array]\n\n    it_parses \"case 1; when 1; 2; else; 3; end\", Case.new(1.int32, [When.new([1.int32] of ASTNode, 2.int32)], 3.int32, exhaustive: false)\n    it_parses \"case 1; when 0, 1; 2; else; 3; end\", Case.new(1.int32, [When.new([0.int32, 1.int32] of ASTNode, 2.int32)], 3.int32, exhaustive: false)\n    it_parses \"case 1\\nwhen 1\\n2\\nelse\\n3\\nend\", Case.new(1.int32, [When.new([1.int32] of ASTNode, 2.int32)], 3.int32, exhaustive: false)\n    it_parses \"case 1\\nwhen 1\\n2\\nend\", Case.new(1.int32, [When.new([1.int32] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case / /; when / /; / /; else; / /; end\", Case.new(regex(\" \"), [When.new([regex(\" \")] of ASTNode, regex(\" \"))], regex(\" \"), exhaustive: false)\n    it_parses \"case / /\\nwhen / /\\n/ /\\nelse\\n/ /\\nend\", Case.new(regex(\" \"), [When.new([regex(\" \")] of ASTNode, regex(\" \"))], regex(\" \"), exhaustive: false)\n\n    it_parses \"case 1; when 1 then 2; else; 3; end\", Case.new(1.int32, [When.new([1.int32] of ASTNode, 2.int32)], 3.int32, exhaustive: false)\n    it_parses \"case 1; when x then 2; else; 3; end\", Case.new(1.int32, [When.new([\"x\".call] of ASTNode, 2.int32)], 3.int32, exhaustive: false)\n    it_parses \"case 1\\nwhen 1\\n2\\nend\\nif a\\nend\", [Case.new(1.int32, [When.new([1.int32] of ASTNode, 2.int32)], else: nil, exhaustive: false), If.new(\"a\".call)]\n    it_parses \"case\\n1\\nwhen 1\\n2\\nend\\nif a\\nend\", [Case.new(1.int32, [When.new([1.int32] of ASTNode, 2.int32)], else: nil, exhaustive: false), If.new(\"a\".call)]\n\n    it_parses \"case 1\\nwhen .foo\\n2\\nend\", Case.new(1.int32, [When.new([Call.new(ImplicitObj.new, \"foo\")] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case 1\\nwhen .responds_to?(:foo)\\n2\\nend\", Case.new(1.int32, [When.new([RespondsTo.new(ImplicitObj.new, \"foo\")] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case 1\\nwhen .is_a?(T)\\n2\\nend\", Case.new(1.int32, [When.new([IsA.new(ImplicitObj.new, \"T\".path)] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case 1\\nwhen .as(T)\\n2\\nend\", Case.new(1.int32, [When.new([Cast.new(ImplicitObj.new, \"T\".path)] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case 1\\nwhen .as?(T)\\n2\\nend\", Case.new(1.int32, [When.new([NilableCast.new(ImplicitObj.new, \"T\".path)] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case 1\\nwhen .!()\\n2\\nend\", Case.new(1.int32, [When.new([Not.new(ImplicitObj.new)] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case when 1\\n2\\nend\", Case.new(nil, [When.new([1.int32] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case \\nwhen 1\\n2\\nend\", Case.new(nil, [When.new([1.int32] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n    it_parses \"case {1, 2}\\nwhen {3, 4}\\n5\\nend\", Case.new(TupleLiteral.new([1.int32, 2.int32] of ASTNode), [When.new([TupleLiteral.new([3.int32, 4.int32] of ASTNode)] of ASTNode, 5.int32)], else: nil, exhaustive: false)\n    it_parses \"case {1, 2}\\nwhen {3, 4}, {5, 6}\\n7\\nend\", Case.new(TupleLiteral.new([1.int32, 2.int32] of ASTNode), [When.new([TupleLiteral.new([3.int32, 4.int32] of ASTNode), TupleLiteral.new([5.int32, 6.int32] of ASTNode)] of ASTNode, 7.int32)], else: nil, exhaustive: false)\n    it_parses \"case {1, 2}\\nwhen {.foo, .bar}\\n5\\nend\", Case.new(TupleLiteral.new([1.int32, 2.int32] of ASTNode), [When.new([TupleLiteral.new([Call.new(ImplicitObj.new, \"foo\"), Call.new(ImplicitObj.new, \"bar\")] of ASTNode)] of ASTNode, 5.int32)], else: nil, exhaustive: false)\n    it_parses \"case {1, 2}\\nwhen foo\\n5\\nend\", Case.new(TupleLiteral.new([1.int32, 2.int32] of ASTNode), [When.new([\"foo\".call] of ASTNode, 5.int32)], else: nil, exhaustive: false)\n    it_parses \"case a\\nwhen b\\n1 / 2\\nelse\\n1 / 2\\nend\", Case.new(\"a\".call, [When.new([\"b\".call] of ASTNode, Call.new(1.int32, \"/\", 2.int32))], Call.new(1.int32, \"/\", 2.int32), exhaustive: false)\n    it_parses \"case a\\nwhen b\\n/ /\\n\\nelse\\n/ /\\nend\", Case.new(\"a\".call, [When.new([\"b\".call] of ASTNode, RegexLiteral.new(StringLiteral.new(\" \")))], RegexLiteral.new(StringLiteral.new(\" \")), exhaustive: false)\n    assert_syntax_error \"case {1, 2}; when {3}; 4; end\", \"wrong number of tuple elements (given 1, expected 2)\", 1, 19\n    it_parses \"case 1; end\", Case.new(1.int32, [] of When, else: nil, exhaustive: false)\n    it_parses \"case foo; end\", Case.new(\"foo\".call, [] of When, else: nil, exhaustive: false)\n    it_parses \"case\\nend\", Case.new(nil, [] of When, else: nil, exhaustive: false)\n    it_parses \"case;end\", Case.new(nil, [] of When, else: nil, exhaustive: false)\n    it_parses \"case 1\\nelse\\n2\\nend\", Case.new(1.int32, [] of When, 2.int32, exhaustive: false)\n    it_parses \"a = 1\\ncase 1\\nwhen a then 1\\nend\", [Assign.new(\"a\".var, 1.int32), Case.new(1.int32, [When.new([\"a\".var] of ASTNode, 1.int32)], else: nil, exhaustive: false)] of ASTNode\n    it_parses \"case\\nwhen true\\n1\\nend\", Case.new(nil, [When.new([true.bool] of ASTNode, 1.int32)] of When, else: nil, exhaustive: false)\n    it_parses \"case;when true;1;end\", Case.new(nil, [When.new([true.bool] of ASTNode, 1.int32)] of When, else: nil, exhaustive: false)\n\n    it_parses \"case 1\\nin Int32; 2; end\", Case.new(1.int32, [When.new([\"Int32\".path] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case 1\\nin Int32.class; 2; end\", Case.new(1.int32, [When.new([Call.new(\"Int32\".path, \"class\")] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case 1\\nin Foo(Int32); 2; end\", Case.new(1.int32, [When.new([Generic.new(\"Foo\".path, [\"Int32\".path] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case 1\\nin false; 2; end\", Case.new(1.int32, [When.new([false.bool] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case 1\\nin true; 2; end\", Case.new(1.int32, [When.new([true.bool] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case 1\\nin nil; 2; end\", Case.new(1.int32, [When.new([NilLiteral.new] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case 1\\nin .bar?; 2; end\", Case.new(1.int32, [When.new([Call.new(ImplicitObj.new, \"bar?\")] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n\n    it_parses \"case {1}\\nin {Int32}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([\"Int32\".path] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {Int32.class}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([Call.new(\"Int32\".path, \"class\")] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {Foo(Int32)}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([Generic.new(\"Foo\".path, [\"Int32\".path] of ASTNode)] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {false}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([false.bool] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {true}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([true.bool] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {nil}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([NilLiteral.new] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {.bar?}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([Call.new(ImplicitObj.new, \"bar?\")] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n    it_parses \"case {1}\\nin {_}; 2; end\", Case.new(TupleLiteral.new([1.int32] of ASTNode), [When.new([TupleLiteral.new([Underscore.new] of ASTNode)] of ASTNode, 2.int32)], else: nil, exhaustive: true)\n\n    assert_syntax_error \"case 1\\nin Int32; 2; when 2\", \"expected 'in', not 'when'\"\n    assert_syntax_error \"case 1\\nwhen Int32; 2; in 2\", \"expected 'when', not 'in'\"\n    assert_syntax_error \"case 1\\nin Int32; 2; else\", \"exhaustive case (case ... in) doesn't allow an 'else'\"\n    assert_syntax_error \"case 1\\nin 1; 2\", \"expression of exhaustive case (case ... in) must be a constant (like `IO::Memory`), a generic (like `Array(Int32)`), a bool literal (true or false), a nil literal (nil) or a question method (like `.red?`)\"\n    assert_syntax_error \"case 1\\nin .nil?; 2\", \"expression of exhaustive case (case ... in) must be a constant (like `IO::Memory`), a generic (like `Array(Int32)`), a bool literal (true or false), a nil literal (nil) or a question method (like `.red?`)\"\n    assert_syntax_error \"case 1\\nin _;\", \"'when _' is not supported\"\n\n    atomic_methods = Crystal::Parser::AtomicWithMethodCheck.join(\", \")\n    assert_syntax_error \"case 1\\nwhen .=(2)\", \"expecting any of these tokens: #{atomic_methods} (not '=')\"\n    assert_syntax_error \"case 1\\nwhen .+=(2)\", \"expecting any of these tokens: #{atomic_methods} (not '+=')\"\n    assert_syntax_error \"case 1\\nwhen .&&(2)\", \"expecting any of these tokens: #{atomic_methods} (not '&&')\"\n\n    it_parses \"case 1; when 2 then /foo/; end\", Case.new(1.int32, [When.new([2.int32] of ASTNode, RegexLiteral.new(\"foo\".string))], else: nil, exhaustive: false)\n\n    it_parses \"select\\nwhen foo\\n2\\nend\", Select.new([When.new(\"foo\".call, 2.int32)])\n    it_parses \"select\\nwhen foo\\n2\\nwhen bar\\n4\\nend\", Select.new([When.new(\"foo\".call, 2.int32), When.new(\"bar\".call, 4.int32)])\n    it_parses \"select\\nwhen foo\\n2\\nelse\\n3\\nend\", Select.new([When.new(\"foo\".call, 2.int32)], 3.int32)\n\n    assert_syntax_error \"select\\nwhen 1\\n2\\nend\", \"invalid select when expression: must be an assignment or call\"\n\n    it_parses \"def foo(x); end; x\", [Def.new(\"foo\", [\"x\".arg]), \"x\".call]\n    it_parses \"def foo; / /; end\", Def.new(\"foo\", body: regex(\" \"))\n\n    it_parses \"\\\"foo\\#{bar}baz\\\"\", StringInterpolation.new([\"foo\".string, \"bar\".call, \"baz\".string] of ASTNode)\n    it_parses \"qux \\\"foo\\#{bar do end}baz\\\"\", Call.new(\"qux\", StringInterpolation.new([\"foo\".string, Call.new(\"bar\", block: Block.new), \"baz\".string] of ASTNode))\n    it_parses \"\\\"\\#{1\\n}\\\"\", StringInterpolation.new([1.int32] of ASTNode)\n\n    # When interpolating a string we don't necessarily need interpolation.\n    # This is useful for example when interpolating __FILE__ and __DIR__\n    it_parses \"\\\"foo\\#{\\\"bar\\\"}baz\\\"\", \"foobarbaz\".string\n\n    it_parses \"lib LibFoo\\nend\\nif true\\nend\", [LibDef.new(\"LibFoo\".path), If.new(true.bool)]\n\n    it_parses \"foo(\\n1\\n)\", Call.new(\"foo\", 1.int32)\n\n    it_parses \"a = 1\\nfoo - a\", [Assign.new(\"a\".var, 1.int32), Call.new(\"foo\".call, \"-\", \"a\".var)]\n    it_parses \"a = 1\\nfoo -a\", [Assign.new(\"a\".var, 1.int32), Call.new(\"foo\", Call.new(\"a\".var, \"-\"))]\n\n    it_parses \"a : Foo\", TypeDeclaration.new(\"a\".var, \"Foo\".path)\n    it_parses \"a : Foo | Int32\", TypeDeclaration.new(\"a\".var, Crystal::Union.new([\"Foo\".path, \"Int32\".path] of ASTNode))\n    it_parses \"@a : Foo\", TypeDeclaration.new(\"@a\".instance_var, \"Foo\".path)\n    it_parses \"@a : Foo | Int32\", TypeDeclaration.new(\"@a\".instance_var, Crystal::Union.new([\"Foo\".path, \"Int32\".path] of ASTNode))\n    it_parses \"@@a : Foo\", TypeDeclaration.new(\"@@a\".class_var, \"Foo\".path)\n\n    it_parses \"a : Foo = 1\", TypeDeclaration.new(\"a\".var, \"Foo\".path, 1.int32)\n    it_parses \"@a : Foo = 1\", TypeDeclaration.new(\"@a\".instance_var, \"Foo\".path, 1.int32)\n    it_parses \"@@a : Foo = 1\", TypeDeclaration.new(\"@@a\".class_var, \"Foo\".path, 1.int32)\n\n    it_parses \"Foo?\", Generic.new(\"Union\".path(global: true), [\"Foo\".path, \"Nil\".path(global: true)] of ASTNode)\n    it_parses \"a : Foo*\", TypeDeclaration.new(\"a\".var, Generic.new(\"Pointer\".path(global: true), [\"Foo\".path] of ASTNode, suffix: Generic::Suffix::Asterisk))\n    it_parses \"a : Foo[12]\", TypeDeclaration.new(\"a\".var, Generic.new(\"StaticArray\".path(global: true), [\"Foo\".path, 12.int32] of ASTNode, suffix: Generic::Suffix::Bracket))\n\n    it_parses \"Foo()?\", Generic.new(\"Union\".path(global: true), [Generic.new(\"Foo\".path, [] of ASTNode), \"Nil\".path(global: true)] of ASTNode)\n    it_parses \"a : Foo()*\", TypeDeclaration.new(\"a\".var, Generic.new(\"Pointer\".path(global: true), [Generic.new(\"Foo\".path, [] of ASTNode)] of ASTNode, suffix: Generic::Suffix::Asterisk))\n    it_parses \"a : Foo()[12]\", TypeDeclaration.new(\"a\".var, Generic.new(\"StaticArray\".path(global: true), [Generic.new(\"Foo\".path, [] of ASTNode), 12.int32] of ASTNode, suffix: Generic::Suffix::Bracket))\n\n    it_parses \"a = uninitialized Foo; a\", [UninitializedVar.new(\"a\".var, \"Foo\".path), \"a\".var]\n    it_parses \"@a = uninitialized Foo\", UninitializedVar.new(\"@a\".instance_var, \"Foo\".path)\n    it_parses \"@@a = uninitialized Foo\", UninitializedVar.new(\"@@a\".class_var, \"Foo\".path)\n\n    it_parses \"()\", Expressions.new([Nop.new] of ASTNode)\n    it_parses \"(1; 2; 3)\", [1.int32, 2.int32, 3.int32] of ASTNode\n\n    it_parses \"begin; rescue; end\", ExceptionHandler.new(Nop.new, [Rescue.new])\n    it_parses \"begin; 1; rescue; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32)])\n    it_parses \"begin; 1; ensure; 2; end\", ExceptionHandler.new(1.int32, ensure: 2.int32)\n    it_parses \"begin\\n1\\nensure\\n2\\nend\", ExceptionHandler.new(1.int32, ensure: 2.int32)\n    it_parses \"begin; 1; rescue Foo; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [\"Foo\".path] of ASTNode)])\n    it_parses \"begin; 1; rescue ::Foo; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [Path.global(\"Foo\")] of ASTNode)])\n    it_parses \"begin; 1; rescue Foo | Bar; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [\"Foo\".path, \"Bar\".path] of ASTNode)])\n    it_parses \"begin; 1; rescue ::Foo | ::Bar; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [Path.global(\"Foo\"), Path.global(\"Bar\")] of ASTNode)])\n    it_parses \"begin; 1; rescue ex : Foo | Bar; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [\"Foo\".path, \"Bar\".path] of ASTNode, \"ex\")])\n    it_parses \"begin; 1; rescue ex : ::Foo | ::Bar; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, [Path.global(\"Foo\"), Path.global(\"Bar\")] of ASTNode, \"ex\")])\n    it_parses \"begin; 1; rescue ex; 2; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32, nil, \"ex\")])\n    it_parses \"begin; 1; rescue; 2; else; 3; end\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32)], 3.int32)\n    it_parses \"begin; 1; rescue ex; 2; end; ex\", [ExceptionHandler.new(1.int32, [Rescue.new(2.int32, nil, \"ex\")]), \"ex\".var]\n\n    it_parses \"def foo(); 1; rescue; 2; end\", Def.new(\"foo\", body: ExceptionHandler.new(1.int32, [Rescue.new(2.int32)]))\n\n    it_parses \"1.tap do; 1; rescue; 2; end\", Call.new(1.int32, \"tap\", block: Block.new(body: ExceptionHandler.new(1.int32, [Rescue.new(2.int32)])))\n    it_parses \"-> do; 1; rescue; 2; end\", ProcLiteral.new(Def.new(\"->\", body: ExceptionHandler.new(1.int32, [Rescue.new(2.int32)])))\n    it_parses \"1.tap do |x|; 1; rescue; x; end\", Call.new(1.int32, \"tap\", block: Block.new([\"x\".var], body: ExceptionHandler.new(1.int32, [Rescue.new(\"x\".var)])))\n\n    it_parses \"1 rescue 2\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32)])\n    it_parses \"x = 1 rescue 2\", Assign.new(\"x\".var, ExceptionHandler.new(1.int32, [Rescue.new(2.int32)]))\n    it_parses \"x = 1 ensure 2\", Assign.new(\"x\".var, ExceptionHandler.new(1.int32, ensure: 2.int32))\n    it_parses \"a = 1; a rescue a\", [Assign.new(\"a\".var, 1.int32), ExceptionHandler.new(\"a\".var, [Rescue.new(\"a\".var)])]\n    it_parses \"a = 1; yield a rescue a\", [Assign.new(\"a\".var, 1.int32), ExceptionHandler.new(Yield.new([\"a\".var] of ASTNode), [Rescue.new(\"a\".var)])]\n\n    it_parses \"1 ensure 2\", ExceptionHandler.new(1.int32, ensure: 2.int32)\n    it_parses \"1 rescue 2\", ExceptionHandler.new(1.int32, [Rescue.new(2.int32)])\n\n    it_parses \"foo ensure 2\", ExceptionHandler.new(\"foo\".call, ensure: 2.int32)\n    it_parses \"foo rescue 2\", ExceptionHandler.new(\"foo\".call, [Rescue.new(2.int32)])\n\n    it_parses \"a = 1; a ensure a\", [Assign.new(\"a\".var, 1.int32), ExceptionHandler.new(\"a\".var, ensure: \"a\".var)]\n    it_parses \"a = 1; yield a ensure a\", [Assign.new(\"a\".var, 1.int32), ExceptionHandler.new(Yield.new([\"a\".var] of ASTNode), ensure: \"a\".var)]\n\n    it_parses \"1 <= 2 <= 3\", Call.new(Call.new(1.int32, \"<=\", 2.int32), \"<=\", 3.int32)\n    it_parses \"1 == 2 == 3 == 4\", Call.new(Call.new(Call.new(1.int32, \"==\", 2.int32), \"==\", 3.int32), \"==\", 4.int32)\n\n    it_parses \"-> do end\", ProcLiteral.new\n    it_parses \"-> { }\", ProcLiteral.new\n    it_parses \"->() { }\", ProcLiteral.new\n    it_parses \"->(x : Int32) { }\", ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\", restriction: \"Int32\".path)]))\n    it_parses \"->(x : Int32) { x }\", ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\", restriction: \"Int32\".path)], \"x\".var))\n    it_parses \"->(x) { x }\", ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\")], \"x\".var))\n    it_parses \"x = 1; ->{ x }\", [Assign.new(\"x\".var, 1.int32), ProcLiteral.new(Def.new(\"->\", body: \"x\".var))]\n    it_parses \"f ->{ a do\\n end\\n }\", Call.new(\"f\", ProcLiteral.new(Def.new(\"->\", body: Call.new(\"a\", block: Block.new))))\n\n    it_parses \"-> : Int32 { }\", ProcLiteral.new(Def.new(\"->\", return_type: \"Int32\".path))\n    it_parses \"->\\n:\\nInt32\\n{\\n}\", ProcLiteral.new(Def.new(\"->\", return_type: \"Int32\".path))\n    it_parses \"->() : Int32 { }\", ProcLiteral.new(Def.new(\"->\", return_type: \"Int32\".path))\n    it_parses \"->() : Int32 do end\", ProcLiteral.new(Def.new(\"->\", return_type: \"Int32\".path))\n    it_parses \"->(x : Int32) : Int32 { }\", ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\", restriction: \"Int32\".path)], return_type: \"Int32\".path))\n\n    assert_syntax_error \"-> :Int32 { }\", \"a space is mandatory between ':' and return type\"\n    assert_syntax_error \"->() :Int32 { }\", \"a space is mandatory between ':' and return type\"\n\n    %w(foo foo= foo? foo!).each do |method|\n      it_parses \"->#{method}\", ProcPointer.new(nil, method)\n      it_parses \"foo = 1; ->foo.#{method}\", [Assign.new(\"foo\".var, 1.int32), ProcPointer.new(\"foo\".var, method)]\n      it_parses \"->Foo.#{method}\", ProcPointer.new(\"Foo\".path, method)\n      it_parses \"->@foo.#{method}\", ProcPointer.new(\"@foo\".instance_var, method)\n      it_parses \"->@@foo.#{method}\", ProcPointer.new(\"@@foo\".class_var, method)\n      it_parses \"->::#{method}\", ProcPointer.new(nil, method, global: true)\n      it_parses \"->::Foo.#{method}\", ProcPointer.new(Path.global(\"Foo\"), method)\n    end\n\n    assert_syntax_error \"->::foo.foo\", \"ProcPointer of local variable cannot be global\"\n    assert_syntax_error \"->::@foo.foo\", \"ProcPointer of instance variable cannot be global\"\n    assert_syntax_error \"->::@@foo.foo\", \"ProcPointer of class variable cannot be global\"\n\n    it_parses \"->Foo::Bar::Baz.foo\", ProcPointer.new([\"Foo\", \"Bar\", \"Baz\"].path, \"foo\")\n    it_parses \"->foo(Int32, Float64)\", ProcPointer.new(nil, \"foo\", [\"Int32\".path, \"Float64\".path] of ASTNode)\n    it_parses \"foo = 1; ->foo.bar(Int32)\", [Assign.new(\"foo\".var, 1.int32), ProcPointer.new(\"foo\".var, \"bar\", [\"Int32\".path] of ASTNode)]\n    it_parses \"->foo(Void*)\", ProcPointer.new(nil, \"foo\", [\"Void\".path.pointer_of] of ASTNode)\n    it_parses \"call ->foo\", Call.new(\"call\", ProcPointer.new(nil, \"foo\"))\n    it_parses \"[] of ->\\n\", ArrayLiteral.new(of: ProcNotation.new)\n\n    it_parses \"foo &->bar\", Call.new(\"foo\", block_arg: ProcPointer.new(nil, \"bar\"))\n\n    it_parses \"foo.bar = {} of Int32 => Int32\", Call.new(\"foo\".call, \"bar=\", HashLiteral.new(of: HashLiteral::Entry.new(\"Int32\".path, \"Int32\".path)))\n\n    it_parses \"alias Foo = Bar\", Alias.new(\"Foo\".path, \"Bar\".path)\n    it_parses \"alias Foo::Bar = Baz\", Alias.new(Path.new(\"Foo\", \"Bar\"), \"Baz\".path)\n    assert_syntax_error \"alias Foo?\"\n\n    it_parses \"def foo\\n1\\nend\\nif 1\\nend\", [Def.new(\"foo\", body: 1.int32), If.new(1.int32)] of ASTNode\n\n    assert_syntax_error \"1 as Bar\"\n    assert_syntax_error \"1 as? Bar\"\n\n    it_parses \"1.as Bar\", Cast.new(1.int32, \"Bar\".path)\n    it_parses \"1.as(Bar)\", Cast.new(1.int32, \"Bar\".path)\n    it_parses \"foo.as(Bar)\", Cast.new(\"foo\".call, \"Bar\".path)\n    it_parses \"foo.bar.as(Bar)\", Cast.new(Call.new(\"foo\".call, \"bar\"), \"Bar\".path)\n    it_parses \"call(foo.as Bar, Baz)\", Call.new(\"call\", args: [Cast.new(\"foo\".call, \"Bar\".path), \"Baz\".path] of ASTNode)\n\n    it_parses \"as(Bar)\", Cast.new(Var.new(\"self\"), \"Bar\".path)\n\n    it_parses \"1.as? Bar\", NilableCast.new(1.int32, \"Bar\".path)\n    it_parses \"1.as?(Bar)\", NilableCast.new(1.int32, \"Bar\".path)\n    it_parses \"as?(Bar)\", NilableCast.new(Var.new(\"self\"), \"Bar\".path)\n\n    it_parses \"typeof(1)\", TypeOf.new([1.int32] of ASTNode)\n\n    # #10521\n    it_parses \"typeof(a = 1); a\", [TypeOf.new([Assign.new(\"a\".var, 1.int32)] of ASTNode), \"a\".call]\n\n    it_parses \"puts ~1\", Call.new(\"puts\", Call.new(1.int32, \"~\"))\n\n    it_parses \"foo\\n.bar\", Call.new(\"foo\".call, \"bar\")\n    it_parses \"foo\\n   .bar\", Call.new(\"foo\".call, \"bar\")\n    it_parses \"foo\\n\\n  .bar\", Call.new(\"foo\".call, \"bar\")\n    it_parses \"foo\\n  #comment\\n  .bar\", Call.new(\"foo\".call, \"bar\")\n\n    it_parses \"{1}\", TupleLiteral.new([1.int32] of ASTNode)\n    it_parses \"{1, 2, 3}\", TupleLiteral.new([1.int32, 2.int32, 3.int32] of ASTNode)\n    it_parses \"{A::B}\", TupleLiteral.new([Path.new(\"A\", \"B\")] of ASTNode)\n    it_parses \"{\\n1,\\n2\\n}\", TupleLiteral.new([1.int32, 2.int32] of ASTNode)\n    it_parses \"{\\n1\\n}\", TupleLiteral.new([1.int32] of ASTNode)\n    it_parses \"{\\n{1}\\n}\", TupleLiteral.new([TupleLiteral.new([1.int32] of ASTNode)] of ASTNode)\n    it_parses %({\"\".id}), TupleLiteral.new([Call.new(\"\".string, \"id\")] of ASTNode)\n\n    it_parses \"foo { a = 1 }; a\", [Call.new(\"foo\", block: Block.new(body: Assign.new(\"a\".var, 1.int32))), \"a\".call] of ASTNode\n\n    it_parses \"foo.bar(1).baz\", Call.new(Call.new(\"foo\".call, \"bar\", 1.int32), \"baz\")\n\n    it_parses \"b.c ||= 1\", OpAssign.new(Call.new(\"b\".call, \"c\"), \"||\", 1.int32)\n    it_parses \"b.c &&= 1\", OpAssign.new(Call.new(\"b\".call, \"c\"), \"&&\", 1.int32)\n\n    it_parses \"a = 1; class Foo; @x = a; end\", [Assign.new(\"a\".var, 1.int32), ClassDef.new(\"Foo\".path, Assign.new(\"@x\".instance_var, \"a\".call))]\n\n    it_parses \"@[Foo]\", Annotation.new(\"Foo\".path)\n    it_parses \"@[Foo()]\", Annotation.new(\"Foo\".path)\n    it_parses \"@[Foo(1)]\", Annotation.new(\"Foo\".path, [1.int32] of ASTNode)\n    it_parses \"@[Foo(\\\"hello\\\")]\", Annotation.new(\"Foo\".path, [\"hello\".string] of ASTNode)\n    it_parses \"@[Foo(1, foo: 2)]\", Annotation.new(\"Foo\".path, [1.int32] of ASTNode, [NamedArgument.new(\"foo\", 2.int32)])\n    it_parses \"@[Foo(1, foo: 2\\n)]\", Annotation.new(\"Foo\".path, [1.int32] of ASTNode, [NamedArgument.new(\"foo\", 2.int32)])\n    it_parses \"@[Foo(\\n1, foo: 2\\n)]\", Annotation.new(\"Foo\".path, [1.int32] of ASTNode, [NamedArgument.new(\"foo\", 2.int32)])\n    it_parses \"@[Foo::Bar]\", Annotation.new(Path.new(\"Foo\", \"Bar\"))\n\n    assert_syntax_error \"@[Foo(\\\"\\\": 1)]\"\n\n    it_parses \"lib LibC\\n@[Bar]; end\", LibDef.new(\"LibC\".path, Annotation.new(\"Bar\".path))\n\n    it_parses \"Foo(_)\", Generic.new(\"Foo\".path, [Underscore.new] of ASTNode)\n\n    it_parses \"{% if true %}\\n{% end %}\\n{% if true %}\\n{% end %}\", [MacroIf.new(true.bool, MacroLiteral.new(\"\\n\")), MacroIf.new(true.bool, MacroLiteral.new(\"\\n\"))] of ASTNode\n    it_parses \"fun foo : Int32; 1; end; 2\", [FunDef.new(\"foo\", return_type: \"Int32\".path, body: 1.int32), 2.int32]\n\n    it_parses \"[] of ->;\", ArrayLiteral.new([] of ASTNode, ProcNotation.new)\n    it_parses \"[] of ->\\n1\", [ArrayLiteral.new([] of ASTNode, ProcNotation.new), 1.int32]\n\n    it_parses \"def foo(x, *y); 1; end\", Def.new(\"foo\", [Arg.new(\"x\"), Arg.new(\"y\")], 1.int32, splat_index: 1)\n    it_parses \"macro foo(x, *y);end\", Macro.new(\"foo\", [Arg.new(\"x\"), Arg.new(\"y\")], body: Expressions.new, splat_index: 1)\n\n    it_parses \"def foo(x = 1, *y); 1; end\", Def.new(\"foo\", [Arg.new(\"x\", 1.int32), Arg.new(\"y\")], 1.int32, splat_index: 1)\n    it_parses \"def foo(x, *y : Int32); 1; end\", Def.new(\"foo\", [Arg.new(\"x\"), Arg.new(\"y\", restriction: \"Int32\".path)], 1.int32, splat_index: 1)\n    it_parses \"def foo(*y : *T); 1; end\", Def.new(\"foo\", [Arg.new(\"y\", restriction: \"T\".path.splat)], 1.int32, splat_index: 0)\n\n    it_parses \"foo *bar\", Call.new(\"foo\", \"bar\".call.splat)\n    it_parses \"foo(*bar)\", Call.new(\"foo\", \"bar\".call.splat)\n    it_parses \"foo x, *bar\", Call.new(\"foo\", \"x\".call, \"bar\".call.splat)\n    it_parses \"foo(x, *bar, *baz, y)\", Call.new(\"foo\", [\"x\".call, \"bar\".call.splat, \"baz\".call.splat, \"y\".call] of ASTNode)\n    it_parses \"foo.bar=(*baz)\", Call.new(\"foo\".call, \"bar=\", \"baz\".call.splat)\n    it_parses \"foo.bar= *baz\", Call.new(\"foo\".call, \"bar=\", \"baz\".call.splat)\n    it_parses \"foo.bar = (1).abs\", Call.new(\"foo\".call, \"bar=\", Call.new(Expressions.new([1.int32] of ASTNode), \"abs\"))\n    it_parses \"foo[*baz]\", Call.new(\"foo\".call, \"[]\", \"baz\".call.splat)\n    it_parses \"foo[*baz] = 1\", Call.new(\"foo\".call, \"[]=\", [\"baz\".call.splat, 1.int32] of ASTNode)\n\n    it_parses \"foo **bar\", Call.new(\"foo\", DoubleSplat.new(\"bar\".call))\n    it_parses \"foo(**bar)\", Call.new(\"foo\", DoubleSplat.new(\"bar\".call))\n\n    it_parses \"foo 1, **bar\", Call.new(\"foo\", [1.int32, DoubleSplat.new(\"bar\".call)] of ASTNode)\n    it_parses \"foo(1, **bar)\", Call.new(\"foo\", [1.int32, DoubleSplat.new(\"bar\".call)] of ASTNode)\n\n    it_parses \"foo 1, **bar, &block\", Call.new(\"foo\", args: [1.int32, DoubleSplat.new(\"bar\".call)] of ASTNode, block_arg: \"block\".call)\n    it_parses \"foo(1, **bar, &block)\", Call.new(\"foo\", args: [1.int32, DoubleSplat.new(\"bar\".call)] of ASTNode, block_arg: \"block\".call)\n\n    assert_syntax_error \"foo **bar, 1\", \"argument not allowed after double splat\"\n    assert_syntax_error \"foo(**bar, 1)\", \"argument not allowed after double splat\"\n\n    assert_syntax_error \"foo **bar, *x\", \"splat not allowed after double splat\"\n    assert_syntax_error \"foo(**bar, *x)\", \"splat not allowed after double splat\"\n\n    assert_syntax_error \"foo **bar, out x\", \"out argument not allowed after double splat\"\n    assert_syntax_error \"foo(**bar, out x)\", \"out argument not allowed after double splat\"\n\n    it_parses \"private def foo; end\", VisibilityModifier.new(Visibility::Private, Def.new(\"foo\"))\n    it_parses \"protected def foo; end\", VisibilityModifier.new(Visibility::Protected, Def.new(\"foo\"))\n\n    it_parses \"`foo`\", Call.new(\"`\", \"foo\".string)\n    it_parses \"`foo\\#{1}bar`\", Call.new(\"`\", StringInterpolation.new([\"foo\".string, 1.int32, \"bar\".string] of ASTNode))\n    it_parses \"`foo\\\\``\", Call.new(\"`\", \"foo`\".string)\n    it_parses \"%x(`which(foo)`)\", Call.new(\"`\", \"`which(foo)`\".string)\n\n    it_parses \"def `(cmd); 1; end\", Def.new(\"`\", [\"cmd\".arg], 1.int32)\n\n    it_parses \"def foo(bar = 1\\n); 2; end\", Def.new(\"foo\", [Arg.new(\"bar\", default_value: 1.int32)], 2.int32)\n\n    it_parses \"Set {1, 2, 3}\", ArrayLiteral.new([1.int32, 2.int32, 3.int32] of ASTNode, name: \"Set\".path)\n    it_parses \"Set() {1, 2, 3}\", ArrayLiteral.new([1.int32, 2.int32, 3.int32] of ASTNode, name: Generic.new(\"Set\".path, [] of ASTNode))\n    it_parses \"Set(Int32) {1, 2, 3}\", ArrayLiteral.new([1.int32, 2.int32, 3.int32] of ASTNode, name: Generic.new(\"Set\".path, [\"Int32\".path] of ASTNode))\n\n    describe \"single splats inside container literals\" do\n      it_parses \"{*1}\", TupleLiteral.new([1.int32.splat] of ASTNode)\n      it_parses \"{*1, 2}\", TupleLiteral.new([1.int32.splat, 2.int32] of ASTNode)\n      it_parses \"{1, *2}\", TupleLiteral.new([1.int32, 2.int32.splat] of ASTNode)\n      it_parses \"{*1, *2}\", TupleLiteral.new([1.int32.splat, 2.int32.splat] of ASTNode)\n      it_parses \"{1, *2, 3, *4, 5}\", TupleLiteral.new([1.int32, 2.int32.splat, 3.int32, 4.int32.splat, 5.int32] of ASTNode)\n\n      it_parses \"[*1]\", ArrayLiteral.new([1.int32.splat] of ASTNode)\n      it_parses \"[*1, 2]\", ArrayLiteral.new([1.int32.splat, 2.int32] of ASTNode)\n      it_parses \"[1, *2]\", ArrayLiteral.new([1.int32, 2.int32.splat] of ASTNode)\n      it_parses \"[*1, *2]\", ArrayLiteral.new([1.int32.splat, 2.int32.splat] of ASTNode)\n      it_parses \"[1, *2, 3, *4, 5]\", ArrayLiteral.new([1.int32, 2.int32.splat, 3.int32, 4.int32.splat, 5.int32] of ASTNode)\n\n      it_parses \"Set {*1, 2, *3}\", ArrayLiteral.new([1.int32.splat, 2.int32, 3.int32.splat] of ASTNode, name: \"Set\".path)\n\n      it_parses \"[*[*[1]], *[2]]\", ArrayLiteral.new([ArrayLiteral.new([ArrayLiteral.new([1.int32] of ASTNode).splat] of ASTNode).splat, ArrayLiteral.new([2.int32] of ASTNode).splat] of ASTNode)\n\n      assert_syntax_error \"{*1 => 2}\"\n      assert_syntax_error \"{*a: 1}\"\n      assert_syntax_error \"{1 => 2, *3}\"\n      assert_syntax_error \"{a: 1, *2}\"\n\n      assert_syntax_error \"case {*1}\\nwhen {2}; 3; end\", \"splat is not allowed inside case expression\"\n      assert_syntax_error \"case {1}\\nwhen {*2}; 3; end\"\n      it_parses \"case 1\\nwhen {*2}; 3; end\", Case.new(1.int32, [When.new([TupleLiteral.new([2.int32.splat] of ASTNode)] of ASTNode, 3.int32)], else: nil, exhaustive: false)\n\n      it_parses \"x = {*1}\", Assign.new(\"x\".var, TupleLiteral.new([1.int32.splat] of ASTNode))\n\n      it_parses \"{*1 * 2}\", TupleLiteral.new([Call.new(1.int32, \"*\", 2.int32).splat] of ASTNode)\n      it_parses \"[*1 ** 2]\", ArrayLiteral.new([Call.new(1.int32, \"**\", 2.int32).splat] of ASTNode)\n      it_parses \"Set {*{1} * 2}\", ArrayLiteral.new([Call.new(TupleLiteral.new([1.int32] of ASTNode), \"*\", 2.int32).splat] of ASTNode, name: \"Set\".path)\n    end\n\n    it_parses \"foo(Bar) { 1 }\", Call.new(\"foo\", args: [\"Bar\".path] of ASTNode, block: Block.new(body: 1.int32))\n    it_parses \"foo Bar { 1 }\", Call.new(\"foo\", args: [ArrayLiteral.new([1.int32] of ASTNode, name: \"Bar\".path)] of ASTNode)\n    it_parses \"foo(Bar { 1 })\", Call.new(\"foo\", args: [ArrayLiteral.new([1.int32] of ASTNode, name: \"Bar\".path)] of ASTNode)\n\n    it_parses \"\\n\\n__LINE__\", 3.int32\n    it_parses \"__FILE__\", \"/foo/bar/baz.cr\".string\n    it_parses \"__DIR__\", \"/foo/bar\".string\n\n    it_parses \"def foo(x = __LINE__); end\", Def.new(\"foo\", args: [Arg.new(\"x\", default_value: MagicConstant.new(:MAGIC_LINE))])\n    it_parses \"def foo(x = __FILE__); end\", Def.new(\"foo\", args: [Arg.new(\"x\", default_value: MagicConstant.new(:MAGIC_FILE))])\n    it_parses \"def foo(x = __DIR__); end\", Def.new(\"foo\", args: [Arg.new(\"x\", default_value: MagicConstant.new(:MAGIC_DIR))])\n\n    it_parses \"macro foo(x = __LINE__);end\", Macro.new(\"foo\", body: Expressions.new, args: [Arg.new(\"x\", default_value: MagicConstant.new(:MAGIC_LINE))])\n\n    it_parses \"1 \\\\\\n + 2\", Call.new(1.int32, \"+\", 2.int32)\n    it_parses \"1\\\\\\n + 2\", Call.new(1.int32, \"+\", 2.int32)\n    it_parses \"1 \\\\\\r\\n + 2\", Call.new(1.int32, \"+\", 2.int32)\n    it_parses \"1\\\\\\r\\n + 2\", Call.new(1.int32, \"+\", 2.int32)\n\n    it_parses %(\"hello \" \\\\\\n \"world\"), StringLiteral.new(\"hello world\")\n    it_parses %(\"hello \"\\\\\\n\"world\"), StringLiteral.new(\"hello world\")\n    it_parses %(\"hello \" \\\\\\r\\n \"world\"), StringLiteral.new(\"hello world\")\n    it_parses %(\"hello \"\\\\\\r\\n\"world\"), StringLiteral.new(\"hello world\")\n    it_parses %(\"hello \\#{1}\" \\\\\\n \"\\#{2} world\"), StringInterpolation.new([\"hello \".string, 1.int32, 2.int32, \" world\".string] of ASTNode)\n    it_parses %(\"hello \\#{1}\" \\\\\\r\\n \"\\#{2} world\"), StringInterpolation.new([\"hello \".string, 1.int32, 2.int32, \" world\".string] of ASTNode)\n    it_parses \"<<-HERE\\nHello, mom! I am HERE.\\nHER dress is beautiful.\\nHE is OK.\\n  HERESY\\nHERE\",\n      \"Hello, mom! I am HERE.\\nHER dress is beautiful.\\nHE is OK.\\n  HERESY\".string_interpolation\n    it_parses \"<<-HERE\\n   One\\n  Zero\\n  HERE\", \" One\\nZero\".string_interpolation\n    it_parses \"<<-HERE\\n   One \\\\n Two\\n  Zero\\n  HERE\", \" One \\n Two\\nZero\".string_interpolation\n    it_parses \"<<-HERE\\n   One\\n\\n  Zero\\n  HERE\", \" One\\n\\nZero\".string_interpolation\n    it_parses \"<<-HERE\\n   One\\n \\n  Zero\\n  HERE\", \" One\\n\\nZero\".string_interpolation\n    it_parses \"<<-HERE\\n   \\#{1}One\\n  \\#{2}Zero\\n  HERE\", StringInterpolation.new([\" \".string, 1.int32, \"One\\n\".string, 2.int32, \"Zero\".string] of ASTNode)\n    it_parses \"<<-HERE\\n  foo\\#{1}bar\\n   baz\\n  HERE\", StringInterpolation.new([\"foo\".string, 1.int32, \"bar\\n baz\".string] of ASTNode)\n    it_parses \"<<-HERE\\r\\n   One\\r\\n  Zero\\r\\n  HERE\", \" One\\r\\nZero\".string_interpolation\n    it_parses \"<<-HERE\\r\\n   One\\r\\n  Zero\\r\\n  HERE\\r\\n\", \" One\\r\\nZero\".string_interpolation\n    it_parses \"<<-SOME\\n  Sa\\n  Se\\n  SOME\", \"Sa\\nSe\".string_interpolation\n    it_parses \"<<-HERE\\n  \\#{1} \\#{2}\\n  HERE\", StringInterpolation.new([1.int32, \" \".string, 2.int32] of ASTNode)\n    it_parses \"<<-HERE\\n  \\#{1} \\\\n \\#{2}\\n  HERE\", StringInterpolation.new([1.int32, \" \\n \".string, 2.int32] of ASTNode)\n    it_parses \"<<-HERE\\nHERE\", \"\".string_interpolation\n    it_parses \"<<-HERE1; <<-HERE2\\nHERE1\\nHERE2\", [\"\".string_interpolation, \"\".string_interpolation] of ASTNode\n    it_parses \"<<-HERE1; <<-HERE2\\nhere1\\nHERE1\\nHERE2\", [\"here1\".string_interpolation, \"\".string_interpolation] of ASTNode\n    it_parses \"<<-HERE1; <<-HERE2\\nHERE1\\nhere2\\nHERE2\", [\"\".string_interpolation, \"here2\".string_interpolation] of ASTNode\n    assert_syntax_error \"<<-HERE\\n   One\\nwrong\\n  Zero\\n  HERE\", \"heredoc line must have an indent greater than or equal to 2\", 3, 1\n    assert_syntax_error \"<<-HERE\\n   One\\n wrong\\n  Zero\\n  HERE\", \"heredoc line must have an indent greater than or equal to 2\", 3, 1\n    assert_syntax_error \"<<-HERE\\n   One\\n \\#{1}\\n  Zero\\n  HERE\", \"heredoc line must have an indent greater than or equal to 2\", 3, 1\n    assert_syntax_error \"<<-HERE\\n   One\\n  \\#{1}\\n wrong\\n  HERE\", \"heredoc line must have an indent greater than or equal to 2\", 4, 1\n    assert_syntax_error \"<<-HERE\\n   One\\n  \\#{1}\\n wrong\\#{1}\\n  HERE\", \"heredoc line must have an indent greater than or equal to 2\", 4, 1\n    assert_syntax_error \"<<-HERE\\n One\\n  \\#{1}\\n  HERE\", \"heredoc line must have an indent greater than or equal to 2\", 2, 1\n    assert_syntax_error %(\"\\#{<<-HERE}\"\\nHERE), \"heredoc cannot be used inside interpolation\"\n    assert_syntax_error %(\"foo\" \"bar\")\n\n    it_parses \"<<-'HERE'\\n  hello \\\\n world\\n  \\#{1}\\n  HERE\", \"hello \\\\n world\\n\\#{1}\".string_interpolation\n    assert_syntax_error \"<<-'HERE\\n\", \"expecting closing single quote\"\n\n    it_parses \"<<-'HERE COMES HEREDOC'\\n  hello \\\\n world\\n  \\#{1}\\n  HERE COMES HEREDOC\", \"hello \\\\n world\\n\\#{1}\".string_interpolation\n\n    it_parses \"<<-EOF.x\\n  foo\\nEOF\", Call.new(\"  foo\".string_interpolation, \"x\")\n    it_parses \"<<-'EOF'.x\\n  foo\\nEOF\", Call.new(\"  foo\".string_interpolation, \"x\")\n\n    assert_syntax_error \"<<-FOO\\n1\\nFOO.bar\", \"Unterminated heredoc: can't find \\\"FOO\\\" anywhere before the end of file\"\n    assert_syntax_error \"<<-FOO\\n1\\nFOO + 2\", \"Unterminated heredoc: can't find \\\"FOO\\\" anywhere before the end of file\"\n\n    it_parses \"<<-FOO\\n\\t1\\n\\tFOO\", \"1\".string_interpolation\n    it_parses \"<<-FOO\\n \\t1\\n \\tFOO\", \"1\".string_interpolation\n    it_parses \"<<-FOO\\n \\t 1\\n \\t FOO\", \"1\".string_interpolation\n    it_parses \"<<-FOO\\n\\t 1\\n\\t FOO\", \"1\".string_interpolation\n\n    it_parses \"x, y = <<-FOO, <<-BAR\\nhello\\nFOO\\nworld\\nBAR\",\n      MultiAssign.new([\"x\".var, \"y\".var] of ASTNode, [\"hello\".string_interpolation, \"world\".string_interpolation] of ASTNode)\n\n    it_parses \"x, y, z = <<-FOO, <<-BAR, <<-BAZ\\nhello\\nFOO\\nworld\\nBAR\\n!\\nBAZ\",\n      MultiAssign.new([\"x\".var, \"y\".var, \"z\".var] of ASTNode, [\"hello\".string_interpolation, \"world\".string_interpolation, \"!\".string_interpolation] of ASTNode)\n\n    it_parses \"enum Foo; A\\nB; C\\nD = 1; end\", EnumDef.new(\"Foo\".path, [Arg.new(\"A\"), Arg.new(\"B\"), Arg.new(\"C\"), Arg.new(\"D\", 1.int32)] of ASTNode)\n    it_parses \"enum Foo; A = 1; B; end\", EnumDef.new(\"Foo\".path, [Arg.new(\"A\", 1.int32), Arg.new(\"B\")] of ASTNode)\n    it_parses \"enum Foo : UInt16; end\", EnumDef.new(\"Foo\".path, base_type: \"UInt16\".path)\n    it_parses \"enum Foo : UInt16 ; end\", EnumDef.new(\"Foo\".path, base_type: \"UInt16\".path)\n    it_parses \"enum Foo : UInt16 # comment\\nend\", EnumDef.new(\"Foo\".path, base_type: \"UInt16\".path)\n    it_parses \"enum Foo; def foo; 1; end; end\", EnumDef.new(\"Foo\".path, [Def.new(\"foo\", body: 1.int32)] of ASTNode)\n    it_parses \"enum Foo; A = 1\\ndef foo; 1; end; end\", EnumDef.new(\"Foo\".path, [Arg.new(\"A\", 1.int32), Def.new(\"foo\", body: 1.int32)] of ASTNode)\n    it_parses \"enum Foo; A = 1\\ndef foo; 1; end\\ndef bar; 2; end\\nend\", EnumDef.new(\"Foo\".path, [Arg.new(\"A\", 1.int32), Def.new(\"foo\", body: 1.int32), Def.new(\"bar\", body: 2.int32)] of ASTNode)\n    it_parses \"enum Foo; A = 1\\ndef self.foo; 1; end\\nend\", EnumDef.new(\"Foo\".path, [Arg.new(\"A\", 1.int32), Def.new(\"foo\", receiver: \"self\".var, body: 1.int32)] of ASTNode)\n    it_parses \"enum Foo::Bar; A = 1; end\", EnumDef.new(Path.new(\"Foo\", \"Bar\"), [Arg.new(\"A\", 1.int32)] of ASTNode)\n\n    it_parses \"enum Foo; @@foo = 1\\n A \\n end\", EnumDef.new(\"Foo\".path, [Assign.new(\"@@foo\".class_var, 1.int32), Arg.new(\"A\")] of ASTNode)\n\n    it_parses \"enum Foo; private def foo; 1; end; end\", EnumDef.new(\"Foo\".path, [VisibilityModifier.new(Visibility::Private, Def.new(\"foo\", body: 1.int32))] of ASTNode)\n    it_parses \"enum Foo; protected def foo; 1; end; end\", EnumDef.new(\"Foo\".path, [VisibilityModifier.new(Visibility::Protected, Def.new(\"foo\", body: 1.int32))] of ASTNode)\n\n    it_parses \"enum Foo; {{1}}; end\", EnumDef.new(\"Foo\".path, [MacroExpression.new(1.int32)] of ASTNode)\n    it_parses \"enum Foo; {% if 1 %}2{% end %}; end\", EnumDef.new(\"Foo\".path, [MacroIf.new(1.int32, MacroLiteral.new(\"2\"))] of ASTNode)\n\n    it_parses \"enum Foo; macro foo;end; end\", EnumDef.new(\"Foo\".path, [Macro.new(\"foo\", [] of Arg, Expressions.new)] of ASTNode)\n\n    it_parses \"enum Foo; @[Bar]; end\", EnumDef.new(\"Foo\".path, [Annotation.new(\"Bar\".path)] of ASTNode)\n\n    assert_syntax_error \"enum Foo; A B; end\", \"expecting ';', 'end' or newline after enum member\"\n    assert_syntax_error \"enum Foo\\n  A,   B,   C\\nend\\n\", \"expecting ';', 'end' or newline after enum member\"\n\n    it_parses \"1.[](2)\", Call.new(1.int32, \"[]\", 2.int32)\n    it_parses \"1.[]?(2)\", Call.new(1.int32, \"[]?\", 2.int32)\n    it_parses \"1.[]=(2, 3)\", Call.new(1.int32, \"[]=\", 2.int32, 3.int32)\n\n    it_parses \"a @b-1\\nc\", [Call.new(\"a\", Call.new(\"@b\".instance_var, \"-\", 1.int32)), \"c\".call] of ASTNode\n    it_parses \"4./(2)\", Call.new(4.int32, \"/\", 2.int32)\n    it_parses \"foo[\\n1\\n]\", Call.new(\"foo\".call, \"[]\", 1.int32)\n    it_parses \"foo[\\nfoo[\\n1\\n]\\n]\", Call.new(\"foo\".call, \"[]\", Call.new(\"foo\".call, \"[]\", 1.int32))\n\n    it_parses \"if (\\ntrue\\n)\\n1\\nend\", If.new(Expressions.new([true.bool] of ASTNode), 1.int32)\n\n    it_parses \"my_def def foo\\nloop do\\nend\\nend\", Call.new(\"my_def\", Def.new(\"foo\", body: Call.new(\"loop\", block: Block.new)))\n\n    it_parses \"foo(*{1})\", Call.new(\"foo\", Splat.new(TupleLiteral.new([1.int32] of ASTNode)))\n    it_parses \"foo *{1}\", Call.new(\"foo\", Splat.new(TupleLiteral.new([1.int32] of ASTNode)))\n\n    it_parses \"a.b/2\", Call.new(Call.new(\"a\".call, \"b\"), \"/\", 2.int32)\n    it_parses \"a.b /2/\", Call.new(\"a\".call, \"b\", regex(\"2\"))\n    it_parses \"a.b / 2\", Call.new(Call.new(\"a\".call, \"b\"), \"/\", 2.int32)\n    it_parses \"a/b\", Call.new(\"a\".call, \"/\", \"b\".call)\n    it_parses \"T/1\", Call.new(\"T\".path, \"/\", 1.int32)\n    it_parses \"T::U/1\", Call.new(Path.new(%w(T U)), \"/\", 1.int32)\n    it_parses \"::T/1\", Call.new(Path.global(\"T\"), \"/\", 1.int32)\n\n    it_parses %(asm(\"nop\" \\n)), Asm.new(\"nop\")\n    it_parses %(asm(\"nop\" : : )), Asm.new(\"nop\")\n    it_parses %(asm(\"nop\" ::)), Asm.new(\"nop\")\n    it_parses %(asm(\"nop\" :: : :)), Asm.new(\"nop\")\n    it_parses %(asm(\"nop\" ::: :)), Asm.new(\"nop\")\n    it_parses %(asm(\"nop\" ::::)), Asm.new(\"nop\")\n    it_parses %(asm(\"nop\" : \"a\"(0))), Asm.new(\"nop\", [AsmOperand.new(\"a\", 0.int32)])\n    it_parses %(asm(\"nop\" : \"a\"(0) : \"b\"(1))), Asm.new(\"nop\", [AsmOperand.new(\"a\", 0.int32)], [AsmOperand.new(\"b\", 1.int32)])\n    it_parses %(asm(\"nop\" : \"a\"(0) : \"b\"(1), \"c\"(2))), Asm.new(\"nop\", [AsmOperand.new(\"a\", 0.int32)], [AsmOperand.new(\"b\", 1.int32), AsmOperand.new(\"c\", 2.int32)])\n    it_parses %(asm(\"nop\" : \"a\"(0), \"b\"(1) : \"c\"(2), \"d\"(3))), Asm.new(\"nop\", [AsmOperand.new(\"a\", 0.int32), AsmOperand.new(\"b\", 1.int32)], [AsmOperand.new(\"c\", 2.int32), AsmOperand.new(\"d\", 3.int32)])\n    it_parses %(asm(\"nop\" :: \"b\"(1), \"c\"(2))), Asm.new(\"nop\", inputs: [AsmOperand.new(\"b\", 1.int32), AsmOperand.new(\"c\", 2.int32)])\n    it_parses %(asm(\"nop\" :: \"b\"(1), \"c\"(2) ::)), Asm.new(\"nop\", inputs: [AsmOperand.new(\"b\", 1.int32), AsmOperand.new(\"c\", 2.int32)])\n    it_parses %(asm(\\n\"nop\"\\n:\\n\"a\"(0)\\n:\\n\"b\"(1),\\n\"c\"(2)\\n)), Asm.new(\"nop\", [AsmOperand.new(\"a\", 0.int32)], [AsmOperand.new(\"b\", 1.int32), AsmOperand.new(\"c\", 2.int32)])\n    it_parses %(asm(\\n\"nop\"\\n:\\n\"a\"(0),\\n\"b\"(1)\\n:\\n\"c\"(2),\\n\"d\"(3)\\n)), Asm.new(\"nop\", [AsmOperand.new(\"a\", 0.int32), AsmOperand.new(\"b\", 1.int32)], [AsmOperand.new(\"c\", 2.int32), AsmOperand.new(\"d\", 3.int32)])\n    it_parses %(asm(\"nop\" :: \"b\"(1), \"c\"(2) : \"eax\", \"ebx\" : \"volatile\", \"alignstack\", \"intel\")), Asm.new(\"nop\", inputs: [AsmOperand.new(\"b\", 1.int32), AsmOperand.new(\"c\", 2.int32)], clobbers: %w(eax ebx), volatile: true, alignstack: true, intel: true)\n    it_parses %(asm(\"nop\" :: \"b\"(1), \"c\"(2) : \"eax\", \"ebx\"\\n: \"volatile\", \"alignstack\"\\n,\\n\"intel\"\\n)), Asm.new(\"nop\", inputs: [AsmOperand.new(\"b\", 1.int32), AsmOperand.new(\"c\", 2.int32)], clobbers: %w(eax ebx), volatile: true, alignstack: true, intel: true)\n    it_parses %(asm(\"nop\" :::: \"volatile\")), Asm.new(\"nop\", volatile: true)\n    it_parses %(asm(\"bl trap\" :::: \"unwind\")), Asm.new(\"bl trap\", can_throw: true)\n\n    assert_syntax_error %q(asm(\"nop\" ::: \"#{foo}\")), \"interpolation not allowed in asm clobber\"\n    assert_syntax_error %q(asm(\"nop\" :::: \"#{volatile}\")), \"interpolation not allowed in asm option\"\n    assert_syntax_error %q(asm(\"\" ::: \"\"(var))), %{unexpected token: \"(\"}\n    assert_syntax_error %q(asm(\"\" : 1)), %(unexpected token: \"1\")\n\n    it_parses \"foo begin\\nbar do\\nend\\nend\", Call.new(\"foo\", Expressions.new([Call.new(\"bar\", block: Block.new)] of ASTNode))\n    it_parses \"foo 1.bar do\\nend\", Call.new(\"foo\", args: [Call.new(1.int32, \"bar\")] of ASTNode, block: Block.new)\n    it_parses \"return 1.bar do\\nend\", Return.new(Call.new(1.int32, \"bar\", block: Block.new))\n\n    %w(\n      begin nil true false yield with abstract\n      def macro require case select if unless include\n      extend class struct module enum while until return\n      next break lib fun alias pointerof sizeof\n      instance_sizeof offsetof typeof private protected asm\n      end self in do else elsif when rescue ensure\n    ).each do |keyword|\n      it_parses \"#{keyword} : Int32\", TypeDeclaration.new(keyword.var, \"Int32\".path)\n      it_parses \"property #{keyword} : Int32\", Call.new(\"property\", TypeDeclaration.new(keyword.var, \"Int32\".path))\n    end\n\n    it_parses \"call(foo : A, end : B)\", Call.new(\"call\", [TypeDeclaration.new(\"foo\".var, \"A\".path), TypeDeclaration.new(\"end\".var, \"B\".path)] of ASTNode)\n    it_parses \"call foo : A, end : B\", Call.new(\"call\", [TypeDeclaration.new(\"foo\".var, \"A\".path), TypeDeclaration.new(\"end\".var, \"B\".path)] of ASTNode)\n\n    it_parses \"case :foo; when :bar; 2; end\", Case.new(\"foo\".symbol, [When.new([\"bar\".symbol] of ASTNode, 2.int32)], else: nil, exhaustive: false)\n\n    it_parses \"Foo.foo(count: 3).bar { }\", Call.new(Call.new(\"Foo\".path, \"foo\", named_args: [NamedArgument.new(\"count\", 3.int32)]), \"bar\", block: Block.new)\n\n    it_parses %(\n      class Foo\n        def bar\n          print as Foo\n        end\n      end\n    ), ClassDef.new(\"Foo\".path, Def.new(\"bar\", body: Call.new(\"print\", Cast.new(Var.new(\"self\"), \"Foo\".path))))\n\n    assert_syntax_error \"a = a\", \"can't use variable name 'a' inside assignment to variable 'a'\"\n\n    assert_syntax_error \"{{ {{ 1 }} }}\", \"can't nest macro expressions\"\n    assert_syntax_error \"{{ {% begin %} }}\", \"can't nest macro expressions\"\n\n    it_parses \"Foo?\", Crystal::Generic.new(Path.global(\"Union\"), [\"Foo\".path, Path.global(\"Nil\")] of ASTNode)\n    it_parses \"Foo::Bar?\", Crystal::Generic.new(Path.global(\"Union\"), [Path.new(%w(Foo Bar)), Path.global(\"Nil\")] of ASTNode)\n    it_parses \"Foo(T)?\", Crystal::Generic.new(Path.global(\"Union\"), [Generic.new(\"Foo\".path, [\"T\".path] of ASTNode), Path.global(\"Nil\")] of ASTNode)\n    it_parses \"Foo??\", Crystal::Generic.new(Path.global(\"Union\"), [\n      Crystal::Generic.new(Path.global(\"Union\"), [\"Foo\".path, Path.global(\"Nil\")] of ASTNode),\n      Path.global(\"Nil\"),\n    ] of ASTNode)\n\n    it_parses \"{1 => 2 / 3}\", HashLiteral.new([HashLiteral::Entry.new(1.int32, Call.new(2.int32, \"/\", 3.int32))])\n    it_parses \"a { |x| x } / b\", Call.new(Call.new(\"a\", block: Block.new(args: [\"x\".var], body: \"x\".var)), \"/\", \"b\".call)\n\n    it_parses \"1 if /x/\", If.new(RegexLiteral.new(\"x\".string), 1.int32)\n\n    it_parses \"foo bar.baz(1) do\\nend\", Call.new(\"foo\", args: [Call.new(\"bar\".call, \"baz\", 1.int32)] of ASTNode, block: Block.new)\n\n    it_parses \"1 rescue 2 if 3\", If.new(3.int32, ExceptionHandler.new(1.int32, [Rescue.new(2.int32)]))\n    it_parses \"1 ensure 2 if 3\", If.new(3.int32, ExceptionHandler.new(1.int32, ensure: 2.int32))\n\n    it_parses \"yield foo do\\nend\", Yield.new([Call.new(\"foo\", block: Block.new)] of ASTNode)\n\n    it_parses \"x.y=(1).to_s\", Call.new(\"x\".call, \"y=\", Call.new(Expressions.new([1.int32] of ASTNode), \"to_s\"))\n\n    it_parses \"1 ** -x\", Call.new(1.int32, \"**\", Call.new(\"x\".call, \"-\"))\n\n    it_parses \"foo.Bar\", Call.new(\"foo\".call, \"Bar\")\n\n    [{'(', ')'}, {'[', ']'}, {'<', '>'}, {'{', '}'}, {'|', '|'}].each do |open, close|\n      it_parses \"{% begin %}%#{open} %s #{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%#{open} %s #{close}\"))\n      it_parses \"{% begin %}%q#{open} %s #{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%q#{open} %s #{close}\"))\n      it_parses \"{% begin %}%Q#{open} %s #{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%Q#{open} %s #{close}\"))\n      it_parses \"{% begin %}%i#{open} %s #{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%i#{open} %s #{close}\"))\n      it_parses \"{% begin %}%w#{open} %s #{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%w#{open} %s #{close}\"))\n      it_parses \"{% begin %}%x#{open} %s #{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%x#{open} %s #{close}\"))\n      it_parses \"{% begin %}%r#{open}\\\\A#{close}{% end %}\", MacroIf.new(true.bool, MacroLiteral.new(\"%r#{open}\\\\A#{close}\"))\n    end\n\n    it_parses %(foo(bar:\"a\", baz:\"b\")), Call.new(\"foo\", named_args: [NamedArgument.new(\"bar\", \"a\".string), NamedArgument.new(\"baz\", \"b\".string)])\n    it_parses %(foo(bar:a, baz:b)), Call.new(\"foo\", named_args: [NamedArgument.new(\"bar\", \"a\".call), NamedArgument.new(\"baz\", \"b\".call)])\n    it_parses %({foo:\"a\", bar:\"b\"}), NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"foo\", \"a\".string), NamedTupleLiteral::Entry.new(\"bar\", \"b\".string)])\n    it_parses %({foo:'a', bar:'b'}), NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"foo\", CharLiteral.new('a')), NamedTupleLiteral::Entry.new(\"bar\", CharLiteral.new('b'))])\n    it_parses %({foo:a, bar:b}), NamedTupleLiteral.new([NamedTupleLiteral::Entry.new(\"foo\", \"a\".call), NamedTupleLiteral::Entry.new(\"bar\", \"b\".call)])\n\n    assert_syntax_error \"return do\\nend\", %(unexpected token: \"do\")\n\n    # #237\n    describe \"`class`, `module`, `def`, etc. inside a `def`\" do\n      assert_syntax_error \"def foo\\ndef\\nend\"\n      assert_syntax_error \"def foo\\nmacro\\nend\"\n      assert_syntax_error \"def foo\\nclass\\nend\"\n      assert_syntax_error \"def foo\\nstruct\\nend\"\n      assert_syntax_error \"def foo\\nmodule\\nend\"\n      assert_syntax_error \"def foo\\nfun\\nend\"\n      assert_syntax_error \"def foo\\nalias\\nend\"\n      assert_syntax_error \"def foo\\nabstract\\nend\"\n      assert_syntax_error \"def foo\\ninclude\\nend\"\n      assert_syntax_error \"def foo\\nextend\\nend\"\n      assert_syntax_error \"def foo\\nlib\\nend\"\n    end\n\n    assert_syntax_error \"def foo(x = 1, y); end\",\n      \"parameter must have a default value\"\n\n    assert_syntax_error \" [1, 2, 3 end\"\n    assert_syntax_error \" {1 => end\"\n\n    assert_syntax_error \" {1, 2, 3 end\"\n    assert_syntax_error \" (1, 2, 3 end\",\n      \"unterminated parenthesized expression\", 1, 2\n\n    assert_syntax_error \"foo(1, 2, 3 end\",\n      \"expecting token ')', not 'end'\", 1, 13\n\n    assert_syntax_error \"foo(foo(&.block)\",\n      \"expecting token ')', not 'EOF'\", 1, 17\n\n    assert_syntax_error \"case when .foo? then 1; end\"\n    assert_syntax_error \"macro foo;{%end};end\"\n    assert_syntax_error \"foo {1, 2}\", %(unexpected token: \",\")\n    assert_syntax_error \"pointerof(self)\", \"can't take address of self\"\n    assert_syntax_error \"def foo 1; end\"\n\n    assert_syntax_error %<{\"x\": [] of Int32,\\n}\\n1.foo(>, \"unterminated call\", 3, 6\n\n    assert_syntax_error \"def foo x y; end\", \"parentheses are mandatory for def parameters\"\n    assert_syntax_error \"macro foo(x y z); end\"\n    assert_syntax_error \"macro foo x y; end\", \"parentheses are mandatory for macro parameters\"\n    assert_syntax_error \"macro foo *y;end\", \"parentheses are mandatory for macro parameters\"\n    assert_syntax_error %(macro foo x; 1 + 2; end), \"parentheses are mandatory for macro parameters\"\n    assert_syntax_error %(macro foo x\\n 1 + 2; end), \"parentheses are mandatory for macro parameters\"\n\n    assert_syntax_error \"1 2\", %(unexpected token: \"2\")\n    assert_syntax_error \"macro foo(*x, *y); end\", %(unexpected token: \"*\")\n\n    assert_syntax_error \"foo x: 1, x: 1\", \"duplicated named argument: x\", 1, 11\n\n    assert_syntax_error \"def foo(x, x); end\", \"duplicated def parameter name: x\", 1, 12\n    assert_syntax_error \"def foo(x y, x z); end\", \"duplicated def parameter external name: x\", 1, 14\n    assert_syntax_error \"class Foo(T, T); end\", \"duplicated type parameter name: T\", 1, 14\n    assert_syntax_error \"->(x : Int32, x : Int32) {}\", \"duplicated proc literal parameter name: x\", 1, 15\n    assert_syntax_error \"foo { |x, x| }\", \"duplicated block parameter name: x\", 1, 11\n    assert_syntax_error \"foo { |x, (x)| }\", \"duplicated block parameter name: x\", 1, 12\n    assert_syntax_error \"foo { |(x, x)| }\", \"duplicated block parameter name: x\", 1, 12\n\n    assert_syntax_error \"def foo(*x, **x); end\", \"duplicated def parameter name: x\"\n    assert_syntax_error \"def foo(*x, &x); end\", \"duplicated def parameter name: x\"\n    assert_syntax_error \"def foo(**x, &x); end\", \"duplicated def parameter name: x\"\n    assert_syntax_error \"def foo(x, **x); end\", \"duplicated def parameter name: x\"\n\n    assert_syntax_error \"fun foo(x : Int32, x : Int64); end\", \"duplicated fun parameter name: x\"\n    assert_syntax_error \"lib Foo; fun foo(x : Int32, x : Int64); end\", \"duplicated fun parameter name: x\"\n\n    assert_syntax_error \"Set {1, 2, 3} of Int32\"\n    assert_syntax_error \"Hash {foo: 1} of Int32 => Int32\"\n    assert_syntax_error \"enum Foo < UInt16; end\"\n    assert_syntax_error \"foo(1 2)\"\n    assert_syntax_error %(foo(\"bar\" \"baz\"))\n\n    assert_syntax_error \"@:Foo\"\n\n    assert_syntax_error \"false foo\"\n    assert_syntax_error \"nil foo\"\n    assert_syntax_error \"'a' foo\"\n    assert_syntax_error %(\"hello\" foo)\n    assert_syntax_error %(:bar foo)\n    assert_syntax_error \"1 foo\"\n    assert_syntax_error \"1 then\"\n    assert_syntax_error \"return 1 foo\"\n    assert_syntax_error \"return false foo\"\n\n    assert_syntax_error \"a = 1; b = 2; a, b += 1, 2\"\n\n    assert_syntax_error \"lib LibC\\n$Errno : Int32\\nend\", \"external variables must start with lowercase, use for example `$errno = Errno : Int32`\"\n\n    assert_syntax_error \"a += 1\",\n      \"'+=' before definition of 'a'\"\n    assert_syntax_error \"self = 1\",\n      \"can't change the value of self\"\n    assert_syntax_error \"self += 1\",\n      \"can't change the value of self\"\n    assert_syntax_error \"FOO, BAR = 1, 2\",\n      \"Multiple assignment is not allowed for constants\"\n    assert_syntax_error \"self, x = 1, 2\",\n      \"can't change the value of self\"\n    assert_syntax_error \"x, self = 1, 2\",\n      \"can't change the value of self\"\n\n    assert_syntax_error \"macro foo(x : Int32); end\"\n\n    assert_syntax_error \"/foo)/\", \"invalid regex\"\n    assert_syntax_error \"def =\\nend\"\n    assert_syntax_error \"def foo; A = 1; end\", \"dynamic constant assignment. Constants can only be declared at the top level or inside other types.\"\n    assert_syntax_error \"{1, ->{ |x| x } }\", \"unexpected token: \\\"|\\\", proc literals specify their parameters like this: ->(x : Type) { ... }\"\n    assert_syntax_error \"{1, ->do\\n|x| x\\end }\", \"unexpected token: \\\"|\\\", proc literals specify their parameters like this: ->(x : Type) { ... }\"\n    assert_syntax_error \"{1, ->{ |_| x } }\", \"unexpected token: \\\"|\\\", proc literals specify their parameters like this: ->(param : Type) { ... }\"\n\n    # #2874\n    assert_syntax_error \"A = B = 1\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = (B = 1)\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = foo(B = 1)\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = foo { B = 1 }\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = begin; B = 1; end\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = begin; 1; rescue; B = 1; end\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = begin; 1; rescue; 1; else; B = 1; end\", \"dynamic constant assignment\"\n    assert_syntax_error \"A = begin; 1; ensure; B = 1; end\", \"dynamic constant assignment\"\n\n    assert_syntax_error \"1 while 3\", \"trailing `while` is not supported\"\n    assert_syntax_error \"1 until 3\", \"trailing `until` is not supported\"\n    assert_syntax_error \"x++\", \"postfix increment is not supported, use `exp += 1`\"\n    assert_syntax_error \"x--\", \"postfix decrement is not supported, use `exp -= 1`\"\n    assert_syntax_error \"if 1 == 1 a; end\", \"unexpected token\"\n    assert_syntax_error \"unless 1 == 1 a; end\", \"unexpected token\"\n    assert_syntax_error \"while 1 == 1 a; end\", \"unexpected token\"\n    assert_syntax_error \"case 1 == 1 a; when 2; end\", \"unexpected token\"\n    assert_syntax_error \"case 1 == 1; when 2 a; end\", \"unexpected token\"\n\n    assert_syntax_error %(class Foo; require \"bar\"; end), \"can't require inside type declarations\"\n    assert_syntax_error %(module Foo; require \"bar\"; end), \"can't require inside type declarations\"\n    assert_syntax_error %(def foo; require \"bar\"; end), \"can't require inside def\"\n\n    assert_syntax_error \"def foo(x: Int32); end\", \"space required before colon in type restriction\"\n    assert_syntax_error \"def foo(x :Int32); end\", \"space required after colon in type restriction\"\n\n    assert_syntax_error \"def f end\", %(unexpected token: \"end\" (expected \";\" or newline))\n\n    assert_syntax_error \"fun foo\\nclass\", \"can't define class inside fun\"\n    assert_syntax_error \"fun foo\\nFoo = 1\", \"dynamic constant assignment\"\n\n    assert_syntax_error %([\\n\"foo\"\\n\"bar\"\\n])\n    it_parses \"[\\n1\\n]\", ArrayLiteral.new([1.int32] of ASTNode)\n    it_parses \"[\\n1,2\\n]\", ArrayLiteral.new([1.int32, 2.int32] of ASTNode)\n\n    assert_syntax_error %({\\n1 => 2\\n3 => 4\\n})\n    assert_syntax_error %({\\n1 => 2, 3 => 4\\n5 => 6})\n\n    assert_syntax_error %({\\n\"foo\"\\n\"bar\"\\n})\n\n    assert_syntax_error %(\n      lib LibFoo\n        fun foo(x : Int32\n              y : Float64)\n      end\n      )\n\n    assert_syntax_error <<-CRYSTAL, \"invalid trailing comma in call\", line: 2, column: 8\n      if 1\n        foo 1,\n      end\n      CRYSTAL\n\n    assert_syntax_error \"foo 1,\", \"invalid trailing comma in call\", line: 1, column: 6\n\n    assert_syntax_error \"def foo:String\\nend\", \"a space is mandatory between ':' and return type\"\n    assert_syntax_error \"def foo :String\\nend\", \"a space is mandatory between ':' and return type\"\n    assert_syntax_error \"def foo():String\\nend\", \"a space is mandatory between ':' and return type\"\n    assert_syntax_error \"def foo() :String\\nend\", \"a space is mandatory between ':' and return type\"\n\n    assert_syntax_error \"foo.responds_to?\"\n\n    assert_syntax_error \"foo :: Foo\"\n    assert_syntax_error \"@foo :: Foo\"\n    assert_syntax_error \"@@foo :: Foo\"\n    assert_syntax_error \"$foo :: Foo\"\n\n    assert_syntax_error \"def foo(var : Foo+); end\"\n\n    describe \"`&&` and `||` are not methods\" do\n      assert_syntax_error \"foo.&&\"\n      assert_syntax_error \"foo.&&()\"\n      assert_syntax_error \"foo &.&&\"\n      assert_syntax_error \"foo &.&&()\"\n\n      assert_syntax_error \"foo.||\"\n      assert_syntax_error \"foo.||()\"\n      assert_syntax_error \"foo &.||\"\n      assert_syntax_error \"foo &.||()\"\n    end\n\n    describe \"redefine pseudo-method\" do\n      assert_syntax_error \"def !; end\", \"'!' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def is_a?; end\", \"'is_a?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def as; end\", \"'as' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def as?; end\", \"'as?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def responds_to?; end\", \"'responds_to?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def nil?; end\", \"'nil?' is a pseudo-method and can't be redefined\"\n\n      assert_syntax_error \"def self.!; end\", \"'!' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def self.is_a?; end\", \"'is_a?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def self.as; end\", \"'as' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def self.as?; end\", \"'as?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def self.responds_to?; end\", \"'responds_to?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"def self.nil?; end\", \"'nil?' is a pseudo-method and can't be redefined\"\n\n      assert_syntax_error \"macro is_a?; end\", \"'is_a?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"macro as; end\", \"'as' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"macro as?; end\", \"'as?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"macro responds_to?; end\", \"'responds_to?' is a pseudo-method and can't be redefined\"\n      assert_syntax_error \"macro nil?; end\", \"'nil?' is a pseudo-method and can't be redefined\"\n    end\n\n    assert_syntax_error \"Foo{one: :two, three: :four}\", \"can't use named tuple syntax for Hash-like literal\"\n    assert_syntax_error \"{one: :two, three: :four} of Symbol => Symbol\"\n    assert_syntax_error %(Hash{\"foo\": 1}), \"can't use named tuple syntax for Hash-like literal\"\n    assert_syntax_error %(Hash{\"foo\": 1, \"bar\": 2}), \"can't use named tuple syntax for Hash-like literal\"\n\n    assert_syntax_error \"{foo: 1\\nbar: 2}\"\n    assert_syntax_error \"{foo: 1, bar: 2\\nbaz: 3}\"\n\n    assert_syntax_error \"'''\", \"invalid empty char literal\"\n\n    assert_syntax_error \"def foo(*args = 1); end\", \"splat parameter can't have default value\"\n    assert_syntax_error \"def foo(**args = 1); end\", \"double splat parameter can't have default value\"\n\n    assert_syntax_error \"require 1\", \"expected string literal for require\"\n    assert_syntax_error %(def foo(\"bar \\#{1} qux\" y); y; end), \"interpolation not allowed in external name\"\n\n    assert_syntax_error \"def Foo(Int32).bar;end\"\n\n    assert_syntax_error \"[\\n]\", \"for empty arrays use '[] of ElementType'\"\n    assert_syntax_error \"[1 1]\"\n    assert_syntax_error \"{\\n}\", \"for empty hashes use '{} of KeyType => ValueType'\"\n    assert_syntax_error \"{1 => 2 3 => 4}\"\n    assert_syntax_error \"{1 => 2, 3 => 4 5 => 6}\"\n    assert_syntax_error \"{a: 1 b: 2}\"\n    assert_syntax_error \"{a: 1, b: 2 c: 3}\"\n    assert_syntax_error \"{1 2}\"\n    assert_syntax_error \"{1, 2 3}\"\n    assert_syntax_error \"(1, 2 3)\"\n    assert_syntax_error \"Foo(T U)\"\n    assert_syntax_error \"Foo(T, U V)\"\n    assert_syntax_error \"class Foo(T U)\"\n    assert_syntax_error \"class Foo(T, U V)\"\n    assert_syntax_error \"->(x y) { }\"\n    assert_syntax_error \"->(x, y z) { }\"\n\n    assert_syntax_error \"x[1:-2]\"\n\n    assert_syntax_error \"1 ? : 2 : 3\"\n\n    assert_syntax_error %(def foo(\"bar\");end), \"expected parameter internal name\"\n\n    it_parses \"{[] of Foo, Bar::Baz.new}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(Path.new(%w[Bar Baz]), \"new\")] of ASTNode)\n    it_parses \"{[] of Foo, ::Bar::Baz.new}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(Path.new(%w[Bar Baz], global: true), \"new\")] of ASTNode)\n    it_parses \"{[] of Foo, Bar::Baz + 2}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(Path.new(%w[Bar Baz]), \"+\", [2.int32] of ASTNode)] of ASTNode)\n    it_parses \"{[] of Foo, Bar::Baz * 2}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(Path.new(%w[Bar Baz]), \"*\", [2.int32] of ASTNode)] of ASTNode)\n    it_parses \"{[] of Foo, Bar::Baz ** 2}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(Path.new(%w[Bar Baz]), \"**\", [2.int32] of ASTNode)] of ASTNode)\n    it_parses \"{[] of Foo, ::foo}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(\"foo\", global: true)] of ASTNode)\n    it_parses \"{[] of Foo, self.foo}\", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, \"Foo\".path), Call.new(\"self\".var, \"foo\")] of ASTNode)\n\n    it_parses <<-'CRYSTAL', Macro.new(\"foo\", body: Expressions.new([MacroLiteral.new(\"  <<-FOO\\n    \\#{ \"), MacroVar.new(\"var\"), MacroLiteral.new(\" }\\n  FOO\\n\")] of ASTNode))\n      macro foo\n        <<-FOO\n          #{ %var }\n        FOO\n      end\n      CRYSTAL\n\n    it_parses <<-'CRYSTAL', Macro.new(\"foo\", body: MacroLiteral.new(\"  <<-FOO, <<-BAR + \\\"\\\"\\n  FOO\\n  BAR\\n\"))\n      macro foo\n        <<-FOO, <<-BAR + \"\"\n        FOO\n        BAR\n      end\n      CRYSTAL\n\n    it_parses <<-'CRYSTAL', Macro.new(\"foo\", body: MacroLiteral.new(\"  <<-FOO\\n    %foo\\n  FOO\\n\"))\n      macro foo\n        <<-FOO\n          %foo\n        FOO\n      end\n      CRYSTAL\n\n    it_parses \"macro foo; bar class: 1; end\", Macro.new(\"foo\", body: MacroLiteral.new(\" bar class: 1; \"))\n\n    assert_syntax_error \"lib Foo%end\", \"expecting any of these tokens: ;, NEWLINE, SPACE (not '%')\"\n\n    it_parses %(class Foo \"a\" end), ClassDef.new(\"Foo\".path, [\"a\".string] of ASTNode)\n    it_parses \"class Foo 'a' end\", ClassDef.new(\"Foo\".path, [CharLiteral.new('a')] of ASTNode)\n    it_parses \"class Foo [1] end\", ClassDef.new(\"Foo\".path, [([1.int32] of ASTNode).array] of ASTNode)\n    it_parses \"class Foo {1} end\", ClassDef.new(\"Foo\".path, [TupleLiteral.new([1.int32] of ASTNode)] of ASTNode)\n    it_parses \"class Foo ->{} end\", ClassDef.new(\"Foo\".path, [ProcLiteral.new(Def.new(\"->\"))] of ASTNode)\n    it_parses \"class Foo ->(x : Bar){} end\", ClassDef.new(\"Foo\".path, [ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\", restriction: \"Bar\".path)]))] of ASTNode)\n    it_parses \"class Foo :Bar end\", ClassDef.new(\"Foo\".path, [\"Bar\".symbol] of ASTNode)\n    it_parses \"class Foo :bar end\", ClassDef.new(\"Foo\".path, [\"bar\".symbol] of ASTNode)\n    it_parses \"class Foo %x() end\", ClassDef.new(\"Foo\".path, [Call.new(nil, \"`\", \"\".string)] of ASTNode)\n    it_parses \"class Foo %w() end\", ClassDef.new(\"Foo\".path, [([] of ASTNode).array_of(Path.global(\"String\"))] of ASTNode)\n    it_parses \"class Foo %() end\", ClassDef.new(\"Foo\".path, [\"\".string] of ASTNode)\n    it_parses \"class Foo < Bar :Qux end\", ClassDef.new(\"Foo\".path, [\"Qux\".symbol] of ASTNode, \"Bar\".path)\n    it_parses \"class Foo include Bar end\", ClassDef.new(\"Foo\".path, [Include.new(\"Bar\".path)] of ASTNode)\n\n    it_parses %(struct Foo \"a\" end), ClassDef.new(\"Foo\".path, [\"a\".string] of ASTNode, struct: true)\n    it_parses \"struct Foo 'a' end\", ClassDef.new(\"Foo\".path, [CharLiteral.new('a')] of ASTNode, struct: true)\n    it_parses \"struct Foo [1] end\", ClassDef.new(\"Foo\".path, [([1.int32] of ASTNode).array] of ASTNode, struct: true)\n    it_parses \"struct Foo {1} end\", ClassDef.new(\"Foo\".path, [TupleLiteral.new([1.int32] of ASTNode)] of ASTNode, struct: true)\n    it_parses \"struct Foo ->{} end\", ClassDef.new(\"Foo\".path, [ProcLiteral.new(Def.new(\"->\"))] of ASTNode, struct: true)\n    it_parses \"struct Foo ->(x : Bar){} end\", ClassDef.new(\"Foo\".path, [ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\", restriction: \"Bar\".path)]))] of ASTNode, struct: true)\n    it_parses \"struct Foo :Bar end\", ClassDef.new(\"Foo\".path, [\"Bar\".symbol] of ASTNode, struct: true)\n    it_parses \"struct Foo :bar end\", ClassDef.new(\"Foo\".path, [\"bar\".symbol] of ASTNode, struct: true)\n    it_parses \"struct Foo %x() end\", ClassDef.new(\"Foo\".path, [Call.new(nil, \"`\", \"\".string)] of ASTNode, struct: true)\n    it_parses \"struct Foo %w() end\", ClassDef.new(\"Foo\".path, [([] of ASTNode).array_of(Path.global(\"String\"))] of ASTNode, struct: true)\n    it_parses \"struct Foo %() end\", ClassDef.new(\"Foo\".path, [\"\".string] of ASTNode, struct: true)\n    it_parses \"struct Foo < Bar :Qux end\", ClassDef.new(\"Foo\".path, [\"Qux\".symbol] of ASTNode, \"Bar\".path, struct: true)\n    it_parses \"struct Foo include Bar end\", ClassDef.new(\"Foo\".path, [Include.new(\"Bar\".path)] of ASTNode, struct: true)\n\n    it_parses %(module Foo \"a\" end), ModuleDef.new(\"Foo\".path, [\"a\".string] of ASTNode)\n    it_parses \"module Foo 'a' end\", ModuleDef.new(\"Foo\".path, [CharLiteral.new('a')] of ASTNode)\n    it_parses \"module Foo [1] end\", ModuleDef.new(\"Foo\".path, [([1.int32] of ASTNode).array] of ASTNode)\n    it_parses \"module Foo {1} end\", ModuleDef.new(\"Foo\".path, [TupleLiteral.new([1.int32] of ASTNode)] of ASTNode)\n    it_parses \"module Foo ->{} end\", ModuleDef.new(\"Foo\".path, [ProcLiteral.new(Def.new(\"->\"))] of ASTNode)\n    it_parses \"module Foo ->(x : Bar){} end\", ModuleDef.new(\"Foo\".path, [ProcLiteral.new(Def.new(\"->\", [Arg.new(\"x\", restriction: \"Bar\".path)]))] of ASTNode)\n    it_parses \"module Foo :Bar end\", ModuleDef.new(\"Foo\".path, [\"Bar\".symbol] of ASTNode)\n    it_parses \"module Foo :bar end\", ModuleDef.new(\"Foo\".path, [\"bar\".symbol] of ASTNode)\n    it_parses \"module Foo %x() end\", ModuleDef.new(\"Foo\".path, [Call.new(nil, \"`\", \"\".string)] of ASTNode)\n    it_parses \"module Foo %w() end\", ModuleDef.new(\"Foo\".path, [([] of ASTNode).array_of(Path.global(\"String\"))] of ASTNode)\n    it_parses \"module Foo %() end\", ModuleDef.new(\"Foo\".path, [\"\".string] of ASTNode)\n    it_parses \"module Foo include Bar end\", ModuleDef.new(\"Foo\".path, [Include.new(\"Bar\".path)] of ASTNode)\n\n    [\n      {\"annotation Foo\", %w(; NEWLINE SPACE)},\n      {\"class Foo\", %w(; NEWLINE SPACE)},\n      {\"class Foo < Bar\", %w(; NEWLINE SPACE)},\n      {\"enum Foo\", %w(; NEWLINE)},\n      {\"enum Foo : Int32\", %w(; NEWLINE)},\n      {\"lib Foo\", %w(; NEWLINE SPACE)},\n      {\"lib Foo; enum Bar\", %w(; NEWLINE)},\n      {\"lib Foo; struct Bar\", %w(; NEWLINE SPACE)},\n      {\"lib Foo; union Bar\", %w(; NEWLINE SPACE)},\n      {\"module Foo\", %w(; NEWLINE SPACE)},\n      {\"struct Foo\", %w(; NEWLINE SPACE)},\n    ].each do |(header, expected)|\n      expected = expected.join \", \"\n      assert_syntax_error %(#{header}\"a\"), \"expecting any of these tokens: #{expected} (not 'DELIMITER_START')\"\n      assert_syntax_error \"#{header}'a'\", \"expecting any of these tokens: #{expected} (not 'a')\"\n      assert_syntax_error \"#{header}[1]\", \"expecting any of these tokens: #{expected} (not '[')\"\n      assert_syntax_error \"#{header}{1}\", \"expecting any of these tokens: #{expected} (not '{')\"\n      assert_syntax_error \"#{header}{|a|a}\", \"expecting any of these tokens: #{expected} (not '{')\"\n      assert_syntax_error \"#{header} {|a|a}\"\n      assert_syntax_error \"#{header}->{}\", \"expecting any of these tokens: #{expected} (not '->')\"\n      assert_syntax_error \"#{header}->(x : Qux){}\", \"expecting any of these tokens: #{expected} (not '->')\"\n      assert_syntax_error \"#{header}:Qux\", \"expecting any of these tokens: #{expected} (not 'Qux')\"\n      assert_syntax_error \"#{header}:qux\", \"expecting any of these tokens: #{expected} (not 'qux')\"\n      assert_syntax_error \"#{header}%x()\", \"expecting any of these tokens: #{expected} (not 'DELIMITER_START')\"\n      assert_syntax_error \"#{header}%w()\", \"expecting any of these tokens: #{expected} (not 'STRING_ARRAY_START')\"\n      assert_syntax_error \"#{header}%()\", \"expecting any of these tokens: #{expected} (not 'DELIMITER_START')\"\n    end\n\n    assert_syntax_error \"foo.[]? = 1\"\n    assert_syntax_error \"foo.[]? += 1\"\n    assert_syntax_error \"foo[0]? = 1\"\n    assert_syntax_error \"foo[0]? += 1\"\n    assert_syntax_error \"foo.[0]? = 1\"\n    assert_syntax_error \"foo.[0]? += 1\"\n    assert_syntax_error \"foo &.[0]? = 1\"\n    assert_syntax_error \"foo &.[0]? += 1\"\n\n    assert_syntax_error \"foo &.[]?=(1)\"\n    assert_syntax_error \"foo &.[]? = 1\"\n    assert_syntax_error \"foo &.[]? 0 =(1)\"\n    assert_syntax_error \"foo &.[]? 0 = 1\"\n    assert_syntax_error \"foo &.[]?(0)=(1)\"\n    assert_syntax_error \"foo &.[]?(0) = 1\"\n    assert_syntax_error \"foo &.[] 0 =(1)\"\n    assert_syntax_error \"foo &.[] 0 = 1\"\n    assert_syntax_error \"foo &.[](0)=(1)\"\n    assert_syntax_error \"foo &.[](0) = 1\"\n\n    assert_syntax_error \"foo &.bar.[] 0 =(1)\"\n    assert_syntax_error \"foo &.bar.[] 0 = 1\"\n    assert_syntax_error \"foo &.bar.[](0)=(1)\"\n    assert_syntax_error \"foo &.bar.[](0) = 1\"\n\n    describe \"end locations\" do\n      assert_end_location \"nil\"\n      assert_end_location \"false\"\n      assert_end_location \"123\"\n      assert_end_location \"123.45\"\n      assert_end_location \"'a'\"\n      assert_end_location \":foo\"\n      assert_end_location %(\"hello\")\n      assert_end_location \"[1, 2]\"\n      assert_end_location \"[] of Int32\"\n      assert_end_location \"{a: 1}\"\n      assert_end_location \"{} of Int32 => String\"\n      assert_end_location \"1..3\"\n      assert_end_location \"/foo/\"\n      assert_end_location \"{1, 2}\"\n      assert_end_location \"foo\"\n      assert_end_location \"foo(1, 2)\"\n      assert_end_location \"foo 1, 2\"\n      assert_end_location \"Foo\"\n      assert_end_location \"Foo(A)\"\n      assert_end_location \"if 1; else; 2; end\"\n      assert_end_location \"if 1; elseif; 2; end\"\n      assert_end_location \"unless 1; 2; end\"\n      assert_end_location \"a = 123\"\n      assert_end_location \"a, b = 1, 2\"\n      assert_end_location \"@foo\"\n      assert_end_location \"foo.@foo\"\n      assert_end_location \"@@foo\"\n      assert_end_location \"a && b\"\n      assert_end_location \"a || b\"\n      assert_end_location \"def foo; end\"\n      assert_end_location \"def foo; 1; end\"\n      assert_end_location \"def foo; rescue ex; end\"\n      assert_end_location \"abstract def foo\"\n      assert_end_location \"abstract def foo : Int32\"\n      assert_end_location \"begin; 1; end\"\n      assert_end_location \"class Foo; end\"\n      assert_end_location \"struct Foo; end\"\n      assert_end_location \"module Foo; end\"\n      assert_end_location \"alias Foo = Bar\"\n      assert_end_location \"->{ }\"\n      assert_end_location \"macro foo;end\"\n      assert_end_location \"macro foo; 123; end\"\n      assert_end_location \"!foo\"\n      assert_end_location \"pointerof(@foo)\"\n      assert_end_location \"sizeof(Foo)\"\n      assert_end_location \"offsetof(Foo, @a)\"\n      assert_end_location \"offsetof({X, Y}, 1)\"\n      assert_end_location \"typeof(1)\"\n      assert_end_location \"1 if 2\"\n      assert_end_location \"while 1; end\"\n      assert_end_location \"return\"\n      assert_end_location \"return 1\"\n      assert_end_location \"yield\"\n      assert_end_location \"yield 1\"\n      assert_end_location \"include Foo\"\n      assert_end_location \"extend Foo\"\n      assert_end_location \"1.as(Int32)\"\n      assert_end_location \"puts obj.foo\"\n      assert_end_location \"a, b = 1, 2 if 3\"\n      assert_end_location \"abstract def foo(x)\"\n      assert_end_location \"::foo\"\n      assert_end_location \"foo.[0] = 1\"\n      assert_end_location \"x : Foo(A, *B, C)\"\n      assert_end_location \"Int[8]?\"\n      assert_end_location \"[1, 2,]\"\n      assert_end_location \"foo(\\n  &.block\\n)\", line_number: 3, column_number: 1\n      assert_end_location \"foo.bar(x) do; end\"\n      assert_end_location \"foo(bar: 123)\"\n      assert_end_location \"foo bar: 123\"\n      assert_end_location \"f foo(bar: 123)\"\n      assert_end_location \"f(foo bar: 123)\"\n      assert_end_location \"f foo bar: 123\"\n      assert_end_location \"f foo(x: 123, &.bar)\"\n      assert_end_location \"f foo x: 123, &.bar\"\n      assert_end_location \"%w(one two)\"\n      assert_end_location \"{%\\nif foo\\n  bar\\n end\\n%}\", line_number: 5, column_number: 2\n      assert_end_location \"foo bar, out baz\"\n      assert_end_location \"Foo?\"\n      assert_end_location \"foo : Foo.class\"\n      assert_end_location \"foo : Foo?\"\n      assert_end_location \"foo : Foo*\"\n      assert_end_location \"foo : Foo**\"\n      assert_end_location \"foo : Foo[42]\"\n      assert_end_location \"foo ->bar\"\n      assert_end_location \"foo ->bar=\"\n      assert_end_location \"foo ->self.bar\"\n      assert_end_location \"foo ->self.bar=\"\n      assert_end_location \"foo ->Bar.baz\"\n      assert_end_location \"foo ->Bar.baz=\"\n      assert_end_location \"foo ->@bar.baz\"\n      assert_end_location \"foo ->@bar.baz=\"\n      assert_end_location \"foo ->@@bar.baz\"\n      assert_end_location \"foo ->@@bar.baz=\"\n      assert_end_location \"foo ->bar(Baz)\"\n      assert_end_location \"foo *bar\"\n      assert_end_location \"foo **bar\"\n      assert_end_location \"Foo { 1 }\"\n      assert_end_location \"foo.!\"\n      assert_end_location \"foo.!()\"\n      assert_end_location \"f.x = foo\"\n      assert_end_location \"f.x=(*foo)\"\n      assert_end_location \"f.x=(foo).bar\"\n      assert_end_location \"x : Foo ->\"\n      assert_end_location \"x : Foo -> Bar\"\n      assert_end_location %(require \"foo\")\n      assert_end_location \"begin; 1; 2; 3; end\"\n      assert_end_location \"1..\"\n      assert_end_location \"foo.responds_to?(:foo)\"\n      assert_end_location \"foo.responds_to? :foo\"\n      assert_end_location \"foo.nil?\"\n      assert_end_location \"foo.nil?(  )\"\n      assert_end_location \"@a = uninitialized Foo\"\n      assert_end_location \"@@a = uninitialized Foo\"\n      assert_end_location \"1 rescue 2\"\n      assert_end_location \"1 ensure 2\"\n      assert_end_location \"foo.bar= *baz\"\n      assert_end_location \"case :foo; when :bar; 2; end\"\n      assert_end_location %(asm(\"nop\" ::))\n      assert_end_location \"select; when foo; 2; end\"\n      assert_end_location %(\"hello \"\\\\\\n\"world\"), line_number: 2, column_number: 7\n      assert_end_location \"foo(&.bar)\"\n      assert_end_location \"foo &.bar\"\n      assert_end_location \"foo(&bar)\"\n      assert_end_location \"foo &bar\"\n    end\n\n    assert_syntax_error %({\"a\" : 1}), \"space not allowed between named argument name and ':'\"\n    assert_syntax_error %({\"a\": 1, \"b\" : 2}), \"space not allowed between named argument name and ':'\"\n\n    assert_syntax_error \"case x; when nil; 2; when nil; end\", \"duplicate when nil in case\"\n    assert_syntax_error \"case x; when true; 2; when true; end\", \"duplicate when true in case\"\n    assert_syntax_error \"case x; when 1; 2; when 1; end\", \"duplicate when 1 in case\"\n    assert_syntax_error \"case x; when 'a'; 2; when 'a'; end\", \"duplicate when 'a' in case\"\n    assert_syntax_error %(case x; when \"a\"; 2; when \"a\"; end), %(duplicate when \"a\" in case)\n    assert_syntax_error %(case x; when :a; 2; when :a; end), \"duplicate when :a in case\"\n    assert_syntax_error %(case x; when {1, 2}; 2; when {1, 2}; end), \"duplicate when {1, 2} in case\"\n    assert_syntax_error %(case x; when [1, 2]; 2; when [1, 2]; end), \"duplicate when [1, 2] in case\"\n    assert_syntax_error %(case x; when 1..2; 2; when 1..2; end), \"duplicate when 1..2 in case\"\n    assert_syntax_error %(case x; when /x/; 2; when /x/; end), \"duplicate when /x/ in case\"\n    assert_syntax_error %(case x; when X; 2; when X; end), \"duplicate when X in case\"\n    assert_syntax_error \"case x; when _; end\", \"'when _' is not supported, use 'else' block instead\"\n    assert_syntax_error \"case x; when 1; when _; end\", \"'when _' is not supported, use 'else' block instead\"\n    assert_syntax_error \"case x; when 1, _; end\", \"'when _' is not supported, use 'else' block instead\"\n\n    it_parses \"%w{one  two}\", ([\"one\".string, \"two\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{one\\ntwo}\", ([\"one\".string, \"two\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{one\\ttwo}\", ([\"one\".string, \"two\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{\\n}\", ([] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{one\\\\ two}\", ([\"one two\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{one{} two}\", ([\"one{}\".string, \"two\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{\\\\{one}\", ([\"{one\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%w{one\\\\}}\", ([\"one}\".string] of ASTNode).array_of(Path.global(\"String\"))\n    it_parses \"%i(one\\\\ two)\", ([\"one two\".symbol] of ASTNode).array_of(Path.global(\"Symbol\"))\n    it_parses \"%i{(one two)}\", ([\"(one\".symbol, \"two)\".symbol] of ASTNode).array_of(Path.global(\"Symbol\"))\n    it_parses \"%i((one two))\", ([\"(one\".symbol, \"two)\".symbol] of ASTNode).array_of(Path.global(\"Symbol\"))\n    it_parses \"%i(foo(bar) baz)\", ([\"foo(bar)\".symbol, \"baz\".symbol] of ASTNode).array_of(Path.global(\"Symbol\"))\n    it_parses \"%i{foo\\\\nbar baz}\", ([\"foo\\\\nbar\".symbol, \"baz\".symbol] of ASTNode).array_of(Path.global(\"Symbol\"))\n\n    describe \"literal escape\" do\n      it_parses_literal \"a\\nb\", {\n        \"%q[\" => \"a\\nb\".string,\n        \"%Q[\" => \"a\\nb\".string,\n        \"%[\"  => \"a\\nb\".string,\n        \"\\\"\"  => \"a\\nb\".string,\n        \"%r[\" => regex(\"a\\nb\"),\n        \"/\"   => regex(\"a\\nb\"),\n        \"%x[\" => command(\"a\\nb\"),\n        \"`\"   => command(\"a\\nb\"),\n        \"%w[\" => string_array(\"a\".string, \"b\".string),\n        \"%i[\" => symbol_array(\"a\".symbol, \"b\".symbol),\n        \":\\\"\" => \"a\\nb\".symbol,\n      }\n      it_parses_literal \"a\\tb\", {\n        \"%q[\" => \"a\\tb\".string,\n        \"%Q[\" => \"a\\tb\".string,\n        \"%[\"  => \"a\\tb\".string,\n        \"%r[\" => regex(\"a\\tb\"),\n        \"/\"   => regex(\"a\\tb\"),\n        \"%x[\" => command(\"a\\tb\"),\n        \"`\"   => command(\"a\\tb\"),\n        \"%w[\" => string_array(\"a\".string, \"b\".string),\n        \"%i[\" => symbol_array(\"a\".symbol, \"b\".symbol),\n        \":\\\"\" => \"a\\tb\".symbol,\n      }\n      it_parses_literal \"a\\r\\nb\", {\n        \"%q[\" => \"a\\r\\nb\".string,\n        \"%Q[\" => \"a\\r\\nb\".string,\n        \"%[\"  => \"a\\r\\nb\".string,\n        \"\\\"\"  => \"a\\r\\nb\".string,\n        \"%r[\" => regex(\"a\\r\\nb\"),\n        \"/\"   => regex(\"a\\r\\nb\"),\n        \"%x[\" => command(\"a\\r\\nb\"),\n        \"`\"   => command(\"a\\r\\nb\"),\n        \"%w[\" => string_array(\"a\".string, \"b\".string),\n        \"%i[\" => symbol_array(\"a\".symbol, \"b\".symbol),\n        \":\\\"\" => \"a\\r\\nb\".symbol,\n      }\n      it_parses_literal \"a\\\\nb\", {\n        \"%q[\" => \"a\\\\nb\".string,\n        \"%Q[\" => \"a\\nb\".string,\n        \"%[\"  => \"a\\nb\".string,\n        \"\\\"\"  => \"a\\nb\".string,\n        \"%r[\" => regex(\"a\\\\nb\"),\n        \"/\"   => regex(\"a\\\\nb\"),\n        \"%x[\" => command(\"a\\nb\"),\n        \"`\"   => command(\"a\\nb\"),\n        \"%w[\" => string_array(\"a\\\\nb\".string),\n        \"%i[\" => symbol_array(\"a\\\\nb\".symbol),\n        \":\\\"\" => \"a\\nb\".symbol,\n      }\n      it_parses_literal \"a\\\\tb\", {\n        \"%q[\" => \"a\\\\tb\".string,\n        \"%Q[\" => \"a\\tb\".string,\n        \"%[\"  => \"a\\tb\".string,\n        \"\\\"\"  => \"a\\tb\".string,\n        \"%r[\" => regex(\"a\\\\tb\"),\n        \"/\"   => regex(\"a\\\\tb\"),\n        \"%x[\" => command(\"a\\tb\"),\n        \"`\"   => command(\"a\\tb\"),\n        \"%w[\" => string_array(\"a\\\\tb\".string),\n        \"%i[\" => symbol_array(\"a\\\\tb\".symbol),\n        \":\\\"\" => \"a\\tb\".symbol,\n      }\n      it_parses_literal \"a\\\\rb\", {\n        \"%q[\" => \"a\\\\rb\".string,\n        \"%Q[\" => \"a\\rb\".string,\n        \"%[\"  => \"a\\rb\".string,\n        \"\\\"\"  => \"a\\rb\".string,\n        \"%r[\" => regex(\"a\\\\rb\"),\n        \"/\"   => regex(\"a\\\\rb\"),\n        \"%x[\" => command(\"a\\rb\"),\n        \"`\"   => command(\"a\\rb\"),\n        \"%w[\" => string_array(\"a\\\\rb\".string),\n        \"%i[\" => symbol_array(\"a\\\\rb\".symbol),\n        \":\\\"\" => \"a\\rb\".symbol,\n      }\n      it_parses_literal \"a\\\\\\nb\", {\n        \"%q[\" => \"a\\\\\\nb\".string,\n        \"%Q[\" => \"ab\".string,\n        \"%[\"  => \"ab\".string,\n        \"\\\"\"  => \"ab\".string,\n        \"%r[\" => regex(\"a\\nb\"),\n        \"/\"   => regex(\"a\\nb\"),\n        \"%x[\" => command(\"ab\"),\n        \"`\"   => command(\"ab\"),\n        \"%w[\" => string_array(\"a\\nb\".string),\n        \"%i[\" => symbol_array(\"a\\nb\".symbol),\n        \":\\\"\" => \"a\\nb\".symbol,\n      }\n      it_parses_literal \"a\\\\u{41}b\", {\n        \"%q[\" => \"a\\\\u{41}b\".string,\n        \"%Q[\" => \"aAb\".string,\n        \"%[\"  => \"aAb\".string,\n        \"\\\"\"  => \"aAb\".string,\n        \"%r[\" => \"invalid regex\",\n        \"/\"   => \"invalid regex\",\n        \"%x[\" => command(\"aAb\"),\n        \"`\"   => command(\"aAb\"),\n        \"%w[\" => string_array(\"a\\\\u{41}b\".string),\n        \"%i[\" => symbol_array(\"a\\\\u{41}b\".symbol),\n        \":\\\"\" => \"aAb\".symbol,\n      }\n      it_parses_literal \"a\\\\x41b\", {\n        \"%q[\" => \"a\\\\x41b\".string,\n        \"%Q[\" => \"aAb\".string,\n        \"%[\"  => \"aAb\".string,\n        \"\\\"\"  => \"aAb\".string,\n        \"%r[\" => regex(\"a\\\\x41b\"),\n        \"/\"   => regex(\"a\\\\x41b\"),\n        \"%x[\" => command(\"aAb\"),\n        \"`\"   => command(\"aAb\"),\n        \"%w[\" => string_array(\"a\\\\x41b\".string),\n        \"%i[\" => symbol_array(\"a\\\\x41b\".symbol),\n        \":\\\"\" => \"aAb\".symbol,\n      }\n      it_parses_literal \"a\\\\101b\", {\n        \"%q[\" => \"a\\\\101b\".string,\n        \"%Q[\" => \"aAb\".string,\n        \"%[\"  => \"aAb\".string,\n        \"\\\"\"  => \"aAb\".string,\n        \"%r[\" => regex(\"a\\\\101b\"),\n        \"/\"   => regex(\"a\\\\101b\"),\n        \"%x[\" => command(\"aAb\"),\n        \"`\"   => command(\"aAb\"),\n        \"%w[\" => string_array(\"a\\\\101b\".string),\n        \"%i[\" => symbol_array(\"a\\\\101b\".symbol),\n        \":\\\"\" => \"aAb\".symbol,\n      }\n      it_parses_literal \"a\\#{x}b\", {\n        \"%q[\" => \"a\\#{x}b\".string,\n        \"%Q[\" => StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode),\n        \"%[\"  => StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode),\n        \"\\\"\"  => StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode),\n        \"%r[\" => regex(StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode)),\n        \"/\"   => regex(StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode)),\n        \"%x[\" => command(StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode)),\n        \"`\"   => command(StringInterpolation.new([\"a\".string, Call.new(\"x\"), \"b\".string] of ASTNode)),\n        \"%w[\" => string_array(\"a\\#{x}b\".string),\n        \"%i[\" => symbol_array(\"a\\#{x}b\".symbol),\n        \":\\\"\" => \"a\\#{x}b\".symbol,\n      }\n      it_parses_literal \"a\\\\\\#{x}b\", {\n        \"%q[\" => \"a\\\\\\#{x}b\".string,\n        \"%Q[\" => \"a\\#{x}b\".string,\n        \"%[\"  => \"a\\#{x}b\".string,\n        \"\\\"\"  => \"a\\#{x}b\".string,\n        \"%r[\" => regex(\"a\\\\\\#{x}b\"),\n        \"/\"   => regex(\"a\\\\\\#{x}b\"),\n        \"%x[\" => command(\"a\\#{x}b\"),\n        \"`\"   => command(\"a\\#{x}b\"),\n        \"%w[\" => string_array(\"a\\\\\\#{x}b\".string),\n        \"%i[\" => symbol_array(\"a\\\\\\#{x}b\".symbol),\n        \":\\\"\" => \"a\\#{x}b\".symbol,\n      }\n      it_parses_literal \"a\\\\]b\", {\n        \"%q[\" => %(unexpected token: \"b\"), # ref #5403\n        \"%q{\" => \"a\\\\]b\".string,\n        \"%q|\" => \"a\\\\]b\".string,\n        \"%Q[\" => \"a]b\".string,\n        \"%[\"  => \"a]b\".string,\n        \"\\\"\"  => \"a]b\".string,\n        \"%r[\" => regex(\"a\\\\]b\"),\n        \"/\"   => regex(\"a\\\\]b\"),\n        \"%x[\" => command(\"a]b\"),\n        \"`\"   => command(\"a]b\"),\n        \"%w[\" => string_array(\"a]b\".string),\n        \"%w{\" => string_array(\"a\\\\]b\".string),\n        \"%w|\" => string_array(\"a\\\\]b\".string),\n        \"%i[\" => symbol_array(\"a]b\".symbol),\n        \"%i{\" => symbol_array(\"a\\\\]b\".symbol),\n        \"%i|\" => symbol_array(\"a\\\\]b\".symbol),\n        \":\\\"\" => \"a]b\".symbol,\n      }\n      it_parses_literal \"a\\\\[b\", {\n        \"%q[\" => \"Unterminated string literal\", # ref #5403\n        \"%q{\" => \"a\\\\[b\".string,\n        \"%q|\" => \"a\\\\[b\".string,\n        \"%Q[\" => \"a[b\".string,\n        \"%[\"  => \"a[b\".string,\n        \"\\\"\"  => \"a[b\".string,\n        \"%r[\" => regex(\"a\\\\[b\"),\n        \"/\"   => regex(\"a\\\\[b\"),\n        \"%x[\" => command(\"a[b\"),\n        \"`\"   => command(\"a[b\"),\n        \"%w[\" => string_array(\"a[b\".string),\n        \"%w{\" => string_array(\"a\\\\[b\".string),\n        \"%w|\" => string_array(\"a\\\\[b\".string),\n        \"%i[\" => symbol_array(\"a[b\".symbol),\n        \"%i{\" => symbol_array(\"a\\\\[b\".symbol),\n        \"%i|\" => symbol_array(\"a\\\\[b\".symbol),\n        \":\\\"\" => \"a[b\".symbol,\n      }\n      it_parses_literal \"a\\\\[b\\\\]c\", {\n        \"%q[\" => \"a\\\\[b\\\\]c\".string,\n        \"%Q[\" => \"a[b]c\".string,\n        \"%[\"  => \"a[b]c\".string,\n        \"\\\"\"  => \"a[b]c\".string,\n        \"%r[\" => regex(\"a\\\\[b\\\\]c\"),\n        \"/\"   => regex(\"a\\\\[b\\\\]c\"),\n        \"%x[\" => command(\"a[b]c\"),\n        \"`\"   => command(\"a[b]c\"),\n        \"%w[\" => string_array(\"a[b]c\".string),\n        \"%w{\" => string_array(\"a\\\\[b\\\\]c\".string),\n        \"%w|\" => string_array(\"a\\\\[b\\\\]c\".string),\n        \"%i[\" => symbol_array(\"a[b]c\".symbol),\n        \"%i{\" => symbol_array(\"a\\\\[b\\\\]c\".symbol),\n        \"%i|\" => symbol_array(\"a\\\\[b\\\\]c\".symbol),\n        \":\\\"\" => \"a[b]c\".symbol,\n      }\n      it_parses_literal \"a[b\\\\]c\", {\n        \"%q[\" => \"a[b\\\\]c\".string,\n        \"%Q[\" => \"Unterminated string literal\", # ref #5403\n        \"%Q{\" => \"a[b]c\".string,\n        \"%Q|\" => \"a[b]c\".string,\n        \"%[\"  => \"Unterminated string literal\", # ref #5403\n        \"%{\"  => \"a[b]c\".string,\n        \"%|\"  => \"a[b]c\".string,\n        \"\\\"\"  => \"a[b]c\".string,\n        \"%r[\" => \"Unterminated regular expression\", # ref #5403\n        \"%r{\" => \"invalid regex: missing terminating ] for character class at 6\",\n        \"%r|\" => \"invalid regex: missing terminating ] for character class at 6\",\n        \"/\"   => \"invalid regex: missing terminating ] for character class at 6\",\n        \"%x[\" => \"Unterminated command literal\", # ref #5403\n        \"%x{\" => command(\"a[b]c\".string),\n        \"%x|\" => command(\"a[b]c\".string),\n        \"`\"   => command(\"a[b]c\"),\n        \"%w[\" => \"Unterminated string array literal\", # ref #5403\n        \"%w{\" => string_array(\"a[b\\\\]c\".string),\n        \"%w|\" => string_array(\"a[b\\\\]c\".string),\n        \"%i[\" => \"Unterminated symbol array literal\", # ref #5403\n        \"%i{\" => symbol_array(\"a[b\\\\]c\".symbol),\n        \"%i|\" => symbol_array(\"a[b\\\\]c\".symbol),\n        \":\\\"\" => \"a[b]c\".symbol,\n      }\n      it_parses_literal \"a\\\\\\\\ b\", {\n        \"%q[\" => \"a\\\\\\\\ b\".string,\n        \"%Q[\" => \"a\\\\ b\".string,\n        \"%[\"  => \"a\\\\ b\".string,\n        \"\\\"\"  => \"a\\\\ b\".string,\n        \"%r[\" => regex(\"a\\\\\\\\ b\"),\n        \"/\"   => regex(\"a\\\\\\\\ b\"),\n        \"%x[\" => command(\"a\\\\ b\"),\n        \"`\"   => command(\"a\\\\ b\"),\n        \"%w[\" => string_array(\"a\\\\ b\".string),\n        \"%i[\" => symbol_array(\"a\\\\ b\".symbol),\n        \":\\\"\" => \"a\\\\ b\".symbol,\n      }\n      it_parses_literal \"\\\\\\\\a\", {\n        \"%q[\" => \"\\\\\\\\a\".string,\n        \"%Q[\" => \"\\\\a\".string,\n        \"%[\"  => \"\\\\a\".string,\n        \"\\\"\"  => \"\\\\a\".string,\n        \"%r[\" => regex(\"\\\\\\\\a\"),\n        \"/\"   => regex(\"\\\\\\\\a\"),\n        \"%x[\" => command(\"\\\\a\"),\n        \"`\"   => command(\"\\\\a\"),\n        \"%w[\" => string_array(\"\\\\\\\\a\".string),\n        \"%i[\" => symbol_array(\"\\\\\\\\a\".symbol),\n        \":\\\"\" => \"\\\\a\".symbol,\n      }\n      it_parses_literal \"\\\\\", {\n        \"%q[\" => \"\\\\\".string,\n        \"%Q[\" => \"Unterminated string literal\",\n        \"%[\"  => \"Unterminated string literal\",\n        \"\\\"\"  => \"Unterminated string literal\",\n        \"%r[\" => \"Unterminated regular expression\",\n        \"/\"   => \"Unterminated regular expression\",\n        \"%x[\" => \"Unterminated command literal\",\n        \"`\"   => \"Unterminated command literal\",\n        \"%w[\" => \"Unterminated string array literal\",\n        \"%i[\" => \"Unterminated symbol array literal\",\n        \":\\\"\" => \"unterminated quoted symbol\",\n      }\n      it_parses_literal \"\\\\\\\\\", {\n        \"%q[\" => \"\\\\\\\\\".string,\n        \"%Q[\" => \"\\\\\".string,\n        \"%[\"  => \"\\\\\".string,\n        \"\\\"\"  => \"\\\\\".string,\n        \"%r[\" => regex(\"\\\\\\\\\"),\n        \"/\"   => regex(\"\\\\\\\\\"),\n        \"%x[\" => command(\"\\\\\"),\n        \"`\"   => command(\"\\\\\"),\n        \"%w[\" => \"Unterminated string array literal\", # FIXME: #12277\n        \"%i[\" => \"Unterminated symbol array literal\", # FIXME: #12277\n        \":\\\"\" => \"\\\\\".symbol,\n      }\n      it_parses_literal \"\\\\\\\\\\\\\", {\n        \"%q[\" => \"\\\\\\\\\\\\\".string,\n        \"%Q[\" => \"Unterminated string literal\",\n        \"%[\"  => \"Unterminated string literal\",\n        \"\\\"\"  => \"Unterminated string literal\",\n        \"%r[\" => \"Unterminated regular expression\",\n        \"/\"   => \"Unterminated regular expression\",\n        \"%x[\" => \"Unterminated command literal\",\n        \"`\"   => \"Unterminated command literal\",\n        \"%w[\" => \"Unterminated string array literal\", # FIXME: #12277\n        \"%i[\" => \"Unterminated symbol array literal\", # FIXME: #12277\n        \":\\\"\" => \"unterminated quoted symbol\",\n      }\n    end\n\n    assert_syntax_error \"%w(\", \"Unterminated string array literal\"\n    assert_syntax_error \"%w{one}}\", \"expecting token 'EOF', not '}'\"\n    assert_syntax_error \"%w{{one}\", \"Unterminated string array literal\"\n    assert_syntax_error \"%i(\", \"Unterminated symbol array literal\"\n    assert_syntax_error \"%i{one}}\", \"expecting token 'EOF', not '}'\"\n    assert_syntax_error \"%i{{one}\", \"Unterminated symbol array literal\"\n    assert_syntax_error \"%x(\", \"Unterminated command literal\"\n    assert_syntax_error \"%r(\", \"Unterminated regular expression\"\n    assert_syntax_error \"%q(\", \"Unterminated string literal\"\n    assert_syntax_error \"%Q(\", \"Unterminated string literal\"\n    assert_syntax_error \"<<-HEREDOC\", \"Unexpected EOF on heredoc identifier\"\n    assert_syntax_error \"<<-HEREDOC\\n\", \"Unterminated heredoc\"\n    assert_syntax_error \"<<-'HEREDOC'\", \"Unexpected EOF on heredoc identifier\"\n    assert_syntax_error \"<<-'HEREDOC'\\n\", \"Unterminated heredoc\"\n\n    assert_syntax_error \"[1\\n,2]\", \"expecting token ']', not ','\"\n    assert_syntax_error \"{1\\n,2}\", \"expecting token '}', not ','\"\n    assert_syntax_error \"{1, 2\\n,3}\", \"expecting token '}', not ','\"\n    assert_syntax_error \"{1 => 2\\n,3 => 4}\", \"expecting token '}', not ','\"\n    assert_syntax_error \"foo(1\\n,2)\", \"expecting token ')', not ','\"\n    assert_syntax_error \"foo(a: 1\\n,b: 2)\", \"expecting token ')', not ','\"\n    assert_syntax_error \"def foo(x\\n,y); 1; end\", \"expecting token ')', not ','\"\n    assert_syntax_error \"macro foo(x\\n,y); 1; end\", \"expecting token ')', not ','\"\n    assert_syntax_error \"class Foo(X\\n,Y); 1; end\", \"expecting token ')', not ','\"\n    assert_syntax_error \"Foo(X\\n,Y)\", \"expecting token ')', not ','\"\n    assert_syntax_error \"Foo(x: X\\n,y: Y)\", \"expecting token ')', not ','\"\n\n    it_parses \"annotation Foo; end\", AnnotationDef.new(\"Foo\".path)\n    it_parses \"annotation Foo\\n\\nend\", AnnotationDef.new(\"Foo\".path)\n    it_parses \"annotation Foo::Bar\\n\\nend\", AnnotationDef.new(Path.new(\"Foo\", \"Bar\"))\n\n    it_parses %(annotation Foo\\nend\\nrequire \"bar\"), [AnnotationDef.new(\"Foo\".path), Require.new(\"bar\")]\n\n    assert_syntax_error \"def foo(x : *Int32); end\", \"invalid type splat\"\n    assert_syntax_error \"def foo(x : (*Int32)); end\", \"invalid type splat\"\n    assert_syntax_error \"def foo(x : Int32, Int32); end\"\n    assert_syntax_error \"def foo(x : (Int32, Int32)); end\"\n    assert_syntax_error \"def foo(x : (Int32, Int32) | Int32); end\"\n    assert_syntax_error \"def foo(x : Int32 | (Int32, Int32)); end\"\n    assert_syntax_error \"def foo(x : {Int32, (Int32, Int32)}); end\"\n    assert_syntax_error \"def foo(x : 1); end\"\n    assert_syntax_error \"def foo(x : {sizeof(Int32), 2}); end\"\n    assert_syntax_error \"def foo(x : Array({sizeof(Int32), 2})); end\"\n\n    it \"gets corrects of ~\" do\n      node = Parser.parse(\"\\n  ~1\")\n      loc = node.location.not_nil!\n      loc.line_number.should eq(2)\n      loc.column_number.should eq(3)\n    end\n\n    it \"gets corrects end location for var\" do\n      parser = Parser.new(\"foo = 1\\nfoo; 1\")\n      node = parser.parse.as(Expressions).expressions[1]\n      end_loc = node.end_location.not_nil!\n      end_loc.line_number.should eq(2)\n      end_loc.column_number.should eq(3)\n    end\n\n    it \"gets corrects end location for var + var\" do\n      parser = Parser.new(\"foo = 1\\nfoo + nfoo; 1\")\n      node = parser.parse.as(Expressions).expressions[1].as(Call).obj.as(Var)\n      end_loc = node.end_location.not_nil!\n      end_loc.line_number.should eq(2)\n      end_loc.column_number.should eq(3)\n    end\n\n    it \"gets corrects end location for block with { ... }\" do\n      parser = Parser.new(\"foo { 1 + 2 }; 1\")\n      node = parser.parse.as(Expressions).expressions[0].as(Call)\n      block = node.block.not_nil!\n      end_loc = block.end_location.not_nil!\n      end_loc.line_number.should eq(1)\n      end_loc.column_number.should eq(13)\n      node.end_location.should eq(end_loc)\n    end\n\n    it \"gets corrects end location for block with do ... end\" do\n      parser = Parser.new(\"foo do\\n  1 + 2\\nend; 1\")\n      node = parser.parse.as(Expressions).expressions[0].as(Call)\n      block = node.block.not_nil!\n      end_loc = block.end_location.not_nil!\n      end_loc.line_number.should eq(3)\n      end_loc.column_number.should eq(3)\n      node.end_location.should eq(end_loc)\n    end\n\n    it \"gets correct location after macro with yield\" do\n      parser = Parser.new(<<-CRYSTAL)\n        macro foo\n          yield\n        end\n\n        1 + 'a'\n        CRYSTAL\n      node = parser.parse.as(Expressions).expressions[1]\n      loc = node.location.not_nil!\n      loc.line_number.should eq(5)\n    end\n\n    it \"gets correct location with \\r\\n (#1558)\" do\n      nodes = Parser.parse(\"class Foo\\r\\nend\\r\\n\\r\\n1\").as(Expressions)\n      loc = nodes.last.location.not_nil!\n      loc.line_number.should eq(4)\n      loc.column_number.should eq(1)\n    end\n\n    it \"sets location of enum method\" do\n      parser = Parser.new(\"enum Foo; A; def bar; end; end\")\n      node = parser.parse.as(EnumDef).members[1].as(Def)\n      loc = node.location.not_nil!\n      loc.line_number.should eq(1)\n      loc.column_number.should eq(14)\n    end\n\n    it \"gets correct location after macro with yield\" do\n      parser = Parser.new(%(\\n  1 ? 2 : 3))\n      node = parser.parse\n      loc = node.location.not_nil!\n      loc.line_number.should eq(2)\n      loc.column_number.should eq(3)\n    end\n\n    it \"gets correct location of empty exception handler inside def\" do\n      parser = Parser.new(\"def foo\\nensure\\nend\")\n      node = parser.parse.as(Def).body\n      loc = node.location.not_nil!\n      loc.line_number.should eq(2)\n    end\n\n    it \"sets location of +=\" do\n      parser = Parser.new(\"a = 1; a += 2\")\n      node = parser.parse.as(Expressions).expressions[1]\n\n      node.name_location.should_not be_nil\n      name_location = node.name_location.not_nil!\n\n      name_location.line_number.should eq(1)\n      name_location.column_number.should eq(10)\n    end\n\n    it \"sets location of obj.x += as call\" do\n      parser = Parser.new(\"a = 1; a.x += 2\")\n      node = parser.parse.as(Expressions).expressions[1]\n\n      node.name_location.should_not be_nil\n      name_location = node.name_location.not_nil!\n\n      name_location.line_number.should eq(1)\n      name_location.column_number.should eq(12)\n    end\n\n    it \"sets location of top-level fun name\" do\n      parser = Parser.new(\"fun foo; end\")\n      node = parser.parse.as(FunDef)\n\n      name_location = node.name_location.should_not be_nil\n      name_location.line_number.should eq(1)\n      name_location.column_number.should eq(5)\n    end\n\n    it \"sets location of lib fun name\" do\n      parser = Parser.new(\"lib Foo; fun foo; end\")\n      node = parser.parse.as(LibDef).body.as(FunDef)\n\n      name_location = node.name_location.should_not be_nil\n      name_location.line_number.should eq(1)\n      name_location.column_number.should eq(14)\n    end\n\n    it \"sets correct location of proc literal\" do\n      parser = Parser.new(\"->(\\n  x : Int32,\\n  y : String\\n) { }\")\n      node = parser.parse.as(ProcLiteral)\n      loc = node.location.not_nil!\n      loc.line_number.should eq(1)\n      loc.column_number.should eq(1)\n    end\n\n    it \"sets correct location of `else` of if statement\" do\n      parser = Parser.new(\"if foo\\nelse\\nend\")\n      node = parser.parse.as(If)\n      node.location.not_nil!.line_number.should eq(1)\n      node.else_location.not_nil!.line_number.should eq(2)\n      node.end_location.not_nil!.line_number.should eq(3)\n\n      parser = Parser.new(\"if foo\\nend\")\n      node = parser.parse.as(If)\n      node.location.not_nil!.line_number.should eq(1)\n      node.else_location.should be_nil\n      node.end_location.not_nil!.line_number.should eq(2)\n    end\n\n    it \"sets correct location of `elsif` of if statement\" do\n      parser = Parser.new(\"if foo\\nelsif bar\\nend\")\n      node = parser.parse.as(If)\n      node.location.not_nil!.line_number.should eq(1)\n      node.else_location.not_nil!.line_number.should eq(2)\n      node.end_location.not_nil!.line_number.should eq(3)\n    end\n\n    it \"sets correct location of `else` of unless statement\" do\n      parser = Parser.new(\"unless foo\\nelse\\nend\")\n      node = parser.parse.as(Unless)\n      node.location.not_nil!.line_number.should eq(1)\n      node.else_location.not_nil!.line_number.should eq(2)\n      node.end_location.not_nil!.line_number.should eq(3)\n    end\n\n    it \"sets correct location and end location of `begin` block\" do\n      parser = Parser.new(\"begin\\nfoo\\nend\")\n      node = parser.parse.as(Expressions)\n      node.location.not_nil!.line_number.should eq(1)\n      node.end_location.not_nil!.line_number.should eq(3)\n    end\n\n    it \"sets correct location and end location of parenthesized empty block\" do\n      parser = Parser.new(\"()\")\n      node = parser.parse.as(Expressions)\n      node.location.not_nil!.column_number.should eq(1)\n      node.end_location.not_nil!.column_number.should eq(2)\n    end\n\n    it \"sets correct location and end location of parenthesized block\" do\n      parser = Parser.new(\"(foo; bar)\")\n      node = parser.parse.as(Expressions)\n      node.location.not_nil!.column_number.should eq(1)\n      node.end_location.not_nil!.column_number.should eq(10)\n    end\n\n    it \"sets correct locations of keywords of exception handler\" do\n      parser = Parser.new(\"begin\\nrescue\\nelse\\nensure\\nend\")\n      node = parser.parse.as(ExceptionHandler)\n      node.location.not_nil!.line_number.should eq(1)\n      node.rescues.not_nil!.first.location.not_nil!.line_number.should eq(2)\n      node.else_location.not_nil!.line_number.should eq(3)\n      node.ensure_location.not_nil!.line_number.should eq(4)\n      node.end_location.not_nil!.line_number.should eq(5)\n    end\n\n    it \"sets correct locations of macro if / else\" do\n      parser = Parser.new(<<-CRYSTAL)\n        {% if 1 == val %}\n          \"one!\"\n          \"bar\"\n        {% else %}\n          \"not one\"\n          \"bar\"\n        {% end %}\n        CRYSTAL\n\n      node = parser.parse.as MacroIf\n\n      location = node.cond.location.should_not be_nil\n      location.line_number.should eq 1\n      location = node.cond.end_location.should_not be_nil\n      location.line_number.should eq 1\n\n      location = node.then.location.should_not be_nil\n      location.line_number.should eq 1\n      location = node.then.end_location.should_not be_nil\n      location.line_number.should eq 4\n\n      location = node.else.location.should_not be_nil\n      location.line_number.should eq 4\n      location = node.else.end_location.should_not be_nil\n      location.line_number.should eq 7\n    end\n\n    it \"sets correct locations of macro if / elsif\" do\n      parser = Parser.new(<<-CRYSTAL)\n        {% if 1 == val %}\n          \"one!\"\n          \"bar\"\n        {% elsif 2 == val %}\n          \"not one\"\n          \"bar\"\n        {% end %}\n        CRYSTAL\n\n      node = parser.parse.as MacroIf\n\n      location = node.cond.location.should_not be_nil\n      location.line_number.should eq 1\n      location = node.cond.end_location.should_not be_nil\n      location.line_number.should eq 1\n\n      location = node.then.location.should_not be_nil\n      location.line_number.should eq 1\n      location = node.then.end_location.should_not be_nil\n      location.line_number.should eq 4\n\n      location = node.else.location.should_not be_nil\n      location.line_number.should eq 4\n      location = node.else.end_location.should_not be_nil\n      location.line_number.should eq 7\n    end\n\n    it \"sets correct locations of macro if / else / elsif\" do\n      parser = Parser.new(<<-CRYSTAL)\n        {% if 1 == val %}\n          \"one!\"\n          \"bar\"\n        {% elsif 2 == val %}\n          \"not one\"\n          \"bar\"\n        {% else %}\n          \"biz\"\n          \"blah\"\n        {% end %}\n        CRYSTAL\n\n      node = parser.parse.as MacroIf\n\n      location = node.cond.location.should_not be_nil\n      location.line_number.should eq 1\n      location = node.cond.end_location.should_not be_nil\n      location.line_number.should eq 1\n\n      location = node.then.location.should_not be_nil\n      location.line_number.should eq 1\n      location = node.then.end_location.should_not be_nil\n      location.line_number.should eq 4\n\n      location = node.else.location.should_not be_nil\n      location.line_number.should eq 4\n      location = node.else.end_location.should_not be_nil\n      location.line_number.should eq 10\n    end\n\n    it \"sets the correct location for MacroExpressions in a MacroIf\" do\n      parser = Parser.new(<<-CRYSTAL)\n        {% if 1 == 2 %}\n          {{2 * 2}}\n        {% else %}\n           {%\n             1 + 1\n             2 + 2\n           %}\n        {% end %}\n        CRYSTAL\n\n      node = parser.parse.should be_a MacroIf\n      location = node.location.should_not be_nil\n      location.line_number.should eq 1\n      location.column_number.should eq 1\n\n      then_node = node.then.should be_a Expressions\n      then_node_location = then_node.location.should_not be_nil\n      then_node_location.line_number.should eq 1\n      then_node_location = then_node.end_location.should_not be_nil\n      then_node_location.line_number.should eq 3\n\n      then_node_location = then_node.expressions[1].location.should_not be_nil\n      then_node_location.line_number.should eq 2\n      then_node_location.column_number.should eq 3\n      then_node_location = then_node.expressions[1].end_location.should_not be_nil\n      then_node_location.line_number.should eq 2\n      then_node_location.column_number.should eq 11\n\n      else_node = node.else.should be_a Expressions\n      else_node_location = else_node.location.should_not be_nil\n      else_node_location.line_number.should eq 3\n      else_node_location = else_node.end_location.should_not be_nil\n      else_node_location.line_number.should eq 8\n\n      else_node = node.else.should be_a Expressions\n      else_node_location = else_node.expressions[1].location.should_not be_nil\n      else_node_location.line_number.should eq 4\n      else_node_location = else_node.expressions[1].end_location.should_not be_nil\n      else_node_location.line_number.should eq 7\n    end\n\n    it \"sets correct location of Begin within another node\" do\n      parser = Parser.new(<<-CRYSTAL)\n        macro finished\n          {% begin %}\n            {{2 * 2}}\n             {%\n               1 + 1\n               2 + 2\n             %}\n          {% end %}\n        end\n        CRYSTAL\n\n      node = parser.parse.should be_a Macro\n      node = node.body.should be_a Expressions\n      node = node.expressions[1].should be_a MacroIf\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 2\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 8\n    end\n\n    it \"sets correct location of MacroIf within another node\" do\n      parser = Parser.new(<<-CRYSTAL)\n        macro finished\n          {% if false %}\n            {{2 * 2}}\n             {%\n               1 + 1\n               2 + 2\n             %}\n          {% end %}\n        end\n        CRYSTAL\n\n      node = parser.parse.should be_a Macro\n      node = node.body.should be_a Expressions\n      node = node.expressions[1].should be_a MacroIf\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 2\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 8\n    end\n\n    it \"sets correct location of MacroIf (unless) within another node\" do\n      parser = Parser.new(<<-CRYSTAL)\n        macro finished\n          {% unless false %}\n            {{2 * 2}}\n             {%\n               1 + 1\n               2 + 2\n             %}\n          {% end %}\n        end\n        CRYSTAL\n\n      node = parser.parse.should be_a Macro\n      node = node.body.should be_a Expressions\n      node = node.expressions[1].should be_a MacroIf\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 2\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 8\n    end\n\n    it \"sets correct location for output macro expression in for loop\" do\n      parser = Parser.new(<<-CRYSTAL)\n        {% for foo in bar %}\n          {{ if true\n                foo\n                bar\n              end }}\n        {% end %}\n        CRYSTAL\n\n      node = parser.parse.should be_a MacroFor\n      node = node.body.should be_a Expressions\n\n      node = node.expressions[1].should be_a MacroExpression\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 2\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 5\n\n      node = node.exp.should be_a If\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 2\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 5\n    end\n\n    it \"sets correct location for single node within another node\" do\n      parser = Parser.new(<<-CRYSTAL)\n        macro finished\n          {% verbatim do %}\n            {%\n\n              a = 1 %}\n          {% end %}\n        end\n        CRYSTAL\n\n      node = parser.parse.should be_a Macro\n      node = node.body.should be_a Expressions\n      node = node.expressions[1].should be_a MacroVerbatim\n      node = node.exp.should be_a Expressions\n      node = node.expressions[1].should be_a MacroExpression\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 3\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 5\n\n      assign = node.exp.should be_a Assign\n\n      location = assign.location.should_not be_nil\n      location.line_number.should eq 5\n      location = assign.end_location.should_not be_nil\n      location.line_number.should eq 5\n\n      target = assign.target.should be_a Var\n\n      location = target.location.should_not be_nil\n      location.line_number.should eq 5\n      location = target.end_location.should_not be_nil\n      location.line_number.should eq 5\n\n      value = assign.value.should be_a NumberLiteral\n\n      location = value.location.should_not be_nil\n      location.line_number.should eq 5\n      location = value.end_location.should_not be_nil\n      location.line_number.should eq 5\n    end\n\n    it \"sets correct location for multiple nodes within another node\" do\n      parser = Parser.new(<<-CRYSTAL)\n        macro finished\n          {% verbatim do %}\n            {%\n\n\n              a = 1\n              b = 2 %}\n          {% end %}\n        end\n        CRYSTAL\n\n      node = parser.parse.should be_a Macro\n      node = node.body.should be_a Expressions\n      node = node.expressions[1].should be_a MacroVerbatim\n      node = node.exp.should be_a Expressions\n      node = node.expressions[1].should be_a MacroExpression\n\n      location = node.location.should_not be_nil\n      location.line_number.should eq 3\n      location = node.end_location.should_not be_nil\n      location.line_number.should eq 7\n\n      node = node.exp.should be_a Expressions\n      assign = node.expressions[0].should be_a Assign\n\n      location = assign.location.should_not be_nil\n      location.line_number.should eq 6\n      location = assign.end_location.should_not be_nil\n      location.line_number.should eq 6\n\n      target = assign.target.should be_a Var\n\n      location = target.location.should_not be_nil\n      location.line_number.should eq 6\n      location = target.end_location.should_not be_nil\n      location.line_number.should eq 6\n\n      value = assign.value.should be_a NumberLiteral\n\n      location = value.location.should_not be_nil\n      location.line_number.should eq 6\n      location = value.end_location.should_not be_nil\n      location.line_number.should eq 6\n\n      assign = node.expressions[1].should be_a Assign\n\n      location = assign.location.should_not be_nil\n      location.line_number.should eq 7\n      location = assign.end_location.should_not be_nil\n      location.line_number.should eq 7\n\n      target = assign.target.should be_a Var\n\n      location = target.location.should_not be_nil\n      location.line_number.should eq 7\n      location = target.end_location.should_not be_nil\n      location.line_number.should eq 7\n\n      value = assign.value.should be_a NumberLiteral\n\n      location = value.location.should_not be_nil\n      location.line_number.should eq 7\n      location = value.end_location.should_not be_nil\n      location.line_number.should eq 7\n    end\n\n    it \"sets correct locations of MacroVar in MacroIf / else\" do\n      parser = Parser.new(<<-CRYSTAL)\n        {% if true %}\n          %a = {{ 1 + 1 }}\n        {% else %}\n          %b = {{ 2 + 2 }}\n        {% end %}\n        CRYSTAL\n\n      node = parser.parse.should be_a MacroIf\n\n      assert_location node.cond, 1, 1\n\n      then_node = node.then.should be_a Expressions\n      then_node = then_node.expressions[1].should be_a MacroVar\n      assert_location then_node, 2, 2\n\n      else_node = node.else.should be_a Expressions\n      else_node = else_node.expressions[1].should be_a MacroVar\n      assert_location else_node, 4, 4\n    end\n\n    it \"sets correct location of trailing ensure\" do\n      parser = Parser.new(\"foo ensure bar\")\n      node = parser.parse.as(ExceptionHandler)\n      ensure_location = node.ensure_location.not_nil!\n      ensure_location.line_number.should eq(1)\n      ensure_location.column_number.should eq(5)\n    end\n\n    it \"sets correct location of trailing rescue\" do\n      source = \"foo rescue bar\"\n      parser = Parser.new(source)\n      node = parser.parse.as(ExceptionHandler).rescues.not_nil![0]\n      node_source(source, node).should eq(\"rescue bar\")\n    end\n\n    it \"sets correct location of call name\" do\n      source = \"foo(bar)\"\n      node = Parser.new(source).parse.as(Call)\n      source_between(source, node.name_location, node.name_end_location).should eq(\"foo\")\n    end\n\n    it \"sets correct location of call name in operator assignment\" do\n      source = \"@foo.bar += 1\"\n      node = Parser.parse(source).as(OpAssign).target.as(Call)\n      source_between(source, node.name_location, node.name_end_location).should eq(\"bar\")\n    end\n\n    it \"sets correct location of element in array literal\" do\n      source = \"%i(foo bar)\"\n      elements = Parser.new(source).parse.as(ArrayLiteral).elements\n      node_source(source, elements[0]).should eq(\"foo\")\n      node_source(source, elements[1]).should eq(\"bar\")\n    end\n\n    it \"sets correct location of implicit tuple literal of multi-return\" do\n      source = \"def foo; return 1, 2; end\"\n      node = Parser.new(source).parse.as(Def).body.as(Return).exp.not_nil!\n      node_source(source, node).should eq(\"1, 2\")\n    end\n\n    it \"sets correct location of var in type declaration\" do\n      source = \"foo : Int32\"\n      node = Parser.new(source).parse.as(TypeDeclaration).var\n      node_source(source, node).should eq(\"foo\")\n\n      source = \"begin : Int32\"\n      node = Parser.new(source).parse.as(TypeDeclaration).var\n      node_source(source, node).should eq(\"begin\")\n    end\n\n    it \"sets correct location of var in proc pointer\" do\n      source = \"foo : Foo; ->foo.bar\"\n      expressions = Parser.new(source).parse.as(Expressions).expressions\n      node = expressions[1].as(ProcPointer).obj.not_nil!\n      node_source(source, node).should eq(\"foo\")\n    end\n\n    it \"sets correct location of var in macro for loop\" do\n      source = \"{% for foo, bar in baz %} {% end %}\"\n      node = Parser.new(source).parse.as(MacroFor)\n      node_source(source, node.vars[0]).should eq(\"foo\")\n      node_source(source, node.vars[1]).should eq(\"bar\")\n    end\n\n    it \"sets correct location of receiver var in method def\" do\n      source = \"def foo.bar; end\"\n      node = Parser.new(source).parse.as(Def).receiver.not_nil!\n      node_source(source, node).should eq(\"foo\")\n    end\n\n    it \"sets correct location of vars in C struct\" do\n      source = \"lib Foo; struct Bar; fizz, buzz : Int32; end; end\"\n      expressions = Parser.new(source).parse.as(LibDef).body.as(CStructOrUnionDef).body.as(Expressions).expressions\n      node_source(source, expressions[0].as(TypeDeclaration).var).should eq(\"fizz\")\n      node_source(source, expressions[1].as(TypeDeclaration).var).should eq(\"buzz\")\n    end\n\n    it \"sets correct location of a parenthesized union\" do\n      source = \"foo : (String | Nil) | Foo\"\n      node = Parser.new(source).parse.as(TypeDeclaration)\n\n      union = node.declared_type.should be_a Union\n      node_source(source, union)\n        .should eq(\"(String | Nil) | Foo\")\n\n      inner_union = union.types.first.should be_a Union\n      node_source(source, inner_union)\n        .should eq(\"(String | Nil)\")\n    end\n\n    it \"doesn't override yield with macro yield\" do\n      parser = Parser.new(\"def foo; yield 1; {% begin %} yield 1 {% end %}; end\")\n      a_def = parser.parse.as(Def)\n      a_def.block_arity.should eq(1)\n    end\n\n    it \"correctly computes line number after `\\\\{%\\n` (#9857)\" do\n      code = <<-CRYSTAL\n      macro foo\n        \\\\{%\n          1\n        %}\n      end\n\n      1\n      CRYSTAL\n\n      exps = Parser.parse(code).as(Expressions)\n      exps.expressions[1].location.not_nil!.line_number.should eq(7)\n    end\n\n    it \"sets correct location for fun def\" do\n      source = \"lib LibFoo; fun foo(x : Int32); end\"\n      node = Parser.new(source).parse.as(LibDef).body\n\n      node_source(source, node).should eq(\"fun foo(x : Int32)\")\n    end\n\n    it \"sets correct location for fun def with return type\" do\n      source = \"lib LibFoo; fun foo(x : Int32) : Void; end\"\n      node = Parser.new(source).parse.as(LibDef).body\n\n      node_source(source, node).should eq(\"fun foo(x : Int32) : Void\")\n    end\n\n    it \"sets correct location for fun def on multiple lines\" do\n      source = \"lib LibFoo\\nfun foo(\\n    x : Int32\\n  )\\nend\"\n      node = Parser.new(source).parse.as(LibDef).body\n\n      node_source(source, node).should eq(\"fun foo(\\n    x : Int32\\n  )\")\n    end\n\n    it \"sets correct location for fun def with body\" do\n      source = \"fun foo(x : Int32) : Void\\nend\"\n      node = Parser.new(source).parse.as(FunDef)\n\n      node_source(source, node).should eq(\"fun foo(x : Int32) : Void\\nend\")\n    end\n\n    it \"sets correct location of parameter in proc literal\" do\n      source = \"->(foo : Bar, baz) { }\"\n      args = Parser.parse(source).as(ProcLiteral).def.args\n      node_source(source, args[0]).should eq(\"foo : Bar\")\n      node_source(source, args[1]).should eq(\"baz\")\n    end\n\n    it \"sets correct location of splat in multiple assignment\" do\n      source = \"*foo, bar = 1, 2\"\n      node = Parser.parse(source).as(MultiAssign).targets[0]\n      node_source(source, node).should eq(\"*foo\")\n\n      source = \"foo, *bar = 1, 2\"\n      node = Parser.parse(source).as(MultiAssign).targets[1]\n      node_source(source, node).should eq(\"*bar\")\n    end\n\n    it \"sets correct location of tuple type\" do\n      source = \"x : {Foo, Bar}\"\n      node = Parser.parse(source).as(TypeDeclaration).declared_type\n      node_source(source, node).should eq(\"{Foo, Bar}\")\n    end\n\n    it \"sets correct location of named tuple type\" do\n      source = \"x : {foo: Bar}\"\n      node = Parser.parse(source).as(TypeDeclaration).declared_type\n      node_source(source, node).should eq(\"{foo: Bar}\")\n    end\n\n    it \"sets correct location of argument in named tuple type\" do\n      source = \"x : {foo: Bar}\"\n      node = Parser.parse(source).as(TypeDeclaration).declared_type.as(Generic).named_args.not_nil!.first\n      node_source(source, node).should eq(\"foo: Bar\")\n    end\n\n    it \"sets correct location of instance variable in proc pointer\" do\n      source = \"->@foo.x\"\n      node = Parser.parse(source).as(ProcPointer).obj.not_nil!\n      node_source(source, node).should eq(\"@foo\")\n    end\n\n    it \"sets correct location of instance variable in proc pointer\" do\n      source = \"->@@foo.x\"\n      node = Parser.parse(source).as(ProcPointer).obj.not_nil!\n      node_source(source, node).should eq(\"@@foo\")\n    end\n\n    it \"sets correct location of annotation on method parameter\" do\n      source = \"def x(@[Foo] y) end\"\n      node = Parser.parse(source).as(Def).args.first.parsed_annotations.not_nil!.first\n      node_source(source, node).should eq(\"@[Foo]\")\n    end\n\n    it \"sets correct location of annotation in lib\" do\n      source = \"lib X; @[Foo]; end\"\n      node = Parser.parse(source).as(LibDef).body\n      node_source(source, node).should eq(\"@[Foo]\")\n    end\n\n    it \"sets correct location of annotation in enum\" do\n      source = \"enum X; @[Foo]; end\"\n      node = Parser.parse(source).as(EnumDef).members.first\n      node_source(source, node).should eq(\"@[Foo]\")\n    end\n\n    it \"sets correct location of private method in enum\" do\n      source = \"enum X; private def foo; end; end\"\n      node = Parser.parse(source).as(EnumDef).members.first\n      node_source(source, node).should eq(\"private def foo; end\")\n    end\n\n    it \"sets correct location of protected macro in enum\" do\n      source = \"enum X; protected macro foo; end; end\"\n      node = Parser.parse(source).as(EnumDef).members.first\n      node_source(source, node).should eq(\"protected macro foo; end\")\n    end\n\n    it \"sets correct location of global path in class def\" do\n      source = \"class ::Foo; end\"\n      node = Parser.parse(source).as(ClassDef).name\n      node_source(source, node).should eq(\"::Foo\")\n    end\n\n    it \"sets correct location of global path in annotation\" do\n      source = \"@[::Foo]\"\n      node = Parser.parse(source).as(Annotation).path\n      node_source(source, node).should eq(\"::Foo\")\n    end\n\n    it \"sets args_in_brackets to false for `a.b`\" do\n      parser = Parser.new(\"a.b\")\n      node = parser.parse.as(Call)\n      node.args_in_brackets?.should be_false\n    end\n\n    it \"sets args_in_brackets to true for `a[b]`\" do\n      parser = Parser.new(\"a[b]\")\n      node = parser.parse.as(Call)\n      node.args_in_brackets?.should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/parser/to_s_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def expect_to_s(original, expected = original, emit_doc = false, file = __FILE__, line = __LINE__, focus = false)\n  it \"does to_s of #{original.inspect}\", file, line, focus: focus do\n    str = IO::Memory.new expected.bytesize\n\n    source = original\n    if source.is_a?(String)\n      parser = Parser.new source\n      parser.wants_doc = emit_doc\n      node = parser.parse\n      node.to_s(str, emit_doc: emit_doc)\n      str.to_s.should eq(expected), file: file, line: line\n\n      # Check keeping information for `to_s` on clone\n      cloned = node.clone\n      str.clear\n      cloned.to_s(str, emit_doc: emit_doc)\n      str.to_s.should eq(expected), file: file, line: line\n    else\n      source.to_s.should eq(expected), file: file, line: line\n    end\n  end\nend\n\ndescribe \"ASTNode#to_s\" do\n  expect_to_s \"([] of T).foo\"\n  expect_to_s \"({} of K => V).foo\"\n  expect_to_s \"foo(bar)\"\n  expect_to_s \"(~1).foo\"\n  expect_to_s \"1 && (a = 2)\"\n  expect_to_s \"(a = 2) && 1\"\n  expect_to_s \"foo(a.as(Int32))\"\n  expect_to_s \"(1 + 2).as(Int32)\", \"(1 + 2).as(Int32)\"\n  expect_to_s \"a.as?(Int32)\"\n  expect_to_s \"(1 + 2).as?(Int32)\", \"(1 + 2).as?(Int32)\"\n  expect_to_s \"@foo.bar\"\n  expect_to_s %(:foo)\n  expect_to_s %(:\"{\")\n  expect_to_s %(%r())\n  expect_to_s %(%r()imx)\n  expect_to_s %(/hello world/)\n  expect_to_s %(/hello world/imx)\n  expect_to_s %(/\\\\s/)\n  expect_to_s %(/\\\\?/)\n  expect_to_s %(/\\\\(group\\\\)/)\n  expect_to_s %(/\\\\//), \"/\\\\//\"\n  expect_to_s %(/\\#{1 / 2}/)\n  expect_to_s %<%r(/)>, %(/\\\\//)\n  expect_to_s %(/ /), %(/\\\\ /)\n  expect_to_s %(%r( )), %(/\\\\ /)\n  expect_to_s %(foo &.bar), %(foo(&.bar))\n  expect_to_s %(foo &.bar(1, 2, 3)), %(foo(&.bar(1, 2, 3)))\n  expect_to_s %(foo x: 1, y: 2, &.bar), %(foo(x: 1, y: 2, &.bar))\n  expect_to_s %(foo { |i| i.bar { i } }), %(foo do |i| i.bar do i end end)\n  expect_to_s %(foo do |k, v|\\n  k.bar(1, 2, 3)\\nend)\n  expect_to_s %(foo(3, &.*(2)))\n  expect_to_s %(return begin\\n  1\\n  2\\nend)\n  expect_to_s %(macro foo\\n  %bar = 1\\nend)\n  expect_to_s %(macro foo\\n  %bar = 1; end)\n  expect_to_s %(macro foo\\n  %bar{1, x} = 1\\nend)\n  expect_to_s %({% foo %})\n  expect_to_s %({{ foo }})\n  expect_to_s %({% if foo %}\\n  foo_then\\n{% end %})\n  expect_to_s %({% if foo %}\\n  foo_then\\n{% else %}\\n  foo_else\\n{% end %})\n  expect_to_s %({% unless foo %}\\n  foo_then\\n{% end %})\n  expect_to_s %({% unless foo %}\\n  foo_then\\n{% else %}\\n  foo_else\\n{% end %})\n  expect_to_s %({% for foo in bar %}\\n  {{ foo }}\\n{% end %})\n  expect_to_s %(macro foo\\n  {% for foo in bar %}\\n    {{ foo }}\\n  {% end %}\\nend)\n  expect_to_s %[1.as(Int32)]\n  expect_to_s %[(1 || 1.1).as(Int32)], %[(1 || 1.1).as(Int32)]\n  expect_to_s %[1 & 2 & (3 | 4)], %[(1 & 2) & (3 | 4)]\n  expect_to_s %[(1 & 2) & (3 | 4)]\n  expect_to_s \"def foo(x : T = 1)\\nend\"\n  expect_to_s \"def foo(@[Foo] x : T = 1)\\nend\"\n  expect_to_s \"def foo(x : X, y : Y) forall X, Y\\nend\"\n  expect_to_s \"def foo(x : X, @[Foo] y : Y) forall X, Y\\nend\"\n  expect_to_s %(foo : A | (B -> C))\n  expect_to_s %(foo : (A | B).class)\n  expect_to_s %[%(\"\\#{foo}\")], %[\"\\\\\"\\#{foo}\\\\\"\"]\n  expect_to_s \"class Foo\\n  private def bar\\n  end\\nend\"\n  expect_to_s \"foo(&.==(2))\"\n  expect_to_s \"foo.nil?\"\n  expect_to_s \"foo._bar\"\n  expect_to_s \"foo._bar(1)\"\n  expect_to_s \"_foo.bar\"\n  expect_to_s \"1.responds_to?(:to_s)\"\n  expect_to_s \"1.responds_to?(:\\\"&&\\\")\"\n  expect_to_s \"macro foo(&block)\\nend\"\n  expect_to_s \"macro foo(&)\\nend\"\n  expect_to_s \"macro foo(*, __var var)\\nend\"\n  expect_to_s \"macro foo(*, var)\\nend\"\n  expect_to_s \"macro foo(*var)\\nend\"\n  expect_to_s \"macro foo(@[Foo] &)\\nend\"\n  expect_to_s \"macro foo(@[Foo] &block)\\nend\"\n  expect_to_s \"macro foo(x, *y)\\nend\"\n  expect_to_s \"macro foo(x, @[Foo] *y)\\nend\"\n  expect_to_s \"macro foo(@[Foo] x, @[Foo] *y)\\nend\"\n  expect_to_s \"{ {1, 2, 3} }\"\n  expect_to_s \"{ {1 => 2} }\"\n  expect_to_s \"{ {1, 2, 3} => 4 }\"\n  expect_to_s \"{ {foo: 2} }\"\n  expect_to_s \"def foo(*args)\\nend\"\n  expect_to_s \"def foo(@[Foo] *args)\\nend\"\n  expect_to_s \"def foo(*args : _)\\nend\"\n  expect_to_s \"def foo(**args)\\nend\"\n  expect_to_s \"def foo(@[Foo] **args)\\nend\"\n  expect_to_s \"def foo(**args : T)\\nend\"\n  expect_to_s \"def foo(x, **args)\\nend\"\n  expect_to_s \"def foo(x, @[Foo] **args)\\nend\"\n  expect_to_s \"def foo(x, **args, &block)\\nend\"\n  expect_to_s \"def foo(@[Foo] x, @[Bar] **args, @[Baz] &block)\\nend\"\n  expect_to_s \"{% [1, 2, 3].each { |v| pp(v) } %}\", \"{% [1, 2, 3].each do |v| pp(v) end %}\"\n  expect_to_s \"{%\\n  [1, 2, 3].each { |v| pp(v) }\\n%}\", \"{%\\n  [1, 2, 3].each do |v| pp(v) end\\n%}\"\n  expect_to_s \"{% [1, 2, 3].find(&.even?.!) %}\", \"{% [1, 2, 3].find() do |__arg0| !__arg0.even? end %}\"\n  expect_to_s <<-'CRYSTAL'\n    {%\n      [1, 2, 3].find do |e|\n        e.even?\n      end\n    %}\n    CRYSTAL\n\n  # 14216\n  expect_to_s \"def foo(x, **args, &block : _ -> _)\\nend\"\n  expect_to_s \"def foo(x, **args, &block : (_ -> _))\\nend\", \"def foo(x, **args, &block : _ -> _)\\nend\"\n  expect_to_s \"def foo(& : ->)\\nend\"\n  expect_to_s \"def foo(& : (->))\\nend\", \"def foo(& : ->)\\nend\"\n  expect_to_s \"def foo(x : (T -> U) -> V, *args : (T -> U) -> V, y : (T -> U) -> V, **opts : (T -> U) -> V, & : (T -> U) -> V) : ((T -> U) -> V)\\nend\"\n  expect_to_s \"foo(x : (T -> U) -> V, W)\"\n  expect_to_s \"foo[x : (T -> U) -> V, W]\"\n  expect_to_s \"foo[x : (T -> U) -> V, W] = 1\"\n  expect_to_s \"lib LibFoo\\n  fun foo(x : (T -> U) -> V, W) : ((T -> U) -> V)\\nend\"\n\n  expect_to_s \"lib LibFoo\\n  fun foo(x : (T -> U) | V)\\nend\"\n  expect_to_s \"lib LibFoo\\n  fun foo(x : Foo((T -> U)))\\nend\"\n  expect_to_s \"lib LibFoo\\n  fun foo(x : (T -> U).class)\\nend\"\n  expect_to_s \"def foo(x : (T -> U) | V)\\nend\"\n  expect_to_s \"def foo(x : Foo((T -> U)))\\nend\"\n  expect_to_s \"def foo(x : (T -> U).class)\\nend\"\n  expect_to_s \"foo(x : (T -> U) | V)\"\n  expect_to_s \"foo(x : Foo((T -> U)))\"\n  expect_to_s \"foo(x : (T -> U).class)\"\n\n  expect_to_s \"macro foo(@[Foo] id)\\nend\"\n  expect_to_s \"macro foo(**args)\\nend\"\n  expect_to_s \"macro foo(@[Foo] **args)\\nend\"\n  expect_to_s \"macro foo(x, **args)\\nend\"\n  expect_to_s \"macro foo(x, @[Foo] **args)\\nend\"\n  expect_to_s \"def foo(x y)\\nend\"\n  expect_to_s \"def foo(@[Foo] x y)\\nend\"\n  expect_to_s %(foo(\"bar baz\": 2))\n  expect_to_s %(Foo(\"bar baz\": Int32))\n  expect_to_s %(Foo())\n  expect_to_s %({\"foo bar\": 1})\n  expect_to_s %(def foo(\"bar baz\" qux)\\nend)\n  expect_to_s \"foo()\"\n  expect_to_s \"/a/x\"\n  expect_to_s \"1_f32\", \"1_f32\"\n  expect_to_s \"1_f64\", \"1_f64\"\n  expect_to_s \"1.0\", \"1.0\"\n  expect_to_s \"1e10_f64\", \"1e10\"\n  expect_to_s \"!a\"\n  expect_to_s \"!(1 < 2)\"\n  expect_to_s \"!a.b && true\"\n  expect_to_s \"x.!.foo\", \"(!x).foo\"\n  expect_to_s \"x.!.!.foo\", \"(!(!x)).foo\"\n  expect_to_s \"x.foo.!\", \"!x.foo\"\n  expect_to_s \"x.foo.!.!\", \"!!x.foo\"\n  expect_to_s \"(1 + 2)..3\"\n  expect_to_s \"macro foo\\n{{ @type }}\\nend\"\n  expect_to_s \"macro foo\\n\\\\{{ @type }}\\nend\"\n  expect_to_s \"macro foo\\n{% @type %}\\nend\"\n  expect_to_s \"macro foo\\n\\\\{%@type %}\\nend\"\n  expect_to_s \"enum A : B\\nend\"\n  expect_to_s \"# doc\\ndef foo\\nend\", emit_doc: true\n  expect_to_s \"class Foo\\n  # doc\\n  def foo\\n  end\\nend\", emit_doc: true\n  expect_to_s \"foo[x, y, a: 1, b: 2]\"\n  expect_to_s \"foo[x, y, a: 1, b: 2] = z\"\n  expect_to_s %(@[Foo(1, 2, a: 1, b: 2)])\n  expect_to_s %(lib Foo\\nend)\n  expect_to_s %(lib LibC\\n  fun getchar(Int, Float)\\nend)\n  expect_to_s %(fun foo(a : Void, b : Void, ...) : Void\\nend)\n  expect_to_s %(fun foo\\nend)\n  expect_to_s %(lib Foo\\n  struct Foo\\n    a : Void\\n    b : Void\\n  end\\nend)\n  expect_to_s %(lib Foo\\n  union Foo\\n    a : Int\\n    b : Int32\\n  end\\nend)\n  expect_to_s %(lib Foo\\n  FOO = 0\\nend)\n  expect_to_s <<-CRYSTAL, <<-CRYSTAL\n    lib Foo\n      A = Pointer(Void).new(0)\n      struct B\n        x : Void*\n        y : Int[1]\n      end\n      fun c(Void*) : Char[2]*\n    end\n    CRYSTAL\n    lib Foo\n      A = Pointer(Void).new(0)\n      struct B\n        x : ::Pointer(Void)\n        y : ::StaticArray(Int, 1)\n      end\n      fun c(::Pointer(Void)) : ::Pointer(::StaticArray(Char, 2))\n    end\n    CRYSTAL\n  expect_to_s %(lib LibC\\n  fun getch = \"get.char\"\\nend)\n  expect_to_s %(lib Foo::Bar\\nend)\n  expect_to_s %(enum Foo\\n  A = 0\\n  B\\nend)\n  expect_to_s %(alias Foo = Void)\n  expect_to_s %(alias Foo::Bar = Void)\n  expect_to_s %(type(Foo = Void))\n  expect_to_s %(return true ? 1 : 2)\n  expect_to_s %(1 <= 2 <= 3)\n  expect_to_s %((1 <= 2) <= 3)\n  expect_to_s %(1 <= (2 <= 3))\n  expect_to_s %(case 1; when .foo?; 2; end), %(case 1\\nwhen .foo?\\n  2\\nend)\n  expect_to_s %(case 1; in .foo?; 2; end), %(case 1\\nin .foo?\\n  2\\nend)\n  expect_to_s %(case 1; when .!; 2; when .< 0; 3; end), %(case 1\\nwhen .!\\n  2\\nwhen .<(0)\\n  3\\nend)\n  expect_to_s %(case 1\\nwhen .[](2)\\n  3\\nwhen .[]=(4)\\n  5\\nend)\n  expect_to_s %({(1 + 2)})\n  expect_to_s %({foo: (1 + 2)})\n  expect_to_s %q(\"#{(1 + 2)}\")\n  expect_to_s %({(1 + 2) => (3 + 4)})\n  expect_to_s %([(1 + 2)] of Int32)\n  expect_to_s %(foo(1, (2 + 3), bar: (4 + 5)))\n  expect_to_s %(if (1 + 2\\n3)\\n  4\\nend)\n  expect_to_s \"%x(whoami)\", \"`whoami`\"\n  expect_to_s %(begin\\n  ()\\nend)\n  expect_to_s %(begin\\n  (1)\\nend)\n  expect_to_s %(begin\\n  (@x = x).is_a?(Foo)\\nend)\n  expect_to_s %(begin\\n  (1)\\n  2\\nend)\n  expect_to_s %(if 1\\n  begin\\n    2\\n  end\\nelse\\n  begin\\n    3\\n  end\\nend)\n\n  expect_to_s <<-CRYSTAL\n    if 1\n      2\n    elsif 3\n      4\n    elsif 5\n    elsif 6\n    else\n      7\n    end\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL, <<-CRYSTAL\n    if 1\n      2\n    else\n      if 3\n      end\n    end\n    CRYSTAL\n    if 1\n      2\n    elsif 3\n    end\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    if 1\n      2\n    else\n      unless 3\n      end\n    end\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    if 1\n      2\n    else\n      3 ? 4 : 5\n    end\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    unless 1\n      2\n    else\n      if 3\n      end\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {% if 1 %}\n      2\n    {% elsif 3 %}\n      4\n    {% elsif 5 %}\n    {% elsif 6 %}\n    {% else %}\n      7\n    {% end %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    {% if 1 %}\n      2\n    {% else %}{% if 3 %}\n    {% end %}{% end %}\n    CRYSTAL\n    {% if 1 %}\n      2\n    {% elsif 3 %}\n    {% end %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {% if 1 %}\n      2\n    {% else %}{% unless 3 %}\n    {% end %}{% end %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {% unless 1 %}\n      2\n    {% else %}{% if 3 %}\n    {% end %}{% end %}\n    CRYSTAL\n\n  expect_to_s %(foo do\\n  begin\\n    bar\\n  end\\nend)\n  expect_to_s %q(\"\\e\\0\\\"\"), %q(\"\\e\\u0000\\\"\")\n  expect_to_s %q(\"#{1}\\0\"), %q(\"#{1}\\u0000\")\n  expect_to_s %q(%r{\\/\\0}), %q(/\\/\\0/)\n  expect_to_s %q(%r{#{1}\\/\\0}), %q(/#{1}\\/\\0/)\n  expect_to_s %q(`\\n\\0`), %q(`\\n\\u0000`)\n  expect_to_s %q(`#{1}\\n\\0`), %q(`#{1}\\n\\u0000`)\n  expect_to_s Call.new(\"`\", Call.new(\"String\".path, \"interpolation\", \"x\".var, global: true)), %q(`#{::String.interpolation(x)}`)\n  expect_to_s StringInterpolation.new([\"#\".string, \"{foo}\".string] of ASTNode), %q(\"\\#{foo}\")\n  expect_to_s StringInterpolation.new([2.int32, \" \".string, \"#\".string, \"{\".string] of ASTNode), %q(\"#{2} \\#{\")\n  expect_to_s StringInterpolation.new([\"a\".string, \"b\".string] of ASTNode), %q(\"ab\")\n  expect_to_s \"macro foo\\n{% verbatim do %}1{% end %}\\nend\"\n  expect_to_s Assign.new(\"x\".var, Expressions.new([1.int32, 2.int32] of ASTNode)), \"x = (1\\n2\\n)\"\n  expect_to_s \"foo.*\"\n  expect_to_s \"foo.%\"\n  expect_to_s \"&+1\"\n  expect_to_s \"&-1\"\n  expect_to_s \"1.&*\"\n  expect_to_s \"1.&**\"\n  expect_to_s \"1.~(2)\"\n  expect_to_s \"1.~(2) do\\nend\"\n  expect_to_s \"1.+ do\\nend\"\n  expect_to_s \"1.[](2) do\\nend\"\n  expect_to_s \"1.[]=\"\n  expect_to_s \"1[&.foo]\"\n  expect_to_s \"1[&.foo]?\"\n  expect_to_s \"1[&.foo] = 2\"\n  expect_to_s \"1[2, x: 3, &.foo]\"\n  expect_to_s \"1[2, x: 3, &.foo]?\"\n  expect_to_s \"1[2, x: 3, &.foo] = 4\"\n  expect_to_s \"1.+(a: 2)\"\n  expect_to_s \"1.+(&block)\"\n  expect_to_s \"1.//(2, a: 3)\"\n  expect_to_s \"1.//(2, &block)\"\n  expect_to_s <<-'CRYSTAL'\n    {% verbatim do %}\n      1{{ 2 }}\n      3{{ 4 }}\n    {% end %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    {% for foo in bar %}\n      {{ if true\n           foo\n           bar\n         end }}\n    {% end %}\n    CRYSTAL\n    {% for foo in bar %}\n      {{ if true\n      foo\n      bar\n    end }}\n    {% end %}\n    CRYSTAL\n\n  expect_to_s \"{% a = 1 %}\"\n  expect_to_s \"{{ a = 1 }}\"\n  expect_to_s \"{%\\n  1\\n  2\\n  3\\n%}\"\n  expect_to_s \"{%\\n  1\\n%}\"\n  expect_to_s \"{%\\n  2 + 2\\n%}\"\n  expect_to_s \"{%\\n  a = 1 %}\"\n  expect_to_s \"{% a = 1\\n%}\"\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      if 1\n        2\n      end\n      3\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      if 1\n        2\n      end\n      3\n      4\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n          # Foo\n\n          20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n\n\n          20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n          # Foo\n          20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n\n          20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n          # Foo\n\n          20\n          30\n\n          # Bar\n\n          40\n        %}\n        {%\n          50\n          60\n        %}\n      {% end %}\n    end\n    CRYSTAL\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n\n\n\n          20\n          30\n\n\n\n          40\n        %}\n        {%\n          50\n          60\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n          20\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n          10\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n\n          a = 1 %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n\n\n          a = 1\n          b = 2 %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {% a = 1\n           b = 2\n\n        %}\n      {% end %}\n    end\n    CRYSTAL\n    macro finished\n      {% verbatim do %}\n        {%     a = 1\n        b = 2\n\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    {%\n      a = 1\n\n      if true\n        b = 2\n        c = 3\n      end\n\n      d = 4\n    %}\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    {%\n      arr.each do |c|\n        c.each do\n          to_process << 1\n          to_process << 2\n        end\n      end\n\n      to_process.each do\n        b = 2\n        a = 1\n      end\n    %}\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    {%\n      a = 1\n\n      unless false\n        b = 2\n        c = 3\n      end\n\n      d = 4\n    %}\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    {%\n      arr.each do\n        b = 2\n        a = 1\n      end\n\n      c = 3\n    %}\n    CRYSTAL\n\n  expect_to_s <<-CRYSTAL\n    {%\n      arr.each do\n        b = 2\n        a = 1\n      end\n    %}\n    CRYSTAL\n\n  expect_to_s %(asm(\"nop\" ::::))\n  expect_to_s %(asm(\"nop\" : \"a\"(1), \"b\"(2) : \"c\"(3), \"d\"(4) : \"e\", \"f\" : \"volatile\", \"alignstack\", \"intel\"))\n  expect_to_s %(asm(\"nop\" :: \"c\"(3), \"d\"(4) ::))\n  expect_to_s %(asm(\"nop\" :::: \"volatile\"))\n  expect_to_s %(asm(\"nop\" :: \"a\"(1) :: \"volatile\"))\n  expect_to_s %(asm(\"nop\" ::: \"e\" : \"volatile\"))\n  expect_to_s %(asm(\"bl trap\" :::: \"unwind\"))\n  expect_to_s %[(1..)]\n  expect_to_s %[..3]\n  expect_to_s \"offsetof(Foo, @bar)\"\n  expect_to_s \"def foo(**options, &block)\\nend\"\n  expect_to_s \"macro foo\\n  123\\nend\"\n  expect_to_s \"if true\\n  (1)\\nend\"\n  expect_to_s \"if true\\n  (1)\\n  2\\nend\"\n  expect_to_s \"begin\\n  (1)\\nrescue\\nend\"\n  expect_to_s \"begin\\n  (1)\\n  2\\nrescue\\nend\"\n  expect_to_s %[他.说(\"你好\")]\n  expect_to_s %[他.说 = \"你好\"]\n  expect_to_s %[あ.い, う.え.お = 1, 2]\n  expect_to_s \"-> : Int32 do\\nend\"\n  expect_to_s \"->(x : Int32, y : Bool) : Char do\\n  'a'\\nend\"\n  expect_to_s \"->::foo(Int32, String)\"\n  expect_to_s \"->::Foo::Bar.foo\"\n  expect_to_s \"yield(1)\"\n  expect_to_s \"foo { |(x, y)| x }\", \"foo do |(x, y)| x end\"\n  expect_to_s \"foo do |(x, y)|\\n  x\\nend\", <<-CRYSTAL\n    foo do |(x, y)|\n      x\n    end\n    CRYSTAL\n  expect_to_s \"foo { |(x, (y, z))| x }\", \"foo do |(x, (y, z))| x end\"\n  expect_to_s \"foo do |(x, (y, z))|\\n  x\\nend\", <<-CRYSTAL\n    foo do |(x, (y, z))|\n      x\n    end\n    CRYSTAL\n  expect_to_s \"def foo\\n  yield\\nend\", \"def foo(&)\\n  yield\\nend\"\n  expect_to_s \"def foo(x)\\n  yield\\nend\", \"def foo(x, &)\\n  yield\\nend\"\n  expect_to_s \"def foo(**x)\\n  yield\\nend\", \"def foo(**x, &)\\n  yield\\nend\"\n  expect_to_s \"macro foo(x)\\n  yield\\nend\"\n  expect_to_s <<-CRYSTAL\n    select\n    when foo\n      select\n      when bar\n        1\n      else\n        2\n      end\n    else\n      select\n      when baz\n        3\n      else\n        4\n      end\n    end\n    CRYSTAL\n\n  expect_to_s %({% {id: 10} %})\n  expect_to_s <<-'CRYSTAL'\n    {%\n      data = {__nil: nil}\n      data[\"foo\"] = {\n        id: 1,\n        active: true,\n        name: \"foo\".upcase,\n        pie: 3.14,\n      }\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      data = {__nil: nil}\n      data[\"foo\"] = {\n        id: 1, active: true,\n        name: \"foo\".upcase,\n        pie: 3.14,\n      }\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      data = {__nil: nil}\n      data[\"foo\"] = {\n        id: 1, active: true,\n        name: \"foo\".upcase,\n        pie: 3.14, biz: \"baz\", blah: false,\n      }\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      {\n        id: 1,\n\n        blah: false,\n\n        pie: 3.14,\n      }\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    {%\n      {\n        id: 1,\n\n        # Foo\n        pie: 3.14,\n      }\n    %}\n    CRYSTAL\n    {%\n      {\n        id: 1,\n\n\n        pie: 3.14,\n      }\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    macro finished\n      {% verbatim do %}\n        {%\n          nt = {\n            id: 1,\n\n            # Foo\n            pie: 3.14,\n          }\n        %}\n      {% end %}\n    end\n    CRYSTAL\n    macro finished\n      {% verbatim do %}\n        {%\n          nt = {\n            id: 1,\n\n\n            pie: 3.14,\n          }\n        %}\n      {% end %}\n    end\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      {\n        id: 1,\n        blah: false,\n        pie: 3.14}\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      {id: 1,\n        blah: false,\n        pie: 3.14}\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      {id: 1,\n        blah: false,\n        pie: 3.14,\n      }\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    {%\n      ({\"a\" => \"b\"} of Nil => Nil).each do |k, v|\n        # stuff and things\n        k + v\n\n        # foo bar\n\n        k + v\n      end\n    %}\n    CRYSTAL\n  {%\n    ({\"a\" => \"b\"} of Nil => Nil).each do |k, v|\n\n      k + v\n\n\n\n      k + v\n    end\n  %}\n  CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      vals = \"foo\".strip.strip.strip\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      vals = \"foo\".strip.strip\n        .strip\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      vals = \"foo\"\n        .strip\n        .strip.strip\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      vals = [4, 1, 12]\n        .sort_by do |v| v end\n        .map do |v| v end\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      vals = [4, 1, 12]\n        .sort_by do |v| v end\n        .join\n        .strip\n        .chars\n        .map do |v| v end\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      (\n        1\n      )\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      (\n        1\n        2\n      )\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      (\n        true ||\n        false\n      )\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      (\n        true ||\n        false\n      ) && true\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL'\n    {%\n      true && (\n        true ||\n        false\n      )\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    {%\n      if (v = 5) &&\n        (\n          1 < v ||\n          (v < 4 && 5 < 6)\n        )\n        123\n      end\n    %}\n    CRYSTAL\n    {%\n      if (v = 5) &&\n      (\n        1 < v ||\n        (v < 4 && 5 < 6)\n      )\n        123\n      end\n    %}\n    CRYSTAL\n\n  expect_to_s <<-'CRYSTAL', <<-'CRYSTAL'\n    {%\n      if (true || false) &&\n        true\n        1\n      end\n    %}\n    CRYSTAL\n    {%\n      if (true || false) &&\n      true\n        1\n      end\n    %}\n    CRYSTAL\nend\n"
  },
  {
    "path": "spec/compiler/parser/warnings_spec.cr",
    "content": "require \"../../support/syntax\"\n\nprivate def assert_parser_warning(source, *messages, file = __FILE__, line = __LINE__)\n  parser = Parser.new(source)\n  parser.filename = \"/test.cr\"\n  parser.parse\n\n  warnings = parser.warnings.infos\n  warnings.size.should eq(messages.size), file: file, line: line\n  warnings.zip(messages) do |warning, message|\n    warning.should contain(message), file: file, line: line\n  end\nend\n\nprivate def assert_no_parser_warning(source, *, file = __FILE__, line = __LINE__)\n  assert_parser_warning(source, file: file, line: line)\nend\n\nVALID_SIGILS = ['i', 'q', 'r', 'w', 'x', 'Q']\n\ndescribe \"Parser warnings\" do\n  it \"warns on suffix-less UInt64 literals > Int64::MAX\" do\n    values = [\n      \"9223372036854775808\", # Int64::MAX + 1\n      \"9999999999999999999\",\n      \"10000000000000000000\",\n      \"18446744073709551615\", # UInt64::MAX\n      \"0x8000_0000_0000_0000\",\n      \"0xFFFF_FFFF_FFFF_FFFF\",\n    ]\n\n    values.each do |value|\n      assert_parser_warning value, \"Warning: #{value} doesn't fit in an Int64, try using the suffix u64 or i128\"\n      assert_parser_warning \"Foo(#{value})\", \"Warning: #{value} doesn't fit in an Int64, try using the suffix u64 or i128\"\n      assert_parser_warning \"{{ #{value} }}\", \"Warning: #{value} doesn't fit in an Int64, try using the suffix u64 or i128\"\n    end\n  end\n\n  describe \"warns on missing space before colon\" do\n    it \"in block param type restriction\" do\n      assert_parser_warning(\"def foo(&block: Foo)\\nend\", \"warning in /test.cr:1\\nWarning: space required before colon in type restriction (run `crystal tool format` to fix this)\")\n      assert_no_parser_warning(\"def foo(&block : Foo)\\nend\")\n      assert_no_parser_warning(\"def foo(&@foo)\\nend\")\n    end\n\n    it \"in anonymous block param type restriction\" do\n      assert_parser_warning(\"def foo(&: Foo)\\nend\", \"warning in /test.cr:1\\nWarning: space required before colon in type restriction (run `crystal tool format` to fix this)\")\n      assert_no_parser_warning(\"def foo(& : Foo)\\nend\")\n      assert_no_parser_warning(\"def foo(&)\\nend\")\n    end\n\n    it \"in type declaration\" do\n      assert_parser_warning(\"x: Int32\", \"warning in /test.cr:1\\nWarning: space required before colon in type declaration (run `crystal tool format` to fix this)\")\n      assert_no_parser_warning(\"x : Int32\")\n      assert_parser_warning(\"class Foo\\n@x: Int32\\nend\", \"warning in /test.cr:2\\nWarning: space required before colon in type declaration (run `crystal tool format` to fix this)\")\n      assert_no_parser_warning(\"class Foo\\n@x : Int32\\nend\")\n      assert_parser_warning(\"class Foo\\n@@x: Int32\\nend\", \"warning in /test.cr:2\\nWarning: space required before colon in type declaration (run `crystal tool format` to fix this)\")\n      assert_no_parser_warning(\"class Foo\\n@@x : Int32\\nend\")\n    end\n\n    it \"in return type restriction\" do\n      assert_parser_warning(\"def foo: Foo\\nend\", \"warning in /test.cr:1\\nWarning: space required before colon in return type restriction (run `crystal tool format` to fix this)\")\n      assert_no_parser_warning(\"def foo : Foo\\nend\")\n    end\n  end\n\n  it \"warns on single-letter macro lowercase fresh variables with indices\" do\n    chars = ('a'..'z').to_a - VALID_SIGILS\n    chars.each do |letter|\n      assert_parser_warning <<-CRYSTAL, \"Warning: single-letter macro fresh variables with indices are deprecated\"\n        macro foo\n          %#{letter}{1} = 2\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"warns on single-letter uppercase macro fresh variables with indices\" do\n    chars = ('A'..'Z').to_a.push('ǲ') - VALID_SIGILS\n    chars.each do |letter|\n      assert_parser_warning <<-CRYSTAL, \"Warning: macro fresh variables with constant names are deprecated\", \"Warning: single-letter macro fresh variables with indices are deprecated\"\n        macro foo\n          %#{letter}{1} = 2\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"doesn't warn on sigils that resemble single-letter macro fresh variables with indices\" do\n    VALID_SIGILS.each do |letter|\n      assert_no_parser_warning <<-CRYSTAL\n        macro foo\n          %#{letter}{1}\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"warns on single-letter uppercase macro fresh variables without indices\" do\n    chars = ('A'..'Z').to_a.push('ǲ')\n    chars.each do |letter|\n      assert_parser_warning <<-CRYSTAL, \"Warning: macro fresh variables with constant names are deprecated\"\n        macro foo\n          %#{letter} = 1\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"doesn't warn on single-letter lowercase macro fresh variables without indices\" do\n    ('a'..'z').each do |letter|\n      assert_no_parser_warning <<-CRYSTAL\n        macro foo\n          %#{letter} = 1\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/abstract_def_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: abstract def\" do\n  it \"errors if using abstract def on subclass\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Baz\"\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      (Bar.new || Baz.new).foo\n      CRYSTAL\n  end\n\n  it \"works on abstract method on abstract class\" do\n    assert_type <<-CRYSTAL { int32 }\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      class Baz < Foo\n        def foo\n          2\n        end\n      end\n\n      b = Bar.new || Baz.new\n      b.foo\n      CRYSTAL\n  end\n\n  it \"works on abstract def on sub-subclass\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      class Baz < Bar\n      end\n\n      p = Pointer(Foo).malloc(1_u64)\n      p.value = Bar.new\n      p.value = Baz.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"errors if using abstract def on subclass that also defines it as abstract\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Baz\"\n      abstract class Foo\n        abstract def foo\n      end\n\n      abstract class Bar < Foo\n        abstract def foo\n      end\n\n      class Baz < Bar\n      end\n      CRYSTAL\n  end\n\n  it \"gives correct error when no overload matches, when an abstract method is implemented (#1406)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Bar#foo' to be Int32, not (Char | Int32)\"\n      abstract class Foo\n        abstract def foo(x : Int32)\n      end\n\n      class Bar < Foo\n        def foo(x : Int32)\n          1\n        end\n      end\n\n      Bar.new.foo(1 || 'a')\n      CRYSTAL\n  end\n\n  it \"errors if using abstract def on non-abstract class\" do\n    assert_error <<-CRYSTAL, \"can't define abstract def on non-abstract class\"\n      class Foo\n        abstract def foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using abstract def on metaclass\" do\n    assert_error <<-CRYSTAL, \"can't define abstract def on metaclass\"\n      class Foo\n        abstract def self.foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method is not implemented by subclass\" do\n    exc = assert_error <<-CRYSTAL,\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n      \"abstract `def Foo#foo()` must be implemented by Bar\"\n    exc.line_number.should eq 5\n    exc.column_number.should eq 1\n  end\n\n  it \"errors if abstract method with arguments is not implemented by subclass\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(x, y)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(x, y)\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method with arguments is not implemented by subclass (wrong number of arguments)\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(x)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(x)\n      end\n\n      class Bar < Foo\n        def foo(x, y)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method with arguments is not implemented by subclass (wrong type)\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(x, y : Int32)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(x, y : Int32)\n      end\n\n      class Bar < Foo\n        def foo(x, y : Float64)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method with arguments is not implemented by subclass (block difference)\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo\n          yield\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method is implemented by subclass\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method with args is implemented by subclass\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(x, y)\n      end\n\n      class Bar < Foo\n        def foo(x, y)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method with args is implemented by subclass (restriction -> no restriction)\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(x, y : Int32)\n      end\n\n      class Bar < Foo\n        def foo(x, y)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method with args is implemented by subclass (don't check subclasses)\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n\n      class Baz < Bar\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method of private type is not implemented by subclass\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Bar\"\n      private abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method is not implemented by subclass of subclass\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Baz\"\n      abstract class Foo\n        abstract def foo\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Baz < Bar\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method is implemented by subclass via module inclusion\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo\n      end\n\n      module Moo\n        def foo\n        end\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method is not implemented by including class\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Bar\"\n      module Foo\n        abstract def foo\n      end\n\n      class Bar\n        include Foo\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method is implemented by including class\" do\n    assert_no_errors <<-CRYSTAL\n      module Foo\n        abstract def foo\n      end\n\n      class Bar\n        include Foo\n\n        def foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method of private type is not implemented by including class\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo()` must be implemented by Bar\"\n      private module Foo\n        abstract def foo\n      end\n\n      class Bar\n        include Foo\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method is not implemented by including module\" do\n    assert_no_errors <<-CRYSTAL\n      module Foo\n        abstract def foo\n      end\n\n      module Bar\n        include Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if abstract method is not implemented by subclass (nested in module)\" do\n    assert_error <<-CRYSTAL, \"abstract `def Moo::Foo#foo()` must be implemented by Bar\"\n      module Moo\n        abstract class Foo\n          abstract def foo\n        end\n      end\n\n      class Bar < Moo::Foo\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if abstract method with args is implemented by subclass (with one default arg)\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(x)\n      end\n\n      class Bar < Foo\n        def foo(x, y = 1)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implements with parent class\" do\n    assert_no_errors <<-CRYSTAL\n      class Parent; end\n      class Child < Parent; end\n\n      abstract class Foo\n        abstract def foo(x : Child)\n      end\n\n      class Bar < Foo\n        def foo(x : Parent)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implements with generic parent class instance\" do\n    assert_no_errors <<-CRYSTAL\n      class Parent(T); end\n      class Child(T) < Parent(T); end\n\n      abstract class Foo\n        abstract def foo(x : Child(Int32))\n      end\n\n      class Bar < Foo\n        def foo(x : Parent(Int32))\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implements with included module\" do\n    assert_no_errors <<-CRYSTAL\n      module Moo\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      abstract class Foo\n        abstract def foo(x : Moo2)\n      end\n\n      class Bar < Foo\n        def foo(x : Moo)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implements with generic included module instance\" do\n    assert_no_errors <<-CRYSTAL\n      module Moo(T)\n      end\n\n      module Moo2(T)\n        include Moo(T)\n      end\n\n      abstract class Foo\n        abstract def foo(x : Moo2(Int32))\n      end\n\n      class Bar < Foo\n        def foo(x : Moo(Int32))\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implements with parent module\" do\n    assert_no_errors <<-CRYSTAL\n      module Moo\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      class Child\n        include Moo2\n      end\n\n      abstract class Foo\n        abstract def foo(x : Child)\n      end\n\n      class Bar < Foo\n        def foo(x : Moo)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implements a NoReturn param\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(x : NoReturn)\n      end\n\n      class Bar < Foo\n        def foo(x : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"finds implements in included module in disorder (#4052)\" do\n    assert_no_errors <<-CRYSTAL\n      module B\n        abstract def x\n      end\n\n      module C\n        def x\n          :x\n        end\n      end\n\n      class A\n        include C\n        include B\n      end\n      CRYSTAL\n  end\n\n  it \"errors if missing return type\" do\n    assert_error <<-CRYSTAL,\n      abstract class Foo\n        abstract def foo : Int32\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n      CRYSTAL\n      \"this method overrides Foo#foo() which has an explicit return type of Int32.\\n\\nPlease add an explicit return type (Int32 or a subtype of it) to this method as well.\"\n  end\n\n  it \"errors if different return type\" do\n    assert_error <<-CRYSTAL,\n      abstract class Foo\n        abstract def foo : Int32\n      end\n\n      class Bar < Foo\n        struct Int32\n        end\n\n        def foo : Int32\n          1\n        end\n      end\n      CRYSTAL\n      \"this method must return Int32, which is the return type of the overridden method Foo#foo(), or a subtype of it, not Bar::Int32\"\n  end\n\n  it \"can return a more specific type\" do\n    assert_type(<<-CRYSTAL) { types[\"Child\"] }\n      class Parent\n      end\n\n      class Child < Parent\n      end\n\n\n      abstract class Foo\n        abstract def foo : Parent\n      end\n\n      class Bar < Foo\n        def foo : Child\n          Child.new\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"matches instantiated generic types\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo(T)\n        abstract def foo(x : T)\n      end\n\n      abstract class Bar(U) < Foo(U)\n      end\n\n      class Baz < Bar(Int32)\n        def foo(x : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"matches generic types\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo(T)\n        abstract def foo(x : T)\n      end\n\n      class Bar(U) < Foo(U)\n        def foo(x : U)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"matches instantiated generic module\" do\n    assert_no_errors <<-CRYSTAL\n      module Foo(T)\n        abstract def foo(x : T)\n      end\n\n      class Bar\n        include Foo(Int32)\n\n        def foo(x : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"matches generic module\" do\n    assert_no_errors <<-CRYSTAL\n      module Foo(T)\n        abstract def foo(x : T)\n      end\n\n      class Bar(U)\n        include Foo(U)\n\n        def foo(x : U)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"matches generic module (a bit more complex)\" do\n    assert_no_errors <<-CRYSTAL\n      class Gen(T)\n      end\n\n      module Foo(T)\n        abstract def foo(x : Gen(T))\n      end\n\n      class Bar\n        include Foo(Int32)\n\n        def foo(x : Gen(Int32))\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"matches generic return type\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo(T)\n        abstract def foo : T\n      end\n\n      class Bar < Foo(Int32)\n        def foo : Int32\n          1\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if missing a return type in subclass of generic subclass\" do\n    assert_error <<-CRYSTAL,\n        abstract class Foo(T)\n          abstract def foo : T\n        end\n\n        class Bar < Foo(Int32)\n          def foo\n          end\n        end\n      CRYSTAL\n      \"this method overrides Foo(T)#foo() which has an explicit return type of T.\\n\\nPlease add an explicit return type (Int32 or a subtype of it) to this method as well.\"\n  end\n\n  it \"errors if can't find parent return type\" do\n    assert_error <<-CRYSTAL,\n        abstract class Foo\n          abstract def foo : Unknown\n        end\n\n        class Bar < Foo\n          def foo\n          end\n        end\n      CRYSTAL\n      \"can't resolve return type Unknown\"\n  end\n\n  it \"errors if can't find child return type\" do\n    assert_error <<-CRYSTAL,\n        abstract class Foo\n          abstract def foo : Int32\n        end\n\n        class Bar < Foo\n          def foo : Unknown\n          end\n        end\n      CRYSTAL\n      \"can't resolve return type Unknown\"\n  end\n\n  it \"implements through extend (considers original type for generic lookup) (#8096)\" do\n    assert_no_errors <<-CRYSTAL\n      module ICallable(T)\n        abstract def call(foo : T)\n      end\n\n      module Moo\n        def call(foo : Int32)\n        end\n      end\n\n      module Caller\n        extend ICallable(Int32)\n        extend Moo\n      end\n      CRYSTAL\n  end\n\n  it \"implements through extend (considers original type for generic lookup) (2) (#8096)\" do\n    assert_no_errors <<-CRYSTAL\n      module ICallable(T)\n        abstract def call(foo : T)\n      end\n\n      module Caller\n        extend ICallable(Int32)\n        extend self\n\n        def call(foo : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can implement even if yield comes later in macro code\" do\n    assert_no_errors <<-CRYSTAL\n      module Moo\n        abstract def each(& : Int32 -> _)\n      end\n\n      class Foo\n        include Moo\n\n        def each\n          yield 1\n\n          {% if true %}\n            yield 2\n          {% end %}\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can implement by block signature even if yield comes later in macro code\" do\n    assert_no_errors <<-CRYSTAL\n      module Moo\n        abstract def each(& : Int32 -> _)\n      end\n\n      class Foo\n        include Moo\n\n        def each(& : Int32 -> _)\n          {% if true %}\n            yield 2\n          {% end %}\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"error shows full signature of block parameter\" do\n    assert_error(<<-CRYSTAL, \"abstract `def Moo#each(& : (Int32 -> _))` must be implemented by Foo\")\n      module Moo\n        abstract def each(& : Int32 -> _)\n      end\n\n      class Foo\n        include Moo\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implementation have default value\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(x)\n      end\n\n      class Bar < Foo\n        def foo(x = 1)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't have default value\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(x = 1)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(x = 1)\n      end\n\n      class Bar < Foo\n        def foo(x)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't have the same default value\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(x = 1)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(x = 1)\n      end\n\n      class Bar < Foo\n        def foo(x = 2)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation adds type restriction\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(x)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(x)\n      end\n\n      class Bar < Foo\n        def foo(x : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't have keyword arguments\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*, x)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*, x)\n      end\n\n      class Bar < Foo\n        def foo(a = 0, b = 0)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't have a keyword argument\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*, x)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*, x)\n      end\n\n      class Bar < Foo\n        def foo(*, y)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implementation matches keyword argument\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(*, x)\n      end\n\n      class Bar < Foo\n        def foo(*, x)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't match keyword argument type\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*, x : Int32)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*, x : Int32)\n      end\n\n      class Bar < Foo\n        def foo(*, x : String)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implementation have keyword arguments in different order\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(*, x : Int32, y : String)\n      end\n\n      class Bar < Foo\n        def foo(*, y : String, x : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation has more keyword arguments\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*, x)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*, x)\n      end\n\n      class Bar < Foo\n        def foo(*, x, y)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if implementation has more keyword arguments with default values\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(*, x)\n      end\n\n      class Bar < Foo\n        def foo(*, x, y = 1)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't have a splat\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*args)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*args)\n      end\n\n      class Bar < Foo\n        def foo(x = 1)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation doesn't match splat type\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*args : Int32)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*args : Int32)\n      end\n\n      class Bar < Foo\n        def foo(*args : String)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error with splat\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(*args)\n      end\n\n      class Bar < Foo\n        def foo(*args)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error with splat and args with default value\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(*args)\n      end\n\n      class Bar < Foo\n        def foo(a = 1, *args)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allows arguments to be collapsed into splat\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(a : Int32, b : String)\n      end\n\n      class Bar < Foo\n        def foo(*args : Int32 | String)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if keyword argument doesn't have the same default value\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*, foo = 1)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*, foo = 1)\n      end\n\n      class Bar < Foo\n        def foo(*, foo = 2)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allow double splat argument\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(**kargs)\n      end\n\n      class Bar < Foo\n        def foo(**kargs)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allow double splat when abstract doesn't have it\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo\n      end\n\n      class Bar < Foo\n        def foo(**kargs)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if implementation misses the double splat\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(**kargs)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(**kargs)\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if double splat type doesn't match\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(**kargs : Int32)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(**kargs : Int32)\n      end\n\n      class Bar < Foo\n        def foo(**kargs : String)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allow splat instead of keyword argument\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Foo\n        abstract def foo(*, foo)\n      end\n\n      class Bar < Foo\n        def foo(**kargs)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"extra keyword arguments must have compatible type to double splat\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(**kargs : String)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(**kargs : String)\n      end\n\n      class Bar < Foo\n        def foo(*, foo : Int32 = 0, **kargs)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"double splat must match keyword argument type\" do\n    assert_error <<-CRYSTAL, \"abstract `def Foo#foo(*, foo : Int32)` must be implemented by Bar\"\n      abstract class Foo\n        abstract def foo(*, foo : Int32)\n      end\n\n      class Bar < Foo\n        def foo(**kargs : String)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if free var in arg restriction shadows another type (#10153)\" do\n    assert_no_errors <<-CRYSTAL\n      module Foo\n        abstract def foo(x : Int32, y : Array(Int32))\n      end\n\n      class Bar\n        include Foo\n\n        def foo(x : Quux, y : Array(Quux)) forall Quux\n          x\n        end\n      end\n\n      class Quux\n      end\n      CRYSTAL\n  end\n\n  describe \"implementation is not inherited from supertype\" do\n    it \"nongeneric class\" do\n      assert_error <<-CRYSTAL, \"abstract `def Abstract#foo()` must be implemented by Concrete\"\n        class Supertype\n          def foo; end\n        end\n\n        abstract class Abstract < Supertype\n          abstract def foo\n        end\n\n        class Concrete < Abstract\n        end\n        CRYSTAL\n    end\n\n    it \"generic class\" do\n      assert_error <<-CRYSTAL, \"abstract `def Abstract(T)#foo()` must be implemented by Concrete\"\n        class Supertype(T)\n          def foo; end\n        end\n\n        abstract class Abstract(T) < Supertype(T)\n          abstract def foo\n        end\n\n        class Concrete(T) < Abstract(T)\n        end\n        CRYSTAL\n    end\n\n    it \"nongeneric module\" do\n      assert_error <<-CRYSTAL, \"abstract `def Abstract#size()` must be implemented by Concrete\"\n        module Supertype\n          def size\n          end\n        end\n\n        module Abstract\n          include Supertype\n\n          abstract def size\n        end\n\n        class Concrete\n          include Abstract\n        end\n        CRYSTAL\n    end\n\n    it \"generic module\" do\n      assert_error <<-CRYSTAL, \"abstract `def Abstract(T)#size()` must be implemented by Concrete(T)\"\n        module Supertype(T)\n          def size\n          end\n        end\n\n        module Abstract(T)\n          include Supertype(T)\n\n          abstract def size\n        end\n\n        class Concrete(T)\n          include Abstract(T)\n        end\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/alias_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: alias\" do\n  it \"resolves alias type\" do\n    assert_type(<<-CRYSTAL) { types[\"Int32\"].metaclass }\n      alias Alias = Int32\n      Alias\n      CRYSTAL\n  end\n\n  it \"declares alias inside type\" do\n    assert_type(<<-CRYSTAL) { types[\"Int32\"].metaclass }\n      alias Foo::Bar = Int32\n      Foo::Bar\n      CRYSTAL\n  end\n\n  it \"works with alias type as restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      alias Alias = Int32\n\n      def foo(x : Alias)\n        x\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"allows using alias type as generic type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize(x : T)\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      alias Num = Int32 | Float64\n\n      f = Foo(Num).new(1)\n      g = Foo(Num).new(1.5)\n      1\n      CRYSTAL\n  end\n\n  it \"allows defining recursive aliases\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n      end\n\n      alias Alias = Int32 | Foo(Alias)\n      1\n      CRYSTAL\n    mod = result.program\n\n    foo = mod.types[\"Foo\"].as(GenericClassType)\n    a = mod.types[\"Alias\"].as(AliasType)\n\n    foo_alias = foo.instantiate([a] of TypeVar)\n\n    aliased_type = a.aliased_type.as(UnionType)\n    union_types = aliased_type.union_types.sort_by &.to_s\n    union_types[0].should eq(foo_alias)\n    union_types[1].should eq(mod.int32)\n  end\n\n  it \"allows defining recursive fun aliases\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      alias Alias = Alias -> Alias\n      1\n      CRYSTAL\n\n    mod = result.program\n\n    a = mod.types[\"Alias\"].as(AliasType)\n    aliased_type = a.aliased_type.as(ProcInstanceType)\n\n    aliased_type.should eq(mod.proc_of(a, a))\n  end\n\n  it \"allows recursive array with alias\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      alias Type = Nil | Pointer(Type)\n      p = Pointer(Type).malloc(1_u64)\n      1\n      CRYSTAL\n  end\n\n  it \"errors if alias already defined\" do\n    assert_error <<-CRYSTAL, \"alias Alias is already defined\"\n      alias Alias = String\n      alias Alias = Int32\n      CRYSTAL\n  end\n\n  it \"errors if alias is already defined as another type\" do\n    assert_error <<-CRYSTAL, \"can't alias String because it's already defined as a class\"\n      alias String = Int32\n      CRYSTAL\n  end\n\n  it \"errors if defining infinite recursive alias\" do\n    assert_error <<-CRYSTAL, \"infinite recursive definition of alias Alias\"\n      alias Alias = Alias\n      Alias\n      CRYSTAL\n  end\n\n  it \"errors if defining infinite recursive alias in union\" do\n    assert_error <<-CRYSTAL, \"infinite recursive definition of alias Alias\"\n      alias Alias = Int32 | Alias\n      Alias\n      CRYSTAL\n  end\n\n  it \"allows using generic type of recursive alias as restriction (#488)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n      end\n\n      alias Rec = String | Foo(Rec)\n\n      def command(request : Foo(Rec))\n        1\n      end\n\n      foo = Foo(Rec).new\n      command(foo)\n      CRYSTAL\n  end\n\n  it \"resolves type through alias (#563)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        Foo = 1\n      end\n\n      alias Alias = Moo\n      Alias::Foo\n      CRYSTAL\n  end\n\n  it \"errors if trying to resolve type of recursive alias\" do\n    assert_error <<-CRYSTAL, \"undefined constant Rec::A\"\n      class Foo(T)\n        A = 1\n      end\n\n      alias Rec = Int32 | Foo(Rec)\n\n      Rec::A\n      CRYSTAL\n  end\n\n  %w(class module struct).each do |type|\n    it \"reopens #{type} through alias\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        #{type} Foo\n        end\n\n        alias Bar = Foo\n\n        #{type} Bar\n          def self.bar\n            1\n          end\n        end\n\n        Bar.bar\n        CRYSTAL\n    end\n\n    it \"reopens #{type} through alias within itself\" do\n      assert_type <<-CRYSTAL { int32 }\n        #{type} Foo\n          alias Bar = Foo\n\n          #{type} Bar\n            def self.bar\n              1\n            end\n          end\n        end\n\n        Foo.bar\n        CRYSTAL\n    end\n  end\n\n  %w(class struct).each do |type|\n    it \"inherits #{type} through alias\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        abstract #{type} Parent\n        end\n\n        alias Alias = Parent\n\n        #{type} Child  < Alias\n          def self.bar\n            1\n          end\n        end\n\n        Child.bar\n        CRYSTAL\n    end\n  end\n\n  it \"includes module through alias\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        def bar\n          1\n        end\n      end\n\n      alias Alias = Moo\n\n      class Foo\n        include Alias\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"errors if declares alias inside if\" do\n    assert_error <<-CRYSTAL, \"can't declare alias dynamically\"\n      if 1 == 2\n        alias Foo = Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if trying to use typeof in alias\" do\n    assert_error <<-CRYSTAL, \"can't use 'typeof' here\"\n      alias Foo = typeof(1)\n      CRYSTAL\n  end\n\n  it \"can use .class in alias (#2835)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32.metaclass, string.metaclass).metaclass }\n      alias Foo = Int32.class | String.class\n      Foo\n      CRYSTAL\n  end\n\n  it \"uses constant in alias (#3259)\" do\n    assert_type(<<-CRYSTAL) { static_array_of(uint8, 10).metaclass }\n      CONST = 10\n      alias Alias = UInt8[CONST]\n      Alias\n      CRYSTAL\n  end\n\n  it \"uses constant in alias with math (#3259)\" do\n    assert_type(<<-CRYSTAL) { static_array_of(uint8, 10).metaclass }\n      CONST = 2*3 + 4\n      alias Alias = UInt8[CONST]\n      Alias\n      CRYSTAL\n  end\n\n  it \"looks up alias for macro resolution (#3548)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        class Bar\n          def self.baz\n            1\n          end\n        end\n      end\n\n      alias Baz = Foo\n\n      Baz::Bar.baz\n      CRYSTAL\n  end\n\n  it \"finds type through alias (#4645)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module FooBar\n        module Foo\n          A = 10\n        end\n\n        module Bar\n          include Foo\n        end\n      end\n\n      class Baz\n        alias Bar = FooBar::Bar\n\n        def test\n          Bar::A\n        end\n      end\n\n      Baz.new.test\n      CRYSTAL\n  end\n\n  it \"doesn't find type parameter in alias (#3502)\" do\n    assert_error <<-CRYSTAL, \"undefined constant T\"\n      class A(T)\n        alias B = A(T)\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash by infinite recursion against type alias and generics (#5329)\" do\n    assert_error <<-CRYSTAL, \"can't cast Foo(Int32) to Bar\"\n      class Foo(T)\n        def initialize(@foo : T)\n        end\n      end\n\n      alias Bar = Foo(Bar | Int32)\n\n      Foo(Bar).new(Foo.new(1).as(Bar))\n      CRYSTAL\n  end\n\n  it \"can pass recursive alias to proc\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nil_type }\n      class Object\n        def itself\n          self\n        end\n      end\n\n      alias Rec = Int32 | Array(Rec)\n\n      a = uninitialized Rec\n\n      f = ->(x : Rec) {}\n      f.call(a.itself)\n      CRYSTAL\n  end\n\n  it \"overloads union type through alias\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      alias X = Int8 | Int32\n\n      def foo(x : Int32)\n        1\n      end\n\n      def foo(x : X)\n        'a'\n      end\n\n      foo(1)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/annotation_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: annotation\" do\n  it \"declares annotation\" do\n    result = semantic(<<-CRYSTAL)\n      annotation Foo\n      end\n      CRYSTAL\n\n    type = result.program.types[\"Foo\"]\n    type.should be_a(AnnotationType)\n    type.name.should eq(\"Foo\")\n  end\n\n  describe \"arguments\" do\n    describe \"#args\" do\n      it \"returns an empty TupleLiteral if there are none defined\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          module Moo\n          end\n\n          {% if (pos_args = Moo.annotation(Foo).args) && pos_args.is_a? TupleLiteral && pos_args.empty? %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"returns a TupleLiteral if there are positional arguments defined\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo(1, \"foo\", true)]\n            module Moo\n          end\n\n          {% if Moo.annotation(Foo).args == {1, \"foo\", true} %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n    end\n\n    describe \"#named_args\" do\n      it \"returns an empty NamedTupleLiteral if there are none defined\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          module Moo\n          end\n\n          {% if (args = Moo.annotation(Foo).named_args) && args.is_a? NamedTupleLiteral && args.empty? %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"returns a NamedTupleLiteral if there are named arguments defined\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo(extra: \"three\", \"foo\": 99)]\n            module Moo\n          end\n\n          {% if Moo.annotation(Foo).named_args == {extra: \"three\", foo: 99} %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n    end\n\n    it \"returns a correctly with named and positional args\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo(1, \"foo\", true, foo: \"bar\", \"cat\": 0..0)]\n          module Moo\n        end\n\n        {% if Moo.annotation(Foo).args == {1, \"foo\", true} && Moo.annotation(Foo).named_args == {foo: \"bar\", cat: 0..0} %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n  end\n\n  describe \"#annotations\" do\n    describe \"all types\" do\n      it \"returns an empty array if there are none defined\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n\n          module Moo\n          end\n\n          {% if Moo.annotations.empty? %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations on a module\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          @[Foo]\n          @[Bar]\n          module Moo\n          end\n\n          {% if Moo.annotations.map(&.name.id) == [Foo.id, Bar.id] %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations on a class\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          @[Foo]\n          @[Bar]\n          class Moo\n          end\n\n          {% if Moo.annotations.map(&.name.id) == [Foo.id, Bar.id] %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations on a struct\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          @[Foo]\n          @[Bar]\n          struct Moo\n          end\n\n          {% if Moo.annotations.map(&.name.id) == [Foo.id, Bar.id] %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations on a enum\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          @[Foo]\n          @[Bar]\n          enum Moo\n            A = 1\n          end\n\n          {% if Moo.annotations.map(&.name.id) == [Foo.id, Bar.id] %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations on a lib\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          @[Foo]\n          @[Bar]\n          lib Moo\n            A = 1\n          end\n\n          {% if Moo.annotations.map(&.name.id) == [Foo.id, Bar.id] %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in instance var (declaration)\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          class Moo\n            @[Foo]\n            @[Bar]\n            @x : Int32 = 1\n\n            def foo\n              {% if @type.instance_vars.first.annotations.size == 2 %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new.foo\n          CRYSTAL\n      end\n\n      it \"finds annotations in instance var (declaration, generic)\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          class Moo(T)\n            @[Foo]\n            @[Bar]\n            @x : T\n\n            def initialize(@x : T)\n            end\n\n            def foo\n              {% if @type.instance_vars.first.annotations.size == 2 %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new(1).foo\n          CRYSTAL\n      end\n\n      it \"adds annotations on def\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          class Moo\n            @[Foo]\n            @[Bar]\n            def foo\n            end\n          end\n\n          {% if Moo.methods.first.annotations.size == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in generic parent (#7885)\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          @[Foo(1)]\n          @[Bar(2)]\n          class Parent(T)\n          end\n\n          class Child < Parent(Int32)\n          end\n\n          {% if Child.superclass.annotations.map(&.[0]) == [1, 2] %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"find annotations on method parameters\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          class Moo\n            def foo(@[Foo] @[Bar] value)\n            end\n          end\n\n          {% if Moo.methods.first.args.first.annotations.size == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n    end\n\n    describe \"of a specific type\" do\n      it \"returns an empty array if there are none defined\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          module Moo\n          end\n\n          {% if Moo.annotations(Foo).size == 0 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations on a module\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          @[Foo]\n          module Moo\n          end\n\n          {% if Moo.annotations(Foo).size == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"uses annotations value, positional\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo(1)]\n          @[Foo(2)]\n          module Moo\n          end\n\n          {% if Moo.annotations(Foo)[0][0] == 1 && Moo.annotations(Foo)[1][0] == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"uses annotations value, keyword\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo(x: 1)]\n          @[Foo(x: 2)]\n          module Moo\n          end\n\n          {% if Moo.annotations(Foo)[0][:x] == 1 && Moo.annotations(Foo)[1][:x] == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in class\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          @[Foo]\n          @[Foo]\n          class Moo\n          end\n\n          {% if Moo.annotations(Foo).size == 3 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in struct\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          @[Foo]\n          @[Foo]\n          @[Foo]\n          struct Moo\n          end\n\n          {% if Moo.annotations(Foo).size == 4 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in enum\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          enum Moo\n            A = 1\n          end\n\n          {% if Moo.annotations(Foo).size == 1 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in lib\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo]\n          @[Foo]\n          lib Moo\n            A = 1\n          end\n\n          {% if Moo.annotations(Foo).size == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"can't find annotations in instance var\" do\n        assert_type(<<-CRYSTAL) { char }\n          annotation Foo\n          end\n\n          class Moo\n            @x : Int32 = 1\n\n            def foo\n              {% unless @type.instance_vars.first.annotations(Foo).empty? %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new.foo\n          CRYSTAL\n      end\n\n      it \"can't find annotations in instance var, when other annotations are present\" do\n        assert_type(<<-CRYSTAL) { char }\n          annotation Foo\n          end\n\n          annotation Bar\n          end\n\n          class Moo\n            @[Bar]\n            @x : Int32 = 1\n\n            def foo\n              {% unless @type.instance_vars.first.annotations(Foo).empty? %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new.foo\n          CRYSTAL\n      end\n\n      it \"finds annotations in instance var (declaration)\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          class Moo\n            @[Foo]\n            @[Foo]\n            @x : Int32 = 1\n\n            def foo\n              {% if @type.instance_vars.first.annotations(Foo).size == 2 %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new.foo\n          CRYSTAL\n      end\n\n      it \"finds annotations in instance var (declaration, generic)\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          class Moo(T)\n            @[Foo]\n            @x : T\n\n            def initialize(@x : T)\n            end\n\n            def foo\n              {% if @type.instance_vars.first.annotations(Foo).size == 1 %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new(1).foo\n          CRYSTAL\n      end\n\n      it \"collects annotations values in type\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          @[Foo(1)]\n          module Moo\n          end\n\n          @[Foo(2)]\n          module Moo\n          end\n\n          {% if Moo.annotations(Foo)[0][0] == 1 && Moo.annotations(Foo)[1][0] == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"overrides annotations value in type\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          class Moo\n            @[Foo(1)]\n            @x : Int32 = 1\n          end\n\n          class Moo\n            @[Foo(2)]\n            @x : Int32 = 1\n\n            def foo\n              {% if @type.instance_vars.first.annotations(Foo).size == 1 && @type.instance_vars.first.annotations(Foo)[0][0] == 2 %}\n                1\n              {% else %}\n                'a'\n              {% end %}\n            end\n          end\n\n          Moo.new.foo\n          CRYSTAL\n      end\n\n      it \"adds annotations on def\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo\n          end\n\n          class Moo\n            @[Foo]\n            @[Foo]\n            def foo\n            end\n          end\n\n          {% if Moo.methods.first.annotations(Foo).size == 2 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"can't find annotations on def\" do\n        assert_type(<<-CRYSTAL) { char }\n          annotation Foo\n          end\n\n          class Moo\n            def foo\n            end\n          end\n\n          {% unless Moo.methods.first.annotations(Foo).empty? %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"can't find annotations on def, when other annotations are present\" do\n        assert_type(<<-CRYSTAL) { char }\n          annotation Foo\n          end\n\n          annotation Bar\n          end\n\n          class Moo\n            @[Bar]\n            def foo\n            end\n          end\n\n          {% unless Moo.methods.first.annotations(Foo).empty? %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n\n      it \"finds annotations in generic parent (#7885)\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Ann\n          end\n\n          @[Ann(1)]\n          class Parent(T)\n          end\n\n          class Child < Parent(Int32)\n          end\n\n          {{ Child.superclass.annotations(Ann)[0][0] }}\n          CRYSTAL\n      end\n\n      it \"find annotations on method parameters\" do\n        assert_type(<<-CRYSTAL) { int32 }\n          annotation Foo; end\n          annotation Bar; end\n\n          class Moo\n            def foo(@[Foo] @[Bar] value)\n            end\n          end\n\n          {% if Moo.methods.first.args.first.annotations(Foo).size == 1 %}\n            1\n          {% else %}\n            'a'\n          {% end %}\n          CRYSTAL\n      end\n    end\n  end\n\n  describe \"#annotation\" do\n    it \"can't find annotation in module\" do\n      assert_type(<<-CRYSTAL) { char }\n        annotation Foo\n        end\n\n        module Moo\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"can't find annotation in module, when other annotations are present\" do\n      assert_type(<<-CRYSTAL) { char }\n        annotation Foo\n        end\n\n        annotation Bar\n        end\n\n        @[Bar]\n        module Moo\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation in module\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo]\n        module Moo\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"uses annotation value, positional\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo(1)]\n        module Moo\n        end\n\n        {% if Moo.annotation(Foo)[0] == 1 %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"uses annotation value, keyword\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo(x: 1)]\n        module Moo\n        end\n\n        {% if Moo.annotation(Foo)[:x] == 1 %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation in class\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo]\n        class Moo\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation in struct\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo]\n        struct Moo\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation in enum\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo]\n        enum Moo\n          A = 1\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation in lib\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo]\n        lib Moo\n          A = 1\n        end\n\n        {% if Moo.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"can't find annotation in instance var\" do\n      assert_type(<<-CRYSTAL) { char }\n        annotation Foo\n        end\n\n        class Moo\n          @x : Int32 = 1\n\n          def foo\n            {% if @type.instance_vars.first.annotation(Foo) %}\n              1\n            {% else %}\n              'a'\n            {% end %}\n          end\n        end\n\n        Moo.new.foo\n        CRYSTAL\n    end\n\n    it \"can't find annotation in instance var, when other annotations are present\" do\n      assert_type(<<-CRYSTAL) { char }\n        annotation Foo\n        end\n\n        annotation Bar\n        end\n\n        class Moo\n          @[Bar]\n          @x : Int32 = 1\n\n          def foo\n            {% if @type.instance_vars.first.annotation(Foo) %}\n              1\n            {% else %}\n              'a'\n            {% end %}\n          end\n        end\n\n        Moo.new.foo\n        CRYSTAL\n    end\n\n    it \"finds annotation in instance var (declaration)\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        class Moo\n          @[Foo]\n          @x : Int32 = 1\n\n          def foo\n            {% if @type.instance_vars.first.annotation(Foo) %}\n              1\n            {% else %}\n              'a'\n            {% end %}\n          end\n        end\n\n        Moo.new.foo\n        CRYSTAL\n    end\n\n    it \"finds annotation in instance var (assignment)\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        class Moo\n          @[Foo]\n          @x = 1\n\n          def foo\n            {% if @type.instance_vars.first.annotation(Foo) %}\n              1\n            {% else %}\n              'a'\n            {% end %}\n          end\n        end\n\n        Moo.new.foo\n        CRYSTAL\n    end\n\n    it \"finds annotation in instance var (declaration, generic)\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        class Moo(T)\n          @[Foo]\n          @x : T\n\n          def initialize(@x : T)\n          end\n\n          def foo\n            {% if @type.instance_vars.first.annotation(Foo) %}\n              1\n            {% else %}\n              'a'\n            {% end %}\n          end\n        end\n\n        Moo.new(1).foo\n        CRYSTAL\n    end\n\n    it \"overrides annotation value in type\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        @[Foo(1)]\n        module Moo\n        end\n\n        @[Foo(2)]\n        module Moo\n        end\n\n        {% if Moo.annotation(Foo)[0] == 2 %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"overrides annotation in instance var\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        class Moo\n          @[Foo(1)]\n          @x : Int32 = 1\n        end\n\n        class Moo\n          @[Foo(2)]\n          @x : Int32 = 1\n\n          def foo\n            {% if @type.instance_vars.first.annotation(Foo)[0] == 2 %}\n              1\n            {% else %}\n              'a'\n            {% end %}\n          end\n        end\n\n        Moo.new.foo\n        CRYSTAL\n    end\n\n    it \"errors if annotation doesn't exist\" do\n      assert_error <<-CRYSTAL, \"undefined constant DoesntExist\"\n        @[DoesntExist]\n        class Moo\n        end\n        CRYSTAL\n    end\n\n    it \"errors if annotation doesn't point to an annotation type\" do\n      assert_error <<-CRYSTAL, \"Int32 is not an annotation, it's a struct\"\n        @[Int32]\n        class Moo\n        end\n        CRYSTAL\n    end\n\n    it \"errors if using annotation other than ThreadLocal for class vars\" do\n      assert_error <<-CRYSTAL, \"class variables can only be annotated with ThreadLocal\"\n        annotation Foo\n        end\n\n        class Moo\n          @[Foo]\n          @@x = 0\n        end\n        CRYSTAL\n    end\n\n    it \"adds annotation on def\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Foo\n        end\n\n        class Moo\n          @[Foo]\n          def foo\n          end\n        end\n\n        {% if Moo.methods.first.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"can't find annotation on def\" do\n      assert_type(<<-CRYSTAL) { char }\n        annotation Foo\n        end\n\n        class Moo\n          def foo\n          end\n        end\n\n        {% if Moo.methods.first.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"can't find annotation on def, when other annotations are present\" do\n      assert_type(<<-CRYSTAL) { char }\n        annotation Foo\n        end\n\n        annotation Bar\n        end\n\n        class Moo\n          @[Bar]\n          def foo\n          end\n        end\n\n        {% if Moo.methods.first.annotation(Foo) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"errors if using invalid annotation on fun\" do\n      assert_error <<-CRYSTAL, \"funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention\"\n        annotation Foo\n        end\n\n        @[Foo]\n        fun foo : Void\n        end\n        CRYSTAL\n    end\n\n    it \"doesn't carry link annotation from lib to fun\" do\n      assert_no_errors <<-CRYSTAL\n        @[Link(\"foo\")]\n        lib LibFoo\n          fun foo\n        end\n        CRYSTAL\n    end\n\n    it \"finds annotation in generic parent (#7885)\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Ann\n        end\n\n        @[Ann(1)]\n        class Parent(T)\n        end\n\n        class Child < Parent(Int32)\n        end\n\n        {{ Child.superclass.annotation(Ann)[0] }}\n        CRYSTAL\n    end\n\n    it \"finds annotation on method arg\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Ann; end\n\n        def foo(\n          @[Ann] foo : Int32\n        )\n        end\n\n        {% if @top_level.methods.find(&.name.==(\"foo\")).args.first.annotation(Ann) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation on method splat arg\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Ann; end\n\n        def foo(\n          id : Int32,\n          @[Ann] *nums : Int32\n        )\n        end\n\n        {% if @top_level.methods.find(&.name.==(\"foo\")).args[1].annotation(Ann) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation on method double splat arg\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Ann; end\n\n        def foo(\n          id : Int32,\n          @[Ann] **nums\n        )\n        end\n\n        {% if @top_level.methods.find(&.name.==(\"foo\")).double_splat.annotation(Ann) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n\n    it \"finds annotation on an restricted method block arg\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        annotation Ann; end\n\n        def foo(\n          id : Int32,\n          @[Ann] &block : Int32 ->\n        )\n          yield 10\n        end\n\n        {% if @top_level.methods.find(&.name.==(\"foo\")).block_arg.annotation(Ann) %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n        CRYSTAL\n    end\n  end\n\n  it \"errors when annotate instance variable in subclass\" do\n    assert_error <<-CRYSTAL, \"can't annotate @x in Child because it was first defined in Base\"\n      annotation Foo\n      end\n\n      class Base\n        @x : Nil\n      end\n\n      class Child < Base\n        @[Foo]\n        @x : Nil\n      end\n      CRYSTAL\n  end\n\n  it \"errors if wanting to add type inside annotation (1) (#8614)\" do\n    assert_error <<-CRYSTAL, \"can't declare type inside annotation Ann\"\n      annotation Ann\n      end\n\n      class Ann::Foo\n      end\n\n      Ann::Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if wanting to add type inside annotation (2) (#8614)\" do\n    assert_error <<-CRYSTAL, \"can't declare type inside annotation Ann\"\n      annotation Ann\n      end\n\n      class Ann::Foo::Bar\n      end\n\n      Ann::Foo::Bar.new\n      CRYSTAL\n  end\n\n  it \"doesn't bleed annotation from class into class variable (#8314)\" do\n    assert_no_errors <<-CRYSTAL\n      annotation Attr; end\n\n      @[Attr]\n      class Bar\n        @@x = 0\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/array_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: array\" do\n  it \"types array literal of int\" do\n    assert_type(\"require \\\"prelude\\\"; [1, 2, 3]\") { array_of(int32) }\n  end\n\n  it \"types array literal of union\" do\n    assert_type(\"require \\\"prelude\\\"; [1, 2.5]\") { array_of(union_of int32, float64) }\n  end\n\n  it \"types empty typed array literal of int32\" do\n    assert_type(\"require \\\"prelude\\\"; [] of Int32\") { array_of(int32) }\n  end\n\n  it \"types non-empty typed array literal of int\" do\n    assert_type(\"require \\\"prelude\\\"; [1, 2, 3] of Int32\") { array_of(int32) }\n  end\n\n  it \"types non-empty typed array literal of int\" do\n    assert_type(\"require \\\"prelude\\\"; [1, 2, 3] of Int8\") { array_of(int8) }\n  end\n\n  it \"types array literal size correctly\" do\n    assert_type(\"require \\\"prelude\\\"; [1].size\") { int32 }\n  end\n\n  it \"assignment in array literal works (#3195)\" do\n    assert_type(\"require \\\"prelude\\\"; [a = 1]; a\") { int32 }\n  end\n\n  it \"types array literal with splats\" do\n    assert_type(\"require \\\"prelude\\\"; [1, *{'a', 1}, 2.5]\") { array_of(union_of int32, char, float64) }\n  end\n\n  it \"types array literal with splats (2)\" do\n    assert_type(\"require \\\"prelude\\\"; [1, *{1, 'a'}, 2.5]\") { array_of(union_of int32, char, float64) }\n  end\n\n  it \"types array literal of int with splats\" do\n    assert_type(\"require \\\"prelude\\\"; [1, *{2_i8, 3_i8}, 4] of Int8\") { array_of(int8) }\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/automatic_cast_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: automatic cast\" do\n  it \"casts literal integer (Int32 -> no restriction)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(x)\n        x + 1\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int32 -> Int64)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(x : Int64)\n        x\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int64 -> Int32, ok)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : Int32)\n        x\n      end\n\n      foo(2147483647_i64)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int64 -> Int32, too big)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int32, not Int64\"\n      def foo(x : Int32)\n        x\n      end\n\n      foo(2147483648_i64)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int32 -> Float32)\" do\n    assert_type(<<-CRYSTAL) { float32 }\n      def foo(x : Float32)\n        x\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"casts literal integer (Int32 -> Float64)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(x : Float64)\n        x\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"casts literal float (Float32 -> Float64)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(x : Float64)\n        x\n      end\n\n      foo(1.23_f32)\n      CRYSTAL\n  end\n\n  it \"casts literal float (Float64 -> Float32)\" do\n    assert_type(<<-CRYSTAL) { float32 }\n      def foo(x : Float32)\n        x\n      end\n\n      foo(1.23)\n      CRYSTAL\n  end\n\n  it \"casts literal integer in private top-level method (#7016)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      private def foo(x : Int64)\n        x\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"matches correct overload\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(x : Int32)\n        x\n      end\n\n      def foo(x : Int64)\n        x\n      end\n\n      foo(1_i64)\n      CRYSTAL\n  end\n\n  it \"casts literal integer through alias with union\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      alias A = Int64 | String\n\n      def foo(x : A)\n        x\n      end\n\n      foo(12345)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call for integer\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of 1 matches all of Int8, UInt8, Int16\"\n      def foo(x : Int8)\n        x\n      end\n\n      def foo(x : UInt8)\n        x\n      end\n\n      def foo(x : Int16)\n        x\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call for integer (2)\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of 1 matches all of Int8, UInt8\"\n      def foo(x : Int8 | UInt8)\n        x\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call for integer on alias (#6620)\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of 1 matches all of Int8, UInt8\"\n      alias A = Int8 | UInt8\n\n      def foo(x : A)\n        x\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"casts symbol literal to enum\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      enum Foo\n        One\n        Two\n        Three\n      end\n\n      def foo(x : Foo)\n        x\n      end\n\n      foo(:one)\n      CRYSTAL\n  end\n\n  it \"casts literal integer through alias with union\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      enum Foo\n        One\n        Two\n      end\n\n      alias A = Foo | String\n\n      def foo(x : A)\n        x\n      end\n\n      foo(:two)\n      CRYSTAL\n  end\n\n  it \"errors if symbol name doesn't match enum member\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to match a member of enum Foo\"\n      enum Foo\n        One\n        Two\n        Three\n      end\n\n      def foo(x : Foo)\n        x\n      end\n\n      foo(:four)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call for symbol\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of :one matches all of Foo, Foo2\"\n      enum Foo\n        One\n        Two\n        Three\n      end\n\n      enum Foo2\n        One\n        Two\n        Three\n      end\n\n      def foo(x : Foo)\n        x\n      end\n\n      def foo(x : Foo2)\n        x\n      end\n\n      foo(:one)\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in ivar assignment\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      class Foo\n        @x : Int64\n\n        def initialize\n          @x = 10\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Symbol to Enum in ivar assignment\" do\n    assert_type(<<-CRYSTAL) { types[\"E\"] }\n      enum E\n        One\n        Two\n        Three\n      end\n\n      class Foo\n        @x : E\n\n        def initialize\n          @x = :two\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in cvar assignment\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      class Foo\n        @@x : Int64 = 0_i64\n\n        def self.x\n          @@x = 10\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in lvar assignment\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      x : Int64\n      x = 123\n      x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in ivar type declaration\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      class Foo\n        @x : Int64 = 10\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Symbol to Enum in ivar type declaration\" do\n    assert_type(<<-CRYSTAL) { types[\"Color\"] }\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      class Foo\n        @x : Color = :red\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in cvar type declaration\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      class Foo\n        @@x : Int64 = 10\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"casts Symbol to Enum in cvar type declaration\" do\n    assert_type(<<-CRYSTAL) { types[\"Color\"] }\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      class Foo\n        @@x : Color = :red\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"casts Int32 -> Int64 in arg restriction\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(x : Int64 = 0)\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"casts Int32 to Int64 in ivar type declaration in generic\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      class Foo(T)\n        @x : T = 10\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int64).new.x\n      CRYSTAL\n  end\n\n  it \"can match multiple times with the same argument type (#7578)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(unused, foo : Int64)\n        unused\n      end\n\n      def foo(foo : Int64)\n        foo\n      end\n\n      foo(foo: 1)\n      CRYSTAL\n  end\n\n  it \"doesn't say 'ambiguous call' when there's an exact match for integer (#6601)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Zed#+' to be Char, not Int32\", inject_primitives: true\n      class Zed\n        def +(other : Char)\n        end\n      end\n\n      a = 1 || Zed.new\n      a + 2\n      CRYSTAL\n  end\n\n  it \"doesn't say 'ambiguous call' when there's an exact match for symbol (#6601)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Zed#+' to be Char, not Symbol\"\n      enum Color1\n        Red\n      end\n\n      enum Color2\n        Red\n      end\n\n      struct Int\n        def +(x : Color1)\n        end\n\n        def +(x : Color2)\n        end\n      end\n\n      class Zed\n        def +(other : Char)\n        end\n      end\n\n      a = 1 || Zed.new\n      a + :red\n      CRYSTAL\n  end\n\n  it \"can use automatic cast with `with ... yield` (#7736)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo\n        with 1 yield\n      end\n\n      struct Int32\n        def bar(x : Int64)\n          x\n        end\n      end\n\n      foo do\n        bar(1)\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't do multidispatch if an overload matches exactly (#8217)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(x : Int64)\n        x\n      end\n\n      def foo(*xs : Int64)\n        xs\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"autocasts first argument and second matches without autocast\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def fill(x : Float64, y : Int)\n        x\n      end\n\n      fill(0, 0)\n      CRYSTAL\n  end\n\n  it \"can autocast to union in default value\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def fill(x : Int64 | String = 1)\n        x\n      end\n\n      fill()\n      CRYSTAL\n  end\n\n  it \"can autocast to alias in default value\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      alias X = Int64 | String\n\n      def fill(x : X = 1)\n        x\n      end\n\n      fill()\n      CRYSTAL\n  end\n\n  it \"can autocast to union in default value (symbol and int)\" do\n    assert_type(<<-CRYSTAL) { types[\"Color\"] }\n      enum Color\n        Red\n      end\n\n      def fill(x : Int64 | Color = :red)\n        x\n      end\n\n      fill()\n      CRYSTAL\n  end\n\n  it \"can autocast to union in default value (multiple enums)\" do\n    assert_type(<<-CRYSTAL) { types[\"AnotherColor\"] }\n      enum Color\n        Red\n      end\n\n      enum AnotherColor\n        Blue\n      end\n\n      def fill(x : Color | AnotherColor = :blue)\n        x\n      end\n\n      fill()\n      CRYSTAL\n  end\n\n  it \"doesn't do multidispatch if an overload matches exactly (#8217)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def foo(x : Int64)\n          x\n        end\n\n        def foo(*xs : Int64)\n          xs\n        end\n      end\n\n      class Baz < Foo\n        def foo(x : Int64)\n          x\n        end\n\n        def foo(*xs : Int64)\n          xs\n        end\n      end\n\n      Baz.new.as(Foo).foo(1)\n      CRYSTAL\n  end\n\n  it \"casts integer variable to larger type (#9565)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(x : Int64)\n        x\n      end\n\n      x = 1_i32\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"casts integer variable to larger type (Int64 to Int128) (#9565)\" do\n    assert_type(<<-CRYSTAL) { int128 }\n      def foo(x : Int128)\n        x\n      end\n\n      x = 1_i64\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"casts integer expression to larger type (#9565)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      def foo(x : Int64)\n        x\n      end\n\n      def bar\n        1_i32\n      end\n\n      foo(bar)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call for integer var to larger type (#9565)\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of UInt8 matches all of Int32, Int64\"\n      def foo(x : Int32)\n        x\n      end\n\n      def foo(x : Int64)\n        x\n      end\n\n      x = 1_u8\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call for integer var to union type (#9565)\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of UInt8 matches all of Int32, UInt32\"\n      def foo(x : Int32 | UInt32)\n        x\n      end\n\n      x = 1_u8\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"can't cast integer to another type when it doesn't fit (#9565)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int32, not Int64\"\n      def foo(x : Int32)\n        x\n      end\n\n      x = 1_i64\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"doesn't cast integer variable to larger type (not #9565)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int64, not Int32\", flags: \"no_number_autocast\"\n      def foo(x : Int64)\n        x\n      end\n\n      x = 1_i32\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"doesn't autocast number on union (#8655)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : UInt8 | Int32, y : Float64)\n        x\n      end\n\n      foo(255, 60)\n      CRYSTAL\n  end\n\n  it \"says ambiguous call on union (#8655)\" do\n    assert_error <<-CRYSTAL, \"ambiguous call, implicit cast of 255 matches all of UInt64, Int64\"\n      def foo(x : UInt64 | Int64, y : Float64)\n        x\n      end\n\n      foo(255, 60)\n      CRYSTAL\n  end\n\n  it \"autocasts integer variable to float type (#9565)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(x : Float64)\n        x\n      end\n\n      x = 1_i32\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"autocasts float32 variable to float64 type (#9565)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(x : Float64)\n        x\n      end\n\n      x = 1.0_f32\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"autocasts nested type from non-nested type (#10315)\" do\n    assert_no_errors(<<-CRYSTAL)\n      module Moo\n        enum Color\n          Red\n        end\n\n        abstract class Foo\n          def initialize(color : Color = :red)\n          end\n        end\n      end\n\n      class Bar < Moo::Foo\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"errors when autocast default value doesn't match enum member\" do\n    assert_error <<-CRYSTAL,\n      enum Foo\n        FOO\n      end\n\n      def foo(foo : Foo = :bar)\n      end\n\n      foo\n      CRYSTAL\n      \"can't autocast :bar to Foo: no matching enum member\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/block_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Block inference\" do\n  it \"infer type of empty block body\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo; yield; end\n\n      foo do\n      end\n      CRYSTAL\n  end\n\n  it \"infer type of block body\" do\n    input = parse(<<-CRYSTAL).as(Expressions)\n      def foo; yield; end\n\n      foo do\n        x = 1\n      end\n      CRYSTAL\n    result = semantic input\n    input.last.as(Call).block.not_nil!.body.type.should eq(result.program.int32)\n  end\n\n  it \"infer type of block parameter\" do\n    input = parse(<<-CRYSTAL).as(Expressions)\n      def foo\n        yield 1\n      end\n\n      foo do |x|\n        1\n      end\n      CRYSTAL\n    result = semantic input\n    mod = result.program\n    input.last.as(Call).block.not_nil!.args[0].type.should eq(mod.int32)\n  end\n\n  it \"infer type of local variable\" do\n    assert_type(<<-CRYSTAL) { union_of(char, int32) }\n      def foo\n        yield 1\n      end\n\n      y = 'a'\n      foo do |x|\n        y = x\n      end\n      y\n      CRYSTAL\n  end\n\n  it \"infer type of yield\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"infer type with union\" do\n    assert_type(<<-CRYSTAL) { union_of(array_of(int32), array_of(float64)) }\n      require \"prelude\"\n      a = [1] || [1.1]\n      a.tap { |x| x }\n      CRYSTAL\n  end\n\n  it \"uses block arg, too many parameters\" do\n    assert_error <<-CRYSTAL, \"too many block parameters (given 1, expected maximum 0)\"\n      def foo\n        yield\n      end\n\n      foo do |x|\n        x\n      end\n      CRYSTAL\n  end\n\n  it \"yields with different types\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      def foo\n        yield 1\n        yield 'a'\n      end\n\n      foo do |x|\n        x\n      end\n      CRYSTAL\n  end\n\n  it \"break from block without value\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo; yield; end\n\n      foo do\n        break\n      end\n      CRYSTAL\n  end\n\n  it \"break without value has nil type\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def foo; yield; 1; end\n      foo do\n        break if false\n      end\n      CRYSTAL\n  end\n\n  it \"infers type of block before call\" do\n    result = assert_type(<<-CRYSTAL) { generic_class \"Foo\", float64 }\n      struct Int32\n        def foo\n          10.5\n        end\n      end\n\n      class Foo(T)\n        def initialize(x : T)\n          @x = x\n        end\n      end\n\n      def bar(&block : Int32 -> U) forall U\n        Foo(U).new(yield 1)\n      end\n\n      bar { |x| x.foo }\n      CRYSTAL\n    mod = result.program\n    type = result.node.type.as(GenericClassInstanceType)\n    type.type_vars[\"T\"].type.should eq(mod.float64)\n    type.instance_vars[\"@x\"].type.should eq(mod.float64)\n  end\n\n  it \"infers type of block before call taking other args free vars into account\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", float64 }\n      class Foo(X)\n        def initialize(x : X)\n          @x = x\n        end\n      end\n\n      def foo(x : U, &block: U -> T) forall T, U\n        Foo(T).new(yield x)\n      end\n\n      a = foo(1) do |x|\n        10.5\n      end\n      CRYSTAL\n  end\n\n  it \"reports error if yields a type that's not that one in the block specification\" do\n    assert_error <<-CRYSTAL, \"argument #1 of yield expected to be Int32, not Float64\"\n      def foo(&block: Int32 -> )\n        yield 10.5\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"reports error if yields a type that's not that one in the block specification\" do\n    assert_error <<-CRYSTAL, \"argument #1 of yield expected to be Int32, not (Float64 | Int32)\"\n      def foo(&block: Int32 -> )\n        yield (1 || 1.5)\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"reports error if yields a type that later changes and that's not that one in the block specification\" do\n    assert_error <<-CRYSTAL, \"argument #1 of yield expected to be Int32, not (Float64 | Int32)\"\n      def foo(&block: Int32 -> )\n        a = 1\n        while true\n          yield a\n          a = 1.5\n        end\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"reports error if missing arguments to yield\" do\n    assert_error <<-CRYSTAL, \"wrong number of yield arguments (given 1, expected 2)\"\n      def foo(&block: Int32, Int32 -> )\n        yield 1\n      end\n\n      foo { |x| x }\n      CRYSTAL\n  end\n\n  it \"reports error if block didn't return expected type\" do\n    assert_error <<-CRYSTAL, \"expected block to return Float64, not Char\"\n      def foo(&block: Int32 -> Float64)\n        yield 1\n      end\n\n      foo { 'a' }\n      CRYSTAL\n  end\n\n  it \"reports error if block type doesn't match\" do\n    assert_error <<-CRYSTAL, \"expected block to return Float64, not (Float64 | Int32)\"\n      def foo(&block: Int32 -> Float64)\n        yield 1\n      end\n\n      foo { 1 || 1.5 }\n      CRYSTAL\n  end\n\n  it \"reports error if block changes type\" do\n    assert_error <<-CRYSTAL, \"type must be Float64\"\n      def foo(&block: Int32 -> Float64)\n        yield 1\n      end\n\n      a = 10.5\n      while true\n        foo { a }\n        a = 1\n      end\n      CRYSTAL\n  end\n\n  it \"reports error on method instantiate (#4543)\" do\n    assert_error <<-CRYSTAL, \"expected block to return Int32, not UInt32\"\n      class Foo\n        @foo = 42\n\n        def initialize(&block : -> Int32)\n          @foo = yield\n        end\n      end\n\n      Foo.new { 42u32 }\n      CRYSTAL\n  end\n\n  it \"matches block arg return type\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", float64 }\n      class Foo(T)\n      end\n\n      def foo(&block: Int32 -> Foo(T)) forall T\n        yield 1\n        Foo(T).new\n      end\n\n      foo { Foo(Float64).new }\n      CRYSTAL\n  end\n\n  it \"infers type of block with generic type\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      class Foo(T)\n      end\n\n      def foo(&block: Foo(Int32) -> )\n        yield Foo(Int32).new\n      end\n\n      foo do |x|\n        10.5\n      end\n      CRYSTAL\n  end\n\n  it \"infer type with self block arg\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Foo\"] }\n      class Foo\n        def foo(&block : self -> )\n          yield self\n        end\n      end\n\n      f = Foo.new\n      a = nil\n      f.foo do |x|\n        a = x\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"error with self input type doesn't match\" do\n    assert_error <<-CRYSTAL, \"argument #1 of yield expected to be Foo, not Int32\"\n      class Foo\n        def foo(&block : self -> )\n          yield 1\n        end\n      end\n\n      f = Foo.new\n      f.foo {}\n      CRYSTAL\n  end\n\n  it \"error with self output type doesn't match\" do\n    assert_error <<-CRYSTAL, \"expected block to return Foo, not Int32\"\n      class Foo\n        def foo(&block : Int32 -> self )\n          yield 1\n        end\n      end\n\n      f = Foo.new\n      f.foo { 1 }\n      CRYSTAL\n  end\n\n  it \"errors when using local variable with block parameter name\" do\n    assert_error \"def foo; yield 1; end; foo { |a| }; a\",\n      \"undefined local variable or method 'a'\"\n  end\n\n  it \"types empty block\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo\n        ret = yield\n        ret\n      end\n\n      foo { }\n      CRYSTAL\n  end\n\n  it \"preserves type filters in block\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo\n        def bar\n          'a'\n        end\n      end\n\n      def foo\n        yield 1\n      end\n\n      a = Foo.new || nil\n      if a\n        foo do |x|\n          a.bar\n        end\n      else\n        'b'\n      end\n      CRYSTAL\n  end\n\n  it \"checks block type with virtual type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = [] of Foo\n      a << Bar.new\n\n      a.map { |x| x.to_s }\n\n      1\n      CRYSTAL\n  end\n\n  it \"maps block of union types to union types\" do\n    assert_type(<<-CRYSTAL) { array_of(union_of(types[\"Foo1\"].virtual_type, types[\"Foo2\"].virtual_type)) }\n      require \"prelude\"\n\n      class Foo1\n      end\n\n      class Bar1 < Foo1\n      end\n\n      class Foo2\n      end\n\n      class Bar2 < Foo2\n      end\n\n      a = [Foo1.new, Foo2.new, Bar1.new, Bar2.new]\n      a.map { |x| x }\n      CRYSTAL\n  end\n\n  it \"does next from block without value\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo; yield; end\n\n      foo do\n        next\n      end\n      CRYSTAL\n  end\n\n  it \"does next from block with value\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo; yield; end\n\n      foo do\n        next 1\n      end\n      CRYSTAL\n  end\n\n  it \"does next from block with value 2\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, bool) }\n      def foo; yield; end\n\n      foo do\n        if 1 == 1\n          next 1\n        end\n        false\n      end\n      CRYSTAL\n  end\n\n  it \"ignores block parameter if not used\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(&block)\n        yield 1\n      end\n\n      foo do |x|\n        x + 1\n      end\n      CRYSTAL\n  end\n\n  it \"allows yielding multiple types when a union is expected\" do\n    assert_type(<<-CRYSTAL) { array_of(float64) }\n      require \"prelude\"\n\n      class Foo\n        include Enumerable(Int32 | Float64)\n\n        def each\n          yield 1\n          yield 1.5\n        end\n      end\n\n      foo = Foo.new\n      foo.map &.to_f\n      CRYSTAL\n  end\n\n  it \"allows initialize with yield (#224)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = yield 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new do |a|\n        a + 1\n      end\n      foo.x\n      CRYSTAL\n  end\n\n  it \"passes #233: block with initialize with default args\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize(x = nil)\n          yield\n        end\n      end\n\n      Foo.new {}\n      CRYSTAL\n  end\n\n  it \"errors if declares def inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare def dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        def bar\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares macro inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare macro dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        macro bar\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares fun inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare fun dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        fun bar : Int32\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares class inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare class dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        class Foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares module inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare module dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        module Foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares lib inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare lib dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        lib LibFoo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares alias inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare alias dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        alias A = Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares include inside block\" do\n    assert_error <<-CRYSTAL, \"can't include dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        include Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares extend inside block\" do\n    assert_error <<-CRYSTAL, \"can't extend dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        extend Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares enum inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare enum dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        enum Foo\n          A\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allows alias as block fun type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      alias Alias = Int32 -> Int32\n\n      def foo(&block : Alias)\n        block.call(1)\n      end\n\n      foo do |x|\n        x + 1\n      end\n      CRYSTAL\n  end\n\n  it \"errors if alias is not a fun type\" do\n    assert_error <<-CRYSTAL, \"expected block type to be a function type, not Int32\"\n      alias Alias = Int32\n\n      def foo(&block : Alias)\n        block.call(1)\n      end\n\n      foo do |x|\n        x + 1\n      end\n      CRYSTAL\n  end\n\n  it \"errors if proc is not instantiated\" do\n    assert_error <<-CRYSTAL, \"can't create an instance of generic class Proc(*T, R) without specifying its type vars\"\n      def capture(&block : Proc)\n        block\n      end\n\n      capture { }\n      CRYSTAL\n  end\n\n  it \"passes #262\" do\n    assert_type(<<-CRYSTAL) { array_of(bool) }\n      require \"prelude\"\n\n      h = {} of String => Int32\n      h.map { true }\n      CRYSTAL\n  end\n\n  it \"allows invoking method on a object of a captured block with a type that was never instantiated\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Bar\"], void) }\n      require \"prelude\"\n\n      class Bar\n        def initialize(@bar : NoReturn)\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      def foo(&block : Bar ->)\n        block\n      end\n\n      def method(bar)\n        bar.bar\n      end\n\n      foo do |bar|\n        method(bar).baz\n      end\n      CRYSTAL\n  end\n\n  it \"types bug with yield not_nil! that is never not nil\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable(int32) }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo\n        key = nil\n        if 1 == 2\n          yield LibC.exit\n        end\n        yield 1\n      end\n\n      extra = nil\n\n      foo do |key|\n        if 1 == 1\n          extra = 1\n          extra + key\n        end\n      end\n\n      extra\n      CRYSTAL\n  end\n\n  it \"ignores void return type (#427)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      lib Fake\n        fun foo(func : -> Void)\n      end\n\n      def foo(&block : -> Void)\n        Fake.foo block\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"ignores void return type (2) (#427)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(&block : Int32 -> Void)\n        yield 1\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"ignores void return type (3) (#427)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      alias Alias = Int32 -> Void\n\n      def foo(&block : Alias)\n        yield 1\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"ignores void return type (4)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      alias Alias = Void\n\n      def foo(&block : -> Alias)\n        yield\n      end\n\n      foo do\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"uses block return type as return type, even if can't infer block type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize(@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      def bar(&block : -> Int32)\n        block\n      end\n\n      f = ->(x : Foo) {\n        bar { x.foo }\n      }\n\n      foo = Foo.new(100)\n      block = f.call(foo)\n      block.call\n      CRYSTAL\n  end\n\n  it \"uses block var with same name as local var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield true\n      end\n\n      a = 1\n      foo do |a|\n        a\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types recursive hash assignment\" do\n    assert_type(<<-CRYSTAL) { array_of int32 }\n      require \"prelude\"\n\n      class Hash\n        def map\n          ary = Array(typeof(yield first_key, first_value)).new(@size)\n          each do |k, v|\n            ary.push yield k, v\n          end\n          ary\n        end\n      end\n\n      hash = {} of Int32 => Int32\n      z = hash.map {|key| key + 1 }\n      hash[1] = z.size\n      z\n      CRYSTAL\n  end\n\n  it \"errors if invoking new with block when no initialize is defined\" do\n    assert_error <<-CRYSTAL, \"'Foo.new' is not expected to be invoked with a block, but a block was given\"\n      class Foo\n      end\n\n      Foo.new { }\n      CRYSTAL\n  end\n\n  it \"recalculates call that uses block arg output as free var\" do\n    assert_type(<<-CRYSTAL) { union_of(char, int32).metaclass }\n      def foo(&block : Int32 -> U) forall U\n        block\n        U\n      end\n\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def x=(@x : Char)\n        end\n\n        def bar\n          foo do |x|\n            @x\n          end\n        end\n      end\n\n      z = Foo.new.bar\n      Foo.new.x = 'a'\n      z\n      CRYSTAL\n  end\n\n  it \"finds type inside module in block\" do\n    assert_type(<<-CRYSTAL) { types[\"Moo\"].types[\"Bar\"] }\n      module Moo\n        class Foo\n        end\n\n        class Bar\n          def initialize(&block : Int32 -> U) forall U\n            block\n          end\n        end\n      end\n\n      z = nil\n      module Moo\n        z = Bar.new { Foo.new }\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"passes &->f\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n      end\n\n      def bar(&block)\n        yield\n        1\n      end\n\n      bar &->foo\n      CRYSTAL\n  end\n\n  it \"errors if declares class inside captured block\" do\n    assert_error <<-CRYSTAL, \"can't declare class dynamically\"\n      def foo(&block)\n        block.call\n      end\n\n      foo do\n        class B\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't assign block variable type to last value (#694)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield 1\n      end\n\n      z = 1\n      foo do |x|\n        z = x\n        x = \"a\"\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"errors if yields from top level\" do\n    assert_error <<-CRYSTAL, \"can't use `yield` outside a method\"\n      yield\n      CRYSTAL\n  end\n\n  it \"errors on recursive yield\" do\n    assert_error <<-CRYSTAL, \"recursive block expansion\"\n      def foo\n        yield\n\n        foo do\n        end\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"errors on recursive yield with non ProcNotation restriction (#6896)\" do\n    assert_error <<-CRYSTAL, \"recursive block expansion\"\n      def foo(&block : -> Int32)\n        yield\n\n        foo do\n          1\n        end\n      end\n\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"errors on recursive yield with ProcNotation restriction\" do\n    assert_error <<-CRYSTAL, \"recursive block expansion\"\n      def foo(&block : -> Int32)\n        yield\n\n        foo do\n          1\n        end\n      end\n\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"binds to proc, not only to its body (#1796)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char).metaclass }\n      def yielder(&block : Int32 -> U) forall U\n        yield 1\n        U\n      end\n\n      yielder { next 'a' if true; 1 }\n      CRYSTAL\n  end\n\n  it \"binds block return type free variable even if there are no block parameters (#1797)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def yielder(&block : -> U) forall U\n        yield\n        U\n      end\n\n      yielder { 1 }\n      CRYSTAL\n  end\n\n  it \"returns from proc literal\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of int32, float64 }\n      foo = ->{\n        if 1 == 1\n          return 1\n        end\n\n        1.5\n      }\n\n      foo.call\n      CRYSTAL\n  end\n\n  it \"errors if returns from captured block\" do\n    assert_error <<-CRYSTAL, \"can't return from captured block, use next\"\n      def foo(&block)\n        block\n      end\n\n      def bar\n        foo do\n          return\n        end\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"errors if breaks from captured block\" do\n    assert_error <<-CRYSTAL, \"can't break from captured block, try using `next`.\"\n      def foo(&block)\n        block\n      end\n\n      def bar\n        foo do\n          break\n        end\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"errors if doing next in proc literal\" do\n    assert_error <<-CRYSTAL, \"invalid next\"\n      foo = ->{\n        next\n      }\n      foo.call\n      CRYSTAL\n  end\n\n  it \"does next from captured block\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of int32, float64 }\n      def foo(&block : -> T) forall T\n        block\n      end\n\n      f = foo do\n        if 1 == 1\n          next 1\n        end\n\n        next 1.5\n      end\n\n      f.call\n      CRYSTAL\n  end\n\n  it \"sets captured block type to that of restriction\" do\n    assert_type(<<-CRYSTAL) { proc_of(union_of(int32, string)) }\n      def foo(&block : -> Int32 | String)\n        block\n      end\n\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"sets captured block type to that of restriction with alias\" do\n    assert_type(<<-CRYSTAL) { proc_of(union_of(int32, string)) }\n      alias Alias = -> Int32 | String\n      def foo(&block : Alias)\n        block\n      end\n\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"matches block with generic type and free var\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n      end\n\n      def foo(&block : -> Foo(T)) forall T\n        block\n        T\n      end\n\n      foo { Foo(Int32).new }\n      CRYSTAL\n  end\n\n  it \"doesn't mix local var with block var, using break (#2314)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      def foo\n        yield 1\n      end\n\n      x = true\n      foo do |x|\n        break\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"doesn't mix local var with block var, using next (#2314)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      def foo\n        yield 1\n      end\n\n      x = true\n      foo do |x|\n        next\n      end\n      x\n      CRYSTAL\n  end\n\n  [\"Object\", \"Bar | Object\", \"(Object ->)\", \"( -> Object)\"].each do |string|\n    it \"errors if using #{string} as block return type (#2358)\" do\n      assert_error <<-CRYSTAL, \"use a more specific type\"\n        class Foo(T)\n        end\n\n        class Bar\n        end\n\n        def capture(&block : -> #{string})\n          block\n        end\n\n        capture { 1 }\n        CRYSTAL\n    end\n  end\n\n  it \"yields splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, int32]) }\n      def foo\n        tup = {1, 'a'}\n        yield *tup\n      end\n\n      foo do |x, y|\n        {y, x}\n      end\n      CRYSTAL\n  end\n\n  it \"yields splat and non splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([nilable(char), union_of(int32, bool)]) }\n      def foo\n        tup = {1, 'a'}\n        yield *tup\n\n        yield true, nil\n      end\n\n      foo do |x, y|\n        {y, x}\n      end\n      CRYSTAL\n  end\n\n  it \"uses splat in block parameter\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo\n        yield 1, 'a'\n      end\n\n      foo do |*args|\n        args\n      end\n      CRYSTAL\n  end\n\n  it \"uses splat in block parameter, many args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, tuple_of([char, bool, nil_type]), float64, string]) }\n      def foo\n        yield 1, 'a', true, nil, 1.5, \"hello\"\n      end\n\n      foo do |x, *y, z, w|\n        {x, y, z, w}\n      end\n      CRYSTAL\n  end\n\n  it \"uses splat in block parameter, but not enough yield expressions\" do\n    assert_error <<-CRYSTAL, \"too many block parameters (given 3+, expected maximum 1)\"\n      def foo\n        yield 1\n      end\n\n      foo do |x, y, z, *w|\n        {x, y, z, w}\n      end\n      CRYSTAL\n  end\n\n  it \"errors if splat parameter becomes a union\" do\n    assert_error <<-CRYSTAL, \"yield argument to block splat parameter must be a Tuple\"\n      def foo\n        yield 1\n        yield 1, 2\n      end\n\n      foo do |*args|\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo\n        tup = {1, 'a'}\n        yield tup\n      end\n\n      foo do |x, y|\n        {x, y}\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, less than max\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo\n        tup = {1, 'a', true}\n        yield tup\n      end\n\n      foo do |x, y|\n        {x, y}\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks with block arg type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(&block : {Int32, Int32} -> _)\n        yield({1, 2})\n      end\n\n      foo do |x, y|\n        x + y\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, captured block\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { tuple_of([int32, char]) }\n      def foo(&block : {Int32, Char} -> _)\n        tup = {1, 'a'}\n        block.call tup\n      end\n\n      foo do |x, y|\n        {x, y}\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, captured empty block\" do\n    assert_no_errors <<-CRYSTAL, inject_primitives: true\n      def foo(&block : {Int32, Char} -> _)\n        tup = {1, 'a'}\n        block.call tup\n      end\n\n      foo do |x, y|\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, captured block with multiple statements\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { tuple_of([float64, int32, bool]) }\n      def foo(&block : {Float64, Int32} -> _)\n        tup = {1.0, 3}\n        block.call tup\n      end\n\n      foo do |x, y|\n        z = x < y\n        {x, y, z}\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, less than max, captured block\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { tuple_of([int32, char]) }\n      def foo(&block : {Int32, Char, Bool} -> _)\n        tup = {1, 'a', true}\n        block.call tup\n      end\n\n      foo do |x, y|\n        {x, y}\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't auto-unpack tuple, more args\" do\n    assert_error <<-CRYSTAL, \"too many block parameters (given 3, expected maximum 2)\"\n      def foo\n        tup = {1, 'a'}\n        yield tup, true\n      end\n\n      foo do |x, y, z|\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, too many args\" do\n    assert_error <<-CRYSTAL, \"too many block parameters (given 3, expected maximum 2)\"\n      def foo\n        tup = {1, 'a'}\n        yield tup\n      end\n\n      foo do |x, y, z|\n      end\n      CRYSTAL\n  end\n\n  it \"auto-unpacks tuple, too many args, captured block\" do\n    assert_error <<-CRYSTAL, \"too many block parameters (given 3, expected maximum 2)\"\n      def foo(&block : {Int32, Char} -> _)\n        tup = {1, 'a'}\n        block.call tup\n      end\n\n      foo do |x, y, z|\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #2531\" do\n    run(<<-CRYSTAL).to_i.should eq(10)\n      def foo\n        yield\n      end\n\n      value = true ? 1 : nil\n      foo do\n        value ? nil : nil\n      end\n      value ? 10 : 20\n      CRYSTAL\n  end\n\n  it \"yields in overload, matches type\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, int64) }\n      struct Int\n        def foo(&block : self ->)\n          yield self\n        end\n      end\n\n      (1 || 1_i64).foo do |x|\n        x\n      end\n      CRYSTAL\n  end\n\n  it \"uses free var in return type in captured block\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class U\n      end\n\n      def foo(&block : -> U) forall U\n        block\n        U\n      end\n\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"uses free var in return type with tuple type\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, int32]), tuple_of([int32, int32]).metaclass]) }\n      class T; end\n\n      class U; end\n\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def foo(&block : T -> U) forall U\n          {yield(@x), U}\n        end\n      end\n\n      Foo.new(1).foo { |x| {x, x} }\n      CRYSTAL\n  end\n\n  it \"reports mismatch with generic argument type in output type\" do\n    assert_error(<<-CRYSTAL, \"expected block to return String, not Int32\")\n      class Foo(T)\n        def foo(&block : -> T)\n        end\n      end\n\n      Foo(String).new.foo { 1 }\n      CRYSTAL\n  end\n\n  it \"reports mismatch with generic argument type in input type\" do\n    assert_error(<<-CRYSTAL, \"argument #1 of yield expected to be String, not Int32\")\n      class Foo(T)\n        def foo(&block : T -> )\n          yield 1\n        end\n      end\n\n      Foo(String).new.foo {}\n      CRYSTAL\n  end\n\n  it \"unpacks block argument\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo\n        yield({1, 'a'})\n      end\n\n      foo do |(x, y)|\n        {x, y}\n      end\n      CRYSTAL\n  end\n\n  it \"correctly types unpacked tuple block arg after block (#3339)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield({\"\"})\n      end\n\n      i = 1\n      foo do |(i)|\n\n      end\n      i\n      CRYSTAL\n  end\n\n  it \"can infer block type given that the method has a return type (#7160)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Int32\n        def self.foo\n          0\n        end\n      end\n\n      class Node\n        @child : Node?\n\n        def sum : Int32\n          if child = @child\n            child.call(&.sum)\n          else\n            0\n          end\n        end\n\n        def call(&block : self -> T) forall T\n          T.foo\n        end\n      end\n\n      Node.new.sum\n      CRYSTAL\n  end\n\n  it \"doesn't crash on cleaning up typeof node without dependencies (#8669)\" do\n    assert_no_errors <<-CRYSTAL\n      def foo(&)\n      end\n\n      foo do\n        typeof(bar)\n      end\n      CRYSTAL\n  end\n\n  it \"respects block arg restriction when block has a splat parameter (#6473)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(&block : Int32 ->)\n        yield 1\n      end\n\n      def bar(x)\n        x\n      end\n\n      foo do |*x|\n        bar(*x)\n      end\n      CRYSTAL\n  end\n\n  it \"respects block arg restriction when block has a splat parameter (2) (#9524)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) }\n      def foo(&block : {Int32, Int32} ->)\n        yield({1, 2})\n      end\n\n      def bar(x)\n        x\n      end\n\n      foo do |*x|\n        bar(*x)\n      end\n      CRYSTAL\n  end\n\n  it \"allows underscore in block return type even if the return type can't be computed\" do\n    assert_no_errors <<-CRYSTAL\n      def foo(& : -> _)\n        yield\n      end\n\n      def recursive\n        if true\n          foo { recursive }\n        end\n      end\n\n      recursive\n      CRYSTAL\n  end\n\n  it \"doesn't fail with 'already had enclosing call' (#11200)\" do\n    assert_no_errors <<-CRYSTAL\n      def capture(&block)\n        block\n      end\n\n      abstract class Foo\n      end\n\n      class Bar(Input) < Foo\n        def method\n        end\n\n        def foo\n          capture do\n            self.method\n            Baz(Input)\n          end\n        end\n      end\n\n      class Baz(Input) < Bar(Input)\n      end\n\n      foo = Bar(Bool).new.as(Foo)\n      foo.foo\n      CRYSTAL\n  end\n\n  it \"renders expected block return type of a free variable on mismatch\" do\n    assert_error(<<-CRYSTAL, \"expected block to return Int64, not String\")\n      struct Foo\n        def bar(arg : U, &block : -> U) forall U\n        end\n      end\n\n      Foo.new.bar(1_i64) { \"hi\" }\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/c_enum_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: c enum\" do\n  it \"types enum value\" do\n    assert_type(\"lib LibFoo; enum Bar; X; Y; Z = 10; W; end; end; LibFoo::Bar::X\") { types[\"LibFoo\"].types[\"Bar\"] }\n  end\n\n  it \"allows using an enum as a type in a fun\" do\n    assert_type(<<-CRYSTAL) { types[\"LibC\"].types[\"Foo\"] }\n      lib LibC\n        enum Foo\n          A\n        end\n        fun my_mega_function(y : Foo) : Foo\n      end\n\n      LibC.my_mega_function(LibC::Foo::A)\n      CRYSTAL\n  end\n\n  it \"allows using an enum as a type in a struct\" do\n    assert_type(<<-CRYSTAL) { types[\"LibC\"].types[\"Foo\"] }\n      lib LibC\n        enum Foo\n          A\n        end\n        struct Bar\n          x : Foo\n        end\n      end\n\n      f = LibC::Bar.new\n      f.x = LibC::Foo::A\n      f.x\n      CRYSTAL\n  end\n\n  it \"types enum value with base type\" do\n    assert_type(\"lib LibFoo; enum Bar : Int16; X; end; end; LibFoo::Bar::X\") { types[\"LibFoo\"].types[\"Bar\"] }\n  end\n\n  it \"errors if enum base type is not an integer\" do\n    assert_error \"lib LibFoo; enum Bar : Float32; X; end; end; LibFoo::Bar::X\",\n      \"enum base type must be an integer type\"\n  end\n\n  it \"errors if enum value is different from default (Int32) (#194)\" do\n    assert_error \"lib LibFoo; enum Bar; X = 0x00000001_u32; end; end; LibFoo::Bar::X\",\n      \"enum value must be an Int32\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/c_struct_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: struct\" do\n  it \"types struct\" do\n    result = assert_type(\"lib LibFoo; struct Bar; x : Int32; y : Float64; end; end; LibFoo::Bar\") { types[\"LibFoo\"].types[\"Bar\"].metaclass }\n    mod = result.program\n\n    bar = mod.types[\"LibFoo\"].types[\"Bar\"].as(NonGenericClassType)\n    bar.extern?.should be_true\n    bar.extern_union?.should be_false\n    bar.instance_vars[\"@x\"].type.should eq(mod.int32)\n    bar.instance_vars[\"@y\"].type.should eq(mod.float64)\n  end\n\n  it \"types Struct#new\" do\n    assert_type(\"lib LibFoo; struct Bar; x : Int32; y : Float64; end; end; LibFoo::Bar.new\") do\n      types[\"LibFoo\"].types[\"Bar\"]\n    end\n  end\n\n  it \"types struct setter\" do\n    assert_type(\"lib LibFoo; struct Bar; x : Int32; y : Float64; end; end; bar = LibFoo::Bar.new; bar.x = 1\") { int32 }\n  end\n\n  it \"types struct getter\" do\n    assert_type(\"lib LibFoo; struct Bar; x : Int32; y : Float64; end; end; bar = LibFoo::Bar.new; bar.x\") { int32 }\n  end\n\n  it \"types struct getter to struct\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"LibFoo\"].types[\"Baz\"] }\n      lib LibFoo\n        struct Baz\n          y : Int32\n        end\n        struct Bar\n          x : Baz\n        end\n      end\n      bar = Pointer(LibFoo::Bar).malloc(1_u64)\n      bar.value.x\n      CRYSTAL\n  end\n\n  it \"types struct getter multiple levels via new\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibFoo\n        struct Baz\n          y : Int32\n        end\n        struct Bar\n          x : Baz\n        end\n      end\n      bar = Pointer(LibFoo::Bar).malloc(1_u64)\n      bar.value.x.y\n      CRYSTAL\n  end\n\n  it \"types struct getter with keyword name\" do\n    assert_type(\"lib LibFoo; struct Bar; type : Int32; end; end; bar = LibFoo::Bar.new; bar.type\") { int32 }\n  end\n\n  it \"errors on struct if no field\" do\n    assert_error \"lib LibFoo; struct Bar; x : Int32; end; end; f = LibFoo::Bar.new; f.y = 'a'\",\n      \"undefined method 'y=' for LibFoo::Bar\"\n  end\n\n  it \"errors on struct setter if different type\" do\n    assert_error \"lib LibFoo; struct Bar; x : Int32; end; end; f = LibFoo::Bar.new; f.x = 'a'\",\n      \"field 'x' of struct LibFoo::Bar has type Int32, not Char\"\n  end\n\n  it \"errors on struct setter if different type via new\" do\n    assert_error \"lib LibFoo; struct Bar; x : Int32; end; end; f = Pointer(LibFoo::Bar).malloc(1_u64); f.value.x = 'a'\",\n      \"field 'x' of struct LibFoo::Bar has type Int32, not Char\", inject_primitives: true\n  end\n\n  it \"types struct getter on pointer type\" do\n    assert_type(\"lib LibFoo; struct Bar; x : Int32*; end; end; b = LibFoo::Bar.new; b.x\") { pointer_of(int32) }\n  end\n\n  it \"errors if setting closure\" do\n    assert_error <<-CRYSTAL, \"can't set closure as C struct member\"\n      lib LibFoo\n        struct Bar\n          x : -> Int32\n        end\n      end\n\n      a = 1\n\n      bar = LibFoo::Bar.new\n      bar.x = -> { a }\n      CRYSTAL\n  end\n\n  it \"errors if already defined\" do\n    assert_error <<-CRYSTAL, \"Foo is already defined\"\n      lib LibC\n        struct Foo\n          x : Int32\n        end\n\n        struct Foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if already defined with another type\" do\n    assert_error <<-CRYSTAL, \"Foo is already defined as enum\"\n      lib LibC\n        enum Foo\n          X\n        end\n\n        struct Foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if already defined with another type (2)\" do\n    assert_error <<-CRYSTAL, \"Foo is already defined as union\"\n      lib LibC\n        union Foo\n          x : Int32\n        end\n\n        struct Foo\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allows inline forward declaration\" do\n    assert_type(<<-CRYSTAL) { pointer_of(types[\"LibC\"].types[\"Node\"]) }\n      lib LibC\n        struct Node\n          next : Node*\n        end\n      end\n\n      node = LibC::Node.new\n      node.next\n      CRYSTAL\n  end\n\n  it \"supports macro if inside struct\" do\n    assert_type(<<-CRYSTAL, flags: \"some_flag\") { int32 }\n      lib LibC\n        struct Foo\n          {% if flag?(:some_flag) %}\n            a : Int32\n          {% else %}\n            a : Float64\n          {% end %}\n        end\n      end\n\n      LibC::Foo.new.a\n      CRYSTAL\n  end\n\n  it \"includes another struct\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        struct Foo\n          a : Int32\n        end\n\n        struct Bar\n          include Foo\n        end\n      end\n\n      LibC::Bar.new.a\n      CRYSTAL\n  end\n\n  it \"errors if includes non-cstruct type\" do\n    assert_error <<-CRYSTAL, \"can only include C struct, not union\"\n      lib LibC\n        union Foo\n          a : Int32\n        end\n\n        struct Bar\n          include Foo\n        end\n      end\n\n      LibC::Bar.new.a\n      CRYSTAL\n  end\n\n  it \"errors if includes unknown type\" do\n    assert_error <<-CRYSTAL, \"undefined constant Foo\"\n      lib LibC\n        struct Bar\n          include Foo\n        end\n      end\n\n      LibC::Bar.new.a\n      CRYSTAL\n  end\n\n  it \"errors if includes and field already exists\" do\n    assert_error <<-CRYSTAL, \"struct LibC::Foo has a field named 'a', which LibC::Bar already defines\"\n      lib LibC\n        struct Foo\n          a : Int32\n        end\n\n        struct Bar\n          a : Float64\n          include Foo\n        end\n      end\n\n      LibC::Bar.new.a\n      CRYSTAL\n  end\n\n  it \"errors if includes and field already exists, the other way around\" do\n    assert_error <<-CRYSTAL, \"struct LibC::Bar already defines a field named 'a'\"\n      lib LibC\n        struct Foo\n          a : Int32\n        end\n\n        struct Bar\n          include Foo\n          a : Float64\n        end\n      end\n\n      LibC::Bar.new.a\n      CRYSTAL\n  end\n\n  it \"marks as packed\" do\n    result = semantic(<<-CRYSTAL)\n      lib LibFoo\n        @[Packed]\n        struct Struct\n          x, y : Int32\n        end\n      end\n      CRYSTAL\n    foo_struct = result.program.types[\"LibFoo\"].types[\"Struct\"].as(NonGenericClassType)\n    foo_struct.packed?.should be_true\n  end\n\n  it \"errors on empty c struct (#633)\" do\n    assert_error <<-CRYSTAL, \"empty structs are disallowed\"\n      lib LibFoo\n        struct Struct\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using void in struct field type\" do\n    assert_error <<-CRYSTAL, \"can't use Void as a struct field type\"\n      lib LibFoo\n        struct Struct\n          x : Void\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using void via typedef in struct field type\" do\n    assert_error <<-CRYSTAL, \"can't use Void as a struct field type\"\n      lib LibFoo\n        type MyVoid = Void\n\n        struct Struct\n          x : MyVoid\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can access instance var from the outside (#1092)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      f = LibFoo::Foo.new x: 123\n      f.@x\n      CRYSTAL\n  end\n\n  it \"automatically converts numeric type in struct field assignment\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = 1_u8\n      foo.x\n      CRYSTAL\n  end\n\n  it \"errors if invoking to_i32! and got error in that call\" do\n    assert_error <<-CRYSTAL, \"converting from Foo to Int32 by invoking 'to_i32!'\"\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      class Foo\n        def to_i32!\n          1 + 'a'\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if invoking to_i32! and got wrong type\" do\n    assert_error <<-CRYSTAL, \"invoked 'to_i32!' to convert from Foo to Int32, but got Char\"\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      class Foo\n        def to_i32!\n          'a'\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if invoking to_unsafe and got error in that call\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Int32#+' to be Float32, Float64, Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64 or UInt8, not Char\", inject_primitives: true\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      class Foo\n        def to_unsafe\n          1 + 'a'\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if invoking to_unsafe and got different type\" do\n    assert_error <<-CRYSTAL, \"invoked 'to_unsafe' to convert from Foo to Int32, but got Char\"\n      lib LibFoo\n        struct Foo\n          x : Int32\n        end\n      end\n\n      class Foo\n        def to_unsafe\n          'a'\n        end\n      end\n\n      foo = LibFoo::Foo.new\n      foo.x = Foo.new\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/c_type_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: type\" do\n  it \"can call methods of original type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { uint64 }\n      lib Lib\n        type X = Void*\n        fun foo : X\n      end\n\n      Lib.foo.address\n      CRYSTAL\n  end\n\n  it \"can call methods of parent type\" do\n    assert_error(<<-CRYSTAL, \"undefined method 'baz'\")\n      lib Lib\n        type X = Void*\n        fun foo : X\n      end\n\n      Lib.foo.baz\n      CRYSTAL\n  end\n\n  it \"can access instance variables of original type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib Lib\n        struct X\n          x : Int32\n        end\n\n        type Y = X\n        fun foo : Y\n      end\n\n      Lib.foo.@x\n      CRYSTAL\n  end\n\n  it \"errors if original type doesn't support instance variables\" do\n    assert_error(<<-CRYSTAL, \"can't use instance variables inside primitive types (at Int32)\")\n      lib Lib\n        type X = Int32\n        fun foo : X\n      end\n\n      Lib.foo.@x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/c_union_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: c union\" do\n  it \"types c union\" do\n    result = assert_type(\"lib LibFoo; union Bar; x : Int32; y : Float64; end; end; LibFoo::Bar\") { types[\"LibFoo\"].types[\"Bar\"].metaclass }\n    mod = result.program\n    bar = mod.types[\"LibFoo\"].types[\"Bar\"].as(NonGenericClassType)\n    bar.extern?.should be_true\n    bar.extern_union?.should be_true\n    bar.instance_vars[\"@x\"].type.should eq(mod.int32)\n    bar.instance_vars[\"@y\"].type.should eq(mod.float64)\n  end\n\n  it \"types Union#new\" do\n    assert_type(\"lib LibFoo; union Bar; x : Int32; y : Float64; end; end; LibFoo::Bar.new\") do\n      types[\"LibFoo\"].types[\"Bar\"]\n    end\n  end\n\n  it \"types union setter\" do\n    assert_type(\"lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = LibFoo::Bar.new; bar.x = 1\") { int32 }\n  end\n\n  it \"types union getter\" do\n    assert_type(\"lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = LibFoo::Bar.new; bar.x\") { int32 }\n  end\n\n  it \"types union setter via pointer\" do\n    assert_type(\"lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x = 1\", inject_primitives: true) { int32 }\n  end\n\n  it \"types union getter via pointer\" do\n    assert_type(\"lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x\", inject_primitives: true) { int32 }\n  end\n\n  it \"errors if setting closure\" do\n    assert_error <<-CRYSTAL, \"can't set closure as C union member\"\n      lib LibFoo\n        union Bar\n          x : ->\n        end\n      end\n\n      a = 1\n\n      bar = LibFoo::Bar.new\n      bar.x = -> { a }\n      CRYSTAL\n  end\n\n  it \"errors on empty c union (#633)\" do\n    assert_error <<-CRYSTAL, \"empty unions are disallowed\"\n      lib LibFoo\n        union Struct\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using void in union field type\" do\n    assert_error <<-CRYSTAL, \"can't use Void as a union field type\"\n      lib LibFoo\n        union Struct\n          x : Void\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using void via typedef in union field type\" do\n    assert_error <<-CRYSTAL, \"can't use Void as a union field type\"\n      lib LibFoo\n        type MyVoid = Void\n\n        union Struct\n          x : MyVoid\n        end\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/call_error_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Call errors\" do\n  it \"says wrong number of arguments (to few arguments)\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'foo' (given 0, expected 1)\"\n      def foo(x)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"says wrong number of arguments even if other overloads don't match by block\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'foo' (given 0, expected 1)\"\n      def foo(x)\n      end\n\n      def foo(x, y)\n        yield\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"says not expected to be invoked with a block\" do\n    assert_error <<-CRYSTAL, \"'foo' is not expected to be invoked with a block, but a block was given\"\n      def foo\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"says expected to be invoked with a block\" do\n    assert_error <<-CRYSTAL, \"'foo' is expected to be invoked with a block, but no block was given\"\n      def foo\n        yield\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"says missing named argument\" do\n    assert_error <<-CRYSTAL, \"missing argument: x\"\n      def foo(*, x)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"says missing named arguments\" do\n    assert_error <<-CRYSTAL, \"missing arguments: x, y\"\n      def foo(*, x, y)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"says no parameter named\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'x'\"\n      def foo\n      end\n\n      foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"says no parameters named\" do\n    assert_error <<-CRYSTAL, \"no parameters named 'x', 'y'\"\n      def foo\n      end\n\n      foo(x: 1, y: 2)\n      CRYSTAL\n  end\n\n  it \"says argument already specified\" do\n    assert_error <<-CRYSTAL, \"argument for parameter 'x' already specified\"\n      def foo(x)\n      end\n\n      foo(1, x: 2)\n      CRYSTAL\n  end\n\n  it \"says type mismatch for positional argument\" do\n    assert_error <<-CRYSTAL, \"expected argument #2 to 'foo' to be Int32, not Char\"\n      def foo(x : Int32, y : Int32)\n      end\n\n      foo(1, 'a')\n      CRYSTAL\n  end\n\n  it \"says type mismatch for positional argument with two options\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int32 or String, not Char\"\n      def foo(x : Int32)\n      end\n\n      def foo(x : String)\n      end\n\n      foo('a')\n      CRYSTAL\n  end\n\n  it \"says type mismatch for positional argument with three options\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Bool, Int32 or String, not Char\"\n      def foo(x : Int32)\n      end\n\n      def foo(x : String)\n      end\n\n      def foo(x : Bool)\n      end\n\n      foo('a')\n      CRYSTAL\n  end\n\n  it \"says type mismatch for named argument \" do\n    assert_error <<-CRYSTAL, \"expected argument 'x' to 'foo' to be Int32, not Char\"\n      def foo(x : Int32, y : Int32)\n      end\n\n      foo(y: 1, x: 'a')\n      CRYSTAL\n  end\n\n  it \"replaces free variables in positional argument\" do\n    assert_error <<-CRYSTAL, \"expected argument #2 to 'foo' to be Int32, not Char\"\n      def foo(x : T, y : T) forall T\n      end\n\n      foo(1, 'a')\n      CRYSTAL\n  end\n\n  it \"replaces free variables in named argument\" do\n    assert_error <<-CRYSTAL, \"expected argument 'y' to 'foo' to be Int32, not Char\"\n      def foo(x : T, y : T) forall T\n      end\n\n      foo(x: 1, y: 'a')\n      CRYSTAL\n  end\n\n  it \"replaces generic type var in positional argument\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo(Int32).foo' to be Int32, not Char\"\n      class Foo(T)\n        def self.foo(x : T)\n        end\n      end\n\n      Foo(Int32).foo('a')\n      CRYSTAL\n  end\n\n  it \"replaces generic type var in named argument\" do\n    assert_error <<-CRYSTAL, \"expected argument 'y' to 'Foo(Int32).foo' to be Int32, not Char\"\n      class Foo(T)\n        def self.foo(x : T, y : T)\n        end\n      end\n\n      Foo(Int32).foo(x: 1, y: 'a')\n      CRYSTAL\n  end\n\n  it \"says type mismatch for positional argument even if there are overloads that don't match\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Char or Int32, not String\"\n      def foo(x : Int32)\n      end\n\n      def foo(x : Char)\n      end\n\n      def foo(x : Char, y : Int32)\n      end\n\n      foo(\"hello\")\n      CRYSTAL\n  end\n\n  it \"says type mismatch for symbol against enum (did you mean)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to match a member of enum Color.\\n\\nDid you mean :red?\"\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      def foo(x : Color)\n      end\n\n      foo(:rred)\n      CRYSTAL\n  end\n\n  it \"says type mismatch for symbol against enum (list all possibilities when 10 or less)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to match a member of enum Color.\\n\\nOptions are: :red, :green, :blue, :violet and :purple\"\n      enum Color\n        Red\n        Green\n        Blue\n        Violet\n        Purple\n      end\n\n      def foo(x : Color)\n      end\n\n      foo(:hello_world)\n      CRYSTAL\n  end\n\n  it \"says type mismatch for symbol against enum, named argument case\" do\n    assert_error <<-CRYSTAL, \"expected argument 'x' to 'foo' to match a member of enum Color.\\n\\nDid you mean :red?\"\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      def foo(x : Color)\n      end\n\n      foo(x: :rred)\n      CRYSTAL\n  end\n\n  it \"errors on argument if more types are given than expected\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int32, not (Int32 | Nil)\"\n      def foo(x : Int32)\n      end\n\n      def foo(x : Char)\n      end\n\n      foo(1 || nil)\n      CRYSTAL\n  end\n\n  it \"errors on argument if more types are given than expected, shows all expected types\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Char or Int32, not (Char | Int32 | Nil)\"\n      def foo(x : Int32)\n      end\n\n      def foo(x : Char)\n      end\n\n      foo(1 ? nil : (1 || 'a'))\n      CRYSTAL\n  end\n\n  it \"errors on argument if argument matches in all overloads but with different types in other arguments\" do\n    assert_error <<-CRYSTAL, \"expected argument #2 to 'foo' to be Int32, not (Int32 | Nil)\"\n      def foo(x : String, y : Int32, w : Int32)\n      end\n\n      def foo(x : String, y : Nil, w : Char)\n      end\n\n      foo(\"a\", 1 || nil, 1)\n      CRYSTAL\n  end\n\n  describe \"method signatures in error traces\" do\n    it \"includes named argument\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(y: Int32)'\"\n        def foo(x)\n        end\n\n        def bar(**opts)\n          foo\n        end\n\n        bar(y: 1)\n        CRYSTAL\n    end\n\n    it \"includes named arguments\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(y: Int32, z: String)'\"\n        def foo(x)\n        end\n\n        def bar(**opts)\n          foo\n        end\n\n        bar(y: 1, z: \"\")\n        CRYSTAL\n    end\n\n    it \"includes positional and named argument\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32, y: String)'\"\n        def foo(x)\n        end\n\n        def bar(*args, **opts)\n          foo\n        end\n\n        bar(1, y: \"\")\n        CRYSTAL\n    end\n\n    it \"expands single splat argument\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32)'\"\n        def foo(x)\n        end\n\n        def bar(*args)\n          foo\n        end\n\n        bar(*{1})\n        CRYSTAL\n    end\n\n    it \"expands single splat argument, more elements\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32, String)'\"\n        def foo(x)\n        end\n\n        def bar(*args)\n          foo\n        end\n\n        bar(*{1, \"\"})\n        CRYSTAL\n    end\n\n    it \"expands single splat argument, empty tuple\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar()'\"\n        #{tuple_new}\n\n        def foo(x)\n        end\n\n        def bar(*args)\n          foo\n        end\n\n        bar(*Tuple.new)\n        CRYSTAL\n    end\n\n    it \"expands positional and single splat argument\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32, String)'\"\n        def foo(x)\n        end\n\n        def bar(*args)\n          foo\n        end\n\n        bar(1, *{\"\"})\n        CRYSTAL\n    end\n\n    it \"expands positional and single splat argument, more elements\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32, String, Bool)'\"\n        def foo(x)\n        end\n\n        def bar(*args)\n          foo\n        end\n\n        bar(1, *{\"\", true})\n        CRYSTAL\n    end\n\n    it \"expands positional and single splat argument, empty tuple\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32)'\"\n        #{tuple_new}\n\n        def foo(x)\n        end\n\n        def bar(*args)\n          foo\n        end\n\n        bar(1, *Tuple.new)\n        CRYSTAL\n    end\n\n    it \"expands double splat argument\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(y: Int32)'\"\n        def foo(x)\n        end\n\n        def bar(**opts)\n          foo\n        end\n\n        bar(**{y: 1})\n        CRYSTAL\n    end\n\n    it \"expands double splat argument, more elements\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(y: Int32, z: String)'\"\n        def foo(x)\n        end\n\n        def bar(**opts)\n          foo\n        end\n\n        bar(**{y: 1, z: \"\"})\n        CRYSTAL\n    end\n\n    it \"expands double splat argument, empty named tuple\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar()'\"\n        #{named_tuple_new}\n\n        def foo(x)\n        end\n\n        def bar(**opts)\n          foo\n        end\n\n        bar(**NamedTuple.new)\n        CRYSTAL\n    end\n\n    it \"expands positional and double splat argument\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32, y: String)'\"\n        def foo(x)\n        end\n\n        def bar(*args, **opts)\n          foo\n        end\n\n        bar(1, **{y: \"\"})\n        CRYSTAL\n    end\n\n    it \"expands positional and double splat argument, more elements\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32, y: String, z: Bool)'\"\n        def foo(x)\n        end\n\n        def bar(*args, **opts)\n          foo\n        end\n\n        bar(1, **{y: \"\", z: true})\n        CRYSTAL\n    end\n\n    it \"expands positional and double splat argument, empty named tuple\" do\n      assert_error <<-CRYSTAL, \"instantiating 'bar(Int32)'\"\n        #{named_tuple_new}\n\n        def foo(x)\n        end\n\n        def bar(*args, **opts)\n          foo\n        end\n\n        bar(1, **NamedTuple.new)\n        CRYSTAL\n    end\n\n    it \"uses `T.method` instead of `T.class#method`\" do\n      assert_error <<-CRYSTAL, \"instantiating 'Bar.bar()'\"\n        def foo(x)\n        end\n\n        class Bar\n          def self.bar\n            foo\n          end\n        end\n\n        Bar.bar\n        CRYSTAL\n    end\n\n    it \"uses `T.method` instead of `T:module#method`\" do\n      assert_error <<-CRYSTAL, \"instantiating 'Bar.bar()'\"\n        def foo(x)\n        end\n\n        module Bar\n          def self.bar\n            foo\n          end\n        end\n\n        Bar.bar\n        CRYSTAL\n    end\n  end\nend\n\nprivate def tuple_new\n  <<-CRYSTAL\n    struct Tuple\n      def self.new(*args)\n        args\n      end\n    end\n    CRYSTAL\nend\n\nprivate def named_tuple_new\n  <<-CRYSTAL\n    struct NamedTuple\n      def self.new(**opts)\n        opts\n      end\n    end\n    CRYSTAL\nend\n"
  },
  {
    "path": "spec/compiler/semantic/case_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"semantic: case\" do\n  it \"doesn't check exhaustiveness when using 'when'\" do\n    assert_no_errors <<-CRYSTAL\n      a = 1 || nil\n      case a\n      when Int32\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of single type\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing types:\\n - Int32\"\n      case 1\n      in Nil\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of single type (T.class)\" do\n    assert_no_errors <<-CRYSTAL\n      case Int32\n      in Int32.class\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of single type (Foo(T).class)\" do\n    assert_no_errors <<-CRYSTAL\n      class Foo(T)\n      end\n\n      case Foo(Int32)\n      in Foo(Int32).class\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of single type (generic)\" do\n    assert_no_errors <<-CRYSTAL\n      class Foo(T)\n      end\n\n      case Foo(Int32).new\n      in Foo(Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if casing against a constant\" do\n    assert_error <<-CRYSTAL, \"can't use constant values in exhaustive case, only constant types\"\n      #{bool_case_eq}\n\n      FOO = false\n\n      case true\n      in FOO\n      end\n      CRYSTAL\n  end\n\n  it \"covers all types\" do\n    assert_no_errors <<-CRYSTAL\n      a = 1 || nil\n      case a\n      in Int32\n      in Nil\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of bool type (missing true)\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - true\"\n      #{bool_case_eq}\n\n      case false\n      in false\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of bool type (missing false)\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - false\"\n      #{bool_case_eq}\n\n      case false\n      in true\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of enum via question method\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive for enum Color.\\n\\nMissing members:\\n - Green\\n - Blue\", inject_primitives: true\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red\n      case e\n      in .red?\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of enum via const\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive for enum Color.\\n\\nMissing members:\\n - Green\\n - Blue\"\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red\n      case e\n      in Color::Red\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of enum (all cases covered)\" do\n    assert_no_errors <<-CRYSTAL\n      require \"prelude\"\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red\n      case e\n      in .red?\n      in .green?\n      in .blue?\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of enum through method (all cases covered)\" do\n    assert_no_errors <<-CRYSTAL\n      require \"prelude\"\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      def foo\n        Color::Red\n      end\n\n      case foo\n      in .red?\n      in .green?\n      in .blue?\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of bool type with other types\" do\n    assert_no_errors <<-CRYSTAL\n      #{bool_case_eq}\n\n      case 1 || true\n      in Int32\n      in true\n      in false\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of union type with virtual type\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing types:\\n - Int32\"\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = 1 || Foo.new || Bar.new\n      case a\n      in Foo\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness, covers in base type covers\" do\n    assert_no_errors <<-CRYSTAL\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = Bar.new\n      case a\n      in Foo\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness, covers in base type covers (generic type)\" do\n    assert_no_errors <<-CRYSTAL\n      class Foo(T)\n      end\n\n      a = Foo(Int32).new\n      case a\n      in Foo\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of nil type with nil literal\" do\n    assert_no_errors <<-CRYSTAL\n      struct Nil\n        def ===(other)\n          true\n        end\n      end\n\n      case nil\n      in nil\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of nilable type with nil literal\" do\n    assert_no_errors <<-CRYSTAL\n      struct Nil\n        def ===(other)\n          true\n        end\n      end\n\n      a = 1 || nil\n      case a\n      in nil\n      in Int32\n      end\n      CRYSTAL\n  end\n\n  it \"can't prove case is exhaustive for @[Flags] enum\" do\n    assert_error <<-CRYSTAL, <<-ERROR\n      #{enum_eq}\n\n      struct Enum\n        def includes?(other : self)\n          false\n        end\n      end\n\n      @[Flags]\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red\n      case e\n      in .red?\n      end\n      CRYSTAL\n      case is not exhaustive.\n\n      Missing cases:\n       - Color\n\n      Note that @[Flags] enum can't be proved to be exhaustive by matching against enum members.\n      In particular, the enum Color can't be proved to be exhaustive like that.\n      ERROR\n  end\n\n  it \"can prove case is exhaustive for @[Flags] enum when matching type\" do\n    assert_no_errors <<-CRYSTAL\n      require \"prelude\"\n\n      @[Flags]\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red\n      case e\n      in Color\n      end\n      CRYSTAL\n  end\n\n  it \"can't prove case is exhaustive for @[Flags] enum, tuple case\" do\n    assert_error <<-CRYSTAL, <<-ERROR\n      #{enum_eq}\n\n      struct Enum\n        def includes?(other : self)\n          false\n        end\n      end\n\n      @[Flags]\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red\n      case {e}\n      in {.red?}\n      end\n      CRYSTAL\n      case is not exhaustive.\n\n      Missing cases:\n       - {Color}\n\n      Note that @[Flags] enum can't be proved to be exhaustive by matching against enum members.\n      In particular, the enum Color can't be proved to be exhaustive like that.\n      ERROR\n  end\n\n  it \"checks exhaustiveness of enum combined with another type\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive for enum Color.\\n\\nMissing members:\\n - Green\\n - Blue\", inject_primitives: true\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      e = Color::Red || 1\n      case e\n      in Int32\n      in .red?\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness of union with bool\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - false\\n - Int32\"\n      #{bool_case_eq}\n\n      e = 1 || true\n      case e\n      in true\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal, and passes\" do\n    assert_no_errors <<-CRYSTAL\n      a = 1 || 'a'\n      b = 1 || 'a'\n\n      case {a, b}\n      in {Int32, Char}\n      in {Int32, Int32}\n      in {Char, Int32}\n      in {Char, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal of 2 elements, and warns\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Int32}\"\n      a = 1 || 'a'\n\n      case {a, a}\n      in {Int32, Char}\n      in {Int32, Int32}\n      in {Char, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal of 3 elements, and warns\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Int32, Char}\\n - {Int32, Int32, Char}\"\n      a = 1 || 'a'\n\n      case {a, a, a}\n      in {Int32, Int32, Int32}\n      in {Int32, Char, Int32}\n      in {Int32, Char, Char}\n      in {Char, Int32, Int32}\n      in {Char, Char, Int32}\n      in {Char, Char, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal of 2 elements, first is bool\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {false, Char}\"\n      #{bool_case_eq}\n\n      case {true, 'a'}\n      in {true, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal of 3 elements, all bool\" do\n    assert_error <<-CRYSTAL, <<-ERROR\n      #{bool_case_eq}\n\n      case {true, true, true}\n      in {true, true, true}\n      end\n      CRYSTAL\n      case is not exhaustive.\n\n      Missing cases:\n       - {true, true, false}\n       - {true, false, Bool}\n       - {false, Bool, Bool}\n      ERROR\n  end\n\n  it \"checks exhaustiveness for tuple literal of 2 elements, first is enum\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Color::Green, Char}\", inject_primitives: true\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      case {Color::Red, 'a'}\n      in {.red?, Char}\n      in {.blue?, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal of 3 elements, all enums\" do\n    assert_error <<-CRYSTAL, <<-ERROR, inject_primitives: true\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      case {Color::Red, Color::Red, Color::Red}\n      in {.red?, .green?, .blue?}\n      end\n      CRYSTAL\n      case is not exhaustive.\n\n      Missing cases:\n       - {Color::Red, Color::Red, Color}\n       - {Color::Red, Color::Green, Color::Red}\n       - {Color::Red, Color::Green, Color::Green}\n       - {Color::Red, Color::Blue, Color}\n       - {Color::Green, Color, Color}\n       - {Color::Blue, Color, Color}\n      ERROR\n  end\n\n  it \"checks exhaustiveness for tuple literal with types and underscore at first position\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Char}\\n - {Int32, Char}\"\n      a = 1 || 'a'\n\n      case {a, a}\n      in {_, Int32}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with types and underscore at second position\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Char}\\n - {Char, Int32}\"\n      a = 1 || 'a'\n\n      case {a, a}\n      in {Int32, _}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at first position\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Bool, Char}\"\n      #{bool_case_eq}\n\n      case {true, 1 || 'a'}\n      in {_, Int32}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at first position, with partial match\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {true, Char}\"\n      #{bool_case_eq}\n\n      case {true, 1 || 'a'}\n      in {_, Int32}\n      in {false, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at second position\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Bool}\"\n      #{bool_case_eq}\n\n      case {1 || 'a', true}\n      in {Int32, _}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at second position, with partial match\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, true}\"\n      #{bool_case_eq}\n\n      case {1 || 'a', true}\n      in {Int32, _}\n      in {Char, false}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at first position\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Color, Char}\"\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      case {Color::Red, 1 || 'a'}\n      in {_, Int32}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at first position, partial match\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Color::Red, Char}\\n - {Color::Green, Char}\", inject_primitives: true\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      case {Color::Red, 1 || 'a'}\n      in {_, Int32}\n      in {.blue?, Char}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at second position\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Color}\"\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      case {1 || 'a', Color::Red}\n      in {Int32, _}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal with bool and underscore at second position, partial match\" do\n    assert_error <<-CRYSTAL, \"case is not exhaustive.\\n\\nMissing cases:\\n - {Char, Color::Red}\\n - {Char, Color::Green}\", inject_primitives: true\n      #{enum_eq}\n\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      case {1 || 'a', Color::Red}\n      in {Int32, _}\n      in {Char, .blue?}\n      end\n      CRYSTAL\n  end\n\n  it \"checks exhaustiveness for tuple literal, with call\" do\n    assert_no_errors <<-CRYSTAL\n      struct Int\n        def bar\n          1 || 'a'\n        end\n      end\n\n      foo = 1\n\n      case {foo.bar, foo.bar}\n      in {Int32, Char}\n      in {Int32, Int32}\n      in {Char, Int32}\n      in {Char, Char}\n      end\n      CRYSTAL\n  end\nend\n\nprivate def bool_case_eq\n  <<-CRYSTAL\n  struct Bool\n    def ===(other)\n      true\n    end\n  end\n  CRYSTAL\nend\n\nprivate def enum_eq\n  <<-CRYSTAL\n  struct Enum\n    def ==(other : self)\n      value == other.value\n    end\n\n    def ===(other)\n      true\n    end\n  end\n  CRYSTAL\nend\n"
  },
  {
    "path": "spec/compiler/semantic/cast_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: cast\" do\n  it \"casts to same type is ok\" do\n    assert_type(\"1.as(Int32)\") { int32 }\n  end\n\n  it \"casts to incompatible type gives error\" do\n    assert_error \"1.as(Float64)\",\n      \"can't cast Int32 to Float64\"\n  end\n\n  pending \"casts from union to incompatible union gives error\" do\n    assert_error \"(1 || 1.5).as(Int32 | Char)\",\n      \"can't cast Int32 | Float64 to Int32 | Char\"\n  end\n\n  it \"casts from pointer to generic class gives error\" do\n    assert_error <<-CRYSTAL, \"can't cast Pointer(Int32) to Foo(T)\"\n      class Foo(T)\n      end\n\n      a = 1\n      pointerof(a).as(Foo)\n      CRYSTAL\n  end\n\n  it \"casts from union to compatible union\" do\n    assert_type(\"(1 || 1.5 || 'a').as(Int32 | Float64)\") { union_of(int32, float64) }\n  end\n\n  it \"casts to compatible type and use it\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n      end\n\n      class Bar < Foo\n        def coco\n          1\n        end\n      end\n\n      a = Foo.new || Bar.new\n      b = a.as(Bar)\n      b.coco\n      CRYSTAL\n  end\n\n  it \"casts pointer of one type to another type\" do\n    assert_type(<<-CRYSTAL) { pointer_of(float64) }\n      a = 1\n      p = pointerof(a)\n      p.as(Float64*)\n      CRYSTAL\n  end\n\n  it \"casts pointer to another type\" do\n    assert_type(<<-CRYSTAL) { types[\"String\"] }\n      a = 1\n      p = pointerof(a)\n      p.as(String)\n      CRYSTAL\n  end\n\n  it \"casts to module\" do\n    assert_type(<<-CRYSTAL) { union_of(types[\"Bar\"].virtual_type, types[\"Baz\"].virtual_type) }\n      module Moo\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n\n      class Baz < Foo\n        include Moo\n      end\n\n      f = Foo.new || Bar.new || Baz.new\n      f.as(Moo)\n      CRYSTAL\n  end\n\n  it \"allows casting object to void pointer\" do\n    assert_type(<<-CRYSTAL) { pointer_of(void) }\n      class Foo\n      end\n\n      Foo.new.as(Void*)\n      CRYSTAL\n  end\n\n  it \"allows casting reference union to void pointer\" do\n    assert_type(<<-CRYSTAL) { pointer_of(void) }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Foo.new || Bar.new\n      foo.as(Void*)\n      CRYSTAL\n  end\n\n  it \"disallows casting int to pointer\" do\n    assert_error <<-CRYSTAL, \"can't cast Int32 to Pointer(Void)\"\n      1.as(Void*)\n      CRYSTAL\n  end\n\n  it \"disallows casting fun to pointer\" do\n    assert_error <<-CRYSTAL, \"can't cast Proc(Int32) to Pointer(Void)\"\n      f = ->{ 1 }\n      f.as(Void*)\n      CRYSTAL\n  end\n\n  it \"disallows casting pointer to fun\" do\n    assert_error <<-CRYSTAL, \"can't cast Pointer(Void) to Proc(Int32)\"\n      a = uninitialized Void*\n      a.as(-> Int32)\n      CRYSTAL\n  end\n\n  it \"doesn't error if casting to a generic type\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(T)\n      end\n\n      foo = Foo(Int32).new\n      foo.as(Foo)\n      CRYSTAL\n  end\n\n  it \"casts to base class making it virtual (1)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.as(Foo)\n      CRYSTAL\n  end\n\n  it \"casts to base class making it virtual (2)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          'a'\n        end\n      end\n\n      bar = Bar.new\n      bar.as(Foo).foo\n      CRYSTAL\n  end\n\n  it \"casts to bigger union\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      1.as(Int32 | Char)\n      CRYSTAL\n  end\n\n  it \"errors on cast inside a call that can't be instantiated\" do\n    assert_error <<-CRYSTAL, \"can't cast Int32 to Bool\"\n      def foo(x)\n      end\n\n      foo 1.as(Bool)\n      CRYSTAL\n  end\n\n  it \"casts to target type even if can't infer casted value type (obsolete)\" do\n    assert_type(<<-CRYSTAL) { array_of(int32) }\n      require \"prelude\"\n\n      class Foo\n        property! x : Int32\n      end\n\n      a = [1, 2, 3]\n      b = a.map { Foo.new.x.as(Int32) }\n\n      Foo.new.x = 1\n      b\n      CRYSTAL\n  end\n\n  it \"should error if can't cast even if not instantiated\" do\n    assert_error <<-CRYSTAL, \"can't cast Foo to Bar\"\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      Foo.new.as(Bar)\n      CRYSTAL\n  end\n\n  it \"can cast to metaclass (bug)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      Int32.as(Int32.class)\n      CRYSTAL\n  end\n\n  it \"can cast to metaclass (2) (#11121)\" do\n    assert_type(<<-CRYSTAL) { types[\"A\"].virtual_type.metaclass }\n      class A\n      end\n\n      class B < A\n      end\n\n      A.as(A.class)\n      CRYSTAL\n  end\n\n  # Later we might want casting something to Object to have a meaning\n  # similar to casting to Void*, but for now it's useless.\n  it \"disallows casting to Object (#815)\" do\n    assert_error <<-CRYSTAL, \"can't cast to Object yet\"\n      nil.as(Object)\n      CRYSTAL\n  end\n\n  it \"doesn't allow upcast of generic type var (#996)\" do\n    assert_error <<-CRYSTAL, \"can't cast Gen(Bar) to Gen(Foo)\"\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Gen(T)\n      end\n\n      Gen(Foo).new\n      Gen(Bar).new.as(Gen(Foo))\n      CRYSTAL\n  end\n\n  it \"allows casting NoReturn to any type (#2132)\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      def foo\n        foo\n      end\n\n      foo.as(Int32)\n      CRYSTAL\n  end\n\n  it \"errors if casting nil to Object inside typeof (#2403)\" do\n    assert_error <<-CRYSTAL, \"can't cast to Object yet\"\n      require \"prelude\"\n\n      puts(typeof(nil.as(Object)))\n      CRYSTAL\n  end\n\n  it \"disallows casting to Reference\" do\n    assert_error <<-CRYSTAL, \"can't cast to Reference yet\"\n      \"foo\".as(Reference)\n      CRYSTAL\n  end\n\n  it \"disallows casting to Class\" do\n    assert_error <<-CRYSTAL, \"can't cast to Class yet\"\n      nil.as(Class)\n      CRYSTAL\n  end\n\n  it \"can cast from Void* to virtual type (#3014)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      abstract class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.as(Void*).as(Foo)\n      CRYSTAL\n  end\n\n  it \"casts to generic virtual type\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Foo\", int32).virtual_type! }\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      Bar(Int32).new.as(Foo(Int32))\n      CRYSTAL\n  end\n\n  it \"doesn't cast to virtual primitive (bug)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      1.as(Int)\n      CRYSTAL\n  end\n\n  it \"doesn't crash with typeof no-type (#7441)\" do\n    assert_type(<<-CRYSTAL) { string }\n      a = 1\n      if a.is_a?(Char)\n        1.as(typeof(a))\n      else\n        \"\"\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't cast to unbound generic type (as) (#5927)\" do\n    assert_error <<-CRYSTAL, \"can't cast Int32 to Gen(T)\"\n      class Gen(T)\n        def foo\n          sizeof(T)\n        end\n      end\n\n      class Foo(I)\n        def initialize(@x : Gen(I))\n        end\n      end\n\n      Foo.new(Gen(Int32).new)\n\n      1.as(Gen).foo\n      CRYSTAL\n  end\n\n  it \"doesn't cast to unbound generic type (as?) (#5927)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      class Gen(T)\n        def foo\n          sizeof(T)\n        end\n      end\n\n      class Foo(I)\n        def initialize(@x : Gen(I))\n        end\n      end\n\n      Foo.new(Gen(Int32).new)\n\n      x = 1.as?(Gen)\n      x.foo if x\n      CRYSTAL\n  end\n\n  it \"considers else to be unreachable (#9658)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      case 1\n      in Int32\n        v = 1\n      end\n      v\n      CRYSTAL\n  end\n\n  it \"casts uninstantiated generic class to itself (#10882)\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Bar\"] }\n      class Foo\n      end\n\n      class Bar(T) < Foo\n      end\n\n      x = Foo.new.as(Foo)\n      if x.is_a?(Bar)\n        x.as(Bar)\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't eagerly try to check cast type (#12268)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      bar = 1\n      if bar.is_a?(Char)\n        pointerof(bar).as(Pointer(typeof(bar)))\n      else\n        bar\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/class_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: class\" do\n  it \"types Const#allocate\" do\n    assert_type(\"class Foo; end; Foo.allocate\") { types[\"Foo\"].as(NonGenericClassType) }\n  end\n\n  it \"types Const#new\" do\n    assert_type(\"class Foo; end; Foo.new\") { types[\"Foo\"].as(NonGenericClassType) }\n  end\n\n  it \"types Const#new#method\" do\n    assert_type(\"class Foo; def coco; 1; end; end; Foo.new.coco\") { int32 }\n  end\n\n  it \"types class inside class\" do\n    assert_type(\"class Foo; class Bar; end; end; Foo::Bar.allocate\") { types[\"Foo\"].types[\"Bar\"] }\n  end\n\n  it \"types instance variable\" do\n    result = assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(T)\n        def set\n          @coco = 2\n        end\n      end\n\n      f = Foo(Int32).new\n      f.set\n      f\n      CRYSTAL\n    mod = result.program\n    type = result.node.type.as(GenericClassInstanceType)\n    type.instance_vars[\"@coco\"].type.should eq(mod.nilable(mod.int32))\n  end\n\n  it \"types generic of generic type\" do\n    assert_type(<<-CRYSTAL\n      class Foo(T)\n        def set\n          @coco = 2\n        end\n      end\n\n      f = Foo(Foo(Int32)).new\n      f.set\n      f\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"].as(GenericClassType)\n      foo_i32 = foo.instantiate([int32] of TypeVar)\n      _foo_foo_i32 = foo.instantiate([foo_i32] of TypeVar)\n    end\n  end\n\n  it \"types instance variable\" do\n    input = parse <<-CRYSTAL\n      class Foo(T)\n        def set(value : T)\n          @coco = value\n        end\n      end\n\n      f = Foo(Int32).new\n      f.set 2\n\n      g = Foo(Float64).new\n      g.set 2.5\n      g\n      CRYSTAL\n    result = semantic input\n    mod, node = result.program, result.node.as(Expressions)\n    foo = mod.types[\"Foo\"].as(GenericClassType)\n\n    node[1].type.should eq(foo.instantiate([mod.int32] of TypeVar))\n    node[1].type.instance_vars[\"@coco\"].type.should eq(mod.nilable(mod.int32))\n\n    node[3].type.should eq(foo.instantiate([mod.float64] of TypeVar))\n    node[3].type.instance_vars[\"@coco\"].type.should eq(mod.nilable(mod.float64))\n  end\n\n  it \"types instance variable on getter\" do\n    input = parse(<<-CRYSTAL).as(Expressions)\n      class Foo(T)\n        def set(value : T)\n          @coco = value\n        end\n\n        def get\n          @coco\n        end\n      end\n\n      f = Foo(Int32).new\n      f.set 2\n      f.get\n\n      g = Foo(Float64).new\n      g.set 2.5\n      g.get\n      CRYSTAL\n    result = semantic input\n    mod, node = result.program, result.node.as(Expressions)\n\n    node[3].type.should eq(mod.nilable(mod.int32))\n    input.last.type.should eq(mod.nilable(mod.float64))\n  end\n\n  it \"types recursive type\" do\n    input = parse(<<-CRYSTAL).as(Expressions)\n      class Node\n        def add\n          if next_node = @next\n            next_node.add\n          else\n            @next = Node.new\n          end\n        end\n      end\n\n      n = Node.new\n      n.add\n      n\n      CRYSTAL\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n    node = mod.types[\"Node\"].as(NonGenericClassType)\n\n    node.lookup_instance_var(\"@next\").type.should eq(mod.nilable(node))\n    input.last.type.should eq(node)\n  end\n\n  it \"types self inside method call without obj\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def foo\n          bar\n        end\n\n        def bar\n          self\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"types type var union\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", union_of(int32, float64) }\n      class Foo(T)\n      end\n\n      Foo(Int32 | Float64).new\n      CRYSTAL\n  end\n\n  it \"types class and subclass as one type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = Foo.new || Bar.new\n      CRYSTAL\n  end\n\n  it \"types class and subclass as one type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      a = Bar.new || Baz.new\n      CRYSTAL\n  end\n\n  it \"types class and subclass as one type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      a = Foo.new || Bar.new || Baz.new\n      CRYSTAL\n  end\n\n  it \"does automatic inference of new for generic types\" do\n    result = assert_type(<<-CRYSTAL) { generic_class \"Box\", int32 }\n      class Box(T)\n        def initialize(value : T)\n          @value = value\n        end\n      end\n\n      b = Box.new(10)\n      CRYSTAL\n    mod = result.program\n    type = result.node.type.as(GenericClassInstanceType)\n    type.type_vars[\"T\"].type.should eq(mod.int32)\n    type.instance_vars[\"@value\"].type.should eq(mod.int32)\n  end\n\n  it \"does automatic type inference of new for generic types 2\" do\n    result = assert_type(<<-CRYSTAL) { generic_class \"Box\", bool }\n      class Box(T)\n        def initialize(x, value : T)\n          @value = value\n        end\n      end\n\n      b1 = Box.new(1, 10)\n      b2 = Box.new(1, false)\n      CRYSTAL\n    mod = result.program\n    type = result.node.type.as(GenericClassInstanceType)\n    type.type_vars[\"T\"].type.should eq(mod.bool)\n    type.instance_vars[\"@value\"].type.should eq(mod.bool)\n  end\n\n  it \"does automatic type inference of new for nested generic type\" do\n    nodes = parse(<<-CRYSTAL).as(Expressions)\n      class Foo\n        class Bar(T)\n          def initialize(x : T)\n            @x = x\n          end\n        end\n      end\n\n      Foo::Bar.new(1)\n      CRYSTAL\n    result = semantic nodes\n    mod = result.program\n    type = nodes.last.type.as(GenericClassInstanceType)\n    type.type_vars[\"T\"].type.should eq(mod.int32)\n    type.instance_vars[\"@x\"].type.should eq(mod.int32)\n  end\n\n  it \"reports uninitialized constant\" do\n    assert_error \"Foo.new\",\n      \"undefined constant Foo\"\n  end\n\n  it \"reports undefined method when method inside a class\" do\n    assert_error \"struct Int; def foo; 1; end; end; foo\",\n      \"undefined local variable or method 'foo'\"\n  end\n\n  it \"reports undefined instance method\" do\n    assert_error \"1.foo\",\n      \"undefined method 'foo' for Int\"\n  end\n\n  it \"reports unknown class when extending\" do\n    assert_error \"class Foo < Bar; end\",\n      \"undefined constant Bar\"\n  end\n\n  it \"reports superclass mismatch\" do\n    assert_error \"class Foo; end; class Bar; end; class Foo < Bar; end\",\n      \"superclass mismatch for class Foo (Bar for Reference)\"\n  end\n\n  it \"reports wrong number of arguments for initialize\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments\"\n      class Foo\n        def initialize(x, y)\n        end\n      end\n\n      f = Foo.new\n      CRYSTAL\n  end\n\n  it \"reports can't instantiate abstract class on new\" do\n    assert_error <<-CRYSTAL, \"can't instantiate abstract class Foo\"\n      abstract class Foo; end\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"reports can't instantiate abstract class on allocate\" do\n    assert_error <<-CRYSTAL, \"can't instantiate abstract class Foo\"\n      abstract class Foo; end\n      Foo.allocate\n      CRYSTAL\n  end\n\n  it \"doesn't lookup new in supermetaclass\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(T)\n      end\n\n      Reference.new\n      Foo(Int32).new\n      CRYSTAL\n  end\n\n  it \"errors when wrong arguments for new\" do\n    assert_error \"Reference.new 1\",\n      \"wrong number of arguments\"\n  end\n\n  it \"types virtual method of generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Object\n        def foo\n          bar\n        end\n\n        def bar\n          'a'\n        end\n      end\n\n      class Foo(T)\n        def bar\n          1\n        end\n      end\n\n      Foo(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"allows defining classes inside modules or classes with ::\" do\n    input = parse(<<-CRYSTAL)\n      class Foo\n      end\n\n      class Foo::Bar\n      end\n      CRYSTAL\n    result = semantic input\n    mod = result.program\n    mod.types[\"Foo\"].types[\"Bar\"].as(NonGenericClassType)\n  end\n\n  it \"doesn't lookup type in parents' namespaces, and lookups and in program\" do\n    code = \"\n      class Bar\n      end\n\n      module Mod\n        class Bar\n        end\n\n        class Foo\n          def self.foo(x : Bar)\n            1\n          end\n\n          def self.foo(x : ::Bar)\n            'a'\n          end\n        end\n      end\n      \"\n\n    assert_type(<<-CRYSTAL) { int32 }\n      #{code}\n      Mod::Foo.foo(Mod::Bar.new)\n      CRYSTAL\n\n    assert_type(<<-CRYSTAL) { char }\n      #{code}\n      Mod::Foo.foo(Bar.new)\n      CRYSTAL\n  end\n\n  it \"type def does not reopen type from parent namespace (#11181)\" do\n    assert_type <<-CRYSTAL, inject_primitives: false { types[\"Baz\"].types[\"Foo\"].types[\"Bar\"].metaclass }\n      class Foo::Bar\n      end\n\n      module Baz\n        class Foo::Bar\n        end\n      end\n\n      Baz::Foo::Bar\n      CRYSTAL\n  end\n\n  it \"finds in global scope if includes module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Baz\n      end\n\n      module Foo\n        class Bar\n          include Foo\n\n          Baz\n        end\n      end\n\n      1\n      CRYSTAL\n  end\n\n  it \"allows instantiating generic class with number\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", 1.int32 }\n      class Foo(T)\n      end\n\n      Foo(1).new\n      CRYSTAL\n  end\n\n  it \"uses number type var in class method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def self.foo\n          T\n        end\n      end\n\n      Foo(1).foo\n      CRYSTAL\n  end\n\n  it \"uses self as type var\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", types[\"Bar\"] }\n      class Foo(T)\n      end\n\n      class Bar\n        def self.coco\n          Foo(self)\n        end\n      end\n\n      Bar.coco.new\n      CRYSTAL\n  end\n\n  it \"uses self as type var\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", types[\"Baz\"] }\n      class Foo(T)\n      end\n\n      class Bar\n        def self.coco\n          Foo(self)\n        end\n      end\n\n      class Baz < Bar\n      end\n\n      Baz.coco.new\n      CRYSTAL\n  end\n\n  it \"infers generic type after instance was created with explicit type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo1 = Foo(Bool).new(true)\n      foo2 = Foo.new(1)\n      foo2.x\n      CRYSTAL\n  end\n\n  it \"errors when creating Value\" do\n    assert_error \"Value.allocate\", \"can't instantiate abstract struct Value\"\n  end\n\n  it \"errors when creating Number\" do\n    assert_error \"Number.allocate\", \"can't instantiate abstract struct Number\"\n  end\n\n  it \"reads an object instance var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.@x\n      CRYSTAL\n  end\n\n  it \"reads a virtual type instance var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      foo = Foo.new(1) || Bar.new(2)\n      foo.@x\n      CRYSTAL\n  end\n\n  it \"errors if reading non-existent ivar\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@y' of Foo\"\n      class Foo\n      end\n\n      foo = Foo.new\n      foo.@y\n      CRYSTAL\n  end\n\n  it \"errors if reading ivar from non-ivar container\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables inside primitive types (at Int32)\"\n      1.@y\n      CRYSTAL\n  end\n\n  it \"reads an object instance var from a union type\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar\n        def initialize(@y : Int32, @x : Char)\n        end\n      end\n\n      foo = Foo.new(1)\n      bar = Bar.new(2, 'a')\n      union = foo || bar\n      union.@x\n      CRYSTAL\n  end\n\n  it \"says that instance vars are not allowed in metaclass\" do\n    assert_error <<-CRYSTAL, \"@instance_vars are not yet allowed in metaclasses: use @@class_vars instead\"\n      module Foo\n        def self.foo\n          @foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"doesn't use initialize from base class\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Bar.new' (given 1, expected 2)\"\n      class Foo\n        def initialize(x)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x, y)\n        end\n      end\n\n      Bar.new(1)\n      CRYSTAL\n  end\n\n  it \"doesn't use initialize from base class with virtual type\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Bar#initialize' (given 1, expected 2)\", inject_primitives: true\n      class Foo\n        def initialize(x)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x, y)\n        end\n      end\n\n      klass = 1 == 1 ? Foo : Bar\n      klass.new(1)\n      CRYSTAL\n  end\n\n  it \"errors if using underscore in generic class\" do\n    assert_error <<-CRYSTAL, \"can't use underscore as generic type argument\"\n      class Foo(T)\n      end\n\n      Foo(_).new\n      CRYSTAL\n  end\n\n  it \"types bug #168 (it inherits instance var even if not mentioned in initialize)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Foo\n        def foo\n          x = @x\n          if x\n            x.foo\n          else\n            1\n          end\n        end\n      end\n\n      class Bar < Foo\n        def initialize(@x : Foo)\n        end\n      end\n\n      Bar.new(Foo.new).foo\n      CRYSTAL\n  end\n\n  it \"doesn't mark instance variable as nilable if calling another initialize\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize(x, y)\n          initialize(x)\n        end\n\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(1, 2)\n      foo.x\n      CRYSTAL\n  end\n\n  it \"says wrong number of arguments for abstract class new\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Foo.new' (given 1, expected 0)\"\n      abstract class Foo\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"says wrong number of arguments for abstract class new (2)\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Foo.new' (given 0, expected 1)\"\n      abstract class Foo\n        def initialize(x)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"can't reopen as struct\" do\n    assert_error <<-CRYSTAL, \"Foo is not a struct, it's a class\"\n      class Foo\n      end\n\n      struct Foo\n      end\n      CRYSTAL\n  end\n\n  it \"can't reopen as module\" do\n    assert_error <<-CRYSTAL, \"Foo is not a module, it's a class\"\n      class Foo\n      end\n\n      module Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening non-generic class as generic\" do\n    assert_error <<-CRYSTAL, \"Foo is not a generic class\"\n      class Foo\n      end\n\n      class Foo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic class with different type vars\" do\n    assert_error <<-CRYSTAL, \"type var must be T, not U\"\n      class Foo(T)\n      end\n\n      class Foo(U)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic class with different type vars (2)\" do\n    assert_error <<-CRYSTAL, \"type vars must be A, B, not C\"\n      class Foo(A, B)\n      end\n\n      class Foo(C)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic class with different splat index\" do\n    assert_error <<-CRYSTAL, \"type var must be A, not *A\"\n      class Foo(A)\n      end\n\n      class Foo(*A)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic class with different splat index (2)\" do\n    assert_error <<-CRYSTAL, \"type var must be *A, not A\"\n      class Foo(*A)\n      end\n\n      class Foo(A)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic class with different splat index (3)\" do\n    assert_error <<-CRYSTAL, \"type vars must be *A, B, not A, *B\"\n      class Foo(*A, B)\n      end\n\n      class Foo(A, *B)\n      end\n      CRYSTAL\n  end\n\n  it \"allows declaring a variable in an initialize and using it\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize\n          @x = uninitialized Int32\n          @x + 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"allows using self in class scope\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def self.foo\n          1\n        end\n\n        @@x = self.foo.as(Int32)\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"can't use implicit initialize if defined in parent\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Bar.new' (given 0, expected 1)\"\n      class Foo\n        def initialize(x)\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"doesn't error on new on abstract virtual type class\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      ptr = Pointer(Foo.class).malloc(1_u64)\n      ptr.value = Bar\n      bar = ptr.value.new(1)\n      bar.x\n      CRYSTAL\n  end\n\n  it \"says no overload matches for class new\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo.new' to be Int32, not Char\"\n      class Foo\n        def self.new(x : Int32)\n        end\n      end\n\n      Foo.new 'a'\n      CRYSTAL\n  end\n\n  it \"correctly types #680\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def initialize(@method : Int32?)\n        end\n\n        def method\n          @method\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          super(method)\n        end\n      end\n\n      Bar.new.method\n      CRYSTAL\n  end\n\n  it \"correctly types #680 (2)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@method' of Foo must be Int32, not Nil\"\n      class Foo\n        def initialize(@method : Int32)\n        end\n\n        def method\n          @method\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          super(method)\n        end\n      end\n\n      Bar.new.method\n      CRYSTAL\n  end\n\n  it \"can invoke method on abstract type without subclasses nor instances\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      abstract class Foo\n      end\n\n      a = [] of Foo\n      a.each &.foo\n      1\n      CRYSTAL\n  end\n\n  it \"can invoke method on abstract generic type without subclasses nor instances\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      abstract class Foo(T)\n      end\n\n      a = [] of Foo(Int32)\n      a.each &.foo\n      1\n      CRYSTAL\n  end\n\n  it \"can invoke method on abstract generic type with subclasses but no instances\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      abstract class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n        def foo\n        end\n      end\n\n      a = [] of Foo(Int32)\n      a.each &.foo\n      1\n      CRYSTAL\n  end\n\n  it \"doesn't crash on instance variable assigned a proc, and never instantiated (#923)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      class Klass\n        def self.f(arg)\n        end\n\n        @a  : Proc(String, Nil) = ->f(String)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declares class inside if\" do\n    assert_error <<-CRYSTAL, \"can't declare class dynamically\"\n      if 1 == 2\n        class Foo; end\n      end\n      CRYSTAL\n  end\n\n  it \"can mark initialize as private\" do\n    assert_error <<-CRYSTAL, \"private method 'new' called for Foo\"\n      class Foo\n        private def initialize\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if creating instance before typing instance variable\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32\"\n      class Foo\n        Foo.new\n\n        @x : Int32\n\n        def initialize\n          @x = false\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if assigning superclass to declared instance var\" do\n    assert_error <<-CRYSTAL, \"instance variable '@bar' of Main must be Bar\"\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Main\n        @bar : Bar\n\n        def initialize\n          @bar = Foo.new\n        end\n      end\n\n      Main.new\n      CRYSTAL\n  end\n\n  it \"hoists instance variable initializer\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = Foo.new.bar + 1\n\n      class Foo\n        @bar = 1\n\n        def bar\n          @bar\n        end\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't mix classes on definition (#2352)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Baz\n      end\n\n      class Moo::Baz::B\n        def self.foo\n          1\n        end\n      end\n\n      Moo::Baz::B.foo\n      CRYSTAL\n  end\n\n  it \"errors if using read-instance-var with non-typed variable\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@foo' of Foo\"\n      class Foo\n        def foo\n          @foo\n        end\n      end\n\n      f = Foo.new\n      f.@foo\n      CRYSTAL\n  end\n\n  it \"doesn't crash with top-level initialize (#2601)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def initialize\n        1\n      end\n\n      initialize\n      CRYSTAL\n  end\n\n  it \"inherits self (#2890)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].metaclass }\n      class Foo\n        class Bar < self\n        end\n      end\n\n      {{Foo::Bar.superclass}}\n      CRYSTAL\n  end\n\n  it \"inherits Gen(self) (#2890)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].metaclass }\n      class Gen(T)\n        def self.t\n          T\n        end\n      end\n\n      class Foo\n        class Bar < Gen(self)\n        end\n      end\n\n      Foo::Bar.t\n      CRYSTAL\n  end\n\n  it \"errors if inheriting Gen(self) and there's no self (#2890)\" do\n    assert_error <<-CRYSTAL, \"there's no self in this scope\"\n      class Gen(T)\n        def self.t\n          T\n        end\n      end\n\n      class Bar < Gen(self)\n      end\n\n      Bar.t\n      CRYSTAL\n  end\n\n  it \"preserves order of instance vars (#3050)\" do\n    result = semantic(<<-CRYSTAL)\n      class Foo\n        @x = uninitialized Int32\n        @y : Int32\n\n        def initialize(@y)\n        end\n      end\n      CRYSTAL\n    instance_vars = result.program.types[\"Foo\"].instance_vars.to_a.map(&.[0])\n    instance_vars.should eq(%w(@x @y))\n  end\n\n  it \"errors if inherits from module\" do\n    assert_error <<-CRYSTAL, \"Moo is not a class, it's a module\"\n      module Moo\n      end\n\n      class Foo < Moo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if inherits from metaclass\" do\n    assert_error <<-CRYSTAL, \"Foo.class is not a class, it's a metaclass\"\n      class Foo\n      end\n\n      alias FooClass = Foo.class\n\n      class Bar < FooClass\n      end\n      CRYSTAL\n  end\n\n  it \"can use short name for top-level type\" do\n    assert_type(<<-CRYSTAL) { types[\"T\"] }\n      class T\n      end\n\n      T.new\n      CRYSTAL\n  end\n\n  it \"errors on no method found on abstract class, class method (#2241)\" do\n    assert_error <<-CRYSTAL, \"undefined method 'bar' for Foo.class\"\n      abstract class Foo\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"inherits self twice (#5495)\" do\n    assert_type(<<-CRYSTAL) { tuple_of [types[\"Foo\"].metaclass, types[\"Foo\"].metaclass] }\n      class Foo\n        class Bar < self\n        end\n\n        class Baz < self\n        end\n      end\n\n      { {{ Foo::Bar.superclass }}, {{ Foo::Baz.superclass }} }\n      CRYSTAL\n  end\n\n  it \"types as no return if calling method on abstract class with all abstract subclasses (#6996)\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      require \"prelude\"\n\n      abstract class Foo\n        abstract def foo?\n      end\n\n      abstract class Bar < Foo\n      end\n\n      Pointer(Foo).malloc(1_u64).value.foo?\n      CRYSTAL\n  end\n\n  it \"types as no return if calling method on abstract class with generic subclasses but no instances (#6996)\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      require \"prelude\"\n\n      abstract class Foo\n        abstract def foo?\n      end\n\n      class Bar(T) < Foo\n        def foo?\n          true\n        end\n      end\n\n      Pointer(Foo).malloc(1_u64).value.foo?\n      CRYSTAL\n  end\n\n  it \"types as no return if calling method on abstract generic class (#6996)\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      require \"prelude\"\n\n      abstract class Foo(T)\n        abstract def foo?\n      end\n\n      Pointer(Foo(Int32)).malloc(1_u64).value.foo?\n      CRYSTAL\n  end\n\n  it \"types as no return if calling method on generic class with subclasses (#6996)\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      require \"prelude\"\n\n      abstract class Foo(T)\n        abstract def foo?\n      end\n\n      abstract class Bar(T) < Foo(T)\n      end\n\n      Bar(Int32)\n\n      Pointer(Foo(Int32)).malloc(1_u64).value.foo?\n      CRYSTAL\n  end\n\n  it \"doesn't try to instantiate abstract generic struct when iterating subtypes (#9621)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      abstract struct Base; end\n      struct A < Base; end\n      struct B < Base; end\n      abstract struct Gen(T) < Base; end\n      struct C < Gen(Int32); end\n\n      [A, B].each &.new\n\n      1\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/class_var_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: class var\" do\n  it \"declares class variable\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo.x=' to be Int32, not Bool\"\n      class Foo\n        @@x : Int32\n        @@x = 1\n\n        def self.x=(x)\n          @@x = x\n        end\n      end\n\n      Foo.x = true\n      CRYSTAL\n  end\n\n  it \"declares class variable (2)\" do\n    assert_error <<-CRYSTAL, \"class variable '@@x' of Foo is not nilable (it's Int32) so it must have an initializer\"\n      class Foo\n        @@x : Int32\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n  it \"types class var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @@foo = 1\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"types class var as nil if not assigned at the top level\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def self.foo\n          @@foo = 1\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"types class var inside instance method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @@foo = 1\n\n        def foo\n          @@foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"types class var inside proc literal inside class\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        @@foo = 1\n        f = -> { @@foo }\n      end\n      f.call\n      CRYSTAL\n  end\n\n  it \"allows self.class as type var in class body (#537)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", types[\"Foo\"].virtual_type.metaclass }\n      class Bar(T)\n      end\n\n      class Foo\n        @@bar = Bar(self.class).new\n\n        def self.bar\n          @@bar\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"errors if using self as type var but there's no self\" do\n    assert_error <<-CRYSTAL, \"there's no self in this scope\"\n      class Bar(T)\n      end\n\n      Bar(self).new\n      CRYSTAL\n  end\n\n  it \"allows class var in primitive types (#612)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Int64\n        @@foo = 1\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Int64.foo\n      CRYSTAL\n  end\n\n  it \"declares class var in generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @@bar = 1\n\n        def bar\n          @@bar\n        end\n      end\n\n      Foo(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"declares class var in generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo(T)\n        @@bar = 1\n\n        def self.bar\n          @@bar\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"types class var as nil if assigned for the first time inside method (#2059)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def self.foo\n          @@foo = 1\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"redefines class variable type\" do\n    assert_type(<<-CRYSTAL) { union_of int32, float64 }\n      class Foo\n        @@x : Int32\n        @@x : Int32 | Float64\n        @@x = 1\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"infers type from number literal\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @@x = 1\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"infers type from T.new\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].types[\"Bar\"] }\n      class Foo\n        class Bar\n        end\n\n        @@x = Bar.new\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"says undefined class variable\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of class variable '@@foo' of Foo\"\n      class Foo\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"errors if using class variable at the top level\" do\n    assert_error <<-CRYSTAL, \"can't use class variables at the top level\"\n      @@foo = 1\n      @@foo\n      CRYSTAL\n  end\n\n  it \"errors when typing a class variable inside a method\" do\n    assert_error <<-CRYSTAL, \"declaring the type of a class variable must be done at the class level\"\n      def foo\n        @@x : Int32\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors if using local variable in initializer\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'a'\"\n      class Foo\n        @@x : Int32\n\n        a = 1\n        @@x = a\n      end\n      CRYSTAL\n  end\n\n  it \"errors on undefined constant (1)\" do\n    assert_error <<-CRYSTAL, \"undefined constant Bar\"\n      class Foo\n        def self.foo\n          @@x = Bar.new\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"errors on undefined constant (2)\" do\n    assert_error <<-CRYSTAL, \"undefined constant Bar\"\n      class Foo\n        @@x = Bar.new\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"infers in multiple assign for tuple type (1)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def self.foo\n          @@x, @@y = Bar.method\n        end\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Bar\n        def self.method : {Int32, Bool}\n          {1, true}\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"errors when using Class (#2605)\" do\n    assert_error <<-CRYSTAL, \"can't use Class as the type of class variable '@@class' of Foo, use a more specific type\"\n      class Foo\n        def foo(@@class : Class)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"gives correct error when trying to use Int as a class variable type\" do\n    assert_error <<-CRYSTAL, \"can't use Int as the type of a class variable yet, use a more specific type\"\n      class Foo\n        @@x : Int\n      end\n      CRYSTAL\n  end\n\n  it \"can find class var in subclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @@var = 1\n      end\n\n      class Bar < Foo\n        def self.var\n          @@var\n        end\n      end\n\n      Bar.var\n      CRYSTAL\n  end\n\n  it \"can find class var through included module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @@var = 1\n      end\n\n      class Bar\n        include Moo\n\n        def self.var\n          @@var\n        end\n      end\n\n      Bar.var\n      CRYSTAL\n  end\n\n  it \"errors if redefining class var type in subclass\" do\n    assert_error <<-CRYSTAL, \"class variable '@@x' of Bar is already defined as Int32 in Foo\"\n      class Foo\n        @@x : Int32\n      end\n\n      class Bar < Foo\n        @@x : Float64\n      end\n      CRYSTAL\n  end\n\n  it \"errors if redefining class var type in subclass, with guess\" do\n    assert_error <<-CRYSTAL, \"class variable '@@x' of Bar is already defined as Int32 in Foo\"\n      class Foo\n        @@x = 1\n      end\n\n      class Bar < Foo\n        @@x = 'a'\n      end\n      CRYSTAL\n  end\n\n  it \"errors if redefining class var type in included module\" do\n    assert_error <<-CRYSTAL, \"class variable '@@x' of Bar is already defined as Int32 in Moo\"\n      module Moo\n        @@x : Int32\n      end\n\n      class Bar\n        include Moo\n\n        @@x : Float64\n      end\n      CRYSTAL\n  end\n\n  it \"declares uninitialized (#2935)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @@x = uninitialized Int32\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't error if accessing class variable before defined (#2941)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Bar\n        @@x : Baz = Foo.x\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Foo\n        @@x = Baz.new\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Baz\n        def y\n          1\n        end\n      end\n\n      Bar.x.y\n      CRYSTAL\n  end\n\n  it \"doesn't error on recursive dependency if var is nilable (#2943)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        @@foo : Int32?\n        @@foo = Foo.bar\n\n        def self.bar\n          @@foo\n        end\n\n        def self.foo\n          @@foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"types as nilable if doesn't have initializer\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def self.x\n          @@x = 1\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"errors if class variable not nilable without initializer\" do\n    assert_error <<-CRYSTAL, \"class variable '@@foo' of Foo is not nilable (it's Int32) so it must have an initializer\"\n      class Foo\n        @@foo : Int32\n      end\n      CRYSTAL\n  end\n\n  it \"can assign to class variable if this type can be up-casted to ancestors class variable type (#4869)\" do\n    assert_type(<<-CRYSTAL) { nilable(int32) }\n      class Foo\n        @@x : Int32?\n\n        def self.x\n          @@x\n        end\n      end\n\n      class Bar < Foo\n        @@x = 42\n      end\n\n      Bar.x\n      CRYSTAL\n  end\n\n  it \"can access constant from generic metaclass (#3719)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo(T)\n        @@x = 0\n\n        def self.inc\n          @@x += 1\n          @@x\n        end\n      end\n\n      Foo(Int32).inc\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/cleanup_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate def assert_after_cleanup(before, after)\n  node = Parser.parse(before)\n  result = semantic node\n  result.node.to_s.strip.should eq(after.strip)\nend\n\ndescribe \"cleanup\" do\n  it \"errors if assigning var to itself\" do\n    assert_error \"a = 1; a = a\", \"expression has no effect\"\n  end\n\n  it \"errors if assigning instance var to itself\" do\n    assert_error <<-CRYSTAL, \"expression has no effect\"\n      class Foo\n        def initialize\n          @a = 1; @a = @a\n        end\n      end\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"strip tuple elements after unreachable element\" do\n    assert_after_cleanup \"{1, while true; end, 2}\", \"1\\nwhile true\\nend\"\n  end\n\n  it \"strip named-tuple elements after unreachable element\" do\n    assert_after_cleanup \"{foo: 1, bar: while true; end, baz: 2}\", \"1\\nwhile true\\nend\"\n  end\n\n  # it \"errors comparison of unsigned integer with zero or negative literal\" do\n  #   error = \"comparison of unsigned integer with zero or negative literal will always be false\"\n  #   assert_error \"1_u32 < 0\", error\n  #   assert_error \"1_u32 <= -1\", error\n  #   assert_error \"0 > 1_u32\", error\n  #   assert_error \"-1 >= 1_u32\", error\n  # end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/closure_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: closure\" do\n  it \"gives error when doing yield inside proc literal\" do\n    assert_error \"-> { yield }\", \"can't use `yield` outside a method\"\n  end\n\n  it \"gives error when doing yield inside proc literal\" do\n    assert_error \"def foo; -> { yield }; end; foo {}\", \"can't use `yield` inside a proc literal or captured block\"\n  end\n\n  it \"marks variable as closured in program\" do\n    result = assert_type(\"x = 1; -> { x }; x\") { int32 }\n    program = result.program\n    var = program.vars[\"x\"]\n    var.closured?.should be_true\n  end\n\n  it \"marks variable as closured in program on assign\" do\n    result = assert_type(\"x = 1; -> { x = 1 }; x\") { int32 }\n    program = result.program\n    var = program.vars[\"x\"]\n    var.closured?.should be_true\n  end\n\n  it \"marks variable as closured in def\" do\n    result = assert_type(\"def foo; x = 1; -> { x }; 1; end; foo\") { int32 }\n    node = result.node.as(Expressions)\n    call = node.expressions.last.as(Call)\n    target_def = call.target_def\n    var = target_def.vars.not_nil![\"x\"]\n    var.closured?.should be_true\n  end\n\n  it \"marks variable as closured in block\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield\n      end\n\n      foo do\n        x = 1\n        -> { x }\n        1\n      end\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions.last.as(Call)\n    block = call.block.not_nil!\n    var = block.vars.not_nil![\"x\"]\n    var.closured?.should be_true\n  end\n\n  it \"unifies types of closured var (1)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      a = 1\n      f = -> { a }\n      a = 2.5\n      a\n      CRYSTAL\n  end\n\n  it \"unifies types of closured var (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, float64) }\n      a = 1\n      f = -> { a }\n      a = 2.5\n      f.call\n      CRYSTAL\n  end\n\n  it \"marks variable as closured inside block in fun\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield\n      end\n\n      a = 1\n      -> { foo { a } }\n      a\n      CRYSTAL\n    program = result.program\n    var = program.vars.not_nil![\"a\"]\n    var.closured?.should be_true\n  end\n\n  it \"doesn't mark var as closured if only used in block\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      x = 1\n\n      def foo\n        yield\n      end\n\n      foo { x }\n      CRYSTAL\n    program = result.program\n    var = program.vars[\"x\"]\n    var.closured?.should be_false\n  end\n\n  it \"doesn't mark var as closured if only used in two block\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield\n      end\n\n      foo do\n        x = 1\n        foo do\n          x\n        end\n      end\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node[1].as(Call)\n    block = call.block.not_nil!\n    var = block.vars.not_nil![\"x\"]\n    var.closured?.should be_false\n  end\n\n  it \"doesn't mark self var as closured, but marks method as self closured\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          -> { self }\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    target_def = call.target_def\n    var = target_def.vars.not_nil![\"self\"]\n    var.closured?.should be_false\n    target_def.self_closured?.should be_true\n  end\n\n  it \"marks method as self closured if instance var is read\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32?\n\n        def foo\n          -> { @x }\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    call.target_def.self_closured?.should be_true\n  end\n\n  it \"marks method as self closured if instance var is written\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          -> { @x = 1 }\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    call.target_def.self_closured?.should be_true\n  end\n\n  it \"marks method as self closured if explicit self call is made\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          -> { self.bar }\n        end\n\n        def bar\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    call.target_def.self_closured?.should be_true\n  end\n\n  it \"marks method as self closured if implicit self call is made\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          -> { bar }\n        end\n\n        def bar\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    call.target_def.self_closured?.should be_true\n  end\n\n  it \"marks method as self closured if used inside a block\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      def bar\n        yield\n      end\n\n      class Foo\n        def foo\n          ->{ bar { self } }\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    call.target_def.self_closured?.should be_true\n  end\n\n  it \"errors if sending closured proc literal to C\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: a)\"\n      lib LibC\n        fun foo(callback : ->)\n      end\n\n      a = 1\n      LibC.foo(-> { a })\n      CRYSTAL\n  end\n\n  it \"errors if sending closured proc pointer to C (1)\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: self)\"\n      lib LibC\n        fun foo(callback : ->)\n      end\n\n      class Foo\n        def foo\n          LibC.foo(->bar)\n        end\n\n        def bar\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if sending closured proc pointer to C (1.2)\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: self)\"\n      lib LibC\n        fun foo(callback : ->)\n      end\n\n      class Foo\n        def foo\n          LibC.foo(->{ bar })\n        end\n\n        def bar\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if sending closured proc pointer to C (2)\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: foo)\"\n      lib LibC\n        fun foo(callback : ->)\n      end\n\n      class Foo\n        def bar\n        end\n      end\n\n      foo = Foo.new\n      LibC.foo(->foo.bar)\n      CRYSTAL\n  end\n\n  it \"errors if sending closured proc pointer to C (3)\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: @a)\"\n      lib LibC\n        fun foo(callback : ->)\n      end\n\n      class Foo\n        def initialize\n          @a = 1\n        end\n\n        def foo\n          LibC.foo(->{ @a })\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"transforms block to proc literal\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def foo(&block : Int32 -> Float64)\n        block.call(1)\n      end\n\n      foo do |x|\n        x.to_f\n      end\n      CRYSTAL\n  end\n\n  it \"transforms block to proc literal with void type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nil_type }\n      def foo(&block : Int32 -> )\n        block.call(1)\n      end\n\n      foo do |x|\n        x.to_f\n      end\n      CRYSTAL\n  end\n\n  it \"errors when transforming block to proc literal if type mismatch\" do\n    assert_error <<-CRYSTAL, \"expected block to return Int32, not Float64\", inject_primitives: true\n      def foo(&block : Int32 -> Int32)\n        block.call(1)\n      end\n\n      foo do |x|\n        x.to_f\n      end\n      CRYSTAL\n  end\n\n  it \"transforms block to proc literal with free var\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def foo(&block : Int32 -> U) forall U\n        block.call(1)\n      end\n\n      foo do |x|\n        x.to_f\n      end\n      CRYSTAL\n  end\n\n  it \"transforms block to proc literal without parameters\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def foo(&block : -> U) forall U\n        block.call\n      end\n\n      foo do\n        1.5\n      end\n      CRYSTAL\n  end\n\n  it \"errors if giving more block args when transforming block to proc literal\" do\n    assert_error <<-CRYSTAL, \"wrong number of block parameters (given 1, expected 0)\"\n      def foo(&block : -> U)\n        block.call\n      end\n\n      foo do |x|\n        x.to_f\n      end\n      CRYSTAL\n  end\n\n  it \"allows giving less block args when transforming block to proc literal\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def foo(&block : Int32 -> U) forall U\n        block.call(1)\n      end\n\n      foo do\n        1.5\n      end\n      CRYSTAL\n  end\n\n  it \"allows passing block as proc literal to new and to initialize\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { proc_of(int32, float64) }\n      class Foo\n        def initialize(&block : Int32 -> Float64)\n          @block = block\n        end\n\n        def block\n          @block\n        end\n      end\n\n      foo = Foo.new { |x| x.to_f }\n      foo.block\n      CRYSTAL\n  end\n\n  it \"errors if forwarding block param doesn't match input type\" do\n    assert_error <<-CRYSTAL, \"expected block argument's parameter #1 to be Int32, not Int64\", inject_primitives: true\n      def foo(&block : Int32 -> U)\n        block\n      end\n\n      f = ->(x : Int64) { x + 1 }\n      foo &f\n      CRYSTAL\n  end\n\n  it \"errors if forwarding block param doesn't match input type size\" do\n    assert_error <<-CRYSTAL, \"wrong number of block argument's parameters (given 1, expected 2)\", inject_primitives: true\n      def foo(&block : Int32, Int32 -> U)\n        block\n      end\n\n      f = ->(x : Int32) { x + 1 }\n      foo &f\n      CRYSTAL\n  end\n\n  it \"lookups return type in correct scope\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { proc_of(int32, float64) }\n      module Mod\n        def foo(&block : Int32 -> T) forall T\n          block\n        end\n      end\n\n      class Foo(T)\n        include Mod\n      end\n\n      Foo(Int32).new.foo { |x| x.to_f }\n      CRYSTAL\n  end\n\n  it \"passes #227\" do\n    result = assert_type(<<-CRYSTAL) { proc_of(proc_of(int32)) }\n      ->{ a = 1; ->{ a } }\n      CRYSTAL\n    fn = result.node.as(ProcLiteral)\n    fn.def.closure?.should be_false\n  end\n\n  it \"marks outer fun inside a block as closured\" do\n    result = assert_type(<<-CRYSTAL) { proc_of(proc_of(int32)) }\n      def foo\n        yield\n      end\n\n      a = 1\n      ->{ ->{ foo { a } } }\n      CRYSTAL\n    fn = result.node.as(Expressions).last.as(ProcLiteral)\n    fn.def.closure?.should be_true\n  end\n\n  it \"marks outer fun as closured when using self\" do\n    result = assert_type(<<-CRYSTAL) { proc_of(proc_of(types[\"Foo\"])) }\n      class Foo\n        def foo\n          ->{ ->{ self } }\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n    call = result.node.as(Expressions).last.as(Call)\n    a_def = call.target_def\n    a_def.self_closured?.should be_true\n    fn = (a_def.body.as(ProcLiteral))\n    fn.def.closure?.should be_true\n  end\n\n  it \"can use fun typedef as block type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { proc_of(int32, int32) }\n      lib LibC\n        alias F = Int32 -> Int32\n      end\n\n      def foo(&block : LibC::F)\n        block\n      end\n\n      foo { |x| x + 1 }\n      CRYSTAL\n  end\n\n  it \"says can't send closure to C with new notation\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: a)\"\n      struct Proc\n        def self.new(&block : self)\n          block\n        end\n      end\n\n      lib LibC\n        fun foo(x : ->)\n      end\n\n      a = 1\n      LibC.foo(Proc(Void).new do\n        a\n      end)\n      CRYSTAL\n  end\n\n  it \"says can't send closure to C with captured block\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: a)\"\n      def capture(&block : -> Int32)\n        block\n      end\n\n      lib LibC\n        fun foo(x : ->)\n      end\n\n      a = 1\n      LibC.foo(capture do\n        a\n      end)\n      CRYSTAL\n  end\n\n  it \"doesn't crash for non-existing variable (#3789)\" do\n    assert_error <<-CRYSTAL, \"can't send closure to C function (closured vars: x)\"\n      lib LibFoo\n        fun foo(->)\n      end\n\n      x = 0\n      LibFoo.foo(->{\n        x = ->(data : Int32) {\n          data\n        }\n      })\n      CRYSTAL\n  end\n\n  it \"doesn't closure typeof local var\" do\n    result = assert_type(\"x = 1; -> { typeof(x) }; x\") { int32 }\n    program = result.program\n    var = program.vars[\"x\"]\n    var.closured?.should be_false\n  end\n\n  it \"doesn't closure typeof instance var (#9479)\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32?\n\n        def foo\n          -> { typeof(@x) }\n        end\n      end\n\n      Foo.new.foo\n      1\n      CRYSTAL\n    node = result.node.as(Expressions)\n    call = node.expressions[-2].as(Call)\n    call.target_def.self_closured?.should be_false\n  end\n\n  it \"correctly detects previous var as closured (#5609)\" do\n    assert_error <<-CRYSTAL, \"undefined method '&+' for String\", inject_primitives: true\n      def block(&block)\n        block.call\n      end\n      def times\n        yield\n        yield\n      end\n      x = 1\n      times do\n        if x.is_a?(Int32)\n          x &+ 2\n        end\n        block do\n          x = \"hello\"\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't assign all types to metavar if closured but only assigned to once\" do\n    assert_no_errors <<-CRYSTAL, inject_primitives: true\n      def capture(&block)\n        block\n      end\n      x = 1 == 2 ? 1 : nil\n      if x\n        capture do\n          x &+ 1\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"does assign all types to metavar if closured but only assigned to once in a loop\" do\n    assert_error <<-CRYSTAL, \"undefined method '&+'\", inject_primitives: true\n      def capture(&block)\n        block\n      end\n      while 1 == 1\n        x = 1 == 2 ? 1 : nil\n        if x\n          capture do\n            x &+ 1\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"does assign all types to metavar if closured but only assigned to once in a loop through block\" do\n    assert_error <<-CRYSTAL, \"undefined method '&+'\", inject_primitives: true\n      def capture(&block)\n        block\n      end\n\n      def loop\n        while 1 == 1\n          yield\n        end\n      end\n\n      x = 1\n      loop do\n        x = 1 == 2 ? 1 : nil\n        if x\n          capture do\n            x &+ 1\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"does assign all types to metavar if closured but only assigned to once in a loop through captured block\" do\n    assert_error <<-CRYSTAL, \"undefined method '&+'\", inject_primitives: true\n      def capture(&block)\n        block\n      end\n\n      def loop(&block)\n        while 1 == 1\n          block.call\n        end\n      end\n\n      x = 1\n      loop do\n        x = 1 == 2 ? 1 : nil\n        if x\n          capture do\n            x &+ 1\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't assign all types to metavar if closured but declared inside block and never re-assigned\" do\n    assert_no_errors <<-CRYSTAL, inject_primitives: true\n      def capture(&block)\n        block\n      end\n\n      def loop(&block)\n        yield\n      end\n\n      loop do\n        x = 1 == 2 ? 1 : nil\n        if x\n          capture do\n            x &+ 1\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't assign all types to metavar if closured but declared inside block and re-assigned inside the same context before the closure\" do\n    assert_no_errors <<-CRYSTAL, inject_primitives: true\n      def capture(&block)\n        block\n      end\n\n      def loop(&block)\n        yield\n      end\n\n      loop do\n        x = 1 == 2 ? 1 : nil\n        x = 1 == 2 ? 1 : nil\n        if x\n          capture do\n            x &+ 1\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"is considered as closure if assigned once but comes from a method arg\" do\n    assert_error <<-CRYSTAL, \"undefined method '&+'\"\n      def capture(&block)\n        block\n      end\n      def foo(x)\n        capture do\n          x &+ 1\n        end\n        x = 1 == 2 ? 1 : nil\n      end\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"considers var as closure-readonly if it was assigned multiple times before it was closured\" do\n    assert_no_errors(<<-CRYSTAL, inject_primitives: true)\n      def capture(&block)\n        block\n      end\n\n      x = \"hello\"\n      x = 1\n\n      capture do\n        x &+ 1\n      end\n      CRYSTAL\n  end\n\n  it \"correctly captures type of closured block arg\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      def capture(&block)\n        block.call\n      end\n      def foo\n        yield nil\n      end\n      z = nil\n      foo do |x|\n        capture do\n          x = 1\n        end\n        z = x\n      end\n      z\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/concrete_types_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate def assert_concrete_types(str, &)\n  result = semantic(\"struct Witness;end\\n\\n#{str}\")\n  program = result.program\n\n  type, expected_concrete_types = yield program.types, program\n\n  if type.responds_to?(:concrete_types)\n    Set.new(type.concrete_types).should eq(Set.new(expected_concrete_types))\n  elsif type.is_a?(ModuleType) || type.is_a?(GenericModuleInstanceType)\n    # Modules are not MultiType, we check only using the witness\n  else\n    fail \"#{type} : #{type.class} does not responds to :concrete_types\"\n  end\n\n  # We enforce that the same results are expected for a concrete types\n  # with respect a union of Witness. See UnionType#each_concrete_type\n  witness_type = program.types[\"Witness\"]\n  wrapped_union = program.union_of([witness_type] of Type + (type.is_a?(UnionType) ? type.union_types : [type] of Type)).as(UnionType)\n  Set.new(wrapped_union.concrete_types).should eq(Set.new(expected_concrete_types).add(witness_type))\nend\n\ndescribe \"Semantic: concrete_types\" do\n  it \"UnionType of structs\" do\n    assert_concrete_types(%(\n      struct Foo\n      end\n\n      struct Bar\n      end\n    )) do |t, p|\n      {p.union_of(t[\"Foo\"], t[\"Bar\"]), [t[\"Foo\"], t[\"Bar\"]]}\n    end\n  end\n\n  it \"VirtualType with abstract base\" do\n    assert_concrete_types(%(\n      abstract class Base\n      end\n\n      class Foo < Base\n      end\n\n      class Bar < Base\n      end\n    )) do |t|\n      {t[\"Base\"].virtual_type, [t[\"Foo\"], t[\"Bar\"]]}\n    end\n  end\n\n  it \"VirtualType with concrete base\" do\n    assert_concrete_types(%(\n      class Base\n      end\n\n      class Foo < Base\n      end\n\n      class Bar < Base\n      end\n    )) do |t|\n      {t[\"Base\"].virtual_type, [t[\"Base\"], t[\"Foo\"], t[\"Bar\"]]}\n    end\n  end\n\n  it \"VirtualMetaclassType with abstract base\" do\n    assert_concrete_types(%(\n      abstract class Base\n      end\n\n      class Foo < Base\n      end\n\n      class Bar < Base\n      end\n    )) do |t|\n      # abstract base class are required because the metaclass can always be used: Base.method\n      {t[\"Base\"].virtual_type.metaclass, [t[\"Base\"].metaclass, t[\"Foo\"].metaclass, t[\"Bar\"].metaclass]}\n    end\n  end\n\n  it \"VirtualMetaclassType with concrete base\" do\n    assert_concrete_types(%(\n      class Base\n      end\n\n      class Foo < Base\n      end\n\n      class Bar < Base\n      end\n    )) do |t|\n      {t[\"Base\"].virtual_type.metaclass, [t[\"Base\"].metaclass, t[\"Foo\"].metaclass, t[\"Bar\"].metaclass]}\n    end\n  end\n\n  it \"ModuleType\" do\n    assert_concrete_types(%(\n      module M\n      end\n\n      class Foo\n        include M\n      end\n\n      class Bar\n        include M\n      end\n    )) do |t|\n      {t[\"M\"], [t[\"Foo\"], t[\"Bar\"]]}\n    end\n  end\n\n  it \"GenericModuleInstanceType\" do\n    assert_concrete_types(%(\n      module M(T)\n      end\n\n      class A\n      end\n\n      class B\n      end\n\n      class Q\n      end\n\n      class Foo\n        include M(A)\n      end\n\n      class Bar\n        include M(A)\n      end\n\n      class Baz\n        include M(B)\n      end\n\n      # Q is used to so the remove_indirection has a GenericModuleInstanceType\n      alias Anchor = M(A) | Q\n    )) do |t, p|\n      {t[\"Anchor\"].remove_indirection, [t[\"Q\"], t[\"Foo\"], t[\"Bar\"]]}\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/const_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: const\" do\n  it \"types a constant\" do\n    input = parse(\"CONST = 1\").as(Assign)\n    semantic input\n    input.target.type?.should be_nil # Don't type value until needed\n  end\n\n  it \"types a constant reference\" do\n    assert_type(\"CONST = 1; CONST\") { int32 }\n  end\n\n  it \"types a nested constant\" do\n    assert_type(\"class Foo; A = 1; end; Foo::A\") { int32 }\n  end\n\n  it \"types a constant using Path\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      Foo::Bar = 1\n\n      Foo::Bar\n      CRYSTAL\n  end\n\n  it \"types a nested constant using Path\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        Bar::Baz = 1\n      end\n\n      Foo::Bar::Baz\n      CRYSTAL\n  end\n\n  it \"types a nested type with same name\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        class Foo\n          A = 1\n        end\n      end\n\n      Foo::Foo::A\n      CRYSTAL\n  end\n\n  it \"creates container module if not exist when using Path\" do\n    assert_type(<<-CRYSTAL\n      Foo::Bar = 1\n      Foo\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"]\n      foo.module?.should be_true\n      foo.metaclass\n    end\n  end\n\n  it \"keeps type of container when using Path\" do\n    assert_type(<<-CRYSTAL\n      class Foo\n      end\n\n      Foo::Const = 1\n      Foo\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"]\n      foo.class?.should be_true\n      foo.metaclass\n    end\n\n    assert_type(<<-CRYSTAL\n      struct Foo\n      end\n\n      Foo::Const = 1\n      Foo\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"]\n      foo.struct?.should be_true\n      foo.metaclass\n    end\n\n    assert_type(<<-CRYSTAL\n      module Foo\n      end\n\n      Foo::Const = 1\n      Foo\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"]\n      foo.module?.should be_true\n      foo.metaclass\n    end\n  end\n\n  it \"types a constant inside a def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        A = 1\n\n        def foo\n          A\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"finds nearest constant first\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      CONST = 1\n\n      class Foo\n        CONST = 2.5\n\n        def foo\n          CONST\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"finds current type first\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        class Bar\n          def self.foo\n            Bar.new.foo\n          end\n\n          def foo\n            1\n          end\n        end\n      end\n\n      Foo::Bar.foo\n      CRYSTAL\n  end\n\n  it \"finds current type before parents (#4086)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        class Bar\n          class Baz < Foo\n            def self.foo\n              Baz.new.foo\n            end\n\n            def foo\n              1\n            end\n          end\n        end\n\n        class Baz\n        end\n      end\n\n      Foo::Bar::Baz.foo\n      CRYSTAL\n  end\n\n  it \"doesn't count parent types as current type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n      end\n\n      class Bar\n        class Foo\n          def foo\n            1\n          end\n        end\n\n        class Baz < Foo\n          def self.bar\n            Foo.new\n          end\n        end\n      end\n\n      Bar::Baz.bar.foo\n      CRYSTAL\n  end\n\n  it \"finds current type only for first path item (1)\" do\n    assert_error <<-CRYSTAL, \"undefined constant Foo::Foo\"\n      class Foo\n        def self.foo\n          Foo::Foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"finds current type only for first path item (2)\" do\n    assert_error <<-CRYSTAL, \"undefined constant Foo::Foo\"\n      class Foo\n        class Foo\n        end\n\n        def self.foo\n          Foo::Foo\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"types a global constant reference in method\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      FOO = 2.5\n\n      class Bar\n        FOO = 1\n\n        def foo\n          ::FOO\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"types a global constant reference in static method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      CONST = 2.5\n\n      class Bar\n        CONST = 1\n\n        def self.foo\n          CONST\n        end\n      end\n\n      Bar.foo\n      CRYSTAL\n  end\n\n  it \"doesn't share variables with global scope\" do\n    assert_error \"a = 1; CONST = a; CONST\",\n      \"undefined local variable or method 'a'\"\n  end\n\n  it \"finds const from restriction\" do\n    assert_type(<<-CRYSTAL) { char }\n      struct Int32\n        FOO = 'a'\n      end\n\n      def foo(x : U) forall U\n        U::FOO\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"doesn't crash with const used in initialize (bug)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      COCO = init_coco\n\n      def init_coco\n        1\n      end\n\n      class Foo\n        def initialize\n          COCO\n        end\n      end\n\n      Foo.new\n\n      COCO\n      CRYSTAL\n  end\n\n  it \"finds constant in module that includes module (#205)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      module Foo\n        CONSTANT = true\n      end\n\n      module Moo\n        include Foo\n      end\n\n      Moo::CONSTANT\n      CRYSTAL\n  end\n\n  it \"finds constant in class that extends class (#205)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      class Foo\n        CONSTANT = true\n      end\n\n      class Bar < Foo\n      end\n\n      Bar::CONSTANT\n      CRYSTAL\n  end\n\n  [\"nil\", \"true\", \"1\", \"'a'\", %(\"foo\"), \"+ 1\", \"- 2\", \"~ 2\",\n   \"1 + 2\", \"1 + ZED\", \"ZED - 1\", \"ZED * 2\", \"ZED // 2\",\n   \"1 &+ ZED\", \"ZED &- 1\", \"ZED &* 2\"].each do |node|\n    it \"doesn't errors if constant depends on another one defined later through method, but constant is simple (#{node})\" do\n      assert_no_errors <<-CRYSTAL, inject_primitives: true\n        ZED = 10\n\n        struct Int32\n          def +; 0; end\n          def ~; 0; end\n          def -; 0; end\n          def //(other); 0; end\n        end\n\n        CONST1 = foo\n        CONST2 = #{node}\n\n        def foo\n          CONST2\n        end\n\n        CONST1\n        CRYSTAL\n    end\n  end\n\n  it \"doesn't error if using c enum\" do\n    assert_type(<<-CRYSTAL) { types[\"LibC\"].types[\"Foo\"] }\n      lib LibC\n        enum Foo\n          A = 1\n        end\n      end\n\n      LibC::Foo::A\n      CRYSTAL\n  end\n\n  it \"errors on dynamic constant assignment inside block\" do\n    assert_error <<-CRYSTAL, \"can't declare constant dynamically\"\n      def foo\n        yield\n      end\n\n      foo do\n        CONST = 1\n      end\n      CRYSTAL\n  end\n\n  it \"errors on dynamic constant assignment inside if\" do\n    assert_error <<-CRYSTAL, \"can't declare constant dynamically\"\n      if 1 == 1\n        CONST = 1\n      end\n      CRYSTAL\n  end\n\n  it \"can use constant defined later (#2906)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      FOO = Foo.new\n\n      class Foo\n        A = Bar.new\n\n        def initialize\n          A\n        end\n      end\n\n      class Bar\n      end\n\n      FOO\n      CRYSTAL\n  end\n\n  it \"errors if can't infer constant type (#3240, #3948)\" do\n    assert_error <<-CRYSTAL, \"can't infer type of constant A\"\n      A = A.b\n      A\n      CRYSTAL\n  end\n\n  it \"errors if using constant as generic type (#3240)\" do\n    assert_error <<-CRYSTAL, \"Foo is not a type, it's a constant\"\n      Foo = Foo(Int32).new\n      Foo\n      CRYSTAL\n  end\n\n  it \"errors if using const in type declaration\" do\n    assert_error <<-CRYSTAL, \"A is not a type, it's a constant\"\n      A = 1\n\n      class Foo\n        @x : A\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using const in uninitialized\" do\n    assert_error <<-CRYSTAL, \"A is not a type, it's a constant\"\n      A = 1\n\n      x = uninitialized A\n      CRYSTAL\n  end\n\n  it \"errors if using const in var declaration\" do\n    assert_error <<-CRYSTAL, \"A is not a type, it's a constant\"\n      A = 1\n\n      x : A\n      CRYSTAL\n  end\n\n  it \"errors if using const in restriction\" do\n    assert_error <<-CRYSTAL, \"A is not a type, it's a constant\"\n      A = 1\n\n      def foo(x : A)\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"errors if using const in proc notation parameter type\" do\n    assert_error <<-CRYSTAL, \"A is not a type, it's a constant\"\n      A = 1\n\n      x : A ->\n      CRYSTAL\n  end\n\n  it \"errors if using const in proc notation return type\" do\n    assert_error <<-CRYSTAL, \"A is not a type, it's a constant\"\n      A = 1\n\n      x : -> A\n      CRYSTAL\n  end\n\n  it \"errors if using return inside constant value (#5391)\" do\n    assert_error <<-CRYSTAL, \"can't return from constant\", inject_primitives: true\n      class Foo\n        A = begin\n          return if 1 == 2\n        end\n      end\n\n      Foo::A\n      CRYSTAL\n  end\n\n  it \"errors if constant has NoReturn type (#6139)\" do\n    assert_error <<-CRYSTAL, \"constant FOO has illegal type NoReturn\"\n      lib LibFoo\n        fun foo : NoReturn\n      end\n\n      FOO = LibFoo.foo\n\n      FOO\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/def_overload_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndef assert_stricter(params1, params2, args, *, file = __FILE__, line = __LINE__)\n  assert_type(<<-CRYSTAL, file: file, line: line, flags: \"preview_overload_order\") { tuple_of([int32, int32]) }\n    def foo(#{params1}); 1; end\n    def foo(#{params2}); 'x'; end\n\n    def bar(#{params2}); 'x'; end\n    def bar(#{params1}); 1; end\n\n    a = foo(#{args})\n    b = bar(#{args})\n    {a, b}\n    CRYSTAL\nend\n\ndef assert_unordered(params1, params2, args, *, file = __FILE__, line = __LINE__)\n  assert_type(<<-CRYSTAL, file: file, line: line, flags: \"preview_overload_order\") { tuple_of([int32, int32]) }\n    def foo(#{params1}); 1; end\n    def foo(#{params2}); 'x'; end\n\n    def bar(#{params2}); 1; end\n    def bar(#{params1}); 'x'; end\n\n    a = foo(#{args})\n    b = bar(#{args})\n    {a, b}\n    CRYSTAL\nend\n\ndescribe \"Semantic: def overload\" do\n  describe \"compare_strictness\" do\n    context \"positional parameters\" do\n      it \"specificity\" do\n        signatures = [\n          {2, 2, \"x0, x1\"},\n          {2, 3, \"x0, x1, x2 = 0\"},\n          {2, 4, \"x0, x1, x2 = 0, x3 = 0\"},\n          {2, 9, \"x0, x1, x2 = 0, x3 = 0, *xs\"},\n          {2, 9, \"x0, x1, x2 = 0, *xs\"},\n          {2, 9, \"x0, x1, *xs\"},\n          {1, 1, \"x0\"}, # incompatible with 6 defs above\n          {1, 2, \"x0, x1 = 0\"},\n          {1, 3, \"x0, x1 = 0, x2 = 0\"},\n          {1, 9, \"x0, x1 = 0, x2 = 0, *xs\"},\n          {1, 9, \"x0, x1 = 0, *xs\"},\n          {1, 9, \"x0, *xs\"},\n          {0, 0, \"\"},       # incompatible with 12 defs above\n          {0, 1, \"x0 = 0\"}, # incompatible with 6 defs above\n          {0, 2, \"x0 = 0, x1 = 0\"},\n          {0, 9, \"x0 = 0, x1 = 0, *xs\"},\n          {0, 9, \"x0 = 0, *xs\"},\n          {0, 9, \"*xs\"},\n        ]\n\n        signatures.each_combination(2, reuse: true) do |(x, y)|\n          min_count1, max_count1, params1 = x\n          min_count2, max_count2, params2 = y\n          next if min_count1 > max_count2 || min_count2 > max_count1\n          args = Array.new({min_count1, min_count2}.max) { \"0\" }.join(\", \")\n\n          assert_stricter(params1, params2, args)\n        end\n      end\n\n      it \"single splat vs single splat with restriction (#3134)\" do\n        assert_stricter(\n          \"*args : Int32\",\n          \"*args\",\n          \"1\")\n      end\n\n      it \"single splat restriction vs single splat with stricter restriction\" do\n        assert_stricter(\n          \"*args : Int32\",\n          \"*args : Int\",\n          \"1\")\n      end\n\n      it \"positional parameter with restriction vs single splat\" do\n        assert_stricter(\n          \"x : Int32\",\n          \"*args\",\n          \"1\")\n\n        assert_stricter(\n          \"x : Int32 = 0\",\n          \"*args\",\n          \"\")\n      end\n\n      it \"positional parameter vs single splat with restriction\" do\n        assert_stricter(\n          \"*args : Int32\",\n          \"x\",\n          \"1\")\n      end\n\n      it \"positional parameter with stricter restriction vs single splat with restriction\" do\n        assert_stricter(\n          \"x : Int32\",\n          \"*args : Int\",\n          \"1\")\n      end\n\n      it \"positional parameter with restriction vs single splat with stricter restriction\" do\n        assert_stricter(\n          \"*args : Int32\",\n          \"x : Int\",\n          \"1\")\n      end\n    end\n\n    context \"named parameters\" do\n      it \"specificity\" do\n        signatures = [\n          {1, 1, \"*, n\"},\n          {1, 9, \"*, n, **ns\"},\n          {0, 0, \"\"},\n          {0, 1, \"*, n = 0\"},\n          {0, 9, \"*, n = 0, **ns\"},\n          {0, 9, \"**ns\"},\n        ]\n\n        signatures.each_combination(2, reuse: true) do |(x, y)|\n          min_count1, max_count1, params1 = x\n          min_count2, max_count2, params2 = y\n          next if min_count1 > max_count2 || min_count2 > max_count1\n          args = Array.new({min_count1, min_count2}.max) { \"n: 0\" }.join(\", \")\n\n          assert_stricter(params1, params2, args)\n        end\n      end\n\n      it \"double splat vs double splat with restriction\" do\n        assert_stricter(\n          \"**args : Int32\",\n          \"**args\",\n          \"x: 1\")\n      end\n\n      it \"double splat restriction vs double splat with stricter restriction\" do\n        assert_stricter(\n          \"**args : Int32\",\n          \"**args : Int\",\n          \"x: 1\")\n      end\n\n      it \"named parameter with restriction vs double splat (#5328)\" do\n        assert_stricter(\n          \"*, x : Int32\",\n          \"**opts\",\n          \"x: 1\")\n\n        assert_stricter(\n          \"*, x : Int32 = 0\",\n          \"**opts\",\n          \"\")\n      end\n\n      it \"named parameter vs double splat with restriction\" do\n        assert_stricter(\n          \"**opts : Int32\",\n          \"*, x\",\n          \"x: 1\")\n      end\n\n      it \"named parameter with stricter restriction vs double splat with restriction\" do\n        assert_stricter(\n          \"*, x : Int32\",\n          \"**opts : Int\",\n          \"x: 1\")\n      end\n\n      it \"named parameter with restriction vs double splat with stricter restriction\" do\n        assert_stricter(\n          \"**opts : Int32\",\n          \"*, x : Int\",\n          \"x: 1\")\n      end\n    end\n\n    context \"subsumption conflicts\" do\n      it \"positional vs positional\" do\n        assert_unordered(\n          \"x : Int32, y : Int\",\n          \"x : Int, y : Int32\",\n          \"1, 2\")\n      end\n\n      it \"positional vs single splat\" do\n        assert_unordered(\n          \"x : Int32, *args : Int\",\n          \"x : Int, *args : Int32\",\n          \"1, 2, 3, 4\")\n\n        assert_unordered(\n          \"x : Int32, *args : Number\",\n          \"*args : Int\",\n          \"1, 2, 3, 4\")\n      end\n\n      it \"positional vs named\" do\n        assert_unordered(\n          \"x : Int32, *, y : Int\",\n          \"x : Int, *, y : Int32\",\n          \"1, y: 2\")\n      end\n\n      it \"positional vs double splat\" do\n        assert_unordered(\n          \"x : Int32, **opts : Int\",\n          \"x : Int, **opts : Int32\",\n          \"1, y: 2, z: 3, w: 4\")\n      end\n\n      it \"single splat vs named\" do\n        assert_unordered(\n          \"*args : Int32, y : Int\",\n          \"*args : Int, y : Int32\",\n          \"1, 2, 3, y: 4\")\n      end\n\n      it \"single splat vs double splat\" do\n        assert_unordered(\n          \"*args : Int32, **opts : Int\",\n          \"*args : Int, **opts : Int32\",\n          \"1, 2, 3, y: 4, z: 5, w: 6\")\n      end\n\n      it \"named vs named\" do\n        assert_unordered(\n          \"*, x : Int32, y : Int\",\n          \"*, x : Int, y : Int32\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"named vs double splat\" do\n        assert_unordered(\n          \"*, x : Int32, **opts : Int\",\n          \"*, x : Int, **opts : Int32\",\n          \"x: 1, y: 2, z: 3, w: 4\")\n\n        assert_unordered(\n          \"*, x : Int32, **opts : Number\",\n          \"**opts : Int\",\n          \"x: 1, y: 2, z: 3, w: 4\")\n      end\n    end\n\n    context \"subsumption has higher precedence over specificity\" do\n      it \"same positional parameter, required > optional\" do\n        assert_stricter(\n          \"x : Int32 = 0\",\n          \"x : Int\",\n          \"1\")\n      end\n\n      it \"same positional parameter, required > single splat\" do\n        assert_stricter(\n          \"*x : Int32\",\n          \"x : Int\",\n          \"1\")\n      end\n\n      it \"same positional parameter, optional > single splat\" do\n        assert_stricter(\n          \"*x : Int32\",\n          \"x : Int = 0\",\n          \"1\")\n      end\n\n      it \"positional vs (required positional > optional positional)\" do\n        assert_stricter(\n          \"x : Int32, y = 0\",\n          \"x : Int, y\",\n          \"1, 2\")\n      end\n\n      it \"positional vs (required positional > single splat)\" do\n        assert_stricter(\n          \"x : Int32, *args\",\n          \"x : Int, y\",\n          \"1, 2\")\n      end\n\n      it \"positional vs (optional positional > single splat)\" do\n        assert_stricter(\n          \"x : Int32, *args\",\n          \"x : Int, y = 0\",\n          \"1, 2\")\n      end\n\n      it \"positional vs (required named > optional named)\" do\n        assert_stricter(\n          \"x : Int32, *, y = 0\",\n          \"x : Int, *, y\",\n          \"1, y: 2\")\n      end\n\n      it \"positional vs (required named > double splat)\" do\n        assert_stricter(\n          \"x : Int32, **opts\",\n          \"x : Int, *, y\",\n          \"1, y: 2\")\n      end\n\n      it \"positional vs (optional named > double splat)\" do\n        assert_stricter(\n          \"x : Int32, **opts\",\n          \"x : Int, *, y = 0\",\n          \"1, y: 2\")\n      end\n\n      it \"single splat vs (required named > optional named)\" do\n        assert_stricter(\n          \"*args : Int32, y = 0\",\n          \"*args : Int, y\",\n          \"1, 2, 3, y: 4\")\n      end\n\n      it \"single splat vs (required named > double splat)\" do\n        assert_stricter(\n          \"*args : Int32, **opts\",\n          \"*args : Int, y\",\n          \"1, 2, 3, y: 4\")\n      end\n\n      it \"single splat vs (optional named > double splat)\" do\n        assert_stricter(\n          \"*args : Int32, **opts\",\n          \"*args : Int, y = 0\",\n          \"1, 2, 3, y: 4\")\n      end\n\n      it \"same named parameter, required > optional\" do\n        assert_stricter(\n          \"*, x : Int32 = 0\",\n          \"*, x : Int\",\n          \"x: 1\")\n      end\n\n      it \"same named parameter, required > double splat\" do\n        assert_stricter(\n          \"**opts : Int32\",\n          \"*, x : Int\",\n          \"x: 1\")\n      end\n\n      it \"same named parameter, optional > double splat\" do\n        assert_stricter(\n          \"**opts : Int32\",\n          \"*, x : Int = 0\",\n          \"x: 1\")\n      end\n\n      it \"named vs (required positional > optional positional)\" do\n        assert_stricter(\n          \"x = 0, *, y : Int32\",\n          \"x, *, y : Int\",\n          \"1, y: 2\")\n      end\n\n      it \"named vs (required positional > single splat)\" do\n        assert_stricter(\n          \"*args, y : Int32\",\n          \"x, *, y : Int\",\n          \"1, y: 2\")\n      end\n\n      it \"named vs (optional positional > single splat)\" do\n        assert_stricter(\n          \"*args, y : Int32\",\n          \"x = 0, *, y : Int\",\n          \"1, y: 2\")\n      end\n\n      it \"named vs (required named > optional named)\" do\n        assert_stricter(\n          \"*, x : Int32, y = 0\",\n          \"*, x : Int, y\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"named vs (required named > double splat)\" do\n        assert_stricter(\n          \"*, x : Int32, **opts\",\n          \"*, x : Int, y\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"named vs (optional named > double splat)\" do\n        assert_stricter(\n          \"*, x : Int32, **opts\",\n          \"*, x : Int, y = 0\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"double splat vs (required positional > optional positional)\" do\n        assert_stricter(\n          \"x = 0, **opts : Int32\",\n          \"x, **opts : Int\",\n          \"1, y: 2, z: 3, w: 4\")\n      end\n\n      it \"double splat vs (required positional > single splat)\" do\n        assert_stricter(\n          \"*args, **opts : Int32\",\n          \"x, **opts : Int\",\n          \"1, y: 2, z: 3, w: 4\")\n      end\n\n      it \"double splat vs (optional positional > single splat)\" do\n        assert_stricter(\n          \"*args, **opts : Int32\",\n          \"x = 0, **opts : Int\",\n          \"1, y: 2, z: 3, w: 4\")\n      end\n    end\n\n    context \"specificity conflicts, positional vs named\" do\n      it \"(required > optional) vs (required > optional)\" do\n        assert_unordered(\n          \"x, *, y = 0\",\n          \"x = 0, *, y\",\n          \"1, y: 2\")\n      end\n\n      it \"(required > optional) vs (required > splat)\" do\n        assert_unordered(\n          \"x, **opts\",\n          \"x = 0, *, y\",\n          \"1, y: 2\")\n      end\n\n      it \"(required > optional) vs (optional > splat)\" do\n        assert_unordered(\n          \"x, **opts\",\n          \"x = 0, *, y = 0\",\n          \"1, y: 2\")\n      end\n\n      it \"(required > splat) vs (required > optional)\" do\n        assert_unordered(\n          \"x, *, y = 0\",\n          \"*x, y\",\n          \"1, y: 2\")\n      end\n\n      it \"(required > splat) vs (required > splat)\" do\n        assert_unordered(\n          \"x, **opts\",\n          \"*x, y\",\n          \"1, y: 2\")\n      end\n\n      it \"(required > splat) vs (optional > splat)\" do\n        assert_unordered(\n          \"x, **opts\",\n          \"*x, y = 0\",\n          \"1, y: 2\")\n      end\n\n      it \"(optional > splat) vs (required > optional)\" do\n        assert_unordered(\n          \"x = 0, *, y = 0\",\n          \"*x, y\",\n          \"1, y: 2\")\n      end\n\n      it \"(optional > splat) vs (required > splat)\" do\n        assert_unordered(\n          \"x = 0, **opts\",\n          \"*x, y\",\n          \"1, y: 2\")\n      end\n\n      it \"(optional > splat) vs (optional > splat)\" do\n        assert_unordered(\n          \"x = 0, **opts\",\n          \"*x, y = 0\",\n          \"1, y: 2\")\n      end\n    end\n\n    context \"specificity conflicts, named vs named\" do\n      it \"(required > optional) vs (required > optional)\" do\n        assert_unordered(\n          \"*, x, y = 0\",\n          \"*, y, x = 0\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"(required > optional) vs (required > splat)\" do\n        assert_unordered(\n          \"*, x, **opts\",\n          \"*, y, x = 0\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"(required > optional) vs (optional > splat)\" do\n        assert_unordered(\n          \"*, x, **opts\",\n          \"*, y = 0, x = 0\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"(required > splat) vs (required > splat)\" do\n        assert_unordered(\n          \"*, x, **opts\",\n          \"*, y, **opts\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"(required > splat) vs (optional > splat)\" do\n        assert_unordered(\n          \"*, x, **opts\",\n          \"*, y = 0, **opts\",\n          \"x: 1, y: 2\")\n      end\n\n      it \"(optional > splat) vs (optional > splat)\" do\n        assert_unordered(\n          \"*, x = 0, **opts\",\n          \"*, y = 0, **opts\",\n          \"x: 1, y: 2\")\n      end\n    end\n  end\n\n  it \"types a call with overload\" do\n    assert_type(\"def foo; 1; end; def foo(x); 2.5; end; foo\") { int32 }\n  end\n\n  it \"types a call with overload with yield\" do\n    assert_type(\"def foo; yield; 1; end; def foo; 2.5; end; foo\") { float64 }\n  end\n\n  it \"types a call with overload with yield after typing another call without yield\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo; yield; 1; end\n      def foo; 2.5; end\n      foo\n      foo {}\n      CRYSTAL\n  end\n\n  it \"types a call with overload with yield the other way\" do\n    assert_type(\"def foo; yield; 1; end; def foo; 2.5; end; foo { 1 }\") { int32 }\n  end\n\n  it \"types a call with overload type first overload\" do\n    assert_type(\"def foo(x : Int); 2.5; end; def foo(x : Float); 1; end; foo(1)\") { float64 }\n  end\n\n  it \"types a call with overload type second overload\" do\n    assert_type(\"def foo(x : Int); 2.5; end; def foo(x : Float); 1; end; foo(1.5)\") { int32 }\n  end\n\n  it \"types a call with overload Object type first overload\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      class Foo\n      end\n\n      class Bar\n      end\n\n      def foo(x : Foo)\n        2.5\n      end\n\n      def foo(x : Bar)\n        1\n      end\n\n      foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"types a call with overload selecting the most restrictive\" do\n    assert_type(\"def foo(x); 1; end; def foo(x : Float); 1.1; end; foo(1.5)\") { float64 }\n  end\n\n  it \"types a call with overload selecting the most restrictive 2\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(x, y : Int)\n        1\n      end\n\n      def foo(x : Int, y)\n        1.1\n      end\n\n      def foo(x : Int, y : Int)\n        'a'\n      end\n\n      foo(1, 1)\n      CRYSTAL\n  end\n\n  it \"types a call with overload matches virtual\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo; end\n\n      def foo(x : Object)\n        1\n      end\n\n      foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"types a call with overload matches virtual 2\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : Foo)\n        1\n      end\n\n      def foo(x : Bar)\n        1.5\n      end\n\n      foo(Bar.new)\n      CRYSTAL\n  end\n\n  it \"types a call with overload matches virtual 3\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : Foo)\n        1\n      end\n\n      def foo(x : Bar)\n        1.5\n      end\n\n      foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"types a call with overload self\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x : self)\n          1\n        end\n\n        def foo(x)\n          1.5\n        end\n      end\n\n      a = Foo.new\n      a.foo(a)\n      CRYSTAL\n  end\n\n  it \"types a call with overload self other match\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      class Foo\n        def foo(x : self)\n          1\n        end\n\n        def foo(x)\n          1.5\n        end\n      end\n\n      a = Foo.new\n      a.foo(1)\n      CRYSTAL\n  end\n\n  it \"types a call with overload self in included module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n        def foo(x : self)\n          1\n        end\n      end\n\n      class Bar\n        def foo(x)\n          1.5\n        end\n      end\n\n      class Baz < Bar\n        include Foo\n      end\n\n      b = Baz.new\n      b.foo(b)\n      CRYSTAL\n  end\n\n  it \"types a call with overload self in included module other type\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      module Foo\n        def foo(x : self)\n          1\n        end\n      end\n\n      class Bar\n        def foo(x)\n          1.5\n        end\n      end\n\n      class Baz < Bar\n        include Foo\n      end\n\n      b = Baz.new\n      b.foo(Bar.new)\n      CRYSTAL\n  end\n\n  it \"types a call with overload self with inherited type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x : self)\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      a = Foo.new\n      a.foo(Bar.new)\n      CRYSTAL\n  end\n\n  it \"matches types with free variables\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n      def foo(x : Array(T), y : T) forall T\n        1\n      end\n\n      def foo(x, y)\n        1.5\n      end\n\n      foo([1], 1)\n      CRYSTAL\n  end\n\n  it \"does not consider global paths as free variables (1)\" do\n    assert_error <<-CRYSTAL, \"undefined constant ::Foo\"\n      def foo(x : ::Foo) forall Foo\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"does not consider global paths as free variables (2)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Foo, not Int32\"\n      class Foo\n      end\n\n      def foo(x : ::Foo) forall Foo\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"prefers more specific overload than one with free variables\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      require \"prelude\"\n      def foo(x : Array(T), y : T)\n        1\n      end\n\n      def foo(x : Array(Int), y : Int)\n        1.5\n      end\n\n      foo([1], 1)\n      CRYSTAL\n  end\n\n  it \"accepts overload with nilable type restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : Int?)\n        1\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"dispatch call to def with restrictions\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      def foo(x : Value)\n        1.1\n      end\n\n      def foo(x : Int32)\n        1\n      end\n\n      a = 1 || 1.1\n      foo(a)\n      CRYSTAL\n  end\n\n  it \"dispatch call to def with restrictions\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(T)\n      end\n\n      def foo(x : T) forall T\n        Foo(T).new\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"can call overload with generic restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n      end\n\n      def foo(x : Foo)\n        1\n      end\n\n      foo(Foo(Int32).new)\n      CRYSTAL\n  end\n\n  it \"can call overload with aliased generic restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n      end\n\n      alias FooAlias = Foo\n\n      def foo(x : FooAlias(T)) forall T\n        1\n      end\n\n      foo(Foo(Int32).new)\n      CRYSTAL\n  end\n\n  it \"restrict matches to minimum necessary 1\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def coco(x : Int, y); 1; end\n      def coco(x, y : Int); 1.5; end\n      def coco(x, y); 'a'; end\n\n      coco 1, 1\n      CRYSTAL\n  end\n\n  it \"single type restriction wins over union\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo; end\n      class Bar < Foo ;end\n\n      def foo(x : Foo | Bar)\n        1.1\n      end\n\n      def foo(x : Foo)\n        1\n      end\n\n      foo(Foo.new || Bar.new)\n      CRYSTAL\n  end\n\n  it \"compare self type with others\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x : Int)\n          1.1\n        end\n\n        def foo(x : self)\n          1\n        end\n      end\n\n      x = Foo.new.foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"uses method defined in base class if the restriction doesn't match\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x)\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo(x : Float64)\n          1.1\n        end\n      end\n\n      Bar.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"lookup matches in virtual type inside union\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz\n        def foo\n          'a'\n        end\n      end\n\n      a = Foo.new || Bar.new || Baz.new\n      a.foo\n      CRYSTAL\n  end\n\n  it \"filter union type with virtual\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      class Foo\n      end\n\n      class Bar < Foo\n        def bar\n          1\n        end\n      end\n\n      def foo(x : Bar)\n        x.bar\n      end\n\n      def foo(x)\n        1.1\n      end\n\n      foo(nil || Foo.new || Bar.new)\n      CRYSTAL\n  end\n\n  it \"restrict virtual type with virtual type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : T, y : T) forall T\n        1\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      x = Foo.new || Bar.new\n      foo(x, x)\n      CRYSTAL\n  end\n\n  it \"restricts union to generic class\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      class Foo(T)\n      end\n\n      def foo(x : Foo(T)) forall T\n        1\n      end\n\n      def foo(x : Int)\n        'a'\n      end\n\n      x = 1 || Foo(Int32).new\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"matches on partial union\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      require \"prelude\"\n\n      def foo(x : Int32 | Float64)\n        x.abs\n        1\n      end\n\n      def foo(x : Char)\n        x.ord\n        'a'\n      end\n\n      foo 1 || 1.5 || 'a'\n      CRYSTAL\n  end\n\n  pending \"restricts on generic type with free type arg\" do\n    assert_type(<<-CRYSTAL) { union_of(bool, int32) }\n      require \"reference\"\n\n      class Object\n        def equal(expectation)\n          expectation == self\n        end\n      end\n\n      class Foo(T)\n        def ==(other : Foo(U))\n          1\n        end\n      end\n\n      a = Foo(Int).new\n      a.equal(a)\n      CRYSTAL\n  end\n\n  pending \"restricts on generic type without type arg\" do\n    assert_type(<<-CRYSTAL) { union_of(bool, int32) }\n      require \"reference\"\n\n      class Object\n        def equal(expectation)\n          expectation == self\n        end\n      end\n\n      class Foo(T)\n        def ==(other : Foo)\n          1\n        end\n      end\n\n      a = Foo(Int).new\n      a.equal(a)\n      CRYSTAL\n  end\n\n  it \"matches generic class instance type with another one\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = [] of Array(Foo)\n      a.push [Foo.new, Bar.new]\n      1\n      CRYSTAL\n  end\n\n  it \"errors if generic type doesn't match\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Foo(Int32), not Foo(Float64 | Int32)\"\n      class Foo(T)\n      end\n\n      def foo(x : Foo(Int32))\n      end\n\n      foo Foo(Int32 | Float64).new\n      CRYSTAL\n  end\n\n  it \"gets free variable from union restriction\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : Nil | U) forall U\n        U\n      end\n\n      foo(1 || nil)\n      CRYSTAL\n  end\n\n  it \"gets free variable from union restriction (2)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : Nil | U) forall U\n        U\n      end\n\n      foo(nil || 1)\n      CRYSTAL\n  end\n\n  it \"gets free variable from union restriction without a union\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : Nil | U) forall U\n        U\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"matches a generic module argument\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Bar(T)\n      end\n\n      class Foo\n        include Bar(Int32)\n      end\n\n      def foo(x : Bar(Int32))\n        1\n      end\n\n      foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"matches a generic module argument with free var\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Bar(T)\n      end\n\n      class Foo\n        include Bar(Int32)\n      end\n\n      def foo(x : Bar(T)) forall T\n        T\n      end\n\n      foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"matches a generic module argument with free var (2)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Bar(T)\n      end\n\n      class Foo(T)\n        include Bar(T)\n      end\n\n      def foo(x : Bar(T)) forall T\n        T\n      end\n\n      foo(Foo(Int32).new)\n      CRYSTAL\n  end\n\n  it \"matches a union argument with free var\" do\n    each_union_variant(\"T\", \"Nil\") do |restriction|\n      assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, int32.metaclass]) }\n        def foo(x : #{restriction}) forall T\n          T\n        end\n\n        {foo(1), foo(1 || nil)}\n        CRYSTAL\n    end\n  end\n\n  it \"matches a union metaclass argument with free var (#8071)\" do\n    each_union_variant(\"T\", \"Nil\") do |restriction|\n      assert_type(<<-CRYSTAL) { tuple_of([string.metaclass, string.metaclass]) }\n        def foo(x : (#{restriction}).class) forall T\n          T\n        end\n\n        {foo(String), foo(String?)}\n        CRYSTAL\n    end\n  end\n\n  it \"matches a union argument with free var, more types (1)\" do\n    each_union_variant(\"T\", \"Nil\") do |restriction|\n      assert_type(<<-CRYSTAL) { union_of(int32, string).metaclass }\n        def foo(x : #{restriction}) forall T\n          T\n        end\n\n        foo(1 || \"\" || nil)\n        CRYSTAL\n    end\n  end\n\n  it \"matches a union argument with free var, more types (2)\" do\n    each_union_variant(\"T\", \"(Int32 | String)\") do |restriction|\n      assert_type(<<-CRYSTAL) { char.metaclass }\n        def foo(x : #{restriction}) forall T\n          T\n        end\n\n        foo(1 || \"\" || 'a')\n        CRYSTAL\n    end\n  end\n\n  it \"errors if union restriction has multiple free vars\" do\n    each_union_variant(\"T\", \"U\") do |restriction|\n      assert_error <<-CRYSTAL, \"can't specify more than one free var in union restriction\"\n        def foo(x : #{restriction}) forall T, U\n        end\n\n        foo(1)\n        CRYSTAL\n    end\n  end\n\n  it \"errors if union restriction has multiple free vars (2)\" do\n    each_union_variant(\"T\", \"U\") do |restriction|\n      assert_error <<-CRYSTAL, \"can't specify more than one free var in union restriction\"\n        def foo(x : #{restriction}) forall T, U\n        end\n\n        foo(1 || 'a')\n        CRYSTAL\n    end\n  end\n\n  it \"matches virtual type to union\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      abstract class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      def foo(x : Bar | Baz)\n        1\n      end\n\n      node = Bar.new || Baz.new\n      foo(node)\n      CRYSTAL\n  end\n\n  it \"doesn't match tuples of different sizes\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be ::Tuple(X, Y, Z), not Tuple(Int32, Int32)\"\n      def foo(x : {X, Y, Z})\n        'a'\n      end\n\n      foo({1, 2})\n      CRYSTAL\n  end\n\n  it \"matches tuples of different sizes\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      def foo(x : {X, Y}) forall X, Y\n        1\n      end\n\n      def foo(x : {X, Y, Z}) forall X, Y, Z\n        'a'\n      end\n\n      x = {1, 2} || {1, 2, 3}\n      foo x\n      CRYSTAL\n  end\n\n  it \"matches tuples and uses free var\" do\n    assert_type(<<-CRYSTAL) { float64.metaclass }\n      def foo(x : {X, Y}) forall X, Y\n        Y\n      end\n\n      foo({1, 2.5})\n      CRYSTAL\n  end\n\n  it \"matches tuple with underscore\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, float64] of Type) }\n      def foo(x : {_, _})\n        x\n      end\n\n      foo({1, 2.5})\n      CRYSTAL\n  end\n\n  it \"gives correct error message, looking up parent defs, when no overload matches\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Bar#foo' to be Int32, not Float64\"\n      class Foo\n        def foo(x : Int32)\n        end\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n\n      Bar.new.foo(1.5)\n      CRYSTAL\n  end\n\n  it \"doesn't match with wrong number of type arguments (#313)\" do\n    assert_error <<-CRYSTAL, \"wrong number of type vars for Foo(A, B) (given 1, expected 2)\"\n      class Foo(A, B)\n      end\n\n      def foo(x : Foo(Int32))\n      end\n\n      foo Foo(Int32, Int32).new\n      CRYSTAL\n  end\n\n  it \"includes splat symbol in error message\" do\n    assert_error <<-CRYSTAL, \"foo(x : Int32, *bar)\"\n      def foo(x : Int32, *bar)\n      end\n\n      foo 'a'\n      CRYSTAL\n  end\n\n  it \"says `no overload matches` instead of `can't instantiate abstract class` on wrong argument in new method\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo.new' to be Int, not Char\"\n      abstract class Foo\n        def self.new(x : Int)\n        end\n      end\n\n      Foo.new('a')\n      CRYSTAL\n  end\n\n  it \"finds method after including module in generic module (#1201)\" do\n    assert_type(<<-CRYSTAL) { char }\n      module Bar\n        def foo\n          'a'\n        end\n      end\n\n      module Moo(T)\n      end\n\n      class Foo\n        include Moo(Int32)\n\n        def foo(x)\n          1\n        end\n      end\n\n      module Moo(T)\n        include Bar\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"reports no overload matches with correct method owner (#2083)\" do\n    assert_error <<-CRYSTAL, <<-MSG\n      class Foo\n        def foo(x : Int32)\n          x + 1\n        end\n      end\n\n      class Bar < Foo\n        def foo(x : Int32)\n          x + 2\n        end\n      end\n\n      Bar.new.foo(\"hello\")\n      CRYSTAL\n       - Bar#foo(x : Int32)\n       - Foo#foo(x : Int32)\n      MSG\n  end\n\n  it \"gives better error message with consecutive arguments sizes\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'foo' (given 3, expected 0..2)\"\n      def foo\n      end\n\n      def foo(x)\n      end\n\n      def foo(x, y)\n      end\n\n      foo 1, 2, 3\n      CRYSTAL\n  end\n\n  it \"errors if no overload matches on union against named arg (#2640)\" do\n    assert_error <<-CRYSTAL, \"expected argument 'a' to 'f' to be Int32, not (Int32 | Nil)\"\n      def f(a : Int32)\n      end\n\n      a = 1 || nil\n      f(a: a)\n      CRYSTAL\n  end\n\n  it \"errors if no overload matches on union against named arg with external param name (#10516)\" do\n    assert_error <<-CRYSTAL, \"expected argument 'a' to 'f' to be Int32, not (Int32 | Nil)\"\n      def f(a b : Int32)\n      end\n\n      a = 1 || nil\n      f(a: a)\n      CRYSTAL\n  end\n\n  it \"dispatches with named arg\" do\n    assert_type(<<-CRYSTAL) { union_of bool, char }\n      def f(a : Int32, b : Int32)\n        true\n      end\n\n      def f(b : Int32, a : Nil)\n        'x'\n      end\n\n      a = 1 || nil\n      f(a: a, b: 2)\n      CRYSTAL\n  end\n\n  it \"uses long name when no overload matches and name is the same (#1030)\" do\n    assert_error <<-CRYSTAL, \" - Moo::String.foo(a : Moo::String, b : Bool)\"\n      module Moo::String\n        def self.foo(a : String, b : Bool)\n          puts a if b\n        end\n      end\n\n      Moo::String.foo(\"Hello, World!\", true)\n      CRYSTAL\n  end\n\n  it \"overloads on metaclass (#2916)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo(x : String.class)\n        1\n      end\n\n      def foo(x : String?.class)\n        'a'\n      end\n\n      {foo(String), foo(typeof(\"\" || nil))}\n      CRYSTAL\n  end\n\n  it \"overloads on metaclass (2) (#2916)\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(x : String.class)\n        1\n      end\n\n      def foo(x : ::String.class)\n        'a'\n      end\n\n      foo(String)\n      CRYSTAL\n  end\n\n  it \"overloads on metaclass (3) (#2916)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, int32]) }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : Foo.class)\n        1\n      end\n\n      def foo(x : Bar.class)\n        'a'\n      end\n\n      {foo(Bar), foo(Foo)}\n      CRYSTAL\n  end\n\n  it \"doesn't crash on unknown metaclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : Foo.class)\n      end\n\n      def foo(x : Bar.class)\n      end\n\n      1\n      CRYSTAL\n  end\n\n  it \"overloads union against non-union (#2904)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, bool]) }\n      def foo(x : Int32?)\n        true\n      end\n\n      def foo(x : Int32)\n        'a'\n      end\n\n      {foo(1), foo(nil)}\n      CRYSTAL\n  end\n\n  it \"errors when binding free variable to different types\" do\n    assert_error <<-CRYSTAL, \"expected argument #2 to 'foo' to be Int32, not Char\"\n      def foo(x : T, y : T) forall T\n      end\n\n      foo(1, 'a')\n      CRYSTAL\n  end\n\n  it \"errors when binding free variable to different types (2)\" do\n    assert_error <<-CRYSTAL, \"expected argument #2 to 'foo' to be Gen(Int32), not Gen(Char)\"\n      class Gen(T)\n      end\n\n      def foo(x : T, y : Gen(T)) forall T\n      end\n\n      foo(1, Gen(Char).new)\n      CRYSTAL\n  end\n\n  it \"overloads with named argument (#4465)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of float64, bool }\n      def do_something(value : Int32)\n        value + 1\n        1.5\n      end\n\n      def do_something(value : Char)\n        value.ord\n        false\n      end\n\n      do_something value: 7.as(Int32 | Char)\n      CRYSTAL\n  end\n\n  it \"resets free vars after a partial match is rejected (#10270)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      def foo(x : T, y : String) forall T\n        1\n      end\n\n      def foo(x : Char, y : T) forall T\n        true\n      end\n\n      foo('a', 1)\n      CRYSTAL\n  end\n\n  it \"resets free vars after a partial match is rejected (2) (#10185)\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({a: int32, b: string}).metaclass }\n      def foo(*x : *T) forall T\n        T\n      end\n\n      def foo(**x : **T) forall T\n        T\n      end\n\n      foo(**{a: 1, b: \"\"})\n      CRYSTAL\n  end\n\n  it \"considers NamedTuple in a module's including types (#10380)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Bar, not Foo\"\n      module Foo\n      end\n\n      struct NamedTuple\n        include Foo\n      end\n\n      class Bar\n        include Foo\n      end\n\n      def foo(x : Bar)\n      end\n\n      # force a name tuple instantiation\n      {a: 1}\n\n      x = uninitialized Foo\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"treats single splats with same restriction as equivalent (#12579)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(*x : Int32)\n        'a'\n      end\n\n      def foo(*x : Int32)\n        1\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"treats single splats with same restriction as equivalent (2) (#12579)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(*x : Int32)\n        'a'\n      end\n\n      def foo(*y : Int32)\n        1\n      end\n\n      foo(1)\n      CRYSTAL\n  end\nend\n\nprivate def each_union_variant(t1, t2, &)\n  yield \"#{t1} | #{t2}\"\n  yield \"#{t2} | #{t1}\"\n  # yield \"Union(#{t1}, #{t2})\"\n  # yield \"Union(#{t2}, #{t1})\"\n  yield \"#{t1}?\" if t2 == \"Nil\"\n  yield \"#{t2}?\" if t1 == \"Nil\" && t2 != \"Nil\"\nend\n"
  },
  {
    "path": "spec/compiler/semantic/def_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: def\" do\n  it \"types a call with an int\" do\n    assert_type(\"def foo; 1; end; foo\") { int32 }\n  end\n\n  it \"types a call with a float\" do\n    assert_type(\"def foo; 2.3f32; end; foo\") { float32 }\n  end\n\n  it \"types a call with a double\" do\n    assert_type(\"def foo; 2.3; end; foo\") { float64 }\n  end\n\n  it \"types a call with an argument\" do\n    assert_type(\"def foo(x); x; end; foo 1\") { int32 }\n  end\n\n  it \"types a call with an argument\" do\n    input = parse \"def foo(x); x; end; foo 1; foo 2.3\"\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n\n    input[1].type.should eq(mod.int32)\n    input[2].type.should eq(mod.float64)\n  end\n\n  it \"types a call with an argument uses a new scope\" do\n    assert_type(\"x = 2.3; def foo(x); x; end; foo 1; x\") { float64 }\n  end\n\n  it \"assigns def owner\" do\n    input = parse \"struct Int; def foo; 2.5; end; end; 1.foo\"\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n    input.last.as(Call).target_def.owner.should eq(mod.int32)\n  end\n\n  it \"types putchar with Char\" do\n    assert_type(\"lib LibC; fun putchar(c : Char) : Char; end; LibC.putchar 'a'\") { char }\n  end\n\n  it \"types getchar with Char\" do\n    assert_type(\"lib LibC; fun getchar : Char; end; LibC.getchar\") { char }\n  end\n\n  it \"allows recursion\" do\n    assert_type(\"def foo; foo; end; foo\") { no_return }\n  end\n\n  it \"allows recursion with arg\" do\n    assert_type(\"def foo(x); foo(x); end; foo 1\") { no_return }\n  end\n\n  it \"types simple recursion\" do\n    assert_type(\"def foo(x); if x > 0; foo(x - 1) + 1; else; 1; end; end; foo(5)\", inject_primitives: true) { int32 }\n  end\n\n  it \"types simple recursion 2\" do\n    assert_type(\"def foo(x); if x > 0; 1 + foo(x - 1); else; 1; end; end; foo(5)\", inject_primitives: true) { int32 }\n  end\n\n  it \"types mutual recursion\" do\n    assert_type(\"def foo(x); if 1 == 1; bar(x); else; 1; end; end; def bar(x); foo(x); end; foo(5)\", inject_primitives: true) { int32 }\n  end\n\n  it \"types empty body def\" do\n    assert_type(\"def foo; end; foo\") { nil_type }\n  end\n\n  it \"types mutual infinite recursion\" do\n    assert_type(\"def foo; bar; end; def bar; foo; end; foo\") { no_return }\n  end\n\n  it \"types call with union argument\" do\n    assert_type(\"def foo(x); x; end; a = 1 || 1.1; foo(a)\") { union_of(int32, float64) }\n  end\n\n  it \"defines class method\" do\n    assert_type(\"def Int.foo; 2.5; end; Int.foo\") { float64 }\n  end\n\n  it \"defines class method with self\" do\n    assert_type(\"struct Int; def self.foo; 2.5; end; end; Int.foo\") { float64 }\n  end\n\n  it \"calls with default argument\" do\n    assert_type(\"def foo(x = 1); x; end; foo\") { int32 }\n  end\n\n  it \"do not use body for the def type\" do\n    input = parse <<-CRYSTAL\n      require \"primitives\"\n\n      def foo\n        if 1 == 2\n          return 0\n        end\n      end\n\n      foo\n      CRYSTAL\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n\n    call = input.last.as(Call)\n    call.type.should eq(mod.nilable(mod.int32))\n    call.target_def.body.type.should eq(mod.nil)\n  end\n\n  it \"reports undefined method\" do\n    assert_error \"foo()\",\n      \"undefined method 'foo'\"\n  end\n\n  it \"reports no overload matches\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int, not (Float64 | Int32)\"\n      def foo(x : Int)\n      end\n\n      foo 1 || 1.5\n      CRYSTAL\n  end\n\n  it \"reports no overload matches 2\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int, not (Char | Int32)\"\n      def foo(x : Int, y : Int)\n      end\n\n      def foo(x : Int, y : Float)\n      end\n\n      foo(1 || 'a', 1 || 1.5)\n      CRYSTAL\n  end\n\n  it \"reports no block given\" do\n    assert_error <<-CRYSTAL, \"'foo' is expected to be invoked with a block, but no block was given\"\n      def foo\n        yield\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"reports block given\" do\n    assert_error <<-CRYSTAL, \"'foo' is not expected to be invoked with a block, but a block was given\"\n      def foo\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"errors when calling two functions with nil type\" do\n    assert_error <<-CRYSTAL, \"undefined method\"\n      def bar\n      end\n\n      def foo\n      end\n\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"errors when default value is incompatible with type restriction\" do\n    assert_error <<-CRYSTAL, \"can't restrict Char to Int64\"\n      def foo(x : Int64 = 'a')\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors when default value is incompatible with non-type restriction\" do\n    assert_error <<-CRYSTAL, \"can't restrict Char to Tuple(_)\"\n      def foo(x : Tuple(_) = 'a')\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types call with global scope\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def bar\n        1\n      end\n\n      class Foo\n        def foo\n          ::bar\n        end\n\n        def bar\n          'a'\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"lookups methods in super modules\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      module Foo\n        def lookup_matches(x = 1)\n          1\n        end\n      end\n\n      module Bar\n        include Foo\n      end\n\n      abstract class Type\n      end\n\n      abstract class CType < Type\n      end\n\n      abstract class MType < CType\n        include Bar\n      end\n\n      class NonGenericMType < MType\n      end\n\n      class GenericMType < MType\n      end\n\n      b = [] of Type\n      b.push NonGenericMType.new\n      b.push GenericMType.new\n      b[0].lookup_matches\n      CRYSTAL\n  end\n\n  it \"fixes bug #165\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Pointer(Node), not Node\", inject_primitives: true\n      abstract class Node\n      end\n\n      def foo(nodes : Pointer(Node))\n        foo nodes.value\n      end\n\n      a = Pointer(Node).new(0_u64)\n      foo a\n      CRYSTAL\n  end\n\n  it \"says can only defined def on types and self\" do\n    assert_error <<-CRYSTAL, \"def receiver can only be a Type or self\"\n      class Foo\n      end\n\n      foo = Foo.new\n      def foo.bar\n      end\n      CRYSTAL\n  end\n\n  it \"errors if return type doesn't match\" do\n    assert_error <<-CRYSTAL, \"method ::foo must return Int32 but it is returning Char\"\n      def foo : Int32\n        'a'\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors if return type doesn't match on instance method\" do\n    assert_error <<-CRYSTAL, \"method Foo#foo must return Int32 but it is returning Char\"\n      class Foo\n        def foo : Int32\n          'a'\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if return type doesn't match on class method\" do\n    assert_error <<-CRYSTAL, \"method Foo.foo must return Int32 but it is returning Char\"\n      class Foo\n        def self.foo : Int32\n          'a'\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"is ok if returns Int32? with explicit return\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      def foo : Int32?\n        if 1 == 2\n          return nil\n        end\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"says compile-time type on error\" do\n    assert_error <<-CRYSTAL, \"compile-time type is Foo+\"\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def bar\n          1\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      f = Bar.new || Baz.new\n      f.bar\n      CRYSTAL\n  end\n\n  it \"gives correct error for wrong number of arguments for program call inside type (#1024)\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'foo' (given 1, expected 0)\"\n      def foo\n      end\n\n      class Foo\n        def self.bar\n          foo 1\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"gives correct error for wrong number of arguments for program call inside type (2) (#1024)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be String, not Int32\"\n      def foo(x : String)\n      end\n\n      class Foo\n        def self.bar\n          foo 1\n        end\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"gives correct error for methods in Class\" do\n    assert_error <<-CRYSTAL, <<-ERROR\n      class Class\n        def foo\n          1\n        end\n      end\n\n      class Foo\n      end\n\n      Foo.foo(1)\n      CRYSTAL\n      wrong number of arguments for 'Foo.foo' (given 1, expected 0)\n\n      Overloads are:\n       - Class#foo()\n      ERROR\n  end\n\n  it \"gives correct error for methods in Class (2)\" do\n    assert_error <<-CRYSTAL, <<-ERROR\n      class Class\n        def self.foo\n          1\n        end\n      end\n\n      class Foo\n      end\n\n      Foo.foo(1)\n      CRYSTAL\n      wrong number of arguments for 'Foo.foo' (given 1, expected 0)\n\n      Overloads are:\n       - Class#foo()\n      ERROR\n  end\n\n  it \"errors if declares def inside if\" do\n    assert_error <<-CRYSTAL, \"can't declare def dynamically\"\n      if 1 == 2\n        def foo; end\n      end\n      CRYSTAL\n  end\n\n  it \"accesses free var of default argument (#1101)\" do\n    assert_type(<<-CRYSTAL) { nil_type.metaclass }\n      def foo(x, y : U = nil) forall U\n        U\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"clones regex literal value (#2384)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      def foo(x : String = \"\")\n        /\\#{1}/\n        10\n      end\n\n      foo\n      foo(\"\")\n      CRYSTAL\n  end\n\n  it \"doesn't find type in namespace through free var\" do\n    assert_error <<-CRYSTAL, \"undefined constant T::String\"\n      def foo(x : T) forall T\n        T::String\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"errors if trying to declare method on generic class instance\" do\n    assert_error <<-CRYSTAL, \"can't define method in generic instance\"\n      class Foo(T)\n      end\n\n      alias Bar = Foo(Int32)\n\n      def Bar.foo\n      end\n      CRYSTAL\n  end\n\n  it \"uses free variable\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : Free) forall Free\n        Free\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"uses free variable with metaclass\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : Free.class) forall Free\n        Free\n      end\n\n      foo(Int32)\n      CRYSTAL\n  end\n\n  it \"uses free variable with metaclass and default value\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : Free.class = Int32) forall Free\n        Free\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"uses free variable as block return type\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(&block : -> Free) forall Free\n        yield\n        Free\n      end\n\n      foo { 1 }\n      CRYSTAL\n  end\n\n  it \"uses free variable and doesn't conflict with top-level type\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Free\n      end\n\n      def foo(x : Free) forall Free\n        Free\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"shows free variables if no overload matches\" do\n    assert_error <<-CRYSTAL, <<-ERROR\n      class Foo(T)\n        def foo(x : T, y : U, z : V) forall U, V\n        end\n      end\n\n      Foo(Int32).new.foo(\"\", \"\", \"\")\n      CRYSTAL\n      Overloads are:\n       - Foo(T)#foo(x : T, y : U, z : V) forall U, V\n      ERROR\n  end\n\n  it \"can't use self in toplevel method\" do\n    assert_error <<-CRYSTAL, \"there's no self in this scope\"\n      def foo\n        self\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"points error at name (#6937)\" do\n    ex = assert_error <<-CRYSTAL,\n      1.\n        foobar\n      CRYSTAL\n      \"undefined method\"\n    ex.line_number.should eq(2)\n    ex.column_number.should eq(3)\n    ex.size.should eq(6)\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/did_you_mean_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: did you mean\" do\n  it \"says did you mean for one mistake in short word in instance method\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'bar'?\"\n      class Foo\n        def bar\n        end\n      end\n\n      Foo.new.baz\n      CRYSTAL\n  end\n\n  it \"says did you mean for two mistakes in long word in instance method\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'barbara'?\"\n      class Foo\n        def barbara\n        end\n      end\n\n      Foo.new.bazbaza\n      CRYSTAL\n  end\n\n  it \"says did you mean for global method with parenthesis\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'bar'?\"\n      def bar\n      end\n\n      baz()\n      CRYSTAL\n  end\n\n  it \"says did you mean for global method without parenthesis\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'bar'?\"\n      def bar\n      end\n\n      baz\n      CRYSTAL\n  end\n\n  it \"says did you mean for variable\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'bar'?\"\n      bar = 1\n      baz\n      CRYSTAL\n  end\n\n  it \"says did you mean for class\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'Foo'?\"\n      class Foo\n      end\n\n      Fog.new\n      CRYSTAL\n  end\n\n  it \"says did you mean for nested class\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'Foo::Bar'?\"\n      class Foo\n        class Bar\n        end\n      end\n\n      Foo::Baz.new\n      CRYSTAL\n  end\n\n  it \"says did you mean for nested class via alias\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'Boo::Bar'?\"\n      class Foo\n        class Bar\n        end\n      end\n\n      alias Boo = Foo\n\n      Boo::Baz.new\n      CRYSTAL\n  end\n\n  it \"says did you mean finds most similar in def\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'barbara'?\"\n      def barbaza\n      end\n\n      def barbara\n      end\n\n      barbarb\n      CRYSTAL\n  end\n\n  it \"says did you mean finds most similar in type\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'Barbara'?\"\n      class Barbaza\n      end\n\n      class Barbara\n      end\n\n      Barbarb\n      CRYSTAL\n  end\n\n  it \"doesn't suggest for operator\" do\n    error = assert_error <<-CRYSTAL\n      class Foo\n        def +\n        end\n      end\n\n      Foo.new.a\n      CRYSTAL\n\n    error.to_s.should_not contain(\"Did you mean\")\n  end\n\n  it \"says did you mean for named argument\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'barbara'?\"\n      def foo(barbara = 1)\n      end\n\n      foo bazbaza: 1\n      CRYSTAL\n  end\n\n  it \"says did you mean for instance var\" do\n    assert_error <<-CRYSTAL, \"Did you mean '@barbara'?\"\n      class Foo\n        def initialize\n          @barbara = 1\n        end\n\n        def foo\n          @bazbaza.abs\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"says did you mean for instance var in subclass\" do\n    assert_error <<-CRYSTAL, \"Did you mean '@barbara'?\"\n      class Foo\n        def initialize\n          @barbara = 1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          @bazbaza.abs\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"doesn't suggest when declaring var with suffix if and using it (#946)\" do\n    assert_error <<-CRYSTAL, \"If you declared 'a' in a suffix if, declare it in a regular if for this to work\"\n      a if a = 1\n      CRYSTAL\n  end\n\n  it \"doesn't suggest when declaring var inside macro (#466)\" do\n    assert_error <<-CRYSTAL, \"If the variable was declared in a macro it's not visible outside it\"\n      macro foo\n        a = 1\n      end\n\n      foo\n      a\n      CRYSTAL\n  end\n\n  it \"suggest that there might be a typo for an initialize method\" do\n    assert_error <<-CRYSTAL, \"do you maybe have a typo in this 'intialize' method?\"\n      class Foo\n        def intialize(x)\n        end\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"suggest that there might be a typo for an initialize method in inherited class\" do\n    assert_error <<-CRYSTAL, \"do you maybe have a typo in this 'intialize' method?\"\n      class Foo\n        def initialize\n        end\n      end\n\n      class Bar < Foo\n        def intialize(x)\n        end\n      end\n\n      Bar.new(1)\n      CRYSTAL\n  end\n\n  it \"suggest that there might be a typo for an initialize method with overload\" do\n    assert_error <<-CRYSTAL, \"do you maybe have a typo in this 'intialize' method?\"\n      class Foo\n        def initialize(x : Int32)\n        end\n\n        def intialize(y : Float64)\n        end\n      end\n\n      Foo.new(1.0)\n      CRYSTAL\n  end\n\n  it \"suggests for class variable\" do\n    assert_error <<-CRYSTAL, \"Did you mean '@@foobar'?\"\n      class Foo\n        @@foobar = 1\n        @@fooobar\n      end\n      CRYSTAL\n  end\n\n  it \"suggests a better alternative to logical operators (#2715)\" do\n    ex = assert_error <<-CRYSTAL, \"undefined method 'and' for top-level\"\n           def rand(x : Int32)\n           end\n\n           class String\n             def bytes\n               self\n             end\n           end\n\n           if \"a\".bytes and 1\n             1\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"Did you mean '&&'?\"\n  end\n\n  it \"says did you mean in instance var declaration\" do\n    assert_error <<-CRYSTAL, \"Did you mean 'FooBar'?\"\n      class FooBar\n      end\n\n      class Foo\n        @x : FooBaz\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/doc_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: doc\" do\n  it \"stores doc for class\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      class Foo\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for abstract class\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      abstract class Foo\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for struct\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      struct Foo\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for module\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      module Foo\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for def\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      class Foo\n        # Hello\n        def bar\n        end\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    bar = foo.lookup_defs(\"bar\").first\n    bar.doc.should eq(\"Hello\")\n  end\n\n  describe \":ditto:\" do\n    it \"stores doc for const\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        # A number\n        ONE = 1\n\n        # :ditto:\n        TWO = 2\n        CRYSTAL\n      program = result.program\n      program.types[\"ONE\"].doc.should eq \"A number\"\n      program.types[\"TWO\"].doc.should eq \"A number\"\n    end\n\n    it \"stores doc for def\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        class Foo\n          # Hello\n          def bar\n          end\n\n          # :ditto:\n          def bar2\n          end\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      bar = foo.lookup_defs(\"bar2\").first\n      bar.doc.should eq(\"Hello\")\n    end\n\n    it \"stores doc for macro\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        # Hello\n        macro bar\n        end\n\n        # :ditto:\n        macro bar2\n        end\n        CRYSTAL\n      program = result.program\n      bar2 = program.lookup_macros(\"bar2\").as(Array(Macro)).first\n      bar2.doc.should eq(\"Hello\")\n    end\n\n    it \"amend previous doc\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        class Foo\n          # Hello\n          def bar\n          end\n\n          # :ditto:\n          #\n          # World\n          def bar2\n          end\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      bar = foo.lookup_defs(\"bar2\").first\n      bar.doc.should eq(\"Hello\\n\\nWorld\")\n    end\n\n    it \"amend previous doc (without empty line)\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        class Foo\n          # Hello\n          def bar\n          end\n\n          # :ditto:\n          # World\n          def bar2\n          end\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      bar = foo.lookup_defs(\"bar2\").first\n      bar.doc.should eq(\"Hello\\n\\nWorld\")\n    end\n\n    it \":ditto: references last non-ditto doc\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        class Foo\n          # Hello\n          def bar\n          end\n\n          # :ditto:\n          #\n          # World\n          def bar2\n          end\n\n          # :ditto:\n          #\n          # Crystal\n          def bar3\n          end\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      bar = foo.lookup_defs(\"bar3\").first\n      bar.doc.should eq(\"Hello\\n\\nCrystal\")\n    end\n  end\n\n  it \"stores doc for def with visibility\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      class Foo\n        # Hello\n        private def bar\n        end\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    bar = foo.lookup_defs(\"bar\").first\n    bar.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for def with annotation\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      class Foo\n        # Hello\n        @[AlwaysInline]\n        def bar\n        end\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    bar = foo.lookup_defs(\"bar\").first\n    bar.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for def with annotation\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      @[AlwaysInline]\n      fun bar : Int32\n        1\n      end\n      CRYSTAL\n    program = result.program\n    bar = program.lookup_defs(\"bar\").first\n    bar.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for abstract def\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      abstract class Foo\n        # Hello\n        abstract def bar\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    bar = foo.lookup_defs(\"bar\").first\n    bar.doc.should eq(\"Hello\")\n  end\n\n  {% for def_type in %w[def macro].map &.id %}\n    it \"overwrites doc for {{def_type}} when redefining\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        module Foo\n          # Doc 1\n          {{def_type}} bar\n          end\n        end\n\n        module Foo\n          # Doc 2\n          {{def_type}} bar\n          end\n        end\n\n        module Foo\n          {{def_type}} bar\n          end\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      bar = foo.lookup_{{def_type}}s(\"bar\").as(Array).first\n      bar.doc.should eq(\"Doc 2\")\n    end\n  {% end %}\n\n  it \"stores doc for macro\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      class Foo\n        # Hello\n        macro bar\n        end\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    bar = foo.metaclass.lookup_macros(\"bar\").as(Array(Macro)).first\n    bar.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for fun def\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      fun foo : Int32\n        1\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.lookup_defs(\"foo\").first\n    foo.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for enum\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      enum Foo\n        A\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for flags enum with base type\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      @[Flags]\n      enum Foo : UInt8\n        A\n      end\n      CRYSTAL\n    program = result.program\n    ann = program.types[\"Flags\"].as(Crystal::AnnotationType)\n    foo = program.types[\"Foo\"]\n    foo.annotation(ann).should_not be_nil\n    foo.doc.should eq(\"Hello\")\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for enum and doesn't mix with value\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      enum Foo\n        # World\n        World\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for enum with @[Flags]\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      @[Flags]\n      enum Foo\n        A\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for enum member\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      enum Foo\n        # Hello\n        A = 1\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    a = foo.types[\"A\"]\n    a.doc.should eq(\"Hello\")\n    a.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores location for implicit flag enum members\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      @[Flags]\n      enum Foo\n        A = 1\n        B = 2\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n\n    a_loc = foo.types[\"All\"].locations.should_not be_nil\n    a_loc.should_not be_empty\n\n    b_loc = foo.types[\"None\"].locations.should_not be_nil\n    b_loc.should_not be_empty\n  end\n\n  it \"stores doc for constant\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      CONST = 1\n      CRYSTAL\n    program = result.program\n    a = program.types[\"CONST\"]\n    a.doc.should eq(\"Hello\")\n    a.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for alias\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      # Hello\n      alias Alias = Int32\n      CRYSTAL\n    program = result.program\n    a = program.types[\"Alias\"]\n    a.doc.should eq(\"Hello\")\n    a.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"stores doc for nodes defined in macro call\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      class Object\n        macro property(name)\n          def {{name}}=(@{{name}})\n          end\n\n          def {{name}}\n            @{{name}}\n          end\n        end\n      end\n\n      class Foo\n        # Hello\n        property bar\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n\n    bar = foo.lookup_defs(\"bar\").first\n    bar.doc.should eq(\"Hello\")\n\n    bar_assign = foo.lookup_defs(\"bar=\").first\n    bar_assign.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for nodes defined in macro call (2)\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      macro foo\n        class Foo\n        end\n      end\n\n      # Hello\n      foo\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.doc.should eq(\"Hello\")\n  end\n\n  it \"stores doc for macro defined in macro call\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      macro def_foo\n        macro foo\n        end\n      end\n\n      # Hello\n      def_foo\n      CRYSTAL\n    program = result.program\n    foo = program.macros.not_nil![\"foo\"].first\n    foo.doc.should eq(\"Hello\")\n  end\n\n  {% for module_type in %w[class struct module enum].map &.id %}\n    it \"stores doc for {{module_type}} when reopening\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        {{module_type}} Foo\n          A = 1\n        end\n\n        # Hello\n        {{module_type}} Foo\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      foo.doc.should eq(\"Hello\")\n      foo.locations.not_nil!.size.should eq(2)\n    end\n\n    it \"overwrites doc for {{module_type}} when reopening\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        # Doc 1\n        {{module_type}} Foo\n          A = 1\n        end\n\n        # Doc 2\n        {{module_type}} Foo\n        end\n\n        {{module_type}} Foo\n        end\n        CRYSTAL\n      program = result.program\n      foo = program.types[\"Foo\"]\n      foo.doc.should eq(\"Doc 2\")\n    end\n  {% end %}\n\n  it \"stores locations for auto-generated module\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      class Foo::Bar\n      end\n      CRYSTAL\n    program = result.program\n    foo = program.types[\"Foo\"]\n    foo.locations.not_nil!.size.should eq(1)\n  end\n\n  it \"attaches doc in double macro expansion (#8463)\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      macro cls(nr)\n        class MyClass{{nr}} end\n      end\n\n      macro cls2(nr)\n        cls({{nr}})\n      end\n\n      # Some description\n      cls2(1)\n      CRYSTAL\n    program = result.program\n    type = program.types[\"MyClass1\"]\n    type.doc.should eq(\"Some description\")\n  end\n\n  it \"attaches doc to annotation in macro expansion (#9628)\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      macro ann\n        annotation MyAnnotation\n        end\n      end\n\n      # Some description\n      ann\n      CRYSTAL\n    program = result.program\n    type = program.types[\"MyAnnotation\"]\n    type.doc.should eq(\"Some description\")\n  end\n\n  context \"doc before annotation\" do\n    it \"attached to struct/class\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        # Some description\n        @[Packed]\n        struct Foo\n        end\n        CRYSTAL\n      program = result.program\n      type = program.types[\"Foo\"]\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to module\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        # Some description\n        @[Ann]\n        module Foo\n        end\n        CRYSTAL\n      program = result.program\n      type = program.types[\"Foo\"]\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to enum\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        # Some description\n        @[Ann]\n        enum Foo\n          One\n        end\n        CRYSTAL\n      program = result.program\n      type = program.types[\"Foo\"]\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to constant\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        # Some description\n        @[Ann]\n        Foo = 1\n        CRYSTAL\n      program = result.program\n      type = program.types[\"Foo\"]\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to alias\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        # Some description\n        @[Ann]\n        alias Foo = Int32\n        CRYSTAL\n      program = result.program\n      type = program.types[\"Foo\"]\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to def\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        # Some description\n        @[Ann]\n        def foo\n        end\n        CRYSTAL\n      program = result.program\n      a_def = program.lookup_defs(\"foo\").first\n      a_def.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to macro\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        # Some description\n        @[Ann]\n        macro foo\n        end\n        CRYSTAL\n      program = result.program\n      type = program.lookup_macros(\"foo\").as(Array(Macro)).first\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to macro call\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        macro gen_type\n          class Foo; end\n        end\n\n        # Some description\n        @[Ann]\n        gen_type\n        CRYSTAL\n      program = result.program\n      type = program.types[\"Foo\"]\n      type.doc.should eq(\"Some description\")\n    end\n\n    it \"attached to macro call that produces multiple types\" do\n      result = semantic <<-CRYSTAL, wants_doc: true\n        annotation Ann\n        end\n\n        class Foo\n          macro getter(decl)\n            @{{decl.var.id}} : {{decl.type.id}}\n\n            def {{decl.var.id}} : {{decl.type.id}}\n              @{{decl.var.id}}\n            end\n          end\n\n          # Some description\n          @[Ann]\n          getter name : String?\n        end\n        CRYSTAL\n      program = result.program\n      a_def = program.types[\"Foo\"].lookup_defs(\"name\").first\n      a_def.doc.should eq(\"Some description\")\n    end\n  end\n\n  it \"expands record macro with comments (#16074)\" do\n    result = semantic <<-CRYSTAL, wants_doc: true\n      require \"macros\"\n      require \"object/properties\"\n\n      record Foo,\n        # This is a multiline\n        # comment\n        name : String?,\n        # This is a single line comment\n        val = 1\n\n      Foo.new(\"test\").name\n      Foo.new(\"test\").val\n    CRYSTAL\n\n    foo = result.program.types[\"Foo\"]\n    foo.lookup_defs(\"val\").first.doc.should eq(\"This is a single line comment\")\n    foo.lookup_defs(\"name\").first.doc.should eq(\"This is a multiline\\ncomment\")\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/double_splat_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: double splat\" do\n  it \"double splats named argument into arguments (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x, y)\n        x\n      end\n\n      tup = {x: 1, y: 'a'}\n      foo **tup\n      CRYSTAL\n  end\n\n  it \"double splats named argument into arguments (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x, y)\n        x\n      end\n\n      tup = {y: 'a', x: 1}\n      foo **tup\n      CRYSTAL\n  end\n\n  it \"errors if duplicate keys on call side with two double splats\" do\n    assert_error <<-CRYSTAL, \"duplicate key: x\"\n      def foo(**args)\n      end\n\n      t1 = {x: 1, y: 2}\n      t2 = {z: 3, x: 4}\n      foo **t1, **t2\n      CRYSTAL\n  end\n\n  it \"errors if duplicate keys on call side with double splat and named args\" do\n    assert_error <<-CRYSTAL, \"duplicate key: x\"\n      def foo(**args)\n      end\n\n      t1 = {x: 1, y: 2}\n      foo **t1, z: 3, x: 4\n      CRYSTAL\n  end\n\n  it \"errors missing argument with double splat\" do\n    assert_error <<-CRYSTAL, \"missing argument: y\"\n      def foo(x, y)\n      end\n\n      tup = {x: 1}\n      foo **tup\n      CRYSTAL\n  end\n\n  it \"matches double splat on method (empty)\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({} of String => Type) }\n      def foo(**args)\n        args\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"matches double splat on method with named args\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": char}) }\n      def foo(**args)\n        args\n      end\n\n      foo x: 1, y: 'a'\n      CRYSTAL\n  end\n\n  it \"matches double splat on method with named args and regular args\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"y\": char, \"z\": int32}) }\n      def foo(x, **args)\n        args\n      end\n\n      foo y: 'a', z: 3, x: \"foo\"\n      CRYSTAL\n  end\n\n  it \"matches double splat with regular splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, char]), named_tuple_of({\"x\": string, \"y\": bool})]) }\n      def foo(*args, **options)\n        {args, options}\n      end\n\n      foo 1, 'a', x: \"foo\", y: true\n      CRYSTAL\n  end\n\n  it \"uses double splat in new\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize(**options)\n          @x = options[:x]\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(x: 1).x\n      CRYSTAL\n  end\n\n  it \"uses restriction on double splat, doesn't match with empty named tuple\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(**options : Int32)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"uses restriction on double splat, doesn't match with empty named tuple (2)\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(x, **options : Int32)\n      end\n\n      foo x: 1\n      CRYSTAL\n  end\n\n  it \"uses restriction on double splat, means all types must be that type\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(**options : Int32)\n      end\n\n      foo x: 1, y: 'a'\n      CRYSTAL\n  end\n\n  it \"overloads based on double splat restriction\" do\n    assert_type(<<-CRYSTAL) { tuple_of([string, bool]) }\n      def foo(**options : Int32)\n        true\n      end\n\n      def foo(**options : Char)\n        \"foo\"\n      end\n\n      x1 = foo x: 'a', y: 'b'\n      x2 = foo x: 1, y: 2\n      {x1, x2}\n      CRYSTAL\n  end\n\n  it \"uses double splat restriction\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\" => int32, \"y\" => char}).metaclass }\n      def foo(**options : **T) forall T\n        T\n      end\n\n      foo x: 1, y: 'a'\n      CRYSTAL\n  end\n\n  it \"uses double splat restriction, matches empty\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({} of String => Type).metaclass }\n      def foo(**options : **T) forall T\n        T\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"uses double splat restriction with concrete type\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      struct NamedTuple(T)\n        def self.foo(**options : **T)\n        end\n      end\n\n      NamedTuple(x: Int32, y: Char).foo(x: 1, y: true)\n      CRYSTAL\n  end\n\n  it \"matches named args producing an empty double splat (#2678)\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({} of String => Type) }\n      def test(x, **kwargs)\n        kwargs\n      end\n\n      test(x: 7)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/enum_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: enum\" do\n  it \"types enum\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      enum Foo\n        A = 1\n      end\n      Foo::A\n      CRYSTAL\n  end\n\n  it \"types enum value\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      enum Foo\n        A = 1\n      end\n      Foo::A.value\n      CRYSTAL\n  end\n\n  it \"disallows implicit conversion of int to enum\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Foo, not Int32\"\n      enum Foo\n        A = 1\n      end\n\n      def foo(x : Foo)\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"finds method in enum type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Enum\n        def foo\n          1\n        end\n      end\n\n      enum Foo\n        A = 1\n      end\n\n      Foo::A.foo\n      CRYSTAL\n  end\n\n  it \"finds class method in enum type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Enum\n        def self.foo\n          1\n        end\n      end\n\n      enum Foo\n        A = 1\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"errors if using a name twice\" do\n    assert_error <<-CRYSTAL, \"enum 'Foo' already contains a member named 'A'\"\n      enum Foo\n        A\n        A\n      end\n      CRYSTAL\n  end\n\n  it \"creates enum from value\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      enum Foo\n        A\n        B\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"defines method on enum\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      enum Foo\n        A\n        B\n\n        def foo\n          1\n        end\n      end\n\n      Foo::A.foo\n      CRYSTAL\n  end\n\n  it \"defines class method on enum\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      enum Foo\n        A\n        B\n\n        def self.foo\n          1\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"reopens an enum\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      enum Foo\n        A\n        B\n      end\n\n      enum Foo\n        def foo\n          1\n        end\n      end\n\n      Foo::A.foo\n      CRYSTAL\n  end\n\n  it \"errors if reopen but not enum\" do\n    assert_error <<-CRYSTAL, \"Foo is not a enum, it's a class\"\n      class Foo\n      end\n\n      enum Foo\n        A\n        B\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopen and tries to define constant\" do\n    assert_error <<-CRYSTAL, \"can't reopen enum and add more constants to it\"\n      enum Foo\n        A\n        B\n      end\n\n      enum Foo\n        C\n      end\n      CRYSTAL\n  end\n\n  it \"has None value when defined as @[Flags]\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Flags]\n      enum Foo\n        A\n        B\n      end\n\n      Foo::None.value\n      CRYSTAL\n  end\n\n  it \"has All value when defined as @[Flags]\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Flags]\n      enum Foo\n        A\n        B\n      end\n\n      Foo::All.value\n      CRYSTAL\n  end\n\n  it \"doesn't break assigned values in enum flags when a member has value 0 (#5767)\" do\n    result = semantic(<<-CRYSTAL)\n      @[Flags]\n      enum Foo\n        OtherNone = 0\n        Bar\n        Baz\n      end\n      CRYSTAL\n    enum_type = result.program.types[\"Foo\"].as(EnumType)\n    enum_type.types[\"OtherNone\"].as(Const).value.should eq(NumberLiteral.new(\"0\", :i32))\n    enum_type.types[\"Bar\"].as(Const).value.should eq(NumberLiteral.new(\"1\", :i32))\n    enum_type.types[\"Baz\"].as(Const).value.should eq(NumberLiteral.new(\"2\", :i32))\n  end\n\n  it \"disallows redefining None to non-0 for @[Flags] enum\" do\n    assert_error <<-CRYSTAL, \"flags enum can't redefine None member to non-0\"\n      @[Flags]\n      enum Foo\n        None = 42\n        Dummy\n      end\n      CRYSTAL\n\n    assert_error <<-CRYSTAL, \"flags enum can't redefine None member to non-0\"\n      @[Flags]\n      enum Foo\n        None    # 1\n        Dummy\n      end\n      CRYSTAL\n  end\n\n  it \"allows redefining None to 0 for @[Flags] enum\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Flags]\n      enum Foo\n        None = 0\n        Dummy\n      end\n\n      Foo::None.value\n      CRYSTAL\n  end\n\n  it \"disallows All value for @[Flags] enum\" do\n    assert_error <<-CRYSTAL, \"flags enum can't redefine All member\"\n      @[Flags]\n      enum Foo\n        All = 50\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error when defining a non-flags enum with None or All\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      enum Foo\n        None\n        All = 50\n      end\n\n      Foo::None.value\n      CRYSTAL\n  end\n\n  it \"doesn't error when defining a flags enum in a lib with None or All\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib Lib\n        @[Flags]\n        enum Foo\n          None\n          Dummy\n          All = 50\n        end\n      end\n\n      Lib::Foo::None.value\n      CRYSTAL\n  end\n\n  it \"doesn't error when defining a method for an enum with flags\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      @[Flags]\n      enum Foo\n        A\n        B\n\n        def foo\n          self\n        end\n      end\n\n      Foo::A.foo\n      CRYSTAL\n  end\n\n  it \"allows class vars in enum\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      enum Foo\n        A\n\n        @@class_var = 1\n\n        def self.class_var\n          @@class_var\n        end\n      end\n\n      Foo.class_var\n      CRYSTAL\n  end\n\n  it \"errors if invoking private enum method\" do\n    assert_error <<-CRYSTAL, \"private method 'foo' called for Foo\"\n      enum Foo\n        A\n\n        private def foo\n          1\n        end\n      end\n\n      Foo::A.foo\n      CRYSTAL\n  end\n\n  it \"errors if enum value is too big for type (#678)\" do\n    assert_error <<-CRYSTAL, \"invalid Int32: 2147486719\"\n      enum Foo\n        A = 2147486719\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using instance var inside enum (#991)\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables inside enums (at enum Foo)\"\n      enum Foo\n        A\n\n        def meth\n          puts @value\n        end\n      end\n\n      Foo::A.meth\n      CRYSTAL\n  end\n\n  it \"marks as flags with base type (#2185)\" do\n    result = semantic(<<-CRYSTAL)\n      @[Flags]\n      enum SomeFacts : UInt8\n        AppleLover\n        PearLover\n        CoolDude\n      end\n\n      SomeFacts::AppleLover\n      CRYSTAL\n    enum_type = result.program.types[\"SomeFacts\"].as(EnumType)\n    annotation_type = result.program.types[\"Flags\"].as(AnnotationType)\n    enum_type.annotation(annotation_type).should_not be_nil\n  end\n\n  it \"reopens enum without base type (1)\" do\n    assert_no_errors <<-CRYSTAL\n      enum Foo\n        X\n      end\n\n      enum Foo\n      end\n      CRYSTAL\n  end\n\n  it \"reopens enum without base type (2)\" do\n    assert_no_errors <<-CRYSTAL\n      enum Foo : UInt8\n        X\n      end\n\n      enum Foo\n      end\n      CRYSTAL\n  end\n\n  it \"reopens enum with same base type (1)\" do\n    assert_no_errors <<-CRYSTAL\n      enum Foo\n        X\n      end\n\n      enum Foo : Int32\n      end\n      CRYSTAL\n  end\n\n  it \"reopens enum with same base type (2)\" do\n    assert_no_errors <<-CRYSTAL\n      enum Foo : UInt8\n        X\n      end\n\n      enum Foo : UInt8\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening enum with different base type (1)\" do\n    assert_error <<-CRYSTAL, \"enum Foo's base type is Int32, not UInt8\"\n      enum Foo\n        X\n      end\n\n      enum Foo : UInt8\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening enum with different base type (2)\" do\n    assert_error <<-CRYSTAL, \"enum Foo's base type is UInt8, not UInt16\"\n      enum Foo : UInt8\n        X\n      end\n\n      enum Foo : UInt16\n      end\n      CRYSTAL\n  end\n\n  it \"can use macro expression inside enum\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      enum Foo\n        {{ \"A\".id }}\n      end\n\n      Foo::A\n      CRYSTAL\n  end\n\n  it \"can use macro for inside enum\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      enum Foo\n        {% for name in %w(A B C) %}\n          {{name.id}}\n        {% end %}\n      end\n\n      Foo::A\n      CRYSTAL\n  end\n\n  it \"errors if inheriting Enum (#3592)\" do\n    assert_error <<-CRYSTAL, \"can't inherit Enum. Use the enum keyword to define enums\"\n      struct Foo < Enum\n      end\n      CRYSTAL\n  end\n\n  it \"errors on enum without members (#3447)\" do\n    assert_error <<-CRYSTAL, \"enum Foo must have at least one member\"\n      enum Foo\n      end\n      CRYSTAL\n\n    assert_error <<-CRYSTAL, \"enum Foo must have at least one member\"\n      @[Flags]\n      enum Foo\n        None = 0\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declaring type inside enum (#3127)\" do\n    assert_error <<-CRYSTAL, \"can't declare type inside enum Foo\"\n      enum Foo\n        A\n      end\n\n      class Foo::Bar\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declaring type inside enum, nested (#3127)\" do\n    assert_error <<-CRYSTAL, \"can't declare type inside enum\"\n      enum Foo\n        A\n      end\n\n      class Foo::Bar::Baz\n      end\n      CRYSTAL\n  end\n\n  it \"attaches annotation to enum method (#6690)\" do\n    result = semantic(<<-CRYSTAL)\n      enum Foo\n        X\n\n        @[AlwaysInline]\n        def bar\n        end\n      end\n      CRYSTAL\n\n    method = result.program.types[\"Foo\"].lookup_first_def(\"bar\", block: false).not_nil!\n    method.always_inline?.should be_true\n  end\n\n  it \"errors if defining initialize in Enum (#7238)\" do\n    assert_error <<-CRYSTAL, \"enums can't define an `initialize` method, try using `def self.new`\"\n      enum Foo\n        FOO = 1\n\n        def initialize\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can redefine Enum.new\" do\n    assert_type(<<-CRYSTAL) { string }\n      enum Foo\n        FOO = 1\n\n        def self.new(x : Int32)\n          \"hello\"\n        end\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"gives error on enum overflow\" do\n    assert_error <<-CRYSTAL, \"value of enum member V129 would overflow the base type Int8\"\n      enum Foo : Int8\n        #{Array.new(129) { |i| \"V#{i + 1}\" }.join \"\\n\"}\n      end\n      CRYSTAL\n  end\n\n  it \"gives error on flags enum overflow\" do\n    assert_error <<-CRYSTAL, \"value of enum member V9 would overflow the base type UInt8\"\n      @[Flags]\n      enum Foo : UInt8\n        #{Array.new(9) { |i| \"V#{i + 1}\" }.join \"\\n\"}\n      end\n      CRYSTAL\n  end\n\n  it \"gives error on enum overflow after a member with value\" do\n    assert_error <<-CRYSTAL, \"value of enum member B would overflow the base type Int32\"\n      enum Foo\n        A = 0x7FFFFFFF\n        B\n      end\n      CRYSTAL\n  end\n\n  it \"gives error on signed flags enum overflow after a member with value\" do\n    assert_error <<-CRYSTAL, \"value of enum member B would overflow the base type Int32\"\n      @[Flags]\n      enum Foo\n        A = 0x40000000\n        B\n      end\n      CRYSTAL\n  end\n\n  it \"gives error on unsigned flags enum overflow after a member with value\" do\n    assert_error <<-CRYSTAL, \"value of enum member B would overflow the base type UInt32\"\n      @[Flags]\n      enum Foo : UInt32\n        A = 0x80000000\n        B\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't overflow when going from negative to zero (#7874)\" do\n    assert_no_errors <<-CRYSTAL\n      enum Nums\n        Zero  = -2\n        One\n        Two\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't overflow on flags member (#7877)\" do\n    assert_no_errors <<-CRYSTAL\n      @[Flags]\n      enum Filter\n        A = 1 << 29\n        B\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't visit enum members generated by macros twice (#10104)\" do\n    result = semantic(<<-CRYSTAL)\n      enum Foo\n        A = 1\n\n        {% begin %}\n          def foo\n          end\n        {% end %}\n      end\n      CRYSTAL\n    a_def = result.program.types[\"Foo\"].lookup_defs(\"foo\").first\n    a_def.previous.should be_nil\n  end\n\n  it \"adds docs to helper methods\" do\n    result = top_level_semantic <<-CRYSTAL, wants_doc: true\n    enum Foo\n      # These are the docs for `Bar`\n      Bar = 1\n    end\n    CRYSTAL\n\n    a_defs = result.program.types[\"Foo\"].lookup_defs(\"bar?\")\n    a_defs.first.doc.should eq(\"Returns `true` if this enum value equals `Bar`\")\n  end\n\n  it \"marks helper methods with `:nodoc:` if the member is `:nodoc:`\" do\n    result = top_level_semantic <<-CRYSTAL, wants_doc: true\n    enum Foo\n      # :nodoc:\n      Bar = 1\n    end\n    CRYSTAL\n\n    a_defs = result.program.types[\"Foo\"].lookup_defs(\"bar?\")\n    a_defs.first.doc.should eq(\":nodoc:\")\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/exception_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: exception\" do\n  it \"type is union of main and rescue blocks\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      begin\n        1\n      rescue\n        'a'\n      end\n      CRYSTAL\n  end\n\n  it \"type union with empty main block\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      begin\n      rescue\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"type union with empty rescue block\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      begin\n        1\n      rescue\n      end\n      CRYSTAL\n  end\n\n  it \"type for exception handler for explicit types\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      class MyEx < Exception\n      end\n\n      begin\n        raise MyEx.new\n      rescue MyEx\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"marks method calling method that raises as raises\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        @[Raises]\n        fun some_fun : Int32\n      end\n\n      def foo\n        LibFoo.some_fun\n      end\n\n      foo\n      CRYSTAL\n    mod = result.program\n    a_def = mod.lookup_first_def(\"foo\", false)\n    def_instance = mod.lookup_def_instance DefInstanceKey.new(a_def.object_id, [] of Type, nil, nil)\n    def_instance.not_nil!.raises?.should be_true\n  end\n\n  it \"marks method calling lib fun that raises as raises\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      @[Raises]\n      fun some_fun : Int32; 1; end\n\n      def foo\n        some_fun\n      end\n\n      foo\n      CRYSTAL\n    mod = result.program\n    a_def = mod.lookup_first_def(\"foo\", false)\n    def_instance = mod.lookup_def_instance DefInstanceKey.new(a_def.object_id, [] of Type, nil, nil)\n    def_instance.not_nil!.raises?.should be_true\n  end\n\n  it \"types exception var with no types\" do\n    assert_type(<<-CRYSTAL) { union_of(nil_type, exception.virtual_type) }\n      a = nil\n      begin\n      rescue ex\n        a = ex\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types exception with type\" do\n    assert_type(<<-CRYSTAL) { union_of(nil_type, types[\"Ex\"].virtual_type) }\n      class Ex < Exception\n      end\n\n      a = nil\n      begin\n      rescue ex : Ex\n        a = ex\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types var as not nil if defined inside begin and defined inside rescue\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      begin\n        a = 1\n      rescue\n        a = 2\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types var as nilable if previously nilable (1)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      if 1 == 2\n        a = 1\n      end\n\n      begin\n        a = 2\n      rescue\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types var as nilable if previously nilable (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      if 1 == 2\n        a = 1\n      end\n\n      begin\n      rescue\n        a = 2\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"errors if caught exception is not a subclass of Exception\" do\n    assert_error \"begin; rescue ex : Int32; end\", \"Int32 cannot be used for `rescue`. Only subclasses of `Exception` and modules, or unions thereof, are allowed.\"\n  end\n\n  it \"errors if caught exception is a union but not all types are valid\" do\n    assert_error \"begin; rescue ex : Union(Exception, String); end\", \"(Exception | String) cannot be used for `rescue`. Only subclasses of `Exception` and modules, or unions thereof, are allowed.\"\n  end\n\n  it \"errors if caught exception is a nested union but not all types are valid\" do\n    assert_error \"begin; rescue ex : Union(Exception, Union(Exception, String)); end\", \"(Exception | String) cannot be used for `rescue`. Only subclasses of `Exception` and modules, or unions thereof, are allowed.\"\n  end\n\n  it \"errors if caught exception is not a subclass of Exception without var\" do\n    assert_error \"begin; rescue Int32; end\", \"Int32 cannot be used for `rescue`. Only subclasses of `Exception` and modules, or unions thereof, are allowed.\"\n  end\n\n  assert_syntax_error \"begin; rescue ex; rescue ex : Foo; end; ex\",\n    \"specific rescue must come before catch-all rescue\"\n\n  assert_syntax_error \"begin; rescue ex; rescue; end; ex\",\n    \"catch-all rescue can only be specified once\"\n\n  assert_syntax_error \"begin; else; 1; end\",\n    \"'else' is useless without 'rescue'\"\n\n  it \"types code with abstract exception that delegates method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      class Object\n        def foo\n          bar(1)\n        end\n\n        def bar(x)\n          1\n        end\n      end\n\n      class SomeException < ::Exception\n      end\n\n      abstract class FooException < ::Exception\n        def bar(io)\n          bar2(nil, io)\n        end\n      end\n\n      begin\n      rescue ex\n        ex.foo\n      end\n\n      1\n      CRYSTAL\n  end\n\n  it \"transform nodes in else block\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      begin\n      rescue\n      else\n        1 || nil\n      end\n      CRYSTAL\n  end\n\n  it \"types var as nilable inside ensure (1)\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      n = nil\n      begin\n        raise \"hey\"\n        n = 3\n      ensure\n        p n\n      end\n      n\n      CRYSTAL\n    mod = result.program\n    eh = result.node.as(Expressions).expressions[-2]\n    call_p_n = eh.as(ExceptionHandler).ensure.not_nil!.as(Call)\n    call_p_n.args.first.type.should eq(mod.nilable(mod.int32))\n  end\n\n  it \"types var as nilable inside ensure (2)\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      begin\n        raise \"hey\"\n        n = 3\n      ensure\n        p n\n      end\n      n\n      CRYSTAL\n    mod = result.program\n    eh = result.node.as(Expressions).expressions[-2]\n    call_p_n = eh.as(ExceptionHandler).ensure.not_nil!.as(Call)\n    call_p_n.args.first.type.should eq(mod.nilable(mod.int32))\n  end\n\n  it \"marks fun as raises\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      @[Raises]\n      fun foo : Int32; 1; end\n      foo\n      CRYSTAL\n    mod = result.program\n    a_def = mod.lookup_first_def(\"foo\", false)\n    a_def.not_nil!.raises?.should be_true\n  end\n\n  it \"marks def as raises\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      @[Raises]\n      def foo\n        1\n      end\n\n      foo\n      CRYSTAL\n    mod = result.program\n    a_def = mod.lookup_first_def(\"foo\", false)\n    a_def.not_nil!.raises?.should be_true\n  end\n\n  it \"marks method that calls another method that raises as raises, recursively\" do\n    result = assert_type(<<-CRYSTAL) { int32 }\n      @[Raises]\n      def foo\n        1\n      end\n\n      def bar\n        foo\n      end\n\n      def baz\n        bar\n      end\n\n      foo\n      bar\n      baz\n      CRYSTAL\n    call = result.node.as(Expressions).expressions.last.as(Call)\n    call.target_defs.not_nil!.first.raises?.should be_true\n  end\n\n  it \"marks proc literal as raises\" do\n    result = assert_type(\"->{ 1 }.call\", inject_primitives: true) { int32 }\n    call = result.node.as(Expressions).last.as(Call)\n    call.target_def.raises?.should be_true\n  end\n\n  it \"shadows local variable (1)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, types[\"Exception\"].virtual_type) }\n      require \"prelude\"\n\n      a = 1\n      begin\n        raise \"OH NO\"\n      rescue a\n        a\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"remains nilable after rescue\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Exception\"].virtual_type }\n      require \"prelude\"\n\n      begin\n        raise \"OH NO\"\n      rescue a\n        a\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't consider vars as nilable inside else (#610)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      x = 1\n      begin\n        a = 1\n      rescue\n      else\n        x = a\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"types instance variable as nilable if assigned inside an exception handler (#1845)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@bar' of Foo must be Int32, not Nil\"\n      class Foo\n        def initialize\n          begin\n            @bar = 1\n          rescue\n          end\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      foo = Foo.new\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"doesn't type instance variable as nilable if assigned inside an exception handler after being assigned\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @bar = 1\n          begin\n            @bar = 1\n          rescue\n          end\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      foo = Foo.new\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"correctly types #1988\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      begin\n        x = 1\n      rescue\n      end\n\n      if x.is_a?(Int32)\n        x\n      else\n        x\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash on break inside rescue, in while (#2441)\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Exception\"].virtual_type }\n      while true\n        begin\n        rescue ex\n          break\n        end\n      end\n\n      ex\n      CRYSTAL\n  end\n\n  it \"types var assignment inside block inside exception handler (#3324)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, string) }\n      def foo\n        yield\n      end\n\n      var = 1\n      begin\n        foo do\n          var = \"foo\"\n        end\n      rescue\n      end\n      var\n      CRYSTAL\n  end\n\n  it \"marks instance variable as nilable if assigned inside rescue inside initialize\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32, not Nil\"\n      require \"prelude\"\n\n      class Coco < Exception\n        def initialize(@x : Foo)\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = 1\n        rescue\n          raise Coco.new(self)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"assigns var inside ensure (1) (#3919)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      begin\n      ensure\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"assigns var inside ensure (2) (#3919)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = true\n      begin\n      ensure\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't infect type to variable before handler (#4002)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1\n      b = a\n      begin\n        a = 'a'\n      rescue\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"detects reading nil-if-read variable after exception handler (#4723)\" do\n    result = assert_type(<<-CRYSTAL) { nilable int32 }\n      if true\n        foo = 42\n      end\n\n      # Now, `@vars[\"foo\"].type` is `Int32` and `@vars[\"foo\"].nil_if_read?` is `true`.\n\n      begin\n      rescue\n      end\n\n      # If `@vars[\"foo\"].nil_if_read?` is `true`, `foo` on `program.vars`\n      # binds to `nil` node, so `program.vars[\"foo\"].type` becomes `Int32 | Nil`.\n      # However if not (it is BUG), `program.vars[\"foo\"].type` is `Int32`\n      # even though the type of the node `foo` is `Int32 | Nil`.\n      foo\n      CRYSTAL\n    program = result.program\n    program.vars[\"foo\"].type.should be(program.nilable program.int32)\n  end\n\n  it \"can't return from ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't return from ensure\")\n      def foo\n        return 1\n      ensure\n        return 2\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"can't return from block inside ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't return from ensure\")\n      def once\n        yield\n      end\n\n      def foo\n        return 1\n      ensure\n        once do\n          return 2\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"can't return from while inside ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't return from ensure\")\n      def foo\n        return 1\n      ensure\n        while true\n          return 2\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"can't use break inside while inside ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't use break inside ensure\")\n      while true\n        begin\n          break\n        ensure\n          break\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can use break inside while inside ensure (#4470)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      while true\n        begin\n          break\n        ensure\n          while true\n            break\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can't use break inside block inside ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't use break inside ensure\")\n      def loop\n        while true\n          yield\n        end\n      end\n\n      loop do\n        begin\n          break\n        ensure\n          break\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can use break inside block inside ensure (#4470)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def loop\n        while true\n          yield\n        end\n      end\n\n      loop do\n        begin\n          break\n        ensure\n          loop do\n            break\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can't use next inside while inside ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't use next inside ensure\")\n      while true\n        begin\n          break\n        ensure\n          next\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can't use next inside block inside ensure (#4470)\" do\n    assert_error(<<-CRYSTAL, \"can't use next inside ensure\")\n      def loop\n        while true\n          yield\n        end\n      end\n\n      loop do\n        begin\n          break\n        ensure\n          next\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can use next inside while inside ensure (#4470)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nil_type }\n      while true\n        begin\n          break\n        ensure\n          a = 0\n          while a < 1\n            a = 1\n            next\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can use next inside block inside ensure (#4470)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def loop\n        while true\n          yield\n        end\n      end\n\n      def once\n        yield\n      end\n\n      loop do\n        begin\n          break\n        ensure\n          once do\n            next\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"correctly types variables inside conditional inside exception handler with no-return rescue (#8012)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def foo\n        begin\n          x = 99 if false\n        rescue\n          return 10\n        end\n\n        x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"gets a non-nilable type if all rescue are unreachable (#8751)\" do\n    assert_no_errors <<-CRYSTAL, inject_primitives: true\n      while true\n        begin\n          foo = 1\n          break\n        rescue\n          foo\n          break\n        end\n\n        foo &+ 2\n      end\n      CRYSTAL\n  end\n\n  it \"correctly types variable assigned inside nested exception handler (#9769)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, string) }\n      int = 1\n      begin\n        begin\n          int = \"a\"\n        rescue\n        end\n      rescue\n      end\n      int\n      CRYSTAL\n  end\n\n  it \"types a var after begin rescue as having all possible types and nil in begin if read (2)\" do\n    assert_type(<<-CRYSTAL) { union_of [int32, char, nil_type] of Type }\n      begin\n        a = 2\n        a = 'a'\n      rescue\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var after begin rescue as having all possible types in begin and rescue\" do\n    assert_type(<<-CRYSTAL) { union_of [float64, int32, char, string, bool] of Type }\n      a = 1.5\n      begin\n        a = 2\n        a = 'a'\n        a = \"hello\"\n      rescue ex\n        a = false\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var after begin rescue as having all possible types in begin and rescue (2)\" do\n    assert_type(<<-CRYSTAL) { union_of [int32, char, string, nil_type] of Type }\n      b = 2\n      begin\n        a = 2\n        a = 'a'\n        a = \"hello\"\n      rescue ex\n        b = a\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"types a var after begin rescue with no-return in rescue\" do\n    assert_type(<<-CRYSTAL) { string }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      begin\n        a = 2\n        a = 'a'\n        a = \"hello\"\n      rescue ex\n        LibC.exit\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var after rescue as being nilable\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      begin\n      rescue\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/extern_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: extern struct\" do\n  it \"declares extern struct with no constructor\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Extern]\n      struct Foo\n        @x = uninitialized Int32\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares with constructor\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Extern]\n      struct Foo\n        @x = uninitialized Int32\n\n        def initialize(@x)\n        end\n\n        def foo\n          @x\n        end\n      end\n\n      Foo.new(1).foo\n      CRYSTAL\n  end\n\n  it \"overrides getter\" do\n    assert_type(<<-CRYSTAL) { char }\n      @[Extern]\n      struct Foo\n        @x = uninitialized Int32\n\n        def x\n          'a'\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can be passed to C fun\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      @[Extern]\n      struct Foo\n        @x = uninitialized Int32\n      end\n\n      lib LibFoo\n        fun foo(x : Foo) : Float64\n      end\n\n      LibFoo.foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"can include module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x = uninitialized Int32\n\n        def x\n          @x\n        end\n      end\n\n      @[Extern]\n      struct Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"errors if using non-primitive for field type\" do\n    assert_error <<-CRYSTAL, \"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations\"\n      class Bar\n      end\n\n      @[Extern]\n      struct Foo\n        @x = uninitialized Bar\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using non-primitive for field type via module\" do\n    assert_error <<-CRYSTAL, \"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations\"\n      class Bar\n      end\n\n      module Moo\n        @x = uninitialized Bar\n      end\n\n      @[Extern]\n      struct Foo\n        include Moo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using non-primitive type in constructor\" do\n    assert_error <<-CRYSTAL, \"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations\"\n      class Bar\n      end\n\n      @[Extern]\n      struct Foo\n        def initialize\n          @x = Bar.new\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"declares extern union with no constructor\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Extern(union: true)]\n      struct Foo\n        @x = uninitialized Int32\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can use extern struct in lib\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      @[Extern]\n      struct Foo\n      end\n\n      lib LibFoo\n        fun foo(x : Foo) : Foo\n      end\n\n      foo = Foo.new\n      LibFoo.foo(foo)\n      CRYSTAL\n  end\n\n  it \"can new with named args\" do\n    assert_type(<<-CRYSTAL) { types[\"A\"] }\n      @[Extern]\n      struct A\n        def initialize(@x : Int32)\n        end\n      end\n\n      A.new(x: 6)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/external_internal_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: external/internal\" do\n  it \"can call with external name and use with internal\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x y)\n        y\n      end\n\n      foo x: 10\n      CRYSTAL\n  end\n\n  it \"can call positionally\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x y)\n        y\n      end\n\n      foo 10\n      CRYSTAL\n  end\n\n  it \"can call with external name and use with internal, after splat\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(*, x y)\n        y\n      end\n\n      foo x: 10\n      CRYSTAL\n  end\n\n  it \"overloads based on external name (#2610)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(*, bar foo)\n        1\n      end\n\n      def foo(*, baz foo)\n        2\n      end\n\n      foo(bar: 1) + foo(baz: 1)\n      CRYSTAL\n  end\n\n  context \"macros\" do\n    it \"can call with external name and use with internal\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        macro foo(x y)\n          {{y}}\n        end\n\n        foo x: 10\n        CRYSTAL\n    end\n\n    it \"can call positionally\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        macro foo(x y)\n          {{y}}\n        end\n\n        foo 10\n        CRYSTAL\n    end\n\n    it \"can call with external name and use with internal, after splat\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        macro foo(*, x y)\n          {{y}}\n        end\n\n        foo x: 10\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/fun_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: fun\" do\n  it \"errors if defining class inside fun through macro (#6874)\" do\n    assert_error <<-CRYSTAL, \"can't define class inside fun\"\n      macro m\n        class Foo\n        end\n      end\n\n      fun foo\n        m\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/generic_class_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: generic class\" do\n  it \"errors if inheriting from generic when it is non-generic\" do\n    assert_error <<-CRYSTAL, \"Foo is not a generic type, it's a class\"\n      class Foo\n      end\n\n      class Bar < Foo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if inheriting from generic and incorrect number of type vars\" do\n    assert_error <<-CRYSTAL, \"wrong number of type vars for Foo(T) (given 2, expected 1)\"\n      class Foo(T)\n      end\n\n      class Bar < Foo(A, B)\n      end\n      CRYSTAL\n  end\n\n  it \"inherits from generic with instantiation\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n        def t\n          T\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new.t\n      CRYSTAL\n  end\n\n  it \"inherits from generic with forwarding (1)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n        def t\n          T\n        end\n      end\n\n      class Bar(U) < Foo(U)\n      end\n\n      Bar(Int32).new.t\n      CRYSTAL\n  end\n\n  it \"inherits from generic with forwarding (2)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        def u\n          U\n        end\n      end\n\n      Bar(Int32).new.u\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, def context (#10834)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { int32.metaclass }\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        def t\n          T\n        end\n      end\n\n      Bar(Int32).new.t\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, metaclass context\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { int32 }\n      struct Int32\n        def self.new(x : Int32)\n          x\n        end\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        @x = T.new(0)\n      end\n\n      Bar(Int32).new.@x\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, macro context\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { char }\n      class Foo(M)\n      end\n\n      class Bar(N) < Foo(N)\n        def t\n          {{ M == 1 ? 'a' : \"\" }}\n        end\n      end\n\n      Bar(1).new.t\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, def restriction\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { int32 }\n      class Foo(T)\n      end\n\n      class Bar(U) < Foo(U)\n        def foo(x : T)\n          x\n        end\n      end\n\n      Bar(Int32).new.foo(1)\n      CRYSTAL\n  end\n\n  it \"uses inherited #initialize from superclass when generic type parameters are identical\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { int32 }\n      class Foo(T)\n        def initialize(@value : T)\n        end\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      Bar.new(25).@value\n      CRYSTAL\n  end\n\n  pending \"accesses generic type argument from superclass, inherited #initialize (1) (#5243)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { int32 }\n      class Foo(T)\n        def initialize(@value : T)\n        end\n      end\n\n      class Bar(U) < Foo(U)\n      end\n\n      Bar.new(25).@value\n      CRYSTAL\n  end\n\n  it \"accesses generic type argument from superclass, inherited #initialize (2) (#5243)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: false) { int32 }\n      class Foo(T)\n        def initialize(@value : T)\n        end\n      end\n\n      class Bar(U) < Foo(U)\n      end\n\n      Bar(Int32).new(25).@value\n      CRYSTAL\n  end\n\n  it \"inherits from generic with instantiation with instance var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new(1).x\n      CRYSTAL\n  end\n\n  it \"inherits twice\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = 1.5\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar(T) < Foo\n        def initialize(@y : T)\n          super()\n        end\n\n        def y\n          @y\n        end\n      end\n\n      class Baz < Bar(Int32)\n        def initialize(y, @z : Char)\n          super(y)\n        end\n\n        def z\n          @z\n        end\n      end\n\n      baz = Baz.new(1, 'a')\n      baz.y\n      CRYSTAL\n  end\n\n  it \"doesn't compute generic instance var initializers in formal superclass's context (#4753)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @foo = T.new\n\n        def foo\n          @foo\n        end\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz\n        def baz\n          1\n        end\n      end\n\n      Bar(Baz).new.foo.baz\n      CRYSTAL\n  end\n\n  it \"inherits non-generic to generic (1)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n        def t1\n          T\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      class Baz(T) < Bar\n      end\n\n      baz = Baz(Float64).new\n      baz.t1\n      CRYSTAL\n  end\n\n  it \"inherits non-generic to generic (2)\" do\n    assert_type(<<-CRYSTAL) { float64.metaclass }\n      class Foo(T)\n        def t1\n          T\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      class Baz(T) < Bar\n        def t2\n          T\n        end\n      end\n\n      baz = Baz(Float64).new\n      baz.t2\n      CRYSTAL\n  end\n\n  it \"defines empty initialize on inherited generic class\" do\n    assert_type(<<-CRYSTAL) { types[\"Nothing\"] }\n      class Maybe(T)\n      end\n\n      class Nothing < Maybe(Int32)\n        def initialize\n        end\n      end\n\n      Nothing.new\n      CRYSTAL\n  end\n\n  it \"restricts non-generic to generic\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      def foo(x : Foo)\n        x\n      end\n\n      foo Bar.new\n      CRYSTAL\n  end\n\n  it \"restricts non-generic to generic with free var\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      def foo(x : Foo(T)) forall T\n        T\n      end\n\n      foo Bar.new\n      CRYSTAL\n  end\n\n  it \"restricts generic to generic with free var\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      def foo(x : Foo(T)) forall T\n        T\n      end\n\n      foo Bar(Int32).new\n      CRYSTAL\n  end\n\n  it \"allows T::Type with T a generic type\" do\n    assert_type(<<-CRYSTAL) { types[\"MyType\"].types[\"Bar\"] }\n      class MyType\n        class Bar\n        end\n      end\n\n      class Foo(T)\n        def bar\n          T::Bar.new\n        end\n      end\n\n      Foo(MyType).new.bar\n      CRYSTAL\n  end\n\n  it \"error on T::Type with T a generic type that's a union\" do\n    assert_error <<-CRYSTAL, \"undefined constant T::Bar\"\n      class Foo(T)\n        def self.bar\n          T::Bar\n        end\n      end\n\n      Foo(Char | String).bar\n      CRYSTAL\n  end\n\n  it \"instantiates generic class with default argument in initialize (#394)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(T)\n        def initialize(x = 1)\n        end\n      end\n\n      Foo(Int32).new\n      CRYSTAL\n  end\n\n  it \"inherits class methods from generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.foo\n      CRYSTAL\n  end\n\n  it \"creates pointer of generic type and uses it\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      abstract class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n        def foo\n          1\n        end\n      end\n\n      ptr = Pointer(Foo(Int32)).malloc(1_u64)\n      ptr.value = Bar.new\n      ptr.value.foo\n      CRYSTAL\n  end\n\n  it \"creates pointer of generic type and uses it (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      abstract class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n        def foo\n          1\n        end\n      end\n\n      ptr = Pointer(Foo(Int32)).malloc(1_u64)\n      ptr.value = Bar(Int32).new\n      ptr.value.foo\n      CRYSTAL\n  end\n\n  it \"errors if inheriting generic type and not specifying type vars (#460)\" do\n    assert_error <<-CRYSTAL, \"generic type arguments must be specified when inheriting Foo(T)\"\n      class Foo(T)\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n  end\n\n  %w(Object Value Reference Number Int Float Struct Class Proc Tuple Enum StaticArray Pointer).each do |type|\n    it \"errors if using #{type} in a generic type\" do\n      assert_error <<-CRYSTAL, \"as generic type argument yet, use a more specific type\"\n        Pointer(#{type})\n        CRYSTAL\n    end\n  end\n\n  it \"errors if using Number | String in a generic type\" do\n    assert_error <<-CRYSTAL, \"can't use Number in unions yet, use a more specific type\"\n      Pointer(Number | String)\n      CRYSTAL\n  end\n\n  it \"errors if using Number in alias\" do\n    assert_error <<-CRYSTAL, \"can't use Number in unions yet, use a more specific type\"\n      alias Alias = Number | String\n      Alias\n      CRYSTAL\n  end\n\n  it \"errors if using Number in recursive alias\" do\n    assert_error <<-CRYSTAL, \"can't use Number in unions yet, use a more specific type\"\n      alias Alias = Number | Pointer(Alias)\n      Alias\n      CRYSTAL\n  end\n\n  it \"finds generic type argument from method with default value\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module It(T)\n        def foo(x = 0)\n          T\n        end\n      end\n\n      class Foo(B)\n        include It(B)\n      end\n\n      Foo(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"allows initializing instance variable (#665)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class SomeType(T)\n        @x = 0\n\n        def x\n          @x\n        end\n      end\n\n      SomeType(Char).new.x\n      CRYSTAL\n  end\n\n  it \"allows initializing instance variable in inherited generic type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        @y = 2\n      end\n\n      Bar(Char).new.x\n      CRYSTAL\n  end\n\n  it \"calls super on generic type when superclass has no initialize (#933)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", float32 }\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n          def initialize()\n              super()\n          end\n      end\n\n      Bar(Float32).new\n      CRYSTAL\n  end\n\n  it \"initializes instance variable of generic type using type var (#961)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", int32 }\n      class Bar(T)\n      end\n\n      class Foo(T)\n        @bar = Bar(T).new\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"errors if passing integer literal to Proc as generic argument (#1120)\" do\n    assert_error <<-CRYSTAL, \"argument to Proc must be a type, not 32\"\n      Proc(32)\n      CRYSTAL\n  end\n\n  it \"errors if passing integer literal to Tuple as generic argument (#1120)\" do\n    assert_error <<-CRYSTAL, \"argument to Tuple must be a type, not 32\"\n      Tuple(32)\n      CRYSTAL\n  end\n\n  it \"errors if passing integer literal to Union as generic argument\" do\n    assert_error <<-CRYSTAL, \"argument to Union must be a type, not 32\"\n      Union(32)\n      CRYSTAL\n  end\n\n  it \"disallow using a non-instantiated generic type as a generic type argument\" do\n    assert_error <<-CRYSTAL, \"use a more specific type\"\n      class Foo(T)\n      end\n\n      class Bar(T)\n      end\n\n      Bar(Foo)\n      CRYSTAL\n  end\n\n  it \"disallow using a non-instantiated module type as a generic type argument\" do\n    assert_error <<-CRYSTAL, \"use a more specific type\"\n      module Moo(T)\n      end\n\n      class Bar(T)\n      end\n\n      Bar(Moo)\n      CRYSTAL\n  end\n\n  it \"errors on too nested generic instance\" do\n    assert_error <<-CRYSTAL, \"generic type too nested\"\n      class Foo(T)\n      end\n\n      def foo\n        Foo(typeof(foo)).new\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors on too nested generic instance, with union type\" do\n    assert_error <<-CRYSTAL, \"generic type too nested\"\n      class Foo(T)\n      end\n\n      def foo\n        1 || Foo(typeof(foo)).new\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors on too nested tuple instance\" do\n    assert_error <<-CRYSTAL, \"tuple type too nested\"\n      def foo\n        {typeof(foo)}\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"gives helpful error message when generic type var is missing (#1526)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type parameter T for the generic class Foo(T). Please provide it explicitly\"\n      class Foo(T)\n        def initialize(x)\n        end\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"gives helpful error message when generic type var is missing in block spec (#1526)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type parameter T for the generic class Foo(T). Please provide it explicitly\"\n      class Foo(T)\n        def initialize(&block : T -> )\n          block\n        end\n      end\n\n      Foo.new { |x| }\n      CRYSTAL\n  end\n\n  it \"can define instance var forward declared (#962)\" do\n    assert_type(<<-CRYSTAL) { int64 }\n      class ClsA\n        @c : ClsB(Int32)\n\n        def initialize\n          @c = ClsB(Int32).new\n        end\n\n        def c\n          @c\n        end\n      end\n\n      class ClsB(T)\n        @pos = 0i64\n\n        def pos\n          @pos\n        end\n      end\n\n      foo = ClsA.new\n      foo.c.pos\n      CRYSTAL\n  end\n\n  it \"inherits instance var type annotation from generic to concrete\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo(T)\n        @x : Int32?\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"inherits instance var type annotation from generic to concrete with T\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo(T)\n        @x : T?\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"inherits instance var type annotation from generic to generic to concrete\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo(T)\n        @x : Int32?\n\n        def x\n          @x\n        end\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Baz < Bar(Int32)\n      end\n\n      Baz.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't duplicate overload on generic class with class method (#2385)\" do\n    error = assert_error <<-CRYSTAL\n      class Foo(T)\n        def self.foo(x : Int32)\n        end\n      end\n\n      Foo(String).foo(35.7)\n      CRYSTAL\n\n    error.to_s.lines.count(\" - Foo(T).foo(x : Int32)\").should eq(1)\n  end\n\n  # Given:\n  #\n  # ```\n  # class Parent; end\n  #\n  # class Child1 < Parent; end\n  #\n  # class Child2 < Parent; end\n  #\n  # $x : Array(Parent)\n  # $x = [] of Parent\n  # ```\n  #\n  # This must not be allowed:\n  #\n  # ```\n  # $x = [] of Child1\n  # ```\n  #\n  # Because if the type of $x is considered Array(Parent) by the compiler,\n  # this should be allowed:\n  #\n  # ```\n  # $x << Child2.new\n  # ```\n  #\n  # However, here we will be inserting a `Child2` inside a `Child1`,\n  # which is totally incorrect.\n  it \"doesn't allow union of generic class with module to be assigned to a generic class with module (#2425)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@value' of Bar must be PluginContainer(Plugin), not PluginContainer(Foo)\"\n      module Plugin\n      end\n\n      class PluginContainer(T)\n      end\n\n      class Foo\n        include Plugin\n      end\n\n      class Bar\n        @value : PluginContainer(Plugin)\n\n        def initialize(@value)\n        end\n      end\n\n      Bar.new(PluginContainer(Foo).new)\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from class method\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      class Foo(*T)\n        def self.t\n          T\n        end\n      end\n\n      Foo(Int32, Char).t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from instance method\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      class Foo(*T)\n        def t\n          T\n        end\n      end\n\n      Foo(Int32, Char).new.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from class method through superclass\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      class Foo(*T)\n        def self.t\n          T\n        end\n      end\n\n      class Bar(*T) < Foo(*T)\n      end\n\n      Bar(Int32, Char).t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from instance method through superclass\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      class Foo(*T)\n        def t\n          T\n        end\n      end\n\n      class Bar(*T) < Foo(*T)\n      end\n\n      Bar(Int32, Char).new.t\n      CRYSTAL\n  end\n\n  it \"splats generic type var\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, char.metaclass]) }\n      class Foo(X, Y)\n        def self.vars\n          {X, Y}\n        end\n      end\n\n      Foo(*{Int32, Char}).vars\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from instance method, more args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, float64]).metaclass, char.metaclass]) }\n      class Foo(*T, R)\n        def t\n          {T, R}\n        end\n      end\n\n      Foo(Int32, Float64, Char).new.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from instance method, more args (2)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([float64]).metaclass, char.metaclass]) }\n      class Foo(A, *T, R)\n        def t\n          {A, T, R}\n        end\n      end\n\n      Foo(Int32, Float64, Char).new.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic class, accesses T from instance method through superclass, more args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([string.metaclass, tuple_of([int32, char]).metaclass, float64.metaclass]) }\n      class Foo(A, *T, B)\n        def t\n          {A, T, B}\n        end\n      end\n\n      class Bar(*T) < Foo(String, *T, Float64)\n      end\n\n      Bar(Int32, Char).new.t\n      CRYSTAL\n  end\n\n  it \"virtual metaclass type implements super virtual metaclass type (#3007)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Base\n      end\n\n      class Child < Base\n      end\n\n      class Child1 < Child\n      end\n\n      class Gen(T)\n        class Entry(T)\n          def initialize(@x : T)\n          end\n\n          def foo\n            1\n          end\n        end\n\n        def foo(x)\n          Entry(T).new(x).foo\n        end\n      end\n\n      gen = Gen(Base.class).new\n      gen.foo(Child || Child1)\n      CRYSTAL\n  end\n\n  it \"can use virtual type for generic class\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      class Foo(T)\n        def foo\n          1\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        def foo\n          'a'\n        end\n      end\n\n      class Baz\n        def initialize(@foo : Foo(Int32))\n        end\n\n        def foo\n          @foo.foo\n        end\n      end\n\n      baz = Baz.new(Bar(Int32).new)\n      baz.foo\n      CRYSTAL\n  end\n\n  it \"recomputes on new subclass\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      class Foo(T)\n        def foo\n          1\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        def foo\n          1\n        end\n      end\n\n      class Qux(T) < Bar(T)\n        def foo\n          'a'\n        end\n      end\n\n      class Baz\n        def initialize(@foo : Foo(Int32))\n        end\n\n        def foo\n          @foo.foo\n        end\n      end\n\n      baz = Baz.new(Bar(Int32).new)\n      baz.foo\n\n      baz = Baz.new(Qux(Int32).new)\n      baz.foo\n      CRYSTAL\n  end\n\n  it \"types macro def with generic instance\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { string }\n      class Reference\n        def foo\n          {{ @type.name.stringify }}\n        end\n      end\n\n      class At\n      end\n\n      class Bt(T) < At\n      end\n\n      a = Pointer(At).malloc(1_u64)\n      a.value = Bt(Int32).new\n      a.value.foo\n      CRYSTAL\n  end\n\n  it \"unifies generic metaclass types\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Foo\", int32).metaclass.virtual_type! }\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      Foo(Int32) || Bar(Int32)\n      CRYSTAL\n  end\n\n  it \"doesn't crash when matching restriction against number literal (#3157)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Gen(3).new' to be T, not String\"\n      class Gen(T)\n        @value : String?\n\n        def initialize(@value : T)\n        end\n      end\n\n      Gen(3).new(\"a\")\n      CRYSTAL\n  end\n\n  it \"doesn't crash when matching restriction against number literal (2) (#3157)\" do\n    assert_error <<-CRYSTAL, \"expected type, not NumberLiteral\"\n      class Cls(T)\n        @a : T?\n      end\n\n      Cls(3).new\n      CRYSTAL\n  end\n\n  it \"replaces type parameters for virtual types (#3235)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Array\", generic_class(\"OutgoingPacket\", types[\"Client\"]).virtual_type! }\n      abstract class Packet(T)\n      end\n\n      abstract class OutgoingPacket(T) < Packet(T)\n      end\n\n      class Client\n      end\n\n      class Connection(T)\n        def initialize\n          @packets = Array(OutgoingPacket(T)).new\n        end\n\n        def packets\n          @packets\n        end\n      end\n\n      Connection(Client).new.packets\n      CRYSTAL\n  end\n\n  it \"nests generics with the same type var (#3297)\" do\n    assert_type(<<-CRYSTAL) { symbol }\n      class Foo(A)\n        @a : A\n\n        def initialize(@a : A)\n        end\n\n        def a\n          @a\n        end\n\n        class Bar(A) < Foo(A)\n        end\n      end\n\n      Foo::Bar.new(:a).a\n      CRYSTAL\n  end\n\n  it \"restricts virtual generic instance type against generic (#3351)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Gen(T)\n      end\n\n      class Sub < Gen(String)\n      end\n\n      def foo(x : Gen(String))\n        1\n      end\n\n      foo(Gen(String).new.as(Gen(String)))\n      CRYSTAL\n  end\n\n  it \"subclasses twice with same generic class (#3423)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", int32 }\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      Bar(Int32).new\n      CRYSTAL\n  end\n\n  it \"errors if invoking new on private new in generic type (#3485)\" do\n    assert_error <<-CRYSTAL, \"private method 'new' called\"\n      class Foo(T)\n        private def self.new\n          super\n        end\n      end\n\n      Foo(String).new\n      CRYSTAL\n  end\n\n  it \"never types Path as virtual outside generic type parameter (#3989)\" do\n    assert_type(<<-CRYSTAL) { types[\"Base\"].metaclass }\n      class Base\n      end\n\n      class Derived < Base\n        def initialize(x : Int32)\n        end\n      end\n\n      class Generic(T)\n        def initialize\n          T.new\n        end\n\n        def t\n          T\n        end\n      end\n\n      Generic(Base).new.t\n      CRYSTAL\n  end\n\n  it \"never types Generic as virtual outside generic type parameter (#3989)\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Base\", int32).metaclass }\n      class Base(T)\n      end\n\n      class Derived(T) < Base(T)\n        def initialize(x : Int32)\n        end\n      end\n\n      class Generic(T)\n        def initialize\n          T.new\n        end\n\n        def t\n          T\n        end\n      end\n\n      Generic(Base(Int32)).new.t\n      CRYSTAL\n  end\n\n  it \"doesn't find T type parameter of current type in superclass (#4604)\" do\n    assert_error <<-CRYSTAL, \"undefined constant \"\n      class X(T)\n        abstract class A(T); end\n\n        class B < A(T)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't find unbound type parameter in main code inside generic type (#6168)\" do\n    assert_error <<-CRYSTAL, \"undefined constant T\"\n      class Foo(T)\n        Foo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"can use type var that resolves to number in restriction (#6502)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", 1.int32 }\n      class Foo(N)\n        def foo : Foo(N)\n          self\n        end\n      end\n\n      f = Foo(1).new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"can use type var that resolves to number in restriction using Int128\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", 1.int128 }\n      class Foo(N)\n        def foo : Foo(N)\n          self\n        end\n      end\n\n      f = Foo(1_i128).new\n      f.foo\n      CRYSTAL\n  end\n\n  it \"doesn't consider unbound generic instantiations as concrete (#7200)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Moo\n      end\n\n      abstract class Foo(T)\n        include Moo\n\n        def call\n          T.as(Int32.class)\n        end\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class MooHolder\n        def initialize(@moo : Moo)\n        end\n      end\n\n      moo = MooHolder.new(Bar(Int32).new)\n      moo.@moo.call\n      CRYSTAL\n  end\n\n  it \"shows error due to generic instantiation (#7083)\" do\n    assert_error <<-CRYSTAL, \"method Gen(String)#valid? must return Bool but it is returning Nil\", inject_primitives: true\n      abstract class Base\n      end\n\n      class Gen(T) < Base\n        def valid? : Bool\n          # true\n        end\n      end\n\n      class Other < Base\n        def valid?\n          true\n        end\n      end\n\n      x = Pointer(Base).malloc(1)\n      x.value.valid?\n\n      Gen(String).new\n      CRYSTAL\n  end\n\n  it \"resolves T through metaclass inheritance (#7914)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Int32\n        def self.foo\n          1\n        end\n      end\n\n      class Matrix(T)\n        def self.foo\n          T.foo\n        end\n      end\n\n      class GeneralMatrix(T) < Matrix(T)\n      end\n\n      GeneralMatrix(Int32).foo\n      CRYSTAL\n  end\n\n  it \"errors if splatting a non-tuple (#9853)\" do\n    assert_error <<-CRYSTAL, \"argument to splat must be a tuple type, not Int32\"\n      Array(*Int32)\n      CRYSTAL\n  end\n\n  it \"correctly checks argument count when target type has a splat (#9855)\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"T\", int32, bool).metaclass }\n      class T(A, B, *C)\n      end\n\n      T(*{Int32, Bool})\n      CRYSTAL\n  end\n\n  it \"restricts generic type argument through alias in a non-strict way\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Gen\", int32 }\n      class Gen(T)\n      end\n\n      alias G = Gen(String | Int32)\n\n      def foo(x : G)\n        x\n      end\n\n      foo(Gen(Int32).new)\n      CRYSTAL\n  end\n\n  it \"replaces type parameters in virtual metaclasses (#10691)\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Foo\", generic_class(\"Parent\", int32).virtual_type.metaclass) }\n      class Parent(T)\n      end\n\n      class Child < Parent(Int32)\n      end\n\n      class Foo(T)\n      end\n\n      class Bar(T)\n        @foo = Foo(Parent(T).class).new\n      end\n\n      Bar(Int32).new.@foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/hooks_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: hooks\" do\n  it \"does inherited macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        macro inherited\n          def self.{{@type.name.downcase.id}}\n            1\n          end\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.bar\n      CRYSTAL\n  end\n\n  it \"does included macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n        macro included\n          def self.{{@type.name.downcase.id}}\n            1\n          end\n        end\n      end\n\n      class Bar\n        include Foo\n      end\n\n      Bar.bar\n      CRYSTAL\n  end\n\n  it \"does extended macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n        macro extended\n          def self.{{@type.name.downcase.id}}\n            1\n          end\n        end\n      end\n\n      class Bar\n        extend Foo\n      end\n\n      Bar.bar\n      CRYSTAL\n  end\n\n  it \"does added method macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        macro method_added(d)\n          def self.{{d.name.downcase.id}}\n            1\n          end\n        end\n\n        def foo; end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"does not invoke 'method_added' hook recursively\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        macro method_added(d)\n          def {{d.name.id}}\n            1\n          end\n        end\n\n        def foo\n          nil\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if wrong inherited params size\" do\n    assert_error <<-CRYSTAL, \"wrong number of parameters for macro 'inherited' (given 1, expected 0)\"\n      class Foo\n        macro inherited(x)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if wrong included params size\" do\n    assert_error <<-CRYSTAL, \"wrong number of parameters for macro 'included' (given 1, expected 0)\"\n      module Foo\n        macro included(x)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if wrong extended params size\" do\n    assert_error <<-CRYSTAL, \"wrong number of parameters for macro 'extended' (given 1, expected 0)\"\n      module Foo\n        macro extended(x)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"types initializer in inherited\" do\n    assert_type(<<-CRYSTAL) { string }\n      abstract class Foo\n        macro inherited\n          @@bar = new\n\n          def self.bar\n            @@bar\n          end\n        end\n      end\n\n      class Bar < Foo\n        def initialize(@name = \"foo\")\n        end\n\n        def name\n          @name\n        end\n      end\n\n      Bar.bar.name\n      CRYSTAL\n  end\n\n  it \"errors if wrong extended params length\" do\n    assert_error <<-CRYSTAL, \"wrong number of parameters for macro 'method_added' (given 0, expected 1)\"\n      class Foo\n        macro method_added\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"includes error message in included hook (#889)\" do\n    assert_error <<-CRYSTAL, \"undefined macro method 'MacroId#unknown'\"\n      module Doable\n        macro included\n          def {{@type.name.unknown}}\n            \"woo!\"\n          end\n        end\n      end\n\n      class BobWaa\n        include Doable\n      end\n      CRYSTAL\n  end\n\n  it \"does included macro for generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Mod(T)\n        macro included\n          def self.method\n            1\n          end\n        end\n      end\n\n      class Klass\n        include Mod(Nil)\n      end\n\n      Klass.method\n      CRYSTAL\n  end\n\n  it \"does inherited macro for generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        macro inherited\n          def self.method\n            1\n          end\n        end\n      end\n\n      class Klass < Foo(Int32)\n      end\n\n      Klass.method\n      CRYSTAL\n  end\n\n  it \"errors if wrong finished params length\" do\n    assert_error <<-CRYSTAL, \"wrong number of parameters for macro 'finished' (given 1, expected 0)\"\n      class Foo\n        macro finished(x)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"types macro finished hook bug regarding initialize (#3964)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([string, int32]) }\n      class A1\n        macro finished\n          @x : String\n          def initialize(@x)\n          end\n\n          def x; @x; end\n        end\n      end\n\n      class A2\n        macro finished\n          @y : Int32\n          def initialize(@y)\n          end\n\n          def y; @y; end\n        end\n      end\n\n      a1 = A1.new(\"x\")\n      a2 = A2.new(1)\n      {a1.x, a2.y}\n      CRYSTAL\n  end\n\n  it \"does inherited macro through generic instance type (#9693)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(X)\n        macro inherited\n          def self.{{@type.name.downcase.id}}\n            1\n          end\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      class Baz < Bar\n      end\n\n      Baz.baz\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/if_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: if\" do\n  it \"types an if without else\" do\n    assert_type(\"if 1 == 1; 1; end\", inject_primitives: true) { nilable int32 }\n  end\n\n  it \"types an if with else of same type\" do\n    assert_type(\"if 1 == 1; 1; else; 2; end\", inject_primitives: true) { int32 }\n  end\n\n  it \"types an if with else of different type\" do\n    assert_type(\"if 1 == 1; 1; else; 'a'; end\", inject_primitives: true) { union_of(int32, char) }\n  end\n\n  it \"types `if` with `&&` and assignment\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      struct Number\n        def abs\n          self\n        end\n      end\n\n      class Foo\n        def coco\n          @a = 1 || nil\n          if (b = @a) && 1 == 1\n            b.abs\n          end\n        end\n      end\n\n      Foo.new.coco\n      CRYSTAL\n  end\n\n  it \"can invoke method on var that is declared on the right hand side of an and\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      if 1 == 2 && (b = 1)\n        b + 1\n      end\n      CRYSTAL\n  end\n\n  it \"errors if requires inside if\" do\n    assert_error <<-CRYSTAL, \"can't require dynamically\"\n      if 1 == 2\n        require \"foo\"\n      end\n      CRYSTAL\n  end\n\n  it \"correctly filters type of variable if there's a raise with an interpolation that can't be typed\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      def bar\n        bar\n      end\n\n      def foo\n        a = 1 || nil\n        unless a\n          raise \"Oh no \\#{bar}\"\n        end\n        a + 2\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"passes bug (related to #1729)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      n = true ? 3 : 3.2\n      if n.is_a?(Float64)\n        n\n      end\n      n\n      CRYSTAL\n  end\n\n  it \"restricts the type of the right hand side of an || when using is_a? (#1728)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { bool }\n      n = 3 || \"foobar\"\n      n.is_a?(String) || (n + 1 == 2)\n      CRYSTAL\n  end\n\n  it \"restricts type with !var and ||\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of bool, int32 }\n      a = 1 == 1 ? 1 : nil\n      !a || a + 2\n      CRYSTAL\n  end\n\n  it \"restricts type with !var.is_a?(...) and ||\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of bool, int32 }\n      a = 1 == 1 ? 1 : nil\n      !a.is_a?(Int32) || a + 2\n      CRYSTAL\n  end\n\n  it \"restricts type with !var.is_a?(...) and &&\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of bool, int32 }\n      a = 1 == 1 ? 1 : \"\"\n      !a.is_a?(String) && a + 2\n      CRYSTAL\n  end\n\n  it \"restricts with || (#2464)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Int32\n        def foo\n          1\n        end\n      end\n\n      struct Char\n        def foo\n          1\n        end\n      end\n\n      a = 1 || \"\" || 'a'\n      if a.is_a?(Int32) || a.is_a?(Char)\n        a.foo\n      else\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't restrict with || on different vars\" do\n    assert_error <<-CRYSTAL, \"undefined method\"\n      struct Int32\n        def foo\n          1\n        end\n      end\n\n      struct Char\n        def bar\n          1\n        end\n      end\n\n      a = 1 || \"\" || 'a'\n      b = a\n      if a.is_a?(Int32) || b.is_a?(Char)\n        a.foo + b.bar\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't restrict with || on var and non-restricting condition\" do\n    assert_error <<-CRYSTAL, \"undefined method\"\n      struct Int32\n        def foo\n          1\n        end\n      end\n\n      a = 1 || \"\" || 'a'\n      if a.is_a?(Int32) || 1 == 2\n        a.foo\n      end\n      CRYSTAL\n  end\n\n  it \"restricts with || but doesn't unify types to base class\" do\n    assert_type(<<-CRYSTAL) { union_of(nil_type, int32, char) }\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      class Baz < Foo\n        def foo\n          'a'\n        end\n      end\n\n      a = Bar.new.as(Foo)\n      if a.is_a?(Bar) || a.is_a?(Baz)\n        a.foo\n      else\n        nil\n      end\n      CRYSTAL\n  end\n\n  it \"restricts with && always falsey\" do\n    assert_type(<<-CRYSTAL) { union_of(bool, int32) }\n      x = 1\n      if (x.is_a?(String) && x.is_a?(String)) && x.is_a?(String)\n        true\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't filter and recombine when variables don't change in if\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n\n      class Baz < Foo\n        include Moo\n      end\n\n      def foo(x : Foo)\n        1\n      end\n\n      x = Bar.new.as(Moo)\n      if x.is_a?(Bar) || x.is_a?(Baz)\n      end\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"restricts and doesn't unify union types\" do\n    assert_type(<<-CRYSTAL) { union_of(nil_type, int32) }\n      class Foo\n      end\n\n      module M\n        def m\n          1\n        end\n      end\n\n      class Bar < Foo\n        include M\n      end\n\n      class Baz < Foo\n        include M\n      end\n\n      a = Bar.new.as(Foo)\n      if b = a.as?(M)\n        b.m\n      else\n        nil\n      end\n      CRYSTAL\n  end\n\n  it \"types variable after unreachable else of && (#3360)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def test\n        foo = 1 if 1\n        return 1 unless foo && foo\n        foo\n      end\n\n      test\n      CRYSTAL\n  end\n\n  it \"restricts || else (1) (#3266)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) }\n      a = 1 || nil\n      b = 1 || nil\n      if !a || !b\n        {1, 2}\n      else\n        {a, b}\n      end\n      CRYSTAL\n  end\n\n  it \"restricts || else (2) (#3266)\" do\n    assert_type(<<-CRYSTAL) { union_of char, int32 }\n      a = 1 || nil\n      if !a || 1\n        'c'\n      else\n        a\n      end\n      CRYSTAL\n  end\n\n  it \"restricts || else (3) (#3266)\" do\n    assert_type(<<-CRYSTAL) { union_of char, int32 }\n      a = 1 || nil\n      if 1 || !a\n        'c'\n      else\n        a\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't restrict || else in sub && (right)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def foo\n        a = 1 || nil\n\n        if false || (!a && false)\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't restrict || else in sub && (left)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def foo\n        a = 1 || nil\n\n        if (!a && false) || false\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"restricts || else in sub || (right)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        a = 1 || nil\n\n        if false || (!a || false)\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"restricts || else in sub || (left)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        a = 1 || nil\n\n        if (!a || false) || false\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"restricts && else in sub && (right)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        a = 1 || nil\n\n        if !a && (!a && !a)\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"restricts && else in sub && (left)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        a = 1 || nil\n\n        if (!a && !a) && !a\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"restricts || of more than 2 clauses (#8864)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        a = 1 || 2.0 || 'c' || \"\"\n\n        if a.is_a?(Float64) || a.is_a?(Char) || a.is_a?(String)\n          return 1\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"restricts && of !var.is_a(...)\" do\n    assert_type(<<-CRYSTAL) { union_of int32, float64, bool }\n      def foo\n        a = 1 || 2.0 || 'c'\n\n        if !a.is_a?(Int32) && !a.is_a?(Float64)\n          return true\n        end\n\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't consider nil type in else branch with if with && (#7434)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        if true && (y = 1 || nil)\n        else\n          return 1\n        end\n        y\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't add Nil to var assigned in && condition with method call (#15739)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        if true && (x = 1) == 1\n          x\n        else\n          0\n        end\n      end\n      foo\n      CRYSTAL\n  end\n\n  it \"types var assigned in && condition correctly when used after (#15739)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def bar : Int32\n        1\n      end\n\n      def foo\n        if true && (x = bar) > 0\n          x\n        else\n          0\n        end\n      end\n      foo\n      CRYSTAL\n  end\n\n  it \"allows chained comparisons of 4+ expressions (#16361)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { bool }\n      def a; 1; end\n      def b; 2; end\n      def c; 3; end\n      def d; 4; end\n\n      a < b < c < d\n      CRYSTAL\n  end\n\n  it \"includes pointer types in falsey branch\" do\n    assert_type(<<-CRYSTAL) { nilable union_of bool, pointer_of(int32), int32 }\n      def foo\n        x = 1\n        y = false || pointerof(x) || nil\n\n        if !y\n          return y\n        end\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on new variables inside typeof condition\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      def foo\n        if typeof(x = 1)\n          \"\"\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on nested conditionals inside typeof condition\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      def foo\n        if typeof(1 || 'a')\n          \"\"\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on Expressions condition (1)\" do\n    assert_type(<<-CRYSTAL) { nilable int32.metaclass }\n      def foo\n        if (v = 1; true)\n          typeof(v)\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on Expressions condition (2)\" do\n    assert_type(<<-CRYSTAL) { nilable nil_type.metaclass }\n      def foo\n        if (v = nil; true)\n          typeof(v)\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/initialize_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: initialize\" do\n  it \"types instance vars as nilable if doesn't invoke super in initialize\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@baz' of Foo, with Bar < Foo, rendering it nilable\"\n      class Foo\n        def initialize\n          @baz = Baz.new\n          @another = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @another = 2\n        end\n      end\n\n      class Baz\n      end\n\n      foo = Foo.new\n      bar = Bar.new\n      CRYSTAL\n  end\n\n  it \"types instance vars as nilable if doesn't invoke super in initialize with deep subclass\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@baz' of Foo, with BarBar < Foo, rendering it nilable\"\n      class Foo\n        def initialize\n          @baz = Baz.new\n          @another = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          super\n        end\n      end\n\n      class BarBar < Bar\n        def initialize\n          @another = 2\n        end\n      end\n\n      class Baz\n      end\n\n      foo = Foo.new\n      bar = Bar.new\n      CRYSTAL\n  end\n\n  it \"types instance vars as nilable if doesn't invoke super with default arguments\" do\n    node = parse(<<-CRYSTAL)\n      class Foo\n        def initialize\n          @baz = Baz.new\n          @another = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x = 1)\n          super()\n        end\n      end\n\n      class Baz\n      end\n\n      foo = Foo.new\n      bar = Bar.new(1)\n      CRYSTAL\n    result = semantic node\n    mod = result.program\n    foo = mod.types[\"Foo\"].as(NonGenericClassType)\n    foo.instance_vars[\"@baz\"].type.should eq(mod.types[\"Baz\"])\n    foo.instance_vars[\"@another\"].type.should eq(mod.int32)\n  end\n\n  it \"checks instance vars of included modules\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be (Char | Nil), not Int32\"\n      module Lala\n        def lala\n          @x = 'a'\n        end\n      end\n\n      class Foo\n        include Lala\n      end\n\n      class Bar < Foo\n        include Lala\n\n        def initialize\n          @x = 1\n        end\n      end\n\n      b = Bar.new\n      f = Foo.new\n      f.lala\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if not always assigned\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32, not Nil\", inject_primitives: true\n      class Foo\n        def initialize\n          if 1 == 2\n            @x = 1\n          end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if assigned in block\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@x' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\", inject_primitives: true\n      def bar\n        yield if 1 == 2\n      end\n\n      class Foo\n        def initialize\n          bar do\n            @x = 1\n          end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as not-nilable if assigned in block but previously assigned\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def bar\n        yield if 1 == 2\n      end\n\n      class Foo\n        def initialize\n          @x = 1\n          bar do\n            @x = 2\n          end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if used before assignment\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@x' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          x = @x\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as non-nilable if calls super and super defines it\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Parent\n        def initialize\n          @x = 1\n        end\n      end\n\n      class Foo < Parent\n        def initialize\n          super\n          @x + 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as non-nilable if calls super and super defines it, with one level of indirection\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Parent\n        def initialize\n          @x = 1\n        end\n      end\n\n      class SubParent < Parent\n      end\n\n      class Foo < SubParent\n        def initialize\n          super\n          @x + 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if out\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibC\n        fun foo(x : Int32*)\n      end\n\n      class Foo\n        def initialize\n          LibC.foo(out @x)\n          @x + 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if used after method call that reads var\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@x' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def foo\n          @x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if used after method call that reads var (2)\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@x' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Bar\n        def bar\n        end\n      end\n\n      class Foo\n        def initialize\n          my_method\n          @x = Bar.new\n        end\n\n        def my_method\n          @x.bar\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if used after global method call\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n      end\n\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if used after method call inside typeof\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          typeof(foo)\n          @x = 1\n        end\n\n        def foo\n          @x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if used after method call that doesn't read var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def foo\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if used after method call that reads var through other calls\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@x' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def foo\n          bar\n        end\n\n        def bar\n          x = 1 || 1.5\n          baz(x)\n        end\n\n        def baz(x : Int32)\n          @x\n        end\n\n        def baz(x : Float64)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if used after method call that assigns var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def foo\n          @x = 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"finishes when analyzing recursive calls\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def foo\n          bar\n        end\n\n        def bar\n          foo\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if not used in method call\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @y = 2\n          foo\n          @x = 1\n        end\n\n        def foo\n          @y\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"types instance var as nilable if used in first of two method calls\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@x' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          foo\n          @x = 1\n        end\n\n        def foo\n          @x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"doesn't type instance var as nilable if assigned before method call\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = 1\n          foo\n          @x = 1\n        end\n\n        def foo\n          @x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new\n      foo.x\n      CRYSTAL\n  end\n\n  it \"marks instance variable as nilable in initialize if using self in method\" do\n    assert_error <<-CRYSTAL, \"'self' was used before initializing instance variable '@foo', rendering it nilable\"\n      class Foo\n        def initialize\n          do_something\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n\n        def do_something\n          Other.new(self)\n        end\n      end\n\n      class Other\n        def initialize(foo)\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"marks instance variable as nilable in initialize if using self\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def initialize\n          Other.new(self)\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      class Other\n        def initialize(foo)\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"marks instance variable as nilable in initialize if assigning self\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def initialize\n          a = self\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      class Other\n        def initialize(foo)\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"marks instance variable as nilable when using self in super\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Parent\n        def initialize(foo)\n        end\n      end\n\n      class Foo < Parent\n        def initialize\n          super(self)\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if found matches for initialize but doesn't cover all (bug #204)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo.new' to be Int32, not (Int32 | Nil)\", inject_primitives: true\n      class Foo\n        def initialize(x : Int32)\n        end\n      end\n\n      a = 1 > 0 ? nil : 1\n      Foo.new(a)\n      CRYSTAL\n  end\n\n  it \"doesn't mark instance variable as nilable when using self.class\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize\n          self.class.foo\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n\n        def self.foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"doesn't mark instance variable as nilable when using self.class in method\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize\n          bar\n          @foo = 1\n        end\n\n        def bar\n          self.class.foo\n        end\n\n        def foo\n          @foo\n        end\n\n        def self.foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"types initializer of recursive generic type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo(T)\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      alias Rec = Foo(Rec)\n\n      Foo(Rec).new.x + 1\n      CRYSTAL\n  end\n\n  it \"types initializer of generic type after instantiated\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo(T)\n      end\n\n      alias Alias = Foo(Int32)\n\n      class Foo(T)\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x + 1\n      CRYSTAL\n  end\n\n  it \"errors on default new when using named arguments (#2245)\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'x'\"\n      class Foo\n      end\n\n      Foo.new(x: 1)\n      CRYSTAL\n  end\n\n  it \"doesn't type ivar as nilable if super call present and parent has already typed ivar (#4764)\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo\n        def initialize(@a = 1)\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          super\n        end\n        def initialize(@a)\n        end\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"doesn't type ivar having initializer as nilable even if it is used before assigned inside initialize (#5112)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x = 42\n\n        def initialize\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/instance_var_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: instance var\" do\n  it \"declares instance var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          x = 1\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var multiple times, last one wins\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      class Foo\n        @x : Int32\n        @x : Int32 | Float64\n\n        def initialize\n          x = 1\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't error when redeclaring subclass variable with the same type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          x = 1\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        @x : Int32\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"errors when redeclaring subclass variable with a different type\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo, with Bar < Foo, is already declared as Int32\"\n      class Foo\n        @x : Int32\n\n        def initialize\n          x = 1\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        @x : String\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var in module, inherits to type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x : Int32\n\n        def initialize\n          x = 1\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var in module, inherits to type recursively\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x : Int32\n\n        def initialize\n          x = 1\n          @x = x\n        end\n\n        def x\n          @x\n        end\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      class Foo\n        include Moo2\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @x : T\n\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(1).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with no type parameter\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @x : Int32\n\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Char).new(1).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with generic type\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Gen\", int32 }\n      class Gen(T)\n      end\n\n      class Foo(T)\n        @x : Gen(T)\n\n        def initialize(@x : Gen(T))\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(Gen(Int32).new).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with union\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      class Foo(T)\n        @x : T | Char\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new(1).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with proc\" do\n    assert_type(<<-CRYSTAL) { proc_of([char, char, int32]) }\n      class Foo(T)\n        @x : T, T -> Int32\n\n        def initialize(@x : T, T -> Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Char).new(->(x : Char, y : Char) { 1 }).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with tuple\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, int32, char]) }\n      class Foo(T)\n        @x : {T, Int32, T}\n\n        def initialize(@x : {T, Int32, T})\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Char).new({'a', 1, 'b'}).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with metaclass\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n        @x : T.class\n\n        def initialize(@x : T.class)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new(Int32).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with virtual metaclass\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"].virtual_type!.metaclass }\n      class Bar; end\n      class Baz < Bar; end\n\n      class Foo(T)\n        @x : T.class\n\n        def initialize(@x : T.class)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Bar).new(Bar).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with static array\" do\n    assert_type(<<-CRYSTAL) { static_array_of(uint8, 3) }\n      class Foo(T)\n        @x : UInt8[T]\n\n        def initialize(@x : UInt8[T])\n        end\n\n        def x\n          @x\n        end\n      end\n\n      z = uninitialized UInt8[3]\n      Foo(3).new(z).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with splat\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Gen\", int32, string }\n      class Gen(*T)\n      end\n\n      class Foo(*T)\n        @x : Gen(*T)\n\n        def initialize(@x : Gen(*T))\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(Gen(Int32, String).new).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with splat inside Tuple\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, string]) }\n      class Foo(*T)\n        @x : Tuple(*T)\n\n        def initialize(@x : Tuple(*T))\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new({1, \"\"}).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type, with splat inside Proc\" do\n    assert_type(<<-CRYSTAL) { proc_of([int32, string, bool]) }\n      class Foo(R, *T)\n        @x : Proc(*T, R)\n\n        def initialize(@x : Proc(*T, R))\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(->(x : Int32, y : String) { true }).x\n      CRYSTAL\n  end\n\n  it \"declares instance var with self, on generic\" do\n    assert_type(<<-CRYSTAL) { nilable generic_class(\"Foo\", int32) }\n      class Foo(T)\n        @x : self | Nil\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"errors if declaring variable with number\" do\n    assert_error <<-CRYSTAL, \"can't declare variable with NumberLiteral\"\n      class Foo(T)\n        @x : T\n\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(3).new(3).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type through module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x : Int32\n\n        def initialize\n          a = 1\n          @x = a\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo(T)\n        include Moo\n      end\n\n      Foo(Float64).new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic type subclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @x : T\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      Bar(Int32).new(1).x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(T)\n        @x : T\n\n        def x\n          @x\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n\n        def initialize\n          a = 1\n          @x = a\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic module (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(U)\n        @x : U\n\n        def x\n          @x\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n\n        def initialize\n          a = 1\n          @x = a\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var of generic module from non-generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x : Int32\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      module Moo2(T)\n        include Moo\n      end\n\n      class Foo(T)\n        include Moo2(T)\n\n        def initialize\n          super(1)\n          a = 1\n          @x = a\n        end\n      end\n\n      Foo(Float64).new.x\n      CRYSTAL\n  end\n\n  it \"infers type from number literal\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from char literal\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo\n        def initialize\n          @x = 'a'\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from bool literal\" do\n    assert_type(<<-CRYSTAL) { bool }\n      class Foo\n        def initialize\n          @x = true\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from string literal\" do\n    assert_type(<<-CRYSTAL) { string }\n      class Foo\n        def initialize\n          @x = \"hi\"\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from string interpolation\" do\n    assert_type(<<-CRYSTAL) { string }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = \"foo\\#{1}\"\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from symbol literal\" do\n    assert_type(<<-CRYSTAL) { symbol }\n      class Foo\n        def initialize\n          @x = :hi\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from array literal with of\" do\n    assert_type(<<-CRYSTAL) { array_of int32 }\n      class Foo\n        def initialize\n          @x = [] of Int32\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from array literal with of metaclass\" do\n    assert_type(<<-CRYSTAL) { array_of int32.metaclass }\n      class Foo\n        def initialize\n          @x = [] of Int32.class\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from array literal from its literals\" do\n    assert_type(<<-CRYSTAL) { array_of union_of(int32, char) }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = [1, 'a']\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from hash literal with of\" do\n    assert_type(<<-CRYSTAL) { hash_of int32, string }\n      class Foo\n        def initialize\n          @x = {} of Int32 => String\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from hash literal from elements\" do\n    assert_type(<<-CRYSTAL) { hash_of(union_of(int32, char), union_of(string, bool)) }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = {1 => \"foo\", 'a' => true}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from range literal\" do\n    assert_type(<<-CRYSTAL) { range_of(int32, char) }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = 1..'a'\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from regex literal\" do\n    assert_type(<<-CRYSTAL) { types[\"Regex\"] }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = /foo/\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from regex literal with interpolation\" do\n    assert_type(<<-CRYSTAL) { types[\"Regex\"] }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = /foo\\#{1}/\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from tuple literal\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, string]) }\n      class Foo\n        def initialize\n          @x = {1, \"foo\"}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from named tuple literal\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": string}) }\n      class Foo\n        def initialize\n          @x = {x: 1, y: \"foo\"}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from proc literal with return type\" do\n    assert_type(<<-CRYSTAL) { proc_of([int32, bool, string]) }\n      class Foo\n        def initialize\n          @x = ->(x : Int32, y : Bool) : String { \"\" }\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from new expression\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n      end\n\n      class Foo\n        def initialize\n          @x = Bar.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from as\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize\n          @x = (1 + 2).as(Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from as?\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      class Foo\n        def initialize\n          @x = (1 + 2).as?(Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from argument restriction\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def x=(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from argument default value\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def set(@x = 1)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from lib fun call\" do\n    assert_type(<<-CRYSTAL) { types[\"LibFoo\"].types[\"Bar\"] }\n      lib LibFoo\n        struct Bar\n          x : Int32\n        end\n\n        fun foo : Bar\n      end\n\n      class Foo\n        def initialize\n          @x = LibFoo.foo\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from lib variable\" do\n    assert_type(<<-CRYSTAL) { types[\"LibFoo\"].types[\"Bar\"] }\n      lib LibFoo\n        struct Bar\n          x : Int32\n        end\n\n        $foo : Bar\n      end\n\n      class Foo\n        def initialize\n          @x = LibFoo.foo\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from ||\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, bool) }\n      class Foo\n        def initialize\n          @x = 1 || true\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from &&\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, bool) }\n      class Foo\n        def initialize\n          @x = 1 && true\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from ||=\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def x\n          @x ||= 1\n        end\n      end\n\n      Foo.new.@x\n      CRYSTAL\n  end\n\n  it \"infers type from ||= inside another assignment\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def x\n          x = @x ||= 1\n        end\n      end\n\n      Foo.new.@x\n      CRYSTAL\n  end\n\n  it \"infers type from if\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, bool) }\n      class Foo\n        def initialize\n          @x = 1 == 1 ? 1 : true\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from case\" do\n    assert_type(<<-CRYSTAL) { union_of(char, bool) }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = case 1\n               when 2\n                 'a'\n               else\n                 true\n               end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from unless\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, bool) }\n      class Foo\n        def initialize\n          @x = unless 1 == 1\n                 1\n               else\n                 true\n               end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from begin\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = begin\n            'a'\n            1\n          end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from assign (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = @y = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from assign (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = @y = 1\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo.new.y\n      CRYSTAL\n  end\n\n  it \"infers type from block argument\" do\n    assert_type(<<-CRYSTAL) { nilable proc_of(int32, int32) }\n      class Foo\n        def set(&@x : Int32 -> Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from block argument without restriction\" do\n    assert_type(<<-CRYSTAL) { nilable proc_of(void) }\n      class Foo\n        def set(&@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from !\" do\n    assert_type(<<-CRYSTAL) { bool }\n      class Foo\n        def initialize\n          @x = !1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from is_a?\" do\n    assert_type(<<-CRYSTAL) { bool }\n      class Foo\n        def initialize\n          @x = 1.is_a?(Char)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from responds_to?\" do\n    assert_type(<<-CRYSTAL) { bool }\n      class Foo\n        def initialize\n          @x = 1.responds_to?(:foo)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from sizeof\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = sizeof(Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from instance_sizeof\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = instance_sizeof(Foo)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from offsetof\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = offsetof(Bar, @x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      struct Bar\n        @x = 0\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from path that is a type\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"].virtual_type!.metaclass }\n      class Bar; end\n      class Baz < Bar; end\n\n      class Foo\n        def initialize\n          @x = Bar\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from path that is a constant\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      CONST = 1\n\n      class Foo\n        def initialize\n          @x = CONST\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer type from redefined method\" do\n    assert_type(<<-CRYSTAL) { nilable char }\n      class Foo\n        def foo\n          @x = 1\n        end\n\n        def foo\n          @x = 'a'\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from redefined method if calls previous_def\" do\n    assert_type(<<-CRYSTAL) { union_of(nil_type, int32, char) }\n      class Foo\n        def foo\n          @x = 1\n        end\n\n        def foo\n          previous_def\n          @x = 'a'\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type in multi assign\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      class Foo\n        def initialize\n          @x, @y = 1, 'a'\n        end\n\n        def x\n          @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      {Foo.new.x, Foo.new.y}\n      CRYSTAL\n  end\n\n  it \"infers type from enum member\" do\n    assert_type(<<-CRYSTAL) { types[\"Color\"] }\n      enum Color\n        Red\n        Green\n        Blue\n      end\n\n      class Foo\n        def initialize\n          @x = Color::Red\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from two literals\" do\n    assert_type(<<-CRYSTAL) { union_of int32, float64 }\n      class Foo\n        def initialize\n          @x = 1\n          @x = 1.5\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from literal outside def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from literal outside def with initialize and type restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n        @x = 1\n\n        def initialize\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from lib out (1)\" do\n    assert_type(<<-CRYSTAL) { types[\"LibFoo\"].types[\"Bar\"] }\n      lib LibFoo\n        struct Bar\n          x : Int32\n        end\n\n        fun foo(x : Int32, y : Bar*) : Int32\n      end\n\n      class Foo\n        def initialize\n          LibFoo.foo(1, out @two)\n        end\n\n        def two\n          @two\n        end\n      end\n\n      Foo.new.two\n      CRYSTAL\n  end\n\n  it \"infers type from lib out (2)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      lib LibFoo\n        fun foo(x : Int32, y : Float64*) : Int32\n      end\n\n      class Foo\n        def initialize\n          @err = LibFoo.foo(1, out @two)\n        end\n\n        def two\n          @two\n        end\n      end\n\n      Foo.new.two\n      CRYSTAL\n  end\n\n  it \"infers type from lib out (3)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        fun foo(x : Int32, y : Float64*) : Int32\n      end\n\n      class Foo\n        def initialize\n          @err = LibFoo.foo(1, out @two)\n        end\n\n        def err\n          @err\n        end\n      end\n\n      Foo.new.err\n      CRYSTAL\n  end\n\n  it \"infers type from uninitialized\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = uninitialized Int32\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer for subclass if assigns another type (1)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32, not Float64\"\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          @x = 1.5\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"doesn't infer for subclass if assigns another type (2)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32, not Float64\"\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          @x = 1.5\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"infers type from included module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from included module, outside def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from included module recursively\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      class Foo\n        include Moo2\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with literal\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Float64).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with T.new\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n      end\n\n      class Foo(T)\n        def initialize\n          @x = T.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Bar).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with T.new and literal\" do\n    assert_type(<<-CRYSTAL) { union_of types[\"Bar\"], int32 }\n      class Bar\n      end\n\n      class Foo(T)\n        def initialize\n          @x = T.new\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Bar).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with lib call\" do\n    assert_type(<<-CRYSTAL) { types[\"LibFoo\"].types[\"Bar\"] }\n      lib LibFoo\n        struct Bar\n          x : Int32\n        end\n\n        fun foo : Bar\n      end\n\n      class Foo(T)\n        def initialize\n          @x = LibFoo.foo\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Float64).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with &&\" do\n    assert_type(<<-CRYSTAL) { union_of(types[\"Foo\"], types[\"Bar\"]) }\n      class Foo\n      end\n\n      class Bar\n      end\n\n      class Gen(T)\n        def initialize\n          @x = T.new || Foo.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Gen(Bar).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with begin\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n      end\n\n      class Gen(T)\n        def initialize\n          @x = begin\n            1\n            T.new\n          end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Gen(Foo).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with if\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(types[\"Foo\"], types[\"Bar\"]) }\n      class Foo\n      end\n\n      class Bar\n      end\n\n      class Gen(T)\n        def initialize\n          @x = 1 == 2 ? T.new : Foo.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Gen(Bar).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with case\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(types[\"Foo\"], types[\"Bar\"]) }\n      class Object\n        def ===(other)\n          self == other\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar\n      end\n\n      class Gen(T)\n        def initialize\n          @x = case 1\n               when 2 then T.new\n               else Foo.new\n               end\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Gen(Bar).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with assign (1)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n      end\n\n      class Gen(T)\n        def initialize\n          @x = @y = T.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Gen(Foo).new.x\n      CRYSTAL\n  end\n\n  it \"infers type for generic class, with assign (2)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n      end\n\n      class Gen(T)\n        def initialize\n          @x = @y = T.new\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Gen(Foo).new.y\n      CRYSTAL\n  end\n\n  it \"infers type for non-generic class, with assign\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n        @y : Int32\n\n        def initialize\n          @x = @y = 1\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo.new.y\n      CRYSTAL\n  end\n\n  it \"infers type for generic module\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n      end\n\n      module Moo(T)\n        def initialize\n          @x = T.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Gen(T)\n        include Moo(T)\n      end\n\n      Gen(Foo).new.x\n      CRYSTAL\n  end\n\n  it \"infers type to be nilable if not initialized\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def x\n          @x = 1\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type to be non-nilable if initialized in all initialize\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"errors if not initialized in all initialize\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't explicitly initialize instance variable '@x' of Foo, rendering it nilable\"\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def initialize(x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializes in all initialize because declared as nilable\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        @x : Int32?\n\n        def initialize\n          @x = 1\n        end\n\n        def initialize(x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from argument with restriction, in generic\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(1).x\n      CRYSTAL\n  end\n\n  it \"says undefined instance variable on read\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Foo\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"says undefined instance variable on assign\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Foo\n        def x\n          a = 1\n          @x = a\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"errors if declaring instance var and turns out to be nilable\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class Foo\n        @x : Int32\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't if declaring nilable instance var and turns out to be nilable\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        @x : Int32?\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"errors if declaring instance var and turns out to be nilable, in generic type\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo(T) was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class Foo(T)\n        @x : T\n      end\n      CRYSTAL\n  end\n\n  it \"errors if declaring instance var and turns out to be nilable, in generic module type\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      module Moo(T)\n        @x : T\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if declaring instance var and doesn't out to be nilable, in generic module type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(T)\n        @x : T\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n\n        def initialize(@x)\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.x\n      CRYSTAL\n  end\n\n  it \"errors if declaring instance var and turns out to be nilable, in generic module type in generic type\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo(T) was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      module Moo(T)\n        @x : T\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling super\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n          super()\n        end\n      end\n\n      Bar.new(10).x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling previous_def (#3210)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Some\n        def initialize\n          @a = 1\n        end\n\n        def initialize\n          previous_def\n        end\n\n        def a\n          @a\n        end\n      end\n\n      Some.new.a\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling super and previous_def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n          super()\n        end\n\n        def initialize(x)\n          previous_def(x)\n        end\n      end\n\n      Bar.new(10).x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling previous_def (2) (#3210)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Some\n        def initialize\n          @a = 1\n          @b = 2\n        end\n\n        def initialize\n          previous_def\n          @b = @a\n        end\n\n        def a\n          @a\n        end\n\n        def b\n          @b\n        end\n      end\n\n      Some.new.a + Some.new.b\n      CRYSTAL\n  end\n\n  it \"errors if not initializing super variables\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@x' of Foo, with Bar < Foo, rendering it nilable\"\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if not initializing super variables (2)\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@x' of Foo, with Bar < Foo, rendering it nilable\"\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @y = 2\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if not initializing super variables (3)\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@x' of Foo, with Bar < Foo, rendering it nilable\"\n      class Foo\n        def initialize\n          @x = 1\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @y = 2\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if not initializing super variable in generic\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@x' of Foo(T), with Bar(T) < Foo(T), rendering it nilable\"\n      class Foo(T)\n        def initialize\n          @x = 1\n        end\n      end\n\n      class Bar(T) < Foo(T)\n        def initialize\n          @y = 2\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error if not calling super but initializing all variables\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n          @x = 2\n        end\n      end\n\n      Bar.new(10).x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling super in parent parent\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Bar\n        def initialize(x)\n          super()\n        end\n      end\n\n      Baz.new(10).x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling super for module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def initialize(x)\n          super()\n        end\n      end\n\n      Foo.new(10).x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing variables but calling super for generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(T)\n        @x : T\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n\n        def initialize(x)\n          super(x)\n        end\n      end\n\n      Foo.new(10).x\n      CRYSTAL\n  end\n\n  it \"ignores redefined initialize (#456)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize\n          @a = 1\n        end\n\n        def initialize\n          @a = 1\n          @b = 2\n        end\n\n        def a\n          @a\n        end\n\n        def b\n          @b\n        end\n      end\n\n      a = Foo.new\n      a.a + a.b\n      CRYSTAL\n  end\n\n  it \"ignores super module initialize (#456)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n        def initialize\n          @a = 1\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def initialize\n          @a = 1\n          @b = 2\n        end\n\n        def a\n          @a\n        end\n\n        def b\n          @b\n        end\n      end\n\n      a = Foo.new\n      a.a + a.b\n      CRYSTAL\n  end\n\n  it \"obeys super module initialize (#456)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n        def initialize\n          @a = 1\n        end\n\n        def a\n          @a\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def initialize\n          @b = 2\n          super\n        end\n\n        def b\n          @b\n        end\n      end\n\n      b = Foo.new\n      b.a + b.b\n      CRYSTAL\n  end\n\n  it \"doesn't error if initializing var in superclass, and then empty initialize\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't error if calling initialize from another initialize (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def initialize\n          initialize(1)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't error if calling initialize from another initialize (2)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def initialize(@x : Int32)\n          @y = nil\n        end\n\n        def initialize\n          initialize(1)\n          @y = 2\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo.new.y\n      CRYSTAL\n  end\n\n  it \"infers nilable instance var of generic type\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo(T)\n        def set\n          @coco = 2\n        end\n\n        def coco\n          @coco\n        end\n      end\n\n      f = Foo(Int32).new\n      f.coco\n      CRYSTAL\n  end\n\n  it \"infers nilable instance var of generic module\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      module Moo(T)\n        def set\n          @coco = 2\n        end\n\n        def coco\n          @coco\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n\n      f = Foo(Int32).new\n      f.coco\n      CRYSTAL\n  end\n\n  it \"infers type to be nilable if self is used before assigning to a variable\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def initialize\n          self\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type to be nilable if self is used in same assign\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def foo(x)\n      end\n\n      class Foo\n        def initialize\n          @x = 1 || foo(self)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer type to be nilable if using self.class\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize\n          self.class\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  pending \"doesn't infer type to be nilable if using self.class in call in assign\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x)\n      end\n\n      class Foo\n        def initialize\n          @x = 1 || foo(self.class)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initializing nilable var in subclass\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        @x : Int32?\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"considers var as assigned in multi-assign\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def some\n        {1, 2}\n      end\n\n      class Foo\n        @x : Int32\n        @y : Int32\n\n        def initialize\n          @x, @y = some\n        end\n\n        def x\n          @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      foo = Foo.new\n      foo.x + foo.y\n      CRYSTAL\n  end\n\n  it \"infers from another instance var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = 1\n          @y = @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo.new.y\n      CRYSTAL\n  end\n\n  it \"infers from another instance var with type declaration\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize(@x)\n          @y = @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo.new(1).y\n      CRYSTAL\n  end\n\n  it \"infers from another instance var in generic type\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n      end\n\n      class Foo(T)\n        def initialize\n          @x = T.new\n          @y = @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo(Bar).new.y\n      CRYSTAL\n  end\n\n  it \"infers from another instance var in generic type with type declaration\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n      end\n\n      class Foo(T)\n        @x : T\n\n        def initialize(@x)\n          @y = @x\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo(Bar).new(Bar.new).y\n      CRYSTAL\n  end\n\n  it \"errors on undefined instance var and subclass calling super\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Bar\"\n      class Foo\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n          super\n          @x = x\n        end\n      end\n\n      point = Bar.new(1)\n      Foo.new(1).x\n      CRYSTAL\n  end\n\n  it \"infers type from array literal in generic type\" do\n    assert_type(<<-CRYSTAL) { array_of(int32) }\n      class Foo(T)\n        def initialize\n          @array = [] of T\n        end\n\n        def array\n          @array\n        end\n      end\n\n      Foo(Int32).new.array\n      CRYSTAL\n  end\n\n  it \"infers type from hash literal in generic type\" do\n    assert_type(<<-CRYSTAL) { hash_of(int32, float64) }\n      class Foo(T)\n        def initialize\n          @array = {} of T => Float64\n        end\n\n        def array\n          @array\n        end\n      end\n\n      Foo(Int32).new.array\n      CRYSTAL\n  end\n\n  it \"infers type from array literal with literals in generic type\" do\n    assert_type(<<-CRYSTAL) { array_of(int32) }\n      require \"prelude\"\n\n      class Foo(T)\n        def initialize\n          @array = [0]\n        end\n\n        def array\n          @array\n        end\n      end\n\n      Foo(Float64).new.array\n      CRYSTAL\n  end\n\n  it \"infers type from hash literal with literals in generic type\" do\n    assert_type(<<-CRYSTAL) { hash_of(int32, symbol) }\n      require \"prelude\"\n\n      class Foo(T)\n        def initialize\n          @hash = {0 => :foo}\n        end\n\n        def hash\n          @hash\n        end\n      end\n\n      Foo(Float64).new.hash\n      CRYSTAL\n  end\n\n  it \"infers from restriction using virtual type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      class Foo; end\n      class Bar < Foo; end\n\n      class Baz\n        def initialize(@x : Foo)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Baz.new(Foo.new).x\n      CRYSTAL\n  end\n\n  it \"doesn't duplicate instance var in subclass\" do\n    result = semantic(<<-CRYSTAL)\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        @x : Int32\n      end\n      CRYSTAL\n\n    foo = result.program.types[\"Foo\"].as(NonGenericClassType)\n    foo.instance_vars[\"@x\"].type.should eq(result.program.int32)\n\n    bar = result.program.types[\"Bar\"].as(NonGenericClassType)\n    bar.instance_vars.should be_empty\n  end\n\n  it \"infers type from custom array literal\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize\n        end\n\n        def <<(v)\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = Foo{1, 2, 3}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from custom generic array literal\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(T)\n        def initialize\n        end\n\n        def <<(v)\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = Foo{1, 2, 3}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from custom hash literal\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize\n        end\n\n        def []=(k, v)\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = Foo{1 => 2}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from custom generic hash literal\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32, string }\n      class Foo(K, V)\n        def initialize\n        end\n\n        def []=(k, v)\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = Foo{1 => \"foo\"}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from custom array literal in generic\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize\n        end\n\n        def <<(v)\n        end\n      end\n\n      class Bar(T)\n        def initialize\n          @x = Foo{1, 2, 3}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"infers type from custom hash literal in generic\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize\n        end\n\n        def []=(k, v)\n        end\n      end\n\n      class Bar(T)\n        def initialize\n          @x = Foo{1 => 2}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"says can't infer type if only nil was assigned\" do\n    assert_error <<-CRYSTAL, \"instance variable @x of Foo was inferred to be Nil, but Nil alone provides no information\"\n      class Foo\n        def initialize\n          @x = nil\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"says can't infer type if only nil was assigned, in generic type\" do\n    assert_error <<-CRYSTAL, \"instance variable @x of Foo(T) was inferred to be Nil, but Nil alone provides no information\"\n      class Foo(T)\n        def initialize\n          @x = nil\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"allows nil instance var because it's a generic type\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(nil).x\n      CRYSTAL\n  end\n\n  it \"uses virtual types in fun\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Node\"].virtual_type, types[\"Node\"].virtual_type) }\n      class Node; end\n      class SubNode < Node; end\n\n      class Foo\n        def initialize(@x : Node -> Node)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(->(x : Node) { x }).x\n      CRYSTAL\n  end\n\n  it \"uses virtual types in union\" do\n    assert_type(<<-CRYSTAL) { union_of(types[\"Node\"].virtual_type, int32) }\n      class Node; end\n      class SubNode < Node; end\n\n      class Foo\n        def initialize(@x : Node | Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(1).x\n      CRYSTAL\n  end\n\n  it \"uses virtual types in self\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Node\"].virtual_type }\n      class Node\n        def initialize\n          @x = nil\n        end\n\n        def initialize(@x : self)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class SubNode < Node; end\n\n      Node.new.x\n      CRYSTAL\n  end\n\n  it \"infers from Pointer.malloc\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { pointer_of(int32) }\n      class Foo\n        def initialize\n          @x = Pointer(Int32).malloc(1_u64)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from Pointer.malloc with two arguments\" do\n    assert_type(<<-CRYSTAL) { pointer_of(uint8) }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = Pointer.malloc(10, 1_u8)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from Pointer.null\" do\n    assert_type(<<-CRYSTAL) { pointer_of(int32) }\n      require \"prelude\"\n\n      class Foo\n        def initialize\n          @x = Pointer(Int32).null\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from Pointer.malloc in generic type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { pointer_of(int32) }\n      class Foo(T)\n        def initialize\n          @x = Pointer(T).malloc(1_u64)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"infers from Pointer.null in generic type\" do\n    assert_type(<<-CRYSTAL) { pointer_of(int32) }\n      require \"prelude\"\n\n      class Foo(T)\n        def initialize\n          @x = Pointer(T).null\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"infers from Pointer.malloc with two arguments in generic type\" do\n    assert_type(<<-CRYSTAL) { pointer_of(uint8) }\n      require \"prelude\"\n\n      class Foo(T)\n        def initialize\n          @x = Pointer.malloc(10, 1_u8)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer generic type without type argument inside generic\" do\n    assert_error <<-CRYSTAL, \"can't infer the type parameter T for the generic class Bar(T)\"\n      class Bar(T)\n      end\n\n      class Foo(T)\n        def initialize\n          @bar = Bar.new\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"doesn't crash on missing var on subclass, with superclass not specifying a type\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@x', rendering it nilable\"\n      class Foo\n        def initialize(@x)\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n        end\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"doesn't complain if not initialized in one initialize, but has initializer (#2465)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x = 1\n\n        def initialize(@x)\n        end\n\n        def initialize\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can declare type even if included module has a guessed var\" do\n    assert_type(<<-CRYSTAL) { union_of int32, float64 }\n      module Moo\n        def foo\n          @x = 1\n        end\n      end\n\n      class Foo\n        include Moo\n\n        @x : Int32 | Float64\n\n        def initialize\n          @x = 1.5\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't complain if declared type is recursive alias that's nilable\" do\n    assert_type(<<-CRYSTAL) { types[\"Rec\"] }\n      class Bar(T)\n      end\n\n      alias Rec = Int32 | Nil | Bar(Rec)\n\n      class Foo\n        @x : Rec\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from assign to local var (#2467)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from assign to local var in generic type (#2467)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize\n          @x = x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Float64).new.x\n      CRYSTAL\n  end\n\n  it \"infers from top-level method that has type annotation\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n      end\n\n      def bar : Bar\n        Bar.new\n      end\n\n      class Foo\n        def initialize\n          @bar = ::bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"infers from simple top-level method without type annotation\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n      end\n\n      def bar\n        Bar.new\n      end\n\n      class Foo\n        def initialize\n          @bar = ::bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"infers from class method that has type annotation\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n        def self.bar : Bar\n          Bar.new\n        end\n      end\n\n      class Foo\n        def initialize\n          @bar = Bar.bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"infers from class method that has type annotation, in generic class\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n        def self.bar : Bar\n          Bar.new\n        end\n      end\n\n      class Foo(T)\n        def initialize\n          @bar = Bar.bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"infers from generic class method that has type annotation\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", int32 }\n      class Bar(T)\n        def self.bar : self\n          Bar(T).new\n        end\n      end\n\n      class Foo\n        def initialize\n          @bar = Bar(Int32).bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"infers from generic class method that has type annotation, without instantiating\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Bar(T)\n        def self.bar : Int32\n          1\n        end\n      end\n\n      class Foo\n        def initialize\n          @bar = Bar.bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"infers from class method that has type annotation, with overload\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Baz\n      end\n\n      class Bar\n        def self.bar : Baz\n          Baz.new\n        end\n\n        def self.bar(x) : Bar\n          Bar.new\n        end\n\n        def self.bar(x) : Baz\n          yield\n          Bar.new\n        end\n      end\n\n      class Foo\n        def initialize\n          @bar = Bar.bar(1)\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"infers from class method that has type annotation, with multiple overloads matching, all with the same type\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n        def self.bar(x : Int32) : Bar\n          Bar.new\n        end\n\n        def self.bar(x : String) : Bar\n          Bar.new\n        end\n      end\n\n      class Foo\n        def initialize(x)\n          @bar = Bar.bar(x)\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new(1).bar\n      CRYSTAL\n  end\n\n  it \"infers from multiple class method overloads with same type but different spellings\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n        def self.bar(x : Int32) : Bar\n          Bar.new\n        end\n\n        def self.bar(x : Float64) : ::Bar\n          Bar.new\n        end\n\n        def self.bar(x : String) : self\n          Bar.new\n        end\n      end\n\n      class Foo\n        def initialize(x)\n          @bar = Bar.bar(x)\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      Foo.new(1).bar\n      CRYSTAL\n  end\n\n  it \"infers from new with return type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def self.new : Int32\n          1\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = Foo.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"infers from new with return type in generic type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def self.new : Int32\n          1\n        end\n      end\n\n      class Bar(T)\n        def initialize\n          @x = Foo.new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar(Float64).new.x\n      CRYSTAL\n  end\n\n  it \"infers from new with return type returning generic\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", int32 }\n      class Foo(T)\n        def self.new : Bar(T)\n          Bar(T).new\n        end\n      end\n\n      class Bar(T)\n      end\n\n      class Baz\n        def initialize\n          @x = Foo(Int32).new\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Baz.new.x\n      CRYSTAL\n  end\n\n  it \"guesses from new on abstract class\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      abstract class Foo\n        def self.new : Bar\n          Bar.new(1)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n        end\n      end\n\n      class Baz\n        def initialize\n          @foo = Foo.new\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Baz.new.foo\n      CRYSTAL\n  end\n\n  it \"errors on undefined constant\" do\n    assert_error <<-CRYSTAL, \"undefined constant Bar\"\n      class Foo\n        def initialize\n          @x = Bar.new\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"infers from class method that invokes new\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo\n        def initialize\n          @x = Bar.create\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.create\n          new\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from class method that has number literal\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = Bar.default_num\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.default_num\n          1\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from class method that refers to constant\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo\n        def initialize\n          @x = Bar.default_instance\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        DEFAULT = new\n\n        def self.default_instance\n          DEFAULT\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infer from class method with multiple statements and return\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      class Foo\n        def initialize\n          @x = Bar.default\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.default\n          if 1 == 2\n            return nil\n          end\n          1\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer from class method with multiple statements and return, on non-easy return\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\", inject_primitives: true\n      class Foo\n        def initialize\n          @x = Bar.default\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.default\n          if 1 == 2\n            a = 1\n            return a\n          end\n          1\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer from class method with multiple statements and return, on non-easy return (2)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\", inject_primitives: true\n      class Foo\n        def initialize\n          @x = Bar.default\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.default\n          if 1 == 2\n            a = 1\n            return a\n          else\n            1\n          end\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infer from class method where new is redefined\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = Bar.default\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.default\n          new\n        end\n\n        def self.new\n          1\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't crash on recursive method call\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Foo\n        def initialize\n          @x = Bar.default\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.default\n          Bar.default2\n        end\n\n        def self.default2\n          Bar.default\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers in multiple assign for tuple type (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x, @y = Bar.method\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        def self.method : {Int32, Bool}\n          {1, true}\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"says can't infer (#2536)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@foo' of Foo(Int32)\"\n      require \"prelude\"\n\n      class Foo(T)\n        def initialize(@arg : T)\n          @foo = [bar]\n        end\n\n        def initialize(@arg : T)\n          @foo = [bar]\n          yield 3\n        end\n\n        def bar\n          3\n        end\n      end\n\n      Foo.new(3).foo\n      CRYSTAL\n  end\n\n  it \"doesn't crash when inferring from new without matches (#2538)\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Foo.new'\"\n      class Foo\n        @@default = Foo.new\n\n        def initialize(@attr)\n        end\n      end\n\n      Foo.new(\"aaaa\")\n      CRYSTAL\n  end\n\n  it \"infers from method on integer literal, with type annotation\" do\n    assert_type(<<-CRYSTAL) { char }\n      struct Int32\n        def foo : Char\n          'a'\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = 1.foo\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers from method in generic type, with type annotation\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Gen\", int32 }\n      class Gen(T)\n        def foo : Gen(T)\n          self\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = Gen(Int32).new.foo\n        end\n      end\n\n      Foo.new.@x\n      CRYSTAL\n  end\n\n  it \"infers type by removing nil from || left side\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Int32\n        def foo : Int32?\n          1\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = 1.foo || 2\n        end\n      end\n\n      Foo.new.@x\n      CRYSTAL\n  end\n\n  it \"infers type from all call matches\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      class Base\n        def foo : Int32\n          1\n        end\n      end\n\n      class Sub1 < Base\n        def foo : Char\n          'a'\n        end\n      end\n\n      class Sub2 < Base\n        def foo\n          1 + 2\n        end\n      end\n\n      class Foo\n        def initialize(base : Base)\n          @x = base.foo\n        end\n      end\n\n      Foo.new(Base.new).@x\n      CRYSTAL\n  end\n\n  it \"guesses inside macro if\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      {% if true %}\n        class Foo\n          def initialize\n            @x = 1\n          end\n\n          def x\n            @x\n          end\n        end\n      {% end %}\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"guesses inside macro expression\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      {{ \"class Foo; def initialize; @x = 1; end; def x; @x; end; end\".id }}\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"guesses inside macro for\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      {% for name in %w(Foo) %}\n        class {{name.id}}\n          def initialize\n            @x = 1\n          end\n\n          def x\n            @x\n          end\n        end\n      {% end %}\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can't infer type from initializer\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Foo\n        @x = 1 + 2\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can't infer type from initializer in non-generic module\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Moo\"\n      module Moo\n        @x = 1 + 2\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can't infer type from initializer in generic module type\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Moo(T)\"\n      module Moo(T)\n        @x = 1 + 2\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can't infer type from initializer in generic class type\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo(T)\"\n      class Foo(T)\n        @x = 1 + 2\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"infers type from self (#2575)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize\n          @x = self\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"infers type from self as virtual type (#2575)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      class Foo\n        def initialize\n          @x = self\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares as named tuple\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": char}) }\n      class Foo\n        @x : NamedTuple(x: Int32, y: Char)\n\n        def initialize\n          a = {x: 1, y: 'a'}\n          @x = a\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't complain in second part of #2575\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @a : Int32\n\n        def initialize\n          @a = 5\n        end\n\n        def initialize(b)\n          initialize\n        end\n\n        def a\n          @a\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.a\n      CRYSTAL\n  end\n\n  it \"guesses from as.(typeof(...))\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize(x : Int32)\n          a = 1\n          @x = a.as(typeof(x))\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(1).x\n      CRYSTAL\n  end\n\n  it \"guesses from as.(typeof(...)) in generic type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def initialize(x : Int32)\n          a = 1\n          @x = a.as(typeof(x))\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Float64).new(1).x\n      CRYSTAL\n  end\n\n  it \"errors if can't find lib call, before erroring on instance var (#2579)\" do\n    assert_error <<-CRYSTAL, \"undefined fun 'nope' for LibFoo\"\n      lib LibFoo\n      end\n\n      class Foo\n        def initialize\n          LibFoo.nope(out @foo)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"errors when using Class (#2605)\" do\n    assert_error <<-CRYSTAL, \"can't use Class as the type of instance variable '@class' of Foo, use a more specific type\"\n      class Foo\n        def initialize(@class : Class)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors when using Class in generic type\" do\n    assert_error <<-CRYSTAL, \"can't use Class as the type of instance variable '@class' of Foo(T), use a more specific type\"\n      class Foo(T)\n        def initialize(@class : Class)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't error when using Class but specifying type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].metaclass }\n      class Foo\n        @x : Foo.class\n\n        def initialize(@x : Class)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(Foo).x\n      CRYSTAL\n  end\n\n  it \"doesn't error when using generic because guessed elsewhere\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", int32 }\n      class Foo\n        @x = Bar(Int32).new\n\n        def initialize\n        end\n\n        def x\n          @x = Bar.new(1)\n          @x\n        end\n      end\n\n      class Bar(T)\n        def initialize\n        end\n\n        def initialize(x : T)\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't error when using generic in generic type because guessed elsewhere\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", int32 }\n      class Foo(T)\n        @x = Bar(Int32).new\n\n        def initialize\n        end\n\n        def x\n          @x = Bar.new(1)\n          @x\n        end\n      end\n\n      class Bar(T)\n        def initialize\n        end\n\n        def initialize(x : T)\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  %w(Object Reference Class).each do |type|\n    it \"errors if declaring var in #{type}\" do\n      assert_error <<-CRYSTAL, \"can't declare instance variables in #{type}\"\n        class #{type}\n          @x : Int32?\n        end\n        CRYSTAL\n    end\n  end\n\n  [\n    \"Value\", \"Number\", \"Int\", \"Float\", \"Int32\",\n    \"Tuple(*T)\", \"NamedTuple(T)\", \"Enum\",\n    \"Pointer(T)\", \"StaticArray(T, N)\",\n    \"Proc(*T, R)\", \"Union(*T)\",\n  ].each do |type|\n    it \"errors if declaring var in #{type}\" do\n      assert_error <<-CRYSTAL, \"can't declare instance variables in #{type}\"\n        struct #{type}\n          @x : Int32?\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"errors if declaring instance variable in module included in Object\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Object\"\n      module Moo\n        @x : Int32?\n      end\n\n      class Object\n        include Moo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if adds instance variable to Object via guess\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Object\"\n      class Object\n        def foo(@foo : Int32)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if adds instance variable to Object via guess via included module\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Object\"\n      module Moo\n        def foo(@foo : Int32)\n        end\n      end\n\n      class Object\n        include Moo\n      end\n      CRYSTAL\n  end\n\n  it \"gives correct error when trying to use Int as an instance variable type\" do\n    assert_error <<-CRYSTAL, \"can't use Int as the type of an instance variable yet, use a more specific type\"\n      class Foo\n        @x : Int\n      end\n      CRYSTAL\n  end\n\n  it \"shouldn't error when accessing instance var in initialized that's always initialized (#2953)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @baz = Baz.new\n\n        def baz\n          @baz\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @baz.x = 2\n        end\n      end\n\n      class Baz\n        def initialize\n          @x = 1\n        end\n\n        def x=(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Bar.new.baz.x\n      CRYSTAL\n  end\n\n  # -----------------\n  # ||| OLD SPECS |||\n  # vvv           vvv\n\n  it \"declares instance var which appears in initialize\" do\n    result = assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n\n    mod = result.program\n\n    foo = mod.types[\"Foo\"].as(NonGenericClassType)\n    foo.instance_vars[\"@x\"].type.should eq(mod.int32)\n  end\n\n  it \"declares instance var of generic class\" do\n    assert_type(<<-CRYSTAL\n      class Foo(T)\n        @x : T\n\n        def initialize(@x)\n        end\n      end\n\n      Foo(Int32).new(1)\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"].as(GenericClassType)\n      foo_i32 = foo.instantiate([int32] of TypeVar)\n      foo_i32.lookup_instance_var(\"@x\").type.should eq(int32)\n      foo_i32\n    end\n  end\n\n  it \"declares instance var of generic class after reopen\" do\n    assert_type(<<-CRYSTAL\n      class Foo(T)\n      end\n\n      f = Foo(Int32).new(1)\n\n      class Foo(T)\n        @x : T\n\n        def initialize(@x : T)\n        end\n      end\n\n      f\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"].as(GenericClassType)\n      foo_i32 = foo.instantiate([int32] of TypeVar)\n      foo_i32.lookup_instance_var(\"@x\").type.should eq(int32)\n      foo_i32\n    end\n  end\n\n  it \"declares instance var with initial value\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x = 0\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares instance var with initial value, with subclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x = 0\n\n        def x\n          @x\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @x = 1\n          @z = 1\n        end\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"errors if declaring generic type without type vars\" do\n    assert_error <<-CRYSTAL, \"can't declare variable of generic non-instantiated type Foo\"\n      class Foo(T)\n      end\n\n      class Baz\n        @x : Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors when typing an instance variable inside a method\" do\n    assert_error <<-CRYSTAL, \"declaring the type of an instance variable must be done at the class level\"\n      def foo\n        @x : Int32\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"declares instance var with union type with a virtual member\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Parent\"].virtual_type! }\n      class Parent; end\n      class Child < Parent; end\n\n      class Foo\n        @x : Parent?\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"declares with `self`\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        @foo : self\n\n        def initialize\n          @foo = uninitialized self\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"guesses from array literal with of, with subclass\" do\n    assert_type(<<-CRYSTAL) { array_of(generic_class(\"Foo\", int32).virtual_type!) }\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      class Some\n        @some = [] of Foo(Int32)\n\n        def some\n          @some\n        end\n      end\n\n      Some.new.some\n      CRYSTAL\n  end\n\n  it \"guesses from hash literal with of, with subclass\" do\n    assert_type(<<-CRYSTAL) { hash_of(generic_class(\"Foo\", int32).virtual_type!, generic_class(\"Foo\", int32).virtual_type!) }\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      class Some\n        @some = {} of Foo(Int32) => Foo(Int32)\n\n        def some\n          @some\n        end\n      end\n\n      Some.new.some\n      CRYSTAL\n  end\n\n  it \"guesses from splat (#3149)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Args\", int32, char }\n      class Args(*T)\n        def initialize(*@args : *T)\n        end\n      end\n\n      Args.new(1, 'a')\n      CRYSTAL\n  end\n\n  it \"guesses from splat (2) (#3149)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      class Args(*T)\n        def initialize(*@args : *T)\n        end\n\n        def args\n          @args\n        end\n      end\n\n      Args.new(1, 'a').args\n      CRYSTAL\n  end\n\n  it \"transfers initializer from generic module to class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(T)\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"transfers initializer from module to generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Foo(T)\n        include Moo\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"doesn't consider self.initialize as initializer (#3239)\" do\n    assert_error <<-CRYSTAL, \"@instance_vars are not yet allowed in metaclasses: use @@class_vars instead\"\n      class Foo\n        def self.initialize\n          @d = 5\n        end\n\n        def test\n          @d\n        end\n      end\n\n      Foo.new.test\n      CRYSTAL\n  end\n\n  it \"doesn't crash on #3580\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method\"\n      class Hoge\n        @hoge_dir : String = \"~/.hoge\" ? \"~/.hoge\" : default_hoge_dir\n      end\n      CRYSTAL\n  end\n\n  it \"is more permissive with macro def initialize\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        @x : Int32\n\n        def initialize\n          {% for ivar in @type.instance_vars %}\n            @{{ivar}} = 0\n          {% end %}\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"is more permissive with macro def initialize, bug with named args\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo was not initialized\"\n      class Foo\n        @x : Int32\n\n        def initialize(**args)\n          {% @type %}\n        end\n      end\n\n      Foo.new(x: 1)\n      CRYSTAL\n  end\n\n  it \"is more permissive with macro def initialize, other initialize\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        @x : Int32\n        @y : Int32\n\n        def initialize\n          {% for ivar in @type.instance_vars %}\n            @{{ivar}} = 0\n          {% end %}\n        end\n\n        def initialize(@x, @y)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"is more permissive with macro def initialize, multiple\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize\n          {% begin %}\n            {% @type %}\n            @x = 1\n          {% end %}\n        end\n\n        def initialize(x)\n          {% begin %}\n            {% @type %}\n            @x = x\n          {% end %}\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new\n      Foo.new(1).x\n      CRYSTAL\n  end\n\n  it \"errors with macro def but another def doesn't initialize all\" do\n    assert_error <<-CRYSTAL, \"instance variable '@y' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class Foo\n        @x : Int32\n        @y : Int32\n\n        def initialize\n          {% for ivar in @type.instance_vars %}\n            @{{ivar}} = 0\n          {% end %}\n        end\n\n        def initialize(@x)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if finally not initialized in macro def\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo was not initialized in this 'initialize', rendering it nilable\"\n      class Foo\n        @x : Int32\n\n        def initialize\n          {% for ivar in @type.instance_vars %}\n          {% end %}\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't error if initializes via super in macro def\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n          super\n          {% for ivar in @type.instance_vars %}\n          {% end %}\n        end\n      end\n\n      Bar.new(1)\n      CRYSTAL\n  end\n\n  it \"doesn't error if uses typeof(@var)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      struct Int32\n        def self.zero\n          0\n        end\n      end\n\n      class Foo\n        @x : Int32\n\n        def initialize\n          @x = typeof(@x).zero\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't error if not initialized in macro def but outside it\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        @x = 1\n\n        def initialize\n          {% @type %}\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't error if inheriting generic instance (#3635)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      module Core(T)\n        @a : Bool\n      end\n\n      class Base(T)\n        include Core(Int32)\n\n        @a = true\n      end\n\n      class Foo < Base(String)\n        def a\n          @a\n        end\n      end\n\n      Foo.new.a\n      CRYSTAL\n  end\n\n  it \"doesn't consider var as nilable if conditionally assigned inside initialize, but has initializer (#3669)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        @x = 0\n\n        def initialize\n          @x = 1 if 1 == 2\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"types generic instance as virtual type if generic type has subclasses (#3805)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Qux\"] }\n      class Foo(T)\n      end\n\n      class Bar(T) < Foo(T)\n      end\n\n      class Qux\n        def initialize\n          @ptr = Pointer(Foo(Int32)).malloc(1_u64)\n        end\n\n        def ptr=(ptr)\n          @ptr = ptr\n        end\n      end\n\n      Bar(Int32).new\n      Qux.new\n      CRYSTAL\n  end\n\n  it \"errors if unknown ivar through macro (#4050)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@bar' of Foo\"\n      class Foo\n        def initialize(**attributes)\n          {% for var in @type.instance_vars %}\n            if arg = attributes[:{{var.name.id}}]?\n              @{{var.name.id}} = arg\n            end\n          {% end %}\n        end\n      end\n\n      class Bar < Foo\n        def initialize(**attributes)\n          @bar = true\n          super\n        end\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"can't infer type when using operation on const (#4054)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@baz' of Foo\", inject_primitives: true\n      class Foo\n        BAR = 5\n\n        def initialize\n          @baz = BAR + 5\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"instance variables initializers are used in class variables initialized objects (#3988)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n       class Foo\n         @@foo = Foo.new\n\n        @never_nil = 1\n\n        def initialize\n          if false\n            @never_nil = 2\n          end\n        end\n      end\n\n      Foo.new.@never_nil\n      CRYSTAL\n  end\n\n  it \"allow usage of instance variable initializer from instance variable initializer\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) }\n      class Foo\n        @bar = Bar.new\n        @never_nil = 1\n\n        def initialize\n          if false\n            @never_nil = 2\n          end\n        end\n      end\n\n      class Bar\n        @never_nil = 1\n\n        def initialize\n          if false\n            @never_nil = 2\n          end\n        end\n      end\n\n      {Foo.new.@never_nil, Bar.new.@never_nil}\n      CRYSTAL\n  end\n\n  it \"resolves unqualified constants in block inside instance var initializer (#14827)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      def with_block\n        yield\n      end\n\n      module A\n        struct B\n        end\n\n        struct C\n          @data : B = with_block { B.new }\n        end\n      end\n\n      A::C.new.is_a?(A::C)\n      CRYSTAL\n  end\n\n  it \"errors when assigning instance variable at top level block\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables at the top level\"\n      def foo\n        yield\n      end\n\n      foo do\n        @foo = 1\n      end\n      CRYSTAL\n  end\n\n  it \"errors when assigning instance variable at top level control block\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables at the top level\"\n      if true\n        @foo = 1\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't check call of non-self instance (#4830)\" do\n    assert_type(<<-CRYSTAL) { types[\"Container\"] }\n      class Container\n        def initialize(other : Container, x)\n          initialize(other)\n          @foo = \"x\"\n        end\n\n        def initialize(other : Container)\n          @foo = other.foo\n        end\n\n        def initialize(@foo : String, bar)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      container = Container.new(\"foo\", nil)\n      Container.new(container, \"foo2\")\n      CRYSTAL\n  end\n\n  it \"errors when assigning instance variable inside nested expression\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables at the top level\"\n      class Foo\n        if true\n          @foo = 1\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't find T in generic type that's not the current type (#4460)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Gen(T)\n        def self.new\n          Gen(T).new\n        end\n      end\n\n      class Foo\n        @x = Gen.new\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't consider instance var as nilable if assigned before self access (#4981)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def f(x)\n      end\n\n      class A\n        def initialize\n          @a = 0\n          f(self)\n          @a = 0\n        end\n\n        def a\n          @a\n        end\n      end\n\n      A.new.a\n      CRYSTAL\n  end\n\n  it \"doesn't combine union of Number and Number subclass (#5073)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Gen\", union_of(int32, types[\"A\"]) }\n      class Gen(T)\n      end\n\n      struct A < Number\n        def hash(hasher)\n          hasher\n        end\n\n        def to_s(io : IO)\n        end\n      end\n\n      class Foo\n        @foo = Gen(Int32 | A).new\n      end\n\n      Foo.new.@foo\n      CRYSTAL\n  end\n\n  it \"uses T.new (#4291)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n      end\n\n      class Gen(T)\n        @x = T.new\n\n        def x\n          @x\n        end\n      end\n\n      Gen(Foo).new.x\n      CRYSTAL\n  end\n\n  it \"can type ivar from module included by generic class (#5281)\" do\n    assert_type(<<-CRYSTAL) { types[\"Baz\"] }\n      module Foo\n        def initialize(@x = \"foo\")\n        end\n      end\n\n      class Bar(T)\n        include Foo\n\n        @y = 42\n      end\n\n      class Baz < Bar(String); end\n\n      Baz.new\n      CRYSTAL\n  end\n\n  it \"can type ivar from class inherited by generic class (#5281)\" do\n    assert_type(<<-CRYSTAL) { types[\"Baz\"] }\n      class Foo\n        def initialize(@x = \"foo\")\n        end\n      end\n\n      class Bar(T) < Foo\n        @y = 42\n      end\n\n      class Baz < Bar(String); end\n\n      Baz.new\n      CRYSTAL\n  end\n\n  it \"can guess the type from splat argument with splatted type\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32]) }\n      class Foo\n        def initialize(*@foo : *{Int32})\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new(1).foo\n      CRYSTAL\n  end\n\n  it \"can guess the type from splat argument with splatted type variable\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) }\n      class Foo(T)\n        def initialize(*@foo : *T)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new(1, 2).foo\n      CRYSTAL\n  end\n\n  it \"cannot guess the type from splat argument with not splatted type\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@foo' of Foo\"\n      class Foo\n        def initialize(*@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new(1).foo\n      CRYSTAL\n  end\n\n  it \"can guess the type from double-splat argument with double-splatted type\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"foo\": int32}) }\n      class Foo\n        def initialize(**@foo : **{foo: Int32})\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new(foo: 1).foo\n      CRYSTAL\n  end\n\n  it \"can guess the type from double-splat argument with double-splatted type variable\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"foo\": int32, \"bar\": int32}) }\n      class Foo(T)\n        def initialize(**@foo : **T)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new(foo: 1, bar: 2).foo\n      CRYSTAL\n  end\n\n  it \"cannot guess the type from double-splat argument with not double-splatted type\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@foo' of Foo\"\n      class Foo\n        def initialize(**@foo : Int32)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new(foo: 1).foo\n      CRYSTAL\n  end\n\n  it \"cannot guess type from argument assigned in body\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Foo\n        def initialize(x : String)\n          x = 1\n          @x = x\n        end\n      end\n\n      Foo.new \"foo\"\n      CRYSTAL\n  end\n\n  it \"can't infer type of generic method that returns self (#5383)\" do\n    assert_error <<-CRYSTAL, \"method Gen(T).new must return Gen(T) but it is returning Nil\"\n      class Gen(T)\n        def self.new(&block : -> T) : self\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = Gen.new { 1 }\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"guesses virtual array type (1) (#5342)\" do\n    assert_type(<<-CRYSTAL) { array_of(array_of(int32).virtual_type).virtual_type }\n      require \"prelude\"\n\n      class First(T) < Array(T)\n      end\n\n      class Second\n        @ary = [[1]]\n\n        def ary\n          @ary\n        end\n      end\n\n      Second.new.ary\n      CRYSTAL\n  end\n\n  it \"guesses virtual array type (2) (#5342)\" do\n    assert_type(<<-CRYSTAL) { array_of(array_of(int32).virtual_type).virtual_type }\n      require \"prelude\"\n\n      class First(T) < Array(T)\n      end\n\n      class Second\n        @ary = Array { Array { 1 } }\n\n        def ary\n          @ary\n        end\n      end\n\n      Second.new.ary\n      CRYSTAL\n  end\n\n  it \"guesses virtual array type (3) (#5342)\" do\n    assert_type(<<-CRYSTAL) { array_of(array_of(int32).virtual_type).virtual_type }\n      require \"prelude\"\n\n      class First(T) < Array(T)\n      end\n\n      class Second\n        @ary = [] of Array(Int32)\n\n        def ary\n          @ary\n        end\n      end\n\n      Second.new.ary\n      CRYSTAL\n  end\n\n  it \"guesses virtual hash type (1) (#5342)\" do\n    assert_type(<<-CRYSTAL) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type }\n      require \"prelude\"\n\n      class First(K, V) < Hash(K, V)\n      end\n\n      class Second\n        @hash = { {1 => 2} => 3}\n\n        def hash\n          @hash\n        end\n      end\n\n      Second.new.hash\n      CRYSTAL\n  end\n\n  it \"guesses virtual hash type (2) (#5342)\" do\n    assert_type(<<-CRYSTAL) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type }\n      require \"prelude\"\n\n      class First(K, V) < Hash(K, V)\n      end\n\n      class Second\n        @hash = Hash { Hash { 1 => 2 } => 3 }\n\n        def hash\n          @hash\n        end\n      end\n\n      Second.new.hash\n      CRYSTAL\n  end\n\n  it \"guesses virtual array type (3) (#5342)\" do\n    assert_type(<<-CRYSTAL) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type }\n      require \"prelude\"\n\n      class First(K, V) < Hash(K, V)\n      end\n\n      class Second\n        @hash = {} of Hash(Int32, Int32) => Int32\n\n        def hash\n          @hash\n        end\n      end\n\n      Second.new.hash\n      CRYSTAL\n  end\n\n  it \"doesn't solve instance var initializer in instance context (1) (#5876)\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'bar'\"\n      class Foo\n        @x : Int32 = bar\n\n        def bar\n          1\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't solve instance var initializer in instance context (2) (#5876)\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'bar'\"\n      class Foo(T)\n        @x : T = bar\n\n        def bar\n          1\n        end\n      end\n\n      Foo(Int32).new\n      CRYSTAL\n  end\n\n  it \"doesn't solve instance var initializer in instance context (3) (#5876)\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'bar'\"\n      module Moo(T)\n        @x : T = bar\n\n        def bar\n          1\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"solves instance var initializer in metaclass context (#5876)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32 = bar\n\n        def self.bar\n          1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"doesn't infer unbound generic type on non-generic call (#6390)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type parameter T for the generic class Gen(T)\"\n      class Gen(T)\n        def self.new(&block)\n          Gen(T).build\n        end\n\n        def self.build : self\n        end\n      end\n\n      class Foo\n        def initialize\n          @x = Gen.new { }\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't infer unbound generic type on generic method called from generic's subclass\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Gen(T)\n        def self.new(x : T)\n          Gen(T).build\n        end\n\n        def self.build : self\n          new\n        end\n      end\n\n      class Foo < Gen(Int32)\n        def initialize\n          @x = Gen.new('a')\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't infer unbound generic type on generic method called from generic's subclass, metaclass context\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@x' of Foo\"\n      class Gen(T)\n        def self.new(x : T)\n          Gen(T).build\n        end\n\n        def self.build : self\n          new\n        end\n      end\n\n      class Foo < Gen(Int32)\n        @x = Gen.new('a')\n      end\n      CRYSTAL\n  end\n\n  it \"errors when overriding inherited instance variable with incompatible type\" do\n    assert_error <<-CRYSTAL, \"instance variable '@a' of A must be Int32, not (Char | Int32)\"\n      class A\n        @a = 1\n      end\n\n      class B < A\n        @a = 'a'\n      end\n      CRYSTAL\n  end\n\n  it \"accepts overriding inherited instance variable with compatible type\" do\n    semantic <<-CRYSTAL\n      class A\n        @a = 1\n      end\n\n      class B < A\n        @a = 2\n      end\n      CRYSTAL\n  end\n\n  it \"looks up return type restriction in defining type, not instantiated type (#11961)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo(T)\n        def foo : T\n          x = uninitialized T\n          x\n        end\n      end\n\n      module Bar(T)\n        include Foo(T)\n      end\n\n      struct Tuple\n        include Bar(Union(*T))\n      end\n\n      class Test\n        def initialize\n          @foo = 0\n        end\n\n        def test\n          @foo = {@foo, 0}.foo\n        end\n      end\n\n      Test.new.@foo\n      CRYSTAL\n  end\n\n  it \"looks up self restriction in instantiated type, not defined type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo2\"] }\n      class Foo1\n        def foo : self\n          self\n        end\n      end\n\n      class Foo2 < Foo1\n      end\n\n      class Bar\n        def initialize\n          @x = Foo2.new\n        end\n\n        def bar\n          @x = @x.foo\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"inferrs Proc(Void) to Proc(Nil)\" do\n    assert_type(<<-CRYSTAL) { proc_of(nil_type) }\n      struct Proc\n        def self.new(&block : self)\n          block\n        end\n      end\n\n      class Foo\n        def initialize\n          @proc = Proc(Void).new { 1 }\n        end\n      end\n\n      Foo.new.@proc\n      CRYSTAL\n  end\n\n  describe \"instance variable inherited from multiple parents\" do\n    context \"with compatible type\" do\n      it \"module and class, with declarations\" do\n        result = assert_type(<<-CRYSTAL) { int32 }\n          module M\n            @a : Int32 = 1\n          end\n\n          class A\n            @a : Int32 = 2\n          end\n\n          class B < A\n            include M\n          end\n\n          B.new.@a\n          CRYSTAL\n\n        program = result.program\n        program.types[\"A\"].instance_vars.size.should eq(1)\n        program.types[\"B\"].instance_vars.size.should eq(0)\n      end\n\n      it \"module and class, with declarations (2)\" do\n        result = assert_type(<<-CRYSTAL) { int32 }\n          module M\n            @a = 1\n          end\n\n          class A\n            include M\n          end\n\n          class B < A\n            @a = 1\n          end\n\n          B.new.@a\n          CRYSTAL\n\n        program = result.program\n        program.types[\"A\"].instance_vars.size.should eq(1)\n        program.types[\"B\"].instance_vars.size.should eq(0)\n      end\n\n      it \"module and class, with declarations (3)\" do\n        result = assert_type(<<-CRYSTAL) { tuple_of [int32, int32] }\n          module M\n            @a = 1\n          end\n\n          class A\n            include M\n          end\n\n          class B < A\n            @a = 1\n          end\n\n          class C\n            @a = 1\n          end\n\n          class D < C\n            include M\n          end\n\n          {B.new.@a, D.new.@a}\n          CRYSTAL\n\n        program = result.program\n        program.types[\"A\"].instance_vars.size.should eq(1)\n        program.types[\"B\"].instance_vars.size.should eq(0)\n        program.types[\"C\"].instance_vars.size.should eq(1)\n        program.types[\"D\"].instance_vars.size.should eq(0)\n      end\n\n      it \"module and class, with definitions\" do\n        result = assert_type(<<-CRYSTAL) { int32 }\n          module M\n            @a = 1\n          end\n\n          class A\n            @a = 2\n          end\n\n          class B < A\n            include M\n          end\n\n          B.new.@a\n          CRYSTAL\n\n        program = result.program\n        program.types[\"A\"].instance_vars.size.should eq(1)\n        program.types[\"B\"].instance_vars.size.should eq(0)\n      end\n\n      it \"accepts module and module, with definitions\" do\n        semantic <<-CRYSTAL\n          module M\n            @a = 1\n          end\n\n          module N\n            @a = 2\n          end\n\n          class B\n            include N\n            include M\n          end\n          CRYSTAL\n      end\n\n      it \"accepts module and module, with declarations\" do\n        semantic <<-CRYSTAL\n          module M\n            @a : Int32?\n          end\n\n          module N\n            @a : Int32?\n          end\n\n          class B\n            include N\n            include M\n          end\n          CRYSTAL\n      end\n    end\n\n    context \"with incompatible type\" do\n      it \"module and class, with definitions\" do\n        assert_error <<-CRYSTAL, \"instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)\"\n          module M\n            @a = 'a'\n          end\n\n          class A\n            @a = 1\n          end\n\n          class B < A\n            include M\n          end\n          CRYSTAL\n      end\n\n      it \"module and class, with declarations\" do\n        assert_error <<-CRYSTAL, \"instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)\"\n          module M\n            @a : Char = 'a'\n          end\n\n          class A\n            @a : Int32 = 1\n          end\n\n          class B < A\n            include M\n          end\n          CRYSTAL\n      end\n\n      it \"errors module and module, with definitions\" do\n        assert_error <<-CRYSTAL, \"instance variable '@a' of B must be Char, not (Char | Int32)\"\n          module M\n            @a = 'c'\n          end\n\n          module N\n            @a = 1\n          end\n\n          class B\n            include N\n            include M\n          end\n          CRYSTAL\n      end\n\n      it \"errors module and module, with declarations\" do\n        assert_error <<-CRYSTAL, \"instance variable '@a' of B must be Int32, not (Char | Int32)\"\n          module M\n            @a : Char = 'c'\n          end\n\n          module N\n            @a : Int32 = 1\n          end\n\n          class B\n            include N\n            include M\n          end\n          CRYSTAL\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/is_a_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: is_a?\" do\n  it \"is bool\" do\n    assert_type(\"1.is_a?(Bool)\") { bool }\n  end\n\n  it \"restricts type inside if scope 1\" do\n    nodes = parse <<-CRYSTAL\n      a = 1 || 'a'\n      if a.is_a?(Int)\n        a\n      end\n      CRYSTAL\n    result = semantic nodes\n    mod, nodes = result.program, result.node.as(Expressions)\n    nodes.last.as(If).then.type.should eq(mod.int32)\n  end\n\n  it \"restricts type inside if scope 2\" do\n    nodes = parse <<-CRYSTAL\n      module Bar\n      end\n\n      class Foo(T)\n        include Bar\n      end\n\n      a = Foo(Int32).new || 1\n      if a.is_a?(Bar)\n        a\n      end\n      CRYSTAL\n\n    result = semantic nodes\n    mod, nodes = result.program, result.node.as(Expressions)\n\n    foo = mod.types[\"Foo\"].as(GenericClassType)\n    nodes.last.as(If).then.type.should eq(foo.instantiate([mod.int32] of TypeVar))\n  end\n\n  it \"restricts type inside if scope 3\" do\n    nodes = parse <<-CRYSTAL\n      class Foo\n        def initialize(@x : Int32)\n        end\n      end\n\n      a = Foo.new(1) || 1\n      if a.is_a?(Foo)\n        a\n      end\n      CRYSTAL\n\n    result = semantic nodes\n    mod, nodes = result.program, result.node.as(Expressions)\n    nodes.last.as(If).then.type.should eq(mod.types[\"Foo\"])\n  end\n\n  it \"restricts other types inside if else\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = 1 || 'a'\n      if a.is_a?(Int32)\n        a.to_i32\n      else\n        a.ord\n      end\n      CRYSTAL\n  end\n\n  it \"applies filter inside block\" do\n    assert_type(<<-CRYSTAL) { union_of(char, int32) }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo\n        yield\n      end\n\n      foo do\n        a = 1\n        unless a.is_a?(Int32)\n          LibC.exit\n        end\n      end\n\n      x = 1\n\n      foo do\n        a = 'a' || 1\n        x = a\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"applies negative condition filter if then is no return\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      def foo\n        if 1 == 1\n          'a'\n        else\n          1\n        end\n      end\n\n      def bar\n        elems = foo\n        if elems.is_a?(Char)\n          raise \"No!\"\n        end\n        elems\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"checks simple type with union\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = 1\n      if a.is_a?(Int32 | Char)\n        a + 1\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"checks union with union\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      struct Char\n        def +(other : Int32)\n          self\n        end\n      end\n\n      struct Bool\n        def foo\n          2\n        end\n      end\n\n      a = 1 || 'a' || false\n      if a.is_a?(Int32 | Char)\n        a + 2\n      else\n        a.foo\n      end\n      CRYSTAL\n  end\n\n  it \"restricts in assignment\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1 || 'a'\n      if (b = a).is_a?(Int32)\n        b\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"restricts type in else but lazily\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      foo = Foo.new(1)\n      x = foo.x\n      if x.is_a?(Int32)\n        z = x + 1\n      else\n        z = x.foo_bar\n      end\n\n      z\n      CRYSTAL\n  end\n\n  it \"types if is_a? preceded by return if (preserves nops)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nil_type }\n      def coco\n        return if 1 == 1\n\n        if 1.is_a?(Int32)\n        end\n      end\n\n      coco\n      CRYSTAL\n  end\n\n  it \"restricts type inside if else when used with module type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { bool }\n      module Moo\n      end\n\n      struct Int32\n        def foo\n          true\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      a = 1 == 1 ? 1 : Foo.new.as(Moo)\n      unless a.is_a?(Moo)\n        a.foo\n      else\n        false\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't fail on untyped is_a (#10317)\" do\n    assert_no_errors(<<-CRYSTAL)\n      require \"prelude\"\n\n      def foo(&block)\n      end\n\n      class Sup\n      end\n\n      foo do\n        Sup.new.is_a?(Sup)\n      end\n      CRYSTAL\n  end\n\n  it \"does is_a? from virtual metaclass to generic metaclass (#12302)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable generic_class(\"B\", string).metaclass }\n      class A\n      end\n\n      class B(T) < A\n      end\n\n      x = B(String).new.as(A).class\n\n      if x.is_a?(B(String).class)\n        x\n      else\n        nil\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/lib_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: lib\" do\n  it \"types a varargs external\" do\n    assert_type(\"lib LibFoo; fun bar(x : Int32, ...) : Int32; end; LibFoo.bar(1, 1.5, 'a')\") { int32 }\n  end\n\n  it \"raises on undefined fun\" do\n    assert_error <<-CRYSTAL, \"undefined fun 'foo' for LibC\"\n      lib LibC\n      end\n\n      LibC.foo\n      CRYSTAL\n  end\n\n  it \"raises wrong number of arguments\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'LibC#foo' (given 1, expected 0)\"\n      lib LibC\n        fun foo : Int32\n      end\n\n      LibC.foo 1\n      CRYSTAL\n  end\n\n  it \"raises wrong argument type\" do\n    assert_error <<-CRYSTAL, \"argument 'x' of 'LibC#foo' must be Int32, not Char\"\n      lib LibC\n        fun foo(x : Int32) : Int32\n      end\n\n      LibC.foo 'a'\n      CRYSTAL\n  end\n\n  it \"reports error when changing var type and something breaks\" do\n    assert_error <<-CRYSTAL, \"undefined method '+' for Char\"\n      class LibFoo\n        def initialize\n          @value = 1\n        end\n\n        def value\n          @value\n        end\n\n        def value=(@value : Char)\n        end\n      end\n\n      f = LibFoo.new\n      f.value + 1\n      f.value = 'a'\n      CRYSTAL\n  end\n\n  it \"reports error when changing instance var type and something breaks\" do\n    assert_error <<-CRYSTAL, \"argument 'c' of 'Lib#bar' must be Char\"\n      lib Lib\n        fun bar(c : Char)\n      end\n\n      class Foo\n        def value=(@value : Int32 | Char)\n        end\n\n        def value\n          @value\n        end\n      end\n\n      def foo(x)\n        x.value = 'a'\n        Lib.bar x.value\n      end\n\n      f = Foo.new\n      foo(f)\n\n      f.value = 1\n      CRYSTAL\n  end\n\n  it \"reports error on fun argument type not primitive like\" do\n    assert_error \"lib LibFoo; fun foo(x : Reference); end\",\n      \"only primitive types\"\n  end\n\n  it \"reports error on fun argument type not primitive like, Nil (#2994)\" do\n    assert_error \"lib LibFoo; fun foo(x : Nil); end\",\n      \"only primitive types\"\n  end\n\n  it \"reports error on fun return type not primitive like\" do\n    assert_error \"lib LibFoo; fun foo : Reference; end\",\n      \"only primitive types\"\n  end\n\n  it \"reports error on struct field type not primitive like\" do\n    assert_error \"lib LibFoo; struct Foo; x : Reference; end; end\",\n      \"only primitive types\"\n  end\n\n  it \"reports error on typedef type not primitive like\" do\n    assert_error \"lib LibFoo; type Foo = Reference; end\",\n      \"only primitive types\"\n  end\n\n  it \"reports error out can only be used with lib funs\" do\n    assert_error \"foo(out x)\",\n      \"out can only be used with lib funs\"\n  end\n\n  it \"reports error out can only be used with lib funs in named argument\" do\n    assert_error \"foo(x: out x)\",\n      \"out can only be used with lib funs\"\n  end\n\n  it \"reports error if using out with an already declared variable\" do\n    assert_error <<-CRYSTAL, \"variable 'x' is already defined, `out` must be used to define a variable, use another name\", inject_primitives: true\n      lib Lib\n        fun foo(x : Int32*)\n      end\n\n      x = Pointer(Int32).malloc(1_u64)\n      Lib.foo out x\n      CRYSTAL\n  end\n\n  it \"allows invoking out with underscore \" do\n    assert_type(<<-CRYSTAL) { float64 }\n      lib Lib\n        fun foo(x : Int32*) : Float64\n      end\n\n      Lib.foo out _\n      CRYSTAL\n  end\n\n  it \"reports redefinition of fun with different signature\" do\n    assert_error <<-CRYSTAL, \"fun redefinition with different signature\"\n      lib LibC\n        fun foo : Int32\n        fun foo : Int64\n      end\n      CRYSTAL\n  end\n\n  it \"types lib var get\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        $errno : Int32\n      end\n\n      LibC.errno\n      CRYSTAL\n  end\n\n  it \"types lib var set\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        $errno : Int32\n      end\n\n      LibC.errno = 1\n      CRYSTAL\n  end\n\n  it \"types lib var get with forward declaration\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        $errno : A\n\n        alias A = Int32\n      end\n\n      LibC.errno\n      CRYSTAL\n  end\n\n  it \"defined fun with aliased type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        alias SizeT = Int32\n        fun foo(x : SizeT) : SizeT\n      end\n\n      LibC.foo(1)\n      CRYSTAL\n  end\n\n  it \"overrides definition of fun\" do\n    result = assert_type(<<-CRYSTAL) { float64 }\n      lib LibC\n        fun foo(x : Int32) : Float64\n      end\n\n      lib LibC\n        fun foo = bar(x : Int32) : Float64\n      end\n\n      LibC.foo(1)\n      CRYSTAL\n    mod = result.program\n    lib_type = mod.types[\"LibC\"].as(LibType)\n    foo = lib_type.lookup_first_def(\"foo\", false).as(External)\n    foo.real_name.should eq(\"bar\")\n  end\n\n  it \"error if passing type to LibC with to_unsafe but type doesn't match\" do\n    assert_error <<-CRYSTAL, \"argument 'x' of 'LibC#foo' must be Int32, not Foo (nor Char returned by 'Foo#to_unsafe')\"\n      lib LibC\n        fun foo(x : Int32) : Int32\n      end\n\n      class Foo\n        def to_unsafe\n          'a'\n        end\n      end\n\n      LibC.foo Foo.new\n      CRYSTAL\n  end\n\n  it \"error if passing non primitive type as varargs\" do\n    assert_error <<-CRYSTAL, \"argument #2 of 'LibC#foo' is not a primitive type and no Foo#to_unsafe method found\"\n      lib LibC\n        fun foo(x : Int32, ...)\n      end\n\n      class Foo\n      end\n\n      LibC.foo 1, Foo.new\n      CRYSTAL\n  end\n\n  it \"error if passing non primitive type as varargs invoking to_unsafe\" do\n    assert_error <<-CRYSTAL, \"converted Foo invoking to_unsafe, but Bar is not a primitive type\"\n      lib LibC\n        fun foo(x : Int32, ...)\n      end\n\n      class Bar\n      end\n\n      class Foo\n        def to_unsafe\n          Bar.new\n        end\n      end\n\n      LibC.foo 1, Foo.new\n      CRYSTAL\n  end\n\n  it \"allows passing splat to LibC fun\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      lib LibC\n        fun foo(x : Int32, y : Float64, ...) : Float64\n      end\n\n      t = {1, 2.5, 3, 4}\n      LibC.foo *t\n      CRYSTAL\n  end\n\n  it \"allows passing double splat to LibC fun\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibC\n        fun foo(x : Int32, y : Float64) : Float64\n      end\n\n      t = {y: 2.5, x: 3}\n      LibC.foo **t\n      CRYSTAL\n  end\n\n  it \"errors if missing link arguments\" do\n    assert_error <<-CRYSTAL, \"missing link arguments: must at least specify a library name\"\n      @[Link]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if first argument is not a string\" do\n    assert_error <<-CRYSTAL, \"'lib' link argument must be a String\"\n      @[Link(1)]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if second argument is not a string\" do\n    assert_error <<-CRYSTAL, \"'ldflags' link argument must be a String\"\n      @[Link(\"foo\", 1)]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if third argument is not a bool\" do\n    assert_error <<-CRYSTAL, \"'static' link argument must be a Bool\"\n      @[Link(\"foo\", \"bar\", 1)]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if fourth argument is not a string\" do\n    assert_error <<-CRYSTAL, \"'framework' link argument must be a String\"\n      @[Link(\"foo\", \"bar\", true, 1)]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if too many link arguments\" do\n    assert_error <<-CRYSTAL, \"wrong number of link arguments (given 5, expected 1..4)\"\n      @[Link(\"foo\", \"bar\", true, \"Cocoa\", 1)]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if unknown named arg\" do\n    assert_error <<-CRYSTAL, \"unknown link argument: 'boo' (valid arguments are 'lib', 'ldflags', 'static', 'pkg_config', 'framework', 'wasm_import_module', and 'dll')\"\n      @[Link(boo: \"bar\")]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"allows dll argument\" do\n    assert_no_errors <<-CRYSTAL\n      @[Link(dll: \"foo.dll\")]\n      lib LibFoo\n      end\n      CRYSTAL\n\n    assert_no_errors <<-CRYSTAL\n      @[Link(dll: \"BAR.DLL\")]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if dll argument contains directory separators\" do\n    assert_error <<-CRYSTAL, \"'dll' link argument must not include directory separators\"\n      @[Link(dll: \"foo/bar.dll\")]\n      lib LibFoo\n      end\n      CRYSTAL\n\n    assert_error <<-CRYSTAL, \"'dll' link argument must not include directory separators\"\n      @[Link(dll: %q(foo\\\\bar.dll))]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if dll argument does not end with '.dll'\" do\n    assert_error <<-CRYSTAL, \"'dll' link argument must use a '.dll' file extension\"\n      @[Link(dll: \"foo\")]\n      lib LibFoo\n      end\n      CRYSTAL\n\n    assert_error <<-CRYSTAL, \"'dll' link argument must use a '.dll' file extension\"\n      @[Link(dll: \"foo.dylib\")]\n      lib LibFoo\n      end\n      CRYSTAL\n\n    assert_error <<-CRYSTAL, \"'dll' link argument must use a '.dll' file extension\"\n      @[Link(dll: \"\")]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if lib already specified with positional argument\" do\n    assert_error <<-CRYSTAL, \"'lib' link argument already specified\"\n      @[Link(\"foo\", lib: \"bar\")]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if lib named arg is not a String\" do\n    assert_error <<-CRYSTAL, \"'lib' link argument must be a String\"\n      @[Link(lib: 1)]\n      lib LibFoo\n      end\n      CRYSTAL\n  end\n\n  it \"clears annotations after lib\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      @[Link(\"foo\")]\n      lib LibFoo\n        fun foo\n      end\n      1\n      CRYSTAL\n  end\n\n  it \"warns if @[Link(static: true)] is specified\" do\n    assert_warning <<-CRYSTAL,\n      @[Link(\"foo\", static: true)]\n      lib Foo\n      end\n      CRYSTAL\n      \"warning in line 1\\nWarning: specifying static linking for individual libraries is deprecated\"\n  end\n\n  it \"warns if Link annotations use positional arguments\" do\n    assert_warning <<-CRYSTAL,\n      @[Link(\"foo\", \"bar\")]\n      lib Foo\n      end\n      CRYSTAL\n      \"warning in line 1\\nWarning: using non-named arguments for Link annotations is deprecated\"\n  end\n\n  it \"allows invoking lib call without obj inside lib\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        fun foo : Int32\n\n        A = foo\n      end\n\n      LibFoo::A\n      CRYSTAL\n  end\n\n  it \"errors if lib fun call is part of dispatch\" do\n    assert_error <<-CRYSTAL, \"lib fun call is not supported in dispatch\"\n      lib LibFoo\n        fun foo : Int32\n      end\n\n      class Bar\n        def self.foo\n        end\n      end\n\n      (LibFoo || Bar).foo\n      CRYSTAL\n  end\n\n  it \"disallows passing nil or pointer to arg expecting pointer\" do\n    assert_error <<-CRYSTAL, \"argument 'x' of 'Foo#foo' must be Pointer(Int32), not (Pointer(Int32) | Nil)\", inject_primitives: true\n      lib Foo\n        fun foo(x : Int32*) : Int64\n      end\n\n      a = 1 == 1 ? nil : Pointer(Int32).malloc(1_u64)\n      Foo.foo(a)\n      CRYSTAL\n  end\n\n  it \"correctly attached link flags if there's a macro if\" do\n    result = semantic(<<-CRYSTAL)\n      @[Link(\"SDL\")]\n      @[Link(\"SDLMain\")]\n      {% if flag?(:some_flag) %}\n        @[Link(framework: \"Cocoa\")]\n      {% end %}\n      lib LibSDL\n        fun init = SDL_Init(flags : UInt32) : Int32\n      end\n\n      LibSDL.init(0_u32)\n      CRYSTAL\n    sdl = result.program.types[\"LibSDL\"].as(LibType)\n    attrs = sdl.link_annotations.not_nil!\n    attrs.size.should eq(2)\n    attrs[0].lib.should eq(\"SDL\")\n    attrs[1].lib.should eq(\"SDLMain\")\n  end\n\n  it \"supports forward references (#399)\" do\n    assert_type(<<-CRYSTAL) { pointer_of(types[\"LibFoo\"].types[\"Bar\"]) }\n      lib LibFoo\n        fun foo() : Bar*\n\n        struct Bar\n          x : Int32\n        end\n      end\n\n      LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"supports forward references with struct inside struct (#399)\" do\n    assert_type(<<-CRYSTAL) { pointer_of(types[\"LibFoo\"].types[\"Foo\"]) }\n      lib LibFoo\n        struct Bar\n          x : Foo*\n        end\n\n        struct Foo\n          x : Int32\n        end\n      end\n\n      LibFoo::Bar.new.x\n      CRYSTAL\n  end\n\n  it \"errors if defines def on lib\" do\n    assert_error <<-CRYSTAL, \"can't define method in lib LibC\"\n      lib LibC\n      end\n\n      def LibC.foo\n      end\n      CRYSTAL\n  end\n\n  it \"reopens lib and adds more link annotations\" do\n    result = semantic(<<-CRYSTAL)\n      @[Link(\"SDL\")]\n      lib LibSDL\n        fun init = SDL_Init(flags : UInt32) : Int32\n      end\n\n      @[Link(\"SDLMain\")]\n      lib LibSDL\n      end\n\n      LibSDL.init(0_u32)\n      CRYSTAL\n    sdl = result.program.types[\"LibSDL\"].as(LibType)\n    attrs = sdl.link_annotations.not_nil!\n    attrs.size.should eq(2)\n    attrs[0].lib.should eq(\"SDL\")\n    attrs[1].lib.should eq(\"SDLMain\")\n  end\n\n  it \"reopens lib and adds same link annotations\" do\n    result = semantic(<<-CRYSTAL)\n      @[Link(\"SDL\")]\n      lib LibSDL\n        fun init = SDL_Init(flags : UInt32) : Int32\n      end\n\n      @[Link(\"SDL\")]\n      lib LibSDL\n      end\n\n      LibSDL.init(0_u32)\n      CRYSTAL\n    sdl = result.program.types[\"LibSDL\"].as(LibType)\n    attrs = sdl.link_annotations.not_nil!\n    attrs.size.should eq(1)\n    attrs[0].lib.should eq(\"SDL\")\n  end\n\n  it \"gathers link annotations from macro expression\" do\n    result = semantic(<<-CRYSTAL)\n      {% begin %}\n        @[Link(\"SDL\")]\n      {% end %}\n      lib LibSDL\n        fun init = SDL_Init : Int32\n      end\n\n      LibSDL.init\n      CRYSTAL\n    sdl = result.program.types[\"LibSDL\"].as(LibType)\n    attrs = sdl.link_annotations.not_nil!\n    attrs.size.should eq(1)\n    attrs[0].lib.should eq(\"SDL\")\n  end\n\n  it \"errors if using void as parameter (related to #508)\" do\n    assert_error <<-CRYSTAL, \"can't use Void as parameter type\"\n      lib LibFoo\n        fun foo(x : Void)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using void via typedef as parameter (related to #508)\" do\n    assert_error <<-CRYSTAL, \"can't use Void as parameter type\"\n      lib LibFoo\n        type Foo = Void\n        fun foo(x : Foo)\n      end\n      CRYSTAL\n  end\n\n  it \"can use tuple as fun return\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of TypeVar) }\n      lib LibC\n        fun foo : {Int32, Int32}\n      end\n\n      LibC.foo\n      CRYSTAL\n  end\n\n  it \"doesn't try to invoke unsafe for c struct/union (#1362)\" do\n    assert_error <<-CRYSTAL, \"argument 'x' of 'LibFoo#foo' must be Pointer(LibFoo::Bar), not LibFoo::Bar\"\n      lib LibFoo\n        struct Bar\n        end\n\n        fun foo(x : Bar*)\n      end\n\n      bar = LibFoo::Bar.new\n      LibFoo.foo(bar)\n      CRYSTAL\n  end\n\n  it \"passes int as another integer type in variable\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      a = 1_u8\n      LibFoo.foo a\n      CRYSTAL\n  end\n\n  it \"passes float as another integer type in variable\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibFoo\n        fun foo(x : Float32) : Int32\n      end\n\n      a = 1_f64\n      LibFoo.foo a\n      CRYSTAL\n  end\n\n  it \"passes int as another integer type with literal\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      LibFoo.foo 1_u8\n      CRYSTAL\n  end\n\n  it \"errors if invoking to_i32! and got error in that call\" do\n    assert_error <<-CRYSTAL, \"converting from Foo to Int32 by invoking 'to_i32!'\"\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      class Foo\n        def to_i32!\n          1 + 'a'\n        end\n      end\n\n      LibFoo.foo Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if invoking to_i32! and got wrong type\" do\n    assert_error <<-CRYSTAL, \"invoked 'to_i32!' to convert from Foo to Int32, but got Char\"\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      class Foo\n        def to_i32!\n          'a'\n        end\n      end\n\n      LibFoo.foo Foo.new\n      CRYSTAL\n  end\n\n  it \"defines lib funs before funs with body\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      fun foo : Int32\n        LibX.x\n      end\n\n      lib LibX\n        fun x : Int32\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors if using out with varargs\" do\n    assert_error <<-CRYSTAL, \"can't use out at varargs position: declare the variable with `z = uninitialized ...` and pass it with `pointerof(z)`\"\n      lib LibX\n        fun x(...)\n      end\n\n      LibX.x(out z)\n      CRYSTAL\n  end\n\n  it \"errors if using out with void pointer (#2424)\" do\n    assert_error <<-CRYSTAL, \"can't use out with Void* (parameter 'x' of LibFoo.foo is Void*)\"\n      lib LibFoo\n        fun foo(x : Void*)\n      end\n\n      LibFoo.foo(out x)\n      CRYSTAL\n  end\n\n  it \"errors if using out with void pointer through type\" do\n    assert_error <<-CRYSTAL, \"can't use out with Void* (parameter 'x' of LibFoo.foo is Void*)\"\n      lib LibFoo\n        type Foo = Void\n        fun foo(x : Foo*)\n      end\n\n      LibFoo.foo(out x)\n      CRYSTAL\n  end\n\n  it \"errors if using out with non-pointer\" do\n    assert_error <<-CRYSTAL, \"parameter 'x' of LibFoo.foo cannot be passed as 'out' because it is not a pointer\"\n      lib LibFoo\n        fun foo(x : Int32)\n      end\n\n      LibFoo.foo(out x)\n      CRYSTAL\n  end\n\n  it \"errors if redefining fun with different signature (#2468)\" do\n    assert_error <<-CRYSTAL, \"fun redefinition with different signature\"\n      fun foo\n      end\n\n      fun foo(x : Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using named args with variadic function\" do\n    assert_error <<-CRYSTAL, \"can't use named args with variadic function\"\n      lib LibC\n        fun foo(x : Int32, y : UInt8, ...) : Int32\n      end\n\n      LibC.foo y: 1_u8, x: 1\n      CRYSTAL\n  end\n\n  it \"errors if using unknown named param\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'z'\"\n      lib LibC\n        fun foo(x : Int32, y : UInt8) : Int32\n      end\n\n      LibC.foo y: 1_u8, x: 1, z: 2\n      CRYSTAL\n  end\n\n  it \"errors if parameter already specified\" do\n    assert_error <<-CRYSTAL, \"argument for parameter 'x' already specified\"\n      lib LibC\n        fun foo(x : Int32, y : UInt8) : Int32\n      end\n\n      LibC.foo 1, x: 2\n      CRYSTAL\n  end\n\n  it \"errors if missing argument\" do\n    assert_error <<-CRYSTAL, \"missing argument: y\"\n      lib LibC\n        fun foo(x : Int32, y : UInt8) : Int32\n      end\n\n      LibC.foo x: 2\n      CRYSTAL\n  end\n\n  it \"errors if missing arguments\" do\n    assert_error <<-CRYSTAL, \"missing arguments: x, z\"\n      lib LibC\n        fun foo(x : Int32, y : UInt8, z: Int32) : Int32\n      end\n\n      LibC.foo y: 1_u8\n      CRYSTAL\n  end\n\n  it \"can use named args\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun foo(x : Int32, y : UInt8) : Int32\n      end\n\n      LibC.foo y: 1_u8, x: 1\n      CRYSTAL\n  end\n\n  it \"can use out with named args\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun foo(x : Int32*)\n      end\n\n      LibC.foo(x: out x)\n      x\n      CRYSTAL\n  end\n\n  it \"types fun returning nothing as nil\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      lib LibFoo\n        fun foo\n      end\n\n      LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"types fun returning void as nil\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      lib LibFoo\n        fun foo : Void\n      end\n\n      LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"types fun returning nil as nil\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      lib LibFoo\n        fun foo : Nil\n      end\n\n      LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"can use macros inside lib\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        {% begin %}\n          fun foo : Int32\n        {% end %}\n      end\n\n      LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"can use macros inside struct\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        struct Foo\n          {% begin %}\n            x : Int32\n          {% end %}\n        end\n      end\n\n      LibFoo::Foo.new.x\n      CRYSTAL\n  end\n\n  it \"errors if defining incompatible funs with the same name in the same lib (#3045)\" do\n    assert_error <<-CRYSTAL, \"fun redefinition with different signature\"\n      lib LibFoo\n        fun foo1 = foo\n        fun foo2 = foo(x : Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if defining incompatible funs with the same name in different libs (#3045)\" do\n    assert_error <<-CRYSTAL, \"fun redefinition with different signature\"\n      lib LibFoo1\n        fun foo1 = foo\n      end\n\n      lib LibFoo2\n        fun foo2 = foo(x : Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"specifies a call convention\" do\n    result = semantic(<<-CRYSTAL)\n      lib LibFoo\n        @[CallConvention(\"X86_StdCall\")]\n        fun foo : Int32\n      end\n      CRYSTAL\n    foo = result.program.types[\"LibFoo\"].lookup_first_def(\"foo\", nil).as(External)\n    foo.call_convention.should eq(LLVM::CallConvention::X86_StdCall)\n  end\n\n  it \"specifies a call convention to a lib\" do\n    result = semantic(<<-CRYSTAL)\n      @[CallConvention(\"X86_StdCall\")]\n      lib LibFoo\n        fun foo : Int32\n      end\n      CRYSTAL\n    foo = result.program.types[\"LibFoo\"].lookup_first_def(\"foo\", nil).as(External)\n    foo.call_convention.should eq(LLVM::CallConvention::X86_StdCall)\n  end\n\n  it \"errors if wrong number of arguments for CallConvention\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for annotation CallConvention (given 2, expected 1)\"\n      lib LibFoo\n        @[CallConvention(\"X86_StdCall\", \"bar\")]\n        fun foo : Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if CallConvention argument is not a string\" do\n    assert_error <<-CRYSTAL, \"argument to CallConvention must be a string\"\n      lib LibFoo\n        @[CallConvention(1)]\n        fun foo : Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if CallConvention argument is not a valid string\" do\n    assert_error <<-CRYSTAL, \"invalid call convention. Valid values are #{LLVM::CallConvention.values.join \", \"}\"\n      lib LibFoo\n        @[CallConvention(\"foo\")]\n        fun foo : Int32\n      end\n      CRYSTAL\n  end\n\n  it \"errors if assigning void lib call to var (#4414)\" do\n    assert_error <<-CRYSTAL, \"assigning Void return value of lib fun call has no effect\"\n      lib LibFoo\n        fun foo\n      end\n\n      x = LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"errors if passing void lib call to call argument (#4414)\" do\n    assert_error <<-CRYSTAL, \"passing Void return value of lib fun call has no effect\"\n      lib LibFoo\n        fun foo\n      end\n\n      def bar(x)\n      end\n\n      bar(LibFoo.foo)\n      CRYSTAL\n  end\n\n  it \"can list lib functions at the top level (#12395)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      lib LibFoo\n        fun foo\n      end\n\n      {% if LibFoo.methods.size == 1 %}\n        true\n      {% else %}\n        1\n      {% end %}\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/macro_overload_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: macro overload\" do\n  it \"doesn't overwrite last macro definition if named args differs\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo(*, arg1)\n        1\n      end\n\n      macro foo(*, arg2)\n        \"foo\"\n      end\n\n      foo(arg1: true)\n      CRYSTAL\n\n    assert_type(<<-CRYSTAL) { string }\n      macro foo(*, arg1)\n        1\n      end\n\n      macro foo(*, arg2)\n        \"foo\"\n      end\n\n      foo(arg2: true)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/macro_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: macro\" do\n  it \"types macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors if macro uses undefined variable\" do\n    assert_error \"macro foo(x) {{y}} end; foo(1)\",\n      \"undefined macro variable 'y'\"\n  end\n\n  it \"types macro def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo : Int32\n          {{ @type }}\n          1\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if macro def type not found\" do\n    assert_error <<-CRYSTAL, \"undefined constant Foo\"\n      class Baz\n        def foo : Foo\n          {{ @type }}\n        end\n      end\n\n      Baz.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if macro def type doesn't match found\" do\n    assert_error <<-CRYSTAL, \"method Foo#foo must return Int32 but it is returning Char\"\n      class Foo\n        def foo : Int32\n          {{ @type}}\n          'a'\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows subclasses of return type for macro def\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          2\n        end\n      end\n\n      class Baz\n        def foobar : Foo\n          {{ @type }}\n          Bar.new\n        end\n      end\n\n      Baz.new.foobar.foo\n      CRYSTAL\n  end\n\n  it \"allows return values that include the return type of the macro def\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      module Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        include Foo\n\n        def foo\n          2\n        end\n      end\n\n      class Baz\n        def foobar : Foo\n          {{ @type }}\n          Bar.new\n        end\n      end\n\n      Baz.new.foobar.foo\n      CRYSTAL\n  end\n\n  it \"allows generic return types for macro def\" do\n    run(<<-CRYSTAL).to_i.should eq(2)\n      class Foo(T)\n        def foo\n          @foo\n        end\n\n        def initialize(@foo : T)\n        end\n      end\n\n      class Baz\n        def foobar : Foo(Int32)\n          {{ @type }}\n          Foo.new(2)\n        end\n      end\n\n      Baz.new.foobar.foo\n      CRYSTAL\n\n    assert_error(<<-CRYSTAL, \"method Bar#bar must return Foo(String) but it is returning Foo(Int32)\")\n      class Foo(T)\n        def initialize(@foo : T)\n        end\n      end\n\n      class Bar\n        def bar : Foo(String)\n          {{ @type }}\n          Foo.new(3)\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"allows union return types for macro def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo : String | Int32\n          {{ @type }}\n          1\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"types macro def that calls another method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def bar_baz\n        1\n      end\n\n      class Foo\n        def foo : Int32\n          {{ @type }}\n          {% begin %}\n            bar_{{ \"baz\".id }}\n          {% end %}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"types macro def that calls another method inside a class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def bar_baz\n          1\n        end\n\n        def foo : Int32\n          {{ @type }}\n          {% begin %}\n            bar_{{ \"baz\".id }}\n          {% end %}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"types macro def that calls another method inside a class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo : Int32\n          {{ @type }}\n          {% begin %}\n            bar_{{ \"baz\".id }}\n          {% end %}\n        end\n      end\n\n      class Bar < Foo\n        def bar_baz\n          1\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"types macro def with argument\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x) : Int32\n          {{ @type }}\n          x\n        end\n      end\n\n      Foo.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"expands macro with block\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        {{yield}}\n      end\n\n      foo do\n        def bar\n          1\n        end\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"expands macro with block and argument to yield\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        {{yield 1}}\n      end\n\n      foo do |value|\n        def bar\n          {{value}}\n        end\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"errors if find macros but wrong arguments\" do\n    assert_error(<<-CRYSTAL, \"wrong number of arguments for macro 'foo' (given 1, expected 0)\", inject_primitives: true)\n      macro foo\n        1\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"errors if find macros but missing argument\" do\n    assert_error(<<-CRYSTAL, \"wrong number of arguments for macro 'foo' (given 0, expected 1)\")\n      macro foo(x)\n        1\n      end\n\n      foo\n      CRYSTAL\n\n    assert_error(<<-CRYSTAL, \"wrong number of arguments for macro 'foo' (given 0, expected 1)\")\n      private macro foo(x)\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  describe \"raise\" do\n    describe \"inside macro\" do\n      describe \"without node\" do\n        it \"does not contain `expanding macro`\" do\n          ex = assert_error(<<-CRYSTAL, \"OH NO\")\n            macro foo\n              {{ raise \"OH NO\" }}\n            end\n\n            foo\n            CRYSTAL\n\n          ex.to_s.should_not contain(\"expanding macro\")\n        end\n\n        it \"supports an empty message (#8631)\" do\n          assert_error(<<-CRYSTAL, \"\")\n            macro foo\n              {{ raise \"\" }}\n            end\n\n            foo\n          CRYSTAL\n        end\n\n        it \"renders both frames (#7147)\" do\n          ex = assert_error(<<-CRYSTAL, \"OH NO\")\n            macro macro_raise(node)\n              {% raise \"OH NO\" %}\n            end\n\n            macro_raise 10\n          CRYSTAL\n\n          ex.to_s.should contain \"OH NO\"\n          ex.to_s.should contain \"error in line 2\"\n          ex.to_s.should contain \"error in line 5\"\n          ex.to_s.scan(\"error in line\").size.should eq 2\n        end\n      end\n\n      describe \"with node\" do\n        it \"contains the message and not `expanding macro` (#5669)\" do\n          ex = assert_error(<<-CRYSTAL, \"OH\")\n            macro foo(x)\n              {{ x.raise \"OH\\nNO\" }}\n            end\n\n            foo(1)\n          CRYSTAL\n\n          ex.to_s.should contain \"NO\"\n          ex.to_s.should_not contain(\"expanding macro\")\n        end\n\n        it \"renders both frames (#7147)\" do\n          ex = assert_error(<<-'CRYSTAL', \"OH\")\n            macro macro_raise_on(arg)\n              {% arg.raise \"OH NO\" %}\n            end\n\n            macro_raise_on 123\n          CRYSTAL\n\n          ex.to_s.should contain \"OH NO\"\n          ex.to_s.should contain \"error in line 5\"\n          ex.to_s.scan(\"error in line\").size.should eq 2\n        end\n\n        it \"pointing at the correct node in complex/nested macro (#7147)\" do\n          ex = assert_error(<<-'CRYSTAL', \"Value method must be an instance method\")\n            class Child\n              def self.value : Nil\n              end\n            end\n\n            module ExampleModule\n              macro calculate_value\n                {% begin %}\n                  {%\n                    if method = Child.class.methods.find &.name.stringify.==(\"value\")\n                      method.raise \"Value method must be an instance method.\"\n                    else\n                      raise \"BUG: Didn't find value method.\"\n                    end\n                  %}\n                {% end %}\n              end\n\n              class_getter value : Nil do\n                calculate_value\n              end\n            end\n\n            ExampleModule.value\n          CRYSTAL\n\n          ex.to_s.should contain \"error in line 20\"\n          ex.to_s.should contain \"error in line 2\"\n          ex.to_s.scan(\"error in line\").size.should eq 2\n        end\n\n        # TODO: Remove this spec once symbols literals have their location fixed\n        it \"points to caller when missing node location information (#7147)\" do\n          ex = assert_error(<<-'CRYSTAL', \"foo\")\n            macro macro_raise_on(arg)\n              {% arg.raise \"foo\" %}\n            end\n\n            macro_raise_on :this\n          CRYSTAL\n\n          ex.to_s.should contain \"error in line 5\"\n          ex.to_s.scan(\"error in line\").size.should eq 1\n        end\n      end\n    end\n\n    describe \"inside method\" do\n      describe \"without node\" do\n        it \"renders both frames (#7147)\" do\n          ex = assert_error(<<-CRYSTAL, \"OH\")\n            def foo(x)\n              {% raise \"OH NO\" %}\n            end\n\n            foo 1\n          CRYSTAL\n\n          ex.to_s.should contain \"OH NO\"\n          ex.to_s.should contain \"error in line 2\"\n          ex.to_s.should contain \"error in line 5\"\n          ex.to_s.scan(\"error in line\").size.should eq 2\n        end\n      end\n    end\n  end\n\n  it \"can specify tuple as return type\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of Type) }\n      class Foo\n        def foo : {Int32, Int32}\n          {{ @type }}\n          {1, 2}\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows specifying self as macro def return type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def foo : self\n          {{ @type }}\n          self\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows specifying self as macro def return type (2)\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo\n        def foo : self\n          {{ @type }}\n          self\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"preserves correct self in restriction when macro def is to be instantiated in subtypes (#5044)\" do\n    assert_type(<<-CRYSTAL) { string }\n      class Foo\n        def foo(x)\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo(x : self)\n          {{ @type }}\n          \"x\"\n        end\n      end\n\n      class Baz < Bar\n      end\n\n      class Baz2 < Bar\n      end\n\n      (Baz.new || Baz2.new).foo(Baz.new)\n      CRYSTAL\n  end\n\n  it \"doesn't affect self restrictions outside the macro def being instantiated in subtypes\" do\n    assert_type(<<-CRYSTAL) { union_of int32, bool }\n      class Foo\n        def foo(other) : Bool\n          {% @type %}\n          false\n        end\n      end\n\n      class Bar1 < Foo\n        def bar1\n          1\n        end\n\n        def foo(other : self)\n          other.bar1\n        end\n      end\n\n      class Bar2 < Foo\n        def bar2\n          \"\"\n        end\n\n        def foo(other : self)\n          other.bar2\n        end\n      end\n\n      Foo.new.as(Foo).foo(Bar1.new)\n      CRYSTAL\n  end\n\n  it \"errors if non-existent named arg\" do\n    assert_error(<<-CRYSTAL, \"no parameter named 'y'\")\n      macro foo(x = 1)\n        {{x}} + 1\n      end\n\n      foo y: 2\n      CRYSTAL\n  end\n\n  it \"errors if named arg already specified\" do\n    assert_error(<<-CRYSTAL, \"argument for parameter 'x' already specified\")\n      macro foo(x = 1)\n        {{x}} + 1\n      end\n\n      foo 2, x: 2\n      CRYSTAL\n  end\n\n  it \"finds macro in included module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        macro bar\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          bar\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors when trying to define def inside def with macro expansion\" do\n    assert_error(<<-CRYSTAL, \"can't define def inside def\")\n      macro foo\n        def bar; end\n      end\n\n      def baz\n        foo\n      end\n\n      baz\n      CRYSTAL\n  end\n\n  it \"error raised within complex macro included hook (#7394)\" do\n    ex = assert_error(<<-'CRYSTAL', \"Value method must be an instance method\")\n      module ExampleModule\n        macro included\n          {% verbatim do %}\n            {%\n              if method = @type.class.methods.find &.name.stringify.==(\"value\")\n                method.raise \"Value method must be an instance method.\"\n              else\n                raise \"BUG: Didn't find value method.\"\n              end\n            %}\n          {% end %}\n        end\n      end\n\n      class ExampleClass\n        def self.value : Nil\n        end\n\n        include ExampleModule\n      end\n    CRYSTAL\n\n    ex.to_s.should contain \"error in line 16\"\n  end\n\n  it \"error raise within macro included hook points to `include` vs `raise`\" do\n    ex = assert_error(<<-'CRYSTAL', \"noooo\")\n      module Foo\n        macro included\n          {% raise \"noooo\" %}\n        end\n      end\n\n      include Foo\n    CRYSTAL\n\n    ex.to_s.should contain \"error in line 3\"\n    ex.to_s.should contain \"error in line 7\"\n  end\n\n  it \"error raise within macro inherited hook points to the inheriting type vs `raise`\" do\n    ex = assert_error(<<-'CRYSTAL', \"noooo\")\n      abstract struct Parent\n        macro inherited\n          {% raise \"noooo\" %}\n        end\n      end\n\n      struct Child < Parent\n      end\n    CRYSTAL\n\n    ex.to_s.should contain \"error in line 3\"\n    ex.to_s.should contain \"error in line 7\"\n  end\n\n  it \"gives precise location info when doing yield inside macro\" do\n    assert_error(<<-CRYSTAL, \"in line 6\")\n      macro foo\n        {{yield}}\n      end\n\n      foo do\n        1 + 'a'\n      end\n      CRYSTAL\n  end\n\n  it \"transforms with {{yield}} and call\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        bar({{yield}})\n      end\n\n      def bar(value)\n        value\n      end\n\n      def baz\n        1\n      end\n\n      foo do\n        baz\n      end\n      CRYSTAL\n  end\n\n  it \"begins with {{ yield }} (#15050)\" do\n    result = top_level_semantic <<-CRYSTAL, wants_doc: true\n      macro foo\n        {{yield}}\n      end\n\n      foo do\n        # doc comment\n        def test\n        end\n      end\n      CRYSTAL\n    result.program.defs.try(&.[\"test\"][0].def.doc).should eq \"doc comment\"\n  end\n\n  it \"can return class type in macro def\" do\n    assert_type(<<-CRYSTAL) { types[\"Int32\"].metaclass }\n      class Foo\n        def foo : Int32.class\n          {{ @type }}\n          Int32\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"can return virtual class type in macro def\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Foo\"].metaclass.virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Foo\n        def foo : Foo.class\n          {{ @type }}\n          1 == 1 ? Foo : Bar\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"can't define new variables (#466)\" do\n    error = assert_error <<-CRYSTAL\n      macro foo\n        hello = 1\n      end\n\n      foo\n      hello\n      CRYSTAL\n\n    error.to_s.should_not contain(\"did you mean\")\n  end\n\n  it \"finds macro in included generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(T)\n        macro moo\n          1\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n\n        def foo\n          moo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"finds macro in inherited generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Moo(T)\n        macro moo\n          1\n        end\n      end\n\n      class Foo < Moo(Int32)\n        def foo\n          moo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"doesn't die on && inside if (bug)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        1 && 2\n      end\n\n      foo ? 3 : 4\n      CRYSTAL\n  end\n\n  it \"checks if macro expansion returns (#821)\" do\n    assert_type(<<-CRYSTAL) { nilable symbol }\n      macro pass\n        return :pass\n      end\n\n      def me\n        pass\n        nil\n      end\n\n      me\n      CRYSTAL\n  end\n\n  it \"errors if declares macro inside if\" do\n    assert_error(<<-CRYSTAL, \"can't declare macro dynamically\")\n      if 1 == 2\n        macro foo; end\n      end\n      CRYSTAL\n  end\n\n  it \"allows declaring class with macro if\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      {% if true %}\n        class Foo; end\n      {% end %}\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"allows declaring class with macro for\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      {% for i in 0..0 %}\n        class Foo; end\n      {% end %}\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"allows declaring class with inline macro expression (#1333)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      {{ \"class Foo; end\".id }}\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"errors if requires inside class through macro expansion\" do\n    str = %(\n      macro req\n        require \"bar\"\n      end\n\n      class Foo\n        req\n      end\n    )\n    expect_raises SyntaxException, \"can't require inside type declarations\" do\n      semantic parse str\n    end\n  end\n\n  it \"errors if requires inside if through macro expansion\" do\n    assert_error(<<-CRYSTAL, \"can't require dynamically\")\n      macro req\n        require \"bar\"\n      end\n\n      if 1 == 2\n        req\n      end\n      CRYSTAL\n  end\n\n  it \"can define constant via macro included\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Mod\n        macro included\n          CONST = 1\n        end\n      end\n\n      include Mod\n\n      CONST\n      CRYSTAL\n  end\n\n  it \"errors if applying protected modifier to macro\" do\n    assert_error(<<-CRYSTAL, \"can only use 'private' for macros\")\n      class Foo\n        protected macro foo\n          1\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"expands macro with break inside while (#1852)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      macro test\n        foo = \"bar\"\n        break\n      end\n\n      while true\n        test\n      end\n      CRYSTAL\n  end\n\n  it \"can access variable inside macro expansion (#2057)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        x\n      end\n\n      def method\n        yield 1\n      end\n\n      method do |x|\n        foo\n      end\n      CRYSTAL\n  end\n\n  it \"declares variable for macro with out\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        fun foo(x : Int32*)\n      end\n\n      macro some_macro\n        z\n      end\n\n      LibFoo.foo(out z)\n      some_macro\n      CRYSTAL\n  end\n\n  it \"show macro trace in errors (1)\" do\n    ex = assert_error(<<-CRYSTAL, \"Error: expanding macro\")\n      macro foo\n        Bar\n      end\n\n      foo\n      CRYSTAL\n\n    ex.to_s.should contain \"error in line 5\"\n  end\n\n  it \"show macro trace in errors (2)\" do\n    ex = assert_error(<<-CRYSTAL, \"Error: expanding macro\")\n      {% begin %}\n        Bar\n      {% end %}\n      CRYSTAL\n\n    ex.to_s.should contain \"error in line 1\"\n  end\n\n  it \"errors if using macro that is defined later\" do\n    assert_error(<<-CRYSTAL, \"macro 'foo' must be defined before this point but is defined later\")\n      class Bar\n        foo\n      end\n\n      macro foo\n      end\n      CRYSTAL\n  end\n\n  it \"looks up argument types in macro owner, not in subclass (#2395)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Nil\n        def method(x : Problem)\n          0\n        end\n      end\n\n      class Foo\n        def method(x : Problem) : Int32\n          {% for ivar in @type.instance_vars %}\n            @{{ivar.id}}.method(x)\n          {% end %}\n          42\n        end\n      end\n\n      class Problem\n      end\n\n      module Moo\n        class Problem\n        end\n\n        class Bar < Foo\n          @foo : Foo?\n        end\n      end\n\n      Moo::Bar.new.method(Problem.new)\n      CRYSTAL\n  end\n\n  it \"doesn't error when adding macro call to constant (#2457)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n      end\n\n      ITS = {} of String => String\n\n      macro coco\n        {% ITS[\"foo\"] = yield %}\n        1\n      end\n\n      coco do\n        foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if named arg matches single splat parameter\" do\n    assert_error(<<-CRYSTAL, \"no parameter named 'x'\")\n      macro foo(*y)\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"errors if named arg matches splat parameter\" do\n    assert_error(<<-CRYSTAL, \"wrong number of arguments for macro 'foo' (given 0, expected 1+)\")\n      macro foo(x, *y)\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"says missing argument because positional args don't match past splat\" do\n    assert_error(<<-CRYSTAL, \"missing argument: z\")\n      macro foo(x, *y, z)\n      end\n\n      foo 1, 2\n      CRYSTAL\n  end\n\n  it \"allows named args after splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32]), char]) }\n      macro foo(*y, x)\n        { {{y}}, {{x}} }\n      end\n\n      foo 1, x: 'a'\n      CRYSTAL\n  end\n\n  it \"errors if missing one argument\" do\n    assert_error(<<-CRYSTAL, \"missing argument: z\")\n      macro foo(x, y, z)\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"errors if missing two arguments\" do\n    assert_error(<<-CRYSTAL, \"missing arguments: x, z\")\n      macro foo(x, y, z)\n      end\n\n      foo y: 2\n      CRYSTAL\n  end\n\n  it \"doesn't include parameters with default values in missing arguments error\" do\n    assert_error(<<-CRYSTAL, \"missing argument: z\")\n      macro foo(x, z, y = 1)\n      end\n\n      foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"solves macro expression arguments before macro expansion (type)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo(x)\n        {% if x.is_a?(TypeNode) && x.name == \"String\" %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n      end\n\n      foo({{ String }})\n      CRYSTAL\n  end\n\n  it \"solves macro expression arguments before macro expansion (constant)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo(x)\n        {% if x.is_a?(NumberLiteral) && x == 1 %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n      end\n\n      CONST = 1\n      foo({{ CONST }})\n      CRYSTAL\n  end\n\n  it \"solves named macro expression arguments before macro expansion (type) (#2423)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo(x)\n        {% if x.is_a?(TypeNode) && x.name == \"String\" %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n      end\n\n      foo(x: {{ String }})\n      CRYSTAL\n  end\n\n  it \"solves named macro expression arguments before macro expansion (constant) (#2423)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo(x)\n        {% if x.is_a?(NumberLiteral) && x == 1 %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n      end\n\n      CONST = 1\n      foo(x: {{ CONST }})\n      CRYSTAL\n  end\n\n  it \"finds generic type argument of included module\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Bar(T)\n        def t\n          {{ T }}\n        end\n      end\n\n      class Foo(U)\n        include Bar(U)\n      end\n\n      Foo(Int32).new.t\n      CRYSTAL\n  end\n\n  it \"finds generic type argument of included module with self\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Foo\", int32).metaclass }\n      module Bar(T)\n        def t\n          {{ T }}\n        end\n      end\n\n      class Foo(U)\n        include Bar(self)\n      end\n\n      Foo(Int32).new.t\n      CRYSTAL\n  end\n\n  it \"finds free type vars\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, string.metaclass]) }\n      module Foo(T)\n        def self.foo(foo : U) forall U\n          { {{ T }}, {{ U }} }\n        end\n      end\n\n      Foo(Int32).foo(\"foo\")\n      CRYSTAL\n  end\n\n  it \"finds type for global path shared with free var\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module T\n      end\n\n      def foo(x : T) forall T\n        {{ ::T.module? ? 1 : 'a' }}\n      end\n\n      foo(\"\")\n      CRYSTAL\n  end\n\n  it \"gets named arguments in double splat\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": string, \"y\": bool}) }\n      macro foo(**options)\n        {{options}}\n      end\n\n      foo x: \"foo\", y: true\n      CRYSTAL\n  end\n\n  it \"uses splat and double splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, char]), named_tuple_of({\"x\": string, \"y\": bool})]) }\n      macro foo(*args, **options)\n        { {{args}}, {{options}} }\n      end\n\n      foo 1, 'a', x: \"foo\", y: true\n      CRYSTAL\n  end\n\n  it \"double splat and regular args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, bool, named_tuple_of({\"w\": char, \"z\": string})]) }\n      macro foo(x, y, **options)\n        { {{x}}, {{y}}, {{options}} }\n      end\n\n      foo 1, w: 'a', y: true, z: \"z\"\n      CRYSTAL\n  end\n\n  it \"declares multi-assign vars for macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro id(x, y)\n        {{x}}\n        {{y}}\n      end\n\n      a, b = 1, 2\n      id(a, b)\n      1\n      CRYSTAL\n  end\n\n  it \"declares rescue variable inside for macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro id(x)\n        {{x}}\n      end\n\n      begin\n      rescue ex\n        id(ex)\n      end\n\n      1\n      CRYSTAL\n  end\n\n  it \"matches with default value after splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, tuple_of([char]), bool]) }\n      macro foo(x, *y, z = true)\n        { {{x}}, {{y}}, {{z}} }\n      end\n\n      foo 1, 'a'\n      CRYSTAL\n  end\n\n  it \"uses bare *\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      macro foo(x, *, y)\n        { {{x}}, {{y}} }\n      end\n\n      foo 10, y: 'a'\n      CRYSTAL\n  end\n\n  it \"uses bare *, doesn't let more args\" do\n    assert_error(<<-CRYSTAL, \"wrong number of arguments for macro 'foo' (given 2, expected 1)\")\n      macro foo(x, *, y)\n      end\n\n      foo 10, 20, y: 30\n      CRYSTAL\n  end\n\n  it \"uses bare *, doesn't let more args\" do\n    assert_error(<<-CRYSTAL, \"no overload matches\")\n      def foo(x, *, y)\n      end\n\n      foo 10, 20, y: 30\n      CRYSTAL\n  end\n\n  it \"finds macro through alias (#2706)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        macro bar\n          1\n        end\n      end\n\n      alias Foo = Moo\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"can override macro (#2773)\" do\n    assert_type(<<-CRYSTAL) { char }\n      macro foo\n        1\n      end\n\n      macro foo\n        'a'\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"works inside proc literal (#2984)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      macro foo\n        1\n      end\n\n      ->{ foo }.call\n      CRYSTAL\n  end\n\n  it \"finds var in proc for macros\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      macro foo(x)\n        {{x}}\n      end\n\n      ->(x : Int32) { foo(x) }.call(1)\n      CRYSTAL\n  end\n\n  it \"applies visibility modifier only to first level\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo\n        class Foo\n          def self.foo\n            1\n          end\n        end\n      end\n\n      private foo\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"gives correct error when method is invoked but macro exists at the same scope\" do\n    assert_error(<<-CRYSTAL, \"undefined method 'foo'\")\n      macro foo(x)\n      end\n\n      class Foo\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"uses uninitialized variable with macros\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      macro foo(x)\n        {{x}}\n      end\n\n      a = uninitialized Int32\n      foo(a)\n      CRYSTAL\n  end\n\n  describe \"skip_file macro directive\" do\n    it \"skips expanding the rest of the current file\" do\n      res = semantic(<<-CRYSTAL)\n        class A\n        end\n\n        {% skip_file %}\n\n        class B\n        end\n        CRYSTAL\n\n      res.program.types.has_key?(\"A\").should be_true\n      res.program.types.has_key?(\"B\").should be_false\n    end\n\n    it \"skips file inside an if macro expression\" do\n      res = semantic(<<-CRYSTAL)\n        class A\n        end\n\n        {% if true %}\n          class C; end\n          {% skip_file %}\n          class D; end\n        {% end %}\n\n        class B\n        end\n        CRYSTAL\n\n      res.program.types.has_key?(\"A\").should be_true\n      res.program.types.has_key?(\"B\").should be_false\n      res.program.types.has_key?(\"C\").should be_true\n      res.program.types.has_key?(\"D\").should be_false\n    end\n  end\n\n  it \"finds method before macro (#236)\" do\n    assert_type(<<-CRYSTAL) { char }\n      macro global\n        1\n      end\n\n      class Foo\n        def global\n          'a'\n        end\n\n        def bar\n          global\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"finds macro and method at the same scope\" do\n    assert_type(<<-CRYSTAL) { tuple_of [int32, char] }\n      macro global(x)\n        1\n      end\n\n      def global(x, y)\n        'a'\n      end\n\n      {global(1), global(1, 2)}\n      CRYSTAL\n  end\n\n  it \"finds macro and method at the same scope inside included module\" do\n    assert_type(<<-CRYSTAL) { tuple_of [int32, char] }\n      module Moo\n        macro global(x)\n          1\n        end\n\n        def global(x, y)\n          'a'\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def main\n          {global(1), global(1, 2)}\n        end\n      end\n\n      Foo.new.main\n      CRYSTAL\n  end\n\n  it \"finds macro in included module at class level (#4639)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        macro foo\n          def self.bar\n            2\n          end\n        end\n      end\n\n      class Foo\n        include Moo\n\n        foo\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"finds macro in module in Object\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Object\n        macro foo\n          def self.bar\n            2\n          end\n        end\n      end\n\n      module Moo\n        foo\n      end\n\n      Moo.bar\n      CRYSTAL\n  end\n\n  it \"finds metaclass instance of instance method (#4739)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Parent\n        macro foo\n          def self.bar\n            1\n          end\n        end\n      end\n\n      class Child < Parent\n        def foo\n        end\n      end\n\n      class GrandChild < Child\n        foo\n      end\n\n      GrandChild.bar\n      CRYSTAL\n  end\n\n  it \"finds metaclass instance of instance method (#4639)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Include\n        macro foo\n          def foo\n            1\n          end\n        end\n      end\n\n      class Parent\n        include Include\n\n        foo\n      end\n\n      class Foo < Parent\n        foo\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"can lookup type parameter when macro is called inside class (#5343)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(T)\n        macro foo\n          {{T}}\n        end\n      end\n\n      alias FooInt32 = Foo(Int32)\n\n      class Bar\n        def self.foo\n          FooInt32.foo\n        end\n      end\n\n      Bar.foo\n      CRYSTAL\n  end\n\n  it \"cannot lookup type defined in caller class\" do\n    assert_error(<<-CRYSTAL, \"undefined constant Baz\")\n      class Foo\n        macro foo\n          {{Baz}}\n        end\n      end\n\n      class Bar\n        def self.foo\n          Foo.foo\n        end\n\n        class Baz\n        end\n      end\n\n      Bar.foo\n      CRYSTAL\n  end\n\n  it \"clones default value before expanding\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      FOO = {} of String => String?\n\n      macro foo(x = {} of String => String)\n        {% FOO[\"foo\"] = x[\"foo\"] %}\n        {% x[\"foo\"] = \"foo\" %}\n      end\n\n      foo\n      foo\n      {{ FOO[\"foo\"] }}\n      CRYSTAL\n  end\n\n  it \"does macro verbatim inside macro\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"].metaclass }\n      class Foo\n        macro inherited\n          {% verbatim do %}\n            def foo\n              {{ @type }}\n            end\n          {% end %}\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"does macro verbatim outside macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      {% verbatim do %}\n        1\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"evaluates yield expression (#2924)\" do\n    assert_type(<<-CRYSTAL) { string }\n      macro a(b)\n        {{yield b}}\n      end\n\n      a(\"foo\") do |c|\n        {{c}}\n      end\n      CRYSTAL\n  end\n\n  it \"finds generic in macro code\" do\n    assert_type(<<-CRYSTAL) { array_of(string).metaclass }\n      {% begin %}\n        {{ Array(String) }}\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"finds generic in macro code using free var\" do\n    assert_type(<<-CRYSTAL) { array_of(int32).metaclass }\n      class Foo(T)\n        def self.foo\n          {% begin %}\n            {{ Array(T) }}\n          {% end %}\n        end\n      end\n\n      Foo(Int32).foo\n      CRYSTAL\n  end\n\n  it \"expands multiline macro expression in verbatim (#6643)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      {% verbatim do %}\n        {{\n          if true\n            1\n            \"2\"\n            3\n          end\n        }}\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"preserves escaped interpolation in verbatim (#16413)\" do\n    assert_type(<<-'CRYSTAL') { nil_type }\n      {% begin %}\n        {% verbatim do %}\n          {%\n            name = \"FOO\"\n            \"\\#{get_env(#{name})}\"\n          %}\n        {% end %}\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"can use macro in instance var initializer (#7666)\" do\n    assert_type(<<-CRYSTAL) { string }\n      class Foo\n        macro m\n          \"test\"\n        end\n\n        @x : String = m\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can use macro in instance var initializer (just assignment) (#7666)\" do\n    assert_type(<<-CRYSTAL) { string }\n      class Foo\n        macro m\n          \"test\"\n        end\n\n        @x = m\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"shows correct error message in macro expansion (#7083)\" do\n    assert_error(<<-CRYSTAL, \"can't instantiate abstract class Foo\")\n      abstract class Foo\n        {% begin %}\n          def self.new\n            allocate\n          end\n        {% end %}\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"doesn't crash on syntax error inside macro (regression, #8038)\" do\n    expect_raises(Crystal::SyntaxException, \"unterminated array literal\") do\n      semantic(<<-CRYSTAL)\n        {% begin %}[{% end %}\n        CRYSTAL\n    end\n  end\n\n  it \"has correct location after expanding assignment after instance var\" do\n    result = semantic <<-CRYSTAL\n      macro foo(x)       #  1\n        @{{x}}           #  2\n                         #  3\n        def bar          #  4\n        end              #  5\n      end                #  6\n                         #  7\n      class Foo          #  8\n        foo(x = 1)       #  9\n      end\n      CRYSTAL\n\n    method = result.program.types[\"Foo\"].lookup_first_def(\"bar\", false).not_nil!\n    method.location.not_nil!.expanded_location.not_nil!.line_number.should eq(9)\n  end\n\n  it \"assigns to underscore\" do\n    assert_no_errors <<-CRYSTAL\n      {% _ = 1 %}\n      CRYSTAL\n  end\n\n  it \"unpacks block parameters inside macros (#13742)\" do\n    assert_no_errors <<-CRYSTAL\n      macro foo\n        {% [{1, 2}, {3, 4}].each { |(k, v)| k } %}\n      end\n\n      foo\n      CRYSTAL\n\n    assert_no_errors <<-CRYSTAL\n      macro foo\n        {% [{1, 2}, {3, 4}].each { |(k, v)| k } %}\n      end\n\n      foo\n      foo\n      CRYSTAL\n  end\n\n  it \"unpacks to underscore within block parameters inside macros\" do\n    assert_type(<<-CRYSTAL) { bool }\n      {% begin %}\n        {% x = nil %}\n        {% [{1, true, 'a', \"\"}].each { |(_, y, _, _)| x = y } %}\n        {{ x }}\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"executes OpAssign (#9356)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      {% begin %}\n        {% a = nil %}\n        {% a ||= 1 %}\n        {% if a %}\n          1\n        {% else %}\n          'a'\n        {% end %}\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"executes MultiAssign\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of Type) }\n      {% begin %}\n        {% a, b = 1, 2 %}\n        { {{a}}, {{b}} }\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"executes MultiAssign with ArrayLiteral value\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of Type) }\n      {% begin %}\n        {% xs = [1, 2] %}\n        {% a, b = xs %}\n        { {{a}}, {{b}} }\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"assigns to underscore in MultiAssign\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, bool]) }\n      {% begin %}\n        {% _, x, *_, y = [1, 'a', \"\", nil, true] %}\n        { {{x}}, {{y}} }\n      {% end %}\n      CRYSTAL\n  end\n\n  describe \"@caller\" do\n    it \"returns an array of each call\" do\n      assert_type(<<-CRYSTAL) { int32 }\n        macro test\n          {{@caller.size == 1 ? 1 : 'f'}}\n        end\n\n        test\n        CRYSTAL\n    end\n\n    it \"provides access to the `Call` information\" do\n      assert_type(<<-CRYSTAL) { tuple_of([int32, char] of Type) }\n        macro test(num)\n          {{@caller.first.args[0] == 1 ? 1 : 'f'}}\n        end\n\n        {test(1), test(2)}\n        CRYSTAL\n    end\n\n    it \"returns nil if no stack is available\" do\n      assert_type(<<-CRYSTAL) { char }\n        def test\n          {{(c = @caller) ? 1 : 'f'}}\n        end\n\n        test\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/metaclass_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate struct ProperSubtypeExpectation\n  def initialize(@expected_value : Crystal::Type?)\n  end\n\n  def match(actual_value)\n    subtype?(actual_value, @expected_value) && !subtype?(@expected_value, actual_value)\n  end\n\n  private def subtype?(t, u)\n    if t.is_a?(Crystal::NoReturnType?)\n      true\n    elsif u.is_a?(Crystal::NoReturnType?)\n      false\n    else\n      t.implements?(u)\n    end\n  end\n\n  def failure_message(actual_value)\n    actual_str = type_to_s(actual_value)\n    expected_str = type_to_s(@expected_value)\n\n    String.build do |io|\n      io << \"Expected: #{actual_str} to be a proper subtype of #{expected_str}, but \"\n      if !subtype?(actual_value, @expected_value)\n        io << \"#{actual_str} <= #{expected_str} is false\"\n      else # subtype?(@expected_value, actual_value)\n        io << \"#{expected_str} <= #{actual_str} is true\"\n      end\n    end\n  end\n\n  def negative_failure_message(actual_value)\n    \"Expected: #{type_to_s(actual_value)} not to be a proper subtype of #{type_to_s(@expected_value)}\"\n  end\n\n  private def type_to_s(type)\n    case type\n    when Nil\n      \"NoReturn\"\n    when Crystal::GenericInstanceType, Crystal::GenericClassInstanceMetaclassType, Crystal::GenericModuleInstanceMetaclassType\n      type.to_s(generic_args: true)\n    else\n      type.to_s(generic_args: false)\n    end\n  end\nend\n\nprivate def be_a_proper_subtype_of(supertype)\n  ProperSubtypeExpectation.new(supertype)\nend\n\ndescribe \"Semantic: metaclass\" do\n  it \"types Object class\" do\n    assert_type(\"Object\") { program.object.metaclass }\n  end\n\n  it \"types Class class\" do\n    assert_type(\"Class\") { class_type }\n  end\n\n  it \"types Object and Class metaclasses\" do\n    assert_type(\"Object.class\", inject_primitives: true) { class_type }\n    assert_type(\"Class.class\", inject_primitives: true) { class_type }\n  end\n\n  it \"types Reference metaclass\" do\n    assert_type(\"Reference\") { program.reference.metaclass }\n    assert_type(\"Reference.class\", inject_primitives: true) { class_type }\n  end\n\n  it \"types generic class metaclass\" do\n    assert_type(\"Pointer\") { pointer.metaclass }\n    assert_type(\"Pointer.class\", inject_primitives: true) { class_type }\n    assert_type(\"Pointer(Int32)\") { pointer_of(int32).metaclass }\n    assert_type(\"Pointer(Int32).class\", inject_primitives: true) { class_type }\n  end\n\n  it \"types generic module metaclass\" do\n    assert_type(\"module Foo(T); end; Foo\") { types[\"Foo\"].metaclass }\n    assert_type(\"module Foo(T); end; Foo.class\", inject_primitives: true) { class_type }\n    assert_type(\"module Foo(T); end; Foo(Int32)\") { generic_module(\"Foo\", int32).metaclass }\n    assert_type(\"module Foo(T); end; Foo(Int32).class\", inject_primitives: true) { class_type }\n  end\n\n  it \"types metaclass superclass\" do\n    mod = semantic(<<-CRYSTAL).program\n      class Foo; end\n      class Bar < Foo; end\n      CRYSTAL\n\n    bar_class = mod.types[\"Bar\"].metaclass.as(MetaclassType)\n    bar_class.superclass.should eq(mod.types[\"Foo\"].metaclass)\n  end\n\n  it \"types generic metaclass superclass\" do\n    mod = semantic(<<-CRYSTAL).program\n      class Foo(T); end\n      class Bar(T) < Foo(T); end\n      CRYSTAL\n\n    foo_class = mod.types[\"Foo\"].metaclass.as(MetaclassType)\n    foo_class.superclass.should eq(mod.program.reference.metaclass)\n\n    bar = mod.types[\"Bar\"].as(GenericClassType)\n    bar_class = bar.metaclass.as(MetaclassType)\n    bar_class.superclass.should eq(mod.generic_class(\"Foo\", bar.type_parameter(\"T\")).metaclass)\n  end\n\n  it \"types generic instance metaclass superclass\" do\n    mod = semantic(<<-CRYSTAL).program\n      class Foo(T); end\n      class Bar(T) < Foo(T); end\n      CRYSTAL\n\n    foo_class = mod.generic_class(\"Foo\", mod.int32).metaclass.as(GenericClassInstanceMetaclassType)\n    foo_class.superclass.should eq(mod.program.reference.metaclass)\n\n    bar_class = mod.generic_class(\"Bar\", mod.int32).metaclass.as(GenericClassInstanceMetaclassType)\n    bar_class.superclass.should eq(foo_class)\n  end\n\n  describe \"subtyping relations between metaclasses\" do\n    it \"non-generic classes\" do\n      mod = semantic(<<-CRYSTAL).program\n        class Foo; end\n        class Bar < Foo; end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n      bar_class.should be_a_proper_subtype_of(foo_class)\n    end\n\n    it \"virtual metaclass type with virtual type (#12628)\" do\n      mod = semantic(<<-CRYSTAL).program\n        class Base; end\n        class Impl < Base; end\n        CRYSTAL\n\n      base = mod.types[\"Base\"]\n      base.virtual_type!.metaclass.implements?(base).should be_false\n      base.virtual_type!.metaclass.implements?(base.metaclass).should be_true\n      base.virtual_type!.metaclass.implements?(base.metaclass.virtual_type!).should be_true\n    end\n\n    it \"generic classes (1)\" do\n      mod = semantic(<<-CRYSTAL).program\n        class Foo(T); end\n        class Bar < Foo(Int32); end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      foo_int32_class = mod.generic_class(\"Foo\", mod.int32).metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n\n      bar_class.should be_a_proper_subtype_of(foo_int32_class)\n      bar_class.should be_a_proper_subtype_of(foo_class)\n      foo_int32_class.should be_a_proper_subtype_of(foo_class)\n    end\n\n    it \"generic classes (2)\" do\n      mod = semantic(<<-CRYSTAL).program\n        class Foo; end\n        class Bar(T) < Foo; end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n      bar_int32_class = mod.generic_class(\"Bar\", mod.int32).metaclass\n\n      bar_int32_class.should be_a_proper_subtype_of(bar_class)\n      bar_int32_class.should be_a_proper_subtype_of(foo_class)\n      bar_class.should be_a_proper_subtype_of(foo_class)\n    end\n\n    it \"generic classes (3)\" do\n      mod = semantic(<<-CRYSTAL).program\n        class Foo(T); end\n        class Bar(T) < Foo(T); end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n      foo_int32_class = mod.generic_class(\"Foo\", mod.int32).metaclass\n      bar_int32_class = mod.generic_class(\"Bar\", mod.int32).metaclass\n\n      bar_int32_class.should be_a_proper_subtype_of(bar_class)\n      bar_int32_class.should be_a_proper_subtype_of(foo_int32_class)\n      bar_class.should be_a_proper_subtype_of(foo_class)\n      foo_int32_class.should be_a_proper_subtype_of(foo_class)\n      bar_int32_class.should be_a_proper_subtype_of(foo_class)\n    end\n\n    it \"non-generic modules\" do\n      mod = semantic(<<-CRYSTAL).program\n        module Foo; end\n        module Bar; include Foo; end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n      bar_class.implements?(foo_class).should be_false\n    end\n\n    it \"generic modules (1)\" do\n      mod = semantic(<<-CRYSTAL).program\n        module Foo(T); end\n        module Bar; include Foo(Int32); end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      foo_int32_class = mod.generic_module(\"Foo\", mod.int32).metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n\n      # only instantiations of generic module metaclasses are subtypes of the\n      # uninstantiated metaclasses\n      foo_int32_class.should be_a_proper_subtype_of(foo_class)\n\n      bar_class.implements?(foo_int32_class).should be_false\n      bar_class.implements?(foo_class).should be_false\n    end\n\n    it \"generic modules (2)\" do\n      mod = semantic(<<-CRYSTAL).program\n        module Foo; end\n        module Bar(T); include Foo; end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n      bar_int32_class = mod.generic_module(\"Bar\", mod.int32).metaclass\n\n      bar_int32_class.should be_a_proper_subtype_of(bar_class)\n\n      bar_int32_class.implements?(foo_class).should be_false\n      bar_class.implements?(foo_class).should be_false\n    end\n\n    it \"generic modules (3)\" do\n      mod = semantic(<<-CRYSTAL).program\n        module Foo(T); end\n        module Bar(T); include Foo(T); end\n        CRYSTAL\n\n      foo_class = mod.types[\"Foo\"].metaclass\n      bar_class = mod.types[\"Bar\"].metaclass\n      foo_int32_class = mod.generic_module(\"Foo\", mod.int32).metaclass\n      bar_int32_class = mod.generic_module(\"Bar\", mod.int32).metaclass\n\n      bar_int32_class.should be_a_proper_subtype_of(bar_class)\n      foo_int32_class.should be_a_proper_subtype_of(foo_class)\n\n      bar_int32_class.implements?(foo_int32_class).should be_false\n      bar_int32_class.implements?(foo_class).should be_false\n      bar_class.implements?(foo_class).should be_false\n    end\n  end\n\n  it \"can't reopen as struct\" do\n    assert_error <<-CRYSTAL, \"Bar is not a struct, it's a metaclass\"\n      class Foo\n      end\n\n      alias Bar = Foo.class\n\n      struct Bar\n      end\n      CRYSTAL\n  end\n\n  it \"can't reopen as module\" do\n    assert_error <<-CRYSTAL, \"Bar is not a module, it's a metaclass\"\n      class Foo\n      end\n\n      alias Bar = Foo.class\n\n      module Bar\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/method_missing_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: method_missing\" do\n  it \"does error in method_missing macro with virtual type\" do\n    assert_error <<-CRYSTAL, \"undefined method 'lala' for Baz\"\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        macro method_missing(call)\n          2\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      foo = Baz.new || Bar.new\n      foo.lala\n      CRYSTAL\n  end\n\n  it \"does error in method_missing if wrong number of params\" do\n    assert_error <<-CRYSTAL, \"wrong number of parameters for macro 'method_missing' (given 2, expected 1)\"\n      class Foo\n        macro method_missing(call, foo)\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"does method missing for generic type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        macro method_missing(call)\n          1\n        end\n      end\n\n      Foo(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"errors if method_missing expands to an incorrect method\" do\n    assert_error <<-CRYSTAL, \"wrong method_missing expansion\"\n      class Foo\n        macro method_missing(call)\n          def baz\n            1\n          end\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"errors if method_missing expands to multiple methods\" do\n    assert_error <<-CRYSTAL, \"wrong method_missing expansion\"\n      class Foo\n        macro method_missing(call)\n          def bar\n            1\n          end\n\n          def qux\n          end\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"finds method_missing with 'with ... yield'\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        macro method_missing(call)\n          1\n        end\n      end\n\n      def bar\n        foo = Foo.new\n        with foo yield\n      end\n\n      bar do\n        baz\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't look up method_missing in with_yield_scope if call has a receiver (#12097)\" do\n    assert_error(<<-CRYSTAL, \"undefined method 'bar' for Int32\")\n      class Foo\n        macro method_missing(method)\n          def {{method}}\n            1\n          end\n        end\n      end\n\n      def run\n        with Foo.new yield\n      end\n\n      run do\n        foo.bar\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/module_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: module\" do\n  it \"includes but not a module\" do\n    assert_error \"class Foo; end; class Bar; include Foo; end\",\n      \"Foo is not a module\"\n  end\n\n  it \"includes module in a class\" do\n    assert_type(\"module Foo; def foo; 1; end; end; class Bar; include Foo; end; Bar.new.foo\") { int32 }\n  end\n\n  it \"includes module in a module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        def foo\n          1\n        end\n      end\n\n      module Boo\n        include Moo\n      end\n\n      class Foo\n        include Boo\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"finds in module when included\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        class B\n          def foo; 1; end\n        end\n      end\n\n      include Moo\n\n      B.new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar\n        include Foo(Int)\n      end\n\n      Bar.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"includes generic module and errors in call\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Bar#foo' to be Int, not Float64\"\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar\n        include Foo(Int)\n      end\n\n      Bar.new.foo(1.5)\n      CRYSTAL\n  end\n\n  it \"includes module but not generic\" do\n    assert_error <<-CRYSTAL, \"Foo is not a generic type\"\n      module Foo\n      end\n\n      class Bar\n        include Foo(Int)\n      end\n      CRYSTAL\n  end\n\n  it \"includes module but wrong number of arguments\" do\n    assert_error <<-CRYSTAL, \"wrong number of type vars for Foo(T, U) (given 1, expected 2)\"\n      module Foo(T, U)\n      end\n\n      class Bar\n        include Foo(Int)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if including generic module and not specifying type vars\" do\n    assert_error <<-CRYSTAL, \"generic type arguments must be specified when including Foo(T)\"\n      module Foo(T)\n      end\n\n      class Bar\n        include Foo\n      end\n      CRYSTAL\n  end\n\n  it \"includes generic module explicitly\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar(U)\n        include Foo(U)\n      end\n\n      Bar(Int32).new.foo(1)\n      CRYSTAL\n  end\n\n  it \"includes generic module explicitly and errors\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Bar(Int32)#foo' to be Int32, not Float64\"\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar(U)\n        include Foo(U)\n      end\n\n      Bar(Int32).new.foo(1.5)\n      CRYSTAL\n  end\n\n  it \"reports can't use instance variables inside module\" do\n    assert_error \"def foo; @a = 1; end; foo\",\n      \"can't use instance variables at the top level\"\n  end\n\n  it \"works with int including enumerable\" do\n    assert_type(<<-CRYSTAL) { array_of(float64) }\n      require \"prelude\"\n\n      struct Int32\n        include Enumerable(Int32)\n\n        def each\n          yield self\n          yield self + 2\n        end\n      end\n\n      1.map { |x| x * 0.5 }\n      CRYSTAL\n  end\n\n  it \"works with range and map\" do\n    assert_type(<<-CRYSTAL) { array_of(float64) }\n      require \"prelude\"\n      (1..3).map { |x| x * 0.5 }\n      CRYSTAL\n  end\n\n  it \"declares module automatically if not previously declared when declaring a class\" do\n    assert_type(<<-CRYSTAL\n      class Foo::Bar\n      end\n      Foo\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"]\n      foo.module?.should be_true\n      foo.metaclass\n    end\n  end\n\n  it \"declares module automatically if not previously declared when declaring a module\" do\n    assert_type(<<-CRYSTAL\n      module Foo::Bar\n      end\n      Foo\n      CRYSTAL\n    ) do\n      foo = types[\"Foo\"]\n      foo.module?.should be_true\n      foo.metaclass\n    end\n  end\n\n  it \"errors when a generic instance type alias is used as namespace (#8512)\" do\n    assert_error <<-CRYSTAL, \"Foo(Int32) can't be used as a namespace\"\n      struct Foo(T)\n      end\n\n      alias Bar = Foo(Int32)\n\n      module Bar::X\n      end\n      CRYSTAL\n  end\n\n  it \"includes generic module with another generic type\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Baz\", int32).metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      class Baz(X)\n      end\n\n      class Bar(U)\n        include Foo(Baz(U))\n      end\n\n      Bar(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with self\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Bar\", int32).metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      Bar(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with self, and inherits it\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Bar\", int32).metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz < Bar(Int32)\n      end\n\n      Baz.new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check argument type, success)\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Bar\", int32) }\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      Bar(Int32).new.foo Bar(Int32).new\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check argument superclass type, success)\" do\n    assert_type(<<-CRYSTAL) { types[\"Baz\"] }\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz < Bar(Int32)\n      end\n\n      Bar(Int32).new.foo Baz.new\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check argument type, error)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Baz1#foo' to be Bar(Int32), not Baz2\"\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz1 < Bar(Int32)\n      end\n\n      class Baz2 < Bar(Float64)\n      end\n\n      Baz1.new.foo Baz2.new\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check argument superclass type, success)\" do\n    assert_type(<<-CRYSTAL) { types[\"Baz2\"] }\n      module Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz1 < Bar(Int32)\n      end\n\n      class Baz2 < Bar(Int32)\n      end\n\n      Baz1.new.foo Baz2.new\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check return type, success)\" do\n    assert_type(<<-CRYSTAL) { generic_class(\"Bar\", int32) }\n      module Foo(T)\n        def foo : T\n          Bar(Int32).new\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      Bar(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check return subclass type, success)\" do\n    assert_type(<<-CRYSTAL) { types[\"Baz\"] }\n      module Foo(T)\n        def foo : T\n          Baz.new\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz < Bar(Int32)\n      end\n\n      Bar(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check return type, error)\" do\n    assert_error <<-CRYSTAL, \"method Baz#foo must return Bar(Float64) but it is returning Bar(Int32)\"\n      module Foo(T)\n        def foo : T\n          Bar(Int32).new\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz < Bar(Float64)\n      end\n\n      Baz.new.foo\n      CRYSTAL\n  end\n\n  it \"includes generic module with self (check return subclass type, error)\" do\n    assert_error <<-CRYSTAL, \"method Baz1#foo must return Bar(Int32) but it is returning Baz2\"\n      module Foo(T)\n        def foo : T\n          Baz2.new\n        end\n      end\n\n      class Bar(U)\n        include Foo(self)\n      end\n\n      class Baz1 < Bar(Int32)\n      end\n\n      class Baz2 < Bar(Float64)\n      end\n\n      Baz1.new.foo\n      CRYSTAL\n  end\n\n  it \"includes module but can't access metaclass methods\" do\n    assert_error <<-CRYSTAL, \"undefined method 'foo'\"\n      module Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar\n        include Foo\n      end\n\n      Bar.foo\n      CRYSTAL\n  end\n\n  it \"extends a module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        extend Foo\n      end\n\n      Bar.foo\n      CRYSTAL\n  end\n\n  it \"extends self\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n        extend self\n\n        def foo\n          1\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"gives error when including self\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Foo\n        include self\n      end\n      CRYSTAL\n  end\n\n  it \"gives error with cyclic include\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Foo\n      end\n\n      module Bar\n        include Foo\n      end\n\n      module Foo\n        include Bar\n      end\n      CRYSTAL\n  end\n\n  it \"gives error when including self, generic module\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Foo(T)\n        include self\n      end\n      CRYSTAL\n  end\n\n  it \"gives error when including instantiation of self, generic module\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Foo(T)\n        include Foo(Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"gives error with cyclic include, generic module\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Foo(T)\n      end\n\n      module Bar(T)\n        include Foo(T)\n      end\n\n      module Foo(T)\n        include Bar(T)\n      end\n      CRYSTAL\n  end\n\n  it \"gives error with cyclic include between non-generic and generic module\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Foo\n      end\n\n      module Bar(T)\n        include Foo\n      end\n\n      module Foo\n        include Bar(Int32)\n      end\n      CRYSTAL\n  end\n\n  it \"gives error with cyclic include between non-generic and generic module (2)\" do\n    assert_error <<-CRYSTAL, \"cyclic include detected\"\n      module Bar(T)\n      end\n\n      module Foo\n        include Bar(Int32)\n      end\n\n      module Bar(T)\n        include Foo\n      end\n      CRYSTAL\n  end\n\n  it \"finds types close to included module\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].types[\"T\"].metaclass }\n      module Foo\n        class T\n        end\n\n        def foo\n          T\n        end\n      end\n\n      class Bar\n        class T\n        end\n\n        include Foo\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"finds nested type inside method in block inside module\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].types[\"Bar\"].metaclass }\n      def foo\n        yield\n      end\n\n      module Foo\n        class Bar; end\n\n        @@x : Bar.class\n        @@x = foo { Bar }\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"finds class method in block\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        yield\n      end\n\n      module Foo\n        def self.bar\n          1\n        end\n\n        @@x : Int32\n        @@x = foo { bar }\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"types pointer of module\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Moo\"] }\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo.new\n      p.value\n      CRYSTAL\n  end\n\n  it \"types pointer of module with method\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"types pointer of module with method with two including types\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      module Moo\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        include Moo\n\n        def foo\n          'a'\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo.new\n      p.value = Bar.new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"types pointer of module with generic type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n      end\n\n      class Foo(T)\n        include Moo\n\n        def foo\n          1\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo(Int32).new\n      p.value.foo\n      CRYSTAL\n  end\n\n  it \"types pointer of module with generic type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      module Moo\n      end\n\n      class Bar\n        def self.boo\n          1\n        end\n      end\n\n      class Baz\n        def self.boo\n          'a'\n        end\n      end\n\n      class Foo(T)\n        include Moo\n\n        def foo\n          T.boo\n        end\n      end\n\n      p = Pointer(Moo).malloc(1_u64)\n      p.value = Foo(Bar).new\n      x = p.value.foo\n\n      p.value = Foo(Baz).new\n\n      x\n      CRYSTAL\n  end\n\n  it \"allows overloading with included generic module\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, string) }\n      module Foo(T)\n        def foo(x : T)\n          bar(x)\n        end\n      end\n\n      class Bar\n        include Foo(Int32)\n        include Foo(String)\n\n        def bar(x : Int32)\n          1\n        end\n\n        def bar(x : String)\n          \"a\"\n        end\n      end\n\n      Bar.new.foo(1 || \"hello\")\n      CRYSTAL\n  end\n\n  it \"finds constant in generic module included in another module\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      module Bar(T)\n        include Foo(T)\n      end\n\n      class Baz\n        include Bar(Int32)\n      end\n\n      Baz.new.foo\n      CRYSTAL\n  end\n\n  it \"calls super on included generic module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo(T)\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        include Foo(Int32)\n\n        def foo\n          super\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"calls super on included generic module and finds type var\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      class Bar(T)\n        include Foo(T)\n\n        def foo\n          super\n        end\n      end\n\n      Bar(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"calls super on included generic module and finds type var (2)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      module Bar(T)\n        include Foo(T)\n\n        def foo\n          super\n        end\n      end\n\n      class Baz(T)\n        include Bar(T)\n      end\n\n      Baz(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"types union of module and class that includes it\" do\n    assert_type(<<-CRYSTAL) { union_of(types[\"Bar\"].metaclass, types[\"Moo\"].metaclass) }\n      module Moo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar\n        include Moo\n\n        def self.foo\n          2\n        end\n      end\n\n      Bar || Moo\n      CRYSTAL\n  end\n\n  it \"works ok in a case where a typed-def type has an underlying type that has an included generic module (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibC\n        type X = Void*\n        fun x : X\n      end\n\n      module Mod(T)\n        def bar(other : T)\n          1\n        end\n      end\n\n      struct Pointer\n        include Mod(self)\n\n        def foo\n          address\n        end\n      end\n\n      LibC.x.foo\n\n      p = Pointer(Void).malloc(1_u64)\n      p.bar(p)\n      CRYSTAL\n  end\n\n  it \"finds inner class from inherited one (#476)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].types[\"Bar\"].types[\"Baz\"].metaclass }\n      class Foo\n        class Bar\n          class Baz\n          end\n        end\n      end\n\n      class Quz < Foo\n      end\n\n      Quz::Bar::Baz\n      CRYSTAL\n  end\n\n  it \"type def does not reopen type from parent namespace (#11181)\" do\n    assert_type <<-CRYSTAL, inject_primitives: false { types[\"Baz\"].types[\"Foo\"].types[\"Bar\"].metaclass }\n      module Foo::Bar\n      end\n\n      module Baz\n        module Foo::Bar\n        end\n      end\n\n      Baz::Foo::Bar\n      CRYSTAL\n  end\n\n  it \"correctly types type var in included module, with a restriction with a free var (bug)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Moo(T)\n      end\n\n      class Foo(T)\n        include Moo(T)\n\n        def foo(x : Moo(U)) forall U\n          T\n        end\n      end\n\n      Foo(Int32).new.foo(Foo(Char).new)\n      CRYSTAL\n  end\n\n  it \"types proc of module after type changes\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n      end\n\n      class Foo(T)\n        include Moo\n\n        def foo\n          3\n        end\n      end\n\n      z = ->(x : Moo) { x.foo }\n      z.call(Foo(Int32).new)\n      CRYSTAL\n  end\n\n  it \"types proc of module with generic class\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { char }\n      module Moo\n      end\n\n      class Foo(T)\n        include Moo\n\n        def foo\n          'a'\n        end\n      end\n\n      z = ->(x : Moo) { x.foo }\n      z.call(Foo(Int32).new)\n      CRYSTAL\n  end\n\n  it \"errors if declares module inside if\" do\n    assert_error <<-CRYSTAL, \"can't declare module dynamically\"\n      if 1 == 2\n        module Foo; end\n      end\n      CRYSTAL\n  end\n\n  it \"can't reopen as class\" do\n    assert_error <<-CRYSTAL, \"Foo is not a class, it's a module\"\n      module Foo\n      end\n\n      class Foo\n      end\n      CRYSTAL\n  end\n\n  it \"can't reopen as struct\" do\n    assert_error <<-CRYSTAL, \"Foo is not a struct, it's a module\"\n      module Foo\n      end\n\n      struct Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening non-generic module as generic\" do\n    assert_error <<-CRYSTAL, \"Foo is not a generic module\"\n      module Foo\n      end\n\n      module Foo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic module with different type vars\" do\n    assert_error <<-CRYSTAL, \"type var must be T, not U\"\n      module Foo(T)\n      end\n\n      module Foo(U)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic module with different type vars (2)\" do\n    assert_error <<-CRYSTAL, \"type vars must be A, B, not C\"\n      module Foo(A, B)\n      end\n\n      module Foo(C)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic module with different splat index\" do\n    assert_error <<-CRYSTAL, \"type var must be A, not *A\"\n      module Foo(A)\n      end\n\n      module Foo(*A)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic module with different splat index (2)\" do\n    assert_error <<-CRYSTAL, \"type var must be *A, not A\"\n      module Foo(*A)\n      end\n\n      module Foo(A)\n      end\n      CRYSTAL\n  end\n\n  it \"errors if reopening generic module with different splat index (3)\" do\n    assert_error <<-CRYSTAL, \"type vars must be *A, B, not A, *B\"\n      module Foo(*A, B)\n      end\n\n      module Foo(A, *B)\n      end\n      CRYSTAL\n  end\n\n  it \"uses :Module name for modules in errors\" do\n    assert_error <<-CRYSTAL, \"undefined method 'new' for Moo:Module\"\n      module Moo; end\n\n      Moo.new\n      CRYSTAL\n  end\n\n  it \"gives error when trying to instantiate with new\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'allocate' for Moo:Module (modules cannot be instantiated)\"\n      module Moo\n        def initialize\n        end\n      end\n      Moo.new\n      CRYSTAL\n  end\n\n  it \"gives error when trying to instantiate with allocate\" do\n    assert_error <<-CRYSTAL, \"undefined method 'allocate' for Moo:Module (modules cannot be instantiated)\"\n      module Moo\n        def initialize\n        end\n      end\n      Moo.allocate\n      CRYSTAL\n  end\n\n  it \"uses type declaration inside module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x : Int32\n\n        def initialize\n          @x = 1\n        end\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"uses type declaration inside module and gives error\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32, not Bool\"\n      module Moo\n        @x : Int32\n\n        def initialize(@x)\n        end\n\n        def moo\n          @x = false\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def initialize\n          super(1)\n          @x = 1\n        end\n      end\n\n      Foo.new.moo\n      CRYSTAL\n  end\n\n  it \"uses type declaration inside module, recursive, and gives error\" do\n    assert_error <<-CRYSTAL, \"instance variable '@x' of Foo must be Int32\"\n      module Moo\n        @x : Int32\n\n        def initialize(@x)\n        end\n\n        def moo\n          @x = false\n        end\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      class Foo\n        include Moo2\n\n        def initialize\n          super(1)\n          @x = 1\n        end\n      end\n\n      Foo.new.moo\n      CRYSTAL\n  end\n\n  it \"initializes variable in module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"initializes variable in module, recursive\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      module Moo2\n        include Moo\n      end\n\n      class Foo\n        include Moo2\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"inherits instance var type annotation from generic to concrete\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      module Foo(T)\n        @x : Int32?\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        include Foo(Int32)\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"inherits instance var type annotation from generic to concrete with T\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      module Foo(T)\n        @x : T?\n\n        def x\n          @x\n        end\n      end\n\n      class Bar\n        include Foo(Int32)\n      end\n\n      Bar.new.x\n      CRYSTAL\n  end\n\n  it \"inherits instance var type annotation from generic to generic to concrete\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      module Foo(T)\n        @x : Int32?\n\n        def x\n          @x\n        end\n      end\n\n      module Bar(T)\n        include Foo(T)\n      end\n\n      class Baz\n        include Bar(Int32)\n      end\n\n      Baz.new.x\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic module, accesses T from instance method\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      module Moo(*T)\n        def t\n          T\n        end\n      end\n\n      class Foo\n        include Moo(Int32, Char)\n      end\n\n      Foo.new.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic module, accesses T from class method\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      module Moo(*T)\n        def t\n          T\n        end\n      end\n\n      class Foo\n        extend Moo(Int32, Char)\n      end\n\n      Foo.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic module, accesses T from instance method through generic include\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      module Moo(*T)\n        def t\n          T\n        end\n      end\n\n      class Foo(*T)\n        include Moo(*T)\n      end\n\n      Foo(Int32, Char).new.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic module, accesses T from class method through generic extend\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      module Moo(*T)\n        def t\n          T\n        end\n      end\n\n      class Foo(*T)\n        extend Moo(*T)\n      end\n\n      Foo(Int32, Char).t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic module, accesses T from instance method, more args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([float64, char]).metaclass, string.metaclass]) }\n      module Moo(A, *T, B)\n        def t\n          {A, T, B}\n        end\n      end\n\n      class Foo\n        include Moo(Int32, Float64, Char, String)\n      end\n\n      Foo.new.t\n      CRYSTAL\n  end\n\n  it \"instantiates generic variadic module, accesses T from instance method through generic include, more args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([float64, char]).metaclass, string.metaclass]) }\n      module Moo(A, *T, B)\n        def t\n          {A, T, B}\n        end\n      end\n\n      class Foo(*T)\n        include Moo(Int32, *T, String)\n      end\n\n      Foo(Float64, Char).new.t\n      CRYSTAL\n  end\n\n  it \"includes module with Union(T*)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char).metaclass }\n      module Foo(U)\n        def u\n          U\n        end\n      end\n\n      struct Tuple\n        include Foo(Union(*T))\n      end\n\n      {1, 'a'}.u\n      CRYSTAL\n  end\n\n  it \"doesn't lookup type in ancestor when matches in current type (#2982)\" do\n    assert_error <<-CRYSTAL, \"undefined constant Qux::Bar\"\n      module Foo\n        module Qux\n          class Bar\n          end\n        end\n      end\n\n      class Qux\n      end\n\n      include Foo\n\n      Qux::Bar\n      CRYSTAL\n  end\n\n  it \"can restrict module with module (#3029)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n      end\n\n      class Gen(T)\n      end\n\n      def foo(x : Gen(Foo))\n        1\n      end\n\n      foo(Gen(Foo).new)\n      CRYSTAL\n  end\n\n  it \"can instantiate generic module\" do\n    assert_type(<<-CRYSTAL) { generic_module(\"Foo\", int32).metaclass }\n      module Foo(T)\n      end\n\n      Foo(Int32)\n      CRYSTAL\n  end\n\n  it \"can use generic module as instance variable type\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      module Moo(T)\n        def foo\n          1\n        end\n      end\n\n      class Foo\n        include Moo(Int32)\n      end\n\n      class Bar\n        include Moo(Int32)\n\n        def foo\n          'a'\n        end\n      end\n\n      class Mooer\n        def initialize(@moo : Moo(Int32))\n        end\n\n        def moo\n          @moo.foo\n        end\n      end\n\n      mooer = Mooer.new(Foo.new)\n      mooer.moo\n      CRYSTAL\n  end\n\n  it \"can use generic module as instance variable type (2)\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      module Moo(T)\n        def foo\n          1\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n\n      class Bar(T)\n        include Moo(T)\n\n        def foo\n          'a'\n        end\n      end\n\n      class Mooer\n        def initialize(@moo : Moo(Int32))\n        end\n\n        def moo\n          @moo.foo\n        end\n      end\n\n      mooer = Mooer.new(Foo(Int32).new)\n      mooer = Mooer.new(Bar(Int32).new)\n      mooer.moo\n      CRYSTAL\n  end\n\n  it \"errors when extending module that defines instance vars (#4065)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Bar extends it\"\n      module Foo\n        @x = 0\n      end\n\n      module Bar\n        extend Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors when extending module that defines instance vars (2) (#4065)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Bar extends it\"\n      module Foo\n        @x : Int32?\n      end\n\n      module Bar\n        extend Foo\n      end\n      CRYSTAL\n  end\n\n  it \"errors when extending generic module that defines instance vars\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo(T) because Bar(T) extends it\"\n      module Foo(T)\n        @x = 0\n      end\n\n      module Bar(T)\n        extend Foo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"errors when extending generic module that defines instance vars (2)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo(T) because Bar(T) extends it\"\n      module Foo(T)\n        @x : T?\n      end\n\n      module Bar(T)\n        extend Foo(T)\n      end\n      CRYSTAL\n  end\n\n  it \"errors when recursively extending module that defines instance vars\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Baz extends it\"\n      module Foo\n        @x = 0\n      end\n\n      module Bar\n        include Foo\n      end\n\n      module Baz\n        extend Bar\n      end\n      CRYSTAL\n  end\n\n  it \"errors when recursively extending module that defines instance vars (2)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Baz extends it\"\n      module Foo\n        @x : Int32?\n      end\n\n      module Bar\n        include Foo\n      end\n\n      module Baz\n        extend Bar\n      end\n      CRYSTAL\n  end\n\n  it \"errors when extending self and self defines instance vars (#9568)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Foo extends it\"\n      module Foo\n        extend self\n\n        @x = 0\n      end\n      CRYSTAL\n  end\n\n  it \"errors when extending self and self defines instance vars (2) (#9568)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Foo extends it\"\n      module Foo\n        extend self\n\n        @x : Int32?\n      end\n      CRYSTAL\n  end\n\n  it \"errors when extending self and self defines instance vars (3) (#9568)\" do\n    assert_error <<-CRYSTAL, \"can't declare instance variables in Foo because Foo extends it\"\n      module Foo\n        extend self\n\n        def initialize\n          @x = 0\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"can't pass module class to virtual metaclass (#6113)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Gen(Foo.class).foo' to be Foo.class, not Moo:Module\"\n      module Moo\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n        include Moo\n      end\n\n      class Gen(T)\n        def self.foo(x : T)\n        end\n      end\n\n      Gen(Foo.class).foo(Moo)\n      CRYSTAL\n  end\n\n  it \"extends module from generic class and calls class method (#7167)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Foo\n        def foo\n          1\n        end\n      end\n\n      class Gen(T)\n        extend Foo\n      end\n\n      Gen(Int32).foo\n      CRYSTAL\n  end\n\n  it \"extends generic module from generic class and calls class method (#7167)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      class Gen(U)\n        extend Foo(U)\n      end\n\n      Gen(Int32).foo\n      CRYSTAL\n  end\n\n  it \"extends generic module from generic module and calls class method (#7167)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      module Foo(T)\n        def foo\n          T\n        end\n      end\n\n      module Gen(U)\n        extend Foo(U)\n      end\n\n      Gen(Int32).foo\n      CRYSTAL\n  end\n\n  it \"doesn't look up initialize past module that defines initialize (#7007)\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments\"\n      module Moo\n        def initialize(x)\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"gives helpful error message when generic type var is missing\" do\n    assert_error <<-CRYSTAL, \"can't infer the type parameter T for the generic module Foo(T). Please provide it explicitly\"\n      module Foo(T)\n        def self.foo\n          T.bar\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"gives helpful error message when generic type var is missing in block spec\" do\n    assert_error <<-CRYSTAL, \"can't infer the type parameter T for the generic module Foo(T). Please provide it explicitly\"\n      module Foo(T)\n        def self.foo(&block : T -> )\n          block\n        end\n      end\n\n      Foo.foo { |x| }\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/multi_assign_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: multi assign\" do\n  context \"without strict_multi_assign\" do\n    it \"doesn't error if assigning tuple to fewer targets\" do\n      assert_type(<<-CRYSTAL) { tuple_of [int32, int32] }\n        require \"prelude\"\n\n        x = {1, 2, \"\"}\n        a, b = x\n        {a, b}\n        CRYSTAL\n    end\n\n    it \"doesn't error if assigning non-Indexable (#11414)\" do\n      assert_no_errors <<-CRYSTAL\n        class Foo\n          def [](index)\n          end\n\n          def size\n            3\n          end\n        end\n\n        a, b, c = Foo.new\n        CRYSTAL\n    end\n\n    it \"errors if assigning non-Indexable to splat (#11414)\" do\n      assert_error <<-CRYSTAL, \"right-hand side of one-to-many assignment must be an Indexable, not Foo\"\n        require \"prelude\"\n\n        class Foo\n          def [](index)\n          end\n\n          def size\n            3\n          end\n        end\n\n        a, *b, c = Foo.new\n        CRYSTAL\n    end\n  end\n\n  context \"strict_multi_assign\" do\n    it \"errors if assigning tuple to fewer targets\" do\n      assert_error <<-CRYSTAL, \"cannot assign Tuple(Int32, Int32, String) to 2 targets\", flags: \"strict_multi_assign\"\n        require \"prelude\"\n\n        x = {1, 2, \"\"}\n        a, b = x\n        CRYSTAL\n    end\n\n    pending \"errors if assigning tuple to more targets\" do\n      assert_error <<-CRYSTAL, \"cannot assign Tuple(Int32) to 2 targets\", flags: \"strict_multi_assign\"\n        require \"prelude\"\n\n        x = {1}\n        a, b = x\n        CRYSTAL\n    end\n\n    it \"errors if assigning union of tuples to fewer targets\" do\n      assert_error <<-CRYSTAL, \"cannot assign (Tuple(Int32, Int32, Int32) | Tuple(Int32, Int32, Int32, Int32)) to 2 targets\", flags: \"strict_multi_assign\"\n        require \"prelude\"\n\n        x = true ? {1, 2, 3} : {4, 5, 6, 7}\n        a, b = x\n        CRYSTAL\n    end\n\n    it \"doesn't error if some type in union matches target count\" do\n      assert_type(<<-CRYSTAL, flags: \"strict_multi_assign\") { tuple_of [int32, union_of(int32, string)] }\n        require \"prelude\"\n\n        x = true ? {1, \"\", 3} : {4, 5}\n        a, b = x\n        {a, b}\n        CRYSTAL\n    end\n\n    it \"doesn't error if some type in union has no constant size\" do\n      assert_type(<<-CRYSTAL, flags: \"strict_multi_assign\") { tuple_of [int32, union_of(int32, string)] }\n        require \"prelude\"\n\n        x = true ? {1, \"\", 3} : [4, 5]\n        a, b = x\n        {a, b}\n        CRYSTAL\n    end\n\n    it \"errors if assigning non-Indexable (#11414)\" do\n      assert_error <<-CRYSTAL, \"right-hand side of one-to-many assignment must be an Indexable, not Foo\", flags: \"strict_multi_assign\"\n        require \"prelude\"\n\n        class Foo\n          def [](index)\n          end\n\n          def size\n            3\n          end\n        end\n\n        a, b, c = Foo.new\n        CRYSTAL\n    end\n\n    it \"errors if assigning non-Indexable to splat (#11414)\" do\n      assert_error <<-CRYSTAL, \"right-hand side of one-to-many assignment must be an Indexable, not Foo\", flags: \"strict_multi_assign\"\n        require \"prelude\"\n\n        class Foo\n          def [](index)\n          end\n\n          def size\n            3\n          end\n        end\n\n        a, *b, c = Foo.new\n        CRYSTAL\n    end\n  end\n\n  it \"can pass splat variable at top-level to macros (#11596)\" do\n    assert_type(<<-CRYSTAL) { tuple_of [int32, int32, int32] }\n      struct Tuple\n        def self.new(*args)\n          args\n        end\n      end\n\n      macro foo(x)\n        {{ x }}\n      end\n\n      a, *b, c = 1, 2, 3, 4, 5\n      foo(b)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/named_args_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: named args\" do\n  it \"errors if named arg not found\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'w'\"\n      def foo(x, y = 1, z = 2)\n      end\n\n      foo 1, w: 3\n      CRYSTAL\n  end\n\n  it \"errors if named arg already specified\" do\n    assert_error <<-CRYSTAL, \"argument for parameter 'x' already specified\"\n      def foo(x, y = 1, z = 2)\n      end\n\n      foo 1, x: 1\n      CRYSTAL\n  end\n\n  it \"errors if named arg already specified, but multiple overloads (#7281)\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(x : String, y = 1, z = 2)\n      end\n\n      def foo(x : Int32, y : Int32)\n      end\n\n      foo 1, x: 1\n      CRYSTAL\n  end\n\n  it \"errors if named arg not found in new\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'w'\"\n      class Foo\n        def initialize(x, y = 1, z = 2)\n        end\n      end\n\n      Foo.new 1, w: 3\n      CRYSTAL\n  end\n\n  it \"errors if named arg already specified\" do\n    assert_error <<-CRYSTAL, \"argument for parameter 'x' already specified\"\n      class Foo\n        def initialize(x, y = 1, z = 2)\n        end\n      end\n\n      Foo.new 1, x: 1\n      CRYSTAL\n  end\n\n  it \"errors if doesn't pass named arg restriction\" do\n    assert_error <<-CRYSTAL, \"expected argument 'x' to 'foo' to be Int32, not Float64\"\n      def foo(x : Int32 = 1)\n      end\n\n      foo x: 1.5\n      CRYSTAL\n  end\n\n  it \"errors if named arg already specified but in same position\" do\n    assert_error <<-CRYSTAL, \"argument for parameter 'headers' already specified\"\n      def foo(headers = nil)\n      end\n\n      foo 1, headers: 2\n      CRYSTAL\n  end\n\n  it \"sends one regular argument as named argument\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x)\n        x\n      end\n\n      foo x: 1\n      CRYSTAL\n  end\n\n  it \"sends two regular arguments as named arguments\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(x, y)\n        x + y\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"sends two regular arguments as named arguments in inverted position (1)\" do\n    assert_type(<<-CRYSTAL) { string }\n      def foo(x, y)\n        x\n      end\n\n      foo y: 1, x: \"foo\"\n      CRYSTAL\n  end\n\n  it \"sends two regular arguments as named arguments in inverted position (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x, y)\n        y\n      end\n\n      foo y: 1, x: \"foo\"\n      CRYSTAL\n  end\n\n  it \"errors if named arg matches single splat argument\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'x'\"\n      def foo(*y)\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"errors if named arg matches splat argument\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(x, *y)\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"allows named arg if there's a splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, tuple_of([int32])]) }\n      def foo(*y, x)\n        { x, y }\n      end\n\n      foo 1, x: 'a'\n      CRYSTAL\n  end\n\n  it \"errors if missing one argument\" do\n    assert_error <<-CRYSTAL, \"missing argument: z\"\n      def foo(x, y, z)\n      end\n\n      foo x: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"errors if missing two arguments\" do\n    assert_error <<-CRYSTAL, \"missing arguments: x, z\"\n      def foo(x, y, z)\n      end\n\n      foo y: 2\n      CRYSTAL\n  end\n\n  it \"doesn't include arguments with default values in missing arguments error\" do\n    assert_error <<-CRYSTAL, \"missing argument: z\"\n\n      def foo(x, z, y = 1)\n      end\n\n      foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"says no overload matches with named arg\" do\n    assert_error <<-CRYSTAL, \"missing argument: y\"\n      def foo(x, y)\n      end\n\n      def foo(x, y, z)\n      end\n\n      foo(x: 2)\n      CRYSTAL\n  end\n\n  it \"gives correct error message for missing args after *\" do\n    assert_error <<-CRYSTAL, \"missing arguments: x, y\"\n      def foo(*, x, y)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"overloads based on required named args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo(x, *, y)\n        1\n      end\n\n      def foo(x, *, z)\n        'a'\n      end\n\n      a = foo(1, y: 2)\n      b = foo(1, z: 2)\n\n      {a, b}\n      CRYSTAL\n  end\n\n  it \"overloads based on required named args, with restrictions\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo(x, *, z : Int32)\n        1\n      end\n\n      def foo(x, *, z : Float64)\n        'a'\n      end\n\n      a = foo(1, z: 1)\n      b = foo(1, z: 1.5)\n\n      {a, b}\n      CRYSTAL\n  end\n\n  it \"uses bare splat in new\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize(*, y = nil)\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"passes #2696\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Bar\n        def bar\n          yield\n          self\n        end\n      end\n\n      module Foo\n        def self.foo(count = 5)\n          Bar.new\n        end\n      end\n\n      Foo.foo(count: 3).bar { }\n      CRYSTAL\n  end\n\n  it \"matches specific overload with named arguments (#2753)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { bool }\n      def foo(x : Nil, y)\n        foo 1, y\n        true\n      end\n\n      def foo(x, y)\n        x + 2\n        'a'\n      end\n\n      foo nil, y: 2\n      CRYSTAL\n  end\n\n  it \"matches specific overload with named arguments (2) (#2753)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { bool }\n      def foo(x : Nil, y, z)\n        foo 1, y, z\n        true\n      end\n\n      def foo(x, y, z)\n        x + 2\n        'a'\n      end\n\n      foo nil, z: 1, y: 2\n      CRYSTAL\n  end\n\n  it \"gives correct error message with external names (#3934)\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(*, arg a : String)\n        a\n      end\n\n      foo(arg: 10)\n      CRYSTAL\n  end\n\n  it \"says correct error when forwarding named args (#7491)\" do\n    assert_error <<-CRYSTAL, \"no parameter named 'baz'\"\n      def bar(foo = false)\n      end\n\n      bar(**{foo: true, baz: true})\n      CRYSTAL\n  end\n\n  it \"doesn't fail on named argument with NoReturn type (#7760)\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo(x : Int32)\n        'a'\n      end\n\n      x = 1\n      LibC.exit if x.is_a?(Int32)\n\n      foo(x: x)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/named_tuple_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: named tuples\" do\n  it \"types named tuple of one element\" do\n    assert_type(\"{x: 1}\") { named_tuple_of({\"x\": int32}) }\n  end\n\n  it \"types named tuple of two elements\" do\n    assert_type(\"{x: 1, y: 'a'}\") { named_tuple_of({\"x\": int32, \"y\": char}) }\n  end\n\n  it \"types named tuple of two elements, follows names order\" do\n    assert_type(\"{y: 'a', x: 1}\") { named_tuple_of({\"y\": char, \"x\": int32}) }\n  end\n\n  it \"types named tuple access (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      t = {x: 1, y: 'a'}\n      t[:x]\n      CRYSTAL\n  end\n\n  it \"types named tuple access (2)\" do\n    assert_type(<<-CRYSTAL) { char }\n      t = {x: 1, y: 'a'}\n      t[:y]\n      CRYSTAL\n  end\n\n  it \"types named tuple access (3)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      t = {x: 1, y: 'a'}\n      t[\"x\"]\n      CRYSTAL\n  end\n\n  it \"types named tuple access (4)\" do\n    assert_type(<<-CRYSTAL) { char }\n      t = {x: 1, y: 'a'}\n      t[\"y\"]\n      CRYSTAL\n  end\n\n  it \"types nilable named tuple access (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      t = {x: 1, y: 'a'}\n      t[:x]?\n      CRYSTAL\n  end\n\n  it \"types nilable named tuple access (2)\" do\n    assert_type(<<-CRYSTAL) { char }\n      t = {x: 1, y: 'a'}\n      t[:y]?\n      CRYSTAL\n  end\n\n  it \"types nilable named tuple access (3)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      t = {x: 1, y: 'a'}\n      t[:foo]?\n      CRYSTAL\n  end\n\n  it \"types nilable named tuple access (4)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      t = {x: 1, y: 'a'}\n      t[\"x\"]?\n      CRYSTAL\n  end\n\n  it \"types nilable named tuple access (5)\" do\n    assert_type(<<-CRYSTAL) { char }\n      t = {x: 1, y: 'a'}\n      t[\"y\"]?\n      CRYSTAL\n  end\n\n  it \"types nilable named tuple access (6)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      t = {x: 1, y: 'a'}\n      t[\"foo\"]?\n      CRYSTAL\n  end\n\n  it \"gives error when indexing with an unknown name\" do\n    assert_error \"{x: 1, y: 'a'}[:z]\",\n      \"missing key 'z' for named tuple NamedTuple(x: Int32, y: Char)\"\n  end\n\n  it \"can write generic type for NamedTuple\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": char}).metaclass }\n      NamedTuple(x: Int32, y: Char)\n      CRYSTAL\n  end\n\n  it \"gives error when using named args on a type other than NamedTuple\" do\n    assert_error <<-CRYSTAL, \"can only use named arguments with NamedTuple\"\n      class Foo(T)\n      end\n\n      Foo(x: Int32, y: Char)\n      CRYSTAL\n  end\n\n  it \"gives error when using positional args with NamedTuple\" do\n    assert_error <<-CRYSTAL, \"can only instantiate NamedTuple with named arguments\"\n      NamedTuple(Int32, Char)\n      CRYSTAL\n  end\n\n  it \"doesn't error if NamedTuple has no args\" do\n    assert_type(\"NamedTuple()\") { named_tuple_of({} of String => Type).metaclass }\n  end\n\n  it \"gets type at compile time\" do\n    assert_type(<<-CRYSTAL) { char.metaclass }\n      struct NamedTuple\n        def y\n          {{ T[:y] }}\n        end\n      end\n\n      {x: 10, y: 'a'}.y\n      CRYSTAL\n  end\n\n  it \"matches in type restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : {x: Int32, y: Char})\n        1\n      end\n\n      foo({x: 1, y: 'a'})\n      CRYSTAL\n  end\n\n  it \"matches in type restriction, different order (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : {y: Char, x: Int32})\n        1\n      end\n\n      foo({x: 1, y: 'a'})\n      CRYSTAL\n  end\n\n  it \"matches in type restriction, different order (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : {x: Int32, y: Char})\n        1\n      end\n\n      foo({y: 'a', x: 1})\n      CRYSTAL\n  end\n\n  it \"doesn't match in type restriction\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be NamedTuple(x: Int32, y: Int32), not NamedTuple(x: Int32, y: Char)\"\n      def foo(x : {x: Int32, y: Int32})\n        1\n      end\n\n      foo({x: 1, y: 'a'})\n      CRYSTAL\n  end\n\n  it \"doesn't match type restriction with instance\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo(NamedTuple(a: Int32)).foo' to be NamedTuple(a: Int32), not NamedTuple(a: Float64)\"\n      class Foo(T)\n        def self.foo(x : T)\n        end\n      end\n\n      Foo({a: Int32}).foo({a: 1.1})\n      CRYSTAL\n  end\n\n  it \"matches in type restriction and gets free var\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      def foo(x : {x: T, y: T}) forall T\n        T\n      end\n\n      foo({x: 1, y: 2})\n      CRYSTAL\n  end\n\n  it \"merges two named tuples with the same keys and types\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": char}) }\n      t1 = {x: 1, y: 'a'}\n      t2 = {y: 'a', x: 1}\n      t1 || t2\n      CRYSTAL\n  end\n\n  it \"can assign to union of compatible named tuple\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(named_tuple_of({\"x\": int32}), named_tuple_of({\"x\": int32, \"y\": string})) }\n      tup1 = {x: 1, y: \"foo\"}\n      tup2 = {x: 3}\n      tup3 = {y: \"bar\", x: 2}\n\n      ptr = Pointer(typeof(tup1, tup2, tup3)).malloc(1_u64)\n      ptr.value = tup3\n      ptr.value\n      CRYSTAL\n  end\n\n  it \"allows tuple covariance\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"foo\": types[\"Foo\"].virtual_type!}) }\n      class Obj\n        def initialize\n          @tuple = {foo: Foo.new}\n        end\n\n        def tuple=(@tuple)\n        end\n\n        def tuple\n          @tuple\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      obj = Obj.new\n      obj.tuple = {foo: Bar.new}\n      obj.tuple\n      CRYSTAL\n  end\n\n  it \"merges two named tuple with same keys but different types\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { named_tuple_of({\"x\": union_of(char, string), \"y\": nilable(int32)}) }\n      def foo\n        if 1 == 2\n          {x: \"foo\", y: 1}\n        else\n          {y: nil, x: 'a'}\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"accept named tuple in type restriction\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"foo\": types[\"Bar\"]}) }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : {foo: Foo})\n        x\n      end\n\n      foo({foo: Bar.new})\n      CRYSTAL\n  end\n\n  it \"accepts named tuple covariance in array\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": types[\"Foo\"].virtual_type!, \"y\": types[\"Foo\"].virtual_type!}) }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = [] of {x: Foo, y: Foo}\n      a << {x: Bar.new, y: Bar.new}\n      a[0]\n      CRYSTAL\n  end\n\n  it \"types T as a tuple of metaclasses\" do\n    assert_type(<<-CRYSTAL\n      struct NamedTuple\n        def named_args\n          T\n        end\n      end\n\n      x = {a: 1, b: 1.5, c: 'a'}\n      x.named_args\n      CRYSTAL\n    ) do\n      meta = named_tuple_of({\"a\": int32, \"b\": float64, \"c\": char}).metaclass\n      meta.metaclass?.should be_true\n      meta\n    end\n  end\n\n  it \"doesn't crash on named tuple in not executed block (#6718)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      require \"prelude\"\n\n      def pending(&block)\n      end\n\n      def untyped(x = nil)\n      end\n\n      # To reproduce this bug, it is needed to the expression that is\n      # not typed on main phase but is typed on cleanup phase.\n      # `untyped(untyped)` is just one.\n      pending do\n        {s: untyped(untyped)}\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't crash on named tuple type recursion (#7162)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def call(*args)\n        call({a: 1})\n        1\n      end\n\n      call(\"\")\n      CRYSTAL\n  end\n\n  it \"doesn't unify named tuple metaclasses (#5384)\" do\n    assert_type(<<-CRYSTAL\n      NamedTuple(a: Int32) || NamedTuple(a: String)\n      CRYSTAL\n    ) {\n      union_of(\n        named_tuple_of({\"a\": int32}).metaclass,\n        named_tuple_of({\"a\": string}).metaclass,\n      )\n    }\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/new_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: new\" do\n  it \"doesn't incorrectly redefines new for generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def self.new\n          1\n        end\n      end\n\n      Foo(Int32).new\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (1) (#731)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize(@x = self)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (2) (#731)\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo\n        def initialize(@x = self, @y = 'a')\n        end\n\n        def y\n          @y\n        end\n      end\n\n      Foo.new(y: 'b').y\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (3) (#731)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize(@x = bar)\n          yield 1, 2\n        end\n\n        def x\n          @x\n        end\n\n        def bar\n          1\n        end\n      end\n\n      foo = Foo.new do |x, y|\n      end\n      foo.x\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (4) (#731)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x : Int32\n\n        def initialize(@x = bar, &@block : ->)\n        end\n\n        def x\n          @x\n        end\n\n        def bar\n          1\n        end\n      end\n\n      foo = Foo.new do\n      end\n      foo.x\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (5) (#731)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo(R)\n        @x : Int32\n\n        def initialize(@x = bar, &@block : -> R)\n        end\n\n        def bar\n          10\n        end\n\n        def r\n          @block.call\n        end\n      end\n\n      Foo.new { 1 }.r\n      CRYSTAL\n  end\n\n  it \"evaluates initialize default value at the instance scope (6) (#731)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo(R)\n        @x : Int32\n\n        def initialize(@x = bar, &@block : -> R)\n        end\n\n        def bar\n          10\n        end\n\n        def r\n          @block.call\n        end\n      end\n\n      Foo(Int32).new { 1 }.r\n      CRYSTAL\n  end\n\n  it \"errors if using self call in default argument (1)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@caps' of My was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class My\n        @name : String\n        @caps : String\n\n        def initialize(@name, caps = self.name)\n          x = caps\n          @caps = x\n        end\n\n        def name\n          @name\n        end\n      end\n\n      My.new(\"foo\")\n      CRYSTAL\n  end\n\n  it \"errors if using self call in default argument (2)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@caps' of My was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class My\n        @name : String\n        @caps : String\n\n        def initialize(@name, caps = self.name)\n          @caps = caps\n        end\n\n        def name\n          @name\n        end\n      end\n\n      My.new(\"foo\")\n      CRYSTAL\n  end\n\n  it \"errors if using self call in default argument (3)\" do\n    assert_error <<-CRYSTAL, \"instance variable '@caps' of My was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class My\n        @name : String\n        @caps : String\n\n        def initialize(@name, @caps = self.name)\n        end\n\n        def name\n          @name\n        end\n      end\n\n      My.new(\"foo\")\n      CRYSTAL\n  end\n\n  it \"inherits initialize and new methods if doesn't define new (#3238)\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo(T)\n        def initialize(x : Int32)\n        end\n\n        def self.new(x : Char)\n          new(1)\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new('a')\n      CRYSTAL\n  end\n\n  it \"doesn't have default new for inherited class from generic type\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'Bar.new' (given 0, expected 1)\"\n      class Foo(T)\n        def initialize(x : Int32)\n        end\n      end\n\n      class Bar < Foo(Int32)\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"uses correct receiver for `initialize` in namespaced generic classes (#4086)\" do\n    assert_type <<-CRYSTAL { char }\n      class Foo\n        class Baz(T)\n        end\n\n        module Bar\n          class Baz(T) < Foo\n            def initialize(x)\n            end\n\n            def foo\n              'a'\n            end\n          end\n        end\n      end\n\n      Foo::Bar::Baz(Int32).new(1).foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/nil_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: nil\" do\n  it \"types empty\" do\n    assert_type(\"\") { nil_type }\n  end\n\n  it \"types nil\" do\n    assert_type(\"nil\") { nil_type }\n  end\n\n  it \"can call a fun with nil for pointer\" do\n    assert_type(\"lib LibA; fun a(c : Char*) : Int32; end; LibA.a(nil)\") { int32 }\n  end\n\n  it \"can call a fun with nil for typedef pointer\" do\n    assert_type(\"lib LibA; type Foo = Char*; fun a(c : Foo) : Int32; end; LibA.a(nil)\") { int32 }\n  end\n\n  it \"marks instance variables as nil but doesn't explode on macros\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      class Foo\n        getter :var\n\n        def initialize\n          @var = [1]\n          @var.last\n        end\n      end\n\n      f = Foo.new\n      f.var.last\n      CRYSTAL\n  end\n\n  it \"marks instance variables as nil when not in initialize\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      class Foo\n        def initialize\n          @foo = 1\n        end\n\n        def bar=(bar : Int32)\n          @bar = bar\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      f = Foo.new\n      f.bar = 1\n      f.bar\n      CRYSTAL\n  end\n\n  it \"marks instance variables as nil when not in initialize 2\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @foo = 1\n        end\n\n        def bar=(bar : Int32)\n          @bar = bar\n        end\n\n        def bar\n          @bar\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      f = Foo.new\n      f.bar = 1\n      f.foo\n      CRYSTAL\n  end\n\n  it \"restricts type of 'if foo'\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      f = nil || Foo.new\n      f ? f.bar : 10\n      CRYSTAL\n  end\n\n  it \"restricts type of 'if foo' on assign\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      if foo = (Foo.new || nil)\n        foo.bar\n      else\n        10\n      end\n      CRYSTAL\n  end\n\n  it \"restricts type of 'while foo'\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      foo = Foo.new || nil\n      while foo\n        foo.bar\n      end\n      1\n      CRYSTAL\n  end\n\n  it \"restricts type of 'while foo' on assign\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      while (foo = Foo.new || nil)\n        foo.bar\n      end\n      1\n      CRYSTAL\n  end\n\n  it \"doesn't check return type for nil\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo : Nil\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't check return type for void\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo : Void\n        1\n      end\n\n      foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/nilable_cast_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: nilable cast\" do\n  it \"types as?\" do\n    assert_type(<<-CRYSTAL) { nilable float64 }\n      1.as?(Float64)\n      CRYSTAL\n  end\n\n  it \"types as? with union\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      (1 || 'a').as?(Int32)\n      CRYSTAL\n  end\n\n  it \"types as? with nil\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      1.as?(Nil)\n      CRYSTAL\n  end\n\n  it \"types as? with NoReturn\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      1.as?(NoReturn)\n      CRYSTAL\n  end\n\n  it \"does upcast\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Foo\"].virtual_type! }\n      class Foo\n        def bar\n          1\n        end\n      end\n\n      class Bar < Foo\n        def bar\n          2\n        end\n      end\n\n      Bar.new.as?(Foo)\n      CRYSTAL\n  end\n\n  it \"doesn't crash with typeof no-type (#7441)\" do\n    assert_type(<<-CRYSTAL) { string }\n      a = 1\n      if a.is_a?(Char)\n        1.as?(typeof(a))\n      else\n        \"\"\n      end\n      CRYSTAL\n  end\n\n  it \"casts to module\" do\n    assert_type(<<-CRYSTAL) { union_of([types[\"Foo\"], types[\"Bar\"], nil_type] of Type) }\n      module Moo\n      end\n\n      class Base\n      end\n\n      class Foo < Base\n        include Moo\n      end\n\n      class Bar < Base\n        include Moo\n      end\n\n      base = (Foo.new || Bar.new)\n      base.as?(Moo)\n      CRYSTAL\n  end\n\n  it \"doesn't introduce type filter for nilable cast object (#12661)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, bool) }\n      val = 1 || false\n\n      if val.as?(Char)\n        true\n      else\n        val\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/nilable_instance_var_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: nilable instance var\" do\n  it \"says instance var was not initialized in all of the initialize methods\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't explicitly initialize instance variable '@foo' of Foo, rendering it nilable\"\n      class Foo\n        def initialize\n          @foo = 1\n        end\n\n        def initialize(other)\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo + 1\n      CRYSTAL\n  end\n\n  it \"says instance var was not initialized in all of the initialize methods (2)\" do\n    assert_error <<-CRYSTAL, \"can't infer the type of instance variable '@foo' of Foo\", inject_primitives: true\n      abstract class Foo\n        def foo\n          @foo\n        end\n      end\n\n      class Bar < Foo\n        def initialize\n          @foo = 1\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      p = Pointer(Foo).malloc(1_u64)\n      p.value = Bar.new\n      p.value = Baz.new\n      p.value.foo + 1\n      CRYSTAL\n  end\n\n  it \"says instance var was not initialized in all of the initialize methods, with var declaration\" do\n    assert_error <<-CRYSTAL, \"instance variable '@foo' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n      class Foo\n        @foo : Int32\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"says instance var was used before initialized\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@foo' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          @foo\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo + 1\n      CRYSTAL\n  end\n\n  it \"says instance var was used before initialized (2)\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@foo' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          foo\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo + 1\n      CRYSTAL\n  end\n\n  it \"says self was used before instance var was initialized\" do\n    assert_error <<-CRYSTAL, \"'self' was used before initializing instance variable '@foo', rendering it nilable\"\n      def baz(x)\n      end\n\n      class Foo\n        def initialize\n          baz(self)\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo + 1\n      CRYSTAL\n  end\n\n  it \"says self was used before instance var was initialized (2)\" do\n    assert_error <<-CRYSTAL, \"'self' was used before initializing instance variable '@foo', rendering it nilable\"\n      class Baz\n        def self.baz(x)\n        end\n      end\n\n      class Foo\n        def initialize\n          Baz.baz(self)\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo + 1\n      CRYSTAL\n  end\n\n  it \"says self was used before instance var was initialized (3)\" do\n    assert_error <<-CRYSTAL, \"'self' was used before initializing instance variable '@foo', rendering it nilable\"\n      class Foo\n        def initialize\n          a = self\n          @foo = 1\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo + 1\n      CRYSTAL\n  end\n\n  it \"finds type that doesn't initialize instance var (#1222)\" do\n    assert_error <<-CRYSTAL, \"this 'initialize' doesn't initialize instance variable '@x' of Base, with Unreferenced < Base, rendering it nilable\"\n      class Base\n        def initialize\n          @x = 0\n        end\n      end\n\n      class Unreferenced < Base\n        def initialize\n          # missing super\n        end\n      end\n\n      class Derived < Base\n        def initialize\n          super\n        end\n\n        def use_x\n          @x + 100\n        end\n      end\n\n      Derived.new.use_x\n      CRYSTAL\n  end\n\n  it \"doesn't consider as nil if initialized with catch-all\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Test\n        @a = 0\n\n        def initialize\n          @a + 1\n        end\n\n        def a\n          @a\n        end\n      end\n\n      Test.new.a\n      CRYSTAL\n  end\n\n  it \"marks instance var as nilable if assigned inside captured block (#1696)\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@foo' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      def capture(&block)\n        block\n      end\n\n      class Foo\n        def initialize\n          capture { @foo = 1 }\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"marks instance var as nilable if assigned inside proc literal\" do\n    assert_error <<-CRYSTAL, \"Instance variable '@foo' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      class Foo\n        def initialize\n          ->{ @foo = 1 }\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/no_return_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: NoReturn\" do\n  it \"types call to LibC.exit as NoReturn\" do\n    assert_type(\"lib LibC; fun exit : NoReturn; end; LibC.exit\") { no_return }\n  end\n\n  it \"types raise as NoReturn\" do\n    assert_type(\"require \\\"prelude\\\"; raise \\\"foo\\\"\") { no_return }\n  end\n\n  it \"types union of NoReturn and something else\" do\n    assert_type(\"lib LibC; fun exit : NoReturn; end; 1 == 1 ? LibC.exit : 1\", inject_primitives: true) { int32 }\n  end\n\n  it \"types union of NoReturns\" do\n    assert_type(\"lib LibC; fun exit : NoReturn; end; 1 == 2 ? LibC.exit : LibC.exit\", inject_primitives: true) { no_return }\n  end\n\n  it \"types with no return even if code follows\" do\n    assert_type(\"lib LibC; fun exit : NoReturn; end; LibC.exit; 1\") { no_return }\n  end\n\n  it \"assumes if condition's type filters when else is no return\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      foo = Foo.new || nil\n      LibC.exit unless foo\n\n      foo.foo\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if then (#314) (1)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, string) }\n      require \"prelude\"\n\n      a = 1\n      b = 1\n      x = nil\n\n      while a < 10\n        if a == 2\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        x = 1\n        a += 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if then (#314) (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      a = 1\n      b = 1\n      x = nil\n\n      while a < 10\n        if a == 2\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        a += 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if then (#314) (3)\" do\n    assert_type(<<-CRYSTAL) { nilable(string) }\n      require \"prelude\"\n\n      a = 1\n      c = nil\n      x = nil\n\n      while a < 10\n        if a == 2\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        if b\n          c = b\n        end\n\n        x = 1\n        a += 1\n      end\n\n      c\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if then (#314) (4)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      require \"prelude\"\n\n      a = 1\n      c = nil\n      x = nil\n\n      while a < 10\n        if a == 2\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        if b\n          c = b\n        end\n\n        a += 1\n      end\n\n      c\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if then (#314) (5)\" do\n    assert_error <<-CRYSTAL, \"undefined method 'size' for Nil\"\n      require \"prelude\"\n\n      a = 1\n      x = nil\n\n      while a < 10\n        if a == 1\n          x.not_nil!\n        else\n          b = \"hello\"\n        end\n\n        b.size\n\n        b = nil\n\n        x = 1\n        a += 1\n      end\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if else (#314) (1)\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, string) }\n      require \"prelude\"\n\n      a = 1\n      b = 1\n      x = nil\n\n      while a < 10\n        if a == 2\n        else\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        x = 1\n        a += 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if else (#314) (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      require \"prelude\"\n\n      a = 1\n      b = 1\n      x = nil\n\n      while a < 10\n        if a == 2\n        else\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        a += 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if else (#314) (3)\" do\n    assert_type(<<-CRYSTAL) { nilable(string) }\n      require \"prelude\"\n\n      a = 1\n      c = nil\n      x = nil\n\n      while a < 10\n        if a == 2\n        else\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        if b\n          c = b\n        end\n\n        x = 1\n        a += 1\n      end\n\n      c\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if else (#314) (4)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      require \"prelude\"\n\n      a = 1\n      c = nil\n      x = nil\n\n      while a < 10\n        if a == 2\n        else\n          b = \"hello\"\n          x.not_nil!\n        end\n\n        if b\n          c = b\n        end\n\n        a += 1\n      end\n\n      c\n      CRYSTAL\n  end\n\n  it \"computes NoReturn in a lazy way inside if else (#314) (5)\" do\n    assert_error <<-CRYSTAL, \"undefined method 'size' for Nil\"\n      require \"prelude\"\n\n      a = 1\n      x = nil\n\n      while a < 10\n        if a == 1\n          b = \"hello\"\n        else\n          x.not_nil!\n        end\n\n        b.size\n\n        b = nil\n\n        x = 1\n        a += 1\n      end\n      CRYSTAL\n  end\n\n  it \"types exception handler as NoReturn if ensure is NoReturn\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      lib LibC\n        fun foo : NoReturn\n      end\n\n      begin\n        1\n      ensure\n        LibC.foo\n      end\n      CRYSTAL\n  end\n\n  it \"types as NoReturn even if Nil return type is forced (#3096)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun exit(Int32) : NoReturn\n      end\n\n      def foo : Nil\n        LibC.exit(0)\n        yield\n      end\n\n      def bar(x)\n        x\n      end\n\n      def baz\n        foo { }\n        bar 0\n      end\n\n      baz\n      CRYSTAL\n  end\n\n  it \"types as NoReturn if typeof(exp)'s exp is NoReturn\" do\n    assert_type(<<-CRYSTAL) { no_return.metaclass }\n      require \"prelude\"\n\n      typeof(raise(\"\").foo)\n      CRYSTAL\n  end\n\n  it \"types as NoReturn if followed by one-to-many assignment (#15638)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      def foo(x)\n        {'a', \"\"}\n      end\n\n      def raise(msg)\n        while true\n        end\n      end\n\n      def bar\n        x = 1\n        return true if x.is_a?(Int32)\n        a, b = foo(x)\n      end\n\n      bar\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/not_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: not\" do\n  it \"types not\" do\n    assert_type(<<-CRYSTAL) { bool }\n      !1\n      CRYSTAL\n  end\n\n  it \"types not as NoReturn if exp is NoReturn\" do\n    assert_type(<<-CRYSTAL) { no_return }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      !LibC.exit\n      CRYSTAL\n  end\n\n  it \"filters types inside if\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      a = 1 || nil\n      z = nil\n      if !a\n        z = a\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"filters types inside if/else\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1 || nil\n      z = 2\n      if !a\n      else\n        z = a\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"filters types with !is_a?\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = 1 == 2 ? \"x\" : 1\n      z = 0\n      if !a.is_a?(String)\n        z = a + 10\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"doesn't restrict and\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      a = 1 || nil\n      z = nil\n      if !(a && (1 == 2))\n        z = a\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"doesn't restrict and in while (#4243)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      x = nil\n      y = nil\n      z = nil\n\n      while !(x && y)\n        z = x\n        x = 1\n      end\n\n      z\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/offsetof_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: offsetof\" do\n  it \"types offsetof\" do\n    assert_type(\"offsetof(String, @length)\") { int32 }\n    assert_type(\"offsetof({Int32, Int32}, 1)\") { int32 }\n  end\n\n  it \"can be used with generic types\" do\n    assert_type(\"struct Foo(T); @a : T = 0; end; offsetof(Foo(Int32), @a)\") { int32 }\n  end\n\n  it \"can be used with classes\" do\n    assert_type(\"class Foo; @a = 0; end; offsetof(Foo, @a)\") { int32 }\n  end\n\n  it \"errors on undefined instance variable\" do\n    assert_error \"struct Foo; @a = 0; end; offsetof(Foo, @b)\", \"type Foo doesn't have an instance variable called @b\"\n  end\n\n  it \"errors on typeof inside offsetof expression\" do\n    assert_error \"struct Foo; @a = 0; end; foo = Foo.new; offsetof(typeof(foo), @a)\", \"can't use typeof inside offsetof expression\"\n  end\n\n  it \"gives error if using offsetof on something that can't have instance variables\" do\n    assert_error \"offsetof(Int32, @foo)\", \"type Int32 can't have instance variables\"\n  end\n\n  it \"gives error if using offsetof on something that's neither a class, a struct nor a Tuple\" do\n    assert_error \"module Foo; @a = 0; end; offsetof(Foo, @a)\", \"Foo is neither a class, a struct nor a Tuple, it's a module\"\n  end\n\n  it \"errors on offsetof element of uninstantiated generic type\" do\n    assert_error \"struct Foo(T); @a = 0; end; offsetof(Foo, @a)\", \"can't take offsetof element @a of uninstantiated generic type Foo(T)\"\n  end\n\n  it \"gives error if using offsetof on Tuples with negative indexes\" do\n    assert_error \"offsetof({Int32,UInt8}, -3)\", \"can't take a negative offset of a tuple\"\n  end\n\n  it \"gives error if using offsetof on Tuples with indexes greater than tuple size\" do\n    assert_error \"offsetof({Int32,UInt8}, 2)\", \"can't take offset element at index 2 from a tuple with 2 elements\"\n    assert_error \"offsetof({Int32,UInt8}, 3)\", \"can't take offset element at index 3 from a tuple with 2 elements\"\n  end\n\n  it \"gives error if using offsetof on Tuples with instance variables\" do\n    assert_error \"offsetof({Int32,UInt8}, @a)\", \"can't take offset of a tuple element using an instance variable, use an index\"\n  end\n\n  it \"gives error if using offsetof on non-Tuples with an index\" do\n    assert_error \"class Foo; @a = 0; end; offsetof(Foo, 0)\", \"can't take offset element of Foo using an index, use an instance variable\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/pointer_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: pointer\" do\n  it \"types int pointer\" do\n    assert_type(\"a = 1; pointerof(a)\") { pointer_of(int32) }\n  end\n\n  it \"types pointer value\" do\n    assert_type(\"a = 1; b = pointerof(a); b.value\", inject_primitives: true) { int32 }\n  end\n\n  it \"types pointer add\" do\n    assert_type(\"a = 1; pointerof(a) + 1_i64\", inject_primitives: true) { pointer_of(int32) }\n  end\n\n  it \"types pointer diff\" do\n    assert_type(\"a = 1; b = 2; pointerof(a) - pointerof(b)\", inject_primitives: true) { int64 }\n  end\n\n  it \"types Pointer.malloc\" do\n    assert_type(\"p = Pointer(Int32).malloc(10_u64); p.value = 1; p\", inject_primitives: true) { pointer_of(int32) }\n  end\n\n  it \"types realloc\" do\n    assert_type(\"p = Pointer(Int32).malloc(10_u64); p.value = 1; x = p.realloc(20_u64); x\", inject_primitives: true) { pointer_of(int32) }\n  end\n\n  it \"type pointer casting\" do\n    assert_type(\"a = 1; pointerof(a).as(Char*)\") { pointer_of(char) }\n  end\n\n  it \"type pointer casting of object type\" do\n    assert_type(\"a = 1; pointerof(a).as(String)\") { string }\n  end\n\n  it \"pointer malloc creates new type\" do\n    assert_type(\"p = Pointer(Int32).malloc(1_u64); p.value = 1; p2 = Pointer(Float64).malloc(1_u64); p2.value = 1.5; p2.value\", inject_primitives: true) { float64 }\n  end\n\n  pending \"allows using pointer with subclass\" do\n    assert_type(<<-CRYSTAL) { union_of(object.virtual_type, int32) }\n      a = Pointer(Object).malloc(1_u64)\n      a.value = 1\n      a.value\n      CRYSTAL\n  end\n\n  it \"can't do Pointer.malloc without type var\" do\n    assert_error <<-CRYSTAL, \"can't malloc pointer without type, use Pointer(Type).malloc(size)\", inject_primitives: true\n      Pointer.malloc(1_u64)\n      CRYSTAL\n  end\n\n  it \"create pointer by address\" do\n    assert_type(\"Pointer(Int32).new(123_u64)\", inject_primitives: true) { pointer_of(int32) }\n  end\n\n  it \"types pointer of constant\" do\n    assert_type(<<-CRYSTAL) { pointer_of(int32) }\n      FOO = 1\n      pointerof(FOO)\n      CRYSTAL\n  end\n\n  it \"pointer of class raises error\" do\n    assert_error \"pointerof(Int32)\", \"can't take address of Int32 because it's a Path. `pointerof` expects a variable or constant.\"\n  end\n\n  it \"pointer of class raises error\" do\n    assert_error \"def foo; foo; end; pointerof(foo)\", \"can't take address of foo because it's a Call. `pointerof` expects a variable or constant.\"\n  end\n\n  it \"pointer of value error\" do\n    assert_error \"pointerof(1)\", \"can't take address of 1 because it's a NumberLiteral. `pointerof` expects a variable or constant.\"\n  end\n\n  it \"types pointer value on typedef\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibC\n        type Foo = Int32*\n        fun foo : Foo\n      end\n\n      LibC.foo.value\n      CRYSTAL\n  end\n\n  it \"detects recursive pointerof expansion (#551) (#553)\" do\n    assert_error <<-CRYSTAL, \"recursive pointerof expansion\"\n      x = 1\n      x = pointerof(x)\n      CRYSTAL\n  end\n\n  it \"detects recursive pointerof expansion (2) (#1654)\" do\n    assert_error <<-CRYSTAL, \"recursive pointerof expansion\"\n      x = 1\n      pointer = pointerof(x)\n      x = pointerof(pointer)\n      CRYSTAL\n  end\n\n  it \"detects recursive pointerof expansion (3)\" do\n    assert_error <<-CRYSTAL, \"recursive pointerof expansion\"\n      x = {1}\n      x = pointerof(x)\n      CRYSTAL\n  end\n\n  it \"detects recursive pointerof expansion (4)\" do\n    assert_error <<-CRYSTAL, \"recursive pointerof expansion\"\n      x = 1\n      x = {pointerof(x)}\n      CRYSTAL\n  end\n\n  it \"doesn't crash if pointerof expansion type has generic splat parameter (#11808)\" do\n    assert_type(<<-CRYSTAL) { pointer_of(union_of int32, generic_class(\"Foo\", string)) }\n      class Foo(*T)\n      end\n\n      x = 1\n      pointer = pointerof(x)\n      x = Foo(String).new\n      pointer\n      CRYSTAL\n  end\n\n  it \"can assign nil to void pointer\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nil_type }\n      ptr = Pointer(Void).malloc(1_u64)\n      ptr.value = ptr.value\n      CRYSTAL\n  end\n\n  it \"can pass any pointer to something expecting void* in lib call\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibFoo\n        fun foo(x : Void*) : Float64\n      end\n\n      LibFoo.foo(Pointer(Int32).malloc(1_u64))\n      CRYSTAL\n  end\n\n  it \"can pass any pointer to something expecting void* in lib call, with to_unsafe\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibFoo\n        fun foo(x : Void*) : Float64\n      end\n\n      class Foo\n        def to_unsafe\n          Pointer(Int32).malloc(1_u64)\n        end\n      end\n\n      LibFoo.foo(Foo.new)\n      CRYSTAL\n  end\n\n  it \"errors if doing Pointer.allocate\" do\n    assert_error <<-CRYSTAL, \"can't create instance of a pointer type\"\n      Pointer(Int32).allocate\n      CRYSTAL\n  end\n\n  it \"takes pointerof lib external var\" do\n    assert_type(<<-CRYSTAL) { pointer_of(int32) }\n      lib LibFoo\n        $extern : Int32\n      end\n\n      pointerof(LibFoo.extern)\n      CRYSTAL\n  end\n\n  it \"says undefined variable (#7556)\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'foo'\"\n      pointerof(foo)\n      CRYSTAL\n  end\n\n  it \"can assign pointerof virtual type (#8216)\" do\n    assert_no_errors <<-CRYSTAL\n      class Base\n      end\n\n      class Sub < Base\n      end\n\n      u = uninitialized Base\n\n      x : Pointer(Base)\n      x = pointerof(u)\n      CRYSTAL\n  end\n\n  it \"errors with non-matching generic value with value= (#10211)\" do\n    assert_error <<-CRYSTAL, \"type must be Gen(Char | Int32), not Gen(Int32)\", inject_primitives: true\n      class Gen(T)\n      end\n\n      ptr = Pointer(Gen(Char | Int32)).malloc(1_u64)\n      ptr.value = Gen(Int32).new\n      CRYSTAL\n  end\n\n  it \"errors with non-matching generic value with value=, generic type (#10211)\" do\n    assert_error <<-CRYSTAL, \"type must be Moo(Char | Int32), not Foo(Int32)\", inject_primitives: true\n      module Moo(T)\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n\n      ptr = Pointer(Moo(Char | Int32)).malloc(1_u64)\n      ptr.value = Foo(Int32).new\n      CRYSTAL\n  end\n\n  it \"errors with non-matching generic value with value=, union of generic types (#10544)\" do\n    assert_error <<-CRYSTAL, \"type must be Foo(Char | Int32), not (Foo(Char | Int32) | Foo(Int32))\", inject_primitives: true\n      class Foo(T)\n      end\n\n      class Bar1\n      end\n\n      class Bar2\n      end\n\n      ptr = Pointer(Foo(Char | Int32)).malloc(1_u64)\n      ptr.value = Foo(Int32).new || Foo(Char | Int32).new\n      CRYSTAL\n  end\n\n  it \"does not recalculate element type on multiple calls to `#value=` (#15742)\" do\n    result = semantic <<-CRYSTAL\n      module Foo\n      end\n\n      class Bar1\n        include Foo\n      end\n\n      class Bar2\n        include Foo\n      end\n\n      struct Pointer(T)\n        @[Primitive(:pointer_set)]\n        def value=(value : T)\n        end\n      end\n\n      # NOTE: `typeof(v)` is `(Bar2 | Foo)*` but should most certainly be just `Foo*`\n      v = uninitialized Pointer(Bar1 | Bar2 | Foo)\n\n      a = uninitialized Bar1 | Bar2\n      v.value = a\n      b = uninitialized Foo\n      v.value = b\n\n      v\n      CRYSTAL\n\n    type = result.node.type.should be_a(PointerInstanceType)\n    type.element_type.should_not eq(result.program.types[\"Foo\"])\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/previous_def_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: previous_def\" do\n  it \"errors if there's no previous def\" do\n    assert_error <<-CRYSTAL, \"there is no previous definition of 'foo'\"\n      def foo\n        previous_def\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types previous def\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo\n        1\n      end\n\n      def foo\n        previous_def\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types previous def in generic class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        def foo\n          1\n        end\n\n        def foo\n          previous_def\n        end\n      end\n\n      Foo(Int32).new.foo\n      CRYSTAL\n  end\n\n  it \"types previous def with explicit arguments\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(x)\n        x\n      end\n\n      def foo(y)\n        previous_def(1.5)\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"types previous def with forwarded arguments, def has parameters\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x)\n        x\n      end\n\n      def foo(y)\n        previous_def\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"types previous def with forwarded arguments, def has bare splat parameter (#8895)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(*, x)\n        x\n      end\n\n      def foo(*, x)\n        previous_def\n      end\n\n      foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"types previous def with named arguments, def has bare splat parameter (#8895)\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      def foo(*, x)\n        x\n      end\n\n      def foo(*, x)\n        previous_def x: x || 'a'\n      end\n\n      foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"types previous def with named arguments, def has bare splat parameter (2) (#8895)\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      def foo(x)\n        x\n      end\n\n      def foo(x)\n        previous_def x: x || 'a'\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"types previous def with forwarded arguments, different internal names (#8895)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(*, x a)\n        a\n      end\n\n      def foo(*, x b)\n        previous_def\n      end\n\n      foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"types previous def with named arguments, def has double splat parameter (#8895)\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": char}) }\n      def foo(**opts)\n        opts\n      end\n\n      def foo(**opts)\n        previous_def\n      end\n\n      foo(x: 1, y: 'a')\n      CRYSTAL\n  end\n\n  it \"types previous def with restrictions\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : Int32)\n        x\n      end\n\n      def foo(y : Int32)\n        previous_def\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"types previous def when inside fun\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        1\n      end\n\n      def foo\n        x = ->{ previous_def }\n        x.call\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types previous def when inside fun and forwards args\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(z)\n        z\n      end\n\n      def foo(z)\n        x = ->{ previous_def }\n        x.call\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"says wrong number of arguments for previous_def (#1223)\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments\"\n      class Foo\n        def x\n        end\n\n        def x\n          previous_def(1)\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/primitives_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: primitives\" do\n  it \"types a bool\" do\n    assert_type(\"false\") { bool }\n  end\n\n  it \"types an int32\" do\n    assert_type(\"1\") { int32 }\n  end\n\n  it \"types a int64\" do\n    assert_type(\"1_i64\") { int64 }\n  end\n\n  it \"types a int128\" do\n    assert_type(\"1_i128\") { int128 }\n  end\n\n  it \"types a uint128\" do\n    assert_type(\"1_u128\") { uint128 }\n  end\n\n  it \"types a float32\" do\n    assert_type(\"2.3_f32\") { float32 }\n  end\n\n  it \"types a float64\" do\n    assert_type(\"2.3_f64\") { float64 }\n  end\n\n  it \"types a char\" do\n    assert_type(\"'a'\") { char }\n  end\n\n  it \"types char ord\" do\n    assert_type(\"'a'.ord\", inject_primitives: true) { int32 }\n  end\n\n  it \"types a symbol\" do\n    assert_type(\":foo\") { symbol }\n  end\n\n  it \"types a string\" do\n    assert_type(\"\\\"foo\\\"\") { string }\n  end\n\n  it \"types nil\" do\n    assert_type(\"nil\") { nil_type }\n  end\n\n  it \"types nop\" do\n    assert_type(\"\") { nil_type }\n  end\n\n  it \"types an expression\" do\n    assert_type(\"1; 'a'\") { char }\n  end\n\n  it \"types 1 + 2\" do\n    assert_type(\"1 + 2\", inject_primitives: true) { int32 }\n  end\n\n  it \"errors when comparing void (#225)\" do\n    assert_error <<-CRYSTAL, \"undefined method '==' for Nil\"\n      lib LibFoo\n        fun foo\n      end\n\n      LibFoo.foo == 1\n      CRYSTAL\n  end\n\n  it \"correctly types first hash from type vars (bug)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Hash\", int32, char }\n      class Hash(K, V)\n      end\n\n      def foo(x : K, y : V) forall K, V\n        {} of K => V\n      end\n\n      x = foo 1, 'a'\n      y = foo 'a', 1\n      x\n      CRYSTAL\n  end\n\n  it \"computes correct hash value type if it's a function literal (#320)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Hash\", string, proc_of(bool) }\n      require \"prelude\"\n\n      {\"foo\" => ->{ true }}\n      CRYSTAL\n  end\n\n  it \"extends from Number and doesn't find + method\" do\n    assert_error <<-CRYSTAL, \"undefined method\"\n      struct Foo < Number\n      end\n\n      Foo.new + 1\n      CRYSTAL\n  end\n\n  it \"extends from Number and doesn't find >= method\" do\n    assert_error <<-CRYSTAL, \"undefined method\"\n      struct Foo < Number\n      end\n\n      Foo.new >= 1\n      CRYSTAL\n  end\n\n  it \"extends from Number and doesn't find to_i method\" do\n    assert_error <<-CRYSTAL, \"undefined method\"\n      struct Foo < Number\n      end\n\n      Foo.new.to_i\n      CRYSTAL\n  end\n\n  pending \"types pointer of int\" do\n    assert_type(<<-CRYSTAL) { types[\"Int\"] }\n      p = Pointer(Int).malloc(1_u64)\n      p.value = 1\n      p.value\n      CRYSTAL\n  end\n\n  it \"can invoke cast on primitive typedef (#614)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib Test\n        type K = Int32\n        fun foo : K\n      end\n\n      Test.foo.to_i\n      CRYSTAL\n  end\n\n  it \"can invoke binary on primitive typedef (#614)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Test\"].types[\"K\"] }\n      lib Test\n        type K = Int32\n        fun foo : K\n      end\n\n      Test.foo + 1\n      CRYSTAL\n  end\n\n  it \"can invoke binary on primitive typedef (2) (#614)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Test\"].types[\"K\"] }\n      lib Test\n        type K = Int32\n        fun foo : K\n      end\n\n      Test.foo.unsafe_shl 1\n      CRYSTAL\n  end\n\n  it \"errors if using instance variable inside primitive type\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables inside primitive types (at Int32)\"\n      struct Int32\n        def meth\n          puts @value\n        end\n      end\n\n      1.meth\n      CRYSTAL\n  end\n\n  it \"types @[Primitive] method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Int32\n        @[Primitive(:binary)]\n        def +(other : Int32) : Int32\n        end\n      end\n\n      1 + 2\n      CRYSTAL\n  end\n\n  it \"errors if @[Primitive] has no args\" do\n    assert_error <<-CRYSTAL, \"expected Primitive annotation to have one argument\"\n      struct Int32\n        @[Primitive]\n        def +(other : Int32) : Int32\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"errors if @[Primitive] has non-symbol arg\" do\n    assert_error <<-CRYSTAL, \"expected Primitive argument to be a symbol literal\"\n      struct Int32\n        @[Primitive(\"foo\")]\n        def +(other : Int32) : Int32\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"allows @[Primitive] on method that has body\" do\n    assert_no_errors <<-CRYSTAL\n      struct Int32\n        @[Primitive(:binary)]\n        def +(other : Int32) : Int32\n          1\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"types va_arg primitive\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct VaList\n        @[Primitive(:va_arg)]\n        def next(type)\n        end\n      end\n\n      list = VaList.new\n      list.next(Int32)\n      CRYSTAL\n  end\n\n  it \"looks up return type in correct scope (#13652)\" do\n    assert_type(<<-CRYSTAL) { types[\"A\"] }\n      class A\n      end\n\n      class Foo\n        @[Primitive(:foo)]\n        def foo : A\n        end\n      end\n\n      class Bar::A < Foo\n      end\n\n      Bar::A.new.foo\n      CRYSTAL\n  end\n\n  describe \"Slice.literal\" do\n    def_slice_literal = <<-CRYSTAL\n      struct Slice(T)\n        def initialize(pointer : T*, size : Int32, *, read_only : Bool)\n        end\n\n        @[Primitive(:slice_literal)]\n        def self.literal(*args)\n        end\n      end\n      CRYSTAL\n\n    context \"without element type\" do\n      it \"types primitive int literal\" do\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", int32 }\n          #{def_slice_literal}\n          Slice.literal(0, 1, 4, 9)\n          CRYSTAL\n\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", uint8 }\n          #{def_slice_literal}\n          Slice.literal(1_u8, 2_u8)\n          CRYSTAL\n      end\n\n      it \"types primitive float literal\" do\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", float64 }\n          #{def_slice_literal}\n          Slice.literal(1.2, 3.4)\n          CRYSTAL\n\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", float32 }\n          #{def_slice_literal}\n          Slice.literal(5.6_f32)\n          CRYSTAL\n      end\n\n      it \"errors if empty\" do\n        assert_error <<-CRYSTAL, \"Cannot create empty slice literal without element type\"\n          #{def_slice_literal}\n          Slice.literal\n          CRYSTAL\n      end\n\n      it \"errors if multiple element types are found\" do\n        assert_error <<-CRYSTAL, \"Too many element types for slice literal without generic argument: Int32, UInt8\"\n          #{def_slice_literal}\n          Slice.literal(1, 2_u8)\n          CRYSTAL\n\n        assert_error <<-CRYSTAL, \"Too many element types for slice literal without generic argument: Float32, Float64\"\n          #{def_slice_literal}\n          Slice.literal(3.0f32, 4.0)\n          CRYSTAL\n      end\n\n      it \"errors if element is not number literal\" do\n        assert_error <<-CRYSTAL, \"Expected NumberLiteral, got StringLiteral\"\n          #{def_slice_literal}\n          Slice.literal(\"\")\n          CRYSTAL\n\n        assert_error <<-CRYSTAL, \"Expected NumberLiteral, got Var\"\n          #{def_slice_literal}\n          x = 1\n          Slice.literal(x)\n          CRYSTAL\n      end\n    end\n\n    context \"with element type\" do\n      it \"types primitive int literal\" do\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", uint8 }\n          #{def_slice_literal}\n          Slice(UInt8).literal(0, 1, 4, 9)\n          CRYSTAL\n      end\n\n      it \"types primitive float literal\" do\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", float64 }\n          #{def_slice_literal}\n          Slice(Float64).literal(0, 1, 4, 9)\n          CRYSTAL\n      end\n\n      it \"types empty literal\" do\n        assert_type(<<-CRYSTAL) { generic_class \"Slice\", int32 }\n          #{def_slice_literal}\n          Slice(Int32).literal\n          CRYSTAL\n      end\n\n      it \"errors if element type is not primitive int or float\" do\n        assert_error <<-CRYSTAL, \"Only slice literals of primitive integer or float types can be created\"\n          #{def_slice_literal}\n          Slice(String).literal\n          CRYSTAL\n\n        assert_error <<-CRYSTAL, \"Only slice literals of primitive integer or float types can be created\"\n          #{def_slice_literal}\n          Slice(Bool).literal\n          CRYSTAL\n\n        assert_error <<-CRYSTAL, \"Only slice literals of primitive integer or float types can be created\"\n          #{def_slice_literal}\n          Slice(Int32 | Int64).literal\n          CRYSTAL\n      end\n\n      it \"errors if element is not number literal\" do\n        assert_error <<-CRYSTAL, \"Expected NumberLiteral, got StringLiteral\"\n          #{def_slice_literal}\n          Slice(Int32).literal(\"\")\n          CRYSTAL\n\n        assert_error <<-CRYSTAL, \"Expected NumberLiteral, got Var\"\n          #{def_slice_literal}\n          x = 1\n          Slice(Int32).literal(x)\n          CRYSTAL\n      end\n\n      it \"errors if element is out of range\" do\n        assert_error <<-CRYSTAL, \"Argument out of range for a Slice(UInt8)\"\n          #{def_slice_literal}\n          Slice(UInt8).literal(-1)\n          CRYSTAL\n\n        assert_error <<-CRYSTAL, \"Argument out of range for a Slice(UInt8)\"\n          #{def_slice_literal}\n          Slice(UInt8).literal(256)\n          CRYSTAL\n      end\n    end\n  end\n\n  describe \"Reference.pre_initialize\" do\n    def_reference_pre_initialize = <<-CRYSTAL\n      class Reference\n        @[Primitive(:pre_initialize)]\n        def self.pre_initialize(address : Pointer)\n          {% @type %}\n        end\n      end\n      CRYSTAL\n\n    it \"types with reference type\" do\n      assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n        #{def_reference_pre_initialize}\n\n        class Foo\n        end\n\n        x = 1\n        Foo.pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n\n    it \"types with virtual reference type\" do\n      assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n        #{def_reference_pre_initialize}\n\n        class Foo\n        end\n\n        class Bar < Foo\n        end\n\n        x = 1\n        Bar.as(Foo.class).pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n\n    it \"errors on uninstantiated generic type\" do\n      assert_error <<-CRYSTAL, \"Can't pre-initialize instance of generic class Foo(T) without specifying its type vars\"\n        #{def_reference_pre_initialize}\n\n        class Foo(T)\n        end\n\n        x = 1\n        Foo.pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n\n    it \"errors on abstract type\" do\n      assert_error <<-CRYSTAL, \"Can't pre-initialize abstract class Foo\"\n        #{def_reference_pre_initialize}\n\n        abstract class Foo\n        end\n\n        x = 1\n        Foo.pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n  end\n\n  describe \"Struct.pre_initialize\" do\n    def_struct_pre_initialize = <<-CRYSTAL\n      struct Struct\n        @[Primitive(:pre_initialize)]\n        def self.pre_initialize(address : Pointer) : Nil\n          {% @type %}\n        end\n      end\n      CRYSTAL\n\n    it \"errors on abstract type\" do\n      assert_error <<-CRYSTAL, \"Can't pre-initialize abstract struct Foo\"\n        #{def_struct_pre_initialize}\n\n        abstract struct Foo\n        end\n\n        x = 1\n        Foo.pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n\n    it \"errors on virtual abstract type\" do\n      assert_error <<-CRYSTAL, \"Can't pre-initialize abstract struct Foo\"\n        #{def_struct_pre_initialize}\n\n        abstract struct Foo\n        end\n\n        struct Bar < Foo\n        end\n\n        x = 1\n        Bar.as(Foo.class).pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n\n    it \"errors on abstract pointee type\" do\n      assert_error <<-CRYSTAL, \"Can't pre-initialize struct using pointer to abstract struct\"\n        #{def_struct_pre_initialize}\n\n        abstract struct Foo\n        end\n\n        struct Bar < Foo\n        end\n\n        x = uninitialized Foo\n        Bar.pre_initialize(pointerof(x))\n        CRYSTAL\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/private_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: private\" do\n  it \"doesn't find private def in another file\" do\n    expect_raises Crystal::TypeException, \"undefined local variable or method 'foo'\" do\n      compiler = Compiler.new\n      sources = [\n        Compiler::Source.new(\"foo.cr\", %(\n                                          private def foo\n                                            1\n                                          end\n                                        )),\n        Compiler::Source.new(\"bar.cr\", %(\n                                          foo\n                                        )),\n      ]\n      compiler.no_codegen = true\n      compiler.prelude = \"empty\"\n      compiler.compile sources, \"output\"\n    end\n  end\n\n  it \"doesn't find private def defined in macro in another file (#7681)\" do\n    expect_raises Crystal::TypeException, \"undefined local variable or method 'foo'\" do\n      compiler = Compiler.new\n      sources = [\n        Compiler::Source.new(\"foo.cr\", %(\n                                          {% begin %}\n                                            private def foo\n                                              1\n                                            end\n                                          {% end %}\n                                        )),\n        Compiler::Source.new(\"bar.cr\", %(\n                                          foo\n                                        )),\n      ]\n      compiler.no_codegen = true\n      compiler.prelude = \"empty\"\n      compiler.compile sources, \"output\"\n    end\n  end\n\n  it \"finds private def in same file\" do\n    compiler = Compiler.new\n    sources = [\n      Compiler::Source.new(\"foo.cr\", %(\n                                        private def foo\n                                          1\n                                        end\n\n                                        foo\n                                      )),\n    ]\n    compiler.no_codegen = true\n    compiler.prelude = \"empty\"\n    compiler.compile sources, \"output\"\n  end\n\n  it \"finds private def in same file that invokes another def\" do\n    compiler = Compiler.new\n    sources = [\n      Compiler::Source.new(\"foo.cr\", %(\n                                        def bar\n                                          2\n                                        end\n\n                                        private def foo\n                                          bar\n                                        end\n\n                                        foo\n                                      )),\n    ]\n    compiler.no_codegen = true\n    compiler.prelude = \"empty\"\n    compiler.compile sources, \"output\"\n  end\n\n  it \"types private def correctly\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      private def foo\n        1\n      end\n\n      def foo\n        'a'\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't find private macro in another file\" do\n    expect_raises Crystal::TypeException, \"undefined local variable or method 'foo'\" do\n      compiler = Compiler.new\n      sources = [\n        Compiler::Source.new(\"foo.cr\", %(\n                                          private macro foo\n                                            1\n                                          end\n                                        )),\n        Compiler::Source.new(\"bar.cr\", %(\n                                          foo\n                                        )),\n      ]\n      compiler.no_codegen = true\n      compiler.prelude = \"empty\"\n      compiler.compile sources, \"output\"\n    end\n  end\n\n  it \"finds private macro in same file\" do\n    compiler = Compiler.new\n    sources = [\n      Compiler::Source.new(\"foo.cr\", %(\n                                        private macro foo\n                                          1\n                                        end\n\n                                        foo\n                                      )),\n    ]\n    compiler.no_codegen = true\n    compiler.prelude = \"empty\"\n    compiler.compile sources, \"output\"\n  end\n\n  it \"finds private macro in same file, invoking from another macro (#1265)\" do\n    compiler = Compiler.new\n    sources = [\n      Compiler::Source.new(\"foo.cr\", %(\n                                        private macro foo\n                                          1\n                                        end\n\n                                        macro bar\n                                          foo\n                                        end\n\n                                        bar\n                                      )),\n    ]\n    compiler.no_codegen = true\n    compiler.prelude = \"empty\"\n    compiler.compile sources, \"output\"\n  end\n\n  it \"find module private macro inside the module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        private macro foo\n          def bar\n            1\n          end\n        end\n\n        foo\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"find module private macro inside a module, which is inherited by the module\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        private macro foo\n          def bar\n            1\n          end\n        end\n      end\n\n      class Bar < Foo\n        foo\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"doesn't find module private macro outside the module\" do\n    assert_error <<-CRYSTAL, \"private macro 'foo' called for Foo\"\n      class Foo\n        private macro foo\n          1\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"finds private def when invoking from inside macro (#2082)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      private def foo\n        42\n      end\n\n      {% begin %}\n        foo\n      {% end %}\n      CRYSTAL\n  end\n\n  it \"doesn't find private class in another file\" do\n    expect_raises Crystal::TypeException, \"undefined constant Foo\" do\n      compiler = Compiler.new\n      sources = [\n        Compiler::Source.new(\"foo.cr\", %(\n                                          private class Foo\n                                          end\n                                        )),\n        Compiler::Source.new(\"bar.cr\", %(\n                                          Foo\n                                        )),\n      ]\n      compiler.no_codegen = true\n      compiler.prelude = \"empty\"\n      compiler.compile sources, \"output\"\n    end\n  end\n\n  it \"doesn't find private alias in another file\" do\n    expect_raises Crystal::TypeException, \"undefined constant Foo\" do\n      compiler = Compiler.new\n      sources = [\n        Compiler::Source.new(\"foo.cr\", %(\n                                          private alias Foo = Int32\n                                        )),\n        Compiler::Source.new(\"bar.cr\", %(\n                                          Foo\n                                        )),\n      ]\n      compiler.no_codegen = true\n      compiler.prelude = \"empty\"\n      compiler.compile sources, \"output\"\n    end\n  end\n\n  it \"finds private type in same file\" do\n    compiler = Compiler.new\n    sources = [\n      Compiler::Source.new(\"foo.cr\", %(\n                                        private class Foo\n                                          def foo\n                                            1\n                                          end\n                                        end\n\n                                        Foo.new.foo\n                                      )),\n    ]\n    compiler.no_codegen = true\n    compiler.prelude = \"empty\"\n    compiler.compile sources, \"output\"\n  end\n\n  it \"can use types in private type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      private class Foo\n        def initialize(@x : Int32)\n        end\n\n        def foo\n          @x + 20\n        end\n      end\n\n      Foo.new(10).foo\n      CRYSTAL\n  end\n\n  it \"can use class var initializer in private type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      private class Foo\n        @@x = 1\n\n        def self.x\n          @@x\n        end\n      end\n\n      Foo.x\n      CRYSTAL\n  end\n\n  it \"can use instance var initializer in private type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      private class Foo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"finds private class in macro expansion\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      private class Foo\n        @x = 1\n\n        def x\n          @x\n        end\n      end\n\n      macro foo\n        Foo.new.x\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  {% for kind, decl in {\n                         \"class\"    => %(class Bar; end),\n                         \"module\"   => %(module Bar; end),\n                         \"enum\"     => %(enum Bar; A; end),\n                         \"alias\"    => %(alias Bar = Int32),\n                         \"lib\"      => %(lib Bar; end),\n                         \"constant\" => %(Bar = 1),\n                       } %}\n    it \"doesn't find private {{ kind.id }} from outside namespace\" do\n      assert_error <<-CRYSTAL, \"private constant Foo::Bar referenced\"\n        module Foo\n          private {{ decl.id }}\n        end\n\n        Foo::Bar\n        CRYSTAL\n    end\n  {% end %}\n\n  {% for kind, decl in {\n                         \"class\"    => %(class Foo::Bar; end),\n                         \"module\"   => %(module Foo::Bar; end),\n                         \"enum\"     => %(enum Foo::Bar; A; end),\n                         \"alias\"    => %(alias Foo::Bar = Int32),\n                         \"lib\"      => %(lib Foo::Bar; end),\n                         \"constant\" => %(Foo::Bar = 1),\n                       } %}\n    it \"doesn't find private {{ kind.id }} from outside namespace, long name (#8831)\" do\n      assert_error <<-CRYSTAL, \"private constant Foo::Bar referenced\"\n        private {{ decl.id }}\n\n        Foo::Bar\n        CRYSTAL\n    end\n\n    it \"doesn't define incorrect type in top-level namespace (#13511)\" do\n      assert_error <<-CRYSTAL, \"undefined constant Bar\"\n        private {{ decl.id }}\n\n        Bar\n        CRYSTAL\n    end\n  {% end %}\n\n  {% for kind, decl in {\n                         \"class\"    => %(class ::Foo; end),\n                         \"module\"   => %(module ::Foo; end),\n                         \"enum\"     => %(enum ::Foo; A; end),\n                         \"alias\"    => %(alias ::Foo = Int32),\n                         \"lib\"      => %(lib ::Foo; end),\n                         \"constant\" => %(::Foo = 1),\n                       } %}\n    it \"doesn't define private {{ kind.id }} with global type name\" do\n      assert_error <<-CRYSTAL, \"can't declare private type in the global namespace\"\n        private {{ decl.id }}\n        CRYSTAL\n    end\n  {% end %}\n\n  it \"finds private type from inside namespace\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        private class Bar\n          def self.foo\n            1\n          end\n        end\n\n        x = Bar.foo\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"finds private type from inside namespace in subclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        private class Bar\n          def self.foo\n            1\n          end\n        end\n      end\n\n      class Foo2 < Foo\n        x = Bar.foo\n      end\n\n      x\n      CRYSTAL\n  end\n\n  it \"gives private constant error in macro\" do\n    assert_error <<-CRYSTAL, \"private constant Foo::Bar referenced\"\n      class Foo\n        private class Bar\n        end\n      end\n\n      {{ Foo::Bar }}\n      CRYSTAL\n  end\n\n  it \"doesn't find private constant in another file (#7850)\" do\n    expect_raises Crystal::TypeException, \"undefined constant Foo\" do\n      compiler = Compiler.new\n      sources = [\n        Compiler::Source.new(\"foo.cr\", %(private Foo = 1)),\n        Compiler::Source.new(\"bar.cr\", %(Foo)),\n      ]\n      compiler.no_codegen = true\n      compiler.prelude = \"empty\"\n      compiler.compile sources, \"output\"\n    end\n  end\n\n  it \"doesn't find private class defined through macro (#8715)\" do\n    assert_error <<-CRYSTAL, \"private constant Foo::Bar referenced\"\n      macro bar\n        class Bar\n        end\n      end\n\n      class Foo\n        private bar\n      end\n\n      Foo::Bar\n      CRYSTAL\n  end\n\n  it \"doesn't find private module defined through macro (#8715)\" do\n    assert_error <<-CRYSTAL, \"private constant Foo::Bar referenced\"\n      macro bar\n        module Bar\n        end\n      end\n\n      class Foo\n        private bar\n      end\n\n      Foo::Bar\n      CRYSTAL\n  end\n\n  it \"doesn't find private macro defined through macro (#8715)\" do\n    assert_error <<-CRYSTAL, \"private macro 'bar' called for Foo\"\n      macro bar\n        macro bar\n        end\n      end\n\n      class Foo\n        private bar\n      end\n\n      Foo.bar\n      CRYSTAL\n  end\n\n  it \"doesn't find private thing defined through recursive macro (#8715)\" do\n    assert_error <<-CRYSTAL, \"private constant Foo::Bar referenced\"\n      macro bar\n        baz\n      end\n\n      macro baz\n        class Bar\n        end\n      end\n\n      class Foo\n        private bar\n      end\n\n      Foo::Bar\n      CRYSTAL\n  end\n\n  it \"doesn't inherit visibility from class node in macro hook (#8794)\" do\n    assert_no_errors <<-CRYSTAL\n      module M1\n        macro included\n          include M2\n        end\n      end\n\n      module M2\n        macro setup_initializer_hook\n          macro finished\n            generate_needy_initializer\n          end\n\n          macro included\n            setup_initializer_hook\n          end\n\n          macro inherited\n            setup_initializer_hook\n          end\n        end\n\n        macro included\n          setup_initializer_hook\n        end\n\n        macro generate_needy_initializer\n          {% if !@type.abstract? %}\n            def initialize(a)\n            end\n          {% end %}\n        end\n      end\n\n      abstract class Base\n        include M1\n      end\n\n      private class Foo < Base\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/proc_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: proc\" do\n  it \"types empty proc literal\" do\n    assert_type(\"-> {}\") { proc_of(nil_type) }\n  end\n\n  it \"types int proc literal\" do\n    assert_type(\"-> { 1 }\") { proc_of(int32) }\n  end\n\n  it \"types proc call\" do\n    assert_type(\"x = -> { 1 }; x.call()\", inject_primitives: true) { int32 }\n  end\n\n  it \"types int -> int proc literal\" do\n    assert_type(\"->(x : Int32) { x }\") { proc_of(int32, int32) }\n  end\n\n  it \"types int -> int proc call\" do\n    assert_type(\"f = ->(x : Int32) { x }; f.call(1)\", inject_primitives: true) { int32 }\n  end\n\n  it \"types proc literal with return type (1)\" do\n    assert_type(\"->(x : Int32) : Int32 { x }\") { proc_of(int32, int32) }\n  end\n\n  it \"types proc literal with return type (2)\" do\n    assert_type(\"-> : Int32 | String { 1 }\") { proc_of(union_of int32, string) }\n  end\n\n  it \"types proc call with return type\" do\n    assert_type(\"x = -> : Int32 | String { 1 }; x.call()\", inject_primitives: true) { union_of int32, string }\n  end\n\n  it \"types proc pointer\" do\n    assert_type(\"def foo; 1; end; ->foo\") { proc_of(int32) }\n  end\n\n  it \"types proc pointer with types\" do\n    assert_type(\"def foo(x); x; end; ->foo(Int32)\") { proc_of(int32, int32) }\n  end\n\n  it \"types a proc pointer with generic types\" do\n    assert_type(\"def foo(x); end; ->foo(Pointer(Int32))\") { proc_of(pointer_of(int32), nil_type) }\n  end\n\n  it \"types proc pointer to instance method\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32) }\n      class Foo\n        def initialize\n          @x = 1\n        end\n\n        def coco\n          @x\n        end\n      end\n\n      foo = Foo.new\n      ->foo.coco\n      CRYSTAL\n  end\n\n  it \"types proc type spec\" do\n    assert_type(\"a = Pointer(Int32 -> Int64).malloc(1_u64)\", inject_primitives: true) { pointer_of(proc_of(int32, int64)) }\n  end\n\n  it \"allows passing proc type if it is a lib alias\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibC\n        alias Callback = Int32 -> Int32\n        fun foo(x : Callback) : Float64\n      end\n\n      f = ->(x : Int32) { x + 1 }\n      LibC.foo f\n      CRYSTAL\n  end\n\n  it \"allows passing proc type if it is typedef'd\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      lib LibC\n        type Callback = Int32 -> Int32\n        fun foo : Callback\n        fun bar(x : Callback) : Float64\n      end\n\n      LibC.bar LibC.foo\n      CRYSTAL\n  end\n\n  it \"errors when using local variable with proc argument name\" do\n    assert_error \"->(a : Int32) { }; a\",\n      \"undefined local variable or method 'a'\"\n  end\n\n  it \"allows implicit cast of proc to return void in LibC function\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun atexit(fun : -> ) : Int32\n      end\n\n      LibC.atexit ->{ 1 }\n      CRYSTAL\n  end\n\n  it \"passes proc pointer as block\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        yield\n      end\n\n      f = -> { 1 }\n      foo &f\n      CRYSTAL\n  end\n\n  it \"passes proc pointer as block with arguments\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def foo\n        yield 1\n      end\n\n      f = ->(x : Int32) { x.to_f }\n      foo &f\n      CRYSTAL\n  end\n\n  it \"binds proc literal to arguments and body\" do\n    assert_type(<<-CRYSTAL) { proc_of(union_of(int32, char)) }\n      x = 1\n      f = -> { x }\n      x = 'a'\n      f\n      CRYSTAL\n  end\n\n  it \"has proc literal as restriction and works\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def foo(x : Int32 -> Float64)\n        x.call(1)\n      end\n\n      foo ->(x : Int32) { x.to_f }\n      CRYSTAL\n  end\n\n  it \"has proc literal as restriction and works when output not specified\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nil_type }\n      def foo(x : Int32 -> )\n        x.call(1)\n      end\n\n      foo ->(x : Int32) { x.to_f }\n      CRYSTAL\n  end\n\n  it \"has proc literal as restriction and errors if output is different\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Proc(Int32, Float64), not Proc(Int32, Int32)\"\n      def foo(x : Int32 -> Float64)\n        x.call(1)\n      end\n\n      foo ->(x : Int32) { x }\n      CRYSTAL\n  end\n\n  it \"has proc literal as restriction and errors if input is different\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Proc(Int32, Float64), not Proc(Int64, Float64)\", inject_primitives: true\n      def foo(x : Int32 -> Float64)\n        x.call(1)\n      end\n\n      foo ->(x : Int64) { x.to_f }\n      CRYSTAL\n  end\n\n  it \"has proc literal as restriction and errors if sizes are different\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Proc(Int32, Float64), not Proc(Int32, Int32, Float64)\", inject_primitives: true\n      def foo(x : Int32 -> Float64)\n        x.call(1)\n      end\n\n      foo ->(x : Int32, y : Int32) { x.to_f }\n      CRYSTAL\n  end\n\n  it \"allows passing nil as proc callback if it is a lib alias\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        alias Cb = Int32 ->\n        fun bla(x : Cb) : Int32\n      end\n\n      LibC.bla(nil)\n      CRYSTAL\n  end\n\n  it \"disallows casting a proc type to one accepting more arguments\" do\n    assert_error(<<-CRYSTAL, \"can't cast\", inject_primitives: true)\n      f = ->(x : Int32) { x.to_f }\n      f.as(Int32, Int32 -> Float64)\n      CRYSTAL\n  end\n\n  it \"allows casting a proc type to one with void argument\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { proc_of [int32, void] }\n      f = ->(x : Int32) { x.to_f }\n      f.as(Int32 -> Void)\n      CRYSTAL\n  end\n\n  it \"disallows casting a proc type to one accepting less arguments\" do\n    assert_error <<-CRYSTAL, \"can't cast Proc(Int32, Float64) to Proc(Float64)\", inject_primitives: true\n      f = ->(x : Int32) { x.to_f }\n      f.as(-> Float64)\n      CRYSTAL\n  end\n\n  it \"disallows casting a proc type to one accepting same size argument but different output\" do\n    assert_error <<-CRYSTAL, \"can't cast Proc(Int32, Float64) to Proc(Int32, Int32)\", inject_primitives: true\n      f = ->(x : Int32) { x.to_f }\n      f.as(Int32 -> Int32)\n      CRYSTAL\n  end\n\n  it \"disallows casting a proc type to one accepting same size argument but different input\" do\n    assert_error <<-CRYSTAL, \"can't cast Proc(Int32, Float64) to Proc(Float64, Float64)\", inject_primitives: true\n      f = ->(x : Int32) { x.to_f }\n      f.as(Float64 -> Float64)\n      CRYSTAL\n  end\n\n  it \"errors if inferred return type doesn't match return type restriction (1)\" do\n    assert_error \"-> : Int32 { true }\", \"expected Proc to return Int32, not Bool\"\n  end\n\n  it \"errors if inferred return type doesn't match return type restriction (2)\" do\n    assert_error \"->(x : Int32) : Int32 { x || 'a' }\", \"expected Proc to return Int32, not (Char | Int32)\"\n  end\n\n  it \"types proc literal hard type inference (1)\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"], tuple_of([types[\"Foo\"], int32])) }\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      ->(f : Foo) do\n        {Foo.new(f.x), 0}\n      end\n      CRYSTAL\n  end\n\n  it \"allows implicit cast of proc to return void in non-generic restriction\" do\n    assert_type(<<-CRYSTAL) { proc_of(void) }\n      def foo(x : ->)\n        x\n      end\n\n      foo ->{ 1 }\n      CRYSTAL\n  end\n\n  it \"allows implicit cast of proc to return void in generic restriction\" do\n    assert_type(<<-CRYSTAL) { proc_of(void) }\n      class Foo(T)\n        def foo(x : T)\n          x\n        end\n      end\n\n      foo = Foo(->).new\n      foo.foo ->{ 1 }\n      CRYSTAL\n  end\n\n  it \"types nil or proc type\" do\n    result = assert_type(\"1 == 1 ? nil : ->{}\", inject_primitives: true) { nilable proc_of(nil_type) }\n    result.node.type.should be_a(NilableProcType)\n  end\n\n  it \"allows passing NoReturn type for any return type (1)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { no_return }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo(f : -> Int32)\n        f.call\n      end\n\n      foo ->{ LibC.exit }\n      CRYSTAL\n  end\n\n  it \"allows passing NoReturn type for any return type (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun exit : NoReturn\n        fun foo(x : -> Int32) : Int32\n      end\n\n      LibC.foo ->{ LibC.exit }\n      CRYSTAL\n  end\n\n  it \"allows passing NoReturn type for any return type (3)\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32) }\n      lib LibC\n        fun exit : NoReturn\n        struct S\n          x : -> Int32\n        end\n      end\n\n      s = LibC::S.new\n      s.x = ->{ LibC.exit }\n      s.x\n      CRYSTAL\n  end\n\n  it \"allows passing NoReturn type for any return type, with Proc notation (#12126)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { no_return }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      def foo(f : Proc(Int32))\n        f.call\n      end\n\n      foo ->{ LibC.exit }\n      CRYSTAL\n  end\n\n  it \"allows new on proc type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { proc_of(int32, int32) }\n      #{proc_new}\n\n      alias Func = Int32 -> Int32\n      Func.new { |x| x + 1 }\n      CRYSTAL\n  end\n\n  it \"allows new on proc type that is a lib alias\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { proc_of(int32, int32) }\n      #{proc_new}\n\n      lib LibC\n        alias F = Int32 -> Int32\n      end\n\n      LibC::F.new { |x| x + 1 }\n      CRYSTAL\n  end\n\n  it \"allows new on proc type with less block params\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32, int32) }\n      #{proc_new}\n\n      alias Func = Int32 -> Int32\n      Func.new { 1 }\n      CRYSTAL\n  end\n\n  it \"says wrong number of block params in new on proc type\" do\n    assert_error <<-CRYSTAL, \"wrong number of block parameters (given 2, expected 1)\"\n      #{proc_new}\n\n      alias Alias = Int32 -> Int32\n      Alias.new { |x, y| }\n      CRYSTAL\n  end\n\n  it \"says wrong return type in new on proc type\" do\n    assert_error <<-CRYSTAL, \"expected block to return Int32, not Float64\", inject_primitives: true\n      #{proc_new}\n\n      alias Alias = Int32 -> Int32\n      Alias.new &.to_f\n      CRYSTAL\n  end\n\n  it \"errors if missing argument type in proc literal\" do\n    assert_error \"->(x) { x }\",\n      \"parameter 'x' of Proc literal must have a type\"\n  end\n\n  it \"allows passing function to LibC without specifying types\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibC\n        fun foo(x : Int32 -> Int32) : Float64\n      end\n\n      LibC.foo ->(x) { x + 1 }\n      CRYSTAL\n  end\n\n  it \"allows passing function to LibC without specifying types, using a global method\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibC\n        fun foo(x : Int32 -> Int32) : Float64\n      end\n\n      def callback(x)\n        x + 1\n      end\n\n      LibC.foo ->callback\n      CRYSTAL\n  end\n\n  it \"allows passing function to LibC without specifying types, using a class method\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      lib LibC\n        fun foo(x : Int32 -> Int32) : Float64\n      end\n\n      class Foo\n        def self.callback(x)\n          x + 1\n        end\n      end\n\n      LibC.foo ->Foo.callback\n      CRYSTAL\n  end\n\n  it \"allows writing a function type with Proc\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32, int32).metaclass }\n      Proc(Int32, Int32)\n      CRYSTAL\n  end\n\n  it \"allows using Proc as restriction (1)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(x : Proc(Int32, Int32))\n        x.call(2)\n      end\n\n      foo ->(x : Int32) { x + 1 }\n      CRYSTAL\n  end\n\n  it \"allows using Proc as restriction (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo(x : Proc)\n        x.call(2)\n      end\n\n      foo ->(x : Int32) { x + 1 }\n      CRYSTAL\n  end\n\n  it \"allows using Proc as restriction (3)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32.metaclass }\n      def foo(x : Proc(T, U)) forall T, U\n        T\n      end\n\n      foo ->(x : Int32) { x + 1 }\n      CRYSTAL\n  end\n\n  it \"forwards block and computes correct type (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { string }\n      def foo(&block : -> _)\n        bar &block\n      end\n\n      def bar(&block : -> _)\n        block\n      end\n\n      foo { 1 }\n      foo { \"hello\" }.call\n      CRYSTAL\n  end\n\n  it \"doesn't need to deduce type of block if return is void\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"], nil_type) }\n      class Foo\n        def initialize\n          @bar = 1\n        end\n\n        def bar\n          @bar\n        end\n      end\n\n      def foo(&block : Foo ->)\n        block\n      end\n\n      f = foo { |f| f.bar }\n      Foo.new\n      f\n      CRYSTAL\n  end\n\n  it \"gives correct error message when proc return type is incorrect (#219)\" do\n    assert_error <<-CRYSTAL, \"argument 'f' of 'LibFoo#bar' must be a Proc returning Int32, not Float64\"\n      lib LibFoo\n        fun bar(f : Int32 -> Int32)\n      end\n\n      LibFoo.bar ->(x) { 1.1 }\n      CRYSTAL\n  end\n\n  it \"doesn't capture closured var if using typeof\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibFoo\n        fun foo(x : ->) : Int32\n      end\n\n      a = 1\n      LibFoo.foo ->{\n        typeof(a)\n        2\n      }\n      CRYSTAL\n  end\n\n  it \"types proc literal with a type that was never instantiated\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"], int32) }\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      ->(s : Foo) { s.x }\n      CRYSTAL\n  end\n\n  it \"types proc pointer with a type that was never instantiated\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"], types[\"Foo\"]) }\n      class Foo\n        def initialize(@x : Int32)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      def foo(f : Foo)\n        Foo.new(f.x)\n      end\n\n      ->foo(Foo)\n      CRYSTAL\n  end\n\n  it \"allows using proc arg name shadowing local variable\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1\n      f = ->(a : String) { }\n      a\n      CRYSTAL\n  end\n\n  it \"uses array argument of proc arg (1)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(&block : Array(Foo) -> Foo)\n      end\n\n      block = foo { |elems| elems[0] }\n      elems = [Foo.new, Bar.new]\n      block\n      CRYSTAL\n  end\n\n  it \"uses array argument of proc arg (2)\" do\n    assert_type(<<-CRYSTAL) { proc_of(array_of(types[\"Foo\"].virtual_type), types[\"Foo\"].virtual_type) }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(&block : Array(Foo) -> Foo)\n        block\n      end\n\n      block = foo { |elems| elems[0] }\n      elems = [Foo.new, Bar.new]\n      block\n      CRYSTAL\n  end\n\n  it \"uses array argument of proc arg (3)\" do\n    assert_type(<<-CRYSTAL) { proc_of(array_of(types[\"Foo\"].virtual_type), types[\"Foo\"].virtual_type) }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n        getter value\n\n        def initialize(@value : Int32)\n        end\n      end\n\n      def foo(&block : Array(Foo) -> Foo)\n        block\n      end\n\n      block = foo { |elems| Bar.new(elems[0].as(Bar).value) }\n      elems = [Foo.new, Bar.new(1)]\n      block\n      CRYSTAL\n  end\n\n  it \"uses array argument of proc arg (4)\" do\n    assert_error <<-CRYSTAL, \"expected block to return Foo, not Int32\"\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(&block : Array(Foo) -> Foo)\n        block\n      end\n\n      block = foo { |elems| 1 }\n      block.call [Foo.new, Bar.new]\n      CRYSTAL\n  end\n\n  it \"doesn't let passing an non-covariant generic argument\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(&block : Array(Foo) -> Foo)\n        block\n      end\n\n      f = ->(x : Array(Foo)) {}\n      f.call [Bar.new]\n      CRYSTAL\n  end\n\n  it \"allows invoking a function with a generic subtype (1)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n        def foo\n          1\n        end\n      end\n\n      class Foo(T)\n        include Moo\n      end\n\n      def func(&block : Moo -> _)\n        block\n      end\n\n      foo = Foo(Int32).new\n      f = func { |moo| moo.foo }\n      f.call foo\n      CRYSTAL\n  end\n\n  it \"allows invoking a function with a generic subtype (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo(T)\n        def foo\n          1\n        end\n      end\n\n      class Foo(T)\n        include Moo(T)\n      end\n\n      def func(&block : Moo(Int32) -> _)\n        block\n      end\n\n      foo = Foo(Int32).new\n      f = func { |moo| moo.foo }\n      f.call foo\n      CRYSTAL\n  end\n\n  it \"gets pointer to lib fun without specifying types\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32, float64) }\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      ->LibFoo.foo\n      CRYSTAL\n  end\n\n  it \"gets pointer to lib fun with types\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32, float64) }\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      ->LibFoo.foo(Int32)\n      CRYSTAL\n  end\n\n  it \"gets pointer to lib fun with compatible parameter types (1)\" do\n    assert_type(<<-CRYSTAL) { proc_of(proc_of(float64), int32) }\n      lib LibFoo\n        fun foo(x : ->) : Int32\n      end\n\n      ->LibFoo.foo(-> Float64)\n      CRYSTAL\n  end\n\n  it \"gets pointer to lib fun with compatible parameter types (2)\" do\n    assert_type(<<-CRYSTAL) { proc_of(pointer_of(float64), int32) }\n      lib LibFoo\n        fun foo(x : Void*) : Int32\n      end\n\n      ->LibFoo.foo(Float64*)\n      CRYSTAL\n  end\n\n  it \"gets pointer to lib fun with compatible `#to_unsafe` type\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"], float64) }\n      lib LibFoo\n        fun foo(x : Int32) : Float64\n      end\n\n      class Foo\n        def to_unsafe\n          1\n        end\n      end\n\n      ->LibFoo.foo(Foo)\n      CRYSTAL\n  end\n\n  it \"gets pointer to variadic lib fun\" do\n    assert_type(<<-CRYSTAL) { proc_of([int32, int16, int8, float64] of Type) }\n      lib LibFoo\n        fun foo(x : Int32, ...) : Float64\n      end\n\n      ->LibFoo.foo(Int32, Int16, Int8)\n      CRYSTAL\n  end\n\n  it \"errors if pointer to lib fun has incompatible parameter type\" do\n    assert_error <<-CRYSTAL, \"argument 'x' of 'LibFoo#foo' must be Int32, not Foo (nor Char returned by 'Foo#to_unsafe')\"\n      lib LibFoo\n        fun foo(x : Int32)\n      end\n\n      class Foo\n        def to_unsafe\n          'a'\n        end\n      end\n\n      ->LibFoo.foo(Foo)\n      CRYSTAL\n  end\n\n  it \"errors if pointer to lib fun has incorrect number of parameters\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'LibFoo#foo' (given 1, expected 2)\"\n      lib LibFoo\n        fun foo(x : Int32, y : Int32)\n      end\n\n      ->LibFoo.foo(Int32)\n      CRYSTAL\n  end\n\n  it \"allows passing union including module to proc\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n        def moo\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar\n        include Moo\n      end\n\n      proc = ->(x : Moo) { x.moo }\n\n      foo = Foo.new || Bar.new\n      proc.call(foo)\n      CRYSTAL\n  end\n\n  it \"allows passing virtual type including module to proc\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      module Moo\n        def moo\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar < Foo\n      end\n\n      proc = ->(x : Moo) { x.moo }\n\n      foo = Foo.new || Bar.new\n      proc.call(foo)\n      CRYSTAL\n  end\n\n  %w(Object Value Reference Number Int Float Struct Class Proc Tuple Enum StaticArray Pointer).each do |type|\n    it \"disallows #{type} in procs\" do\n      assert_error <<-CRYSTAL, \"can't use #{type} as a Proc argument type\"\n        ->(x : #{type}) { }\n        CRYSTAL\n    end\n\n    it \"disallows #{type} in proc return types\" do\n      assert_error <<-CRYSTAL, \"can't use #{type} as a Proc argument type\"\n        -> : #{type} { }\n        CRYSTAL\n    end\n\n    it \"disallows #{type} in captured block\" do\n      assert_error <<-CRYSTAL, \"can't use #{type} as a Proc argument type\"\n        def foo(&block : #{type} ->)\n        end\n\n        foo {}\n        CRYSTAL\n    end\n\n    it \"disallows #{type} in proc pointer\" do\n      assert_error <<-CRYSTAL, \"can't use #{type} as a Proc argument type\"\n        def foo(x)\n        end\n\n        ->foo(#{type})\n        CRYSTAL\n    end\n\n    it \"disallows #{type} in proc notation parameter type\" do\n      assert_error \"x : #{type} ->\", \"can't use #{type} as a Proc argument type\"\n    end\n\n    it \"disallows #{type} in proc notation return type\" do\n      assert_error \"x : -> #{type}\", \"can't use #{type} as a Proc argument type\"\n    end\n  end\n\n  it \"allows metaclass in procs\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].metaclass, types[\"Foo\"]) }\n      class Foo\n      end\n\n      ->(x : Foo.class) { x.new }\n      CRYSTAL\n  end\n\n  it \"allows metaclass in proc return types\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].metaclass) }\n      class Foo\n      end\n\n      -> : Foo.class { Foo }\n      CRYSTAL\n  end\n\n  it \"allows metaclass in captured block\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].metaclass, types[\"Foo\"]) }\n      class Foo\n      end\n\n      def foo(&block : Foo.class -> Foo)\n        block\n      end\n\n      foo { |x| x.new }\n      CRYSTAL\n  end\n\n  it \"allows metaclass in proc pointer\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].metaclass, types[\"Foo\"]) }\n      class Foo\n      end\n\n      def foo(x : Foo.class)\n        x.new\n      end\n\n      ->foo(Foo.class)\n      CRYSTAL\n  end\n\n  it \"allows metaclass in proc notation parameter type\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].metaclass, nil_type) }\n      class Foo\n      end\n\n      #{proc_new}\n\n      x : Foo.class -> = Proc(Foo.class, Nil).new { }\n      x\n      CRYSTAL\n  end\n\n  it \"allows metaclass in proc notation return type\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].metaclass) }\n      class Foo\n      end\n      x : -> Foo.class = ->{ Foo }\n      x\n      CRYSTAL\n  end\n\n  it \"...\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        ->{ a = 1; return 0 }.call\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't crash on constant to proc pointer\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun foo\n      end\n\n      FOO = ->LibC.foo\n      1\n      CRYSTAL\n  end\n\n  it \"sets proc type as void if explicitly told so, when using new\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32, nil_type) }\n      #{proc_new}\n\n      Proc(Int32, Void).new { 1 }\n      CRYSTAL\n  end\n\n  it \"unpacks tuple but doesn't override local variables, when using new (#9813)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      #{proc_new}\n\n      i = 1\n      Proc(Tuple(Char), Nil).new do |(x)|\n\n      end.call({'a'})\n      i\n      CRYSTAL\n  end\n\n  it \"accesses T and R\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32]).metaclass, char.metaclass]) }\n      struct Proc\n        def t\n          {T, R}\n        end\n      end\n\n      ->(x : Int32) { 'a' }.t\n      CRYSTAL\n  end\n\n  it \"can match *T in block argument\" do\n    assert_type(<<-CRYSTAL) { bool.metaclass }\n      struct Tuple\n        def foo(&block : *T -> U) forall T, U\n          yield self[0], self[1]\n          U\n        end\n      end\n\n      {1, 'a'}.foo { |x, y| true }\n      CRYSTAL\n  end\n\n  it \"says wrong number of arguments\" do\n    assert_error <<-CRYSTAL, \"no overload matches\", inject_primitives: true\n      ->(x : Int32) { }.call\n      CRYSTAL\n  end\n\n  it \"finds method of object\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Object\n        def foo\n          1\n        end\n      end\n\n      ->{}.foo\n      CRYSTAL\n  end\n\n  it \"accesses T inside variadic generic\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, float64]).metaclass, char.metaclass]) }\n      def foo(proc : Proc(*T, R)) forall T, R\n        {T, R}\n      end\n\n      foo(->(x : Int32, y : Float64) { 'a' })\n      CRYSTAL\n  end\n\n  it \"accesses T inside variadic generic (2)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32]).metaclass, char.metaclass]) }\n      def foo(proc : Proc(*T, R)) forall T, R\n        {T, R}\n      end\n\n      foo(->(x : Int32) { 'a' })\n      CRYSTAL\n  end\n\n  it \"accesses T inside variadic generic, in proc notation\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, float64]).metaclass, char.metaclass]) }\n      def foo(proc : *T -> R) forall T, R\n        {T, R}\n      end\n\n      foo(->(x : Int32, y : Float64) { 'a' })\n      CRYSTAL\n  end\n\n  it \"declares an instance variable with splat in proc notation\" do\n    assert_type(<<-CRYSTAL) { proc_of([int32, char, string]) }\n      class Foo\n        @x : *{Int32, Char} -> String\n\n        def initialize\n          @x = ->(x : Int32, y : Char) { \"a\" }\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can assign NoReturn proc to other proc (#3032)\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32) }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      class Foo\n        @x : -> Int32\n\n        def initialize\n          @x = ->{ LibC.exit }\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"*doesn't* merge Proc that returns Nil with another one that returns something else (#3655) (this was reverted)\" do\n    assert_type(<<-CRYSTAL) { union_of proc_of(int32, int32), proc_of(int32, nil_type) }\n      a = ->(x : Int32) { 1 }\n      b = ->(x : Int32) { nil }\n      a || b\n      CRYSTAL\n  end\n\n  it \"*doesn't* merge Proc that returns NoReturn with another one that returns something else (#9971)\" do\n    assert_type(<<-CRYSTAL) { union_of proc_of(int32, int32), proc_of(int32, no_return) }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      a = ->(x : Int32) { 1 }\n      b = ->(x : Int32) { LibC.exit }\n      a || b\n      CRYSTAL\n  end\n\n  it \"merges return type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      a = ->(x : Int32) { 1 }\n      b = ->(x : Int32) { nil }\n      (a || b).call(1)\n      CRYSTAL\n  end\n\n  it \"can assign proc that returns anything to proc that returns nil, with instance var (#3655)\" do\n    assert_type(<<-CRYSTAL) { proc_of(nil_type) }\n      class Foo\n        @block : -> Nil\n\n        def initialize\n          @block = ->{ 1 }\n        end\n\n        def block\n          @block\n        end\n      end\n\n      Foo.new.block\n      CRYSTAL\n  end\n\n  it \"can assign proc that returns anything to proc that returns nil, with class var (#3655)\" do\n    assert_type(<<-CRYSTAL) { proc_of(nil_type) }\n      module Moo\n        @@block : -> Nil = ->{ nil }\n\n        def self.block=(@@block)\n        end\n\n        def self.block\n          @@block\n        end\n      end\n\n      Moo.block = ->{ 1 }\n      Moo.block\n      CRYSTAL\n  end\n\n  it \"can assign proc that returns anything to proc that returns nil, with local var (#3655)\" do\n    assert_type(<<-CRYSTAL) { proc_of(nil_type) }\n      proc : -> Nil\n\n      a = ->{ 1 }\n      b = ->{ nil }\n      proc = a || b\n\n      proc\n      CRYSTAL\n  end\n\n  it \"can pass proc that returns T as Void with named args (#7523)\" do\n    assert_type(<<-CRYSTAL) { proc_of(nil_type) }\n      def foo(proc : ->)\n        proc\n      end\n\n      foo(proc: ->{ 1 })\n      CRYSTAL\n  end\n\n  it \"errors when using macro as proc value (top-level) (#7465)\" do\n    ex = assert_error <<-CRYSTAL, \"undefined method 'bar'\"\n           macro bar\n           end\n\n           ->bar\n           CRYSTAL\n\n    ex.to_s.should contain \"'bar' exists as a macro, but macros can't be used in proc pointers\"\n  end\n\n  it \"errors when using macro as proc value (top-level with obj) (#7465)\" do\n    ex = assert_error <<-CRYSTAL, \"undefined method 'bar' for Foo.class\"\n           class Foo\n             macro bar\n             end\n           end\n\n           ->Foo.bar\n           CRYSTAL\n\n    ex.to_s.should contain \"'bar' exists as a macro, but macros can't be used in proc pointers\"\n  end\n\n  it \"errors when using macro as proc value (inside method) (#7465)\" do\n    ex = assert_error <<-CRYSTAL, \"undefined method 'bar'\\n\\n\"\n           macro bar\n           end\n\n           def foo\n             ->bar\n           end\n\n           foo\n           CRYSTAL\n\n    ex.to_s.should contain \"'bar' exists as a macro, but macros can't be used in proc pointers\"\n  end\n\n  it \"virtualizes proc type (#6789)\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].virtual_type!, types[\"Foo\"].virtual_type!) }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Capture(T)\n        def initialize(@block : Foo -> T)\n        end\n\n        def block\n          @block\n        end\n      end\n\n      def capture(&block : Foo -> T) forall T\n        Capture.new(block)\n      end\n\n      capture do |foo|\n        Foo.new\n      end.block\n      CRYSTAL\n  end\n\n  it \"virtualizes proc type with -> (#8730)\" do\n    assert_type(<<-CRYSTAL) { proc_of(types[\"Foo\"].virtual_type!, types[\"Foo\"].virtual_type!) }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x)\n        Foo.new\n      end\n\n      ->foo(Foo)\n      CRYSTAL\n  end\n\n  it \"can pass Proc(T) to Proc(Nil) in type restriction (#8964)\" do\n    assert_type(<<-CRYSTAL) { proc_of nil_type }\n      def foo(x : Proc(Nil))\n        x\n      end\n\n      foo(->{ 1 })\n      CRYSTAL\n  end\n\n  it \"can pass Proc(X, T) to Proc(X, Nil) in type restriction (#8964)\" do\n    assert_type(<<-CRYSTAL) { proc_of string, nil_type }\n      def foo(x : Proc(String, Nil))\n        x\n      end\n\n      foo(->(x : String) { 1 })\n      CRYSTAL\n  end\n\n  it \"casts to Proc(Nil) when specified in return type\" do\n    assert_type(<<-CRYSTAL) { proc_of nil_type }\n      def foo : Proc(Nil)\n        ->{ 1 }\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"can use @ivar as pointer syntax receiver (#9239)\" do\n    assert_type(<<-CRYSTAL) { proc_of int32 }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar\n        @foo = Foo.new\n\n        def foo\n          ->@foo.foo\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"can use @@cvar as pointer syntax receiver (#9239)\" do\n    assert_type(<<-CRYSTAL) { proc_of int32 }\n      class Foo\n        @@foo = new\n\n        def self.foo\n          ->@@foo.foo\n        end\n\n        def foo\n          1\n        end\n      end\n\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"doesn't cause upcast bug (#8428)\" do\n    assert_type(<<-CRYSTAL) { union_of proc_of(string), proc_of(nil_type), nil_type }\n      def foo\n        if true\n          begin\n            ->{\"\"}\n          rescue\n            return ->{}\n          end\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types Proc(*T, Void) as Proc(*T, Nil)\" do\n    assert_type(<<-CRYSTAL) { proc_of(int32, nil_type) }\n      #{proc_new}\n\n      Proc(Int32, Void).new { |x| x }\n      CRYSTAL\n  end\nend\n\nprivate def proc_new\n  <<-CRYSTAL\n  struct Proc\n    def self.new(&block : self)\n      block\n    end\n  end\n  CRYSTAL\nend\n"
  },
  {
    "path": "spec/compiler/semantic/recursive_struct_check_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: recursive struct check\" do\n  it \"errors on recursive struct\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Test detected\"\n           struct Test\n             def initialize(@test : Test?)\n             end\n           end\n\n           Test.new(Test.new(nil))\n           CRYSTAL\n\n    ex.to_s.should contain \"`@test : (Test | Nil)`\"\n  end\n\n  it \"errors on recursive struct inside module\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo::Test detected\"\n           struct Foo::Test\n             def initialize(@test : Foo::Test?)\n             end\n           end\n\n           Foo::Test.new(Foo::Test.new(nil))\n           CRYSTAL\n\n    ex.to_s.should contain \"`@test : (Foo::Test | Nil)`\"\n  end\n\n  it \"errors on recursive generic struct inside module\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo::Test(T) detected\"\n           struct Foo::Test(T)\n             def initialize(@test : Foo::Test(T)?)\n             end\n           end\n\n           Foo::Test(Int32).new(Foo::Test(Int32).new(nil))\n           CRYSTAL\n\n    ex.to_s.should contain \"`@test : (Foo::Test(T) | Nil)`\"\n  end\n\n  it \"errors on mutually recursive struct\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo detected\"\n           struct Foo\n             def initialize(@bar : Bar?)\n             end\n           end\n\n           struct Bar\n             def initialize(@foo : Foo?)\n             end\n           end\n\n           Foo.new(Bar.new(nil))\n           Bar.new(Foo.new(nil))\n           CRYSTAL\n\n    ex.to_s.should contain \"`@bar : (Bar | Nil)` -> `(Bar | Nil)` -> `Bar` -> `@foo : (Foo | Nil)`\"\n  end\n\n  it \"detects recursive struct through module\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo detected\"\n           module Moo\n           end\n\n           struct Foo\n             include Moo\n\n             def initialize(@moo : Moo)\n             end\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@moo : Moo` -> `Moo` -> `Foo`\"\n  end\n\n  pending \"errors on recursive abstract struct through module (#11384)\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo detected\"\n           module Moo\n           end\n\n           abstract struct Foo\n             include Moo\n\n             def initialize(@moo : Moo)\n             end\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@moo : Moo` -> `Moo` -> `Foo`\"\n  end\n\n  it \"detects recursive generic struct through module (#4720)\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo(T) detected\"\n           module Bar\n           end\n\n           struct Foo(T)\n             include Bar\n             def initialize(@base : Bar?)\n             end\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@base : (Bar | Nil)` -> `(Bar | Nil)` -> `Bar` -> `Foo(T)`\"\n  end\n\n  it \"detects recursive generic struct through generic module (#4720)\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo(T) detected\"\n           module Bar(T)\n           end\n\n           struct Foo(T)\n             include Bar(T)\n             def initialize(@base : Bar(T)?)\n             end\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@base : (Bar(T) | Nil)` -> `(Bar(T) | Nil)` -> `Bar(T)` -> `Foo(T)`\"\n  end\n\n  it \"detects recursive struct through inheritance (#3071)\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Bar detected\"\n           abstract struct Foo\n           end\n\n           struct Bar < Foo\n             @value = uninitialized Foo\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@value : Foo` -> `Foo` -> `Bar`\"\n  end\n\n  it \"errors on recursive struct through tuple\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo detected\"\n           struct Foo\n             @x : {Foo}\n\n             def initialize(@x)\n             end\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@x : Tuple(Foo)`\"\n  end\n\n  it \"errors on recursive struct through named tuple\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo detected\"\n           struct Foo\n             @x : {x: Foo}\n\n             def initialize(@x)\n             end\n           end\n           CRYSTAL\n\n    ex.to_s.should contain \"`@x : NamedTuple(x: Foo)`\"\n  end\n\n  it \"errors on recursive struct through recursive alias (#4454) (#4455)\" do\n    ex = assert_error <<-CRYSTAL, \"recursive struct Foo detected (recursive aliases are structs)\"\n           struct Bar(T)\n             def initialize(@x : T)\n             end\n           end\n\n           alias Foo = Int32 | Bar(Foo)\n           CRYSTAL\n\n    ex.to_s.should contain \"`(Bar(Foo) | Int32)` -> `Bar(Foo)` -> `@x : Foo`\"\n  end\n\n  it \"errors on private recursive type\" do\n    assert_error <<-CRYSTAL, \"recursive struct Test detected\"\n      private struct Test\n        def initialize(@test : Test?)\n        end\n      end\n\n      Test.new(Test.new(nil))\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/reference_storage_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: ReferenceStorage\" do\n  it \"errors if T is a struct type\" do\n    assert_error <<-CRYSTAL, \"Can't instantiate ReferenceStorage(T) with T = Foo (T must be a reference type)\"\n      @[Primitive(:ReferenceStorageType)]\n      struct ReferenceStorage(T) < Value\n      end\n\n      struct Foo\n        @x = 1\n      end\n\n      ReferenceStorage(Foo)\n      CRYSTAL\n  end\n\n  it \"errors if T is a value type\" do\n    assert_error <<-CRYSTAL, \"Can't instantiate ReferenceStorage(T) with T = Int32 (T must be a reference type)\"\n      @[Primitive(:ReferenceStorageType)]\n      struct ReferenceStorage(T) < Value\n      end\n\n      ReferenceStorage(Int32)\n      CRYSTAL\n  end\n\n  it \"errors if T is a union type\" do\n    assert_error <<-CRYSTAL, \"Can't instantiate ReferenceStorage(T) with T = (Bar | Foo) (T must be a reference type)\"\n      @[Primitive(:ReferenceStorageType)]\n      struct ReferenceStorage(T) < Value\n      end\n\n      class Foo\n      end\n\n      class Bar\n      end\n\n      ReferenceStorage(Foo | Bar)\n      CRYSTAL\n  end\n\n  it \"errors if T is a nilable type\" do\n    assert_error <<-CRYSTAL, \"Can't instantiate ReferenceStorage(T) with T = (Foo | Nil) (T must be a reference type)\"\n      @[Primitive(:ReferenceStorageType)]\n      struct ReferenceStorage(T) < Value\n      end\n\n      class Foo\n      end\n\n      ReferenceStorage(Foo?)\n      CRYSTAL\n  end\n\n  it \"allows a different name\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].metaclass }\n      @[Primitive(:ReferenceStorageType)]\n      struct MyRef(U) < Value\n        def u\n          U\n        end\n      end\n\n      class Foo\n      end\n\n      MyRef(Foo).new.u\n      CRYSTAL\n  end\n\n  it \"adds ReferenceStorage to Value.subclasses once (#15677)\" do\n    assert_type(<<-CRYSTAL) { bool }\n      @[Primitive(:ReferenceStorageType)]\n      struct ReferenceStorage(T) < Value\n      end\n\n      {{ Value.subclasses.select(&.<=(ReferenceStorage)).size == 1 ? true : nil }}\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/require_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: require\" do\n  describe \"file not found\" do\n    it \"require\" do\n      error = assert_error %(require \"file_that_doesnt_exist\"),\n        \"can't find file 'file_that_doesnt_exist'\"\n\n      error.message.not_nil!.should contain \"If you're trying to require a shard:\"\n    end\n\n    it \"relative require\" do\n      error = assert_error %(require \"./file_that_doesnt_exist\"),\n        \"can't find file './file_that_doesnt_exist'\"\n\n      error.message.not_nil!.should_not contain \"If you're trying to require a shard:\"\n    end\n\n    it \"wildcard\" do\n      error = assert_error %(require \"file_that_doesnt_exist/*\"),\n        \"can't find file 'file_that_doesnt_exist/*'\"\n\n      error.message.not_nil!.should contain \"If you're trying to require a shard:\"\n    end\n\n    it \"relative wildcard\" do\n      error = assert_error %(require \"./file_that_doesnt_exist/*\"),\n        \"can't find file './file_that_doesnt_exist/*'\"\n\n      error.message.not_nil!.should_not contain \"If you're trying to require a shard:\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/responds_to_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: responds_to?\" do\n  it \"is bool\" do\n    assert_type(\"1.responds_to?(:foo)\") { bool }\n  end\n\n  it \"restricts type inside if scope 1\" do\n    nodes = parse <<-CRYSTAL\n      require \"primitives\"\n\n      a = 1 || 'a'\n      if a.responds_to?(:\"+\")\n        a\n      end\n      CRYSTAL\n    result = semantic nodes\n    mod, nodes = result.program, result.node.as(Expressions)\n    nodes.last.as(If).then.type.should eq(mod.int32)\n  end\n\n  it \"restricts other types inside if else\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = 1 || 'a'\n      if a.responds_to?(:\"+\")\n        a.to_i32\n      else\n        a.ord\n      end\n      CRYSTAL\n  end\n\n  it \"restricts in assignment\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1 || 'a'\n      if (b = a).responds_to?(:abs)\n        b\n      else\n        2\n      end\n      CRYSTAL\n  end\n\n  it \"restricts virtual generic superclass to subtypes\" do\n    assert_type(<<-CRYSTAL) { nilable union_of(char, string) }\n      module Foo(T)\n      end\n\n      class Bar\n        include Foo(Int32)\n\n        def foo\n          'a'\n        end\n      end\n\n      class Baz(T)\n        include Foo(T)\n\n        def foo\n          \"\"\n        end\n      end\n\n      x = Baz(Int32).new.as(Foo(Int32))\n      if x.responds_to?(:foo)\n        x.foo\n      end\n      CRYSTAL\n  end\n\n  it \"restricts virtual generic module to including types (#8334)\" do\n    assert_type(<<-CRYSTAL) { nilable union_of(char, string) }\n      class Foo(T)\n      end\n\n      class Bar < Foo(Int32)\n        def foo\n          'a'\n        end\n      end\n\n      class Baz(T) < Foo(T)\n        def foo\n          \"\"\n        end\n      end\n\n      x = Baz(Int32).new.as(Foo(Int32))\n      if x.responds_to?(:foo)\n        x.foo\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/restrictions_augmenter_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate def expect_augment(before : String, after : String, *, file : String = __FILE__, line : Int32 = __LINE__)\n  result = semantic(before)\n  result.node.to_s.chomp.should eq(after.chomp), file: file, line: line\nend\n\nprivate def expect_no_augment(code : String, flags = nil)\n  result = semantic(code, flags: flags)\n  result.node.to_s.chomp.should eq(code.chomp)\nend\n\nprivate def it_augments_for_ivar(ivar_type : String, expected_type : String, file = __FILE__, line = __LINE__)\n  it \"augments #{ivar_type}\", file, line do\n    before = <<-CRYSTAL\n      class Foo\n        @x : #{ivar_type}\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo\n        @x : #{ivar_type}\n        def initialize(value : #{expected_type})\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after, file: file, line: line\n  end\nend\n\ndescribe \"Semantic: restrictions augmenter\" do\n  it_augments_for_ivar \"Nil\", \"::Nil\"\n  it_augments_for_ivar \"Bool\", \"::Bool\"\n  it_augments_for_ivar \"Char\", \"::Char\"\n  it_augments_for_ivar \"Int32\", \"::Int32\"\n  it_augments_for_ivar \"Float32\", \"::Float32\"\n  it_augments_for_ivar \"Symbol\", \"::Symbol\"\n  it_augments_for_ivar \"String\", \"::String\"\n  it_augments_for_ivar \"Array(String)\", \"::Array(::String)\"\n  it_augments_for_ivar \"Tuple(Int32, Char)\", \"::Tuple(::Int32, ::Char)\"\n  it_augments_for_ivar \"NamedTuple(a: Int32, b: Char)\", \"::NamedTuple(a: ::Int32, b: ::Char)\"\n  it_augments_for_ivar \"Proc(Int32, Char)\", \"::Int32 -> ::Char\"\n  it_augments_for_ivar \"Proc(Int32, Nil)\", \"::Int32 -> _\"\n  it_augments_for_ivar \"Pointer(Void)\", \"::Pointer(::Void)\"\n  it_augments_for_ivar \"StaticArray(Int32, 8)\", \"::StaticArray(::Int32, 8)\"\n  it_augments_for_ivar \"Char | Int32 | String\", \"::Char | ::Int32 | ::String\"\n  it_augments_for_ivar \"Char | Int32 | String\", \"::Char | ::Int32 | ::String\"\n  it_augments_for_ivar \"Int32.class\", \"::Int32.class\"\n  it_augments_for_ivar \"NoReturn\", \"::NoReturn\"\n  it_augments_for_ivar \"Array(Int32).class\", \"::Array(::Int32).class\"\n  it_augments_for_ivar \"Enumerable(Int32).class\", \"::Enumerable(::Int32).class\"\n\n  it \"augments relative public type\" do\n    before = <<-CRYSTAL\n      class Foo\n        class Bar\n          class Baz\n          end\n        end\n\n        @x : Bar:: Baz\n\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo\n        class Bar\n          class Baz\n          end\n        end\n\n        @x : Bar::Baz\n\n        def initialize(value : ::Foo::Bar::Baz)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments relative private type\" do\n    before = <<-CRYSTAL\n      class Foo\n        private class Bar\n          class Baz\n          end\n        end\n\n        @x : Bar:: Baz\n\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo\n        private class Bar\n          class Baz\n          end\n        end\n\n        @x : Bar::Baz\n\n        def initialize(value : Bar::Baz)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments relative private type in same namespace\" do\n    before = <<-CRYSTAL\n      class Foo\n        private class Bar\n        end\n        private class Baz\n          @x : Bar\n          def initialize(value)\n            @x = value\n          end\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo\n        private class Bar\n        end\n        private class Baz\n          @x : Bar\n          def initialize(value : Bar)\n            @x = value\n          end\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments generic uninstantiated type\" do\n    before = <<-CRYSTAL\n      class Foo(T)\n        @x : Array(T)\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo(T)\n        @x : Array(T)\n        def initialize(value : ::Array(T))\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments for class var\" do\n    before = <<-CRYSTAL\n      class Foo\n        @@x = 1\n        def self.set(value)\n          @@x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo\n        @@x = 1\n        def self.set(value : ::Int32)\n          @@x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"doesn't augment if assigned inside if\" do\n    expect_no_augment <<-CRYSTAL\n      class Foo\n        @x : Int32\n        def initialize(value)\n          if value\n            @x = value\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't augment if assigned inside while\" do\n    expect_no_augment <<-CRYSTAL\n      class Foo\n        @x : Int32\n        def initialize(value)\n          while false\n            @x = value\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't augment if assigned inside block\" do\n    expect_no_augment <<-CRYSTAL\n      def foo(&)\n        yield\n      end\n      class Foo\n        @x : Int32\n        def initialize(value)\n          foo do\n            @x = value\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't augment if the no_restrictions_augmenter flag is present\" do\n    expect_no_augment <<-CRYSTAL, flags: \"no_restrictions_augmenter\"\n      class Foo\n        @x : Int32\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"augments recursive alias type (#12134)\" do\n    before = <<-CRYSTAL\n      alias BasicObject = Array(BasicObject) | Hash(String, BasicObject)\n      class Foo\n        def initialize(value = Hash(String, BasicObject).new)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      alias BasicObject = Array(BasicObject) | Hash(String, BasicObject)\n      class Foo\n        def initialize(value : ::Hash(::String, ::BasicObject) = Hash(String, BasicObject).new)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments typedef\" do\n    before = <<-CRYSTAL\n      lib LibFoo\n        type X = Int32\n      end\n      class Foo\n        @x : LibFoo::X\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      lib LibFoo\n        type X = Int32\n      end\n      class Foo\n        @x : LibFoo::X\n        def initialize(value : ::LibFoo::X)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments virtual type\" do\n    before = <<-CRYSTAL\n      class A\n      end\n      class B < A\n      end\n      class Foo\n        @x : A\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class A\n      end\n      class B < A\n      end\n      class Foo\n        @x : A\n        def initialize(value : ::A)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments virtual metaclass type\" do\n    before = <<-CRYSTAL\n      class A\n      end\n      class B < A\n      end\n      class Foo\n        @x : A.class\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class A\n      end\n      class B < A\n      end\n      class Foo\n        @x : A.class\n        def initialize(value : ::A.class)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments type splat\" do\n    before = <<-CRYSTAL\n      class Foo(T)\n        @x : Array(*T)\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo(T)\n        @x : Array(*T)\n        def initialize(value : ::Array(*T))\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"doesn't crash on macro that yields and defines class (#12142)\" do\n    before = <<-CRYSTAL\n      macro foo\n        {{yield}}\n      end\n      foo do\n        class Foo\n        end\n      end\n      class Bar\n        @x : Foo\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      macro foo\n        {{ yield }}\n      end\n\n      class Foo\n      end\n\n      class Bar\n        @x : Foo\n        def initialize(value : ::Foo)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\n\n  it \"augments for Union(*T) (#12435)\" do\n    before = <<-CRYSTAL\n      class Foo(*T)\n        @x : Union(*T)\n        def initialize(value)\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    after = <<-CRYSTAL\n      class Foo(*T)\n        @x : Union(*T)\n        def initialize(value : ::Union(*T))\n          @x = value\n        end\n      end\n      CRYSTAL\n\n    expect_augment before, after\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/restrictions_spec.cr",
    "content": "require \"../../spec_helper\"\n\nclass Crystal::Program\n  def t(type)\n    types[type.rchop('+')].virtual_type\n  end\nend\n\ndescribe \"Restrictions\" do\n  describe \"restrict\" do\n    it \"restricts type with same type\" do\n      mod = Program.new\n      mod.int32.restrict(mod.int32, MatchContext.new(mod, mod)).should eq(mod.int32)\n    end\n\n    it \"restricts type with another type\" do\n      mod = Program.new\n      mod.int32.restrict(mod.int16, MatchContext.new(mod, mod)).should be_nil\n    end\n\n    it \"restricts type with superclass\" do\n      mod = Program.new\n      mod.int32.restrict(mod.value, MatchContext.new(mod, mod)).should eq(mod.int32)\n    end\n\n    it \"restricts type with included module\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Mod\n        end\n\n        class Foo\n          include Mod\n        end\n        CRYSTAL\n\n      mod.types[\"Foo\"].restrict(mod.types[\"Mod\"], MatchContext.new(mod, mod)).should eq(mod.types[\"Foo\"])\n    end\n\n    it \"restricts virtual type with included module 1\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Moo; end\n        class Foo; include Moo; end\n        CRYSTAL\n\n      mod.t(\"Foo+\").restrict(mod.t(\"Moo\"), MatchContext.new(mod, mod)).should eq(mod.t(\"Foo+\"))\n    end\n\n    it \"restricts virtual type with included module 2\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Mxx; end\n        class Axx; end\n        class Bxx < Axx; include Mxx; end\n        class Cxx < Axx; include Mxx; end\n        class Dxx < Cxx; end\n        class Exx < Axx; end\n        CRYSTAL\n\n      mod.t(\"Axx+\").restrict(mod.t(\"Mxx\"), MatchContext.new(mod, mod)).should eq(mod.union_of(mod.t(\"Bxx+\"), mod.t(\"Cxx+\")))\n    end\n\n    it \"restricts module with another module\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Mxx; end\n        module Nxx; end\n        class Axx; include Mxx; end\n        class Bxx; include Nxx; end\n        class Cxx; include Mxx; include Nxx; end\n        class Dxx < Axx; include Nxx; end\n        class Exx < Bxx; include Mxx; end\n        CRYSTAL\n\n      mod.t(\"Mxx\").restrict(mod.t(\"Nxx\"), MatchContext.new(mod, mod)).should eq(mod.union_of(mod.t(\"Cxx\"), mod.t(\"Dxx\"), mod.t(\"Exx\")))\n    end\n\n    it \"restricts generic module instance with another module\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Mxx(T); end\n        module Nxx; end\n        class Axx; include Mxx(Int32); end\n        class Bxx; include Nxx; end\n        class Cxx; include Mxx(Int32); include Nxx; end\n        class Dxx < Axx; include Nxx; end\n        class Exx < Bxx; include Mxx(Int32); end\n        CRYSTAL\n\n      result = mod.generic_module(\"Mxx\", mod.int32).restrict(mod.t(\"Nxx\"), MatchContext.new(mod, mod))\n      result.should eq(mod.union_of(mod.t(\"Cxx\"), mod.t(\"Dxx\"), mod.t(\"Exx\")))\n    end\n\n    it \"restricts generic module instance with another generic module instance\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Mxx(T); end\n        module Nxx(T); end\n        class Axx; include Mxx(Int32); end\n        class Bxx; include Nxx(Int32); end\n        class Cxx; include Mxx(Int32); include Nxx(Int32); end\n        class Dxx < Axx; include Nxx(Int32); end\n        class Exx < Bxx; include Mxx(Int32); end\n        class Fxx; include Mxx(Int32); include Nxx(Char); end\n        class Gxx; include Mxx(Char); include Nxx(Int32); end\n        CRYSTAL\n\n      result = mod.generic_module(\"Mxx\", mod.int32).restrict(mod.generic_module(\"Nxx\", mod.int32), MatchContext.new(mod, mod))\n      result.should eq(mod.union_of(mod.t(\"Cxx\"), mod.t(\"Dxx\"), mod.t(\"Exx\")))\n    end\n\n    it \"restricts generic module instance with class\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Mxx(T); end\n        module Nxx; end\n        class Axx; include Mxx(Int32); end\n        class Bxx; include Nxx; end\n        class Cxx; include Mxx(Int32); include Nxx; end\n        class Dxx < Axx; include Nxx; end\n        class Exx < Bxx; include Mxx(Int32); end\n        CRYSTAL\n\n      result = mod.generic_module(\"Mxx\", mod.int32).restrict(mod.t(\"Nxx\"), MatchContext.new(mod, mod))\n      result.should eq(mod.union_of(mod.t(\"Cxx\"), mod.t(\"Dxx\"), mod.t(\"Exx\")))\n    end\n\n    it \"restricts module through generic include (#4287)\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        module Axx; end\n        module Bxx(T); include Axx; end\n        class Cxx; include Bxx(Int32); end\n        CRYSTAL\n\n      mod.t(\"Axx\").restrict(mod.t(\"Cxx\"), MatchContext.new(mod, mod)).should eq(mod.t(\"Cxx\"))\n    end\n\n    it \"restricts class against uninstantiated generic base class through multiple inheritance (1) (#9660)\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        class Axx(T); end\n        class Bxx(T) < Axx(T); end\n        class Cxx < Bxx(Int32); end\n        CRYSTAL\n\n      result = mod.t(\"Cxx\").restrict(mod.t(\"Axx\"), MatchContext.new(mod, mod))\n      result.should eq(mod.t(\"Cxx\"))\n    end\n\n    it \"restricts class against uninstantiated generic base class through multiple inheritance (2) (#9660)\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        class Axx(T); end\n        class Bxx(T) < Axx(T); end\n        class Cxx(T) < Bxx(T); end\n        CRYSTAL\n\n      result = mod.generic_class(\"Cxx\", mod.int32).restrict(mod.t(\"Axx\"), MatchContext.new(mod, mod))\n      result.should eq(mod.generic_class(\"Cxx\", mod.int32))\n    end\n\n    it \"restricts virtual generic class against uninstantiated generic subclass (1)\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        class Axx(T); end\n        class Bxx(T) < Axx(T); end\n        class Cxx < Bxx(Int32); end\n        CRYSTAL\n\n      result = mod.generic_class(\"Axx\", mod.int32).virtual_type.restrict(mod.generic_class(\"Bxx\", mod.int32), MatchContext.new(mod, mod))\n      result.should eq(mod.generic_class(\"Bxx\", mod.int32).virtual_type)\n    end\n\n    it \"restricts virtual generic class against uninstantiated generic subclass (2)\" do\n      mod = Program.new\n      mod.semantic parse(<<-CRYSTAL)\n        class Axx(T); end\n        class Bxx(T) < Axx(T); end\n        class Cxx(T) < Bxx(T); end\n        CRYSTAL\n\n      result = mod.generic_class(\"Axx\", mod.int32).virtual_type.restrict(mod.generic_class(\"Bxx\", mod.int32), MatchContext.new(mod, mod))\n      result.should eq(mod.generic_class(\"Bxx\", mod.int32).virtual_type)\n    end\n  end\n\n  describe \"restriction_of?\" do\n    describe \"Metaclass vs Metaclass\" do\n      it \"inserts typed Metaclass before untyped Metaclass\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : T.class) forall T\n            1\n          end\n\n          def foo(a : Int32.class)\n            true\n          end\n\n          foo(Int32)\n          CRYSTAL\n      end\n\n      it \"keeps typed Metaclass before untyped Metaclass\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : Int32.class)\n            true\n          end\n\n          def foo(a : T.class) forall T\n            1\n          end\n\n          foo(Int32)\n          CRYSTAL\n      end\n    end\n\n    describe \"Metaclass vs Path\" do\n      {% for type in [Object, Value, Class] %}\n        it \"inserts metaclass before {{ type }}\" do\n          assert_type(<<-CRYSTAL) { bool }\n            def foo(a : {{ type }})\n              1\n            end\n\n            def foo(a : Int32.class)\n              true\n            end\n\n            foo(Int32)\n            CRYSTAL\n        end\n\n        it \"keeps metaclass before {{ type }}\" do\n          assert_type(<<-CRYSTAL) { bool }\n            def foo(a : Int32.class)\n              true\n            end\n\n            def foo(a : {{ type }})\n              1\n            end\n\n            foo(Int32)\n            CRYSTAL\n        end\n      {% end %}\n\n      it \"doesn't error if path is undefined and method is not called (1) (#12516)\" do\n        assert_no_errors <<-CRYSTAL\n          def foo(a : Int32.class)\n          end\n\n          def foo(a : Foo)\n          end\n          CRYSTAL\n      end\n\n      it \"doesn't error if path is undefined and method is not called (2) (#12516)\" do\n        assert_no_errors <<-CRYSTAL\n          def foo(a : Foo)\n          end\n\n          def foo(a : Int32.class)\n          end\n          CRYSTAL\n      end\n    end\n\n    describe \"Path vs Path\" do\n      it \"inserts typed Path before untyped Path\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : T) forall T\n            1\n          end\n\n          def foo(a : Int32)\n            true\n          end\n\n          foo(1)\n          CRYSTAL\n      end\n\n      it \"keeps typed Path before untyped Path\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : Int32)\n            true\n          end\n\n          def foo(a : T) forall T\n            1\n          end\n\n          foo(1)\n          CRYSTAL\n      end\n    end\n\n    describe \"Generic vs Path\" do\n      it \"inserts typed Generic before untyped Path\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : T) forall T\n            1\n          end\n\n          def foo(a : Array(Int32))\n            true\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n\n      it \"keeps typed Generic before untyped Path\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : Array(Int32))\n            true\n          end\n\n          def foo(a : T) forall T\n            1\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n\n      it \"inserts untyped Generic before untyped Path\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : T) forall T\n            1\n          end\n\n          def foo(a : Array(T)) forall T\n            true\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n\n      it \"inserts untyped Generic before untyped Path (2)\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : T) forall T\n            1\n          end\n\n          def foo(a : Array)\n            true\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n\n      it \"keeps untyped Generic before untyped Path\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : Array(T)) forall T\n            true\n          end\n\n          def foo(a : T) forall T\n            1\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n    end\n\n    describe \"Generic vs Generic\" do\n      it \"inserts typed Generic before untyped Generic\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : Array(T)) forall T\n            1\n          end\n\n          def foo(a : Array(Int32))\n            true\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n\n      it \"keeps typed Generic before untyped Generic\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(a : Array(Int32))\n            true\n          end\n\n          def foo(a : Array(T)) forall T\n            1\n          end\n\n          foo(Array(Int32).new)\n          CRYSTAL\n      end\n    end\n\n    describe \"GenericClassType vs GenericClassInstanceType\" do\n      it \"inserts GenericClassInstanceType before GenericClassType\" do\n        assert_type(<<-CRYSTAL) { tuple_of([bool, int32]) }\n          class Foo(T)\n          end\n\n          def bar(a : Foo)\n            1\n          end\n\n          def bar(a : Foo(Int32))\n            true\n          end\n\n          {\n            bar(Foo(Int32).new),\n            bar(Foo(Float64).new)\n          }\n          CRYSTAL\n      end\n\n      it \"keeps GenericClassInstanceType before GenericClassType\" do\n        assert_type(<<-CRYSTAL) { tuple_of([bool, int32]) }\n          class Foo(T)\n          end\n\n          def bar(a : Foo(Int32))\n            true\n          end\n\n          def bar(a : Foo)\n            1\n          end\n\n          {\n            bar(Foo(Int32).new),\n            bar(Foo(Float64).new)\n          }\n          CRYSTAL\n      end\n\n      it \"works with classes in different namespaces\" do\n        assert_type(<<-CRYSTAL) { tuple_of([bool, int32]) }\n          class Foo(T)\n          end\n\n          class Mod::Foo(G)\n          end\n\n          def bar(a : Foo(Int32))\n            true\n          end\n\n          def bar(a : Mod::Foo)\n            1\n          end\n\n          {\n            bar(Foo(Int32).new),\n            bar(Mod::Foo(Int32).new)\n          }\n          CRYSTAL\n      end\n\n      it \"doesn't mix different generic classes\" do\n        assert_type(<<-CRYSTAL) { tuple_of([int32, bool]) }\n          class Foo(T)\n          end\n\n          class Bar(U)\n          end\n\n          def bar(a : Bar(Int32))\n            true\n          end\n\n          def bar(a : Foo)\n            1\n          end\n\n          {\n            bar(Foo(Int32).new),\n            bar(Bar(Int32).new)\n          }\n          CRYSTAL\n      end\n    end\n\n    describe \"NamedTuple vs NamedTuple\" do\n      it \"inserts more specialized NamedTuple before less specialized one\" do\n        assert_type(<<-CRYSTAL) { bool }\n          class Foo\n          end\n\n          class Bar < Foo\n          end\n\n          def foo(a : NamedTuple(x: Foo))\n            1\n          end\n\n          def foo(a : NamedTuple(x: Bar))\n            true\n          end\n\n          foo({x: Bar.new})\n          CRYSTAL\n      end\n\n      it \"keeps more specialized NamedTuple before less specialized one\" do\n        assert_type(<<-CRYSTAL) { bool }\n          class Foo\n          end\n\n          class Bar < Foo\n          end\n\n          def foo(a : NamedTuple(x: Bar))\n            true\n          end\n\n          def foo(a : NamedTuple(x: Foo))\n            1\n          end\n\n          foo({x: Bar.new})\n          CRYSTAL\n      end\n\n      it \"doesn't mix incompatible NamedTuples (#10238)\" do\n        assert_type(<<-CRYSTAL) { tuple_of([int32, bool]) }\n          def foo(a : NamedTuple(a: Int32))\n            1\n          end\n\n          def foo(a : NamedTuple(b: Int32))\n            true\n          end\n\n          {\n            foo({a: 1}),\n            foo({b: 1})\n          }\n          CRYSTAL\n      end\n    end\n\n    describe \"Path vs NumberLiteral\" do\n      it \"inserts constant before number literal of same value with generic arguments\" do\n        assert_type(<<-CRYSTAL) { bool }\n          X = 1\n\n          class Foo(N)\n          end\n\n          def foo(a : Foo(1))\n            'a'\n          end\n\n          def foo(a : Foo(X))\n            true\n          end\n\n          foo(Foo(1).new)\n          CRYSTAL\n      end\n\n      it \"inserts number literal before constant of same value with generic arguments\" do\n        assert_type(<<-CRYSTAL) { bool }\n          X = 1\n\n          class Foo(N)\n          end\n\n          def foo(a : Foo(X))\n            'a'\n          end\n\n          def foo(a : Foo(1))\n            true\n          end\n\n          foo(Foo(1).new)\n          CRYSTAL\n      end\n    end\n\n    describe \"free variables\" do\n      it \"inserts path before free variable with same name\" do\n        assert_type(<<-CRYSTAL) { tuple_of([char, bool]) }\n          def foo(x : Int32) forall Int32\n            true\n          end\n\n          def foo(x : Int32)\n            'a'\n          end\n\n          {foo(1), foo(\"\")}\n          CRYSTAL\n      end\n\n      it \"keeps path before free variable with same name\" do\n        assert_type(<<-CRYSTAL) { tuple_of([char, bool]) }\n          def foo(x : Int32)\n            'a'\n          end\n\n          def foo(x : Int32) forall Int32\n            true\n          end\n\n          {foo(1), foo(\"\")}\n          CRYSTAL\n      end\n\n      it \"inserts constant before free variable with same name\" do\n        assert_type(<<-CRYSTAL) { tuple_of([char, bool]) }\n          class Foo(T); end\n\n          X = 1\n\n          def foo(x : Foo(X)) forall X\n            true\n          end\n\n          def foo(x : Foo(X))\n            'a'\n          end\n\n          {foo(Foo(1).new), foo(Foo(2).new)}\n          CRYSTAL\n      end\n\n      it \"keeps constant before free variable with same name\" do\n        assert_type(<<-CRYSTAL) { tuple_of([char, bool]) }\n          class Foo(T); end\n\n          X = 1\n\n          def foo(x : Foo(X))\n            'a'\n          end\n\n          def foo(x : Foo(X)) forall X\n            true\n          end\n\n          {foo(Foo(1).new), foo(Foo(2).new)}\n          CRYSTAL\n      end\n\n      it \"inserts path before free variable even if free var resolves to a more specialized type\" do\n        assert_type(<<-CRYSTAL) { tuple_of([int32, int32, bool]) }\n          class Foo\n          end\n\n          class Bar < Foo\n          end\n\n          def foo(x : Bar) forall Bar\n            true\n          end\n\n          def foo(x : Foo)\n            1\n          end\n\n          {foo(Foo.new), foo(Bar.new), foo('a')}\n          CRYSTAL\n      end\n\n      it \"keeps path before free variable even if free var resolves to a more specialized type\" do\n        assert_type(<<-CRYSTAL) { tuple_of([int32, int32, bool]) }\n          class Foo\n          end\n\n          class Bar < Foo\n          end\n\n          def foo(x : Foo)\n            1\n          end\n\n          def foo(x : Bar) forall Bar\n            true\n          end\n\n          {foo(Foo.new), foo(Bar.new), foo('a')}\n          CRYSTAL\n      end\n    end\n\n    describe \"Union\" do\n      it \"handles redefinitions (1) (#12330)\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(x : Int32 | String)\n            'a'\n          end\n\n          def foo(x : ::Int32 | String)\n            true\n          end\n\n          foo(1)\n          CRYSTAL\n      end\n\n      it \"handles redefinitions (2) (#12330)\" do\n        assert_type(<<-CRYSTAL) { bool }\n          def foo(x : Int32 | String)\n            'a'\n          end\n\n          def foo(x : String | Int32)\n            true\n          end\n\n          foo(1)\n          CRYSTAL\n      end\n\n      it \"orders union before generic (#12330)\" do\n        assert_type(<<-CRYSTAL) { bool }\n          module Foo(T)\n          end\n\n          class Bar1\n            include Foo(Int32)\n          end\n\n          class Bar2\n            include Foo(Int32)\n          end\n\n          def foo(x : Foo(Int32))\n            'a'\n          end\n\n          def foo(x : Bar1 | Bar2)\n            true\n          end\n\n          foo(Bar1.new)\n          CRYSTAL\n      end\n    end\n\n    describe \"Underscore vs Path\" do\n      it \"inserts Path before underscore (#12854)\" do\n        assert_type(<<-CRYSTAL) { bool }\n          class Foo\n          end\n\n          def foo(x : _)\n            'a'\n          end\n\n          def foo(x : Foo)\n            true\n          end\n\n          foo(Foo.new)\n          CRYSTAL\n      end\n\n      it \"keeps underscore after Path (#12854)\" do\n        assert_type(<<-CRYSTAL) { bool }\n          class Foo\n          end\n\n          def foo(x : Foo)\n            true\n          end\n\n          def foo(x : _)\n            'a'\n          end\n\n          foo(Foo.new)\n          CRYSTAL\n      end\n\n      it \"works with splats and modules, under -Dpreview_overload_order (#12854)\" do\n        assert_type(<<-CRYSTAL, flags: \"preview_overload_order\") { bool }\n          module Foo\n          end\n\n          class Bar\n            include Foo\n          end\n\n          def foo(*x : _)\n            'a'\n          end\n\n          def foo(x : Foo)\n            true\n          end\n\n          foo(Bar.new)\n          CRYSTAL\n      end\n    end\n  end\n\n  it \"self always matches instance type in restriction\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def self.foo(x : self)\n          x\n        end\n      end\n\n      Foo.foo Foo.new\n      CRYSTAL\n  end\n\n  it \"self always matches instance type in return type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def self.foo : self\n          {{ @type }}\n          Foo.new\n        end\n      end\n      Foo.foo\n      CRYSTAL\n  end\n\n  it \"errors if using typeof\" do\n    assert_error <<-CRYSTAL, \"can't use typeof in type restrictions\"\n      def foo(x : typeof(1))\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"errors if using typeof inside generic type\" do\n    assert_error <<-CRYSTAL, \"can't use typeof in type restrictions\"\n      class Gen(T)\n      end\n\n      def foo(x : Gen(typeof(1)))\n      end\n\n      foo(Gen(Int32).new)\n      CRYSTAL\n  end\n\n  it \"errors if using typeof in block restriction\" do\n    assert_error <<-CRYSTAL, \"can't use 'typeof' here\"\n      def foo(&x : typeof(1) -> )\n        yield 1\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"errors if using typeof in block restriction\" do\n    assert_error <<-CRYSTAL, \"can't use typeof in type restriction\"\n      def foo(&x : -> typeof(1))\n        yield\n      end\n\n      foo {}\n      CRYSTAL\n  end\n\n  it \"passes #278\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'bar' to be String, not (Int32 | String)\"\n      def bar(x : String, y : String = nil)\n      end\n\n      bar(1 || \"\")\n      CRYSTAL\n  end\n\n  it \"errors on T::Type that's union when used from type restriction\" do\n    assert_error <<-CRYSTAL, \"undefined constant T::Baz\"\n      def foo(x : T) forall T\n        T::Baz\n      end\n\n      foo(1 || 1.5)\n      CRYSTAL\n  end\n\n  it \"errors on T::Type that's a union when used from block type restriction\" do\n    assert_error <<-CRYSTAL, \"undefined constant T::Baz\"\n      class Foo(T)\n        def self.foo(&block : T::Baz ->)\n        end\n      end\n\n      Foo(Int32 | Float64).foo { 1 + 2 }\n      CRYSTAL\n  end\n\n  it \"errors if can't find type on lookup\" do\n    assert_error <<-CRYSTAL, \"undefined constant Something\"\n      def foo(x : Something)\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"errors if can't find type on lookup with nested type\" do\n    assert_error <<-CRYSTAL, \"undefined constant Foo::Bar\"\n      def foo(x : Foo::Bar)\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"works with static array (#637)\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(x : UInt8[1])\n        1\n      end\n\n      def foo(x : UInt8[2])\n        'a'\n      end\n\n      x = uninitialized UInt8[2]\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"works with static array that uses underscore\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(x : UInt8[_])\n        'a'\n      end\n\n      x = uninitialized UInt8[2]\n      foo(x)\n      CRYSTAL\n  end\n\n  it \"works with generic compared to fixed (primitive) type\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo(T)\n      end\n\n      struct Float64\n        def /(other : Foo(_))\n          'a'\n        end\n      end\n\n      1.5 / Foo(Int32).new\n      CRYSTAL\n  end\n\n  it \"works with generic class metaclass vs. generic instance class metaclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n      end\n\n      def foo(x : Foo(Int32).class)\n        1\n      end\n\n      foo Foo(Int32)\n      CRYSTAL\n  end\n\n  it \"works with generic class metaclass vs. generic class metaclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n      end\n\n      def foo(x : Foo.class)\n        1\n      end\n\n      foo Foo(Int32)\n      CRYSTAL\n  end\n\n  it \"works with union against unions of generics\" do\n    assert_type(<<-CRYSTAL) { union_of(generic_class(\"Foo\", int32), generic_class(\"Foo\", float64)) }\n      class Foo(T)\n      end\n\n      def foo(x : Foo | Int32)\n        x\n      end\n\n      foo(Foo(Int32).new || Foo(Float64).new)\n      CRYSTAL\n  end\n\n  it \"should not let GenericChild(Base) pass as a GenericBase(Child) (#1294)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be GenericBase(Child), not GenericChild(Base)\"\n      class Base\n      end\n\n      class Child < Base\n      end\n\n      class GenericBase(T)\n      end\n\n      class GenericChild(T) < GenericBase(T)\n      end\n\n      def foo(x : GenericBase(Child))\n      end\n\n      foo GenericChild(Base).new\n      CRYSTAL\n  end\n\n  it \"allows passing recursive type to free var (#1076)\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo(T)\n      end\n\n      alias NestedParams = Nil | Foo(NestedParams)\n\n      class Bar(X)\n      end\n\n      def bar(other : Bar(Y)) forall Y\n        'a'\n      end\n\n      h1 = Bar(NestedParams).new\n      bar(h1)\n      CRYSTAL\n  end\n\n  it \"restricts class union type to overloads with classes\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of([uint8, uint16, uint32] of Type) }\n      def foo(x : Int32.class)\n        1_u8\n      end\n\n      def foo(x : String.class)\n        1_u16\n      end\n\n      def foo(x : Bool.class)\n        1_u32\n      end\n\n      a = 1 || \"foo\" || true\n      foo(a.class)\n      CRYSTAL\n  end\n\n  it \"restricts class union type to overloads with classes (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of([uint8, uint16] of Type) }\n      def foo(x : Int32.class)\n        1_u8\n      end\n\n      def foo(x : String.class)\n        1_u16\n      end\n\n      def foo(x : Bool.class)\n        1_u32\n      end\n\n      a = 1 || \"foo\"\n      foo(a.class)\n      CRYSTAL\n  end\n\n  it \"makes metaclass subclass pass parent metaclass restriction (#2079)\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"].metaclass }\n      class Foo; end\n\n      class Bar < Foo; end\n\n      def foo : Foo.class # offending return type restriction\n        Bar\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"matches virtual type against alias\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo\n      end\n\n      class Foo\n        include Moo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Bar\n      end\n\n      alias Alias = Moo\n\n      def foo(x : Alias)\n        1\n      end\n\n      foo(Baz.new.as(Bar))\n      CRYSTAL\n  end\n\n  it \"matches alias against alias in block type\" do\n    assert_type(<<-CRYSTAL) { types[\"Rec\"].metaclass }\n      class Foo(T)\n        def self.new(&block : -> T)\n          Foo(T).new\n        end\n\n        def initialize\n        end\n\n        def t\n          T\n        end\n      end\n\n      alias Rec = Nil | Array(Rec)\n\n      Foo.new { nil.as(Rec)}.t\n      CRYSTAL\n  end\n\n  it \"matches free variable for type variable\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", int32 }\n      class Foo(Type)\n        def initialize(x : Type)\n        end\n      end\n\n      Foo.new(1)\n      CRYSTAL\n  end\n\n  it \"restricts virtual metaclass type against metaclass (#3438)\" do\n    assert_type(<<-CRYSTAL) { types[\"Parent\"].metaclass.virtual_type! }\n      class Parent\n      end\n\n      class Child < Parent\n      end\n\n      def foo(x : Parent.class)\n        x\n      end\n\n      foo(Parent || Child)\n      CRYSTAL\n  end\n\n  it \"errors if using free var without forall\" do\n    assert_error <<-CRYSTAL, \"undefined constant T\"\n      def foo(x : T)\n        T\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"sets number as free variable (#2699)\" do\n    assert_error <<-CRYSTAL, \"expected argument #2 to 'foo' to be StaticArray(UInt8, 10), not StaticArray(UInt8, 11)\"\n      def foo(x : T[N], y : T[N]) forall T, N\n      end\n\n      x = uninitialized UInt8[10]\n      y = uninitialized UInt8[11]\n      foo(x, y)\n      CRYSTAL\n  end\n\n  it \"does not treat single path as free variable when given number (1) (#11859)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo(1)#foo' to be Foo(1), not Foo(2)\"\n      class Foo(T)\n        def foo(x : Foo(T))\n        end\n      end\n\n      Foo(1).new.foo(Foo(2).new)\n      CRYSTAL\n  end\n\n  it \"does not treat single path as free variable when given number (2) (#11859)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Foo(1), not Foo(2)\"\n      X = 1\n\n      class Foo(T)\n      end\n\n      def foo(x : Foo(X))\n      end\n\n      foo(Foo(2).new)\n      CRYSTAL\n  end\n\n  it \"matches number in bound free variable (#13605)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", 1.int32 }\n      class Foo(T)\n      end\n\n      def foo(x : Foo(T), y : Foo(T)) forall T\n        y\n      end\n\n      foo(Foo(1).new, Foo(1).new)\n      CRYSTAL\n  end\n\n  it \"sets number as unbound generic type var (#13110)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", 1.int32 }\n      class Foo(T)\n        def self.foo(x : Foo(T))\n          x\n        end\n      end\n\n      Foo.foo(Foo(1).new)\n      CRYSTAL\n  end\n\n  it \"restricts aliased typedef type (#9474)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib A\n        alias B = Int32\n      end\n\n      alias C = A::B\n\n      def foo(x : C)\n        1\n      end\n\n      x = uninitialized C\n      foo x\n      CRYSTAL\n  end\n\n  it \"errors if using Tuple with named args\" do\n    assert_error <<-CRYSTAL, \"can only instantiate NamedTuple with named arguments\"\n      def foo(x : Tuple(a: Int32))\n      end\n\n      foo({1})\n      CRYSTAL\n  end\n\n  it \"doesn't error if using Tuple with no args\" do\n    assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n      def foo(x : Tuple())\n        x\n      end\n\n      def bar(*args : *T) forall T\n        args\n      end\n\n      foo(bar)\n      CRYSTAL\n  end\n\n  it \"errors if using NamedTuple with positional args\" do\n    assert_error <<-CRYSTAL, \"can only instantiate NamedTuple with named arguments\"\n      def foo(x : NamedTuple(Int32))\n      end\n\n      foo({a: 1})\n      CRYSTAL\n  end\n\n  it \"doesn't error if using NamedTuple with no args\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({} of String => Type) }\n      def foo(x : NamedTuple())\n        x\n      end\n\n      def bar(**opts : **T) forall T\n        opts\n      end\n\n      foo(bar)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/return_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: return\" do\n  it \"infers return type\" do\n    assert_type(\"def foo; return 1; end; foo\") { int32 }\n  end\n\n  it \"infers return type with many returns (1)\" do\n    assert_type(\"def foo; if true; return 1; end; 'a'; end; foo\") { union_of(int32, char) }\n  end\n\n  it \"infers return type with many returns (2)\" do\n    assert_type(\"def foo; if 1 == 1; return 1; end; 'a'; end; foo\", inject_primitives: true) { union_of(int32, char) }\n  end\n\n  it \"errors on return in top level\" do\n    assert_error \"return\",\n      \"can't return from top level\"\n  end\n\n  it \"types return if true\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def bar\n        return if true\n        1\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"can use type var as return type (#1226)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module Moo(T)\n      end\n\n      class Foo(T)\n        def initialize(@x : T)\n        end\n\n        def foo : T\n          @x\n        end\n      end\n\n      Foo.new(1).foo\n      CRYSTAL\n  end\n\n  it \"can use type var as return type with an included generic module\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      module Moo(T)\n        def moo : T\n          1.5\n        end\n      end\n\n      class Foo(T)\n        include Moo(Float64)\n\n        def initialize(@x : T)\n        end\n      end\n\n      Foo.new(1).moo\n      CRYSTAL\n  end\n\n  it \"can use type var as return type with an inherited generic class\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      class Moo(T)\n        def moo : T\n          1.5\n        end\n      end\n\n      class Foo(T) < Moo(Float64)\n        def initialize(@x : T)\n        end\n      end\n\n      Foo.new(1).moo\n      CRYSTAL\n  end\n\n  it \"doesn't confuse return type from base class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        class Baz\n          def foo\n            1\n          end\n        end\n\n        def x : Baz\n          Baz.new\n        end\n      end\n\n      class Bar < Foo\n        class Baz\n        end\n      end\n\n      Bar.new.x.foo\n      CRYSTAL\n  end\n\n  it \"allows returning NoReturn instead of the wanted type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      module Moo\n        def bar : Int32\n          foo\n          1\n        end\n      end\n\n      class Foo\n        include Moo\n\n        def foo\n          # Not implemented\n          LibC.exit\n        end\n      end\n\n      foo = Foo.new\n      foo.bar\n      CRYSTAL\n  end\n\n  it \"types bug (#1823)\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      def test\n        b = nil\n\n        begin\n        rescue\n          b ? return 1 : return 2\n        end\n\n        b\n      end\n\n      test\n      CRYSTAL\n  end\n\n  it \"allows nilable return type to match subclasses (#1735)\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Bar\"] }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def test : Foo?\n        if true\n          Bar.new\n        else\n          nil\n        end\n      end\n\n      test\n      CRYSTAL\n  end\n\n  it \"can use free var in return type (#2492)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { float64 }\n      def self.demo(a : A, &block : A -> B) : B forall A, B\n        block.call(a)\n      end\n\n      z = demo(1) do |x|\n        x.to_f\n      end\n      z\n      CRYSTAL\n  end\n\n  it \"can use non-type free var in return type (#6543)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Foo\", 1.int32 }\n      class Foo(A)\n      end\n\n      def foo(a : Foo(P)) : Foo(P) forall P\n        a\n      end\n\n      foo(Foo(1).new)\n      CRYSTAL\n  end\n\n  it \"can use non-type free var in return type (2) (#6543)\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Matrix\", 3.int32, 4.int32 }\n      class Matrix(N, M)\n        def *(other : Matrix(M, P)) : Matrix(N, P) forall P\n          Matrix(N, P).new\n        end\n      end\n\n      Matrix(3, 2).new * Matrix(2, 4).new\n      CRYSTAL\n  end\n\n  it \"errors if non-type free var cannot be inferred\" do\n    assert_error <<-CRYSTAL, \"undefined constant P\"\n      class Foo(A)\n      end\n\n      def foo(a) : Foo(P) forall P\n        a\n      end\n\n      foo(Foo(1).new)\n      CRYSTAL\n  end\n\n  it \"forms a tuple from multiple return values\" do\n    assert_type(\"def foo; return 1, 1.0; end; foo\") { tuple_of([int32, float64]) }\n  end\n\n  it \"flattens splats inside multiple return values\" do\n    assert_type(\"def foo; return 1, *{1.0, 'a'}, true; end; foo\") { tuple_of([int32, float64, char, bool]) }\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/sizeof_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: sizeof\" do\n  {% for name in %w(sizeof instance_sizeof alignof instance_alignof).map(&.id) %}\n    it \"types {{name}}\" do\n      assert_type(\"{{name}}(Reference)\") { int32 }\n    end\n\n    it \"types {{name}} NoReturn (missing type) (#5717)\" do\n      assert_type(\"x = nil; x ? {{name}}(typeof(x)) : 1\") { int32 }\n    end\n  {% end %}\n\n  it \"errors on sizeof uninstantiated generic type (#6415)\" do\n    assert_error \"sizeof(Array)\", \"can't take size of uninstantiated generic type Array(T)\"\n  end\n\n  it \"gives error if using instance_sizeof on something that's not a class\" do\n    assert_error <<-CRYSTAL, \"instance_sizeof can only be used with a class, but Int32 is a struct\"\n      instance_sizeof(Int32)\n      CRYSTAL\n  end\n\n  it \"gives error if using instance_sizeof on a struct\" do\n    assert_error <<-CRYSTAL, \"instance_sizeof can only be used with a class, but Foo is a struct\"\n      struct Foo\n      end\n\n      instance_sizeof(Foo)\n      CRYSTAL\n  end\n\n  it \"gives error if using instance_sizeof on an abstract struct (#11855)\" do\n    assert_error <<-CRYSTAL, \"instance_sizeof can only be used with a class, but Foo is a struct\"\n      abstract struct Foo\n      end\n\n      instance_sizeof(Foo)\n      CRYSTAL\n  end\n\n  it \"gives error if using instance_sizeof on an abstract struct with multiple subtypes (#11855)\" do\n    assert_error <<-CRYSTAL, \"instance_sizeof can only be used with a class, but Foo is a struct\"\n      abstract struct Foo\n      end\n\n      struct Child1 < Foo\n      end\n\n      struct Child2 < Foo\n      end\n\n      instance_sizeof(Foo)\n      CRYSTAL\n  end\n\n  it \"gives error if using instance_sizeof on a module\" do\n    assert_error <<-CRYSTAL, \"instance_sizeof can only be used with a class, but Moo is a module\"\n      module Moo\n      end\n\n      instance_sizeof(Moo)\n      CRYSTAL\n  end\n\n  it \"gives error if using instance_sizeof on a metaclass\" do\n    assert_error <<-CRYSTAL, \"instance_sizeof can only be used with a class, but Foo.class is a metaclass\"\n      class Foo\n      end\n\n      instance_sizeof(Foo.class)\n      CRYSTAL\n  end\n\n  it \"gives error if using instance_sizeof on a generic type without type vars\" do\n    assert_error \"instance_sizeof(Array)\", \"can't take instance size of uninstantiated generic type Array(T)\"\n  end\n\n  it \"gives error if using instance_sizeof on a union type (#8349)\" do\n    assert_error \"instance_sizeof(Int32 | Bool)\",\n      \"instance_sizeof can only be used with a class, but (Bool | Int32) is a union\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/special_vars_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: special vars\" do\n  [\"$~\", \"$?\"].each do |name|\n    it \"infers #{name}\" do\n      assert_type(<<-CRYSTAL) { nilable string }\n        class Object; def not_nil!; self; end; end\n\n        def foo\n          #{name} = \"hey\"\n        end\n\n        foo\n        #{name}\n        CRYSTAL\n    end\n\n    it \"types #{name} when not defined as no return\" do\n      assert_type(<<-CRYSTAL) { no_return }\n        require \"prelude\"\n\n        #{name}\n        CRYSTAL\n    end\n\n    it \"types #{name} when not defined as no return (2)\" do\n      assert_type(<<-CRYSTAL) { nilable string }\n        class Object; def not_nil!; self; end; end\n\n        def foo\n          #{name} = \"hey\"\n          #{name}\n        end\n\n        foo\n        CRYSTAL\n    end\n\n    it \"errors if assigning #{name} at top level\" do\n      assert_error <<-CRYSTAL, \"'#{name}' can't be assigned at the top level\"\n        #{name} = \"hey\"\n        CRYSTAL\n    end\n  end\n\n  it \"infers when assigning inside block\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      class Object; def not_nil!; self; end; end\n\n      def bar\n        yield\n      end\n\n      def foo\n        bar do\n          $~ = \"hello\"\n        end\n      end\n\n      foo\n      $~\n      CRYSTAL\n  end\n\n  it \"infers in block\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $~ = \"hey\"\n        yield\n      end\n\n      a = nil\n      foo do\n        a = $~\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"infers in block with nested block\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      class Object; def not_nil!; self; end; end\n\n      def bar\n        yield\n      end\n\n      def foo\n        bar do\n          $~ = \"hey\"\n          yield\n        end\n      end\n\n      a = nil\n      foo do\n        a = $~\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"infers after block\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      class Object; def not_nil!; self; end; end\n\n      def foo\n        $~ = \"hey\"\n        yield\n      end\n\n      foo do\n      end\n      $~\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/splat_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate macro expect_splat(e_arg, e_arg_index, e_obj, e_obj_index)\n  arg.name.should eq({{e_arg}})\n  arg_index.should eq({{e_arg_index}})\n  obj.should eq({{e_obj}})\n  obj_index.should eq({{e_obj_index}})\nend\n\ndescribe \"Semantic: splat\" do\n  it \"splats\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, float64, char] of Type) }\n      def foo(*args)\n        args\n      end\n\n      foo 1, 1.5, 'a'\n      CRYSTAL\n  end\n\n  it \"errors on zero args with named arg and splat\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments\"\n      def foo(x, y = 1, *z)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"redefines method with splat (bug #248)\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo\n        def bar(*x)\n          1\n        end\n      end\n\n      class Foo\n        def bar(*x)\n          'a'\n        end\n      end\n\n      Foo.new.bar 1\n      CRYSTAL\n  end\n\n  it \"errors if splatting union\" do\n    assert_error <<-CRYSTAL, \"not yet supported\"\n      a = {1} || {1, 2}\n      foo *a\n      CRYSTAL\n  end\n\n  it \"errors if splatting non-tuple type in call arguments\" do\n    assert_error <<-CRYSTAL, \"argument to splat must be a tuple, not Int32\"\n      foo *1\n      CRYSTAL\n  end\n\n  it \"errors if splatting non-tuple type in return values\" do\n    assert_error <<-CRYSTAL, \"argument to splat must be a tuple, not Int32\"\n      def foo\n        return *1\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"forwards tuple with an extra argument\" do\n    assert_type(<<-CRYSTAL) { tuple_of [int32] of TypeVar }\n      def foo(*args)\n        bar 1, *args\n      end\n\n      def bar(name, *args)\n        args\n      end\n\n      x = foo 2\n      x\n      CRYSTAL\n  end\n\n  it \"forwards tuple in return statement\" do\n    assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, char]), int32, char]) }\n      def foo(*args)\n        return args, *args\n      end\n\n      foo 1, 'a'\n      CRYSTAL\n  end\n\n  it \"can splat after type filter left it as a tuple (#442)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def output(x, y)\n        x + y\n      end\n\n      b = {1, 2} || nil\n      if b\n        output(*b)\n      else\n        4\n      end\n      CRYSTAL\n  end\n\n  it \"errors if doesn't match splat with type restriction\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(*args : Int32)\n      end\n\n      foo 1, 2, 3, 'a'\n      CRYSTAL\n  end\n\n  it \"works if matches splat with type restriction\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(*args : Int32)\n        args[0]\n      end\n\n      foo 1, 2, 3\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(arg : Int32)\n        1\n      end\n\n      def foo(*args : Int32)\n        'a'\n      end\n\n      foo 1\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (2)\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(arg : Int32)\n        1\n      end\n\n      def foo(*args : Int32)\n        'a'\n      end\n\n      foo 1, 2, 3\n      CRYSTAL\n  end\n\n  it \"errors if doesn't match splat with type restriction because of zero arguments\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'foo' (given 0, expected 1+)\"\n      def foo(*args : Int32)\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (3)\" do\n    assert_type(<<-CRYSTAL) { string }\n      def foo(*args : Char)\n        \"hello\"\n      end\n\n      def foo(*args : Int32)\n        1.5\n      end\n\n      foo 'a', 'b', 'c'\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (4)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(*args : Char)\n        \"hello\"\n      end\n\n      def foo(*args : Int32)\n        1.5\n      end\n\n      foo 1, 2, 3\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (5)\" do\n    assert_type(<<-CRYSTAL) { string }\n      def foo(*args : Int32)\n        \"hello\"\n      end\n\n      def foo\n        1.5\n      end\n\n      foo 1, 2, 3\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (6)\" do\n    assert_type(<<-CRYSTAL) { float64 }\n      def foo(*args : Int32)\n        \"hello\"\n      end\n\n      def foo\n        1.5\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"overloads with type restriction and splat (7)\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(*args)\n        foo args\n      end\n\n      def foo(args : Tuple)\n        'a'\n      end\n\n      foo 1, 2, 3\n      CRYSTAL\n  end\n\n  it \"overloads with splat against method with two arguments (#986) (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(a, b)\n        1\n      end\n\n      def foo(*arg)\n        'a'\n      end\n\n      foo \"bar\", \"baz\"\n      CRYSTAL\n  end\n\n  it \"overloads with splat against method with two arguments (#986) (2)\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(a, b)\n        1\n      end\n\n      def foo(*arg)\n        'a'\n      end\n\n      foo \"bar\"\n      CRYSTAL\n  end\n\n  it \"calls super with implicit splat arg (#1001)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(name)\n          name\n        end\n      end\n\n      class Bar < Foo\n        def foo(*args)\n          super\n        end\n      end\n\n      Bar.new.foo 1\n      CRYSTAL\n  end\n\n  it \"splats arg and splat against splat (1) (#1042)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(a : Bool, *b : Int32)\n        1\n      end\n\n      def foo(*b : Int32)\n        'a'\n      end\n\n      foo(true, 3, 4, 5)\n      CRYSTAL\n  end\n\n  it \"splats arg and splat against splat (2) (#1042)\" do\n    assert_type(<<-CRYSTAL) { char }\n      def foo(a : Bool, *b : Int32)\n        1\n      end\n\n      def foo(*b : Int32)\n        'a'\n      end\n\n      foo(3, 4, 5)\n      CRYSTAL\n  end\n\n  it \"gives correct error when forwarding splat\" do\n    assert_error <<-CRYSTAL, \"wrong number of arguments for 'foo' (given 2, expected 1)\"\n      def foo(x : Int)\n      end\n\n      def bar(*args)\n        foo *args\n      end\n\n      bar 'a', 1\n      CRYSTAL\n  end\n\n  it \"gives correct error when forwarding splat (2)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Int, not Char\"\n      def foo(x : Int, y : Int, z : Int, w : Int)\n      end\n\n      def bar(*args)\n        foo 'a', *args\n      end\n\n      bar 1, \"a\", 1.7\n      CRYSTAL\n  end\n\n  it \"doesn't crash on non-match (#2521)\" do\n    assert_error <<-CRYSTAL, \"missing arguments: c, d\"\n      def test_func(a : String, *b, c, d)\n      end\n\n      if true\n        val = \"\"\n      end\n\n      test_func(val, 1, 2, 3, 4, 5)\n      CRYSTAL\n  end\n\n  it \"says no overload matches on type restrictions past the splat arg\" do\n    assert_error <<-CRYSTAL, \"missing arguments: a, b\"\n      def foo(*z, a : String, b : String)\n      end\n\n      foo(1, 2, 3, (\"foo\" || nil), (\"bar\" || nil))\n      CRYSTAL\n  end\n\n  it \"says missing argument because positional args don't match past splat\" do\n    assert_error <<-CRYSTAL, \"missing argument: z\"\n      def foo(x, *y, z)\n      end\n\n      foo 1, 2\n      CRYSTAL\n  end\n\n  it \"allows default value after splat index\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, tuple_of([bool, float64]), int32]) }\n      def foo(x, *y, z = 10)\n        {x, y, z}\n      end\n\n      foo 'a', true, 1.5\n      CRYSTAL\n  end\n\n  it \"uses bare *\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]) }\n      def foo(x, *, y)\n        {x, y}\n      end\n\n      foo 10, y: 'a'\n      CRYSTAL\n  end\n\n  it \"uses bare *, doesn't let more args\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      def foo(x, *, y)\n      end\n\n      foo 10, 20, y: 30\n      CRYSTAL\n  end\n\n  it \"uses splat restriction\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char, bool]).metaclass }\n      def foo(*args : *T) forall T\n        T\n      end\n\n      foo 1, 'a', false\n      CRYSTAL\n  end\n\n  it \"uses splat restriction, matches empty\" do\n    assert_type(<<-CRYSTAL) { tuple_of([] of Type).metaclass }\n      def foo(*args : *T) forall T\n        T\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"uses splat restriction after non-splat arguments (#5037)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([char, string]).metaclass }\n      def foo(x, *y : *T) forall T\n        T\n      end\n\n      foo 1, 'a', \"\"\n      CRYSTAL\n  end\n\n  it \"uses splat restriction with concrete type\" do\n    assert_error <<-CRYSTAL, \"no overload matches\"\n      struct Tuple(*T)\n        def self.foo(*args : *T)\n        end\n      end\n\n      Tuple(Int32, Char).foo(1, true)\n      CRYSTAL\n  end\n\n  it \"method with splat and optional named argument matches zero args call (#2746)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n      def foo(*args, k1 = nil)\n        args\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"method with default arguments and splat matches call with one arg (#2766)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(a = nil, b = nil, *, c = nil)\n        a\n      end\n\n      foo(10)\n      CRYSTAL\n  end\n\n  it \"accesses T when empty, via module\" do\n    assert_type(<<-CRYSTAL) { no_return.metaclass }\n      module Moo(T)\n        def t\n          T\n        end\n      end\n\n      struct Tuple\n        include Moo(Union(*T))\n\n        def self.new(*args)\n          args\n        end\n      end\n\n      Tuple.new.t\n      CRYSTAL\n  end\n\n  it \"matches type splat with splat in generic type (1)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([char, string]).metaclass, bool.metaclass]) }\n      class Foo(*T)\n      end\n\n      def method(x : Foo(A, *B, C)) forall A, B, C\n        {A, B, C}\n      end\n\n      foo = Foo(Int32, Char, String, Bool).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"matches type splat with splat in generic type (2)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([char, string]).metaclass, bool.metaclass]) }\n      class Foo(T, *U, V)\n        def t\n          {T, U, V}\n        end\n      end\n\n      def method(x : Foo(*A)) forall A\n        x.t\n      end\n\n      foo = Foo(Int32, Char, String, Bool).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"matches instantiated generic with splat in generic type\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo(*T)\n      end\n\n      def method(x : Foo(Int32, String))\n        'a'\n      end\n\n      foo = Foo(Int32, String).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"doesn't match splat in generic type with unsplatted tuple (#10164)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'method' to be Foo(Tuple(Int32, String)), not Foo(Int32, String)\"\n      class Foo(*T)\n      end\n\n      def method(x : Foo(Tuple(Int32, String)))\n        'a'\n      end\n\n      foo = Foo(Int32, String).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"matches partially instantiated generic with splat in generic type\" do\n    assert_type(<<-CRYSTAL) { string.metaclass }\n      class Foo(*T)\n      end\n\n      def method(x : Foo(Int32, T)) forall T\n        T\n      end\n\n      foo = Foo(Int32, String).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"errors with too few non-splat type arguments (1)\" do\n    assert_error <<-CRYSTAL, \"wrong number of type vars for Foo(T, U, *V) (given 1, expected 2+)\"\n      class Foo(T, U, *V)\n      end\n\n      def method(x : Foo(Int32))\n      end\n\n      foo = Foo(Int32, String).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"errors with too few non-splat type arguments (2)\" do\n    assert_error <<-CRYSTAL, \"wrong number of type vars for Foo(T, U, *V) (given 1, expected 2+)\"\n      class Foo(T, U, *V)\n      end\n\n      def method(x : Foo(A)) forall A\n      end\n\n      foo = Foo(Int32, String).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"errors with too many non-splat type arguments\" do\n    assert_error <<-CRYSTAL, \"wrong number of type vars for Foo(A) (given 2+, expected 1)\"\n      class Foo(A)\n      end\n\n      def method(x : Foo(T, U, *V)) forall T, U, V\n      end\n\n      foo = Foo(Int32).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"errors if using two splat indices on restriction\" do\n    assert_error <<-CRYSTAL, \"can't specify more than one splat in restriction\"\n      class Foo(*T)\n      end\n\n      def method(x : Foo(A, *B, *C)) forall A, B, C\n        {A, B, C}\n      end\n\n      foo = Foo(Int32, Char, String, Bool).new\n      method(foo)\n      CRYSTAL\n  end\n\n  it \"matches with splat\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) }\n      def foo(&block : *{Int32, Int32} -> U) forall U\n        tup = {1, 2}\n        yield *tup\n      end\n\n      foo do |x, y|\n        {x, y}\n      end\n      CRYSTAL\n  end\n\n  it \"matches with tuple splat inside explicit Union\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def foo(x : Union(*{Int32, String}))\n        x\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  it \"matches with type var splat inside explicit Union\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(*T)\n        def self.foo(x : Union(*T))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo(1)\n      CRYSTAL\n  end\n\n  it \"matches with type var splat inside explicit Union (2)\" do\n    assert_type(<<-CRYSTAL) { string }\n      class Foo(*T)\n        def self.foo(x : Union(*T))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo(\"\")\n      CRYSTAL\n  end\n\n  it \"matches with type var splat inside explicit Union, when all splat elements match\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(*T)\n        def self.foo(x : Union(*T))\n          x\n        end\n      end\n\n      Foo(Int32 | Bool, Int32 | String, Int32 | Char).foo(1)\n      CRYSTAL\n  end\n\n  it \"matches with type var splat inside explicit Union, when one splat fails entirely\" do\n    assert_type(<<-CRYSTAL) { bool }\n      class Foo(*T)\n        def self.foo(x : Union(*T, Bool))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo(true)\n      CRYSTAL\n  end\n\n  it \"matches with type var splat inside explicit Union, when non-splat vars fail\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(*T)\n        def self.foo(x : Union(*T, Char, Bool))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo(1)\n      CRYSTAL\n  end\n\n  it \"matches with type var and splat of itself inside explicit Union\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, string]) }\n      class Foo(*T)\n        def self.foo(x : Union(T, *T))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo({1, \"\"})\n      CRYSTAL\n  end\n\n  it \"matches with type var and splat of itself inside explicit Union (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(*T)\n        def self.foo(x : Union(T, *T))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo(1)\n      CRYSTAL\n  end\n\n  it \"matches with type var and splat of itself inside explicit Union (3)\" do\n    assert_type(<<-CRYSTAL) { string }\n      class Foo(*T)\n        def self.foo(x : Union(T, *T))\n          x\n        end\n      end\n\n      Foo(Int32, String).foo(\"\")\n      CRYSTAL\n  end\n\n  it \"doesn't match free var type splats inside explicit Union\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Union(*T), not Int32\"\n      def foo(x : Union(*T)) forall T\n        x\n      end\n\n      foo(1)\n      CRYSTAL\n  end\n\n  describe Splat do\n    it \"without splat\" do\n      a_def = Def.new(\"foo\", args: [Arg.new(\"x\"), Arg.new(\"y\")])\n      objs = [10, 20]\n\n      i = 0\n      Splat.before(a_def, objs) do |arg, arg_index, obj, obj_index|\n        case i\n        when 0\n          expect_splat \"x\", 0, 10, 0\n        when 1\n          expect_splat \"y\", 1, 20, 1\n        else\n          fail \"shouldn't happen\"\n        end\n        i += 1\n      end\n      i.should eq(2)\n\n      Splat.at(a_def, objs) do\n        fail \"expected at_splat not to invoke the block\"\n      end\n    end\n\n    it \"with splat\" do\n      a_def = Def.new(\"foo\", args: [Arg.new(\"a1\"), Arg.new(\"a2\"), Arg.new(\"a3\"), Arg.new(\"a4\")], splat_index: 2)\n      objs = [10, 20, 30, 40, 50, 60]\n\n      i = 0\n      Splat.before(a_def, objs) do |arg, arg_index, obj, obj_index|\n        case i\n        when 0\n          expect_splat \"a1\", 0, 10, 0\n        when 1\n          expect_splat \"a2\", 1, 20, 1\n        else\n          fail \"shouldn't happen\"\n        end\n        i += 1\n      end\n      i.should eq(2)\n\n      i = 0\n      Splat.at(a_def, objs) do |arg, arg_index, obj, obj_index|\n        case i\n        when 0\n          expect_splat \"a3\", 2, 30, 2\n        when 1\n          expect_splat \"a3\", 2, 40, 3\n        when 2\n          expect_splat \"a3\", 2, 50, 4\n        when 3\n          expect_splat \"a3\", 2, 60, 5\n        else\n          fail \"shouldn't happen\"\n        end\n        i += 1\n      end\n      i.should eq(4)\n    end\n  end\n\n  it \"doesn't shift a call's location\" do\n    result = semantic <<-CRYSTAL\n      class Foo\n        def bar(x)\n          bar(*{\"test\"})\n        end\n      end\n      Foo.new.bar(\"test\")\n      CRYSTAL\n    program = result.program\n    a_type = program.types[\"Foo\"].as(NonGenericClassType)\n    a_def = a_type.def_instances.values[0]\n\n    a_def.location.should eq Location.new(\"\", line_number: 2, column_number: 3)\n    a_def.body.location.should eq Location.new(\"\", line_number: 3, column_number: 5)\n  end\n\n  it \"normalizes with filename\" do\n    result = semantic <<-CRYSTAL\n      def foo(x, y)\n      end\n\n      #<loc:\"foo.cr\",1,1>foo(*{1, 2})\n      #<loc:\"bar.cr\",1,1>foo(*{3, 4})\n      CRYSTAL\n\n    result.node.to_s.should end_with <<-CRYSTAL\n      __temp_cd6ae5dd_1 = {1, 2}\n      foo(__temp_cd6ae5dd_1[0], __temp_cd6ae5dd_1[1])\n      __temp_fbcf3d84_1 = {3, 4}\n      foo(__temp_fbcf3d84_1[0], __temp_fbcf3d84_1[1])\\n\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/ssa_spec.cr",
    "content": "require \"../../spec_helper\"\n\ninclude Crystal\n\ndescribe \"Semantic: ssa\" do\n  it \"types a redefined variable\" do\n    assert_type(<<-CRYSTAL) { char }\n      a = 1\n      a = 'a'\n      a\n      CRYSTAL\n  end\n\n  it \"types a var inside an if without previous definition\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      if 1 == 1\n        a = 1\n      else\n        a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var inside an if with previous definition\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = \"hello\"\n      if 1 == 1\n        a = 1\n      else\n        a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var inside an if without change in then\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = 1\n      if 1 == 1\n      else\n        a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var inside an if without change in else\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = 1\n      if 1 == 1\n        a = 'a'\n      else\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var inside an if without definition in else\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable char }\n      if 1 == 1\n        a = 'a'\n      else\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var inside an if without definition in then\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable char }\n      if 1 == 1\n      else\n        a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var with an if but without change\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = 1\n      if 1 == 1\n      else\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var with an if with nested if\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      if 1 == 2\n        a = 1\n      else\n        if 2 == 3\n        end\n        a = 4\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is re-assigned in a block\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      def foo\n        yield\n      end\n\n      a = 1\n      foo do\n        a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is re-assigned in a while\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = 1\n      while 1 == 2\n        a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is re-assigned in a while and used in condition\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char) }\n      a = 1\n      while b = a\n        a = 'a'\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"types a var that is re-assigned in a while in next and used in condition\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = 1\n      while b = a\n        if 1 == 1\n          a = 'a'\n          next\n        end\n        a = 1\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"types a var that is declared in a while\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      while 1 == 2\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is re-assigned in a while condition\" do\n    assert_type(<<-CRYSTAL) { char }\n      a = 1\n      while a = 'a'\n        a = \"hello\"\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is declared in a while condition\" do\n    assert_type(<<-CRYSTAL) { char }\n      while a = 'a'\n        a = \"hello\"\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is declared in a while with out\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(char, int32) }\n      lib LibC\n        fun foo(x : Int32*)\n      end\n\n      a = 'a'\n      while 1 == 2\n        LibC.foo(out x)\n        a = x\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var after begin ensure as having last type\" do\n    assert_type(<<-CRYSTAL) { string }\n      a = 1.5\n      begin\n        a = 2\n        a = 'a'\n        a = \"hello\"\n      ensure\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var after begin ensure as having last type (2)\" do\n    assert_type(<<-CRYSTAL) { char }\n      begin\n        a = 2\n        a = 'a'\n      ensure\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't change type to nilable inside if\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        yield\n      end\n\n      def bar\n        if 1 == 2\n          l = 1\n          foo {}\n          l\n        else\n          2\n        end\n      end\n\n      x = bar\n      CRYSTAL\n  end\n\n  it \"types if with return in then\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        if 1 == 1\n          a = 1\n        else\n          return 2\n        end\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types if with return in then with assign\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        if 1 == 1\n          a = 1\n        else\n          a = 'a'\n          return 2\n        end\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types if with return in else\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        if 1 == 1\n          return 2\n        else\n          a = 1\n        end\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types if with return in else with assign\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        if 1 == 1\n          a = 'a'\n          return 2\n        else\n          a = 1\n        end\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types if with return in both branches\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        if 1 == 1\n          if 2 == 2\n            a = 'a'\n            return 2\n          else\n            a = false\n            return 3\n          end\n        else\n          a = 1\n        end\n        a\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"types if with unreachable in then\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      lib LibC\n        fun exit : NoReturn\n      end\n\n      if 1 == 1\n        a = 1\n      else\n        a = 'a'\n        LibC.exit\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"types if with break in then\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      b = 1\n\n      while 1 == 2\n        if 1 == 1\n          a = 1\n        else\n          a = 'a'\n          break\n        end\n        b = a\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types if with next in then\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      b = 1\n\n      while 1 == 2\n        if 1 == 1\n          a = 1\n        else\n          a = 'a'\n          next\n        end\n        b = a\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types while with break\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = 1\n\n      while 1 == 2\n        if 1 == 1\n          a = 'a'\n          break\n        end\n        a = 1\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"types while with break with new var\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable char }\n      while 1 == 2\n        if 1 == 1\n          b = 'a'\n          break\n        end\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types while with break doesn't infect initial vars\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = 1\n      b = 1\n\n      while 1 == 2\n        b = a\n        if 1 == 1\n          a = 'a'\n          break\n        end\n        a = 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types a var that is declared in a while condition with break before re-assignment\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { char }\n      while a = 'a'\n        break if 1 == 1\n        a = \"hello\"\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types a var that is declared in a while condition with break after re-assignment\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(char, string) }\n      while a = 'a'\n        a = \"hello\"\n        break if 1 == 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types while with next\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = 1\n      b = 1\n      while 1 == 2\n        b = a\n        if 1 == 1\n          a = 'a'\n          next\n        end\n        a = 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types block with break\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      def foo\n        yield\n      end\n\n      a = 1\n\n      foo do\n        if 1 == 1\n          a = 'a'\n          break\n        end\n        a = 1\n      end\n\n      a\n      CRYSTAL\n  end\n\n  it \"types block with break doesn't infect initial vars\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        yield\n      end\n\n      a = 1\n      b = 1\n\n      foo do\n        b = a\n        if 1 == 1\n          a = 'a'\n          break\n        end\n        a = 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types block with next\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      def foo\n        yield\n      end\n\n      a = 1\n      b = 1\n\n      foo do\n        b = a\n        if 1 == 1\n          a = 'a'\n          next\n        end\n        a = 1\n      end\n\n      b\n      CRYSTAL\n  end\n\n  it \"types if with restricted type in then\" do\n    assert_type(<<-CRYSTAL) { char }\n      a = 1 || 'a'\n      if a.is_a?(Int32)\n        a = 'a'\n      else\n        # a = 'a'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types if with restricted type in else\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1 || 'a'\n      if a.is_a?(Int32)\n        # a = 1\n      else\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types if/else with var (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      a = 1 || nil\n      d = nil\n      if a && 1 == 2\n        b = 2\n      else\n        d = a\n      end\n      d\n      CRYSTAL\n  end\n\n  it \"types re-assign inside if (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      struct Nil\n        def to_i\n          0\n        end\n      end\n\n      index = nil\n      if index\n        a = index\n      else\n        if 1 == 1\n          index = 1\n        end\n        a = index\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types re-assign inside while (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      struct Nil\n        def to_i\n          0\n        end\n      end\n\n      index = nil\n      if index\n        a = index\n      else\n        while 1 == 2\n          index = 1\n        end\n        a = index\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"preserves type filters after block (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      def foo\n        yield\n      end\n\n      if (a = 'a' || nil) && (b = 2)\n        if 1 == 2\n          foo { }\n        end\n        a.ord\n      else\n        1\n      end\n      CRYSTAL\n  end\n\n  it \"errors if accessing variable declared inside typeof\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'x'\"\n      typeof(x = 1)\n      x\n      CRYSTAL\n  end\n\n  it \"doesn't error if same variable is declared in multiple typeofs\" do\n    assert_type(<<-CRYSTAL) { char.metaclass }\n      typeof((x = uninitialized Int32; x))\n      typeof((x = uninitialized Char; x))\n      CRYSTAL\n  end\n\n  it \"doesn't error if same variable is used in multiple arguments of same typeof\" do\n    assert_type(<<-CRYSTAL) { union_of(string, char).metaclass }\n      def foo(x : String)\n        'a'\n      end\n\n      x = 1\n      typeof(x = \"\", x = foo(x))\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/static_array_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: static array\" do\n  it \"types static array with var declaration\" do\n    assert_type(\"x = uninitialized Char[3]\") { static_array_of(char, 3) }\n  end\n\n  it \"types static array new\" do\n    assert_type(\"x = StaticArray(Char, 3).new; x\") { static_array_of(char, 3) }\n  end\n\n  it \"types static array with type as size\" do\n    assert_type(<<-CRYSTAL) { static_array_of(char, 1) }\n      class Foo(N)\n        def self.foo\n          x = uninitialized Char[N]\n          x\n        end\n      end\n\n      Foo(1).foo\n      CRYSTAL\n  end\n\n  it \"errors if trying to instantiate static array with N not an integer\" do\n    assert_error <<-CRYSTAL, \"can't instantiate StaticArray(T, N) with N = Int32 (N must be an integer)\"\n      x = uninitialized Char[Int32]\n      CRYSTAL\n\n    assert_error <<-CRYSTAL,\n      StaticArray(UInt8, 1.2)\n      CRYSTAL\n      \"can't instantiate StaticArray(T, N) with N = 1.2 (N must be an integer)\"\n  end\n\n  it \"allows instantiating static array instance var in initialize of generic type\" do\n    assert_type(<<-CRYSTAL) { static_array_of(char, 1) }\n      class Foo(N)\n        def initialize\n          @x = uninitialized Char[N]\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(1).new.x\n      CRYSTAL\n  end\n\n  it \"errors on negative static array size\" do\n    assert_error <<-CRYSTAL, \"can't instantiate StaticArray(T, N) with N = -1 (N must be positive)\"\n      x = uninitialized Int32[-1]\n      CRYSTAL\n  end\n\n  it \"types static array new with size being a constant\" do\n    assert_type(<<-CRYSTAL) { static_array_of(char, 3) }\n      SIZE = 3\n      x = StaticArray(Char, SIZE).new\n      x\n      CRYSTAL\n  end\n\n  it \"types static array new with size being a nested constant inside type declaration (#5426)\" do\n    assert_type(<<-CRYSTAL) { static_array_of(char, 3) }\n      module Moo\n        SIZE = 3\n      end\n\n      class Foo\n        @x : StaticArray(Char, Moo::SIZE)\n\n        def initialize(@x)\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new(StaticArray(Char, Moo::SIZE).new).x\n      CRYSTAL\n  end\n\n  it \"types static array new with size being a computed constant\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { static_array_of(char, 100) }\n      struct Int\n        def //(other : Int)\n          0\n        end\n      end\n\n      OTHER = 10\n      SIZE = OTHER * 20 // 2\n      x = StaticArray(Char, SIZE).new\n      x\n      CRYSTAL\n  end\n\n  it \"types static array new with size being a computed constant, and use N (bug)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      struct StaticArray(T, N)\n        def size\n          N\n        end\n      end\n\n      SIZE = 1 * 2\n      x = uninitialized UInt8[SIZE]\n      x.size\n      a = 1\n      CRYSTAL\n  end\n\n  it \"doesn't crash on restriction (#584)\" do\n    assert_error <<-CRYSTAL, \"can't instantiate StaticArray(T, N) with N = Int32 (N must be an integer)\"\n      def foo(&block : Int32[Int32] -> Int32)\n        block.call([0])\n      end\n\n      foo { |x| 0 }\n      CRYSTAL\n  end\n\n  it \"can match N type argument of static array (#1203)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def fn(a : StaticArray(T, N)) forall T, N\n        N\n      end\n\n      n = uninitialized StaticArray(Int32, 10)\n      fn(n)\n      CRYSTAL\n  end\n\n  it \"can match number type argument of static array (#1203)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      def fn(a : StaticArray(T, 10)) forall T\n        10\n      end\n\n      n = uninitialized StaticArray(Int32, 10)\n      fn(n)\n      CRYSTAL\n  end\n\n  it \"doesn't match other number type argument of static array (#1203)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'fn' to be StaticArray(Int32, 11), not StaticArray(Int32, 10)\"\n      def fn(a : StaticArray(T, 11)) forall T\n        10\n      end\n\n      n = uninitialized StaticArray(Int32, 10)\n      fn(n)\n      CRYSTAL\n  end\n\n  it \"doesn't crash on sizeof (#8858)\" do\n    assert_error <<-CRYSTAL, \"can't use sizeof(Int32) as a generic type argument\"\n      alias BadArray = Int32[sizeof(Int32)]\n      CRYSTAL\n  end\n\n  it \"doesn't crash on instance_sizeof (#8858)\" do\n    assert_error <<-CRYSTAL, \"can't use instance_sizeof(String) as a generic type argument\"\n      alias BadArray = Int32[instance_sizeof(String)]\n      CRYSTAL\n  end\n\n  it \"doesn't crash on offsetof (#8858)\" do\n    assert_error <<-CRYSTAL, \"can't use offsetof(Foo, @foo) as a generic type argument\"\n      class Foo\n        @foo : Int32 = 0\n      end\n      alias BadArray = Int32[offsetof(Foo, @foo)]\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/struct_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: struct\" do\n  it \"types struct declaration\" do\n    assert_type(<<-CRYSTAL\n      struct Foo\n      end\n      Foo\n      CRYSTAL\n    ) do\n      str = types[\"Foo\"].as(NonGenericClassType)\n      str.struct?.should be_true\n      str.metaclass\n    end\n  end\n\n  it \"types generic struct declaration\" do\n    assert_type(<<-CRYSTAL\n      struct Foo(T)\n      end\n      Foo(Int32)\n      CRYSTAL\n    ) do\n      str = types[\"Foo\"].as(GenericClassType)\n      str.struct?.should be_true\n\n      str_inst = str.instantiate([int32] of TypeVar)\n      str_inst.struct?.should be_true\n      str_inst.metaclass\n    end\n  end\n\n  it \"allows struct to participate in virtual\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n      end\n\n      struct Baz < Foo\n      end\n\n      Bar.new || Baz.new\n      CRYSTAL\n  end\n\n  %w(Value Struct Int Float).each do |type|\n    it \"doesn't make virtual for #{type}\" do\n      assert_type(<<-CRYSTAL) { union_of(types[\"Foo\"], types[\"Bar\"]) }\n        struct Foo < #{type}\n        end\n\n        struct Bar < #{type}\n        end\n\n        Foo.new || Bar.new\n        CRYSTAL\n    end\n  end\n\n  it \"can't be nilable\" do\n    assert_type(<<-CRYSTAL\n      struct Foo\n      end\n\n      Foo.new || nil\n      CRYSTAL\n    ) do\n      type = nilable types[\"Foo\"]\n      type.should_not be_a(NilableType)\n      type\n    end\n  end\n\n  it \"can't extend struct from class\" do\n    assert_error <<-CRYSTAL, \"can't make struct 'Foo' inherit class 'Reference'\"\n      struct Foo < Reference\n      end\n      CRYSTAL\n  end\n\n  it \"can't extend class from struct\" do\n    assert_error <<-CRYSTAL, \"can't make class 'Bar' inherit struct 'Foo'\"\n      struct Foo\n      end\n\n      class Bar < Foo\n      end\n      CRYSTAL\n  end\n\n  it \"can't reopen as class\" do\n    assert_error <<-CRYSTAL, \"Foo is not a class, it's a struct\"\n      struct Foo\n      end\n\n      class Foo\n      end\n      CRYSTAL\n  end\n\n  it \"can't reopen as module\" do\n    assert_error <<-CRYSTAL, \"Foo is not a module, it's a struct\"\n      struct Foo\n      end\n\n      module Foo\n      end\n      CRYSTAL\n  end\n\n  it \"can't extend struct from non-abstract struct\" do\n    assert_error <<-CRYSTAL, \"can't extend non-abstract struct Foo\"\n      struct Foo\n      end\n\n      struct Bar < Foo\n      end\n      CRYSTAL\n  end\n\n  it \"unifies type to virtual type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Foo\"].virtual_type! }\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n      end\n\n      ptr = Pointer(Foo).malloc(1_u64)\n      ptr.value = Bar.new\n      ptr.value\n      CRYSTAL\n  end\n\n  it \"doesn't error if method is not found in abstract type\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      struct Baz < Foo\n        def foo\n          'a'\n        end\n      end\n\n      ptr = Pointer(Foo).malloc(1_u64)\n      ptr.value = Bar.new\n      ptr.value = Baz.new\n      ptr.value.foo\n      CRYSTAL\n  end\n\n  it \"can cast to base abstract struct\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      abstract struct Foo\n      end\n\n      struct Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      Bar.new.as(Foo)\n      CRYSTAL\n  end\n\n  it \"errors if defining finalize for struct (#3840)\" do\n    assert_error <<-CRYSTAL, \"structs can't have finalizers because they are not tracked by the GC\"\n      struct Foo\n        def finalize\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"passes subtype check with generic module type on virtual type\" do\n    mod = semantic(<<-CRYSTAL).program\n      module Base(T)\n      end\n\n      abstract struct Foo\n        include Base(Foo)\n      end\n      CRYSTAL\n\n    base_foo = mod.generic_module(\"Base\", mod.types[\"Foo\"].virtual_type!)\n    mod.types[\"Foo\"].implements?(base_foo).should be_true\n  end\n\n  it \"passes subtype check with generic module type on virtual type (2) (#10302)\" do\n    mod = semantic(<<-CRYSTAL).program\n      module Base(T)\n      end\n\n      abstract struct Foo\n        include Base(Foo)\n      end\n\n      struct Bar < Foo\n      end\n      CRYSTAL\n\n    base_foo = mod.generic_module(\"Base\", mod.types[\"Foo\"].virtual_type)\n    mod.types[\"Bar\"].implements?(base_foo).should be_true\n  end\n\n  it \"passes subtype check with generic module type on virtual type (3)\" do\n    mod = semantic(<<-CRYSTAL).program\n      module Base(T, N)\n      end\n\n      abstract struct Foo\n        include Base(Foo, 10)\n      end\n      CRYSTAL\n\n    mod.types[\"Foo\"].implements?(mod.generic_module(\"Base\", mod.types[\"Foo\"].virtual_type!, NumberLiteral.new(\"10\", :i32))).should be_true\n    mod.types[\"Foo\"].implements?(mod.generic_module(\"Base\", mod.types[\"Foo\"].virtual_type!, NumberLiteral.new(\"9\", :i32))).should be_false\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/super_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: super\" do\n  it \"types super without arguments\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"types super without arguments and instance variable\" do\n    result = assert_type(<<-CRYSTAL) { types[\"Bar\"] }\n      class Foo\n        def foo\n          @x = 1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n      end\n\n      bar = Bar.new\n      bar.foo\n      bar\n      CRYSTAL\n\n    mod, type = result.program, result.node.type.as(NonGenericClassType)\n\n    superclass = type.superclass.as(NonGenericClassType)\n    superclass.instance_vars[\"@x\"].type.should eq(mod.nilable(mod.int32))\n  end\n\n  it \"types super with forwarded arguments, parent has parameters\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(x)\n          x\n        end\n      end\n\n      class Bar < Foo\n        def foo(x)\n          super\n        end\n      end\n\n      Bar.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"types super with forwarded arguments, def has bare splat parameter (#8895)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(*, x)\n          x\n        end\n      end\n\n      class Bar < Foo\n        def foo(*, x)\n          super\n        end\n      end\n\n      Bar.new.foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"types super with named arguments, def has bare splat parameter (#8895)\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      class Foo\n        def foo(*, x)\n          x\n        end\n      end\n\n      class Bar < Foo\n        def foo(*, x)\n          super x: x || 'a'\n        end\n      end\n\n      Bar.new.foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"types super with named arguments, def has bare splat parameter (2) (#8895)\" do\n    assert_type(<<-CRYSTAL) { union_of int32, char }\n      class Foo\n        def foo(x)\n          x\n        end\n      end\n\n      class Bar < Foo\n        def foo(x)\n          super x: x || 'a'\n        end\n      end\n\n      Bar.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"types super with forwarded arguments, different internal names (#8895)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo(*, x a)\n          a\n        end\n      end\n\n      class Bar < Foo\n        def foo(*, x b)\n          super\n        end\n      end\n\n      Bar.new.foo(x: 1)\n      CRYSTAL\n  end\n\n  it \"types super with forwarded arguments, def has double splat parameter (#8895)\" do\n    assert_type(<<-CRYSTAL) { named_tuple_of({\"x\": int32, \"y\": char}) }\n      class Foo\n        def foo(**opts)\n          opts\n        end\n      end\n\n      class Bar < Foo\n        def foo(**opts)\n          super\n        end\n      end\n\n      Bar.new.foo(x: 1, y: 'a')\n      CRYSTAL\n  end\n\n  it \"types super when container method is defined in parent class\" do\n    nodes = parse <<-CRYSTAL\n      class Foo\n        def initialize\n          @x = 1\n        end\n      end\n      class Bar < Foo\n        def initialize\n          super\n        end\n      end\n      class Baz < Bar\n      end\n      Baz.new\n      CRYSTAL\n    result = semantic nodes\n    mod, type = result.program, result.node.type.as(NonGenericClassType)\n\n    type.should eq(mod.types[\"Baz\"])\n\n    superclass = type.superclass.as(NonGenericClassType)\n    superclass2 = superclass.superclass.as(NonGenericClassType)\n    superclass2.instance_vars[\"@x\"].type.should eq(mod.int32)\n  end\n\n  it \"types super when container method is defined in parent class two levels up\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Base\n        def foo\n          1\n        end\n      end\n\n      class Foo < Base\n      end\n\n      class Bar < Foo\n        def foo\n          super\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"types super when inside fun\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          x = ->{ super }\n          x.call\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\n\n  it \"types super when inside fun and forwards args\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      class Foo\n        def foo(z)\n          z\n        end\n      end\n\n      class Bar < Foo\n        def foo(z)\n          x = ->{ super }\n          x.call\n        end\n      end\n\n      Bar.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"errors no superclass method in top-level\" do\n    assert_error <<-CRYSTAL, \"there's no superclass in this scope\"\n      super\n      CRYSTAL\n  end\n\n  it \"errors no superclass method in top-level def\" do\n    assert_error <<-CRYSTAL, \"there's no superclass in this scope\"\n      def foo\n        super\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"errors no superclass method\" do\n    assert_error <<-CRYSTAL, \"undefined method 'foo'\"\n      require \"prelude\"\n\n      class Foo\n        def foo(x)\n          super\n        end\n      end\n\n      Foo.new.foo(1)\n      CRYSTAL\n  end\n\n  it \"finds super initialize if not explicitly defined in superclass, 1 (#273)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Foo\n        def initialize\n          super\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"finds super initialize if not explicitly defined in superclass, 2 (#273)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"] }\n      class Base\n      end\n\n      class Foo < Base\n        def initialize\n          super\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"says correct error message when no overload matches in super call (#272)\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Foo#initialize' to be Char, not Int32\"\n      abstract class Foo\n        def initialize(x : Char)\n        end\n      end\n\n      class Bar < Foo\n        def initialize(a, b)\n          super(a)\n        end\n      end\n\n      Bar.new(1, 2)\n      CRYSTAL\n  end\n\n  it \"calls super in module method (1) (#556)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Parent\n        def a\n          1\n        end\n      end\n\n      module Mod\n        def a\n          super\n        end\n      end\n\n      class Child < Parent\n        include Mod\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"calls super in module method (2) (#556)\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Parent\n        def a\n          1\n        end\n      end\n\n      module Mod2\n        def a\n          'a'\n        end\n      end\n\n      module Mod\n        def a\n          super\n        end\n      end\n\n      class Child < Parent\n        include Mod2\n        include Mod\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"calls super in module method (3) (#556)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Parent\n        def a\n          1\n        end\n      end\n\n      module Mod2\n      end\n\n      module Mod\n        def a\n          super\n        end\n      end\n\n      class Child < Parent\n        include Mod2\n        include Mod\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"errors if calling super on module method and not found\" do\n    assert_error <<-CRYSTAL, \"undefined method 'a'\"\n      module Mod\n        def a\n          super\n        end\n      end\n\n      class Child\n        include Mod\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"calls super in generic module method\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Parent\n        def a\n          1\n        end\n      end\n\n      module Mod(T)\n        def a\n          super\n        end\n      end\n\n      class Child < Parent\n        include Mod(Int32)\n      end\n\n      Child.new.a\n      CRYSTAL\n  end\n\n  it \"doesn't error if invoking super and match isn't found in direct superclass (even though it's find in one superclass)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo(x)\n          'a'\n        end\n      end\n\n      class Baz < Bar\n        def foo\n          super()\n        end\n      end\n\n      Baz.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if invoking super and match isn't found in direct superclass in initialize (even though it's find in one superclass)\" do\n    assert_error <<-CRYSTAL, \"wrong number of argument\"\n      class Foo\n        def initialize\n        end\n      end\n\n      class Bar < Foo\n        def initialize(x)\n        end\n      end\n\n      class Baz < Bar\n        def initialize\n          super()\n        end\n      end\n\n      Baz.new\n      CRYSTAL\n  end\n\n  it \"gives correct error when calling super and target is abstract method (#2675)\" do\n    assert_error <<-CRYSTAL, \"undefined method 'Base#method()'\"\n      abstract class Base\n        abstract def method\n      end\n\n      class Sub < Base\n        def method\n          super\n        end\n      end\n\n      Sub.new.method\n      CRYSTAL\n  end\n\n  it \"errors on super outside method (#4481)\" do\n    assert_error <<-CRYSTAL, \"can't use 'super' outside method\"\n      class Foo\n        super\n      end\n      CRYSTAL\n  end\n\n  it \"errors on super where only target would be a top level method (#5201)\" do\n    assert_error <<-CRYSTAL, \"no overload matches 'bar'\"\n      def bar\n      end\n\n      class Foo\n        def bar\n          super\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"invokes super inside macro (#6636)\" do\n    assert_type(<<-CRYSTAL) { char }\n      class Foo\n        def foo\n          1\n        end\n\n        def foo(x)\n          'a'\n        end\n      end\n\n      class Bar < Foo\n        def foo(x)\n          {% begin %}\n            super\n          {% end %}\n        end\n      end\n\n      Bar.new.foo(3)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/tuple_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: tuples\" do\n  it \"types tuple of one element\" do\n    assert_type(\"{1}\") { tuple_of([int32] of TypeVar) }\n  end\n\n  it \"types tuple of three elements\" do\n    assert_type(\"{1, 2.5, 'a'}\") { tuple_of([int32, float64, char] of TypeVar) }\n  end\n\n  it \"types tuple of one element and then two elements\" do\n    assert_type(\"{1}; {1, 2}\") { tuple_of([int32, int32] of TypeVar) }\n  end\n\n  it \"types tuple with splats inside\" do\n    assert_type(\"{1, *{2.5, 'a'}, true}\") { tuple_of([int32, float64, char, bool] of TypeVar) }\n  end\n\n  it \"errors if non-tuple is splatted inside tuple\" do\n    assert_error \"{*1}\", \"argument to splat must be a tuple, not Int32\"\n  end\n\n  it \"errors if non-tuple is splatted inside tuple (2)\" do\n    assert_error \"{*{1} || {2, 3}}\", \"argument to splat must be a tuple, not (Tuple(Int32) | Tuple(Int32, Int32))\"\n  end\n\n  describe \"#[](NumberLiteral)\" do\n    it \"types, inbound index\" do\n      assert_type(\"{1, 'a'}[0]\") { int32 }\n      assert_type(\"{1, 'a'}[1]\") { char }\n\n      assert_type(\"{1, 'a'}[-1]\") { char }\n      assert_type(\"{1, 'a'}[-2]\") { int32 }\n    end\n\n    it \"types, inbound index, nilable\" do\n      assert_type(\"{1, 'a'}[0]?\") { int32 }\n      assert_type(\"{1, 'a'}[1]?\") { char }\n\n      assert_type(\"{1, 'a'}[-1]?\") { char }\n      assert_type(\"{1, 'a'}[-2]?\") { int32 }\n    end\n\n    it \"types, out of bound, nilable\" do\n      assert_type(\"{1, 'a'}[2]?\") { nil_type }\n      assert_type(\"{1, 'a'}[-3]?\") { nil_type }\n\n      assert_type(<<-CRYSTAL) { nil_type }\n        def tuple(*args)\n          args\n        end\n\n        tuple()[0]?\n        CRYSTAL\n    end\n\n    it \"types, metaclass index\" do\n      assert_type(\"{1, 'a'}.class[0]\", inject_primitives: true) { int32.metaclass }\n      assert_type(\"{1, 'a'}.class[1]\", inject_primitives: true) { char.metaclass }\n\n      assert_type(\"{1, 'a'}.class[-1]\", inject_primitives: true) { char.metaclass }\n      assert_type(\"{1, 'a'}.class[-2]\", inject_primitives: true) { int32.metaclass }\n    end\n\n    it \"gives error when indexing out of range\" do\n      assert_error \"{1, 'a'}[2]\",\n        \"index out of bounds for Tuple(Int32, Char) (2 not in -2..1)\"\n    end\n\n    it \"gives error when indexing out of range on empty tuple\" do\n      assert_error <<-CRYSTAL, \"index '0' out of bounds for empty tuple\"\n        def tuple(*args)\n          args\n        end\n\n        tuple()[0]\n        CRYSTAL\n    end\n  end\n\n  describe \"#[](RangeLiteral)\" do\n    it \"types, inbound begin\" do\n      assert_type(%(#{range_new}; {1, 'a'}[0..-3])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..-2])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..-1])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..0])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..1])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..2])) { tuple_of([int32, char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[1..-3])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..-1])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..1])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..2])) { tuple_of([char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[2..-3])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..-1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..2])) { tuple_of([] of Type) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[-1..-3])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..-1])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..1])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..2])) { tuple_of([char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[-2..-3])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..-2])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..-1])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..0])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..1])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..2])) { tuple_of([int32, char]) }\n\n      assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[0..0]\n        CRYSTAL\n    end\n\n    it \"types, inbound begin, end-less\" do\n      assert_type(%(#{range_new}; {1, 'a'}[0..])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..])) { tuple_of([int32, char]) }\n\n      assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[0..]\n        CRYSTAL\n    end\n\n    it \"types, begin-less\" do\n      assert_type(%(#{range_new}; {1, 'a'}[..0])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[..1])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[..2])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[..-3])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[..-2])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[..-1])) { tuple_of([int32, char]) }\n\n      assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[..0]\n        CRYSTAL\n    end\n\n    it \"types, begin-less, end-less\" do\n      assert_type(%(#{range_new}; {1, 'a'}[..])) { tuple_of([int32, char]) }\n\n      assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[..]\n        CRYSTAL\n    end\n\n    it \"types, exclusive range\" do\n      assert_type(%(#{range_new}; {1, 'a'}[0...-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[0...-1])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0...0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[0...1])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0...2])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0...3])) { tuple_of([int32, char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[1...-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1...-1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1...0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1...1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1...2])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[1...3])) { tuple_of([char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[2...-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2...-1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2...0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2...1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2...2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2...3])) { tuple_of([] of Type) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[-1...-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1...-1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1...0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1...1])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1...2])) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1...3])) { tuple_of([char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[-2...-2])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2...-1])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2...0])) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2...1])) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2...2])) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2...3])) { tuple_of([int32, char]) }\n    end\n\n    it \"types, inbound begin, nilable\" do\n      assert_type(%(#{range_new}; {1, 'a'}[0..-3]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..-2]?)) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..-1]?)) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..0]?)) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..1]?)) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[0..2]?)) { tuple_of([int32, char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[1..-3]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..-2]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..-1]?)) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..0]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..1]?)) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[1..2]?)) { tuple_of([char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[2..-3]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..-2]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..-1]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..0]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..1]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[2..2]?)) { tuple_of([] of Type) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[-1..-3]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..-2]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..-1]?)) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..0]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..1]?)) { tuple_of([char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-1..2]?)) { tuple_of([char]) }\n\n      assert_type(%(#{range_new}; {1, 'a'}[-2..-3]?)) { tuple_of([] of Type) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..-2]?)) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..-1]?)) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..0]?)) { tuple_of([int32]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..1]?)) { tuple_of([int32, char]) }\n      assert_type(%(#{range_new}; {1, 'a'}[-2..2]?)) { tuple_of([int32, char]) }\n\n      assert_type(<<-CRYSTAL) { tuple_of([] of Type) }\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[0..0]?\n        CRYSTAL\n    end\n\n    it \"types, out of bound begin, nilable\" do\n      assert_type(%(#{range_new}; {1, 'a'}[-3..0]?)) { nil_type }\n      assert_type(%(#{range_new}; {1, 'a'}[3..2]?)) { nil_type }\n\n      assert_type(<<-CRYSTAL) { nil_type }\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[1..0]?\n        CRYSTAL\n    end\n\n    it \"types, metaclass index\" do\n      assert_type(%(#{range_new}; {1, 'a'}.class[0..1]), inject_primitives: true) { tuple_of([int32, char]).metaclass }\n      assert_type(%(#{range_new}; {1, 'a'}.class[1..2]), inject_primitives: true) { tuple_of([char]).metaclass }\n      assert_type(%(#{range_new}; {1, 'a'}.class[1..-2]), inject_primitives: true) { tuple_of([] of Type).metaclass }\n      assert_type(%(#{range_new}; {1, 'a'}.class[-2..-1]), inject_primitives: true) { tuple_of([int32, char]).metaclass }\n      assert_type(%(#{range_new}; {1, 'a'}.class[-1..0]), inject_primitives: true) { tuple_of([] of Type).metaclass }\n    end\n\n    it \"gives error when begin index is out of range\" do\n      assert_error <<-CRYSTAL, \"begin index out of bounds for Tuple(Int32, Char) (3 not in -2..2)\"\n        #{range_new}\n\n        {1, 'a'}[3..0]\n        CRYSTAL\n\n      assert_error <<-CRYSTAL, \"begin index out of bounds for Tuple(Int32, Char) (-3 not in -2..2)\"\n        #{range_new}\n\n        {1, 'a'}[-3..0]\n        CRYSTAL\n\n      assert_error <<-CRYSTAL, \"begin index out of bounds for Tuple() (1 not in 0..0)\"\n        #{range_new}\n\n        def tuple(*args)\n          args\n        end\n\n        tuple()[1..0]\n        CRYSTAL\n    end\n  end\n\n  describe \"#[](Path)\" do\n    it \"works for tuple indexer\" do\n      assert_type(\"A = 0; {1, 'a'}[A]\") { int32 }\n    end\n\n    it \"works for named tuple indexer\" do\n      assert_type(\"A = :a; {a: 1, b: 'a'}[A]\") { int32 }\n    end\n  end\n\n  it \"can name a tuple type\" do\n    assert_type(\"Tuple(Int32, Float64)\") { tuple_of([int32, float64]).metaclass }\n  end\n\n  it \"gives error when using named args on Tuple\" do\n    assert_error <<-CRYSTAL, \"can only use named arguments with NamedTuple\"\n      Tuple(x: Int32, y: Char)\n      CRYSTAL\n  end\n\n  it \"doesn't error if Tuple has no args\" do\n    assert_type(\"Tuple()\") { tuple_of([] of Type).metaclass }\n  end\n\n  it \"types T as a tuple of metaclasses\" do\n    assert_type(<<-CRYSTAL\n      struct Tuple\n        def type_args\n          T\n        end\n      end\n\n      x = {1, 1.5, 'a'}\n      x.type_args\n      CRYSTAL\n    ) do\n      meta = tuple_of([int32, float64, char]).metaclass\n      meta.metaclass?.should be_true\n      meta\n    end\n  end\n\n  it \"errors on recursive splat expansion (#218)\" do\n    assert_error <<-CRYSTAL, \"recursive splat expansion\"\n      def foo(*a)\n        foo(a)\n      end\n\n      def foo(a : Tuple(String))\n      end\n\n      foo(\"a\", \"b\")\n      CRYSTAL\n  end\n\n  it \"errors on recursive splat expansion (1) (#361)\" do\n    assert_error <<-CRYSTAL, \"recursive splat expansion\"\n      require \"prelude\"\n\n      def foo(type, *args)\n        foo 1, args.to_a\n      end\n\n      foo \"foo\", 1\n      CRYSTAL\n  end\n\n  it \"errors on recursive splat expansion (2) (#361)\" do\n    assert_error <<-CRYSTAL, \"recursive splat expansion\"\n      class Foo(T)\n      end\n\n      def foo(type, *args)\n        foo 1, Foo(typeof(args)).new\n      end\n\n      foo \"foo\", 1\n      CRYSTAL\n  end\n\n  it \"doesn't trigger recursive splat expansion error (#7164)\" do\n    assert_no_errors <<-CRYSTAL\n      def call(*args)\n        call({1})\n      end\n\n      call(1)\n      CRYSTAL\n  end\n\n  it \"allows tuple covariance\" do\n    assert_type(<<-CRYSTAL) { tuple_of [types[\"Foo\"].virtual_type!] }\n      class Obj\n        def initialize\n          @tuple = {Foo.new}\n        end\n\n        def tuple=(@tuple)\n        end\n\n        def tuple\n          @tuple\n        end\n      end\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      obj = Obj.new\n      obj.tuple = {Bar.new}\n      obj.tuple\n      CRYSTAL\n  end\n\n  it \"merges two tuple types of same size\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { tuple_of [string, nilable(int32)] }\n      def foo\n        if 1 == 2\n          {\"foo\", 1}\n        else\n          {\"foo\", nil}\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"accept tuple in type restriction\" do\n    assert_type(<<-CRYSTAL) { tuple_of [types[\"Bar\"]] }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : {Foo})\n        x\n      end\n\n      foo({Bar.new})\n      CRYSTAL\n  end\n\n  it \"accepts tuple covariance in array\" do\n    assert_type(<<-CRYSTAL) { tuple_of [types[\"Foo\"].virtual_type!, types[\"Foo\"].virtual_type!] }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = [] of {Foo, Foo}\n      a << {Bar.new, Bar.new}\n      a[0]\n      CRYSTAL\n  end\n\n  it \"can iterate T\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, string.metaclass]) }\n      struct Tuple\n        def self.types\n          {% begin %}\n            {\n              {% for type in T %}\n                {{type}},\n              {% end %}\n            }\n          {% end %}\n        end\n      end\n      Tuple(Int32, String).types\n      CRYSTAL\n  end\n\n  it \"can call [] on T\" do\n    assert_type(<<-CRYSTAL) { nil_type.metaclass }\n      struct Tuple\n        def self.types\n          {{ T[0] }}\n        end\n      end\n      Tuple(Nil, Int32).types\n      CRYSTAL\n  end\n\n  it \"matches tuple with splat (#2932)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }\n      def foo(x : Tuple(*T)) forall T\n        T\n      end\n\n      foo({1, 'a'})\n      CRYSTAL\n  end\n\n  it \"matches tuple with splat (2) (#2932)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([char, bool]).metaclass, float64.metaclass]) }\n      def foo(x : Tuple(A, *B, C)) forall A, B, C\n        {A, B, C}\n      end\n\n      foo({1, 'a', true, 1.5})\n      CRYSTAL\n  end\n\n  it \"errors if using two splat indices on restriction\" do\n    assert_error <<-CRYSTAL, \"can't specify more than one splat in restriction\"\n      def foo(x : Tuple(*A, *B)) forall A, B\n      end\n\n      foo({1, 'a'})\n      CRYSTAL\n  end\n\n  it \"errors on tuple too big (#3816)\" do\n    assert_error <<-CRYSTAL, \"tuple size cannot be greater than 300 (size is 302)\"\n      require \"prelude\"\n\n      pos = {0, 0}\n      while true\n        pos += {0, 0}\n      end\n      CRYSTAL\n  end\n\n  it \"errors on named tuple too big\" do\n    named_tuple_keys = String.build do |io|\n      333.times { |i| io << \"key\" << i << \": 0, \" }\n    end\n\n    assert_error <<-CRYSTAL, \"named tuple size cannot be greater than 300 (size is 333)\"\n      { #{named_tuple_keys} }\n      CRYSTAL\n  end\n\n  it \"doesn't unify tuple metaclasses (#5384)\" do\n    assert_type(<<-CRYSTAL\n      Tuple(Int32) || Tuple(String)\n      CRYSTAL\n    ) {\n      union_of(\n        tuple_of([int32] of Type).metaclass,\n        tuple_of([string] of Type).metaclass,\n      )\n    }\n  end\n\n  it \"doesn't crash on tuple in not executed block (#6718)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      require \"prelude\"\n\n      def pending(&block)\n      end\n\n      def untyped(x = nil)\n      end\n\n      # To reproduce this bug, it is needed to the expression that is\n      # not typed on main phase but is typed on cleanup phase.\n      # `untyped(untyped)` is just one.\n      pending do\n        {untyped(untyped)}\n      end\n      CRYSTAL\n  end\nend\n\nprivate def range_new\n  %(\n    struct Range(B, E)\n      def initialize(@begin : B, @end : E, @exclusive : Bool = false)\n      end\n    end\n  )\nend\n"
  },
  {
    "path": "spec/compiler/semantic/uninitialized_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: uninitialized\" do\n  it \"declares as uninitialized\" do\n    assert_type(\"a = uninitialized Int32\") { int32 }\n  end\n\n  it \"declares as uninitialized and reads it\" do\n    assert_type(\"a = uninitialized Int32; a\") { int32 }\n  end\n\n  it \"declares an instance variable in initialize as uninitialized\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def initialize\n          @x = uninitialized Int32\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"errors if declaring generic type without type vars (with instance var)\" do\n    assert_error <<-CRYSTAL, \"can't declare variable of generic non-instantiated type Foo\"\n      class Foo(T)\n      end\n\n      class Bar\n        def initialize\n          @x = uninitialized Foo\n        end\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"errors if declaring generic type without type vars (with class var)\" do\n    assert_error <<-CRYSTAL, \"can't declare variable of generic non-instantiated type Foo\"\n      class Foo(T)\n      end\n\n      class Bar\n        @@x = uninitialized Foo\n      end\n\n      Bar.new\n      CRYSTAL\n  end\n\n  it \"errors if declares var and then assigns other type\" do\n    assert_error <<-CRYSTAL, \"type must be Int32, not (Char | Int32)\"\n      x = uninitialized Int32\n      x = 'a'\n      CRYSTAL\n  end\n\n  it \"errors if declaring variable multiple times with different types (#917)\" do\n    assert_error <<-CRYSTAL, \"variable 'buf' already declared with type Int32\", inject_primitives: true\n      if 1 == 0\n        buf = uninitialized Int32\n      else\n        buf = uninitialized Float64\n      end\n      CRYSTAL\n  end\n\n  it \"can uninitialize variable outside initialize (#2828)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        @x = uninitialized Int32\n\n        def x\n          @x\n        end\n      end\n\n      Foo.new.x\n      CRYSTAL\n  end\n\n  it \"can uninitialize variable outside initialize, generic (#2828)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        @x = uninitialized T\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32).new.x\n      CRYSTAL\n  end\n\n  it \"can use uninitialized with class type (#2940)\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      class Foo(U)\n        def initialize\n          @x = uninitialized U\n        end\n\n        def x\n          @x\n        end\n      end\n\n      Foo(Int32.class).new.x\n      CRYSTAL\n  end\n\n  %w(Object Value Reference Number Int Float Struct Class Enum).each do |type|\n    it \"disallows declaring var of type #{type}\" do\n      assert_error <<-CRYSTAL, \"use a more specific type\"\n        x = uninitialized #{type}\n        CRYSTAL\n    end\n  end\n\n  it \"works with uninitialized NoReturn (#3314)\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo\n        x = uninitialized typeof(yield)\n      end\n\n      def bar\n        foo { return }\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"has type (#3641)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      x = uninitialized Int32\n      CRYSTAL\n  end\n\n  it \"uses virtual type for uninitialized (#8216)\" do\n    assert_type(<<-CRYSTAL) { types[\"Base\"].virtual_type! }\n      class Base\n      end\n\n      class Sub < Base\n      end\n\n      u = uninitialized Base\n      u\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/union_spec.cr",
    "content": "require \"../../spec_helper\"\n\nprivate def assert_commutes(str, *, file = __FILE__, line = __LINE__, &)\n  result = semantic(str)\n  program = result.program\n  type1, type2, expected = with program yield program\n  union1 = program.type_merge([type1, type2])\n  union2 = program.type_merge([type2, type1])\n  union1.should eq(expected), file: file, line: line\n  union2.should eq(expected), file: file, line: line\nend\n\ndescribe \"Semantic: union\" do\n  context \"commutativity\" do\n    it \"module v.s. including module\" do\n      assert_commutes(<<-CRYSTAL) { [types[\"A\"], types[\"B\"], types[\"A\"]] }\n        module A\n        end\n\n        module B\n          include A\n        end\n        CRYSTAL\n    end\n\n    it \"module v.s. including generic module instance\" do\n      assert_commutes(<<-CRYSTAL) { [types[\"A\"], generic_module(\"B\", types[\"Cxx\"]), types[\"A\"]] }\n        class Cxx\n        end\n\n        module A\n        end\n\n        module B(T)\n          include A\n        end\n        CRYSTAL\n    end\n\n    it \"generic module instance v.s. including module\" do\n      assert_commutes(<<-CRYSTAL) { [generic_module(\"A\", types[\"Cxx\"]), types[\"B\"], generic_module(\"A\", types[\"Cxx\"])] }\n        class Cxx\n        end\n\n        module A(T)\n        end\n\n        module B\n          include A(Cxx)\n        end\n        CRYSTAL\n    end\n\n    it \"generic module instance v.s. including generic module instance\" do\n      assert_commutes(<<-CRYSTAL) { [generic_module(\"A\", types[\"Cxx\"]), generic_module(\"B\", types[\"Cxx\"]), generic_module(\"A\", types[\"Cxx\"])] }\n        class Cxx\n        end\n\n        module A(T)\n        end\n\n        module B(T)\n          include A(T)\n        end\n        CRYSTAL\n    end\n\n    it \"module v.s. extending generic module instance metaclass\" do\n      assert_commutes(<<-CRYSTAL) { [types[\"A\"], generic_module(\"B\", types[\"Cxx\"]).metaclass, types[\"A\"]] }\n        class Cxx\n        end\n\n        module A\n        end\n\n        module B(T)\n          extend A\n        end\n        CRYSTAL\n    end\n\n    it \"generic module instance v.s. extending generic module instance metaclass\" do\n      assert_commutes(<<-CRYSTAL) { [generic_module(\"A\", types[\"Cxx\"]), generic_module(\"B\", types[\"Cxx\"]).metaclass, generic_module(\"A\", types[\"Cxx\"])] }\n        class Cxx\n        end\n\n        module A(T)\n        end\n\n        module B(T)\n          extend A(T)\n        end\n        CRYSTAL\n    end\n\n    it \"virtual metaclass v.s. generic subclass instance metaclass\" do\n      assert_commutes(<<-CRYSTAL) { [types[\"A\"].virtual_type!.metaclass, generic_class(\"B\", types[\"Cxx\"]).metaclass, types[\"A\"].virtual_type!.metaclass] }\n        class Cxx\n        end\n\n        class A\n        end\n\n        class B(T) < A\n        end\n        CRYSTAL\n    end\n\n    it \"superclass v.s. uninstantiated generic subclass\" do\n      assert_commutes(<<-CRYSTAL) { [types[\"A\"], types[\"B\"], types[\"A\"].virtual_type!] }\n        class A\n        end\n\n        class B(T) < A\n        end\n        CRYSTAL\n    end\n\n    it \"uninstantiated generic super-metaclass v.s. uninstantiated generic sub-metaclass\" do\n      assert_commutes(<<-CRYSTAL) { [types[\"A\"].metaclass, types[\"B\"].metaclass, types[\"A\"].metaclass.virtual_type!] }\n        class A(T)\n        end\n\n        class B(T) < A(T)\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"types union when obj is union\" do\n    assert_type(\"struct Char; def +(other); self; end; end; a = 1 || 'a'; a + 1\", inject_primitives: true) { union_of(int32, char) }\n  end\n\n  it \"types union when arg is union\" do\n    assert_type(\"struct Int; def +(x : Char); x; end; end; a = 1 || 'a'; 1 + a\", inject_primitives: true) { union_of(int32, char) }\n  end\n\n  it \"types union when both obj and arg are union\" do\n    assert_type(\"struct Char; def +(other); self; end; end; struct Int; def +(x : Char); x; end; end; a = 1 || 'a'; a + a\", inject_primitives: true) { union_of(int32, char) }\n  end\n\n  it \"types union of classes\" do\n    assert_type(\"class Foo; end; class Bar; end; a = Foo.new || Bar.new; a\") { union_of(types[\"Foo\"], types[\"Bar\"]) }\n  end\n\n  it \"assigns to union and keeps new union type in call\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, bool, char) }\n      def foo(x)\n        while false\n          x = 'a'\n        end\n        x\n      end\n\n      foo(1 || false)\n      CRYSTAL\n  end\n\n  it \"looks up type in union type with free var\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", union_of(int32, char) }\n      class Bar(T)\n      end\n\n      def foo(x : T) forall T\n        Bar(T).new\n      end\n\n      foo(1 || 'a')\n      CRYSTAL\n  end\n\n  it \"supports macro if inside union\" do\n    assert_type(<<-CRYSTAL, flags: \"some_flag\") { int32 }\n      lib LibC\n        union Foo\n          {% if flag?(:some_flag) %}\n            a : Int32\n          {% else %}\n            a : Float64\n          {% end %}\n        end\n      end\n\n      LibC::Foo.new.a\n      CRYSTAL\n  end\n\n  it \"types union\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, string).metaclass }\n      Union(Int32, String)\n      CRYSTAL\n  end\n\n  it \"types union of same type\" do\n    assert_type(<<-CRYSTAL) { int32.metaclass }\n      Union(Int32, Int32, Int32)\n      CRYSTAL\n  end\n\n  it \"can reopen Union\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Union\n        def self.foo\n          1\n        end\n      end\n      Union(Int32, String).foo\n      CRYSTAL\n  end\n\n  it \"can reopen Union and access T\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, string]).metaclass }\n      struct Union\n        def self.types\n          T\n        end\n      end\n      Union(Int32, String).types\n      CRYSTAL\n  end\n\n  it \"can iterate T\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, string.metaclass]) }\n      struct Union\n        def self.types\n          {% begin %}\n            {\n              {% for type in T %}\n                {{type}},\n              {% end %}\n            }\n          {% end %}\n        end\n      end\n      Union(Int32, String).types\n      CRYSTAL\n  end\n\n  it \"errors if instantiates union\" do\n    assert_error <<-CRYSTAL, \"can't create instance of a union type\"\n      Union(Int32, String).new\n      CRYSTAL\n  end\n\n  it \"finds method in Object\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Object\n        def self.foo\n          1\n        end\n      end\n\n      Union(Int32, String).foo\n      CRYSTAL\n  end\n\n  it \"finds method in Value\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      struct Value\n        def self.foo\n          1\n        end\n      end\n\n      Union(Int32, String).foo\n      CRYSTAL\n  end\n\n  it \"merges types in the same hierarchy with Union\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type!.metaclass }\n      class Foo; end\n      class Bar < Foo; end\n\n      Union(Foo, Bar)\n      CRYSTAL\n  end\n\n  it \"treats void as nil in union\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      nil.as(Void?)\n      CRYSTAL\n  end\n\n  it \"can use Union in type restriction (#2988)\" do\n    assert_type(<<-CRYSTAL) { tuple_of([int32, string]) }\n      def foo(x : Union(Int32, String))\n        x\n      end\n\n      {foo(1), foo(\"hi\")}\n      CRYSTAL\n  end\n\n  it \"doesn't crash with union of no-types (#5805)\" do\n    assert_type(<<-CRYSTAL) { union_of char, generic_class(\"Gen\", int32).metaclass }\n      class Gen(T)\n      end\n\n      foo = 42\n      if foo.is_a?(String)\n        Gen(typeof(foo) | Int32)\n      else\n        'a'\n      end\n      CRYSTAL\n  end\n\n  it \"doesn't virtualize union elements (#7814)\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].metaclass }\n      class Foo; end\n      class Bar < Foo; end\n\n      Union(Foo)\n      CRYSTAL\n  end\n\n  it \"doesn't run virtual lookup on unbound unions (#9173)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Object\n        def foo\n          self\n        end\n      end\n\n      abstract class Parent\n      end\n\n      class Child(T) < Parent\n        @buffer = uninitialized T\n\n        def bar\n          @buffer.foo\n        end\n      end\n\n      class Foo(U)\n        @x = Child(U | Char).new\n      end\n\n      Child(Int32).new.as(Parent).bar\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/var_spec.cr",
    "content": "require \"../../spec_helper\"\n\ninclude Crystal\n\ndescribe \"Semantic: var\" do\n  it \"types an assign\" do\n    input = parse \"a = 1\"\n    result = semantic input\n    mod = result.program\n    node = result.node.as(Assign)\n    node.target.type.should eq(mod.int32)\n    node.value.type.should eq(mod.int32)\n    node.type.should eq(mod.int32)\n  end\n\n  it \"types a variable\" do\n    input = parse \"a = 1; a\"\n    result = semantic input\n    mod = result.program\n    node = result.node.as(Expressions)\n    node.last.type.should eq(mod.int32)\n    node.type.should eq(mod.int32)\n  end\n\n  it \"types an assign with type declaration\" do\n    assert_type(\"a : Int32 = 1\") { int32 }\n  end\n\n  it \"reports undefined local variable or method\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'something'\"\n      def foo\n        a = something\n      end\n\n      def bar\n        foo\n      end\n\n      bar\n      CRYSTAL\n  end\n\n  it \"reports there's no self\" do\n    assert_error \"self\", \"there's no self in this scope\"\n  end\n\n  it \"reports variable always nil\" do\n    assert_error \"1 == 2 ? (a = 1) : a\",\n      \"read before assignment to local variable 'a'\", inject_primitives: true\n  end\n\n  it \"lets type on else side of if with a Bool | Nil union\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable bool }\n      a = (1 == 1) || nil\n      a ? nil : a\n      CRYSTAL\n  end\n\n  it \"errors if declaring var that is already declared\" do\n    assert_error <<-CRYSTAL, \"variable 'a' already declared\"\n      a = 1\n      a = uninitialized Float64\n      CRYSTAL\n  end\n\n  it \"errors if reads from underscore\" do\n    assert_error <<-CRYSTAL, \"can't read from _\"\n      _\n      CRYSTAL\n  end\n\n  it \"declares local variable with value\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a : Int32 = 0\n      a\n      CRYSTAL\n  end\n\n  it \"declares local variable and then assigns it\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a : Int32\n      a = 0\n      a\n      CRYSTAL\n  end\n\n  it \"declares local variable and immediately reads it\" do\n    assert_error <<-CRYSTAL, \"read before assignment to local variable 'a'\"\n      a : Int32\n      a\n      CRYSTAL\n  end\n\n  it \"declares local variable and assigns it with if\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a : Int32\n      if 1 == 2\n        a = 0\n      else\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"declares local variable but doesn't assign it in all branches\" do\n    assert_error <<-CRYSTAL, \"type must be Int32\", inject_primitives: true\n      a : Int32\n      if 1 == 2\n        a = 0\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"declares local variable and assigns wrong type\" do\n    assert_error <<-CRYSTAL, \"type must be Int32\"\n      a : Int32\n      a = true\n      CRYSTAL\n  end\n\n  it \"parse local variable as method call even if local variable is declared in call arguments\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'a'\"\n      macro foo(x)\n        {{x}}\n      end\n      foo a : Int32\n      a\n      CRYSTAL\n  end\n\n  it \"errors if variable already exists\" do\n    assert_error <<-CRYSTAL, \"variable 'a' already declared\"\n      a = true\n      a : Int32\n      CRYSTAL\n  end\n\n  it \"errors if declaring generic type without type vars (with local var)\" do\n    assert_error <<-CRYSTAL, \"can't declare variable of generic non-instantiated type Foo\"\n      class Foo(T)\n      end\n\n      x : Foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/virtual_metaclass_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: virtual metaclass\" do\n  it \"types virtual metaclass\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { types[\"Foo\"].virtual_type.metaclass }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      f = Foo.new || Bar.new\n      f.class\n      CRYSTAL\n  end\n\n  it \"types virtual metaclass method\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, float64) }\n      class Foo\n        def self.foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def self.foo\n          1.5\n        end\n      end\n\n      f = Foo.new || Bar.new\n      f.class.foo\n      CRYSTAL\n  end\n\n  it \"allows allocating virtual type when base class is abstract\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      require \"prelude\"\n\n      abstract class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      bar = Bar.new || Baz.new\n      baz = bar.class.allocate\n      CRYSTAL\n  end\n\n  it \"yields virtual type in block arg if class is abstract\" do\n    assert_type(<<-CRYSTAL) { array_of(types[\"Foo\"].virtual_type) }\n      require \"prelude\"\n\n      abstract class Foo\n        def clone\n          self.class.allocate\n        end\n\n        def to_s\n          \"Foo\"\n        end\n      end\n\n      class Bar < Foo\n        def to_s\n          \"Bar\"\n        end\n      end\n\n      class Baz < Foo\n        def to_s\n          \"Baz\"\n        end\n      end\n\n      a = [Bar.new, Baz.new] of Foo\n      b = a.map { |e| e.clone }\n      CRYSTAL\n  end\n\n  it \"merges metaclass types\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type.metaclass }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      Foo || Bar\n      CRYSTAL\n  end\n\n  it \"merges metaclass types with 3 types\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type.metaclass }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      Foo || Bar || Baz\n      CRYSTAL\n  end\n\n  it \"types metaclass node\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type.metaclass }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = uninitialized Foo.class\n      a\n      CRYSTAL\n  end\n\n  it \"allows passing metaclass to virtual metaclass restriction\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].metaclass }\n      class Foo\n      end\n\n      def foo(x : Foo.class)\n        x\n      end\n\n      foo(Foo)\n      CRYSTAL\n  end\n\n  it \"allows passing metaclass to virtual metaclass restriction\" do\n    assert_type(<<-CRYSTAL) { types[\"Bar\"].metaclass }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : Foo.class)\n        x\n      end\n\n      foo(Bar)\n      CRYSTAL\n  end\n\n  it \"restricts virtual metaclass to Class (#11376)\" do\n    assert_type(<<-CRYSTAL) { nilable types[\"Foo\"].virtual_type.metaclass }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      x = Foo || Bar\n      x if x.is_a?(Class)\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/virtual_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: virtual\" do\n  it \"types two classes without a shared virtual\" do\n    assert_type(<<-CRYSTAL) { union_of(types[\"Foo\"], types[\"Bar\"]) }\n      class Foo\n      end\n\n      class Bar\n      end\n\n      a = Foo.new || Bar.new\n      CRYSTAL\n  end\n\n  it \"types class and subclass as one type\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      a = Foo.new || Bar.new\n      CRYSTAL\n  end\n\n  it \"types two subclasses\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      a = Bar.new || Baz.new\n      CRYSTAL\n  end\n\n  it \"types class and two subclasses\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      a = Foo.new || Bar.new || Baz.new\n      CRYSTAL\n  end\n\n  it \"types method call of virtual type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      a = Foo.new || Bar.new\n      a.foo\n      CRYSTAL\n  end\n\n  it \"types method call of virtual type with override\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo\n          1.5\n        end\n      end\n\n      a = Foo.new || Bar.new\n      a.foo\n      CRYSTAL\n  end\n\n  it \"dispatches virtual method\" do\n    nodes = parse(<<-CRYSTAL)\n      class Foo\n        def foo\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      x = Foo.new || Bar.new || Baz.new\n      x.foo\n      CRYSTAL\n    result = semantic nodes\n    _, nodes = result.program, result.node.as(Expressions)\n    nodes.last.as(Call).target_defs.not_nil!.size.should eq(1)\n  end\n\n  it \"dispatches virtual method with overload\" do\n    nodes = parse(<<-CRYSTAL)\n      class Foo\n        def foo\n        end\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n\n      class Baz < Foo\n      end\n\n      x = Foo.new || Bar.new || Baz.new\n      x.foo\n      CRYSTAL\n    result = semantic nodes\n    _, nodes = result.program, result.node.as(Expressions)\n    nodes.last.as(Call).target_defs.not_nil!.size.should eq(2)\n  end\n\n  it \"works with restriction alpha\" do\n    assert_no_errors <<-CRYSTAL\n      require \"prelude\"\n\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n\n      class Baz < Bar\n      end\n\n      class Ban < Bar\n      end\n\n      a = [nil, Foo.new, Bar.new, Baz.new]\n      a.push(Baz.new || Ban.new)\n      CRYSTAL\n  end\n\n  it \"doesn't check cover for subclasses\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      class Foo\n        def foo(other)\n          1\n        end\n      end\n\n      class Bar < Foo\n        def foo(other : Bar)\n          1.5\n        end\n      end\n\n      f = Foo.new || Bar.new\n      x = f.foo(f)\n      CRYSTAL\n  end\n\n  it \"removes instance var from subclasses\" do\n    nodes = parse <<-CRYSTAL\n      class Base\n      end\n\n      class Var < Base\n        def x=(x : Int32)\n          @x = x\n        end\n      end\n\n      class Base\n        def x=(x : Int32)\n          @x = x\n        end\n      end\n\n      v = Var.new\n      v.x = 1\n      v\n      CRYSTAL\n    result = semantic nodes\n    mod = result.program\n\n    var = mod.types[\"Var\"]\n    var.instance_vars.size.should eq(0)\n\n    base = mod.types[\"Base\"]\n    base.instance_vars[\"@x\"].type.should eq(mod.nilable(mod.int32))\n  end\n\n  it \"types inspect\" do\n    assert_type(<<-CRYSTAL) { string }\n      require \"prelude\"\n\n      class Foo\n      end\n\n      Foo.new.inspect\n      CRYSTAL\n  end\n\n  it \"reports no matches for virtual type\" do\n    assert_error <<-CRYSTAL, \"undefined method 'foo' for Foo\"\n      class Foo\n      end\n\n      class Bar < Foo\n        def foo\n        end\n      end\n\n      x = Foo.new || Bar.new\n      x.foo\n      CRYSTAL\n  end\n\n  it \"doesn't check methods on abstract classes\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      abstract class Foo\n      end\n\n      class Bar1 < Foo\n        def foo\n          1\n        end\n      end\n\n      class Bar2 < Foo\n        def foo\n          2.5\n        end\n      end\n\n      f = Bar1.new || Bar2.new\n      x = f.foo\n      CRYSTAL\n  end\n\n  it \"doesn't check methods on abstract classes 2\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64, char) }\n      abstract class Foo\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Bar2 < Bar\n        def foo\n          1\n        end\n      end\n\n      class Bar3 < Foo\n        def foo\n          2.5\n        end\n      end\n\n      class Baz < Foo\n        def foo\n          'a'\n        end\n      end\n\n      f = Bar2.new || Bar3.new || Baz.new\n      x = f.foo\n      CRYSTAL\n  end\n\n  it \"reports undefined method in subclass of abstract class\" do\n    assert_error <<-CRYSTAL, \"undefined method 'foo' for Bar3\"\n      abstract class Foo\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Bar2 < Bar\n        def foo\n          1\n        end\n      end\n\n      class Bar3 < Bar\n      end\n\n      class Baz < Foo\n        def foo\n          'a'\n        end\n      end\n\n      f = Bar2.new || Bar3.new || Baz.new\n      x = f.foo\n      CRYSTAL\n  end\n\n  it \"doesn't check cover for abstract classes\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64, char) }\n      abstract class Foo\n        def foo(other)\n          1\n        end\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Bar1 < Bar\n      end\n\n      class Bar2 < Bar\n      end\n\n      class Baz < Foo\n      end\n\n      def foo(other : Bar1)\n        1\n      end\n\n      def foo(other : Bar2)\n        2.5\n      end\n\n      def foo(other : Baz)\n        'a'\n      end\n\n      f = Bar1.new || Bar2.new || Baz.new\n      foo(f)\n      CRYSTAL\n  end\n\n  it \"reports missing cover for subclass of abstract class\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'foo' to be Bar1 or Baz, not Foo\"\n      abstract class Foo\n        def foo(other)\n          1\n        end\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Bar1 < Bar\n      end\n\n      class Bar2 < Bar\n      end\n\n      class Baz < Foo\n      end\n\n      def foo(other : Bar1)\n        1\n      end\n\n      def foo(other : Baz)\n        'a'\n      end\n\n      f = Bar1.new || Bar2.new || Baz.new\n      foo(f)\n      CRYSTAL\n  end\n\n  it \"checks cover in every concrete subclass\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      abstract class Foo\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Bar1 < Bar\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      class Bar2 < Bar\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      class Baz < Foo\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      f = Bar1.new || Bar2.new || Baz.new\n      f.foo(f)\n      CRYSTAL\n  end\n\n  it \"checks cover in every concrete subclass 2\" do\n    assert_error <<-CRYSTAL, \"expected argument #1 to 'Baz#foo' to be Bar1 or Baz, not Foo\"\n      abstract class Foo\n      end\n\n      abstract class Bar < Foo\n      end\n\n      class Bar1 < Bar\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      class Bar2 < Bar\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      class Baz < Foo\n        def foo(x : Bar1); end\n        def foo(x : Baz); end\n      end\n\n      f = Bar1.new || Bar2.new || Baz.new\n      f.foo(f)\n      CRYSTAL\n  end\n\n  it \"checks cover in every concrete subclass 3\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      abstract class Foo\n      end\n\n      abstract class Bar < Foo\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      class Bar1 < Bar\n      end\n\n      class Bar2 < Bar\n      end\n\n      class Baz < Foo\n        def foo(x : Bar1); end\n        def foo(x : Bar2); end\n        def foo(x : Baz); end\n      end\n\n      f = Bar1.new || Bar2.new || Baz.new\n      f.foo(f)\n      CRYSTAL\n  end\n\n  it \"checks method in every concrete subclass but method in Object\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      class Object\n        def foo\n        end\n      end\n\n      abstract class Foo\n      end\n\n      class Bar1 < Foo\n      end\n\n      class Bar2 < Foo\n      end\n\n      f = Bar1.new || Bar2.new\n      f.foo\n      CRYSTAL\n  end\n\n  # it \"recalculates virtual type when subclass is added\" do\n  #   assert_type(<<-CRYSTAL) { union_of(nil_type, int32, char) }\n  #     class Foo\n  #       def foo\n  #         nil\n  #       end\n  #     end\n\n  #     class Bar(T) < Foo\n  #       def initialize(x : T)\n  #         @x = x\n  #       end\n\n  #       def foo\n  #         @x\n  #       end\n  #     end\n\n  #     def coco(x)\n  #       x.foo\n  #     end\n\n  #     a = Foo.new || Bar.new(1)\n  #     b = coco(a)\n\n  #     a2 = Foo.new || Bar.new('a')\n  #     b2 = coco(a2)\n  #     CRYSTAL\n  # end\n\n  it \"finds overloads of union of virtual, class and nil\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      def foo(x : Reference)\n        1\n      end\n\n      def foo(x : Value)\n        1.5\n      end\n\n      f = Foo.new || Bar.new || Reference.new || nil\n      foo(f)\n      CRYSTAL\n  end\n\n  it \"finds overloads of union of virtual, class and nil with abstract class\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, float64) }\n      abstract class Foo\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n      end\n\n      def foo(x : Reference)\n        1\n      end\n\n      def foo(x : Value)\n        1.5\n      end\n\n      f = Bar.new || Baz.new || Reference.new || nil\n      foo(f)\n      CRYSTAL\n  end\n\n  it \"restricts with union and doesn't merge to super type\" do\n    assert_type(<<-CRYSTAL) { union_of(int32, char, string) }\n      abstract class Foo\n      end\n\n      class Bar < Foo\n        def foo\n          1\n        end\n      end\n\n      class Baz < Foo\n        def foo\n          'a'\n        end\n      end\n\n      class Bag < Foo\n      end\n\n      def foo(x : Bar | Baz)\n        x.foo\n      end\n\n      def foo(x)\n        \"hello\"\n      end\n\n      f = Bar.new || Baz.new || Bag.new\n      foo(f)\n      CRYSTAL\n  end\n\n  it \"uses virtual type as generic type if class is abstract\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", types[\"Foo\"].virtual_type }\n      abstract class Foo\n      end\n\n      class Bar(T)\n      end\n\n      Bar(Foo).new\n      CRYSTAL\n  end\n\n  it \"uses virtual type as generic type if class is abstract even in union\" do\n    assert_type(<<-CRYSTAL) { generic_class \"Bar\", union_of(types[\"Foo\"].virtual_type, int32) }\n      abstract class Foo\n      end\n\n      class Baz < Foo\n      end\n\n      class Bar(T)\n      end\n\n      Bar(Foo | Int32).new\n      CRYSTAL\n  end\n\n  it \"automatically does virtual for generic type if there are subclasses\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { pointer_of(types[\"Foo\"].virtual_type) }\n      class Foo; end\n      class Bar < Foo; end\n\n      Pointer(Foo).malloc(1_u64)\n      CRYSTAL\n  end\n\n  it \"types instance var as virtual when using type declaration and has subclasses\" do\n    assert_type(<<-CRYSTAL) { types[\"Foo\"].virtual_type! }\n      class Foo\n      end\n\n      class Bar < Foo\n        @foo : Foo\n\n        def initialize\n          @foo = Foo.new\n        end\n\n        def foo\n          @foo\n        end\n      end\n\n      Bar.new.foo\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/visibility_modifiers_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Visibility modifiers\" do\n  it \"disallows invoking private method\" do\n    assert_error <<-CRYSTAL, \"private method 'foo' called for Foo\"\n      class Foo\n        private def foo\n          1\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows setting visibility modifier to macro\" do\n    assert_error <<-CRYSTAL, \"private method 'foo' called for Foo\"\n      class Object\n        macro x\n          def foo\n          end\n        end\n      end\n\n      class Foo\n        private x\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows setting visibility modifier to macro that generates many methods (1)\" do\n    assert_error <<-CRYSTAL, \"private method 'foo' called for Foo\"\n      class Object\n        macro x\n          def foo\n          end\n\n          def bar\n          end\n        end\n      end\n\n      class Foo\n        private x\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows setting visibility modifier to macro that generates many methods (2)\" do\n    assert_error <<-CRYSTAL, \"private method 'bar' called for Foo\"\n      class Object\n        macro x\n          def foo\n          end\n\n          def bar\n          end\n        end\n      end\n\n      class Foo\n        private x\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"allows invoking private method from the same class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        private def foo\n          1\n        end\n\n        def bar\n          self.foo\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method from the same class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        protected def foo\n          1\n        end\n\n        def bar\n          self.foo\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method from subclass\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        protected def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n        def bar\n          Foo.new.foo\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method from subclass (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        protected def foo\n          1\n        end\n      end\n\n      class Bar < Foo\n      end\n\n      class Baz < Foo\n        def baz\n          Bar.new.foo\n        end\n      end\n\n      Baz.new.baz\n      CRYSTAL\n  end\n\n  it \"errors if invoking protected method from top-level\" do\n    assert_error <<-CRYSTAL, \"protected method 'foo' called for Foo\"\n      class Foo\n        protected def foo\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if invoking protected method from non-subclass\" do\n    assert_error <<-CRYSTAL, \"protected method 'foo' called for Foo\"\n      class Foo\n        protected def foo\n        end\n      end\n\n      class Bar\n        def bar\n          Foo.new.foo\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"errors if invoking protected method from non-subclass, generated with macro that generates a macro\" do\n    assert_error <<-CRYSTAL, \"protected method 'foo' called for Foo\"\n      class Object\n        macro y\n          def foo\n          end\n        end\n\n        macro x\n          y\n        end\n      end\n\n      class Foo\n        protected x\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"errors if applying visibility modifier to non-def or non-call\" do\n    assert_error <<-CRYSTAL, \"can't apply visibility modifier\"\n      class Foo\n        private 1\n      end\n      CRYSTAL\n  end\n\n  it \"allows invoking protected from instance to class\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def instance_foo\n          Foo.class_foo\n        end\n\n        protected def self.class_foo\n          1\n        end\n      end\n\n      Foo.new.instance_foo\n      CRYSTAL\n  end\n\n  it \"automatically makes initialize be protected\" do\n    assert_error <<-CRYSTAL, \"protected method 'initialize' called for Foo\"\n      class Foo\n        def initialize(x)\n        end\n      end\n\n      foo = Foo.new(1)\n      foo.initialize(2)\n      CRYSTAL\n  end\n\n  it \"allows invoking private setter with self\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        private def x=(x)\n          x\n        end\n\n        def foo\n          self.x = 1\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method from namespace to namespaced type\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          Bar.new.bar\n        end\n\n        class Bar\n          protected def bar\n            1\n          end\n        end\n      end\n\n      Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method from namespaced type to namespace\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        protected def foo\n          1\n        end\n\n        class Bar\n          def bar\n            Foo.new.foo\n          end\n        end\n      end\n\n      Foo::Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method between types in the same namespace\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module NS1\n        class NS2\n          class Foo\n            def foo\n              Bar.new.bar\n            end\n          end\n\n          class Bar\n            protected def bar\n              1\n            end\n          end\n        end\n      end\n\n      NS1::NS2::Foo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method between types in the same namespace when inheriting\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      module NS1\n        class NS2\n          class Foo\n            def foo\n              Bar.new.bar\n            end\n          end\n\n          class Bar\n            protected def bar\n              1\n            end\n          end\n        end\n      end\n\n      class MyFoo < NS1::NS2::Foo\n      end\n\n      MyFoo.new.foo\n      CRYSTAL\n  end\n\n  it \"allows invoking protected method from virtual type\" do\n    assert_type(<<-CRYSTAL) { union_of int32, float64 }\n      abstract class Foo\n        def foo\n          bar\n        end\n      end\n\n      class Bar < Foo\n        protected def bar\n          1\n        end\n      end\n\n      class Baz < Foo\n        protected def bar\n          1.5\n        end\n      end\n\n      (Bar.new || Baz.new).foo\n      CRYSTAL\n  end\n\n  it \"allows calling protected method from nested generic class (1)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        class Bar(U)\n          def bar\n            Foo.new.foo\n          end\n        end\n\n        protected def foo\n          1\n        end\n      end\n\n      Foo::Bar(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"allows calling protected method from nested generic class (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo(T)\n        class Bar(U)\n          def bar\n            Foo(Int32).new.foo\n          end\n        end\n\n        protected def foo\n          1\n        end\n      end\n\n      Foo::Bar(Int32).new.bar\n      CRYSTAL\n  end\n\n  it \"gives correct error on unknown call (#2838)\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'foo'\"\n      private foo\n      CRYSTAL\n  end\n\n  it \"defines protected initialize (#7501)\" do\n    assert_error <<-CRYSTAL, \"protected method 'new' called for Foo.class\"\n      class Foo\n        protected def initialize\n        end\n      end\n\n      Foo.new\n      CRYSTAL\n  end\n\n  it \"handles virtual types (#8561)\" do\n    assert_no_errors <<-CRYSTAL\n      module Namespace\n        class Foo\n          protected def foo\n          end\n        end\n\n        class Bar\n          def bar\n            Foo.new.foo\n          end\n        end\n\n        class Baz < Bar\n          def initialize\n            @bar = Bar.new\n          end\n\n          def bar\n            @bar.bar\n          end\n        end\n      end\n\n      Namespace::Baz.new.bar\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/warnings_spec.cr",
    "content": "require \"../spec_helper\"\n\ndescribe \"Semantic: warnings\" do\n  describe \"deprecated types\" do\n    it \"detects deprecated class methods\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        class Foo\n        end\n\n        Foo.new\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated Foo.\"\n\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        module Foo::Bar\n          def self.baz\n          end\n        end\n\n        Foo::Bar.baz\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo::Bar.\"\n    end\n\n    it \"detects deprecated superclass\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        class Foo\n        end\n\n        class Bar < Foo\n        end\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated Foo.\"\n    end\n\n    it \"doesn't check superclass when the class is deprecated\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        class Foo\n        end\n\n        @[Deprecated]\n        class Bar < Foo\n        end\n\n        Bar.new\n        CRYSTAL\n        \"warning in line 9\\nWarning: Deprecated Bar.\"\n    end\n\n    it \"detects deprecated type reference\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        class Foo\n        end\n\n        def p(x)\n          x\n        end\n\n        p Foo\n        CRYSTAL\n        \"warning in line 9\\nWarning: Deprecated Foo.\"\n    end\n\n    it \"only affects the type not the namespace\" do\n      assert_no_warning <<-CRYSTAL\n        @[Deprecated]\n        class Foo\n          class Bar\n          end\n        end\n\n        Foo::Bar.new\n        CRYSTAL\n    end\n\n    it \"doesn't deprecate instance methods (constructors already warn)\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        class Foo\n          def do_something\n          end\n        end\n\n        foo = Foo.new\n        foo.do_something\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.\"\n    end\n\n    it \"detects deprecated through alias\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        class Foo\n        end\n\n        alias Bar = Foo\n        alias Baz = Bar\n\n        Baz.new\n        CRYSTAL\n        \"warning in line 8\\nWarning: Deprecated Foo.\"\n    end\n\n    it \"detects deprecated constant in generic argument\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        class Foo\n        end\n\n        class Bar(T)\n        end\n\n        Bar(Foo)\n        CRYSTAL\n        \"warning in line 8\\nWarning: Deprecated Foo. Do not use me\"\n    end\n\n    it \"detects deprecated constant in include\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        module Foo\n        end\n\n        class Bar\n          include Foo\n        end\n\n        Bar.new\n        CRYSTAL\n        \"warning in line 6\\nWarning: Deprecated Foo. Do not use me\"\n    end\n\n    it \"detects deprecated constant in extend\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        module Foo\n        end\n\n        class Bar\n          extend Foo\n        end\n        CRYSTAL\n        \"warning in line 6\\nWarning: Deprecated Foo. Do not use me\"\n    end\n  end\n\n  describe \"deprecated alias\" do\n    it \"detects deprecated class method calls\" do\n      assert_warning <<-CRYSTAL,\n        class Foo\n        end\n\n        @[Deprecated(\"Use Foo.\")]\n        alias Bar = Foo\n\n        Bar.new\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Bar. Use Foo.\"\n\n      assert_warning <<-CRYSTAL,\n        module Foo::Bar\n          def self.baz; end\n        end\n\n        @[Deprecated(\"Use Foo::Bar.\")]\n        alias Bar = Foo::Bar\n\n        Bar.baz\n        CRYSTAL\n        \"warning in line 8\\nWarning: Deprecated Bar. Use Foo::Bar.\"\n    end\n\n    it \"doesn't deprecate the aliased type\" do\n      assert_no_warning <<-CRYSTAL\n        class Foo\n        end\n\n        @[Deprecated(\"Use Foo.\")]\n        alias Bar = Foo\n\n        Foo.new\n        CRYSTAL\n    end\n  end\n\n  describe \"deprecated annotations\" do\n    it \"detects deprecated annotations\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        annotation Foo; end\n\n        @[Foo]\n        def bar; end\n\n        bar\n        CRYSTAL\n        \"warning in line 2\\nWarning: Deprecated annotation Foo.\"\n    end\n\n    it \"detects deprecated namespaced annotations\" do\n      assert_warning <<-CRYSTAL,\n        module MyNamespace\n          @[Deprecated]\n          annotation Foo; end\n        end\n\n        @[MyNamespace::Foo]\n        def bar; end\n\n        bar\n        CRYSTAL\n        \"warning in line 3\\nWarning: Deprecated annotation MyNamespace::Foo.\"\n    end\n  end\n\n  describe \"deprecated methods\" do\n    it \"detects top-level deprecated methods\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        def foo\n        end\n\n        foo\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated ::foo. Do not use me\"\n    end\n\n    it \"deprecation reason is optional\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        def foo\n        end\n\n        foo\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated ::foo.\"\n    end\n\n    it \"detects deprecated instance methods\" do\n      assert_warning <<-CRYSTAL,\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          def m\n          end\n        end\n\n        Foo.new.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo#m. Do not use me\"\n    end\n\n    it \"detects deprecated class methods\" do\n      assert_warning <<-CRYSTAL,\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          def self.m\n          end\n        end\n\n        Foo.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.m. Do not use me\"\n    end\n\n    it \"detects deprecated generic instance methods\" do\n      assert_warning <<-CRYSTAL,\n        class Foo(T)\n          @[Deprecated(\"Do not use me\")]\n          def m\n          end\n        end\n\n        Foo(Int32).new.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo(Int32)#m. Do not use me\"\n    end\n\n    it \"detects deprecated generic class methods\" do\n      assert_warning <<-CRYSTAL,\n        class Foo(T)\n          @[Deprecated(\"Do not use me\")]\n          def self.m\n          end\n        end\n\n        Foo(Int32).m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo(Int32).m. Do not use me\"\n    end\n\n    it \"detects deprecated module methods\" do\n      assert_warning <<-CRYSTAL,\n        module Foo\n          @[Deprecated(\"Do not use me\")]\n          def self.m\n          end\n        end\n\n        Foo.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.m. Do not use me\"\n    end\n\n    it \"detects deprecated methods with named arguments\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        def foo(*, a)\n        end\n\n        foo(a: 2)\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated ::foo.\"\n    end\n\n    it \"detects deprecated initialize\" do\n      assert_warning <<-CRYSTAL,\n        class Foo\n          @[Deprecated]\n          def initialize\n          end\n        end\n\n        Foo.new\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.new.\"\n    end\n\n    it \"detects deprecated initialize with named arguments\" do\n      assert_warning <<-CRYSTAL,\n        class Foo\n          @[Deprecated]\n          def initialize(*, a)\n          end\n        end\n\n        Foo.new(a: 2)\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.new:a.\"\n    end\n\n    it \"informs warnings once per call site location (a)\" do\n      warning_failures = warnings_result <<-CRYSTAL\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          def m\n          end\n\n          def b\n            m\n          end\n        end\n\n        Foo.new.b\n        Foo.new.b\n        CRYSTAL\n      warning_failures.size.should eq(1)\n    end\n\n    it \"informs warnings once per call site location (b)\" do\n      warning_failures = warnings_result <<-CRYSTAL\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          def m\n          end\n        end\n\n        Foo.new.m\n        Foo.new.m\n        CRYSTAL\n\n      warning_failures.size.should eq(2)\n    end\n\n    it \"informs warnings once per yield\" do\n      warning_failures = warnings_result <<-CRYSTAL\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          def m\n          end\n        end\n\n        def twice\n          yield\n          yield\n        end\n\n        twice { Foo.new.m }\n        CRYSTAL\n\n      warning_failures.size.should eq(1)\n    end\n\n    it \"informs warnings once per target type\" do\n      warning_failures = warnings_result <<-CRYSTAL\n        class Foo(T)\n          @[Deprecated(\"Do not use me\")]\n          def m\n          end\n\n          def b\n            m\n          end\n        end\n\n        Foo(Int32).new.b\n        Foo(Int64).new.b\n        CRYSTAL\n\n      warning_failures.size.should eq(2)\n    end\n\n    it \"ignore deprecation excluded locations\" do\n      with_tempdir(\"check_warnings_excludes\") do\n        Dir.mkdir \"lib\"\n\n        # NOTE tempfile might be created in symlinked folder\n        # which affects how to match current dir /var/folders/...\n        # with the real path /private/var/folders/...\n        path = File.realpath(\".\")\n\n        main_filename = File.join(path, \"main.cr\")\n        output_filename = File.join(path, \"main\")\n\n        File.write main_filename, <<-CRYSTAL\n          require \"./lib/foo\"\n\n          bar\n          foo\n          CRYSTAL\n        File.write File.join(path, \"lib\", \"foo.cr\"), <<-CRYSTAL\n          @[Deprecated(\"Do not use me\")]\n          def foo\n          end\n\n          def bar\n            foo\n          end\n          CRYSTAL\n\n        compiler = create_spec_compiler\n        compiler.warnings.level = :all\n        compiler.warnings.exclude_lib_path = true\n        compiler.prelude = \"empty\"\n        compiler.compile Compiler::Source.new(main_filename, File.read(main_filename)), output_filename\n\n        compiler.warnings.infos.size.should eq(1)\n      end\n    end\n\n    it \"ignores nested calls to deprecated methods\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        def foo; bar; end\n\n        @[Deprecated]\n        def bar; end\n\n        foo\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated ::foo.\"\n    end\n\n    it \"errors if invalid argument type\" do\n      assert_error <<-CRYSTAL,\n        @[Deprecated(42)]\n        def foo\n        end\n        CRYSTAL\n        \"first argument must be a String\"\n    end\n\n    it \"errors if too many arguments\" do\n      assert_error <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\", \"extra arg\")]\n        def foo\n        end\n        CRYSTAL\n        \"wrong number of deprecated annotation arguments (given 2, expected 1)\"\n    end\n\n    it \"errors if invalid named arguments\" do\n      assert_error <<-CRYSTAL,\n        @[Deprecated(invalid: \"Do not use me\")]\n        def foo\n        end\n        CRYSTAL\n        \"too many named arguments (given 1, expected maximum 0)\"\n    end\n  end\n\n  describe \"deprecated method args\" do\n    describe \"defs\" do\n      it \"warns when a deprecated positional argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          def foo(a, @[Deprecated] b, c)\n          end\n\n          foo(1, 2, 3)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"warns when a deprecated keyword argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          def foo(x, *, a, @[Deprecated] b)\n          end\n\n          foo(0, a: 1, b: 2)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"warns when a deprecated splat argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          def foo(a, @[Deprecated] *args)\n          end\n\n          foo(1, 2)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument args.\"\n      end\n\n      it \"warns when a deprecated double splat argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          def foo(*, a, @[Deprecated] **opts)\n          end\n\n          foo(a: 1, bad: 2)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument opts.\"\n      end\n\n      it \"warns when a deprecated default positional argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          def foo(a, @[Deprecated] b = nil, c = nil)\n          end\n\n          foo(1, 2)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"warns when a deprecated default keyword argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          def foo(*, a, @[Deprecated] b = nil)\n          end\n\n          foo(a: 1, b: 2)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"doesn't warn when a deprecated default positional argument isn't explicitly passed\" do\n        assert_no_warning <<-CRYSTAL\n          def foo(a, @[Deprecated] b = nil, c = nil)\n          end\n\n          foo(1)\n          foo(1, c: 3)\n        CRYSTAL\n      end\n\n      it \"doesn't warn when a deprecated default keyword argument isn't explicitly passed\" do\n        assert_no_warning <<-CRYSTAL\n          def foo(*, a, @[Deprecated] b = nil)\n          end\n\n          foo(a: 1)\n        CRYSTAL\n      end\n\n      it \"warns when a default value calls a method with a deprecated arg\" do\n        assert_warning <<-CRYSTAL,\n          def bar(@[Deprecated] x)\n          end\n\n          def foo(a, @[Deprecated] b = bar(a))\n          end\n\n          foo(1)\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument x.\"\n\n        assert_warning <<-CRYSTAL,\n          def bar(@[Deprecated] x)\n          end\n\n          def foo(a, @[Deprecated] b = bar(a))\n          end\n\n          foo(1, 2)\n        CRYSTAL\n          \"warning in line 7\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"warns when a deprecated arg default value calls a method with a deprecated arg\" do\n        assert_warning <<-CRYSTAL,\n          def bar(@[Deprecated] x)\n          end\n\n          def foo(@[Deprecated] b = bar(1))\n          end\n\n          foo\n        CRYSTAL\n          \"warning in line 4\\nWarning: Deprecated argument x.\"\n      end\n    end\n\n    describe \"constructors\" do\n      it \"warns when a deprecated positional arg is passed\" do\n        assert_warning <<-CRYSTAL,\n          class Foo\n            def initialize(a, @[Deprecated] b, c)\n            end\n          end\n\n          Foo.new(1, 2, 3)\n        CRYSTAL\n          \"warning in line 6\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"warns when a deprecated keyword argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          class Foo\n            def initialize(x, *, a, @[Deprecated] b)\n            end\n          end\n\n          Foo.new(0, a: 1, b: 2)\n        CRYSTAL\n          \"warning in line 6\\nWarning: Deprecated argument b.\"\n      end\n\n      it \"warns when a deprecated splat argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          class Foo\n            def initialize(a, @[Deprecated] *args)\n            end\n          end\n\n          Foo.new(1, 2)\n        CRYSTAL\n          \"warning in line 6\\nWarning: Deprecated argument args.\"\n      end\n\n      it \"warns when a deprecated double splat argument is passed\" do\n        assert_warning <<-CRYSTAL,\n          class Foo\n            def initialize(*, a, @[Deprecated] **opts)\n            end\n          end\n\n          Foo.new(a: 1, bad: 2)\n        CRYSTAL\n          \"warning in line 6\\nWarning: Deprecated argument opts.\"\n      end\n    end\n  end\n\n  describe \"deprecated macros\" do\n    it \"detects top-level deprecated macros\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        macro foo\n        end\n\n        foo\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated ::foo. Do not use me\"\n    end\n\n    it \"deprecation reason is optional\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        macro foo\n        end\n\n        foo\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated ::foo.\"\n    end\n\n    it \"detects deprecated class macros\" do\n      assert_warning <<-CRYSTAL,\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          macro m\n          end\n        end\n\n        Foo.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.m. Do not use me\"\n    end\n\n    it \"detects deprecated generic class macros\" do\n      assert_warning <<-CRYSTAL,\n        class Foo(T)\n          @[Deprecated(\"Do not use me\")]\n          macro m\n          end\n        end\n\n        Foo.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.m. Do not use me\"\n    end\n\n    it \"detects deprecated module macros\" do\n      assert_warning <<-CRYSTAL,\n        module Foo\n          @[Deprecated(\"Do not use me\")]\n          macro m\n          end\n        end\n\n        Foo.m\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated Foo.m. Do not use me\"\n    end\n\n    it \"detects deprecated macros with named arguments\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated]\n        macro foo(*, a)\n        end\n\n        foo(a: 2)\n        CRYSTAL\n        \"warning in line 5\\nWarning: Deprecated ::foo.\"\n    end\n\n    it \"informs warnings once per call site location (a)\" do\n      warning_failures = warnings_result <<-CRYSTAL\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          macro m\n          end\n\n          macro b\n            Foo.m\n          end\n        end\n\n        Foo.b\n        Foo.b\n        CRYSTAL\n\n      warning_failures.size.should eq(1)\n    end\n\n    it \"informs warnings once per call site location (b)\" do\n      warning_failures = warnings_result <<-CRYSTAL\n        class Foo\n          @[Deprecated(\"Do not use me\")]\n          macro m\n          end\n        end\n\n        Foo.m\n        Foo.m\n        CRYSTAL\n\n      warning_failures.size.should eq(2)\n    end\n\n    it \"ignore deprecation excluded locations\" do\n      with_tempdir(\"check_warnings_excludes\") do\n        Dir.mkdir_p \"lib\"\n\n        # NOTE tempfile might be created in symlinked folder\n        # which affects how to match current dir /var/folders/...\n        # with the real path /private/var/folders/...\n        path = File.realpath(\".\")\n\n        main_filename = File.join(path, \"main.cr\")\n        output_filename = File.join(path, \"main\")\n\n        File.write main_filename, %(\n          require \"./lib/foo\"\n\n          bar\n          foo\n        )\n        File.write File.join(path, \"lib\", \"foo.cr\"), %(\n          @[Deprecated(\"Do not use me\")]\n          macro foo\n          end\n\n          macro bar\n            foo\n          end\n        )\n\n        compiler = create_spec_compiler\n        compiler.warnings.level = :all\n        compiler.warnings.exclude_lib_path = true\n        compiler.prelude = \"empty\"\n        compiler.compile Compiler::Source.new(main_filename, File.read(main_filename)), output_filename\n\n        compiler.warnings.infos.size.should eq(1)\n      end\n    end\n\n    it \"errors if invalid argument type\" do\n      assert_error <<-CRYSTAL, \"first argument must be a String\"\n        @[Deprecated(42)]\n        macro foo\n        end\n        CRYSTAL\n    end\n\n    it \"errors if too many arguments\" do\n      assert_error <<-CRYSTAL, \"wrong number of deprecated annotation arguments (given 2, expected 1)\"\n        @[Deprecated(\"Do not use me\", \"extra arg\")]\n        macro foo\n        end\n        CRYSTAL\n    end\n\n    it \"errors if invalid named argument\" do\n      assert_error <<-CRYSTAL, \"too many named arguments (given 1, expected maximum 0)\"\n        @[Deprecated(invalid: \"Do not use me\")]\n        macro foo\n        end\n        CRYSTAL\n    end\n  end\n\n  describe \"deprecated constants\" do\n    it \"detects deprecated constants\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        FOO = 1\n\n        FOO\n        CRYSTAL\n        \"warning in line 4\\nWarning: Deprecated FOO. Do not use me\"\n    end\n\n    it \"detects deprecated constants inside macros\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        FOO = 1\n\n        {% FOO %}\n        CRYSTAL\n        \"warning in line 4\\nWarning: Deprecated FOO. Do not use me\"\n    end\n\n    it \"detects deprecated constants in type declarations (1)\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        FOO = 1\n\n        class Foo(N)\n        end\n\n        class Bar < Foo(FOO)\n        end\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated FOO. Do not use me\"\n    end\n\n    it \"detects deprecated constants in type declarations (2)\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        FOO = 1\n\n        module Foo(N)\n        end\n\n        class Bar\n          include Foo(FOO)\n        end\n        CRYSTAL\n        \"warning in line 8\\nWarning: Deprecated FOO. Do not use me\"\n    end\n\n    it \"detects deprecated constants in type declarations (3)\" do\n      assert_warning <<-CRYSTAL,\n        @[Deprecated(\"Do not use me\")]\n        FOO = 1\n\n        class Foo(N)\n        end\n\n        alias Bar = Foo(FOO)\n        CRYSTAL\n        \"warning in line 7\\nWarning: Deprecated FOO. Do not use me\"\n    end\n  end\n\n  describe \"abstract def positional parameter name mismatch\" do\n    it \"detects mismatch with single parameter\" do\n      assert_warning <<-CRYSTAL, \"warning in line 6\\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method\"\n        abstract class Foo\n          abstract def foo(x)\n        end\n\n        class Bar < Foo\n          def foo(y); end\n        end\n        CRYSTAL\n    end\n\n    it \"detects mismatch within many parameters\" do\n      assert_warning <<-CRYSTAL, \"warning in line 6\\nWarning: positional parameter 'e' corresponds to parameter 'c' of the overridden method\"\n        abstract class Foo\n          abstract def foo(a, b, c, d)\n        end\n\n        class Bar < Foo\n          def foo(a, b, e, d); end\n        end\n        CRYSTAL\n    end\n\n    it \"detects multiple mismatches\" do\n      warnings_result(<<-CRYSTAL).size.should eq(2)\n        abstract class Foo\n          abstract def foo(src, dst)\n        end\n\n        class Bar < Foo\n          def foo(dst, src); end\n        end\n        CRYSTAL\n    end\n\n    it \"respects external names of positional parameters (1)\" do\n      assert_warning <<-CRYSTAL, \"warning in line 6\\nWarning: positional parameter 'a' corresponds to parameter 'b' of the overridden method\"\n        abstract class Foo\n          abstract def foo(b)\n        end\n\n        class Bar < Foo\n          def foo(a b); end\n        end\n        CRYSTAL\n    end\n\n    it \"respects external names of positional parameters (2)\" do\n      assert_warning <<-CRYSTAL, \"warning in line 6\\nWarning: positional parameter 'b' corresponds to parameter 'a' of the overridden method\"\n        abstract class Foo\n          abstract def foo(a b)\n        end\n\n        class Bar < Foo\n          def foo(b); end\n        end\n        CRYSTAL\n    end\n\n    it \"doesn't warn if external parameter name matches (1)\" do\n      warnings_result(<<-CRYSTAL).should be_empty\n        abstract class Foo\n          abstract def foo(a)\n        end\n\n        class Bar < Foo\n          def foo(a b); end\n        end\n        CRYSTAL\n    end\n\n    it \"doesn't warn if external parameter name matches (2)\" do\n      warnings_result(<<-CRYSTAL).should be_empty\n        abstract class Foo\n          abstract def foo(a b)\n        end\n\n        class Bar < Foo\n          def foo(a c); end\n        end\n        CRYSTAL\n    end\n\n    it \"doesn't compare positional parameters to single splat\" do\n      warnings_result(<<-CRYSTAL).should be_empty\n        abstract class Foo\n          abstract def foo(x)\n        end\n\n        class Bar < Foo\n          def foo(*y); end\n        end\n        CRYSTAL\n    end\n\n    it \"doesn't compare single splats\" do\n      warnings_result(<<-CRYSTAL).should be_empty\n        abstract class Foo\n          abstract def foo(*x)\n        end\n\n        class Bar < Foo\n          def foo(*y); end\n        end\n        CRYSTAL\n    end\n\n    it \"informs warnings once per matching overload (1)\" do\n      assert_warning <<-CRYSTAL, \"warning in line 6\\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method\"\n        abstract class Foo\n          abstract def foo(x : Int32)\n        end\n\n        class Bar < Foo\n          def foo(y : Int32 | Char); end\n          def foo(x : Int32 | String); end\n        end\n        CRYSTAL\n    end\n\n    it \"informs warnings once per matching overload (2)\" do\n      warnings_result(<<-CRYSTAL).size.should eq(2)\n        abstract class Foo\n          abstract def foo(x : Int32)\n        end\n\n        class Bar < Foo\n          def foo(y : Int32 | Char); end\n          def foo(z : Int32 | String); end\n        end\n        CRYSTAL\n    end\n\n    describe \"stops warning after implementation with matching parameters is found (#12150)\" do\n      it \"exact match\" do\n        warnings_result(<<-CRYSTAL).should be_empty\n          abstract class Foo\n            abstract def foo(x : Int32)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32); end\n            def foo(y : Int32 | String); end\n          end\n          CRYSTAL\n      end\n\n      it \"contravariant restrictions\" do\n        warnings_result(<<-CRYSTAL).should be_empty\n          abstract class Foo\n            abstract def foo(x : Int32, y : Int32)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32 | Char, y : Int); end\n            def foo(y : Int32 | String, z : Int32); end\n          end\n          CRYSTAL\n      end\n\n      it \"different single splats\" do\n        warnings_result(<<-CRYSTAL).should be_empty\n          abstract class Foo\n            abstract def foo(x : Int32, *y)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32, *z); end\n            def foo(y : Int32 | String, *z); end\n          end\n          CRYSTAL\n      end\n\n      it \"reordered named parameters\" do\n        warnings_result(<<-CRYSTAL).should be_empty\n          abstract class Foo\n            abstract def foo(x : Int32, *, y : Int32, z : Int32)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32, *, z : Int32, y : Int32); end\n            def foo(w : Int, *, y : Int32, z : Int32); end\n          end\n          CRYSTAL\n      end\n    end\n\n    describe \"continues warning if implementation with matching parameters is not found (#12150)\" do\n      it \"not a full implementation\" do\n        assert_warning <<-CRYSTAL, \"warning in line 8\\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method\"\n          abstract class Foo\n            abstract def foo(x : Int32 | String)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32); end\n            def foo(x : String); end\n            def foo(y : Int32 | String); end\n          end\n          CRYSTAL\n      end\n\n      it \"single splat\" do\n        assert_warning <<-CRYSTAL, \"warning in line 7\\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method\"\n          abstract class Foo\n            abstract def foo(x : Int32)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32, *y); end\n            def foo(y : Int32 | String); end\n          end\n          CRYSTAL\n      end\n\n      it \"double splat\" do\n        assert_warning <<-CRYSTAL, \"warning in line 7\\nWarning: positional parameter 'z' corresponds to parameter 'x' of the overridden method\"\n          abstract class Foo\n            abstract def foo(x : Int32, *, y)\n          end\n\n          class Bar < Foo\n            def foo(x : Int32, **opts); end\n            def foo(z : Int32, *, y); end\n          end\n          CRYSTAL\n      end\n    end\n\n    it \"doesn't warn if current type is abstract (#12266)\" do\n      warnings_result(<<-CRYSTAL).should be_empty\n        class Foo\n          def foo(x); end\n        end\n\n        abstract class Bar < Foo\n          abstract def foo(y)\n        end\n\n        abstract class Baz < Bar\n        end\n        CRYSTAL\n    end\n\n    it \"doesn't warn if current type is a module (#12266)\" do\n      warnings_result(<<-CRYSTAL).should be_empty\n        module Foo\n          def foo(x); end # Warning: positional parameter 'x' corresponds to parameter 'y' of the overridden method Bar#foo(y), which has a different name and may affect named argument passing\n        end\n\n        module Bar\n          include Foo\n          abstract def foo(y)\n        end\n\n        module Baz\n          include Bar\n        end\n        CRYSTAL\n    end\n  end\n\n  it \"exposes syntax warnings\" do\n    assert_warning UInt64::MAX.to_s, \"Warning: #{UInt64::MAX} doesn't fit in an Int64, try using the suffix u64 or i128\"\n  end\n\n  it \"exposes syntax warnings after macro interpolation\" do\n    assert_warning \"{% begin %}0x8000_0000_0000_000{{ 0 }}{% end %}\", \"Warning: 0x8000_0000_0000_0000 doesn't fit in an Int64, try using the suffix u64 or i128\"\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/while_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: while\" do\n  it \"types while\" do\n    assert_type(\"while 1; 1; end\") { nil_type }\n  end\n\n  it \"types while with break without value\" do\n    assert_type(\"while 1; break; end\") { nil_type }\n  end\n\n  it \"types while with break with value\" do\n    assert_type(\"while 1; break 'a'; end\") { nilable char }\n  end\n\n  it \"types while with multiple breaks with value\" do\n    assert_type(<<-CRYSTAL) { nilable union_of(char, tuple_of([string, int32])) }\n      while 1\n        break 'a' if 1\n        break \"\", 123 if 1\n      end\n      CRYSTAL\n  end\n\n  it \"types endless while with break without value\" do\n    assert_type(\"while true; break; end\") { nil_type }\n  end\n\n  it \"types endless while with break with value\" do\n    assert_type(\"while true; break 1; end\") { int32 }\n  end\n\n  it \"types endless while with multiple breaks with value\" do\n    assert_type(<<-CRYSTAL) { union_of(char, tuple_of([string, int32])) }\n      while true\n        break 'a' if 1\n        break \"\", 123 if 1\n      end\n      CRYSTAL\n  end\n\n  it \"reports break cannot be used outside a while\" do\n    assert_error \"break\",\n      \"invalid break\"\n  end\n\n  it \"types while true as NoReturn\" do\n    assert_type(\"while true; end\") { no_return }\n  end\n\n  it \"types while (true) as NoReturn\" do\n    assert_type(\"while (true); end\") { no_return }\n  end\n\n  it \"types while ((true)) as NoReturn\" do\n    assert_type(\"while ((true)); end\") { no_return }\n  end\n\n  it \"reports next cannot be used outside a while\" do\n    assert_error \"next\",\n      \"invalid next\"\n  end\n\n  it \"uses var type inside while if endless loop\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = nil\n      while true\n        a = 1\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"uses var type inside while if endless loop (2)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      while true\n        a = 1\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"marks variable as nil if breaking before assigning to it in an endless loop\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      a = nil\n      while true\n        break if 1 == 2\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"marks variable as nil if breaking before assigning to it in an endless loop (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      while true\n        break if 1 == 2\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types while with && (#1425)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      a = 1\n      while a.is_a?(Int32) && (1 == 1)\n        a = nil\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types while with assignment\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      while a = 1\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types while with assignment and &&\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      while (a = 1) && (1 == 1)\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"types while with assignment and call\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      while (a = 1) > 0\n        break\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't modify var's type before while\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      x = 'x'\n      x.ord\n      while 1 == 2\n        x = 1\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"restricts type after while (#4242)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = nil\n      while a.nil?\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"restricts type after while with not (#4242)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = nil\n      while !a\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"restricts type after `while` with `not` and `and` (#4242)\" do\n    assert_type(<<-CRYSTAL) { tuple_of [int32, char] }\n      a = nil\n      b = nil\n      while !(a && b)\n        a = 1\n        b = 'a'\n      end\n      {a, b}\n      CRYSTAL\n  end\n\n  it \"doesn't restrict type after while if there's a break (#4242)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 }\n      a = nil\n      while a.nil?\n        if 1 == 1\n          break\n        end\n        a = 1\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't use type at end of endless while if variable is reassigned\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      while true\n        a = 1\n        if 1 == 1\n          break\n        end\n        a = 'x'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't use type at end of endless while if variable is reassigned (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }\n      a = \"\"\n      while true\n        a = 1\n        if 1 == 1\n          break\n        end\n        a = 'x'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"doesn't use type at end of endless while if variable is reassigned (3)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }\n      a = {1}\n      while true\n        a = a[0]\n        if 1 == 1\n          break\n        end\n        a = {'x'}\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"uses type at end of endless while if variable is reassigned, but not before first break\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable union_of(int32, char) }\n      while true\n        if 1 == 1\n          break\n        end\n        a = 1\n        if 1 == 1\n          break\n        end\n        a = 'x'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"uses type at end of endless while if variable is reassigned, but not before first break (2)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char, string) }\n      a = \"\"\n      while true\n        if 1 == 1\n          break\n        end\n        a = 1\n        if 1 == 1\n          break\n        end\n        a = 'x'\n      end\n      a\n      CRYSTAL\n  end\n\n  it \"rebinds condition variable after while body (#6158)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable types[\"Foo\"] }\n      class Foo\n        @parent : self?\n\n        def parent\n          @parent\n        end\n      end\n\n      class Bar\n        def initialize(@parent : Foo)\n        end\n\n        def parent\n          @parent\n        end\n      end\n\n      a = Foo.new\n      b = Bar.new(a)\n      while b = b.parent\n        break if 1 == 1\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"doesn't type var as nilable after break inside rescue\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      while true\n        begin\n          foo = 1\n          break\n        rescue\n        end\n      end\n      foo\n      CRYSTAL\n  end\n\n  it \"types variable as nilable if raise before assign\" do\n    assert_type(<<-CRYSTAL) { nilable int32 }\n      require \"prelude\"\n\n      while true\n        begin\n          raise \"oops\"\n          foo = 12345\n        rescue\n        end\n        break\n      end\n      foo\n      CRYSTAL\n  end\n\n  it \"finds while cond assign target in Not (#10345)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      while !(x = 1 || nil)\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"finds all while cond assign targets in expressions (#10350)\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      a = 1\n      while ((b = 1); a)\n        a = nil\n        b = \"hello\"\n      end\n      b\n      CRYSTAL\n  end\n\n  it \"finds all while cond assign targets in expressions (2)\" do\n    assert_type(<<-CRYSTAL) { tuple_of [int32, int32] }\n      def foo(x, y)\n        true ? 1 : nil\n      end\n\n      while foo(a = 1, b = 1)\n        a = nil\n        b = \"hello\"\n      end\n\n      {a, b}\n      CRYSTAL\n  end\n\n  it \"finds all while cond assign targets in expressions (3)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable union_of(int32, char) }\n      while 1 == 1 ? (x = 1; 1 == 1) : false\n        x = 'a'\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"finds all while cond assign targets in expressions (4)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char, string) }\n      x = \"\"\n      while 1 == 1 ? (x = 1; 1 == 1) : false\n        x = 'a'\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"finds all while cond assign targets in expressions (5)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { nilable union_of(int32, char) }\n      while 1 == 1 ? (x = 1; 1 == 1) : false\n        x\n        x = 'a'\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"finds all while cond assign targets in expressions (6)\" do\n    assert_type(<<-CRYSTAL, inject_primitives: true) { tuple_of [int32, int32] }\n       while (x = true ? (y = 1) : 1; y = x; 1 == 1)\n         x = 'a'\n       end\n       {x, y}\n       CRYSTAL\n  end\n\n  it \"doesn't fail on new variables inside typeof condition\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      def foo\n        while typeof(x = 1)\n          return \"\"\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on nested conditionals inside typeof condition\" do\n    assert_type(<<-CRYSTAL) { nilable string }\n      def foo\n        while typeof(1 || 'a')\n          return \"\"\n        end\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on Expressions condition (1)\" do\n    assert_type(<<-CRYSTAL) { union_of int32.metaclass, char }\n      def foo\n        while (v = 1; true)\n          return typeof(v)\n        end\n        'a'\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't fail on Expressions condition (2)\" do\n    assert_type(<<-CRYSTAL) { union_of nil_type.metaclass, char }\n      def foo\n        while (v = nil; true)\n          return typeof(v)\n        end\n        'a'\n      end\n\n      foo\n      CRYSTAL\n  end\n\n  it \"doesn't modify variables unchanged in condition and body\" do\n    assert_no_errors <<-CRYSTAL\n      abstract class Base; end\n\n      class A < Base; end\n\n      class B < Base; end\n\n      class C < Base; end\n\n      def foo(x : A | B)\n      end\n\n      el = A.new.as(Base)\n      if el.is_a?(A) || el.is_a?(B)\n        while false\n          break\n        end\n\n        foo(el)\n      end\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/semantic/yield_with_scope_spec.cr",
    "content": "require \"../../spec_helper\"\n\ndescribe \"Semantic: yield with scope\" do\n  it \"infer type of empty block body\" do\n    assert_type(<<-CRYSTAL) { nil_type }\n      def foo; with 1 yield; end\n\n      foo do\n      end\n      CRYSTAL\n  end\n\n  it \"infer type of block body\" do\n    input = parse <<-CRYSTAL\n      def foo; with 1 yield; end\n\n      foo do\n        x = 1\n      end\n      CRYSTAL\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n    call = input.last.as(Call)\n    assign = call.block.not_nil!.body.as(Assign)\n    assign.target.type.should eq(mod.int32)\n  end\n\n  it \"infer type of block body with yield scope\" do\n    input = parse <<-CRYSTAL\n      require \"primitives\"\n\n      def foo; with 1 yield; end\n\n      foo do\n        to_i64\n      end\n      CRYSTAL\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n    input.last.as(Call).block.not_nil!.body.type.should eq(mod.int64)\n  end\n\n  it \"infer type of block body with yield scope and arguments\" do\n    input = parse <<-CRYSTAL\n      require \"primitives\"\n\n      def foo; with 1 yield 1.5; end\n\n      foo do |f|\n        to_i64 + f\n      end\n      CRYSTAL\n    result = semantic input\n    mod, input = result.program, result.node.as(Expressions)\n    input.last.as(Call).block.not_nil!.body.type.should eq(mod.float64)\n  end\n\n  it \"passes #229\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          1\n        end\n      end\n\n      def a\n        with Foo.new yield\n      end\n\n      module Bar\n        x = a { foo }\n      end\n      x\n      CRYSTAL\n  end\n\n  it \"invokes nested calls\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def x\n          with self yield\n        end\n\n        def y\n          yield\n        end\n      end\n\n      def bar\n        yield\n      end\n\n      foo = Foo.new\n      foo.x do\n        bar do\n          y do\n            1\n          end\n        end\n      end\n      CRYSTAL\n  end\n\n  it \"finds macro\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def x\n          with self yield\n        end\n\n        macro y\n          1\n        end\n      end\n\n      def bar\n        yield\n      end\n\n      foo = Foo.new\n      foo.x do\n        y\n      end\n      CRYSTAL\n  end\n\n  it \"errors if using instance variable at top level\" do\n    assert_error <<-CRYSTAL, \"can't use instance variables at the top level\"\n      class Foo\n        def foo\n          with self yield\n        end\n      end\n\n      Foo.new.foo do\n        @foo\n      end\n      CRYSTAL\n  end\n\n  it \"uses instance variable of enclosing scope\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          with self yield\n        end\n      end\n\n      class Bar\n        def initialize\n          @x = 1\n        end\n\n        def bar\n          Foo.new.foo do\n            @x\n          end\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"uses method of enclosing scope\" do\n    assert_type(<<-CRYSTAL) { int32 }\n      class Foo\n        def foo\n          with self yield\n        end\n      end\n\n      class Bar\n        def bar\n          Foo.new.foo do\n            baz\n          end\n        end\n\n        def baz\n          1\n        end\n      end\n\n      Bar.new.bar\n      CRYSTAL\n  end\n\n  it \"mentions with yield scope and current scope in error\" do\n    assert_error <<-CRYSTAL, \"undefined local variable or method 'baz' for Int32 (with ... yield) and Foo (current scope)\"\n      def foo\n        with 1 yield\n      end\n\n      class Foo\n        def bar\n          foo do\n            baz\n          end\n        end\n      end\n\n      Foo.new.bar\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/compiler/spec_helper.cr",
    "content": "require \"../spec_helper\"\nrequire \"../support/tempfile\"\n\ndef compiler_datapath(*components)\n  File.join(\"spec\", \"compiler\", \"data\", *components)\nend\n"
  },
  {
    "path": "spec/compiler/util_spec.cr",
    "content": "require \"spec\"\nrequire \"compiler/crystal/util\"\n\ndescribe Crystal do\n  describe \"normalize_path\" do\n    sep = {{ flag?(:win32) ? \"\\\\\" : \"/\" }}\n\n    it { Crystal.normalize_path(\"a\").should eq \".#{sep}a\" }\n    it { Crystal.normalize_path(\"./a/b\").should eq \".#{sep}a#{sep}b\" }\n    it { Crystal.normalize_path(\"../a/b\").should eq \".#{sep}..#{sep}a#{sep}b\" }\n    it { Crystal.normalize_path(\"/foo/bar\").should eq \"#{sep}foo#{sep}bar\" }\n\n    {% if flag?(:win32) %}\n      it { Crystal.normalize_path(\"C:\\\\foo\\\\bar\").should eq \"C:\\\\foo\\\\bar\" }\n      it { Crystal.normalize_path(\"C:foo\\\\bar\").should eq \"C:foo\\\\bar\" }\n      it { Crystal.normalize_path(\"\\\\foo\\\\bar\").should eq \"\\\\foo\\\\bar\" }\n      it { Crystal.normalize_path(\"foo\\\\bar\").should eq \".\\\\foo\\\\bar\" }\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/compiler_spec.cr",
    "content": "require \"./compiler/**\"\n"
  },
  {
    "path": "spec/debug/arrays.cr",
    "content": "a = [0, 1, 4, 9, 16, 25]\n# print: *a\n# lldb-check: (Array(Int32)) $0 = ([0] = 0, [1] = 1, [2] = 4, [3] = 9, [4] = 16, [5] = 25)\n# gdb-check: $1 = Array(Int32) = {0, 1, 4, 9, 16, 25}\ndebugger\na << 36\n# print: *a\n# lldb-check: (Array(Int32)) $1 = ([0] = 0, [1] = 1, [2] = 4, [3] = 9, [4] = 16, [5] = 25, [6] = 36)\n# gdb-check: $2 = Array(Int32) = {0, 1, 4, 9, 16, 25, 36}\ndebugger\na.unshift 49\n# print: *a\n# lldb-check: (Array(Int32)) $2 = ([0] = 49, [1] = 0, [2] = 1, [3] = 4, [4] = 9, [5] = 16, [6] = 25, [7] = 36)\n# gdb-check: $3 = Array(Int32) = {49, 0, 1, 4, 9, 16, 25, 36}\ndebugger\n"
  },
  {
    "path": "spec/debug/blocks.cr",
    "content": "[\"hello world\"].each do |v|\n  a = v\n  # print: a\n  # lldb-check: (String *) $0 = {{0x[0-9a-f]+}} \"hello world\"\n  # gdb-check: $1 = \"hello world\"\n  # print: v\n  # lldb-check: (String *) $1 = {{0x[0-9a-f]+}} \"hello world\"\n  # gdb-check: $2 = \"hello world\"\n  debugger\nend\n"
  },
  {
    "path": "spec/debug/driver.cr",
    "content": "abstract class DebuggerRunner\n  CRYSTAL    = ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"]? || \"#{REPO_BASE_DIR}/bin/crystal\"\n  FILE_CHECK = \"FileCheck#{File.basename(`#{__DIR__}/../../src/llvm/ext/find-llvm-config.sh`).lchop(\"llvm-config\")}\"\n\n  REPO_BASE_DIR      = \"#{__DIR__}/../../\"\n  SESSION_OUTPUT_DIR = File.join(REPO_BASE_DIR, \"tmp\", \"debug\")\n  DEBUG_BIN          = File.join(REPO_BASE_DIR, \".build\", \"debug_test_case\")\n\n  def initialize(@input : String)\n    Dir.mkdir_p(SESSION_OUTPUT_DIR)\n    @basename = File.join(SESSION_OUTPUT_DIR, File.basename(@input, File.extname(@input)))\n  end\n\n  abstract def name\n\n  def setup\n    check_regex = /# (?:#{Regex.escape(name)}-)?check: (.*)/\n\n    File.open(\"#{@basename}.#{name}-script\", \"w\") do |script|\n      script_header(script)\n\n      File.open(\"#{@basename}.#{name}-assert\", \"w\") do |assert|\n        File.each_line(@input) do |line|\n          if md = line.match(/# print: (.*)/)\n            command = print_command(md[1])\n            script.puts command\n            assert << \"CHECK: \"\n            debugger_prompt(assert, command)\n          elsif md = line.match(check_regex)\n            assert.puts \"CHECK-NEXT: #{md[1]}\"\n          elsif line.matches?(/\\bdebugger\\b/)\n            script_continue(script)\n          end\n        end\n      end\n    end\n  end\n\n  abstract def script_header(script : IO)\n  abstract def script_continue(script : IO)\n  abstract def print_command(expr)\n  abstract def debugger_prompt(assert : IO, command)\n\n  def run_compiler\n    Dir.mkdir_p(File.dirname(DEBUG_BIN))\n    Process.run(CRYSTAL, [\"build\", \"--debug\", @input, \"-o\", DEBUG_BIN], error: Process::Redirect::Inherit)\n  end\n\n  abstract def run_debugger\n\n  def run_file_check\n    File.open(\"#{@basename}.#{name}-session\", \"r\") do |session|\n      Process.run(FILE_CHECK, [\"#{@basename}.#{name}-assert\"], input: session, error: Process::Redirect::Inherit)\n    end\n  end\nend\n\nclass LLDBRunner < DebuggerRunner\n  CRYSTAL_FORMATTERS = File.expand_path(File.join(REPO_BASE_DIR, \"etc\", \"lldb\", \"crystal_formatters.py\"))\n\n  def name\n    \"lldb\"\n  end\n\n  def script_header(script : IO)\n    script.puts \"version\"\n    script.puts \"command script import #{CRYSTAL_FORMATTERS}\"\n\n    # skip signals intentionally raised during GC initialization: https://hboehm.info/gc/debugging.html\n    script.puts \"breakpoint set -n main -G true -o true -C 'process handle -s false -n false SIGSEGV SIGBUS'\"\n    script.puts \"breakpoint set -n __crystal_main -G true -o true -C 'process handle -s true -n true SIGSEGV SIGBUS'\"\n\n    script.puts \"run\"\n  end\n\n  def script_continue(script : IO)\n    script.puts \"continue\"\n  end\n\n  def print_command(expr)\n    \"print #{expr}\"\n  end\n\n  def debugger_prompt(assert : IO, command)\n    assert.puts \"(lldb) #{command}\"\n  end\n\n  def run_debugger\n    File.open(\"#{@basename}.#{name}-session\", \"w\") do |session|\n      Process.run(\"lldb\", [\"-b\", \"--source\", \"#{@basename}.#{name}-script\", DEBUG_BIN], output: session)\n    end\n  end\nend\n\nclass GDBRunner < DebuggerRunner\n  CRYSTAL_FORMATTERS = File.expand_path(File.join(REPO_BASE_DIR, \"etc\", \"gdb\", \"crystal_formatters.py\"))\n\n  def name\n    \"gdb\"\n  end\n\n  def script_header(script : IO)\n    script.puts \"source #{CRYSTAL_FORMATTERS}\"\n\n    # skip signals intentionally raised during GC initialization: https://hboehm.info/gc/debugging.html\n    script.puts <<-GDB\n    tbreak main\n    commands\n    silent\n    handle SIGSEGV nostop noprint\n    handle SIGBUS nostop noprint\n    continue\n    end\n    tbreak __crystal_main\n    commands\n    silent\n    handle SIGSEGV stop print\n    handle SIGBUS stop print\n    continue\n    end\n    GDB\n\n    script.puts \"set trace-commands on\"\n    script.puts \"run\"\n  end\n\n  def script_continue(script : IO)\n    script.puts \"continue\"\n  end\n\n  def print_command(expr)\n    \"print #{expr}\"\n  end\n\n  def debugger_prompt(assert : IO, command)\n    assert.puts \"+#{command}\"\n  end\n\n  def run_debugger\n    File.open(\"#{@basename}.#{name}-session\", \"w\") do |session|\n      Process.run(\"gdb\", [\"--batch\", \"-x\", \"#{@basename}.#{name}-script\", DEBUG_BIN], output: session)\n    end\n  end\nend\n\ninput = ARGV.shift\ndebugger_name = ARGV.shift? || \"lldb\"\n\nrunner = case debugger_name\n         when \"lldb\"\n           LLDBRunner.new(input)\n         when \"gdb\"\n           GDBRunner.new(input)\n         else\n           raise \"unknown debugger: #{debugger_name}\"\n         end\nrunner.setup\n\nstatus = runner.run_compiler\nexit 1 unless status.success?\n\nrunner.run_debugger\nstatus = runner.run_file_check\nexit status.exit_code\n"
  },
  {
    "path": "spec/debug/extern_unions.cr",
    "content": "@[Extern(union: true)]\nstruct Foo\n  @x : Float32\n  @y = uninitialized UInt32\n  @z = uninitialized UInt8[4]\n\n  def initialize(@x)\n  end\nend\n\nraise \"wrong endianness\" unless IO::ByteFormat::SystemEndian == IO::ByteFormat::LittleEndian\n\nx = Foo.new(1.0_f32)\n# print: x\n# lldb-check: $0 = (x = 1065353216, y = 1, z = \"\\0\\0\\x80?\")\n# gdb-check: $1 = {x = 1065353216, y = 1, z = \"\\000\\000\\200?\"}\ndebugger\n"
  },
  {
    "path": "spec/debug/large_enums.cr",
    "content": "enum SignedEnum : Int64\n  X = 0x0123_4567_89ab_cdef_i64\nend\n\nenum UnsignedEnum : UInt64\n  Y = 0xfedc_ba98_7654_3210_u64\nend\n\nx = SignedEnum::X\ny = UnsignedEnum::Y\n# print: x\n# lldb-check: (SignedEnum) $0 = X\n# print: y\n# lldb-check: (UnsignedEnum) $1 = Y\ndebugger\n"
  },
  {
    "path": "spec/debug/strings.cr",
    "content": "a = \"hello world\"\nb = \"abcσdeσf\"\n# print: a\n# lldb-check: (String *) $0 = {{0x[0-9a-f]+}} \"hello world\"\n# gdb-check: $1 = \"hello world\"\n# print: b\n# lldb-check: (String *) $1 = {{0x[0-9a-f]+}} \"abcσdeσf\"\n# gdb-check: $2 = \"abcσdeσf\"\ndebugger\n"
  },
  {
    "path": "spec/debug/test.sh",
    "content": "#!/bin/bash\n\n# This file can be executed from the root of the working copy\n#\n# $ ./spec/debug/test.sh [lldb|gdb]\n#\n# The argument selects one of the debuggers, defaulting to LLDB.\n#\n# It will use the ./spec/debug/driver.cr program to execute\n# the files explicitly listed at the end of this file.\n#\n# Those files have magic comments to build a script for a debugger session\n# and a FileCheck file with assertions over that session.\n#\n# The magic comments interpreted by the driver are:\n#\n# * `# print: expr`\n#   Prints the given expression in the debugger, e.g. a variable.\n# * `# check: pattern`\n#   Asserts that the debugger output matches the given FileCheck pattern.\n# * `# xxx-check: pattern`\n#   Like above, but only effective if `xxx` matches the debugger name. Has no\n#   effect for the other debuggers.\n#\n# These comments should then be followed by a call to `debugger` which sets up\n# the actual breakpoint.\n#\n# In ./tmp/debug you can find a dump of the session and the assertion file.\n\nset -euo pipefail\n\nSCRIPT_PATH=\"$(realpath \"$0\")\"\nSCRIPT_ROOT=\"$(dirname \"$SCRIPT_PATH\")\"\n\nBUILD_DIR=$SCRIPT_ROOT/../../.build\ncrystal=${CRYSTAL_SPEC_COMPILER_BIN:-$SCRIPT_ROOT/../../bin/crystal}\ndebugger=${1:-lldb}\ndriver=$BUILD_DIR/debug_driver\nmkdir -p \"$BUILD_DIR\"\n\"$crystal\" build \"$SCRIPT_ROOT\"/driver.cr -o \"$driver\"\n\n$driver \"$SCRIPT_ROOT\"/top_level.cr \"$debugger\"\n$driver \"$SCRIPT_ROOT\"/strings.cr \"$debugger\"\n$driver \"$SCRIPT_ROOT\"/arrays.cr \"$debugger\"\n$driver \"$SCRIPT_ROOT\"/blocks.cr \"$debugger\"\n$driver \"$SCRIPT_ROOT\"/large_enums.cr \"$debugger\"\n"
  },
  {
    "path": "spec/debug/top_level.cr",
    "content": "# print: a\n# lldb-check: (int) $0 = 0\n# gdb-check: $1 = 0\ndebugger\na = 42\n# print: a\n# lldb-check: (int) $1 = 42\n# gdb-check: $2 = 42\ndebugger\n"
  },
  {
    "path": "spec/generate_wasm32_spec.sh",
    "content": "#! /usr/bin/env bash\nset +x\n\n# This script iterates through each spec file and tries to build and run it.\n#\n# * `failed codegen` annotates specs that error in the compiler.\n#   This is mostly caused by some API not being ported to wasm32 (either the spec\n#   target itself or some tools used by the spec).\n# * `failed linking` annotates specs that compile but don't link.\n#   Most failures are caused by missing libraries (libxml2, libyaml, libgmp,\n#   libllvm, libz, libssl).\n# * `failed to run` annotates specs that compile and link but don't properly\n#   execute.\n#\n# PREREQUISITES:\n#\n# - wasmtime (https://wasmtime.dev/)\n#\n# Note: Libs are downloaded from https://github.com/lbguilherme/wasm-libs\n#\n# USAGE:\n#\n# $ spec/generate_wasm32_spec.sh > spec/wasm32_std_spec.cr\n\nWORK_DIR=$(mktemp -d)\nfunction cleanup {\n  rm -rf \"$WORK_DIR\"\n}\ntrap cleanup EXIT\n\nmkdir \"$WORK_DIR\"/wasm32-wasi-libs\ncurl -L https://github.com/lbguilherme/wasm-libs/releases/download/0.0.2/wasm32-wasi-libs.tar.gz | tar -C \"$WORK_DIR\"/wasm32-wasi-libs -xz\nexport CRYSTAL_LIBRARY_PATH=\"$WORK_DIR\"/wasm32-wasi-libs\n\ncommand=\"$0 $*\"\necho \"# This file is autogenerated by \\`${command% }\\`\"\necho \"# $(date --rfc-3339 seconds)\"\necho\n\nfor spec in $(find \"spec/std\" -type f -iname \"*_spec.cr\" | LC_ALL=C sort); do\n  require=\"require \\\"./${spec##spec/}\\\"\"\n  target=\"$WORK_DIR/$spec.wasm\"\n  mkdir -p \"$(dirname \"$target\")\"\n\n  if ! output=$(bin/crystal build \"$spec\" -o \"$target\" --target wasm32-wasi 2>&1); then\n    if [[ \"$output\" =~ \"execution of command failed\" ]]; then\n      echo \"# $require (failed linking)\"\n    else\n      echo \"# $require (failed codegen)\"\n    fi\n    continue\n  fi\n\n  wasmtime run \"$target\" > /dev/null; exit=$?\n\n  if [ $exit -eq 0 ]; then\n    echo \"$require\"\n  else\n    echo \"# $require (failed to run)\"\n  fi\ndone\n"
  },
  {
    "path": "spec/llvm-ir/argless-initialize-debug-loc.cr",
    "content": "class Foo(T)\n  # CHECK:      define internal %\"Foo(Int32)\"* @\"*Foo(Int32)@Foo(T)::new:Foo(Int32)\"(i32 %self)\n  # CHECK-SAME: !dbg [[LOC1:![0-9]+]]\n  # CHECK-NEXT: alloca:\n  # CHECK-NEXT: %_ = alloca %\"Foo(Int32)\"*\n  # CHECK-SAME: !dbg [[LOC2:![0-9]+]]\n  # CHECK:      [[LOC2]] = !DILocation\n  # CHECK-SAME: scope: [[LOC1]]\nend\n\nFoo(Int32).new\n"
  },
  {
    "path": "spec/llvm-ir/assign-unions.cr",
    "content": "class Foo\n  @v : Int32 | Bool | UInt8[12]\n\n  def initialize\n    @v = 1 || true\n\n    # X64: [[DEST:%.*]] = bitcast %\"(Bool | Int32 | StaticArray(UInt8, 12))\"* {{.*}} to %\"(Bool | Int32)\"*\n    # X64-NEXT: [[SRC:%.*]] = load %\"(Bool | Int32)\", %\"(Bool | Int32)\"* {{.*}}\n    # X64-NEXT: store %\"(Bool | Int32)\" {{.*}}[[SRC]], %\"(Bool | Int32)\"* {{.*}}[[DEST]]\n  end\nend\n\nFoo.new\n"
  },
  {
    "path": "spec/llvm-ir/cast-unions.cr",
    "content": "(1 || true).as(Int32 | Bool | UInt8[12])\n\n# X64: [[DEST:%.*]] = bitcast %\"(Bool | Int32 | StaticArray(UInt8, 12))\"* {{.*}} to %\"(Bool | Int32)\"*\n# X64-NEXT: [[SRC:%.*]] = load %\"(Bool | Int32)\", %\"(Bool | Int32)\"* {{.*}}\n# X64-NEXT: store %\"(Bool | Int32)\" {{.*}}[[SRC]], %\"(Bool | Int32)\"* {{.*}}[[DEST]]\n"
  },
  {
    "path": "spec/llvm-ir/class-var-read-debug-loc.cr",
    "content": "require \"prelude\"\n\nclass Foo\n  def foo\n    @@x\n    # CHECK:      call %String** @\"~Bar::x:read\"\n    # CHECK-SAME: !dbg [[LOC:![0-9]+]]\n    # CHECK:      [[LOC]] = !DILocation\n    # CHECK-SAME: line: [[# @LINE - 4]]\n    # CHECK-SAME: column: 5\n  end\n\n  @@x = \"\"\nend\n\nclass Bar < Foo\nend\n\nBar.new.foo\n"
  },
  {
    "path": "spec/llvm-ir/const-read-debug-loc.cr",
    "content": "require \"prelude\"\n\nclass Foo\n  def foo\n  end\nend\n\ndef a_foo\n  Foo.new\nend\n\nTHE_FOO.foo\n# CHECK:      call %Foo** @\"~THE_FOO:const_read\"()\n# CHECK-SAME: !dbg [[LOC:![0-9]+]]\n# CHECK:      [[LOC]] = !DILocation\n# CHECK-SAME: line: 12\n# CHECK-SAME: column: 1\n\nTHE_FOO = a_foo\n"
  },
  {
    "path": "spec/llvm-ir/memcpy.cr",
    "content": "lib LibMylib\n  struct Foo\n    x : Int64\n    y : Int64\n  end\nend\n\na = 1\nf = ->(s : LibMylib::Foo) {\n  a\n}\n\ns = LibMylib::Foo.new\nf.call(s)\n\n# X64: ctx_is_null:\n# X64: [[SRC:%.*]] = bitcast { i64, i64 }* {{%[0-9]+}} to i8*\n# X64-NEXT: [[PTR:%.*]] = getelementptr inbounds %\"struct.LibMylib::Foo\", %\"struct.LibMylib::Foo\"* %s, i32 0, i32 0\n# X64-NEXT: [[DEST:%.*]] = bitcast i64* {{.*}}[[PTR]] to i8*\n# X64-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 {{.*}}[[SRC]], i8* align 8 {{.*}}[[DEST]], i64 16, i1 false)\n"
  },
  {
    "path": "spec/llvm-ir/memset.cr",
    "content": "Pointer(UInt8).malloc(10)\n\n# X32: [[PTR:%.*]] = call i8* @malloc(i32 trunc ([[SIZE:.*]]) to i32))\n# X32-NEXT: call void @llvm.memset.p0i8.i32(i8* align 4 {{.*}}[[PTR]], i8 0, i32 trunc ({{.*}}[[SIZE]]) to i32), i1 false)\n\n# X64: [[PTR:%.*]] = call i8* @malloc([[SIZE:.*]])\n# X64-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}[[PTR]], i8 0, {{.*}}[[SIZE]], i1 false)\n"
  },
  {
    "path": "spec/llvm-ir/pass-closure-to-c-debug-loc.cr",
    "content": "lib Foo\n  fun foo(x : ->)\nend\n\ndef raise(msg)\n  while true # ameba:disable Lint/EmptyLoop\n  end\nend\n\nx = 1\nf = -> { x }\nFoo.foo(f)\n\n# CHECK:      define internal i8* @\"~check_proc_is_not_closure\"(%\"->\" %0)\n# CHECK:      ctx_is_not_null:\n# CHECK-NEXT: call void @\"*raise<String>:NoReturn\"\n# CHECK-SAME: !dbg [[LOC1:![0-9]+]]\n# CHECK:      [[LOC1]] = !DILocation\n# CHECK-SAME: line: 0\n"
  },
  {
    "path": "spec/llvm-ir/proc-call-debug-loc.cr",
    "content": "x = -> { }\nx.call\n# CHECK:      extractvalue %\"->\" %{{[0-9]+}}, 0\n# CHECK-SAME: !dbg [[LOC:![0-9]+]]\n# CHECK:      ctx_is_null:\n# CHECK:      call %Nil\n# CHECK-SAME: !dbg [[LOC]]\n# CHECK:      ctx_is_not_null:\n# CHECK:      call %Nil\n# CHECK-SAME: !dbg [[LOC]]\n# CHECK:      [[LOC]] = !DILocation\n# CHECK-SAME: line: 2\n# CHECK-SAME: column: 3\n"
  },
  {
    "path": "spec/llvm-ir/proc-pointer-debug-loc.cr",
    "content": "def foo\nend\n\n(->foo).call\n\n# CHECK:      define internal void @\"~procProc(Nil)\n# CHECK-SAME: !dbg [[LOC1:![0-9]+]]\n# CHECK-NEXT: entry:\n# CHECK-NEXT:   ret void, !dbg [[LOC2:![0-9]+]]\n# CHECK-NEXT: }\n# CHECK:      [[LOC2]] = !DILocation(line: 4, column: 2, scope: [[LOC1]])\n"
  },
  {
    "path": "spec/llvm-ir/test.sh",
    "content": "#!/bin/bash\n\n# TODO: the specs in this folder still expect typed pointers and so will fail\n# on LLVM 15+ which use opaque pointers\n\nset -euo pipefail\n\nSCRIPT_PATH=\"$(realpath \"$0\")\"\nSCRIPT_ROOT=\"$(dirname \"$SCRIPT_PATH\")\"\n\nBUILD_DIR=$SCRIPT_ROOT/../../.build\nLLVM_CONFIG=\"$(basename \"$(\"$SCRIPT_ROOT\"/../../src/llvm/ext/find-llvm-config.sh)\")\"\nFILE_CHECK=FileCheck-\"${LLVM_CONFIG#llvm-config-}\"\ncrystal=${CRYSTAL_SPEC_COMPILER_BIN:-$SCRIPT_ROOT/../../bin/crystal}\n\nmkdir -p \"$BUILD_DIR\"\n\nfunction test() {\n  echo \"test: $*\"\n\n  input_cr=\"$SCRIPT_ROOT/$1\"\n  output_ll=\"$BUILD_DIR/${1%.cr}.ll\"\n  # FIXME: unused variable\n  # compiler_options=\"$2\"\n  check_prefix=\"${3+--check-prefix $3}\"\n\n  # $BUILD_DIR/test-ir is never used\n  # pushd $BUILD_DIR + $output_ll is a workaround due to the fact that we can't control\n  # the filename generated by --emit=llvm-ir\n  \"$crystal\" build --single-module --no-color --emit=llvm-ir \"$2\" -o \"$BUILD_DIR\"/test-ir \"$input_cr\"\n  $FILE_CHECK \"$input_cr\" --input-file \"$output_ll\" \"$check_prefix\"\n\n  rm \"$BUILD_DIR\"/test-ir.o\n  rm \"$output_ll\"\n}\n\npushd \"$BUILD_DIR\" >/dev/null\n\ntest argless-initialize-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\ntest proc-call-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\ntest proc-pointer-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\ntest pass-closure-to-c-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\n\n# #11416\ntest const-read-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\ntest class-var-read-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\ntest virtual-class-var-read-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\ntest virtual-metaclass-var-read-debug-loc.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty\"\n\ntest memset.cr \"--cross-compile --target i386-apple-darwin --prelude=empty --no-debug\" X32\ntest memset.cr \"--cross-compile --target i386-unknown-linux-gnu --prelude=empty --no-debug\" X32\ntest memset.cr \"--cross-compile --target x86_64-apple-darwin --prelude=empty --no-debug\" X64\ntest memset.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty --no-debug\" X64\n\ntest memcpy.cr \"--cross-compile --target x86_64-apple-darwin --prelude=empty --no-debug\" X64\ntest memcpy.cr \"--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty --no-debug\" X64\n\ntest cast-unions.cr \"--cross-compile --target x86_64-apple-darwin --prelude=empty --no-debug\" X64\ntest assign-unions.cr \"--cross-compile --target x86_64-apple-darwin --prelude=empty --no-debug\" X64\n\npopd >/dev/null\n"
  },
  {
    "path": "spec/llvm-ir/virtual-class-var-read-debug-loc.cr",
    "content": "class Foo\n  def foo\n    @@x\n    # CHECK:      call i32* @\"~Foo+::x:read\"\n    # CHECK-SAME: !dbg [[LOC:![0-9]+]]\n    # CHECK:      [[LOC]] = !DILocation\n    # CHECK-SAME: line: [[# @LINE - 4]]\n    # CHECK-SAME: column: 5\n  end\n\n  @@x = 1\nend\n\nclass Bar < Foo\nend\n\n(Foo.new || Bar.new).foo\n"
  },
  {
    "path": "spec/llvm-ir/virtual-metaclass-var-read-debug-loc.cr",
    "content": "class Foo\n  def self.foo\n    @@x\n    # CHECK:      call i32* @\"~Foo+.class::x:read\"\n    # CHECK-SAME: !dbg [[LOC3:![0-9]+]]\n    # CHECK:      [[LOC3]] = !DILocation\n    # CHECK-SAME: line: [[# @LINE - 4]]\n    # CHECK-SAME: column: 5\n  end\n\n  @@x = 1\nend\n\nclass Bar < Foo\nend\n\n(Foo || Bar).foo\n"
  },
  {
    "path": "spec/manual/badssl_spec.cr",
    "content": "require \"spec\"\nrequire \"socket\"\nrequire \"openssl\"\n\nprivate def connect_to(host, context = OpenSSL::SSL::Context::Client.new)\n  io = TCPSocket.new(host, 443)\n  socket = OpenSSL::SSL::Socket::Client.new(io, context: context, hostname: host)\n  socket << \"GET / HTTP/1.1\\r\\nHost: #{host}\\r\\n\\r\\n\"\n  socket.gets\n  true\nensure\n  io.close if io\n  socket.close if socket\nend\n\ndescribe \"OpenSSL::SSL::Context has sane client defaults\" do\n  {\n    \"expired.badssl.com\",\n    \"wrong.host.badssl.com\",\n    \"self-signed.badssl.com\",\n    \"incomplete-chain.badssl.com\",\n    \"superfish.badssl.com\",\n    \"edellroot.badssl.com\",\n    \"dsdtestprovider.badssl.com\",\n    \"subdomain.preloaded-hsts.badssl.com\",\n  }.each do |host|\n    it \"shouldn't connect to #{host}\" do\n      expect_raises(OpenSSL::SSL::Error) do\n        connect_to(host)\n      end\n    end\n\n    it \"should connect to #{host} with verification disabled\" do\n      context = OpenSSL::SSL::Context::Client.new\n      context.verify_mode = OpenSSL::SSL::VerifyMode::NONE\n      connect_to(host, context).should be_true\n    end\n  end\n\n  {\n    \"rc4.badssl.com\",\n    \"10000-sans.badssl.com\",\n    \"dh480.badssl.com\",\n  }.each do |host|\n    it \"shouldn't connect to #{host}\" do\n      expect_raises(OpenSSL::SSL::Error) do\n        connect_to(host)\n      end\n    end\n\n    it \"shouldn't connect to #{host} with verification disabled\" do\n      context = OpenSSL::SSL::Context::Client.new\n      context.verify_mode = OpenSSL::SSL::VerifyMode::NONE\n\n      expect_raises(OpenSSL::SSL::Error) do\n        connect_to(host, context).should be_true\n      end\n    end\n  end\n\n  {\n    \"google.com\",\n    \"sha1-2016.badssl.com\",\n    \"sha1-2017.badssl.com\",\n    \"sha256.badssl.com\",\n    \"1000-sans.badssl.com\",\n    \"rsa8192.badssl.com\",\n    \"mixed-script.badssl.com\",\n    \"very.badssl.com\",\n    \"mixed.badssl.com\",\n    \"mixed-favicon.badssl.com\",\n    \"cbc.badssl.com\",\n    \"mozilla-old.badssl.com\",\n    \"mozilla-intermediate.badssl.com\",\n    \"mozilla-modern.badssl.com\",\n    \"dh1024.badssl.com\",\n    \"dh2048.badssl.com\",\n    \"dh-small-subgroup.badssl.com\",\n    \"dh-composite.badssl.com\",\n    \"hsts.badssl.com\",\n    \"upgrade.badssl.com\",\n    \"preloaded-hsts.badssl.com\",\n  }.each do |host|\n    it \"should connect to #{host} successfully\" do\n      connect_to(host).should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/manual/digest_large_file_spec.cr",
    "content": "require \"spec\"\nrequire \"digest/sha1\"\nrequire \"digest/md5\"\n\nprivate DATA          = \"a\" * 1024\nprivate TOTAL_SIZE_GB = 1\nprivate TOTAL_SIZE    = TOTAL_SIZE_GB * 1024 * 1024 * 1024\n\ndescribe Digest::SHA1 do\n  it \"does digest for large file\" do\n    Digest::SHA1.digest do |ctx|\n      (TOTAL_SIZE / DATA.size).ceil.to_i.times do\n        ctx.update DATA\n      end\n    end\n  end\nend\n\ndescribe Digest::MD5 do\n  it \"does digest for large file\" do\n    Digest::MD5.digest do |ctx|\n      (TOTAL_SIZE / DATA.size).ceil.to_i.times do\n        ctx.update DATA\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/manual/find_executable_spec.cr",
    "content": "# Verifies that find_executable's specs match the behavior of Process.run.\n# This doesn't actually test find_executable, only takes all the test cases\n# directly from spec/std/process/find_executable_spec.cr and checks that\n# *they* match what the OS actually does when finding an executable for the\n# purpose of running it.\n\nrequire \"spec\"\nrequire \"digest/sha1\"\nrequire \"../support/env\"\nrequire \"../support/tempfile\"\nrequire \"../std/process/find_executable_spec\"\n\ndescribe \"Process.run\" do\n  test_dir = Path[SPEC_TEMPFILE_PATH] / \"manual_find_executable\"\n  base_dir = Path[test_dir] / \"base\"\n  path_dir = Path[test_dir] / \"path\"\n\n  around_all do |all|\n    Dir.mkdir_p(test_dir)\n\n    exe_names, non_exe_names = FIND_EXECUTABLE_TEST_FILES\n    exe_names.each do |name|\n      src_fn = test_dir / \"self_printer.cr\"\n      exe_fn = test_dir / \"self_printer.exe\"\n      File.write(src_fn, \"print #{name.inspect}\")\n      Process.run(ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"]? || \"bin/crystal\", [\"build\", \"-o\", exe_fn.to_s, src_fn.to_s])\n      Dir.mkdir_p((base_dir / name).parent)\n      File.rename(exe_fn, base_dir / name)\n    end\n    non_exe_names.each do |name|\n      File.write(base_dir / name, \"\")\n    end\n\n    with_env \"PATH\": {ENV[\"PATH\"], path_dir}.join(Process::PATH_DELIMITER) do\n      Dir.cd(base_dir) do\n        all.run\n      end\n    end\n\n    FileUtils.rm_r(test_dir.to_s)\n  end\n\n  find_executable_test_cases(base_dir).each do |(command, exp)|\n    if exp\n      it \"runs '#{command}' as '#{exp}'\" do\n        output = Process.run command, &.output.gets_to_end\n        $?.success?.should be_true\n        output.should eq exp\n      end\n    else\n      it \"fails to run '#{command}'\" do\n        expect_raises IO::Error do\n          Process.run(command)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/manual/gzip_large_file_spec.cr",
    "content": "require \"compress/gzip\"\nrequire \"spec\"\n\n# This spec tests piping a file with a size of more than\n# UInt32::MAX bytes through GZip::Writer and GZ::Reader.\n# Zipping and unzipping so many bytes takes some time,\n# so this spec is quite slow.\nit \"Gzip file larger than UInt32::MAX\" do\n  read, write = IO.pipe\n  bytes_written = 0_i64\n  bytes_read = 0_i64\n\n  spawn do\n    slice = Slice.new(1024, 0_u8, read_only: true)\n\n    Compress::Gzip::Writer.open(write) do |writer|\n      target_bytes = UInt32::MAX.to_i64 + 1\n      while bytes_written < target_bytes\n        writer.write(slice)\n        bytes_written += slice.bytesize\n      end\n    end\n\n    write.close\n  end\n\n  Compress::Gzip::Reader.open(read) do |reader|\n    slice = Slice.new(1024, 0_u8)\n\n    while true\n      read_bytes = reader.read(slice)\n      break if read_bytes == 0\n      bytes_read += read_bytes\n    end\n\n    read.close\n  end\n\n  bytes_read.should eq bytes_written\n  bytes_read.should be > UInt32::MAX\nend\n"
  },
  {
    "path": "spec/manual/hash_large_spec.cr",
    "content": "require \"spec\"\n\nit \"creates Hash at maximum capacity\" do\n  # we don't try to go as high as Int32::MAX because it would allocate 18GB of\n  # memory in total. This already tests for Int32 overflows while 'only' needing\n  # 4.5GB of memory.\n  Hash(Int32, Int32).new(initial_capacity: (Int32::MAX // 4) + 1)\nend\n"
  },
  {
    "path": "spec/manual/https_client_spec.cr",
    "content": "require \"spec\"\nrequire \"openssl\"\nrequire \"http\"\n\ndescribe \"https requests\" do\n  it \"can fetch from google.com\" do\n    HTTP::Client.get(\"https://google.com\")\n  end\n\n  it \"can fetch from google.com. FQDN with trailing dot (#12777)\" do\n    HTTP::Client.get(\"https://google.com.\")\n  end\n\n  it \"can close request before consuming body\" do\n    HTTP::Client.get(\"https://crystal-lang.org\") do\n      break\n    end\n  end\nend\n"
  },
  {
    "path": "spec/manual/string_normalize_spec.cr",
    "content": "require \"spec\"\nrequire \"http/client\"\nrequire \"spec/helpers/string\"\n\nUCD_ROOT = \"http://www.unicode.org/Public/#{Unicode::VERSION}/ucd/\"\n\nprivate struct CodepointsEqualExpectation\n  @expected_value : Array(Int32)\n\n  def initialize(str : String, @desc : String)\n    @expected_value = str.codepoints\n  end\n\n  def match(actual_value)\n    @expected_value == actual_value.codepoints\n  end\n\n  def failure_message(actual_value)\n    expected = @expected_value.join(\", \") { |x| \"U+%04X\" % x }\n    got = actual_value.codepoints.join(\", \") { |x| \"U+%04X\" % x }\n    \"While testing #{@desc}:\\nexpected: [#{expected}]\\n     got: [#{got}]\"\n  end\n\n  def negative_failure_message(actual_value)\n    expected = @expected_value.join(\", \") { |x| \"U+%04X\" % x }\n    \"While testing #{@desc}:\\nexpected: actual_value.codepoints != [#{expected}]\"\n  end\nend\n\nprivate macro assert_prints_codepoints(call, str, desc, *, file = __FILE__, line = __LINE__)\n  %expectation = CodepointsEqualExpectation.new(({{ str }}).as(String), {{ desc }})\n  assert_prints({{ call }}, should: %expectation, file: {{ file }}, line: {{ line }})\nend\n\nprivate def assert_normalized(source, target, form : Unicode::NormalizationForm, *, file = __FILE__, line = __LINE__)\n  normalized = source.unicode_normalized?(form)\n  equal = (source == target)\n  return if normalized == equal\n\n  got = source.codepoints.join(\", \") { |x| \"U+%04X\" % x }\n  kind = form.to_s.upcase\n  if equal # !normalized\n    fail <<-ERROR, file: file, line: line\n      Expected: is#{kind}(str) == false\n           got: str == to#{kind}(str)\n                str == [#{got}]\"\n      ERROR\n  else # !equal && normalized\n    expected = target.codepoints.join(\", \") { |x| \"U+%04X\" % x }\n    fail <<-ERROR, file: file, line: line\n      Expected: is#{kind}(str) == true\n           got: str != to#{kind}(str)\n                str == [#{got}]\n                to#{kind}(str) == [#{expected}]\"\n      ERROR\n  end\nend\n\nprivate def assert_normalizes(source, nfc_str, nfd_str, nfkc_str, nfkd_str, *, file = __FILE__, line = __LINE__)\n  assert_normalized source, nfc_str, :nfc, file: file, line: line\n  assert_normalized source, nfd_str, :nfd, file: file, line: line\n  assert_normalized source, nfkc_str, :nfkc, file: file, line: line\n  assert_normalized source, nfkd_str, :nfkd, file: file, line: line\n\n  assert_prints_codepoints source.unicode_normalize(:nfc), nfc_str, \"c2 == toNFC(c1)\", file: file, line: line\n  assert_prints_codepoints nfc_str.unicode_normalize(:nfc), nfc_str, \"c2 == toNFC(c2)\", file: file, line: line\n  assert_prints_codepoints nfd_str.unicode_normalize(:nfc), nfc_str, \"c2 == toNFC(c3)\", file: file, line: line\n  assert_prints_codepoints nfkc_str.unicode_normalize(:nfc), nfkc_str, \"c4 == toNFC(c4)\", file: file, line: line\n  assert_prints_codepoints nfkd_str.unicode_normalize(:nfc), nfkc_str, \"c4 == toNFC(c5)\", file: file, line: line\n\n  assert_prints_codepoints source.unicode_normalize(:nfd), nfd_str, \"c3 == toNFD(c1)\", file: file, line: line\n  assert_prints_codepoints nfc_str.unicode_normalize(:nfd), nfd_str, \"c3 == toNFD(c2)\", file: file, line: line\n  assert_prints_codepoints nfd_str.unicode_normalize(:nfd), nfd_str, \"c3 == toNFD(c3)\", file: file, line: line\n  assert_prints_codepoints nfkc_str.unicode_normalize(:nfd), nfkd_str, \"c5 == toNFD(c4)\", file: file, line: line\n  assert_prints_codepoints nfkd_str.unicode_normalize(:nfd), nfkd_str, \"c5 == toNFD(c5)\", file: file, line: line\n\n  assert_prints_codepoints source.unicode_normalize(:nfkc), nfkc_str, \"c4 == toNFKC(c1)\", file: file, line: line\n  assert_prints_codepoints nfc_str.unicode_normalize(:nfkc), nfkc_str, \"c4 == toNFKC(c2)\", file: file, line: line\n  assert_prints_codepoints nfd_str.unicode_normalize(:nfkc), nfkc_str, \"c4 == toNFKC(c3)\", file: file, line: line\n  assert_prints_codepoints nfkc_str.unicode_normalize(:nfkc), nfkc_str, \"c4 == toNFKC(c4)\", file: file, line: line\n  assert_prints_codepoints nfkd_str.unicode_normalize(:nfkc), nfkc_str, \"c4 == toNFKC(c5)\", file: file, line: line\n\n  assert_prints_codepoints source.unicode_normalize(:nfkd), nfkd_str, \"c5 == toNFKD(c1)\", file: file, line: line\n  assert_prints_codepoints nfc_str.unicode_normalize(:nfkd), nfkd_str, \"c5 == toNFKD(c2)\", file: file, line: line\n  assert_prints_codepoints nfd_str.unicode_normalize(:nfkd), nfkd_str, \"c5 == toNFKD(c3)\", file: file, line: line\n  assert_prints_codepoints nfkc_str.unicode_normalize(:nfkd), nfkd_str, \"c5 == toNFKD(c4)\", file: file, line: line\n  assert_prints_codepoints nfkd_str.unicode_normalize(:nfkd), nfkd_str, \"c5 == toNFKD(c5)\", file: file, line: line\nend\n\ndescribe String do\n  describe \"#unicode_normalize\" do\n    context \"official test cases\" do\n      url = \"#{UCD_ROOT}NormalizationTest.txt\"\n      body = HTTP::Client.get(url).body\n      body.each_line do |line|\n        line = line.strip\n        next if line.empty?\n        next if line.starts_with?('#') || line.starts_with?('@')\n\n        it line do\n          pieces = line.split(';', limit: 6)\n          (0..4).each do |i|\n            pieces[i] = pieces[i].split(' ').join &.to_i(16).chr\n          end\n          assert_normalizes pieces[0], pieces[1], pieces[2], pieces[3], pieces[4]\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/manual/string_to_f32_spec.cr",
    "content": "require \"spec\"\n\n# Exhaustively checks that for all 4294967296 possible `Float32` values,\n# `to_s.to_f32` returns the original number. Splits the floats into 4096 bins\n# for better progress tracking. Also useful as a sort of benchmark.\n#\n# This was originally added when `String#to_f` moved from `LibC.strtod` to\n# `fast_float`, but is applicable to any other implementation as well.\ndescribe \"x.to_s.to_f32 == x\" do\n  (0_u32..0xFFF_u32).each do |i|\n    it \"%03x00000..%03xfffff\" % {i, i} do\n      0x100000.times do |j|\n        bits = i << 20 | j\n        float = bits.unsafe_as(Float32)\n        str = float.to_s\n        val = str.to_f32?.should_not be_nil\n\n        if float.nan?\n          val.nan?.should be_true\n        else\n          val.should eq(float)\n          val.sign_bit.should eq(float.sign_bit)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/manual/string_to_f_supplemental_spec.cr",
    "content": "# Runs the fast_float supplemental test suite:\n# https://github.com/fastfloat/supplemental_test_files\n#\n#   Supplemental data files for testing floating parsing (credit: Nigel Tao for\n#   the data)\n#\n#   LICENSE file (Apache 2): https://github.com/nigeltao/parse-number-fxx-test-data/blob/main/LICENSE\n#\n# Due to the sheer volume of the test cases (5.2+ million test cases across\n# 270+ MB of text) these specs are not vendored into the Crystal repository.\n\nrequire \"spec\"\nrequire \"http/client\"\nrequire \"../support/number\"\nrequire \"wait_group\"\n\n# these specs permit underflow and overflow to return 0 and infinity\n# respectively (when `ret.rc == Errno::ERANGE`), so we have to use\n# `Float::FastFloat` directly\ndef fast_float_to_f32(str)\n  value = uninitialized Float32\n  start = str.to_unsafe\n  finish = start + str.bytesize\n  options = Float::FastFloat::ParseOptionsT(typeof(str.to_unsafe.value)).new(format: :general)\n\n  ret = Float::FastFloat::BinaryFormat_Float32.new.from_chars_advanced(start, finish, pointerof(value), options)\n  {Errno::NONE, Errno::ERANGE}.should contain(ret.ec)\n  value\nend\n\ndef fast_float_to_f64(str)\n  value = uninitialized Float64\n  start = str.to_unsafe\n  finish = start + str.bytesize\n  options = Float::FastFloat::ParseOptionsT(typeof(str.to_unsafe.value)).new(format: :general)\n\n  ret = Float::FastFloat::BinaryFormat_Float64.new.from_chars_advanced(start, finish, pointerof(value), options)\n  {Errno::NONE, Errno::ERANGE}.should contain(ret.ec)\n  value\nend\n\nRAW_BASE_URL = \"https://raw.githubusercontent.com/fastfloat/supplemental_test_files/7cc512a7c60361ebe1baf54991d7905efdc62aa0/data/\" # @1.0.0\n\nTEST_SUITES = %w(\n  freetype-2-7.txt\n  google-double-conversion.txt\n  google-wuffs.txt\n  ibm-fpgen.txt\n  lemire-fast-double-parser.txt\n  lemire-fast-float.txt\n  more-test-cases.txt\n  remyoudompheng-fptest-0.txt\n  remyoudompheng-fptest-1.txt\n  remyoudompheng-fptest-2.txt\n  remyoudompheng-fptest-3.txt\n  tencent-rapidjson.txt\n  ulfjack-ryu.txt\n)\n\ntest_suite_cache = {} of String => Array({UInt32, UInt64, String})\nputs \"Fetching #{TEST_SUITES.size} test suites\"\nWaitGroup.wait do |wg|\n  TEST_SUITES.each do |suite|\n    wg.spawn do\n      url = RAW_BASE_URL + suite\n\n      cache = HTTP::Client.get(url) do |res|\n        res.body_io.each_line.map do |line|\n          args = line.split(' ')\n          raise \"BUG: should have 4 args\" unless args.size == 4\n\n          # f16_bits = args[0].to_u16(16)\n          f32_bits = args[1].to_u32(16)\n          f64_bits = args[2].to_u64(16)\n          str = args[3]\n\n          {f32_bits, f64_bits, str}\n        end.to_a\n      end\n\n      puts \"#{cache.size} test cases cached from #{url}\"\n      test_suite_cache[suite] = cache\n    end\n  end\nend\nputs \"There are a total of #{test_suite_cache.sum(&.last.size)} test cases\"\n\ndescribe String do\n  describe \"#to_f\" do\n    test_suite_cache.each do |suite, cache|\n      describe suite do\n        each_hardware_rounding_mode do |mode, mode_name|\n          it mode_name do\n            cache.each do |f32_bits, f64_bits, str|\n              fast_float_to_f32(str).unsafe_as(UInt32).should eq(f32_bits)\n              fast_float_to_f64(str).unsafe_as(UInt64).should eq(f64_bits)\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/external_command_spec.cr",
    "content": "{% skip_file if flag?(:interpreted) %}\n\nrequire \"../support/tempfile\"\n\ndescribe \"Crystal::Command\" do\n  it \"exec external commands\", tags: %w[slow external_commands] do\n    with_temp_executable \"crystal-external\" do |command_path|\n      compiler_path = File.expand_path(ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"]? || \"bin/crystal\")\n\n      with_tempfile \"crystal-external.cr\" do |source_file|\n        File.write source_file, <<-CRYSTAL\n          puts Process.find_executable(\"crystal\")\n          puts ENV[\"CRYSTAL_EXEC_PATH\"]?\n          puts PROGRAM_NAME\n          puts ARGV\n          CRYSTAL\n\n        Process.run(compiler_path, [\"build\", source_file, \"-o\", command_path], error: :inherit)\n      end\n\n      File.exists?(command_path).should be_true\n\n      process = Process.new(compiler_path,\n        [\"external\", \"foo\", \"bar\"],\n        output: :pipe, error: :pipe,\n        env: {\"PATH\" => {ENV[\"PATH\"], File.dirname(command_path)}.join(Process::PATH_DELIMITER)}\n      )\n\n      output = process.output.gets_to_end\n      error = process.error.gets_to_end\n      status = process.wait\n      status.success?.should be_true, failure_message: \"Running external subcommand failed.\\nstderr:\\n#{error}\\nstdout:\\n#{output}\"\n\n      output.lines.should eq [\n        compiler_path,\n        File.dirname(compiler_path),\n        command_path,\n        %([\"foo\", \"bar\"]),\n      ]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/float_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/number\"\nrequire \"../support/interpreted\"\n\ndescribe \"Primitives: Float\" do\n  {% for op in %w(== != < <= > >=) %}\n    {% unequal = (op == \"!=\") %}\n    describe {{ \"##{op.id}\" }} do\n      {% for float in BUILTIN_FLOAT_TYPES %}\n        {% for float2 in BUILTIN_FLOAT_TYPES %}\n          it {{ \"returns #{unequal} for #{float}::NAN #{op.id} #{float2}::NAN\" }} do\n            ({{ float }}::NAN {{ op.id }} {{ float2 }}::NAN).should eq({{ unequal }})\n          end\n        {% end %}\n\n        {% for num in BUILTIN_NUMBER_TYPES %}\n          it {{ \"returns #{unequal} for #{float}::NAN #{op.id} #{num}.zero\" }} do\n            ({{ float }}::NAN {{ op.id }} {{ num }}.zero).should eq({{ unequal }})\n            ({{ num }}.zero {{ op.id }} {{ float }}::NAN).should eq({{ unequal }})\n          end\n        {% end %}\n      {% end %}\n    end\n  {% end %}\n\n  describe \"#to_i\" do\n    {% for float in BUILTIN_FLOAT_TYPES %}\n      {% for method, int in BUILTIN_INT_CONVERSIONS %}\n        it {{ \"raises on overflow for #{float}##{method}\" }} do\n          if {{ float }}::MAX > {{ int }}::MAX\n            expect_raises(OverflowError) do\n              {{ float }}.new!({{ int }}::MAX).next_float.{{ method }}\n            end\n          end\n\n          expect_raises(OverflowError) do\n            {{ float }}::INFINITY.{{ method }}\n          end\n\n          if {{ int }}::MIN.zero? # unsigned\n            expect_raises(OverflowError) do\n              {{ float }}.zero.prev_float.{{ method }}\n            end\n          end\n\n          expect_raises(OverflowError) do\n            (-{{ float }}::INFINITY).{{ method }}\n          end\n        end\n\n        it \"raises overflow if not a number (#10421)\" do\n          expect_raises(OverflowError) do\n            {{ float }}::NAN.{{ method }}\n          end\n        end\n      {% end %}\n    {% end %}\n\n    it \"raises overflow if equal to Int::MAX (#11105)\" do\n      # these examples hold because the integer would be rounded _up_ to the\n      # nearest representable float\n\n      expect_raises(OverflowError) { Float32.new!(Int32::MAX).to_i32 }\n      expect_raises(OverflowError) { Float32.new!(UInt32::MAX).to_u32 }\n      expect_raises(OverflowError) { Float32.new!(Int64::MAX).to_i64 }\n      expect_raises(OverflowError) { Float32.new!(UInt64::MAX).to_u64 }\n      expect_raises(OverflowError) { Float32.new!(Int128::MAX).to_i128 }\n\n      expect_raises(OverflowError) { Float64.new!(Int64::MAX).to_i64 }\n      expect_raises(OverflowError) { Float64.new!(UInt64::MAX).to_u64 }\n      expect_raises(OverflowError) { Float64.new!(Int128::MAX).to_i128 }\n      expect_raises(OverflowError) { Float64.new!(UInt128::MAX).to_u128 }\n    end\n\n    it \"doesn't raise overflow if lower than Int::MAX (#11105)\" do\n      Float32.new!(Int32::MAX).prev_float.to_i32\n      Float32.new!(UInt32::MAX).prev_float.to_u32\n      Float32.new!(Int64::MAX).prev_float.to_i64\n      Float32.new!(UInt64::MAX).prev_float.to_u64\n      Float32.new!(Int128::MAX).prev_float.to_i128\n      Float32::MAX.to_u128\n\n      Float64.new!(Int64::MAX).prev_float.to_i64\n      Float64.new!(UInt64::MAX).prev_float.to_u64\n      Float64.new!(Int128::MAX).prev_float.to_i128\n      Float64.new!(UInt128::MAX).prev_float.to_u128\n    end\n  end\n\n  describe \"#to_f\" do\n    it \"raises on overflow for Float64#to_f32\" do\n      expect_raises(OverflowError) { Float64::MAX.to_f32 }\n      expect_raises(OverflowError) { Float32::MAX.to_f64!.next_float.to_f32 }\n      expect_raises(OverflowError) { Float32::MIN.to_f64!.prev_float.to_f32 }\n      expect_raises(OverflowError) { Float64::MIN.to_f32 }\n    end\n\n    it \"doesn't raise for infinity\" do\n      x = Float64::INFINITY.to_f32\n      x.should be_a(Float32)\n      x.should eq(Float32::INFINITY)\n\n      x = (-Float64::INFINITY).to_f32\n      x.should be_a(Float32)\n      x.should eq(-Float32::INFINITY)\n    end\n\n    it \"doesn't raise for NaN\" do\n      x = Float64::NAN.to_f32\n      x.should be_a(Float32)\n      x.nan?.should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/int_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/number\"\nrequire \"big\"\n\n{% for i in Int::Signed.union_types %}\n  struct {{i}}\n    TEST_CASES = [MIN, MIN &+ 1, MIN &+ 2, -2, -1, 0, 1, 2, MAX &- 2, MAX &- 1, MAX] of {{i}}\n  end\n{% end %}\n\n{% for i in Int::Unsigned.union_types %}\n  struct {{i}}\n    TEST_CASES = [MIN, MIN &+ 1, MIN &+ 2, MAX // 2 &- 1, MAX // 2, MAX // 2 &+ 1, MAX &- 2, MAX &- 1, MAX] of {{i}}\n  end\n{% end %}\n\nmacro run_op_tests(t, u, op)\n  it \"overflow test #{{{t}}} #{{{op}}} #{{{u}}}\" do\n    {{t}}::TEST_CASES.each do |lhs|\n      {{u}}::TEST_CASES.each do |rhs|\n        result = lhs.to_big_i {{op.id}} rhs.to_big_i\n        passes = {{t}}::MIN <= result <= {{t}}::MAX\n        begin\n          if passes\n            (lhs {{op.id}} rhs).should eq(lhs &{{op.id}} rhs)\n          else\n            expect_raises(OverflowError) { lhs {{op.id}} rhs }\n          end\n        rescue e : Spec::AssertionFailed\n          raise Spec::AssertionFailed.new(\"#{e.message}: #{lhs} #{{{op}}} #{rhs}\", e.file, e.line)\n        rescue e : OverflowError\n          raise OverflowError.new(\"#{e.message}: #{lhs} #{{{op}}} #{rhs}\")\n        end\n      end\n    end\n  end\nend\n\ndescribe \"Primitives: Int\" do\n  describe \"#&+\" do\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it \"wraps around for {{ int }}\" do\n        ({{ int }}::MAX &+ {{ int }}.new(1)).should eq({{ int }}::MIN)\n        ({{ int }}::MAX &+ 1_i64).should eq({{ int }}::MIN)\n      end\n    {% end %}\n  end\n\n  describe \"#&-\" do\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it \"wraps around for {{ int }}\" do\n        ({{ int }}::MIN &- {{ int }}.new(1)).should eq({{ int }}::MAX)\n        ({{ int }}::MIN &- 1_i64).should eq({{ int }}::MAX)\n      end\n    {% end %}\n  end\n\n  describe \"#&*\" do\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it \"wraps around for {{ int }}\" do\n        %val{int} = {{ int }}::MAX // {{ int }}.new(2) &+ {{ int }}.new(1)\n        (%val{int} &* {{ int }}.new(2)).should eq({{ int }}::MIN)\n        (%val{int} &* 2_i64).should eq({{ int }}::MIN)\n      end\n    {% end %}\n  end\n\n  describe \"#+\" do\n    {% for int1 in BUILTIN_INTEGER_TYPES %}\n      {% for int2 in BUILTIN_INTEGER_TYPES %}\n        run_op_tests {{ int1 }}, {{ int2 }}, :+\n      {% end %}\n    {% end %}\n  end\n\n  describe \"#-\" do\n    {% for int1 in BUILTIN_INTEGER_TYPES %}\n      {% for int2 in BUILTIN_INTEGER_TYPES %}\n        run_op_tests {{ int1 }}, {{ int2 }}, :-\n      {% end %}\n    {% end %}\n  end\n\n  describe \"#*\" do\n    {% for int1 in BUILTIN_INTEGER_TYPES %}\n      {% for int2 in BUILTIN_INTEGER_TYPES %}\n        run_op_tests {{ int1 }}, {{ int2 }}, :*\n      {% end %}\n    {% end %}\n  end\n\n  describe \"#to_i\" do\n    {% for int1 in BUILTIN_INTEGER_TYPES %}\n      {% for method, int2 in BUILTIN_INT_CONVERSIONS %}\n        {% if int1 != int2 %}\n          it {{ \"raises on overflow for #{int1}##{method}\" }} do\n            if {{ int1 }}::MAX > {{ int2 }}::MAX\n              expect_raises(OverflowError) do\n                ({{ int1 }}.new!({{ int2 }}::MAX) &+ 1).{{ method }}\n              end\n            end\n\n            if {{ int1 }}::MIN < {{ int2 }}::MIN\n              expect_raises(OverflowError) do\n                ({{ int1 }}.new!({{ int2 }}::MIN) &- 1).{{ method }}\n              end\n            end\n          end\n        {% end %}\n      {% end %}\n    {% end %}\n  end\n\n  describe \"#to_i!\" do\n    it \"works from negative values to unsigned types\" do\n      x = (-1).to_u!\n      x.should be_a(UInt32)\n      x.should eq(4294967295_u32)\n    end\n\n    it \"works from greater values to smaller types\" do\n      x = 47866.to_i8!\n      x.should be_a(Int8)\n      x.should eq(-6_i8)\n    end\n\n    it \"preserves negative sign\" do\n      x = (-1_i8).to_i!\n      x.should be_a(Int32)\n      x.should eq(-1_i32)\n    end\n  end\n\n  describe \"#unsafe_chr\" do\n    it \"doesn't raise on overflow\" do\n      (0x41_i64 - 0x100000000_i64).unsafe_chr.should eq('A')\n      0xFFFF_FFFF_0010_FFFF_u64.unsafe_chr.should eq('\\u{10FFFF}')\n    end\n  end\n\n  describe \"#to_f\" do\n    it \"raises on overflow for UInt128#to_f32\" do\n      expect_raises(OverflowError) { UInt128::MAX.to_f32 }\n      expect_raises(OverflowError) { Float32::MAX.to_u128.succ.to_f32 } # Float32::MAX == 2 ** 128 - 2 ** 104\n    end\n  end\n\n  describe \"#to_f!\" do\n    it \"doesn't raise on overflow for UInt128#to_f32\" do\n      UInt128::MAX.to_f32!.should eq(Float32::INFINITY)\n      Float32::MAX.to_u128.succ.to_f32!.should eq(Float32::MAX)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/pointer_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/finalize\"\nrequire \"../support/interpreted\"\n\nprivate class Inner\n  include FinalizeCounter\n\n  def initialize(@key : String)\n  end\nend\n\nprivate class Outer\n  @inner = Inner.new(\"reference-storage\")\nend\n\ndescribe \"Primitives: pointer\" do\n  describe \".malloc\" do\n    pending_interpreted \"is non-atomic for ReferenceStorage(T) if T is non-atomic (#14692)\" do\n      FinalizeState.reset\n      Outer.unsafe_construct(Pointer(ReferenceStorage(Outer)).malloc(1))\n      GC.collect\n      FinalizeState.count(\"reference-storage\").should eq(0)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/reference_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/interpreted\"\n\nprivate abstract class Base\nend\n\nprivate class Foo < Base\n  getter i : Int64\n  getter str = \"abc\"\n\n  def initialize(@i)\n  end\n\n  def initialize(@str, @i)\n  end\nend\n\nprivate class Bar < Base\n  getter x : UInt8[128]\n\n  def initialize(@x)\n  end\nend\n\nprivate struct Inner\nend\n\nprivate class Outer\n  @x = Inner.new\nend\n\ndescribe \"Primitives: reference\" do\n  describe \".allocate\" do\n    it \"doesn't fail on complex ivar initializer if value is discarded (#14325)\" do\n      Outer.allocate\n      1\n    end\n  end\n\n  describe \".pre_initialize\" do\n    it \"doesn't fail on complex ivar initializer if value is discarded (#14325)\" do\n      bar_buffer = GC.malloc(instance_sizeof(Outer))\n      Outer.pre_initialize(bar_buffer)\n      1\n    end\n\n    it \"zeroes the instance data\" do\n      bar_buffer = GC.malloc(instance_sizeof(Bar))\n      Slice.new(bar_buffer.as(UInt8*), instance_sizeof(Bar)).fill(0xFF)\n      bar = Bar.pre_initialize(bar_buffer)\n      bar.x.all?(&.zero?).should be_true\n    end\n\n    it \"sets type ID\" do\n      foo_buffer = GC.malloc(instance_sizeof(Foo))\n      base = Foo.pre_initialize(foo_buffer).as(Base)\n      base.should be_a(Foo)\n      base.as(typeof(Foo.crystal_instance_type_id)*).value.should eq(Foo.crystal_instance_type_id)\n      {% unless flag?(:interpreted) %}\n        # FIXME: `Object#crystal_type_id` is incorrect for virtual types in the interpreter (#14967)\n        base.crystal_type_id.should eq(Foo.crystal_instance_type_id)\n      {% end %}\n    end\n\n    it \"runs inline instance initializers\" do\n      foo_buffer = GC.malloc(instance_sizeof(Foo))\n      foo = Foo.pre_initialize(foo_buffer)\n      foo.str.should eq(\"abc\")\n    end\n\n    it \"works when address is on the stack\" do\n      foo_buffer = uninitialized ReferenceStorage(Foo)\n      foo = Foo.pre_initialize(pointerof(foo_buffer))\n      pointerof(foo_buffer).as(typeof(Foo.crystal_instance_type_id)*).value.should eq(Foo.crystal_instance_type_id)\n      foo.str.should eq(\"abc\")\n    end\n\n    # see notes in `Reference.pre_initialize`\n    {% if compare_versions(Crystal::VERSION, \"1.2.0\") >= 0 %}\n      it \"works with virtual type\" do\n        foo_buffer = GC.malloc(instance_sizeof(Foo))\n        foo = Foo.as(Base.class).pre_initialize(foo_buffer).should be_a(Foo)\n        foo.str.should eq(\"abc\")\n      end\n    {% else %}\n      pending! \"works with virtual type\"\n    {% end %}\n\n    it \"raises on abstract virtual type\" do\n      expect_raises(Exception, \"Can't pre-initialize abstract class Base\") do\n        Base.as(Base.class).pre_initialize(Pointer(Void).null)\n      end\n    end\n  end\n\n  describe \".unsafe_construct\" do\n    it \"constructs an object in-place\" do\n      foo_buffer = GC.malloc(instance_sizeof(Foo))\n      foo = Foo.unsafe_construct(foo_buffer, 123_i64)\n      foo.i.should eq(123)\n      foo.str.should eq(\"abc\")\n\n      str = String.build &.<< \"def\"\n      foo = Foo.unsafe_construct(foo_buffer, str, 789_i64)\n      foo.i.should eq(789)\n      foo.str.should be(str)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/slice_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/number\"\nrequire \"../support/interpreted\"\n\nprivate module Foo\n  def self.foo\n    Slice.literal(1)\n  end\nend\n\ndescribe \"Primitives: Slice\" do\n  describe \".literal\" do\n    {% for num in BUILTIN_NUMBER_TYPES %}\n      it {{ \"creates a read-only Slice(#{num})\" }} do\n        slice = Slice({{ num }}).literal(0, 1, 4, 9, 16, 25)\n        slice.should be_a(Slice({{ num }}))\n        slice.to_a.should eq([0, 1, 4, 9, 16, 25] of {{ num }})\n        slice.read_only?.should be_true\n      end\n\n      # TODO: these should probably return the same pointers\n      it \"creates multiple literals\" do\n        slice1 = Slice({{ num }}).literal(1, 2, 3)\n        slice2 = Slice({{ num }}).literal(1, 2, 3)\n        slice1.should eq(slice2)\n      end\n    {% end %}\n\n    {% for num, suffix in BUILTIN_NUMBER_SUFFIXES %}\n      pending_interpreted {{ \"creates a read-only Slice of #{num}\" }} do\n        slice = Slice.literal(1_{{ suffix.id }}, 2_{{ suffix.id }}, 3_{{ suffix.id }})\n        slice.should be_a(Slice({{ num }}))\n        slice.to_a.should eq([1, 2, 3] of {{ num }})\n        slice.read_only?.should be_true\n      end\n    {% end %}\n\n    it \"links against slice literal from a different LLVM module\" do\n      Foo.foo.should eq(Slice.literal(1))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives/struct_spec.cr",
    "content": "require \"spec\"\n\nprivate struct Foo\n  getter i : Int64\n  getter str = \"abc\"\n\n  def initialize(@i)\n  end\n\n  def initialize(@str, @i)\n  end\nend\n\nprivate struct Bar\n  getter x : UInt8[128]\n\n  def initialize(@x)\n  end\nend\n\nprivate struct Inner\nend\n\nprivate struct Outer\n  @x = Inner.new\nend\n\ndescribe \"Primitives: struct\" do\n  describe \".pre_initialize\" do\n    it \"doesn't fail on complex ivar initializer if value is discarded (#14325)\" do\n      bar = uninitialized Outer\n      Outer.pre_initialize(pointerof(bar))\n      1\n    end\n\n    it \"zeroes the instance data\" do\n      bar = uninitialized Bar\n      Slice.new(pointerof(bar).as(UInt8*), sizeof(Bar)).fill(0xFF)\n      Bar.pre_initialize(pointerof(bar))\n      bar.x.all?(&.zero?).should be_true\n    end\n\n    it \"runs inline instance initializers\" do\n      foo = uninitialized Foo\n      Foo.pre_initialize(pointerof(foo)).should be_nil\n      foo.str.should eq(\"abc\")\n    end\n\n    it \"works when address is on the heap\" do\n      foo_buffer = Pointer(Foo).malloc(1)\n      Foo.pre_initialize(foo_buffer)\n      foo_buffer.value.str.should eq(\"abc\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/primitives_spec.cr",
    "content": "require \"./primitives/**\"\n"
  },
  {
    "path": "spec/spec_helper.cr",
    "content": "{% raise(\"Please use `make test` or `bin/crystal` when running specs, or set the i_know_what_im_doing flag if you know what you're doing\") unless env(\"CRYSTAL_HAS_WRAPPER\") || flag?(\"i_know_what_im_doing\") %}\n\nCrystal::Config.path = \"#{__DIR__}/../src\"\n\nrequire \"spec\"\n\nrequire \"compiler/requires\"\nrequire \"./support/syntax\"\nrequire \"./support/tempfile\"\nrequire \"./support/win32\"\nrequire \"./support/wasm32\"\n\nclass Crystal::Program\n  def reset_temp_vars\n    @temp_vars.clear\n  end\n\n  def union_of(type1, type2, type3)\n    union_of([type1, type2, type3] of Type).not_nil!\n  end\n\n  def proc_of(type1 : Type)\n    proc_of([type1] of Type)\n  end\n\n  def proc_of(type1 : Type, type2 : Type)\n    proc_of([type1, type2] of Type)\n  end\n\n  def generic_class(name, *type_vars)\n    types[name].as(GenericClassType).instantiate(type_vars.to_a.map &.as(TypeVar))\n  end\n\n  def generic_module(name, *type_vars)\n    types[name].as(GenericModuleType).instantiate(type_vars.to_a.map &.as(TypeVar))\n  end\nend\n\nrecord SemanticResult,\n  program : Program,\n  node : ASTNode\n\ndef assert_type(str, *, inject_primitives = false, flags = nil, file = __FILE__, line = __LINE__, &)\n  result = semantic(str, flags: flags, inject_primitives: inject_primitives)\n  program = result.program\n  expected_type = with program yield program\n  node = result.node\n  if node.is_a?(Expressions)\n    node = node.last\n  end\n  node.type.should eq(expected_type), file: file, line: line\n  result\nend\n\ndef semantic(code : String, wants_doc = false, inject_primitives = false, flags = nil, filename = nil)\n  warnings = WarningCollection.new\n  node = parse(code, wants_doc: wants_doc, filename: filename, warnings: warnings)\n  node = inject_primitives(node) if inject_primitives\n  semantic node, warnings: warnings, wants_doc: wants_doc, flags: flags\nend\n\nprivate def inject_primitives(node : ASTNode)\n  req = Crystal::Require.new(\"primitives\")\n  case node\n  when Crystal::Expressions\n    node.expressions.unshift req\n    node\n  when Crystal::Nop\n    node\n  else\n    Crystal::Expressions.new [req, node] of ASTNode\n  end\nend\n\ndef semantic(node : ASTNode, *, warnings = nil, wants_doc = false, flags = nil)\n  program = new_program\n  program.warnings = warnings if warnings\n  program.flags.concat(flags.split) if flags\n  program.wants_doc = wants_doc\n  node = program.normalize node\n  node = program.semantic node\n  SemanticResult.new(program, node)\nend\n\ndef top_level_semantic(code : String, wants_doc = false, inject_primitives = false)\n  node = parse(code, wants_doc: wants_doc)\n  node = inject_primitives(node) if inject_primitives\n  top_level_semantic node, wants_doc: wants_doc\nend\n\ndef top_level_semantic(node : ASTNode, wants_doc = false)\n  program = new_program\n  program.wants_doc = wants_doc\n  node = program.normalize node\n  node, _ = program.top_level_semantic node\n  SemanticResult.new(program, node)\nend\n\ndef assert_normalize(from, to, flags = nil, *, filename = nil, file = __FILE__, line = __LINE__)\n  program = new_program\n  program.flags.concat(flags.split) if flags\n  from_nodes = parse(from, filename: filename)\n  to_nodes = program.normalize(from_nodes)\n  to_nodes_str = to_nodes.to_s.strip\n  to_nodes_str.should eq(to.strip), file: file, line: line\n\n  # first idempotency check: the result should be fully normalized\n  to_nodes_str2 = program.normalize(to_nodes).to_s.strip\n  unless to_nodes_str2 == to_nodes_str\n    fail \"Idempotency failed:\\nBefore: #{to_nodes_str.inspect}\\nAfter:  #{to_nodes_str2.inspect}\", file: file, line: line\n  end\n\n  # second idempotency check: if the normalizer mutates the original node,\n  # further normalizations should not produce a different result\n  program.reset_temp_vars\n  to_nodes_str2 = program.normalize(from_nodes).to_s.strip\n  unless to_nodes_str2 == to_nodes_str\n    fail \"Idempotency failed:\\nBefore: #{to_nodes_str.inspect}\\nAfter:  #{to_nodes_str2.inspect}\", file: file, line: line\n  end\n\n  to_nodes\nend\n\ndef assert_expand(from : String, to, *, flags = nil, file = __FILE__, line = __LINE__)\n  node = Parser.parse(from)\n  if node.is_a?(Expressions)\n    node = node.last\n  end\n  assert_expand node, to, flags: flags, file: file, line: line\nend\n\ndef assert_expand(from_nodes : ASTNode, to, *, flags = nil, file = __FILE__, line = __LINE__)\n  assert_expand(from_nodes, flags: flags, file: file, line: line) do |to_nodes|\n    to_nodes.to_s.strip.should eq(to.strip), file: file, line: line\n  end\nend\n\ndef assert_expand(from_nodes : ASTNode, *, flags = nil, file = __FILE__, line = __LINE__, &)\n  program = new_program\n  program.flags.concat(flags.split) if flags\n  to_nodes = LiteralExpander.new(program).expand(from_nodes)\n  yield to_nodes, program\nend\n\ndef assert_expand_named(from : String, to, *, generic = nil, flags = nil, filename = nil, file = __FILE__, line = __LINE__)\n  program = new_program\n  program.flags.concat(flags.split) if flags\n  from_nodes = parse(from, filename: filename)\n  generic_type = generic.path if generic\n  case from_nodes\n  when ArrayLiteral\n    to_nodes = LiteralExpander.new(program).expand_named(from_nodes, generic_type)\n  when HashLiteral\n    to_nodes = LiteralExpander.new(program).expand_named(from_nodes, generic_type)\n  else\n    fail \"Expected: ArrayLiteral | HashLiteral, got: #{from_nodes.class}\", file: file, line: line\n  end\n  to_nodes.to_s.strip.should eq(to.strip), file: file, line: line\nend\n\ndef assert_error(str, message = nil, *, inject_primitives = false, flags = nil, file = __FILE__, line = __LINE__)\n  expect_raises TypeException, message, file, line do\n    semantic str, inject_primitives: inject_primitives, flags: flags\n  end\nend\n\ndef assert_no_errors(*args, **opts)\n  semantic(*args, **opts)\nend\n\ndef warnings_result(code)\n  semantic(code).program.warnings.infos\nend\n\ndef assert_warning(code, message, *, file = __FILE__, line = __LINE__)\n  warning_failures = warnings_result(code)\n  warning_failures.size.should eq(1), file: file, line: line\n  warning_failures[0].should contain(message), file: file, line: line\nend\n\ndef assert_no_warning(code, *, file = __FILE__, line = __LINE__)\n  warning_failures = warnings_result(code)\n  warning_failures.should be_empty, file: file, line: line\nend\n\ndef assert_macro(macro_body, expected, args = nil, *, expected_pragmas = nil, flags = nil, file = __FILE__, line = __LINE__)\n  assert_macro(macro_body, expected, expected_pragmas: expected_pragmas, flags: flags, file: file, line: line) { args }\nend\n\ndef assert_macro(macro_body, expected, *, expected_pragmas = nil, flags = nil, file = __FILE__, line = __LINE__, &)\n  program, a_macro, call = prepare_macro_call(macro_body, flags) { |program| yield program }\n  result, result_pragmas = program.expand_macro(a_macro, call, program, program)\n  result = result.chomp(';')\n  result.should eq(expected), file: file, line: line\n  result_pragmas.should eq(expected_pragmas), file: file, line: line if expected_pragmas\nend\n\ndef assert_macro_error(macro_body, message = nil, args = nil, *, flags = nil, file = __FILE__, line = __LINE__)\n  assert_macro_error(macro_body, message, flags: flags, file: file, line: line) { args }\nend\n\ndef assert_macro_error(macro_body, message = nil, *, flags = nil, file = __FILE__, line = __LINE__, &)\n  program, a_macro, call = prepare_macro_call(macro_body, flags) { |program| yield program }\n  expect_raises(Crystal::TypeException, message, file: file, line: line) do\n    program.expand_macro(a_macro, call, program, program)\n  end\nend\n\ndef prepare_macro_call(macro_body, flags = nil, &)\n  program = new_program\n  flags = flags.split if flags.is_a?(String)\n  program.flags.concat(flags) if flags\n  program.top_level_semantic_complete = true\n  args = yield program\n\n  macro_params = args.try &.keys.join(\", \")\n  call_args = [] of ASTNode\n  call_args.concat(args.values) if args\n\n  a_macro = Parser.parse(\"macro foo(#{macro_params});#{macro_body};end\").as(Macro)\n  call = Call.new(\"\", call_args)\n\n  {program, a_macro, call}\nend\n\ndef codegen(code, *, inject_primitives = true, single_module = false, debug = Crystal::Debug::None, filename = __FILE__)\n  result = semantic code, inject_primitives: inject_primitives, filename: filename\n  result.program.codegen(result.node, single_module: single_module, debug: debug)[\"\"].mod\nend\n\ndef compile(*codes, prelude = \"empty\", debug = Crystal::Debug::None, target = nil)\n  sources = codes.map_with_index do |code, index|\n    Compiler::Source.new(\"file#{index}.cr\", code)\n  end.to_a\n\n  compiler = create_spec_compiler\n  compiler.prelude = prelude\n  compiler.debug = debug\n  compiler.stdout = IO::Memory.new # don't print anything\n\n  if target\n    compiler.cross_compile = true # skip linker\n    compiler.codegen_target = Crystal::Codegen::Target.new(target)\n  end\n\n  with_temp_executable(\"crystal-spec-output\") do |output_filename|\n    compiler.compile(sources, output_filename)\n  end\nend\n\nprivate def new_program\n  program = Program.new\n  program.color = false\n  apply_program_flags(program.flags)\n  program\nend\n\n# Use CRYSTAL_SPEC_COMPILER_FLAGS env var to run the compiler specs\n# against a compiler with the specified options.\n# Separate flags with a space.\n# Using CRYSTAL_SPEC_COMPILER_FLAGS=\"foo bar\" will mimic -Dfoo -Dbar options.\nprivate def apply_program_flags(target)\n  ENV[\"CRYSTAL_SPEC_COMPILER_FLAGS\"]?.try { |f| target.concat(f.split) }\nend\n\nprivate def spec_compiler_threads\n  ENV[\"CRYSTAL_SPEC_COMPILER_THREADS\"]?.try(&.to_i)\nend\n\nprivate def encode_program_flags : String\n  program_flags_options.join(' ')\nend\n\ndef program_flags_options : Array(String)\n  f = [] of String\n  apply_program_flags(f)\n  options = f.map { |x| \"-D#{x}\" }\n  if (n_threads = spec_compiler_threads)\n    options << \"--threads=#{n_threads}\"\n  end\n  options\nend\n\nclass Crystal::SpecRunOutput\n  @output : String\n\n  def initialize(@output)\n  end\n\n  def to_string\n    @output\n  end\n\n  delegate to_i, to_i64, to_u64, to_f, to_f32, to_f64, to: @output\n\n  def to_b\n    @output == \"true\"\n  end\nend\n\ndef create_spec_compiler\n  compiler = Compiler.new\n  if (n_threads = spec_compiler_threads)\n    compiler.n_threads = n_threads\n  end\n\n  compiler\nend\n\ndef run(code, filename : String? = nil, inject_primitives = true, debug = Crystal::Debug::None, flags = nil, *, file = __FILE__) : LLVM::GenericValue | SpecRunOutput\n  if inject_primitives\n    code = %(require \"primitives\"\\n#{code})\n  end\n\n  # Code that requires the prelude doesn't run in LLVM's MCJIT\n  # because of missing linked functions (which are available\n  # in the current executable!), so instead we compile\n  # the program and run it, printing the last\n  # expression and using that to compare the result.\n  if code.includes?(%(require \"prelude\"))\n    ast = Parser.parse(code).as(Expressions)\n    last = ast.expressions.last\n    assign = Assign.new(Var.new(\"__tempvar\"), last)\n    call = Call.new(\"print\", Var.new(\"__tempvar\"))\n    exps = Expressions.new([assign, call] of ASTNode)\n    ast.expressions[-1] = exps\n    code = ast.to_s\n\n    compiler = create_spec_compiler\n    compiler.debug = debug\n    compiler.flags.concat flags if flags\n    apply_program_flags(compiler.flags)\n\n    with_temp_executable(\"crystal-spec-output\", file: file) do |output_filename|\n      compiler.compile Compiler::Source.new(\"spec\", code), output_filename\n\n      output = `#{Process.quote(output_filename)}`\n      return SpecRunOutput.new(output)\n    end\n  else\n    program = new_program\n    program.flags.concat(flags) if flags\n    program.run(code, filename: filename, debug: debug)\n  end\nend\n\ndef run(code, return_type : T.class, filename : String? = nil, inject_primitives = true, debug = Crystal::Debug::None, flags = nil, *, file = __FILE__) forall T\n  if inject_primitives\n    code = %(require \"primitives\"\\n#{code})\n  end\n\n  if code.includes?(%(require \"prelude\"))\n    fail \"TODO: support the prelude in typed codegen specs\", file: file\n  else\n    program = new_program\n    program.flags.concat(flags) if flags\n    program.run(code, return_type: T, filename: filename, debug: debug)\n  end\nend\n\ndef test_c(c_code, crystal_code, *, file = __FILE__, &)\n  with_temp_c_object_file(c_code, file: file) do |o_filename|\n    yield run(<<-CRYSTAL)\n      require \"prelude\"\n\n      @[Link(ldflags: #{o_filename.inspect})]\n      #{crystal_code}\n      CRYSTAL\n  end\nend\n"
  },
  {
    "path": "spec/std/array_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\nprivate alias RecursiveArray = Array(RecursiveArray)\n\nprivate class Spaceship\n  getter value : Float64\n\n  def initialize(@value : Float64, @return_nil = false)\n  end\n\n  def <=>(other : Spaceship)\n    return nil if @return_nil\n\n    value <=> other.value\n  end\nend\n\nprivate def is_stable_sort(*, mutable, &block)\n  n = 42\n  # [Spaceship.new(0), ..., Spaceship.new(n - 1), Spaceship.new(0), ..., Spaceship.new(n - 1)]\n  arr = Array.new(n * 2) { |i| Spaceship.new((i % n).to_f) }\n  # [Spaceship.new(0), Spaceship.new(0), ..., Spaceship.new(n - 1), Spaceship.new(n - 1)]\n  expected = Array.new(n * 2) { |i| arr[i % 2 * n + i // 2] }\n\n  if mutable\n    yield arr\n    result = arr\n  else\n    result = yield arr\n    result.should_not eq(arr)\n  end\n\n  result.size.should eq(expected.size)\n  expected.zip(result) do |exp, res|\n    res.should be(exp) # reference-equality is necessary to check sorting is stable.\n  end\nend\n\ndescribe \"Array\" do\n  describe \"new\" do\n    it \"creates with default value\" do\n      ary = Array.new(5, 3)\n      ary.should eq([3, 3, 3, 3, 3])\n    end\n\n    it \"creates with default value in block\" do\n      ary = Array.new(5) { |i| i * 2 }\n      ary.should eq([0, 2, 4, 6, 8])\n\n      ary = Array.new(5_u32) { |i| i * 2 }\n      ary.should eq([0, 2, 4, 6, 8])\n    end\n\n    it \"raises on negative count\" do\n      expect_raises(ArgumentError, \"Negative array size\") do\n        Array.new(-1, 3)\n      end\n    end\n\n    it \"raises on negative capacity\" do\n      expect_raises(ArgumentError, \"Negative array size\") do\n        Array(Int32).new(-1)\n      end\n    end\n  end\n\n  describe \"==\" do\n    it \"compares empty\" do\n      ([] of Int32).should eq([] of Int32)\n      [1].should_not eq([] of Int32)\n      ([] of Int32).should_not eq([1])\n    end\n\n    it \"compares elements\" do\n      [1, 2, 3].should eq([1, 2, 3])\n      [1, 2, 3].should_not eq([3, 2, 1])\n    end\n\n    it \"compares other\" do\n      a = [1, 2, 3]\n      b = [1, 2, 3]\n      c = [1, 2, 3, 4]\n      d = [1, 2, 4]\n      (a == b).should be_true\n      (b == c).should be_false\n      (a == d).should be_false\n    end\n  end\n\n  describe \"&\" do\n    it \"small arrays\" do\n      ([1, 2, 3] & [] of Int32).should eq([] of Int32)\n      ([] of Int32 & [1, 2, 3]).should eq([] of Int32)\n      ([1, 2, 3] & [3, 2, 4]).should eq([2, 3])\n      ([1, 2, 3, 1, 2, 3] & [3, 2, 4, 3, 2, 4]).should eq([2, 3])\n      ([1, 2, 3, 1, 2, 3, nil, nil] & [3, 2, 4, 3, 2, 4, nil]).should eq([2, 3, nil])\n    end\n\n    it \"big arrays\" do\n      a1 = (1..64).to_a\n      a2 = (33..96).to_a\n      (a1 & a2).should eq((33..64).to_a)\n    end\n  end\n\n  describe \"|\" do\n    it \"small arrays\" do\n      ([1, 2, 3, 2, 3] | ([] of Int32)).should eq([1, 2, 3])\n      (([] of Int32) | [1, 2, 3, 2, 3]).should eq([1, 2, 3])\n      ([1, 2, 3] | [5, 3, 2, 4]).should eq([1, 2, 3, 5, 4])\n      ([1, 1, 2, 3, 3] | [4, 5, 5, 6]).should eq([1, 2, 3, 4, 5, 6])\n    end\n\n    it \"large arrays\" do\n      a = [1, 2, 3] * 10\n      b = [4, 5, 6] * 10\n      (a | b).should eq([1, 2, 3, 4, 5, 6])\n    end\n\n    it \"different types\" do\n      ([1, 2, 3] | [\"hello\"]).should eq([1, 2, 3, \"hello\"])\n    end\n  end\n\n  it \"does +\" do\n    a = [1, 2, 3]\n    b = [4, 5]\n    c = a + b\n    c.size.should eq(5)\n    0.upto(4) { |i| c[i].should eq(i + 1) }\n  end\n\n  it \"does + with empty tuple converted to array (#909)\" do\n    ([1, 2] + Tuple.new.to_a).should eq([1, 2])\n    (Tuple.new.to_a + [1, 2]).should eq([1, 2])\n  end\n\n  describe \"-\" do\n    it \"does it\" do\n      ([1, 2, 3, 4, 5] - [4, 2]).should eq([1, 3, 5])\n    end\n\n    it \"does with larger array coming second\" do\n      ([4, 2] - [1, 2, 3]).should eq([4])\n    end\n\n    it \"does with even larger arrays\" do\n      ((1..64).to_a - (1..32).to_a).should eq((33..64).to_a)\n    end\n\n    context \"with different types\" do\n      it \"small array\" do\n        ([1, 2, 3, 'c'] - [2, nil]).should eq [1, 3, 'c']\n      end\n\n      it \"big array\" do\n        (((1..64).to_a + ['c']) - ((2..63).to_a + [nil])).should eq [1, 64, 'c']\n      end\n    end\n  end\n\n  it \"does *\" do\n    (([] of Int32) * 10).should be_empty\n    ([1, 2, 3] * 0).should be_empty\n    ([1] * 3).should eq([1, 1, 1])\n    ([1, 2, 3] * 3).should eq([1, 2, 3, 1, 2, 3, 1, 2, 3])\n    ([1, 2] * 10).should eq([1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2])\n  end\n\n  describe \"[]\" do\n    it \"gets on positive index\" do\n      [1, 2, 3][1].should eq(2)\n    end\n\n    it \"gets on negative index\" do\n      [1, 2, 3][-1].should eq(3)\n    end\n\n    it \"gets on inclusive range\" do\n      [1, 2, 3, 4, 5, 6][1..4].should eq([2, 3, 4, 5])\n    end\n\n    it \"gets on inclusive range with negative indices\" do\n      [1, 2, 3, 4, 5, 6][-5..-2].should eq([2, 3, 4, 5])\n    end\n\n    it \"gets on exclusive range\" do\n      [1, 2, 3, 4, 5, 6][1...4].should eq([2, 3, 4])\n    end\n\n    it \"gets on exclusive range with negative indices\" do\n      [1, 2, 3, 4, 5, 6][-5...-2].should eq([2, 3, 4])\n    end\n\n    it \"gets on range with start higher than end\" do\n      [1, 2, 3][2..1].should eq([] of Int32)\n      [1, 2, 3][3..1].should eq([] of Int32)\n      expect_raises IndexError do\n        [1, 2, 3][4..1]\n      end\n    end\n\n    it \"gets on range with start higher than negative end\" do\n      [1, 2, 3][1..-1].should eq([2, 3] of Int32)\n      [1, 2, 3][2..-2].should eq([] of Int32)\n    end\n\n    it \"gets on range without end\" do\n      [1, 2, 3][1..nil].should eq([2, 3])\n    end\n\n    it \"gets on range without begin\" do\n      [1, 2, 3][nil..1].should eq([1, 2])\n    end\n\n    it \"raises on index out of bounds with range\" do\n      expect_raises IndexError do\n        [1, 2, 3][4..6]\n      end\n    end\n\n    it \"raises on index out of bounds with range without end\" do\n      expect_raises IndexError do\n        [1, 2, 3][4..nil]\n      end\n    end\n\n    it \"gets with start and count\" do\n      [1, 2, 3, 4, 5, 6][1, 3].should eq([2, 3, 4])\n    end\n\n    it \"gets with start and count exceeding size\" do\n      [1, 2, 3][1, 3].should eq([2, 3])\n    end\n\n    it \"gets with negative start\" do\n      [1, 2, 3, 4, 5, 6][-4, 2].should eq([3, 4])\n    end\n\n    it \"raises on index out of bounds with start and count\" do\n      expect_raises IndexError do\n        [1, 2, 3][4, 0]\n      end\n    end\n\n    it \"raises on negative count\" do\n      expect_raises ArgumentError do\n        [1, 2, 3][3, -1]\n      end\n    end\n\n    it \"raises on index out of bounds\" do\n      expect_raises IndexError do\n        [1, 2, 3][-4, 2]\n      end\n    end\n\n    it \"raises on negative count\" do\n      expect_raises ArgumentError, /Negative count: -1/ do\n        [1, 2, 3][1, -1]\n      end\n    end\n\n    it \"raises on negative count on empty Array\" do\n      expect_raises ArgumentError, /Negative count: -1/ do\n        Array(Int32).new[0, -1]\n      end\n    end\n\n    it \"gets 0, 0 on empty array\" do\n      a = [] of Int32\n      a[0, 0].should eq(a)\n    end\n\n    it \"gets 0 ... 0 on empty array\" do\n      a = [] of Int32\n      a[0..0].should eq(a)\n    end\n\n    it \"doesn't exceed limits\" do\n      [1][0..3].should eq([1])\n    end\n\n    it \"returns empty if at end\" do\n      [1][1, 0].should eq([] of Int32)\n      [1][1, 10].should eq([] of Int32)\n    end\n\n    it \"raises on too negative left bound\" do\n      expect_raises IndexError do\n        [1, 2, 3][-4..0]\n      end\n    end\n  end\n\n  describe \"[]?\" do\n    it \"gets with index\" do\n      [1, 2, 3][2]?.should eq(3)\n      [1, 2, 3][3]?.should be_nil\n    end\n\n    it \"gets with range\" do\n      [1, 2, 3][1..2]?.should eq([2, 3])\n      [1, 2, 3][4..-1]?.should be_nil\n    end\n\n    it \"gets with start and count\" do\n      [1, 2, 3][1, 3]?.should eq([2, 3])\n      [1, 2, 3][4, 0]?.should be_nil\n    end\n\n    it \"gets with range without end\" do\n      [1, 2, 3][1..nil]?.should eq([2, 3])\n      [1, 2, 3][4..nil]?.should be_nil\n    end\n\n    it \"gets with range without beginning\" do\n      [1, 2, 3][nil..1]?.should eq([1, 2])\n    end\n  end\n\n  describe \"[]=\" do\n    it \"sets on positive index\" do\n      a = [1, 2, 3]\n      a[1] = 4\n      a[1].should eq(4)\n    end\n\n    it \"sets on negative index\" do\n      a = [1, 2, 3]\n      a[-1] = 4\n      a[2].should eq(4)\n    end\n\n    it \"replaces a subrange with a single value\" do\n      a = [1, 2, 3, 4, 5]\n      a[1, 3] = 6\n      a.should eq([1, 6, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1, 1] = 6\n      a.should eq([1, 6, 3, 4, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1, 0] = 6\n      a.should eq([1, 6, 2, 3, 4, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1, 10] = 6\n      a.should eq([1, 6])\n\n      a = [1, 2, 3, 4, 5]\n      a[-3, 2] = 6\n      a.should eq([1, 2, 6, 5])\n\n      a = [1, 2, 3, 4, 5, 6, 7, 8]\n      a[1, 3] = 6\n      a.should eq([1, 6, 5, 6, 7, 8])\n\n      expect_raises ArgumentError, \"Negative count\" do\n        [1, 2, 3][0, -1]\n      end\n\n      a = [1, 2, 3, 4, 5]\n      a[1..3] = 6\n      a.should eq([1, 6, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[2..3] = 6\n      a.should eq([1, 2, 6, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1...1] = 6\n      a.should eq([1, 6, 2, 3, 4, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[2..nil] = 6\n      a.should eq([1, 2, 6])\n\n      a = [1, 2, 3, 4, 5]\n      a[nil..2] = 6\n      a.should eq([6, 4, 5])\n    end\n\n    it \"replaces a subrange with an array\" do\n      a = [1, 2, 3, 4, 5]\n      a[1, 3] = [6, 7, 8]\n      a.should eq([1, 6, 7, 8, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1, 3] = [6, 7]\n      a.should eq([1, 6, 7, 5])\n\n      a = [1, 2, 3, 4, 5, 6, 7, 8]\n      a[1, 3] = [6, 7]\n      a.should eq([1, 6, 7, 5, 6, 7, 8])\n\n      a = [1, 2, 3, 4, 5]\n      a[1, 3] = [6, 7, 8, 9, 10]\n      a.should eq([1, 6, 7, 8, 9, 10, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1, 2] = [6, 7, 8, 9, 10]\n      a.should eq([1, 6, 7, 8, 9, 10, 4, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[1..3] = [6, 7, 8]\n      a.should eq([1, 6, 7, 8, 5])\n\n      a = [1, 2, 3, 4, 5]\n      a[2..nil] = [6, 7]\n      a.should eq([1, 2, 6, 7])\n\n      a = [1, 2, 3, 4, 5]\n      a[nil..2] = [6, 7]\n      a.should eq([6, 7, 4, 5])\n    end\n\n    it \"optimizes when index is 0\" do\n      a = [1, 2, 3, 4, 5, 6, 7, 8]\n      buffer = a.@buffer\n      a[0..2] = 10\n      a.should eq([10, 4, 5, 6, 7, 8])\n      a.@offset_to_buffer.should eq(2)\n      a.@buffer.should eq(buffer + 2)\n    end\n\n    it \"replaces entire range with a value for empty array (#8341)\" do\n      a = [] of Int32\n      a[..] = 6\n      a.should eq([6])\n    end\n\n    it \"pushes a new value with []=(...)\" do\n      a = [1, 2, 3]\n      a[3..] = 4\n      a.should eq([1, 2, 3, 4])\n    end\n\n    it \"replaces entire range with an array for empty array (#8341)\" do\n      a = [] of Int32\n      a[..] = [1, 2, 3]\n      a.should eq([1, 2, 3])\n    end\n\n    it \"concats a new array with []=(...)\" do\n      a = [1, 2, 3]\n      a[3..] = [4, 5, 6]\n      a.should eq([1, 2, 3, 4, 5, 6])\n    end\n\n    it \"reuses the buffer if possible\" do\n      a = [1, 2, 3, 4, 5]\n      a.pop\n      a[4, 0] = [6]\n      a.should eq([1, 2, 3, 4, 6])\n      a.@capacity.should eq(5)\n      a.@offset_to_buffer.should eq(0)\n    end\n\n    it \"resizes the buffer if capacity is not enough\" do\n      a = [1, 2, 3, 4, 5]\n      a.shift\n      a[4, 0] = [6, 7, 8, 9]\n      a.should eq([2, 3, 4, 5, 6, 7, 8, 9])\n      a.@capacity.should eq(10)\n      a.@offset_to_buffer.should eq(1)\n    end\n  end\n\n  describe \"values_at\" do\n    it \"returns the given indexes\" do\n      [\"a\", \"b\", \"c\", \"d\"].values_at(1, 0, 2).should eq({\"b\", \"a\", \"c\"})\n    end\n\n    it \"raises when passed an invalid index\" do\n      expect_raises IndexError do\n        [\"a\"].values_at(10)\n      end\n    end\n\n    it \"works with mixed types\" do\n      [1, \"a\", 1.0, 'a'].values_at(0, 1, 2, 3).should eq({1, \"a\", 1.0, 'a'})\n    end\n  end\n\n  it \"find the element by using binary search\" do\n    [2, 5, 7, 10].bsearch { |x| x >= 4 }.should eq 5\n    [2, 5, 7, 10].bsearch { |x| x > 10 }.should be_nil\n    [2, 5, 7, 10].bsearch { |x| x >= 4 ? 1 : nil }.should eq 5\n  end\n\n  it \"find the index by using binary search\" do\n    [2, 5, 7, 10].bsearch_index { |x, i| x >= 4 }.should eq 1\n    [2, 5, 7, 10].bsearch_index { |x, i| x > 10 }.should be_nil\n\n    [2, 5, 7, 10].bsearch_index { |x, i| i >= 3 }.should eq 3\n    [2, 5, 7, 10].bsearch_index { |x, i| i > 3 }.should be_nil\n  end\n\n  it \"does clear\" do\n    a = [1, 2, 3]\n    a.clear\n    a.should eq([] of Int32)\n  end\n\n  it \"does clone\" do\n    x = {1 => 2}\n    a = [x]\n    b = a.clone\n    b.should eq(a)\n    a.should_not be(b)\n    a[0].should_not be(b[0])\n  end\n\n  it \"does clone with recursive array\" do\n    ary = [] of RecursiveArray\n    ary << ary\n    clone = ary.clone\n    clone[0].should be(clone)\n  end\n\n  it \"does compact\" do\n    a = [1, nil, 2, nil, 3]\n    b = a.compact.should eq([1, 2, 3])\n    a.should eq([1, nil, 2, nil, 3])\n  end\n\n  it \"does compact!\" do\n    a = [1, nil, 2, nil, 3]\n    b = a.compact!\n    b.should eq([1, 2, 3])\n    b.should be(a)\n  end\n\n  describe \"concat\" do\n    it \"concats small arrays\" do\n      a = [1, 2, 3]\n      a.concat([4, 5, 6])\n      a.should eq([1, 2, 3, 4, 5, 6])\n    end\n\n    it \"concats large arrays\" do\n      a = [1, 2, 3]\n      a.concat((4..1000).to_a)\n      a.should eq((1..1000).to_a)\n    end\n\n    it \"concats enumerable\" do\n      a = [1, 2, 3]\n      a.concat((4..1000))\n      a.should eq((1..1000).to_a)\n    end\n\n    it \"concats enumerable to empty array (#2047)\" do\n      a = [] of Int32\n      a.concat(1..1)\n      a.@capacity.should eq(3)\n\n      a = [] of Int32\n      a.concat(1..4)\n      a.@capacity.should eq(6)\n    end\n\n    it \"concats indexable\" do\n      a = [1, 2, 3]\n      a.concat(Slice.new(97) { |i| i + 4 })\n      a.should eq((1..100).to_a)\n\n      a = [1, 2, 3]\n      a.concat(StaticArray(Int32, 97).new { |i| i + 4 })\n      a.should eq((1..100).to_a)\n\n      a = [1, 2, 3]\n      a.concat(Deque.new(97) { |i| i + 4 })\n      a.should eq((1..100).to_a)\n    end\n\n    it \"concats a union of arrays\" do\n      a = [1, '2']\n      a.concat([3] || ['4'])\n      a.should eq([1, '2', 3])\n    end\n  end\n\n  describe \"delete\" do\n    it \"deletes many\" do\n      a = [1, 2, 3, 1, 2, 3]\n      a.delete(2).should eq(2)\n      a.should eq([1, 3, 1, 3])\n    end\n\n    it \"delete not found\" do\n      a = [1, 2]\n      a.delete(4).should be_nil\n      a.should eq([1, 2])\n    end\n  end\n\n  describe \"delete_at\" do\n    it \"deletes positive index\" do\n      a = [1, 2, 3, 4]\n      a.delete_at(1).should eq(2)\n      a.should eq([1, 3, 4])\n    end\n\n    it \"deletes at beginning is same as shift\" do\n      a = [1, 2, 3, 4]\n      buffer = a.@buffer\n      a.delete_at(0)\n      a.should eq([2, 3, 4])\n      a.@offset_to_buffer.should eq(1)\n      a.@buffer.should eq(buffer + 1)\n    end\n\n    it \"deletes use range\" do\n      a = [1, 2, 3]\n      a.delete_at(1).should eq(2)\n      a.should eq([1, 3])\n\n      a = [1, 2, 3]\n      a.delete_at(-1).should eq(3)\n      a.should eq([1, 2])\n\n      a = [1, 2, 3]\n      a.delete_at(-2..-1).should eq([2, 3])\n      a.should eq([1])\n\n      a = [1, 2, 3]\n      a.delete_at(1, 2).should eq([2, 3])\n      a.should eq([1])\n\n      a = [1, 2, 3]\n      a.delete_at(1..5).should eq([2, 3])\n      a.should eq([1])\n      a.size.should eq(1)\n\n      a = [1, 2, 3, 4, 5]\n      a.delete_at(1..2)\n      a.should eq([1, 4, 5])\n\n      a = [1, 2, 3, 4, 5, 6, 7]\n      a.delete_at(1..2)\n      a.should eq([1, 4, 5, 6, 7])\n\n      a = [1, 2, 3, 4, 5, 6, 7]\n      a.delete_at(3..nil)\n      a.should eq([1, 2, 3])\n    end\n\n    it \"deletes with index and count\" do\n      a = [1, 2, 3, 4, 5]\n      a.delete_at(1, 2)\n      a.should eq([1, 4, 5])\n\n      a = [1, 2, 3, 4, 5, 6, 7]\n      a.delete_at(1, 2)\n      a.should eq([1, 4, 5, 6, 7])\n    end\n\n    it \"returns empty if at end\" do\n      a = [1]\n      a.delete_at(1, 0).should eq([] of Int32)\n      a.delete_at(1, 10).should eq([] of Int32)\n      a.delete_at(1..0).should eq([] of Int32)\n      a.delete_at(1..10).should eq([] of Int32)\n      a.should eq([1])\n    end\n\n    it \"deletes negative index\" do\n      a = [1, 2, 3, 4]\n      a.delete_at(-3).should eq(2)\n      a.should eq([1, 3, 4])\n    end\n\n    it \"deletes negative index with range\" do\n      a = [1, 2, 3, 4, 5, 6]\n      a.delete_at(-3, 2).should eq([4, 5])\n      a.should eq([1, 2, 3, 6])\n    end\n\n    it \"deletes negative index with range, out of bounds\" do\n      a = [1, 2, 3, 4, 5, 6]\n\n      expect_raises IndexError do\n        a.delete_at(-7, 2)\n      end\n    end\n\n    it \"deletes out of bounds\" do\n      expect_raises IndexError do\n        [1].delete_at(2)\n      end\n      expect_raises IndexError do\n        [1].delete_at(2, 1)\n      end\n      expect_raises IndexError do\n        [1].delete_at(2..3)\n      end\n      expect_raises IndexError do\n        [1].delete_at(-2..-1)\n      end\n    end\n  end\n\n  it \"does dup\" do\n    x = {1 => 2}\n    a = [x]\n    b = a.dup\n    b.should eq([x])\n    a.should_not be(b)\n    a[0].should be(b[0])\n    b << {3 => 4}\n    a.should eq([x])\n  end\n\n  it \"does each_index\" do\n    a = [1, 1, 1]\n    b = 0\n    a.each_index { |i| b += i }.should be_nil\n    b.should eq(3)\n  end\n\n  describe \"empty\" do\n    it \"is empty\" do\n      ([] of Int32).empty?.should be_true\n    end\n\n    it \"is not empty\" do\n      [1].empty?.should be_false\n    end\n  end\n\n  it \"does equals? with custom block\" do\n    a = [1, 3, 2]\n    b = [3, 9, 4]\n    c = [5, 7, 3]\n    d = [1, 3, 2, 4]\n    f = ->(x : Int32, y : Int32) { (x % 2) == (y % 2) }\n    a.equals?(b, &f).should be_true\n    a.equals?(c, &f).should be_false\n    a.equals?(d, &f).should be_false\n  end\n\n  describe \"#fill\" do\n    it \"replaces values in a subrange\" do\n      a = [0, 1, 2, 3, 4]\n      a.fill(7).should be(a)\n      a.should eq([7, 7, 7, 7, 7])\n\n      a = [0, 1, 2, 3, 4]\n      a.fill(7, 1, 2).should be(a)\n      a.should eq([0, 7, 7, 3, 4])\n\n      a = [0, 1, 2, 3, 4]\n      a.fill(7, 2..3).should be(a)\n      a.should eq([0, 1, 7, 7, 4])\n\n      a = [0, 0, 0, 0, 0]\n      a.fill { |i| i + 7 }.should be(a)\n      a.should eq([7, 8, 9, 10, 11])\n\n      a = [0, 0, 0, 0, 0]\n      a.fill(offset: 2) { |i| i * i }.should be(a)\n      a.should eq([4, 9, 16, 25, 36])\n\n      a = [0, 0, 0, 0, 0]\n      a.fill(1, 2) { |i| i + 7 }.should be(a)\n      a.should eq([0, 8, 9, 0, 0])\n\n      a = [0, 0, 0, 0, 0]\n      a.fill(2..3) { |i| i + 7 }.should be(a)\n      a.should eq([0, 0, 9, 10, 0])\n    end\n  end\n\n  describe \"first\" do\n    it \"gets first when non empty\" do\n      a = [1, 2, 3]\n      a.first.should eq(1)\n    end\n\n    it \"raises when empty\" do\n      expect_raises Enumerable::EmptyError do\n        ([] of Int32).first\n      end\n    end\n\n    it \"returns a sub array with given number of elements\" do\n      arr = [1, 2, 3]\n      arr.first(0).should eq([] of Int32)\n      arr.first(1).should eq [1]\n      arr.first(2).should eq [1, 2]\n      arr.first(3).should eq [1, 2, 3]\n      arr.first(4).should eq [1, 2, 3]\n    end\n  end\n\n  describe \"first?\" do\n    it \"gets first? when non empty\" do\n      a = [1, 2, 3]\n      a.first?.should eq(1)\n    end\n\n    it \"gives nil when empty\" do\n      ([] of Int32).first?.should be_nil\n    end\n  end\n\n  it \"does hash\" do\n    a = [1, 2, [3]]\n    b = [1, 2, [3]]\n    a.hash.should eq(b.hash)\n  end\n\n  describe \"index\" do\n    it \"performs without a block\" do\n      a = [1, 2, 3]\n      a.index(3).should eq(2)\n      a.index(4).should be_nil\n    end\n\n    it \"performs without a block and offset\" do\n      a = [1, 2, 3, 1, 2, 3]\n      a.index(3, offset: 3).should eq(5)\n      a.index(3, offset: -3).should eq(5)\n    end\n\n    it \"performs with a block\" do\n      a = [1, 2, 3]\n      a.index { |i| i > 1 }.should eq(1)\n      a.index { |i| i > 3 }.should be_nil\n    end\n\n    it \"performs with a block and offset\" do\n      a = [1, 2, 3, 1, 2, 3]\n      a.index(offset: 3) { |i| i > 1 }.should eq(4)\n      a.index(offset: -3) { |i| i > 1 }.should eq(4)\n    end\n\n    it \"raises if out of bounds\" do\n      expect_raises IndexError do\n        [1, 2, 3][4]\n      end\n    end\n  end\n\n  describe \"insert\" do\n    it \"inserts with positive index\" do\n      a = [1, 3, 4]\n      expected = [1, 2, 3, 4]\n      a.insert(1, 2).should eq(expected)\n      a.should eq(expected)\n    end\n\n    it \"inserts with negative index\" do\n      a = [1, 2, 3]\n      expected = [1, 2, 3, 4]\n      a.insert(-1, 4).should eq(expected)\n      a.should eq(expected)\n    end\n\n    it \"inserts with negative index (2)\" do\n      a = [1, 2, 3]\n      expected = [4, 1, 2, 3]\n      a.insert(-4, 4).should eq(expected)\n      a.should eq(expected)\n    end\n\n    it \"inserts out of range\" do\n      a = [1, 3, 4]\n\n      expect_raises IndexError do\n        a.insert(4, 1)\n      end\n    end\n  end\n\n  describe \"insert_all\" do\n    it \"inserts with index 0\" do\n      a = [2, 3]\n      a.insert_all(0, [0, 1]).should be a\n      a.should eq([0, 1, 2, 3])\n    end\n\n    it \"inserts with positive index\" do\n      a = [1, 2, 5, 6]\n      a.insert_all(2, [3, 4]).should be a\n      a.should eq([1, 2, 3, 4, 5, 6])\n    end\n\n    it \"inserts with index of #size\" do\n      a = [1, 2, 3]\n      a.insert_all(a.size, [4, 5]).should be a\n      a.should eq([1, 2, 3, 4, 5])\n    end\n\n    it \"inserts with negative index\" do\n      a = [1, 2, 3]\n      a.insert_all(-1, [4, 5]).should be a\n      a.should eq([1, 2, 3, 4, 5])\n    end\n\n    it \"inserts with negative index (2)\" do\n      a = [1, 2, 5, 6]\n      a.insert_all(-3, [3, 4]).should be a\n      a.should eq([1, 2, 3, 4, 5, 6])\n    end\n\n    it \"inserts when empty\" do\n      a = [] of Int32\n      a.insert_all(0, [1, 2, 3]).should be a\n      a.should eq([1, 2, 3])\n    end\n\n    it \"inserts when other is empty\" do\n      a = [1, 2, 3]\n      a.insert_all(1, [] of Int32).should be a\n      a.should eq([1, 2, 3])\n    end\n\n    it \"raises with index greater than size\" do\n      a = [1, 2, 3]\n      expect_raises IndexError do\n        a.insert_all(4, [4, 5])\n      end\n    end\n\n    it \"raises with negative index greater than size\" do\n      a = [1, 2, 3]\n      expect_raises IndexError do\n        a.insert_all(-5, [4, 5])\n      end\n    end\n\n    it \"inserts indexable\" do\n      a = [1, 9, 10]\n      a.insert_all(1, Slice.new(3, 8)).should be a\n      a.should eq([1, 8, 8, 8, 9, 10])\n\n      a.insert_all(-6, StaticArray(Int32, 3).new { |i| i + 2 }).should be a\n      a.should eq([1, 2, 3, 4, 8, 8, 8, 9, 10])\n\n      a.insert_all(4, Deque{5, 6, 7}).should be a\n      a.should eq([1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 10])\n    end\n  end\n\n  describe \"inspect\" do\n    it { [1, 2, 3].inspect.should eq(\"[1, 2, 3]\") }\n  end\n\n  describe \"last\" do\n    it \"gets last when non empty\" do\n      a = [1, 2, 3]\n      a.last.should eq(3)\n    end\n\n    it \"raises when empty\" do\n      expect_raises IndexError do\n        ([] of Int32).last\n      end\n    end\n\n    it \"returns a sub array with given number of elements\" do\n      arr = [1, 2, 3]\n      arr.last(0).should eq([] of Int32)\n      arr.last(1).should eq [3]\n      arr.last(2).should eq [2, 3]\n      arr.last(3).should eq [1, 2, 3]\n      arr.last(4).should eq [1, 2, 3]\n    end\n  end\n\n  describe \"size\" do\n    it \"has size 0\" do\n      ([] of Int32).size.should eq(0)\n    end\n\n    it \"has size 2\" do\n      [1, 2].size.should eq(2)\n    end\n  end\n\n  it \"does map\" do\n    a = [1, 2, 3]\n    a.map { |x| x * 2 }.should eq([2, 4, 6])\n    a.should eq([1, 2, 3])\n  end\n\n  it \"does map!\" do\n    a = [1, 2, 3]\n    a.map! { |x| x * 2 }\n    a.should eq([2, 4, 6])\n  end\n\n  describe \"pop\" do\n    it \"pops when non empty\" do\n      a = [1, 2, 3]\n      a.pop.should eq(3)\n      a.should eq([1, 2])\n    end\n\n    it \"raises when empty\" do\n      expect_raises IndexError do\n        ([] of Int32).pop\n      end\n    end\n\n    it \"pops many elements\" do\n      a = [1, 2, 3, 4, 5]\n      b = a.pop(3)\n      b.should eq([3, 4, 5])\n      a.should eq([1, 2])\n    end\n\n    it \"pops more elements that what is available\" do\n      a = [1, 2, 3, 4, 5]\n      b = a.pop(10)\n      b.should eq([1, 2, 3, 4, 5])\n      a.should eq([] of Int32)\n    end\n\n    it \"pops negative count raises\" do\n      a = [1, 2]\n      expect_raises ArgumentError do\n        a.pop(-1)\n      end\n    end\n  end\n\n  it \"does product with block\" do\n    r = [] of Int32\n    [1, 2, 3].product([5, 6]) { |a, b| r << a; r << b }\n    r.should eq([1, 5, 1, 6, 2, 5, 2, 6, 3, 5, 3, 6])\n  end\n\n  it \"does product without block\" do\n    [1, 2, 3].product(['a', 'b']).should eq([{1, 'a'}, {1, 'b'}, {2, 'a'}, {2, 'b'}, {3, 'a'}, {3, 'b'}])\n  end\n\n  describe \"push\" do\n    it \"pushes one element\" do\n      a = [1, 2]\n      a.push(3).should be(a)\n      a.should eq [1, 2, 3]\n    end\n\n    it \"pushes multiple elements\" do\n      a = [1, 2]\n      a.push(3, 4).should be(a)\n      a.should eq [1, 2, 3, 4]\n    end\n\n    it \"pushes multiple elements to an empty array\" do\n      a = [] of Int32\n      a.push(1, 2, 3).should be(a)\n      a.should eq([1, 2, 3])\n    end\n\n    it \"has the << alias\" do\n      a = [1, 2]\n      a << 3\n      a.should eq [1, 2, 3]\n    end\n  end\n\n  describe \"#replace\" do\n    it \"replaces all elements\" do\n      a = [1, 2, 3]\n      b = [4, 5, 6]\n      a.replace(b).should be(a)\n      a.should eq(b)\n    end\n\n    it \"reuses the buffer if possible\" do\n      a = [1, 2, 3, 4, 5]\n      a.shift\n      b = [6, 7, 8, 9, 10]\n      a.replace(b).should be(a)\n      a.should eq(b)\n      a.@capacity.should eq(5)\n      a.@offset_to_buffer.should eq(0)\n\n      a = [1, 2, 3, 4, 5]\n      a.shift(2)\n      b = [6, 7, 8, 9]\n      a.replace(b).should be(a)\n      a.should eq(b)\n      a.@capacity.should eq(5)\n      a.@offset_to_buffer.should eq(1)\n    end\n\n    it \"resizes the buffer if capacity is not enough\" do\n      a = [1, 2, 3, 4, 5]\n      b = [6, 7, 8, 9, 10, 11]\n      a.replace(b).should be(a)\n      a.should eq(b)\n      a.@capacity.should eq(10)\n      a.@offset_to_buffer.should eq(0)\n    end\n\n    it \"clears unused elements if new size is smaller\" do\n      a = [1, 2, 3, 4, 5]\n      b = [6, 7, 8]\n      a.replace(b).should be(a)\n      a.should eq(b)\n      a.@capacity.should eq(5)\n      a.@offset_to_buffer.should eq(0)\n      a.unsafe_fetch(3).should eq(0)\n      a.unsafe_fetch(4).should eq(0)\n    end\n  end\n\n  it \"does reverse with an odd number of elements\" do\n    a = [1, 2, 3]\n    a.reverse.should eq([3, 2, 1])\n    a.should eq([1, 2, 3])\n  end\n\n  it \"does reverse with an even number of elements\" do\n    a = [1, 2, 3, 4]\n    a.reverse.should eq([4, 3, 2, 1])\n    a.should eq([1, 2, 3, 4])\n  end\n\n  it \"does reverse! with an odd number of elements\" do\n    a = [1, 2, 3, 4, 5]\n    a.reverse!\n    a.should eq([5, 4, 3, 2, 1])\n  end\n\n  it \"does reverse! with an even number of elements\" do\n    a = [1, 2, 3, 4, 5, 6]\n    a.reverse!\n    a.should eq([6, 5, 4, 3, 2, 1])\n  end\n\n  describe \"rindex\" do\n    it \"performs without a block\" do\n      a = [1, 2, 3, 4, 5, 3, 6]\n      a.rindex(3).should eq(5)\n      a.rindex(7).should be_nil\n    end\n\n    it \"performs without a block and an offset\" do\n      a = [1, 2, 3, 4, 5, 3, 6]\n      a.rindex(3, offset: 4).should eq(2)\n      a.rindex(6, offset: 4).should be_nil\n      a.rindex(3, offset: -2).should eq(5)\n      a.rindex(3, offset: -3).should eq(2)\n      a.rindex(3, offset: -100).should be_nil\n    end\n\n    it \"performs with a block\" do\n      a = [1, 2, 3, 4, 5, 3, 6]\n      a.rindex { |i| i > 1 }.should eq(6)\n      a.rindex { |i| i > 6 }.should be_nil\n    end\n\n    it \"performs with a block and offset\" do\n      a = [1, 2, 3, 4, 5, 3, 6]\n      a.rindex { |i| i > 1 }.should eq(6)\n      a.rindex { |i| i > 6 }.should be_nil\n      a.rindex(offset: 4) { |i| i == 3 }.should eq(2)\n      a.rindex(offset: -3) { |i| i == 3 }.should eq(2)\n    end\n  end\n\n  describe \"shift\" do\n    it \"shifts when non empty\" do\n      a = [1, 2, 3]\n      a.shift.should eq(1)\n      a.should eq([2, 3])\n    end\n\n    it \"raises when empty\" do\n      expect_raises IndexError do\n        ([] of Int32).shift\n      end\n    end\n\n    it \"shifts many elements\" do\n      a = [1, 2, 3, 4, 5]\n      b = a.shift(3)\n      b.should eq([1, 2, 3])\n      a.should eq([4, 5])\n    end\n\n    it \"shifts more than what is available\" do\n      a = [1, 2, 3, 4, 5]\n      b = a.shift(10)\n      b.should eq([1, 2, 3, 4, 5])\n      a.should eq([] of Int32)\n    end\n\n    it \"shifts negative count raises\" do\n      a = [1, 2]\n      expect_raises ArgumentError do\n        a.shift(-1)\n      end\n    end\n\n    it \"shifts one and resizes\" do\n      a = [1, 2, 3, 4]\n      old_capacity = a.@capacity\n      a.shift.should eq(1)\n      a.@offset_to_buffer.should eq(1)\n      a << 5\n      a.@capacity.should eq(old_capacity * 2)\n      a.@offset_to_buffer.should eq(1)\n      a.size.should eq(4)\n      a.should eq([2, 3, 4, 5])\n    end\n\n    it \"shifts almost all and then avoid resize\" do\n      a = [1, 2, 3, 4]\n      old_capacity = a.@capacity\n      (1..3).each do |i|\n        a.shift.should eq(i)\n      end\n      a.@offset_to_buffer.should eq(3)\n      a << 5\n      a.@capacity.should eq(old_capacity)\n      a.@offset_to_buffer.should eq(0)\n      a.size.should eq(2)\n      a.should eq([4, 5])\n    end\n\n    it \"shifts and then concats Array\" do\n      size = 10_000\n      a = (1..size).to_a\n      (size - 1).times do\n        a.shift\n      end\n      a.size.should eq(1)\n      a.concat((1..size).to_a)\n      a.size.should eq(size + 1)\n      a.should eq([size] + (1..size).to_a)\n    end\n\n    it \"shifts and then concats Enumerable\" do\n      size = 10_000\n      a = (1..size).to_a\n      (size - 1).times do\n        a.shift\n      end\n      a.size.should eq(1)\n      a.concat((1..size))\n      a.size.should eq(size + 1)\n      a.should eq([size] + (1..size).to_a)\n    end\n\n    it \"shifts all\" do\n      a = [1, 2, 3, 4]\n      buffer = a.@buffer\n      4.times do\n        a.shift\n      end\n      a.size.should eq(0)\n      a.@offset_to_buffer.should eq(0)\n      a.@buffer.should eq(buffer)\n    end\n\n    it \"shifts all after pop\" do\n      a = [1, 2, 3, 4]\n      buffer = a.@buffer\n      a.pop\n      3.times do\n        a.shift\n      end\n      a.size.should eq(0)\n      a.@offset_to_buffer.should eq(0)\n      a.@buffer.should eq(buffer)\n    end\n\n    it \"pops after shift\" do\n      a = [1, 2, 3, 4]\n      buffer = a.@buffer\n      3.times do\n        a.shift\n      end\n      a.pop\n      a.size.should eq(0)\n      a.@offset_to_buffer.should eq(0)\n      a.@buffer.should eq(buffer)\n    end\n\n    it \"shifts all with shift(n)\" do\n      a = [1, 2, 3, 4]\n      buffer = a.@buffer\n      a.shift\n      a.shift(3)\n      a.size.should eq(0)\n      a.@offset_to_buffer.should eq(0)\n      a.@buffer.should eq(buffer)\n    end\n  end\n\n  describe \"shuffle\" do\n    it \"shuffle!\" do\n      a = [1, 2, 3]\n      a.shuffle!\n      b = [1, 2, 3]\n      3.times { a.should contain(b.shift) }\n    end\n\n    it \"shuffle\" do\n      a = [1, 2, 3]\n      b = a.shuffle\n      a.should_not be(b)\n      a.should eq([1, 2, 3])\n\n      3.times { b.should contain(a.shift) }\n    end\n\n    it \"shuffle! with random\" do\n      a = [1, 2, 3]\n      a.shuffle!(Random.new(1))\n      a.should eq([1, 3, 2])\n    end\n\n    it \"shuffle with random\" do\n      a = [1, 2, 3]\n      b = a.shuffle(Random.new(1))\n      b.should eq([1, 3, 2])\n    end\n  end\n\n  describe \"sort\" do\n    {% for sort in [\"sort\".id, \"unstable_sort\".id] %}\n      describe {{ \"##{sort}\" }} do\n        it \"without block\" do\n          a = [3, 4, 1, 2, 5, 6]\n          b = a.{{ sort }}\n          b.should eq([1, 2, 3, 4, 5, 6])\n          a.should_not eq(b)\n        end\n\n        it \"with a block\" do\n          a = [\"foo\", \"a\", \"hello\"]\n          b = a.{{ sort }} { |x, y| x.size <=> y.size }\n          b.should eq([\"a\", \"foo\", \"hello\"])\n          a.should_not eq(b)\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort without a block\" do\n            is_stable_sort(mutable: false, &.sort)\n          end\n\n          it \"stable sort with a block\" do\n            is_stable_sort(mutable: false, &.sort { |a, b| a.value <=> b.value })\n          end\n        {% end %}\n      end\n\n      describe {{ \"##{sort}!\" }} do\n        it \"without block\" do\n          a = [3, 4, 1, 2, 5, 6]\n          a.{{ sort.id }}!\n          a.should eq([1, 2, 3, 4, 5, 6])\n        end\n\n        it \"with a block\" do\n          a = [\"foo\", \"a\", \"hello\"]\n          a.{{ sort.id }}! { |x, y| x.size <=> y.size }\n          a.should eq([\"a\", \"foo\", \"hello\"])\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort without a block\" do\n            is_stable_sort(mutable: true, &.sort!)\n          end\n\n          it \"stable sort with a block\" do\n            is_stable_sort(mutable: true, &.sort! { |a, b| a.value <=> b.value })\n          end\n        {% end %}\n      end\n\n      describe {{ \"##{sort}_by\" }} do\n        it \"sorts\" do\n          a = [\"foo\", \"a\", \"hello\"]\n          b = a.{{ sort }}_by(&.size)\n          b.should eq([\"a\", \"foo\", \"hello\"])\n          a.should_not eq(b)\n        end\n\n        it \"unpacks tuple\" do\n          a = [{\"d\", 4}, {\"a\", 1}, {\"c\", 3}, {\"e\", 5}, {\"b\", 2}]\n          b = a.{{ sort }}_by { |x, y| y }\n          b.should eq([{\"a\", 1}, {\"b\", 2}, {\"c\", 3}, {\"d\", 4}, {\"e\", 5}])\n          a.should_not eq(b)\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort\" do\n            is_stable_sort(mutable: false, &.sort_by(&.value))\n          end\n        {% end %}\n      end\n\n      describe {{ \"##{sort}_by!\" }} do\n        it \"sorts\" do\n          a = [\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}_by!(&.size)\n          a.should eq([\"a\", \"foo\", \"hello\"])\n        end\n\n        it \"calls given block exactly once for each element\" do\n          calls = Hash(String, Int32).new(0)\n          a = [\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}_by! { |e| calls[e] += 1; e.size }\n          calls.should eq({\"foo\" => 1, \"a\" => 1, \"hello\" => 1})\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort\" do\n            is_stable_sort(mutable: true, &.sort_by!(&.value))\n          end\n        {% end %}\n      end\n    {% end %}\n  end\n\n  describe \"swap\" do\n    it \"swaps\" do\n      a = [1, 2, 3]\n      a.swap(0, 2)\n      a.should eq([3, 2, 1])\n    end\n\n    it \"swaps with negative indices\" do\n      a = [1, 2, 3]\n      a.swap(-3, -1)\n      a.should eq([3, 2, 1])\n    end\n\n    it \"swaps but raises out of bounds on left\" do\n      a = [1, 2, 3]\n      expect_raises IndexError do\n        a.swap(3, 0)\n      end\n    end\n\n    it \"swaps but raises out of bounds on right\" do\n      a = [1, 2, 3]\n      expect_raises IndexError do\n        a.swap(0, 3)\n      end\n    end\n  end\n\n  describe \"to_s\" do\n    it \"does to_s\" do\n      [1, 2, 3].to_s.should eq(\"[1, 2, 3]\")\n    end\n\n    it \"does with recursive\" do\n      ary = [] of RecursiveArray\n      ary << ary\n      ary.to_s.should eq(\"[[...]]\")\n    end\n  end\n\n  describe \"#truncate\" do\n    it \"truncates with index and count\" do\n      a = [0, 1, 4, 9, 16, 25]\n      a.truncate(2, 3).should be(a)\n      a.should eq([4, 9, 16])\n    end\n\n    it \"truncates with index and count == 0\" do\n      a = [0, 1, 4, 9, 16, 25]\n      a.truncate(2, 0).should be(a)\n      a.should be_empty\n    end\n\n    it \"truncates with index and count, not enough elements\" do\n      a = [0, 1, 4, 9, 16, 25]\n      a.truncate(4, 4).should be(a)\n      a.should eq([16, 25])\n    end\n\n    it \"truncates with index == size and count\" do\n      a = [0, 1, 4, 9, 16, 25]\n      a.truncate(6, 1).should be(a)\n      a.should be_empty\n    end\n\n    it \"truncates with index < 0 and count\" do\n      a = [0, 1, 4, 9, 16, 25]\n      a.truncate(-5, 3).should be(a)\n      a.should eq([1, 4, 9])\n    end\n\n    it \"raises on out of bound index\" do\n      a = [0, 1, 4, 9, 16, 25]\n      expect_raises(IndexError) { a.truncate(-7, 1) }\n    end\n\n    it \"raises on negative count\" do\n      a = [0, 1, 4, 9, 16, 25]\n      expect_raises(ArgumentError) { a.truncate(0, -1) }\n    end\n\n    it \"truncates with range\" do\n      a = [0, 1, 4, 9, 16, 25]\n      a.truncate(2..4).should be(a)\n      a.should eq([4, 9, 16])\n\n      b = [0, 1, 4, 9, 16, 25]\n      b.truncate(-5..-3).should be(b)\n      b.should eq([1, 4, 9])\n    end\n  end\n\n  describe \"uniq\" do\n    it \"uniqs without block\" do\n      a = [1, 2, 2, 3, 1, 4, 5, 3]\n      b = a.uniq\n      b.should eq([1, 2, 3, 4, 5])\n      a.should_not be(b)\n    end\n\n    it \"uniqs with block\" do\n      a = [-1, 1, 0, 2, -2]\n      b = a.uniq &.abs\n      b.should eq([-1, 0, 2])\n      a.should_not be(b)\n    end\n\n    it \"uniqs with true\" do\n      a = [1, 2, 3]\n      b = a.uniq { true }\n      b.should eq([1])\n      a.should_not be(b)\n    end\n\n    it \"uniqs large array\" do\n      a = (1..32).to_a\n      (a * 4).uniq.should eq(a)\n    end\n  end\n\n  describe \"uniq!\" do\n    it \"uniqs without block\" do\n      a = [1, 2, 2, 3, 1, 4, 5, 3]\n      a.uniq!\n      a.should eq([1, 2, 3, 4, 5])\n    end\n\n    it \"uniqs with block\" do\n      a = [-1, 1, 0, 2, -2]\n      a.uniq! &.abs\n      a.should eq([-1, 0, 2])\n    end\n\n    it \"uniqs with true\" do\n      a = [1, 2, 3]\n      a.uniq! { true }\n      a.should eq([1])\n    end\n\n    it \"uniqs large array\" do\n      a = (1..32).to_a\n      b = a * 2\n      b.uniq!\n      b.should eq(a)\n    end\n  end\n\n  describe \"unshift\" do\n    it \"unshifts one element\" do\n      a = [1, 2]\n      a.unshift(3).should be(a)\n      a.should eq [3, 1, 2]\n    end\n\n    it \"unshifts one elements three times\" do\n      a = [] of Int32\n      3.times do |i|\n        a.unshift(i)\n      end\n      a.should eq([2, 1, 0])\n    end\n\n    it \"unshifts one element multiple times\" do\n      a = [1, 2]\n      (3..100).each do |i|\n        a.unshift(i)\n      end\n      a.should eq((3..100).to_a.reverse + [1, 2])\n    end\n\n    it \"unshifts multiple elements\" do\n      a = [1, 2]\n      a.unshift(3, 4).should be(a)\n      a.should eq [3, 4, 1, 2]\n    end\n\n    it \"unshifts multiple elements to an empty array\" do\n      a = [] of Int32\n      a.unshift(1, 2, 3).should be(a)\n      a.should eq([1, 2, 3])\n    end\n\n    it \"unshifts after shift\" do\n      a = [1, 2, 3, 4]\n      buffer = a.@buffer\n      a.shift\n      a.unshift(10)\n      a.should eq([10, 2, 3, 4])\n      a.@offset_to_buffer.should eq(0)\n      a.@buffer.should eq(buffer)\n    end\n\n    it \"unshifts many after many shifts\" do\n      a = [1, 2, 3, 4, 5, 6, 7, 8]\n      buffer = a.@buffer\n      3.times { a.shift }\n      a.unshift(10, 20, 30)\n      a.should eq([10, 20, 30, 4, 5, 6, 7, 8])\n      a.@offset_to_buffer.should eq(0)\n      a.@buffer.should eq(buffer)\n    end\n\n    it \"repeated unshift/shift does not exhaust memory\" do\n      a = [] of Int32\n      10.times do\n        a.unshift(1)\n        a.shift\n      end\n      a.@capacity.should eq(3)\n    end\n\n    it \"repeated unshift/pop does not exhaust memory (#10748)\" do\n      a = [] of Int32\n      10.times do\n        a.unshift(1)\n        a.pop\n      end\n      a.@capacity.should eq(3)\n    end\n\n    it \"repeated unshift/clear does not exhaust memory\" do\n      a = [] of Int32\n      10.times do\n        a.unshift(1)\n        a.clear\n      end\n      a.@capacity.should eq(3)\n    end\n\n    it \"unshift of large array does not corrupt elements\" do\n      a = Array.new(300, &.itself)\n      a.@capacity.should eq(300)\n      a.unshift 1\n      a.@capacity.should be > 300\n      a[-1].should eq(299)\n    end\n  end\n\n  it \"does update\" do\n    a = [1, 2, 3]\n    a.update(1) { |x| x * 2 }\n    a.should eq([1, 4, 3])\n  end\n\n  it \"does <=>\" do\n    a = [1, 2, 3]\n    b = [4, 5, 6]\n    c = [1, 2]\n\n    (a <=> b).should be < 0\n    (a <=> c).should be > 0\n    (b <=> c).should be > 0\n    (b <=> a).should be > 0\n    (c <=> a).should be < 0\n    (c <=> b).should be < 0\n    (a <=> a).should eq(0)\n\n    ([8] <=> [1, 2, 3]).should be > 0\n    ([8] <=> [8, 1, 2]).should be < 0\n\n    [[1, 2, 3], [4, 5], [8], [1, 2, 3, 4]].sort.should eq([[1, 2, 3], [1, 2, 3, 4], [4, 5], [8]])\n  end\n\n  it \"does each while modifying array\" do\n    a = [1, 2, 3]\n    count = 0\n    a.each do\n      count += 1\n      a.clear\n    end.should be_nil\n    count.should eq(1)\n  end\n\n  it \"does each index while modifying array\" do\n    a = [1, 2, 3]\n    count = 0\n    a.each_index do\n      count += 1\n      a.clear\n    end.should be_nil\n    count.should eq(1)\n  end\n\n  describe \"zip\" do\n    describe \"when a block is provided\" do\n      it \"yields pairs of self's elements and passed array\" do\n        a, b, r = [1, 2, 3], [4, 5, 6], \"\"\n        a.zip(b) { |x, y| r += \"#{x}:#{y},\" }\n        r.should eq(\"1:4,2:5,3:6,\")\n      end\n\n      it \"works with iterable\" do\n        a = [1, 2, 3]\n        b = ('a'..'c')\n        r = [] of {Int32, Char}\n        a.zip(b) do |x, y|\n          r << {x, y}\n        end\n        r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])\n      end\n\n      it \"works with iterator\" do\n        a = [1, 2, 3]\n        b = ('a'..'c').each\n        r = [] of {Int32, Char}\n        a.zip(b) do |x, y|\n          r << {x, y}\n        end\n        r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])\n      end\n    end\n\n    describe \"when no block is provided\" do\n      describe \"and the arrays have different typed elements\" do\n        it \"returns an array of paired elements (tuples)\" do\n          a, b = [1, 2, 3], [\"a\", \"b\", \"c\"]\n          r = a.zip(b)\n          r.should be_a(Array({Int32, String}))\n          r.should eq([{1, \"a\"}, {2, \"b\"}, {3, \"c\"}])\n        end\n\n        it \"works with iterable\" do\n          a = [1, 2, 3]\n          b = ('a'..'c')\n          r = a.zip(b)\n          r.should be_a(Array({Int32, Char}))\n          r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])\n        end\n\n        it \"works with iterator\" do\n          a = [1, 2, 3]\n          b = ('a'..'c').each\n          r = a.zip(b)\n          r.should be_a(Array({Int32, Char}))\n          r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])\n        end\n\n        it \"zips three things\" do\n          a = [1, 2, 3]\n          b = 'a'..'c'\n          c = ('x'..'z').each\n          r = a.zip(b, c)\n          r.should be_a(Array({Int32, Char, Char}))\n          r.should eq([{1, 'a', 'x'}, {2, 'b', 'y'}, {3, 'c', 'z'}])\n        end\n\n        it \"zips union type (#8608)\" do\n          a = [1, 2, 3]\n          b = 'a'..'c'\n          c = ('x'..'z').each\n          r = a.zip(a || b || c)\n          r.should be_a(Array({Int32, Int32 | Char}))\n          r.should eq([{1, 1}, {2, 2}, {3, 3}])\n        end\n      end\n    end\n  end\n\n  describe \"zip?\" do\n    describe \"when a block is provided\" do\n      describe \"and size of an arg is less than receiver\" do\n        it \"yields pairs of self's elements and passed array (with nil)\" do\n          a, b, r = [1, 2, 3], [4, 5], \"\"\n          a.zip?(b) { |x, y| r += \"#{x}:#{y},\" }\n          r.should eq(\"1:4,2:5,3:,\")\n        end\n\n        it \"works with iterable\" do\n          a = [1, 2, 3]\n          b = ('a'..'b')\n          r = [] of {Int32, Char?}\n          a.zip?(b) do |x, y|\n            r << {x, y}\n          end\n          r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])\n        end\n\n        it \"works with iterator\" do\n          a = [1, 2, 3]\n          b = ('a'..'b').each\n          r = [] of {Int32, Char?}\n          a.zip?(b) do |x, y|\n            r << {x, y}\n          end\n          r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])\n        end\n      end\n    end\n\n    describe \"when no block is provided\" do\n      describe \"and the arrays have different typed elements\" do\n        describe \"and size of an arg is less than receiver\" do\n          it \"returns an array of paired elements (tuples with nil)\" do\n            a, b = [1, 2, 3], [\"a\", \"b\"]\n            r = a.zip?(b)\n            r.should eq([{1, \"a\"}, {2, \"b\"}, {3, nil}])\n          end\n\n          it \"works with iterable\" do\n            a = [1, 2, 3]\n            b = ('a'..'b')\n            r = a.zip?(b)\n            r.should be_a(Array({Int32, Char?}))\n            r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])\n          end\n\n          it \"works with iterator\" do\n            a = [1, 2, 3]\n            b = ('a'..'b').each\n            r = a.zip?(b)\n            r.should be_a(Array({Int32, Char?}))\n            r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])\n          end\n\n          it \"zips three things\" do\n            a = [1, 2, 3]\n            b = 'a'..'b'\n            c = ('x'..'y').each\n            r = a.zip?(b, c)\n            r.should be_a(Array({Int32, Char?, Char?}))\n            r.should eq([{1, 'a', 'x'}, {2, 'b', 'y'}, {3, nil, nil}])\n          end\n\n          it \"zips union type (#8608)\" do\n            a = [1, 2, 3]\n            b = 'a'..'c'\n            c = ('x'..'z').each\n            r = a.zip?(a || b || c)\n            r.should be_a(Array({Int32, Int32 | Char | Nil}))\n            r.should eq([{1, 1}, {2, 2}, {3, 3}])\n          end\n        end\n      end\n    end\n  end\n\n  it \"does compact_map\" do\n    a = [1, 2, 3, 4, 5]\n    b = a.compact_map { |e| e.divisible_by?(2) ? e : nil }\n    b.size.should eq(2)\n    b.should eq([2, 4])\n  end\n\n  it \"does compact_map with false\" do\n    a = [1, 2, 3]\n    b = a.compact_map do |e|\n      case e\n      when 1 then 1\n      when 2 then nil\n      else        false\n      end\n    end\n    b.size.should eq(2)\n    b.should eq([1, false])\n  end\n\n  it \"builds from buffer\" do\n    ary = Array(Int32).build(4) do |buffer|\n      buffer[0] = 1\n      buffer[1] = 2\n      2\n    end\n    ary.size.should eq(2)\n    ary.should eq([1, 2])\n  end\n\n  it \"selects!\" do\n    ary1 = [1, 2, 3, 4, 5]\n\n    ary2 = ary1.select! { |elem| elem % 2 == 0 }\n    ary2.should eq([2, 4])\n    ary2.should be(ary1)\n  end\n\n  it \"selects! with pattern\" do\n    ary1 = [1, 2, 3, 4, 5]\n\n    ary2 = ary1.select!(2..4)\n    ary2.should eq([2, 3, 4])\n    ary2.should be(ary1)\n  end\n\n  it \"rejects!\" do\n    ary1 = [1, 2, 3, 4, 5]\n\n    ary2 = ary1.reject! { |elem| elem % 2 == 0 }\n    ary2.should eq([1, 3, 5])\n    ary2.should be(ary1)\n  end\n\n  it \"rejects! with pattern\" do\n    ary1 = [1, 2, 3, 4, 5]\n\n    ary2 = ary1.reject!(2..4)\n    ary2.should eq([1, 5])\n    ary2.should be(ary1)\n  end\n\n  it \"does map_with_index\" do\n    ary = [1, 1, 2, 2]\n    ary2 = ary.map_with_index { |e, i| e + i }\n    ary2.should eq([1, 2, 4, 5])\n  end\n\n  it \"does map_with_index, with offset\" do\n    ary = [1, 1, 2, 2]\n    ary2 = ary.map_with_index(10) { |e, i| e + i }\n    ary2.should eq([11, 12, 14, 15])\n  end\n\n  it \"does map_with_index!\" do\n    ary = [0, 1, 2]\n    ary2 = ary.map_with_index! { |e, i| i * 2 }\n    ary.should eq([0, 2, 4])\n    ary2.should be(ary)\n  end\n\n  it \"does map_with_index!, with offset\" do\n    ary = [0, 1, 2]\n    ary2 = ary.map_with_index!(10) { |e, i| i * 2 }\n    ary.should eq([20, 22, 24])\n    ary2.should be(ary)\n  end\n\n  it \"does + with different types (#568)\" do\n    a = [1, 2, 3]\n    a += [\"hello\"]\n    a.should eq([1, 2, 3, \"hello\"])\n  end\n\n  it_iterates \"#each\", [1, 2, 3], [1, 2, 3].each\n  it_iterates \"#reverse_each\", [3, 2, 1], [1, 2, 3].reverse_each\n\n  it_iterates \"#cycle\", [1, 2, 3, 1, 2, 3, 1, 2], [1, 2, 3].cycle, infinite: true\n  it_iterates \"#cycle(limit)\", [1, 2, 3, 1, 2, 3], [1, 2, 3].cycle(2), infinite: true\n\n  it_iterates \"#each_index\", [0, 1, 2], [1, 2, 3].each_index\n\n  describe \"transpose\" do\n    it \"transposes elements\" do\n      [['a', 'b'], ['c', 'd'], ['e', 'f']].transpose.should eq([['a', 'c', 'e'], ['b', 'd', 'f']])\n      [['a', 'c', 'e'], ['b', 'd', 'f']].transpose.should eq([['a', 'b'], ['c', 'd'], ['e', 'f']])\n      [['a']].transpose.should eq([['a']])\n    end\n\n    it \"transposes union of arrays\" do\n      [[1, 2], [1.0, 2.0]].transpose.should eq([[1, 1.0], [2, 2.0]])\n      [[1, 2.0], [1, 2.0]].transpose.should eq([[1, 1], [2.0, 2.0]])\n      [[1, 1.0], ['a', \"aaa\"]].transpose.should eq([[1, 'a'], [1.0, \"aaa\"]])\n\n      typeof([[1.0], [1]].transpose).should eq(Array(Array(Int32 | Float64)))\n      typeof([[1, 1.0], ['a', \"aaa\"]].transpose).should eq(Array(Array(String | Int32 | Float64 | Char)))\n    end\n\n    it \"transposes empty array\" do\n      e = [] of Array(Int32)\n      e.transpose.should be_empty\n      [e].transpose.should be_empty\n      [e, e, e].transpose.should be_empty\n    end\n\n    it \"raises IndexError error when size of element is invalid\" do\n      expect_raises(IndexError) { [[1], [1, 2]].transpose }\n      expect_raises(IndexError) { [[1, 2], [1]].transpose }\n    end\n\n    it \"transposes array of tuples\" do\n      [{1, 1.0}].transpose.should eq([[1], [1.0]])\n      [{1}, {1.0}].transpose.should eq([[1, 1.0]])\n      [{1, 1.0}, {'a', \"aaa\"}].transpose.should eq([[1, 'a'], [1.0, \"aaa\"]])\n\n      typeof([{1, 1.0}].transpose).should eq(Array(Array(Int32 | Float64)))\n      typeof([{1}, {1.0}].transpose).should eq(Array(Array(Int32 | Float64)))\n      typeof([{1, 1.0}, {'a', \"aaa\"}].transpose).should eq(Array(Array(String | Int32 | Float64 | Char)))\n    end\n  end\n\n  describe \"rotate\" do\n    it \"rotate!\" do\n      a = [1, 2, 3]\n      a.rotate!.should be(a); a.should eq([2, 3, 1])\n      a.rotate!.should be(a); a.should eq([3, 1, 2])\n      a.rotate!.should be(a); a.should eq([1, 2, 3])\n      a.rotate!.should be(a); a.should eq([2, 3, 1])\n    end\n\n    it \"rotate\" do\n      a = [1, 2, 3]\n      a.rotate.should eq([2, 3, 1])\n      a.should eq([1, 2, 3])\n      a.rotate.should eq([2, 3, 1])\n    end\n\n    it { a = [1, 2, 3]; a.rotate!(0); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate!(1); a.should eq([2, 3, 1]) }\n    it { a = [1, 2, 3]; a.rotate!(2); a.should eq([3, 1, 2]) }\n    it { a = [1, 2, 3]; a.rotate!(3); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate!(4); a.should eq([2, 3, 1]) }\n    it { a = [1, 2, 3]; a.rotate!(3001); a.should eq([2, 3, 1]) }\n    it { a = [1, 2, 3]; a.rotate!(-1); a.should eq([3, 1, 2]) }\n    it { a = [1, 2, 3]; a.rotate!(-3001); a.should eq([3, 1, 2]) }\n\n    it { a = [1, 2, 3]; a.rotate(0).should eq([1, 2, 3]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(1).should eq([2, 3, 1]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(2).should eq([3, 1, 2]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(3).should eq([1, 2, 3]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(4).should eq([2, 3, 1]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(3001).should eq([2, 3, 1]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(-1).should eq([3, 1, 2]); a.should eq([1, 2, 3]) }\n    it { a = [1, 2, 3]; a.rotate(-3001).should eq([3, 1, 2]); a.should eq([1, 2, 3]) }\n\n    it do\n      a = Array(Int32).new(50) { |i| i }\n      a.rotate!(5)\n      a.should eq([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, 0, 1, 2, 3, 4])\n    end\n\n    it do\n      a = Array(Int32).new(50) { |i| i }\n      a.rotate!(-5)\n      a.should eq([45, 46, 47, 48, 49, 0, 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])\n    end\n\n    it do\n      a = Array(Int32).new(50) { |i| i }\n      a.rotate!(20)\n      a.should eq([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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])\n    end\n\n    it do\n      a = Array(Int32).new(50) { |i| i }\n      a.rotate!(-20)\n      a.should eq([30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 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])\n    end\n  end\n\n  describe \"repeated_permutations\" do\n    it { [1, 2, 2].repeated_permutations.should eq([[1, 1, 1], [1, 1, 2], [1, 1, 2], [1, 2, 1], [1, 2, 2], [1, 2, 2], [1, 2, 1], [1, 2, 2], [1, 2, 2], [2, 1, 1], [2, 1, 2], [2, 1, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2], [2, 1, 1], [2, 1, 2], [2, 1, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2]]) }\n    it { [1, 2, 3].repeated_permutations.should eq([[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3], [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], [3, 1, 1], [3, 1, 2], [3, 1, 3], [3, 2, 1], [3, 2, 2], [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3]]) }\n    it { [1, 2, 3].repeated_permutations(1).should eq([[1], [2], [3]]) }\n    it { [1, 2, 3].repeated_permutations(2).should eq([[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]) }\n    it { [1, 2, 3].repeated_permutations(3).should eq([[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3], [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], [3, 1, 1], [3, 1, 2], [3, 1, 3], [3, 2, 1], [3, 2, 2], [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3]]) }\n    it { [1, 2, 3].repeated_permutations(0).should eq([[] of Int32]) }\n    it { [1, 2, 3].repeated_permutations(4).should eq([[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 2, 1], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 3, 1], [1, 1, 3, 2], [1, 1, 3, 3], [1, 2, 1, 1], [1, 2, 1, 2], [1, 2, 1, 3], [1, 2, 2, 1], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 3, 1], [1, 2, 3, 2], [1, 2, 3, 3], [1, 3, 1, 1], [1, 3, 1, 2], [1, 3, 1, 3], [1, 3, 2, 1], [1, 3, 2, 2], [1, 3, 2, 3], [1, 3, 3, 1], [1, 3, 3, 2], [1, 3, 3, 3], [2, 1, 1, 1], [2, 1, 1, 2], [2, 1, 1, 3], [2, 1, 2, 1], [2, 1, 2, 2], [2, 1, 2, 3], [2, 1, 3, 1], [2, 1, 3, 2], [2, 1, 3, 3], [2, 2, 1, 1], [2, 2, 1, 2], [2, 2, 1, 3], [2, 2, 2, 1], [2, 2, 2, 2], [2, 2, 2, 3], [2, 2, 3, 1], [2, 2, 3, 2], [2, 2, 3, 3], [2, 3, 1, 1], [2, 3, 1, 2], [2, 3, 1, 3], [2, 3, 2, 1], [2, 3, 2, 2], [2, 3, 2, 3], [2, 3, 3, 1], [2, 3, 3, 2], [2, 3, 3, 3], [3, 1, 1, 1], [3, 1, 1, 2], [3, 1, 1, 3], [3, 1, 2, 1], [3, 1, 2, 2], [3, 1, 2, 3], [3, 1, 3, 1], [3, 1, 3, 2], [3, 1, 3, 3], [3, 2, 1, 1], [3, 2, 1, 2], [3, 2, 1, 3], [3, 2, 2, 1], [3, 2, 2, 2], [3, 2, 2, 3], [3, 2, 3, 1], [3, 2, 3, 2], [3, 2, 3, 3], [3, 3, 1, 1], [3, 3, 1, 2], [3, 3, 1, 3], [3, 3, 2, 1], [3, 3, 2, 2], [3, 3, 2, 3], [3, 3, 3, 1], [3, 3, 3, 2], [3, 3, 3, 3]]) }\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].repeated_permutations(-1) } }\n\n    it \"accepts a block\" do\n      sums = [] of Int32\n      [1, 2, 3].each_repeated_permutation(2) do |a|\n        sums << a.sum\n      end.should be_nil\n      sums.should eq([2, 3, 4, 3, 4, 5, 4, 5, 6])\n    end\n\n    it \"yielding dup of arrays\" do\n      sums = [] of Int32\n      [1, 2, 3].each_repeated_permutation(3) do |a|\n        a.map! &.+(1)\n        sums << a.sum\n      end.should be_nil\n      sums.should eq([6, 7, 8, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 9, 10, 11, 10, 11, 12])\n    end\n\n    it \"yields with reuse = true\" do\n      sums = [] of Int32\n      object_ids = Set(UInt64).new\n      [1, 2, 3].each_repeated_permutation(3, reuse: true) do |a|\n        object_ids << a.object_id\n        a.map! &.+(1)\n        sums << a.sum\n      end.should be_nil\n      sums.should eq([6, 7, 8, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 9, 10, 11, 10, 11, 12])\n      object_ids.size.should eq(1)\n    end\n\n    it \"yields with reuse = array\" do\n      sums = [] of Int32\n      reuse = [] of Int32\n      [1, 2, 3].each_repeated_permutation(3, reuse: reuse) do |a|\n        a.should be(reuse)\n        a.map! &.+(1)\n        sums << a.sum\n      end.should be_nil\n      sums.should eq([6, 7, 8, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 9, 10, 11, 10, 11, 12])\n    end\n\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].each_repeated_permutation(-1) { } } }\n  end\n\n  describe \"Array.each_product\" do\n    it \"one empty array\" do\n      empty = [] of Int32\n      res = [] of Array(Int32)\n      Array.each_product([empty, [1, 2, 3]]) { |r| res << r }\n      Array.each_product([[1, 2, 3], empty]) { |r| res << r }\n      res.size.should eq(0)\n    end\n\n    it \"single array\" do\n      res = [] of Array(Int32)\n      Array.each_product([[1]]) { |r| res << r }\n      res.should eq([[1]])\n    end\n\n    it \"2 arrays\" do\n      res = [] of Array(Int32)\n      Array.each_product([[1, 2], [3, 4]]) { |r| res << r }\n      res.should eq([[1, 3], [1, 4], [2, 3], [2, 4]])\n    end\n\n    it \"2 arrays different types\" do\n      res = [] of Array(Int32 | Char)\n      Array.each_product([[1, 2], ['a', 'b']]) { |r| res << r }\n      res.should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])\n    end\n\n    it \"more arrays\" do\n      res = [] of Array(Int32)\n      Array.each_product([[1, 2], [3], [5, 6]]) { |r| res << r }\n      res.should eq([[1, 3, 5], [1, 3, 6], [2, 3, 5], [2, 3, 6]])\n    end\n\n    it \"more arrays, reuse = true\" do\n      res = [] of Array(Int32)\n      object_ids = Set(UInt64).new\n      Array.each_product([[1, 2], [3], [5, 6]], reuse: true) do |r|\n        object_ids << r.object_id\n        res << r.dup\n      end\n      res.should eq([[1, 3, 5], [1, 3, 6], [2, 3, 5], [2, 3, 6]])\n      object_ids.size.should eq(1)\n    end\n\n    it \"with splat\" do\n      res = [] of Array(Int32 | Char)\n      Array.each_product([1, 2], ['a', 'b']) { |r| res << r }\n      res.should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])\n    end\n  end\n\n  describe \"Array.product\" do\n    it \"with array\" do\n      Array.product([[1, 2], ['a', 'b']]).should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])\n    end\n\n    it \"with splat\" do\n      Array.product([1, 2], ['a', 'b']).should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])\n    end\n  end\n\n  it \"doesn't overflow buffer with Array.new(size, value) (#1209)\" do\n    a = Array.new(1, 1_i64)\n    b = Array.new(1, 1_i64)\n    b << 2_i64 << 3_i64\n    a.should eq([1])\n    b.should eq([1, 2, 3])\n  end\n\n  it \"flattens\" do\n    [[1, 'a'], [[[[true], \"hi\"]]]].flatten.should eq([1, 'a', true, \"hi\"])\n\n    s = [1, 2, 3]\n    t = [4, 5, 6, [7, 8]]\n    u = [9, [10, 11].each]\n    a = [s, t, u, 12, 13]\n    result = a.flatten.to_a\n    result.should eq([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])\n    result.should be_a(Array(Int32))\n  end\n\n  it \"#skip\" do\n    ary = [1, 2, 3]\n    ary.skip(0).should eq([1, 2, 3])\n    ary.skip(1).should eq([2, 3])\n    ary.skip(2).should eq([3])\n    ary.skip(3).should eq([] of Int32)\n    ary.skip(4).should eq([] of Int32)\n\n    expect_raises(ArgumentError, \"Attempt to skip negative size\") do\n      ary.skip(-1)\n    end\n  end\n\n  describe \"capacity re-sizing\" do\n    it \"initializes an array capacity to INITIAL_CAPACITY\" do\n      a = [] of Int32\n      a.push(1)\n      a.@capacity.should eq(3)\n    end\n\n    it \"doubles capacity for arrays smaller than CAPACITY_THRESHOLD\" do\n      a = Array.new(255, 1)\n      a.push(1)\n      a.@capacity.should eq(255 * 2)\n    end\n\n    it \"uses slow growth heuristic for arrays larger than CAPACITY_THRESHOLD\" do\n      a = Array.new(512, 1)\n      a.push(1)\n      # ~63% larger\n      a.@capacity.should eq(832)\n\n      b = Array.new(4096, 1)\n      b.push(1)\n      # ~30% larger, starts converging toward 25%\n      b.@capacity.should eq(5312)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/atomic_spec.cr",
    "content": "require \"spec\"\n\nenum AtomicEnum\n  One\n  Two\n  Three\n  Minus = -1\nend\n\n@[Flags]\nenum AtomicEnumFlags\n  One\n  Two\n  Three\nend\n\nprivate struct AtomicBooleans\n  @one = Atomic(Bool).new(false)\n  @two = Atomic(Bool).new(false)\n  @three = Atomic(Bool).new(false)\nend\n\ndescribe Atomic do\n  describe \"#compare_and_set\" do\n    it \"with bool\" do\n      atomic = Atomic.new(true)\n\n      atomic.compare_and_set(false, true).should eq({true, false})\n      atomic.get.should be_true\n\n      atomic.compare_and_set(true, false).should eq({true, true})\n      atomic.get.should be_false\n    end\n\n    it \"with integer\" do\n      atomic = Atomic.new(1)\n\n      atomic.compare_and_set(2, 3).should eq({1, false})\n      atomic.get.should eq(1)\n\n      atomic.compare_and_set(1, 3).should eq({1, true})\n      atomic.get.should eq(3)\n    end\n\n    it \"with enum\" do\n      atomic = Atomic(AtomicEnum).new(AtomicEnum::One)\n\n      atomic.compare_and_set(AtomicEnum::Two, AtomicEnum::Three).should eq({AtomicEnum::One, false})\n      atomic.get.should eq(AtomicEnum::One)\n\n      atomic.compare_and_set(AtomicEnum::One, AtomicEnum::Three).should eq({AtomicEnum::One, true})\n      atomic.get.should eq(AtomicEnum::Three)\n    end\n\n    it \"with flags enum\" do\n      atomic = Atomic(AtomicEnumFlags).new(AtomicEnumFlags::One)\n\n      atomic.compare_and_set(AtomicEnumFlags::Two, AtomicEnumFlags::Three).should eq({AtomicEnumFlags::One, false})\n      atomic.get.should eq(AtomicEnumFlags::One)\n\n      atomic.compare_and_set(AtomicEnumFlags::One, AtomicEnumFlags::Three).should eq({AtomicEnumFlags::One, true})\n      atomic.get.should eq(AtomicEnumFlags::Three)\n    end\n\n    it \"with pointer\" do\n      atomic = Atomic.new(Pointer(Void).null)\n\n      atomic.compare_and_set(Pointer(Void).new(1), Pointer(Void).new(3)).should eq({Pointer(Void).null, false})\n      atomic.get.should eq(Pointer(Void).null)\n\n      atomic.compare_and_set(Pointer(Void).null, Pointer(Void).new(3)).should eq({Pointer(Void).null, true})\n      atomic.get.should eq(Pointer(Void).new(3))\n    end\n\n    it \"with nilable reference\" do\n      atomic = Atomic(String?).new(nil)\n      string = \"hello\"\n\n      atomic.compare_and_set(string, \"foo\").should eq({nil, false})\n      atomic.get.should be_nil\n\n      atomic.compare_and_set(nil, string).should eq({nil, true})\n      atomic.get.should be(string)\n\n      atomic.compare_and_set(string, nil).should eq({string, true})\n      atomic.get.should be_nil\n    end\n\n    it \"with reference type\" do\n      str1 = \"hello\"\n      str2 = \"bye\"\n\n      atomic = Atomic(String).new(str1)\n\n      atomic.compare_and_set(str2, \"foo\").should eq({str1, false})\n      atomic.get.should be(str1)\n\n      atomic.compare_and_set(str1, str2).should eq({str1, true})\n      atomic.get.should be(str2)\n\n      atomic.compare_and_set(str2, str1).should eq({str2, true})\n      atomic.get.should be(str1)\n\n      atomic.compare_and_set(String.build(&.<< \"bye\"), str2).should eq({str1, false})\n      atomic.get.should be(str1)\n    end\n\n    it \"with reference union\" do\n      arr1 = [1]\n      arr2 = [\"\"]\n\n      atomic = Atomic(Array(Int32) | Array(String)).new(arr1)\n\n      atomic.compare_and_set(arr2, [\"foo\"]).should eq({arr1, false})\n      atomic.get.should be(arr1)\n\n      atomic.compare_and_set(arr1, arr2).should eq({arr1, true})\n      atomic.get.should be(arr2)\n\n      atomic.compare_and_set(arr2, arr1).should eq({arr2, true})\n      atomic.get.should be(arr1)\n\n      atomic.compare_and_set([1], arr2).should eq({arr1, false})\n      atomic.get.should be(arr1)\n    end\n\n    it \"explicit ordering\" do\n      atomic = Atomic.new(1)\n\n      atomic.compare_and_set(2, 3, :acquire, :relaxed).should eq({1, false})\n      atomic.get.should eq(1)\n\n      atomic.compare_and_set(1, 3, :acquire_release, :relaxed).should eq({1, true})\n      atomic.get.should eq(3)\n    end\n  end\n\n  it \"#adds\" do\n    atomic = Atomic.new(1)\n    atomic.add(2).should eq(1)\n    atomic.get.should eq(3)\n    atomic.add(1, :relaxed).should eq(3)\n    atomic.get.should eq(4)\n  end\n\n  it \"#sub\" do\n    atomic = Atomic.new(1)\n    atomic.sub(2).should eq(1)\n    atomic.get.should eq(-1)\n    atomic.sub(1, :relaxed).should eq(-1)\n    atomic.get.should eq(-2)\n  end\n\n  it \"#and\" do\n    atomic = Atomic.new(5)\n    atomic.and(3).should eq(5)\n    atomic.get.should eq(1)\n    atomic.and(7, :relaxed).should eq(1)\n    atomic.get.should eq(1)\n  end\n\n  it \"#nand\" do\n    atomic = Atomic.new(5)\n    atomic.nand(3).should eq(5)\n    atomic.get.should eq(-2)\n    atomic.nand(7, :relaxed).should eq(-2)\n    atomic.get.should eq(-7)\n  end\n\n  it \"#or\" do\n    atomic = Atomic.new(5)\n    atomic.or(2).should eq(5)\n    atomic.get.should eq(7)\n    atomic.or(8, :relaxed).should eq(7)\n    atomic.get.should eq(15)\n  end\n\n  it \"#xor\" do\n    atomic = Atomic.new(5)\n    atomic.xor(3).should eq(5)\n    atomic.get.should eq(6)\n    atomic.xor(5, :relaxed).should eq(6)\n    atomic.get.should eq(3)\n  end\n\n  it \"#max with signed\" do\n    atomic = Atomic.new(5)\n    atomic.max(2).should eq(5)\n    atomic.get.should eq(5)\n    atomic.max(10, :relaxed).should eq(5)\n    atomic.get.should eq(10)\n  end\n\n  it \"#max with unsigned\" do\n    atomic = Atomic.new(5_u32)\n    atomic.max(2_u32).should eq(5_u32)\n    atomic.get.should eq(5_u32)\n    atomic.max(UInt32::MAX, :relaxed).should eq(5_u32)\n    atomic.get.should eq(UInt32::MAX)\n  end\n\n  it \"#max with signed enum\" do\n    atomic = Atomic.new(AtomicEnum::Two)\n    atomic.max(AtomicEnum::One).should eq(AtomicEnum::Two)\n    atomic.get.should eq(AtomicEnum::Two)\n    atomic.max(AtomicEnum::Three).should eq(AtomicEnum::Two)\n    atomic.get.should eq(AtomicEnum::Three)\n    atomic.max(AtomicEnum::Minus).should eq(AtomicEnum::Three)\n    atomic.get.should eq(AtomicEnum::Three)\n  end\n\n  it \"#max with pointer type\" do\n    atomic = Atomic.new(Pointer(Void).new(2))\n    atomic.max(Pointer(Void).new(1)).should eq(Pointer(Void).new(2))\n    atomic.get.should eq(Pointer(Void).new(2))\n    atomic.max(Pointer(Void).new(3)).should eq(Pointer(Void).new(2))\n    atomic.get.should eq(Pointer(Void).new(3))\n    atomic.max(Pointer(Void).new(UInt64::MAX)).should eq(Pointer(Void).new(3))\n    atomic.get.should eq(Pointer(Void).new(UInt64::MAX))\n  end\n\n  it \"#min with signed\" do\n    atomic = Atomic.new(5)\n    atomic.min(10).should eq(5)\n    atomic.get.should eq(5)\n    atomic.min(2, :relaxed).should eq(5)\n    atomic.get.should eq(2)\n  end\n\n  it \"#min with unsigned\" do\n    atomic = Atomic.new(UInt32::MAX)\n    atomic.min(10_u32).should eq(UInt32::MAX)\n    atomic.get.should eq(10_u32)\n    atomic.min(15_u32, :relaxed).should eq(10_u32)\n    atomic.get.should eq(10_u32)\n  end\n\n  it \"#min with signed enum\" do\n    atomic = Atomic.new(AtomicEnum::Two)\n    atomic.min(AtomicEnum::Three).should eq(AtomicEnum::Two)\n    atomic.get.should eq(AtomicEnum::Two)\n    atomic.min(AtomicEnum::One).should eq(AtomicEnum::Two)\n    atomic.get.should eq(AtomicEnum::One)\n    atomic.min(AtomicEnum::Minus).should eq(AtomicEnum::One)\n    atomic.get.should eq(AtomicEnum::Minus)\n  end\n\n  it \"#min with pointer type\" do\n    atomic = Atomic.new(Pointer(Void).new(2))\n    atomic.min(Pointer(Void).new(3)).should eq(Pointer(Void).new(2))\n    atomic.get.should eq(Pointer(Void).new(2))\n    atomic.min(Pointer(Void).new(1)).should eq(Pointer(Void).new(2))\n    atomic.get.should eq(Pointer(Void).new(1))\n    atomic.min(Pointer(Void).new(UInt64::MAX)).should eq(Pointer(Void).new(1))\n    atomic.get.should eq(Pointer(Void).new(1))\n  end\n\n  describe \"#set\" do\n    it \"with bool\" do\n      atomic = Atomic.new(false)\n      atomic.set(true).should be_true\n      atomic.get.should be_true\n    end\n\n    it \"with integer\" do\n      atomic = Atomic.new(1)\n      atomic.set(2).should eq(2)\n      atomic.get.should eq(2)\n    end\n\n    it \"with pointer type\" do\n      atomic = Atomic.new(Pointer(Void).new(1))\n      atomic.set(Pointer(Void).new(3)).should eq(Pointer(Void).new(3))\n      atomic.get.should eq(Pointer(Void).new(3))\n    end\n\n    it \"with nil (#4062)\" do\n      atomic = Atomic(String?).new(nil)\n\n      atomic.set(\"foo\")\n      atomic.get.should eq(\"foo\")\n\n      atomic.set(nil)\n      atomic.get.should be_nil\n    end\n\n    it \"explicit ordering\" do\n      atomic = Atomic.new(1)\n      atomic.set(0, :release).should eq(0)\n      atomic.get(:acquire).should eq(0)\n    end\n  end\n\n  it \"#lazy_set\" do\n    atomic = Atomic.new(1)\n    atomic.lazy_set(2).should eq(2)\n    atomic.lazy_get.should eq(2)\n\n    bool = Atomic.new(true)\n    bool.lazy_set(false).should be_false\n    bool.lazy_get.should be_false\n  end\n\n  describe \"#swap\" do\n    it \"with bool\" do\n      atomic = Atomic.new(true)\n      atomic.swap(false).should be_true\n      atomic.get.should be_false\n    end\n\n    it \"with integer\" do\n      atomic = Atomic.new(1)\n      atomic.swap(2).should eq(1)\n      atomic.get.should eq(2)\n    end\n\n    it \"with pointer type\" do\n      atomic = Atomic.new(Pointer(Void).new(1))\n      atomic.swap(Pointer(Void).new(3)).should eq(Pointer(Void).new(1))\n      atomic.get.should eq(Pointer(Void).new(3))\n    end\n\n    it \"with reference type\" do\n      atomic = Atomic.new(\"hello\")\n      atomic.swap(\"world\").should eq(\"hello\")\n      atomic.get.should eq(\"world\")\n    end\n\n    it \"with nilable reference\" do\n      atomic = Atomic(String?).new(nil)\n\n      atomic.swap(\"not nil\").should be_nil\n      atomic.get.should eq(\"not nil\")\n\n      atomic.swap(nil).should eq(\"not nil\")\n      atomic.get.should be_nil\n    end\n\n    it \"with reference union\" do\n      arr1 = [1]\n      arr2 = [\"\"]\n      atomic = Atomic(Array(Int32) | Array(String)).new(arr1)\n\n      atomic.swap(arr2).should be(arr1)\n      atomic.get.should be(arr2)\n\n      atomic.swap(arr1).should be(arr2)\n      atomic.get.should be(arr1)\n    end\n\n    it \"explicit ordering\" do\n      atomic = Atomic.new(1)\n      atomic.swap(2, :acquire).should eq(1)\n      atomic.get.should eq(2)\n    end\n  end\n\n  describe \"atomic bool\" do\n    it \"sizeof\" do\n      sizeof(Atomic(Bool)).should eq(1)\n      sizeof(AtomicBooleans).should eq(3)\n    end\n\n    it \"gets and sets\" do\n      booleans = AtomicBooleans.new\n\n      booleans.@one.get.should be_false\n      booleans.@two.get.should be_false\n      booleans.@three.get.should be_false\n\n      booleans.@two.set(true)\n      booleans.@one.get.should be_false\n      booleans.@two.get.should be_true\n      booleans.@three.get.should be_false\n\n      booleans.@one.set(true)\n      booleans.@three.set(true)\n      booleans.@one.get.should be_true\n      booleans.@two.get.should be_true\n      booleans.@three.get.should be_true\n\n      booleans.@one.set(false)\n      booleans.@three.set(false)\n      booleans.@one.get.should be_false\n      booleans.@two.get.should be_true\n      booleans.@three.get.should be_false\n\n      booleans.@two.set(false)\n      booleans.@one.get.should be_false\n      booleans.@two.get.should be_false\n      booleans.@three.get.should be_false\n    end\n  end\nend\n\ndescribe Atomic::Flag do\n  it \"#test_and_set\" do\n    flag = Atomic::Flag.new\n    flag.test_and_set.should be_true\n    flag.test_and_set.should be_false\n  end\n\n  it \"#clear\" do\n    flag = Atomic::Flag.new\n    flag.test_and_set.should be_true\n    flag.clear\n    flag.test_and_set.should be_true\n  end\nend\n"
  },
  {
    "path": "spec/std/base64_spec.cr",
    "content": "require \"spec\"\nrequire \"base64\"\nrequire \"crystal/digest/md5\"\nrequire \"spec/helpers/string\"\n\n# rearrange parameters for `assert_prints`\n{% for method in %w(encode strict_encode urlsafe_encode) %}\n  private def base64_{{ method.id }}(io : IO, data, *args)\n    Base64.{{ method.id }}(data, io, *args)\n  end\n\n  private def base64_{{ method.id }}(data, *args)\n    Base64.{{ method.id }}(data, *args)\n  end\n{% end %}\n\ndescribe \"Base64\" do\n  context \"simple test\" do\n    eqs = {\"\" => \"\", \"a\" => \"YQ==\\n\", \"ab\" => \"YWI=\\n\", \"abc\" => \"YWJj\\n\",\n           \"abcd\" => \"YWJjZA==\\n\", \"abcde\" => \"YWJjZGU=\\n\", \"abcdef\" => \"YWJjZGVm\\n\",\n           \"abcdefg\" => \"YWJjZGVmZw==\\n\"}\n    eqs.each do |a, b|\n      it \"encode #{a.inspect} to #{b.inspect}\" do\n        assert_prints base64_encode(a), b\n      end\n      it \"decode from #{b.inspect} to #{a.inspect}\" do\n        Base64.decode(b).should eq(a.to_slice)\n        Base64.decode_string(b).should eq(a)\n      end\n    end\n  end\n\n  context \"\\n in multiple places\" do\n    eqs = {\"abcd\" => \"YWJj\\nZA==\\n\", \"abcde\" => \"YWJj\\nZGU=\\n\", \"abcdef\" => \"YWJj\\nZGVm\\n\",\n           \"abcdefg\" => \"YWJj\\nZGVmZw==\\n\",\n    }\n    eqs.each do |a, b|\n      it \"decode from #{b.inspect} to #{a.inspect}\" do\n        Base64.decode(b).should eq(a.to_slice)\n        Base64.decode_string(b).should eq(a)\n      end\n    end\n  end\n\n  it \"encodes byte slice\" do\n    slice = Bytes.new(5) { 1_u8 }\n    assert_prints base64_encode(slice), \"AQEBAQE=\\n\"\n    assert_prints base64_strict_encode(slice), \"AQEBAQE=\"\n  end\n\n  it \"encodes empty slice\" do\n    slice = Bytes.empty\n    assert_prints base64_encode(slice), \"\"\n    assert_prints base64_strict_encode(slice), \"\"\n  end\n\n  it \"encodes static array\" do\n    array = uninitialized StaticArray(UInt8, 5)\n    (0...5).each { |i| array[i] = 1_u8 }\n    assert_prints base64_encode(array), \"AQEBAQE=\\n\"\n    assert_prints base64_strict_encode(array), \"AQEBAQE=\"\n  end\n\n  describe \"base\" do\n    eqs = {\"Send reinforcements\"                                                    => \"U2VuZCByZWluZm9yY2VtZW50cw==\\n\",\n           \"Now is the time for all good coders\\nto learn Crystal\"                  => \"Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\\nQ3J5c3RhbA==\\n\",\n           \"This is line one\\nThis is line two\\nThis is line three\\nAnd so on...\\n\" => \"VGhpcyBpcyBsaW5lIG9uZQpUaGlzIGlzIGxpbmUgdHdvClRoaXMgaXMgbGlu\\nZSB0aHJlZQpBbmQgc28gb24uLi4K\\n\",\n           \"hahah⊙ⓧ⊙\"                                                               => \"aGFoYWjiipnik6fiipk=\\n\"}\n    eqs.each do |a, b|\n      it \"encode #{a.inspect} to #{b.inspect}\" do\n        assert_prints base64_encode(a), b\n      end\n      it \"decode from #{b.inspect} to #{a.inspect}\" do\n        Base64.decode(b).should eq(a.to_slice)\n        Base64.decode_string(b).should eq(a)\n      end\n    end\n\n    it \"decode from strict form\" do\n      Base64.decode_string(\"Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4gQ3J5c3RhbA==\").should eq(\n        \"Now is the time for all good coders\\nto learn Crystal\")\n    end\n\n    it \"encode to stream returns number of written characters\" do\n      io = IO::Memory.new\n      count = Base64.encode(\"Now is the time for all good coders\\nto learn Crystal\", io)\n      count.should eq 74\n    end\n\n    it \"decode from stream returns number of written bytes\" do\n      io = IO::Memory.new\n      count = Base64.decode(\"Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4gQ3J5c3RhbA==\", io)\n      count.should eq 52\n    end\n\n    it \"big message\" do\n      a = \"a\" * 100000\n      b = Base64.encode(a)\n      Crystal::Digest::MD5.hexdigest(Base64.decode_string(b)).should eq(Crystal::Digest::MD5.hexdigest(a))\n    end\n\n    it \"works for most characters\" do\n      a = String.build(65536 * 4) do |buf|\n        65536.times { |i| buf << (i + 1).unsafe_chr }\n      end\n      b = Base64.encode(a)\n      Crystal::Digest::MD5.hexdigest(Base64.decode_string(b)).should eq(Crystal::Digest::MD5.hexdigest(a))\n    end\n  end\n\n  describe \"decode cases\" do\n    it \"decode \\r\\n\" do\n      decoded = \"hahah⊙ⓧ⊙\"\n      {\"aGFo\\r\\nYWjiipnik6fiipk=\\r\\n\", \"aGFo\\r\\nYWjiipnik6fiipk=\\r\\n\\r\\n\"}.each do |encoded|\n        Base64.decode(encoded).should eq(decoded.to_slice)\n        Base64.decode_string(encoded).should eq(decoded)\n      end\n    end\n\n    it \"decode \\n in multiple places\" do\n      decoded = \"hahah⊙ⓧ⊙\"\n      {\"aGFoYWjiipnik6fiipk=\", \"aGFo\\nYWjiipnik6fiipk=\", \"aGFo\\nYWji\\nipnik6fiipk=\",\n       \"aGFo\\nYWji\\nipni\\nk6fiipk=\", \"aGFo\\nYWji\\nipni\\nk6fi\\nipk=\",\n       \"aGFo\\nYWji\\nipni\\nk6fi\\nipk=\\n\"}.each do |encoded|\n        Base64.decode(encoded).should eq(decoded.to_slice)\n        Base64.decode_string(encoded).should eq(decoded)\n      end\n    end\n\n    it \"raise error when \\n in incorrect place\" do\n      expect_raises Base64::Error do\n        Base64.decode(\"aG\\nFoYWjiipnik6fiipk=\")\n      end\n\n      expect_raises Base64::Error do\n        Base64.decode_string(\"aG\\nFoYWjiipnik6fiipk=\")\n      end\n    end\n\n    it \"raise error when incorrect symbol\" do\n      expect_raises Base64::Error do\n        Base64.decode(\"()\")\n      end\n\n      expect_raises Base64::Error do\n        Base64.decode_string(\"()\")\n      end\n    end\n\n    it \"raise error when incorrect size\" do\n      expect_raises Base64::Error do\n        Base64.decode(\"a\")\n      end\n\n      expect_raises Base64::Error do\n        Base64.decode_string(\"a\")\n      end\n    end\n\n    it \"decode small tail after last \\n, was a bug\" do\n      s = \"Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\\nnA==\\n\"\n      Base64.decode(s).should eq Bytes[78, 111, 119, 32, 105, 115, 32, 116, 104, 101, 32, 116, 105, 109, 101, 32, 102, 111, 114, 32, 97, 108, 108, 32, 103, 111, 111, 100, 32, 99, 111, 100, 101, 114, 115, 10, 116, 111, 32, 108, 101, 97, 114, 110, 32, 156]\n    end\n  end\n\n  describe \"strict\" do\n    it \"encode\" do\n      assert_prints base64_strict_encode(\"Now is the time for all good coders\\nto learn Crystal\"),\n        \"Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4gQ3J5c3RhbA==\"\n    end\n    it \"with spec symbols\" do\n      s = String.build { |b| (160..179).each { |i| b << i.chr } }\n      se = \"wqDCocKiwqPCpMKlwqbCp8KowqnCqsKrwqzCrcKuwq/CsMKxwrLCsw==\"\n      assert_prints base64_strict_encode(s), se\n    end\n\n    it \"encode to stream returns number of written characters\" do\n      s = String.build { |b| (160..179).each { |i| b << i.chr } }\n      io = IO::Memory.new\n      Base64.strict_encode(s, io).should eq(56)\n    end\n  end\n\n  describe \"urlsafe\" do\n    it \"work\" do\n      s = String.build { |b| (160..179).each { |i| b << i.chr } }\n      se = \"wqDCocKiwqPCpMKlwqbCp8KowqnCqsKrwqzCrcKuwq_CsMKxwrLCsw==\"\n      assert_prints base64_urlsafe_encode(s), se\n    end\n\n    it \"encode to stream returns number of written characters\" do\n      s = String.build { |b| (160..179).each { |i| b << i.chr } }\n      io = IO::Memory.new\n      Base64.urlsafe_encode(s, io).should eq(56)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/benchmark_spec.cr",
    "content": "require \"spec\"\nrequire \"benchmark\"\n\n# Make sure this compiles (#2578)\ntypeof(begin\n  Benchmark.bm do |b|\n    b.report(\"Here\") { puts \"Yes\" }\n  end\nend)\n\ndescribe Benchmark::IPS::Job do\n  it \"works in general / integration test\" do\n    # test several things to avoid running a benchmark over and over again in\n    # the specs\n    j = Benchmark::IPS::Job.new(1.millisecond, 1.millisecond, interactive: false)\n    a = j.report(\"a\") { sleep 1.milliseconds }\n    b = j.report(\"b\") { sleep 2.milliseconds }\n\n    j.execute\n\n    # the mean should be calculated\n    a.mean.should be > 10\n\n    # one of the reports should be normalized to the fastest but do to the\n    # timer precision sleep 0.001 may not always be faster than 0.002 so we\n    # don't care which\n    first, second = [a.slower, b.slower].sort\n    first.should eq(1)\n    second.should be > 1\n  end\nend\n\nprivate def create_entry\n  Benchmark::IPS::Entry.new(\"label\", -> { 1 + 1 })\nend\n\nprivate def h_mean(mean)\n  create_entry.tap { |e| e.mean = mean }.human_mean\nend\n\nprivate def h_ips(seconds)\n  mean = 1.0 / seconds\n  create_entry.tap { |e| e.mean = mean }.human_iteration_time\nend\n\ndescribe Benchmark::IPS::Entry do\n  describe \"#set_cycles\" do\n    it \"sets the number of cycles needed to make 100ms\" do\n      e = create_entry\n      e.set_cycles(2.seconds, 100)\n      e.cycles.should eq(5)\n\n      e.set_cycles(100.milliseconds, 1)\n      e.cycles.should eq(1)\n    end\n\n    it \"sets the cycles to 1 no matter what\" do\n      e = create_entry\n      e.set_cycles(2.seconds, 1)\n      e.cycles.should eq(1)\n    end\n  end\n\n  describe \"#calculate_stats\" do\n    it \"correctly calculates basic stats\" do\n      e = create_entry\n      e.calculate_stats([2, 4, 4, 4, 5, 5, 7, 9])\n\n      e.size.should eq(8)\n      e.mean.should eq(5.0)\n      e.variance.should eq(4.0)\n      e.stddev.should eq(2.0)\n    end\n  end\n\n  describe \"#human_mean\" do\n    it { h_mean(0.01234567890123).should eq(\" 12.35m\") }\n    it { h_mean(0.12345678901234).should eq(\"123.46m\") }\n\n    it { h_mean(1.23456789012345).should eq(\"  1.23 \") }\n    it { h_mean(12.3456789012345).should eq(\" 12.35 \") }\n    it { h_mean(123.456789012345).should eq(\"123.46 \") }\n\n    it { h_mean(1234.56789012345).should eq(\"  1.23k\") }\n    it { h_mean(12345.6789012345).should eq(\" 12.35k\") }\n    it { h_mean(123456.789012345).should eq(\"123.46k\") }\n\n    it { h_mean(1234567.89012345).should eq(\"  1.23M\") }\n    it { h_mean(12345678.9012345).should eq(\" 12.35M\") }\n    it { h_mean(123456789.012345).should eq(\"123.46M\") }\n\n    it { h_mean(1234567890.12345).should eq(\"  1.23G\") }\n    it { h_mean(12345678901.2345).should eq(\" 12.35G\") }\n    it { h_mean(123456789012.345).should eq(\"123.46G\") }\n  end\n\n  describe \"#human_iteration_time\" do\n    it { h_ips(1234.567_890_123).should eq(\"1,234.57s \") }\n    it { h_ips(123.456_789_012_3).should eq(\"123.46s \") }\n    it { h_ips(12.345_678_901_23).should eq(\" 12.35s \") }\n    it { h_ips(1.234_567_890_123).should eq(\"  1.23s \") }\n\n    it { h_ips(0.123_456_789_012).should eq(\"123.46ms\") }\n    it { h_ips(0.012_345_678_901).should eq(\" 12.35ms\") }\n    it { h_ips(0.001_234_567_890).should eq(\"  1.23ms\") }\n\n    it { h_ips(0.000_123_456_789).should eq(\"123.46µs\") }\n    it { h_ips(0.000_012_345_678).should eq(\" 12.35µs\") }\n    it { h_ips(0.000_001_234_567).should eq(\"  1.23µs\") }\n\n    it { h_ips(0.000_000_123_456).should eq(\"123.46ns\") }\n    it { h_ips(0.000_000_012_345).should eq(\" 12.34ns\") }\n    it { h_ips(0.000_000_001_234).should eq(\"  1.23ns\") }\n    it { h_ips(0.000_000_000_123).should eq(\"  0.12ns\") }\n  end\nend\n"
  },
  {
    "path": "spec/std/big/big_decimal_spec.cr",
    "content": "require \"spec\"\nrequire \"big\"\nrequire \"spec/helpers/string\"\n\ndescribe BigDecimal do\n  it \"initializes from valid input\" do\n    BigDecimal.new\n      .should eq(BigDecimal.new(BigInt.new(0)))\n\n    BigDecimal.new(\"41.0123\")\n      .should eq(BigDecimal.new(BigInt.new(410123), 4))\n\n    BigDecimal.new(1)\n      .should eq(BigDecimal.new(BigInt.new(1)))\n\n    BigDecimal.new(-1)\n      .should eq(BigDecimal.new(BigInt.new(-1)))\n\n    BigDecimal.new(0)\n      .should eq(BigDecimal.new(BigInt.new(0)))\n\n    BigDecimal.new(\"42.0123\")\n      .should eq(BigDecimal.new(BigInt.new(420123), 4))\n\n    BigDecimal.new(\"42_42_42_24.0123_456_789\")\n      .should eq(BigDecimal.new(BigInt.new(424242240123456789), 10))\n\n    BigDecimal.new(\"0.0\")\n      .should eq(BigDecimal.new(BigInt.new(0)))\n\n    BigDecimal.new(\".2\")\n      .should eq(BigDecimal.new(BigInt.new(2), 1))\n\n    BigDecimal.new(\"2.\")\n      .should eq(BigDecimal.new(BigInt.new(2)))\n\n    BigDecimal.new(\"-.2\")\n      .should eq(BigDecimal.new(BigInt.new(-2), 1))\n\n    BigDecimal.new(\"-2.\")\n      .should eq(BigDecimal.new(BigInt.new(-2)))\n\n    BigDecimal.new(\"-0.1\")\n      .should eq(BigDecimal.new(BigInt.new(-1), 1))\n\n    BigDecimal.new(\"-1.1\")\n      .should eq(BigDecimal.new(BigInt.new(-11), 1))\n\n    BigDecimal.new(\"123871293879123790874230984702938470917238971298379127390182739812739817239087123918273098.1029387192083710928371092837019283701982370918237\")\n      .should eq(BigDecimal.new(BigInt.new(\"1238712938791237908742309847029384709172389712983791273901827398127398172390871239182730981029387192083710928371092837019283701982370918237\".to_big_i), 49))\n\n    BigDecimal.new(\"-123871293879123790874230984702938470917238971298379127390182739812739817239087123918273098.1029387192083710928371092837019283701982370918237\")\n      .should eq(BigDecimal.new(BigInt.new(\"-1238712938791237908742309847029384709172389712983791273901827398127398172390871239182730981029387192083710928371092837019283701982370918237\".to_big_i), 49))\n\n    BigDecimal.new(\"-0.1029387192083710928371092837019283701982370918237\")\n      .should eq(BigDecimal.new(BigInt.new(\"-1029387192083710928371092837019283701982370918237\".to_big_i), 49))\n\n    BigDecimal.new(\"2\")\n      .should eq(BigDecimal.new(BigInt.new(2)))\n\n    BigDecimal.new(\"-1\")\n      .should eq(BigDecimal.new(BigInt.new(-1)))\n\n    BigDecimal.new(\"0\")\n      .should eq(BigDecimal.new(BigInt.new(0)))\n\n    BigDecimal.new(\"-0\")\n      .should eq(BigDecimal.new(BigInt.new(0)))\n\n    BigDecimal.new(BigDecimal.new(2))\n      .should eq(BigDecimal.new(2.to_big_i))\n\n    BigDecimal.new(BigRational.new(1, 2))\n      .should eq(BigDecimal.new(BigInt.new(5), 1))\n  end\n\n  it \"raises InvalidBigDecimalException when initializing from invalid input\" do\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"derp\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1.2.3\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"..2\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1..2\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"a1.2\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1a.2\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1.a2\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1.2a\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1ee1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"e+e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1e1e\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1 e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"..e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"-..e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"e+5\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\".e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\".e+1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"-.e1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1e.\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1e0.1\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1e+\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1.1e-\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"-\")\n    end\n\n    expect_raises(InvalidBigDecimalException) do\n      BigDecimal.new(\"1.0e\")\n    end\n  end\n\n  it \"raises if creating from infinity\" do\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigDecimal.new(Float32::INFINITY) }\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigDecimal.new(Float64::INFINITY) }\n  end\n\n  it \"raises if creating from NaN\" do\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigDecimal.new(Float32::NAN) }\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigDecimal.new(Float64::NAN) }\n  end\n\n  it \"performs arithmetic with bigdecimals\" do\n    BigDecimal.new(0).should eq(BigDecimal.new(0) + BigDecimal.new(0))\n    BigDecimal.new(1).should eq(BigDecimal.new(0) + BigDecimal.new(1))\n    BigDecimal.new(1).should eq(BigDecimal.new(1) + BigDecimal.new(0))\n    BigDecimal.new(0).should eq(BigDecimal.new(1) + BigDecimal.new(-1))\n    BigDecimal.new(0).should eq(BigDecimal.new(-1) + BigDecimal.new(1))\n    BigDecimal.new(\"0.1\").should eq(BigDecimal.new(\"-1.1\") + BigDecimal.new(\"1.2\"))\n    BigDecimal.new(\"0.076543211\").should eq(BigDecimal.new(\"-1.123456789\") + BigDecimal.new(\"1.2\"))\n    BigDecimal.new(\"0.13456789\").should eq(BigDecimal.new(\"-1.1\") + BigDecimal.new(\"1.23456789\"))\n\n    BigDecimal.new(0).should eq(BigDecimal.new(0) - BigDecimal.new(0))\n    BigDecimal.new(-1).should eq(BigDecimal.new(0) - BigDecimal.new(1))\n    BigDecimal.new(1).should eq(BigDecimal.new(1) - BigDecimal.new(0))\n    BigDecimal.new(2).should eq(BigDecimal.new(1) - BigDecimal.new(-1))\n    BigDecimal.new(-2).should eq(BigDecimal.new(-1) - BigDecimal.new(1))\n    BigDecimal.new(1).should eq(BigDecimal.new(\"1.12345\") - BigDecimal.new(\"0.12345\"))\n    BigDecimal.new(\"1.0000067\").should eq(BigDecimal.new(\"1.1234567\") - BigDecimal.new(\"0.12345\"))\n    BigDecimal.new(\"0.9999933\").should eq(BigDecimal.new(\"1.12345\") - BigDecimal.new(\"0.1234567\"))\n\n    BigDecimal.new(0).should eq(BigDecimal.new(0) * BigDecimal.new(0))\n    BigDecimal.new(0).should eq(BigDecimal.new(0) * BigDecimal.new(1))\n    BigDecimal.new(0).should eq(BigDecimal.new(1) * BigDecimal.new(0))\n    BigDecimal.new(-1).should eq(BigDecimal.new(1) * BigDecimal.new(-1))\n    BigDecimal.new(-1).should eq(BigDecimal.new(-1) * BigDecimal.new(1))\n    BigDecimal.new(\"1.2621466432\").should eq(BigDecimal.new(\"1.12345\") * BigDecimal.new(\"1.123456\"))\n    BigDecimal.new(\"1.2621466432\").should eq(BigDecimal.new(\"1.123456\") * BigDecimal.new(\"1.12345\"))\n\n    expect_raises(DivisionByZeroError) do\n      BigDecimal.new(0) / BigDecimal.new(0)\n    end\n    expect_raises(DivisionByZeroError) do\n      BigDecimal.new(1) / BigDecimal.new(0)\n    end\n    expect_raises(DivisionByZeroError) do\n      BigDecimal.new(-1) / BigDecimal.new(0)\n    end\n\n    expect_raises(DivisionByZeroError) do\n      BigDecimal.new(0) // BigDecimal.new(0)\n    end\n    expect_raises(DivisionByZeroError) do\n      BigDecimal.new(1) // BigDecimal.new(0)\n    end\n    expect_raises(DivisionByZeroError) do\n      BigDecimal.new(-1) // BigDecimal.new(0)\n    end\n\n    BigDecimal.new(1).should eq(BigDecimal.new(1) / BigDecimal.new(1))\n    BigDecimal.new(10).should eq(BigDecimal.new(100, 1) / BigDecimal.new(100000000, 8))\n    BigDecimal.new(5.to_big_i, 1_u64).should eq(BigDecimal.new(1) / BigDecimal.new(2))\n    BigDecimal.new(-5.to_big_i, 1_u64).should eq(BigDecimal.new(1) / BigDecimal.new(-2))\n    BigDecimal.new(-5.to_big_i, 4_u64).should eq(BigDecimal.new(1) / BigDecimal.new(-2000))\n    BigDecimal.new(-500.to_big_i, 0).should eq(BigDecimal.new(1000) / BigDecimal.new(-2))\n    BigDecimal.new(-500.to_big_i, 0).should eq(BigDecimal.new(-1000) / BigDecimal.new(2))\n    BigDecimal.new(500.to_big_i, 0).should eq(BigDecimal.new(-1000) / BigDecimal.new(-2))\n    BigDecimal.new(5.to_big_i, 1_u64).should eq(BigDecimal.new(-1) / BigDecimal.new(-2))\n    BigDecimal.new(5.to_big_i, 4_u64).should eq(BigDecimal.new(-1) / BigDecimal.new(-2000))\n    BigDecimal.new(500.to_big_i, 0).should eq(BigDecimal.new(-1000) / BigDecimal.new(-2))\n    BigDecimal.new(0).should eq(BigDecimal.new(0) / BigDecimal.new(1))\n    BigDecimal.new(\"3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333\".to_big_i, 100_u64).should eq(BigDecimal.new(1) / BigDecimal.new(3))\n    BigDecimal.new(-2000).should eq(BigDecimal.new(-0.02) / (BigDecimal.new(0.00001)))\n\n    BigDecimal.new(0).should eq(BigDecimal.new(1) // BigDecimal.new(2))\n    BigDecimal.new(-1).should eq(BigDecimal.new(1) // BigDecimal.new(-2))\n    BigDecimal.new(-1).should eq(BigDecimal.new(1) // BigDecimal.new(-2000))\n    BigDecimal.new(-500).should eq(BigDecimal.new(1000) // BigDecimal.new(-2))\n    BigDecimal.new(-500).should eq(BigDecimal.new(-1000) // BigDecimal.new(2))\n    BigDecimal.new(500).should eq(BigDecimal.new(-1000) // BigDecimal.new(-2))\n    BigDecimal.new(0).should eq(BigDecimal.new(-1) // BigDecimal.new(-2))\n    BigDecimal.new(0).should eq(BigDecimal.new(-1) // BigDecimal.new(-2000))\n    BigDecimal.new(500).should eq(BigDecimal.new(-1000) // BigDecimal.new(-2))\n    BigDecimal.new(0).should eq(BigDecimal.new(0) // BigDecimal.new(1))\n    BigDecimal.new(0).should eq(BigDecimal.new(1) // BigDecimal.new(3))\n    BigDecimal.new(-2000).should eq(BigDecimal.new(-0.02) // (BigDecimal.new(0.00001)))\n\n    BigDecimal.new(33333.to_big_i, 5_u64).should eq(BigDecimal.new(1).div(BigDecimal.new(3), 5))\n    BigDecimal.new(33.to_big_i, 5_u64).should eq(BigDecimal.new(1).div(BigDecimal.new(3000), 5))\n\n    BigDecimal.new(3333333.to_big_i, 7_u64).should eq(BigDecimal.new(1).div(BigDecimal.new(3), 7))\n    BigDecimal.new(3333.to_big_i, 7_u64).should eq(BigDecimal.new(1).div(BigDecimal.new(3000), 7))\n\n    (-BigDecimal.new(3)).should eq(BigDecimal.new(-3))\n\n    (BigDecimal.new(5) % BigDecimal.new(2)).should eq(BigDecimal.new(1))\n    (BigDecimal.new(500) % BigDecimal.new(2)).should eq(BigDecimal.new(0))\n    (BigDecimal.new(500) % BigDecimal.new(2000)).should eq(BigDecimal.new(500))\n  end\n\n  it \"handles modulus correctly\" do\n    (BigDecimal.new(13.0) % BigDecimal.new(4.0)).should eq(BigDecimal.new(1.0))\n    (BigDecimal.new(13.0) % BigDecimal.new(-4.0)).should eq(BigDecimal.new(-3.0))\n    (BigDecimal.new(-13.0) % BigDecimal.new(4.0)).should eq(BigDecimal.new(3.0))\n    (BigDecimal.new(-13.0) % BigDecimal.new(-4.0)).should eq(BigDecimal.new(-1.0))\n    (BigDecimal.new(11.5) % BigDecimal.new(4.0)).should eq(BigDecimal.new(3.5))\n    (BigDecimal.new(11.5) % BigDecimal.new(-4.0)).should eq(BigDecimal.new(-0.5))\n    (BigDecimal.new(-11.5) % BigDecimal.new(4.0)).should eq(BigDecimal.new(0.5))\n    (BigDecimal.new(-11.5) % BigDecimal.new(-4.0)).should eq(BigDecimal.new(-3.5))\n  end\n\n  it \"performs arithmetic with other number types\" do\n    (1.to_big_d + 2).should eq(BigDecimal.new(\"3.0\"))\n    (2 + 1.to_big_d).should eq(BigDecimal.new(\"3.0\"))\n    (2.to_big_d - 1).should eq(BigDecimal.new(\"1.0\"))\n    (2 - 1.to_big_d).should eq(BigDecimal.new(\"1.0\"))\n    (1.to_big_d * 2).should eq(BigDecimal.new(\"2.0\"))\n    (1 * 2.to_big_d).should eq(BigDecimal.new(\"2.0\"))\n    (3.to_big_d / 2).should eq(BigDecimal.new(\"1.5\"))\n    (3 / 2.to_big_d).should eq(BigDecimal.new(\"1.5\"))\n  end\n\n  it \"exponentiates\" do\n    result = \"12.34\".to_big_d ** 5\n    result.should be_a(BigDecimal)\n    result.to_s.should eq(\"286138.1721051424\")\n  end\n\n  it \"exponentiates with negative powers\" do\n    result = \"2.0\".to_big_d ** -1\n    result.should be_a(BigDecimal)\n    result.to_s.should eq(\"0.5\")\n  end\n\n  it \"can be converted from other types\" do\n    1.to_big_d.should eq(BigDecimal.new(1))\n    \"1.5\".to_big_d.should eq(BigDecimal.new(15, 1))\n    \"+1.5\".to_big_d.should eq(BigDecimal.new(15, 1))\n    BigInt.new(15).to_big_d.should eq(BigDecimal.new(15, 0))\n    1.5.to_big_d.should eq(BigDecimal.new(15, 1))\n    1.5.to_big_f.to_big_d.should eq(BigDecimal.new(15, 1))\n    1.5.to_big_r.to_big_d.should eq(BigDecimal.new(15, 1))\n  end\n\n  it \"can be converted from scientific notation\" do\n    \"10.01e1\".to_big_d.should eq(BigDecimal.new(\"100.1\"))\n    \"10.01e-1\".to_big_d.should eq(BigDecimal.new(\"1.001\"))\n    \"6.033e2\".to_big_d.should eq(BigDecimal.new(\"603.3\"))\n    \"603.3e-2\".to_big_d.should eq(BigDecimal.new(\"6.033\"))\n    \"-0.123e12\".to_big_d.should eq(BigDecimal.new(\"-123000000000\"))\n    \"0.123e12\".to_big_d.should eq(BigDecimal.new(\"123000000000\"))\n    \"0.123e+12\".to_big_d.should eq(BigDecimal.new(\"123000000000\"))\n    \"-0.123e-7\".to_big_d.should eq(BigDecimal.new(\"-0.0000000123\"))\n    \"-0.1e-7\".to_big_d.should eq(BigDecimal.new(\"-0.00000001\"))\n    \"0.1e-7\".to_big_d.should eq(BigDecimal.new(\"0.00000001\"))\n    \"1.0e-8\".to_big_d.should eq(BigDecimal.new(\"0.00000001\"))\n    \"10e-8\".to_big_d.should eq(BigDecimal.new(\"0.0000001\"))\n    \"1.0e+8\".to_big_d.should eq(BigDecimal.new(\"100000000\"))\n    \"10e+8\".to_big_d.should eq(BigDecimal.new(\"1000000000\"))\n    \"10E+8\".to_big_d.should eq(BigDecimal.new(\"1000000000\"))\n    \"10E8\".to_big_d.should eq(BigDecimal.new(\"1000000000\"))\n  end\n\n  it \"is comparable with other types\" do\n    BigDecimal.new(\"1.0\").should eq BigDecimal.new(\"1\")\n    BigDecimal.new(\"1\").should eq BigDecimal.new(\"1.0\")\n    1.should_not eq BigDecimal.new(\"-1.0\")\n    1.should_not eq BigDecimal.new(\"0.1\")\n    BigDecimal.new(1, 10).should eq BigDecimal.new(10, 11)\n    BigDecimal.new(10, 11).should eq BigDecimal.new(1, 10)\n\n    (BigDecimal.new(1) > BigDecimal.new(1)).should be_false\n    (BigDecimal.new(\"1.00000000000000000000000000000000000001\") > BigDecimal.new(1)).should be_true\n    (BigDecimal.new(\"0.99999999999999999999999999999999999999\") > BigDecimal.new(1)).should be_false\n    BigDecimal.new(\"1.00000000000000000000000000000000000000\").should eq BigDecimal.new(1)\n\n    (1 < BigDecimal.new(1)).should be_false\n    (BigDecimal.new(1) < 1).should be_false\n    (2 < BigDecimal.new(1)).should be_false\n    (BigDecimal.new(2) < 1).should be_false\n    (BigDecimal.new(\"-1\") > BigDecimal.new(\"1\")).should be_false\n\n    (1 > BigDecimal.new(1)).should be_false\n    (BigDecimal.new(1) > 1).should be_false\n    (2 > BigDecimal.new(1)).should be_true\n    (BigDecimal.new(2) > 1).should be_true\n    (BigDecimal.new(\"-1\") < BigDecimal.new(\"1\")).should be_true\n\n    (1 >= BigDecimal.new(1)).should be_true\n    (2 >= BigDecimal.new(1)).should be_true\n\n    (1 <= BigDecimal.new(1)).should be_true\n    (0 <= BigDecimal.new(1)).should be_true\n\n    (BigDecimal.new(\"6.5\") < 6.6).should be_true\n    (6.6 > BigDecimal.new(\"6.5\")).should be_true\n    (BigDecimal.new(\"7.5\") > 6.6).should be_true\n    (6.6 < BigDecimal.new(\"7.5\")).should be_true\n\n    \"1.0000000000000002\".to_big_d.should be < 1.0.next_float\n    (1.0.to_big_d + 0.5.to_big_d ** 52).should eq(1.0.next_float)\n    \"1.0000000000000003\".to_big_d.should be > 1.0.next_float\n\n    1.0.next_float.should be > \"1.0000000000000002\".to_big_d\n    1.0.next_float.should eq(1.0.to_big_d + 0.5.to_big_d ** 52)\n    1.0.next_float.should be < \"1.0000000000000003\".to_big_d\n\n    0.to_big_d.should be < Float64::INFINITY\n    (Float64::MAX.to_big_d ** 7).should be < Float64::INFINITY\n    0.to_big_d.should be > -Float64::INFINITY\n    (Float64::MIN.to_big_d ** 7).should be > -Float64::INFINITY\n\n    Float64::INFINITY.should be > 0.to_big_d\n    Float64::INFINITY.should be > (Float64::MAX.to_big_d ** 7)\n    (-Float64::INFINITY).should be < 0.to_big_d\n    (-Float64::INFINITY).should be < (Float64::MIN.to_big_d ** 7)\n\n    (BigDecimal.new(\"6.5\") > 7).should be_false\n    (BigDecimal.new(\"7.5\") > 6).should be_true\n\n    BigDecimal.new(\"0.5\").should eq(BigRational.new(1, 2))\n    BigDecimal.new(\"0.25\").should eq(BigDecimal.new(\"0.25\"))\n\n    BigRational.new(1, 2).should eq(BigDecimal.new(\"0.5\"))\n    BigRational.new(1, 4).should eq(BigDecimal.new(\"0.25\"))\n\n    (1.to_big_d / 3).should be < BigRational.new(1, 3)\n    (-(1.to_big_d / 3)).should be > BigRational.new(-1, 3)\n    (-1.to_big_d / 3).should be < BigRational.new(-1, 3)\n\n    BigRational.new(1, 3).should be > 1.to_big_d / 3\n    BigRational.new(-1, 3).should be < -(1.to_big_d / 3)\n    BigRational.new(-1, 3).should be > -1.to_big_d / 3\n\n    (1.to_big_d / 3 + BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION)).should be > BigRational.new(1, 3)\n    (-(1.to_big_d / 3) - BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION)).should be < BigRational.new(-1, 3)\n\n    BigRational.new(1, 3).should be < (1.to_big_d / 3 + BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION))\n    BigRational.new(-1, 3).should be > (-(1.to_big_d / 3) - BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION))\n\n    (0.5.to_big_d ** 10000).should eq(0.5.to_big_f ** 10000)\n    \"5.0123727492064520093e-3011\".to_big_d.should be > 0.5.to_big_f ** 10000\n\n    (0.5.to_big_f ** 10000).should eq(0.5.to_big_d ** 10000)\n    (0.5.to_big_f ** 10000).should be < \"5.0123727492064520093e-3011\".to_big_d\n  end\n\n  describe \"#<=>\" do\n    it \"compares against NaNs\" do\n      (1.to_big_d <=> Float64::NAN).should be_nil\n      (1.to_big_d <=> Float32::NAN).should be_nil\n      (Float64::NAN <=> 1.to_big_d).should be_nil\n      (Float32::NAN <=> 1.to_big_d).should be_nil\n\n      typeof(1.to_big_d <=> Float64::NAN).should eq(Int32?)\n      typeof(1.to_big_d <=> Float32::NAN).should eq(Int32?)\n      typeof(Float64::NAN <=> 1.to_big_d).should eq(Int32?)\n      typeof(Float32::NAN <=> 1.to_big_d).should eq(Int32?)\n    end\n  end\n\n  it \"keeps precision\" do\n    one_thousandth = BigDecimal.new(\"0.001\")\n    one = BigDecimal.new(\"1\")\n\n    x = BigDecimal.new\n    1000.times do\n      x += one_thousandth\n    end\n    one.should eq(x)\n\n    x = BigDecimal.new(\"2\")\n    1000.times do\n      x -= one_thousandth\n    end\n    one.should eq(x)\n  end\n\n  it \"converts to string\" do\n    assert_prints BigDecimal.new.to_s, \"0.0\"\n    assert_prints BigDecimal.new(0).to_s, \"0.0\"\n    assert_prints BigDecimal.new(1).to_s, \"1.0\"\n    assert_prints BigDecimal.new(-1).to_s, \"-1.0\"\n    assert_prints BigDecimal.new(\"8.5\").to_s, \"8.5\"\n    assert_prints BigDecimal.new(\"-0.35\").to_s, \"-0.35\"\n    assert_prints BigDecimal.new(\"-.35\").to_s, \"-0.35\"\n    assert_prints BigDecimal.new(\"0.01\").to_s, \"0.01\"\n    assert_prints BigDecimal.new(\"-0.01\").to_s, \"-0.01\"\n    assert_prints BigDecimal.new(\"0.00123\").to_s, \"0.00123\"\n    assert_prints BigDecimal.new(\"-0.00123\").to_s, \"-0.00123\"\n    assert_prints BigDecimal.new(\"1.0\").to_s, \"1.0\"\n    assert_prints BigDecimal.new(\"-1.0\").to_s, \"-1.0\"\n    assert_prints BigDecimal.new(\"1.000\").to_s, \"1.0\"\n    assert_prints BigDecimal.new(\"-1.000\").to_s, \"-1.0\"\n    assert_prints BigDecimal.new(\"1.0001\").to_s, \"1.0001\"\n    assert_prints BigDecimal.new(\"-1.0001\").to_s, \"-1.0001\"\n\n    assert_prints BigDecimal.new(1).div(BigDecimal.new(3), 9).to_s, \"0.333333333\"\n    assert_prints BigDecimal.new(1000).div(BigDecimal.new(3000), 9).to_s, \"0.333333333\"\n    assert_prints BigDecimal.new(1).div(BigDecimal.new(3000), 9).to_s, \"0.000333333\"\n\n    assert_prints BigDecimal.new(\"112839719283\").div(BigDecimal.new(\"3123779\"), 9).to_s, \"36122.824080384\"\n    assert_prints BigDecimal.new(\"112839719283\").div(BigDecimal.new(\"3123779\"), 14).to_s, \"36122.8240803846879\"\n    assert_prints BigDecimal.new(\"-0.4098\").div(BigDecimal.new(\"0.2229011193\"), 20).to_s, \"-1.83848336557007141059\"\n\n    assert_prints BigDecimal.new(1, 2).to_s, \"0.01\"\n    assert_prints BigDecimal.new(100, 4).to_s, \"0.01\"\n\n    assert_prints \"12345678901234567\".to_big_d.to_s, \"1.2345678901234567e+16\"\n    assert_prints \"1234567890123456789\".to_big_d.to_s, \"1.234567890123456789e+18\"\n\n    assert_prints BigDecimal.new(1_000_000_000_000_000_i64, 0).to_s, \"1.0e+15\"\n    assert_prints BigDecimal.new(100_000_000_000_000_i64, 0).to_s, \"100000000000000.0\"\n    assert_prints BigDecimal.new(1, 4).to_s, \"0.0001\"\n    assert_prints BigDecimal.new(1, 5).to_s, \"1.0e-5\"\n\n    assert_prints \"1.23e45\".to_big_d.to_s, \"1.23e+45\"\n    assert_prints \"1e-234\".to_big_d.to_s, \"1.0e-234\"\n  end\n\n  it \"converts to other number types\" do\n    bd1 = BigDecimal.new(123, 5)\n    bd2 = BigDecimal.new(-123, 5)\n    bd3 = BigDecimal.new(123, 0)\n    bd4 = BigDecimal.new(-123, 0)\n    bd5 = \"-123.000\".to_big_d\n    bd6 = \"-1.1\".to_big_d\n\n    bd1.to_i.should eq 0\n    bd2.to_i.should eq 0\n    bd3.to_i.should eq 123\n    bd4.to_i.should eq -123\n    bd5.to_i.should eq -123\n    bd6.to_i.should eq -1\n\n    bd1.to_u.should eq 0\n    expect_raises(OverflowError) { bd2.to_u }\n    bd3.to_u.should eq 123\n    expect_raises(OverflowError) { bd4.to_u }\n    expect_raises(OverflowError) { bd5.to_u }\n    expect_raises(OverflowError) { bd6.to_u }\n\n    bd1.to_f.should eq 0.00123\n    bd2.to_f.should eq -0.00123\n    bd3.to_f.should eq 123.0\n    bd4.to_f.should eq -123.0\n    bd5.to_f.should eq -123.0\n    bd6.to_f.should eq -1.1\n\n    bd1.to_i!.should eq 0\n    bd2.to_i!.should eq 0\n    bd3.to_i!.should eq 123\n    bd4.to_i!.should eq -123\n    bd5.to_i!.should eq -123\n    bd6.to_i!.should eq -1\n\n    bd1.to_u!.should eq 0\n    bd2.to_u!.should eq 0\n    bd3.to_u!.should eq 123\n    bd4.to_u!.should eq 123\n    bd5.to_u!.should eq 123\n    bd6.to_u!.should eq 1\n\n    bd1.to_f!.should eq 0.00123\n    bd2.to_f!.should eq -0.00123\n    bd3.to_f!.should eq 123.0\n    bd4.to_f!.should eq -123.0\n    bd5.to_f!.should eq -123.0\n    bd6.to_f!.should eq -1.1\n  end\n\n  it \"hashes\" do\n    bd1 = BigDecimal.new(\"123.456\")\n    bd2 = BigDecimal.new(\"0.12345\")\n    bd3 = BigDecimal.new(\"1.23456\")\n    bd4 = BigDecimal.new(\"-123456\")\n    bd5 = BigDecimal.new(\"0\")\n\n    hash = {} of BigDecimal => String\n    hash[bd1] = \"bd1\"\n    hash[bd2] = \"bd2\"\n    hash[bd3] = \"bd3\"\n    hash[bd4] = \"bd4\"\n    hash[bd5] = \"bd5\"\n\n    # regular cases\n    hash[BigDecimal.new(\"123.456\")].should eq \"bd1\"\n    hash[BigDecimal.new(\"0.12345\")].should eq \"bd2\"\n    hash[BigDecimal.new(\"1.23456\")].should eq \"bd3\"\n    hash[BigDecimal.new(\"-123456\")].should eq \"bd4\"\n    hash[BigDecimal.new(\"0\")].should eq \"bd5\"\n\n    # not found\n    expect_raises(KeyError) do\n      hash[BigDecimal.new(\"4\")]\n    end\n  end\n\n  it \"upkeeps hashing invariant\" do\n    # a == b => h[a] == h[b]\n    bd1 = BigDecimal.new(1, 2)\n    bd2 = BigDecimal.new(100, 4)\n\n    bd1.hash.should eq bd2.hash\n  end\n\n  it \"can normalize quotient\" do\n    positive_one = BigDecimal.new(\"1.0\")\n    negative_one = BigDecimal.new(\"-1.0\")\n\n    positive_ten = BigInt.new(10)\n    negative_ten = BigInt.new(-10)\n\n    positive_one.normalize_quotient(positive_one, positive_ten).should eq(positive_ten)\n    positive_one.normalize_quotient(positive_one, negative_ten).should eq(negative_ten)\n\n    positive_one.normalize_quotient(negative_one, positive_ten).should eq(negative_ten)\n    positive_one.normalize_quotient(negative_one, negative_ten).should eq(negative_ten)\n\n    negative_one.normalize_quotient(positive_one, positive_ten).should eq(negative_ten)\n    negative_one.normalize_quotient(positive_one, negative_ten).should eq(negative_ten)\n\n    negative_one.normalize_quotient(negative_one, positive_ten).should eq(positive_ten)\n    negative_one.normalize_quotient(negative_one, negative_ten).should eq(negative_ten)\n  end\n\n  describe \"#ceil\" do\n    it { 2.0.to_big_d.ceil.should eq(2) }\n    it { 2.1.to_big_d.ceil.should eq(3) }\n    it { 2.9.to_big_d.ceil.should eq(3) }\n\n    it { 2.01.to_big_d.ceil.should eq(3) }\n    it { 2.11.to_big_d.ceil.should eq(3) }\n    it { 2.91.to_big_d.ceil.should eq(3) }\n\n    it { -2.01.to_big_d.ceil.should eq(-2) }\n    it { -2.91.to_big_d.ceil.should eq(-2) }\n\n    it { \"-123.000\".to_big_d.ceil.value.should eq(-123) }\n    it { \"-1.1\".to_big_d.ceil.value.should eq(-1) }\n  end\n\n  describe \"#floor\" do\n    it { 2.1.to_big_d.floor.should eq(2) }\n    it { 2.9.to_big_d.floor.should eq(2) }\n    it { -2.9.to_big_d.floor.should eq(-3) }\n\n    it { 2.11.to_big_d.floor.should eq(2) }\n    it { 2.91.to_big_d.floor.should eq(2) }\n    it { -2.91.to_big_d.floor.should eq(-3) }\n\n    it { \"-123.000\".to_big_d.floor.value.should eq(-123) }\n    it { \"-1.1\".to_big_d.floor.value.should eq(-2) }\n  end\n\n  describe \"#trunc\" do\n    it { 2.1.to_big_d.trunc.should eq(2) }\n    it { 2.9.to_big_d.trunc.should eq(2) }\n    it { -2.9.to_big_d.trunc.should eq(-2) }\n\n    it { 2.11.to_big_d.trunc.should eq(2) }\n    it { 2.91.to_big_d.trunc.should eq(2) }\n    it { -2.91.to_big_d.trunc.should eq(-2) }\n  end\n\n  describe \"#round\" do\n    describe \"rounding modes\" do\n      it \"to_zero\" do\n        \"-1.5\".to_big_d.round(:to_zero).should eq \"-1\".to_big_d\n        \"-1.0\".to_big_d.round(:to_zero).should eq \"-1\".to_big_d\n        \"-0.9\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"-0.5\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"-0.1\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"0.0\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"0.1\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"0.5\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"0.9\".to_big_d.round(:to_zero).should eq \"0\".to_big_d\n        \"1.0\".to_big_d.round(:to_zero).should eq \"1\".to_big_d\n        \"1.5\".to_big_d.round(:to_zero).should eq \"1\".to_big_d\n\n        \"123456789123456789123.0\".to_big_d.round(:to_zero).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.1\".to_big_d.round(:to_zero).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.5\".to_big_d.round(:to_zero).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.9\".to_big_d.round(:to_zero).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789124.0\".to_big_d.round(:to_zero).should eq \"123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.0\".to_big_d.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.1\".to_big_d.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.5\".to_big_d.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.9\".to_big_d.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789124.0\".to_big_d.round(:to_zero).should eq \"-123456789123456789124.0\".to_big_d\n      end\n\n      it \"to_positive\" do\n        \"-1.5\".to_big_d.round(:to_positive).should eq \"-1\".to_big_d\n        \"-1.0\".to_big_d.round(:to_positive).should eq \"-1\".to_big_d\n        \"-0.9\".to_big_d.round(:to_positive).should eq \"0\".to_big_d\n        \"-0.5\".to_big_d.round(:to_positive).should eq \"0\".to_big_d\n        \"-0.1\".to_big_d.round(:to_positive).should eq \"0\".to_big_d\n        \"0.0\".to_big_d.round(:to_positive).should eq \"0\".to_big_d\n        \"0.1\".to_big_d.round(:to_positive).should eq \"1\".to_big_d\n        \"0.5\".to_big_d.round(:to_positive).should eq \"1\".to_big_d\n        \"0.9\".to_big_d.round(:to_positive).should eq \"1\".to_big_d\n        \"1.0\".to_big_d.round(:to_positive).should eq \"1\".to_big_d\n        \"1.5\".to_big_d.round(:to_positive).should eq \"2\".to_big_d\n\n        \"123456789123456789123.0\".to_big_d.round(:to_positive).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.1\".to_big_d.round(:to_positive).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789123.5\".to_big_d.round(:to_positive).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789123.9\".to_big_d.round(:to_positive).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.0\".to_big_d.round(:to_positive).should eq \"123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.0\".to_big_d.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.1\".to_big_d.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.5\".to_big_d.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.9\".to_big_d.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789124.0\".to_big_d.round(:to_positive).should eq \"-123456789123456789124.0\".to_big_d\n      end\n\n      it \"to_negative\" do\n        \"-1.5\".to_big_d.round(:to_negative).should eq \"-2.0\".to_big_d\n        \"-1.0\".to_big_d.round(:to_negative).should eq \"-1.0\".to_big_d\n        \"-0.9\".to_big_d.round(:to_negative).should eq \"-1.0\".to_big_d\n        \"-0.5\".to_big_d.round(:to_negative).should eq \"-1.0\".to_big_d\n        \"-0.1\".to_big_d.round(:to_negative).should eq \"-1.0\".to_big_d\n        \"0.0\".to_big_d.round(:to_negative).should eq \"0.0\".to_big_d\n        \"0.1\".to_big_d.round(:to_negative).should eq \"0.0\".to_big_d\n        \"0.5\".to_big_d.round(:to_negative).should eq \"0.0\".to_big_d\n        \"0.9\".to_big_d.round(:to_negative).should eq \"0.0\".to_big_d\n        \"1.0\".to_big_d.round(:to_negative).should eq \"1.0\".to_big_d\n        \"1.5\".to_big_d.round(:to_negative).should eq \"1.0\".to_big_d\n\n        \"123456789123456789123.0\".to_big_d.round(:to_negative).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.1\".to_big_d.round(:to_negative).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.5\".to_big_d.round(:to_negative).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.9\".to_big_d.round(:to_negative).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789124.0\".to_big_d.round(:to_negative).should eq \"123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.0\".to_big_d.round(:to_negative).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.1\".to_big_d.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.5\".to_big_d.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.9\".to_big_d.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.0\".to_big_d.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_d\n      end\n\n      it \"ties_even\" do\n        \"-2.5\".to_big_d.round(:ties_even).should eq \"-2.0\".to_big_d\n        \"-1.5\".to_big_d.round(:ties_even).should eq \"-2.0\".to_big_d\n        \"-1.0\".to_big_d.round(:ties_even).should eq \"-1.0\".to_big_d\n        \"-0.9\".to_big_d.round(:ties_even).should eq \"-1.0\".to_big_d\n        \"-0.5\".to_big_d.round(:ties_even).should eq \"0.0\".to_big_d\n        \"-0.1\".to_big_d.round(:ties_even).should eq \"0.0\".to_big_d\n        \"0.0\".to_big_d.round(:ties_even).should eq \"0.0\".to_big_d\n        \"0.1\".to_big_d.round(:ties_even).should eq \"0.0\".to_big_d\n        \"0.5\".to_big_d.round(:ties_even).should eq \"0.0\".to_big_d\n        \"0.9\".to_big_d.round(:ties_even).should eq \"1.0\".to_big_d\n        \"1.0\".to_big_d.round(:ties_even).should eq \"1.0\".to_big_d\n        \"1.5\".to_big_d.round(:ties_even).should eq \"2.0\".to_big_d\n        \"2.5\".to_big_d.round(:ties_even).should eq \"2.0\".to_big_d\n\n        \"123456789123456789123.0\".to_big_d.round(:ties_even).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.1\".to_big_d.round(:ties_even).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.5\".to_big_d.round(:ties_even).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789123.9\".to_big_d.round(:ties_even).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.0\".to_big_d.round(:ties_even).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.5\".to_big_d.round(:ties_even).should eq \"123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.0\".to_big_d.round(:ties_even).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.1\".to_big_d.round(:ties_even).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.5\".to_big_d.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.9\".to_big_d.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.0\".to_big_d.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.5\".to_big_d.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_d\n      end\n\n      it \"ties_away\" do\n        \"-2.5\".to_big_d.round(:ties_away).should eq \"-3.0\".to_big_d\n        \"-1.5\".to_big_d.round(:ties_away).should eq \"-2.0\".to_big_d\n        \"-1.0\".to_big_d.round(:ties_away).should eq \"-1.0\".to_big_d\n        \"-0.9\".to_big_d.round(:ties_away).should eq \"-1.0\".to_big_d\n        \"-0.5\".to_big_d.round(:ties_away).should eq \"-1.0\".to_big_d\n        \"-0.1\".to_big_d.round(:ties_away).should eq \"0.0\".to_big_d\n        \"0.0\".to_big_d.round(:ties_away).should eq \"0.0\".to_big_d\n        \"0.1\".to_big_d.round(:ties_away).should eq \"0.0\".to_big_d\n        \"0.5\".to_big_d.round(:ties_away).should eq \"1.0\".to_big_d\n        \"0.9\".to_big_d.round(:ties_away).should eq \"1.0\".to_big_d\n        \"1.0\".to_big_d.round(:ties_away).should eq \"1.0\".to_big_d\n        \"1.5\".to_big_d.round(:ties_away).should eq \"2.0\".to_big_d\n        \"2.5\".to_big_d.round(:ties_away).should eq \"3.0\".to_big_d\n\n        \"123456789123456789123.0\".to_big_d.round(:ties_away).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.1\".to_big_d.round(:ties_away).should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.5\".to_big_d.round(:ties_away).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789123.9\".to_big_d.round(:ties_away).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.0\".to_big_d.round(:ties_away).should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.5\".to_big_d.round(:ties_away).should eq \"123456789123456789125.0\".to_big_d\n        \"-123456789123456789123.0\".to_big_d.round(:ties_away).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.1\".to_big_d.round(:ties_away).should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.5\".to_big_d.round(:ties_away).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.9\".to_big_d.round(:ties_away).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.0\".to_big_d.round(:ties_away).should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.5\".to_big_d.round(:ties_away).should eq \"-123456789123456789125.0\".to_big_d\n      end\n\n      it \"default (=ties_even)\" do\n        \"-2.5\".to_big_d.round.should eq \"-2.0\".to_big_d\n        \"-1.5\".to_big_d.round.should eq \"-2.0\".to_big_d\n        \"-1.0\".to_big_d.round.should eq \"-1.0\".to_big_d\n        \"-0.9\".to_big_d.round.should eq \"-1.0\".to_big_d\n        \"-0.5\".to_big_d.round.should eq \"0.0\".to_big_d\n        \"-0.1\".to_big_d.round.should eq \"0.0\".to_big_d\n        \"0.0\".to_big_d.round.should eq \"0.0\".to_big_d\n        \"0.1\".to_big_d.round.should eq \"0.0\".to_big_d\n        \"0.5\".to_big_d.round.should eq \"0.0\".to_big_d\n        \"0.9\".to_big_d.round.should eq \"1.0\".to_big_d\n        \"1.0\".to_big_d.round.should eq \"1.0\".to_big_d\n        \"1.5\".to_big_d.round.should eq \"2.0\".to_big_d\n        \"2.5\".to_big_d.round.should eq \"2.0\".to_big_d\n\n        \"123456789123456789123.0\".to_big_d.round.should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.1\".to_big_d.round.should eq \"123456789123456789123.0\".to_big_d\n        \"123456789123456789123.5\".to_big_d.round.should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789123.9\".to_big_d.round.should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.0\".to_big_d.round.should eq \"123456789123456789124.0\".to_big_d\n        \"123456789123456789124.5\".to_big_d.round.should eq \"123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.0\".to_big_d.round.should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.1\".to_big_d.round.should eq \"-123456789123456789123.0\".to_big_d\n        \"-123456789123456789123.5\".to_big_d.round.should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789123.9\".to_big_d.round.should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.0\".to_big_d.round.should eq \"-123456789123456789124.0\".to_big_d\n        \"-123456789123456789124.5\".to_big_d.round.should eq \"-123456789123456789124.0\".to_big_d\n      end\n    end\n\n    describe \"with digits\" do\n      it \"to_zero\" do\n        \"12.345\".to_big_d.round(-1, mode: :to_zero).should eq \"10\".to_big_d\n        \"12.345\".to_big_d.round(0, mode: :to_zero).should eq \"12\".to_big_d\n        \"12.345\".to_big_d.round(1, mode: :to_zero).should eq \"12.3\".to_big_d\n        \"12.345\".to_big_d.round(2, mode: :to_zero).should eq \"12.34\".to_big_d\n        \"-12.345\".to_big_d.round(-1, mode: :to_zero).should eq \"-10\".to_big_d\n        \"-12.345\".to_big_d.round(0, mode: :to_zero).should eq \"-12\".to_big_d\n        \"-12.345\".to_big_d.round(1, mode: :to_zero).should eq \"-12.3\".to_big_d\n        \"-12.345\".to_big_d.round(2, mode: :to_zero).should eq \"-12.34\".to_big_d\n\n        # 1 + 3.0000e-200 -> 1 + 3.0e-200 (ditto for others)\n        (1.to_big_d + BigDecimal.new(30000, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(30001, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(39999, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(40000, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40001, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(49999, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(50000, 204)).round(200, mode: :to_zero).should eq(1.to_big_d + BigDecimal.new(5, 200))\n\n        (-1.to_big_d - BigDecimal.new(30000, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(30001, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(39999, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(40000, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40001, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(49999, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(50000, 204)).round(200, mode: :to_zero).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n      end\n\n      it \"to_positive\" do\n        \"12.345\".to_big_d.round(-1, mode: :to_positive).should eq \"20\".to_big_d\n        \"12.345\".to_big_d.round(0, mode: :to_positive).should eq \"13\".to_big_d\n        \"12.345\".to_big_d.round(1, mode: :to_positive).should eq \"12.4\".to_big_d\n        \"12.345\".to_big_d.round(2, mode: :to_positive).should eq \"12.35\".to_big_d\n        \"-12.345\".to_big_d.round(-1, mode: :to_positive).should eq \"-10\".to_big_d\n        \"-12.345\".to_big_d.round(0, mode: :to_positive).should eq \"-12\".to_big_d\n        \"-12.345\".to_big_d.round(1, mode: :to_positive).should eq \"-12.3\".to_big_d\n        \"-12.345\".to_big_d.round(2, mode: :to_positive).should eq \"-12.34\".to_big_d\n\n        # 1 + 3.0000e-200 -> 1 + 3.0e-200 (ditto for others)\n        (1.to_big_d + BigDecimal.new(30000, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(30001, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(39999, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40000, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40001, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(5, 200))\n        (1.to_big_d + BigDecimal.new(49999, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(5, 200))\n        (1.to_big_d + BigDecimal.new(50000, 204)).round(200, mode: :to_positive).should eq(1.to_big_d + BigDecimal.new(5, 200))\n\n        (-1.to_big_d - BigDecimal.new(30000, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(30001, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(39999, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(40000, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40001, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(49999, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(50000, 204)).round(200, mode: :to_positive).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n      end\n\n      it \"to_negative\" do\n        \"12.345\".to_big_d.round(-1, mode: :to_negative).should eq \"10\".to_big_d\n        \"12.345\".to_big_d.round(0, mode: :to_negative).should eq \"12\".to_big_d\n        \"12.345\".to_big_d.round(1, mode: :to_negative).should eq \"12.3\".to_big_d\n        \"12.345\".to_big_d.round(2, mode: :to_negative).should eq \"12.34\".to_big_d\n        \"-12.345\".to_big_d.round(-1, mode: :to_negative).should eq \"-20\".to_big_d\n        \"-12.345\".to_big_d.round(0, mode: :to_negative).should eq \"-13\".to_big_d\n        \"-12.345\".to_big_d.round(1, mode: :to_negative).should eq \"-12.4\".to_big_d\n        \"-12.345\".to_big_d.round(2, mode: :to_negative).should eq \"-12.35\".to_big_d\n\n        # 1 + 3.0000e-200 -> 1 + 3.0e-200 (ditto for others)\n        (1.to_big_d + BigDecimal.new(30000, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(30001, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(39999, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(40000, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40001, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(49999, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(50000, 204)).round(200, mode: :to_negative).should eq(1.to_big_d + BigDecimal.new(5, 200))\n\n        (-1.to_big_d - BigDecimal.new(30000, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(30001, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(39999, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40000, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40001, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n        (-1.to_big_d - BigDecimal.new(49999, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n        (-1.to_big_d - BigDecimal.new(50000, 204)).round(200, mode: :to_negative).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n      end\n\n      it \"ties_away\" do\n        \"13.825\".to_big_d.round(-1, mode: :ties_away).should eq \"10\".to_big_d\n        \"13.825\".to_big_d.round(0, mode: :ties_away).should eq \"14\".to_big_d\n        \"13.825\".to_big_d.round(1, mode: :ties_away).should eq \"13.8\".to_big_d\n        \"13.825\".to_big_d.round(2, mode: :ties_away).should eq \"13.83\".to_big_d\n        \"-13.825\".to_big_d.round(-1, mode: :ties_away).should eq \"-10\".to_big_d\n        \"-13.825\".to_big_d.round(0, mode: :ties_away).should eq \"-14\".to_big_d\n        \"-13.825\".to_big_d.round(1, mode: :ties_away).should eq \"-13.8\".to_big_d\n        \"-13.825\".to_big_d.round(2, mode: :ties_away).should eq \"-13.83\".to_big_d\n\n        # 1 + 3.0000e-200 -> 1 + 3.0e-200 (ditto for others)\n        (1.to_big_d + BigDecimal.new(30000, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(30001, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(34999, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(35000, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(35001, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(39999, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40000, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40001, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(44999, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(45000, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(5, 200))\n        (1.to_big_d + BigDecimal.new(45001, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(5, 200))\n        (1.to_big_d + BigDecimal.new(50000, 204)).round(200, mode: :ties_away).should eq(1.to_big_d + BigDecimal.new(5, 200))\n\n        (-1.to_big_d - BigDecimal.new(30000, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(30001, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(34999, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(35000, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(35001, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(39999, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40000, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40001, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(44999, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(45000, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n        (-1.to_big_d - BigDecimal.new(45001, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n        (-1.to_big_d - BigDecimal.new(50000, 204)).round(200, mode: :ties_away).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n      end\n\n      it \"ties_even\" do\n        \"15.255\".to_big_d.round(-1, mode: :ties_even).should eq \"20\".to_big_d\n        \"15.255\".to_big_d.round(0, mode: :ties_even).should eq \"15\".to_big_d\n        \"15.255\".to_big_d.round(1, mode: :ties_even).should eq \"15.3\".to_big_d\n        \"15.255\".to_big_d.round(2, mode: :ties_even).should eq \"15.26\".to_big_d\n        \"-15.255\".to_big_d.round(-1, mode: :ties_even).should eq \"-20\".to_big_d\n        \"-15.255\".to_big_d.round(0, mode: :ties_even).should eq \"-15\".to_big_d\n        \"-15.255\".to_big_d.round(1, mode: :ties_even).should eq \"-15.3\".to_big_d\n        \"-15.255\".to_big_d.round(2, mode: :ties_even).should eq \"-15.26\".to_big_d\n\n        # 1 + 3.0000e-200 -> 1 + 3.0e-200 (ditto for others)\n        (1.to_big_d + BigDecimal.new(30000, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(30001, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(34999, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(3, 200))\n        (1.to_big_d + BigDecimal.new(35000, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(35001, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(39999, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40000, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(40001, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(44999, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(45000, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(4, 200))\n        (1.to_big_d + BigDecimal.new(45001, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(5, 200))\n        (1.to_big_d + BigDecimal.new(50000, 204)).round(200, mode: :ties_even).should eq(1.to_big_d + BigDecimal.new(5, 200))\n\n        (-1.to_big_d - BigDecimal.new(30000, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(30001, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(34999, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(3, 200))\n        (-1.to_big_d - BigDecimal.new(35000, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(35001, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(39999, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40000, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(40001, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(44999, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(45000, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(4, 200))\n        (-1.to_big_d - BigDecimal.new(45001, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n        (-1.to_big_d - BigDecimal.new(50000, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(5, 200))\n      end\n    end\n\n    describe \"#integer?\" do\n      it { BigDecimal.new(0, 0).integer?.should be_true }\n      it { BigDecimal.new(1, 0).integer?.should be_true }\n      it { BigDecimal.new(10, 0).integer?.should be_true }\n      it { BigDecimal.new(-10, 1).integer?.should be_true }\n      it { BigDecimal.new(10000, 4).integer?.should be_true }\n\n      it { BigDecimal.new(1, 1).integer?.should be_false }\n      it { BigDecimal.new(13, 2).integer?.should be_false }\n      it { BigDecimal.new(-2400, 3).integer?.should be_false }\n    end\n  end\n\n  describe \"#inspect\" do\n    it { \"123\".to_big_d.inspect.should eq(\"123.0\") }\n  end\nend\n"
  },
  {
    "path": "spec/std/big/big_float_spec.cr",
    "content": "require \"spec\"\nrequire \"big\"\nrequire \"spec/helpers/string\"\n\nprivate def it_converts_to_s(value : BigFloat, str, *, file = __FILE__, line = __LINE__)\n  it \"converts to #{str}\", file: file, line: line do\n    assert_prints value.to_s, str, file: file, line: line\n    str.to_big_f.should eq(value), file: file, line: line\n  end\nend\n\nprivate def with_precision(precision, &)\n  old_precision = BigFloat.default_precision\n  BigFloat.default_precision = precision\n\n  begin\n    yield\n  ensure\n    BigFloat.default_precision = old_precision\n  end\nend\n\ndescribe \"BigFloat\" do\n  describe \".new\" do\n    string_of_integer_value = \"123456789012345678901\"\n    string_of_integer_value_as_float = \"1.23456789012345678901e+20\"\n    bigfloat_of_integer_value = BigFloat.new(string_of_integer_value)\n    string_of_float_value = \"1234567890.12345678901\"\n    bigfloat_of_float_value = BigFloat.new(string_of_float_value)\n\n    it \"new(String)\" do\n      bigfloat_of_integer_value.to_s.should eq(string_of_integer_value_as_float)\n      bigfloat_of_float_value.to_s.should eq(string_of_float_value)\n      BigFloat.new(\"+#{string_of_integer_value}\").to_s.should eq(string_of_integer_value_as_float)\n      BigFloat.new(\"-#{string_of_integer_value}\").to_s.should eq(\"-#{string_of_integer_value_as_float}\")\n      BigFloat.new(\"123_456_789.123_456_789\").to_s.should eq(\"123456789.123456789\")\n    end\n\n    it \"raises an ArgumentError unless string denotes valid float\" do\n      expect_raises(ArgumentError) { BigFloat.new(\"abc\") }\n      expect_raises(ArgumentError) { BigFloat.new(\"+\") }\n      expect_raises(ArgumentError) { BigFloat.new(\"\") }\n    end\n\n    it \"new(BigInt)\" do\n      bigfloat_on_bigint_value = BigFloat.new(BigInt.new(string_of_integer_value))\n      bigfloat_on_bigint_value.should eq(bigfloat_of_integer_value)\n      bigfloat_on_bigint_value.to_s.should eq(string_of_integer_value_as_float)\n    end\n\n    it \"new(BigRational)\" do\n      bigfloat_on_bigrational_value = BigFloat.new(BigRational.new(1, 3))\n      bigfloat_on_bigrational_value.should eq(BigFloat.new(1) / BigFloat.new(3))\n    end\n\n    it \"new(BigFloat)\" do\n      BigFloat.new(bigfloat_of_integer_value).should eq(bigfloat_of_integer_value)\n      BigFloat.new(bigfloat_of_float_value).should eq(bigfloat_of_float_value)\n    end\n\n    it \"new(Int)\" do\n      BigFloat.new(1_u8).to_s.should eq(\"1.0\")\n      BigFloat.new(1_u16).to_s.should eq(\"1.0\")\n      BigFloat.new(1_u32).to_s.should eq(\"1.0\")\n      BigFloat.new(1_u64).to_s.should eq(\"1.0\")\n      BigFloat.new(1_i8).to_s.should eq(\"1.0\")\n      BigFloat.new(1_i16).to_s.should eq(\"1.0\")\n      BigFloat.new(1_i32).to_s.should eq(\"1.0\")\n      BigFloat.new(1_i64).to_s.should eq(\"1.0\")\n      BigFloat.new(-1_i8).to_s.should eq(\"-1.0\")\n      BigFloat.new(-1_i16).to_s.should eq(\"-1.0\")\n      BigFloat.new(-1_i32).to_s.should eq(\"-1.0\")\n      BigFloat.new(-1_i64).to_s.should eq(\"-1.0\")\n\n      BigFloat.new(255_u8).to_s.should eq(\"255.0\")\n      BigFloat.new(65535_u16).to_s.should eq(\"65535.0\")\n      BigFloat.new(4294967295_u32).to_s.should eq(\"4294967295.0\")\n      BigFloat.new(18446744073709551615_u64).to_s.should eq(\"1.8446744073709551615e+19\")\n      BigFloat.new(127_i8).to_s.should eq(\"127.0\")\n      BigFloat.new(32767_i16).to_s.should eq(\"32767.0\")\n      BigFloat.new(2147483647_i32).to_s.should eq(\"2147483647.0\")\n      BigFloat.new(9223372036854775807_i64).to_s.should eq(\"9.223372036854775807e+18\")\n      BigFloat.new(-128_i8).to_s.should eq(\"-128.0\")\n      BigFloat.new(-32768_i16).to_s.should eq(\"-32768.0\")\n      BigFloat.new(-2147483648_i32).to_s.should eq(\"-2147483648.0\")\n      BigFloat.new(-9223372036854775808_i64).to_s.should eq(\"-9.223372036854775808e+18\")\n    end\n\n    it \"raises if creating from infinity\" do\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float32::INFINITY) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float64::INFINITY) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float32::INFINITY, precision: 128) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float64::INFINITY, precision: 128) }\n    end\n\n    it \"raises if creating from NaN\" do\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float32::NAN) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float64::NAN) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float32::NAN, precision: 128) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigFloat.new(Float64::NAN, precision: 128) }\n    end\n  end\n\n  describe \"#<=>\" do\n    it \"compares against NaNs\" do\n      (1.to_big_f <=> Float64::NAN).should be_nil\n      (1.to_big_f <=> Float32::NAN).should be_nil\n      (Float64::NAN <=> 1.to_big_f).should be_nil\n      (Float32::NAN <=> 1.to_big_f).should be_nil\n\n      typeof(1.to_big_f <=> Float64::NAN).should eq(Int32?)\n      typeof(1.to_big_f <=> Float32::NAN).should eq(Int32?)\n      typeof(Float64::NAN <=> 1.to_big_f).should eq(Int32?)\n      typeof(Float32::NAN <=> 1.to_big_f).should eq(Int32?)\n\n      typeof(1.to_big_f <=> 1.to_big_f).should eq(Int32)\n    end\n  end\n\n  describe \"unary #-\" do\n    it do\n      bf = \"0.12345\".to_big_f\n      (-bf).to_s.should eq(\"-0.12345\")\n    end\n\n    it do\n      bf = \"61397953.0005354\".to_big_f\n      (-bf).to_s.should eq(\"-61397953.0005354\")\n    end\n\n    it do\n      bf = \"395.009631567315769036\".to_big_f\n      (-bf).to_s.should eq(\"-395.009631567315769036\")\n    end\n  end\n\n  describe \"#+\" do\n    it { (\"1.0\".to_big_f + \"2.0\".to_big_f).to_s.should eq(\"3.0\") }\n    it { (\"0.04\".to_big_f + \"89.0001\".to_big_f).to_s.should eq(\"89.0401\") }\n    it { (\"-5.5\".to_big_f + \"5.5\".to_big_f).to_s.should eq(\"0.0\") }\n    it { (\"5.5\".to_big_f + \"-5.5\".to_big_f).to_s.should eq(\"0.0\") }\n  end\n\n  describe \"#-\" do\n    it { (\"1.0\".to_big_f - \"2.0\".to_big_f).to_s.should eq(\"-1.0\") }\n    it { (\"0.04\".to_big_f - \"89.0001\".to_big_f).to_s.should eq(\"-88.9601\") }\n    it { (\"-5.5\".to_big_f - \"5.5\".to_big_f).to_s.should eq(\"-11.0\") }\n    it { (\"5.5\".to_big_f - \"-5.5\".to_big_f).to_s.should eq(\"11.0\") }\n  end\n\n  describe \"#*\" do\n    it { (\"1.0\".to_big_f * \"2.0\".to_big_f).to_s.should eq(\"2.0\") }\n    it { (\"0.04\".to_big_f * \"89.0001\".to_big_f).to_s.should eq(\"3.560004\") }\n    it { (\"-5.5\".to_big_f * \"5.5\".to_big_f).to_s.should eq(\"-30.25\") }\n    it { (\"5.5\".to_big_f * \"-5.5\".to_big_f).to_s.should eq(\"-30.25\") }\n  end\n\n  describe \"#/\" do\n    it { (\"1.0\".to_big_f / \"2.0\".to_big_f).to_s.should eq(\"0.5\") }\n    it { (\"0.04\".to_big_f / \"89.0001\".to_big_f).to_s.should eq(\"0.000449437697261014313467\") }\n    it { (\"-5.5\".to_big_f / \"5.5\".to_big_f).to_s.should eq(\"-1.0\") }\n    it { (\"5.5\".to_big_f / \"-5.5\".to_big_f).to_s.should eq(\"-1.0\") }\n    expect_raises(DivisionByZeroError) { 0.1.to_big_f / 0 }\n    it { (\"5.5\".to_big_f / 16_u64).to_s.should eq(\"0.34375\") }\n    it { (\"5.5\".to_big_f / 16_u8).to_s.should eq(\"0.34375\") }\n  end\n\n  describe \"#//\" do\n    it { (\"1.0\".to_big_f // \"2.0\".to_big_f).to_s.should eq(\"0.0\") }\n    it { (\"0.04\".to_big_f // \"89.0001\".to_big_f).to_s.should eq(\"0.0\") }\n    it { (\"-5.5\".to_big_f // \"5.5\".to_big_f).to_s.should eq(\"-1.0\") }\n    it { (\"5.5\".to_big_f // \"-5.5\".to_big_f).to_s.should eq(\"-1.0\") }\n    expect_raises(DivisionByZeroError) { 0.1.to_big_f // 0 }\n    it { (\"5.5\".to_big_f // 16_u64).to_s.should eq(\"0.0\") }\n    it { (\"5.5\".to_big_f // 16_u8).to_s.should eq(\"0.0\") }\n\n    it { (\"-1\".to_big_f // 2_u8).to_s.should eq(\"-1.0\") }\n  end\n\n  describe \"#**\" do\n    it { (\"1.34\".to_big_f ** 2).should be_close(\"1.7956\".to_big_f, 1e-12) }\n    it { (\"-0.05\".to_big_f ** 10).should be_close(\"0.00000000000009765625\".to_big_f, 1e-12) }\n    it { (\"0.1234567890\".to_big_f ** 3).should be_close(\"0.001881676371789154860897069\".to_big_f, 1e-12) }\n\n    it { (\"1.34\".to_big_f ** 2.to_big_i).should be_close(\"1.7956\".to_big_f, 1e-12) }\n    it { (\"-0.05\".to_big_f ** 10.to_big_i).should be_close(\"0.00000000000009765625\".to_big_f, 1e-12) }\n    it { (\"0.1234567890\".to_big_f ** 3.to_big_i).should be_close(\"0.001881676371789154860897069\".to_big_f, 1e-12) }\n\n    it { (\"10\".to_big_f ** -5).should be_close(\"0.00001\".to_big_f, 1e-12) }\n    it { (\"0.1\".to_big_f ** -5).should be_close(\"100000\".to_big_f, 1e-12) }\n\n    it { (\"10\".to_big_f ** (-5).to_big_i).should be_close(\"0.00001\".to_big_f, 1e-12) }\n    it { (\"0.1\".to_big_f ** (-5).to_big_i).should be_close(\"100000\".to_big_f, 1e-12) }\n    it { (\"0\".to_big_f ** 1.to_big_i).should eq(0.to_big_f) }\n    it { (\"0\".to_big_f ** 0.to_big_i).should eq(1.to_big_f) }\n    it { expect_raises(ArgumentError, \"Cannot raise 0 to a negative power\") { \"0\".to_big_f ** (-1).to_big_i } }\n  end\n\n  describe \"#abs\" do\n    it { -5.to_big_f.abs.should eq(5) }\n    it { 5.to_big_f.abs.should eq(5) }\n    it { \"-0.00001\".to_big_f.abs.to_s.should eq(\"1.0e-5\") }\n    it { \"0.00000000001\".to_big_f.abs.to_s.should eq(\"1.0e-11\") }\n  end\n\n  describe \"#ceil\" do\n    it { 2.0.to_big_f.ceil.should eq(2) }\n    it { 2.1.to_big_f.ceil.should eq(3) }\n    it { 2.9.to_big_f.ceil.should eq(3) }\n  end\n\n  describe \"#floor\" do\n    it { 2.1.to_big_f.floor.should eq(2) }\n    it { 2.9.to_big_f.floor.should eq(2) }\n    it { -2.9.to_big_f.floor.should eq(-3) }\n  end\n\n  describe \"#trunc\" do\n    it { 2.1.to_big_f.trunc.should eq(2) }\n    it { 2.9.to_big_f.trunc.should eq(2) }\n    it { -2.9.to_big_f.trunc.should eq(-2) }\n  end\n\n  describe \"#to_f\" do\n    it { 1.34.to_big_f.to_f.should eq(1.34) }\n    it { 0.0001304.to_big_f.to_f.should eq(0.0001304) }\n    it { 1.234567.to_big_f.to_f32.should eq(1.234567_f32) }\n  end\n\n  describe \"#to_f!\" do\n    it { 1.34.to_big_f.to_f!.should eq(1.34) }\n    it { 0.0001304.to_big_f.to_f!.should eq(0.0001304) }\n    it { 1.234567.to_big_f.to_f32!.should eq(1.234567_f32) }\n  end\n\n  describe \"#to_i\" do\n    it { 1.34.to_big_f.to_i.should eq(1) }\n    it { 123.to_big_f.to_i.should eq(123) }\n    it { -4321.to_big_f.to_i.should eq(-4321) }\n  end\n\n  describe \"#to_i!\" do\n    it { 1.34.to_big_f.to_i!.should eq(1) }\n    it { 123.to_big_f.to_i!.should eq(123) }\n    it { -4321.to_big_f.to_i!.should eq(-4321) }\n  end\n\n  describe \"#to_i64\" do\n    it \"basic\" do\n      1.to_big_f.to_i64.should eq 1\n      1.to_big_f.to_i64.should be_a(Int64)\n    end\n\n    it { expect_raises(OverflowError) { (2.0 ** 63).to_big_f.to_i64 } }\n    it { expect_raises(OverflowError) { (BigFloat.new(2.0 ** 63, precision: 128) - 0.9999).to_i64 } }\n    it { expect_raises(OverflowError) { (-9.223372036854778e+18).to_big_f.to_i64 } } # (-(2.0 ** 63)).prev_float\n  end\n\n  describe \"#to_i64!\" do\n    it \"basic\" do\n      1.to_big_f.to_i64!.should eq 1\n      1.to_big_f.to_i64!.should be_a(Int64)\n    end\n\n    it \"doesn't raise on overflow\" do\n      (2.0 ** 63).to_big_f.to_i64!\n      (BigFloat.new(2.0 ** 63, precision: 128) - 0.9999).to_i64!\n      (-9.223372036854778e+18).to_big_f.to_i64! # (-(2.0 ** 63)).prev_float\n    end\n  end\n\n  describe \"#to_u\" do\n    it { 1.34.to_big_f.to_u.should eq(1) }\n    it { 123.to_big_f.to_u.should eq(123) }\n    it { 4321.to_big_f.to_u.should eq(4321) }\n    it do\n      expect_raises(OverflowError) { -123.34.to_big_f.to_u }\n    end\n  end\n\n  describe \"#to_u!\" do\n    it { 1.34.to_big_f.to_u!.should eq(1) }\n    it { 123.to_big_f.to_u!.should eq(123) }\n    it { 4321.to_big_f.to_u!.should eq(4321) }\n  end\n\n  describe \"#to_u64\" do\n    it \"basic\" do\n      1.to_big_f.to_u64.should eq 1\n      1.to_big_f.to_u64.should be_a(UInt64)\n    end\n\n    it { expect_raises(OverflowError) { (2.0 ** 64).to_big_f.to_u64 } }\n    it { expect_raises(OverflowError) { (-1).to_big_f.to_u64 } }\n    it { expect_raises(OverflowError) { (-0.0001).to_big_f.to_u64 } }\n  end\n\n  describe \"#to_u64!\" do\n    it \"basic\" do\n      1.to_big_f.to_u64!.should eq 1\n      1.to_big_f.to_u64!.should be_a(UInt64)\n    end\n\n    it \"doesn't raise on overflow\" do\n      (2.0 ** 64).to_big_f.to_u64!\n      (-1).to_big_f.to_u64!\n      (-0.0001).to_big_f.to_u64!\n    end\n  end\n\n  describe \"to_s\" do\n    it_converts_to_s \"0\".to_big_f, \"0.0\"\n    it_converts_to_s \"-0\".to_big_f, \"0.0\"\n    it_converts_to_s \"0.000001\".to_big_f, \"1.0e-6\"\n    it_converts_to_s \"48600000\".to_big_f, \"48600000.0\"\n    it_converts_to_s \"12345678.87654\".to_big_f, \"12345678.87654\"\n    it_converts_to_s \"12345678.87654321\".to_big_f, \"12345678.87654321\"\n    it_converts_to_s \"9.000000000000987\".to_big_f, \"9.000000000000987\"\n    it_converts_to_s \"12345678901234567\".to_big_f, \"1.2345678901234567e+16\"\n    it_converts_to_s \"1234567890123456789\".to_big_f, \"1.234567890123456789e+18\"\n\n    it_converts_to_s \".01\".to_big_f, \"0.01\"\n    it_converts_to_s \"-.01\".to_big_f, \"-0.01\"\n    it_converts_to_s \".1\".to_big_f, \"0.1\"\n    it_converts_to_s \"-.1\".to_big_f, \"-0.1\"\n    it_converts_to_s \"1\".to_big_f, \"1.0\"\n    it_converts_to_s \"-1\".to_big_f, \"-1.0\"\n    it_converts_to_s \"10\".to_big_f, \"10.0\"\n    it_converts_to_s \"100\".to_big_f, \"100.0\"\n    it_converts_to_s \"150\".to_big_f, \"150.0\"\n\n    it_converts_to_s (3.0).to_big_f, \"3.0\"\n    it_converts_to_s 3.to_big_f, \"3.0\"\n    it_converts_to_s -3.to_big_f, \"-3.0\"\n\n    it_converts_to_s \"1.23e45\".to_big_f, \"1.23e+45\"\n    it_converts_to_s \"1e-234\".to_big_f, \"1.0e-234\"\n\n    it_converts_to_s Float64::MAX.to_s.to_big_f, \"1.7976931348623157e+308\"\n    it_converts_to_s Float64::MIN_POSITIVE.to_s.to_big_f, \"2.2250738585072014e-308\"\n\n    # since Float64-to-BigFloat conversion is always exact, but GMP floats have\n    # a *minimum* precision equal to the machine word size, we cannot assume the\n    # shortest round-trip property for Float64s converted this way\n    it { assert_prints (0.1).to_big_f.to_s, \"0.100000000000000005551\" }\n    it { assert_prints Float64::MAX.to_big_f.to_s, \"1.79769313486231570815e+308\" }\n    it { assert_prints Float64::MIN_POSITIVE.to_big_f.to_s, \"2.22507385850720138309e-308\" }\n\n    it { (2.to_big_f ** 7133786264).to_s.should end_with(\"e+2147483648\") }  # least power of two with a base-10 exponent greater than Int32::MAX\n    it { (2.to_big_f ** -7133786264).to_s.should end_with(\"e-2147483649\") } # least power of two with a base-10 exponent less than Int32::MIN\n    it { (10.to_big_f ** 3000000000 * 1.5).to_s.should end_with(\"e+3000000000\") }\n    it { (10.to_big_f ** -3000000000 * 1.5).to_s.should end_with(\"e-3000000000\") }\n\n    {% unless flag?(:win32) && flag?(:gnu) %}\n      it { (10.to_big_f ** 10000000000 * 1.5).to_s.should end_with(\"e+10000000000\") }\n      it { (10.to_big_f ** -10000000000 * 1.5).to_s.should end_with(\"e-10000000000\") }\n    {% end %}\n  end\n\n  describe \"#inspect\" do\n    it { assert_prints \"2.3\".to_big_f.inspect, \"2.3\" }\n  end\n\n  describe \"#round\" do\n    describe \"rounding modes\" do\n      it \"to_zero\" do\n        -1.5.to_big_f.round(:to_zero).should eq -1.0.to_big_f\n        -1.0.to_big_f.round(:to_zero).should eq -1.0.to_big_f\n        -0.9.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        -0.5.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        -0.1.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        0.0.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        0.1.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        0.5.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        0.9.to_big_f.round(:to_zero).should eq 0.0.to_big_f\n        1.0.to_big_f.round(:to_zero).should eq 1.0.to_big_f\n        1.5.to_big_f.round(:to_zero).should eq 1.0.to_big_f\n\n        with_precision(256) do\n          \"123456789123456789123.0\".to_big_f.round(:to_zero).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.1\".to_big_f.round(:to_zero).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.5\".to_big_f.round(:to_zero).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.9\".to_big_f.round(:to_zero).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789124.0\".to_big_f.round(:to_zero).should eq \"123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.0\".to_big_f.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.1\".to_big_f.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.5\".to_big_f.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.9\".to_big_f.round(:to_zero).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789124.0\".to_big_f.round(:to_zero).should eq \"-123456789123456789124.0\".to_big_f\n        end\n      end\n\n      it \"to_positive\" do\n        -1.5.to_big_f.round(:to_positive).should eq -1.0.to_big_f\n        -1.0.to_big_f.round(:to_positive).should eq -1.0.to_big_f\n        -0.9.to_big_f.round(:to_positive).should eq 0.0.to_big_f\n        -0.5.to_big_f.round(:to_positive).should eq 0.0.to_big_f\n        -0.1.to_big_f.round(:to_positive).should eq 0.0.to_big_f\n        0.0.to_big_f.round(:to_positive).should eq 0.0.to_big_f\n        0.1.to_big_f.round(:to_positive).should eq 1.0.to_big_f\n        0.5.to_big_f.round(:to_positive).should eq 1.0.to_big_f\n        0.9.to_big_f.round(:to_positive).should eq 1.0.to_big_f\n        1.0.to_big_f.round(:to_positive).should eq 1.0.to_big_f\n        1.5.to_big_f.round(:to_positive).should eq 2.0.to_big_f\n\n        with_precision(256) do\n          \"123456789123456789123.0\".to_big_f.round(:to_positive).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.1\".to_big_f.round(:to_positive).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789123.5\".to_big_f.round(:to_positive).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789123.9\".to_big_f.round(:to_positive).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.0\".to_big_f.round(:to_positive).should eq \"123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.0\".to_big_f.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.1\".to_big_f.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.5\".to_big_f.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.9\".to_big_f.round(:to_positive).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789124.0\".to_big_f.round(:to_positive).should eq \"-123456789123456789124.0\".to_big_f\n        end\n      end\n\n      it \"to_negative\" do\n        -1.5.to_big_f.round(:to_negative).should eq -2.0.to_big_f\n        -1.0.to_big_f.round(:to_negative).should eq -1.0.to_big_f\n        -0.9.to_big_f.round(:to_negative).should eq -1.0.to_big_f\n        -0.5.to_big_f.round(:to_negative).should eq -1.0.to_big_f\n        -0.1.to_big_f.round(:to_negative).should eq -1.0.to_big_f\n        0.0.to_big_f.round(:to_negative).should eq 0.0.to_big_f\n        0.1.to_big_f.round(:to_negative).should eq 0.0.to_big_f\n        0.5.to_big_f.round(:to_negative).should eq 0.0.to_big_f\n        0.9.to_big_f.round(:to_negative).should eq 0.0.to_big_f\n        1.0.to_big_f.round(:to_negative).should eq 1.0.to_big_f\n        1.5.to_big_f.round(:to_negative).should eq 1.0.to_big_f\n\n        with_precision(256) do\n          \"123456789123456789123.0\".to_big_f.round(:to_negative).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.1\".to_big_f.round(:to_negative).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.5\".to_big_f.round(:to_negative).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.9\".to_big_f.round(:to_negative).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789124.0\".to_big_f.round(:to_negative).should eq \"123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.0\".to_big_f.round(:to_negative).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.1\".to_big_f.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.5\".to_big_f.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.9\".to_big_f.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.0\".to_big_f.round(:to_negative).should eq \"-123456789123456789124.0\".to_big_f\n        end\n      end\n\n      it \"ties_even\" do\n        -2.5.to_big_f.round(:ties_even).should eq -2.0.to_big_f\n        -1.5.to_big_f.round(:ties_even).should eq -2.0.to_big_f\n        -1.0.to_big_f.round(:ties_even).should eq -1.0.to_big_f\n        -0.9.to_big_f.round(:ties_even).should eq -1.0.to_big_f\n        -0.5.to_big_f.round(:ties_even).should eq 0.0.to_big_f\n        -0.1.to_big_f.round(:ties_even).should eq 0.0.to_big_f\n        0.0.to_big_f.round(:ties_even).should eq 0.0.to_big_f\n        0.1.to_big_f.round(:ties_even).should eq 0.0.to_big_f\n        0.5.to_big_f.round(:ties_even).should eq 0.0.to_big_f\n        0.9.to_big_f.round(:ties_even).should eq 1.0.to_big_f\n        1.0.to_big_f.round(:ties_even).should eq 1.0.to_big_f\n        1.5.to_big_f.round(:ties_even).should eq 2.0.to_big_f\n        2.5.to_big_f.round(:ties_even).should eq 2.0.to_big_f\n\n        with_precision(256) do\n          \"123456789123456789123.0\".to_big_f.round(:ties_even).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.1\".to_big_f.round(:ties_even).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.5\".to_big_f.round(:ties_even).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789123.9\".to_big_f.round(:ties_even).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.0\".to_big_f.round(:ties_even).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.5\".to_big_f.round(:ties_even).should eq \"123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.0\".to_big_f.round(:ties_even).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.1\".to_big_f.round(:ties_even).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.5\".to_big_f.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.9\".to_big_f.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.0\".to_big_f.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.5\".to_big_f.round(:ties_even).should eq \"-123456789123456789124.0\".to_big_f\n        end\n      end\n\n      it \"ties_away\" do\n        -2.5.to_big_f.round(:ties_away).should eq -3.0.to_big_f\n        -1.5.to_big_f.round(:ties_away).should eq -2.0.to_big_f\n        -1.0.to_big_f.round(:ties_away).should eq -1.0.to_big_f\n        -0.9.to_big_f.round(:ties_away).should eq -1.0.to_big_f\n        -0.5.to_big_f.round(:ties_away).should eq -1.0.to_big_f\n        -0.1.to_big_f.round(:ties_away).should eq 0.0.to_big_f\n        0.0.to_big_f.round(:ties_away).should eq 0.0.to_big_f\n        0.1.to_big_f.round(:ties_away).should eq 0.0.to_big_f\n        0.5.to_big_f.round(:ties_away).should eq 1.0.to_big_f\n        0.9.to_big_f.round(:ties_away).should eq 1.0.to_big_f\n        1.0.to_big_f.round(:ties_away).should eq 1.0.to_big_f\n        1.5.to_big_f.round(:ties_away).should eq 2.0.to_big_f\n        2.5.to_big_f.round(:ties_away).should eq 3.0.to_big_f\n\n        with_precision(256) do\n          \"123456789123456789123.0\".to_big_f.round(:ties_away).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.1\".to_big_f.round(:ties_away).should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.5\".to_big_f.round(:ties_away).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789123.9\".to_big_f.round(:ties_away).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.0\".to_big_f.round(:ties_away).should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.5\".to_big_f.round(:ties_away).should eq \"123456789123456789125.0\".to_big_f\n          \"-123456789123456789123.0\".to_big_f.round(:ties_away).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.1\".to_big_f.round(:ties_away).should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.5\".to_big_f.round(:ties_away).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.9\".to_big_f.round(:ties_away).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.0\".to_big_f.round(:ties_away).should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.5\".to_big_f.round(:ties_away).should eq \"-123456789123456789125.0\".to_big_f\n        end\n      end\n\n      it \"default (=ties_even)\" do\n        -2.5.to_big_f.round.should eq -2.0.to_big_f\n        -1.5.to_big_f.round.should eq -2.0.to_big_f\n        -1.0.to_big_f.round.should eq -1.0.to_big_f\n        -0.9.to_big_f.round.should eq -1.0.to_big_f\n        -0.5.to_big_f.round.should eq 0.0.to_big_f\n        -0.1.to_big_f.round.should eq 0.0.to_big_f\n        0.0.to_big_f.round.should eq 0.0.to_big_f\n        0.1.to_big_f.round.should eq 0.0.to_big_f\n        0.5.to_big_f.round.should eq 0.0.to_big_f\n        0.9.to_big_f.round.should eq 1.0.to_big_f\n        1.0.to_big_f.round.should eq 1.0.to_big_f\n        1.5.to_big_f.round.should eq 2.0.to_big_f\n        2.5.to_big_f.round.should eq 2.0.to_big_f\n\n        with_precision(256) do\n          \"123456789123456789123.0\".to_big_f.round.should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.1\".to_big_f.round.should eq \"123456789123456789123.0\".to_big_f\n          \"123456789123456789123.5\".to_big_f.round.should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789123.9\".to_big_f.round.should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.0\".to_big_f.round.should eq \"123456789123456789124.0\".to_big_f\n          \"123456789123456789124.5\".to_big_f.round.should eq \"123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.0\".to_big_f.round.should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.1\".to_big_f.round.should eq \"-123456789123456789123.0\".to_big_f\n          \"-123456789123456789123.5\".to_big_f.round.should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789123.9\".to_big_f.round.should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.0\".to_big_f.round.should eq \"-123456789123456789124.0\".to_big_f\n          \"-123456789123456789124.5\".to_big_f.round.should eq \"-123456789123456789124.0\".to_big_f\n        end\n      end\n    end\n  end\n\n  describe \"#integer?\" do\n    it { BigFloat.zero.integer?.should be_true }\n    it { 1.to_big_f.integer?.should be_true }\n    it { 1.2.to_big_f.integer?.should be_false }\n  end\n\n  it \"#hash\" do\n    b = 123.to_big_f\n    b.hash.should eq(b.to_f64.hash)\n  end\n\n  it \"clones\" do\n    x = 1.to_big_f\n    x.clone.should eq(x)\n  end\nend\n\ndescribe \"BigFloat Math\" do\n  it \".ilogb\" do\n    Math.ilogb(0.2.to_big_f).should eq(-3)\n    Math.ilogb(123.45.to_big_f).should eq(6)\n    Math.ilogb(2.to_big_f ** 1_000_000_000).should eq(1_000_000_000)\n\n    {% unless flag?(:win32) && flag?(:gnu) %}\n      Math.ilogb(2.to_big_f ** 100_000_000_000).should eq(100_000_000_000)\n      Math.ilogb(2.to_big_f ** -100_000_000_000).should eq(-100_000_000_000)\n    {% end %}\n\n    expect_raises(ArgumentError) { Math.ilogb(0.to_big_f) }\n  end\n\n  it \".logb\" do\n    Math.logb(0.2.to_big_f).should eq(-3.to_big_f)\n    Math.logb(123.45.to_big_f).should eq(6.to_big_f)\n    Math.logb(2.to_big_f ** 1_000_000_000).should eq(1_000_000_000.to_big_f)\n\n    {% unless flag?(:win32) && flag?(:gnu) %}\n      Math.logb(2.to_big_f ** 100_000_000_000).should eq(100_000_000_000.to_big_f)\n      Math.logb(2.to_big_f ** -100_000_000_000).should eq(-100_000_000_000.to_big_f)\n    {% end %}\n\n    expect_raises(ArgumentError) { Math.logb(0.to_big_f) }\n  end\n\n  it \".ldexp\" do\n    Math.ldexp(0.2.to_big_f, 2).should eq(0.8.to_big_f)\n    Math.ldexp(0.2.to_big_f, -2).should eq(0.05.to_big_f)\n    Math.ldexp(1.to_big_f, 1_000_000_000).should eq(2.to_big_f ** 1_000_000_000)\n\n    {% unless flag?(:win32) && flag?(:gnu) %}\n      Math.ldexp(1.to_big_f, 100_000_000_000).should eq(2.to_big_f ** 100_000_000_000)\n      Math.ldexp(1.to_big_f, -100_000_000_000).should eq(0.5.to_big_f ** 100_000_000_000)\n    {% end %}\n  end\n\n  it \".scalbn\" do\n    Math.scalbn(0.2.to_big_f, 2).should eq(0.8.to_big_f)\n    Math.scalbn(0.2.to_big_f, -2).should eq(0.05.to_big_f)\n    Math.scalbn(1.to_big_f, 1_000_000_000).should eq(2.to_big_f ** 1_000_000_000)\n\n    {% unless flag?(:win32) && flag?(:gnu) %}\n      Math.scalbn(1.to_big_f, 100_000_000_000).should eq(2.to_big_f ** 100_000_000_000)\n      Math.scalbn(1.to_big_f, -100_000_000_000).should eq(0.5.to_big_f ** 100_000_000_000)\n    {% end %}\n  end\n\n  it \".scalbln\" do\n    Math.scalbln(0.2.to_big_f, 2).should eq(0.8.to_big_f)\n    Math.scalbln(0.2.to_big_f, -2).should eq(0.05.to_big_f)\n    Math.scalbln(1.to_big_f, 1_000_000_000).should eq(2.to_big_f ** 1_000_000_000)\n\n    {% unless flag?(:win32) && flag?(:gnu) %}\n      Math.scalbln(1.to_big_f, 100_000_000_000).should eq(2.to_big_f ** 100_000_000_000)\n      Math.scalbln(1.to_big_f, -100_000_000_000).should eq(0.5.to_big_f ** 100_000_000_000)\n    {% end %}\n  end\n\n  it \".frexp\" do\n    Math.frexp(0.to_big_f).should eq({0.0, 0})\n    Math.frexp(1.to_big_f).should eq({0.5, 1})\n    Math.frexp(0.2.to_big_f).should eq({0.8, -2})\n    Math.frexp(2.to_big_f ** 63).should eq({0.5, 64})\n    Math.frexp(2.to_big_f ** 64).should eq({0.5, 65})\n    Math.frexp(2.to_big_f ** 200).should eq({0.5, 201})\n    Math.frexp(2.to_big_f ** -200).should eq({0.5, -199})\n    Math.frexp(2.to_big_f ** 0x7FFFFFFF).should eq({0.5, 0x80000000})\n    Math.frexp(2.to_big_f ** 0x80000000).should eq({0.5, 0x80000001})\n    Math.frexp(2.to_big_f ** 0xFFFFFFFF).should eq({0.5, 0x100000000})\n    Math.frexp(1.75 * 2.to_big_f ** 0x123456789).should eq({0.875, 0x12345678A})\n    Math.frexp(2.to_big_f ** -0x80000000).should eq({0.5, -0x7FFFFFFF})\n    Math.frexp(2.to_big_f ** -0x80000001).should eq({0.5, -0x80000000})\n    Math.frexp(2.to_big_f ** -0x100000000).should eq({0.5, -0xFFFFFFFF})\n    Math.frexp(1.75 * 2.to_big_f ** -0x123456789).should eq({0.875, -0x123456788})\n    Math.frexp(-(2.to_big_f ** 0x7FFFFFFF)).should eq({-0.5, 0x80000000})\n    Math.frexp(-(2.to_big_f ** -0x100000000)).should eq({-0.5, -0xFFFFFFFF})\n  end\n\n  it \".copysign\" do\n    Math.copysign(3.to_big_f, 2.to_big_f).should eq(3.to_big_f)\n    Math.copysign(3.to_big_f, 0.to_big_f).should eq(3.to_big_f)\n    Math.copysign(3.to_big_f, -2.to_big_f).should eq(-3.to_big_f)\n    Math.copysign(0.to_big_f, 2.to_big_f).should eq(0.to_big_f)\n    Math.copysign(0.to_big_f, 0.to_big_f).should eq(0.to_big_f)\n    Math.copysign(0.to_big_f, -2.to_big_f).should eq(0.to_big_f)\n    Math.copysign(-3.to_big_f, 2.to_big_f).should eq(3.to_big_f)\n    Math.copysign(-3.to_big_f, 0.to_big_f).should eq(3.to_big_f)\n    Math.copysign(-3.to_big_f, -2.to_big_f).should eq(-3.to_big_f)\n  end\n\n  it \".sqrt\" do\n    Math.sqrt(BigFloat.new(\"1\" + \"0\"*48)).should eq(BigFloat.new(\"1\" + \"0\"*24))\n  end\nend\n"
  },
  {
    "path": "spec/std/big/big_int_spec.cr",
    "content": "require \"spec\"\nrequire \"big\"\n\nprivate def it_converts_to_s(num, str, *, file = __FILE__, line = __LINE__, **opts)\n  it file: file, line: line do\n    num.to_s(**opts).should eq(str), file: file, line: line\n    String.build { |io| num.to_s(io, **opts) }.should eq(str), file: file, line: line\n  end\nend\n\ndescribe \"BigInt\" do\n  describe \"#integer?\" do\n    it { BigInt.zero.integer?.should be_true }\n    it { 12345.to_big_i.integer?.should be_true }\n  end\n\n  it \"creates with a value of zero\" do\n    BigInt.new.to_s.should eq(\"0\")\n  end\n\n  it \"creates from signed ints\" do\n    BigInt.new(-1_i8).to_s.should eq(\"-1\")\n    BigInt.new(-1_i16).to_s.should eq(\"-1\")\n    BigInt.new(-1_i32).to_s.should eq(\"-1\")\n    BigInt.new(-1_i64).to_s.should eq(\"-1\")\n  end\n\n  it \"creates from unsigned ints\" do\n    BigInt.new(1_u8).to_s.should eq(\"1\")\n    BigInt.new(1_u16).to_s.should eq(\"1\")\n    BigInt.new(1_u32).to_s.should eq(\"1\")\n    BigInt.new(1_u64).to_s.should eq(\"1\")\n  end\n\n  it \"creates from string\" do\n    BigInt.new(\"12345678\").to_s.should eq(\"12345678\")\n    BigInt.new(\"123_456_78\").to_s.should eq(\"12345678\")\n    BigInt.new(\"+12345678\").to_s.should eq(\"12345678\")\n    BigInt.new(\"-12345678\").to_s.should eq(\"-12345678\")\n  end\n\n  it \"raises if creates from string but invalid\" do\n    expect_raises ArgumentError, \"Invalid BigInt: 123 hello 456\" do\n      BigInt.new(\"123 hello 456\")\n    end\n  end\n\n  it \"raises if creating from infinity\" do\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigInt.new(Float32::INFINITY) }\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigInt.new(Float64::INFINITY) }\n  end\n\n  it \"raises if creating from NaN\" do\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigInt.new(Float32::NAN) }\n    expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigInt.new(Float64::NAN) }\n  end\n\n  it \"creates from float\" do\n    BigInt.new(12.3).to_s.should eq(\"12\")\n  end\n\n  describe \".from_digits\" do\n    it \"creates from digits\" do\n      BigInt.from_digits([] of Int32).should eq(0.to_big_i)\n      BigInt.from_digits([3, 2, 1]).should eq(123.to_big_i)\n      BigInt.from_digits({3, 2, 1}, base: 16).should eq(0x123.to_big_i)\n      BigInt.from_digits((0xfa..0xff), base: 256).should eq(0xfffefdfcfbfa.to_big_i)\n      BigInt.from_digits([UInt128::MAX, UInt128::MAX, UInt128::MAX, UInt128::MAX], base: 2.to_big_i ** 128).should eq(2.to_big_i ** 512 - 1)\n      BigInt.from_digits(Deque{1, 0.to_big_i, 0, 1.to_big_i}, base: 2).should eq(9.to_big_i)\n    end\n\n    it \"raises for base less than 2\" do\n      [-1, 0, 1].each do |base|\n        expect_raises(ArgumentError, \"Invalid base #{base}\") do\n          BigInt.from_digits([1, 2, 3], base)\n        end\n      end\n    end\n\n    it \"raises for digits greater than base\" do\n      expect_raises(ArgumentError, \"Invalid digit 2 for base 2\") do\n        BigInt.from_digits([1, 0, 2], 2)\n      end\n\n      expect_raises(ArgumentError, \"Invalid digit 10 for base 2\") do\n        BigInt.from_digits([1, 0, 10], 2)\n      end\n    end\n\n    it \"raises for negative digits\" do\n      expect_raises(ArgumentError, \"Invalid digit -1\") do\n        BigInt.from_digits([1, 2, -1])\n      end\n    end\n  end\n\n  it \"compares\" do\n    1.to_big_i.should eq(1.to_big_i)\n    1.to_big_i.should eq(1)\n    1.to_big_i.should eq(1_u8)\n\n    [3.to_big_i, 2.to_big_i, 10.to_big_i, 4, 8_u8].sort.should eq([2, 3, 4, 8, 10])\n  end\n\n  it \"compares against float\" do\n    1.to_big_i.should eq(1.0)\n    1.to_big_i.should eq(1.0_f32)\n    1.to_big_i.should_not eq(1.1)\n    1.0.should eq(1.to_big_i)\n    1.0_f32.should eq(1.to_big_i)\n    1.1.should_not eq(1.to_big_i)\n\n    [1.1, 1.to_big_i, 3.to_big_i, 2.2].sort.should eq([1, 1.1, 2.2, 3])\n\n    (1.to_big_i <=> Float64::NAN).should be_nil\n    (1.to_big_i <=> Float32::NAN).should be_nil\n    (Float64::NAN <=> 1.to_big_i).should be_nil\n    (Float32::NAN <=> 1.to_big_i).should be_nil\n\n    typeof(1.to_big_i <=> Float64::NAN).should eq(Int32?)\n    typeof(1.to_big_i <=> Float32::NAN).should eq(Int32?)\n    typeof(Float64::NAN <=> 1.to_big_i).should eq(Int32?)\n    typeof(Float32::NAN <=> 1.to_big_i).should eq(Int32?)\n\n    typeof(1.to_big_i <=> 1.to_big_f).should eq(Int32)\n    typeof(1.to_big_f <=> 1.to_big_i).should eq(Int32)\n  end\n\n  it \"divides and calculates the modulo\" do\n    11.to_big_i.divmod(3.to_big_i).should eq({3, 2})\n    11.to_big_i.divmod(-3.to_big_i).should eq({-4, -1})\n\n    11.to_big_i.divmod(3_i32).should eq({3, 2})\n    11.to_big_i.divmod(-3_i32).should eq({-4, -1})\n\n    10.to_big_i.divmod(2).should eq({5, 0})\n    11.to_big_i.divmod(2).should eq({5, 1})\n\n    10.to_big_i.divmod(2.to_big_i).should eq({5, 0})\n    11.to_big_i.divmod(2.to_big_i).should eq({5, 1})\n\n    10.to_big_i.divmod(-2).should eq({-5, 0})\n    11.to_big_i.divmod(-2).should eq({-6, -1})\n\n    -10.to_big_i.divmod(2).should eq({-5, 0})\n    -11.to_big_i.divmod(2).should eq({-6, 1})\n\n    -10.to_big_i.divmod(-2).should eq({5, 0})\n    -11.to_big_i.divmod(-2).should eq({5, -1})\n  end\n\n  it \"adds\" do\n    (1.to_big_i + 2.to_big_i).should eq(3.to_big_i)\n    (1.to_big_i + 2).should eq(3.to_big_i)\n    (1.to_big_i + 2_u8).should eq(3.to_big_i)\n    (5.to_big_i + (-2_i64)).should eq(3.to_big_i)\n    (5.to_big_i + Int64::MAX).should be > Int64::MAX.to_big_i\n    (5.to_big_i + Int64::MAX).should eq(Int64::MAX.to_big_i + 5)\n\n    (2 + 1.to_big_i).should eq(3.to_big_i)\n\n    (1.to_big_i &+ 2.to_big_i).should eq(3.to_big_i)\n    (1.to_big_i &+ 2).should eq(3.to_big_i)\n    (1.to_big_i &+ 2_u8).should eq(3.to_big_i)\n    (5.to_big_i &+ (-2_i64)).should eq(3.to_big_i)\n    (5.to_big_i &+ Int64::MAX).should be > Int64::MAX.to_big_i\n    (5.to_big_i &+ Int64::MAX).should eq(Int64::MAX.to_big_i &+ 5)\n\n    (2 &+ 1.to_big_i).should eq(3.to_big_i)\n  end\n\n  it \"subs\" do\n    (5.to_big_i - 2.to_big_i).should eq(3.to_big_i)\n    (5.to_big_i - 2).should eq(3.to_big_i)\n    (5.to_big_i - 2_u8).should eq(3.to_big_i)\n    (5.to_big_i - (-2_i64)).should eq(7.to_big_i)\n    (-5.to_big_i - Int64::MAX).should be < -Int64::MAX.to_big_i\n    (-5.to_big_i - Int64::MAX).should eq(-Int64::MAX.to_big_i - 5)\n\n    (5 - 1.to_big_i).should eq(4.to_big_i)\n    (-5 - 1.to_big_i).should eq(-6.to_big_i)\n\n    (5.to_big_i &- 2.to_big_i).should eq(3.to_big_i)\n    (5.to_big_i &- 2).should eq(3.to_big_i)\n    (5.to_big_i &- 2_u8).should eq(3.to_big_i)\n    (5.to_big_i &- (-2_i64)).should eq(7.to_big_i)\n    (-5.to_big_i &- Int64::MAX).should be < -Int64::MAX.to_big_i\n    (-5.to_big_i &- Int64::MAX).should eq(-Int64::MAX.to_big_i &- 5)\n\n    (5 &- 1.to_big_i).should eq(4.to_big_i)\n    (-5 &- 1.to_big_i).should eq(-6.to_big_i)\n  end\n\n  it \"negates\" do\n    (-(-123.to_big_i)).should eq(123.to_big_i)\n  end\n\n  it \"multiplies\" do\n    (2.to_big_i * 3.to_big_i).should eq(6.to_big_i)\n    (2.to_big_i * 3).should eq(6.to_big_i)\n    (2.to_big_i * 3_u8).should eq(6.to_big_i)\n    (3 * 2.to_big_i).should eq(6.to_big_i)\n    (3_u8 * 2.to_big_i).should eq(6.to_big_i)\n    (2.to_big_i * Int64::MAX).should eq(2.to_big_i * Int64::MAX.to_big_i)\n\n    (2.to_big_i &* 3.to_big_i).should eq(6.to_big_i)\n    (2.to_big_i &* 3).should eq(6.to_big_i)\n    (2.to_big_i &* 3_u8).should eq(6.to_big_i)\n    (3 &* 2.to_big_i).should eq(6.to_big_i)\n    (3_u8 &* 2.to_big_i).should eq(6.to_big_i)\n    (2.to_big_i &* Int64::MAX).should eq(2.to_big_i &* Int64::MAX.to_big_i)\n  end\n\n  it \"gets absolute value\" do\n    (-10.to_big_i.abs).should eq(10.to_big_i)\n  end\n\n  it \"gets factorial value\" do\n    0.to_big_i.factorial.should eq(1.to_big_i)\n    5.to_big_i.factorial.should eq(120.to_big_i)\n    100.to_big_i.factorial.should eq(\"93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000\".to_big_i)\n  end\n\n  it \"raises if factorial of negative\" do\n    expect_raises ArgumentError do\n      -1.to_big_i.factorial\n    end\n\n    expect_raises ArgumentError do\n      \"-93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000\".to_big_i.factorial\n    end\n  end\n\n  it \"raises if factorial of 2^64\" do\n    expect_raises ArgumentError do\n      (LibGMP::UI::MAX.to_big_i + 1).factorial\n    end\n  end\n\n  it \"divides\" do\n    (10.to_big_i / 3.to_big_i).should be_close(3.3333.to_big_f, 0.0001)\n    (10.to_big_i / 3).should be_close(3.3333.to_big_f, 0.0001)\n    (10 / 3.to_big_i).should be_close(3.3333.to_big_f, 0.0001)\n    ((Int64::MAX.to_big_i * 2.to_big_i) / Int64::MAX).should eq(2.to_big_i)\n  end\n\n  it \"divides\" do\n    (10.to_big_i // 3.to_big_i).should eq(3.to_big_i)\n    (10.to_big_i // 3).should eq(3.to_big_i)\n    (10 // 3.to_big_i).should eq(3.to_big_i)\n    ((Int64::MAX.to_big_i * 2.to_big_i) // Int64::MAX).should eq(2.to_big_i)\n  end\n\n  it \"divides with negative numbers\" do\n    (7.to_big_i / 2).should eq(3.5.to_big_f)\n    (7.to_big_i / 2.to_big_i).should eq(3.5.to_big_f)\n    (7.to_big_i / -2).should eq(-3.5.to_big_f)\n    (7.to_big_i / -2.to_big_i).should eq(-3.5.to_big_f)\n    (-7.to_big_i / 2).should eq(-3.5.to_big_f)\n    (-7.to_big_i / 2.to_big_i).should eq(-3.5.to_big_f)\n    (-7.to_big_i / -2).should eq(3.5.to_big_f)\n    (-7.to_big_i / -2.to_big_i).should eq(3.5.to_big_f)\n\n    (-6.to_big_i / 2).should eq(-3.to_big_f)\n    (6.to_big_i / -2).should eq(-3.to_big_f)\n    (-6.to_big_i / -2).should eq(3.to_big_f)\n  end\n\n  it \"divides with negative numbers\" do\n    (7.to_big_i // 2).should eq(3.to_big_i)\n    (7.to_big_i // 2.to_big_i).should eq(3.to_big_i)\n    (7.to_big_i // -2).should eq(-4.to_big_i)\n    (7.to_big_i // -2.to_big_i).should eq(-4.to_big_i)\n    (-7.to_big_i // 2).should eq(-4.to_big_i)\n    (-7.to_big_i // 2.to_big_i).should eq(-4.to_big_i)\n    (-7.to_big_i // -2).should eq(3.to_big_i)\n    (-7.to_big_i // -2.to_big_i).should eq(3.to_big_i)\n\n    (-6.to_big_i // 2).should eq(-3.to_big_i)\n    (6.to_big_i // -2).should eq(-3.to_big_i)\n    (-6.to_big_i // -2).should eq(3.to_big_i)\n  end\n\n  it \"tdivs\" do\n    5.to_big_i.tdiv(3).should eq(1)\n    -5.to_big_i.tdiv(3).should eq(-1)\n    5.to_big_i.tdiv(-3).should eq(-1)\n    -5.to_big_i.tdiv(-3).should eq(1)\n  end\n\n  it \"does modulo\" do\n    (10.to_big_i % 3.to_big_i).should eq(1.to_big_i)\n    (10.to_big_i % 3).should eq(1.to_big_i)\n    (10.to_big_i % 3u8).should eq(1.to_big_i)\n    (10 % 3.to_big_i).should eq(1.to_big_i)\n  end\n\n  it \"does modulo with negative numbers\" do\n    (7.to_big_i % 2).should eq(1.to_big_i)\n    (7.to_big_i % 2.to_big_i).should eq(1.to_big_i)\n    (7.to_big_i % -2).should eq(-1.to_big_i)\n    (7.to_big_i % -2.to_big_i).should eq(-1.to_big_i)\n    (-7.to_big_i % 2).should eq(1.to_big_i)\n    (-7.to_big_i % 2.to_big_i).should eq(1.to_big_i)\n    (-7.to_big_i % -2).should eq(-1.to_big_i)\n    (-7.to_big_i % -2.to_big_i).should eq(-1.to_big_i)\n\n    (6.to_big_i % 2).should eq(0.to_big_i)\n    (6.to_big_i % -2).should eq(0.to_big_i)\n    (-6.to_big_i % 2).should eq(0.to_big_i)\n    (-6.to_big_i % -2).should eq(0.to_big_i)\n  end\n\n  it \"does remainder with negative numbers\" do\n    5.to_big_i.remainder(3).should eq(2)\n    -5.to_big_i.remainder(3).should eq(-2)\n    5.to_big_i.remainder(-3).should eq(2)\n    -5.to_big_i.remainder(-3).should eq(-2)\n  end\n\n  it \"#bit\" do\n    x = 123.to_big_i\n    x.bit(-10.to_big_i ** 99).should eq(0)\n    x.bit(-(2.to_big_i ** 64)).should eq(0)\n    x.bit(-1).should eq(0)\n    x.bit(0).should eq(1)\n    x.bit(2).should eq(0)\n    x.bit(3).should eq(1)\n    x.bit(6).should eq(1)\n    x.bit(7).should eq(0)\n    x.bit(64).should eq(0)\n    x.bit(2.to_big_i ** 64).should eq(0)\n    x.bit(10.to_big_i ** 99).should eq(0)\n\n    x = ~(123.to_big_i)\n    x.bit(-10.to_big_i ** 99).should eq(0)\n    x.bit(-(2.to_big_i ** 64)).should eq(0)\n    x.bit(-1).should eq(0)\n    x.bit(0).should eq(0)\n    x.bit(2).should eq(1)\n    x.bit(3).should eq(0)\n    x.bit(6).should eq(0)\n    x.bit(7).should eq(1)\n    x.bit(64).should eq(1)\n    x.bit(2.to_big_i ** 64).should eq(1)\n    x.bit(10.to_big_i ** 99).should eq(1)\n  end\n\n  it \"does bitwise and\" do\n    (123.to_big_i & 321).should eq(65)\n    (BigInt.new(\"96238761238973286532\") & 86325735648).should eq(69124358272)\n  end\n\n  it \"does bitwise or\" do\n    (123.to_big_i | 4).should eq(127)\n    (BigInt.new(\"96238761238986532\") | 8632573).should eq(96238761247506429)\n  end\n\n  it \"does bitwise xor\" do\n    (123.to_big_i ^ 50).should eq(73)\n    (BigInt.new(\"96238761238986532\") ^ 8632573).should eq(96238761247393753)\n  end\n\n  it \"does bitwise not\" do\n    (~123).should eq(-124)\n\n    a = BigInt.new(\"192623876123689865327\")\n    b = BigInt.new(\"-192623876123689865328\")\n    (~a).should eq(b)\n  end\n\n  it \"does bitwise right shift\" do\n    (123.to_big_i >> 4).should eq(7)\n    (123456.to_big_i >> 8).should eq(482)\n  end\n\n  it \"does bitwise left shift\" do\n    (123.to_big_i << 4).should eq(1968)\n    (123456.to_big_i << 8).should eq(31604736)\n  end\n\n  it \"raises if divides by zero\" do\n    expect_raises DivisionByZeroError do\n      10.to_big_i / 0.to_big_i\n    end\n\n    expect_raises DivisionByZeroError do\n      10.to_big_i / 0\n    end\n\n    expect_raises DivisionByZeroError do\n      10 / 0.to_big_i\n    end\n  end\n\n  it \"raises if divides by zero\" do\n    expect_raises DivisionByZeroError do\n      10.to_big_i // 0.to_big_i\n    end\n\n    expect_raises DivisionByZeroError do\n      10.to_big_i // 0\n    end\n\n    expect_raises DivisionByZeroError do\n      10 // 0.to_big_i\n    end\n  end\n\n  it \"raises if mods by zero\" do\n    expect_raises DivisionByZeroError do\n      10.to_big_i % 0.to_big_i\n    end\n\n    expect_raises DivisionByZeroError do\n      10.to_big_i % 0\n    end\n\n    expect_raises DivisionByZeroError do\n      10 % 0.to_big_i\n    end\n  end\n\n  it \"exponentiates\" do\n    result = (2.to_big_i ** 1000)\n    result.should be_a(BigInt)\n    result.to_s.should eq(\"10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376\")\n  end\n\n  describe \"#to_s\" do\n    context \"base and upcase parameters\" do\n      a = BigInt.new(\"1234567890123456789\")\n      it_converts_to_s a, \"1000100100010000100001111010001111101111010011000000100010101\", base: 2\n      it_converts_to_s a, \"112210f47de98115\", base: 16\n      it_converts_to_s a, \"112210F47DE98115\", base: 16, upcase: true\n      it_converts_to_s a, \"128gguhuuj08l\", base: 32\n      it_converts_to_s a, \"128GGUHUUJ08L\", base: 32, upcase: true\n      it_converts_to_s a, \"1tckI1NfUnH\", base: 62\n\n      # ensure case is same as for primitive integers\n      it_converts_to_s 10.to_big_i, 10.to_s(62), base: 62\n\n      it_converts_to_s (-a), \"-1000100100010000100001111010001111101111010011000000100010101\", base: 2\n      it_converts_to_s (-a), \"-112210f47de98115\", base: 16\n      it_converts_to_s (-a), \"-112210F47DE98115\", base: 16, upcase: true\n      it_converts_to_s (-a), \"-128gguhuuj08l\", base: 32\n      it_converts_to_s (-a), \"-128GGUHUUJ08L\", base: 32, upcase: true\n      it_converts_to_s (-a), \"-1tckI1NfUnH\", base: 62\n\n      it_converts_to_s 16.to_big_i ** 1000, \"1#{\"0\" * 1000}\", base: 16\n\n      it \"raises on base 1\" do\n        expect_raises(ArgumentError, \"Invalid base 1\") { a.to_s(1) }\n        expect_raises(ArgumentError, \"Invalid base 1\") { a.to_s(IO::Memory.new, 1) }\n      end\n\n      it \"raises on base 37\" do\n        expect_raises(ArgumentError, \"Invalid base 37\") { a.to_s(37) }\n        expect_raises(ArgumentError, \"Invalid base 37\") { a.to_s(IO::Memory.new, 37) }\n      end\n\n      it \"raises on base 62 with upcase\" do\n        expect_raises(ArgumentError, \"upcase must be false for base 62\") { a.to_s(62, upcase: true) }\n        expect_raises(ArgumentError, \"upcase must be false for base 62\") { a.to_s(IO::Memory.new, 62, upcase: true) }\n      end\n    end\n\n    context \"precision parameter\" do\n      it_converts_to_s 0.to_big_i, \"\", precision: 0\n      it_converts_to_s 0.to_big_i, \"0\", precision: 1\n      it_converts_to_s 0.to_big_i, \"00\", precision: 2\n      it_converts_to_s 0.to_big_i, \"00000\", precision: 5\n      it_converts_to_s 0.to_big_i, \"0\" * 200, precision: 200\n\n      it_converts_to_s 1.to_big_i, \"1\", precision: 0\n      it_converts_to_s 1.to_big_i, \"1\", precision: 1\n      it_converts_to_s 1.to_big_i, \"01\", precision: 2\n      it_converts_to_s 1.to_big_i, \"00001\", precision: 5\n      it_converts_to_s 1.to_big_i, \"#{\"0\" * 199}1\", precision: 200\n\n      it_converts_to_s 2.to_big_i, \"2\", precision: 0\n      it_converts_to_s 2.to_big_i, \"2\", precision: 1\n      it_converts_to_s 2.to_big_i, \"02\", precision: 2\n      it_converts_to_s 2.to_big_i, \"00002\", precision: 5\n      it_converts_to_s 2.to_big_i, \"#{\"0\" * 199}2\", precision: 200\n\n      it_converts_to_s (-1).to_big_i, \"-1\", precision: 0\n      it_converts_to_s (-1).to_big_i, \"-1\", precision: 1\n      it_converts_to_s (-1).to_big_i, \"-01\", precision: 2\n      it_converts_to_s (-1).to_big_i, \"-00001\", precision: 5\n      it_converts_to_s (-1).to_big_i, \"-#{\"0\" * 199}1\", precision: 200\n\n      it_converts_to_s 85.to_big_i, \"85\", precision: 0\n      it_converts_to_s 85.to_big_i, \"85\", precision: 1\n      it_converts_to_s 85.to_big_i, \"85\", precision: 2\n      it_converts_to_s 85.to_big_i, \"085\", precision: 3\n      it_converts_to_s 85.to_big_i, \"0085\", precision: 4\n      it_converts_to_s 85.to_big_i, \"00085\", precision: 5\n      it_converts_to_s 85.to_big_i, \"#{\"0\" * 198}85\", precision: 200\n\n      it_converts_to_s (-85).to_big_i, \"-85\", precision: 0\n      it_converts_to_s (-85).to_big_i, \"-85\", precision: 1\n      it_converts_to_s (-85).to_big_i, \"-85\", precision: 2\n      it_converts_to_s (-85).to_big_i, \"-085\", precision: 3\n      it_converts_to_s (-85).to_big_i, \"-0085\", precision: 4\n      it_converts_to_s (-85).to_big_i, \"-00085\", precision: 5\n      it_converts_to_s (-85).to_big_i, \"-#{\"0\" * 198}85\", precision: 200\n\n      it_converts_to_s 123.to_big_i, \"123\", precision: 0\n      it_converts_to_s 123.to_big_i, \"123\", precision: 1\n      it_converts_to_s 123.to_big_i, \"123\", precision: 2\n      it_converts_to_s 123.to_big_i, \"00123\", precision: 5\n      it_converts_to_s 123.to_big_i, \"#{\"0\" * 197}123\", precision: 200\n\n      a = 2.to_big_i ** 1024 - 1\n      it_converts_to_s a, \"#{\"1\" * 1024}\", base: 2, precision: 1023\n      it_converts_to_s a, \"#{\"1\" * 1024}\", base: 2, precision: 1024\n      it_converts_to_s a, \"0#{\"1\" * 1024}\", base: 2, precision: 1025\n      it_converts_to_s a, \"#{\"0\" * 976}#{\"1\" * 1024}\", base: 2, precision: 2000\n\n      it_converts_to_s (-a), \"-#{\"1\" * 1024}\", base: 2, precision: 1023\n      it_converts_to_s (-a), \"-#{\"1\" * 1024}\", base: 2, precision: 1024\n      it_converts_to_s (-a), \"-0#{\"1\" * 1024}\", base: 2, precision: 1025\n      it_converts_to_s (-a), \"-#{\"0\" * 976}#{\"1\" * 1024}\", base: 2, precision: 2000\n    end\n  end\n\n  it \"does to_big_f\" do\n    a = BigInt.new(\"1234567890123456789\")\n    a.to_big_f.should eq(BigFloat.new(\"1234567890123456789.0\"))\n  end\n\n  describe \"#inspect\" do\n    it { \"2\".to_big_i.inspect.should eq(\"2\") }\n  end\n\n  it \"does gcd and lcm\" do\n    # 3 primes\n    a = BigInt.new(\"48112959837082048697\")\n    b = BigInt.new(\"12764787846358441471\")\n    c = BigInt.new(\"36413321723440003717\")\n    abc = a * b * c\n    a_17 = a * 17\n\n    (abc * b).gcd(abc * c).should eq(abc)\n    abc.gcd(a_17).should eq(a)\n    (abc * b).lcm(abc * c).should eq(abc * b * c)\n    (abc * b).gcd(abc * c).should be_a(BigInt)\n\n    (a_17).gcd(17).should eq(17)\n    (-a_17).gcd(17).should eq(17)\n    (17).gcd(a_17).should eq(17)\n    (17).gcd(-a_17).should eq(17)\n\n    (a_17).lcm(17).should eq(a_17)\n    (-a_17).lcm(17).should eq(a_17)\n    (17).lcm(a_17).should eq(a_17)\n    (17).lcm(-a_17).should eq(a_17)\n\n    (a_17).gcd(17).should be_a(Int::Unsigned)\n  end\n\n  it \"can use Number::[]\" do\n    a = BigInt[146, \"3464\", 97, \"545\"]\n    b = [BigInt.new(146), BigInt.new(3464), BigInt.new(97), BigInt.new(545)]\n    a.should eq(b)\n  end\n\n  describe \"#to_i\" do\n    it \"converts to Int32\" do\n      BigInt.new(1234567890).to_i.should(be_a(Int32)).should eq(1234567890)\n      expect_raises(OverflowError) { BigInt.new(2147483648).to_i }\n      expect_raises(OverflowError) { BigInt.new(-2147483649).to_i }\n    end\n  end\n\n  describe \"#to_i!\" do\n    it \"converts to Int32\" do\n      BigInt.new(1234567890).to_i!.should(be_a(Int32)).should eq(1234567890)\n      BigInt.new(2147483648).to_i!.should eq(Int32::MIN)\n      BigInt.new(-2147483649).to_i!.should eq(Int32::MAX)\n    end\n  end\n\n  describe \"#to_u\" do\n    it \"converts to UInt32\" do\n      BigInt.new(1234567890).to_u.should(be_a(UInt32)).should eq(1234567890_u32)\n      expect_raises(OverflowError) { BigInt.new(4294967296).to_u }\n      expect_raises(OverflowError) { BigInt.new(-1).to_u }\n    end\n  end\n\n  describe \"#to_u!\" do\n    it \"converts to UInt32\" do\n      BigInt.new(1234567890).to_u!.should(be_a(UInt32)).should eq(1234567890_u32)\n      BigInt.new(4294967296).to_u!.should eq(0_u32)\n      BigInt.new(-1).to_u!.should eq(UInt32::MAX)\n    end\n  end\n\n  {% for n in [8, 16, 32, 64, 128] %}\n    describe \"#to_u{{n}}\" do\n      it \"converts to UInt{{n}}\" do\n        (0..{{n - 1}}).each do |i|\n          (1.to_big_i << i).to_u{{n}}.should eq(UInt{{n}}.new(1) << i)\n        end\n\n        UInt{{n}}::MIN.to_big_i.to_u{{n}}.should eq(UInt{{n}}::MIN)\n        UInt{{n}}::MAX.to_big_i.to_u{{n}}.should eq(UInt{{n}}::MAX)\n      end\n\n      it \"raises OverflowError\" do\n        expect_raises(OverflowError) { (1.to_big_i << {{n}}).to_u{{n}} }\n        expect_raises(OverflowError) { (-1.to_big_i).to_u{{n}} }\n        expect_raises(OverflowError) { (-1.to_big_i << {{n}}).to_u{{n}} }\n      end\n    end\n\n    describe \"#to_i{{n}}\" do\n      it \"converts to Int{{n}}\" do\n        (0..{{n - 2}}).each do |i|\n          (1.to_big_i << i).to_i{{n}}.should eq(Int{{n}}.new(1) << i)\n          (-1.to_big_i << i).to_i{{n}}.should eq(Int{{n}}.new(-1) << i)\n        end\n\n        Int{{n}}.zero.to_big_i.to_i{{n}}.should eq(Int{{n}}.zero)\n        Int{{n}}::MAX.to_big_i.to_i{{n}}.should eq(Int{{n}}::MAX)\n        Int{{n}}::MIN.to_big_i.to_i{{n}}.should eq(Int{{n}}::MIN)\n      end\n\n      it \"raises OverflowError\" do\n        expect_raises(OverflowError) { (Int{{n}}::MAX.to_big_i + 1).to_i{{n}} }\n        expect_raises(OverflowError) { (Int{{n}}::MIN.to_big_i - 1).to_i{{n}} }\n        expect_raises(OverflowError) { (1.to_big_i << {{n}}).to_i{{n}} }\n        expect_raises(OverflowError) { (-1.to_big_i << {{n}}).to_i{{n}} }\n      end\n    end\n\n    describe \"#to_u{{n}}!\" do\n      it \"converts to UInt{{n}}\" do\n        (0..{{n - 1}}).each do |i|\n          (1.to_big_i << i).to_u{{n}}!.should eq(UInt{{n}}.new(1) << i)\n        end\n\n        UInt{{n}}::MAX.to_big_i.to_u{{n}}!.should eq(UInt{{n}}::MAX)\n      end\n\n      it \"converts modulo (2 ** {{n}})\" do\n        (1.to_big_i << {{n}}).to_u{{n}}!.should eq(UInt{{n}}.new(0))\n        (-1.to_big_i).to_u{{n}}!.should eq(UInt{{n}}::MAX)\n        (-1.to_big_i << {{n}}).to_u{{n}}!.should eq(UInt{{n}}.new(0))\n        (123.to_big_i - (1.to_big_i << {{n}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))\n        (123.to_big_i + (1.to_big_i << {{n}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))\n        (123.to_big_i - (1.to_big_i << {{n + 2}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))\n        (123.to_big_i + (1.to_big_i << {{n + 2}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))\n      end\n    end\n\n    describe \"#to_i{{n}}!\" do\n      it \"converts to Int{{n}}\" do\n        (0..126).each do |i|\n          (1.to_big_i << i).to_i{{n}}!.should eq(Int{{n}}.new(1) << i)\n          (-1.to_big_i << i).to_i{{n}}!.should eq(Int{{n}}.new(-1) << i)\n        end\n\n        Int{{n}}::MAX.to_big_i.to_i{{n}}!.should eq(Int{{n}}::MAX)\n        Int{{n}}::MIN.to_big_i.to_i{{n}}!.should eq(Int{{n}}::MIN)\n      end\n\n      it \"converts modulo (2 ** {{n}})\" do\n        (1.to_big_i << {{n - 1}}).to_i{{n}}!.should eq(Int{{n}}::MIN)\n        (1.to_big_i << {{n}}).to_i{{n}}!.should eq(Int{{n}}.new(0))\n        (-1.to_big_i << {{n}}).to_i{{n}}!.should eq(Int{{n}}.new(0))\n        (123.to_big_i - (1.to_big_i << {{n}})).to_i{{n}}!.should eq(Int{{n}}.new(123))\n        (123.to_big_i + (1.to_big_i << {{n}})).to_i{{n}}!.should eq(Int{{n}}.new(123))\n        (123.to_big_i - (1.to_big_i << {{n + 2}})).to_i{{n}}!.should eq(Int{{n}}.new(123))\n        (123.to_big_i + (1.to_big_i << {{n + 2}})).to_i{{n}}!.should eq(Int{{n}}.new(123))\n      end\n    end\n  {% end %}\n\n  it \"does String#to_big_i\" do\n    \"123456789123456789\".to_big_i.should eq(BigInt.new(\"123456789123456789\"))\n    \"abcabcabcabcabcabc\".to_big_i(base: 16).should eq(BigInt.new(\"3169001976782853491388\"))\n  end\n\n  it \"does popcount\" do\n    5.to_big_i.popcount.should eq(2)\n  end\n\n  it \"#trailing_zeros_count\" do\n    \"00000000000000001000000000001000\".to_big_i(base: 2).trailing_zeros_count.should eq(3)\n  end\n\n  it \"#hash\" do\n    b1 = 5.to_big_i\n    b2 = 5.to_big_i\n    b3 = -6.to_big_i\n\n    b1.hash.should eq(b2.hash)\n    b1.hash.should_not eq(b3.hash)\n\n    b3.hash.should eq((-6).hash)\n  end\n\n  it \"clones\" do\n    x = 1.to_big_i\n    x.clone.should eq(x)\n  end\n\n  describe \"#humanize_bytes\" do\n    it { BigInt.new(\"1180591620717411303424\").humanize_bytes.should eq(\"1.0ZiB\") }\n    it { BigInt.new(\"1208925819614629174706176\").humanize_bytes.should eq(\"1.0YiB\") }\n  end\n\n  it \"has unsafe_shr (#8691)\" do\n    BigInt.new(8).unsafe_shr(1).should eq(4)\n  end\n\n  describe \"#digits\" do\n    it \"works for positive numbers or zero\" do\n      0.to_big_i.digits.should eq([0])\n      1.to_big_i.digits.should eq([1])\n      10.to_big_i.digits.should eq([0, 1])\n      123.to_big_i.digits.should eq([3, 2, 1])\n      123456789.to_big_i.digits.should eq([9, 8, 7, 6, 5, 4, 3, 2, 1])\n    end\n\n    it \"works with a base\" do\n      123.to_big_i.digits(16).should eq([11, 7])\n    end\n\n    it \"raises for invalid base\" do\n      [1, 0, -1].each do |base|\n        expect_raises(ArgumentError, \"Invalid base #{base}\") do\n          123.to_big_i.digits(base)\n        end\n      end\n    end\n\n    it \"raises for negative numbers\" do\n      expect_raises(ArgumentError, \"Can't request digits of negative number\") do\n        -123.to_big_i.digits\n      end\n    end\n  end\n\n  describe \"#divisible_by?\" do\n    it { 0.to_big_i.divisible_by?(0).should be_true }\n    it { 0.to_big_i.divisible_by?(1).should be_true }\n    it { 0.to_big_i.divisible_by?(-1).should be_true }\n    it { 0.to_big_i.divisible_by?(0.to_big_i).should be_true }\n    it { 0.to_big_i.divisible_by?(1.to_big_i).should be_true }\n    it { 0.to_big_i.divisible_by?((-1).to_big_i).should be_true }\n\n    it { 135.to_big_i.divisible_by?(0).should be_false }\n    it { 135.to_big_i.divisible_by?(1).should be_true }\n    it { 135.to_big_i.divisible_by?(2).should be_false }\n    it { 135.to_big_i.divisible_by?(3).should be_true }\n    it { 135.to_big_i.divisible_by?(4).should be_false }\n    it { 135.to_big_i.divisible_by?(5).should be_true }\n    it { 135.to_big_i.divisible_by?(135).should be_true }\n    it { 135.to_big_i.divisible_by?(270).should be_false }\n\n    it { \"100000000000000000000000000000000\".to_big_i.divisible_by?(\"4294967296\".to_big_i).should be_true }\n    it { \"100000000000000000000000000000000\".to_big_i.divisible_by?(\"8589934592\".to_big_i).should be_false }\n    it { \"100000000000000000000000000000000\".to_big_i.divisible_by?(\"23283064365386962890625\".to_big_i).should be_true }\n    it { \"100000000000000000000000000000000\".to_big_i.divisible_by?(\"116415321826934814453125\".to_big_i).should be_false }\n  end\nend\n\ndescribe \"BigInt Math\" do\n  it \"sqrt\" do\n    Math.sqrt(BigInt.new(\"1\" + \"0\"*48)).should eq(BigFloat.new(\"1\" + \"0\"*24))\n  end\n\n  it \"isqrt\" do\n    Math.isqrt(BigInt.new(\"1\" + \"0\"*48)).should eq(BigInt.new(\"1\" + \"0\"*24))\n  end\n\n  it \"pw2ceil\" do\n    Math.pw2ceil(\"-100000000000000000000000000000000\".to_big_i).should eq(1.to_big_i)\n    Math.pw2ceil(-1234567.to_big_i).should eq(1.to_big_i)\n    Math.pw2ceil(-1.to_big_i).should eq(1.to_big_i)\n    Math.pw2ceil(0.to_big_i).should eq(1.to_big_i)\n    Math.pw2ceil(1.to_big_i).should eq(1.to_big_i)\n    Math.pw2ceil(2.to_big_i).should eq(2.to_big_i)\n    Math.pw2ceil(3.to_big_i).should eq(4.to_big_i)\n    Math.pw2ceil(4.to_big_i).should eq(4.to_big_i)\n    Math.pw2ceil(5.to_big_i).should eq(8.to_big_i)\n    Math.pw2ceil(32.to_big_i).should eq(32.to_big_i)\n    Math.pw2ceil(33.to_big_i).should eq(64.to_big_i)\n    Math.pw2ceil(64.to_big_i).should eq(64.to_big_i)\n    Math.pw2ceil(2.to_big_i ** 12345 - 1).should eq(2.to_big_i ** 12345)\n    Math.pw2ceil(2.to_big_i ** 12345).should eq(2.to_big_i ** 12345)\n    Math.pw2ceil(2.to_big_i ** 12345 + 1).should eq(2.to_big_i ** 12346)\n  end\nend\n"
  },
  {
    "path": "spec/std/big/big_rational_spec.cr",
    "content": "require \"spec\"\nrequire \"big\"\n\nprivate def br(n, d)\n  BigRational.new(n, d)\nend\n\nprivate def test_greater(val, other, *, file = __FILE__, line = __LINE__)\n  val.should be > other, file: file, line: line\n  other.should_not be > val, file: file, line: line\n  (val <=> other).should_not(be_nil).should be > 0, file: file, line: line\n  (other <=> val).should_not(be_nil).should be < 0, file: file, line: line\nend\n\nprivate def test_equal(val, other, *, file = __FILE__, line = __LINE__)\n  (val == other).should be_true, file: file, line: line\n  (other == val).should be_true, file: file, line: line\n  (val <=> other).should eq(0), file: file, line: line\n  (other <=> val).should eq(0), file: file, line: line\nend\n\nprivate def test_less(val, other, *, file = __FILE__, line = __LINE__)\n  val.should be < other, file: file, line: line\n  other.should_not be < val, file: file, line: line\n  (val <=> other).should_not(be_nil).should be < 0, file: file, line: line\n  (other <=> val).should_not(be_nil).should be > 0, file: file, line: line\nend\n\nprivate def test_comp(val, less, equal, greater, *, file = __FILE__, line = __LINE__)\n  test_greater(val, less, file: file, line: line)\n  test_equal(val, equal, file: file, line: line)\n  test_less(val, greater, file: file, line: line)\nend\n\ndescribe BigRational do\n  describe \".new\" do\n    it \"initialize\" do\n      BigRational.new(BigInt.new(10), BigInt.new(3))\n        .should eq(BigRational.new(10, 3))\n\n      expect_raises(DivisionByZeroError) do\n        BigRational.new(BigInt.new(2), BigInt.new(0))\n      end\n\n      expect_raises(DivisionByZeroError) do\n        BigRational.new(2, 0)\n      end\n    end\n\n    it \"initializes from BigFloat with high precision\" do\n      (0..12).each do |i|\n        bf = BigFloat.new(2.0, precision: 64) ** 64 + BigFloat.new(2.0, precision: 64) ** i\n        br = BigRational.new(bf)\n        br.should eq(bf)\n      end\n    end\n\n    it \"raises if creating from infinity\" do\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigRational.new(Float32::INFINITY) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigRational.new(Float64::INFINITY) }\n    end\n\n    it \"raises if creating from NaN\" do\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigRational.new(Float32::NAN) }\n      expect_raises(ArgumentError, \"Can only construct from a finite number\") { BigRational.new(Float64::NAN) }\n    end\n  end\n\n  it \"#numerator\" do\n    br(10, 3).numerator.should eq(BigInt.new(10))\n  end\n\n  it \"#denominator\" do\n    br(10, 3).denominator.should eq(BigInt.new(3))\n  end\n\n  it \"#to_s\" do\n    br(10, 3).to_s.should eq(\"10/3\")\n    br(90, 3).to_s.should eq(\"30\")\n    br(1, 98).to_s.should eq(\"1/98\")\n\n    r = BigRational.new(8243243, 562828882)\n    r.to_s(16).should eq(\"7dc82b/218c1652\")\n    r.to_s(36).should eq(\"4woiz/9b3djm\")\n  end\n\n  it \"#to_i\" do\n    br(10, 3).to_i.should eq(3)\n    br(90, 3).to_i.should eq(30)\n    br(1, 98).to_i.should eq(0)\n    br(-10, 3).to_i.should eq(-3)\n    expect_raises(OverflowError) { br(Int64::MAX, 1).to_i }\n  end\n\n  it \"#to_f64\" do\n    r = br(10, 3)\n    f = 10.to_f64 / 3.to_f64\n    r.to_f64.should be_close(f, 0.001)\n  end\n\n  it \"#to_f64!\" do\n    r = br(10, 3)\n    f = 10.to_f64 / 3.to_f64\n    r.to_f64!.should be_close(f, 0.001)\n  end\n\n  it \"#to_f\" do\n    r = br(10, 3)\n    f = 10.to_f64 / 3.to_f64\n    r.to_f.should be_close(f, 0.001)\n  end\n\n  it \"#to_f!\" do\n    r = br(10, 3)\n    f = 10.to_f64 / 3.to_f64\n    r.to_f!.should be_close(f, 0.001)\n  end\n\n  it \"#to_f32\" do\n    r = br(10, 3)\n    f = 10.to_f32 / 3.to_f32\n    r.to_f32.should be_close(f, 0.001)\n  end\n\n  it \"#to_f32!\" do\n    r = br(10, 3)\n    f = 10.to_f32 / 3.to_f32\n    r.to_f32!.should be_close(f, 0.001)\n  end\n\n  it \"#to_big_f\" do\n    r = br(10, 3)\n    f = 10.to_big_f / 3.to_big_f\n    r.to_big_f.should be_close(f, 0.001)\n  end\n\n  it \"#to_big_r\" do\n    r = br(10, 3)\n    r.to_big_r.should eq(r)\n  end\n\n  it \"Int#to_big_r\" do\n    3.to_big_r.should eq(br(3, 1))\n  end\n\n  it \"Float32#to_big_r\" do\n    0.3333333333333333333333_f32.to_big_r.should eq(br(11184811, 33554432))\n  end\n\n  it \"Float64#to_big_r\" do\n    0.3333333333333333333333_f64.to_big_r.should eq(br(6004799503160661, 18014398509481984))\n  end\n\n  it \"BigDecimal#to_big_r\" do\n    BigDecimal.new(\"1.123\").to_big_r.should eq(br(1123, 1000))\n  end\n\n  describe \"#<=>\" do\n    it \"BigRational and Comparable\" do\n      a = br(11, 3)\n      l = br(10, 3)\n      e = a\n      g = br(12, 3)\n\n      # verify things aren't swapped\n      [l, e, g].each { |o| (a <=> o).should eq(a.to_f <=> o.to_f) }\n\n      test_comp(a, l, e, g)\n    end\n\n    it \"Int and Comparable\" do\n      test_comp(br(10, 2), 4_i32, 5_i32, 6_i32)\n      test_comp(br(10, 2), 4_i64, 5_i64, 6_i64)\n    end\n\n    it \"BigInt and Comparable\" do\n      test_comp(br(10, 2), BigInt.new(4), BigInt.new(5), BigInt.new(6))\n    end\n\n    it \"Float and Comparable\" do\n      test_comp(br(10, 2), 4.0_f32, 5.0_f32, 6.0_f32)\n      test_comp(br(10, 2), 4.0_f64, 5.0_f64, 6.0_f64)\n    end\n\n    it \"BigFloat and Comparable\" do\n      test_greater(1.to_big_r + 0.5.to_big_r ** (BigFloat.default_precision + 66), 1.to_big_f)\n      test_less(1.to_big_r - 0.5.to_big_r ** (BigFloat.default_precision + 66), 1.to_big_f)\n    end\n\n    it \"compares against NaNs\" do\n      (1.to_big_r <=> Float64::NAN).should be_nil\n      (1.to_big_r <=> Float32::NAN).should be_nil\n      (Float64::NAN <=> 1.to_big_r).should be_nil\n      (Float32::NAN <=> 1.to_big_r).should be_nil\n\n      typeof(1.to_big_r <=> Float64::NAN).should eq(Int32?)\n      typeof(1.to_big_r <=> Float32::NAN).should eq(Int32?)\n      typeof(Float64::NAN <=> 1.to_big_r).should eq(Int32?)\n      typeof(Float32::NAN <=> 1.to_big_r).should eq(Int32?)\n\n      typeof(1.to_big_r <=> 1.to_big_f).should eq(Int32)\n    end\n  end\n\n  it \"#+\" do\n    (br(10, 7) + br(3, 7)).should eq(br(13, 7))\n    (0 + br(10, 7) + 3).should eq(br(31, 7))\n  end\n\n  it \"#-\" do\n    (br(10, 7) - br(3, 7)).should eq(br(7, 7))\n    (br(10, 7) - 3).should eq(br(-11, 7))\n    (0 - br(10, 7)).should eq(br(-10, 7))\n  end\n\n  it \"#*\" do\n    (br(10, 7) * br(3, 7)).should eq(br(30, 49))\n    (1 * br(10, 7) * 3).should eq(br(30, 7))\n  end\n\n  it \"#/\" do\n    (br(10, 7) / br(3, 7)).should eq(br(10, 3))\n    expect_raises(DivisionByZeroError) { br(10, 7) / br(0, 10) }\n    (br(10, 7) / 3).should eq(br(10, 21))\n    (1 / br(10, 7)).should eq(br(7, 10))\n  end\n\n  it \"#//\" do\n    (br(18, 7) // br(4, 5)).should eq(br(3, 1))\n    (br(-18, 7) // br(4, 5)).should eq(br(-4, 1))\n    (br(18, 7) // br(-4, 5)).should eq(br(-4, 1))\n    (br(-18, 7) // br(-4, 5)).should eq(br(3, 1))\n\n    (br(18, 5) // 2).should eq(br(1, 1))\n    (br(-18, 5) // 2).should eq(br(-2, 1))\n    (br(18, 5) // -2).should eq(br(-2, 1))\n    (br(-18, 5) // -2).should eq(br(1, 1))\n    (br(18, 5) // 2.to_big_i).should eq(br(1, 1))\n\n    expect_raises(DivisionByZeroError) { br(18, 7) // br(0, 1) }\n    expect_raises(DivisionByZeroError) { br(18, 7) // 0 }\n\n    (8 // br(10, 7)).should eq(5)\n    (-8 // br(10, 7)).should eq(-6)\n    (8 // br(-10, 7)).should eq(-6)\n    (-8 // br(-10, 7)).should eq(5)\n\n    expect_raises(DivisionByZeroError) { 8 // br(0, 7) }\n  end\n\n  it \"#%\" do\n    (br(18, 7) % br(4, 5)).should eq(br(6, 35))\n    (br(-18, 7) % br(4, 5)).should eq(br(22, 35))\n    (br(18, 7) % br(-4, 5)).should eq(br(-22, 35))\n    (br(-18, 7) % br(-4, 5)).should eq(br(-6, 35))\n\n    (br(18, 5) % 2).should eq(br(8, 5))\n    (br(-18, 5) % 2).should eq(br(2, 5))\n    (br(18, 5) % -2).should eq(br(-2, 5))\n    (br(-18, 5) % -2).should eq(br(-8, 5))\n    (br(18, 5) % 2.to_big_i).should eq(br(8, 5))\n\n    expect_raises(DivisionByZeroError) { br(18, 7) % br(0, 1) }\n    expect_raises(DivisionByZeroError) { br(18, 7) % 0 }\n  end\n\n  it \"#tdiv\" do\n    br(18, 7).tdiv(br(4, 5)).should eq(br(3, 1))\n    br(-18, 7).tdiv(br(4, 5)).should eq(br(-3, 1))\n    br(18, 7).tdiv(br(-4, 5)).should eq(br(-3, 1))\n    br(-18, 7).tdiv(br(-4, 5)).should eq(br(3, 1))\n\n    br(18, 5).tdiv(2).should eq(br(1, 1))\n    br(-18, 5).tdiv(2).should eq(br(-1, 1))\n    br(18, 5).tdiv(-2).should eq(br(-1, 1))\n    br(-18, 5).tdiv(-2).should eq(br(1, 1))\n    br(18, 5).tdiv(2.to_big_i).should eq(br(1, 1))\n\n    expect_raises(DivisionByZeroError) { br(18, 7).tdiv(br(0, 1)) }\n    expect_raises(DivisionByZeroError) { br(18, 7).tdiv(0) }\n  end\n\n  it \"#remainder\" do\n    br(18, 7).remainder(br(4, 5)).should eq(br(6, 35))\n    br(-18, 7).remainder(br(4, 5)).should eq(br(-6, 35))\n    br(18, 7).remainder(br(-4, 5)).should eq(br(6, 35))\n    br(-18, 7).remainder(br(-4, 5)).should eq(br(-6, 35))\n\n    br(18, 5).remainder(2).should eq(br(8, 5))\n    br(-18, 5).remainder(2).should eq(br(-8, 5))\n    br(18, 5).remainder(-2).should eq(br(8, 5))\n    br(-18, 5).remainder(-2).should eq(br(-8, 5))\n    br(18, 5).remainder(2.to_big_i).should eq(br(8, 5))\n\n    expect_raises(DivisionByZeroError) { br(18, 7).remainder(br(0, 1)) }\n    expect_raises(DivisionByZeroError) { br(18, 7).remainder(0) }\n  end\n\n  it \"#- (negation)\" do\n    (-br(10, 3)).should eq(br(-10, 3))\n  end\n\n  it \"#inv\" do\n    (br(10, 3).inv).should eq(br(3, 10))\n    expect_raises(DivisionByZeroError) { br(0, 3).inv }\n  end\n\n  it \"#abs\" do\n    (br(-10, 3).abs).should eq(br(10, 3))\n  end\n\n  it \"#<<\" do\n    (br(10, 3) << 2).should eq(br(40, 3))\n  end\n\n  it \"#>>\" do\n    (br(10, 3) >> 2).should eq(br(5, 6))\n  end\n\n  describe \"#**\" do\n    it \"exponentiates with positive powers\" do\n      result = br(17, 11) ** 5\n      result.should be_a(BigRational)\n      result.should eq(br(1419857, 161051))\n\n      result = br(17, 11) ** 5_u8\n      result.should be_a(BigRational)\n      result.should eq(br(1419857, 161051))\n    end\n\n    it \"exponentiates with negative powers\" do\n      result = br(17, 11) ** -5\n      result.should eq(br(161051, 1419857))\n    end\n\n    it \"cannot raise 0 to a negative power\" do\n      expect_raises(DivisionByZeroError) { br(0, 1) ** -1 }\n    end\n  end\n\n  it \"#ceil\" do\n    br(2, 1).ceil.should eq(2)\n    br(21, 10).ceil.should eq(3)\n    br(29, 10).ceil.should eq(3)\n\n    br(201, 100).ceil.should eq(3)\n    br(211, 100).ceil.should eq(3)\n    br(291, 100).ceil.should eq(3)\n\n    br(-201, 100).ceil.should eq(-2)\n    br(-291, 100).ceil.should eq(-2)\n  end\n\n  it \"#floor\" do\n    br(21, 10).floor.should eq(2)\n    br(29, 10).floor.should eq(2)\n    br(-29, 10).floor.should eq(-3)\n\n    br(211, 100).floor.should eq(2)\n    br(291, 100).floor.should eq(2)\n    br(-291, 100).floor.should eq(-3)\n  end\n\n  it \"#trunc\" do\n    br(21, 10).trunc.should eq(2)\n    br(29, 10).trunc.should eq(2)\n    br(-29, 10).trunc.should eq(-2)\n\n    br(211, 100).trunc.should eq(2)\n    br(291, 100).trunc.should eq(2)\n    br(-291, 100).trunc.should eq(-2)\n  end\n\n  describe \"#round\" do\n    describe \"rounding modes\" do\n      it \"to_zero\" do\n        br(-9, 6).round(:to_zero).should eq BigRational.new(-1)\n        br(-6, 6).round(:to_zero).should eq BigRational.new(-1)\n        br(-5, 6).round(:to_zero).should eq BigRational.new(0)\n        br(-3, 6).round(:to_zero).should eq BigRational.new(0)\n        br(-1, 6).round(:to_zero).should eq BigRational.new(0)\n        br(0, 6).round(:to_zero).should eq BigRational.new(0)\n        br(1, 6).round(:to_zero).should eq BigRational.new(0)\n        br(3, 6).round(:to_zero).should eq BigRational.new(0)\n        br(5, 6).round(:to_zero).should eq BigRational.new(0)\n        br(6, 6).round(:to_zero).should eq BigRational.new(1)\n        br(9, 6).round(:to_zero).should eq BigRational.new(1)\n      end\n\n      it \"to_positive\" do\n        br(-9, 6).round(:to_positive).should eq BigRational.new(-1)\n        br(-6, 6).round(:to_positive).should eq BigRational.new(-1)\n        br(-5, 6).round(:to_positive).should eq BigRational.new(0)\n        br(-3, 6).round(:to_positive).should eq BigRational.new(0)\n        br(-1, 6).round(:to_positive).should eq BigRational.new(0)\n        br(0, 6).round(:to_positive).should eq BigRational.new(0)\n        br(1, 6).round(:to_positive).should eq BigRational.new(1)\n        br(3, 6).round(:to_positive).should eq BigRational.new(1)\n        br(5, 6).round(:to_positive).should eq BigRational.new(1)\n        br(6, 6).round(:to_positive).should eq BigRational.new(1)\n        br(9, 6).round(:to_positive).should eq BigRational.new(2)\n      end\n\n      it \"to_negative\" do\n        br(-9, 6).round(:to_negative).should eq BigRational.new(-2)\n        br(-6, 6).round(:to_negative).should eq BigRational.new(-1)\n        br(-5, 6).round(:to_negative).should eq BigRational.new(-1)\n        br(-3, 6).round(:to_negative).should eq BigRational.new(-1)\n        br(-1, 6).round(:to_negative).should eq BigRational.new(-1)\n        br(0, 6).round(:to_negative).should eq BigRational.new(0)\n        br(1, 6).round(:to_negative).should eq BigRational.new(0)\n        br(3, 6).round(:to_negative).should eq BigRational.new(0)\n        br(5, 6).round(:to_negative).should eq BigRational.new(0)\n        br(6, 6).round(:to_negative).should eq BigRational.new(1)\n        br(9, 6).round(:to_negative).should eq BigRational.new(1)\n      end\n\n      it \"ties_even\" do\n        br(-15, 6).round(:ties_even).should eq BigRational.new(-2)\n        br(-9, 6).round(:ties_even).should eq BigRational.new(-2)\n        br(-6, 6).round(:ties_even).should eq BigRational.new(-1)\n        br(-5, 6).round(:ties_even).should eq BigRational.new(-1)\n        br(-3, 6).round(:ties_even).should eq BigRational.new(0)\n        br(-1, 6).round(:ties_even).should eq BigRational.new(0)\n        br(0, 6).round(:ties_even).should eq BigRational.new(0)\n        br(1, 6).round(:ties_even).should eq BigRational.new(0)\n        br(3, 6).round(:ties_even).should eq BigRational.new(0)\n        br(5, 6).round(:ties_even).should eq BigRational.new(1)\n        br(6, 6).round(:ties_even).should eq BigRational.new(1)\n        br(9, 6).round(:ties_even).should eq BigRational.new(2)\n        br(15, 6).round(:ties_even).should eq BigRational.new(2)\n      end\n\n      it \"ties_away\" do\n        br(-15, 6).round(:ties_away).should eq BigRational.new(-3)\n        br(-9, 6).round(:ties_away).should eq BigRational.new(-2)\n        br(-6, 6).round(:ties_away).should eq BigRational.new(-1)\n        br(-5, 6).round(:ties_away).should eq BigRational.new(-1)\n        br(-3, 6).round(:ties_away).should eq BigRational.new(-1)\n        br(-1, 6).round(:ties_away).should eq BigRational.new(0)\n        br(0, 6).round(:ties_away).should eq BigRational.new(0)\n        br(1, 6).round(:ties_away).should eq BigRational.new(0)\n        br(3, 6).round(:ties_away).should eq BigRational.new(1)\n        br(5, 6).round(:ties_away).should eq BigRational.new(1)\n        br(6, 6).round(:ties_away).should eq BigRational.new(1)\n        br(9, 6).round(:ties_away).should eq BigRational.new(2)\n        br(15, 6).round(:ties_away).should eq BigRational.new(3)\n      end\n\n      it \"default (=ties_even)\" do\n        br(-15, 6).round.should eq BigRational.new(-2)\n        br(-9, 6).round.should eq BigRational.new(-2)\n        br(-6, 6).round.should eq BigRational.new(-1)\n        br(-5, 6).round.should eq BigRational.new(-1)\n        br(-3, 6).round.should eq BigRational.new(0)\n        br(-1, 6).round.should eq BigRational.new(0)\n        br(0, 6).round.should eq BigRational.new(0)\n        br(1, 6).round.should eq BigRational.new(0)\n        br(3, 6).round.should eq BigRational.new(0)\n        br(5, 6).round.should eq BigRational.new(1)\n        br(6, 6).round.should eq BigRational.new(1)\n        br(9, 6).round.should eq BigRational.new(2)\n        br(15, 6).round.should eq BigRational.new(2)\n      end\n    end\n  end\n\n  describe \"#integer?\" do\n    it { br(0, 1).integer?.should be_true }\n    it { br(1, 1).integer?.should be_true }\n    it { br(9, 3).integer?.should be_true }\n    it { br(-126, 7).integer?.should be_true }\n    it { br(5, 2).integer?.should be_false }\n    it { br(7, -3).integer?.should be_false }\n  end\n\n  it \"is a number\" do\n    br(10, 3).is_a?(Number).should be_true\n  end\n\n  it \"clones\" do\n    x = br(10, 3)\n    x.clone.should eq(x)\n  end\n\n  describe \"#inspect\" do\n    it { 123.to_big_r.inspect.should eq(\"123\") }\n  end\n\n  it \"#format\" do\n    br(100, 3).format.should eq(\"100/3\")\n    br(1234567, 890123).format.should eq(\"1,234,567/890,123\")\n    br(1234567, 890123).format(\".\", \" \").should eq(\"1 234 567/890 123\")\n    br(1234567, 890123).format(\".\", \" \", group: 2).should eq(\"1 23 45 67/89 01 23\")\n    br(1234567, 890123).format(\",\", \".\", group: 4).should eq(\"123.4567/89.0123\")\n  end\nend\n\ndescribe \"BigRational Math\" do\n  it \"sqrt\" do\n    Math.sqrt(BigRational.new(BigInt.new(\"1\" + \"0\"*48), 1)).should eq(BigFloat.new(\"1\" + \"0\"*24))\n  end\nend\n"
  },
  {
    "path": "spec/std/big/number_spec.cr",
    "content": "{% skip_file if flag?(:bits32) %}\n\nrequire \"spec\"\nrequire \"big\"\nrequire \"../../support/number\"\n\nprivate BIG_NUMBER_TYPES = [BigInt, BigFloat, BigDecimal, BigRational]\n\ndescribe \"Big* as numbers\" do\n  {% for number_type in BIG_NUMBER_TYPES %}\n    it_initializes_from_value_to {{number_type}}\n  {% end %}\n\n  it_can_convert_between({{BIG_NUMBER_TYPES}}, {{BIG_NUMBER_TYPES}})\n  it_can_convert_between({{BUILTIN_NUMBER_TYPES_LTE_64}}, {{BIG_NUMBER_TYPES}})\n  it_can_convert_between({{BIG_NUMBER_TYPES}}, {{BUILTIN_NUMBER_TYPES_LTE_64}})\n  # TODO pending conversion between Int128\n\n  floor_division_returns_lhs_type {{BIG_NUMBER_TYPES}}, {{BIG_NUMBER_TYPES}}\n  floor_division_returns_lhs_type {{BUILTIN_NUMBER_TYPES_LTE_64}}, {{BIG_NUMBER_TYPES}}\n  floor_division_returns_lhs_type {{BIG_NUMBER_TYPES}}, {{BUILTIN_NUMBER_TYPES_LTE_64}}\n  # TODO pending cases between Int128\n\n  division_between_returns {{BUILTIN_NUMBER_TYPES}}, [BigInt, BigFloat], BigFloat\n  division_between_returns [BigInt, BigFloat], {{BUILTIN_NUMBER_TYPES}}, BigFloat\n  division_between_returns [BigInt, BigFloat], [BigInt, BigFloat], BigFloat\n\n  division_between_returns {{BUILTIN_NUMBER_TYPES}}, [BigDecimal], BigDecimal\n  division_between_returns [BigDecimal], {{BUILTIN_NUMBER_TYPES}}, BigDecimal\n  division_between_returns [BigDecimal], [BigInt, BigFloat, BigDecimal], BigDecimal\n  division_between_returns [BigInt, BigFloat, BigDecimal], [BigDecimal], BigDecimal\n\n  division_between_returns {{BUILTIN_NUMBER_TYPES}}, [BigRational], BigRational\n  division_between_returns [BigRational], {{BUILTIN_NUMBER_TYPES}}, BigRational\n  division_between_returns [BigRational], {{BIG_NUMBER_TYPES}}, BigRational\n  division_between_returns {{BIG_NUMBER_TYPES}}, [BigRational], BigRational\nend\n"
  },
  {
    "path": "spec/std/bit_array_spec.cr",
    "content": "require \"spec\"\nrequire \"bit_array\"\nrequire \"spec/helpers/iterate\"\n\nprivate def from_int(size : Int32, int : Int)\n  BitArray.new(size) { |i| int.bit(size - i - 1) > 0 }\nend\n\nprivate def assert_no_unused_bits(ba : BitArray, *, file = __FILE__, line = __LINE__)\n  bit_count = 32 * ((ba.size - 1) // 32 + 1)\n  (ba.size...bit_count).each do |index|\n    ba.unsafe_fetch(index).should be_false, file: file, line: line\n  end\nend\n\nprivate def assert_rotates!(from : BitArray, n : Int, to : BitArray, *, file = __FILE__, line = __LINE__)\n  from.rotate!(n).should eq(from), file: file, line: line\n  from.should eq(to), file: file, line: line\n  assert_no_unused_bits from, file: file, line: line\nend\n\nprivate def assert_rotates!(from : BitArray, to : BitArray, *, file = __FILE__, line = __LINE__)\n  from.rotate!.should eq(from), file: file, line: line\n  from.should eq(to), file: file, line: line\n  assert_no_unused_bits from, file: file, line: line\nend\n\ndescribe \"BitArray\" do\n  describe \".new\" do\n    context \"without block\" do\n      it \"initializes with initial value\" do\n        ary = BitArray.new(64, false)\n        ary.size.times { |i| ary[i].should be_false }\n\n        ary = BitArray.new(64, true)\n        ary.size.times { |i| ary[i].should be_true }\n      end\n\n      it \"initializes with false by default\" do\n        ary = BitArray.new(64)\n        ary.size.times { |i| ary[i].should be_false }\n      end\n\n      it \"initializes with non-Int32 size\" do\n        BitArray.new(5_i8).size.should eq(5)\n        BitArray.new(5_u64).size.should eq(5)\n      end\n\n      it \"initializes with unused bits cleared\" do\n        ary = BitArray.new(3, true)\n        assert_no_unused_bits ary\n      end\n    end\n\n    context \"with block\" do\n      it \"initializes elements with block\" do\n        BitArray.new(5) { |i| i >= 3 }.to_a.should eq([false, false, false, true, true])\n        BitArray.new(6) { |i| i < 2 ? \"\" : nil }.to_a.should eq([true, true, false, false, false, false])\n        BitArray.new(7_i64, &.even?).to_a.should eq([true, false, true, false, true, false, true])\n      end\n    end\n\n    it \"raises if size is negative\" do\n      expect_raises(ArgumentError) { BitArray.new(-1) }\n      expect_raises(ArgumentError) { BitArray.new(-2) { true } }\n    end\n  end\n\n  it \"has size\" do\n    ary = BitArray.new(100)\n    ary.size.should eq(100)\n  end\n\n  it \"is initially empty\" do\n    ary = BitArray.new(100)\n    100.times do |i|\n      ary[i].should be_false\n    end\n  end\n\n  it \"sets first bit to true\" do\n    ary = BitArray.new(100)\n    ary[0] = true\n    ary[0].should be_true\n  end\n\n  it \"sets second bit to true\" do\n    ary = BitArray.new(100)\n    ary[1] = true\n    ary[1].should be_true\n  end\n\n  it \"sets first bit to false\" do\n    ary = BitArray.new(100)\n    ary[0] = true\n    ary[0] = false\n    ary[0].should be_false\n  end\n\n  it \"sets second bit to false\" do\n    ary = BitArray.new(100)\n    ary[1] = true\n    ary[1] = false\n    ary[1].should be_false\n  end\n\n  it \"sets last bit to true with negative index\" do\n    ary = BitArray.new(100)\n    ary[-1] = true\n    ary[-1].should be_true\n    ary[99].should be_true\n  end\n\n  describe \"==\" do\n    it \"compares empty\" do\n      (BitArray.new(0)).should eq(BitArray.new(0))\n      from_int(1, 0b1).should_not eq(BitArray.new(0))\n      (BitArray.new(0)).should_not eq(from_int(1, 0b1))\n    end\n\n    it \"compares elements\" do\n      from_int(3, 0b101).should eq(from_int(3, 0b101))\n      from_int(3, 0b101).should_not eq(from_int(3, 0b010))\n    end\n\n    it \"compares other\" do\n      a = from_int(3, 0b101)\n      b = from_int(3, 0b101)\n      c = from_int(4, 0b1111)\n      d = from_int(3, 0b010)\n      (a == b).should be_true\n      (b == c).should be_false\n      (a == d).should be_false\n\n      e = from_int(16, 0b01001101_00011111)\n      f = from_int(16, 0b00000000_00011111)\n      (e == f).should be_false\n    end\n\n    it \"compares other initialized with true (#8543)\" do\n      a = BitArray.new(26, true)\n      b = BitArray.new(26, true)\n      b[23] = false\n      (a == b).should be_false\n    end\n\n    it \"compares other type\" do\n      from_int(3, 0b101).should_not eq(\"other type\")\n    end\n  end\n\n  describe \"[]\" do\n    it \"gets on inclusive range\" do\n      from_int(6, 0b011110)[1..4].should eq(from_int(4, 0b1111))\n    end\n\n    it \"gets on inclusive range with negative indices\" do\n      from_int(6, 0b011110)[-5..-2].should eq(from_int(4, 0b1111))\n    end\n\n    it \"gets on exclusive range\" do\n      from_int(6, 0b010100)[1...4].should eq(from_int(3, 0b101))\n    end\n\n    it \"gets on exclusive range with negative indices\" do\n      from_int(6, 0b010100)[-5...-2].should eq(from_int(3, 0b101))\n    end\n\n    it \"gets on range with start higher than end\" do\n      from_int(3, 0b101)[2..1].should eq(BitArray.new(0))\n      from_int(3, 0b101)[3..1].should eq(BitArray.new(0))\n      expect_raises IndexError do\n        from_int(3, 0b101)[4..1]\n      end\n    end\n\n    it \"gets on range with start higher than negative end\" do\n      from_int(3, 0b011)[1..-1].should eq(from_int(2, 0b11))\n      from_int(3, 0b011)[2..-2].should eq(BitArray.new(0))\n    end\n\n    it \"gets on endless range\" do\n      from_int(6, 0b011110)[2..nil].should eq(from_int(4, 0b1110))\n    end\n\n    it \"gets on beginless range\" do\n      from_int(6, 0b011110)[nil..2].should eq(from_int(3, 0b011))\n    end\n\n    it \"raises on index out of bounds with range\" do\n      expect_raises IndexError do\n        from_int(3, 0b111)[4..6]\n      end\n    end\n\n    it \"gets with start and count\" do\n      from_int(6, 0b011100)[1, 3].should eq(from_int(3, 0b111))\n    end\n\n    it \"gets with start and count exceeding size\" do\n      from_int(3, 0b011)[1, 3].should eq(from_int(2, 0b11))\n    end\n\n    it \"gets with negative start\" do\n      from_int(6, 0b001100)[-4, 2].should eq(from_int(2, 0b11))\n    end\n\n    it \"raises on index out of bounds with start and count\" do\n      expect_raises IndexError do\n        from_int(3, 0b101)[4, 0]\n      end\n    end\n\n    it \"raises on negative count\" do\n      expect_raises ArgumentError do\n        from_int(3, 0b101)[3, -1]\n      end\n    end\n\n    it \"raises on index out of bounds\" do\n      expect_raises IndexError do\n        from_int(3, 0b101)[-4, 2]\n      end\n    end\n\n    it \"raises on negative count\" do\n      expect_raises ArgumentError, /Negative count: -1/ do\n        from_int(3, 0b101)[1, -1]\n      end\n    end\n\n    it \"raises on negative count on empty Array\" do\n      ba = BitArray.new(0)\n      expect_raises ArgumentError, /Negative count: -1/ do\n        ba[0, -1]\n      end\n    end\n\n    it \"gets 0, 0 on empty array\" do\n      a = BitArray.new(0)\n      a[0, 0].should eq(a)\n    end\n\n    it \"gets (0..0) on empty array\" do\n      a = BitArray.new(0)\n      a[0..0].should eq(a)\n    end\n\n    it \"doesn't exceed limits\" do\n      from_int(1, 0b1)[0..3].should eq(from_int(1, 0b1))\n    end\n\n    it \"returns empty if at end\" do\n      from_int(1, 0b1)[1, 0].should eq(BitArray.new(0))\n      from_int(1, 0b1)[1, 10].should eq(BitArray.new(0))\n    end\n\n    it \"raises on too negative left bound\" do\n      expect_raises IndexError do\n        from_int(3, 0b101)[-4..0]\n      end\n    end\n\n    it \"gets on medium bitarrays\" do\n      ba = BitArray.new(40)\n      ba[30] = true\n      ba[31] = true\n      ba[32] = true\n      ba[34] = true\n      ba[37] = true\n\n      ba[28..-1].should eq(from_int(12, 0b0011_10100100))\n    end\n\n    it \"gets on large bitarrays\" do\n      ba = BitArray.new(100)\n      ba[30] = true\n      ba[31] = true\n      ba[32] = true\n      ba[34] = true\n      ba[37] = true\n\n      ba[28..40].should eq(from_int(13, 0b00111_01001000))\n\n      ba[62] = true\n      ba[63] = true\n      ba[64] = true\n      ba[66] = true\n      ba[69] = true\n\n      ba[60..72].should eq(from_int(13, 0b00111_01001000))\n      ba[28..72].should eq(from_int(45, 0b00111_01001000_00000000_00000000_00000111_01001000_u64))\n    end\n\n    it \"preserves equality\" do\n      ba = BitArray.new(100)\n      25.upto(42) { |i| ba[i] = true }\n\n      ba[28..40].should eq(from_int(13, 0b11111_11111111))\n    end\n\n    it \"does not cause overflow (#8494)\" do\n      ba = BitArray.new(64, true)\n      ba[0] = false\n      ba[33] = false\n      ba[0, 32].should eq(from_int(32, 0b01111111_11111111_11111111_11111111_u32))\n      ba[1, 32].should eq(from_int(32, 0b11111111_11111111_11111111_11111111_u32))\n      ba[2, 32].should eq(from_int(32, 0b11111111_11111111_11111111_11111110_u32))\n    end\n\n    it \"zeroes unused bits\" do\n      ba = BitArray.new(32, true)\n      assert_no_unused_bits ba[0, 26]\n      assert_no_unused_bits ba[7, 11]\n\n      ba = BitArray.new(64, true)\n      assert_no_unused_bits ba[0, 26]\n      assert_no_unused_bits ba[0, 33]\n      assert_no_unused_bits ba[7, 53]\n\n      ba = BitArray.new(100, true)\n      assert_no_unused_bits ba[60, 26]\n      assert_no_unused_bits ba[0, 97]\n\n      ba = BitArray.new(38, true)\n      ba[0, 34].should eq(BitArray.new(34, true))\n    end\n  end\n\n  describe \"#none?\" do\n    context \"without block\" do\n      it \"returns true if no bits are set\" do\n        from_int(0, 0).none?.should be_true\n        from_int(1, 0b0).none?.should be_true\n        from_int(1, 0b1).none?.should be_false\n        from_int(2, 0b00).none?.should be_true\n        from_int(2, 0b01).none?.should be_false\n        from_int(2, 0b10).none?.should be_false\n        from_int(2, 0b11).none?.should be_false\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).none?.should be_true\n        from_int(32, 0b00000000_00000000_00000000_00000001_u32).none?.should be_false\n        from_int(32, 0b00000000_00000000_00010000_00000000_u32).none?.should be_false\n        from_int(32, 0b10000000_00000000_00000000_00000000_u32).none?.should be_false\n        from_int(34, 0b00_00000000_00000000_00000000_00000000_u64).none?.should be_true\n        from_int(34, 0b00_00000000_00000000_00000000_00000001_u64).none?.should be_false\n        from_int(34, 0b00_00000000_00000000_00010000_00000000_u64).none?.should be_false\n        from_int(34, 0b00_10000000_00000000_00000000_00000000_u64).none?.should be_false\n        from_int(34, 0b10_00000000_00000000_00000000_00000000_u64).none?.should be_false\n      end\n    end\n  end\n\n  describe \"#any?\" do\n    context \"without block\" do\n      it \"returns true if any bits are set\" do\n        from_int(0, 0).any?.should be_false\n        from_int(1, 0b0).any?.should be_false\n        from_int(1, 0b1).any?.should be_true\n        from_int(2, 0b00).any?.should be_false\n        from_int(2, 0b01).any?.should be_true\n        from_int(2, 0b10).any?.should be_true\n        from_int(2, 0b11).any?.should be_true\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).any?.should be_false\n        from_int(32, 0b00000000_00000000_00000000_00000001_u32).any?.should be_true\n        from_int(32, 0b00000000_00000000_00010000_00000000_u32).any?.should be_true\n        from_int(32, 0b10000000_00000000_00000000_00000000_u32).any?.should be_true\n        from_int(34, 0b00_00000000_00000000_00000000_00000000_u64).any?.should be_false\n        from_int(34, 0b00_00000000_00000000_00000000_00000001_u64).any?.should be_true\n        from_int(34, 0b00_00000000_00000000_00010000_00000000_u64).any?.should be_true\n        from_int(34, 0b00_10000000_00000000_00000000_00000000_u64).any?.should be_true\n        from_int(34, 0b10_00000000_00000000_00000000_00000000_u64).any?.should be_true\n      end\n    end\n  end\n\n  describe \"#all?\" do\n    context \"without block\" do\n      it \"returns true if all bits are set\" do\n        from_int(0, 0).all?.should be_true\n        from_int(1, 0b0).all?.should be_false\n        from_int(1, 0b1).all?.should be_true\n        from_int(2, 0b00).all?.should be_false\n        from_int(2, 0b01).all?.should be_false\n        from_int(2, 0b10).all?.should be_false\n        from_int(2, 0b11).all?.should be_true\n        from_int(32, 0b11111111_11111111_11111111_11111111_u32).all?.should be_true\n        from_int(32, 0b11111111_11111111_11111111_11111110_u32).all?.should be_false\n        from_int(32, 0b11111111_11111111_11101111_11111111_u32).all?.should be_false\n        from_int(32, 0b01111111_11111111_11111111_11111111_u32).all?.should be_false\n        from_int(34, 0b11_11111111_11111111_11111111_11111111_u64).all?.should be_true\n        from_int(34, 0b11_11111111_11111111_11111111_11111110_u64).all?.should be_false\n        from_int(34, 0b11_11111111_11111111_11101111_11111111_u64).all?.should be_false\n        from_int(34, 0b11_01111111_11111111_11111111_11111111_u64).all?.should be_false\n        from_int(34, 0b01_11111111_11111111_11111111_11111111_u64).all?.should be_false\n      end\n    end\n  end\n\n  describe \"#includes?\" do\n    it \"returns whether the given bit is included\" do\n      ary = BitArray.new(0)\n      ary.includes?(true).should be_false\n      ary.includes?(false).should be_false\n\n      ary = BitArray.new(1)\n      ary.includes?(true).should be_false\n      ary.includes?(false).should be_true\n      ary[0] = true\n      ary.includes?(true).should be_true\n      ary.includes?(false).should be_false\n\n      ary = BitArray.new(32, true)\n      ary.includes?(true).should be_true\n      ary.includes?(false).should be_false\n      ary[6] = false\n      ary.includes?(true).should be_true\n      ary.includes?(false).should be_true\n\n      ary = BitArray.new(34)\n      ary.includes?(true).should be_false\n      ary.includes?(false).should be_true\n      ary[6] = true\n      ary.includes?(true).should be_true\n      ary.includes?(false).should be_true\n    end\n  end\n\n  describe \"#one?\" do\n    it \"returns true if exactly one bit is set\" do\n      from_int(0, 0).one?.should be_false\n      from_int(1, 0b0).one?.should be_false\n      from_int(1, 0b1).one?.should be_true\n      from_int(2, 0b00).one?.should be_false\n      from_int(2, 0b01).one?.should be_true\n      from_int(2, 0b10).one?.should be_true\n      from_int(2, 0b11).one?.should be_false\n      from_int(32, 0b00000000_00000000_00000000_00000000_u32).one?.should be_false\n      from_int(32, 0b00000000_00000000_00000000_00000001_u32).one?.should be_true\n      from_int(32, 0b10000000_00000000_00000000_00000000_u32).one?.should be_true\n      from_int(32, 0b10000000_00000000_00000000_00000001_u32).one?.should be_false\n      from_int(34, 0b00_00000000_00000000_00000000_00000000_u64).one?.should be_false\n      from_int(34, 0b00_00000000_00000000_00000000_00000001_u64).one?.should be_true\n      from_int(34, 0b00_00000000_00000000_00010000_00000000_u64).one?.should be_true\n      from_int(34, 0b10_00000000_00000000_00000000_00000000_u64).one?.should be_true\n      from_int(34, 0b00_00000000_00000000_00010000_00000001_u64).one?.should be_false\n      from_int(34, 0b01_00000000_00000000_00000000_00000001_u64).one?.should be_false\n      from_int(34, 0b11_00000000_00000000_00000000_00000000_u64).one?.should be_false\n    end\n  end\n\n  describe \"#index\" do\n    context \"without block\" do\n      it \"returns index of first bit set\" do\n        from_int(0, 0).index(true).should be_nil\n        from_int(1, 0b0).index(true).should be_nil\n        from_int(1, 0b1).index(true).should eq(0)\n        from_int(2, 0b00).index(true).should be_nil\n        from_int(2, 0b01).index(true).should eq(1)\n        from_int(2, 0b10).index(true).should eq(0)\n        from_int(2, 0b11).index(true).should eq(0)\n        from_int(3, 0b000).index(true).should be_nil\n        from_int(3, 0b001).index(true).should eq(2)\n        from_int(3, 0b010).index(true).should eq(1)\n        from_int(3, 0b011).index(true).should eq(1)\n        from_int(3, 0b100).index(true).should eq(0)\n        from_int(3, 0b101).index(true).should eq(0)\n        from_int(3, 0b110).index(true).should eq(0)\n        from_int(3, 0b111).index(true).should eq(0)\n\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).index(true).should be_nil\n        from_int(32, 0b00000000_00000000_00000100_01000000_u32).index(true).should eq(21)\n        from_int(34, 0b00_00000000_00000000_00000000_00000000_u64).index(true).should be_nil\n        from_int(34, 0b01_00000000_00000000_00000000_00000010_u64).index(true).should eq(1)\n        from_int(34, 0b00_00000000_00000000_00000000_00000001_u64).index(true).should eq(33)\n\n        ba = BitArray.new(200, false)\n        ba.index(true).should be_nil\n        ba[151] = true\n        ba.index(true).should eq(151)\n        ba[187] = true\n        ba.index(true).should eq(151)\n        ba[46] = true\n        ba.index(true).should eq(46)\n      end\n\n      it \"returns index of first bit cleared\" do\n        from_int(0, 0).index(false).should be_nil\n        from_int(1, 0b0).index(false).should eq(0)\n        from_int(1, 0b1).index(false).should be_nil\n        from_int(2, 0b00).index(false).should eq(0)\n        from_int(2, 0b01).index(false).should eq(0)\n        from_int(2, 0b10).index(false).should eq(1)\n        from_int(2, 0b11).index(false).should be_nil\n        from_int(3, 0b000).index(false).should eq(0)\n        from_int(3, 0b001).index(false).should eq(0)\n        from_int(3, 0b010).index(false).should eq(0)\n        from_int(3, 0b011).index(false).should eq(0)\n        from_int(3, 0b100).index(false).should eq(1)\n        from_int(3, 0b101).index(false).should eq(1)\n        from_int(3, 0b110).index(false).should eq(2)\n        from_int(3, 0b111).index(false).should be_nil\n\n        from_int(32, 0b11111111_11111111_11111111_11111111_u32).index(false).should be_nil\n        from_int(32, 0b11111111_11111111_11111011_10111111_u32).index(false).should eq(21)\n        from_int(34, 0b11_11111111_11111111_11111111_11111111_u64).index(false).should be_nil\n        from_int(34, 0b10_11111111_11111111_11111111_11111101_u64).index(false).should eq(1)\n        from_int(34, 0b11_11111111_11111111_11111111_11111110_u64).index(false).should eq(33)\n\n        ba = BitArray.new(200, true)\n        ba.index(false).should be_nil\n        ba[151] = false\n        ba.index(false).should eq(151)\n        ba[187] = false\n        ba.index(false).should eq(151)\n        ba[46] = false\n        ba.index(false).should eq(46)\n      end\n\n      it \"accepts a start offset\" do\n        ba = from_int(5, 0b01011)\n        ba.index(true, 0).should eq(1)\n        ba.index(true, 1).should eq(1)\n        ba.index(true, 2).should eq(3)\n        ba.index(true, 3).should eq(3)\n        ba.index(true, 4).should eq(4)\n        ba.index(true, -5).should eq(1)\n        ba.index(true, -4).should eq(1)\n        ba.index(true, -3).should eq(3)\n        ba.index(true, -2).should eq(3)\n        ba.index(true, -1).should eq(4)\n        ba.index(true, 5).should be_nil\n        ba.index(true, -6).should be_nil\n\n        ba.index(false, 0).should eq(0)\n        ba.index(false, 1).should eq(2)\n        ba.index(false, 2).should eq(2)\n        ba.index(false, 3).should be_nil\n        ba.index(false, 4).should be_nil\n        ba.index(false, -5).should eq(0)\n        ba.index(false, -4).should eq(2)\n        ba.index(false, -3).should eq(2)\n        ba.index(false, -2).should be_nil\n        ba.index(false, -1).should be_nil\n        ba.index(false, 5).should be_nil\n        ba.index(false, -6).should be_nil\n\n        ba = BitArray.new(100, false)\n        ba[51] = ba[87] = true\n        ba.index(true, 1).should eq(51)\n        ba.index(true, 51).should eq(51)\n        ba.index(true, 52).should eq(87)\n        ba.index(true, 87).should eq(87)\n        ba.index(true, 88).should be_nil\n        ba.index(true, -99).should eq(51)\n        ba.index(true, -49).should eq(51)\n        ba.index(true, -48).should eq(87)\n\n        ba = BitArray.new(100, true)\n        ba[51] = ba[87] = false\n        ba.index(false, 1).should eq(51)\n        ba.index(false, 51).should eq(51)\n        ba.index(false, 52).should eq(87)\n        ba.index(false, 87).should eq(87)\n        ba.index(false, 88).should be_nil\n        ba.index(false, -99).should eq(51)\n        ba.index(false, -49).should eq(51)\n        ba.index(false, -48).should eq(87)\n      end\n    end\n  end\n\n  describe \"#rindex\" do\n    context \"without block\" do\n      it \"returns index of last bit set\" do\n        from_int(0, 0).rindex(true).should be_nil\n        from_int(1, 0b0).rindex(true).should be_nil\n        from_int(1, 0b1).rindex(true).should eq(0)\n        from_int(2, 0b00).rindex(true).should be_nil\n        from_int(2, 0b01).rindex(true).should eq(1)\n        from_int(2, 0b10).rindex(true).should eq(0)\n        from_int(2, 0b11).rindex(true).should eq(1)\n        from_int(3, 0b000).rindex(true).should be_nil\n        from_int(3, 0b001).rindex(true).should eq(2)\n        from_int(3, 0b010).rindex(true).should eq(1)\n        from_int(3, 0b011).rindex(true).should eq(2)\n        from_int(3, 0b100).rindex(true).should eq(0)\n        from_int(3, 0b101).rindex(true).should eq(2)\n        from_int(3, 0b110).rindex(true).should eq(1)\n        from_int(3, 0b111).rindex(true).should eq(2)\n\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).rindex(true).should be_nil\n        from_int(32, 0b00000000_00000000_00000100_01000000_u32).rindex(true).should eq(25)\n        from_int(34, 0b00_00000000_00000000_00000000_00000000_u64).rindex(true).should be_nil\n        from_int(34, 0b01_00000000_00000000_00000000_00000010_u64).rindex(true).should eq(32)\n        from_int(34, 0b10_00000000_00000000_00000000_00000000_u64).rindex(true).should eq(0)\n\n        ba = BitArray.new(200, false)\n        ba.rindex(true).should be_nil\n        ba[151] = true\n        ba.rindex(true).should eq(151)\n        ba[46] = true\n        ba.rindex(true).should eq(151)\n        ba[187] = true\n        ba.rindex(true).should eq(187)\n      end\n\n      it \"returns index of last bit cleared\" do\n        from_int(0, 0).rindex(false).should be_nil\n        from_int(1, 0b0).rindex(false).should eq(0)\n        from_int(1, 0b1).rindex(false).should be_nil\n        from_int(2, 0b00).rindex(false).should eq(1)\n        from_int(2, 0b01).rindex(false).should eq(0)\n        from_int(2, 0b10).rindex(false).should eq(1)\n        from_int(2, 0b11).rindex(false).should be_nil\n        from_int(3, 0b000).rindex(false).should eq(2)\n        from_int(3, 0b001).rindex(false).should eq(1)\n        from_int(3, 0b010).rindex(false).should eq(2)\n        from_int(3, 0b011).rindex(false).should eq(0)\n        from_int(3, 0b100).rindex(false).should eq(2)\n        from_int(3, 0b101).rindex(false).should eq(1)\n        from_int(3, 0b110).rindex(false).should eq(2)\n        from_int(3, 0b111).rindex(false).should be_nil\n\n        from_int(32, 0b11111111_11111111_11111111_11111111_u32).rindex(false).should be_nil\n        from_int(32, 0b11111111_11111111_11111011_10111111_u32).rindex(false).should eq(25)\n        from_int(34, 0b11_11111111_11111111_11111111_11111111_u64).rindex(false).should be_nil\n        from_int(34, 0b10_11111111_11111111_11111111_11111101_u64).rindex(false).should eq(32)\n        from_int(34, 0b01_11111111_11111111_11111111_11111111_u64).rindex(false).should eq(0)\n\n        ba = BitArray.new(200, true)\n        ba.rindex(false).should be_nil\n        ba[151] = false\n        ba.rindex(false).should eq(151)\n        ba[46] = false\n        ba.rindex(false).should eq(151)\n        ba[187] = false\n        ba.rindex(false).should eq(187)\n      end\n\n      it \"accepts a start offset\" do\n        ba = from_int(5, 0b01011)\n        ba.rindex(true, 0).should be_nil\n        ba.rindex(true, 1).should eq(1)\n        ba.rindex(true, 2).should eq(1)\n        ba.rindex(true, 3).should eq(3)\n        ba.rindex(true, 4).should eq(4)\n        ba.rindex(true, -5).should be_nil\n        ba.rindex(true, -4).should eq(1)\n        ba.rindex(true, -3).should eq(1)\n        ba.rindex(true, -2).should eq(3)\n        ba.rindex(true, -1).should eq(4)\n        ba.rindex(true, 5).should be_nil\n        ba.rindex(true, -6).should be_nil\n\n        ba.rindex(false, 0).should eq(0)\n        ba.rindex(false, 1).should eq(0)\n        ba.rindex(false, 2).should eq(2)\n        ba.rindex(false, 3).should eq(2)\n        ba.rindex(false, 4).should eq(2)\n        ba.rindex(false, -5).should eq(0)\n        ba.rindex(false, -4).should eq(0)\n        ba.rindex(false, -3).should eq(2)\n        ba.rindex(false, -2).should eq(2)\n        ba.rindex(false, -1).should eq(2)\n        ba.rindex(false, 5).should be_nil\n        ba.rindex(false, -6).should be_nil\n\n        ba = BitArray.new(100, false)\n        ba[51] = ba[87] = true\n        ba.rindex(true, 99).should eq(87)\n        ba.rindex(true, 87).should eq(87)\n        ba.rindex(true, 86).should eq(51)\n        ba.rindex(true, 51).should eq(51)\n        ba.rindex(true, 50).should be_nil\n        ba.rindex(true, -1).should eq(87)\n        ba.rindex(true, -13).should eq(87)\n        ba.rindex(true, -14).should eq(51)\n\n        ba = BitArray.new(100, true)\n        ba[51] = ba[87] = false\n        ba.rindex(false, 99).should eq(87)\n        ba.rindex(false, 87).should eq(87)\n        ba.rindex(false, 86).should eq(51)\n        ba.rindex(false, 51).should eq(51)\n        ba.rindex(false, 50).should be_nil\n        ba.rindex(false, -1).should eq(87)\n        ba.rindex(false, -13).should eq(87)\n        ba.rindex(false, -14).should eq(51)\n      end\n    end\n  end\n\n  describe \"#count\" do\n    context \"without block\" do\n      it \"returns the number of bits set\" do\n        from_int(0, 0).count(true).should eq(0)\n        from_int(1, 0b0).count(true).should eq(0)\n        from_int(1, 0b1).count(true).should eq(1)\n        from_int(2, 0b00).count(true).should eq(0)\n        from_int(2, 0b01).count(true).should eq(1)\n        from_int(2, 0b10).count(true).should eq(1)\n        from_int(2, 0b11).count(true).should eq(2)\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).count(true).should eq(0)\n        from_int(32, 0b11000101_00010111_11000001_00011101_u32).count(true).should eq(15)\n        from_int(32, 0b11111111_11111111_11111111_11111111_u32).count(true).should eq(32)\n        from_int(45, 0b00111_01001000_00000000_00000000_00000111_01001000_u64).count(true).should eq(10)\n      end\n\n      it \"returns the number of bits cleared\" do\n        from_int(0, 0).count(false).should eq(0)\n        from_int(1, 0b0).count(false).should eq(1)\n        from_int(1, 0b1).count(false).should eq(0)\n        from_int(2, 0b00).count(false).should eq(2)\n        from_int(2, 0b01).count(false).should eq(1)\n        from_int(2, 0b10).count(false).should eq(1)\n        from_int(2, 0b11).count(false).should eq(0)\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).count(false).should eq(32)\n        from_int(32, 0b11000101_00010111_11000001_00011101_u32).count(false).should eq(17)\n        from_int(32, 0b11111111_11111111_11111111_11111111_u32).count(false).should eq(0)\n        from_int(45, 0b00111_01001000_00000000_00000000_00000111_01001000_u64).count(false).should eq(35)\n      end\n    end\n  end\n\n  describe \"#tally\" do\n    context \"without block\" do\n      it \"tallies the number of set and cleared bits\" do\n        from_int(0, 0).tally.should eq({} of Bool => Int32)\n        from_int(1, 0b0).tally.should eq({false => 1})\n        from_int(1, 0b1).tally.should eq({true => 1})\n        from_int(2, 0b00).tally.should eq({false => 2})\n        from_int(2, 0b01).tally.should eq({true => 1, false => 1})\n        from_int(2, 0b10).tally.should eq({true => 1, false => 1})\n        from_int(2, 0b11).tally.should eq({true => 2})\n        from_int(32, 0b00000000_00000000_00000000_00000000_u32).tally.should eq({false => 32})\n        from_int(32, 0b11000101_00010111_11000001_00011101_u32).tally.should eq({true => 15, false => 17})\n        from_int(32, 0b11111111_11111111_11111111_11111111_u32).tally.should eq({true => 32})\n        from_int(45, 0b00111_01001000_00000000_00000000_00000111_01001000_u64).tally.should eq({true => 10, false => 35})\n      end\n\n      it \"tallies into the given hash\" do\n        hash = {false => 0}\n        ary = from_int(45, 0b00111_01001000_00000000_00000000_00000111_01001000_u64)\n        ary.tally(hash).should be(hash)\n        hash.should eq({true => 10, false => 35})\n        ary.tally(hash).should be(hash)\n        hash.should eq({true => 20, false => 70})\n\n        hash = {true => -4_i64}\n        ary.tally(hash).should be(hash)\n        hash.should eq({true => 6_i64, false => 35})\n      end\n\n      it \"doesn't add key into the tally hash if element doesn't exist\" do\n        from_int(3, 0b111).tally({true => 0}).has_key?(false).should be_false\n        from_int(3, 0b000).tally({false => 0}).has_key?(true).should be_false\n      end\n    end\n  end\n\n  describe \"#toggle\" do\n    it \"toggles a bit\" do\n      ary = BitArray.new(32)\n      ary[3].should be_false\n\n      ary.toggle(3)\n      ary[3].should be_true\n\n      ary.toggle(3)\n      ary[3].should be_false\n    end\n\n    it \"toggles with index and count\" do\n      ary = from_int(4, 0b0011)\n      ary.toggle(1, 2)\n      ary.should eq(from_int(4, 0b0101))\n\n      ary = from_int(40, 0b00110011_01010101)\n      ary.toggle(30, 6)\n      ary[24..].should eq(from_int(16, 0b00110000_10100101))\n\n      ary = from_int(32, 0b10000000_00000000_00000000_00000001)\n      ary.toggle(0, 32)\n      ary.should eq(from_int(32, 0b01111111_11111111_11111111_11111110))\n    end\n\n    it \"toggles with index and count, not enough bits\" do\n      ary = from_int(4, 0b0011)\n      ary.toggle(1, 5)\n      ary.should eq(from_int(4, 0b0100))\n      (4..31).each { |i| ary.unsafe_fetch(i).should be_false }\n\n      ary = from_int(40, 0b00110011_01010101)\n      ary.toggle(30, 12)\n      ary[24..].should eq(from_int(16, 0b00110000_10101010))\n      (40..63).each { |i| ary.unsafe_fetch(i).should be_false }\n    end\n\n    it \"toggles with index == size and count\" do\n      ary = from_int(4, 0b0011)\n      ary.toggle(4, 2)\n      ary.should eq(from_int(4, 0b0011))\n      (4..31).each { |i| ary.unsafe_fetch(i).should be_false }\n\n      ary = from_int(40, 0b00110011_01010101)\n      ary.toggle(40, 6)\n      ary[24..].should eq(from_int(16, 0b00110011_01010101))\n      (40..63).each { |i| ary.unsafe_fetch(i).should be_false }\n    end\n\n    it \"toggles with index < 0 and count\" do\n      ary = from_int(4, 0b0011)\n      ary.toggle(-3, 2)\n      ary.should eq(from_int(4, 0b0101))\n\n      ary = from_int(40, 0b00110011_01010101)\n      ary.toggle(-10, 6)\n      ary[24..].should eq(from_int(16, 0b00110000_10100101))\n    end\n\n    it \"raises on out of bound index\" do\n      expect_raises(IndexError) { BitArray.new(2).toggle(2) }\n      expect_raises(IndexError) { BitArray.new(2).toggle(-3) }\n\n      expect_raises(IndexError) { BitArray.new(2).toggle(3, 1) }\n      expect_raises(IndexError) { BitArray.new(2).toggle(-3, 1) }\n    end\n\n    it \"raises on negative count\" do\n      expect_raises(ArgumentError) { BitArray.new(2).toggle(0, -1) }\n    end\n\n    it \"toggles with range\" do\n      ary = from_int(40, 0b00110011_01010101)\n      ary.toggle(30..35)\n      ary[24..].should eq(from_int(16, 0b00110000_10100101))\n    end\n\n    it \"toggles zero bits correctly\" do\n      ary = BitArray.new(32)\n      ary.toggle(0, 0)\n      ary.none?.should be_true\n      ary.toggle(32, 0)\n      ary.none?.should be_true\n      ary.toggle(32, 2)\n      ary.none?.should be_true\n    end\n  end\n\n  it \"inverts all bits\" do\n    ary = BitArray.new(100)\n    ary.none?.should be_true\n\n    ary.invert\n    ary.all?.should be_true\n    assert_no_unused_bits ary\n\n    ary[50] = false\n    ary[33] = false\n    ary.count { |b| b }.should eq(98)\n\n    ary.invert\n    ary.count { |b| b }.should eq(2)\n  end\n\n  describe \"#fill\" do\n    context \"without block\" do\n      it \"clears all bits\" do\n        ba = BitArray.new(7, true)\n        ba.fill(false).none?.should be_true\n        ba.none?.should be_true\n\n        ba = BitArray.new(32, true)\n        ba.fill(false).none?.should be_true\n        ba.none?.should be_true\n\n        ba = BitArray.new(100, true)\n        ba.fill(false).none?.should be_true\n        ba.none?.should be_true\n      end\n\n      it \"sets all bits\" do\n        ba = BitArray.new(7)\n        ba.fill(true).all?.should be_true\n        ba.all?.should be_true\n        assert_no_unused_bits ba\n\n        ba = BitArray.new(32)\n        ba.fill(true).all?.should be_true\n        ba.all?.should be_true\n\n        ba = BitArray.new(100)\n        ba.fill(true).all?.should be_true\n        ba.all?.should be_true\n        assert_no_unused_bits ba\n      end\n    end\n\n    context \"without block, with start and count\" do\n      it \"sets or clears a subrange of bits\" do\n        ba = from_int(5, 0b01011)\n        ba.fill(true, 1, 3).should eq(from_int(5, 0b01111))\n        ba.should eq(from_int(5, 0b01111))\n        ba.fill(false, 2, 5).should eq(from_int(5, 0b01000))\n        ba.should eq(from_int(5, 0b01000))\n        ba.fill(true, -2, 7).should eq(from_int(5, 0b01011))\n        ba.should eq(from_int(5, 0b01011))\n        assert_no_unused_bits ba\n\n        ba = from_int(8, 0b11010001)\n        ba.fill(false, 1, 3).should eq(from_int(8, 0b10000001))\n        ba.should eq(from_int(8, 0b10000001))\n        ba.fill(true, 4, 5).should eq(from_int(8, 0b10001111))\n        ba.should eq(from_int(8, 0b10001111))\n        ba.fill(true, 8, 0).should eq(from_int(8, 0b10001111))\n        ba.should eq(from_int(8, 0b10001111))\n        ba.fill(true, 8, 10).should eq(from_int(8, 0b10001111))\n        ba.should eq(from_int(8, 0b10001111))\n        ba.fill(true, -6, 0).should eq(from_int(8, 0b10001111))\n        ba.should eq(from_int(8, 0b10001111))\n\n        ba = from_int(32, 0b11000101_00011111_11000001_00011101_u32)\n        ba.fill(true, 6, 15).should eq(from_int(32, 0b11000111_11111111_11111001_00011101_u32))\n        ba.should eq(from_int(32, 0b11000111_11111111_11111001_00011101_u32))\n        ba.fill(false, -20, 12).should eq(from_int(32, 0b11000111_11110000_00000000_00011101_u32))\n        ba.should eq(from_int(32, 0b11000111_11110000_00000000_00011101_u32))\n        ba.fill(true, 23, 2).should eq(from_int(32, 0b11000111_11110000_00000001_10011101_u32))\n        ba.should eq(from_int(32, 0b11000111_11110000_00000001_10011101_u32))\n        ba.fill(false, 24, 0).should eq(from_int(32, 0b11000111_11110000_00000001_10011101_u32))\n        ba.should eq(from_int(32, 0b11000111_11110000_00000001_10011101_u32))\n      end\n\n      it \"raises if start index is out of range\" do\n        expect_raises(IndexError) { BitArray.new(7).fill(true, 8, 0) }\n        expect_raises(IndexError) { BitArray.new(7).fill(true, -8, 0) }\n      end\n    end\n\n    context \"without block, with range\" do\n      it \"sets or clears a subrange of bits\" do\n        ba = from_int(5, 0b01011)\n        ba.fill(true, 1..3).should eq(from_int(5, 0b01111))\n        ba.should eq(from_int(5, 0b01111))\n        ba.fill(false, 2..).should eq(from_int(5, 0b01000))\n        ba.should eq(from_int(5, 0b01000))\n        ba.fill(true, ...-2).should eq(from_int(5, 0b11100))\n        ba.should eq(from_int(5, 0b11100))\n        ba.fill(true, 0...0).should eq(from_int(5, 0b11100))\n        ba.should eq(from_int(5, 0b11100))\n      end\n\n      it \"raises if start index is out of range\" do\n        expect_raises(IndexError) { BitArray.new(7).fill(true, 8..9) }\n        expect_raises(IndexError) { BitArray.new(7).fill(true, -8...) }\n      end\n    end\n  end\n\n  describe \"#reverse!\" do\n    it \"reverses empty BitArray\" do\n      ba = from_int(0, 0)\n      ba.reverse!\n      ba.should eq(from_int(0, 0))\n    end\n\n    it \"reverses short BitArray\" do\n      ba = from_int(5, 0b01011)\n      ba.reverse!\n      ba.should eq(from_int(5, 0b11010))\n      assert_no_unused_bits ba\n\n      ba = from_int(8, 0b11101001)\n      ba.reverse!\n      ba.should eq(from_int(8, 0b10010111))\n      assert_no_unused_bits ba\n\n      ba = from_int(20, 0b1010_00110011_00001111)\n      ba.reverse!\n      ba.should eq(from_int(20, 0b1111_00001100_11000101))\n      assert_no_unused_bits ba\n\n      ba = from_int(32, 0b11000101_00011111_11000001_00011101_u32)\n      ba.reverse!\n      ba.should eq(from_int(32, 0b10111000_10000011_11111000_10100011_u32))\n    end\n\n    it \"reverses medium BitArray\" do\n      ba = from_int(45, 0b00111_01001000_00000000_00000000_00000111_01001000_u64)\n      ba.reverse!\n      ba.should eq(from_int(45, 0b00010_01011100_00000000_00000000_00000010_01011100_u64))\n      assert_no_unused_bits ba\n\n      ba = from_int(64, 0b11001100_00101001_01111010_10110001_10111111_00100101_11101100_10101010_u64)\n      ba.reverse!\n      ba.should eq(from_int(64, 0b01010101_00110111_10100100_11111101_10001101_01011110_10010100_00110011_u64))\n    end\n\n    it \"reverses large BitArray\" do\n      ba = BitArray.new(200)\n      ba[0] = ba[2] = ba[5] = ba[11] = ba[64] = ba[103] = ba[193] = ba[194] = true\n      ba.reverse!\n      ba2 = BitArray.new(200)\n      ba2[199] = ba2[197] = ba2[194] = ba2[188] = ba2[135] = ba2[96] = ba2[6] = ba2[5] = true\n      ba.should eq(ba2)\n      assert_no_unused_bits ba\n\n      ba = BitArray.new(256)\n      ba[0] = ba[2] = ba[5] = ba[11] = ba[64] = ba[103] = ba[193] = ba[194] = true\n      ba.reverse!\n      ba2 = BitArray.new(256)\n      ba2[255] = ba2[253] = ba2[250] = ba2[244] = ba2[191] = ba2[152] = ba2[62] = ba2[61] = true\n      ba.should eq(ba2)\n    end\n  end\n\n  describe \"#rotate!\" do\n    it \"rotates empty BitArray\" do\n      assert_rotates! from_int(0, 0), from_int(0, 0)\n      assert_rotates! from_int(0, 0), 0, from_int(0, 0)\n      assert_rotates! from_int(0, 0), 1, from_int(0, 0)\n      assert_rotates! from_int(0, 0), -1, from_int(0, 0)\n    end\n\n    it \"rotates short BitArray\" do\n      assert_rotates! from_int(5, 0b10011), from_int(5, 0b00111)\n      assert_rotates! from_int(5, 0b10011), 0, from_int(5, 0b10011)\n      assert_rotates! from_int(5, 0b10011), 1, from_int(5, 0b00111)\n      assert_rotates! from_int(5, 0b10011), 2, from_int(5, 0b01110)\n      assert_rotates! from_int(5, 0b10011), 3, from_int(5, 0b11100)\n      assert_rotates! from_int(5, 0b10011), 4, from_int(5, 0b11001)\n      assert_rotates! from_int(5, 0b10011), 5, from_int(5, 0b10011)\n      assert_rotates! from_int(5, 0b10011), 6, from_int(5, 0b00111)\n      assert_rotates! from_int(5, 0b10011), -1, from_int(5, 0b11001)\n      assert_rotates! from_int(5, 0b10011), -2, from_int(5, 0b11100)\n      assert_rotates! from_int(5, 0b10011), -3, from_int(5, 0b01110)\n      assert_rotates! from_int(5, 0b10011), -4, from_int(5, 0b00111)\n\n      ba = from_int(5, 0b10011)\n      assert_rotates! ba, from_int(5, 0b00111)\n      assert_rotates! ba, from_int(5, 0b01110)\n      assert_rotates! ba, 2, from_int(5, 0b11001)\n\n      ba = from_int(32, 0b11000101_00011111_11000001_00011101_u32)\n      assert_rotates! ba, 5, from_int(32, 0b10100011_11111000_00100011_10111000_u32)\n      assert_rotates! ba, -8, from_int(32, 0b10111000_10100011_11111000_00100011_u32)\n      assert_rotates! ba, 45, from_int(32, 0b01111111_00000100_01110111_00010100_u32)\n    end\n\n    it \"rotates medium BitArray\" do\n      ba = from_int(64, 0b11001100_00101001_01111010_10110001_10111111_00100101_11101100_10101010_u64)\n      assert_rotates! ba, from_int(64, 0b10011000_01010010_11110101_01100011_01111110_01001011_11011001_01010101_u64)\n      assert_rotates! ba, 10, from_int(64, 0b01001011_11010101_10001101_11111001_00101111_01100101_01010110_01100001_u64)\n      assert_rotates! ba, 51, from_int(64, 0b10110011_00001010_01011110_10101100_01101111_11001001_01111011_00101010_u64)\n      assert_rotates! ba, -40, from_int(64, 0b10101100_01101111_11001001_01111011_00101010_10110011_00001010_01011110_u64)\n      assert_rotates! ba, 128, from_int(64, 0b10101100_01101111_11001001_01111011_00101010_10110011_00001010_01011110_u64)\n      assert_rotates! ba, 97, from_int(64, 0b01010101_01100110_00010100_10111101_01011000_11011111_10010010_11110110_u64)\n    end\n\n    it \"rotates large BitArray\" do\n      ba = BitArray.new(200)\n      ba[0] = ba[2] = ba[5] = ba[11] = ba[64] = ba[103] = ba[193] = ba[194] = true\n\n      ba.rotate!\n      ba2 = BitArray.new(200)\n      ba2[199] = ba2[1] = ba2[4] = ba2[10] = ba2[63] = ba2[102] = ba2[192] = ba2[193] = true\n      ba.should eq(ba2)\n      assert_no_unused_bits ba\n\n      ba.rotate!(21)\n      ba2 = BitArray.new(200)\n      ba2[178] = ba2[180] = ba2[183] = ba2[189] = ba2[42] = ba2[81] = ba2[171] = ba2[172] = true\n      ba.should eq(ba2)\n      assert_no_unused_bits ba\n\n      ba.rotate!(192)\n      ba2 = BitArray.new(200)\n      ba2[186] = ba2[188] = ba2[191] = ba2[197] = ba2[50] = ba2[89] = ba2[179] = ba2[180] = true\n      ba.should eq(ba2)\n      assert_no_unused_bits ba\n\n      ba.rotate!(50)\n      ba2 = BitArray.new(200)\n      ba2[136] = ba2[138] = ba2[141] = ba2[147] = ba2[0] = ba2[39] = ba2[129] = ba2[130] = true\n      ba.should eq(ba2)\n      assert_no_unused_bits ba\n\n      ba.rotate!(123)\n      ba2 = BitArray.new(200)\n      ba2[13] = ba2[15] = ba2[18] = ba2[24] = ba2[77] = ba2[116] = ba2[6] = ba2[7] = true\n      ba.should eq(ba2)\n      assert_no_unused_bits ba\n    end\n  end\n\n  it \"raises when out of bounds\" do\n    ary = BitArray.new(10)\n    expect_raises IndexError do\n      ary[10] = true\n    end\n  end\n\n  it \"does to_s and inspect\" do\n    ary = BitArray.new(8)\n    ary[0] = true\n    ary[2] = true\n    ary[4] = true\n    ary.to_s.should eq(\"BitArray[10101000]\")\n    ary.inspect.should eq(\"BitArray[10101000]\")\n  end\n\n  it \"reads bits from slice\" do\n    ary = BitArray.new(43) # 5 bytes 3 bits\n    # 11010000_00000000_00001011_00000000_00000000_101xxxxx\n    ary[0] = true\n    ary[1] = true\n    ary[3] = true\n    ary[20] = true\n    ary[22] = true\n    ary[23] = true\n    ary[40] = true\n    ary[42] = true\n    slice = ary.to_slice\n\n    slice.size.should eq(6)\n    slice[0].should eq(0b00001011_u8)\n    slice[1].should eq(0b00000000_u8)\n    slice[2].should eq(0b11010000_u8)\n    slice[3].should eq(0b00000000_u8)\n    slice[4].should eq(0b00000000_u8)\n    slice[5].should eq(0b00000101_u8)\n  end\n\n  it \"read bits written from slice\" do\n    ary = BitArray.new(43) # 5 bytes 3 bits\n    slice = ary.to_slice\n    slice[0] = 0b10101010_u8\n    slice[1] = 0b01010101_u8\n    slice[5] = 0b00000101_u8\n    ary.each_with_index do |e, i|\n      e.should eq(i.in?(1, 3, 5, 7, 8, 10, 12, 14, 40, 42))\n    end\n  end\n\n  ary = BitArray.new(2)\n  ary[0] = true\n  ary[1] = false\n\n  it_iterates \"#each\", [true, false], ary.each\n  it_iterates \"#each_index\", [0, 1], ary.each_index\n  it_iterates \"#reverse_each\", [false, true], ary.reverse_each\n\n  it \"provides dup\" do\n    a = BitArray.new(2)\n    b = a.dup\n\n    b[0] = true\n    a[0].should be_false\n    b[0].should be_true\n  end\nend\n"
  },
  {
    "path": "spec/std/bool_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Bool\" do\n  describe \"!\" do\n    it { (!true).should be_false }\n    it { (!false).should be_true }\n  end\n\n  describe \"|\" do\n    it { (false | false).should be_false }\n    it { (false | true).should be_true }\n    it { (true | false).should be_true }\n    it { (true | true).should be_true }\n  end\n\n  describe \"&\" do\n    it { (false & false).should be_false }\n    it { (false & true).should be_false }\n    it { (true & false).should be_false }\n    it { (true & true).should be_true }\n  end\n\n  describe \"^\" do\n    it { (false ^ false).should be_false }\n    it { (false ^ true).should be_true }\n    it { (true ^ false).should be_true }\n    it { (true ^ true).should be_false }\n  end\n\n  describe \"hash\" do\n    it { true.hash.should_not eq(false.hash) }\n  end\n\n  describe \"to_unsafe\" do\n    it { true.to_unsafe.should eq(1) }\n    it { false.to_unsafe.should eq(0) }\n  end\n\n  describe \"to_s\" do\n    it { true.to_s.should eq(\"true\") }\n    it { false.to_s.should eq(\"false\") }\n  end\n\n  describe \"clone\" do\n    it { true.clone.should be_true }\n    it { false.clone.should be_false }\n  end\nend\n"
  },
  {
    "path": "spec/std/box_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Box\" do\n  it \"boxes and unboxes\" do\n    a = 1\n    box = Box.box(a)\n    Box(Int32).unbox(box).should eq(1)\n  end\n\n  it \"boxing a reference returns the same pointer\" do\n    a = \"foo\"\n    box = Box.box(a)\n    box.address.should eq(a.object_id)\n\n    Box(String).unbox(box).should be(a)\n  end\n\n  it \"boxing a pointer returns the same pointer\" do\n    a = 123\n    b = pointerof(a)\n    box = Box.box(b)\n    box.address.should eq(b.address)\n\n    Box(Pointer(Int32)).unbox(box).should eq(b)\n  end\n\n  it \"boxing a nilable reference returns the same pointer\" do\n    a = \"foo\".as(String?)\n    box = Box.box(a)\n    box.address.should eq(a.object_id)\n\n    b = Box(String?).unbox(box)\n    b.should be_a(String)\n    b.should be(a)\n  end\n\n  it \"boxing a nilable value returns the same value\" do\n    a = 1.as(Int32?)\n    box = Box.box(a)\n\n    b = Box(Int32?).unbox(box)\n    b.should be_a(Int32)\n    b.should eq(a)\n  end\n\n  it \"boxes with explicit type\" do\n    box = Box(Int32?).box(1)\n    b = Box(Int32?).unbox(box)\n    b.should be_a(Int32)\n    b.should eq(1)\n  end\n\n  it \"boxing nil returns a null pointer\" do\n    box = Box.box(nil)\n    box.address.should eq(0)\n\n    Box(Nil).unbox(box).should be_nil\n  end\n\n  describe \"unboxing a null pointer\" do\n    it \"returns nil if nilable\" do\n      Box(Nil).unbox(Pointer(Void).null).should be_nil\n      Box(String?).unbox(Pointer(Void).null).should be_nil\n      Box(UInt8*).unbox(Pointer(Void).null).should eq Pointer(UInt8).null\n    end\n\n    it \"raises if non-nilable\" do\n      expect_raises(NilAssertionError, \"Unboxing null pointer\") do\n        Box(String).unbox(Pointer(Void).null)\n      end\n      expect_raises(NilAssertionError, \"Unboxing null pointer\") do\n        Box(Int32).unbox(Pointer(Void).null)\n      end\n    end\n\n    it \"raises for mixed union\" do\n      expect_raises(NilAssertionError, \"Unboxing null pointer in mixed union\") do\n        Box(Int32 | String?).unbox(Pointer(Void).null)\n      end\n      expect_raises(NilAssertionError, \"Unboxing null pointer in mixed union\") do\n        Box(Int32?).unbox(Pointer(Void).null)\n      end\n    end\n  end\n\n  it \"boxing nil in a reference-like union returns a null pointer (#11839)\" do\n    box = Box.box(nil.as(String?))\n    box.address.should eq(0)\n\n    Box(String?).unbox(box).should be_nil\n  end\n\n  it \"boxing nil in a value-like union doesn't crash (#11839)\" do\n    box = Box.box(nil.as(Int32?))\n\n    Box(Int32?).unbox(box).should be_nil\n  end\nend\n"
  },
  {
    "path": "spec/std/channel_spec.cr",
    "content": "require \"spec\"\nrequire \"./sync/spec_helper\"\n\nprivate macro parallel(*jobs)\n  %channel = Channel(Exception | Nil).new\n\n  {% for job, i in jobs %}\n    %ret{i} = uninitialized typeof({{job}})\n    spawn do\n      begin\n        %ret{i} = {{job}}\n      rescue e : Exception\n        %channel.send e\n      else\n        %channel.send nil\n      end\n    end\n  {% end %}\n\n  {{ jobs.size }}.times do\n    %value = %channel.receive\n    if %value.is_a?(Exception)\n      raise Exception.new(\n        \"An unhandled error occurred inside a `parallel` call\",\n        cause: %value\n      )\n    end\n  end\n\n  {\n    {% for job, i in jobs %}\n      %ret{i},\n    {% end %}\n  }\nend\n\ndescribe Channel do\n  it \"creates unbuffered with no arguments\" do\n    Channel(Int32).new\n  end\n\n  it \"creates buffered with capacity argument\" do\n    Channel(Int32).new(32)\n  end\n\n  it \"send returns channel\" do\n    channel = Channel(Int32).new(1)\n    channel.send(1).should be(channel)\n  end\n\n  it \"does receive_first\" do\n    channel = Channel(Int32).new(1)\n    channel.send(1)\n    Channel.receive_first(Channel(Int32).new, channel).should eq 1\n  end\n\n  it \"does send_first\" do\n    ch1 = Channel(Int32).new(1)\n    ch2 = Channel(Int32).new(1)\n    ch1.send(1)\n    Channel.send_first(2, ch1, ch2)\n    ch2.receive.should eq 2\n  end\n\n  it \"does not raise or change its status when it is closed more than once\" do\n    ch = Channel(Int32).new\n    ch.closed?.should be_false\n\n    ch.close\n    ch.closed?.should be_true\n\n    ch.close\n    ch.closed?.should be_true\n  end\n\n  describe \".select\" do\n    context \"receive raise-on-close single-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String)\n        end\n      end\n\n      it \"types nilable channel\" do\n        # Yes, although it is discouraged\n        ch = Channel(Nil).new\n        spawn_and_wait(-> { ch.send nil }) do\n          i, m = Channel.select(ch.receive_select_action)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil)\n        end\n      end\n\n      it \"raises if channel was closed\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.receive_select_action)\n          end\n        end\n      end\n\n      it \"raises if channel is closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { sleep 0.2.seconds; ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.receive_select_action)\n          end\n        end\n      end\n\n      it \"awakes all waiting selects\" do\n        ch = Channel(String).new\n\n        p = -> {\n          begin\n            Channel.select(ch.receive_select_action)\n            0\n          rescue Channel::ClosedError\n            1\n          end\n        }\n\n        spawn_and_wait(-> { sleep 0.2.seconds; ch.close }) do\n          r = parallel p.call, p.call, p.call, p.call\n          r.should eq({1, 1, 1, 1})\n        end\n      end\n    end\n\n    context \"receive raise-on-close multi-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action, ch2.receive_select_action)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Bool)\n        end\n      end\n    end\n\n    context \"receive nil-on-close single-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action?)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Nil)\n        end\n      end\n\n      it \"types nilable channel\" do\n        # Yes, although it is discouraged\n        ch = Channel(Nil).new\n        spawn_and_wait(-> { ch.send nil }) do\n          i, m = Channel.select(ch.receive_select_action?)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil)\n        end\n      end\n\n      it \"returns nil if channel was closed\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          i, m = Channel.select(ch.receive_select_action?)\n          m.should be_nil\n        end\n      end\n\n      it \"returns nil channel is closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { sleep 0.2.seconds; ch.close }) do\n          i, m = Channel.select(ch.receive_select_action?)\n          m.should be_nil\n        end\n      end\n\n      it \"awakes all waiting selects\" do\n        ch = Channel(String).new\n\n        p = -> {\n          Channel.select(ch.receive_select_action?)\n        }\n\n        spawn_and_wait(-> { sleep 0.2.seconds; ch.close }) do\n          r = parallel p.call, p.call, p.call, p.call\n          r.should eq({ {0, nil}, {0, nil}, {0, nil}, {0, nil} })\n        end\n      end\n    end\n\n    context \"receive nil-on-close multi-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action?, ch2.receive_select_action?)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Bool | Nil)\n        end\n      end\n\n      it \"returns index of closed channel\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n        spawn_and_wait(-> { ch2.close }) do\n          i, m = Channel.select(ch.receive_select_action?, ch2.receive_select_action?)\n          i.should eq(1)\n          m.should be_nil\n        end\n      end\n    end\n\n    context \"mix of receive and receive? multi-channel\" do\n      it \"raises if receive channel was closed and receive? channel was not ready\" do\n        ch = Channel(String).new\n        ch2 = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.receive_select_action, ch2.receive_select_action?)\n          end\n        end\n      end\n\n      it \"returns nil if receive channel was not ready and receive? channel was closed\" do\n        ch = Channel(String).new\n        ch2 = Channel(String).new\n        spawn_and_wait(-> { ch2.close }) do\n          i, m = Channel.select(ch.receive_select_action, ch2.receive_select_action?)\n          i.should eq(1)\n          m.should be_nil\n        end\n      end\n    end\n\n    context \"send raise-on-close single-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.receive }) do\n          i, m = Channel.select(ch.send_select_action(\"foo\"))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil)\n        end\n      end\n\n      it \"types nilable channel\" do\n        # Yes, although it is discouraged\n        ch = Channel(Nil).new\n        spawn_and_wait(-> { ch.receive }) do\n          i, m = Channel.select(ch.send_select_action(nil))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil)\n        end\n      end\n\n      it \"raises if channel was closed\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.send_select_action(\"foo\"))\n          end\n        end\n      end\n\n      it \"raises if channel is closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { sleep 0.2.seconds; ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.send_select_action(\"foo\"))\n          end\n        end\n      end\n\n      it \"awakes all waiting selects\" do\n        ch = Channel(String).new\n\n        p = -> {\n          begin\n            Channel.select(ch.send_select_action(\"foo\"))\n            0\n          rescue Channel::ClosedError\n            1\n          end\n        }\n\n        spawn_and_wait(-> { sleep 0.2.seconds; ch.close }) do\n          r = parallel p.call, p.call, p.call, p.call\n          r.should eq({1, 1, 1, 1})\n        end\n      end\n    end\n\n    context \"send raise-on-close multi-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n        spawn_and_wait(-> { ch.receive }) do\n          i, m = Channel.select(ch.send_select_action(\"foo\"), ch2.send_select_action(true))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil)\n        end\n      end\n    end\n\n    context \"timeout\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String?)\n        end\n      end\n\n      it \"triggers timeout\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { }) do\n          i, m = Channel.select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n\n          i.should eq(1)\n          m.should be_nil\n        end\n      end\n\n      it \"triggers timeout (reverse order)\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { }) do\n          i, m = Channel.select(timeout_select_action(0.1.seconds), ch.receive_select_action)\n\n          i.should eq(0)\n          m.should be_nil\n        end\n      end\n\n      it \"triggers timeout (same fiber multiple times)\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { }) do\n          3.times do\n            i, m = Channel.select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n\n            i.should eq(1)\n            m.should be_nil\n          end\n        end\n      end\n\n      it \"allows receiving while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action, timeout_select_action(1.seconds))\n          i.should eq(0)\n          m.should eq(\"foo\")\n        end\n      end\n\n      it \"allows receiving while waiting (reverse order)\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(timeout_select_action(1.seconds), ch.receive_select_action)\n          i.should eq(1)\n          m.should eq(\"foo\")\n        end\n      end\n\n      it \"allows receiving while waiting (same fiber multiple times)\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { 3.times { ch.send \"foo\" } }) do\n          3.times do\n            i, m = Channel.select(ch.receive_select_action, timeout_select_action(1.seconds))\n            i.should eq(0)\n            m.should eq(\"foo\")\n          end\n        end\n      end\n\n      it \"negative amounts should not trigger timeout\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.select(ch.receive_select_action, timeout_select_action(-1.seconds))\n\n          i.should eq(0)\n          m.should eq(\"foo\")\n        end\n      end\n\n      it \"send raise-on-close raises if channel was closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.send_select_action(\"foo\"), timeout_select_action(0.1.seconds))\n          end\n        end\n      end\n\n      it \"receive raise-on-close raises if channel was closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n          end\n        end\n      end\n\n      it \"receive nil-on-close returns index of closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          i, m = Channel.select(ch.receive_select_action?, timeout_select_action(0.1.seconds))\n\n          i.should eq(0)\n          m.should be_nil\n        end\n      end\n    end\n  end\n\n  describe \".non_blocking_select\" do\n    context \"receive raise-on-close single-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Channel::NotReady)\n        end\n      end\n    end\n\n    context \"receive raise-on-close multi-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action, ch2.receive_select_action)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Bool | Channel::NotReady)\n        end\n      end\n    end\n\n    context \"receive nil-on-close single-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action?)\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Nil | Channel::NotReady)\n        end\n      end\n\n      it \"returns nil if channel was closed\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action?)\n          m.should be_nil\n        end\n      end\n    end\n\n    context \"mix of receive and receive? multi-channel\" do\n      it \"raises if receive channel was close and receive? channel was not ready\" do\n        ch = Channel(String).new\n        ch2 = Channel(String).new\n\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.non_blocking_select(ch.receive_select_action, ch2.receive_select_action?)\n          end\n        end\n      end\n\n      it \"returns nil if receive channel was not ready and receive? channel was closed\" do\n        ch = Channel(String).new\n        ch2 = Channel(String).new\n        spawn_and_wait(-> { ch2.close }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action, ch2.receive_select_action?)\n          i.should eq(1)\n          m.should be_nil\n        end\n      end\n    end\n\n    context \"send raise-on-close single-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.receive }) do\n          i, m = Channel.non_blocking_select(ch.send_select_action(\"foo\"))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil | Channel::NotReady)\n        end\n      end\n    end\n\n    context \"send raise-on-close multi-channel\" do\n      it \"types\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n        spawn_and_wait(-> { ch.receive }) do\n          i, m = Channel.non_blocking_select(ch.send_select_action(\"foo\"), ch2.send_select_action(true))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(Nil | Channel::NotReady)\n        end\n      end\n    end\n\n    context \"timeout\" do\n      it \"types\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.send \"foo\" }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n          typeof(i).should eq(Int32)\n          typeof(m).should eq(String | Nil | Channel::NotReady)\n        end\n      end\n\n      it \"should not trigger timeout\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n\n          i.should eq(2)\n          m.should eq(Channel::NotReady.new)\n        end\n      end\n\n      it \"negative amounts should not trigger timeout\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action, timeout_select_action(-1.seconds))\n\n          i.should eq(2)\n          m.should eq(Channel::NotReady.new)\n        end\n      end\n\n      it \"send raise-on-close raises if channel was closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.non_blocking_select(ch.send_select_action(\"foo\"), timeout_select_action(0.1.seconds))\n          end\n        end\n      end\n\n      it \"receive raise-on-close raises if channel was closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          expect_raises Channel::ClosedError do\n            Channel.non_blocking_select(ch.receive_select_action, timeout_select_action(0.1.seconds))\n          end\n        end\n      end\n\n      it \"receive nil-on-close returns index of closed while waiting\" do\n        ch = Channel(String).new\n        spawn_and_wait(-> { ch.close }) do\n          i, m = Channel.non_blocking_select(ch.receive_select_action?, timeout_select_action(0.1.seconds))\n\n          i.should eq(0)\n          m.should be_nil\n        end\n      end\n    end\n\n    it \"returns correct index for array argument\" do\n      ch = [Channel(String).new, Channel(String).new, Channel(String).new]\n      channels = [ch[0], ch[2], ch[1]] # shuffle around to get non-sequential lock_object_ids\n      spawn_and_wait(-> { channels[0].send \"foo\" }) do\n        i, m = Channel.non_blocking_select(channels.map(&.receive_select_action))\n\n        i.should eq(0)\n        m.should eq(\"foo\")\n      end\n    end\n  end\nend\n\ndescribe \"unbuffered\" do\n  it \"pings\" do\n    ch = Channel(Int32).new\n    spawn { ch.send(ch.receive) }\n    ch.send 123\n    ch.receive.should eq(123)\n  end\n\n  it \"blocks if there is no receiver\" do\n    ch = Channel(Int32).new\n    state = 0\n\n    Sync::CONCURRENT.spawn do\n      state = 1\n      ch.send 123\n      state = 2\n    end\n\n    Sync::CONCURRENT.spawn do\n      Sync.eventually { state.should eq(1) }\n      ch.receive.should eq(123)\n      state.should eq(1)\n    end\n\n    Sync.eventually { state.should eq(2) }\n  end\n\n  it \"deliver many senders\" do\n    ch = Channel(Int32).new\n    spawn { ch.send 1; ch.send 4 }\n    spawn { ch.send 2; ch.send 5 }\n    spawn { ch.send 3; ch.send 6 }\n\n    (1..6).map { ch.receive }.sort!.should eq([1, 2, 3, 4, 5, 6])\n  end\n\n  it \"works with select\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    spawn { ch1.send 123 }\n    Channel.select(ch1.receive_select_action, ch2.receive_select_action).should eq({0, 123})\n  end\n\n  it \"can send and receive nil\" do\n    ch = Channel(Nil).new\n    spawn { ch.send nil }\n    ch.receive.should be_nil\n  end\n\n  it \"can be closed\" do\n    ch = Channel(Int32).new\n    ch.closed?.should be_false\n    ch.close.should be_true\n    ch.closed?.should be_true\n    ch.close.should be_false\n    expect_raises(Channel::ClosedError) { ch.receive }\n  end\n\n  it \"can be closed after sending\" do\n    ch = Channel(Int32).new\n    spawn { ch.send 123; ch.close }\n    ch.receive.should eq(123)\n    expect_raises(Channel::ClosedError) { ch.receive }\n  end\n\n  it \"can be closed from different fiber\" do\n    ch = Channel(Int32).new\n    state = :none\n\n    spawn do\n      state = :ready\n      expect_raises(Channel::ClosedError) { ch.receive }\n      state = :closed\n    end\n    Sync.eventually { state.should eq(:ready) }\n\n    ch.close\n    Sync.eventually { state.should eq(:closed) }\n  end\n\n  it \"cannot send if closed\" do\n    ch = Channel(Int32).new\n    ch.close\n    expect_raises(Channel::ClosedError) { ch.send 123 }\n  end\n\n  it \"can receive? when closed\" do\n    ch = Channel(Int32).new\n    ch.close\n    ch.receive?.should be_nil\n  end\n\n  it \"can receive? when not empty\" do\n    ch = Channel(Int32).new\n    spawn { ch.send 123 }\n    ch.receive?.should eq(123)\n  end\n\n  it \"wakes up sender fiber when channel is closed\" do\n    ch = Channel(Nil).new\n    state = :none\n\n    Sync::CONCURRENT.spawn do\n      begin\n        state = :ready\n        ch.send(nil)\n      rescue Channel::ClosedError\n        state = :closed\n      end\n    end\n\n    Sync::CONCURRENT.spawn do\n      Sync.eventually { state.should eq(:ready) }\n      ch.close\n    end\n\n    Sync.eventually { state.should eq(:closed) }\n  end\n\n  it \"wakes up receiver fibers when channel is closed\" do\n    ch = Channel(Nil).new\n    state = :none\n    closed = false\n\n    Sync::CONCURRENT.spawn do\n      state = :ready\n      ch.receive\n    rescue Channel::ClosedError\n      closed = ch.closed?\n    end\n\n    Sync::CONCURRENT.spawn do\n      Sync.eventually { state.should eq(:ready) }\n      ch.close\n    end\n\n    Sync.eventually { closed.should be_true }\n  end\n\n  it \"can send successfully without raise\" do\n    ch = Channel(Int32).new\n    state = :none\n\n    Sync::CONCURRENT.spawn do\n      state = :ready\n      ch.send 1\n    rescue ex\n      state = :raised\n    else\n      state = :done\n    end\n\n    Sync::CONCURRENT.spawn do\n      Sync.eventually { state.should eq(:ready) }\n      ch.receive.should eq(1)\n      ch.close\n    end\n\n    Sync.eventually { state.should eq(:done) }\n  end\nend\n\ndescribe \"buffered\" do\n  it \"pings\" do\n    ch = Channel(Int32).new(10)\n    spawn { ch.send(ch.receive) }\n    ch.send 123\n    ch.receive.should eq(123)\n  end\n\n  it \"blocks when full\" do\n    ch = Channel(Int32).new(2)\n    done = false\n    f = spawn { 5.times { |i| ch.send i }; done = true }\n\n    ch.receive\n    done.should be_false\n\n    ch.receive\n    done.should be_false\n\n    # after the third receive, since the buffer is 2\n    # f should be able to exec fully\n    ch.receive\n    wait_until_finished f\n    done.should be_true\n  end\n\n  it \"doesn't block when not full\" do\n    ch = Channel(Int32).new(10)\n    done = false\n    spawn { ch.send 123; done = true }\n    Sync.eventually { done.should be_true }\n  end\n\n  it \"gets ready with data\" do\n    ch = Channel(Int32).new(10)\n    ch.send 123\n    ch.receive.should eq(123)\n  end\n\n  it \"works with select\" do\n    ch1 = Channel(Int32).new(10)\n    ch2 = Channel(Int32).new(10)\n    spawn { ch1.send 123 }\n    Channel.select(ch1.receive_select_action, ch2.receive_select_action).should eq({0, 123})\n  end\n\n  it \"can send and receive nil\" do\n    ch = Channel(Nil).new(10)\n    spawn { ch.send nil }\n    ch.receive.should be_nil\n  end\n\n  it \"can be closed\" do\n    ch = Channel(Int32).new(10)\n    ch.closed?.should be_false\n    ch.close\n    ch.closed?.should be_true\n    expect_raises(Channel::ClosedError) { ch.receive }\n  end\n\n  it \"can be closed after sending\" do\n    ch = Channel(Int32).new(10)\n    ch.send 123\n    ch.close\n    ch.receive.should eq(123)\n    expect_raises(Channel::ClosedError) { ch.receive }\n  end\n\n  it \"can be closed from different fiber\" do\n    ch = Channel(Int32).new(10)\n    ready = received = false\n\n    spawn do\n      ready = true\n      expect_raises(Channel::ClosedError) { ch.receive }\n      received = true\n    end\n    Sync.eventually { ready.should be_true }\n\n    ch.close\n    Sync.eventually { received.should be_true }\n  end\n\n  it \"cannot send if closed\" do\n    ch = Channel(Int32).new(10)\n    ch.close\n    expect_raises(Channel::ClosedError) { ch.send 123 }\n  end\n\n  it \"can receive? when closed\" do\n    ch = Channel(Int32).new(10)\n    ch.close\n    ch.receive?.should be_nil\n  end\n\n  it \"can receive? when not empty\" do\n    ch = Channel(Int32).new(10)\n    spawn { ch.send 123 }\n    ch.receive?.should eq(123)\n  end\n\n  it \"can send successfully without raise\" do\n    ch = Channel(Int32).new(1)\n    state = :none\n\n    Sync::CONCURRENT.spawn do\n      ch.send 1\n      state = :ready\n      ch.send 2\n    rescue ex\n      state = :raised\n    else\n      state = :done\n    end\n\n    Sync::CONCURRENT.spawn do\n      Sync.eventually { state.should eq(:ready) }\n      ch.receive.should eq(1)\n      ch.receive.should eq(2)\n      ch.close\n    end\n\n    Sync.eventually { {:raised, :done}.should contain(state) }\n    state.should eq(:done)\n  end\n\n  it \"does inspect on unbuffered channel\" do\n    ch = Channel(Int32).new\n    ch.inspect.should eq(\"#<Channel(Int32):0x#{ch.object_id.to_s(16)}>\")\n  end\n\n  it \"does inspect on buffered channel\" do\n    ch = Channel(Int32).new(10)\n    ch.inspect.should eq(\"#<Channel(Int32):0x#{ch.object_id.to_s(16)}>\")\n  end\n\n  it \"does pretty_inspect on unbuffered channel\" do\n    ch = Channel(Int32).new\n    ch.pretty_inspect.should eq(\"#<Channel(Int32):0x#{ch.object_id.to_s(16)}>\")\n  end\n\n  it \"does pretty_inspect on buffered channel\" do\n    ch = Channel(Int32).new(10)\n    ch.pretty_inspect.should eq(\"#<Channel(Int32):0x#{ch.object_id.to_s(16)}>\")\n  end\nend\n"
  },
  {
    "path": "spec/std/char/reader_spec.cr",
    "content": "require \"spec\"\nrequire \"char/reader\"\nrequire \"../../support/string\"\n\nprivate def assert_invalid_byte_sequence(bytes, *, file = __FILE__, line = __LINE__)\n  reader = Char::Reader.new(String.new bytes)\n  reader.current_char.should eq(Char::REPLACEMENT), file: file, line: line\n  reader.current_char_width.should eq(1), file: file, line: line\n  reader.error.should eq(bytes[0]), file: file, line: line\nend\n\nprivate def assert_reads_at_end(bytes, char, *, file = __FILE__, line = __LINE__)\n  str = String.new bytes\n  reader = Char::Reader.new(str, pos: bytes.size)\n  reader.previous_char.should eq(char), file: file, line: line\n  reader.current_char.should eq(char), file: file, line: line\n  reader.current_char_width.should eq(bytes.size), file: file, line: line\n  reader.pos.should eq(0), file: file, line: line\n  reader.error.should be_nil, file: file, line: line\nend\n\nprivate def assert_invalid_byte_sequence_at_end(bytes, *, file = __FILE__, line = __LINE__)\n  str = String.new bytes\n  reader = Char::Reader.new(str, pos: bytes.size)\n  reader.previous_char\n  reader.current_char.should eq(Char::REPLACEMENT), file: file, line: line\n  reader.current_char_width.should eq(1), file: file, line: line\n  reader.pos.should eq(bytes.size - 1), file: file, line: line\n  reader.error.should eq(bytes[-1]), file: file, line: line\nend\n\ndescribe \"Char::Reader\" do\n  it \"iterates through empty string\" do\n    reader = Char::Reader.new(\"\")\n    reader.pos.should eq(0)\n    reader.current_char.ord.should eq(0)\n    reader.error.should be_nil\n    reader.has_next?.should be_false\n\n    expect_raises IndexError do\n      reader.next_char\n    end\n  end\n\n  it \"iterates through string of size one\" do\n    reader = Char::Reader.new(\"a\")\n    reader.pos.should eq(0)\n    reader.current_char.should eq('a')\n    reader.has_next?.should be_true\n    reader.next_char.ord.should eq(0)\n    reader.has_next?.should be_false\n\n    expect_raises IndexError do\n      reader.next_char\n    end\n  end\n\n  it \"iterates through chars\" do\n    reader = Char::Reader.new(\"há日本語\")\n    reader.pos.should eq(0)\n    reader.current_char.ord.should eq(104)\n    reader.has_next?.should be_true\n\n    reader.next_char.ord.should eq(225)\n\n    reader.pos.should eq(1)\n    reader.current_char.ord.should eq(225)\n\n    reader.next_char.ord.should eq(26085)\n    reader.next_char.ord.should eq(26412)\n    reader.next_char.ord.should eq(35486)\n    reader.has_next?.should be_true\n\n    reader.next_char.ord.should eq(0)\n    reader.has_next?.should be_false\n\n    expect_raises IndexError do\n      reader.next_char\n    end\n  end\n\n  it \"peeks next char\" do\n    reader = Char::Reader.new(\"há日本語\")\n    reader.peek_next_char.ord.should eq(225)\n  end\n\n  it \"sets pos\" do\n    reader = Char::Reader.new(\"há日本語\")\n    reader.pos = 1\n    reader.pos.should eq(1)\n    reader.current_char.ord.should eq(225)\n  end\n\n  describe \"#each\" do\n    it \"yields chars\" do\n      reader = Char::Reader.new(\"abc\")\n      chars = [] of Char\n      reader.each do |char|\n        chars << char\n      end.should be_nil\n      chars.should eq ['a', 'b', 'c']\n    end\n\n    it \"does not yield if empty\" do\n      reader = Char::Reader.new(\"\")\n      reader.each do |char|\n        fail \"reader each shouldn't yield on empty string\"\n      end.should be_nil\n    end\n\n    it \"checks bounds after block\" do\n      string = \"f\"\n      reader = Char::Reader.new(string)\n      reader.each do |c|\n        c.should eq 'f'\n        reader.next_char\n      end\n    end\n  end\n\n  it \"starts at end\" do\n    reader = Char::Reader.new(at_end: \"\")\n    reader.pos.should eq(0)\n    reader.current_char.ord.should eq(0)\n    reader.has_previous?.should be_false\n    reader.has_next?.should be_false\n  end\n\n  it \"gets previous char (ascii)\" do\n    reader = Char::Reader.new(at_end: \"hello\")\n    reader.pos.should eq(4)\n    reader.current_char.should eq('o')\n    reader.has_previous?.should be_true\n    reader.has_next?.should be_true\n\n    reader.previous_char.should eq('l')\n    reader.has_next?.should be_true\n    reader.previous_char.should eq('l')\n    reader.previous_char.should eq('e')\n    reader.previous_char.should eq('h')\n    reader.has_previous?.should be_false\n\n    expect_raises IndexError do\n      reader.previous_char\n    end\n  end\n\n  it \"gets previous char (unicode)\" do\n    reader = Char::Reader.new(at_end: \"há日本語\")\n    reader.pos.should eq(9)\n    reader.current_char.should eq('語')\n    reader.has_previous?.should be_true\n    reader.has_next?.should be_true\n\n    reader.previous_char.should eq('本')\n    reader.has_next?.should be_true\n    reader.previous_char.should eq('日')\n    reader.previous_char.should eq('á')\n    reader.previous_char.should eq('h')\n    reader.has_previous?.should be_false\n  end\n\n  it \"starts at pos\" do\n    reader = Char::Reader.new(\"há日本語\", pos: 9)\n    reader.pos.should eq(9)\n    reader.current_char.should eq('語')\n  end\n\n  it \"#current_char?\" do\n    reader = Char::Reader.new(\"há日本語\")\n    reader.current_char?.should eq('h')\n    reader.next_char\n    reader.current_char?.should eq('á')\n    reader.next_char\n    reader.current_char?.should eq('日')\n    reader.next_char\n    reader.current_char?.should eq('本')\n    reader.next_char\n    reader.current_char?.should eq('語')\n    reader.next_char\n    reader.current_char?.should be_nil\n    reader.previous_char\n    reader.current_char?.should eq('語')\n  end\n\n  it \"#next_char?\" do\n    reader = Char::Reader.new(\"há日本語\")\n    reader.next_char?.should eq('á')\n    reader.pos.should eq(1)\n    reader.next_char?.should eq('日')\n    reader.pos.should eq(3)\n    reader.next_char?.should eq('本')\n    reader.pos.should eq(6)\n    reader.next_char?.should eq('語')\n    reader.pos.should eq(9)\n    reader.next_char?.should be_nil\n    reader.pos.should eq(12)\n    reader.next_char?.should be_nil\n    reader.pos.should eq(12)\n  end\n\n  it \"#previous_char?\" do\n    reader = Char::Reader.new(\"há日本語\", pos: 12)\n    reader.previous_char?.should eq('語')\n    reader.pos.should eq(9)\n    reader.previous_char?.should eq('本')\n    reader.pos.should eq(6)\n    reader.previous_char?.should eq('日')\n    reader.pos.should eq(3)\n    reader.previous_char?.should eq('á')\n    reader.pos.should eq(1)\n    reader.previous_char?.should eq('h')\n    reader.pos.should eq(0)\n    reader.previous_char?.should be_nil\n    reader.pos.should eq(0)\n  end\n\n  it \"errors on invalid UTF-8\" do\n    {% for bytes in INVALID_UTF8_BYTE_SEQUENCES %}\n      assert_invalid_byte_sequence Bytes{{ bytes }}\n    {% end %}\n  end\n\n  describe \"#previous_char\" do\n    it \"reads on valid UTF-8\" do\n      {% for bytes, char in VALID_UTF8_BYTE_SEQUENCES %}\n        assert_reads_at_end Bytes{{ bytes }}, {{ char }}\n      {% end %}\n    end\n\n    it \"errors on invalid UTF-8\" do\n      assert_invalid_byte_sequence_at_end Bytes[0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xbf]\n      assert_invalid_byte_sequence_at_end Bytes[0xc0]\n      assert_invalid_byte_sequence_at_end Bytes[0xff]\n\n      assert_invalid_byte_sequence_at_end Bytes[0x00, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x7f, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x9f, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xbf, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc1, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xe0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xff, 0x80]\n\n      assert_invalid_byte_sequence_at_end Bytes[0x00, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x7f, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x80, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x8f, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x90, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xbf, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc0, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc1, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc2, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xdf, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xe0, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xe0, 0x9f, 0xbf]\n      assert_invalid_byte_sequence_at_end Bytes[0xf0, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xff, 0x80, 0x80]\n\n      assert_invalid_byte_sequence_at_end Bytes[0x00, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x7f, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x80, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x8f, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0x90, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xbf, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc0, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc1, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xc2, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xdf, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xed, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xed, 0xbf, 0xbf]\n      assert_invalid_byte_sequence_at_end Bytes[0xf0, 0xa0, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xff, 0xa0, 0x80]\n\n      assert_invalid_byte_sequence_at_end Bytes[0x00, 0x80, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xef, 0x80, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xf0, 0x80, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xf5, 0x80, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xff, 0x80, 0x80, 0x80]\n\n      assert_invalid_byte_sequence_at_end Bytes[0x00, 0x90, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xef, 0x90, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xf4, 0x90, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xf5, 0x90, 0x80, 0x80]\n      assert_invalid_byte_sequence_at_end Bytes[0xff, 0x90, 0x80, 0x80]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/char_spec.cr",
    "content": "require \"spec\"\nrequire \"unicode\"\nrequire \"spec/helpers/iterate\"\nrequire \"spec/helpers/string\"\n\ndescribe \"Char\" do\n  describe \"#upcase\" do\n    it { 'a'.upcase.should eq('A') }\n    it { '1'.upcase.should eq('1') }\n    it { assert_iterates_yielding ['F', 'F', 'L'], 'ﬄ'.upcase }\n\n    it \"writes to IO\" do\n      io = IO::Memory.new\n      'a'.upcase(io)\n      '1'.upcase(io)\n      'ﬄ'.upcase(io)\n      io.to_s.should eq(\"A1FFL\")\n    end\n  end\n\n  describe \"#downcase\" do\n    it { 'A'.downcase.should eq('a') }\n    it { '1'.downcase.should eq('1') }\n    it { assert_iterates_yielding ['i', '\\u{0307}'], 'İ'.downcase }\n    it { assert_iterates_yielding ['s', 's'], 'ß'.downcase(Unicode::CaseOptions::Fold) }\n    it { 'Ń'.downcase(Unicode::CaseOptions::Fold).should eq('ń') }\n    it { 'ꭰ'.downcase(Unicode::CaseOptions::Fold).should eq('Ꭰ') } # U+AB70 CHEROKEE SMALL LETTER A\n    it { 'Ꭰ'.downcase(Unicode::CaseOptions::Fold).should eq('Ꭰ') } # U+13A0 CHEROKEE LETTER A\n\n    it \"writes to IO\" do\n      io = IO::Memory.new\n      'A'.downcase(io)\n      '1'.downcase(io)\n      'İ'.downcase(io)\n      'ß'.downcase(io, Unicode::CaseOptions::Fold)\n      'Ń'.downcase(io, Unicode::CaseOptions::Fold)\n      'ꭰ'.downcase(io, Unicode::CaseOptions::Fold)\n      'Ꭰ'.downcase(io, Unicode::CaseOptions::Fold)\n      io.to_s.should eq(\"a1i\\u{0307}ssńᎠᎠ\")\n    end\n  end\n\n  describe \"#titlecase\" do\n    it { 'a'.titlecase.should eq('A') }\n    it { '1'.titlecase.should eq('1') }\n    it { '\\u{10D0}'.titlecase.should eq('\\u{10D0}') } # GEORGIAN LETTER AN\n    it { assert_iterates_yielding ['F', 'f', 'l'], 'ﬄ'.titlecase }\n\n    it \"writes to IO\" do\n      io = IO::Memory.new\n      'a'.titlecase(io)\n      '1'.titlecase(io)\n      '\\u{10D0}'.titlecase(io)\n      'ﬄ'.titlecase(io)\n      io.to_s.should eq(\"A1\\u{10D0}Ffl\")\n    end\n  end\n\n  it \"#succ\" do\n    'a'.succ.should eq('b')\n    'あ'.succ.should eq('ぃ')\n\n    '\\uD7FF'.succ.should eq '\\uE000'\n\n    expect_raises OverflowError, \"Out of Char range\" do\n      Char::MAX.succ\n    end\n  end\n\n  it \"#pred\" do\n    'b'.pred.should eq('a')\n    'ぃ'.pred.should eq('あ')\n\n    '\\uE000'.pred.should eq '\\uD7FF'\n\n    expect_raises OverflowError, \"Out of Char range\" do\n      Char::ZERO.pred\n    end\n  end\n\n  describe \"+\" do\n    it { ('a' + 2).should eq('c') }\n  end\n\n  describe \"-\" do\n    it { ('c' - 2).should eq('a') }\n  end\n\n  describe \"ascii_uppercase?\" do\n    it { 'a'.ascii_uppercase?.should be_false }\n    it { 'A'.ascii_uppercase?.should be_true }\n    it { '1'.ascii_uppercase?.should be_false }\n    it { ' '.ascii_uppercase?.should be_false }\n  end\n\n  describe \"uppercase?\" do\n    it { 'A'.uppercase?.should be_true }\n    it { 'Á'.uppercase?.should be_true }\n    it { 'Ā'.uppercase?.should be_true }\n    it { 'Ą'.uppercase?.should be_true }\n    it { 'ā'.uppercase?.should be_false }\n    it { 'á'.uppercase?.should be_false }\n    it { 'a'.uppercase?.should be_false }\n    it { '1'.uppercase?.should be_false }\n    it { ' '.uppercase?.should be_false }\n  end\n\n  describe \"ascii_lowercase?\" do\n    it { 'a'.ascii_lowercase?.should be_true }\n    it { 'A'.ascii_lowercase?.should be_false }\n    it { '1'.ascii_lowercase?.should be_false }\n    it { ' '.ascii_lowercase?.should be_false }\n  end\n\n  describe \"lowercase?\" do\n    it { 'a'.lowercase?.should be_true }\n    it { 'á'.lowercase?.should be_true }\n    it { 'ā'.lowercase?.should be_true }\n    it { 'ă'.lowercase?.should be_true }\n    it { 'A'.lowercase?.should be_false }\n    it { 'Á'.lowercase?.should be_false }\n    it { '1'.lowercase?.should be_false }\n    it { ' '.lowercase?.should be_false }\n  end\n\n  describe \"#titlecase?\" do\n    it { 'ǲ'.titlecase?.should be_true }\n    it { 'ᾈ'.titlecase?.should be_true }\n    it { 'A'.titlecase?.should be_false }\n    it { 'a'.titlecase?.should be_false }\n  end\n\n  describe \"ascii_letter?\" do\n    it { 'a'.ascii_letter?.should be_true }\n    it { 'A'.ascii_letter?.should be_true }\n    it { '1'.ascii_letter?.should be_false }\n    it { ' '.ascii_letter?.should be_false }\n  end\n\n  it \"#letter?\" do\n    'A'.letter?.should be_true # Unicode General Category Lu\n    'a'.letter?.should be_true # Unicode General Category Ll\n    'ǅ'.letter?.should be_true # Unicode General Category Lt\n    'ʰ'.letter?.should be_true # Unicode General Category Lm\n    'か'.letter?.should be_true # Unicode General Category Lo\n\n    'ः'.letter?.should be_false  # Unicode General Category M\n    '1'.letter?.should be_false  # Unicode General Category Nd\n    'Ⅰ'.letter?.should be_false  # Unicode General Category Nl\n    '_'.letter?.should be_false  # Unicode General Category P\n    '$'.letter?.should be_false  # Unicode General Category S\n    ' '.letter?.should be_false  # Unicode General Category Z\n    '\\n'.letter?.should be_false # Unicode General Category C\n  end\n\n  describe \"alphanumeric?\" do\n    it { 'a'.alphanumeric?.should be_true }\n    it { 'A'.alphanumeric?.should be_true }\n    it { '1'.alphanumeric?.should be_true }\n    it { ' '.alphanumeric?.should be_false }\n  end\n\n  describe \"ascii_whitespace?\" do\n    [' ', '\\t', '\\n', '\\v', '\\f', '\\r'].each do |char|\n      it { char.ascii_whitespace?.should be_true }\n    end\n    it { 'A'.ascii_whitespace?.should be_false }\n  end\n\n  describe \"hex?\" do\n    \"0123456789abcdefABCDEF\".each_char do |char|\n      it { char.hex?.should be_true }\n    end\n    ('g'..'z').each do |char|\n      it { char.hex?.should be_false }\n    end\n    [' ', '-', '\\0'].each do |char|\n      it { char.hex?.should be_false }\n    end\n  end\n\n  it \"#dump\" do\n    assert_prints 'a'.dump, %('a')\n    assert_prints '\\\\'.dump, %('\\\\\\\\')\n    assert_prints '\\0'.dump, %('\\\\0')\n    assert_prints '\\u0001'.dump, %('\\\\u0001')\n    assert_prints ' '.dump, %(' ')\n    assert_prints '\\a'.dump, %('\\\\a')\n    assert_prints '\\b'.dump, %('\\\\b')\n    assert_prints '\\e'.dump, %('\\\\e')\n    assert_prints '\\f'.dump, %('\\\\f')\n    assert_prints '\\n'.dump, %('\\\\n')\n    assert_prints '\\r'.dump, %('\\\\r')\n    assert_prints '\\t'.dump, %('\\\\t')\n    assert_prints '\\v'.dump, %('\\\\v')\n    assert_prints '\\f'.dump, %('\\\\f')\n    assert_prints 'á'.dump, %('\\\\u00E1')\n    assert_prints '\\uF8FF'.dump, %('\\\\uF8FF')\n    assert_prints '\\u202A'.dump, %('\\\\u202A')\n    assert_prints '\\u{81}'.dump, %('\\\\u0081')\n    assert_prints '\\u{110BD}'.dump, %('\\\\u{110BD}')\n    assert_prints '\\u{1F48E}'.dump, %('\\\\u{1F48E}')\n    assert_prints '\\u00AD'.dump, %('\\\\u00AD')\n  end\n\n  it \"#inspect\" do\n    assert_prints 'a'.inspect, %('a')\n    assert_prints '\\\\'.inspect, %('\\\\\\\\')\n    assert_prints '\\0'.inspect, %('\\\\0')\n    assert_prints '\\u0001'.inspect, %('\\\\u0001')\n    assert_prints ' '.inspect, %(' ')\n    assert_prints '\\a'.inspect, %('\\\\a')\n    assert_prints '\\b'.inspect, %('\\\\b')\n    assert_prints '\\e'.inspect, %('\\\\e')\n    assert_prints '\\f'.inspect, %('\\\\f')\n    assert_prints '\\n'.inspect, %('\\\\n')\n    assert_prints '\\r'.inspect, %('\\\\r')\n    assert_prints '\\t'.inspect, %('\\\\t')\n    assert_prints '\\v'.inspect, %('\\\\v')\n    assert_prints '\\f'.inspect, %('\\\\f')\n    assert_prints 'á'.inspect, %('á')\n    assert_prints '\\uF8FF'.inspect, %('\\\\uF8FF')\n    assert_prints '\\u202A'.inspect, %('\\\\u202A')\n    assert_prints '\\u{81}'.inspect, %('\\\\u0081')\n    assert_prints '\\u{110BD}'.inspect, %('\\\\u{110BD}')\n    assert_prints '\\u{1F48E}'.inspect, %('\\u{1F48E}')\n    assert_prints '\\u00AD'.inspect, %('\\\\u00AD')\n  end\n\n  it \"#unicode_escape\" do\n    assert_prints 'a'.unicode_escape, %(\\\\u0061)\n    assert_prints '\\\\'.unicode_escape, %(\\\\u005C)\n    assert_prints '\\0'.unicode_escape, %(\\\\u0000)\n    assert_prints '\\u0001'.unicode_escape, %(\\\\u0001)\n    assert_prints ' '.unicode_escape, %(\\\\u0020)\n    assert_prints '\\a'.unicode_escape, %(\\\\u0007)\n    assert_prints '\\b'.unicode_escape, %(\\\\u0008)\n    assert_prints '\\e'.unicode_escape, %(\\\\u001B)\n    assert_prints '\\f'.unicode_escape, %(\\\\u000C)\n    assert_prints '\\n'.unicode_escape, %(\\\\u000A)\n    assert_prints '\\r'.unicode_escape, %(\\\\u000D)\n    assert_prints '\\t'.unicode_escape, %(\\\\u0009)\n    assert_prints '\\v'.unicode_escape, %(\\\\u000B)\n    assert_prints '\\f'.unicode_escape, %(\\\\u000C)\n    assert_prints 'á'.unicode_escape, %(\\\\u00E1)\n    assert_prints '\\uF8FF'.unicode_escape, %(\\\\uF8FF)\n    assert_prints '\\u202A'.unicode_escape, %(\\\\u202A)\n    assert_prints '\\u{81}'.unicode_escape, %(\\\\u0081)\n    assert_prints '\\u{110BD}'.unicode_escape, %(\\\\u{110BD})\n    assert_prints '\\u00AD'.unicode_escape, %(\\\\u00AD)\n  end\n\n  it \"escapes\" do\n    '\\a'.ord.should eq(7)\n    '\\b'.ord.should eq(8)\n    '\\t'.ord.should eq(9)\n    '\\n'.ord.should eq(10)\n    '\\v'.ord.should eq(11)\n    '\\f'.ord.should eq(12)\n    '\\r'.ord.should eq(13)\n    '\\e'.ord.should eq(27)\n    '\\''.ord.should eq(39)\n    '\\\\'.ord.should eq(92)\n  end\n\n  it \"escapes with unicode\" do\n    '\\u{12}'.ord.should eq(1 * 16 + 2)\n    '\\u{A}'.ord.should eq(10)\n    '\\u{AB}'.ord.should eq(10 * 16 + 11)\n  end\n\n  it \"does to_i without a base\" do\n    ('0'..'9').each_with_index do |c, i|\n      c.to_i.should eq(i)\n    end\n    expect_raises(ArgumentError) { 'a'.to_i }\n    'a'.to_i?.should be_nil\n\n    '1'.to_i8.should eq(1i8)\n    '1'.to_i16.should eq(1i16)\n    '1'.to_i32.should eq(1i32)\n    '1'.to_i64.should eq(1i64)\n    '1'.to_i128.should eq(1i128)\n\n    expect_raises(ArgumentError) { 'a'.to_i8 }\n    expect_raises(ArgumentError) { 'a'.to_i16 }\n    expect_raises(ArgumentError) { 'a'.to_i32 }\n    expect_raises(ArgumentError) { 'a'.to_i64 }\n    expect_raises(ArgumentError) { 'a'.to_i128 }\n\n    'a'.to_i8?.should be_nil\n    'a'.to_i16?.should be_nil\n    'a'.to_i32?.should be_nil\n    'a'.to_i64?.should be_nil\n    'a'.to_i128?.should be_nil\n\n    '1'.to_u8.should eq(1u8)\n    '1'.to_u16.should eq(1u16)\n    '1'.to_u32.should eq(1u32)\n    '1'.to_u64.should eq(1u64)\n    '1'.to_u128.should eq(1u128)\n\n    expect_raises(ArgumentError) { 'a'.to_u8 }\n    expect_raises(ArgumentError) { 'a'.to_u16 }\n    expect_raises(ArgumentError) { 'a'.to_u32 }\n    expect_raises(ArgumentError) { 'a'.to_u64 }\n    expect_raises(ArgumentError) { 'a'.to_u128 }\n\n    'a'.to_u8?.should be_nil\n    'a'.to_u16?.should be_nil\n    'a'.to_u32?.should be_nil\n    'a'.to_u64?.should be_nil\n    'a'.to_u128?.should be_nil\n  end\n\n  it \"does to_i with 16 base\" do\n    ('0'..'9').each_with_index do |c, i|\n      c.to_i(16).should eq(i)\n    end\n    ('a'..'f').each_with_index do |c, i|\n      c.to_i(16).should eq(10 + i)\n    end\n    ('A'..'F').each_with_index do |c, i|\n      c.to_i(16).should eq(10 + i)\n    end\n    expect_raises(ArgumentError) { 'Z'.to_i(16) }\n    'Z'.to_i?(16).should be_nil\n  end\n\n  it \"does to_i with base 36\" do\n    letters = ('0'..'9').each.chain(('a'..'z').each).chain(('A'..'Z').each)\n    nums = (0..9).each.chain((10..35).each).chain((10..35).each)\n    letters.zip(nums).each do |(letter, num)|\n      letter.to_i(36).should eq(num)\n    end\n  end\n\n  it \"to_i rejects unsupported base (1)\" do\n    expect_raises ArgumentError, \"Invalid base 1\" do\n      '0'.to_i(1)\n    end\n  end\n\n  it \"to_i rejects unsupported base (37)\" do\n    expect_raises ArgumentError, \"Invalid base 37\" do\n      '0'.to_i(37)\n    end\n  end\n\n  it \"does to_f\" do\n    ('0'..'9').each.zip((0..9).each).each do |c, i|\n      c.to_f.should eq(i.to_f)\n    end\n    expect_raises(ArgumentError) { 'A'.to_f }\n    '1'.to_f32.should eq(1.0f32)\n    '1'.to_f64.should eq(1.0f64)\n    'a'.to_f?.should be_nil\n    'a'.to_f32?.should be_nil\n    'a'.to_f64?.should be_nil\n  end\n\n  it \"does ord for multibyte char\" do\n    '日'.ord.should eq(26085)\n  end\n\n  it \"does to_s for single-byte char\" do\n    'a'.to_s.should eq(\"a\")\n  end\n\n  it \"does to_s for multibyte char\" do\n    '日'.to_s.should eq(\"日\")\n  end\n\n  describe \"index\" do\n    it { \"foo\".index('o').should eq(1) }\n    it { \"foo\".index('x').should be_nil }\n  end\n\n  it \"does <=>\" do\n    ('a' <=> 'b').should be < 0\n    ('a' <=> 'a').should eq(0)\n    ('b' <=> 'a').should be > 0\n  end\n\n  describe \"#step\" do\n    it_iterates \"basic\", ['a', 'b', 'c', 'd', 'e'], 'a'.step(to: 'e')\n    it_iterates \"basic by\", ['a', 'c', 'e'], 'a'.step(to: 'e', by: 2)\n  end\n\n  describe \"+\" do\n    it \"does for both ascii\" do\n      str = 'f' + \"oo\"\n      str.@length.should eq(3) # Check that it was precomputed\n      str.should eq(\"foo\")\n    end\n\n    it \"does for both unicode\" do\n      str = '青' + \"旅路\"\n      str.@length.should eq(3) # Check that it was precomputed\n      str.should eq(\"青旅路\")\n    end\n  end\n\n  describe \"bytesize\" do\n    it \"does for ascii\" do\n      'a'.bytesize.should eq(1)\n    end\n\n    it \"does for unicode\" do\n      '青'.bytesize.should eq(3)\n    end\n  end\n\n  describe \"in_set?\" do\n    it { 'a'.in_set?(\"a\").should be_true }\n    it { 'a'.in_set?(\"b\").should be_false }\n    it { 'a'.in_set?(\"a-c\").should be_true }\n    it { 'b'.in_set?(\"a-c\").should be_true }\n    it { 'c'.in_set?(\"a-c\").should be_true }\n    it { 'c'.in_set?(\"a-bc\").should be_true }\n    it { 'b'.in_set?(\"a-bc\").should be_true }\n    it { 'd'.in_set?(\"a-c\").should be_false }\n    it { 'b'.in_set?(\"^a-c\").should be_false }\n    it { 'd'.in_set?(\"^a-c\").should be_true }\n    it { 'a'.in_set?(\"ab-c\").should be_true }\n    it { 'a'.in_set?(\"\\\\^ab-c\").should be_true }\n    it { '^'.in_set?(\"\\\\^ab-c\").should be_true }\n    it { '^'.in_set?(\"a^b-c\").should be_true }\n    it { '^'.in_set?(\"ab-c^\").should be_true }\n    it { '^'.in_set?(\"a0-^\").should be_true }\n    it { '^'.in_set?(\"^-c\").should be_true }\n    it { '^'.in_set?(\"a^-c\").should be_true }\n    it { '\\\\'.in_set?(\"ab-c\\\\\").should be_true }\n    it { '\\\\'.in_set?(\"a\\\\b-c\").should be_false }\n    it { '\\\\'.in_set?(\"a0-\\\\c\").should be_true }\n    it { '\\\\'.in_set?(\"a\\\\-c\").should be_false }\n    it { '-'.in_set?(\"a-c\").should be_false }\n    it { '-'.in_set?(\"a-c\").should be_false }\n    it { '-'.in_set?(\"a\\\\-c\").should be_true }\n    it { '-'.in_set?(\"-c\").should be_true }\n    it { '-'.in_set?(\"a-\").should be_true }\n    it { '-'.in_set?(\"^-c\").should be_false }\n    it { '-'.in_set?(\"^\\\\-c\").should be_false }\n    it { 'b'.in_set?(\"^\\\\-c\").should be_true }\n    it { '-'.in_set?(\"a^-c\").should be_false }\n    it { 'a'.in_set?(\"a\", \"ab\").should be_true }\n    it { 'a'.in_set?(\"a\", \"^b\").should be_true }\n    it { 'a'.in_set?(\"a\", \"b\").should be_false }\n    it { 'a'.in_set?(\"ab\", \"ac\", \"ad\").should be_true }\n\n    it \"rejects invalid ranges\" do\n      expect_raises(ArgumentError, \"Invalid range c-a\") do\n        'a'.in_set?(\"c-a\")\n      end\n    end\n  end\n\n  it \"does each_byte\" do\n    'a'.each_byte(&.should eq('a'.ord)).should be_nil\n  end\n\n  it \"does bytes\" do\n    '\\u{FF}'.bytes.should eq([195, 191])\n  end\n\n  it \"#===(:Int)\" do\n    ('c'.ord).should eq(99)\n    ('c' === 99_u8).should be_true\n    ('c' === 99).should be_true\n    ('z' === 99).should be_false\n\n    ('酒'.ord).should eq(37202)\n    ('酒' === 37202).should be_true\n  end\n\n  it \"does ascii_number?\" do\n    256.times do |i|\n      chr = i.chr\n      (\"01\".chars.includes?(chr) == chr.ascii_number?(2)).should be_true\n      (\"01234567\".chars.includes?(chr) == chr.ascii_number?(8)).should be_true\n      (\"0123456789\".chars.includes?(chr) == chr.ascii_number?).should be_true\n      (\"0123456789\".chars.includes?(chr) == chr.ascii_number?(10)).should be_true\n      (\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\".includes?(chr) == chr.ascii_number?(36)).should be_true\n      unless 2 <= i <= 36\n        expect_raises ArgumentError do\n          '0'.ascii_number?(i)\n        end\n      end\n    end\n  end\n\n  it \"does number?\" do\n    '1'.number?.should be_true\n    '٠'.number?.should be_true\n    '٢'.number?.should be_true\n    'a'.number?.should be_false\n  end\n\n  it \"#ascii_control?\" do\n    'ù'.ascii_control?.should be_false\n    'a'.ascii_control?.should be_false\n    '\\u0019'.ascii_control?.should be_true\n    '\\u007F'.ascii_control?.should be_true\n    '\\u0080'.ascii_control?.should be_false\n  end\n\n  it \"does mark?\" do\n    0x300.chr.mark?.should be_true\n  end\n\n  it \"does ascii?\" do\n    'a'.ascii?.should be_true\n    127.chr.ascii?.should be_true\n    128.chr.ascii?.should be_false\n    '酒'.ascii?.should be_false\n  end\n\n  it \"#printable?\" do\n    ' '.printable?.should be_true\n    'a'.printable?.should be_true\n    '酒'.printable?.should be_true\n    '\\n'.printable?.should be_false\n    '\\e'.printable?.should be_false\n    '\\uF8FF'.printable?.should be_false\n  end\n\n  describe \"clone\" do\n    it { 'a'.clone.should eq('a') }\n  end\nend\n"
  },
  {
    "path": "spec/std/class_spec.cr",
    "content": "require \"spec\"\n\nprivate class A\nend\n\nprivate class B1 < A\nend\n\nprivate class C1 < B1\nend\n\nprivate class B2 < A\nend\n\nprivate class ClassWithRedefinedName\n  def self.name\n    \"OtherName\"\n  end\nend\n\nprivate alias RecursiveNilableType = Array(RecursiveNilableType)?\n\ndescribe Class do\n  it \"does ===\" do\n    (Int32 === 1).should be_true\n    (Int32 === 1.5).should be_false\n    (Array === [1]).should be_true\n    (Array(Int32) === [1]).should be_true\n    (Array(Int32) === ['a']).should be_false\n    (Int32.class === 1).should be_false\n    (Int32.class === 1.5).should be_false\n    (Int32.class === Int32).should be_true\n    (Int32.class === Array).should be_true\n  end\n\n  it \"casts, allowing the class to be passed in at runtime\" do\n    ar = [99, \"something\"]\n    cl = {Int32, String}\n    casted = {cl[0].cast(ar[0]), cl[1].cast(ar[1])}\n    casted.should eq({99, \"something\"})\n    typeof(casted[0]).should eq(Int32)\n    typeof(casted[1]).should eq(String)\n  end\n\n  it \"does |\" do\n    (Int32 | Char).should eq(typeof(1, 'a'))\n    (Int32 | Char | Float64).should eq(typeof(1, 'a', 1.0))\n  end\n\n  it \"dups\" do\n    Int32.dup.should eq(Int32)\n  end\n\n  it \"clones\" do\n    Int32.clone.should eq(Int32)\n  end\n\n  it \"#nilable?\" do\n    Int32.nilable?.should be_false\n    Nil.nilable?.should be_true\n    (Int32 | String).nilable?.should be_false\n    Int32?.nilable?.should be_true\n    NoReturn.nilable?.should be_false\n    Reference.nilable?.should be_false\n    Value.nilable?.should be_true\n    Class.nilable?.should be_false\n    Object.nilable?.should be_true\n    RecursiveNilableType.nilable?.should be_true\n  end\n\n  it \"does to_s\" do\n    Int32.to_s.should eq(\"Int32\")\n  end\n\n  it \"does to_s with name redefined (#7292)\" do\n    ClassWithRedefinedName.name.should eq(\"OtherName\")\n    ClassWithRedefinedName.to_s.should eq(\"ClassWithRedefinedName\")\n  end\n\n  describe \"comparison operators\" do\n    t = [A, B1, B2, C1]\n\n    it \"<\" do\n      (t[0] < t[0]).should be_false\n      (t[0] < t[1]).should be_false\n      (t[0] < t[2]).should be_false\n      (t[0] < t[3]).should be_false\n\n      (t[1] < t[0]).should be_true\n      (t[1] < t[1]).should be_false\n      (t[1] < t[2]).should be_false\n      (t[1] < t[3]).should be_false\n\n      (t[2] < t[0]).should be_true\n      (t[2] < t[1]).should be_false\n      (t[2] < t[2]).should be_false\n      (t[2] < t[3]).should be_false\n\n      (t[3] < t[0]).should be_true\n      (t[3] < t[1]).should be_true\n      (t[3] < t[2]).should be_false\n      (t[3] < t[3]).should be_false\n    end\n\n    it \"<=\" do\n      (t[0] <= t[0]).should be_true\n      (t[0] <= t[1]).should be_false\n      (t[0] <= t[2]).should be_false\n      (t[0] <= t[3]).should be_false\n\n      (t[1] <= t[0]).should be_true\n      (t[1] <= t[1]).should be_true\n      (t[1] <= t[2]).should be_false\n      (t[1] <= t[3]).should be_false\n\n      (t[2] <= t[0]).should be_true\n      (t[2] <= t[1]).should be_false\n      (t[2] <= t[2]).should be_true\n      (t[2] <= t[3]).should be_false\n\n      (t[3] <= t[0]).should be_true\n      (t[3] <= t[1]).should be_true\n      (t[3] <= t[2]).should be_false\n      (t[3] <= t[3]).should be_true\n    end\n\n    it \">\" do\n      (t[0] > t[0]).should be_false\n      (t[0] > t[1]).should be_true\n      (t[0] > t[2]).should be_true\n      (t[0] > t[3]).should be_true\n\n      (t[1] > t[0]).should be_false\n      (t[1] > t[1]).should be_false\n      (t[1] > t[2]).should be_false\n      (t[1] > t[3]).should be_true\n\n      (t[2] > t[0]).should be_false\n      (t[2] > t[1]).should be_false\n      (t[2] > t[2]).should be_false\n      (t[2] > t[3]).should be_false\n\n      (t[3] > t[0]).should be_false\n      (t[3] > t[1]).should be_false\n      (t[3] > t[2]).should be_false\n      (t[3] > t[3]).should be_false\n    end\n\n    it \">=\" do\n      (t[0] >= t[0]).should be_true\n      (t[0] >= t[1]).should be_true\n      (t[0] >= t[2]).should be_true\n      (t[0] >= t[3]).should be_true\n\n      (t[1] >= t[0]).should be_false\n      (t[1] >= t[1]).should be_true\n      (t[1] >= t[2]).should be_false\n      (t[1] >= t[3]).should be_true\n\n      (t[2] >= t[0]).should be_false\n      (t[2] >= t[1]).should be_false\n      (t[2] >= t[2]).should be_true\n      (t[2] >= t[3]).should be_false\n\n      (t[3] >= t[0]).should be_false\n      (t[3] >= t[1]).should be_false\n      (t[3] >= t[2]).should be_false\n      (t[3] >= t[3]).should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/colorize_spec.cr",
    "content": "require \"spec\"\nrequire \"colorize\"\nrequire \"../support/env\"\n\nprivate def colorize(obj, *args)\n  obj.colorize(*args).toggle(true)\nend\n\nprivate def with_color_wrap(*args)\n  Colorize.with(*args).toggle(true)\nend\n\nprivate class ColorizeToS\n  def to_s(io)\n    io << \"hello\"\n    io << ::colorize(\"world\").blue\n    io << \"bye\"\n  end\nend\n\nprivate class ColorizeTTY < IO\n  def tty? : Bool\n    true\n  end\n\n  def read(slice : Bytes)\n    0\n  end\n\n  def write(slice : Bytes) : Nil\n  end\nend\n\ndescribe \"colorize\" do\n  it \".default_enabled?\" do\n    io = IO::Memory.new\n    tty = ColorizeTTY.new\n\n    with_env(\"TERM\": nil, \"NO_COLOR\": nil) do\n      Colorize.default_enabled?(io).should be_false\n      Colorize.default_enabled?(tty).should be_true\n      Colorize.default_enabled?(io, io).should be_false\n      Colorize.default_enabled?(io, tty).should be_false\n      Colorize.default_enabled?(tty, io).should be_false\n      Colorize.default_enabled?(tty, tty).should be_true\n    end\n\n    with_env(\"TERM\": nil, \"NO_COLOR\": \"\") do\n      Colorize.default_enabled?(io).should be_false\n      Colorize.default_enabled?(tty).should be_true\n      Colorize.default_enabled?(io, io).should be_false\n      Colorize.default_enabled?(io, tty).should be_false\n      Colorize.default_enabled?(tty, io).should be_false\n      Colorize.default_enabled?(tty, tty).should be_true\n    end\n\n    with_env(\"TERM\": nil, \"NO_COLOR\": \"1\") do\n      Colorize.default_enabled?(io).should be_false\n      Colorize.default_enabled?(tty).should be_false\n      Colorize.default_enabled?(io, io).should be_false\n      Colorize.default_enabled?(io, tty).should be_false\n      Colorize.default_enabled?(tty, io).should be_false\n      Colorize.default_enabled?(tty, tty).should be_false\n    end\n\n    with_env(\"TERM\": \"xterm\", \"NO_COLOR\": nil) do\n      Colorize.default_enabled?(io).should be_false\n      Colorize.default_enabled?(tty).should be_true\n      Colorize.default_enabled?(io, io).should be_false\n      Colorize.default_enabled?(io, tty).should be_false\n      Colorize.default_enabled?(tty, io).should be_false\n      Colorize.default_enabled?(tty, tty).should be_true\n    end\n\n    with_env(\"TERM\": \"dumb\", \"NO_COLOR\": nil) do\n      Colorize.default_enabled?(io).should be_false\n      Colorize.default_enabled?(tty).should be_false\n      Colorize.default_enabled?(io, io).should be_false\n      Colorize.default_enabled?(io, tty).should be_false\n      Colorize.default_enabled?(tty, io).should be_false\n      Colorize.default_enabled?(tty, tty).should be_false\n    end\n  end\n\n  it \"colorizes without change\" do\n    colorize(\"hello\").to_s.should eq(\"hello\")\n  end\n\n  it \"colorizes foreground\" do\n    colorize(\"hello\").black.to_s.should eq(\"\\e[30mhello\\e[39m\")\n    colorize(\"hello\").red.to_s.should eq(\"\\e[31mhello\\e[39m\")\n    colorize(\"hello\").green.to_s.should eq(\"\\e[32mhello\\e[39m\")\n    colorize(\"hello\").yellow.to_s.should eq(\"\\e[33mhello\\e[39m\")\n    colorize(\"hello\").blue.to_s.should eq(\"\\e[34mhello\\e[39m\")\n    colorize(\"hello\").magenta.to_s.should eq(\"\\e[35mhello\\e[39m\")\n    colorize(\"hello\").cyan.to_s.should eq(\"\\e[36mhello\\e[39m\")\n    colorize(\"hello\").light_gray.to_s.should eq(\"\\e[37mhello\\e[39m\")\n    colorize(\"hello\").dark_gray.to_s.should eq(\"\\e[90mhello\\e[39m\")\n    colorize(\"hello\").light_red.to_s.should eq(\"\\e[91mhello\\e[39m\")\n    colorize(\"hello\").light_green.to_s.should eq(\"\\e[92mhello\\e[39m\")\n    colorize(\"hello\").light_yellow.to_s.should eq(\"\\e[93mhello\\e[39m\")\n    colorize(\"hello\").light_blue.to_s.should eq(\"\\e[94mhello\\e[39m\")\n    colorize(\"hello\").light_magenta.to_s.should eq(\"\\e[95mhello\\e[39m\")\n    colorize(\"hello\").light_cyan.to_s.should eq(\"\\e[96mhello\\e[39m\")\n    colorize(\"hello\").white.to_s.should eq(\"\\e[97mhello\\e[39m\")\n  end\n\n  it \"colorizes foreground with 8-bit color\" do\n    colorize(\"hello\").fore(Colorize::Color256.new(123u8)).to_s.should eq(\"\\e[38;5;123mhello\\e[39m\")\n    colorize(\"hello\").fore(123u8).to_s.should eq(\"\\e[38;5;123mhello\\e[39m\")\n    colorize(\"hello\", 123_u8).to_s.should eq(\"\\e[38;5;123mhello\\e[39m\")\n  end\n\n  it \"colorizes foreground with true color\" do\n    colorize(\"hello\").fore(Colorize::ColorRGB.new(12u8, 34u8, 56u8)).to_s.should eq(\"\\e[38;2;12;34;56mhello\\e[39m\")\n    colorize(\"hello\").fore(12u8, 34u8, 56u8).to_s.should eq(\"\\e[38;2;12;34;56mhello\\e[39m\")\n    colorize(\"hello\", 12u8, 34u8, 56u8).to_s.should eq(\"\\e[38;2;12;34;56mhello\\e[39m\")\n  end\n\n  it \"colorizes background\" do\n    colorize(\"hello\").on_black.to_s.should eq(\"\\e[40mhello\\e[49m\")\n    colorize(\"hello\").on_red.to_s.should eq(\"\\e[41mhello\\e[49m\")\n    colorize(\"hello\").on_green.to_s.should eq(\"\\e[42mhello\\e[49m\")\n    colorize(\"hello\").on_yellow.to_s.should eq(\"\\e[43mhello\\e[49m\")\n    colorize(\"hello\").on_blue.to_s.should eq(\"\\e[44mhello\\e[49m\")\n    colorize(\"hello\").on_magenta.to_s.should eq(\"\\e[45mhello\\e[49m\")\n    colorize(\"hello\").on_cyan.to_s.should eq(\"\\e[46mhello\\e[49m\")\n    colorize(\"hello\").on_light_gray.to_s.should eq(\"\\e[47mhello\\e[49m\")\n    colorize(\"hello\").on_dark_gray.to_s.should eq(\"\\e[100mhello\\e[49m\")\n    colorize(\"hello\").on_light_red.to_s.should eq(\"\\e[101mhello\\e[49m\")\n    colorize(\"hello\").on_light_green.to_s.should eq(\"\\e[102mhello\\e[49m\")\n    colorize(\"hello\").on_light_yellow.to_s.should eq(\"\\e[103mhello\\e[49m\")\n    colorize(\"hello\").on_light_blue.to_s.should eq(\"\\e[104mhello\\e[49m\")\n    colorize(\"hello\").on_light_magenta.to_s.should eq(\"\\e[105mhello\\e[49m\")\n    colorize(\"hello\").on_light_cyan.to_s.should eq(\"\\e[106mhello\\e[49m\")\n    colorize(\"hello\").on_white.to_s.should eq(\"\\e[107mhello\\e[49m\")\n  end\n\n  it \"colorizes background with 8-bit color\" do\n    colorize(\"hello\").back(Colorize::Color256.new(123u8)).to_s.should eq(\"\\e[48;5;123mhello\\e[49m\")\n    colorize(\"hello\").back(123u8).to_s.should eq(\"\\e[48;5;123mhello\\e[49m\")\n  end\n\n  it \"colorizes background with true color\" do\n    colorize(\"hello\").back(Colorize::ColorRGB.new(12u8, 34u8, 56u8)).to_s.should eq(\"\\e[48;2;12;34;56mhello\\e[49m\")\n    colorize(\"hello\").back(12u8, 34u8, 56u8).to_s.should eq(\"\\e[48;2;12;34;56mhello\\e[49m\")\n  end\n\n  it \"colorizes mode\" do\n    colorize(\"hello\").bold.to_s.should eq(\"\\e[1mhello\\e[22m\")\n    colorize(\"hello\").bright.to_s.should eq(\"\\e[1mhello\\e[22m\")\n    colorize(\"hello\").dim.to_s.should eq(\"\\e[2mhello\\e[22m\")\n    colorize(\"hello\").italic.to_s.should eq(\"\\e[3mhello\\e[23m\")\n    colorize(\"hello\").underline.to_s.should eq(\"\\e[4mhello\\e[24m\")\n    colorize(\"hello\").blink.to_s.should eq(\"\\e[5mhello\\e[25m\")\n    colorize(\"hello\").blink_fast.to_s.should eq(\"\\e[6mhello\\e[26m\")\n    colorize(\"hello\").reverse.to_s.should eq(\"\\e[7mhello\\e[27m\")\n    colorize(\"hello\").hidden.to_s.should eq(\"\\e[8mhello\\e[28m\")\n    colorize(\"hello\").strikethrough.to_s.should eq(\"\\e[9mhello\\e[29m\")\n    colorize(\"hello\").double_underline.to_s.should eq(\"\\e[21mhello\\e[24m\")\n    colorize(\"hello\").overline.to_s.should eq(\"\\e[53mhello\\e[55m\")\n  end\n\n  it \"prints colorize ANSI escape codes\" do\n    Colorize.with.bold.ansi_escape.should eq(\"\\e[1m\")\n    Colorize.with.bright.ansi_escape.should eq(\"\\e[1m\")\n    Colorize.with.dim.ansi_escape.should eq(\"\\e[2m\")\n    Colorize.with.italic.ansi_escape.should eq(\"\\e[3m\")\n    Colorize.with.underline.ansi_escape.should eq(\"\\e[4m\")\n    Colorize.with.blink.ansi_escape.should eq(\"\\e[5m\")\n    Colorize.with.blink_fast.ansi_escape.should eq(\"\\e[6m\")\n    Colorize.with.reverse.ansi_escape.should eq(\"\\e[7m\")\n    Colorize.with.hidden.ansi_escape.should eq(\"\\e[8m\")\n    Colorize.with.strikethrough.ansi_escape.should eq(\"\\e[9m\")\n    Colorize.with.double_underline.ansi_escape.should eq(\"\\e[21m\")\n    Colorize.with.overline.ansi_escape.should eq(\"\\e[53m\")\n  end\n\n  it \"only prints colorize ANSI escape codes\" do\n    colorize(\"hello\").red.bold.ansi_escape.should eq(\"\\e[31;1m\")\n    colorize(\"hello\").bold.dim.underline.blink.reverse.hidden.ansi_escape.should eq(\"\\e[1;2;4;5;7;8m\")\n  end\n\n  it \"colorizes mode combination\" do\n    colorize(\"hello\").bold.dim.underline.blink.reverse.hidden.to_s.should eq(\"\\e[1;2;4;5;7;8mhello\\e[22;22;24;25;27;28m\")\n  end\n\n  it \"colorizes foreground with background\" do\n    colorize(\"hello\").blue.on_green.to_s.should eq(\"\\e[34;42mhello\\e[39;49m\")\n  end\n\n  it \"colorizes foreground with background with mode\" do\n    colorize(\"hello\").blue.on_green.bold.to_s.should eq(\"\\e[34;42;1mhello\\e[39;49;22m\")\n  end\n\n  it \"colorizes foreground with symbol\" do\n    colorize(\"hello\", :red).to_s.should eq(\"\\e[31mhello\\e[39m\")\n    colorize(\"hello\").fore(:red).to_s.should eq(\"\\e[31mhello\\e[39m\")\n  end\n\n  it \"colorizes mode with symbol\" do\n    colorize(\"hello\").mode(:bold).to_s.should eq(\"\\e[1mhello\\e[22m\")\n  end\n\n  it \"raises on unknown foreground color\" do\n    expect_raises ArgumentError, \"Unknown color: brown\" do\n      colorize(\"hello\", :brown)\n    end\n  end\n\n  it \"raises on unknown background color\" do\n    expect_raises ArgumentError, \"Unknown color: brown\" do\n      colorize(\"hello\").back(:brown)\n    end\n  end\n\n  it \"inspects\" do\n    colorize(\"hello\", :red).inspect.should eq(\"\\e[31m\\\"hello\\\"\\e[39m\")\n  end\n\n  it \"colorizes with surround\" do\n    io = IO::Memory.new\n    with_color_wrap.red.surround(io) do\n      io << \"hello\"\n      with_color_wrap.green.surround(io) do\n        io << \"world\"\n      end\n      io << \"bye\"\n    end\n    io.to_s.should eq(\"\\e[31mhello\\e[39;32mworld\\e[39;31mbye\\e[39m\")\n  end\n\n  it \"colorizes with surround and reset\" do\n    io = IO::Memory.new\n    with_color_wrap.red.surround(io) do\n      io << \"hello\"\n      with_color_wrap.green.bold.surround(io) do\n        io << \"world\"\n      end\n      io << \"bye\"\n    end\n    io.to_s.should eq(\"\\e[31mhello\\e[39;32;1mworld\\e[39;22;31mbye\\e[39m\")\n  end\n\n  it \"colorizes with surround and no reset\" do\n    io = IO::Memory.new\n    with_color_wrap.red.surround(io) do\n      io << \"hello\"\n      with_color_wrap.red.surround(io) do\n        io << \"world\"\n      end\n      io << \"bye\"\n    end\n    io.to_s.should eq(\"\\e[31mhelloworldbye\\e[39m\")\n  end\n\n  it \"colorizes with surround and default\" do\n    io = IO::Memory.new\n    with_color_wrap.red.surround(io) do\n      io << \"hello\"\n      with_color_wrap.surround(io) do\n        io << \"world\"\n      end\n      io << \"bye\"\n    end\n    io.to_s.should eq(\"\\e[31mhello\\e[39mworld\\e[31mbye\\e[39m\")\n  end\n\n  it \"colorizes with to_s\" do\n    colorize(ColorizeToS.new).red.to_s.should eq(\"\\e[31mhello\\e[39;34mworld\\e[39;31mbye\\e[39m\")\n  end\n\n  it \"toggles off\" do\n    colorize(\"hello\").black.toggle(false).to_s.should eq(\"hello\")\n    colorize(\"hello\").toggle(false).black.to_s.should eq(\"hello\")\n  end\n\n  it \"toggles off and on\" do\n    colorize(\"hello\").toggle(false).black.toggle(true).to_s.should eq(\"\\e[30mhello\\e[39m\")\n  end\n\n  it \"colorizes nested strings\" do\n    colorize(\"hello #{colorize(\"foo\").red} bar\").underline.to_s.should eq(\"\\e[4mhello \\e[31mfoo\\e[39m bar\\e[24m\")\n\n    # TODO: Ideally this should work\n    # colorize(\"hello #{colorize(\"foo\").red} bar\").green.to_s.should eq \"\\e[32mhello \\e[39m\\e[31mfoo\\e[39m\\e[32m bar\\e[39m\"\n  end\nend\n"
  },
  {
    "path": "spec/std/comparable_spec.cr",
    "content": "require \"spec\"\n\nprivate class ComparableTestClass\n  include Comparable(Int)\n\n  def initialize(@value : Int32, @return_nil = false)\n  end\n\n  def <=>(other : Int)\n    return nil if @return_nil\n\n    @value <=> other\n  end\nend\n\ndescribe Comparable do\n  it \"can compare against Int (#2461)\" do\n    obj = ComparableTestClass.new(4)\n    (obj == 3).should be_false\n    (obj == 4).should be_true\n\n    (obj < 3).should be_false\n    (obj < 4).should be_false\n\n    (obj > 3).should be_true\n    (obj > 4).should be_false\n\n    (obj <= 3).should be_false\n    (obj <= 4).should be_true\n    (obj <= 5).should be_true\n\n    (obj >= 3).should be_true\n    (obj >= 4).should be_true\n    (obj >= 5).should be_false\n  end\n\n  it \"checks for nil\" do\n    obj = ComparableTestClass.new(4, return_nil: true)\n\n    (obj < 1).should be_false\n    (obj <= 1).should be_false\n    (obj == 1).should be_false\n    (obj >= 1).should be_false\n    (obj > 1).should be_false\n  end\n\n  describe \"clamp\" do\n    describe \"number\" do\n      it \"clamps integers\" do\n        -5.clamp(-10, 100).should eq(-5)\n        -5.clamp(10, 100).should eq(10)\n        5.clamp(10, 100).should eq(10)\n        50.clamp(10, 100).should eq(50)\n        500.clamp(10, 100).should eq(100)\n\n        50.clamp(10..100).should eq(50)\n\n        50.clamp(10..nil).should eq(50)\n        50.clamp(10...nil).should eq(50)\n        5.clamp(10..nil).should eq(10)\n        5.clamp(10...nil).should eq(10)\n\n        5.clamp(nil..10).should eq(5)\n        50.clamp(nil..10).should eq(10)\n      end\n\n      it \"clamps floats\" do\n        -5.5.clamp(-10.1, 100.1).should eq(-5.5)\n        -5.5.clamp(10.1, 100.1).should eq(10.1)\n        5.5.clamp(10.1, 100.1).should eq(10.1)\n        50.5.clamp(10.1, 100.1).should eq(50.5)\n        500.5.clamp(10.1, 100.1).should eq(100.1)\n\n        50.5.clamp(10.1..100.1).should eq(50.5)\n      end\n\n      it \"fails with an exclusive range\" do\n        expect_raises(ArgumentError) do\n          range = Range.new(1, 2, exclusive: true)\n          5.clamp(range)\n        end\n      end\n    end\n\n    describe \"String\" do\n      it \"clamps strings\" do\n        \"e\".clamp(\"a\", \"s\").should eq \"e\"\n        \"e\".clamp(\"f\", \"s\").should eq \"f\"\n        \"e\".clamp(\"a\", \"c\").should eq \"c\"\n        \"this\".clamp(\"thief\", \"thin\").should eq \"thin\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/complex_spec.cr",
    "content": "require \"spec\"\nrequire \"complex\"\nrequire \"../support/number\"\n{% unless flag?(:wasm32) %}\n  require \"big\"\n{% end %}\n\n# exact equality, including component signs\nprivate def assert_complex_eq(z1 : Complex, z2 : Complex, *, file = __FILE__, line = __LINE__)\n  z1.should eq(z2), file: file, line: line\n  z1.real.sign_bit.should eq(z2.real.sign_bit), file: file, line: line\n  z1.imag.sign_bit.should eq(z2.imag.sign_bit), file: file, line: line\nend\n\nprivate def assert_complex_nan(z : Complex, *, file = __FILE__, line = __LINE__)\n  z.real.nan?.should be_true, file: file, line: line\n  z.imag.nan?.should be_true, file: file, line: line\nend\n\ndescribe \"Complex\" do\n  describe \"as numbers\" do\n    it_can_convert_between([Complex], [Complex])\n    it_can_convert_between({{BUILTIN_NUMBER_TYPES}}, [Complex])\n    it_can_convert_between([Complex], {{BUILTIN_NUMBER_TYPES}})\n\n    division_between_returns {{BUILTIN_NUMBER_TYPES}}, [Complex], Complex\n    division_between_returns [Complex], {{BUILTIN_NUMBER_TYPES}}, Complex\n\n    division_between_returns [Complex], [Complex], Complex\n  end\n\n  it \"i\" do\n    a = 4.5 + 6.7.i\n    b = Complex.new(4.5, 6.7)\n    c = Complex.new(4.5, 9.6)\n    a.should eq(b)\n    a.should_not eq(c)\n  end\n\n  describe \"==\" do\n    it \"complex == complex\" do\n      a = Complex.new(1.5, 2)\n      b = Complex.new(1.5, 2)\n      c = Complex.new(2.25, 3)\n      (a == b).should be_true\n      (a == c).should be_false\n    end\n\n    it \"complex == number\" do\n      a = Complex.new(5.3, 0)\n      b = 5.3\n      c = 4.2\n      (a == b).should be_true\n      (a == c).should be_false\n\n      {% unless flag?(:wasm32) %}\n        (a == BigDecimal.new(53, 1)).should be_false\n      {% end %}\n    end\n\n    it \"number == complex\" do\n      a = -1.75\n      b = Complex.new(-1.75, 0)\n      c = Complex.new(7.2, 0)\n      (a == b).should be_true\n      (a == c).should be_false\n\n      {% unless flag?(:wasm32) %}\n        (BigDecimal.new(72, 1) == c).should be_false\n      {% end %}\n    end\n  end\n\n  it \"to_s\" do\n    Complex.new(1.25, 8.2).to_s.should eq(\"1.25 + 8.2i\")\n    Complex.new(1.25, -8.2).to_s.should eq(\"1.25 - 8.2i\")\n\n    Complex.new(+0.0, +0.0).to_s.should eq(\"0.0 + 0.0i\")\n    Complex.new(-0.0, -0.0).to_s.should eq(\"-0.0 - 0.0i\")\n\n    Complex.new(+Float64::INFINITY, +Float64::INFINITY).to_s.should eq(\"Infinity + Infinityi\")\n    Complex.new(-Float64::INFINITY, -Float64::INFINITY).to_s.should eq(\"-Infinity - Infinityi\")\n\n    pos_nan = Math.copysign(Float64::NAN, 1)\n    neg_nan = Math.copysign(Float64::NAN, -1)\n    Complex.new(pos_nan, pos_nan).to_s.should eq(\"NaN + NaNi\")\n    Complex.new(neg_nan, neg_nan).to_s.should eq(\"NaN + NaNi\")\n  end\n\n  it \"inspect\" do\n    Complex.new(1.25, 8.2).inspect.should eq(\"(1.25 + 8.2i)\")\n    Complex.new(1.25, -8.2).inspect.should eq(\"(1.25 - 8.2i)\")\n\n    Complex.new(+0.0, +0.0).inspect.should eq(\"(0.0 + 0.0i)\")\n    Complex.new(-0.0, -0.0).inspect.should eq(\"(-0.0 - 0.0i)\")\n\n    Complex.new(+Float64::INFINITY, +Float64::INFINITY).inspect.should eq(\"(Infinity + Infinityi)\")\n    Complex.new(-Float64::INFINITY, -Float64::INFINITY).inspect.should eq(\"(-Infinity - Infinityi)\")\n\n    pos_nan = Math.copysign(Float64::NAN, 1)\n    neg_nan = Math.copysign(Float64::NAN, -1)\n    Complex.new(pos_nan, pos_nan).inspect.should eq(\"(NaN + NaNi)\")\n    Complex.new(neg_nan, neg_nan).inspect.should eq(\"(NaN + NaNi)\")\n  end\n\n  it \"abs\" do\n    Complex.new(5.1, 9.7).abs.should eq(10.959014554237985)\n  end\n\n  it \"abs2\" do\n    Complex.new(-1.1, 9).abs2.should eq(82.21)\n  end\n\n  describe \"sign\" do\n    it \"finite, non-zero\" do\n      Complex.new(-1.4, 7.7).sign.should be_close(Complex.new(-0.17888543819998315, 0.9838699100999074), 1e-14)\n      Complex.new(1.4, -7.7).sign.should be_close(Complex.new(0.17888543819998315, -0.9838699100999074), 1e-14)\n    end\n\n    it \"complex zero\" do\n      assert_complex_eq Complex.new(+0.0, +0.0).sign, Complex.new(+0.0, +0.0)\n      assert_complex_eq Complex.new(+0.0, -0.0).sign, Complex.new(+0.0, -0.0)\n      assert_complex_eq Complex.new(-0.0, +0.0).sign, Complex.new(-0.0, +0.0)\n      assert_complex_eq Complex.new(-0.0, -0.0).sign, Complex.new(-0.0, -0.0)\n    end\n\n    it \"real zero\" do\n      assert_complex_eq Complex.new(+0.0, +2.0).sign, Complex.new(+0.0, +1.0)\n      assert_complex_eq Complex.new(+0.0, -2.0).sign, Complex.new(+0.0, -1.0)\n      assert_complex_eq Complex.new(-0.0, +2.0).sign, Complex.new(-0.0, +1.0)\n      assert_complex_eq Complex.new(-0.0, -2.0).sign, Complex.new(-0.0, -1.0)\n    end\n\n    it \"imaginary zero\" do\n      assert_complex_eq Complex.new(+2.0, +0.0).sign, Complex.new(+1.0, +0.0)\n      assert_complex_eq Complex.new(+2.0, -0.0).sign, Complex.new(+1.0, -0.0)\n      assert_complex_eq Complex.new(-2.0, +0.0).sign, Complex.new(-1.0, +0.0)\n      assert_complex_eq Complex.new(-2.0, -0.0).sign, Complex.new(-1.0, -0.0)\n    end\n\n    it \"infinity\" do\n      inf = Float64::INFINITY\n\n      # 1st quadrant\n      assert_complex_eq Complex.new(+inf, +0.0).sign, Complex.new(+1.0, +0.0)\n      assert_complex_eq Complex.new(+inf, +1.0).sign, Complex.new(+1.0, +0.0)\n      assert_complex_eq Complex.new(+1.0, +inf).sign, Complex.new(+0.0, +1.0)\n      assert_complex_eq Complex.new(+0.0, +inf).sign, Complex.new(+0.0, +1.0)\n\n      # 2nd quadrant\n      assert_complex_eq Complex.new(-0.0, +inf).sign, Complex.new(-0.0, +1.0)\n      assert_complex_eq Complex.new(-1.0, +inf).sign, Complex.new(-0.0, +1.0)\n      assert_complex_eq Complex.new(-inf, +1.0).sign, Complex.new(-1.0, +0.0)\n      assert_complex_eq Complex.new(-inf, +0.0).sign, Complex.new(-1.0, +0.0)\n\n      # 3rd quadrant\n      assert_complex_eq Complex.new(-inf, -0.0).sign, Complex.new(-1.0, -0.0)\n      assert_complex_eq Complex.new(-inf, -1.0).sign, Complex.new(-1.0, -0.0)\n      assert_complex_eq Complex.new(-1.0, -inf).sign, Complex.new(-0.0, -1.0)\n      assert_complex_eq Complex.new(-0.0, -inf).sign, Complex.new(-0.0, -1.0)\n\n      # 4th quadrant\n      assert_complex_eq Complex.new(+0.0, -inf).sign, Complex.new(+0.0, -1.0)\n      assert_complex_eq Complex.new(+1.0, -inf).sign, Complex.new(+0.0, -1.0)\n      assert_complex_eq Complex.new(+inf, -1.0).sign, Complex.new(+1.0, -0.0)\n      assert_complex_eq Complex.new(+inf, -0.0).sign, Complex.new(+1.0, -0.0)\n\n      # diagonals\n      sqr = Math.sqrt(0.5)\n      Complex.new(+inf, +inf).sign.should be_close(Complex.new(+sqr, +sqr), 1e-14)\n      Complex.new(-inf, +inf).sign.should be_close(Complex.new(-sqr, +sqr), 1e-14)\n      Complex.new(-inf, -inf).sign.should be_close(Complex.new(-sqr, -sqr), 1e-14)\n      Complex.new(+inf, -inf).sign.should be_close(Complex.new(+sqr, -sqr), 1e-14)\n    end\n\n    it \"not-a-number\" do\n      assert_complex_nan Complex.new(Float64::NAN, +0.0).sign\n      assert_complex_nan Complex.new(Float64::NAN, +1.0).sign\n      assert_complex_nan Complex.new(Float64::NAN, Float64::INFINITY).sign\n      assert_complex_nan Complex.new(-0.0, Float64::NAN).sign\n      assert_complex_nan Complex.new(-1.0, Float64::NAN).sign\n      assert_complex_nan Complex.new(-Float64::INFINITY, Float64::NAN).sign\n      assert_complex_nan Complex.new(Float64::NAN, Float64::NAN).sign\n      assert_complex_nan Complex.new(Float64::NAN, Float64::NAN).sign\n      assert_complex_nan Complex.new(Float64::NAN, Float64::NAN).sign\n      assert_complex_nan Complex.new(Float64::NAN, Float64::NAN).sign\n    end\n  end\n\n  it \"phase\" do\n    Complex.new(11.5, -6.25).phase.should eq(-0.4978223326170012)\n  end\n\n  it \"polar\" do\n    Complex.new(7.25, -13.1).polar.should eq({14.972391258579906, -1.0653196179316864})\n  end\n\n  it \"cis\" do\n    {% if flag?(:aarch64) && flag?(:darwin) %}\n      2.4.cis.should eq(Complex.new(-0.7373937155412454, 0.6754631805511511))\n    {% else %}\n      2.4.cis.should eq(Complex.new(-0.7373937155412454, 0.675463180551151))\n    {% end %}\n  end\n\n  it \"conj\" do\n    Complex.new(10.1, 3.7).conj.should eq(Complex.new(10.1, -3.7))\n  end\n\n  it \"inv\" do\n    Complex.new(1.5, -2.5).inv.should eq(Complex.new(0.17647058823529413, 0.29411764705882354))\n  end\n\n  describe \"+\" do\n    it \"+ complex\" do\n      (+Complex.new(-5.43, -27.12)).should eq(Complex.new(-5.43, -27.12))\n    end\n\n    it \"complex + complex\" do\n      (Complex.new(2.2, 7) + Complex.new(10.1, 1.34)).should eq(Complex.new(12.3, 8.34))\n    end\n\n    it \"complex + number\" do\n      (Complex.new(0.3, 5.5) + 15).should eq(Complex.new(15.3, 5.5))\n    end\n\n    it \"number + complex\" do\n      (-1.7 + Complex.new(7, 4.1)).should eq(Complex.new(5.3, 4.1))\n    end\n  end\n\n  describe \"-\" do\n    it \"- complex\" do\n      (-Complex.new(5.43, 27.12)).should eq(Complex.new(-5.43, -27.12))\n    end\n\n    it \"complex - complex\" do\n      (Complex.new(21.7, 2.0) - Complex.new(0.15, 3.4)).should eq(Complex.new(21.55, -1.4))\n    end\n\n    it \"complex - number\" do\n      (Complex.new(8.1, 6.15) - 15).should eq(Complex.new(-6.9, 6.15))\n    end\n\n    it \"number - complex\" do\n      (-3.27 - Complex.new(7, 5.1)).should eq(Complex.new(-10.27, -5.1))\n    end\n  end\n\n  describe \"*\" do\n    it \"complex * complex\" do\n      (Complex.new(12.2, 9.8)*Complex.new(4.78, 2.86)).should eq(Complex.new(30.288, 81.736))\n    end\n\n    it \"complex * number\" do\n      (Complex.new(11.3, 15.25)*1.2).should eq(Complex.new(13.56, 18.3))\n    end\n\n    it \"number * complex\" do\n      (-1.7*Complex.new(9.7, 3.22)).should eq(Complex.new(-16.49, -5.474))\n    end\n  end\n\n  describe \"/\" do\n    it \"complex / complex\" do\n      ((Complex.new(4, 6.2))/(Complex.new(0.5, 2.7))).should eq(Complex.new(2.485411140583554, -1.0212201591511936))\n      ((Complex.new(4.1, 6.0))/(Complex.new(10, 2.2))).should eq(Complex.new(0.5169782525753529, 0.48626478443342236))\n\n      (1.to_c / -1.to_c).should eq(-1.to_c)\n      assert_complex_nan 1.to_c / Float64::NAN\n\n      (1.to_c / 0.to_c).real.abs.should eq(Float64::INFINITY)\n      (1.to_c / 0.to_c).imag.nan?.should be_true\n    end\n\n    it \"complex / number\" do\n      ((Complex.new(21.3, 5.8))/1.9).should eq(Complex.new(11.210526315789474, 3.0526315789473686))\n    end\n\n    it \"number / complex\" do\n      (-5.7/(Complex.new(2.27, 8.92))).should eq(Complex.new(-0.1527278908111847, 0.6001466017778712))\n    end\n  end\n\n  it \"clones\" do\n    c = Complex.new(4, 6.2)\n    c.clone.should eq(c)\n  end\n\n  it \"hashes real without imag like real only\" do\n    c = Complex.new(4, 0)\n    c.hash.should eq(4_f64.hash)\n  end\n\n  it \"test zero\" do\n    Complex.zero.should eq(Complex.new(0, 0))\n  end\n\n  it \"test zero?\" do\n    Complex.new(0, 0).zero?.should be_true\n    Complex.new(0, 3.4).zero?.should be_false\n    Complex.new(1.2, 0).zero?.should be_false\n    Complex.new(1.2, 3.4).zero?.should be_false\n  end\n\n  it \"test additive_identity\" do\n    Complex.additive_identity.should eq(Complex.new(0, 0))\n  end\n\n  it \"test multiplicative_identity\" do\n    Complex.multiplicative_identity.should eq(Complex.new(1, 0))\n  end\n\n  it \"rounds\" do\n    (Complex.new(1.125, 0.875).round(2)).should eq(Complex.new(1.12, 0.88))\n    (Complex.new(1.135, 0.865).round(2)).should eq(Complex.new(1.14, 0.86))\n    (Complex.new(1.125, 0.875).round(digits: 1)).should eq(Complex.new(1.1, 0.9))\n  end\n\n  describe \"Math\" do\n    it \"exp\" do\n      Math.exp(Complex.new(1.15, -5.1)).should be_close(Complex.new(1.1937266270566773, 2.923901365414129), 1e-15)\n    end\n\n    it \"log\" do\n      Math.log(Complex.new(1.25, -4.7)).should eq(Complex.new(1.5817344087982312, -1.3108561866063686))\n    end\n\n    it \"log2\" do\n      Math.log2(Complex.new(-9.1, 3.2)).should eq(Complex.new(3.2699671225858946, +4.044523592551345))\n    end\n\n    it \"log10\" do\n      Math.log10(Complex.new(2.11, 1.21)).should eq(Complex.new(0.38602142355392594, +0.22612668967405536))\n    end\n\n    it \"sqrt\" do\n      Math.sqrt(Complex.new(1.32, 7.25)).should be_close(Complex.new(2.0843687106374236, 1.739135682425128), 1e-15)\n      Math.sqrt(Complex.new(7.11, -0.9)).should be_close(Complex.new(2.671772413453534, -0.1684275194002508), 1e-15)\n      Math.sqrt(Complex.new(-2.2, 6.22)).should be_close(Complex.new(1.4828360708935342, 2.0973323087062226), 1e-15)\n      Math.sqrt(Complex.new(-8.3, -1.11)).should be_close(Complex.new(0.1922159681400434, -2.8873771797962275), 1e-15)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/compress/deflate/deflate_spec.cr",
    "content": "require \"spec\"\nrequire \"compress/deflate\"\n\nprivate def new_sample_io\n  io = IO::Memory.new\n  \"cbc9cc4b350402ae1c20c30808b800\".scan(/../).each do |match|\n    io.write_byte match[0].to_u8(16)\n  end\n  io.rewind\nend\n\nmodule Compress::Deflate\n  describe Reader do\n    it \"should read byte by byte (#4192)\" do\n      io = new_sample_io\n\n      reader = Reader.new(io)\n\n      str = String::Builder.build do |builder|\n        while b = reader.read_byte\n          builder.write_byte b\n        end\n      end\n\n      str.should eq(\"line1111\\nline2222\\n\")\n    end\n\n    it \"should rewind\" do\n      io = new_sample_io\n\n      reader = Reader.new(io)\n      reader.gets.should eq(\"line1111\")\n      reader.rewind\n      reader.gets_to_end.should eq(\"line1111\\nline2222\\n\")\n    end\n\n    describe \".open\" do\n      it \"yields itself to block\" do\n        # Hello Crystal!\n        message = Bytes[243, 72, 205, 201, 201, 87, 112, 46, 170, 44, 46, 73,\n          204, 81, 4, 0]\n\n        io = IO::Memory.new(message)\n        Reader.open(io) do |reader|\n          reader.gets_to_end.should eq(\"Hello Crystal!\")\n        end\n      end\n    end\n  end\n\n  describe Writer do\n    it \"should be able to write\" do\n      message = \"this is a test string !!!!\\n\"\n      io = IO::Memory.new\n      writer = Writer.new(io)\n      writer.print message\n      writer.close\n\n      io.rewind\n      reader = Reader.new(io)\n      reader.gets_to_end.should eq(message)\n    end\n\n    it \"can be closed without sync\" do\n      io = IO::Memory.new\n      writer = Writer.new(io)\n      writer.close\n      writer.closed?.should be_true\n      io.closed?.should be_false\n\n      expect_raises IO::Error, \"Closed stream\" do\n        writer.print \"a\"\n      end\n    end\n\n    it \"can be closed with sync (1)\" do\n      io = IO::Memory.new\n      writer = Writer.new(io, sync_close: true)\n      writer.close\n      writer.closed?.should be_true\n      io.closed?.should be_true\n    end\n\n    it \"can be closed with sync (2)\" do\n      io = IO::Memory.new\n      writer = Writer.new(io)\n      writer.sync_close = true\n      writer.close\n      writer.closed?.should be_true\n      io.closed?.should be_true\n    end\n\n    describe \".open\" do\n      it \"yields itself to block\" do\n        io = IO::Memory.new\n        Writer.open(io) do |writer|\n          writer.write \"Hello Crystal!\".to_slice\n        end\n\n        io.rewind\n        io.to_slice.should eq(Bytes[243, 72, 205, 201, 201, 87, 112, 46, 170, 44, 46, 73,\n          204, 81, 4, 0])\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/compress/gzip/gzip_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"compress/gzip\"\n\nprivate SAMPLE_TIME     = Time.utc(2016, 1, 2)\nprivate SAMPLE_OS       = 4_u8\nprivate SAMPLE_EXTRA    = Bytes[1, 2, 3]\nprivate SAMPLE_NAME     = \"foo.txt\"\nprivate SAMPLE_COMMENT  = \"some comment\"\nprivate SAMPLE_CONTENTS = \"hello world\\nfoo bar\"\n\nprivate def new_sample_io\n  io = IO::Memory.new\n\n  Compress::Gzip::Writer.open(io) do |gzip|\n    header = gzip.header\n    header.modification_time = SAMPLE_TIME\n    header.os = SAMPLE_OS\n    header.extra = SAMPLE_EXTRA\n    header.name = SAMPLE_NAME\n    header.comment = SAMPLE_COMMENT\n\n    io.bytesize.should eq(0)\n    gzip.flush\n    io.bytesize.should_not eq(0)\n\n    gzip.print SAMPLE_CONTENTS\n  end\n\n  io.rewind\nend\n\ndescribe Compress::Gzip do\n  it \"writes and reads to memory\" do\n    io = new_sample_io\n\n    Compress::Gzip::Reader.open(io) do |gzip|\n      header = gzip.header.not_nil!\n      header.modification_time.should eq(SAMPLE_TIME)\n      header.os.should eq(SAMPLE_OS)\n      header.extra.should eq(SAMPLE_EXTRA)\n      header.name.should eq(SAMPLE_NAME)\n      header.comment.should eq(SAMPLE_COMMENT)\n\n      # Reading zero bytes is OK\n      gzip.read(Bytes.empty).should eq(0)\n\n      gzip.gets_to_end.should eq(SAMPLE_CONTENTS)\n    end\n  end\n\n  it \"rewinds\" do\n    io = new_sample_io\n\n    gzip = Compress::Gzip::Reader.new(io)\n    gzip.gets.should eq(SAMPLE_CONTENTS.lines.first)\n\n    gzip.rewind\n    gzip.gets_to_end.should eq(SAMPLE_CONTENTS)\n  end\n\n  it \"reads file with extra fields from file system\" do\n    File.open(datapath(\"test.gz\")) do |file|\n      Compress::Gzip::Reader.open(file) do |gzip|\n        header = gzip.header.not_nil!\n        header.modification_time.to_utc.should eq(Time.utc(2012, 9, 4, 22, 6, 5))\n        header.os.should eq(3_u8)\n        header.extra.should eq(Bytes[1, 2, 3, 4, 5])\n        header.name.should eq(\"test.txt\")\n        header.comment.should eq(\"happy birthday\")\n        gzip.gets_to_end.should eq(\"One\\nTwo\")\n      end\n    end\n  end\n\n  it \"writes and reads file with extra fields\" do\n    io = IO::Memory.new\n    Compress::Gzip::Writer.open(io) do |gzip|\n      header = gzip.header\n      header.modification_time = Time.utc(2012, 9, 4, 22, 6, 5)\n      header.os = 3_u8\n      header.extra = Bytes[1, 2, 3, 4, 5]\n      header.name = \"test.txt\"\n      header.comment = \"happy birthday\"\n      gzip << \"One\\nTwo\"\n    end\n    io.rewind\n    Compress::Gzip::Reader.open(io) do |gzip|\n      header = gzip.header.not_nil!\n      header.modification_time.to_utc.should eq(Time.utc(2012, 9, 4, 22, 6, 5))\n      header.os.should eq(3_u8)\n      header.extra.should eq(Bytes[1, 2, 3, 4, 5])\n      header.name.should eq(\"test.txt\")\n      header.comment.should eq(\"happy birthday\")\n      gzip.gets_to_end.should eq(\"One\\nTwo\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/compress/zip/zip_file_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"compress/zip\"\n\ndescribe Compress::Zip do\n  it \"reads file from memory\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add \"foo.txt\", \"contents of foo\"\n      zip.add \"bar.txt\", \"contents of bar\"\n    end\n\n    io.rewind\n\n    Compress::Zip::File.open(io) do |zip|\n      entries = zip.entries\n      entries.size.should eq(2)\n\n      foo = entries[0]\n      foo.filename.should eq(\"foo.txt\")\n\n      bar = entries[1]\n      bar.filename.should eq(\"bar.txt\")\n\n      zip[\"foo.txt\"].filename.should eq(\"foo.txt\")\n      zip[\"bar.txt\"].filename.should eq(\"bar.txt\")\n      zip[\"baz.txt\"]?.should be_nil\n\n      foo.open do |foo_io|\n        bar.open do |bar_io|\n          foo_io.gets_to_end.should eq(\"contents of foo\")\n          bar_io.gets_to_end.should eq(\"contents of bar\")\n        end\n      end\n    end\n  end\n\n  it \"reads file from file system\" do\n    filename = datapath(\"file.zip\")\n\n    begin\n      File.open(filename, \"w\") do |file|\n        Compress::Zip::Writer.open(file) do |zip|\n          zip.add \"foo.txt\", \"contents of foo\"\n          zip.add \"bar.txt\", \"contents of bar\"\n        end\n      end\n\n      File.open(filename, \"r\") do |file|\n        Compress::Zip::File.open(file) do |zip|\n          entries = zip.entries\n          entries.size.should eq(2)\n\n          foo = entries[0]\n          foo.filename.should eq(\"foo.txt\")\n\n          bar = entries[1]\n          bar.filename.should eq(\"bar.txt\")\n\n          zip[\"foo.txt\"].filename.should eq(\"foo.txt\")\n          zip[\"bar.txt\"].filename.should eq(\"bar.txt\")\n          zip[\"baz.txt\"]?.should be_nil\n\n          foo.open do |foo_io|\n            bar.open do |bar_io|\n              foo_io.gets_to_end.should eq(\"contents of foo\")\n              bar_io.gets_to_end.should eq(\"contents of bar\")\n            end\n          end\n        end\n      end\n    ensure\n      File.delete(filename)\n    end\n  end\n\n  it \"writes comment\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add Compress::Zip::Writer::Entry.new(\"foo.txt\", comment: \"some comment\"),\n        \"contents of foo\"\n    end\n\n    io.rewind\n\n    Compress::Zip::File.open(io) do |zip|\n      zip[\"foo.txt\"].comment.should eq(\"some comment\")\n    end\n  end\n\n  it \"reads big file\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      100.times do |i|\n        zip.add \"foo#{i}.txt\", \"some contents #{i}\"\n      end\n    end\n\n    io.rewind\n\n    Compress::Zip::File.open(io) do |zip|\n      zip.entries.size.should eq(100)\n    end\n  end\n\n  it \"reads zip file with different extra in local file header and central directory header\" do\n    Compress::Zip::File.open(datapath(\"test.zip\")) do |zip|\n      zip.entries.size.should eq(2)\n      zip[\"one.txt\"].open(&.gets_to_end).should eq(\"One\")\n      zip[\"two.txt\"].open(&.gets_to_end).should eq(\"Two\")\n    end\n  end\n\n  it \"reads zip comment\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.comment = \"zip comment\"\n    end\n\n    io.rewind\n\n    Compress::Zip::File.open(io) do |zip|\n      zip.comment.should eq(\"zip comment\")\n    end\n  end\n\n  it \"writes over int16 files to make sure we can parse\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      0_u16.upto(UInt16::MAX - 1).each do |index|\n        zip.add Compress::Zip::Writer::Entry.new(\"foo_#{index}.txt\", comment: \"some comment\"),\n          \"contents of foo\"\n      end\n    end\n\n    io.rewind\n\n    Compress::Zip::File.open(io) do |zip|\n      zip.entries.size.should eq(UInt16::MAX)\n    end\n  end\n\n  typeof(Compress::Zip::File.new(\"file.zip\"))\n  typeof(Compress::Zip::File.open(\"file.zip\") { })\nend\n"
  },
  {
    "path": "spec/std/compress/zip/zip_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"compress/zip\"\n\ndescribe Compress::Zip do\n  it \"writes and reads to memory\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add \"foo.txt\", &.print(\"contents of foo\")\n      zip.add \"bar.txt\", &.print(\"contents of bar\")\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.file?.should be_true\n      entry.dir?.should be_false\n      entry.filename.should eq(\"foo.txt\")\n      entry.compression_method.should eq(Compress::Zip::CompressionMethod::DEFLATED)\n      entry.crc32.should eq(0)\n      entry.compressed_size.should eq(0)\n      entry.uncompressed_size.should eq(0)\n      entry.extra.should be_empty\n      entry.io.gets_to_end.should eq(\"contents of foo\")\n\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"bar.txt\")\n      entry.io.gets_to_end.should eq(\"contents of bar\")\n\n      zip.next_entry.should be_nil\n    end\n  end\n\n  it \"writes entry\" do\n    io = IO::Memory.new\n\n    time = Time.utc(2017, 1, 14, 2, 3, 4)\n    extra = Bytes[1, 2, 3, 4]\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add(Compress::Zip::Writer::Entry.new(\"foo.txt\", time: time, extra: extra)) do |io|\n        io.print(\"contents of foo\")\n      end\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"foo.txt\")\n      entry.time.should eq(time)\n      entry.extra.should eq(extra)\n      entry.io.gets_to_end.should eq(\"contents of foo\")\n    end\n  end\n\n  it \"writes entry uncompressed\" do\n    io = IO::Memory.new\n\n    text = \"contents of foo\"\n    crc32 = Digest::CRC32.checksum(text)\n\n    Compress::Zip::Writer.open(io) do |zip|\n      entry = Compress::Zip::Writer::Entry.new(\"foo.txt\")\n      entry.compression_method = Compress::Zip::CompressionMethod::STORED\n      entry.crc32 = crc32\n      entry.compressed_size = text.bytesize.to_u32\n      entry.uncompressed_size = text.bytesize.to_u32\n      zip.add entry, &.print(text)\n\n      entry = Compress::Zip::Writer::Entry.new(\"bar.txt\")\n      entry.compression_method = Compress::Zip::CompressionMethod::STORED\n      entry.crc32 = crc32\n      entry.compressed_size = text.bytesize.to_u32\n      entry.uncompressed_size = text.bytesize.to_u32\n      zip.add entry, &.print(text)\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"foo.txt\")\n      entry.compression_method.should eq(Compress::Zip::CompressionMethod::STORED)\n      entry.crc32.should eq(crc32)\n      entry.compressed_size.should eq(text.bytesize)\n      entry.uncompressed_size.should eq(text.bytesize)\n      entry.io.gets_to_end.should eq(text)\n\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"bar.txt\")\n      entry.io.gets_to_end.should eq(text)\n    end\n  end\n\n  it \"writes entry uncompressed and reads with Compress::Zip::File\" do\n    io = IO::Memory.new\n\n    text = \"contents of foo\"\n    crc32 = Digest::CRC32.checksum(text)\n\n    Compress::Zip::Writer.open(io) do |zip|\n      entry = Compress::Zip::Writer::Entry.new(\"foo.txt\")\n      entry.compression_method = Compress::Zip::CompressionMethod::STORED\n      entry.crc32 = crc32\n      entry.compressed_size = text.bytesize.to_u32\n      entry.uncompressed_size = text.bytesize.to_u32\n      zip.add entry, &.print(text)\n    end\n\n    io.rewind\n\n    Compress::Zip::File.open(io) do |zip|\n      zip.entries.size.should eq(1)\n      entry = zip.entries.first\n      entry.filename.should eq(\"foo.txt\")\n      entry.open(&.gets_to_end).should eq(text)\n    end\n  end\n\n  it \"adds a directory\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add_dir \"one\"\n      zip.add_dir \"two/\"\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"one/\")\n      entry.file?.should be_false\n      entry.dir?.should be_true\n      entry.io.gets_to_end.should eq(\"\")\n\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"two/\")\n      entry.dir?.should be_true\n      entry.io.gets_to_end.should eq(\"\")\n    end\n  end\n\n  it \"writes string\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add \"foo.txt\", \"contents of foo\"\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"foo.txt\")\n      entry.io.gets_to_end.should eq(\"contents of foo\")\n    end\n  end\n\n  it \"writes bytes\" do\n    io = IO::Memory.new\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add \"foo.txt\", \"contents of foo\".to_slice\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"foo.txt\")\n      entry.io.gets_to_end.should eq(\"contents of foo\")\n    end\n  end\n\n  it \"writes io\" do\n    io = IO::Memory.new\n    data = IO::Memory.new(\"contents of foo\")\n\n    Compress::Zip::Writer.open(io) do |zip|\n      zip.add \"foo.txt\", data\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"foo.txt\")\n      entry.io.gets_to_end.should eq(\"contents of foo\")\n    end\n  end\n\n  it \"writes file\" do\n    io = IO::Memory.new\n    filename = datapath(\"test_file.txt\")\n\n    Compress::Zip::Writer.open(io) do |zip|\n      file = File.open(filename)\n      zip.add \"foo.txt\", file\n      file.closed?.should be_true\n    end\n\n    io.rewind\n\n    Compress::Zip::Reader.open(io) do |zip|\n      entry = zip.next_entry.not_nil!\n      entry.filename.should eq(\"foo.txt\")\n      entry.io.gets_to_end.should eq(File.read(filename))\n    end\n  end\n\n  typeof(Compress::Zip::Reader.new(\"file.zip\"))\n  typeof(Compress::Zip::Reader.open(\"file.zip\") { })\n\n  typeof(Compress::Zip::Writer.new(\"file.zip\"))\n  typeof(Compress::Zip::Writer.open(\"file.zip\") { })\nend\n"
  },
  {
    "path": "spec/std/compress/zlib/reader_spec.cr",
    "content": "require \"spec\"\nrequire \"compress/zlib\"\n\nprivate def new_sample_io\n  io = IO::Memory.new\n  \"789c2bc9c82c5600a2448592d4e21285e292a2ccbc74054520e00200854f087b\".scan(/../).each do |match|\n    io.write_byte match[0].to_u8(16)\n  end\n  io.rewind\nend\n\nmodule Compress::Zlib\n  describe Reader do\n    it \"should be able to read\" do\n      io = new_sample_io\n\n      reader = Reader.new(io)\n\n      str = String::Builder.build do |builder|\n        IO.copy(reader, builder)\n      end\n\n      str.should eq(\"this is a test string !!!!\\n\")\n      reader.read(Bytes.new(10)).should eq(0)\n    end\n\n    it \"rewinds\" do\n      io = new_sample_io\n\n      reader = Reader.new(io)\n      reader.gets(3).should eq(\"thi\")\n      reader.rewind\n      reader.gets_to_end.should eq(\"this is a test string !!!!\\n\")\n    end\n\n    it \"can be closed without sync\" do\n      io = IO::Memory.new(Bytes[120, 156, 3, 0, 0, 0, 0, 1])\n      reader = Reader.new(io)\n      reader.close\n      reader.closed?.should be_true\n      io.closed?.should be_false\n\n      expect_raises IO::Error, \"Closed stream\" do\n        reader.gets\n      end\n    end\n\n    it \"can be closed with sync (1)\" do\n      io = IO::Memory.new(Bytes[120, 156, 3, 0, 0, 0, 0, 1])\n      reader = Reader.new(io, sync_close: true)\n      reader.close\n      reader.closed?.should be_true\n      io.closed?.should be_true\n    end\n\n    it \"can be closed with sync (2)\" do\n      io = IO::Memory.new(Bytes[120, 156, 3, 0, 0, 0, 0, 1])\n      reader = Reader.new(io)\n      reader.sync_close = true\n      reader.close\n      reader.closed?.should be_true\n      io.closed?.should be_true\n    end\n\n    it \"should not read from empty stream\" do\n      io = IO::Memory.new(Bytes[120, 156, 3, 0, 0, 0, 0, 1])\n      reader = Reader.new(io)\n      reader.read_byte.should be_nil\n    end\n\n    it \"should not freeze when reading empty slice\" do\n      io = new_sample_io\n      reader = Reader.new(io)\n      slice = Bytes.empty\n      reader.read(slice).should eq(0)\n    end\n\n    it \"should raise buffer error on error (#6575)\" do\n      io = IO::Memory.new(\"x\\x9C4\\xC9\\xD1\\n@@\\u0010\\u0005\\xD0\\u007F\\xB9ϻeEj~E\\xD2`B\\xAD\\xA55H\\x9B\\u007F\\xE7\\xC5۩\\x93\\xA0\\xA0pxo\\xB0\\xFFX7\\x90\\xCB\\f\\u0006P\\xC2$\\u001C\\xB5\\u0013\\xD6v\\u000E*\\xF1d\\u000F*\\\\^~\\xDFj\\xE4^@5FV\\xB9\\xF8\\xB6[\\u001C\\xEC\\xC2s\\xB0\\x99\\xD3\\n\\xCD\\xF3\\xBC\\u0000\\u0000\\u0000\\xFF\\xFF\")\n\n      reader = Reader.new(io)\n\n      expect_raises(Compress::Deflate::Error, \"deflate: buffer error\") do\n        reader.gets_to_end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/compress/zlib/stress_spec.cr",
    "content": "require \"spec\"\nrequire \"compress/zlib\"\n\nmodule Compress::Zlib\n  describe Zlib do\n    it \"write read should be inverse with random string\" do\n      expected = String.build do |io|\n        1_000_000.times { rand(2000).to_i.to_s(io, 32) }\n      end\n\n      io = IO::Memory.new\n\n      writer = Writer.new(io)\n      writer.print expected\n      writer.close\n\n      io.rewind\n      reader = Reader.new(io)\n      reader.gets_to_end.should eq(expected)\n    end\n\n    it \"write read should be inverse (utf-8)\" do\n      expected = \"日本さん語日本さん語\"\n\n      io = IO::Memory.new\n\n      writer = Writer.new(io)\n      writer.print expected\n      writer.close\n\n      io.rewind\n      reader = Reader.new(io)\n      reader.gets_to_end.should eq(expected)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/compress/zlib/writer_spec.cr",
    "content": "require \"spec\"\nrequire \"compress/zlib\"\n\nmodule Compress::Zlib\n  describe Writer do\n    it \"should be able to write\" do\n      message = \"this is a test string !!!!\\n\"\n      io = IO::Memory.new\n\n      writer = Writer.new(io)\n\n      io.bytesize.should eq(0)\n      writer.flush\n      io.bytesize.should_not eq(0)\n\n      writer.print message\n      writer.close\n\n      io.rewind\n      reader = Reader.new(io)\n      reader.gets_to_end.should eq(message)\n    end\n\n    it \"can be closed without sync\" do\n      io = IO::Memory.new\n      writer = Writer.new(io)\n      writer.close\n      writer.closed?.should be_true\n      io.closed?.should be_false\n\n      expect_raises IO::Error, \"Closed stream\" do\n        writer.print \"a\"\n      end\n    end\n\n    it \"can be closed with sync (1)\" do\n      io = IO::Memory.new\n      writer = Writer.new(io, sync_close: true)\n      writer.close\n      writer.closed?.should be_true\n      io.closed?.should be_true\n    end\n\n    it \"can be closed with sync (2)\" do\n      io = IO::Memory.new\n      writer = Writer.new(io)\n      writer.sync_close = true\n      writer.close\n      writer.closed?.should be_true\n      io.closed?.should be_true\n    end\n\n    it \"can be flushed\" do\n      io = IO::Memory.new\n      writer = Writer.new(io)\n\n      writer.print \"this\"\n      io.to_slice.hexstring.should eq(\"789c\")\n\n      writer.flush\n      io.to_slice.hexstring.size.should be > 4\n\n      writer.print \" is a test string !!!!\\n\"\n      writer.close\n\n      io.rewind\n      reader = Reader.new(io)\n      reader.gets_to_end.should eq(\"this is a test string !!!!\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/concurrent/select_spec.cr",
    "content": "require \"spec\"\nrequire \"../spec_helper\"\n\ndescribe \"select\" do\n  it \"select many receivers\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    res = [] of Int32\n    spawn do\n      10.times do |i|\n        (i % 2 == 0) ? ch1.send(i) : ch2.send(i)\n      end\n    end\n\n    10.times do\n      select\n      when x = ch1.receive\n        res << x\n      when y = ch2.receive\n        res << y\n      end\n    end\n    res.should eq (0...10).to_a\n  end\n\n  it \"select many senders\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    res = Array.new(10, 0)\n\n    f1 = spawn do\n      5.times { res[ch1.receive] = 1 }\n    end\n\n    f2 = spawn do\n      5.times { res[ch2.receive] = 1 }\n    end\n\n    10.times do |i|\n      select\n      when ch1.send(i)\n      when ch2.send(i)\n      end\n    end\n\n    until f1.dead? && f2.dead?\n      Fiber.yield\n    end\n\n    res.should eq Array.new(10, 1)\n  end\n\n  it \"select many receivers, senders\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    res = [] of Int32\n    f = spawn do\n      10.times do |i|\n        select\n        when x = ch1.receive\n          res << x\n        when ch2.send(i)\n        end\n      end\n    end\n\n    10.times do |i|\n      select\n      when ch1.send(i)\n      when y = ch2.receive\n        res << y\n      end\n    end\n\n    wait_until_finished f\n\n    res.should eq (0...10).to_a\n  end\n\n  it \"select should work with send which started before receive, fixed #3862\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    main = Fiber.current\n\n    spawn do\n      select\n      when ch1.send(1)\n      when ch2.receive\n      end\n    end\n\n    x = nil\n\n    spawn do\n      select\n      when a = ch1.receive\n        x = a\n      when b = ch2.receive\n        x = b\n      end\n    ensure\n      main.enqueue\n    end\n\n    sleep\n    x.should eq 1\n  end\n\n  it \"select fiber has one chance to be enqueued into scheduler (1)\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    ch3 = Channel(Int32).new\n    x = nil\n\n    f = spawn do\n      select\n      when x = ch1.receive\n      when x = ch2.receive\n      end\n    end\n\n    spawn do\n      ch1.send 1\n      ch3.send 3\n      ch2.close\n    end\n\n    ch3.receive.should eq(3)\n    wait_until_finished f\n    x.should eq(1)\n  end\n\n  it \"select fiber has one chance to be enqueued into scheduler (2)\" do\n    ch1 = Channel(Int32).new\n    ch2 = Channel(Int32).new\n    ch3 = Channel(Int32).new\n    x = nil\n\n    f = spawn do\n      select\n      when ch1.send 1\n        x = 1\n      when ch2.send 2\n        x = 2\n      end\n    end\n\n    spawn do\n      ch1.receive\n      ch3.send 3\n      ch2.close\n    end\n\n    ch3.receive.should eq(3)\n    wait_until_finished f\n    x.should eq(1)\n  end\n\n  it \"select same channel multiple times\" do\n    ch = Channel(Int32).new\n\n    spawn do\n      ch.send(123)\n    end\n\n    select\n    when ch.send(456)\n    when x = ch.receive\n    end\n\n    x.should eq 123\n  end\n\n  it \"prioritize by order when entering in a select\" do\n    ch1 = Channel(Int32).new(5)\n    ch2 = Channel(Int32).new(5)\n\n    2.times { ch1.send 1 }\n    2.times { ch2.send 2 }\n\n    select\n    when x = ch1.receive\n    when x = ch2.receive\n    end\n    x.should eq 1\n\n    select\n    when x = ch2.receive\n    when x = ch1.receive\n    end\n    x.should eq 2\n  end\n\n  it \"stress select with send/receive in multiple fibers\" do\n    fibers = 4\n    msg_per_sender = 1000\n    ch = Array.new(fibers) { Array.new(fibers) { Channel(Int32).new } }\n    done = Channel({Int32, Int32}).new\n\n    fibers.times do |i|\n      spawn(name: \"sender #{i}\") do\n        channels = ch[i]\n        msg_per_sender.times do |i|\n          Channel.send_first(i, channels)\n        end\n        channels.map &.send(-1)\n      end\n    end\n\n    fibers.times do |i|\n      spawn(name: \"receiver #{i}\") do\n        channels = ch.map { |chs| chs[i] }\n        closed = 0\n        count = 0\n        sum = 0\n        loop do\n          x = Channel.receive_first(channels).not_nil!\n          if x == -1\n            closed += 1\n            break if closed == fibers\n          else\n            count += 1\n            sum += x\n          end\n        end\n        done.send({count, sum})\n      end\n    end\n\n    count = 0\n    sum = 0\n    fibers.times do\n      c, s = done.receive\n      count += c\n      sum += s\n    end\n\n    count.should eq(fibers * msg_per_sender)\n    sum.should eq(msg_per_sender * (msg_per_sender - 1) / 2 * fibers)\n  end\n\n  context \"blocking raise-on-close single-channel\" do\n    it \"types and exec when\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive\n          w.check\n          typeof(m).should eq(String)\n          m.should eq(\"foo\")\n        end\n      end\n    end\n\n    {% if flag?(:win32) && flag?(:aarch64) %}\n      pending \"raises if channel was closed\"\n    {% else %}\n      it \"raises if channel was closed\" do\n        ch = Channel(String).new\n\n        spawn_and_check(-> { ch.close }) do |w|\n          begin\n            select\n            when m = ch.receive\n            end\n          rescue Channel::ClosedError\n            w.check\n          end\n        end\n      end\n    {% end %}\n  end\n\n  context \"non-blocking raise-on-close single-channel\" do\n    it \"types and exec when if message was ready\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive\n          w.check\n          typeof(m).should eq(String)\n          m.should eq(\"foo\")\n        else\n        end\n      end\n    end\n\n    it \"exec else if no message was ready\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { nil }) do |w|\n        select\n        when m = ch.receive\n        else\n          w.check\n        end\n      end\n    end\n\n    {% if flag?(:win32) && flag?(:aarch64) %}\n      pending \"raises if channel was closed\"\n    {% else %}\n      it \"raises if channel was closed\" do\n        ch = Channel(String).new\n\n        spawn_and_check(-> { ch.close }) do |w|\n          begin\n            select\n            when m = ch.receive\n            else\n            end\n          rescue Channel::ClosedError\n            w.check\n          end\n        end\n      end\n    {% end %}\n  end\n\n  context \"blocking raise-on-close multi-channel\" do\n    it \"types and exec when (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive\n          w.check\n          typeof(m).should eq(String)\n          m.should eq(\"foo\")\n        when m = ch2.receive\n        end\n      end\n    end\n\n    it \"types and exec when (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.send true }) do |w|\n        select\n        when m = ch.receive\n        when m = ch2.receive\n          w.check\n          typeof(m).should eq(Bool)\n          m.should be_true\n        end\n      end\n    end\n\n    {% if flag?(:win32) && flag?(:aarch64) %}\n      pending \"raises if channel was closed\"\n    {% else %}\n      it \"raises if channel was closed (1)\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n\n        spawn_and_check(-> { ch.close }) do |w|\n          begin\n            select\n            when m = ch.receive\n            when m = ch2.receive\n            end\n          rescue Channel::ClosedError\n            w.check\n          end\n        end\n      end\n\n      it \"raises if channel was closed (2)\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n\n        spawn_and_check(-> { ch2.close }) do |w|\n          begin\n            select\n            when m = ch.receive\n            when m = ch2.receive\n            end\n          rescue Channel::ClosedError\n            w.check\n          end\n        end\n      end\n    {% end %}\n  end\n\n  context \"non-blocking raise-on-close multi-channel\" do\n    it \"types and exec when (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive\n          w.check\n          typeof(m).should eq(String)\n          m.should eq(\"foo\")\n        when m = ch2.receive\n        else\n        end\n      end\n    end\n\n    it \"types and exec when (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.send true }) do |w|\n        select\n        when m = ch.receive\n        when m = ch2.receive\n          w.check\n          typeof(m).should eq(Bool)\n          m.should be_true\n        else\n        end\n      end\n    end\n\n    it \"exec else if no message was ready\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { nil }) do |w|\n        select\n        when m = ch.receive\n        when m = ch2.receive\n        else\n          w.check\n        end\n      end\n    end\n\n    {% if flag?(:win32) && flag?(:aarch64) %}\n      pending \"raises if channel was closed\"\n    {% else %}\n      it \"raises if channel was closed (1)\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n\n        spawn_and_check(-> { ch.close }) do |w|\n          begin\n            select\n            when m = ch.receive\n            when m = ch2.receive\n            else\n            end\n          rescue Channel::ClosedError\n            w.check\n          end\n        end\n      end\n\n      it \"raises if channel was closed (2)\" do\n        ch = Channel(String).new\n        ch2 = Channel(Bool).new\n\n        spawn_and_check(-> { ch2.close }) do |w|\n          begin\n            select\n            when m = ch.receive\n            when m = ch2.receive\n            else\n            end\n          rescue Channel::ClosedError\n            w.check\n          end\n        end\n      end\n    {% end %}\n  end\n\n  context \"blocking nil-on-close single-channel\" do\n    it \"types and exec when\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should eq(\"foo\")\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel was closed\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { ch.close }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should be_nil\n        end\n      end\n    end\n  end\n\n  context \"blocking nil-on-close multi-channel\" do\n    it \"types and exec when (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should eq(\"foo\")\n        when m = ch2.receive?\n        end\n      end\n    end\n\n    it \"types and exec when (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.send true }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n          w.check\n          typeof(m).should eq(Bool?)\n          m.should be_true\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel was closed (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.close }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should be_nil\n        when m = ch2.receive?\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel was closed (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.close }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n          w.check\n          typeof(m).should eq(Bool?)\n          m.should be_nil\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel is closed while waiting (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.close }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should be_nil\n        when m = ch2.receive?\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel is closed while waiting (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.close }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n          w.check\n          typeof(m).should eq(Bool?)\n          m.should be_nil\n        end\n      end\n    end\n  end\n\n  context \"non-blocking nil-on-close single-channel\" do\n    it \"types and exec when\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should eq(\"foo\")\n        else\n        end\n      end\n    end\n\n    it \"exec else if no message was ready\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { nil }) do |w|\n        select\n        when m = ch.receive?\n        else\n          w.check\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel was closed\" do\n      ch = Channel(String).new\n\n      spawn_and_check(-> { ch.close }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should be_nil\n        else\n        end\n      end\n    end\n  end\n\n  context \"non-blocking nil-on-close multi-channel\" do\n    it \"types and exec when (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.send \"foo\" }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should eq(\"foo\")\n        when m = ch2.receive?\n        else\n        end\n      end\n    end\n\n    it \"types and exec when (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.send true }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n          w.check\n          typeof(m).should eq(Bool?)\n          m.should be_true\n        else\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel was closed (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.close }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should be_nil\n        when m = ch2.receive?\n        else\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel was closed (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.close }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n          w.check\n          typeof(m).should eq(Bool?)\n          m.should be_nil\n        else\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel is closed while waiting (1)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch.close }) do |w|\n        select\n        when m = ch.receive?\n          w.check\n          typeof(m).should eq(String?)\n          m.should be_nil\n        when m = ch2.receive?\n        else\n        end\n      end\n    end\n\n    it \"types and exec when with nil if channel is closed while waiting (2)\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { ch2.close }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n          w.check\n          typeof(m).should eq(Bool?)\n          m.should be_nil\n        else\n        end\n      end\n    end\n\n    it \"exec else if no message was ready\" do\n      ch = Channel(String).new\n      ch2 = Channel(Bool).new\n\n      spawn_and_check(-> { nil }) do |w|\n        select\n        when m = ch.receive?\n        when m = ch2.receive?\n        else\n          w.check\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/concurrent_spec.cr",
    "content": "require \"./spec_helper\"\n\nprivate def method_with_named_args(chan, x = 1, y = 2)\n  chan.send(x + y)\nend\n\nprivate def method_returning_2tuple(x, y)\n  {x, y}\nend\n\nprivate def method_returning_3tuple(chan, x = 1, y = 2)\n  {chan, x, y}\nend\n\nprivate def method_named(expected_named, chan)\n  Fiber.current.name.should eq(expected_named)\n  chan.close\nend\n\ndescribe \"concurrent\" do\n  describe \"spawn\" do\n    it \"uses spawn macro\" do\n      chan = Channel(Int32).new\n\n      spawn method_with_named_args(chan)\n      chan.receive.should eq(3)\n\n      spawn method_with_named_args(chan, y: 20)\n      chan.receive.should eq(21)\n\n      spawn method_with_named_args(chan, x: 10, y: 20)\n      chan.receive.should eq(30)\n\n      spawn method_with_named_args(*method_returning_3tuple(chan))\n      chan.receive.should eq(3)\n\n      spawn method_with_named_args(*method_returning_3tuple(chan, 30, 40))\n      chan.receive.should eq(70)\n\n      spawn method_with_named_args(*method_returning_2tuple(chan, 10))\n      chan.receive.should eq(12)\n\n      spawn method_with_named_args(chan, *method_returning_2tuple(10, 20))\n      chan.receive.should eq(30)\n\n      spawn method_with_named_args(*method_returning_2tuple(chan, 10), 20)\n      chan.receive.should eq(30)\n    end\n\n    it \"spawns named\" do\n      chan = Channel(Nil).new\n      spawn(name: \"sub\") do\n        Fiber.current.name.should eq(\"sub\")\n        chan.close\n      end\n      chan.receive?\n    end\n\n    it \"spawns named with macro\" do\n      chan = Channel(Nil).new\n      spawn method_named(\"foo\", chan), name: \"foo\"\n      chan.receive?\n    end\n  end\n\n  it \"accepts method call with receiver\" do\n    typeof(spawn String.new)\n  end\n\n  {% if flag?(:darwin) %}\n    pending \"schedules intermitting sleeps\"\n    # TODO: This spec fails on darwin, even with highly increased sleep times. Needs investigation.\n  {% else %}\n    it \"schedules intermitting sleeps\" do\n      chan = Channel(Int32).new\n      spawn do\n        3.times do |i|\n          sleep 40.milliseconds\n          chan.send(i + 1)\n        end\n      end\n      spawn do\n        2.times do |i|\n          sleep 100.milliseconds\n          chan.send (i + 1) * 10\n        end\n      end\n\n      Array(Int32).new(5) { chan.receive }.should eq [1, 2, 10, 3, 20]\n    end\n  {% end %}\n\n  it \"sleep does not return immediately (#16578)\" do\n    elapsed = Time.measure do\n      sleep 50.milliseconds\n    end\n    elapsed.should be >= 50.milliseconds\n  end\nend\n"
  },
  {
    "path": "spec/std/crypto/bcrypt/base64_spec.cr",
    "content": "require \"spec\"\nrequire \"crypto/bcrypt/base64\"\n\ndescribe \"Crypto::Bcrypt::Base64\" do\n  vectors = [\n    {UInt8[0x25], \"HO\"},\n    {UInt8[0x6b, 0x11], \"YvC\"},\n    {UInt8[0x54, 0xe0, 0x19], \"TM.X\"},\n    {UInt8[0xb7, 0xf4, 0x24, 0x20], \"r9OiG.\"},\n    {UInt8[0xee, 0x98, 0xfb, 0xf3, 0x6c], \"5nh560u\"},\n    {UInt8[0xfc, 0x4f, 0x0a, 0x3d, 0x78, 0x22], \"9C6INVeg\"},\n    {UInt8[0x72, 0x4b, 0x0d, 0x7b, 0xd5, 0xae, 0xda], \"aiqLc7Us0e\"},\n    {UInt8[0xe4, 0x58, 0x0e, 0xff, 0xb8, 0x33, 0xab, 0x56], \"3DeM95exozW\"},\n    {UInt8[0x67, 0xf8, 0x86, 0x87, 0x99, 0xd1, 0x47, 0xcb, 0x86], \"X9gEf3lPP6sE\"},\n    {UInt8[0x69, 0x5e, 0x82, 0x7f, 0xb1, 0x52, 0x47, 0x95, 0x6d, 0x57], \"YT4Ad5DQP3TrTu\"},\n    {UInt8[0xa3, 0x33, 0x9b, 0xa9, 0x2b, 0x27, 0xfd, 0x07, 0xc2, 0x86, 0xc9], \"mxMZoQql9OdAfqi\"},\n    {UInt8[0xae, 0x1f, 0xa7, 0xc6, 0xe2, 0x90, 0x76, 0x27, 0x51, 0x6d, 0xfc, 0x8e], \"pf8lvsIObgbPZdwM\"},\n    {UInt8[0xaa, 0xab, 0x0d, 0xcc, 0x66, 0x98, 0x2d, 0xaa, 0x23, 0x6e, 0x7d, 0xac, 0xae], \"ooqLxEYWJYmhZl0qpe\"},\n    {UInt8[0x71, 0xc5, 0x7d, 0x5a, 0x84, 0x1b, 0x24, 0xdd, 0x8c, 0xaa, 0xe0, 0xc6, 0xe3, 0xd1], \"aaT7UmOZHL0KosBE27C\"},\n    {UInt8[0xb9, 0x6d, 0xa5, 0x22, 0x72, 0xa5, 0xb5, 0xa4, 0xfd, 0x1b, 0x55, 0xac, 0x94, 0x3c, 0xc1], \"sU0jGlIjrYR7EzUqjBx/\"},\n    {UInt8[0x21, 0xc4, 0x14, 0x2b, 0xe6, 0xba, 0xdd, 0xf2, 0xb4, 0x7b, 0xee, 0x16, 0xae, 0x7b, 0xf2, 0x73], \"GaOSI8Y41dIyc82Upltwau\"},\n    {UInt8[0x00, 0x3f, 0xf4, 0x21, 0x6a, 0x58, 0x43, 0xf5, 0x9e, 0x73, 0x45, 0xbd, 0xe1, 0x05, 0x88, 0x1b, 0x94], \".B9yGUnWO9UcayU72OUGE3O\"},\n    {UInt8[0x89, 0xc4, 0x46, 0x1e, 0x18, 0xe3, 0x25, 0xd6, 0x1a, 0xcc, 0x84, 0x2c, 0xb1, 0x11, 0x91, 0x13, 0x2d, 0x61], \"gaPEFfhhHbWYxGOqqPEPCwzf\"},\n    {UInt8[0x2b, 0x37, 0x94, 0xaf, 0xc6, 0x52, 0x60, 0x15, 0x92, 0x03, 0x3f, 0x36, 0x64, 0x34, 0xa6, 0xf0, 0xc9, 0x6d, 0xfa], \"IxcSp6XQW/UQ.x60XBQk6Kjr8e\"},\n    {UInt8[0x4e, 0x22, 0xdc, 0x36, 0xa3, 0xd6, 0x6f, 0x14, 0x1b, 0x56, 0xb6, 0x26, 0x72, 0x3b, 0x23, 0xa1, 0x3f, 0x6f, 0x2f, 0xaa], \"RgJaLoNUZvOZTpWkahqhmR7tJ4m\"},\n    {UInt8[0x18, 0x42, 0x6b, 0x5e, 0x6b, 0xff, 0x02, 0x88, 0x9c, 0x7f, 0xcb, 0xb5, 0xb7, 0x0e, 0x9c, 0xdb, 0x5a, 0x26, 0x79, 0x2a, 0x6d], \"ECHpVkt9.mgad6szru4a0zmkcQnr\"},\n    {UInt8[0x4c, 0x3c, 0x8d, 0x04, 0xab, 0xf2, 0xb1, 0x9c, 0xbc, 0xb5, 0x33, 0xe1, 0x24, 0xf3, 0x0a, 0x69, 0x6c, 0xcc, 0xec, 0xf5, 0xd4, 0x21], \"RBwL/ItwqXw6rRNfHNKIYUxK5NVSGO\"},\n    {UInt8[0xe1, 0x2a, 0x52, 0x02, 0xeb, 0x8a, 0x40, 0x85, 0x5f, 0xba, 0xdb, 0x13, 0x1e, 0x94, 0x9b, 0xb9, 0x55, 0x54, 0xe9, 0x9c, 0x87, 0x9c, 0xbf], \"2QnQ.ssIOGTdsrqRFnQZsTTS4XwFlJ6\"},\n  ]\n\n  it \"encodes\" do\n    vectors.each do |vector|\n      decoded, encoded = vector\n      Crypto::Bcrypt::Base64.encode(decoded, decoded.size).should eq(encoded)\n    end\n  end\n\n  it \"decodes\" do\n    vectors.each do |vector|\n      decoded, encoded = vector\n      Crypto::Bcrypt::Base64.decode(encoded, decoded.size).to_a.should eq(decoded)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crypto/bcrypt/password_spec.cr",
    "content": "require \"spec\"\nrequire \"crypto/bcrypt/password\"\n\ndescribe \"Crypto::Bcrypt::Password\" do\n  describe \"new\" do\n    password = Crypto::Bcrypt::Password.new(\"$2a$08$K8y0i4Wyqyei3SiGHLEd.OweXJt7sno2HdPVrMvVf06kGgAZvPkga\")\n\n    it \"parses version\" do\n      password.version.should eq(\"2a\")\n    end\n\n    it \"parses cost\" do\n      password.cost.should eq(8)\n    end\n\n    it \"parses salt\" do\n      password.salt.should eq(\"K8y0i4Wyqyei3SiGHLEd.O\")\n    end\n\n    it \"parses digest\" do\n      password.digest.should eq(\"weXJt7sno2HdPVrMvVf06kGgAZvPkga\")\n    end\n\n    it \"validates the hash string has the required amount of parts\" do\n      expect_raises(Crypto::Bcrypt::Error, \"Invalid hash string\") do\n        Crypto::Bcrypt::Password.new(\"blarp\")\n      end\n    end\n\n    it \"raises on unsupported version (#11584)\" do\n      expect_raises(Crypto::Bcrypt::Error, \"Invalid hash version\") do\n        Crypto::Bcrypt::Password.new(\"$-1$10$blarp\")\n      end\n    end\n  end\n\n  describe \"create\" do\n    password = Crypto::Bcrypt::Password.create(\"super secret\", 5)\n\n    it \"uses cost\" do\n      password.cost.should eq(5)\n    end\n\n    it \"generates salt\" do\n      password.salt.should_not be_nil\n    end\n\n    it \"generates digest\" do\n      password.digest.should_not be_nil\n    end\n  end\n\n  describe \"verify\" do\n    password = Crypto::Bcrypt::Password.create(\"secret\", 4)\n    password2 = Crypto::Bcrypt::Password.new(\"$2$04$ZsHrsVlj.dsmn74Az1rjmeE/21nYRC0vB5LPjG7ySBfi6lRaO/P22\")\n    password2a = Crypto::Bcrypt::Password.new(\"$2a$04$ZsHrsVlj.dsmn74Az1rjmeE/21nYRC0vB5LPjG7ySBfi6lRaO/P22\")\n    password2b = Crypto::Bcrypt::Password.new(\"$2b$04$ZsHrsVlj.dsmn74Az1rjmeE/21nYRC0vB5LPjG7ySBfi6lRaO/P22\")\n    password2y = Crypto::Bcrypt::Password.new(\"$2y$04$ZsHrsVlj.dsmn74Az1rjmeE/21nYRC0vB5LPjG7ySBfi6lRaO/P22\")\n\n    it \"verifies password is incorrect\" do\n      (password.verify \"wrong\").should be_false\n    end\n\n    it \"verifies password is correct\" do\n      (password.verify \"secret\").should be_true\n    end\n\n    it \"verifies password version 2 is correct (#11584)\" do\n      (password2.verify \"secret\").should be_true\n    end\n    it \"verifies password version 2a is correct (#11584)\" do\n      (password2a.verify \"secret\").should be_true\n    end\n    it \"verifies password version 2b is correct (#11584)\" do\n      (password2b.verify \"secret\").should be_true\n    end\n    it \"verifies password version 2y is correct\" do\n      (password2y.verify \"secret\").should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crypto/bcrypt_spec.cr",
    "content": "require \"spec\"\nrequire \"crypto/bcrypt\"\nrequire \"random/secure\"\n\ndescribe \"Crypto::Bcrypt\" do\n  latin1_pound_sign = String.new(Bytes.new(1, 0xa3_u8))\n  utf8_pound_sign = String.new(Bytes.new(2) { |i| i == 0 ? 0xc2_u8 : 0xa3_u8 })\n  bit8_unicode_pound_sign = \"\\u00A3\"\n\n  vectors = [\n    {6, \"a\", \"m0CrhHm10qJ3lXRY.5zDGO\", \"3rS2KdeeWLuGmsfGlMfOxih58VYVfxe\"},\n    {6, \"abc\", \"If6bvum7DFjUnE9p2uDeDu\", \"0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i\"},\n    {6, \"abcdefghijklmnopqrstuvwxyz\", \".rCVZVOThsIa97pEDOxvGu\", \"RRgzG64bvtJ0938xuqzv18d3ZpQhstC\"},\n    {6, \"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"fPIsBO8qRqkjj273rfaOI.\", \"HtSV9jLDpTbZn782DC6/t7qT67P6FfO\"},\n    {8, \"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"Eq2r4G/76Wv39MzSX262hu\", \"zPz612MZiYHVUJe/OcOql2jo4.9UxTW\"},\n    {10, \"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"LgfYWkbzEvQ4JakH7rOvHe\", \"0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS\"},\n    {5, latin1_pound_sign, \"CCCCCCCCCCCCCCCCCCCCC.\", \"BvtRGGx3p8o0C5C36uS442Qqnrwofrq\"},\n    {5, utf8_pound_sign, \"CCCCCCCCCCCCCCCCCCCCC.\", \"CAzSxlf0FLW7g1A5q7W/ZCj1xsN6A.e\"},\n    {5, bit8_unicode_pound_sign, \"CCCCCCCCCCCCCCCCCCCCC.\", \"CAzSxlf0FLW7g1A5q7W/ZCj1xsN6A.e\"},\n    {5, \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789012345678\", \"VU6N0LbtX7trKLCg4Uf8qe\", \"5WYPzqIUUIrkveFjCbMg/hXc592OQLK\"},\n  ]\n\n  it \"computes digest vectors\" do\n    vectors.each_with_index do |vector, index|\n      cost, password, salt, digest = vector\n      bc = Crypto::Bcrypt.new(password, salt, cost)\n      Crypto::Bcrypt::Base64.encode(bc.digest, 23).should eq(digest)\n    end\n  end\n\n  it \"validates salt size\" do\n    expect_raises(Crypto::Bcrypt::Error, /Invalid salt size/) do\n      Crypto::Bcrypt.new(\"abcd\", Random::Secure.hex(7))\n    end\n\n    expect_raises(Crypto::Bcrypt::Error, /Invalid salt size/) do\n      Crypto::Bcrypt.new(\"abcd\", Random::Secure.hex(9))\n    end\n  end\n\n  it \"validates cost\" do\n    salt = Random::Secure.hex(8)\n\n    expect_raises(Crypto::Bcrypt::Error, /Invalid cost/) do\n      Crypto::Bcrypt.new(\"abcd\", salt, 3)\n    end\n\n    expect_raises(Crypto::Bcrypt::Error, /Invalid cost/) do\n      Crypto::Bcrypt.new(\"abcd\", salt, 32)\n    end\n  end\n\n  it \"validates password size\" do\n    salt = Random::Secure.random_bytes(16)\n\n    expect_raises(Crypto::Bcrypt::Error, /Invalid password size/) do\n      Crypto::Bcrypt.new(\"\".to_slice, salt)\n    end\n\n    expect_raises(Crypto::Bcrypt::Error, /Invalid password size/) do\n      Crypto::Bcrypt.new(\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 invalid\".to_slice, salt)\n    end\n  end\n\n  # Read http://lwn.net/Articles/448699/\n  it \"doesn't have the sign expansion (high 8bit) security flaw\" do\n    salt = \"OK.fbVrR/bpIqNJ5ianF.C\"\n    hash1 = Crypto::Bcrypt.new(\"ab#{latin1_pound_sign}\", salt, 5)\n    hash2 = Crypto::Bcrypt.new(latin1_pound_sign, salt, 5)\n    hash2.should_not eq(hash1)\n  end\nend\n"
  },
  {
    "path": "spec/std/crypto/blowfish_spec.cr",
    "content": "require \"spec\"\nrequire \"crypto/blowfish\"\n\ndescribe \"Crypto::Blowfish\" do\n  vectors = [\n    {UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], UInt32[0x00000000, 0x00000000], UInt32[0x4EF99745, 0x6198DD78]},\n    {UInt8[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], UInt32[0xFFFFFFFF, 0xFFFFFFFF], UInt32[0x51866FD5, 0xB85ECB8A]},\n    {UInt8[0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], UInt32[0x10000000, 0x00000001], UInt32[0x7D856F9A, 0x613063F2]},\n    {UInt8[0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11], UInt32[0x11111111, 0x11111111], UInt32[0x2466DD87, 0x8B963C9D]},\n    {UInt8[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF], UInt32[0x11111111, 0x11111111], UInt32[0x61F9C380, 0x2281B096]},\n    {UInt8[0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11], UInt32[0x01234567, 0x89ABCDEF], UInt32[0x7D0CC630, 0xAFDA1EC7]},\n    {UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], UInt32[0x00000000, 0x00000000], UInt32[0x4EF99745, 0x6198DD78]},\n    {UInt8[0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], UInt32[0x01234567, 0x89ABCDEF], UInt32[0x0ACEAB0F, 0xC6A0A28D]},\n    {UInt8[0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57], UInt32[0x01A1D6D0, 0x39776742], UInt32[0x59C68245, 0xEB05282B]},\n    {UInt8[0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E], UInt32[0x5CD54CA8, 0x3DEF57DA], UInt32[0xB1B8CC0B, 0x250F09A0]},\n    {UInt8[0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86], UInt32[0x0248D438, 0x06F67172], UInt32[0x1730E577, 0x8BEA1DA4]},\n    {UInt8[0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E], UInt32[0x51454B58, 0x2DDF440A], UInt32[0xA25E7856, 0xCF2651EB]},\n    {UInt8[0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6], UInt32[0x42FD4430, 0x59577FA2], UInt32[0x353882B1, 0x09CE8F1A]},\n    {UInt8[0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE], UInt32[0x059B5E08, 0x51CF143A], UInt32[0x48F4D088, 0x4C379918]},\n    {UInt8[0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6], UInt32[0x0756D8E0, 0x774761D2], UInt32[0x432193B7, 0x8951FC98]},\n    {UInt8[0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE], UInt32[0x762514B8, 0x29BF486A], UInt32[0x13F04154, 0xD69D1AE5]},\n    {UInt8[0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16], UInt32[0x3BDD1190, 0x49372802], UInt32[0x2EEDDA93, 0xFFD39C79]},\n    {UInt8[0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F], UInt32[0x26955F68, 0x35AF609A], UInt32[0xD887E039, 0x3C2DA6E3]},\n    {UInt8[0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46], UInt32[0x164D5E40, 0x4F275232], UInt32[0x5F99D04F, 0x5B163969]},\n    {UInt8[0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E], UInt32[0x6B056E18, 0x759F5CCA], UInt32[0x4A057A3B, 0x24D3977B]},\n    {UInt8[0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76], UInt32[0x004BD6EF, 0x09176062], UInt32[0x452031C1, 0xE4FADA8E]},\n    {UInt8[0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07], UInt32[0x480D3900, 0x6EE762F2], UInt32[0x7555AE39, 0xF59B87BD]},\n    {UInt8[0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F], UInt32[0x437540C8, 0x698F3CFA], UInt32[0x53C55F9C, 0xB49FC019]},\n    {UInt8[0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7], UInt32[0x072D43A0, 0x77075292], UInt32[0x7A8E7BFA, 0x937E89A3]},\n    {UInt8[0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF], UInt32[0x02FE5577, 0x8117F12A], UInt32[0xCF9C5D7A, 0x4986ADB5]},\n    {UInt8[0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6], UInt32[0x1D9D5C50, 0x18F728C2], UInt32[0xD1ABB290, 0x658BC778]},\n    {UInt8[0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF], UInt32[0x30553228, 0x6D6F295A], UInt32[0x55CB3774, 0xD13EF201]},\n    {UInt8[0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01], UInt32[0x01234567, 0x89ABCDEF], UInt32[0xFA34EC48, 0x47B268B2]},\n    {UInt8[0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E], UInt32[0x01234567, 0x89ABCDEF], UInt32[0xA7907951, 0x08EA3CAE]},\n    {UInt8[0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE], UInt32[0x01234567, 0x89ABCDEF], UInt32[0xC39E072D, 0x9FAC631D]},\n    {UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], UInt32[0xFFFFFFFF, 0xFFFFFFFF], UInt32[0x014933E0, 0xCDAFF6E4]},\n    {UInt8[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], UInt32[0x00000000, 0x00000000], UInt32[0xF21E9A77, 0xB71C49BC]},\n    {UInt8[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF], UInt32[0x00000000, 0x00000000], UInt32[0x24594688, 0x5754369A]},\n    {UInt8[0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], UInt32[0xFFFFFFFF, 0xFFFFFFFF], UInt32[0x6B5C5A9C, 0x5D9E0A5A]},\n  ]\n\n  it \"encrypt and decrypt pair\" do\n    vectors.each_with_index do |(key, text, cipher), index|\n      bf = Crypto::Blowfish.new(16)\n      bf.expand_key(key)\n\n      # encrypt_pair\n      lr = bf.encrypt_pair(text[0], text[1])\n      lr.to_a.should eq(cipher)\n\n      # decrypt_pair\n      lr = bf.decrypt_pair(lr[0], lr[1])\n      lr.to_a.should eq(text)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crypto/subtle_spec.cr",
    "content": "require \"spec\"\nrequire \"crypto/subtle\"\n\ndescribe \"Subtle\" do\n  it \"compares constant times\" do\n    Crypto::Subtle.constant_time_compare(Slice.new(1, 0x11), Slice.new(1, 0x11)).should be_true\n    Crypto::Subtle.constant_time_compare(Slice.new(1, 0x12), Slice.new(1, 0x11)).should be_false\n    Crypto::Subtle.constant_time_compare(Slice.new(1, 0x11), Slice.new(2) { |i| 0x11 + i }).should be_false\n    Crypto::Subtle.constant_time_compare(Slice.new(2) { |i| 0x11 + i }, Slice.new(1, 0x11)).should be_false\n  end\n\n  it \"compares constant time bytes on equality\" do\n    Crypto::Subtle.constant_time_byte_eq(0x00_u8, 0x00_u8).should eq 1_u8\n    Crypto::Subtle.constant_time_byte_eq(0x00_u8, 0x01_u8).should eq 0_u8\n    Crypto::Subtle.constant_time_byte_eq(0x01_u8, 0x00_u8).should eq 0_u8\n    Crypto::Subtle.constant_time_byte_eq(0xff_u8, 0xff_u8).should eq 1_u8\n    Crypto::Subtle.constant_time_byte_eq(0xff_u8, 0xfe_u8).should eq 0_u8\n  end\n\n  it \"compares constant time bytes bug\" do\n    h1 = \"$2a$05$LEC1XBXgXECzKUO2LBDhKOa9lH9zigNKnksVaDwViFNgPU4WkrD53J\"\n    h2 = \"$2a$05$LEC1XBXgXECzKUO2LBDhKOaHlSGFuDDwMuVg6gOzdxQ0xN4rFOwMUn\"\n    Crypto::Subtle.constant_time_compare(h1, h2).should be_false\n  end\n\n  it \"compares constant time and slices strings\" do\n    h1 = \"$2a$05$LEC1XBXgXECzKUO2LBDhKOa9lH9zigNKnksVaDwViFNgPU4WkrD53J\"\n    h2 = \"$2a$05$LEC1XBXgXECzKUO2LBDhKOaHlSGFuDDwMuVg6gOzdxQ0xN4rFOwMUn\"\n\n    slice_result = Crypto::Subtle.constant_time_compare(h1.to_slice, h2.to_slice)\n    Crypto::Subtle.constant_time_compare(h1, h2).should eq(slice_result)\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/ashlti3_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/compiler_rt/shift\"\nrequire \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/ashlti3_test.c\n\nit \".__ashlti3\" do\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 0).should eq make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 1).should eq make_ti(0xFDB97530ECA8642Bu64, 0xFDB97530ECA8642Au64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 2).should eq make_ti(0xFB72EA61D950C857u64, 0xFB72EA61D950C854u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 3).should eq make_ti(0xF6E5D4C3B2A190AFu64, 0xF6E5D4C3B2A190A8u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 4).should eq make_ti(0xEDCBA9876543215Fu64, 0xEDCBA98765432150u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 28).should eq make_ti(0x876543215FEDCBA9u64, 0x8765432150000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 29).should eq make_ti(0x0ECA8642BFDB9753u64, 0x0ECA8642A0000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 30).should eq make_ti(0x1D950C857FB72EA6u64, 0x1D950C8540000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 31).should eq make_ti(0x3B2A190AFF6E5D4Cu64, 0x3B2A190A80000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 32).should eq make_ti(0x76543215FEDCBA98u64, 0x7654321500000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 33).should eq make_ti(0xECA8642BFDB97530u64, 0xECA8642A00000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 34).should eq make_ti(0xD950C857FB72EA61u64, 0xD950C85400000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 35).should eq make_ti(0xB2A190AFF6E5D4C3u64, 0xB2A190A800000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 36).should eq make_ti(0x6543215FEDCBA987u64, 0x6543215000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 60).should eq make_ti(0x5FEDCBA987654321u64, 0x5000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 61).should eq make_ti(0xBFDB97530ECA8642u64, 0xA000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 62).should eq make_ti(0x7FB72EA61D950C85u64, 0x4000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 63).should eq make_ti(0xFF6E5D4C3B2A190Au64, 0x8000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 64).should eq make_ti(0xFEDCBA9876543215u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 65).should eq make_ti(0xFDB97530ECA8642Au64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 66).should eq make_ti(0xFB72EA61D950C854u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 67).should eq make_ti(0xF6E5D4C3B2A190A8u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 68).should eq make_ti(0xEDCBA98765432150u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 92).should eq make_ti(0x8765432150000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 93).should eq make_ti(0x0ECA8642A0000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 94).should eq make_ti(0x1D950C8540000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 95).should eq make_ti(0x3B2A190A80000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 96).should eq make_ti(0x7654321500000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 97).should eq make_ti(0xECA8642A00000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 98).should eq make_ti(0xD950C85400000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 99).should eq make_ti(0xB2A190A800000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 100).should eq make_ti(0x6543215000000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 124).should eq make_ti(0x5000000000000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 125).should eq make_ti(0xA000000000000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 126).should eq make_ti(0x4000000000000000u64, 0x0000000000000000u64)\n  __ashlti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 127).should eq make_ti(0x8000000000000000u64, 0x0000000000000000u64)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/ashrti3_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/compiler_rt/shift\"\nrequire \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/ashrti3_test.c\n\nit \".__ashrti3\" do\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 0).should eq make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 1).should eq make_ti(0xFF6E5D4C3B2A190Au64, 0xFF6E5D4C3B2A190Au64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 2).should eq make_ti(0xFFB72EA61D950C85u64, 0x7FB72EA61D950C85u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 3).should eq make_ti(0xFFDB97530ECA8642u64, 0xBFDB97530ECA8642u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 4).should eq make_ti(0xFFEDCBA987654321u64, 0x5FEDCBA987654321u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 28).should eq make_ti(0xFFFFFFFFEDCBA987u64, 0x6543215FEDCBA987u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 29).should eq make_ti(0xFFFFFFFFF6E5D4C3u64, 0xB2A190AFF6E5D4C3u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 30).should eq make_ti(0xFFFFFFFFFB72EA61u64, 0xD950C857FB72EA61u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 31).should eq make_ti(0xFFFFFFFFFDB97530u64, 0xECA8642BFDB97530u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 32).should eq make_ti(0xFFFFFFFFFEDCBA98u64, 0x76543215FEDCBA98u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 33).should eq make_ti(0xFFFFFFFFFF6E5D4Cu64, 0x3B2A190AFF6E5D4Cu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 34).should eq make_ti(0xFFFFFFFFFFB72EA6u64, 0x1D950C857FB72EA6u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 35).should eq make_ti(0xFFFFFFFFFFDB9753u64, 0x0ECA8642BFDB9753u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 36).should eq make_ti(0xFFFFFFFFFFEDCBA9u64, 0x876543215FEDCBA9u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 60).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xEDCBA9876543215Fu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 61).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xF6E5D4C3B2A190AFu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 62).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFB72EA61D950C857u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 63).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFDB97530ECA8642Bu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 64).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFEDCBA9876543215u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 65).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFF6E5D4C3B2A190Au64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 66).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFB72EA61D950C85u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 67).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFDB97530ECA8642u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 68).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFEDCBA987654321u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 92).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFEDCBA987u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 93).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFF6E5D4C3u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 94).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFB72EA61u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 95).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFDB97530u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 96).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFEDCBA98u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 97).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFF6E5D4Cu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 98).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFB72EA6u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 99).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFDB9753u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 100).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFEDCBA9u64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 124).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 125).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 126).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64)\n  __ashrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 127).should eq make_ti(0xFFFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/divmod128_spec.cr",
    "content": "require \"./spec_helper\"\n\n# Ported from:\n# - https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/umodti3_test.c\n# - https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/udivti3_test.c\n# - https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/modti3_test.c\n# - https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/divti3_test.c\n\nprivate def test__divti3(a : Int128, b : Int128, expected : Int128, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __divti3(a, b)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__modti3(a : Int128, b : Int128, expected : Int128, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __modti3(a, b)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__udivti3(a : UInt128, b : UInt128, expected : UInt128, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __udivti3(a, b)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__umodti3(a : UInt128, b : UInt128, expected : UInt128, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __umodti3(a, b)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\ndescribe \"__divti3\" do\n  test__divti3(0, 1, 0)\n  test__divti3(0, -1, 0)\n  test__divti3(2, 1, 2)\n  test__divti3(2, -1, -2)\n  test__divti3(-2, 1, -2)\n  test__divti3(-2, -1, 2)\n  test__divti3(make_ti(-9223372036854775808, 0x0), 1, make_ti(-9223372036854775808, 0x0))\n  test__divti3(make_ti(-9223372036854775808, 0x0), -1, make_ti(-9223372036854775808, 0x0))\n  test__divti3(make_ti(-9223372036854775808, 0x0), -2, make_ti(0x4000000000000000, 0x0))\n  test__divti3(make_ti(-9223372036854775808, 0x0), 2, make_ti(-0x4000000000000000, 0x0))\nend\n\ndescribe \"__modti3\" do\n  test__modti3(0, 1, 0)\n  test__modti3(0, -1, 0)\n\n  test__modti3(5, 3, 2)\n  test__modti3(5, -3, 2)\n  test__modti3(-5, 3, -2)\n  test__modti3(-5, -3, -2)\n\n  test__modti3(make_ti(-9223372036854775808, 0x0), 1, 0)\n  test__modti3(make_ti(-9223372036854775808, 0x0), -1, 0)\n  test__modti3(make_ti(-9223372036854775808, 0x0), 2, 0)\n  test__modti3(make_ti(-9223372036854775808, 0x0), -2, 0)\n  test__modti3(make_ti(-9223372036854775808, 0x0), 3, -2)\n  test__modti3(make_ti(-9223372036854775808, 0x0), -3, -2)\nend\n\ndescribe \"__udivti3\" do\n  test__udivti3(0, 1, 0)\n  test__udivti3(2, 1, 2)\n\n  test__udivti3(make_tu(0x0, 0x8000000000000000u64), 1, make_tu(0x0, 0x8000000000000000u64))\n  test__udivti3(make_tu(0x0, 0x8000000000000000u64), 2, make_tu(0x0, 0x4000000000000000u64))\n  test__udivti3(make_tu(0xffffffffffffffffu64, 0xffffffffffffffffu64), 2, make_tu(0x7fffffffffffffffu64, 0xffffffffffffffffu64))\nend\n\ndescribe \"__umodti3\" do\n  test__umodti3(0, 1, 0)\n  test__umodti3(2, 1, 0)\n\n  test__umodti3(make_tu(0x0, 0x8000000000000000u64), 1, 0)\n  test__umodti3(make_tu(0x0, 0x8000000000000000u64), 2, 0)\n  test__umodti3(make_tu(0xffffffffffffffffu64, 0xffffffffffffffffu64), 2, 1)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/fixint_spec.cr",
    "content": "require \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/tree/82b74363a943b570c4ee7799d5f3ee4b3e7163a5/compiler-rt/test/builtins/Unit\n\nprivate def test__fixdfti(a : Float64, expected : Int128, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __fixdfti(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__fixsfti(a : Float32, expected : Int128, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __fixsfti(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__fixunsdfti(a : Float64, expected : UInt128, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __fixunsdfti(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__fixunssfti(a : Float32, expected : UInt128, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __fixunssfti(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\ndescribe \"__fixdfti\" do\n  test__fixdfti(0.0, 0)\n\n  test__fixdfti(0.5, 0)\n  test__fixdfti(0.99, 0)\n  test__fixdfti(1.0, 1)\n  test__fixdfti(1.5, 1)\n  test__fixdfti(1.99, 1)\n  test__fixdfti(2.0, 2)\n  test__fixdfti(2.01, 2)\n  test__fixdfti(-0.5, 0)\n  test__fixdfti(-0.99, 0)\n  test__fixdfti(-1.0, -1)\n  test__fixdfti(-1.5, -1)\n  test__fixdfti(-1.99, -1)\n  test__fixdfti(-2.0, -2)\n  test__fixdfti(-2.01, -2)\n\n  test__fixdfti(hexfloat(\"0x1.FFFFFEp+62\"), 0x7FFFFF8000000000_u64)\n  test__fixdfti(hexfloat(\"0x1.FFFFFCp+62\"), 0x7FFFFF0000000000_u64)\n\n  test__fixdfti(hexfloat(\"-0x1.FFFFFEp+62\"), make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000008000000000_u64))\n  test__fixdfti(hexfloat(\"-0x1.FFFFFCp+62\"), make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000010000000000_u64))\n\n  test__fixdfti(hexfloat(\"0x1.FFFFFFFFFFFFFp+62\"), 0x7FFFFFFFFFFFFC00_u64)\n  test__fixdfti(hexfloat(\"0x1.FFFFFFFFFFFFEp+62\"), 0x7FFFFFFFFFFFF800_u64)\n\n  test__fixdfti(hexfloat(\"-0x1.FFFFFFFFFFFFFp+62\"), make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000000000000400_u64))\n  test__fixdfti(hexfloat(\"-0x1.FFFFFFFFFFFFEp+62\"), make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000000000000800_u64))\n\n  test__fixdfti(hexfloat(\"0x1.FFFFFFFFFFFFFp+126\"), make_ti(0x7FFFFFFFFFFFFC00_u64, 0))\n  test__fixdfti(hexfloat(\"0x1.FFFFFFFFFFFFEp+126\"), make_ti(0x7FFFFFFFFFFFF800_u64, 0))\n\n  test__fixdfti(hexfloat(\"-0x1.FFFFFFFFFFFFFp+126\"), make_ti(0x8000000000000400_u64, 0))\n  test__fixdfti(hexfloat(\"-0x1.FFFFFFFFFFFFEp+126\"), make_ti(0x8000000000000800_u64, 0))\nend\n\ndescribe \"__fixsfti\" do\n  test__fixsfti(0.0_f32, 0)\n\n  test__fixsfti(0.5_f32, 0)\n  test__fixsfti(0.99_f32, 0)\n  test__fixsfti(1.0_f32, 1)\n  test__fixsfti(1.5_f32, 1)\n  test__fixsfti(1.99_f32, 1)\n  test__fixsfti(2.0_f32, 2)\n  test__fixsfti(2.01_f32, 2)\n  test__fixsfti(-0.5_f32, 0)\n  test__fixsfti(-0.99_f32, 0)\n  test__fixsfti(-1.0_f32, -1)\n  test__fixsfti(-1.5_f32, -1)\n  test__fixsfti(-1.99_f32, -1)\n  test__fixsfti(-2.0_f32, -2)\n  test__fixsfti(-2.01_f32, -2)\n\n  test__fixsfti(hexfloat(\"0x1.FFFFFEp+62_f32\"), 0x7FFFFF8000000000_u64)\n  test__fixsfti(hexfloat(\"0x1.FFFFFCp+62_f32\"), 0x7FFFFF0000000000_u64)\n\n  test__fixsfti(hexfloat(\"-0x1.FFFFFEp+62_f32\"), make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000008000000000_u64))\n  test__fixsfti(hexfloat(\"-0x1.FFFFFCp+62_f32\"), make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000010000000000_u64))\n\n  test__fixsfti(hexfloat(\"0x1.FFFFFEp+126_f32\"), make_ti(0x7FFFFF8000000000_u64, 0))\n  test__fixsfti(hexfloat(\"0x1.FFFFFCp+126_f32\"), make_ti(0x7FFFFF0000000000_u64, 0))\n\n  test__fixsfti(hexfloat(\"-0x1.FFFFFEp+126_f32\"), make_ti(0x8000008000000000_u64, 0))\n  test__fixsfti(hexfloat(\"-0x1.FFFFFCp+126_f32\"), make_ti(0x8000010000000000_u64, 0))\nend\n\ndescribe \"__fixunsdfti\" do\n  test__fixunsdfti(0.0, 0)\n\n  test__fixunsdfti(0.5, 0)\n  test__fixunsdfti(0.99, 0)\n  test__fixunsdfti(1.0, 1)\n  test__fixunsdfti(1.5, 1)\n  test__fixunsdfti(1.99, 1)\n  test__fixunsdfti(2.0, 2)\n  test__fixunsdfti(2.01, 2)\n  test__fixunsdfti(-0.5, 0)\n  test__fixunsdfti(-0.99, 0)\n\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFEp+62\"), 0x7FFFFF8000000000_u64)\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFCp+62\"), 0x7FFFFF0000000000_u64)\n\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFFFFFFFFFp+63\"), 0xFFFFFFFFFFFFF800_u64)\n  test__fixunsdfti(hexfloat(\"0x1.0000000000000p+63\"), 0x8000000000000000_u64)\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFFFFFFFFFp+62\"), 0x7FFFFFFFFFFFFC00_u64)\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFFFFFFFFEp+62\"), 0x7FFFFFFFFFFFF800_u64)\n\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFFFFFFFFFp+127\"), make_tu(0xFFFFFFFFFFFFF800_u64, 0))\n  test__fixunsdfti(hexfloat(\"0x1.0000000000000p+127\"), make_tu(0x8000000000000000_u64, 0))\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFFFFFFFFFp+126\"), make_tu(0x7FFFFFFFFFFFFC00_u64, 0))\n  test__fixunsdfti(hexfloat(\"0x1.FFFFFFFFFFFFEp+126\"), make_tu(0x7FFFFFFFFFFFF800_u64, 0))\n  test__fixunsdfti(hexfloat(\"0x1.0000000000000p+128\"), make_tu(0xFFFFFFFFFFFFFFFF_u64, 0xFFFFFFFFFFFFFFFF_u64))\nend\n\ndescribe \"__fixunssfti\" do\n  test__fixunssfti(0.0_f32, 0)\n\n  test__fixunssfti(0.5_f32, 0)\n  test__fixunssfti(0.99_f32, 0)\n  test__fixunssfti(1.0_f32, 1)\n  test__fixunssfti(1.5_f32, 1)\n  test__fixunssfti(1.99_f32, 1)\n  test__fixunssfti(2.0_f32, 2)\n  test__fixunssfti(2.01_f32, 2)\n  test__fixunssfti(-0.5_f32, 0)\n  test__fixunssfti(-0.99_f32, 0)\n\n  test__fixunssfti(hexfloat(\"0x1.FFFFFEp+63_f32\"), 0xFFFFFF0000000000_u64)\n  test__fixunssfti(hexfloat(\"0x1.000000p+63_f32\"), 0x8000000000000000_u64)\n  test__fixunssfti(hexfloat(\"0x1.FFFFFEp+62_f32\"), 0x7FFFFF8000000000_u64)\n  test__fixunssfti(hexfloat(\"0x1.FFFFFCp+62_f32\"), 0x7FFFFF0000000000_u64)\n\n  test__fixunssfti(hexfloat(\"0x1.FFFFFEp+127_f32\"), make_tu(0xFFFFFF0000000000_u64, 0))\n  test__fixunssfti(hexfloat(\"0x1.000000p+127_f32\"), make_tu(0x8000000000000000_u64, 0))\n  test__fixunssfti(hexfloat(\"0x1.FFFFFEp+126_f32\"), make_tu(0x7FFFFF8000000000_u64, 0))\n  test__fixunssfti(hexfloat(\"0x1.FFFFFCp+126_f32\"), make_tu(0x7FFFFF0000000000_u64, 0))\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/float_spec.cr",
    "content": "require \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/tree/82b74363a943b570c4ee7799d5f3ee4b3e7163a5/compiler-rt/test/builtins/Unit\n\nprivate def test__floattidf(a : Int128, expected : Float64, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __floattidf(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__floattisf(a : Int128, expected : Float32, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __floattisf(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__floatuntidf(a : UInt128, expected : Float64, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __floatuntidf(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\nprivate def test__floatuntisf(a : UInt128, expected : Float32, *, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual = __floatuntisf(a)\n    actual.should eq(expected), file: file, line: line\n  end\nend\n\ndescribe \"__floattidf\" do\n  test__floattidf(0, 0.0)\n  test__floattidf(1, 1.0)\n  test__floattidf(2, 2.0)\n  test__floattidf(20, 20.0)\n  test__floattidf(-1, -1.0)\n  test__floattidf(-2, -2.0)\n  test__floattidf(-20, -20.0)\n\n  test__floattidf(0x7FFFFF8000000000_u64, hexfloat(\"0x1.FFFFFEp+62\"))\n  test__floattidf(0x7FFFFFFFFFFFF800_u64, hexfloat(\"0x1.FFFFFFFFFFFFEp+62\"))\n  test__floattidf(0x7FFFFF0000000000_u64, hexfloat(\"0x1.FFFFFCp+62\"))\n  test__floattidf(0x7FFFFFFFFFFFF000_u64, hexfloat(\"0x1.FFFFFFFFFFFFCp+62\"))\n\n  test__floattidf(make_ti(0x8000008000000000_u64, 0), -hexfloat(\"0x1.FFFFFEp+126\"))\n  test__floattidf(make_ti(0x8000000000000800_u64, 0), -hexfloat(\"0x1.FFFFFFFFFFFFEp+126\"))\n  test__floattidf(make_ti(0x8000010000000000_u64, 0), -hexfloat(\"0x1.FFFFFCp+126\"))\n  test__floattidf(make_ti(0x8000000000001000_u64, 0), -hexfloat(\"0x1.FFFFFFFFFFFFCp+126\"))\n\n  test__floattidf(make_ti(0x8000000000000000_u64, 0), -hexfloat(\"0x1.000000p+127\"))\n  test__floattidf(make_ti(0x8000000000000001_u64, 0), -hexfloat(\"0x1.000000p+127\"))\n\n  test__floattidf(0x0007FB72E8000000_u64, hexfloat(\"0x1.FEDCBAp+50\"))\n\n  test__floattidf(0x0007FB72EA000000_u64, hexfloat(\"0x1.FEDCBA8p+50\"))\n  test__floattidf(0x0007FB72EB000000_u64, hexfloat(\"0x1.FEDCBACp+50\"))\n  test__floattidf(0x0007FB72EBFFFFFF_u64, hexfloat(\"0x1.FEDCBAFFFFFFCp+50\"))\n  test__floattidf(0x0007FB72EC000000_u64, hexfloat(\"0x1.FEDCBBp+50\"))\n  test__floattidf(0x0007FB72E8000001_u64, hexfloat(\"0x1.FEDCBA0000004p+50\"))\n\n  test__floattidf(0x0007FB72E6000000_u64, hexfloat(\"0x1.FEDCB98p+50\"))\n  test__floattidf(0x0007FB72E7000000_u64, hexfloat(\"0x1.FEDCB9Cp+50\"))\n  test__floattidf(0x0007FB72E7FFFFFF_u64, hexfloat(\"0x1.FEDCB9FFFFFFCp+50\"))\n  test__floattidf(0x0007FB72E4000001_u64, hexfloat(\"0x1.FEDCB90000004p+50\"))\n  test__floattidf(0x0007FB72E4000000_u64, hexfloat(\"0x1.FEDCB9p+50\"))\n\n  test__floattidf(0x023479FD0E092DC0_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DA1_u64, hexfloat(\"0x1.1A3CFE870496Dp+57\"))\n  test__floattidf(0x023479FD0E092DB0_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DB8_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DB6_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DBF_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DC1_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DC7_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DC8_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DCF_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DD0_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floattidf(0x023479FD0E092DD1_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n  test__floattidf(0x023479FD0E092DD8_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n  test__floattidf(0x023479FD0E092DDF_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n  test__floattidf(0x023479FD0E092DE0_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n\n  test__floattidf(make_ti(0x023479FD0E092DC0_u64, 0), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DA1_u64, 1), hexfloat(\"0x1.1A3CFE870496Dp+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DB0_u64, 2), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DB8_u64, 3), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DB6_u64, 4), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DBF_u64, 5), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DC1_u64, 6), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DC7_u64, 7), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DC8_u64, 8), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DCF_u64, 9), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DD0_u64, 0), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DD1_u64, 11), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DD8_u64, 12), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DDF_u64, 13), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\n  test__floattidf(make_ti(0x023479FD0E092DE0_u64, 14), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\nend\n\ndescribe \"__floattisf\" do\n  test__floattisf(0, 0.0_f32)\n\n  test__floattisf(1, 1.0_f32)\n  test__floattisf(2, 2.0_f32)\n  test__floattisf(-1, -1.0_f32)\n  test__floattisf(-2, -2.0_f32)\n\n  test__floattisf(0x7FFFFF8000000000_u64, hexfloat(\"0x1.FFFFFEp+62_f32\"))\n  test__floattisf(0x7FFFFF0000000000_u64, hexfloat(\"0x1.FFFFFCp+62_f32\"))\n\n  test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000008000000000_u64), hexfloat(\"-0x1.FFFFFEp+62_f32\"))\n  test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000010000000000_u64), hexfloat(\"-0x1.FFFFFCp+62_f32\"))\n\n  test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000000000000000_u64), hexfloat(\"-0x1.000000p+63_f32\"))\n  test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF_u64, 0x8000000000000001_u64), hexfloat(\"-0x1.000000p+63_f32\"))\n\n  test__floattisf(0x0007FB72E8000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n\n  test__floattisf(0x0007FB72EA000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72EB000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72EBFFFFFF_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72EC000000_u64, hexfloat(\"0x1.FEDCBCp+50_f32\"))\n  test__floattisf(0x0007FB72E8000001_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n\n  test__floattisf(0x0007FB72E6000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72E7000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72E7FFFFFF_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72E4000001_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floattisf(0x0007FB72E4000000_u64, hexfloat(\"0x1.FEDCB8p+50_f32\"))\n\n  test__floattisf(make_ti(0x0007FB72E8000000_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n\n  test__floattisf(make_ti(0x0007FB72EA000000_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72EB000000_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72EBFFFFFF_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72EC000000_u64, 0), hexfloat(\"0x1.FEDCBCp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72E8000001_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n\n  test__floattisf(make_ti(0x0007FB72E6000000_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72E7000000_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72E7FFFFFF_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72E4000001_u64, 0), hexfloat(\"0x1.FEDCBAp+114_f32\"))\n  test__floattisf(make_ti(0x0007FB72E4000000_u64, 0), hexfloat(\"0x1.FEDCB8p+114_f32\"))\nend\n\ndescribe \"__floatuntidf\" do\n  test__floatuntidf(0, 0.0)\n\n  test__floatuntidf(1, 1.0)\n  test__floatuntidf(2, 2.0)\n  test__floatuntidf(20, 20.0)\n\n  test__floatuntidf(0x7FFFFF8000000000_u64, hexfloat(\"0x1.FFFFFEp+62\"))\n  test__floatuntidf(0x7FFFFFFFFFFFF800_u64, hexfloat(\"0x1.FFFFFFFFFFFFEp+62\"))\n  test__floatuntidf(0x7FFFFF0000000000_u64, hexfloat(\"0x1.FFFFFCp+62\"))\n  test__floatuntidf(0x7FFFFFFFFFFFF000_u64, hexfloat(\"0x1.FFFFFFFFFFFFCp+62\"))\n\n  test__floatuntidf(make_tu(0x8000008000000000_u64, 0), hexfloat(\"0x1.000001p+127\"))\n  test__floatuntidf(make_tu(0x8000000000000800_u64, 0), hexfloat(\"0x1.0000000000001p+127\"))\n  test__floatuntidf(make_tu(0x8000010000000000_u64, 0), hexfloat(\"0x1.000002p+127\"))\n  test__floatuntidf(make_tu(0x8000000000001000_u64, 0), hexfloat(\"0x1.0000000000002p+127\"))\n\n  test__floatuntidf(make_tu(0x8000000000000000_u64, 0), hexfloat(\"0x1.000000p+127\"))\n  test__floatuntidf(make_tu(0x8000000000000001_u64, 0), hexfloat(\"0x1.0000000000000002p+127\"))\n\n  test__floatuntidf(0x0007FB72E8000000_u64, hexfloat(\"0x1.FEDCBAp+50\"))\n\n  test__floatuntidf(0x0007FB72EA000000_u64, hexfloat(\"0x1.FEDCBA8p+50\"))\n  test__floatuntidf(0x0007FB72EB000000_u64, hexfloat(\"0x1.FEDCBACp+50\"))\n  test__floatuntidf(0x0007FB72EBFFFFFF_u64, hexfloat(\"0x1.FEDCBAFFFFFFCp+50\"))\n  test__floatuntidf(0x0007FB72EC000000_u64, hexfloat(\"0x1.FEDCBBp+50\"))\n  test__floatuntidf(0x0007FB72E8000001_u64, hexfloat(\"0x1.FEDCBA0000004p+50\"))\n\n  test__floatuntidf(0x0007FB72E6000000_u64, hexfloat(\"0x1.FEDCB98p+50\"))\n  test__floatuntidf(0x0007FB72E7000000_u64, hexfloat(\"0x1.FEDCB9Cp+50\"))\n  test__floatuntidf(0x0007FB72E7FFFFFF_u64, hexfloat(\"0x1.FEDCB9FFFFFFCp+50\"))\n  test__floatuntidf(0x0007FB72E4000001_u64, hexfloat(\"0x1.FEDCB90000004p+50\"))\n  test__floatuntidf(0x0007FB72E4000000_u64, hexfloat(\"0x1.FEDCB9p+50\"))\n\n  test__floatuntidf(0x023479FD0E092DC0_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DA1_u64, hexfloat(\"0x1.1A3CFE870496Dp+57\"))\n  test__floatuntidf(0x023479FD0E092DB0_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DB8_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DB6_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DBF_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DC1_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DC7_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DC8_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DCF_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DD0_u64, hexfloat(\"0x1.1A3CFE870496Ep+57\"))\n  test__floatuntidf(0x023479FD0E092DD1_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n  test__floatuntidf(0x023479FD0E092DD8_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n  test__floatuntidf(0x023479FD0E092DDF_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n  test__floatuntidf(0x023479FD0E092DE0_u64, hexfloat(\"0x1.1A3CFE870496Fp+57\"))\n\n  test__floatuntidf(make_tu(0x023479FD0E092DC0_u64, 0), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DA1_u64, 1), hexfloat(\"0x1.1A3CFE870496Dp+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DB0_u64, 2), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DB8_u64, 3), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DB6_u64, 4), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DBF_u64, 5), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DC1_u64, 6), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DC7_u64, 7), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DC8_u64, 8), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DCF_u64, 9), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DD0_u64, 0), hexfloat(\"0x1.1A3CFE870496Ep+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DD1_u64, 11), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DD8_u64, 12), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DDF_u64, 13), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\n  test__floatuntidf(make_tu(0x023479FD0E092DE0_u64, 14), hexfloat(\"0x1.1A3CFE870496Fp+121\"))\nend\n\ndescribe \"__floatuntisf\" do\n  test__floatuntisf(0, 0.0_f32)\n\n  test__floatuntisf(1, 1.0_f32)\n  test__floatuntisf(2, 2.0_f32)\n  test__floatuntisf(20, 20.0_f32)\n\n  test__floatuntisf(0x7FFFFF8000000000_u64, hexfloat(\"0x1.FFFFFEp+62_f32\"))\n  test__floatuntisf(0x7FFFFF0000000000_u64, hexfloat(\"0x1.FFFFFCp+62_f32\"))\n\n  test__floatuntisf(make_tu(0x8000008000000000_u64, 0), hexfloat(\"0x1.000000p+127_f32\")) # inexact hexfloat changed\n  test__floatuntisf(make_tu(0x8000000000000800_u64, 0), hexfloat(\"0x1.0p+127_f32\"))\n  test__floatuntisf(make_tu(0x8000010000000000_u64, 0), hexfloat(\"0x1.000002p+127_f32\"))\n\n  test__floatuntisf(make_tu(0x8000000000000000_u64, 0), hexfloat(\"0x1.000000p+127_f32\"))\n\n  test__floatuntisf(0x0007FB72E8000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n\n  test__floatuntisf(0x0007FB72EA000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\")) # inexact hexfloat changed\n  test__floatuntisf(0x0007FB72EB000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\")) # inexact hexfloat changed\n\n  test__floatuntisf(0x0007FB72EC000000_u64, hexfloat(\"0x1.FEDCBCp+50_f32\")) # inexact hexfloat changed\n\n  test__floatuntisf(0x0007FB72E6000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\")) # inexact hexfloat changed\n  test__floatuntisf(0x0007FB72E7000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\")) # inexact hexfloat changed\n  test__floatuntisf(0x0007FB72E4000000_u64, hexfloat(\"0x1.FEDCB8p+50_f32\")) # inexact hexfloat changed\n\n  test__floatuntisf(0xFFFFFFFFFFFFFFFE_u64, hexfloat(\"0x1p+64_f32\"))\n  test__floatuntisf(0xFFFFFFFFFFFFFFFF_u64, hexfloat(\"0x1p+64_f32\"))\n\n  test__floatuntisf(0x0007FB72E8000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n\n  test__floatuntisf(0x0007FB72EA000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72EB000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72EBFFFFFF_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72EC000000_u64, hexfloat(\"0x1.FEDCBCp+50_f32\"))\n  test__floatuntisf(0x0007FB72E8000001_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n\n  test__floatuntisf(0x0007FB72E6000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72E7000000_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72E7FFFFFF_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72E4000001_u64, hexfloat(\"0x1.FEDCBAp+50_f32\"))\n  test__floatuntisf(0x0007FB72E4000000_u64, hexfloat(\"0x1.FEDCB8p+50_f32\"))\n\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCB90000000000001_u64), hexfloat(\"0x1.FEDCBAp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBA0000000000000_u64), hexfloat(\"0x1.FEDCBAp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBAFFFFFFFFFFFFF_u64), hexfloat(\"0x1.FEDCBAp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBB0000000000000_u64), hexfloat(\"0x1.FEDCBCp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBB0000000000001_u64), hexfloat(\"0x1.FEDCBCp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBBFFFFFFFFFFFFF_u64), hexfloat(\"0x1.FEDCBCp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBC0000000000000_u64), hexfloat(\"0x1.FEDCBCp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBC0000000000001_u64), hexfloat(\"0x1.FEDCBCp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBD0000000000000_u64), hexfloat(\"0x1.FEDCBCp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBD0000000000001_u64), hexfloat(\"0x1.FEDCBEp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBDFFFFFFFFFFFFF_u64), hexfloat(\"0x1.FEDCBEp+76_f32\"))\n  test__floatuntisf(make_tu(0x0000000000001FED_u64, 0xCBE0000000000000_u64), hexfloat(\"0x1.FEDCBEp+76_f32\"))\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/lshrti3_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/compiler_rt/shift\"\nrequire \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/lshrti3_test.c\n\nit \".__lshrti3\" do\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 0).should eq make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 1).should eq make_ti(0x7F6E5D4C3B2A190Au64, 0xFF6E5D4C3B2A190Au64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 2).should eq make_ti(0x3FB72EA61D950C85u64, 0x7FB72EA61D950C85u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 3).should eq make_ti(0x1FDB97530ECA8642u64, 0xBFDB97530ECA8642u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 4).should eq make_ti(0x0FEDCBA987654321u64, 0x5FEDCBA987654321u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 28).should eq make_ti(0x0000000FEDCBA987u64, 0x6543215FEDCBA987u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 29).should eq make_ti(0x00000007F6E5D4C3u64, 0xB2A190AFF6E5D4C3u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 30).should eq make_ti(0x00000003FB72EA61u64, 0xD950C857FB72EA61u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 31).should eq make_ti(0x00000001FDB97530u64, 0xECA8642BFDB97530u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 32).should eq make_ti(0x00000000FEDCBA98u64, 0x76543215FEDCBA98u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 33).should eq make_ti(0x000000007F6E5D4Cu64, 0x3B2A190AFF6E5D4Cu64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 34).should eq make_ti(0x000000003FB72EA6u64, 0x1D950C857FB72EA6u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 35).should eq make_ti(0x000000001FDB9753u64, 0x0ECA8642BFDB9753u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 36).should eq make_ti(0x000000000FEDCBA9u64, 0x876543215FEDCBA9u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 60).should eq make_ti(0x000000000000000Fu64, 0xEDCBA9876543215Fu64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 61).should eq make_ti(0x0000000000000007u64, 0xF6E5D4C3B2A190AFu64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 62).should eq make_ti(0x0000000000000003u64, 0xFB72EA61D950C857u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 63).should eq make_ti(0x0000000000000001u64, 0xFDB97530ECA8642Bu64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 64).should eq make_ti(0x0000000000000000u64, 0xFEDCBA9876543215u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 65).should eq make_ti(0x0000000000000000u64, 0x7F6E5D4C3B2A190Au64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 66).should eq make_ti(0x0000000000000000u64, 0x3FB72EA61D950C85u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 67).should eq make_ti(0x0000000000000000u64, 0x1FDB97530ECA8642u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 68).should eq make_ti(0x0000000000000000u64, 0x0FEDCBA987654321u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 92).should eq make_ti(0x0000000000000000u64, 0x0000000FEDCBA987u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 93).should eq make_ti(0x0000000000000000u64, 0x00000007F6E5D4C3u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 94).should eq make_ti(0x0000000000000000u64, 0x00000003FB72EA61u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 95).should eq make_ti(0x0000000000000000u64, 0x00000001FDB97530u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 96).should eq make_ti(0x0000000000000000u64, 0x00000000FEDCBA98u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 97).should eq make_ti(0x0000000000000000u64, 0x000000007F6E5D4Cu64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 98).should eq make_ti(0x0000000000000000u64, 0x000000003FB72EA6u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 99).should eq make_ti(0x0000000000000000u64, 0x000000001FDB9753u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 100).should eq make_ti(0x0000000000000000u64, 0x000000000FEDCBA9u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 124).should eq make_ti(0x0000000000000000u64, 0x000000000000000Fu64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 125).should eq make_ti(0x0000000000000000u64, 0x0000000000000007u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 126).should eq make_ti(0x0000000000000000u64, 0x0000000000000003u64)\n  __lshrti3(make_ti(0xFEDCBA9876543215u64, 0xFEDCBA9876543215u64), 127).should eq make_ti(0x0000000000000000u64, 0x0000000000000001u64)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/mulodi4_spec.cr",
    "content": "require \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/mulodi4_test.c\n\nprivate def test__mulodi4(a : Int64, b : Int64, expected : Int64, expected_overflow : Int32, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual_overflow : Int32 = 0\n    actual = __mulodi4(a, b, pointerof(actual_overflow))\n    actual_overflow.should eq(expected_overflow), file: file, line: line\n    if !expected_overflow\n      actual.should eq(expected), file: file, line: line\n    end\n  end\nend\n\nprivate HEX_0_7FFFFFFFFFFFFFFF = 0x7FFFFFFFFFFFFFFFi64\nprivate HEX_0_8000000000000001 = 0x8000000000000001u64.to_i64!\nprivate HEX_0_8000000000000000 = 0x8000000000000000u64.to_i64!\n\ndescribe \"__mulodi4\" do\n  test__mulodi4(0, 0, 0, 0)\n  test__mulodi4(0, 1, 0, 0)\n  test__mulodi4(1, 0, 0, 0)\n  test__mulodi4(0, 10, 0, 0)\n  test__mulodi4(10, 0, 0, 0)\n  test__mulodi4(0, 81985529216486895, 0, 0)\n  test__mulodi4(81985529216486895, 0, 0, 0)\n  test__mulodi4(0, -1, 0, 0)\n  test__mulodi4(-1, 0, 0, 0)\n  test__mulodi4(0, -10, 0, 0)\n  test__mulodi4(-10, 0, 0, 0)\n  test__mulodi4(0, -81985529216486895, 0, 0)\n  test__mulodi4(-81985529216486895, 0, 0, 0)\n  test__mulodi4(1, 1, 1, 0)\n  test__mulodi4(1, 10, 10, 0)\n  test__mulodi4(10, 1, 10, 0)\n  test__mulodi4(1, 81985529216486895, 81985529216486895, 0)\n  test__mulodi4(81985529216486895, 1, 81985529216486895, 0)\n  test__mulodi4(1, -1, -1, 0)\n  test__mulodi4(1, -10, -10, 0)\n  test__mulodi4(-10, 1, -10, 0)\n  test__mulodi4(1, -81985529216486895, -81985529216486895, 0)\n  test__mulodi4(-81985529216486895, 1, -81985529216486895, 0)\n  test__mulodi4(3037000499, 3037000499, 9223372030926249001, 0)\n  test__mulodi4(-3037000499, 3037000499, -9223372030926249001, 0)\n  test__mulodi4(3037000499, -3037000499, -9223372030926249001, 0)\n  test__mulodi4(-3037000499, -3037000499, 9223372030926249001, 0)\n  test__mulodi4(4398046511103, 2097152, 9223372036852678656, 0)\n  test__mulodi4(-4398046511103, 2097152, -9223372036852678656, 0)\n  test__mulodi4(4398046511103, -2097152, -9223372036852678656, 0)\n  test__mulodi4(-4398046511103, -2097152, 9223372036852678656, 0)\n  test__mulodi4(2097152, 4398046511103, 9223372036852678656, 0)\n  test__mulodi4(-2097152, 4398046511103, -9223372036852678656, 0)\n  test__mulodi4(2097152, -4398046511103, -9223372036852678656, 0)\n  test__mulodi4(-2097152, -4398046511103, 9223372036852678656, 0)\n  test__mulodi4(HEX_0_7FFFFFFFFFFFFFFF, -2, 2, 1)\n  test__mulodi4(-2, HEX_0_7FFFFFFFFFFFFFFF, 2, 1)\n  test__mulodi4(HEX_0_7FFFFFFFFFFFFFFF, -1, HEX_0_8000000000000001, 0)\n  test__mulodi4(-1, HEX_0_7FFFFFFFFFFFFFFF, HEX_0_8000000000000001, 0)\n  test__mulodi4(HEX_0_7FFFFFFFFFFFFFFF, 0, 0, 0)\n  test__mulodi4(0, HEX_0_7FFFFFFFFFFFFFFF, 0, 0)\n  test__mulodi4(HEX_0_7FFFFFFFFFFFFFFF, 1, HEX_0_7FFFFFFFFFFFFFFF, 0)\n  test__mulodi4(1, HEX_0_7FFFFFFFFFFFFFFF, HEX_0_7FFFFFFFFFFFFFFF, 0)\n  test__mulodi4(HEX_0_7FFFFFFFFFFFFFFF, 2, HEX_0_8000000000000001, 1)\n  test__mulodi4(2, HEX_0_7FFFFFFFFFFFFFFF, HEX_0_8000000000000001, 1)\n  test__mulodi4(HEX_0_8000000000000000, -2, HEX_0_8000000000000000, 1)\n  test__mulodi4(-2, HEX_0_8000000000000000, HEX_0_8000000000000000, 1)\n  test__mulodi4(HEX_0_8000000000000000, -1, HEX_0_8000000000000000, 1)\n  test__mulodi4(-1, HEX_0_8000000000000000, HEX_0_8000000000000000, 1)\n  test__mulodi4(HEX_0_8000000000000000, 0, 0, 0)\n  test__mulodi4(0, HEX_0_8000000000000000, 0, 0)\n  test__mulodi4(HEX_0_8000000000000000, 1, HEX_0_8000000000000000, 0)\n  test__mulodi4(1, HEX_0_8000000000000000, HEX_0_8000000000000000, 0)\n  test__mulodi4(HEX_0_8000000000000000, 2, HEX_0_8000000000000000, 1)\n  test__mulodi4(2, HEX_0_8000000000000000, HEX_0_8000000000000000, 1)\n  test__mulodi4(HEX_0_8000000000000001, -2, HEX_0_8000000000000001, 1)\n  test__mulodi4(-2, HEX_0_8000000000000001, HEX_0_8000000000000001, 1)\n  test__mulodi4(HEX_0_8000000000000001, -1, HEX_0_7FFFFFFFFFFFFFFF, 0)\n  test__mulodi4(-1, HEX_0_8000000000000001, HEX_0_7FFFFFFFFFFFFFFF, 0)\n  test__mulodi4(HEX_0_8000000000000001, 0, 0, 0)\n  test__mulodi4(0, HEX_0_8000000000000001, 0, 0)\n  test__mulodi4(HEX_0_8000000000000001, 1, HEX_0_8000000000000001, 0)\n  test__mulodi4(1, HEX_0_8000000000000001, HEX_0_8000000000000001, 0)\n  test__mulodi4(HEX_0_8000000000000001, 2, HEX_0_8000000000000000, 1)\n  test__mulodi4(2, HEX_0_8000000000000001, HEX_0_8000000000000000, 1)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/mulosi4_spec.cr",
    "content": "require \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/mulosi4_test.c\n\nprivate def test__mulosi4(a : Int32, b : Int32, expected : Int32, expected_overflow : Int32, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual_overflow : Int32 = 0\n    actual = __mulosi4(a, b, pointerof(actual_overflow))\n    actual_overflow.should eq(expected_overflow), file: file, line: line\n    if !expected_overflow\n      actual.should eq(expected), file: file, line: line\n    end\n  end\nend\n\ndescribe \"__mulosi4\" do\n  test__mulosi4(0, 0, 0, 0)\n  test__mulosi4(0, 1, 0, 0)\n  test__mulosi4(1, 0, 0, 0)\n  test__mulosi4(0, 10, 0, 0)\n  test__mulosi4(10, 0, 0, 0)\n  test__mulosi4(0, 0x1234567, 0, 0)\n  test__mulosi4(0x1234567, 0, 0, 0)\n\n  test__mulosi4(0, -1, 0, 0)\n  test__mulosi4(-1, 0, 0, 0)\n  test__mulosi4(0, -10, 0, 0)\n  test__mulosi4(-10, 0, 0, 0)\n  test__mulosi4(0, 0x1234567, 0, 0)\n  test__mulosi4(0x1234567, 0, 0, 0)\n\n  test__mulosi4(1, 1, 1, 0)\n  test__mulosi4(1, 10, 10, 0)\n  test__mulosi4(10, 1, 10, 0)\n  test__mulosi4(1, 0x1234567, 0x1234567, 0)\n  test__mulosi4(0x1234567, 1, 0x1234567, 0)\n\n  test__mulosi4(1, -1, -1, 0)\n  test__mulosi4(1, -10, -10, 0)\n  test__mulosi4(-10, 1, -10, 0)\n  test__mulosi4(1, -0x1234567, -0x1234567, 0)\n  test__mulosi4(-0x1234567, 1, -0x1234567, 0)\n\n  test__mulosi4(0x7FFFFFFF, -2, -0x7fffffff, 1)\n  test__mulosi4(-2, 0x7FFFFFFF, -0x7fffffff, 1)\n  test__mulosi4(0x7FFFFFFF, -1, -0x7fffffff, 0)\n  test__mulosi4(-1, 0x7FFFFFFF, -0x7fffffff, 0)\n  test__mulosi4(0x7FFFFFFF, 0, 0, 0)\n  test__mulosi4(0, 0x7FFFFFFF, 0, 0)\n  test__mulosi4(0x7FFFFFFF, 1, 0x7FFFFFFF, 0)\n  test__mulosi4(1, 0x7FFFFFFF, 0x7FFFFFFF, 0)\n  test__mulosi4(0x7FFFFFFF, 2, -0x7fffffff, 1)\n  test__mulosi4(2, 0x7FFFFFFF, -0x7fffffff, 1)\n\n  test__mulosi4(-0x80000000, -2, -0x80000000, 1)\n  test__mulosi4(-2, -0x80000000, -0x80000000, 1)\n  test__mulosi4(-0x80000000, -1, -0x80000000, 1)\n  test__mulosi4(-1, -0x80000000, -0x80000000, 1)\n  test__mulosi4(-0x80000000, 0, 0, 0)\n  test__mulosi4(0, -0x80000000, 0, 0)\n  test__mulosi4(-0x80000000, 1, -0x80000000, 0)\n  test__mulosi4(1, -0x80000000, -0x80000000, 0)\n  test__mulosi4(-0x80000000, 2, -0x80000000, 1)\n  test__mulosi4(2, -0x80000000, -0x80000000, 1)\n\n  test__mulosi4(-0x7fffffff, -2, -0x7fffffff, 1)\n  test__mulosi4(-2, -0x7fffffff, -0x7fffffff, 1)\n  test__mulosi4(-0x7fffffff, -1, 0x7FFFFFFF, 0)\n  test__mulosi4(-1, -0x7fffffff, 0x7FFFFFFF, 0)\n  test__mulosi4(-0x7fffffff, 0, 0, 0)\n  test__mulosi4(0, -0x7fffffff, 0, 0)\n  test__mulosi4(-0x7fffffff, 1, -0x7fffffff, 0)\n  test__mulosi4(1, -0x7fffffff, -0x7fffffff, 0)\n  test__mulosi4(-0x7fffffff, 2, -0x80000000, 1)\n  test__mulosi4(2, -0x7fffffff, -0x80000000, 1)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/muloti4_spec.cr",
    "content": "require \"./spec_helper\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/muloti4_test.c\n\nprivate def test__muloti4(a : Int128, b : Int128, expected : Int128, expected_overflow : Int32, file = __FILE__, line = __LINE__)\n  it \"passes compiler-rt builtins unit tests\" do\n    actual_overflow : Int32 = 0\n    actual = __muloti4(a, b, pointerof(actual_overflow))\n    actual_overflow.should eq(expected_overflow), file: file, line: line\n    if !expected_overflow\n      actual.should eq(expected), file: file, line: line\n    end\n  end\nend\n\ndescribe \"__muloti4\" do\n  test__muloti4(0, 0, 0, 0)\n  test__muloti4(0, 1, 0, 0)\n  test__muloti4(1, 0, 0, 0)\n  test__muloti4(0, 10, 0, 0)\n  test__muloti4(10, 0, 0, 0)\n  test__muloti4(0, 81985529216486895, 0, 0)\n  test__muloti4(81985529216486895, 0, 0, 0)\n  test__muloti4(0, -1, 0, 0)\n  test__muloti4(-1, 0, 0, 0)\n  test__muloti4(0, -10, 0, 0)\n  test__muloti4(-10, 0, 0, 0)\n  test__muloti4(0, -81985529216486895, 0, 0)\n  test__muloti4(-81985529216486895, 0, 0, 0)\n  test__muloti4(1, 1, 1, 0)\n  test__muloti4(1, 10, 10, 0)\n  test__muloti4(10, 1, 10, 0)\n  test__muloti4(1, 81985529216486895, 81985529216486895, 0)\n  test__muloti4(81985529216486895, 1, 81985529216486895, 0)\n  test__muloti4(1, -1, -1, 0)\n  test__muloti4(1, -10, -10, 0)\n  test__muloti4(-10, 1, -10, 0)\n  test__muloti4(1, -81985529216486895, -81985529216486895, 0)\n  test__muloti4(-81985529216486895, 1, -81985529216486895, 0)\n  test__muloti4(3037000499, 3037000499, 9223372030926249001, 0)\n  test__muloti4(-3037000499, 3037000499, -9223372030926249001, 0)\n  test__muloti4(3037000499, -3037000499, -9223372030926249001, 0)\n  test__muloti4(-3037000499, -3037000499, 9223372030926249001, 0)\n  test__muloti4(4398046511103, 2097152, 9223372036852678656, 0)\n  test__muloti4(-4398046511103, 2097152, -9223372036852678656, 0)\n  test__muloti4(4398046511103, -2097152, -9223372036852678656, 0)\n  test__muloti4(-4398046511103, -2097152, 9223372036852678656, 0)\n  test__muloti4(2097152, 4398046511103, 9223372036852678656, 0)\n  test__muloti4(-2097152, 4398046511103, -9223372036852678656, 0)\n  test__muloti4(2097152, -4398046511103, -9223372036852678656, 0)\n  test__muloti4(-2097152, -4398046511103, 9223372036852678656, 0)\n  test__muloti4(make_ti(0x00000000000000B5, 0x04F333F9DE5BE000),\n    make_ti(0x0000000000000000u64, 0x00B504F333F9DE5Bu64),\n    make_ti(0x7FFFFFFFFFFFF328u64, 0xDF915DA296E8A000u64), 0)\n  test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    -2,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 1)\n  test__muloti4(-2,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 1)\n  test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    -1,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 0)\n  test__muloti4(-1,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 0)\n  test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    0,\n    0, 0)\n  test__muloti4(0,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    0, 0)\n  test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    1,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64), 0)\n  test__muloti4(1,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64), 0)\n  test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    2,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 1)\n  test__muloti4(2,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64),\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 1)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    -2,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(-2,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    -1,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(-1,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    0,\n    0, 0)\n  test__muloti4(0,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    0, 0)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    1,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 0)\n  test__muloti4(1,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 0)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    2,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(2,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    -2,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 1)\n  test__muloti4(-2,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 1)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    -1,\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64), 0)\n  test__muloti4(-1,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    make_ti(0x7FFFFFFFFFFFFFFFu64, 0xFFFFFFFFFFFFFFFFu64), 0)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    0,\n    0, 0)\n  test__muloti4(0,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    0, 0)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    1,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 0)\n  test__muloti4(1,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64), 0)\n  test__muloti4(make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    2,\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\n  test__muloti4(2,\n    make_ti(0x8000000000000000u64, 0x0000000000000001u64),\n    make_ti(0x8000000000000000u64, 0x0000000000000000u64), 1)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/multi3_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/compiler_rt/multi3\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/test/builtins/Unit/multi3_test.c\n\n# TODO: Replace helper methods with literals once possible\n\nprivate def make_ti(a : Int128, b : Int128)\n  (a << 64) + b\nend\n\nit \".__multi3\" do\n  __multi3(0, 0).should eq 0\n  __multi3(0, 1).should eq 0\n  __multi3(1, 0).should eq 0\n  __multi3(0, 10).should eq 0\n  __multi3(10, 0).should eq 0\n  __multi3(0, 81985529216486895).should eq 0\n  __multi3(81985529216486895, 0).should eq 0\n  __multi3(0, -1).should eq 0\n  __multi3(-1, 0).should eq 0\n  __multi3(0, -10).should eq 0\n  __multi3(-10, 0).should eq 0\n  __multi3(0, -81985529216486895).should eq 0\n  __multi3(-81985529216486895, 0).should eq 0\n  __multi3(1, 1).should eq 1\n  __multi3(1, 10).should eq 10\n  __multi3(10, 1).should eq 10\n  __multi3(1, 81985529216486895).should eq 81985529216486895\n  __multi3(81985529216486895, 1).should eq 81985529216486895\n  __multi3(1, -1).should eq -1\n  __multi3(1, -10).should eq -10\n  __multi3(-10, 1).should eq -10\n  __multi3(1, -81985529216486895).should eq -81985529216486895\n  __multi3(-81985529216486895, 1).should eq -81985529216486895\n  __multi3(3037000499, 3037000499).should eq 9223372030926249001\n  __multi3(-3037000499, 3037000499).should eq -9223372030926249001\n  __multi3(3037000499, -3037000499).should eq -9223372030926249001\n  __multi3(-3037000499, -3037000499).should eq 9223372030926249001\n  __multi3(4398046511103, 2097152).should eq 9223372036852678656\n  __multi3(-4398046511103, 2097152).should eq -9223372036852678656\n  __multi3(4398046511103, -2097152).should eq -9223372036852678656\n  __multi3(-4398046511103, -2097152).should eq 9223372036852678656\n  __multi3(2097152, 4398046511103).should eq 9223372036852678656\n  __multi3(-2097152, 4398046511103).should eq -9223372036852678656\n  __multi3(2097152, -4398046511103).should eq -9223372036852678656\n  __multi3(-2097152, -4398046511103).should eq 9223372036852678656\n  __multi3(\n    make_ti(0x00000000000000B5, 0x04F333F9DE5BE000),\n    make_ti(0x0000000000000000, 0x00B504F333F9DE5B)\n  ).should eq make_ti(0x7FFFFFFFFFFFF328u64, 0xDF915DA296E8A000u64)\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/powidf2_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/compiler_rt/pow\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/2e9df860468425645dcd1b241c5dbf76c072e314/compiler-rt/test/builtins/Unit/powidf2_test.c\n\nit \".__powidf2\" do\n  __powidf2(0, 0).should eq 1\n  __powidf2(1, 0).should eq 1\n  __powidf2(1.5, 0).should eq 1\n  __powidf2(2, 0).should eq 1\n  __powidf2(Float64::INFINITY, 0).should eq 1\n  __powidf2(-0.0, 0).should eq 1\n  __powidf2(-1, 0).should eq 1\n  __powidf2(-1.5, 0).should eq 1\n  __powidf2(-2, 0).should eq 1\n  __powidf2(-Float64::INFINITY, 0).should eq 1\n  __powidf2(0, 1).should eq 0\n  __powidf2(0, 2).should eq 0\n  __powidf2(0, 3).should eq 0\n  __powidf2(0, 4).should eq 0\n  __powidf2(0, Int32::MAX - 1).should eq 0\n  __powidf2(0, Int32::MAX).should eq 0\n  __powidf2(-0.0, 1).should eq -0.0\n  __powidf2(-0.0, 2).should eq 0\n  __powidf2(-0.0, 3).should eq -0.0\n  __powidf2(-0.0, 4).should eq 0\n  __powidf2(-0.0, Int32::MAX - 1).should eq 0\n  __powidf2(-0.0, Int32::MAX).should eq -0.0\n  __powidf2(1, 1).should eq 1\n  __powidf2(1, 2).should eq 1\n  __powidf2(1, 3).should eq 1\n  __powidf2(1, 4).should eq 1\n  __powidf2(1, Int32::MAX - 1).should eq 1\n  __powidf2(1, Int32::MAX).should eq 1\n  __powidf2(Float64::INFINITY, 1).should eq Float64::INFINITY\n  __powidf2(Float64::INFINITY, 2).should eq Float64::INFINITY\n  __powidf2(Float64::INFINITY, 3).should eq Float64::INFINITY\n  __powidf2(Float64::INFINITY, 4).should eq Float64::INFINITY\n  __powidf2(Float64::INFINITY, Int32::MAX - 1).should eq Float64::INFINITY\n  __powidf2(Float64::INFINITY, Int32::MAX).should eq Float64::INFINITY\n  __powidf2(-Float64::INFINITY, 1).should eq -Float64::INFINITY\n  __powidf2(-Float64::INFINITY, 2).should eq Float64::INFINITY\n  __powidf2(-Float64::INFINITY, 3).should eq -Float64::INFINITY\n  __powidf2(-Float64::INFINITY, 4).should eq Float64::INFINITY\n  __powidf2(-Float64::INFINITY, Int32::MAX - 1).should eq Float64::INFINITY\n  __powidf2(-Float64::INFINITY, Int32::MAX).should eq -Float64::INFINITY\n  __powidf2(0, -1).should eq Float64::INFINITY\n  __powidf2(0, -2).should eq Float64::INFINITY\n  __powidf2(0, -3).should eq Float64::INFINITY\n  __powidf2(0, -4).should eq Float64::INFINITY\n  __powidf2(0, Int32::MIN + 2).should eq Float64::INFINITY\n  __powidf2(0, Int32::MIN + 1).should eq Float64::INFINITY\n  __powidf2(0, Int32::MIN).should eq Float64::INFINITY\n  __powidf2(-0.0, -1).should eq -Float64::INFINITY\n  __powidf2(-0.0, -2).should eq Float64::INFINITY\n  __powidf2(-0.0, -3).should eq -Float64::INFINITY\n  __powidf2(-0.0, -4).should eq Float64::INFINITY\n  __powidf2(-0.0, Int32::MIN + 2).should eq Float64::INFINITY\n  __powidf2(-0.0, Int32::MIN + 1).should eq -Float64::INFINITY\n  __powidf2(-0.0, Int32::MIN).should eq Float64::INFINITY\n  __powidf2(1, -1).should eq 1\n  __powidf2(1, -2).should eq 1\n  __powidf2(1, -3).should eq 1\n  __powidf2(1, -4).should eq 1\n  __powidf2(1, Int32::MIN + 2).should eq 1\n  __powidf2(1, Int32::MIN + 1).should eq 1\n  __powidf2(1, Int32::MIN).should eq 1\n  __powidf2(Float64::INFINITY, -1).should eq 0\n  __powidf2(Float64::INFINITY, -2).should eq 0\n  __powidf2(Float64::INFINITY, -3).should eq 0\n  __powidf2(Float64::INFINITY, -4).should eq 0\n  __powidf2(Float64::INFINITY, Int32::MIN + 2).should eq 0\n  __powidf2(Float64::INFINITY, Int32::MIN + 1).should eq 0\n  __powidf2(Float64::INFINITY, Int32::MIN).should eq 0\n  __powidf2(-Float64::INFINITY, -1).should eq -0.0\n  __powidf2(-Float64::INFINITY, -2).should eq 0\n  __powidf2(-Float64::INFINITY, -3).should eq -0.0\n  __powidf2(-Float64::INFINITY, -4).should eq 0\n  __powidf2(-Float64::INFINITY, Int32::MIN + 2).should eq 0\n  __powidf2(-Float64::INFINITY, Int32::MIN + 1).should eq -0.0\n  __powidf2(-Float64::INFINITY, Int32::MIN).should eq 0\n  __powidf2(2, 10).should eq 1024.0\n  __powidf2(-2, 10).should eq 1024.0\n  __powidf2(2, -10).should eq 1/1024.0\n  __powidf2(-2, -10).should eq 1/1024.0\n  __powidf2(2, 19).should eq 524288.0\n  __powidf2(-2, 19).should eq -524288.0\n  __powidf2(2, -19).should eq 1/524288.0\n  __powidf2(-2, -19).should eq -1/524288.0\n  __powidf2(2, 31).should eq 2147483648.0\n  __powidf2(-2, 31).should eq -2147483648.0\n  __powidf2(2, -31).should eq 1/2147483648.0\n  __powidf2(-2, -31).should eq -1/2147483648.0\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/powisf2_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/compiler_rt/pow\"\n\n# Ported from https://github.com/llvm/llvm-project/blob/2e9df860468425645dcd1b241c5dbf76c072e314/compiler-rt/test/builtins/Unit/powisf2_test.c\n\nit \".__powisf2\" do\n  __powisf2(0, 0).should eq 1\n  __powisf2(1, 0).should eq 1\n  __powisf2(1.5, 0).should eq 1\n  __powisf2(2, 0).should eq 1\n  __powisf2(Float32::INFINITY, 0).should eq 1\n  __powisf2(-0.0, 0).should eq 1\n  __powisf2(-1, 0).should eq 1\n  __powisf2(-1.5, 0).should eq 1\n  __powisf2(-2, 0).should eq 1\n  __powisf2(-Float32::INFINITY, 0).should eq 1\n  __powisf2(0, 1).should eq 0\n  __powisf2(0, 2).should eq 0\n  __powisf2(0, 3).should eq 0\n  __powisf2(0, 4).should eq 0\n  __powisf2(0, Int32::MAX - 1).should eq 0\n  __powisf2(0, Int32::MAX).should eq 0\n  __powisf2(-0.0, 1).should eq -0.0\n  __powisf2(-0.0, 2).should eq 0\n  __powisf2(-0.0, 3).should eq -0.0\n  __powisf2(-0.0, 4).should eq 0\n  __powisf2(-0.0, Int32::MAX - 1).should eq 0\n  __powisf2(-0.0, Int32::MAX).should eq -0.0\n  __powisf2(1, 1).should eq 1\n  __powisf2(1, 2).should eq 1\n  __powisf2(1, 3).should eq 1\n  __powisf2(1, 4).should eq 1\n  __powisf2(1, Int32::MAX - 1).should eq 1\n  __powisf2(1, Int32::MAX).should eq 1\n  __powisf2(Float32::INFINITY, 1).should eq Float32::INFINITY\n  __powisf2(Float32::INFINITY, 2).should eq Float32::INFINITY\n  __powisf2(Float32::INFINITY, 3).should eq Float32::INFINITY\n  __powisf2(Float32::INFINITY, 4).should eq Float32::INFINITY\n  __powisf2(Float32::INFINITY, Int32::MAX - 1).should eq Float32::INFINITY\n  __powisf2(Float32::INFINITY, Int32::MAX).should eq Float32::INFINITY\n  __powisf2(-Float32::INFINITY, 1).should eq -Float32::INFINITY\n  __powisf2(-Float32::INFINITY, 2).should eq Float32::INFINITY\n  __powisf2(-Float32::INFINITY, 3).should eq -Float32::INFINITY\n  __powisf2(-Float32::INFINITY, 4).should eq Float32::INFINITY\n  __powisf2(-Float32::INFINITY, Int32::MAX - 1).should eq Float32::INFINITY\n  __powisf2(-Float32::INFINITY, Int32::MAX).should eq -Float32::INFINITY\n  __powisf2(0, -1).should eq Float32::INFINITY\n  __powisf2(0, -2).should eq Float32::INFINITY\n  __powisf2(0, -3).should eq Float32::INFINITY\n  __powisf2(0, -4).should eq Float32::INFINITY\n  __powisf2(0, Int32::MIN + 2).should eq Float32::INFINITY\n  __powisf2(0, Int32::MIN + 1).should eq Float32::INFINITY\n  __powisf2(0, Int32::MIN).should eq Float32::INFINITY\n  __powisf2(-0.0, -1).should eq -Float32::INFINITY\n  __powisf2(-0.0, -2).should eq Float32::INFINITY\n  __powisf2(-0.0, -3).should eq -Float32::INFINITY\n  __powisf2(-0.0, -4).should eq Float32::INFINITY\n  __powisf2(-0.0, Int32::MIN + 2).should eq Float32::INFINITY\n  __powisf2(-0.0, Int32::MIN + 1).should eq -Float32::INFINITY\n  __powisf2(-0.0, Int32::MIN).should eq Float32::INFINITY\n  __powisf2(1, -1).should eq 1\n  __powisf2(1, -2).should eq 1\n  __powisf2(1, -3).should eq 1\n  __powisf2(1, -4).should eq 1\n  __powisf2(1, Int32::MIN + 2).should eq 1\n  __powisf2(1, Int32::MIN + 1).should eq 1\n  __powisf2(1, Int32::MIN).should eq 1\n  __powisf2(Float32::INFINITY, -1).should eq 0\n  __powisf2(Float32::INFINITY, -2).should eq 0\n  __powisf2(Float32::INFINITY, -3).should eq 0\n  __powisf2(Float32::INFINITY, -4).should eq 0\n  __powisf2(Float32::INFINITY, Int32::MIN + 2).should eq 0\n  __powisf2(Float32::INFINITY, Int32::MIN + 1).should eq 0\n  __powisf2(Float32::INFINITY, Int32::MIN).should eq 0\n  __powisf2(-Float32::INFINITY, -1).should eq -0.0\n  __powisf2(-Float32::INFINITY, -2).should eq 0\n  __powisf2(-Float32::INFINITY, -3).should eq -0.0\n  __powisf2(-Float32::INFINITY, -4).should eq 0\n  __powisf2(-Float32::INFINITY, Int32::MIN + 2).should eq 0\n  __powisf2(-Float32::INFINITY, Int32::MIN + 1).should eq -0.0\n  __powisf2(-Float32::INFINITY, Int32::MIN).should eq 0\n  __powisf2(2, 10).should eq 1024.0\n  __powisf2(-2, 10).should eq 1024.0\n  __powisf2(2, -10).should eq 1/1024.0\n  __powisf2(-2, -10).should eq 1/1024.0\n  __powisf2(2, 19).should eq 524288.0\n  __powisf2(-2, 19).should eq -524288.0\n  __powisf2(2, -19).should eq 1/524288.0\n  __powisf2(-2, -19).should eq -1/524288.0\n  __powisf2(2, 31).should eq 2147483648.0\n  __powisf2(-2, 31).should eq -2147483648.0\n  __powisf2(2, -31).should eq 1/2147483648.0\n  __powisf2(-2, -31).should eq -1/2147483648.0\nend\n"
  },
  {
    "path": "spec/std/crystal/compiler_rt/spec_helper.cr",
    "content": "require \"spec\"\nrequire \"../../../support/number\"\n\n# TODO: Replace helper methods with literals once possible\n\ndef make_ti(a : Int128, b : Int128)\n  (a.to_i128! << 64) + b\nend\n\ndef make_tu(a : UInt128, b : UInt128)\n  (a.to_u128! << 64) + b\nend\n"
  },
  {
    "path": "spec/std/crystal/digest/md5_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"../../digest/spec_helper\"\nrequire \"crystal/digest/md5\"\n\ndescribe Crystal::Digest::MD5 do\n  it_acts_as_digest_algorithm Crystal::Digest::MD5\n\n  it \"calculates digest from string\" do\n    Crystal::Digest::MD5.digest(\"foo\").to_slice.should eq Bytes[0xac, 0xbd, 0x18, 0xdb, 0x4c, 0xc2, 0xf8, 0x5c, 0xed, 0xef, 0x65, 0x4f, 0xcc, 0xc4, 0xa4, 0xd8]\n  end\n\n  it \"calculates hash from string\" do\n    Crystal::Digest::MD5.hexdigest(\"foo\").should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"calculates hash from unicode string\" do\n    Crystal::Digest::MD5.hexdigest(\"fooø\").should eq(\"d841c4eb31535db11faab98d10316b29\")\n  end\n\n  it \"calculates hash from UInt8 slices\" do\n    s = Bytes[0x66, 0x6f, 0x6f] # f,o,o\n    Crystal::Digest::MD5.hexdigest(s).should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"calculates hash of #to_slice\" do\n    buffer = StaticArray(UInt8, 1).new(1_u8)\n    Crystal::Digest::MD5.hexdigest(buffer).should eq(\"55a54008ad1ba589aa210d2629c1df41\")\n  end\n\n  it \"can take a block\" do\n    Crystal::Digest::MD5.hexdigest do |ctx|\n      ctx.update \"f\"\n      ctx.update Bytes[0x6f, 0x6f]\n    end.should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"calculates base64'd hash from string\" do\n    Crystal::Digest::MD5.base64digest(\"foo\").should eq(\"rL0Y20zC+Fzt72VPzMSk2A==\")\n  end\n\n  it \"resets\" do\n    digest = Crystal::Digest::MD5.new\n    digest.update \"foo\"\n    digest.final.hexstring.should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n\n    digest.reset\n    digest.update \"foo\"\n    digest.final.hexstring.should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"can't call final twice\" do\n    digest = Crystal::Digest::MD5.new\n    digest.final\n    expect_raises(Digest::FinalizedError) do\n      digest.final\n    end\n  end\n\n  it \"return the digest size\" do\n    Crystal::Digest::MD5.new.digest_size.should eq 16\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/digest/sha1_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"../../digest/spec_helper\"\nrequire \"crystal/digest/sha1\"\n\ndescribe Crystal::Digest::SHA1 do\n  it_acts_as_digest_algorithm Crystal::Digest::SHA1\n\n  [\n    {\"\", \"da39a3ee5e6b4b0d3255bfef95601890afd80709\", \"2jmj7l5rSw0yVb/vlWAYkK/YBwk=\"},\n    {\"The quick brown fox jumps over the lazy dog\", \"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\", \"L9ThxnotKPzthJ7hu3bnORuT6xI=\"},\n    {\"abc\", \"a9993e364706816aba3e25717850c26c9cd0d89d\", \"qZk+NkcGgWq6PiVxeFDCbJzQ2J0=\"},\n    {\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\", \"84983e441c3bd26ebaae4aa1f95129e5e54670f1\", \"hJg+RBw70m66rkqh+VEp5eVGcPE=\"},\n    {\"a\", \"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8\", \"hvfkN/qlp/zhXR3cuerq6jd2Z7g=\"},\n    {\"0123456701234567012345670123456701234567012345670123456701234567\", \"e0c094e867ef46c350ef54a7f59dd60bed92ae83\", \"4MCU6GfvRsNQ71Sn9Z3WC+2SroM=\"},\n    {\"fooø\", \"dcf4a1e3542b1a40a4ac2a3f7c92ffdb2d19812f\", \"3PSh41QrGkCkrCo/fJL/2y0ZgS8=\"},\n  ].each do |(string, hexstring, base64digest)|\n    it \"does digest for #{string.inspect}\" do\n      bytes = Crystal::Digest::SHA1.digest(string)\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"resets\" do\n      digest = Crystal::Digest::SHA1.new\n      digest.update string\n      digest.final.hexstring.should eq(hexstring)\n\n      digest.reset\n      digest.update string\n      digest.final.hexstring.should eq(hexstring)\n    end\n\n    it \"can't call #final more than once\" do\n      digest = Crystal::Digest::SHA1.new\n      digest.final\n      expect_raises(Digest::FinalizedError) do\n        digest.final\n      end\n    end\n\n    it \"does digest for #{string.inspect} in a block\" do\n      bytes = Crystal::Digest::SHA1.digest do |ctx|\n        string.each_char do |chr|\n          ctx.update chr.to_s\n        end\n      end\n\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"does hexdigest for #{string.inspect}\" do\n      Crystal::Digest::SHA1.hexdigest(string).should eq(hexstring)\n    end\n\n    it \"does base64digest for #{string.inspect}\" do\n      Crystal::Digest::SHA1.base64digest(string).should eq(base64digest)\n    end\n  end\n\n  it \"returns the digest_size\" do\n    Crystal::Digest::SHA1.new.digest_size.should eq(20)\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/event_loop/polling/arena_spec.cr",
    "content": "{% skip_file unless Crystal::EventLoop.has_constant?(:Polling) %}\n\nrequire \"spec\"\n\ndescribe Crystal::EventLoop::Polling::Arena do\n  describe \"#allocate_at?\" do\n    it \"yields block when not allocated\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      pointer = nil\n      index = nil\n      called = 0\n\n      ret = arena.allocate_at?(0) do |ptr, idx|\n        pointer = ptr\n        index = idx\n        called += 1\n      end\n      ret.should eq(index)\n      called.should eq(1)\n\n      ret = arena.allocate_at?(0) { called += 1 }\n      ret.should be_nil\n      called.should eq(1)\n\n      pointer.should_not be_nil\n      index.should_not be_nil\n\n      arena.get(index.not_nil!) do |ptr|\n        ptr.should eq(pointer)\n      end\n    end\n\n    it \"allocates up to capacity\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n\n      indexes = Array.new(32) do |i|\n        arena.allocate_at?(i) { |ptr, _| ptr.value = i }\n      end\n\n      indexes.size.should eq(32)\n\n      indexes.each do |index|\n        arena.get(index.not_nil!) do |pointer|\n          pointer.should eq(pointer)\n          pointer.value.should eq(index.not_nil!.index)\n        end\n      end\n    end\n\n    it \"checks bounds\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      expect_raises(IndexError) { arena.allocate_at?(-1) { } }\n      expect_raises(IndexError) { arena.allocate_at?(33) { } }\n    end\n  end\n\n  describe \"#get\" do\n    it \"returns previously allocated object\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      pointer = nil\n\n      index = arena.allocate_at(30) do |ptr|\n        pointer = ptr\n        ptr.value = 654321\n      end\n      called = 0\n\n      2.times do\n        arena.get(index.not_nil!) do |ptr|\n          ptr.should eq(pointer)\n          ptr.value.should eq(654321)\n          called += 1\n        end\n      end\n      called.should eq(2)\n    end\n\n    it \"can't access unallocated object\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n\n      expect_raises(RuntimeError) do\n        arena.get(Crystal::EventLoop::Polling::Arena::Index.new(10, 0)) { }\n      end\n    end\n\n    it \"checks generation\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      called = 0\n\n      index1 = arena.allocate_at(2) { called += 1 }\n      called.should eq(1)\n\n      arena.free(index1) { }\n      expect_raises(RuntimeError) { arena.get(index1) { } }\n\n      index2 = arena.allocate_at(2) { called += 1 }\n      called.should eq(2)\n      expect_raises(RuntimeError) { arena.get(index1) { } }\n\n      arena.get(index2) { }\n    end\n\n    it \"checks out of bounds\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      expect_raises(IndexError) { arena.get(Crystal::EventLoop::Polling::Arena::Index.new(-1, 0)) { } }\n      expect_raises(IndexError) { arena.get(Crystal::EventLoop::Polling::Arena::Index.new(33, 0)) { } }\n    end\n  end\n\n  describe \"#get?\" do\n    it \"returns previously allocated object\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      pointer = nil\n\n      index = arena.allocate_at(30) do |ptr|\n        pointer = ptr\n        ptr.value = 654321\n      end\n\n      called = 0\n      2.times do\n        ret = arena.get?(index) do |ptr|\n          ptr.should eq(pointer)\n          ptr.not_nil!.value.should eq(654321)\n          called += 1\n        end\n        ret.should be_true\n      end\n      called.should eq(2)\n    end\n\n    it \"can't access unallocated index\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n\n      called = 0\n      ret = arena.get?(Crystal::EventLoop::Polling::Arena::Index.new(10, 0)) { called += 1 }\n      ret.should be_false\n      called.should eq(0)\n    end\n\n    it \"checks generation\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      called = 0\n\n      old_index = arena.allocate_at(2) { }\n      arena.free(old_index) { }\n\n      # not accessible after free:\n      ret = arena.get?(old_index) { called += 1 }\n      ret.should be_false\n      called.should eq(0)\n\n      # can be reallocated:\n      new_index = arena.allocate_at(2) { }\n\n      # still not accessible after reallocate:\n      ret = arena.get?(old_index) { called += 1 }\n      ret.should be_false\n      called.should eq(0)\n\n      # accessible after reallocate (new index):\n      ret = arena.get?(new_index) { called += 1 }\n      ret.should be_true\n      called.should eq(1)\n    end\n\n    it \"checks out of bounds\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      called = 0\n\n      arena.get?(Crystal::EventLoop::Polling::Arena::Index.new(-1, 0)) { called += 1 }.should be_false\n      arena.get?(Crystal::EventLoop::Polling::Arena::Index.new(33, 0)) { called += 1 }.should be_false\n\n      called.should eq(0)\n    end\n  end\n\n  describe \"#free\" do\n    it \"deallocates the object\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n\n      index1 = arena.allocate_at(3) { |ptr| ptr.value = 123 }\n      arena.free(index1) { }\n\n      index2 = arena.allocate_at(3) { }\n      index2.should_not eq(index1)\n\n      value = nil\n      arena.get(index2) { |ptr| value = ptr.value }\n      value.should eq(0)\n    end\n\n    it \"checks generation\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n\n      called = 0\n      old_index = arena.allocate_at(1) { }\n\n      # can free:\n      arena.free(old_index) { called += 1 }\n      called.should eq(1)\n\n      # can reallocate:\n      new_index = arena.allocate_at(1) { }\n\n      # can't free with invalid index:\n      arena.free(old_index) { called += 1 }\n      called.should eq(1)\n\n      # but new index can:\n      arena.free(new_index) { called += 1 }\n      called.should eq(2)\n    end\n\n    it \"checks out of bounds\" do\n      arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n      called = 0\n\n      arena.free(Crystal::EventLoop::Polling::Arena::Index.new(-1, 0)) { called += 1 }\n      arena.free(Crystal::EventLoop::Polling::Arena::Index.new(33, 0)) { called += 1 }\n\n      called.should eq(0)\n    end\n  end\n\n  it \"#each_index\" do\n    arena = Crystal::EventLoop::Polling::Arena(Int32, 96).new(32)\n    indices = [] of {Int32, Crystal::EventLoop::Polling::Arena::Index}\n\n    arena.each_index { |i, index| indices << {i, index} }\n    indices.should be_empty\n\n    index5 = arena.allocate_at(5) { }\n\n    arena.each_index { |i, index| indices << {i, index} }\n    indices.should eq([{5, index5}])\n\n    index3 = arena.allocate_at(3) { }\n    index11 = arena.allocate_at(11) { }\n    index10 = arena.allocate_at(10) { }\n    index30 = arena.allocate_at(30) { }\n\n    indices.clear\n    arena.each_index { |i, index| indices << {i, index} }\n    indices.should eq([\n      {3, index3},\n      {5, index5},\n      {10, index10},\n      {11, index11},\n      {30, index30},\n    ])\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/event_loop/polling/poll_descriptor_spec.cr",
    "content": "{% skip_file unless Crystal::EventLoop.has_constant?(:Polling) %}\n\nrequire \"spec\"\n\nclass Crystal::EventLoop::FakeLoop < Crystal::EventLoop::Polling\n  def self.default_file_blocking?\n    false\n  end\n\n  def self.default_socket_blocking?\n    false\n  end\n\n  getter operations = [] of {Symbol, Int32, Arena::Index | Bool}\n\n  private def system_run(blocking : Bool, & : Fiber ->) : Nil\n  end\n\n  def interrupt : Nil\n  end\n\n  protected def system_add(fd : Int32, index : Arena::Index) : Nil\n    operations << {:add, fd, index}\n  end\n\n  protected def system_del(fd : Int32, closing = true) : Nil\n    operations << {:del, fd, closing}\n  end\n\n  protected def system_del(fd : Int32, closing = true, &) : Nil\n    operations << {:del, fd, closing}\n  end\n\n  private def system_set_timer(time : Time::Instant?) : Nil\n  end\nend\n\ndescribe Crystal::EventLoop::Polling::Waiters do\n  describe \"#take_ownership\" do\n    it \"associates a poll descriptor to an evloop instance\" do\n      fd = Int32::MAX\n      pd = Crystal::EventLoop::Polling::PollDescriptor.new\n      index = Crystal::EventLoop::Polling::Arena::Index.new(fd, 0)\n      evloop = Crystal::EventLoop::Polling::FakeLoop.new\n\n      pd.take_ownership(evloop, fd, index)\n      pd.@event_loop.should be(evloop)\n\n      evloop.operations.should eq([\n        {:add, fd, index},\n      ])\n    end\n\n    it \"moves a poll descriptor to another evloop instance\" do\n      fd = Int32::MAX\n      pd = Crystal::EventLoop::Polling::PollDescriptor.new\n      index = Crystal::EventLoop::Polling::Arena::Index.new(fd, 0)\n\n      evloop1 = Crystal::EventLoop::Polling::FakeLoop.new\n      evloop2 = Crystal::EventLoop::Polling::FakeLoop.new\n\n      pd.take_ownership(evloop1, fd, index)\n      pd.take_ownership(evloop2, fd, index)\n\n      pd.@event_loop.should be(evloop2)\n\n      evloop1.operations.should eq([\n        {:add, fd, index},\n        {:del, fd, false},\n      ])\n      evloop2.operations.should eq([\n        {:add, fd, index},\n      ])\n    end\n\n    it \"can't move to the current evloop\" do\n      fd = Int32::MAX\n      pd = Crystal::EventLoop::Polling::PollDescriptor.new\n      index = Crystal::EventLoop::Polling::Arena::Index.new(fd, 0)\n\n      evloop = Crystal::EventLoop::Polling::FakeLoop.new\n\n      pd.take_ownership(evloop, fd, index)\n      expect_raises(Exception) { pd.take_ownership(evloop, fd, index) }\n    end\n\n    it \"can't move with pending waiters\" do\n      fd = Int32::MAX\n      pd = Crystal::EventLoop::Polling::PollDescriptor.new\n      index = Crystal::EventLoop::Polling::Arena::Index.new(fd, 0)\n      event = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n\n      evloop1 = Crystal::EventLoop::Polling::FakeLoop.new\n      pd.take_ownership(evloop1, fd, index)\n      pd.@readers.add(pointerof(event))\n\n      evloop2 = Crystal::EventLoop::Polling::FakeLoop.new\n      expect_raises(RuntimeError) { pd.take_ownership(evloop2, fd, index) }\n\n      pd.@event_loop.should be(evloop1)\n\n      evloop1.operations.should eq([\n        {:add, fd, index},\n      ])\n      evloop2.operations.should be_empty\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/event_loop/polling/waiters_spec.cr",
    "content": "{% skip_file unless Crystal::EventLoop.has_constant?(:Polling) %}\n\nrequire \"spec\"\n\ndescribe Crystal::EventLoop::Polling::Waiters do\n  describe \"#add\" do\n    it \"adds event to list\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n\n      event = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      ret = waiters.add(pointerof(event))\n      ret.should be_true\n    end\n\n    it \"doesn't add the event when the list is ready (race condition)\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      waiters.ready_one { true }\n\n      event = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      ret = waiters.add(pointerof(event))\n      ret.should be_false\n      waiters.@ready.should be_false\n    end\n\n    it \"doesn't add the event when the list is always ready\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      waiters.ready_all { }\n\n      event = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      ret = waiters.add(pointerof(event))\n      ret.should be_false\n      waiters.@always_ready.should be_true\n    end\n  end\n\n  describe \"#delete\" do\n    it \"removes the event from the list\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      event = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n\n      waiters.add(pointerof(event))\n      waiters.delete(pointerof(event))\n\n      called = false\n      waiters.ready_one { called = true }\n      called.should be_false\n    end\n\n    it \"does nothing when the event isn't in the list\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      event = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      waiters.delete(pointerof(event))\n    end\n  end\n\n  describe \"#ready_one\" do\n    it \"marks the list as ready when empty (race condition)\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      called = false\n\n      waiters.ready_one { called = true }\n\n      called.should be_false\n      waiters.@ready.should be_true\n    end\n\n    it \"dequeues events in FIFO order\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      event1 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event2 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event3 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      called = 0\n\n      waiters.add(pointerof(event1))\n      waiters.add(pointerof(event2))\n      waiters.add(pointerof(event3))\n\n      3.times do\n        waiters.ready_one do |event|\n          case called += 1\n          when 1 then event.should eq(pointerof(event1))\n          when 2 then event.should eq(pointerof(event2))\n          when 3 then event.should eq(pointerof(event3))\n          end\n          true\n        end\n      end\n      called.should eq(3)\n      waiters.@ready.should be_false\n\n      waiters.ready_one do\n        called += 1\n        true\n      end\n      called.should eq(3)\n      waiters.@ready.should be_true\n    end\n\n    it \"dequeues events until the block returns true\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      event1 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event2 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event3 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      called = 0\n\n      waiters.add(pointerof(event1))\n      waiters.add(pointerof(event2))\n      waiters.add(pointerof(event3))\n\n      waiters.ready_one do |event|\n        (called += 1) == 2\n      end\n      called.should eq(2)\n      waiters.@ready.should be_false\n    end\n\n    it \"dequeues events until empty and marks the list as ready\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      event1 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event2 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      called = 0\n\n      waiters.add(pointerof(event1))\n      waiters.add(pointerof(event2))\n\n      waiters.ready_one do |event|\n        called += 1\n        false\n      end\n      called.should eq(2)\n      waiters.@ready.should be_true\n    end\n  end\n\n  describe \"#ready_all\" do\n    it \"marks the list as always ready\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      called = false\n\n      waiters.ready_all { called = true }\n\n      called.should be_false\n      waiters.@always_ready.should be_true\n    end\n\n    it \"dequeues all events\" do\n      waiters = Crystal::EventLoop::Polling::Waiters.new\n      event1 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event2 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      event3 = Crystal::EventLoop::Polling::Event.new(:io_read, Fiber.current)\n      called = 0\n\n      waiters.add(pointerof(event1))\n      waiters.add(pointerof(event2))\n      waiters.add(pointerof(event3))\n\n      waiters.ready_all do |event|\n        case called += 1\n        when 1 then event.should eq(pointerof(event1))\n        when 2 then event.should eq(pointerof(event2))\n        when 3 then event.should eq(pointerof(event3))\n        end\n      end\n      called.should eq(3)\n      waiters.@always_ready.should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/event_loop/timers_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/event_loop/timers\"\n\nprivate struct Timer\n  include Crystal::PointerPairingHeap::Node\n\n  property! wake_at : Time::Instant\n\n  def initialize(timeout : Time::Span? = nil)\n    @wake_at = Crystal::System::Time.instant + timeout if timeout\n  end\n\n  def heap_compare(other : Pointer(self)) : Bool\n    wake_at < other.value.wake_at\n  end\nend\n\ndescribe Crystal::EventLoop::Timers do\n  it \"#empty?\" do\n    timers = Crystal::EventLoop::Timers(Timer).new\n    timers.empty?.should be_true\n\n    event = Timer.new(7.seconds)\n    timers.add(pointerof(event))\n    timers.empty?.should be_false\n\n    timers.delete(pointerof(event))\n    timers.empty?.should be_true\n  end\n\n  it \"#next_ready?\" do\n    # empty\n    timers = Crystal::EventLoop::Timers(Timer).new\n    timers.next_ready?.should be_nil\n\n    # with events\n    event1s = Timer.new(1.second)\n    event3m = Timer.new(3.minutes)\n    event5m = Timer.new(5.minutes)\n\n    timers.add(pointerof(event5m))\n    timers.next_ready?.should eq(event5m.wake_at?)\n\n    timers.add(pointerof(event1s))\n    timers.next_ready?.should eq(event1s.wake_at?)\n\n    timers.add(pointerof(event3m))\n    timers.next_ready?.should eq(event1s.wake_at?)\n  end\n\n  it \"#dequeue_ready\" do\n    timers = Crystal::EventLoop::Timers(Timer).new\n\n    event1 = Timer.new(0.seconds)\n    event2 = Timer.new(0.seconds)\n    event3 = Timer.new(1.minute)\n\n    # empty\n    called = 0\n    timers.dequeue_ready { called += 1 }\n    called.should eq(0)\n\n    # add events in non chronological order\n    timers = Crystal::EventLoop::Timers(Timer).new\n    timers.add(pointerof(event1))\n    timers.add(pointerof(event3))\n    timers.add(pointerof(event2))\n\n    events = [] of Timer*\n    timers.dequeue_ready { |event| events << event }\n\n    events.should eq([\n      pointerof(event1),\n      pointerof(event2),\n    ])\n    timers.empty?.should be_false\n  end\n\n  it \"#add\" do\n    timers = Crystal::EventLoop::Timers(Timer).new\n\n    event0 = Timer.new\n    event1 = Timer.new(0.seconds)\n    event2 = Timer.new(2.minutes)\n    event3 = Timer.new(1.minute)\n\n    # add events in non chronological order\n    timers.add(pointerof(event1)).should be_true # added to the head (next ready)\n    timers.add(pointerof(event2)).should be_false\n    timers.add(pointerof(event3)).should be_false\n\n    event0.wake_at = Crystal::System::Time.instant - 1.minute\n    timers.add(pointerof(event0)).should be_true # added new head (next ready)\n  end\n\n  it \"#delete\" do\n    event1 = Timer.new(0.seconds)\n    event2 = Timer.new(0.seconds)\n    event3 = Timer.new(1.minute)\n    event4 = Timer.new(4.minutes)\n\n    # add events in non chronological order\n    timers = Crystal::EventLoop::Timers(Timer).new\n    timers.add(pointerof(event1))\n    timers.add(pointerof(event3))\n    timers.add(pointerof(event2))\n\n    timers.delete(pointerof(event1)).should eq({true, true})  # dequeued+removed head (next ready)\n    timers.delete(pointerof(event3)).should eq({true, false}) # dequeued\n    timers.delete(pointerof(event2)).should eq({true, true})  # dequeued+removed new head (next ready)\n    timers.empty?.should be_true\n    timers.delete(pointerof(event2)).should eq({false, false}) # not dequeued\n    timers.delete(pointerof(event4)).should eq({false, false}) # not dequeued\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/fd_lock_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/fd_lock\"\nrequire \"wait_group\"\n\ndescribe Crystal::FdLock do\n  describe \"#read\" do\n    it \"acquires read lock\" do\n      lock = Crystal::FdLock.new\n      called = 0\n\n      lock.read { called += 1 }\n      lock.read { called += 1 }\n\n      called.should eq(2)\n    end\n\n    it \"acquires exclusive lock\" do\n      lock = Crystal::FdLock.new\n      increment = 0\n\n      WaitGroup.wait do |wg|\n        10.times do\n          wg.spawn do\n            100_000.times do |i|\n              lock.read do\n                increment += 1\n                Fiber.yield if i % 1000 == 1\n              end\n            end\n          end\n        end\n      end\n\n      increment.should eq(1_000_000)\n    end\n\n    it \"raises when closed\" do\n      lock = Crystal::FdLock.new\n      called = false\n\n      lock.try_close? { }\n      expect_raises(IO::Error, \"Closed\") { lock.read { called = true; Fiber.yield } }\n\n      called.should eq(false)\n    end\n  end\n\n  describe \"#write\" do\n    it \"acquires write lock\" do\n      lock = Crystal::FdLock.new\n      called = 0\n\n      lock.write { called += 1 }\n      lock.write { called += 1 }\n\n      called.should eq(2)\n    end\n\n    it \"acquires exclusive lock\" do\n      lock = Crystal::FdLock.new\n      increment = 0\n\n      WaitGroup.wait do |wg|\n        10.times do\n          wg.spawn do\n            100_000.times do |i|\n              lock.write do\n                increment += 1\n                Fiber.yield if i % 1000 == 1\n              end\n            end\n          end\n        end\n      end\n\n      increment.should eq(1_000_000)\n    end\n\n    it \"raises when closed\" do\n      lock = Crystal::FdLock.new\n      called = false\n\n      lock.try_close? { }\n      expect_raises(IO::Error, \"Closed\") { lock.read { called = true } }\n\n      called.should eq(false)\n    end\n  end\n\n  describe \"#reference\" do\n    it \"acquires\" do\n      lock = Crystal::FdLock.new\n      called = 0\n\n      lock.reference { called += 1 }\n      lock.reference { called += 1 }\n\n      called.should eq(2)\n    end\n\n    it \"allows reentrancy (side effect)\" do\n      lock = Crystal::FdLock.new\n      called = 0\n\n      lock.reference { called += 1 }\n      lock.reference do\n        lock.reference { called += 1 }\n      end\n\n      called.should eq(2)\n    end\n\n    it \"acquires shared reference\" do\n      lock = Crystal::FdLock.new\n\n      ready = WaitGroup.new(1)\n      release = Channel(String).new\n\n      spawn do\n        lock.reference do\n          ready.done\n\n          select\n          when release.send(\"ok\")\n          when timeout(1.second)\n            release.send(\"timeout\")\n          end\n        end\n      end\n\n      ready.wait\n      lock.reference { }\n\n      release.receive.should eq(\"ok\")\n    end\n\n    it \"raises when closed\" do\n      lock = Crystal::FdLock.new\n      lock.try_close? { }\n\n      called = false\n      expect_raises(IO::Error, \"Closed\") do\n        lock.reference { called = true }\n      end\n\n      called.should be_false\n    end\n  end\n\n  describe \"#try_close?\" do\n    it \"closes\" do\n      lock = Crystal::FdLock.new\n      lock.closed?.should be_false\n\n      called = false\n      lock.try_close? { called = true }.should be_true\n      lock.closed?.should be_true\n      called.should be_true\n    end\n\n    it \"closes once\" do\n      lock = Crystal::FdLock.new\n\n      called = 0\n\n      WaitGroup.wait do |wg|\n        10.times do\n          wg.spawn do\n            lock.try_close? { called += 1 }\n            lock.try_close? { called += 1 }\n          end\n        end\n      end\n\n      called.should eq(1)\n    end\n\n    it \"waits for all references to return\" do\n      lock = Crystal::FdLock.new\n\n      ready = WaitGroup.new(10)\n      exceptions = Channel(Exception).new(10)\n\n      WaitGroup.wait do |wg|\n        10.times do\n          wg.spawn do\n            begin\n              lock.reference do\n                ready.done\n                Fiber.yield\n              end\n            rescue ex\n              exceptions.send(ex)\n            end\n          end\n        end\n\n        ready.wait\n\n        called = false\n        lock.try_close? { called = true }.should be_true\n        lock.closed?.should be_true\n        called.should be_true\n      end\n      exceptions.close\n\n      if ex = exceptions.receive?\n        raise ex\n      end\n    end\n\n    it \"resumes waiters\" do\n      lock = Crystal::FdLock.new\n\n      ready = WaitGroup.new(8)\n      running = WaitGroup.new\n      exceptions = Channel(Exception).new(8)\n\n      # acquire locks\n      lock.read do\n        lock.write do\n          # spawn concurrent fibers\n          4.times do |i|\n            running.spawn do\n              ready.done\n              lock.read { }\n            rescue ex\n              exceptions.send(ex)\n            end\n\n            running.spawn do\n              ready.done\n              lock.write { }\n            rescue ex\n              exceptions.send(ex)\n            end\n          end\n\n          # wait for all the concurrent fibers to be trying to lock\n          ready.wait\n        end\n      end\n\n      # close, then wait for the fibers to be resumed (and fail)\n      lock.try_close? { }.should eq(true)\n      running.wait\n      exceptions.close\n\n      # fibers should have failed (unlikely: one may succeed to lock)\n      failed = 0\n      while ex = exceptions.receive?\n        failed += 1\n        ex.should be_a(IO::Error)\n        ex.message.should eq(\"Closed\")\n      end\n      failed.should be > 0\n    end\n  end\n\n  it \"locks read + write + shared reference\" do\n    lock = Crystal::FdLock.new\n    called = 0\n\n    lock.read do\n      lock.write do\n        lock.reference do\n          called += 1\n        end\n      end\n    end\n\n    called.should eq(1)\n  end\n\n  it \"#reset\" do\n    lock = Crystal::FdLock.new\n    lock.try_close? { }\n    lock.reset\n    lock.try_close? { }.should eq(true)\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/hasher_spec.cr",
    "content": "require \"spec\"\nrequire \"bit_array\"\nrequire \"../spec_helper\"\nrequire \"big\"\nrequire \"random/secure\"\n\nstruct Crystal::Hasher\n  def self.for_test\n    new(1_u64, 1_u64)\n  end\nend\n\nenum TestHasherEnum\n  A\n  B\nend\n\nalias TestHasher = Crystal::Hasher\n\ndescribe \"Crystal::Hasher\" do\n  context \"behavior\" do\n    it \"#nil should change hasher state\" do\n      hasher = TestHasher.for_test\n      hasher1 = nil.hash(hasher)\n      hasher2 = nil.hash(hasher1)\n      hasher1.result.should_not eq(hasher.result)\n      hasher2.result.should_not eq(hasher.result)\n      hasher2.result.should_not eq(hasher1.result)\n    end\n\n    it \"#bool should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher_true = true.hash(hasher)\n      hasher_false = false.hash(hasher)\n      hasher.result.should_not eq(hasher_true.result)\n      hasher.result.should_not eq(hasher_false.result)\n      hasher_true.result.should_not eq(hasher_false.result)\n    end\n\n    it \"#int should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = 1.hash(hasher)\n      hasher2 = 2.hash(hasher)\n      hasher12 = 2.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#int should be equal for different types\" do\n      1.hash.should eq(1_u64.hash)\n      2.hash.should eq(2_u64.hash)\n    end\n\n    it \"Big i64 numbers should be hashed ok\" do\n      Int64::MAX.hash.should eq(Int64::MAX.hash)\n    end\n\n    {% if flag?(:bits64) %}\n      it \"128bit types should be hashed ok\" do\n        1.to_i128.hash.should eq(1_i8.hash)\n        1.to_u128.hash.should eq(1_u8.hash)\n      end\n    {% end %}\n\n    it \"#float should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = 1.0.hash(hasher)\n      hasher2 = 2.0.hash(hasher)\n      hasher12 = 2.0.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#char should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = 'a'.hash(hasher)\n      hasher2 = 'b'.hash(hasher)\n      hasher12 = 'b'.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#enum should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = TestHasherEnum::A.hash(hasher)\n      hasher2 = TestHasherEnum::B.hash(hasher)\n      hasher12 = TestHasherEnum::B.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#symbol should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = :A.hash(hasher)\n      hasher2 = :B.hash(hasher)\n      hasher12 = :B.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#reference should change state and differ\" do\n      hasher = TestHasher.for_test\n      a, b = Reference.new, Reference.new\n      hasher1 = a.hash(hasher)\n      hasher2 = b.hash(hasher)\n      hasher12 = b.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#string should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = \"a\".hash(hasher)\n      hasher2 = \"b\".hash(hasher)\n      hasher12 = \"b\".hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#class should change state and differ\" do\n      hasher = TestHasher.for_test\n      hasher1 = TestHasher.hash(hasher)\n      hasher2 = TestHasherEnum.hash(hasher)\n      hasher12 = TestHasherEnum.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n\n    it \"#bytes should change state and differ\" do\n      hasher = TestHasher.for_test\n      a = Bytes[1, 2, 3]\n      b = Bytes[2, 3, 4]\n      hasher1 = a.hash(hasher)\n      hasher2 = b.hash(hasher)\n      hasher12 = b.hash(hasher1)\n      [hasher, hasher1, hasher2, hasher12]\n        .map(&.result)\n        .combinations(2)\n        .each { |(a, b)| a.should_not eq(b) }\n    end\n  end\n\n  context \"funny_hash\" do\n    it \"result should work\" do\n      hasher = TestHasher.new(1_u64, 1_u64)\n      typeof(hasher.result).should eq(UInt64)\n      hasher.result.should eq(0x162c591a100060e5_u64)\n\n      hasher = TestHasher.new(1_u64, 2_u64)\n      hasher.result.should eq(0x7f8304f0947082d1_u64)\n\n      hasher = TestHasher.new(2_u64, 1_u64)\n      hasher.result.should eq(0xc302065c9b909fdf_u64)\n\n      hasher = TestHasher.new(0x123456789abcdef0_u64, 0x016fcd2b89e745a3_u64)\n      hasher.result.should eq(0x54258afe17b6a4bb_u64)\n\n      # \"bad seed\"\n      hasher = TestHasher.new(0_u64, 0_u64)\n      hasher.result.should eq(0_u64)\n    end\n\n    it \"#nil should match test vectors\" do\n      hasher = TestHasher.for_test\n      hasher1 = nil.hash(hasher)\n      hasher2 = nil.hash(hasher1)\n      hasher1.result.should eq(0x2c58b2332000c1cb_u64)\n      hasher2.result.should eq(0xef5ab89129b8991b_u64)\n    end\n\n    it \"#bool should match test vectors\" do\n      hasher = TestHasher.for_test\n      hasher_true = true.hash(hasher)\n      hasher_false = false.hash(hasher)\n      hasher_true.result.should eq(0x94e4534c12903881_u64)\n      hasher_false.result.should eq(0x15cf71e56618745b_u64)\n    end\n\n    it \"#int should match test vectors\" do\n      hasher = TestHasher.for_test\n      hasher1 = 1.hash(hasher)\n      hasher2 = 2.hash(hasher)\n      hasher1.result.should eq(0x94e4534c12903881_u64)\n      hasher2.result.should eq(0xaf42909720d981e7_u64)\n    end\n\n    it \"#float should match test vectors\" do\n      hasher = TestHasher.for_test\n      hasher1 = 1.0.hash(hasher)\n      hasher2 = 2.0.hash(hasher)\n      hasher1.result.should eq(10728791798497425537_u64)\n      hasher2.result.should eq(12628815283865879015_u64)\n    end\n\n    it \"#string should match test vectors\" do\n      hasher = TestHasher.for_test\n      hasher0 = \"\".hash(hasher)\n      hasher1 = \"1\".hash(hasher)\n      hasher2 = \"2.0\".hash(hasher)\n      hasher0.result.should eq(0x15cf71e56618745b_u64)\n      hasher1.result.should eq(0x70ef623beff8c213_u64)\n      hasher2.result.should eq(0x2908fdd2bb81fbed_u64)\n    end\n  end\n\n  describe \"to_s\" do\n    it \"should not expose internal data\" do\n      hasher = TestHasher.new(1_u64, 2_u64)\n      hasher.to_s.should_not contain('1')\n      hasher.to_s.should_not contain(hasher.@a.to_s)\n      hasher.to_s.should_not contain(hasher.@a.to_s(16))\n      hasher.to_s.should_not contain('2')\n      hasher.to_s.should_not contain(hasher.@b.to_s)\n      hasher.to_s.should_not contain(hasher.@b.to_s(16))\n    end\n  end\n\n  describe \"inspect\" do\n    it \"should not expose internal data\" do\n      hasher = TestHasher.new(1_u64, 2_u64)\n      hasher.inspect.should_not contain('1')\n      hasher.inspect.should_not contain(hasher.@a.to_s)\n      hasher.inspect.should_not contain(hasher.@a.to_s(16))\n      hasher.inspect.should_not contain('2')\n      hasher.inspect.should_not contain(hasher.@b.to_s)\n      hasher.inspect.should_not contain(hasher.@b.to_s(16))\n    end\n  end\n\n  describe \"normalization of numbers\" do\n    it \"should 1_i32 and 1_f64 hashes equal\" do\n      1_i32.hash.should eq(1_f64.hash)\n    end\n\n    it \"should 1_f32 and 1.to_big_f hashes equal\" do\n      1_f32.hash.should eq(1.to_big_f.hash)\n    end\n\n    it \"should 1_f32 and 1.to_big_r hashes equal\" do\n      1_f32.hash.should eq(1.to_big_r.hash)\n    end\n\n    it \"should 1_f32 and 1.to_big_i hashes equal\" do\n      1_f32.hash.should eq(1.to_big_i.hash)\n    end\n  end\n\n  describe \".reduce_num\" do\n    it \"reduces primitive int\" do\n      {% for int in Int::Primitive.union_types %}\n        Crystal::Hasher.reduce_num({{ int }}.new(0)).should eq(0_u64)\n        Crystal::Hasher.reduce_num({{ int }}.new(1)).should eq(1_u64)\n        Crystal::Hasher.reduce_num({{ int }}::MAX).should eq(UInt64.new!({{ int }}::MAX % 0x1FFF_FFFF_FFFF_FFFF_u64))\n      {% end %}\n\n      {% for int in Int::Signed.union_types %}\n        Crystal::Hasher.reduce_num({{ int }}.new(-1)).should eq(UInt64::MAX)\n        Crystal::Hasher.reduce_num({{ int }}::MIN).should eq(UInt64::MAX - UInt64.new!({{ int }}::MAX % 0x1FFF_FFFF_FFFF_FFFF_u64))\n      {% end %}\n    end\n\n    it \"reduces primitive float\" do\n      {% for float in Float::Primitive.union_types %}\n        Crystal::Hasher.reduce_num({{ float }}.new(0)).should eq(0_u64)\n        Crystal::Hasher.reduce_num({{ float }}.new(1)).should eq(1_u64)\n        Crystal::Hasher.reduce_num({{ float }}.new(-1)).should eq(UInt64::MAX)\n        Crystal::Hasher.reduce_num({{ float }}::INFINITY).should eq(Crystal::Hasher::HASH_INF_PLUS)\n        Crystal::Hasher.reduce_num(-{{ float }}::INFINITY).should eq(Crystal::Hasher::HASH_INF_MINUS)\n        Crystal::Hasher.reduce_num({{ float }}::NAN).should eq(Crystal::Hasher::HASH_NAN)\n\n        x = {{ float }}.new(2)\n        i = 1\n        until x.infinite?\n          Crystal::Hasher.reduce_num(x).should eq(1_u64 << (i % 61))\n          x *= 2\n          i += 1\n        end\n\n        x = {{ float }}.new(0.5)\n        i = 1\n        until x.zero?\n          Crystal::Hasher.reduce_num(x).should eq(1_u64 << ((-i) % 61))\n          x /= 2\n          i += 1\n        end\n      {% end %}\n\n      Crystal::Hasher.reduce_num(Float32::MAX).should eq(0x1FFF_F800_0000_003F_u64)\n      Crystal::Hasher.reduce_num(Float64::MAX).should eq(0x1F00_FFFF_FFFF_FFFF_u64)\n    end\n\n    it \"reduces BigInt\" do\n      Crystal::Hasher.reduce_num(0.to_big_i).should eq(0_u64)\n      Crystal::Hasher.reduce_num(1.to_big_i).should eq(1_u64)\n      Crystal::Hasher.reduce_num((-1).to_big_i).should eq(UInt64::MAX)\n\n      (1..300).each do |i|\n        Crystal::Hasher.reduce_num(2.to_big_i ** i).should eq(1_u64 << (i % 61))\n        Crystal::Hasher.reduce_num(-(2.to_big_i ** i)).should eq(&-(1_u64 << (i % 61)))\n      end\n    end\n\n    it \"reduces BigFloat\" do\n      Crystal::Hasher.reduce_num(0.to_big_f).should eq(0_u64)\n      Crystal::Hasher.reduce_num(1.to_big_f).should eq(1_u64)\n      Crystal::Hasher.reduce_num((-1).to_big_f).should eq(UInt64::MAX)\n      Crystal::Hasher.reduce_num(Float32::MAX.to_big_f).should eq(0x1FFF_F800_0000_003F_u64)\n      Crystal::Hasher.reduce_num(Float64::MAX.to_big_f).should eq(0x1F00_FFFF_FFFF_FFFF_u64)\n\n      (1..300).each do |i|\n        Crystal::Hasher.reduce_num(2.to_big_f ** i).should eq(1_u64 << (i % 61))\n        Crystal::Hasher.reduce_num(-(2.to_big_f ** i)).should eq(&-(1_u64 << (i % 61)))\n        Crystal::Hasher.reduce_num(0.5.to_big_f ** i).should eq(1_u64 << ((-i) % 61))\n        Crystal::Hasher.reduce_num(-(0.5.to_big_f ** i)).should eq(&-(1_u64 << ((-i) % 61)))\n      end\n    end\n\n    it \"reduces BigDecimal\" do\n      Crystal::Hasher.reduce_num(0.to_big_d).should eq(0_u64)\n      Crystal::Hasher.reduce_num(1.to_big_d).should eq(1_u64)\n      Crystal::Hasher.reduce_num((-1).to_big_d).should eq(UInt64::MAX)\n\n      # small inverse powers of 10\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 1)).should eq(0x1CCCCCCCCCCCCCCC_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 2)).should eq(0x0FAE147AE147AE14_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 3)).should eq(0x0E5E353F7CED9168_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 4)).should eq(0x14A305532617C1BD_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 5)).should eq(0x05438088509BF9C6_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 6)).should eq(0x06ED2674080F98FA_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 7)).should eq(0x1A4AEA3ECD9B28E5_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 8)).should eq(0x12A1176CAE291DB0_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 9)).should eq(0x01DCE8BE116A82F8_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 10)).should eq(0x1362E41301BDD9E5_u64)\n\n      # a^(p-1) === 1 (mod p)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFFE_u64)).should eq(1_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFFD_u64)).should eq(10_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFFC_u64)).should eq(100_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFFB_u64)).should eq(1000_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFFA_u64)).should eq(10000_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFF9_u64)).should eq(100000_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFF8_u64)).should eq(1000000_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFF7_u64)).should eq(10000000_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFF6_u64)).should eq(100000000_u64)\n      Crystal::Hasher.reduce_num(BigDecimal.new(1, 0x1FFFFFFFFFFFFFF5_u64)).should eq(1000000000_u64)\n\n      (1..300).each do |i|\n        Crystal::Hasher.reduce_num(2.to_big_d ** i).should eq(1_u64 << (i % 61))\n        Crystal::Hasher.reduce_num(-(2.to_big_d ** i)).should eq(&-(1_u64 << (i % 61)))\n        Crystal::Hasher.reduce_num(0.5.to_big_d ** i).should eq(1_u64 << ((-i) % 61))\n        Crystal::Hasher.reduce_num(-(0.5.to_big_d ** i)).should eq(&-(1_u64 << ((-i) % 61)))\n      end\n    end\n\n    it \"reduces BigRational\" do\n      Crystal::Hasher.reduce_num(0.to_big_r).should eq(0_u64)\n      Crystal::Hasher.reduce_num(1.to_big_r).should eq(1_u64)\n      Crystal::Hasher.reduce_num((-1).to_big_r).should eq(UInt64::MAX)\n\n      # inverses of small integers\n      Crystal::Hasher.reduce_num(BigRational.new(1, 2)).should eq(0x1000000000000000_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 3)).should eq(0x1555555555555555_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 4)).should eq(0x0800000000000000_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 5)).should eq(0x1999999999999999_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 6)).should eq(0x1AAAAAAAAAAAAAAA_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 7)).should eq(0x1B6DB6DB6DB6DB6D_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 8)).should eq(0x0400000000000000_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 9)).should eq(0x1C71C71C71C71C71_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 10)).should eq(0x1CCCCCCCCCCCCCCC_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 11)).should eq(0x1D1745D1745D1745_u64)\n\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1000000000000000_u64)).should eq(2_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1555555555555555_u64)).should eq(3_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x0800000000000000_u64)).should eq(4_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1999999999999999_u64)).should eq(5_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1AAAAAAAAAAAAAAA_u64)).should eq(6_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1B6DB6DB6DB6DB6D_u64)).should eq(7_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x0400000000000000_u64)).should eq(8_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1C71C71C71C71C71_u64)).should eq(9_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1CCCCCCCCCCCCCCC_u64)).should eq(10_u64)\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1D1745D1745D1745_u64)).should eq(11_u64)\n\n      (1..300).each do |i|\n        Crystal::Hasher.reduce_num(2.to_big_r ** i).should eq(1_u64 << (i % 61))\n        Crystal::Hasher.reduce_num(-(2.to_big_r ** i)).should eq(&-(1_u64 << (i % 61)))\n        Crystal::Hasher.reduce_num(0.5.to_big_r ** i).should eq(1_u64 << ((-i) % 61))\n        Crystal::Hasher.reduce_num(-(0.5.to_big_r ** i)).should eq(&-(1_u64 << ((-i) % 61)))\n      end\n\n      Crystal::Hasher.reduce_num(BigRational.new(1, 0x1FFF_FFFF_FFFF_FFFF_u64)).should eq(Crystal::Hasher::HASH_INF_PLUS)\n      Crystal::Hasher.reduce_num(BigRational.new(-1, 0x1FFF_FFFF_FFFF_FFFF_u64)).should eq(Crystal::Hasher::HASH_INF_MINUS)\n      Crystal::Hasher.reduce_num(BigRational.new(2, 0x1FFF_FFFF_FFFF_FFFF_u64)).should eq(Crystal::Hasher::HASH_INF_PLUS)\n      Crystal::Hasher.reduce_num(BigRational.new(-2, 0x1FFF_FFFF_FFFF_FFFF_u64)).should eq(Crystal::Hasher::HASH_INF_MINUS)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/pointer_linked_list_spec.cr",
    "content": "require \"spec\"\nrequire \"crystal/pointer_linked_list.cr\"\n\nprivate struct TestedObject\n  include Crystal::PointerLinkedList::Node\n\n  property value : Int32\n\n  def initialize(@value : Int32)\n  end\nend\n\nprivate module ExpectOrderHelper\n  macro by_next(*vars)\n    {% sz = vars.size %}\n    {% for var, index in vars %}\n      {% if (index + 1) < sz %}\n        {{ var.id }}.next.should eq(pointerof({{ vars[(index + 1)].id }}))\n      {% end %}\n    {% end %}\n  end\n\n  macro by_previous(*vars)\n    {% sz = vars.size %}\n    {% for var, index in vars %}\n      {% if (index + 1) < sz %}\n        {{ var.id }}.previous.should eq(pointerof({{ vars[(index + 1)].id }}))\n      {% end %}\n    {% end %}\n  end\nend\n\ndescribe Crystal::PointerLinkedList do\n  describe \"empty?\" do\n    it \"return true if there is no element in list\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n      list.empty?.should be_true\n    end\n  end\n\n  describe \"push\" do\n    it \"append the node into the list\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 0\n      y = TestedObject.new 1\n      z = TestedObject.new 2\n\n      list.push pointerof(x)\n      list.push pointerof(y)\n      list.push pointerof(z)\n\n      ExpectOrderHelper.by_next(x, y, z, x)\n      ExpectOrderHelper.by_previous(x, z, y, x)\n    end\n  end\n\n  describe \"unshift\" do\n    it \"prepends the node into the list\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 0\n      y = TestedObject.new 1\n      z = TestedObject.new 2\n\n      list.unshift pointerof(x)\n      list.unshift pointerof(y)\n      list.unshift pointerof(z)\n\n      ExpectOrderHelper.by_next(x, z, y, x)\n      ExpectOrderHelper.by_previous(x, y, z, x)\n    end\n  end\n\n  describe \"delete\" do\n    it \"remove a node from list\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 0\n      y = TestedObject.new 1\n      z = TestedObject.new 2\n      w = TestedObject.new 3\n\n      list.push pointerof(x)\n      list.push pointerof(y)\n      list.push pointerof(z)\n      list.push pointerof(w)\n\n      list.delete pointerof(y)\n      y.next.should eq(Pointer(TestedObject).null)\n      y.previous.should eq(Pointer(TestedObject).null)\n\n      ExpectOrderHelper.by_next(x, z, w, x)\n      ExpectOrderHelper.by_previous(x, w, z, x)\n    end\n  end\n\n  describe \"#first?\" do\n    it \"returns nil when the list is empty\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      obj = list.first?\n\n      obj.should be_nil\n    end\n\n    it \"returns the head item\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 0\n      y = TestedObject.new 1\n      z = TestedObject.new 2\n\n      list.push pointerof(x)\n      list.first?.should eq(pointerof(x))\n\n      list.push pointerof(y)\n      list.first?.should eq(pointerof(x))\n\n      list.push pointerof(z)\n      list.first?.should eq(pointerof(x))\n\n      list.shift?\n      list.shift?\n      list.first?.should eq(pointerof(z))\n\n      list.shift?\n      list.first?.should be_nil\n    end\n  end\n\n  describe \"shift?\" do\n    it \"remove and return the first element\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 0\n      y = TestedObject.new 1\n      z = TestedObject.new 2\n      w = TestedObject.new 3\n\n      list.push pointerof(x)\n      list.push pointerof(y)\n      list.push pointerof(z)\n      list.push pointerof(w)\n\n      obj = list.shift?\n\n      typeof(obj).should eq(Pointer(TestedObject)?)\n\n      obj.should_not be_nil\n      obj.should eq(pointerof(x))\n      obj.not_nil!.value.next.should eq(Pointer(TestedObject).null)\n      obj.not_nil!.value.previous.should eq(Pointer(TestedObject).null)\n\n      ExpectOrderHelper.by_next(y, z, w, y)\n      ExpectOrderHelper.by_previous(y, w, z, y)\n    end\n\n    it \"return nil if list is empty\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      obj = list.shift?\n\n      obj.should be_nil\n    end\n  end\n\n  describe \"pop?\" do\n    it \"remove and return the last element\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 0\n      y = TestedObject.new 1\n      z = TestedObject.new 2\n      w = TestedObject.new 3\n\n      list.push pointerof(x)\n      list.push pointerof(y)\n      list.push pointerof(z)\n      list.push pointerof(w)\n\n      obj = list.pop?\n\n      typeof(obj).should eq(Pointer(TestedObject)?)\n\n      obj.should_not be_nil\n      obj.should eq(pointerof(w))\n      obj.not_nil!.value.next.should eq(Pointer(TestedObject).null)\n      obj.not_nil!.value.previous.should eq(Pointer(TestedObject).null)\n\n      ExpectOrderHelper.by_next(x, y, z, x)\n      ExpectOrderHelper.by_previous(x, z, y, x)\n    end\n\n    it \"return nil if list is empty\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      obj = list.pop?\n\n      obj.should be_nil\n    end\n  end\n\n  describe \"#each\" do\n    it \"iterates everything\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 1\n      y = TestedObject.new 2\n      z = TestedObject.new 4\n\n      sum = 0\n\n      list.push pointerof(x)\n      list.push pointerof(y)\n      list.push pointerof(z)\n\n      list.each do |object_ptr|\n        sum += object_ptr.value.value\n      end\n\n      sum.should eq(7)\n    end\n\n    it \"can delete while iterating\" do\n      list = Crystal::PointerLinkedList(TestedObject).new\n\n      x = TestedObject.new 1\n      y = TestedObject.new 2\n      z = TestedObject.new 4\n\n      sum = 0\n\n      list.push pointerof(x)\n      list.push pointerof(y)\n      list.push pointerof(z)\n\n      list.each do |obj|\n        list.delete(obj)\n        sum += obj.value.value\n      end\n\n      sum.should eq(7)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/pointer_pairing_heap_spec.cr",
    "content": "require \"spec\"\nrequire \"../../../src/crystal/pointer_pairing_heap\"\n\nprivate struct Node\n  getter key : Int32\n\n  include Crystal::PointerPairingHeap::Node\n\n  def initialize(@key : Int32)\n  end\n\n  def heap_compare(other : Pointer(self)) : Bool\n    key < other.value.key\n  end\n\n  def inspect(io : IO, indent = 0) : Nil\n    prv = @heap_previous\n    nxt = @heap_next\n    chd = @heap_child\n\n    indent.times { io << ' ' }\n    io << \"Node value=\" << key\n    io << \" prv=\" << prv.try(&.value.key)\n    io << \" nxt=\" << nxt.try(&.value.key)\n    io << \" chd=\" << chd.try(&.value.key)\n    io.puts\n\n    node = heap_child?\n    while node\n      node.value.inspect(io, indent + 2)\n      node = node.value.heap_next?\n    end\n  end\nend\n\ndescribe Crystal::PointerPairingHeap do\n  it \"#add\" do\n    heap = Crystal::PointerPairingHeap(Node).new\n    node1 = Node.new(1)\n    node2 = Node.new(2)\n    node2b = Node.new(2)\n    node3 = Node.new(3)\n\n    # can add distinct nodes\n    heap.add(pointerof(node3))\n    heap.add(pointerof(node1))\n    heap.add(pointerof(node2))\n\n    # can add duplicate key (different nodes)\n    heap.add(pointerof(node2b))\n\n    # can't add same node twice\n    expect_raises(ArgumentError) { heap.add(pointerof(node1)) }\n\n    # can re-add removed nodes\n    heap.delete(pointerof(node3))\n    heap.add(pointerof(node3))\n\n    heap.shift?.should eq(pointerof(node1))\n    heap.add(pointerof(node1))\n  end\n\n  it \"#shift?\" do\n    heap = Crystal::PointerPairingHeap(Node).new\n    nodes = StaticArray(Node, 10).new { |i| Node.new(i) }\n\n    # insert in random order\n    (0..9).to_a.shuffle.each do |i|\n      heap.add nodes.to_unsafe + i\n    end\n\n    # removes in ascending order\n    10.times do |i|\n      node = heap.shift?\n      node.should eq(nodes.to_unsafe + i)\n    end\n  end\n\n  it \"#delete\" do\n    heap = Crystal::PointerPairingHeap(Node).new\n    nodes = StaticArray(Node, 10).new { |i| Node.new(i) }\n\n    # insert in random order\n    (0..9).to_a.shuffle.each do |i|\n      heap.add nodes.to_unsafe + i\n    end\n\n    # remove some values\n    heap.delete(nodes.to_unsafe + 3)\n    heap.delete(nodes.to_unsafe + 7)\n    heap.delete(nodes.to_unsafe + 1)\n\n    # remove tail\n    heap.delete(nodes.to_unsafe + 9)\n\n    # remove head\n    heap.delete(nodes.to_unsafe + 0)\n\n    # repeatedly delete min\n    [2, 4, 5, 6, 8].each do |i|\n      heap.shift?.should eq(nodes.to_unsafe + i)\n    end\n    heap.shift?.null?.should be_true\n  end\n\n  it \"adds 1000 nodes then shifts them in order\" do\n    heap = Crystal::PointerPairingHeap(Node).new\n\n    nodes = StaticArray(Node, 1000).new { |i| Node.new(i) }\n    (0..999).to_a.shuffle.each { |i| heap.add(nodes.to_unsafe + i) }\n\n    i = 0\n    while node = heap.shift?\n      node.value.key.should eq(i)\n      i += 1\n    end\n    i.should eq(1000)\n\n    heap.shift?.null?.should be_true\n  end\n\n  it \"randomly shift while we add nodes\" do\n    heap = Crystal::PointerPairingHeap(Node).new\n\n    nodes = uninitialized StaticArray(Node, 1000)\n    (0..999).to_a.shuffle.each_with_index { |i, j| nodes[j] = Node.new(i) }\n\n    i = 0\n    removed = 0\n\n    # regularly calls delete-min while we insert\n    loop do\n      if rand(0..5) == 0\n        removed += 1 if heap.shift?\n      else\n        heap.add(nodes.to_unsafe + i)\n        break if (i += 1) == 1000\n      end\n    end\n\n    # exhaust the heap\n    while heap.shift?\n      removed += 1\n    end\n\n    # we must have added and removed all nodes _once_\n    i.should eq(1000)\n    removed.should eq(1000)\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/syntax_highlighter/colorize_spec.cr",
    "content": "require \"crystal/syntax_highlighter/colorize\"\nrequire \"spec\"\n\nprivate def it_highlights(code, expected, *, file = __FILE__, line = __LINE__)\n  it code.inspect, file, line do\n    highlighted = Crystal::SyntaxHighlighter::Colorize.highlight code\n    highlighted.should eq(expected), file: file, line: line\n    extracted_code = highlighted.gsub(/\\e\\[(?:\\d+;)?\\d+m/, \"\")\n    extracted_code.should eq(code), file: file, line: line\n  end\nend\n\nprivate def it_highlights!(code, expected = code, *, file = __FILE__, line = __LINE__)\n  it code.inspect, file, line do\n    highlighted = Crystal::SyntaxHighlighter::Colorize.highlight! code\n    highlighted.should eq(expected), file: file, line: line\n    extracted_code = highlighted.gsub(/\\e\\[(?:\\d+;)?\\d+m/, \"\")\n    extracted_code.should eq(code), file: file, line: line\n\n    no_colorized = String.build do |io|\n      colorize = Crystal::SyntaxHighlighter::Colorize.new io, ::Colorize.with.toggle(false)\n      colorize.highlight(code)\n    end rescue code\n    no_colorized.should eq(code), file: file, line: line\n  end\nend\n\ndescribe Crystal::SyntaxHighlighter::Colorize do\n  describe \".highlight\" do\n    it_highlights %(foo = bar(\"baz\\#{PI + 1}\") # comment), %(foo \\e[91m=\\e[39m bar(\\e[93m\"baz\\#{\\e[39m\\e[36mPI\\e[39m \\e[91m+\\e[39m \\e[35m1\\e[39m\\e[93m}\"\\e[39m) \\e[90m# comment\\e[39m)\n\n    it_highlights \"foo\", \"foo\"\n    it_highlights \"foo bar\", \"foo bar\"\n    it_highlights \"foo\\nbar\", \"foo\\nbar\"\n\n    it_highlights \"# foo\", %(\\e[90m# foo\\e[39m)\n    it_highlights \"# bar\\n\", %(\\e[90m# bar\\e[39m\\n)\n    it_highlights \"# foo\\n# bar\\n\", %(\\e[90m# foo\\e[39m\\n\\e[90m# bar\\e[39m\\n)\n    it_highlights %(# <\">), %(\\e[90m# <\">\\e[39m)\n\n    it_highlights \"42\", %(\\e[35m42\\e[39m)\n    it_highlights \"3.14\", %(\\e[35m3.14\\e[39m)\n    it_highlights \"123_i64\", %(\\e[35m123_i64\\e[39m)\n\n    it_highlights \"'a'\", %(\\e[93m'a'\\e[39m)\n    it_highlights \"'<'\", %(\\e[93m'<'\\e[39m)\n\n    it_highlights \":foo\", %(\\e[35m:foo\\e[39m)\n    it_highlights %(:\"foo\"), %(\\e[35m:\"foo\"\\e[39m)\n\n    it_highlights \"Foo\", %(\\e[36mFoo\\e[39m)\n    it_highlights \"Foo::Bar\", %(\\e[36mFoo\\e[39m\\e[36m::\\e[39m\\e[36mBar\\e[39m)\n\n    %w(\n      def if else elsif end class module include\n      extend while until do yield return unless next break\n      begin lib fun type struct union enum macro out require\n      case when select then of rescue ensure is_a? alias sizeof alignof\n      as as? typeof for in with super private asm\n      nil? abstract pointerof\n      protected uninitialized instance_sizeof instance_alignof offsetof\n      annotation verbatim\n    ).each do |kw|\n      it_highlights kw, %(\\e[91m#{kw}\\e[39m)\n    end\n\n    it_highlights \"self\", %(\\e[34mself\\e[39m)\n\n    %w(true false nil).each do |lit|\n      it_highlights lit, %(\\e[35m#{lit}\\e[39m)\n    end\n\n    it_highlights \"def foo\", %(\\e[91mdef\\e[39m \\e[92mfoo\\e[39m)\n\n    %w(\n      [] []? []= <=>\n      + - * /\n      == < <= > >= != =~ !~\n      & | ^ ~ ** >> << %\n    ).each do |op|\n      it_highlights %(def #{op}), %(\\e[91mdef\\e[39m \\e[92m#{op}\\e[39m)\n    end\n\n    it_highlights %(def //), %(\\e[91mdef\\e[39m \\e[92m/\\e[39m\\e[92m/\\e[39m)\n\n    %w(\n      + - * &+ &- &* &** / // = == < <= > >= ! != =~ !~ & | ^ ~ **\n      >> << % [] []? []= <=> === && ||\n      += -= *= /= //= &= |= ^= **= >>= <<= %= &+= &-= &*= &&= ||=\n    ).each do |op|\n      it_highlights \"1 #{op} 2\", %(\\e[35m1\\e[39m \\e[91m#{op}\\e[39m \\e[35m2\\e[39m)\n    end\n\n    it_highlights %(1/2), %(\\e[35m1\\e[39m\\e[91m/\\e[39m\\e[35m2\\e[39m)\n    it_highlights %(1 /2), %(\\e[35m1\\e[39m \\e[91m/\\e[39m\\e[35m2\\e[39m)\n    it_highlights %(1/ 2), %(\\e[35m1\\e[39m\\e[91m/\\e[39m \\e[35m2\\e[39m)\n\n    it_highlights %(a/b), %(a\\e[91m/\\e[39mb)\n    it_highlights %(a/ b), %(a\\e[91m/\\e[39m b)\n    it_highlights %(a / b), %(a \\e[91m/\\e[39m b)\n\n    it_highlights %(a /b/), %(a \\e[93m/b/\\e[39m)\n\n    it_highlights %($1), %($1)\n    it_highlights %($2?), %($2?)\n    it_highlights %($?), %($?)\n    it_highlights %($~), %($~)\n\n    it_highlights %(\"foo\"), %(\\e[93m\"foo\"\\e[39m)\n    it_highlights %(\"<>\"), %(\\e[93m\"<>\"\\e[39m)\n    it_highlights %(\"foo\\#{bar}baz\"), %(\\e[93m\"foo\\#{\\e[39mbar\\e[93m}baz\"\\e[39m)\n    it_highlights %(\"foo\\#{[1, bar, \"str\"]}baz\"), %(\\e[93m\"foo\\#{\\e[39m[\\e[35m1\\e[39m, bar, \\e[93m\"str\"\\e[39m]\\e[93m}baz\"\\e[39m)\n    it_highlights %(\"nest1\\#{foo + \"nest2\\#{1 + 1}bar\"}baz\"), %(\\e[93m\"nest1\\#{\\e[39mfoo \\e[91m+\\e[39m \\e[93m\"nest2\\#{\\e[39m\\e[35m1\\e[39m \\e[91m+\\e[39m \\e[35m1\\e[39m\\e[93m}bar\"\\e[39m\\e[93m}baz\"\\e[39m)\n    it_highlights \"/foo/xim\", %(\\e[93m/foo/\\e[39mxim)\n    it_highlights \"`foo`\", %(\\e[93m`foo`\\e[39m)\n    it_highlights \"%(foo)\", %(\\e[93m%(foo)\\e[39m)\n    it_highlights \"%<foo>\", %(\\e[93m%<foo>\\e[39m)\n    it_highlights \"%q(foo)\", %(\\e[93m%q(foo)\\e[39m)\n    it_highlights \"%Q(foo)\", %(\\e[93m%Q(foo)\\e[39m)\n    it_highlights \"%r(foo)xim\", %(\\e[93m%r(foo)\\e[39mxim)\n    it_highlights \"%x(foo)\", %(\\e[93m%x(foo)\\e[39m)\n\n    it_highlights \"%w(foo bar baz)\", %(\\e[93m%w(foo bar baz)\\e[39m)\n    it_highlights \"%w(foo  bar\\n  baz)\", %(\\e[93m%w(foo  bar\\n  baz)\\e[39m)\n    it_highlights \"%w<foo bar baz>\", %(\\e[93m%w<foo bar baz>\\e[39m)\n    it_highlights \"%i(foo bar baz)\", %(\\e[93m%i(foo bar baz)\\e[39m)\n\n    it_highlights \"Set{1, 2, 3}\", %(\\e[36mSet\\e[39m{\\e[35m1\\e[39m, \\e[35m2\\e[39m, \\e[35m3\\e[39m})\n\n    it_highlights \"foo(/Name: /)\", %(foo(\\e[93m/Name: /\\e[39m))\n    it_highlights \"foo[/Name: /]\", %(foo[\\e[93m/Name: /\\e[39m])\n    it_highlights \"Foo{/Name: /}\", %(\\e[36mFoo\\e[39m{\\e[93m/Name: /\\e[39m})\n\n    it_highlights <<-CRYSTAL, <<-ANSI\n      foo, bar = <<-FOO, <<-BAR\n        foo\n        FOO\n        bar\n        BAR\n      CRYSTAL\n      foo, bar \\e[91m=\\e[39m \\e[93m<<-FOO\\e[39m, \\e[93m<<-BAR\\e[39m\n      \\e[93m  foo\n        FOO\\e[39m\n      \\e[93m  bar\n        BAR\\e[39m\n      ANSI\n  end\n\n  describe \".highlight!\" do\n    it_highlights! %(foo = bar(\"baz\\#{PI + 1}\") # comment), %(foo \\e[91m=\\e[39m bar(\\e[93m\"baz\\#{\\e[39m\\e[36mPI\\e[39m \\e[91m+\\e[39m \\e[35m1\\e[39m\\e[93m}\"\\e[39m) \\e[90m# comment\\e[39m)\n\n    it_highlights! <<-CRYSTAL\n      foo, bar = <<-FOO, <<-BAR\n        foo\n        FOO\n      CRYSTAL\n\n    it_highlights! <<-CRYSTAL\n      foo, bar = <<-FOO, <<-BAR\n        foo\n      CRYSTAL\n\n    it_highlights! \"\\\"foo\"\n    it_highlights! \"%w[foo\"\n    it_highlights! \"%i[foo\"\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/syntax_highlighter/html_spec.cr",
    "content": "require \"xml\"\nrequire \"crystal/syntax_highlighter/html\"\nrequire \"spec\"\n\nprivate def it_highlights(code, expected, *, file = __FILE__, line = __LINE__)\n  it code.inspect, file, line do\n    highlighted = Crystal::SyntaxHighlighter::HTML.highlight code\n    highlighted.should eq(expected), file: file, line: line\n    doc = XML.parse_html highlighted\n    doc.content.should eq(code), file: file, line: line\n  end\nend\n\nprivate def it_highlights!(code, expected = code, *, file = __FILE__, line = __LINE__)\n  it code.inspect, file, line do\n    highlighted = Crystal::SyntaxHighlighter::HTML.highlight! code\n    highlighted.should eq(expected), file: file, line: line\n    doc = XML.parse_html highlighted\n    doc.content.should eq(code), file: file, line: line\n  end\nend\n\ndescribe Crystal::SyntaxHighlighter::HTML do\n  describe \".highlight\" do\n    it_highlights %(foo = bar(\"baz\\#{PI + 1}\") # comment), \"foo <span class=\\\"o\\\">=</span> bar(<span class=\\\"s\\\">&quot;baz</span><span class=\\\"i\\\">\\#{</span><span class=\\\"t\\\">PI</span> <span class=\\\"o\\\">+</span> <span class=\\\"n\\\">1</span><span class=\\\"i\\\">}</span><span class=\\\"s\\\">&quot;</span>) <span class=\\\"c\\\"># comment</span>\"\n\n    it_highlights \"foo\", \"foo\"\n    it_highlights \"foo bar\", \"foo bar\"\n    it_highlights \"foo\\nbar\", \"foo\\nbar\"\n\n    it_highlights \"# foo\", %(<span class=\"c\"># foo</span>)\n    it_highlights \"# bar\\n\", %(<span class=\"c\"># bar</span>\\n)\n    it_highlights \"# foo\\n# bar\\n\", %(<span class=\"c\"># foo</span>\\n<span class=\"c\"># bar</span>\\n)\n    it_highlights %(# <\">), %(<span class=\"c\"># &lt;&quot;&gt;</span>)\n\n    it_highlights \"42\", %(<span class=\"n\">42</span>)\n    it_highlights \"3.14\", %(<span class=\"n\">3.14</span>)\n    it_highlights \"123_i64\", %(<span class=\"n\">123_i64</span>)\n\n    it_highlights \"'a'\", %(<span class=\"s\">&#39;a&#39;</span>)\n    it_highlights \"'<'\", %(<span class=\"s\">&#39;&lt;&#39;</span>)\n\n    it_highlights \":foo\", %(<span class=\"n\">:foo</span>)\n    it_highlights %(:\"foo\"), %(<span class=\"n\">:&quot;foo&quot;</span>)\n\n    it_highlights \"Foo\", %(<span class=\"t\">Foo</span>)\n    it_highlights \"Foo::Bar\", %(<span class=\"t\">Foo</span><span class=\"t\">::</span><span class=\"t\">Bar</span>)\n\n    %w(\n      def if else elsif end class module include\n      extend while until do yield return unless next break\n      begin lib fun type struct union enum macro out require\n      case when select then of rescue ensure is_a? alias sizeof alignof\n      as as? typeof for in with self super private asm\n      nil? abstract pointerof\n      protected uninitialized instance_sizeof instance_alignof offsetof\n      annotation verbatim\n    ).each do |kw|\n      it_highlights kw, %(<span class=\"k\">#{kw}</span>)\n    end\n\n    it_highlights \"self\", %(<span class=\"k\">self</span>)\n\n    %w(true false nil).each do |lit|\n      it_highlights lit, %(<span class=\"n\">#{lit}</span>)\n    end\n\n    it_highlights \"def foo\", %(<span class=\"k\">def</span> <span class=\"m\">foo</span>)\n\n    %w(\n      [] []? []= <=>\n      + - * /\n      == < <= > >= != =~ !~\n      & | ^ ~ ** >> << %\n    ).each do |op|\n      it_highlights %(def #{op}), %(<span class=\"k\">def</span> <span class=\"m\">#{HTML.escape(op)}</span>)\n    end\n\n    it_highlights %(def //), %(<span class=\"k\">def</span> <span class=\"m\">/</span><span class=\"m\">/</span>)\n\n    %w(\n      + - * &+ &- &* &** / // = == < <= > >= ! != =~ !~ & | ^ ~ **\n      >> << % [] []? []= <=> === && ||\n      += -= *= /= //= &= |= ^= **= >>= <<= %= &+= &-= &*= &&= ||=\n    ).each do |op|\n      it_highlights \"1 #{op} 2\", %(<span class=\"n\">1</span> <span class=\"o\">#{HTML.escape(op)}</span> <span class=\"n\">2</span>)\n    end\n\n    it_highlights %(1/2), %(<span class=\"n\">1</span><span class=\"o\">/</span><span class=\"n\">2</span>)\n    it_highlights %(1 /2), %(<span class=\"n\">1</span> <span class=\"o\">/</span><span class=\"n\">2</span>)\n    it_highlights %(1/ 2), %(<span class=\"n\">1</span><span class=\"o\">/</span> <span class=\"n\">2</span>)\n\n    it_highlights %(a/b), %(a<span class=\"o\">/</span>b)\n    it_highlights %(a/ b), %(a<span class=\"o\">/</span> b)\n    it_highlights %(a / b), %(a <span class=\"o\">/</span> b)\n\n    it_highlights %(a /b/), %(a <span class=\"s\">/b/</span>)\n\n    it_highlights %($1), %($1)\n    it_highlights %($2?), %($2?)\n    it_highlights %($?), %($?)\n    it_highlights %($~), %($~)\n\n    it_highlights %(\"foo\"), %(<span class=\"s\">&quot;foo&quot;</span>)\n    it_highlights %(\"<>\"), %(<span class=\"s\">&quot;&lt;&gt;&quot;</span>)\n    it_highlights %(\"foo\\#{bar}baz\"), %(<span class=\"s\">&quot;foo</span><span class=\"i\">\\#{</span>bar<span class=\"i\">}</span><span class=\"s\">baz&quot;</span>)\n    it_highlights %(\"foo\\#{[1, bar, \"str\"]}baz\"), %(<span class=\"s\">&quot;foo</span><span class=\"i\">\\#{</span>[<span class=\"n\">1</span>, bar, <span class=\"s\">&quot;str&quot;</span>]<span class=\"i\">}</span><span class=\"s\">baz&quot;</span>)\n    it_highlights %(\"nest1\\#{foo + \"nest2\\#{1 + 1}bar\"}baz\"), %(<span class=\"s\">&quot;nest1</span><span class=\"i\">\\#{</span>foo <span class=\"o\">+</span> <span class=\"s\">&quot;nest2</span><span class=\"i\">\\#{</span><span class=\"n\">1</span> <span class=\"o\">+</span> <span class=\"n\">1</span><span class=\"i\">}</span><span class=\"s\">bar&quot;</span><span class=\"i\">}</span><span class=\"s\">baz&quot;</span>)\n    it_highlights \"/foo/xim\", %(<span class=\"s\">/foo/</span>xim)\n    it_highlights \"`foo`\", %(<span class=\"s\">`foo`</span>)\n    it_highlights \"%(foo)\", %(<span class=\"s\">%(foo)</span>)\n    it_highlights \"%<foo>\", %(<span class=\"s\">%&lt;foo&gt;</span>)\n    it_highlights \"%q(foo)\", %(<span class=\"s\">%q(foo)</span>)\n    it_highlights \"%Q(foo)\", %(<span class=\"s\">%Q(foo)</span>)\n    it_highlights \"%r(foo)xim\", %(<span class=\"s\">%r(foo)</span>xim)\n    it_highlights \"%x(foo)\", %(<span class=\"s\">%x(foo)</span>)\n\n    it_highlights \"%w(foo bar baz)\", %(<span class=\"s\">%w(foo bar baz)</span>)\n    it_highlights \"%w(foo  bar\\n  baz)\", %(<span class=\"s\">%w(foo  bar\\n  baz)</span>)\n    it_highlights \"%w<foo bar baz>\", %(<span class=\"s\">%w&lt;foo bar baz&gt;</span>)\n    it_highlights \"%i(foo bar baz)\", %(<span class=\"s\">%i(foo bar baz)</span>)\n\n    it_highlights \"Set{1, 2, 3}\", %(<span class=\"t\">Set</span>{<span class=\"n\">1</span>, <span class=\"n\">2</span>, <span class=\"n\">3</span>})\n\n    it_highlights \"foo(/Name: /)\", %(foo(<span class=\"s\">/Name: /</span>))\n    it_highlights \"foo[/Name: /]\", %(foo[<span class=\"s\">/Name: /</span>])\n    it_highlights \"Foo{/Name: /}\", %(<span class=\"t\">Foo</span>{<span class=\"s\">/Name: /</span>})\n\n    it_highlights <<-CRYSTAL, <<-HTML\n    foo, bar = <<-FOO, <<-BAR\n      foo\n      FOO\n      bar\n      BAR\n    CRYSTAL\n    foo, bar <span class=\"o\">=</span> <span class=\"s\">&lt;&lt;-FOO</span>, <span class=\"s\">&lt;&lt;-BAR</span>\n    <span class=\"s\">  foo\n      FOO</span>\n    <span class=\"s\">  bar\n      BAR</span>\n    HTML\n  end\n\n  describe \"#highlight!\" do\n    it_highlights! %(foo = bar(\"baz\\#{PI + 1}\") # comment), \"foo <span class=\\\"o\\\">=</span> bar(<span class=\\\"s\\\">&quot;baz</span><span class=\\\"i\\\">\\#{</span><span class=\\\"t\\\">PI</span> <span class=\\\"o\\\">+</span> <span class=\\\"n\\\">1</span><span class=\\\"i\\\">}</span><span class=\\\"s\\\">&quot;</span>) <span class=\\\"c\\\"># comment</span>\"\n\n    it_highlights! <<-CRYSTAL, <<-HTML\n      foo, bar = <<-FOO, <<-BAR\n        foo\n        FOO\n      CRYSTAL\n      foo, bar = &lt;&lt;-FOO, &lt;&lt;-BAR\n        foo\n        FOO\n      HTML\n\n    it_highlights! <<-CRYSTAL, <<-HTML\n      foo, bar = <<-FOO, <<-BAR\n        foo\n      CRYSTAL\n      foo, bar = &lt;&lt;-FOO, &lt;&lt;-BAR\n        foo\n      HTML\n\n    it_highlights! \"\\\"foo\", \"&quot;foo\"\n    it_highlights! \"%w[foo\"\n    it_highlights! \"%i[foo\"\n  end\n\n  # fix for https://forum.crystal-lang.org/t/question-about-the-crystal-syntax-highlighter/7283\n  it_highlights %q(/#{l[\"\"]}/\n    \"\\\\n\"), %(<span class=\"s\">/</span><span class=\"i\">\\#{</span>l[<span class=\"s\">&quot;&quot;</span>]<span class=\"i\">}</span><span class=\"s\">/</span>\\n    <span class=\"s\">&quot;\\\\\\\\n&quot;</span>)\nend\n"
  },
  {
    "path": "spec/std/crystal/system/cpucount_spec.cr",
    "content": "require \"spec\"\n\ndescribe Crystal::System do\n  it \"#effective_cpu_count\" do\n    # smoke test: must compile and must run\n    Crystal::System.effective_cpu_count\n  end\nend\n"
  },
  {
    "path": "spec/std/crystal/system_spec.cr",
    "content": "require \"../spec_helper\"\n\nprivate def printf_to_s(format, *args)\n  io = IO::Memory.new\n  Crystal::System.printf(format, *args) do |bytes|\n    io.write_string(bytes)\n  end\n  io.to_s\nend\n\ndescribe \"Crystal::System\" do\n  describe \".printf\" do\n    it \"works\" do\n      printf_to_s(\"abcde\").should eq(\"abcde\")\n    end\n\n    it \"supports %d\" do\n      printf_to_s(\"%d,%d,%d,%d,%d\", 0, 1234, Int32::MAX, Int32::MIN, UInt64::MAX).should eq(\"0,1234,2147483647,-2147483648,-1\")\n    end\n\n    it \"supports %u\" do\n      printf_to_s(\"%u,%u,%u,%u,%u\", 0, 1234, UInt32::MAX, Int32::MIN, UInt64::MAX).should eq(\"0,1234,4294967295,2147483648,4294967295\")\n    end\n\n    it \"supports %x\" do\n      printf_to_s(\"%x,%x,%x,%x,%x\", 0, 0x1234, UInt32::MAX, Int32::MIN, UInt64::MAX).should eq(\"0,1234,ffffffff,80000000,ffffffff\")\n    end\n\n    # TODO: investigate why this prints `(???)`\n    pending_interpreted \"supports %p\" do\n      printf_to_s(\"%p,%p,%p\", Pointer(Void).new(0x0), Pointer(Void).new(0x1234), Pointer(Void).new(UInt64::MAX)).should eq(\"0x0,0x1234,0xffffffffffffffff\")\n    end\n\n    it \"supports %s\" do\n      printf_to_s(\"%s,%s,%s\", \"abc\\0def\", \"ghi\".to_unsafe, Pointer(UInt8).null).should eq(\"abc\\0def,ghi,(null)\")\n    end\n\n    # BUG: missing downcast_distinct from Tuple(Int64 | UInt64, Int64 | UInt64, Int64 | UInt64, Int64 | UInt64) to Tuple(Int64, Int64, Int64, Int64)\n    pending_interpreted \"supports %l width\" do\n      values = {LibC::Long::MIN, LibC::Long::MAX, LibC::LongLong::MIN, LibC::LongLong::MAX}\n      printf_to_s(\"%ld,%ld,%lld,%lld\", *values).should eq(values.join(','))\n\n      values = {LibC::ULong::MIN, LibC::ULong::MAX, LibC::ULongLong::MIN, LibC::ULongLong::MAX}\n      printf_to_s(\"%lu,%lu,%llu,%llu\", *values).should eq(values.join(','))\n      printf_to_s(\"%lx,%lx,%llx,%llx\", *values).should eq(values.join(',', &.to_s(16)))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/csv/csv_build_spec.cr",
    "content": "require \"spec\"\nrequire \"csv\"\nrequire \"spec/helpers/string\"\n\ndescribe CSV do\n  describe \"build\" do\n    it \"builds two rows\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << \"one\"\n          row << \"two\"\n        end\n        csv.row do |row|\n          row << \"three\"\n          row << \"four\"\n        end\n      end, \"one,two\\nthree,four\\n\")\n    end\n\n    it \"builds with numbers\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << 1\n          row << 2\n        end\n        csv.row do |row|\n          row << 3\n          row << 4\n        end\n      end, \"1,2\\n3,4\\n\")\n    end\n\n    it \"builds with commas\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << %(hello,world)\n        end\n      end, %(\"hello,world\"\\n))\n    end\n\n    it \"builds with custom separator\" do\n      assert_prints(CSV.build(separator: ';') do |csv|\n        csv.row do |row|\n          row << \"one\"\n          row << \"two\"\n          row << \"thr;ee\"\n        end\n      end, %(one;two;\"thr;ee\"\\n))\n    end\n\n    it \"builds with quotes\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << %(he said \"no\")\n        end\n      end, %(\"he said \"\"no\"\"\"\\n))\n    end\n\n    it \"builds with custom quote character\" do\n      assert_prints(CSV.build(quote_char: '\\'') do |csv|\n        csv.row do |row|\n          row << %(he said 'no')\n        end\n      end, %('he said ''no'''\\n))\n    end\n\n    it \"builds row from enumerable\" do\n      assert_prints(CSV.build do |csv|\n        csv.row [1, 2, 3]\n      end, \"1,2,3\\n\")\n    end\n\n    it \"builds row from splat\" do\n      assert_prints(CSV.build do |csv|\n        csv.row 1, 2, 3\n      end, \"1,2,3\\n\")\n    end\n\n    it \"skips inside row\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << 1\n          row.skip_cell\n          row << 2\n        end\n      end, \"1,,2\\n\")\n    end\n\n    it \"concats enumerable to row\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << 1\n          row.concat [2, 3, 4]\n          row << 5\n        end\n      end, \"1,2,3,4,5\\n\")\n    end\n\n    it \"concats splat to row\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << 1\n          row.concat 2, 3, 4\n          row << 5\n        end\n      end, \"1,2,3,4,5\\n\")\n    end\n\n    it \"builds with commas\" do\n      assert_prints(CSV.build do |csv|\n        csv.row do |row|\n          row << \" , \"\n          row << \" , \"\n        end\n      end, %(\" , \",\" , \"\\n))\n    end\n\n    it \"builds with quoting\" do\n      assert_prints(CSV.build(quoting: CSV::Builder::Quoting::NONE) do |csv|\n        csv.row 1, \"doesn't\", \" , \", %(he said \"no\")\n      end, %(1,doesn't, , ,he said \"no\"\\n))\n\n      assert_prints(CSV.build(quoting: CSV::Builder::Quoting::RFC) do |csv|\n        csv.row 1, \"doesn't\", \" , \", %(he said \"no\")\n      end, %(1,doesn't,\" , \",\"he said \"\"no\"\"\"\\n))\n\n      assert_prints(CSV.build(quoting: CSV::Builder::Quoting::ALL) do |csv|\n        csv.row 1, \"doesn't\", \" , \", %(he said \"no\")\n      end, %(\"1\",\"doesn't\",\" , \",\"he said \"\"no\"\"\"\\n))\n    end\n\n    it \"builds with inside quoted chars and symbols\" do\n      assert_prints(CSV.build(quoting: CSV::Builder::Quoting::NONE) do |csv|\n        csv.row 'c', '\\'', '\"', :sym, :\"s'm\", :\"s\\\"m\"\n      end, %(c,',\",sym,s'm,s\"m\\n))\n\n      assert_prints(CSV.build(quoting: CSV::Builder::Quoting::RFC) do |csv|\n        csv.row 'c', '\\'', '\"', :sym, :\"s'm\", :\"s\\\"m\"\n      end, %(c,',\"\"\"\",sym,s'm,\"s\"\"m\"\\n))\n\n      assert_prints(CSV.build(quoting: CSV::Builder::Quoting::ALL) do |csv|\n        csv.row 'c', '\\'', '\"', :sym, :\"s'm\", :\"s\\\"m\"\n      end, %(\"c\",\"'\",\"\"\"\",\"sym\",\"s'm\",\"s\"\"m\"\\n))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/csv/csv_lex_spec.cr",
    "content": "require \"spec\"\nrequire \"csv\"\n\nclass CSV::Lexer\n  def expect_cell(value, file = __FILE__, line = __LINE__)\n    token = next_token\n    token.kind.should eq(CSV::Token::Kind::Cell), file: file, line: line\n    token.value.should eq(value), file: file, line: line\n  end\n\n  def expect_eof(file = __FILE__, line = __LINE__)\n    next_token.kind.should eq(CSV::Token::Kind::Eof), file: file, line: line\n  end\n\n  def expect_newline(file = __FILE__, line = __LINE__)\n    next_token.kind.should eq(CSV::Token::Kind::Newline), file: file, line: line\n  end\nend\n\ndescribe CSV do\n  describe \"lex\" do\n    it \"lexes two columns\" do\n      lexer = CSV::Lexer.new(\"hello,world\")\n      lexer.expect_cell \"hello\"\n      lexer.expect_cell \"world\"\n      lexer.expect_eof\n    end\n\n    it \"lexes two columns with two rows\" do\n      lexer = CSV::Lexer.new(\"hello,world\\nfoo,bar\")\n      lexer.expect_cell \"hello\"\n      lexer.expect_cell \"world\"\n      lexer.expect_newline\n      lexer.expect_cell \"foo\"\n      lexer.expect_cell \"bar\"\n      lexer.expect_eof\n    end\n\n    it \"lexes two columns with two rows with \\r\\n\" do\n      lexer = CSV::Lexer.new(\"hello,world\\r\\nfoo,bar\")\n      lexer.expect_cell \"hello\"\n      lexer.expect_cell \"world\"\n      lexer.expect_newline\n      lexer.expect_cell \"foo\"\n      lexer.expect_cell \"bar\"\n      lexer.expect_eof\n    end\n\n    it \"lexes two empty columns\" do\n      lexer = CSV::Lexer.new(\",\")\n      lexer.expect_cell \"\"\n      lexer.expect_cell \"\"\n      lexer.expect_eof\n    end\n\n    it \"lexes last empty column\" do\n      lexer = CSV::Lexer.new(\"foo,\")\n      lexer.expect_cell \"foo\"\n      lexer.expect_cell \"\"\n      lexer.expect_eof\n    end\n\n    it \"lexes with empty columns\" do\n      lexer = CSV::Lexer.new(\"foo,,bar\")\n      lexer.expect_cell \"foo\"\n      lexer.expect_cell \"\"\n      lexer.expect_cell \"bar\"\n      lexer.expect_eof\n    end\n\n    it \"lexes with whitespace\" do\n      lexer = CSV::Lexer.new(\"  foo  ,  bar  \")\n      lexer.expect_cell \"  foo  \"\n      lexer.expect_cell \"  bar  \"\n      lexer.expect_eof\n    end\n\n    it \"lexes two with quotes\" do\n      lexer = CSV::Lexer.new(%(\"hello\",\"world\"))\n      lexer.expect_cell \"hello\"\n      lexer.expect_cell \"world\"\n      lexer.expect_eof\n    end\n\n    it \"lexes two with inner quotes\" do\n      lexer = CSV::Lexer.new(%(\"hel\"\"lo\",\"wor\"\"ld\"))\n      lexer.expect_cell %(hel\"lo)\n      lexer.expect_cell %(wor\"ld)\n      lexer.expect_eof\n    end\n\n    it \"lexes with comma inside quote\" do\n      lexer = CSV::Lexer.new(%(\"foo,bar\"))\n      lexer.expect_cell \"foo,bar\"\n      lexer.expect_eof\n    end\n\n    it \"lexes with newline inside quote\" do\n      lexer = CSV::Lexer.new(%(\"foo\\nbar\"))\n      lexer.expect_cell \"foo\\nbar\"\n      lexer.expect_eof\n    end\n\n    it \"lexes newline followed by eof\" do\n      lexer = CSV::Lexer.new(\"hello,world\\n\")\n      lexer.expect_cell \"hello\"\n      lexer.expect_cell \"world\"\n      lexer.expect_newline\n      lexer.expect_eof\n    end\n\n    it \"lexes with a given separator\" do\n      lexer = CSV::Lexer.new(\"hello;world\\n\", separator: ';')\n      lexer.expect_cell \"hello\"\n      lexer.expect_cell \"world\"\n      lexer.expect_newline\n      lexer.expect_eof\n    end\n\n    it \"lexes with a given quote char\" do\n      lexer = CSV::Lexer.new(\"'hello,world'\\n\", quote_char: '\\'')\n      lexer.expect_cell \"hello,world\"\n      lexer.expect_newline\n      lexer.expect_eof\n    end\n\n    it \"raises if single quote in the middle\" do\n      expect_raises CSV::MalformedCSVError, \"Unexpected quote at line 1, column 4\" do\n        lexer = CSV::Lexer.new %(hel\"lo)\n        lexer.next_token\n      end\n    end\n\n    it \"raises if command, newline or end doesn't follow quote\" do\n      expect_raises CSV::MalformedCSVError, \"Expecting comma, newline or end, not 'a' at line 1, column 6\" do\n        lexer = CSV::Lexer.new %(\"hel\"a)\n        lexer.next_token\n      end\n    end\n\n    it \"raises on unclosed quote\" do\n      expect_raises CSV::MalformedCSVError, \"Unclosed quote at line 1, column 5\" do\n        lexer = CSV::Lexer.new %(\"foo)\n        lexer.next_token\n      end\n    end\n\n    it \"doesn't consume char after \\\\n (#11172)\" do\n      io = IO::Memory.new(\"a\\nx\")\n      lexer = CSV::Lexer.new(io)\n      lexer.expect_cell \"a\"\n      lexer.expect_newline\n      io.pos.should eq(2)\n    end\n\n    it \"doesn't consume char after \\\\r (#11172)\" do\n      io = IO::Memory.new(\"a\\r\\nx\")\n      lexer = CSV::Lexer.new(io)\n      lexer.expect_cell \"a\"\n      io.pos.should eq(2)\n      lexer.expect_newline\n      lexer.expect_cell \"x\"\n      lexer.expect_eof\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/csv/csv_parse_spec.cr",
    "content": "require \"spec\"\nrequire \"csv\"\n\ndescribe CSV do\n  describe \"parse\" do\n    it \"parses empty string\" do\n      CSV.parse(\"\").should eq([] of String)\n    end\n\n    it \"parses one simple row\" do\n      CSV.parse(\"hello,world\").should eq([[\"hello\", \"world\"]])\n    end\n\n    it \"parses one row with spaces\" do\n      CSV.parse(\"   hello   ,   world  \").should eq([[\"   hello   \", \"   world  \"]])\n    end\n\n    it \"parses two rows\" do\n      CSV.parse(\"hello,world\\ngood,bye\").should eq([\n        [\"hello\", \"world\"],\n        [\"good\", \"bye\"],\n      ])\n    end\n\n    it \"parses two rows with the last one having a newline\" do\n      CSV.parse(\"hello,world\\ngood,bye\\n\").should eq([\n        [\"hello\", \"world\"],\n        [\"good\", \"bye\"],\n      ])\n    end\n\n    it \"parses with quote\" do\n      CSV.parse(%(\"hello\",\"world\")).should eq([[\"hello\", \"world\"]])\n    end\n\n    it \"parses with quote and newline\" do\n      CSV.parse(%(\"hello\",\"world\"\\nfoo)).should eq([[\"hello\", \"world\"], [\"foo\"]])\n    end\n\n    it \"parses with double quote\" do\n      CSV.parse(%(\"hel\"\"lo\",\"wor\"\"ld\")).should eq([[%(hel\"lo), %(wor\"ld)]])\n    end\n\n    it \"parses some commas\" do\n      CSV.parse(%(,,)).should eq([[\"\", \"\", \"\"]])\n    end\n\n    it \"parses empty quoted string\" do\n      CSV.parse(%(\"\",\"\")).should eq([[\"\", \"\"]])\n    end\n\n    it \"raises if single quote in the middle\" do\n      expect_raises CSV::MalformedCSVError, \"Unexpected quote at line 1, column 4\" do\n        CSV.parse(%(hel\"lo))\n      end\n    end\n\n    it \"raises if command, newline or end doesn't follow quote\" do\n      expect_raises CSV::MalformedCSVError, \"Expecting comma, newline or end, not 'a' at line 2, column 6\" do\n        CSV.parse(%(foo\\n\"hel\"a))\n      end\n    end\n\n    it \"raises if command, newline or end doesn't follow quote (2)\" do\n      expect_raises CSV::MalformedCSVError, \"Expecting comma, newline or end, not 'a' at line 2, column 6\" do\n        CSV.parse(%(\\n\"hel\"a))\n      end\n    end\n\n    it \"parses from IO\" do\n      CSV.parse(IO::Memory.new(%(\"hel\"\"lo\",world))).should eq([[%(hel\"lo), %(world)]])\n    end\n\n    it \"takes an optional separator argument\" do\n      CSV.parse(\"foo;bar\", separator: ';').should eq([[\"foo\", \"bar\"]])\n    end\n\n    it \"takes an optional quote char argument\" do\n      CSV.parse(\"'foo,bar'\", quote_char: '\\'').should eq([[\"foo,bar\"]])\n    end\n  end\n\n  it \"parses row by row\" do\n    parser = CSV::Parser.new(\"hello,world\\ngood,bye\\n\")\n    parser.next_row.should eq(%w(hello world))\n    parser.next_row.should eq(%w(good bye))\n    parser.next_row.should be_nil\n  end\n\n  it \"does CSV.each_row\" do\n    sum = 0\n    CSV.each_row(\"1,2\\n3,4\\n\") do |row|\n      sum += row.sum(&.to_i)\n    end.should be_nil\n    sum.should eq(10)\n  end\n\n  it \"does CSV.each_row with separator and quotes\" do\n    sum = 0\n    CSV.each_row(\"1\\t'2'\\n3\\t4\\n\", '\\t', '\\'') do |row|\n      sum += row.sum(&.to_i)\n    end.should be_nil\n    sum.should eq(10)\n  end\n\n  it \"gets row iterator\" do\n    iter = CSV.each_row(\"1,2\\n3,4\\n\")\n    iter.next.should eq([\"1\", \"2\"])\n    iter.next.should eq([\"3\", \"4\"])\n    iter.next.should be_a(Iterator::Stop)\n  end\nend\n"
  },
  {
    "path": "spec/std/csv/csv_spec.cr",
    "content": "require \"spec\"\nrequire \"csv\"\n\nprivate def new_csv(headers = false, strip = false)\n  CSV.new %(one, two\\n1, 2\\n3, 4\\n5), headers: headers, strip: strip\nend\n\ndescribe CSV do\n  it \"gets headers\" do\n    csv = new_csv headers: true\n    csv.headers.should eq(%w(one two))\n  end\n\n  it \"works without headers\" do\n    csv = CSV.new(\"\", headers: true)\n    csv.headers.should be_empty\n  end\n\n  it \"raises if trying to access before first row\" do\n    csv = new_csv headers: true\n    expect_raises(CSV::Error, \"Before first row\") do\n      csv[\"one\"]\n    end\n  end\n\n  it \"gets row values with string\" do\n    csv = new_csv headers: true\n    csv.next.should be_true\n    csv[\"one\"].should eq(\"1\")\n    csv[\"two\"].should eq(\" 2\")\n\n    expect_raises(KeyError) { csv[\"three\"] }\n\n    csv[\"one\"]?.should eq(\"1\")\n    csv[\"three\"]?.should be_nil\n\n    csv.next.should be_true\n    csv[\"one\"].should eq(\"3\")\n\n    csv.next.should be_true\n    csv[\"one\"].should eq(\"5\")\n    csv[\"two\"].should eq(\"\")\n\n    csv.next.should be_false\n\n    expect_raises(CSV::Error, \"After last row\") do\n      csv[\"one\"]\n    end\n  end\n\n  it \"gets row values with integer\" do\n    csv = new_csv headers: true\n    csv.next.should be_true\n    csv[0].should eq(\"1\")\n    csv[1].should eq(\" 2\")\n\n    expect_raises(IndexError) do\n      csv[2]\n    end\n\n    csv[-1].should eq(\" 2\")\n    csv[-2].should eq(\"1\")\n\n    csv.next\n    csv.next\n\n    csv[0].should eq(\"5\")\n    csv[1].should eq(\"\")\n    csv[-2].should eq(\"5\")\n    csv[-1].should eq(\"\")\n  end\n\n  it \"gets row values with regex\" do\n    csv = new_csv headers: true\n    csv.next.should be_true\n\n    csv[/on/].should eq(\"1\")\n    csv[/tw/].should eq(\" 2\")\n\n    expect_raises(KeyError) do\n      csv[/foo/]\n    end\n  end\n\n  it \"gets current row\" do\n    csv = new_csv headers: true\n    csv.next.should be_true\n\n    row = csv.row\n    row[\"one\"].should eq(\"1\")\n    row[1].should eq(\" 2\")\n    row[/on/].should eq(\"1\")\n    row.size.should eq(2)\n\n    row.to_a.should eq([\"1\", \" 2\"])\n    row.to_h.should eq({\"one\" => \"1\", \"two\" => \" 2\"})\n  end\n\n  it \"strips\" do\n    csv = new_csv headers: true, strip: true\n    csv.next.should be_true\n\n    csv[\"one\"].should eq(\"1\")\n    csv[\"two\"].should eq(\"2\")\n\n    csv.row.to_a.should eq(%w(1 2))\n    csv.row.to_h.should eq({\"one\" => \"1\", \"two\" => \"2\"})\n  end\n\n  it \"works without headers\" do\n    csv = new_csv headers: false\n    csv.next.should be_true\n    csv[0].should eq(\"one\")\n  end\n\n  it \"can do each\" do\n    csv = new_csv headers: true\n    csv.each do\n      csv[\"one\"].should eq(\"1\")\n      break\n    end.should be_nil\n  end\n\n  it \"can do new with block\" do\n    CSV.new(%(one, two\\n1, 2\\n3, 4\\n5), headers: true, strip: true) do |csv|\n      csv[\"one\"].should eq(\"1\")\n      csv[\"two\"].should eq(\"2\")\n      break\n    end\n  end\n\n  it \"returns a Tuple(String, String) for current row with indices\" do\n    CSV.new(\"John,20\\nPeter,30\") do |csv|\n      csv.values_at(0, -1).should eq({\"John\", \"20\"})\n      break\n    end\n  end\n\n  it \"returns a Tuple(String, String) for current row with headers\" do\n    CSV.new(\"Name,Age\\nJohn,20\\nPeter,30\", headers: true) do |csv|\n      csv.values_at(\"Name\", \"Age\").should eq({\"John\", \"20\"})\n      break\n    end\n  end\n\n  it \"returns a Tuple(String, String) for this row with indices\" do\n    CSV.new(\"John,20\\nPeter,30\") do |csv|\n      csv.row.values_at(0, -1).should eq({\"John\", \"20\"})\n      break\n    end\n  end\n\n  it \"returns a Tuple(String, String) for this row with headers\" do\n    CSV.new(\"Name,Age\\nJohn,20\\nPeter,30\", headers: true) do |csv|\n      csv.row.values_at(\"Name\", \"Age\").should eq({\"John\", \"20\"})\n      break\n    end\n  end\n\n  describe \"rewind\" do\n    describe \"string based\" do\n      it \"without headers\" do\n        csv = CSV.new(\"one,two\\nthree,four\", headers: false)\n        csv.next\n        csv.row.to_a.should eq(%w(one two))\n        csv.next\n        csv.row.to_a.should eq(%w(three four))\n        csv.rewind\n        csv.next\n        csv.row.to_a.should eq(%w(one two))\n      end\n\n      it \"with headers\" do\n        csv = CSV.new(\"one,two\\nthree,four\\nfive,six\", headers: true)\n        csv.next\n        csv.row.to_h.should eq({\"one\" => \"three\", \"two\" => \"four\"})\n        csv.next\n        csv.row.to_h.should eq({\"one\" => \"five\", \"two\" => \"six\"})\n        csv.rewind\n        csv.next\n        csv.row.to_h.should eq({\"one\" => \"three\", \"two\" => \"four\"})\n      end\n    end\n\n    describe \"IO based\" do\n      it \"without headers\" do\n        csv = CSV.new(IO::Memory.new(\"one,two\\nthree,four\"), headers: false)\n        csv.next\n        csv.row.to_a.should eq(%w(one two))\n        csv.next\n        csv.row.to_a.should eq(%w(three four))\n        csv.rewind\n        csv.next\n        csv.row.to_a.should eq(%w(one two))\n      end\n\n      it \"with headers\" do\n        csv = CSV.new(IO::Memory.new(\"one,two\\nthree,four\\nfive,six\"), headers: true)\n        csv.next\n        csv.row.to_h.should eq({\"one\" => \"three\", \"two\" => \"four\"})\n        csv.next\n        csv.row.to_h.should eq({\"one\" => \"five\", \"two\" => \"six\"})\n        csv.rewind\n        csv.next\n        csv.row.to_h.should eq({\"one\" => \"three\", \"two\" => \"four\"})\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/data/argf_test_file_1.txt",
    "content": "12345\n"
  },
  {
    "path": "spec/std/data/argf_test_file_2.txt",
    "content": "67890\n"
  },
  {
    "path": "spec/std/data/argf_test_file_3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jobs>\n  <ad>\n    <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus facilisis leo nunc, consectetur dictum libero faucibus non. Donec blandit eros nec auctor commodo. Vestibulum semper bibendum bibendum. Nullam semper non lacus eget tempus. Fusce quis justo est. Mauris at lobortis leo. Nam efficitur laoreet orci, et auctor dolor auctor at. Aenean sit amet sapien et nibh elementum viverra. Nulla tincidunt purus sit amet euismod rutrum. Mauris rutrum mattis mauris, vel euismod odio efficitur eu. Suspendisse auctor lacus tempor fringilla luctus. Vestibulum efficitur lorem in ante feugiat porttitor. Aenean mollis ipsum facilisis aliquam efficitur.  Curabitur erat nulla, venenatis commodo metus vel, bibendum sagittis metus. Aenean sagittis, tortor eget porta porta, ipsum eros congue elit, quis pellentesque lorem ipsum pharetra quam. Sed pellentesque ex a consectetur dapibus. Aenean ligula nibh, auctor quis lacus eget, sollicitudin suscipit dui. Curabitur accumsan quis nisi nec porta. Morbi id augue at augue aliquam suscipit quis eget nisl. Integer vel orci risus. Aenean leo leo, sodales et nibh sit amet, placerat ullamcorper nibh. Nunc lorem mauris, hendrerit eget pretium sit amet, lacinia dignissim enim. Fusce eget tortor mauris.  Phasellus in nibh elit. Integer ut massa metus. Nam mattis lectus sed semper efficitur. Vestibulum et rutrum odio. Aenean finibus magna sit amet sapien rutrum, et condimentum dui dapibus. Nunc eget nisl sit amet augue mollis venenatis quis in ante. Nullam facilisis dui eget libero aliquam sodales. Donec faucibus mollis nibh, sed tristique velit. Aliquam tellus dui, sodales at nisi in, elementum tincidunt tortor. Nunc consectetur mi tortor, et aliquet nisl consectetur id.  Sed ultrices enim ante, vel rhoncus massa blandit non. Morbi quis cursus dui. Integer pretium leo mauris, eget vestibulum ante fringilla non. Etiam scelerisque mi non diam aliquam vehicula. Mauris sed mauris vitae nisi dictum faucibus non sit amet lacus. Sed porta felis sagittis, aliquam ante ac, mattis urna. Nullam sodales nisl vitae urna tempus, a mollis leo eleifend. Etiam sed sem vitae ante iaculis lacinia. Quisque sed semper justo, sit amet lacinia tortor. Nullam ut varius risus, vestibulum semper elit. Suspendisse non laoreet massa, eget ultrices magna. Proin in ante odio. Mauris vel faucibus nunc, id convallis leo.  Mauris quis lacinia lorem. Etiam vel molestie tellus, vitae pellentesque turpis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In in dictum elit. Nulla facilisi. Suspendisse faucibus semper dui, lobortis mattis nibh feugiat nec. Nunc augue urna, tincidunt at risus eget, lacinia placerat lectus. Donec pulvinar arcu sit amet lacus dictum, ut commodo magna fringilla. Ut et convallis eros. In eu malesuada sapien. Pellentesque ac est sagittis, volutpat libero ut, tincidunt ipsum. Praesent non felis dolor.  Morbi ac orci elit. Nulla placerat libero at neque lacinia tempor. Suspendisse potenti. Donec eget ipsum elementum, porttitor ligula id, tempus massa. Maecenas vestibulum posuere ipsum et consequat. Sed efficitur mauris elit, vel porttitor nisl pulvinar tincidunt. Ut metus est, finibus luctus metus at, consequat vehicula dolor. Donec suscipit est at lorem finibus tristique. Etiam diam dolor, ullamcorper vulputate sagittis id, laoreet a ligula. Proin in interdum felis. Nullam vel diam velit. Phasellus eget scelerisque nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit.  Duis in lacus lacus. Cras ligula lectus, lacinia sit amet ligula ut, egestas interdum ipsum. Maecenas feugiat consectetur libero eget sagittis. Praesent hendrerit eleifend nulla, sed hendrerit erat aliquam quis. Pellentesque ante odio, sodales et arcu nec, sodales porta ex. Donec iaculis nibh at orci semper vehicula. In euismod ornare dapibus. Nullam placerat tortor eu nibh porttitor, aliquet fermentum lorem convallis. Quisque et sapien sem. Suspendisse elementum turpis ut maximus sodales.  Phasellus pretium, sapien et pellentesque ornare, erat eros ornare turpis, ut condimentum nunc ex sed nibh. Praesent sit amet tortor urna. Integer id lacinia diam. Etiam massa elit, interdum vitae iaculis et, posuere id dui. Sed euismod dolor at tincidunt interdum. Curabitur vel tellus commodo, vulputate augue nec, rutrum tellus. In quis malesuada tellus. Vestibulum lectus libero, ultricies quis magna at, fermentum tempor quam. Pellentesque pulvinar nisi mauris, nec lobortis nulla cursus eu. Proin purus neque, suscipit sed tellus suscipit, suscipit sollicitudin arcu.</content>\n    <date>15/07/2019</date>\n    <category>Interpreting</category>\n    <contract>permanent</contract>\n    <experience>between 3 and 5 years</experience>\n  </ad>\n  <ad>\n    <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus facilisis leo nunc, consectetur dictum libero faucibus non. Donec blandit eros nec auctor commodo. Vestibulum semper bibendum bibendum. Nullam semper non lacus eget tempus. Fusce quis justo est. Mauris at lobortis leo. Nam efficitur laoreet orci, et auctor dolor auctor at. Aenean sit amet sapien et nibh elementum viverra. Nulla tincidunt purus sit amet euismod rutrum. Mauris rutrum mattis mauris, vel euismod odio efficitur eu. Suspendisse auctor lacus tempor fringilla luctus. Vestibulum efficitur lorem in ante feugiat porttitor. Aenean mollis ipsum facilisis aliquam efficitur.  Curabitur erat nulla, venenatis commodo metus vel, bibendum sagittis metus. Aenean sagittis, tortor eget porta porta, ipsum eros congue elit, quis pellentesque lorem ipsum pharetra quam. Sed pellentesque ex a consectetur dapibus. Aenean ligula nibh, auctor quis lacus eget, sollicitudin suscipit dui. Curabitur accumsan quis nisi nec porta. Morbi id augue at augue aliquam suscipit quis eget nisl. Integer vel orci risus. Aenean leo leo, sodales et nibh sit amet, placerat ullamcorper nibh. Nunc lorem mauris, hendrerit eget pretium sit amet, lacinia dignissim enim. Fusce eget tortor mauris.  Phasellus in nibh elit. Integer ut massa metus. Nam mattis lectus sed semper efficitur. Vestibulum et rutrum odio. Aenean finibus magna sit amet sapien rutrum, et condimentum dui dapibus. Nunc eget nisl sit amet augue mollis venenatis quis in ante. Nullam facilisis dui eget libero aliquam sodales. Donec faucibus mollis nibh, sed tristique velit. Aliquam tellus dui, sodales at nisi in, elementum tincidunt tortor. Nunc consectetur mi tortor, et aliquet nisl consectetur id.  Sed ultrices enim ante, vel rhoncus massa blandit non. Morbi quis cursus dui. Integer pretium leo mauris, eget vestibulum ante fringilla non. Etiam scelerisque mi non diam aliquam vehicula. Mauris sed mauris vitae nisi dictum faucibus non sit amet lacus. Sed porta felis sagittis, aliquam ante ac, mattis urna. Nullam sodales nisl vitae urna tempus, a mollis leo eleifend. Etiam sed sem vitae ante iaculis lacinia. Quisque sed semper justo, sit amet lacinia tortor. Nullam ut varius risus, vestibulum semper elit. Suspendisse non laoreet massa, eget ultrices magna. Proin in ante odio. Mauris vel faucibus nunc, id convallis leo.  Mauris quis lacinia lorem. Etiam vel molestie tellus, vitae pellentesque turpis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In in dictum elit. Nulla facilisi. Suspendisse faucibus semper dui, lobortis mattis nibh feugiat nec. Nunc augue urna, tincidunt at risus eget, lacinia placerat lectus. Donec pulvinar arcu sit amet lacus dictum, ut commodo magna fringilla. Ut et convallis eros. In eu malesuada sapien. Pellentesque ac est sagittis, volutpat libero ut, tincidunt ipsum. Praesent non felis dolor.  Morbi ac orci elit. Nulla placerat libero at neque lacinia tempor. Suspendisse potenti. Donec eget ipsum elementum, porttitor ligula id, tempus massa. Maecenas vestibulum posuere ipsum et consequat. Sed efficitur mauris elit, vel porttitor nisl pulvinar tincidunt. Ut metus est, finibus luctus metus at, consequat vehicula dolor. Donec suscipit est at lorem finibus tristique. Etiam diam dolor, ullamcorper vulputate sagittis id, laoreet a ligula. Proin in interdum felis. Nullam vel diam velit. Phasellus eget scelerisque nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit.  Duis in lacus lacus. Cras ligula lectus, lacinia sit amet ligula ut, egestas interdum ipsum. Maecenas feugiat consectetur libero eget sagittis. Praesent hendrerit eleifend nulla, sed hendrerit erat aliquam quis. Pellentesque ante odio, sodales et arcu nec, sodales porta ex. Donec iaculis nibh at orci semper vehicula. In euismod ornare dapibus. Nullam placerat tortor eu nibh porttitor, aliquet fermentum lorem convallis. Quisque et sapien sem. Suspendisse elementum turpis ut maximus sodales.  Phasellus pretium, sapien et pellentesque ornare, erat eros ornare turpis, ut condimentum nunc ex sed nibh. Praesent sit amet tortor urna. Integer id lacinia diam. Etiam massa elit, interdum vitae iaculis et, posuere id dui. Sed euismod dolor at tincidunt interdum. Curabitur vel tellus commodo, vulputate augue nec, rutrum tellus. In quis malesuada tellus. Vestibulum lectus libero, ultricies quis magna at, fermentum tempor quam. Pellentesque pulvinar nisi mauris, nec lobortis nulla cursus eu. Proin purus neque, suscipit sed tellus suscipit, suscipit sollicitudin arcu.</content>\n    <date>15/07/2019</date>\n    <category>Interpreting</category>\n    <contract>permanent</contract>\n    <experience>between 3 and 5 years</experience>\n  </ad>\n  <ad>\n    <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus facilisis leo nunc, consectetur dictum libero faucibus non. Donec blandit eros nec auctor commodo. Vestibulum semper bibendum bibendum. Nullam semper non lacus eget tempus. Fusce quis justo est. Mauris at lobortis leo. Nam efficitur laoreet orci, et auctor dolor auctor at. Aenean sit amet sapien et nibh elementum viverra. Nulla tincidunt purus sit amet euismod rutrum. Mauris rutrum mattis mauris, vel euismod odio efficitur eu. Suspendisse auctor lacus tempor fringilla luctus. Vestibulum efficitur lorem in ante feugiat porttitor. Aenean mollis ipsum facilisis aliquam efficitur.  Curabitur erat nulla, venenatis commodo metus vel, bibendum sagittis metus. Aenean sagittis, tortor eget porta porta, ipsum eros congue elit, quis pellentesque lorem ipsum pharetra quam. Sed pellentesque ex a consectetur dapibus. Aenean ligula nibh, auctor quis lacus eget, sollicitudin suscipit dui. Curabitur accumsan quis nisi nec porta. Morbi id augue at augue aliquam suscipit quis eget nisl. Integer vel orci risus. Aenean leo leo, sodales et nibh sit amet, placerat ullamcorper nibh. Nunc lorem mauris, hendrerit eget pretium sit amet, lacinia dignissim enim. Fusce eget tortor mauris.  Phasellus in nibh elit. Integer ut massa metus. Nam mattis lectus sed semper efficitur. Vestibulum et rutrum odio. Aenean finibus magna sit amet sapien rutrum, et condimentum dui dapibus. Nunc eget nisl sit amet augue mollis venenatis quis in ante. Nullam facilisis dui eget libero aliquam sodales. Donec faucibus mollis nibh, sed tristique velit. Aliquam tellus dui, sodales at nisi in, elementum tincidunt tortor. Nunc consectetur mi tortor, et aliquet nisl consectetur id.  Sed ultrices enim ante, vel rhoncus massa blandit non. Morbi quis cursus dui. Integer pretium leo mauris, eget vestibulum ante fringilla non. Etiam scelerisque mi non diam aliquam vehicula. Mauris sed mauris vitae nisi dictum faucibus non sit amet lacus. Sed porta felis sagittis, aliquam ante ac, mattis urna. Nullam sodales nisl vitae urna tempus, a mollis leo eleifend. Etiam sed sem vitae ante iaculis lacinia. Quisque sed semper justo, sit amet lacinia tortor. Nullam ut varius risus, vestibulum semper elit. Suspendisse non laoreet massa, eget ultrices magna. Proin in ante odio. Mauris vel faucibus nunc, id convallis leo.  Mauris quis lacinia lorem. Etiam vel molestie tellus, vitae pellentesque turpis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In in dictum elit. Nulla facilisi. Suspendisse faucibus semper dui, lobortis mattis nibh feugiat nec. Nunc augue urna, tincidunt at risus eget, lacinia placerat lectus. Donec pulvinar arcu sit amet lacus dictum, ut commodo magna fringilla. Ut et convallis eros. In eu malesuada sapien. Pellentesque ac est sagittis, volutpat libero ut, tincidunt ipsum. Praesent non felis dolor.  Morbi ac orci elit. Nulla placerat libero at neque lacinia tempor. Suspendisse potenti. Donec eget ipsum elementum, porttitor ligula id, tempus massa. Maecenas vestibulum posuere ipsum et consequat. Sed efficitur mauris elit, vel porttitor nisl pulvinar tincidunt. Ut metus est, finibus luctus metus at, consequat vehicula dolor. Donec suscipit est at lorem finibus tristique. Etiam diam dolor, ullamcorper vulputate sagittis id, laoreet a ligula. Proin in interdum felis. Nullam vel diam velit. Phasellus eget scelerisque nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit.  Duis in lacus lacus. Cras ligula lectus, lacinia sit amet ligula ut, egestas interdum ipsum. Maecenas feugiat consectetur libero eget sagittis. Praesent hendrerit eleifend nulla, sed hendrerit erat aliquam quis. Pellentesque ante odio, sodales et arcu nec, sodales porta ex. Donec iaculis nibh at orci semper vehicula. In euismod ornare dapibus. Nullam placerat tortor eu nibh porttitor, aliquet fermentum lorem convallis. Quisque et sapien sem. Suspendisse elementum turpis ut maximus sodales.  Phasellus pretium, sapien et pellentesque ornare, erat eros ornare turpis, ut condimentum nunc ex sed nibh. Praesent sit amet tortor urna. Integer id lacinia diam. Etiam massa elit, interdum vitae iaculis et, posuere id dui. Sed euismod dolor at tincidunt interdum. Curabitur vel tellus commodo, vulputate augue nec, rutrum tellus. In quis malesuada tellus. Vestibulum lectus libero, ultricies quis magna at, fermentum tempor quam. Pellentesque pulvinar nisi mauris, nec lobortis nulla cursus eu. Proin purus neque, suscipit sed tellus suscipit, suscipit sollicitudin arcu.</content>\n    <date>31/07/2019</date>\n    <category>Client Services</category>\n    <working_hours>full-time</working_hours>\n    <contract>permanent</contract>\n    <experience>between 1 and 3 years</experience>\n  </ad>\n</jobs>"
  },
  {
    "path": "spec/std/data/backtrace_sample",
    "content": "class Kls\n  def callee1\n    puts caller.join('\\n')\n  end\nend\n\ndef callee2\n  yield\nend\n\ndef callee3\n  callee2 do\n    Kls.new.callee1\n  end\nend\n\ncallee3\n"
  },
  {
    "path": "spec/std/data/blank_test_file.txt",
    "content": ""
  },
  {
    "path": "spec/std/data/cipher_spec.ciphertext",
    "content": "v$2Epas/\u001b}0\u001c[|ip\u0005"
  },
  {
    "path": "spec/std/data/collect_within_ensure",
    "content": "begin\n  raise \"Oh no!\"\nensure\n  GC.collect\nend\n"
  },
  {
    "path": "spec/std/data/crash_backtrace_sample",
    "content": "class Kls\n  def callee1\n    foo = \"foo\".to_unsafe\n    foo.realloc(100)\n  end\nend\n\ndef callee2\n  yield\nend\n\ndef callee3\n  callee2 do\n    Kls.new.callee1\n  end\nend\n\ncallee3\n"
  },
  {
    "path": "spec/std/data/dir/dots/.dot.hidden",
    "content": ""
  },
  {
    "path": "spec/std/data/dir/dots/.hidden/f1.txt",
    "content": ""
  },
  {
    "path": "spec/std/data/dir/f1.txt",
    "content": "This file is for Dir.glob specs"
  },
  {
    "path": "spec/std/data/dir/f2.txt",
    "content": "This file is for Dir.glob specs"
  },
  {
    "path": "spec/std/data/dir/f3.txx",
    "content": "This file is for Dir.glob specs"
  },
  {
    "path": "spec/std/data/dir/g2.txt",
    "content": "This file is for Dir.glob specs"
  },
  {
    "path": "spec/std/data/dir/subdir/f1.txt",
    "content": "This file is for Dir.glob specs"
  },
  {
    "path": "spec/std/data/dir/subdir/subdir2/f2.txt",
    "content": "This file is for Dir.glob specs"
  },
  {
    "path": "spec/std/data/dir/subdir2/.gitkeep",
    "content": ""
  },
  {
    "path": "spec/std/data/exception_backtrace_sample",
    "content": "class Kls\n  def callee1\n    (0..10).to_a[123]\n  end\nend\n\ndef callee2\n  yield\nend\n\ndef callee3\n  callee2 do\n    Kls.new.callee1\n  end\nend\n\ncallee3\n"
  },
  {
    "path": "spec/std/data/io_data_incomplete_multibyte_sequence.txt",
    "content": "77u/PGJvZHk+Cgo8ZGl2IGlkPSJ3cmFwcGVyIj4KCgk8ZGl2IGlkPSJiYW5u\nZXIiPiA8L2Rpdj4KCgk8ZGl2IGlkPSJtZW51Ij4KCTx1bD4KCQk8bGk+PGEg\naHJlZj0iaHR0cDovL21lbnNoYWtvdi5ydS8iPtCSINC90LDRh9Cw0LvQvjwv\nYT48L2xpPgoJCQk8L3VsPgoJPGJyIGNsYXNzPSJjbGVhciIgLz4KCTwvZGl2\nPgoJPGRpdiBpZD0iY29udGVudCI+CgkKCQkKCQk8ZGl2IGNsYXNzPSJlbnRy\neSI+CgkJCTxoMT48YSBocmVmPSJodHRwOi8vbWVuc2hha292LnJ1LzIwMDkv\nMDEvMjMvJWQwJWJjJWQxJTgzJWQwJWI0JWQxJTgwJWQwJWI1JWQxJTg2LSVk\nMCViOC0lZDAlYmMlZDAlYmUlZDAlYmQlZDAlYjUlZDElODIlZDAlYjAvIiBy\nZWw9ImJvb2ttYXJrIiB0aXRsZT0i0JzRg9C00YDQtdGGINC4INC80L7QvdC1\n0YLQsCI+0JzRg9C00YDQtdGGINC4INC80L7QvdC10YLQsDwvYT48L2gxPgoJ\nCQk8ZGl2IGNsYXNzPSJkYXRlIj5KYW51YXJ5IDIzcmQsIDIwMDk8L2Rpdj4K\nCQkJPHA+PCEtLVtlbmRpZl0tLT48L3A+CjxwIGNsYXNzPSJNc29Ob3JtYWwi\nPtCe0LTQvdCw0LbQtNGLINCn0LXQu9C+0LLQtdC6INC/0YDQuNGI0LXQuyDQ\nuiDRgdGC0LDRgNC+0LzRgyDQvNGD0LTRgNC10YbRgyDQt9CwINGB0L7QstC1\n0YLQvtC8OjwvcD4KPHAgY2xhc3M9Ik1zb05vcm1hbCI+4oCTINCn0YLQviDQ\nvNC90LUg0L3Rg9C20L3QviDRgdC00LXQu9Cw0YLRjCwg0YfRgtC+0LHRiyDR\ngdGC0LDRgtGMINGF0L7Qt9GP0LjQvdC+0Lwg0YHQstC+0LXQuSDQttC40LfQ\nvdC4LCDRh9GC0L7QsdGLINGPINC80L7QsyDRgNC10LDQu9C40LfQvtCy0LDR\ngtGMINGB0LLQvtC4INC20LXQu9Cw0L3QuNGPPyDQryDRgtGA0LDRh9GDINC+\n0YfQtdC90Ywg0LzQvdC+0LPQviDQstGA0LXQvNC10L3QuCwg0YfRgtC+0LHR\niyDQtNC+0YHRgtC40LPQvdGD0YLRjCDRgtC+0LPQviwg0YfQtdCz0L4g0Y8g\n0YXQvtGH0YMsINC90L4g0LzQtdC90Y8g0LLRgdC10LPQtNCwINC/0L7RgdGC\n0LjQs9Cw0Y7RgiDQvdC10YPQtNCw0YfQuCDQuCDQstGA0LXQvNGPINGD0YXQ\nvtC00LjRgiDQvdCwINC/0L7RgdGC0L7RgNC+0L3QvdC40LUsINC90L4g0YLQ\nvtC20LUg0L3QtdC+0LHRhdC+0LTQuNC80YvQtSDQvNC90LUsINCy0LXRidC4\nLiA8YSBocmVmPSJodHRwOi8vbWVuc2hha292LnJ1LzIwMDkvMDEvMjMvJWQw\nJWJjJWQxJTgzJWQwJWI0JWQxJTgwJWQwJWI1JWQxJTg2LSVkMCViOC0lZDAl\nYmMlZDAlYmUlZDAlYmQlZDAlYjUlZDElODIlZDAlYjAvI21vcmUtMzQiIGNs\nYXNzPSJtb3JlLWxpbmsiPtCU0LDQu9C10LU8L2E+PC9wPgoJCQk8ZGl2IGNs\nYXNzPSJtZXRhIj4KCQkJCQkJCQkg0JTQvtCx0LDQstC70LXQvdC+INCyIDxh\nIGhyZWY9Imh0dHA6Ly9tZW5zaGFrb3YucnUvY2F0ZWdvcnkvcGFyYWJsZXMv\nIiB0aXRsZT0iVmlldyBhbGwgcG9zdHMgaW4g0J/RgNC40YLRh9C4IiByZWw9\nImNhdGVnb3J5IHRhZyI+0J/RgNC40YLRh9C4PC9hPiB8IDxhIGhyZWY9Imh0\ndHA6Ly9tZW5zaGFrb3YucnUvMjAwOS8wMS8yMy8lZDAlYmMlZDElODMlZDAl\nYjQlZDElODAlZDAlYjUlZDElODYtJWQwJWI4LSVkMCViYyVkMCViZSVkMCVi\nZCVkMCViNSVkMSU4MiVkMCViMC8jY29tbWVudHMiIHRpdGxlPSJDb21tZW50\nIG9uINCc0YPQtNGA0LXRhiDQuCDQvNC+0L3QtdGC0LAiPjIg0LrQvtC80LzQ\ntdC90YLQsNGA0LjQtdCyICYjMTg3OzwvYT4JCQk8L2Rpdj4KCQk8L2Rpdj4K\nCgkJCgkJCgkJPGRpdiBjbGFzcz0iZW50cnkiPgoJCQk8aDE+PGEgaHJlZj0i\naHR0cDovL21lbnNoYWtvdi5ydS8yMDA4LzEyLzEzL2ljZS8iIHJlbD0iYm9v\na21hcmsiIHRpdGxlPSLQktC+0LTQsCDQuCDQu9C10LQiPtCS0L7QtNCwINC4\nINC70LXQtDwvYT48L2gxPgoJCQk8ZGl2IGNsYXNzPSJkYXRlIj5EZWNlbWJl\nciAxM3RoLCAyMDA4PC9kaXY+CgkJCTxwPtCe0LTQvdCw0LbQtNGLINC30LjQ\nvNC+0Lkg0KPRh9C40YLQtdC70Ywg0Lgg0YPRh9C10L3QuNC6INC/0YDQvtCz\n0YPQu9C40LLQsNC70LjRgdGMINC/0L4g0LHQtdGA0LXQs9GDINGA0LXQutC4\nLjxiciAvPgrigJQg0KPRh9C40YLQtdC70YwhINCb0Y7QtNC4INC90LUg0L/Q\nvtC90LjQvNCw0Y7RgiDQtNGA0YPQsyDQtNGA0YPQs9CwLiDQm9GO0LTQuCDR\ngdGC0LDRgNCw0Y7RgtGB0Y8g0L7QsdGJ0LDRgtGM0YHRjywg0YfQuNGC0LDR\njtGCINC60L3QuNCz0LgsINC60L7RgtC+0YDRi9C1INC/0L7QstC10YHRgtCy\n0YPRjtGCINC+INCy0LfQsNC40LzQvtC/0L7QvdC40LzQsNC90LjQuCDigJQg\n0Lgg0L3QsNGC0YvQutCw0Y7RgtGB0Y8g0L3QsCDQvdC10LLQuNC00LjQvNGD\n0Y4g0YHRgtC10L3Rgy4g0J/QvtGH0LXQvNGDINGC0LDQuj8g0J3QtdGD0LbQ\ntdC70Lgg0L3QtdC70YzQt9GPINGN0YLQvtC80YMg0L3QsNGD0YfQuNGC0Yw/\nPGJyIC8+CuKAlCDQn9C+0LnQtNC10Lwg0YHQviDQvNC90L7QuSDigJQg0KPR\nh9C40YLQtdC70Ywg0L/QvtGI0LXQuyDQv9C+INC70YzQtNGDINC90LAg0YHQ\ntdGA0LXQtNC40L3RgyDRgNC10LrQuC4g4oCUINCf0L7RgdC80L7RgtGA0Lgg\n0LLQvdC40LcuINCi0Ysg0LLQuNC00LjRiNGMINC30LTQtdGB0Ywg0YfRgtC+\nLdGC0L4/PGJyIC8+CuKAlCDQndC10YIuINCa0LDQuiDRjyDQvNC+0LPRgyDR\ng9Cy0LjQtNC10YLRjCDRh9GC0L4t0YLQviDRgdC60LLQvtC30Ywg0LvQtdC0\nPzxiciAvPgrigJQg0KLQsNC8LCDQv9C+0LTQviDQu9GM0LTQvtC8IOKAlCDR\nhtC10LvRi9C5INC80LjRgCwg0L3QtdCy0LXQtNC+0LzRi9C5INGC0LXQsdC1\nLiDQoNCw0YHRgtC+0L/QuCDQu9C10LQg4oCUINC4INC+0L0g0YHRgtCw0L3Q\ntdGCINCy0L7QtNC+0LksINC00LDRjtGJ0LXQuSDQttC40LfQvdGMINGG0LXQ\nu9C+0LzRgyDQvNC40YDRgy4g0J3QviDQv9GA0L7Qu9C10Lkg0YHRjtC00LAg\n0LPQvtGC0L7QstGD0Y4g0LLQvtC00YMg4oCUINC+0L3QsCDQt9Cw0LzQtdGA\n0LfQvdC10YIg0Lgg0LvQuNGI0Ywg0YPQutGA0LXQv9C40YIg0YbQsNGA0YHR\ngtCy0L4g0LvRjNC00LAuPC9wPgoJCQk8ZGl2IGNsYXNzPSJtZXRhIj4KCQkJ\nCQkJCQkg0JTQvtCx0LDQstC70LXQvdC+INCyIDxhIGhyZWY9Imh0dHA6Ly9t\nZW5zaGFrb3YucnUvY2F0ZWdvcnkvcGFyYWJsZXMvIiB0aXRsZT0iVmlldyBh\nbGwgcG9zdHMgaW4g0J/RgNC40YLRh9C4IiByZWw9ImNhdGVnb3J5IHRhZyI+\n0J/RgNC40YLRh9C4PC9hPiB8IDxhIGhyZWY9Imh0dHA6Ly9tZW5zaGFrb3Yu\ncnUvMjAwOC8xMi8xMy9pY2UvI2NvbW1lbnRzIiB0aXRsZT0iQ29tbWVudCBv\nbiDQktC+0LTQsCDQuCDQu9C10LQiPjEg0LrQvtC80LzQtdC90YLQsNGA0LjQ\nuSAmIzE4Nzs8L2E+CQkJPC9kaXY+CgkJPC9kaXY+CgoJCQoJCQoJCTxkaXYg\nY2xhc3M9ImVudHJ5Ij4KCQkJPGgxPjxhIGhyZWY9Imh0dHA6Ly9tZW5zaGFr\nb3YucnUvMjAwOC8xMS8wNi9mcmFnaWxlLyIgcmVsPSJib29rbWFyayIgdGl0\nbGU9ItCl0YDRg9C/0LrQuNC1INCy0LXRidC4Ij7QpdGA0YPQv9C60LjQtSDQ\nstC10YnQuDwvYT48L2gxPgoJCQk8ZGl2IGNsYXNzPSJkYXRlIj5Ob3ZlbWJl\nciA2dGgsIDIwMDg8L2Rpdj4KCQkJPHA+0JrQvtCz0LTQsC3RgtC+INCyINC+\n0LTQvdC+0Lwg0YHQtdC70LXQvdC40Lgg0LbQuNC7INGB0YLQsNGA0YvQuSDQ\nvNGD0LTRgNGL0Lkg0YfQtdC70L7QstC10LouINCe0L0g0LvRjtCx0LjQuyDQ\ntNC10YLQtdC5INC4INC/0YDQvtCy0L7QtNC40Lsg0YEg0L3QuNC80Lgg0LzQ\nvdC+0LPQviDQstGA0LXQvNC10L3QuC4g0JXRidGRINC+0L0g0LvRjtCx0LjQ\nuyDQtNC10LvQsNGC0Ywg0LjQvCDQv9C+0LTQsNGA0LrQuCwg0L/RgNCw0LLQ\ntNCwINC00LDRgNC40Lsg0L/RgNC10LrRgNCw0YHQvdGL0LUs0L3QviDQvtGH\n0LXQvdGMINGF0YDRg9C/0LrQuNC1INCy0LXRidC4LiDQmtCw0Log0L3QuCDR\ngdGC0LDRgNCw0LvQuNGB0Ywg0LTQtdGC0Lgg0LHRi9GC0Ywg0LDQutC60YPR\ngNCw0YLQvdGL0LzQuCwg0LjRhSDQvdC+0LLRi9C1INC40LPRgNGD0YjQutC4\nINGH0LDRgdGC0L4g0LvQvtC80LDQu9C40YHRjC4g0JTQtdGC0Lgg0YDQsNGB\n0YHRgtGA0LDQuNCy0LDQu9C40YHRjCDQuCDQs9C+0YDRjNC60L4g0L/Qu9Cw\n0LrQsNC70LguINCQINC80YPQtNGA0LXRhiDRgdC90L7QstCwINC00LDRgNC4\n0Lsg0LjQvCDQuNCz0YDRg9GI0LrQuCwg0L3QviDQtdGJ0ZEg0LHQvtC70LXQ\ntSDRhdGA0YPQv9C60LjQtSDQuCDQvdC10LbQvdGL0LUmIzgyMzA7PGJyIC8+\nCtCe0LTQvdCw0LbQtNGLINGA0L7QtNC40YLQtdC70Lgg0L3QtSDQstGL0LTQ\ntdGA0LbQsNC70Lgg0Lgg0L/RgNC40YjQu9C4INC6INC90LXQvNGDOjxiciAv\nPgo="
  },
  {
    "path": "spec/std/data/io_data_incomplete_multibyte_sequence_2.txt",
    "content": "77u/PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBYSFRNTCAx\nLjAgU3RyaWN0Ly9FTiIgImh0dHA6Ly93d3cudzMub3JnL1RSL3hodG1sMS9E\nVEQveGh0bWwxLXN0cmljdC5kdGQiPgo8aHRtbCB4bWxucz0iaHR0cDovL3d3\ndy53My5vcmcvMTk5OS94aHRtbCI+CjxoZWFkIHByb2ZpbGU9Imh0dHA6Ly9n\nbXBnLm9yZy94Zm4vMTEiPgoKPCEtLSBNZXRhIEluZm9ybWF0aW9uIC0tPgo8\ndGl0bGU+VGhvdWdodHMgQmF5ICA8L3RpdGxlPgo8bWV0YSBuYW1lPSJjb3B5\ncmlnaHQiIGNvbnRlbnQ9IlRob3VnaHRzIEJheSB8IDIwMDkiIC8+CjxtZXRh\nIG5hbWU9ImRpc3RyaWJ1dGlvbiIgY29udGVudD0iZ2xvYmFsIiAvPgo8bWV0\nYSBuYW1lPSJyb2JvdHMiIGNvbnRlbnQ9ImZvbGxvdywgYWxsIiAvPgo8bWV0\nYSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNvbnRlbnQ9InRleHQvaHRt\nbDsgY2hhcnNldD1VVEYtOCIgLz4KCjwhLS0gQ1NTIC0tPgo8c3R5bGUgdHlw\nZT0idGV4dC9jc3MiIG1lZGlhPSJzY3JlZW4iPgoJCUBpbXBvcnQgdXJsKCBo\ndHRwOi8vbWVuc2hha292LnJ1L3dwLWNvbnRlbnQvdGhlbWVzL2VnZWNpYS9z\ndHlsZS5jc3MgKTsKPC9zdHlsZT4KCjwhLS0gV29yZHByZXNzIC0tPgo8bWV0\nYSBuYW1lPSJnZW5lcmF0b3IiIGNvbnRlbnQ9IldvcmRQcmVzcyAyLjguNCIg\nLz4KPGxpbmsgcmVsPSJhbHRlcm5hdGUiIHR5cGU9ImFwcGxpY2F0aW9uL3Jz\ncyt4bWwiIHRpdGxlPSJSU1MgMi4wIiBocmVmPSJodHRwOi8vbWVuc2hha292\nLnJ1L2ZlZWQvIiAvPgo8bGluayByZWw9ImFsdGVybmF0ZSIgdHlwZT0idGV4\ndC94bWwiIHRpdGxlPSJSU1MgLjkyIiBocmVmPSJodHRwOi8vbWVuc2hha292\nLnJ1L2ZlZWQvcnNzLyIgLz4KPGxpbmsgcmVsPSJhbHRlcm5hdGUiIHR5cGU9\nImFwcGxpY2F0aW9uL2F0b20reG1sIiB0aXRsZT0iQXRvbSAwLjMiIGhyZWY9\nImh0dHA6Ly9tZW5zaGFrb3YucnUvZmVlZC9hdG9tLyIgLz4KPGxpbmsgcmVs\nPSJwaW5nYmFjayIgaHJlZj0iaHR0cDovL21lbnNoYWtvdi5ydS94bWxycGMu\ncGhwIiAvPgoJPGxpbmsgcmVsPSdhcmNoaXZlcycgdGl0bGU9J0phbnVhcnkg\nMjAwOScgaHJlZj0naHR0cDovL21lbnNoYWtvdi5ydS8yMDA5LzAxLycgLz4K\nCTxsaW5rIHJlbD0nYXJjaGl2ZXMnIHRpdGxlPSdEZWNlbWJlciAyMDA4JyBo\ncmVmPSdodHRwOi8vbWVuc2hha292LnJ1LzIwMDgvMTIvJyAvPgoJPGxpbmsg\ncmVsPSdhcmNoaXZlcycgdGl0bGU9J05vdmVtYmVyIDIwMDgnIGhyZWY9J2h0\ndHA6Ly9tZW5zaGFrb3YucnUvMjAwOC8xMS8nIC8+Cgk8bGluayByZWw9J2Fy\nY2hpdmVzJyB0aXRsZT0nQXVndXN0IDIwMDgnIGhyZWY9J2h0dHA6Ly9tZW5z\naGFrb3YucnUvMjAwOC8wOC8nIC8+CjxsaW5rIHJlbD0iRWRpdFVSSSIgdHlw\nZT0iYXBwbGljYXRpb24vcnNkK3htbCIgdGl0bGU9IlJTRCIgaHJlZj0iaHR0\ncDovL21lbnNoYWtvdi5ydS94bWxycGMucGhwP3JzZCIgLz4KPGxpbmsgcmVs\nPSJ3bHdtYW5pZmVzdCIgdHlwZT0iYXBwbGljYXRpb24vd2x3bWFuaWZlc3Qr\neG1sIiBocmVmPSJodHRwOi8vbWVuc2hha292LnJ1L3dwLWluY2x1ZGVzL3ds\nd21hbmlmZXN0LnhtbCIgLz4gCjxsaW5rIHJlbD0naW5kZXgnIHRpdGxlPSdU\naG91Z2h0cyBCYXknIGhyZWY9J2h0dHA6Ly9tZW5zaGFrb3YucnUnIC8+Cjxt\nZXRhIG5hbWU9ImdlbmVyYXRvciIgY29udGVudD0iV29yZFByZXNzIDIuOC40\nIiAvPgo8L2hlYWQ+Cgo8Ym9keT4KCjxkaXYgaWQ9IndyYXBwZXIiPgoKCTxk\naXYgaWQ9ImhlYWRlciI+CgkJPGgxPjxhIGhyZWY9Imh0dHA6Ly9tZW5zaGFr\nb3YucnUvIj5UaG91Z2h0cyBCYXk8L2E+PC9oMT4KCQk8aDI+SnVzdCBhbm90\naGVyIFdvcmRQcmVzcyB3ZWJsb2c8L2gyPgoJPC9kaXY+CgoJPGRpdiBpZD0i\nYmFubmVyIj4gPC9kaXY+CgoJPGRpdiBpZD0ibWVudSI+Cgk8dWw+CgkJPGxp\nPjxhIGhyZWY9Imh0dHA6Ly9tZW5zaGFrb3YucnUvIj7QkiDQvdCw0YfQsNC7\n0L48L2E+PC9saT4KCQkJPC91bD4KCTxiciBjbGFzcz0iY2xlYXIiIC8+Cgk8\nL2Rpdj4KCTxkaXYgaWQ9ImNvbnRlbnQiPgoJCgkJCgkJPGRpdiBjbGFzcz0i\nZW50cnkiPgoJCQk8aDE+PGEgaHJlZj0iaHR0cDovL21lbnNoYWtvdi5ydS8y\nMDA5LzAxLzIzLyVkMCViYyVkMSU4MyVkMCViNCVkMSU4MCVkMCViNSVkMSU4\nNi0lZDAlYjgtJWQwJWJjJWQwJWJlJWQwJWJkJWQwJWI1JWQxJTgyJWQwJWIw\nLyIgcmVsPSJib29rbWFyayIgdGl0bGU9ItCc0YPQtNGA0LXRhiDQuCDQvNC+\n0L3QtdGC0LAiPtCc0YPQtNGA0LXRhiDQuCDQvNC+0L3QtdGC0LA8L2E+PC9o\nMT4KCQkJPGRpdiBjbGFzcz0iZGF0ZSI+SmFudWFyeSAyM3JkLCAyMDA5PC9k\naXY+CgkJCTxwPjwhLS1bZW5kaWZdLS0+PC9wPgo8cCBjbGFzcz0iTXNvTm9y\nbWFsIj7QntC00L3QsNC20LTRiyDQp9C10LvQvtCy0LXQuiDQv9GA0LjRiNC1\n0Lsg0Log0YHRgtCw0YDQvtC80YMg0LzRg9C00YDQtdGG0YMg0LfQsCDRgdC+\n0LLQtdGC0L7QvDo8L3A+CjxwIGNsYXNzPSJNc29Ob3JtYWwiPuKAkyDQp9GC\n0L4g0LzQvdC1INC90YPQttC90L4g0YHQtNC10LvQsNGC0YwsINGH0YLQvtCx\n0Ysg0YHRgtCw0YLRjCDRhdC+0LfRj9C40L3QvtC8INGB0LLQvtC10Lkg0LbQ\nuNC30L3QuCwg0YfRgtC+0LHRiyDRjyDQvNC+0LMg0YDQtdCw0LvQuNC30L7Q\nstCw0YLRjCDRgdCy0L7QuCDQttC10LvQsNC90LjRjz8g0K8g0YLRgNCw0YfR\ngyDQvtGH0LXQvdGMINC80L3QvtCz0L4g0LLRgNC10LzQtdC90LgsINGH0YLQ\nvtCx0Ysg0LTQvtGB0YLQuNCz0L3Rg9GC0Ywg0YLQvtCz0L4sINGH0LXQs9C+\nINGPINGF0L7Rh9GDLCDQvdC+INC80LXQvdGPINCy0YHQtdCz0LTQsCDQv9C+\n0YHRgtC40LPQsNGO0YIg0L3QtdGD0LTQsNGH0Lgg0Lgg0LLRgNC10LzRjyDR\ng9GF0L7QtNC40YIg0L3QsCDQv9C+0YHRgtC+0YDQvtC90L3QuNC1LCDQvdC+\nINGC0L7QttC1INC90LXQvtCx0YXQvtC00LjQvNGL0LUg0LzQvdC1LCDQstC1\n0YnQuC4gPGEgaHJlZj0iaHR0cDovL21lbnNoYWtvdi5ydS8yMDA5LzAxLzIz\nLyVkMCViYyVkMSU4MyVkMCViNCVkMSU4MCVkMCViNSVkMSU4Ni0lZDAlYjgt\nJWQwJWJjJWQwJWJlJWQwJWJkJWQwJWI1JWQxJTgyJWQwJWIwLyNtb3JlLTM0\nIiBjbGFzcz0ibW9yZS1saW5rIj7QlNCw0LvQtdC1PC9hPjwvcD4KCQkJPGRp\ndiBjbGFzcz0ibWV0YSI+CgkJCQkJCQkJINCU0L7QsdCw0LLQu9C10L3QviDQ\nsiA8YSBocmVmPSJodHRwOi8vbWVuc2hha292LnJ1L2NhdGVnb3J5L3BhcmFi\nbGVzLyIgdGl0bGU9IlZpZXcgYWxsIHBvc3RzIGluINCf0YDQuNGC0YfQuCIg\ncmVsPSJjYXRlZ29yeSB0YWciPtCf0YDQuNGC0YfQuDwvYT4gfCA8YSBocmVm\nPSJodHRwOi8vbWVuc2hha292LnJ1LzIwMDkvMDEvMjMvJWQwJWJjJWQxJTgz\nJWQwJWI0JWQxJTgwJWQwJWI1JWQxJTg2LSVkMCViOC0lZDAlYmMlZDAlYmUl\nZDAlYmQlZDAlYjUlZDElODIlZDAlYjAvI2NvbW1lbnRzIiB0aXRsZT0iQ29t\nbWVudCBvbiDQnNGD0LTRgNC10YYg0Lgg0LzQvtC90LXRgtCwIj4yINC60L7Q\nvNC80LXQvdGC0LDRgNC40LXQsiAmIzE4Nzs8L2E+CQkJPC9kaXY+CgkJPC9k\naXY+CgoJCQoJCQoJCTxkaXYgY2xhc3M9ImVudHJ5Ij4KCQkJPGgxPjxhIGhy\nZWY9Imh0dHA6Ly9tZW5zaGFrb3YucnUvMjAwOC8xMi8xMy9pY2UvIiByZWw9\nImJvb2ttYXJrIiB0aXRsZT0i0JLQvtC00LAg0Lgg0LvQtdC0Ij7QktC+0LTQ\nsCDQuCDQu9C10LQ8L2E+PC9oMT4KCQkJPGRpdiBjbGFzcz0iZGF0ZSI+RGVj\nZW1iZXIgMTN0aCwgMjAwODwvZGl2PgoJCQk8cD7QntC00L3QsNC20LTRiyDQ\nt9C40LzQvtC5INCj0YfQuNGC0LXQu9GMINC4INGD0YfQtdC90LjQuiDQv9GA\n0L7Qs9GD0LvQuNCy0LDQu9C40YHRjCDQv9C+INCx0LXRgNC10LPRgyDRgNC1\n0LrQuC48YnIgLz4K4oCUINCj0YfQuNGC0LXQu9GMISDQm9GO0LTQuCDQvdC1\nINC/0L7QvdC40LzQsNGO0YIg0LTRgNGD0LMg0LTRgNGD0LPQsC4g0JvRjtC0\n0Lgg0YHRgtCw0YDQsNGO0YLRgdGPINC+0LHRidCw0YLRjNGB0Y8sINGH0LjR\ngtCw0Y7RgiDQutC90LjQs9C4LCDQutC+0YLQvtGA0YvQtSDQv9C+0LLQtdGB\n0YLQstGD0Y7RgiDQviDQstC30LDQuNC80L7Qv9C+0L3QuNC80LDQvdC40Lgg\n4oCUINC4INC90LDRgtGL0LrQsNGO0YLRgdGPINC90LAg0L3QtdCy0LjQtNC4\n0LzRg9GOINGB0YLQtdC90YMuINCf0L7Rh9C10LzRgyDRgtCw0Lo/INCd0LXR\ng9C20LXQu9C4INC90LXQu9GM0LfRjyDRjdGC0L7QvNGDINC90LDRg9GH0LjR\ngtGMPzxiciAvPgrigJQg0J/QvtC50LTQtdC8INGB0L4g0LzQvdC+0Lkg4oCU\nINCj0YfQuNGC0LXQu9GMINC/0L7RiNC10Lsg0L/QviDQu9GM0LTRgyDQvdCw\nINGB0LXRgNC10LTQuNC90YMg0YDQtdC60LguIOKAlCDQn9C+0YHQvNC+0YLR\ngNC4INCy0L3QuNC3LiDQotGLINCy0LjQtNC40YjRjCDQt9C00LXRgdGMINGH\n0YLQvi3RgtC+PzxiciAvPgrigJQg0J3QtdGCLiDQmtCw0Log0Y8g0LzQvtCz\n0YMg0YPQstC40LTQtdGC0Ywg0YfRgtC+LdGC0L4g0YHQutCy0L7Qt9GMINC7\n0LXQtD88YnIgLz4K4oCUINCi0LDQvCwg0L/QvtC00L4g0LvRjNC00L7QvCDi\ngJQg0YbQtdC70YvQuSDQvNC40YAsINC90LXQstC10LTQvtC80YvQuSDRgtC1\n0LHQtS4g0KDQsNGB0YLQvtC/0Lgg0LvQtdC0IOKAlCDQuCDQvtC9INGB0YLQ\nsNC90LXRgiDQstC+0LTQvtC5LCDQtNCw0Y7RidC10Lkg0LbQuNC30L3RjCDR\nhtC10LvQvtC80YMg0LzQuNGA0YMuINCd0L4g0L/RgNC+0LvQtdC5INGB0Y7Q\ntNCwINCz0L7RgtC+0LLRg9GOINCy0L7QtNGDIOKAlCDQvtC90LAg0LfQsNC8\n0LXRgNC30L3QtdGCINC4INC70LjRiNGMINGD0LrRgNC10L/QuNGCINGG0LDR\ngNGB0YLQstC+INC70YzQtNCwLjwvcD4KCQkJPGRpdiBjbGFzcz0ibWV0YSI+\nCgkJCQkJCQkJINCU0L7QsdCw0LLQu9C10L3QviDQsiA8YSBocmVmPSJodHRw\nOi8vbWVuc2hha292LnJ1L2NhdGVnb3J5L3BhcmFibGVzLyIgdGl0bGU9IlZp\nZXcgYWxsIHBvc3RzIGluINCf0YDQuNGC0YfQuCIgcmVsPSJjYXRlZ29yeSB0\nYWciPtCf0YDQuNGC0YfQuDwvYT4gfCA8YSBocmVmPSJodHRwOi8vbWVuc2hh\na292LnJ1LzIwMDgvMTIvMTMvaWNlLyNjb21tZW50cyIgdGl0bGU9IkNvbW1l\nbnQgb24g0JLQvtC00LAg0Lgg0LvQtdC0Ij4xINC60L7QvNC80LXQvdGC0LDR\ngNC40LkgJiMxODc7PC9hPgkJCTwvZGl2PgoJCTwvZGl2PgoKCQkKCQkKCQk8\nZGl2IGNsYXNzPSJlbnRyeSI+CgkJCTxoMT48YSBocmVmPSJodHRwOi8vbWVu\nc2hha292LnJ1LzIwMDgvMTEvMDYvZnJhZ2lsZS8iIHJlbD0iYm9va21hcmsi\nIHRpdGxlPSLQpdGA0YPQv9C60LjQtSDQstC10YnQuCI+0KXRgNGD0L/QutC4\n0LUg0LLQtdGJ0Lg8L2E+PC9oMT4KCQkJPGRpdiBjbGFzcz0iZGF0ZSI+Tm92\nZW1iZXIgNnRoLCAyMDA4PC9kaXY+CgkJCTxwPtCa0L7Qs9C00LAt0YLQviDQ\nsiDQvtC00L3QvtC8INGB0LXQu9C10L3QuNC4INC20LjQuyDRgdGC0LDRgNGL\n0Lkg0LzRg9C00YDRi9C5INGH0LXQu9C+0LLQtdC6LiDQntC9INC70Y7QsdC4\n0Lsg0LTQtdGC0LXQuSDQuCDQv9GA0L7QstC+0LTQuNC7INGBINC90LjQvNC4\nINC80L3QvtCz0L4g0LLRgNC10LzQtdC90LguINCV0YnRkSDQvtC9INC70Y7Q\nsdC40Lsg0LTQtdC70LDRgtGMINC40Lwg0L/QvtC00LDRgNC60LgsINC/0YDQ\nsNCy0LTQsCDQtNCw0YDQuNC7INC/0YDQtdC60YDQsNGB0L3Ri9C1LNC90L4g\n0L7Rh9C10L3RjCDRhdGA0YPQv9C60LjQtSDQstC10YnQuC4g0JrQsNC6INC9\n0Lgg0YHRgtCw0YDQsNC70LjRgdGMINC00LXRgtC4INCx0YvRgtGMINCw0LrQ\nutGD0YDQsNGC0L3Ri9C80LgsINC40YUg0L3QvtCy0YvQtSDQuNCz0YDRg9GI\n0LrQuCDRh9Cw0YHRgtC+INC70L7QvNCw0LvQuNGB0YwuINCU0LXRgtC4INGA\n0LDRgdGB0YLRgNCw0LjQstCw0LvQuNGB0Ywg0Lgg0LPQvtGA0YzQutC+INC/\n0LvQsNC60LDQu9C4LiDQkCDQvNGD0LTRgNC10YYg0YHQvdC+0LLQsCDQtNCw\n0YDQuNC7INC40Lwg0LjQs9GA0YPRiNC60LgsINC90L4g0LXRidGRINCx0L7Q\nu9C10LUg0YXRgNGD0L/QutC40LUg0Lgg0L3QtdC20L3Ri9C1JiM4MjMwOzxi\nciAvPgrQntC00L3QsNC20LTRiyDRgNC+0LTQuNGC0LXQu9C4INC90LUg0LLR\ni9C00LXRgNC20LDQu9C4INC4INC/0YDQuNGI0LvQuCDQuiDQvdC10LzRgzo8\nYnIgLz4K4oCUINCi0Ysg0LzRg9C00YAg0Lgg0LbQtdC70LDQtdGI0Ywg0L3Q\nsNGI0LjQvCDQtNC10YLRj9C8INGC0L7Qu9GM0LrQviDQtNC+0LHRgNCwLiDQ\nndC+INC30LDRh9C10Lwg0YLRiyDQtNC10LvQsNC10YjRjCDQuNC8INGC0LDQ\nutC40LUg0L/QvtC00LDRgNC60Lg/INCe0L3QuCDRgdGC0LDRgNCw0Y7RgtGB\n0Y8sINC60LDQuiDQvNC+0LPRg9GCLCDQvdC+INC40LPRgNGD0YjQutC4INCy\n0YHRkSDRgNCw0LLQvdC+INC70L7QvNCw0Y7RgtGB0Y8sINC4INC00LXRgtC4\nINC/0LvQsNGH0YPRgi48YnIgLz4K4oCUINCf0YDQvtC50LTRkdGCINGB0L7Q\nstGB0LXQvCDQvdC10LzQvdC+0LPQviDQu9C10YIsIOKAlCDRg9C70YvQsdC9\n0YPQu9GB0Y8g0YHRgtCw0YDQtdGGLCDigJQg0Lgg0LrRgtC+LdGC0L4g0L/Q\nvtC00LDRgNC40YIg0LjQvCDRgdCy0L7RkSDRgdC10YDQtNGG0LUuINCc0L7Q\nttC10YIg0LHRi9GC0YwsINGN0YLQviDQvdCw0YPRh9C40YIg0LjRhSDQvtCx\n0YDQsNGJ0LDRgtGM0YHRjyDRgSDRjdGC0LjQvCDQsdC10YHRhtC10L3QvdGL\n0Lwg0LTQsNGA0L7QvCDRhdC+0YLRjCDQvdC10LzQvdC+0LPQviDQsNC60LrR\ng9GA0LDRgtC90LXQuS48L3A+CgkJCTxkaXYgY2xhc3M9Im1ldGEiPgoJCQkJ\nCQkJCSDQlNC+0LHQsNCy0LvQtdC90L4g0LIgPGEgaHJlZj0iaHR0cDovL21l\nbnNoYWtvdi5ydS9jYXRlZ29yeS9wYXJhYmxlcy8iIHRpdGxlPSJWaWV3IGFs\nbCBwb3N0cyBpbiDQn9GA0LjRgtGH0LgiIHJlbD0iY2F0ZWdvcnkgdGFnIj7Q\nn9GA0LjRgtGH0Lg8L2E+IHwgPGEgaHJlZj0iaHR0cDovL21lbnNoYWtvdi5y\ndS8yMDA4LzExLzA2L2ZyYWdpbGUvI3Jlc3BvbmQiIHRpdGxlPSJDb21tZW50\nIG9uINCl0YDRg9C/0LrQuNC1INCy0LXRidC4Ij7QmtC+0LzQvNC10L3RgtCw\n0YDQuNC10LIg0L3QtdGCICYjMTg3OzwvYT4JCQk8L2Rpdj4KCQk8L2Rpdj4K\nCgkJCgkJCgkJPGRpdiBjbGFzcz0iZW50cnkiPgoJCQk8aDE+PGEgaHJlZj0i\naHR0cDovL21lbnNoYWtvdi5ydS8yMDA4LzA4LzI2L3RoaW5rLXBvc2l0aXZl\nbHktJWQwJWI0JWQxJTgzJWQwJWJjJWQwJWIwJWQwJWI5LSVkMCViZiVkMCVi\nZSVkMCViNyVkMCViOCVkMSU4MiVkMCViOCVkMCViMiVkMCViZCVkMCViZS8i\nIHJlbD0iYm9va21hcmsiIHRpdGxlPSJUaGluayBwb3NpdGl2ZWx5ICjQtNGD\n0LzQsNC5INC/0L7Qt9C40YLQuNCy0L3QvikhIj5UaGluayBwb3NpdGl2ZWx5\nICjQtNGD0LzQsNC5INC/0L7Qt9C40YLQuNCy0L3QvikhPC9hPjwvaDE+CgkJ\nCTxkaXYgY2xhc3M9ImRhdGUiPkF1Z3VzdCAyNnRoLCAyMDA4PC9kaXY+CgkJ\nCTxwPtCf0L7Qv9Cw0Lsg0YfQtdC70L7QstC10Log0LIg0YDQsNC5LiDQodC8\n0L7RgtGA0LjRgiwg0LAg0YLQsNC8INCy0YHQtSDQu9GO0LTQuCDRhdC+0LTR\nj9GCINGA0LDQtNC+0YHRgtC90YvQtSwg0YHRh9Cw0YHRgtC70LjQstGL0LUs\nINC+0YLQutGA0YvRgtGL0LUsINC00L7QsdGA0L7QttC10LvQsNGC0LXQu9GM\n0L3Ri9C1LiDQkCDQstC+0LrRgNGD0LMg0LLRgdC1INC60LDQuiDQsiDQvtCx\n0YvRh9C90L7QuSDQttC40LfQvdC4LiDQn9C+0YXQvtC00LjQuyDQvtC9LCDQ\nv9C+0LPRg9C70Y/Quywg0L/QvtC90YDQsNCy0LjQu9C+0YHRjC4g0Jgg0LPQ\nvtCy0L7RgNC40YIg0LDRgNGF0LDQvdCz0LXQu9GDOjwvcD4KPHA+4oCUINCQ\nINC80L7QttC90L4g0L/QvtGB0LzQvtGC0YDQtdGC0YwsINGH0YLQviDRgtCw\n0LrQvtC1INCw0LQ/INCl0L7RgtGMINC+0LTQvdC40Lwg0LPQu9Cw0LfQutC+\n0LwhPGJyIC8+CuKAlCDQpdC+0YDQvtGI0L4sINC/0L7QudC00LXQvCwg0L/Q\nvtC60LDQttGDLjwvcD4KPHA+0J/RgNC40YXQvtC00Y/RgiDQvtC90Lgg0LIg\n0LDQtC4g0KfQtdC70L7QstC10Log0YHQvNC+0YLRgNC40YIsINCwINGC0LDQ\nvCDQstGA0L7QtNC1INCx0Ysg0L3QsCDQv9C10YDQstGL0Lkg0LLQt9Cz0LvR\nj9C0INCy0YHQtSDRgtCw0Log0LbQtSDQutCw0Log0LIg0YDQsNGOOiDRgtCw\nINC20LUg0L7QsdGL0YfQvdCw0Y8g0LbQuNC30L3RjCwg0YLQvtC70YzQutC+\nINC70Y7QtNC4INCy0YHQtSDQt9C70YvQtSwg0L7QsdC40LbQtdC90L3Ri9C1\nLCDQstC40LTQvdC+LCDRh9GC0L4g0L/Qu9C+0YXQviDQuNC8INGC0YPRgi4g\n0J7QvSDRgdC/0YDQsNGI0LjQstCw0LXRgiDRgyDQsNGA0YXQsNC90LPQtdC7\n0LA6PC9wPgo8cD7igJQg0KLRg9GCINC20LUg0LLRgdC1INCy0YDQvtC00LUg\n0YLQsNC6INC20LUsINC60LDQuiDQuCDQsiDRgNCw0Y4hINCf0L7Rh9C10LzR\ngyDQvtC90Lgg0LLRgdC1INGC0LDQutC40LUg0L3QtdC00L7QstC+0LvRjNC9\n0YvQtT88YnIgLz4K4oCUINCQINC/0L7RgtC+0LzRgyDRh9GC0L4g0L7QvdC4\nINC00YPQvNCw0Y7Rgiwg0YfRgtC+INCyINGA0LDRjiDQu9GD0YfRiNC1Ljwv\ncD4KCQkJPGRpdiBjbGFzcz0ibWV0YSI+CgkJCQkJCQkJINCU0L7QsdCw0LLQ\nu9C10L3QviDQsiA8YSBocmVmPSJodHRwOi8vbWVuc2hha292LnJ1L2NhdGVn\nb3J5L3BhcmFibGVzLyIgdGl0bGU9IlZpZXcgYWxsIHBvc3RzIGluINCf0YDQ\nuNGC0YfQuCIgcmVsPSJjYXRlZ29yeSB0YWciPtCf0YDQuNGC0YfQuDwvYT4g\nfCA8YSBocmVmPSJodHRwOi8vbWVuc2hha292LnJ1LzIwMDgvMDgvMjYvdGhp\nbmstcG9zaXRpdmVseS0lZDAlYjQlZDElODMlZDAlYmMlZDAlYjAlZDAlYjkt\nJWQwJWJmJWQwJWJlJWQwJWI3JWQwJWI4JWQxJTgyJWQwJWI4JWQwJWIyJWQw\nJWJkJWQwJWJlLyNyZXNwb25kIiB0aXRsZT0iQ29tbWVudCBvbiBUaGluayBw\nb3NpdGl2ZWx5ICjQtNGD0LzQsNC5INC/0L7Qt9C40YLQuNCy0L3QvikhIj7Q\nmtC+0LzQvNC10L3RgtCw0YDQuNC10LIg0L3QtdGCICYjMTg3OzwvYT4JCQk8\nL2Rpdj4KCQk8L2Rpdj4KCgkJCgkJCg=="
  },
  {
    "path": "spec/std/data/large_single_line_string.txt",
    "content": "/wEPDwULLTE5OTkxOTUyMzAPZBYCZg9kFgRmD2QWAgICD2QWAmYPFgIeB1Zpc2libGVoZAIBD2QWIgICDw8WAh4EVGV4dAUd16nXnNeV150sINeT15XXktee15Ag15TXmdek15RkZAIDDw8WBB4ISW1hZ2VVcmwFGn4vQ1NTL0ltYWdlcy9Gb250U21hbGwucG5nHg1PbkNsaWVudENsaWNrBThjaGFuZ2VGb250U2l6ZSgnNTgzODM4MzgnLCcxJyxmYWxzZSx0cnVlKTsgcmV0dXJuIGZhbHNlOxYCHgVTdHlsZQUvbWFyZ2luLXRvcDo1cHg7bWFyZ2luLWxlZnQ6LTRweDtjdXJzb3I6cG9pbnRlcjtkAgQPDxYEHwIFHX4vQ1NTL0ltYWdlcy9Gb250QmlnX092ZXIucG5nHgdFbmFibGVkaBYCHwQFQW1hcmdpbi10b3A6NXB4O21hcmdpbi1yaWdodDotNHB4O21hcmdpbi1sZWZ0Oi03cHg7Y3Vyc29yOmRlZmF1bHQ7ZAIGDw8WAh8BBTPXqteo15XXpNeV16og16LXkyDXlNeR15nXqiDXkNeq16gg15zXodeR15nXkdeV16ogUUFkZAIHDxYEHgNhbHQFLtee15XXp9eTINep15nXqNeV16og15zXp9eV15fXldeqIDEtNzAwLTUwNy01MDceBXRpdGxlBS7XnteV16fXkyDXqdeZ16jXldeqINec16fXldeX15XXqiAxLTcwMC01MDctNTA3ZAIIDxYCHglpbm5lcmh0bWwFDTEtNzAwLTUwNy01MDdkAgoPZBYCZg9kFggCAQ8PFgIfAgVffi9Vc2VyQ29udHJvbC9VcGRhdGVVc2VyRGV0YWlscy9JbWFnZUhhbmRsZXIuYXNoeD9mYW1pbHlNZW1iZXI9MCZDYWNoZUJ1c3Rlcj02MzcwNDQ1MTA5MDA1MzM2ODdkZAIDDxYCHwgFHNeq15nXpyDXlNeR16jXmdeQ15XXqiDXqdec15pkAgUPFgIfAGdkAgcPFgIfAGgWBAIBDw8WAh8AaGRkAgMPD2QWAh4Hb25jbGljawUmT3BlblVwbG9hZEltYWdlV2luZG93KDApO3JldHVybiBmYWxzZTtkAgwPFgIeBWNsYXNzBRJtZW51X2l0ZW1fc2VsZWN0ZWRkAhIPFgIfAGdkAhMPFgIfAGdkAhcPFgIfAGdkAh4PFgIfAGhkAiIPZBYCAgIPFgIeC18hSXRlbUNvdW50AgQWCGYPZBYCAgEPFgIeBGNvZGUFAteRFgJmDxUBFdeQ15nXqdeV16gg15HXmden15XXqGQCAQ9kFgICAQ8WAh8MBQLXnhYCZg8VARPXkNeZ16nXldeoINee15fXnNeUZAICD2QWAgIBDxYCHwwFAteZFgJmDxUBGdeQ15nXqdeV16gg15zXktefL9eR15ki16FkAgMPZBYCAgEPFgIfDAUC15QWAmYPFQEh15DXmdep15XXqCDXotecINee15fXnNeqINeU15nXnNeTZAIjD2QWAgICDxYCHwsCAhYEZg9kFgICAQ8WAh8MBQLXpBYCZg8VAR7XlNek16DXmdeUINec15HXk9eZ16fXqiDXoteW16hkAgEPZBYCAgEPFgIfDAUC15MWAmYPFQEi15TXpNeg15nXlCDXnNeR15PXmden16og157XoteR15PXlGQCKw9kFgJmD2QWAmYPZBYEAgEPFgIfCwIEFggCAQ9kFgRmDxUBIHN0eWxlPSdiYWNrZ3JvdW5kLWNvbG9yOiMwYWIyNWQnZAIBDw8WAh4HVG9vbFRpcAUl15zXqteZ16cg15TXkdeo15nXkNeV16og16nXnCDXoteV157XqGQWAmYPFQQgc3R5bGU9J2JvcmRlcjoxcHggc29saWQgIzBhYjI1ZCc1L09ubGluZV9CZWxhX1RTX1FBL0NTUy9JbWFnZXMvUGVvcGxlL2dyZWVuX2JveV8yNS5qcGcgc3R5bGU9J2JhY2tncm91bmQtY29sb3I6IzBhYjI1ZCcI16LXldee16hkAgIPZBYEZg8VASBzdHlsZT0nYmFja2dyb3VuZC1jb2xvcjojZjI2NTIyJ2QCAQ8PFgIfDQUl15zXqteZ16cg15TXkdeo15nXkNeV16og16nXnCDXmdeV16DXlGQWAmYPFQQgc3R5bGU9J2JvcmRlcjoxcHggc29saWQgI2YyNjUyMic2L09ubGluZV9CZWxhX1RTX1FBL0NTUy9JbWFnZXMvUGVvcGxlL29yYW5nZV9ib3lfMjUuanBnIHN0eWxlPSdiYWNrZ3JvdW5kLWNvbG9yOiNmMjY1MjInCNeZ15XXoNeUZAIDD2QWBGYPFQEgc3R5bGU9J2JhY2tncm91bmQtY29sb3I6IzhkYzYzZidkAgEPDxYCHw0FKdec16rXmdenINeU15HXqNeZ15DXldeqINep15wg16rXmdeg15XXp9eqZBYCZg8VBCBzdHlsZT0nYm9yZGVyOjFweCBzb2xpZCAjOGRjNjNmJzwvT25saW5lX0JlbGFfVFNfUUEvQ1NTL0ltYWdlcy9QZW9wbGUvbGlnaHRfZ3JlZW5fZ2lybF8yNS5qcGcgc3R5bGU9J2JhY2tncm91bmQtY29sb3I6IzhkYzYzZicM16rXmdeg15XXp9eqZAIED2QWBGYPFQEgc3R5bGU9J2JhY2tncm91bmQtY29sb3I6IzAwYWVlZidkAgEPDxYCHw0FI9ec16rXmdenINeU15HXqNeZ15DXldeqINep15wg157XmdeUZBYCZg8VBCBzdHlsZT0nYm9yZGVyOjFweCBzb2xpZCAjMDBhZWVmJzsvT25saW5lX0JlbGFfVFNfUUEvQ1NTL0ltYWdlcy9QZW9wbGUvbGlnaHRfYmx1ZV9naXJsXzI1LmpwZyBzdHlsZT0nYmFja2dyb3VuZC1jb2xvcjojMDBhZWVmJwbXnteZ15RkAgMPFgIfAGdkAiwPZBYCAgEPZBYCZg9kFgICAQ9kFggCAQ8PFgIeClNlYXJjaE1vZGULKWFDb21tb24uRW51bXMrU2VhcmNoQXBwb2ludG1lbnRNb2RlLCBDb21tb24sIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsAWQWAmYPZBYCZg9kFgICAQ8WAh4Fc3R5bGUFFWJvcmRlci1jb2xvcjojMDA1NWE0OxYSZg8WAh8PBRliYWNrZ3JvdW5kLWNvbG9yOiMwMDU1YTQ7ZAIBDw8WAh8AaGRkAgIPFgIfCgUNUXVpY2tTY2hlZHVsZRYEAgMPFgIfDwUaZGlzcGxheTogYmxvY2sgIWltcG9ydGFudDsWBAIBDxAPFgIfAGhkZBYAZAIDDw8WBB8BBRXXqNek15XXkNeqINee16nXpNeX15QfAGcWAh4EQ29kZQUCNDRkAgUPFgIfAGhkAgMPFgIfAGgWAgIFDw8WAh8BZBYCHxAFATBkAgQPFgQfCgUNUXVpY2tTY2hlZHVsZR8AZxYEAgMPFgIfAGhkAgUPDxYEHwEFHNeh16DXmdejINen16DXmdeV158g15TXkdeQ16gfAGcWAh8QBQQxMDIxZAIFDxYEHwoFDVF1aWNrU2NoZWR1bGUfAGcWBgIDDw8WBB8BBRPXm9ecINeU16jXldek15DXmdedHwBoZGQCBQ8PFgQfAQUl15Mi16gg15zXmdeh15LXldeo16HXp9eZINeQ15nXlteR15zXlB8AZ2RkAgcPEA8WAh8AaGRkFgBkAgoPEA8WAh4LXyFEYXRhQm91bmRnZBAVIwUwNjowMAUwNjozMAUwNzowMAUwNzozMAUwODowMAUwODozMAUwOTowMAUwOTozMAUxMDowMAUxMDozMAUxMTowMAUxMTozMAUxMjowMAUxMjozMAUxMzowMAUxMzozMAUxNDowMAUxNDozMAUxNTowMAUxNTozMAUxNjowMAUxNjozMAUxNzowMAUxNzozMAUxODowMAUxODozMAUxOTowMAUxOTozMAUyMDowMAUyMDozMAUyMTowMAUyMTozMAUyMjowMAUyMjozMAUyMzowMBUjBTA2OjAwBTA2OjMwBTA3OjAwBTA3OjMwBTA4OjAwBTA4OjMwBTA5OjAwBTA5OjMwBTEwOjAwBTEwOjMwBTExOjAwBTExOjMwBTEyOjAwBTEyOjMwBTEzOjAwBTEzOjMwBTE0OjAwBTE0OjMwBTE1OjAwBTE1OjMwBTE2OjAwBTE2OjMwBTE3OjAwBTE3OjMwBTE4OjAwBTE4OjMwBTE5OjAwBTE5OjMwBTIwOjAwBTIwOjMwBTIxOjAwBTIxOjMwBTIyOjAwBTIyOjMwBTIzOjAwFCsDI2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZGQCDA8QDxYCHxFnZBAVIwUwNjowMAUwNjozMAUwNzowMAUwNzozMAUwODowMAUwODozMAUwOTowMAUwOTozMAUxMDowMAUxMDozMAUxMTowMAUxMTozMAUxMjowMAUxMjozMAUxMzowMAUxMzozMAUxNDowMAUxNDozMAUxNTowMAUxNTozMAUxNjowMAUxNjozMAUxNzowMAUxNzozMAUxODowMAUxODozMAUxOTowMAUxOTozMAUyMDowMAUyMDozMAUyMTowMAUyMTozMAUyMjowMAUyMjozMAUyMzowMBUjBTA2OjAwBTA2OjMwBTA3OjAwBTA3OjMwBTA4OjAwBTA4OjMwBTA5OjAwBTA5OjMwBTEwOjAwBTEwOjMwBTExOjAwBTExOjMwBTEyOjAwBTEyOjMwBTEzOjAwBTEzOjMwBTE0OjAwBTE0OjMwBTE1OjAwBTE1OjMwBTE2OjAwBTE2OjMwBTE3OjAwBTE3OjMwBTE4OjAwBTE4OjMwBTE5OjAwBTE5OjMwBTIwOjAwBTIwOjMwBTIxOjAwBTIxOjMwBTIyOjAwBTIyOjMwBTIzOjAwFCsDI2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZGQCDQ9kFgJmD2QWBAIBDw8WBB4FV2lkdGgbAAAAAACgYUABAAAAHgRfIVNCAoACZGQCAw8WAh8LAggWEGYPZBYGZg8VAQEwZAIBDxAPZBYCHgV2YWx1ZQUBMGRkZAIDDw8WAh8BBQ/Xm9ecINeU15nXnteZ151kZAIBD2QWBmYPFQEBMWQCAQ8QD2QWAh8UBQExZGRkAgMPDxYCHwEFCteo15DXqdeV159kZAICD2QWBmYPFQEBMmQCAQ8QD2QWAh8UBQEyZGRkAgMPDxYCHwEFBtep16DXmWRkAgMPZBYGZg8VAQEzZAIBDxAPZBYCHxQFATNkZGQCAw8PFgIfAQUK16nXnNeZ16nXmWRkAgQPZBYGZg8VAQE0ZAIBDxAPZBYCHxQFATRkZGQCAw8PFgIfAQUK16jXkdeZ16LXmWRkAgUPZBYGZg8VAQE1ZAIBDxAPZBYCHxQFATVkZGQCAw8PFgIfAQUK15fXnteZ16nXmWRkAgYPZBYGZg8VAQE2ZAIBDxAPZBYCHxQFATZkZGQCAw8PFgIfAQUI16nXmdep15lkZAIHD2QWBmYPFQEBN2QCAQ8QD2QWAh8UBQE3ZGRkAgMPDxYCHwEFBtep15HXqmRkAgMPZBYCAgEPZBYCZg9kFgICAQ8WAh8AaBYCAgEPFgIfDwVtYmFja2dyb3VuZC1jb2xvcjojMDA1NWE0O2JhY2tncm91bmQtaW1hZ2U6dXJsKC4uL0NTUy9JbWFnZXMvUGVyc29uYWxEb2N0b3IvVGFibGVfSWNvbl8xX1JvZmVfSXNoaV9HcmVlbi5wbmcpOxYEAgEPFgIfCAUw15bXmdee15XXnyDXnteU15nXqCDXnNeo15XXpNeQINeU15DXmdep15kg16nXnNeZZAIDDzwrAA0BAA8WBB8RZx8LAgFkFgJmD2QWBmYPZBYCZg8PFgQeCENzc0NsYXNzBQ5yb3VuZGVkQ29yZW5lch8TAgIWAh8PBVFiYWNrZ3JvdW5kLWltYWdlOnVybCguLi9DU1MvSW1hZ2VzL1BlcnNvbmFsRG9jdG9yL1RhYmxlX3JvdW5kX2Nvcm5lcl8wMDU1YTQucG5nKTtkAgEPZBYKZg9kFgICAQ8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUFgIfEAUCNDRkAgEPZBYCAgEPDxYCHwEFJdeTIteoINec15nXodeS15XXqNeh16fXmSDXkNeZ15bXkdec15QWAh4NRG9jdG9yTGljZW5zZQUFMzA2MDBkAgIPZBYCAgEPDxYCHwEFL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefFgoeDk1haW5CcmFuY2hDb2RlBQIxMB4NU3ViQnJhbmNoQ29kZQUCMjEeCkJyYW5jaE5hbWUFHNeh16DXmdejINen16DXmdeV158g15TXkdeQ16geBENpdHkFBDgzMDAfCQVPc2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTsgcmV0dXJuIGZhbHNlO2QCAw9kFgICAQ8PFgIfAQUKMTkvMDkvMjAxOWRkAgQPZBYCAgEPDxYEHwEFDSAgIDAzLTcyOTMzMDAfAGhkZAICDw8WAh8AaGRkAgUPDxYCHgxwYXN0QXBwb2ludHMyqq0CAAEAAAD/////AQAAAAAAAAAMAgAAAD1Db21tb24sIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBAEAAACCAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkxpc3RgMVtbQ29tbW9uLkNsYXNzLkRvY3RvckFwcG9pbnRtZW50LCBDb21tb24sIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsXV0DAAAABl9pdGVtcwVfc2l6ZQhfdmVyc2lvbgQAACBDb21tb24uQ2xhc3MuRG9jdG9yQXBwb2ludG1lbnRbXQIAAAAICAkDAAAAdQAAAHwAAAAHAwAAAAABAAAAgAAAAAQeQ29tbW9uLkNsYXNzLkRvY3RvckFwcG9pbnRtZW50AgAAAAkEAAAACQUAAAAJBgAAAAkHAAAACQgAAAAJCQAAAAkKAAAACQsAAAAJDAAAAAkNAAAACQ4AAAAJDwAAAAkQAAAACREAAAAJEgAAAAkTAAAACRQAAAAJFQAAAAkWAAAACRcAAAAJGAAAAAkZAAAACRoAAAAJGwAAAAkcAAAACR0AAAAJHgAAAAkfAAAACSAAAAAJIQAAAAkiAAAACSMAAAAJJAAAAAklAAAACSYAAAAJJwAAAAkoAAAACSkAAAAJKgAAAAkrAAAACSwAAAAJLQAAAAkuAAAACS8AAAAJMAAAAAkxAAAACTIAAAAJMwAAAAk0AAAACTUAAAAJNgAAAAk3AAAACTgAAAAJOQAAAAk6AAAACTsAAAAJPAAAAAk9AAAACT4AAAAJPwAAAAlAAAAACUEAAAAJQgAAAAlDAAAACUQAAAAJRQAAAAlGAAAACUcAAAAJSAAAAAlJAAAACUoAAAAJSwAAAAlMAAAACU0AAAAJTgAAAAlPAAAACVAAAAAJUQAAAAlSAAAACVMAAAAJVAAAAAlVAAAACVYAAAAJVwAAAAlYAAAACVkAAAAJWgAAAAlbAAAACVwAAAAJXQAAAAleAAAACV8AAAAJYAAAAAlhAAAACWIAAAAJYwAAAAlkAAAACWUAAAAJZgAAAAlnAAAACWgAAAAJaQAAAAlqAAAACWsAAAAJbAAAAAltAAAACW4AAAAJbwAAAAlwAAAACXEAAAAJcgAAAAlzAAAACXQAAAAJdQAAAAl2AAAACXcAAAAJeAAAAA0LBQQAAAAeQ29tbW9uLkNsYXNzLkRvY3RvckFwcG9pbnRtZW50IgAAACA8QXBwb2ludG1lbnREYXRlPmtfX0JhY2tpbmdGaWVsZB88QXBwb2ludG1lbnREYXk+a19fQmFja2luZ0ZpZWxkGTxGcm9tSG91cj5rX19CYWNraW5nRmllbGQXPFRvSG91cj5rX19CYWNraW5nRmllbGQcPERvY3RvclRpdGxlPmtfX0JhY2tpbmdGaWVsZBs8RG9jdG9yTmFtZT5rX19CYWNraW5nRmllbGQkPERvY3RvclNwZWNpYWxpdGFpb24+a19fQmFja2luZ0ZpZWxkLzxEb2N0b3JTcGVjaWFsaXRhaW9uRGVzY3JpcHRpb24+a19fQmFja2luZ0ZpZWxkHjxEb2N0b3JMaWNlbnNlPmtfX0JhY2tpbmdGaWVsZBw8RG9jdG9yTm90ZXM+a19fQmFja2luZ0ZpZWxkGzxCcmFuY2hOYW1lPmtfX0JhY2tpbmdGaWVsZB48TWFpbkJybmNoQ29kZT5rX19CYWNraW5nRmllbGQdPFN1YkJybmNoQ29kZT5rX19CYWNraW5nRmllbGQePFJlY2VwdGlvblNpdGU+a19fQmFja2luZ0ZpZWxkHDxIb3VzZU51bWJlcj5rX19CYWNraW5nRmllbGQXPFN0cmVldD5rX19CYWNraW5nRmllbGQVPENpdHk+a19fQmFja2luZ0ZpZWxkJDxJbnZpdGF0aW9uSW5kaWNhdG9yPmtfX0JhY2tpbmdGaWVsZB48SXNTaG93SW5NYWhpcj5rX19CYWNraW5nRmllbGQWPFBob25lPmtfX0JhY2tpbmdGaWVsZBY8UHJpY2U+a19fQmFja2luZ0ZpZWxkIDxFeGFtaW5hdGlvbkNvZGU+a19fQmFja2luZ0ZpZWxkJzxFeGFtaW5hdGlvbkRlc2NyaXB0aW9uPmtfX0JhY2tpbmdGaWVsZB48SW5zdGl0dXRlQ29kZT5rX19CYWNraW5nRmllbGQgPFJlbWFpbmRlck9wdGlvbj5rX19CYWNraW5nRmllbGQlPElzUHJvZmVzc2lvbmFsRG9jdG9yPmtfX0JhY2tpbmdGaWVsZBo8UGF0aWVudElkPmtfX0JhY2tpbmdGaWVsZCI8T25saW5lQXBwb2ludG1lbnQ+a19fQmFja2luZ0ZpZWxkIjxCcmFuY2hBcHBvaW50bWVudD5rX19CYWNraW5nRmllbGQhPE9ubGluZUluZGljYXRpb24+a19fQmFja2luZ0ZpZWxkHzxTaXRlQWNjZXB0YW5jZT5rX19CYWNraW5nRmllbGQdPFNpdGVQcm9wZXJ0eT5rX19CYWNraW5nRmllbGQcPFZpc2l0TnVtYmVyPmtfX0JhY2tpbmdGaWVsZBU8VHlwZT5rX19CYWNraW5nRmllbGQAAQEBAQEBAQEBAQEBAQEBAQABAQABAQEBAAEBAQAAAQEEDQUIAQEFHENvbW1vbi5DbGFzcy5BcHBvaW50bWVudFR5cGUCAAAAAgAAAAAMkMAoudYIBnkAAAAC15QKCgZ6AAAAAAZ7AAAAH9eV15XXnNen15XXkdeUINec15nXldeT157Xmdec15QGfAAAAAE1Bn0AAAAI16DXqdeZ150GfgAAAAYxMTU3NjAJegAAAAoGgAAAAAI4MwaBAAAAATYGggAAABvXodeg15nXoyDXkifXkdecINee15XXm9eR16gGgwAAAAExBoQAAAAS15In15HXnCDXnteV15vXkdeoBoUAAAAO15nXqNeV16nXnNeZ150BMgaGAAAAATEGhwAAAA0gICAwMi02NzMwNjg2AAAAAAl6AAAACXoAAAAJegAAAAaJAAAAATABBooAAAAINTgzODM4MzgGiwAAAAExCgABMAoKBXT///8cQ29tbW9uLkNsYXNzLkFwcG9pbnRtZW50VHlwZQEAAAAHdmFsdWVfXwAIAgAAAAAAAAABBQAAAAQAAAAAOlz+LbjWCAaNAAAAAteTCgoGjgAAAAXXkyLXqAaPAAAAD9eo15XXmCDXoteV157XqAaQAAAAATYGkQAAAAzXoteZ16DXmdeZ150GkgAAAAYxMzAxOTgJegAAAAoGlAAAAAIxNwaVAAAAAjYwBpYAAAAR16HXoNeZ16Mg15DXmdec16oGlwAAAAIxMgaYAAAAEdeS158t15HXoNeZ157XmdefBpkAAAAI15DXmdec16oBMgaaAAAAATEGmwAAAA0gICAwOC02MzA2MjAwAAAAAAl6AAAACXoAAAAJegAAAAadAAAAATABCYoAAAAGnwAAAAExCgABMAoKAWD///90////AAAAAAEGAAAABAAAAAAs/KhMt9YIBqEAAAAC15IKCgaiAAAABdeRIteQBqMAAAAU15LXldeZ15jXlCDXkdeqINeQ15wGpAAAAAI5NgalAAAAG9en15zXmdeg15DXmdeqINeq16fXqdeV16jXqgamAAAABjEwMDI0OAl6AAAACgaoAAAAATYGqQAAAAI2NAaqAAAAEdeh16DXmdejINeX16jXmdepBqsAAAACMzkGrAAAAA3Xk9eo15og15DXqNelBq0AAAAI15fXqNeZ16kBMgauAAAAATEGrwAAAA0gICAwNC04ODAwNDAwAAAAAAl6AAAACXoAAAAJegAAAAaxAAAAATABCYoAAAAGswAAAAExCgABMAoKAUz///90////AAAAAAEHAAAABAAAAABySe8KttYIBrUAAAAC15AKCga2AAAABdeS15EnBrcAAAAU15cn15XXqNeZINeQ15fXnNeQ150GuAAAAAIzMwa5AAAAHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUBroAAAAGMTMxMzE1CXoAAAAKBrwAAAACMjAGvQAAAAIxMQa+AAAAHNeh16DXmdejINeQ16nXk9eV15Mg16HXmdeY15kGvwAAAAE3BsAAAAAR157XoNeX150g15HXkteZ158GwQAAAArXkNep15PXldeTATIGwgAAAAExBsMAAAANICAgMDgtODU3OTgwMAAAAAAJegAAAAl6AAAACXoAAAAGxQAAAAEwAQmKAAAABscAAAABMQoAATAKCgE4////dP///wAAAAABCAAAAAQAAAAAxjk4gLPWCAbJAAAAAteUCgoGygAAAAXXkyLXqAbLAAAAENeR16jXnteZINeSJ9eV158GzAAAAAIzNAbNAAAACNeh15vXqNeqBs4AAAAFMTY4MTYJegAAAAoG0AAAAAIyMAbRAAAAAjcwBtIAAAAb16HXoNeZ16Mg16fXqNeZ16og15LXqiDXnteZBtMAAAABOAbUAAAACNee15nXm9eUBtUAAAAN16fXqNeZ16og15LXqgEyBtYAAAABMAbXAAAADSAgIDA4LTY4Nzk4ODgAAAAACXoAAAAJegAAAAl6AAAABtkAAAABMAEJigAAAAbbAAAAATEKAAEwCgoBJP///3T///8AAAAAAQkAAAAEAAAAAA6+A3Cz1ggG3QAAAALXlAoKBt4AAAAF15LXkScG3wAAABvXlNeo15jXnteQ15nXqCDXqdeV15zXnteZ16oG4AAAAAMxMjMG4QAAABvXkteg15jXmden15Qg16fXnNeZ16DXmdeqIC0G4gAAAAY5OTk5MjIJegAAAAoG5AAAAAEyBuUAAAABMwbmAAAAGNeh16DXmdejINeZ15Mg15DXnNeZ15TXlQbnAAAAATgG6AAAAAzXk9eZ157Xldeg15QG6QAAABTXqtecINeQ15HXmdeRLdeZ16TXlQEyBuoAAAABMQbrAAAADSAgIDAzLTYzMDQ2MDAAAAAACXoAAAAJegAAAAl6AAAABu0AAAABMAEJigAAAAbvAAAAATEKAAEwCgoBEP///3T///8AAAAAAQoAAAAEAAAAAKiiPMiy1ggG8QAAAALXkwoKBvIAAAAF15Mi16gG8wAAABfXkdeV16fXmdefINeQ15zXm9eh15nXmQb0AAAAATMG9QAAABvXm9eZ16jXldeo15LXmdeUINeb15zXnNeZ16oG9gAAAAUzMzEzOAl6AAAACgb4AAAAAjExBvkAAAACMjAG+gAAABzXodeg15nXoyDXp9eo15nXqiDXqdee15XXoNeUBvsAAAABNAb8AAAAFteY16nXqNeg15nXl9eV15HXoden15kG/QAAABPXp9eo15nXqiDXqdee15XXoNeUATIG/gAAAAExBv8AAAANICAgMDQtNjgxODYwMAAAAAAJegAAAAl6AAAACXoAAAAGAQEAAAEwAQmKAAAABgMBAAABMQoAATAKCgH8/v//dP///wAAAAABCwAAAAQAAAAA+nfVrbLWCAYFAQAAAteTCgoGBgEAAAXXkyLXqAYHAQAAF9eo15XXmNep15jXmdefINeY15zXmdeUBggBAAABNgYJAQAADNei15nXoNeZ15nXnQYKAQAABjE0MDkwMgl6AAAACgYMAQAAAjE3Bg0BAAACNjAGDgEAABHXodeg15nXoyDXkNeZ15zXqgYPAQAAAjEyBhABAAAR15LXny3Xkdeg15nXnteZ158GEQEAAAjXkNeZ15zXqgEyBhIBAAABMQYTAQAADSAgIDA4LTYzMDYyMDAAAAAACXoAAAAJegAAAAl6AAAABhUBAAABMAEJigAAAAYXAQAAATEKAAEwCgoB6P7//3T///8AAAAAAQwAAAAEAAAAAO5krgOy1ggGGQEAAALXkgoKBhoBAAAF15Mi16gGGwEAABXXmdek16jXnteV15Eg15DXldec15IGHAEAAAEzBh0BAAAb15vXmdeo15XXqNeS15nXlCDXm9ec15zXmdeqBh4BAAAFMzY0MTAJegAAAAoGIAEAAAIxMQYhAQAAAjIyBiIBAAAc16HXoNeZ16Mg15fXpteV16gg15TXktec15nXnAYjAQAAATIGJAEAABHXkdefINeS15XXqNeZ15XXnwYlAQAAF9eX16bXldeoINeU15LXnNeZ15zXmdeqATIGJgEAAAEwBicBAAANICAgMDQtNjgwODkwMAAAAAAJegAAAAl6AAAACXoAAAAGKQEAAAEwAQmKAAAABisBAAABMQoAATAKCgHU/v//dP///wAAAAABDQAAAAQAAAAAuNgw2rHWCAYtAQAAAteSCgoGLgEAAAXXkyLXqAYvAQAAEdeQ15zXkdeWINee16jXodecBjABAAABMgYxAQAACNeQLteQLteSBjIBAAAFNjk0MjkJegAAAAoGNAEAAAIyMAY1AQAAATQGNgEAABzXodeg15nXoyDXnteo15vXliDXkNec157XldeSBjcBAAACNzEGOAEAAAjXm9eg16jXqgY5AQAACteQ16nXk9eV15MBMgY6AQAAATEGOwEAAA0gICAwOC04Njg3NjAwAAAAAAl6AAAACXoAAAAJegAAAAY9AQAAATEBCYoAAAAGPwEAAAExCgABMAoKAcD+//90////AAAAAAEOAAAABAAAAAAYaovKsdYIBkEBAAAC15IKCgZCAQAABdeTIteoBkMBAAAZ15XXoNeY15XXqNeo15Ug157Xldeo15nXoQZEAQAAATMGRQEAABvXm9eZ16jXldeo15LXmdeUINeb15zXnNeZ16oGRgEAAAUyNjU3MAl6AAAACgZIAQAAATYGSQEAAAIxMQZKAQAAG9eh16DXmdejINec15nXnNeZ16DXkdec15XXnQZLAQAAAjEzBkwBAAAS15zXmdec15nXoNeR15zXldedBk0BAAAK16DXqteg15nXlAEyBk4BAAABMQZPAQAADSAgIDA5LTg4OTY2MDAAAAAACXoAAAAJegAAAAl6AAAABlEBAAABMAEJigAAAAZTAQAAATEKAAEwCgoBrP7//3T///8AAAAAAQ8AAAAEAAAAAGAzaBmx1ggGVQEAAALXkQoKBlYBAAAF15LXkScGVwEAABPXodeR15nXldefINeg15DXldeUBlgBAAACNzkGWQEAAArXk9eZ15DXmNeUBloBAAAGMTg4MzY2BlsBAAA515TXk9eZ15DXmNefL9eg15nXqiDXnteY16TXnC/XqiDXkdeU16TXqNei15XXqiDXkNeb15nXnNeUCgZcAQAAAjgxBl0BAAABMgZeAQAAFNeh16DXmdejINeU16gg16DXldejBl8BAAACNTgGYAEAABXXlNeo15Eg16nXkNeV15zXlteV158GYQEAAA7Xmdeo15XXqdec15nXnQEyBmIBAAABMAZjAQAADSAgIDAyLTY1MTE2MDIAAAAACXoAAAAJegAAAAl6AAAABmUBAAABMAEJigAAAAZnAQAAATEKAQEwCgoBmP7//3T///8AAAAAARAAAAAEAAAAAFgAYQGx1ggGaQEAAALXkQoKBmoBAAAF15Mi16gGawEAABfXkNeR15Ug15cn15zXoyDXodeQ157XqAZsAQAAAjM4Bm0BAAAV157Xmdeg15TXnCDXqNek15XXkNeZBm4BAAAFODQ2NTUJegAAAAoGcAEAAAI4MwZxAQAAAjk5BnIBAAAa16HXoNeZ16Mg157XoNeULiDXnteWLteZ16gJegAAAAl6AAAABnQBAAAO15nXqNeV16nXnNeZ150BMgZ1AQAAATEGdgEAAA0gICAwMi01NzIyMzc3AAAAAAl6AAAACXoAAAAJegAAAAZ4AQAAATABCYoAAAAGegEAAAExCgABMAoKAYX+//90////AAAAAAERAAAABAAAAAAExoxVsNYIBnwBAAAC15AKCgZ9AQAABdeTIteoBn4BAAAb15PXldep16DXmdem16fXmSDXk9eV16jXldefBn8BAAACMzgGgAEAABXXnteZ16DXlNecINeo16TXldeQ15kGgQEAAAUyNzg4OQl6AAAACgaDAQAAAjkyBoQBAAABMQaFAQAAG9eh16DXmdejINeULteQ15LXoyDXqNek15XXkAaGAQAAAjIzBocBAAAO16nXpNeo15nXoNem16cGiAEAABTXqtecINeQ15HXmdeRLdeZ16TXlQEyBokBAAABMQaKAQAADSAgIDAzLTY5NzAzMTYAAAAACXoAAAAJegAAAAl6AAAABowBAAABMAEJigAAAAaOAQAAATEKAAEwCgoBcf7//3T///8AAAAAARIAAAAEAAAAAOqxkkGw1ggGkAEAAALXkAoKBpEBAAAF154i15AGkgEAAA3Xoden15wg15fXoNeZBpMBAAACNzkGlAEAAArXk9eZ15DXmNeUBpUBAAAFOTUwNjIJegAAAAoGlwEAAAEzBpgBAAABMQaZAQAAHNeh16DXmdejINee16HXmdec16og15nXqdeo15kGmgEAAAIxNAabAQAAFdee16HXmdec16og15nXqdeo15nXnQacAQAADteZ16jXldep15zXmdedATIGnQEAAAExBp4BAAANICAgMDItNTY3NzY3NwAAAAAJegAAAAl6AAAACXoAAAAGoAEAAAEwAQmKAAAABqIBAAABMQoAATAKCgFd/v//dP///wAAAAABEwAAAAQAAAAArt8H+63WCAakAQAAAteUCgoGpQEAAAXXkteRJwamAQAAE9eh15HXmdeV158g16DXkNeV15QGpwEAAAI3OQaoAQAACteT15nXkNeY15QGqQEAAAYxODgzNjYJegAAAAoGqwEAAAI4MQasAQAAATEGrQEAABPXodeg15nXoyDXkteQ15XXnNeUBq4BAAABMQavAQAACNei157XldehBrABAAAO15nXqNeV16nXnNeZ150BMgaxAQAAATEGsgEAAA0gICAwMi02MjI4MTExAAAAAAl6AAAACXoAAAAJegAAAAa0AQAAATABCYoAAAAGtgEAAAExCgEBMAoKAUn+//90////AAAAAAEUAAAABAAAAABY++YprdYIBrgBAAAC15MKCga5AQAABdeTIteoBroBAAAQ15HXqNee15kg15In15XXnwa7AQAAAjM0BrwBAAAI16HXm9eo16oGvQEAAAUxNjgxNgl6AAAACga/AQAAAjE3BsABAAACMTQGwQEAABjXodeg15nXoyDXoNeV15XXlCDXlteQ15EGwgEAAAE5BsMBAAAN15HXqNepINeQ16nXqAbEAQAADdeR15DXqCDXqdeR16IBMgbFAQAAATAGxgEAAA0gICAwOC02NDcxNTU1AAAAAAl6AAAACXoAAAAJegAAAAbIAQAAATABCYoAAAAGygEAAAExCgABMAoKATX+//90////AAAAAAEVAAAABAAAAABMi6GRrNYIBswBAAAC15IKCgbNAQAABdeTIteoBs4BAAAT16nXqNei15HXmSDXkNee15nXqAbPAQAAAjE0BtABAAAd16jXkNeV157XmNeV15zXldeS15nXlC/Xntek16gG0QEAAAUzNDA2OQl6AAAACgbTAQAAATkG1AEAAAI1MgbVAQAAGteh16DXmdejINen16jXmdeqINeQ15XXoNeVBtYBAAACMzEG1wEAAAjXldeV15zXmgbYAQAAEden16jXmdeqINeQ15XXoNeVATIG2QEAAAExBtoBAAANICAgMDMtNTMwODMwMAAAAAAJegAAAAl6AAAACXoAAAAG3AEAAAEwAQmKAAAABt4BAAABMQoAATAKCgEh/v//dP///wAAAAABFgAAAAQAAAAALlASA6vWCAbgAQAAAteQCgoG4QEAAAXXkteRJwbiAQAAE9eh15HXmdeV158g16DXkNeV15QG4wEAAAI3OQbkAQAACteT15nXkNeY15QG5QEAAAYxODgzNjYJegAAAAoG5wEAAAI0NAboAQAAAjEwBukBAAAb16HXoNeZ16Mg15LXkdei16og16nXqNeqLdeRBuoBAAABNgbrAQAADdeR16gg15DXmdec158G7AEAAA3XkdeZ16og16nXntepATIG7QEAAAEwBu4BAAANICAgMDItOTkwNjc3NwAAAAAJegAAAAl6AAAACXoAAAAG8AEAAAEwAQmKAAAABvIBAAABMQoBATAKCgEN/v//dP///wAAAAABFwAAAAQAAAAA8O2rr6rWCAb0AQAAAteQCgoG9QEAAAXXkteRJwb2AQAADdep15PXlCDXkteR15kG9wEAAAIzMwb4AQAAHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUBvkBAAAFOTUxMjMJegAAAAoG+wEAAAE5BvwBAAABNgb9AQAAHNeh16DXmdejINeQ150g15TXnteV16nXkdeV16oG/gEAAAIxMQb/AQAAF9ek15nXoNep15jXmdeZ158g15fXmdeUBgACAAAR16TXqteXINeq16fXldeV15QBMgYBAgAAATEGAgIAAA0gICAwMy03Mjg5OTAwAAAAAAl6AAAACXoAAAAJegAAAAYEAgAAATABCYoAAAAGBgIAAAExCgABMAoKAfn9//90////AAAAAAEYAAAABAAAAADc69yHqNYIBggCAAAC15QKCgYJAgAABdeTIteoBgoCAAAQ15HXqNee15kg15In15XXnwYLAgAAAjM4BgwCAAAV157Xmdeg15TXnCDXqNek15XXkNeZBg0CAAAFMTY4MTYJegAAAAoGDwIAAAI4OAYQAgAAATEGEQIAABvXodeg15nXoyDXlNeg15TXnNeqINeeLteT16gGEgIAAAIxMAYTAgAAFNeR158g16bXkdeZINeZ16bXl9enBhQCAAAN15HXkNeoINep15HXogEyBhUCAAABMQYWAgAADSAgIDA4LTYyMDMyMDMAAAAACXoAAAAJegAAAAl6AAAABhgCAAABMAEJigAAAAYaAgAAATEKAAEwCgoB5f3//3T///8AAAAAARkAAAAEAAAAAAAKL6mn1ggGHAIAAALXkwoKBh0CAAAF15Mi16gGHgIAABXXkteo15bXldeW15kg15fXkNeq150GHwIAAAEyBiACAAAI15Au15Au15IGIQIAAAUzMDMzOQl6AAAACgYjAgAAAjEwBiQCAAACMzQGJQIAABbXodeg15nXoyDXkteg15kg15nXoteoBiYCAAACMTQGJwIAAA7XlNeQ15nXmdec15XXnwYoAgAABtec15XXkwEyBikCAAABMQYqAgAADSAgIDA4LTk1ODM1NTUAAAAACXoAAAAJegAAAAl6AAAABiwCAAABMAEJigAAAAYuAgAAATEKAAEwCgoB0f3//3T///8AAAAAARoAAAAEAAAAAIIQQVqn1ggGMAIAAALXkwoKBjECAAAF15Mi16gGMgIAABPXqNeV16TXkCDXk9eV15LXnteQBjMCAAABMwY0AgAAG9eb15nXqNeV16jXkteZ15Qg15vXnNec15nXqgY1AgAABTg4ODg4CXoAAAAKBjcCAAABMgY4AgAAATEGOQIAABvXodeg15nXoyDXqtecINeQ15HXmdeRINeo15AGOgIAAAE0BjsCAAAK15TXpNeY157XnwY8AgAAFNeq15wg15DXkdeZ15Et15nXpNeVATIGPQIAAAEwBj4CAAANICAgMDMtNjk3MDMwNgAAAAAJegAAAAl6AAAACXoAAAAGQAIAAAEwAQmKAAAABkICAAABMQoAATAKCgG9/f//dP///wAAAAABGwAAAAQAAAAAQObHA6fWCAZEAgAAAteSCgoGRQIAAAXXkyLXqAZGAgAAFdeQ15LXqNeg15gg15DXkdeo15TXnQZHAgAAAzEwNQZIAgAAEdee15nXldefINeg16nXmdedBkkCAAAFMTI4NTkJegAAAAoGSwIAAAE2BkwCAAABMQZNAgAAG9eh16DXmdejINeg16rXoNeZ15Qg15zXkSDXlAZOAgAAAjE3Bk8CAAAM16nXmNee16TXpNeoBlACAAAK16DXqteg15nXlAEyBlECAAABMQZSAgAADSAgIDA5LTg2MDcyMjIAAAAACXoAAAAJegAAAAl6AAAABlQCAAABMQEJigAAAAZWAgAAATEKAAEwCgoBqf3//3T///8AAAAAARwAAAAEAAAAADwdAzym1ggGWAIAAALXkQoKBlkCAAAF15Mi16gGWgIAAA/XlteS15wg157Xl9ee15MGWwIAAAIzMgZcAgAAHdeo15PXmdeV15zXldeS15nXlCDXkNeR15fXoNeqBl0CAAAGMTEzNzkxCXoAAAAKBl8CAAACODMGYAIAAAEyBmECAAAY16HXoNeZ16Mg16bXldeoINeR15DXl9eoCXoAAAAGYwIAAA/XpteV16gg15HXkNeX16gGZAIAAA7Xmdeo15XXqdec15nXnQEyBmUCAAABMQZmAgAADSAgIDAyLTY0NDIzMDAAAAAACXoAAAAJegAAAAl6AAAABmgCAAABMAEJigAAAAZqAgAAATEKAAEwCgoBlf3//3T///8AAAAAAR0AAAAEAAAAAIwx9DGm1ggGbAIAAALXkQoKBm0CAAAF15Mi16gGbgIAAA/XmNeU15Qg16LXldee16gGbwIAAAE3BnACAAAP16LXldeoINeV157XmdefBnECAAAFODMyMzkJegAAAAoGcwIAAAE5BnQCAAABMQZ1AgAAGNeh16DXmdejINek16rXlyDXqten15XXlAZ2AgAAAjE4BncCAAAT15nXlNeV15PXlCDXlNec15XXmQZ4AgAAEdek16rXlyDXqten15XXldeUATIGeQIAAAExBnoCAAANICAgMDMtOTA1MTMxMwAAAAAJegAAAAl6AAAACXoAAAAGfAIAAAEwAQmKAAAABn4CAAABMQoAATAKCgGB/f//dP///wAAAAABHgAAAAQAAAAAcnoYDKbWCAaAAgAAAteRCgoGgQIAAAXXkSLXkAaCAgAAFteSJ9eV15HXqNeQ158g16HXkNec15cGgwIAAAIzMwaEAgAAHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUBoUCAAAGMTAwMTY0CXoAAAAKBocCAAACMTAGiAIAAAIyMwaJAgAAG9eh16DXmdejINeYLteR15nXqiDXqNeQ16nXnAaKAgAAATcGiwIAAA7XqNeV15jXqdeZ15zXkwaMAgAAFdeo15DXqdeV158g15zXpteZ15XXnwEyBo0CAAABMQaOAgAADSAgIDAzLTk1OTc4MDAAAAAACXoAAAAJegAAAAl6AAAABpACAAABMAEJigAAAAaSAgAAATEKAAEwCgoBbf3//3T///8AAAAAAR8AAAAEAAAAANCn6fql1ggGlAIAAALXkQoKBpUCAAAF15Mi16gGlgIAACDXk9eV15LXnteQINep16kg16TXldeW15nXpteZ15XXqgaXAgAAATIGmAIAAAjXkC7XkC7XkgaZAgAABjk4NzY1NAl6AAAACgabAgAAATIGnAIAAAExBp0CAAAb16HXoNeZ16Mg16rXnCDXkNeR15nXkSDXqNeQBp4CAAABNAafAgAACteU16TXmNee158GoAIAABTXqtecINeQ15HXmdeRLdeZ16TXlQEyBqECAAABMQaiAgAADSAgIDAzLTY5NzAzMDYbAAAACXoAAAAJegAAAAl6AAAABqQCAAABMAEJigAAAAamAgAAATEKAAEwCgoBWf3//3T///8AAAAAASAAAAAEAAAAAKSDlJGl1ggGqAIAAALXkAoKBqkCAAAF15Mi16gGqgIAABbXkNeR15Ug15jXmdeoINee15fXnteTBqsCAAABNAasAgAAEteQ15XXqNeq15XXpNeT15nXlAatAgAABTM1NjI4CXoAAAAKBq8CAAACODMGsAIAAAIyNwaxAgAAGNeh16DXmdejINeQ15XXnSDXmNeV15HXkAayAgAAATEGswIAAB7XkNeV150g15jXldeR15Ag15DXnNep15nXpNeQ15AGtAIAAA7Xmdeo15XXqdec15nXnQEyBrUCAAABMQa2AgAADSAgIDAyLTk5MTEwMDcAAAAACXoAAAAJegAAAAl6AAAABrgCAAABMQEJigAAAAa6AgAAATEKAAEwCgoBRf3//3T///8AAAAAASEAAAAEAAAAAO6CRE2l1ggGvAIAAALXkAoKBr0CAAAF15Mi16gGvgIAABbXqtee16gg157Xmdeb15DXnCDXqdeZBr8CAAABNAbAAgAAEteQ15XXqNeq15XXpNeT15nXlAbBAgAABjExODQ0MQl6AAAACgbDAgAAAjE3BsQCAAABMQbFAgAAFteh16DXmdejINeR15DXqCDXqdeR16IGxgIAAAE0BscCAAAc16fXoNeZ15XXnyDXlNeg15LXkSDXp9eV157XlAbIAgAADdeR15DXqCDXqdeR16IBMgbJAgAAATEGygIAAA0gICAwOC02MjAzNjAwAAAAAAl6AAAACXoAAAAJegAAAAbMAgAAATABCYoAAAAGzgIAAAExCgABMAoKATH9//90////AAAAAAEiAAAABAAAAADeX+PyotYIBtACAAAC15QKCgbRAgAABdeS15EnBtICAAAN16nXk9eUINeS15HXmQbTAgAAAjMzBtQCAAAd16nXqNeV16og16TXmdeW15nXldeq16jXpNeZ15QG1QIAAAU5NTEyMwl6AAAACgbXAgAAAjkyBtgCAAABMQbZAgAAG9eh16DXmdejINeULteQ15LXoyDXqNek15XXkAbaAgAAAjIzBtsCAAAO16nXpNeo15nXoNem16cG3AIAABTXqtecINeQ15HXmdeRLdeZ16TXlQEyBt0CAAABMQbeAgAADSAgIDAzLTY5NzAzMTYAAAAACXoAAAAJegAAAAl6AAAABuACAAABMAEJigAAAAbiAgAAATEKAAEwCgoBHf3//3T///8AAAAAASMAAAAEAAAAAJBpuNqi1ggG5AIAAALXlAoKBuUCAAAF15Ei15AG5gIAABjXlyfXnNeQ15nXnNeUINek15DXqteg15QG5wIAAAI5NAboAgAAFdee16jXpNeQINeR16LXmdeh15XXpwbpAgAABjEwMDIzOQl6AAAACgbrAgAAATgG7AIAAAIxNQbtAgAAHNeh16DXmdejINen16jXmdeqINeR15nXkNec15kG7gIAAAIyMgbvAgAADdeT16jXmiDXoteb15UG8AIAABXXp9eo15nXqiDXkdeZ15DXnNeZ16cBMgbxAgAAATEG8gIAAA0gICAwNC05NTA1NTAwAAAAAAl6AAAACXoAAAAJegAAAAb0AgAAATABCYoAAAAG9gIAAAExCgABMAoKAQn9//90////AAAAAAEkAAAABAAAAADSqdo2otYIBvgCAAAC15MKCgl6AAAABvoCAAAT15TXk9eQ15nXlCDXlNeZ15zXlAb7AgAAAjMzBvwCAAAd16nXqNeV16og16TXmdeW15nXldeq16jXpNeZ15QG/QIAAAU5NTA4OQl6AAAACgb/AgAAATMGAAMAAAIyMwYBAwAAG9eh16DXmdejINeZ16jXldep15zXmdedINeoLgYCAwAAATMGAwMAABLXmNeo15XXntek15zXk9eV16gGBAMAAA7Xmdeo15XXqdec15nXnQEyBgUDAAABMQYGAwAADSAgIDAyLTU2NzUxMTEAAAAACXoAAAAJegAAAAl6AAAABggDAAABMAEJigAAAAYKAwAAATEKAAEwCgoB9fz//3T///8AAAAAASUAAAAEAAAAAHLSvCKi1ggGDAMAAALXkwoKBg0DAAAF15Ei15AGDgMAAB7Xp9ek16fXkCDXkNeR16jXlNedINeZ15XXnNeZ15QGDwMAAAI3OQYQAwAACteT15nXkNeY15QGEQMAAAYxMDAyNDQJegAAAAoGEwMAAAIxNwYUAwAAATEGFQMAABbXodeg15nXoyDXkdeQ16gg16nXkdeiBhYDAAABNAYXAwAAHNen16DXmdeV158g15TXoNeS15Eg16fXldee15QGGAMAAA3XkdeQ16gg16nXkdeiATIGGQMAAAExBhoDAAANICAgMDgtNjIwMzYwMAAAAAAJegAAAAl6AAAACXoAAAAGHAMAAAEwAQmKAAAABh4DAAABMQoAATAKCgHh/P//dP///wAAAAABJgAAAAQAAAAA9AaFaKHWCAYgAwAAAteSCgoGIQMAAAXXkteRJwYiAwAAE9eh15HXmdeV158g16DXkNeV15QGIwMAAAI3OQYkAwAACteT15nXkNeY15QGJQMAAAYxODgzNjYGJgMAAOcB157XmNeV16TXnCDXl9eZ15nXkSDXnNeU15LXmdeiINec16TXoNeZINeU16TXkteZ16nXlCDXnNeQ15fXldeqINeR16HXoNeZ16Mg15TXp9eo15XXkSDXnNei15PXm9efINee15PXk9eZINeS15XXkdeUINeV157Xqden15wgINeg15nXqtefINec15bXntefINeq15XXqCDXnNeZ15zXk9eZ150g15XXqteZ16DXlden15XXqiwg16HXldeb16jXqiwg15TXqNeZ15XXnyDXldeU16TXqNei15XXqiDXkNeb15nXnNeUCgYnAwAAAjg3BigDAAACMTIGKQMAABzXodeg15nXoyDXmNec157Xk9eZ16HXmdefINeZCXoAAAAGKwMAAAExBiwDAAAO15nXqNeV16nXnNeZ150BMgYtAwAAATAJegAAAAAAAAAJegAAAAl6AAAACXoAAAAGLwMAAAEwAQmKAAAABjEDAAABMQoBATAKCgHO/P//dP///wAAAAABJwAAAAQAAAAAPNBht6DWCAYzAwAAAteRCgoGNAMAAAXXkyLXqAY1AwAAEdeX157XkNeV15kg15DXodejBjYDAAABNAY3AwAAEteQ15XXqNeq15XXpNeT15nXlAY4AwAABjEwMzg2MQl6AAAACgY6AwAAATMGOwMAAAE3BjwDAAAR16HXoNeZ16Mg15LXmdec15QGPQMAAAE1Bj4DAAAT16bXkdeZ15Qg15XXmdem15fXpwY/AwAADteZ16jXldep15zXmdedATIGQAMAAAExBkEDAAANICAgMDItNjQ2MTIyMgAAAAAJegAAAAl6AAAACXoAAAAGQwMAAAEwAQmKAAAABkUDAAABMQoAATAKCgG6/P//dP///wAAAAABKAAAAAQAAAAA1MU8i6DWCAZHAwAAAteRCgoGSAMAAAXXkyLXqAZJAwAAENeR16jXnteZINeSJ9eV158GSgMAAAIzNAZLAwAACNeh15vXqNeqBkwDAAAFMTY4MTYJegAAAAoGTgMAAAIyMAZPAwAAAjgzBlADAAAT16HXoNeZ16Mg16nXk9eo15XXqgZRAwAAAjIxBlIDAAAP15HXnyDXmdeU15XXk9eUBlMDAAAK16nXk9eo15XXqgEyBlQDAAABMAZVAwAADSAgIDA4LTY2MjQ2NjYAAAAACXoAAAAJegAAAAl6AAAABlcDAAABMAEJigAAAAZZAwAAATEKAAEwCgoBpvz//3T///8AAAAAASkAAAAEAAAAABKbatKf1ggGWwMAAALXkAoKBlwDAAAF15Mi16gGXQMAABvXpNeo15nXk9eo15nXmiDXmdeV15zXoNeT15QGXgMAAAIzOAZfAwAAFdee15nXoNeU15wg16jXpNeV15DXmQZgAwAABTI0NzI3BmEDAACVAtec15HXk9eZ16fXqiDXqNeQ15nXlCDXnNeU16rXkNee16og157Xqden16TXmdeZ150g15nXqSDXnNek16DXldeqINec15fXoNeV16og15DXldek15jXmden15Qg15HXlNeh15PXqCDXotedINec15DXldee15nXqiAgICAg15zXkdeT15nXp9eqINeo15DXmdeUINec15TXqteQ157XqiDXntep16fXpNeZ15nXnSDXnNeZ15zXk9eZ150g157XotecINeS15nXnCA2INeZ16kg15zXpNeg15XXqiDXnNeX16DXldeqINeQ15XXpNeY15nXp9eUICAgINeR15TXodeT16gg16LXnSDXnNeQ15XXnteZ16oKBmIDAAACODUGYwMAAAExBmQDAAAb16HXoNeZ16Mg15TXoNeU15zXqiDXni7XptekBmUDAAABOQZmAwAAEdeT16jXmiDXlNep15zXldedBmcDAAAG16DXqdeoATIGaAMAAAExBmkDAAANICAgMDQtODQ3OTIyMQAAAAAJegAAAAl6AAAACXoAAAAGawMAAAEwAQmKAAAABm0DAAABMQoAATAKCgGS/P//dP///wAAAAABKgAAAAQAAAAAkn/ukpzWCAZvAwAAAteTCgoGcAMAAAXXkteRJwZxAwAAFNeXJ9eQ15jXqCDXnteh15nXqNeUBnIDAAACNzkGcwMAAArXk9eZ15DXmNeUBnQDAAAGMTMzMzI1CXoAAAAKBnYDAAACNDMGdwMAAAI4NwZ4AwAAGteh16DXmdejINeR15nXqiDXkifXkNefINemCXoAAAAGegMAABXXqdeb15XXoNeqINen15HXkNec158GewMAAAzXkdeZ16og15Jg158BMgZ8AwAAATEGfQMAAA0gICAwNC05ODAzNTMxAAAAAAl6AAAACXoAAAAJegAAAAZ/AwAAATABCYoAAAAGgQMAAAExCgABMAoKAX78//90////AAAAAAErAAAABAAAAADm/SHZm9YIBoMDAAAC15IKCgaEAwAABdeTIteoBoUDAAAL157XpSDXoteo158GhgMAAAIzOAaHAwAAFdee15nXoNeU15wg16jXpNeV15DXmQaIAwAABTE3NTEyCXoAAAAKBooDAAACOTIGiwMAAAExBowDAAAb16HXoNeZ16Mg15Qu15DXktejINeo16TXldeQBo0DAAACMjMGjgMAAA7Xqdek16jXmdeg16bXpwaPAwAAFNeq15wg15DXkdeZ15Et15nXpNeVATIGkAMAAAExBpEDAAANICAgMDMtNjk3MDMxNgAAAAAJegAAAAl6AAAACXoAAAAGkwMAAAEwAQmKAAAABpUDAAABMQoAATAKCgFq/P//dP///wAAAAABLAAAAAQAAAAA8rJ40JvWCAaXAwAAAteSCgoGmAMAAAXXkyLXqAaZAwAAENeR16jXnteZINeSJ9eV158GmgMAAAIzNAabAwAACNeh15vXqNeqBpwDAAAFMTY4MTYJegAAAAoGngMAAAIyMAafAwAAAjc2BqADAAAc16HXoNeZ16Mg15HXqNeg16Ig15DXpNeo15nXkwahAwAAAjY1BqIDAAAa16nXkyDXk9eo15XXnSDXkNek16jXmden15QGowMAAAzXkNep16fXnNeV158BMgakAwAAATAGpQMAAA0gICAwOC02NzQ3MTAyAAAAAAl6AAAACXoAAAAJegAAAAanAwAAATABCYoAAAAGqQMAAAExCgABMAoKAVb8//90////AAAAAAEtAAAABAAAAADk9rbFm9YIBqsDAAAC15IKCgasAwAABdeRIteQBq0DAAAX15TXldeo15XXkdeZ16Ug16DXotee15QGrgMAAAI3OQavAwAACteT15nXkNeY15QGsAMAAAYxMDAyMjgJegAAAAoGsgMAAAI0NAazAwAAATIGtAMAABXXodeg15nXoyDXkdeo15vXpNec15MGtQMAAAExBrYDAAAJ16jXqdeRIteZBrcDAAAZ157XldeT15nXoteZ158g16LXmdec15nXqgEyBrgDAAABMQa5AwAADSAgIDA4LTk3NzA2MDAAAAAACXoAAAAJegAAAAl6AAAABrsDAAABMAEJigAAAAa9AwAAATEKAAEwCgoBQvz//3T///8AAAAAAS4AAAAEAAAAACzAkxSb1ggGvwMAAALXkQoKBsADAAAE15LXkQbBAwAAFdeQ15jXoNec15XXkSDXpteZ15zXlAbCAwAAAjMzBsMDAAAd16nXqNeV16og16TXmdeW15nXldeq16jXpNeZ15QGxAMAAAU5NTExMQl6AAAACgbGAwAAAjIwBscDAAACMTEGyAMAABzXodeg15nXoyDXkNep15PXldeTINeh15nXmNeZBskDAAABNwbKAwAAEdee16DXl9edINeR15LXmdefBssDAAAK15DXqdeT15XXkwEyBswDAAABMQbNAwAADSAgIDA4LTg1Nzk4MDAAAAAACXoAAAAJegAAAAl6AAAABs8DAAABMQEJigAAAAbRAwAAATEKAAEwCgoBLvz//3T///8AAAAAAS8AAAAEAAAAABJPexKb1ggG0wMAAALXkQoKBtQDAAAF15LXkScG1QMAABPXnteV15zXmdeQ158g15nXotecBtYDAAACNzkG1wMAAArXk9eZ15DXmNeUBtgDAAAFOTU2OTUG2QMAAJUB157Xp9eR15wg15fXldec15kg16HXm9eo16ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgINeU15PXmdeQ15jXny/XmdeqINee15jXpNecL9eqINeR15TXqdee16DXqiDXmdeq16gg15DXptecINeZ15zXk9eZ150KBtoDAAABMgbbAwAAATEG3AMAABvXodeg15nXoyDXqtecINeQ15HXmdeRINeo15AG3QMAAAE0Bt4DAAAK15TXpNeY157XnwbfAwAAFNeq15wg15DXkdeZ15Et15nXpNeVATIG4AMAAAExBuEDAAANICAgMDMtNjk3MDMwNgAAAAAJegAAAAl6AAAACXoAAAAG4wMAAAEwAQmKAAAABuUDAAABMQoAATAKCgEa/P//dP///wAAAAABMAAAAAQAAAAAkBkBCJvWCAbnAwAAAteRCgoG6AMAAAXXkyLXqAbpAwAAE9ek15DXldeW16DXqCDXk9eV15MG6gMAAAE1BusDAAAI16DXqdeZ150G7AMAAAUxNTI2OQbtAwAAggHXlNeo15XXpNeQINee15jXpNecINeR16HXm9eo16og15TXqNeZ15XXoNeZ16ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg15TXqNeV16TXkCDXnteR16bXoiDXntei16fXkSDXlNeo15nXldefCgbuAwAAATYG7wMAAAExBvADAAAb16HXoNeZ16Mg16DXqteg15nXlCDXnNeRINeUBvEDAAACMTcG8gMAAAzXqdeY157XpNek16gG8wMAAArXoNeq16DXmdeUATIG9AMAAAExBvUDAAANICAgMDktODYwNzIyMgAAAAAJegAAAAl6AAAACXoAAAAG9wMAAAEwAQmKAAAABvkDAAABMQoAATAKCgEG/P//dP///wAAAAABMQAAAAQAAAAATr0JCpjWCAb7AwAAAteUCgoG/AMAAAXXkyLXqAb9AwAAD9eh15cg157XldeX157Xkwb+AwAAATcG/wMAAA/XoteV16gg15XXnteZ158GAAQAAAYxMDYxMjIJegAAAAoGAgQAAAIxMQYDBAAAAjc2BgQEAAAc16HXoNeZ16Mg15HXldei15nXoNeUINeg15XXkgl6AAAABgYEAAAe15HXldei15nXmdeg15Qg16DXldeSJ9eZ15PXkNeqBgcEAAAc15HXldei15nXmdeg15Qt16DXldeSYNeZ15PXkAEyBggEAAABMQYJBAAADSAgIDA0LTY3MzA4MTAAAAAACXoAAAAJegAAAAl6AAAABgsEAAABMAEJigAAAAYNBAAAATEKAAEwCgoB8vv//3T///8AAAAAATIAAAAEAAAAAHSbUVaW1ggGDwQAAALXkgoKCXoAAAAGEQQAABfXnteV16nXmdeZ15Eg15DXmdeo16DXlAYSBAAAAjk2BhMEAAAb16fXnNeZ16DXkNeZ16og16rXp9ep15XXqNeqBhQEAAAGMTAwMjQxCXoAAAAKBhYEAAABOQYXBAAAAjYxBhgEAAAa16HXoNeZ16Mg15TXldeTINeU16nXqNeV158GGQQAAAIxNAYaBAAACteU15HXoNeZ150GGwQAABHXlNeV15Mg15TXqdeo15XXnwEyBhwEAAABMQYdBAAADSAgIDA5LTc2MjE1MDAAAAAACXoAAAAJegAAAAl6AAAABh8EAAABMAEJigAAAAYhBAAAATEKAAEwCgoB3vv//3T///8AAAAAATMAAAAEAAAAAPbOZ9eU1ggGIwQAAALXkAoKBiQEAAAF15Mi16gGJQQAABPXqdeb15jXqCDXqNeV15bXoNeUBiYEAAACMTUGJwQAAArXkteh15jXqNeVBigEAAAGMTQ2MDc1CXoAAAAKBioEAAABMwYrBAAAATEGLAQAABzXodeg15nXoyDXnteh15nXnNeqINeZ16nXqNeZBi0EAAACMTQGLgQAABXXnteh15nXnNeqINeZ16nXqNeZ150GLwQAAA7Xmdeo15XXqdec15nXnQEyBjAEAAABMAYxBAAADSAgIDAyLTU2Nzc2NzcbAAAACXoAAAAJegAAAAl6AAAABjMEAAABMAEJigAAAAY1BAAAATEKAAEwCgoByvv//3T///8AAAAAATQAAAAEAAAAAK5h97GU1ggGNwQAAALXkAoKBjgEAAAF15Mi16gGOQQAACDXpNeo15nXk9ee158g15HXqNeU15XXnSDXmNec15nXlAY6BAAAAzEwNQY7BAAAEdee15nXldefINeg16nXmdedBjwEAAAFODM0OTYJegAAAAoGPgQAAAEyBj8EAAACNzAGQAQAABbXodeg15nXoyDXkdeg15kg15HXqNenBkEEAAABMQZCBAAAE9eZ15TXldeT15Qg15TXnNeV15kGQwQAAA3Xkdeg15kg15HXqNenATIGRAQAAAExBkUEAAANICAgMDMtNTc3ODcwMAAAAAAJegAAAAl6AAAACXoAAAAGRwQAAAEwAQmKAAAABkkEAAABMQoAATAKCgG2+///dP///wAAAAABNQAAAAQAAAAAxKkWsJHWCAZLBAAAAteTCgoGTAQAAAXXkSLXkAZNBAAAF9eU15XXqNeV15HXmdelINeg16LXnteUBk4EAAACNzkGTwQAAArXk9eZ15DXmNeUBlAEAAAGMTAwMjI4CXoAAAAKBlIEAAACNDQGUwQAAAExBlQEAAAY16HXoNeZ16Mg16fXqNeZ16og16HXpNeoBlUEAAABNQZWBAAAF9eg16rXmdeR15XXqiDXlNee16nXpNeYBlcEAAAZ157XldeT15nXoteZ158g16LXmdec15nXqgEyBlgEAAABMQZZBAAADSAgIDA4LTkxNDI1MDAAAAAACXoAAAAJegAAAAl6AAAABlsEAAABMAEJigAAAAZdBAAAATEKAAEwCgoBovv//3T///8AAAAAATYAAAAEAAAAAKxWxIuR1ggGXwQAAALXkwoKBmAEAAAF15Ei15AGYQQAACDXoNeq158g15LXldec15PXqdeY15nXmdefINeZ16LXnAZiBAAAAjc5BmMEAAAK15PXmdeQ15jXlAZkBAAABjEwMDIzNAZlBAAAPdeU15PXmdeQ15jXny/XoNeZ16og157XmNek15wv16og15HXnten16jXmSDXoNek16jXldec15XXkteZ15QKBmYEAAACNDQGZwQAAAIzMQZoBAAAHNeh16DXmdejINep15vXldeg16og15TXktek158GaQQAAAIyMAZqBAAAENek15nXoNeqINeU16gi158GawQAABTXkdeZ16oi16gg16LXmdec15nXqgEyBmwEAAABMQZtBAAADSAgIDAyLTU3MjM1NTUAAAAACXoAAAAJegAAAAl6AAAABm8EAAABMAEJigAAAAZxBAAAATEKAAEwCgoBjvv//3T///8AAAAAATcAAAAEAAAAAMgqtMqQ1ggGcwQAAALXkgoKBnQEAAAF15Mi16gGdQQAAA/XnteW15XXqCDXkdec15QGdgQAAAE1BncEAAAI16DXqdeZ150GeAQAAAUzMDA4Ngl6AAAACgZ6BAAAATYGewQAAAIxMQZ8BAAAG9eh16DXmdejINec15nXnNeZ16DXkdec15XXnQZ9BAAAAjEzBn4EAAAS15zXmdec15nXoNeR15zXldedBn8EAAAK16DXqteg15nXlAEyBoAEAAABMQaBBAAADSAgIDA5LTg4OTY2MDAAAAAACXoAAAAJegAAAAl6AAAABoMEAAABMAEJigAAAAaFBAAAATEKAAEwCgoBevv//3T///8AAAAAATgAAAAEAAAAAPiFGxiN1ggGhwQAAALXlAoKBogEAAAF15LXkScGiQQAAA3XoNeV15kg15TXk9ehBooEAAACOTQGiwQAABXXnteo16TXkCDXkdei15nXodeV16cGjAQAAAU5OTg3Mwl6AAAACgaOBAAAAjE3Bo8EAAACNjAGkAQAABHXodeg15nXoyDXkNeZ15zXqgaRBAAAAjEyBpIEAAAR15LXny3Xkdeg15nXnteZ158GkwQAAAjXkNeZ15zXqgEyBpQEAAABMQaVBAAADSAgIDA4LTYzMDYyMDAAAAAACXoAAAAJegAAAAl6AAAABpcEAAABMAEJigAAAAaZBAAAATEKAAEwCgoBZvv//3T///8AAAAAATkAAAAEAAAAAFp7/wmN1ggGmwQAAALXlAoKBpwEAAAF15LXkScGnQQAABnXkteo16nXmNeZ15nXnyDXkNeZ16jXoNeUBp4EAAACNzEGnwQAABPXqNeV16fXlyDXp9ec15nXoNeZBqAEAAAGOTk5OTE1CXoAAAAKBqIEAAACNjcGowQAAAEyBqQEAAAc16HXoNeZ16Mg157Xlden15Mg15TXqteX15nXmQalBAAAATIGpgQAABPXkNeR158g15LXkdeZ16jXldecBqcEAAAU16rXnCDXkNeR15nXkS3Xmdek15UBMgaoBAAAATEGqQQAAA0gIDA3Ni04ODg5NjAwAAAAAAl6AAAACXoAAAAJegAAAAarBAAAATABCYoAAAAGrQQAAAExCgABMAoKAVL7//90////AAAAAAE6AAAABAAAAACmxis4jNYIBq8EAAAC15MKCgawBAAABdeTIteoBrEEAAAQ15HXqNee15kg15In15XXnwayBAAAAjM0BrMEAAAI16HXm9eo16oGtAQAAAUxNjgxNgl6AAAACga2BAAAAjE3BrcEAAACNDAGuAQAABXXodeg15nXoyDXk9eZ157Xldeg15QGuQQAAAI0NQa6BAAAD9ep15Mg15TXoNep15nXkAa7BAAADNeT15nXnteV16DXlAEyBrwEAAABMAa9BAAADSAgIDA4LTY1MDM2MDAAAAAACXoAAAAJegAAAAl6AAAABr8EAAABMAEJigAAAAbBBAAAATEKAAEwCgoBPvv//3T///8AAAAAATsAAAAEAAAAAIxUYXGL1ggGwwQAAALXkgoKBsQEAAAF15Ei15AGxQQAABPXqdeV15XXmdeTINei15XXlteZBsYEAAACMzMGxwQAAB3Xqdeo15XXqiDXpNeZ15bXmdeV16rXqNek15nXlAbIBAAABjEwMDIzNQl6AAAACgbKBAAAAjEwBssEAAACNTAGzAQAABXXodeg15nXoyDXqNeX15XXkdeV16oGzQQAAAEyBs4EAAAJ15HXmdecIteVBs8EAAAM16jXl9eV15HXldeqATIG0AQAAAExBtEEAAANICAgMDgtOTM3NzgwMAAAAAAJegAAAAl6AAAACXoAAAAG0wQAAAEwAQmKAAAABtUEAAABMQoAATAKCgEq+///dP///wAAAAABPAAAAAQAAAAAiNFfzYrWCAbXBAAAAteRCgoG2AQAAAXXkyLXqAbZBAAAFdeT15XXqNek157XnyDXodeZ157XlAbaBAAAATUG2wQAAAjXoNep15nXnQbcBAAABTMyOTk4Bt0EAACgAdeU16jXldek15Ag157Xkdem16Ig157Xoten15Eg15TXqNeZ15XXnyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDXlNeo15XXpNeQINee15HXpteiINeQ15XXnNeY16jXodeQ15XXoNeTINeS16DXmden15XXnNeV15LXmS/XldeS15nXoNec15kKBt4EAAACMTEG3wQAAAExBuAEAAAT16HXoNeZ16Mg15jXkdeo15nXlAl6AAAABuIEAAAN15HXnyDXlteb15DXmQbjBAAACteY15HXqNeZ15QBMgbkBAAAATEG5QQAAA0gICAwNC02NzI3MDAwAAAAAAl6AAAACXoAAAAJegAAAAbnBAAAATABCYoAAAAG6QQAAAExCgABMAoKARb7//90////AAAAAAE9AAAABAAAAAAWiQBjh9YIBusEAAAC15QKCgbsBAAABdeTIteoBu0EAAAV15XXldeU15HXmSDXldeV15TXkdeZBu4EAAABNgbvBAAADNei15nXoNeZ15nXnQbwBAAABTM4MzE0CXoAAAAKBvIEAAABNgbzBAAAATEG9AQAABvXodeg15nXoyDXoNeq16DXmdeUINec15Eg15QG9QQAAAIxNwb2BAAADNep15jXntek16TXqAb3BAAACteg16rXoNeZ15QBMgb4BAAAATEG+QQAAA0gICAwOS04NjA3MjIyAAAAAAl6AAAACXoAAAAJegAAAAb7BAAAATABCYoAAAAG/QQAAAExCgABMAoKAQL7//90////AAAAAAE+AAAABAAAAAB4IBSihtYIBv8EAAAC15MKCgYABQAABdeTIteoBgEFAAAe15HXm9eoINeQ15HXoNeZ15DXnNeZINei15nXqNeQBgIFAAABNAYDBQAAEteQ15XXqNeq15XXpNeT15nXlAYEBQAABjEyMjMzMgl6AAAACgYGBQAAAjE3BgcFAAACMjAGCAUAABXXodeg15nXoyDXkNeV16TXp9eZ150GCQUAAAI2MgYKBQAADdep15Mg15TXqNem15wGCwUAAAzXkNeV16TXp9eZ150BMgYMBQAAATEGDQUAAA0gICAwOC05MTE5ODg4AAAAAAl6AAAACXoAAAAJegAAAAYPBQAAATABCYoAAAAGEQUAAAExCgABMAoKAe76//90////AAAAAAE/AAAABAAAAAAc+fsBhtYIBhMFAAAC15IKCgYUBQAABdeRIteQBhUFAAAR16nXqCDXkdeo15XXqNeZ15QGFgUAAAI3OQYXBQAACteT15nXkNeY15QGGAUAAAYxMDAyMTIJegAAAAoGGgUAAAEyBhsFAAACMTEGHAUAABvXodeg15nXoyDXqNee16og15DXkdeZ15Eg15IGHQUAAAIyNgYeBQAAFdeQ15fXmdee15DXmdeoINeQ15HXkAYfBQAAFNeq15wg15DXkdeZ15Et15nXpNeVATIGIAUAAAExBiEFAAANICAgMDMtNzQ1MDQwMAAAAAAJegAAAAl6AAAACXoAAAAGIwUAAAEwAQmKAAAABiUFAAABMQoAATAKCgHa+v//dP///wAAAAABQAAAAAQAAAAAGI1VKIXWCAYnBQAAAteRCgoGKAUAAAXXkSLXkAYpBQAAINeg16rXnyDXkteV15zXk9ep15jXmdeZ158g15nXotecBioFAAACNzkGKwUAAArXk9eZ15DXmNeUBiwFAAAGMTAwMjM0CXoAAAAKBi4FAAACODEGLwUAAAIxMAYwBQAAHNeh16DXmdejINei15bXqNeUINeV157XqNek15AGMQUAAAExBjIFAAAO15nXoten15HXlteV158GMwUAAA7Xmdeo15XXqdec15nXnQEyBjQFAAABMQY1BQAADSAgIDAyLTUwMTQ1NTUAAAAACXoAAAAJegAAAAl6AAAABjcFAAABMAEJigAAAAY5BQAAATEKAAEwCgoBxvr//3T///8AAAAAAUEAAAAEAAAAALhXZ2GE1ggGOwUAAALXkAoKBjwFAAAF15Ei15AGPQUAABzXodeY15XXmdemJ9eRINeV15zXk9eZ157XmdeoBj4FAAACMzMGPwUAAB3Xqdeo15XXqiDXpNeZ15bXmdeV16rXqNek15nXlAZABQAABjEwMDE2Ngl6AAAACgZCBQAAAjEwBkMFAAACODgGRAUAABrXodeg15nXoyDXmC7XkdeZ16og15HXqiDXmQZFBQAAATMGRgUAABvXnteV15vXqNeZINeU16HXmdeS16jXmdeV16oGRwUAAAnXkdeqINeZ150BMgZIBQAAATEGSQUAAA0gICAwMy01NTU2OTAwAAAAAAl6AAAACXoAAAAJegAAAAZLBQAAATABCYoAAAAGTQUAAAExCgABMAoKAbL6//90////AAAAAAFCAAAABAAAAAAetSkagtYIBk8FAAAC15QKCgZQBQAABdeTIteoBlEFAAAT16TXldeV15DXliDXnNeQ16TXmQZSBQAAATUGUwUAAAjXoNep15nXnQZUBQAABjEwMDgwNgl6AAAACgZWBQAAAjQ0BlcFAAACMjIGWAUAABvXodeg15nXoyDXnNeRINeU16jXnteUINeR15kGWQUAAAExBloFAAAR16DXlNeoINeU15nXqNeT158GWwUAAA3XkdeZ16og16nXntepATIGXAUAAAExBl0FAAANICAgMDItNjMyNTUwMAAAAAAJegAAAAl6AAAACXoAAAAGXwUAAAEwAQmKAAAABmEFAAABMQoAATAKCgGe+v//dP///wAAAAABQwAAAAQAAAAAtqoE7oHWCAZjBQAAAteUCgoGZAUAAAXXkteRJwZlBQAAGdeo15XXlteg15HXqNeSINem15XXpNeZ15QGZgUAAAI5NwZnBQAAFNek16HXmdeb15XXqteo16TXmdeUBmgFAAAGNjE5OTA3CXoAAAAKBmoFAAACNDQGawUAAAEyBmwFAAAV16HXoNeZ16Mg15HXqNeb16TXnNeTBm0FAAABMQZuBQAACdeo16nXkSLXmQZvBQAAGdee15XXk9eZ16LXmdefINei15nXnNeZ16oBMgZwBQAAATEGcQUAAA0gICAwOC05NzcwNjAwAAAAAAl6AAAACXoAAAAJegAAAAZzBQAAATABCYoAAAAGdQUAAAExCgABMAoKAYr6//90////AAAAAAFEAAAABAAAAACiqplPgdYIBncFAAAC15MKCgZ4BQAABNee16gGeQUAABXXkNeS15HXqNeZ15Qg15DXl9ee15MGegUAAAIzMwZ7BQAAHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUBnwFAAAFOTU0NjAJegAAAAoGfgUAAAE2Bn8FAAABMQaABQAAG9eh16DXmdejINeg16rXoNeZ15Qg15zXkSDXlAaBBQAAAjE3BoIFAAAM16nXmNee16TXpNeoBoMFAAAK16DXqteg15nXlAEyBoQFAAABMQaFBQAADSAgIDA5LTg2MDcyMjIAAAAACXoAAAAJegAAAAl6AAAABocFAAABMAEJigAAAAaJBQAAATEKAAEwCgoBdvr//3T///8AAAAAAUUAAAAEAAAAAO4N0wyB1ggGiwUAAALXkwoKBowFAAAF15Mi16gGjQUAAB/XoteQ157XqCDXkNec16nXmdenINeSJ9eV16DXmdeUBo4FAAABNQaPBQAACNeg16nXmdedBpAFAAAFOTU0ODkJegAAAAoGkgUAAAE5BpMFAAACODYGlAUAABrXodeg15nXoyDXkNec16LXkyDXptek15XXnwaVBQAAAjEwBpYFAAAW16nXntei15XXnyDXkdefINep15jXlwaXBQAACNeQ15zXoteTATIGmAUAAAExBpkFAAANICAgMDMtOTA4MzgwMAAAAAAJegAAAAl6AAAACXoAAAAGmwUAAAEwAQmKAAAABp0FAAABMQoAATAKCgFi+v//dP///wAAAAABRgAAAAQAAAAAPANMYIDWCAafBQAAAteSCgoGoAUAAAXXkyLXqAahBQAAEdeS15zXldeW157XnyDXnNeRBqIFAAACMjIGowUAABvXqNek15XXkNeUINeq16LXodeV16fXqteZ16oGpAUAAAU4MzI1Nwl6AAAACgamBQAAAjIwBqcFAAABNAaoBQAAHNeh16DXmdejINee16jXm9eWINeQ15zXnteV15IGqQUAAAI3MQaqBQAACNeb16DXqNeqBqsFAAAK15DXqdeT15XXkwEyBqwFAAABMQatBQAADSAgIDA4LTg2ODc2MDAAAAAACXoAAAAJegAAAAl6AAAABq8FAAABMAEJigAAAAaxBQAAATEKAAEwCgoBTvr//3T///8AAAAAAUcAAAAEAAAAAAqFpF2A1ggGswUAAALXkgoKBrQFAAAF15Mi16gGtQUAABfXpNeV15zXmdeQ16fXldeRINeg15zXmQa2BQAAAjEyBrcFAAAK16jXmdeQ15XXqga4BQAABTI3NjgzCXoAAAAKBroFAAACMjAGuwUAAAE0BrwFAAAc16HXoNeZ16Mg157XqNeb15Yg15DXnNee15XXkga9BQAAAjcxBr4FAAAI15vXoNeo16oGvwUAAArXkNep15PXldeTATIGwAUAAAEwBsEFAAANICAgMDgtODY4NzYwMAAAAAAJegAAAAl6AAAACXoAAAAGwwUAAAEwAQmKAAAABsUFAAABMQoAATAKCgE6+v//dP///wAAAAABSAAAAAQAAAAApohVWIDWCAbHBQAAAteSCgoGyAUAAAXXkyLXqAbJBQAAEdeR16jXmdenINeQ16jXmdeUBsoFAAACMTIGywUAAArXqNeZ15DXldeqBswFAAAFMTQ0NTEJegAAAAoGzgUAAAE0Bs8FAAABMQbQBQAAGteh16DXmdejINeX15nXpNeUINeo15DXqdeZBtEFAAABMgbSBQAACNeX15XXqNeZBtMFAAAI15fXmdek15QBMgbUBQAAATAG1QUAAA0gICAwNC04Njg2ODAwAAAAAAl6AAAACXoAAAAJegAAAAbXBQAAATABCYoAAAAG2QUAAAExCgABMAoKASb6//90////AAAAAAFJAAAABAAAAABmTTqGf9YIBtsFAAAC15EKCgbcBQAABdeS15EnBt0FAAAR15TXqNeY15wg16DXmdem158G3gUAAAI5NAbfBQAAFdee16jXpNeQINeR16LXmdeh15XXpwbgBQAABjE0ODg3NQl6AAAACgbiBQAAATQG4wUAAAI4MQbkBQAAGteh16DXmdejINee15LXk9ecINeU16LXntenBuUFAAACNzcG5gUAABbXqdeTINep15DXldecINei157XldeoBucFAAAR157XkteT15wg15TXotee16cBMgboBQAAATEG6QUAAA0gICAwNC02MDQ5NjAwAAAAAAl6AAAACXoAAAAJegAAAAbrBQAAATABCYoAAAAG7QUAAAExCgABMAoKARL6//90////AAAAAAFKAAAABAAAAAAC8ASAfNYIBu8FAAAC15QKCgbwBQAABdeeIteQBvEFAAAV15DXkdeo157XodeV158g15nXotecBvIFAAACOTcG8wUAABTXpNeh15nXm9eV16rXqNek15nXlAb0BQAABjEwMDIzMQl6AAAACgb2BQAAAjQ0BvcFAAACNDAG+AUAABfXodeg15nXoyDXnteV15PXmdei15nXnwb5BQAAAjI5BvoFAAAW15LXmdeg16og15HXmdeqINep15DXnwb7BQAAHNee15XXk9eZ16LXmdefLdee15vXkdeZ150t16gBMgb8BQAAATEG/QUAAA0gICAwOC05NzM1MjIyAAAAAAl6AAAACXoAAAAJegAAAAb/BQAAATABCYoAAAAGAQYAAAExCgABMAoKAf75//90////AAAAAAFLAAAABAAAAAA+G+ahe9YIBgMGAAAC15MKCgYEBgAABNee16gGBQYAABHXodei15nXkyDXqNek15nXpwYGBgAAAjkzBgcGAAAb16LXkdeV15PXlCDXodeV16bXmdeQ15zXmdeqBggGAAAGMjkwNzQwCXoAAAAKBgoGAAACODMGCwYAAAIyOQYMBgAAHNeh16DXmdejINeh157XkC3XkS7Xl9eg15nvv70GDQYAAAExBg4GAAAS16DXldeoINeQ15wg15PXmdefBg8GAAAO15nXqNeV16nXnNeZ150BMgYQBgAAATEGEQYAAA0gICAwMi02NDgzMTIzAAAAAAl6AAAACXoAAAAJegAAAAYTBgAAATABCYoAAAAGFQYAAAExCgABMAoKAer5//90////AAAAAAFMAAAABAAAAADe5ffaetYIBhcGAAAC15IKCgYYBgAABdeTIteoBhkGAAAV16jXkNeR15kg15vXmdek15DXmdeUBhoGAAABNQYbBgAACNeg16nXmdedBhwGAAAFMzUwNjUJegAAAAoGHgYAAAE5Bh8GAAACNjQGIAYAABPXodeg15nXoyDXmNeZ15nXkdeUBiEGAAACMjQGIgYAABXXnteX157XldeTINeT16jXldeZ16kGIwYAABfXmNeZ15nXkdeUINeR157XqdeV15zXqQEyBiQGAAABMQYlBgAADSAgIDA5LTc5OTIxNDQAAAAACXoAAAAJegAAAAl6AAAABicGAAABMAEJigAAAAYpBgAAATEKAAEwCgoB1vn//3T///8AAAAAAU0AAAAEAAAAALLvWAZ61ggGKwYAAALXkQoKBiwGAAAF154i15AGLQYAAA3Xoden15wg15fXoNeZBi4GAAACNzkGLwYAAArXk9eZ15DXmNeUBjAGAAAFOTUwNjIJegAAAAoGMgYAAAI4MQYzBgAAATIGNAYAABTXodeg15nXoyDXlNeoINeg15XXowY1BgAAAjU4BjYGAAAV15TXqNeRINep15DXldec15bXldefBjcGAAAO15nXqNeV16nXnNeZ150BMgY4BgAAATEGOQYAAA0gICAwMi02NTExNjAyAAAAAAl6AAAACXoAAAAJegAAAAY7BgAAATABCYoAAAAGPQYAAAExCgABMAoKAcL5//90////AAAAAAFOAAAABAAAAAC+QWVzddYIBj8GAAAC15IKCgZABgAABdeTIteoBkEGAAAV157XqNeW15XXnyDXmdeR15LXoNeZBkIGAAACMzgGQwYAABXXnteZ16DXlNecINeo16TXldeQ15kGRAYAAAUyOTA5OAl6AAAACgZGBgAAAjkyBkcGAAABMQZIBgAAG9eh16DXmdejINeULteQ15LXoyDXqNek15XXkAZJBgAAAjIzBkoGAAAO16nXpNeo15nXoNem16cGSwYAABTXqtecINeQ15HXmdeRLdeZ16TXlQEyBkwGAAABMQZNBgAADSAgIDAzLTY5NzAzMTYAAAAACXoAAAAJegAAAAl6AAAABk8GAAABMAEJigAAAAZRBgAAATEKAAEwCgoBrvn//3T///8AAAAAAU8AAAAEAAAAAFg9+ZV01ggGUwYAAALXkQoKBlQGAAAF154i15AGVQYAABbXqNeQ15XXkdeg15kg15In15XXnNeZBlYGAAACOTcGVwYAABTXpNeh15nXm9eV16rXqNek15nXlAZYBgAABjEwMDIyNwl6AAAACgZaBgAAATMGWwYAAAIyMwZcBgAAG9eh16DXmdejINeZ16jXldep15zXmdedINeoLgZdBgAAATMGXgYAABLXmNeo15XXntek15zXk9eV16gGXwYAAA7Xmdeo15XXqdec15nXnQEyBmAGAAABMQZhBgAADSAgIDAyLTU2NzUxMTEAAAAACXoAAAAJegAAAAl6AAAABmMGAAABMAEJigAAAAZlBgAAATEKAAEwCgoBmvn//3T///8AAAAAAVAAAAAEAAAAAHqXjXlx1ggGZwYAAALXlAoKBmgGAAAF15Ei15AGaQYAABnXkNec15jXqNeV15HXmdelINeU15nXnNeUBmoGAAACNzkGawYAAArXk9eZ15DXmNeUBmwGAAAGMTAwMjE3CXoAAAAKBm4GAAABOAZvBgAAAjE1BnAGAAAc16HXoNeZ16Mg16fXqNeZ16og15HXmdeQ15zXmQZxBgAAAjIyBnIGAAAN15PXqNeaINei15vXlQZzBgAAFden16jXmdeqINeR15nXkNec15nXpwEyBnQGAAABMQZ1BgAADSAgIDA0LTk1MDU1MDAAAAAACXoAAAAJegAAAAl6AAAABncGAAABMAEJigAAAAZ5BgAAATEKAAEwCgoBhvn//3T///8AAAAAAVEAAAAEAAAAAJAhqIhr1ggGewYAAALXkwoKBnwGAAAF15Mi16gGfQYAAAvXkdeb16gg15PXkQZ+BgAAAjI3Bn8GAAAM16TXldeo15nXldeqBoAGAAAFODA0NDAJegAAAAoGggYAAAEyBoMGAAABMQaEBgAAG9eh16DXmdejINeq15wg15DXkdeZ15Eg16jXkAaFBgAAATQGhgYAAArXlNek15jXntefBocGAAAU16rXnCDXkNeR15nXkS3Xmdek15UBMgaIBgAAATEGiQYAAA0gICAwMy02OTcwMzA2AAAAAAl6AAAACXoAAAAJegAAAAaLBgAAATABCYoAAAAGjQYAAAExCgABMAoKAXL5//90////AAAAAAFSAAAABAAAAADQLKk8a9YIBo8GAAAC15MKCgaQBgAABdeRIteQBpEGAAAV15DXnNem16DXoiDXkNeZ157XkNefBpIGAAACNzkGkwYAAArXk9eZ15DXmNeUBpQGAAAGMTAwMjE1CXoAAAAKBpYGAAACMTcGlwYAAAI4MAaYBgAAD9eh16DXmdejINeo15TXmAl6AAAABpoGAAAT157XqNeb15Yg157XodeX16jXmQabBgAABteo15TXmAEyBpwGAAABMQadBgAADSAgIDA4LTY4MDc4MDAAAAAACXoAAAAJegAAAAl6AAAABp8GAAABMAEJigAAAAahBgAAATEKAAEwCgoBXvn//3T///8AAAAAAVMAAAAEAAAAAGyNeCVr1ggGowYAAALXkwoKBqQGAAAF15LXkScGpQYAABvXktec15XXqdeY15nXmdefINeT15XXqNeZ158GpgYAAAI3OQanBgAACteT15nXkNeY15QGqAYAAAU5NTE4MwapBgAAngHXnten15HXnCDXl9eV15zXmSDXodeb16jXqiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg157Xp9eR15wg15zXkNeX16gg16DXmdeq15XXlyDXkdeo15nXkNeY16jXmdeUL9en15nXpteV16gg16fXmdeR15Qv16nXqNeV15XXnAoGqgYAAAI4MgarBgAAAjIwBqwGAAAR16HXoNeZ16Mg16nXmdec15QJegAAAAauBgAAE9eTLtegLiDXkNek16jXmdeZ150GrwYAAAjXqdeZ15zXlAEyBrAGAAABMQaxBgAADSAgIDAyLTk5NDEyOTMAAAAACXoAAAAJegAAAAl6AAAABrMGAAABMAEJigAAAAa1BgAAATEKAAEwCgoBSvn//3T///8AAAAAAVQAAAAEAAAAAIi+hlJq1ggGtwYAAALXkgoKBrgGAAAF15LXkScGuQYAABfXkdeV16jXldeR15nXpyDXkteZ15zXmQa6BgAAAjc5BrsGAAAK15PXmdeQ15jXlAa8BgAABTk5ODgwCXoAAAAKBr4GAAABNga/BgAAAjY1BsAGAAAR16HXoNeZ16Mg15fXk9eo15QGwQYAAAI2NgbCBgAAE9ep15Mg16jXldeY16nXmdec15MGwwYAAAjXl9eT16jXlAEyBsQGAAABMQbFBgAADSAgIDA0LTYxMjY2MDAAAAAACXoAAAAJegAAAAl6AAAABscGAAABMAEJigAAAAbJBgAAATEKAAEwCgoBNvn//3T///8AAAAAAVUAAAAEAAAAAOTfclZm1ggGywYAAALXlAoKBswGAAAF15Mi16gGzQYAACXXlNec16jXntefINeY15XXodenINec15nXkNeqICjXnNeQ15QpBs4GAAACNTgGzwYAAB3XoNeV15nXqNeV15zXldeS15nXlCDXmdec15PXmQbQBgAABTMyODg0CXoAAAAKBtIGAAACMTAG0wYAAAIyMAbUBgAAHNeh16DXmdejINeo15DXqdeV158g15zXpteZ15UG1QYAAAE3BtYGAAAO16jXldeY16nXmdec15MG1wYAABXXqNeQ16nXldefINec16bXmdeV158BMgbYBgAAATEG2QYAAA0gICAwMy05NTk3ODAwGwAAAAl6AAAACXoAAAAJegAAAAbbBgAAATABCYoAAAAG3QYAAAExCgABMAoKASL5//90////AAAAAAFWAAAABAAAAABodmAUZNYIBt8GAAAC15EKCgbgBgAABdeTIteoBuEGAAAR16nXkteZ15Ag16LXnteZ16oG4gYAAAE0BuMGAAAS15DXldeo16rXldek15PXmdeUBuQGAAAGMTE3MDcyCXoAAAAKBuYGAAACMTcG5wYAAAIyMAboBgAAFdeh16DXmdejINeQ15XXpNen15nXnQbpBgAAAjYyBuoGAAAN16nXkyDXlNeo16bXnAbrBgAADNeQ15XXpNen15nXnQEyBuwGAAABMQbtBgAADSAgIDA4LTkxMTk4ODgAAAAACXoAAAAJegAAAAl6AAAABu8GAAABMAEJigAAAAbxBgAAATEKAAEwCgoBDvn//3T///8AAAAAAVcAAAAEAAAAADIwpg5k1ggG8wYAAALXkQoKBvQGAAAF15LXkScG9QYAABTXkdeoINeW15nXlSDXoNeV16LXlAb2BgAAAjc5BvcGAAAK15PXmdeQ15jXlAb4BgAABTk1ODA5BvkGAACeAdee16fXkdecINeX15XXnNeZINeh15vXqNeqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDXnten15HXnCDXnNeQ15fXqCDXoNeZ16rXldeXINeR16jXmdeQ15jXqNeZ15Qv16fXmdem15XXqCDXp9eZ15HXlC/Xqdeo15XXldecCgb6BgAAATQG+wYAAAI4Mgb8BgAAFdeh16DXmdejINeZ15XXp9eg16LXnQb9BgAAATEG/gYAABXXqdeT16jXldeqINeU15nXldeR15wG/wYAABXXmden16DXotedINei15nXnNeZ16oBMgYABwAAATEGAQcAAA0gICAwNC02NzczNzAwAAAAAAl6AAAACXoAAAAJegAAAAYDBwAAATABCYoAAAAGBQcAAAExCgABMAoKAfr4//90////AAAAAAFYAAAABAAAAACEp25BY9YIBgcHAAAC15AKCgYIBwAABdeRIteQBgkHAAAV15DXnNeg16fXqNeZINeZ16TXoteqBgoHAAACNzkGCwcAAArXk9eZ15DXmNeUBgwHAAAGMTAwMDI2CXoAAAAKBg4HAAABMwYPBwAAAjIzBhAHAAAb16HXoNeZ16Mg15nXqNeV16nXnNeZ150g16guBhEHAAABMwYSBwAAEteY16jXldee16TXnNeT15XXqAYTBwAADteZ16jXldep15zXmdedATIGFAcAAAExBhUHAAANICAgMDItNTY3NTExMQAAAAAJegAAAAl6AAAACXoAAAAGFwcAAAEwAQmKAAAABhkHAAABMQoAATAKCgHm+P//dP///wAAAAABWQAAAAQAAAAAGnxt6WDWCAYbBwAAAteUCgoGHAcAAAXXniLXkAYdBwAADdeh16fXnCDXl9eg15kGHgcAAAI3OQYfBwAACteT15nXkNeY15QGIAcAAAU5NTA2Mgl6AAAACgYiBwAAATMGIwcAAAIxMwYkBwAAE9eh16DXmdejINee15HXqdeo16oGJQcAAAE2BiYHAAAP16nXkyDXlNeX15XXqdefBicHAAAT157Xkdep16jXqiDXpteZ15XXnwEyBigHAAABMQYpBwAADSAgIDAyLTUzMzAyNDQAAAAACXoAAAAJegAAAAl6AAAABisHAAABMAEJigAAAAYtBwAAATEKAAEwCgoB0vj//3T///8AAAAAAVoAAAAEAAAAAKpqk7Be1ggGLwcAAALXkQoKBjAHAAAF15Mi16gGMQcAABPXmdeg15nXkSDXnteo15PXm9eZBjIHAAACMzgGMwcAABXXnteZ16DXlNecINeo16TXldeQ15kGNAcAAAUxODE1OAl6AAAACgY2BwAAAjE3BjcHAAABMQY4BwAAFteh16DXmdejINeR15DXqCDXqdeR16IGOQcAAAE0BjoHAAAc16fXoNeZ15XXnyDXlNeg15LXkSDXp9eV157XlAY7BwAADdeR15DXqCDXqdeR16IBMgY8BwAAATEGPQcAAA0gICAwOC02MjAzNjAwAAAAAAl6AAAACXoAAAAJegAAAAY/BwAAATABCYoAAAAGQQcAAAExCgABMAoKAb74//90////AAAAAAFbAAAABAAAAACWlchYW9YIBkMHAAAC15QKCgZEBwAABdeeIteQBkUHAAAY16TXnNeT157XnyDXpNeo15kg15DXoNeUBkYHAAACOTcGRwcAABTXpNeh15nXm9eV16rXqNek15nXlAZIBwAABjEwMDIxMQl6AAAACgZKBwAAATQGSwcAAAI4MQZMBwAAGteh16DXmdejINee15LXk9ecINeU16LXntenBk0HAAACNzcGTgcAABbXqdeTINep15DXldecINei157XldeoBk8HAAAR157XkteT15wg15TXotee16cBMgZQBwAAATEGUQcAAA0gICAwNC02MDQ5NjAwAAAAAAl6AAAACXoAAAAJegAAAAZTBwAAATABCYoAAAAGVQcAAAExCgABMAoKAar4//90////AAAAAAFcAAAABAAAAAD2aoLjWdYIBlcHAAAC15IKCgZYBwAABdeTIteoBlkHAAAZ15DXpNeg16HXmdeZ15Eg16TXkNeZ16DXlAZaBwAAAjEyBlsHAAAK16jXmdeQ15XXqgZcBwAABTMyMTU4CXoAAAAKBl4HAAABNgZfBwAAATEGYAcAABvXodeg15nXoyDXoNeq16DXmdeUINec15Eg15QGYQcAAAIxNwZiBwAADNep15jXntek16TXqAZjBwAACteg16rXoNeZ15QBMgZkBwAAATAGZQcAAA0gICAwOS04NjA3MjIyAAAAAAl6AAAACXoAAAAJegAAAAZnBwAAATABCYoAAAAGaQcAAAExCgABMAoKAZb4//90////AAAAAAFdAAAABAAAAABIJ1x1WNYIBmsHAAAC15AKCgZsBwAABdeTIteoBm0HAAAb16TXqNeZ15PXqNeZ15og15nXldec16DXk9eUBm4HAAABNgZvBwAADNei15nXoNeZ15nXnQZwBwAABTI0NzI3BnEHAABC15TXqNeV16TXkNeUINee16fXkdec16og15fXldec15nXnSDXodeb16jXqteZ15nXnSDXntei15wg15LXmdecIDQwCgZyBwAAAjg1BnMHAAABMQZ0BwAAG9eh16DXmdejINeU16DXlNec16og154u16bXpAZ1BwAAATkGdgcAABHXk9eo15og15TXqdec15XXnQZ3BwAABteg16nXqAEyBngHAAABMQZ5BwAADSAgIDA0LTg0NzkyMjEbAAAACXoAAAAJegAAAAl6AAAABnsHAAABMAEJigAAAAZ9BwAAATEKAAEwCgoBgvj//3T///8AAAAAAV4AAAAEAAAAAETP+hdV1ggGfwcAAALXkwoKBoAHAAAE157XqAaBBwAAE9ee15jXnNeV158g15nXldeQ15EGggcAAAI5NwaDBwAAFNek16HXmdeb15XXqteo16TXmdeUBoQHAAAGOTk5OTAzCXoAAAAKBoYHAAABMgaHBwAAAjEwBogHAAAX16HXoNeZ16Mg15HXkNeW15wg16oi15AJegAAAAaKBwAAHdeR15bXnCAzNSDXqNeV16DXmdeqINeo16TXkNecBosHAAAU16rXnCDXkNeR15nXkS3Xmdek15UBMgaMBwAAATEGjQcAAA0gICAwMy02MDEyNjAwAAAAAAl6AAAACXoAAAAJegAAAAaPBwAAATABCYoAAAAGkQcAAAExCgABMAoKAW74//90////AAAAAAFfAAAABAAAAADeVhWCVNYIBpMHAAAC15IKCgaUBwAABdeTIteoBpUHAAAV15bXmdeq15DXldeZINeQ157XkteTBpYHAAABNgaXBwAADNei15nXoNeZ15nXnQaYBwAABjExNzE4Nwl6AAAACgaaBwAAAjgzBpsHAAACMzIGnAcAABzXodeg15nXoyDXldeV16TXkCDXqdeV16LXpNeYBp0HAAADMTAwBp4HAAAe157Xl9eg15Qg16TXnNeZ15jXmdedINep16LXpNeYBp8HAAAO15nXqNeV16nXnNeZ150BMgagBwAAATEGoQcAAA0gICAwMi01MDkwNDQ4AAAAAAl6AAAACXoAAAAJegAAAAajBwAAATABCYoAAAAGpQcAAAExCgABMAoKAVr4//90////AAAAAAFgAAAABAAAAACKkWxTVNYIBqcHAAAC15IKCgaoBwAABdeRIteQBqkHAAAR16nXqCDXkdeo15XXqNeZ15QGqgcAAAI3OQarBwAACteT15nXkNeY15QGrAcAAAYxMDAyMTIJegAAAAoGrgcAAAEyBq8HAAACNTAGsAcAABTXodeg15nXoyDXqNee16og15LXnwaxBwAAAjc2BrIHAAAM15HXmdeQ15zXmdenBrMHAAAL16jXnteqINeS158BMga0BwAAATEGtQcAAA0gICAwMy02NzU2ODg4AAAAAAl6AAAACXoAAAAJegAAAAa3BwAAATABCYoAAAAGuQcAAAExCgABMAoKAUb4//90////AAAAAAFhAAAABAAAAABWly7AU9YIBrsHAAAC15EKCga8BwAABdeTIteoBr0HAAAV16fXmdeY16jXldeh16gg15DXnNeUBr4HAAABNQa/BwAACNeg16nXmdedBsAHAAAGMTMwMzczCXoAAAAKBsIHAAACODEGwwcAAAIyMAbEBwAAGteh16DXmdejINeg15XXldeUINeZ16LXp9eRBsUHAAABNgbGBwAAEdeU16jXkSDXlteV15XXmdefBscHAAAO15nXqNeV16nXnNeZ150BMgbIBwAAATEGyQcAAA0gICAwMi01ODQzODg4AAAAAAl6AAAACXoAAAAJegAAAAbLBwAAATABCYoAAAAGzQcAAAExCgABMAoKATL4//90////AAAAAAFiAAAABAAAAAAcPMAyTtYIBs8HAAAC15EKCgbQBwAABdeTIteoBtEHAAAT15DXqNeS15Ug15PXoNeZ15DXnAbSBwAAAjIxBtMHAAAU16TXodeZ15vXmdeQ15jXqNeZ15QG1AcAAAYxMTk4NDUJegAAAAoG1gcAAAEzBtcHAAACMjMG2AcAABvXodeg15nXoyDXmdeo15XXqdec15nXnSDXqC4G2QcAAAEzBtoHAAAS15jXqNeV157XpNec15PXldeoBtsHAAAO15nXqNeV16nXnNeZ150BMgbcBwAAATEG3QcAAA0gICAwMi01Njc1MTExAAAAAAl6AAAACXoAAAAJegAAAAbfBwAAATABCYoAAAAG4QcAAAExCgABMAoKAR74//90////AAAAAAFjAAAABAAAAAAwmJf6TdYIBuMHAAAC15EKCgbkBwAABdeTIteoBuUHAAAQ15HXqCDXkNecINeQ16HXowbmBwAAAjExBucHAAAS15DXldeo15XXnNeV15LXmdeUBugHAAAGMTE0NTg0CXoAAAAKBuoHAAABMgbrBwAAAjYwBuwHAAAV16HXoNeZ16Mg15TXqNem15zXmdeUBu0HAAACMTMG7gcAAArXlNeo16bXldeSBu8HAAAM15TXqNem15zXmdeUATIG8AcAAAEwBvEHAAANICAgMDktOTcyNzUwMAAAAAAJegAAAAl6AAAACXoAAAAG8wcAAAEwAQmKAAAABvUHAAABMQoAATAKCgEK+P//dP///wAAAAABZAAAAAQAAAAAQr6bVU3WCAb3BwAAAteQCgoG+AcAAAXXkyLXqAb5BwAAE9ek15XXodee158g16jXoNeY15UG+gcAAAIzOAb7BwAAFdee15nXoNeU15wg16jXpNeV15DXmQb8BwAABTMzNTg3CXoAAAAKBv4HAAACODYG/wcAAAExBgAIAAAb16HXoNeZ16Mg15TXoNeU15zXqiDXni7XnteoBgEIAAACMTkGAggAABHXmdeTINeX16jXldem15nXnQYDCAAACteg16rXoNeZ15QBMgYECAAAATEGBQgAAA0gICAwOS04OTI2MzAwAAAAAAl6AAAACXoAAAAJegAAAAYHCAAAATABCYoAAAAGCQgAAAExCgABMAoKAfb3//90////AAAAAAFlAAAABAAAAAAoB8AvTdYIBgsIAAAC15AKCgYMCAAABdeTIteoBg0IAAAR16rXnteZ16gg15bXmdeV15QGDggAAAMxMDUGDwgAABHXnteZ15XXnyDXoNep15nXnQYQCAAABjEwNzc3NQl6AAAACgYSCAAAATIGEwgAAAI3MAYUCAAAFteh16DXmdejINeR16DXmSDXkdeo16cGFQgAAAExBhYIAAAT15nXlNeV15PXlCDXlNec15XXmQYXCAAADdeR16DXmSDXkdeo16cBMgYYCAAAATEGGQgAAA0gICAwMy01Nzc4NzAwAAAAAAl6AAAACXoAAAAJegAAAAYbCAAAATABCYoAAAAGHQgAAAExCgABMAoKAeL3//90////AAAAAAFmAAAABAAAAAAMisJCSdYIBh8IAAAC15IKCgYgCAAABdeS15EnBiEIAAAT16HXkdeZ15XXnyDXoNeQ15XXlAYiCAAAAjc5BiMIAAAK15PXmdeQ15jXlAYkCAAABjE4ODM2Ngl6AAAACgYmCAAAAjgxBicIAAABMwYoCAAAHNeh16DXmdejINeo157XldeqINek15XXnNeZ158GKQgAAAI4MQYqCAAAHNeV16LXkyDXkNeo15HXoiDXlNeQ16jXpteV16oGKwgAAA7Xmdeo15XXqdec15nXnQEyBiwIAAABMQYtCAAADSAgIDAyLTU4ODA4MDAAAAAACXoAAAAJegAAAAl6AAAABi8IAAABMAEJigAAAAYxCAAAATEKAQEwCgoBzvf//3T///8AAAAAAWcAAAAEAAAAALxdxL1I1ggGMwgAAALXkQoKBjQIAAAF15Mi16gGNQgAABPXodek15XXqNeZINei15DXnteoBjYIAAACMTQGNwgAAB3XqNeQ15XXnteY15XXnNeV15LXmdeUL9ee16TXqAY4CAAABTI4OTM5CXoAAAAKBjoIAAABNAY7CAAAATEGPAgAABrXodeg15nXoyDXl9eZ16TXlCDXqNeQ16nXmQY9CAAAATIGPggAAAjXm9eV16jXmQY/CAAACNeX15nXpNeUATIGQAgAAAExBkEIAAANICAgMDQtODY4NjgwMAAAAAAJegAAAAl6AAAACXoAAAAGQwgAAAEwAQmKAAAABkUIAAABMQoAATAKCgG69///dP///wAAAAABaAAAAAQAAAAAoIgiukjWCAZHCAAAAteRCgoGSAgAAAXXkyLXqAZJCAAAF9eR16jXkNep15jXqCDXkNeg15PXqNeZBkoIAAACMjYGSwgAABLXlNee15jXldec15XXkteZ15QGTAgAAAUxNjkxMgl6AAAACgZOCAAAATQGTwgAAAExBlAIAAAa16HXoNeZ16Mg15fXmdek15Qg16jXkNep15kGUQgAAAEyBlIIAAAI15fXldeo15kGUwgAAAjXl9eZ16TXlAEyBlQIAAABMQZVCAAADSAgIDA0LTg2ODY4MDAAAAAACXoAAAAJegAAAAl6AAAABlcIAAABMAEJigAAAAZZCAAAATEKAAEwCgoBpvf//3T///8AAAAAAWkAAAAEAAAAAAxytbNI1ggGWwgAAALXkQoKBlwIAAAF15Mi16gGXQgAABzXkNeZ15XXkSDXoteR15PXkNecINei15bXmdedBl4IAAABNAZfCAAAEteQ15XXqNeq15XXpNeT15nXlAZgCAAABjEwMzIzNwl6AAAACgZiCAAAATQGYwgAAAExBmQIAAAa16HXoNeZ16Mg15fXmdek15Qg16jXkNep15kGZQgAAAEyBmYIAAAI15vXldeo15kGZwgAAAjXl9eZ16TXlAEyBmgIAAABMQZpCAAADSAgIDA0LTg2ODY4MDAAAAAACXoAAAAJegAAAAl6AAAABmsIAAABMAEJigAAAAZtCAAAATEKAAEwCgoBkvf//3T///8AAAAAAWoAAAAEAAAAAKrZ769I1ggGbwgAAALXkQoKBnAIAAAF15Mi16gGcQgAABLXnteXJ9eV15wg15DXmdee158GcggAAAEyBnMIAAAI15Au15Au15IGdAgAAAYxMTk0MDgJegAAAAoGdggAAAE0BncIAAABMQZ4CAAAGteh16DXmdejINeX15nXpNeUINeo15DXqdeZBnkIAAABMgZ6CAAACNeb15XXqNeZBnsIAAAI15fXmdek15QBMgZ8CAAAATEGfQgAAA0gICAwNC04Njg2ODAwAAAAAAl6AAAACXoAAAAJegAAAAZ/CAAAATABCYoAAAAGgQgAAAExCgABMAoKAX73//90////AAAAAAFrAAAABAAAAABKpbOtSNYIBoMIAAAC15EKCgaECAAABdeTIteoBoUIAAAV15HXqNeR15DXqNeUINeZ15XXodejBoYIAAACMTEGhwgAABLXkNeV16jXldec15XXkteZ15QGiAgAAAUxOTYyNgl6AAAACgaKCAAAATQGiwgAAAExBowIAAAa16HXoNeZ16Mg15fXmdek15Qg16jXkNep15kGjQgAAAEyBo4IAAAI15fXldeo15kGjwgAAAjXl9eZ16TXlAEyBpAIAAABMAaRCAAADSAgIDA0LTg2ODY4MDAbAAAACXoAAAAJegAAAAl6AAAABpMIAAABMAEJigAAAAaVCAAAATEKAAEwCgoBavf//3T///8AAAAAAWwAAAAEAAAAAKCffYRI1ggGlwgAAALXkQoKBpgIAAAF15LXkScGmQgAABHXotee15DXqSDXqNeg15nXnwaaCAAAAjk2BpsIAAAb16fXnNeZ16DXkNeZ16og16rXp9ep15XXqNeqBpwIAAAGMTAwMTc2CXoAAAAKBp4IAAABNgafCAAAAjczBqAIAAAa16HXoNeZ16Mg15In16HXqCDXkCfXlteo16cJegAAAAaiCAAAE9eSJ9eh16gg15An15bXqNen15AGowgAABXXkmDXodeQ16gg15At15bXqNen15ABMgakCAAAATEGpQgAAA0gICAwNC02MjYxNDQxAAAAAAl6AAAACXoAAAAJegAAAAanCAAAATABCYoAAAAGqQgAAAExCgABMAoKAVb3//90////AAAAAAFtAAAABAAAAABCKCFfRdYIBqsIAAAC15QKCgasCAAABdeRIteQBq0IAAAX15DXkdeVINeY15nXqCDXlyfXnNeV15MGrggAAAI3OQavCAAACteT15nXkNeY15QGsAgAAAYxMDAxOTkJegAAAAoGsggAAAI4MwazCAAAATIGtAgAABjXodeg15nXoyDXpteV16gg15HXkNeX16gJegAAAAa2CAAAD9em15XXqCDXkdeQ15fXqAa3CAAADteZ16jXldep15zXmdedATIGuAgAAAExBrkIAAANICAgMDItNjQ0MjMwMAAAAAAJegAAAAl6AAAACXoAAAAGuwgAAAEwAQmKAAAABr0IAAABMQoAATAKCgFC9///dP///wAAAAABbgAAAAQAAAAAeA6nKEPWCAa/CAAAAteRCgoGwAgAAAXXkyLXqAbBCAAAIdeS15XXqNeZ15DXoNeV15Eg15DXnNeb16HXoNeT16jXlAbCCAAAAjM4BsMIAAAV157Xmdeg15TXnCDXqNek15XXkNeZBsQIAAAFMjU3MjUJegAAAAoGxggAAAI2NwbHCAAAATIGyAgAABzXodeg15nXoyDXnteV16fXkyDXlNeq15fXmdeZBskIAAABMgbKCAAAE9eQ15HXnyDXkteR15nXqNeV15wGywgAABTXqtecINeQ15HXmdeRLdeZ16TXlQEyBswIAAABMQbNCAAADSAgMDc2LTg4ODk2MDAAAAAACXoAAAAJegAAAAl6AAAABs8IAAABMAEJigAAAAbRCAAAATEKAAEwCgoBLvf//3T///8AAAAAAW8AAAAEAAAAANaRuKQ91ggG0wgAAALXkQoKBtQIAAAF15LXkScG1QgAABPXodeR15nXldefINeg15DXldeUBtYIAAACNzkG1wgAAArXk9eZ15DXmNeUBtgIAAAGMTg4MzY2CXoAAAAKBtoIAAACNDQG2wgAAAIxNQbcCAAAG9eh16DXmdejINeeLteo16TXldeQ15kg16bXkAbdCAAAATYG3ggAABjXqdeTINeg15fXnNeqINem15DXnNeZ150G3wgAAA3XkdeZ16og16nXntepATIG4AgAAAExBuEIAAANICAgMDItOTY2OTQ0NAAAAAAJegAAAAl6AAAACXoAAAAG4wgAAAEwAQmKAAAABuUIAAABMQoBATAKCgEa9///dP///wAAAAABcAAAAAQAAAAA2Ku2bzrWCAbnCAAAAteUCgoG6AgAAAXXkteRJwbpCAAAG9eS15zXldep15jXmdeZ158g15PXldeo15nXnwbqCAAAAjc5BusIAAAK15PXmdeQ15jXlAbsCAAABTk1MTgzBu0IAACeAdee16fXkdecINeX15XXnNeZINeh15vXqNeqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDXnten15HXnCDXnNeQ15fXqCDXoNeZ16rXldeXINeR16jXmdeQ15jXqNeZ15Qv16fXmdem15XXqCDXp9eZ15HXlC/Xqdeo15XXldecCgbuCAAAAjgyBu8IAAABMgbwCAAAFNeh16DXmdejINeR15nXqiDXkNecCXoAAAAG8ggAABzXky7XoC4g157Xlteo15cg15HXoNeZ157XmdefBvMIAAAL15HXmdeqINeQ15wBMgb0CAAAATEG9QgAAA0gICAwMi05OTczMzI5AAAAAAl6AAAACXoAAAAJegAAAAb3CAAAATABCYoAAAAG+QgAAAExCgABMAoKAQb3//90////AAAAAAFxAAAABAAAAAC0ow1UOtYIBvsIAAAC15QKCgb8CAAABdeTIteoBv0IAAAV16fXqNee15XXoNeUINec15nXnNeZBv4IAAABNgb/CAAADNei15nXoNeZ15nXnQYACQAABTg3NTE4BgEJAACJA9eU16jXldek15Ag157XmNek15wg15HXqNep16rXmdeqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDXlNeo15XXpNeQINee15jXpNecINeR16fXqNeg15nXqiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg15TXqNeV16TXkCDXnteY16TXnCDXkdeS15zXldeQ16fXldee15QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgINeU16jXldek15Ag157XmNek15wg15HXp9eY16jXp9eYICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDXlNeo15XXpNeQINee15jXpNecINeR16TXnNeh15jXmden15Qg16nXnCDXlNei15nXnwoGAgkAAAEyBgMJAAACNTAGBAkAABTXodeg15nXoyDXqNee16og15LXnwYFCQAAAjc2BgYJAAAM15HXmdeQ15zXmdenBgcJAAAL16jXnteqINeS158BMgYICQAAATEGCQkAAA0gICAwMy02NzU2ODg4AAAAAAl6AAAACXoAAAAJegAAAAYLCQAAATABCYoAAAAGDQkAAAExCgABMAoKAfL2//90////AAAAAAFyAAAABAAAAABcoSalOdYIBg8JAAAC15MKCgYQCQAABdeTIteoBhEJAAAV16LXkdeQ16Eg15nXkNeh157XmdefBhIJAAABNQYTCQAACNeg16nXmdedBhQJAAAGMTA3MzM0CXoAAAAKBhYJAAABNAYXCQAAAjQxBhgJAAAc16HXoNeZ16Mg16DXpteo16og15TXqdec15XXnQYZCQAAAjE2BhoJAAAI16HXnNeQ150GGwkAAAjXoNem16jXqgEyBhwJAAABMQYdCQAADSAgIDA0LTYwMzY2MDAAAAAACXoAAAAJegAAAAl6AAAABh8JAAABMAEJigAAAAYhCQAAATEKAAEwCgoB3vb//3T///8AAAAAAXMAAAAEAAAAACAv0Jo51ggGIwkAAALXkwoKBiQJAAAF15Mi16gGJQkAABnXnNeZ16nXkdeZ16Ug15LXkdeo15nXkNecBiYJAAACMTMGJwkAABzXkNeg15PXlden16jXmdeg15XXnNeV15LXmdeUBigJAAAGMTE0OTIwCXoAAAAKBioJAAABMwYrCQAAATEGLAkAABzXodeg15nXoyDXnteh15nXnNeqINeZ16nXqNeZBi0JAAACMTQGLgkAABXXnteh15nXnNeqINeZ16nXqNeZ150GLwkAAA7Xmdeo15XXqdec15nXnQEyBjAJAAABMAYxCQAADSAgIDAyLTU2Nzc2NzcAAAAACXoAAAAJegAAAAl6AAAABjMJAAABMAEJigAAAAY1CQAAATEKAAEwCgoByvb//3T///8AAAAAAXQAAAAEAAAAACT2MNk41ggGNwkAAALXkgoKBjgJAAAF15Mi16gGOQkAABvXnNeZ15HXldeR15nXpSDXmdeV15zXmdeQ158GOgkAAAIzOAY7CQAAFdee15nXoNeU15wg16jXpNeV15DXmQY8CQAABTIwMDc5CXoAAAAKBj4JAAACNjcGPwkAAAEyBkAJAAAc16HXoNeZ16Mg157Xlden15Mg15TXqteX15nXmQZBCQAAATIGQgkAABPXkNeR158g15LXkdeZ16jXldecBkMJAAAU16rXnCDXkNeR15nXkS3Xmdek15UBMgZECQAAATEGRQkAAA0gIDA3Ni04ODg5NjAwAAAAAAl6AAAACXoAAAAJegAAAAZHCQAAATABCYoAAAAGSQkAAAExCgABMAoKAbb2//90////AAAAAAF1AAAABAAAAACg/lwaONYIBksJAAAC15EKCgZMCQAAB9ei15Ui16EGTQkAABPXlden16HXntefINeg16LXnteUBk4JAAACOTMGTwkAABvXoteR15XXk9eUINeh15XXpteZ15DXnNeZ16oGUAkAAAY5MjM5NTUJegAAAAoGUgkAAAI0NAZTCQAAAjEwBlQJAAAb16HXoNeZ16Mg15LXkdei16og16nXqNeqLdeRBlUJAAABNgZWCQAADdeR16gg15DXmdec158GVwkAAA3XkdeZ16og16nXntepATIGWAkAAAExBlkJAAANICAgMDItOTkwNjc3NwAAAAAJegAAAAl6AAAACXoAAAAGWwkAAAEwAQmKAAAABl0JAAABMQoAATAKCgGi9v//dP///wAAAAABdgAAAAQAAAAA8G9s/jfWCAZfCQAAAteRCgoGYAkAAAXXkyLXqAZhCQAAEdeR15XXqNeV16cg16nXqNeUBmIJAAACMTQGYwkAAB3XqNeQ15XXnteY15XXnNeV15LXmdeUL9ee16TXqAZkCQAABTM0NTQxCXoAAAAKBmYJAAABNgZnCQAAAjY1BmgJAAAR16HXoNeZ16Mg15fXk9eo15QGaQkAAAI2NgZqCQAAE9ep15Mg16jXldeY16nXmdec15MGawkAAAjXl9eT16jXlAEyBmwJAAABMQZtCQAADSAgIDA0LTYxMjY2MDAAAAAACXoAAAAJegAAAAl6AAAABm8JAAABMAEJigAAAAZxCQAAATEKAAEwCgoBjvb//3T///8AAAAAAXcAAAAEAAAAAGbv3Uw31ggGcwkAAALXkAoKBnQJAAAF15Mi16gGdQkAABLXlyfXmNeZ15Eg15nXkNeh16gGdgkAAAI1NAZ3CQAAHdeQ15XXqNeq15XXpNeT15nXqiDXmdec15PXmdedBngJAAAFMzM0OTIJegAAAAoGegkAAAE0BnsJAAABMQZ8CQAAGteh16DXmdejINeX15nXpNeUINeo15DXqdeZBn0JAAABMgZ+CQAACNeb15XXqNeZBn8JAAAI15fXmdek15QBMgaACQAAATEGgQkAAA0gICAwNC04Njg2ODAwAAAAAAl6AAAACXoAAAAJegAAAAaDCQAAATABCYoAAAAGhQkAAAExCgABMAoKAXr2//90////AAAAAAF4AAAABAAAAABuOUAvN9YIBocJAAAC15AKCgaICQAABdeS15EnBokJAAAT16HXkdeZ15XXnyDXoNeQ15XXlAaKCQAAAjc5BosJAAAK15PXmdeQ15jXlAaMCQAABjE4ODM2Ngl6AAAACgaOCQAAAjQ0Bo8JAAACNDAGkAkAABfXodeg15nXoyDXnteV15PXmdei15nXnwaRCQAAAjI5BpIJAAAW15LXmdeg16og15HXmdeqINep15DXnwaTCQAAHNee15XXk9eZ16LXmdefLdee15vXkdeZ150t16gBMgaUCQAAATEGlQkAAA0gICAwOC05NzM1MjIyAAAAAAl6AAAACXoAAAAJegAAAAaXCQAAATABCYoAAAAGmQkAAAExCgEBMAoKAWb2//90////AAAAAAsWAh8PBQtoZWlnaHQ6MHB4OxYCZg9kFgJmD2QWAmYPFgIfAGgWBgIJD2QWBAIDDw8WAh8BBSvXlteZ157XldefINee15TXmdeoINec16jXldek15Ag157Xp9em15XXoteZZGQCCQ8WAh8LAnUW7AFmD2QWBgIBDw8WBB8VBQhTb3J0TGluax8TAgJkZAIDDw8WBB8VBQhTb3J0TGluax8TAgJkZAIHDw8WBB8VBQxTb3J0TGlua0RFU0MfEwICZGQCAQ9kFgpmDxUBCNeg16nXmdedZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzExNTc2MCcpO3JldHVybiBmYWxzZTsWAmYPFQIAH9eV15XXnNen15XXkdeUINec15nXldeT157Xmdec15RkAgMPD2QWAh8JBU1qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzEnLCfXkteR15wg157Xldeb15HXqCcsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VASTXkifXkdecINee15XXm9eR16ggMSwg15nXqNeV16nXnNeZ151kAgYPFQEKMDQvMDQvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTY3MzA2ODZkZAICD2QWCmYPFQEM16LXmdeg15nXmdedZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEzMDE5OCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gP16jXldeYINei15XXnteoZAIFDw9kFgIfCQVIamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxMicsJ9eS158t15HXoNeZ157XmdefJywn15DXmdec16onKTtyZXR1cm4gZmFsc2U7FgJmDxUBHteS158t15HXoNeZ157XmdefIDEyLCDXkNeZ15zXqmQCCA8VAQowMy8wNC8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDgtNjMwNjIwMGRkAgMPZBYKZg8VARvXp9ec15nXoNeQ15nXqiDXqten16nXldeo16pkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjQ4Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBTXkteV15nXmNeUINeR16og15DXnGQCAw8PZBYCHwkFRGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMzknLCfXk9eo15og15DXqNelJywn15fXqNeZ16knKTtyZXR1cm4gZmFsc2U7FgJmDxUBGteT16jXmiDXkNeo16UgMzksINeX16jXmdepZAIGDxUBCjAyLzA0LzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC04ODAwNDAwZGQCBA9kFgpmDxUBHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEzMTMxNScpO3JldHVybiBmYWxzZTsWAmYPFQIF15LXkScU15cn15XXqNeZINeQ15fXnNeQ151kAgUPD2QWAh8JBUlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzcnLCfXnteg15fXnSDXkdeS15nXnycsJ9eQ16nXk9eV15MnKTtyZXR1cm4gZmFsc2U7FgJmDxUBH9ee16DXl9edINeR15LXmdefIDcsINeQ16nXk9eV15NkAggPFQEKMzEvMDMvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTg1Nzk4MDBkZAIFD2QWCmYPFQEI16HXm9eo16pkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTY4MTYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoENeR16jXnteZINeSJ9eV159kAgMPD2QWAh8JBUNqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzgnLCfXnteZ15vXlCcsJ9en16jXmdeqINeS16onKTtyZXR1cm4gZmFsc2U7FgJmDxUBGdee15nXm9eUIDgsINen16jXmdeqINeS16pkAgYPFQEKMjgvMDMvMjAxOWQCBw8WAh8AZxYCZg8PZBYCHwkFYmphdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCc0Jywn157Xmdeb15QgOCwg16fXqNeZ16og15LXqicsJzE2ODE2Jywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAIGD2QWCmYPFQEb15LXoNeY15nXp9eUINen15zXmdeg15nXqiAtZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk5OTkyMicpO3JldHVybiBmYWxzZTsWAmYPFQIF15LXkScb15TXqNeY157XkNeZ16gg16nXldec157XmdeqZAIFDw9kFgIfCQVOamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc4Jywn15PXmdee15XXoNeUJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBJNeT15nXnteV16DXlCA4LCDXqtecINeQ15HXmdeRLdeZ16TXlWQCCA8VAQoyOC8wMy8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDMtNjMwNDYwMGRkAgcPZBYKZg8VARvXm9eZ16jXldeo15LXmdeUINeb15zXnNeZ16pkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzMxMzgnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoF9eR15XXp9eZ158g15DXnNeb16HXmdeZZAIDDw9kFgIfCQVXamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc0Jywn15jXqdeo16DXmdeX15XXkdeh16fXmScsJ9en16jXmdeqINep157Xldeg15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBLdeY16nXqNeg15nXl9eV15HXoden15kgNCwg16fXqNeZ16og16nXnteV16DXlGQCBg8VAQoyNy8wMy8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDQtNjgxODYwMGRkAggPZBYKZg8VAQzXoteZ16DXmdeZ151kAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTQwOTAyJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBfXqNeV15jXqdeY15nXnyDXmNec15nXlGQCBQ8PZBYCHwkFSGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTInLCfXktefLdeR16DXmdee15nXnycsJ9eQ15nXnNeqJyk7cmV0dXJuIGZhbHNlOxYCZg8VAR7XktefLdeR16DXmdee15nXnyAxMiwg15DXmdec16pkAggPFQEKMjcvMDMvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTYzMDYyMDBkZAIJD2QWCmYPFQEb15vXmdeo15XXqNeS15nXlCDXm9ec15zXmdeqZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzM2NDEwJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBXXmdek16jXnteV15Eg15DXldec15JkAgMPD2QWAh8JBVZqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzInLCfXkdefINeS15XXqNeZ15XXnycsJ9eX16bXldeoINeU15LXnNeZ15zXmdeqJyk7cmV0dXJuIGZhbHNlOxYCZg8VASzXkdefINeS15XXqNeZ15XXnyAyLCDXl9em15XXqCDXlNeS15zXmdec15nXqmQCBg8VAQoyNi8wMy8yMDE5ZAIHDxYCHwBnFgJmDw9kFgIfCQV1amF2YXNjcmlwdDpTZXRVcEFwcG9pbnQoJzgnLCfXkdefINeS15XXqNeZ15XXnyAyLCDXl9em15XXqCDXlNeS15zXmdec15nXqicsJzM2NDEwJywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAIKD2QWCmYPFQEI15Au15Au15JkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnNjk0MjknKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoEdeQ15zXkdeWINee16jXodecZAIFDw9kFgIfCQVBamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc3MScsJ9eb16DXqNeqJywn15DXqdeT15XXkycpO3JldHVybiBmYWxzZTsWAmYPFQEX15vXoNeo16ogNzEsINeQ16nXk9eV15NkAggPFQEKMjYvMDMvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTg2ODc2MDBkZAILD2QWCmYPFQEb15vXmdeo15XXqNeS15nXlCDXm9ec15zXmdeqZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI2NTcwJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBnXldeg15jXldeo16jXlSDXnteV16jXmdehZAIDDw9kFgIfCQVLamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxMycsJ9ec15nXnNeZ16DXkdec15XXnScsJ9eg16rXoNeZ15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBIdec15nXnNeZ16DXkdec15XXnSAxMywg16DXqteg15nXlGQCBg8VAQoyNi8wMy8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDktODg5NjYwMGRkAgwPZBYKZg8VAQrXk9eZ15DXmNeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzE4ODM2NicpO3JldHVybiBmYWxzZTsWAmYPFQIF15LXkScT16HXkdeZ15XXnyDXoNeQ15XXlGQCBQ8PZBYCHwkFUmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNTgnLCfXlNeo15Eg16nXkNeV15zXlteV158nLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEo15TXqNeRINep15DXldec15bXldefIDU4LCDXmdeo15XXqdec15nXnWQCCA8VAQoyNS8wMy8yMDE5ZAIJDxYCHwBnFgJmDw9kFgIfCQVyamF2YXNjcmlwdDpTZXRVcEFwcG9pbnQoJzExJywn15TXqNeRINep15DXldec15bXldefIDU4LCDXmdeo15XXqdec15nXnScsJzE4ODM2NicsJ9ee15jXpNecJywnVHJ1ZScpOyByZXR1cm4gZmFsc2U7ZAIND2QWCmYPFQEV157Xmdeg15TXnCDXqNek15XXkNeZZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzg0NjU1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBfXkNeR15Ug15cn15zXoyDXodeQ157XqGQCAw8PZBYCHwkFO2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnJywnJywn15nXqNeV16nXnNeZ150nKTtyZXR1cm4gZmFsc2U7FgJmDxUBECAg15nXqNeV16nXnNeZ151kAgYPFQEKMjUvMDMvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTU3MjIzNzdkZAIOD2QWCmYPFQEV157Xmdeg15TXnCDXqNek15XXkNeZZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI3ODg5Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBvXk9eV16nXoNeZ16bXp9eZINeT15XXqNeV159kAgUPD2QWAh8JBVFqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzIzJywn16nXpNeo15nXoNem16cnLCfXqtecINeQ15HXmdeRLdeZ16TXlScpO3JldHVybiBmYWxzZTsWAmYPFQEn16nXpNeo15nXoNem16cgMjMsINeq15wg15DXkdeZ15Et15nXpNeVZAIIDxUBCjI0LzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy02OTcwMzE2ZGQCDw9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTUwNjInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeeIteQDdeh16fXnCDXl9eg15lkAgMPD2QWAh8JBVJqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE0Jywn157XodeZ15zXqiDXmdep16jXmdedJywn15nXqNeV16nXnNeZ150nKTtyZXR1cm4gZmFsc2U7FgJmDxUBKNee16HXmdec16og15nXqdeo15nXnSAxNCwg15nXqNeV16nXnNeZ151kAgYPFQEKMjQvMDMvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTU2Nzc2NzdkZAIQD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxODgzNjYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnE9eh15HXmdeV158g16DXkNeV15RkAgUPD2QWAh8JBURqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzEnLCfXotee15XXoScsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VARrXotee15XXoSAxLCDXmdeo15XXqdec15nXnWQCCA8VAQoyMS8wMy8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNjIyODExMWRkAhEPZBYKZg8VAQjXodeb16jXqmQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNjgxNicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gQ15HXqNee15kg15In15XXn2QCAw8PZBYCHwkFSGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnOScsJ9eR16jXqSDXkNep16gnLCfXkdeQ16gg16nXkdeiJyk7cmV0dXJuIGZhbHNlOxYCZg8VAR7Xkdeo16kg15DXqdeoIDksINeR15DXqCDXqdeR16JkAgYPFQEKMjAvMDMvMjAxOWQCBw8WAh8AZxYCZg8PZBYCHwkFaGphdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCcxNicsJ9eR16jXqSDXkNep16ggOSwg15HXkNeoINep15HXoicsJzE2ODE2Jywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAISD2QWCmYPFQEd16jXkNeV157XmNeV15zXldeS15nXlC/Xntek16hkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzQwNjknKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoE9ep16jXoteR15kg15DXnteZ16hkAgUPD2QWAh8JBUhqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzMxJywn15XXldec15onLCfXp9eo15nXqiDXkNeV16DXlScpO3JldHVybiBmYWxzZTsWAmYPFQEe15XXldec15ogMzEsINen16jXmdeqINeQ15XXoNeVZAIIDxUBCjE5LzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy01MzA4MzAwZGQCEw9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTg4MzY2Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxPXodeR15nXldefINeg15DXldeUZAIDDw9kFgIfCQVIamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc2Jywn15HXqCDXkNeZ15zXnycsJ9eR15nXqiDXqdee16knKTtyZXR1cm4gZmFsc2U7FgJmDxUBHteR16gg15DXmdec158gNiwg15HXmdeqINep157XqWQCBg8VAQoxNy8wMy8yMDE5ZAIHDxYCHwBnFgJmDw9kFgIfCQVoamF2YXNjcmlwdDpTZXRVcEFwcG9pbnQoJzE4Jywn15HXqCDXkNeZ15zXnyA2LCDXkdeZ16og16nXntepJywnMTg4MzY2Jywn157XmNek15wnLCdUcnVlJyk7IHJldHVybiBmYWxzZTtkAhQPZBYKZg8VAR3Xqdeo15XXqiDXpNeZ15bXmdeV16rXqNek15nXlGQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCc5NTEyMycpO3JldHVybiBmYWxzZTsWAmYPFQIF15LXkScN16nXk9eUINeS15HXmWQCBQ8PZBYCHwkFV2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTEnLCfXpNeZ16DXqdeY15nXmdefINeX15nXlCcsJ9ek16rXlyDXqten15XXldeUJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS3XpNeZ16DXqdeY15nXmdefINeX15nXlCAxMSwg16TXqteXINeq16fXldeV15RkAggPFQEKMTcvMDMvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTcyODk5MDBkZAIVD2QWCmYPFQEV157Xmdeg15TXnCDXqNek15XXkNeZZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzE2ODE2Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBDXkdeo157XmSDXkifXldefZAIDDw9kFgIfCQVQamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxMCcsJ9eR158g16bXkdeZINeZ16bXl9enJywn15HXkNeoINep15HXoicpO3JldHVybiBmYWxzZTsWAmYPFQEm15HXnyDXpteR15kg15nXpteX16cgMTAsINeR15DXqCDXqdeR16JkAgYPFQEKMTQvMDMvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTYyMDMyMDNkZAIWD2QWCmYPFQEI15Au15Au15JkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzAzMzknKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoFdeS16jXlteV15bXmSDXl9eQ16rXnWQCBQ8PZBYCHwkFQ2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTQnLCfXlNeQ15nXmdec15XXnycsJ9ec15XXkycpO3JldHVybiBmYWxzZTsWAmYPFQEZ15TXkNeZ15nXnNeV158gMTQsINec15XXk2QCCA8VAQoxMy8wMy8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDgtOTU4MzU1NWRkAhcPZBYKZg8VARvXm9eZ16jXldeo15LXmdeUINeb15zXnNeZ16pkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnODg4ODgnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoE9eo15XXpNeQINeT15XXktee15BkAgMPD2QWAh8JBUxqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzQnLCfXlNek15jXntefJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBIteU16TXmNee158gNCwg16rXnCDXkNeR15nXkS3Xmdek15VkAgYPFQEKMTMvMDMvMjAxOWQCBw8WAh8AZxYCZg8PZBYCHwkFbGphdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCcyMicsJ9eU16TXmNee158gNCwg16rXnCDXkNeR15nXkS3Xmdek15UnLCc4ODg4OCcsJ9eo15XXpNeQJywnRmFsc2UnKTsgcmV0dXJuIGZhbHNlO2QCGA9kFgpmDxUBEdee15nXldefINeg16nXmdedZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEyODU5Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBXXkNeS16jXoNeYINeQ15HXqNeU151kAgUPD2QWAh8JBUVqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE3Jywn16nXmNee16TXpNeoJywn16DXqteg15nXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEb16nXmNee16TXpNeoIDE3LCDXoNeq16DXmdeUZAIIDxUBCjEyLzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOS04NjA3MjIyZGQCGQ9kFgpmDxUBHdeo15PXmdeV15zXldeS15nXlCDXkNeR15fXoNeqZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzExMzc5MScpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gP15bXktecINee15fXnteTZAIDDw9kFgIfCQVKamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcnLCfXpteV16gg15HXkNeX16gnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEf16bXldeoINeR15DXl9eoICDXmdeo15XXqdec15nXnWQCBg8VAQoxMS8wMy8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNjQ0MjMwMGRkAhoPZBYKZg8VAQ/XoteV16gg15XXnteZ159kAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnODMyMzknKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoD9eY15TXlCDXoteV157XqGQCBQ8PZBYCHwkFU2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTgnLCfXmdeU15XXk9eUINeU15zXldeZJywn16TXqteXINeq16fXldeV15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBKdeZ15TXldeT15Qg15TXnNeV15kgMTgsINek16rXlyDXqten15XXldeUZAIIDxUBCjExLzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy05MDUxMzEzZGQCGw9kFgpmDxUBHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEwMDE2NCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15Ei15AW15In15XXkdeo15DXnyDXodeQ15zXl2QCAw8PZBYCHwkFUWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNycsJ9eo15XXmNep15nXnNeTJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VASfXqNeV15jXqdeZ15zXkyA3LCDXqNeQ16nXldefINec16bXmdeV159kAgYPFQEKMTEvMDMvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTk1OTc4MDBkZAIcD2QWCmYPFQEI15Au15Au15JkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTg3NjU0Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqCDXk9eV15LXnteQINep16kg16TXldeW15nXpteZ15XXqmQCBQ8PZBYCHwkFTGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNCcsJ9eU16TXmNee158nLCfXqtecINeQ15HXmdeRLdeZ16TXlScpO3JldHVybiBmYWxzZTsWAmYPFQEi15TXpNeY157XnyA0LCDXqtecINeQ15HXmdeRLdeZ16TXlWQCCA8VAQoxMS8wMy8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDMtNjk3MDMwNmRkAh0PZBYKZg8VARLXkNeV16jXqteV16TXk9eZ15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzU2MjgnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoFteQ15HXlSDXmNeZ16gg157Xl9ee15NkAgMPD2QWAh8JBVpqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzEnLCfXkNeV150g15jXldeR15Ag15DXnNep15nXpNeQ15AnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEw15DXldedINeY15XXkdeQINeQ15zXqdeZ16TXkNeQIDEsINeZ16jXldep15zXmdedZAIGDxUBCjEwLzAzLzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMi05OTExMDA3ZGQCHg9kFgpmDxUBEteQ15XXqNeq15XXpNeT15nXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMTg0NDEnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoFteq157XqCDXnteZ15vXkNecINep15lkAgUPD2QWAh8JBVdqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzQnLCfXp9eg15nXldefINeU16DXkteRINen15XXnteUJywn15HXkNeoINep15HXoicpO3JldHVybiBmYWxzZTsWAmYPFQEt16fXoNeZ15XXnyDXlNeg15LXkSDXp9eV157XlCA0LCDXkdeQ16gg16nXkdeiZAIIDxUBCjEwLzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOC02MjAzNjAwZGQCHw9kFgpmDxUBHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk1MTIzJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJw3XqdeT15Qg15LXkdeZZAIDDw9kFgIfCQVRamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyMycsJ9ep16TXqNeZ16DXptenJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBJ9ep16TXqNeZ16DXptenIDIzLCDXqtecINeQ15HXmdeRLdeZ16TXlWQCBg8VAQowNy8wMy8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDMtNjk3MDMxNmRkAiAPZBYKZg8VARXXnteo16TXkCDXkdei15nXodeV16dkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjM5Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBjXlyfXnNeQ15nXnNeUINek15DXqteg15RkAgUPD2QWAh8JBVFqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzIyJywn15PXqNeaINei15vXlScsJ9en16jXmdeqINeR15nXkNec15nXpycpO3JldHVybiBmYWxzZTsWAmYPFQEn15PXqNeaINei15vXlSAyMiwg16fXqNeZ16og15HXmdeQ15zXmdenZAIIDxUBCjA3LzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC05NTA1NTAwZGQCIQ9kFgpmDxUBHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk1MDg5Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgAT15TXk9eQ15nXlCDXlNeZ15zXlGQCAw8PZBYCHwkFTmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMycsJ9eY16jXldee16TXnNeT15XXqCcsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VASTXmNeo15XXntek15zXk9eV16ggMywg15nXqNeV16nXnNeZ151kAgYPFQEKMDYvMDMvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTU2NzUxMTFkZAIiD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAyNDQnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeRIteQHten16TXp9eQINeQ15HXqNeU150g15nXldec15nXlGQCBQ8PZBYCHwkFV2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNCcsJ9en16DXmdeV158g15TXoNeS15Eg16fXldee15QnLCfXkdeQ16gg16nXkdeiJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS3Xp9eg15nXldefINeU16DXkteRINen15XXnteUIDQsINeR15DXqCDXqdeR16JkAggPFQEKMDYvMDMvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTYyMDM2MDBkZAIjD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxODgzNjYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnE9eh15HXmdeV158g16DXkNeV15RkAgMPD2QWAh8JBTxqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJycsJzEnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQERMSAg15nXqNeV16nXnNeZ151kAgYPFQEKMDUvMDMvMjAxOWQCBw8WAh8AZxYCZg8PZBYCHwkFW2phdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCczNCcsJzEgINeZ16jXldep15zXmdedJywnMTg4MzY2Jywn157XmNek15wnLCdUcnVlJyk7IHJldHVybiBmYWxzZTtkAiQPZBYKZg8VARLXkNeV16jXqteV16TXk9eZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAzODYxJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBHXl9ee15DXldeZINeQ16HXo2QCBQ8PZBYCHwkFT2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNScsJ9em15HXmdeUINeV15nXpteX16cnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEl16bXkdeZ15Qg15XXmdem15fXpyA1LCDXmdeo15XXqdec15nXnWQCCA8VAQowNC8wMy8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNjQ2MTIyMmRkAiUPZBYKZg8VAQjXodeb16jXqmQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNjgxNicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gQ15HXqNee15kg15In15XXn2QCAw8PZBYCHwkFSGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMjEnLCfXkdefINeZ15TXldeT15QnLCfXqdeT16jXldeqJyk7cmV0dXJuIGZhbHNlOxYCZg8VAR7XkdefINeZ15TXldeT15QgMjEsINep15PXqNeV16pkAgYPFQEKMDQvMDMvMjAxOWQCBw8WAh8AZxYCZg8PZBYCHwkFaGphdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCczNicsJ9eR158g15nXlNeV15PXlCAyMSwg16nXk9eo15XXqicsJzE2ODE2Jywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAImD2QWCmYPFQEV157Xmdeg15TXnCDXqNek15XXkNeZZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI0NzI3Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBvXpNeo15nXk9eo15nXmiDXmdeV15zXoNeT15RkAgUPD2QWAh8JBUVqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzknLCfXk9eo15og15TXqdec15XXnScsJ9eg16nXqCcpO3JldHVybiBmYWxzZTsWAmYPFQEb15PXqNeaINeU16nXnNeV150gOSwg16DXqdeoZAIIDxUBCjAzLzAzLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC04NDc5MjIxZGQCJw9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTMzMzI1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxTXlyfXkNeY16gg157XodeZ16jXlGQCAw8PZBYCHwkFTmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnJywn16nXm9eV16DXqiDXp9eR15DXnNefJywn15HXmdeqINeSYNefJyk7cmV0dXJuIGZhbHNlOxYCZg8VASPXqdeb15XXoNeqINen15HXkNec158gINeR15nXqiDXkmDXn2QCBg8VAQoyNy8wMi8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDQtOTgwMzUzMWRkAigPZBYKZg8VARXXnteZ16DXlNecINeo16TXldeQ15lkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTc1MTInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoC9ee16Ug16LXqNefZAIFDw9kFgIfCQVRamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyMycsJ9ep16TXqNeZ16DXptenJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBJ9ep16TXqNeZ16DXptenIDIzLCDXqtecINeQ15HXmdeRLdeZ16TXlWQCCA8VAQoyNi8wMi8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDMtNjk3MDMxNmRkAikPZBYKZg8VAQjXodeb16jXqmQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNjgxNicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gQ15HXqNee15kg15In15XXn2QCAw8PZBYCHwkFVWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNjUnLCfXqdeTINeT16jXldedINeQ16TXqNeZ16fXlCcsJ9eQ16nXp9ec15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEr16nXkyDXk9eo15XXnSDXkNek16jXmden15QgNjUsINeQ16nXp9ec15XXn2QCBg8VAQoyNi8wMi8yMDE5ZAIHDxYCHwBnFgJmDw9kFgIfCQV1amF2YXNjcmlwdDpTZXRVcEFwcG9pbnQoJzQwJywn16nXkyDXk9eo15XXnSDXkNek16jXmden15QgNjUsINeQ16nXp9ec15XXnycsJzE2ODE2Jywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAIqD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAyMjgnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeRIteQF9eU15XXqNeV15HXmdelINeg16LXnteUZAIFDw9kFgIfCQVQamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxJywn16jXqdeRIteZJywn157XldeT15nXoteZ158g16LXmdec15nXqicpO3JldHVybiBmYWxzZTsWAmYPFQEm16jXqdeRIteZIDEsINee15XXk9eZ16LXmdefINei15nXnNeZ16pkAggPFQEKMjYvMDIvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTk3NzA2MDBkZAIrD2QWCmYPFQEd16nXqNeV16og16TXmdeW15nXldeq16jXpNeZ15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTUxMTEnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBNeS15EV15DXmNeg15zXldeRINem15nXnNeUZAIDDw9kFgIfCQVJamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc3Jywn157XoNeX150g15HXkteZ158nLCfXkNep15PXldeTJyk7cmV0dXJuIGZhbHNlOxYCZg8VAR/Xnteg15fXnSDXkdeS15nXnyA3LCDXkNep15PXldeTZAIGDxUBCjI1LzAyLzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOC04NTc5ODAwZGQCLA9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTU2OTUnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnE9ee15XXnNeZ15DXnyDXmdei15xkAgUPD2QWAh8JBUxqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzQnLCfXlNek15jXntefJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBIteU16TXmNee158gNCwg16rXnCDXkNeR15nXkS3Xmdek15VkAggPFQEKMjUvMDIvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTY5NzAzMDZkZAItD2QWCmYPFQEI16DXqdeZ151kAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTUyNjknKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoE9ek15DXldeW16DXqCDXk9eV15NkAgMPD2QWAh8JBUVqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE3Jywn16nXmNee16TXpNeoJywn16DXqteg15nXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEb16nXmNee16TXpNeoIDE3LCDXoNeq16DXmdeUZAIGDxUBCjI1LzAyLzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOS04NjA3MjIyZGQCLg9kFgpmDxUBD9ei15XXqCDXldee15nXn2QCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDYxMjInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoD9eh15cg157XldeX157Xk2QCBQ8PZBYCHwkFZmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnJywn15HXldei15nXmdeg15Qg16DXldeS15nXk9eQ16onLCfXkdeV16LXmdeZ16DXlC3XoNeV15Jg15nXk9eQJyk7cmV0dXJuIGZhbHNlOxYCZg8VATzXkdeV16LXmdeZ16DXlCDXoNeV15In15nXk9eQ16ogINeR15XXoteZ15nXoNeULdeg15XXkmDXmdeT15BkAggPFQEKMjEvMDIvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA0LTY3MzA4MTBkZAIvD2QWCmYPFQEb16fXnNeZ16DXkNeZ16og16rXp9ep15XXqNeqZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEwMDI0MScpO3JldHVybiBmYWxzZTsWAmYPFQIAF9ee15XXqdeZ15nXkSDXkNeZ16jXoNeUZAIDDw9kFgIfCQVKamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNCcsJ9eU15HXoNeZ150nLCfXlNeV15Mg15TXqdeo15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEg15TXkdeg15nXnSAxNCwg15TXldeTINeU16nXqNeV159kAgYPFQEKMTkvMDIvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA5LTc2MjE1MDBkZAIwD2QWCmYPFQEK15LXodeY16jXlWQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNDYwNzUnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoE9ep15vXmNeoINeo15XXlteg15RkAgUPD2QWAh8JBVJqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE0Jywn157XodeZ15zXqiDXmdep16jXmdedJywn15nXqNeV16nXnNeZ150nKTtyZXR1cm4gZmFsc2U7FgJmDxUBKNee16HXmdec16og15nXqdeo15nXnSAxNCwg15nXqNeV16nXnNeZ151kAggPFQEKMTcvMDIvMjAxOWQCCQ8WAh8AZxYCZg8PZBYCHwkFc2phdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCc0NycsJ9ee16HXmdec16og15nXqdeo15nXnSAxNCwg15nXqNeV16nXnNeZ150nLCcxNDYwNzUnLCfXqNeV16TXkCcsJ0ZhbHNlJyk7IHJldHVybiBmYWxzZTtkAjEPZBYKZg8VARHXnteZ15XXnyDXoNep15nXnWQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCc4MzQ5NicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gg16TXqNeZ15PXntefINeR16jXlNeV150g15jXnNeZ15RkAgMPD2QWAh8JBU5qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzEnLCfXmdeU15XXk9eUINeU15zXldeZJywn15HXoNeZINeR16jXpycpO3JldHVybiBmYWxzZTsWAmYPFQEk15nXlNeV15PXlCDXlNec15XXmSAxLCDXkdeg15kg15HXqNenZAIGDxUBCjE3LzAyLzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy01Nzc4NzAwZGQCMg9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjI4Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBfXlNeV16jXldeR15nXpSDXoNei157XlGQCBQ8PZBYCHwkFXmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNScsJ9eg16rXmdeR15XXqiDXlNee16nXpNeYJywn157XldeT15nXoteZ158g16LXmdec15nXqicpO3JldHVybiBmYWxzZTsWAmYPFQE016DXqteZ15HXldeqINeU157Xqdek15ggNSwg157XldeT15nXoteZ158g16LXmdec15nXqmQCCA8VAQoxMy8wMi8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDgtOTE0MjUwMGRkAjMPZBYKZg8VAQrXk9eZ15DXmNeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEwMDIzNCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15Ei15Ag16DXqtefINeS15XXnNeT16nXmNeZ15nXnyDXmdei15xkAgMPD2QWAh8JBVNqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzIwJywn16TXmdeg16og15TXqCLXnycsJ9eR15nXqiLXqCDXoteZ15zXmdeqJyk7cmV0dXJuIGZhbHNlOxYCZg8VASnXpNeZ16DXqiDXlNeoItefIDIwLCDXkdeZ16oi16gg16LXmdec15nXqmQCBg8VAQoxMy8wMi8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNTcyMzU1NWRkAjQPZBYKZg8VAQjXoNep15nXnWQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCczMDA4NicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gP157XlteV16gg15HXnNeUZAIFDw9kFgIfCQVLamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxMycsJ9ec15nXnNeZ16DXkdec15XXnScsJ9eg16rXoNeZ15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBIdec15nXnNeZ16DXkdec15XXnSAxMywg16DXqteg15nXlGQCCA8VAQoxMi8wMi8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDktODg5NjYwMGRkAjUPZBYKZg8VARXXnteo16TXkCDXkdei15nXodeV16dkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTk4NzMnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnDdeg15XXmSDXlNeT16FkAgMPD2QWAh8JBUhqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzEyJywn15LXny3Xkdeg15nXnteZ158nLCfXkNeZ15zXqicpO3JldHVybiBmYWxzZTsWAmYPFQEe15LXny3Xkdeg15nXnteZ158gMTIsINeQ15nXnNeqZAIGDxUBCjA3LzAyLzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOC02MzA2MjAwZGQCNg9kFgpmDxUBE9eo15XXp9eXINen15zXmdeg15lkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTk5OTE1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxnXkteo16nXmNeZ15nXnyDXkNeZ16jXoNeUZAIFDw9kFgIfCQVVamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyJywn15DXkdefINeS15HXmdeo15XXnCcsJ9eq15wg15DXkdeZ15Et15nXpNeVJyk7cmV0dXJuIGZhbHNlOxYCZg8VASvXkNeR158g15LXkdeZ16jXldecIDIsINeq15wg15DXkdeZ15Et15nXpNeVZAIIDxUBCjA3LzAyLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gIDA3Ni04ODg5NjAwZGQCNw9kFgpmDxUBCNeh15vXqNeqZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzE2ODE2Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBDXkdeo157XmSDXkifXldefZAIDDw9kFgIfCQVKamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc0NScsJ9ep15Mg15TXoNep15nXkCcsJ9eT15nXnteV16DXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEg16nXkyDXlNeg16nXmdeQIDQ1LCDXk9eZ157Xldeg15RkAgYPFQEKMDYvMDIvMjAxOWQCBw8WAh8AZxYCZg8PZBYCHwkFamphdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCc1NCcsJ9ep15Mg15TXoNep15nXkCA0NSwg15PXmdee15XXoNeUJywnMTY4MTYnLCfXqNeV16TXkCcsJ0ZhbHNlJyk7IHJldHVybiBmYWxzZTtkAjgPZBYKZg8VAR3Xqdeo15XXqiDXpNeZ15bXmdeV16rXqNek15nXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAyMzUnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeRIteQE9ep15XXldeZ15Mg16LXldeW15lkAgUPD2QWAh8JBUNqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzInLCfXkdeZ15wi15UnLCfXqNeX15XXkdeV16onKTtyZXR1cm4gZmFsc2U7FgJmDxUBGdeR15nXnCLXlSAyLCDXqNeX15XXkdeV16pkAggPFQEKMDUvMDIvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTkzNzc4MDBkZAI5D2QWCmYPFQEI16DXqdeZ151kAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzI5OTgnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoFdeT15XXqNek157XnyDXodeZ157XlGQCAw8PZBYCHwkFRGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnJywn15HXnyDXlteb15DXmScsJ9eY15HXqNeZ15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBGdeR158g15bXm9eQ15kgINeY15HXqNeZ15RkAgYPFQEKMDQvMDIvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA0LTY3MjcwMDBkZAI6D2QWCmYPFQEM16LXmdeg15nXmdedZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzM4MzE0Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBXXldeV15TXkdeZINeV15XXlNeR15lkAgUPD2QWAh8JBUVqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE3Jywn16nXmNee16TXpNeoJywn16DXqteg15nXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEb16nXmNee16TXpNeoIDE3LCDXoNeq16DXmdeUZAIIDxUBCjMxLzAxLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOS04NjA3MjIyZGQCOw9kFgpmDxUBEteQ15XXqNeq15XXpNeT15nXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMjIzMzInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoHteR15vXqCDXkNeR16DXmdeQ15zXmSDXoteZ16jXkGQCAw8PZBYCHwkFSGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNjInLCfXqdeTINeU16jXptecJywn15DXldek16fXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VAR7XqdeTINeU16jXptecIDYyLCDXkNeV16TXp9eZ151kAgYPFQEKMzAvMDEvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTkxMTk4ODhkZAI8D2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAyMTInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeRIteQEdep16gg15HXqNeV16jXmdeUZAIFDw9kFgIfCQVYamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyNicsJ9eQ15fXmdee15DXmdeoINeQ15HXkCcsJ9eq15wg15DXkdeZ15Et15nXpNeVJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS7XkNeX15nXnteQ15nXqCDXkNeR15AgMjYsINeq15wg15DXkdeZ15Et15nXpNeVZAIIDxUBCjI5LzAxLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy03NDUwNDAwZGQCPQ9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjM0Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkCDXoNeq158g15LXldec15PXqdeY15nXmdefINeZ16LXnGQCAw8PZBYCHwkFSmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMScsJ9eZ16LXp9eR15bXldefJywn15nXqNeV16nXnNeZ150nKTtyZXR1cm4gZmFsc2U7FgJmDxUBINeZ16LXp9eR15bXldefIDEsINeZ16jXldep15zXmdedZAIGDxUBCjI4LzAxLzIwMTlkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMi01MDE0NTU1ZGQCPg9kFgpmDxUBHdep16jXldeqINek15nXlteZ15XXqteo16TXmdeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEwMDE2NicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Ei15Ac16HXmNeV15nXpifXkSDXldec15PXmdee15nXqGQCBQ8PZBYCHwkFUmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMycsJ9ee15XXm9eo15kg15TXodeZ15LXqNeZ15XXqicsJ9eR16og15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEo157Xldeb16jXmSDXlNeh15nXkteo15nXldeqIDMsINeR16og15nXnWQCCA8VAQoyNy8wMS8yMDE5ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDMtNTU1NjkwMGRkAj8PZBYKZg8VAQjXoNep15nXnWQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDA4MDYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoE9ek15XXldeQ15Yg15zXkNek15lkAgMPD2QWAh8JBUxqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzEnLCfXoNeU16gg15TXmdeo15PXnycsJ9eR15nXqiDXqdee16knKTtyZXR1cm4gZmFsc2U7FgJmDxUBIteg15TXqCDXlNeZ16jXk9efIDEsINeR15nXqiDXqdee16lkAgYPFQEKMjQvMDEvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTYzMjU1MDBkZAJAD2QWCmYPFQEU16TXodeZ15vXldeq16jXpNeZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnNjE5OTA3Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxnXqNeV15bXoNeR16jXkiDXpteV16TXmdeUZAIFDw9kFgIfCQVQamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxJywn16jXqdeRIteZJywn157XldeT15nXoteZ158g16LXmdec15nXqicpO3JldHVybiBmYWxzZTsWAmYPFQEm16jXqdeRIteZIDEsINee15XXk9eZ16LXmdefINei15nXnNeZ16pkAggPFQEKMjQvMDEvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTk3NzA2MDBkZAJBD2QWCmYPFQEd16nXqNeV16og16TXmdeW15nXldeq16jXpNeZ15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTU0NjAnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBNee16gV15DXkteR16jXmdeUINeQ15fXnteTZAIDDw9kFgIfCQVFamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNycsJ9ep15jXntek16TXqCcsJ9eg16rXoNeZ15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBG9ep15jXntek16TXqCAxNywg16DXqteg15nXlGQCBg8VAQoyMy8wMS8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDktODYwNzIyMmRkAkIPZBYKZg8VAQjXoNep15nXnWQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCc5NTQ4OScpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gf16LXkNee16gg15DXnNep15nXpyDXkifXldeg15nXlGQCBQ8PZBYCHwkFTWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTAnLCfXqdee16LXldefINeR158g16nXmNeXJywn15DXnNei15MnKTtyZXR1cm4gZmFsc2U7FgJmDxUBI9ep157XoteV158g15HXnyDXqdeY15cgMTAsINeQ15zXoteTZAIIDxUBCjIzLzAxLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy05MDgzODAwZGQCQw9kFgpmDxUBG9eo16TXldeQ15Qg16rXoteh15XXp9eq15nXqmQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCc4MzI1NycpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gR15LXnNeV15bXntefINec15FkAgMPD2QWAh8JBUFqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzcxJywn15vXoNeo16onLCfXkNep15PXldeTJyk7cmV0dXJuIGZhbHNlOxYCZg8VARfXm9eg16jXqiA3MSwg15DXqdeT15XXk2QCBg8VAQoyMi8wMS8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDgtODY4NzYwMGRkAkQPZBYKZg8VAQrXqNeZ15DXldeqZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI3NjgzJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBfXpNeV15zXmdeQ16fXldeRINeg15zXmWQCBQ8PZBYCHwkFQWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNzEnLCfXm9eg16jXqicsJ9eQ16nXk9eV15MnKTtyZXR1cm4gZmFsc2U7FgJmDxUBF9eb16DXqNeqIDcxLCDXkNep15PXldeTZAIIDxUBCjIyLzAxLzIwMTlkAgkPFgIfAGcWAmYPD2QWAh8JBWFqYXZhc2NyaXB0OlNldFVwQXBwb2ludCgnNjcnLCfXm9eg16jXqiA3MSwg15DXqdeT15XXkycsJzI3NjgzJywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAJFD2QWCmYPFQEK16jXmdeQ15XXqmQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNDQ1MScpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gR15HXqNeZ16cg15DXqNeZ15RkAgMPD2QWAh8JBT5qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzInLCfXl9eV16jXmScsJ9eX15nXpNeUJyk7cmV0dXJuIGZhbHNlOxYCZg8VARTXl9eV16jXmSAyLCDXl9eZ16TXlGQCBg8VAQoyMi8wMS8yMDE5ZAIHDxYCHwBnFgJmDw9kFgIfCQVeamF2YXNjcmlwdDpTZXRVcEFwcG9pbnQoJzY4Jywn15fXldeo15kgMiwg15fXmdek15QnLCcxNDQ1MScsJ9eo15XXpNeQJywnRmFsc2UnKTsgcmV0dXJuIGZhbHNlO2QCRg9kFgpmDxUBFdee16jXpNeQINeR16LXmdeh15XXp2QCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNDg4NzUnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnEdeU16jXmNecINeg15nXptefZAIFDw9kFgIfCQVWamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc3NycsJ9ep15Mg16nXkNeV15wg16LXnteV16gnLCfXnteS15PXnCDXlNei157XpycpO3JldHVybiBmYWxzZTsWAmYPFQEs16nXkyDXqdeQ15XXnCDXotee15XXqCA3Nywg157XkteT15wg15TXotee16dkAggPFQEKMjEvMDEvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA0LTYwNDk2MDBkZAJHD2QWCmYPFQEU16TXodeZ15vXldeq16jXpNeZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjMxJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXniLXkBXXkNeR16jXnteh15XXnyDXmdei15xkAgMPD2QWAh8JBWFqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzI5Jywn15LXmdeg16og15HXmdeqINep15DXnycsJ9ee15XXk9eZ16LXmdefLdee15vXkdeZ150t16gnKTtyZXR1cm4gZmFsc2U7FgJmDxUBN9eS15nXoNeqINeR15nXqiDXqdeQ158gMjksINee15XXk9eZ16LXmdefLdee15vXkdeZ150t16hkAgYPFQEKMTcvMDEvMjAxOWQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA4LTk3MzUyMjJkZAJID2QWCmYPFQEb16LXkdeV15PXlCDXodeV16bXmdeQ15zXmdeqZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI5MDc0MCcpO3JldHVybiBmYWxzZTsWAmYPFQIE157XqBHXodei15nXkyDXqNek15nXp2QCBQ8PZBYCHwkFTmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMScsJ9eg15XXqCDXkNecINeT15nXnycsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VASTXoNeV16gg15DXnCDXk9eZ158gMSwg15nXqNeV16nXnNeZ151kAggPFQEKMTYvMDEvMjAxOWQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTY0ODMxMjNkZAJJD2QWCmYPFQEI16DXqdeZ151kAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzUwNjUnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoFdeo15DXkdeZINeb15nXpNeQ15nXlGQCAw8PZBYCHwkFW2phdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMjQnLCfXnteX157XldeTINeT16jXldeZ16knLCfXmNeZ15nXkdeUINeR157XqdeV15zXqScpO3JldHVybiBmYWxzZTsWAmYPFQEx157Xl9ee15XXkyDXk9eo15XXmdepIDI0LCDXmNeZ15nXkdeUINeR157XqdeV15zXqWQCBg8VAQoxNS8wMS8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDktNzk5MjE0NGRkAkoPZBYKZg8VAQrXk9eZ15DXmNeUZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk1MDYyJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXniLXkA3Xoden15wg15fXoNeZZAIFDw9kFgIfCQVSamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc1OCcsJ9eU16jXkSDXqdeQ15XXnNeW15XXnycsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VASjXlNeo15Eg16nXkNeV15zXlteV158gNTgsINeZ16jXldep15zXmdedZAIIDxUBCjE0LzAxLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMi02NTExNjAyZGQCSw9kFgpmDxUBFdee15nXoNeU15wg16jXpNeV15DXmWQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcyOTA5OCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gV157XqNeW15XXnyDXmdeR15LXoNeZZAIDDw9kFgIfCQVRamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyMycsJ9ep16TXqNeZ16DXptenJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBJ9ep16TXqNeZ16DXptenIDIzLCDXqtecINeQ15HXmdeRLdeZ16TXlWQCBg8VAQowOC8wMS8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDMtNjk3MDMxNmRkAkwPZBYKZg8VARTXpNeh15nXm9eV16rXqNek15nXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAyMjcnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeeIteQFteo15DXldeR16DXmSDXkifXldec15lkAgUPD2QWAh8JBU5qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzMnLCfXmNeo15XXntek15zXk9eV16gnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEk15jXqNeV157XpNec15PXldeoIDMsINeZ16jXldep15zXmdedZAIIDxUBCjA3LzAxLzIwMTlkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMi01Njc1MTExZGQCTQ9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjE3Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBnXkNec15jXqNeV15HXmdelINeU15nXnNeUZAIDDw9kFgIfCQVRamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyMicsJ9eT16jXmiDXoteb15UnLCfXp9eo15nXqiDXkdeZ15DXnNeZ16cnKTtyZXR1cm4gZmFsc2U7FgJmDxUBJ9eT16jXmiDXoteb15UgMjIsINen16jXmdeqINeR15nXkNec15nXp2QCBg8VAQowMy8wMS8yMDE5ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDQtOTUwNTUwMGRkAk4PZBYKZg8VAQzXpNeV16jXmdeV16pkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnODA0NDAnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoC9eR15vXqCDXk9eRZAIFDw9kFgIfCQVMamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc0Jywn15TXpNeY157XnycsJ9eq15wg15DXkdeZ15Et15nXpNeVJyk7cmV0dXJuIGZhbHNlOxYCZg8VASLXlNek15jXntefIDQsINeq15wg15DXkdeZ15Et15nXpNeVZAIIDxUBCjI2LzEyLzIwMThkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy02OTcwMzA2ZGQCTw9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMjE1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBXXkNec16bXoNeiINeQ15nXnteQ159kAgMPD2QWAh8JBUZqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJycsJ9ee16jXm9eWINee16HXl9eo15knLCfXqNeU15gnKTtyZXR1cm4gZmFsc2U7FgJmDxUBG9ee16jXm9eWINee16HXl9eo15kgINeo15TXmGQCBg8VAQoyNi8xMi8yMDE4ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDgtNjgwNzgwMGRkAlAPZBYKZg8VAQrXk9eZ15DXmNeUZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk1MTgzJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxvXktec15XXqdeY15nXmdefINeT15XXqNeZ159kAgUPD2QWAh8JBUhqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJycsJ9eTLtegLiDXkNek16jXmdeZ150nLCfXqdeZ15zXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEd15Mu16AuINeQ16TXqNeZ15nXnSAg16nXmdec15RkAggPFQEKMjYvMTIvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTk5NDEyOTNkZAJRD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCc5OTg4MCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15LXkScX15HXldeo15XXkdeZ16cg15LXmdec15lkAgMPD2QWAh8JBUpqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzY2Jywn16nXkyDXqNeV15jXqdeZ15zXkycsJ9eX15PXqNeUJyk7cmV0dXJuIGZhbHNlOxYCZg8VASDXqdeTINeo15XXmNep15nXnNeTIDY2LCDXl9eT16jXlGQCBg8VAQoyNS8xMi8yMDE4ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDQtNjEyNjYwMGRkAlIPZBYKZg8VAR3XoNeV15nXqNeV15zXldeS15nXlCDXmdec15PXmWQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCczMjg4NCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gl15TXnNeo157XnyDXmNeV16HXpyDXnNeZ15DXqiAo15zXkNeUKWQCBQ8PZBYCHwkFUWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNycsJ9eo15XXmNep15nXnNeTJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VASfXqNeV15jXqdeZ15zXkyA3LCDXqNeQ16nXldefINec16bXmdeV159kAggPFQEKMjAvMTIvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTk1OTc4MDBkZAJTD2QWCmYPFQES15DXldeo16rXldek15PXmdeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzExNzA3MicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gR16nXkteZ15Ag16LXnteZ16pkAgMPD2QWAh8JBUhqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzYyJywn16nXkyDXlNeo16bXnCcsJ9eQ15XXpNen15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEe16nXkyDXlNeo16bXnCA2Miwg15DXldek16fXmdedZAIGDxUBCjE3LzEyLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOC05MTE5ODg4ZGQCVA9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTU4MDknKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnFNeR16gg15bXmdeVINeg15XXoteUZAIFDw9kFgIfCQVYamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxJywn16nXk9eo15XXqiDXlNeZ15XXkdecJywn15nXp9eg16LXnSDXoteZ15zXmdeqJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS7XqdeT16jXldeqINeU15nXldeR15wgMSwg15nXp9eg16LXnSDXoteZ15zXmdeqZAIIDxUBCjE3LzEyLzIwMThkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC02NzczNzAwZGQCVQ9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMDI2Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBXXkNec16DXp9eo15kg15nXpNei16pkAgMPD2QWAh8JBU5qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzMnLCfXmNeo15XXntek15zXk9eV16gnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEk15jXqNeV157XpNec15PXldeoIDMsINeZ16jXldep15zXmdedZAIGDxUBCjE2LzEyLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMi01Njc1MTExZGQCVg9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnOTUwNjInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeeIteQDdeh16fXnCDXl9eg15lkAgUPD2QWAh8JBVBqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzYnLCfXqdeTINeU15fXldep158nLCfXnteR16nXqNeqINem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VASbXqdeTINeU15fXldep158gNiwg157Xkdep16jXqiDXpteZ15XXn2QCCA8VAQoxMy8xMi8yMDE4ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNTMzMDI0NGRkAlcPZBYKZg8VARXXnteZ16DXlNecINeo16TXldeQ15lkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTgxNTgnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoE9eZ16DXmdeRINee16jXk9eb15lkAgMPD2QWAh8JBVdqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzQnLCfXp9eg15nXldefINeU16DXkteRINen15XXnteUJywn15HXkNeoINep15HXoicpO3JldHVybiBmYWxzZTsWAmYPFQEt16fXoNeZ15XXnyDXlNeg15LXkSDXp9eV157XlCA0LCDXkdeQ16gg16nXkdeiZAIGDxUBCjEwLzEyLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOC02MjAzNjAwZGQCWA9kFgpmDxUBFNek16HXmdeb15XXqteo16TXmdeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEwMDIxMScpO3JldHVybiBmYWxzZTsWAmYPFQIF154i15AY16TXnNeT157XnyDXpNeo15kg15DXoNeUZAIFDw9kFgIfCQVWamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc3NycsJ9ep15Mg16nXkNeV15wg16LXnteV16gnLCfXnteS15PXnCDXlNei157XpycpO3JldHVybiBmYWxzZTsWAmYPFQEs16nXkyDXqdeQ15XXnCDXotee15XXqCA3Nywg157XkteT15wg15TXotee16dkAggPFQEKMDYvMTIvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA0LTYwNDk2MDBkZAJZD2QWCmYPFQEK16jXmdeQ15XXqmQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCczMjE1OCcpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gZ15DXpNeg16HXmdeZ15Eg16TXkNeZ16DXlGQCAw8PZBYCHwkFRWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTcnLCfXqdeY157XpNek16gnLCfXoNeq16DXmdeUJyk7cmV0dXJuIGZhbHNlOxYCZg8VARvXqdeY157XpNek16ggMTcsINeg16rXoNeZ15RkAgYPFQEKMDQvMTIvMjAxOGQCBw8WAh8AZxYCZg8PZBYCHwkFZWphdmFzY3JpcHQ6U2V0VXBBcHBvaW50KCc4OCcsJ9ep15jXntek16TXqCAxNywg16DXqteg15nXlCcsJzMyMTU4Jywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAJaD2QWCmYPFQEM16LXmdeg15nXmdedZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI0NzI3Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBvXpNeo15nXk9eo15nXmiDXmdeV15zXoNeT15RkAgUPD2QWAh8JBUVqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzknLCfXk9eo15og15TXqdec15XXnScsJ9eg16nXqCcpO3JldHVybiBmYWxzZTsWAmYPFQEb15PXqNeaINeU16nXnNeV150gOSwg16DXqdeoZAIIDxUBCjAyLzEyLzIwMThkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC04NDc5MjIxZGQCWw9kFgpmDxUBFNek16HXmdeb15XXqteo16TXmdeUZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk5OTkwMycpO3JldHVybiBmYWxzZTsWAmYPFQIE157XqBPXnteY15zXldefINeZ15XXkNeRZAIDDw9kFgIfCQVeamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcnLCfXkdeW15wgMzUg16jXldeg15nXqiDXqNek15DXnCcsJ9eq15wg15DXkdeZ15Et15nXpNeVJyk7cmV0dXJuIGZhbHNlOxYCZg8VATPXkdeW15wgMzUg16jXldeg15nXqiDXqNek15DXnCAg16rXnCDXkNeR15nXkS3Xmdek15VkAgYPFQEKMjgvMTEvMjAxOGQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTYwMTI2MDBkZAJcD2QWCmYPFQEM16LXmdeg15nXmdedZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzExNzE4NycpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gV15bXmdeq15DXldeZINeQ157XkteTZAIFDw9kFgIfCQVcamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxMDAnLCfXnteX16DXlCDXpNec15nXmNeZ150g16nXotek15gnLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEy157Xl9eg15Qg16TXnNeZ15jXmdedINep16LXpNeYIDEwMCwg15nXqNeV16nXnNeZ151kAggPFQEKMjcvMTEvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTUwOTA0NDhkZAJdD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAyMTInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeRIteQEdep16gg15HXqNeV16jXmdeUZAIDDw9kFgIfCQVGamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc3NicsJ9eR15nXkNec15nXpycsJ9eo157XqiDXktefJyk7cmV0dXJuIGZhbHNlOxYCZg8VARzXkdeZ15DXnNeZ16cgNzYsINeo157XqiDXktefZAIGDxUBCjI3LzExLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwMy02NzU2ODg4ZGQCXg9kFgpmDxUBCNeg16nXmdedZAIBDw9kFgIfCQVBamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzEzMDM3MycpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gV16fXmdeY16jXldeh16gg15DXnNeUZAIFDw9kFgIfCQVNamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc2Jywn15TXqNeRINeW15XXldeZ158nLCfXmdeo15XXqdec15nXnScpO3JldHVybiBmYWxzZTsWAmYPFQEj15TXqNeRINeW15XXldeZ158gNiwg15nXqNeV16nXnNeZ151kAggPFQEKMjYvMTEvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTU4NDM4ODhkZAJfD2QWCmYPFQEU16TXodeZ15vXmdeQ15jXqNeZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTE5ODQ1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBPXkNeo15LXlSDXk9eg15nXkNecZAIDDw9kFgIfCQVOamF2YXNjcmlwdDpzZW5kVG9FbWFwKCczJywn15jXqNeV157XpNec15PXldeoJywn15nXqNeV16nXnNeZ150nKTtyZXR1cm4gZmFsc2U7FgJmDxUBJNeY16jXldee16TXnNeT15XXqCAzLCDXmdeo15XXqdec15nXnWQCBg8VAQoxOS8xMS8yMDE4ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNTY3NTExMWRkAmAPZBYKZg8VARLXkNeV16jXldec15XXkteZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTE0NTg0Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBDXkdeoINeQ15wg15DXodejZAIFDw9kFgIfCQVFamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxMycsJ9eU16jXpteV15InLCfXlNeo16bXnNeZ15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBG9eU16jXpteV15IgMTMsINeU16jXptec15nXlGQCCA8VAQoxOS8xMS8yMDE4ZAIJDxYCHwBnFgJmDw9kFgIfCQVmamF2YXNjcmlwdDpTZXRVcEFwcG9pbnQoJzk1Jywn15TXqNem15XXkiAxMywg15TXqNem15zXmdeUJywnMTE0NTg0Jywn16jXldek15AnLCdGYWxzZScpOyByZXR1cm4gZmFsc2U7ZAJhD2QWCmYPFQEV157Xmdeg15TXnCDXqNek15XXkNeZZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzMzNTg3Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBPXpNeV16HXntefINeo16DXmNeVZAIDDw9kFgIfCQVKamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxOScsJ9eZ15Mg15fXqNeV16bXmdedJywn16DXqteg15nXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEg15nXkyDXl9eo15XXpteZ150gMTksINeg16rXoNeZ15RkAgYPFQEKMTgvMTEvMjAxOGQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA5LTg5MjYzMDBkZAJiD2QWCmYPFQER157XmdeV158g16DXqdeZ151kAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTA3Nzc1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBHXqtee15nXqCDXlteZ15XXlGQCBQ8PZBYCHwkFTmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMScsJ9eZ15TXldeT15Qg15TXnNeV15knLCfXkdeg15kg15HXqNenJyk7cmV0dXJuIGZhbHNlOxYCZg8VASTXmdeU15XXk9eUINeU15zXldeZIDEsINeR16DXmSDXkdeo16dkAggPFQEKMTgvMTEvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTU3Nzg3MDBkZAJjD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxODgzNjYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnE9eh15HXmdeV158g16DXkNeV15RkAgMPD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzgxJywn15XXoteTINeQ16jXkdeiINeU15DXqNem15XXqicsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/Xldei15Mg15DXqNeR16Ig15TXkNeo16bXldeqIDgxLCDXmdeo15XXqdec15nXnWQCBg8VAQoxMy8xMS8yMDE4ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItNTg4MDgwMGRkAmQPZBYKZg8VAR3XqNeQ15XXnteY15XXnNeV15LXmdeUL9ee16TXqGQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcyODkzOScpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gT16HXpNeV16jXmSDXoteQ157XqGQCBQ8PZBYCHwkFPmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMicsJ9eb15XXqNeZJywn15fXmdek15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBFNeb15XXqNeZIDIsINeX15nXpNeUZAIIDxUBCjEyLzExLzIwMThkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC04Njg2ODAwZGQCZQ9kFgpmDxUBEteU157XmNeV15zXldeS15nXlGQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxNjkxMicpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gX15HXqNeQ16nXmNeoINeQ16DXk9eo15lkAgMPD2QWAh8JBT5qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzInLCfXl9eV16jXmScsJ9eX15nXpNeUJyk7cmV0dXJuIGZhbHNlOxYCZg8VARTXl9eV16jXmSAyLCDXl9eZ16TXlGQCBg8VAQoxMi8xMS8yMDE4ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDQtODY4NjgwMGRkAmYPZBYKZg8VARLXkNeV16jXqteV16TXk9eZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAzMjM3Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBzXkNeZ15XXkSDXoteR15PXkNecINei15bXmdedZAIFDw9kFgIfCQU+amF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyJywn15vXldeo15knLCfXl9eZ16TXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEU15vXldeo15kgMiwg15fXmdek15RkAggPFQEKMTIvMTEvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDA0LTg2ODY4MDBkZAJnD2QWCmYPFQEI15Au15Au15JkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTE5NDA4Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBLXnteXJ9eV15wg15DXmdee159kAgMPD2QWAh8JBT5qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzInLCfXm9eV16jXmScsJ9eX15nXpNeUJyk7cmV0dXJuIGZhbHNlOxYCZg8VARTXm9eV16jXmSAyLCDXl9eZ16TXlGQCBg8VAQoxMi8xMS8yMDE4ZAIJDxYCHwBnFgICAQ8PFgIfAQUNICAgMDQtODY4NjgwMGRkAmgPZBYKZg8VARLXkNeV16jXldec15XXkteZ15RkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTk2MjYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoFdeR16jXkdeQ16jXlCDXmdeV16HXo2QCBQ8PZBYCHwkFPmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMicsJ9eX15XXqNeZJywn15fXmdek15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBFNeX15XXqNeZIDIsINeX15nXpNeUZAIIDxUBCjEyLzExLzIwMThkAgkPFgIfAGcWAmYPD2QWAh8JBV9qYXZhc2NyaXB0OlNldFVwQXBwb2ludCgnMTAzJywn15fXldeo15kgMiwg15fXmdek15QnLCcxOTYyNicsJ9eo15XXpNeQJywnRmFsc2UnKTsgcmV0dXJuIGZhbHNlO2QCaQ9kFgpmDxUBG9en15zXmdeg15DXmdeqINeq16fXqdeV16jXqmQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxMDAxNzYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnEdei157XkNepINeo16DXmdefZAIDDw9kFgIfCQVTamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcnLCfXkteh16gg15DXlteo16fXkCcsJ9eSYNeh15DXqCDXkC3Xlteo16fXkCcpO3JldHVybiBmYWxzZTsWAmYPFQEq15In16HXqCDXkCfXlteo16fXkCAg15Jg16HXkNeoINeQLdeW16jXp9eQZAIGDxUBCjEyLzExLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC02MjYxNDQxZGQCag9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTAwMTk5Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkSLXkBfXkNeR15Ug15jXmdeoINeXJ9ec15XXk2QCBQ8PZBYCHwkFSmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnJywn16bXldeoINeR15DXl9eoJywn15nXqNeV16nXnNeZ150nKTtyZXR1cm4gZmFsc2U7FgJmDxUBH9em15XXqCDXkdeQ15fXqCAg15nXqNeV16nXnNeZ151kAggPFQEKMDgvMTEvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTY0NDIzMDBkZAJrD2QWCmYPFQEV157Xmdeg15TXnCDXqNek15XXkNeZZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzI1NzI1Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqCHXkteV16jXmdeQ16DXldeRINeQ15zXm9eh16DXk9eo15RkAgMPD2QWAh8JBVVqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzInLCfXkNeR158g15LXkdeZ16jXldecJywn16rXnCDXkNeR15nXkS3Xmdek15UnKTtyZXR1cm4gZmFsc2U7FgJmDxUBK9eQ15HXnyDXkteR15nXqNeV15wgMiwg16rXnCDXkNeR15nXkS3Xmdek15VkAgYPFQEKMDUvMTEvMjAxOGQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgMDc2LTg4ODk2MDBkZAJsD2QWCmYPFQEK15PXmdeQ15jXlGQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcxODgzNjYnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeS15EnE9eh15HXmdeV158g16DXkNeV15RkAgUPD2QWAh8JBVNqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzYnLCfXqdeTINeg15fXnNeqINem15DXnNeZ150nLCfXkdeZ16og16nXntepJyk7cmV0dXJuIGZhbHNlOxYCZg8VASnXqdeTINeg15fXnNeqINem15DXnNeZ150gNiwg15HXmdeqINep157XqWQCCA8VAQoyOS8xMC8yMDE4ZAILDxYCHwBnFgICAQ8PFgIfAQUNICAgMDItOTY2OTQ0NGRkAm0PZBYKZg8VAQrXk9eZ15DXmNeUZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzk1MTgzJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxvXktec15XXqdeY15nXmdefINeT15XXqNeZ159kAgMPD2QWAh8JBVRqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJycsJ9eTLtegLiDXnteW16jXlyDXkdeg15nXnteZ158nLCfXkdeZ16og15DXnCcpO3JldHVybiBmYWxzZTsWAmYPFQEp15Mu16AuINee15bXqNeXINeR16DXmdee15nXnyAg15HXmdeqINeQ15xkAgYPFQEKMjUvMTAvMjAxOGQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTk5NzMzMjlkZAJuD2QWCmYPFQEM16LXmdeg15nXmdedZAIBDw9kFgIfCQVAamF2YXNjcmlwdDpTZXRTZWFyY2hBcHBvaW50c0RvY3RvcmRldGFpbHMoJzg3NTE4Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBXXp9eo157Xldeg15Qg15zXmdec15lkAgUPD2QWAh8JBUZqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzc2Jywn15HXmdeQ15zXmdenJywn16jXnteqINeS158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBHNeR15nXkNec15nXpyA3Niwg16jXnteqINeS159kAggPFQEKMjUvMTAvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAzLTY3NTY4ODhkZAJvD2QWCmYPFQEI16DXqdeZ151kAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTA3MzM0Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBXXoteR15DXoSDXmdeQ16HXnteZ159kAgMPD2QWAh8JBT9qYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE2Jywn16HXnNeQ150nLCfXoNem16jXqicpO3JldHVybiBmYWxzZTsWAmYPFQEV16HXnNeQ150gMTYsINeg16bXqNeqZAIGDxUBCjI0LzEwLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC02MDM2NjAwZGQCcA9kFgpmDxUBHNeQ16DXk9eV16fXqNeZ16DXldec15XXkteZ15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTE0OTIwJyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkyLXqBnXnNeZ16nXkdeZ16Ug15LXkdeo15nXkNecZAIFDw9kFgIfCQVSamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNCcsJ9ee16HXmdec16og15nXqdeo15nXnScsJ9eZ16jXldep15zXmdedJyk7cmV0dXJuIGZhbHNlOxYCZg8VASjXnteh15nXnNeqINeZ16nXqNeZ150gMTQsINeZ16jXldep15zXmdedZAIIDxUBCjI0LzEwLzIwMThkAgkPFgIfAGcWAmYPD2QWAh8JBXRqYXZhc2NyaXB0OlNldFVwQXBwb2ludCgnMTExJywn157XodeZ15zXqiDXmdep16jXmdedIDE0LCDXmdeo15XXqdec15nXnScsJzExNDkyMCcsJ9eo15XXpNeQJywnRmFsc2UnKTsgcmV0dXJuIGZhbHNlO2QCcQ9kFgpmDxUBFdee15nXoNeU15wg16jXpNeV15DXmWQCAQ8PZBYCHwkFQGphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCcyMDA3OScpO3JldHVybiBmYWxzZTsWAmYPFQIF15Mi16gb15zXmdeR15XXkdeZ16Ug15nXldec15nXkNefZAIDDw9kFgIfCQVVamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyJywn15DXkdefINeS15HXmdeo15XXnCcsJ9eq15wg15DXkdeZ15Et15nXpNeVJyk7cmV0dXJuIGZhbHNlOxYCZg8VASvXkNeR158g15LXkdeZ16jXldecIDIsINeq15wg15DXkdeZ15Et15nXpNeVZAIGDxUBCjIzLzEwLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gIDA3Ni04ODg5NjAwZGQCcg9kFgpmDxUBG9ei15HXldeT15Qg16HXldem15nXkNec15nXqmQCAQ8PZBYCHwkFQWphdmFzY3JpcHQ6U2V0U2VhcmNoQXBwb2ludHNEb2N0b3JkZXRhaWxzKCc5MjM5NTUnKTtyZXR1cm4gZmFsc2U7FgJmDxUCB9ei15Ui16ET15XXp9eh157XnyDXoNei157XlGQCBQ8PZBYCHwkFSGphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnNicsJ9eR16gg15DXmdec158nLCfXkdeZ16og16nXntepJyk7cmV0dXJuIGZhbHNlOxYCZg8VAR7XkdeoINeQ15nXnNefIDYsINeR15nXqiDXqdee16lkAggPFQEKMjIvMTAvMjAxOGQCCw8WAh8AZxYCAgEPDxYCHwEFDSAgIDAyLTk5MDY3NzdkZAJzD2QWCmYPFQEd16jXkNeV157XmNeV15zXldeS15nXlC/Xntek16hkAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzQ1NDEnKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoEdeR15XXqNeV16cg16nXqNeUZAIDDw9kFgIfCQVKamF2YXNjcmlwdDpzZW5kVG9FbWFwKCc2NicsJ9ep15Mg16jXldeY16nXmdec15MnLCfXl9eT16jXlCcpO3JldHVybiBmYWxzZTsWAmYPFQEg16nXkyDXqNeV15jXqdeZ15zXkyA2Niwg15fXk9eo15RkAgYPFQEKMjIvMTAvMjAxOGQCCQ8WAh8AZxYCAgEPDxYCHwEFDSAgIDA0LTYxMjY2MDBkZAJ0D2QWCmYPFQEd15DXldeo16rXldek15PXmdeqINeZ15zXk9eZ151kAgEPD2QWAh8JBUBqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMzM0OTInKTtyZXR1cm4gZmFsc2U7FgJmDxUCBdeTIteoEteXJ9eY15nXkSDXmdeQ16HXqGQCBQ8PZBYCHwkFPmphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMicsJ9eb15XXqNeZJywn15fXmdek15QnKTtyZXR1cm4gZmFsc2U7FgJmDxUBFNeb15XXqNeZIDIsINeX15nXpNeUZAIIDxUBCjIxLzEwLzIwMThkAgsPFgIfAGcWAgIBDw8WAh8BBQ0gICAwNC04Njg2ODAwZGQCdQ9kFgpmDxUBCteT15nXkNeY15RkAgEPD2QWAh8JBUFqYXZhc2NyaXB0OlNldFNlYXJjaEFwcG9pbnRzRG9jdG9yZGV0YWlscygnMTg4MzY2Jyk7cmV0dXJuIGZhbHNlOxYCZg8VAgXXkteRJxPXodeR15nXldefINeg15DXldeUZAIDDw9kFgIfCQVhamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcyOScsJ9eS15nXoNeqINeR15nXqiDXqdeQ158nLCfXnteV15PXmdei15nXny3Xnteb15HXmdedLdeoJyk7cmV0dXJuIGZhbHNlOxYCZg8VATfXkteZ16DXqiDXkdeZ16og16nXkNefIDI5LCDXnteV15PXmdei15nXny3Xnteb15HXmdedLdeoZAIGDxUBCjIxLzEwLzIwMThkAgkPFgIfAGcWAgIBDw8WAh8BBQ0gICAwOC05NzM1MjIyZGQCEQ8WAh4FVmFsdWUFbF9fZG9Qb3N0QmFjaygnY3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkUXVpY2tBcHBvaW50bWVudHMkU2V0RmFzdEFwcG9pbnRCb3R0dW4nLCcnKWQCEw8WAh8cBW9fX2RvUG9zdEJhY2soJ2N0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyJGZpcnN0VGFiJFF1aWNrQXBwb2ludG1lbnRzJFNldEFwcG9udEZvckFsbEJyYW5jaGVzJywnJylkAgcPDxYGHglTZWFyY2hPYmoyxxEAAQAAAP////8BAAAAAAAAAAwCAAAAPUNvbW1vbiwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAAChDb21tb24uQ2xhc3MuU2VhcmNoQXBwb2ludG1lbnRQYXJhbWV0ZXJzFAAAABc8SURLaW5kPmtfX0JhY2tpbmdGaWVsZBc8VXNlcklEPmtfX0JhY2tpbmdGaWVsZBc8Q2l0aWVzPmtfX0JhY2tpbmdGaWVsZB88U3BlY2lhbGl6YXRpb24+a19fQmFja2luZ0ZpZWxkGzxEb2N0b3JOYW1lPmtfX0JhY2tpbmdGaWVsZB08RG9jdG9yTnVtYmVyPmtfX0JhY2tpbmdGaWVsZBk8QnJhbmNoZXM+a19fQmFja2luZ0ZpZWxkJTxGYXN0U2VhcmNoQnJhbmNoQ29kZT5rX19CYWNraW5nRmllbGQZPEZyb21EYXRlPmtfX0JhY2tpbmdGaWVsZBc8VG9EYXRlPmtfX0JhY2tpbmdGaWVsZBk8RnJvbUhvdXI+a19fQmFja2luZ0ZpZWxkFzxUb0hvdXI+a19fQmFja2luZ0ZpZWxkGzxEYXlzSW5XZWVrPmtfX0JhY2tpbmdGaWVsZB08SXNGYXN0U2VhcmNoPmtfX0JhY2tpbmdGaWVsZCQ8SXNGcm9tRmFzdFNlYXJjaEJveD5rX19CYWNraW5nRmllbGQZPENhbGxUeXBlPmtfX0JhY2tpbmdGaWVsZB08TG9jYXRpb25GbGFnPmtfX0JhY2tpbmdGaWVsZCo8SXNTZWFyY2hGb3JTYW1wbGluZ0NlbnRlcj5rX19CYWNraW5nRmllbGQgPEFwcG9pbnRtZW50VHlwZT5rX19CYWNraW5nRmllbGRbPElzTmVlZFRvT3BlblBvcHVwSW5kaWNhdGlvblRoYXRQZXJzb25hbERvY3RvclN0YXJ0VGVsZW1lZGljaW5lQXBwb2ludG1lbnRzPmtfX0JhY2tpbmdGaWVsZAEBAwQBAQMBAQEBAQEAAAQEAAMAiAFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW0NvbW1vbi5Eb2N0b3JBcHBvaW50bWVudHMuRG9jdG9yQ2l0eSwgQ29tbW9uLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbF1dDENvbW1vbi5UdXBsZQIAAABwU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tDb21tb24uVHVwbGUsIENvbW1vbiwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGxdXQEBJkNvbW1vbi5FbnVtcytTZWFyY2hBcHBvaW50bWVudENhbGxUeXBlAgAAACpDb21tb24uRW51bXMrU2VhcmNoQXBwb2ludG1lbnRMb2NhdGlvbkZsdWcCAAAAARBTeXN0ZW0uRGVjaW1hbFtdAQIAAAAGAwAAAAExBgQAAAAINTgzODM4MzgJBQAAAAkGAAAABgcAAAAl15Mi16gg15zXmdeh15LXldeo16HXp9eZINeQ15nXlteR15zXlAYIAAAAATAJCQAAAAoGCgAAAAgyMDE5MDkxOQYLAAAACDIwMjAwMzE5BgwAAAAEMDYwMAYNAAAABDIzMDAGDgAAAAcwMDAwMDAwAQAF8f///yZDb21tb24uRW51bXMrU2VhcmNoQXBwb2ludG1lbnRDYWxsVHlwZQEAAAAHdmFsdWVfXwAIAgAAAAEAAAAF8P///ypDb21tb24uRW51bXMrU2VhcmNoQXBwb2ludG1lbnRMb2NhdGlvbkZsdWcBAAAAB3ZhbHVlX18ACAIAAAABAAAAAAoABAUAAACIAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkxpc3RgMVtbQ29tbW9uLkRvY3RvckFwcG9pbnRtZW50cy5Eb2N0b3JDaXR5LCBDb21tb24sIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsXV0DAAAABl9pdGVtcwVfc2l6ZQhfdmVyc2lvbgQAACZDb21tb24uRG9jdG9yQXBwb2ludG1lbnRzLkRvY3RvckNpdHlbXQIAAAAICAkRAAAAAQAAAAEAAAAFBgAAAAxDb21tb24uVHVwbGUCAAAAFTxUZXh0PmtfX0JhY2tpbmdGaWVsZBY8VmFsdWU+a19fQmFja2luZ0ZpZWxkAQECAAAABhIAAAAV16jXpNeV15DXqiDXntep16TXl9eUBhMAAAADMDQ0BAkAAABwU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tDb21tb24uVHVwbGUsIENvbW1vbiwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGxdXQMAAAAGX2l0ZW1zBV9zaXplCF92ZXJzaW9uBAAADkNvbW1vbi5UdXBsZVtdAgAAAAgICRQAAAABAAAAAQAAAAcRAAAAAAEAAAAEAAAABCRDb21tb24uRG9jdG9yQXBwb2ludG1lbnRzLkRvY3RvckNpdHkCAAAACRUAAAANAwcUAAAAAAEAAAAEAAAABAxDb21tb24uVHVwbGUCAAAACRYAAAANAwUVAAAAJENvbW1vbi5Eb2N0b3JBcHBvaW50bWVudHMuRG9jdG9yQ2l0eQIAAAAVPENvZGU+a19fQmFja2luZ0ZpZWxkFTxOYW1lPmtfX0JhY2tpbmdGaWVsZAABBQIAAAABMAoBFgAAAAYAAAAGFwAAABzXodeg15nXoyDXp9eg15nXldefINeU15HXkNeoBhgAAAAEMTAyMQseDVNvcnREaXJlY3Rpb24LKidTeXN0ZW0uV2ViLlVJLldlYkNvbnRyb2xzLlNvcnREaXJlY3Rpb24AHglTb3J0RmllbGQFD0FwcG9pbnRtZW50RGF0ZWQWAgICD2QWAmYPZBYGAiEPDxYCHwFlZGQCIw9kFgJmD2QWBgIBD2QWBgIBDw8WBh8VBQlzdWJIZWFkZXIfAQUn16DXntem15DXlSA1MyDXqteV16jXmdedINeQ16TXqdeo15nXmdedHxMCAmRkAgMPDxYCHwFlZGQCBQ8PFgIfAQU1157Xldem15LXmdedINeq15XXqNeZ150g15zXqNeV16TXkCDXlNeQ15nXqdeZINep15zXmi5kZAIDDw8WAh8AZ2RkAgkPZBYCAgEPEGRkFgECAmQCJA88KwANAQAPFgQfEWcfCwI1ZBYCZg9kFi5mDw8WAh8AaGRkAgEPZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMDg6MDBkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcwOCUzQTEyJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA4JTNBMDAnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMCcsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMDglM0ExMicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOCUzQTAwJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzAnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMDglM0ExMicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOCUzQTAwJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzAnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAICD2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTA4OjEyZAIHDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw9kFgIfCQVZamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNScsJ9ee15XXqNep16og15nXqdeo15DXnCcsJ9eo15DXqdeV158g15zXpteZ15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEv157Xldeo16nXqiDXmdep16jXkNecIDE1LCDXqNeQ16nXldefINec16bXmdeV159kAg8PFgIfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMDglM0EyNCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOCUzQTEyJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzEnLCcnKTtyZXR1cm4gZmFsc2U7ZAIRDxYEHwkFwQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzA4JTNBMjQnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDglM0ExMicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxJywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEw8WBB8JBb0DamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnJywnMzcyMicsJycsJzA0NCcsJzA4JTNBMjQnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDglM0ExMicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxJywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCAw9kFgJmD2QWEgIBD2QWAmYPFgIfCAUI15TXmdeV151kAgMPZBYCZg8VAQUwODoyNGQCBw8WAh8AaGQCCQ8PFgIfAQUl15Mi16gg15DXmdeW15HXnNeUINec15nXodeS15XXqNeh16fXmRYCHwkFOWphdmFzY3JpcHQ6b3BlbkRvY3RvckRldGFpbHNXaW5kb3coJzMwNjAwJyk7cmV0dXJuIGZhbHNlO2QCCw8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUZGQCDQ8PZBYCHwkFWWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefZAIPDxYCHwkFwQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzA4JTNBMzYnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDglM0EyNCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcyJywnJyk7cmV0dXJuIGZhbHNlO2QCEQ8WBB8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcwOCUzQTM2JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA4JTNBMjQnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMicsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW9A2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzM3MjInLCcnLCcwNDQnLCcwOCUzQTM2JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA4JTNBMjQnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMicsJycpO3JldHVybiBmYWxzZTsfAGhkAgQPZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMDg6MzZkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcwOCUzQTQ4JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA4JTNBMzYnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMycsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMDglM0E0OCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOCUzQTM2JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzMnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMDglM0E0OCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOCUzQTM2JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzMnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAIFD2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTA4OjQ4ZAIHDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw9kFgIfCQVZamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNScsJ9ee15XXqNep16og15nXqdeo15DXnCcsJ9eo15DXqdeV158g15zXpteZ15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEv157Xldeo16nXqiDXmdep16jXkNecIDE1LCDXqNeQ16nXldefINec16bXmdeV159kAg8PFgIfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMDklM0EwMCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOCUzQTQ4JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzQnLCcnKTtyZXR1cm4gZmFsc2U7ZAIRDxYEHwkFwQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzA5JTNBMDAnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDglM0E0OCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCc0JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEw8WBB8JBb0DamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnJywnMzcyMicsJycsJzA0NCcsJzA5JTNBMDAnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDglM0E0OCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCc0JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCBg9kFgJmD2QWEgIBD2QWAmYPFgIfCAUI15TXmdeV151kAgMPZBYCZg8VAQUwOTowMGQCBw8WAh8AaGQCCQ8PFgIfAQUl15Mi16gg15DXmdeW15HXnNeUINec15nXodeS15XXqNeh16fXmRYCHwkFOWphdmFzY3JpcHQ6b3BlbkRvY3RvckRldGFpbHNXaW5kb3coJzMwNjAwJyk7cmV0dXJuIGZhbHNlO2QCCw8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUZGQCDQ8PZBYCHwkFWWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefZAIPDxYCHwkFwQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzA5JTNBMTInLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDklM0EwMCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCc1JywnJyk7cmV0dXJuIGZhbHNlO2QCEQ8WBB8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcwOSUzQTEyJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA5JTNBMDAnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnNScsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW9A2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzM3MjInLCcnLCcwNDQnLCcwOSUzQTEyJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA5JTNBMDAnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnNScsJycpO3JldHVybiBmYWxzZTsfAGhkAgcPZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMDk6MTJkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcwOSUzQTI0JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA5JTNBMTInLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnNicsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMDklM0EyNCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOSUzQTEyJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzYnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMDklM0EyNCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOSUzQTEyJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzYnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAIID2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTA5OjI0ZAIHDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw9kFgIfCQVZamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNScsJ9ee15XXqNep16og15nXqdeo15DXnCcsJ9eo15DXqdeV158g15zXpteZ15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEv157Xldeo16nXqiDXmdep16jXkNecIDE1LCDXqNeQ16nXldefINec16bXmdeV159kAg8PFgIfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMDklM0EzNicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOSUzQTI0JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzcnLCcnKTtyZXR1cm4gZmFsc2U7ZAIRDxYEHwkFwQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzA5JTNBMzYnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDklM0EyNCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCc3JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEw8WBB8JBb0DamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnJywnMzcyMicsJycsJzA0NCcsJzA5JTNBMzYnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDklM0EyNCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCc3JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCCQ9kFgJmD2QWEgIBD2QWAmYPFgIfCAUI15TXmdeV151kAgMPZBYCZg8VAQUwOTozNmQCBw8WAh8AaGQCCQ8PFgIfAQUl15Mi16gg15DXmdeW15HXnNeUINec15nXodeS15XXqNeh16fXmRYCHwkFOWphdmFzY3JpcHQ6b3BlbkRvY3RvckRldGFpbHNXaW5kb3coJzMwNjAwJyk7cmV0dXJuIGZhbHNlO2QCCw8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUZGQCDQ8PZBYCHwkFWWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefZAIPDxYCHwkFwQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzA5JTNBNDgnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMDklM0EzNicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCc4JywnJyk7cmV0dXJuIGZhbHNlO2QCEQ8WBB8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcwOSUzQTQ4JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA5JTNBMzYnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnOCcsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW9A2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzM3MjInLCcnLCcwNDQnLCcwOSUzQTQ4JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA5JTNBMzYnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnOCcsJycpO3JldHVybiBmYWxzZTsfAGhkAgoPZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMDk6NDhkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcEDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMCUzQTAwJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzA5JTNBNDgnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnOScsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXBA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTAlM0EwMCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOSUzQTQ4JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzknLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvQNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMTAlM0EwMCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcwOSUzQTQ4JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzknLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAILD2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTEwOjEyZAIHDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw9kFgIfCQVZamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNScsJ9ee15XXqNep16og15nXqdeo15DXnCcsJ9eo15DXqdeV158g15zXpteZ15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEv157Xldeo16nXqiDXmdep16jXkNecIDE1LCDXqNeQ16nXldefINec16bXmdeV159kAg8PFgIfCQXCA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTAlM0EyNCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMCUzQTEyJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzEwJywnJyk7cmV0dXJuIGZhbHNlO2QCEQ8WBB8JBcIDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMCUzQTI0JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzEwJTNBMTInLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTAnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMTAlM0EyNCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMCUzQTEyJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzEwJywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCDA9kFgJmD2QWEgIBD2QWAmYPFgIfCAUI15TXmdeV151kAgMPZBYCZg8VAQUxMDoyNGQCBw8WAh8AaGQCCQ8PFgIfAQUl15Mi16gg15DXmdeW15HXnNeUINec15nXodeS15XXqNeh16fXmRYCHwkFOWphdmFzY3JpcHQ6b3BlbkRvY3RvckRldGFpbHNXaW5kb3coJzMwNjAwJyk7cmV0dXJuIGZhbHNlO2QCCw8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUZGQCDQ8PZBYCHwkFWWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefZAIPDxYCHwkFwgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzEwJTNBMzYnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTAlM0EyNCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxMScsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXCA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTAlM0EzNicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMCUzQTI0JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzExJywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEw8WBB8JBb4DamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnJywnMzcyMicsJycsJzA0NCcsJzEwJTNBMzYnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTAlM0EyNCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxMScsJycpO3JldHVybiBmYWxzZTsfAGhkAg0PZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMTA6MzZkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcIDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMCUzQTQ4JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzEwJTNBMzYnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTInLCcnKTtyZXR1cm4gZmFsc2U7ZAIRDxYEHwkFwgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzEwJTNBNDgnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTAlM0EzNicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxMicsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW+A2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzM3MjInLCcnLCcwNDQnLCcxMCUzQTQ4JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzEwJTNBMzYnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTInLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAIOD2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTEwOjQ4ZAIHDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw9kFgIfCQVZamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNScsJ9ee15XXqNep16og15nXqdeo15DXnCcsJ9eo15DXqdeV158g15zXpteZ15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEv157Xldeo16nXqiDXmdep16jXkNecIDE1LCDXqNeQ16nXldefINec16bXmdeV159kAg8PFgIfCQXCA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTElM0EwMCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMCUzQTQ4JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzEzJywnJyk7cmV0dXJuIGZhbHNlO2QCEQ8WBB8JBcIDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMSUzQTAwJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzEwJTNBNDgnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTMnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMTElM0EwMCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMCUzQTQ4JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzEzJywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCDw9kFgJmD2QWEgIBD2QWAmYPFgIfCAUI15TXmdeV151kAgMPZBYCZg8VAQUxMTowMGQCBw8WAh8AaGQCCQ8PFgIfAQUl15Mi16gg15DXmdeW15HXnNeUINec15nXodeS15XXqNeh16fXmRYCHwkFOWphdmFzY3JpcHQ6b3BlbkRvY3RvckRldGFpbHNXaW5kb3coJzMwNjAwJyk7cmV0dXJuIGZhbHNlO2QCCw8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUZGQCDQ8PZBYCHwkFWWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefZAIPDxYCHwkFwgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzExJTNBMTInLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTElM0EwMCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxNCcsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXCA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTElM0ExMicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMSUzQTAwJywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzE0JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEw8WBB8JBb4DamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnJywnMzcyMicsJycsJzA0NCcsJzExJTNBMTInLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTElM0EwMCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxNCcsJycpO3JldHVybiBmYWxzZTsfAGhkAhAPZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMTE6MTJkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcIDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMSUzQTI0JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzExJTNBMTInLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTUnLCcnKTtyZXR1cm4gZmFsc2U7ZAIRDxYEHwkFwgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzExJTNBMjQnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTElM0ExMicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxNScsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW+A2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzM3MjInLCcnLCcwNDQnLCcxMSUzQTI0JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzExJTNBMTInLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTUnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAIRD2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTExOjI0ZAIHDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw9kFgIfCQVZamF2YXNjcmlwdDpzZW5kVG9FbWFwKCcxNScsJ9ee15XXqNep16og15nXqdeo15DXnCcsJ9eo15DXqdeV158g15zXpteZ15XXnycpO3JldHVybiBmYWxzZTsWAmYPFQEv157Xldeo16nXqiDXmdep16jXkNecIDE1LCDXqNeQ16nXldefINec16bXmdeV159kAg8PFgIfCQXCA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTElM0EzNicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMSUzQTI0JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzE2JywnJyk7cmV0dXJuIGZhbHNlO2QCEQ8WBB8JBcIDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMSUzQTM2JywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzExJTNBMjQnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTYnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAITDxYEHwkFvgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgndmlydHVhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcnLCczNzIyJywnJywnMDQ0JywnMTElM0EzNicsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMSUzQTI0JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzE2JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEg9kFgJmD2QWEgIBD2QWAmYPFgIfCAUI15TXmdeV151kAgMPZBYCZg8VAQUxMTozNmQCBw8WAh8AaGQCCQ8PFgIfAQUl15Mi16gg15DXmdeW15HXnNeUINec15nXodeS15XXqNeh16fXmRYCHwkFOWphdmFzY3JpcHQ6b3BlbkRvY3RvckRldGFpbHNXaW5kb3coJzMwNjAwJyk7cmV0dXJuIGZhbHNlO2QCCw8PFgIfAQUV16jXpNeV15DXqiDXntep16TXl9eUZGQCDQ8PZBYCHwkFWWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnMTUnLCfXnteV16jXqdeqINeZ16nXqNeQ15wnLCfXqNeQ16nXldefINec16bXmdeV158nKTtyZXR1cm4gZmFsc2U7FgJmDxUBL9ee15XXqNep16og15nXqdeo15DXnCAxNSwg16jXkNep15XXnyDXnNem15nXldefZAIPDxYCHwkFwgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzExJTNBNDgnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTElM0EzNicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxNycsJycpO3JldHVybiBmYWxzZTtkAhEPFgQfCQXCA2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDEnLCczNzIyJywnJywnMDQ0JywnMTElM0E0OCcsJycsJyV1MDVEMCV1MDVEOSV1MDVENiV1MDVEMSV1MDVEQyV1MDVENCUyMCV1MDVEQyV1MDVEOSV1MDVFMSV1MDVEMiV1MDVENSV1MDVFOCV1MDVFMSV1MDVFNyV1MDVEOScsJyV1MDVERSV1MDVENSV1MDVFOCV1MDVFOSV1MDVFQSUyMCV1MDVEOSV1MDVFOSV1MDVFOCV1MDVEMCV1MDVEQycsJzE1JywnJXUwNUU4JXUwNUQwJXUwNUU5JXUwNUQ1JXUwNURGJTIwJXUwNURDJXUwNUU2JXUwNUQ5JXUwNUQ1JXUwNURGJywnMDMtNzI5MzMwMCcsJzAnLCcxMCcsJzIxJywnMzA2MDAnLCcxMSUzQTM2JywnMTkvMDkvMjAxOScsJyV1MDVENCcsJyV1MDVEMyUyMiV1MDVFOCcsJzE3JywnJyk7cmV0dXJuIGZhbHNlOx8AaGQCEw8WBB8JBb4DamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnJywnMzcyMicsJycsJzA0NCcsJzExJTNBNDgnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTElM0EzNicsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxNycsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPZBYCZg9kFhICAQ9kFgJmDxYCHwgFCNeU15nXldedZAIDD2QWAmYPFQEFMTE6NDhkAgcPFgIfAGhkAgkPDxYCHwEFJdeTIteoINeQ15nXlteR15zXlCDXnNeZ16HXkteV16jXoden15kWAh8JBTlqYXZhc2NyaXB0Om9wZW5Eb2N0b3JEZXRhaWxzV2luZG93KCczMDYwMCcpO3JldHVybiBmYWxzZTtkAgsPDxYCHwEFFdeo16TXldeQ16og157Xqdek15fXlGRkAg0PD2QWAh8JBVlqYXZhc2NyaXB0OnNlbmRUb0VtYXAoJzE1Jywn157Xldeo16nXqiDXmdep16jXkNecJywn16jXkNep15XXnyDXnNem15nXldefJyk7cmV0dXJuIGZhbHNlOxYCZg8VAS/XnteV16jXqdeqINeZ16nXqNeQ15wgMTUsINeo15DXqdeV158g15zXpteZ15XXn2QCDw8WAh8JBcIDamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ2Zyb250YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMScsJzM3MjInLCcnLCcwNDQnLCcxMiUzQTAwJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzExJTNBNDgnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTgnLCcnKTtyZXR1cm4gZmFsc2U7ZAIRDxYEHwkFwgNqYXZhc2NyaXB0OnNlbGVjdFR5cGVBcHBvaW50bWVudCgnZnJvbnRhbCcpO0NoZWNrU2V0dGluZ0ZyZWVBcG9vaW50cyh0aGlzLCcwMDAxJywnMzcyMicsJycsJzA0NCcsJzEyJTNBMDAnLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcldTA1REUldTA1RDUldTA1RTgldTA1RTkldTA1RUElMjAldTA1RDkldTA1RTkldTA1RTgldTA1RDAldTA1REMnLCcxNScsJyV1MDVFOCV1MDVEMCV1MDVFOSV1MDVENSV1MDVERiUyMCV1MDVEQyV1MDVFNiV1MDVEOSV1MDVENSV1MDVERicsJzAzLTcyOTMzMDAnLCcwJywnMTAnLCcyMScsJzMwNjAwJywnMTElM0E0OCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxOCcsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW+A2phdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzM3MjInLCcnLCcwNDQnLCcxMiUzQTAwJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJXUwNURFJXUwNUQ1JXUwNUU4JXUwNUU5JXUwNUVBJTIwJXUwNUQ5JXUwNUU5JXUwNUU4JXUwNUQwJXUwNURDJywnMTUnLCcldTA1RTgldTA1RDAldTA1RTkldTA1RDUldTA1REYlMjAldTA1REMldTA1RTYldTA1RDkldTA1RDUldTA1REYnLCcwMy03MjkzMzAwJywnMCcsJzEwJywnMjEnLCczMDYwMCcsJzExJTNBNDgnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTgnLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAIUD2QWAmYPZBYSAgEPZBYCZg8WAh8IBQjXlNeZ15XXnWQCAw9kFgJmDxUBBTEzOjAwZAIFDxYCHwBoZAIJDw8WAh8BBSXXkyLXqCDXkNeZ15bXkdec15Qg15zXmdeh15LXldeo16HXp9eZFgIfCQU5amF2YXNjcmlwdDpvcGVuRG9jdG9yRGV0YWlsc1dpbmRvdygnMzA2MDAnKTtyZXR1cm4gZmFsc2U7ZAILDw8WAh8BBRXXqNek15XXkNeqINee16nXpNeX15RkZAINDw8WAh8AaBYCHwkFLWphdmFzY3JpcHQ6c2VuZFRvRW1hcCgnJywnJywnJyk7cmV0dXJuIGZhbHNlOxYCZg8VAQIgIGQCDw8WAh8JBboCamF2YXNjcmlwdDpzZWxlY3RUeXBlQXBwb2ludG1lbnQoJ3ZpcnR1YWwnKTtDaGVja1NldHRpbmdGcmVlQXBvb2ludHModGhpcywnMDAwMicsJzU0ODQnLCdCTScsJzA0NCcsJzEzJTNBMTInLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcnLCcnLCcnLCcnLCcwJywnODgnLCcxMicsJzMwNjAwJywnMTMlM0EwMCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxOScsJycpO3JldHVybiBmYWxzZTsWAgIDDw8WAh8BBSPXlteZ157XldefPEJyIC8+16rXldeoINeR15XXmdeT15DXlWRkAhEPFgQfCQW2AmphdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCdmcm9udGFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJycsJzU0ODQnLCdCTScsJzA0NCcsJzEzJTNBMTInLCcnLCcldTA1RDAldTA1RDkldTA1RDYldTA1RDEldTA1REMldTA1RDQlMjAldTA1REMldTA1RDkldTA1RTEldTA1RDIldTA1RDUldTA1RTgldTA1RTEldTA1RTcldTA1RDknLCcnLCcnLCcnLCcnLCcwJywnODgnLCcxMicsJzMwNjAwJywnMTMlM0EwMCcsJzE5LzA5LzIwMTknLCcldTA1RDQnLCcldTA1RDMlMjIldTA1RTgnLCcxOScsJycpO3JldHVybiBmYWxzZTsfAGhkAhMPFgQfCQW6AmphdmFzY3JpcHQ6c2VsZWN0VHlwZUFwcG9pbnRtZW50KCd2aXJ0dWFsJyk7Q2hlY2tTZXR0aW5nRnJlZUFwb29pbnRzKHRoaXMsJzAwMDInLCc1NDg0JywnQk0nLCcwNDQnLCcxMyUzQTEyJywnJywnJXUwNUQwJXUwNUQ5JXUwNUQ2JXUwNUQxJXUwNURDJXUwNUQ0JTIwJXUwNURDJXUwNUQ5JXUwNUUxJXUwNUQyJXUwNUQ1JXUwNUU4JXUwNUUxJXUwNUU3JXUwNUQ5JywnJywnJywnJywnJywnMCcsJzg4JywnMTInLCczMDYwMCcsJzEzJTNBMDAnLCcxOS8wOS8yMDE5JywnJXUwNUQ0JywnJXUwNUQzJTIyJXUwNUU4JywnMTknLCcnKTtyZXR1cm4gZmFsc2U7HwBoZAIVDw8WAh8AaGRkAhYPZBYCZg8PFgQeC0JvcmRlcldpZHRoGwAAAAAAAAAAAQAAAB8TAiBkFgQCAQ8PFgQfAgUdLi4vLi4vQ1NTL0ltYWdlcy9QYWdlUmRpcy5wbmcfBWhkZAIDDxYCHwsCAxYGZg9kFgICAQ8PFggfFQUPUGdyQ3JudE51bVN0eWxlHwEFATEeD0NvbW1hbmRBcmd1bWVudAUBMR8TAgJkZAIBD2QWAgIBDw8WBB8BBQEyHyEFATJkZAICD2QWAgIBDw8WBB8BBQEzHyEFATNkZAIyD2QWCAIFDw8WAh8BBRrXntec15wg15fXk9epINeR16LXkdeo15nXqmRkAgcPDxYCHwBoZGQCCQ8PFgIfAGhkFgICAw8PFgIfAQWaA9eQ16DXmSDXnteZ15nXpNeUINeQ16og15vXldeX15Ug16nXnCDXkdeZ16og157XqNen15fXqiBiZWxhINep15vXqteV15HXqteVINeo15XXkteV15bXmdefIDEsINeQ16nXk9eV15MgLSDXnNeU16LXkdeZ16gg15DXnNeZINeq15vXqdeZ16jXmdedINei15wg15nXk9eZINep15zXmdeXINee15jXotee15Ug15XXnteh15vXmdedL9eUINec16fXkdecDQrXnteZ15PXoiDXldeZ16LXldelINeq16jXldek16rXmSDXkdeQ157Xptei15XXqiDXmNeV16TXoSDXqdeZ16bXldeo16Mg15zXqteb16nXmdeo15nXnSDXqdeR157Xqdec15XXlyDXlS/XkNeVINec16fXkdecINee15nXk9eiINeR15DXntem16LXmdedINeU157XpNeV16jXmNeZ150g15HXpNeo15jXmSDXlNeq16fXqdeV16jXqiDXotedINeR15nXqiDXlNee16jXp9eX16ouZGQCCw8PFgIfAGhkZBgEBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WIgUaY3RsMDAkU21hbGxGb250SW1hZ2VCdXR0b24FGGN0bDAwJEJpZ0ZvbnRJbWFnZUJ1dHRvbgUdY3RsMDAkU2VsZWN0ZWRVc2VySW1hZ2VCdXR0b24FKWN0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyBV1jdGwwMCRNYWluQ29udGVudFBsYWNlSG9sZGVyJHRhYkNvbnRhaW5lciRmaXJzdFRhYiRTZWFyY2hCb3gkRGF5c1NlbGVjdCRycHRJdGVtcyRjdGwwMCRjYkl0ZW0FXWN0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyJGZpcnN0VGFiJFNlYXJjaEJveCREYXlzU2VsZWN0JHJwdEl0ZW1zJGN0bDAxJGNiSXRlbQVdY3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkU2VhcmNoQm94JERheXNTZWxlY3QkcnB0SXRlbXMkY3RsMDIkY2JJdGVtBV1jdGwwMCRNYWluQ29udGVudFBsYWNlSG9sZGVyJHRhYkNvbnRhaW5lciRmaXJzdFRhYiRTZWFyY2hCb3gkRGF5c1NlbGVjdCRycHRJdGVtcyRjdGwwMyRjYkl0ZW0FXWN0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyJGZpcnN0VGFiJFNlYXJjaEJveCREYXlzU2VsZWN0JHJwdEl0ZW1zJGN0bDA0JGNiSXRlbQVdY3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkU2VhcmNoQm94JERheXNTZWxlY3QkcnB0SXRlbXMkY3RsMDUkY2JJdGVtBV1jdGwwMCRNYWluQ29udGVudFBsYWNlSG9sZGVyJHRhYkNvbnRhaW5lciRmaXJzdFRhYiRTZWFyY2hCb3gkRGF5c1NlbGVjdCRycHRJdGVtcyRjdGwwNiRjYkl0ZW0FXWN0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyJGZpcnN0VGFiJFNlYXJjaEJveCREYXlzU2VsZWN0JHJwdEl0ZW1zJGN0bDA3JGNiSXRlbQV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwMiRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwMyRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwNCRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwNSRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwNiRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwNyRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwOCRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwwOSRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxMCRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxMSRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxMiRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxMyRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxNCRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxNSRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxNiRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxNyRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxOCRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwxOSRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwyMCRpbWdDcmVhdGVBcHBvaW50bWVudAV/Y3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwyMSRpbWdDcmVhdGVBcHBvaW50bWVudAVyY3RsMDAkTWFpbkNvbnRlbnRQbGFjZUhvbGRlciR0YWJDb250YWluZXIkZmlyc3RUYWIkRG9jdG9yQXBwb2ludG1lbnRzTGlzdCRndlNlYXJjaEFwcG9pbnRtZW50UmVzdWx0cyRjdGwyMyRpbWdQcmV2BXJjdGwwMCRNYWluQ29udGVudFBsYWNlSG9sZGVyJHRhYkNvbnRhaW5lciRmaXJzdFRhYiREb2N0b3JBcHBvaW50bWVudHNMaXN0JGd2U2VhcmNoQXBwb2ludG1lbnRSZXN1bHRzJGN0bDIzJGltZ05leHQFKWN0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyDw9kZmQFZGN0bDAwJE1haW5Db250ZW50UGxhY2VIb2xkZXIkdGFiQ29udGFpbmVyJGZpcnN0VGFiJERvY3RvckFwcG9pbnRtZW50c0xpc3QkZ3ZTZWFyY2hBcHBvaW50bWVudFJlc3VsdHMPPCsACgEIAgNkBWZjdGwwMCRNYWluQ29udGVudFBsYWNlSG9sZGVyJHRhYkNvbnRhaW5lciRmaXJzdFRhYiRRdWlja0FwcG9pbnRtZW50Rm9yUGVyc29uYWxEb2N0b3IkZ3ZQZXJzb25hbERvY3RvcnMPPCsACgEIAgFkMaJf3FeRhx1BgTdwvd/kj4s6rbM="
  },
  {
    "path": "spec/std/data/mime.types",
    "content": "foo/bar foo\n"
  },
  {
    "path": "spec/std/data/openssl/openssl.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKtJGQyJHN83MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\nBAYTAkFSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQwHhcNMTYwNTI5MTUwMzI1WhcNNDMxMDE0MTUwMzI1WjBF\nMQswCQYDVQQGEwJBUjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEAqz+M6CnLr8wJ5ooDiNU7D2hxfZqxculFP1y2wTDuxJfP8LqPO4o1NLpU\nE9H2idIn/iMLZrRpeK38lw8RmorEh0ykOQ2jXbw9Lw+xgQXjmsf0ZcXqSB82VD6q\n7JsGOF+Qq3I/YGegINfiOYMw60r8YEMTBJlz7tyeuJrCx2VUwBOa2Rtx7n0fzSom\n5jYAHEMQA6bAmShNOtCRn45NeVStQS1XTZ6XavmLiCUrgvEfWj+FlrpQQiTqoxGd\ndOTz1G0/0+FdJ7By/G/GbDBc2xuix7Fai7qhuLB5KAVd73Vy6T09U5TfDcUi+CNx\ncvJu0YPn9vVkRIuAoH3lMpprtzLaGwIDAQABo1AwTjAdBgNVHQ4EFgQU7DjYFtaT\nvnHQCfVWLOilVdco8mwwHwYDVR0jBBgwFoAU7DjYFtaTvnHQCfVWLOilVdco8mww\nDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAgApc4DjKd3x3lDeENS/s\nCZck6IkK+iErXeevIQFvi2poorTdoCdmnl4Hn+VgElTx8OL48WulDtqppEVY5I5t\nqT4AU7UXMBvmySw9cOB4nSMSYJVmtAYnVa61WICpQ8tIOunanRxwB32I/BUUc6rr\n0i+iAiW8x4aPsG5SGafIwtfNhY1pJa4nyo/VAJKxEKIl5jgeITBGHCO8ZHHqTcBu\nbj/DuWC3vGN5pVR3mb+O7Q1X+nhOZaSJYkB0nfLBKpWdMx0jrp1ZvbwDH5RrzzdD\nZRtrL2CVb3uWpbiCS8UaFcd1PaD92yT0IkxEhdIWH6WyDqs1/DZzbKDzxAlDiaCS\nZA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "spec/std/data/openssl/openssl.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrP4zoKcuvzAnm\nigOI1TsPaHF9mrFy6UU/XLbBMO7El8/wuo87ijU0ulQT0faJ0if+IwtmtGl4rfyX\nDxGaisSHTKQ5DaNdvD0vD7GBBeOax/RlxepIHzZUPqrsmwY4X5Crcj9gZ6Ag1+I5\ngzDrSvxgQxMEmXPu3J64msLHZVTAE5rZG3HufR/NKibmNgAcQxADpsCZKE060JGf\njk15VK1BLVdNnpdq+YuIJSuC8R9aP4WWulBCJOqjEZ105PPUbT/T4V0nsHL8b8Zs\nMFzbG6LHsVqLuqG4sHkoBV3vdXLpPT1TlN8NxSL4I3Fy8m7Rg+f29WREi4CgfeUy\nmmu3MtobAgMBAAECggEAHmOir7hrCwFcaGrpgajFWFCigzWmc8vtm/bp/5KdbIm8\nPu38aQZ3tqmyLeo+o+qFalXxugIeDWpivrPP3eruQUxagD1pVkMHYIiaaVkQMPF2\n73CVyMKxM3YDgwVnry1WUPZvRL5e7jUhUi9zyO1/p91/THum1SaVjBD6q8PRrFwD\n2hjbi1ZYuzTJE9/7EWnrIeJUUx/TbhTM1aseufCpCbTQA5MA7ZVJKiW9+ZWPUw4x\nhfaDJx/kzFWE3DHU0L3eU83AFPZR2rmLOQONB2BhsSr5V7BGLtcY/4HTcRlzTt2g\nZSPZiD7IhpFcOyWiqATmGtmuFOl9dOORHtgF6VxJsQKBgQDdlcRbEbBv+78GQosK\nVh8ByhiDE1GQ4G4MoxxdtainBfbg4Uy+A2GDO9SgrD2VX3CSn+ZtJ8EBOO3wh9io\n/+X4Eoitkfpo0ZyAQQbSwXjVCZNANw8T27lYRAjIGVlNf/A3c7k3XdkdvSqkifhx\ne8sUFKZVR+82g9s3nAJgGxI5VwKBgQDF2GPdOw64rT3GxYsFl4qp5fc7r5IAgR73\nZPWPVApYrimzpu/5AEPA/dgAcRqflP3HKCJE+gJxtUgOnyd6GXSApD6rkBx3PGd1\n1ZtAsQw3wzWwQxzrQfjv4OMHy1ky1OtORvT/g1zWUKJdE+cm0CmonSbYE2Fgjkh4\n+G8spDk23QKBgDMBvLdx9PlyK+DXBIaWmICi8s2Jbuc4olyKV4dCv9Xiy5eshSvg\nP1wkM6fgvjRaSeGWqUZLNmR/pFYQD1Gnxlo6effqeIgUaEAlt9pf6t6vW5QWmIPr\nuliVIKhfHW13m+ZH30Tdd5Me7mf90pDc/DxdHITZEDmuVJISeYGB+cn1AoGAHdHt\ny2yZXXCPPSSNPbyHo/ALga2G3hiYKEXJVV8faBpoIrHovakyjSY1pmtlzePRFHGS\nKL9eGvFt+PY4JwkrLDCVWZqRD8/E8FfP3MJSyxzbPMQA2dzJvq4wyf32Zdj91oCP\ncOvF1G+26TyUvJ7niIiXUD4rkTgg6ErZxurBzOkCgYAOLlXQC+GkDjOzksSwJ60e\nKtcI4/7Z0CA43Tp6rLxcheNAaTcmMtmoqGyp7kChOlcJ5IOl1uXCaWP9Xho7gJqu\nbIK9i6hum8b1uTBI2U7FN3pUD+mKgaqXXfHjvVtoB9OS9Rug9loRkyXFtD6EZhdJ\nuGKDTMXbzMWWKPpoYd55Vw==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "spec/std/data/static_file_handler/empty.txt",
    "content": ""
  },
  {
    "path": "spec/std/data/static_file_handler/foo/bar.txt",
    "content": "bar\n"
  },
  {
    "path": "spec/std/data/static_file_handler/range.txt",
    "content": "Hello world\n"
  },
  {
    "path": "spec/std/data/static_file_handler/test.txt",
    "content": "Hello"
  },
  {
    "path": "spec/std/data/test_file.ini",
    "content": "[general]\nlog_level = D\n\n[section1]\nfoo = 1.1\nbar = 2\n\n[section2]\nx.y.z = coco lala\n\n"
  },
  {
    "path": "spec/std/data/test_file.txt",
    "content": "Hello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\nHello World\n"
  },
  {
    "path": "spec/std/data/test_template.ecr",
    "content": "Hello <%= @msg %> <% 3.times { |i| %><%= i %><% } %>\n"
  },
  {
    "path": "spec/std/data/test_template2.ecr",
    "content": "<%= 1 -%>\n<%= 2 -%>\n<%= 3 -%>\n"
  },
  {
    "path": "spec/std/data/test_template3.ecr",
    "content": "  <%- 2.times do |i| %><%= i %><% end -%>\n"
  },
  {
    "path": "spec/std/data/test_template4.ecr",
    "content": "hi\n  <%- 2.times do |i| %><%= i %><% end -%>\n"
  },
  {
    "path": "spec/std/data/test_template5.ecr",
    "content": "hi\n  <% 2.times do |i| -%>\n    <%= i %>\n  <% end -%>\n"
  },
  {
    "path": "spec/std/data/test_template6.ecr",
    "content": "<%= \"string with -%\" %>\n"
  },
  {
    "path": "spec/std/data/test_template7.ecr",
    "content": "<%% if @name %>\nGreetings, <%%= @name %>!\n  <%-% else -%>\nGreetings!\n<%-% end -%>\n"
  },
  {
    "path": "spec/std/data/zoneinfo/Foo/invalid",
    "content": ""
  },
  {
    "path": "spec/std/deque_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\nprivate class DequeTester\n  # Execute the same actions on an Array and a Deque and compare them at each step.\n\n  @deque : Deque(Int32)\n  @array : Array(Int32)\n  @i : Int32\n  @c : Array(Int32) | Deque(Int32) | Nil\n\n  def step(&)\n    @c = @deque\n    yield\n    @c = @array\n    yield\n    @deque.to_a.should eq(@array)\n    @i += 1\n  end\n\n  def initialize\n    @deque = Deque(Int32).new\n    @array = Array(Int32).new\n    @i = 1\n  end\n\n  getter i\n\n  def c\n    @c.not_nil!\n  end\n\n  def test(&)\n    with self yield\n  end\nend\n\nprivate alias RecursiveDeque = Deque(RecursiveDeque)\n\n# Yields multiple forms of `Deque{}`, `Deque{0}`, `Deque{0, 1}`, `Deque{0, 1, 2}`, ...,\n# `Deque{0, 1, 2, ..., max_size - 1}`. All deques have *max_size* as their\n# capacity; each deque is yielded *max_size* times with a different start\n# position in the internal buffer. Every deque is a fresh instance.\nprivate def each_queue_repr(max_size, &)\n  (0..max_size).each do |size|\n    max_size.times do |i|\n      x = Deque(Int32).new(max_size, 0)\n      x.rotate!(i)\n      x.pop(max_size - size)\n      x.fill &.itself\n      yield x\n    end\n  end\nend\n\ndescribe \"Deque\" do\n  describe \"implementation\" do\n    it \"works the same as array\" do\n      DequeTester.new.test do\n        step { c.unshift i }\n        step { c.pop }\n        step { c.push i }\n        step { c.shift }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.pop }\n        step { c.shift }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.push i }\n        step { c.unshift i }\n        step { c.unshift i }\n        step { c.unshift i }\n        step { c.unshift i }\n        step { c.unshift i }\n        step { c.unshift i }\n        step { c.insert(1, i) }\n        step { c.insert(0, i) }\n        step { c.insert(17, i) }\n        step { c.insert(14, i) }\n        step { c.insert(10, i) }\n        step { c.insert(10, i) }\n      end\n    end\n\n    it \"works the same as array when inserting at 1/8 size and deleting at 3/4 size\" do\n      DequeTester.new.test do\n        1000.times do\n          step { c.insert(c.size // 8, i) }\n        end\n        1000.times do\n          step { c.delete_at(c.size * 3 // 4) }\n        end\n      end\n    end\n\n    it \"works the same as array when inserting at 3/4 size and deleting at 1/8 size\" do\n      DequeTester.new.test do\n        1000.times do\n          step { c.insert(c.size * 3 // 4, i) }\n        end\n        1000.times do\n          step { c.delete_at(c.size // 8) }\n        end\n      end\n    end\n  end\n\n  describe \"new\" do\n    it \"creates with default value\" do\n      deq = Deque.new(5, 3)\n      deq.should eq(Deque{3, 3, 3, 3, 3})\n    end\n\n    it \"creates with default value in block\" do\n      deq = Deque.new(5) { |i| i * 2 }\n      deq.should eq(Deque{0, 2, 4, 6, 8})\n    end\n\n    it \"creates from an array\" do\n      deq = Deque(Int32).new([1, 2, 3, 4, 5])\n      deq.should eq(Deque{1, 2, 3, 4, 5})\n    end\n\n    it \"raises on negative count\" do\n      expect_raises(ArgumentError, \"Negative deque size\") do\n        Deque.new(-1, 3)\n      end\n    end\n\n    it \"raises on negative capacity\" do\n      expect_raises(ArgumentError, \"Negative deque capacity\") do\n        Deque(Int32).new(-1)\n      end\n    end\n  end\n\n  describe \"==\" do\n    it \"compares empty\" do\n      Deque(Int32).new.should eq(Deque(Int32).new)\n      Deque{1}.should_not eq(Deque(Int32).new)\n      Deque(Int32).new.should_not eq(Deque{1})\n    end\n\n    it \"compares elements\" do\n      Deque{1, 2, 3}.should eq(Deque{1, 2, 3})\n      Deque{1, 2, 3}.should_not eq(Deque{3, 2, 1})\n    end\n\n    it \"compares other\" do\n      a = Deque{1, 2, 3}\n      b = Deque{1, 2, 3}\n      c = Deque{1, 2, 3, 4}\n      d = Deque{1, 2, 4}\n      (a == b).should be_true\n      (b == c).should be_false\n      (a == d).should be_false\n    end\n\n    it \"compares other types\" do\n      a = Deque{1, 2, 3}\n      b = Deque{true, false}\n      c = \"other type\"\n      (a == b).should be_false\n      (b == c).should be_false\n      (a == c).should be_false\n    end\n  end\n\n  describe \"+\" do\n    it \"does +\" do\n      a = Deque{1, 2, 3}\n      b = Deque{4, 5}\n      c = a + b\n      c.size.should eq(5)\n      0.upto(4) { |i| c[i].should eq(i + 1) }\n    end\n\n    it \"does + with different types\" do\n      a = Deque{1, 2, 3}\n      a += Deque{\"hello\"}\n      a.should eq(Deque{1, 2, 3, \"hello\"})\n    end\n  end\n\n  describe \"[]\" do\n    it \"gets on positive index\" do\n      Deque{1, 2, 3}[1].should eq(2)\n    end\n\n    it \"gets on negative index\" do\n      Deque{1, 2, 3}[-1].should eq(3)\n    end\n\n    it \"gets nilable\" do\n      Deque{1, 2, 3}[2]?.should eq(3)\n      Deque{1, 2, 3}[3]?.should be_nil\n    end\n  end\n\n  describe \"[]=\" do\n    it \"sets on positive index\" do\n      a = Deque{1, 2, 3}\n      a[1] = 4\n      a[1].should eq(4)\n    end\n\n    it \"sets on negative index\" do\n      a = Deque{1, 2, 3}\n      a[-1] = 4\n      a[2].should eq(4)\n    end\n  end\n\n  it \"does clear\" do\n    a = Deque{1, 2, 3}\n    a.clear\n    a.should eq(Deque(Int32).new)\n  end\n\n  it \"does clone\" do\n    x = {1 => 2}\n    a = Deque{x}\n    b = a.clone\n    b.should eq(a)\n    a.should_not be(b)\n    a[0].should_not be(b[0])\n  end\n\n  it \"does clone with recursive type\" do\n    deq = Deque(RecursiveDeque).new\n    deq << deq\n    clone = deq.clone\n    clone.should be(clone.first)\n  end\n\n  describe \"concat\" do\n    it \"concats deque\" do\n      a = Deque{1, 2, 3}\n      a.concat(Deque{4, 5, 6})\n      a.should eq(Deque{1, 2, 3, 4, 5, 6})\n    end\n\n    it \"concats large deques\" do\n      a = Deque{1, 2, 3}\n      a.concat((4..1000).to_a)\n      a.should eq(Deque.new((1..1000).to_a))\n    end\n\n    it \"concats enumerable\" do\n      a = Deque{1, 2, 3}\n      a.concat((4..1000))\n      a.should eq(Deque.new((1..1000).to_a))\n    end\n\n    it \"concats indexable\" do\n      each_queue_repr(16) do |a|\n        size = a.size\n        b = Array.new(10, &.+(size))\n        a.concat(b).should be(a)\n        a.should eq(Deque.new(size + 10, &.itself))\n      end\n\n      each_queue_repr(16) do |a|\n        size = a.size\n        b = Slice.new(10, &.+(size))\n        a.concat(b).should be(a)\n        a.should eq(Deque.new(size + 10, &.itself))\n      end\n\n      each_queue_repr(16) do |a|\n        size = a.size\n        b = StaticArray(Int32, 10).new(&.+(size))\n        a.concat(b).should be(a)\n        a.should eq(Deque.new(size + 10, &.itself))\n      end\n\n      each_queue_repr(16) do |a|\n        size = a.size\n        b = Deque.new(10, &.+(size))\n        a.concat(b).should be(a)\n        a.should eq(Deque.new(size + 10, &.itself))\n      end\n    end\n\n    it \"concats itself\" do\n      each_queue_repr(16) do |a|\n        size = a.size\n        a.concat(a).should be(a)\n        a.should eq(Deque.new((0...size).to_a * 2))\n      end\n    end\n  end\n\n  describe \"delete\" do\n    it \"deletes many\" do\n      a = Deque{1, 2, 3, 1, 2, 3}\n      a.delete(2).should be_true\n      a.should eq(Deque{1, 3, 1, 3})\n    end\n\n    it \"delete not found\" do\n      a = Deque{1, 2}\n      a.delete(4).should be_false\n      a.should eq(Deque{1, 2})\n    end\n  end\n\n  describe \"delete_at\" do\n    it \"deletes positive index\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.delete_at(3).should eq(4)\n      a.should eq(Deque{1, 2, 3, 5})\n    end\n\n    it \"deletes negative index\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.delete_at(-4).should eq(2)\n      a.should eq(Deque{1, 3, 4, 5})\n    end\n\n    it \"deletes out of bounds\" do\n      a = Deque{1, 2, 3, 4}\n      expect_raises IndexError do\n        a.delete_at(4)\n      end\n    end\n  end\n\n  it \"does dup\" do\n    x = {1 => 2}\n    a = Deque{x}\n    b = a.dup\n    b.should eq(Deque{x})\n    a.should_not be(b)\n    a[0].should be(b[0])\n    b << {3 => 4}\n    a.should eq(Deque{x})\n  end\n\n  it \"does each\" do\n    a = Deque{1, 1, 1}\n    b = 0\n    a.each { |i| b += i }.should be_nil\n    b.should eq(3)\n  end\n\n  it \"does each_index\" do\n    a = Deque{1, 1, 1}\n    b = 0\n    a.each_index { |i| b += i }.should be_nil\n    b.should eq(3)\n  end\n\n  describe \"empty\" do\n    it \"is empty\" do\n      (Deque(Int32).new.empty?).should be_true\n    end\n\n    it \"is not empty\" do\n      Deque{1}.empty?.should be_false\n    end\n  end\n\n  it \"does equals? with custom block\" do\n    a = Deque{1, 3, 2}\n    b = Deque{3, 9, 4}\n    c = Deque{5, 7, 3}\n    d = Deque{1, 3, 2, 4}\n    f = ->(x : Int32, y : Int32) { (x % 2) == (y % 2) }\n    a.equals?(b, &f).should be_true\n    a.equals?(c, &f).should be_false\n    a.equals?(d, &f).should be_false\n  end\n\n  describe \"first\" do\n    it \"gets first when non empty\" do\n      a = Deque{1, 2, 3}\n      a.first.should eq(1)\n    end\n\n    it \"raises when empty\" do\n      expect_raises Enumerable::EmptyError do\n        Deque(Int32).new.first\n      end\n    end\n  end\n\n  describe \"first?\" do\n    it \"gets first? when non empty\" do\n      a = Deque{1, 2, 3}\n      a.first?.should eq(1)\n    end\n\n    it \"gives nil when empty\" do\n      Deque(Int32).new.first?.should be_nil\n    end\n  end\n\n  it \"does hash\" do\n    a = Deque{1, 2, Deque{3}}\n    b = Deque{1, 2, Deque{3}}\n    a.hash.should eq(b.hash)\n  end\n\n  describe \"insert\" do\n    it \"inserts with positive index\" do\n      a = Deque{1, 3, 4}\n      expected = Deque{1, 2, 3, 4}\n      a.insert(1, 2).should eq(expected)\n      a.should eq(expected)\n    end\n\n    it \"inserts with negative index\" do\n      a = Deque{1, 2, 3}\n      expected = Deque{1, 2, 3, 4}\n      a.insert(-1, 4).should eq(expected)\n      a.should eq(expected)\n    end\n\n    it \"inserts with negative index (2)\" do\n      a = Deque{1, 2, 3}\n      expected = Deque{4, 1, 2, 3}\n      a.insert(-4, 4).should eq(expected)\n      a.should eq(expected)\n    end\n\n    it \"inserts out of range\" do\n      a = Deque{1, 3, 4}\n\n      expect_raises IndexError do\n        a.insert(4, 1)\n      end\n    end\n  end\n\n  describe \"inspect\" do\n    it { Deque{1, 2, 3}.inspect.should eq(\"Deque{1, 2, 3}\") }\n  end\n\n  describe \"last\" do\n    it \"gets last when non empty\" do\n      a = Deque{1, 2, 3}\n      a.last.should eq(3)\n    end\n\n    it \"raises when empty\" do\n      expect_raises IndexError do\n        Deque(Int32).new.last\n      end\n    end\n  end\n\n  describe \"size\" do\n    it \"has size 0\" do\n      Deque(Int32).new.size.should eq(0)\n    end\n\n    it \"has size 2\" do\n      Deque{1, 2}.size.should eq(2)\n    end\n  end\n\n  describe \"pop\" do\n    it \"pops when non empty\" do\n      a = Deque{1, 2, 3}\n      a.pop.should eq(3)\n      a.should eq(Deque{1, 2})\n    end\n\n    it \"raises when empty\" do\n      expect_raises IndexError do\n        Deque(Int32).new.pop\n      end\n    end\n\n    it \"pops many elements\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.pop(3)\n      a.should eq(Deque{1, 2})\n    end\n\n    it \"pops more elements than what is available\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.pop(10)\n      a.should eq(Deque(Int32).new)\n    end\n\n    it \"pops negative count raises\" do\n      a = Deque{1, 2}\n      expect_raises ArgumentError do\n        a.pop(-1)\n      end\n    end\n  end\n\n  describe \"push\" do\n    it \"adds one element to the deque\" do\n      a = Deque{\"a\", \"b\"}\n      a.push(\"c\")\n      a.should eq Deque{\"a\", \"b\", \"c\"}\n    end\n\n    it \"returns the deque\" do\n      a = Deque{\"a\", \"b\"}\n      a.push(\"c\").should eq Deque{\"a\", \"b\", \"c\"}\n    end\n\n    it \"has the << alias\" do\n      a = Deque{\"a\", \"b\"}\n      a << \"c\"\n      a.should eq Deque{\"a\", \"b\", \"c\"}\n    end\n  end\n\n  describe \"reject!\" do\n    it \"with block\" do\n      a1 = Deque{1, 2, 3, 4, 5}\n\n      a2 = a1.reject! &.even?\n      a2.should eq(Deque{1, 3, 5})\n      a2.should be(a1)\n    end\n\n    it \"with pattern\" do\n      a1 = Deque{1, 2, 3, 4, 5}\n\n      a2 = a1.reject!(2..4)\n      a2.should eq(Deque{1, 5})\n      a2.should be(a1)\n    end\n  end\n\n  describe \"rotate!\" do\n    it \"rotates\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.rotate!\n      a.should eq(Deque{2, 3, 4, 5, 1})\n      a.rotate!(-2)\n      a.should eq(Deque{5, 1, 2, 3, 4})\n      a.rotate!(10)\n      a.should eq(Deque{5, 1, 2, 3, 4})\n    end\n\n    it \"rotates with size=capacity\" do\n      a = Deque{1, 2, 3, 4}\n      a.rotate!\n      a.should eq(Deque{2, 3, 4, 1})\n      a.rotate!(-2)\n      a.should eq(Deque{4, 1, 2, 3})\n      a.rotate!(8)\n      a.should eq(Deque{4, 1, 2, 3})\n    end\n\n    it \"rotates with size=0\" do\n      a = Deque(Int32).new\n      a.rotate!\n      a.should eq(Deque(Int32).new)\n    end\n\n    it \"rotates with size=1\" do\n      a = Deque{1}\n      a.rotate!\n      a.should eq(Deque{1})\n    end\n  end\n\n  describe \"select!\" do\n    it \"with block\" do\n      a1 = Deque{1, 2, 3, 4, 5}\n\n      a2 = a1.select! &.even?\n      a2.should eq(Deque{2, 4})\n      a2.should be(a1)\n    end\n\n    it \"with pattern\" do\n      a1 = Deque{1, 2, 3, 4, 5}\n\n      a2 = a1.select!(2..4)\n      a2.should eq(Deque{2, 3, 4})\n      a2.should be(a1)\n    end\n  end\n\n  describe \"shift\" do\n    it \"shifts when non empty\" do\n      a = Deque{1, 2, 3}\n      a.shift.should eq(1)\n      a.should eq(Deque{2, 3})\n    end\n\n    it \"raises when empty\" do\n      expect_raises IndexError do\n        Deque(Int32).new.shift\n      end\n    end\n\n    it \"shifts many elements\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.shift(3)\n      a.should eq(Deque{4, 5})\n    end\n\n    it \"shifts more than what is available\" do\n      a = Deque{1, 2, 3, 4, 5}\n      a.shift(10)\n      a.should eq(Deque(Int32).new)\n    end\n\n    it \"shifts negative count raises\" do\n      a = Deque{1, 2}\n      expect_raises ArgumentError do\n        a.shift(-1)\n      end\n    end\n  end\n\n  describe \"swap\" do\n    it \"swaps\" do\n      a = Deque{1, 2, 3}\n      a.swap(0, 2)\n      a.should eq(Deque{3, 2, 1})\n    end\n\n    it \"swaps with negative indices\" do\n      a = Deque{1, 2, 3}\n      a.swap(-3, -1)\n      a.should eq(Deque{3, 2, 1})\n    end\n\n    it \"swaps but raises out of bounds on left\" do\n      a = Deque{1, 2, 3}\n      expect_raises IndexError do\n        a.swap(3, 0)\n      end\n    end\n\n    it \"swaps but raises out of bounds on right\" do\n      a = Deque{1, 2, 3}\n      expect_raises IndexError do\n        a.swap(0, 3)\n      end\n    end\n  end\n\n  describe \"to_s\" do\n    it \"does to_s\" do\n      Deque{1, 2, 3}.to_s.should eq(\"Deque{1, 2, 3}\")\n    end\n\n    it \"does with recursive\" do\n      deq = Deque(RecursiveDeque).new\n      deq << deq\n      deq.to_s.should eq(\"Deque{Deque{...}}\")\n    end\n  end\n\n  it \"does unshift\" do\n    a = Deque{2, 3}\n    expected = Deque{1, 2, 3}\n    a.unshift(1).should eq(expected)\n    a.should eq(expected)\n  end\n\n  describe \"each iterator\" do\n    it_iterates \"#each\", [1, 2, 3], Deque{1, 2, 3}.each\n\n    it \"works while modifying deque\" do\n      a = Deque{1, 2, 3}\n      count = 0\n      it = a.each\n      it.each do\n        count += 1\n        a.clear\n      end\n      count.should eq(1)\n    end\n  end\n\n  describe \"each_index iterator\" do\n    it_iterates \"#each_index\", [0, 1, 2], Deque{1, 2, 3}.each_index\n\n    it \"works while modifying deque\" do\n      a = Deque{1, 2, 3}\n      count = 0\n      it = a.each_index\n      it.each do\n        count += 1\n        a.clear\n      end\n      count.should eq(1)\n    end\n  end\n\n  it_iterates \"#reverse_each\", [3, 2, 1], Deque{1, 2, 3}.reverse_each\n\n  it_iterates \"#cycle\", [1, 2, 3, 1, 2, 3, 1, 2], Deque{1, 2, 3}.cycle, infinite: true\n  it_iterates \"#cycle(limit)\", [1, 2, 3, 1, 2, 3], Deque{1, 2, 3}.cycle(2)\nend\n"
  },
  {
    "path": "spec/std/digest/adler32_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\nrequire \"digest/adler32\"\n\ndescribe Digest::Adler32 do\n  it_acts_as_digest_algorithm Digest::Adler32\n\n  it \"calculates digest from string\" do\n    Digest::Adler32.digest(\"foo\").to_slice.should eq Bytes[0x02, 0x82, 0x01, 0x45]\n  end\n\n  it \"calculates hash from string\" do\n    Digest::Adler32.hexdigest(\"foo\").should eq(\"02820145\")\n  end\n\n  it \"calculates hash from unicode string\" do\n    Digest::Adler32.hexdigest(\"fooø\").should eq(\"074a02c0\")\n  end\n\n  it \"calculates hash from UInt8 slices\" do\n    s = Bytes[0x66, 0x6f, 0x6f] # f,o,o\n    Digest::Adler32.hexdigest(s).should eq(\"02820145\")\n  end\n\n  it \"calculates hash of #to_slice\" do\n    buffer = StaticArray(UInt8, 1).new(1_u8)\n    Digest::Adler32.hexdigest(buffer).should eq(\"00020002\")\n  end\n\n  it \"can take a block\" do\n    Digest::Adler32.hexdigest do |ctx|\n      ctx.update \"f\"\n      ctx.update Bytes[0x6f, 0x6f]\n    end.should eq(\"02820145\")\n  end\n\n  it \"calculates base64'd hash from string\" do\n    Digest::Adler32.base64digest(\"foo\").should eq(\"AoIBRQ==\")\n  end\n\n  it \"resets\" do\n    digest = Digest::Adler32.new\n    digest.update \"foo\"\n    digest.final.hexstring.should eq(\"02820145\")\n\n    digest.reset\n    digest.update \"foo\"\n    digest.final.hexstring.should eq(\"02820145\")\n  end\n\n  it \"can't call final twice\" do\n    digest = Digest::Adler32.new\n    digest.final\n    expect_raises(Digest::FinalizedError) do\n      digest.final\n    end\n  end\n\n  it \"return the digest size\" do\n    Digest::Adler32.new.digest_size.should eq 4\n  end\n\n  it \"should be able to calculate adler32\" do\n    adler = Digest::Adler32.checksum(\"foo\").to_s(16)\n    adler.should eq(\"2820145\")\n  end\n\n  it \"should be able to calculate adler32 combined\" do\n    adler1 = Digest::Adler32.checksum(\"hello\")\n    adler2 = Digest::Adler32.checksum(\" world!\")\n    combined = Digest::Adler32.combine(adler1, adler2, \" world!\".size)\n    Digest::Adler32.checksum(\"hello world!\").should eq(combined)\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/crc32_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\nrequire \"digest/crc32\"\n\ndescribe Digest::CRC32 do\n  it_acts_as_digest_algorithm Digest::CRC32\n\n  it \"calculates digest from string\" do\n    Digest::CRC32.digest(\"foo\").to_slice.should eq Bytes[0x8c, 0x73, 0x65, 0x21]\n  end\n\n  it \"calculates hash from string\" do\n    Digest::CRC32.hexdigest(\"foo\").should eq(\"8c736521\")\n  end\n\n  it \"calculates hash from unicode string\" do\n    Digest::CRC32.hexdigest(\"fooø\").should eq(\"d5f3a18b\")\n  end\n\n  it \"calculates hash from UInt8 slices\" do\n    s = Bytes[0x66, 0x6f, 0x6f] # f,o,o\n    Digest::CRC32.hexdigest(s).should eq(\"8c736521\")\n  end\n\n  it \"calculates hash of #to_slice\" do\n    buffer = StaticArray(UInt8, 1).new(1_u8)\n    Digest::CRC32.hexdigest(buffer).should eq(\"a505df1b\")\n  end\n\n  it \"can take a block\" do\n    Digest::CRC32.hexdigest do |ctx|\n      ctx.update \"f\"\n      ctx.update Bytes[0x6f, 0x6f]\n    end.should eq(\"8c736521\")\n  end\n\n  it \"calculates base64'd hash from string\" do\n    Digest::CRC32.base64digest(\"foo\").should eq(\"jHNlIQ==\")\n  end\n\n  it \"resets\" do\n    digest = Digest::CRC32.new\n    digest.update \"foo\"\n    digest.final.hexstring.should eq(\"8c736521\")\n\n    digest.reset\n    digest.update \"foo\"\n    digest.final.hexstring.should eq(\"8c736521\")\n  end\n\n  it \"can't call final twice\" do\n    digest = Digest::CRC32.new\n    digest.final\n    expect_raises(Digest::FinalizedError) do\n      digest.final\n    end\n  end\n\n  it \"return the digest size\" do\n    Digest::CRC32.new.digest_size.should eq 4\n  end\n\n  it \"should be able to calculate crc32\" do\n    crc = Digest::CRC32.checksum(\"foo\").to_s(16)\n    crc.should eq(\"8c736521\")\n  end\n\n  it \"should be able to calculate crc32 combined\" do\n    crc1 = Digest::CRC32.checksum(\"hello\")\n    crc2 = Digest::CRC32.checksum(\" world!\")\n    combined = Digest::CRC32.combine(crc1, crc2, \" world!\".size)\n    Digest::CRC32.checksum(\"hello world!\").should eq(combined)\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/io_digest_spec.cr",
    "content": "require \"spec\"\nrequire \"digest\"\n\ndescribe IO::Digest do\n  it \"calculates digest from reading\" do\n    base_io = IO::Memory.new(\"foo\")\n    io = IO::Digest.new(base_io, ::Digest::SHA256.new)\n    slice = Bytes.new(256)\n    io.read(slice).should eq(3)\n\n    slice[0, 3].should eq(\"foo\".to_slice)\n    io.final.should eq(\"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\".hexbytes)\n  end\n\n  it \"can be created with ongoing digest\" do\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    base_digest.update(\"foo\")\n\n    base_io = IO::Memory.new(\"bar\")\n    io = IO::Digest.new(base_io, base_digest)\n    slice = Bytes.new(256)\n    io.read(slice).should eq(3)\n\n    base_digest.update(\"baz\")\n\n    # sha256(\"foobarbaz\")\n    io.final.should eq(\"97df3588b5a3f24babc3851b372f0ba71a9dcdded43b14b9d06961bfc1707d9d\".hexbytes)\n  end\n\n  it \"calculates digest from multiple reads\" do\n    base_io = IO::Memory.new(\"foo\")\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    io = IO::Digest.new(base_io, base_digest)\n    slice = Bytes.new(2)\n    io.read(slice).should eq(2)\n    slice[0, 2].should eq(\"fo\".to_slice)\n\n    io.read(slice).should eq(1)\n    slice[0, 1].should eq(\"o\".to_slice)\n\n    io.final.should eq(\"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\".hexbytes)\n  end\n\n  it \"does not calculate digest on read\" do\n    base_io = IO::Memory.new(\"foo\")\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    empty_digest = OpenSSL::Digest.new(\"SHA256\").final\n    io = IO::Digest.new(base_io, base_digest, IO::Digest::DigestMode::Write)\n    slice = Bytes.new(256)\n    io.read(slice).should eq(3)\n    slice[0, 3].should eq(\"foo\".to_slice)\n    io.final.should eq(empty_digest)\n  end\n\n  it \"calculates digest from writing\" do\n    base_io = IO::Memory.new\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    io = IO::Digest.new(base_io, base_digest, IO::Digest::DigestMode::Write)\n    io.write(\"foo\".to_slice)\n\n    base_io.to_slice[0, 3].should eq(\"foo\".to_slice)\n    io.final.should eq(\"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\".hexbytes)\n  end\n\n  it \"calculates digest from writing a string\" do\n    base_io = IO::Memory.new\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    io = IO::Digest.new(base_io, base_digest, IO::Digest::DigestMode::Write)\n    io.print(\"foo\")\n\n    base_io.to_slice[0, 3].should eq(\"foo\".to_slice)\n    io.final.should eq(\"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\".hexbytes)\n  end\n\n  it \"calculates digest from multiple writes\" do\n    base_io = IO::Memory.new\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    io = IO::Digest.new(base_io, base_digest, IO::Digest::DigestMode::Write)\n    io.write(\"fo\".to_slice)\n    io.write(\"o\".to_slice)\n    base_io.to_slice[0, 3].should eq(\"foo\".to_slice)\n\n    io.final.should eq(\"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\".hexbytes)\n  end\n\n  it \"does not calculate digest on write\" do\n    base_io = IO::Memory.new\n    base_digest = OpenSSL::Digest.new(\"SHA256\")\n    empty_digest = OpenSSL::Digest.new(\"SHA256\").final\n    io = IO::Digest.new(base_io, base_digest, IO::Digest::DigestMode::Read)\n    io.write(\"foo\".to_slice)\n\n    base_io.to_slice[0, 3].should eq(\"foo\".to_slice)\n    io.final.should eq(empty_digest)\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/md5_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\nrequire \"digest/md5\"\n\ndescribe Digest::MD5 do\n  it_acts_as_digest_algorithm Digest::MD5\n\n  it \"calculates digest from string\" do\n    Digest::MD5.digest(\"foo\").to_slice.should eq Bytes[0xac, 0xbd, 0x18, 0xdb, 0x4c, 0xc2, 0xf8, 0x5c, 0xed, 0xef, 0x65, 0x4f, 0xcc, 0xc4, 0xa4, 0xd8]\n  end\n\n  it \"calculates hash from string\" do\n    Digest::MD5.hexdigest(\"foo\").should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"calculates hash from unicode string\" do\n    Digest::MD5.hexdigest(\"fooø\").should eq(\"d841c4eb31535db11faab98d10316b29\")\n  end\n\n  it \"calculates hash from UInt8 slices\" do\n    s = Bytes[0x66, 0x6f, 0x6f] # f,o,o\n    Digest::MD5.hexdigest(s).should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"calculates hash of #to_slice\" do\n    buffer = StaticArray(UInt8, 1).new(1_u8)\n    Digest::MD5.hexdigest(buffer).should eq(\"55a54008ad1ba589aa210d2629c1df41\")\n  end\n\n  it \"can take a block\" do\n    Digest::MD5.hexdigest do |ctx|\n      ctx.update \"f\"\n      ctx.update Bytes[0x6f, 0x6f]\n    end.should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"calculates base64'd hash from string\" do\n    Digest::MD5.base64digest(\"foo\").should eq(\"rL0Y20zC+Fzt72VPzMSk2A==\")\n  end\n\n  it \"resets\" do\n    digest = Digest::MD5.new\n    digest.update \"foo\"\n    digest.hexfinal.should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n\n    digest.reset\n    digest.update \"foo\"\n    digest.hexfinal.should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"#hexfinal\" do\n    digest = Digest::MD5.new\n    digest.update \"foo\"\n    dst = Bytes.new digest.digest_size * 2\n    digest.hexfinal(dst)\n    String.new(dst).should eq(\"acbd18db4cc2f85cedef654fccc4a4d8\")\n  end\n\n  it \"can't call final twice\" do\n    digest = Digest::MD5.new\n    digest.final\n    expect_raises(Digest::FinalizedError) do\n      digest.final\n    end\n    expect_raises(Digest::FinalizedError) do\n      digest.hexfinal\n    end\n  end\n\n  it \"return the digest size\" do\n    Digest::MD5.new.digest_size.should eq 16\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/sha1_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\nrequire \"digest/sha1\"\n\ndescribe Digest::SHA1 do\n  it_acts_as_digest_algorithm Digest::SHA1\n\n  [\n    {\"\", \"da39a3ee5e6b4b0d3255bfef95601890afd80709\", \"2jmj7l5rSw0yVb/vlWAYkK/YBwk=\"},\n    {\"The quick brown fox jumps over the lazy dog\", \"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\", \"L9ThxnotKPzthJ7hu3bnORuT6xI=\"},\n    {\"abc\", \"a9993e364706816aba3e25717850c26c9cd0d89d\", \"qZk+NkcGgWq6PiVxeFDCbJzQ2J0=\"},\n    {\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\", \"84983e441c3bd26ebaae4aa1f95129e5e54670f1\", \"hJg+RBw70m66rkqh+VEp5eVGcPE=\"},\n    {\"a\", \"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8\", \"hvfkN/qlp/zhXR3cuerq6jd2Z7g=\"},\n    {\"0123456701234567012345670123456701234567012345670123456701234567\", \"e0c094e867ef46c350ef54a7f59dd60bed92ae83\", \"4MCU6GfvRsNQ71Sn9Z3WC+2SroM=\"},\n    {\"fooø\", \"dcf4a1e3542b1a40a4ac2a3f7c92ffdb2d19812f\", \"3PSh41QrGkCkrCo/fJL/2y0ZgS8=\"},\n  ].each do |(string, hexstring, base64digest)|\n    it \"does digest for #{string.inspect}\" do\n      bytes = Digest::SHA1.digest(string)\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"resets\" do\n      digest = Digest::SHA1.new\n      digest.update string\n      digest.hexfinal.should eq(hexstring)\n\n      digest.reset\n      digest.update string\n      digest.hexfinal.should eq(hexstring)\n    end\n\n    it \"can't call #final more than once\" do\n      digest = Digest::SHA1.new\n      digest.final\n      expect_raises(Digest::FinalizedError) do\n        digest.final\n      end\n    end\n\n    it \"does digest for #{string.inspect} in a block\" do\n      bytes = Digest::SHA1.digest do |ctx|\n        string.each_char do |chr|\n          ctx.update chr.to_s\n        end\n      end\n\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"does hexdigest for #{string.inspect}\" do\n      Digest::SHA1.hexdigest(string).should eq(hexstring)\n    end\n\n    it \"does base64digest for #{string.inspect}\" do\n      Digest::SHA1.base64digest(string).should eq(base64digest)\n    end\n  end\n\n  it \"returns the digest_size\" do\n    Digest::SHA1.new.digest_size.should eq(20)\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/sha256_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\nrequire \"digest/sha256\"\n\ndescribe Digest::SHA256 do\n  it_acts_as_digest_algorithm Digest::SHA256\n\n  [\n    {\"\", \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\", \"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=\"},\n    {\"The quick brown fox jumps over the lazy dog\", \"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592\", \"16j7swfXgJRpypq8sAguT41WUeRtPNt2LQLQvzfJ5ZI=\"},\n    {\"abc\", \"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad\", \"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=\"},\n    {\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\", \"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1\", \"JI1qYdIGOLjlwCaTDD5gOaM85Flk/yFn9uzt1BnbBsE=\"},\n    {\"a\", \"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb\", \"ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs=\"},\n    {\"0123456701234567012345670123456701234567012345670123456701234567\", \"8182cadb21af0e37c06414ece08e19c65bdb22c396d48ba7341012eea9ffdfdd\", \"gYLK2yGvDjfAZBTs4I4ZxlvbIsOW1IunNBAS7qn/390=\"},\n    {\"fooø\", \"df81eea14671ce970fb1052e9f5dd6dbda652ed37423ed3624120ec1534784a7\", \"34HuoUZxzpcPsQUun13W29plLtN0I+02JBIOwVNHhKc=\"},\n  ].each do |(string, hexstring, base64digest)|\n    it \"does digest for #{string.inspect}\" do\n      bytes = Digest::SHA256.digest(string)\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"resets\" do\n      digest = Digest::SHA256.new\n      digest.update string\n      digest.final.hexstring.should eq(hexstring)\n\n      digest.reset\n      digest.update string\n      digest.final.hexstring.should eq(hexstring)\n    end\n\n    it \"can't call #final more than once\" do\n      digest = Digest::SHA256.new\n      digest.final\n      expect_raises(Digest::FinalizedError) do\n        digest.final\n      end\n    end\n\n    it \"does digest for #{string.inspect} in a block\" do\n      bytes = Digest::SHA256.digest do |ctx|\n        string.each_char do |chr|\n          ctx.update chr.to_s\n        end\n      end\n\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"does hexdigest for #{string.inspect}\" do\n      Digest::SHA256.hexdigest(string).should eq(hexstring)\n    end\n\n    it \"does base64digest for #{string.inspect}\" do\n      Digest::SHA256.base64digest(string).should eq(base64digest)\n    end\n  end\n\n  it \"returns the digest_size\" do\n    Digest::SHA256.new.digest_size.should eq(32)\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/sha512_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"./spec_helper\"\nrequire \"digest/sha512\"\n\ndescribe Digest::SHA512 do\n  it_acts_as_digest_algorithm Digest::SHA512\n\n  [\n    {\"\", \"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e\", \"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==\"},\n    {\"The quick brown fox jumps over the lazy dog\", \"07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6\", \"B+VH2VhvanP3P7rAQ17XaVEhj7fQyNeIownXhUNru2Quk6JSqVTyORJUfR6KO17W4b/XCXghIz+gU489uFT+5g==\"},\n    {\"abc\", \"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f\", \"3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw==\"},\n    {\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\", \"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445\", \"IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ==\"},\n    {\"a\", \"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75\", \"H0D8ktokFpR1CXnubPWC8tXX0o4YM13gWrxU0FYOD1MChgxlK/CNVgJSql50IQVG82n7u86MEs/HlXsmUv6adQ==\"},\n    {\"0123456701234567012345670123456701234567012345670123456701234567\", \"846e0ef73436438a4acb0ba7078cfe381f10a0f5edebcb985b3790086ef5e7ac5992ac9c23c77761c764bb3b1c25702d06b99955eb197d45b82fb3d124699d78\", \"hG4O9zQ2Q4pKywunB4z+OB8QoPXt68uYWzeQCG7156xZkqycI8d3YcdkuzscJXAtBrmZVesZfUW4L7PRJGmdeA==\"},\n    {\"fooø\", \"082907b85fe25c33bba4765185b52993a493cfd24454edf4b977ccd9301a890659c52592456cbd8aeb5215055d9dd4a7d50a4db9961715fb764fb6c393a83192\", \"CCkHuF/iXDO7pHZRhbUpk6STz9JEVO30uXfM2TAaiQZZxSWSRWy9iutSFQVdndSn1QpNuZYXFft2T7bDk6gxkg==\"},\n  ].each do |(string, hexstring, base64digest)|\n    it \"does digest for #{string.inspect}\" do\n      bytes = Digest::SHA512.digest(string)\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"resets\" do\n      digest = Digest::SHA512.new\n      digest.update string\n      digest.final.hexstring.should eq(hexstring)\n\n      digest.reset\n      digest.update string\n      digest.final.hexstring.should eq(hexstring)\n    end\n\n    it \"can't call #final more than once\" do\n      digest = Digest::SHA512.new\n      digest.final\n      expect_raises(Digest::FinalizedError) do\n        digest.final\n      end\n    end\n\n    it \"does digest for #{string.inspect} in a block\" do\n      bytes = Digest::SHA512.digest do |ctx|\n        string.each_char do |chr|\n          ctx.update chr.to_s\n        end\n      end\n\n      bytes.hexstring.should eq(hexstring)\n    end\n\n    it \"does .hexdigest for #{string.inspect}\" do\n      Digest::SHA512.hexdigest(string).should eq(hexstring)\n    end\n\n    it \"does #hexdigest for #{string.inspect}\" do\n      digest = Digest::SHA512.new\n      hdst = Bytes.new digest.digest_size * 2\n      digest.update string\n\n      digest.dup.hexfinal.should eq(hexstring)\n\n      digest.hexfinal(hdst)\n      String.new(hdst).should eq(hexstring)\n\n      expect_raises(Digest::FinalizedError) do\n        digest.final\n      end\n    end\n\n    it \"does base64digest for #{string.inspect}\" do\n      Digest::SHA512.base64digest(string).should eq(base64digest)\n    end\n  end\n\n  it \"returns the digest_size\" do\n    Digest::SHA512.new.digest_size.should eq(64)\n  end\nend\n"
  },
  {
    "path": "spec/std/digest/spec_helper.cr",
    "content": "def it_acts_as_digest_algorithm(type : T.class) forall T\n  it \"#hexdigest can update within a loop from explicit expr (#9483)\" do\n    i = 0\n    type.hexdigest do |digest|\n      while i < 3\n        digest.update(\"\")\n        i += 1\n      end\n    end\n  end\n\n  it \"#hexdigest can update within a loop by indirect expr (#9483)\" do\n    algorithm = {} of String => ::Digest::ClassMethods\n    algorithm[\"me\"] = type\n    i = 0\n    algorithm[\"me\"].hexdigest do |digest|\n      while i < 3\n        digest.update(\"\")\n        i += 1\n      end\n    end\n  end\n\n  it \"context are independent\" do\n    algorithm = type\n    res = algorithm.hexdigest do |digest|\n      digest.update(\"a\")\n      digest.update(\"b\")\n    end\n\n    inner_res = nil\n\n    outer_res = algorithm.hexdigest do |outer|\n      outer.update(\"a\")\n\n      inner_res = algorithm.hexdigest do |inner|\n        inner.update(\"a\")\n        inner.update(\"b\")\n      end\n\n      outer.update(\"b\")\n    end\n\n    outer_res.should eq(res)\n    inner_res.should eq(res)\n  end\n\n  describe \".dup\" do\n    it \"preserves type\" do\n      type.new.dup.class.should eq(type)\n    end\n\n    it \"preserves value\" do\n      digest1 = type.new\n      digest1.update(\"a\")\n      digest2 = digest1.dup\n\n      digest1.final.should eq(digest2.final)\n    end\n\n    it \"leads to not sharing state\" do\n      digest1 = type.new\n      digest1.update(\"a\")\n\n      digest2 = digest1.dup\n\n      digest1.update(\"b\")\n\n      digest1.final.should_not eq(digest2.final)\n    end\n\n    it \"leads to deterministic updates\" do\n      digest1 = type.new\n      digest1.update(\"a\")\n\n      digest2 = digest1.dup\n\n      digest1.update(\"b\")\n      digest2.update(\"b\")\n\n      digest1.final.should eq(digest2.final)\n    end\n  end\n\n  it \"digest with file content\" do\n    path = datapath(\"test_file.txt\")\n    type.new.file(path).final.should eq(type.digest(File.read(path)))\n  end\nend\n"
  },
  {
    "path": "spec/std/dir_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../support/env\"\n\nprivate def reset_tempdir(tmp_path = nil, &)\n  {% if flag?(:windows) %}\n    with_env(\"TMP\": tmp_path, \"TEMP\": nil, \"USERPROFILE\": nil) { yield }\n  {% else %}\n    with_env(\"TMPDIR\": tmp_path) { yield }\n  {% end %}\nend\n\n{% if flag?(:win32) %}\n  private def make_hidden(path)\n    wstr = Crystal::System.to_wstr(path)\n    attributes = LibC.GetFileAttributesW(wstr)\n    LibC.SetFileAttributesW(wstr, attributes | LibC::FILE_ATTRIBUTE_HIDDEN)\n  end\n\n  private def make_system(path)\n    wstr = Crystal::System.to_wstr(path)\n    attributes = LibC.GetFileAttributesW(wstr)\n    LibC.SetFileAttributesW(wstr, attributes | LibC::FILE_ATTRIBUTE_SYSTEM)\n  end\n{% end %}\n\nprivate def it_raises_on_null_byte(operation, &block)\n  it \"errors on #{operation}\" do\n    expect_raises(ArgumentError, \"String contains null byte\") do\n      block.call\n    end\n  end\nend\n\ndescribe \"Dir\" do\n  it \"tests exists? on existing directory\" do\n    Dir.exists?(datapath).should be_true\n  end\n\n  it \"tests exists? on existing file\" do\n    Dir.exists?(datapath(\"dir\", \"f1.txt\")).should be_false\n  end\n\n  it \"tests exists? on nonexistent directory\" do\n    Dir.exists?(datapath(\"foo\", \"bar\")).should be_false\n  end\n\n  it \"tests exists? on a directory path to a file\" do\n    Dir.exists?(datapath(\"dir\", \"f1.txt\", \"/\")).should be_false\n  end\n\n  describe \"empty?\" do\n    it \"tests empty? on a full directory\" do\n      Dir.empty?(datapath).should be_false\n    end\n\n    it \"tests empty? on an empty directory\" do\n      with_tempfile \"empty_directory\" do |path|\n        Dir.mkdir(path, 0o700)\n        Dir.empty?(path).should be_true\n      end\n    end\n\n    it \"tests empty? on nonexistent directory\" do\n      expect_raises(File::NotFoundError, \"Error opening directory: '#{datapath(\"foo\", \"bar\").inspect_unquoted}'\") do\n        Dir.empty?(datapath(\"foo\", \"bar\"))\n      end\n    end\n\n    it \"tests empty? on a directory path to a file\" do\n      expect_raises(File::Error, \"Error opening directory: '#{datapath(\"dir\", \"f1.txt\", \"/\").inspect_unquoted}'\") do\n        Dir.empty?(datapath(\"dir\", \"f1.txt\", \"/\"))\n      end\n    end\n  end\n\n  it \"tests info on existing directory\" do\n    Dir.open(datapath) do |dir|\n      info = dir.info\n      info.directory?.should be_true\n    end\n  end\n\n  it \"tests mkdir and delete with a new path\" do\n    with_tempfile(\"mkdir\") do |path|\n      Dir.mkdir(path, 0o700)\n      Dir.exists?(path).should be_true\n      Dir.delete(path)\n      Dir.exists?(path).should be_false\n    end\n  end\n\n  it \"tests mkdir and delete? with a new path\" do\n    with_tempfile(\"mkdir\") do |path|\n      Dir.mkdir(path, 0o700)\n      Dir.exists?(path).should be_true\n      Dir.delete?(path).should be_true\n      Dir.exists?(path).should be_false\n      Dir.delete?(path).should be_false\n    end\n  end\n\n  it \"tests mkdir and rmdir with a new path\" do\n    with_tempfile(\"mkdir\") do |path|\n      Dir.mkdir(path, 0o700)\n      Dir.exists?(path).should be_true\n      Dir.delete(path)\n      Dir.exists?(path).should be_false\n    end\n  end\n\n  it \"tests mkdir with an existing path\" do\n    expect_raises(File::AlreadyExistsError, \"Unable to create directory: '#{datapath.inspect_unquoted}'\") do\n      Dir.mkdir(datapath, 0o700)\n    end\n  end\n\n  describe \".mkdir_p\" do\n    it \"with a new path\" do\n      with_tempfile(\"mkdir_p-new\") do |path|\n        Dir.mkdir_p(path)\n        Dir.exists?(path).should be_true\n        path = File.join(path, \"a\", \"b\", \"c\")\n        Dir.mkdir_p(path)\n        Dir.exists?(path).should be_true\n      end\n    end\n\n    context \"path exists\" do\n      it \"fails when path is a file\" do\n        expect_raises(File::AlreadyExistsError, \"Unable to create directory: '#{datapath(\"test_file.txt\").inspect_unquoted}'\") do\n          Dir.mkdir_p(datapath(\"test_file.txt\"))\n        end\n      end\n\n      it \"noop when path is a directory\" do\n        Dir.exists?(datapath(\"dir\")).should be_true\n        Dir.mkdir_p(datapath(\"dir\"))\n        Dir.exists?(datapath(\"dir\")).should be_true\n      end\n    end\n  end\n\n  describe \".delete\" do\n    it \"raises with an nonexistent path\" do\n      with_tempfile(\"nonexistent\") do |path|\n        expect_raises(File::NotFoundError, \"Unable to remove directory: '#{path.inspect_unquoted}'\") do\n          Dir.delete(path)\n        end\n      end\n    end\n\n    it \"raises with a path that cannot be removed\" do\n      expect_raises(File::Error, \"Unable to remove directory: '#{datapath.inspect_unquoted}'\") do\n        Dir.delete(datapath)\n      end\n    end\n\n    it \"raises with symlink directory\" do\n      with_tempfile(\"delete-target-directory\", \"delete-symlink-directory\") do |target_path, symlink_path|\n        Dir.mkdir(target_path)\n        File.symlink(target_path, symlink_path)\n        expect_raises(File::Error) do\n          Dir.delete(symlink_path)\n        end\n      end\n    end\n\n    it \"deletes a read-only directory\" do\n      with_tempfile(\"delete-readonly-dir\") do |path|\n        Dir.mkdir(path)\n        File.chmod(path, 0o000)\n        Dir.delete(path)\n        Dir.exists?(path).should be_false\n      end\n    end\n  end\n\n  describe \"glob\" do\n    it \"tests glob with a single pattern\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*.txt\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ].sort\n    end\n\n    it \"tests glob with multiple patterns\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*.txt\", \"#{Path[datapath].to_posix}/dir/subdir/*.txt\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n        datapath(\"dir\", \"subdir\", \"f1.txt\"),\n      ].sort\n    end\n\n    it \"tests glob with a single pattern with block\" do\n      result = [] of String\n      Dir.glob(\"#{Path[datapath].to_posix}/dir/*.txt\") do |filename|\n        result << filename\n      end\n      result.sort.should eq([\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ].sort)\n    end\n\n    it \"tests a recursive glob\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/**/*.txt\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n        datapath(\"dir\", \"subdir\", \"f1.txt\"),\n        datapath(\"dir\", \"subdir\", \"subdir2\", \"f2.txt\"),\n      ].sort\n\n      Dir[\"#{Path[datapath].to_posix}/dir/**/subdir2/f2.txt\"].sort.should eq [\n        datapath(\"dir\", \"subdir\", \"subdir2\", \"f2.txt\"),\n      ].sort\n\n      Dir[\"#{Path[datapath].to_posix}/dir/**/subdir2/*.txt\"].sort.should eq [\n        datapath(\"dir\", \"subdir\", \"subdir2\", \"f2.txt\"),\n      ].sort\n    end\n\n    it \"tests double recursive matcher (#10807)\" do\n      with_tempdir \"glob-double-recurse\" do\n        path1 = Path[\"x\", \"b\", \"x\"]\n        Dir.mkdir_p path1\n        File.touch path1.join(\"file\")\n\n        Dir[\"**/b/**/*\"].sort.should eq [\n          path1.to_s,\n          path1.join(\"file\").to_s,\n        ].sort\n      end\n    end\n\n    it \"tests double recursive matcher, multiple paths\" do\n      with_tempdir \"glob-double-recurse2\" do\n        p1 = Path[\"x\", \"a\", \"x\", \"c\"]\n        p2 = Path[\"x\", \"a\", \"x\", \"a\", \"x\", \"c\"]\n\n        Dir.mkdir_p p1\n        Dir.mkdir_p p2\n\n        Dir[\"**/a/**/c\"].sort.should eq [\n          p1.to_s,\n          p2.to_s,\n        ].sort\n      end\n    end\n\n    it \"tests a recursive glob with '?'\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/f?.tx?\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"f3.txx\"),\n      ].sort\n    end\n\n    it \"tests a recursive glob with alternation\" do\n      Dir[\"#{Path[datapath].to_posix}/{dir,dir/subdir}/*.txt\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n        datapath(\"dir\", \"subdir\", \"f1.txt\"),\n      ].sort\n    end\n\n    it \"tests a glob with recursion inside alternation\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/{**/*.txt,**/*.txx}\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"f3.txx\"),\n        datapath(\"dir\", \"g2.txt\"),\n        datapath(\"dir\", \"subdir\", \"f1.txt\"),\n        datapath(\"dir\", \"subdir\", \"subdir2\", \"f2.txt\"),\n      ].sort\n    end\n\n    it \"tests a recursive glob with nested alternations\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/{?1.*,{f,g}2.txt}\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ].sort\n    end\n\n    it \"tests with []\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/[a-z][0-9].txt\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ].sort\n    end\n\n    it \"tests with {}\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/{f,g}{1,2,3}.tx{t,x}\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"f3.txx\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ].sort\n    end\n\n    {% if flag?(:windows) %}\n      pending \"tests with \\\\\"\n    {% else %}\n      it \"tests with \\\\\" do\n        with_tempdir \"glob-escape-pattern\" do\n          File.touch \"g1.txt\"\n          File.touch %q(\\g3)\n          File.touch %q(\\g4*)\n\n          Dir[%q(\\\\g*)].sort.should eq [\n            \"\\\\g3\",\n            \"\\\\g4*\",\n          ].sort\n\n          Dir[%q(*g?\\*)].sort.should eq [\n            \"\\\\g4*\",\n          ].sort\n        end\n      end\n    {% end %}\n\n    it \"tests with *\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*\"].sort.should eq [\n        datapath(\"dir\", \"dots\"),\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"f3.txx\"),\n        datapath(\"dir\", \"g2.txt\"),\n        datapath(\"dir\", \"subdir\"),\n        datapath(\"dir\", \"subdir2\"),\n      ].sort\n    end\n\n    it \"tests with ** (same as *)\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/**\"].sort.should eq [\n        datapath(\"dir\", \"dots\"),\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"f3.txx\"),\n        datapath(\"dir\", \"g2.txt\"),\n        datapath(\"dir\", \"subdir\"),\n        datapath(\"dir\", \"subdir2\"),\n      ].sort\n    end\n\n    it \"tests with */\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*/\"].sort.should eq [\n        datapath(\"dir\", \"dots\", \"\"),\n        datapath(\"dir\", \"subdir\", \"\"),\n        datapath(\"dir\", \"subdir2\", \"\"),\n      ].sort\n    end\n\n    it \"tests glob with a single pattern with extra slashes\" do\n      Dir[\"spec/std////data////dir////*.txt\"].sort.should eq [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ].sort\n    end\n\n    it \"tests with relative path\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*/\"].sort.should eq [\n        datapath(\"dir\", \"dots\", \"\"),\n        datapath(\"dir\", \"subdir\", \"\"),\n        datapath(\"dir\", \"subdir2\", \"\"),\n      ].sort\n    end\n\n    it \"tests with relative path (starts with .)\" do\n      Dir[\"./#{Path[datapath].to_posix}/dir/*/\"].sort.should eq [\n        File.join(\".\", \"spec\", \"std\", \"data\", \"dir\", \"dots\", \"\"),\n        File.join(\".\", \"spec\", \"std\", \"data\", \"dir\", \"subdir\", \"\"),\n        File.join(\".\", \"spec\", \"std\", \"data\", \"dir\", \"subdir2\", \"\"),\n      ].sort\n    end\n\n    it \"tests with relative path (starts with ..)\" do\n      Dir.cd(datapath) do\n        base_path = Path[\"..\", \"data\", \"dir\"]\n        Dir[\"#{base_path.to_posix}/*/\"].sort.should eq [\n          base_path.join(\"dots\", \"\").to_s,\n          base_path.join(\"subdir\", \"\").to_s,\n          base_path.join(\"subdir2\", \"\").to_s,\n        ].sort\n      end\n    end\n\n    it \"tests with relative path starting recursive\" do\n      Dir[\"**/dir/*/\"].sort.should eq [\n        datapath(\"dir\", \"dots\", \"\"),\n        datapath(\"dir\", \"subdir\", \"\"),\n        datapath(\"dir\", \"subdir2\", \"\"),\n      ].sort\n    end\n\n    it \"matches symlinks\" do\n      with_tempfile \"symlinks\" do |path|\n        Dir.mkdir_p(path)\n\n        link = Path[path, \"f1_link.txt\"]\n        non_link = Path[path, \"non_link.txt\"]\n\n        File.symlink(datapath(\"dir\", \"f1.txt\"), link)\n        File.symlink(datapath(\"dir\", \"nonexisting\"), non_link)\n\n        Dir[\"#{Path[path].to_posix}/*_link.txt\"].sort.should eq [\n          link.to_s,\n          non_link.to_s,\n        ].sort\n        Dir[\"#{Path[path].to_posix}/non_link.txt\"].should eq [non_link.to_s]\n      end\n    end\n\n    it \"matches symlink dir\" do\n      with_tempfile \"symlink_dir\" do |path|\n        target = Path[path, \"target\"]\n        non_link = target / \"a.txt\"\n        link_dir = Path[path, \"glob\", \"dir\"]\n\n        Dir.mkdir_p(Path[path, \"glob\"])\n        Dir.mkdir_p(target)\n\n        File.write(non_link, \"\")\n        File.symlink(target, link_dir)\n\n        Dir.glob(\"#{Path[path].to_posix}/glob/*/a.txt\").sort.should eq [] of String\n        Dir.glob(\"#{Path[path].to_posix}/glob/*/a.txt\", follow_symlinks: true).sort.should eq [\n          File.join(path, \"glob\", \"dir\", \"a.txt\"),\n        ]\n      end\n    end\n\n    it \"empty pattern\" do\n      Dir[\"\"].should eq [] of String\n    end\n\n    it \"root pattern\" do\n      {% if flag?(:windows) %}\n        Dir[\"C:/\"].should eq [\"C:\\\\\"]\n        Dir[\"/\"].should eq [Path[Dir.current].anchor.not_nil!.to_s]\n      {% else %}\n        Dir[\"/\"].should eq [\"/\"]\n      {% end %}\n    end\n\n    it \"pattern ending with ..\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/..\"].sort.should eq [\n        datapath(\"dir\", \"..\"),\n      ].sort\n    end\n\n    it \"pattern ending with */..\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*/..\"].sort.should eq [\n        datapath(\"dir\", \"dots\", \"..\"),\n        datapath(\"dir\", \"subdir\", \"..\"),\n        datapath(\"dir\", \"subdir2\", \"..\"),\n      ].sort\n    end\n\n    it \"pattern ending with .\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/.\"].sort.should eq [\n        datapath(\"dir\", \".\"),\n      ].sort\n    end\n\n    it \"pattern ending with */.\" do\n      Dir[\"#{Path[datapath].to_posix}/dir/*/.\"].sort.should eq [\n        datapath(\"dir\", \"dots\", \".\"),\n        datapath(\"dir\", \"subdir\", \".\"),\n        datapath(\"dir\", \"subdir2\", \".\"),\n      ].sort\n    end\n\n    context \"match: :dot_files / match_hidden\" do\n      it \"matches dot files\" do\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/**/*\", match: :dot_files).sort.should eq [\n          datapath(\"dir\", \"dots\", \".dot.hidden\"),\n          datapath(\"dir\", \"dots\", \".hidden\"),\n          datapath(\"dir\", \"dots\", \".hidden\", \"f1.txt\"),\n        ].sort\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/**/*\", match_hidden: true).sort.should eq [\n          datapath(\"dir\", \"dots\", \".dot.hidden\"),\n          datapath(\"dir\", \"dots\", \".hidden\"),\n          datapath(\"dir\", \"dots\", \".hidden\", \"f1.txt\"),\n        ].sort\n      end\n\n      it \"ignores hidden files\" do\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/*\", match: :none).should be_empty\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/*/\", match: :none).should be_empty\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/*\", match_hidden: false).should be_empty\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/*/\", match_hidden: false).should be_empty\n      end\n\n      it \"ignores hidden files recursively\" do\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/**/*\", match: :none).should be_empty\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/**/*/\", match: :none).should be_empty\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/**/*\", match_hidden: false).should be_empty\n        Dir.glob(\"#{Path[datapath].to_posix}/dir/dots/**/*/\", match_hidden: false).should be_empty\n      end\n    end\n\n    {% if flag?(:win32) %}\n      it \"respects `NativeHidden` and `OSHidden`\" do\n        with_tempfile(\"glob-system-hidden\") do |path|\n          FileUtils.mkdir_p(path)\n\n          visible_txt = File.join(path, \"visible.txt\")\n          hidden_txt = File.join(path, \"hidden.txt\")\n          system_txt = File.join(path, \"system.txt\")\n          system_hidden_txt = File.join(path, \"system_hidden.txt\")\n\n          File.write(visible_txt, \"\")\n          File.write(hidden_txt, \"\")\n          File.write(system_txt, \"\")\n          File.write(system_hidden_txt, \"\")\n          make_hidden(hidden_txt)\n          make_hidden(system_hidden_txt)\n          make_system(system_txt)\n          make_system(system_hidden_txt)\n\n          visible_dir = File.join(path, \"visible_dir\")\n          hidden_dir = File.join(path, \"hidden_dir\")\n          system_dir = File.join(path, \"system_dir\")\n          system_hidden_dir = File.join(path, \"system_hidden_dir\")\n\n          Dir.mkdir(visible_dir)\n          Dir.mkdir(hidden_dir)\n          Dir.mkdir(system_dir)\n          Dir.mkdir(system_hidden_dir)\n          make_hidden(hidden_dir)\n          make_hidden(system_hidden_dir)\n          make_system(system_dir)\n          make_system(system_hidden_dir)\n\n          inside_visible = File.join(visible_dir, \"inside.txt\")\n          inside_hidden = File.join(hidden_dir, \"inside.txt\")\n          inside_system = File.join(system_dir, \"inside.txt\")\n          inside_system_hidden = File.join(system_hidden_dir, \"inside.txt\")\n\n          File.write(inside_visible, \"\")\n          File.write(inside_hidden, \"\")\n          File.write(inside_system, \"\")\n          File.write(inside_system_hidden, \"\")\n\n          expected = [visible_txt, visible_dir, inside_visible, system_txt, system_dir, inside_system].sort!\n          expected_hidden = (expected + [hidden_txt, hidden_dir, inside_hidden]).sort!\n          expected_system_hidden = (expected_hidden + [system_hidden_txt, system_hidden_dir, inside_system_hidden]).sort!\n\n          Dir.glob(\"#{Path[path].to_posix}/**/*\", match: :none).sort.should eq(expected)\n          Dir.glob(\"#{Path[path].to_posix}/**/*\", match: :native_hidden).sort.should eq(expected_hidden)\n          Dir.glob(\"#{Path[path].to_posix}/**/*\", match: :os_hidden).sort.should eq(expected)\n          Dir.glob(\"#{Path[path].to_posix}/**/*\", match: File::MatchOptions[NativeHidden, OSHidden]).sort.should eq(expected_system_hidden)\n        end\n      end\n    {% end %}\n\n    context \"with path\" do\n      expected = [\n        datapath(\"dir\", \"f1.txt\"),\n        datapath(\"dir\", \"f2.txt\"),\n        datapath(\"dir\", \"g2.txt\"),\n      ]\n\n      it \"posix path\" do\n        Dir[Path.posix(Path[datapath].to_posix, \"dir\", \"*.txt\")].sort.should eq expected\n        Dir[[Path.posix(Path[datapath].to_posix, \"dir\", \"*.txt\")]].sort.should eq expected\n      end\n\n      it \"windows path\" do\n        Dir[Path.windows(datapath, \"dir\", \"*.txt\")].sort.should eq expected\n        Dir[[Path.windows(datapath, \"dir\", \"*.txt\")]].sort.should eq expected\n      end\n    end\n  end\n\n  describe \"cd\" do\n    it \"accepts string\" do\n      cwd = Dir.current\n      Dir.cd(\"..\")\n      Dir.current.should_not eq(cwd)\n      Dir.cd(cwd)\n      Dir.current.should eq(cwd)\n    end\n\n    it \"accepts path\" do\n      cwd = Dir.current\n      Dir.cd(Path.new(\"..\"))\n      Dir.current.should_not eq(cwd)\n      Dir.cd(cwd)\n      Dir.current.should eq(cwd)\n    end\n\n    it \"raises\" do\n      expect_raises(File::NotFoundError, \"Error while changing directory: '/nope'\") do\n        Dir.cd(\"/nope\")\n      end\n    end\n\n    it \"accepts a block with path\" do\n      cwd = Dir.current\n\n      Dir.cd(Path.new(\"..\")) do\n        Dir.current.should_not eq(cwd)\n      end\n\n      Dir.current.should eq(cwd)\n    end\n\n    it \"accepts a block with string\" do\n      cwd = Dir.current\n\n      Dir.cd(\"..\") do\n        Dir.current.should_not eq(cwd)\n      end\n\n      Dir.current.should eq(cwd)\n    end\n  end\n\n  describe \".current\" do\n    # can't use backtick in interpreted code (#12241)\n    pending_interpreted \"matches shell\" do\n      Dir.current.should eq(`#{{{ flag?(:win32) ? \"cmd /c cd\" : \"pwd\" }}}`.chomp)\n    end\n\n    # Skip spec on Windows due to weak support for symlinks and $PWD.\n    {% unless flag?(:win32) %}\n      it \"follows $PWD\" do\n        with_tempfile \"current-pwd\" do |path|\n          Dir.mkdir_p path\n          # Resolve any symbolic links in path caused by tmpdir being a link.\n          # For example on macOS, /tmp is a symlink to /private/tmp.\n          path = File.realpath(path)\n\n          target_path = File.join(path, \"target\")\n          link_path = File.join(path, \"link\")\n          Dir.mkdir_p target_path\n          File.symlink(target_path, link_path)\n\n          Dir.cd(link_path) do\n            with_env({\"PWD\" => nil}) do\n              Dir.current.should eq target_path\n            end\n\n            with_env({\"PWD\" => link_path}) do\n              Dir.current.should eq link_path\n            end\n\n            with_env({\"PWD\" => \"/some/other/path\"}) do\n              Dir.current.should eq target_path\n            end\n          end\n        end\n      end\n    {% end %}\n  end\n\n  describe \".tempdir\" do\n    it \"returns default directory for tempfiles\" do\n      reset_tempdir do\n        {% if flag?(:windows) %}\n          # GetTempPathW defaults to the Windows directory when %TMP%, %TEMP%\n          # and %USERPROFILE% are not set.\n          # Without going further into the implementation details, simply\n          # verifying that the directory exits is sufficient.\n          Dir.exists?(Dir.tempdir).should be_true\n        {% else %}\n          # POSIX implementation is in Crystal::System::Dir and defaults to\n          # `/tmp` when $TMPDIR is not set.\n          Dir.tempdir.should eq \"/tmp\"\n        {% end %}\n      end\n    end\n\n    it \"returns configure directory for tempfiles\" do\n      tmp_path = Path[\"my_temporary_path\"].expand.to_s\n      reset_tempdir(tmp_path) do\n        Dir.tempdir.should eq tmp_path\n      end\n    end\n  end\n\n  it \"opens with new\" do\n    filenames = [] of String\n\n    dir = Dir.new(datapath(\"dir\"))\n    dir.each do |filename|\n      filenames << filename\n    end.should be_nil\n    dir.close\n\n    filenames.should contain(\"f1.txt\")\n  end\n\n  it \"opens with open\" do\n    filenames = [] of String\n\n    Dir.open(datapath(\"dir\")) do |dir|\n      dir.each do |filename|\n        filenames << filename\n      end.should be_nil\n    end\n\n    filenames.should contain(\"f1.txt\")\n  end\n\n  describe \"#path\" do\n    it \"returns init value\" do\n      path = datapath(\"dir\")\n      dir = Dir.new(path)\n      dir.path.should eq path\n    end\n  end\n\n  it \"lists entries\" do\n    filenames = Dir.entries(datapath(\"dir\"))\n    filenames.should contain(\".\")\n    filenames.should contain(\"..\")\n    filenames.should contain(\"f1.txt\")\n  end\n\n  it \"lists children\" do\n    Dir.children(datapath(\"dir\")).should eq(Dir.entries(datapath(\"dir\")) - %w(. ..))\n  end\n\n  it \"does to_s\" do\n    Dir.new(datapath(\"dir\")).to_s.should eq(\"#<Dir:#{datapath(\"dir\")}>\")\n  end\n\n  it \"gets dir iterator\" do\n    filenames = [] of String\n\n    iter = Dir.new(datapath(\"dir\")).each\n    iter.each do |filename|\n      filenames << filename\n    end\n\n    filenames.should contain(\".\")\n    filenames.should contain(\"..\")\n    filenames.should contain(\"f1.txt\")\n  end\n\n  it \"gets child iterator\" do\n    filenames = [] of String\n\n    iter = Dir.new(datapath(\"dir\")).each_child\n    iter.each do |filename|\n      filenames << filename\n    end\n\n    filenames.should_not contain(\".\")\n    filenames.should_not contain(\"..\")\n    filenames.should contain(\"f1.txt\")\n  end\n\n  it \"double close doesn't error\" do\n    dir = Dir.open(datapath(\"dir\")) do |dir|\n      dir.close\n      dir.close\n    end\n  end\n\n  describe \"raises on null byte\" do\n    it_raises_on_null_byte \"new\" do\n      Dir.new(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"cd\" do\n      Dir.cd(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"exists?\" do\n      Dir.exists?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"mkdir\" do\n      Dir.mkdir(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"mkdir_p\" do\n      Dir.mkdir_p(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"delete\" do\n      Dir.delete(\"foo\\0bar\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/double_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Double\" do\n  describe \"**\" do\n    it { (2.5 ** 2).should be_close(6.25, 0.0001) }\n    it { (2.5 ** 2.5_f32).should be_close(9.882117688026186, 0.0001) }\n    it { (2.5 ** 2.5).should be_close(9.882117688026186, 0.0001) }\n  end\nend\n"
  },
  {
    "path": "spec/std/ecr/ecr_lexer_spec.cr",
    "content": "require \"spec\"\nrequire \"ecr/lexer\"\n\nprivate def t(type : ECR::Lexer::Token::Type)\n  type\nend\n\ndescribe \"ECR::Lexer\" do\n  it \"lexes without interpolation\" do\n    lexer = ECR::Lexer.new(\"hello\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <% %>\" do\n    lexer = ECR::Lexer.new(\"hello <% foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\" foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(9)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(16)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%- %>\" do\n    lexer = ECR::Lexer.new(\"<%- foo %>\")\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\" foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(4)\n    token.suppress_leading?.should be_true\n    token.suppress_trailing?.should be_false\n  end\n\n  it \"lexes with <% -%>\" do\n    lexer = ECR::Lexer.new(\"<% foo -%>\")\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\" foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(3)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_true\n  end\n\n  it \"lexes with -% inside string\" do\n    lexer = ECR::Lexer.new(\"<% \\\"-%\\\" %>\")\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\" \\\"-%\\\" \")\n  end\n\n  it \"lexes with <%= %>\" do\n    lexer = ECR::Lexer.new(\"hello <%= foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(ECR::Lexer::Token::Type::Output)\n    token.value.should eq(\" foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(10)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(17)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%= -%>\" do\n    lexer = ECR::Lexer.new(\"<%= foo -%>\")\n\n    token = lexer.next_token\n    token.type.should eq(ECR::Lexer::Token::Type::Output)\n    token.value.should eq(\" foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(4)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_true\n  end\n\n  it \"lexes with <%# %>\" do\n    lexer = ECR::Lexer.new(\"hello <%# foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\"# foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(9)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(17)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%# -%>\" do\n    lexer = ECR::Lexer.new(\"<%# foo -%>\")\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\"# foo \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(3)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_true\n  end\n\n  it \"lexes with <%% %>\" do\n    lexer = ECR::Lexer.new(\"hello <%% foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"<% foo %>\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(10)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(17)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%%= %>\" do\n    lexer = ECR::Lexer.new(\"hello <%%= foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"<%= foo %>\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(10)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(18)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%-% %> (#14734)\" do\n    lexer = ECR::Lexer.new(\"hello <%-% foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"<%- foo %>\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(11)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(18)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%-%= %> (#14734)\" do\n    lexer = ECR::Lexer.new(\"hello <%-%= foo %> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"<%-= foo %>\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(11)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(19)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <%% -%> (#14734)\" do\n    lexer = ECR::Lexer.new(\"hello <%% foo -%> bar\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hello \")\n    token.column_number.should eq(1)\n    token.line_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"<% foo -%>\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(10)\n    token.suppress_leading?.should be_false\n    token.suppress_trailing?.should be_false\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" bar\")\n    token.line_number.should eq(1)\n    token.column_number.should eq(18)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\n\n  it \"lexes with <% %> and correct location info\" do\n    lexer = ECR::Lexer.new(\"hi\\nthere <% foo\\nbar %> baz\")\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\"hi\\nthere \")\n    token.line_number.should eq(1)\n    token.column_number.should eq(1)\n\n    token = lexer.next_token\n    token.type.should eq(t :control)\n    token.value.should eq(\" foo\\nbar \")\n    token.line_number.should eq(2)\n    token.column_number.should eq(9)\n\n    token = lexer.next_token\n    token.type.should eq(t :string)\n    token.value.should eq(\" baz\")\n    token.line_number.should eq(3)\n    token.column_number.should eq(7)\n\n    token = lexer.next_token\n    token.type.should eq(t :eof)\n  end\nend\n"
  },
  {
    "path": "spec/std/ecr/ecr_spec.cr",
    "content": "require \"spec\"\nrequire \"ecr\"\nrequire \"ecr/processor\"\n\nprivate class ECRSpecHelloView\n  @msg : String\n\n  def initialize(@msg)\n  end\n\n  ECR.def_to_s \"#{__DIR__}/../data/test_template.ecr\"\nend\n\ndescribe \"ECR\" do\n  it \"builds a crystal program from a source\" do\n    program = ECR.process_string \"hello <%= 1 %> wor\\nld <% while true %> 2 <% end %>\\n<%# skip %> <%% \\\"string\\\" %>\", \"foo.cr\"\n\n    pieces = [\n      %(__str__ << \"hello \"),\n      %(#<loc:push>(#<loc:\"foo.cr\",1,10> 1 )#<loc:pop>.to_s __str__),\n      %(__str__ << \" wor\\\\nld \"),\n      %(#<loc:push>#<loc:\"foo.cr\",2,6> while true #<loc:pop>),\n      %(__str__ << \" 2 \"),\n      %(#<loc:push>#<loc:\"foo.cr\",2,25> end #<loc:pop>),\n      %(__str__ << \"\\\\n\"),\n      %(#<loc:push>#<loc:\"foo.cr\",3,3> # skip #<loc:pop>),\n      %(__str__ << \" \"),\n      %(__str__ << \"<% \\\\\"string\\\\\" %>\"),\n    ]\n    program.should eq(pieces.join('\\n') + '\\n')\n  end\n\n  it \"escapes quotes in filename\" do\n    program = ECR.process_string \"<%= 123 %>\", %(\"foo\".cr)\n    program.should eq <<-CRYSTAL\n      #<loc:push>(#<loc:\"\\\\\"foo\\\\\".cr\",1,4> 123 )#<loc:pop>.to_s __str__\\n\n      CRYSTAL\n  end\n\n  it \"does ECR.def_to_s\" do\n    view = ECRSpecHelloView.new(\"world!\")\n    view.to_s.strip.should eq(\"Hello world! 012\")\n  end\n\n  it \"does with <%= -%>\" do\n    io = IO::Memory.new\n    ECR.embed \"#{__DIR__}/../data/test_template2.ecr\", io\n    io.to_s.should eq(\"123\")\n  end\n\n  it \"does with <%- %> (1)\" do\n    io = IO::Memory.new\n    ECR.embed \"#{__DIR__}/../data/test_template3.ecr\", io\n    io.to_s.should eq(\"01\")\n  end\n\n  it \"does with <%- %> (2)\" do\n    io = IO::Memory.new\n    ECR.embed \"#{__DIR__}/../data/test_template4.ecr\", io\n    io.to_s.should eq(\"hi\\n01\")\n  end\n\n  it \"does with <% -%>\" do\n    io = IO::Memory.new\n    ECR.embed \"#{__DIR__}/../data/test_template5.ecr\", io\n    io.to_s.should eq(\"hi\\n      0\\n      1\\n  \")\n  end\n\n  it \"does with -% inside string\" do\n    io = IO::Memory.new\n    ECR.embed \"#{__DIR__}/../data/test_template6.ecr\", io\n    io.to_s.should eq(\"string with -%\\n\")\n  end\n\n  it \"does with <%% %>\" do\n    io = IO::Memory.new\n    ECR.embed \"#{__DIR__}/../data/test_template7.ecr\", io\n    io.to_s.should eq(<<-ECR)\n      <% if @name %>\n      Greetings, <%= @name %>!\n        <%- else -%>\n      Greetings!\n      <%- end -%>\n\n      ECR\n  end\n\n  it \".render\" do\n    ECR.render(\"#{__DIR__}/../data/test_template2.ecr\").should eq(\"123\")\n  end\nend\n"
  },
  {
    "path": "spec/std/enum_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/string\"\n\nenum SpecEnum : Int8\n  One\n  Two\n  Three\nend\n\nenum SpecEnum2\n  FortyTwo\n  FORTY_FOUR\nend\n\nprivate enum PrivateEnum\n  FOO = 0\n  BAR = 1\n  BAZ = 2\n  QUX = 0\nend\n\n@[Flags]\nenum SpecEnumFlags\n  One\n  Two\n  Three\nend\n\n@[Flags]\nenum SpecEnumFlags8 : Int8\n  One\n  Two\n  Three\nend\n\n@[Flags]\nprivate enum PrivateFlagsEnum\n  FOO\n  BAR\n  BAZ\nend\n\nenum SpecBigEnum : Int64\n  TooBig = 4294967296i64 # == 2**32\nend\n\nprivate enum SpecEnumWithCaseSensitiveMembers\n  FOO = 1\n  Foo = 2\nend\n\ndescribe Enum do\n  describe \"#to_s\" do\n    it \"for simple enum\" do\n      assert_prints SpecEnum::One.to_s, \"One\"\n      assert_prints SpecEnum::Two.to_s, \"Two\"\n      assert_prints SpecEnum::Three.to_s, \"Three\"\n      assert_prints SpecEnum.new(127).to_s, \"127\"\n    end\n\n    it \"for flags enum\" do\n      assert_prints SpecEnumFlags::None.to_s, \"None\"\n      assert_prints SpecEnumFlags::All.to_s, \"All\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags::Two).to_s, \"One | Two\"\n      assert_prints SpecEnumFlags.new(128).to_s, \"128\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags.new(128)).to_s, \"One | 128\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags.new(8) | SpecEnumFlags.new(16)).to_s, \"One | 24\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags::Two | SpecEnumFlags.new(16)).to_s, \"One | Two | 16\"\n    end\n\n    it \"for private enum\" do\n      assert_prints PrivateEnum::FOO.to_s, \"FOO\"\n      assert_prints PrivateFlagsEnum::FOO.to_s, \"FOO\"\n      assert_prints PrivateEnum::QUX.to_s, \"FOO\"\n      assert_prints (PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAZ).to_s, \"FOO | BAZ\"\n      assert_prints PrivateFlagsEnum.new(128).to_s, \"128\"\n      assert_prints (PrivateFlagsEnum::FOO | PrivateFlagsEnum.new(128)).to_s, \"FOO | 128\"\n    end\n  end\n\n  describe \"#inspect\" do\n    it \"for simple enum\" do\n      assert_prints SpecEnum::One.inspect, \"SpecEnum::One\"\n      assert_prints SpecEnum::Two.inspect, \"SpecEnum::Two\"\n      assert_prints SpecEnum::Three.inspect, \"SpecEnum::Three\"\n      assert_prints SpecEnum.new(127).inspect, \"SpecEnum[127]\"\n    end\n\n    it \"for flags enum\" do\n      assert_prints SpecEnumFlags::None.inspect, \"SpecEnumFlags::None\"\n      assert_prints SpecEnumFlags::All.inspect, \"SpecEnumFlags::All\"\n      assert_prints (SpecEnumFlags::One).inspect, \"SpecEnumFlags::One\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags::Two).inspect, \"SpecEnumFlags[One, Two]\"\n      assert_prints SpecEnumFlags.new(128).inspect, \"SpecEnumFlags[128]\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags.new(128)).inspect, \"SpecEnumFlags[One, 128]\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags.new(8) | SpecEnumFlags.new(16)).inspect, \"SpecEnumFlags[One, 24]\"\n      assert_prints (SpecEnumFlags::One | SpecEnumFlags::Two | SpecEnumFlags.new(16)).inspect, \"SpecEnumFlags[One, Two, 16]\"\n    end\n\n    it \"for private enum\" do\n      assert_prints PrivateEnum::FOO.inspect, \"PrivateEnum::FOO\"\n      assert_prints PrivateFlagsEnum::FOO.inspect, \"PrivateFlagsEnum::FOO\"\n      assert_prints PrivateEnum::QUX.inspect, \"PrivateEnum::FOO\"\n      assert_prints (PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAZ).inspect, \"PrivateFlagsEnum[FOO, BAZ]\"\n      assert_prints PrivateFlagsEnum.new(128).inspect, \"PrivateFlagsEnum[128]\"\n      assert_prints (PrivateFlagsEnum::FOO | PrivateFlagsEnum.new(128)).inspect, \"PrivateFlagsEnum[FOO, 128]\"\n    end\n  end\n\n  it \"creates an enum instance from an auto-casted symbol (#8573)\" do\n    enum_value = SpecEnum.new(:two)\n    enum_value.should eq SpecEnum::Two\n\n    SpecEnumWithCaseSensitiveMembers.new(:foo).should eq SpecEnumWithCaseSensitiveMembers::FOO\n    SpecEnumWithCaseSensitiveMembers.new(:Foo).should eq SpecEnumWithCaseSensitiveMembers::FOO\n    SpecEnumWithCaseSensitiveMembers.new(:FOO).should eq SpecEnumWithCaseSensitiveMembers::FOO\n  end\n\n  it \"gets value\" do\n    SpecEnum::Two.value.should eq(1)\n    SpecEnum::Two.value.should be_a(Int8)\n  end\n\n  it \"gets value with to_i\" do\n    SpecEnum::Two.to_i.should eq(1)\n    SpecEnum::Two.to_i.should be_a(Int32)\n  end\n\n  it \"gets value with to_i<bit>\" do\n    SpecEnum::Two.to_i8.should eq(1)\n    SpecEnum::Two.to_i8.should be_a(Int8)\n\n    SpecEnum::Two.to_i16.should eq(1)\n    SpecEnum::Two.to_i16.should be_a(Int16)\n\n    SpecEnum::Two.to_i32.should eq(1)\n    SpecEnum::Two.to_i32.should be_a(Int32)\n\n    SpecEnum::Two.to_i64.should eq(1)\n    SpecEnum::Two.to_i64.should be_a(Int64)\n\n    SpecEnum::Two.to_i128.should eq(1)\n    SpecEnum::Two.to_i128.should be_a(Int128)\n  end\n\n  it \"gets value with to_u<bit>\" do\n    SpecEnum::Two.to_u8.should eq(1)\n    SpecEnum::Two.to_u8.should be_a(UInt8)\n\n    SpecEnum::Two.to_u16.should eq(1)\n    SpecEnum::Two.to_u16.should be_a(UInt16)\n\n    SpecEnum::Two.to_u32.should eq(1)\n    SpecEnum::Two.to_u32.should be_a(UInt32)\n\n    SpecEnum::Two.to_u64.should eq(1)\n    SpecEnum::Two.to_u64.should be_a(UInt64)\n\n    SpecEnum::Two.to_u128.should eq(1)\n    SpecEnum::Two.to_u128.should be_a(UInt128)\n  end\n\n  it \"does +\" do\n    (SpecEnum::One + 1).should eq(SpecEnum::Two)\n  end\n\n  it \"does -\" do\n    (SpecEnum::Two - 1).should eq(SpecEnum::One)\n  end\n\n  it \"sorts\" do\n    [SpecEnum::Three, SpecEnum::One, SpecEnum::Two].sort.should eq([SpecEnum::One, SpecEnum::Two, SpecEnum::Three])\n  end\n\n  it \"does includes?\" do\n    (SpecEnumFlags::One | SpecEnumFlags::Two).includes?(SpecEnumFlags::One).should be_true\n    (SpecEnumFlags::One | SpecEnumFlags::Two).includes?(SpecEnumFlags::Three).should be_false\n    SpecEnumFlags::One.includes?(SpecEnumFlags::None).should be_true\n    SpecEnumFlags::None.includes?(SpecEnumFlags::None).should be_true\n    SpecEnumFlags::None.includes?(SpecEnumFlags::One).should be_false\n    SpecEnumFlags::One.includes?(SpecEnumFlags::One | SpecEnumFlags::Two).should be_false\n    (SpecEnumFlags::One | SpecEnumFlags::Two).includes?(SpecEnumFlags::One | SpecEnumFlags::Two).should be_true\n    (SpecEnumFlags::One | SpecEnumFlags::Two | SpecEnumFlags::Three).includes?(SpecEnumFlags::One | SpecEnumFlags::Two).should be_true\n  end\n\n  describe \"each\" do\n    it \"won't yield None\" do\n      SpecEnumFlags::None.each do |name|\n        raise \"unexpected yield\"\n      end\n    end\n\n    it \"won't yield All\" do\n      SpecEnumFlags::All.each do |name|\n        raise \"unexpected yield\" if name == SpecEnumFlags::All\n      end\n    end\n\n    it \"yields each member\" do\n      names = [] of SpecEnumFlags\n      values = [] of Int32\n      SpecEnumFlags.flags(One, Three).each do |name, value|\n        names << name\n        values << value\n      end\n      names.should eq([SpecEnumFlags::One, SpecEnumFlags::Three])\n      values.should eq([SpecEnumFlags::One.value, SpecEnumFlags::Three.value])\n    end\n\n    it \"private enum\" do\n      names = [] of PrivateFlagsEnum\n      values = [] of Int32\n      (PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAZ).each do |name, value|\n        names << name\n        values << value\n      end\n      names.should eq([PrivateFlagsEnum::FOO, PrivateFlagsEnum::BAZ])\n      values.should eq([PrivateFlagsEnum::FOO.value, PrivateFlagsEnum::BAZ.value])\n    end\n  end\n\n  describe \"names\" do\n    it \"for simple enum\" do\n      SpecEnum.names.should eq(%w(One Two Three))\n    end\n\n    it \"for flags enum\" do\n      SpecEnumFlags.names.should eq(%w(One Two Three))\n    end\n  end\n\n  describe \"values\" do\n    it \"for simple enum\" do\n      SpecEnum.values.should eq([SpecEnum::One, SpecEnum::Two, SpecEnum::Three])\n    end\n\n    it \"for flags enum\" do\n      SpecEnumFlags.values.should eq([SpecEnumFlags::One, SpecEnumFlags::Two, SpecEnumFlags::Three])\n    end\n  end\n\n  describe \"from_value?\" do\n    it \"for simple enum\" do\n      SpecEnum.from_value?(0).should eq(SpecEnum::One)\n      SpecEnum.from_value?(1).should eq(SpecEnum::Two)\n      SpecEnum.from_value?(1_i8).should eq(SpecEnum::Two)\n      SpecEnum.from_value?(2).should eq(SpecEnum::Three)\n      SpecEnum.from_value?(3).should be_nil\n    end\n\n    it \"for flags enum\" do\n      SpecEnumFlags.from_value?(0).should eq(SpecEnumFlags::None)\n      SpecEnumFlags.from_value?(1).should eq(SpecEnumFlags::One)\n      SpecEnumFlags.from_value?(1_i8).should eq(SpecEnumFlags::One)\n      SpecEnumFlags.from_value?(2).should eq(SpecEnumFlags::Two)\n      SpecEnumFlags.from_value?(3).should eq(SpecEnumFlags::One | SpecEnumFlags::Two)\n      SpecEnumFlags.from_value?(8).should be_nil\n      SpecEnumFlags8.from_value?(1_i8).should eq(SpecEnumFlags8::One)\n    end\n  end\n\n  describe \"from_value\" do\n    it \"for simple enum\" do\n      SpecEnum.from_value(0).should eq(SpecEnum::One)\n      SpecEnum.from_value(1).should eq(SpecEnum::Two)\n      SpecEnum.from_value(2).should eq(SpecEnum::Three)\n      expect_raises(ArgumentError, \"Unknown enum SpecEnum value: 3\") { SpecEnum.from_value(3) }\n    end\n\n    it \"for flags enum\" do\n      SpecEnumFlags.from_value(0).should eq(SpecEnumFlags::None)\n      SpecEnumFlags.from_value(1).should eq(SpecEnumFlags::One)\n      SpecEnumFlags.from_value(2).should eq(SpecEnumFlags::Two)\n      SpecEnumFlags.from_value(3).should eq(SpecEnumFlags::One | SpecEnumFlags::Two)\n      expect_raises(ArgumentError, \"Unknown enum SpecEnumFlags value: 8\") { SpecEnumFlags.from_value(8) }\n    end\n\n    it \"for private enum\" do\n      PrivateEnum.from_value(0).should eq(PrivateEnum::FOO)\n    end\n  end\n\n  describe \"valid?\" do\n    it \"for simple enum\" do\n      SpecEnum.valid?(SpecEnum::One).should be_true\n      SpecEnum.valid?(SpecEnum::Two).should be_true\n      SpecEnum.valid?(SpecEnum::Three).should be_true\n      SpecEnum.valid?(SpecEnum.new(3i8)).should be_false\n    end\n\n    it \"for flags enum\" do\n      SpecEnumFlags.valid?(SpecEnumFlags::One).should be_true\n      SpecEnumFlags.valid?(SpecEnumFlags::Two).should be_true\n      SpecEnumFlags.valid?(SpecEnumFlags::One | SpecEnumFlags::Two).should be_true\n      SpecEnumFlags.valid?(SpecEnumFlags.new(8)).should be_false\n      SpecEnumFlags.valid?(SpecEnumFlags::None).should be_true\n      SpecEnumFlags.valid?(SpecEnumFlags::All).should be_true\n    end\n\n    it \"for Int64 enum\" do\n      SpecBigEnum.valid?(SpecBigEnum::TooBig).should be_true\n      SpecBigEnum.valid?(SpecBigEnum.new(0i64)).should be_false\n    end\n  end\n\n  it \"has hash\" do\n    SpecEnum::Two.hash.should_not eq(SpecEnum::Three.hash)\n  end\n\n  it \".parse\" do\n    SpecEnum.parse(\"Two\").should eq(SpecEnum::Two)\n    SpecEnum2.parse(\"FortyTwo\").should eq(SpecEnum2::FortyTwo)\n    SpecEnum2.parse(\"forty_two\").should eq(SpecEnum2::FortyTwo)\n    expect_raises(ArgumentError, \"Unknown enum SpecEnum value: Four\") { SpecEnum.parse(\"Four\") }\n\n    SpecEnum.parse(\"TWO\").should eq(SpecEnum::Two)\n    SpecEnum.parse(\"TwO\").should eq(SpecEnum::Two)\n    SpecEnum2.parse(\"FORTY_TWO\").should eq(SpecEnum2::FortyTwo)\n    SpecEnum2.parse(\"FORTY___TWO\").should eq(SpecEnum2::FortyTwo)\n    SpecEnum2.parse(\"FORTY___TWO_\").should eq(SpecEnum2::FortyTwo)\n\n    SpecEnum2.parse(\"FORTY_FOUR\").should eq(SpecEnum2::FORTY_FOUR)\n    SpecEnum2.parse(\"forty_four\").should eq(SpecEnum2::FORTY_FOUR)\n    SpecEnum2.parse(\"FORTY-FOUR\").should eq(SpecEnum2::FORTY_FOUR)\n    SpecEnum2.parse(\"forty-four\").should eq(SpecEnum2::FORTY_FOUR)\n    SpecEnum2.parse(\"FortyFour\").should eq(SpecEnum2::FORTY_FOUR)\n    SpecEnum2.parse(\"FORTYFOUR\").should eq(SpecEnum2::FORTY_FOUR)\n    SpecEnum2.parse(\"fortyfour\").should eq(SpecEnum2::FORTY_FOUR)\n\n    PrivateEnum.parse(\"FOO\").should eq(PrivateEnum::FOO)\n    PrivateEnum.parse(\"BAR\").should eq(PrivateEnum::BAR)\n    PrivateEnum.parse(\"QUX\").should eq(PrivateEnum::QUX)\n\n    SpecEnumWithCaseSensitiveMembers.parse(\"foo\").should eq SpecEnumWithCaseSensitiveMembers::FOO\n    SpecEnumWithCaseSensitiveMembers.parse(\"FOO\").should eq SpecEnumWithCaseSensitiveMembers::FOO\n    SpecEnumWithCaseSensitiveMembers.parse(\"Foo\").should eq SpecEnumWithCaseSensitiveMembers::FOO\n  end\n\n  it \".parse?\" do\n    SpecEnum.parse?(\"Two\").should eq(SpecEnum::Two)\n    SpecEnum.parse?(\"Four\").should be_nil\n    SpecEnum.parse?(\"Fo-ur\").should be_nil\n  end\n\n  it \"clones\" do\n    SpecEnum::One.clone.should eq(SpecEnum::One)\n  end\n\n  describe \".[]\" do\n    it \"non-flags enum\" do\n      SpecEnum[].should be_nil\n      SpecEnum[One].should eq SpecEnum::One\n      SpecEnum[1].should eq SpecEnum::Two\n      SpecEnum[One, Two].should eq SpecEnum::One | SpecEnum::Two\n      SpecEnum[One, :two].should eq SpecEnum::One | SpecEnum::Two\n      SpecEnum[One, 1].should eq SpecEnum::One | SpecEnum::Two\n    end\n\n    it \"flags enum\" do\n      SpecEnumFlags.flags.should be_nil\n      SpecEnumFlags[One].should eq SpecEnumFlags::One\n      SpecEnumFlags[2].should eq SpecEnumFlags::Two\n      SpecEnumFlags[One, Two].should eq SpecEnumFlags::One | SpecEnumFlags::Two\n      SpecEnumFlags[One, :two].should eq SpecEnumFlags::One | SpecEnumFlags::Two\n      SpecEnumFlags[One, 2].should eq SpecEnumFlags::One | SpecEnumFlags::Two\n    end\n\n    it \"private flags enum\" do\n      PrivateFlagsEnum.flags.should be_nil\n      PrivateFlagsEnum[FOO].should eq PrivateFlagsEnum::FOO\n      PrivateFlagsEnum[FOO, BAR].should eq PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAR\n    end\n  end\n\n  describe \".flags\" do\n    it \"non-flags enum\" do\n      SpecEnum.flags.should be_nil\n      SpecEnum.flags(One).should eq SpecEnum::One\n      SpecEnum.flags(One, Two).should eq SpecEnum::One | SpecEnum::Two\n    end\n\n    it \"flags enum\" do\n      SpecEnumFlags.flags.should be_nil\n      SpecEnumFlags.flags(One).should eq SpecEnumFlags::One\n      SpecEnumFlags.flags(One, Two).should eq SpecEnumFlags::One | SpecEnumFlags::Two\n    end\n\n    it \"private flags enum\" do\n      PrivateFlagsEnum.flags.should be_nil\n      PrivateFlagsEnum.flags(FOO).should eq PrivateFlagsEnum::FOO\n      PrivateFlagsEnum.flags(FOO, BAR).should eq PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAR\n    end\n  end\n\n  describe \"each\" do\n    it \"iterates each member\" do\n      keys = [] of SpecEnum\n      values = [] of Int8\n\n      SpecEnum.each do |key, value|\n        keys << key\n        values << value\n      end\n\n      keys.should eq([SpecEnum::One, SpecEnum::Two, SpecEnum::Three])\n      values.should eq([SpecEnum::One.value, SpecEnum::Two.value, SpecEnum::Three.value])\n    end\n\n    it \"iterates each flag\" do\n      keys = [] of SpecEnumFlags\n      values = [] of Int32\n\n      SpecEnumFlags.each do |key, value|\n        keys << key\n        values << value\n      end\n\n      keys.should eq([SpecEnumFlags::One, SpecEnumFlags::Two, SpecEnumFlags::Three])\n      values.should eq([SpecEnumFlags::One.value, SpecEnumFlags::Two.value, SpecEnumFlags::Three.value])\n    end\n\n    it \"iterates private enum members\" do\n      keys = [] of PrivateEnum\n      values = [] of Int32\n\n      PrivateEnum.each do |key, value|\n        keys << key\n        values << value\n      end\n\n      keys.should eq([PrivateEnum::FOO, PrivateEnum::BAR, PrivateEnum::BAZ, PrivateEnum::QUX])\n      values.should eq([PrivateEnum::FOO.value, PrivateEnum::BAR.value, PrivateEnum::BAZ.value, PrivateEnum::QUX.value])\n    end\n  end\n\n  it \"different enums classes not eq always\" do\n    SpecEnum::One.should_not eq SpecEnum2::FortyTwo\n  end\nend\n"
  },
  {
    "path": "spec/std/enumerable_spec.cr",
    "content": "require \"spec\"\nrequire \"./spec_helper\"\nrequire \"spec/helpers/iterate\"\n\nmodule SomeInterface; end\n\nprivate record One do\n  include SomeInterface\nend\n\nprivate record Two do\n  include SomeInterface\nend\n\nprivate struct InterfaceEnumerable\n  include Enumerable(SomeInterface)\n\n  def each(&)\n    yield One.new\n    yield Two.new\n  end\nend\n\nprivate class SpecEnumerable\n  include Enumerable(Int32)\n\n  def each(&)\n    yield 1\n    yield 2\n    yield 3\n  end\nend\n\nprivate class SpecEmptyEnumerable\n  include Enumerable(Int32)\n\n  def each(&block : T -> _)\n  end\nend\n\nprivate class SpecCountUpIterator\n  include Iterator(Int32)\n\n  def initialize(@size : Int32, @count = 0)\n  end\n\n  def next\n    (@count += 1) <= @size ? (@count - 1) : stop\n  end\nend\n\ndescribe \"Enumerable\" do\n  describe \"all? with block\" do\n    it \"returns true\" do\n      [\"ant\", \"bear\", \"cat\"].all? { |word| word.size >= 3 }.should be_true\n    end\n\n    it \"returns false\" do\n      [\"ant\", \"bear\", \"cat\"].all? { |word| word.size >= 4 }.should be_false\n    end\n  end\n\n  describe \"all? without block\" do\n    it \"returns true\" do\n      [15].all?.should be_true\n    end\n\n    it \"returns false\" do\n      [nil, true, 99].all?.should be_false\n    end\n  end\n\n  describe \"all? with pattern\" do\n    it \"returns true\" do\n      [2, 3, 4].all?(1..5).should be_true\n    end\n\n    it \"returns false\" do\n      [2, 3, 4].all?(1..3).should be_false\n    end\n  end\n\n  describe \"any? with block\" do\n    it \"returns true if at least one element fulfills the condition\" do\n      [\"ant\", \"bear\", \"cat\"].any? { |word| word.size >= 4 }.should be_true\n    end\n\n    it \"returns false if all elements does not fulfill the condition\" do\n      [\"ant\", \"bear\", \"cat\"].any? { |word| word.size > 4 }.should be_false\n    end\n  end\n\n  describe \"any? without block\" do\n    it \"returns true if at least one element is truthy\" do\n      [nil, true, 99].any?.should be_true\n    end\n\n    it \"returns false if all elements are falsey\" do\n      [nil, false].any?.should be_false\n    end\n  end\n\n  describe \"any? with pattern\" do\n    it \"returns true\" do\n      [nil, true, 99].any?(Int32).should be_true\n    end\n\n    it \"returns false\" do\n      [nil, false].any?(Int32).should be_false\n    end\n  end\n\n  describe \"compact map\" do\n    it { Set{1, nil, 2, nil, 3}.compact_map { |x| x.try &.+(1) }.should eq([2, 3, 4]) }\n  end\n\n  describe \"size without block\" do\n    it \"returns the number of elements in the Enumerable\" do\n      SpecEnumerable.new.size.should eq 3\n    end\n  end\n\n  describe \"count with block\" do\n    it \"returns the number of the times the item is present\" do\n      %w(a b c a d A).count(\"a\").should eq 2\n    end\n  end\n\n  describe \"cycle\" do\n    it \"calls forever if we don't break\" do\n      called = 0\n      elements = [] of Int32\n      (1..2).cycle do |e|\n        elements << e\n        called += 1\n        break if called == 6\n      end\n      called.should eq 6\n      elements.should eq [1, 2, 1, 2, 1, 2]\n    end\n\n    it \"calls the block n times given the optional argument\" do\n      called = 0\n      elements = [] of Int32\n      (1..2).cycle(3) do |e|\n        elements << e\n        called += 1\n      end\n      called.should eq 6\n      elements.should eq [1, 2, 1, 2, 1, 2]\n    end\n  end\n\n  describe \"to_a\" do\n    it \"with a block\" do\n      SpecEnumerable.new.to_a { |e| e*2 }.should eq [2, 4, 6]\n    end\n\n    it \"without a block\" do\n      SpecEnumerable.new.to_a.should eq [1, 2, 3]\n    end\n\n    it \"without a block of an interface type\" do\n      InterfaceEnumerable.new.to_a.should eq [One.new, Two.new]\n    end\n  end\n\n  describe \"#to_set\" do\n    context \"without block\" do\n      it \"creates a Set from the unique elements of the collection\" do\n        {1, 1, 2, 3}.to_set.should eq Set{1, 2, 3}\n      end\n    end\n\n    context \"with block\" do\n      it \"creates a Set from running the block against the collection's elements\" do\n        {1, 2, 3, 4, 5}.to_set { |i| i // 2 }.should eq Set{0, 1, 2}\n      end\n    end\n  end\n\n  describe \"chunk\" do\n    it \"works\" do\n      [1].chunk { true }.to_a.should eq [{true, [1]}]\n      [1, 2].chunk { false }.to_a.should eq [{false, [1, 2]}]\n      [1, 1, 2, 3, 3].chunk(&.itself).to_a.should eq [{1, [1, 1]}, {2, [2]}, {3, [3, 3]}]\n      [1, 1, 2, 3, 3].chunk(&.<=(2)).to_a.should eq [{true, [1, 1, 2]}, {false, [3, 3]}]\n      (0..10).chunk(&.//(3)).to_a.should eq [{0, [0, 1, 2]}, {1, [3, 4, 5]}, {2, [6, 7, 8]}, {3, [9, 10]}]\n    end\n\n    it \"work with class\" do\n      [1, 1, 2, 3, 3].chunk(&.class).to_a.should eq [{Int32, [1, 1, 2, 3, 3]}]\n    end\n\n    it \"works with block\" do\n      res = [] of Tuple(Bool, Array(Int32))\n      [1, 2, 3].chunk { |x| x < 3 }.each { |(k, v)| res << {k, v} }\n      res.should eq [{true, [1, 2]}, {false, [3]}]\n    end\n\n    it \"rewind\" do\n      i = (0..10).chunk(&.//(3))\n      i.next.should eq({0, [0, 1, 2]})\n      i.next.should eq({1, [3, 4, 5]})\n    end\n\n    it \"returns elements of the Enumerable in an Array of Tuple, {v, ary}, where 'ary' contains the consecutive elements for which the block returned the value 'v'\" do\n      result = [1, 2, 3, 2, 3, 2, 1].chunk { |x| x < 3 ? 1 : 0 }.to_a\n      result.should eq [{1, [1, 2]}, {0, [3]}, {1, [2]}, {0, [3]}, {1, [2, 1]}]\n    end\n\n    it \"returns elements for which the block returns Enumerable::Chunk::Alone in separate Arrays\" do\n      result = [1, 2, 3, 2, 1].chunk { |x| x < 2 ? Enumerable::Chunk::Alone : false }.to_a\n      result.should eq [{Enumerable::Chunk::Alone, [1]}, {false, [2, 3, 2]}, {Enumerable::Chunk::Alone, [1]}]\n    end\n\n    it \"alone all\" do\n      result = [1, 2].chunk { Enumerable::Chunk::Alone }.to_a\n      result.should eq [{Enumerable::Chunk::Alone, [1]}, {Enumerable::Chunk::Alone, [2]}]\n    end\n\n    it \"does not return elements for which the block returns Enumerable::Chunk::Drop\" do\n      result = [1, 2, 3, 3, 2, 1].chunk { |x| x == 2 ? Enumerable::Chunk::Drop : 1 }.to_a\n      result.should eq [{1, [1]}, {1, [3, 3]}, {1, [1]}]\n    end\n\n    it \"drop all\" do\n      result = [1, 2].chunk { Enumerable::Chunk::Drop }.to_a\n      result.should be_a(Array(Tuple(NoReturn, Array(Int32))))\n      result.size.should eq 0\n    end\n\n    it \"nil allowed as value\" do\n      result = [1, 2, 3, 2, 1].chunk { |x| x == 2 ? nil : 1 }.to_a\n      result.should eq [{1, [1]}, {nil, [2]}, {1, [3]}, {nil, [2]}, {1, [1]}]\n    end\n\n    it \"nil 2 case\" do\n      result = [nil, nil, 1, 1, nil].chunk(&.itself).to_a\n      result.should eq [{nil, [nil, nil]}, {1, [1, 1]}, {nil, [nil]}]\n    end\n\n    it \"reuses true\" do\n      iter = [1, 1, 2, 3, 3].chunk(reuse: true, &.itself)\n      a = iter.next.should be_a(Tuple(Int32, Array(Int32)))\n      a.should eq({1, [1, 1]})\n\n      b = iter.next.should be_a(Tuple(Int32, Array(Int32)))\n      b.should eq({2, [2]})\n      b[1].should be(a[1])\n\n      c = iter.next.should be_a(Tuple(Int32, Array(Int32)))\n      c.should eq({3, [3, 3]})\n      c[1].should be(a[1])\n    end\n  end\n\n  describe \"chunks\" do\n    it \"works\" do\n      [1].chunks { true }.should eq [{true, [1]}]\n      [1, 2].chunks { false }.should eq [{false, [1, 2]}]\n      [1, 1, 2, 3, 3].chunks(&.itself).should eq [{1, [1, 1]}, {2, [2]}, {3, [3, 3]}]\n      [1, 1, 2, 3, 3].chunks(&.<=(2)).should eq [{true, [1, 1, 2]}, {false, [3, 3]}]\n      (0..10).chunks(&.//(3)).should eq [{0, [0, 1, 2]}, {1, [3, 4, 5]}, {2, [6, 7, 8]}, {3, [9, 10]}]\n    end\n\n    it \"work with class\" do\n      [1, 1, 2, 3, 3].chunks(&.class).should eq [{Int32, [1, 1, 2, 3, 3]}]\n    end\n\n    it \"work with pure enumerable\" do\n      SpecEnumerable.new.chunks(&.//(2)).should eq [{0, [1]}, {1, [2, 3]}]\n    end\n\n    it \"returns elements for which the block returns Enumerable::Chunk::Alone in separate Arrays\" do\n      result = [1, 2, 3, 2, 1].chunks { |x| x < 2 ? Enumerable::Chunk::Alone : false }\n      result.should eq [{Enumerable::Chunk::Alone, [1]}, {false, [2, 3, 2]}, {Enumerable::Chunk::Alone, [1]}]\n    end\n\n    it \"alone all\" do\n      result = [1, 2].chunks { Enumerable::Chunk::Alone }\n      result.should eq [{Enumerable::Chunk::Alone, [1]}, {Enumerable::Chunk::Alone, [2]}]\n    end\n\n    it \"does not return elements for which the block returns Enumerable::Chunk::Drop\" do\n      result = [1, 2, 3, 3, 2, 1].chunks { |x| x == 2 ? Enumerable::Chunk::Drop : 1 }\n      result.should eq [{1, [1]}, {1, [3, 3]}, {1, [1]}]\n    end\n\n    it \"drop all\" do\n      result = [1, 2].chunks { Enumerable::Chunk::Drop }\n      result.should be_a(Array(Tuple(NoReturn, Array(Int32))))\n      result.size.should eq 0\n    end\n\n    it \"nil allowed as value\" do\n      result = [1, 2, 3, 2, 1].chunks { |x| x == 2 ? nil : 1 }\n      result.should eq [{1, [1]}, {nil, [2]}, {1, [3]}, {nil, [2]}, {1, [1]}]\n    end\n\n    it \"nil 2 case\" do\n      result = [nil, nil, 1, 1, nil].chunks(&.itself)\n      result.should eq [{nil, [nil, nil]}, {1, [1, 1]}, {nil, [nil]}]\n    end\n  end\n\n  describe \"#each_cons\" do\n    context \"iterator\" do\n      it \"iterates\" do\n        iter = [1, 2, 3, 4, 5].each_cons(3)\n        iter.next.should eq([1, 2, 3])\n        iter.next.should eq([2, 3, 4])\n        iter.next.should eq([3, 4, 5])\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"iterates with reuse = true\" do\n        iter = [1, 2, 3, 4, 5].each_cons(3, reuse: true)\n\n        a = iter.next\n        a.should eq([1, 2, 3])\n\n        b = iter.next\n        b.should be(a)\n      end\n\n      it \"iterates with reuse = array\" do\n        reuse = [] of Int32\n        iter = [1, 2, 3, 4, 5].each_cons(3, reuse: reuse)\n\n        a = iter.next\n        a.should eq([1, 2, 3])\n        a.should be(reuse)\n      end\n\n      it \"iterates with reuse = deque\" do\n        reuse = Deque(Int32).new\n        iter = [1, 2, 3, 4, 5].each_cons(3, reuse: reuse)\n\n        a = iter.next\n        a.should eq(Deque{1, 2, 3})\n        a.should be(reuse)\n      end\n    end\n\n    context \"yield\" do\n      it \"returns running pairs\" do\n        array = [] of Array(Int32)\n        [1, 2, 3, 4].each_cons(2) { |pair| array << pair }\n        array.should eq([[1, 2], [2, 3], [3, 4]])\n      end\n\n      it \"returns running triples\" do\n        array = [] of Array(Int32)\n        [1, 2, 3, 4, 5].each_cons(3) { |triple| array << triple }\n        array.should eq([[1, 2, 3], [2, 3, 4], [3, 4, 5]])\n      end\n\n      it \"yields running pairs with reuse = true\" do\n        array = [] of Array(Int32)\n        object_ids = Set(UInt64).new\n        [1, 2, 3, 4].each_cons(2, reuse: true) do |pair|\n          object_ids << pair.object_id\n          array << pair.dup\n        end\n        array.should eq([[1, 2], [2, 3], [3, 4]])\n        object_ids.size.should eq(1)\n      end\n\n      it \"yields running pairs with reuse = array\" do\n        array = [] of Array(Int32)\n        reuse = [] of Int32\n        [1, 2, 3, 4].each_cons(2, reuse: reuse) do |pair|\n          pair.should be(reuse)\n          array << pair.dup\n        end\n        array.should eq([[1, 2], [2, 3], [3, 4]])\n      end\n\n      it \"yields running pairs with reuse = deque\" do\n        array = [] of Deque(Int32)\n        reuse = Deque(Int32).new\n        [1, 2, 3, 4].each_cons(2, reuse: reuse) do |pair|\n          pair.should be(reuse)\n          array << pair.dup\n        end\n        array.should eq([Deque{1, 2}, Deque{2, 3}, Deque{3, 4}])\n      end\n    end\n  end\n\n  describe \"#each_cons_pair\" do\n    it \"returns running pairs\" do\n      array = [] of Array(Int32)\n      [1, 2, 3, 4].each_cons_pair { |a, b| array << [a, b] }\n      array.should eq([[1, 2], [2, 3], [3, 4]])\n    end\n  end\n\n  describe \"each_slice\" do\n    it \"returns partial slices\" do\n      array = [] of Array(Int32)\n      [1, 2, 3].each_slice(2) { |slice| array << slice }\n      array.should eq([[1, 2], [3]])\n      array[0].should_not be(array[1])\n    end\n\n    it \"returns full slices\" do\n      array = [] of Array(Int32)\n      [1, 2, 3, 4].each_slice(2) { |slice| array << slice }\n      array.should eq([[1, 2], [3, 4]])\n      array[0].should_not be(array[1])\n    end\n\n    it \"reuses with true\" do\n      array = [] of Array(Int32)\n      object_ids = Set(UInt64).new\n      [1, 2, 3, 4].each_slice(2, reuse: true) do |slice|\n        object_ids << slice.object_id\n        array << slice.dup\n      end\n      array.should eq([[1, 2], [3, 4]])\n      object_ids.size.should eq(1)\n    end\n\n    it \"reuses with existing array\" do\n      array = [] of Array(Int32)\n      reuse = [] of Int32\n      [1, 2, 3, 4].each_slice(2, reuse: reuse) do |slice|\n        slice.should be(reuse)\n        array << slice.dup\n      end\n      array.should eq([[1, 2], [3, 4]])\n    end\n\n    it \"returns each_slice iterator\" do\n      iter = [1, 2, 3, 4, 5].each_slice(2)\n      iter.next.should eq([1, 2])\n      iter.next.should eq([3, 4])\n      iter.next.should eq([5])\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"each_step\" do\n    it_iterates \"yields every 2nd element\", %w[a c e], %w[a b c d e f].each_step(2)\n    it_iterates \"accepts an optional offset parameter\", %w[b d f], %w[a b c d e f].each_step(2, offset: 1)\n    it_iterates \"accepts an offset of 0\", %w[a c e], %w[a b c d e f].each_step(2, offset: 0)\n    it_iterates \"accepts an offset larger then the step size\", %w[d f], %w[a b c d e f].each_step(2, offset: 3)\n\n    it_iterates \"accepts a step larger then the enumerable size\", %w[a], %w[a b c d e f].each_step(7)\n    it_iterates \"accepts an offset larger then the enumerable size\", %w[], %w[a b c d e f].each_step(1, offset: 7)\n\n    it \"doesn't accept a negative step\" do\n      expect_raises(ArgumentError) do\n        %w[a b c d e f].each_step(-2)\n      end\n      expect_raises(ArgumentError) do\n        %w[a b c d e f].each_step(-2) { }\n      end\n    end\n\n    it \"doesn't accept a step of 0\" do\n      expect_raises(ArgumentError) do\n        %w[a b c d e f].each_step(0)\n      end\n      expect_raises(ArgumentError) do\n        %w[a b c d e f].each_step(0) { }\n      end\n    end\n\n    it \"doesn't accept a negative offset\" do\n      expect_raises(ArgumentError) do\n        %w[a b c d e f].each_step(2, offset: -2)\n      end\n      expect_raises(ArgumentError) do\n        %w[a b c d e f].each_step(2, offset: -2) { }\n      end\n    end\n  end\n\n  describe \"each_with_index\" do\n    it \"yields the element and the index\" do\n      collection = [] of {String, Int32}\n      [\"a\", \"b\", \"c\"].each_with_index do |e, i|\n        collection << {e, i}\n      end\n      collection.should eq [{\"a\", 0}, {\"b\", 1}, {\"c\", 2}]\n    end\n\n    it \"accepts an optional offset parameter\" do\n      collection = [] of {String, Int32}\n      [\"Alice\", \"Bob\"].each_with_index(1) do |e, i|\n        collection << {e, i}\n      end\n      collection.should eq [{\"Alice\", 1}, {\"Bob\", 2}]\n    end\n\n    it \"gets each_with_index iterator\" do\n      iter = [1, 2].each_with_index\n      iter.next.should eq({1, 0})\n      iter.next.should eq({2, 1})\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"each_with_object\" do\n    it \"yields the element and the given object\" do\n      collection = [] of {Int32, String}\n      object = \"a\"\n      (1..3).each_with_object(object) do |e, o|\n        collection << {e, o}\n      end.should be(object)\n      collection.should eq [{1, object}, {2, object}, {3, object}]\n    end\n\n    it \"gets each_with_object iterator\" do\n      iter = [1, 2].each_with_object(\"a\")\n      iter.next.should eq({1, \"a\"})\n      iter.next.should eq({2, \"a\"})\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"#empty?\" do\n    it { SpecEnumerable.new.empty?.should be_false }\n    it { SpecEmptyEnumerable.new.empty?.should be_true }\n  end\n\n  describe \"#present?\" do\n    it { SpecEnumerable.new.present?.should be_true }\n    it { SpecEmptyEnumerable.new.present?.should be_false }\n  end\n\n  describe \"find\" do\n    it \"finds\" do\n      [1, 2, 3].find { |x| x > 2 }.should eq(3)\n    end\n\n    it \"doesn't find\" do\n      [1, 2, 3].find { |x| x > 3 }.should be_nil\n    end\n\n    it \"doesn't find with default value\" do\n      [1, 2, 3].find(-1) { |x| x > 3 }.should eq(-1)\n    end\n  end\n\n  describe \"find!\" do\n    it \"finds\" do\n      [1, 2, 3].find! { |x| x > 2 }.should eq(3)\n    end\n\n    it \"raises if not found\" do\n      expect_raises Enumerable::NotFoundError do\n        [1, 2, 3].find! { false }\n      end\n    end\n  end\n\n  describe \"find_value\" do\n    it \"finds and returns the first truthy block result\" do\n      [1, 2, 3].find_value { |i| \"1\" if i == 1 }.should eq \"1\"\n      {1, 2, 3}.find_value { |i| \"2\" if i == 2 }.should eq \"2\"\n      (1..3).find_value { |i| \"3\" if i == 3 }.should eq \"3\"\n\n      # Block returns `true && expression` vs the above `expression if true`.\n      # Same idea, but a different idiom. It serves as an allegory for the next\n      # test which checks `false` vs `nil`.\n      [1, 2, 3].find_value { |i| i == 1 && \"1\" }.should eq \"1\"\n      {1, 2, 3}.find_value { |i| i == 2 && \"2\" }.should eq \"2\"\n      (1..3).find_value { |i| i == 3 && \"3\" }.should eq \"3\"\n    end\n\n    it \"returns the default value if there are no truthy block results\" do\n      {1, 2, 3}.find_value { |i| \"4\" if i == 4 }.should be_nil\n      {1, 2, 3}.find_value \"nope\" { |i| \"4\" if i == 4 }.should eq \"nope\"\n      ([] of Int32).find_value false { true }.should be_false\n\n      # Same as above but returns `false` instead of `nil`.\n      {1, 2, 3}.find_value { |i| i == 4 && \"4\" }.should be_nil\n      {1, 2, 3}.find_value \"nope\" { |i| i == 4 && \"4\" }.should eq \"nope\"\n    end\n  end\n\n  describe \"first\" do\n    it \"calls block if empty\" do\n      (1...1).first { 10 }.should eq(10)\n    end\n\n    it \"gets first\" do\n      (1..3).first.should eq(1)\n    end\n\n    it \"raises if enumerable empty\" do\n      expect_raises Enumerable::EmptyError do\n        (1...1).first\n      end\n    end\n\n    it { [-1, -2, -3].first.should eq(-1) }\n  end\n\n  describe \"first?\" do\n    it \"gets first?\" do\n      (1..3).first?.should eq(1)\n    end\n\n    it \"returns nil if enumerable empty\" do\n      (1...1).first?.should be_nil\n    end\n  end\n\n  describe \"flat_map\" do\n    it \"does example 1\" do\n      [1, 2, 3, 4].flat_map { |e| [e, -e] }.should eq([1, -1, 2, -2, 3, -3, 4, -4])\n    end\n\n    it \"does example 2\" do\n      [[1, 2], [3, 4]].flat_map { |e| e + [100] }.should eq([1, 2, 100, 3, 4, 100])\n    end\n\n    it \"does example 3\" do\n      [[1, 2, 3], 4, 5].flat_map { |e| e }.should eq([1, 2, 3, 4, 5])\n    end\n\n    it \"does example 4\" do\n      [{1 => 2}, {3 => 4}].flat_map { |e| e }.should eq([{1 => 2}, {3 => 4}])\n    end\n\n    it \"flattens iterators\" do\n      [[1, 2], [3, 4]].flat_map(&.each).should eq([1, 2, 3, 4])\n    end\n\n    it \"accepts mixed element types\" do\n      [[1, 2, 3], ['a', 'b'].each, \"cde\"].flat_map { |e| e }.should eq([1, 2, 3, 'a', 'b', \"cde\"])\n    end\n  end\n\n  describe \"group_by\" do\n    it { [1, 2, 2, 3].group_by { |x| x == 2 }.should eq({true => [2, 2], false => [1, 3]}) }\n\n    it \"groups can group by size (like the doc example)\" do\n      %w(Alice Bob Ary).group_by(&.size).should eq({3 => [\"Bob\", \"Ary\"],\n                                                    5 => [\"Alice\"]})\n    end\n  end\n\n  describe \"in_groups_of\" do\n    it { [1, 2, 3].in_groups_of(1).should eq([[1], [2], [3]]) }\n    it { [1, 2, 3].in_groups_of(2).should eq([[1, 2], [3, nil]]) }\n    it { [1, 2, 3, 4].in_groups_of(3).should eq([[1, 2, 3], [4, nil, nil]]) }\n    it { ([] of Int32).in_groups_of(2).should eq([] of Array(Array(Int32 | Nil))) }\n    it { [1, 2, 3].in_groups_of(2, \"x\").should eq([[1, 2], [3, \"x\"]]) }\n\n    it \"raises argument error if size is less than 0\" do\n      expect_raises ArgumentError, \"Size must be positive\" do\n        [1, 2, 3].in_groups_of(0)\n      end\n    end\n\n    it \"takes a block\" do\n      sums = [] of Int32\n      [1, 2, 4, 5].in_groups_of(3, 10) { |a| sums << a.sum }\n      sums.should eq([7, 25])\n    end\n\n    it \"reuses with true\" do\n      array = [] of Array(Int32)\n      object_ids = Set(UInt64).new\n      [1, 2, 4, 5].in_groups_of(3, 10, reuse: true) do |group|\n        object_ids << group.object_id\n        array << group.dup\n      end\n      array.should eq([[1, 2, 4], [5, 10, 10]])\n      object_ids.size.should eq(1)\n    end\n\n    it \"reuses with existing array\" do\n      array = [] of Array(Int32)\n      reuse = [] of Int32\n      [1, 2, 4, 5].in_groups_of(3, 10, reuse: reuse) do |slice|\n        slice.should be(reuse)\n        array << slice.dup\n      end\n      array.should eq([[1, 2, 4], [5, 10, 10]])\n    end\n  end\n\n  describe \"in slices of\" do\n    it { [1, 2, 3].in_slices_of(1).should eq([[1], [2], [3]]) }\n    it { [1, 2, 3].in_slices_of(2).should eq([[1, 2], [3]]) }\n    it { [1, 2, 3, 4].in_slices_of(3).should eq([[1, 2, 3], [4]]) }\n    it { ([] of Int32).in_slices_of(2).should eq([] of Array(Int32)) }\n\n    it \"raises argument error if size is less than 0\" do\n      expect_raises ArgumentError, \"Size must be positive\" do\n        [1, 2, 3].in_slices_of(0)\n      end\n    end\n  end\n\n  describe \"includes?\" do\n    it \"is true if the object exists in the collection\" do\n      [1, 2, 3].includes?(2).should be_true\n    end\n\n    it \"is false if the object is not part of the collection\" do\n      [1, 2, 3].includes?(5).should be_false\n    end\n  end\n\n  describe \"index with a block\" do\n    it \"returns the index of the first element where the block returns true\" do\n      [\"Alice\", \"Bob\"].index { |name| name.size < 4 }.should eq 1\n    end\n\n    it \"returns nil if no object could be found\" do\n      [\"Alice\", \"Bob\"].index { |name| name.size < 3 }.should be_nil\n    end\n  end\n\n  describe \"index with an object\" do\n    it \"returns the index of that object if found\" do\n      [\"Alice\", \"Bob\"].index(\"Alice\").should eq 0\n    end\n\n    it \"returns nil if the object was not found\" do\n      [\"Alice\", \"Bob\"].index(\"Mallory\").should be_nil\n    end\n  end\n\n  describe \"index! with a block\" do\n    it \"returns the index of the first element where the block returns true\" do\n      [\"Alice\", \"Bob\"].index! { |name| name.size < 4 }.should eq 1\n    end\n\n    it \"raises if not found\" do\n      expect_raises Enumerable::NotFoundError do\n        [\"Alice\", \"Bob\"].index! { |name| name.size < 3 }\n      end\n    end\n  end\n\n  describe \"index! with an object\" do\n    it \"returns the index of that object if found\" do\n      [\"Alice\", \"Bob\"].index!(\"Alice\").should eq 0\n    end\n\n    it \"raises if not found\" do\n      expect_raises Enumerable::NotFoundError do\n        [\"Alice\", \"Bob\"].index!(\"Mallory\")\n      end\n    end\n  end\n\n  describe \"index_by\" do\n    it \"creates a hash indexed by the value returned by the block\" do\n      hash = [\"Anna\", \"Ary\", \"Alice\"].index_by(&.size)\n      hash.should eq({4 => \"Anna\", 3 => \"Ary\", 5 => \"Alice\"})\n    end\n\n    it \"overrides values if a value is returned twice\" do\n      hash = [\"Anna\", \"Ary\", \"Alice\", \"Bob\"].index_by(&.size)\n      hash.should eq({4 => \"Anna\", 3 => \"Bob\", 5 => \"Alice\"})\n    end\n  end\n\n  describe \"reduce\" do\n    it { [1, 2, 3].reduce { |memo, i| memo + i }.should eq(6) }\n    it { [1, 2, 3].reduce(10) { |memo, i| memo + i }.should eq(16) }\n    it { [1, 2, 3].reduce([] of Int32) { |memo, i| memo.unshift(i) }.should eq([3, 2, 1]) }\n    it { [[0, 1], [2, 3], [4, 5]].reduce([] of Int32) { |memo, i| memo.concat(i) }.should eq([0, 1, 2, 3, 4, 5]) }\n\n    it \"raises if empty\" do\n      expect_raises Enumerable::EmptyError do\n        ([] of Int32).reduce { |memo, i| memo + i }\n      end\n    end\n\n    it \"does not raise if empty if there is a memo argument\" do\n      result = ([] of Int32).reduce(10) { |memo, i| memo + i }\n      result.should eq 10\n    end\n\n    it \"allows block return type to be different from element type\" do\n      [1, 2, 3].reduce { |x, y| \"#{x}-#{y}\" }.should eq(\"1-2-3\")\n      [1].reduce { |x, y| \"#{x}-#{y}\" }.should eq(1)\n      {1}.reduce { |x, y| \"#{x}-#{y}\" }.should eq(1)\n\n      expect_raises Enumerable::EmptyError do\n        ([] of Int32).reduce { |x, y| \"#{x}-#{y}\" }\n      end\n\n      expect_raises Enumerable::EmptyError do\n        Tuple.new.reduce { |x, y| \"#{x}-#{y}\" }\n      end\n    end\n  end\n\n  describe \"reduce?\" do\n    it { [1, 2, 3].reduce? { |memo, i| memo + i }.should eq(6) }\n\n    it \"returns nil if empty\" do\n      ([] of Int32).reduce? { |memo, i| memo + i }.should be_nil\n    end\n\n    it \"allows block return type to be different from element type\" do\n      [1, 2, 3].reduce? { |x, y| \"#{x}-#{y}\" }.should eq(\"1-2-3\")\n      [1].reduce? { |x, y| \"#{x}-#{y}\" }.should eq(1)\n      {1}.reduce? { |x, y| \"#{x}-#{y}\" }.should eq(1)\n      ([] of Int32).reduce? { |x, y| \"#{x}-#{y}\" }.should be_nil\n      Tuple.new.reduce? { |x, y| \"#{x}-#{y}\" }.should be_nil\n    end\n  end\n\n  describe \"#accumulate\" do\n    context \"prefix sums\" do\n      it { SpecEnumerable.new.accumulate.should eq([1, 3, 6]) }\n      it { [1.5, 3.75, 6.125].accumulate.should eq([1.5, 5.25, 11.375]) }\n      it { Array(Int32).new.accumulate.should eq(Array(Int32).new) }\n    end\n\n    context \"prefix sums, with init\" do\n      it { SpecEnumerable.new.accumulate(0).should eq([0, 1, 3, 6]) }\n      it { [1.5, 3.75, 6.125].accumulate(0.5).should eq([0.5, 2.0, 5.75, 11.875]) }\n      it { Array(Int32).new.accumulate(7).should eq([7]) }\n\n      it \"preserves initial type\" do\n        x = SpecEnumerable.new.accumulate(4.0)\n        x.should be_a(Array(Float64))\n        x.should eq([4.0, 5.0, 7.0, 10.0])\n      end\n    end\n\n    context \"generic cumulative fold\" do\n      it { SpecEnumerable.new.accumulate { |x, y| x * 10 + y }.should eq([1, 12, 123]) }\n      it { Array(Int32).new.accumulate { raise \"\" }.should eq(Array(Int32).new) }\n    end\n\n    context \"generic cumulative fold, with init\" do\n      it { SpecEnumerable.new.accumulate(4) { |x, y| x * 10 + y }.should eq([4, 41, 412, 4123]) }\n      it { Array(Int32).new.accumulate(7) { raise \"\" }.should eq([7]) }\n\n      it \"preserves initial type\" do\n        x = [4, 3, 2].accumulate(\"X\") { |x, y| x * y }\n        x.should be_a(Array(String))\n        x.should eq(%w(X XXXX XXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXX))\n      end\n    end\n  end\n\n  describe \"#join\" do\n    it \"()\" do\n      [1, 2, 3].join.should eq(\"123\")\n    end\n\n    it \"(separator)\" do\n      [\"Ruby\", \"Crystal\", \"Python\"].join(\", \").should eq \"Ruby, Crystal, Python\"\n    end\n\n    it \"(&)\" do\n      str = [1, 2, 3].join { |x| x + 1 }\n      str.should eq(\"234\")\n    end\n\n    it \"(separator, &)\" do\n      str = [1, 2, 3].join(\", \") { |x| x + 1 }\n      str.should eq(\"2, 3, 4\")\n    end\n\n    it \"(io)\" do\n      io = IO::Memory.new\n      [1, 2, 3].join(io)\n      io.to_s.should eq(\"123\")\n    end\n\n    it \"(io, separator)\" do\n      io = IO::Memory.new\n      [\"Ruby\", \"Crystal\", \"Python\"].join(io, \", \")\n      io.to_s.should eq \"Ruby, Crystal, Python\"\n    end\n\n    it \"(separator, io) (deprecated)\" do\n      io = IO::Memory.new\n      [\"Ruby\", \"Crystal\", \"Python\"].join(\", \", io)\n      io.to_s.should eq \"Ruby, Crystal, Python\"\n    end\n\n    it \"(io, &)\" do\n      io = IO::Memory.new\n      [1, 2, 3].join(io) { |x, io| io << x + 1 }\n      io.to_s.should eq(\"234\")\n    end\n\n    it \"(io, separator, &)\" do\n      io = IO::Memory.new\n      [1, 2, 3].join(io, \", \") { |x, io| io << x + 1 }\n      io.to_s.should eq(\"2, 3, 4\")\n    end\n\n    it \"(separator, io, &) (deprecated)\" do\n      str = IO::Memory.new\n      [1, 2, 3].join(\", \", str) { |x, io| io << x + 1 }\n      str.to_s.should eq(\"2, 3, 4\")\n    end\n  end\n\n  describe \"map\" do\n    it \"applies the function to each element and returns a new array\" do\n      result = [1, 2, 3].map { |i| i * 10 }\n      result.should eq [10, 20, 30]\n    end\n\n    it \"leaves the original unmodified\" do\n      original = [1, 2, 3]\n      original.map { |i| i * 10 }\n      original.should eq [1, 2, 3]\n    end\n  end\n\n  describe \"map_with_index\" do\n    it \"yields the element and the index\" do\n      result = SpecEnumerable.new.map_with_index { |e, i| \"Value ##{i}: #{e}\" }\n      result.should eq [\"Value #0: 1\", \"Value #1: 2\", \"Value #2: 3\"]\n    end\n\n    it \"yields the element and the index of an iterator\" do\n      str = \"hello\"\n      result = str.each_char.map_with_index { |char, i| \"#{char}#{i}\" }\n      result.should eq [\"h0\", \"e1\", \"l2\", \"l3\", \"o4\"]\n    end\n  end\n\n  describe \"max\" do\n    it { [1, 2, 3].max.should eq(3) }\n    it { [1, 2, 3].max(0).should eq([] of Int32) }\n    it { [1, 2, 3].max(1).should eq([3]) }\n    it { [1, 2, 3].max(2).should eq([3, 2]) }\n    it { [1, 2, 3].max(3).should eq([3, 2, 1]) }\n    it { [1, 2, 3].max(4).should eq([3, 2, 1]) }\n    it { ([] of Int32).max(0).should eq([] of Int32) }\n    it { ([] of Int32).max(5).should eq([] of Int32) }\n    it {\n      (0..1000).map { |x| (x*137 + x*x*139) % 5000 }.max(10).should eq([\n        4992, 4990, 4980, 4972, 4962, 4962, 4960, 4960, 4952, 4952,\n      ])\n    }\n\n    it \"does not modify the array\" do\n      xs = [7, 5, 2, 4, 9]\n      xs.max(2).should eq([9, 7])\n      xs.should eq([7, 5, 2, 4, 9])\n    end\n\n    it \"raises if empty\" do\n      expect_raises Enumerable::EmptyError do\n        ([] of Int32).max\n      end\n    end\n\n    it \"raises if n is negative\" do\n      expect_raises ArgumentError do\n        ([1, 2, 3] of Int32).max(-1)\n      end\n    end\n\n    it \"raises if not comparable\" do\n      expect_raises ArgumentError do\n        [Float64::NAN, 1.0, 2.0, Float64::NAN].max\n      end\n    end\n\n    it \"raises if not comparable in max(n)\" do\n      expect_raises ArgumentError do\n        [Float64::NAN, 1.0, 2.0, Float64::NAN].max(2)\n      end\n    end\n  end\n\n  describe \"max?\" do\n    it \"returns nil if empty\" do\n      ([] of Int32).max?.should be_nil\n    end\n  end\n\n  describe \"max_by\" do\n    it { [-1, -2, -3].max_by { |x| -x }.should eq(-3) }\n  end\n\n  describe \"max_by?\" do\n    it \"returns nil if empty\" do\n      ([] of Int32).max_by? { |x| -x }.should be_nil\n    end\n  end\n\n  describe \"max_of\" do\n    it { [-1, -2, -3].max_of { |x| -x }.should eq(3) }\n\n    it \"raises if not comparable\" do\n      expect_raises ArgumentError do\n        [-1.0, Float64::NAN, -3.0].max_of { |x| -x }\n      end\n    end\n  end\n\n  describe \"max_of?\" do\n    it \"returns nil if empty\" do\n      ([] of Int32).max_of? { |x| -x }.should be_nil\n    end\n  end\n\n  describe \"min\" do\n    it { [1, 2, 3].min.should eq(1) }\n    it { [1, 2, 3].min(0).should eq([] of Int32) }\n    it { [1, 2, 3].min(1).should eq([1]) }\n    it { [1, 2, 3].min(2).should eq([1, 2]) }\n    it { [1, 2, 3].min(3).should eq([1, 2, 3]) }\n    it { [1, 2, 3].min(4).should eq([1, 2, 3]) }\n    it { ([] of Int32).min(0).should eq([] of Int32) }\n    it { ([] of Int32).min(1).should eq([] of Int32) }\n    it {\n      (0..1000).map { |x| (x*137 + x*x*139) % 5000 }.min(10).should eq([\n        0, 10, 20, 26, 26, 26, 26, 30, 32, 32,\n      ])\n    }\n\n    it \"does not modify the array\" do\n      xs = [7, 5, 2, 4, 9]\n      xs.min(2).should eq([2, 4])\n      xs.should eq([7, 5, 2, 4, 9])\n    end\n\n    it \"raises if empty\" do\n      expect_raises Enumerable::EmptyError do\n        ([] of Int32).min\n      end\n    end\n\n    it \"raises if n is negative\" do\n      expect_raises ArgumentError do\n        ([1, 2, 3] of Int32).min(-1)\n      end\n    end\n\n    it \"raises if not comparable\" do\n      expect_raises ArgumentError do\n        [-1.0, Float64::NAN, -3.0].min\n      end\n    end\n\n    it \"raises if not comparable in min(n)\" do\n      expect_raises ArgumentError do\n        [Float64::NAN, 1.0, 2.0, Float64::NAN].min(2)\n      end\n    end\n  end\n\n  describe \"min?\" do\n    it \"returns nil if empty\" do\n      ([] of Int32).min?.should be_nil\n    end\n  end\n\n  describe \"min_by\" do\n    it { [1, 2, 3].min_by { |x| -x }.should eq(3) }\n  end\n\n  describe \"min_by?\" do\n    it \"returns nil if empty\" do\n      ([] of Int32).min_by? { |x| -x }.should be_nil\n    end\n  end\n\n  describe \"min_of\" do\n    it { [1, 2, 3].min_of { |x| -x }.should eq(-3) }\n\n    it \"raises if not comparable\" do\n      expect_raises ArgumentError do\n        [-1.0, Float64::NAN, -3.0].min_of { |x| -x }\n      end\n    end\n  end\n\n  describe \"min_of?\" do\n    it \"returns nil if empty\" do\n      ([] of Int32).min_of? { |x| -x }.should be_nil\n    end\n  end\n\n  describe \"minmax\" do\n    it { [1, 2, 3].minmax.should eq({1, 3}) }\n\n    it \"raises if empty\" do\n      expect_raises Enumerable::EmptyError do\n        ([] of Int32).minmax\n      end\n    end\n  end\n\n  describe \"minmax?\" do\n    it \"returns two nils if empty\" do\n      ([] of Int32).minmax?.should eq({nil, nil})\n    end\n\n    it \"raises if not comparable\" do\n      expect_raises ArgumentError do\n        [-1.0, Float64::NAN, -3.0].minmax\n      end\n    end\n  end\n\n  describe \"minmax_by\" do\n    it { [-1, -2, -3].minmax_by { |x| -x }.should eq({-1, -3}) }\n  end\n\n  describe \"minmax_by?\" do\n    it \"returns two nils if empty\" do\n      ([] of Int32).minmax_by? { |x| -x }.should eq({nil, nil})\n    end\n  end\n\n  describe \"minmax_of\" do\n    it { [-1, -2, -3].minmax_of { |x| -x }.should eq({1, 3}) }\n\n    it \"raises if not comparable\" do\n      expect_raises ArgumentError do\n        [-1.0, Float64::NAN, -3.0].minmax_of { |x| -x }\n      end\n    end\n  end\n\n  describe \"minmax_of?\" do\n    it \"returns two nils if empty\" do\n      ([] of Int32).minmax_of? { |x| -x }.should eq({nil, nil})\n    end\n  end\n\n  describe \"none?\" do\n    it { [1, 2, 2, 3].none? { |x| x == 1 }.should be_false }\n    it { [1, 2, 2, 3].none? { |x| x == 0 }.should be_true }\n  end\n\n  describe \"none? without block\" do\n    it { [nil, false].none?.should be_true }\n    it { [nil, false, true].none?.should be_false }\n  end\n\n  describe \"none? with pattern\" do\n    it { [2, 3, 4].none?(5..7).should be_true }\n    it { [1, false, nil].none?(Bool).should be_false }\n  end\n\n  describe \"one?\" do\n    it { [1, 2, 2, 3].one? { |x| x == 1 }.should be_true }\n    it { [1, 2, 2, 3].one? { |x| x == 2 }.should be_false }\n    it { [1, 2, 2, 3].one? { |x| x == 0 }.should be_false }\n    it { [1, 2, false].one?.should be_false }\n    it { [1, false, false].one?.should be_true }\n    it { [false].one?.should be_false }\n    it { [1, 5, 9].one?(3..6).should be_true }\n    it { [1, false, 2].one?(Int32).should be_false }\n  end\n\n  describe \"partition\" do\n    it { [1, 2, 2, 3].partition { |x| x == 2 }.should eq({[2, 2], [1, 3]}) }\n    it { [1, 2, 3, 4, 5, 6].partition(&.even?).should eq({[2, 4, 6], [1, 3, 5]}) }\n\n    it \"with mono type on union type\" do\n      ints, others = [1, true, nil, 3, false, \"string\", 'c'].partition(Int32)\n      ints.should eq([1, 3])\n      others.should eq([true, nil, false, \"string\", 'c'])\n      ints.should be_a(Array(Int32))\n      others.should be_a(Array(Bool | String | Char | Nil))\n    end\n\n    it \"with union type on union type\" do\n      ints_bools, others = [1, true, nil, 3, false, \"string\", 'c'].partition(Int32 | Bool)\n      ints_bools.should eq([1, true, 3, false])\n      others.should eq([nil, \"string\", 'c'])\n      ints_bools.should be_a(Array(Int32 | Bool))\n      others.should be_a(Array(String | Char | Nil))\n    end\n\n    it \"with missing type on union type\" do\n      symbols, others = [1, true, nil, 3, false, \"string\", 'c'].partition(Symbol)\n      symbols.empty?.should be_true\n      others.should eq([1, true, nil, 3, false, \"string\", 'c'])\n      symbols.should be_a(Array(Symbol))\n      others.should be_a(Array(Int32 | Bool | String | Char | Nil))\n    end\n\n    it \"with mono type on mono type\" do\n      ints, others = [1, 3].partition(Int32)\n      ints.should eq([1, 3])\n      others.empty?.should be_true\n      ints.should be_a(Array(Int32))\n      others.should be_a(Array(NoReturn))\n    end\n  end\n\n  describe \"reject\" do\n    it \"rejects the values for which the block returns true\" do\n      [1, 2, 3, 4].reject(&.even?).should eq([1, 3])\n    end\n\n    it \"rejects with pattern\" do\n      [1, 2, 3, 4, 5, 6].reject(2..4).should eq([1, 5, 6])\n    end\n\n    it \"with type\" do\n      ints = [1, true, false, 3].reject(Bool)\n      ints.should eq([1, 3])\n      ints.should be_a(Array(Int32))\n    end\n\n    it \"with type, for tuples\" do\n      ints = {1, true, false, 3}.reject(Int32)\n      ints.should eq([true, false])\n      ints.should be_a(Array(Bool))\n    end\n  end\n\n  describe \"sample\" do\n    describe \"single-element\" do\n      it \"samples without random\" do\n        [1].sample.should eq(1)\n\n        x = SpecEnumerable.new.sample\n        [1, 2, 3].should contain(x)\n      end\n\n      it \"samples with random\" do\n        SpecEnumerable.new.sample(Random.new(1)).should eq(1)\n        [1, 2, 3].sample(Random.new(1)).should eq(2)\n      end\n\n      it \"raises on empty self\" do\n        expect_raises(IndexError) { Array(Int32).new.sample }\n        expect_raises(IndexError) { SpecEmptyEnumerable.new.sample }\n      end\n    end\n\n    describe \"multiple-element\" do\n      it \"samples 0 elements\" do\n        ary = [1].sample(0)\n        ary.should eq([] of Int32)\n        ary.should be_a(Array(Int32))\n\n        ary = SpecEmptyEnumerable.new.sample(0)\n        ary.should eq([] of Int32)\n        ary.should be_a(Array(Int32))\n      end\n\n      it \"samples 1 element\" do\n        [1].sample(1).should eq([1])\n\n        x = [1, 2, 3].sample(1)\n        x.size.should eq(1)\n        x = x.first\n        [1, 2, 3].should contain(x)\n      end\n\n      it \"samples k elements out of n\" do\n        ary = [1].sample(1)\n        ary.should eq([1])\n\n        a = {1, 2, 3, 4, 5}\n        b = a.sample(3)\n        set = Set.new(b)\n        set.size.should eq(3)\n\n        set.each do |e|\n          a.should contain(e)\n        end\n      end\n\n      it \"raises on k < 0\" do\n        expect_raises(ArgumentError) { Array(Int32).new.sample(-1) }\n        expect_raises(ArgumentError) { SpecEnumerable.new.sample(-1) }\n      end\n\n      it \"samples k elements out of n, where k > n\" do\n        a = SpecEnumerable.new\n        b = a.sample(10)\n        b.size.should eq(3)\n        set = Set.new(b)\n        set.size.should eq(3)\n\n        set.each do |e|\n          a.should contain(e)\n        end\n\n        SpecEmptyEnumerable.new.sample(1).should eq([] of Int32)\n      end\n\n      it \"samples k elements out of n, with random\" do\n        a = (1..5)\n        b = a.sample(3, Random.new(1))\n        b.should eq([4, 3, 1])\n      end\n    end\n  end\n\n  describe \"select\" do\n    it \"selects the values for which the block returns true\" do\n      [1, 2, 3, 4].select(&.even?).should eq([2, 4])\n    end\n\n    it \"with pattern\" do\n      [1, 2, 3, 4, 5].select(2..4).should eq([2, 3, 4])\n    end\n\n    it \"with type\" do\n      ints = [1, true, nil, 3, false].select(Int32)\n      ints.should eq([1, 3])\n      ints.should be_a(Array(Int32))\n    end\n  end\n\n  describe \"skip\" do\n    it \"returns an array without the skipped elements\" do\n      [1, 2, 3, 4, 5, 6].skip(3).should eq [4, 5, 6]\n    end\n\n    it \"returns an empty array when skipping more elements than array size\" do\n      [1, 2].skip(3).should eq [] of Int32\n    end\n\n    it \"raises if count is negative\" do\n      expect_raises(ArgumentError) do\n        [1, 2].skip(-1)\n      end\n    end\n  end\n\n  describe \"skip_while\" do\n    it \"skips elements while the condition holds true\" do\n      result = [1, 2, 3, 4, 5, 0].skip_while { |i| i < 3 }\n      result.should eq [3, 4, 5, 0]\n    end\n\n    it \"returns an empty array if the condition is always true\" do\n      [1, 2, 3].skip_while { true }.should eq [] of Int32\n    end\n\n    it \"returns the full Array if the first check is false\" do\n      [5, 0, 1, 2, 3].skip_while { |x| x < 4 }.should eq [5, 0, 1, 2, 3]\n    end\n\n    it \"does not yield to the block anymore once it returned false\" do\n      called = 0\n      [1, 2, 3, 4, 4].skip_while do |i|\n        called += 1\n        i < 3\n      end\n      called.should eq 3\n    end\n  end\n\n  describe \"sum\" do\n    it { ([] of Int32).sum.should eq(0) }\n    it { [1, 2, 3].sum.should eq(6) }\n    it { [1, 2, 3].sum(4).should eq(10) }\n    it { [1, 2, 3].sum(4.5).should eq(10.5) }\n    it { (1..3).sum { |x| x * 2 }.should eq(12) }\n    it { (1..3).sum(1.5) { |x| x * 2 }.should eq(13.5) }\n    it { [1, 3_u64].sum(0_i32).should eq(4_u32) }\n    it { [1, 3].sum(0_u64).should eq(4_u64) }\n    it { [1, 10000000000_u64].sum(0_u64).should eq(10000000001) }\n    pending_wasm32 \"raises if union types are summed\", tags: %w[slow] do\n      assert_compile_error <<-CRYSTAL,\n        require \"prelude\"\n        [1, 10000000000_u64].sum\n        CRYSTAL\n        \"`Enumerable#sum` and `#product` do not support Union \" +\n        \"types. Instead, use `Enumerable#sum(initial)` and \" +\n        \"`#product(initial)`, respectively, with an initial value \" +\n        \"of the intended type of the call.\"\n    end\n\n    it \"uses additive_identity from type\" do\n      typeof([1, 2, 3].sum).should eq(Int32)\n      typeof([1.5, 2.5, 3.5].sum).should eq(Float64)\n      typeof([1, 2, 3].sum(&.to_f)).should eq(Float64)\n      typeof(([1, 2, 3] of Float32).sum).should eq(Float32)\n    end\n\n    it \"array of arrays\" do\n      [[1, 2, 3], [3, 4, 5]].sum.should eq [1, 2, 3, 3, 4, 5]\n      [[[1, 2], [3]], [[1, 2], [3, 4, 5]]].sum.should eq [[1, 2], [3], [1, 2], [3, 4, 5]]\n      Deque{[1, 2, 3], [3, 4, 5]}.sum.should eq [1, 2, 3, 3, 4, 5]\n    end\n\n    it \"strings\" do\n      [\"foo\", \"bar\"].sum.should eq \"foobar\"\n      [1, 2, 3].sum(&.to_s).should eq \"123\"\n    end\n\n    it \"float\" do\n      [1.0, 2.0, 3.5, 4.5].sum.should eq 11.0\n      ([1.0, 2.0, 3.5, 4.5] of Float32).sum.should eq 11.0\n    end\n\n    it \"slices\" do\n      [Slice[1, 2], Slice[3, 'a', 'b', 'c']].sum.should eq(Slice[1, 2, 3, 'a', 'b', 'c'])\n    end\n  end\n\n  describe \"product\" do\n    it { ([] of Int32).product.should eq(1) }\n    it { [1, 2, 3].product.should eq(6) }\n    it { [1, 2, 3].product(4).should eq(24) }\n    it { [1, 2, 3].product(4.5).should eq(27) }\n    it { (1..3).product { |x| x * 2 }.should eq(48) }\n    it { (1..3).product(1.5) { |x| x * 2 }.should eq(72) }\n\n    it \"uses zero from type\" do\n      typeof([1, 2, 3].product).should eq(Int32)\n      typeof([1.5, 2.5, 3.5].product).should eq(Float64)\n      typeof([1, 2, 3].product(&.to_f)).should eq(Float64)\n    end\n\n    it { [1, 3_u64].product(3_i32).should eq(9_u32) }\n    it { [1, 3].product(3_u64).should eq(9_u64) }\n    it { [1, 10000000000_u64].product(3_u64).should eq(30000000000_u64) }\n    pending_wasm32 \"raises if union types are multiplied\", tags: %w[slow] do\n      assert_compile_error <<-CRYSTAL,\n        require \"prelude\"\n        [1, 10000000000_u64].product\n        CRYSTAL\n        \"`Enumerable#sum` and `#product` do not support Union \" +\n        \"types. Instead, use `Enumerable#sum(initial)` and \" +\n        \"`#product(initial)`, respectively, with an initial value \" +\n        \"of the intended type of the call.\"\n    end\n  end\n\n  describe \"first\" do\n    it { (1..3).first(1).should eq([1]) }\n    it { (1..3).first(4).should eq([1, 2, 3]) }\n\n    it \"raises if count is negative\" do\n      expect_raises(ArgumentError) do\n        (1..2).first(-1)\n      end\n    end\n  end\n\n  describe \"take_while\" do\n    it \"keeps elements while the block returns true\" do\n      [1, 2, 3, 4, 5, 0].take_while { |i| i < 3 }.should eq [1, 2]\n    end\n\n    it \"returns the full Array if the condition is always true\" do\n      [1, 2, 3, -3].take_while { true }.should eq [1, 2, 3, -3]\n    end\n\n    it \"returns an empty Array if the block is false for the first element\" do\n      [1, 2, -1, 0].take_while { |i| i <= 0 }.should eq [] of Int32\n    end\n\n    it \"does not call the block again once it returned false\" do\n      called = 0\n      [1, 2, 3, 4, 0].take_while do |i|\n        called += 1\n        i < 3\n      end\n      called.should eq 3\n    end\n  end\n\n  describe \"tally_by\" do\n    it \"returns a hash with counts according to the value returned by the block\" do\n      %w[a A b B c C C].tally_by(&.downcase).should eq({\"a\" => 2, \"b\" => 2, \"c\" => 3})\n    end\n\n    context \"with hash\" do\n      it \"returns a hash with counts according to the value returned by the block\" do\n        hash = {} of Char => Int32\n        words = [\"Crystal\", \"Ruby\"]\n        words.each { |word| word.chars.tally_by(hash, &.downcase) }\n\n        hash.should eq(\n          {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}\n        )\n      end\n    end\n  end\n\n  describe \"tally\" do\n    it \"returns a hash with counts according to the value\" do\n      %w[1 2 3 3 3 2].tally.should eq({\"1\" => 1, \"2\" => 2, \"3\" => 3})\n    end\n\n    context \"with hash\" do\n      it \"returns a hash with counts according to the value\" do\n        hash = {} of Char => Int32\n        words = [\"crystal\", \"ruby\"]\n        words.each(&.chars.tally(hash))\n\n        hash.should eq(\n          {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}\n        )\n      end\n\n      it \"updates existing hash with counts according to the value\" do\n        hash = {'a' => 1, 'b' => 1, 'c' => 1, 'd' => 1}\n        words = [\"crystal\", \"ruby\"]\n        words.each(&.chars.tally(hash))\n\n        hash.should eq(\n          {'a' => 2, 'b' => 2, 'c' => 2, 'd' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'l' => 1, 'u' => 1}\n        )\n      end\n\n      it \"ignores the default value\" do\n        hash = Hash(Char, Int32).new(100)\n        words = [\"crystal\", \"ruby\"]\n        words.each(&.chars.tally(hash))\n\n        hash.should eq(\n          {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}\n        )\n      end\n\n      it \"returns a hash with Int64 counts according to the value\" do\n        hash = {} of Char => Int64\n        words = [\"crystal\", \"ruby\"]\n        words.each(&.chars.tally(hash))\n\n        hash.should eq(\n          {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}\n        )\n      end\n\n      it \"tallies an interface type\" do\n        InterfaceEnumerable.new.tally.should eq({One.new => 1, Two.new => 1})\n      end\n    end\n  end\n\n  describe \"to_a\" do\n    it \"converts to an Array\" do\n      (1..3).to_a.should eq [1, 2, 3]\n    end\n  end\n\n  describe \"to_h\" do\n    it \"for tuples\" do\n      hash = Tuple.new({\"a\", 1}, {\"c\", 2}).to_h\n      hash.should be_a(Hash(String, Int32))\n      hash.should eq({\"a\" => 1, \"c\" => 2})\n\n      hash = Tuple.new({1, 1.0}, {'a', \"aaa\"}).to_h\n      hash.should be_a(Hash(Int32 | Char, Float64 | String))\n      hash.should eq({1 => 1.0, 'a' => \"aaa\"})\n    end\n\n    it \"for array\" do\n      [['a', 'b'], ['c', 'd']].to_h.should eq({'a' => 'b', 'c' => 'd'})\n    end\n\n    it \"with block\" do\n      (1..3).to_h { |i| {i, i ** 2} }.should eq({1 => 1, 2 => 4, 3 => 9})\n    end\n  end\n\n  describe \"zip\" do\n    it \"works for Iterators as receiver\" do\n      SpecCountUpIterator.new(3).zip(1..3, 2..4).should eq([{0, 1, 2}, {1, 2, 3}, {2, 3, 4}])\n    end\n  end\n\n  describe \"zip?\" do\n    it \"works for Iterators as receiver\" do\n      SpecCountUpIterator.new(3).zip?(1..2, 2..4).should eq([{0, 1, 2}, {1, 2, 3}, {2, nil, 4}])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/env_spec.cr",
    "content": "require \"spec\"\nrequire \"./spec_helper\"\n\ndescribe \"ENV\" do\n  # Preserves the existing environment for each spec.\n  # To avoid potential circular definitions, this has to use the system methods\n  # directly, rather than `ENV` or `with_env`.\n  around_each do |example|\n    old_env = {} of String => String\n    Crystal::System::Env.each { |key, value| old_env[key] = value }\n\n    begin\n      example.run\n    ensure\n      keys = [] of String\n      Crystal::System::Env.each { |key| keys << key }\n      keys.each { |key| Crystal::System::Env.set(key, nil) }\n      old_env.each { |key, value| Crystal::System::Env.set(key, value) }\n    end\n  end\n\n  it \"gets non existent key raises\" do\n    expect_raises KeyError, \"Missing ENV key: \\\"NON-EXISTENT\\\"\" do\n      ENV[\"NON-EXISTENT\"]\n    end\n  end\n\n  it \"gets non existent key as nilable\" do\n    ENV[\"NON-EXISTENT\"]?.should be_nil\n  end\n\n  it \"set and gets\" do\n    (ENV[\"FOO\"] = \"1\").should eq(\"1\")\n    ENV[\"FOO\"].should eq(\"1\")\n    ENV[\"FOO\"]?.should eq(\"1\")\n  end\n\n  {% if flag?(:win32) %}\n    it \"sets and gets case-insensitive\" do\n      (ENV[\"FOO\"] = \"1\").should eq(\"1\")\n      ENV[\"Foo\"].should eq(\"1\")\n      ENV[\"foo\"]?.should eq(\"1\")\n    end\n  {% else %}\n    it \"sets and gets case-sensitive\" do\n      ENV[\"FOO\"] = \"1\"\n      ENV[\"foo\"]?.should be_nil\n    end\n  {% end %}\n\n  it \"sets to nil (same as delete)\" do\n    ENV[\"FOO\"] = \"1\"\n    ENV[\"FOO\"]?.should_not be_nil\n    ENV[\"FOO\"] = nil\n    ENV[\"FOO\"]?.should be_nil\n  end\n\n  it \"sets to empty string\" do\n    (ENV[\"FOO_EMPTY\"] = \"\").should eq \"\"\n    ENV[\"FOO_EMPTY\"]?.should eq \"\"\n  end\n\n  it \"does has_key?\" do\n    ENV[\"FOO\"] = \"1\"\n    ENV.has_key?(\"NON_EXISTENT\").should be_false\n    ENV.has_key?(\"FOO\").should be_true\n  end\n\n  it \"deletes a key\" do\n    ENV[\"FOO\"] = \"1\"\n    ENV.delete(\"FOO\").should eq(\"1\")\n    ENV.delete(\"FOO\").should be_nil\n    ENV.has_key?(\"FOO\").should be_false\n  end\n\n  it \"does .keys\" do\n    %w(FOO BAR).each { |k| ENV.keys.should_not contain(k) }\n    ENV[\"FOO\"] = ENV[\"BAR\"] = \"1\"\n    %w(FOO BAR).each { |k| ENV.keys.should contain(k) }\n  end\n\n  it \"does not have an empty key\" do\n    # Setting an empty key is invalid on both POSIX and Windows. So reporting an empty key\n    # would always be a bug. And there *was* a bug - see win32/ Crystal::System::Env.each\n    ENV.keys.should_not contain(\"\")\n  end\n\n  it \"does .values\" do\n    [1, 2].each { |i| ENV.values.should_not contain(\"SOMEVALUE_#{i}\") }\n    ENV[\"FOO\"] = \"SOMEVALUE_1\"\n    ENV[\"BAR\"] = \"SOMEVALUE_2\"\n    [1, 2].each { |i| ENV.values.should contain(\"SOMEVALUE_#{i}\") }\n  end\n\n  describe \"[]=\" do\n    it \"disallows NUL-bytes in key\" do\n      expect_raises(ArgumentError, \"String `key` contains null byte\") do\n        ENV[\"FOO\\0BAR\"] = \"something\"\n      end\n    end\n\n    it \"disallows NUL-bytes in key if value is nil\" do\n      expect_raises(ArgumentError, \"String `key` contains null byte\") do\n        ENV[\"FOO\\0BAR\"] = nil\n      end\n    end\n\n    it \"disallows NUL-bytes in value\" do\n      expect_raises(ArgumentError, \"String `value` contains null byte\") do\n        ENV[\"FOO\"] = \"BAR\\0BAZ\"\n      end\n    end\n  end\n\n  describe \"fetch\" do\n    it \"fetches with one argument\" do\n      ENV[\"1\"] = \"2\"\n      ENV.fetch(\"1\").should eq(\"2\")\n    end\n\n    it \"fetches with default value\" do\n      ENV[\"1\"] = \"2\"\n      ENV.fetch(\"1\", \"3\").should eq(\"2\")\n      ENV.fetch(\"2\", \"3\").should eq(\"3\")\n    end\n\n    it \"fetches with block\" do\n      ENV[\"1\"] = \"2\"\n      ENV.fetch(\"1\") { |k| k + \"block\" }.should eq(\"2\")\n      ENV.fetch(\"2\") { |k| k + \"block\" }.should eq(\"2block\")\n      ENV.fetch(\"3\") { 4 }.should eq(4)\n    end\n\n    it \"fetches and raises\" do\n      ENV[\"1\"] = \"2\"\n      expect_raises KeyError, \"Missing ENV key: \\\"2\\\"\" do\n        ENV.fetch(\"2\")\n      end\n    end\n\n    it \"fetches arbitrary default value\" do\n      ENV.fetch(\"nonexistent\", true).should be_true\n    end\n  end\n\n  it \"handles unicode\" do\n    ENV[\"TEST_UNICODE_1\"] = \"bar\\u{d7ff}\\u{10000}\"\n    ENV[\"TEST_UNICODE_2\"] = \"\\u{1234}\"\n    ENV[\"TEST_UNICODE_1\"].should eq \"bar\\u{d7ff}\\u{10000}\"\n    ENV[\"TEST_UNICODE_2\"].should eq \"\\u{1234}\"\n\n    values = {} of String => String\n    ENV.each do |key, value|\n      if key.starts_with?(\"TEST_UNICODE_\")\n        values[key] = value\n      end\n    end\n    values.should eq({\n      \"TEST_UNICODE_1\" => \"bar\\u{d7ff}\\u{10000}\",\n      \"TEST_UNICODE_2\" => \"\\u{1234}\",\n    })\n  end\n\n  it \"#to_h\" do\n    ENV[\"FOO\"] = \"foo\"\n    ENV.to_h[\"FOO\"].should eq \"foo\"\n  end\n\n  {% if flag?(:win32) %}\n    it \"skips internal environment variables\" do\n      key = \"=#{Path[Dir.current].drive}\"\n      ENV.has_key?(key).should be_false\n      ENV[key]?.should be_nil\n      expect_raises(ArgumentError) { ENV[key] = \"foo\" }\n      expect_raises(ArgumentError) { ENV[key] = nil }\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/errno_spec.cr",
    "content": "require \"spec\"\n\ndescribe Errno do\n  it \".value\" do\n    Errno.value = Errno::EACCES\n    Errno.value.should eq Errno::EACCES\n    Errno.value = Errno::EPERM\n    Errno.value.should eq Errno::EPERM\n  end\n\n  it \"#message\" do\n    Errno::EACCES.message.should eq \"Permission denied\"\n  end\nend\n"
  },
  {
    "path": "spec/std/exception/call_stack_spec.cr",
    "content": "require \"../spec_helper\"\n\ndescribe \"Backtrace\" do\n  it \"prints file line:column\", tags: %w[slow] do\n    source_file = datapath(\"backtrace_sample\")\n\n    # CallStack tries to make files relative to the current dir,\n    # so we do the same for tests\n    current_dir = Dir.current\n    current_dir += File::SEPARATOR unless current_dir.ends_with?(File::SEPARATOR)\n    source_file = source_file.lchop(current_dir)\n\n    _, output, _ = compile_and_run_file(source_file)\n\n    # resolved file:line:column (no column for MSVC PDB because of poor support\n    # by external tooling in general)\n    {% if flag?(:msvc) %}\n      output.should match(/^#{Regex.escape(source_file)}:3 in 'callee1'/m)\n      output.should match(/^#{Regex.escape(source_file)}:13 in 'callee3'/m)\n    {% else %}\n      output.should match(/^#{Regex.escape(source_file)}:3:10 in 'callee1'/m)\n      output.should match(/^#{Regex.escape(source_file)}:13:5 in 'callee3'/m)\n    {% end %}\n\n    # skipped internal details\n    output.should_not contain(\"src/callstack.cr\")\n    output.should_not contain(\"src/exception.cr\")\n    output.should_not contain(\"src/raise.cr\")\n  end\n\n  it \"doesn't relativize paths outside of current dir (#10169)\", tags: %w[slow] do\n    with_tempfile(\"source_file\") do |source_file|\n      source_path = Path.new(source_file)\n      source_path.absolute?.should be_true\n\n      File.write source_file, <<-CRYSTAL\n        def callee1\n          puts caller.join('\\n')\n        end\n\n        callee1\n        CRYSTAL\n      _, output, _ = compile_and_run_file(source_file)\n\n      output.should match /\\A(#{Regex.escape(source_path.to_s)}):/\n    end\n  end\n\n  it \"prints exception backtrace to stderr\", tags: %w[slow] do\n    sample = datapath(\"exception_backtrace_sample\")\n\n    _, output, error = compile_and_run_file(sample)\n\n    output.to_s.should be_empty\n    error.to_s.should contain(\"IndexError\")\n  end\n\n  {% if flag?(:openbsd) %}\n    # FIXME: the segfault handler doesn't work on OpenBSD\n    pending \"prints crash backtrace to stderr\"\n  {% else %}\n    it \"prints crash backtrace to stderr\", tags: %w[slow] do\n      sample = datapath(\"crash_backtrace_sample\")\n\n      _, output, error = compile_and_run_file(sample)\n\n      output.to_s.should be_empty\n      error.to_s.should contain(\"Invalid memory access\")\n    end\n  {% end %}\n\n  # Do not test this on platforms that cannot remove the current working\n  # directory of the process:\n  #\n  # Solaris: https://man.freebsd.org/cgi/man.cgi?query=rmdir&sektion=2&manpath=SunOS+5.10\n  # Windows: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rmdir-wrmdir?view=msvc-170#remarks\n  {% unless flag?(:win32) || flag?(:solaris) %}\n    it \"print exception with non-existing PWD\", tags: %w[slow] do\n      source_file = datapath(\"blank_test_file.txt\")\n      compile_file(source_file) do |executable_file|\n        output, error = IO::Memory.new, IO::Memory.new\n        with_tempdir(\"non-existent\") do\n          Dir.delete(Dir.current)\n          status = Process.run executable_file\n\n          status.success?.should be_true\n        end\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/exception_spec.cr",
    "content": "require \"./spec_helper\"\n\nprivate class FooError < Exception\n  def message\n    \"#{super || \"\"} -- bar!\"\n  end\nend\n\ndescribe \"Exception\" do\n  it \"allows subclassing #message\" do\n    ex = FooError.new(\"foo?\")\n    ex.message.should eq(\"foo? -- bar!\")\n    ex.to_s.should eq(\"foo? -- bar!\")\n    ex.inspect_with_backtrace.should contain(\"foo? -- bar!\")\n  end\n\n  it \"inspects\" do\n    ex = FooError.new(\"foo?\")\n    ex.inspect.should eq(\"#<FooError:foo? -- bar!>\")\n  end\n\n  it \"inspects with cause\" do\n    cause = Exception.new(\"inner\")\n    ex = expect_raises(Exception, \"wrapper\") do\n      begin\n        raise cause\n      rescue ex\n        raise Exception.new(\"wrapper\", cause: ex)\n      end\n    end\n\n    ex.cause.should be(cause)\n    ex.inspect_with_backtrace.should contain(\"wrapper\")\n    ex.inspect_with_backtrace.should contain(\"Caused by\")\n    ex.inspect_with_backtrace.should contain(\"inner\")\n  end\n\n  it \"collect memory within ensure block\", tags: %w[slow] do\n    sample = datapath(\"collect_within_ensure\")\n\n    _, output, error = compile_and_run_file(sample, [\"--release\"])\n\n    output.to_s.should be_empty\n    error.to_s.should contain(\"Unhandled exception: Oh no! (Exception)\")\n    error.to_s.should_not contain(\"Invalid memory access\")\n    error.to_s.should_not contain(\"Illegal instruction\")\n  end\nend\n"
  },
  {
    "path": "spec/std/fiber/execution_context/global_queue_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../../../support/thread\"\n\ndescribe Fiber::ExecutionContext::GlobalQueue do\n  it \"#initialize\" do\n    q = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n    q.empty?.should be_true\n  end\n\n  it \"#unsafe_push and #unsafe_pop\" do\n    f1 = new_fake_fiber(\"f1\")\n    f2 = new_fake_fiber(\"f2\")\n    f3 = new_fake_fiber(\"f3\")\n\n    q = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n    q.unsafe_push(f1)\n    q.size.should eq(1)\n\n    q.unsafe_push(f2)\n    q.unsafe_push(f3)\n    q.size.should eq(3)\n\n    q.unsafe_pop?.should be(f3)\n    q.size.should eq(2)\n\n    q.unsafe_pop?.should be(f2)\n    q.unsafe_pop?.should be(f1)\n    q.unsafe_pop?.should be_nil\n    q.size.should eq(0)\n    q.empty?.should be_true\n  end\n\n  describe \"#unsafe_grab?\" do\n    it \"can't grab from empty queue\" do\n      q = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      runnables = Fiber::ExecutionContext::Runnables(6).new(q)\n      q.unsafe_grab?(runnables, 4).should be_nil\n    end\n\n    it \"grabs fibers\" do\n      q = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      fibers = Array.new(10) { |i| new_fake_fiber(\"f#{i}\") }\n      fibers.each { |f| q.unsafe_push(f) }\n\n      runnables = Fiber::ExecutionContext::Runnables(6).new(q)\n      fiber = q.unsafe_grab?(runnables, 4)\n\n      # returned the last enqueued fiber\n      fiber.should be(fibers[9])\n\n      # enqueued the next 2 fibers\n      runnables.size.should eq(2)\n      runnables.shift?.should be(fibers[8])\n      runnables.shift?.should be(fibers[7])\n\n      # the remaining fibers are still there:\n      6.downto(0).each do |i|\n        q.unsafe_pop?.should be(fibers[i])\n      end\n    end\n\n    it \"can't grab more than available\" do\n      f = new_fake_fiber\n      q = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      q.unsafe_push(f)\n\n      # dequeues the unique fiber\n      runnables = Fiber::ExecutionContext::Runnables(6).new(q)\n      fiber = q.unsafe_grab?(runnables, 4)\n      fiber.should be(f)\n\n      # had nothing left to dequeue\n      runnables.size.should eq(0)\n    end\n\n    it \"clamps divisor to 1\" do\n      f = new_fake_fiber\n      q = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      q.unsafe_push(f)\n\n      # dequeues the unique fiber\n      runnables = Fiber::ExecutionContext::Runnables(6).new(q)\n      fiber = q.unsafe_grab?(runnables, 0)\n      fiber.should be(f)\n\n      # had nothing left to dequeue\n      runnables.size.should eq(0)\n    end\n  end\n\n  # interpreter doesn't support threads yet (#14287)\n  pending_interpreted describe: \"thread safety\" do\n    it \"one by one\", tags: %w[slow] do\n      {% if flag?(:win32) && flag?(:aarch64) %}\n        pending! \"CI/WIN32/CLANGARM64 always fails\"\n      {% end %}\n\n      fibers = StaticArray(Fiber::ExecutionContext::FiberCounter, 763).new do |i|\n        Fiber::ExecutionContext::FiberCounter.new(new_fake_fiber(\"f#{i}\"))\n      end\n\n      n = 7\n      increments = 15\n\n      queue = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n\n      Fiber::ExecutionContext.stress_test(\n        n,\n        iteration: ->(i : Int32) {\n          if fiber = queue.pop?\n            fc = fibers.find! { |x| x.@fiber == fiber }\n            queue.push(fiber) if fc.increment < increments\n            return :next\n          end\n\n          # done?\n          if fibers.all? { |fc| fc.counter >= increments }\n            return :break\n          end\n        },\n        publish: -> {\n          fibers.each_with_index do |fc, i|\n            queue.push(fc.@fiber)\n            Thread.sleep(10.nanoseconds) if i % 10 == 9\n          end\n        },\n      )\n\n      # must have dequeued each fiber exactly X times\n      fibers.each { |fc| fc.counter.should eq(increments) }\n    end\n\n    it \"bulk operations\", tags: %w[slow] do\n      n = 7\n      increments = 15\n\n      fibers = StaticArray(Fiber::ExecutionContext::FiberCounter, 765).new do |i| # 765 can be divided by 3 and 5\n        Fiber::ExecutionContext::FiberCounter.new(new_fake_fiber(\"f#{i}\"))\n      end\n\n      queue = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n\n      runnables = Array.new(n) { Fiber::ExecutionContext::Runnables(3).new(queue) }\n      batches = Array.new(n) { Fiber::List.new }\n\n      reenqueue = ->(batch : Pointer(Fiber::List)) {\n        if batch.value.size > 0\n          queue.bulk_push(batch)\n          names = [] of String?\n          batch.value.each { |f| names << f.name }\n          batch.value.clear\n        end\n      }\n\n      execute = ->(fiber : Fiber, batch : Pointer(Fiber::List)) {\n        fc = fibers.find! { |x| x.@fiber == fiber }\n\n        if fc.increment < increments\n          batch.value.push(fc.@fiber)\n        end\n      }\n\n      Fiber::ExecutionContext.stress_test(\n        n,\n        iteration: ->(i : Int32) {\n          r = runnables[i]\n          batch = batches.to_unsafe + i\n\n          if fiber = r.shift?\n            execute.call(fiber, batch)\n            return :next\n          end\n\n          if fiber = queue.grab?(r, 1)\n            reenqueue.call(batch)\n            execute.call(fiber, batch)\n            return :next\n          end\n\n          # done?\n          if fibers.all? { |fc| fc.counter >= increments }\n            return :break\n          end\n\n          reenqueue.call(batch)\n        },\n        publish: -> {\n          # enqueue in batches of 5\n          0.step(to: fibers.size - 1, by: 5) do |i|\n            list = Fiber::List.new\n            5.times { |j| list.push(fibers[i + j].@fiber) }\n            queue.bulk_push(pointerof(list))\n            Thread.sleep(10.nanoseconds) if i % 4 == 3\n          end\n        }\n      )\n\n      # must have dequeued each fiber exactly X times (no less, no more)\n      fibers.each { |fc| fc.counter.should eq(increments) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/fiber/execution_context/parallel_spec.cr",
    "content": "{% skip_file unless flag?(:execution_context) %}\nrequire \"spec\"\nrequire \"wait_group\"\n\ndescribe Fiber::ExecutionContext::Parallel do\n  it \".new\" do\n    mt = Fiber::ExecutionContext::Parallel.new(\"test\", maximum: 2)\n    mt.size.should eq(0)\n    mt.capacity.should eq(2)\n\n    expect_raises(ArgumentError, \"Parallelism can't be less than one\") do\n      Fiber::ExecutionContext::Parallel.new(\"test\", maximum: -1)\n    end\n\n    expect_raises(ArgumentError, \"Parallelism can't be less than one\") do\n      Fiber::ExecutionContext::Parallel.new(\"test\", maximum: 0)\n    end\n\n    # the following are deprecated constructors\n\n    mt = Fiber::ExecutionContext::Parallel.new(\"test\", size: 0..2)\n    mt.size.should eq(0)\n    mt.capacity.should eq(2)\n\n    mt = Fiber::ExecutionContext::Parallel.new(\"test\", size: ..4)\n    mt.size.should eq(0)\n    mt.capacity.should eq(4)\n\n    mt = Fiber::ExecutionContext::Parallel.new(\"test\", size: 1..5)\n    mt.size.should eq(0)\n    mt.capacity.should eq(5)\n\n    mt = Fiber::ExecutionContext::Parallel.new(\"test\", size: 1...5)\n    mt.size.should eq(0)\n    mt.capacity.should eq(4)\n\n    expect_raises(ArgumentError, \"Parallelism can't be less than one.\") do\n      Fiber::ExecutionContext::Parallel.new(\"test\", size: 0...1)\n    end\n\n    expect_raises(ArgumentError, \"invalid range\") do\n      Fiber::ExecutionContext::Parallel.new(\"test\", size: 5..1)\n    end\n  end\n\n  it \"#resize\" do\n    ctx = Fiber::ExecutionContext::Parallel.new(\"ctx\", 1)\n    running = Atomic(Bool).new(true)\n    wg = WaitGroup.new\n\n    10.times do\n      wg.add(1)\n\n      ctx.spawn do\n        while running.get(:relaxed)\n          sleep(10.microseconds)\n        end\n      ensure\n        wg.done\n      end\n    end\n\n    # it grows\n    ctx.resize(4)\n    ctx.capacity.should eq(4)\n\n    # it shrinks\n    ctx.resize(2)\n    ctx.capacity.should eq(2)\n\n    # it doesn't change\n    ctx.resize(2)\n    ctx.capacity.should eq(2)\n\n    10.times do\n      n = rand(1..4)\n      ctx.resize(n)\n      ctx.capacity.should eq(n)\n    end\n\n    running.set(false)\n    wg.wait\n  end\nend\n"
  },
  {
    "path": "spec/std/fiber/execution_context/runnables_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../../../support/thread\"\n\ndescribe Fiber::ExecutionContext::Runnables do\n  it \"#initialize\" do\n    g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n    r = Fiber::ExecutionContext::Runnables(16).new(g)\n    r.capacity.should eq(16)\n  end\n\n  describe \"#push\" do\n    it \"enqueues the fiber in local queue\" do\n      fibers = Array.new(4) { |i| new_fake_fiber(\"f#{i}\") }\n\n      # local enqueue\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r = Fiber::ExecutionContext::Runnables(4).new(g)\n      fibers.each { |f| r.push(f) }\n\n      # local dequeue\n      fibers.each { |f| r.shift?.should be(f) }\n      r.shift?.should be_nil\n\n      # didn't push to global queue\n      g.pop?.should be_nil\n    end\n\n    it \"moves half the local queue to the global queue on overflow\" do\n      fibers = Array.new(5) { |i| new_fake_fiber(\"f#{i}\") }\n\n      # local enqueue + overflow\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r = Fiber::ExecutionContext::Runnables(4).new(g)\n      fibers.each { |f| r.push(f) }\n\n      # kept half of local queue\n      r.shift?.should be(fibers[2])\n      r.shift?.should be(fibers[3])\n\n      # moved half of local queue + last push to global queue\n      g.pop?.should eq(fibers[0])\n      g.pop?.should eq(fibers[1])\n      g.pop?.should eq(fibers[4])\n    end\n\n    it \"can always push up to capacity\" do\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r = Fiber::ExecutionContext::Runnables(4).new(g)\n\n      4.times do\n        # local\n        4.times { r.push(new_fake_fiber) }\n        2.times { r.shift? }\n        2.times { r.push(new_fake_fiber) }\n\n        # overflow (2+1 fibers are sent to global queue + 1 local)\n        2.times { r.push(new_fake_fiber) }\n\n        # clear\n        3.times { r.shift? }\n      end\n\n      # on each iteration we pushed 2+1 fibers to the global queue\n      g.size.should eq(12)\n\n      # grab fibers back from the global queue\n      fiber = g.unsafe_grab?(r, divisor: 1)\n      fiber.should_not be_nil\n      r.shift?.should_not be_nil\n      r.shift?.should be_nil\n    end\n  end\n\n  describe \"#drain\" do\n    it \"drains the local queue into the global queue\" do\n      fibers = Array.new(6) { |i| new_fake_fiber(\"f#{i}\") }\n\n      # local enqueue + overflow\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r = Fiber::ExecutionContext::Runnables(6).new(g)\n\n      # empty\n      r.drain\n      g.size.should eq(0)\n\n      # full\n      fibers.each { |f| r.push(f) }\n      r.drain\n      r.shift?.should be_nil\n      g.size.should eq(6)\n\n      # refill half (1 pop + 2 grab) and drain again\n      g.unsafe_grab?(r, divisor: 1)\n      r.drain\n      r.shift?.should be_nil\n      g.size.should eq(5)\n    end\n  end\n\n  describe \"#bulk_push\" do\n    it \"fills the local queue\" do\n      l = Fiber::List.new\n      fibers = Array.new(4) { |i| new_fake_fiber(\"f#{i}\") }\n      fibers.each { |f| l.push(f) }\n\n      # local enqueue\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r = Fiber::ExecutionContext::Runnables(4).new(g)\n      r.bulk_push(pointerof(l))\n\n      fibers.reverse_each { |f| r.shift?.should be(f) }\n      g.empty?.should be_true\n    end\n\n    it \"pushes the overflow to the global queue\" do\n      l = Fiber::List.new\n      fibers = Array.new(7) { |i| new_fake_fiber(\"f#{i}\") }\n      fibers.each { |f| l.push(f) }\n\n      # local enqueue + overflow\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r = Fiber::ExecutionContext::Runnables(4).new(g)\n      r.bulk_push(pointerof(l))\n\n      # filled the local queue\n      r.shift?.should eq(fibers[6])\n      r.shift?.should eq(fibers[5])\n      r.shift?.should be(fibers[4])\n      r.shift?.should be(fibers[3])\n\n      # moved the rest to the global queue\n      g.pop?.should eq(fibers[2])\n      g.pop?.should eq(fibers[1])\n      g.pop?.should eq(fibers[0])\n    end\n  end\n\n  describe \"#shift?\" do\n    # TODO: need specific tests (though we already use it in the above tests?)\n  end\n\n  describe \"#steal_from\" do\n    it \"steals from another runnables\" do\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      fibers = Array.new(6) { |i| new_fake_fiber(\"f#{i}\") }\n\n      # fill the source queue\n      r1 = Fiber::ExecutionContext::Runnables(16).new(g)\n      fibers.each { |f| r1.push(f) }\n\n      # steal from source queue\n      r2 = Fiber::ExecutionContext::Runnables(16).new(g)\n      fiber = r2.steal_from(r1)\n\n      # stole half of the runnable fibers\n      fiber.should be(fibers[2])\n      r2.shift?.should be(fibers[0])\n      r2.shift?.should be(fibers[1])\n      r2.shift?.should be_nil\n\n      # left the other half\n      r1.shift?.should be(fibers[3])\n      r1.shift?.should be(fibers[4])\n      r1.shift?.should be(fibers[5])\n      r1.shift?.should be_nil\n\n      # global queue is left untouched\n      g.empty?.should be_true\n    end\n\n    it \"steals the last fiber\" do\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      lone = new_fake_fiber(\"lone\")\n\n      # fill the source queue\n      r1 = Fiber::ExecutionContext::Runnables(16).new(g)\n      r1.push(lone)\n\n      # steal from source queue\n      r2 = Fiber::ExecutionContext::Runnables(16).new(g)\n      fiber = r2.steal_from(r1)\n\n      # stole the fiber & local queue is still empty\n      fiber.should be(lone)\n      r2.shift?.should be_nil\n\n      # left nothing in original queue\n      r1.shift?.should be_nil\n\n      # global queue is left untouched\n      g.empty?.should be_true\n    end\n\n    it \"steals nothing\" do\n      g = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n      r1 = Fiber::ExecutionContext::Runnables(16).new(g)\n      r2 = Fiber::ExecutionContext::Runnables(16).new(g)\n\n      fiber = r2.steal_from(r1)\n      fiber.should be_nil\n      r2.shift?.should be_nil\n      r1.shift?.should be_nil\n    end\n  end\n\n  # interpreter doesn't support threads yet (#14287)\n  pending_interpreted describe: \"thread safety\" do\n    it \"stress test\", tags: %w[slow] do\n      n = 7\n      increments = 7919\n\n      # less fibers than space in runnables (so threads can starve)\n      # 54 is roughly half of 16 × 7 and can be divided by 9 (for batch enqueues below)\n      fibers = Array(Fiber::ExecutionContext::FiberCounter).new(54) do |i|\n        Fiber::ExecutionContext::FiberCounter.new(new_fake_fiber(\"f#{i}\"))\n      end\n\n      global_queue = Fiber::ExecutionContext::GlobalQueue.new(Thread::Mutex.new)\n\n      all_runnables = Array(Fiber::ExecutionContext::Runnables(16)).new(n) do\n        Fiber::ExecutionContext::Runnables(16).new(global_queue)\n      end\n\n      all_randoms = Array.new(n) { Random.split }\n\n      execute = ->(fiber : Fiber, runnables : Fiber::ExecutionContext::Runnables(16)) {\n        fc = fibers.find! { |x| x.@fiber == fiber }\n        runnables.push(fiber) if fc.increment < increments\n      }\n\n      Fiber::ExecutionContext.stress_test(\n        n,\n        iteration: ->(i : Int32) {\n          runnables = all_runnables[i]\n          random = all_randoms[i]\n\n          # dequeue from local queue\n          if fiber = runnables.shift?\n            execute.call(fiber, runnables)\n            return :next\n          end\n\n          # steal from another queue\n          j = 0\n          while (r = all_runnables.sample(random)) == runnables\n            next if (j += 1) < 1000\n            raise \"FATAL: all_runnables.sample returned the local queue 1000 times!\"\n          end\n          if fiber = runnables.steal_from(r)\n            execute.call(fiber, runnables)\n            return :next\n          end\n\n          # dequeue from global queue\n          if fiber = global_queue.grab?(runnables, n)\n            execute.call(fiber, runnables)\n            return :next\n          end\n\n          # done?\n          if fibers.all? { |fc| fc.counter >= increments }\n            return :break\n          end\n        },\n        publish: -> {\n          # enqueue in batches of 9\n          0.step(to: fibers.size - 1, by: 9) do |i|\n            list = Fiber::List.new\n            9.times { |j| list.push(fibers[i + j].@fiber) }\n            global_queue.bulk_push(pointerof(list))\n            Thread.sleep(10.nanoseconds) if i % 2 == 1\n          end\n        },\n      )\n\n      # must have dequeued each fiber exactly X times (no less, no more)\n      fibers.each { |fc| fc.counter.should eq(increments) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/fiber/execution_context/spec_helper.cr",
    "content": "require \"../../spec_helper\"\nrequire \"../../../support/fibers\"\nrequire \"crystal/system/thread_wait_group\"\nrequire \"fiber/execution_context/runnables\"\nrequire \"fiber/execution_context/global_queue\"\n\nmodule Fiber::ExecutionContext\n  class FiberCounter\n    def initialize(@fiber : Fiber)\n      @counter = Atomic(Int32).new(0)\n    end\n\n    # fetch and add\n    def increment\n      @counter.add(1, :relaxed) + 1\n    end\n\n    def counter\n      @counter.get(:relaxed)\n    end\n  end\n\n  # Runs a multithreaded test by starting *n* threads, waiting for all the\n  # threads to have been started, then runs the *publish* proc.\n  #\n  # Each thread calls *iteration* until the timeout is reached or the proc\n  # returns `:break`; if the proc returns `:next` the thread goes immediately to\n  # the next iteration, other it will ease the CPU before the next iteration.\n  #\n  # Returns after every thread has been joined.\n  def self.stress_test(n, *, iteration, publish, name = \"STRESS\", timeout = 5.seconds)\n    ready = Thread::WaitGroup.new(n)\n\n    threads = Array.new(n) do |i|\n      new_thread(\"#{name}-#{i}\") do\n        ready.done\n\n        started = Crystal::System::Time.instant\n        attempts = 0\n\n        iter = 0\n        while iter += 1\n          if iter % 100 == 99 && (Crystal::System::Time.instant - started) >= timeout\n            # reached timeout: abort\n            break\n          end\n\n          case iteration.call(i)\n          when :next\n            attempts = 0\n            next\n          when :break\n            break\n          else\n            # don't burn CPU\n            attempts = Thread.delay(attempts)\n          end\n        end\n      end\n    end\n\n    ready.wait(timeout * 2) do\n      raise \"timeout while waiting for threads to be ready\"\n    end\n\n    publish.call\n\n    threads.each(&.join)\n  end\nend\n"
  },
  {
    "path": "spec/std/fiber/list_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/fibers\"\nrequire \"fiber/list\"\n\ndescribe Fiber::List do\n  describe \"#initialize\" do\n    it \"creates an empty queue\" do\n      list = Fiber::List.new\n      list.@head.should be_nil\n      list.@tail.should be_nil\n      list.size.should eq(0)\n      list.empty?.should be_true\n    end\n\n    it \"creates a filled queue\" do\n      f1 = new_fake_fiber(\"f1\")\n      f2 = new_fake_fiber(\"f2\")\n      f1.list_next = f2\n      f2.list_next = nil\n\n      list = Fiber::List.new(f2, f1, size: 2)\n      list.@head.should be(f2)\n      list.@tail.should be(f1)\n      list.size.should eq(2)\n      list.empty?.should be_false\n    end\n  end\n\n  describe \"#push\" do\n    it \"to head\" do\n      list = Fiber::List.new\n      f1 = new_fake_fiber(\"f1\")\n      f2 = new_fake_fiber(\"f2\")\n      f3 = new_fake_fiber(\"f3\")\n\n      # simulate fibers previously added to other queues\n      f1.list_next = f3\n      f2.list_next = f1\n\n      # push first fiber\n      list.push(f1)\n      list.@head.should be(f1)\n      list.@tail.should be(f1)\n      f1.list_next.should be_nil\n      list.size.should eq(1)\n\n      # push second fiber\n      list.push(f2)\n      list.@head.should be(f2)\n      list.@tail.should be(f1)\n      f2.list_next.should be(f1)\n      f1.list_next.should be_nil\n      list.size.should eq(2)\n\n      # push third fiber\n      list.push(f3)\n      list.@head.should be(f3)\n      list.@tail.should be(f1)\n      f3.list_next.should be(f2)\n      f2.list_next.should be(f1)\n      f1.list_next.should be_nil\n      list.size.should eq(3)\n    end\n  end\n\n  describe \"#bulk_unshift\" do\n    it \"to empty queue\" do\n      # manually create a queue\n      f1 = new_fake_fiber(\"f1\")\n      f2 = new_fake_fiber(\"f2\")\n      f3 = new_fake_fiber(\"f3\")\n      f3.list_next = f2\n      f2.list_next = f1\n      f1.list_next = nil\n      q1 = Fiber::List.new(f3, f1, size: 3)\n\n      # push in bulk\n      q2 = Fiber::List.new(nil, nil, size: 0)\n      q2.bulk_unshift(pointerof(q1))\n      q2.@head.should be(f3)\n      q2.@tail.should be(f1)\n      q2.size.should eq(3)\n    end\n\n    it \"to filled queue\" do\n      f1 = new_fake_fiber(\"f1\")\n      f2 = new_fake_fiber(\"f2\")\n      f3 = new_fake_fiber(\"f3\")\n      f4 = new_fake_fiber(\"f4\")\n      f5 = new_fake_fiber(\"f5\")\n\n      # source queue\n      f3.list_next = f2\n      f2.list_next = f1\n      f1.list_next = nil\n      q1 = Fiber::List.new(f3, f1, size: 3)\n\n      # destination queue\n      f5.list_next = f4\n      f4.list_next = nil\n      q2 = Fiber::List.new(f5, f4, size: 2)\n\n      # push in bulk\n      q2.bulk_unshift(pointerof(q1))\n      q2.@head.should be(f5)\n      q2.@tail.should be(f1)\n      q2.size.should eq(5)\n\n      f5.list_next.should be(f4)\n      f4.list_next.should be(f3)\n      f3.list_next.should be(f2)\n      f2.list_next.should be(f1)\n      f1.list_next.should be(nil)\n    end\n  end\n\n  describe \"#pop\" do\n    it \"from head\" do\n      f1 = new_fake_fiber(\"f1\")\n      f2 = new_fake_fiber(\"f2\")\n      f3 = new_fake_fiber(\"f3\")\n      f3.list_next = f2\n      f2.list_next = f1\n      f1.list_next = nil\n      list = Fiber::List.new(f3, f1, size: 3)\n\n      # removes third element\n      list.pop.should be(f3)\n      list.@head.should be(f2)\n      list.@tail.should be(f1)\n      list.size.should eq(2)\n\n      # removes second element\n      list.pop.should be(f2)\n      list.@head.should be(f1)\n      list.@tail.should be(f1)\n      list.size.should eq(1)\n\n      # removes first element\n      list.pop.should be(f1)\n      list.@head.should be_nil\n      list.@tail.should be_nil\n      list.size.should eq(0)\n\n      # empty queue\n      expect_raises(IndexError) { list.pop }\n      list.size.should eq(0)\n    end\n  end\n\n  describe \"#pop?\" do\n    it \"from head\" do\n      f1 = new_fake_fiber(\"f1\")\n      f2 = new_fake_fiber(\"f2\")\n      f3 = new_fake_fiber(\"f3\")\n      f3.list_next = f2\n      f2.list_next = f1\n      f1.list_next = nil\n      list = Fiber::List.new(f3, f1, size: 3)\n\n      # removes third element\n      list.pop?.should be(f3)\n      list.@head.should be(f2)\n      list.@tail.should be(f1)\n      list.size.should eq(2)\n\n      # removes second element\n      list.pop?.should be(f2)\n      list.@head.should be(f1)\n      list.@tail.should be(f1)\n      list.size.should eq(1)\n\n      # removes first element\n      list.pop?.should be(f1)\n      list.@head.should be_nil\n      list.@tail.should be_nil\n      list.size.should eq(0)\n\n      # empty queue\n      list.pop?.should be_nil\n      list.size.should eq(0)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/fiber_spec.cr",
    "content": "require \"./sync/spec_helper\"\n\ndescribe Fiber do\n  it \"#resumable?\" do\n    ch = Channel(Bool).new\n\n    Sync::CONCURRENT.spawn do\n      fiber = spawn do\n        # not resumable: fiber is running (sent second)\n        ch.send Fiber.current.resumable?\n      end\n\n      # resumable: fiber hasn't been resumed (sent first)\n      ch.send fiber.resumable?\n    end\n\n    {true, false}.each do |expected|\n      select\n      when resumable = ch.receive\n        resumable.should eq(expected)\n      when timeout(1.second)\n        raise \"reached timeout\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/file/match-fast-glob_spec.cr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_glob_specs.cr\n#\n# DO NOT EDIT\n\n# These tests are autogenerated from https://raw.githubusercontent.com/oxc-project/fast-glob/refs/heads/main/tests/test.rs\n# They are are  collection of tests from bash and micromatch\n# https://github.com/micromatch/picomatch/blob/master/test/bash.js.\n\nrequire \"spec\"\n\nprivate def assert_file_matches(pattern, path : String, *, file = __FILE__, line = __LINE__)\n  File.match?(pattern, path).should be_true, file: file, line: line\n  File.match?(pattern, Path.posix(path)).should be_true, file: file, line: line\n  File.match?(pattern, Path.posix(path).to_windows(mappings: false)).should be_true, file: file, line: line\nend\n\nprivate def refute_file_matches(pattern, path : String, *, file = __FILE__, line = __LINE__)\n  File.match?(pattern, path).should be_false, file: file, line: line\n  File.match?(pattern, Path.posix(path)).should be_false, file: file, line: line\n  File.match?(pattern, Path.posix(path).to_windows(mappings: false)).should be_false, file: file, line: line\nend\n\ndescribe \"File .match? bash tests\" do\n  it \"webpack\" do\n    # Match everything\n    assert_file_matches \"**/*\", \"foo\"\n\n    # Match the end\n    assert_file_matches \"**/f*\", \"foo\"\n\n    # Match the start\n    assert_file_matches \"**/*o\", \"foo\"\n\n    # Match the middle\n    assert_file_matches \"**/f*uck\", \"firetruck\"\n\n    # Don't match without Regexp 'g'\n    refute_file_matches \"**/uc\", \"firetruck\"\n\n    # Match zero characters\n    assert_file_matches \"**/f*uck\", \"fuck\"\n\n    # More complex matches\n    assert_file_matches \"**/*.min.js\", \"some/jquery.min.js\"\n    assert_file_matches \"**/*.min.*\", \"some/jquery.min.js\"\n    assert_file_matches \"*/js/*.js\", \"some/js/jquery.min.js\"\n\n    # More complex matches with RegExp 'g' flag (complex regression)\n    assert_file_matches \"**/*.min.*\", \"some/jquery.min.js\"\n    assert_file_matches \"**/*.min.js\", \"some/jquery.min.js\"\n    assert_file_matches \"*/js/*.js\", \"some/js/jquery.min.js\"\n\n    refute_file_matches \"\\\\\\\\/$^+?.()=!|{},[].*\", \"\\\\/$^+?.()=!|{},[].*\"\n\n    # Equivalent matches without/with using RegExp 'g'\n    refute_file_matches \"**/.min.\", \"some/jquery.min.js\"\n    assert_file_matches \"**/*.min.*\", \"some/jquery.min.js\"\n    refute_file_matches \"**/.min.\", \"some/jquery.min.js\"\n\n    refute_file_matches \"**/min.js\", \"some/jquery.min.js\"\n    assert_file_matches \"**/*.min.js\", \"some/jquery.min.js\"\n    refute_file_matches \"**/min.js\", \"some/jquery.min.js\"\n\n    # Match anywhere (globally) using RegExp 'g'\n    refute_file_matches \"**/min\", \"some/jquery.min.js\"\n    refute_file_matches \"/js/\", \"some/js/jquery.min.js\"\n\n    refute_file_matches \"/js*jq*.js\", \"some/js/jquery.min.js\"\n\n    # Extended mode\n\n    # ?: Match one character, no more and no less\n    assert_file_matches \"**/f?o\", \"foo\"\n    refute_file_matches \"**/f?o\", \"fooo\"\n    refute_file_matches \"**/f?oo\", \"foo\"\n\n    # ?: Match one character with RegExp 'g'\n    assert_file_matches \"**/f?o\", \"foo\"\n    refute_file_matches \"**/f?o\", \"fooo\"\n    assert_file_matches \"**/f?o?\", \"fooo\"\n    refute_file_matches \"**/?fo\", \"fooo\"\n    refute_file_matches \"**/f?oo\", \"foo\"\n    refute_file_matches \"**/foo?\", \"foo\"\n\n    # []: Match a character range\n    assert_file_matches \"**/fo[oz]\", \"foo\"\n    assert_file_matches \"**/fo[oz]\", \"foz\"\n    refute_file_matches \"**/fo[oz]\", \"fog\"\n\n    # []: Match a character range and RegExp 'g' (regression)\n    assert_file_matches \"**/fo[oz]\", \"foo\"\n    assert_file_matches \"**/fo[oz]\", \"foz\"\n    refute_file_matches \"**/fo[oz]\", \"fog\"\n\n    # {}: Match a choice of different substrings\n    assert_file_matches \"**/foo{bar,baaz}\", \"foobaaz\"\n    assert_file_matches \"**/foo{bar,baaz}\", \"foobar\"\n    refute_file_matches \"**/foo{bar,baaz}\", \"foobuzz\"\n    assert_file_matches \"**/foo{bar,b*z}\", \"foobuzz\"\n\n    # {}: Match a choice of different substrings and RegExp 'g' (regression)\n    assert_file_matches \"**/foo{bar,baaz}\", \"foobaaz\"\n    assert_file_matches \"**/foo{bar,baaz}\", \"foobar\"\n    refute_file_matches \"**/foo{bar,baaz}\", \"foobuzz\"\n\n    # More complex extended matches\n    assert_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"foo.baaz.com/jquery.min.js\"\n    assert_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"moz.buzz.com/index.html\"\n    refute_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"moz.buzz.com/index.htm\"\n    refute_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"moz.bar.com/index.html\"\n    refute_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"flozz.buzz.com/index.html\"\n\n    # More complex extended matches and RegExp 'g' (regression)\n    assert_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"foo.baaz.com/jquery.min.js\"\n    assert_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"moz.buzz.com/index.html\"\n    refute_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"moz.buzz.com/index.htm\"\n    refute_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"moz.bar.com/index.html\"\n    refute_file_matches \"?o[oz].b*z.com/{*.js,*.html}\", \"flozz.buzz.com/index.html\"\n\n    # globstar\n    assert_file_matches \"some/**/{*.js,*.html}\", \"some/bar/jquery.min.js\"\n    assert_file_matches \"some/**/{*.js,*.html}\", \"some/bar/baz/jquery.min.js\"\n    assert_file_matches \"some/**\", \"some/bar/baz/jquery.min.js\"\n\n    assert_file_matches \"\\\\\\\\/$^+.()=!|,.*\", \"\\\\/$^+.()=!|,.*\"\n\n    # globstar specific tests\n    assert_file_matches \"/foo/*\", \"/foo/bar.txt\"\n    assert_file_matches \"/foo/**\", \"/foo/baz.txt\"\n    assert_file_matches \"/foo/**\", \"/foo/bar/baz.txt\"\n    assert_file_matches \"/foo/*/*.txt\", \"/foo/bar/baz.txt\"\n    assert_file_matches \"/foo/**/*.txt\", \"/foo/bar/baz.txt\"\n    assert_file_matches \"/foo/**/*.txt\", \"/foo/bar/baz/qux.txt\"\n    assert_file_matches \"/foo/**/bar.txt\", \"/foo/bar.txt\"\n    assert_file_matches \"/foo/**/**/bar.txt\", \"/foo/bar.txt\"\n    assert_file_matches \"/foo/**/*/baz.txt\", \"/foo/bar/baz.txt\"\n    assert_file_matches \"/foo/**/*.txt\", \"/foo/bar.txt\"\n    assert_file_matches \"/foo/**/**/*.txt\", \"/foo/bar.txt\"\n    assert_file_matches \"/foo/**/*/*.txt\", \"/foo/bar/baz.txt\"\n    assert_file_matches \"**/*.txt\", \"/foo/bar/baz/qux.txt\"\n    assert_file_matches \"**/foo.txt\", \"foo.txt\"\n    assert_file_matches \"**/*.txt\", \"foo.txt\"\n\n    refute_file_matches \"/foo/*\", \"/foo/bar/baz.txt\"\n    refute_file_matches \"/foo/*.txt\", \"/foo/bar/baz.txt\"\n    refute_file_matches \"/foo/*/*.txt\", \"/foo/bar/baz/qux.txt\"\n    refute_file_matches \"/foo/*/bar.txt\", \"/foo/bar.txt\"\n    refute_file_matches \"/foo/*/*/baz.txt\", \"/foo/bar/baz.txt\"\n    refute_file_matches \"/foo/**.txt\", \"/foo/bar/baz/qux.txt\"\n    refute_file_matches \"/foo/bar**/*.txt\", \"/foo/bar/baz/qux.txt\"\n    refute_file_matches \"/foo/bar**\", \"/foo/bar/baz.txt\"\n    refute_file_matches \"**/.txt\", \"/foo/bar/baz/qux.txt\"\n    refute_file_matches \"*/*.txt\", \"/foo/bar/baz/qux.txt\"\n    refute_file_matches \"*/*.txt\", \"foo.txt\"\n\n    refute_file_matches \"some/*\", \"some/bar/baz/jquery.min.js\"\n\n    refute_file_matches \"some/*\", \"some/bar/baz/jquery.min.js\"\n    assert_file_matches \"some/**\", \"some/bar/baz/jquery.min.js\"\n\n    assert_file_matches \"some/*/*/jquery.min.js\", \"some/bar/baz/jquery.min.js\"\n    assert_file_matches \"some/**/jquery.min.js\", \"some/bar/baz/jquery.min.js\"\n    assert_file_matches \"some/*/*/jquery.min.js\", \"some/bar/baz/jquery.min.js\"\n    refute_file_matches \"some/*/jquery.min.js\", \"some/bar/baz/jquery.min.js\"\n    refute_file_matches \"some/*/jquery.min.js\", \"some/bar/baz/jquery.min.js\"\n  end\n\n  it \"basic\" do\n    assert_file_matches \"abc\", \"abc\"\n    assert_file_matches \"*\", \"abc\"\n    assert_file_matches \"*\", \"\"\n    assert_file_matches \"**\", \"\"\n    assert_file_matches \"*c\", \"abc\"\n    refute_file_matches \"*b\", \"abc\"\n    assert_file_matches \"a*\", \"abc\"\n    refute_file_matches \"b*\", \"abc\"\n    assert_file_matches \"a*\", \"a\"\n    assert_file_matches \"*a\", \"a\"\n    assert_file_matches \"a*b*c*d*e*\", \"axbxcxdxe\"\n    assert_file_matches \"a*b*c*d*e*\", \"axbxcxdxexxx\"\n    assert_file_matches \"a*b?c*x\", \"abxbbxdbxebxczzx\"\n    refute_file_matches \"a*b?c*x\", \"abxbbxdbxebxczzy\"\n\n    assert_file_matches \"a/*/test\", \"a/foo/test\"\n    refute_file_matches \"a/*/test\", \"a/foo/bar/test\"\n    assert_file_matches \"a/**/test\", \"a/foo/test\"\n    assert_file_matches \"a/**/test\", \"a/foo/bar/test\"\n    assert_file_matches \"a/**/b/c\", \"a/foo/bar/b/c\"\n    assert_file_matches \"a\\\\*b\", \"a*b\"\n    refute_file_matches \"a\\\\*b\", \"axb\"\n\n    assert_file_matches \"[abc]\", \"a\"\n    assert_file_matches \"[abc]\", \"b\"\n    assert_file_matches \"[abc]\", \"c\"\n    refute_file_matches \"[abc]\", \"d\"\n    assert_file_matches \"x[abc]x\", \"xax\"\n    assert_file_matches \"x[abc]x\", \"xbx\"\n    assert_file_matches \"x[abc]x\", \"xcx\"\n    refute_file_matches \"x[abc]x\", \"xdx\"\n    refute_file_matches \"x[abc]x\", \"xay\"\n    assert_file_matches \"[?]\", \"?\"\n    refute_file_matches \"[?]\", \"a\"\n    assert_file_matches \"[*]\", \"*\"\n    refute_file_matches \"[*]\", \"a\"\n\n    assert_file_matches \"[a-cx]\", \"a\"\n    assert_file_matches \"[a-cx]\", \"b\"\n    assert_file_matches \"[a-cx]\", \"c\"\n    refute_file_matches \"[a-cx]\", \"d\"\n    assert_file_matches \"[a-cx]\", \"x\"\n\n    refute_file_matches \"[^abc]\", \"a\"\n    refute_file_matches \"[^abc]\", \"b\"\n    refute_file_matches \"[^abc]\", \"c\"\n    assert_file_matches \"[^abc]\", \"d\"\n    assert_file_matches \"[!abc]\", \"a\" # unsupported\n    assert_file_matches \"[!abc]\", \"b\" # unsupported\n    assert_file_matches \"[!abc]\", \"c\" # unsupported\n    refute_file_matches \"[!abc]\", \"d\" # unsupported\n    assert_file_matches \"[!abc]\", \"!\" # unsupported\n    assert_file_matches \"[\\\\!]\", \"!\"\n\n    assert_file_matches \"a*b*[cy]*d*e*\", \"axbxcxdxexxx\"\n    assert_file_matches \"a*b*[cy]*d*e*\", \"axbxyxdxexxx\"\n    assert_file_matches \"a*b*[cy]*d*e*\", \"axbxxxyxdxexxx\"\n\n    assert_file_matches \"test.{jpg,png}\", \"test.jpg\"\n    assert_file_matches \"test.{jpg,png}\", \"test.png\"\n    assert_file_matches \"test.{j*g,p*g}\", \"test.jpg\"\n    assert_file_matches \"test.{j*g,p*g}\", \"test.jpxxxg\"\n    assert_file_matches \"test.{j*g,p*g}\", \"test.jxg\"\n    refute_file_matches \"test.{j*g,p*g}\", \"test.jnt\"\n\n    assert_file_matches \"test.{j*g,j*c}\", \"test.jnc\"\n    assert_file_matches \"test.{jpg,p*g}\", \"test.png\"\n    assert_file_matches \"test.{jpg,p*g}\", \"test.pxg\"\n    refute_file_matches \"test.{jpg,p*g}\", \"test.pnt\"\n    assert_file_matches \"test.{jpeg,png}\", \"test.jpeg\"\n    refute_file_matches \"test.{jpeg,png}\", \"test.jpg\"\n    assert_file_matches \"test.{jpeg,png}\", \"test.png\"\n    assert_file_matches \"test.{jp\\\\,g,png}\", \"test.jp,g\"\n    refute_file_matches \"test.{jp\\\\,g,png}\", \"test.jxg\"\n    assert_file_matches \"test/{foo,bar}/baz\", \"test/foo/baz\"\n    assert_file_matches \"test/{foo,bar}/baz\", \"test/bar/baz\"\n    refute_file_matches \"test/{foo,bar}/baz\", \"test/baz/baz\"\n    assert_file_matches \"test/{foo*,bar*}/baz\", \"test/foooooo/baz\"\n    assert_file_matches \"test/{foo*,bar*}/baz\", \"test/barrrrr/baz\"\n    assert_file_matches \"test/{*foo,*bar}/baz\", \"test/xxxxfoo/baz\"\n    assert_file_matches \"test/{*foo,*bar}/baz\", \"test/xxxxbar/baz\"\n    assert_file_matches \"test/{foo/**,bar}/baz\", \"test/bar/baz\"\n    refute_file_matches \"test/{foo/**,bar}/baz\", \"test/bar/test/baz\"\n\n    refute_file_matches \"*.txt\", \"some/big/path/to/the/needle.txt\"\n    assert_file_matches(\n      \"some/**/needle.{js,tsx,mdx,ts,jsx,txt}\",\n      \"some/a/bigger/path/to/the/crazy/needle.txt\"\n    )\n    assert_file_matches(\n      \"some/**/{a,b,c}/**/needle.txt\",\n      \"some/foo/a/bigger/path/to/the/crazy/needle.txt\"\n    )\n    refute_file_matches(\n      \"some/**/{a,b,c}/**/needle.txt\",\n      \"some/foo/d/bigger/path/to/the/crazy/needle.txt\"\n    )\n\n    assert_file_matches \"a/{a{a,b},b}\", \"a/aa\"\n    assert_file_matches \"a/{a{a,b},b}\", \"a/ab\"\n    refute_file_matches \"a/{a{a,b},b}\", \"a/ac\"\n    assert_file_matches \"a/{a{a,b},b}\", \"a/b\"\n    refute_file_matches \"a/{a{a,b},b}\", \"a/c\"\n    assert_file_matches \"a/{b,c[}]*}\", \"a/b\"\n    assert_file_matches \"a/{b,c[}]*}\", \"a/c}xx\"\n\n    assert_file_matches \"/**/*a\", \"/a/a\"\n    assert_file_matches \"**/*.js\", \"a/b.c/c.js\"\n    assert_file_matches \"**/**/*.js\", \"a/b.c/c.js\"\n    assert_file_matches \"a/**/*.d\", \"a/b/c.d\"\n    assert_file_matches \"a/**/*.d\", \"a/.b/c.d\"\n\n    assert_file_matches \"**/*/**\", \"a/b/c\"\n    assert_file_matches \"**/*/c.js\", \"a/b/c.js\"\n  end\n\n  # The below tests are based on Bash and micromatch.\n  # https://github.com/micromatch/picomatch/blob/master/test/bash.js\n  # Converted using the following find and replace regex:\n  # find: assert\\(([!])?isMatch\\('(.*?)', ['\"](.*?)['\"]\\)\\);\n  # replace: assert!($1glob_match(\"$3\", \"$2\")\n\n  it \"bash\" do\n    refute_file_matches \"a*\", \"*\"\n    refute_file_matches \"a*\", \"**\"\n    refute_file_matches \"a*\", \"\\\\*\"\n    refute_file_matches \"a*\", \"a/*\"\n    refute_file_matches \"a*\", \"b\"\n    refute_file_matches \"a*\", \"bc\"\n    refute_file_matches \"a*\", \"bcd\"\n    refute_file_matches \"a*\", \"bdir/\"\n    refute_file_matches \"a*\", \"Beware\"\n    assert_file_matches \"a*\", \"a\"\n    assert_file_matches \"a*\", \"ab\"\n    assert_file_matches \"a*\", \"abc\"\n\n    refute_file_matches \"\\\\a*\", \"*\"\n    refute_file_matches \"\\\\a*\", \"**\"\n    refute_file_matches \"\\\\a*\", \"\\\\*\"\n\n    assert_file_matches \"\\\\a*\", \"a\"\n    refute_file_matches \"\\\\a*\", \"a/*\"\n    assert_file_matches \"\\\\a*\", \"abc\"\n    assert_file_matches \"\\\\a*\", \"abd\"\n    assert_file_matches \"\\\\a*\", \"abe\"\n    refute_file_matches \"\\\\a*\", \"b\"\n    refute_file_matches \"\\\\a*\", \"bb\"\n    refute_file_matches \"\\\\a*\", \"bcd\"\n    refute_file_matches \"\\\\a*\", \"bdir/\"\n    refute_file_matches \"\\\\a*\", \"Beware\"\n    refute_file_matches \"\\\\a*\", \"c\"\n    refute_file_matches \"\\\\a*\", \"ca\"\n    refute_file_matches \"\\\\a*\", \"cb\"\n    refute_file_matches \"\\\\a*\", \"d\"\n    refute_file_matches \"\\\\a*\", \"dd\"\n    refute_file_matches \"\\\\a*\", \"de\"\n  end\n\n  it \"bash_directories\" do\n    refute_file_matches \"b*/\", \"*\"\n    refute_file_matches \"b*/\", \"**\"\n    refute_file_matches \"b*/\", \"\\\\*\"\n    refute_file_matches \"b*/\", \"a\"\n    refute_file_matches \"b*/\", \"a/*\"\n    refute_file_matches \"b*/\", \"abc\"\n    refute_file_matches \"b*/\", \"abd\"\n    refute_file_matches \"b*/\", \"abe\"\n    refute_file_matches \"b*/\", \"b\"\n    refute_file_matches \"b*/\", \"bb\"\n    refute_file_matches \"b*/\", \"bcd\"\n    assert_file_matches \"b*/\", \"bdir/\"\n    refute_file_matches \"b*/\", \"Beware\"\n    refute_file_matches \"b*/\", \"c\"\n    refute_file_matches \"b*/\", \"ca\"\n    refute_file_matches \"b*/\", \"cb\"\n    refute_file_matches \"b*/\", \"d\"\n    refute_file_matches \"b*/\", \"dd\"\n    refute_file_matches \"b*/\", \"de\"\n  end\n\n  it \"bash_escaping\" do\n    refute_file_matches \"\\\\^\", \"*\"\n    refute_file_matches \"\\\\^\", \"**\"\n    refute_file_matches \"\\\\^\", \"\\\\*\"\n    refute_file_matches \"\\\\^\", \"a\"\n    refute_file_matches \"\\\\^\", \"a/*\"\n    refute_file_matches \"\\\\^\", \"abc\"\n    refute_file_matches \"\\\\^\", \"abd\"\n    refute_file_matches \"\\\\^\", \"abe\"\n    refute_file_matches \"\\\\^\", \"b\"\n    refute_file_matches \"\\\\^\", \"bb\"\n    refute_file_matches \"\\\\^\", \"bcd\"\n    refute_file_matches \"\\\\^\", \"bdir/\"\n    refute_file_matches \"\\\\^\", \"Beware\"\n    refute_file_matches \"\\\\^\", \"c\"\n    refute_file_matches \"\\\\^\", \"ca\"\n    refute_file_matches \"\\\\^\", \"cb\"\n    refute_file_matches \"\\\\^\", \"d\"\n    refute_file_matches \"\\\\^\", \"dd\"\n    refute_file_matches \"\\\\^\", \"de\"\n\n    assert_file_matches \"\\\\*\", \"*\"\n    # assert_file_matches \"\\\\*\", \"\\\\*\"\n    refute_file_matches \"\\\\*\", \"**\"\n    refute_file_matches \"\\\\*\", \"a\"\n    refute_file_matches \"\\\\*\", \"a/*\"\n    refute_file_matches \"\\\\*\", \"abc\"\n    refute_file_matches \"\\\\*\", \"abd\"\n    refute_file_matches \"\\\\*\", \"abe\"\n    refute_file_matches \"\\\\*\", \"b\"\n    refute_file_matches \"\\\\*\", \"bb\"\n    refute_file_matches \"\\\\*\", \"bcd\"\n    refute_file_matches \"\\\\*\", \"bdir/\"\n    refute_file_matches \"\\\\*\", \"Beware\"\n    refute_file_matches \"\\\\*\", \"c\"\n    refute_file_matches \"\\\\*\", \"ca\"\n    refute_file_matches \"\\\\*\", \"cb\"\n    refute_file_matches \"\\\\*\", \"d\"\n    refute_file_matches \"\\\\*\", \"dd\"\n    refute_file_matches \"\\\\*\", \"de\"\n\n    refute_file_matches \"a\\\\*\", \"*\"\n    refute_file_matches \"a\\\\*\", \"**\"\n    refute_file_matches \"a\\\\*\", \"\\\\*\"\n    refute_file_matches \"a\\\\*\", \"a\"\n    refute_file_matches \"a\\\\*\", \"a/*\"\n    refute_file_matches \"a\\\\*\", \"abc\"\n    refute_file_matches \"a\\\\*\", \"abd\"\n    refute_file_matches \"a\\\\*\", \"abe\"\n    refute_file_matches \"a\\\\*\", \"b\"\n    refute_file_matches \"a\\\\*\", \"bb\"\n    refute_file_matches \"a\\\\*\", \"bcd\"\n    refute_file_matches \"a\\\\*\", \"bdir/\"\n    refute_file_matches \"a\\\\*\", \"Beware\"\n    refute_file_matches \"a\\\\*\", \"c\"\n    refute_file_matches \"a\\\\*\", \"ca\"\n    refute_file_matches \"a\\\\*\", \"cb\"\n    refute_file_matches \"a\\\\*\", \"d\"\n    refute_file_matches \"a\\\\*\", \"dd\"\n    refute_file_matches \"a\\\\*\", \"de\"\n\n    assert_file_matches \"*q*\", \"aqa\"\n    assert_file_matches \"*q*\", \"aaqaa\"\n    refute_file_matches \"*q*\", \"*\"\n    refute_file_matches \"*q*\", \"**\"\n    refute_file_matches \"*q*\", \"\\\\*\"\n    refute_file_matches \"*q*\", \"a\"\n    refute_file_matches \"*q*\", \"a/*\"\n    refute_file_matches \"*q*\", \"abc\"\n    refute_file_matches \"*q*\", \"abd\"\n    refute_file_matches \"*q*\", \"abe\"\n    refute_file_matches \"*q*\", \"b\"\n    refute_file_matches \"*q*\", \"bb\"\n    refute_file_matches \"*q*\", \"bcd\"\n    refute_file_matches \"*q*\", \"bdir/\"\n    refute_file_matches \"*q*\", \"Beware\"\n    refute_file_matches \"*q*\", \"c\"\n    refute_file_matches \"*q*\", \"ca\"\n    refute_file_matches \"*q*\", \"cb\"\n    refute_file_matches \"*q*\", \"d\"\n    refute_file_matches \"*q*\", \"dd\"\n    refute_file_matches \"*q*\", \"de\"\n\n    assert_file_matches \"\\\\**\", \"*\"\n    assert_file_matches \"\\\\**\", \"**\"\n    refute_file_matches \"\\\\**\", \"\\\\*\"\n    refute_file_matches \"\\\\**\", \"a\"\n    refute_file_matches \"\\\\**\", \"a/*\"\n    refute_file_matches \"\\\\**\", \"abc\"\n    refute_file_matches \"\\\\**\", \"abd\"\n    refute_file_matches \"\\\\**\", \"abe\"\n    refute_file_matches \"\\\\**\", \"b\"\n    refute_file_matches \"\\\\**\", \"bb\"\n    refute_file_matches \"\\\\**\", \"bcd\"\n    refute_file_matches \"\\\\**\", \"bdir/\"\n    refute_file_matches \"\\\\**\", \"Beware\"\n    refute_file_matches \"\\\\**\", \"c\"\n    refute_file_matches \"\\\\**\", \"ca\"\n    refute_file_matches \"\\\\**\", \"cb\"\n    refute_file_matches \"\\\\**\", \"d\"\n    refute_file_matches \"\\\\**\", \"dd\"\n    refute_file_matches \"\\\\**\", \"de\"\n  end\n\n  it \"bash_classes\" do\n    refute_file_matches \"a*[^c]\", \"*\"\n    refute_file_matches \"a*[^c]\", \"**\"\n    refute_file_matches \"a*[^c]\", \"\\\\*\"\n    refute_file_matches \"a*[^c]\", \"a\"\n    refute_file_matches \"a*[^c]\", \"a/*\"\n    refute_file_matches \"a*[^c]\", \"abc\"\n    assert_file_matches \"a*[^c]\", \"abd\"\n    assert_file_matches \"a*[^c]\", \"abe\"\n    refute_file_matches \"a*[^c]\", \"b\"\n    refute_file_matches \"a*[^c]\", \"bb\"\n    refute_file_matches \"a*[^c]\", \"bcd\"\n    refute_file_matches \"a*[^c]\", \"bdir/\"\n    refute_file_matches \"a*[^c]\", \"Beware\"\n    refute_file_matches \"a*[^c]\", \"c\"\n    refute_file_matches \"a*[^c]\", \"ca\"\n    refute_file_matches \"a*[^c]\", \"cb\"\n    refute_file_matches \"a*[^c]\", \"d\"\n    refute_file_matches \"a*[^c]\", \"dd\"\n    refute_file_matches \"a*[^c]\", \"de\"\n    refute_file_matches \"a*[^c]\", \"baz\"\n    refute_file_matches \"a*[^c]\", \"bzz\"\n    refute_file_matches \"a*[^c]\", \"BZZ\"\n    refute_file_matches \"a*[^c]\", \"beware\"\n    refute_file_matches \"a*[^c]\", \"BewAre\"\n\n    assert_file_matches \"a[X-]b\", \"a-b\"\n    assert_file_matches \"a[X-]b\", \"aXb\"\n\n    refute_file_matches \"[a-y]*[^c]\", \"*\"\n    assert_file_matches \"[a-y]*[^c]\", \"a*\"\n    refute_file_matches \"[a-y]*[^c]\", \"**\"\n    refute_file_matches \"[a-y]*[^c]\", \"\\\\*\"\n    refute_file_matches \"[a-y]*[^c]\", \"a\"\n    assert_file_matches \"[a-y]*[^c]\", \"a123b\"\n    refute_file_matches \"[a-y]*[^c]\", \"a123c\"\n    assert_file_matches \"[a-y]*[^c]\", \"ab\"\n    refute_file_matches \"[a-y]*[^c]\", \"a/*\"\n    refute_file_matches \"[a-y]*[^c]\", \"abc\"\n    assert_file_matches \"[a-y]*[^c]\", \"abd\"\n    assert_file_matches \"[a-y]*[^c]\", \"abe\"\n    refute_file_matches \"[a-y]*[^c]\", \"b\"\n    assert_file_matches \"[a-y]*[^c]\", \"bd\"\n    assert_file_matches \"[a-y]*[^c]\", \"bb\"\n    assert_file_matches \"[a-y]*[^c]\", \"bcd\"\n    assert_file_matches \"[a-y]*[^c]\", \"bdir/\"\n    refute_file_matches \"[a-y]*[^c]\", \"Beware\"\n    refute_file_matches \"[a-y]*[^c]\", \"c\"\n    assert_file_matches \"[a-y]*[^c]\", \"ca\"\n    assert_file_matches \"[a-y]*[^c]\", \"cb\"\n    refute_file_matches \"[a-y]*[^c]\", \"d\"\n    assert_file_matches \"[a-y]*[^c]\", \"dd\"\n    assert_file_matches \"[a-y]*[^c]\", \"de\"\n    assert_file_matches \"[a-y]*[^c]\", \"baz\"\n    assert_file_matches \"[a-y]*[^c]\", \"bzz\"\n    # assert(!isMatch('bzz', '[a-y]*[^c]', { regex: true })\n    refute_file_matches \"[a-y]*[^c]\", \"BZZ\"\n    assert_file_matches \"[a-y]*[^c]\", \"beware\"\n    refute_file_matches \"[a-y]*[^c]\", \"BewAre\"\n\n    assert_file_matches \"a\\\\*b/*\", \"a*b/ooo\"\n    assert_file_matches \"a\\\\*?/*\", \"a*b/ooo\"\n\n    refute_file_matches \"a[b]c\", \"*\"\n    refute_file_matches \"a[b]c\", \"**\"\n    refute_file_matches \"a[b]c\", \"\\\\*\"\n    refute_file_matches \"a[b]c\", \"a\"\n    refute_file_matches \"a[b]c\", \"a/*\"\n    assert_file_matches \"a[b]c\", \"abc\"\n    refute_file_matches \"a[b]c\", \"abd\"\n    refute_file_matches \"a[b]c\", \"abe\"\n    refute_file_matches \"a[b]c\", \"b\"\n    refute_file_matches \"a[b]c\", \"bb\"\n    refute_file_matches \"a[b]c\", \"bcd\"\n    refute_file_matches \"a[b]c\", \"bdir/\"\n    refute_file_matches \"a[b]c\", \"Beware\"\n    refute_file_matches \"a[b]c\", \"c\"\n    refute_file_matches \"a[b]c\", \"ca\"\n    refute_file_matches \"a[b]c\", \"cb\"\n    refute_file_matches \"a[b]c\", \"d\"\n    refute_file_matches \"a[b]c\", \"dd\"\n    refute_file_matches \"a[b]c\", \"de\"\n    refute_file_matches \"a[b]c\", \"baz\"\n    refute_file_matches \"a[b]c\", \"bzz\"\n    refute_file_matches \"a[b]c\", \"BZZ\"\n    refute_file_matches \"a[b]c\", \"beware\"\n    refute_file_matches \"a[b]c\", \"BewAre\"\n\n    refute_file_matches \"a[\\\"b\\\"]c\", \"*\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"**\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"\\\\*\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"a\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"a/*\"\n    assert_file_matches \"a[\\\"b\\\"]c\", \"abc\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"abd\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"abe\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"b\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"bb\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"bcd\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"bdir/\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"Beware\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"c\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"ca\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"cb\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"d\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"dd\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"de\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"baz\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"bzz\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"BZZ\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"beware\"\n    refute_file_matches \"a[\\\"b\\\"]c\", \"BewAre\"\n\n    refute_file_matches \"a[\\\\\\\\b]c\", \"*\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"**\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"\\\\*\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"a\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"a/*\"\n    assert_file_matches \"a[\\\\\\\\b]c\", \"abc\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"abd\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"abe\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"b\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"bb\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"bcd\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"bdir/\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"Beware\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"c\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"ca\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"cb\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"d\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"dd\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"de\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"baz\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"bzz\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"BZZ\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"beware\"\n    refute_file_matches \"a[\\\\\\\\b]c\", \"BewAre\"\n\n    refute_file_matches \"a[\\\\b]c\", \"*\"\n    refute_file_matches \"a[\\\\b]c\", \"**\"\n    refute_file_matches \"a[\\\\b]c\", \"\\\\*\"\n    refute_file_matches \"a[\\\\b]c\", \"a\"\n    refute_file_matches \"a[\\\\b]c\", \"a/*\"\n    refute_file_matches \"a[\\\\b]c\", \"abc\"\n    refute_file_matches \"a[\\\\b]c\", \"abd\"\n    refute_file_matches \"a[\\\\b]c\", \"abe\"\n    refute_file_matches \"a[\\\\b]c\", \"b\"\n    refute_file_matches \"a[\\\\b]c\", \"bb\"\n    refute_file_matches \"a[\\\\b]c\", \"bcd\"\n    refute_file_matches \"a[\\\\b]c\", \"bdir/\"\n    refute_file_matches \"a[\\\\b]c\", \"Beware\"\n    refute_file_matches \"a[\\\\b]c\", \"c\"\n    refute_file_matches \"a[\\\\b]c\", \"ca\"\n    refute_file_matches \"a[\\\\b]c\", \"cb\"\n    refute_file_matches \"a[\\\\b]c\", \"d\"\n    refute_file_matches \"a[\\\\b]c\", \"dd\"\n    refute_file_matches \"a[\\\\b]c\", \"de\"\n    refute_file_matches \"a[\\\\b]c\", \"baz\"\n    refute_file_matches \"a[\\\\b]c\", \"bzz\"\n    refute_file_matches \"a[\\\\b]c\", \"BZZ\"\n    refute_file_matches \"a[\\\\b]c\", \"beware\"\n    refute_file_matches \"a[\\\\b]c\", \"BewAre\"\n\n    refute_file_matches \"a[b-d]c\", \"*\"\n    refute_file_matches \"a[b-d]c\", \"**\"\n    refute_file_matches \"a[b-d]c\", \"\\\\*\"\n    refute_file_matches \"a[b-d]c\", \"a\"\n    refute_file_matches \"a[b-d]c\", \"a/*\"\n    assert_file_matches \"a[b-d]c\", \"abc\"\n    refute_file_matches \"a[b-d]c\", \"abd\"\n    refute_file_matches \"a[b-d]c\", \"abe\"\n    refute_file_matches \"a[b-d]c\", \"b\"\n    refute_file_matches \"a[b-d]c\", \"bb\"\n    refute_file_matches \"a[b-d]c\", \"bcd\"\n    refute_file_matches \"a[b-d]c\", \"bdir/\"\n    refute_file_matches \"a[b-d]c\", \"Beware\"\n    refute_file_matches \"a[b-d]c\", \"c\"\n    refute_file_matches \"a[b-d]c\", \"ca\"\n    refute_file_matches \"a[b-d]c\", \"cb\"\n    refute_file_matches \"a[b-d]c\", \"d\"\n    refute_file_matches \"a[b-d]c\", \"dd\"\n    refute_file_matches \"a[b-d]c\", \"de\"\n    refute_file_matches \"a[b-d]c\", \"baz\"\n    refute_file_matches \"a[b-d]c\", \"bzz\"\n    refute_file_matches \"a[b-d]c\", \"BZZ\"\n    refute_file_matches \"a[b-d]c\", \"beware\"\n    refute_file_matches \"a[b-d]c\", \"BewAre\"\n\n    refute_file_matches \"a?c\", \"*\"\n    refute_file_matches \"a?c\", \"**\"\n    refute_file_matches \"a?c\", \"\\\\*\"\n    refute_file_matches \"a?c\", \"a\"\n    refute_file_matches \"a?c\", \"a/*\"\n    assert_file_matches \"a?c\", \"abc\"\n    refute_file_matches \"a?c\", \"abd\"\n    refute_file_matches \"a?c\", \"abe\"\n    refute_file_matches \"a?c\", \"b\"\n    refute_file_matches \"a?c\", \"bb\"\n    refute_file_matches \"a?c\", \"bcd\"\n    refute_file_matches \"a?c\", \"bdir/\"\n    refute_file_matches \"a?c\", \"Beware\"\n    refute_file_matches \"a?c\", \"c\"\n    refute_file_matches \"a?c\", \"ca\"\n    refute_file_matches \"a?c\", \"cb\"\n    refute_file_matches \"a?c\", \"d\"\n    refute_file_matches \"a?c\", \"dd\"\n    refute_file_matches \"a?c\", \"de\"\n    refute_file_matches \"a?c\", \"baz\"\n    refute_file_matches \"a?c\", \"bzz\"\n    refute_file_matches \"a?c\", \"BZZ\"\n    refute_file_matches \"a?c\", \"beware\"\n    refute_file_matches \"a?c\", \"BewAre\"\n\n    assert_file_matches \"*/man*/bash.*\", \"man/man1/bash.1\"\n\n    assert_file_matches \"[^a-c]*\", \"*\"\n    assert_file_matches \"[^a-c]*\", \"**\"\n    refute_file_matches \"[^a-c]*\", \"a\"\n    refute_file_matches \"[^a-c]*\", \"a/*\"\n    refute_file_matches \"[^a-c]*\", \"abc\"\n    refute_file_matches \"[^a-c]*\", \"abd\"\n    refute_file_matches \"[^a-c]*\", \"abe\"\n    refute_file_matches \"[^a-c]*\", \"b\"\n    refute_file_matches \"[^a-c]*\", \"bb\"\n    refute_file_matches \"[^a-c]*\", \"bcd\"\n    refute_file_matches \"[^a-c]*\", \"bdir/\"\n    assert_file_matches \"[^a-c]*\", \"Beware\"\n    refute_file_matches \"[^a-c]*\", \"c\"\n    refute_file_matches \"[^a-c]*\", \"ca\"\n    refute_file_matches \"[^a-c]*\", \"cb\"\n    assert_file_matches \"[^a-c]*\", \"d\"\n    assert_file_matches \"[^a-c]*\", \"dd\"\n    assert_file_matches \"[^a-c]*\", \"de\"\n    refute_file_matches \"[^a-c]*\", \"baz\"\n    refute_file_matches \"[^a-c]*\", \"bzz\"\n    assert_file_matches \"[^a-c]*\", \"BZZ\"\n    refute_file_matches \"[^a-c]*\", \"beware\"\n    assert_file_matches \"[^a-c]*\", \"BewAre\"\n  end\n\n  it \"bash_wildmatch\" do\n    refute_file_matches \"a[]-]b\", \"aab\"\n    refute_file_matches \"[ten]\", \"ten\"\n    assert_file_matches \"]\", \"]\"\n    assert_file_matches \"a[]-]b\", \"a-b\"\n    assert_file_matches \"a[]-]b\", \"a]b\"\n    assert_file_matches \"a[]]b\", \"a]b\"\n    assert_file_matches \"a[\\\\]a\\\\-]b\", \"aab\"\n    assert_file_matches \"t[a-g]n\", \"ten\"\n    assert_file_matches \"t[^a-g]n\", \"ton\"\n  end\n\n  it \"bash_slashmatch\" do\n    # refute_file_matches \"f[^eiu][^eiu][^eiu][^eiu][^eiu]r\", \"foo/bar\"\n    assert_file_matches \"foo[/]bar\", \"foo/bar\"\n    assert_file_matches \"f[^eiu][^eiu][^eiu][^eiu][^eiu]r\", \"foo-bar\"\n  end\n\n  it \"bash_extra_stars\" do\n    refute_file_matches \"a**c\", \"bbc\"\n    assert_file_matches \"a**c\", \"abc\"\n    refute_file_matches \"a**c\", \"bbd\"\n\n    refute_file_matches \"a***c\", \"bbc\"\n    assert_file_matches \"a***c\", \"abc\"\n    refute_file_matches \"a***c\", \"bbd\"\n\n    refute_file_matches \"a*****?c\", \"bbc\"\n    assert_file_matches \"a*****?c\", \"abc\"\n    refute_file_matches \"a*****?c\", \"bbc\"\n\n    assert_file_matches \"?*****??\", \"bbc\"\n    assert_file_matches \"?*****??\", \"abc\"\n\n    assert_file_matches \"*****??\", \"bbc\"\n    assert_file_matches \"*****??\", \"abc\"\n\n    assert_file_matches \"?*****?c\", \"bbc\"\n    assert_file_matches \"?*****?c\", \"abc\"\n\n    assert_file_matches \"?***?****c\", \"bbc\"\n    assert_file_matches \"?***?****c\", \"abc\"\n    refute_file_matches \"?***?****c\", \"bbd\"\n\n    assert_file_matches \"?***?****?\", \"bbc\"\n    assert_file_matches \"?***?****?\", \"abc\"\n\n    assert_file_matches \"?***?****\", \"bbc\"\n    assert_file_matches \"?***?****\", \"abc\"\n\n    assert_file_matches \"*******c\", \"bbc\"\n    assert_file_matches \"*******c\", \"abc\"\n\n    assert_file_matches \"*******?\", \"bbc\"\n    assert_file_matches \"*******?\", \"abc\"\n\n    assert_file_matches \"a*cd**?**??k\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??k\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??k***\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??***k\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??***k**\", \"abcdecdhjk\"\n    assert_file_matches \"a****c**?**??*****\", \"abcdecdhjk\"\n  end\n\n  it \"stars\" do\n    refute_file_matches \"*.js\", \"a/b/c/z.js\"\n    refute_file_matches \"*.js\", \"a/b/z.js\"\n    refute_file_matches \"*.js\", \"a/z.js\"\n    assert_file_matches \"*.js\", \"z.js\"\n\n    # refute_file_matches \"*/*\", \"a/.ab\"\n    # refute_file_matches \"*\", \".ab\"\n\n    assert_file_matches \"z*.js\", \"z.js\"\n    assert_file_matches \"*/*\", \"a/z\"\n    assert_file_matches \"*/z*.js\", \"a/z.js\"\n    assert_file_matches \"a/z*.js\", \"a/z.js\"\n\n    assert_file_matches \"*\", \"ab\"\n    assert_file_matches \"*\", \"abc\"\n\n    refute_file_matches \"f*\", \"bar\"\n    refute_file_matches \"*r\", \"foo\"\n    refute_file_matches \"b*\", \"foo\"\n    refute_file_matches \"*\", \"foo/bar\"\n    assert_file_matches \"*c\", \"abc\"\n    assert_file_matches \"a*\", \"abc\"\n    assert_file_matches \"a*c\", \"abc\"\n    assert_file_matches \"*r\", \"bar\"\n    assert_file_matches \"b*\", \"bar\"\n    assert_file_matches \"f*\", \"foo\"\n\n    assert_file_matches \"*abc*\", \"one abc two\"\n    assert_file_matches \"a*b\", \"a         b\"\n\n    refute_file_matches \"*a*\", \"foo\"\n    assert_file_matches \"*a*\", \"bar\"\n    assert_file_matches \"*abc*\", \"oneabctwo\"\n    refute_file_matches \"*-bc-*\", \"a-b.c-d\"\n    assert_file_matches \"*-*.*-*\", \"a-b.c-d\"\n    assert_file_matches \"*-b*c-*\", \"a-b.c-d\"\n    assert_file_matches \"*-b.c-*\", \"a-b.c-d\"\n    assert_file_matches \"*.*\", \"a-b.c-d\"\n    assert_file_matches \"*.*-*\", \"a-b.c-d\"\n    assert_file_matches \"*.*-d\", \"a-b.c-d\"\n    assert_file_matches \"*.c-*\", \"a-b.c-d\"\n    assert_file_matches \"*b.*d\", \"a-b.c-d\"\n    assert_file_matches \"a*.c*\", \"a-b.c-d\"\n    assert_file_matches \"a-*.*-d\", \"a-b.c-d\"\n    assert_file_matches \"*.*\", \"a.b\"\n    assert_file_matches \"*.b\", \"a.b\"\n    assert_file_matches \"a.*\", \"a.b\"\n    assert_file_matches \"a.b\", \"a.b\"\n\n    refute_file_matches \"**-bc-**\", \"a-b.c-d\"\n    assert_file_matches \"**-**.**-**\", \"a-b.c-d\"\n    assert_file_matches \"**-b**c-**\", \"a-b.c-d\"\n    assert_file_matches \"**-b.c-**\", \"a-b.c-d\"\n    assert_file_matches \"**.**\", \"a-b.c-d\"\n    assert_file_matches \"**.**-**\", \"a-b.c-d\"\n    assert_file_matches \"**.**-d\", \"a-b.c-d\"\n    assert_file_matches \"**.c-**\", \"a-b.c-d\"\n    assert_file_matches \"**b.**d\", \"a-b.c-d\"\n    assert_file_matches \"a**.c**\", \"a-b.c-d\"\n    assert_file_matches \"a-**.**-d\", \"a-b.c-d\"\n    assert_file_matches \"**.**\", \"a.b\"\n    assert_file_matches \"**.b\", \"a.b\"\n    assert_file_matches \"a.**\", \"a.b\"\n    assert_file_matches \"a.b\", \"a.b\"\n\n    assert_file_matches \"*/*\", \"/ab\"\n    assert_file_matches \".\", \".\"\n    refute_file_matches \"a/\", \"a/.b\"\n    assert_file_matches \"/*\", \"/ab\"\n    assert_file_matches \"/??\", \"/ab\"\n    assert_file_matches \"/?b\", \"/ab\"\n    assert_file_matches \"/*\", \"/cd\"\n    assert_file_matches \"a\", \"a\"\n    assert_file_matches \"a/.*\", \"a/.b\"\n    assert_file_matches \"?/?\", \"a/b\"\n    assert_file_matches \"a/**/j/**/z/*.md\", \"a/b/c/d/e/j/n/p/o/z/c.md\"\n    assert_file_matches \"a/**/z/*.md\", \"a/b/c/d/e/z/c.md\"\n    assert_file_matches \"a/b/c/*.md\", \"a/b/c/xyz.md\"\n    assert_file_matches \"a/*/z/.a\", \"a/b/z/.a\"\n    refute_file_matches \"bz\", \"a/b/z/.a\"\n    assert_file_matches \"a/**/c/*.md\", \"a/bb.bb/aa/b.b/aa/c/xyz.md\"\n    assert_file_matches \"a/**/c/*.md\", \"a/bb.bb/aa/bb/aa/c/xyz.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bb.bb/c/xyz.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bb/c/xyz.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bbbb/c/xyz.md\"\n    assert_file_matches \"*\", \"aaa\"\n    assert_file_matches \"*\", \"ab\"\n    assert_file_matches \"ab\", \"ab\"\n\n    refute_file_matches \"*/*/*\", \"aaa\"\n    refute_file_matches \"*/*/*\", \"aaa/bb/aa/rr\"\n    refute_file_matches \"aaa*\", \"aaa/bba/ccc\"\n    # refute_file_matches \"aaa**\", \"aaa/bba/ccc\"\n    refute_file_matches \"aaa/*\", \"aaa/bba/ccc\"\n    refute_file_matches \"aaa/*ccc\", \"aaa/bba/ccc\"\n    refute_file_matches \"aaa/*z\", \"aaa/bba/ccc\"\n    refute_file_matches \"*/*/*\", \"aaa/bbb\"\n    refute_file_matches \"*/*jk*/*i\", \"ab/zzz/ejkl/hi\"\n    assert_file_matches \"*/*/*\", \"aaa/bba/ccc\"\n    assert_file_matches \"aaa/**\", \"aaa/bba/ccc\"\n    assert_file_matches \"aaa/*\", \"aaa/bbb\"\n    assert_file_matches \"*/*z*/*/*i\", \"ab/zzz/ejkl/hi\"\n    assert_file_matches \"*j*i\", \"abzzzejklhi\"\n\n    assert_file_matches \"*\", \"a\"\n    assert_file_matches \"*\", \"b\"\n    refute_file_matches \"*\", \"a/a\"\n    refute_file_matches \"*\", \"a/a/a\"\n    refute_file_matches \"*\", \"a/a/b\"\n    refute_file_matches \"*\", \"a/a/a/a\"\n    refute_file_matches \"*\", \"a/a/a/a/a\"\n\n    refute_file_matches \"*/*\", \"a\"\n    assert_file_matches \"*/*\", \"a/a\"\n    refute_file_matches \"*/*\", \"a/a/a\"\n\n    refute_file_matches \"*/*/*\", \"a\"\n    refute_file_matches \"*/*/*\", \"a/a\"\n    assert_file_matches \"*/*/*\", \"a/a/a\"\n    refute_file_matches \"*/*/*\", \"a/a/a/a\"\n\n    refute_file_matches \"*/*/*/*\", \"a\"\n    refute_file_matches \"*/*/*/*\", \"a/a\"\n    refute_file_matches \"*/*/*/*\", \"a/a/a\"\n    assert_file_matches \"*/*/*/*\", \"a/a/a/a\"\n    refute_file_matches \"*/*/*/*\", \"a/a/a/a/a\"\n\n    refute_file_matches \"*/*/*/*/*\", \"a\"\n    refute_file_matches \"*/*/*/*/*\", \"a/a\"\n    refute_file_matches \"*/*/*/*/*\", \"a/a/a\"\n    refute_file_matches \"*/*/*/*/*\", \"a/a/b\"\n    refute_file_matches \"*/*/*/*/*\", \"a/a/a/a\"\n    assert_file_matches \"*/*/*/*/*\", \"a/a/a/a/a\"\n    refute_file_matches \"*/*/*/*/*\", \"a/a/a/a/a/a\"\n\n    refute_file_matches \"a/*\", \"a\"\n    assert_file_matches \"a/*\", \"a/a\"\n    refute_file_matches \"a/*\", \"a/a/a\"\n    refute_file_matches \"a/*\", \"a/a/a/a\"\n    refute_file_matches \"a/*\", \"a/a/a/a/a\"\n\n    refute_file_matches \"a/*/*\", \"a\"\n    refute_file_matches \"a/*/*\", \"a/a\"\n    assert_file_matches \"a/*/*\", \"a/a/a\"\n    refute_file_matches \"a/*/*\", \"b/a/a\"\n    refute_file_matches \"a/*/*\", \"a/a/a/a\"\n    refute_file_matches \"a/*/*\", \"a/a/a/a/a\"\n\n    refute_file_matches \"a/*/*/*\", \"a\"\n    refute_file_matches \"a/*/*/*\", \"a/a\"\n    refute_file_matches \"a/*/*/*\", \"a/a/a\"\n    assert_file_matches \"a/*/*/*\", \"a/a/a/a\"\n    refute_file_matches \"a/*/*/*\", \"a/a/a/a/a\"\n\n    refute_file_matches \"a/*/*/*/*\", \"a\"\n    refute_file_matches \"a/*/*/*/*\", \"a/a\"\n    refute_file_matches \"a/*/*/*/*\", \"a/a/a\"\n    refute_file_matches \"a/*/*/*/*\", \"a/a/b\"\n    refute_file_matches \"a/*/*/*/*\", \"a/a/a/a\"\n    assert_file_matches \"a/*/*/*/*\", \"a/a/a/a/a\"\n\n    refute_file_matches \"a/*/a\", \"a\"\n    refute_file_matches \"a/*/a\", \"a/a\"\n    assert_file_matches \"a/*/a\", \"a/a/a\"\n    refute_file_matches \"a/*/a\", \"a/a/b\"\n    refute_file_matches \"a/*/a\", \"a/a/a/a\"\n    refute_file_matches \"a/*/a\", \"a/a/a/a/a\"\n\n    refute_file_matches \"a/*/b\", \"a\"\n    refute_file_matches \"a/*/b\", \"a/a\"\n    refute_file_matches \"a/*/b\", \"a/a/a\"\n    assert_file_matches \"a/*/b\", \"a/a/b\"\n    refute_file_matches \"a/*/b\", \"a/a/a/a\"\n    refute_file_matches \"a/*/b\", \"a/a/a/a/a\"\n\n    refute_file_matches \"*/**/a\", \"a\"\n    refute_file_matches \"*/**/a\", \"a/a/b\"\n    assert_file_matches \"*/**/a\", \"a/a\"\n    assert_file_matches \"*/**/a\", \"a/a/a\"\n    assert_file_matches \"*/**/a\", \"a/a/a/a\"\n    assert_file_matches \"*/**/a\", \"a/a/a/a/a\"\n\n    refute_file_matches \"*/\", \"a\"\n    refute_file_matches \"*/*\", \"a\"\n    refute_file_matches \"a/*\", \"a\"\n    # refute_file_matches \"*/*\", \"a/\"\n    # refute_file_matches \"a/*\", \"a/\"\n    refute_file_matches \"*\", \"a/a\"\n    refute_file_matches \"*/\", \"a/a\"\n    refute_file_matches \"*/\", \"a/x/y\"\n    refute_file_matches \"*/*\", \"a/x/y\"\n    refute_file_matches \"a/*\", \"a/x/y\"\n    # assert_file_matches \"*\", \"a/\"\n    assert_file_matches \"*\", \"a\"\n    assert_file_matches \"*/\", \"a/\"\n    assert_file_matches \"*{,/}\", \"a/\"\n    assert_file_matches \"*/*\", \"a/a\"\n    assert_file_matches \"a/*\", \"a/a\"\n\n    refute_file_matches \"a/**/*.txt\", \"a.txt\"\n    assert_file_matches \"a/**/*.txt\", \"a/x/y.txt\"\n    refute_file_matches \"a/**/*.txt\", \"a/x/y/z\"\n\n    refute_file_matches \"a/*.txt\", \"a.txt\"\n    assert_file_matches \"a/*.txt\", \"a/b.txt\"\n    refute_file_matches \"a/*.txt\", \"a/x/y.txt\"\n    refute_file_matches \"a/*.txt\", \"a/x/y/z\"\n\n    assert_file_matches \"a*.txt\", \"a.txt\"\n    refute_file_matches \"a*.txt\", \"a/b.txt\"\n    refute_file_matches \"a*.txt\", \"a/x/y.txt\"\n    refute_file_matches \"a*.txt\", \"a/x/y/z\"\n\n    assert_file_matches \"*.txt\", \"a.txt\"\n    refute_file_matches \"*.txt\", \"a/b.txt\"\n    refute_file_matches \"*.txt\", \"a/x/y.txt\"\n    refute_file_matches \"*.txt\", \"a/x/y/z\"\n\n    refute_file_matches \"a*\", \"a/b\"\n    refute_file_matches \"a/**/b\", \"a/a/bb\"\n    refute_file_matches \"a/**/b\", \"a/bb\"\n\n    refute_file_matches \"*/**\", \"foo\"\n    refute_file_matches \"**/\", \"foo/bar\"\n    refute_file_matches \"**/*/\", \"foo/bar\"\n    refute_file_matches \"*/*/\", \"foo/bar\"\n\n    assert_file_matches \"**/..\", \"/home/foo/..\"\n    assert_file_matches \"**/a\", \"a\"\n    assert_file_matches \"**\", \"a/a\"\n    assert_file_matches \"a/**\", \"a/a\"\n    assert_file_matches \"a/**\", \"a/\"\n    # assert_file_matches \"a/**\", \"a\"\n    refute_file_matches \"**/\", \"a/a\"\n    # assert_file_matches \"**/a/**\", \"a\"\n    # assert_file_matches \"a/**\", \"a\"\n    refute_file_matches \"**/\", \"a/a\"\n    assert_file_matches \"*/**/a\", \"a/a\"\n    # assert_file_matches \"a/**\", \"a\"\n    assert_file_matches \"*/**\", \"foo/\"\n    assert_file_matches \"**/*\", \"foo/bar\"\n    assert_file_matches \"*/*\", \"foo/bar\"\n    assert_file_matches \"*/**\", \"foo/bar\"\n    assert_file_matches \"**/\", \"foo/bar/\"\n    # assert_file_matches \"**/*\", \"foo/bar/\"\n    assert_file_matches \"**/*/\", \"foo/bar/\"\n    assert_file_matches \"*/**\", \"foo/bar/\"\n    assert_file_matches \"*/*/\", \"foo/bar/\"\n\n    refute_file_matches \"*/foo\", \"bar/baz/foo\"\n    refute_file_matches \"**/bar/*\", \"deep/foo/bar\"\n    refute_file_matches \"*/bar/**\", \"deep/foo/bar/baz/x\"\n    refute_file_matches \"/*\", \"ef\"\n    refute_file_matches \"foo?bar\", \"foo/bar\"\n    refute_file_matches \"**/bar*\", \"foo/bar/baz\"\n    # refute_file_matches \"**/bar**\", \"foo/bar/baz\"\n    refute_file_matches \"foo**bar\", \"foo/baz/bar\"\n    refute_file_matches \"foo*bar\", \"foo/baz/bar\"\n    # assert_file_matches \"foo/**\", \"foo\"\n    assert_file_matches \"/*\", \"/ab\"\n    assert_file_matches \"/*\", \"/cd\"\n    assert_file_matches \"/*\", \"/ef\"\n    assert_file_matches \"a/**/j/**/z/*.md\", \"a/b/j/c/z/x.md\"\n    assert_file_matches \"a/**/j/**/z/*.md\", \"a/j/z/x.md\"\n\n    assert_file_matches \"**/foo\", \"bar/baz/foo\"\n    assert_file_matches \"**/bar/*\", \"deep/foo/bar/baz\"\n    assert_file_matches \"**/bar/**\", \"deep/foo/bar/baz/\"\n    assert_file_matches \"**/bar/*/*\", \"deep/foo/bar/baz/x\"\n    assert_file_matches \"foo/**/**/bar\", \"foo/b/a/z/bar\"\n    assert_file_matches \"foo/**/bar\", \"foo/b/a/z/bar\"\n    assert_file_matches \"foo/**/**/bar\", \"foo/bar\"\n    assert_file_matches \"foo/**/bar\", \"foo/bar\"\n    assert_file_matches \"*/bar/**\", \"foo/bar/baz/x\"\n    assert_file_matches \"foo/**/**/bar\", \"foo/baz/bar\"\n    assert_file_matches \"foo/**/bar\", \"foo/baz/bar\"\n    assert_file_matches \"**/foo\", \"XXX/foo\"\n  end\n\n  it \"globstars\" do\n    assert_file_matches \"**/*.js\", \"a/b/c/d.js\"\n    assert_file_matches \"**/*.js\", \"a/b/c.js\"\n    assert_file_matches \"**/*.js\", \"a/b.js\"\n    assert_file_matches \"a/b/**/*.js\", \"a/b/c/d/e/f.js\"\n    assert_file_matches \"a/b/**/*.js\", \"a/b/c/d/e.js\"\n    assert_file_matches \"a/b/c/**/*.js\", \"a/b/c/d.js\"\n    assert_file_matches \"a/b/**/*.js\", \"a/b/c/d.js\"\n    assert_file_matches \"a/b/**/*.js\", \"a/b/d.js\"\n    refute_file_matches \"a/b/**/*.js\", \"a/d.js\"\n    refute_file_matches \"a/b/**/*.js\", \"d.js\"\n\n    refute_file_matches \"**c\", \"a/b/c\"\n    refute_file_matches \"a/**c\", \"a/b/c\"\n    refute_file_matches \"a/**z\", \"a/b/c\"\n    refute_file_matches \"a/**b**/c\", \"a/b/c/b/c\"\n    refute_file_matches \"a/b/c**/*.js\", \"a/b/c/d/e.js\"\n    assert_file_matches \"a/**/b/**/c\", \"a/b/c/b/c\"\n    assert_file_matches \"a/**b**/c\", \"a/aba/c\"\n    assert_file_matches \"a/**b**/c\", \"a/b/c\"\n    assert_file_matches \"a/b/c**/*.js\", \"a/b/c/d.js\"\n\n    refute_file_matches \"a/**/*\", \"a\"\n    refute_file_matches \"a/**/**/*\", \"a\"\n    refute_file_matches \"a/**/**/**/*\", \"a\"\n    refute_file_matches \"**/a\", \"a/\"\n    assert_file_matches \"a/**/*\", \"a/\"\n    assert_file_matches \"a/**/**/*\", \"a/\"\n    assert_file_matches \"a/**/**/**/*\", \"a/\"\n    refute_file_matches \"**/a\", \"a/b\"\n    refute_file_matches \"a/**/j/**/z/*.md\", \"a/b/c/j/e/z/c.txt\"\n    refute_file_matches \"a/**/b\", \"a/bb\"\n    refute_file_matches \"**/a\", \"a/c\"\n    refute_file_matches \"**/a\", \"a/b\"\n    refute_file_matches \"**/a\", \"a/x/y\"\n    refute_file_matches \"**/a\", \"a/b/c/d\"\n    assert_file_matches \"**\", \"a\"\n    assert_file_matches \"**/a\", \"a\"\n    # assert_file_matches \"a/**\", \"a\"\n    assert_file_matches \"**\", \"a/\"\n    assert_file_matches \"**/a/**\", \"a/\"\n    assert_file_matches \"a/**\", \"a/\"\n    assert_file_matches \"a/**/**\", \"a/\"\n    assert_file_matches \"**/a\", \"a/a\"\n    assert_file_matches \"**\", \"a/b\"\n    assert_file_matches \"*/*\", \"a/b\"\n    assert_file_matches \"a/**\", \"a/b\"\n    assert_file_matches \"a/**/*\", \"a/b\"\n    assert_file_matches \"a/**/**/*\", \"a/b\"\n    assert_file_matches \"a/**/**/**/*\", \"a/b\"\n    assert_file_matches \"a/**/b\", \"a/b\"\n    assert_file_matches \"**\", \"a/b/c\"\n    assert_file_matches \"**/*\", \"a/b/c\"\n    assert_file_matches \"**/**\", \"a/b/c\"\n    assert_file_matches \"*/**\", \"a/b/c\"\n    assert_file_matches \"a/**\", \"a/b/c\"\n    assert_file_matches \"a/**/*\", \"a/b/c\"\n    assert_file_matches \"a/**/**/*\", \"a/b/c\"\n    assert_file_matches \"a/**/**/**/*\", \"a/b/c\"\n    assert_file_matches \"**\", \"a/b/c/d\"\n    assert_file_matches \"a/**\", \"a/b/c/d\"\n    assert_file_matches \"a/**/*\", \"a/b/c/d\"\n    assert_file_matches \"a/**/**/*\", \"a/b/c/d\"\n    assert_file_matches \"a/**/**/**/*\", \"a/b/c/d\"\n    assert_file_matches \"a/b/**/c/**/*.*\", \"a/b/c/d.e\"\n    assert_file_matches \"a/**/f/*.md\", \"a/b/c/d/e/f/g.md\"\n    assert_file_matches \"a/**/f/**/k/*.md\", \"a/b/c/d/e/f/g/h/i/j/k/l.md\"\n    assert_file_matches \"a/b/c/*.md\", \"a/b/c/def.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bb.bb/c/ddd.md\"\n    assert_file_matches \"a/**/f/*.md\", \"a/bb.bb/cc/d.d/ee/f/ggg.md\"\n    assert_file_matches \"a/**/f/*.md\", \"a/bb.bb/cc/dd/ee/f/ggg.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bb/c/ddd.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bbbb/c/ddd.md\"\n\n    assert_file_matches \"foo/bar/**/one/**/*.*\", \"foo/bar/baz/one/image.png\"\n    assert_file_matches \"foo/bar/**/one/**/*.*\", \"foo/bar/baz/one/two/image.png\"\n    assert_file_matches \"foo/bar/**/one/**/*.*\", \"foo/bar/baz/one/two/three/image.png\"\n    refute_file_matches \"a/b/**/f\", \"a/b/c/d/\"\n    # assert_file_matches \"a/**\", \"a\"\n    assert_file_matches \"**\", \"a\"\n    # assert_file_matches \"a{,/**}\", \"a\"\n    assert_file_matches \"**\", \"a/\"\n    assert_file_matches \"a/**\", \"a/\"\n    assert_file_matches \"**\", \"a/b/c/d\"\n    assert_file_matches \"**\", \"a/b/c/d/\"\n    assert_file_matches \"**/**\", \"a/b/c/d/\"\n    assert_file_matches \"**/b/**\", \"a/b/c/d/\"\n    assert_file_matches \"a/b/**\", \"a/b/c/d/\"\n    assert_file_matches \"a/b/**/\", \"a/b/c/d/\"\n    assert_file_matches \"a/b/**/c/**/\", \"a/b/c/d/\"\n    assert_file_matches \"a/b/**/c/**/d/\", \"a/b/c/d/\"\n    assert_file_matches \"a/b/**/**/*.*\", \"a/b/c/d/e.f\"\n    assert_file_matches \"a/b/**/*.*\", \"a/b/c/d/e.f\"\n    assert_file_matches \"a/b/**/c/**/d/*.*\", \"a/b/c/d/e.f\"\n    assert_file_matches \"a/b/**/d/**/*.*\", \"a/b/c/d/e.f\"\n    assert_file_matches \"a/b/**/d/**/*.*\", \"a/b/c/d/g/e.f\"\n    assert_file_matches \"a/b/**/d/**/*.*\", \"a/b/c/d/g/g/e.f\"\n    assert_file_matches \"a/b-*/**/z.js\", \"a/b-c/z.js\"\n    assert_file_matches \"a/b-*/**/z.js\", \"a/b-c/d/e/z.js\"\n\n    assert_file_matches \"*/*\", \"a/b\"\n    assert_file_matches \"a/b/c/*.md\", \"a/b/c/xyz.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bb.bb/c/xyz.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bb/c/xyz.md\"\n    assert_file_matches \"a/*/c/*.md\", \"a/bbbb/c/xyz.md\"\n\n    assert_file_matches \"**/*\", \"a/b/c\"\n    assert_file_matches \"**/**\", \"a/b/c\"\n    assert_file_matches \"*/**\", \"a/b/c\"\n    assert_file_matches \"a/**/j/**/z/*.md\", \"a/b/c/d/e/j/n/p/o/z/c.md\"\n    assert_file_matches \"a/**/z/*.md\", \"a/b/c/d/e/z/c.md\"\n    assert_file_matches \"a/**/c/*.md\", \"a/bb.bb/aa/b.b/aa/c/xyz.md\"\n    assert_file_matches \"a/**/c/*.md\", \"a/bb.bb/aa/bb/aa/c/xyz.md\"\n    refute_file_matches \"a/**/j/**/z/*.md\", \"a/b/c/j/e/z/c.txt\"\n    refute_file_matches \"a/b/**/c{d,e}/**/xyz.md\", \"a/b/c/xyz.md\"\n    refute_file_matches \"a/b/**/c{d,e}/**/xyz.md\", \"a/b/d/xyz.md\"\n    refute_file_matches \"a/**/\", \"a/b\"\n    # refute_file_matches \"**/*\", \"a/b/.js/c.txt\"\n    refute_file_matches \"a/**/\", \"a/b/c/d\"\n    refute_file_matches \"a/**/\", \"a/bb\"\n    refute_file_matches \"a/**/\", \"a/cb\"\n    assert_file_matches \"/**\", \"/a/b\"\n    assert_file_matches \"**/*\", \"a.b\"\n    assert_file_matches \"**/*\", \"a.js\"\n    assert_file_matches \"**/*.js\", \"a.js\"\n    # assert_file_matches \"a/**/\", \"a/\"\n    assert_file_matches \"**/*.js\", \"a/a.js\"\n    assert_file_matches \"**/*.js\", \"a/a/b.js\"\n    assert_file_matches \"a/**/b\", \"a/b\"\n    assert_file_matches \"a/**b\", \"a/b\"\n    assert_file_matches \"**/*.md\", \"a/b.md\"\n    assert_file_matches \"**/*\", \"a/b/c.js\"\n    assert_file_matches \"**/*\", \"a/b/c.txt\"\n    assert_file_matches \"a/**/\", \"a/b/c/d/\"\n    assert_file_matches \"**/*\", \"a/b/c/d/a.js\"\n    assert_file_matches \"a/b/**/*.js\", \"a/b/c/z.js\"\n    assert_file_matches \"a/b/**/*.js\", \"a/b/z.js\"\n    assert_file_matches \"**/*\", \"ab\"\n    assert_file_matches \"**/*\", \"ab/c\"\n    assert_file_matches \"**/*\", \"ab/c/d\"\n    assert_file_matches \"**/*\", \"abc.js\"\n\n    refute_file_matches \"**/\", \"a\"\n    refute_file_matches \"**/a/*\", \"a\"\n    refute_file_matches \"**/a/*/*\", \"a\"\n    refute_file_matches \"*/a/**\", \"a\"\n    refute_file_matches \"a/**/*\", \"a\"\n    refute_file_matches \"a/**/**/*\", \"a\"\n    refute_file_matches \"**/\", \"a/b\"\n    refute_file_matches \"**/b/*\", \"a/b\"\n    refute_file_matches \"**/b/*/*\", \"a/b\"\n    refute_file_matches \"b/**\", \"a/b\"\n    refute_file_matches \"**/\", \"a/b/c\"\n    refute_file_matches \"**/**/b\", \"a/b/c\"\n    refute_file_matches \"**/b\", \"a/b/c\"\n    refute_file_matches \"**/b/*/*\", \"a/b/c\"\n    refute_file_matches \"b/**\", \"a/b/c\"\n    refute_file_matches \"**/\", \"a/b/c/d\"\n    refute_file_matches \"**/d/*\", \"a/b/c/d\"\n    refute_file_matches \"b/**\", \"a/b/c/d\"\n    assert_file_matches \"**\", \"a\"\n    assert_file_matches \"**/**\", \"a\"\n    assert_file_matches \"**/**/*\", \"a\"\n    assert_file_matches \"**/**/a\", \"a\"\n    assert_file_matches \"**/a\", \"a\"\n    # assert_file_matches \"**/a/**\", \"a\"\n    # assert_file_matches \"a/**\", \"a\"\n    assert_file_matches \"**\", \"a/b\"\n    assert_file_matches \"**/**\", \"a/b\"\n    assert_file_matches \"**/**/*\", \"a/b\"\n    assert_file_matches \"**/**/b\", \"a/b\"\n    assert_file_matches \"**/b\", \"a/b\"\n    # assert_file_matches \"**/b/**\", \"a/b\"\n    # assert_file_matches \"*/b/**\", \"a/b\"\n    assert_file_matches \"a/**\", \"a/b\"\n    assert_file_matches \"a/**/*\", \"a/b\"\n    assert_file_matches \"a/**/**/*\", \"a/b\"\n    assert_file_matches \"**\", \"a/b/c\"\n    assert_file_matches \"**/**\", \"a/b/c\"\n    assert_file_matches \"**/**/*\", \"a/b/c\"\n    assert_file_matches \"**/b/*\", \"a/b/c\"\n    assert_file_matches \"**/b/**\", \"a/b/c\"\n    assert_file_matches \"*/b/**\", \"a/b/c\"\n    assert_file_matches \"a/**\", \"a/b/c\"\n    assert_file_matches \"a/**/*\", \"a/b/c\"\n    assert_file_matches \"a/**/**/*\", \"a/b/c\"\n    assert_file_matches \"**\", \"a/b/c/d\"\n    assert_file_matches \"**/**\", \"a/b/c/d\"\n    assert_file_matches \"**/**/*\", \"a/b/c/d\"\n    assert_file_matches \"**/**/d\", \"a/b/c/d\"\n    assert_file_matches \"**/b/**\", \"a/b/c/d\"\n    assert_file_matches \"**/b/*/*\", \"a/b/c/d\"\n    assert_file_matches \"**/d\", \"a/b/c/d\"\n    assert_file_matches \"*/b/**\", \"a/b/c/d\"\n    assert_file_matches \"a/**\", \"a/b/c/d\"\n    assert_file_matches \"a/**/*\", \"a/b/c/d\"\n    assert_file_matches \"a/**/**/*\", \"a/b/c/d\"\n\n    assert_file_matches \"**/**.txt.js\", \"/foo/bar.txt.js\"\n  end\n\n  it \"utf8\" do\n    assert_file_matches \"フ*/**/*\", \"フォルダ/aaa.js\"\n    assert_file_matches \"フォ*/**/*\", \"フォルダ/aaa.js\"\n    assert_file_matches \"フォル*/**/*\", \"フォルダ/aaa.js\"\n    assert_file_matches \"フ*ル*/**/*\", \"フォルダ/aaa.js\"\n    assert_file_matches \"フォルダ/**/*\", \"フォルダ/aaa.js\"\n  end\n\n  pending \"negation\" do\n    refute_file_matches \"!*\", \"abc\"\n    refute_file_matches \"!abc\", \"abc\"\n    refute_file_matches \"*!.md\", \"bar.md\"\n    refute_file_matches \"foo!.md\", \"bar.md\"\n    refute_file_matches \"\\\\!*!*.md\", \"foo!.md\"\n    refute_file_matches \"\\\\!*!*.md\", \"foo!bar.md\"\n    assert_file_matches \"*!*.md\", \"!foo!.md\"\n    assert_file_matches \"\\\\!*!*.md\", \"!foo!.md\"\n    assert_file_matches \"!*foo\", \"abc\"\n    assert_file_matches \"!foo*\", \"abc\"\n    assert_file_matches \"!xyz\", \"abc\"\n    assert_file_matches \"*!*.*\", \"ba!r.js\"\n    assert_file_matches \"*.md\", \"bar.md\"\n    assert_file_matches \"*!*.*\", \"foo!.md\"\n    assert_file_matches \"*!*.md\", \"foo!.md\"\n    assert_file_matches \"*!.md\", \"foo!.md\"\n    assert_file_matches \"*.md\", \"foo!.md\"\n    assert_file_matches \"foo!.md\", \"foo!.md\"\n    assert_file_matches \"*!*.md\", \"foo!bar.md\"\n    assert_file_matches \"*b*.md\", \"foobar.md\"\n\n    refute_file_matches \"a!!b\", \"a\"\n    refute_file_matches \"a!!b\", \"aa\"\n    refute_file_matches \"a!!b\", \"a/b\"\n    refute_file_matches \"a!!b\", \"a!b\"\n    assert_file_matches \"a!!b\", \"a!!b\"\n    refute_file_matches \"a!!b\", \"a/!!/b\"\n\n    refute_file_matches \"!a/b\", \"a/b\"\n    assert_file_matches \"!a/b\", \"a\"\n    assert_file_matches \"!a/b\", \"a.b\"\n    assert_file_matches \"!a/b\", \"a/a\"\n    assert_file_matches \"!a/b\", \"a/c\"\n    assert_file_matches \"!a/b\", \"b/a\"\n    assert_file_matches \"!a/b\", \"b/b\"\n    assert_file_matches \"!a/b\", \"b/c\"\n\n    refute_file_matches \"!abc\", \"abc\"\n    assert_file_matches \"!!abc\", \"abc\"\n    refute_file_matches \"!!!abc\", \"abc\"\n    assert_file_matches \"!!!!abc\", \"abc\"\n    refute_file_matches \"!!!!!abc\", \"abc\"\n    assert_file_matches \"!!!!!!abc\", \"abc\"\n    refute_file_matches \"!!!!!!!abc\", \"abc\"\n    assert_file_matches \"!!!!!!!!abc\", \"abc\"\n\n    # refute_file_matches \"!(*/*)\", \"a/a\"\n    # refute_file_matches \"!(*/*)\", \"a/b\"\n    # refute_file_matches \"!(*/*)\", \"a/c\"\n    # refute_file_matches \"!(*/*)\", \"b/a\"\n    # refute_file_matches \"!(*/*)\", \"b/b\"\n    # refute_file_matches \"!(*/*)\", \"b/c\"\n    # refute_file_matches \"!(*/b)\", \"a/b\"\n    # refute_file_matches \"!(*/b)\", \"b/b\"\n    # refute_file_matches \"!(a/b)\", \"a/b\"\n    refute_file_matches \"!*\", \"a\"\n    refute_file_matches \"!*\", \"a.b\"\n    refute_file_matches \"!*/*\", \"a/a\"\n    refute_file_matches \"!*/*\", \"a/b\"\n    refute_file_matches \"!*/*\", \"a/c\"\n    refute_file_matches \"!*/*\", \"b/a\"\n    refute_file_matches \"!*/*\", \"b/b\"\n    refute_file_matches \"!*/*\", \"b/c\"\n    refute_file_matches \"!*/b\", \"a/b\"\n    refute_file_matches \"!*/b\", \"b/b\"\n    refute_file_matches \"!*/c\", \"a/c\"\n    refute_file_matches \"!*/c\", \"b/c\"\n    refute_file_matches \"!*a*\", \"bar\"\n    refute_file_matches \"!*a*\", \"fab\"\n    # refute_file_matches \"!a/(*)\", \"a/a\"\n    # refute_file_matches \"!a/(*)\", \"a/b\"\n    # refute_file_matches \"!a/(*)\", \"a/c\"\n    # refute_file_matches \"!a/(b)\", \"a/b\"\n    refute_file_matches \"!a/*\", \"a/a\"\n    refute_file_matches \"!a/*\", \"a/b\"\n    refute_file_matches \"!a/*\", \"a/c\"\n    refute_file_matches \"!f*b\", \"fab\"\n    # assert_file_matches \"!(*/*)\", \"a\"\n    # assert_file_matches \"!(*/*)\", \"a.b\"\n    # assert_file_matches \"!(*/b)\", \"a\"\n    # assert_file_matches \"!(*/b)\", \"a.b\"\n    # assert_file_matches \"!(*/b)\", \"a/a\"\n    # assert_file_matches \"!(*/b)\", \"a/c\"\n    # assert_file_matches \"!(*/b)\", \"b/a\"\n    # assert_file_matches \"!(*/b)\", \"b/c\"\n    # assert_file_matches \"!(a/b)\", \"a\"\n    # assert_file_matches \"!(a/b)\", \"a.b\"\n    # assert_file_matches \"!(a/b)\", \"a/a\"\n    # assert_file_matches \"!(a/b)\", \"a/c\"\n    # assert_file_matches \"!(a/b)\", \"b/a\"\n    # assert_file_matches \"!(a/b)\", \"b/b\"\n    # assert_file_matches \"!(a/b)\", \"b/c\"\n    assert_file_matches \"!*\", \"a/a\"\n    assert_file_matches \"!*\", \"a/b\"\n    assert_file_matches \"!*\", \"a/c\"\n    assert_file_matches \"!*\", \"b/a\"\n    assert_file_matches \"!*\", \"b/b\"\n    assert_file_matches \"!*\", \"b/c\"\n    assert_file_matches \"!*/*\", \"a\"\n    assert_file_matches \"!*/*\", \"a.b\"\n    assert_file_matches \"!*/b\", \"a\"\n    assert_file_matches \"!*/b\", \"a.b\"\n    assert_file_matches \"!*/b\", \"a/a\"\n    assert_file_matches \"!*/b\", \"a/c\"\n    assert_file_matches \"!*/b\", \"b/a\"\n    assert_file_matches \"!*/b\", \"b/c\"\n    assert_file_matches \"!*/c\", \"a\"\n    assert_file_matches \"!*/c\", \"a.b\"\n    assert_file_matches \"!*/c\", \"a/a\"\n    assert_file_matches \"!*/c\", \"a/b\"\n    assert_file_matches \"!*/c\", \"b/a\"\n    assert_file_matches \"!*/c\", \"b/b\"\n    assert_file_matches \"!*a*\", \"foo\"\n    # assert_file_matches \"!a/(*)\", \"a\"\n    # assert_file_matches \"!a/(*)\", \"a.b\"\n    # assert_file_matches \"!a/(*)\", \"b/a\"\n    # assert_file_matches \"!a/(*)\", \"b/b\"\n    # assert_file_matches \"!a/(*)\", \"b/c\"\n    # assert_file_matches \"!a/(b)\", \"a\"\n    # assert_file_matches \"!a/(b)\", \"a.b\"\n    # assert_file_matches \"!a/(b)\", \"a/a\"\n    # assert_file_matches \"!a/(b)\", \"a/c\"\n    # assert_file_matches \"!a/(b)\", \"b/a\"\n    # assert_file_matches \"!a/(b)\", \"b/b\"\n    # assert_file_matches \"!a/(b)\", \"b/c\"\n    assert_file_matches \"!a/*\", \"a\"\n    assert_file_matches \"!a/*\", \"a.b\"\n    assert_file_matches \"!a/*\", \"b/a\"\n    assert_file_matches \"!a/*\", \"b/b\"\n    assert_file_matches \"!a/*\", \"b/c\"\n    assert_file_matches \"!f*b\", \"bar\"\n    assert_file_matches \"!f*b\", \"foo\"\n\n    refute_file_matches \"!.md\", \".md\"\n    assert_file_matches \"!**/*.md\", \"a.js\"\n    # refute_file_matches \"!**/*.md\", \"b.md\"\n    assert_file_matches \"!**/*.md\", \"c.txt\"\n    assert_file_matches \"!*.md\", \"a.js\"\n    refute_file_matches \"!*.md\", \"b.md\"\n    assert_file_matches \"!*.md\", \"c.txt\"\n    refute_file_matches \"!*.md\", \"abc.md\"\n    assert_file_matches \"!*.md\", \"abc.txt\"\n    refute_file_matches \"!*.md\", \"foo.md\"\n    assert_file_matches \"!.md\", \"foo.md\"\n\n    assert_file_matches \"!*.md\", \"a.js\"\n    assert_file_matches \"!*.md\", \"b.txt\"\n    refute_file_matches \"!*.md\", \"c.md\"\n    refute_file_matches \"!a/*/a.js\", \"a/a/a.js\"\n    refute_file_matches \"!a/*/a.js\", \"a/b/a.js\"\n    refute_file_matches \"!a/*/a.js\", \"a/c/a.js\"\n    refute_file_matches \"!a/*/*/a.js\", \"a/a/a/a.js\"\n    assert_file_matches \"!a/*/*/a.js\", \"b/a/b/a.js\"\n    assert_file_matches \"!a/*/*/a.js\", \"c/a/c/a.js\"\n    refute_file_matches \"!a/a*.txt\", \"a/a.txt\"\n    assert_file_matches \"!a/a*.txt\", \"a/b.txt\"\n    assert_file_matches \"!a/a*.txt\", \"a/c.txt\"\n    refute_file_matches \"!a.a*.txt\", \"a.a.txt\"\n    assert_file_matches \"!a.a*.txt\", \"a.b.txt\"\n    assert_file_matches \"!a.a*.txt\", \"a.c.txt\"\n    refute_file_matches \"!a/*.txt\", \"a/a.txt\"\n    refute_file_matches \"!a/*.txt\", \"a/b.txt\"\n    refute_file_matches \"!a/*.txt\", \"a/c.txt\"\n\n    assert_file_matches \"!*.md\", \"a.js\"\n    assert_file_matches \"!*.md\", \"b.txt\"\n    refute_file_matches \"!*.md\", \"c.md\"\n    # refute_file_matches \"!**/a.js\", \"a/a/a.js\"\n    # refute_file_matches \"!**/a.js\", \"a/b/a.js\"\n    # refute_file_matches \"!**/a.js\", \"a/c/a.js\"\n    assert_file_matches \"!**/a.js\", \"a/a/b.js\"\n    refute_file_matches \"!a/**/a.js\", \"a/a/a/a.js\"\n    assert_file_matches \"!a/**/a.js\", \"b/a/b/a.js\"\n    assert_file_matches \"!a/**/a.js\", \"c/a/c/a.js\"\n    assert_file_matches \"!**/*.md\", \"a/b.js\"\n    assert_file_matches \"!**/*.md\", \"a.js\"\n    refute_file_matches \"!**/*.md\", \"a/b.md\"\n    # refute_file_matches \"!**/*.md\", \"a.md\"\n    refute_file_matches \"**/*.md\", \"a/b.js\"\n    refute_file_matches \"**/*.md\", \"a.js\"\n    assert_file_matches \"**/*.md\", \"a/b.md\"\n    assert_file_matches \"**/*.md\", \"a.md\"\n    assert_file_matches \"!**/*.md\", \"a/b.js\"\n    assert_file_matches \"!**/*.md\", \"a.js\"\n    refute_file_matches \"!**/*.md\", \"a/b.md\"\n    # refute_file_matches \"!**/*.md\", \"a.md\"\n    assert_file_matches \"!*.md\", \"a/b.js\"\n    assert_file_matches \"!*.md\", \"a.js\"\n    assert_file_matches \"!*.md\", \"a/b.md\"\n    refute_file_matches \"!*.md\", \"a.md\"\n    assert_file_matches \"!**/*.md\", \"a.js\"\n    # refute_file_matches \"!**/*.md\", \"b.md\"\n    assert_file_matches \"!**/*.md\", \"c.txt\"\n  end\n\n  it \"question_mark\" do\n    assert_file_matches \"?\", \"a\"\n    refute_file_matches \"?\", \"aa\"\n    refute_file_matches \"?\", \"ab\"\n    refute_file_matches \"?\", \"aaa\"\n    refute_file_matches \"?\", \"abcdefg\"\n\n    refute_file_matches \"??\", \"a\"\n    assert_file_matches \"??\", \"aa\"\n    assert_file_matches \"??\", \"ab\"\n    refute_file_matches \"??\", \"aaa\"\n    refute_file_matches \"??\", \"abcdefg\"\n\n    refute_file_matches \"???\", \"a\"\n    refute_file_matches \"???\", \"aa\"\n    refute_file_matches \"???\", \"ab\"\n    assert_file_matches \"???\", \"aaa\"\n    refute_file_matches \"???\", \"abcdefg\"\n\n    refute_file_matches \"a?c\", \"aaa\"\n    assert_file_matches \"a?c\", \"aac\"\n    assert_file_matches \"a?c\", \"abc\"\n    refute_file_matches \"ab?\", \"a\"\n    refute_file_matches \"ab?\", \"aa\"\n    refute_file_matches \"ab?\", \"ab\"\n    refute_file_matches \"ab?\", \"ac\"\n    refute_file_matches \"ab?\", \"abcd\"\n    refute_file_matches \"ab?\", \"abbb\"\n    assert_file_matches \"a?b\", \"acb\"\n\n    refute_file_matches \"a/?/c/?/e.md\", \"a/bb/c/dd/e.md\"\n    assert_file_matches \"a/??/c/??/e.md\", \"a/bb/c/dd/e.md\"\n    refute_file_matches \"a/??/c.md\", \"a/bbb/c.md\"\n    assert_file_matches \"a/?/c.md\", \"a/b/c.md\"\n    assert_file_matches \"a/?/c/?/e.md\", \"a/b/c/d/e.md\"\n    refute_file_matches \"a/?/c/???/e.md\", \"a/b/c/d/e.md\"\n    assert_file_matches \"a/?/c/???/e.md\", \"a/b/c/zzz/e.md\"\n    refute_file_matches \"a/?/c.md\", \"a/bb/c.md\"\n    assert_file_matches \"a/??/c.md\", \"a/bb/c.md\"\n    assert_file_matches \"a/???/c.md\", \"a/bbb/c.md\"\n    assert_file_matches \"a/????/c.md\", \"a/bbbb/c.md\"\n  end\n\n  it \"braces\" do\n    assert_file_matches \"{a,b,c}\", \"a\"\n    assert_file_matches \"{a,b,c}\", \"b\"\n    assert_file_matches \"{a,b,c}\", \"c\"\n    refute_file_matches \"{a,b,c}\", \"aa\"\n    refute_file_matches \"{a,b,c}\", \"bb\"\n    refute_file_matches \"{a,b,c}\", \"cc\"\n\n    assert_file_matches \"a/{a,b}\", \"a/a\"\n    assert_file_matches \"a/{a,b}\", \"a/b\"\n    refute_file_matches \"a/{a,b}\", \"a/c\"\n    refute_file_matches \"a/{a,b}\", \"b/b\"\n    refute_file_matches \"a/{a,b,c}\", \"b/b\"\n    assert_file_matches \"a/{a,b,c}\", \"a/c\"\n    assert_file_matches \"a{b,bc}.txt\", \"abc.txt\"\n\n    assert_file_matches \"foo[{a,b}]baz\", \"foo{baz\"\n\n    refute_file_matches \"a{,b}.txt\", \"abc.txt\"\n    refute_file_matches \"a{a,b,}.txt\", \"abc.txt\"\n    refute_file_matches \"a{b,}.txt\", \"abc.txt\"\n    assert_file_matches \"a{,b}.txt\", \"a.txt\"\n    assert_file_matches \"a{b,}.txt\", \"a.txt\"\n    assert_file_matches \"a{a,b,}.txt\", \"aa.txt\"\n    assert_file_matches \"a{,b}.txt\", \"ab.txt\"\n    assert_file_matches \"a{b,}.txt\", \"ab.txt\"\n\n    # assert_file_matches \"{a/,}a/**\", \"a\"\n    assert_file_matches \"a{a,b/}*.txt\", \"aa.txt\"\n    assert_file_matches \"a{a,b/}*.txt\", \"ab/.txt\"\n    assert_file_matches \"a{a,b/}*.txt\", \"ab/a.txt\"\n    # assert_file_matches \"{a/,}a/**\", \"a/\"\n    assert_file_matches \"{a/,}a/**\", \"a/a/\"\n    # assert_file_matches \"{a/,}a/**\", \"a/a\"\n    assert_file_matches \"{a/,}a/**\", \"a/a/a\"\n    assert_file_matches \"{a/,}a/**\", \"a/a/\"\n    assert_file_matches \"{a/,}a/**\", \"a/a/a/\"\n    assert_file_matches \"{a/,}b/**\", \"a/b/a/\"\n    assert_file_matches \"{a/,}b/**\", \"b/a/\"\n    assert_file_matches \"a{,/}*.txt\", \"a.txt\"\n    assert_file_matches \"a{,/}*.txt\", \"ab.txt\"\n    assert_file_matches \"a{,/}*.txt\", \"a/b.txt\"\n    assert_file_matches \"a{,/}*.txt\", \"a/ab.txt\"\n\n    assert_file_matches \"a{,.*{foo,db},\\\\(bar\\\\)}.txt\", \"a.txt\"\n    refute_file_matches \"a{,.*{foo,db},\\\\(bar\\\\)}.txt\", \"adb.txt\"\n    assert_file_matches \"a{,.*{foo,db},\\\\(bar\\\\)}.txt\", \"a.db.txt\"\n\n    assert_file_matches \"a{,*.{foo,db},\\\\(bar\\\\)}.txt\", \"a.txt\"\n    refute_file_matches \"a{,*.{foo,db},\\\\(bar\\\\)}.txt\", \"adb.txt\"\n    assert_file_matches \"a{,*.{foo,db},\\\\(bar\\\\)}.txt\", \"a.db.txt\"\n\n    # assert_file_matches \"a{,.*{foo,db},\\\\(bar\\\\)}\", \"a\"\n    refute_file_matches \"a{,.*{foo,db},\\\\(bar\\\\)}\", \"adb\"\n    assert_file_matches \"a{,.*{foo,db},\\\\(bar\\\\)}\", \"a.db\"\n\n    # assert_file_matches \"a{,*.{foo,db},\\\\(bar\\\\)}\", \"a\"\n    refute_file_matches \"a{,*.{foo,db},\\\\(bar\\\\)}\", \"adb\"\n    assert_file_matches \"a{,*.{foo,db},\\\\(bar\\\\)}\", \"a.db\"\n\n    refute_file_matches \"{,.*{foo,db},\\\\(bar\\\\)}\", \"a\"\n    refute_file_matches \"{,.*{foo,db},\\\\(bar\\\\)}\", \"adb\"\n    refute_file_matches \"{,.*{foo,db},\\\\(bar\\\\)}\", \"a.db\"\n    assert_file_matches \"{,.*{foo,db},\\\\(bar\\\\)}\", \".db\"\n\n    refute_file_matches \"{,*.{foo,db},\\\\(bar\\\\)}\", \"a\"\n    assert_file_matches \"{*,*.{foo,db},\\\\(bar\\\\)}\", \"a\"\n    refute_file_matches \"{,*.{foo,db},\\\\(bar\\\\)}\", \"adb\"\n    assert_file_matches \"{,*.{foo,db},\\\\(bar\\\\)}\", \"a.db\"\n\n    refute_file_matches \"a/b/**/c{d,e}/**/xyz.md\", \"a/b/c/xyz.md\"\n    refute_file_matches \"a/b/**/c{d,e}/**/xyz.md\", \"a/b/d/xyz.md\"\n    assert_file_matches \"a/b/**/c{d,e}/**/xyz.md\", \"a/b/cd/xyz.md\"\n    assert_file_matches \"a/b/**/{c,d,e}/**/xyz.md\", \"a/b/c/xyz.md\"\n    assert_file_matches \"a/b/**/{c,d,e}/**/xyz.md\", \"a/b/d/xyz.md\"\n    assert_file_matches \"a/b/**/{c,d,e}/**/xyz.md\", \"a/b/e/xyz.md\"\n\n    assert_file_matches \"*{a,b}*\", \"xax\"\n    assert_file_matches \"*{a,b}*\", \"xxax\"\n    assert_file_matches \"*{a,b}*\", \"xbx\"\n\n    assert_file_matches \"*{*a,b}\", \"xba\"\n    assert_file_matches \"*{*a,b}\", \"xb\"\n\n    refute_file_matches \"*??\", \"a\"\n    refute_file_matches \"*???\", \"aa\"\n    assert_file_matches \"*???\", \"aaa\"\n    refute_file_matches \"*****??\", \"a\"\n    refute_file_matches \"*****???\", \"aa\"\n    assert_file_matches \"*****???\", \"aaa\"\n\n    refute_file_matches \"a*?c\", \"aaa\"\n    assert_file_matches \"a*?c\", \"aac\"\n    assert_file_matches \"a*?c\", \"abc\"\n\n    assert_file_matches \"a**?c\", \"abc\"\n    refute_file_matches \"a**?c\", \"abb\"\n    assert_file_matches \"a**?c\", \"acc\"\n    assert_file_matches \"a*****?c\", \"abc\"\n\n    assert_file_matches \"*****?\", \"a\"\n    assert_file_matches \"*****?\", \"aa\"\n    assert_file_matches \"*****?\", \"abc\"\n    assert_file_matches \"*****?\", \"zzz\"\n    assert_file_matches \"*****?\", \"bbb\"\n    assert_file_matches \"*****?\", \"aaaa\"\n\n    refute_file_matches \"*****??\", \"a\"\n    assert_file_matches \"*****??\", \"aa\"\n    assert_file_matches \"*****??\", \"abc\"\n    assert_file_matches \"*****??\", \"zzz\"\n    assert_file_matches \"*****??\", \"bbb\"\n    assert_file_matches \"*****??\", \"aaaa\"\n\n    refute_file_matches \"?*****??\", \"a\"\n    refute_file_matches \"?*****??\", \"aa\"\n    assert_file_matches \"?*****??\", \"abc\"\n    assert_file_matches \"?*****??\", \"zzz\"\n    assert_file_matches \"?*****??\", \"bbb\"\n    assert_file_matches \"?*****??\", \"aaaa\"\n\n    assert_file_matches \"?*****?c\", \"abc\"\n    refute_file_matches \"?*****?c\", \"abb\"\n    refute_file_matches \"?*****?c\", \"zzz\"\n\n    assert_file_matches \"?***?****c\", \"abc\"\n    refute_file_matches \"?***?****c\", \"bbb\"\n    refute_file_matches \"?***?****c\", \"zzz\"\n\n    assert_file_matches \"?***?****?\", \"abc\"\n    assert_file_matches \"?***?****?\", \"bbb\"\n    assert_file_matches \"?***?****?\", \"zzz\"\n\n    assert_file_matches \"?***?****\", \"abc\"\n    assert_file_matches \"*******c\", \"abc\"\n    assert_file_matches \"*******?\", \"abc\"\n    assert_file_matches \"a*cd**?**??k\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??k\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??k***\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??***k\", \"abcdecdhjk\"\n    assert_file_matches \"a**?**cd**?**??***k**\", \"abcdecdhjk\"\n    assert_file_matches \"a****c**?**??*****\", \"abcdecdhjk\"\n\n    refute_file_matches \"a/?/c/?/*/e.md\", \"a/b/c/d/e.md\"\n    assert_file_matches \"a/?/c/?/*/e.md\", \"a/b/c/d/e/e.md\"\n    assert_file_matches \"a/?/c/?/*/e.md\", \"a/b/c/d/efghijk/e.md\"\n    assert_file_matches \"a/?/**/e.md\", \"a/b/c/d/efghijk/e.md\"\n    refute_file_matches \"a/?/e.md\", \"a/bb/e.md\"\n    assert_file_matches \"a/??/e.md\", \"a/bb/e.md\"\n    refute_file_matches \"a/?/**/e.md\", \"a/bb/e.md\"\n    assert_file_matches \"a/?/**/e.md\", \"a/b/ccc/e.md\"\n    assert_file_matches \"a/*/?/**/e.md\", \"a/b/c/d/efghijk/e.md\"\n    assert_file_matches \"a/*/?/**/e.md\", \"a/b/c/d/efgh.ijk/e.md\"\n    assert_file_matches \"a/*/?/**/e.md\", \"a/b.bb/c/d/efgh.ijk/e.md\"\n    assert_file_matches \"a/*/?/**/e.md\", \"a/bbb/c/d/efgh.ijk/e.md\"\n\n    assert_file_matches \"a/*/ab??.md\", \"a/bbb/abcd.md\"\n    assert_file_matches \"a/bbb/ab??.md\", \"a/bbb/abcd.md\"\n    assert_file_matches \"a/bbb/ab???md\", \"a/bbb/abcd.md\"\n\n    assert_file_matches \"{a,b}/c/{d,e}/**/*.ts\", \"a/c/d/one/two/three.test.ts\"\n    assert_file_matches \"{a,{d,e}b}/c\", \"a/c\"\n    assert_file_matches \"{**/a,**/b}\", \"b\"\n  end\n\n  it \"not_paired_braces\" do\n    refute_file_matches \"{a,}}\", \"a\"\n    assert_file_matches \"{a,}}\", \"a}\"\n  end\nend\n"
  },
  {
    "path": "spec/std/file/match_spec.cr",
    "content": "require \"spec\"\n\nprivate def assert_file_matches(pattern, path : String, *, file = __FILE__, line = __LINE__)\n  File.match?(pattern, path).should be_true, file: file, line: line\n  File.match?(pattern, Path.posix(path)).should be_true, file: file, line: line\n  File.match?(pattern, Path.posix(path).to_windows).should be_true, file: file, line: line\nend\n\nprivate def refute_file_matches(pattern, path : String, *, file = __FILE__, line = __LINE__)\n  File.match?(pattern, path).should be_false, file: file, line: line\n  File.match?(pattern, Path.posix(path)).should be_false, file: file, line: line\n  File.match?(pattern, Path.posix(path).to_windows).should be_false, file: file, line: line\nend\n\ndescribe File do\n  describe \".match?\" do\n    it \"matches basics\" do\n      assert_file_matches \"abc\", \"abc\"\n      assert_file_matches \"*\", \"abc\"\n      assert_file_matches \"*c\", \"abc\"\n      assert_file_matches \"a*\", \"a\"\n      assert_file_matches \"a*\", \"abc\"\n      assert_file_matches \"a*/b\", \"abc/b\"\n      assert_file_matches \"*x\", \"xxx\"\n      assert_file_matches \"*.x\", \"a.x\"\n      assert_file_matches \"a/b/*.x\", \"a/b/c.x\"\n      refute_file_matches \"*.x\", \"a/b/c.x\"\n      refute_file_matches \"c.x\", \"a/b/c.x\"\n      refute_file_matches \"b/*.x\", \"a/b/c.x\"\n    end\n\n    it \"matches multiple expansions\" do\n      assert_file_matches \"a*b*c*d*e*/f\", \"axbxcxdxe/f\"\n      assert_file_matches \"a*b*c*d*e*/f\", \"axbxcxdxexxx/f\"\n      assert_file_matches \"a*b?c*x\", \"abxbbxdbxebxczzx\"\n      refute_file_matches \"a*b?c*x\", \"abxbbxdbxebxczzy\"\n    end\n\n    describe \"multibyte\" do\n      it \"single-character match\" do\n        assert_file_matches \"a?b\", \"a☺b\"\n        refute_file_matches \"a???b\", \"a☺b\"\n      end\n\n      it \"character sets\" do\n        assert_file_matches \"[🐶🐱🐰].jpg\", \"🐶.jpg\"\n        refute_file_matches \"[🐶🐱🐰].jpg\", \"🐯.jpg\"\n        refute_file_matches \"[🐶🐱🐰].jpg\", \"x.jpg\"\n        assert_file_matches \"[🐶🐱🐰x].jpg\", \"🐶.jpg\"\n        refute_file_matches \"[🐶🐱🐰x].jpg\", \"🐯.jpg\"\n        assert_file_matches \"[🐶🐱🐰x].jpg\", \"x.jpg\"\n        assert_file_matches \"[^🐶🐱🐰].jpg\", \"🐯.jpg\"\n        refute_file_matches \"[^🐶🐱🐰].jpg\", \"🐶.jpg\"\n        assert_file_matches \"[^🐶🐱🐰].jpg\", \"x.jpg\"\n      end\n\n      it \"character ranges\" do\n        assert_file_matches \"[α-ω].doc\", \"β.doc\"\n        refute_file_matches \"[α-ω].doc\", \"Ω.doc\"\n        assert_file_matches \"[Α-Ω].pdf\", \"Θ.pdf\"\n\n        assert_file_matches \"[🥇-🥉].png\", \"🥈.png\"\n        refute_file_matches \"[🥇-🥉].png\", \"🏆.png\"\n        refute_file_matches \"[🥇-🥉].png\", \"2.png\"\n\n        assert_file_matches \"[α-ω🥇-🥉].doc\", \"β.doc\"\n        assert_file_matches \"[α-ω🥇-🥉].doc\", \"🥈.doc\"\n        refute_file_matches \"[α-ω🥇-🥉].doc\", \"Ω.doc\"\n        refute_file_matches \"[α-ω🥇-🥉].doc\", \"🏆.doc\"\n        assert_file_matches \"[Α-Ω🥇-🥉].pdf\", \"Θ.pdf\"\n      end\n\n      it \"braces\" do\n        assert_file_matches \"{café,restaurant}.png\", \"café.png\"\n        assert_file_matches \"{🐶,🐱,🐰}.log\", \"🐶.log\"\n        refute_file_matches \"{🐶,🐱,🐰}.log\", \"🐯.log\"\n      end\n\n      it \"wildcard\" do\n        assert_file_matches \"重要/*/中.txt\", \"重要/子文件夹/中.txt\"\n        refute_file_matches \"重要/*/中.txt\", \"重要/子文/件夹/中.txt\"\n      end\n\n      it \"globstar\" do\n        assert_file_matches \"重要/**/中.txt\", \"重要/子文件夹/中.txt\"\n        assert_file_matches \"重要/**/中.txt\", \"重要/子文/件夹/中.txt\"\n      end\n\n      it \"NFC and NFD are disparate\" do\n        assert_file_matches \"café.txt\", \"café.txt\"        # NFC\n        refute_file_matches \"café.txt\", \"cafe\\u0301.txt\"  # NFD\n        refute_file_matches \"cafe*.txt\", \"café.txt\"       # NFC\n        assert_file_matches \"cafe*.txt\", \"cafe\\u0301.txt\" # NFD\n      end\n    end\n\n    describe \"invalid byte sequences\" do\n      it \"single-character with invalid path\" do\n        assert_file_matches \"?.txt\", \"\\xC3.txt\"         # Invalid byte sequence\n        refute_file_matches \"?.txt\", \"\\xC3\\x28.txt\"     # Invalid byte sequence\n        refute_file_matches \"?.txt\", \"\\xED\\xA0\\x80.txt\" # Lone surrogate\n        assert_file_matches \"?.txt\", \"\\uFFFF.txt\"       # Noncharacter codepoint\n      end\n\n      it \"single-character with invalid pattern\" do\n        refute_file_matches \"\\xC3\\x28.txt\", \"a.txt\"     # Invalid byte sequence\n        refute_file_matches \"\\xED\\xA0\\x80.txt\", \"b.txt\" # Lone surrogate\n      end\n\n      it \"character set with invalid path\" do\n        refute_file_matches \"[a-z].txt\", \"\\xF0\\x28\\x8C\\x28.txt\" # Invalid byte sequence\n        refute_file_matches \"[A-Z].txt\", \"\\xED\\xA0\\x80.txt\"     # Lone surrogate\n      end\n\n      it \"character set with invalid pattern\" do\n        refute_file_matches \"[\\xC3\\x28].txt\", \"m.txt\"     # Invalid byte sequence\n        refute_file_matches \"[\\xED\\xA0\\x80].txt\", \"A.txt\" # Lone surrogate\n      end\n\n      it \"character range with invalid path\" do\n        refute_file_matches \"[a-z].txt\", \"\\xED\\xA0\\x80.txt\" # Invalid byte sequence\n        refute_file_matches \"[α-ω].txt\", \"\\xED\\xBF\\xBF.txt\" # Lone surrogate\n        refute_file_matches \"[😀-🙏].png\", \"\\xFF\\xFE\\xFD.png\" # Invalid byte sequence\n      end\n\n      it \"character range with invalid pattern\" do\n        refute_file_matches \"[\\xF0\\x28\\x8C\\x28].txt\", \"o.txt\"          # Corrupt range\n        refute_file_matches \"[\\xED\\xA0\\x80-\\xED\\xBD\\xBF].csv\", \"X.csv\" # Invalid range of surrogates\n      end\n\n      it \"invalid pattern and path\" do\n        assert_file_matches \"[\\xED\\xA0\\x80-α]?.log\", \"\\xC3\\x28.log\"      # Lone surrogate in pattern, bad in path\n        refute_file_matches \"[😀-\\uFFFF]?.json\", \"\\xF0\\x90\\x28\\xBC.json\"  # Invalid range with corrupt UTF-8\n        refute_file_matches \"[\\xED\\xA0\\x80-\\uFFFF]?\", \"\\xED\\xBD\\xBF.txt\" # Invalid pattern range and lone low surrogate in path\n      end\n    end\n\n    it \"* don't match path separator\" do\n      refute_file_matches \"a*\", \"ab/c\"\n      refute_file_matches \"a*/b\", \"a/c/b\"\n      refute_file_matches \"a*b*c*d*e*/f\", \"axbxcxdxe/xxx/f\"\n      refute_file_matches \"a*b*c*d*e*/f\", \"axbxcxdxexxx/fff\"\n    end\n\n    it \"**\" do\n      assert_file_matches \"a/b/**\", \"a/b/c.x\"\n      assert_file_matches \"a/**\", \"a/b/c.x\"\n      assert_file_matches \"a/**/d.x\", \"a/b/c/d.x\"\n      refute_file_matches \"a/**b/d.x\", \"a/bb/c/d.x\"\n      refute_file_matches \"a/b**/*\", \"a/bb/c/d.x\"\n    end\n\n    it \"** bugs (#15319)\" do\n      assert_file_matches \"a/**/*\", \"a/b/c/d.x\"\n      refute_file_matches \"a/b**/d.x\", \"a/bb/c/d.x\"\n      assert_file_matches \"**/*.x\", \"a/b/c.x\"\n      refute_file_matches \"**.x\", \"a/b/c.x\"\n    end\n\n    it \"** matches path separator\" do\n      refute_file_matches \"a**\", \"ab/c\"\n      refute_file_matches \"a**/b\", \"a/c/b\"\n      refute_file_matches \"a*b*c*d*e**/f\", \"axbxcxdxe/xxx/f\"\n      assert_file_matches \"a*b*c*d*e**/f\", \"axbxcxdxexxx/f\"\n      refute_file_matches \"a*b*c*d*e**/f\", \"axbxcxdxexxx/fff\"\n    end\n\n    it \"classes\" do\n      assert_file_matches \"ab[c]\", \"abc\"\n      assert_file_matches \"ab[b-d]\", \"abc\"\n      refute_file_matches \"ab[d-b]\", \"abc\"\n      refute_file_matches \"ab[e-g]\", \"abc\"\n      assert_file_matches \"ab[e-gc]\", \"abc\"\n      refute_file_matches \"ab[^c]\", \"abc\"\n      refute_file_matches \"ab[^b-d]\", \"abc\"\n      assert_file_matches \"ab[^e-g]\", \"abc\"\n      assert_file_matches \"a[^a]b\", \"a☺b\"\n      refute_file_matches \"a[^a][^a][^a]b\", \"a☺b\"\n      assert_file_matches \"[a-ζ]*\", \"α\"\n      refute_file_matches \"*[a-ζ]\", \"A\"\n    end\n\n    it \"escape\" do\n      # NOTE: `*` is forbidden in Windows paths\n      File.match?(\"a\\\\*b\", \"a*b\").should be_true\n      refute_file_matches \"a\\\\*b\", \"ab\"\n      File.match?(\"a\\\\**b\", \"a*bb\").should be_true\n      refute_file_matches \"a\\\\**b\", \"abb\"\n      File.match?(\"a*\\\\*b\", \"ab*b\").should be_true\n      refute_file_matches \"a*\\\\*b\", \"abb\"\n\n      assert_file_matches \"a\\\\[b\\\\]\", \"a[b]\"\n      refute_file_matches \"a\\\\[b\\\\]\", \"ab\"\n      assert_file_matches \"a\\\\[bb\\\\]\", \"a[bb]\"\n      refute_file_matches \"a\\\\[bb\\\\]\", \"abb\"\n      assert_file_matches \"a[b]\\\\[b\\\\]\", \"ab[b]\"\n      refute_file_matches \"a[b]\\\\[b\\\\]\", \"abb\"\n    end\n\n    it \"special chars\" do\n      refute_file_matches \"a?b\", \"a/b\"\n      refute_file_matches \"a*b\", \"a/b\"\n    end\n\n    it \"classes escapes\" do\n      assert_file_matches \"[\\\\]a]\", \"]\"\n      assert_file_matches \"[\\\\-]\", \"-\"\n      assert_file_matches \"[x\\\\-]\", \"x\"\n      assert_file_matches \"[x\\\\-]\", \"-\"\n      refute_file_matches \"[x\\\\-]\", \"z\"\n      assert_file_matches \"[\\\\-x]\", \"x\"\n      assert_file_matches \"[\\\\-x]\", \"-\"\n      refute_file_matches \"[\\\\-x]\", \"a\"\n      assert_file_matches \"[]a]\", \"]\"\n      assert_file_matches \"[-]\", \"-\"\n      assert_file_matches \"[x-]\", \"x\"\n      assert_file_matches \"[-x]\", \"x\"\n      expect_raises(File::BadPatternError, \"Empty escape character\") do\n        File.match?(\"\\\\\", \"a\")\n      end\n      assert_file_matches \"[a-b-c]\", \"a\"\n      expect_raises(File::BadPatternError, \"unterminated character set\") do\n        File.match?(\"[\", \"a\")\n      end\n      expect_raises(File::BadPatternError, \"unterminated character set\") do\n        File.match?(\"[^\", \"a\")\n      end\n      expect_raises(File::BadPatternError, \"unterminated character set\") do\n        File.match?(\"[^bc\", \"a\")\n      end\n      refute_file_matches \"a[\", \"a\"\n    end\n\n    it \"alternates\" do\n      assert_file_matches \"{abc,def}\", \"abc\"\n      assert_file_matches \"ab{c,}\", \"abc\"\n      assert_file_matches \"ab{c,}\", \"ab\"\n      refute_file_matches \"ab{d,e}\", \"abc\"\n      assert_file_matches \"ab{*,/cde}\", \"abcde\"\n      assert_file_matches \"ab{*,/cde}\", \"ab/cde\"\n      assert_file_matches \"ab{?,/}de\", \"abcde\"\n      assert_file_matches \"ab{?,/}de\", \"ab/de\"\n      assert_file_matches \"ab{{c,d}ef,}\", \"ab\"\n      assert_file_matches \"ab{{c,d}ef,}\", \"abcef\"\n      assert_file_matches \"ab{{c,d}ef,}\", \"abdef\"\n    end\n\n    describe \"brace stack\" do\n      it \"allows up to 10 levels\" do\n        assert_file_matches \"{{{{{{{{{{a},b},b},b},b},b},b},b},b},b}\", \"a\"\n      end\n\n      it \"raises at more than 10 levels\" do\n        expect_raises File::BadPatternError, \"Brace nesting too deep: must not exceed 10 levels\" do\n          File.match? \"{{{{{{{{{{{a},b},b},b},b},b},b},b},b},b},b}\", \"b\"\n        end\n      end\n    end\n  end\n\n  it \"fuzz tests\" do\n    # https://github.com/devongovett/glob-match/issues/1\n    s = \"{*{??*{??**,Uz*zz}w**{*{**a,z***b*[!}w??*azzzzzzzz*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!z[za,z&zz}w**z*z*}\"\n    refute_file_matches s, s\n    s = \"**** *{*{??*{??***\\u{5} *{*{??*{??***\\u{5},\\0U\\0}]*****\\u{1},\\0***\\0,\\0\\0}w****,\\0U\\0}]*****\\u{1},\\0***\\0,\\0\\0}w*****\\u{1}***{}*.*\\0\\0*\\0\"\n    # Must use `String` overload here because `Path` raises on null byte\n    File.match?(s, s).should be_false\n  end\nend\n"
  },
  {
    "path": "spec/std/file/tempfile_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/tempfile\"\n\nprivate class TestRNG(T)\n  include Random\n\n  def initialize(@data : Array(T))\n    @i = 0\n  end\n\n  def next_u : T\n    i = @i\n    @i = (i + 1) % @data.size\n    @data[i]\n  end\n\n  def reset\n    @i = 0\n  end\nend\n\nprivate def normalize_permissions(permissions, *, directory)\n  {% if flag?(:win32) %}\n    normalized_permissions = 0o444\n    normalized_permissions |= 0o222 if permissions.bits_set?(0o200)\n    normalized_permissions |= 0o111 if directory\n    File::Permissions.new(normalized_permissions)\n  {% else %}\n    File::Permissions.new(permissions)\n  {% end %}\nend\n\ndescribe File do\n  describe \".tempname\" do\n    it \"creates a path without creating the file\" do\n      path = File.tempname\n\n      File.exists?(path).should be_false\n      File.dirname(path).should eq Dir.tempdir\n    end\n\n    it \"accepts single suffix argument\" do\n      path = File.tempname \".bar\"\n\n      File.exists?(path).should be_false\n      File.dirname(path).should eq Dir.tempdir\n      File.extname(path).should eq(\".bar\")\n    end\n\n    it \"accepts prefix and suffix arguments\" do\n      path = File.tempname \"foo\", \".bar\"\n\n      File.exists?(path).should be_false\n      File.dirname(path).should eq Dir.tempdir\n      File.extname(path).should eq(\".bar\")\n      File.basename(path).should start_with(\"foo\")\n    end\n\n    it \"accepts prefix with separator\" do\n      path = File.tempname \"foo/\", nil\n      File.dirname(path).should eq File.join(Dir.tempdir, \"foo\")\n      File.basename(path).should_not start_with(\"foo\")\n    end\n\n    it \"accepts dir argument\" do\n      path = File.tempname(dir: \"foo\")\n      File.dirname(path).should eq(\"foo\")\n    end\n  end\n\n  describe \".tempfile\" do\n    it \"creates and writes\" do\n      tempfile = File.tempfile\n      tempfile.print \"Hello!\"\n      tempfile.info.permissions.should eq normalize_permissions(0o600, directory: false)\n      tempfile.close\n\n      File.exists?(tempfile.path).should be_true\n      File.read(tempfile.path).should eq(\"Hello!\")\n    ensure\n      tempfile.try &.delete\n    end\n\n    it \"accepts single suffix argument\" do\n      tempfile = File.tempfile \".bar\"\n      tempfile.print \"Hello!\"\n      tempfile.info.permissions.should eq normalize_permissions(0o600, directory: false)\n      tempfile.close\n\n      File.extname(tempfile.path).should eq(\".bar\")\n\n      File.exists?(tempfile.path).should be_true\n      File.read(tempfile.path).should eq(\"Hello!\")\n    ensure\n      tempfile.try &.delete\n    end\n\n    it \"accepts prefix and suffix arguments\" do\n      tempfile = File.tempfile \"foo\", \".bar\"\n      tempfile.print \"Hello!\"\n      tempfile.info.permissions.should eq normalize_permissions(0o600, directory: false)\n      tempfile.close\n\n      File.extname(tempfile.path).should eq(\".bar\")\n      File.basename(tempfile.path).should start_with(\"foo\")\n\n      File.exists?(tempfile.path).should be_true\n      File.read(tempfile.path).should eq(\"Hello!\")\n    ensure\n      tempfile.try &.delete\n    end\n\n    it \"accepts dir argument\" do\n      file = File.tempfile(dir: datapath)\n      File.dirname(file.path).should eq(datapath)\n      file.close\n    ensure\n      file.try &.delete\n    end\n\n    it \"fails in nonwriteable folder\" do\n      err_directory = (datapath(\"non-existing-folder\") + Path::SEPARATORS[0]).inspect_unquoted\n      expect_raises(File::NotFoundError, \"Error creating temporary file: '#{err_directory}\") do\n        File.tempfile dir: datapath(\"non-existing-folder\")\n      end\n    end\n\n    it \"rejects null byte\" do\n      expect_raises(ArgumentError, \"String contains null byte\") do\n        File.tempfile(\"foo\\0\")\n      end\n      expect_raises(ArgumentError, \"String contains null byte\") do\n        File.tempfile(\"foo\", \"bar\\0\")\n      end\n      expect_raises(ArgumentError, \"String contains null byte\") do\n        File.tempfile(\"foo\", \"bar\", dir: \"baz\\0\")\n      end\n    end\n\n    describe \"with block\" do\n      it \"closes file\" do\n        filepath = nil\n        tempfile = File.tempfile do |tempfile|\n          filepath = tempfile.path\n        end\n        tempfile.path.should eq filepath\n        tempfile.closed?.should be_true\n\n        filepath = filepath.not_nil!\n        File.exists?(filepath).should be_true\n      ensure\n        File.delete(filepath) if filepath\n      end\n\n      it \"accepts single suffix argument\" do\n        tempfile = File.tempfile(\".bar\") do |tempfile|\n          File.exists?(tempfile.path).should be_true\n          tempfile.closed?.should be_false\n        end\n        tempfile.closed?.should be_true\n\n        File.extname(tempfile.path).should eq(\".bar\")\n\n        File.exists?(tempfile.path).should be_true\n      ensure\n        File.delete(tempfile.path) if tempfile\n      end\n\n      it \"accepts prefix and suffix arguments\" do\n        tempfile = File.tempfile(\"foo\", \".bar\") do |tempfile|\n          File.exists?(tempfile.path).should be_true\n          tempfile.closed?.should be_false\n        end\n        tempfile.closed?.should be_true\n\n        File.extname(tempfile.path).should eq(\".bar\")\n        File.basename(tempfile.path).should start_with(\"foo\")\n\n        File.exists?(tempfile.path).should be_true\n      ensure\n        File.delete(tempfile.path) if tempfile\n      end\n\n      it \"accepts dir argument\" do\n        tempfile = File.tempfile(dir: datapath) do |tempfile|\n        end\n        File.dirname(tempfile.path).should eq(datapath)\n      ensure\n        File.delete(tempfile.path) if tempfile\n      end\n    end\n  end\nend\n\ndescribe Crystal::System::File do\n  describe \".mktemp\" do\n    it \"creates random file name\" do\n      with_tempfile \"random-path\" do |tempdir|\n        Dir.mkdir tempdir\n        fd, path, _ = Crystal::System::File.mktemp(\"A\", \"Z\", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14]))\n        path.should eq Path[tempdir, \"A789abcdeZ\"].to_s\n      ensure\n        IO::FileDescriptor.new(fd).close if fd\n      end\n    end\n\n    it \"retries when file exists\" do\n      with_tempfile \"retry\" do |tempdir|\n        Dir.mkdir tempdir\n        existing_path = Path[tempdir, \"A789abcdeZ\"]\n        File.touch existing_path\n        fd, path, _ = Crystal::System::File.mktemp(\"A\", \"Z\", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]))\n        path.should eq File.join(tempdir, \"AfghijklmZ\")\n      ensure\n        IO::FileDescriptor.new(fd).close if fd\n      end\n    end\n\n    it \"raises when no valid path is found\" do\n      with_tempfile \"random-path\" do |tempdir|\n        Dir.mkdir tempdir\n        File.touch Path[tempdir, \"A789abcdeZ\"]\n        expect_raises(File::AlreadyExistsError, \"Error creating temporary file\") do\n          fd, _, _ = Crystal::System::File.mktemp(\"A\", \"Z\", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14]))\n        ensure\n          IO::FileDescriptor.new(fd).close if fd\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/file_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../support/thread\"\nrequire \"wait_group\"\n\nprivate def it_raises_on_null_byte(operation, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n  it \"errors on #{operation}\", file, line, end_line do\n    expect_raises(ArgumentError, \"String contains null byte\") do\n      block.call\n    end\n  end\nend\n\nprivate def normalize_permissions(permissions, *, directory)\n  {% if flag?(:win32) %}\n    normalized_permissions = 0o444\n    normalized_permissions |= 0o222 if permissions.bits_set?(0o200)\n    normalized_permissions |= 0o111 if directory\n    File::Permissions.new(normalized_permissions)\n  {% else %}\n    File::Permissions.new(permissions)\n  {% end %}\nend\n\n# TODO: Find a better way to execute specs involving file permissions when\n# running as a privileged user. Compiling a program and running a separate\n# process would be a lot of overhead.\nprivate def pending_if_superuser!\n  {% if flag?(:unix) %}\n    if LibC.getuid == 0\n      pending! \"Spec cannot run as superuser\"\n    end\n  {% end %}\nend\n\ndescribe \"File\" do\n  it \"gets path\" do\n    path = datapath(\"test_file.txt\")\n    File.open(path) do |file|\n      file.path.should eq(path)\n    end\n  end\n\n  it \"raises if opening a non-existent file\" do\n    with_tempfile(\"test_nonexistent.txt\") do |file|\n      expect_raises(File::NotFoundError) do\n        File.open(file)\n      end\n    end\n  end\n\n  {% if LibC.has_method?(:mkfifo) && !flag?(:darwin) %}\n    # interpreter doesn't support threads yet (#14287)\n    pending_interpreted \"can read/write fifo file without blocking\" do\n      path = File.tempname(\"chardev\")\n      ret = LibC.mkfifo(path, File::DEFAULT_CREATE_PERMISSIONS)\n      raise RuntimeError.from_errno(\"mkfifo\") unless ret == 0\n\n      rbuf = Bytes.new(5120)\n      wbuf = Bytes.new(5120)\n      Random::Secure.random_bytes(wbuf)\n\n      {% if flag?(:execution_context) %}\n        WaitGroup.wait do |wg|\n          # one fiber may block on open(2) (depends on the event loop) but the\n          # monitor thread will notice and move the scheduler to another thread,\n          # unblocking the other fiber\n          wg.spawn(name: \"fifo:write\") do\n            File.open(path, \"w\") do |writer|\n              64.times { |i| writer.write(wbuf) }\n            end\n          end\n\n          wg.spawn(name: \"fifo:read\") do\n            File.open(path, \"r\") do |reader|\n              64.times { |i| reader.read_fully(rbuf) }\n            end\n          end\n        end\n      {% else %}\n        # open(2) will block when opening a fifo file until another thread or\n        # process also opened the file; so we must explicitly start a thread\n        writer = nil\n        thread = new_thread { writer = File.new(path, \"w\") }\n\n        File.open(path, \"r\") do |reader|\n          WaitGroup.wait do |wg|\n            # opened fifo for read: wait for thread to open for write\n            thread.join\n\n            wg.spawn(name: \"fifo:read\") do\n              64.times { |i| reader.read_fully(rbuf) }\n            end\n\n            wg.spawn(name: \"fifo:write\") do\n              64.times { |i| writer.not_nil!.write(wbuf) }\n              writer.not_nil!.close\n            end\n          end\n        ensure\n          writer.try(&.close)\n        end\n      {% end %}\n\n      rbuf.should eq(wbuf)\n    ensure\n      File.delete(path) if path\n    end\n  {% end %}\n\n  # This test verifies that the workaround for a win32 bug with the O_APPEND\n  # equivalent with OVERLAPPED operations is working as expected.\n  it \"returns the actual position after append\" do\n    with_tempfile(\"delete-file.txt\") do |filename|\n      File.write(filename, \"hello\")\n\n      File.open(filename, \"a\") do |file|\n        file.tell.should eq(0)\n\n        file.write \"12345\".to_slice\n        file.tell.should eq(10)\n\n        file.seek(5, IO::Seek::Set)\n        file.write \"6789\".to_slice\n        file.tell.should eq(14)\n      end\n\n      File.read(filename).should eq(\"hello123456789\")\n    end\n  end\n\n  it \"reads entire file\" do\n    str = File.read datapath(\"test_file.txt\")\n    str.should eq(\"Hello World\\n\" * 20)\n  end\n\n  {% if flag?(:linux) %}\n    it \"reads entire file from proc virtual filesystem\" do\n      str1 = File.open \"/proc/self/cmdline\", &.gets_to_end\n      str2 = File.read \"/proc/self/cmdline\"\n      str2.should_not be_empty\n      str2.should eq(str1)\n    end\n  {% end %}\n\n  it \"reads lines from file\" do\n    lines = File.read_lines datapath(\"test_file.txt\")\n    lines.size.should eq(20)\n    lines.first.should eq(\"Hello World\")\n  end\n\n  it \"reads lines from file with chomp = false\" do\n    lines = File.read_lines datapath(\"test_file.txt\"), chomp: false\n    lines.size.should eq(20)\n    lines.first.should eq(\"Hello World\\n\")\n  end\n\n  it \"reads lines from file with each\" do\n    idx = 0\n    File.each_line(datapath(\"test_file.txt\")) do |line|\n      if idx == 0\n        line.should eq(\"Hello World\")\n      end\n      idx += 1\n    end\n    idx.should eq(20)\n  end\n\n  it \"reads lines from file with each, chomp = false\" do\n    idx = 0\n    File.each_line(datapath(\"test_file.txt\"), chomp: false) do |line|\n      if idx == 0\n        line.should eq(\"Hello World\\n\")\n      end\n      idx += 1\n    end\n    idx.should eq(20)\n  end\n\n  describe \"empty?\" do\n    it \"gives true when file is empty\" do\n      File.empty?(datapath(\"blank_test_file.txt\")).should be_true\n    end\n\n    it \"gives false when file is not empty\" do\n      File.empty?(datapath(\"test_file.txt\")).should be_false\n    end\n\n    it \"raises an error when the file does not exist\" do\n      filename = datapath(\"non_existing_file.txt\")\n      expect_raises(File::NotFoundError, \"Unable to get file info: '#{filename.inspect_unquoted}'\") do\n        File.empty?(filename)\n      end\n    end\n\n    # TODO: do we even want this?\n    it \"raises an error when a component of the path is a file\" do\n      expect_raises(File::Error, \"Unable to get file info: '#{datapath(\"test_file.txt\", \"\").inspect_unquoted}'\") do\n        File.empty?(datapath(\"test_file.txt\", \"\"))\n      end\n    end\n  end\n\n  describe \"exists?\" do\n    it \"gives true\" do\n      File.exists?(datapath(\"test_file.txt\")).should be_true\n    end\n\n    it \"gives false\" do\n      File.exists?(datapath(\"non_existing_file.txt\")).should be_false\n    end\n\n    it \"gives false when a component of the path is a file\" do\n      File.exists?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n    end\n\n    it \"follows symlinks\" do\n      with_tempfile(\"good_symlink.txt\", \"bad_symlink.txt\") do |good_path, bad_path|\n        File.symlink(File.expand_path(datapath(\"test_file.txt\")), good_path)\n        File.symlink(File.expand_path(datapath(\"non_existing_file.txt\")), bad_path)\n\n        File.exists?(good_path).should be_true\n        File.exists?(bad_path).should be_false\n      end\n    end\n\n    it \"gives true for null file (#15019)\" do\n      File.exists?(File::NULL).should be_true\n    end\n  end\n\n  describe \"file?\" do\n    it \"gives true\" do\n      File.file?(datapath(\"test_file.txt\")).should be_true\n    end\n\n    it \"gives false with dir\" do\n      File.file?(datapath(\"dir\")).should be_false\n    end\n\n    it \"gives false when the file doesn't exist\" do\n      File.file?(datapath(\"non_existing_file.txt\")).should be_false\n    end\n\n    it \"gives false when a component of the path is a file\" do\n      File.file?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n    end\n  end\n\n  describe \"directory?\" do\n    it \"gives true\" do\n      File.directory?(datapath).should be_true\n    end\n\n    it \"gives false\" do\n      File.directory?(datapath(\"test_file.txt\")).should be_false\n    end\n\n    it \"gives false when the directory doesn't exist\" do\n      File.directory?(datapath(\"non_existing\")).should be_false\n    end\n\n    it \"gives false when a component of the path is a file\" do\n      File.directory?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n    end\n  end\n\n  # hard links are practically unavailable on Android\n  {% unless flag?(:android) %}\n    describe \"link\" do\n      it \"creates a hard link\" do\n        with_tempfile(\"hard_link_source.txt\", \"hard_link_target.txt\") do |in_path, out_path|\n          File.write(in_path, \"\")\n          File.link(in_path, out_path)\n          File.exists?(out_path).should be_true\n          File.symlink?(out_path).should be_false\n          File.same?(in_path, out_path).should be_true\n        end\n      end\n    end\n  {% end %}\n\n  describe \"same?\" do\n    it \"compares following symlinks only if requested\" do\n      file = datapath(\"test_file.txt\")\n      other = datapath(\"test_file.ini\")\n\n      with_tempfile(\"test_file_symlink.txt\") do |symlink|\n        File.symlink(File.realpath(file), symlink)\n\n        File.same?(file, symlink).should be_false\n        File.same?(file, symlink, follow_symlinks: true).should be_true\n        File.same?(file, symlink, follow_symlinks: false).should be_false\n        File.same?(file, other).should be_false\n      end\n    end\n  end\n\n  describe \"symlink\" do\n    it \"creates a symbolic link\" do\n      in_path = datapath(\"test_file.txt\")\n      with_tempfile(\"test_file_link.txt\") do |out_path|\n        File.symlink(File.realpath(in_path), out_path)\n        File.symlink?(out_path).should be_true\n        File.same?(in_path, out_path, follow_symlinks: true).should be_true\n      end\n    end\n\n    it \"works if destination contains forward slashes (#14520)\" do\n      with_tempfile(\"test_slash_dest.txt\", \"test_slash_link.txt\") do |dest_path, link_path|\n        File.write(dest_path, \"hello\")\n        File.symlink(\"./test_slash_dest.txt\", link_path)\n        File.same?(dest_path, link_path, follow_symlinks: true).should be_true\n        File.read(link_path).should eq(\"hello\")\n      end\n    end\n  end\n\n  describe \"symlink?\" do\n    it \"gives false\" do\n      File.symlink?(datapath(\"test_file.txt\")).should be_false\n      File.symlink?(datapath(\"unknown_file.txt\")).should be_false\n    end\n\n    it \"gives false when the symlink doesn't exist\" do\n      File.symlink?(datapath(\"non_existing_file.txt\")).should be_false\n    end\n\n    it \"gives false when a component of the path is a file\" do\n      File.symlink?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n    end\n  end\n\n  describe \".readlink\" do\n    it \"reads link\" do\n      File.readlink(datapath(\"symlink.txt\")).should eq \"test_file.txt\"\n    end\n\n    it \"raises when not a link\" do\n      expect_raises File::Error, \"Cannot read link: '#{datapath(\"test_file.txt\").inspect_unquoted}'\" do\n        File.readlink(datapath(\"test_file.txt\"))\n      end\n    end\n\n    it \"raises when non-existent\" do\n      expect_raises File::NotFoundError, \"Cannot read link: '#{datapath(\"nonexistent.txt\").inspect_unquoted}'\" do\n        File.readlink(datapath(\"nonexistent.txt\"))\n      end\n    end\n\n    it \"returns non-existent target\" do\n      with_tempfile(\"target-nonexistent\") do |path|\n        Dir.mkdir_p(path)\n        Dir.cd(path) do\n          File.symlink(\"nonexistent.txt\", \"symlink.txt\")\n\n          File.readlink(\"symlink.txt\").should eq \"nonexistent.txt\"\n        end\n      end\n    end\n\n    it \"raises when inaccessible\" do\n      # Crystal does not expose ways to make a file unreadable on Windows\n      pending! if {{ flag?(:win32) }}\n      pending_if_superuser!\n\n      with_tempfile(\"readlink-inaccessible\") do |path|\n        Dir.mkdir_p(path)\n        symlink = File.join(path, \"symlink.txt\")\n        File.symlink(\"nonexistent.txt\", symlink)\n        File.chmod(path, File::Permissions::None)\n\n        expect_raises File::AccessDeniedError, \"Cannot read link: '#{symlink.inspect_unquoted}'\" do\n          File.readlink(symlink)\n        end\n      end\n    end\n  end\n\n  describe \".readlink?\" do\n    it \"reads link\" do\n      File.readlink?(datapath(\"symlink.txt\")).should eq \"test_file.txt\"\n    end\n\n    it \"returns nil when not a link\" do\n      File.readlink?(datapath(\"test_file.txt\")).should be_nil\n    end\n\n    it \"returns nil when non-existent\" do\n      File.readlink?(datapath(\"nonexistent.txt\")).should be_nil\n    end\n\n    it \"raises when target non-existent\" do\n      with_tempfile(\"target-nonexistent2\") do |path|\n        Dir.mkdir_p(path)\n        Dir.cd(path) do\n          File.symlink(\"nonexistent.txt\", \"symlink.txt\")\n\n          File.readlink?(\"symlink.txt\").should eq \"nonexistent.txt\"\n        end\n      end\n    end\n\n    it \"raises when inaccessible\" do\n      # Crystal does not expose ways to make a file unreadable on Windows\n      pending! if {{ flag?(:win32) }}\n      pending_if_superuser!\n\n      with_tempfile(\"readlink-inaccessible2\") do |path|\n        Dir.mkdir_p(path)\n        symlink = File.join(path, \"symlink.txt\")\n        File.symlink(\"nonexistent.txt\", symlink)\n        File.chmod(path, File::Permissions::None)\n\n        expect_raises File::AccessDeniedError, \"Cannot read link: '#{symlink.inspect_unquoted}'\" do\n          File.readlink?(symlink)\n        end\n      end\n    end\n  end\n\n  it \"gets dirname\" do\n    File.dirname(\"/Users/foo/bar.cr\").should eq(\"/Users/foo\")\n    File.dirname(\"foo\").should eq(\".\")\n    File.dirname(\"\").should eq(\".\")\n    File.dirname(\"/τελεία/łódź\").should eq(\"/τελεία\")\n  end\n\n  it \"gets basename\" do\n    File.basename(\"/foo/bar/baz.cr\").should eq(\"baz.cr\")\n    File.basename(\"/foo/\").should eq(\"foo\")\n    File.basename(\"foo\").should eq(\"foo\")\n    File.basename(\"\").should eq(\"\")\n    File.basename(\"/\").should eq(\"/\")\n  end\n\n  it \"gets basename removing suffix\" do\n    File.basename(\"/foo/bar/baz.cr\", \".cr\").should eq(\"baz\")\n  end\n\n  it \"gets extname\" do\n    File.extname(\"/foo/bar/a.cr\").should eq(\".cr\")\n    File.extname(\"/foo/bar/baz.cr\").should eq(\".cr\")\n    File.extname(\"/foo/bar/baz.cr.cz\").should eq(\".cz\")\n    File.extname(\"/foo/bar/.profile\").should eq(\"\")\n    File.extname(\"/foo/bar/.profile.sh\").should eq(\".sh\")\n    File.extname(\"/foo/bar/foo.\").should eq(\"\")\n    File.extname(\"/foo.bar/baz\").should eq(\"\")\n    File.extname(\"a.cr\").should eq(\".cr\")\n    File.extname(\"test.cr\").should eq(\".cr\")\n    File.extname(\"test.cr.cz\").should eq(\".cz\")\n    File.extname(\".test\").should eq(\"\")\n    File.extname(\".test.cr\").should eq(\".cr\")\n    File.extname(\".test.cr.cz\").should eq(\".cz\")\n    File.extname(\"test\").should eq(\"\")\n    File.extname(\"test.\").should eq(\"\")\n    File.extname(\"\").should eq(\"\")\n  end\n\n  # There are more detailed specs for `Path#join` in path_spec.cr\n  it \"constructs a path from parts\" do\n    {% if flag?(:win32) %}\n      File.join([\"///foo\", \"bar\"]).should eq(\"///foo\\\\bar\")\n      File.join([\"///foo\", \"//bar\"]).should eq(\"///foo//bar\")\n      File.join([\"/foo/\", \"/bar\"]).should eq(\"/foo/bar\")\n      File.join([\"foo\", \"bar\", \"baz\"]).should eq(\"foo\\\\bar\\\\baz\")\n      File.join([\"foo\", \"//bar//\", \"baz///\"]).should eq(\"foo//bar//baz///\")\n      File.join([\"/foo/\", \"/bar/\", \"/baz/\"]).should eq(\"/foo/bar/baz/\")\n      File.join([\"\", \"foo\"]).should eq(\"\\\\foo\")\n      File.join([\"foo\", \"\"]).should eq(\"foo\\\\\")\n      File.join([\"\", \"\", \"foo\"]).should eq(\"\\\\foo\")\n      File.join([\"foo\", \"\", \"bar\"]).should eq(\"foo\\\\bar\")\n      File.join([\"foo\", \"\", \"\", \"bar\"]).should eq(\"foo\\\\bar\")\n      File.join([\"foo\", \"/\", \"bar\"]).should eq(\"foo/bar\")\n      File.join([\"foo\", \"/\", \"/\", \"bar\"]).should eq(\"foo/bar\")\n      File.join([\"/\", \"/foo\", \"/\", \"bar/\", \"/\"]).should eq(\"/foo/bar/\")\n      File.join([\"foo\"]).should eq(\"foo\")\n      File.join(\"foo\").should eq(\"foo\")\n    {% else %}\n      File.join([\"///foo\", \"bar\"]).should eq(\"///foo/bar\")\n      File.join([\"///foo\", \"//bar\"]).should eq(\"///foo//bar\")\n      File.join([\"/foo/\", \"/bar\"]).should eq(\"/foo/bar\")\n      File.join([\"foo\", \"bar\", \"baz\"]).should eq(\"foo/bar/baz\")\n      File.join([\"foo\", \"//bar//\", \"baz///\"]).should eq(\"foo//bar//baz///\")\n      File.join([\"/foo/\", \"/bar/\", \"/baz/\"]).should eq(\"/foo/bar/baz/\")\n      File.join([\"\", \"foo\"]).should eq(\"/foo\")\n      File.join([\"foo\", \"\"]).should eq(\"foo/\")\n      File.join([\"\", \"\", \"foo\"]).should eq(\"/foo\")\n      File.join([\"foo\", \"\", \"bar\"]).should eq(\"foo/bar\")\n      File.join([\"foo\", \"\", \"\", \"bar\"]).should eq(\"foo/bar\")\n      File.join([\"foo\", \"/\", \"bar\"]).should eq(\"foo/bar\")\n      File.join([\"foo\", \"/\", \"/\", \"bar\"]).should eq(\"foo/bar\")\n      File.join([\"/\", \"/foo\", \"/\", \"bar/\", \"/\"]).should eq(\"/foo/bar/\")\n      File.join([\"foo\"]).should eq(\"foo\")\n      File.join(\"foo\").should eq(\"foo\")\n    {% end %}\n  end\n\n  it \"chown\" do\n    # changing owners requires special privileges, so we test that method calls do compile\n    typeof(File.chown(\".\"))\n    typeof(File.chown(\".\", uid: 1001, gid: 100, follow_symlinks: true))\n\n    File.open(File::NULL, \"w\") do |file|\n      typeof(file.chown)\n      typeof(file.chown(uid: 1001, gid: 100))\n    end\n  end\n\n  describe \"chmod\" do\n    it \"changes file permissions with class method\" do\n      path = datapath(\"chmod.txt\")\n      begin\n        File.write(path, \"\")\n        File.chmod(path, 0o775)\n        File.info(path).permissions.should eq(normalize_permissions(0o775, directory: false))\n      ensure\n        File.delete?(path)\n      end\n    end\n\n    it \"changes file permissions with instance method\" do\n      path = datapath(\"chmod.txt\")\n      begin\n        File.open(path, \"w\") do |file|\n          file.chmod(0o775)\n        end\n        File.info(path).permissions.should eq(normalize_permissions(0o775, directory: false))\n      ensure\n        File.delete(path) if File.exists?(path)\n      end\n    end\n\n    it \"changes dir permissions\" do\n      path = datapath(\"chmod\")\n      begin\n        Dir.mkdir(path, 0o775)\n        File.chmod(path, 0o664)\n        File.info(path).permissions.should eq(normalize_permissions(0o664, directory: true))\n      ensure\n        Dir.delete?(path)\n      end\n    end\n\n    it \"can take File::Permissions\" do\n      path = datapath(\"chmod.txt\")\n      begin\n        File.write(path, \"\")\n        File.chmod(path, File::Permissions.flags(OwnerAll, GroupAll, OtherExecute, OtherRead))\n        File.info(path).permissions.should eq(normalize_permissions(0o775, directory: false))\n      ensure\n        File.delete?(path)\n      end\n    end\n\n    it \"follows symlinks\" do\n      with_tempfile(\"chmod-destination.txt\", \"chmod-source.txt\") do |source_path, target_path|\n        File.write(source_path, \"\")\n\n        File.symlink(File.realpath(source_path), target_path)\n        File.symlink?(target_path).should be_true\n\n        File.chmod(source_path, 0o664)\n        File.chmod(target_path, 0o444)\n\n        File.info(source_path).permissions.should eq(normalize_permissions(0o444, directory: false))\n      end\n    end\n\n    it \"raises when destination doesn't exist\" do\n      expect_raises(File::NotFoundError, \"Error changing permissions: '#{datapath(\"unknown_chmod_path.txt\").inspect_unquoted}'\") do\n        File.chmod(datapath(\"unknown_chmod_path.txt\"), 0o664)\n      end\n    end\n  end\n\n  long_path = \"a\" * 1000\n  describe \".info\" do\n    it \"raises for too long pathname\" do\n      expect_raises(File::NotFoundError, /Unable to get file info: '#{long_path}': (File ?name too long|The system cannot find the path specified)/) do\n        File.info(long_path)\n      end\n    end\n\n    it \"raises for invalid pathname\" do\n      expect_raises(File::NotFoundError, /Unable to get file info: '': (No such file or directory|The system cannot find the path specified)/) do\n        File.info(\"\")\n      end\n    end\n\n    it \"raises for invalid pathname\" do\n      expect_raises(File::NotFoundError, /Unable to get file info: '<': (No such file or directory|The filename, directory name, or volume label syntax is incorrect)/) do\n        File.info(\"<\")\n      end\n    end\n  end\n\n  describe \".info?\" do\n    it \"returns nil for too long pathname\" do\n      File.info?(long_path).should be_nil\n    end\n\n    it \"returns nil for invalid pathname\" do\n      File.info?(\"\").should be_nil\n    end\n\n    it \"returns nil for invalid pathname\" do\n      File.info?(\"<\").should be_nil\n    end\n  end\n\n  describe \"File::Info\" do\n    it \"gets for this file\" do\n      info = File.info(datapath(\"test_file.txt\"))\n      info.type.should eq(File::Type::File)\n    end\n\n    it \"gets for this directory\" do\n      info = File.info(datapath)\n      info.type.should eq(File::Type::Directory)\n    end\n\n    it \"gets for a character device\" do\n      info = File.info(File::NULL)\n      info.type.should eq(File::Type::CharacterDevice)\n    end\n\n    it \"gets for a symlink\" do\n      file_path = File.expand_path(datapath(\"test_file.txt\"))\n      with_tempfile(\"symlink.txt\") do |symlink_path|\n        File.symlink(file_path, symlink_path)\n        info = File.info(symlink_path, follow_symlinks: false)\n        info.type.should eq(File::Type::Symlink)\n        info = File.info(symlink_path, follow_symlinks: true)\n        info.type.should_not eq(File::Type::Symlink)\n      end\n    end\n\n    it \"gets for open file\" do\n      File.open(datapath(\"test_file.txt\"), \"r\") do |file|\n        info = file.info\n        info.type.should eq(File::Type::File)\n      end\n    end\n\n    it \"gets for pipe\" do\n      IO.pipe do |r, w|\n        r.info.type.should eq(File::Type::Pipe)\n        w.info.type.should eq(File::Type::Pipe)\n      end\n    end\n\n    it \"gets for non-existent file and raises\" do\n      expect_raises(File::NotFoundError, \"Unable to get file info: 'non-existent'\") do\n        File.info(\"non-existent\")\n      end\n    end\n\n    it \"gets mtime for new file\" do\n      with_tempfile(\"mtime\") do |path|\n        File.touch(path)\n        File.open(path) do |file|\n          file.info.modification_time.should be_close(Time.utc, 1.seconds)\n        end\n        File.info(path).modification_time.should be_close(Time.utc, 1.seconds)\n      end\n    end\n\n    it \"tests equal for the same file\" do\n      File.info(datapath(\"test_file.txt\")).should eq(File.info(datapath(\"test_file.txt\")))\n    end\n\n    it \"tests equal for the same directory\" do\n      File.info(datapath(\"dir\")).should eq(File.info(datapath(\"dir\")))\n    end\n\n    it \"tests unequal for different files\" do\n      File.info(datapath(\"test_file.txt\")).should_not eq(File.info(datapath(\"test_file.ini\")))\n    end\n\n    it \"tests unequal for file and directory\" do\n      File.info(datapath(\"dir\")).should_not eq(File.info(datapath(\"test_file.txt\")))\n    end\n\n    describe \".executable?\" do\n      it \"gives true\" do\n        crystal = Process.executable_path || pending! \"Unable to locate compiler executable\"\n        File::Info.executable?(crystal).should be_true\n        File.executable?(crystal).should be_true # deprecated\n      end\n\n      it \"gives false\" do\n        File::Info.executable?(datapath(\"test_file.txt\")).should be_false\n      end\n\n      it \"gives false when the file doesn't exist\" do\n        File::Info.executable?(datapath(\"non_existing_file.txt\")).should be_false\n      end\n\n      it \"gives false when a component of the path is a file\" do\n        File::Info.executable?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n      end\n\n      it \"follows symlinks\" do\n        with_tempfile(\"good_symlink_x.txt\", \"bad_symlink_x.txt\") do |good_path, bad_path|\n          crystal = Process.executable_path || pending! \"Unable to locate compiler executable\"\n          File.symlink(File.expand_path(crystal), good_path)\n          File.symlink(File.expand_path(datapath(\"non_existing_file.txt\")), bad_path)\n\n          File::Info.executable?(good_path).should be_true\n          File::Info.executable?(bad_path).should be_false\n        end\n      end\n    end\n\n    describe \".readable?\" do\n      it \"gives true\" do\n        File::Info.readable?(datapath(\"test_file.txt\")).should be_true\n        File.readable?(datapath(\"test_file.txt\")).should be_true # deprecated\n      end\n\n      it \"gives false when the file doesn't exist\" do\n        File::Info.readable?(datapath(\"non_existing_file.txt\")).should be_false\n      end\n\n      it \"gives false when a component of the path is a file\" do\n        File::Info.readable?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n      end\n\n      # win32 doesn't have a way to make files unreadable via chmod\n      {% unless flag?(:win32) %}\n        it \"gives false when the file has no read permissions\" do\n          with_tempfile(\"unreadable.txt\") do |path|\n            File.write(path, \"\")\n            File.chmod(path, 0o222)\n            pending_if_superuser!\n            File::Info.readable?(path).should be_false\n          end\n        end\n\n        it \"gives false when the file has no permissions\" do\n          with_tempfile(\"inaccessible.txt\") do |path|\n            File.write(path, \"\")\n            File.chmod(path, 0o000)\n            pending_if_superuser!\n            File::Info.readable?(path).should be_false\n          end\n        end\n\n        it \"follows symlinks\" do\n          with_tempfile(\"good_symlink_r.txt\", \"bad_symlink_r.txt\", \"unreadable.txt\") do |good_path, bad_path, unreadable|\n            File.write(unreadable, \"\")\n            File.chmod(unreadable, 0o222)\n            pending_if_superuser!\n\n            File.symlink(File.expand_path(datapath(\"test_file.txt\")), good_path)\n            File.symlink(File.expand_path(unreadable), bad_path)\n\n            File::Info.readable?(good_path).should be_true\n            File::Info.readable?(bad_path).should be_false\n          end\n        end\n      {% end %}\n\n      it \"gives false when the symbolic link destination doesn't exist\" do\n        with_tempfile(\"missing_symlink_r.txt\") do |missing_path|\n          File.symlink(File.expand_path(datapath(\"non_existing_file.txt\")), missing_path)\n          File::Info.readable?(missing_path).should be_false\n        end\n      end\n    end\n\n    describe \".writable?\" do\n      it \"gives true\" do\n        File::Info.writable?(datapath(\"test_file.txt\")).should be_true\n        File.writable?(datapath(\"test_file.txt\")).should be_true # deprecated\n      end\n\n      it \"gives false when the file doesn't exist\" do\n        File::Info.writable?(datapath(\"non_existing_file.txt\")).should be_false\n      end\n\n      it \"gives false when a component of the path is a file\" do\n        File::Info.writable?(datapath(\"dir\", \"test_file.txt\", \"\")).should be_false\n      end\n\n      it \"gives false when the file has no write permissions\" do\n        with_tempfile(\"readonly.txt\") do |path|\n          File.write(path, \"\")\n          File.chmod(path, 0o444)\n          pending_if_superuser!\n          File::Info.writable?(path).should be_false\n        end\n      end\n\n      it \"follows symlinks\" do\n        with_tempfile(\"good_symlink_w.txt\", \"bad_symlink_w.txt\", \"readonly.txt\") do |good_path, bad_path, readonly|\n          File.write(readonly, \"\")\n          File.chmod(readonly, 0o444)\n          pending_if_superuser!\n\n          File.symlink(File.expand_path(datapath(\"test_file.txt\")), good_path)\n          File.symlink(File.expand_path(readonly), bad_path)\n\n          File::Info.writable?(good_path).should be_true\n          File::Info.writable?(bad_path).should be_false\n        end\n      end\n\n      it \"gives false when the symbolic link destination doesn't exist\" do\n        with_tempfile(\"missing_symlink_w.txt\") do |missing_path|\n          File.symlink(File.expand_path(datapath(\"non_existing_file.txt\")), missing_path)\n          File::Info.writable?(missing_path).should be_false\n        end\n      end\n    end\n  end\n\n  describe \"size\" do\n    it { File.size(datapath(\"test_file.txt\")).should eq(240) }\n    it do\n      File.open(datapath(\"test_file.txt\"), \"r\") do |file|\n        file.size.should eq(240)\n      end\n    end\n\n    it \"raises an error when the file does not exist\" do\n      filename = datapath(\"non_existing_file.txt\")\n      expect_raises(File::NotFoundError, \"Unable to get file info: '#{filename.inspect_unquoted}'\") do\n        File.size(filename)\n      end\n    end\n\n    # TODO: do we even want this?\n    it \"raises an error when a component of the path is a file\" do\n      expect_raises(File::Error, \"Unable to get file info: '#{datapath(\"test_file.txt\", \"\").inspect_unquoted}'\") do\n        File.size(datapath(\"test_file.txt\", \"\"))\n      end\n    end\n  end\n\n  describe \".delete\" do\n    it \"deletes a file\" do\n      with_tempfile(\"delete-file.txt\") do |filename|\n        File.open(filename, \"w\") { }\n        File.exists?(filename).should be_true\n        File.delete(filename)\n        File.exists?(filename).should be_false\n      end\n    end\n\n    it \"deletes an open file\" do\n      with_tempfile(\"delete-file.txt\") do |filename|\n        file = File.open filename, \"w\"\n        File.exists?(file.path).should be_true\n        file.delete\n        File.exists?(file.path).should be_false\n      end\n    end\n\n    it \"deletes a read-only file\" do\n      with_tempfile(\"delete-file-dir\") do |path|\n        Dir.mkdir(path)\n        File.chmod(path, 0o755)\n\n        filename = File.join(path, \"foo\")\n        File.open(filename, \"w\") { }\n        File.exists?(filename).should be_true\n        File.chmod(filename, 0o000)\n        File.delete(filename)\n        File.exists?(filename).should be_false\n      end\n    end\n\n    it \"deletes? a file\" do\n      with_tempfile(\"delete-file.txt\") do |filename|\n        File.open(filename, \"w\") { }\n        File.exists?(filename).should be_true\n        File.delete?(filename).should be_true\n        File.exists?(filename).should be_false\n        File.delete?(filename).should be_false\n      end\n    end\n\n    it \"raises when file doesn't exist\" do\n      with_tempfile(\"nonexistent_file.txt\") do |path|\n        expect_raises(File::NotFoundError, \"Error deleting file: '#{path.inspect_unquoted}'\") do\n          File.delete(path)\n        end\n      end\n    end\n\n    it \"deletes a symlink directory\" do\n      with_tempfile(\"delete-target-directory\", \"delete-symlink-directory\") do |target_path, symlink_path|\n        Dir.mkdir(target_path)\n        File.symlink(target_path, symlink_path)\n        File.delete(symlink_path)\n      end\n    end\n  end\n\n  describe \"rename\" do\n    it \"renames a file\" do\n      with_tempfile(\"rename-source.txt\", \"rename-target.txt\") do |source_path, target_path|\n        File.write(source_path, \"hello\")\n        File.rename(source_path, target_path)\n        File.exists?(source_path).should be_false\n        File.exists?(target_path).should be_true\n        File.read(target_path).strip.should eq(\"hello\")\n        File.delete(target_path)\n      end\n    end\n\n    it \"replaces a file\" do\n      with_tempfile(\"rename-source.txt\", \"rename-target.txt\") do |source_path, target_path|\n        File.write(source_path, \"foo\")\n        File.write(target_path, \"bar\")\n        File.rename(source_path, target_path)\n        File.exists?(source_path).should be_false\n        File.read(target_path).strip.should eq(\"foo\")\n        File.delete(target_path)\n      end\n    end\n\n    it \"raises if old file doesn't exist\" do\n      with_tempfile(\"rename-fail-source.txt\", \"rename-fail-target.txt\") do |source_path, target_path|\n        expect_raises(File::NotFoundError, \"Error renaming file: '#{source_path.inspect_unquoted}' -> '#{target_path.inspect_unquoted}'\") do\n          File.rename(source_path, target_path)\n        end\n      end\n    end\n\n    it \"renames a File instance\" do\n      with_tempfile(\"rename-source.txt\", \"rename-target.txt\") do |source_path, target_path|\n        f = File.new(source_path, \"w\")\n        f.rename target_path\n        f.path.should eq target_path\n        File.exists?(source_path).should be_false\n        File.exists?(target_path).should be_true\n      end\n    end\n  end\n\n  # There are more detailed specs for `Path#expand` in path_spec.cr\n  describe \".expand_path\" do\n    it \"converts a pathname to an absolute pathname\" do\n      File.expand_path(\"a/b\").should eq(Path.new(\"a/b\").expand(Dir.current).to_s)\n      File.expand_path(\"a/b\", \"c/d\").should eq(Path.new(\"a/b\").expand(\"c/d\").to_s)\n      File.expand_path(\"~/b\", home: \"c/d\").should eq(Path.new(\"~/b\").expand(Dir.current, home: \"c/d\").to_s)\n      File.expand_path(\"~/b\", \"c/d\", home: false).should eq(Path.new(\"~/b\").expand(\"c/d\", home: false).to_s)\n\n      File.expand_path(Path.new(\"a/b\")).should eq(Path.new(\"a/b\").expand(Dir.current).to_s)\n    end\n  end\n\n  describe \"#realpath\" do\n    it \"expands paths for normal files\" do\n      path = File.join(File.realpath(\".\"), datapath(\"dir\"))\n      File.realpath(path).should eq(path)\n      File.realpath(File.join(path, \"..\")).should eq(File.dirname(path))\n    end\n\n    it \"raises if file doesn't exist\" do\n      path = datapath(\"doesnotexist\")\n      expect_raises(File::NotFoundError, \"Error resolving real path: '#{path.inspect_unquoted}'\") do\n        File.realpath(path)\n      end\n    end\n\n    it \"expands paths of symlinks\" do\n      file_path = File.expand_path(datapath(\"test_file.txt\"))\n      with_tempfile(\"symlink.txt\") do |symlink_path|\n        File.symlink(file_path, symlink_path)\n        real_symlink_path = File.realpath(symlink_path)\n        real_file_path = File.realpath(file_path)\n        real_symlink_path.should eq(real_file_path)\n      end\n    end\n\n    it \"expands multiple layers of symlinks\" do\n      file_path = File.expand_path(datapath(\"test_file.txt\"))\n      with_tempfile(\"symlink1.txt\") do |symlink_path1|\n        with_tempfile(\"symlink2.txt\") do |symlink_path2|\n          File.symlink(file_path, symlink_path1)\n          File.symlink(symlink_path1, symlink_path2)\n          real_symlink_path = File.realpath(symlink_path2)\n          real_file_path = File.realpath(file_path)\n          real_symlink_path.should eq(real_file_path)\n        end\n      end\n    end\n  end\n\n  describe \"write\" do\n    it \"can write to a file\" do\n      with_tempfile(\"write.txt\") do |path|\n        File.write(path, \"hello\")\n        File.read(path).should eq(\"hello\")\n      end\n    end\n\n    it \"writes bytes\" do\n      with_tempfile(\"write-bytes.txt\") do |path|\n        File.write(path, \"hello\".to_slice)\n        File.read(path).should eq(\"hello\")\n      end\n    end\n\n    it \"writes io\" do\n      with_tempfile(\"write-io.txt\") do |path|\n        File.open(datapath(\"test_file.txt\")) do |file|\n          File.write(path, file)\n        end\n        File.read(path).should eq(File.read(datapath(\"test_file.txt\")))\n      end\n    end\n\n    it \"raises if trying to write to a file not opened for writing\" do\n      with_tempfile(\"write-fails.txt\") do |path|\n        File.write(path, \"hello\")\n        expect_raises(IO::Error, \"File not open for writing\") do\n          File.open(path) { |file| file << \"hello\" }\n        end\n      end\n    end\n\n    it \"can create a new file in append mode\" do\n      with_tempfile(\"append-create.txt\") do |path|\n        File.write(path, \"hello\", mode: \"a\")\n        File.read(path).should eq(\"hello\")\n      end\n    end\n\n    it \"can append to an existing file\" do\n      with_tempfile(\"append-existing.txt\") do |path|\n        File.write(path, \"hello\")\n        File.read(path).should eq(\"hello\")\n        File.write(path, \" world\", mode: \"a\")\n        File.read(path).should eq(\"hello world\")\n      end\n    end\n  end\n\n  it \"does to_s and inspect\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      file.to_s.should eq(\"#<File:0x#{file.object_id.to_s(16)}>\")\n      file.inspect.should eq(\"#<File:#{datapath(\"test_file.txt\")}>\")\n    end\n  end\n\n  describe \"close\" do\n    it \"is not closed when opening\" do\n      File.open(datapath(\"test_file.txt\")) do |file|\n        file.closed?.should be_false\n      end\n    end\n\n    it \"is closed when closed\" do\n      file = File.new(datapath(\"test_file.txt\"))\n      file.close\n      file.closed?.should be_true\n    end\n\n    it \"should not raise when closing twice\" do\n      file = File.new(datapath(\"test_file.txt\"))\n      file.close\n      file.close\n    end\n\n    it \"does to_s when closed\" do\n      file = File.new(datapath(\"test_file.txt\"))\n      file.close\n      file.to_s.should eq(\"#<File:0x#{file.object_id.to_s(16)}>\")\n      file.inspect.should eq(\"#<File:#{datapath(\"test_file.txt\")} (closed)>\")\n    end\n  end\n\n  it \"supports the `b` mode flag\" do\n    with_tempfile(\"b-mode-flag.txt\") do |path|\n      File.open(path, \"wb\") do |f|\n        f.write(Bytes[1, 3, 6, 10])\n      end\n      File.open(path, \"rb\") do |f|\n        bytes = Bytes.new(4)\n        f.read(bytes)\n        bytes.should eq(Bytes[1, 3, 6, 10])\n      end\n      File.open(path, \"ab\") do |f|\n        f.size.should eq(4)\n      end\n\n      File.open(path, \"r+b\") do |f|\n        bytes = Bytes.new(4)\n        f.read(bytes)\n        bytes.should eq(Bytes[1, 3, 6, 10])\n        f.seek(0)\n        f.write(Bytes[1, 3, 6, 10])\n      end\n      File.open(path, \"a+b\") do |f|\n        f.write(Bytes[13, 13, 10])\n        f.flush\n        f.seek(0)\n        bytes = Bytes.new(7)\n        f.read(bytes)\n        bytes.should eq(Bytes[1, 3, 6, 10, 13, 13, 10])\n      end\n      File.open(path, \"w+b\") do |f|\n        f.size.should eq(0)\n      end\n\n      File.open(path, \"rb+\") { }\n      File.open(path, \"wb+\") { }\n      File.open(path, \"ab+\") { }\n    end\n  end\n\n  it \"opens with perm (int)\" do\n    with_tempfile(\"write_with_perm-int.txt\") do |path|\n      perm = 0o600\n      File.open(path, \"w\", perm) do |file|\n        file.info.permissions.should eq(normalize_permissions(perm, directory: false))\n      end\n    end\n  end\n\n  it \"opens with perm (File::Permissions)\" do\n    with_tempfile(\"write_with_perm.txt\") do |path|\n      perm = File::Permissions.flags(OwnerRead, OwnerWrite)\n      File.open(path, \"w\", perm) do |file|\n        file.info.permissions.should eq(normalize_permissions(perm.value, directory: false))\n      end\n    end\n  end\n\n  it \"clears the read buffer after a seek\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      file.gets(5).should eq(\"Hello\")\n      file.seek(1)\n      file.gets(4).should eq(\"ello\")\n    end\n  end\n\n  it \"seeks from the current position\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      file.gets(5)\n      file.seek(-4, IO::Seek::Current)\n      file.tell.should eq(1)\n    end\n  end\n\n  it \"raises if invoking seek with a closed file\" do\n    file = File.new(datapath(\"test_file.txt\"))\n    file.close\n    expect_raises(IO::Error, \"Closed stream\") { file.seek(1) }\n  end\n\n  it \"returns the current read position with tell\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      file.tell.should eq(0)\n      file.gets(5).should eq(\"Hello\")\n      file.tell.should eq(5)\n      file.sync = true\n      file.tell.should eq(5)\n    end\n  end\n\n  it \"returns the current write position with tell\" do\n    with_tempfile(\"delete-file.txt\") do |filename|\n      File.open(filename, \"w\") do |file|\n        file.tell.should eq(0)\n        file.write \"12345\".to_slice\n        file.tell.should eq(5)\n        file.sync = true\n        file.tell.should eq(5)\n      end\n    end\n  end\n\n  it \"returns the actual position with tell after append\" do\n    with_tempfile(\"delete-file.txt\") do |filename|\n      File.write(filename, \"hello\")\n      File.open(filename, \"a\") do |file|\n        file.write \"12345\".to_slice\n        file.tell.should eq(10)\n      end\n    end\n  end\n\n  it \"does not overwrite existing content in append mode\" do\n    with_tempfile(\"append-override.txt\") do |filename|\n      File.write(filename, \"0123456789\")\n\n      File.open(filename, \"a\") do |file|\n        file.seek(5)\n        file.write \"abcd\".to_slice\n      end\n\n      File.read(filename).should eq \"0123456789abcd\"\n    end\n  end\n\n  it \"truncates file opened in append mode (#14702)\" do\n    with_tempfile(\"truncate-append.txt\") do |path|\n      File.write(path, \"0123456789\")\n\n      File.open(path, \"a\") do |file|\n        file.truncate(4)\n      end\n\n      File.read(path).should eq \"0123\"\n    end\n  end\n\n  it \"locks file opened in append mode (#14702)\" do\n    with_tempfile(\"truncate-append.txt\") do |path|\n      File.write(path, \"0123456789\")\n\n      File.open(path, \"a\") do |file|\n        file.flock_exclusive { }\n      end\n    end\n  end\n\n  it \"can navigate with pos\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      file.pos = 3\n      file.gets(2).should eq(\"lo\")\n      file.pos -= 4\n      file.gets(4).should eq(\"ello\")\n    end\n  end\n\n  it \"raises if invoking tell with a closed file\" do\n    file = File.new(datapath(\"test_file.txt\"))\n    file.close\n    expect_raises(IO::Error, \"Closed stream\") { file.tell }\n  end\n\n  it \"iterates with each_char\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      i = 0\n      file.each_char do |char|\n        case i\n        when 0 then char.should eq('H')\n        when 1 then char.should eq('e')\n        else\n          break\n        end\n        i += 1\n      end\n    end\n  end\n\n  it \"iterates with each_byte\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      i = 0\n      file.each_byte do |byte|\n        case i\n        when 0 then byte.should eq('H'.ord)\n        when 1 then byte.should eq('e'.ord)\n        else\n          break\n        end\n        i += 1\n      end\n    end\n  end\n\n  it \"rewinds\" do\n    File.open(datapath(\"test_file.txt\")) do |file|\n      content = file.gets_to_end\n      content.size.should_not eq(0)\n      file.rewind\n      file.gets_to_end.should eq(content)\n    end\n  end\n\n  # Crystal does not expose ways to make a file unreadable on Windows\n  {% unless flag?(:win32) %}\n    it \"raises when reading a file with no permission\" do\n      with_tempfile(\"file.txt\") do |path|\n        File.touch(path)\n        File.chmod(path, File::Permissions::None)\n        pending_if_superuser!\n        expect_raises(File::AccessDeniedError, \"Error opening file with mode 'r': '#{path.inspect_unquoted}'\") { File.read(path) }\n      end\n    end\n  {% end %}\n\n  it \"raises when writing to a file with no permission\" do\n    with_tempfile(\"file.txt\") do |path|\n      File.touch(path)\n      File.chmod(path, File::Permissions::None)\n      pending_if_superuser!\n      expect_raises(File::AccessDeniedError, \"Error opening file with mode 'w': '#{path.inspect_unquoted}'\") { File.write(path, \"foo\") }\n    end\n  end\n\n  describe \"truncate\" do\n    it \"truncates\" do\n      with_tempfile(\"truncate.txt\") do |path|\n        File.write(path, \"0123456789\")\n        File.open(path, \"r+\") do |f|\n          f.gets_to_end.should eq(\"0123456789\")\n          f.rewind\n          f.puts(\"333\")\n          f.truncate(4)\n        end\n\n        File.read(path).should eq(\"333\\n\")\n      end\n    end\n\n    it \"truncates completely when no size is passed\" do\n      with_tempfile(\"truncate-no_size.txt\") do |path|\n        File.write(path, \"0123456789\")\n        File.open(path, \"r+\") do |f|\n          f.puts(\"333\")\n          f.truncate\n        end\n\n        File.read(path).should eq(\"\")\n      end\n    end\n\n    it \"requires a file opened for writing\" do\n      with_tempfile(\"truncate-opened.txt\") do |path|\n        File.write(path, \"0123456789\")\n        File.open(path, \"r\") do |f|\n          expect_raises(File::Error, \"Error truncating file: '#{path.inspect_unquoted}'\") do\n            f.truncate(4)\n          end\n        end\n      end\n    end\n  end\n\n  describe \"fsync\" do\n    it \"syncs OS file buffer to disk\" do\n      with_tempfile(\"fsync.txt\") do |path|\n        File.open(path, \"a\") do |f|\n          f.puts(\"333\")\n          f.fsync\n          File.read(path).should eq(\"333\\n\")\n        end\n      end\n    end\n  end\n\n  describe \"flock\" do\n    it \"#flock_exclusive\" do\n      File.open(datapath(\"test_file.txt\")) do |file1|\n        File.open(datapath(\"test_file.txt\")) do |file2|\n          file1.flock_exclusive do\n            exc = expect_raises(IO::Error, \"Error applying file lock: file is already locked\") do\n              file2.flock_exclusive(blocking: false) { }\n            end\n            exc.os_error.should eq({% if flag?(:win32) %}WinError::ERROR_LOCK_VIOLATION{% else %}Errno::EWOULDBLOCK{% end %})\n          end\n        end\n      end\n    end\n\n    {true, false}.each do |blocking|\n      context \"blocking: #{blocking}\" do\n        it \"#flock_shared\" do\n          File.open(datapath(\"test_file.txt\"), blocking: blocking) do |file1|\n            File.open(datapath(\"test_file.txt\"), blocking: blocking) do |file2|\n              file1.flock_shared do\n                file2.flock_shared(blocking: false) { }\n              end\n            end\n          end\n        end\n\n        it \"#flock_shared soft blocking fiber\" do\n          File.open(datapath(\"test_file.txt\"), blocking: blocking) do |file1|\n            File.open(datapath(\"test_file.txt\"), blocking: blocking) do |file2|\n              done = Channel(Nil).new\n              file1.flock_exclusive\n\n              spawn do\n                file1.flock_unlock\n                done.send nil\n              end\n\n              file2.flock_shared\n              done.receive\n            end\n          end\n        end\n\n        it \"#flock_exclusive soft blocking fiber\" do\n          File.open(datapath(\"test_file.txt\"), blocking: blocking) do |file1|\n            File.open(datapath(\"test_file.txt\"), blocking: blocking) do |file2|\n              done = Channel(Nil).new\n              file1.flock_exclusive\n\n              spawn do\n                file1.flock_unlock\n                done.send nil\n              end\n\n              file2.flock_exclusive\n              done.receive\n            end\n          end\n        end\n      end\n    end\n  end\n\n  it \"reads at offset\" do\n    filename = datapath(\"test_file.txt\")\n    {true, false}.each do |blocking|\n      File.open(filename, blocking: blocking) do |file|\n        file.read_at(6, 100) do |io|\n          io.gets_to_end.should eq(\"World\\nHello World\\nHello World\\nHello World\\nHello World\\nHello World\\nHello World\\nHello World\\nHello Worl\")\n        end\n\n        file.read_at(0, 240) do |io|\n          io.gets_to_end.should eq(File.read(filename))\n        end\n\n        file.read_at(6_i64, 5_i64) do |io|\n          io.gets_to_end.should eq(\"World\")\n        end\n      end\n    end\n  end\n\n  it \"raises when reading at offset outside of bounds\" do\n    with_tempfile(\"read-out_of_bounds\") do |path|\n      File.write(path, \"hello world\")\n\n      begin\n        File.open(path) do |io|\n          expect_raises(ArgumentError, \"Negative bytesize\") do\n            io.read_at(3, -1) { }\n          end\n\n          expect_raises(ArgumentError, \"Offset out of bounds\") do\n            io.read_at(12, 1) { }\n          end\n\n          expect_raises(ArgumentError, \"Bytesize out of bounds\") do\n            io.read_at(6, 6) { }\n          end\n        end\n      end\n    end\n  end\n\n  describe \"raises on null byte\" do\n    it_raises_on_null_byte \"new\" do\n      File.new(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"join\" do\n      File.join(\"foo\", \"\\0bar\")\n    end\n\n    it_raises_on_null_byte \"size\" do\n      File.size(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"rename (first arg)\" do\n      File.rename(\"foo\\0bar\", \"baz\")\n    end\n\n    it_raises_on_null_byte \"rename (second arg)\" do\n      File.rename(\"baz\", \"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"info\" do\n      File.info(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"info?\" do\n      File.info?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"exists?\" do\n      File.exists?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"readable?\" do\n      File::Info.readable?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"writable?\" do\n      File::Info.writable?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"executable?\" do\n      File::Info.executable?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"file?\" do\n      File.file?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"directory?\" do\n      File.directory?(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"dirname\" do\n      File.dirname(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"basename\" do\n      File.basename(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"basename 2, first arg\" do\n      File.basename(\"foo\\0bar\", \"baz\")\n    end\n\n    it_raises_on_null_byte \"basename 2, second arg\" do\n      File.basename(\"foobar\", \"baz\\0\")\n    end\n\n    it_raises_on_null_byte \"delete\" do\n      File.delete(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"extname\" do\n      File.extname(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"expand_path, first arg\" do\n      File.expand_path(\"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"expand_path, second arg\" do\n      File.expand_path(\"baz\", \"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"link, first arg\" do\n      File.link(\"foo\\0bar\", \"baz\")\n    end\n\n    it_raises_on_null_byte \"link, second arg\" do\n      File.link(\"baz\", \"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"symlink, first arg\" do\n      File.symlink(\"foo\\0bar\", \"baz\")\n    end\n\n    it_raises_on_null_byte \"symlink, second arg\" do\n      File.symlink(\"baz\", \"foo\\0bar\")\n    end\n\n    it_raises_on_null_byte \"symlink?\" do\n      File.symlink?(\"foo\\0bar\")\n    end\n  end\n\n  describe \"#delete\" do\n    it \"deletes\" do\n      path = datapath(\"file-to-be-deleted\")\n      File.touch(path)\n\n      file = File.new path\n      file.close\n\n      File.exists?(path).should be_true\n      file.delete\n      File.exists?(path).should be_false\n    ensure\n      File.delete(path) if path && File.exists?(path)\n    end\n  end\n\n  {% unless flag?(:without_iconv) %}\n    describe \"encoding\" do\n      it \"writes with encoding\" do\n        with_tempfile(\"encoding-write.txt\") do |path|\n          File.write(path, \"hello\", encoding: \"UCS-2LE\")\n          File.read(path).to_slice.should eq(\"hello\".encode(\"UCS-2LE\"))\n        end\n      end\n\n      it \"reads with encoding\" do\n        with_tempfile(\"encoding-read.txt\") do |path|\n          File.write(path, \"hello\", encoding: \"UCS-2LE\")\n          File.read(path, encoding: \"UCS-2LE\").should eq(\"hello\")\n        end\n      end\n\n      it \"opens with encoding\" do\n        with_tempfile(\"encoding-open.txt\") do |path|\n          File.write(path, \"hello\", encoding: \"UCS-2LE\")\n          File.open(path, encoding: \"UCS-2LE\") do |file|\n            file.gets_to_end.should eq(\"hello\")\n          end\n        end\n      end\n\n      it \"does each line with encoding\" do\n        with_tempfile(\"encoding-each_line.txt\") do |path|\n          File.write(path, \"hello\", encoding: \"UCS-2LE\")\n          File.each_line(path, encoding: \"UCS-2LE\") do |line|\n            line.should eq(\"hello\")\n          end\n        end\n      end\n\n      it \"reads lines with encoding\" do\n        with_tempfile(\"encoding-read_lines.txt\") do |path|\n          File.write(path, \"hello\", encoding: \"UCS-2LE\")\n          File.read_lines(path, encoding: \"UCS-2LE\").should eq([\"hello\"])\n        end\n      end\n    end\n  {% end %}\n\n  describe \"closed stream\" do\n    it \"raises if writing on a closed stream\" do\n      io = File.open(datapath(\"test_file.txt\"), \"r\")\n      io.close\n\n      expect_raises(IO::Error, \"Closed stream\") { io.gets_to_end }\n      expect_raises(IO::Error, \"Closed stream\") { io.print \"hi\" }\n      expect_raises(IO::Error, \"Closed stream\") { io.puts \"hi\" }\n      expect_raises(IO::Error, \"Closed stream\") { io.seek(1) }\n      expect_raises(IO::Error, \"Closed stream\") { io.gets }\n      expect_raises(IO::Error, \"Closed stream\") { io.read_byte }\n      expect_raises(IO::Error, \"Closed stream\") { io.write_byte('a'.ord.to_u8) }\n    end\n  end\n\n  describe \"utime\" do\n    it \"sets times with class method\" do\n      with_tempfile(\"utime-set.txt\") do |path|\n        File.write(path, \"\")\n\n        atime = Time.utc(2000, 1, 2)\n        mtime = Time.utc(2000, 3, 4)\n\n        File.utime(atime, mtime, path)\n\n        info = File.info(path)\n        info.modification_time.should eq(mtime)\n      end\n    end\n\n    it \"sets times with instance method\" do\n      with_tempfile(\"utime-set.txt\") do |path|\n        File.open(path, \"w\") do |file|\n          atime = Time.utc(2000, 1, 2)\n          mtime = Time.utc(2000, 3, 4)\n\n          file.utime(atime, mtime)\n\n          info = File.info(path)\n          info.modification_time.should eq(mtime)\n        end\n      end\n    end\n\n    it \"raises if file not found\" do\n      atime = Time.utc(2000, 1, 2)\n      mtime = Time.utc(2000, 3, 4)\n\n      expect_raises(File::NotFoundError, \"Error setting time on file: '#{datapath(\"nonexistent_file.txt\").inspect_unquoted}'\") do\n        File.utime(atime, mtime, datapath(\"nonexistent_file.txt\"))\n      end\n    end\n  end\n\n  describe \".touch\" do\n    it \"creates file if it doesn't exist\" do\n      with_tempfile(\"touch-create.txt\") do |path|\n        File.exists?(path).should be_false\n        File.touch(path)\n        File.exists?(path).should be_true\n      end\n    end\n\n    it \"sets file times to given time\" do\n      time = Time.utc(2000, 3, 4)\n      with_tempfile(\"touch-times.txt\") do |path|\n        File.touch(path, time)\n\n        info = File.info(path)\n        info.modification_time.should eq(time)\n      end\n    end\n\n    it \"sets file times to current time if no time argument given\" do\n      with_tempfile(\"touch-time_now.txt\") do |path|\n        File.touch(path)\n\n        info = File.info(path)\n        info.modification_time.should be_close(Time.utc, 1.second)\n      end\n    end\n\n    it \"raises if path contains non-existent directory\" do\n      with_tempfile(File.join(\"nonexistent-dir\", \"touch.txt\")) do |path|\n        expect_raises(File::NotFoundError, \"Error opening file with mode 'a': '#{path.inspect_unquoted}'\") do\n          File.touch(path)\n        end\n      end\n    end\n\n    describe \"touches existing\" do\n      it \"file\" do\n        with_tempfile(\"touch-file\") do |path|\n          File.write(path, \"\")\n\n          File.touch(path, Time.utc(2021, 1, 23))\n          info = File.info(path)\n          info.modification_time.should eq Time.utc(2021, 1, 23)\n\n          File.touch(path)\n          info = File.info(path)\n          info.modification_time.should be_close(Time.utc, 1.second)\n        end\n      end\n\n      it \"directory\" do\n        with_tempfile(\"touch-directory\") do |path|\n          Dir.mkdir(path)\n\n          File.touch(path, Time.utc(2021, 1, 23))\n          info = File.info(path)\n          info.modification_time.should eq Time.utc(2021, 1, 23)\n\n          File.touch(path)\n          info = File.info(path)\n          info.modification_time.should be_close(Time.utc, 1.second)\n        end\n      end\n    end\n\n    it \"raises if file cannot be accessed\" do\n      # This path is invalid because it represents a file path as a directory path\n      path = File.join(datapath(\"test_file.txt\"), \"doesnotexist\")\n      expect_raises(File::Error, path.inspect_unquoted) do\n        File.touch(path)\n      end\n    end\n  end\n\n  describe \".same_content?\" do\n    it \"compares two equal files\" do\n      File.same_content?(\n        datapath(\"test_file.txt\"),\n        datapath(\"test_file.txt\")\n      ).should be_true\n    end\n\n    it \"compares two different files\" do\n      File.same_content?(\n        datapath(\"test_file.txt\"),\n        datapath(\"test_file.ini\")\n      ).should be_false\n    end\n  end\n\n  describe \".copy\" do\n    it \"copies a file\" do\n      src_path = datapath(\"test_file.txt\")\n      with_tempfile(\"cp.txt\") do |out_path|\n        File.copy(src_path, out_path)\n        File.exists?(out_path).should be_true\n        File.same_content?(src_path, out_path).should be_true\n      end\n    end\n\n    it \"copies permissions\" do\n      with_tempfile(\"cp-permissions-src.txt\", \"cp-permissions-out.txt\") do |src_path, out_path|\n        File.write(src_path, \"foo\")\n        File.chmod(src_path, 0o444)\n\n        File.copy(src_path, out_path)\n\n        File.info(out_path).permissions.should eq(File::Permissions.new(0o444))\n        File.same_content?(src_path, out_path).should be_true\n      end\n    end\n\n    it \"overwrites existing destination and permissions\" do\n      with_tempfile(\"cp-permissions-src.txt\", \"cp-permissions-out.txt\") do |src_path, out_path|\n        File.write(src_path, \"foo\")\n        File.chmod(src_path, 0o444)\n\n        File.write(out_path, \"bar\")\n        File.chmod(out_path, 0o666)\n\n        File.copy(src_path, out_path)\n\n        File.info(out_path).permissions.should eq(File::Permissions.new(0o444))\n        File.same_content?(src_path, out_path).should be_true\n      end\n    end\n\n    it \"copies read-only permission\" do\n      with_tempfile(\"cp-permissions-src.txt\", \"cp-permissions-out.txt\") do |src_path, out_path|\n        File.write(src_path, \"foo\")\n        File.chmod(src_path, 0o444)\n\n        File.copy(src_path, out_path)\n\n        File.info(out_path).permissions.should eq normalize_permissions(0o444, directory: false)\n        File.same_content?(src_path, out_path).should be_true\n      end\n    end\n\n    it \"copies read-only permission over existing file\" do\n      with_tempfile(\"cp-permissions-src.txt\", \"cp-permissions-out.txt\") do |src_path, out_path|\n        File.write(src_path, \"foo\")\n        File.chmod(src_path, 0o444)\n\n        File.write(out_path, \"bar\")\n\n        File.copy(src_path, out_path)\n\n        File.info(out_path).permissions.should eq normalize_permissions(0o444, directory: false)\n        File.same_content?(src_path, out_path).should be_true\n      end\n    end\n  end\n\n  describe File::Permissions do\n    it \"does to_s\" do\n      perm = File::Permissions.flags(OwnerAll, GroupRead, GroupWrite, OtherRead)\n      perm.to_s.should eq(\"rwxrw-r-- (0o764)\")\n      perm.inspect.should eq(\"File::Permissions[OtherRead, GroupWrite, GroupRead, OwnerExecute, OwnerWrite, OwnerRead]\")\n      perm.pretty_inspect.should eq(\"File::Permissions[OtherRead, GroupWrite, GroupRead, OwnerExecute, OwnerWrite, OwnerRead]\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/file_utils_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"file_utils\"\n\nprivate def test_with_string_and_path(*paths, &)\n  yield *paths\n  yield *paths.map { |path| Path[path] }\nend\n\ndescribe \"FileUtils\" do\n  describe \".cd\" do\n    it \"should work\" do\n      cwd = Dir.current\n      test_with_string_and_path(cwd) do |arg|\n        FileUtils.cd(\"..\")\n        Dir.current.should_not eq(cwd)\n        FileUtils.cd(arg)\n        Dir.current.should eq(cwd)\n      end\n    end\n\n    it \"raises\" do\n      test_with_string_and_path \"/nope\" do |arg|\n        expect_raises(File::NotFoundError, \"Error while changing directory: '/nope'\") do\n          FileUtils.cd(arg)\n        end\n      end\n    end\n\n    it \"accepts a block\" do\n      cwd = Dir.current\n      test_with_string_and_path(\"..\") do |arg|\n        FileUtils.cd(arg) do\n          Dir.current.should_not eq(cwd)\n        end\n\n        Dir.current.should eq(cwd)\n      end\n    end\n  end\n\n  describe \".pwd\" do\n    it \"returns the current working directory\" do\n      FileUtils.pwd.should eq(Dir.current)\n    end\n  end\n\n  describe \".cmp\" do\n    it \"compares two equal files\" do\n      test_with_string_and_path(datapath(\"test_file.txt\")) do |arg|\n        FileUtils.cmp(arg, arg).should be_true\n      end\n    end\n\n    it \"compares two different files\" do\n      test_with_string_and_path(datapath(\"test_file.txt\"), datapath(\"test_file.ini\")) do |*args|\n        FileUtils.cmp(*args).should be_false\n      end\n    end\n  end\n\n  describe \".touch\" do\n    it \"creates file if it doesn't exist\" do\n      with_tempfile(\"touch.txt\") do |path|\n        test_with_string_and_path(path) do |arg|\n          File.exists?(path).should be_false\n          FileUtils.touch(arg)\n          File.exists?(path).should be_true\n\n          FileUtils.rm_rf path\n        end\n      end\n    end\n\n    it \"creates multiple files if they don't exists\" do\n      with_tempfile(\"touch1\", \"touch2\", \"touch3\") do |path1, path2, path3|\n        paths = {path1, path2, path3}\n        test_with_string_and_path(*paths) do |*args|\n          paths.each { |path| File.exists?(path).should be_false }\n          FileUtils.touch(args.to_a)\n          paths.each { |path| File.exists?(path).should be_true }\n\n          FileUtils.rm_rf paths\n        end\n      end\n    end\n  end\n\n  describe \".cp\" do\n    it \"copies a file\" do\n      src_path = datapath(\"test_file.txt\")\n      with_tempfile(\"cp.txt\") do |out_path|\n        test_with_string_and_path(src_path, out_path) do |*args|\n          File.exists?(out_path).should be_false\n          FileUtils.cp(*args)\n          File.exists?(out_path).should be_true\n          FileUtils.cmp(src_path, out_path).should be_true\n\n          FileUtils.rm_rf(out_path)\n        end\n      end\n    end\n\n    it \"copies permissions\" do\n      with_tempfile(\"cp-permissions-src.txt\", \"cp-permissions-out.txt\") do |src_path, out_path|\n        File.write(src_path, \"foo\")\n        File.chmod(src_path, 0o444)\n\n        test_with_string_and_path(src_path, out_path) do |*args|\n          FileUtils.cp(*args)\n\n          File.info(out_path).permissions.should eq(File::Permissions.new(0o444))\n          FileUtils.cmp(src_path, out_path).should be_true\n\n          FileUtils.rm_rf(out_path)\n        end\n      end\n    end\n\n    it \"raises an error if the directory doesn't exist\" do\n      expect_raises(ArgumentError, \"No such directory : not_existing_dir\") do\n        test_with_string_and_path(datapath(\"test_file.txt\"), \"not_existing_dir\") do |src_path, dest_path|\n          FileUtils.cp({src_path}, dest_path)\n        end\n      end\n    end\n\n    it \"copies multiple files\" do\n      name1 = \"test_file.txt\"\n      name2 = \"test_file.ini\"\n      src_path = datapath\n      src_name1 = File.join(src_path, name1)\n      src_name2 = File.join(src_path, name2)\n      with_tempfile(\"cp-multiple\") do |out_path|\n        out_name1 = File.join(out_path, name1)\n        out_name2 = File.join(out_path, name2)\n        test_with_string_and_path(src_name1, src_name2, out_path) do |arg1, arg2, dest_arg|\n          Dir.mkdir_p(out_path)\n\n          File.exists?(out_name1).should be_false\n          File.exists?(out_name2).should be_false\n\n          FileUtils.cp({arg1, arg2}, dest_arg)\n\n          File.exists?(out_name1).should be_true\n          File.exists?(out_name2).should be_true\n          FileUtils.cmp(src_name1, out_name1).should be_true\n          FileUtils.cmp(src_name2, out_name2).should be_true\n\n          FileUtils.rm_rf(out_path)\n        end\n      end\n    end\n  end\n\n  describe \".cp_r\" do\n    it \"copies a directory recursively\" do\n      with_tempfile(\"cp_r-test\", \"cp_r-test-copied\") do |src_path, dest_path|\n        test_with_string_and_path(src_path, dest_path) do |*args|\n          File.exists?(File.join(dest_path, \"a\")).should be_false\n          File.exists?(File.join(dest_path, \"b/c\")).should be_false\n          Dir.mkdir_p(src_path)\n          File.write(File.join(src_path, \"a\"), \"\")\n          Dir.mkdir(File.join(src_path, \"b\"))\n          File.write(File.join(src_path, \"b/c\"), \"\")\n\n          FileUtils.cp_r(*args)\n          File.exists?(File.join(dest_path, \"a\")).should be_true\n          File.exists?(File.join(dest_path, \"b/c\")).should be_true\n\n          FileUtils.rm_rf(src_path)\n          FileUtils.rm_rf(dest_path)\n        end\n      end\n    end\n\n    it \"copies a directory recursively if destination exists and is empty\" do\n      with_tempfile(\"cp_r-test\", \"cp_r-test-copied\") do |src_path, dest_path|\n        test_with_string_and_path(src_path, dest_path) do |*args|\n          Dir.mkdir_p(dest_path)\n\n          Dir.mkdir_p(src_path)\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"a\")).should be_false\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"b/c\")).should be_false\n          File.write(File.join(src_path, \"a\"), \"\")\n          Dir.mkdir(File.join(src_path, \"b\"))\n          File.write(File.join(src_path, \"b/c\"), \"\")\n\n          FileUtils.cp_r(*args)\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"a\")).should be_true\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"b/c\")).should be_true\n\n          FileUtils.rm_rf(src_path)\n          FileUtils.rm_rf(dest_path)\n        end\n      end\n    end\n\n    it \"copies a directory recursively if destination exists leaving existing files\" do\n      with_tempfile(\"cp_r-test\", \"cp_r-test-copied\") do |src_path, dest_path|\n        test_with_string_and_path(src_path, dest_path) do |*args|\n          Dir.mkdir_p(dest_path)\n          File.write(File.join(dest_path, \"d\"), \"\")\n          Dir.mkdir(File.join(dest_path, \"cp_r-test\"))\n          Dir.mkdir(File.join(dest_path, \"cp_r-test\", \"b\"))\n\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"a\")).should be_false\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"b/c\")).should be_false\n          File.exists?(File.join(dest_path, \"d\")).should be_true\n\n          Dir.mkdir_p(src_path)\n          File.write(File.join(src_path, \"a\"), \"\")\n          Dir.mkdir(File.join(src_path, \"b\"))\n          File.write(File.join(src_path, \"b/c\"), \"\")\n\n          FileUtils.cp_r(*args)\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"a\")).should be_true\n          File.exists?(File.join(dest_path, \"cp_r-test\", \"b/c\")).should be_true\n          File.exists?(File.join(dest_path, \"d\")).should be_true\n\n          FileUtils.rm_rf(src_path)\n          FileUtils.rm_rf(dest_path)\n        end\n      end\n    end\n  end\n\n  describe \".rm\" do\n    it \"tests rm with an existing path\" do\n      with_tempfile(\"rm\") do |path|\n        test_with_string_and_path(path) do |arg|\n          File.write(path, \"\")\n          FileUtils.rm(arg).should be_nil\n          File.exists?(path).should be_false\n        end\n      end\n    end\n\n    it \"tests rm with nonexistent path\" do\n      with_tempfile(\"rm-nonexistent\") do |path|\n        test_with_string_and_path(path) do |arg|\n          expect_raises(File::Error, \"Error deleting file: '#{path.inspect_unquoted}': No such file or directory\") do\n            FileUtils.rm(arg)\n          end\n        end\n      end\n    end\n\n    it \"tests rm with directory\" do\n      with_tempfile(\"rm-directory\") do |path|\n        test_with_string_and_path(path) do |arg|\n          Dir.mkdir_p(path)\n\n          expect_raises(File::Error, \"Error deleting file: '#{path.inspect_unquoted}'\") do\n            FileUtils.rm(arg)\n          end\n\n          Dir.exists?(path).should be_true\n        end\n      end\n    end\n\n    it \"tests rm with file and directory\" do\n      with_tempfile(\"rm-multi1\", \"rm-multi2\", \"rm-dir3\") do |path1, path2, path3|\n        test_with_string_and_path(path1, path2, path3) do |*args|\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n          Dir.mkdir_p(path3)\n\n          expect_raises(File::Error, \"Error deleting file: '#{path3.inspect_unquoted}'\") do\n            FileUtils.rm(args.to_a)\n          end\n\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n          Dir.exists?(path3).should be_true\n        end\n      end\n    end\n\n    it \"tests rm with multiple existing paths\" do\n      with_tempfile(\"rm-multi1\", \"rm-multi2\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |*args|\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n          FileUtils.rm(args.to_a).should be_nil\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n        end\n      end\n    end\n\n    it \"tests rm with some nonexistent paths\" do\n      with_tempfile(\"rm-nonexistent1\", \"rm-nonexistent2\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n\n          expect_raises(File::NotFoundError, \"Error deleting file: '#{path2.inspect_unquoted}'\") do\n            FileUtils.rm([arg1, arg2, arg2])\n          end\n        end\n      end\n    end\n  end\n\n  describe \".rm_f\" do\n    it \"tests rm_f with an existing path\" do\n      with_tempfile(\"rm_f\") do |path|\n        test_with_string_and_path(path) do |arg|\n          File.write(path, \"\")\n          FileUtils.rm_f(arg).should be_nil\n          File.exists?(path).should be_false\n        end\n      end\n    end\n\n    it \"tests rm_f with nonexistent path\" do\n      with_tempfile(\"rm_f-nonexistent\") do |path|\n        test_with_string_and_path(path) do |arg|\n          FileUtils.rm_f(arg)\n        end\n      end\n    end\n\n    it \"tests rm_f with directory\" do\n      with_tempfile(\"rm_f-directory\") do |path|\n        test_with_string_and_path(path) do |arg|\n          Dir.mkdir_p(path)\n\n          expect_raises(File::Error, \"Error deleting file: '#{path.inspect_unquoted}'\") do\n            FileUtils.rm_f(arg)\n          end\n\n          Dir.exists?(path).should be_true\n        end\n      end\n    end\n\n    it \"tests rm_f with file and directory\" do\n      with_tempfile(\"rm_f-multi1\", \"rm_f-multi2\", \"rm_f-dir3\") do |path1, path2, path3|\n        test_with_string_and_path(path1, path2, path3) do |*args|\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n          Dir.mkdir_p(path3)\n\n          expect_raises(File::Error, \"Error deleting file: '#{path3.inspect_unquoted}'\") do\n            FileUtils.rm_f(args.to_a)\n          end\n\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n          Dir.exists?(path3).should be_true\n        end\n      end\n    end\n\n    it \"tests rm_f with multiple existing paths\" do\n      with_tempfile(\"rm_f-multi1\", \"rm_f-multi2\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |*args|\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n          FileUtils.rm_f(args.to_a).should be_nil\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n        end\n      end\n    end\n\n    it \"tests rm_f with some nonexistent paths\" do\n      with_tempfile(\"rm-nonexistent1\", \"rm-nonexistent2\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n          FileUtils.rm_f([arg1, arg2, arg2])\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n        end\n      end\n    end\n  end\n\n  describe \".rm_r\" do\n    it \"deletes a directory recursively\" do\n      with_tempfile(\"rm_r\") do |path|\n        test_with_string_and_path(path) do |arg|\n          Dir.mkdir(path)\n          File.write(File.join(path, \"a\"), \"\")\n          Dir.mkdir(File.join(path, \"b\"))\n          File.write(File.join(path, \"b/c\"), \"\")\n\n          FileUtils.rm_r(arg)\n          Dir.exists?(path).should be_false\n        end\n      end\n    end\n\n    it \"doesn't follow symlinks\" do\n      with_tempfile(\"rm_r-removed\", \"rm_r-linked\") do |removed_path, linked_path|\n        link_path = File.join(removed_path, \"link\")\n        file_path = File.join(linked_path, \"file\")\n\n        test_with_string_and_path(removed_path) do |arg|\n          Dir.mkdir(removed_path)\n          Dir.mkdir(linked_path)\n          File.symlink(linked_path, link_path)\n          File.write(file_path, \"\")\n\n          Dir.exists?(removed_path).should be_true\n          Dir.exists?(linked_path).should be_true\n          File.exists?(file_path).should be_true\n\n          FileUtils.rm_r(arg)\n          Dir.exists?(removed_path).should be_false\n          Dir.exists?(linked_path).should be_true\n          File.exists?(file_path).should be_true\n\n          FileUtils.rm_rf(linked_path)\n        end\n      end\n    end\n  end\n\n  describe \".rm_rf\" do\n    it \"delete recursively a directory\" do\n      with_tempfile(\"rm_rf\") do |path|\n        test_with_string_and_path(path) do |arg|\n          FileUtils.mkdir(path)\n          File.write(File.join(path, \"a\"), \"\")\n          FileUtils.mkdir(File.join(path, \"b\"))\n          FileUtils.rm_rf(arg).should be_nil\n          Dir.exists?(path).should be_false\n        end\n      end\n    end\n\n    it \"delete recursively multiple directory\" do\n      with_tempfile(\"rm_rf-multi1\", \"rm_rf-multi2\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |*args|\n          FileUtils.mkdir(path1)\n          FileUtils.mkdir(path2)\n          File.write(File.join(path1, \"a\"), \"\")\n          File.write(File.join(path2, \"a\"), \"\")\n          FileUtils.mkdir(File.join(path1, \"b\"))\n          FileUtils.mkdir(File.join(path2, \"b\"))\n          FileUtils.rm_rf(args.to_a).should be_nil\n          Dir.exists?(path1).should be_false\n          Dir.exists?(path2).should be_false\n        end\n      end\n    end\n\n    it \"doesn't return error on nonexistent file\" do\n      with_tempfile(\"rm_rf-nonexistent\") do |path|\n        test_with_string_and_path(path) do |arg|\n          FileUtils.rm_rf(arg).should be_nil\n        end\n      end\n    end\n\n    it \"doesn't return error on nonexistent files\" do\n      with_tempfile(\"rm_rf-nonexistent\") do |path1|\n        path2 = File.join(path1, \"a\")\n        test_with_string_and_path(path1, path2) do |*args|\n          FileUtils.mkdir(path1)\n          FileUtils.rm_rf(args.to_a).should be_nil\n        end\n      end\n    end\n  end\n\n  describe \".mv\" do\n    it \"moves a file from one place to another\" do\n      with_tempfile(\"mv1\", \"mv2\") do |path1, path2|\n        a = File.join(path1, \"a\")\n        b = File.join(path2, \"b\")\n        test_with_string_and_path(a, b) do |*args|\n          FileUtils.mkdir([path1, path2])\n          File.write(a, \"\")\n          FileUtils.mv(*args).should be_nil\n          File.exists?(a).should be_false\n          File.exists?(b).should be_true\n          FileUtils.rm_rf(path1)\n          FileUtils.rm_rf(path2)\n        end\n      end\n    end\n\n    it \"raises an error if non correct arguments\" do\n      with_tempfile(\"mv-nonexistent\") do |path|\n        test_with_string_and_path(File.join(path, \"a\"), File.join(path, \"b\")) do |*args|\n          expect_raises(File::NotFoundError, \"Error renaming file: '#{File.join(path, \"a\").inspect_unquoted}' -> '#{File.join(path, \"b\").inspect_unquoted}'\") do\n            FileUtils.mv(*args)\n          end\n        end\n      end\n    end\n\n    it \"moves multiple files to one place\" do\n      with_tempfile(\"mv-multi1\", \"mv-multi2\", \"mv-multi3\") do |path1, path2, path3|\n        source1 = File.join(path1, \"a\")\n        source2 = File.join(path2, \"b\")\n        test_with_string_and_path(source1, source2, path3) do |arg1, arg2, arg3|\n          FileUtils.mkdir([path1, path2, path3])\n          File.write(source1, \"\")\n          File.write(source2, \"\")\n          FileUtils.mv([arg1, arg2], arg3).should be_nil\n          File.exists?(source1).should be_false\n          File.exists?(source2).should be_false\n          File.exists?(File.join(path3, \"a\")).should be_true\n          File.exists?(File.join(path3, \"b\")).should be_true\n          FileUtils.rm_rf([path1, path2, path3])\n        end\n      end\n    end\n\n    it \"raises an error if dest is non correct\" do\n      expect_raises ArgumentError do\n        with_tempfile(\"mv-nonexistent\") do |path|\n          test_with_string_and_path(File.join(path, \"a\"), File.join(path, \"b\"), File.join(path, \"c\")) do |arg1, arg2, arg3|\n            FileUtils.mv([arg1, arg2], arg3)\n          end\n        end\n      end\n    end\n\n    it \"moves all existing files to destination\" do\n      with_tempfile(\"mv-source\", \"mv-target\") do |source_path, target_path|\n        path1 = File.join(source_path, \"a\")\n        path2 = File.join(source_path, \"b\")\n        path3 = File.join(source_path, \"c\", \"sub\")\n\n        test_with_string_and_path(path1, path2, path3, target_path) do |arg1, arg2, arg3, arg4|\n          FileUtils.mkdir_p([path1, path2, target_path])\n          path1 = File.join(path1, \"a\")\n          path2 = File.join(path2, \"b\")\n          File.write(path1, \"\")\n          File.write(path2, \"\")\n          FileUtils.mv([arg1, arg2, arg3], arg4).should be_nil\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n          File.exists?(File.join(target_path, \"a\")).should be_true\n          File.exists?(File.join(target_path, \"b\")).should be_true\n          FileUtils.rm_rf([path1, path2, target_path])\n        end\n      end\n    end\n  end\n\n  it \"tests mkdir and rmdir with a new path\" do\n    with_tempfile(\"mkdir-new\") do |path|\n      test_with_string_and_path(path) do |arg|\n        FileUtils.mkdir(arg, 0o700).should be_nil\n        Dir.exists?(path).should be_true\n        FileUtils.rmdir(arg).should be_nil\n        Dir.exists?(path).should be_false\n      end\n    end\n  end\n\n  it \"tests mkdir and rmdir with multiple new paths\" do\n    with_tempfile(\"mkdir-new1\", \"mkdir-new2\") do |path1, path2|\n      test_with_string_and_path(path1, path2) do |*args|\n        FileUtils.mkdir(args.to_a, 0o700).should be_nil\n        Dir.exists?(path1).should be_true\n        Dir.exists?(path2).should be_true\n        FileUtils.rmdir(args.to_a).should be_nil\n        Dir.exists?(path1).should be_false\n        Dir.exists?(path2).should be_false\n        FileUtils.rm_rf([path1, path2])\n      end\n    end\n  end\n\n  it \"tests mkdir with an existing path\" do\n    test_with_string_and_path(datapath) do |arg|\n      expect_raises(File::AlreadyExistsError, \"Unable to create directory: '#{datapath.inspect_unquoted}'\") do\n        Dir.mkdir(arg, 0o700)\n      end\n    end\n  end\n\n  it \"tests mkdir with multiples existing paths\" do\n    test_with_string_and_path(datapath) do |arg|\n      expect_raises(File::AlreadyExistsError, \"Unable to create directory: '#{datapath.inspect_unquoted}'\") do\n        FileUtils.mkdir([arg, arg], 0o700)\n      end\n    end\n\n    with_tempfile(\"mkdir-nonexistent\") do |path|\n      test_with_string_and_path(path, datapath) do |*args|\n        expect_raises(File::AlreadyExistsError, \"Unable to create directory: '#{datapath.inspect_unquoted}'\") do\n          FileUtils.mkdir(args.to_a, 0o700)\n        end\n        FileUtils.rm_rf(path)\n      end\n    end\n  end\n\n  it \"tests mkdir_p with multiples new path\" do\n    with_tempfile(\"mkdir_p-multi1\", \"mkdir_p-multi2\") do |path1, path2|\n      path3 = File.join({path1, \"a\", \"b\", \"c\"})\n      path4 = File.join({path2, \"a\", \"b\", \"c\"})\n      test_with_string_and_path(path3, path4) do |*args|\n        FileUtils.mkdir_p([path1, path2]).should be_nil\n        Dir.exists?(path1).should be_true\n        Dir.exists?(path2).should be_true\n        FileUtils.mkdir_p(args.to_a).should be_nil\n        Dir.exists?(path3).should be_true\n        Dir.exists?(path4).should be_true\n        FileUtils.rm_rf([path1, path2])\n      end\n    end\n  end\n\n  it \"tests mkdir_p with multiple existing path\" do\n    FileUtils.mkdir_p([datapath, datapath]).should be_nil\n    with_tempfile(\"mkdir_p-existing\") do |path|\n      test_with_string_and_path(datapath(\"test_file.txt\"), path) do |*args|\n        expect_raises(File::AlreadyExistsError, \"Unable to create directory: '#{datapath(\"test_file.txt\").inspect_unquoted}'\") do\n          FileUtils.mkdir_p(args.to_a)\n        end\n      end\n    end\n  end\n\n  it \"tests rmdir with an nonexistent path\" do\n    with_tempfile(\"rmdir-nonexistent\") do |path|\n      test_with_string_and_path(path) do |arg|\n        expect_raises(File::NotFoundError, \"Unable to remove directory: '#{path.inspect_unquoted}'\") do\n          FileUtils.rmdir(arg)\n        end\n      end\n    end\n  end\n\n  it \"tests rmdir with multiple nonexistent path\" do\n    with_tempfile(\"rmdir-nonexistent\") do |path|\n      test_with_string_and_path(\"#{path}1\", \"#{path}2\") do |*args|\n        expect_raises(File::NotFoundError, \"Unable to remove directory: '#{path.inspect_unquoted}1'\") do\n          FileUtils.rmdir(args.to_a)\n        end\n      end\n    end\n  end\n\n  it \"tests rmdir with a path that cannot be removed\" do\n    test_with_string_and_path(datapath) do |arg|\n      expect_raises(File::Error, \"Unable to remove directory: '#{datapath.inspect_unquoted}'\") do\n        FileUtils.rmdir(arg)\n      end\n    end\n  end\n\n  it \"tests rmdir with multiple path that cannot be removed\" do\n    test_with_string_and_path(datapath) do |arg|\n      expect_raises(File::Error, \"Unable to remove directory: '#{datapath.inspect_unquoted}'\") do\n        FileUtils.rmdir([arg, arg])\n      end\n    end\n  end\n\n  # hard links are practically unavailable on Android\n  {% unless flag?(:android) %}\n    describe \".ln\" do\n      it \"creates a hardlink\" do\n        with_tempfile(\"ln_src\", \"ln_dst\") do |path1, path2|\n          test_with_string_and_path(path1, path2) do |arg1, arg2|\n            FileUtils.touch(path1)\n            FileUtils.ln(arg1, arg2)\n            File.exists?(path2).should be_true\n            File.symlink?(path2).should be_false\n            FileUtils.rm_rf([path1, path2])\n          end\n        end\n      end\n\n      it \"creates a hardlink inside a destination dir\" do\n        with_tempfile(\"ln_src\", \"ln_dst_dir\") do |path1, path2|\n          path2 += File::SEPARATOR\n          path3 = File.join(path2, File.basename(path1))\n          test_with_string_and_path(path1, path2) do |arg1, arg2|\n            FileUtils.touch(path1)\n            FileUtils.mkdir(path2)\n            FileUtils.ln(arg1, arg2)\n            File.exists?(path3).should be_true\n            File.symlink?(path3).should be_false\n            FileUtils.rm_rf([path1, path2])\n          end\n        end\n      end\n\n      it \"creates multiple hardlinks inside a destination dir\" do\n        with_tempfile(\"ln_src_1\", \"ln_src_2\", \"ln_src_3\", \"ln_dst_dir\") do |path1, path2, path3, dir_path|\n          paths = [path1, path2, path3]\n          dir_path += File::SEPARATOR\n          test_with_string_and_path(path1, path2, path3, dir_path) do |arg1, arg2, arg3, arg4|\n            paths.each { |path| FileUtils.touch(path) }\n            FileUtils.mkdir(dir_path)\n            FileUtils.ln([arg1, arg2, arg3], arg4)\n\n            paths.each do |path|\n              link_path = File.join(dir_path, File.basename(path))\n              File.exists?(link_path).should be_true\n              File.symlink?(link_path).should be_false\n            end\n            FileUtils.rm_rf(dir_path)\n          end\n        end\n      end\n\n      it \"fails with a nonexistent source\" do\n        with_tempfile(\"ln_src_missing\", \"ln_dst_missing\") do |path1, path2|\n          test_with_string_and_path(path1, path2) do |arg1, arg2|\n            expect_raises(File::NotFoundError, \"Error creating link: '#{path1.inspect_unquoted}' -> '#{path2.inspect_unquoted}'\") do\n              FileUtils.ln(arg1, arg2)\n            end\n          end\n        end\n      end\n\n      it \"fails with an extant destination\" do\n        with_tempfile(\"ln_src\", \"ln_dst_exists\") do |path1, path2|\n          FileUtils.touch([path1, path2])\n\n          test_with_string_and_path(path1, path2) do |arg1, arg2|\n            expect_raises(File::AlreadyExistsError, \"Error creating link: '#{path1.inspect_unquoted}' -> '#{path2.inspect_unquoted}'\") do\n              FileUtils.ln(arg1, arg2)\n            end\n          end\n        end\n      end\n    end\n  {% end %}\n\n  describe \".ln_s\" do\n    it \"creates a symlink\" do\n      with_tempfile(\"ln_s_src\", \"ln_s_dst\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.touch(path1)\n          FileUtils.ln_s(arg1, arg2)\n          File.exists?(path2).should be_true\n          File.symlink?(path2).should be_true\n          FileUtils.rm_rf([path1, path2])\n        end\n      end\n    end\n\n    it \"creates a symlink inside a destination dir\" do\n      with_tempfile(\"ln_s_src\", \"ln_s_dst_dir\") do |path1, path2|\n        path3 = File.join(path2, File.basename(path1))\n\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.touch(path1)\n          FileUtils.mkdir(path2)\n          FileUtils.ln_s(arg1, arg2)\n          File.exists?(path3).should be_true\n          File.symlink?(path3).should be_true\n          FileUtils.rm_rf([path1, path2])\n        end\n      end\n    end\n\n    it \"creates multiple symlinks inside a destination dir\" do\n      with_tempfile(\"ln_s_src_1\", \"ln_s_src_2\", \"ln_s_src_3\", \"ln_s_dst_dir\") do |path1, path2, path3, dir_path|\n        dir_path += File::SEPARATOR\n        test_with_string_and_path(path1, path2, path3, dir_path) do |arg1, arg2, arg3, dir_arg|\n          paths = [arg1, arg2, arg3]\n          paths.each { |path| FileUtils.touch(path) }\n          FileUtils.mkdir(dir_path)\n          FileUtils.ln_s(paths, dir_arg)\n\n          paths.each do |path|\n            link_path = File.join(dir_path, File.basename(path))\n            File.exists?(link_path).should be_true\n            File.symlink?(link_path).should be_true\n          end\n          FileUtils.rm_rf(paths)\n          FileUtils.rm_rf(dir_path)\n        end\n      end\n    end\n\n    it \"works with a nonexistent source\" do\n      with_tempfile(\"ln_s_src_missing\", \"ln_s_dst_missing\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.ln_s(arg1, arg2)\n          File.exists?(path2).should be_false\n          File.symlink?(path2).should be_true\n\n          expect_raises(File::NotFoundError, \"Error resolving real path: '#{path2.inspect_unquoted}'\") do\n            File.realpath(path2)\n          end\n          FileUtils.rm_rf(path2)\n        end\n      end\n    end\n\n    it \"fails with an existing destination\" do\n      with_tempfile(\"ln_s_src\", \"ln_s_dst_exists\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.touch([path1, path2])\n\n          expect_raises(File::AlreadyExistsError, \"Error creating symlink: '#{path1.inspect_unquoted}' -> '#{path2.inspect_unquoted}'\") do\n            FileUtils.ln_s(arg1, arg2)\n          end\n          FileUtils.rm_rf([path1, path2])\n        end\n      end\n    end\n  end\n\n  describe \".ln_sf\" do\n    it \"overwrites a destination file\" do\n      with_tempfile(\"ln_sf_src\", \"ln_sf_dst_exists\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.touch([path1, path2])\n          File.symlink?(path1).should be_false\n          File.symlink?(path2).should be_false\n\n          FileUtils.ln_sf(arg1, arg2)\n          File.symlink?(path1).should be_false\n          File.symlink?(path2).should be_true\n          FileUtils.rm_rf([path1, path2])\n        end\n      end\n    end\n\n    {% if flag?(:unix) %}\n      # can't use backtick in interpreted code (#12241)\n      pending_interpreted \"overwrites a destination named pipe\" do\n        with_tempfile(\"ln_sf_src\", \"ln_sf_dst_pipe_exists\") do |path1, path2|\n          test_with_string_and_path(path1, path2) do |arg1, arg2|\n            FileUtils.touch([path1])\n            `mkfifo #{path2}`\n            File.symlink?(path1).should be_false\n            File.symlink?(path2).should be_false\n\n            FileUtils.ln_sf(arg1, arg2)\n            File.symlink?(path1).should be_false\n            File.symlink?(path2).should be_true\n            FileUtils.rm_rf([path1, path2])\n          end\n        end\n      end\n    {% end %}\n\n    it \"overwrites a destination dir in dir\" do\n      with_tempfile(\"ln_sf_src\", \"ln_sf_dst_dir_exists\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.touch([path1])\n          dir_dest = File.join(path2, File.basename(path1))\n          Dir.mkdir_p(dir_dest)\n          File.symlink?(path1).should be_false\n          File.symlink?(path2).should be_false\n\n          FileUtils.ln_sf(arg1, arg2)\n          File.symlink?(path1).should be_false\n          File.symlink?(dir_dest).should be_true\n          FileUtils.rm_rf([path1, path2])\n        end\n      end\n    end\n\n    it \"overwrites a destination file inside a dir\" do\n      with_tempfile(\"ln_sf_dst_dir\", \"ln_sf_dst\") do |dir, path2|\n        dir += File::SEPARATOR\n        path1 = File.join(dir, File.basename(path2))\n\n        test_with_string_and_path(dir, path2) do |dir_arg, arg2|\n          FileUtils.mkdir(dir)\n          FileUtils.touch([path1, path2])\n          File.symlink?(path1).should be_false\n          File.symlink?(path2).should be_false\n\n          FileUtils.ln_sf(arg2, dir_arg)\n          File.symlink?(path1).should be_true\n          File.symlink?(path2).should be_false\n\n          FileUtils.rm_rf([dir, path1, path2])\n        end\n      end\n    end\n\n    it \"creates multiple symlinks in a destination dir, with overwrites\" do\n      with_tempfile(\"ln_sf_src_dir\", \"ln_sf_dst_dir\") do |src_dir, dir|\n        test_with_string_and_path(src_dir, dir) do |src_arg, dir_arg|\n          paths1 = Array.new(3) { |i| \"exists_#{i}\" }\n          paths2 = paths1.map { |p| File.join(src_arg, p) }\n          paths3 = paths1.map { |p| File.join(dir, p) }\n\n          FileUtils.mkdir(src_dir)\n          FileUtils.mkdir(dir)\n          FileUtils.touch(paths2 + paths3)\n          (paths2 + paths3).each { |p| File.symlink?(p).should be_false }\n\n          FileUtils.ln_sf(paths2, dir_arg)\n          paths2.each { |p| File.symlink?(p).should be_false }\n          paths3.each { |p| File.symlink?(p).should be_true }\n\n          FileUtils.rm_rf([src_dir, dir])\n        end\n      end\n    end\n\n    it \"creates a symlink even if there's nothing to overwrite\" do\n      with_tempfile(\"ln_sf_src\", \"ln_sf_dst\") do |path1, path2|\n        test_with_string_and_path(path1, path2) do |arg1, arg2|\n          FileUtils.touch(path1)\n          File.exists?(path2).should be_false\n\n          FileUtils.ln_sf(arg1, arg2)\n          File.symlink?(path2).should be_true\n          FileUtils.rm_rf([path1, path2])\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/float_printer/diy_fp_spec.cr",
    "content": "# The example numbers in these specs are ported from the C++\n# \"double-conversions\" library. The following is their license:\n#   Copyright 2012 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\nrequire \"spec\"\n\nprivate alias DiyFP = Float::Printer::DiyFP\n\ndescribe DiyFP do\n  it \"multiply\" do\n    fp1 = DiyFP.new(3_u64, 0)\n    fp2 = DiyFP.new(2_u64, 0)\n    prod = fp1 * fp2\n\n    prod.frac.should eq 0\n    prod.exp.should eq 64\n  end\n\n  it \"multiply\" do\n    fp1 = DiyFP.new(0x8000000000000000_u64, 11)\n    fp2 = DiyFP.new(2_u64, 13)\n    prod = fp1 * fp2\n\n    prod.frac.should eq 1\n    prod.exp.should eq 11 + 13 + 64\n  end\n\n  it \"multiply rounding\" do\n    fp1 = DiyFP.new(0x8000000000000001_u64, 11)\n    fp2 = DiyFP.new(1_u64, 13)\n    prod = fp1 * fp2\n\n    prod.frac.should eq 1\n    prod.exp.should eq 11 + 13 + 64\n  end\n\n  it \"multiply rounding\" do\n    fp1 = DiyFP.new(0x7fffffffffffffff_u64, 11)\n    fp2 = DiyFP.new(1_u64, 13)\n    prod = fp1 * fp2\n\n    prod.frac.should eq 0\n    prod.exp.should eq 11 + 13 + 64\n  end\n\n  it \"multiply big numbers\" do\n    fp1 = DiyFP.new(0xffffffffffffffff_u64, 11)\n    fp2 = DiyFP.new(0xffffffffffffffff_u64, 13)\n    prod = fp1 * fp2\n\n    prod.frac.should eq 0xfffffffffffffffe_u64\n    prod.exp.should eq 11 + 13 + 64\n  end\n\n  it \"converts ordered 64\" do\n    ordered = 0x0123456789ABCDEF_u64\n    f = ordered.unsafe_as(Float64)\n    f.should eq 3512700564088504e-318 # ensure byte order\n\n    fp = DiyFP.from_f(f)\n\n    fp.exp.should eq 0x12 - 0x3FF - 52\n    # The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64.\n    fp.frac.should eq 0x0013456789ABCDEF\n  end\n\n  it \"converts ordered 32\" do\n    ordered = 0x01234567_u32\n    f = ordered.unsafe_as(Float32)\n    f.should eq(2.9988165487136453e-38_f32)\n\n    fp = DiyFP.from_f(f)\n\n    fp.exp.should eq 0x2 - 0x7F - 23\n    # The 23 mantissa bits, plus the implicit 1 in bit 24 as a uint32.\n\n    fp.frac.should eq 0xA34567\n  end\n\n  it \"converts min f64\" do\n    min = 0x0000000000000001_u64\n    f = min.unsafe_as(Float64)\n    f.should eq 5e-324 # ensure byte order\n\n    fp = DiyFP.from_f(f)\n\n    fp.exp.should eq -0x3FF - 52 + 1\n    # This is denormal, so no hidden bit\n    fp.frac.should eq 1\n  end\n\n  it \"converts min f32\" do\n    min = 0x00000001_u32\n    f = min.unsafe_as(Float32)\n    fp = DiyFP.from_f(f)\n\n    fp.exp.should eq -0x7F - 23 + 1\n    # This is a denormal; so no hidden bit.\n    fp.frac.should eq 1\n  end\n\n  it \"converts max f64\" do\n    max = 0x7fefffffffffffff_u64\n    f = max.unsafe_as(Float64)\n    f.should eq 1.7976931348623157e308 # ensure byte order\n\n    fp = DiyFP.from_f(f)\n\n    fp.exp.should eq 0x7FE - 0x3FF - 52\n    fp.frac.should eq 0x001fffffffffffff_u64\n  end\n\n  it \"converts max f32\" do\n    max = 0x7f7fffff_u64\n    f = max.unsafe_as(Float32)\n    f.should eq 3.4028234e38_f32 # ensure byte order\n\n    fp = DiyFP.from_f(f)\n\n    fp.exp.should eq 0xFE - 0x7F - 23\n    fp.frac.should eq 0x00ffffff_u64\n  end\n\n  it \"normalizes ordered\" do\n    ordered = 0x0123456789ABCDEF_u64\n    f = ordered.unsafe_as(Float64)\n\n    fp = DiyFP.from_f_normalized(f)\n\n    fp.exp.should eq 0x12 - 0x3FF - 52 - 11\n    fp.frac.should eq 0x0013456789ABCDEF_u64 << 11\n  end\n\n  it \"normalizes min f64\" do\n    min = 0x0000000000000001_u64\n    f = min.unsafe_as(Float64)\n\n    fp = DiyFP.from_f_normalized(f)\n\n    fp.exp.should eq -0x3FF - 52 + 1 - 63\n    # This is a denormal; so no hidden bit\n    fp.frac.should eq 0x8000000000000000_u64\n  end\n\n  it \"normalizes max f64\" do\n    max = 0x7fefffffffffffff_u64\n    f = max.unsafe_as(Float64)\n\n    fp = DiyFP.from_f_normalized(f)\n\n    fp.exp.should eq 0x7FE - 0x3FF - 52 - 11\n    fp.frac.should eq 0x001fffffffffffff_u64 << 11\n  end\nend\n"
  },
  {
    "path": "spec/std/float_printer/grisu3_spec.cr",
    "content": "# The example numbers in these specs are ported from the C++\n# \"double-conversions\" library. The following is their license:\n#   Copyright 2012 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\nrequire \"spec\"\n\nprivate def test_grisu(v : UInt64)\n  test_grisu v.unsafe_as(Float64)\nend\n\nprivate def test_grisu(v : UInt32)\n  test_grisu v.unsafe_as(Float32)\nend\n\nprivate def test_grisu(v : Float64 | Float32)\n  buffer = StaticArray(UInt8, 128).new(0_u8)\n  status, decimal_exponent, length = Float::Printer::Grisu3.grisu3(v, buffer.to_unsafe)\n  point = decimal_exponent + length\n  return status, point, String.new(buffer.to_unsafe)\nend\n\ndescribe \"grisu3\" do\n  context \"float64\" do\n    it \"min float\" do\n      status, point, str = test_grisu 5e-324\n      status.should be_true\n      str.should eq \"5\"\n      point.should eq -323\n    end\n\n    it \"max float\" do\n      status, point, str = test_grisu 1.7976931348623157e308\n      status.should be_true\n      str.should eq \"17976931348623157\"\n      point.should eq 309\n    end\n\n    it \"point at end\" do\n      status, point, str = test_grisu 4294967272.0\n      status.should be_true\n      str.should eq \"4294967272\"\n      point.should eq 10\n    end\n\n    it \"large number\" do\n      status, point, str = test_grisu 4.1855804968213567e298\n      status.should be_true\n      str.should eq \"4185580496821357\"\n      point.should eq 299\n    end\n\n    it \"small number\" do\n      status, point, str = test_grisu 5.5626846462680035e-309\n      status.should be_true\n      str.should eq \"5562684646268003\"\n      point.should eq -308\n    end\n\n    it \"another no point move\" do\n      status, point, str = test_grisu 2147483648.0\n      status.should be_true\n      str.should eq \"2147483648\"\n      point.should eq 10\n    end\n\n    it \"failure case\" do\n      # grisu should not be able to do this number\n      # this number is reused to ensure the fallback works\n      status, _, str = test_grisu 3.5844466002796428e+298\n      status.should be_false\n      str.should_not eq \"35844466002796428\"\n    end\n\n    it \"smallest normal\" do\n      status, point, str = test_grisu 0x0010000000000000_u64\n      status.should be_true\n      str.should eq \"22250738585072014\"\n      point.should eq -307\n    end\n\n    it \"largest denormal\" do\n      status, point, str = test_grisu 0x000FFFFFFFFFFFFF_u64\n      status.should be_true\n      str.should eq \"2225073858507201\"\n      point.should eq -307\n    end\n  end\n\n  context \"float32\" do\n    it \"min\" do\n      status, point, str = test_grisu 1e-45_f32\n      status.should be_true\n      str.should eq \"1\"\n      point.should eq -44\n    end\n\n    it \"max\" do\n      status, point, str = test_grisu 3.4028234e38_f32\n      status.should be_true\n      str.should eq \"34028235\"\n      point.should eq 39\n    end\n\n    it \"general whole number, rounding\" do\n      status, point, str = test_grisu 4294967272.0_f32\n      status.should be_true\n      str.should eq \"42949673\"\n      point.should eq 10\n    end\n\n    it \"general whole number, rounding\" do\n      status, point, str = test_grisu 4294967272.0_f32\n      status.should be_true\n      str.should eq \"42949673\"\n      point.should eq 10\n    end\n\n    it \"large number, rounding\" do\n      status, point, str = test_grisu 3.32306998946228968226e35_f32\n      status.should be_true\n      str.should eq \"332307\"\n      point.should eq 36\n    end\n\n    it \"small number\" do\n      status, point, str = test_grisu 1.2341e-41_f32\n      status.should be_true\n      str.should eq \"12341\"\n      point.should eq -40\n    end\n\n    it \"general no rounding\" do\n      status, point, str = test_grisu 3.3554432e7_f32\n      status.should be_true\n      str.should eq \"33554432\"\n      point.should eq 8\n    end\n\n    it \"general with rounding up\" do\n      status, point, str = test_grisu 3.26494756798464e14_f32\n      status.should be_true\n      str.should eq \"32649476\"\n      point.should eq 15\n    end\n\n    it \"general with rounding down\" do\n      status, point, str = test_grisu 3.91132223637771935344e37_f32\n      status.should be_true\n      str.should eq \"39113222\"\n      point.should eq 38\n    end\n\n    it \"smallest normal\" do\n      status, point, str = test_grisu 0x00800000_u32\n      status.should be_true\n      str.should eq \"11754944\"\n      point.should eq -37\n    end\n\n    it \"largest denormal\" do\n      status, point, str = test_grisu 0x007FFFFF_u32\n      status.should be_true\n      str.should eq \"11754942\"\n      point.should eq -37\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/float_printer/hexfloat_spec.cr",
    "content": "# This file contains test cases derived from Microsoft's STL:\n# https://github.com/microsoft/STL/tree/main/tests/std/tests/P0067R5_charconv\n#\n# REUSE-IgnoreStart\n# Original license:\n# Copyright (c) Microsoft Corporation.\n# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n# REUSE-IgnoreEnd\n\nrequire \"spec\"\nrequire \"spec/helpers/string\"\n\nprivate def assert_to_s(num : F, str, *, file = __FILE__, line = __LINE__) forall F\n  assert_prints num.to_hexfloat, str, file: file, line: line\n  if num.nan?\n    F.parse_hexfloat(str).nan?.should be_true, file: file, line: line\n  else\n    F.parse_hexfloat(str).should eq(num), file: file, line: line\n  end\nend\n\nprivate def assert_parse_error(type : F.class, str, err, *, file = __FILE__, line = __LINE__) forall F\n  expect_raises ArgumentError, \"Invalid hexfloat: #{err}\", file: file, line: line do\n    F.parse_hexfloat(str)\n  end\n  F.parse_hexfloat?(str).should be_nil, file: file, line: line\nend\n\ndescribe Float64 do\n  describe \".parse_hexfloat\" do\n    it { Float64.parse_hexfloat(\"0x123p+0\").should eq(291_f64) }\n    it { Float64.parse_hexfloat(\"0x123.0p+0\").should eq(291_f64) }\n    it { Float64.parse_hexfloat(\"0x123p0\").should eq(291_f64) }\n\n    it { Float64.parse_hexfloat(\"0x123.p0\").should eq(291_f64) }\n    it { Float64.parse_hexfloat(\"0x.123p12\").should eq(291_f64) }\n    it { Float64.parse_hexfloat(\"0x123.456p7\").should eq(37282.6875_f64) }\n\n    it { Float64.parse_hexfloat(\"+0x123p+0\").should eq(291_f64) }\n    it { Float64.parse_hexfloat(\"-0x123p-0\").should eq(-291_f64) }\n\n    it { Float64.parse_hexfloat(\"0XABCDEFP+0\").should eq(11259375_f64) }\n\n    it { Float64.parse_hexfloat(\"0x1.000000000000a000p+0\").should eq(1.0000000000000022) } # exact\n    it { Float64.parse_hexfloat(\"0x1.000000000000a001p+0\").should eq(1.0000000000000022) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1.000000000000a800p+0\").should eq(1.0000000000000022) } # midpoint, round down to even\n    it { Float64.parse_hexfloat(\"0x1.000000000000a801p+0\").should eq(1.0000000000000024) } # above midpoint, round up\n    it { Float64.parse_hexfloat(\"0x1.000000000000b000p+0\").should eq(1.0000000000000024) } # exact\n    it { Float64.parse_hexfloat(\"0x1.000000000000b001p+0\").should eq(1.0000000000000024) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1.000000000000b800p+0\").should eq(1.0000000000000027) } # midpoint, round up to even\n    it { Float64.parse_hexfloat(\"0x1.000000000000b801p+0\").should eq(1.0000000000000027) } # above midpoint, round up\n\n    it { Float64.parse_hexfloat(\"0x1.00000000000020p+0\").should eq(1.0000000000000004) } # exact\n    it { Float64.parse_hexfloat(\"0x1.00000000000021p+0\").should eq(1.0000000000000004) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1.00000000000028p+0\").should eq(1.0000000000000004) } # midpoint, round down to even\n    it { Float64.parse_hexfloat(\"0x1.00000000000029p+0\").should eq(1.0000000000000007) } # above midpoint, round up\n    it { Float64.parse_hexfloat(\"0x1.00000000000030p+0\").should eq(1.0000000000000007) } # exact\n    it { Float64.parse_hexfloat(\"0x1.00000000000031p+0\").should eq(1.0000000000000007) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1.00000000000038p+0\").should eq(1.0000000000000009) } # midpoint, round up to even\n    it { Float64.parse_hexfloat(\"0x1.00000000000039p+0\").should eq(1.0000000000000009) } # above midpoint, round up\n\n    it { Float64.parse_hexfloat(\"0x1.000000000000a000000000000000000p+0\").should eq(1.0000000000000022) } # exact\n    it { Float64.parse_hexfloat(\"0x1.000000000000a000000000000000001p+0\").should eq(1.0000000000000022) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1.000000000000a800000000000000000p+0\").should eq(1.0000000000000022) } # midpoint, round down to even\n    it { Float64.parse_hexfloat(\"0x1.000000000000a800000000000000001p+0\").should eq(1.0000000000000024) } # above midpoint, round up\n    it { Float64.parse_hexfloat(\"0x1.000000000000b000000000000000000p+0\").should eq(1.0000000000000024) } # exact\n    it { Float64.parse_hexfloat(\"0x1.000000000000b000000000000000001p+0\").should eq(1.0000000000000024) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1.000000000000b800000000000000000p+0\").should eq(1.0000000000000027) } # midpoint, round up to even\n    it { Float64.parse_hexfloat(\"0x1.000000000000b800000000000000001p+0\").should eq(1.0000000000000027) } # above midpoint, round up\n\n    it { Float64.parse_hexfloat(\"0x1000000000000a0000000p+0\").should eq(1.2089258196146319e+24) } # exact\n    it { Float64.parse_hexfloat(\"0x1000000000000a0010000p+0\").should eq(1.2089258196146319e+24) } # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1000000000000a8000000p+0\").should eq(1.2089258196146319e+24) } # midpoint, round down to even\n    it { Float64.parse_hexfloat(\"0x1000000000000a8010000p+0\").should eq(1.208925819614632e+24) }  # above midpoint, round up\n    it { Float64.parse_hexfloat(\"0x1000000000000b0000000p+0\").should eq(1.208925819614632e+24) }  # exact\n    it { Float64.parse_hexfloat(\"0x1000000000000b0010000p+0\").should eq(1.208925819614632e+24) }  # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x1000000000000b8000000p+0\").should eq(1.2089258196146324e+24) } # midpoint, round up to even\n    it { Float64.parse_hexfloat(\"0x1000000000000b8010000p+0\").should eq(1.2089258196146324e+24) } # above midpoint, round up\n\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000a000p+0\").should eq(3.388131789017209e-21) }  # exact\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000a001p+0\").should eq(3.388131789017209e-21) }  # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000a800p+0\").should eq(3.388131789017209e-21) }  # midpoint, round down to even\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000a801p+0\").should eq(3.38813178901721e-21) }   # above midpoint, round up\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000b000p+0\").should eq(3.38813178901721e-21) }   # exact\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000b001p+0\").should eq(3.38813178901721e-21) }   # below midpoint, round down\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000b800p+0\").should eq(3.3881317890172104e-21) } # midpoint, round up to even\n    it { Float64.parse_hexfloat(\"0x.00000000000000001000000000000b801p+0\").should eq(3.3881317890172104e-21) } # above midpoint, round up\n\n    # https://www.exploringbinary.com/nondeterministic-floating-point-conversions-in-java/\n    it { Float64.parse_hexfloat(\"0x0.0000008p-1022\").should eq(6.63123685e-316) }\n\n    describe \"round-to-nearest, ties-to-even\" do\n      it { Float64.parse_hexfloat(\"0x0.00000000000008p-1022\").should eq(0.0) }\n      it { Float64.parse_hexfloat(\"0x0.00000000000008#{\"0\" * 1000}1p-1022\").should eq(5.0e-324) }\n\n      it { Float64.parse_hexfloat(\"0x0.ffffffffffffe8p-1022\").should eq(2.2250738585072004e-308) }\n      it { Float64.parse_hexfloat(\"0x0.ffffffffffffe8#{\"0\" * 1000}1p-1022\").should eq(Float64::MIN_POSITIVE.prev_float) }\n\n      it { Float64.parse_hexfloat(\"0x1.00000000000008p+0\").should eq(1.0) }\n      it { Float64.parse_hexfloat(\"0x1.00000000000008#{\"0\" * 1000}1p+0\").should eq(1.0000000000000002) }\n\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffe8p+0\").should eq(1.9999999999999996) }\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffe8#{\"0\" * 1000}1p+0\").should eq(1.9999999999999998) }\n\n      it { Float64.parse_hexfloat(\"0x1.00000000000008p+1023\").should eq(8.98846567431158e307) }\n      it { Float64.parse_hexfloat(\"0x1.00000000000008#{\"0\" * 1000}1p+1023\").should eq(8.988465674311582e307) }\n\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffe8p+1023\").should eq(1.7976931348623155e308) }\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffe8#{\"0\" * 1000}1p+1023\").should eq(1.7976931348623157e308) }\n    end\n\n    describe \"values close to zero\" do\n      it { Float64.parse_hexfloat(\"0x0.7p-1074\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x0.8p-1074\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x0.9p-1074\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x0.fp-1074\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x1.0p-1074\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x1.1p-1074\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x1.7p-1074\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x1.8p-1074\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x1.9p-1074\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x1.fp-1074\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x2.0p-1074\").should eq(Float64::MIN_SUBNORMAL * 2) }\n\n      it { Float64.parse_hexfloat(\"0x0.fp-1075\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x1.0p-1075\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x1.1p-1075\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x1.fp-1075\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x2.0p-1075\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x2.1p-1075\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x2.fp-1075\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x3.0p-1075\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x3.1p-1075\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x3.fp-1075\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x4.0p-1075\").should eq(Float64::MIN_SUBNORMAL * 2) }\n\n      it { Float64.parse_hexfloat(\"0x1.fp-1076\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x2.0p-1076\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x2.1p-1076\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x3.fp-1076\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x4.0p-1076\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x4.1p-1076\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x5.fp-1076\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x6.0p-1076\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x6.1p-1076\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x7.fp-1076\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x8.0p-1076\").should eq(Float64::MIN_SUBNORMAL * 2) }\n\n      it { Float64.parse_hexfloat(\"0x3.fp-1077\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x4.0p-1077\").should eq(Float64.zero) }\n      it { Float64.parse_hexfloat(\"0x4.1p-1077\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x7.fp-1077\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x8.0p-1077\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0x8.1p-1077\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0xb.fp-1077\").should eq(Float64::MIN_SUBNORMAL) }\n      it { Float64.parse_hexfloat(\"0xc.0p-1077\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0xc.1p-1077\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0xf.fp-1077\").should eq(Float64::MIN_SUBNORMAL * 2) }\n      it { Float64.parse_hexfloat(\"0x10.0p-1077\").should eq(Float64::MIN_SUBNORMAL * 2) }\n    end\n\n    describe \"values close to MIN_POSITIVE and MAX\" do\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffp-1022\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x1.0000000000000p-1022\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffep-1023\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffffp-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x2.0000000000000p-1023\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffcp-1024\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffdp-1024\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffep-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x3.fffffffffffffp-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x4.0000000000000p-1024\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffff8p-1025\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffff9p-1025\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffbp-1025\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffcp-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffdp-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffep-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.fffffffffffffp-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x8.0000000000000p-1025\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff0p-1022\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff1p-1022\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff7p-1022\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff8p-1022\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff9p-1022\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffbp-1022\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffcp-1022\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffdp-1022\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x0.ffffffffffffffp-1022\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x1.00000000000000p-1022\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffe0p-1023\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffe1p-1023\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffefp-1023\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff0p-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff1p-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff7p-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff8p-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff9p-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffffp-1023\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x2.00000000000000p-1023\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffc0p-1024\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffc1p-1024\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffdfp-1024\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffe0p-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffe1p-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffefp-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x3.fffffffffffff0p-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x3.fffffffffffff1p-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffffp-1024\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x4.00000000000000p-1024\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffff80p-1025\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffff81p-1025\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffbfp-1025\").should eq(Float64::MIN_POSITIVE.prev_float) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffc0p-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffc1p-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffdfp-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffe0p-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffe1p-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffffp-1025\").should eq(Float64::MIN_POSITIVE) }\n      it { Float64.parse_hexfloat(\"0x8.00000000000000p-1025\").should eq(Float64::MIN_POSITIVE) }\n\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffffp+1023\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x2.0000000000000p+1023\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffep+1022\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x3.fffffffffffffp+1022\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x4.0000000000000p+1022\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffcp+1021\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffdp+1021\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffep+1021\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x7.fffffffffffffp+1021\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x8.0000000000000p+1021\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff8p+1024\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff9p+1024\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffbp+1024\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffcp+1024\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffdp+1024\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x0.ffffffffffffffp+1024\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x1.00000000000000p+1024\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff0p+1023\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff1p+1023\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff7p+1023\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff8p+1023\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x1.fffffffffffff9p+1023\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x1.ffffffffffffffp+1023\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x2.00000000000000p+1023\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffe0p+1022\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffe1p+1022\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffefp+1022\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x3.fffffffffffff0p+1022\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x3.fffffffffffff1p+1022\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x3.ffffffffffffffp+1022\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x4.00000000000000p+1022\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffc0p+1021\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffc1p+1021\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffdfp+1021\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffe0p+1021\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffe1p+1021\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x7.ffffffffffffffp+1021\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x8.00000000000000p+1021\").should eq(Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff80p+1024\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffff81p+1024\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffbfp+1024\").should eq(Float64::MAX) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffc0p+1024\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffc1p+1024\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x0.fffffffffffffffp+1024\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"0x1.000000000000000p+1024\").should eq(Float64::INFINITY) }\n    end\n\n    describe \"special cases\" do\n      it { Float64.parse_hexfloat(\"-0x0p+0\").to_s.should eq(\"-0.0\") } # sign bit must be negative\n\n      it { Float64.parse_hexfloat(\"inf\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"INF\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"infinity\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"INFINITY\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"+Infinity\").should eq(Float64::INFINITY) }\n      it { Float64.parse_hexfloat(\"-iNF\").should eq(-Float64::INFINITY) }\n\n      it { Float64.parse_hexfloat(\"nan\").nan?.should be_true }\n      it { Float64.parse_hexfloat(\"NAN\").nan?.should be_true }\n      it { Float64.parse_hexfloat(\"+NaN\").nan?.should be_true }\n      it { Float64.parse_hexfloat(\"-nAn\").nan?.should be_true }\n    end\n\n    describe \"invalid hexfloats\" do\n      it { assert_parse_error Float64, \"\", \"expected '0'\" }\n      it { assert_parse_error Float64, \" \", \"expected '0'\" }\n      it { assert_parse_error Float64, \"1\", \"expected '0'\" }\n      it { assert_parse_error Float64, \"0\", \"expected 'x' or 'X'\" }\n      it { assert_parse_error Float64, \"01\", \"expected 'x' or 'X'\" }\n      it { assert_parse_error Float64, \"0x\", \"expected at least one digit\" }\n      it { assert_parse_error Float64, \"0x.\", \"expected at least one digit\" }\n      it { assert_parse_error Float64, \"0xp\", \"expected at least one digit\" }\n      it { assert_parse_error Float64, \"0x.p\", \"expected at least one digit\" }\n      it { assert_parse_error Float64, \"0x1\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float64, \"0x1.\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float64, \"0x1.1\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float64, \"0x.1\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float64, \"0x1p\", \"empty exponent\" }\n      it { assert_parse_error Float64, \"0x1p+\", \"empty exponent\" }\n      it { assert_parse_error Float64, \"0x1p-\", \"empty exponent\" }\n      it { assert_parse_error Float64, \"0x1p2147483648\", \"exponent overflow\" }\n      it { assert_parse_error Float64, \"0x1p2147483650\", \"exponent overflow\" }\n      it { assert_parse_error Float64, \"0x1p+2147483648\", \"exponent overflow\" }\n      it { assert_parse_error Float64, \"0x1p+2147483650\", \"exponent overflow\" }\n      it { assert_parse_error Float64, \"0x1p-2147483648\", \"exponent overflow\" }\n      it { assert_parse_error Float64, \"0x1p-2147483650\", \"exponent overflow\" }\n      it { assert_parse_error Float64, \"0x1p0 \", \"trailing characters\" }\n      it { assert_parse_error Float64, \"0x1p0_f32\", \"trailing characters\" }\n      it { assert_parse_error Float64, \"0x1p0_f64\", \"trailing characters\" }\n      it { assert_parse_error Float64, \"0x1p0f\", \"trailing characters\" }\n      it { assert_parse_error Float64, \"NaN \", \"expected '0'\" }\n      it { assert_parse_error Float64, \"- Infinity\", \"expected '0'\" }\n    end\n  end\n\n  describe \"#to_hexfloat\" do\n    describe \"special cases\" do\n      it { assert_to_s 0.0, \"0x0p+0\" }\n      it { assert_to_s -0.0, \"-0x0p+0\" }\n      it { assert_to_s Float64::INFINITY, \"Infinity\" }\n      it { assert_to_s -Float64::INFINITY, \"-Infinity\" }\n      it { assert_to_s Float64::NAN, \"NaN\" }\n      it { assert_to_s 1.447509765625, \"0x1.729p+0\" }\n      it { assert_to_s -1.447509765625, \"-0x1.729p+0\" }\n    end\n\n    describe \"corner cases\" do\n      it { assert_to_s 1.447265625, \"0x1.728p+0\" }                                   # instead of \"2.e5p-1\"\n      it { assert_to_s Float64::MIN_SUBNORMAL, \"0x0.0000000000001p-1022\" }           # instead of \"1p-1074\"\n      it { assert_to_s Float64::MIN_POSITIVE.prev_float, \"0x0.fffffffffffffp-1022\" } # max subnormal\n      it { assert_to_s Float64::MIN_POSITIVE, \"0x1p-1022\" }                          # min normal\n      it { assert_to_s Float64::MAX, \"0x1.fffffffffffffp+1023\" }                     # max normal\n    end\n\n    describe \"exponents\" do\n      it { assert_to_s 1.8227805048890994e-304, \"0x1p-1009\" }\n      it { assert_to_s 1.8665272370064378e-301, \"0x1p-999\" }\n      it { assert_to_s 1.5777218104420236e-30, \"0x1p-99\" }\n      it { assert_to_s 0.001953125, \"0x1p-9\" }\n      it { assert_to_s 1.0, \"0x1p+0\" }\n      it { assert_to_s 512.0, \"0x1p+9\" }\n      it { assert_to_s 6.338253001141147e+29, \"0x1p+99\" }\n      it { assert_to_s 5.357543035931337e+300, \"0x1p+999\" }\n      it { assert_to_s 5.486124068793689e+303, \"0x1p+1009\" }\n    end\n\n    describe \"hexits\" do\n      it { assert_to_s 1.0044444443192333, \"0x1.01234567p+0\" }\n      it { assert_to_s 1.5377777775283903, \"0x1.89abcdefp+0\" }\n    end\n\n    describe \"trimming\" do\n      it { assert_to_s 1.0000000000000022, \"0x1.000000000000ap+0\" }\n      it { assert_to_s 1.0000000000000355, \"0x1.00000000000ap+0\" }\n      it { assert_to_s 1.0000000000005684, \"0x1.0000000000ap+0\" }\n      it { assert_to_s 1.000000000009095, \"0x1.000000000ap+0\" }\n      it { assert_to_s 1.0000000001455192, \"0x1.00000000ap+0\" }\n      it { assert_to_s 1.0000000023283064, \"0x1.0000000ap+0\" }\n      it { assert_to_s 1.000000037252903, \"0x1.000000ap+0\" }\n      it { assert_to_s 1.0000005960464478, \"0x1.00000ap+0\" }\n      it { assert_to_s 1.000009536743164, \"0x1.0000ap+0\" }\n      it { assert_to_s 1.000152587890625, \"0x1.000ap+0\" }\n      it { assert_to_s 1.00244140625, \"0x1.00ap+0\" }\n      it { assert_to_s 1.0390625, \"0x1.0ap+0\" }\n      it { assert_to_s 1.625, \"0x1.ap+0\" }\n      it { assert_to_s 1.0, \"0x1p+0\" }\n    end\n  end\nend\n\ndescribe Float32 do\n  describe \".parse_hexfloat\" do\n    it { Float32.parse_hexfloat(\"0x123p+0\").should eq(291_f32) }\n    it { Float32.parse_hexfloat(\"0x123.0p+0\").should eq(291_f32) }\n    it { Float32.parse_hexfloat(\"0x123p0\").should eq(291_f32) }\n\n    it { Float32.parse_hexfloat(\"0x123.p0\").should eq(291_f32) }\n    it { Float32.parse_hexfloat(\"0x.123p12\").should eq(291_f32) }\n    it { Float32.parse_hexfloat(\"0x123.456p7\").should eq(37282.6875_f32) }\n\n    it { Float32.parse_hexfloat(\"+0x123p+0\").should eq(291_f32) }\n    it { Float32.parse_hexfloat(\"-0x123p-0\").should eq(-291_f32) }\n\n    it { Float32.parse_hexfloat(\"0XABCDEFP+0\").should eq(11259375_f32) }\n\n    it { Float32.parse_hexfloat(\"0x1.a0000400p+0\").should eq(1.6250002_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1.a0000401p+0\").should eq(1.6250002_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1.a0000500p+0\").should eq(1.6250002_f32) } # midpoint, round down to even\n    it { Float32.parse_hexfloat(\"0x1.a0000501p+0\").should eq(1.6250004_f32) } # above midpoint, round up\n    it { Float32.parse_hexfloat(\"0x1.a0000600p+0\").should eq(1.6250004_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1.a0000601p+0\").should eq(1.6250004_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1.a0000700p+0\").should eq(1.6250005_f32) } # midpoint, round up to even\n    it { Float32.parse_hexfloat(\"0x1.a0000701p+0\").should eq(1.6250005_f32) } # above midpoint, round up\n\n    it { Float32.parse_hexfloat(\"0x1.0000040p+0\").should eq(1.0000002_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1.0000041p+0\").should eq(1.0000002_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1.0000050p+0\").should eq(1.0000002_f32) } # midpoint, round down to even\n    it { Float32.parse_hexfloat(\"0x1.0000051p+0\").should eq(1.0000004_f32) } # above midpoint, round up\n    it { Float32.parse_hexfloat(\"0x1.0000060p+0\").should eq(1.0000004_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1.0000061p+0\").should eq(1.0000004_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1.0000070p+0\").should eq(1.0000005_f32) } # midpoint, round up to even\n    it { Float32.parse_hexfloat(\"0x1.0000071p+0\").should eq(1.0000005_f32) } # above midpoint, round up\n\n    it { Float32.parse_hexfloat(\"0x1.a0000400000000000000000p+0\").should eq(1.6250002_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1.a0000400000000000000001p+0\").should eq(1.6250002_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1.a0000500000000000000000p+0\").should eq(1.6250002_f32) } # midpoint, round down to even\n    it { Float32.parse_hexfloat(\"0x1.a0000500000000000000001p+0\").should eq(1.6250004_f32) } # above midpoint, round up\n    it { Float32.parse_hexfloat(\"0x1.a0000600000000000000000p+0\").should eq(1.6250004_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1.a0000600000000000000001p+0\").should eq(1.6250004_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1.a0000700000000000000000p+0\").should eq(1.6250005_f32) } # midpoint, round up to even\n    it { Float32.parse_hexfloat(\"0x1.a0000700000000000000001p+0\").should eq(1.6250005_f32) } # above midpoint, round up\n\n    it { Float32.parse_hexfloat(\"0x1a0000400000000000p+0\").should eq(4.796154e+20_f32) }  # exact\n    it { Float32.parse_hexfloat(\"0x1a0000401000000000p+0\").should eq(4.796154e+20_f32) }  # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1a0000500000000000p+0\").should eq(4.796154e+20_f32) }  # midpoint, round down to even\n    it { Float32.parse_hexfloat(\"0x1a0000501000000000p+0\").should eq(4.7961545e+20_f32) } # above midpoint, round up\n    it { Float32.parse_hexfloat(\"0x1a0000600000000000p+0\").should eq(4.7961545e+20_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x1a0000601000000000p+0\").should eq(4.7961545e+20_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x1a0000700000000000p+0\").should eq(4.796155e+20_f32) }  # midpoint, round up to even\n    it { Float32.parse_hexfloat(\"0x1a0000701000000000p+0\").should eq(4.796155e+20_f32) }  # above midpoint, round up\n\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000400p+0\").should eq(5.505715e-21_f32) }  # exact\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000401p+0\").should eq(5.505715e-21_f32) }  # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000500p+0\").should eq(5.505715e-21_f32) }  # midpoint, round down to even\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000501p+0\").should eq(5.5057154e-21_f32) } # above midpoint, round up\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000600p+0\").should eq(5.5057154e-21_f32) } # exact\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000601p+0\").should eq(5.5057154e-21_f32) } # below midpoint, round down\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000700p+0\").should eq(5.5057158e-21_f32) } # midpoint, round up to even\n    it { Float32.parse_hexfloat(\"0x.00000000000000001a0000701p+0\").should eq(5.5057158e-21_f32) } # above midpoint, round up\n\n    describe \"values close to zero\" do\n      it { Float32.parse_hexfloat(\"0x0.7p-149\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x0.8p-149\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x0.9p-149\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x0.fp-149\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x1.0p-149\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x1.1p-149\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x1.7p-149\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x1.8p-149\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x1.9p-149\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x1.fp-149\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x2.0p-149\").should eq(Float32::MIN_SUBNORMAL * 2) }\n\n      it { Float32.parse_hexfloat(\"0x0.fp-150\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x1.0p-150\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x1.1p-150\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x1.fp-150\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x2.0p-150\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x2.1p-150\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x2.fp-150\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x3.0p-150\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x3.1p-150\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x3.fp-150\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x4.0p-150\").should eq(Float32::MIN_SUBNORMAL * 2) }\n\n      it { Float32.parse_hexfloat(\"0x1.fp-151\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x2.0p-151\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x2.1p-151\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x3.fp-151\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x4.0p-151\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x4.1p-151\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x5.fp-151\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x6.0p-151\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x6.1p-151\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x7.fp-151\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x8.0p-151\").should eq(Float32::MIN_SUBNORMAL * 2) }\n\n      it { Float32.parse_hexfloat(\"0x3.fp-152\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x4.0p-152\").should eq(Float32.zero) }\n      it { Float32.parse_hexfloat(\"0x4.1p-152\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x7.fp-152\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x8.0p-152\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0x8.1p-152\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0xb.fp-152\").should eq(Float32::MIN_SUBNORMAL) }\n      it { Float32.parse_hexfloat(\"0xc.0p-152\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0xc.1p-152\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0xf.fp-152\").should eq(Float32::MIN_SUBNORMAL * 2) }\n      it { Float32.parse_hexfloat(\"0x10.0p-152\").should eq(Float32::MIN_SUBNORMAL * 2) }\n    end\n\n    describe \"values close to MIN_POSITIVE and MAX\" do\n      it { Float32.parse_hexfloat(\"0x7.fffffp-129\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x8.00000p-129\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x0.fffffep-126\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x0.ffffffp-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.000000p-126\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x1.fffffcp-127\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x1.fffffdp-127\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x1.fffffep-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.ffffffp-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x2.000000p-127\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x3.fffff8p-128\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x3.fffff9p-128\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x3.fffffbp-128\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x3.fffffcp-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffdp-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffep-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.ffffffp-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x4.000000p-128\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x7.fffff0p-129\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x7.fffff1p-129\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x7.fffff7p-129\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x7.fffff8p-129\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x7.fffff9p-129\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x7.fffffbp-129\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x7.fffffcp-129\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x7.fffffdp-129\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x7.ffffffp-129\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x8.000000p-129\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x0.fffffe0p-126\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x0.fffffe1p-126\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x0.fffffefp-126\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff0p-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff1p-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff7p-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff8p-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff9p-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x0.fffffffp-126\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.0000000p-126\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x1.fffffc0p-127\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x1.fffffc1p-127\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x1.fffffdfp-127\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x1.fffffe0p-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.fffffe1p-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.fffffefp-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.ffffff0p-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.ffffff1p-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x1.fffffffp-127\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x2.0000000p-127\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x3.fffff80p-128\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x3.fffff81p-128\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x3.fffffbfp-128\").should eq(Float32::MIN_POSITIVE.prev_float) }\n      it { Float32.parse_hexfloat(\"0x3.fffffc0p-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffc1p-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffdfp-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffe0p-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffe1p-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x3.fffffffp-128\").should eq(Float32::MIN_POSITIVE) }\n      it { Float32.parse_hexfloat(\"0x4.0000000p-128\").should eq(Float32::MIN_POSITIVE) }\n\n      it { Float32.parse_hexfloat(\"0x0.ffffffp+128\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x1.000000p+128\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x1.fffffep+127\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x1.ffffffp+127\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x2.000000p+127\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x3.fffffcp+126\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x3.fffffdp+126\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x3.fffffep+126\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x3.ffffffp+126\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x4.000000p+126\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x7.fffff8p+125\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x7.fffff9p+125\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x7.fffffbp+125\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x7.fffffcp+125\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x7.fffffdp+125\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x7.ffffffp+125\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x8.000000p+125\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x0.ffffff0p+128\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff1p+128\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff7p+128\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff8p+128\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x0.ffffff9p+128\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x0.fffffffp+128\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x1.0000000p+128\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x1.fffffe0p+127\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x1.fffffe1p+127\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x1.fffffefp+127\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x1.ffffff0p+127\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x1.ffffff1p+127\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x1.fffffffp+127\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x2.0000000p+127\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x3.fffffc0p+126\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x3.fffffc1p+126\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x3.fffffdfp+126\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x3.fffffe0p+126\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x3.fffffe1p+126\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x3.fffffffp+126\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x4.0000000p+126\").should eq(Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"0x7.fffff80p+125\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x7.fffff81p+125\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x7.fffffbfp+125\").should eq(Float32::MAX) }\n      it { Float32.parse_hexfloat(\"0x7.fffffc0p+125\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x7.fffffc1p+125\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x7.fffffffp+125\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"0x8.0000000p+125\").should eq(Float32::INFINITY) }\n    end\n\n    describe \"special cases\" do\n      it { Float32.parse_hexfloat(\"-0x0p+0\").to_s.should eq(\"-0.0\") } # sign bit must be negative\n\n      it { Float32.parse_hexfloat(\"inf\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"INF\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"infinity\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"INFINITY\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"+Infinity\").should eq(Float32::INFINITY) }\n      it { Float32.parse_hexfloat(\"-iNF\").should eq(-Float32::INFINITY) }\n\n      it { Float32.parse_hexfloat(\"nan\").nan?.should be_true }\n      it { Float32.parse_hexfloat(\"NAN\").nan?.should be_true }\n      it { Float32.parse_hexfloat(\"+NaN\").nan?.should be_true }\n      it { Float32.parse_hexfloat(\"-nAn\").nan?.should be_true }\n    end\n\n    describe \"invalid hexfloats\" do\n      it { assert_parse_error Float32, \"\", \"expected '0'\" }\n      it { assert_parse_error Float32, \" \", \"expected '0'\" }\n      it { assert_parse_error Float32, \"1\", \"expected '0'\" }\n      it { assert_parse_error Float32, \"0\", \"expected 'x' or 'X'\" }\n      it { assert_parse_error Float32, \"01\", \"expected 'x' or 'X'\" }\n      it { assert_parse_error Float32, \"0x\", \"expected at least one digit\" }\n      it { assert_parse_error Float32, \"0x.\", \"expected at least one digit\" }\n      it { assert_parse_error Float32, \"0xp\", \"expected at least one digit\" }\n      it { assert_parse_error Float32, \"0x.p\", \"expected at least one digit\" }\n      it { assert_parse_error Float32, \"0x1\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float32, \"0x1.\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float32, \"0x1.1\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float32, \"0x.1\", \"expected 'p' or 'P'\" }\n      it { assert_parse_error Float32, \"0x1p\", \"empty exponent\" }\n      it { assert_parse_error Float32, \"0x1p+\", \"empty exponent\" }\n      it { assert_parse_error Float32, \"0x1p-\", \"empty exponent\" }\n      it { assert_parse_error Float32, \"0x1p2147483648\", \"exponent overflow\" }\n      it { assert_parse_error Float32, \"0x1p2147483650\", \"exponent overflow\" }\n      it { assert_parse_error Float32, \"0x1p+2147483648\", \"exponent overflow\" }\n      it { assert_parse_error Float32, \"0x1p+2147483650\", \"exponent overflow\" }\n      it { assert_parse_error Float32, \"0x1p-2147483648\", \"exponent overflow\" }\n      it { assert_parse_error Float32, \"0x1p-2147483650\", \"exponent overflow\" }\n      it { assert_parse_error Float32, \"0x1p0 \", \"trailing characters\" }\n      it { assert_parse_error Float32, \"0x1p0_f32\", \"trailing characters\" }\n      it { assert_parse_error Float32, \"0x1p0_f64\", \"trailing characters\" }\n      it { assert_parse_error Float32, \"0x1p0f\", \"trailing characters\" }\n      it { assert_parse_error Float32, \"NaN \", \"expected '0'\" }\n      it { assert_parse_error Float32, \"- Infinity\", \"expected '0'\" }\n    end\n  end\n\n  describe \"#to_hexfloat\" do\n    describe \"special cases\" do\n      it { assert_to_s 0.0_f32, \"0x0p+0\" }\n      it { assert_to_s -0.0_f32, \"-0x0p+0\" }\n      it { assert_to_s Float32::INFINITY, \"Infinity\" }\n      it { assert_to_s -Float32::INFINITY, \"-Infinity\" }\n      it { assert_to_s Float32::NAN, \"NaN\" }\n      it { assert_to_s 1.4475098_f32, \"0x1.729p+0\" }\n      it { assert_to_s -1.4475098_f32, \"-0x1.729p+0\" }\n    end\n\n    describe \"corner cases\" do\n      it { assert_to_s 1.4472656_f32, \"0x1.728p+0\" }                         # instead of \"2.e5p-1\"\n      it { assert_to_s Float32::MIN_SUBNORMAL, \"0x0.000002p-126\" }           # instead of \"1p-1074\"\n      it { assert_to_s Float32::MIN_POSITIVE.prev_float, \"0x0.fffffep-126\" } # max subnormal\n      it { assert_to_s Float32::MIN_POSITIVE, \"0x1p-126\" }                   # min normal\n      it { assert_to_s Float32::MAX, \"0x1.fffffep+127\" }                     # max normal\n    end\n\n    describe \"exponents\" do\n      it { assert_to_s 1.540744e-33_f32, \"0x1p-109\" }\n      it { assert_to_s 1.5777218e-30_f32, \"0x1p-99\" }\n      it { assert_to_s 0.001953125_f32, \"0x1p-9\" }\n      it { assert_to_s 1.0_f32, \"0x1p+0\" }\n      it { assert_to_s 512.0_f32, \"0x1p+9\" }\n      it { assert_to_s 6.338253e+29_f32, \"0x1p+99\" }\n      it { assert_to_s 6.490371e+32_f32, \"0x1p+109\" }\n    end\n\n    describe \"hexits\" do\n      it { assert_to_s 1.0044403_f32, \"0x1.0123p+0\" }\n      it { assert_to_s 1.2711029_f32, \"0x1.4567p+0\" }\n      it { assert_to_s 1.5377655_f32, \"0x1.89abp+0\" }\n      it { assert_to_s 1.8044281_f32, \"0x1.cdefp+0\" }\n    end\n\n    describe \"trimming\" do\n      it { assert_to_s 1.0000006_f32, \"0x1.00000ap+0\" }\n      it { assert_to_s 1.0000095_f32, \"0x1.0000ap+0\" }\n      it { assert_to_s 1.0001526_f32, \"0x1.000ap+0\" }\n      it { assert_to_s 1.0390625_f32, \"0x1.0ap+0\" }\n      it { assert_to_s 1.0024414_f32, \"0x1.00ap+0\" }\n      it { assert_to_s 1.625_f32, \"0x1.ap+0\" }\n      it { assert_to_s 1.0_f32, \"0x1p+0\" }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/float_printer/ieee_spec.cr",
    "content": "# The example numbers in these specs are ported from the C++\n# \"double-conversions\" library. The following is their license:\n#   Copyright 2012 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\nrequire \"spec\"\n\nprivate def gen_bound(v : UInt64)\n  f = v.unsafe_as(Float64)\n  gen_bound(f)\nend\n\nprivate def gen_bound(v : UInt32)\n  f = v.unsafe_as(Float32)\n  gen_bound(f)\nend\n\nprivate def gen_bound(v : Float64 | Float32)\n  fp = Float::Printer::DiyFP.from_f_normalized(v)\n  b = Float::Printer::IEEE.normalized_boundaries(v)\n  b[:minus].exp.should eq fp.exp\n  b[:plus].exp.should eq fp.exp\n\n  return fp.frac, b[:minus].frac, b[:plus].frac\nend\n\ndescribe \"Float64 boundaries\" do\n  it \"boundaries 1.5\" do\n    fp, mi, pl = gen_bound(1.5)\n    # 1.5 does not have a significand of the form 2^p (for some p).\n    # Therefore its boundaries are at the same distance.\n    (pl - fp).should eq(fp - mi)\n    (fp - mi).should eq(1 << 10)\n  end\n\n  it \"boundaries 1.0\" do\n    fp, mi, pl = gen_bound(1.0)\n    # 1.0 does have a significand of the form 2^p (for some p).\n    # Therefore its lower boundary is twice as close as the upper boundary.\n    (pl - fp).should be > fp - mi\n    (fp - mi).should eq 1 << 9\n    (pl - fp).should eq 1 << 10\n  end\n\n  it \"boundaries min float64\" do\n    fp, mi, pl = gen_bound(0x0000000000000001_u64)\n    # min-value does not have a significand of the form 2^p (for some p).\n    # Therefore its boundaries are at the same distance.\n    (pl - fp).should eq fp - mi\n    (fp - mi).should eq 1_u64 << 62\n  end\n\n  it \"boundaries min normal f64\" do\n    fp, mi, pl = gen_bound(0x0010000000000000_u64)\n    # Even though the significand is of the form 2^p (for some p), its boundaries\n    # are at the same distance. (This is the only exception).\n    (fp - mi).should eq(pl - fp)\n    (fp - mi).should eq(1 << 10)\n  end\n\n  it \"boundaries max denormal f64\" do\n    fp, mi, pl = gen_bound(0x000FFFFFFFFFFFFF_u64)\n\n    (fp - mi).should eq(pl - fp)\n    (fp - mi).should eq(1 << 11)\n  end\n\n  it \"boundaries max f64\" do\n    fp, mi, pl = gen_bound(0x7fEFFFFFFFFFFFFF_u64)\n    # max-value does not have a significand of the form 2^p (for some p).\n    # Therefore its boundaries are at the same distance.\n    (fp - mi).should eq(pl - fp)\n    (fp - mi).should eq(1 << 10)\n  end\nend\n\ndescribe \"Float32 boundaries\" do\n  it \"boundaries 1.5\" do\n    fp, mi, pl = gen_bound(1.5_f32)\n    # 1.5 does not have a significand of the form 2^p (for some p).\n    # Therefore its boundaries are at the same distance.\n    (pl - fp).should eq(fp - mi)\n    # Normalization shifts the significand by 8 bits. Add 32 bits for the bigger\n    # data-type, and remove 1 because boundaries are at half a ULP.\n    (fp - mi).should eq(1_u64 << 39)\n  end\n\n  it \"boundaries 1.0\" do\n    fp, mi, pl = gen_bound(1.0_f32)\n    # 1.0 does have a significand of the form 2^p (for some p).\n    # Therefore its lower boundary is twice as close as the upper boundary.\n    (pl - fp).should be > fp - mi\n    (fp - mi).should eq(1_u64 << 38)\n    (pl - fp).should eq(1_u64 << 39)\n  end\n\n  it \"min Float32\" do\n    fp, mi, pl = gen_bound(0x00000001_u32)\n    #  min-value does not have a significand of the form 2^p (for some p).\n    # Therefore its boundaries are at the same distance.\n    (pl - fp).should eq(fp - mi)\n    # Denormals have their boundaries much closer.\n    (fp - mi).should eq(1_u64 << 62)\n  end\n\n  it \"smallest normal 32\" do\n    fp, mi, pl = gen_bound(0x00800000_u32)\n    # Even though the significand is of the form 2^p (for some p), its boundaries\n    # are at the same distance. (This is the only exception).\n    (pl - fp).should eq(fp - mi)\n    (fp - mi).should eq(1_u64 << 39)\n  end\n\n  it \"largest denormal 32\" do\n    fp, mi, pl = gen_bound(0x007FFFFF_u32)\n    (pl - fp).should eq(fp - mi)\n    (fp - mi).should eq(1_u64 << 40)\n  end\n\n  it \"max Float32\" do\n    fp, mi, pl = gen_bound(0x7F7FFFFF_u32)\n    # max-value does not have a significand of the form 2^p (for some p).\n    # Therefore its boundaries are at the same distance.\n    (pl - fp).should eq(fp - mi)\n    (fp - mi).should eq(1_u64 << 39)\n  end\nend\n"
  },
  {
    "path": "spec/std/float_printer/ryu_printf_spec.cr",
    "content": "{% skip_file unless String::Formatter::HAS_RYU_PRINTF %}\n\n# This file contains test cases derived from:\n#\n# * https://github.com/ulfjack/ryu\n# * https://github.com/microsoft/STL/tree/main/tests/std/tests/P0067R5_charconv\n#\n# The following is their license:\n#\n#   Copyright 2020-2021 Junekey Jeon\n#\n#   The contents of this file may be used under the terms of\n#   the Apache License v2.0 with LLVM Exceptions.\n#\n#      (See accompanying file LICENSE-Apache or copy at\n#       https://llvm.org/foundation/relicensing/LICENSE.txt)\n#\n#   Alternatively, the contents of this file may be used under the terms of\n#   the Boost Software License, Version 1.0.\n#      (See accompanying file LICENSE-Boost or copy at\n#       https://www.boost.org/LICENSE_1_0.txt)\n#\n#   Unless required by applicable law or agreed to in writing, this software\n#   is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n#   KIND, either express or implied.\n\nrequire \"spec\"\nrequire \"../../support/number\"\nrequire \"float/printer/ryu_printf\"\nrequire \"big\"\nrequire \"./ryu_printf_test_cases\"\n\nstruct BigFloat\n  def to_s_with_range(*, point_range : Range = -3..15)\n    to_s_impl(point_range: point_range, int_trailing_zeros: false)\n  end\n\n  def to_s_with_range(io : IO, *, point_range : Range = -3..15) : Nil\n    to_s_impl(io, point_range: point_range, int_trailing_zeros: false)\n  end\nend\n\nprivate def fixed_reference(value, precision)\n  if precision == 0\n    value.to_big_i.to_s\n  else\n    BigFloat.new(value, 4096).to_s_with_range(point_range: ..)\n  end\nend\n\nprivate def exp_reference(value, precision)\n  BigFloat.new(value, 4096).to_s_with_range(point_range: 0...0)\nend\n\nprivate def ieee_parts_to_f64(sign, exponent, mantissa)\n  ((sign ? 1_u64 << 63 : 0_u64) | (exponent.to_u64 << 52) | mantissa.to_u64).unsafe_as(Float64)\nend\n\nprivate macro expect_fixed(float, precision, string)\n  Float::Printer::RyuPrintf.d2fixed({{ float }}, {{ precision }}).should eq({{ string }})\nend\n\nprivate macro expect_exp(float, precision, string)\n  Float::Printer::RyuPrintf.d2exp({{ float }}, {{ precision }}).should eq({{ string }})\nend\n\nprivate macro expect_gen(float, precision, string, *, file = __FILE__, line = __LINE__)\n  Float::Printer::RyuPrintf.d2gen({{ float }}, {{ precision }}).should eq({{ string }}), file: {{ file }}, line: {{ line }}\nend\n\ndescribe Float::Printer::RyuPrintf do\n  describe \".d2fixed\" do\n    it \"Basic\" do\n      expect_fixed(\n        ieee_parts_to_f64(false, 1234, 99999), 0,\n        \"3291009114715486435425664845573426149758869524108446525879746560\")\n    end\n\n    it \"Zero\" do\n      expect_fixed(0.0, 4, \"0.0000\")\n      expect_fixed(0.0, 3, \"0.000\")\n      expect_fixed(0.0, 2, \"0.00\")\n      expect_fixed(0.0, 1, \"0.0\")\n      expect_fixed(0.0, 0, \"0\")\n    end\n\n    it \"MinMax\" do\n      expect_fixed(ieee_parts_to_f64(false, 0, 1), 1074,\n        \"0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625\")\n\n      expect_fixed(ieee_parts_to_f64(false, 2046, 0xFFFFFFFFFFFFFu64), 0,\n        \"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368\")\n    end\n\n    it \"AllPowersOfTen\" do\n      {% for tc in ALL_POWERS_OF_TEN %}\n        expect_fixed({{ tc[0] }}, {{ tc[1] }}, fixed_reference({{ tc[0] }}, {{ tc[1] }}))\n      {% end %}\n    end\n\n    it \"RoundToEven\" do\n      expect_fixed(0.125, 3, \"0.125\")\n      expect_fixed(0.125, 2, \"0.12\")\n      expect_fixed(0.375, 3, \"0.375\")\n      expect_fixed(0.375, 2, \"0.38\")\n    end\n\n    it \"RoundToEvenInteger\" do\n      expect_fixed(2.5, 1, \"2.5\")\n      expect_fixed(2.5, 0, \"2\")\n      expect_fixed(3.5, 1, \"3.5\")\n      expect_fixed(3.5, 0, \"4\")\n    end\n\n    it \"NonRoundToEvenScenarios\" do\n      expect_fixed(0.748046875, 3, \"0.748\")\n      expect_fixed(0.748046875, 2, \"0.75\")\n      expect_fixed(0.748046875, 1, \"0.7\") # 0.75 would round to \"0.8\", but this is smaller\n\n      expect_fixed(0.2509765625, 3, \"0.251\")\n      expect_fixed(0.2509765625, 2, \"0.25\")\n      expect_fixed(0.2509765625, 1, \"0.3\") # 0.25 would round to \"0.2\", but this is larger\n\n      expect_fixed(ieee_parts_to_f64(false, 1021, 1), 54, \"0.250000000000000055511151231257827021181583404541015625\")\n      expect_fixed(ieee_parts_to_f64(false, 1021, 1), 3, \"0.250\")\n      expect_fixed(ieee_parts_to_f64(false, 1021, 1), 2, \"0.25\")\n      expect_fixed(ieee_parts_to_f64(false, 1021, 1), 1, \"0.3\") # 0.25 would round to \"0.2\", but this is larger (again)\n    end\n\n    it \"VaryingPrecision\" do\n      expect_fixed(1729.142857142857, 47, \"1729.14285714285711037518922239542007446289062500000\")\n      expect_fixed(1729.142857142857, 46, \"1729.1428571428571103751892223954200744628906250000\")\n      expect_fixed(1729.142857142857, 45, \"1729.142857142857110375189222395420074462890625000\")\n      expect_fixed(1729.142857142857, 44, \"1729.14285714285711037518922239542007446289062500\")\n      expect_fixed(1729.142857142857, 43, \"1729.1428571428571103751892223954200744628906250\")\n      expect_fixed(1729.142857142857, 42, \"1729.142857142857110375189222395420074462890625\")\n      expect_fixed(1729.142857142857, 41, \"1729.14285714285711037518922239542007446289062\")\n      expect_fixed(1729.142857142857, 40, \"1729.1428571428571103751892223954200744628906\")\n      expect_fixed(1729.142857142857, 39, \"1729.142857142857110375189222395420074462891\")\n      expect_fixed(1729.142857142857, 38, \"1729.14285714285711037518922239542007446289\")\n      expect_fixed(1729.142857142857, 37, \"1729.1428571428571103751892223954200744629\")\n      expect_fixed(1729.142857142857, 36, \"1729.142857142857110375189222395420074463\")\n      expect_fixed(1729.142857142857, 35, \"1729.14285714285711037518922239542007446\")\n      expect_fixed(1729.142857142857, 34, \"1729.1428571428571103751892223954200745\")\n      expect_fixed(1729.142857142857, 33, \"1729.142857142857110375189222395420074\")\n      expect_fixed(1729.142857142857, 32, \"1729.14285714285711037518922239542007\")\n      expect_fixed(1729.142857142857, 31, \"1729.1428571428571103751892223954201\")\n      expect_fixed(1729.142857142857, 30, \"1729.142857142857110375189222395420\")\n      expect_fixed(1729.142857142857, 29, \"1729.14285714285711037518922239542\")\n      expect_fixed(1729.142857142857, 28, \"1729.1428571428571103751892223954\")\n      expect_fixed(1729.142857142857, 27, \"1729.142857142857110375189222395\")\n      expect_fixed(1729.142857142857, 26, \"1729.14285714285711037518922240\")\n      expect_fixed(1729.142857142857, 25, \"1729.1428571428571103751892224\")\n      expect_fixed(1729.142857142857, 24, \"1729.142857142857110375189222\")\n      expect_fixed(1729.142857142857, 23, \"1729.14285714285711037518922\")\n      expect_fixed(1729.142857142857, 22, \"1729.1428571428571103751892\")\n      expect_fixed(1729.142857142857, 21, \"1729.142857142857110375189\")\n      expect_fixed(1729.142857142857, 20, \"1729.14285714285711037519\")\n      expect_fixed(1729.142857142857, 19, \"1729.1428571428571103752\")\n      expect_fixed(1729.142857142857, 18, \"1729.142857142857110375\")\n      expect_fixed(1729.142857142857, 17, \"1729.14285714285711038\")\n      expect_fixed(1729.142857142857, 16, \"1729.1428571428571104\")\n      expect_fixed(1729.142857142857, 15, \"1729.142857142857110\")\n      expect_fixed(1729.142857142857, 14, \"1729.14285714285711\")\n      expect_fixed(1729.142857142857, 13, \"1729.1428571428571\")\n      expect_fixed(1729.142857142857, 12, \"1729.142857142857\")\n      expect_fixed(1729.142857142857, 11, \"1729.14285714286\")\n      expect_fixed(1729.142857142857, 10, \"1729.1428571429\")\n      expect_fixed(1729.142857142857, 9, \"1729.142857143\")\n      expect_fixed(1729.142857142857, 8, \"1729.14285714\")\n      expect_fixed(1729.142857142857, 7, \"1729.1428571\")\n      expect_fixed(1729.142857142857, 6, \"1729.142857\")\n      expect_fixed(1729.142857142857, 5, \"1729.14286\")\n      expect_fixed(1729.142857142857, 4, \"1729.1429\")\n      expect_fixed(1729.142857142857, 3, \"1729.143\")\n      expect_fixed(1729.142857142857, 2, \"1729.14\")\n      expect_fixed(1729.142857142857, 1, \"1729.1\")\n      expect_fixed(1729.142857142857, 0, \"1729\")\n    end\n\n    it \"Carrying\" do\n      expect_fixed(0.0009, 4, \"0.0009\")\n      expect_fixed(0.0009, 3, \"0.001\")\n      expect_fixed(0.0029, 4, \"0.0029\")\n      expect_fixed(0.0029, 3, \"0.003\")\n      expect_fixed(0.0099, 4, \"0.0099\")\n      expect_fixed(0.0099, 3, \"0.010\")\n      expect_fixed(0.0299, 4, \"0.0299\")\n      expect_fixed(0.0299, 3, \"0.030\")\n      expect_fixed(0.0999, 4, \"0.0999\")\n      expect_fixed(0.0999, 3, \"0.100\")\n      expect_fixed(0.2999, 4, \"0.2999\")\n      expect_fixed(0.2999, 3, \"0.300\")\n      expect_fixed(0.9999, 4, \"0.9999\")\n      expect_fixed(0.9999, 3, \"1.000\")\n      expect_fixed(2.9999, 4, \"2.9999\")\n      expect_fixed(2.9999, 3, \"3.000\")\n      expect_fixed(9.9999, 4, \"9.9999\")\n      expect_fixed(9.9999, 3, \"10.000\")\n      expect_fixed(29.9999, 4, \"29.9999\")\n      expect_fixed(29.9999, 3, \"30.000\")\n      expect_fixed(99.9999, 4, \"99.9999\")\n      expect_fixed(99.9999, 3, \"100.000\")\n      expect_fixed(299.9999, 4, \"299.9999\")\n      expect_fixed(299.9999, 3, \"300.000\")\n\n      expect_fixed(0.09, 2, \"0.09\")\n      expect_fixed(0.09, 1, \"0.1\")\n      expect_fixed(0.29, 2, \"0.29\")\n      expect_fixed(0.29, 1, \"0.3\")\n      expect_fixed(0.99, 2, \"0.99\")\n      expect_fixed(0.99, 1, \"1.0\")\n      expect_fixed(2.99, 2, \"2.99\")\n      expect_fixed(2.99, 1, \"3.0\")\n      expect_fixed(9.99, 2, \"9.99\")\n      expect_fixed(9.99, 1, \"10.0\")\n      expect_fixed(29.99, 2, \"29.99\")\n      expect_fixed(29.99, 1, \"30.0\")\n      expect_fixed(99.99, 2, \"99.99\")\n      expect_fixed(99.99, 1, \"100.0\")\n      expect_fixed(299.99, 2, \"299.99\")\n      expect_fixed(299.99, 1, \"300.0\")\n\n      expect_fixed(0.9, 1, \"0.9\")\n      expect_fixed(0.9, 0, \"1\")\n      expect_fixed(2.9, 1, \"2.9\")\n      expect_fixed(2.9, 0, \"3\")\n      expect_fixed(9.9, 1, \"9.9\")\n      expect_fixed(9.9, 0, \"10\")\n      expect_fixed(29.9, 1, \"29.9\")\n      expect_fixed(29.9, 0, \"30\")\n      expect_fixed(99.9, 1, \"99.9\")\n      expect_fixed(99.9, 0, \"100\")\n      expect_fixed(299.9, 1, \"299.9\")\n      expect_fixed(299.9, 0, \"300\")\n    end\n\n    it \"RoundingResultZero\" do\n      expect_fixed(0.004, 3, \"0.004\")\n      expect_fixed(0.004, 2, \"0.00\")\n      expect_fixed(0.4, 1, \"0.4\")\n      expect_fixed(0.4, 0, \"0\")\n      expect_fixed(0.5, 1, \"0.5\")\n      expect_fixed(0.5, 0, \"0\")\n    end\n\n    it \"AllBinaryExponents\" do\n      {% for tc in ALL_BINARY_EXPONENTS %}\n        expect_fixed({{ tc[0] }}, {{ tc[1] }}, fixed_reference({{ tc[0] }}, {{ tc[1] }}))\n      {% end %}\n    end\n\n    it \"Regression\" do\n      expect_fixed(7.018232e-82, 6, \"0.000000\")\n    end\n  end\n\n  describe \".d2exp\" do\n    it \"Basic\" do\n      expect_exp(ieee_parts_to_f64(false, 1234, 99999), 62,\n        \"3.29100911471548643542566484557342614975886952410844652587974656e+63\")\n    end\n\n    it \"Zero\" do\n      expect_exp(0.0, 4, \"0.0000e+0\")\n      expect_exp(0.0, 3, \"0.000e+0\")\n      expect_exp(0.0, 2, \"0.00e+0\")\n      expect_exp(0.0, 1, \"0.0e+0\")\n      expect_exp(0.0, 0, \"0e+0\")\n    end\n\n    it \"MinMax\" do\n      expect_exp(ieee_parts_to_f64(false, 0, 1), 750,\n        \"4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625e-324\")\n\n      expect_exp(ieee_parts_to_f64(false, 2046, 0xFFFFFFFFFFFFFu64), 308,\n        \"1.79769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368e+308\")\n    end\n\n    it \"AllPowersOfTen\" do\n      {% for tc in ALL_POWERS_OF_TEN %}\n        expect_exp({{ tc[0] }}, {{ tc[2] }}, exp_reference({{ tc[0] }}, {{ tc[2] }}))\n      {% end %}\n    end\n\n    it \"RoundToEven\" do\n      expect_exp(0.125, 2, \"1.25e-1\")\n      expect_exp(0.125, 1, \"1.2e-1\")\n      expect_exp(0.375, 2, \"3.75e-1\")\n      expect_exp(0.375, 1, \"3.8e-1\")\n    end\n\n    it \"RoundToEvenInteger\" do\n      expect_exp(2.5, 1, \"2.5e+0\")\n      expect_exp(2.5, 0, \"2e+0\")\n      expect_exp(3.5, 1, \"3.5e+0\")\n      expect_exp(3.5, 0, \"4e+0\")\n    end\n\n    it \"NonRoundToEvenScenarios\" do\n      expect_exp(0.748046875, 2, \"7.48e-1\")\n      expect_exp(0.748046875, 1, \"7.5e-1\")\n      expect_exp(0.748046875, 0, \"7e-1\") # 0.75 would round to \"8e-1\", but this is smaller\n\n      expect_exp(0.2509765625, 2, \"2.51e-1\")\n      expect_exp(0.2509765625, 1, \"2.5e-1\")\n      expect_exp(0.2509765625, 0, \"3e-1\") # 0.25 would round to \"2e-1\", but this is larger\n\n      expect_exp(ieee_parts_to_f64(false, 1021, 1), 53, \"2.50000000000000055511151231257827021181583404541015625e-1\")\n      expect_exp(ieee_parts_to_f64(false, 1021, 1), 2, \"2.50e-1\")\n      expect_exp(ieee_parts_to_f64(false, 1021, 1), 1, \"2.5e-1\")\n      expect_exp(ieee_parts_to_f64(false, 1021, 1), 0, \"3e-1\") # 0.25 would round to \"2e-1\", but this is larger (again)\n    end\n\n    it \"VaryingPrecision\" do\n      expect_exp(1729.142857142857, 50, \"1.72914285714285711037518922239542007446289062500000e+3\")\n      expect_exp(1729.142857142857, 49, \"1.7291428571428571103751892223954200744628906250000e+3\")\n      expect_exp(1729.142857142857, 48, \"1.729142857142857110375189222395420074462890625000e+3\")\n      expect_exp(1729.142857142857, 47, \"1.72914285714285711037518922239542007446289062500e+3\")\n      expect_exp(1729.142857142857, 46, \"1.7291428571428571103751892223954200744628906250e+3\")\n      expect_exp(1729.142857142857, 45, \"1.729142857142857110375189222395420074462890625e+3\")\n      expect_exp(1729.142857142857, 44, \"1.72914285714285711037518922239542007446289062e+3\")\n      expect_exp(1729.142857142857, 43, \"1.7291428571428571103751892223954200744628906e+3\")\n      expect_exp(1729.142857142857, 42, \"1.729142857142857110375189222395420074462891e+3\")\n      expect_exp(1729.142857142857, 41, \"1.72914285714285711037518922239542007446289e+3\")\n      expect_exp(1729.142857142857, 40, \"1.7291428571428571103751892223954200744629e+3\")\n      expect_exp(1729.142857142857, 39, \"1.729142857142857110375189222395420074463e+3\")\n      expect_exp(1729.142857142857, 38, \"1.72914285714285711037518922239542007446e+3\")\n      expect_exp(1729.142857142857, 37, \"1.7291428571428571103751892223954200745e+3\")\n      expect_exp(1729.142857142857, 36, \"1.729142857142857110375189222395420074e+3\")\n      expect_exp(1729.142857142857, 35, \"1.72914285714285711037518922239542007e+3\")\n      expect_exp(1729.142857142857, 34, \"1.7291428571428571103751892223954201e+3\")\n      expect_exp(1729.142857142857, 33, \"1.729142857142857110375189222395420e+3\")\n      expect_exp(1729.142857142857, 32, \"1.72914285714285711037518922239542e+3\")\n      expect_exp(1729.142857142857, 31, \"1.7291428571428571103751892223954e+3\")\n      expect_exp(1729.142857142857, 30, \"1.729142857142857110375189222395e+3\")\n      expect_exp(1729.142857142857, 29, \"1.72914285714285711037518922240e+3\")\n      expect_exp(1729.142857142857, 28, \"1.7291428571428571103751892224e+3\")\n      expect_exp(1729.142857142857, 27, \"1.729142857142857110375189222e+3\")\n      expect_exp(1729.142857142857, 26, \"1.72914285714285711037518922e+3\")\n      expect_exp(1729.142857142857, 25, \"1.7291428571428571103751892e+3\")\n      expect_exp(1729.142857142857, 24, \"1.729142857142857110375189e+3\")\n      expect_exp(1729.142857142857, 23, \"1.72914285714285711037519e+3\")\n      expect_exp(1729.142857142857, 22, \"1.7291428571428571103752e+3\")\n      expect_exp(1729.142857142857, 21, \"1.729142857142857110375e+3\")\n      expect_exp(1729.142857142857, 20, \"1.72914285714285711038e+3\")\n      expect_exp(1729.142857142857, 19, \"1.7291428571428571104e+3\")\n      expect_exp(1729.142857142857, 18, \"1.729142857142857110e+3\")\n      expect_exp(1729.142857142857, 17, \"1.72914285714285711e+3\")\n      expect_exp(1729.142857142857, 16, \"1.7291428571428571e+3\")\n      expect_exp(1729.142857142857, 15, \"1.729142857142857e+3\")\n      expect_exp(1729.142857142857, 14, \"1.72914285714286e+3\")\n      expect_exp(1729.142857142857, 13, \"1.7291428571429e+3\")\n      expect_exp(1729.142857142857, 12, \"1.729142857143e+3\")\n      expect_exp(1729.142857142857, 11, \"1.72914285714e+3\")\n      expect_exp(1729.142857142857, 10, \"1.7291428571e+3\")\n      expect_exp(1729.142857142857, 9, \"1.729142857e+3\")\n      expect_exp(1729.142857142857, 8, \"1.72914286e+3\")\n      expect_exp(1729.142857142857, 7, \"1.7291429e+3\")\n      expect_exp(1729.142857142857, 6, \"1.729143e+3\")\n      expect_exp(1729.142857142857, 5, \"1.72914e+3\")\n      expect_exp(1729.142857142857, 4, \"1.7291e+3\")\n      expect_exp(1729.142857142857, 3, \"1.729e+3\")\n      expect_exp(1729.142857142857, 2, \"1.73e+3\")\n      expect_exp(1729.142857142857, 1, \"1.7e+3\")\n      expect_exp(1729.142857142857, 0, \"2e+3\")\n    end\n\n    it \"Carrying\" do\n      expect_exp(2.0009, 4, \"2.0009e+0\")\n      expect_exp(2.0009, 3, \"2.001e+0\")\n      expect_exp(2.0029, 4, \"2.0029e+0\")\n      expect_exp(2.0029, 3, \"2.003e+0\")\n      expect_exp(2.0099, 4, \"2.0099e+0\")\n      expect_exp(2.0099, 3, \"2.010e+0\")\n      expect_exp(2.0299, 4, \"2.0299e+0\")\n      expect_exp(2.0299, 3, \"2.030e+0\")\n      expect_exp(2.0999, 4, \"2.0999e+0\")\n      expect_exp(2.0999, 3, \"2.100e+0\")\n      expect_exp(2.2999, 4, \"2.2999e+0\")\n      expect_exp(2.2999, 3, \"2.300e+0\")\n      expect_exp(2.9999, 4, \"2.9999e+0\")\n      expect_exp(2.9999, 3, \"3.000e+0\")\n      expect_exp(9.9999, 4, \"9.9999e+0\")\n      expect_exp(9.9999, 3, \"1.000e+1\")\n\n      expect_exp(2.09, 2, \"2.09e+0\")\n      expect_exp(2.09, 1, \"2.1e+0\")\n      expect_exp(2.29, 2, \"2.29e+0\")\n      expect_exp(2.29, 1, \"2.3e+0\")\n      expect_exp(2.99, 2, \"2.99e+0\")\n      expect_exp(2.99, 1, \"3.0e+0\")\n      expect_exp(9.99, 2, \"9.99e+0\")\n      expect_exp(9.99, 1, \"1.0e+1\")\n\n      expect_exp(2.9, 1, \"2.9e+0\")\n      expect_exp(2.9, 0, \"3e+0\")\n      expect_exp(9.9, 1, \"9.9e+0\")\n      expect_exp(9.9, 0, \"1e+1\")\n    end\n\n    it \"Exponents\" do\n      expect_exp(9.99e-100, 2, \"9.99e-100\")\n      expect_exp(9.99e-99, 2, \"9.99e-99\")\n      expect_exp(9.99e-10, 2, \"9.99e-10\")\n      expect_exp(9.99e-9, 2, \"9.99e-9\")\n      expect_exp(9.99e-1, 2, \"9.99e-1\")\n      expect_exp(9.99e+0, 2, \"9.99e+0\")\n      expect_exp(9.99e+1, 2, \"9.99e+1\")\n      expect_exp(9.99e+9, 2, \"9.99e+9\")\n      expect_exp(9.99e+10, 2, \"9.99e+10\")\n      expect_exp(9.99e+99, 2, \"9.99e+99\")\n      expect_exp(9.99e+100, 2, \"9.99e+100\")\n\n      expect_exp(9.99e-100, 1, \"1.0e-99\")\n      expect_exp(9.99e-99, 1, \"1.0e-98\")\n      expect_exp(9.99e-10, 1, \"1.0e-9\")\n      expect_exp(9.99e-9, 1, \"1.0e-8\")\n      expect_exp(9.99e-1, 1, \"1.0e+0\")\n      expect_exp(9.99e+0, 1, \"1.0e+1\")\n      expect_exp(9.99e+1, 1, \"1.0e+2\")\n      expect_exp(9.99e+9, 1, \"1.0e+10\")\n      expect_exp(9.99e+10, 1, \"1.0e+11\")\n      expect_exp(9.99e+99, 1, \"1.0e+100\")\n      expect_exp(9.99e+100, 1, \"1.0e+101\")\n    end\n\n    it \"AllBinaryExponents\" do\n      {% for tc in ALL_BINARY_EXPONENTS %}\n        expect_exp({{ tc[0] }}, {{ tc[2] }}, exp_reference({{ tc[0] }}, {{ tc[2] }}))\n      {% end %}\n    end\n\n    it \"PrintDecimalPoint\" do\n      # These values exercise each codepath.\n      expect_exp(1e+54, 0, \"1e+54\")\n      expect_exp(1e+54, 1, \"1.0e+54\")\n      expect_exp(1e-63, 0, \"1e-63\")\n      expect_exp(1e-63, 1, \"1.0e-63\")\n      expect_exp(1e+83, 0, \"1e+83\")\n      expect_exp(1e+83, 1, \"1.0e+83\")\n    end\n  end\n\n  describe \".d2gen\" do\n    it \"Basic\" do\n      expect_gen(0.0, 4, \"0\")\n      expect_gen(1.729, 4, \"1.729\")\n    end\n\n    it \"corner cases\" do\n      expect_gen(Float64::MIN_SUBNORMAL, 1000,\n        \"4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625e-324\")\n      expect_gen(Float64::MIN_POSITIVE.prev_float, 1000,\n        \"2.2250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375e-308\")\n      expect_gen(Float64::MIN_POSITIVE, 1000,\n        \"2.225073858507201383090232717332404064219215980462331830553327416887204434813918195854283159012511020564067339731035811005152434161553460108856012385377718821130777993532002330479610147442583636071921565046942503734208375250806650616658158948720491179968591639648500635908770118304874799780887753749949451580451605050915399856582470818645113537935804992115981085766051992433352114352390148795699609591288891602992641511063466313393663477586513029371762047325631781485664350872122828637642044846811407613911477062801689853244110024161447421618567166150540154285084716752901903161322778896729707373123334086988983175067838846926092773977972858659654941091369095406136467568702398678315290680984617210924625396728515625e-308\")\n      expect_gen(Float64::MAX, 1000,\n        \"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368\")\n\n      expect_gen(Float64::MIN_SUBNORMAL, 6, \"4.94066e-324\")\n      expect_gen(Float64::MIN_POSITIVE.prev_float, 6, \"2.22507e-308\")\n      expect_gen(Float64::MIN_POSITIVE, 6, \"2.22507e-308\")\n      expect_gen(Float64::MAX, 6, \"1.79769e+308\")\n    end\n\n    it \"maximum-length output\" do\n      expect_gen(hexfloat(\"0x1.fffffffffffffp-1022\"), 1000,\n        \"4.4501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375e-308\")\n      expect_gen(hexfloat(\"0x1.fffffffffffffp-14\"), 1000,\n        \"0.000122070312499999986447472843931194574906839989125728607177734375\")\n    end\n\n    it \"varying precision\" do\n      expect_gen(hexfloat(\"0x1.b04p0\"), 0, \"2\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 1, \"2\") # fixed notation trims decimal point\n      expect_gen(hexfloat(\"0x1.b04p0\"), 2, \"1.7\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 3, \"1.69\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 4, \"1.688\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 5, \"1.6885\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 6, \"1.68848\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 7, \"1.688477\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 8, \"1.6884766\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 9, \"1.68847656\")\n      expect_gen(hexfloat(\"0x1.b04p0\"), 10, \"1.688476562\")  # round to even\n      expect_gen(hexfloat(\"0x1.b04p0\"), 11, \"1.6884765625\") # exact\n      expect_gen(hexfloat(\"0x1.b04p0\"), 12, \"1.6884765625\") # trim trailing zeros\n      expect_gen(hexfloat(\"0x1.b04p0\"), 13, \"1.6884765625\")\n\n      expect_gen(hexfloat(\"0x1.8p-15\"), 0, \"5e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 1, \"5e-5\") # scientific notation trims decimal point\n      expect_gen(hexfloat(\"0x1.8p-15\"), 2, \"4.6e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 3, \"4.58e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 4, \"4.578e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 5, \"4.5776e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 6, \"4.57764e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 7, \"4.577637e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 8, \"4.5776367e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 9, \"4.57763672e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 10, \"4.577636719e-5\")\n      expect_gen(hexfloat(\"0x1.8p-15\"), 11, \"4.5776367188e-5\")  # round to even\n      expect_gen(hexfloat(\"0x1.8p-15\"), 12, \"4.57763671875e-5\") # exact\n      expect_gen(hexfloat(\"0x1.8p-15\"), 13, \"4.57763671875e-5\") # trim trailing zeros\n      expect_gen(hexfloat(\"0x1.8p-15\"), 14, \"4.57763671875e-5\")\n    end\n\n    it \"trim trailing zeros\" do\n      expect_gen(hexfloat(\"0x1.80015p0\"), 1, \"2\") # fixed notation trims decimal point\n      expect_gen(hexfloat(\"0x1.80015p0\"), 2, \"1.5\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 3, \"1.5\") # general trims trailing zeros\n      expect_gen(hexfloat(\"0x1.80015p0\"), 4, \"1.5\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 5, \"1.5\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 6, \"1.50002\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 7, \"1.50002\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 8, \"1.50002\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 9, \"1.50002003\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 10, \"1.500020027\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 11, \"1.5000200272\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 12, \"1.50002002716\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 13, \"1.500020027161\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 14, \"1.5000200271606\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 15, \"1.50002002716064\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 16, \"1.500020027160645\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 17, \"1.5000200271606445\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 18, \"1.50002002716064453\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 19, \"1.500020027160644531\")\n      expect_gen(hexfloat(\"0x1.80015p0\"), 20, \"1.5000200271606445312\")  # round to even\n      expect_gen(hexfloat(\"0x1.80015p0\"), 21, \"1.50002002716064453125\") # exact\n    end\n\n    it \"trim trailing zeros and decimal point\" do\n      expect_gen(hexfloat(\"0x1.00015p0\"), 1, \"1\") # fixed notation trims decimal point\n      expect_gen(hexfloat(\"0x1.00015p0\"), 2, \"1\") # general trims decimal point and trailing zeros\n      expect_gen(hexfloat(\"0x1.00015p0\"), 3, \"1\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 4, \"1\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 5, \"1\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 6, \"1.00002\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 7, \"1.00002\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 8, \"1.00002\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 9, \"1.00002003\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 10, \"1.000020027\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 11, \"1.0000200272\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 12, \"1.00002002716\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 13, \"1.000020027161\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 14, \"1.0000200271606\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 15, \"1.00002002716064\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 16, \"1.000020027160645\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 17, \"1.0000200271606445\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 18, \"1.00002002716064453\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 19, \"1.000020027160644531\")\n      expect_gen(hexfloat(\"0x1.00015p0\"), 20, \"1.0000200271606445312\")  # round to even\n      expect_gen(hexfloat(\"0x1.00015p0\"), 21, \"1.00002002716064453125\") # exact\n    end\n\n    it \"trim trailing zeros, scientific notation\" do\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 1, \"1e-6\") # scientific notation trims decimal point\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 2, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 3, \"1.3e-6\") # general trims trailing zeros\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 4, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 5, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 6, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 7, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 8, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 9, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 10, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 11, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 12, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 13, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 14, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 15, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 16, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 17, \"1.3e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 18, \"1.30000000000000005e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 19, \"1.300000000000000047e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 20, \"1.3000000000000000471e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 21, \"1.30000000000000004705e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 22, \"1.300000000000000047052e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 23, \"1.3000000000000000470517e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 24, \"1.30000000000000004705166e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 25, \"1.300000000000000047051664e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 26, \"1.3000000000000000470516638e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 27, \"1.30000000000000004705166378e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 28, \"1.30000000000000004705166378e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 29, \"1.3000000000000000470516637804e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 30, \"1.30000000000000004705166378044e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 31, \"1.30000000000000004705166378044e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 32, \"1.3000000000000000470516637804397e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 33, \"1.30000000000000004705166378043968e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 34, \"1.300000000000000047051663780439679e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 35, \"1.3000000000000000470516637804396787e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 36, \"1.30000000000000004705166378043967867e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 37, \"1.300000000000000047051663780439678675e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 38, \"1.3000000000000000470516637804396786748e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 39, \"1.30000000000000004705166378043967867484e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 40, \"1.300000000000000047051663780439678674838e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 41, \"1.3000000000000000470516637804396786748384e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 42, \"1.30000000000000004705166378043967867483843e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 43, \"1.300000000000000047051663780439678674838433e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 44, \"1.3000000000000000470516637804396786748384329e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 45, \"1.30000000000000004705166378043967867483843293e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 46, \"1.300000000000000047051663780439678674838432926e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 47, \"1.3000000000000000470516637804396786748384329258e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 48, \"1.30000000000000004705166378043967867483843292575e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 49, \"1.300000000000000047051663780439678674838432925753e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 50, \"1.3000000000000000470516637804396786748384329257533e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 51, \"1.3000000000000000470516637804396786748384329257533e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 52, \"1.300000000000000047051663780439678674838432925753295e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 53, \"1.3000000000000000470516637804396786748384329257532954e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 54, \"1.30000000000000004705166378043967867483843292575329542e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 55, \"1.300000000000000047051663780439678674838432925753295422e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 56, \"1.3000000000000000470516637804396786748384329257532954216e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 57, \"1.3000000000000000470516637804396786748384329257532954216e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 58, \"1.3000000000000000470516637804396786748384329257532954216e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 59, \"1.3000000000000000470516637804396786748384329257532954216003e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 60, \"1.30000000000000004705166378043967867483843292575329542160034e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 61, \"1.300000000000000047051663780439678674838432925753295421600342e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 62, \"1.3000000000000000470516637804396786748384329257532954216003418e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 63, \"1.3000000000000000470516637804396786748384329257532954216003418e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 64, \"1.300000000000000047051663780439678674838432925753295421600341797e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 65, \"1.3000000000000000470516637804396786748384329257532954216003417969e-6\")\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 66, \"1.30000000000000004705166378043967867483843292575329542160034179688e-6\")  # round to even\n      expect_gen(hexfloat(\"0x1.5cf751db94e6bp-20\"), 67, \"1.300000000000000047051663780439678674838432925753295421600341796875e-6\") # exact\n    end\n\n    it \"trim trailing zeros and decimal point, scientific notation\" do\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 1, \"3e-6\") # scientific notation trims decimal point\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 2, \"3e-6\") # general trims decimal point and trailing zeros\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 3, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 4, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 5, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 6, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 7, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 8, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 9, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 10, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 11, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 12, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 13, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 14, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 15, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 16, \"3e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 17, \"3.0000000000000001e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 18, \"3.00000000000000008e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 19, \"3.000000000000000076e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 20, \"3.000000000000000076e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 21, \"3.000000000000000076e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 22, \"3.000000000000000076003e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 23, \"3.0000000000000000760026e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 24, \"3.00000000000000007600257e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 25, \"3.000000000000000076002572e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 26, \"3.0000000000000000760025723e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 27, \"3.00000000000000007600257229e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 28, \"3.000000000000000076002572291e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 29, \"3.0000000000000000760025722912e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 30, \"3.00000000000000007600257229123e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 31, \"3.000000000000000076002572291234e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 32, \"3.0000000000000000760025722912339e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 33, \"3.00000000000000007600257229123386e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 34, \"3.000000000000000076002572291233861e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 35, \"3.0000000000000000760025722912338608e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 36, \"3.00000000000000007600257229123386082e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 37, \"3.000000000000000076002572291233860824e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 38, \"3.0000000000000000760025722912338608239e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 39, \"3.00000000000000007600257229123386082392e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 40, \"3.000000000000000076002572291233860823922e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 41, \"3.0000000000000000760025722912338608239224e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 42, \"3.00000000000000007600257229123386082392244e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 43, \"3.000000000000000076002572291233860823922441e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 44, \"3.0000000000000000760025722912338608239224413e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 45, \"3.00000000000000007600257229123386082392244134e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 46, \"3.000000000000000076002572291233860823922441341e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 47, \"3.000000000000000076002572291233860823922441341e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 48, \"3.00000000000000007600257229123386082392244134098e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 49, \"3.000000000000000076002572291233860823922441340983e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 50, \"3.0000000000000000760025722912338608239224413409829e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 51, \"3.00000000000000007600257229123386082392244134098291e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 52, \"3.000000000000000076002572291233860823922441340982914e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 53, \"3.000000000000000076002572291233860823922441340982914e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 54, \"3.00000000000000007600257229123386082392244134098291397e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 55, \"3.000000000000000076002572291233860823922441340982913971e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 56, \"3.0000000000000000760025722912338608239224413409829139709e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 57, \"3.00000000000000007600257229123386082392244134098291397095e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 58, \"3.000000000000000076002572291233860823922441340982913970947e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 59, \"3.0000000000000000760025722912338608239224413409829139709473e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 60, \"3.00000000000000007600257229123386082392244134098291397094727e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 61, \"3.000000000000000076002572291233860823922441340982913970947266e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 62, \"3.0000000000000000760025722912338608239224413409829139709472656e-6\")\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 63, \"3.00000000000000007600257229123386082392244134098291397094726562e-6\")  # round to even\n      expect_gen(hexfloat(\"0x1.92a737110e454p-19\"), 64, \"3.000000000000000076002572291233860823922441340982913970947265625e-6\") # exact\n    end\n\n    it \"large precision with fixed notation and scientific notation\" do\n      expect_gen(hexfloat(\"0x1.ba9fbe76c8b44p+0\"), 5000, \"1.72900000000000009237055564881302416324615478515625\")\n      expect_gen(hexfloat(\"0x1.d01ff9abb93d1p-20\"), 5000, \"1.729000000000000090107283613749533657255597063340246677398681640625e-6\")\n    end\n\n    it \"transitions between fixed notation and scientific notation\" do\n      expect_gen(5555555.0, 1, \"6e+6\")\n      expect_gen(555555.0, 1, \"6e+5\")\n      expect_gen(55555.0, 1, \"6e+4\")\n      expect_gen(5555.0, 1, \"6e+3\")\n      expect_gen(555.0, 1, \"6e+2\")\n      expect_gen(55.0, 1, \"6e+1\") # round to even\n      expect_gen(5.0, 1, \"5\")\n      expect_gen(hexfloat(\"0x1p-3\"), 1, \"0.1\")     # 0.125\n      expect_gen(hexfloat(\"0x1p-6\"), 1, \"0.02\")    # 0.015625\n      expect_gen(hexfloat(\"0x1p-9\"), 1, \"0.002\")   # 0.001953125\n      expect_gen(hexfloat(\"0x1p-13\"), 1, \"0.0001\") # 0.0001220703125\n      expect_gen(hexfloat(\"0x1p-16\"), 1, \"2e-5\")   # 1.52587890625e-5\n      expect_gen(hexfloat(\"0x1p-19\"), 1, \"2e-6\")   # 1.9073486328125e-6\n\n      expect_gen(5555555.0, 2, \"5.6e+6\")\n      expect_gen(555555.0, 2, \"5.6e+5\")\n      expect_gen(55555.0, 2, \"5.6e+4\")\n      expect_gen(5555.0, 2, \"5.6e+3\")\n      expect_gen(555.0, 2, \"5.6e+2\") # round to even\n      expect_gen(55.0, 2, \"55\")\n      expect_gen(5.0, 2, \"5\")\n      expect_gen(hexfloat(\"0x1p-3\"), 2, \"0.12\") # round to even\n      expect_gen(hexfloat(\"0x1p-6\"), 2, \"0.016\")\n      expect_gen(hexfloat(\"0x1p-9\"), 2, \"0.002\")\n      expect_gen(hexfloat(\"0x1p-13\"), 2, \"0.00012\")\n      expect_gen(hexfloat(\"0x1p-16\"), 2, \"1.5e-5\")\n      expect_gen(hexfloat(\"0x1p-19\"), 2, \"1.9e-6\")\n\n      expect_gen(5555555.0, 3, \"5.56e+6\")\n      expect_gen(555555.0, 3, \"5.56e+5\")\n      expect_gen(55555.0, 3, \"5.56e+4\")\n      expect_gen(5555.0, 3, \"5.56e+3\") # round to even\n      expect_gen(555.0, 3, \"555\")\n      expect_gen(55.0, 3, \"55\")\n      expect_gen(5.0, 3, \"5\")\n      expect_gen(hexfloat(\"0x1p-3\"), 3, \"0.125\")\n      expect_gen(hexfloat(\"0x1p-6\"), 3, \"0.0156\")\n      expect_gen(hexfloat(\"0x1p-9\"), 3, \"0.00195\")\n      expect_gen(hexfloat(\"0x1p-13\"), 3, \"0.000122\")\n      expect_gen(hexfloat(\"0x1p-16\"), 3, \"1.53e-5\")\n      expect_gen(hexfloat(\"0x1p-19\"), 3, \"1.91e-6\")\n\n      expect_gen(5555555.0, 4, \"5.556e+6\")\n      expect_gen(555555.0, 4, \"5.556e+5\")\n      expect_gen(55555.0, 4, \"5.556e+4\") # round to even\n      expect_gen(5555.0, 4, \"5555\")\n      expect_gen(555.0, 4, \"555\")\n      expect_gen(55.0, 4, \"55\")\n      expect_gen(5.0, 4, \"5\")\n      expect_gen(hexfloat(\"0x1p-3\"), 4, \"0.125\")\n      expect_gen(hexfloat(\"0x1p-6\"), 4, \"0.01562\") # round to even\n      expect_gen(hexfloat(\"0x1p-9\"), 4, \"0.001953\")\n      expect_gen(hexfloat(\"0x1p-13\"), 4, \"0.0001221\")\n      expect_gen(hexfloat(\"0x1p-16\"), 4, \"1.526e-5\")\n      expect_gen(hexfloat(\"0x1p-19\"), 4, \"1.907e-6\")\n\n      expect_gen(5555555.0, 5, \"5.5556e+6\")\n      expect_gen(555555.0, 5, \"5.5556e+5\") # round to even\n      expect_gen(55555.0, 5, \"55555\")\n      expect_gen(5555.0, 5, \"5555\")\n      expect_gen(555.0, 5, \"555\")\n      expect_gen(55.0, 5, \"55\")\n      expect_gen(5.0, 5, \"5\")\n      expect_gen(hexfloat(\"0x1p-3\"), 5, \"0.125\")\n      expect_gen(hexfloat(\"0x1p-6\"), 5, \"0.015625\")\n      expect_gen(hexfloat(\"0x1p-9\"), 5, \"0.0019531\")\n      expect_gen(hexfloat(\"0x1p-13\"), 5, \"0.00012207\")\n      expect_gen(hexfloat(\"0x1p-16\"), 5, \"1.5259e-5\")\n      expect_gen(hexfloat(\"0x1p-19\"), 5, \"1.9073e-6\")\n    end\n\n    it \"tricky corner cases\" do\n      expect_gen(999.999, 1, \"1e+3\")    # \"%.0e\" is \"1e+3\"; X == 3\n      expect_gen(999.999, 2, \"1e+3\")    # \"%.1e\" is \"1.0e+3\"; X == 3\n      expect_gen(999.999, 3, \"1e+3\")    # \"%.2e\" is \"1.00e+3\"; X == 3\n      expect_gen(999.999, 4, \"1000\")    # \"%.3e\" is \"1.000e+3\"; X == 3\n      expect_gen(999.999, 5, \"1000\")    # \"%.4e\" is \"1.0000e+3\"; X == 3\n      expect_gen(999.999, 6, \"999.999\") # \"%.5e\" is \"9.99999e+2\"; X == 2\n\n      expect_gen(999.99, 1, \"1e+3\")\n      expect_gen(999.99, 2, \"1e+3\")\n      expect_gen(999.99, 3, \"1e+3\")\n      expect_gen(999.99, 4, \"1000\")\n      expect_gen(999.99, 5, \"999.99\")\n      expect_gen(999.99, 6, \"999.99\")\n\n      # C11's Standardese is slightly vague about how to perform the trial formatting in scientific notation,\n      # but the intention is to use precision P - 1, which is what's used when scientific notation is actually chosen.\n      # This example verifies this behavior. Here, P == 3 performs trial formatting with \"%.2e\", triggering rounding.\n      # That increases X to 3, forcing scientific notation to be chosen.\n      # If P == 3 performed trial formatting with \"%.3e\", rounding wouldn't happen,\n      # X would be 2, and fixed notation would be chosen.\n      expect_gen(999.9, 1, \"1e+3\")  # \"%.0e\" is \"1e+3\"; X == 3\n      expect_gen(999.9, 2, \"1e+3\")  # \"%.1e\" is \"1.0e+3\"; X == 3\n      expect_gen(999.9, 3, \"1e+3\")  # \"%.2e\" is \"1.00e+3\"; X == 3; SPECIAL CORNER CASE\n      expect_gen(999.9, 4, \"999.9\") # \"%.3e\" is \"9.999e+2\"; X == 2\n      expect_gen(999.9, 5, \"999.9\") # \"%.4e\" is \"9.9990e+2\"; X == 2\n      expect_gen(999.9, 6, \"999.9\") # \"%.5e\" is \"9.99900e+2\"; X == 2\n\n      expect_gen(999.0, 1, \"1e+3\")\n      expect_gen(999.0, 2, \"1e+3\")\n      expect_gen(999.0, 3, \"999\")\n      expect_gen(999.0, 4, \"999\")\n      expect_gen(999.0, 5, \"999\")\n      expect_gen(999.0, 6, \"999\")\n\n      expect_gen(99.9999, 1, \"1e+2\")\n      expect_gen(99.9999, 2, \"1e+2\")\n      expect_gen(99.9999, 3, \"100\")\n      expect_gen(99.9999, 4, \"100\")\n      expect_gen(99.9999, 5, \"100\")\n      expect_gen(99.9999, 6, \"99.9999\")\n\n      expect_gen(99.999, 1, \"1e+2\")\n      expect_gen(99.999, 2, \"1e+2\")\n      expect_gen(99.999, 3, \"100\")\n      expect_gen(99.999, 4, \"100\")\n      expect_gen(99.999, 5, \"99.999\")\n      expect_gen(99.999, 6, \"99.999\")\n\n      expect_gen(99.99, 1, \"1e+2\")\n      expect_gen(99.99, 2, \"1e+2\")\n      expect_gen(99.99, 3, \"100\")\n      expect_gen(99.99, 4, \"99.99\")\n      expect_gen(99.99, 5, \"99.99\")\n      expect_gen(99.99, 6, \"99.99\")\n\n      expect_gen(99.9, 1, \"1e+2\")\n      expect_gen(99.9, 2, \"1e+2\")\n      expect_gen(99.9, 3, \"99.9\")\n      expect_gen(99.9, 4, \"99.9\")\n      expect_gen(99.9, 5, \"99.9\")\n      expect_gen(99.9, 6, \"99.9\")\n\n      expect_gen(99.0, 1, \"1e+2\")\n      expect_gen(99.0, 2, \"99\")\n      expect_gen(99.0, 3, \"99\")\n      expect_gen(99.0, 4, \"99\")\n      expect_gen(99.0, 5, \"99\")\n      expect_gen(99.0, 6, \"99\")\n\n      expect_gen(9.99999, 1, \"1e+1\")\n      expect_gen(9.99999, 2, \"10\")\n      expect_gen(9.99999, 3, \"10\")\n      expect_gen(9.99999, 4, \"10\")\n      expect_gen(9.99999, 5, \"10\")\n      expect_gen(9.99999, 6, \"9.99999\")\n\n      expect_gen(9.9999, 1, \"1e+1\")\n      expect_gen(9.9999, 2, \"10\")\n      expect_gen(9.9999, 3, \"10\")\n      expect_gen(9.9999, 4, \"10\")\n      expect_gen(9.9999, 5, \"9.9999\")\n      expect_gen(9.9999, 6, \"9.9999\")\n\n      expect_gen(9.999, 1, \"1e+1\")\n      expect_gen(9.999, 2, \"10\")\n      expect_gen(9.999, 3, \"10\")\n      expect_gen(9.999, 4, \"9.999\")\n      expect_gen(9.999, 5, \"9.999\")\n      expect_gen(9.999, 6, \"9.999\")\n\n      expect_gen(9.99, 1, \"1e+1\")\n      expect_gen(9.99, 2, \"10\")\n      expect_gen(9.99, 3, \"9.99\")\n      expect_gen(9.99, 4, \"9.99\")\n      expect_gen(9.99, 5, \"9.99\")\n      expect_gen(9.99, 6, \"9.99\")\n\n      expect_gen(9.9, 1, \"1e+1\")\n      expect_gen(9.9, 2, \"9.9\")\n      expect_gen(9.9, 3, \"9.9\")\n      expect_gen(9.9, 4, \"9.9\")\n      expect_gen(9.9, 5, \"9.9\")\n      expect_gen(9.9, 6, \"9.9\")\n\n      expect_gen(9.0, 1, \"9\")\n      expect_gen(9.0, 2, \"9\")\n      expect_gen(9.0, 3, \"9\")\n      expect_gen(9.0, 4, \"9\")\n      expect_gen(9.0, 5, \"9\")\n      expect_gen(9.0, 6, \"9\")\n\n      expect_gen(0.999999, 1, \"1\")\n      expect_gen(0.999999, 2, \"1\")\n      expect_gen(0.999999, 3, \"1\")\n      expect_gen(0.999999, 4, \"1\")\n      expect_gen(0.999999, 5, \"1\")\n      expect_gen(0.999999, 6, \"0.999999\")\n\n      expect_gen(0.99999, 1, \"1\")\n      expect_gen(0.99999, 2, \"1\")\n      expect_gen(0.99999, 3, \"1\")\n      expect_gen(0.99999, 4, \"1\")\n      expect_gen(0.99999, 5, \"0.99999\")\n      expect_gen(0.99999, 6, \"0.99999\")\n\n      expect_gen(0.9999, 1, \"1\")\n      expect_gen(0.9999, 2, \"1\")\n      expect_gen(0.9999, 3, \"1\")\n      expect_gen(0.9999, 4, \"0.9999\")\n      expect_gen(0.9999, 5, \"0.9999\")\n      expect_gen(0.9999, 6, \"0.9999\")\n\n      expect_gen(0.999, 1, \"1\")\n      expect_gen(0.999, 2, \"1\")\n      expect_gen(0.999, 3, \"0.999\")\n      expect_gen(0.999, 4, \"0.999\")\n      expect_gen(0.999, 5, \"0.999\")\n      expect_gen(0.999, 6, \"0.999\")\n\n      expect_gen(0.99, 1, \"1\")\n      expect_gen(0.99, 2, \"0.99\")\n      expect_gen(0.99, 3, \"0.99\")\n      expect_gen(0.99, 4, \"0.99\")\n      expect_gen(0.99, 5, \"0.99\")\n      expect_gen(0.99, 6, \"0.99\")\n\n      expect_gen(0.9, 1, \"0.9\")\n      expect_gen(0.9, 2, \"0.9\")\n      expect_gen(0.9, 3, \"0.9\")\n      expect_gen(0.9, 4, \"0.9\")\n      expect_gen(0.9, 5, \"0.9\")\n      expect_gen(0.9, 6, \"0.9\")\n\n      expect_gen(0.0999999, 1, \"0.1\")\n      expect_gen(0.0999999, 2, \"0.1\")\n      expect_gen(0.0999999, 3, \"0.1\")\n      expect_gen(0.0999999, 4, \"0.1\")\n      expect_gen(0.0999999, 5, \"0.1\")\n      expect_gen(0.0999999, 6, \"0.0999999\")\n\n      expect_gen(0.099999, 1, \"0.1\")\n      expect_gen(0.099999, 2, \"0.1\")\n      expect_gen(0.099999, 3, \"0.1\")\n      expect_gen(0.099999, 4, \"0.1\")\n      expect_gen(0.099999, 5, \"0.099999\")\n      expect_gen(0.099999, 6, \"0.099999\")\n\n      expect_gen(0.09999, 1, \"0.1\")\n      expect_gen(0.09999, 2, \"0.1\")\n      expect_gen(0.09999, 3, \"0.1\")\n      expect_gen(0.09999, 4, \"0.09999\")\n      expect_gen(0.09999, 5, \"0.09999\")\n      expect_gen(0.09999, 6, \"0.09999\")\n\n      expect_gen(0.0999, 1, \"0.1\")\n      expect_gen(0.0999, 2, \"0.1\")\n      expect_gen(0.0999, 3, \"0.0999\")\n      expect_gen(0.0999, 4, \"0.0999\")\n      expect_gen(0.0999, 5, \"0.0999\")\n      expect_gen(0.0999, 6, \"0.0999\")\n\n      expect_gen(0.099, 1, \"0.1\")\n      expect_gen(0.099, 2, \"0.099\")\n      expect_gen(0.099, 3, \"0.099\")\n      expect_gen(0.099, 4, \"0.099\")\n      expect_gen(0.099, 5, \"0.099\")\n      expect_gen(0.099, 6, \"0.099\")\n\n      expect_gen(0.09, 1, \"0.09\")\n      expect_gen(0.09, 2, \"0.09\")\n      expect_gen(0.09, 3, \"0.09\")\n      expect_gen(0.09, 4, \"0.09\")\n      expect_gen(0.09, 5, \"0.09\")\n      expect_gen(0.09, 6, \"0.09\")\n\n      expect_gen(0.00999999, 1, \"0.01\")\n      expect_gen(0.00999999, 2, \"0.01\")\n      expect_gen(0.00999999, 3, \"0.01\")\n      expect_gen(0.00999999, 4, \"0.01\")\n      expect_gen(0.00999999, 5, \"0.01\")\n      expect_gen(0.00999999, 6, \"0.00999999\")\n\n      expect_gen(0.0099999, 1, \"0.01\")\n      expect_gen(0.0099999, 2, \"0.01\")\n      expect_gen(0.0099999, 3, \"0.01\")\n      expect_gen(0.0099999, 4, \"0.01\")\n      expect_gen(0.0099999, 5, \"0.0099999\")\n      expect_gen(0.0099999, 6, \"0.0099999\")\n\n      expect_gen(0.009999, 1, \"0.01\")\n      expect_gen(0.009999, 2, \"0.01\")\n      expect_gen(0.009999, 3, \"0.01\")\n      expect_gen(0.009999, 4, \"0.009999\")\n      expect_gen(0.009999, 5, \"0.009999\")\n      expect_gen(0.009999, 6, \"0.009999\")\n\n      expect_gen(0.00999, 1, \"0.01\")\n      expect_gen(0.00999, 2, \"0.01\")\n      expect_gen(0.00999, 3, \"0.00999\")\n      expect_gen(0.00999, 4, \"0.00999\")\n      expect_gen(0.00999, 5, \"0.00999\")\n      expect_gen(0.00999, 6, \"0.00999\")\n\n      expect_gen(0.0099, 1, \"0.01\")\n      expect_gen(0.0099, 2, \"0.0099\")\n      expect_gen(0.0099, 3, \"0.0099\")\n      expect_gen(0.0099, 4, \"0.0099\")\n      expect_gen(0.0099, 5, \"0.0099\")\n      expect_gen(0.0099, 6, \"0.0099\")\n\n      expect_gen(0.009, 1, \"0.009\")\n      expect_gen(0.009, 2, \"0.009\")\n      expect_gen(0.009, 3, \"0.009\")\n      expect_gen(0.009, 4, \"0.009\")\n      expect_gen(0.009, 5, \"0.009\")\n      expect_gen(0.009, 6, \"0.009\")\n\n      expect_gen(0.000999999, 1, \"0.001\")\n      expect_gen(0.000999999, 2, \"0.001\")\n      expect_gen(0.000999999, 3, \"0.001\")\n      expect_gen(0.000999999, 4, \"0.001\")\n      expect_gen(0.000999999, 5, \"0.001\")\n      expect_gen(0.000999999, 6, \"0.000999999\")\n\n      expect_gen(0.00099999, 1, \"0.001\")\n      expect_gen(0.00099999, 2, \"0.001\")\n      expect_gen(0.00099999, 3, \"0.001\")\n      expect_gen(0.00099999, 4, \"0.001\")\n      expect_gen(0.00099999, 5, \"0.00099999\")\n      expect_gen(0.00099999, 6, \"0.00099999\")\n\n      expect_gen(0.0009999, 1, \"0.001\")\n      expect_gen(0.0009999, 2, \"0.001\")\n      expect_gen(0.0009999, 3, \"0.001\")\n      expect_gen(0.0009999, 4, \"0.0009999\")\n      expect_gen(0.0009999, 5, \"0.0009999\")\n      expect_gen(0.0009999, 6, \"0.0009999\")\n\n      expect_gen(0.000999, 1, \"0.001\")\n      expect_gen(0.000999, 2, \"0.001\")\n      expect_gen(0.000999, 3, \"0.000999\")\n      expect_gen(0.000999, 4, \"0.000999\")\n      expect_gen(0.000999, 5, \"0.000999\")\n      expect_gen(0.000999, 6, \"0.000999\")\n\n      expect_gen(0.00099, 1, \"0.001\")\n      expect_gen(0.00099, 2, \"0.00099\")\n      expect_gen(0.00099, 3, \"0.00099\")\n      expect_gen(0.00099, 4, \"0.00099\")\n      expect_gen(0.00099, 5, \"0.00099\")\n      expect_gen(0.00099, 6, \"0.00099\")\n\n      expect_gen(0.0009, 1, \"0.0009\")\n      expect_gen(0.0009, 2, \"0.0009\")\n      expect_gen(0.0009, 3, \"0.0009\")\n      expect_gen(0.0009, 4, \"0.0009\")\n      expect_gen(0.0009, 5, \"0.0009\")\n      expect_gen(0.0009, 6, \"0.0009\")\n\n      # Having a scientific exponent X == -5 triggers scientific notation.\n      # If rounding adjusts this to X == -4, then fixed notation will be selected.\n      expect_gen(0.0000999999, 1, \"0.0001\")\n      expect_gen(0.0000999999, 2, \"0.0001\")\n      expect_gen(0.0000999999, 3, \"0.0001\")\n      expect_gen(0.0000999999, 4, \"0.0001\")\n      expect_gen(0.0000999999, 5, \"0.0001\")\n      expect_gen(0.0000999999, 6, \"9.99999e-5\")\n\n      expect_gen(0.000099999, 1, \"0.0001\")\n      expect_gen(0.000099999, 2, \"0.0001\")\n      expect_gen(0.000099999, 3, \"0.0001\")\n      expect_gen(0.000099999, 4, \"0.0001\")\n      expect_gen(0.000099999, 5, \"9.9999e-5\")\n      expect_gen(0.000099999, 6, \"9.9999e-5\")\n\n      expect_gen(0.00009999, 1, \"0.0001\")\n      expect_gen(0.00009999, 2, \"0.0001\")\n      expect_gen(0.00009999, 3, \"0.0001\")\n      expect_gen(0.00009999, 4, \"9.999e-5\")\n      expect_gen(0.00009999, 5, \"9.999e-5\")\n      expect_gen(0.00009999, 6, \"9.999e-5\")\n\n      expect_gen(0.0000999, 1, \"0.0001\")\n      expect_gen(0.0000999, 2, \"0.0001\")\n      expect_gen(0.0000999, 3, \"9.99e-5\")\n      expect_gen(0.0000999, 4, \"9.99e-5\")\n      expect_gen(0.0000999, 5, \"9.99e-5\")\n      expect_gen(0.0000999, 6, \"9.99e-5\")\n\n      expect_gen(0.000099, 1, \"0.0001\")\n      expect_gen(0.000099, 2, \"9.9e-5\")\n      expect_gen(0.000099, 3, \"9.9e-5\")\n      expect_gen(0.000099, 4, \"9.9e-5\")\n      expect_gen(0.000099, 5, \"9.9e-5\")\n      expect_gen(0.000099, 6, \"9.9e-5\")\n\n      expect_gen(0.00009, 1, \"9e-5\")\n      expect_gen(0.00009, 2, \"9e-5\")\n      expect_gen(0.00009, 3, \"9e-5\")\n      expect_gen(0.00009, 4, \"9e-5\")\n      expect_gen(0.00009, 5, \"9e-5\")\n      expect_gen(0.00009, 6, \"9e-5\")\n\n      # Rounding test cases without exponent-adjusting behavior.\n      expect_gen(2999.999, 1, \"3e+3\")\n      expect_gen(2999.999, 2, \"3e+3\")\n      expect_gen(2999.999, 3, \"3e+3\")\n      expect_gen(2999.999, 4, \"3000\")\n      expect_gen(2999.999, 5, \"3000\")\n      expect_gen(2999.999, 6, \"3000\")\n\n      expect_gen(2999.99, 1, \"3e+3\")\n      expect_gen(2999.99, 2, \"3e+3\")\n      expect_gen(2999.99, 3, \"3e+3\")\n      expect_gen(2999.99, 4, \"3000\")\n      expect_gen(2999.99, 5, \"3000\")\n      expect_gen(2999.99, 6, \"2999.99\")\n\n      expect_gen(2999.9, 1, \"3e+3\")\n      expect_gen(2999.9, 2, \"3e+3\")\n      expect_gen(2999.9, 3, \"3e+3\")\n      expect_gen(2999.9, 4, \"3000\")\n      expect_gen(2999.9, 5, \"2999.9\")\n      expect_gen(2999.9, 6, \"2999.9\")\n\n      expect_gen(2999.0, 1, \"3e+3\")\n      expect_gen(2999.0, 2, \"3e+3\")\n      expect_gen(2999.0, 3, \"3e+3\")\n      expect_gen(2999.0, 4, \"2999\")\n      expect_gen(2999.0, 5, \"2999\")\n      expect_gen(2999.0, 6, \"2999\")\n\n      expect_gen(299.999, 1, \"3e+2\")\n      expect_gen(299.999, 2, \"3e+2\")\n      expect_gen(299.999, 3, \"300\")\n      expect_gen(299.999, 4, \"300\")\n      expect_gen(299.999, 5, \"300\")\n      expect_gen(299.999, 6, \"299.999\")\n\n      expect_gen(299.99, 1, \"3e+2\")\n      expect_gen(299.99, 2, \"3e+2\")\n      expect_gen(299.99, 3, \"300\")\n      expect_gen(299.99, 4, \"300\")\n      expect_gen(299.99, 5, \"299.99\")\n      expect_gen(299.99, 6, \"299.99\")\n\n      expect_gen(299.9, 1, \"3e+2\")\n      expect_gen(299.9, 2, \"3e+2\")\n      expect_gen(299.9, 3, \"300\")\n      expect_gen(299.9, 4, \"299.9\")\n      expect_gen(299.9, 5, \"299.9\")\n      expect_gen(299.9, 6, \"299.9\")\n\n      expect_gen(299.0, 1, \"3e+2\")\n      expect_gen(299.0, 2, \"3e+2\")\n      expect_gen(299.0, 3, \"299\")\n      expect_gen(299.0, 4, \"299\")\n      expect_gen(299.0, 5, \"299\")\n      expect_gen(299.0, 6, \"299\")\n\n      expect_gen(29.999, 1, \"3e+1\")\n      expect_gen(29.999, 2, \"30\")\n      expect_gen(29.999, 3, \"30\")\n      expect_gen(29.999, 4, \"30\")\n      expect_gen(29.999, 5, \"29.999\")\n      expect_gen(29.999, 6, \"29.999\")\n\n      expect_gen(29.99, 1, \"3e+1\")\n      expect_gen(29.99, 2, \"30\")\n      expect_gen(29.99, 3, \"30\")\n      expect_gen(29.99, 4, \"29.99\")\n      expect_gen(29.99, 5, \"29.99\")\n      expect_gen(29.99, 6, \"29.99\")\n\n      expect_gen(29.9, 1, \"3e+1\")\n      expect_gen(29.9, 2, \"30\")\n      expect_gen(29.9, 3, \"29.9\")\n      expect_gen(29.9, 4, \"29.9\")\n      expect_gen(29.9, 5, \"29.9\")\n      expect_gen(29.9, 6, \"29.9\")\n\n      expect_gen(29.0, 1, \"3e+1\")\n      expect_gen(29.0, 2, \"29\")\n      expect_gen(29.0, 3, \"29\")\n      expect_gen(29.0, 4, \"29\")\n      expect_gen(29.0, 5, \"29\")\n      expect_gen(29.0, 6, \"29\")\n\n      expect_gen(2.999, 1, \"3\")\n      expect_gen(2.999, 2, \"3\")\n      expect_gen(2.999, 3, \"3\")\n      expect_gen(2.999, 4, \"2.999\")\n      expect_gen(2.999, 5, \"2.999\")\n      expect_gen(2.999, 6, \"2.999\")\n\n      expect_gen(2.99, 1, \"3\")\n      expect_gen(2.99, 2, \"3\")\n      expect_gen(2.99, 3, \"2.99\")\n      expect_gen(2.99, 4, \"2.99\")\n      expect_gen(2.99, 5, \"2.99\")\n      expect_gen(2.99, 6, \"2.99\")\n\n      expect_gen(2.9, 1, \"3\")\n      expect_gen(2.9, 2, \"2.9\")\n      expect_gen(2.9, 3, \"2.9\")\n      expect_gen(2.9, 4, \"2.9\")\n      expect_gen(2.9, 5, \"2.9\")\n      expect_gen(2.9, 6, \"2.9\")\n\n      expect_gen(2.0, 1, \"2\")\n      expect_gen(2.0, 2, \"2\")\n      expect_gen(2.0, 3, \"2\")\n      expect_gen(2.0, 4, \"2\")\n      expect_gen(2.0, 5, \"2\")\n      expect_gen(2.0, 6, \"2\")\n\n      expect_gen(0.2999, 1, \"0.3\")\n      expect_gen(0.2999, 2, \"0.3\")\n      expect_gen(0.2999, 3, \"0.3\")\n      expect_gen(0.2999, 4, \"0.2999\")\n      expect_gen(0.2999, 5, \"0.2999\")\n      expect_gen(0.2999, 6, \"0.2999\")\n\n      expect_gen(0.299, 1, \"0.3\")\n      expect_gen(0.299, 2, \"0.3\")\n      expect_gen(0.299, 3, \"0.299\")\n      expect_gen(0.299, 4, \"0.299\")\n      expect_gen(0.299, 5, \"0.299\")\n      expect_gen(0.299, 6, \"0.299\")\n\n      expect_gen(0.29, 1, \"0.3\")\n      expect_gen(0.29, 2, \"0.29\")\n      expect_gen(0.29, 3, \"0.29\")\n      expect_gen(0.29, 4, \"0.29\")\n      expect_gen(0.29, 5, \"0.29\")\n      expect_gen(0.29, 6, \"0.29\")\n\n      expect_gen(0.2, 1, \"0.2\")\n      expect_gen(0.2, 2, \"0.2\")\n      expect_gen(0.2, 3, \"0.2\")\n      expect_gen(0.2, 4, \"0.2\")\n      expect_gen(0.2, 5, \"0.2\")\n      expect_gen(0.2, 6, \"0.2\")\n\n      expect_gen(0.02999, 1, \"0.03\")\n      expect_gen(0.02999, 2, \"0.03\")\n      expect_gen(0.02999, 3, \"0.03\")\n      expect_gen(0.02999, 4, \"0.02999\")\n      expect_gen(0.02999, 5, \"0.02999\")\n      expect_gen(0.02999, 6, \"0.02999\")\n\n      expect_gen(0.0299, 1, \"0.03\")\n      expect_gen(0.0299, 2, \"0.03\")\n      expect_gen(0.0299, 3, \"0.0299\")\n      expect_gen(0.0299, 4, \"0.0299\")\n      expect_gen(0.0299, 5, \"0.0299\")\n      expect_gen(0.0299, 6, \"0.0299\")\n\n      expect_gen(0.029, 1, \"0.03\")\n      expect_gen(0.029, 2, \"0.029\")\n      expect_gen(0.029, 3, \"0.029\")\n      expect_gen(0.029, 4, \"0.029\")\n      expect_gen(0.029, 5, \"0.029\")\n      expect_gen(0.029, 6, \"0.029\")\n\n      expect_gen(0.02, 1, \"0.02\")\n      expect_gen(0.02, 2, \"0.02\")\n      expect_gen(0.02, 3, \"0.02\")\n      expect_gen(0.02, 4, \"0.02\")\n      expect_gen(0.02, 5, \"0.02\")\n      expect_gen(0.02, 6, \"0.02\")\n\n      expect_gen(0.002999, 1, \"0.003\")\n      expect_gen(0.002999, 2, \"0.003\")\n      expect_gen(0.002999, 3, \"0.003\")\n      expect_gen(0.002999, 4, \"0.002999\")\n      expect_gen(0.002999, 5, \"0.002999\")\n      expect_gen(0.002999, 6, \"0.002999\")\n\n      expect_gen(0.00299, 1, \"0.003\")\n      expect_gen(0.00299, 2, \"0.003\")\n      expect_gen(0.00299, 3, \"0.00299\")\n      expect_gen(0.00299, 4, \"0.00299\")\n      expect_gen(0.00299, 5, \"0.00299\")\n      expect_gen(0.00299, 6, \"0.00299\")\n\n      expect_gen(0.0029, 1, \"0.003\")\n      expect_gen(0.0029, 2, \"0.0029\")\n      expect_gen(0.0029, 3, \"0.0029\")\n      expect_gen(0.0029, 4, \"0.0029\")\n      expect_gen(0.0029, 5, \"0.0029\")\n      expect_gen(0.0029, 6, \"0.0029\")\n\n      expect_gen(0.002, 1, \"0.002\")\n      expect_gen(0.002, 2, \"0.002\")\n      expect_gen(0.002, 3, \"0.002\")\n      expect_gen(0.002, 4, \"0.002\")\n      expect_gen(0.002, 5, \"0.002\")\n      expect_gen(0.002, 6, \"0.002\")\n\n      expect_gen(0.0002999, 1, \"0.0003\")\n      expect_gen(0.0002999, 2, \"0.0003\")\n      expect_gen(0.0002999, 3, \"0.0003\")\n      expect_gen(0.0002999, 4, \"0.0002999\")\n      expect_gen(0.0002999, 5, \"0.0002999\")\n      expect_gen(0.0002999, 6, \"0.0002999\")\n\n      expect_gen(0.000299, 1, \"0.0003\")\n      expect_gen(0.000299, 2, \"0.0003\")\n      expect_gen(0.000299, 3, \"0.000299\")\n      expect_gen(0.000299, 4, \"0.000299\")\n      expect_gen(0.000299, 5, \"0.000299\")\n      expect_gen(0.000299, 6, \"0.000299\")\n\n      expect_gen(0.00029, 1, \"0.0003\")\n      expect_gen(0.00029, 2, \"0.00029\")\n      expect_gen(0.00029, 3, \"0.00029\")\n      expect_gen(0.00029, 4, \"0.00029\")\n      expect_gen(0.00029, 5, \"0.00029\")\n      expect_gen(0.00029, 6, \"0.00029\")\n\n      expect_gen(0.0002, 1, \"0.0002\")\n      expect_gen(0.0002, 2, \"0.0002\")\n      expect_gen(0.0002, 3, \"0.0002\")\n      expect_gen(0.0002, 4, \"0.0002\")\n      expect_gen(0.0002, 5, \"0.0002\")\n      expect_gen(0.0002, 6, \"0.0002\")\n\n      expect_gen(0.00002999, 1, \"3e-5\")\n      expect_gen(0.00002999, 2, \"3e-5\")\n      expect_gen(0.00002999, 3, \"3e-5\")\n      expect_gen(0.00002999, 4, \"2.999e-5\")\n      expect_gen(0.00002999, 5, \"2.999e-5\")\n      expect_gen(0.00002999, 6, \"2.999e-5\")\n\n      expect_gen(0.0000299, 1, \"3e-5\")\n      expect_gen(0.0000299, 2, \"3e-5\")\n      expect_gen(0.0000299, 3, \"2.99e-5\")\n      expect_gen(0.0000299, 4, \"2.99e-5\")\n      expect_gen(0.0000299, 5, \"2.99e-5\")\n      expect_gen(0.0000299, 6, \"2.99e-5\")\n\n      expect_gen(0.000029, 1, \"3e-5\")\n      expect_gen(0.000029, 2, \"2.9e-5\")\n      expect_gen(0.000029, 3, \"2.9e-5\")\n      expect_gen(0.000029, 4, \"2.9e-5\")\n      expect_gen(0.000029, 5, \"2.9e-5\")\n      expect_gen(0.000029, 6, \"2.9e-5\")\n\n      expect_gen(0.00002, 1, \"2e-5\")\n      expect_gen(0.00002, 2, \"2e-5\")\n      expect_gen(0.00002, 3, \"2e-5\")\n      expect_gen(0.00002, 4, \"2e-5\")\n      expect_gen(0.00002, 5, \"2e-5\")\n      expect_gen(0.00002, 6, \"2e-5\")\n    end\n\n    it \"transitions between values of the scientific exponent X\" do\n      {% for tc in GEN_TRANSITIONS %}\n        expect_gen(hexfloat({{ tc[0] }}), {{ tc[1] }}, {{ tc[2] }}, file: {{ tc.filename }}, line: {{ tc.line_number }})\n      {% end %}\n    end\n\n    it \"UCRT had trouble with rounding this value\" do\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 105, \"109995565999999994887854821710219658911365648587951921896774663603198787416706536331386569598149846892544\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 19, \"1.099955659999999949e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 18, \"1.09995565999999995e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 17, \"1.0999556599999999e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 16, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 15, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 14, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 13, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 12, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 11, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 10, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 9, \"1.09995566e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 8, \"1.0999557e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 7, \"1.099956e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 6, \"1.09996e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 5, \"1.1e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 4, \"1.1e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 3, \"1.1e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 2, \"1.1e+104\")\n      expect_gen(hexfloat(\"0x1.88e2d605edc3dp+345\"), 1, \"1e+104\")\n    end\n\n    it \"more cases that the UCRT had trouble with (e.g. DevCom-1093399)\" do\n      expect_gen(hexfloat(\"0x1.8p+62\"), 17, \"6.9175290276410819e+18\")\n      expect_gen(hexfloat(\"0x1.0a2742p+17\"), 6, \"136271\")\n      expect_gen(hexfloat(\"0x1.f8b0f962cdffbp+205\"), 14, \"1.0137595739223e+62\")\n      expect_gen(hexfloat(\"0x1.f8b0f962cdffbp+205\"), 17, \"1.0137595739222531e+62\")\n      expect_gen(hexfloat(\"0x1.f8b0f962cdffbp+205\"), 51, \"1.01375957392225305727423222620636224221808910954041e+62\")\n      expect_gen(hexfloat(\"0x1.f8b0f962cdffbp+205\"), 55, \"1.013759573922253057274232226206362242218089109540405973e+62\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/float_printer/ryu_printf_test_cases.cr",
    "content": "# Test cases used by `./ryu_printf_spec.cr`\n\n# The following two lists store: {value, fixed_precision, exp_precision}\n# Iterate over them via macros only, otherwise the entire array literal will be\n# materialized!\n# The original specs contain also the full expected results which would have\n# increased the file size by over 18x. Here we assume that `BigFloat#to_s` and\n# `BigDecimal#to_s` are always correct, so that we don't have to write the long\n# results in the source code.\n\n# These values test every power of ten that's within the range of doubles.\nALL_POWERS_OF_TEN = [\n  {1e-323, 1073, 749},\n  {1e-322, 1072, 749},\n  {1e-321, 1073, 751},\n  {1e-320, 1071, 750},\n  {1e-319, 1070, 750},\n  {1e-318, 1073, 754},\n  {1e-317, 1074, 757},\n  {1e-316, 1074, 757},\n  {1e-315, 1074, 758},\n  {1e-314, 1074, 759},\n  {1e-313, 1074, 761},\n  {1e-312, 1074, 761},\n  {1e-311, 1074, 762},\n  {1e-310, 1074, 763},\n  {1e-309, 1074, 765},\n  {1e-308, 1073, 764},\n  {1e-307, 1072, 764},\n  {1e-306, 1069, 763},\n  {1e-305, 1066, 760},\n  {1e-304, 1062, 757},\n  {1e-303, 1059, 755},\n  {1e-302, 1054, 751},\n  {1e-301, 1052, 751},\n  {1e-300, 1049, 749},\n  {1e-299, 1046, 746},\n  {1e-298, 1042, 743},\n  {1e-297, 1039, 742},\n  {1e-296, 1035, 739},\n  {1e-295, 1032, 737},\n  {1e-294, 1029, 735},\n  {1e-293, 1022, 729},\n  {1e-292, 1021, 729},\n  {1e-291, 1019, 727},\n  {1e-290, 1015, 725},\n  {1e-289, 1012, 723},\n  {1e-288, 1009, 721},\n  {1e-287, 1006, 719},\n  {1e-286, 1003, 717},\n  {1e-285, 999, 714},\n  {1e-284, 996, 712},\n  {1e-283, 993, 709},\n  {1e-282, 986, 704},\n  {1e-281, 985, 704},\n  {1e-280, 981, 700},\n  {1e-279, 979, 700},\n  {1e-278, 976, 697},\n  {1e-277, 973, 695},\n  {1e-276, 957, 681},\n  {1e-275, 966, 690},\n  {1e-274, 963, 688},\n  {1e-273, 954, 681},\n  {1e-272, 956, 683},\n  {1e-271, 953, 681},\n  {1e-270, 948, 678},\n  {1e-269, 944, 674},\n  {1e-268, 943, 674},\n  {1e-267, 935, 667},\n  {1e-266, 934, 667},\n  {1e-265, 933, 667},\n  {1e-264, 925, 661},\n  {1e-263, 924, 661},\n  {1e-262, 923, 661},\n  {1e-261, 920, 658},\n  {1e-260, 916, 655},\n  {1e-259, 909, 650},\n  {1e-258, 910, 651},\n  {1e-257, 904, 646},\n  {1e-256, 903, 646},\n  {1e-255, 898, 643},\n  {1e-254, 894, 639},\n  {1e-253, 890, 637},\n  {1e-252, 890, 637},\n  {1e-251, 886, 635},\n  {1e-250, 881, 631},\n  {1e-249, 880, 631},\n  {1e-248, 876, 627},\n  {1e-247, 872, 625},\n  {1e-246, 870, 623},\n  {1e-245, 863, 617},\n  {1e-244, 862, 617},\n  {1e-243, 860, 616},\n  {1e-242, 850, 607},\n  {1e-241, 849, 607},\n  {1e-240, 848, 607},\n  {1e-239, 845, 606},\n  {1e-238, 840, 601},\n  {1e-237, 839, 601},\n  {1e-236, 835, 599},\n  {1e-235, 829, 593},\n  {1e-234, 828, 593},\n  {1e-233, 827, 593},\n  {1e-232, 823, 591},\n  {1e-231, 819, 587},\n  {1e-230, 817, 587},\n  {1e-229, 813, 584},\n  {1e-228, 810, 582},\n  {1e-227, 807, 579},\n  {1e-226, 803, 576},\n  {1e-225, 799, 573},\n  {1e-224, 796, 572},\n  {1e-223, 792, 568},\n  {1e-222, 790, 568},\n  {1e-221, 787, 566},\n  {1e-220, 783, 562},\n  {1e-219, 777, 558},\n  {1e-218, 776, 558},\n  {1e-217, 773, 556},\n  {1e-216, 768, 552},\n  {1e-215, 767, 552},\n  {1e-214, 763, 548},\n  {1e-213, 758, 544},\n  {1e-212, 757, 544},\n  {1e-211, 753, 542},\n  {1e-210, 747, 537},\n  {1e-209, 746, 537},\n  {1e-208, 740, 532},\n  {1e-207, 740, 532},\n  {1e-206, 737, 531},\n  {1e-205, 731, 526},\n  {1e-204, 730, 526},\n  {1e-203, 723, 520},\n  {1e-202, 722, 520},\n  {1e-201, 720, 518},\n  {1e-200, 715, 514},\n  {1e-199, 714, 514},\n  {1e-198, 709, 510},\n  {1e-197, 706, 508},\n  {1e-196, 703, 507},\n  {1e-195, 699, 504},\n  {1e-194, 697, 503},\n  {1e-193, 693, 500},\n  {1e-192, 689, 497},\n  {1e-191, 684, 493},\n  {1e-190, 683, 493},\n  {1e-189, 680, 491},\n  {1e-188, 676, 487},\n  {1e-187, 672, 485},\n  {1e-186, 669, 482},\n  {1e-185, 666, 480},\n  {1e-184, 663, 479},\n  {1e-183, 660, 477},\n  {1e-182, 655, 473},\n  {1e-181, 654, 473},\n  {1e-180, 647, 467},\n  {1e-179, 646, 467},\n  {1e-178, 641, 462},\n  {1e-177, 640, 462},\n  {1e-176, 632, 455},\n  {1e-175, 631, 455},\n  {1e-174, 630, 455},\n  {1e-173, 624, 451},\n  {1e-172, 623, 451},\n  {1e-171, 618, 446},\n  {1e-170, 617, 446},\n  {1e-169, 614, 445},\n  {1e-168, 610, 442},\n  {1e-167, 607, 440},\n  {1e-166, 604, 438},\n  {1e-165, 600, 435},\n  {1e-164, 597, 432},\n  {1e-163, 594, 430},\n  {1e-162, 591, 428},\n  {1e-161, 587, 426},\n  {1e-160, 582, 421},\n  {1e-159, 581, 421},\n  {1e-158, 577, 419},\n  {1e-157, 574, 416},\n  {1e-156, 571, 415},\n  {1e-155, 567, 412},\n  {1e-154, 563, 408},\n  {1e-153, 561, 408},\n  {1e-152, 557, 405},\n  {1e-151, 553, 401},\n  {1e-150, 551, 401},\n  {1e-149, 547, 397},\n  {1e-148, 544, 395},\n  {1e-147, 540, 392},\n  {1e-146, 538, 392},\n  {1e-145, 534, 388},\n  {1e-144, 527, 382},\n  {1e-143, 526, 382},\n  {1e-142, 521, 379},\n  {1e-141, 520, 379},\n  {1e-140, 517, 376},\n  {1e-139, 514, 375},\n  {1e-138, 511, 373},\n  {1e-137, 508, 370},\n  {1e-136, 504, 368},\n  {1e-135, 499, 364},\n  {1e-134, 498, 364},\n  {1e-133, 493, 360},\n  {1e-132, 487, 354},\n  {1e-131, 486, 354},\n  {1e-130, 482, 352},\n  {1e-129, 477, 347},\n  {1e-128, 478, 350},\n  {1e-127, 473, 346},\n  {1e-126, 470, 343},\n  {1e-125, 468, 343},\n  {1e-124, 464, 339},\n  {1e-123, 459, 336},\n  {1e-122, 458, 336},\n  {1e-121, 452, 330},\n  {1e-120, 451, 330},\n  {1e-119, 448, 329},\n  {1e-118, 444, 325},\n  {1e-117, 441, 324},\n  {1e-116, 437, 320},\n  {1e-115, 431, 316},\n  {1e-114, 430, 316},\n  {1e-113, 428, 314},\n  {1e-112, 424, 311},\n  {1e-111, 421, 310},\n  {1e-110, 417, 307},\n  {1e-109, 414, 304},\n  {1e-108, 411, 303},\n  {1e-107, 407, 300},\n  {1e-106, 405, 298},\n  {1e-105, 401, 295},\n  {1e-104, 398, 293},\n  {1e-103, 395, 291},\n  {1e-102, 391, 288},\n  {1e-101, 388, 287},\n  {1e-100, 381, 281},\n  {1e-99, 380, 281},\n  {1e-98, 378, 279},\n  {1e-97, 375, 278},\n  {1e-96, 370, 273},\n  {1e-95, 368, 272},\n  {1e-94, 364, 269},\n  {1e-93, 360, 266},\n  {1e-92, 358, 265},\n  {1e-91, 355, 264},\n  {1e-90, 351, 260},\n  {1e-89, 348, 259},\n  {1e-88, 345, 256},\n  {1e-87, 342, 255},\n  {1e-86, 338, 252},\n  {1e-85, 334, 248},\n  {1e-84, 328, 244},\n  {1e-83, 327, 244},\n  {1e-82, 320, 237},\n  {1e-81, 319, 237},\n  {1e-80, 318, 237},\n  {1e-79, 313, 233},\n  {1e-78, 312, 233},\n  {1e-77, 306, 228},\n  {1e-76, 305, 228},\n  {1e-75, 299, 223},\n  {1e-74, 298, 223},\n  {1e-73, 295, 221},\n  {1e-72, 291, 218},\n  {1e-71, 287, 215},\n  {1e-70, 285, 214},\n  {1e-69, 280, 210},\n  {1e-68, 278, 210},\n  {1e-67, 275, 207},\n  {1e-66, 271, 204},\n  {1e-65, 268, 202},\n  {1e-64, 265, 200},\n  {1e-63, 262, 199},\n  {1e-62, 255, 193},\n  {1e-61, 254, 193},\n  {1e-60, 251, 190},\n  {1e-59, 245, 186},\n  {1e-58, 244, 186},\n  {1e-57, 242, 184},\n  {1e-56, 238, 182},\n  {1e-55, 235, 179},\n  {1e-54, 230, 176},\n  {1e-53, 229, 176},\n  {1e-52, 221, 169},\n  {1e-51, 220, 169},\n  {1e-50, 219, 169},\n  {1e-49, 215, 165},\n  {1e-48, 209, 160},\n  {1e-47, 208, 160},\n  {1e-46, 205, 159},\n  {1e-45, 202, 156},\n  {1e-44, 199, 154},\n  {1e-43, 195, 152},\n  {1e-42, 192, 150},\n  {1e-41, 189, 148},\n  {1e-40, 183, 142},\n  {1e-39, 182, 142},\n  {1e-38, 177, 138},\n  {1e-37, 175, 138},\n  {1e-36, 171, 134},\n  {1e-35, 169, 134},\n  {1e-34, 165, 130},\n  {1e-33, 160, 127},\n  {1e-32, 159, 127},\n  {1e-31, 148, 117},\n  {1e-30, 147, 117},\n  {1e-29, 149, 119},\n  {1e-28, 146, 117},\n  {1e-27, 138, 111},\n  {1e-26, 137, 111},\n  {1e-25, 136, 111},\n  {1e-24, 132, 107},\n  {1e-23, 129, 105},\n  {1e-22, 125, 103},\n  {1e-21, 122, 100},\n  {1e-20, 119, 98},\n  {1e-19, 114, 94},\n  {1e-18, 110, 92},\n  {1e-17, 109, 92},\n  {1e-16, 104, 87},\n  {1e-15, 101, 86},\n  {1e-14, 99, 84},\n  {1e-13, 95, 82},\n  {1e-12, 92, 79},\n  {1e-11, 89, 77},\n  {1e-10, 86, 76},\n  {1e-9, 82, 73},\n  {1e-8, 78, 70},\n  {1e-7, 73, 65},\n  {1e-6, 72, 65},\n  {1e-5, 69, 64},\n  {1e-4, 66, 62},\n  {1e-3, 60, 57},\n  {1e-2, 59, 57},\n  {1e-1, 55, 54},\n  {1e0, 0, 0},\n  {1e1, 0, 0},\n  {1e2, 0, 0},\n  {1e3, 0, 0},\n  {1e4, 0, 0},\n  {1e5, 0, 0},\n  {1e6, 0, 0},\n  {1e7, 0, 0},\n  {1e8, 0, 0},\n  {1e9, 0, 0},\n  {1e10, 0, 0},\n  {1e11, 0, 0},\n  {1e12, 0, 0},\n  {1e13, 0, 0},\n  {1e14, 0, 0},\n  {1e15, 0, 0},\n  {1e16, 0, 0},\n  {1e17, 0, 0},\n  {1e18, 0, 0},\n  {1e19, 0, 0},\n  {1e20, 0, 0},\n  {1e21, 0, 0},\n  {1e22, 0, 0},\n  {1e23, 0, 22},\n  {1e24, 0, 23},\n  {1e25, 0, 25},\n  {1e26, 0, 26},\n  {1e27, 0, 27},\n  {1e28, 0, 27},\n  {1e29, 0, 28},\n  {1e30, 0, 30},\n  {1e31, 0, 30},\n  {1e32, 0, 32},\n  {1e33, 0, 32},\n  {1e34, 0, 32},\n  {1e35, 0, 34},\n  {1e36, 0, 36},\n  {1e37, 0, 36},\n  {1e38, 0, 37},\n  {1e39, 0, 38},\n  {1e40, 0, 40},\n  {1e41, 0, 41},\n  {1e42, 0, 42},\n  {1e43, 0, 43},\n  {1e44, 0, 44},\n  {1e45, 0, 44},\n  {1e46, 0, 45},\n  {1e47, 0, 47},\n  {1e48, 0, 47},\n  {1e49, 0, 48},\n  {1e50, 0, 49},\n  {1e51, 0, 50},\n  {1e52, 0, 50},\n  {1e53, 0, 50},\n  {1e54, 0, 54},\n  {1e55, 0, 55},\n  {1e56, 0, 56},\n  {1e57, 0, 57},\n  {1e58, 0, 57},\n  {1e59, 0, 58},\n  {1e60, 0, 59},\n  {1e61, 0, 59},\n  {1e62, 0, 62},\n  {1e63, 0, 63},\n  {1e64, 0, 64},\n  {1e65, 0, 64},\n  {1e66, 0, 65},\n  {1e67, 0, 66},\n  {1e68, 0, 67},\n  {1e69, 0, 68},\n  {1e70, 0, 68},\n  {1e71, 0, 71},\n  {1e72, 0, 71},\n  {1e73, 0, 72},\n  {1e74, 0, 73},\n  {1e75, 0, 74},\n  {1e76, 0, 76},\n  {1e77, 0, 76},\n  {1e78, 0, 78},\n  {1e79, 0, 78},\n  {1e80, 0, 80},\n  {1e81, 0, 80},\n  {1e82, 0, 81},\n  {1e83, 0, 83},\n  {1e84, 0, 84},\n  {1e85, 0, 85},\n  {1e86, 0, 85},\n  {1e87, 0, 86},\n  {1e88, 0, 86},\n  {1e89, 0, 88},\n  {1e90, 0, 89},\n  {1e91, 0, 90},\n  {1e92, 0, 92},\n  {1e93, 0, 92},\n  {1e94, 0, 94},\n  {1e95, 0, 94},\n  {1e96, 0, 96},\n  {1e97, 0, 97},\n  {1e98, 0, 97},\n  {1e99, 0, 98},\n  {1e100, 0, 100},\n  {1e101, 0, 100},\n  {1e102, 0, 100},\n  {1e103, 0, 103},\n  {1e104, 0, 103},\n  {1e105, 0, 104},\n  {1e106, 0, 106},\n  {1e107, 0, 106},\n  {1e108, 0, 108},\n  {1e109, 0, 108},\n  {1e110, 0, 110},\n  {1e111, 0, 110},\n  {1e112, 0, 111},\n  {1e113, 0, 113},\n  {1e114, 0, 113},\n  {1e115, 0, 113},\n  {1e116, 0, 113},\n  {1e117, 0, 117},\n  {1e118, 0, 117},\n  {1e119, 0, 118},\n  {1e120, 0, 119},\n  {1e121, 0, 121},\n  {1e122, 0, 122},\n  {1e123, 0, 122},\n  {1e124, 0, 123},\n  {1e125, 0, 124},\n  {1e126, 0, 124},\n  {1e127, 0, 126},\n  {1e128, 0, 126},\n  {1e129, 0, 128},\n  {1e130, 0, 130},\n  {1e131, 0, 130},\n  {1e132, 0, 131},\n  {1e133, 0, 133},\n  {1e134, 0, 133},\n  {1e135, 0, 134},\n  {1e136, 0, 136},\n  {1e137, 0, 137},\n  {1e138, 0, 137},\n  {1e139, 0, 137},\n  {1e140, 0, 140},\n  {1e141, 0, 141},\n  {1e142, 0, 142},\n  {1e143, 0, 143},\n  {1e144, 0, 143},\n  {1e145, 0, 144},\n  {1e146, 0, 145},\n  {1e147, 0, 146},\n  {1e148, 0, 148},\n  {1e149, 0, 148},\n  {1e150, 0, 149},\n  {1e151, 0, 151},\n  {1e152, 0, 152},\n  {1e153, 0, 152},\n  {1e154, 0, 154},\n  {1e155, 0, 155},\n  {1e156, 0, 155},\n  {1e157, 0, 155},\n  {1e158, 0, 157},\n  {1e159, 0, 158},\n  {1e160, 0, 160},\n  {1e161, 0, 161},\n  {1e162, 0, 161},\n  {1e163, 0, 161},\n  {1e164, 0, 164},\n  {1e165, 0, 164},\n  {1e166, 0, 165},\n  {1e167, 0, 167},\n  {1e168, 0, 167},\n  {1e169, 0, 167},\n  {1e170, 0, 170},\n  {1e171, 0, 170},\n  {1e172, 0, 172},\n  {1e173, 0, 173},\n  {1e174, 0, 174},\n  {1e175, 0, 174},\n  {1e176, 0, 176},\n  {1e177, 0, 176},\n  {1e178, 0, 178},\n  {1e179, 0, 178},\n  {1e180, 0, 180},\n  {1e181, 0, 180},\n  {1e182, 0, 182},\n  {1e183, 0, 182},\n  {1e184, 0, 184},\n  {1e185, 0, 184},\n  {1e186, 0, 184},\n  {1e187, 0, 186},\n  {1e188, 0, 188},\n  {1e189, 0, 188},\n  {1e190, 0, 190},\n  {1e191, 0, 190},\n  {1e192, 0, 192},\n  {1e193, 0, 193},\n  {1e194, 0, 193},\n  {1e195, 0, 194},\n  {1e196, 0, 195},\n  {1e197, 0, 195},\n  {1e198, 0, 198},\n  {1e199, 0, 199},\n  {1e200, 0, 199},\n  {1e201, 0, 201},\n  {1e202, 0, 200},\n  {1e203, 0, 202},\n  {1e204, 0, 202},\n  {1e205, 0, 205},\n  {1e206, 0, 206},\n  {1e207, 0, 206},\n  {1e208, 0, 207},\n  {1e209, 0, 209},\n  {1e210, 0, 209},\n  {1e211, 0, 210},\n  {1e212, 0, 211},\n  {1e213, 0, 212},\n  {1e214, 0, 213},\n  {1e215, 0, 214},\n  {1e216, 0, 216},\n  {1e217, 0, 216},\n  {1e218, 0, 217},\n  {1e219, 0, 218},\n  {1e220, 0, 219},\n  {1e221, 0, 221},\n  {1e222, 0, 221},\n  {1e223, 0, 221},\n  {1e224, 0, 223},\n  {1e225, 0, 224},\n  {1e226, 0, 225},\n  {1e227, 0, 225},\n  {1e228, 0, 227},\n  {1e229, 0, 228},\n  {1e230, 0, 230},\n  {1e231, 0, 231},\n  {1e232, 0, 231},\n  {1e233, 0, 232},\n  {1e234, 0, 234},\n  {1e235, 0, 235},\n  {1e236, 0, 235},\n  {1e237, 0, 235},\n  {1e238, 0, 238},\n  {1e239, 0, 238},\n  {1e240, 0, 240},\n  {1e241, 0, 241},\n  {1e242, 0, 241},\n  {1e243, 0, 243},\n  {1e244, 0, 243},\n  {1e245, 0, 245},\n  {1e246, 0, 246},\n  {1e247, 0, 246},\n  {1e248, 0, 248},\n  {1e249, 0, 247},\n  {1e250, 0, 247},\n  {1e251, 0, 251},\n  {1e252, 0, 252},\n  {1e253, 0, 252},\n  {1e254, 0, 252},\n  {1e255, 0, 254},\n  {1e256, 0, 256},\n  {1e257, 0, 256},\n  {1e258, 0, 258},\n  {1e259, 0, 258},\n  {1e260, 0, 260},\n  {1e261, 0, 258},\n  {1e262, 0, 262},\n  {1e263, 0, 262},\n  {1e264, 0, 264},\n  {1e265, 0, 265},\n  {1e266, 0, 266},\n  {1e267, 0, 266},\n  {1e268, 0, 266},\n  {1e269, 0, 269},\n  {1e270, 0, 269},\n  {1e271, 0, 270},\n  {1e272, 0, 272},\n  {1e273, 0, 272},\n  {1e274, 0, 273},\n  {1e275, 0, 274},\n  {1e276, 0, 276},\n  {1e277, 0, 277},\n  {1e278, 0, 277},\n  {1e279, 0, 279},\n  {1e280, 0, 280},\n  {1e281, 0, 280},\n  {1e282, 0, 280},\n  {1e283, 0, 282},\n  {1e284, 0, 284},\n  {1e285, 0, 284},\n  {1e286, 0, 286},\n  {1e287, 0, 287},\n  {1e288, 0, 288},\n  {1e289, 0, 289},\n  {1e290, 0, 289},\n  {1e291, 0, 290},\n  {1e292, 0, 292},\n  {1e293, 0, 292},\n  {1e294, 0, 294},\n  {1e295, 0, 294},\n  {1e296, 0, 294},\n  {1e297, 0, 297},\n  {1e298, 0, 297},\n  {1e299, 0, 299},\n  {1e300, 0, 299},\n  {1e301, 0, 299},\n  {1e302, 0, 302},\n  {1e303, 0, 303},\n  {1e304, 0, 303},\n  {1e305, 0, 303},\n  {1e306, 0, 306},\n  {1e307, 0, 306},\n  {1e308, 0, 308},\n] of _\n\n# These values test every binary exponent (which would be more obvious with hexfloats).\n# The mantissas were randomly generated.\nALL_BINARY_EXPONENTS = [\n  {8.667315560151837e-309, 1074, 765},\n  {3.402496288854889e-308, 1072, 764},\n  {5.674095874064163e-308, 1068, 760},\n  {1.5540526173622352e-307, 1072, 765},\n  {1.990320005135143e-307, 1071, 764},\n  {5.107418539046847e-307, 1070, 763},\n  {9.580341507134765e-307, 1068, 761},\n  {2.7686246461533843e-306, 1067, 761},\n  {4.123539674247709e-306, 1066, 760},\n  {1.0292523314007669e-305, 1065, 760},\n  {1.869562966771459e-305, 1065, 760},\n  {2.83258275754601e-305, 1063, 758},\n  {6.143575467580988e-305, 1061, 756},\n  {1.4317575812076783e-304, 1062, 758},\n  {1.8736954404407843e-304, 1061, 757},\n  {3.8678085703767116e-304, 1060, 756},\n  {7.635297514304952e-304, 1058, 754},\n  {1.9931833457401843e-303, 1056, 753},\n  {5.7104439440441904e-303, 1057, 754},\n  {5.845773229837182e-303, 1056, 753},\n  {1.6446596413123796e-302, 1054, 752},\n  {3.571334515579776e-302, 1054, 752},\n  {5.226450761688444e-302, 1053, 751},\n  {1.0747997253788843e-301, 1051, 750},\n  {1.90892926200005e-301, 1051, 750},\n  {6.786272682763782e-301, 1047, 746},\n  {1.2172372613102281e-300, 1044, 744},\n  {1.7966676643662128e-300, 1042, 742},\n  {5.462819694528703e-300, 1047, 747},\n  {6.961706248722051e-300, 1043, 743},\n  {1.5112507155402445e-299, 1045, 746},\n  {2.673980873749012e-299, 1044, 745},\n  {8.503369157355244e-299, 1043, 744},\n  {1.3233106901096892e-298, 1042, 744},\n  {2.129178977672023e-298, 1041, 743},\n  {4.178648615235546e-298, 1036, 738},\n  {9.216189377840373e-298, 1038, 740},\n  {2.0166542228894894e-297, 1036, 739},\n  {3.5449747515908894e-297, 1034, 737},\n  {8.064939085743458e-297, 1036, 739},\n  {2.1182119864764602e-296, 1035, 739},\n  {3.820427653349436e-296, 1034, 738},\n  {9.166969936802109e-296, 1033, 737},\n  {1.0587007370833377e-295, 1031, 736},\n  {2.5617734189585968e-295, 1030, 735},\n  {6.117667163341597e-295, 1030, 735},\n  {1.2442918509206014e-294, 1028, 734},\n  {3.100051235126117e-294, 1027, 733},\n  {5.514599643126014e-294, 1026, 732},\n  {8.019518080011996e-294, 1025, 731},\n  {2.2148549407571022e-293, 1022, 729},\n  {4.20911452464387e-293, 1024, 731},\n  {6.673970944986261e-293, 1022, 729},\n  {1.746546364023377e-292, 1021, 729},\n  {2.5532945426002905e-292, 1021, 729},\n  {7.167199051004838e-292, 1020, 728},\n  {1.4331413352279302e-291, 1018, 727},\n  {1.6537479214140957e-291, 1018, 727},\n  {4.290843909020547e-291, 1017, 726},\n  {9.634563122144926e-291, 1014, 723},\n  {2.1086057222075156e-290, 1010, 720},\n  {3.4206800637910767e-290, 1013, 723},\n  {6.629016758967737e-290, 1013, 723},\n  {1.352094685757577e-289, 1011, 722},\n  {3.065153212531983e-289, 1011, 722},\n  {5.074022975467963e-289, 1006, 717},\n  {1.5066401531008766e-288, 1009, 721},\n  {2.8015937941494583e-288, 1008, 720},\n  {5.38303645298669e-288, 1007, 719},\n  {8.441234238050258e-288, 1006, 718},\n  {1.7229963037415354e-287, 1004, 717},\n  {4.998022788892795e-287, 1004, 717},\n  {8.728697912491584e-287, 1003, 716},\n  {1.9042271010497776e-286, 1002, 716},\n  {2.9003728382951163e-286, 1000, 714},\n  {6.030230011480532e-286, 999, 713},\n  {1.2707290656410452e-285, 997, 712},\n  {3.3470583063356332e-285, 998, 713},\n  {6.5966578537292035e-285, 997, 712},\n  {7.427398668320939e-285, 995, 710},\n  {2.5312725644714646e-284, 994, 710},\n  {4.7441324100905746e-284, 994, 710},\n  {7.352811059453878e-284, 992, 708},\n  {1.2773195914029405e-283, 990, 707},\n  {3.1336514683809336e-283, 991, 708},\n  {4.624339948076099e-283, 990, 707},\n  {8.894087772740341e-283, 989, 706},\n  {2.3953652083727512e-282, 988, 706},\n  {3.761567466326268e-282, 985, 703},\n  {1.2928212167160486e-281, 985, 704},\n  {2.2480352182123753e-281, 982, 701},\n  {3.154850939942071e-281, 983, 702},\n  {6.071363984983044e-281, 982, 701},\n  {2.1910202165183368e-280, 981, 701},\n  {4.3370603492214e-280, 980, 700},\n  {6.968471572869319e-280, 980, 700},\n  {1.473331115243779e-279, 979, 700},\n  {2.66044060949483e-279, 971, 692},\n  {5.683973436284207e-279, 976, 697},\n  {1.4089452631685327e-278, 976, 698},\n  {2.2755564362864927e-278, 973, 695},\n  {3.821906858286435e-278, 973, 695},\n  {8.103117627864485e-278, 973, 695},\n  {1.832473425427903e-277, 970, 693},\n  {2.727222054531752e-277, 970, 693},\n  {7.882910515028218e-277, 964, 687},\n  {1.479332526434952e-276, 966, 690},\n  {2.2180410620430226e-276, 968, 692},\n  {5.690649743658698e-276, 966, 690},\n  {1.4121156100358157e-275, 966, 691},\n  {2.6685811370633676e-275, 965, 690},\n  {4.6692815012344967e-275, 963, 688},\n  {9.834548098669551e-275, 958, 683},\n  {1.8600057734718372e-274, 962, 688},\n  {2.811775675630843e-274, 961, 687},\n  {6.27142702954425e-274, 959, 685},\n  {1.0250975871333601e-273, 959, 686},\n  {2.5253325080332935e-273, 958, 685},\n  {6.913296125147658e-273, 957, 684},\n  {9.747372287629775e-273, 956, 683},\n  {1.9721670909138485e-272, 953, 681},\n  {4.469048372679605e-272, 953, 681},\n  {1.0205638445192075e-271, 952, 681},\n  {1.5594946591458144e-271, 952, 681},\n  {2.3721122629194564e-271, 949, 678},\n  {5.551135919284155e-271, 944, 673},\n  {1.0734739674446298e-270, 947, 677},\n  {2.4398554262702667e-270, 948, 678},\n  {6.8642158764630495e-270, 942, 672},\n  {1.2055339469240307e-269, 941, 672},\n  {1.5235738192618518e-269, 945, 676},\n  {4.043876497442965e-269, 944, 675},\n  {8.743556251142875e-269, 941, 672},\n  {1.4649312749107113e-268, 941, 673},\n  {3.98040680032538e-268, 941, 673},\n  {8.586286660615516e-268, 938, 670},\n  {1.657418018730156e-267, 939, 672},\n  {3.613106834542997e-267, 937, 670},\n  {5.9183241751764955e-267, 936, 669},\n  {1.1798475245572565e-266, 935, 669},\n  {3.084511928870661e-266, 934, 668},\n  {4.1367697432326765e-266, 930, 664},\n  {1.1356367562498619e-265, 933, 668},\n  {1.5494624584252724e-265, 928, 663},\n  {3.028575537095104e-265, 931, 666},\n  {9.42740333348115e-265, 930, 665},\n  {1.729174836691517e-264, 929, 665},\n  {2.1531241335994103e-264, 928, 664},\n  {7.094381157920946e-264, 927, 663},\n  {1.037244238361319e-263, 922, 659},\n  {1.6682266784627316e-263, 925, 662},\n  {5.516139500818587e-263, 924, 661},\n  {1.1456020464232508e-262, 919, 657},\n  {2.066981873698563e-262, 919, 657},\n  {2.7219184652406806e-262, 921, 659},\n  {5.081283542674735e-262, 918, 656},\n  {1.6876650530578208e-261, 917, 656},\n  {2.276883127404377e-261, 916, 655},\n  {6.29191834725416e-261, 917, 656},\n  {9.114070309202437e-261, 915, 654},\n  {2.5433112499173348e-260, 909, 649},\n  {4.8383034142174994e-260, 914, 654},\n  {9.557162041008455e-260, 913, 653},\n  {2.3218163991190385e-259, 912, 653},\n  {4.4322942901196377e-259, 911, 652},\n  {7.248624545665186e-259, 904, 645},\n  {1.4667081257360905e-258, 909, 651},\n  {3.832202635359104e-258, 908, 650},\n  {6.943148025853397e-258, 905, 647},\n  {9.18729939902564e-258, 906, 648},\n  {3.0584908958671776e-257, 905, 648},\n  {3.332730679024704e-257, 901, 644},\n  {7.495951055510854e-257, 902, 645},\n  {1.6420266726944826e-256, 900, 644},\n  {3.9297951542678785e-256, 900, 644},\n  {8.581799124622545e-256, 896, 640},\n  {1.3408011506316038e-255, 899, 644},\n  {3.074598537137766e-255, 898, 643},\n  {8.304947313449564e-255, 893, 638},\n  {1.4474411243713317e-254, 893, 639},\n  {1.9586457971591685e-254, 895, 641},\n  {4.686715367492042e-254, 894, 640},\n  {1.3394037849881335e-253, 893, 640},\n  {2.3636119615931954e-253, 892, 639},\n  {2.941610445695594e-253, 891, 638},\n  {5.910628843543644e-253, 890, 637},\n  {1.6810396848558274e-252, 889, 637},\n  {3.105977744955943e-252, 887, 635},\n  {5.296557380144569e-252, 886, 634},\n  {1.4534181673457758e-251, 886, 635},\n  {2.3938479247234097e-251, 885, 634},\n  {5.13191114201037e-251, 884, 633},\n  {8.74142907505523e-251, 883, 632},\n  {2.0810847152471556e-250, 881, 631},\n  {3.470210840000035e-250, 879, 629},\n  {7.63186683615216e-250, 879, 629},\n  {1.5834019337617896e-249, 872, 623},\n  {2.760497402953147e-249, 876, 627},\n  {4.645119455324602e-249, 877, 628},\n  {9.91990200069646e-249, 874, 625},\n  {2.5293584256227207e-248, 874, 626},\n  {6.890528046616735e-248, 874, 626},\n  {9.9700406511611e-248, 870, 622},\n  {2.0741563200652303e-247, 872, 625},\n  {4.6848861238967826e-247, 869, 622},\n  {7.812879056117969e-247, 870, 623},\n  {1.16023447415079e-246, 868, 622},\n  {2.6285314099653654e-246, 867, 621},\n  {7.24619526711458e-246, 866, 620},\n  {1.513557579372713e-245, 864, 619},\n  {2.2022598987138438e-245, 864, 619},\n  {4.1410759136055036e-245, 864, 619},\n  {1.0252325908734784e-244, 863, 619},\n  {2.0526028348315092e-244, 861, 617},\n  {3.9232668413253934e-244, 861, 617},\n  {7.579615073559597e-244, 859, 615},\n  {2.042372149921196e-243, 859, 616},\n  {2.9353293314833334e-243, 858, 615},\n  {6.372363858570045e-243, 855, 612},\n  {1.575234414235322e-242, 854, 612},\n  {3.3434436478293493e-242, 855, 613},\n  {5.199566493700135e-242, 853, 611},\n  {1.151222665835575e-241, 853, 612},\n  {1.5543636774561637e-241, 850, 609},\n  {4.079493099457495e-241, 850, 609},\n  {8.503783454502844e-241, 850, 609},\n  {2.0072820595479573e-240, 845, 605},\n  {3.536438208901198e-240, 845, 605},\n  {8.722809014595794e-240, 845, 605},\n  {1.3749379101340066e-239, 846, 607},\n  {3.05119881849728e-239, 845, 606},\n  {6.2081425746751336e-239, 844, 605},\n  {1.5212878641555803e-238, 841, 603},\n  {2.521616635184641e-238, 842, 604},\n  {4.498289788351931e-238, 841, 603},\n  {7.28128014416453e-238, 834, 596},\n  {2.0208837171448896e-237, 839, 602},\n  {4.2234650668214423e-237, 837, 600},\n  {8.084125743595665e-237, 836, 599},\n  {1.6733590187416607e-236, 835, 599},\n  {2.744824473945537e-236, 835, 599},\n  {6.9330212812845956e-236, 832, 596},\n  {9.887608848404935e-236, 831, 595},\n  {1.8140520866375598e-235, 832, 597},\n  {3.4634694166405886e-235, 831, 596},\n  {1.0308725436052673e-234, 830, 596},\n  {1.3123008280909585e-234, 829, 595},\n  {3.87322423781539e-234, 825, 591},\n  {9.2010739647813e-234, 827, 593},\n  {1.1992483584063614e-233, 817, 584},\n  {2.3452909097606e-233, 823, 590},\n  {6.55832259537843e-233, 824, 591},\n  {1.01023596160976e-232, 822, 590},\n  {2.5973213970621353e-232, 822, 590},\n  {4.955980006523515e-232, 821, 589},\n  {1.1244377262803144e-231, 820, 589},\n  {2.4089050126555283e-231, 819, 588},\n  {3.1957993198054626e-231, 815, 584},\n  {6.380419758740376e-231, 816, 585},\n  {1.6095894110621704e-230, 816, 586},\n  {4.032612821947131e-230, 813, 583},\n  {6.0387918131443855e-230, 811, 581},\n  {8.974323011245674e-230, 813, 583},\n  {1.8798157375327938e-229, 812, 583},\n  {6.1019603119906395e-229, 810, 581},\n  {1.1326182622927228e-228, 808, 580},\n  {1.3463284210193606e-228, 808, 580},\n  {4.951126320251196e-228, 806, 578},\n  {8.472853590985601e-228, 806, 578},\n  {1.268665118648964e-227, 802, 575},\n  {2.688551806792703e-227, 805, 578},\n  {4.421464033750511e-227, 803, 576},\n  {1.6504299596692555e-226, 803, 577},\n  {2.290009142698998e-226, 799, 573},\n  {4.095081668886285e-226, 800, 574},\n  {1.15703960191472e-225, 799, 574},\n  {1.8221726162720878e-225, 799, 574},\n  {3.7536981380660996e-225, 798, 573},\n  {7.40644242989838e-225, 797, 572},\n  {1.5302655517853282e-224, 796, 572},\n  {3.266244852085614e-224, 794, 570},\n  {6.839580807204396e-224, 794, 570},\n  {1.1596240640257126e-223, 792, 569},\n  {2.137739829010208e-223, 792, 569},\n  {5.154905434635419e-223, 790, 567},\n  {7.890896642961214e-223, 790, 567},\n  {2.4268259861629554e-222, 788, 566},\n  {5.1376518013281177e-222, 788, 566},\n  {6.49233931232799e-222, 785, 563},\n  {1.5726294053843144e-221, 784, 563},\n  {2.5630401696102993e-221, 783, 562},\n  {7.564140571297787e-221, 784, 563},\n  {1.572950734084536e-220, 783, 563},\n  {3.270170075841635e-220, 782, 562},\n  {3.794310430010385e-220, 781, 561},\n  {7.2152364285788e-220, 780, 560},\n  {2.6272169024527132e-219, 779, 560},\n  {4.969214075705504e-219, 778, 559},\n  {5.88301228914231e-219, 777, 558},\n  {1.4743436359705467e-218, 776, 558},\n  {3.4783661139951837e-218, 774, 556},\n  {5.813216384022507e-218, 774, 556},\n  {1.4584641434878504e-217, 769, 552},\n  {2.3948318645722384e-217, 771, 554},\n  {6.217084198272334e-217, 771, 554},\n  {1.2105626076543997e-216, 770, 554},\n  {2.7486687055906063e-216, 768, 552},\n  {3.502765745049966e-216, 768, 552},\n  {9.371878794082114e-216, 765, 549},\n  {2.2015678401956502e-215, 766, 551},\n  {3.05741111150528e-215, 765, 550},\n  {5.373256321068217e-215, 764, 549},\n  {1.0390415242853729e-214, 763, 549},\n  {2.5621446229095083e-214, 761, 547},\n  {6.657612640369708e-214, 759, 545},\n  {1.0096315129023046e-213, 760, 547},\n  {2.385050068827949e-213, 759, 546},\n  {4.872323574333538e-213, 756, 543},\n  {1.1263700063301884e-212, 757, 545},\n  {1.7927923533931953e-212, 755, 543},\n  {4.1945272696274144e-212, 755, 543},\n  {7.22363072585383e-212, 752, 540},\n  {9.64175435706941e-212, 753, 541},\n  {2.805853499967036e-211, 752, 541},\n  {7.509881594270143e-211, 750, 539},\n  {1.4162133808983744e-210, 750, 540},\n  {2.760873558001602e-210, 749, 539},\n  {5.239340425140705e-210, 748, 538},\n  {1.1985208897227799e-209, 747, 538},\n  {1.7811558999586524e-209, 743, 534},\n  {4.196264296353732e-209, 745, 536},\n  {9.615178102174303e-209, 744, 535},\n  {1.8427321798270692e-208, 742, 534},\n  {2.9126998289538513e-208, 742, 534},\n  {4.61528401072508e-208, 740, 532},\n  {1.3913867725663096e-207, 735, 528},\n  {1.7692162295985285e-207, 736, 529},\n  {3.3309984084439476e-207, 738, 531},\n  {6.851070184689407e-207, 736, 529},\n  {2.3088921688496224e-206, 736, 530},\n  {4.569104830644564e-206, 735, 529},\n  {9.727078311637122e-206, 733, 527},\n  {1.0256959546160606e-205, 733, 528},\n  {3.8508135367330314e-205, 725, 520},\n  {5.362873934497765e-205, 731, 526},\n  {1.178267040698183e-204, 727, 523},\n  {1.9633198540986484e-204, 727, 523},\n  {5.684033913906095e-204, 728, 524},\n  {1.0645321758200814e-203, 727, 524},\n  {1.6758731287663245e-203, 726, 523},\n  {4.3010538577728234e-203, 724, 521},\n  {8.557459533511549e-203, 721, 518},\n  {1.1995252022699291e-202, 723, 521},\n  {3.224746096518046e-202, 719, 517},\n  {5.329245319538759e-202, 720, 518},\n  {8.264074174120558e-202, 719, 517},\n  {2.6560666151344703e-201, 718, 517},\n  {4.349521497293475e-201, 718, 517},\n  {1.049722365714753e-200, 717, 517},\n  {2.110793667228417e-200, 716, 516},\n  {4.12255833883331e-200, 713, 513},\n  {7.981480278624725e-200, 714, 514},\n  {1.7718621999986475e-199, 711, 512},\n  {4.0389599243158305e-199, 712, 513},\n  {5.111949257928288e-199, 711, 512},\n  {1.0079029980377604e-198, 710, 512},\n  {3.3000803685506845e-198, 709, 511},\n  {5.525608163454049e-198, 708, 510},\n  {1.3309156466126676e-197, 706, 509},\n  {2.537666484635188e-197, 704, 507},\n  {4.1725845639630823e-197, 702, 505},\n  {8.054891565255361e-197, 700, 503},\n  {1.5502849382509784e-196, 701, 505},\n  {3.36121320874639e-196, 702, 506},\n  {7.484551181550847e-196, 700, 504},\n  {8.735452590688936e-196, 699, 503},\n  {2.178462933634283e-195, 698, 503},\n  {3.537794875075355e-195, 698, 503},\n  {1.2853400648125002e-194, 694, 500},\n  {2.4171125083478186e-194, 694, 500},\n  {5.266324045366901e-194, 691, 497},\n  {8.478256274282183e-194, 692, 498},\n  {1.1953794466200875e-193, 693, 500},\n  {2.260950903334843e-193, 691, 498},\n  {6.350686450423479e-193, 690, 497},\n  {1.6719092307496788e-192, 690, 498},\n  {2.8861375664964044e-192, 686, 494},\n  {6.643795862244709e-192, 687, 495},\n  {1.2021435877944513e-191, 687, 496},\n  {1.7389964555399593e-191, 686, 495},\n  {5.506102911439653e-191, 684, 493},\n  {6.118268064240606e-191, 682, 491},\n  {2.0188412709311976e-190, 683, 493},\n  {3.623294624463911e-190, 681, 491},\n  {6.109098683649796e-190, 681, 491},\n  {1.0644449452139396e-189, 680, 491},\n  {2.2861786222533955e-189, 679, 490},\n  {5.1576905486221095e-189, 678, 489},\n  {1.4282019181687436e-188, 677, 489},\n  {2.5645102658000638e-188, 676, 488},\n  {5.24708561795544e-188, 675, 487},\n  {1.0885720130033328e-187, 674, 487},\n  {2.155560898279388e-187, 673, 486},\n  {3.40475602113904e-187, 669, 482},\n  {8.113439450169649e-187, 669, 482},\n  {1.2501335652550157e-186, 669, 483},\n  {2.921645514519799e-186, 667, 481},\n  {4.02036891910035e-186, 668, 482},\n  {1.1406014918543049e-185, 667, 482},\n  {2.234456407425393e-185, 666, 481},\n  {3.119559897062789e-185, 665, 480},\n  {7.840889282617378e-185, 663, 478},\n  {1.913574910036887e-184, 663, 479},\n  {4.522727919382536e-184, 662, 478},\n  {8.399922966296771e-184, 661, 477},\n  {1.6000703209667126e-183, 660, 477},\n  {2.579345132582455e-183, 656, 473},\n  {5.82883665073076e-183, 657, 474},\n  {1.1477041919553152e-182, 655, 473},\n  {2.162629407013832e-182, 653, 471},\n  {3.534083085250713e-182, 655, 473},\n  {1.1362518681203582e-181, 654, 473},\n  {1.374874967440177e-181, 652, 471},\n  {2.435650549099599e-181, 652, 471},\n  {7.063226461599998e-181, 651, 470},\n  {1.502554163537965e-180, 650, 470},\n  {2.3571270014353365e-180, 648, 468},\n  {6.74887905042332e-180, 647, 467},\n  {8.516860687556732e-180, 645, 465},\n  {3.055786593521296e-179, 642, 463},\n  {4.069012575835782e-179, 645, 466},\n  {1.0237088948578388e-178, 644, 466},\n  {1.54322893356522e-178, 643, 465},\n  {4.787867741637937e-178, 641, 463},\n  {6.628678990891977e-178, 639, 461},\n  {1.6597719647556535e-177, 638, 461},\n  {3.9129616361835475e-177, 638, 461},\n  {4.917433318520185e-177, 637, 460},\n  {1.4065772131663602e-176, 637, 461},\n  {2.9943788764426967e-176, 636, 460},\n  {5.798827328485947e-176, 633, 457},\n  {6.383737379183111e-176, 629, 453},\n  {1.702914739406024e-175, 631, 456},\n  {3.8762215209996965e-175, 632, 457},\n  {6.878988216191751e-175, 631, 456},\n  {1.4887562197711447e-174, 629, 455},\n  {3.5651743098904664e-174, 628, 454},\n  {4.061595423920378e-174, 628, 454},\n  {1.080598113210515e-173, 626, 453},\n  {2.610049033590998e-173, 626, 453},\n  {5.479224332391463e-173, 625, 452},\n  {1.045438016267349e-172, 624, 452},\n  {1.6069032799845684e-172, 623, 451},\n  {3.231763455627856e-172, 621, 449},\n  {8.881611379300798e-172, 621, 449},\n  {2.034516015869994e-171, 620, 449},\n  {2.1811364732319512e-171, 618, 447},\n  {8.077406891608053e-171, 618, 447},\n  {1.3085617726519542e-170, 617, 447},\n  {1.8431087174635782e-170, 616, 446},\n  {4.132806492532133e-170, 613, 443},\n  {1.2069870317233675e-169, 613, 444},\n  {2.5119843525307734e-169, 610, 441},\n  {4.9132134419173275e-169, 612, 443},\n  {7.82202882219777e-169, 611, 442},\n  {1.4528932941845978e-168, 605, 437},\n  {2.1572028357831315e-168, 608, 440},\n  {6.050734210571264e-168, 607, 439},\n  {1.4678367854256142e-167, 605, 438},\n  {3.164114305557189e-167, 606, 439},\n  {5.701265996213117e-167, 605, 438},\n  {1.0449784962477288e-166, 603, 437},\n  {1.412310305063278e-166, 603, 437},\n  {3.0165478216464075e-166, 602, 436},\n  {6.935268655974538e-166, 601, 435},\n  {1.5523915631659218e-165, 599, 434},\n  {3.204088122440647e-165, 599, 434},\n  {5.708706599661439e-165, 596, 431},\n  {1.3233342513427447e-164, 594, 430},\n  {2.902749268222377e-164, 594, 430},\n  {4.005031318533225e-164, 594, 430},\n  {1.370839408075575e-163, 593, 430},\n  {2.5492926252346404e-163, 592, 429},\n  {4.5041091507036546e-163, 592, 429},\n  {6.727683165794e-163, 591, 428},\n  {1.2036075452964588e-162, 590, 428},\n  {3.694027105345616e-162, 588, 426},\n  {8.037749748602946e-162, 588, 426},\n  {1.5950567846572765e-161, 587, 426},\n  {3.2198008776677366e-161, 581, 420},\n  {4.1052288903310634e-161, 585, 424},\n  {1.1247015311011619e-160, 584, 424},\n  {2.8199919854660424e-160, 581, 421},\n  {3.44065016480784e-160, 582, 422},\n  {1.0582157294697289e-159, 581, 422},\n  {1.3388582036266356e-159, 580, 421},\n  {3.9808070524262983e-159, 578, 419},\n  {6.84874465910972e-159, 577, 418},\n  {1.5742030359655316e-158, 577, 419},\n  {2.9202734088397783e-158, 576, 418},\n  {4.963143223862366e-158, 575, 417},\n  {1.2414975541479183e-157, 574, 417},\n  {1.7875996042719942e-157, 573, 416},\n  {4.442886865786028e-157, 572, 415},\n  {9.640850940808092e-157, 570, 413},\n  {1.3646071402129266e-156, 566, 410},\n  {3.95713245503973e-156, 567, 411},\n  {7.069217936658262e-156, 568, 412},\n  {1.203556015715792e-155, 566, 411},\n  {2.317281696978397e-155, 566, 411},\n  {6.4262723679026235e-155, 565, 410},\n  {9.730107236224658e-155, 563, 408},\n  {2.2224325771347584e-154, 558, 404},\n  {5.423112811224111e-154, 562, 408},\n  {7.326435394881725e-154, 558, 404},\n  {2.3801222320149625e-153, 560, 407},\n  {3.0618730536691986e-153, 559, 406},\n  {8.347194867235908e-153, 555, 402},\n  {1.3657896771381836e-152, 555, 403},\n  {2.7059739547586404e-152, 555, 403},\n  {7.577545297109583e-152, 555, 403},\n  {1.1796090875694487e-151, 553, 402},\n  {2.794960489625111e-151, 551, 400},\n  {3.2753828239853734e-151, 548, 397},\n  {9.593803205082154e-151, 549, 398},\n  {1.582863447572883e-150, 549, 399},\n  {2.6064697688781666e-150, 546, 396},\n  {9.557214378356368e-150, 546, 396},\n  {1.191935964515715e-149, 546, 397},\n  {2.433444775335062e-149, 545, 396},\n  {6.832519569622276e-149, 535, 386},\n  {8.70223166700988e-149, 544, 395},\n  {2.9861508065908777e-148, 543, 395},\n  {4.817811775899338e-148, 540, 392},\n  {9.297672441478371e-148, 540, 392},\n  {2.4845337797454605e-147, 535, 388},\n  {2.885736152741721e-147, 536, 389},\n  {7.338298742534727e-147, 538, 391},\n  {1.6347598189352822e-146, 537, 391},\n  {3.7793226434705114e-146, 536, 390},\n  {4.216777346143084e-146, 535, 389},\n  {1.5906736035433479e-145, 534, 389},\n  {2.8672951771554432e-145, 527, 382},\n  {3.4379041633188434e-145, 532, 387},\n  {9.483896231013576e-145, 531, 386},\n  {2.1982900216496952e-144, 530, 386},\n  {4.601395928544956e-144, 528, 384},\n  {7.926089617836489e-144, 528, 384},\n  {1.126604874345762e-143, 527, 384},\n  {3.7906989762760977e-143, 526, 383},\n  {6.436458682719877e-143, 525, 382},\n  {1.4199943377251188e-142, 521, 379},\n  {2.9387007315399e-142, 523, 381},\n  {4.321275750819566e-142, 520, 378},\n  {7.47956749010095e-142, 520, 378},\n  {1.598743985708575e-141, 520, 379},\n  {3.8772543479398746e-141, 519, 378},\n  {8.857290458084179e-141, 518, 377},\n  {1.4852721614344735e-140, 517, 377},\n  {3.2114166221217845e-140, 516, 376},\n  {6.843053332100658e-140, 515, 375},\n  {1.565107103951972e-139, 514, 375},\n  {2.0617783292012132e-139, 513, 374},\n  {5.660205412204873e-139, 511, 372},\n  {9.624163455997459e-139, 509, 370},\n  {2.1752909285251192e-138, 509, 371},\n  {4.3072629120188103e-138, 509, 371},\n  {8.87808969808457e-138, 507, 369},\n  {1.2102860505856427e-137, 506, 369},\n  {4.0538881189604875e-137, 506, 369},\n  {4.942621032233825e-137, 503, 366},\n  {1.5411744957634479e-136, 502, 366},\n  {2.217010614359645e-136, 502, 366},\n  {6.356757540897539e-136, 499, 363},\n  {9.59982670654558e-136, 501, 365},\n  {2.6352597315051092e-135, 500, 365},\n  {4.4355852076232534e-135, 497, 362},\n  {5.742999336614182e-135, 498, 363},\n  {1.5533843624429324e-134, 497, 363},\n  {2.38869404724781e-134, 496, 362},\n  {4.887121523587299e-134, 492, 358},\n  {1.2048289504187996e-133, 494, 361},\n  {2.6488657681982224e-133, 491, 358},\n  {5.6050315915746605e-133, 489, 356},\n  {1.2786580967537967e-132, 489, 357},\n  {2.084833548391119e-132, 490, 358},\n  {3.2003528362030217e-132, 488, 356},\n  {1.0296499233307166e-131, 488, 357},\n  {2.24263947672835e-131, 486, 355},\n  {2.4568729171490547e-131, 485, 354},\n  {6.620060796036904e-131, 483, 352},\n  {1.4197241995605936e-130, 484, 354},\n  {3.6028982430553334e-130, 482, 352},\n  {4.887939370696328e-130, 481, 351},\n  {7.31748922552339e-130, 481, 351},\n  {2.342962029692207e-129, 479, 350},\n  {3.2020192787585524e-129, 479, 350},\n  {6.424759263634416e-129, 478, 349},\n  {1.2342695551721854e-128, 476, 348},\n  {4.4587660431030846e-128, 476, 348},\n  {8.593040354013083e-128, 471, 343},\n  {1.2304010474281754e-127, 474, 347},\n  {2.5763001930484683e-127, 473, 346},\n  {3.839904229353329e-127, 469, 342},\n  {1.1107053483914007e-126, 470, 344},\n  {2.62113368468091e-126, 469, 343},\n  {3.9436012113465104e-126, 468, 342},\n  {1.1279031828486725e-125, 467, 342},\n  {1.359122081875358e-125, 467, 342},\n  {4.718684877818967e-125, 466, 341},\n  {9.34382880965103e-125, 464, 339},\n  {1.3234786366979427e-124, 463, 339},\n  {3.421581321410546e-124, 463, 339},\n  {7.366599240502793e-124, 461, 337},\n  {1.2895722651891138e-123, 460, 337},\n  {2.921936515059182e-123, 455, 332},\n  {3.2696466282687586e-123, 454, 331},\n  {6.399893206284219e-123, 458, 335},\n  {1.4998583170769895e-122, 456, 334},\n  {3.302628563203096e-122, 456, 334},\n  {5.784701017386636e-122, 454, 332},\n  {1.2327531669436545e-121, 454, 333},\n  {3.625335804997574e-121, 451, 330},\n  {6.509903526347757e-121, 451, 330},\n  {1.0362549937904696e-120, 448, 328},\n  {2.8372763744588855e-120, 448, 328},\n  {4.695776279670974e-120, 449, 329},\n  {9.262841373599042e-120, 448, 328},\n  {1.864465441767582e-119, 445, 326},\n  {4.010086210363826e-119, 444, 325},\n  {9.843181783316012e-119, 445, 326},\n  {1.8986595125046417e-118, 438, 320},\n  {3.508320544508897e-118, 443, 325},\n  {4.943071564858405e-118, 440, 322},\n  {1.4386748780689463e-117, 441, 324},\n  {2.1750535480226044e-117, 440, 323},\n  {3.5554120237464135e-117, 439, 322},\n  {1.2023735450313651e-116, 432, 316},\n  {2.4073250149223044e-116, 437, 321},\n  {4.1701370789218156e-116, 434, 318},\n  {1.0102559419519476e-115, 435, 320},\n  {1.5737569358633548e-115, 433, 318},\n  {3.5846791147293954e-115, 433, 318},\n  {6.392064227463433e-115, 428, 313},\n  {1.5412187845587149e-114, 429, 315},\n  {2.3399657115069453e-114, 430, 316},\n  {6.407770724945315e-114, 429, 315},\n  {1.1069178065391464e-113, 427, 314},\n  {1.573395880167279e-113, 426, 313},\n  {2.951264303968299e-113, 425, 312},\n  {8.761895839903948e-113, 425, 312},\n  {1.4581718353001727e-112, 421, 309},\n  {3.964464541482814e-112, 423, 311},\n  {7.87961036169431e-112, 422, 310},\n  {1.4076373030698913e-111, 421, 310},\n  {3.164529578942993e-111, 418, 307},\n  {4.67946374062146e-111, 419, 308},\n  {1.1022787950727772e-110, 417, 307},\n  {1.670185551585791e-110, 416, 306},\n  {3.270479232134614e-110, 416, 306},\n  {8.778772927538465e-110, 413, 303},\n  {1.4356894911231068e-109, 412, 303},\n  {3.2111903570682556e-109, 407, 298},\n  {4.416972490924674e-109, 412, 303},\n  {1.6902188100294219e-108, 411, 303},\n  {2.7486157518272163e-108, 407, 299},\n  {5.3211348201503956e-108, 408, 300},\n  {8.333478562393578e-108, 408, 300},\n  {1.8401463566583057e-107, 407, 300},\n  {5.376784423862267e-107, 405, 298},\n  {6.333829374806563e-107, 405, 298},\n  {1.7514400642486577e-106, 403, 297},\n  {2.3358765579576565e-106, 403, 297},\n  {4.724398661049588e-106, 402, 296},\n  {1.2692169671992972e-105, 400, 295},\n  {1.963131601289876e-105, 398, 293},\n  {4.342989487256073e-105, 397, 292},\n  {8.812616575391183e-105, 397, 292},\n  {1.692966213667821e-104, 397, 293},\n  {4.242001623031538e-104, 393, 289},\n  {6.030529809392781e-104, 395, 291},\n  {1.5137520077480203e-103, 394, 291},\n  {4.1339493171608476e-103, 393, 290},\n  {6.201035931513464e-103, 392, 289},\n  {1.3967279728811321e-102, 391, 289},\n  {2.6059428457609154e-102, 390, 288},\n  {5.292565718610749e-102, 389, 287},\n  {1.2698160651847153e-101, 388, 287},\n  {2.547070634124935e-101, 384, 283},\n  {4.084116507906642e-101, 384, 283},\n  {6.852426249982067e-101, 380, 279},\n  {1.8083260374699235e-100, 382, 282},\n  {4.1041218769683544e-100, 382, 282},\n  {6.059640250896112e-100, 381, 281},\n  {1.4091162264655094e-99, 381, 282},\n  {2.1541940438019325e-99, 380, 281},\n  {4.956208739898403e-99, 379, 280},\n  {8.634882168235289e-99, 374, 275},\n  {2.426134041499035e-98, 377, 279},\n  {5.688034203287511e-98, 375, 277},\n  {7.42082070481487e-98, 371, 273},\n  {1.4938910681786228e-97, 371, 274},\n  {3.3198659912779945e-97, 370, 273},\n  {5.663431628653758e-97, 370, 273},\n  {1.7437369721819611e-96, 371, 275},\n  {3.535005437982104e-96, 370, 274},\n  {5.461560619344413e-96, 366, 270},\n  {1.1364847491964382e-95, 362, 267},\n  {2.3020007806568932e-95, 366, 271},\n  {3.7529890242406666e-95, 366, 271},\n  {7.38338448129336e-95, 365, 270},\n  {1.3934768122159904e-94, 360, 266},\n  {2.504286566562723e-94, 362, 268},\n  {5.356123085643122e-94, 360, 266},\n  {1.3829457207184735e-93, 357, 264},\n  {2.922539729701329e-93, 358, 265},\n  {4.873642795487652e-93, 359, 266},\n  {1.1847214117665038e-92, 358, 266},\n  {1.6641279667471598e-92, 356, 264},\n  {5.8818504424690985e-92, 356, 264},\n  {8.169815841110726e-92, 353, 261},\n  {1.5452699464889383e-91, 354, 263},\n  {3.659395376290149e-91, 353, 262},\n  {8.251660766595778e-91, 350, 259},\n  {1.4826450102010818e-90, 351, 261},\n  {3.280747503211524e-90, 350, 260},\n  {5.042127927662887e-90, 349, 259},\n  {1.1071352931313988e-89, 348, 259},\n  {1.8434860272299224e-89, 346, 257},\n  {4.503629714546149e-89, 343, 254},\n  {7.978949976696619e-89, 345, 256},\n  {2.5118610569922107e-88, 344, 256},\n  {2.574328439038412e-88, 342, 254},\n  {7.991143060138612e-88, 341, 253},\n  {1.584115281757909e-87, 341, 254},\n  {2.47707398648512e-87, 340, 253},\n  {5.1571453461558415e-87, 339, 252},\n  {9.677861708438445e-87, 338, 251},\n  {3.06005556558652e-86, 337, 251},\n  {4.078986710839316e-86, 336, 250},\n  {8.39906286351288e-86, 332, 246},\n  {1.992717826656166e-85, 332, 247},\n  {4.771839231117578e-85, 332, 247},\n  {9.178454461057711e-85, 332, 247},\n  {1.363704451806317e-84, 330, 246},\n  {3.8533883620323424e-84, 325, 241},\n  {6.032190925744832e-84, 328, 244},\n  {1.5705072660535393e-83, 326, 243},\n  {2.2134072641839148e-83, 326, 243},\n  {4.218896315696479e-83, 326, 243},\n  {1.0992259537194823e-82, 322, 240},\n  {2.4840542381260296e-82, 322, 240},\n  {2.689980129196105e-82, 321, 239},\n  {7.169144283915297e-82, 320, 238},\n  {1.1408995211686882e-81, 318, 237},\n  {3.6501624740693436e-81, 320, 239},\n  {6.558396567940629e-81, 319, 238},\n  {9.973540436014331e-81, 317, 236},\n  {2.1285263571540236e-80, 316, 236},\n  {5.738667020230138e-80, 315, 235},\n  {1.2858099560631301e-79, 315, 236},\n  {1.5440597737151946e-79, 314, 235},\n  {5.085041310658149e-79, 312, 233},\n  {7.85989179025863e-79, 310, 231},\n  {1.5008325529570412e-78, 308, 230},\n  {3.3480454826799275e-78, 306, 228},\n  {8.497568581224052e-78, 309, 231},\n  {1.6394772414307023e-77, 308, 231},\n  {2.901315501375183e-77, 302, 225},\n  {3.82611804506936e-77, 305, 228},\n  {7.235982721954788e-77, 304, 227},\n  {2.6324142915079567e-76, 301, 225},\n  {3.0439003831515616e-76, 302, 226},\n  {8.685053970949932e-76, 299, 223},\n  {1.2427314157729456e-75, 301, 226},\n  {3.398379710807704e-75, 295, 220},\n  {7.841658923587687e-75, 292, 217},\n  {1.3291407760196483e-74, 297, 223},\n  {2.902089657903194e-74, 297, 223},\n  {6.8493063311021444e-74, 295, 221},\n  {1.0308144847135354e-73, 295, 222},\n  {1.827277123300593e-73, 292, 219},\n  {4.9351506696419103e-73, 292, 219},\n  {1.0781250168794166e-72, 292, 220},\n  {1.7181272168700196e-72, 289, 217},\n  {4.2659307529076217e-72, 290, 218},\n  {8.264425689667123e-72, 289, 217},\n  {1.6273802065732598e-71, 288, 217},\n  {1.8350586944381438e-71, 287, 216},\n  {4.348286154284214e-71, 286, 215},\n  {8.6134539761559e-71, 285, 214},\n  {2.5905952437250456e-70, 284, 214},\n  {3.406827648187512e-70, 279, 209},\n  {6.61570075213638e-70, 281, 211},\n  {1.7253632008143088e-69, 281, 212},\n  {3.3892588392445133e-69, 280, 211},\n  {4.939790429145187e-69, 278, 209},\n  {1.47761815700755e-68, 278, 210},\n  {3.538839275393052e-68, 277, 209},\n  {4.821856338577311e-68, 276, 208},\n  {1.0037463923948035e-67, 275, 208},\n  {1.8046301120677578e-67, 272, 205},\n  {5.037756265273766e-67, 273, 206},\n  {7.294226364184892e-67, 271, 204},\n  {1.7260576914043516e-66, 271, 205},\n  {2.413521126424911e-66, 270, 204},\n  {6.085503028299745e-66, 268, 202},\n  {1.357171943220921e-65, 267, 202},\n  {3.0836891319146543e-65, 267, 202},\n  {6.537134301197315e-65, 263, 198},\n  {8.131245964149248e-65, 265, 200},\n  {1.8559814129506474e-64, 261, 197},\n  {3.449567762615896e-64, 263, 199},\n  {7.688715671540768e-64, 262, 198},\n  {2.007143911426273e-63, 259, 196},\n  {3.990126450920154e-63, 258, 195},\n  {6.173618951228879e-63, 258, 195},\n  {1.4822815568311571e-62, 258, 196},\n  {3.8189270099686454e-62, 256, 194},\n  {4.82120756263091e-62, 254, 192},\n  {1.3790405547409109e-61, 255, 194},\n  {2.6993809501677147e-61, 254, 193},\n  {5.23202189544879e-61, 253, 192},\n  {8.267169964142634e-61, 249, 188},\n  {1.9276428005917082e-60, 251, 191},\n  {2.7720738716829783e-60, 246, 186},\n  {6.309714925893337e-60, 248, 188},\n  {1.4603881466067395e-59, 243, 184},\n  {2.390974995457393e-59, 247, 188},\n  {7.148682183253571e-59, 246, 187},\n  {9.322651868323872e-59, 245, 186},\n  {2.202049881264495e-58, 242, 184},\n  {5.387261159597293e-58, 242, 184},\n  {8.629622859499133e-58, 240, 182},\n  {2.1518906041456637e-57, 239, 182},\n  {3.3393236367487655e-57, 238, 181},\n  {8.670831372094515e-57, 238, 181},\n  {1.8197046818766603e-56, 234, 178},\n  {2.2260313359925405e-56, 237, 181},\n  {4.302660672241003e-56, 234, 178},\n  {1.0540262429918154e-55, 233, 178},\n  {3.0681879985386226e-55, 232, 177},\n  {5.481823354294243e-55, 233, 178},\n  {1.0702958332703833e-54, 231, 177},\n  {2.0733146515683786e-54, 225, 171},\n  {3.663911049484028e-54, 230, 176},\n  {7.133428824499037e-54, 227, 173},\n  {1.155049021815273e-53, 226, 173},\n  {2.1292639261534582e-53, 227, 174},\n  {7.116291893522144e-53, 225, 172},\n  {1.563247190926505e-52, 224, 172},\n  {2.8724553500903598e-52, 223, 171},\n  {6.558170706952157e-52, 221, 169},\n  {1.1006685169756963e-51, 220, 169},\n  {1.7539896072768475e-51, 220, 169},\n  {3.7433921715043595e-51, 218, 167},\n  {9.95642899800938e-51, 219, 168},\n  {1.1636497924322522e-50, 218, 168},\n  {3.715838411594935e-50, 217, 167},\n  {6.580569634177383e-50, 212, 162},\n  {1.3361521745438593e-49, 206, 157},\n  {1.758805859472407e-49, 213, 164},\n  {3.5728259387967836e-49, 213, 164},\n  {1.3532863609445875e-48, 212, 164},\n  {2.3787372002095833e-48, 211, 163},\n  {2.962627533001959e-48, 210, 162},\n  {9.002988068912365e-48, 208, 160},\n  {1.6528407152844145e-47, 208, 161},\n  {2.475761918536981e-47, 206, 159},\n  {4.442389874949008e-47, 200, 153},\n  {1.597036901212251e-46, 205, 159},\n  {2.7344299616431544e-46, 203, 157},\n  {4.923161560346421e-46, 203, 157},\n  {1.0179995696026282e-45, 202, 157},\n  {2.0723091166540274e-45, 201, 156},\n  {4.7196310508913905e-45, 199, 154},\n  {8.839991027458473e-45, 198, 153},\n  {1.793400495465429e-44, 197, 153},\n  {4.229568359105647e-44, 197, 153},\n  {6.736360002384042e-44, 196, 152},\n  {1.777871026025537e-43, 194, 151},\n  {3.3315696595658846e-43, 194, 151},\n  {4.002301960990423e-43, 191, 148},\n  {1.1156841616796605e-42, 192, 150},\n  {2.1966221825446394e-42, 191, 149},\n  {5.676537374800469e-42, 188, 146},\n  {1.1265477503439073e-41, 189, 148},\n  {2.1310740108629267e-41, 188, 147},\n  {3.47261690499443e-41, 187, 146},\n  {5.986279457346766e-41, 186, 145},\n  {1.256777780832933e-40, 185, 145},\n  {3.178582703527919e-40, 182, 142},\n  {4.8131885839189464e-40, 182, 142},\n  {1.050509091106282e-39, 180, 141},\n  {2.3469216079446797e-39, 181, 142},\n  {3.701509845820645e-39, 179, 140},\n  {7.539508429995885e-39, 172, 133},\n  {1.6455979868696784e-38, 176, 138},\n  {2.8896506750036806e-38, 176, 138},\n  {7.637447758897944e-38, 176, 138},\n  {1.742931242211031e-37, 171, 134},\n  {3.658831567832271e-37, 171, 134},\n  {6.4153225021810435e-37, 173, 136},\n  {1.2882866599344565e-36, 172, 136},\n  {2.2438898905531304e-36, 171, 135},\n  {5.427246992721223e-36, 170, 134},\n  {1.0773562313702051e-35, 168, 133},\n  {1.8138709823870135e-35, 167, 132},\n  {3.486395423440646e-35, 167, 132},\n  {7.444961768380648e-35, 166, 131},\n  {1.5947728558224363e-34, 164, 130},\n  {3.6958300841764796e-34, 163, 129},\n  {4.902792957147279e-34, 162, 128},\n  {1.3763280779744356e-33, 162, 129},\n  {2.4567652907408955e-33, 161, 128},\n  {5.775941398121507e-33, 160, 127},\n  {6.76218542428814e-33, 158, 125},\n  {1.9564777034032247e-32, 156, 124},\n  {3.7630713133101953e-32, 153, 121},\n  {5.69811750812064e-32, 156, 124},\n  {1.6901389263110543e-31, 154, 123},\n  {2.416607441957109e-31, 154, 123},\n  {5.310587796480732e-31, 152, 121},\n  {1.272832974790223e-30, 150, 120},\n  {2.758607109138819e-30, 151, 121},\n  {6.158392474838414e-30, 147, 117},\n  {9.79111897583252e-30, 149, 119},\n  {2.1005583908999636e-29, 146, 117},\n  {3.268892213509477e-29, 147, 118},\n  {9.861264878636652e-29, 146, 117},\n  {1.9807195772041245e-28, 145, 117},\n  {2.459477005766037e-28, 143, 115},\n  {7.134230712364223e-28, 141, 113},\n  {1.3485130668183938e-27, 142, 115},\n  {1.7558778301243823e-27, 140, 113},\n  {4.603883324842795e-27, 139, 112},\n  {6.787292759429055e-27, 139, 112},\n  {2.338893604866531e-26, 138, 112},\n  {5.0522652313759764e-26, 136, 110},\n  {5.453193492635159e-26, 136, 110},\n  {1.0432046065743156e-25, 134, 109},\n  {3.459820865051479e-25, 134, 109},\n  {4.919945865610923e-25, 133, 108},\n  {1.1905894569192663e-24, 131, 107},\n  {3.1044959876029037e-24, 129, 105},\n  {5.874569663977718e-24, 125, 101},\n  {1.0765469843178544e-23, 128, 105},\n  {1.6374325809042254e-23, 128, 105},\n  {5.135092112079404e-23, 127, 104},\n  {9.161408014517678e-23, 126, 103},\n  {1.1058854487286022e-22, 125, 103},\n  {2.4417307811691994e-22, 124, 102},\n  {6.962426049274539e-22, 121, 99},\n  {1.3365747882614801e-21, 121, 100},\n  {3.1416350487962487e-21, 118, 97},\n  {6.17210332953516e-21, 120, 99},\n  {7.67358493865774e-21, 114, 93},\n  {1.5483757149089377e-20, 118, 98},\n  {4.124682451133355e-20, 117, 97},\n  {9.516929012310325e-20, 115, 95},\n  {1.828195094563647e-19, 115, 96},\n  {4.2349766597973143e-19, 114, 95},\n  {5.027552313830036e-19, 106, 87},\n  {1.4064120230650801e-18, 112, 94},\n  {1.9740440391270057e-18, 111, 93},\n  {5.043775268437966e-18, 110, 92},\n  {1.133834572888795e-17, 109, 92},\n  {2.5020470223212867e-17, 108, 91},\n  {3.702836080714707e-17, 107, 90},\n  {6.060941731133067e-17, 104, 87},\n  {1.4669586812661773e-16, 105, 89},\n  {3.686977051305637e-16, 104, 88},\n  {4.80324090949893e-16, 101, 85},\n  {1.3780266196844444e-15, 97, 82},\n  {2.1377242939848385e-15, 101, 86},\n  {6.212619127432843e-15, 99, 84},\n  {1.1594451093913135e-14, 98, 84},\n  {2.602091364932322e-14, 98, 84},\n  {3.756321509921839e-14, 97, 83},\n  {8.208072800557448e-14, 96, 82},\n  {1.922432441672551e-13, 95, 82},\n  {3.3711192675176746e-13, 92, 79},\n  {7.644553608444343e-13, 91, 78},\n  {1.2428666212744918e-12, 92, 80},\n  {2.218814409588626e-12, 91, 79},\n  {5.927330708414901e-12, 90, 78},\n  {1.1828210767544923e-11, 86, 75},\n  {2.0300112761836498e-11, 88, 77},\n  {2.9825408935925034e-11, 85, 74},\n  {7.41800536873849e-11, 86, 75},\n  {2.3280428024704825e-10, 83, 73},\n  {3.067358161655257e-10, 83, 73},\n  {8.26606194612068e-10, 78, 68},\n  {1.1606565070959597e-9, 82, 73},\n  {2.25770689068302e-9, 79, 70},\n  {4.2174650747812235e-9, 78, 69},\n  {1.4887528604095427e-8, 78, 70},\n  {2.245874199067278e-8, 78, 70},\n  {3.8693071250734114e-8, 77, 69},\n  {9.867450456369697e-8, 76, 68},\n  {1.2770978278302815e-7, 72, 65},\n  {3.9274196153024064e-7, 69, 62},\n  {7.032112886574409e-7, 71, 64},\n  {1.334954758379263e-6, 72, 66},\n  {3.4751715684057034e-6, 71, 65},\n  {5.621298897168876e-6, 62, 56},\n  {9.338413843761515e-6, 69, 63},\n  {2.834895459445889e-5, 68, 63},\n  {4.8806263333083725e-5, 63, 58},\n  {6.981573871413016e-5, 64, 59},\n  {1.5709595512811012e-4, 64, 60},\n  {4.8573351579579444e-4, 60, 56},\n  {9.689777316624939e-4, 63, 59},\n  {1.6816816128152761e-3, 62, 59},\n  {2.6297079760909843e-3, 58, 55},\n  {7.23583892421345e-3, 55, 52},\n  {1.0321932464352318e-2, 59, 57},\n  {2.643880607664138e-2, 58, 56},\n  {4.7182495515037774e-2, 57, 55},\n  {1.190207776709587e-1, 54, 53},\n  {2.0454888297013502e-1, 54, 53},\n  {4.470011069997743e-1, 54, 53},\n  {7.22094745351015e-1, 53, 52},\n  {1.3799513502451215e+0, 51, 51},\n  {3.417399494666547e+0, 51, 51},\n  {5.281345514239382e+0, 49, 49},\n  {1.3580697413368053e+1, 48, 49},\n  {1.854546242122947e+1, 48, 49},\n  {4.1257609000643654e+1, 46, 47},\n  {1.1980413676630091e+2, 46, 48},\n  {2.3573434179257086e+2, 45, 47},\n  {4.514328266785161e+2, 43, 45},\n  {8.043055296192766e+2, 43, 45},\n  {1.24858959528734e+3, 42, 45},\n  {3.5482738915027107e+3, 41, 44},\n  {4.3128420357200675e+3, 40, 43},\n  {1.5941359869713324e+4, 38, 42},\n  {2.497338563971861e+4, 37, 41},\n  {4.518139121214868e+4, 35, 39},\n  {7.552218526149612e+4, 36, 40},\n  {1.711305224325764e+5, 31, 36},\n  {2.882980788973542e+5, 30, 35},\n  {9.050663149996944e+5, 33, 38},\n  {2.0409610459455398e+6, 32, 38},\n  {2.6947477926234123e+6, 31, 37},\n  {6.216317612802105e+6, 29, 35},\n  {1.2602046337445939e+7, 29, 36},\n  {2.2551536557625037e+7, 28, 35},\n  {3.5688575294042416e+7, 27, 34},\n  {7.100154148679338e+7, 26, 33},\n  {2.3023209155038175e+8, 25, 33},\n  {5.343589913228248e+8, 24, 32},\n  {6.370971111337817e+8, 22, 30},\n  {1.1648983464144683e+9, 21, 30},\n  {3.62429343510599e+9, 21, 30},\n  {7.923089394878991e+9, 20, 29},\n  {1.1994134449184855e+10, 19, 29},\n  {2.6327568489536655e+10, 18, 28},\n  {5.744587213490791e+10, 16, 26},\n  {8.696933735612083e+10, 16, 26},\n  {1.6199492099573203e+11, 15, 26},\n  {4.646500235357022e+11, 14, 25},\n  {9.038018345501305e+11, 13, 24},\n  {2.0751822109812212e+12, 11, 23},\n  {2.4783388817039565e+12, 11, 23},\n  {4.627429732435399e+12, 10, 22},\n  {9.300399090639615e+12, 9, 21},\n  {3.2555272280322004e+13, 8, 21},\n  {5.961096741057368e+13, 7, 20},\n  {9.42651077771202e+13, 6, 19},\n  {2.553913921435299e+14, 5, 19},\n  {4.472244273086706e+14, 3, 17},\n  {5.835513905481408e+14, 2, 16},\n  {2.0680691881101218e+15, 2, 17},\n  {3.328555513255053e+15, 0, 15},\n  {6.456802203838757e+15, 0, 15},\n  {1.7045704215755864e+16, 0, 16},\n  {2.84788661041812e+16, 0, 14},\n  {5.025270207504599e+16, 0, 16},\n  {1.0675866324035571e+17, 0, 17},\n  {2.113981699080119e+17, 0, 17},\n  {4.2645533513929344e+17, 0, 16},\n  {6.872142041884154e+17, 0, 16},\n  {1.1799694815132618e+18, 0, 18},\n  {3.082431250837574e+18, 0, 18},\n  {4.656624004411896e+18, 0, 18},\n  {1.2710318822043552e+19, 0, 19},\n  {2.122367359171886e+19, 0, 19},\n  {5.243529562420319e+19, 0, 19},\n  {1.2577828348716568e+20, 0, 20},\n  {2.5306784987647436e+20, 0, 20},\n  {4.170387336967606e+20, 0, 20},\n  {6.974057652717262e+20, 0, 20},\n  {1.9037873644969454e+21, 0, 21},\n  {3.8919116102079354e+21, 0, 21},\n  {7.985405687712874e+21, 0, 21},\n  {1.2304624807151021e+22, 0, 22},\n  {2.877579059642156e+22, 0, 22},\n  {5.820875799468994e+22, 0, 22},\n  {1.3414090792760231e+23, 0, 23},\n  {2.8913245990717846e+23, 0, 23},\n  {5.891246287064668e+23, 0, 23},\n  {1.0714737361583606e+24, 0, 24},\n  {1.9464410087794222e+24, 0, 24},\n  {4.038971174128977e+24, 0, 24},\n  {9.574063461859928e+24, 0, 24},\n  {1.8851337402710216e+25, 0, 25},\n  {3.1247728753733907e+25, 0, 24},\n  {7.618322003647741e+25, 0, 25},\n  {1.2658076075629417e+26, 0, 26},\n  {2.035966265810824e+26, 0, 25},\n  {3.819893374231787e+26, 0, 26},\n  {1.0086763820538085e+27, 0, 27},\n  {1.8722435720189532e+27, 0, 27},\n  {4.199164189291375e+27, 0, 27},\n  {5.655595157253245e+27, 0, 27},\n  {1.300173405645727e+28, 0, 27},\n  {3.435085557417992e+28, 0, 28},\n  {5.665464211510795e+28, 0, 28},\n  {1.0145707014004108e+29, 0, 29},\n  {2.891228424386253e+29, 0, 29},\n  {5.286890236526336e+29, 0, 29},\n  {1.0225186710420342e+30, 0, 30},\n  {2.0108243842273892e+30, 0, 29},\n  {4.3265449624459286e+30, 0, 28},\n  {8.843189919417627e+30, 0, 30},\n  {1.9480726537977613e+31, 0, 31},\n  {3.002386285018348e+31, 0, 30},\n  {5.643671566392372e+31, 0, 31},\n  {1.01957610305534e+32, 0, 32},\n  {1.6310534536755234e+32, 0, 32},\n  {3.545708028358011e+32, 0, 32},\n  {1.2833888390368551e+33, 0, 33},\n  {1.392377581640218e+33, 0, 33},\n  {3.364225166474354e+33, 0, 33},\n  {5.629829199100142e+33, 0, 33},\n  {1.5854951477853974e+34, 0, 33},\n  {2.9422458257062698e+34, 0, 34},\n  {5.271105157627402e+34, 0, 34},\n  {1.0730605396566132e+35, 0, 35},\n  {2.7860337216506062e+35, 0, 35},\n  {4.6519994160495565e+35, 0, 35},\n  {9.613291858434125e+35, 0, 35},\n  {2.4445443869856408e+36, 0, 36},\n  {4.961465895993372e+36, 0, 36},\n  {9.540055986377937e+36, 0, 35},\n  {1.1787368324472862e+37, 0, 37},\n  {2.2217886245582568e+37, 0, 37},\n  {7.482803782461342e+37, 0, 37},\n  {1.221681695558803e+38, 0, 38},\n  {1.9229409068293893e+38, 0, 38},\n  {3.977549408655239e+38, 0, 38},\n  {7.032764985693427e+38, 0, 38},\n  {2.579787897255991e+39, 0, 39},\n  {2.997970965091339e+39, 0, 39},\n  {1.0000548245536084e+40, 0, 40},\n  {1.54316993761888e+40, 0, 40},\n  {4.2448087869444726e+40, 0, 40},\n  {7.226422026835713e+40, 0, 40},\n  {1.5440832081521699e+41, 0, 39},\n  {1.9018305301016465e+41, 0, 41},\n  {3.769840630981528e+41, 0, 41},\n  {7.966617882916526e+41, 0, 40},\n  {1.6479249230626738e+42, 0, 42},\n  {5.4563711872283434e+42, 0, 42},\n  {8.479619741110214e+42, 0, 42},\n  {2.179088893187299e+43, 0, 43},\n  {2.4514764141293366e+43, 0, 43},\n  {7.969690106218293e+43, 0, 41},\n  {1.0174451327093896e+44, 0, 44},\n  {2.3728120049330384e+44, 0, 44},\n  {5.065733073087785e+44, 0, 44},\n  {1.1484248148943394e+45, 0, 45},\n  {1.4749512803946582e+45, 0, 45},\n  {3.184462066193894e+45, 0, 45},\n  {6.322074926642195e+45, 0, 45},\n  {1.7884100129279887e+46, 0, 46},\n  {2.325073960940094e+46, 0, 45},\n  {7.785030375679882e+46, 0, 46},\n  {1.4448801732473917e+47, 0, 47},\n  {2.6558108692875686e+47, 0, 46},\n  {4.372023657618532e+47, 0, 47},\n  {1.1801773326503515e+48, 0, 48},\n  {2.3529994342524252e+48, 0, 48},\n  {3.427891103047345e+48, 0, 48},\n  {9.31630524930085e+48, 0, 46},\n  {1.9116211540697205e+49, 0, 49},\n  {3.069617131966241e+49, 0, 49},\n  {5.496151148596413e+49, 0, 49},\n  {1.7035934506961495e+50, 0, 50},\n  {2.833139550712863e+50, 0, 50},\n  {6.133786732856586e+50, 0, 50},\n  {1.0621093207976587e+51, 0, 51},\n  {2.8455345909273967e+51, 0, 51},\n  {5.385580087774829e+51, 0, 51},\n  {1.0385481005999035e+52, 0, 52},\n  {1.7051361668200266e+52, 0, 50},\n  {4.0348216442512827e+52, 0, 52},\n  {9.203615836914227e+52, 0, 52},\n  {1.4419377590076046e+53, 0, 53},\n  {3.7517809629812945e+53, 0, 53},\n  {6.921465596389916e+53, 0, 53},\n  {1.1246377895743686e+54, 0, 54},\n  {1.9141569906490816e+54, 0, 54},\n  {4.804705186047788e+54, 0, 54},\n  {8.071435564947533e+54, 0, 54},\n  {2.3128140200050004e+55, 0, 55},\n  {4.439935895842503e+55, 0, 55},\n  {6.395351593160262e+55, 0, 55},\n  {1.6961369224103472e+56, 0, 56},\n  {2.0665317001185354e+56, 0, 55},\n  {4.267199288089499e+56, 0, 54},\n  {1.1913532457592459e+57, 0, 57},\n  {2.148060287120407e+57, 0, 57},\n  {4.2134227293218824e+57, 0, 57},\n  {7.35889330966464e+57, 0, 57},\n  {2.4387563351268132e+58, 0, 58},\n  {2.755962153622423e+58, 0, 58},\n  {7.777910645414689e+58, 0, 57},\n  {1.9082027100674306e+59, 0, 59},\n  {2.9725481934856135e+59, 0, 59},\n  {6.272978739898479e+59, 0, 59},\n  {1.6068731094294698e+60, 0, 60},\n  {2.976043223039824e+60, 0, 60},\n  {3.489997472879309e+60, 0, 60},\n  {1.0670380970822516e+61, 0, 61},\n  {2.1280541721170622e+61, 0, 61},\n  {4.118037303766816e+61, 0, 61},\n  {9.480559641663617e+61, 0, 60},\n  {1.596797376905473e+62, 0, 62},\n  {2.860736632381932e+62, 0, 62},\n  {6.175504515676743e+62, 0, 61},\n  {1.508691188244575e+63, 0, 63},\n  {2.660508539901083e+63, 0, 63},\n  {4.809861739392637e+63, 0, 63},\n  {1.1682993418673642e+64, 0, 64},\n  {2.3441185866765202e+64, 0, 64},\n  {3.485421002254914e+64, 0, 64},\n  {7.273116277037653e+64, 0, 64},\n  {1.354521653245005e+65, 0, 64},\n  {3.8672800363603606e+65, 0, 65},\n  {5.066109744077407e+65, 0, 65},\n  {1.4793086587583503e+66, 0, 66},\n  {2.795839034978864e+66, 0, 66},\n  {6.432840401396406e+66, 0, 66},\n  {7.648199062043261e+66, 0, 65},\n  {1.773601417088583e+67, 0, 67},\n  {4.060845372454403e+67, 0, 67},\n  {5.9366267010857e+67, 0, 67},\n  {2.0059924456841817e+68, 0, 68},\n  {4.1783323703661896e+68, 0, 68},\n  {7.479841058470886e+68, 0, 68},\n  {1.1009811776563666e+69, 0, 69},\n  {3.2783442635024616e+69, 0, 69},\n  {6.563409273861358e+69, 0, 69},\n  {1.0567882407580983e+70, 0, 70},\n  {1.5374031618797994e+70, 0, 70},\n  {5.338524581842552e+70, 0, 69},\n  {1.0972432160602172e+71, 0, 71},\n  {1.5001044647337975e+71, 0, 70},\n  {2.8316543156525084e+71, 0, 71},\n  {8.3236641225376745e+71, 0, 71},\n  {1.405812739508858e+72, 0, 72},\n  {3.4886185525806396e+72, 0, 72},\n  {3.6195137423664015e+72, 0, 72},\n  {9.41094888383514e+72, 0, 72},\n  {1.6890382785900104e+73, 0, 73},\n  {4.5033691214168135e+73, 0, 73},\n  {6.278199128750284e+73, 0, 73},\n  {1.1565059569236081e+74, 0, 74},\n  {4.353326831627433e+74, 0, 74},\n  {8.277733334111089e+74, 0, 74},\n  {1.4048499964520923e+75, 0, 75},\n  {2.322541569987462e+75, 0, 75},\n  {4.557727968952481e+75, 0, 75},\n  {1.215633227097655e+76, 0, 74},\n  {1.5116664880436698e+76, 0, 76},\n  {5.765280163648591e+76, 0, 76},\n  {7.214835817706474e+76, 0, 76},\n  {2.1100734325357315e+77, 0, 77},\n  {3.9254415593380374e+77, 0, 77},\n  {6.516897273419297e+77, 0, 76},\n  {1.7597137621226992e+78, 0, 76},\n  {3.434875728114805e+78, 0, 78},\n  {4.496894013184632e+78, 0, 77},\n  {1.0023515028874743e+79, 0, 79},\n  {2.0460627987672047e+79, 0, 77},\n  {3.676686386898502e+79, 0, 79},\n  {6.987059926149719e+79, 0, 79},\n  {1.795081604158087e+80, 0, 80},\n  {3.8104574251556824e+80, 0, 80},\n  {5.627357082833958e+80, 0, 80},\n  {1.8281571967861733e+81, 0, 81},\n  {3.3545136086293428e+81, 0, 81},\n  {4.116922864713555e+81, 0, 81},\n  {1.075543470816983e+82, 0, 82},\n  {1.8609826393606468e+82, 0, 82},\n  {3.903192479447499e+82, 0, 81},\n  {6.473791115981879e+82, 0, 82},\n  {1.538865780906402e+83, 0, 83},\n  {4.6154122259230323e+83, 0, 83},\n  {5.213671588000098e+83, 0, 82},\n  {1.7231285500027554e+84, 0, 84},\n  {3.3263411825363985e+84, 0, 82},\n  {6.7438490624371245e+84, 0, 84},\n  {1.5324966500321058e+85, 0, 85},\n  {2.9031045606668695e+85, 0, 85},\n  {4.0285461203337304e+85, 0, 85},\n  {6.347438261947277e+85, 0, 85},\n  {1.4849990568686154e+86, 0, 86},\n  {3.144137214709148e+86, 0, 84},\n  {7.35111663943684e+86, 0, 86},\n  {1.5577475663863448e+87, 0, 87},\n  {2.2732058810982127e+87, 0, 87},\n  {4.947293841380347e+87, 0, 87},\n  {1.5322047885180557e+88, 0, 87},\n  {2.2054038951762965e+88, 0, 87},\n  {3.4979111598588176e+88, 0, 88},\n  {1.1594354499619093e+89, 0, 89},\n  {1.868122415575413e+89, 0, 89},\n  {3.482810444725715e+89, 0, 89},\n  {7.257639265994533e+89, 0, 89},\n  {1.1535270490147722e+90, 0, 90},\n  {2.4967979092938603e+90, 0, 90},\n  {7.0072237787891005e+90, 0, 90},\n  {1.1095288700912677e+91, 0, 91},\n  {2.9607930405769014e+91, 0, 91},\n  {5.652005070494756e+91, 0, 91},\n  {1.1255100772707936e+92, 0, 92},\n  {1.7318608033666267e+92, 0, 92},\n  {4.919954612222507e+92, 0, 92},\n  {6.919660091186515e+92, 0, 92},\n  {1.0987955187264384e+93, 0, 93},\n  {2.315894257968477e+93, 0, 93},\n  {5.072050876594695e+93, 0, 92},\n  {1.036357099998561e+94, 0, 94},\n  {3.1561899942152433e+94, 0, 94},\n  {5.80588996039864e+94, 0, 94},\n  {8.543176492274635e+94, 0, 94},\n  {1.3894319871042075e+95, 0, 95},\n  {4.778618490169982e+95, 0, 95},\n  {1.0203895888996092e+96, 0, 96},\n  {1.5960456887763758e+96, 0, 96},\n  {3.249173807353552e+96, 0, 96},\n  {4.96163817638071e+96, 0, 96},\n  {1.3169653838480804e+97, 0, 96},\n  {2.7600802833290057e+97, 0, 97},\n  {3.678975869510067e+97, 0, 97},\n  {9.316977605792739e+97, 0, 97},\n  {2.636218658095782e+98, 0, 98},\n  {2.8795801629202426e+98, 0, 98},\n  {1.0271479449330235e+99, 0, 99},\n  {1.7364389499462398e+99, 0, 99},\n  {2.8311014904498133e+99, 0, 99},\n  {8.717780031735018e+99, 0, 98},\n  {1.2937429393963081e+100, 0, 100},\n  {3.076211787425004e+100, 0, 99},\n  {6.075000204770712e+100, 0, 100},\n  {1.0051913192324967e+101, 0, 101},\n  {1.6040435699970458e+101, 0, 101},\n  {3.2033279820176996e+101, 0, 101},\n  {6.504689809313382e+101, 0, 101},\n  {1.3085843965991656e+102, 0, 102},\n  {4.2878646530008384e+102, 0, 102},\n  {6.562953660628769e+102, 0, 101},\n  {1.2236285152807308e+103, 0, 103},\n  {2.414637776562117e+103, 0, 103},\n  {6.180397605502719e+103, 0, 103},\n  {8.952357506110561e+103, 0, 102},\n  {2.6739842993463814e+104, 0, 104},\n  {4.9051642185904874e+104, 0, 104},\n  {1.1379543402482949e+105, 0, 105},\n  {1.41259541205596e+105, 0, 104},\n  {3.1114274318600263e+105, 0, 105},\n  {8.750317145527779e+105, 0, 104},\n  {1.1436652181690534e+106, 0, 106},\n  {3.411483086538129e+106, 0, 106},\n  {4.073577415899245e+106, 0, 106},\n  {1.1569270331282534e+107, 0, 107},\n  {1.8083251599082483e+107, 0, 106},\n  {3.791515819931573e+107, 0, 107},\n  {9.49764100015205e+107, 0, 107},\n  {1.4509958077289753e+108, 0, 108},\n  {4.424174112983774e+108, 0, 108},\n  {7.455899165473143e+108, 0, 108},\n  {1.8319982128819878e+109, 0, 109},\n  {3.0467669457405913e+109, 0, 107},\n  {6.37361253846025e+109, 0, 109},\n  {8.38955229182671e+109, 0, 109},\n  {2.8651611621506035e+110, 0, 110},\n  {6.001453332972219e+110, 0, 110},\n  {8.11448033384131e+110, 0, 109},\n  {2.2581394399876124e+111, 0, 110},\n  {3.136912645074335e+111, 0, 110},\n  {8.75100561003784e+111, 0, 111},\n  {1.7339706643869121e+112, 0, 112},\n  {1.934331254218702e+112, 0, 110},\n  {6.254524509233588e+112, 0, 112},\n  {9.375622388315127e+112, 0, 112},\n  {2.4189606341952683e+113, 0, 112},\n  {5.2244400989180614e+113, 0, 113},\n  {6.794474678720091e+113, 0, 111},\n  {2.046778382742435e+114, 0, 113},\n  {4.195175792183929e+114, 0, 114},\n  {7.880516976947723e+114, 0, 114},\n  {1.1711035514327282e+115, 0, 115},\n  {2.338338695923868e+115, 0, 115},\n  {5.385799521445425e+115, 0, 115},\n  {1.2991906643558943e+116, 0, 116},\n  {1.7757658123869845e+116, 0, 116},\n  {6.1954646341609976e+116, 0, 116},\n  {8.320622375414998e+116, 0, 116},\n  {1.3055545505825232e+117, 0, 117},\n  {3.836650434819978e+117, 0, 116},\n  {8.752670682427704e+117, 0, 117},\n  {1.371103427759537e+118, 0, 118},\n  {3.047042188071979e+118, 0, 117},\n  {7.589006090032981e+118, 0, 117},\n  {1.0473293912342299e+119, 0, 119},\n  {2.0986207045386187e+119, 0, 119},\n  {3.236179661386884e+119, 0, 118},\n  {7.972193856244527e+119, 0, 119},\n  {1.8299315060253445e+120, 0, 120},\n  {4.6151785414218586e+120, 0, 120},\n  {5.923044070696605e+120, 0, 120},\n  {1.361457289463494e+121, 0, 120},\n  {2.7540713949104843e+121, 0, 121},\n  {6.706809109602002e+121, 0, 121},\n  {1.6292678335806155e+122, 0, 121},\n  {2.171602703883232e+122, 0, 121},\n  {5.723756099896326e+122, 0, 122},\n  {6.89498173085618e+122, 0, 121},\n  {2.1357574596497743e+123, 0, 123},\n  {2.961187277322293e+123, 0, 123},\n  {9.093166616854773e+123, 0, 123},\n  {1.2290156989602755e+124, 0, 122},\n  {3.2889389033303796e+124, 0, 124},\n  {7.62000449356734e+124, 0, 123},\n  {1.6491192862717743e+125, 0, 123},\n  {2.1235620018124726e+125, 0, 124},\n  {5.0830874862362334e+125, 0, 125},\n  {1.3230337364814056e+126, 0, 126},\n  {1.8514824347085014e+126, 0, 124},\n  {4.297255645889242e+126, 0, 126},\n  {9.214123983353544e+126, 0, 125},\n  {1.6005984240937532e+127, 0, 126},\n  {4.0927181564650246e+127, 0, 127},\n  {6.9786454159176675e+127, 0, 127},\n  {8.88030935259018e+127, 0, 127},\n  {2.0038549597024574e+128, 0, 128},\n  {6.439948347184869e+128, 0, 128},\n  {9.745603385424579e+128, 0, 128},\n  {1.682287066188509e+129, 0, 129},\n  {5.159777854912e+129, 0, 128},\n  {1.0427009479036798e+130, 0, 130},\n  {1.8648218159777694e+130, 0, 130},\n  {2.7990719302881793e+130, 0, 130},\n  {7.540407358942798e+130, 0, 130},\n  {9.729616533270472e+130, 0, 130},\n  {2.3271929702892176e+131, 0, 130},\n  {4.8467161954318766e+131, 0, 130},\n  {8.654197708736783e+131, 0, 131},\n  {2.486253574248309e+132, 0, 132},\n  {4.520706326672799e+132, 0, 132},\n  {8.587398813646014e+132, 0, 131},\n  {1.7982783689310961e+133, 0, 133},\n  {3.51092292749737e+133, 0, 133},\n  {8.854772868346543e+133, 0, 133},\n  {1.2604934227155403e+134, 0, 132},\n  {2.7086719929942504e+134, 0, 134},\n  {5.9197489271133586e+134, 0, 134},\n  {8.607845504713468e+134, 0, 134},\n  {2.3984166963813072e+135, 0, 134},\n  {3.250007960862918e+135, 0, 135},\n  {7.443610248681703e+135, 0, 135},\n  {1.9390729656615215e+136, 0, 136},\n  {3.6046982835224656e+136, 0, 136},\n  {8.883580710031506e+136, 0, 136},\n  {1.0473820665170049e+137, 0, 136},\n  {2.7139682262053297e+137, 0, 137},\n  {4.3539205936145105e+137, 0, 137},\n  {1.299944829366617e+138, 0, 137},\n  {2.4557929966471053e+138, 0, 138},\n  {3.448376481821885e+138, 0, 136},\n  {6.779013852180732e+138, 0, 138},\n  {1.801148096883475e+139, 0, 139},\n  {3.791452902390207e+139, 0, 138},\n  {6.474870054646572e+139, 0, 139},\n  {1.6231677484748648e+140, 0, 139},\n  {3.527980476981338e+140, 0, 140},\n  {4.747525868091625e+140, 0, 140},\n  {9.241043811243549e+140, 0, 139},\n  {1.7472268158903568e+141, 0, 141},\n  {5.1503502855445634e+141, 0, 141},\n  {1.0693743613454128e+142, 0, 142},\n  {1.727103927590088e+142, 0, 142},\n  {4.6987106320234365e+142, 0, 142},\n  {9.644313300615438e+142, 0, 142},\n  {1.430838540572183e+143, 0, 143},\n  {2.27786457822541e+143, 0, 143},\n  {4.212849184362802e+143, 0, 143},\n  {8.237715226546193e+143, 0, 143},\n  {2.6654737215359226e+144, 0, 143},\n  {6.019079138812162e+144, 0, 144},\n  {1.2409084690292542e+145, 0, 145},\n  {1.549900904806159e+145, 0, 145},\n  {3.1738661663567816e+145, 0, 145},\n  {6.996909564666351e+145, 0, 145},\n  {1.4136130050003495e+146, 0, 146},\n  {2.98468447673297e+146, 0, 146},\n  {5.072484297164673e+146, 0, 146},\n  {9.172861001485728e+146, 0, 146},\n  {3.1827325058993567e+147, 0, 147},\n  {4.205587083947037e+147, 0, 147},\n  {8.648615720577233e+147, 0, 147},\n  {2.4309692254959333e+148, 0, 148},\n  {4.466309998082111e+148, 0, 148},\n  {7.497564003947889e+148, 0, 148},\n  {1.9029191895593583e+149, 0, 149},\n  {3.8006641833677102e+149, 0, 149},\n  {6.913824994082101e+149, 0, 147},\n  {8.712815483801716e+149, 0, 149},\n  {2.3780639727460607e+150, 0, 150},\n  {4.467392649638729e+150, 0, 150},\n  {8.066185078159116e+150, 0, 148},\n  {1.8852448369964825e+151, 0, 150},\n  {3.566152723739795e+151, 0, 151},\n  {5.924851411681645e+151, 0, 151},\n  {1.5060587321219467e+152, 0, 152},\n  {2.8583520249281916e+152, 0, 152},\n  {4.535449894907774e+152, 0, 152},\n  {1.012602649962377e+153, 0, 153},\n  {2.0189450716998492e+153, 0, 153},\n  {6.095760398208723e+153, 0, 152},\n  {1.0839728287028179e+154, 0, 154},\n  {2.0160697875193073e+154, 0, 154},\n  {4.539520336739885e+154, 0, 153},\n  {7.982163447075079e+154, 0, 154},\n  {1.1108659309948223e+155, 0, 155},\n  {2.938991096845654e+155, 0, 155},\n  {7.734832320107849e+155, 0, 155},\n  {1.0600772804334342e+156, 0, 154},\n  {2.560225316421402e+156, 0, 156},\n  {6.4957894162689224e+156, 0, 153},\n  {1.0469685229597166e+157, 0, 157},\n  {1.8049425471684504e+157, 0, 157},\n  {3.7963414617308416e+157, 0, 157},\n  {9.092676802446734e+157, 0, 156},\n  {2.1420954571874796e+158, 0, 158},\n  {3.5406074604903996e+158, 0, 158},\n  {6.059179837113682e+158, 0, 158},\n  {1.7129340266632835e+159, 0, 159},\n  {1.9501271083673803e+159, 0, 159},\n  {5.17312771823294e+159, 0, 158},\n  {9.785122654459946e+159, 0, 159},\n  {2.070565985519521e+160, 0, 160},\n  {5.5612314177003026e+160, 0, 160},\n  {7.487464053324147e+160, 0, 160},\n  {1.72170539588904e+161, 0, 161},\n  {3.101655447037901e+161, 0, 160},\n  {7.190773969829955e+161, 0, 160},\n  {1.6233719112674835e+162, 0, 162},\n  {2.8766398779168217e+162, 0, 161},\n  {5.119867078891217e+162, 0, 162},\n  {1.0829229605647497e+163, 0, 163},\n  {2.588897361986241e+163, 0, 163},\n  {4.8574461844070457e+163, 0, 162},\n  {7.897885248231481e+163, 0, 162},\n  {1.7757792408248608e+164, 0, 164},\n  {4.5181869438995584e+164, 0, 164},\n  {6.04440676037902e+164, 0, 164},\n  {9.436631806093061e+164, 0, 164},\n  {3.252849304270332e+165, 0, 165},\n  {4.971119941681659e+165, 0, 165},\n  {7.72793435885625e+165, 0, 165},\n  {2.49887461307668e+166, 0, 166},\n  {4.138019138142108e+166, 0, 165},\n  {7.671967953519936e+166, 0, 166},\n  {1.4213879533988813e+167, 0, 166},\n  {4.6541532390763674e+167, 0, 166},\n  {5.769302991727879e+167, 0, 167},\n  {1.7540289541978555e+168, 0, 167},\n  {3.454961038953494e+168, 0, 168},\n  {3.923334972089952e+168, 0, 168},\n  {1.270785497029221e+169, 0, 169},\n  {2.376127169235011e+169, 0, 169},\n  {5.282087226424358e+169, 0, 169},\n  {9.538748971891788e+169, 0, 169},\n  {2.1105330303243357e+170, 0, 170},\n  {2.595142529235477e+170, 0, 169},\n  {6.194530882320718e+170, 0, 170},\n  {1.7393845792769813e+171, 0, 171},\n  {2.5114408497872505e+171, 0, 171},\n  {6.964312528803397e+171, 0, 171},\n  {1.229619287434228e+172, 0, 171},\n  {2.2726320991720394e+172, 0, 172},\n  {6.1180092244552894e+172, 0, 172},\n  {7.849033701858734e+172, 0, 172},\n  {1.876016568195346e+173, 0, 173},\n  {3.504523456271905e+173, 0, 173},\n  {7.263516584915379e+173, 0, 173},\n  {1.9020249607206747e+174, 0, 174},\n  {3.954563518862104e+174, 0, 174},\n  {4.179264564957457e+174, 0, 174},\n  {1.3433838372088608e+175, 0, 175},\n  {2.3932438386350752e+175, 0, 175},\n  {4.2972187544143625e+175, 0, 174},\n  {8.694788966088642e+175, 0, 175},\n  {1.5650872395957953e+176, 0, 176},\n  {2.6609127251749555e+176, 0, 176},\n  {7.304131091528228e+176, 0, 176},\n  {1.8749367638301195e+177, 0, 177},\n  {2.525113715353648e+177, 0, 177},\n  {5.287208897505958e+177, 0, 177},\n  {1.2213351108095344e+178, 0, 178},\n  {2.1111997723509844e+178, 0, 178},\n  {5.015156723398377e+178, 0, 178},\n  {7.845248843802528e+178, 0, 178},\n  {1.5083269248992405e+179, 0, 179},\n  {5.03076797664753e+179, 0, 178},\n  {6.133803460001548e+179, 0, 179},\n  {1.7956317799329456e+180, 0, 180},\n  {2.805357599815183e+180, 0, 178},\n  {6.516381475942179e+180, 0, 180},\n  {1.0735479295792078e+181, 0, 181},\n  {1.8311493838142104e+181, 0, 181},\n  {5.233651721774458e+181, 0, 181},\n  {7.965462598423407e+181, 0, 180},\n  {1.5936468405690223e+182, 0, 181},\n  {4.823391413011241e+182, 0, 182},\n  {8.068434006659677e+182, 0, 182},\n  {1.1652548548873181e+183, 0, 182},\n  {3.145002956331295e+183, 0, 183},\n  {6.761293527838664e+183, 0, 182},\n  {1.4932090269565783e+184, 0, 184},\n  {2.8283859855362886e+184, 0, 184},\n  {4.215793138787126e+184, 0, 184},\n  {1.2070899708662365e+185, 0, 185},\n  {2.2700631068240273e+185, 0, 185},\n  {4.0401294303115837e+185, 0, 185},\n  {6.426088010351117e+185, 0, 185},\n  {1.706800695515592e+186, 0, 186},\n  {4.104631682494189e+186, 0, 185},\n  {5.818142413455796e+186, 0, 186},\n  {1.521111645270472e+187, 0, 187},\n  {2.605426852604142e+187, 0, 187},\n  {4.105785371952793e+187, 0, 187},\n  {1.371762624713214e+188, 0, 188},\n  {2.7195986230515428e+188, 0, 188},\n  {4.8243077416927026e+188, 0, 187},\n  {9.498678712086855e+188, 0, 188},\n  {1.5645616537346162e+189, 0, 189},\n  {4.111969199449788e+189, 0, 189},\n  {8.157644542642784e+189, 0, 188},\n  {9.397820074087553e+189, 0, 189},\n  {3.0139458754847525e+190, 0, 190},\n  {6.799079050215055e+190, 0, 189},\n  {1.0012275116878429e+191, 0, 191},\n  {1.450477696477974e+191, 0, 191},\n  {3.5956024160941395e+191, 0, 191},\n  {7.905240079936544e+191, 0, 191},\n  {1.5954884853337538e+192, 0, 192},\n  {3.104998331330946e+192, 0, 192},\n  {8.903071845176859e+192, 0, 192},\n  {1.3587214394332288e+193, 0, 191},\n  {1.86599842632827e+193, 0, 192},\n  {6.954412455796678e+193, 0, 193},\n  {1.0832755189816974e+194, 0, 193},\n  {1.7216892018906268e+194, 0, 193},\n  {2.9919164363027124e+194, 0, 193},\n  {7.751180767767667e+194, 0, 194},\n  {1.4469419386503595e+195, 0, 195},\n  {4.3846223856409726e+195, 0, 195},\n  {5.38120316360689e+195, 0, 195},\n  {1.1192297812198031e+196, 0, 196},\n  {3.6733861060912077e+196, 0, 196},\n  {5.367174654182726e+196, 0, 196},\n  {1.1985977495349634e+197, 0, 197},\n  {2.7706336697369672e+197, 0, 197},\n  {3.81638203242153e+197, 0, 197},\n  {7.793902438347514e+197, 0, 197},\n  {1.762580890481988e+198, 0, 197},\n  {4.590880368222411e+198, 0, 198},\n  {8.941282866369577e+198, 0, 198},\n  {1.8668922163822093e+199, 0, 199},\n  {3.3015541406240128e+199, 0, 199},\n  {6.66780077867008e+199, 0, 199},\n  {8.582818157212463e+199, 0, 199},\n  {1.7299550663482326e+200, 0, 199},\n  {3.174260421239594e+200, 0, 200},\n  {1.0577387790503058e+201, 0, 200},\n  {1.869752072792986e+201, 0, 200},\n  {2.955061165839854e+201, 0, 201},\n  {9.330031602327955e+201, 0, 201},\n  {1.9362841443169072e+202, 0, 200},\n  {3.825314140412424e+202, 0, 202},\n  {5.636134524042645e+202, 0, 202},\n  {1.0455633800468254e+203, 0, 203},\n  {2.394954328277626e+203, 0, 203},\n  {4.637958398638087e+203, 0, 203},\n  {8.55447002868069e+203, 0, 203},\n  {2.2442269843101123e+204, 0, 204},\n  {3.3453762259499943e+204, 0, 204},\n  {6.28195345463042e+204, 0, 204},\n  {1.568756700441443e+205, 0, 205},\n  {2.7948083967253507e+205, 0, 203},\n  {5.0694600816666435e+205, 0, 205},\n  {1.5167731946556582e+206, 0, 206},\n  {1.608485680821464e+206, 0, 206},\n  {3.555480059704267e+206, 0, 206},\n  {7.943813334385863e+206, 0, 206},\n  {1.3580600028505664e+207, 0, 205},\n  {5.1194339355719443e+207, 0, 207},\n  {6.055737748179178e+207, 0, 207},\n  {1.5368136201009418e+208, 0, 207},\n  {2.3929799628231875e+208, 0, 208},\n  {5.905063752643223e+208, 0, 207},\n  {1.2410164169914757e+209, 0, 209},\n  {2.1964388171812186e+209, 0, 209},\n  {5.643355654143248e+209, 0, 209},\n  {1.2832349330194718e+210, 0, 210},\n  {2.4701961710739764e+210, 0, 210},\n  {4.055943573502299e+210, 0, 209},\n  {9.344204033720965e+210, 0, 209},\n  {1.5562829932078268e+211, 0, 211},\n  {4.0777821620060193e+211, 0, 210},\n  {7.651488357127853e+211, 0, 210},\n  {1.4653017873373999e+212, 0, 210},\n  {3.1057366810904798e+212, 0, 212},\n  {3.494919053072309e+212, 0, 211},\n  {1.0912691038049118e+213, 0, 213},\n  {1.9415837118656972e+213, 0, 213},\n  {5.063717719666344e+213, 0, 213},\n  {8.227221790228981e+213, 0, 213},\n  {1.227440354851669e+214, 0, 214},\n  {2.336960786523524e+214, 0, 214},\n  {4.516795524723723e+214, 0, 214},\n  {9.603333317066072e+214, 0, 214},\n  {1.9527229344211296e+215, 0, 213},\n  {5.881603409821294e+215, 0, 215},\n  {1.3688597232290091e+216, 0, 216},\n  {2.295764284659215e+216, 0, 216},\n  {3.873452666452653e+216, 0, 215},\n  {8.054654093095746e+216, 0, 216},\n  {2.1236865355155596e+217, 0, 217},\n  {2.2954536079504795e+217, 0, 217},\n  {5.09388995969254e+217, 0, 217},\n  {8.87462625679e+217, 0, 217},\n  {2.3307227637493686e+218, 0, 218},\n  {3.6312540549690455e+218, 0, 216},\n  {1.0992397195630485e+219, 0, 219},\n  {2.5896743857569935e+219, 0, 219},\n  {5.2383553757590477e+219, 0, 219},\n  {8.90027956004149e+219, 0, 219},\n  {2.2251161587610878e+220, 0, 220},\n  {2.6761461913428335e+220, 0, 219},\n  {5.829314115768354e+220, 0, 220},\n  {1.0417852442068364e+221, 0, 221},\n  {2.1607520560286885e+221, 0, 221},\n  {6.6122291466551174e+221, 0, 221},\n  {1.0456456707732716e+222, 0, 222},\n  {2.1319581917624135e+222, 0, 222},\n  {3.02173423868133e+222, 0, 222},\n  {8.957564546729514e+222, 0, 222},\n  {1.6091267209523208e+223, 0, 223},\n  {3.7212585176976675e+223, 0, 223},\n  {7.024301672009426e+223, 0, 223},\n  {1.3922688598771826e+224, 0, 224},\n  {2.7509119604975913e+224, 0, 224},\n  {6.578680864102577e+224, 0, 224},\n  {8.127797721199312e+224, 0, 223},\n  {2.8202379495294703e+225, 0, 225},\n  {3.4957237382552115e+225, 0, 225},\n  {6.721082912592893e+225, 0, 225},\n  {2.196269058711357e+226, 0, 225},\n  {4.702863683113725e+226, 0, 226},\n  {9.307324517542515e+226, 0, 226},\n  {9.775137400232788e+226, 0, 226},\n  {3.311384964585959e+227, 0, 227},\n  {4.296740089682654e+227, 0, 227},\n  {1.3512844888942569e+228, 0, 228},\n  {2.5091984687837548e+228, 0, 228},\n  {4.20961732885855e+228, 0, 228},\n  {6.135661532855286e+228, 0, 228},\n  {1.835960873424025e+229, 0, 228},\n  {4.582242303370038e+229, 0, 229},\n  {5.018545601421532e+229, 0, 229},\n  {1.5236616837822261e+230, 0, 229},\n  {2.5667407186328553e+230, 0, 230},\n  {6.564151959920961e+230, 0, 230},\n  {9.49846077230578e+230, 0, 230},\n  {2.2123883505536183e+231, 0, 230},\n  {5.172961691829432e+231, 0, 231},\n  {1.1249841891755881e+232, 0, 232},\n  {1.2896696221903866e+232, 0, 232},\n  {4.778497687103402e+232, 0, 232},\n  {9.071740946235158e+232, 0, 232},\n  {1.36899006488196e+233, 0, 233},\n  {3.258574493508666e+233, 0, 233},\n  {7.68435010871856e+233, 0, 233},\n  {1.0486551202848853e+234, 0, 234},\n  {2.5823121007464567e+234, 0, 234},\n  {3.773798625179518e+234, 0, 234},\n  {8.43646796917978e+234, 0, 234},\n  {1.8696054522844014e+235, 0, 235},\n  {2.951623859521488e+235, 0, 235},\n  {5.731246293623385e+235, 0, 235},\n  {1.0607775243868116e+236, 0, 235},\n  {2.2867476796644975e+236, 0, 236},\n  {6.151644498588618e+236, 0, 236},\n  {1.5549079171535267e+237, 0, 237},\n  {2.990844031338328e+237, 0, 237},\n  {4.227499559860197e+237, 0, 237},\n  {6.92155672717332e+237, 0, 237},\n  {1.5461124618479105e+238, 0, 238},\n  {2.8916888598976405e+238, 0, 238},\n  {8.571703697432337e+238, 0, 238},\n  {1.5154374230926383e+239, 0, 239},\n  {3.991365102553957e+239, 0, 239},\n  {8.307426908068283e+239, 0, 239},\n  {1.2399350584739399e+240, 0, 240},\n  {2.2885616582924817e+240, 0, 240},\n  {5.712118804366146e+240, 0, 239},\n  {1.1908490153821645e+241, 0, 241},\n  {1.6045249823083896e+241, 0, 241},\n  {4.212493492663987e+241, 0, 241},\n  {7.52651047700803e+241, 0, 241},\n  {1.9963765370119968e+242, 0, 242},\n  {3.5635374969254025e+242, 0, 242},\n  {5.447736670805847e+242, 0, 242},\n  {1.1156495207540661e+243, 0, 243},\n  {2.3644722323729203e+243, 0, 242},\n  {4.9029985515120055e+243, 0, 241},\n  {7.61131574093882e+243, 0, 243},\n  {1.5041965738192946e+244, 0, 244},\n  {3.35946042057764e+244, 0, 244},\n  {5.625581093732572e+244, 0, 242},\n  {1.730191634549964e+245, 0, 245},\n  {3.761446787869432e+245, 0, 244},\n  {8.60775465603473e+245, 0, 244},\n  {1.1562529259815605e+246, 0, 246},\n  {2.246394864375404e+246, 0, 246},\n  {6.325983032753727e+246, 0, 246},\n  {7.123352302648312e+246, 0, 246},\n  {1.523998978888332e+247, 0, 247},\n  {4.281781595885279e+247, 0, 247},\n  {7.835530851123771e+247, 0, 247},\n  {1.506816682188537e+248, 0, 248},\n  {4.2240545805625663e+248, 0, 248},\n  {6.21305241046745e+248, 0, 248},\n  {1.548221225307829e+249, 0, 249},\n  {1.8642201656825735e+249, 0, 249},\n  {6.106758858401295e+249, 0, 249},\n  {1.2843929885114427e+250, 0, 250},\n  {1.9018081890692326e+250, 0, 250},\n  {4.9450566101263624e+250, 0, 248},\n  {1.0162079024060942e+251, 0, 251},\n  {1.7447831757371136e+251, 0, 250},\n  {4.270366017096213e+251, 0, 250},\n  {6.868063596101005e+251, 0, 251},\n  {1.822171630415365e+252, 0, 251},\n  {1.9952782556234112e+252, 0, 252},\n  {7.158784748789308e+252, 0, 252},\n  {1.2301580219420288e+253, 0, 253},\n  {2.8298450670726808e+253, 0, 253},\n  {3.975680900562017e+253, 0, 252},\n  {8.709330821451115e+253, 0, 253},\n  {2.0174005971626055e+254, 0, 254},\n  {2.394882217848989e+254, 0, 254},\n  {7.067225914874603e+254, 0, 253},\n  {1.8724413449264554e+255, 0, 255},\n  {2.8751146201193473e+255, 0, 255},\n  {6.421888796776797e+255, 0, 255},\n  {7.523354749558753e+255, 0, 254},\n  {2.2569755092325087e+256, 0, 254},\n  {5.3636854859010334e+256, 0, 256},\n  {7.261725338002667e+256, 0, 256},\n  {1.8966691176866934e+257, 0, 257},\n  {4.4518263636702484e+257, 0, 256},\n  {9.255196758728659e+257, 0, 257},\n  {1.8593264122501495e+258, 0, 258},\n  {3.6316167357983088e+258, 0, 258},\n  {6.877505680971994e+258, 0, 258},\n  {8.77084305549325e+258, 0, 258},\n  {1.722353804283124e+259, 0, 259},\n  {3.422411361841639e+259, 0, 259},\n  {7.034346313302069e+259, 0, 259},\n  {2.1022143522712643e+260, 0, 258},\n  {3.9689033579839334e+260, 0, 260},\n  {5.348655415676104e+260, 0, 260},\n  {1.6669857056073904e+261, 0, 261},\n  {2.5268130061515184e+261, 0, 261},\n  {7.013556395550645e+261, 0, 261},\n  {1.115205775476797e+262, 0, 261},\n  {1.8218885128879403e+262, 0, 262},\n  {4.882195462759247e+262, 0, 262},\n  {1.1647232869439797e+263, 0, 263},\n  {2.4733296974221884e+263, 0, 263},\n  {3.825561477223162e+263, 0, 263},\n  {5.295052253043566e+263, 0, 263},\n  {1.7290409059596491e+264, 0, 264},\n  {2.8917911522817363e+264, 0, 264},\n  {5.3515050581631755e+264, 0, 263},\n  {1.0862117360156829e+265, 0, 265},\n  {2.312878548854693e+265, 0, 265},\n  {5.710434640774437e+265, 0, 265},\n  {1.1391963969865948e+266, 0, 266},\n  {1.93471671572856e+266, 0, 265},\n  {4.71279659258191e+266, 0, 266},\n  {9.710448034768053e+266, 0, 266},\n  {1.7049690600947821e+267, 0, 267},\n  {2.1264667446960467e+267, 0, 267},\n  {7.398129672423321e+267, 0, 267},\n  {9.763305417386079e+267, 0, 267},\n  {2.0663273080075778e+268, 0, 268},\n  {5.79917895558858e+268, 0, 268},\n  {7.426275326973986e+268, 0, 268},\n  {2.1266018116022865e+269, 0, 268},\n  {4.31743642311704e+269, 0, 269},\n  {8.537997866147938e+269, 0, 269},\n  {1.498071428134464e+270, 0, 268},\n  {2.2911327982233443e+270, 0, 269},\n  {8.038363677318397e+270, 0, 270},\n  {1.6367042236629719e+271, 0, 271},\n  {1.7563972945923824e+271, 0, 271},\n  {4.380651719659702e+271, 0, 271},\n  {1.0481984706425231e+272, 0, 272},\n  {2.1003524500133663e+272, 0, 272},\n  {5.0546833046720617e+272, 0, 272},\n  {5.467994685893837e+272, 0, 272},\n  {1.2485068025844776e+273, 0, 272},\n  {2.8248640371828673e+273, 0, 272},\n  {8.391364185949281e+273, 0, 273},\n  {1.4139675630970964e+274, 0, 273},\n  {2.907211624912509e+274, 0, 274},\n  {4.372977278689428e+274, 0, 274},\n  {1.2067462091729205e+275, 0, 275},\n  {2.224391242910452e+275, 0, 275},\n  {4.317611102271228e+275, 0, 275},\n  {5.865999613363234e+275, 0, 275},\n  {1.8057130538588834e+276, 0, 276},\n  {2.684819410822478e+276, 0, 276},\n  {6.918394090981146e+276, 0, 276},\n  {9.614934849906353e+276, 0, 276},\n  {3.489094951176538e+277, 0, 277},\n  {6.096297720785519e+277, 0, 276},\n  {7.672262077744885e+277, 0, 277},\n  {1.7454649363112346e+278, 0, 278},\n  {4.3709703490882645e+278, 0, 278},\n  {1.0726114671866103e+279, 0, 279},\n  {1.3228423831361687e+279, 0, 279},\n  {3.696652073770945e+279, 0, 279},\n  {5.506752176655332e+279, 0, 278},\n  {1.4487895211141177e+280, 0, 280},\n  {2.9868446101560017e+280, 0, 279},\n  {5.1539637484359395e+280, 0, 280},\n  {1.3665660611345062e+281, 0, 281},\n  {2.7298170376572543e+281, 0, 280},\n  {4.090888763436566e+281, 0, 280},\n  {7.150463385743894e+281, 0, 281},\n  {1.5175210833272951e+282, 0, 282},\n  {4.186412589165422e+282, 0, 282},\n  {6.374560719497041e+282, 0, 282},\n  {1.5603097675062575e+283, 0, 283},\n  {2.303138994294191e+283, 0, 283},\n  {6.845537430162203e+283, 0, 283},\n  {8.140470197133932e+283, 0, 283},\n  {2.8387070865957636e+284, 0, 284},\n  {4.7222972051563225e+284, 0, 283},\n  {7.735866498835463e+284, 0, 284},\n  {1.456494620618158e+285, 0, 285},\n  {3.765187536411294e+285, 0, 285},\n  {5.407406833340397e+285, 0, 284},\n  {1.6691777225183443e+286, 0, 286},\n  {2.842291093748383e+286, 0, 285},\n  {3.9527246153864666e+286, 0, 286},\n  {9.117687890595329e+286, 0, 286},\n  {2.6403715176860095e+287, 0, 287},\n  {4.672760871330719e+287, 0, 287},\n  {9.585177496788628e+287, 0, 285},\n  {1.823719730845468e+288, 0, 287},\n  {3.3099160671433535e+288, 0, 288},\n  {8.394239974695799e+288, 0, 287},\n  {1.3765053672754183e+289, 0, 289},\n  {3.643679829326757e+289, 0, 289},\n  {6.662084711556889e+289, 0, 289},\n  {1.4882800748283324e+290, 0, 290},\n  {1.8128485634074353e+290, 0, 290},\n  {3.59863757645859e+290, 0, 289},\n  {1.1306303530723758e+291, 0, 291},\n  {1.9883184243838977e+291, 0, 291},\n  {3.152458971727972e+291, 0, 291},\n  {5.55344865848389e+291, 0, 289},\n  {1.9381607498203374e+292, 0, 291},\n  {3.364847896300297e+292, 0, 292},\n  {6.842765724941906e+292, 0, 291},\n  {1.249825938708993e+293, 0, 293},\n  {2.392791615385826e+293, 0, 293},\n  {4.152722646899415e+293, 0, 293},\n  {6.790906283978802e+293, 0, 293},\n  {1.3561785760749324e+294, 0, 294},\n  {3.341059095259842e+294, 0, 293},\n  {9.977930881477503e+294, 0, 294},\n  {1.515106038250912e+295, 0, 294},\n  {3.002184910089352e+295, 0, 295},\n  {5.876579596327993e+295, 0, 295},\n  {1.4003917488295084e+296, 0, 296},\n  {3.1684232621942047e+296, 0, 296},\n  {3.4747005896842154e+296, 0, 296},\n  {7.669723855021269e+296, 0, 296},\n  {1.3614131925096533e+297, 0, 297},\n  {3.191738550947262e+297, 0, 297},\n  {8.888378843987562e+297, 0, 297},\n  {1.5112090835975456e+298, 0, 298},\n  {2.3682327923940456e+298, 0, 298},\n  {5.099780421145732e+298, 0, 298},\n  {1.0673033377644726e+299, 0, 298},\n  {2.378013021177001e+299, 0, 299},\n  {6.554057728282088e+299, 0, 299},\n  {8.318933161474208e+299, 0, 299},\n  {1.3614082701057758e+300, 0, 300},\n  {5.35086581101499e+300, 0, 300},\n  {8.494089296675853e+300, 0, 299},\n  {1.0890134027784214e+301, 0, 301},\n  {2.358927877102592e+301, 0, 301},\n  {8.117690363105265e+301, 0, 301},\n  {9.341511426794588e+301, 0, 301},\n  {2.64210043201936e+302, 0, 302},\n  {6.847933389378864e+302, 0, 302},\n  {1.1002556200184007e+303, 0, 303},\n  {2.5611530892094995e+303, 0, 303},\n  {5.076344585537476e+303, 0, 303},\n  {8.304487832500949e+303, 0, 303},\n  {1.1230804446665463e+304, 0, 304},\n  {4.2777559220816043e+304, 0, 304},\n  {6.283260967439312e+304, 0, 304},\n  {1.3747215164191865e+305, 0, 305},\n  {3.3096929273918684e+305, 0, 305},\n  {6.171133478197849e+305, 0, 305},\n  {1.2292521010494213e+306, 0, 306},\n  {2.2862202954633178e+306, 0, 305},\n  {3.5065941308845355e+306, 0, 306},\n  {1.0940422247589228e+307, 0, 307},\n  {2.0068458193014486e+307, 0, 306},\n  {2.7914963723745636e+307, 0, 307},\n  {6.123699292104195e+307, 0, 307},\n  {1.7026387749989901e+308, 0, 308},\n] of _\n\n# For brevity, we avoid testing all possible combinations of P and X. Instead, we test:\n# * All values of P where some X can be affected by rounding. (For double, this is [1, 15].)\n# * P == 25, which is arbitrary.\n# * P == numeric_limits::max_exponent10 + 1. This selects fixed notation for numeric_limits::max(),\n#   so it's the largest interesting value of P.\n# * Finally, we test the transitions around X == P - 1, ensuring that we can recognize every value of X.\nGEN_TRANSITIONS = [\n  {\"0x1.8e757928e0c9dp-14\", 1, \"9e-5\"},\n  {\"0x1.8e757928e0c9ep-14\", 1, \"0.0001\"},\n  {\"0x1.f212d77318fc5p-11\", 1, \"0.0009\"},\n  {\"0x1.f212d77318fc6p-11\", 1, \"0.001\"},\n  {\"0x1.374bc6a7ef9dbp-7\", 1, \"0.009\"},\n  {\"0x1.374bc6a7ef9dcp-7\", 1, \"0.01\"},\n  {\"0x1.851eb851eb851p-4\", 1, \"0.09\"},\n  {\"0x1.851eb851eb852p-4\", 1, \"0.1\"},\n  {\"0x1.e666666666666p-1\", 1, \"0.9\"},\n  {\"0x1.e666666666667p-1\", 1, \"1\"},\n  {\"0x1.2ffffffffffffp+3\", 1, \"9\"},\n  {\"0x1.3000000000000p+3\", 1, \"1e+1\"},\n  {\"0x1.a1554fbdad751p-14\", 2, \"9.9e-5\"},\n  {\"0x1.a1554fbdad752p-14\", 2, \"0.0001\"},\n  {\"0x1.04d551d68c692p-10\", 2, \"0.00099\"},\n  {\"0x1.04d551d68c693p-10\", 2, \"0.001\"},\n  {\"0x1.460aa64c2f837p-7\", 2, \"0.0099\"},\n  {\"0x1.460aa64c2f838p-7\", 2, \"0.01\"},\n  {\"0x1.978d4fdf3b645p-4\", 2, \"0.099\"},\n  {\"0x1.978d4fdf3b646p-4\", 2, \"0.1\"},\n  {\"0x1.fd70a3d70a3d7p-1\", 2, \"0.99\"},\n  {\"0x1.fd70a3d70a3d8p-1\", 2, \"1\"},\n  {\"0x1.3e66666666666p+3\", 2, \"9.9\"},\n  {\"0x1.3e66666666667p+3\", 2, \"10\"},\n  {\"0x1.8dfffffffffffp+6\", 2, \"99\"},\n  {\"0x1.8e00000000000p+6\", 2, \"1e+2\"},\n  {\"0x1.a3387ecc8eb96p-14\", 3, \"9.99e-5\"},\n  {\"0x1.a3387ecc8eb97p-14\", 3, \"0.0001\"},\n  {\"0x1.06034f3fd933ep-10\", 3, \"0.000999\"},\n  {\"0x1.06034f3fd933fp-10\", 3, \"0.001\"},\n  {\"0x1.4784230fcf80dp-7\", 3, \"0.00999\"},\n  {\"0x1.4784230fcf80ep-7\", 3, \"0.01\"},\n  {\"0x1.99652bd3c3611p-4\", 3, \"0.0999\"},\n  {\"0x1.99652bd3c3612p-4\", 3, \"0.1\"},\n  {\"0x1.ffbe76c8b4395p-1\", 3, \"0.999\"},\n  {\"0x1.ffbe76c8b4396p-1\", 3, \"1\"},\n  {\"0x1.3fd70a3d70a3dp+3\", 3, \"9.99\"},\n  {\"0x1.3fd70a3d70a3ep+3\", 3, \"10\"},\n  {\"0x1.8fcccccccccccp+6\", 3, \"99.9\"},\n  {\"0x1.8fccccccccccdp+6\", 3, \"100\"},\n  {\"0x1.f3bffffffffffp+9\", 3, \"999\"},\n  {\"0x1.f3c0000000000p+9\", 3, \"1e+3\"},\n  {\"0x1.a368d04e0ba6ap-14\", 4, \"9.999e-5\"},\n  {\"0x1.a368d04e0ba6bp-14\", 4, \"0.0001\"},\n  {\"0x1.06218230c7482p-10\", 4, \"0.0009999\"},\n  {\"0x1.06218230c7483p-10\", 4, \"0.001\"},\n  {\"0x1.47a9e2bcf91a3p-7\", 4, \"0.009999\"},\n  {\"0x1.47a9e2bcf91a4p-7\", 4, \"0.01\"},\n  {\"0x1.99945b6c3760bp-4\", 4, \"0.09999\"},\n  {\"0x1.99945b6c3760cp-4\", 4, \"0.1\"},\n  {\"0x1.fff972474538ep-1\", 4, \"0.9999\"},\n  {\"0x1.fff972474538fp-1\", 4, \"1\"},\n  {\"0x1.3ffbe76c8b439p+3\", 4, \"9.999\"},\n  {\"0x1.3ffbe76c8b43ap+3\", 4, \"10\"},\n  {\"0x1.8ffae147ae147p+6\", 4, \"99.99\"},\n  {\"0x1.8ffae147ae148p+6\", 4, \"100\"},\n  {\"0x1.f3f9999999999p+9\", 4, \"999.9\"},\n  {\"0x1.f3f999999999ap+9\", 4, \"1000\"},\n  {\"0x1.387bfffffffffp+13\", 4, \"9999\"},\n  {\"0x1.387c000000000p+13\", 4, \"1e+4\"},\n  {\"0x1.a36da54164f19p-14\", 5, \"9.9999e-5\"},\n  {\"0x1.a36da54164f1ap-14\", 5, \"0.0001\"},\n  {\"0x1.06248748df16fp-10\", 5, \"0.00099999\"},\n  {\"0x1.06248748df170p-10\", 5, \"0.001\"},\n  {\"0x1.47ada91b16dcbp-7\", 5, \"0.0099999\"},\n  {\"0x1.47ada91b16dccp-7\", 5, \"0.01\"},\n  {\"0x1.99991361dc93ep-4\", 5, \"0.099999\"},\n  {\"0x1.99991361dc93fp-4\", 5, \"0.1\"},\n  {\"0x1.ffff583a53b8ep-1\", 5, \"0.99999\"},\n  {\"0x1.ffff583a53b8fp-1\", 5, \"1\"},\n  {\"0x1.3fff972474538p+3\", 5, \"9.9999\"},\n  {\"0x1.3fff972474539p+3\", 5, \"10\"},\n  {\"0x1.8fff7ced91687p+6\", 5, \"99.999\"},\n  {\"0x1.8fff7ced91688p+6\", 5, \"100\"},\n  {\"0x1.f3ff5c28f5c28p+9\", 5, \"999.99\"},\n  {\"0x1.f3ff5c28f5c29p+9\", 5, \"1000\"},\n  {\"0x1.387f999999999p+13\", 5, \"9999.9\"},\n  {\"0x1.387f99999999ap+13\", 5, \"10000\"},\n  {\"0x1.869f7ffffffffp+16\", 5, \"99999\"},\n  {\"0x1.869f800000000p+16\", 5, \"1e+5\"},\n  {\"0x1.a36e20f35445dp-14\", 6, \"9.99999e-5\"},\n  {\"0x1.a36e20f35445ep-14\", 6, \"0.0001\"},\n  {\"0x1.0624d49814abap-10\", 6, \"0.000999999\"},\n  {\"0x1.0624d49814abbp-10\", 6, \"0.001\"},\n  {\"0x1.47ae09be19d69p-7\", 6, \"0.00999999\"},\n  {\"0x1.47ae09be19d6ap-7\", 6, \"0.01\"},\n  {\"0x1.99998c2da04c3p-4\", 6, \"0.0999999\"},\n  {\"0x1.99998c2da04c4p-4\", 6, \"0.1\"},\n  {\"0x1.ffffef39085f4p-1\", 6, \"0.999999\"},\n  {\"0x1.ffffef39085f5p-1\", 6, \"1\"},\n  {\"0x1.3ffff583a53b8p+3\", 6, \"9.99999\"},\n  {\"0x1.3ffff583a53b9p+3\", 6, \"10\"},\n  {\"0x1.8ffff2e48e8a7p+6\", 6, \"99.9999\"},\n  {\"0x1.8ffff2e48e8a8p+6\", 6, \"100\"},\n  {\"0x1.f3ffef9db22d0p+9\", 6, \"999.999\"},\n  {\"0x1.f3ffef9db22d1p+9\", 6, \"1000\"},\n  {\"0x1.387ff5c28f5c2p+13\", 6, \"9999.99\"},\n  {\"0x1.387ff5c28f5c3p+13\", 6, \"10000\"},\n  {\"0x1.869ff33333333p+16\", 6, \"99999.9\"},\n  {\"0x1.869ff33333334p+16\", 6, \"100000\"},\n  {\"0x1.e847effffffffp+19\", 6, \"999999\"},\n  {\"0x1.e847f00000000p+19\", 6, \"1e+6\"},\n  {\"0x1.a36e2d51ec34bp-14\", 7, \"9.999999e-5\"},\n  {\"0x1.a36e2d51ec34cp-14\", 7, \"0.0001\"},\n  {\"0x1.0624dc5333a0ep-10\", 7, \"0.0009999999\"},\n  {\"0x1.0624dc5333a0fp-10\", 7, \"0.001\"},\n  {\"0x1.47ae136800892p-7\", 7, \"0.009999999\"},\n  {\"0x1.47ae136800893p-7\", 7, \"0.01\"},\n  {\"0x1.9999984200ab7p-4\", 7, \"0.09999999\"},\n  {\"0x1.9999984200ab8p-4\", 7, \"0.1\"},\n  {\"0x1.fffffe5280d65p-1\", 7, \"0.9999999\"},\n  {\"0x1.fffffe5280d66p-1\", 7, \"1\"},\n  {\"0x1.3ffffef39085fp+3\", 7, \"9.999999\"},\n  {\"0x1.3ffffef390860p+3\", 7, \"10\"},\n  {\"0x1.8ffffeb074a77p+6\", 7, \"99.99999\"},\n  {\"0x1.8ffffeb074a78p+6\", 7, \"100\"},\n  {\"0x1.f3fffe5c91d14p+9\", 7, \"999.9999\"},\n  {\"0x1.f3fffe5c91d15p+9\", 7, \"1000\"},\n  {\"0x1.387ffef9db22dp+13\", 7, \"9999.999\"},\n  {\"0x1.387ffef9db22ep+13\", 7, \"10000\"},\n  {\"0x1.869ffeb851eb8p+16\", 7, \"99999.99\"},\n  {\"0x1.869ffeb851eb9p+16\", 7, \"100000\"},\n  {\"0x1.e847fe6666666p+19\", 7, \"999999.9\"},\n  {\"0x1.e847fe6666667p+19\", 7, \"1000000\"},\n  {\"0x1.312cfefffffffp+23\", 7, \"9999999\"},\n  {\"0x1.312cff0000000p+23\", 7, \"1e+7\"},\n  {\"0x1.a36e2e8e94ffcp-14\", 8, \"9.9999999e-5\"},\n  {\"0x1.a36e2e8e94ffdp-14\", 8, \"0.0001\"},\n  {\"0x1.0624dd191d1fdp-10\", 8, \"0.00099999999\"},\n  {\"0x1.0624dd191d1fep-10\", 8, \"0.001\"},\n  {\"0x1.47ae145f6467dp-7\", 8, \"0.0099999999\"},\n  {\"0x1.47ae145f6467ep-7\", 8, \"0.01\"},\n  {\"0x1.999999773d81cp-4\", 8, \"0.099999999\"},\n  {\"0x1.999999773d81dp-4\", 8, \"0.1\"},\n  {\"0x1.ffffffd50ce23p-1\", 8, \"0.99999999\"},\n  {\"0x1.ffffffd50ce24p-1\", 8, \"1\"},\n  {\"0x1.3fffffe5280d6p+3\", 8, \"9.9999999\"},\n  {\"0x1.3fffffe5280d7p+3\", 8, \"10\"},\n  {\"0x1.8fffffde7210bp+6\", 8, \"99.999999\"},\n  {\"0x1.8fffffde7210cp+6\", 8, \"100\"},\n  {\"0x1.f3ffffd60e94ep+9\", 8, \"999.99999\"},\n  {\"0x1.f3ffffd60e94fp+9\", 8, \"1000\"},\n  {\"0x1.387fffe5c91d1p+13\", 8, \"9999.9999\"},\n  {\"0x1.387fffe5c91d2p+13\", 8, \"10000\"},\n  {\"0x1.869fffdf3b645p+16\", 8, \"99999.999\"},\n  {\"0x1.869fffdf3b646p+16\", 8, \"100000\"},\n  {\"0x1.e847ffd70a3d7p+19\", 8, \"999999.99\"},\n  {\"0x1.e847ffd70a3d8p+19\", 8, \"1000000\"},\n  {\"0x1.312cffe666666p+23\", 8, \"9999999.9\"},\n  {\"0x1.312cffe666667p+23\", 8, \"10000000\"},\n  {\"0x1.7d783fdffffffp+26\", 8, \"99999999\"},\n  {\"0x1.7d783fe000000p+26\", 8, \"1e+8\"},\n  {\"0x1.a36e2eae3f7a7p-14\", 9, \"9.99999999e-5\"},\n  {\"0x1.a36e2eae3f7a8p-14\", 9, \"0.0001\"},\n  {\"0x1.0624dd2ce7ac8p-10\", 9, \"0.000999999999\"},\n  {\"0x1.0624dd2ce7ac9p-10\", 9, \"0.001\"},\n  {\"0x1.47ae14782197bp-7\", 9, \"0.00999999999\"},\n  {\"0x1.47ae14782197cp-7\", 9, \"0.01\"},\n  {\"0x1.9999999629fd9p-4\", 9, \"0.0999999999\"},\n  {\"0x1.9999999629fdap-4\", 9, \"0.1\"},\n  {\"0x1.fffffffbb47d0p-1\", 9, \"0.999999999\"},\n  {\"0x1.fffffffbb47d1p-1\", 9, \"1\"},\n  {\"0x1.3ffffffd50ce2p+3\", 9, \"9.99999999\"},\n  {\"0x1.3ffffffd50ce3p+3\", 9, \"10\"},\n  {\"0x1.8ffffffca501ap+6\", 9, \"99.9999999\"},\n  {\"0x1.8ffffffca501bp+6\", 9, \"100\"},\n  {\"0x1.f3fffffbce421p+9\", 9, \"999.999999\"},\n  {\"0x1.f3fffffbce422p+9\", 9, \"1000\"},\n  {\"0x1.387ffffd60e94p+13\", 9, \"9999.99999\"},\n  {\"0x1.387ffffd60e95p+13\", 9, \"10000\"},\n  {\"0x1.869ffffcb923ap+16\", 9, \"99999.9999\"},\n  {\"0x1.869ffffcb923bp+16\", 9, \"100000\"},\n  {\"0x1.e847fffbe76c8p+19\", 9, \"999999.999\"},\n  {\"0x1.e847fffbe76c9p+19\", 9, \"1000000\"},\n  {\"0x1.312cfffd70a3dp+23\", 9, \"9999999.99\"},\n  {\"0x1.312cfffd70a3ep+23\", 9, \"10000000\"},\n  {\"0x1.7d783ffccccccp+26\", 9, \"99999999.9\"},\n  {\"0x1.7d783ffcccccdp+26\", 9, \"100000000\"},\n  {\"0x1.dcd64ffbfffffp+29\", 9, \"999999999\"},\n  {\"0x1.dcd64ffc00000p+29\", 9, \"1e+9\"},\n  {\"0x1.a36e2eb16a205p-14\", 10, \"9.999999999e-5\"},\n  {\"0x1.a36e2eb16a206p-14\", 10, \"0.0001\"},\n  {\"0x1.0624dd2ee2543p-10\", 10, \"0.0009999999999\"},\n  {\"0x1.0624dd2ee2544p-10\", 10, \"0.001\"},\n  {\"0x1.47ae147a9ae94p-7\", 10, \"0.009999999999\"},\n  {\"0x1.47ae147a9ae95p-7\", 10, \"0.01\"},\n  {\"0x1.9999999941a39p-4\", 10, \"0.09999999999\"},\n  {\"0x1.9999999941a3ap-4\", 10, \"0.1\"},\n  {\"0x1.ffffffff920c8p-1\", 10, \"0.9999999999\"},\n  {\"0x1.ffffffff920c9p-1\", 10, \"1\"},\n  {\"0x1.3fffffffbb47dp+3\", 10, \"9.999999999\"},\n  {\"0x1.3fffffffbb47ep+3\", 10, \"10\"},\n  {\"0x1.8fffffffaa19cp+6\", 10, \"99.99999999\"},\n  {\"0x1.8fffffffaa19dp+6\", 10, \"100\"},\n  {\"0x1.f3ffffff94a03p+9\", 10, \"999.9999999\"},\n  {\"0x1.f3ffffff94a04p+9\", 10, \"1000\"},\n  {\"0x1.387fffffbce42p+13\", 10, \"9999.999999\"},\n  {\"0x1.387fffffbce43p+13\", 10, \"10000\"},\n  {\"0x1.869fffffac1d2p+16\", 10, \"99999.99999\"},\n  {\"0x1.869fffffac1d3p+16\", 10, \"100000\"},\n  {\"0x1.e847ffff97247p+19\", 10, \"999999.9999\"},\n  {\"0x1.e847ffff97248p+19\", 10, \"1000000\"},\n  {\"0x1.312cffffbe76cp+23\", 10, \"9999999.999\"},\n  {\"0x1.312cffffbe76dp+23\", 10, \"10000000\"},\n  {\"0x1.7d783fffae147p+26\", 10, \"99999999.99\"},\n  {\"0x1.7d783fffae148p+26\", 10, \"100000000\"},\n  {\"0x1.dcd64fff99999p+29\", 10, \"999999999.9\"},\n  {\"0x1.dcd64fff9999ap+29\", 10, \"1000000000\"},\n  {\"0x1.2a05f1ffbffffp+33\", 10, \"9999999999\"},\n  {\"0x1.2a05f1ffc0000p+33\", 10, \"1e+10\"},\n  {\"0x1.a36e2eb1bb30fp-14\", 11, \"9.9999999999e-5\"},\n  {\"0x1.a36e2eb1bb310p-14\", 11, \"0.0001\"},\n  {\"0x1.0624dd2f14fe9p-10\", 11, \"0.00099999999999\"},\n  {\"0x1.0624dd2f14feap-10\", 11, \"0.001\"},\n  {\"0x1.47ae147ada3e3p-7\", 11, \"0.0099999999999\"},\n  {\"0x1.47ae147ada3e4p-7\", 11, \"0.01\"},\n  {\"0x1.9999999990cdcp-4\", 11, \"0.099999999999\"},\n  {\"0x1.9999999990cddp-4\", 11, \"0.1\"},\n  {\"0x1.fffffffff5014p-1\", 11, \"0.99999999999\"},\n  {\"0x1.fffffffff5015p-1\", 11, \"1\"},\n  {\"0x1.3ffffffff920cp+3\", 11, \"9.9999999999\"},\n  {\"0x1.3ffffffff920dp+3\", 11, \"10\"},\n  {\"0x1.8ffffffff768fp+6\", 11, \"99.999999999\"},\n  {\"0x1.8ffffffff7690p+6\", 11, \"100\"},\n  {\"0x1.f3fffffff5433p+9\", 11, \"999.99999999\"},\n  {\"0x1.f3fffffff5434p+9\", 11, \"1000\"},\n  {\"0x1.387ffffff94a0p+13\", 11, \"9999.9999999\"},\n  {\"0x1.387ffffff94a1p+13\", 11, \"10000\"},\n  {\"0x1.869ffffff79c8p+16\", 11, \"99999.999999\"},\n  {\"0x1.869ffffff79c9p+16\", 11, \"100000\"},\n  {\"0x1.e847fffff583ap+19\", 11, \"999999.99999\"},\n  {\"0x1.e847fffff583bp+19\", 11, \"1000000\"},\n  {\"0x1.312cfffff9724p+23\", 11, \"9999999.9999\"},\n  {\"0x1.312cfffff9725p+23\", 11, \"10000000\"},\n  {\"0x1.7d783ffff7cedp+26\", 11, \"99999999.999\"},\n  {\"0x1.7d783ffff7ceep+26\", 11, \"100000000\"},\n  {\"0x1.dcd64ffff5c28p+29\", 11, \"999999999.99\"},\n  {\"0x1.dcd64ffff5c29p+29\", 11, \"1000000000\"},\n  {\"0x1.2a05f1fff9999p+33\", 11, \"9999999999.9\"},\n  {\"0x1.2a05f1fff999ap+33\", 11, \"10000000000\"},\n  {\"0x1.74876e7ff7fffp+36\", 11, \"99999999999\"},\n  {\"0x1.74876e7ff8000p+36\", 11, \"1e+11\"},\n  {\"0x1.a36e2eb1c34c3p-14\", 12, \"9.99999999999e-5\"},\n  {\"0x1.a36e2eb1c34c4p-14\", 12, \"0.0001\"},\n  {\"0x1.0624dd2f1a0fap-10\", 12, \"0.000999999999999\"},\n  {\"0x1.0624dd2f1a0fbp-10\", 12, \"0.001\"},\n  {\"0x1.47ae147ae0938p-7\", 12, \"0.00999999999999\"},\n  {\"0x1.47ae147ae0939p-7\", 12, \"0.01\"},\n  {\"0x1.9999999998b86p-4\", 12, \"0.0999999999999\"},\n  {\"0x1.9999999998b87p-4\", 12, \"0.1\"},\n  {\"0x1.fffffffffee68p-1\", 12, \"0.999999999999\"},\n  {\"0x1.fffffffffee69p-1\", 12, \"1\"},\n  {\"0x1.3fffffffff501p+3\", 12, \"9.99999999999\"},\n  {\"0x1.3fffffffff502p+3\", 12, \"10\"},\n  {\"0x1.8fffffffff241p+6\", 12, \"99.9999999999\"},\n  {\"0x1.8fffffffff242p+6\", 12, \"100\"},\n  {\"0x1.f3fffffffeed1p+9\", 12, \"999.999999999\"},\n  {\"0x1.f3fffffffeed2p+9\", 12, \"1000\"},\n  {\"0x1.387fffffff543p+13\", 12, \"9999.99999999\"},\n  {\"0x1.387fffffff544p+13\", 12, \"10000\"},\n  {\"0x1.869fffffff294p+16\", 12, \"99999.9999999\"},\n  {\"0x1.869fffffff295p+16\", 12, \"100000\"},\n  {\"0x1.e847fffffef39p+19\", 12, \"999999.999999\"},\n  {\"0x1.e847fffffef3ap+19\", 12, \"1000000\"},\n  {\"0x1.312cffffff583p+23\", 12, \"9999999.99999\"},\n  {\"0x1.312cffffff584p+23\", 12, \"10000000\"},\n  {\"0x1.7d783fffff2e4p+26\", 12, \"99999999.9999\"},\n  {\"0x1.7d783fffff2e5p+26\", 12, \"100000000\"},\n  {\"0x1.dcd64ffffef9dp+29\", 12, \"999999999.999\"},\n  {\"0x1.dcd64ffffef9ep+29\", 12, \"1000000000\"},\n  {\"0x1.2a05f1ffff5c2p+33\", 12, \"9999999999.99\"},\n  {\"0x1.2a05f1ffff5c3p+33\", 12, \"10000000000\"},\n  {\"0x1.74876e7fff333p+36\", 12, \"99999999999.9\"},\n  {\"0x1.74876e7fff334p+36\", 12, \"100000000000\"},\n  {\"0x1.d1a94a1ffefffp+39\", 12, \"999999999999\"},\n  {\"0x1.d1a94a1fff000p+39\", 12, \"1e+12\"},\n  {\"0x1.a36e2eb1c41bbp-14\", 13, \"9.999999999999e-5\"},\n  {\"0x1.a36e2eb1c41bcp-14\", 13, \"0.0001\"},\n  {\"0x1.0624dd2f1a915p-10\", 13, \"0.0009999999999999\"},\n  {\"0x1.0624dd2f1a916p-10\", 13, \"0.001\"},\n  {\"0x1.47ae147ae135ap-7\", 13, \"0.009999999999999\"},\n  {\"0x1.47ae147ae135bp-7\", 13, \"0.01\"},\n  {\"0x1.9999999999831p-4\", 13, \"0.09999999999999\"},\n  {\"0x1.9999999999832p-4\", 13, \"0.1\"},\n  {\"0x1.ffffffffffe3dp-1\", 13, \"0.9999999999999\"},\n  {\"0x1.ffffffffffe3ep-1\", 13, \"1\"},\n  {\"0x1.3fffffffffee6p+3\", 13, \"9.999999999999\"},\n  {\"0x1.3fffffffffee7p+3\", 13, \"10\"},\n  {\"0x1.8fffffffffea0p+6\", 13, \"99.99999999999\"},\n  {\"0x1.8fffffffffea1p+6\", 13, \"100\"},\n  {\"0x1.f3ffffffffe48p+9\", 13, \"999.9999999999\"},\n  {\"0x1.f3ffffffffe49p+9\", 13, \"1000\"},\n  {\"0x1.387fffffffeedp+13\", 13, \"9999.999999999\"},\n  {\"0x1.387fffffffeeep+13\", 13, \"10000\"},\n  {\"0x1.869fffffffea8p+16\", 13, \"99999.99999999\"},\n  {\"0x1.869fffffffea9p+16\", 13, \"100000\"},\n  {\"0x1.e847ffffffe52p+19\", 13, \"999999.9999999\"},\n  {\"0x1.e847ffffffe53p+19\", 13, \"1000000\"},\n  {\"0x1.312cffffffef3p+23\", 13, \"9999999.999999\"},\n  {\"0x1.312cffffffef4p+23\", 13, \"10000000\"},\n  {\"0x1.7d783fffffeb0p+26\", 13, \"99999999.99999\"},\n  {\"0x1.7d783fffffeb1p+26\", 13, \"100000000\"},\n  {\"0x1.dcd64fffffe5cp+29\", 13, \"999999999.9999\"},\n  {\"0x1.dcd64fffffe5dp+29\", 13, \"1000000000\"},\n  {\"0x1.2a05f1ffffef9p+33\", 13, \"9999999999.999\"},\n  {\"0x1.2a05f1ffffefap+33\", 13, \"10000000000\"},\n  {\"0x1.74876e7fffeb8p+36\", 13, \"99999999999.99\"},\n  {\"0x1.74876e7fffeb9p+36\", 13, \"100000000000\"},\n  {\"0x1.d1a94a1fffe66p+39\", 13, \"999999999999.9\"},\n  {\"0x1.d1a94a1fffe67p+39\", 13, \"1000000000000\"},\n  {\"0x1.2309ce53ffeffp+43\", 13, \"9999999999999\"},\n  {\"0x1.2309ce53fff00p+43\", 13, \"1e+13\"},\n  {\"0x1.a36e2eb1c4307p-14\", 14, \"9.9999999999999e-5\"},\n  {\"0x1.a36e2eb1c4308p-14\", 14, \"0.0001\"},\n  {\"0x1.0624dd2f1a9e4p-10\", 14, \"0.00099999999999999\"},\n  {\"0x1.0624dd2f1a9e5p-10\", 14, \"0.001\"},\n  {\"0x1.47ae147ae145ep-7\", 14, \"0.0099999999999999\"},\n  {\"0x1.47ae147ae145fp-7\", 14, \"0.01\"},\n  {\"0x1.9999999999975p-4\", 14, \"0.099999999999999\"},\n  {\"0x1.9999999999976p-4\", 14, \"0.1\"},\n  {\"0x1.fffffffffffd2p-1\", 14, \"0.99999999999999\"},\n  {\"0x1.fffffffffffd3p-1\", 14, \"1\"},\n  {\"0x1.3ffffffffffe3p+3\", 14, \"9.9999999999999\"},\n  {\"0x1.3ffffffffffe4p+3\", 14, \"10\"},\n  {\"0x1.8ffffffffffdcp+6\", 14, \"99.999999999999\"},\n  {\"0x1.8ffffffffffddp+6\", 14, \"100\"},\n  {\"0x1.f3fffffffffd4p+9\", 14, \"999.99999999999\"},\n  {\"0x1.f3fffffffffd5p+9\", 14, \"1000\"},\n  {\"0x1.387ffffffffe4p+13\", 14, \"9999.9999999999\"},\n  {\"0x1.387ffffffffe5p+13\", 14, \"10000\"},\n  {\"0x1.869ffffffffddp+16\", 14, \"99999.999999999\"},\n  {\"0x1.869ffffffffdep+16\", 14, \"100000\"},\n  {\"0x1.e847fffffffd5p+19\", 14, \"999999.99999999\"},\n  {\"0x1.e847fffffffd6p+19\", 14, \"1000000\"},\n  {\"0x1.312cfffffffe5p+23\", 14, \"9999999.9999999\"},\n  {\"0x1.312cfffffffe6p+23\", 14, \"10000000\"},\n  {\"0x1.7d783ffffffdep+26\", 14, \"99999999.999999\"},\n  {\"0x1.7d783ffffffdfp+26\", 14, \"100000000\"},\n  {\"0x1.dcd64ffffffd6p+29\", 14, \"999999999.99999\"},\n  {\"0x1.dcd64ffffffd7p+29\", 14, \"1000000000\"},\n  {\"0x1.2a05f1fffffe5p+33\", 14, \"9999999999.9999\"},\n  {\"0x1.2a05f1fffffe6p+33\", 14, \"10000000000\"},\n  {\"0x1.74876e7ffffdfp+36\", 14, \"99999999999.999\"},\n  {\"0x1.74876e7ffffe0p+36\", 14, \"100000000000\"},\n  {\"0x1.d1a94a1ffffd7p+39\", 14, \"999999999999.99\"},\n  {\"0x1.d1a94a1ffffd8p+39\", 14, \"1000000000000\"},\n  {\"0x1.2309ce53fffe6p+43\", 14, \"9999999999999.9\"},\n  {\"0x1.2309ce53fffe7p+43\", 14, \"10000000000000\"},\n  {\"0x1.6bcc41e8fffdfp+46\", 14, \"99999999999999\"},\n  {\"0x1.6bcc41e8fffe0p+46\", 14, \"1e+14\"},\n  {\"0x1.a36e2eb1c4328p-14\", 15, \"9.99999999999999e-5\"},\n  {\"0x1.a36e2eb1c4329p-14\", 15, \"0.0001\"},\n  {\"0x1.0624dd2f1a9f9p-10\", 15, \"0.000999999999999999\"},\n  {\"0x1.0624dd2f1a9fap-10\", 15, \"0.001\"},\n  {\"0x1.47ae147ae1477p-7\", 15, \"0.00999999999999999\"},\n  {\"0x1.47ae147ae1478p-7\", 15, \"0.01\"},\n  {\"0x1.9999999999995p-4\", 15, \"0.0999999999999999\"},\n  {\"0x1.9999999999996p-4\", 15, \"0.1\"},\n  {\"0x1.ffffffffffffbp-1\", 15, \"0.999999999999999\"},\n  {\"0x1.ffffffffffffcp-1\", 15, \"1\"},\n  {\"0x1.3fffffffffffdp+3\", 15, \"9.99999999999999\"},\n  {\"0x1.3fffffffffffep+3\", 15, \"10\"},\n  {\"0x1.8fffffffffffcp+6\", 15, \"99.9999999999999\"},\n  {\"0x1.8fffffffffffdp+6\", 15, \"100\"},\n  {\"0x1.f3ffffffffffbp+9\", 15, \"999.999999999999\"},\n  {\"0x1.f3ffffffffffcp+9\", 15, \"1000\"},\n  {\"0x1.387fffffffffdp+13\", 15, \"9999.99999999999\"},\n  {\"0x1.387fffffffffep+13\", 15, \"10000\"},\n  {\"0x1.869fffffffffcp+16\", 15, \"99999.9999999999\"},\n  {\"0x1.869fffffffffdp+16\", 15, \"100000\"},\n  {\"0x1.e847ffffffffbp+19\", 15, \"999999.999999999\"},\n  {\"0x1.e847ffffffffcp+19\", 15, \"1000000\"},\n  {\"0x1.312cffffffffdp+23\", 15, \"9999999.99999999\"},\n  {\"0x1.312cffffffffep+23\", 15, \"10000000\"},\n  {\"0x1.7d783fffffffcp+26\", 15, \"99999999.9999999\"},\n  {\"0x1.7d783fffffffdp+26\", 15, \"100000000\"},\n  {\"0x1.dcd64fffffffbp+29\", 15, \"999999999.999999\"},\n  {\"0x1.dcd64fffffffcp+29\", 15, \"1000000000\"},\n  {\"0x1.2a05f1ffffffdp+33\", 15, \"9999999999.99999\"},\n  {\"0x1.2a05f1ffffffep+33\", 15, \"10000000000\"},\n  {\"0x1.74876e7fffffcp+36\", 15, \"99999999999.9999\"},\n  {\"0x1.74876e7fffffdp+36\", 15, \"100000000000\"},\n  {\"0x1.d1a94a1fffffbp+39\", 15, \"999999999999.999\"},\n  {\"0x1.d1a94a1fffffcp+39\", 15, \"1000000000000\"},\n  {\"0x1.2309ce53ffffdp+43\", 15, \"9999999999999.99\"},\n  {\"0x1.2309ce53ffffep+43\", 15, \"10000000000000\"},\n  {\"0x1.6bcc41e8ffffcp+46\", 15, \"99999999999999.9\"},\n  {\"0x1.6bcc41e8ffffdp+46\", 15, \"100000000000000\"},\n  {\"0x1.c6bf52633fffbp+49\", 15, \"999999999999999\"},\n  {\"0x1.c6bf52633fffcp+49\", 15, \"1e+15\"},\n  {\"0x1.1c37937e07fffp+53\", 16, \"9999999999999998\"},\n  {\"0x1.1c37937e08000p+53\", 16, \"1e+16\"},\n  {\"0x1.6345785d89fffp+56\", 17, \"99999999999999984\"},\n  {\"0x1.6345785d8a000p+56\", 17, \"1e+17\"},\n  {\"0x1.bc16d674ec7ffp+59\", 18, \"999999999999999872\"},\n  {\"0x1.bc16d674ec800p+59\", 18, \"1e+18\"},\n  {\"0x1.158e460913cffp+63\", 19, \"9999999999999997952\"},\n  {\"0x1.158e460913d00p+63\", 19, \"1e+19\"},\n  {\"0x1.5af1d78b58c3fp+66\", 20, \"99999999999999983616\"},\n  {\"0x1.5af1d78b58c40p+66\", 20, \"1e+20\"},\n  {\"0x1.b1ae4d6e2ef4fp+69\", 21, \"999999999999999868928\"},\n  {\"0x1.b1ae4d6e2ef50p+69\", 21, \"1e+21\"},\n  {\"0x1.0f0cf064dd591p+73\", 22, \"9999999999999997902848\"},\n  {\"0x1.0f0cf064dd592p+73\", 22, \"1e+22\"},\n  {\"0x1.52d02c7e14af6p+76\", 23, \"99999999999999991611392\"},\n  {\"0x1.52d02c7e14af7p+76\", 23, \"1.0000000000000000838861e+23\"},\n  {\"0x1.a784379d99db4p+79\", 24, \"999999999999999983222784\"},\n  {\"0x1.a784379d99db5p+79\", 24, \"1.00000000000000011744051e+24\"},\n  {\"0x1.a36e2eb1c432cp-14\", 25, \"9.999999999999999123964645e-5\"},\n  {\"0x1.a36e2eb1c432dp-14\", 25, \"0.0001000000000000000047921736\"},\n  {\"0x1.0624dd2f1a9fbp-10\", 25, \"0.0009999999999999998039762472\"},\n  {\"0x1.0624dd2f1a9fcp-10\", 25, \"0.001000000000000000020816682\"},\n  {\"0x1.47ae147ae147ap-7\", 25, \"0.009999999999999998473443341\"},\n  {\"0x1.47ae147ae147bp-7\", 25, \"0.01000000000000000020816682\"},\n  {\"0x1.9999999999999p-4\", 25, \"0.09999999999999999167332732\"},\n  {\"0x1.999999999999ap-4\", 25, \"0.1000000000000000055511151\"},\n  {\"0x1.fffffffffffffp-1\", 25, \"0.9999999999999998889776975\"},\n  {\"0x1.0000000000000p+0\", 25, \"1\"},\n  {\"0x1.3ffffffffffffp+3\", 25, \"9.999999999999998223643161\"},\n  {\"0x1.4000000000000p+3\", 25, \"10\"},\n  {\"0x1.8ffffffffffffp+6\", 25, \"99.99999999999998578914528\"},\n  {\"0x1.9000000000000p+6\", 25, \"100\"},\n  {\"0x1.f3fffffffffffp+9\", 25, \"999.9999999999998863131623\"},\n  {\"0x1.f400000000000p+9\", 25, \"1000\"},\n  {\"0x1.387ffffffffffp+13\", 25, \"9999.999999999998181010596\"},\n  {\"0x1.3880000000000p+13\", 25, \"10000\"},\n  {\"0x1.869ffffffffffp+16\", 25, \"99999.99999999998544808477\"},\n  {\"0x1.86a0000000000p+16\", 25, \"100000\"},\n  {\"0x1.e847fffffffffp+19\", 25, \"999999.9999999998835846782\"},\n  {\"0x1.e848000000000p+19\", 25, \"1000000\"},\n  {\"0x1.312cfffffffffp+23\", 25, \"9999999.999999998137354851\"},\n  {\"0x1.312d000000000p+23\", 25, \"10000000\"},\n  {\"0x1.7d783ffffffffp+26\", 25, \"99999999.99999998509883881\"},\n  {\"0x1.7d78400000000p+26\", 25, \"100000000\"},\n  {\"0x1.dcd64ffffffffp+29\", 25, \"999999999.9999998807907104\"},\n  {\"0x1.dcd6500000000p+29\", 25, \"1000000000\"},\n  {\"0x1.2a05f1fffffffp+33\", 25, \"9999999999.999998092651367\"},\n  {\"0x1.2a05f20000000p+33\", 25, \"10000000000\"},\n  {\"0x1.74876e7ffffffp+36\", 25, \"99999999999.99998474121094\"},\n  {\"0x1.74876e8000000p+36\", 25, \"100000000000\"},\n  {\"0x1.d1a94a1ffffffp+39\", 25, \"999999999999.9998779296875\"},\n  {\"0x1.d1a94a2000000p+39\", 25, \"1000000000000\"},\n  {\"0x1.2309ce53fffffp+43\", 25, \"9999999999999.998046875\"},\n  {\"0x1.2309ce5400000p+43\", 25, \"10000000000000\"},\n  {\"0x1.6bcc41e8fffffp+46\", 25, \"99999999999999.984375\"},\n  {\"0x1.6bcc41e900000p+46\", 25, \"100000000000000\"},\n  {\"0x1.c6bf52633ffffp+49\", 25, \"999999999999999.875\"},\n  {\"0x1.c6bf526340000p+49\", 25, \"1000000000000000\"},\n  {\"0x1.1c37937e07fffp+53\", 25, \"9999999999999998\"},\n  {\"0x1.1c37937e08000p+53\", 25, \"10000000000000000\"},\n  {\"0x1.6345785d89fffp+56\", 25, \"99999999999999984\"},\n  {\"0x1.6345785d8a000p+56\", 25, \"100000000000000000\"},\n  {\"0x1.bc16d674ec7ffp+59\", 25, \"999999999999999872\"},\n  {\"0x1.bc16d674ec800p+59\", 25, \"1000000000000000000\"},\n  {\"0x1.158e460913cffp+63\", 25, \"9999999999999997952\"},\n  {\"0x1.158e460913d00p+63\", 25, \"10000000000000000000\"},\n  {\"0x1.5af1d78b58c3fp+66\", 25, \"99999999999999983616\"},\n  {\"0x1.5af1d78b58c40p+66\", 25, \"100000000000000000000\"},\n  {\"0x1.b1ae4d6e2ef4fp+69\", 25, \"999999999999999868928\"},\n  {\"0x1.b1ae4d6e2ef50p+69\", 25, \"1000000000000000000000\"},\n  {\"0x1.0f0cf064dd591p+73\", 25, \"9999999999999997902848\"},\n  {\"0x1.0f0cf064dd592p+73\", 25, \"10000000000000000000000\"},\n  {\"0x1.52d02c7e14af6p+76\", 25, \"99999999999999991611392\"},\n  {\"0x1.52d02c7e14af7p+76\", 25, \"100000000000000008388608\"},\n  {\"0x1.a784379d99db4p+79\", 25, \"999999999999999983222784\"},\n  {\"0x1.a784379d99db5p+79\", 25, \"1000000000000000117440512\"},\n  {\"0x1.08b2a2c280290p+83\", 25, \"9999999999999998758486016\"},\n  {\"0x1.08b2a2c280291p+83\", 25, \"1.000000000000000090596966e+25\"},\n  {\"0x1.4adf4b7320334p+86\", 26, \"99999999999999987584860160\"},\n  {\"0x1.4adf4b7320335p+86\", 26, \"1.0000000000000000476472934e+26\"},\n  {\"0x1.9d971e4fe8401p+89\", 27, \"999999999999999875848601600\"},\n  {\"0x1.9d971e4fe8402p+89\", 27, \"1.00000000000000001328755507e+27\"},\n  {\"0x1.027e72f1f1281p+93\", 28, \"9999999999999999583119736832\"},\n  {\"0x1.027e72f1f1282p+93\", 28, \"1.000000000000000178214299238e+28\"},\n  {\"0x1.431e0fae6d721p+96\", 29, \"99999999999999991433150857216\"},\n  {\"0x1.431e0fae6d722p+96\", 29, \"1.0000000000000000902533690163e+29\"},\n  {\"0x1.93e5939a08ce9p+99\", 30, \"999999999999999879147136483328\"},\n  {\"0x1.93e5939a08ceap+99\", 30, \"1.00000000000000001988462483866e+30\"},\n  {\"0x1.f8def8808b024p+102\", 31, \"9999999999999999635896294965248\"},\n  {\"0x1.f8def8808b025p+102\", 31, \"1.000000000000000076179620180787e+31\"},\n  {\"0x1.3b8b5b5056e16p+106\", 32, \"99999999999999987351763694911488\"},\n  {\"0x1.3b8b5b5056e17p+106\", 32, \"1.0000000000000000536616220439347e+32\"},\n  {\"0x1.8a6e32246c99cp+109\", 33, \"999999999999999945575230987042816\"},\n  {\"0x1.8a6e32246c99dp+109\", 33, \"1.00000000000000008969041906289869e+33\"},\n  {\"0x1.ed09bead87c03p+112\", 34, \"9999999999999999455752309870428160\"},\n  {\"0x1.ed09bead87c04p+112\", 34, \"1.000000000000000060867381447727514e+34\"},\n  {\"0x1.3426172c74d82p+116\", 35, \"99999999999999996863366107917975552\"},\n  {\"0x1.3426172c74d83p+116\", 35, \"1.0000000000000001531011018162752717e+35\"},\n  {\"0x1.812f9cf7920e2p+119\", 36, \"999999999999999894846684784341549056\"},\n  {\"0x1.812f9cf7920e3p+119\", 36, \"1.00000000000000004242063737401796198e+36\"},\n  {\"0x1.e17b84357691bp+122\", 37, \"9999999999999999538762658202121142272\"},\n  {\"0x1.e17b84357691cp+122\", 37, \"1.00000000000000007193542789195324457e+37\"},\n  {\"0x1.2ced32a16a1b1p+126\", 38, \"99999999999999997748809823456034029568\"},\n  {\"0x1.2ced32a16a1b2p+126\", 38, \"1.0000000000000001663827575493461488435e+38\"},\n  {\"0x1.78287f49c4a1dp+129\", 39, \"999999999999999939709166371603178586112\"},\n  {\"0x1.78287f49c4a1ep+129\", 39, \"1.00000000000000009082489382343182542438e+39\"},\n  {\"0x1.d6329f1c35ca4p+132\", 40, \"9999999999999999094860208812374492184576\"},\n  {\"0x1.d6329f1c35ca5p+132\", 40, \"1.000000000000000030378602842700366689075e+40\"},\n  {\"0x1.25dfa371a19e6p+136\", 41, \"99999999999999981277195531206711524196352\"},\n  {\"0x1.25dfa371a19e7p+136\", 41, \"1.0000000000000000062000864504077831949517e+41\"},\n  {\"0x1.6f578c4e0a060p+139\", 42, \"999999999999999890143207767403382423158784\"},\n  {\"0x1.6f578c4e0a061p+139\", 42, \"1.00000000000000004488571267807591678554931e+42\"},\n  {\"0x1.cb2d6f618c878p+142\", 43, \"9999999999999998901432077674033824231587840\"},\n  {\"0x1.cb2d6f618c879p+142\", 43, \"1.000000000000000013937211695941409913071206e+43\"},\n  {\"0x1.1efc659cf7d4bp+146\", 44, \"99999999999999989014320776740338242315878400\"},\n  {\"0x1.1efc659cf7d4cp+146\", 44, \"1.0000000000000000882136140530642264070186598e+44\"},\n  {\"0x1.66bb7f0435c9ep+149\", 45, \"999999999999999929757289024535551219930759168\"},\n  {\"0x1.66bb7f0435c9fp+149\", 45, \"1.00000000000000008821361405306422640701865984e+45\"},\n  {\"0x1.c06a5ec5433c6p+152\", 46, \"9999999999999999931398190359470212947659194368\"},\n  {\"0x1.c06a5ec5433c7p+152\", 46, \"1.000000000000000119904879058769961444436239974e+46\"},\n  {\"0x1.18427b3b4a05bp+156\", 47, \"99999999999999984102174700855949311516153479168\"},\n  {\"0x1.18427b3b4a05cp+156\", 47, \"1.0000000000000000438458430450761973546340476518e+47\"},\n  {\"0x1.5e531a0a1c872p+159\", 48, \"999999999999999881586566215862833963056037363712\"},\n  {\"0x1.5e531a0a1c873p+159\", 48, \"1.00000000000000004384584304507619735463404765184e+48\"},\n  {\"0x1.b5e7e08ca3a8fp+162\", 49, \"9999999999999999464902769475481793196872414789632\"},\n  {\"0x1.b5e7e08ca3a90p+162\", 49, \"1.000000000000000076297698410918870032949649709466e+49\"},\n  {\"0x1.11b0ec57e6499p+166\", 50, \"99999999999999986860582406952576489172979654066176\"},\n  {\"0x1.11b0ec57e649ap+166\", 50, \"1.0000000000000000762976984109188700329496497094656e+50\"},\n  {\"0x1.561d276ddfdc0p+169\", 51, \"999999999999999993220948674361627976461708441944064\"},\n  {\"0x1.561d276ddfdc1p+169\", 51, \"1.00000000000000015937444814747611208943759097698714e+51\"},\n  {\"0x1.aba4714957d30p+172\", 52, \"9999999999999999932209486743616279764617084419440640\"},\n  {\"0x1.aba4714957d31p+172\", 52, \"1.000000000000000126143748252853215266842414469978522e+52\"},\n  {\"0x1.0b46c6cdd6e3ep+176\", 53, \"99999999999999999322094867436162797646170844194406400\"},\n  {\"0x1.0b46c6cdd6e3fp+176\", 53, \"1.0000000000000002058974279999481676410708380867991962e+53\"},\n  {\"0x1.4e1878814c9cdp+179\", 54, \"999999999999999908150356944127012110618056584002011136\"},\n  {\"0x1.4e1878814c9cep+179\", 54, \"1.00000000000000007829154040459624384230536029988611686e+54\"},\n  {\"0x1.a19e96a19fc40p+182\", 55, \"9999999999999998741221202520331657642805958408251899904\"},\n  {\"0x1.a19e96a19fc41p+182\", 55, \"1.000000000000000010235067020408551149630438813532474573e+55\"},\n  {\"0x1.05031e2503da8p+186\", 56, \"99999999999999987412212025203316576428059584082518999040\"},\n  {\"0x1.05031e2503da9p+186\", 56, \"1.0000000000000000919028350814337823808403445971568453222e+56\"},\n  {\"0x1.4643e5ae44d12p+189\", 57, \"999999999999999874122120252033165764280595840825189990400\"},\n  {\"0x1.4643e5ae44d13p+189\", 57, \"1.00000000000000004834669211555365905752839484589051425587e+57\"},\n  {\"0x1.97d4df19d6057p+192\", 58, \"9999999999999999438119489974413630815797154428513196965888\"},\n  {\"0x1.97d4df19d6058p+192\", 58, \"1.000000000000000083191606488257757716177954646903579108966e+58\"},\n  {\"0x1.fdca16e04b86dp+195\", 59, \"99999999999999997168788049560464200849936328366177157906432\"},\n  {\"0x1.fdca16e04b86ep+195\", 59, \"1.0000000000000000831916064882577577161779546469035791089664e+59\"},\n  {\"0x1.3e9e4e4c2f344p+199\", 60, \"999999999999999949387135297074018866963645011013410073083904\"},\n  {\"0x1.3e9e4e4c2f345p+199\", 60, \"1.00000000000000012779309688531900399924939119220030212092723e+60\"},\n  {\"0x1.8e45e1df3b015p+202\", 61, \"9999999999999999493871352970740188669636450110134100730839040\"},\n  {\"0x1.8e45e1df3b016p+202\", 61, \"1.000000000000000092111904567670006972792241955962923711358566e+61\"},\n  {\"0x1.f1d75a5709c1ap+205\", 62, \"99999999999999992084218144295482124579792562202350734542897152\"},\n  {\"0x1.f1d75a5709c1bp+205\", 62, \"1.0000000000000000350219968594316117304608031779831182560487014e+62\"},\n  {\"0x1.3726987666190p+209\", 63, \"999999999999999875170255276364105051932774599639662981181079552\"},\n  {\"0x1.3726987666191p+209\", 63, \"1.00000000000000005785795994272696982739337868917504043817264742e+63\"},\n  {\"0x1.84f03e93ff9f4p+212\", 64, \"9999999999999998751702552763641050519327745996396629811810795520\"},\n  {\"0x1.84f03e93ff9f5p+212\", 64, \"1.00000000000000002132041900945439687230125787126796494677433385e+64\"},\n  {\"0x1.e62c4e38ff872p+215\", 65, \"99999999999999999209038626283633850822756121694230455365568299008\"},\n  {\"0x1.e62c4e38ff873p+215\", 65, \"1.0000000000000001090105172493085719645223478342449461261302864282e+65\"},\n  {\"0x1.2fdbb0e39fb47p+219\", 66, \"999999999999999945322333868247445125709646570021247924665841614848\"},\n  {\"0x1.2fdbb0e39fb48p+219\", 66, \"1.00000000000000013239454344660301865578130515770547444062520711578e+66\"},\n  {\"0x1.7bd29d1c87a19p+222\", 67, \"9999999999999999827367757839185598317239782875580932278577147150336\"},\n  {\"0x1.7bd29d1c87a1ap+222\", 67, \"1.000000000000000132394543446603018655781305157705474440625207115776e+67\"},\n  {\"0x1.dac74463a989fp+225\", 68, \"99999999999999995280522225138166806691251291352861698530421623488512\"},\n  {\"0x1.dac74463a98a0p+225\", 68, \"1.000000000000000072531436381529235126158374409646521955518210155479e+68\"},\n  {\"0x1.28bc8abe49f63p+229\", 69, \"999999999999999880969493773293127831364996015857874003175819882528768\"},\n  {\"0x1.28bc8abe49f64p+229\", 69, \"1.00000000000000007253143638152923512615837440964652195551821015547904e+69\"},\n  {\"0x1.72ebad6ddc73cp+232\", 70, \"9999999999999999192818822949403492903236716946156035936442979371188224\"},\n  {\"0x1.72ebad6ddc73dp+232\", 70, \"1.00000000000000007253143638152923512615837440964652195551821015547904e+70\"},\n  {\"0x1.cfa698c95390bp+235\", 71, \"99999999999999991928188229494034929032367169461560359364429793711882240\"},\n  {\"0x1.cfa698c95390cp+235\", 71, \"1.0000000000000000418815255642114579589914338666403382831434277118069965e+71\"},\n  {\"0x1.21c81f7dd43a7p+239\", 72, \"999999999999999943801810948794571024057224129020550531544123892056457216\"},\n  {\"0x1.21c81f7dd43a8p+239\", 72, \"1.00000000000000013996124017962834489392564360426012603474273153155753574e+72\"},\n  {\"0x1.6a3a275d49491p+242\", 73, \"9999999999999999830336967949613257980309080240684656321838454199566729216\"},\n  {\"0x1.6a3a275d49492p+242\", 73, \"1.000000000000000139961240179628344893925643604260126034742731531557535744e+73\"},\n  {\"0x1.c4c8b1349b9b5p+245\", 74, \"99999999999999995164818811802792197885196090803013355167206819763650035712\"},\n  {\"0x1.c4c8b1349b9b6p+245\", 74, \"1.000000000000000077190222825761537255567749372183461873719177086917190615e+74\"},\n  {\"0x1.1afd6ec0e1411p+249\", 75, \"999999999999999926539781176481198923508803215199467887262646419780362305536\"},\n  {\"0x1.1afd6ec0e1412p+249\", 75, \"1.00000000000000012740703670885498336625406475784479320253802064262946671821e+75\"},\n  {\"0x1.61bcca7119915p+252\", 76, \"9999999999999998863663300700064420349597509066704028242075715752105414230016\"},\n  {\"0x1.61bcca7119916p+252\", 76, \"1.000000000000000047060134495905469589155960140786663076427870953489824953139e+76\"},\n  {\"0x1.ba2bfd0d5ff5bp+255\", 77, \"99999999999999998278261272554585856747747644714015897553975120217811154108416\"},\n  {\"0x1.ba2bfd0d5ff5cp+255\", 77, \"1.0000000000000001113376562662650806108344438344331671773159907048015383651942e+77\"},\n  {\"0x1.145b7e285bf98p+259\", 78, \"999999999999999802805551768538947706777722104929947493053015898505313987330048\"},\n  {\"0x1.145b7e285bf99p+259\", 78, \"1.00000000000000000849362143368970297614886992459876061589499910270279690590618e+78\"},\n  {\"0x1.59725db272f7fp+262\", 79, \"9999999999999999673560075006595519222746403606649979913266024618633003221909504\"},\n  {\"0x1.59725db272f80p+262\", 79, \"1.000000000000000131906463232780156137771558616400048489600189025221286657051853e+79\"},\n  {\"0x1.afcef51f0fb5ep+265\", 80, \"99999999999999986862573406138718939297648940722396769236245052384850852127440896\"},\n  {\"0x1.afcef51f0fb5fp+265\", 80, \"1.0000000000000000002660986470836727653740240118120080909813197745348975891631309e+80\"},\n  {\"0x1.0de1593369d1bp+269\", 81, \"999999999999999921281879895665782741935503249059183851809998224123064148429897728\"},\n  {\"0x1.0de1593369d1cp+269\", 81, \"1.0000000000000001319064632327801561377715586164000484896001890252212866570518528e+81\"},\n  {\"0x1.5159af8044462p+272\", 82, \"9999999999999999634067965630886574211027143225273567793680363843427086501542887424\"},\n  {\"0x1.5159af8044463p+272\", 82, \"1.0000000000000001319064632327801561377715586164000484896001890252212866570518528e+82\"},\n  {\"0x1.a5b01b605557ap+275\", 83, \"99999999999999989600692989521205793443517660497828009527517532799127744739526311936\"},\n  {\"0x1.a5b01b605557bp+275\", 83, \"1.0000000000000000308066632309652569077702520400764334634608974406941398529133143654e+83\"},\n  {\"0x1.078e111c3556cp+279\", 84, \"999999999999999842087036560910778345101146430939018748000886482910132485188042620928\"},\n  {\"0x1.078e111c3556dp+279\", 84, \"1.00000000000000005776660989811589670243726712709606413709804186323471233401692461466e+84\"},\n  {\"0x1.4971956342ac7p+282\", 85, \"9999999999999998420870365609107783451011464309390187480008864829101324851880426209280\"},\n  {\"0x1.4971956342ac8p+282\", 85, \"1.00000000000000001463069523067487303097004298786465505927861078716979636425114821591e+85\"},\n  {\"0x1.9bcdfabc13579p+285\", 86, \"99999999999999987659576829486359728227492574232414601025643134376206526100066373992448\"},\n  {\"0x1.9bcdfabc1357ap+285\", 86, \"1.0000000000000000146306952306748730309700429878646550592786107871697963642511482159104e+86\"},\n  {\"0x1.0160bcb58c16cp+289\", 87, \"999999999999999959416724456350362731491996089648451439669739009806703922950954425516032\"},\n  {\"0x1.0160bcb58c16dp+289\", 87, \"1.0000000000000001802726075536484039294041836825132659181052261192590736881517295870935e+87\"},\n  {\"0x1.41b8ebe2ef1c7p+292\", 88, \"9999999999999999594167244563503627314919960896484514396697390098067039229509544255160320\"},\n  {\"0x1.41b8ebe2ef1c8p+292\", 88, \"1.00000000000000013610143093418879568982174616394030302241812869736859973511157455477801e+88\"},\n  {\"0x1.922726dbaae39p+295\", 89, \"99999999999999999475366575191804932315794610450682175621941694731908308538307845136842752\"},\n  {\"0x1.922726dbaae3ap+295\", 89, \"1.0000000000000001361014309341887956898217461639403030224181286973685997351115745547780096e+89\"},\n  {\"0x1.f6b0f092959c7p+298\", 90, \"999999999999999966484112715463900049825186092620125502979674597309179755437379230686511104\"},\n  {\"0x1.f6b0f092959c8p+298\", 90, \"1.00000000000000007956232486128049714315622614016691051593864399734879307522017611341417677e+90\"},\n  {\"0x1.3a2e965b9d81cp+302\", 91, \"9999999999999998986371854279739417938265620640920544952042929572854117635677011010499117056\"},\n  {\"0x1.3a2e965b9d81dp+302\", 91, \"1.000000000000000079562324861280497143156226140166910515938643997348793075220176113414176768e+91\"},\n  {\"0x1.88ba3bf284e23p+305\", 92, \"99999999999999989863718542797394179382656206409205449520429295728541176356770110104991170560\"},\n  {\"0x1.88ba3bf284e24p+305\", 92, \"1.0000000000000000433772969746191860732902933249519393117917737893361168128896811109413237555e+92\"},\n  {\"0x1.eae8caef261acp+308\", 93, \"999999999999999927585207737302990649719308316264031458521789123695552773432097103028194115584\"},\n  {\"0x1.eae8caef261adp+308\", 93, \"1.00000000000000004337729697461918607329029332495193931179177378933611681288968111094132375552e+93\"},\n  {\"0x1.32d17ed577d0bp+312\", 94, \"9999999999999998349515363474500343108625203093137051759058013911831015418660298966976904036352\"},\n  {\"0x1.32d17ed577d0cp+312\", 94, \"1.000000000000000020218879127155946988576096323214357741137776856208004004998164309358697827533e+94\"},\n  {\"0x1.7f85de8ad5c4ep+315\", 95, \"99999999999999987200500490339121684640523551209383568895219648418808203449245677922989188841472\"},\n  {\"0x1.7f85de8ad5c4fp+315\", 95, \"1.0000000000000000202188791271559469885760963232143577411377768562080040049981643093586978275328e+95\"},\n  {\"0x1.df67562d8b362p+318\", 96, \"999999999999999931290554592897108903273579836542044509826428632996050822694739791281414264061952\"},\n  {\"0x1.df67562d8b363p+318\", 96, \"1.00000000000000004986165397190889301701026848543846215157489293061198839909930581538445901535642e+96\"},\n  {\"0x1.2ba095dc7701dp+322\", 97, \"9999999999999998838621148412923952577789043769834774531270429139496757921329133816401963635441664\"},\n  {\"0x1.2ba095dc7701ep+322\", 97, \"1.000000000000000073575873847711249839757606215217745679924585790135175914380219020205067965615309e+97\"},\n  {\"0x1.7688bb5394c25p+325\", 98, \"99999999999999999769037024514370800696612547992403838920556863966097586548129676477911932478685184\"},\n  {\"0x1.7688bb5394c26p+325\", 98, \"1.0000000000000001494613774502787916725490869505114529706436029406093759632791412756310166064437658e+98\"},\n  {\"0x1.d42aea2879f2ep+328\", 99, \"999999999999999967336168804116691273849533185806555472917961779471295845921727862608739868455469056\"},\n  {\"0x1.d42aea2879f2fp+328\", 99, \"1.00000000000000008875297456822475820631590236227648713806838922023001592416000347129025769378100019e+99\"},\n  {\"0x1.249ad2594c37cp+332\", 100, \"9999999999999998216360018871870109548898901740426374747374488505608317520357971321909184780648316928\"},\n  {\"0x1.249ad2594c37dp+332\", 100, \"1.00000000000000001590289110975991804683608085639452813897813275577478387721703810608134699858568151e+100\"},\n  {\"0x1.6dc186ef9f45cp+335\", 101, \"99999999999999997704951326524533662844684271992415000612999597473199345218078991130326129448151154688\"},\n  {\"0x1.6dc186ef9f45dp+335\", 101, \"1.000000000000000132463024643303662302003795265805662537522543098903155152325782690415604110898191401e+101\"},\n  {\"0x1.c931e8ab87173p+338\", 102, \"999999999999999977049513265245336628446842719924150006129995974731993452180789911303261294481511546880\"},\n  {\"0x1.c931e8ab87174p+338\", 102, \"1.00000000000000010138032236769199716729240475662936003124403367406892281229678413459313554761485543014e+102\"},\n  {\"0x1.1dbf316b346e7p+342\", 103, \"9999999999999998029863805218200118740630558685368559709703431956602923480183979986974373400948301103104\"},\n  {\"0x1.1dbf316b346e8p+342\", 103, \"1.000000000000000001915675085734668736215955127265192011152803514599379324203988755961236145108180323533e+103\"},\n  {\"0x1.652efdc6018a1p+345\", 104, \"99999999999999984277223943460294324649363572028252317900683525944810974325551615015019710109750015295488\"},\n  {\"0x1.652efdc6018a2p+345\", 104, \"1.0000000000000000019156750857346687362159551272651920111528035145993793242039887559612361451081803235328e+104\"},\n  {\"0x1.be7abd3781ecap+348\", 105, \"999999999999999938258300825281978540327027364472124478294416212538871491824599713636820527503908255301632\"},\n  {\"0x1.be7abd3781ecbp+348\", 105, \"1.00000000000000006557304934618735893210488289005825954401119081665988715658337779828565176271245239176397e+105\"},\n  {\"0x1.170cb642b133ep+352\", 106, \"9999999999999998873324014169198263836158851542376704520077063708904652259210884797772880334204906007166976\"},\n  {\"0x1.170cb642b133fp+352\", 106, \"1.000000000000000091035999050368435010460453995175486557154545737484090289535133415215418009754161219056435e+106\"},\n  {\"0x1.5ccfe3d35d80ep+355\", 107, \"99999999999999996881384047029926983435371269061279689406644211752791525136670645395254002395395884805259264\"},\n  {\"0x1.5ccfe3d35d80fp+355\", 107, \"1.0000000000000001317767185770581567358293677633630497781839136108028153022579424023030440050208953427243827e+107\"},\n  {\"0x1.b403dcc834e11p+358\", 108, \"999999999999999903628689227595715073763450661512695740419453520217955231010212074612338431527184250183876608\"},\n  {\"0x1.b403dcc834e12p+358\", 108, \"1.00000000000000003399899171300282459494397471971289804771343071483787527172320083329274161638073344592130867e+108\"},\n  {\"0x1.108269fd210cbp+362\", 109, \"9999999999999999818508707188399807864717650964328171247958398369899072554380053298205803424393137676263358464\"},\n  {\"0x1.108269fd210ccp+362\", 109, \"1.000000000000000190443354695491356020360603589553140816466203348381779320578787343709225438204992480806227149e+109\"},\n  {\"0x1.54a3047c694fdp+365\", 110, \"99999999999999985669538033284915564613846200056062290979362173015478401635353612148739328497990653971840106496\"},\n  {\"0x1.54a3047c694fep+365\", 110, \"1.0000000000000000235693675141702558332495327950568818631299125392682816684661617325983093615924495102623141069e+110\"},\n  {\"0x1.a9cbc59b83a3dp+368\", 111, \"999999999999999956819772641641815758405104477258378281795396215622882607621111488153942930947432322044748890112\"},\n  {\"0x1.a9cbc59b83a3ep+368\", 111, \"1.00000000000000009031896238669869590809396111285538544446442886291368072931121197704267579223746669847987932365e+111\"},\n  {\"0x1.0a1f5b8132466p+372\", 112, \"9999999999999999301199346926304397284673331501389768492615896861647229832830913903761963586894254467577228034048\"},\n  {\"0x1.0a1f5b8132467p+372\", 112, \"1.000000000000000143718638284721447967969503767094188309532041921829999977987252172598168936753480449053931497062e+112\"},\n  {\"0x1.4ca732617ed7fp+375\", 113, \"99999999999999984468045325579403643266646490335689226515340879189861218540142707748740732746380344583923932594176\"},\n  {\"0x1.4ca732617ed80p+375\", 113, \"1.0000000000000000155594161294668430242682013969210614333697705804308337811647557032649853899150474476762062808678e+113\"},\n  {\"0x1.9fd0fef9de8dfp+378\", 114, \"999999999999999878856245830528597750986812202069726098796681149605056504554092802642922939954052246206632716926976\"},\n  {\"0x1.9fd0fef9de8e0p+378\", 114, \"1.00000000000000001555941612946684302426820139692106143336977058043083378116475570326498538991504744767620628086784e+114\"},\n  {\"0x1.03e29f5c2b18bp+382\", 115, \"9999999999999997968343436511656505870179786851589248980528274911095901385876950622696854699774551253248885785624576\"},\n  {\"0x1.03e29f5c2b18cp+382\", 115, \"1.00000000000000001555941612946684302426820139692106143336977058043083378116475570326498538991504744767620628086784e+115\"},\n  {\"0x1.44db473335deep+385\", 116, \"99999999999999984057935814682588907446802322751135220511621610897383886710310719046874545396497358979515211902353408\"},\n  {\"0x1.44db473335defp+385\", 116, \"1.00000000000000001555941612946684302426820139692106143336977058043083378116475570326498538991504744767620628086784e+116\"},\n  {\"0x1.961219000356ap+388\", 117, \"999999999999999910571381339882270654388094495275235896417637897556636832727766595587241428345003132947573783761256448\"},\n  {\"0x1.961219000356bp+388\", 117, \"1.00000000000000005055542772599503381422823703080300327902048147472223276397708540582423337710506221925241711323670118e+117\"},\n  {\"0x1.fb969f40042c5p+391\", 118, \"9999999999999999665649998943273759183241515094863428494587753284228752052274941196820382078490267674695111155514343424\"},\n  {\"0x1.fb969f40042c6p+391\", 118, \"1.000000000000000078552237003217586446196265537908556755541050190155351950226949167871631766857074036513385779131790131e+118\"},\n  {\"0x1.3d3e2388029bbp+395\", 119, \"99999999999999994416755247254933381274972870380190006824232035607637985622760311004411949604741731366073618283536318464\"},\n  {\"0x1.3d3e2388029bcp+395\", 119, \"1.0000000000000001233471318467736706573451111492774423179739601348483426482267311871474691904602929441309356445639324467e+119\"},\n  {\"0x1.8c8dac6a0342ap+398\", 120, \"999999999999999980003468347394201181668805192897008518188648311830772414627428725464789434929992439754776075181077037056\"},\n  {\"0x1.8c8dac6a0342bp+398\", 120, \"1.00000000000000012334713184677367065734511114927744231797396013484834264822673118714746919046029294413093564456393244672e+120\"},\n  {\"0x1.efb1178484134p+401\", 121, \"9999999999999999226660029476424133913982828103448349982745235826237443211877077407917175327178722380043122474279348731904\"},\n  {\"0x1.efb1178484135p+401\", 121, \"1.000000000000000037340933747145988971939327575449182038102773041037800508067149710137861337142112641505239902934219200922e+121\"},\n  {\"0x1.35ceaeb2d28c0p+405\", 122, \"99999999999999983092605830803955292696544699826135736641192401589249937168415416531480248917847991520357012302290741100544\"},\n  {\"0x1.35ceaeb2d28c1p+405\", 122, \"1.0000000000000000144059475872452738558311186224283126301371231493549892706912613162686325762572645608050543718329623353754e+122\"},\n  {\"0x1.83425a5f872f1p+408\", 123, \"999999999999999977709969731404129670057984297594921577392083322662491290889839886077866558841507631684757522070951350501376\"},\n  {\"0x1.83425a5f872f2p+408\", 123, \"1.00000000000000012449388115476870641315052159692848578837224262943248321009552560684093062850453534816594492111899528999731e+123\"},\n  {\"0x1.e412f0f768fadp+411\", 124, \"9999999999999999483531874467312143214394768377282087351960514613084929070487027419252537449089020883885200422613425626021888\"},\n  {\"0x1.e412f0f768faep+411\", 124, \"1.000000000000000065780316585422875715913506677195060103980178906724486442413251318535705000639324261573469961499777714198938e+124\"},\n  {\"0x1.2e8bd69aa19ccp+415\", 125, \"99999999999999992486776161899288204254467086983483846143922597222529419997579302660316349376281765375153005841365553228283904\"},\n  {\"0x1.2e8bd69aa19cdp+415\", 125, \"1.0000000000000001127511682408995402737031186129818006514938298848908838565590707491798855029314931308474499291951517748376371e+125\"},\n  {\"0x1.7a2ecc414a03fp+418\", 126, \"999999999999999924867761618992882042544670869834838461439225972225294199975793026603163493762817653751530058413655532282839040\"},\n  {\"0x1.7a2ecc414a040p+418\", 126, \"1.0000000000000000751744869165182086274714290643524082134829091023577659252424152046645411010977580354282659550388525263266775e+126\"},\n  {\"0x1.d8ba7f519c84fp+421\", 127, \"9999999999999999549291066784979473595300225087383524118479625982517885450291174622154390152298057300868772377386949310916067328\"},\n  {\"0x1.d8ba7f519c850p+421\", 127, \"1.000000000000000075174486916518208627471429064352408213482909102357765925242415204664541101097758035428265955038852526326677504e+127\"},\n  {\"0x1.27748f9301d31p+425\", 128, \"99999999999999988278187853568579059876517857536991893086699469578820211690113881674597776370903434688204400735860037395056427008\"},\n  {\"0x1.27748f9301d32p+425\", 128, \"1.000000000000000075174486916518208627471429064352408213482909102357765925242415204664541101097758035428265955038852526326677504e+128\"},\n  {\"0x1.7151b377c247ep+428\", 129, \"999999999999999998217443564185241415988928868759412500436543339729940401905904649497115766142268560009777175966751665376232210432\"},\n  {\"0x1.7151b377c247fp+428\", 129, \"1.00000000000000015213153026885117583895392925994540392652927486498559144857892575983196643605324751084675473411095338727712279757e+129\"},\n  {\"0x1.cda62055b2d9dp+431\", 130, \"9999999999999999366518088823188676468029287122850159299994507296276799832366962053631754981778769796749861527090709766158759755776\"},\n  {\"0x1.cda62055b2d9ep+431\", 130, \"1.000000000000000059783078246051615185174929025233809070873635949832200820575113093631056034106660140344568199224432354136588445286e+130\"},\n  {\"0x1.2087d4358fc82p+435\", 131, \"99999999999999991202555500957231813912852864969525730182461368558677581576901282770959939099212034754106974340599870111173348163584\"},\n  {\"0x1.2087d4358fc83p+435\", 131, \"1.0000000000000001090355859915447142005237291504133263272233100379140091555104798489382082484781734046124010178305769051448734331699e+131\"},\n  {\"0x1.68a9c942f3ba3p+438\", 132, \"999999999999999990829567402361276563686608849982484911984092226517669151665599636201042933986541570369602253175829982724989462249472\"},\n  {\"0x1.68a9c942f3ba4p+438\", 132, \"1.00000000000000014843759218793919341280276925055694013230304930837945582345877325318393001997538401602666727271549254595150142347674e+132\"},\n  {\"0x1.c2d43b93b0a8bp+441\", 133, \"9999999999999998962647525310145264542169126096378117797927179774005971485896954660113106823932361029753632414520324447890822855131136\"},\n  {\"0x1.c2d43b93b0a8cp+441\", 133, \"1.000000000000000022351172359476859933509840930097375956047883642890026486024234359597620351184310059501015257083762495370291854494925e+133\"},\n  {\"0x1.19c4a53c4e697p+445\", 134, \"99999999999999992148203649670699315007549827372972461504375111049848301607660324472857261615145089428049364457837845490532419930947584\"},\n  {\"0x1.19c4a53c4e698p+445\", 134, \"1.0000000000000001232203082222467267169441835864650272970520161752815699559718654744666680862171692247215368695891465358352595096803738e+134\"},\n  {\"0x1.6035ce8b6203dp+448\", 135, \"999999999999999961829690841814939863449235336276785151445404123455100404055655690676191710164594560368702289580532071091311261383655424\"},\n  {\"0x1.6035ce8b6203ep+448\", 135, \"1.00000000000000012322030822224672671694418358646502729705201617528156995597186547446666808621716922472153686958914653583525950968037376e+135\"},\n  {\"0x1.b843422e3a84cp+451\", 136, \"9999999999999999295515673657285824927502456862391367223240817130898064936724137339180964349540796274981353735788091781425216117243117568\"},\n  {\"0x1.b843422e3a84dp+451\", 136, \"1.000000000000000058664061270074011975546204286389730438809371354550982135205381560950477535796139358980403037585700749937680210361686426e+136\"},\n  {\"0x1.132a095ce492fp+455\", 137, \"99999999999999982626157224225223890651347880611866174913584999992086598044603947229219155428043184231232124237329592070639473281441202176\"},\n  {\"0x1.132a095ce4930p+455\", 137, \"1.0000000000000000328415624892049260789870125663596116955123134262587470068987879955440013156277274126839495047843224355786484906342114918e+137\"},\n  {\"0x1.57f48bb41db7bp+458\", 138, \"999999999999999867577570291642776341008185558166851738411142685188442185736589176942553506549890956386646894855501223680845484378371915776\"},\n  {\"0x1.57f48bb41db7cp+458\", 138, \"1.00000000000000003284156248920492607898701256635961169551231342625874700689878799554400131562772741268394950478432243557864849063421149184e+138\"},\n  {\"0x1.adf1aea12525ap+461\", 139, \"9999999999999999006303687311552062886039509598054037298313768334025031499690289406628430683654582476461074168412654660604060856295398309888\"},\n  {\"0x1.adf1aea12525bp+461\", 139, \"1.00000000000000003284156248920492607898701256635961169551231342625874700689878799554400131562772741268394950478432243557864849063421149184e+139\"},\n  {\"0x1.0cb70d24b7378p+465\", 140, \"99999999999999984774589122793531837245072631718372054355900219626000560719712531871037976946055058163097058166404267825310912362767116664832\"},\n  {\"0x1.0cb70d24b7379p+465\", 140, \"1.0000000000000000592838012408148700370636248876704532886485007448299957782847398065202329650801812456915179223729338294822969716351458240102e+140\"},\n  {\"0x1.4fe4d06de5056p+468\", 141, \"999999999999999847745891227935318372450726317183720543559002196260005607197125318710379769460550581630970581664042678253109123627671166648320\"},\n  {\"0x1.4fe4d06de5057p+468\", 141, \"1.00000000000000001697621923823895970414104517357310673963060103511599774406721690895826232595625511287940845423115559923645940203365089253786e+141\"},\n  {\"0x1.a3de04895e46cp+471\", 142, \"9999999999999999154380224320567749051268538597394750219876417318024024619451619548095327920588323941303457306908878466464492349900630570041344\"},\n  {\"0x1.a3de04895e46dp+471\", 142, \"1.000000000000000050822284840299687970479108944850983978844920802887196171441235227007838837255396019129096028744578183433129457714846837715763e+142\"},\n  {\"0x1.066ac2d5daec3p+475\", 143, \"99999999999999980713061250546244445284504979165026785650181847493456749434830333705088795590158149413134549224793557721710505681023603243483136\"},\n  {\"0x1.066ac2d5daec4p+475\", 143, \"1.0000000000000000237454323586511053574086579278286821874734649886702374295420205725681776282160832941293459691338401160757934131698900815734374e+143\"},\n  {\"0x1.4805738b51a74p+478\", 144, \"999999999999999850453576476100176633757771418885950722696147777681701481387046784154345890364481854130945587625116484988842728082166842262552576\"},\n  {\"0x1.4805738b51a75p+478\", 144, \"1.00000000000000002374543235865110535740865792782868218747346498867023742954202057256817762821608329412934596913384011607579341316989008157343744e+144\"},\n  {\"0x1.9a06d06e26112p+481\", 145, \"9999999999999999890870611821409196126784806260401358945180015464725302399110258148854112806457630061296658928320953898584032761523454337112604672\"},\n  {\"0x1.9a06d06e26113p+481\", 145, \"1.000000000000000127720545888181662591599189833194321066339855315263358998435004845616476670927044158128386198039074294727963824222524025159968358e+145\"},\n  {\"0x1.00444244d7cabp+485\", 146, \"99999999999999993363366729972462242111019694317846182578926003895619873650143420259298512453325054533017777074930382791057905692427399713177731072\"},\n  {\"0x1.00444244d7cacp+485\", 146, \"1.0000000000000001554472428293898111873833316746251581007042260690215247501398006517626897489833003885281302590804700757018759338365597434497099366e+146\"},\n  {\"0x1.405552d60dbd6p+488\", 147, \"999999999999999977996382405657660174364823889467801080772253244969263939229107492426926049423260513969768268415537077468838432306731146395363835904\"},\n  {\"0x1.405552d60dbd7p+488\", 147, \"1.00000000000000015544724282938981118738333167462515810070422606902152475013980065176268974898330038852813025908047007570187593383655974344970993664e+147\"},\n  {\"0x1.906aa78b912cbp+491\", 148, \"9999999999999999070160382361647997691574207754048582727994641153483596148648302286926205695992445641464234721495638781756234316947997075736253956096\"},\n  {\"0x1.906aa78b912ccp+491\", 148, \"1.000000000000000048976726575150520579572227003530743888745042374590168263593384756161231529247276463793113064681510276762053432918662585217102276198e+148\"},\n  {\"0x1.f485516e7577ep+494\", 149, \"99999999999999993540817590396194393124038202103003539598857976719672134461054113418634276152885094407576139065595315789290943193957228310232077172736\"},\n  {\"0x1.f485516e7577fp+494\", 149, \"1.0000000000000000489767265751505205795722270035307438887450423745901682635933847561612315292472764637931130646815102767620534329186625852171022761984e+149\"},\n  {\"0x1.38d352e5096afp+498\", 150, \"999999999999999980835596172437374590573120014030318793091164810154100112203678582976298268616221151962702060266176005440567032331208403948233373515776\"},\n  {\"0x1.38d352e5096b0p+498\", 150, \"1.00000000000000016254527724633909722790407198603145238150150498198361518257622837813612029696570198351046473870706739563119743389775288733188378066944e+150\"},\n  {\"0x1.8708279e4bc5ap+501\", 151, \"9999999999999998718097875280963410081745488308296386400449607070563910699801487058804050516065326530340444532016411713261887913912817139180431292235776\"},\n  {\"0x1.8708279e4bc5bp+501\", 151, \"1.000000000000000017177532387217719118039310408430545510773232844520003126278188542008262674286117318272254595954354283478693112644517300624963454946509e+151\"},\n  {\"0x1.e8ca3185deb71p+504\", 152, \"99999999999999992995688547174489225212045346187000138833626956204183589249936464033154810067836651912932851030272641618719051989257594860081125951275008\"},\n  {\"0x1.e8ca3185deb72p+504\", 152, \"1.000000000000000046251081359041994740012262723950726884918887272012725537537796509233834198822034251319896624504896905909193976895164417966347520091095e+152\"},\n  {\"0x1.317e5ef3ab327p+508\", 153, \"999999999999999999733403004123153744855539019118436686285840188024369679522423761672919759564567158443669378824028710020392594094129030220133015859757056\"},\n  {\"0x1.317e5ef3ab328p+508\", 153, \"1.00000000000000018580411642379851772548243383844759748081802852397779311158391475191657751659443552994857836154750149357559812529827058120499103278510899e+153\"},\n  {\"0x1.7dddf6b095ff0p+511\", 154, \"9999999999999998880909749523179353564794021275209402095665271864523156202855291675267251053466461355407239891899450398872692753716440996292182057045458944\"},\n  {\"0x1.7dddf6b095ff1p+511\", 154, \"1.000000000000000036947545688058226540980917982984268845192277855215054365934721959721651310970540832744651175368723266731433700334957340417104619244827443e+154\"},\n  {\"0x1.dd55745cbb7ecp+514\", 155, \"99999999999999988809097495231793535647940212752094020956652718645231562028552916752672510534664613554072398918994503988726927537164409962921820570454589440\"},\n  {\"0x1.dd55745cbb7edp+514\", 155, \"1.0000000000000000071762315409101683040806148118916031180671277214625066168048834012826660698457618933038657381329676213626008153422946922595273365367711334e+155\"},\n  {\"0x1.2a5568b9f52f4p+518\", 156, \"999999999999999983359180223191721714560372275017470536367007614460468417501012554531477876945938741751237388344363105067534507348164573733465510370326085632\"},\n  {\"0x1.2a5568b9f52f5p+518\", 156, \"1.0000000000000001738955907649392944307223125700105311899679684704767740119319793285409834201445239541722641866531992354280649713012055219419601197018864681e+156\"},\n  {\"0x1.74eac2e8727b1p+521\", 157, \"9999999999999999833591802231917217145603722750174705363670076144604684175010125545314778769459387417512373883443631050675345073481645737334655103703260856320\"},\n  {\"0x1.74eac2e8727b2p+521\", 157, \"1.000000000000000135788308656589779887489924511011919059247776299273512893045785973739082311504806911688058826991432009355958878510597332300261197835574391603e+157\"},\n  {\"0x1.d22573a28f19dp+524\", 158, \"99999999999999995287335453651211007997446182781858083179085387749785952239205787068995699003416510776387310061494932420984963311567802202010637287727642443776\"},\n  {\"0x1.d22573a28f19ep+524\", 158, \"1.0000000000000000748166572832305566183181036166141396500954688253482951028278766060560405376812596437133302515326044476405891300456242288735429228494750692147e+158\"},\n  {\"0x1.2357684599702p+528\", 159, \"999999999999999928484693987168420772305733470059469068129930887927772406304894123616740280504746200573981670431418299523701733729688780649419062882836695482368\"},\n  {\"0x1.2357684599703p+528\", 159, \"1.0000000000000001235939783819179352336555603321323631774173148044884693350022041002024739567400974580931131118996664970128849288176027116149175428383545271255e+159\"},\n  {\"0x1.6c2d4256ffcc2p+531\", 160, \"9999999999999998504409802292686149877658027252303114244149773213034936348259701329824468100106056975663290938441190205280284556945232082632196709006295628251136\"},\n  {\"0x1.6c2d4256ffcc3p+531\", 160, \"1.000000000000000006528407745068226556845664214888626711844884454552051177783818114251033750998886703581634247018717578519375011764854353035618454865043828139622e+160\"},\n  {\"0x1.c73892ecbfbf3p+534\", 161, \"99999999999999991287595123558845961539774732109363753938694017460291665200910932548988158640591809997245115511395844372456707812265566617217918448639526895091712\"},\n  {\"0x1.c73892ecbfbf4p+534\", 161, \"1.0000000000000000377458932482281488706616365128202897693308658812017626863753877105047511391965429047846952776536372901176443229789205819900982116579266812025242e+161\"},\n  {\"0x1.1c835bd3f7d78p+538\", 162, \"999999999999999937849939638116397466450525159438967985375725315922685858882365002492855496964043060934899979621894213003182527093908649335762989920701551401238528\"},\n  {\"0x1.1c835bd3f7d79p+538\", 162, \"1.00000000000000013764184685833990027487274786620161155328600644648083951386841041851664678142904274863449057568538036723210611886393251464443343339515181100380979e+162\"},\n  {\"0x1.63a432c8f5cd6p+541\", 163, \"9999999999999999378499396381163974664505251594389679853757253159226858588823650024928554969640430609348999796218942130031825270939086493357629899207015514012385280\"},\n  {\"0x1.63a432c8f5cd7p+541\", 163, \"1.000000000000000097683465414295199713188303324849082839703950220369208782871201335311888524536042811094572456472683136386321400509927741582699344700261759083295539e+163\"},\n  {\"0x1.bc8d3f7b3340bp+544\", 164, \"99999999999999987391652932764487656775541389327492204364443535414407668928683046936524228593524316087103098888157864364992697772750101243698844800887746832841572352\"},\n  {\"0x1.bc8d3f7b3340cp+544\", 164, \"1.0000000000000000017833499485879183651456364256030139271070152777012950284778995356204687079928429609987689703622097823564380764603162862345375318325256344740613325e+164\"},\n  {\"0x1.15d847ad00087p+548\", 165, \"999999999999999899489893451833484927233458399740540420336951338855520357125044282616287570346763120896578585177704871391229197474064067196498264773607101557544845312\"},\n  {\"0x1.15d847ad00088p+548\", 165, \"1.00000000000000010407680644534235180305781445146548743387707921654706969983075478862464984563892280110095935554671469332164695544656850527257679889144416739057781965e+165\"},\n  {\"0x1.5b4e5998400a9p+551\", 166, \"9999999999999999404072760505352583023983296100855298230449769143938302256661863838179600254051950569374547392515068357773127490685649548117139715971745147241514401792\"},\n  {\"0x1.5b4e5998400aap+551\", 166, \"1.000000000000000104076806445342351803057814451465487433877079216547069699830754788624649845638922801100959355546714693321646955446568505272576798891444167390577819648e+166\"},\n  {\"0x1.b221effe500d3p+554\", 167, \"99999999999999990767336997157383960226643264180953830087855645396318233083327270285662206135844950810475381599246526426844590779296424471954140613832058419086616428544\"},\n  {\"0x1.b221effe500d4p+554\", 167, \"1.0000000000000000386089942874195144027940205149135043895442382956857739101649274267019739175454317034355575090286315503039132728953670850882316679737363063240072678605e+167\"},\n  {\"0x1.0f5535fef2084p+558\", 168, \"999999999999999933860494834742974562371950216430331518611692822307700646699603647625692432595845947170914554599698521475539380813444812793279458505403728617494385000448\"},\n  {\"0x1.0f5535fef2085p+558\", 168, \"1.00000000000000014335749374009605424321609081339667726047678376906384717363025120577825540249501745970020046345756457913228716497728935738318387744206888403052015072051e+168\"},\n  {\"0x1.532a837eae8a5p+561\", 169, \"9999999999999999338604948347429745623719502164303315186116928223077006466996036476256924325958459471709145545996985214755393808134448127932794585054037286174943850004480\"},\n  {\"0x1.532a837eae8a6p+561\", 169, \"1.000000000000000101458093959025438307047262694003408112103765579712617868244121694147742808515183157194343281685991367600937608144520448465202993654735852947914997576499e+169\"},\n  {\"0x1.a7f5245e5a2cep+564\", 170, \"99999999999999990034097500988648181343688772091571619991327827082671720239070003832128235741197850516622880918243995225045973534722968565889475147553730375141026248523776\"},\n  {\"0x1.a7f5245e5a2cfp+564\", 170, \"1.0000000000000000344190543093124528091771377029741774747069364767506509796263144755389226581474482731849717908514742291507783172120901941964335795950030032157467525460787e+170\"},\n  {\"0x1.08f936baf85c1p+568\", 171, \"999999999999999953972206729656870211732987713739100709830741553196290713284945813208338477706166412373726001850053663010587168093173889073910282723323583537144858509574144\"},\n  {\"0x1.08f936baf85c2p+568\", 171, \"1.00000000000000016849713360873842380491738768503263874950059468267458475686192891275656295888291804120371477252050850605109689907695070273397240771446870268008324260691968e+171\"},\n  {\"0x1.4b378469b6731p+571\", 172, \"9999999999999999110672213538405594930961077194803931018967709273006319045695491932986935814708160866077282477159626944024852218964185263418978577250945597085571816901050368\"},\n  {\"0x1.4b378469b6732p+571\", 172, \"1.000000000000000082687162857105802367643627696515223533632653430883267139431135672937273166412217389671719264252326568834893006683439977269947557718010655022907888967981466e+172\"},\n  {\"0x1.9e056584240fdp+574\", 173, \"99999999999999987674323305318751091818660372407342701554959442658410485759723189737097766448253582599493004440868991951600366493901423615628791772651134064568704023452975104\"},\n  {\"0x1.9e056584240fep+574\", 173, \"1.0000000000000000140391862557997052178246197057012913609383004294502130454865010810818413324356568684461228576377810190619298927686313968987276777208442168971676060568308941e+173\"},\n  {\"0x1.02c35f729689ep+578\", 174, \"999999999999999849284042412665072058259000527747854146471853226010883220019378060628804930891911617504691481762871699606818419373090804007799965727644765395390927070069522432\"},\n  {\"0x1.02c35f729689fp+578\", 174, \"1.0000000000000000689575675368445829376798260983524370990937828305966563206422087545661867996169052854265999829294174588803003839004782611957035817185773673977598323857513513e+174\"},\n  {\"0x1.4374374f3c2c6p+581\", 175, \"9999999999999999371534524623368764100273307559896873275206250678451924602685103382037576783819090846734548822294900033162112051840457868829614121240178061963384891963422539776\"},\n  {\"0x1.4374374f3c2c7p+581\", 175, \"1.000000000000000112892272561680485113563991212473353689618168751513810940766774893353663173361904019010981683162726610734996776805955752633284304916763887798233613448887717069e+175\"},\n  {\"0x1.945145230b377p+584\", 176, \"99999999999999986685792442259943292861266657339622078268160759437774506806920451614379548038991111093844416185619536034869697653528180058283225500691937355558043949532406874112\"},\n  {\"0x1.945145230b378p+584\", 176, \"1.0000000000000000074489805020743198914419949385831538723596425413126398524678161602637198763739070584084656026027846462837254338328097731830905692411162388370965388973604392141e+176\"},\n  {\"0x1.f965966bce055p+587\", 177, \"999999999999999894976135638494410321178532246433607400617214583764724024948926844967780359586710300432448450005513217535702667994787395102883917853758746611883659375731342835712\"},\n  {\"0x1.f965966bce056p+587\", 177, \"1.00000000000000000744898050207431989144199493858315387235964254131263985246781616026371987637390705840846560260278464628372543383280977318309056924111623883709653889736043921408e+177\"},\n  {\"0x1.3bdf7e0360c35p+591\", 178, \"9999999999999998724815666657784284071258397080036981062687289922551408594451489819085924562292709488372450194860589317860981148271829194868425875762872481668410834714055235600384\"},\n  {\"0x1.3bdf7e0360c36p+591\", 178, \"1.000000000000000052438118447506283719547380015442972461056613724331806183475371886382095683088785761598872463641693217782934540168018724415173229796059235727181690706012077765427e+178\"},\n  {\"0x1.8ad75d8438f43p+594\", 179, \"99999999999999998045549773481514159457876389246726271914145983150114005386328272459269439234497983649422148597943950338419997003168440244384097290815044070304544781216945608327168\"},\n  {\"0x1.8ad75d8438f44p+594\", 179, \"1.0000000000000001244207391601974258445159961384186822029717676171624723130874610481714969738325916867035234413039469321816691103043530463865054866839680307513179335998546994475827e+179\"},\n  {\"0x1.ed8d34e547313p+597\", 180, \"999999999999999894076352879585771044616424544896411028843275160104340698328775730445412843452412726368640312784735046105718485868083216078242264642659886674081956339558310064685056\"},\n  {\"0x1.ed8d34e547314p+597\", 180, \"1.00000000000000000924854601989159844456621034165754661590752138863340650570811838930845490864250220653608187704434098914369379808621813123237387566331395871269994496970650475613389e+180\"},\n  {\"0x1.3478410f4c7ecp+601\", 181, \"9999999999999999171107915076469365246063817042486381462561244058101538598046442622180212564904306224021286256366562347133135483117101991090685868467907010818055540655879490029748224\"},\n  {\"0x1.3478410f4c7edp+601\", 181, \"1.000000000000000101386300532136260364526038979066455085558918371456659151611592516398888560794573790670035128452025743574074047860726063355679164479837216343594335873825060509292954e+181\"},\n  {\"0x1.819651531f9e7p+604\", 182, \"99999999999999991711079150764693652460638170424863814625612440581015385980464426221802125649043062240212862563665623471331354831171019910906858684679070108180555406558794900297482240\"},\n  {\"0x1.819651531f9e8p+604\", 182, \"1.0000000000000000645311987272383955965421075241028916976983595783273580932502028655627150999337451570164538278889518418019219479509228905063570489532279132912365795121776382080293274e+182\"},\n  {\"0x1.e1fbe5a7e7861p+607\", 183, \"999999999999999946594872951565228338993526868219488856544571440313594706493755982886960025179093529324993666087115356131035228239552737388526279268078143523691759154905886843985723392\"},\n  {\"0x1.e1fbe5a7e7862p+607\", 183, \"1.00000000000000006453119872723839559654210752410289169769835957832735809325020286556271509993374515701645382788895184180192194795092289050635704895322791329123657951217763820802932736e+183\"},\n  {\"0x1.2d3d6f88f0b3cp+611\", 184, \"9999999999999998286585471758920610814449462123360860153907833022998313197373091002112049504244419016335335042852788704601485085281825842706955095829283737561469387976341354799421194240\"},\n  {\"0x1.2d3d6f88f0b3dp+611\", 184, \"1.000000000000000017356668416969128693522675261749530561236844323121852738547624112492413070031884505939869763168217247533567260066374829259224741079168005384218651369268937662411885773e+184\"},\n  {\"0x1.788ccb6b2ce0cp+614\", 185, \"99999999999999997961704416875371517110712945186684165206763211895744845478556111003617144611039598507860251139162957211888350975873638026151889477992007905860430885494197722591793250304\"},\n  {\"0x1.788ccb6b2ce0dp+614\", 185, \"1.0000000000000001305755411616153692607693126913975972887444809356150655898338131198611379417963500685236715184979802737776185109892901762523422799769117843610616789122498189718937455821e+185\"},\n  {\"0x1.d6affe45f818fp+617\", 186, \"999999999999999979617044168753715171107129451866841652067632118957448454785561110036171446110395985078602511391629572118883509758736380261518894779920079058604308854941977225917932503040\"},\n  {\"0x1.d6affe45f8190p+617\", 186, \"1.00000000000000010038384176304303844283687604349144616140911117228354216282416271789614464265915925183465771707671013344587151074317941705417760293751344330057020490078825062269858296627e+186\"},\n  {\"0x1.262dfeebbb0f9p+621\", 187, \"9999999999999999071569656121801212080692814968920789464627446869617922299624001453201875281811380250249693879805812353226907091680705581859236698853640605134247712274342131878495422251008\"},\n  {\"0x1.262dfeebbb0fap+621\", 187, \"1.000000000000000100383841763043038442836876043491446161409111172283542162824162717896144642659159251834657717076710133445871510743179417054177602937513443300570204900788250622698582966272e+187\"},\n  {\"0x1.6fb97ea6a9d37p+624\", 188, \"99999999999999986851159038200753776111576258757220550347347138989744224339004763080499610528553377966303172216135545569805454885304878641227288327493418395599568449276340570087973407686656\"},\n  {\"0x1.6fb97ea6a9d38p+624\", 188, \"1.0000000000000000230930913026978715489298382248516992754305645781548421896794576888657617968679507611107823854382585741965991901131358735068760297166536901857120314314466356487589666698035e+188\"},\n  {\"0x1.cba7de5054485p+627\", 189, \"999999999999999899427890566145604518678577715028104257864890027548922232647929642417149243602017175952581854816736079397763477105066203831193512563278085201938953880500051690455580595453952\"},\n  {\"0x1.cba7de5054486p+627\", 189, \"1.00000000000000002309309130269787154892983822485169927543056457815484218967945768886576179686795076111078238543825857419659919011313587350687602971665369018571203143144663564875896666980352e+189\"},\n  {\"0x1.1f48eaf234ad3p+631\", 190, \"9999999999999998746948504188351511126283256130633852543517551174277382412416240331274267329488304589209417486924315804379963345034522698960570091326029642051843383703107348987949033805840384\"},\n  {\"0x1.1f48eaf234ad4p+631\", 190, \"1.000000000000000072559171597318778361030342428781137282456834398397210172492068907445206818174324195174062597686867572116133475316363741377149036578003932179221262451825269232080321099543347e+190\"},\n  {\"0x1.671b25aec1d88p+634\", 191, \"99999999999999991426771465453187656230872897620693565997277097362163262749171300799098274999392920617156591849131877877362376266603456419227541462168315779999172318661364176545198692437590016\"},\n  {\"0x1.671b25aec1d89p+634\", 191, \"1.0000000000000000725591715973187783610303424287811372824568343983972101724920689074452068181743241951740625976868675721161334753163637413771490365780039321792212624518252692320803210995433472e+191\"},\n  {\"0x1.c0e1ef1a724eap+637\", 192, \"999999999999999914267714654531876562308728976206935659972770973621632627491713007990982749993929206171565918491318778773623762666034564192275414621683157799991723186613641765451986924375900160\"},\n  {\"0x1.c0e1ef1a724ebp+637\", 192, \"1.00000000000000004090088020876139800128601973826629695796002171344209466349199772755436200453824519737356326184775781344763153278629790594017431218673977730337535459878294373875465426450985779e+192\"},\n  {\"0x1.188d357087712p+641\", 193, \"9999999999999998636144484328400679867178126713831911407778706776934478130915991201656310481762028096907669811487431649040206546179292274931158555956605099986382706217459209761309199883223171072\"},\n  {\"0x1.188d357087713p+641\", 193, \"1.000000000000000066227513319607302289081477890678169217557471861406187070692054671467037855447108395613962730519045620382433086810350574289754091699751101204052080881216804133415187732536649318e+193\"},\n  {\"0x1.5eb082cca94d7p+644\", 194, \"99999999999999994465967438754696170766327875910118237148971115117854351613178134068619377108456504406004528089686414709538562749489776621177115003729674648080379472553427423904462708600804999168\"},\n  {\"0x1.5eb082cca94d8p+644\", 194, \"1.0000000000000001067501262969607491495542109345371648329133920981487349222121457817273192169012895127986018803931061114781155732488348436490817389205692194451348429331109807648720412813795157606e+194\"},\n  {\"0x1.b65ca37fd3a0dp+647\", 195, \"999999999999999977077764769429719196041465194188378863774447340572581797347854228894418860247909937807756600796112539971931616645685181699233267813951241073670004367049615544210109925082343145472\"},\n  {\"0x1.b65ca37fd3a0ep+647\", 195, \"1.00000000000000010675012629696074914955421093453716483291339209814873492221214578172731921690128951279860188039310611147811557324883484364908173892056921944513484293311098076487204128137951576064e+195\"},\n  {\"0x1.11f9e62fe4448p+651\", 196, \"9999999999999999511432924639235132053389160461186216699466583890573511723749959183278387889172340228095875448767138256706948253250552493092635735926276453993770366538373425000777236538229086224384\"},\n  {\"0x1.11f9e62fe4449p+651\", 196, \"1.000000000000000158619070907973161130959309230676679220568970001179196172157862402860479359562641342794939992231903540080589155890094708429021127363216410793720778359535526853136813823898384806707e+196\"},\n  {\"0x1.56785fbbdd55ap+654\", 197, \"99999999999999995114329246392351320533891604611862166994665838905735117237499591832783878891723402280958754487671382567069482532505524930926357359262764539937703665383734250007772365382290862243840\"},\n  {\"0x1.56785fbbdd55bp+654\", 197, \"1.0000000000000001171239152191632315458352305937650677104445076787548271722012891059539512454335598787978695027608655971986102897770868166050696166090986577148520300183958899825249957898832895698534e+197\"},\n  {\"0x1.ac1677aad4ab0p+657\", 198, \"999999999999999884751043361827625869140390227060043253747518673178360772444478643277393806310703680414274761723053117059528639544242622390941156386039240473187039308013923507098814799398756243472384\"},\n  {\"0x1.ac1677aad4ab1p+657\", 198, \"1.00000000000000001753554156601940054153744186517720008614579810493634157230551319337828377152376436520490032803037453428186101110586787622758599079921605032556703399966076149305663250824706100140442e+198\"},\n  {\"0x1.0b8e0acac4eaep+661\", 199, \"9999999999999998847510433618276258691403902270600432537475186731783607724444786432773938063107036804142747617230531170595286395442426223909411563860392404731870393080139235070988147993987562434723840\"},\n  {\"0x1.0b8e0acac4eafp+661\", 199, \"1.000000000000000097206240488534465344975672848047494185584765763991130052222133923438817750651600776079275667814767384615260434042843028529572891447122136236995030814648864284631323133556043856163635e+199\"},\n  {\"0x1.4e718d7d7625ap+664\", 200, \"99999999999999996973312221251036165947450327545502362648241750950346848435554075534196338404706251868027512415973882408182135734368278484639385041047239877871023591066789981811181813306167128854888448\"},\n  {\"0x1.4e718d7d7625bp+664\", 200, \"1.0000000000000001396972799138758332401427293722449843719522151821536839081776649794711025395197801952122758490331102381264067929425631097572992384593387153897566291159758524401378248003875013787018854e+200\"},\n  {\"0x1.a20df0dcd3af0p+667\", 201, \"999999999999999901747459131964173027207212836739039328294498440443382314826691065690307721857975448067474834210390258463987183104130654882031695190925872134291678628544718769301415466131339252487684096\"},\n  {\"0x1.a20df0dcd3af1p+667\", 201, \"1.00000000000000003771878529305655029174179371417100792467033657856355465388439044499361904623614958929307541410908738969965553158323491481075600563001892542312879319279108086692222079999200332461008486e+201\"},\n  {\"0x1.0548b68a044d6p+671\", 202, \"9999999999999999017474591319641730272072128367390393282944984404433823148266910656903077218579754480674748342103902584639871831041306548820316951909258721342916786285447187693014154661313392524876840960\"},\n  {\"0x1.0548b68a044d7p+671\", 202, \"1.000000000000000119301580989711976650462542240630189082495839461435658057319010072575605840863054074028435762048305668441056540670697470767990591893474757396431061931338898125494704000308401767883525325e+202\"},\n  {\"0x1.469ae42c8560cp+674\", 203, \"99999999999999998876910787506329447650934459829549922997503484884029261182361866844442696946000689845185920534555642245481492613075738123641525387194542623914743194966239051177873087980216425864602058752\"},\n  {\"0x1.469ae42c8560dp+674\", 203, \"1.0000000000000001628124053612615373751136081214084190333361076656341132058174738739526654646640697992206279476158887504364704121840108339451823712339845344488589385918977339967333617071438142709626935706e+203\"},\n  {\"0x1.98419d37a6b8fp+677\", 204, \"999999999999999988769107875063294476509344598295499229975034848840292611823618668444426969460006898451859205345556422454814926130757381236415253871945426239147431949662390511778730879802164258646020587520\"},\n  {\"0x1.98419d37a6b90p+677\", 204, \"1.00000000000000012800374586402188879539275541678583507266389310227534908701870283285101776562325721906687419916182228484013931497336014340342894776157671280691663726345066529974243554167548426849935897395e+204\"},\n  {\"0x1.fe52048590672p+680\", 205, \"9999999999999999052283250816881378851792981072012977243617198967792587267065681698004724917620567060828502090557969050236202928251957239362070375381666542984859087613894256390005080826781722527340175556608\"},\n  {\"0x1.fe52048590673p+680\", 205, \"1.000000000000000016616035472855013340286026761993566398512806499527303906862635501325745128692656962574862204108809594931879803899277933669817992649871683552701273012420045469371471812176828260616688264806e+205\"},\n  {\"0x1.3ef342d37a407p+684\", 206, \"99999999999999986067324092522138770313660664528439025470128525568004065464414123719036343698981660348604541103459182906031648839556284004276265549348464259679976306097717770685212259087870984958094927200256\"},\n  {\"0x1.3ef342d37a408p+684\", 206, \"1.0000000000000000388935775510883884313073724929520201333430238200769129428938489676307996560787770138732646031194121329135317061140943756165401836722126894035443458626261694354456645580765594621932224066355e+206\"},\n  {\"0x1.8eb0138858d09p+687\", 207, \"999999999999999896317308250394787848770759814817916230429632968559415112294082783278450680807608685563489249451555889830959531939269147157518161129230251958148679621306976052570830984318279772103403898929152\"},\n  {\"0x1.8eb0138858d0ap+687\", 207, \"1.00000000000000003889357755108838843130737249295202013334302382007691294289384896763079965607877701387326460311941213291353170611409437561654018367221268940354434586262616943544566455807655946219322240663552e+207\"},\n  {\"0x1.f25c186a6f04cp+690\", 208, \"9999999999999999818630698308109481982927274216983785721776674794699138106539424938898600659703096825493544616522696356805028364441642842329313746550197144253860793660984920822957311285732475861572950035529728\"},\n  {\"0x1.f25c186a6f04dp+690\", 208, \"1.000000000000000095924085271365828664322017564205661694508380160683912075133755441371739246187244345197174744586554630146560575784024467000148992689405664381702612359153846788595597987579871338229149809718067e+208\"},\n  {\"0x1.37798f428562fp+694\", 209, \"99999999999999989061425747836704382546929530769255207431309733449871519907009213590435672179676195243109823530484164010765664497227613801915728022751095446033285297165420831725583764136794858449981115862089728\"},\n  {\"0x1.37798f4285630p+694\", 209, \"1.0000000000000000731118821832548525711161595357042050700422376244411124222377928518753634101438574126676106879996976312533490279160524304467054690825284743904393057605427758473356246157785465878147788484850483e+209\"},\n  {\"0x1.8557f31326bbbp+697\", 210, \"999999999999999927113782419344605574598668153294882673458925392487194643703632279098558059466181044478400725843812838336795121561031396504666917998514458446354143529431921823271795036250068185162804696593727488\"},\n  {\"0x1.8557f31326bbcp+697\", 210, \"1.00000000000000007311188218325485257111615953570420507004223762444111242223779285187536341014385741266761068799969763125334902791605243044670546908252847439043930576054277584733562461577854658781477884848504832e+210\"},\n  {\"0x1.e6adefd7f06aap+700\", 211, \"9999999999999999563134023721266549739021664297767471527755878388779781994104643936539191296017163181162427182749897969201059028320356032930746282153172616351711759756540926280845609521557638656931995269719916544\"},\n  {\"0x1.e6adefd7f06abp+700\", 211, \"1.00000000000000007311188218325485257111615953570420507004223762444111242223779285187536341014385741266761068799969763125334902791605243044670546908252847439043930576054277584733562461577854658781477884848504832e+211\"},\n  {\"0x1.302cb5e6f642ap+704\", 212, \"99999999999999990959401044767537593501656918740576398586892792465272451027953301036534141738485988029569553038510666318680865279842887243162229186843277653306392406169861934038413548670665077684456779836676898816\"},\n  {\"0x1.302cb5e6f642bp+704\", 212, \"1.0000000000000000964715781454804920905589581568896966534955675815537392668032585435196522662522856315778842819446391981199976529328557958774316372559707169414929317175205124911858373485031031322390947127876596531e+212\"},\n  {\"0x1.7c37e360b3d35p+707\", 213, \"999999999999999984345037526797422397233524775199337052919583787413130412889023223627065756931830180808571031008919677160084252852199641809946030023447952696435527124027376600704816231425231719002378564135125254144\"},\n  {\"0x1.7c37e360b3d36p+707\", 213, \"1.00000000000000013384709168504151532166743595078648318702089551293394221810800365015051443602577078183432203225654570510663545295974118056659350633347830502317873324868489112134617772086239360331800009567183778611e+213\"},\n  {\"0x1.db45dc38e0c82p+710\", 214, \"9999999999999999544446266951486038123467425400819078260993214423089680518452271383223760211130420606034208307593944715707740128306913340586165347614418822310868858990958736965765439335377993421392542578277827477504\"},\n  {\"0x1.db45dc38e0c83p+710\", 214, \"1.000000000000000074046270021743878151893871480551624733380370822725617496020411479541134964388194541424021631757495293928014972916724565063934515809466164092481450798821885313089633125087528849591751483057152773325e+214\"},\n  {\"0x1.290ba9a38c7d1p+714\", 215, \"99999999999999990660396936451049407652789096389402106318690169014230827417515340183487244380298106827518051036015414262787762879627804165648934234223216948652905993920546904997130825691790753915825536773603473752064\"},\n  {\"0x1.290ba9a38c7d2p+714\", 215, \"1.0000000000000000979665986870629330198032972686455681148365806988089473848554483477848867530432250375881417919571154583994631649339312112649981120190710204647603637787670876363922509633974747510822509281030267784397e+215\"},\n  {\"0x1.734e940c6f9c5p+717\", 216, \"999999999999999868331443500000006287872809702943711652856965888408980452039094412644869581954932274412588254040761879473560521568747407734787588406864399290882799171293145332687119715621994096773456255662636329336832\"},\n  {\"0x1.734e940c6f9c6p+717\", 216, \"1.00000000000000002142154695804195744249313474674494929417670909534229174058333036940488102934712744986295727931833093209082895047886994342159460414833548007346784224294244020182387388080564786631265270395622996207206e+216\"},\n  {\"0x1.d022390f8b837p+720\", 217, \"9999999999999999601855055748251769806450047292244542376488118125689672251656359867008764503902493796828096692073033110439215789148209291468717978517470477604338250142827222541691722147321863584969741246387925089779712\"},\n  {\"0x1.d022390f8b838p+720\", 217, \"1.000000000000000082657588341258737904341264764265444350704606378115616256001024752108885608304005520043104889429358553137736322042918957696317410444923912386501859471602158149478575546879109374128331283273667415166157e+217\"},\n  {\"0x1.221563a9b7322p+724\", 218, \"99999999999999988670225591496504042642724870819986016981533507324097780666440272745607095564199569546663253707407016578763273303796211201720443029584092898479300433989106071698353021544403254911815982945786756526505984\"},\n  {\"0x1.221563a9b7323p+724\", 218, \"1.0000000000000000826575883412587379043412647642654443507046063781156162560010247521088856083040055200431048894293585531377363220429189576963174104449239123865018594716021581494785755468791093741283312832736674151661568e+218\"},\n  {\"0x1.6a9abc9424febp+727\", 219, \"999999999999999965084388885482519417592855130626093842171043595190833186399051537317196816706799625297221478016185520727674168639944850288849622355474122345476546392575499689981548348018063279122228410984187505225498624\"},\n  {\"0x1.6a9abc9424fecp+727\", 219, \"1.00000000000000012184865482651747739992406797547856118688246063909054394586834915703944853883640748495839935990041623060775703984391032683214000647474050906684363049794437763597758461316612473913036557403682738514637619e+219\"},\n  {\"0x1.c5416bb92e3e6p+730\", 220, \"9999999999999999964372420736895110140590976995965873111133270039707753382929110612616471611327211972294570543930316627036907428807379455975076991793273996897499632136492752791807556010476755711238558435947154812096741376\"},\n  {\"0x1.c5416bb92e3e7p+730\", 220, \"1.000000000000000121848654826517477399924067975478561186882460639090543945868349157039448538836407484958399359900416230607757039843910326832140006474740509066843630497944377635977584613166124739130365574036827385146376192e+220\"},\n  {\"0x1.1b48e353bce6fp+734\", 221, \"99999999999999984594354677029595135102113336853821866019036664182705300920238534632828550788829765195472628778417018121881118652493108811594893042483166843723756247249515245102456078650553656951604416706418119648563167232\"},\n  {\"0x1.1b48e353bce70p+734\", 221, \"1.0000000000000000466018071748206975684050858099493768614209804580186827813230862995727677122141957123210339765959854898653172616660068980913606220974926434405874301273673162218994872058950552383264597357715602427843549594e+221\"},\n  {\"0x1.621b1c28ac20bp+737\", 222, \"999999999999999886075198851200900594497923856820450300436489405065378963626525536977181948753477264027987825546533242948112401553146250111031268759363863437907536003469585205199546070383440303278127280805657005745376329728\"},\n  {\"0x1.621b1c28ac20cp+737\", 222, \"1.00000000000000004660180717482069756840508580994937686142098045801868278132308629957276771221419571232103397659598548986531726166600689809136062209749264344058743012736731622189948720589505523832645973577156024278435495936e+222\"},\n  {\"0x1.baa1e332d728ep+740\", 223, \"9999999999999999181805205159248599892793562474462356126333876156560397271658376894962991014456209536865970557564236923315533735757183797070971394269896194384435148282491314085395342974857632902877937717988376531531720556544\"},\n  {\"0x1.baa1e332d728fp+740\", 223, \"1.00000000000000004660180717482069756840508580994937686142098045801868278132308629957276771221419571232103397659598548986531726166600689809136062209749264344058743012736731622189948720589505523832645973577156024278435495936e+223\"},\n  {\"0x1.14a52dffc6799p+744\", 224, \"99999999999999996954903517948319502092964807244749211214842475260109694882873713352688654575305085714037182409224841134505892881183378706080253249519082903930108094789640533388351546084948006950326015738792668900564521713664\"},\n  {\"0x1.14a52dffc679ap+744\", 224, \"1.0000000000000001750230938337165351475308153724525181102085733003813258354803349096492363229827704709554708974355472873990811497562954164756241047679956674427313454264855010352594401143043471863651256997442828324155378630656e+224\"},\n  {\"0x1.59ce797fb817fp+747\", 225, \"999999999999999928454223448636526995609414612446486912536395043045051171498417578302416590307106934377352009423588636134254484622941461177838218040629861358615028052178586193608330530158506646130887048916655460323666687950848\"},\n  {\"0x1.59ce797fb8180p+747\", 225, \"1.00000000000000009283347037202319909689034845245050771098451388126923428081969579920029641209088262542943126809822773697747226137851076470969547585887373208135923963504986275470907025292240033962037948280174037505158080469402e+225\"},\n  {\"0x1.b04217dfa61dfp+750\", 226, \"9999999999999999613300728333138614158656013804472910722260188106898877933626732224819925546638620725877678611585164563028980399740553218842096696042786355031638703687528415058284784747112853848287855356936724432692495112994816\"},\n  {\"0x1.b04217dfa61e0p+750\", 226, \"1.000000000000000092833470372023199096890348452450507710984513881269234280819695799200296412090882625429431268098227736977472261378510764709695475858873732081359239635049862754709070252922400339620379482801740375051580804694016e+226\"},\n  {\"0x1.0e294eebc7d2bp+754\", 227, \"99999999999999988242803431008825880725075313724536108897092176834227990088845967645101024020764974088276981699468968789815350713138205618891818585152157755624664880897462875650012340778461641195382916742883168419985073526276096\"},\n  {\"0x1.0e294eebc7d2cp+754\", 227, \"1.000000000000000092833470372023199096890348452450507710984513881269234280819695799200296412090882625429431268098227736977472261378510764709695475858873732081359239635049862754709070252922400339620379482801740375051580804694016e+227\"},\n  {\"0x1.51b3a2a6b9c76p+757\", 228, \"999999999999999924509121522475246865178672200286390413373640190927670776874706901000867474584296317792102107215397297714017257980807797893073643852992008461269166974189675556141912776812173197487139230503413422370196749149011968\"},\n  {\"0x1.51b3a2a6b9c77p+757\", 228, \"1.000000000000000092833470372023199096890348452450507710984513881269234280819695799200296412090882625429431268098227736977472261378510764709695475858873732081359239635049862754709070252922400339620379482801740375051580804694016e+228\"},\n  {\"0x1.a6208b5068394p+760\", 229, \"9999999999999999918388610622944277578633427011520373324179896670642961784527024602806390495869308408470337715685294734193992593398889846197223766553446979093051960385337504355687757672562640543404353314227442034427503713670135808\"},\n  {\"0x1.a6208b5068395p+760\", 229, \"1.000000000000000126498340141932789543232683702883331170506688619337546981608693578840182199592199886956897100274793824830163262058051358073019842260050076805377254167221900194422501748144445768047027533261405765587857615803016806e+229\"},\n  {\"0x1.07d457124123cp+764\", 230, \"99999999999999988411127779858373832956786989976700226194703050524569553592790956543300452958271560395914310860351799229078805716535908585708440417158039479244754953558323062848579498254571868337516156995181495372666457581821100032\"},\n  {\"0x1.07d457124123dp+764\", 230, \"1.0000000000000000995664443260051171861588155025370724028889488288828968209774953551282735695911460777349244345335409545480104615144188833823603491391090010261628425414842702426517565519668094253057090928936734531588361669158161613e+230\"},\n  {\"0x1.49c96cd6d16cbp+767\", 231, \"999999999999999884111277798583738329567869899767002261947030505245695535927909565433004529582715603959143108603517992290788057165359085857084404171580394792447549535583230628485794982545718683375161569951814953726664575818211000320\"},\n  {\"0x1.49c96cd6d16ccp+767\", 231, \"1.00000000000000005647541102052084141484062638198305837470056516415545656396757819718921976158945998297976816934753636209656598064460692387730516014560327977941978394030406231981856423808259127691959958830530175327240184869629512909e+231\"},\n  {\"0x1.9c3bc80c85c7ep+770\", 232, \"9999999999999999185841044429711589466224211962102134844977374370276477415358432917842475759840644797632681207523216662519436418612086534611285553663849717898419964165273969667523488336530932020840491736225123136358120303938278260736\"},\n  {\"0x1.9c3bc80c85c7fp+770\", 232, \"1.000000000000000056475411020520841414840626381983058374700565164155456563967578197189219761589459982979768169347536362096565980644606923877305160145603279779419783940304062319818564238082591276919599588305301753272401848696295129088e+232\"},\n  {\"0x1.01a55d07d39cfp+774\", 233, \"99999999999999997374062707399103193390970327051935144057886852787877127050853725394623645022622268104986814019040754458979257737456796162759919727807229498567311142603806310797883499542489243201826933949562808949044795771481474727936\"},\n  {\"0x1.01a55d07d39d0p+774\", 233, \"1.0000000000000001943667175980705238830588315677559032649033928912832653863993131025941919471948554861962682179427510579411883194280051942934817649248215877689975714640807276728847796425120893517551500029880911929089916669987624321024e+233\"},\n  {\"0x1.420eb449c8842p+777\", 234, \"999999999999999841364972759543336764420226292177420345984153909836074800974071744757463152045042997962028093539001436578955132142505622028069656690022719315678435403212464369035268207172574280176140941400150227439321732144446136385536\"},\n  {\"0x1.420eb449c8843p+777\", 234, \"1.00000000000000001786584517880693032373952892996666180544377340055967009368669242367582754961994924207914815574087624726007172578525540816077571080742215354233800343364659602096002392484233181596564547219412071017415669957160428424397e+234\"},\n  {\"0x1.9292615c3aa53p+780\", 235, \"9999999999999999119653217272487741881479473472931169297680017061255129180591200163248089110750054956088761184197513608514017695996055364811520783369824930063422626153861170298051704942404772944919427537177384205332557191153093955289088\"},\n  {\"0x1.9292615c3aa54p+780\", 235, \"1.000000000000000053166019662659649035603389457524510097335697298704389152229216559459500429134930490902572168181251209396295044513805365387316921630902040387669917039733422351344975068376283323123546378352914806721123693057035913815654e+235\"},\n  {\"0x1.f736f9b3494e8p+783\", 236, \"99999999999999994020546131433094915763903576933939556328154082464128816489313932495174721468699049466761532837205133056038042458244550226238504699576640248260779350025557809411313140906763850021826347864477369777082931390365469918625792\"},\n  {\"0x1.f736f9b3494e9p+783\", 236, \"1.0000000000000000531660196626596490356033894575245100973356972987043891522292165594595004291349304909025721681812512093962950445138053653873169216309020403876699170397334223513449750683762833231235463783529148067211236930570359138156544e+236\"},\n  {\"0x1.3a825c100dd11p+787\", 237, \"999999999999999940205461314330949157639035769339395563281540824641288164893139324951747214686990494667615328372051330560380424582445502262385046995766402482607793500255578094113131409067638500218263478644773697770829313903654699186257920\"},\n  {\"0x1.3a825c100dd12p+787\", 237, \"1.00000000000000012094235467165686896238200167043557881776819118314224974463086290016415235780369448864354627206677113669784381647262128326227604641198342313070719116342012890568408126396147021686671611817779947209130032054906464259329229e+237\"},\n  {\"0x1.8922f31411455p+790\", 238, \"9999999999999999040580826428657651966904425891201589123842107529410958489455946099092661860636496958724291396331073693328877462044103460624068471125229983529879139676226679317989414380888721568885729507381685429067351125745727105048510464\"},\n  {\"0x1.8922f31411456p+790\", 238, \"1.000000000000000048647597328726501040484815309997105515973531039741865112735773470079190300557012891053173894588883214242858459716550970862319646645496614871467432098154308581055701322003937530207335062364589162363111917890900665230478541e+238\"},\n  {\"0x1.eb6bafd91596bp+793\", 239, \"99999999999999999081179145438220670296706622164632687453780292502155740721970192601122065475966761298087599260657287627887017431169472094235452683230716826407562484594165232135299736843791138087983021771402091458056119576436948334022754304\"},\n  {\"0x1.eb6bafd91596cp+793\", 239, \"1.0000000000000001064834032030707953780025643983478841574092591544621728182518450141471599463543581691254717965711935522068467451214072207822847664586860614788592393503669648407584052755699636795348399070151574101456626400174318471207295386e+239\"},\n  {\"0x1.33234de7ad7e2p+797\", 240, \"999999999999999828871535006218182557917368774264146678517764203804695831774701602620905646527100834378441867056103929979702975178097221166452191355376717763378564539746214794185426298453038162762816652692429820789419173810082174047524749312\"},\n  {\"0x1.33234de7ad7e3p+797\", 240, \"1.00000000000000001394611380411992443797416585698663833111209417090968048942613054363840851307860572420979515339949701146446548847363722091034057475758294690703234774682671482523407894986432184061083215557424821369358148461498195609632794214e+240\"},\n  {\"0x1.7fec216198ddbp+800\", 241, \"9999999999999999029013665253788793099400876073531433395554961906466896948352731790279067931477027903109831815934611625736079804963132210640075447162592094208400778225784148066048873590175516339020228538451571779510840981320420868670460264448\"},\n  {\"0x1.7fec216198ddcp+800\", 241, \"1.00000000000000005096102956370027281398552527353113666163096016433067742095641633184190908638890670217606581066817562776141799113274522085911825143802419273576310438824281483144380948014657857618043525615061189227441394677596191250608858071e+241\"},\n  {\"0x1.dfe729b9ff152p+803\", 242, \"99999999999999993251329913304315801074917514058874200397058898538348724005950180959070725179594357268399970740840405561116998262359962102302968606061220608382468313571129481157267178324335702235770533430624812081575006786082605199485453729792\"},\n  {\"0x1.dfe729b9ff153p+803\", 242, \"1.0000000000000000509610295637002728139855252735311366616309601643306774209564163318419090863889067021760658106681756277614179911327452208591182514380241927357631043882428148314438094801465785761804352561506118922744139467759619125060885807104e+242\"},\n  {\"0x1.2bf07a143f6d3p+807\", 243, \"999999999999999885134206960780312089454635087411784140906440513804611167700736000690226517958758320887173266104495426751070779219941381088594259909647411423049314634698686803624216704482068400828613365568502612232284516294771707790360919932928\"},\n  {\"0x1.2bf07a143f6d4p+807\", 243, \"1.0000000000000000746505756498316957746327953001196155931630344001201154571357992362921494533074993280744790313201299421914675928345743408263359645135065900661507886387491188354180370195272228869449812405194846465661467225589890846083353893929e+243\"},\n  {\"0x1.76ec98994f488p+810\", 244, \"9999999999999999230374806985905888264902671299533504313577592910677120255877486478106111050285065223246344191476223298391501419428679730361426008304192471516696094355087732099829807674910992980518869405586990190990569575476151831539558138249216\"},\n  {\"0x1.76ec98994f489p+810\", 244, \"1.000000000000000074650575649831695774632795300119615593163034400120115457135799236292149453307499328074479031320129942191467592834574340826335964513506590066150788638749118835418037019527222886944981240519484646566146722558989084608335389392896e+244\"},\n  {\"0x1.d4a7bebfa31aap+813\", 245, \"99999999999999992303748069859058882649026712995335043135775929106771202558774864781061110502850652232463441914762232983915014194286797303614260083041924715166960943550877320998298076749109929805188694055869901909905695754761518315395581382492160\"},\n  {\"0x1.d4a7bebfa31abp+813\", 245, \"1.0000000000000000443279566595834743850042896660863625608019793783096347708261891185958417836517007669245101088856284197210041026562330672682972917768891214832545527981010497103310257691199981691663623805273275210727287695567143043174594742793011e+245\"},\n  {\"0x1.24e8d737c5f0ap+817\", 246, \"999999999999999874521290314193434603084658115500145579580071256170942927492372459496518833579228824484684143252419893886408557657521935343280724451831297419035632090471862609843762766839539749606096764571247618309588232743975534688554349643169792\"},\n  {\"0x1.24e8d737c5f0bp+817\", 246, \"1.00000000000000006858605185178205149670709417331296498669082339575801931987387721275288791937633961584448524683322963769737489479890608611472822996618309634957154147061950501040063476944577794338925746852105322146746313195853412855016020637017702e+246\"},\n  {\"0x1.6e230d05b76cdp+820\", 247, \"9999999999999999521471949292288813605336325386252733424243721120057734844449743607990664678980731410286045846847437914107950925140755956518597266575720169912499958425309195700665115678820350271193610461511698595727381924297989722331966923339726848\"},\n  {\"0x1.6e230d05b76cep+820\", 247, \"1.00000000000000010739900415929977487543158138487552886811297382367543459835017816340416173653576177411644546754939158645956816222718291626901773106905345613567872334664903349051200916996702558214588960931101434209903811180144584732248137771557847e+247\"},\n  {\"0x1.c9abd04725480p+823\", 248, \"99999999999999992109683308321470265755404276937522223728665176967184126166393360027804741417053541441103640811181423240104047857145413152842812577527572916236425034170729678597741204746503691611405533351920096306747820855546959721533975525765152768\"},\n  {\"0x1.c9abd04725481p+823\", 248, \"1.0000000000000000452982804672714174694724018463754266578375331390075701527880966423621236290806863208813091144035324684400589343419399880221545293044608804779072323450017879223338101291330293601352781840470765490885181440527870972867675035629361562e+248\"},\n  {\"0x1.1e0b622c774d0p+827\", 249, \"999999999999999921096833083214702657554042769375222237286651769671841261663933600278047414170535414411036408111814232401040478571454131528428125775275729162364250341707296785977412047465036916114055333519200963067478208555469597215339755257651527680\"},\n  {\"0x1.1e0b622c774d1p+827\", 249, \"1.00000000000000011981914889770544635662341729257554931016806196060900748746259446761256935802677686476347273817856341006347000780423150191839037142197197126723302154697848260414764897813382482654801189436380190070114210535117759732962415254610693325e+249\"},\n  {\"0x1.658e3ab795204p+830\", 250, \"9999999999999999210968330832147026575540427693752222372866517696718412616639336002780474141705354144110364081118142324010404785714541315284281257752757291623642503417072967859774120474650369161140553335192009630674782085554695972153397552576515276800\"},\n  {\"0x1.658e3ab795205p+830\", 250, \"1.000000000000000080074685734807297616809542387935483895591779922421574242302862294145664969255528574692985472165213574530984101957676027840397922292632722846259267305924245440513601592000067244461220582194881713174409325992035997306767273088415852134e+250\"},\n  {\"0x1.bef1c9657a685p+833\", 251, \"99999999999999992109683308321470265755404276937522223728665176967184126166393360027804741417053541441103640811181423240104047857145413152842812577527572916236425034170729678597741204746503691611405533351920096306747820855546959721533975525765152768000\"},\n  {\"0x1.bef1c9657a686p+833\", 251, \"1.0000000000000000482791152044887786249584424642234315639307542918716276461750765553721414582385299426365956593545337061049953772804316485780039629891613241094802639130808557096063636830930611787917875324597455631530231025047227172884817695222629872435e+251\"},\n  {\"0x1.17571ddf6c813p+837\", 252, \"999999999999999895660376658959887464073162830405580371957831265231883984761705009259228605356936508765924557863270337660249498829658628118512958332498610172941047627432585001251621720339432063578508893731092043050369229765618973200711352404729235767296\"},\n  {\"0x1.17571ddf6c814p+837\", 252, \"1.00000000000000009915202805299840901192020234216271529458839530075154219997953373740977907586572775392681935985162149558657733676402265539783429787471556208832666934163027927905794433734427088386288041203596340318724106008442396531773857522810757106893e+252\"},\n  {\"0x1.5d2ce55747a18p+840\", 253, \"9999999999999999363587069377675917736425707327570073564839440723358156278052707548893386994586947577981035182609405692455150664165314335743772262409420005560181719702721238568128862437403998276353831973920663150777435958293799716241167969694049028276224\"},\n  {\"0x1.5d2ce55747a19p+840\", 253, \"1.000000000000000099152028052998409011920202342162715294588395300751542199979533737409779075865727753926819359851621495586577336764022655397834297874715562088326669341630279279057944337344270883862880412035963403187241060084423965317738575228107571068928e+253\"},\n  {\"0x1.b4781ead1989ep+843\", 254, \"99999999999999993635870693776759177364257073275700735648394407233581562780527075488933869945869475779810351826094056924551506641653143357437722624094200055601817197027212385681288624374039982763538319739206631507774359582937997162411679696940490282762240\"},\n  {\"0x1.b4781ead1989fp+843\", 254, \"1.0000000000000000665933638299522455642646760202815737069675050550683968855446811409056910005843211547010761915334853103183648826945244110331428835479608497818649698673586481946089327186234966726173809691071839855653415672334151665790142195763670374206669e+254\"},\n  {\"0x1.10cb132c2ff63p+847\", 255, \"999999999999999988452569694641453289891412847766833896677368465428848130901034909295879619908945316559292587569958465674654992927728624557883489163749540246356891129106733591931304833693638565628182306078113383272782784390994049606075766012189756664840192\"},\n  {\"0x1.10cb132c2ff64p+847\", 255, \"1.00000000000000019682802072213689935488678130780614005745106603780097814328409152692204330170994755160404886480603005139121469897251738849190854085497969900771176776444517253240497919350659351759937874082230165605293953863745036153391164218332917201371136e+255\"},\n  {\"0x1.54fdd7f73bf3bp+850\", 256, \"9999999999999998634272990781441856508941917717432502002131499220055701234712009387201814108283439755324388212283155142447191693008553661974684581490114449895439651479036702276471002178058655944454644452316004196046887318431202624493742403095061074555174912\"},\n  {\"0x1.54fdd7f73bf3cp+850\", 256, \"1.000000000000000030127659900140542502890486539774695128832107979903274133377646232821112356269145763568243843017172782817966934136686377344688499501995571998627866456174421380026039705656229556022421593026951037828814135240285311991642941246417639734614426e+256\"},\n  {\"0x1.aa3d4df50af0ap+853\", 257, \"99999999999999989676737124254345702129345072534953918593694153358511092545248999754036759991650433313959982558608696795936872226802156842691246641960827039136074540955782045812288811537593838676085587479067054324951381252255327235782798049688841391133687808\"},\n  {\"0x1.aa3d4df50af0bp+853\", 257, \"1.0000000000000000301276599001405425028904865397746951288321079799032741333776462328211123562691457635682438430171727828179669341366863773446884995019955719986278664561744213800260397056562295560224215930269510378288141352402853119916429412464176397346144256e+257\"},\n  {\"0x1.0a6650b926d66p+857\", 258, \"999999999999999843423255779504622828654636399579476808778874955057845642282427503428069697375447760968142218613652642015929437520555644859802053186653349748453896990911180089361627479263821919056229587496158345417793683435460456504301996197076723582025859072\"},\n  {\"0x1.0a6650b926d67p+857\", 258, \"1.0000000000000000567997176316599595992098937026597263174111412691669067749626774798772613075396740496539726465033899457896865765104193391282437061184730323200812906654977415644066700237122877898747347366742071367446741997838317199184059333963234848992699351e+258\"},\n  {\"0x1.4cffe4e7708c0p+860\", 259, \"9999999999999999287738405203667575368767393208115766122317814807014700953545274940077463414411382764424743897695475635254322931165011225671787143593812227771048544607458046793796444970432082673836316471673778619485458899748089618699435710767754281089234894848\"},\n  {\"0x1.4cffe4e7708c1p+860\", 259, \"1.00000000000000009947501000209102695332094516327577621913759453198871900149872747516709962957251930739113873208133740654443800430839207798193203670483696883440676940041505385941567853260198096403843576650981689501005030305350597260122672083617283716271875031e+259\"},\n  {\"0x1.a03fde214caf0p+863\", 260, \"99999999999999992877384052036675753687673932081157661223178148070147009535452749400774634144113827644247438976954756352543229311650112256717871435938122277710485446074580467937964449704320826738363164716737786194854588997480896186994357107677542810892348948480\"},\n  {\"0x1.a03fde214caf1p+863\", 260, \"1.0000000000000000653347761057461730700321039947829362977564319217312692202698874789352289719462431012014058636189794379406368620700138868989813722357458196229463864124812040234084717254902264247074749426413290883977494204377665704549700908842933553519596981453e+260\"},\n  {\"0x1.0427ead4cfed6p+867\", 261, \"999999999999999928773840520366757536876739320811576612231781480701470095354527494007746341441138276442474389769547563525432293116501122567178714359381222777104854460745804679379644497043208267383631647167377861948545889974808961869943571076775428108923489484800\"},\n  {\"0x1.0427ead4cfed7p+867\", 261, \"1.00000000000000014727133745697382238992532279916575210907122218634914869521910346989171855024930599605676474792863856258975960344212154549806296696156457773045130558352244362982576806255843731910178091992569982426727153871554113560598600276880411169778142334157e+261\"},\n  {\"0x1.4531e58a03e8bp+870\", 262, \"9999999999999998413748417457239315956573059294699064134960051984423986554086971036541574579178711885967582465059111638997013689862529533948250133185078807957662740116351490992011950708371166466963719380640490770210556304785160923755265983999639546733803159420928\"},\n  {\"0x1.4531e58a03e8cp+870\", 262, \"1.000000000000000016172839295009583478096172712153246810967557762960541535300357884361335224964405364288190533033183963151163217246749291739532415400254564758443434909856460259558093923249299888070891356270706646876036149471101831364360543753586901544466663027507e+262\"},\n  {\"0x1.967e5eec84e2ep+873\", 263, \"99999999999999987633444125558106197214507928600657449299031571134602723138702925979559301132717802373504470381136572374999373863835222106376649373485721758830170619127941133127257484131955329497127582170538059099205173427703324017329338747068854404759758535917568\"},\n  {\"0x1.967e5eec84e2fp+873\", 263, \"1.0000000000000000161728392950095834780961727121532468109675577629605415353003578843613352249644053642881905330331839631511632172467492917395324154002545647584434349098564602595580939232492998880708913562707066468760361494711018313643605437535869015444666630275072e+263\"},\n  {\"0x1.fc1df6a7a61bap+876\", 264, \"999999999999999932269800471352470574525516656465243420181212531991832952952360709621889896782068959956303035500093019510461530081711049334072862401016156456358397678710230902586782474091451932211122035531511013345645500354660676649720249983847887046345216426508288\"},\n  {\"0x1.fc1df6a7a61bbp+876\", 264, \"1.0000000000000000441405189028952877792863913973825812745630061732834443960830236092744836676918508323988196988775476110313971129684287058746855997333340341924717806535718700452151977396352492066908144631837718580528330325099155496025739750101665730438404785611735e+264\"},\n  {\"0x1.3d92ba28c7d14p+880\", 265, \"9999999999999998875215130987353436926211667600983082784284950754751883757000955497608523884181562109792963701491111829020872969270239867178277674680890053619130444887655752455354163678739330224192450644706066754627704874925587274685787599733204126473471115726422016\"},\n  {\"0x1.3d92ba28c7d15p+880\", 265, \"1.000000000000000066514662589203851220238566345566048845439364901541766684709156189205002421873807206887323031553038529335584229545772237182808147199797609739694457248544197873740880792744008661586752948714224026994270538940966524193144720015430310243339530988106547e+265\"},\n  {\"0x1.8cf768b2f9c59p+883\", 266, \"99999999999999988752151309873534369262116676009830827842849507547518837570009554976085238841815621097929637014911118290208729692702398671782776746808900536191304448876557524553541636787393302241924506447060667546277048749255872746857875997332041264734711157264220160\"},\n  {\"0x1.8cf768b2f9c5ap+883\", 266, \"1.0000000000000000307160326911101497147150864284725007320371909363284510229073440613161724151826770077057176992722530600488848430220225870898120712534558888641381746965884733480997879077699935337532513718655005566879705286512849648482315280070083307241410471050136781e+266\"},\n  {\"0x1.f03542dfb8370p+886\", 267, \"999999999999999973438224854160227305877518561122823750593712591987145964024444656694044404476868689015149167622996309190165824584023146941018349739309135463248122613459314107074039291811569329219648848907543004197890512187794469896370420793533163493423472892065087488\"},\n  {\"0x1.f03542dfb8371p+886\", 267, \"1.00000000000000008799384052806007212355265429582217771348066928066975608179024346593830042588848532639628623092150981090760386146002202723860579276760264226502822677971763258912553652372841773828685389482345810917805054511477545980009263522048349795485862131796226867e+267\"},\n  {\"0x1.362149cbd3226p+890\", 268, \"9999999999999999734382248541602273058775185611228237505937125919871459640244446566940444044768686890151491676229963091901658245840231469410183497393091354632481226134593141070740392918115693292196488489075430041978905121877944698963704207935331634934234728920650874880\"},\n  {\"0x1.362149cbd3227p+890\", 268, \"1.000000000000000156727209932399979014157735736641790091212843293879322152449722751484854038735455308824968468900617911938066683585621355417158258584578746346096289279472623678356434862878526783727176922373007172166146564870964053742325963876653698631719710373500577382e+268\"},\n  {\"0x1.83a99c3ec7eafp+893\", 269, \"99999999999999990012263082286432662256543169091523721434606031123027548865433341877772055077343404109122144711194766809100548098338386355056238620120129111010885594705399027856108106338478634741663761952135733701058809111452663635798820356028494943810497789949089153024\"},\n  {\"0x1.83a99c3ec7eb0p+893\", 269, \"1.0000000000000000467538188854561279891896054313304102868413648727440164393945558946103682581803033369390768881340449502893261681846624303314743132774169798163873892798646379355869975202383523110226600782937286713851929332610623034347526380267813775487419678846392834458e+269\"},\n  {\"0x1.e494034e79e5bp+896\", 270, \"999999999999999929448868435382686895890266438998271828845121223533023678802377913944250092254807900260792535316367124530669618423639576906744771616444428851364562613616119809966264354755499540137842111275831603885509059543833769773341090453584235060232375896520569913344\"},\n  {\"0x1.e494034e79e5cp+896\", 270, \"1.00000000000000004675381888545612798918960543133041028684136487274401643939455589461036825818030333693907688813404495028932616818466243033147431327741697981638738927986463793558699752023835231102266007829372867138519293326106230343475263802678137754874196788463928344576e+270\"},\n  {\"0x1.2edc82110c2f9p+900\", 271, \"9999999999999999529098585253973751145501342374646995204443699533752222309208135100774737254399069875964494058799026896824009283758441475916906799486389390443691279468658234350904109878520700943148057046794110173854458342872794765056233999682236635579342942941443126198272\"},\n  {\"0x1.2edc82110c2fap+900\", 271, \"1.000000000000000140597779245514880863829076625196121053238359792112810647868298279143262790920699686281704370388187210896251407993480713071257946606195020588405650612863452436083584052624634527730514451908046325384940032234845130363881876085339091539549641475134254271693e+271\"},\n  {\"0x1.7a93a2954f3b7p+903\", 272, \"99999999999999991537227438137387396469434575991841521388557198562770454753131655626431591234374844785939841297824578543963083245231683449577722661712772273556182341366629763489177637489755720763166395523368395578554699469776634573397170474480057796161122485794632428945408\"},\n  {\"0x1.7a93a2954f3b8p+903\", 272, \"1.0000000000000000655226109574678785641174996701035524401207638566177752810893043715169471647283826068076023845848734024107112161464260868794310399431725879707910415464644008356863148267156087543642309530165922021851423530558188688205784856384929203469035026027382776109466e+272\"},\n  {\"0x1.d9388b3aa30a5p+906\", 273, \"999999999999999945402341696592674884578976541955442659132610359825718694242914119314842162820675279649039207299571308833846909191138684972507989282336695782607667040225918275050684065261167516978177354790265605065466066369376850351293060923539046438669680406904714953752576\"},\n  {\"0x1.d9388b3aa30a6p+906\", 273, \"1.00000000000000006552261095746787856411749967010355244012076385661777528108930437151694716472838260680760238458487340241071121614642608687943103994317258797079104154646440083568631482671560875436423095301659220218514235305581886882057848563849292034690350260273827761094656e+273\"},\n  {\"0x1.27c35704a5e67p+910\", 274, \"9999999999999999213782878444176341486712719163258207029349796604673073768736360688744211624391338142173265718425108901184740478000812045911233791501695173449709921389782217629235579129702792695009666351450002856415308090320884466574359759805482716570229159677380024223137792\"},\n  {\"0x1.27c35704a5e68p+910\", 274, \"1.000000000000000113570718661817960035932908921362796352516025255334597915827860472397789165491465537671027655498994239841456938928541047642200260207506944846064391348959793859940567131297385249318652392307122841033012867730395676208292655524474469910197031481071702673824154e+274\"},\n  {\"0x1.71b42cc5cf601p+913\", 275, \"99999999999999995981677400789769932612359931733321583285118877944076548466448094957909476304960015890806678857380756006307062602577317320133875536163700284518967198097453618232695975663570046546450378657742479671982722077174989256760731188933351130765773907040474247261585408\"},\n  {\"0x1.71b42cc5cf602p+913\", 275, \"1.0000000000000001135707186618179600359329089213627963525160252553345979158278604723977891654914655376710276554989942398414569389285410476422002602075069448460643913489597938599405671312973852493186523923071228410330128677303956762082926555244744699101970314810717026738241536e+275\"},\n  {\"0x1.ce2137f743381p+916\", 276, \"999999999999999929065985077113647184161737396527299728918221484261998998431805045015355882561227083155474615188770224107393363445219598313166454392463014445014728107377484646804238281703363508693674065431485187857190091380020735839470243162305319587149880588271350432374194176\"},\n  {\"0x1.ce2137f743382p+916\", 276, \"1.00000000000000005206914080024985575200918507975096414465009066497706494336250866327031140451471938616584330872891956793010241376743389786585565826915896804571450360176569078889512418143271133577699295001524362330773860894693736275201851807041808646918131451680491859334083379e+276\"},\n  {\"0x1.20d4c2fa8a030p+920\", 277, \"9999999999999998060628293539774386163142897133036353131863523035469330535011014267604003606077347801451059216486208802846843131230052987604772505157670608443149526129892785047133523819740156816103551808477267524066415738131041089269219682541925527051184466597377822714075545600\"},\n  {\"0x1.20d4c2fa8a031p+920\", 277, \"1.000000000000000002867878510995372324870206006461498378357342992691038565390227215968329195733322464961695831312859830401018793638548178044779976718480586605434593404010408332058769821540972204943665396181740249127519201920170711986999208107172979716368740945391491328954177946e+277\"},\n  {\"0x1.6909f3b92c83dp+923\", 278, \"99999999999999996350686867959178558315902274782992576532314485486221746301240205812674342870820492799837784938001204037775189753543960218791943147793788145321066524580618236658968633362758090027700335311493754978334367629875739137498376013657689431411868208826074951744485326848\"},\n  {\"0x1.6909f3b92c83ep+923\", 278, \"1.000000000000000120950908005206132550003755782356216217459937406177501872523702689493086496808675075851649777111403200470819481947873905615361612440108702062106377878623086228466020285281146118943651525382148347160045778784410673823045552018961235923118917516783716763482151977e+278\"},\n  {\"0x1.c34c70a777a4cp+926\", 279, \"999999999999999932018060814468916189790076140924667674895786344599160581110141931853474815088110898427723463833733808359138380652952741502430995285503717331431522719242801594214419543296867856543673718661495390308003255801626734885371401760100025992318635002556156068237393526784\"},\n  {\"0x1.c34c70a777a4dp+926\", 279, \"1.00000000000000005797329227496039376326586256854570003660522038565138810871918243694654926956848701671034100601884673643359244818290018424438474005524037381854809282549632468371548670461972003147699225647526402820936493779014936084382083526600749927951882334537452986506723249357e+279\"},\n  {\"0x1.1a0fc668aac6fp+930\", 280, \"9999999999999998312538756460757341310094469988278417855282391117573785590229095277790152515038100038016294300856434658995751266289947873088679994697143921417382666342399831226135658142385861165970188884104804799869139102108086341186118549553740473625584843283014570307735223533568\"},\n  {\"0x1.1a0fc668aac70p+930\", 280, \"1.000000000000000032782245982862098248570705283021493564263333577440942603197374335927934378672411793053817497581824150818701634676910695695993991101293042521124778804245620065815273272355149596490328548912510300629092601392444835652130948564826004622078785676810855105701264700211e+280\"},\n  {\"0x1.6093b802d578bp+933\", 281, \"99999999999999987155954971343300695452169865566657214127525800489409136785780248940879907693753036165206704358487960288340042823857796898629319779603012221761556906824111051125390730586189881257568082051088644411534964844713587442531567367726443881446254459800333664575907082272768\"},\n  {\"0x1.6093b802d578cp+933\", 281, \"1.0000000000000000327822459828620982485707052830214935642633335774409426031973743359279343786724117930538174975818241508187016346769106956959939911012930425211247788042456200658152732723551495964903285489125103006290926013924448356521309485648260046220787856768108551057012647002112e+281\"},\n  {\"0x1.b8b8a6038ad6ep+936\", 282, \"999999999999999903804088967318825213331499981137556425872873119403461614925716858712626137284506647932417134384268512470460669526244514328233356457082706278317411015442012422166180499160548969358610366191211215418098239036197666670678728654776751975985792813764840337747509598224384\"},\n  {\"0x1.b8b8a6038ad6fp+936\", 282, \"1.0000000000000000327822459828620982485707052830214935642633335774409426031973743359279343786724117930538174975818241508187016346769106956959939911012930425211247788042456200658152732723551495964903285489125103006290926013924448356521309485648260046220787856768108551057012647002112e+282\"},\n  {\"0x1.137367c236c65p+940\", 283, \"9999999999999999553953517735361344274271821018911312812290573026184540102343798495987494338396687059809772796632907678097570555865109868753376103147668407754403581309634554796258176084383892202112976392797308495024959839786965342632596166187964530344229899589832462449290116390191104\"},\n  {\"0x1.137367c236c66p+940\", 283, \"1.000000000000000161760402998405371283809910584905430702653794035478423591469031813143242620060316938175217860779379789166942599827576877063754625745503378763932146593049227709464366045549750223622046731633809385840086963748692004633583168474875257268171778539856869873655019802198016e+283\"},\n  {\"0x1.585041b2c477ep+943\", 284, \"99999999999999991412234152856228705615063640528827139694410995604646009398744945688985079659553905954212916344007296353831994673829780883765420722861953317774200043854630103365810792101611701952914782080891514223497778802469744018919490624758069218767323224280852151918381000638332928\"},\n  {\"0x1.585041b2c477fp+943\", 284, \"1.0000000000000000792143825084576765412568191916997109340838993423344357589751710277254453455720576452975216283329441806240683821311505209883878195732087635685354312082149188175289466707052058222577470946921779713050505718406938164854537477324437355746722631075074204221646165369264538e+284\"},\n  {\"0x1.ae64521f7595ep+946\", 285, \"999999999999999980159157920520442850193109519852847211800025710561650359982538085224088616186146493844286149397221450372619320895438893697947652166455225334059372746413748147206443420891752540620587530362220273863006901551095990707698442841525909542472844588688081080376132618600579072\"},\n  {\"0x1.ae64521f7595fp+946\", 285, \"1.00000000000000011223279070443675443827805574898199884151185721959203089197271534189256425536736136244860012131151842404121806920972106341853454204212660964669411736214864237430311442064302358280346694946883053711906512860389309174470551602941634425207206928044720020276077784303507866e+285\"},\n  {\"0x1.0cfeb353a97dap+950\", 286, \"9999999999999998216707985798208689444911740448978652561458278997251937215943253772219178491686886515191093831000650819703008229183002900332433843156495641588976792075318750746904382211902272900011322274342879579557370290877394694632899550160573878909537749585771381335145583492791795712\"},\n  {\"0x1.0cfeb353a97dbp+950\", 286, \"1.000000000000000032988611034086967485427088011504507863684758314173802572778608987891478871858632441286011738162940239840058820221151761586182408116723779059113270592707705838045111820792260957493739298004864379165430192372214831122501272116682083426312534465391728729329990708374378906e+286\"},\n  {\"0x1.503e602893dd1p+953\", 287, \"99999999999999990619792356152730836086553963154052229916140006550463726206803882148974225824466616742587032512521514511820402183944087865441899383607925011898391576160220738003230766103104075699817505566251852643961429440152961412697448185630726610509727876130297437184073129291725930496\"},\n  {\"0x1.503e602893dd2p+953\", 287, \"1.0000000000000000752521735249401871936142708048258363851925443970635243430154657100253910763966211992393922091755152714140104196817220558967702128769386220391563888697428719907160465407126676909922607121189796634073688250291099034543435355368070225333842863667546468484930771801934187725e+287\"},\n  {\"0x1.a44df832b8d45p+956\", 288, \"999999999999999872387073568844732594315793396883459481955171199192859845878553443782612494614275161063165948315155119859042742270984643205948750027907375734949421139974074457895559885094715370199357924371226299046063388276013556261500671120207314819439877240212639876510262115462027411456\"},\n  {\"0x1.a44df832b8d46p+956\", 288, \"1.00000000000000000763047353957503566051477833551171075078008666443996951063649495461113154913583918651398345555539522089568786054480958499982972526059487327108739962648660614644255098884001691739462644953639520862026701277807778772339591406460711996206948332457397785783213882528295498547e+288\"},\n  {\"0x1.06b0bb1fb384bp+960\", 289, \"9999999999999998453383935746986719810759964091578092281901881061434379129269651416169086837099623559730024468671070996517137186162196548471725549813698762277218254426715681201861616643456550607603042193381925171312226633756007099691216225313273537909139560233403722802458867734978418966528\"},\n  {\"0x1.06b0bb1fb384cp+960\", 289, \"1.000000000000000061727833527867156886994372310963011258310052850538813376539671558942539170944464796694310458451491261310345907854339561717382115353669872285542591021091618821861347430338137536272733859602462772449948462578903480308154011242367042019121325758318513050360889509211326015078e+289\"},\n  {\"0x1.485ce9e7a065ep+963\", 290, \"99999999999999988861628156533236896225967158951884963421416105502251300564950642508203478115686284411726404918398393198344015646384363622121446705582987543928597855835557826052119881754415155586279014739104656819496782321626126403692810027353529143655542997033600043426888732064053872033792\"},\n  {\"0x1.485ce9e7a065fp+963\", 290, \"1.0000000000000000617278335278671568869943723109630112583100528505388133765396715589425391709444647966943104584514912613103459078543395617173821153536698722855425910210916188218613474303381375362727338596024627724499484625789034803081540112423670420191213257583185130503608895092113260150784e+290\"},\n  {\"0x1.9a742461887f6p+966\", 291, \"999999999999999957860902350346284132153551878096514283852517773229033154005572478626236537071903625148082612890986863714202457020042006419681526374965874177788623543449994485057258262661745948026767632275613049896960078961318150545418464661067991669581788285529005480705688196068853638234112\"},\n  {\"0x1.9a742461887f7p+966\", 291, \"1.0000000000000000963501439203741144719413124552518435831292312096420734507177045857146400489019851872097197403049927271757270581324387468166156450132378716547939135136388269341293771528969347323547226020447460133009445904514319235623991934361333921356345049159150155735792899469254834740265e+291\"},\n  {\"0x1.008896bcf54f9p+970\", 292, \"9999999999999997916738124663128877244082391855101191247204616495333847979510139501201523228758057506741180599941798275603729356851659179433605840090394772053822755792233955461707155943795194068332216685526534938121786651731816229250415901309895111103185283290657933692573660950408978352832512\"},\n  {\"0x1.008896bcf54fap+970\", 292, \"1.000000000000000013256598978357416268068656108958646003563203147794249272690425321461597941803936249972737463856589209098812297465000702578455173830274673168590739531525527464686105818755821461757949620183266235258553883557363659752210756171094151856002874937683409517855128896411505572551066e+292\"},\n  {\"0x1.40aabc6c32a38p+973\", 293, \"99999999999999992462348437353960485060448933957923525202610654848990348279466077292501969423268405025328970231162545648343655275306678872441733790178059478330735395060467469727994972900530063978805843953102113868000379620369084502134308975505229555772913629423636305841602377586326247764393984\"},\n  {\"0x1.40aabc6c32a39p+973\", 293, \"1.0000000000000001018897135831752276855328228783380567551002997470985950625861898699981761893751884496921852254015529617141880421769346164324930097587687515538741251124463802320922619085063422837278408008355113318371039709110364744830784225871360081542766135811304559772942340169597486674581914e+293\"},\n  {\"0x1.90d56b873f4c6p+976\", 294, \"999999999999999924623484373539604850604489339579235252026106548489903482794660772925019694232684050253289702311625456483436552753066788724417337901780594783307353950604674697279949729005300639788058439531021138680003796203690845021343089755052295557729136294236363058416023775863262477643939840\"},\n  {\"0x1.90d56b873f4c7p+976\", 294, \"1.00000000000000006643646774124810311854715617058629245448546110737685674662788405058354489034668756980440612078356746066803774429216105089087787538737112019976077088007803912512979947260613395493988432857461329320568393596956734859073135602071926563496711812375163739351859196874045142949534106e+294\"},\n  {\"0x1.f50ac6690f1f8p+979\", 295, \"9999999999999999813486777206230041577815560719820581330098483720446847883279500839884297726782854580737362697004022581572770293687044935910015528960168049498887207223940204684198896264456339658487887951484580004902758521100414464490983962613190835886243290260424727924570510530141380583845003264\"},\n  {\"0x1.f50ac6690f1f9p+979\", 295, \"1.000000000000000094799064414789802772135689536787703894977332019154247399394528706115249929569488273714629404477955861504957982599979903324169982884489225283051454265972712010699769421326300617970249506383331724110819963922742649304609009273852659650414714489654692260539105607315889219865621299e+295\"},\n  {\"0x1.3926bc01a973bp+983\", 296, \"99999999999999998134867772062300415778155607198205813300984837204468478832795008398842977267828545807373626970040225815727702936870449359100155289601680494988872072239402046841988962644563396584878879514845800049027585211004144644909839626131908358862432902604247279245705105301413805838450032640\"},\n  {\"0x1.3926bc01a973cp+983\", 296, \"1.0000000000000001628692964312898819407481696156710913521578222074199849660344758793913420237042099630991652853444880235135665545387451491640710408775726774829490943921199269360676972982547006092431259331242559582831464310103633710179153770813728052874889457678220239413883383398969399167542938829e+296\"},\n  {\"0x1.87706b0213d09p+986\", 297, \"999999999999999872436306494222877488001587945768638201521064070819504681704034606746682422062730755058478860313950798943503314266680100247159860107083281430052496520558476587831205023360193979812186512362979225814553504769848291707808207769286850569305558980974742103098278680884456943362624192512\"},\n  {\"0x1.87706b0213d0ap+986\", 297, \"1.0000000000000000176528014627563797143748787807198647768394431391197448238692552430690122228834703590788220728292194112285349344027126247056154504923279794565007954563392017619494511608074472945276562227436175920488499678901058313628617924253298279283972523743983830222433085103906984300584590377e+297\"},\n  {\"0x1.e94c85c298c4cp+989\", 298, \"9999999999999999595662034753429788238255624467393741467120915117996487670031669885400803025551745174706847878231119663145222863482996149222332143382301002459214758820269116923021527058285459686414683385913622455551313826420028155008403585629126369847605750170289266545852965785882018353801250996224\"},\n  {\"0x1.e94c85c298c4dp+989\", 298, \"1.00000000000000007573939945016978060492419511470035540696679476643984088073534349759794414321176620068695935783532685614254758245712563448899768664642585866708011503065149183159674961578634862041384410689587293854256855313820884722488322628774701887203392973176783938990132044219319502473679297577e+298\"},\n  {\"0x1.31cfd3999f7afp+993\", 299, \"99999999999999986662764669548153739894665631237058913850832890808749507601742578129378923002990117089766513181334005445210204946123879926882163649167349350899456456312724758086647517786230384722356772394775369116518164624503799012160606438304513147494189124523779646633247748770420728389479079870464\"},\n  {\"0x1.31cfd3999f7b0p+993\", 299, \"1.0000000000000000525047602552044202487044685811081591549158541155118024579889081957863713750804478640437044438328838781769425232353604305756447921847867069828483872009265758037378302337947880900593689532349707999450811190389676408800746527427801424945792587888200568428381156694721963868654594005402e+299\"},\n  {\"0x1.7e43c8800759bp+996\", 300, \"999999999999999903803069407426113968898218766118103141789833949572356552411722264192305659040010509526872994217248819197070144216063125530186267630296136203765329090687113225440746189048800695790727969805197112921161540803823920273299782054992133678869364753954248541633605124057805104488924519071744\"},\n  {\"0x1.7e43c8800759cp+996\", 300, \"1.00000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054016e+300\"},\n  {\"0x1.ddd4baa009302p+999\", 301, \"9999999999999999335434075769817752248594687291161143444150379827602457335271594505111188022480979804302392841403758309930446200199225865392779725411942503595819407127350057411001629979979981746444561664911518503259454564508526643946547561925497354420113435609274102018745072331406833609642314953654272\"},\n  {\"0x1.ddd4baa009303p+999\", 301, \"1.00000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054016e+301\"},\n  {\"0x1.2aa4f4a405be1p+1003\", 302, \"99999999999999988595886650569271721532146878831929642021471152965962304374245995240101777311515802698485322026337261211948545873374744892473124468375726771027536211745837771604509610367928220847849105179362427047829119141560667380048679757245757262098417746977035154548906385860807815060374033329553408\"},\n  {\"0x1.2aa4f4a405be2p+1003\", 302, \"1.0000000000000000762970307908489492534734685515065681170160173420621138028812579448414218896469178407663974757713854876137221038784479993829181561135051983075016764985648898162653636809541460731423515105837345898689082515565906361771586320528262239050928418343985861710308373567384989920457049815751066e+302\"},\n  {\"0x1.754e31cd072d9p+1006\", 303, \"999999999999999847891233648661470807691068835681842080854450367179124891914700353912936949808806064228544369161770037020638129704807338833093862397807681590830099241237075296001042588224309435545718960035602206600167779387409881325152430676383842364162444596844704620380709158981993982315347403639619584\"},\n  {\"0x1.754e31cd072dap+1006\", 303, \"1.00000000000000000016176507678645643821266864623165943829549501710111749922573874786526024303421391525377977356818033741602744582056777919964339154160602606861115074612228497617725665004420052727680732706769046211266142750019705122648989826067876339144937608854729232081412795748633065546891912226327757e+303\"},\n  {\"0x1.d2a1be4048f90p+1009\", 304, \"9999999999999999392535525055364621860040287220117324953190771571323204563013233902843309257440507748436856118056162172578717193742636030530235798840866882774987301441682011041067710253162440905843719802548551599076639682550821832659549112269607949805346034918662572406407604380845959862074904348138143744\"},\n  {\"0x1.d2a1be4048f91p+1009\", 304, \"1.000000000000000061069977648036450690421308570451586381271912877069914542150154105446189560324377055663873935330744457574183172266871955346263203199125363859723571348076368848247742274772156963969242673880525764317658886745311919187024885294396731802364148685228327400987495476888065324730347809712740762e+304\"},\n  {\"0x1.23a516e82d9bap+1013\", 305, \"99999999999999993925355250553646218600402872201173249531907715713232045630132339028433092574405077484368561180561621725787171937426360305302357988408668827749873014416820110410677102531624409058437198025485515990766396825508218326595491122696079498053460349186625724064076043808459598620749043481381437440\"},\n  {\"0x1.23a516e82d9bbp+1013\", 305, \"1.0000000000000001341598327335364437930716764795154987128436143090324709936594525345433047410725728241559869294458214017639700440024369667222069771881485692090584760704212694947323250244457046880001650900559281269636558378394497607396668697348582938954618758012455694971955365001701469278440622346520965939e+305\"},\n  {\"0x1.6c8e5ca239028p+1016\", 306, \"999999999999999861291040414336469543176969619010226008309262296372260241358071732580741399612641955118765084749534143455432389522994257585350220962461935904874831773666973747856549425664459851618054736334425973085267220421335152276470127823801795414563694568114532338018850013250375609552861714878501486592\"},\n  {\"0x1.6c8e5ca239029p+1016\", 306, \"1.00000000000000001721606459673645482883108782501323898232889201789238067124457504798792045187545959456860613886169829106031104922553294852069693880571144065012262851466942846035699262496802832955068922417528434673006071608882921425543969463011979454650551241561798214326267086291881636286211915474912726221e+306\"},\n  {\"0x1.c7b1f3cac7433p+1019\", 307, \"9999999999999999860310597602564577717002641838126363875249660735883565852672743849064846414228960666786379280392654615393353172850252103336275952370615397010730691664689375178569039851073146339641623266071126720011020169553304018596457812688561947201171488461172921822139066929851282122002676667750021070848\"},\n  {\"0x1.c7b1f3cac7434p+1019\", 307, \"1.000000000000000110771079106176446000223558748615046766740669850804452929176477037232227883233150178238510771328996779623238245047056163081904969511661143497271306559270901287857258544550169416310269916879799370916936813489325651442821434713910594025670603124120052026408963372719880814847673618671502727578e+307\"},\n  {\"0x1.1ccf385ebc89fp+1023\", 308, \"99999999999999981139503267596847425176765179308926185662298078548582170379439067165044410288854031049481594743364161622187121841818187648603927125262209438639553681654618823985640760188731793867961170022535129351893330180773705244319986644578003569234231285691342840034082734135647456849389933411990123839488\"},\n  {\"0x1.1ccf385ebc8a0p+1023\", 308, \"1.0000000000000000109790636294404554174049230967731184633681068290315758540491149153716332897849468889906124966972117251561159028374314008832830700919814604603127166450293302718569748969958855904333838446616500117842689762621294517762809119578670745812278397017178441510529180289320787327297488571543022311834e+308\"},\n  {\"0x1.a36e2eb1c432cp-14\", 309, \"9.99999999999999912396464463171241732197813689708709716796875e-5\"},\n  {\"0x1.a36e2eb1c432dp-14\", 309, \"0.000100000000000000004792173602385929598312941379845142364501953125\"},\n  {\"0x1.0624dd2f1a9fbp-10\", 309, \"0.00099999999999999980397624721462079833145253360271453857421875\"},\n  {\"0x1.0624dd2f1a9fcp-10\", 309, \"0.001000000000000000020816681711721685132943093776702880859375\"},\n  {\"0x1.47ae147ae147ap-7\", 309, \"0.0099999999999999984734433411404097569175064563751220703125\"},\n  {\"0x1.47ae147ae147bp-7\", 309, \"0.01000000000000000020816681711721685132943093776702880859375\"},\n  {\"0x1.9999999999999p-4\", 309, \"0.09999999999999999167332731531132594682276248931884765625\"},\n  {\"0x1.999999999999ap-4\", 309, \"0.1000000000000000055511151231257827021181583404541015625\"},\n  {\"0x1.fffffffffffffp-1\", 309, \"0.99999999999999988897769753748434595763683319091796875\"},\n  {\"0x1.0000000000000p+0\", 309, \"1\"},\n  {\"0x1.3ffffffffffffp+3\", 309, \"9.9999999999999982236431605997495353221893310546875\"},\n  {\"0x1.4000000000000p+3\", 309, \"10\"},\n  {\"0x1.8ffffffffffffp+6\", 309, \"99.9999999999999857891452847979962825775146484375\"},\n  {\"0x1.9000000000000p+6\", 309, \"100\"},\n  {\"0x1.f3fffffffffffp+9\", 309, \"999.9999999999998863131622783839702606201171875\"},\n  {\"0x1.f400000000000p+9\", 309, \"1000\"},\n  {\"0x1.387ffffffffffp+13\", 309, \"9999.999999999998181010596454143524169921875\"},\n  {\"0x1.3880000000000p+13\", 309, \"10000\"},\n  {\"0x1.869ffffffffffp+16\", 309, \"99999.999999999985448084771633148193359375\"},\n  {\"0x1.86a0000000000p+16\", 309, \"100000\"},\n  {\"0x1.e847fffffffffp+19\", 309, \"999999.999999999883584678173065185546875\"},\n  {\"0x1.e848000000000p+19\", 309, \"1000000\"},\n  {\"0x1.312cfffffffffp+23\", 309, \"9999999.99999999813735485076904296875\"},\n  {\"0x1.312d000000000p+23\", 309, \"10000000\"},\n  {\"0x1.7d783ffffffffp+26\", 309, \"99999999.99999998509883880615234375\"},\n  {\"0x1.7d78400000000p+26\", 309, \"100000000\"},\n  {\"0x1.dcd64ffffffffp+29\", 309, \"999999999.99999988079071044921875\"},\n  {\"0x1.dcd6500000000p+29\", 309, \"1000000000\"},\n  {\"0x1.2a05f1fffffffp+33\", 309, \"9999999999.9999980926513671875\"},\n  {\"0x1.2a05f20000000p+33\", 309, \"10000000000\"},\n  {\"0x1.74876e7ffffffp+36\", 309, \"99999999999.9999847412109375\"},\n  {\"0x1.74876e8000000p+36\", 309, \"100000000000\"},\n  {\"0x1.d1a94a1ffffffp+39\", 309, \"999999999999.9998779296875\"},\n  {\"0x1.d1a94a2000000p+39\", 309, \"1000000000000\"},\n  {\"0x1.2309ce53fffffp+43\", 309, \"9999999999999.998046875\"},\n  {\"0x1.2309ce5400000p+43\", 309, \"10000000000000\"},\n  {\"0x1.6bcc41e8fffffp+46\", 309, \"99999999999999.984375\"},\n  {\"0x1.6bcc41e900000p+46\", 309, \"100000000000000\"},\n  {\"0x1.c6bf52633ffffp+49\", 309, \"999999999999999.875\"},\n  {\"0x1.c6bf526340000p+49\", 309, \"1000000000000000\"},\n  {\"0x1.1c37937e07fffp+53\", 309, \"9999999999999998\"},\n  {\"0x1.1c37937e08000p+53\", 309, \"10000000000000000\"},\n  {\"0x1.6345785d89fffp+56\", 309, \"99999999999999984\"},\n  {\"0x1.6345785d8a000p+56\", 309, \"100000000000000000\"},\n  {\"0x1.bc16d674ec7ffp+59\", 309, \"999999999999999872\"},\n  {\"0x1.bc16d674ec800p+59\", 309, \"1000000000000000000\"},\n  {\"0x1.158e460913cffp+63\", 309, \"9999999999999997952\"},\n  {\"0x1.158e460913d00p+63\", 309, \"10000000000000000000\"},\n  {\"0x1.5af1d78b58c3fp+66\", 309, \"99999999999999983616\"},\n  {\"0x1.5af1d78b58c40p+66\", 309, \"100000000000000000000\"},\n  {\"0x1.b1ae4d6e2ef4fp+69\", 309, \"999999999999999868928\"},\n  {\"0x1.b1ae4d6e2ef50p+69\", 309, \"1000000000000000000000\"},\n  {\"0x1.0f0cf064dd591p+73\", 309, \"9999999999999997902848\"},\n  {\"0x1.0f0cf064dd592p+73\", 309, \"10000000000000000000000\"},\n  {\"0x1.52d02c7e14af6p+76\", 309, \"99999999999999991611392\"},\n  {\"0x1.52d02c7e14af7p+76\", 309, \"100000000000000008388608\"},\n  {\"0x1.a784379d99db4p+79\", 309, \"999999999999999983222784\"},\n  {\"0x1.a784379d99db5p+79\", 309, \"1000000000000000117440512\"},\n  {\"0x1.08b2a2c280290p+83\", 309, \"9999999999999998758486016\"},\n  {\"0x1.08b2a2c280291p+83\", 309, \"10000000000000000905969664\"},\n  {\"0x1.4adf4b7320334p+86\", 309, \"99999999999999987584860160\"},\n  {\"0x1.4adf4b7320335p+86\", 309, \"100000000000000004764729344\"},\n  {\"0x1.9d971e4fe8401p+89\", 309, \"999999999999999875848601600\"},\n  {\"0x1.9d971e4fe8402p+89\", 309, \"1000000000000000013287555072\"},\n  {\"0x1.027e72f1f1281p+93\", 309, \"9999999999999999583119736832\"},\n  {\"0x1.027e72f1f1282p+93\", 309, \"10000000000000001782142992384\"},\n  {\"0x1.431e0fae6d721p+96\", 309, \"99999999999999991433150857216\"},\n  {\"0x1.431e0fae6d722p+96\", 309, \"100000000000000009025336901632\"},\n  {\"0x1.93e5939a08ce9p+99\", 309, \"999999999999999879147136483328\"},\n  {\"0x1.93e5939a08ceap+99\", 309, \"1000000000000000019884624838656\"},\n  {\"0x1.f8def8808b024p+102\", 309, \"9999999999999999635896294965248\"},\n  {\"0x1.f8def8808b025p+102\", 309, \"10000000000000000761796201807872\"},\n  {\"0x1.3b8b5b5056e16p+106\", 309, \"99999999999999987351763694911488\"},\n  {\"0x1.3b8b5b5056e17p+106\", 309, \"100000000000000005366162204393472\"},\n  {\"0x1.8a6e32246c99cp+109\", 309, \"999999999999999945575230987042816\"},\n  {\"0x1.8a6e32246c99dp+109\", 309, \"1000000000000000089690419062898688\"},\n  {\"0x1.ed09bead87c03p+112\", 309, \"9999999999999999455752309870428160\"},\n  {\"0x1.ed09bead87c04p+112\", 309, \"10000000000000000608673814477275136\"},\n  {\"0x1.3426172c74d82p+116\", 309, \"99999999999999996863366107917975552\"},\n  {\"0x1.3426172c74d83p+116\", 309, \"100000000000000015310110181627527168\"},\n  {\"0x1.812f9cf7920e2p+119\", 309, \"999999999999999894846684784341549056\"},\n  {\"0x1.812f9cf7920e3p+119\", 309, \"1000000000000000042420637374017961984\"},\n  {\"0x1.e17b84357691bp+122\", 309, \"9999999999999999538762658202121142272\"},\n  {\"0x1.e17b84357691cp+122\", 309, \"10000000000000000719354278919532445696\"},\n  {\"0x1.2ced32a16a1b1p+126\", 309, \"99999999999999997748809823456034029568\"},\n  {\"0x1.2ced32a16a1b2p+126\", 309, \"100000000000000016638275754934614884352\"},\n  {\"0x1.78287f49c4a1dp+129\", 309, \"999999999999999939709166371603178586112\"},\n  {\"0x1.78287f49c4a1ep+129\", 309, \"1000000000000000090824893823431825424384\"},\n  {\"0x1.d6329f1c35ca4p+132\", 309, \"9999999999999999094860208812374492184576\"},\n  {\"0x1.d6329f1c35ca5p+132\", 309, \"10000000000000000303786028427003666890752\"},\n  {\"0x1.25dfa371a19e6p+136\", 309, \"99999999999999981277195531206711524196352\"},\n  {\"0x1.25dfa371a19e7p+136\", 309, \"100000000000000000620008645040778319495168\"},\n  {\"0x1.6f578c4e0a060p+139\", 309, \"999999999999999890143207767403382423158784\"},\n  {\"0x1.6f578c4e0a061p+139\", 309, \"1000000000000000044885712678075916785549312\"},\n  {\"0x1.cb2d6f618c878p+142\", 309, \"9999999999999998901432077674033824231587840\"},\n  {\"0x1.cb2d6f618c879p+142\", 309, \"10000000000000000139372116959414099130712064\"},\n  {\"0x1.1efc659cf7d4bp+146\", 309, \"99999999999999989014320776740338242315878400\"},\n  {\"0x1.1efc659cf7d4cp+146\", 309, \"100000000000000008821361405306422640701865984\"},\n  {\"0x1.66bb7f0435c9ep+149\", 309, \"999999999999999929757289024535551219930759168\"},\n  {\"0x1.66bb7f0435c9fp+149\", 309, \"1000000000000000088213614053064226407018659840\"},\n  {\"0x1.c06a5ec5433c6p+152\", 309, \"9999999999999999931398190359470212947659194368\"},\n  {\"0x1.c06a5ec5433c7p+152\", 309, \"10000000000000001199048790587699614444362399744\"},\n  {\"0x1.18427b3b4a05bp+156\", 309, \"99999999999999984102174700855949311516153479168\"},\n  {\"0x1.18427b3b4a05cp+156\", 309, \"100000000000000004384584304507619735463404765184\"},\n  {\"0x1.5e531a0a1c872p+159\", 309, \"999999999999999881586566215862833963056037363712\"},\n  {\"0x1.5e531a0a1c873p+159\", 309, \"1000000000000000043845843045076197354634047651840\"},\n  {\"0x1.b5e7e08ca3a8fp+162\", 309, \"9999999999999999464902769475481793196872414789632\"},\n  {\"0x1.b5e7e08ca3a90p+162\", 309, \"10000000000000000762976984109188700329496497094656\"},\n  {\"0x1.11b0ec57e6499p+166\", 309, \"99999999999999986860582406952576489172979654066176\"},\n  {\"0x1.11b0ec57e649ap+166\", 309, \"100000000000000007629769841091887003294964970946560\"},\n  {\"0x1.561d276ddfdc0p+169\", 309, \"999999999999999993220948674361627976461708441944064\"},\n  {\"0x1.561d276ddfdc1p+169\", 309, \"1000000000000000159374448147476112089437590976987136\"},\n  {\"0x1.aba4714957d30p+172\", 309, \"9999999999999999932209486743616279764617084419440640\"},\n  {\"0x1.aba4714957d31p+172\", 309, \"10000000000000001261437482528532152668424144699785216\"},\n  {\"0x1.0b46c6cdd6e3ep+176\", 309, \"99999999999999999322094867436162797646170844194406400\"},\n  {\"0x1.0b46c6cdd6e3fp+176\", 309, \"100000000000000020589742799994816764107083808679919616\"},\n  {\"0x1.4e1878814c9cdp+179\", 309, \"999999999999999908150356944127012110618056584002011136\"},\n  {\"0x1.4e1878814c9cep+179\", 309, \"1000000000000000078291540404596243842305360299886116864\"},\n  {\"0x1.a19e96a19fc40p+182\", 309, \"9999999999999998741221202520331657642805958408251899904\"},\n  {\"0x1.a19e96a19fc41p+182\", 309, \"10000000000000000102350670204085511496304388135324745728\"},\n  {\"0x1.05031e2503da8p+186\", 309, \"99999999999999987412212025203316576428059584082518999040\"},\n  {\"0x1.05031e2503da9p+186\", 309, \"100000000000000009190283508143378238084034459715684532224\"},\n  {\"0x1.4643e5ae44d12p+189\", 309, \"999999999999999874122120252033165764280595840825189990400\"},\n  {\"0x1.4643e5ae44d13p+189\", 309, \"1000000000000000048346692115553659057528394845890514255872\"},\n  {\"0x1.97d4df19d6057p+192\", 309, \"9999999999999999438119489974413630815797154428513196965888\"},\n  {\"0x1.97d4df19d6058p+192\", 309, \"10000000000000000831916064882577577161779546469035791089664\"},\n  {\"0x1.fdca16e04b86dp+195\", 309, \"99999999999999997168788049560464200849936328366177157906432\"},\n  {\"0x1.fdca16e04b86ep+195\", 309, \"100000000000000008319160648825775771617795464690357910896640\"},\n  {\"0x1.3e9e4e4c2f344p+199\", 309, \"999999999999999949387135297074018866963645011013410073083904\"},\n  {\"0x1.3e9e4e4c2f345p+199\", 309, \"1000000000000000127793096885319003999249391192200302120927232\"},\n  {\"0x1.8e45e1df3b015p+202\", 309, \"9999999999999999493871352970740188669636450110134100730839040\"},\n  {\"0x1.8e45e1df3b016p+202\", 309, \"10000000000000000921119045676700069727922419559629237113585664\"},\n  {\"0x1.f1d75a5709c1ap+205\", 309, \"99999999999999992084218144295482124579792562202350734542897152\"},\n  {\"0x1.f1d75a5709c1bp+205\", 309, \"100000000000000003502199685943161173046080317798311825604870144\"},\n  {\"0x1.3726987666190p+209\", 309, \"999999999999999875170255276364105051932774599639662981181079552\"},\n  {\"0x1.3726987666191p+209\", 309, \"1000000000000000057857959942726969827393378689175040438172647424\"},\n  {\"0x1.84f03e93ff9f4p+212\", 309, \"9999999999999998751702552763641050519327745996396629811810795520\"},\n  {\"0x1.84f03e93ff9f5p+212\", 309, \"10000000000000000213204190094543968723012578712679649467743338496\"},\n  {\"0x1.e62c4e38ff872p+215\", 309, \"99999999999999999209038626283633850822756121694230455365568299008\"},\n  {\"0x1.e62c4e38ff873p+215\", 309, \"100000000000000010901051724930857196452234783424494612613028642816\"},\n  {\"0x1.2fdbb0e39fb47p+219\", 309, \"999999999999999945322333868247445125709646570021247924665841614848\"},\n  {\"0x1.2fdbb0e39fb48p+219\", 309, \"1000000000000000132394543446603018655781305157705474440625207115776\"},\n  {\"0x1.7bd29d1c87a19p+222\", 309, \"9999999999999999827367757839185598317239782875580932278577147150336\"},\n  {\"0x1.7bd29d1c87a1ap+222\", 309, \"10000000000000001323945434466030186557813051577054744406252071157760\"},\n  {\"0x1.dac74463a989fp+225\", 309, \"99999999999999995280522225138166806691251291352861698530421623488512\"},\n  {\"0x1.dac74463a98a0p+225\", 309, \"100000000000000007253143638152923512615837440964652195551821015547904\"},\n  {\"0x1.28bc8abe49f63p+229\", 309, \"999999999999999880969493773293127831364996015857874003175819882528768\"},\n  {\"0x1.28bc8abe49f64p+229\", 309, \"1000000000000000072531436381529235126158374409646521955518210155479040\"},\n  {\"0x1.72ebad6ddc73cp+232\", 309, \"9999999999999999192818822949403492903236716946156035936442979371188224\"},\n  {\"0x1.72ebad6ddc73dp+232\", 309, \"10000000000000000725314363815292351261583744096465219555182101554790400\"},\n  {\"0x1.cfa698c95390bp+235\", 309, \"99999999999999991928188229494034929032367169461560359364429793711882240\"},\n  {\"0x1.cfa698c95390cp+235\", 309, \"100000000000000004188152556421145795899143386664033828314342771180699648\"},\n  {\"0x1.21c81f7dd43a7p+239\", 309, \"999999999999999943801810948794571024057224129020550531544123892056457216\"},\n  {\"0x1.21c81f7dd43a8p+239\", 309, \"1000000000000000139961240179628344893925643604260126034742731531557535744\"},\n  {\"0x1.6a3a275d49491p+242\", 309, \"9999999999999999830336967949613257980309080240684656321838454199566729216\"},\n  {\"0x1.6a3a275d49492p+242\", 309, \"10000000000000001399612401796283448939256436042601260347427315315575357440\"},\n  {\"0x1.c4c8b1349b9b5p+245\", 309, \"99999999999999995164818811802792197885196090803013355167206819763650035712\"},\n  {\"0x1.c4c8b1349b9b6p+245\", 309, \"100000000000000007719022282576153725556774937218346187371917708691719061504\"},\n  {\"0x1.1afd6ec0e1411p+249\", 309, \"999999999999999926539781176481198923508803215199467887262646419780362305536\"},\n  {\"0x1.1afd6ec0e1412p+249\", 309, \"1000000000000000127407036708854983366254064757844793202538020642629466718208\"},\n  {\"0x1.61bcca7119915p+252\", 309, \"9999999999999998863663300700064420349597509066704028242075715752105414230016\"},\n  {\"0x1.61bcca7119916p+252\", 309, \"10000000000000000470601344959054695891559601407866630764278709534898249531392\"},\n  {\"0x1.ba2bfd0d5ff5bp+255\", 309, \"99999999999999998278261272554585856747747644714015897553975120217811154108416\"},\n  {\"0x1.ba2bfd0d5ff5cp+255\", 309, \"100000000000000011133765626626508061083444383443316717731599070480153836519424\"},\n  {\"0x1.145b7e285bf98p+259\", 309, \"999999999999999802805551768538947706777722104929947493053015898505313987330048\"},\n  {\"0x1.145b7e285bf99p+259\", 309, \"1000000000000000008493621433689702976148869924598760615894999102702796905906176\"},\n  {\"0x1.59725db272f7fp+262\", 309, \"9999999999999999673560075006595519222746403606649979913266024618633003221909504\"},\n  {\"0x1.59725db272f80p+262\", 309, \"10000000000000001319064632327801561377715586164000484896001890252212866570518528\"},\n  {\"0x1.afcef51f0fb5ep+265\", 309, \"99999999999999986862573406138718939297648940722396769236245052384850852127440896\"},\n  {\"0x1.afcef51f0fb5fp+265\", 309, \"100000000000000000026609864708367276537402401181200809098131977453489758916313088\"},\n  {\"0x1.0de1593369d1bp+269\", 309, \"999999999999999921281879895665782741935503249059183851809998224123064148429897728\"},\n  {\"0x1.0de1593369d1cp+269\", 309, \"1000000000000000131906463232780156137771558616400048489600189025221286657051852800\"},\n  {\"0x1.5159af8044462p+272\", 309, \"9999999999999999634067965630886574211027143225273567793680363843427086501542887424\"},\n  {\"0x1.5159af8044463p+272\", 309, \"10000000000000001319064632327801561377715586164000484896001890252212866570518528000\"},\n  {\"0x1.a5b01b605557ap+275\", 309, \"99999999999999989600692989521205793443517660497828009527517532799127744739526311936\"},\n  {\"0x1.a5b01b605557bp+275\", 309, \"100000000000000003080666323096525690777025204007643346346089744069413985291331436544\"},\n  {\"0x1.078e111c3556cp+279\", 309, \"999999999999999842087036560910778345101146430939018748000886482910132485188042620928\"},\n  {\"0x1.078e111c3556dp+279\", 309, \"1000000000000000057766609898115896702437267127096064137098041863234712334016924614656\"},\n  {\"0x1.4971956342ac7p+282\", 309, \"9999999999999998420870365609107783451011464309390187480008864829101324851880426209280\"},\n  {\"0x1.4971956342ac8p+282\", 309, \"10000000000000000146306952306748730309700429878646550592786107871697963642511482159104\"},\n  {\"0x1.9bcdfabc13579p+285\", 309, \"99999999999999987659576829486359728227492574232414601025643134376206526100066373992448\"},\n  {\"0x1.9bcdfabc1357ap+285\", 309, \"100000000000000001463069523067487303097004298786465505927861078716979636425114821591040\"},\n  {\"0x1.0160bcb58c16cp+289\", 309, \"999999999999999959416724456350362731491996089648451439669739009806703922950954425516032\"},\n  {\"0x1.0160bcb58c16dp+289\", 309, \"1000000000000000180272607553648403929404183682513265918105226119259073688151729587093504\"},\n  {\"0x1.41b8ebe2ef1c7p+292\", 309, \"9999999999999999594167244563503627314919960896484514396697390098067039229509544255160320\"},\n  {\"0x1.41b8ebe2ef1c8p+292\", 309, \"10000000000000001361014309341887956898217461639403030224181286973685997351115745547780096\"},\n  {\"0x1.922726dbaae39p+295\", 309, \"99999999999999999475366575191804932315794610450682175621941694731908308538307845136842752\"},\n  {\"0x1.922726dbaae3ap+295\", 309, \"100000000000000013610143093418879568982174616394030302241812869736859973511157455477800960\"},\n  {\"0x1.f6b0f092959c7p+298\", 309, \"999999999999999966484112715463900049825186092620125502979674597309179755437379230686511104\"},\n  {\"0x1.f6b0f092959c8p+298\", 309, \"1000000000000000079562324861280497143156226140166910515938643997348793075220176113414176768\"},\n  {\"0x1.3a2e965b9d81cp+302\", 309, \"9999999999999998986371854279739417938265620640920544952042929572854117635677011010499117056\"},\n  {\"0x1.3a2e965b9d81dp+302\", 309, \"10000000000000000795623248612804971431562261401669105159386439973487930752201761134141767680\"},\n  {\"0x1.88ba3bf284e23p+305\", 309, \"99999999999999989863718542797394179382656206409205449520429295728541176356770110104991170560\"},\n  {\"0x1.88ba3bf284e24p+305\", 309, \"100000000000000004337729697461918607329029332495193931179177378933611681288968111094132375552\"},\n  {\"0x1.eae8caef261acp+308\", 309, \"999999999999999927585207737302990649719308316264031458521789123695552773432097103028194115584\"},\n  {\"0x1.eae8caef261adp+308\", 309, \"1000000000000000043377296974619186073290293324951939311791773789336116812889681110941323755520\"},\n  {\"0x1.32d17ed577d0bp+312\", 309, \"9999999999999998349515363474500343108625203093137051759058013911831015418660298966976904036352\"},\n  {\"0x1.32d17ed577d0cp+312\", 309, \"10000000000000000202188791271559469885760963232143577411377768562080040049981643093586978275328\"},\n  {\"0x1.7f85de8ad5c4ep+315\", 309, \"99999999999999987200500490339121684640523551209383568895219648418808203449245677922989188841472\"},\n  {\"0x1.7f85de8ad5c4fp+315\", 309, \"100000000000000002021887912715594698857609632321435774113777685620800400499816430935869782753280\"},\n  {\"0x1.df67562d8b362p+318\", 309, \"999999999999999931290554592897108903273579836542044509826428632996050822694739791281414264061952\"},\n  {\"0x1.df67562d8b363p+318\", 309, \"1000000000000000049861653971908893017010268485438462151574892930611988399099305815384459015356416\"},\n  {\"0x1.2ba095dc7701dp+322\", 309, \"9999999999999998838621148412923952577789043769834774531270429139496757921329133816401963635441664\"},\n  {\"0x1.2ba095dc7701ep+322\", 309, \"10000000000000000735758738477112498397576062152177456799245857901351759143802190202050679656153088\"},\n  {\"0x1.7688bb5394c25p+325\", 309, \"99999999999999999769037024514370800696612547992403838920556863966097586548129676477911932478685184\"},\n  {\"0x1.7688bb5394c26p+325\", 309, \"100000000000000014946137745027879167254908695051145297064360294060937596327914127563101660644376576\"},\n  {\"0x1.d42aea2879f2ep+328\", 309, \"999999999999999967336168804116691273849533185806555472917961779471295845921727862608739868455469056\"},\n  {\"0x1.d42aea2879f2fp+328\", 309, \"1000000000000000088752974568224758206315902362276487138068389220230015924160003471290257693781000192\"},\n  {\"0x1.249ad2594c37cp+332\", 309, \"9999999999999998216360018871870109548898901740426374747374488505608317520357971321909184780648316928\"},\n  {\"0x1.249ad2594c37dp+332\", 309, \"10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104\"},\n  {\"0x1.6dc186ef9f45cp+335\", 309, \"99999999999999997704951326524533662844684271992415000612999597473199345218078991130326129448151154688\"},\n  {\"0x1.6dc186ef9f45dp+335\", 309, \"100000000000000013246302464330366230200379526580566253752254309890315515232578269041560411089819140096\"},\n  {\"0x1.c931e8ab87173p+338\", 309, \"999999999999999977049513265245336628446842719924150006129995974731993452180789911303261294481511546880\"},\n  {\"0x1.c931e8ab87174p+338\", 309, \"1000000000000000101380322367691997167292404756629360031244033674068922812296784134593135547614855430144\"},\n  {\"0x1.1dbf316b346e7p+342\", 309, \"9999999999999998029863805218200118740630558685368559709703431956602923480183979986974373400948301103104\"},\n  {\"0x1.1dbf316b346e8p+342\", 309, \"10000000000000000019156750857346687362159551272651920111528035145993793242039887559612361451081803235328\"},\n  {\"0x1.652efdc6018a1p+345\", 309, \"99999999999999984277223943460294324649363572028252317900683525944810974325551615015019710109750015295488\"},\n  {\"0x1.652efdc6018a2p+345\", 309, \"100000000000000000191567508573466873621595512726519201115280351459937932420398875596123614510818032353280\"},\n  {\"0x1.be7abd3781ecap+348\", 309, \"999999999999999938258300825281978540327027364472124478294416212538871491824599713636820527503908255301632\"},\n  {\"0x1.be7abd3781ecbp+348\", 309, \"1000000000000000065573049346187358932104882890058259544011190816659887156583377798285651762712452391763968\"},\n  {\"0x1.170cb642b133ep+352\", 309, \"9999999999999998873324014169198263836158851542376704520077063708904652259210884797772880334204906007166976\"},\n  {\"0x1.170cb642b133fp+352\", 309, \"10000000000000000910359990503684350104604539951754865571545457374840902895351334152154180097541612190564352\"},\n  {\"0x1.5ccfe3d35d80ep+355\", 309, \"99999999999999996881384047029926983435371269061279689406644211752791525136670645395254002395395884805259264\"},\n  {\"0x1.5ccfe3d35d80fp+355\", 309, \"100000000000000013177671857705815673582936776336304977818391361080281530225794240230304400502089534272438272\"},\n  {\"0x1.b403dcc834e11p+358\", 309, \"999999999999999903628689227595715073763450661512695740419453520217955231010212074612338431527184250183876608\"},\n  {\"0x1.b403dcc834e12p+358\", 309, \"1000000000000000033998991713002824594943974719712898047713430714837875271723200833292741616380733445921308672\"},\n  {\"0x1.108269fd210cbp+362\", 309, \"9999999999999999818508707188399807864717650964328171247958398369899072554380053298205803424393137676263358464\"},\n  {\"0x1.108269fd210ccp+362\", 309, \"10000000000000001904433546954913560203606035895531408164662033483817793205787873437092254382049924808062271488\"},\n  {\"0x1.54a3047c694fdp+365\", 309, \"99999999999999985669538033284915564613846200056062290979362173015478401635353612148739328497990653971840106496\"},\n  {\"0x1.54a3047c694fep+365\", 309, \"100000000000000002356936751417025583324953279505688186312991253926828166846616173259830936159244951026231410688\"},\n  {\"0x1.a9cbc59b83a3dp+368\", 309, \"999999999999999956819772641641815758405104477258378281795396215622882607621111488153942930947432322044748890112\"},\n  {\"0x1.a9cbc59b83a3ep+368\", 309, \"1000000000000000090318962386698695908093961112855385444464428862913680729311211977042675792237466698479879323648\"},\n  {\"0x1.0a1f5b8132466p+372\", 309, \"9999999999999999301199346926304397284673331501389768492615896861647229832830913903761963586894254467577228034048\"},\n  {\"0x1.0a1f5b8132467p+372\", 309, \"10000000000000001437186382847214479679695037670941883095320419218299999779872521725981689367534804490539314970624\"},\n  {\"0x1.4ca732617ed7fp+375\", 309, \"99999999999999984468045325579403643266646490335689226515340879189861218540142707748740732746380344583923932594176\"},\n  {\"0x1.4ca732617ed80p+375\", 309, \"100000000000000001555941612946684302426820139692106143336977058043083378116475570326498538991504744767620628086784\"},\n  {\"0x1.9fd0fef9de8dfp+378\", 309, \"999999999999999878856245830528597750986812202069726098796681149605056504554092802642922939954052246206632716926976\"},\n  {\"0x1.9fd0fef9de8e0p+378\", 309, \"1000000000000000015559416129466843024268201396921061433369770580430833781164755703264985389915047447676206280867840\"},\n  {\"0x1.03e29f5c2b18bp+382\", 309, \"9999999999999997968343436511656505870179786851589248980528274911095901385876950622696854699774551253248885785624576\"},\n  {\"0x1.03e29f5c2b18cp+382\", 309, \"10000000000000000155594161294668430242682013969210614333697705804308337811647557032649853899150474476762062808678400\"},\n  {\"0x1.44db473335deep+385\", 309, \"99999999999999984057935814682588907446802322751135220511621610897383886710310719046874545396497358979515211902353408\"},\n  {\"0x1.44db473335defp+385\", 309, \"100000000000000001555941612946684302426820139692106143336977058043083378116475570326498538991504744767620628086784000\"},\n  {\"0x1.961219000356ap+388\", 309, \"999999999999999910571381339882270654388094495275235896417637897556636832727766595587241428345003132947573783761256448\"},\n  {\"0x1.961219000356bp+388\", 309, \"1000000000000000050555427725995033814228237030803003279020481474722232763977085405824233377105062219252417113236701184\"},\n  {\"0x1.fb969f40042c5p+391\", 309, \"9999999999999999665649998943273759183241515094863428494587753284228752052274941196820382078490267674695111155514343424\"},\n  {\"0x1.fb969f40042c6p+391\", 309, \"10000000000000000785522370032175864461962655379085567555410501901553519502269491678716317668570740365133857791317901312\"},\n  {\"0x1.3d3e2388029bbp+395\", 309, \"99999999999999994416755247254933381274972870380190006824232035607637985622760311004411949604741731366073618283536318464\"},\n  {\"0x1.3d3e2388029bcp+395\", 309, \"100000000000000012334713184677367065734511114927744231797396013484834264822673118714746919046029294413093564456393244672\"},\n  {\"0x1.8c8dac6a0342ap+398\", 309, \"999999999999999980003468347394201181668805192897008518188648311830772414627428725464789434929992439754776075181077037056\"},\n  {\"0x1.8c8dac6a0342bp+398\", 309, \"1000000000000000123347131846773670657345111149277442317973960134848342648226731187147469190460292944130935644563932446720\"},\n  {\"0x1.efb1178484134p+401\", 309, \"9999999999999999226660029476424133913982828103448349982745235826237443211877077407917175327178722380043122474279348731904\"},\n  {\"0x1.efb1178484135p+401\", 309, \"10000000000000000373409337471459889719393275754491820381027730410378005080671497101378613371421126415052399029342192009216\"},\n  {\"0x1.35ceaeb2d28c0p+405\", 309, \"99999999999999983092605830803955292696544699826135736641192401589249937168415416531480248917847991520357012302290741100544\"},\n  {\"0x1.35ceaeb2d28c1p+405\", 309, \"100000000000000001440594758724527385583111862242831263013712314935498927069126131626863257625726456080505437183296233537536\"},\n  {\"0x1.83425a5f872f1p+408\", 309, \"999999999999999977709969731404129670057984297594921577392083322662491290889839886077866558841507631684757522070951350501376\"},\n  {\"0x1.83425a5f872f2p+408\", 309, \"1000000000000000124493881154768706413150521596928485788372242629432483210095525606840930628504535348165944921118995289997312\"},\n  {\"0x1.e412f0f768fadp+411\", 309, \"9999999999999999483531874467312143214394768377282087351960514613084929070487027419252537449089020883885200422613425626021888\"},\n  {\"0x1.e412f0f768faep+411\", 309, \"10000000000000000657803165854228757159135066771950601039801789067244864424132513185357050006393242615734699614997777141989376\"},\n  {\"0x1.2e8bd69aa19ccp+415\", 309, \"99999999999999992486776161899288204254467086983483846143922597222529419997579302660316349376281765375153005841365553228283904\"},\n  {\"0x1.2e8bd69aa19cdp+415\", 309, \"100000000000000011275116824089954027370311861298180065149382988489088385655907074917988550293149313084744992919515177483763712\"},\n  {\"0x1.7a2ecc414a03fp+418\", 309, \"999999999999999924867761618992882042544670869834838461439225972225294199975793026603163493762817653751530058413655532282839040\"},\n  {\"0x1.7a2ecc414a040p+418\", 309, \"1000000000000000075174486916518208627471429064352408213482909102357765925242415204664541101097758035428265955038852526326677504\"},\n  {\"0x1.d8ba7f519c84fp+421\", 309, \"9999999999999999549291066784979473595300225087383524118479625982517885450291174622154390152298057300868772377386949310916067328\"},\n  {\"0x1.d8ba7f519c850p+421\", 309, \"10000000000000000751744869165182086274714290643524082134829091023577659252424152046645411010977580354282659550388525263266775040\"},\n  {\"0x1.27748f9301d31p+425\", 309, \"99999999999999988278187853568579059876517857536991893086699469578820211690113881674597776370903434688204400735860037395056427008\"},\n  {\"0x1.27748f9301d32p+425\", 309, \"100000000000000007517448691651820862747142906435240821348290910235776592524241520466454110109775803542826595503885252632667750400\"},\n  {\"0x1.7151b377c247ep+428\", 309, \"999999999999999998217443564185241415988928868759412500436543339729940401905904649497115766142268560009777175966751665376232210432\"},\n  {\"0x1.7151b377c247fp+428\", 309, \"1000000000000000152131530268851175838953929259945403926529274864985591448578925759831966436053247510846754734110953387277122797568\"},\n  {\"0x1.cda62055b2d9dp+431\", 309, \"9999999999999999366518088823188676468029287122850159299994507296276799832366962053631754981778769796749861527090709766158759755776\"},\n  {\"0x1.cda62055b2d9ep+431\", 309, \"10000000000000000597830782460516151851749290252338090708736359498322008205751130936310560341066601403445681992244323541365884452864\"},\n  {\"0x1.2087d4358fc82p+435\", 309, \"99999999999999991202555500957231813912852864969525730182461368558677581576901282770959939099212034754106974340599870111173348163584\"},\n  {\"0x1.2087d4358fc83p+435\", 309, \"100000000000000010903558599154471420052372915041332632722331003791400915551047984893820824847817340461240101783057690514487343316992\"},\n  {\"0x1.68a9c942f3ba3p+438\", 309, \"999999999999999990829567402361276563686608849982484911984092226517669151665599636201042933986541570369602253175829982724989462249472\"},\n  {\"0x1.68a9c942f3ba4p+438\", 309, \"1000000000000000148437592187939193412802769250556940132303049308379455823458773253183930019975384016026667272715492545951501423476736\"},\n  {\"0x1.c2d43b93b0a8bp+441\", 309, \"9999999999999998962647525310145264542169126096378117797927179774005971485896954660113106823932361029753632414520324447890822855131136\"},\n  {\"0x1.c2d43b93b0a8cp+441\", 309, \"10000000000000000223511723594768599335098409300973759560478836428900264860242343595976203511843100595010152570837624953702918544949248\"},\n  {\"0x1.19c4a53c4e697p+445\", 309, \"99999999999999992148203649670699315007549827372972461504375111049848301607660324472857261615145089428049364457837845490532419930947584\"},\n  {\"0x1.19c4a53c4e698p+445\", 309, \"100000000000000012322030822224672671694418358646502729705201617528156995597186547446666808621716922472153686958914653583525950968037376\"},\n  {\"0x1.6035ce8b6203dp+448\", 309, \"999999999999999961829690841814939863449235336276785151445404123455100404055655690676191710164594560368702289580532071091311261383655424\"},\n  {\"0x1.6035ce8b6203ep+448\", 309, \"1000000000000000123220308222246726716944183586465027297052016175281569955971865474466668086217169224721536869589146535835259509680373760\"},\n  {\"0x1.b843422e3a84cp+451\", 309, \"9999999999999999295515673657285824927502456862391367223240817130898064936724137339180964349540796274981353735788091781425216117243117568\"},\n  {\"0x1.b843422e3a84dp+451\", 309, \"10000000000000000586640612700740119755462042863897304388093713545509821352053815609504775357961393589804030375857007499376802103616864256\"},\n  {\"0x1.132a095ce492fp+455\", 309, \"99999999999999982626157224225223890651347880611866174913584999992086598044603947229219155428043184231232124237329592070639473281441202176\"},\n  {\"0x1.132a095ce4930p+455\", 309, \"100000000000000003284156248920492607898701256635961169551231342625874700689878799554400131562772741268394950478432243557864849063421149184\"},\n  {\"0x1.57f48bb41db7bp+458\", 309, \"999999999999999867577570291642776341008185558166851738411142685188442185736589176942553506549890956386646894855501223680845484378371915776\"},\n  {\"0x1.57f48bb41db7cp+458\", 309, \"1000000000000000032841562489204926078987012566359611695512313426258747006898787995544001315627727412683949504784322435578648490634211491840\"},\n  {\"0x1.adf1aea12525ap+461\", 309, \"9999999999999999006303687311552062886039509598054037298313768334025031499690289406628430683654582476461074168412654660604060856295398309888\"},\n  {\"0x1.adf1aea12525bp+461\", 309, \"10000000000000000328415624892049260789870125663596116955123134262587470068987879955440013156277274126839495047843224355786484906342114918400\"},\n  {\"0x1.0cb70d24b7378p+465\", 309, \"99999999999999984774589122793531837245072631718372054355900219626000560719712531871037976946055058163097058166404267825310912362767116664832\"},\n  {\"0x1.0cb70d24b7379p+465\", 309, \"100000000000000005928380124081487003706362488767045328864850074482999577828473980652023296508018124569151792237293382948229697163514582401024\"},\n  {\"0x1.4fe4d06de5056p+468\", 309, \"999999999999999847745891227935318372450726317183720543559002196260005607197125318710379769460550581630970581664042678253109123627671166648320\"},\n  {\"0x1.4fe4d06de5057p+468\", 309, \"1000000000000000016976219238238959704141045173573106739630601035115997744067216908958262325956255112879408454231155599236459402033650892537856\"},\n  {\"0x1.a3de04895e46cp+471\", 309, \"9999999999999999154380224320567749051268538597394750219876417318024024619451619548095327920588323941303457306908878466464492349900630570041344\"},\n  {\"0x1.a3de04895e46dp+471\", 309, \"10000000000000000508222848402996879704791089448509839788449208028871961714412352270078388372553960191290960287445781834331294577148468377157632\"},\n  {\"0x1.066ac2d5daec3p+475\", 309, \"99999999999999980713061250546244445284504979165026785650181847493456749434830333705088795590158149413134549224793557721710505681023603243483136\"},\n  {\"0x1.066ac2d5daec4p+475\", 309, \"100000000000000002374543235865110535740865792782868218747346498867023742954202057256817762821608329412934596913384011607579341316989008157343744\"},\n  {\"0x1.4805738b51a74p+478\", 309, \"999999999999999850453576476100176633757771418885950722696147777681701481387046784154345890364481854130945587625116484988842728082166842262552576\"},\n  {\"0x1.4805738b51a75p+478\", 309, \"1000000000000000023745432358651105357408657927828682187473464988670237429542020572568177628216083294129345969133840116075793413169890081573437440\"},\n  {\"0x1.9a06d06e26112p+481\", 309, \"9999999999999999890870611821409196126784806260401358945180015464725302399110258148854112806457630061296658928320953898584032761523454337112604672\"},\n  {\"0x1.9a06d06e26113p+481\", 309, \"10000000000000001277205458881816625915991898331943210663398553152633589984350048456164766709270441581283861980390742947279638242225240251599683584\"},\n  {\"0x1.00444244d7cabp+485\", 309, \"99999999999999993363366729972462242111019694317846182578926003895619873650143420259298512453325054533017777074930382791057905692427399713177731072\"},\n  {\"0x1.00444244d7cacp+485\", 309, \"100000000000000015544724282938981118738333167462515810070422606902152475013980065176268974898330038852813025908047007570187593383655974344970993664\"},\n  {\"0x1.405552d60dbd6p+488\", 309, \"999999999999999977996382405657660174364823889467801080772253244969263939229107492426926049423260513969768268415537077468838432306731146395363835904\"},\n  {\"0x1.405552d60dbd7p+488\", 309, \"1000000000000000155447242829389811187383331674625158100704226069021524750139800651762689748983300388528130259080470075701875933836559743449709936640\"},\n  {\"0x1.906aa78b912cbp+491\", 309, \"9999999999999999070160382361647997691574207754048582727994641153483596148648302286926205695992445641464234721495638781756234316947997075736253956096\"},\n  {\"0x1.906aa78b912ccp+491\", 309, \"10000000000000000489767265751505205795722270035307438887450423745901682635933847561612315292472764637931130646815102767620534329186625852171022761984\"},\n  {\"0x1.f485516e7577ep+494\", 309, \"99999999999999993540817590396194393124038202103003539598857976719672134461054113418634276152885094407576139065595315789290943193957228310232077172736\"},\n  {\"0x1.f485516e7577fp+494\", 309, \"100000000000000004897672657515052057957222700353074388874504237459016826359338475616123152924727646379311306468151027676205343291866258521710227619840\"},\n  {\"0x1.38d352e5096afp+498\", 309, \"999999999999999980835596172437374590573120014030318793091164810154100112203678582976298268616221151962702060266176005440567032331208403948233373515776\"},\n  {\"0x1.38d352e5096b0p+498\", 309, \"1000000000000000162545277246339097227904071986031452381501504981983615182576228378136120296965701983510464738707067395631197433897752887331883780669440\"},\n  {\"0x1.8708279e4bc5ap+501\", 309, \"9999999999999998718097875280963410081745488308296386400449607070563910699801487058804050516065326530340444532016411713261887913912817139180431292235776\"},\n  {\"0x1.8708279e4bc5bp+501\", 309, \"10000000000000000171775323872177191180393104084305455107732328445200031262781885420082626742861173182722545959543542834786931126445173006249634549465088\"},\n  {\"0x1.e8ca3185deb71p+504\", 309, \"99999999999999992995688547174489225212045346187000138833626956204183589249936464033154810067836651912932851030272641618719051989257594860081125951275008\"},\n  {\"0x1.e8ca3185deb72p+504\", 309, \"100000000000000004625108135904199474001226272395072688491888727201272553753779650923383419882203425131989662450489690590919397689516441796634752009109504\"},\n  {\"0x1.317e5ef3ab327p+508\", 309, \"999999999999999999733403004123153744855539019118436686285840188024369679522423761672919759564567158443669378824028710020392594094129030220133015859757056\"},\n  {\"0x1.317e5ef3ab328p+508\", 309, \"1000000000000000185804116423798517725482433838447597480818028523977793111583914751916577516594435529948578361547501493575598125298270581204991032785108992\"},\n  {\"0x1.7dddf6b095ff0p+511\", 309, \"9999999999999998880909749523179353564794021275209402095665271864523156202855291675267251053466461355407239891899450398872692753716440996292182057045458944\"},\n  {\"0x1.7dddf6b095ff1p+511\", 309, \"10000000000000000369475456880582265409809179829842688451922778552150543659347219597216513109705408327446511753687232667314337003349573404171046192448274432\"},\n  {\"0x1.dd55745cbb7ecp+514\", 309, \"99999999999999988809097495231793535647940212752094020956652718645231562028552916752672510534664613554072398918994503988726927537164409962921820570454589440\"},\n  {\"0x1.dd55745cbb7edp+514\", 309, \"100000000000000000717623154091016830408061481189160311806712772146250661680488340128266606984576189330386573813296762136260081534229469225952733653677113344\"},\n  {\"0x1.2a5568b9f52f4p+518\", 309, \"999999999999999983359180223191721714560372275017470536367007614460468417501012554531477876945938741751237388344363105067534507348164573733465510370326085632\"},\n  {\"0x1.2a5568b9f52f5p+518\", 309, \"1000000000000000173895590764939294430722312570010531189967968470476774011931979328540983420144523954172264186653199235428064971301205521941960119701886468096\"},\n  {\"0x1.74eac2e8727b1p+521\", 309, \"9999999999999999833591802231917217145603722750174705363670076144604684175010125545314778769459387417512373883443631050675345073481645737334655103703260856320\"},\n  {\"0x1.74eac2e8727b2p+521\", 309, \"10000000000000001357883086565897798874899245110119190592477762992735128930457859737390823115048069116880588269914320093559588785105973323002611978355743916032\"},\n  {\"0x1.d22573a28f19dp+524\", 309, \"99999999999999995287335453651211007997446182781858083179085387749785952239205787068995699003416510776387310061494932420984963311567802202010637287727642443776\"},\n  {\"0x1.d22573a28f19ep+524\", 309, \"100000000000000007481665728323055661831810361661413965009546882534829510282787660605604053768125964371333025153260444764058913004562422887354292284947506921472\"},\n  {\"0x1.2357684599702p+528\", 309, \"999999999999999928484693987168420772305733470059469068129930887927772406304894123616740280504746200573981670431418299523701733729688780649419062882836695482368\"},\n  {\"0x1.2357684599703p+528\", 309, \"1000000000000000123593978381917935233655560332132363177417314804488469335002204100202473956740097458093113111899666497012884928817602711614917542838354527125504\"},\n  {\"0x1.6c2d4256ffcc2p+531\", 309, \"9999999999999998504409802292686149877658027252303114244149773213034936348259701329824468100106056975663290938441190205280284556945232082632196709006295628251136\"},\n  {\"0x1.6c2d4256ffcc3p+531\", 309, \"10000000000000000065284077450682265568456642148886267118448844545520511777838181142510337509988867035816342470187175785193750117648543530356184548650438281396224\"},\n  {\"0x1.c73892ecbfbf3p+534\", 309, \"99999999999999991287595123558845961539774732109363753938694017460291665200910932548988158640591809997245115511395844372456707812265566617217918448639526895091712\"},\n  {\"0x1.c73892ecbfbf4p+534\", 309, \"100000000000000003774589324822814887066163651282028976933086588120176268637538771050475113919654290478469527765363729011764432297892058199009821165792668120252416\"},\n  {\"0x1.1c835bd3f7d78p+538\", 309, \"999999999999999937849939638116397466450525159438967985375725315922685858882365002492855496964043060934899979621894213003182527093908649335762989920701551401238528\"},\n  {\"0x1.1c835bd3f7d79p+538\", 309, \"1000000000000000137641846858339900274872747866201611553286006446480839513868410418516646781429042748634490575685380367232106118863932514644433433395151811003809792\"},\n  {\"0x1.63a432c8f5cd6p+541\", 309, \"9999999999999999378499396381163974664505251594389679853757253159226858588823650024928554969640430609348999796218942130031825270939086493357629899207015514012385280\"},\n  {\"0x1.63a432c8f5cd7p+541\", 309, \"10000000000000000976834654142951997131883033248490828397039502203692087828712013353118885245360428110945724564726831363863214005099277415826993447002617590832955392\"},\n  {\"0x1.bc8d3f7b3340bp+544\", 309, \"99999999999999987391652932764487656775541389327492204364443535414407668928683046936524228593524316087103098888157864364992697772750101243698844800887746832841572352\"},\n  {\"0x1.bc8d3f7b3340cp+544\", 309, \"100000000000000000178334994858791836514563642560301392710701527770129502847789953562046870799284296099876897036220978235643807646031628623453753183252563447406133248\"},\n  {\"0x1.15d847ad00087p+548\", 309, \"999999999999999899489893451833484927233458399740540420336951338855520357125044282616287570346763120896578585177704871391229197474064067196498264773607101557544845312\"},\n  {\"0x1.15d847ad00088p+548\", 309, \"1000000000000000104076806445342351803057814451465487433877079216547069699830754788624649845638922801100959355546714693321646955446568505272576798891444167390577819648\"},\n  {\"0x1.5b4e5998400a9p+551\", 309, \"9999999999999999404072760505352583023983296100855298230449769143938302256661863838179600254051950569374547392515068357773127490685649548117139715971745147241514401792\"},\n  {\"0x1.5b4e5998400aap+551\", 309, \"10000000000000001040768064453423518030578144514654874338770792165470696998307547886246498456389228011009593555467146933216469554465685052725767988914441673905778196480\"},\n  {\"0x1.b221effe500d3p+554\", 309, \"99999999999999990767336997157383960226643264180953830087855645396318233083327270285662206135844950810475381599246526426844590779296424471954140613832058419086616428544\"},\n  {\"0x1.b221effe500d4p+554\", 309, \"100000000000000003860899428741951440279402051491350438954423829568577391016492742670197391754543170343555750902863155030391327289536708508823166797373630632400726786048\"},\n  {\"0x1.0f5535fef2084p+558\", 309, \"999999999999999933860494834742974562371950216430331518611692822307700646699603647625692432595845947170914554599698521475539380813444812793279458505403728617494385000448\"},\n  {\"0x1.0f5535fef2085p+558\", 309, \"1000000000000000143357493740096054243216090813396677260476783769063847173630251205778255402495017459700200463457564579132287164977289357383183877442068884030520150720512\"},\n  {\"0x1.532a837eae8a5p+561\", 309, \"9999999999999999338604948347429745623719502164303315186116928223077006466996036476256924325958459471709145545996985214755393808134448127932794585054037286174943850004480\"},\n  {\"0x1.532a837eae8a6p+561\", 309, \"10000000000000001014580939590254383070472626940034081121037655797126178682441216941477428085151831571943432816859913676009376081445204484652029936547358529479149975764992\"},\n  {\"0x1.a7f5245e5a2cep+564\", 309, \"99999999999999990034097500988648181343688772091571619991327827082671720239070003832128235741197850516622880918243995225045973534722968565889475147553730375141026248523776\"},\n  {\"0x1.a7f5245e5a2cfp+564\", 309, \"100000000000000003441905430931245280917713770297417747470693647675065097962631447553892265814744827318497179085147422915077831721209019419643357959500300321574675254607872\"},\n  {\"0x1.08f936baf85c1p+568\", 309, \"999999999999999953972206729656870211732987713739100709830741553196290713284945813208338477706166412373726001850053663010587168093173889073910282723323583537144858509574144\"},\n  {\"0x1.08f936baf85c2p+568\", 309, \"1000000000000000168497133608738423804917387685032638749500594682674584756861928912756562958882918041203714772520508506051096899076950702733972407714468702680083242606919680\"},\n  {\"0x1.4b378469b6731p+571\", 309, \"9999999999999999110672213538405594930961077194803931018967709273006319045695491932986935814708160866077282477159626944024852218964185263418978577250945597085571816901050368\"},\n  {\"0x1.4b378469b6732p+571\", 309, \"10000000000000000826871628571058023676436276965152235336326534308832671394311356729372731664122173896717192642523265688348930066834399772699475577180106550229078889679814656\"},\n  {\"0x1.9e056584240fdp+574\", 309, \"99999999999999987674323305318751091818660372407342701554959442658410485759723189737097766448253582599493004440868991951600366493901423615628791772651134064568704023452975104\"},\n  {\"0x1.9e056584240fep+574\", 309, \"100000000000000001403918625579970521782461970570129136093830042945021304548650108108184133243565686844612285763778101906192989276863139689872767772084421689716760605683089408\"},\n  {\"0x1.02c35f729689ep+578\", 309, \"999999999999999849284042412665072058259000527747854146471853226010883220019378060628804930891911617504691481762871699606818419373090804007799965727644765395390927070069522432\"},\n  {\"0x1.02c35f729689fp+578\", 309, \"1000000000000000068957567536844582937679826098352437099093782830596656320642208754566186799616905285426599982929417458880300383900478261195703581718577367397759832385751351296\"},\n  {\"0x1.4374374f3c2c6p+581\", 309, \"9999999999999999371534524623368764100273307559896873275206250678451924602685103382037576783819090846734548822294900033162112051840457868829614121240178061963384891963422539776\"},\n  {\"0x1.4374374f3c2c7p+581\", 309, \"10000000000000001128922725616804851135639912124733536896181687515138109407667748933536631733619040190109816831627266107349967768059557526332843049167638877982336134488877170688\"},\n  {\"0x1.945145230b377p+584\", 309, \"99999999999999986685792442259943292861266657339622078268160759437774506806920451614379548038991111093844416185619536034869697653528180058283225500691937355558043949532406874112\"},\n  {\"0x1.945145230b378p+584\", 309, \"100000000000000000744898050207431989144199493858315387235964254131263985246781616026371987637390705840846560260278464628372543383280977318309056924111623883709653889736043921408\"},\n  {\"0x1.f965966bce055p+587\", 309, \"999999999999999894976135638494410321178532246433607400617214583764724024948926844967780359586710300432448450005513217535702667994787395102883917853758746611883659375731342835712\"},\n  {\"0x1.f965966bce056p+587\", 309, \"1000000000000000007448980502074319891441994938583153872359642541312639852467816160263719876373907058408465602602784646283725433832809773183090569241116238837096538897360439214080\"},\n  {\"0x1.3bdf7e0360c35p+591\", 309, \"9999999999999998724815666657784284071258397080036981062687289922551408594451489819085924562292709488372450194860589317860981148271829194868425875762872481668410834714055235600384\"},\n  {\"0x1.3bdf7e0360c36p+591\", 309, \"10000000000000000524381184475062837195473800154429724610566137243318061834753718863820956830887857615988724636416932177829345401680187244151732297960592357271816907060120777654272\"},\n  {\"0x1.8ad75d8438f43p+594\", 309, \"99999999999999998045549773481514159457876389246726271914145983150114005386328272459269439234497983649422148597943950338419997003168440244384097290815044070304544781216945608327168\"},\n  {\"0x1.8ad75d8438f44p+594\", 309, \"100000000000000012442073916019742584451599613841868220297176761716247231308746104817149697383259168670352344130394693218166911030435304638650548668396803075131793359985469944758272\"},\n  {\"0x1.ed8d34e547313p+597\", 309, \"999999999999999894076352879585771044616424544896411028843275160104340698328775730445412843452412726368640312784735046105718485868083216078242264642659886674081956339558310064685056\"},\n  {\"0x1.ed8d34e547314p+597\", 309, \"1000000000000000009248546019891598444566210341657546615907521388633406505708118389308454908642502206536081877044340989143693798086218131232373875663313958712699944969706504756133888\"},\n  {\"0x1.3478410f4c7ecp+601\", 309, \"9999999999999999171107915076469365246063817042486381462561244058101538598046442622180212564904306224021286256366562347133135483117101991090685868467907010818055540655879490029748224\"},\n  {\"0x1.3478410f4c7edp+601\", 309, \"10000000000000001013863005321362603645260389790664550855589183714566591516115925163988885607945737906700351284520257435740740478607260633556791644798372163435943358738250605092929536\"},\n  {\"0x1.819651531f9e7p+604\", 309, \"99999999999999991711079150764693652460638170424863814625612440581015385980464426221802125649043062240212862563665623471331354831171019910906858684679070108180555406558794900297482240\"},\n  {\"0x1.819651531f9e8p+604\", 309, \"100000000000000006453119872723839559654210752410289169769835957832735809325020286556271509993374515701645382788895184180192194795092289050635704895322791329123657951217763820802932736\"},\n  {\"0x1.e1fbe5a7e7861p+607\", 309, \"999999999999999946594872951565228338993526868219488856544571440313594706493755982886960025179093529324993666087115356131035228239552737388526279268078143523691759154905886843985723392\"},\n  {\"0x1.e1fbe5a7e7862p+607\", 309, \"1000000000000000064531198727238395596542107524102891697698359578327358093250202865562715099933745157016453827888951841801921947950922890506357048953227913291236579512177638208029327360\"},\n  {\"0x1.2d3d6f88f0b3cp+611\", 309, \"9999999999999998286585471758920610814449462123360860153907833022998313197373091002112049504244419016335335042852788704601485085281825842706955095829283737561469387976341354799421194240\"},\n  {\"0x1.2d3d6f88f0b3dp+611\", 309, \"10000000000000000173566684169691286935226752617495305612368443231218527385476241124924130700318845059398697631682172475335672600663748292592247410791680053842186513692689376624118857728\"},\n  {\"0x1.788ccb6b2ce0cp+614\", 309, \"99999999999999997961704416875371517110712945186684165206763211895744845478556111003617144611039598507860251139162957211888350975873638026151889477992007905860430885494197722591793250304\"},\n  {\"0x1.788ccb6b2ce0dp+614\", 309, \"100000000000000013057554116161536926076931269139759728874448093561506558983381311986113794179635006852367151849798027377761851098929017625234227997691178436106167891224981897189374558208\"},\n  {\"0x1.d6affe45f818fp+617\", 309, \"999999999999999979617044168753715171107129451866841652067632118957448454785561110036171446110395985078602511391629572118883509758736380261518894779920079058604308854941977225917932503040\"},\n  {\"0x1.d6affe45f8190p+617\", 309, \"1000000000000000100383841763043038442836876043491446161409111172283542162824162717896144642659159251834657717076710133445871510743179417054177602937513443300570204900788250622698582966272\"},\n  {\"0x1.262dfeebbb0f9p+621\", 309, \"9999999999999999071569656121801212080692814968920789464627446869617922299624001453201875281811380250249693879805812353226907091680705581859236698853640605134247712274342131878495422251008\"},\n  {\"0x1.262dfeebbb0fap+621\", 309, \"10000000000000001003838417630430384428368760434914461614091111722835421628241627178961446426591592518346577170767101334458715107431794170541776029375134433005702049007882506226985829662720\"},\n  {\"0x1.6fb97ea6a9d37p+624\", 309, \"99999999999999986851159038200753776111576258757220550347347138989744224339004763080499610528553377966303172216135545569805454885304878641227288327493418395599568449276340570087973407686656\"},\n  {\"0x1.6fb97ea6a9d38p+624\", 309, \"100000000000000002309309130269787154892983822485169927543056457815484218967945768886576179686795076111078238543825857419659919011313587350687602971665369018571203143144663564875896666980352\"},\n  {\"0x1.cba7de5054485p+627\", 309, \"999999999999999899427890566145604518678577715028104257864890027548922232647929642417149243602017175952581854816736079397763477105066203831193512563278085201938953880500051690455580595453952\"},\n  {\"0x1.cba7de5054486p+627\", 309, \"1000000000000000023093091302697871548929838224851699275430564578154842189679457688865761796867950761110782385438258574196599190113135873506876029716653690185712031431446635648758966669803520\"},\n  {\"0x1.1f48eaf234ad3p+631\", 309, \"9999999999999998746948504188351511126283256130633852543517551174277382412416240331274267329488304589209417486924315804379963345034522698960570091326029642051843383703107348987949033805840384\"},\n  {\"0x1.1f48eaf234ad4p+631\", 309, \"10000000000000000725591715973187783610303424287811372824568343983972101724920689074452068181743241951740625976868675721161334753163637413771490365780039321792212624518252692320803210995433472\"},\n  {\"0x1.671b25aec1d88p+634\", 309, \"99999999999999991426771465453187656230872897620693565997277097362163262749171300799098274999392920617156591849131877877362376266603456419227541462168315779999172318661364176545198692437590016\"},\n  {\"0x1.671b25aec1d89p+634\", 309, \"100000000000000007255917159731877836103034242878113728245683439839721017249206890744520681817432419517406259768686757211613347531636374137714903657800393217922126245182526923208032109954334720\"},\n  {\"0x1.c0e1ef1a724eap+637\", 309, \"999999999999999914267714654531876562308728976206935659972770973621632627491713007990982749993929206171565918491318778773623762666034564192275414621683157799991723186613641765451986924375900160\"},\n  {\"0x1.c0e1ef1a724ebp+637\", 309, \"1000000000000000040900880208761398001286019738266296957960021713442094663491997727554362004538245197373563261847757813447631532786297905940174312186739777303375354598782943738754654264509857792\"},\n  {\"0x1.188d357087712p+641\", 309, \"9999999999999998636144484328400679867178126713831911407778706776934478130915991201656310481762028096907669811487431649040206546179292274931158555956605099986382706217459209761309199883223171072\"},\n  {\"0x1.188d357087713p+641\", 309, \"10000000000000000662275133196073022890814778906781692175574718614061870706920546714670378554471083956139627305190456203824330868103505742897540916997511012040520808812168041334151877325366493184\"},\n  {\"0x1.5eb082cca94d7p+644\", 309, \"99999999999999994465967438754696170766327875910118237148971115117854351613178134068619377108456504406004528089686414709538562749489776621177115003729674648080379472553427423904462708600804999168\"},\n  {\"0x1.5eb082cca94d8p+644\", 309, \"100000000000000010675012629696074914955421093453716483291339209814873492221214578172731921690128951279860188039310611147811557324883484364908173892056921944513484293311098076487204128137951576064\"},\n  {\"0x1.b65ca37fd3a0dp+647\", 309, \"999999999999999977077764769429719196041465194188378863774447340572581797347854228894418860247909937807756600796112539971931616645685181699233267813951241073670004367049615544210109925082343145472\"},\n  {\"0x1.b65ca37fd3a0ep+647\", 309, \"1000000000000000106750126296960749149554210934537164832913392098148734922212145781727319216901289512798601880393106111478115573248834843649081738920569219445134842933110980764872041281379515760640\"},\n  {\"0x1.11f9e62fe4448p+651\", 309, \"9999999999999999511432924639235132053389160461186216699466583890573511723749959183278387889172340228095875448767138256706948253250552493092635735926276453993770366538373425000777236538229086224384\"},\n  {\"0x1.11f9e62fe4449p+651\", 309, \"10000000000000001586190709079731611309593092306766792205689700011791961721578624028604793595626413427949399922319035400805891558900947084290211273632164107937207783595355268531368138238983848067072\"},\n  {\"0x1.56785fbbdd55ap+654\", 309, \"99999999999999995114329246392351320533891604611862166994665838905735117237499591832783878891723402280958754487671382567069482532505524930926357359262764539937703665383734250007772365382290862243840\"},\n  {\"0x1.56785fbbdd55bp+654\", 309, \"100000000000000011712391521916323154583523059376506771044450767875482717220128910595395124543355987879786950276086559719861028977708681660506961660909865771485203001839588998252499578988328956985344\"},\n  {\"0x1.ac1677aad4ab0p+657\", 309, \"999999999999999884751043361827625869140390227060043253747518673178360772444478643277393806310703680414274761723053117059528639544242622390941156386039240473187039308013923507098814799398756243472384\"},\n  {\"0x1.ac1677aad4ab1p+657\", 309, \"1000000000000000017535541566019400541537441865177200086145798104936341572305513193378283771523764365204900328030374534281861011105867876227585990799216050325567033999660761493056632508247061001404416\"},\n  {\"0x1.0b8e0acac4eaep+661\", 309, \"9999999999999998847510433618276258691403902270600432537475186731783607724444786432773938063107036804142747617230531170595286395442426223909411563860392404731870393080139235070988147993987562434723840\"},\n  {\"0x1.0b8e0acac4eafp+661\", 309, \"10000000000000000972062404885344653449756728480474941855847657639911300522221339234388177506516007760792756678147673846152604340428430285295728914471221362369950308146488642846313231335560438561636352\"},\n  {\"0x1.4e718d7d7625ap+664\", 309, \"99999999999999996973312221251036165947450327545502362648241750950346848435554075534196338404706251868027512415973882408182135734368278484639385041047239877871023591066789981811181813306167128854888448\"},\n  {\"0x1.4e718d7d7625bp+664\", 309, \"100000000000000013969727991387583324014272937224498437195221518215368390817766497947110253951978019521227584903311023812640679294256310975729923845933871538975662911597585244013782480038750137870188544\"},\n  {\"0x1.a20df0dcd3af0p+667\", 309, \"999999999999999901747459131964173027207212836739039328294498440443382314826691065690307721857975448067474834210390258463987183104130654882031695190925872134291678628544718769301415466131339252487684096\"},\n  {\"0x1.a20df0dcd3af1p+667\", 309, \"1000000000000000037718785293056550291741793714171007924670336578563554653884390444993619046236149589293075414109087389699655531583234914810756005630018925423128793192791080866922220799992003324610084864\"},\n  {\"0x1.0548b68a044d6p+671\", 309, \"9999999999999999017474591319641730272072128367390393282944984404433823148266910656903077218579754480674748342103902584639871831041306548820316951909258721342916786285447187693014154661313392524876840960\"},\n  {\"0x1.0548b68a044d7p+671\", 309, \"10000000000000001193015809897119766504625422406301890824958394614356580573190100725756058408630540740284357620483056684410565406706974707679905918934747573964310619313388981254947040003084017678835253248\"},\n  {\"0x1.469ae42c8560cp+674\", 309, \"99999999999999998876910787506329447650934459829549922997503484884029261182361866844442696946000689845185920534555642245481492613075738123641525387194542623914743194966239051177873087980216425864602058752\"},\n  {\"0x1.469ae42c8560dp+674\", 309, \"100000000000000016281240536126153737511360812140841903333610766563411320581747387395266546466406979922062794761588875043647041218401083394518237123398453444885893859189773399673336170714381427096269357056\"},\n  {\"0x1.98419d37a6b8fp+677\", 309, \"999999999999999988769107875063294476509344598295499229975034848840292611823618668444426969460006898451859205345556422454814926130757381236415253871945426239147431949662390511778730879802164258646020587520\"},\n  {\"0x1.98419d37a6b90p+677\", 309, \"1000000000000000128003745864021888795392755416785835072663893102275349087018702832851017765623257219066874199161822284840139314973360143403428947761576712806916637263450665299742435541675484268499358973952\"},\n  {\"0x1.fe52048590672p+680\", 309, \"9999999999999999052283250816881378851792981072012977243617198967792587267065681698004724917620567060828502090557969050236202928251957239362070375381666542984859087613894256390005080826781722527340175556608\"},\n  {\"0x1.fe52048590673p+680\", 309, \"10000000000000000166160354728550133402860267619935663985128064995273039068626355013257451286926569625748622041088095949318798038992779336698179926498716835527012730124200454693714718121768282606166882648064\"},\n  {\"0x1.3ef342d37a407p+684\", 309, \"99999999999999986067324092522138770313660664528439025470128525568004065464414123719036343698981660348604541103459182906031648839556284004276265549348464259679976306097717770685212259087870984958094927200256\"},\n  {\"0x1.3ef342d37a408p+684\", 309, \"100000000000000003889357755108838843130737249295202013334302382007691294289384896763079965607877701387326460311941213291353170611409437561654018367221268940354434586262616943544566455807655946219322240663552\"},\n  {\"0x1.8eb0138858d09p+687\", 309, \"999999999999999896317308250394787848770759814817916230429632968559415112294082783278450680807608685563489249451555889830959531939269147157518161129230251958148679621306976052570830984318279772103403898929152\"},\n  {\"0x1.8eb0138858d0ap+687\", 309, \"1000000000000000038893577551088388431307372492952020133343023820076912942893848967630799656078777013873264603119412132913531706114094375616540183672212689403544345862626169435445664558076559462193222406635520\"},\n  {\"0x1.f25c186a6f04cp+690\", 309, \"9999999999999999818630698308109481982927274216983785721776674794699138106539424938898600659703096825493544616522696356805028364441642842329313746550197144253860793660984920822957311285732475861572950035529728\"},\n  {\"0x1.f25c186a6f04dp+690\", 309, \"10000000000000000959240852713658286643220175642056616945083801606839120751337554413717392461872443451971747445865546301465605757840244670001489926894056643817026123591538467885955979875798713382291498097180672\"},\n  {\"0x1.37798f428562fp+694\", 309, \"99999999999999989061425747836704382546929530769255207431309733449871519907009213590435672179676195243109823530484164010765664497227613801915728022751095446033285297165420831725583764136794858449981115862089728\"},\n  {\"0x1.37798f4285630p+694\", 309, \"100000000000000007311188218325485257111615953570420507004223762444111242223779285187536341014385741266761068799969763125334902791605243044670546908252847439043930576054277584733562461577854658781477884848504832\"},\n  {\"0x1.8557f31326bbbp+697\", 309, \"999999999999999927113782419344605574598668153294882673458925392487194643703632279098558059466181044478400725843812838336795121561031396504666917998514458446354143529431921823271795036250068185162804696593727488\"},\n  {\"0x1.8557f31326bbcp+697\", 309, \"1000000000000000073111882183254852571116159535704205070042237624441112422237792851875363410143857412667610687999697631253349027916052430446705469082528474390439305760542775847335624615778546587814778848485048320\"},\n  {\"0x1.e6adefd7f06aap+700\", 309, \"9999999999999999563134023721266549739021664297767471527755878388779781994104643936539191296017163181162427182749897969201059028320356032930746282153172616351711759756540926280845609521557638656931995269719916544\"},\n  {\"0x1.e6adefd7f06abp+700\", 309, \"10000000000000000731118821832548525711161595357042050700422376244411124222377928518753634101438574126676106879996976312533490279160524304467054690825284743904393057605427758473356246157785465878147788484850483200\"},\n  {\"0x1.302cb5e6f642ap+704\", 309, \"99999999999999990959401044767537593501656918740576398586892792465272451027953301036534141738485988029569553038510666318680865279842887243162229186843277653306392406169861934038413548670665077684456779836676898816\"},\n  {\"0x1.302cb5e6f642bp+704\", 309, \"100000000000000009647157814548049209055895815688969665349556758155373926680325854351965226625228563157788428194463919811999765293285579587743163725597071694149293171752051249118583734850310313223909471278765965312\"},\n  {\"0x1.7c37e360b3d35p+707\", 309, \"999999999999999984345037526797422397233524775199337052919583787413130412889023223627065756931830180808571031008919677160084252852199641809946030023447952696435527124027376600704816231425231719002378564135125254144\"},\n  {\"0x1.7c37e360b3d36p+707\", 309, \"1000000000000000133847091685041515321667435950786483187020895512933942218108003650150514436025770781834322032256545705106635452959741180566593506333478305023178733248684891121346177720862393603318000095671837786112\"},\n  {\"0x1.db45dc38e0c82p+710\", 309, \"9999999999999999544446266951486038123467425400819078260993214423089680518452271383223760211130420606034208307593944715707740128306913340586165347614418822310868858990958736965765439335377993421392542578277827477504\"},\n  {\"0x1.db45dc38e0c83p+710\", 309, \"10000000000000000740462700217438781518938714805516247333803708227256174960204114795411349643881945414240216317574952939280149729167245650639345158094661640924814507988218853130896331250875288495917514830571527733248\"},\n  {\"0x1.290ba9a38c7d1p+714\", 309, \"99999999999999990660396936451049407652789096389402106318690169014230827417515340183487244380298106827518051036015414262787762879627804165648934234223216948652905993920546904997130825691790753915825536773603473752064\"},\n  {\"0x1.290ba9a38c7d2p+714\", 309, \"100000000000000009796659868706293301980329726864556811483658069880894738485544834778488675304322503758814179195711545839946316493393121126499811201907102046476036377876708763639225096339747475108225092810302677843968\"},\n  {\"0x1.734e940c6f9c5p+717\", 309, \"999999999999999868331443500000006287872809702943711652856965888408980452039094412644869581954932274412588254040761879473560521568747407734787588406864399290882799171293145332687119715621994096773456255662636329336832\"},\n  {\"0x1.734e940c6f9c6p+717\", 309, \"1000000000000000021421546958041957442493134746744949294176709095342291740583330369404881029347127449862957279318330932090828950478869943421594604148335480073467842242942440201823873880805647866312652703956229962072064\"},\n  {\"0x1.d022390f8b837p+720\", 309, \"9999999999999999601855055748251769806450047292244542376488118125689672251656359867008764503902493796828096692073033110439215789148209291468717978517470477604338250142827222541691722147321863584969741246387925089779712\"},\n  {\"0x1.d022390f8b838p+720\", 309, \"10000000000000000826575883412587379043412647642654443507046063781156162560010247521088856083040055200431048894293585531377363220429189576963174104449239123865018594716021581494785755468791093741283312832736674151661568\"},\n  {\"0x1.221563a9b7322p+724\", 309, \"99999999999999988670225591496504042642724870819986016981533507324097780666440272745607095564199569546663253707407016578763273303796211201720443029584092898479300433989106071698353021544403254911815982945786756526505984\"},\n  {\"0x1.221563a9b7323p+724\", 309, \"100000000000000008265758834125873790434126476426544435070460637811561625600102475210888560830400552004310488942935855313773632204291895769631741044492391238650185947160215814947857554687910937412833128327366741516615680\"},\n  {\"0x1.6a9abc9424febp+727\", 309, \"999999999999999965084388885482519417592855130626093842171043595190833186399051537317196816706799625297221478016185520727674168639944850288849622355474122345476546392575499689981548348018063279122228410984187505225498624\"},\n  {\"0x1.6a9abc9424fecp+727\", 309, \"1000000000000000121848654826517477399924067975478561186882460639090543945868349157039448538836407484958399359900416230607757039843910326832140006474740509066843630497944377635977584613166124739130365574036827385146376192\"},\n  {\"0x1.c5416bb92e3e6p+730\", 309, \"9999999999999999964372420736895110140590976995965873111133270039707753382929110612616471611327211972294570543930316627036907428807379455975076991793273996897499632136492752791807556010476755711238558435947154812096741376\"},\n  {\"0x1.c5416bb92e3e7p+730\", 309, \"10000000000000001218486548265174773999240679754785611868824606390905439458683491570394485388364074849583993599004162306077570398439103268321400064747405090668436304979443776359775846131661247391303655740368273851463761920\"},\n  {\"0x1.1b48e353bce6fp+734\", 309, \"99999999999999984594354677029595135102113336853821866019036664182705300920238534632828550788829765195472628778417018121881118652493108811594893042483166843723756247249515245102456078650553656951604416706418119648563167232\"},\n  {\"0x1.1b48e353bce70p+734\", 309, \"100000000000000004660180717482069756840508580994937686142098045801868278132308629957276771221419571232103397659598548986531726166600689809136062209749264344058743012736731622189948720589505523832645973577156024278435495936\"},\n  {\"0x1.621b1c28ac20bp+737\", 309, \"999999999999999886075198851200900594497923856820450300436489405065378963626525536977181948753477264027987825546533242948112401553146250111031268759363863437907536003469585205199546070383440303278127280805657005745376329728\"},\n  {\"0x1.621b1c28ac20cp+737\", 309, \"1000000000000000046601807174820697568405085809949376861420980458018682781323086299572767712214195712321033976595985489865317261666006898091360622097492643440587430127367316221899487205895055238326459735771560242784354959360\"},\n  {\"0x1.baa1e332d728ep+740\", 309, \"9999999999999999181805205159248599892793562474462356126333876156560397271658376894962991014456209536865970557564236923315533735757183797070971394269896194384435148282491314085395342974857632902877937717988376531531720556544\"},\n  {\"0x1.baa1e332d728fp+740\", 309, \"10000000000000000466018071748206975684050858099493768614209804580186827813230862995727677122141957123210339765959854898653172616660068980913606220974926434405874301273673162218994872058950552383264597357715602427843549593600\"},\n  {\"0x1.14a52dffc6799p+744\", 309, \"99999999999999996954903517948319502092964807244749211214842475260109694882873713352688654575305085714037182409224841134505892881183378706080253249519082903930108094789640533388351546084948006950326015738792668900564521713664\"},\n  {\"0x1.14a52dffc679ap+744\", 309, \"100000000000000017502309383371653514753081537245251811020857330038132583548033490964923632298277047095547089743554728739908114975629541647562410476799566744273134542648550103525944011430434718636512569974428283241553786306560\"},\n  {\"0x1.59ce797fb817fp+747\", 309, \"999999999999999928454223448636526995609414612446486912536395043045051171498417578302416590307106934377352009423588636134254484622941461177838218040629861358615028052178586193608330530158506646130887048916655460323666687950848\"},\n  {\"0x1.59ce797fb8180p+747\", 309, \"1000000000000000092833470372023199096890348452450507710984513881269234280819695799200296412090882625429431268098227736977472261378510764709695475858873732081359239635049862754709070252922400339620379482801740375051580804694016\"},\n  {\"0x1.b04217dfa61dfp+750\", 309, \"9999999999999999613300728333138614158656013804472910722260188106898877933626732224819925546638620725877678611585164563028980399740553218842096696042786355031638703687528415058284784747112853848287855356936724432692495112994816\"},\n  {\"0x1.b04217dfa61e0p+750\", 309, \"10000000000000000928334703720231990968903484524505077109845138812692342808196957992002964120908826254294312680982277369774722613785107647096954758588737320813592396350498627547090702529224003396203794828017403750515808046940160\"},\n  {\"0x1.0e294eebc7d2bp+754\", 309, \"99999999999999988242803431008825880725075313724536108897092176834227990088845967645101024020764974088276981699468968789815350713138205618891818585152157755624664880897462875650012340778461641195382916742883168419985073526276096\"},\n  {\"0x1.0e294eebc7d2cp+754\", 309, \"100000000000000009283347037202319909689034845245050771098451388126923428081969579920029641209088262542943126809822773697747226137851076470969547585887373208135923963504986275470907025292240033962037948280174037505158080469401600\"},\n  {\"0x1.51b3a2a6b9c76p+757\", 309, \"999999999999999924509121522475246865178672200286390413373640190927670776874706901000867474584296317792102107215397297714017257980807797893073643852992008461269166974189675556141912776812173197487139230503413422370196749149011968\"},\n  {\"0x1.51b3a2a6b9c77p+757\", 309, \"1000000000000000092833470372023199096890348452450507710984513881269234280819695799200296412090882625429431268098227736977472261378510764709695475858873732081359239635049862754709070252922400339620379482801740375051580804694016000\"},\n  {\"0x1.a6208b5068394p+760\", 309, \"9999999999999999918388610622944277578633427011520373324179896670642961784527024602806390495869308408470337715685294734193992593398889846197223766553446979093051960385337504355687757672562640543404353314227442034427503713670135808\"},\n  {\"0x1.a6208b5068395p+760\", 309, \"10000000000000001264983401419327895432326837028833311705066886193375469816086935788401821995921998869568971002747938248301632620580513580730198422600500768053772541672219001944225017481444457680470275332614057655878576158030168064\"},\n  {\"0x1.07d457124123cp+764\", 309, \"99999999999999988411127779858373832956786989976700226194703050524569553592790956543300452958271560395914310860351799229078805716535908585708440417158039479244754953558323062848579498254571868337516156995181495372666457581821100032\"},\n  {\"0x1.07d457124123dp+764\", 309, \"100000000000000009956644432600511718615881550253707240288894882888289682097749535512827356959114607773492443453354095454801046151441888338236034913910900102616284254148427024265175655196680942530570909289367345315883616691581616128\"},\n  {\"0x1.49c96cd6d16cbp+767\", 309, \"999999999999999884111277798583738329567869899767002261947030505245695535927909565433004529582715603959143108603517992290788057165359085857084404171580394792447549535583230628485794982545718683375161569951814953726664575818211000320\"},\n  {\"0x1.49c96cd6d16ccp+767\", 309, \"1000000000000000056475411020520841414840626381983058374700565164155456563967578197189219761589459982979768169347536362096565980644606923877305160145603279779419783940304062319818564238082591276919599588305301753272401848696295129088\"},\n  {\"0x1.9c3bc80c85c7ep+770\", 309, \"9999999999999999185841044429711589466224211962102134844977374370276477415358432917842475759840644797632681207523216662519436418612086534611285553663849717898419964165273969667523488336530932020840491736225123136358120303938278260736\"},\n  {\"0x1.9c3bc80c85c7fp+770\", 309, \"10000000000000000564754110205208414148406263819830583747005651641554565639675781971892197615894599829797681693475363620965659806446069238773051601456032797794197839403040623198185642380825912769195995883053017532724018486962951290880\"},\n  {\"0x1.01a55d07d39cfp+774\", 309, \"99999999999999997374062707399103193390970327051935144057886852787877127050853725394623645022622268104986814019040754458979257737456796162759919727807229498567311142603806310797883499542489243201826933949562808949044795771481474727936\"},\n  {\"0x1.01a55d07d39d0p+774\", 309, \"100000000000000019436671759807052388305883156775590326490339289128326538639931310259419194719485548619626821794275105794118831942800519429348176492482158776899757146408072767288477964251208935175515000298809119290899166699876243210240\"},\n  {\"0x1.420eb449c8842p+777\", 309, \"999999999999999841364972759543336764420226292177420345984153909836074800974071744757463152045042997962028093539001436578955132142505622028069656690022719315678435403212464369035268207172574280176140941400150227439321732144446136385536\"},\n  {\"0x1.420eb449c8843p+777\", 309, \"1000000000000000017865845178806930323739528929966661805443773400559670093686692423675827549619949242079148155740876247260071725785255408160775710807422153542338003433646596020960023924842331815965645472194120710174156699571604284243968\"},\n  {\"0x1.9292615c3aa53p+780\", 309, \"9999999999999999119653217272487741881479473472931169297680017061255129180591200163248089110750054956088761184197513608514017695996055364811520783369824930063422626153861170298051704942404772944919427537177384205332557191153093955289088\"},\n  {\"0x1.9292615c3aa54p+780\", 309, \"10000000000000000531660196626596490356033894575245100973356972987043891522292165594595004291349304909025721681812512093962950445138053653873169216309020403876699170397334223513449750683762833231235463783529148067211236930570359138156544\"},\n  {\"0x1.f736f9b3494e8p+783\", 309, \"99999999999999994020546131433094915763903576933939556328154082464128816489313932495174721468699049466761532837205133056038042458244550226238504699576640248260779350025557809411313140906763850021826347864477369777082931390365469918625792\"},\n  {\"0x1.f736f9b3494e9p+783\", 309, \"100000000000000005316601966265964903560338945752451009733569729870438915222921655945950042913493049090257216818125120939629504451380536538731692163090204038766991703973342235134497506837628332312354637835291480672112369305703591381565440\"},\n  {\"0x1.3a825c100dd11p+787\", 309, \"999999999999999940205461314330949157639035769339395563281540824641288164893139324951747214686990494667615328372051330560380424582445502262385046995766402482607793500255578094113131409067638500218263478644773697770829313903654699186257920\"},\n  {\"0x1.3a825c100dd12p+787\", 309, \"1000000000000000120942354671656868962382001670435578817768191183142249744630862900164152357803694488643546272066771136697843816472621283262276046411983423130707191163420128905684081263961470216866716118177799472091300320549064642593292288\"},\n  {\"0x1.8922f31411455p+790\", 309, \"9999999999999999040580826428657651966904425891201589123842107529410958489455946099092661860636496958724291396331073693328877462044103460624068471125229983529879139676226679317989414380888721568885729507381685429067351125745727105048510464\"},\n  {\"0x1.8922f31411456p+790\", 309, \"10000000000000000486475973287265010404848153099971055159735310397418651127357734700791903005570128910531738945888832142428584597165509708623196466454966148714674320981543085810557013220039375302073350623645891623631119178909006652304785408\"},\n  {\"0x1.eb6bafd91596bp+793\", 309, \"99999999999999999081179145438220670296706622164632687453780292502155740721970192601122065475966761298087599260657287627887017431169472094235452683230716826407562484594165232135299736843791138087983021771402091458056119576436948334022754304\"},\n  {\"0x1.eb6bafd91596cp+793\", 309, \"100000000000000010648340320307079537800256439834788415740925915446217281825184501414715994635435816912547179657119355220684674512140722078228476645868606147885923935036696484075840527556996367953483990701515741014566264001743184712072953856\"},\n  {\"0x1.33234de7ad7e2p+797\", 309, \"999999999999999828871535006218182557917368774264146678517764203804695831774701602620905646527100834378441867056103929979702975178097221166452191355376717763378564539746214794185426298453038162762816652692429820789419173810082174047524749312\"},\n  {\"0x1.33234de7ad7e3p+797\", 309, \"1000000000000000013946113804119924437974165856986638331112094170909680489426130543638408513078605724209795153399497011464465488473637220910340574757582946907032347746826714825234078949864321840610832155574248213693581484614981956096327942144\"},\n  {\"0x1.7fec216198ddbp+800\", 309, \"9999999999999999029013665253788793099400876073531433395554961906466896948352731790279067931477027903109831815934611625736079804963132210640075447162592094208400778225784148066048873590175516339020228538451571779510840981320420868670460264448\"},\n  {\"0x1.7fec216198ddcp+800\", 309, \"10000000000000000509610295637002728139855252735311366616309601643306774209564163318419090863889067021760658106681756277614179911327452208591182514380241927357631043882428148314438094801465785761804352561506118922744139467759619125060885807104\"},\n  {\"0x1.dfe729b9ff152p+803\", 309, \"99999999999999993251329913304315801074917514058874200397058898538348724005950180959070725179594357268399970740840405561116998262359962102302968606061220608382468313571129481157267178324335702235770533430624812081575006786082605199485453729792\"},\n  {\"0x1.dfe729b9ff153p+803\", 309, \"100000000000000005096102956370027281398552527353113666163096016433067742095641633184190908638890670217606581066817562776141799113274522085911825143802419273576310438824281483144380948014657857618043525615061189227441394677596191250608858071040\"},\n  {\"0x1.2bf07a143f6d3p+807\", 309, \"999999999999999885134206960780312089454635087411784140906440513804611167700736000690226517958758320887173266104495426751070779219941381088594259909647411423049314634698686803624216704482068400828613365568502612232284516294771707790360919932928\"},\n  {\"0x1.2bf07a143f6d4p+807\", 309, \"1000000000000000074650575649831695774632795300119615593163034400120115457135799236292149453307499328074479031320129942191467592834574340826335964513506590066150788638749118835418037019527222886944981240519484646566146722558989084608335389392896\"},\n  {\"0x1.76ec98994f488p+810\", 309, \"9999999999999999230374806985905888264902671299533504313577592910677120255877486478106111050285065223246344191476223298391501419428679730361426008304192471516696094355087732099829807674910992980518869405586990190990569575476151831539558138249216\"},\n  {\"0x1.76ec98994f489p+810\", 309, \"10000000000000000746505756498316957746327953001196155931630344001201154571357992362921494533074993280744790313201299421914675928345743408263359645135065900661507886387491188354180370195272228869449812405194846465661467225589890846083353893928960\"},\n  {\"0x1.d4a7bebfa31aap+813\", 309, \"99999999999999992303748069859058882649026712995335043135775929106771202558774864781061110502850652232463441914762232983915014194286797303614260083041924715166960943550877320998298076749109929805188694055869901909905695754761518315395581382492160\"},\n  {\"0x1.d4a7bebfa31abp+813\", 309, \"100000000000000004432795665958347438500428966608636256080197937830963477082618911859584178365170076692451010888562841972100410265623306726829729177688912148325455279810104971033102576911999816916636238052732752107272876955671430431745947427930112\"},\n  {\"0x1.24e8d737c5f0ap+817\", 309, \"999999999999999874521290314193434603084658115500145579580071256170942927492372459496518833579228824484684143252419893886408557657521935343280724451831297419035632090471862609843762766839539749606096764571247618309588232743975534688554349643169792\"},\n  {\"0x1.24e8d737c5f0bp+817\", 309, \"1000000000000000068586051851782051496707094173312964986690823395758019319873877212752887919376339615844485246833229637697374894798906086114728229966183096349571541470619505010400634769445777943389257468521053221467463131958534128550160206370177024\"},\n  {\"0x1.6e230d05b76cdp+820\", 309, \"9999999999999999521471949292288813605336325386252733424243721120057734844449743607990664678980731410286045846847437914107950925140755956518597266575720169912499958425309195700665115678820350271193610461511698595727381924297989722331966923339726848\"},\n  {\"0x1.6e230d05b76cep+820\", 309, \"10000000000000001073990041592997748754315813848755288681129738236754345983501781634041617365357617741164454675493915864595681622271829162690177310690534561356787233466490334905120091699670255821458896093110143420990381118014458473224813777155784704\"},\n  {\"0x1.c9abd04725480p+823\", 309, \"99999999999999992109683308321470265755404276937522223728665176967184126166393360027804741417053541441103640811181423240104047857145413152842812577527572916236425034170729678597741204746503691611405533351920096306747820855546959721533975525765152768\"},\n  {\"0x1.c9abd04725481p+823\", 309, \"100000000000000004529828046727141746947240184637542665783753313900757015278809664236212362908068632088130911440353246844005893434193998802215452930446088047790723234500178792233381012913302936013527818404707654908851814405278709728676750356293615616\"},\n  {\"0x1.1e0b622c774d0p+827\", 309, \"999999999999999921096833083214702657554042769375222237286651769671841261663933600278047414170535414411036408111814232401040478571454131528428125775275729162364250341707296785977412047465036916114055333519200963067478208555469597215339755257651527680\"},\n  {\"0x1.1e0b622c774d1p+827\", 309, \"1000000000000000119819148897705446356623417292575549310168061960609007487462594467612569358026776864763472738178563410063470007804231501918390371421971971267233021546978482604147648978133824826548011894363801900701142105351177597329624152546106933248\"},\n  {\"0x1.658e3ab795204p+830\", 309, \"9999999999999999210968330832147026575540427693752222372866517696718412616639336002780474141705354144110364081118142324010404785714541315284281257752757291623642503417072967859774120474650369161140553335192009630674782085554695972153397552576515276800\"},\n  {\"0x1.658e3ab795205p+830\", 309, \"10000000000000000800746857348072976168095423879354838955917799224215742423028622941456649692555285746929854721652135745309841019576760278403979222926327228462592673059242454405136015920000672444612205821948817131744093259920359973067672730884158521344\"},\n  {\"0x1.bef1c9657a685p+833\", 309, \"99999999999999992109683308321470265755404276937522223728665176967184126166393360027804741417053541441103640811181423240104047857145413152842812577527572916236425034170729678597741204746503691611405533351920096306747820855546959721533975525765152768000\"},\n  {\"0x1.bef1c9657a686p+833\", 309, \"100000000000000004827911520448877862495844246422343156393075429187162764617507655537214145823852994263659565935453370610499537728043164857800396298916132410948026391308085570960636368309306117879178753245974556315302310250472271728848176952226298724352\"},\n  {\"0x1.17571ddf6c813p+837\", 309, \"999999999999999895660376658959887464073162830405580371957831265231883984761705009259228605356936508765924557863270337660249498829658628118512958332498610172941047627432585001251621720339432063578508893731092043050369229765618973200711352404729235767296\"},\n  {\"0x1.17571ddf6c814p+837\", 309, \"1000000000000000099152028052998409011920202342162715294588395300751542199979533737409779075865727753926819359851621495586577336764022655397834297874715562088326669341630279279057944337344270883862880412035963403187241060084423965317738575228107571068928\"},\n  {\"0x1.5d2ce55747a18p+840\", 309, \"9999999999999999363587069377675917736425707327570073564839440723358156278052707548893386994586947577981035182609405692455150664165314335743772262409420005560181719702721238568128862437403998276353831973920663150777435958293799716241167969694049028276224\"},\n  {\"0x1.5d2ce55747a19p+840\", 309, \"10000000000000000991520280529984090119202023421627152945883953007515421999795337374097790758657277539268193598516214955865773367640226553978342978747155620883266693416302792790579443373442708838628804120359634031872410600844239653177385752281075710689280\"},\n  {\"0x1.b4781ead1989ep+843\", 309, \"99999999999999993635870693776759177364257073275700735648394407233581562780527075488933869945869475779810351826094056924551506641653143357437722624094200055601817197027212385681288624374039982763538319739206631507774359582937997162411679696940490282762240\"},\n  {\"0x1.b4781ead1989fp+843\", 309, \"100000000000000006659336382995224556426467602028157370696750505506839688554468114090569100058432115470107619153348531031836488269452441103314288354796084978186496986735864819460893271862349667261738096910718398556534156723341516657901421957636703742066688\"},\n  {\"0x1.10cb132c2ff63p+847\", 309, \"999999999999999988452569694641453289891412847766833896677368465428848130901034909295879619908945316559292587569958465674654992927728624557883489163749540246356891129106733591931304833693638565628182306078113383272782784390994049606075766012189756664840192\"},\n  {\"0x1.10cb132c2ff64p+847\", 309, \"1000000000000000196828020722136899354886781307806140057451066037800978143284091526922043301709947551604048864806030051391214698972517388491908540854979699007711767764445172532404979193506593517599378740822301656052939538637450361533911642183329172013711360\"},\n  {\"0x1.54fdd7f73bf3bp+850\", 309, \"9999999999999998634272990781441856508941917717432502002131499220055701234712009387201814108283439755324388212283155142447191693008553661974684581490114449895439651479036702276471002178058655944454644452316004196046887318431202624493742403095061074555174912\"},\n  {\"0x1.54fdd7f73bf3cp+850\", 309, \"10000000000000000301276599001405425028904865397746951288321079799032741333776462328211123562691457635682438430171727828179669341366863773446884995019955719986278664561744213800260397056562295560224215930269510378288141352402853119916429412464176397346144256\"},\n  {\"0x1.aa3d4df50af0ap+853\", 309, \"99999999999999989676737124254345702129345072534953918593694153358511092545248999754036759991650433313959982558608696795936872226802156842691246641960827039136074540955782045812288811537593838676085587479067054324951381252255327235782798049688841391133687808\"},\n  {\"0x1.aa3d4df50af0bp+853\", 309, \"100000000000000003012765990014054250289048653977469512883210797990327413337764623282111235626914576356824384301717278281796693413668637734468849950199557199862786645617442138002603970565622955602242159302695103782881413524028531199164294124641763973461442560\"},\n  {\"0x1.0a6650b926d66p+857\", 309, \"999999999999999843423255779504622828654636399579476808778874955057845642282427503428069697375447760968142218613652642015929437520555644859802053186653349748453896990911180089361627479263821919056229587496158345417793683435460456504301996197076723582025859072\"},\n  {\"0x1.0a6650b926d67p+857\", 309, \"1000000000000000056799717631659959599209893702659726317411141269166906774962677479877261307539674049653972646503389945789686576510419339128243706118473032320081290665497741564406670023712287789874734736674207136744674199783831719918405933396323484899269935104\"},\n  {\"0x1.4cffe4e7708c0p+860\", 309, \"9999999999999999287738405203667575368767393208115766122317814807014700953545274940077463414411382764424743897695475635254322931165011225671787143593812227771048544607458046793796444970432082673836316471673778619485458899748089618699435710767754281089234894848\"},\n  {\"0x1.4cffe4e7708c1p+860\", 309, \"10000000000000000994750100020910269533209451632757762191375945319887190014987274751670996295725193073911387320813374065444380043083920779819320367048369688344067694004150538594156785326019809640384357665098168950100503030535059726012267208361728371627187503104\"},\n  {\"0x1.a03fde214caf0p+863\", 309, \"99999999999999992877384052036675753687673932081157661223178148070147009535452749400774634144113827644247438976954756352543229311650112256717871435938122277710485446074580467937964449704320826738363164716737786194854588997480896186994357107677542810892348948480\"},\n  {\"0x1.a03fde214caf1p+863\", 309, \"100000000000000006533477610574617307003210399478293629775643192173126922026988747893522897194624310120140586361897943794063686207001388689898137223574581962294638641248120402340847172549022642470747494264132908839774942043776657045497009088429335535195969814528\"},\n  {\"0x1.0427ead4cfed6p+867\", 309, \"999999999999999928773840520366757536876739320811576612231781480701470095354527494007746341441138276442474389769547563525432293116501122567178714359381222777104854460745804679379644497043208267383631647167377861948545889974808961869943571076775428108923489484800\"},\n  {\"0x1.0427ead4cfed7p+867\", 309, \"1000000000000000147271337456973822389925322799165752109071222186349148695219103469891718550249305996056764747928638562589759603442121545498062966961564577730451305583522443629825768062558437319101780919925699824267271538715541135605986002768804111697781423341568\"},\n  {\"0x1.4531e58a03e8bp+870\", 309, \"9999999999999998413748417457239315956573059294699064134960051984423986554086971036541574579178711885967582465059111638997013689862529533948250133185078807957662740116351490992011950708371166466963719380640490770210556304785160923755265983999639546733803159420928\"},\n  {\"0x1.4531e58a03e8cp+870\", 309, \"10000000000000000161728392950095834780961727121532468109675577629605415353003578843613352249644053642881905330331839631511632172467492917395324154002545647584434349098564602595580939232492998880708913562707066468760361494711018313643605437535869015444666630275072\"},\n  {\"0x1.967e5eec84e2ep+873\", 309, \"99999999999999987633444125558106197214507928600657449299031571134602723138702925979559301132717802373504470381136572374999373863835222106376649373485721758830170619127941133127257484131955329497127582170538059099205173427703324017329338747068854404759758535917568\"},\n  {\"0x1.967e5eec84e2fp+873\", 309, \"100000000000000001617283929500958347809617271215324681096755776296054153530035788436133522496440536428819053303318396315116321724674929173953241540025456475844343490985646025955809392324929988807089135627070664687603614947110183136436054375358690154446666302750720\"},\n  {\"0x1.fc1df6a7a61bap+876\", 309, \"999999999999999932269800471352470574525516656465243420181212531991832952952360709621889896782068959956303035500093019510461530081711049334072862401016156456358397678710230902586782474091451932211122035531511013345645500354660676649720249983847887046345216426508288\"},\n  {\"0x1.fc1df6a7a61bbp+876\", 309, \"1000000000000000044140518902895287779286391397382581274563006173283444396083023609274483667691850832398819698877547611031397112968428705874685599733334034192471780653571870045215197739635249206690814463183771858052833032509915549602573975010166573043840478561173504\"},\n  {\"0x1.3d92ba28c7d14p+880\", 309, \"9999999999999998875215130987353436926211667600983082784284950754751883757000955497608523884181562109792963701491111829020872969270239867178277674680890053619130444887655752455354163678739330224192450644706066754627704874925587274685787599733204126473471115726422016\"},\n  {\"0x1.3d92ba28c7d15p+880\", 309, \"10000000000000000665146625892038512202385663455660488454393649015417666847091561892050024218738072068873230315530385293355842295457722371828081471997976097396944572485441978737408807927440086615867529487142240269942705389409665241931447200154303102433395309881065472\"},\n  {\"0x1.8cf768b2f9c59p+883\", 309, \"99999999999999988752151309873534369262116676009830827842849507547518837570009554976085238841815621097929637014911118290208729692702398671782776746808900536191304448876557524553541636787393302241924506447060667546277048749255872746857875997332041264734711157264220160\"},\n  {\"0x1.8cf768b2f9c5ap+883\", 309, \"100000000000000003071603269111014971471508642847250073203719093632845102290734406131617241518267700770571769927225306004888484302202258708981207125345588886413817469658847334809978790776999353375325137186550055668797052865128496484823152800700833072414104710501367808\"},\n  {\"0x1.f03542dfb8370p+886\", 309, \"999999999999999973438224854160227305877518561122823750593712591987145964024444656694044404476868689015149167622996309190165824584023146941018349739309135463248122613459314107074039291811569329219648848907543004197890512187794469896370420793533163493423472892065087488\"},\n  {\"0x1.f03542dfb8371p+886\", 309, \"1000000000000000087993840528060072123552654295822177713480669280669756081790243465938300425888485326396286230921509810907603861460022027238605792767602642265028226779717632589125536523728417738286853894823458109178050545114775459800092635220483497954858621317962268672\"},\n  {\"0x1.362149cbd3226p+890\", 309, \"9999999999999999734382248541602273058775185611228237505937125919871459640244446566940444044768686890151491676229963091901658245840231469410183497393091354632481226134593141070740392918115693292196488489075430041978905121877944698963704207935331634934234728920650874880\"},\n  {\"0x1.362149cbd3227p+890\", 309, \"10000000000000001567272099323999790141577357366417900912128432938793221524497227514848540387354553088249684689006179119380666835856213554171582585845787463460962892794726236783564348628785267837271769223730071721661465648709640537423259638766536986317197103735005773824\"},\n  {\"0x1.83a99c3ec7eafp+893\", 309, \"99999999999999990012263082286432662256543169091523721434606031123027548865433341877772055077343404109122144711194766809100548098338386355056238620120129111010885594705399027856108106338478634741663761952135733701058809111452663635798820356028494943810497789949089153024\"},\n  {\"0x1.83a99c3ec7eb0p+893\", 309, \"100000000000000004675381888545612798918960543133041028684136487274401643939455589461036825818030333693907688813404495028932616818466243033147431327741697981638738927986463793558699752023835231102266007829372867138519293326106230343475263802678137754874196788463928344576\"},\n  {\"0x1.e494034e79e5bp+896\", 309, \"999999999999999929448868435382686895890266438998271828845121223533023678802377913944250092254807900260792535316367124530669618423639576906744771616444428851364562613616119809966264354755499540137842111275831603885509059543833769773341090453584235060232375896520569913344\"},\n  {\"0x1.e494034e79e5cp+896\", 309, \"1000000000000000046753818885456127989189605431330410286841364872744016439394555894610368258180303336939076888134044950289326168184662430331474313277416979816387389279864637935586997520238352311022660078293728671385192933261062303434752638026781377548741967884639283445760\"},\n  {\"0x1.2edc82110c2f9p+900\", 309, \"9999999999999999529098585253973751145501342374646995204443699533752222309208135100774737254399069875964494058799026896824009283758441475916906799486389390443691279468658234350904109878520700943148057046794110173854458342872794765056233999682236635579342942941443126198272\"},\n  {\"0x1.2edc82110c2fap+900\", 309, \"10000000000000001405977792455148808638290766251961210532383597921128106478682982791432627909206996862817043703881872108962514079934807130712579466061950205884056506128634524360835840526246345277305144519080463253849400322348451303638818760853390915395496414751342542716928\"},\n  {\"0x1.7a93a2954f3b7p+903\", 309, \"99999999999999991537227438137387396469434575991841521388557198562770454753131655626431591234374844785939841297824578543963083245231683449577722661712772273556182341366629763489177637489755720763166395523368395578554699469776634573397170474480057796161122485794632428945408\"},\n  {\"0x1.7a93a2954f3b8p+903\", 309, \"100000000000000006552261095746787856411749967010355244012076385661777528108930437151694716472838260680760238458487340241071121614642608687943103994317258797079104154646440083568631482671560875436423095301659220218514235305581886882057848563849292034690350260273827761094656\"},\n  {\"0x1.d9388b3aa30a5p+906\", 309, \"999999999999999945402341696592674884578976541955442659132610359825718694242914119314842162820675279649039207299571308833846909191138684972507989282336695782607667040225918275050684065261167516978177354790265605065466066369376850351293060923539046438669680406904714953752576\"},\n  {\"0x1.d9388b3aa30a6p+906\", 309, \"1000000000000000065522610957467878564117499670103552440120763856617775281089304371516947164728382606807602384584873402410711216146426086879431039943172587970791041546464400835686314826715608754364230953016592202185142353055818868820578485638492920346903502602738277610946560\"},\n  {\"0x1.27c35704a5e67p+910\", 309, \"9999999999999999213782878444176341486712719163258207029349796604673073768736360688744211624391338142173265718425108901184740478000812045911233791501695173449709921389782217629235579129702792695009666351450002856415308090320884466574359759805482716570229159677380024223137792\"},\n  {\"0x1.27c35704a5e68p+910\", 309, \"10000000000000001135707186618179600359329089213627963525160252553345979158278604723977891654914655376710276554989942398414569389285410476422002602075069448460643913489597938599405671312973852493186523923071228410330128677303956762082926555244744699101970314810717026738241536\"},\n  {\"0x1.71b42cc5cf601p+913\", 309, \"99999999999999995981677400789769932612359931733321583285118877944076548466448094957909476304960015890806678857380756006307062602577317320133875536163700284518967198097453618232695975663570046546450378657742479671982722077174989256760731188933351130765773907040474247261585408\"},\n  {\"0x1.71b42cc5cf602p+913\", 309, \"100000000000000011357071866181796003593290892136279635251602525533459791582786047239778916549146553767102765549899423984145693892854104764220026020750694484606439134895979385994056713129738524931865239230712284103301286773039567620829265552447446991019703148107170267382415360\"},\n  {\"0x1.ce2137f743381p+916\", 309, \"999999999999999929065985077113647184161737396527299728918221484261998998431805045015355882561227083155474615188770224107393363445219598313166454392463014445014728107377484646804238281703363508693674065431485187857190091380020735839470243162305319587149880588271350432374194176\"},\n  {\"0x1.ce2137f743382p+916\", 309, \"1000000000000000052069140800249855752009185079750964144650090664977064943362508663270311404514719386165843308728919567930102413767433897865855658269158968045714503601765690788895124181432711335776992950015243623307738608946937362752018518070418086469181314516804918593340833792\"},\n  {\"0x1.20d4c2fa8a030p+920\", 309, \"9999999999999998060628293539774386163142897133036353131863523035469330535011014267604003606077347801451059216486208802846843131230052987604772505157670608443149526129892785047133523819740156816103551808477267524066415738131041089269219682541925527051184466597377822714075545600\"},\n  {\"0x1.20d4c2fa8a031p+920\", 309, \"10000000000000000028678785109953723248702060064614983783573429926910385653902272159683291957333224649616958313128598304010187936385481780447799767184805866054345934040104083320587698215409722049436653961817402491275192019201707119869992081071729797163687409453914913289541779456\"},\n  {\"0x1.6909f3b92c83dp+923\", 309, \"99999999999999996350686867959178558315902274782992576532314485486221746301240205812674342870820492799837784938001204037775189753543960218791943147793788145321066524580618236658968633362758090027700335311493754978334367629875739137498376013657689431411868208826074951744485326848\"},\n  {\"0x1.6909f3b92c83ep+923\", 309, \"100000000000000012095090800520613255000375578235621621745993740617750187252370268949308649680867507585164977711140320047081948194787390561536161244010870206210637787862308622846602028528114611894365152538214834716004577878441067382304555201896123592311891751678371676348215197696\"},\n  {\"0x1.c34c70a777a4cp+926\", 309, \"999999999999999932018060814468916189790076140924667674895786344599160581110141931853474815088110898427723463833733808359138380652952741502430995285503717331431522719242801594214419543296867856543673718661495390308003255801626734885371401760100025992318635002556156068237393526784\"},\n  {\"0x1.c34c70a777a4dp+926\", 309, \"1000000000000000057973292274960393763265862568545700036605220385651388108719182436946549269568487016710341006018846736433592448182900184244384740055240373818548092825496324683715486704619720031476992256475264028209364937790149360843820835266007499279518823345374529865067232493568\"},\n  {\"0x1.1a0fc668aac6fp+930\", 309, \"9999999999999998312538756460757341310094469988278417855282391117573785590229095277790152515038100038016294300856434658995751266289947873088679994697143921417382666342399831226135658142385861165970188884104804799869139102108086341186118549553740473625584843283014570307735223533568\"},\n  {\"0x1.1a0fc668aac70p+930\", 309, \"10000000000000000327822459828620982485707052830214935642633335774409426031973743359279343786724117930538174975818241508187016346769106956959939911012930425211247788042456200658152732723551495964903285489125103006290926013924448356521309485648260046220787856768108551057012647002112\"},\n  {\"0x1.6093b802d578bp+933\", 309, \"99999999999999987155954971343300695452169865566657214127525800489409136785780248940879907693753036165206704358487960288340042823857796898629319779603012221761556906824111051125390730586189881257568082051088644411534964844713587442531567367726443881446254459800333664575907082272768\"},\n  {\"0x1.6093b802d578cp+933\", 309, \"100000000000000003278224598286209824857070528302149356426333357744094260319737433592793437867241179305381749758182415081870163467691069569599399110129304252112477880424562006581527327235514959649032854891251030062909260139244483565213094856482600462207878567681085510570126470021120\"},\n  {\"0x1.b8b8a6038ad6ep+936\", 309, \"999999999999999903804088967318825213331499981137556425872873119403461614925716858712626137284506647932417134384268512470460669526244514328233356457082706278317411015442012422166180499160548969358610366191211215418098239036197666670678728654776751975985792813764840337747509598224384\"},\n  {\"0x1.b8b8a6038ad6fp+936\", 309, \"1000000000000000032782245982862098248570705283021493564263333577440942603197374335927934378672411793053817497581824150818701634676910695695993991101293042521124778804245620065815273272355149596490328548912510300629092601392444835652130948564826004622078785676810855105701264700211200\"},\n  {\"0x1.137367c236c65p+940\", 309, \"9999999999999999553953517735361344274271821018911312812290573026184540102343798495987494338396687059809772796632907678097570555865109868753376103147668407754403581309634554796258176084383892202112976392797308495024959839786965342632596166187964530344229899589832462449290116390191104\"},\n  {\"0x1.137367c236c66p+940\", 309, \"10000000000000001617604029984053712838099105849054307026537940354784235914690318131432426200603169381752178607793797891669425998275768770637546257455033787639321465930492277094643660455497502236220467316338093858400869637486920046335831684748752572681717785398568698736550198021980160\"},\n  {\"0x1.585041b2c477ep+943\", 309, \"99999999999999991412234152856228705615063640528827139694410995604646009398744945688985079659553905954212916344007296353831994673829780883765420722861953317774200043854630103365810792101611701952914782080891514223497778802469744018919490624758069218767323224280852151918381000638332928\"},\n  {\"0x1.585041b2c477fp+943\", 309, \"100000000000000007921438250845767654125681919169971093408389934233443575897517102772544534557205764529752162833294418062406838213115052098838781957320876356853543120821491881752894667070520582225774709469217797130505057184069381648545374773244373557467226310750742042216461653692645376\"},\n  {\"0x1.ae64521f7595ep+946\", 309, \"999999999999999980159157920520442850193109519852847211800025710561650359982538085224088616186146493844286149397221450372619320895438893697947652166455225334059372746413748147206443420891752540620587530362220273863006901551095990707698442841525909542472844588688081080376132618600579072\"},\n  {\"0x1.ae64521f7595fp+946\", 309, \"1000000000000000112232790704436754438278055748981998841511857219592030891972715341892564255367361362448600121311518424041218069209721063418534542042126609646694117362148642374303114420643023582803466949468830537119065128603893091744705516029416344252072069280447200202760777843035078656\"},\n  {\"0x1.0cfeb353a97dap+950\", 309, \"9999999999999998216707985798208689444911740448978652561458278997251937215943253772219178491686886515191093831000650819703008229183002900332433843156495641588976792075318750746904382211902272900011322274342879579557370290877394694632899550160573878909537749585771381335145583492791795712\"},\n  {\"0x1.0cfeb353a97dbp+950\", 309, \"10000000000000000329886110340869674854270880115045078636847583141738025727786089878914788718586324412860117381629402398400588202211517615861824081167237790591132705927077058380451118207922609574937392980048643791654301923722148311225012721166820834263125344653917287293299907083743789056\"},\n  {\"0x1.503e602893dd1p+953\", 309, \"99999999999999990619792356152730836086553963154052229916140006550463726206803882148974225824466616742587032512521514511820402183944087865441899383607925011898391576160220738003230766103104075699817505566251852643961429440152961412697448185630726610509727876130297437184073129291725930496\"},\n  {\"0x1.503e602893dd2p+953\", 309, \"100000000000000007525217352494018719361427080482583638519254439706352434301546571002539107639662119923939220917551527141401041968172205589677021287693862203915638886974287199071604654071266769099226071211897966340736882502910990345434353553680702253338428636675464684849307718019341877248\"},\n  {\"0x1.a44df832b8d45p+956\", 309, \"999999999999999872387073568844732594315793396883459481955171199192859845878553443782612494614275161063165948315155119859042742270984643205948750027907375734949421139974074457895559885094715370199357924371226299046063388276013556261500671120207314819439877240212639876510262115462027411456\"},\n  {\"0x1.a44df832b8d46p+956\", 309, \"1000000000000000007630473539575035660514778335511710750780086664439969510636494954611131549135839186513983455555395220895687860544809584999829725260594873271087399626486606146442550988840016917394626449536395208620267012778077787723395914064607119962069483324573977857832138825282954985472\"},\n  {\"0x1.06b0bb1fb384bp+960\", 309, \"9999999999999998453383935746986719810759964091578092281901881061434379129269651416169086837099623559730024468671070996517137186162196548471725549813698762277218254426715681201861616643456550607603042193381925171312226633756007099691216225313273537909139560233403722802458867734978418966528\"},\n  {\"0x1.06b0bb1fb384cp+960\", 309, \"10000000000000000617278335278671568869943723109630112583100528505388133765396715589425391709444647966943104584514912613103459078543395617173821153536698722855425910210916188218613474303381375362727338596024627724499484625789034803081540112423670420191213257583185130503608895092113260150784\"},\n  {\"0x1.485ce9e7a065ep+963\", 309, \"99999999999999988861628156533236896225967158951884963421416105502251300564950642508203478115686284411726404918398393198344015646384363622121446705582987543928597855835557826052119881754415155586279014739104656819496782321626126403692810027353529143655542997033600043426888732064053872033792\"},\n  {\"0x1.485ce9e7a065fp+963\", 309, \"100000000000000006172783352786715688699437231096301125831005285053881337653967155894253917094446479669431045845149126131034590785433956171738211535366987228554259102109161882186134743033813753627273385960246277244994846257890348030815401124236704201912132575831851305036088950921132601507840\"},\n  {\"0x1.9a742461887f6p+966\", 309, \"999999999999999957860902350346284132153551878096514283852517773229033154005572478626236537071903625148082612890986863714202457020042006419681526374965874177788623543449994485057258262661745948026767632275613049896960078961318150545418464661067991669581788285529005480705688196068853638234112\"},\n  {\"0x1.9a742461887f7p+966\", 309, \"1000000000000000096350143920374114471941312455251843583129231209642073450717704585714640048901985187209719740304992727175727058132438746816615645013237871654793913513638826934129377152896934732354722602044746013300944590451431923562399193436133392135634504915915015573579289946925483474026496\"},\n  {\"0x1.008896bcf54f9p+970\", 309, \"9999999999999997916738124663128877244082391855101191247204616495333847979510139501201523228758057506741180599941798275603729356851659179433605840090394772053822755792233955461707155943795194068332216685526534938121786651731816229250415901309895111103185283290657933692573660950408978352832512\"},\n  {\"0x1.008896bcf54fap+970\", 309, \"10000000000000000132565989783574162680686561089586460035632031477942492726904253214615979418039362499727374638565892090988122974650007025784551738302746731685907395315255274646861058187558214617579496201832662352585538835573636597522107561710941518560028749376834095178551288964115055725510656\"},\n  {\"0x1.40aabc6c32a38p+973\", 309, \"99999999999999992462348437353960485060448933957923525202610654848990348279466077292501969423268405025328970231162545648343655275306678872441733790178059478330735395060467469727994972900530063978805843953102113868000379620369084502134308975505229555772913629423636305841602377586326247764393984\"},\n  {\"0x1.40aabc6c32a39p+973\", 309, \"100000000000000010188971358317522768553282287833805675510029974709859506258618986999817618937518844969218522540155296171418804217693461643249300975876875155387412511244638023209226190850634228372784080083551133183710397091103647448307842258713600815427661358113045597729423401695974866745819136\"},\n  {\"0x1.90d56b873f4c6p+976\", 309, \"999999999999999924623484373539604850604489339579235252026106548489903482794660772925019694232684050253289702311625456483436552753066788724417337901780594783307353950604674697279949729005300639788058439531021138680003796203690845021343089755052295557729136294236363058416023775863262477643939840\"},\n  {\"0x1.90d56b873f4c7p+976\", 309, \"1000000000000000066436467741248103118547156170586292454485461107376856746627884050583544890346687569804406120783567460668037744292161050890877875387371120199760770880078039125129799472606133954939884328574613293205683935969567348590731356020719265634967118123751637393518591968740451429495341056\"},\n  {\"0x1.f50ac6690f1f8p+979\", 309, \"9999999999999999813486777206230041577815560719820581330098483720446847883279500839884297726782854580737362697004022581572770293687044935910015528960168049498887207223940204684198896264456339658487887951484580004902758521100414464490983962613190835886243290260424727924570510530141380583845003264\"},\n  {\"0x1.f50ac6690f1f9p+979\", 309, \"10000000000000000947990644147898027721356895367877038949773320191542473993945287061152499295694882737146294044779558615049579825999799033241699828844892252830514542659727120106997694213263006179702495063833317241108199639227426493046090092738526596504147144896546922605391056073158892198656212992\"},\n  {\"0x1.3926bc01a973bp+983\", 309, \"99999999999999998134867772062300415778155607198205813300984837204468478832795008398842977267828545807373626970040225815727702936870449359100155289601680494988872072239402046841988962644563396584878879514845800049027585211004144644909839626131908358862432902604247279245705105301413805838450032640\"},\n  {\"0x1.3926bc01a973cp+983\", 309, \"100000000000000016286929643128988194074816961567109135215782220741998496603447587939134202370420996309916528534448802351356655453874514916407104087757267748294909439211992693606769729825470060924312593312425595828314643101036337101791537708137280528748894576782202394138833833989693991675429388288\"},\n  {\"0x1.87706b0213d09p+986\", 309, \"999999999999999872436306494222877488001587945768638201521064070819504681704034606746682422062730755058478860313950798943503314266680100247159860107083281430052496520558476587831205023360193979812186512362979225814553504769848291707808207769286850569305558980974742103098278680884456943362624192512\"},\n  {\"0x1.87706b0213d0ap+986\", 309, \"1000000000000000017652801462756379714374878780719864776839443139119744823869255243069012222883470359078822072829219411228534934402712624705615450492327979456500795456339201761949451160807447294527656222743617592048849967890105831362861792425329827928397252374398383022243308510390698430058459037696\"},\n  {\"0x1.e94c85c298c4cp+989\", 309, \"9999999999999999595662034753429788238255624467393741467120915117996487670031669885400803025551745174706847878231119663145222863482996149222332143382301002459214758820269116923021527058285459686414683385913622455551313826420028155008403585629126369847605750170289266545852965785882018353801250996224\"},\n  {\"0x1.e94c85c298c4dp+989\", 309, \"10000000000000000757393994501697806049241951147003554069667947664398408807353434975979441432117662006869593578353268561425475824571256344889976866464258586670801150306514918315967496157863486204138441068958729385425685531382088472248832262877470188720339297317678393899013204421931950247367929757696\"},\n  {\"0x1.31cfd3999f7afp+993\", 309, \"99999999999999986662764669548153739894665631237058913850832890808749507601742578129378923002990117089766513181334005445210204946123879926882163649167349350899456456312724758086647517786230384722356772394775369116518164624503799012160606438304513147494189124523779646633247748770420728389479079870464\"},\n  {\"0x1.31cfd3999f7b0p+993\", 309, \"100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054016\"},\n  {\"0x1.7e43c8800759bp+996\", 309, \"999999999999999903803069407426113968898218766118103141789833949572356552411722264192305659040010509526872994217248819197070144216063125530186267630296136203765329090687113225440746189048800695790727969805197112921161540803823920273299782054992133678869364753954248541633605124057805104488924519071744\"},\n  {\"0x1.7e43c8800759cp+996\", 309, \"1000000000000000052504760255204420248704468581108159154915854115511802457988908195786371375080447864043704443832883878176942523235360430575644792184786706982848387200926575803737830233794788090059368953234970799945081119038967640880074652742780142494579258788820056842838115669472196386865459400540160\"},\n  {\"0x1.ddd4baa009302p+999\", 309, \"9999999999999999335434075769817752248594687291161143444150379827602457335271594505111188022480979804302392841403758309930446200199225865392779725411942503595819407127350057411001629979979981746444561664911518503259454564508526643946547561925497354420113435609274102018745072331406833609642314953654272\"},\n  {\"0x1.ddd4baa009303p+999\", 309, \"10000000000000000525047602552044202487044685811081591549158541155118024579889081957863713750804478640437044438328838781769425232353604305756447921847867069828483872009265758037378302337947880900593689532349707999450811190389676408800746527427801424945792587888200568428381156694721963868654594005401600\"},\n  {\"0x1.2aa4f4a405be1p+1003\", 309, \"99999999999999988595886650569271721532146878831929642021471152965962304374245995240101777311515802698485322026337261211948545873374744892473124468375726771027536211745837771604509610367928220847849105179362427047829119141560667380048679757245757262098417746977035154548906385860807815060374033329553408\"},\n  {\"0x1.2aa4f4a405be2p+1003\", 309, \"100000000000000007629703079084894925347346855150656811701601734206211380288125794484142188964691784076639747577138548761372210387844799938291815611350519830750167649856488981626536368095414607314235151058373458986890825155659063617715863205282622390509284183439858617103083735673849899204570498157510656\"},\n  {\"0x1.754e31cd072d9p+1006\", 309, \"999999999999999847891233648661470807691068835681842080854450367179124891914700353912936949808806064228544369161770037020638129704807338833093862397807681590830099241237075296001042588224309435545718960035602206600167779387409881325152430676383842364162444596844704620380709158981993982315347403639619584\"},\n  {\"0x1.754e31cd072dap+1006\", 309, \"1000000000000000000161765076786456438212668646231659438295495017101117499225738747865260243034213915253779773568180337416027445820567779199643391541606026068611150746122284976177256650044200527276807327067690462112661427500197051226489898260678763391449376088547292320814127957486330655468919122263277568\"},\n  {\"0x1.d2a1be4048f90p+1009\", 309, \"9999999999999999392535525055364621860040287220117324953190771571323204563013233902843309257440507748436856118056162172578717193742636030530235798840866882774987301441682011041067710253162440905843719802548551599076639682550821832659549112269607949805346034918662572406407604380845959862074904348138143744\"},\n  {\"0x1.d2a1be4048f91p+1009\", 309, \"10000000000000000610699776480364506904213085704515863812719128770699145421501541054461895603243770556638739353307444575741831722668719553462632031991253638597235713480763688482477422747721569639692426738805257643176588867453119191870248852943967318023641486852283274009874954768880653247303478097127407616\"},\n  {\"0x1.23a516e82d9bap+1013\", 309, \"99999999999999993925355250553646218600402872201173249531907715713232045630132339028433092574405077484368561180561621725787171937426360305302357988408668827749873014416820110410677102531624409058437198025485515990766396825508218326595491122696079498053460349186625724064076043808459598620749043481381437440\"},\n  {\"0x1.23a516e82d9bbp+1013\", 309, \"100000000000000013415983273353644379307167647951549871284361430903247099365945253454330474107257282415598692944582140176397004400243696672220697718814856920905847607042126949473232502444570468800016509005592812696365583783944976073966686973485829389546187580124556949719553650017014692784406223465209659392\"},\n  {\"0x1.6c8e5ca239028p+1016\", 309, \"999999999999999861291040414336469543176969619010226008309262296372260241358071732580741399612641955118765084749534143455432389522994257585350220962461935904874831773666973747856549425664459851618054736334425973085267220421335152276470127823801795414563694568114532338018850013250375609552861714878501486592\"},\n  {\"0x1.6c8e5ca239029p+1016\", 309, \"1000000000000000017216064596736454828831087825013238982328892017892380671244575047987920451875459594568606138861698291060311049225532948520696938805711440650122628514669428460356992624968028329550689224175284346730060716088829214255439694630119794546505512415617982143262670862918816362862119154749127262208\"},\n  {\"0x1.c7b1f3cac7433p+1019\", 309, \"9999999999999999860310597602564577717002641838126363875249660735883565852672743849064846414228960666786379280392654615393353172850252103336275952370615397010730691664689375178569039851073146339641623266071126720011020169553304018596457812688561947201171488461172921822139066929851282122002676667750021070848\"},\n  {\"0x1.c7b1f3cac7434p+1019\", 309, \"10000000000000001107710791061764460002235587486150467667406698508044529291764770372322278832331501782385107713289967796232382450470561630819049695116611434972713065592709012878572585445501694163102699168797993709169368134893256514428214347139105940256706031241200520264089633727198808148476736186715027275776\"},\n  {\"0x1.1ccf385ebc89fp+1023\", 309, \"99999999999999981139503267596847425176765179308926185662298078548582170379439067165044410288854031049481594743364161622187121841818187648603927125262209438639553681654618823985640760188731793867961170022535129351893330180773705244319986644578003569234231285691342840034082734135647456849389933411990123839488\"},\n  {\"0x1.1ccf385ebc8a0p+1023\", 309, \"100000000000000001097906362944045541740492309677311846336810682903157585404911491537163328978494688899061249669721172515611590283743140088328307009198146046031271664502933027185697489699588559043338384466165001178426897626212945177628091195786707458122783970171784415105291802893207873272974885715430223118336\"},\n  {\"0x1.fffffffffffffp+1023\", 309, \"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368\"},\n] of _\n"
  },
  {
    "path": "spec/std/float_printer/shortest_spec.cr",
    "content": "# The example numbers in these specs are ported from Microsoft's C++ STL.\n# The following is their license:\n#\n#   Copyright 2018 Ulf Adams\n#   Copyright (c) Microsoft Corporation. All rights reserved.\n#\n#   Boost Software License - Version 1.0 - August 17th, 2003\n#\n#   Permission is hereby granted, free of charge, to any person or organization\n#   obtaining a copy of the software and accompanying documentation covered by\n#   this license (the \"Software\") to use, reproduce, display, distribute,\n#   execute, and transmit the Software, and to prepare derivative works of the\n#   Software, and to permit third-parties to whom the Software is furnished to\n#   do so, all subject to the following:\n#\n#   The copyright notices in the Software and this entire statement, including\n#   the above license grant, this restriction and the following disclaimer,\n#   must be included in all copies of the Software, in whole or in part, and\n#   all derivative works of the Software, unless such copies or derivative\n#   works are solely in the form of machine-executable object code generated by\n#   a source language processor.\n#\n#   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n#   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n#   FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\n#   SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\n#   FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\n#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n#   DEALINGS IN THE SOFTWARE.\n#\n# This file contains test cases derived from:\n#\n# * https://github.com/ulfjack/ryu\n# * https://github.com/microsoft/STL/tree/main/tests/std/tests/P0067R5_charconv\n\nrequire \"spec\"\nrequire \"../spec_helper\"\nrequire \"spec/helpers/string\"\nrequire \"../../support/number\"\n\n# Tests that `v.to_s` is the same as the *v* literal is written in the source\n# code, except possibly omitting the `_f32` suffix for `Float32` literals.\nprivate macro it_converts_to_s(v)\n  it {{ \"converts #{v} to \\\"#{v.id.gsub(/_f32$/, \"\")}\\\"\" }} do\n    assert_prints {{ v }}.to_s, \"{{ v.id.gsub(/_f32$/, \"\") }}\"\n  end\nend\n\n# Tests that `v.to_s == str`.\nprivate macro it_converts_to_s(v, str)\n  it {{ \"converts #{v.id.gsub(/^hexfloat\\(\"(.*)\"\\)$/, \"\\\\1\")} to #{str}\" }} do\n    assert_prints ({{ v }}).to_s, {{ str }}\n  end\nend\n\ndescribe \"Float32#to_s\" do\n  context \"special cases\" do\n    it_converts_to_s 0.0_f32\n    it_converts_to_s -0.0_f32\n    it_converts_to_s Float32::INFINITY, \"Infinity\"\n    it_converts_to_s -Float32::INFINITY, \"-Infinity\"\n    it_converts_to_s 0x7FC0_0000_u32.unsafe_as(Float32), \"NaN\"\n    it_converts_to_s 0xFFC0_0000_u32.unsafe_as(Float32), \"NaN\"\n    it_converts_to_s Float32::MIN_POSITIVE, \"1.1754944e-38\"\n    it_converts_to_s Float32::MAX, \"3.4028235e+38\"\n    it_converts_to_s Float32::MIN_POSITIVE.prev_float, \"1.1754942e-38\" # largest subnormal\n    it_converts_to_s Float32::MIN_SUBNORMAL, \"1.0e-45\"\n  end\n\n  context \"Ryu f2s_test.cc BoundaryRoundEven\" do\n    it_converts_to_s 3.355445e7_f32, \"33554450.0\"\n    it_converts_to_s 8.999999e9_f32, \"9000000000.0\"\n    it_converts_to_s 3.4366717e10_f32, \"34366720000.0\"\n  end\n\n  context \"Ryu f2s_test.cc ExactValueRoundEven\" do\n    it_converts_to_s 3.0540412e5_f32, \"305404.12\"\n    it_converts_to_s 8.0990312e3_f32, \"8099.0312\"\n  end\n\n  context \"Ryu f2s_test.cc LotsOfTrailingZeros\" do\n    it_converts_to_s 2.4414062e-4_f32, \"0.00024414062\"\n    it_converts_to_s 2.4414062e-3_f32, \"0.0024414062\"\n    it_converts_to_s 4.3945312e-3_f32, \"0.0043945312\"\n    it_converts_to_s 6.3476562e-3_f32, \"0.0063476562\"\n  end\n\n  context \"Ryu f2s_test.cc Regression\" do\n    it_converts_to_s 4.7223665e21_f32, \"4.7223665e+21\"\n    it_converts_to_s 8388608.0_f32, \"8388608.0\"\n    it_converts_to_s 1.6777216e7_f32, \"16777216.0\"\n    it_converts_to_s 3.3554436e7_f32, \"33554436.0\"\n    it_converts_to_s 6.7131496e7_f32, \"67131496.0\"\n    it_converts_to_s 1.9310392e-38_f32, \"1.9310392e-38\"\n    it_converts_to_s -2.47e-43_f32, \"-2.47e-43\"\n    it_converts_to_s 1.993244e-38_f32, \"1.993244e-38\"\n    it_converts_to_s 4103.9003_f32, \"4103.9004\"\n    it_converts_to_s 5.3399997e9_f32, \"5339999700.0\"\n    it_converts_to_s 6.0898e-39_f32, \"6.0898e-39\"\n    it_converts_to_s 0.0010310042_f32, \"0.0010310042\"\n    it_converts_to_s 2.8823261e17_f32, \"2.882326e+17\"\n    it_converts_to_s 7.038531e-26_f32, \"7.038531e-26\"\n    it_converts_to_s 9.2234038e17_f32, \"9.223404e+17\"\n    it_converts_to_s 6.7108872e7_f32, \"67108870.0\"\n    it_converts_to_s 1.0e-44_f32, \"1.0e-44\"\n    it_converts_to_s 2.816025e14_f32, \"281602500000000.0\"\n    it_converts_to_s 9.223372e18_f32, \"9.223372e+18\"\n    it_converts_to_s 1.5846085e29_f32, \"1.5846086e+29\"\n    it_converts_to_s 1.1811161e19_f32, \"1.1811161e+19\"\n    it_converts_to_s 5.368709e18_f32, \"5.368709e+18\"\n    it_converts_to_s 4.6143165e18_f32, \"4.6143166e+18\"\n    it_converts_to_s 0.007812537_f32, \"0.007812537\"\n    it_converts_to_s 1.4e-45_f32, \"1.0e-45\"\n    it_converts_to_s 1.18697724e20_f32, \"1.18697725e+20\"\n    it_converts_to_s 1.00014165e-36_f32, \"1.00014165e-36\"\n    it_converts_to_s 200.0_f32, \"200.0\"\n    it_converts_to_s 3.3554432e7_f32, \"33554432.0\"\n  end\n\n  context \"Ryu f2s_test.cc LooksLikePow5\" do\n    it_converts_to_s hexfloat(\"0x1.2a05f2p+59_f32\"), \"6.7108864e+17\"\n    it_converts_to_s hexfloat(\"0x1.2a05f2p+60_f32\"), \"1.3421773e+18\"\n    it_converts_to_s hexfloat(\"0x1.2a05f2p+61_f32\"), \"2.6843546e+18\"\n  end\n\n  it_converts_to_s 1.0_f32\n  it_converts_to_s 1.2_f32\n  it_converts_to_s 1.23_f32\n  it_converts_to_s 1.234_f32\n  it_converts_to_s 1.2345_f32\n  it_converts_to_s 1.23456_f32\n  it_converts_to_s 1.234567_f32\n  it_converts_to_s 1.2345678_f32\n  it_converts_to_s 1.23456789_f32, \"1.2345679\"\n  it_converts_to_s 1.23456735e-36_f32, \"1.23456735e-36\"\n\n  it_converts_to_s 1.0_f32\n  it_converts_to_s 12.0_f32\n  it_converts_to_s 123.0_f32\n  it_converts_to_s 1234.0_f32\n  it_converts_to_s 12345.0_f32\n  it_converts_to_s 123456.0_f32\n  it_converts_to_s 1234567.0_f32\n  it_converts_to_s 12345678.0_f32\n  it_converts_to_s 123456789.0_f32, \"123456790.0\"\n  it_converts_to_s 1234567890.0_f32, \"1234568000.0\"\n  it_converts_to_s 1234567895.0_f32, \"1234568000.0\"\n  it_converts_to_s 12345678901.0_f32, \"12345679000.0\"\n  it_converts_to_s 123456789012.0_f32, \"123456790000.0\"\n  it_converts_to_s 1234567890123.0_f32, \"1234568000000.0\"\n  it_converts_to_s 12345678901234.0_f32, \"12345679000000.0\"\n  it_converts_to_s 123456789012345.0_f32, \"123456790000000.0\"\n  it_converts_to_s 1234567890123456.0_f32, \"1.234568e+15\"\n\n  it_converts_to_s 1.0_f32\n  it_converts_to_s 10.0_f32\n  it_converts_to_s 100.0_f32\n  it_converts_to_s 1000.0_f32\n  it_converts_to_s 10000.0_f32\n  it_converts_to_s 100000.0_f32\n  it_converts_to_s 1000000.0_f32\n  it_converts_to_s 10000000.0_f32\n  it_converts_to_s 100000000.0_f32\n  it_converts_to_s 1000000000.0_f32\n  it_converts_to_s 10000000000.0_f32\n  it_converts_to_s 100000000000.0_f32\n  it_converts_to_s 1000000000000.0_f32\n  it_converts_to_s 10000000000000.0_f32\n  it_converts_to_s 100000000000000.0_f32\n  it_converts_to_s 1000000000000000.0_f32, \"1.0e+15\"\n\n  it_converts_to_s 1000000010000000.0_f32, \"1.0e+15\"\n  it_converts_to_s 1000000100000000.0_f32, \"1.0000001e+15\"\n  it_converts_to_s 1000001000000000.0_f32, \"1.000001e+15\"\n  it_converts_to_s 1000010000000000.0_f32, \"1.00001e+15\"\n  it_converts_to_s 1000100000000000.0_f32, \"1.0001e+15\"\n  it_converts_to_s 1001000000000000.0_f32, \"1.001e+15\"\n  it_converts_to_s 1010000000000000.0_f32, \"1.01e+15\"\n  it_converts_to_s 1100000000000000.0_f32, \"1.1e+15\"\n\n  it_converts_to_s 8.0_f32\n  it_converts_to_s 64.0_f32\n  it_converts_to_s 512.0_f32\n  it_converts_to_s 8192.0_f32\n  it_converts_to_s 65536.0_f32\n  it_converts_to_s 524288.0_f32\n  it_converts_to_s 8388608.0_f32\n  it_converts_to_s 67108864.0_f32\n\n  it_converts_to_s 8000.0_f32\n  it_converts_to_s 64000.0_f32\n  it_converts_to_s 512000.0_f32\n  it_converts_to_s 8192000.0_f32\n  it_converts_to_s 65536000.0_f32\n  it_converts_to_s 524288000.0_f32\n  it_converts_to_s 8388608000.0_f32\n  it_converts_to_s 67108864000.0_f32\n\n  context \"all exponents\" do\n    it_converts_to_s 1.729e-45_f32, \"1.0e-45\"\n    it_converts_to_s 1.729e-44_f32, \"1.7e-44\"\n    it_converts_to_s 1.729e-43_f32, \"1.72e-43\"\n    it_converts_to_s 1.729e-42_f32, \"1.729e-42\"\n    it_converts_to_s 1.729e-41_f32, \"1.729e-41\"\n    it_converts_to_s 1.729e-40_f32, \"1.729e-40\"\n    it_converts_to_s 1.729e-39_f32, \"1.729e-39\"\n    it_converts_to_s 1.729e-38_f32, \"1.729e-38\"\n    it_converts_to_s 1.729e-37_f32, \"1.729e-37\"\n    it_converts_to_s 1.729e-36_f32, \"1.729e-36\"\n    it_converts_to_s 1.729e-35_f32, \"1.729e-35\"\n    it_converts_to_s 1.729e-34_f32, \"1.729e-34\"\n    it_converts_to_s 1.729e-33_f32, \"1.729e-33\"\n    it_converts_to_s 1.729e-32_f32, \"1.729e-32\"\n    it_converts_to_s 1.729e-31_f32, \"1.729e-31\"\n    it_converts_to_s 1.729e-30_f32, \"1.729e-30\"\n    it_converts_to_s 1.729e-29_f32, \"1.729e-29\"\n    it_converts_to_s 1.729e-28_f32, \"1.729e-28\"\n    it_converts_to_s 1.729e-27_f32, \"1.729e-27\"\n    it_converts_to_s 1.729e-26_f32, \"1.729e-26\"\n    it_converts_to_s 1.729e-25_f32, \"1.729e-25\"\n    it_converts_to_s 1.729e-24_f32, \"1.729e-24\"\n    it_converts_to_s 1.729e-23_f32, \"1.729e-23\"\n    it_converts_to_s 1.729e-22_f32, \"1.729e-22\"\n    it_converts_to_s 1.729e-21_f32, \"1.729e-21\"\n    it_converts_to_s 1.729e-20_f32, \"1.729e-20\"\n    it_converts_to_s 1.729e-19_f32, \"1.729e-19\"\n    it_converts_to_s 1.729e-18_f32, \"1.729e-18\"\n    it_converts_to_s 1.729e-17_f32, \"1.729e-17\"\n    it_converts_to_s 1.729e-16_f32, \"1.729e-16\"\n    it_converts_to_s 1.729e-15_f32, \"1.729e-15\"\n    it_converts_to_s 1.729e-14_f32, \"1.729e-14\"\n    it_converts_to_s 1.729e-13_f32, \"1.729e-13\"\n    it_converts_to_s 1.729e-12_f32, \"1.729e-12\"\n    it_converts_to_s 1.729e-11_f32, \"1.729e-11\"\n    it_converts_to_s 1.729e-10_f32, \"1.729e-10\"\n    it_converts_to_s 1.729e-9_f32, \"1.729e-9\"\n    it_converts_to_s 1.729e-8_f32, \"1.729e-8\"\n    it_converts_to_s 1.729e-7_f32, \"1.729e-7\"\n    it_converts_to_s 1.729e-6_f32, \"1.729e-6\"\n    it_converts_to_s 1.729e-5_f32, \"1.729e-5\"\n\n    it_converts_to_s 1.729e-4_f32, \"0.0001729\"\n    it_converts_to_s 1.729e-3_f32, \"0.001729\"\n    it_converts_to_s 1.729e-2_f32, \"0.01729\"\n    it_converts_to_s 1.729e-1_f32, \"0.1729\"\n    it_converts_to_s 1.729e+0_f32, \"1.729\"\n    it_converts_to_s 1.729e+1_f32, \"17.29\"\n    it_converts_to_s 1.729e+2_f32, \"172.9\"\n    it_converts_to_s 1.729e+3_f32, \"1729.0\"\n    it_converts_to_s 1.729e+4_f32, \"17290.0\"\n    it_converts_to_s 1.729e+5_f32, \"172900.0\"\n    it_converts_to_s 1.729e+6_f32, \"1729000.0\"\n    it_converts_to_s 1.729e+7_f32, \"17290000.0\"\n    it_converts_to_s 1.729e+8_f32, \"172900000.0\"\n    it_converts_to_s 1.729e+9_f32, \"1729000000.0\"\n    it_converts_to_s 1.729e+10_f32, \"17290000000.0\"\n    it_converts_to_s 1.729e+11_f32, \"172900000000.0\"\n    it_converts_to_s 1.729e+12_f32, \"1729000000000.0\"\n    it_converts_to_s 1.729e+13_f32, \"17290000000000.0\"\n    it_converts_to_s 1.729e+14_f32, \"172900000000000.0\"\n\n    it_converts_to_s 1.729e15_f32, \"1.729e+15\"\n    it_converts_to_s 1.729e16_f32, \"1.729e+16\"\n    it_converts_to_s 1.729e17_f32, \"1.729e+17\"\n    it_converts_to_s 1.729e18_f32, \"1.729e+18\"\n    it_converts_to_s 1.729e19_f32, \"1.729e+19\"\n    it_converts_to_s 1.729e20_f32, \"1.729e+20\"\n    it_converts_to_s 1.729e21_f32, \"1.729e+21\"\n    it_converts_to_s 1.729e22_f32, \"1.729e+22\"\n    it_converts_to_s 1.729e23_f32, \"1.729e+23\"\n    it_converts_to_s 1.729e24_f32, \"1.729e+24\"\n    it_converts_to_s 1.729e25_f32, \"1.729e+25\"\n    it_converts_to_s 1.729e26_f32, \"1.729e+26\"\n    it_converts_to_s 1.729e27_f32, \"1.729e+27\"\n    it_converts_to_s 1.729e28_f32, \"1.729e+28\"\n    it_converts_to_s 1.729e29_f32, \"1.729e+29\"\n    it_converts_to_s 1.729e30_f32, \"1.729e+30\"\n    it_converts_to_s 1.729e31_f32, \"1.729e+31\"\n    it_converts_to_s 1.729e32_f32, \"1.729e+32\"\n    it_converts_to_s 1.729e33_f32, \"1.729e+33\"\n    it_converts_to_s 1.729e34_f32, \"1.729e+34\"\n    it_converts_to_s 1.729e35_f32, \"1.729e+35\"\n    it_converts_to_s 1.729e36_f32, \"1.729e+36\"\n    it_converts_to_s 1.729e37_f32, \"1.729e+37\"\n    it_converts_to_s 1.729e38_f32, \"1.729e+38\"\n  end\n\n  context \"one-digit cases, where the decimal point can't appear between digits like \\\"17.29\\\"\" do\n    it_converts_to_s 7e-3_f32, \"0.007\"\n    it_converts_to_s 7e-2_f32, \"0.07\"\n    it_converts_to_s 7e-1_f32, \"0.7\"\n    it_converts_to_s 7e+0_f32, \"7.0\"\n    it_converts_to_s 7e+1_f32, \"70.0\"\n    it_converts_to_s 7e+2_f32, \"700.0\"\n    it_converts_to_s 7e+3_f32, \"7000.0\"\n  end\n\n  context \"highly-trimmed powers of 2\" do\n    it_converts_to_s hexfloat(\"0x1p118_f32\"), \"3.32307e+35\"\n    it_converts_to_s hexfloat(\"0x1p119_f32\"), \"6.64614e+35\"\n  end\n\n  context \"odd mantissas (unaffected by shifting)\" do\n    it_converts_to_s 3355443e1_f32, \"33554430.0\"\n    it_converts_to_s 671087e2_f32, \"67108700.0\"\n    it_converts_to_s 134217e3_f32, \"134217000.0\"\n    it_converts_to_s 26843e4_f32, \"268430000.0\"\n    it_converts_to_s 5367e5_f32, \"536700000.0\"\n    it_converts_to_s 1073e6_f32, \"1073000000.0\"\n    it_converts_to_s 213e7_f32, \"2130000000.0\"\n    it_converts_to_s 41e8_f32, \"4100000000.0\"\n    it_converts_to_s 7e9_f32, \"7000000000.0\"\n    it_converts_to_s 1e10_f32, \"10000000000.0\"\n    it_converts_to_s 3355445e1_f32, \"33554450.0\"\n    it_converts_to_s 671089e2_f32, \"67108900.0\"\n    it_converts_to_s 134219e3_f32, \"134219000.0\"\n    it_converts_to_s 26845e4_f32, \"268450000.0\"\n    it_converts_to_s 5369e5_f32, \"536900000.0\"\n    it_converts_to_s 1075e6_f32, \"1075000000.0\"\n    it_converts_to_s 215e7_f32, \"2150000000.0\"\n    it_converts_to_s 43e8_f32, \"4300000000.0\"\n    it_converts_to_s 9e9_f32, \"9000000000.0\"\n    it_converts_to_s 3e10_f32, \"30000000000.0\"\n    it_converts_to_s 5495808e5_f32, \"549580800000.0\"\n    it_converts_to_s 5497856e5_f32, \"549785600000.0\"\n  end\n\n  context \"Grisu failures\" do\n    it_converts_to_s 85_f32 / 512_f32, \"0.16601562\"\n  end\n\n  # https://www.exploringbinary.com/the-shortest-decimal-string-that-round-trips-may-not-be-the-nearest/\n  context \"anomalous values\" do\n    it_converts_to_s hexfloat(\"0x1p90_f32\"), \"1.2379401e+27\"\n    it_converts_to_s hexfloat(\"0x1p87_f32\"), \"1.5474251e+26\"\n    it_converts_to_s hexfloat(\"0x1p-96_f32\"), \"1.2621775e-29\"\n  end\nend\n\ndescribe \"Float64#to_s\" do\n  context \"special cases\" do\n    it_converts_to_s 0.0\n    it_converts_to_s -0.0\n    it_converts_to_s Float64::INFINITY, \"Infinity\"\n    it_converts_to_s -Float64::INFINITY, \"-Infinity\"\n    it_converts_to_s 0x7FF8_0000_0000_0000_u64.unsafe_as(Float64), \"NaN\"\n    it_converts_to_s 0xFFF8_0000_0000_0000_u64.unsafe_as(Float64), \"NaN\"\n    it_converts_to_s Float64::MIN_POSITIVE, \"2.2250738585072014e-308\"\n    it_converts_to_s Float64::MAX, \"1.7976931348623157e+308\"\n    it_converts_to_s Float64::MIN_POSITIVE.prev_float, \"2.225073858507201e-308\" # largest subnormal\n    it_converts_to_s Float64::MIN_SUBNORMAL, \"5.0e-324\"\n  end\n\n  context \"Ryu d2s_test.cc LotsOfTrailingZeros\" do\n    it_converts_to_s 2.98023223876953125e-8, \"2.9802322387695312e-8\"\n  end\n\n  context \"Ryu d2s_test.cc Regression\" do\n    it_converts_to_s -2.109808898695963e16, \"-2.109808898695963e+16\"\n    it_converts_to_s 4.940656e-318, \"4.940656e-318\"\n    it_converts_to_s 1.18575755e-316, \"1.18575755e-316\"\n    it_converts_to_s 2.989102097996e-312, \"2.989102097996e-312\"\n    it_converts_to_s 9.0608011534336e15, \"9.0608011534336e+15\"\n    it_converts_to_s 4.708356024711512e18, \"4.708356024711512e+18\"\n    it_converts_to_s 9.409340012568248e18, \"9.409340012568248e+18\"\n  end\n\n  context \"Ryu d2s_test.cc LooksLikePow5\" do\n    it_converts_to_s hexfloat(\"0x1.0f0cf064dd592p+132\"), \"5.764607523034235e+39\"\n    it_converts_to_s hexfloat(\"0x1.0f0cf064dd592p+133\"), \"1.152921504606847e+40\"\n    it_converts_to_s hexfloat(\"0x1.0f0cf064dd592p+134\"), \"2.305843009213694e+40\"\n  end\n\n  context \"Ryu d2s_test.cc 32-bit Chunking\" do\n    it_converts_to_s 4.294967294, \"4.294967294\"\n    it_converts_to_s 4.294967295, \"4.294967295\"\n    it_converts_to_s 4.294967296, \"4.294967296\"\n    it_converts_to_s 4.294967297, \"4.294967297\"\n    it_converts_to_s 4.294967298, \"4.294967298\"\n  end\n\n  context \"Ryu d2s_test.cc MinMaxShift\" do\n    it_converts_to_s hexfloat(\"0x1.0000000000000p-1019\"), \"1.7800590868057611e-307\"\n    it_converts_to_s hexfloat(\"0x1.fffffffffffffp-1016\"), \"2.8480945388892175e-306\"\n    it_converts_to_s hexfloat(\"0x1.0000000000000p-982\"), \"2.446494580089078e-296\"\n    it_converts_to_s hexfloat(\"0x1.fffffffffffffp-982\"), \"4.8929891601781557e-296\"\n    it_converts_to_s hexfloat(\"0x1.0000000000000p+54\"), \"1.8014398509481984e+16\"\n    it_converts_to_s hexfloat(\"0x1.fffffffffffffp+54\"), \"3.6028797018963964e+16\"\n    it_converts_to_s hexfloat(\"0x1.0000000000000p-716\"), \"2.900835519859558e-216\"\n    it_converts_to_s hexfloat(\"0x1.fffffffffffffp-716\"), \"5.801671039719115e-216\"\n    it_converts_to_s hexfloat(\"0x1.fa7161a4d6e0cp-89\"), \"3.196104012172126e-27\"\n  end\n\n  context \"Ryu d2s_test.cc SmallIntegers\" do\n    it_converts_to_s 9007199254740991.0, \"9.007199254740991e+15\"\n    it_converts_to_s 9007199254740992.0, \"9.007199254740992e+15\"\n  end\n\n  it_converts_to_s 1.0\n  it_converts_to_s 1.2\n  it_converts_to_s 1.23\n  it_converts_to_s 1.234\n  it_converts_to_s 1.2345\n  it_converts_to_s 1.23456\n  it_converts_to_s 1.234567\n  it_converts_to_s 1.2345678\n  it_converts_to_s 1.23456789\n  it_converts_to_s 1.234567895\n  it_converts_to_s 1.2345678901\n  it_converts_to_s 1.23456789012\n  it_converts_to_s 1.234567890123\n  it_converts_to_s 1.2345678901234\n  it_converts_to_s 1.23456789012345\n  it_converts_to_s 1.234567890123456\n  it_converts_to_s 1.2345678901234567\n\n  it_converts_to_s 1.0\n  it_converts_to_s 12.0\n  it_converts_to_s 123.0\n  it_converts_to_s 1234.0\n  it_converts_to_s 12345.0\n  it_converts_to_s 123456.0\n  it_converts_to_s 1234567.0\n  it_converts_to_s 12345678.0\n  it_converts_to_s 123456789.0\n  it_converts_to_s 1234567890.0\n  it_converts_to_s 1234567895.0\n  it_converts_to_s 12345678901.0\n  it_converts_to_s 123456789012.0\n  it_converts_to_s 1234567890123.0\n  it_converts_to_s 12345678901234.0\n  it_converts_to_s 123456789012345.0\n  it_converts_to_s 1234567890123456.0, \"1.234567890123456e+15\"\n\n  it_converts_to_s 1.0\n  it_converts_to_s 10.0\n  it_converts_to_s 100.0\n  it_converts_to_s 1000.0\n  it_converts_to_s 10000.0\n  it_converts_to_s 100000.0\n  it_converts_to_s 1000000.0\n  it_converts_to_s 10000000.0\n  it_converts_to_s 100000000.0\n  it_converts_to_s 1000000000.0\n  it_converts_to_s 10000000000.0\n  it_converts_to_s 100000000000.0\n  it_converts_to_s 1000000000000.0\n  it_converts_to_s 10000000000000.0\n  it_converts_to_s 100000000000000.0\n  it_converts_to_s 1000000000000000.0, \"1.0e+15\"\n\n  it_converts_to_s 1000000000000001.0, \"1.000000000000001e+15\"\n  it_converts_to_s 1000000000000010.0, \"1.00000000000001e+15\"\n  it_converts_to_s 1000000000000100.0, \"1.0000000000001e+15\"\n  it_converts_to_s 1000000000001000.0, \"1.000000000001e+15\"\n  it_converts_to_s 1000000000010000.0, \"1.00000000001e+15\"\n  it_converts_to_s 1000000000100000.0, \"1.0000000001e+15\"\n  it_converts_to_s 1000000001000000.0, \"1.000000001e+15\"\n  it_converts_to_s 1000000010000000.0, \"1.00000001e+15\"\n  it_converts_to_s 1000000100000000.0, \"1.0000001e+15\"\n  it_converts_to_s 1000001000000000.0, \"1.000001e+15\"\n  it_converts_to_s 1000010000000000.0, \"1.00001e+15\"\n  it_converts_to_s 1000100000000000.0, \"1.0001e+15\"\n  it_converts_to_s 1001000000000000.0, \"1.001e+15\"\n  it_converts_to_s 1010000000000000.0, \"1.01e+15\"\n  it_converts_to_s 1100000000000000.0, \"1.1e+15\"\n\n  it_converts_to_s 8.0\n  it_converts_to_s 64.0\n  it_converts_to_s 512.0\n  it_converts_to_s 8192.0\n  it_converts_to_s 65536.0\n  it_converts_to_s 524288.0\n  it_converts_to_s 8388608.0\n  it_converts_to_s 67108864.0\n  it_converts_to_s 536870912.0\n  it_converts_to_s 8589934592.0\n  it_converts_to_s 68719476736.0\n  it_converts_to_s 549755813888.0\n  it_converts_to_s 8796093022208.0\n  it_converts_to_s 70368744177664.0\n  it_converts_to_s 562949953421312.0\n  it_converts_to_s 9007199254740992.0, \"9.007199254740992e+15\"\n\n  it_converts_to_s 8000.0\n  it_converts_to_s 64000.0\n  it_converts_to_s 512000.0\n  it_converts_to_s 8192000.0\n  it_converts_to_s 65536000.0\n  it_converts_to_s 524288000.0\n  it_converts_to_s 8388608000.0\n  it_converts_to_s 67108864000.0\n  it_converts_to_s 536870912000.0\n  it_converts_to_s 8589934592000.0\n  it_converts_to_s 68719476736000.0\n  it_converts_to_s 549755813888000.0\n  it_converts_to_s 8796093022208000.0, \"8.796093022208e+15\"\n\n  context \"all exponents\" do\n    it_converts_to_s 7.29e-324, \"5.0e-324\"\n    it_converts_to_s 1.729e-323, \"1.5e-323\"\n    it_converts_to_s 1.729e-322, \"1.73e-322\"\n    it_converts_to_s 1.729e-321, \"1.73e-321\"\n    it_converts_to_s 1.729e-320, \"1.729e-320\"\n    it_converts_to_s 1.729e-319, \"1.729e-319\"\n    it_converts_to_s 1.729e-318, \"1.729e-318\"\n    it_converts_to_s 1.729e-317, \"1.729e-317\"\n    it_converts_to_s 1.729e-316, \"1.729e-316\"\n    it_converts_to_s 1.729e-315, \"1.729e-315\"\n    it_converts_to_s 1.729e-314, \"1.729e-314\"\n    it_converts_to_s 1.729e-313, \"1.729e-313\"\n    it_converts_to_s 1.729e-312, \"1.729e-312\"\n    it_converts_to_s 1.729e-311, \"1.729e-311\"\n    it_converts_to_s 1.729e-310, \"1.729e-310\"\n    it_converts_to_s 1.729e-309, \"1.729e-309\"\n    it_converts_to_s 1.729e-308, \"1.729e-308\"\n    it_converts_to_s 1.729e-307, \"1.729e-307\"\n    it_converts_to_s 1.729e-306, \"1.729e-306\"\n    it_converts_to_s 1.729e-305, \"1.729e-305\"\n    it_converts_to_s 1.729e-304, \"1.729e-304\"\n    it_converts_to_s 1.729e-303, \"1.729e-303\"\n    it_converts_to_s 1.729e-302, \"1.729e-302\"\n    it_converts_to_s 1.729e-301, \"1.729e-301\"\n    it_converts_to_s 1.729e-300, \"1.729e-300\"\n    it_converts_to_s 1.729e-299, \"1.729e-299\"\n    it_converts_to_s 1.729e-298, \"1.729e-298\"\n    it_converts_to_s 1.729e-297, \"1.729e-297\"\n    it_converts_to_s 1.729e-296, \"1.729e-296\"\n    it_converts_to_s 1.729e-295, \"1.729e-295\"\n    it_converts_to_s 1.729e-294, \"1.729e-294\"\n    it_converts_to_s 1.729e-293, \"1.729e-293\"\n    it_converts_to_s 1.729e-292, \"1.729e-292\"\n    it_converts_to_s 1.729e-291, \"1.729e-291\"\n    it_converts_to_s 1.729e-290, \"1.729e-290\"\n    it_converts_to_s 1.729e-289, \"1.729e-289\"\n    it_converts_to_s 1.729e-288, \"1.729e-288\"\n    it_converts_to_s 1.729e-287, \"1.729e-287\"\n    it_converts_to_s 1.729e-286, \"1.729e-286\"\n    it_converts_to_s 1.729e-285, \"1.729e-285\"\n    it_converts_to_s 1.729e-284, \"1.729e-284\"\n    it_converts_to_s 1.729e-283, \"1.729e-283\"\n    it_converts_to_s 1.729e-282, \"1.729e-282\"\n    it_converts_to_s 1.729e-281, \"1.729e-281\"\n    it_converts_to_s 1.729e-280, \"1.729e-280\"\n    it_converts_to_s 1.729e-279, \"1.729e-279\"\n    it_converts_to_s 1.729e-278, \"1.729e-278\"\n    it_converts_to_s 1.729e-277, \"1.729e-277\"\n    it_converts_to_s 1.729e-276, \"1.729e-276\"\n    it_converts_to_s 1.729e-275, \"1.729e-275\"\n    it_converts_to_s 1.729e-274, \"1.729e-274\"\n    it_converts_to_s 1.729e-273, \"1.729e-273\"\n    it_converts_to_s 1.729e-272, \"1.729e-272\"\n    it_converts_to_s 1.729e-271, \"1.729e-271\"\n    it_converts_to_s 1.729e-270, \"1.729e-270\"\n    it_converts_to_s 1.729e-269, \"1.729e-269\"\n    it_converts_to_s 1.729e-268, \"1.729e-268\"\n    it_converts_to_s 1.729e-267, \"1.729e-267\"\n    it_converts_to_s 1.729e-266, \"1.729e-266\"\n    it_converts_to_s 1.729e-265, \"1.729e-265\"\n    it_converts_to_s 1.729e-264, \"1.729e-264\"\n    it_converts_to_s 1.729e-263, \"1.729e-263\"\n    it_converts_to_s 1.729e-262, \"1.729e-262\"\n    it_converts_to_s 1.729e-261, \"1.729e-261\"\n    it_converts_to_s 1.729e-260, \"1.729e-260\"\n    it_converts_to_s 1.729e-259, \"1.729e-259\"\n    it_converts_to_s 1.729e-258, \"1.729e-258\"\n    it_converts_to_s 1.729e-257, \"1.729e-257\"\n    it_converts_to_s 1.729e-256, \"1.729e-256\"\n    it_converts_to_s 1.729e-255, \"1.729e-255\"\n    it_converts_to_s 1.729e-254, \"1.729e-254\"\n    it_converts_to_s 1.729e-253, \"1.729e-253\"\n    it_converts_to_s 1.729e-252, \"1.729e-252\"\n    it_converts_to_s 1.729e-251, \"1.729e-251\"\n    it_converts_to_s 1.729e-250, \"1.729e-250\"\n    it_converts_to_s 1.729e-249, \"1.729e-249\"\n    it_converts_to_s 1.729e-248, \"1.729e-248\"\n    it_converts_to_s 1.729e-247, \"1.729e-247\"\n    it_converts_to_s 1.729e-246, \"1.729e-246\"\n    it_converts_to_s 1.729e-245, \"1.729e-245\"\n    it_converts_to_s 1.729e-244, \"1.729e-244\"\n    it_converts_to_s 1.729e-243, \"1.729e-243\"\n    it_converts_to_s 1.729e-242, \"1.729e-242\"\n    it_converts_to_s 1.729e-241, \"1.729e-241\"\n    it_converts_to_s 1.729e-240, \"1.729e-240\"\n    it_converts_to_s 1.729e-239, \"1.729e-239\"\n    it_converts_to_s 1.729e-238, \"1.729e-238\"\n    it_converts_to_s 1.729e-237, \"1.729e-237\"\n    it_converts_to_s 1.729e-236, \"1.729e-236\"\n    it_converts_to_s 1.729e-235, \"1.729e-235\"\n    it_converts_to_s 1.729e-234, \"1.729e-234\"\n    it_converts_to_s 1.729e-233, \"1.729e-233\"\n    it_converts_to_s 1.729e-232, \"1.729e-232\"\n    it_converts_to_s 1.729e-231, \"1.729e-231\"\n    it_converts_to_s 1.729e-230, \"1.729e-230\"\n    it_converts_to_s 1.729e-229, \"1.729e-229\"\n    it_converts_to_s 1.729e-228, \"1.729e-228\"\n    it_converts_to_s 1.729e-227, \"1.729e-227\"\n    it_converts_to_s 1.729e-226, \"1.729e-226\"\n    it_converts_to_s 1.729e-225, \"1.729e-225\"\n    it_converts_to_s 1.729e-224, \"1.729e-224\"\n    it_converts_to_s 1.729e-223, \"1.729e-223\"\n    it_converts_to_s 1.729e-222, \"1.729e-222\"\n    it_converts_to_s 1.729e-221, \"1.729e-221\"\n    it_converts_to_s 1.729e-220, \"1.729e-220\"\n    it_converts_to_s 1.729e-219, \"1.729e-219\"\n    it_converts_to_s 1.729e-218, \"1.729e-218\"\n    it_converts_to_s 1.729e-217, \"1.729e-217\"\n    it_converts_to_s 1.729e-216, \"1.729e-216\"\n    it_converts_to_s 1.729e-215, \"1.729e-215\"\n    it_converts_to_s 1.729e-214, \"1.729e-214\"\n    it_converts_to_s 1.729e-213, \"1.729e-213\"\n    it_converts_to_s 1.729e-212, \"1.729e-212\"\n    it_converts_to_s 1.729e-211, \"1.729e-211\"\n    it_converts_to_s 1.729e-210, \"1.729e-210\"\n    it_converts_to_s 1.729e-209, \"1.729e-209\"\n    it_converts_to_s 1.729e-208, \"1.729e-208\"\n    it_converts_to_s 1.729e-207, \"1.729e-207\"\n    it_converts_to_s 1.729e-206, \"1.729e-206\"\n    it_converts_to_s 1.729e-205, \"1.729e-205\"\n    it_converts_to_s 1.729e-204, \"1.729e-204\"\n    it_converts_to_s 1.729e-203, \"1.729e-203\"\n    it_converts_to_s 1.729e-202, \"1.729e-202\"\n    it_converts_to_s 1.729e-201, \"1.729e-201\"\n    it_converts_to_s 1.729e-200, \"1.729e-200\"\n    it_converts_to_s 1.729e-199, \"1.729e-199\"\n    it_converts_to_s 1.729e-198, \"1.729e-198\"\n    it_converts_to_s 1.729e-197, \"1.729e-197\"\n    it_converts_to_s 1.729e-196, \"1.729e-196\"\n    it_converts_to_s 1.729e-195, \"1.729e-195\"\n    it_converts_to_s 1.729e-194, \"1.729e-194\"\n    it_converts_to_s 1.729e-193, \"1.729e-193\"\n    it_converts_to_s 1.729e-192, \"1.729e-192\"\n    it_converts_to_s 1.729e-191, \"1.729e-191\"\n    it_converts_to_s 1.729e-190, \"1.729e-190\"\n    it_converts_to_s 1.729e-189, \"1.729e-189\"\n    it_converts_to_s 1.729e-188, \"1.729e-188\"\n    it_converts_to_s 1.729e-187, \"1.729e-187\"\n    it_converts_to_s 1.729e-186, \"1.729e-186\"\n    it_converts_to_s 1.729e-185, \"1.729e-185\"\n    it_converts_to_s 1.729e-184, \"1.729e-184\"\n    it_converts_to_s 1.729e-183, \"1.729e-183\"\n    it_converts_to_s 1.729e-182, \"1.729e-182\"\n    it_converts_to_s 1.729e-181, \"1.729e-181\"\n    it_converts_to_s 1.729e-180, \"1.729e-180\"\n    it_converts_to_s 1.729e-179, \"1.729e-179\"\n    it_converts_to_s 1.729e-178, \"1.729e-178\"\n    it_converts_to_s 1.729e-177, \"1.729e-177\"\n    it_converts_to_s 1.729e-176, \"1.729e-176\"\n    it_converts_to_s 1.729e-175, \"1.729e-175\"\n    it_converts_to_s 1.729e-174, \"1.729e-174\"\n    it_converts_to_s 1.729e-173, \"1.729e-173\"\n    it_converts_to_s 1.729e-172, \"1.729e-172\"\n    it_converts_to_s 1.729e-171, \"1.729e-171\"\n    it_converts_to_s 1.729e-170, \"1.729e-170\"\n    it_converts_to_s 1.729e-169, \"1.729e-169\"\n    it_converts_to_s 1.729e-168, \"1.729e-168\"\n    it_converts_to_s 1.729e-167, \"1.729e-167\"\n    it_converts_to_s 1.729e-166, \"1.729e-166\"\n    it_converts_to_s 1.729e-165, \"1.729e-165\"\n    it_converts_to_s 1.729e-164, \"1.729e-164\"\n    it_converts_to_s 1.729e-163, \"1.729e-163\"\n    it_converts_to_s 1.729e-162, \"1.729e-162\"\n    it_converts_to_s 1.729e-161, \"1.729e-161\"\n    it_converts_to_s 1.729e-160, \"1.729e-160\"\n    it_converts_to_s 1.729e-159, \"1.729e-159\"\n    it_converts_to_s 1.729e-158, \"1.729e-158\"\n    it_converts_to_s 1.729e-157, \"1.729e-157\"\n    it_converts_to_s 1.729e-156, \"1.729e-156\"\n    it_converts_to_s 1.729e-155, \"1.729e-155\"\n    it_converts_to_s 1.729e-154, \"1.729e-154\"\n    it_converts_to_s 1.729e-153, \"1.729e-153\"\n    it_converts_to_s 1.729e-152, \"1.729e-152\"\n    it_converts_to_s 1.729e-151, \"1.729e-151\"\n    it_converts_to_s 1.729e-150, \"1.729e-150\"\n    it_converts_to_s 1.729e-149, \"1.729e-149\"\n    it_converts_to_s 1.729e-148, \"1.729e-148\"\n    it_converts_to_s 1.729e-147, \"1.729e-147\"\n    it_converts_to_s 1.729e-146, \"1.729e-146\"\n    it_converts_to_s 1.729e-145, \"1.729e-145\"\n    it_converts_to_s 1.729e-144, \"1.729e-144\"\n    it_converts_to_s 1.729e-143, \"1.729e-143\"\n    it_converts_to_s 1.729e-142, \"1.729e-142\"\n    it_converts_to_s 1.729e-141, \"1.729e-141\"\n    it_converts_to_s 1.729e-140, \"1.729e-140\"\n    it_converts_to_s 1.729e-139, \"1.729e-139\"\n    it_converts_to_s 1.729e-138, \"1.729e-138\"\n    it_converts_to_s 1.729e-137, \"1.729e-137\"\n    it_converts_to_s 1.729e-136, \"1.729e-136\"\n    it_converts_to_s 1.729e-135, \"1.729e-135\"\n    it_converts_to_s 1.729e-134, \"1.729e-134\"\n    it_converts_to_s 1.729e-133, \"1.729e-133\"\n    it_converts_to_s 1.729e-132, \"1.729e-132\"\n    it_converts_to_s 1.729e-131, \"1.729e-131\"\n    it_converts_to_s 1.729e-130, \"1.729e-130\"\n    it_converts_to_s 1.729e-129, \"1.729e-129\"\n    it_converts_to_s 1.729e-128, \"1.729e-128\"\n    it_converts_to_s 1.729e-127, \"1.729e-127\"\n    it_converts_to_s 1.729e-126, \"1.729e-126\"\n    it_converts_to_s 1.729e-125, \"1.729e-125\"\n    it_converts_to_s 1.729e-124, \"1.729e-124\"\n    it_converts_to_s 1.729e-123, \"1.729e-123\"\n    it_converts_to_s 1.729e-122, \"1.729e-122\"\n    it_converts_to_s 1.729e-121, \"1.729e-121\"\n    it_converts_to_s 1.729e-120, \"1.729e-120\"\n    it_converts_to_s 1.729e-119, \"1.729e-119\"\n    it_converts_to_s 1.729e-118, \"1.729e-118\"\n    it_converts_to_s 1.729e-117, \"1.729e-117\"\n    it_converts_to_s 1.729e-116, \"1.729e-116\"\n    it_converts_to_s 1.729e-115, \"1.729e-115\"\n    it_converts_to_s 1.729e-114, \"1.729e-114\"\n    it_converts_to_s 1.729e-113, \"1.729e-113\"\n    it_converts_to_s 1.729e-112, \"1.729e-112\"\n    it_converts_to_s 1.729e-111, \"1.729e-111\"\n    it_converts_to_s 1.729e-110, \"1.729e-110\"\n    it_converts_to_s 1.729e-109, \"1.729e-109\"\n    it_converts_to_s 1.729e-108, \"1.729e-108\"\n    it_converts_to_s 1.729e-107, \"1.729e-107\"\n    it_converts_to_s 1.729e-106, \"1.729e-106\"\n    it_converts_to_s 1.729e-105, \"1.729e-105\"\n    it_converts_to_s 1.729e-104, \"1.729e-104\"\n    it_converts_to_s 1.729e-103, \"1.729e-103\"\n    it_converts_to_s 1.729e-102, \"1.729e-102\"\n    it_converts_to_s 1.729e-101, \"1.729e-101\"\n    it_converts_to_s 1.729e-100, \"1.729e-100\"\n    it_converts_to_s 1.729e-99, \"1.729e-99\"\n    it_converts_to_s 1.729e-98, \"1.729e-98\"\n    it_converts_to_s 1.729e-97, \"1.729e-97\"\n    it_converts_to_s 1.729e-96, \"1.729e-96\"\n    it_converts_to_s 1.729e-95, \"1.729e-95\"\n    it_converts_to_s 1.729e-94, \"1.729e-94\"\n    it_converts_to_s 1.729e-93, \"1.729e-93\"\n    it_converts_to_s 1.729e-92, \"1.729e-92\"\n    it_converts_to_s 1.729e-91, \"1.729e-91\"\n    it_converts_to_s 1.729e-90, \"1.729e-90\"\n    it_converts_to_s 1.729e-89, \"1.729e-89\"\n    it_converts_to_s 1.729e-88, \"1.729e-88\"\n    it_converts_to_s 1.729e-87, \"1.729e-87\"\n    it_converts_to_s 1.729e-86, \"1.729e-86\"\n    it_converts_to_s 1.729e-85, \"1.729e-85\"\n    it_converts_to_s 1.729e-84, \"1.729e-84\"\n    it_converts_to_s 1.729e-83, \"1.729e-83\"\n    it_converts_to_s 1.729e-82, \"1.729e-82\"\n    it_converts_to_s 1.729e-81, \"1.729e-81\"\n    it_converts_to_s 1.729e-80, \"1.729e-80\"\n    it_converts_to_s 1.729e-79, \"1.729e-79\"\n    it_converts_to_s 1.729e-78, \"1.729e-78\"\n    it_converts_to_s 1.729e-77, \"1.729e-77\"\n    it_converts_to_s 1.729e-76, \"1.729e-76\"\n    it_converts_to_s 1.729e-75, \"1.729e-75\"\n    it_converts_to_s 1.729e-74, \"1.729e-74\"\n    it_converts_to_s 1.729e-73, \"1.729e-73\"\n    it_converts_to_s 1.729e-72, \"1.729e-72\"\n    it_converts_to_s 1.729e-71, \"1.729e-71\"\n    it_converts_to_s 1.729e-70, \"1.729e-70\"\n    it_converts_to_s 1.729e-69, \"1.729e-69\"\n    it_converts_to_s 1.729e-68, \"1.729e-68\"\n    it_converts_to_s 1.729e-67, \"1.729e-67\"\n    it_converts_to_s 1.729e-66, \"1.729e-66\"\n    it_converts_to_s 1.729e-65, \"1.729e-65\"\n    it_converts_to_s 1.729e-64, \"1.729e-64\"\n    it_converts_to_s 1.729e-63, \"1.729e-63\"\n    it_converts_to_s 1.729e-62, \"1.729e-62\"\n    it_converts_to_s 1.729e-61, \"1.729e-61\"\n    it_converts_to_s 1.729e-60, \"1.729e-60\"\n    it_converts_to_s 1.729e-59, \"1.729e-59\"\n    it_converts_to_s 1.729e-58, \"1.729e-58\"\n    it_converts_to_s 1.729e-57, \"1.729e-57\"\n    it_converts_to_s 1.729e-56, \"1.729e-56\"\n    it_converts_to_s 1.729e-55, \"1.729e-55\"\n    it_converts_to_s 1.729e-54, \"1.729e-54\"\n    it_converts_to_s 1.729e-53, \"1.729e-53\"\n    it_converts_to_s 1.729e-52, \"1.729e-52\"\n    it_converts_to_s 1.729e-51, \"1.729e-51\"\n    it_converts_to_s 1.729e-50, \"1.729e-50\"\n    it_converts_to_s 1.729e-49, \"1.729e-49\"\n    it_converts_to_s 1.729e-48, \"1.729e-48\"\n    it_converts_to_s 1.729e-47, \"1.729e-47\"\n    it_converts_to_s 1.729e-46, \"1.729e-46\"\n    it_converts_to_s 1.729e-45, \"1.729e-45\"\n    it_converts_to_s 1.729e-44, \"1.729e-44\"\n    it_converts_to_s 1.729e-43, \"1.729e-43\"\n    it_converts_to_s 1.729e-42, \"1.729e-42\"\n    it_converts_to_s 1.729e-41, \"1.729e-41\"\n    it_converts_to_s 1.729e-40, \"1.729e-40\"\n    it_converts_to_s 1.729e-39, \"1.729e-39\"\n    it_converts_to_s 1.729e-38, \"1.729e-38\"\n    it_converts_to_s 1.729e-37, \"1.729e-37\"\n    it_converts_to_s 1.729e-36, \"1.729e-36\"\n    it_converts_to_s 1.729e-35, \"1.729e-35\"\n    it_converts_to_s 1.729e-34, \"1.729e-34\"\n    it_converts_to_s 1.729e-33, \"1.729e-33\"\n    it_converts_to_s 1.729e-32, \"1.729e-32\"\n    it_converts_to_s 1.729e-31, \"1.729e-31\"\n    it_converts_to_s 1.729e-30, \"1.729e-30\"\n    it_converts_to_s 1.729e-29, \"1.729e-29\"\n    it_converts_to_s 1.729e-28, \"1.729e-28\"\n    it_converts_to_s 1.729e-27, \"1.729e-27\"\n    it_converts_to_s 1.729e-26, \"1.729e-26\"\n    it_converts_to_s 1.729e-25, \"1.729e-25\"\n    it_converts_to_s 1.729e-24, \"1.729e-24\"\n    it_converts_to_s 1.729e-23, \"1.729e-23\"\n    it_converts_to_s 1.729e-22, \"1.729e-22\"\n    it_converts_to_s 1.729e-21, \"1.729e-21\"\n    it_converts_to_s 1.729e-20, \"1.729e-20\"\n    it_converts_to_s 1.729e-19, \"1.729e-19\"\n    it_converts_to_s 1.729e-18, \"1.729e-18\"\n    it_converts_to_s 1.729e-17, \"1.729e-17\"\n    it_converts_to_s 1.729e-16, \"1.729e-16\"\n    it_converts_to_s 1.729e-15, \"1.729e-15\"\n    it_converts_to_s 1.729e-14, \"1.729e-14\"\n    it_converts_to_s 1.729e-13, \"1.729e-13\"\n    it_converts_to_s 1.729e-12, \"1.729e-12\"\n    it_converts_to_s 1.729e-11, \"1.729e-11\"\n    it_converts_to_s 1.729e-10, \"1.729e-10\"\n    it_converts_to_s 1.729e-9, \"1.729e-9\"\n    it_converts_to_s 1.729e-8, \"1.729e-8\"\n    it_converts_to_s 1.729e-7, \"1.729e-7\"\n    it_converts_to_s 1.729e-6, \"1.729e-6\"\n    it_converts_to_s 1.729e-5, \"1.729e-5\"\n\n    it_converts_to_s 1.729e-4, \"0.0001729\"\n    it_converts_to_s 1.729e-3, \"0.001729\"\n    it_converts_to_s 1.729e-2, \"0.01729\"\n    it_converts_to_s 1.729e-1, \"0.1729\"\n    it_converts_to_s 1.729e+0, \"1.729\"\n    it_converts_to_s 1.729e+1, \"17.29\"\n    it_converts_to_s 1.729e+2, \"172.9\"\n    it_converts_to_s 1.729e+3, \"1729.0\"\n    it_converts_to_s 1.729e+4, \"17290.0\"\n    it_converts_to_s 1.729e+5, \"172900.0\"\n    it_converts_to_s 1.729e+6, \"1729000.0\"\n    it_converts_to_s 1.729e+7, \"17290000.0\"\n    it_converts_to_s 1.729e+8, \"172900000.0\"\n    it_converts_to_s 1.729e+9, \"1729000000.0\"\n    it_converts_to_s 1.729e+10, \"17290000000.0\"\n    it_converts_to_s 1.729e+11, \"172900000000.0\"\n    it_converts_to_s 1.729e+12, \"1729000000000.0\"\n    it_converts_to_s 1.729e+13, \"17290000000000.0\"\n    it_converts_to_s 1.729e+14, \"172900000000000.0\"\n\n    it_converts_to_s 1.729e+15, \"1.729e+15\"\n    it_converts_to_s 1.729e+16, \"1.729e+16\"\n    it_converts_to_s 1.729e+17, \"1.729e+17\"\n    it_converts_to_s 1.729e+18, \"1.729e+18\"\n    it_converts_to_s 1.729e+19, \"1.729e+19\"\n    it_converts_to_s 1.729e+20, \"1.729e+20\"\n    it_converts_to_s 1.729e+21, \"1.729e+21\"\n    it_converts_to_s 1.729e+22, \"1.729e+22\"\n    it_converts_to_s 1.729e+23, \"1.729e+23\"\n    it_converts_to_s 1.729e+24, \"1.729e+24\"\n    it_converts_to_s 1.729e+25, \"1.729e+25\"\n    it_converts_to_s 1.729e+26, \"1.729e+26\"\n    it_converts_to_s 1.729e+27, \"1.729e+27\"\n    it_converts_to_s 1.729e+28, \"1.729e+28\"\n    it_converts_to_s 1.729e+29, \"1.729e+29\"\n    it_converts_to_s 1.729e+30, \"1.729e+30\"\n    it_converts_to_s 1.729e+31, \"1.729e+31\"\n    it_converts_to_s 1.729e+32, \"1.729e+32\"\n    it_converts_to_s 1.729e+33, \"1.729e+33\"\n    it_converts_to_s 1.729e+34, \"1.729e+34\"\n    it_converts_to_s 1.729e+35, \"1.729e+35\"\n    it_converts_to_s 1.729e+36, \"1.729e+36\"\n    it_converts_to_s 1.729e+37, \"1.729e+37\"\n    it_converts_to_s 1.729e+38, \"1.729e+38\"\n    it_converts_to_s 1.729e+39, \"1.729e+39\"\n    it_converts_to_s 1.729e+40, \"1.729e+40\"\n    it_converts_to_s 1.729e+41, \"1.729e+41\"\n    it_converts_to_s 1.729e+42, \"1.729e+42\"\n    it_converts_to_s 1.729e+43, \"1.729e+43\"\n    it_converts_to_s 1.729e+44, \"1.729e+44\"\n    it_converts_to_s 1.729e+45, \"1.729e+45\"\n    it_converts_to_s 1.729e+46, \"1.729e+46\"\n    it_converts_to_s 1.729e+47, \"1.729e+47\"\n    it_converts_to_s 1.729e+48, \"1.729e+48\"\n    it_converts_to_s 1.729e+49, \"1.729e+49\"\n    it_converts_to_s 1.729e+50, \"1.729e+50\"\n    it_converts_to_s 1.729e+51, \"1.729e+51\"\n    it_converts_to_s 1.729e+52, \"1.729e+52\"\n    it_converts_to_s 1.729e+53, \"1.729e+53\"\n    it_converts_to_s 1.729e+54, \"1.729e+54\"\n    it_converts_to_s 1.729e+55, \"1.729e+55\"\n    it_converts_to_s 1.729e+56, \"1.729e+56\"\n    it_converts_to_s 1.729e+57, \"1.729e+57\"\n    it_converts_to_s 1.729e+58, \"1.729e+58\"\n    it_converts_to_s 1.729e+59, \"1.729e+59\"\n    it_converts_to_s 1.729e+60, \"1.729e+60\"\n    it_converts_to_s 1.729e+61, \"1.729e+61\"\n    it_converts_to_s 1.729e+62, \"1.729e+62\"\n    it_converts_to_s 1.729e+63, \"1.729e+63\"\n    it_converts_to_s 1.729e+64, \"1.729e+64\"\n    it_converts_to_s 1.729e+65, \"1.729e+65\"\n    it_converts_to_s 1.729e+66, \"1.729e+66\"\n    it_converts_to_s 1.729e+67, \"1.729e+67\"\n    it_converts_to_s 1.729e+68, \"1.729e+68\"\n    it_converts_to_s 1.729e+69, \"1.729e+69\"\n    it_converts_to_s 1.729e+70, \"1.729e+70\"\n    it_converts_to_s 1.729e+71, \"1.729e+71\"\n    it_converts_to_s 1.729e+72, \"1.729e+72\"\n    it_converts_to_s 1.729e+73, \"1.729e+73\"\n    it_converts_to_s 1.729e+74, \"1.729e+74\"\n    it_converts_to_s 1.729e+75, \"1.729e+75\"\n    it_converts_to_s 1.729e+76, \"1.729e+76\"\n    it_converts_to_s 1.729e+77, \"1.729e+77\"\n    it_converts_to_s 1.729e+78, \"1.729e+78\"\n    it_converts_to_s 1.729e+79, \"1.729e+79\"\n    it_converts_to_s 1.729e+80, \"1.729e+80\"\n    it_converts_to_s 1.729e+81, \"1.729e+81\"\n    it_converts_to_s 1.729e+82, \"1.729e+82\"\n    it_converts_to_s 1.729e+83, \"1.729e+83\"\n    it_converts_to_s 1.729e+84, \"1.729e+84\"\n    it_converts_to_s 1.729e+85, \"1.729e+85\"\n    it_converts_to_s 1.729e+86, \"1.729e+86\"\n    it_converts_to_s 1.729e+87, \"1.729e+87\"\n    it_converts_to_s 1.729e+88, \"1.729e+88\"\n    it_converts_to_s 1.729e+89, \"1.729e+89\"\n    it_converts_to_s 1.729e+90, \"1.729e+90\"\n    it_converts_to_s 1.729e+91, \"1.729e+91\"\n    it_converts_to_s 1.729e+92, \"1.729e+92\"\n    it_converts_to_s 1.729e+93, \"1.729e+93\"\n    it_converts_to_s 1.729e+94, \"1.729e+94\"\n    it_converts_to_s 1.729e+95, \"1.729e+95\"\n    it_converts_to_s 1.729e+96, \"1.729e+96\"\n    it_converts_to_s 1.729e+97, \"1.729e+97\"\n    it_converts_to_s 1.729e+98, \"1.729e+98\"\n    it_converts_to_s 1.729e+99, \"1.729e+99\"\n    it_converts_to_s 1.729e+100, \"1.729e+100\"\n    it_converts_to_s 1.729e+101, \"1.729e+101\"\n    it_converts_to_s 1.729e+102, \"1.729e+102\"\n    it_converts_to_s 1.729e+103, \"1.729e+103\"\n    it_converts_to_s 1.729e+104, \"1.729e+104\"\n    it_converts_to_s 1.729e+105, \"1.729e+105\"\n    it_converts_to_s 1.729e+106, \"1.729e+106\"\n    it_converts_to_s 1.729e+107, \"1.729e+107\"\n    it_converts_to_s 1.729e+108, \"1.729e+108\"\n    it_converts_to_s 1.729e+109, \"1.729e+109\"\n    it_converts_to_s 1.729e+110, \"1.729e+110\"\n    it_converts_to_s 1.729e+111, \"1.729e+111\"\n    it_converts_to_s 1.729e+112, \"1.729e+112\"\n    it_converts_to_s 1.729e+113, \"1.729e+113\"\n    it_converts_to_s 1.729e+114, \"1.729e+114\"\n    it_converts_to_s 1.729e+115, \"1.729e+115\"\n    it_converts_to_s 1.729e+116, \"1.729e+116\"\n    it_converts_to_s 1.729e+117, \"1.729e+117\"\n    it_converts_to_s 1.729e+118, \"1.729e+118\"\n    it_converts_to_s 1.729e+119, \"1.729e+119\"\n    it_converts_to_s 1.729e+120, \"1.729e+120\"\n    it_converts_to_s 1.729e+121, \"1.729e+121\"\n    it_converts_to_s 1.729e+122, \"1.729e+122\"\n    it_converts_to_s 1.729e+123, \"1.729e+123\"\n    it_converts_to_s 1.729e+124, \"1.729e+124\"\n    it_converts_to_s 1.729e+125, \"1.729e+125\"\n    it_converts_to_s 1.729e+126, \"1.729e+126\"\n    it_converts_to_s 1.729e+127, \"1.729e+127\"\n    it_converts_to_s 1.729e+128, \"1.729e+128\"\n    it_converts_to_s 1.729e+129, \"1.729e+129\"\n    it_converts_to_s 1.729e+130, \"1.729e+130\"\n    it_converts_to_s 1.729e+131, \"1.729e+131\"\n    it_converts_to_s 1.729e+132, \"1.729e+132\"\n    it_converts_to_s 1.729e+133, \"1.729e+133\"\n    it_converts_to_s 1.729e+134, \"1.729e+134\"\n    it_converts_to_s 1.729e+135, \"1.729e+135\"\n    it_converts_to_s 1.729e+136, \"1.729e+136\"\n    it_converts_to_s 1.729e+137, \"1.729e+137\"\n    it_converts_to_s 1.729e+138, \"1.729e+138\"\n    it_converts_to_s 1.729e+139, \"1.729e+139\"\n    it_converts_to_s 1.729e+140, \"1.729e+140\"\n    it_converts_to_s 1.729e+141, \"1.729e+141\"\n    it_converts_to_s 1.729e+142, \"1.729e+142\"\n    it_converts_to_s 1.729e+143, \"1.729e+143\"\n    it_converts_to_s 1.729e+144, \"1.729e+144\"\n    it_converts_to_s 1.729e+145, \"1.729e+145\"\n    it_converts_to_s 1.729e+146, \"1.729e+146\"\n    it_converts_to_s 1.729e+147, \"1.729e+147\"\n    it_converts_to_s 1.729e+148, \"1.729e+148\"\n    it_converts_to_s 1.729e+149, \"1.729e+149\"\n    it_converts_to_s 1.729e+150, \"1.729e+150\"\n    it_converts_to_s 1.729e+151, \"1.729e+151\"\n    it_converts_to_s 1.729e+152, \"1.729e+152\"\n    it_converts_to_s 1.729e+153, \"1.729e+153\"\n    it_converts_to_s 1.729e+154, \"1.729e+154\"\n    it_converts_to_s 1.729e+155, \"1.729e+155\"\n    it_converts_to_s 1.729e+156, \"1.729e+156\"\n    it_converts_to_s 1.729e+157, \"1.729e+157\"\n    it_converts_to_s 1.729e+158, \"1.729e+158\"\n    it_converts_to_s 1.729e+159, \"1.729e+159\"\n    it_converts_to_s 1.729e+160, \"1.729e+160\"\n    it_converts_to_s 1.729e+161, \"1.729e+161\"\n    it_converts_to_s 1.729e+162, \"1.729e+162\"\n    it_converts_to_s 1.729e+163, \"1.729e+163\"\n    it_converts_to_s 1.729e+164, \"1.729e+164\"\n    it_converts_to_s 1.729e+165, \"1.729e+165\"\n    it_converts_to_s 1.729e+166, \"1.729e+166\"\n    it_converts_to_s 1.729e+167, \"1.729e+167\"\n    it_converts_to_s 1.729e+168, \"1.729e+168\"\n    it_converts_to_s 1.729e+169, \"1.729e+169\"\n    it_converts_to_s 1.729e+170, \"1.729e+170\"\n    it_converts_to_s 1.729e+171, \"1.729e+171\"\n    it_converts_to_s 1.729e+172, \"1.729e+172\"\n    it_converts_to_s 1.729e+173, \"1.729e+173\"\n    it_converts_to_s 1.729e+174, \"1.729e+174\"\n    it_converts_to_s 1.729e+175, \"1.729e+175\"\n    it_converts_to_s 1.729e+176, \"1.729e+176\"\n    it_converts_to_s 1.729e+177, \"1.729e+177\"\n    it_converts_to_s 1.729e+178, \"1.729e+178\"\n    it_converts_to_s 1.729e+179, \"1.729e+179\"\n    it_converts_to_s 1.729e+180, \"1.729e+180\"\n    it_converts_to_s 1.729e+181, \"1.729e+181\"\n    it_converts_to_s 1.729e+182, \"1.729e+182\"\n    it_converts_to_s 1.729e+183, \"1.729e+183\"\n    it_converts_to_s 1.729e+184, \"1.729e+184\"\n    it_converts_to_s 1.729e+185, \"1.729e+185\"\n    it_converts_to_s 1.729e+186, \"1.729e+186\"\n    it_converts_to_s 1.729e+187, \"1.729e+187\"\n    it_converts_to_s 1.729e+188, \"1.729e+188\"\n    it_converts_to_s 1.729e+189, \"1.729e+189\"\n    it_converts_to_s 1.729e+190, \"1.729e+190\"\n    it_converts_to_s 1.729e+191, \"1.729e+191\"\n    it_converts_to_s 1.729e+192, \"1.729e+192\"\n    it_converts_to_s 1.729e+193, \"1.729e+193\"\n    it_converts_to_s 1.729e+194, \"1.729e+194\"\n    it_converts_to_s 1.729e+195, \"1.729e+195\"\n    it_converts_to_s 1.729e+196, \"1.729e+196\"\n    it_converts_to_s 1.729e+197, \"1.729e+197\"\n    it_converts_to_s 1.729e+198, \"1.729e+198\"\n    it_converts_to_s 1.729e+199, \"1.729e+199\"\n    it_converts_to_s 1.729e+200, \"1.729e+200\"\n    it_converts_to_s 1.729e+201, \"1.729e+201\"\n    it_converts_to_s 1.729e+202, \"1.729e+202\"\n    it_converts_to_s 1.729e+203, \"1.729e+203\"\n    it_converts_to_s 1.729e+204, \"1.729e+204\"\n    it_converts_to_s 1.729e+205, \"1.729e+205\"\n    it_converts_to_s 1.729e+206, \"1.729e+206\"\n    it_converts_to_s 1.729e+207, \"1.729e+207\"\n    it_converts_to_s 1.729e+208, \"1.729e+208\"\n    it_converts_to_s 1.729e+209, \"1.729e+209\"\n    it_converts_to_s 1.729e+210, \"1.729e+210\"\n    it_converts_to_s 1.729e+211, \"1.729e+211\"\n    it_converts_to_s 1.729e+212, \"1.729e+212\"\n    it_converts_to_s 1.729e+213, \"1.729e+213\"\n    it_converts_to_s 1.729e+214, \"1.729e+214\"\n    it_converts_to_s 1.729e+215, \"1.729e+215\"\n    it_converts_to_s 1.729e+216, \"1.729e+216\"\n    it_converts_to_s 1.729e+217, \"1.729e+217\"\n    it_converts_to_s 1.729e+218, \"1.729e+218\"\n    it_converts_to_s 1.729e+219, \"1.729e+219\"\n    it_converts_to_s 1.729e+220, \"1.729e+220\"\n    it_converts_to_s 1.729e+221, \"1.729e+221\"\n    it_converts_to_s 1.729e+222, \"1.729e+222\"\n    it_converts_to_s 1.729e+223, \"1.729e+223\"\n    it_converts_to_s 1.729e+224, \"1.729e+224\"\n    it_converts_to_s 1.729e+225, \"1.729e+225\"\n    it_converts_to_s 1.729e+226, \"1.729e+226\"\n    it_converts_to_s 1.729e+227, \"1.729e+227\"\n    it_converts_to_s 1.729e+228, \"1.729e+228\"\n    it_converts_to_s 1.729e+229, \"1.729e+229\"\n    it_converts_to_s 1.729e+230, \"1.729e+230\"\n    it_converts_to_s 1.729e+231, \"1.729e+231\"\n    it_converts_to_s 1.729e+232, \"1.729e+232\"\n    it_converts_to_s 1.729e+233, \"1.729e+233\"\n    it_converts_to_s 1.729e+234, \"1.729e+234\"\n    it_converts_to_s 1.729e+235, \"1.729e+235\"\n    it_converts_to_s 1.729e+236, \"1.729e+236\"\n    it_converts_to_s 1.729e+237, \"1.729e+237\"\n    it_converts_to_s 1.729e+238, \"1.729e+238\"\n    it_converts_to_s 1.729e+239, \"1.729e+239\"\n    it_converts_to_s 1.729e+240, \"1.729e+240\"\n    it_converts_to_s 1.729e+241, \"1.729e+241\"\n    it_converts_to_s 1.729e+242, \"1.729e+242\"\n    it_converts_to_s 1.729e+243, \"1.729e+243\"\n    it_converts_to_s 1.729e+244, \"1.729e+244\"\n    it_converts_to_s 1.729e+245, \"1.729e+245\"\n    it_converts_to_s 1.729e+246, \"1.729e+246\"\n    it_converts_to_s 1.729e+247, \"1.729e+247\"\n    it_converts_to_s 1.729e+248, \"1.729e+248\"\n    it_converts_to_s 1.729e+249, \"1.729e+249\"\n    it_converts_to_s 1.729e+250, \"1.729e+250\"\n    it_converts_to_s 1.729e+251, \"1.729e+251\"\n    it_converts_to_s 1.729e+252, \"1.729e+252\"\n    it_converts_to_s 1.729e+253, \"1.729e+253\"\n    it_converts_to_s 1.729e+254, \"1.729e+254\"\n    it_converts_to_s 1.729e+255, \"1.729e+255\"\n    it_converts_to_s 1.729e+256, \"1.729e+256\"\n    it_converts_to_s 1.729e+257, \"1.729e+257\"\n    it_converts_to_s 1.729e+258, \"1.729e+258\"\n    it_converts_to_s 1.729e+259, \"1.729e+259\"\n    it_converts_to_s 1.729e+260, \"1.729e+260\"\n    it_converts_to_s 1.729e+261, \"1.729e+261\"\n    it_converts_to_s 1.729e+262, \"1.729e+262\"\n    it_converts_to_s 1.729e+263, \"1.729e+263\"\n    it_converts_to_s 1.729e+264, \"1.729e+264\"\n    it_converts_to_s 1.729e+265, \"1.729e+265\"\n    it_converts_to_s 1.729e+266, \"1.729e+266\"\n    it_converts_to_s 1.729e+267, \"1.729e+267\"\n    it_converts_to_s 1.729e+268, \"1.729e+268\"\n    it_converts_to_s 1.729e+269, \"1.729e+269\"\n    it_converts_to_s 1.729e+270, \"1.729e+270\"\n    it_converts_to_s 1.729e+271, \"1.729e+271\"\n    it_converts_to_s 1.729e+272, \"1.729e+272\"\n    it_converts_to_s 1.729e+273, \"1.729e+273\"\n    it_converts_to_s 1.729e+274, \"1.729e+274\"\n    it_converts_to_s 1.729e+275, \"1.729e+275\"\n    it_converts_to_s 1.729e+276, \"1.729e+276\"\n    it_converts_to_s 1.729e+277, \"1.729e+277\"\n    it_converts_to_s 1.729e+278, \"1.729e+278\"\n    it_converts_to_s 1.729e+279, \"1.729e+279\"\n    it_converts_to_s 1.729e+280, \"1.729e+280\"\n    it_converts_to_s 1.729e+281, \"1.729e+281\"\n    it_converts_to_s 1.729e+282, \"1.729e+282\"\n    it_converts_to_s 1.729e+283, \"1.729e+283\"\n    it_converts_to_s 1.729e+284, \"1.729e+284\"\n    it_converts_to_s 1.729e+285, \"1.729e+285\"\n    it_converts_to_s 1.729e+286, \"1.729e+286\"\n    it_converts_to_s 1.729e+287, \"1.729e+287\"\n    it_converts_to_s 1.729e+288, \"1.729e+288\"\n    it_converts_to_s 1.729e+289, \"1.729e+289\"\n    it_converts_to_s 1.729e+290, \"1.729e+290\"\n    it_converts_to_s 1.729e+291, \"1.729e+291\"\n    it_converts_to_s 1.729e+292, \"1.729e+292\"\n    it_converts_to_s 1.729e+293, \"1.729e+293\"\n    it_converts_to_s 1.729e+294, \"1.729e+294\"\n    it_converts_to_s 1.729e+295, \"1.729e+295\"\n    it_converts_to_s 1.729e+296, \"1.729e+296\"\n    it_converts_to_s 1.729e+297, \"1.729e+297\"\n    it_converts_to_s 1.729e+298, \"1.729e+298\"\n    it_converts_to_s 1.729e+299, \"1.729e+299\"\n    it_converts_to_s 1.729e+300, \"1.729e+300\"\n    it_converts_to_s 1.729e+301, \"1.729e+301\"\n    it_converts_to_s 1.729e+302, \"1.729e+302\"\n    it_converts_to_s 1.729e+303, \"1.729e+303\"\n    it_converts_to_s 1.729e+304, \"1.729e+304\"\n    it_converts_to_s 1.729e+305, \"1.729e+305\"\n    it_converts_to_s 1.729e+306, \"1.729e+306\"\n    it_converts_to_s 1.729e+307, \"1.729e+307\"\n    it_converts_to_s 1.729e+308, \"1.729e+308\"\n  end\n\n  context \"one-digit cases, where the decimal point can't appear between digits like \\\"17.29\\\"\" do\n    it_converts_to_s 7e-3, \"0.007\"\n    it_converts_to_s 7e-2, \"0.07\"\n    it_converts_to_s 7e-1, \"0.7\"\n    it_converts_to_s 7e+0, \"7.0\"\n    it_converts_to_s 7e+1, \"70.0\"\n    it_converts_to_s 7e+2, \"700.0\"\n    it_converts_to_s 7e+3, \"7000.0\"\n  end\n\n  context \"highly-trimmed powers of 2\" do\n    it_converts_to_s hexfloat(\"0x1p959\"), \"4.8726570057e+288\"\n    it_converts_to_s hexfloat(\"0x1p960\"), \"9.7453140114e+288\"\n  end\n\n  context \"odd mantissas (unaffected by shifting)\" do\n    it_converts_to_s 1801439850948197e1, \"1.801439850948197e+16\"\n    it_converts_to_s 360287970189639e2, \"3.60287970189639e+16\"\n    it_converts_to_s 72057594037927e3, \"7.2057594037927e+16\"\n    it_converts_to_s 14411518807585e4, \"1.4411518807585e+17\"\n    it_converts_to_s 2882303761517e5, \"2.882303761517e+17\"\n    it_converts_to_s 576460752303e6, \"5.76460752303e+17\"\n    it_converts_to_s 115292150459e7, \"1.15292150459e+18\"\n    it_converts_to_s 23058430091e8, \"2.3058430091e+18\"\n    it_converts_to_s 4611686017e9, \"4.611686017e+18\"\n    it_converts_to_s 922337203e10, \"9.22337203e+18\"\n    it_converts_to_s 184467439e11, \"1.84467439e+19\"\n    it_converts_to_s 36893487e12, \"3.6893487e+19\"\n    it_converts_to_s 7378697e13, \"7.378697e+19\"\n    it_converts_to_s 1475739e14, \"1.475739e+20\"\n    it_converts_to_s 295147e15, \"2.95147e+20\"\n    it_converts_to_s 59029e16, \"5.9029e+20\"\n    it_converts_to_s 11805e17, \"1.1805e+21\"\n    it_converts_to_s 2361e18, \"2.361e+21\"\n    it_converts_to_s 471e19, \"4.71e+21\"\n    it_converts_to_s 93e20, \"9.3e+21\"\n    it_converts_to_s 17e21, \"1.7e+22\"\n    it_converts_to_s 3e22, \"3.0e+22\"\n    it_converts_to_s 1801439850948199e1, \"1.801439850948199e+16\"\n    it_converts_to_s 360287970189641e2, \"3.60287970189641e+16\"\n    it_converts_to_s 72057594037929e3, \"7.2057594037929e+16\"\n    it_converts_to_s 14411518807587e4, \"1.4411518807587e+17\"\n    it_converts_to_s 2882303761519e5, \"2.882303761519e+17\"\n    it_converts_to_s 576460752305e6, \"5.76460752305e+17\"\n    it_converts_to_s 115292150461e7, \"1.15292150461e+18\"\n    it_converts_to_s 23058430093e8, \"2.3058430093e+18\"\n    it_converts_to_s 4611686019e9, \"4.611686019e+18\"\n    it_converts_to_s 922337205e10, \"9.22337205e+18\"\n    it_converts_to_s 184467441e11, \"1.84467441e+19\"\n    it_converts_to_s 36893489e12, \"3.6893489e+19\"\n    it_converts_to_s 7378699e13, \"7.378699e+19\"\n    it_converts_to_s 1475741e14, \"1.475741e+20\"\n    it_converts_to_s 295149e15, \"2.95149e+20\"\n    it_converts_to_s 59031e16, \"5.9031e+20\"\n    it_converts_to_s 11807e17, \"1.1807e+21\"\n    it_converts_to_s 2363e18, \"2.363e+21\"\n    it_converts_to_s 473e19, \"4.73e+21\"\n    it_converts_to_s 95e20, \"9.5e+21\"\n    it_converts_to_s 19e21, \"1.9e+22\"\n    it_converts_to_s 5e22, \"5.0e+22\"\n    it_converts_to_s 302230528e15, \"3.02230528e+23\"\n    it_converts_to_s 302232576e15, \"3.02232576e+23\"\n    it_converts_to_s 81123342286848e18, \"8.1123342286848e+31\"\n    it_converts_to_s 81192061763584e18, \"8.1192061763584e+31\"\n  end\n\n  context \"Grisu failures\" do\n    it_converts_to_s hexfloat(\"0x1.e0ffed391517ep-186\"), \"1.9156918820264798e-56\"\n    it_converts_to_s hexfloat(\"0x1.a6c767640cd71p+879\"), \"6.6564021122018745e+264\"\n    it_converts_to_s 4.91e-6\n    it_converts_to_s 5.547e-6\n    it_converts_to_s 1.0e+23\n    it_converts_to_s 3.5844466002796428e+298\n  end\n\n  # https://www.exploringbinary.com/the-shortest-decimal-string-that-round-trips-may-not-be-the-nearest/\n  context \"anomalous values\" do\n    it_converts_to_s hexfloat(\"0x1p976\"), \"6.386688990511104e+293\"\n    it_converts_to_s hexfloat(\"0x1p896\"), \"5.282945311356653e+269\"\n    it_converts_to_s hexfloat(\"0x1p863\"), \"6.150157786156811e+259\"\n    it_converts_to_s hexfloat(\"0x1p803\"), \"5.334411546303884e+241\"\n    it_converts_to_s hexfloat(\"0x1p710\"), \"5.386379163185535e+213\"\n    it_converts_to_s hexfloat(\"0x1p594\"), \"6.483618076376552e+178\"\n    it_converts_to_s hexfloat(\"0x1p574\"), \"6.183260036827614e+172\"\n    it_converts_to_s hexfloat(\"0x1p554\"), \"5.896816288783659e+166\"\n    it_converts_to_s hexfloat(\"0x1p544\"), \"5.758609657015292e+163\"\n    it_converts_to_s hexfloat(\"0x1p534\"), \"5.623642243178996e+160\"\n    it_converts_to_s hexfloat(\"0x1p481\"), \"6.243497100631985e+144\"\n    it_converts_to_s hexfloat(\"0x1p405\"), \"8.263199609878108e+121\"\n    it_converts_to_s hexfloat(\"0x1p398\"), \"6.455624695217272e+119\"\n    it_converts_to_s hexfloat(\"0x1p378\"), \"6.156563468186638e+113\"\n    it_converts_to_s hexfloat(\"0x1p345\"), \"7.167183174968974e+103\"\n    it_converts_to_s hexfloat(\"0x1p305\"), \"6.518515124270356e+91\"\n    it_converts_to_s hexfloat(\"0x1p275\"), \"6.070840288205404e+82\"\n    it_converts_to_s hexfloat(\"0x1p182\"), \"6.129982163463556e+54\"\n    it_converts_to_s hexfloat(\"0x1p172\"), \"5.986310706507379e+51\"\n    it_converts_to_s hexfloat(\"0x1p132\"), \"5.444517870735016e+39\"\n    it_converts_to_s hexfloat(\"0x1p122\"), \"5.316911983139664e+36\"\n    it_converts_to_s hexfloat(\"0x1p89\"), \"6.189700196426902e+26\"\n    it_converts_to_s hexfloat(\"0x1p-24\"), \"5.960464477539063e-8\"\n    it_converts_to_s hexfloat(\"0x1p-44\"), \"5.684341886080802e-14\"\n    it_converts_to_s hexfloat(\"0x1p-77\"), \"6.617444900424222e-24\"\n    it_converts_to_s hexfloat(\"0x1p-97\"), \"6.310887241768095e-30\"\n    it_converts_to_s hexfloat(\"0x1p-140\"), \"7.174648137343064e-43\"\n    it_converts_to_s hexfloat(\"0x1p-296\"), \"7.854549544476363e-90\"\n    it_converts_to_s hexfloat(\"0x1p-366\"), \"6.653062250012736e-111\"\n    it_converts_to_s hexfloat(\"0x1p-383\"), \"5.075883674631299e-116\"\n    it_converts_to_s hexfloat(\"0x1p-489\"), \"6.256509672447191e-148\"\n    it_converts_to_s hexfloat(\"0x1p-496\"), \"4.887898181599368e-150\"\n    it_converts_to_s hexfloat(\"0x1p-509\"), \"5.966672584960166e-154\"\n    it_converts_to_s hexfloat(\"0x1p-549\"), \"5.426657103235053e-166\"\n    it_converts_to_s hexfloat(\"0x1p-652\"), \"5.351097043477547e-197\"\n    it_converts_to_s hexfloat(\"0x1p-662\"), \"5.225680706521042e-200\"\n    it_converts_to_s hexfloat(\"0x1p-695\"), \"6.083493012144512e-210\"\n    it_converts_to_s hexfloat(\"0x1p-705\"), \"5.940911144672375e-213\"\n    it_converts_to_s hexfloat(\"0x1p-778\"), \"6.290184345309701e-235\"\n    it_converts_to_s hexfloat(\"0x1p-788\"), \"6.142758149716505e-238\"\n    it_converts_to_s hexfloat(\"0x1p-791\"), \"7.678447687145631e-239\"\n    it_converts_to_s hexfloat(\"0x1p-808\"), \"5.858190679279809e-244\"\n    it_converts_to_s hexfloat(\"0x1p-921\"), \"5.641232424577593e-278\"\n    it_converts_to_s hexfloat(\"0x1p-957\"), \"8.209073602596753e-289\"\n    it_converts_to_s hexfloat(\"0x1p-1007\"), \"7.291122019556398e-304\"\n    it_converts_to_s hexfloat(\"0x1p-1017\"), \"7.120236347223045e-307\"\n  end\n\n  context \"almost-but-not-quite-anomalous values\" do\n    it_converts_to_s hexfloat(\"0x1p966\"), \"6.237000967296e+290\"\n    it_converts_to_s hexfloat(\"0x1p956\"), \"6.090821257125e+287\"\n    it_converts_to_s hexfloat(\"0x1p890\"), \"8.25460204899477e+267\"\n    it_converts_to_s hexfloat(\"0x1p740\"), \"5.78358058743443e+222\"\n    it_converts_to_s hexfloat(\"0x1p149\"), \"7.1362384635298e+44\"\n    it_converts_to_s hexfloat(\"0x1p-499\"), \"6.10987272699921e-151\"\n    it_converts_to_s hexfloat(\"0x1p-569\"), \"5.17526350329881e-172\"\n    it_converts_to_s hexfloat(\"0x1p-645\"), \"6.84940421565126e-195\"\n  end\nend\n"
  },
  {
    "path": "spec/std/float_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/string\"\n\ndescribe \"Float\" do\n  describe \"**\" do\n    it { (2.5_f32 ** 2_i32).should be_close(6.25_f32, 0.0001) }\n    it { (2.5_f32 ** 2).should be_close(6.25_f32, 0.0001) }\n    it { (2.5_f32 ** 2.5_f32).should be_close(9.882117688026186_f32, 0.0001) }\n    it { (2.5_f32 ** 2.5).should be_close(9.882117688026186_f32, 0.0001) }\n    it { (2.5_f64 ** 2_i32).should be_close(6.25_f64, 0.0001) }\n    it { (2.5_f64 ** 2).should be_close(6.25_f64, 0.0001) }\n    it { (2.5_f64 ** 2.5_f64).should be_close(9.882117688026186_f64, 0.0001) }\n    it { (2.5_f64 ** 2.5).should be_close(9.882117688026186_f64, 0.001) }\n  end\n\n  describe \"%\" do\n    it \"uses modulo behavior, not remainder behavior\" do\n      ((-11.5) % 4.0).should eq(0.5)\n    end\n  end\n\n  describe \"modulo\" do\n    it \"raises when mods by zero\" do\n      expect_raises(DivisionByZeroError) { 1.2.modulo 0.0 }\n    end\n\n    it { (13.0.modulo 4.0).should eq(1.0) }\n    it { (13.0.modulo -4.0).should eq(-3.0) }\n    it { (-13.0.modulo 4.0).should eq(3.0) }\n    it { (-13.0.modulo -4.0).should eq(-1.0) }\n    it { (11.5.modulo 4.0).should eq(3.5) }\n    it { (11.5.modulo -4.0).should eq(-0.5) }\n    it { (-11.5.modulo 4.0).should eq(0.5) }\n    it { (-11.5.modulo -4.0).should eq(-3.5) }\n  end\n\n  describe \"remainder\" do\n    it \"raises when mods by zero\" do\n      expect_raises(DivisionByZeroError) { 1.2.remainder 0.0 }\n    end\n\n    it { (13.0.remainder 4.0).should eq(1.0) }\n    it { (13.0.remainder -4.0).should eq(1.0) }\n    it { (-13.0.remainder 4.0).should eq(-1.0) }\n    it { (-13.0.remainder -4.0).should eq(-1.0) }\n    it { (11.5.remainder 4.0).should eq(3.5) }\n    it { (11.5.remainder -4.0).should eq(3.5) }\n    it { (-11.5.remainder 4.0).should eq(-3.5) }\n    it { (-11.5.remainder -4.0).should eq(-3.5) }\n\n    it \"preserves type\" do\n      r = 1.5_f32.remainder(1)\n      typeof(r).should eq(Float32)\n    end\n  end\n\n  describe \"round\" do\n    it { 2.5.round.should eq(2) }\n    it { 3.5.round.should eq(4) }\n    it { 2.4.round.should eq(2) }\n  end\n\n  describe \"floor\" do\n    it { 2.1.floor.should eq(2) }\n    it { 2.9.floor.should eq(2) }\n  end\n\n  describe \"ceil\" do\n    it { 2.0_f32.ceil.should eq(2) }\n    it { 2.0.ceil.should eq(2) }\n\n    it { 2.1_f32.ceil.should eq(3_f32) }\n    it { 2.1.ceil.should eq(3) }\n\n    it { 2.9_f32.ceil.should eq(3) }\n    it { 2.9.ceil.should eq(3) }\n  end\n\n  describe \"#integer?\" do\n    it { 1.0_f32.integer?.should be_true }\n    it { 1.0_f64.integer?.should be_true }\n\n    it { 1.2_f32.integer?.should be_false }\n    it { 1.2_f64.integer?.should be_false }\n\n    it { Float32::MAX.integer?.should be_true }\n    it { Float64::MAX.integer?.should be_true }\n\n    it { Float32::INFINITY.integer?.should be_false }\n    it { Float64::INFINITY.integer?.should be_false }\n\n    it { Float32::NAN.integer?.should be_false }\n    it { Float64::NAN.integer?.should be_false }\n  end\n\n  describe \"fdiv\" do\n    it { 1.0.fdiv(1).should eq 1.0 }\n    it { 1.0.fdiv(2).should eq 0.5 }\n    it { 1.0.fdiv(0.5).should eq 2.0 }\n    it { 0.0.fdiv(1).should eq 0.0 }\n    it { 1.0.fdiv(0).should eq 1.0/0.0 }\n  end\n\n  describe \"divmod\" do\n    it { 1.2.divmod(0.3)[0].should eq(4) }\n    it { 1.2.divmod(0.3)[1].should be_close(0.0, 0.00001) }\n\n    it { 1.3.divmod(0.3)[0].should eq(4) }\n    it { 1.3.divmod(0.3)[1].should be_close(0.1, 0.00001) }\n\n    it { 1.4.divmod(0.3)[0].should eq(4) }\n    it { 1.4.divmod(0.3)[1].should be_close(0.2, 0.00001) }\n\n    it { -1.2.divmod(0.3)[0].should eq(-4) }\n    it { -1.2.divmod(0.3)[1].should be_close(0.0, 0.00001) }\n\n    it { -1.3.divmod(0.3)[0].should eq(-5) }\n    it { -1.3.divmod(0.3)[1].should be_close(0.2, 0.00001) }\n\n    it { -1.4.divmod(0.3)[0].should eq(-5) }\n    it { -1.4.divmod(0.3)[1].should be_close(0.1, 0.00001) }\n\n    it { 1.2.divmod(-0.3)[0].should eq(-4) }\n    it { 1.2.divmod(-0.3)[1].should be_close(0.0, 0.00001) }\n\n    it { 1.3.divmod(-0.3)[0].should eq(-5) }\n    it { 1.3.divmod(-0.3)[1].should be_close(-0.2, 0.00001) }\n\n    it { 1.4.divmod(-0.3)[0].should eq(-5) }\n    it { 1.4.divmod(-0.3)[1].should be_close(-0.1, 0.00001) }\n\n    it { -1.2.divmod(-0.3)[0].should eq(4) }\n    it { -1.2.divmod(-0.3)[1].should be_close(0.0, 0.00001) }\n\n    it { -1.3.divmod(-0.3)[0].should eq(4) }\n    it { -1.3.divmod(-0.3)[1].should be_close(-0.1, 0.00001) }\n\n    it { -1.4.divmod(-0.3)[0].should eq(4) }\n    it { -1.4.divmod(-0.3)[1].should be_close(-0.2, 0.00001) }\n  end\n\n  describe \"floor division //\" do\n    it \"preserves type of lhs\" do\n      (7.0 // 2).should be_a(Float64)\n      (7.0 // 2i32).should be_a(Float64)\n      (7.0 // 2.0).should be_a(Float64)\n      (7.0_f32 // 2.0_f64).should be_a(Float32)\n      (7.0_f32 // 2.0_f32).should be_a(Float32)\n    end\n\n    it \"applies floor\" do\n      (7.0 // 2.0).should eq(3.0)\n      (-7.0 // 2.0).should eq(-4.0)\n\n      (6.0 // 2.0).should eq(3.0)\n      (-6.0 // 2.0).should eq(-3.0)\n\n      (30.3 // 3.9).should eq(7.0)\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"does to_s for f64\" do\n      assert_prints 12.34.to_s, \"12.34\"\n      assert_prints 1.2.to_s, \"1.2\"\n      assert_prints 1.23.to_s, \"1.23\"\n      assert_prints 1.234.to_s, \"1.234\"\n      assert_prints 0.65000000000000002.to_s, \"0.65\"\n      assert_prints 1.234001.to_s, \"1.234001\"\n      assert_prints 1.23499.to_s, \"1.23499\"\n      assert_prints 1.23499999999999999.to_s, \"1.235\"\n      assert_prints 1.2345.to_s, \"1.2345\"\n      assert_prints 1.23456.to_s, \"1.23456\"\n      assert_prints 1.234567.to_s, \"1.234567\"\n      assert_prints 1.2345678.to_s, \"1.2345678\"\n      assert_prints 1.23456789.to_s, \"1.23456789\"\n      assert_prints 1.234567891.to_s, \"1.234567891\"\n      assert_prints 1.2345678911.to_s, \"1.2345678911\"\n      assert_prints 1.2345678912.to_s, \"1.2345678912\"\n      assert_prints 1.23456789123.to_s, \"1.23456789123\"\n      assert_prints 9525365.25.to_s, \"9525365.25\"\n      assert_prints 12.9999.to_s, \"12.9999\"\n      assert_prints 12.9999999999999999.to_s, \"13.0\"\n      assert_prints 1.0.to_s, \"1.0\"\n      assert_prints 2e20.to_s, \"2.0e+20\"\n      assert_prints 1e-10.to_s, \"1.0e-10\"\n      assert_prints 1464132168.65.to_s, \"1464132168.65\"\n      assert_prints 146413216.865.to_s, \"146413216.865\"\n      assert_prints 14641321.6865.to_s, \"14641321.6865\"\n      assert_prints 1464132.16865.to_s, \"1464132.16865\"\n      assert_prints 654329382.1.to_s, \"654329382.1\"\n      assert_prints 6543293824.1.to_s, \"6543293824.1\"\n      assert_prints 65432938242.1.to_s, \"65432938242.1\"\n      assert_prints 654329382423.1.to_s, \"654329382423.1\"\n      assert_prints 6543293824234.1.to_s, \"6543293824234.1\"\n      assert_prints 65432938242345.1.to_s, \"65432938242345.1\"\n      assert_prints 65432.123e20.to_s, \"6.5432123e+24\"\n      assert_prints 65432.123e200.to_s, \"6.5432123e+204\"\n      assert_prints -65432.123e200.to_s, \"-6.5432123e+204\"\n      assert_prints 65432.123456e20.to_s, \"6.5432123456e+24\"\n      assert_prints 65432.1234567e20.to_s, \"6.54321234567e+24\"\n      assert_prints 65432.12345678e20.to_s, \"6.543212345678e+24\"\n      assert_prints 65432.1234567891e20.to_s, \"6.54321234567891e+24\"\n      assert_prints (1.0/0.0).to_s, \"Infinity\"\n      assert_prints (-1.0/0.0).to_s, \"-Infinity\"\n      assert_prints (0.999999999999999989).to_s, \"1.0\"\n    end\n\n    it \"does to_s for f32\" do\n      assert_prints 12.34_f32.to_s, \"12.34\"\n      assert_prints 1.2_f32.to_s, \"1.2\"\n      assert_prints 1.23_f32.to_s, \"1.23\"\n      assert_prints 1.234_f32.to_s, \"1.234\"\n      assert_prints 0.65000000000000002_f32.to_s, \"0.65\"\n      # assert_prints 1.234001_f32.to_s, \"1.234001\"\n      assert_prints 1.23499_f32.to_s, \"1.23499\"\n      assert_prints 1.23499999999999_f32.to_s, \"1.235\"\n      assert_prints 1.2345_f32.to_s, \"1.2345\"\n      assert_prints 1.23456_f32.to_s, \"1.23456\"\n      # assert_prints 9525365.25_f32.to_s, \"9525365.25\"\n      assert_prints (1.0_f32/0.0_f32).to_s, \"Infinity\"\n      assert_prints (-1.0_f32/0.0_f32).to_s, \"-Infinity\"\n    end\n  end\n\n  describe \"#next_float\" do\n    it \"does for f64\" do\n      0.0.next_float.should eq(Float64::MIN_SUBNORMAL)\n      1.0.next_float.should eq(1.0000000000000002)\n      (-1.0).next_float.should eq(-0.9999999999999999)\n      Float64::MAX.next_float.should eq(Float64::INFINITY)\n      Float64::INFINITY.next_float.should eq(Float64::INFINITY)\n      (-Float64::INFINITY).next_float.should eq(Float64::MIN)\n      Float64::NAN.next_float.nan?.should be_true\n    end\n\n    it \"does for f32\" do\n      0.0_f32.next_float.should eq(Float32::MIN_SUBNORMAL)\n      1.0_f32.next_float.should eq(1.0000001_f32)\n      (-1.0_f32).next_float.should eq(-0.99999994_f32)\n      Float32::MAX.next_float.should eq(Float32::INFINITY)\n      Float32::INFINITY.next_float.should eq(Float32::INFINITY)\n      (-Float32::INFINITY).next_float.should eq(Float32::MIN)\n      Float32::NAN.next_float.nan?.should be_true\n    end\n  end\n\n  describe \"#prev_float\" do\n    it \"does for f64\" do\n      0.0.prev_float.should eq(-Float64::MIN_SUBNORMAL)\n      1.0.prev_float.should eq(0.9999999999999999)\n      (-1.0).prev_float.should eq(-1.0000000000000002)\n      Float64::MIN.prev_float.should eq(-Float64::INFINITY)\n      Float64::INFINITY.prev_float.should eq(Float64::MAX)\n      (-Float64::INFINITY).prev_float.should eq(-Float64::INFINITY)\n      Float64::NAN.prev_float.nan?.should be_true\n    end\n\n    it \"does for f32\" do\n      0.0_f32.prev_float.should eq(-Float32::MIN_SUBNORMAL)\n      1.0_f32.prev_float.should eq(0.99999994_f32)\n      (-1.0_f32).prev_float.should eq(-1.0000001_f32)\n      Float32::MIN.prev_float.should eq(-Float32::INFINITY)\n      Float32::INFINITY.prev_float.should eq(Float32::MAX)\n      (-Float32::INFINITY).prev_float.should eq(-Float32::INFINITY)\n      Float32::NAN.prev_float.nan?.should be_true\n    end\n  end\n\n  describe \"#inspect\" do\n    it \"does inspect for f64\" do\n      3.2.inspect.should eq(\"3.2\")\n    end\n\n    it \"does inspect for f32\" do\n      3.2_f32.inspect.should eq(\"3.2\")\n    end\n\n    it \"does inspect for f64 with IO\" do\n      str = String.build { |io| 3.2.inspect(io) }\n      str.should eq(\"3.2\")\n    end\n\n    it \"does inspect for f32\" do\n      str = String.build { |io| 3.2_f32.inspect(io) }\n      str.should eq(\"3.2\")\n    end\n  end\n\n  describe \"hash\" do\n    it \"does for Float32\" do\n      1.2_f32.hash.should eq(1.2_f32.hash)\n    end\n\n    it \"does for Float64\" do\n      1.2.hash.should eq(1.2.hash)\n    end\n  end\n\n  describe \".new\" do\n    it \"String overload\" do\n      Float32.new(\"1\").should be_a(Float32)\n      Float32.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid Float32: \" 1 \") do\n        Float32.new(\" 1 \", whitespace: false)\n      end\n\n      Float64.new(\"1\").should be_a(Float64)\n      Float64.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid Float64: \" 1 \") do\n        Float64.new(\" 1 \", whitespace: false)\n      end\n    end\n\n    it \"fallback overload\" do\n      Float32.new(1_f64).should be_a(Float32)\n      Float32.new(1_f64).should eq(1)\n\n      Float64.new(1_f32).should be_a(Float64)\n      Float64.new(1_f32).should eq(1)\n    end\n  end\n\n  it \"does nan?\" do\n    1.5.nan?.should be_false\n    (0.0 / 0.0).nan?.should be_true\n  end\n\n  it \"does infinite?\" do\n    (0.0).infinite?.should be_nil\n    (-1.0/0.0).infinite?.should eq(-1)\n    (1.0/0.0).infinite?.should eq(1)\n\n    (0.0_f32).infinite?.should be_nil\n    (-1.0_f32/0.0_f32).infinite?.should eq(-1)\n    (1.0_f32/0.0_f32).infinite?.should eq(1)\n  end\n\n  it \"does finite?\" do\n    0.0.finite?.should be_true\n    1.5.finite?.should be_true\n    (1.0/0.0).finite?.should be_false\n    (-1.0/0.0).finite?.should be_false\n    (-0.0/0.0).finite?.should be_false\n  end\n\n  {% if compare_versions(Crystal::VERSION, \"0.36.1\") > 0 %}\n    it \"converts infinity\" do\n      Float32::INFINITY.to_f64.infinite?.should eq 1\n      Float32::INFINITY.to_f32.infinite?.should eq 1\n      expect_raises(OverflowError) { Float32::INFINITY.to_i }\n      (-Float32::INFINITY).to_f64.infinite?.should eq -1\n      (-Float32::INFINITY).to_f32.infinite?.should eq -1\n      expect_raises(OverflowError) { (-Float32::INFINITY).to_i }\n\n      Float64::INFINITY.to_f64.infinite?.should eq 1\n      Float64::INFINITY.to_f32.infinite?.should eq 1\n      expect_raises(OverflowError) { Float64::INFINITY.to_i }\n      (-Float64::INFINITY).to_f64.infinite?.should eq -1\n      (-Float64::INFINITY).to_f32.infinite?.should eq -1\n      expect_raises(OverflowError) { (-Float64::INFINITY).to_i }\n    end\n  {% else %}\n    pending \"converts infinity\"\n  {% end %}\n\n  it \"does unary -\" do\n    f = -(1.5)\n    f.should eq(-1.5)\n    f.should be_a(Float64)\n\n    f = -(1.5_f32)\n    f.should eq(-1.5_f32)\n    f.should be_a(Float32)\n\n    (-(0.0)).sign_bit.should eq(-1)\n    (-(-0.0)).sign_bit.should eq(1)\n\n    (-(0.0_f32)).sign_bit.should eq(-1)\n    (-(-0.0_f32)).sign_bit.should eq(1)\n\n    (-Math.copysign(Float64::NAN, 1.0)).sign_bit.should eq(-1)\n    (-Math.copysign(Float64::NAN, -1.0)).sign_bit.should eq(1)\n  end\n\n  it \"clones\" do\n    1.0.clone.should eq(1.0)\n    1.0_f32.clone.should eq(1.0_f32)\n  end\n\n  it \"constants have right binary value\" do\n    Float32::MIN.unsafe_as(UInt32).should eq 0xff7fffff_u32\n    Float32::MAX.unsafe_as(UInt32).should eq 0x7f7fffff_u32\n    Float32::EPSILON.unsafe_as(UInt32).should eq 0x34000000_u32\n    Float32::MIN_POSITIVE.unsafe_as(UInt32).should eq 0x00800000_u32\n\n    Float64::MIN.unsafe_as(UInt64).should eq 0xffefffffffffffff_u64\n    Float64::MAX.unsafe_as(UInt64).should eq 0x7fefffffffffffff_u64\n    Float64::EPSILON.unsafe_as(UInt64).should eq 0x3cb0000000000000_u64\n    Float64::MIN_POSITIVE.unsafe_as(UInt64).should eq 0x0010000000000000_u64\n  end\n\n  it \"returns nil in <=> for NaN values (Float32)\" do\n    nan = Float32::NAN\n\n    (1_f32 <=> nan).should be_nil\n    (1_f64 <=> nan).should be_nil\n\n    (1_u8 <=> nan).should be_nil\n    (1_u16 <=> nan).should be_nil\n    (1_u32 <=> nan).should be_nil\n    (1_u64 <=> nan).should be_nil\n    (1_i8 <=> nan).should be_nil\n    (1_i16 <=> nan).should be_nil\n    (1_i32 <=> nan).should be_nil\n    (1_i64 <=> nan).should be_nil\n\n    (nan <=> 1_u8).should be_nil\n    (nan <=> 1_u16).should be_nil\n    (nan <=> 1_u32).should be_nil\n    (nan <=> 1_u64).should be_nil\n    (nan <=> 1_i8).should be_nil\n    (nan <=> 1_i16).should be_nil\n    (nan <=> 1_i32).should be_nil\n    (nan <=> 1_i64).should be_nil\n  end\n\n  it \"returns nil in <=> for NaN values (Float64)\" do\n    nan = Float64::NAN\n\n    (1_f32 <=> nan).should be_nil\n    (1_f64 <=> nan).should be_nil\n\n    (1_u8 <=> nan).should be_nil\n    (1_u16 <=> nan).should be_nil\n    (1_u32 <=> nan).should be_nil\n    (1_u64 <=> nan).should be_nil\n    (1_i8 <=> nan).should be_nil\n    (1_i16 <=> nan).should be_nil\n    (1_i32 <=> nan).should be_nil\n    (1_i64 <=> nan).should be_nil\n\n    (nan <=> 1_u8).should be_nil\n    (nan <=> 1_u16).should be_nil\n    (nan <=> 1_u32).should be_nil\n    (nan <=> 1_u64).should be_nil\n    (nan <=> 1_i8).should be_nil\n    (nan <=> 1_i16).should be_nil\n    (nan <=> 1_i32).should be_nil\n    (nan <=> 1_i64).should be_nil\n  end\n\n  it \"#abs\" do\n    0.0_f64.abs.sign_bit.should eq 1\n    -0.0_f64.abs.sign_bit.should eq 1\n\n    0.1_f64.abs.should eq 0.1_f64\n    -0.1_f64.abs.should eq 0.1_f64\n\n    0.0_f32.abs.sign_bit.should eq 1_f32\n    -0.0_f32.abs.sign_bit.should eq 1_f32\n\n    0.1_f32.abs.should eq 0.1_f32\n    -0.1_f32.abs.should eq 0.1_f32\n\n    Float64::MAX.abs.should eq Float64::MAX\n    Float64::MIN.abs.should eq -Float64::MIN\n    Float64::INFINITY.abs.should eq Float64::INFINITY\n    (-Float64::INFINITY).abs.should eq Float64::INFINITY\n\n    Float32::MAX.abs.should eq Float32::MAX\n    Float32::MIN.abs.should eq -Float32::MIN\n    Float32::INFINITY.abs.should eq Float32::INFINITY\n    (-Float32::INFINITY).abs.should eq Float32::INFINITY\n  end\n\n  it \"#sign_bit\" do\n    1.2_f64.sign_bit.should eq(1)\n    -1.2_f64.sign_bit.should eq(-1)\n    0.0_f64.sign_bit.should eq(1)\n    -0.0_f64.sign_bit.should eq(-1)\n    Float64::INFINITY.sign_bit.should eq(1)\n    (-Float64::INFINITY).sign_bit.should eq(-1)\n    0x7ff0_0000_0000_0001_u64.unsafe_as(Float64).sign_bit.should eq(1)\n    0xfff0_0000_0000_0001_u64.unsafe_as(Float64).sign_bit.should eq(-1)\n\n    1.2_f32.sign_bit.should eq(1)\n    -1.2_f32.sign_bit.should eq(-1)\n    0.0_f32.sign_bit.should eq(1)\n    -0.0_f32.sign_bit.should eq(-1)\n    Float32::INFINITY.sign_bit.should eq(1)\n    (-Float32::INFINITY).sign_bit.should eq(-1)\n    0x7f80_0001_u32.unsafe_as(Float32).sign_bit.should eq(1)\n    0xff80_0001_u32.unsafe_as(Float32).sign_bit.should eq(-1)\n  end\nend\n"
  },
  {
    "path": "spec/std/gc_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"GC\" do\n  it \"compiles GC.stats\" do\n    typeof(GC.stats).should eq(GC::Stats)\n  end\n\n  it \"raises if calling enable when not disabled\" do\n    expect_raises(Exception, \"GC is not disabled\") do\n      GC.enable\n    end\n  end\n\n  it \".stats\" do\n    GC.stats.should be_a(GC::Stats)\n  end\n\n  it \".prof_stats\" do\n    GC.prof_stats.should be_a(GC::ProfStats)\n  end\nend\n"
  },
  {
    "path": "spec/std/hash_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\nprivate alias RecursiveHash = Hash(RecursiveHash, RecursiveHash)\n\nprivate class HashBreaker\n  getter x : Int32\n\n  def initialize(@x)\n  end\nend\n\nprivate class NeverInstantiated\nend\n\nprivate alias RecursiveType = String | Int32 | Array(RecursiveType) | Hash(String, RecursiveType)\n\nprivate class HashWrapper(K, V)\n  include Enumerable({K, V})\n\n  @hash = {} of K => V\n\n  delegate each, to: @hash\nend\n\ndescribe \"Hash\" do\n  describe \"empty\" do\n    it \"size should be zero\" do\n      h = {} of Int32 => Int32\n      h.size.should eq(0)\n      h.empty?.should be_true\n    end\n  end\n\n  it \"sets and gets\" do\n    a = {} of Int32 => Int32\n    a[1] = 2\n    a[1].should eq(2)\n  end\n\n  it \"gets from literal\" do\n    a = {1 => 2}\n    a[1].should eq(2)\n  end\n\n  it \"gets from union\" do\n    a = {1 => 2, \"foo\" => 1.1}\n    a[1].should eq(2)\n  end\n\n  it \"gets nilable\" do\n    a = {1 => 2}\n    a[1]?.should eq(2)\n    a[2]?.should be_nil\n  end\n\n  it \"gets array of keys\" do\n    a = {} of String => Int32\n    a.keys.should eq([] of String)\n    a[\"foo\"] = 1\n    a[\"bar\"] = 2\n    a.keys.should eq([\"foo\", \"bar\"])\n  end\n\n  it \"gets array of values\" do\n    a = {} of String => Int32\n    a.values.should eq([] of Int32)\n    a[\"foo\"] = 1\n    a[\"bar\"] = 2\n    a.values.should eq([1, 2])\n  end\n\n  describe \"==\" do\n    it do\n      a = {1 => 2, 3 => 4}\n      b = {3 => 4, 1 => 2}\n      c = {2 => 3}\n      d = {5 => 6, 7 => 8}\n      a.should eq(a)\n      a.should eq(b)\n      b.should eq(a)\n      a.should_not eq(c)\n      c.should_not eq(a)\n      d.should_not eq(a)\n    end\n\n    it do\n      a = {1 => nil}\n      b = {3 => 4}\n      a.should_not eq(b)\n    end\n\n    it \"compares hash of nested hash\" do\n      a = { {1 => 2} => 3 }\n      b = { {1 => 2} => 3 }\n      a.should eq(b)\n    end\n  end\n\n  context \"subset/superset operators\" do\n    h1 = {\"a\" => 1, \"b\" => 2}\n    h2 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n    h3 = {\"c\" => 3}\n    h4 = {} of Nil => Nil\n\n    describe \"#proper_subset_of?\" do\n      it do\n        h1.proper_subset_of?(h2).should be_true\n        h2.proper_subset_of?(h1).should be_false\n        h1.proper_subset_of?(h1).should be_false\n        h1.proper_subset_of?(h3).should be_false\n        h1.proper_subset_of?(h4).should be_false\n      end\n\n      it \"handles edge case where both values are nil\" do\n        {\"a\" => nil}.proper_subset_of?({\"b\" => nil, \"c\" => nil}).should be_false\n      end\n    end\n\n    describe \"#subset_of?\" do\n      it do\n        h1.subset_of?(h2).should be_true\n        h2.subset_of?(h1).should be_false\n        h1.subset_of?(h1).should be_true\n        h1.subset_of?(h3).should be_false\n        h1.subset_of?(h4).should be_false\n      end\n\n      it \"handles edge case where both values are nil\" do\n        {\"a\" => nil}.subset_of?({\"b\" => nil}).should be_false\n      end\n    end\n\n    describe \"#proper_superset_of?\" do\n      it do\n        h1.proper_superset_of?(h2).should be_false\n        h2.proper_superset_of?(h1).should be_true\n        h1.proper_superset_of?(h1).should be_false\n        h1.proper_superset_of?(h3).should be_false\n        h1.proper_superset_of?(h4).should be_true\n      end\n    end\n\n    describe \"#superset_of?\" do\n      it do\n        h1.superset_of?(h2).should be_false\n        h2.superset_of?(h1).should be_true\n        h1.superset_of?(h1).should be_true\n        h1.superset_of?(h3).should be_false\n        h1.superset_of?(h4).should be_true\n      end\n    end\n  end\n\n  describe \"[]\" do\n    it \"gets\" do\n      a = {1 => 2}\n      a[1].should eq(2)\n      # a[2].should raise_exception\n      a.should eq({1 => 2})\n    end\n\n    it \"raises on missing key\" do\n      h = {1 => 2}\n\n      expect_raises KeyError, \"Missing hash key: 2\" do\n        h[2]\n      end\n    end\n\n    describe \"with block\" do\n      it \"calls and returns if of the same type\" do\n        h = Hash(Int32, String).new { \"b\" }\n        h[0].should eq(\"b\")\n      end\n\n      it \"errors if of the wrong type\" do\n        h = Hash(Int32, String).new { \"b\" }\n\n        expect_raises KeyError, \"Invalid key type: expected Int32, got String\" do\n          h[\"0\"]\n        end\n      end\n    end\n  end\n\n  describe \"[]=\" do\n    it \"overrides value\" do\n      a = {1 => 2}\n      a[1] = 3\n      a[1].should eq(3)\n    end\n  end\n\n  describe \"#put\" do\n    it \"puts in a small hash\" do\n      a = {} of Int32 => Int32\n      a.put(1, 2) { nil }.should be_nil\n      a.put(1, 3) { nil }.should eq(2)\n    end\n\n    it \"puts in a big hash\" do\n      a = {} of Int32 => Int32\n      100.times do |i|\n        a[i] = i\n      end\n      a.put(100, 2) { nil }.should be_nil\n      a.put(100, 3) { nil }.should eq(2)\n    end\n\n    it \"yields key\" do\n      a = {} of Int32 => Int32\n      a.put(1, 2, &.to_s).should eq(\"1\")\n    end\n  end\n\n  describe \"#put_if_absent\" do\n    it \"puts if key doesn't exist\" do\n      v = [] of String\n      h = {} of Int32 => Array(String)\n      h.put_if_absent(1, v).should be(v)\n      h.should eq({1 => v})\n      h[1].should be(v)\n    end\n\n    it \"returns existing value if key exists\" do\n      v = [] of String\n      h = {1 => v}\n      h.put_if_absent(1, [] of String).should be(v)\n      h.should eq({1 => v})\n      h[1].should be(v)\n    end\n\n    it \"accepts a block\" do\n      v = [] of String\n      h = {1 => v}\n      h.put_if_absent(1) { [] of String }.should be(v)\n      h.put_if_absent(2) { |key| [key.to_s] }.should eq([\"2\"])\n      h.should eq({1 => v, 2 => [\"2\"]})\n      h[1].should be(v)\n    end\n\n    it \"doesn't put duplicate keys (#14425)\" do\n      h = {1 => 2}\n      h.put_if_absent(3) { h[3] = 4 }.should eq(4)\n      h.should eq({1 => 2, 3 => 4})\n    end\n  end\n\n  describe \"update\" do\n    it \"updates the value of an existing key with the given block\" do\n      h = {\"a\" => 0, \"b\" => 1}\n\n      h.update(\"b\") { |v| v + 41 }\n      h[\"b\"].should eq(42)\n    end\n\n    it \"updates the value of an existing key with the given block (big hash)\" do\n      h = {} of Int32 => Int32\n      100.times do |i|\n        h[i] = i\n      end\n\n      h.update(2) { |v|\n        x = v * 20\n        x + 2\n      }\n      h[2].should eq(42)\n    end\n\n    it \"returns the old value when key exists\" do\n      h = {\"a\" => 0}\n\n      h.update(\"a\") { |v| v + 1 }.should eq(0)\n    end\n\n    it \"returns the old value when key exists (big hash)\" do\n      h = {} of Int32 => Int32\n      100.times do |i|\n        h[i] = i\n      end\n\n      h.update(0) { |v| v + 1 }.should eq(0)\n    end\n\n    it \"inserts a new entry using the value returned by the default block as input, if key does not exist\" do\n      h = Hash(String, Int32).new { |h, new_key| new_key.size }\n\n      h.update(\"new key\") { |v| v * 6 }\n      h[\"new key\"].should eq(7 * 6)\n    end\n\n    it \"inserts a new entry using the value returned by the default block as input, if key does not exist (big hash)\" do\n      h = Hash(Int32, Int32).new { |h, new_key| new_key }\n      100.times do |i|\n        h[i] = i\n      end\n\n      h.update(3000) { |v| v + 42 }\n      h[3000].should eq(3000 + 42)\n    end\n\n    it \"doesn't create a duplicate key, if key does not exist and default block adds the given key (#14416)\" do\n      h = Hash(String, Int32).new do |h, new_key|\n        h[new_key] = 1\n        new_key.size\n      end\n\n      h.update(\"new key\") { |v| v * 6 }\n      h.size.should eq(1)\n      h[\"new key\"].should eq(7 * 6)\n    end\n\n    it \"inserts a new entry using the default value as input, if key does not exist\" do\n      h = Hash(String, Int32).new(2)\n\n      h.update(\"new key\") { |v| v + 40 }\n      h[\"new key\"].should eq(2 + 40)\n    end\n\n    it \"inserts a new entry using the default value as input, if key does not exist (big hash)\" do\n      h = Hash(Int32, Int32).new(2)\n      100.times do |i|\n        h[i] = i\n      end\n\n      h.update(3000) { |v| v + 40 }\n      h[3000].should eq(2 + 40)\n    end\n\n    it \"returns the default value when key does not exist\" do\n      h = Hash(String, Int32).new(0)\n\n      h.update(\"a\") { |v| v + 1 }.should eq(0)\n    end\n\n    it \"returns the default value when key does not exist (big hash)\" do\n      h = Hash(Int32, Int32).new(0)\n      100.times do |i|\n        h[i] = i\n      end\n\n      h.update(3000) { |v| v + 1 }.should eq(0)\n    end\n\n    it \"raises if key does not exist and no default value specified\" do\n      h = {} of String => Int32\n\n      expect_raises KeyError, %(Missing hash key: \"a\") do\n        h.update(\"a\") { 42 }\n      end\n    end\n\n    it \"raises if key does not exist and no default value specified (big hash)\" do\n      h = {} of Int32 => Int32\n      100.times do |i|\n        h[i] = i\n      end\n\n      expect_raises KeyError, %(Missing hash key: 3000) do\n        h.update(3000) { 42 }\n      end\n    end\n\n    it \"can update with a nil value\" do\n      h = {\"a\" => 42} of String => Int32?\n\n      h.update(\"a\") { nil }\n      h[\"a\"].should be_nil\n    end\n\n    it \"can update a current nil value with a new value\" do\n      h = {\"a\" => nil} of String => Int32?\n\n      h.has_key?(\"a\").should be_true\n      h.update(\"a\") { 42 }.should be_nil\n      h[\"a\"].should eq(42)\n    end\n  end\n\n  describe \"dig?\" do\n    it \"gets the value at given path given splat\" do\n      ary = [1, 2, 3]\n      h = {\"a\" => {\"b\" => {\"c\" => [10, 20]}}, ary => {\"a\" => \"b\"}}\n\n      h.dig?(\"a\", \"b\", \"c\").should eq([10, 20])\n      h.dig?(ary, \"a\").should eq(\"b\")\n    end\n\n    it \"returns nil if not found\" do\n      ary = [1, 2, 3]\n      h = {\"a\" => {\"b\" => {\"c\" => 300}}, ary => {\"a\" => \"b\"}}\n\n      h.dig?(\"a\", \"b\", \"c\", \"d\", \"e\").should be_nil\n      h.dig?(\"z\").should be_nil\n      h.dig?(\"\").should be_nil\n    end\n  end\n\n  describe \"dig\" do\n    it \"gets the value at given path given splat\" do\n      ary = [1, 2, 3]\n      h = {\"a\" => {\"b\" => {\"c\" => [10, 20]}}, ary => {\"a\" => \"b\", \"c\" => nil}}\n\n      h.dig(\"a\", \"b\", \"c\").should eq([10, 20])\n      h.dig(ary, \"a\").should eq(\"b\")\n      h.dig(ary, \"c\").should be_nil\n    end\n\n    it \"raises KeyError if not found\" do\n      ary = [1, 2, 3]\n      h = {\"a\" => {\"b\" => {\"c\" => 300}}, ary => {\"a\" => \"b\"}}\n\n      expect_raises KeyError, %(Hash value not diggable for key: \"c\") do\n        h.dig(\"a\", \"b\", \"c\", \"d\", \"e\")\n      end\n      expect_raises KeyError, %(Missing hash key: \"z\") do\n        h.dig(\"z\")\n      end\n      expect_raises KeyError, %(Missing hash key: \"\") do\n        h.dig(\"\")\n      end\n    end\n  end\n\n  describe \"fetch\" do\n    it \"fetches with default value\" do\n      a = {1 => 2}\n      a.fetch(1, 3).should eq(2)\n      a.fetch(2, 3).should eq(3)\n      a.should eq({1 => 2})\n    end\n\n    it \"fetches with block\" do\n      a = {1 => 2}\n      a.fetch(1) { |k| k * 3 }.should eq(2)\n      a.fetch(2) { |k| k * 3 }.should eq(6)\n      a.should eq({1 => 2})\n    end\n  end\n\n  describe \"values_at\" do\n    it \"returns the given keys\" do\n      {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.values_at(\"b\", \"a\", \"c\").should eq({2, 1, 3})\n    end\n\n    it \"raises when passed an invalid key\" do\n      expect_raises KeyError do\n        {\"a\" => 1}.values_at(\"b\")\n      end\n    end\n\n    it \"works with mixed types\" do\n      {1 => \"a\", \"a\" => 1, 2.0 => \"a\", \"b\" => 1.0}.values_at(1, \"a\", 2.0, \"b\").should eq({\"a\", 1, \"a\", 1.0})\n    end\n  end\n\n  describe \"key_for\" do\n    it \"returns the first key with the given value\" do\n      hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n      hash.key_for(\"bar\").should eq(\"foo\")\n      hash.key_for(\"qux\").should eq(\"baz\")\n    end\n\n    it \"raises when no key pairs with the given value\" do\n      expect_raises KeyError do\n        {\"foo\" => \"bar\"}.key_for(\"qux\")\n      end\n    end\n\n    describe \"if block is given,\" do\n      it \"returns the first key with the given value\" do\n        hash = {\"foo\" => \"bar\", \"baz\" => \"bar\"}\n        hash.key_for(\"bar\", &.upcase).should eq(\"foo\")\n      end\n\n      it \"yields the argument if no hash key pairs with the value\" do\n        hash = {\"foo\" => \"bar\"}\n        hash.key_for(\"qux\", &.upcase).should eq(\"QUX\")\n      end\n    end\n  end\n\n  describe \"key_for?\" do\n    it \"returns the first key with the given value\" do\n      hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n      hash.key_for?(\"bar\").should eq(\"foo\")\n      hash.key_for?(\"qux\").should eq(\"baz\")\n    end\n\n    it \"returns nil if no key pairs with the given value\" do\n      hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n      hash.key_for?(\"foobar\").should be_nil\n      hash.key_for?(\"bazqux\").should be_nil\n    end\n  end\n\n  describe \"has_key?\" do\n    it \"doesn't have key\" do\n      a = {1 => 2}\n      a.has_key?(2).should be_false\n    end\n\n    it \"has key\" do\n      a = {1 => 2}\n      a.has_key?(1).should be_true\n    end\n  end\n\n  describe \"has_value?\" do\n    it \"returns true if contains the value\" do\n      a = {1 => 2, 3 => 4, 5 => 6}\n      a.has_value?(4).should be_true\n    end\n\n    it \"returns false if does not contain the value\" do\n      a = {1 => 2, 3 => 4, 5 => 6}\n      a.has_value?(3).should be_false\n    end\n  end\n\n  describe \"delete\" do\n    it \"deletes key in the beginning\" do\n      a = {1 => 2, 3 => 4, 5 => 6}\n      a.delete(1).should eq(2)\n      a.has_key?(1).should be_false\n      a.has_key?(3).should be_true\n      a.has_key?(5).should be_true\n      a.size.should eq(2)\n      a.should eq({3 => 4, 5 => 6})\n    end\n\n    it \"deletes key in the middle\" do\n      a = {1 => 2, 3 => 4, 5 => 6}\n      a.delete(3).should eq(4)\n      a.has_key?(1).should be_true\n      a.has_key?(3).should be_false\n      a.has_key?(5).should be_true\n      a.size.should eq(2)\n      a.should eq({1 => 2, 5 => 6})\n    end\n\n    it \"deletes key in the end\" do\n      a = {1 => 2, 3 => 4, 5 => 6}\n      a.delete(5).should eq(6)\n      a.has_key?(1).should be_true\n      a.has_key?(3).should be_true\n      a.has_key?(5).should be_false\n      a.size.should eq(2)\n      a.should eq({1 => 2, 3 => 4})\n    end\n\n    it \"deletes only remaining entry\" do\n      a = {1 => 2}\n      a.delete(1).should eq(2)\n      a.has_key?(1).should be_false\n      a.size.should eq(0)\n      a.should eq({} of Int32 => Int32)\n    end\n\n    it \"deletes not found\" do\n      a = {1 => 2}\n      a.delete(2).should be_nil\n    end\n\n    it \"deletes many in the beginning and then will need a resize\" do\n      h = {} of Int32 => Int32\n      8.times do |i|\n        h[i] = i\n      end\n      5.times do |i|\n        h.delete(i)\n      end\n      (9..12).each do |i|\n        h[i] = i\n      end\n      h.should eq({5 => 5, 6 => 6, 7 => 7, 9 => 9, 10 => 10, 11 => 11, 12 => 12})\n    end\n\n    describe \"with block\" do\n      it \"returns the value if a key is found\" do\n        a = {1 => 2}\n        a.delete(1) { 5 }.should eq(2)\n      end\n\n      it \"returns the value of the block if key is not found\" do\n        a = {1 => 2}\n        a.delete(3) { |key| key }.should eq(3)\n      end\n\n      it \"returns nil if key is found and value is nil\" do\n        {3 => nil}.delete(3) { 7 }.should be_nil\n      end\n    end\n  end\n\n  describe \"size\" do\n    it \"is the same as size\" do\n      a = {} of Int32 => Int32\n      a.size.should eq(a.size)\n\n      a = {1 => 2}\n      a.size.should eq(a.size)\n\n      a = {1 => 2, 3 => 4, 5 => 6, 7 => 8}\n      a.size.should eq(a.size)\n    end\n  end\n\n  it \"maps\" do\n    hash = {1 => 2, 3 => 4}\n    array = hash.map { |k, v| k + v }\n    array.should eq([3, 7])\n  end\n\n  describe \"to_s\" do\n    it { {1 => 2, 3 => 4}.to_s.should eq(\"{1 => 2, 3 => 4}\") }\n\n    it do\n      h = {} of RecursiveHash => RecursiveHash\n      h[h] = h\n      h.to_s.should eq(\"{ {...} => {...} }\")\n    end\n\n    context \"when the first key starts with '{'\" do\n      it \"inserts a space after '{' and before '}' when first key is a Hash, preventing macro interpolation ({{ ... }})\" do\n        hash = { {1 => 2} => 3 }\n        hash.to_s.should eq(\"{ {1 => 2} => 3 }\")\n      end\n\n      it \"inserts a space after '{' and before '}' when first key is a Tuple, preventing macro interpolation ({{ ... }})\" do\n        hash = { {1, 2, 3} => 4 }\n        hash.to_s.should eq(\"{ {1, 2, 3} => 4 }\")\n      end\n\n      it \"inserts a space after '{' and before '}' when first key is a NamedTuple, preventing macro interpolation ({{ ... }})\" do\n        hash = { {a: 1} => 2 }\n        hash.to_s.should eq(\"{ {a: 1} => 2 }\")\n      end\n    end\n  end\n\n  it \"does to_h\" do\n    h = {\"a\" => 1}\n    h.to_h.should be(h)\n  end\n\n  describe \"clone\" do\n    it \"clones with size = 1\" do\n      h1 = {1 => 2}\n      h2 = h1.clone\n      h1.should_not be(h2)\n      h1.should eq(h2)\n    end\n\n    it \"clones empty hash\" do\n      h1 = {} of Int32 => Int32\n      h2 = h1.clone\n      h2.should be_empty\n    end\n\n    it \"clones small hash\" do\n      h1 = {} of Int32 => Array(Int32)\n      4.times do |i|\n        h1[i] = [i]\n      end\n      h2 = h1.clone\n      h1.should_not be(h2)\n      h1.should eq(h2)\n\n      4.times do |i|\n        h1[i].should_not be(h2[i])\n      end\n\n      h1.delete(0)\n      h2[0].should eq([0])\n    end\n\n    it \"clones big hash\" do\n      h1 = {} of Int32 => Array(Int32)\n      1_000.times do |i|\n        h1[i] = [i]\n      end\n      h2 = h1.clone\n      h1.should_not be(h2)\n      h1.should eq(h2)\n\n      1_000.times do |i|\n        h1[i].should_not be(h2[i])\n      end\n\n      h1.delete(0)\n      h2[0].should eq([0])\n    end\n\n    it \"clones recursive hash\" do\n      h = {} of RecursiveHash => RecursiveHash\n      h[h] = h\n      clone = h.clone\n      clone.should be(clone.first[1])\n    end\n\n    it \"retains default value\" do\n      h1 = Hash(Int32, String).new(\"a\")\n      h2 = h1.clone\n      h2[0].should eq(\"a\")\n\n      h1[1] = \"b\"\n      h3 = h1.clone\n      h3[0].should eq(\"a\")\n    end\n\n    it \"retains default block\" do\n      h1 = Hash(Int32, String).new { \"b\" }\n      h2 = h1.clone\n      h2[0].should eq(\"b\")\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.clone.compare_by_identity?.should be_true\n    end\n  end\n\n  describe \"dup\" do\n    it \"dups empty hash\" do\n      h1 = {} of Int32 => Int32\n      h2 = h1.dup\n      h2.should be_empty\n    end\n\n    it \"dups small hash\" do\n      h1 = {} of Int32 => Array(Int32)\n      4.times do |i|\n        h1[i] = [i]\n      end\n      h2 = h1.dup\n      h1.should_not be(h2)\n      h1.should eq(h2)\n\n      4.times do |i|\n        h1[i].should be(h2[i])\n      end\n\n      h1.delete(0)\n      h2[0].should eq([0])\n    end\n\n    it \"dups big hash\" do\n      h1 = {} of Int32 => Array(Int32)\n      1_000.times do |i|\n        h1[i] = [i]\n      end\n      h2 = h1.dup\n      h1.should_not be(h2)\n      h1.should eq(h2)\n\n      1_000.times do |i|\n        h1[i].should be(h2[i])\n      end\n\n      h1.delete(0)\n      h2[0].should eq([0])\n    end\n\n    it \"retains default value\" do\n      h1 = Hash(Int32, String).new(\"a\")\n      h2 = h1.dup\n      h2[0].should eq(\"a\")\n\n      h1[1] = \"b\"\n      h3 = h1.dup\n      h3[0].should eq(\"a\")\n    end\n\n    it \"retains default block\" do\n      h1 = Hash(Int32, String).new { \"b\" }\n      h2 = h1.dup\n      h2[0].should eq(\"b\")\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.dup.compare_by_identity?.should be_true\n    end\n  end\n\n  it \"initializes with block\" do\n    h1 = Hash(String, Array(Int32)).new { |h, k| h[k] = [] of Int32 }\n    h1[\"foo\"].should eq([] of Int32)\n    h1[\"bar\"].push 2\n    h1[\"bar\"].should eq([2])\n  end\n\n  it \"initializes with default value\" do\n    h = Hash(Int32, Int32).new(10)\n    h[0].should eq(10)\n    h.has_key?(0).should be_false\n    h[1] += 2\n    h[1].should eq(12)\n    h.has_key?(1).should be_true\n  end\n\n  describe \"merge\" do\n    it \"merges\" do\n      h1 = {1 => 2, 3 => 4}\n      h2 = {1 => 5, 2 => 3}\n      h3 = {\"1\" => \"5\", \"2\" => \"3\"}\n\n      h4 = h1.merge(h2)\n      h4.should_not be(h1)\n      h4.should eq({1 => 5, 3 => 4, 2 => 3})\n\n      h5 = h1.merge(h3)\n      h5.should_not be(h1)\n      h5.should eq({1 => 2, 3 => 4, \"1\" => \"5\", \"2\" => \"3\"})\n    end\n\n    it \"merges with block\" do\n      h1 = {1 => 5, 2 => 3}\n      h2 = {1 => 5, 3 => 4, 2 => 3}\n\n      h3 = h2.merge(h1) { |k, v1, v2| k + v1 + v2 }\n      h3.should_not be(h2)\n      h3.should eq({1 => 11, 3 => 4, 2 => 8})\n    end\n\n    it \"merges recursive type (#1693)\" do\n      hash = {\"foo\" => \"bar\"} of String => RecursiveType\n      result = hash.merge({\"foobar\" => \"foo\"})\n      result.should eq({\"foo\" => \"bar\", \"foobar\" => \"foo\"})\n    end\n\n    it \"merges other type with block\" do\n      h1 = {1 => \"foo\"}\n      h2 = {1 => \"bar\", \"fizz\" => \"buzz\"}\n\n      h3 = h1.merge(h2) { |k, v1, v2| v1 + v2 }\n      h3.should eq({1 => \"foobar\", \"fizz\" => \"buzz\"})\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n      h.merge({1 => \"a\"})[0]?.should be_nil\n      h.merge({1 => \"a\"}) { |k, v1, v2| v1 }[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n      h.merge({1 => \"a\"})[0]?.should be_nil\n      h.merge({1 => \"a\"}) { |k, v1, v2| v1 }[0]?.should be_nil\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.merge({\"a\" => 1}).compare_by_identity?.should be_true\n      h.merge({\"a\" => 1}) { |k, v1, v2| v1 + v2 }.compare_by_identity?.should be_true\n    end\n\n    it \"ignores compare_by_identity of an argument\" do\n      h = ({} of String => Int32).compare_by_identity\n      {\"a\" => 1}.merge(h).compare_by_identity?.should be_false\n      {\"a\" => 1}.merge(h) { |k, v1, v2| v1 + v2 }.compare_by_identity?.should be_false\n    end\n  end\n\n  it \"merges!\" do\n    h1 = {1 => 2, 3 => 4}\n    h2 = {1 => 5, 2 => 3}\n\n    h3 = h1.merge!(h2)\n    h3.should be(h1)\n    h3.should eq({1 => 5, 3 => 4, 2 => 3})\n  end\n\n  it \"merges! with block\" do\n    h1 = {1 => 5, 2 => 3}\n    h2 = {1 => 5, 3 => 4, 2 => 3}\n\n    h3 = h2.merge!(h1) { |k, v1, v2| k + v1 + v2 }\n    h3.should be(h2)\n    h3.should eq({1 => 11, 3 => 4, 2 => 8})\n  end\n\n  it \"merges! with block and nilable keys\" do\n    h1 = {1 => nil, 2 => 4, 3 => \"x\"}\n    h2 = {1 => 2, 2 => nil, 3 => \"y\"}\n\n    h3 = h1.merge!(h2) { |k, v1, v2| (v1 || v2).to_s }\n    h3.should be(h1)\n    h3.should eq({1 => \"2\", 2 => \"4\", 3 => \"x\"})\n  end\n\n  it \"selects\" do\n    h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n    h2 = h1.select { |k, v| k == \"b\" }\n    h2.should eq({\"b\" => 2})\n    h2.should_not be(h1)\n  end\n\n  it \"select with non-equality key\" do\n    h = {Float64::NAN => true, 0.0 => true}\n    h.select { |k| !k.nan? }.should eq({0.0 => true})\n  end\n\n  it \"selects!\" do\n    h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n    h2 = h1.select! { |k, v| k == \"b\" }\n    h2.should be_a(Hash(String, Int32))\n    h2.should eq({\"b\" => 2})\n    h2.should be(h1)\n  end\n\n  it \"select! with non-equality key\" do\n    h = {Float64::NAN => true, 0.0 => true}\n    h.select! { |k| !k.nan? }\n    h.should eq({0.0 => true})\n  end\n\n  it \"rejects\" do\n    h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n    h2 = h1.reject { |k, v| k == \"b\" }\n    h2.should eq({\"a\" => 1, \"c\" => 3})\n    h2.should_not be(h1)\n  end\n\n  it \"reject with non-equality key\" do\n    h = {Float64::NAN => true, 0.0 => true}\n    h.reject(&.nan?).should eq({0.0 => true})\n  end\n\n  it \"rejects!\" do\n    h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n    h2 = h1.reject! { |k, v| k == \"b\" }\n    h2.should be_a(Hash(String, Int32))\n    h2.should eq({\"a\" => 1, \"c\" => 3})\n    h2.should be(h1)\n  end\n\n  it \"reject with non-equality key\" do\n    h = {Float64::NAN => true, 0.0 => true}\n    h.reject!(&.nan?)\n    h.should eq({0.0 => true})\n  end\n\n  describe \"compact\" do\n    it \"compacts\" do\n      h1 = {\"a\" => 1, \"b\" => 2, \"c\" => nil}\n\n      h2 = h1.compact\n      h2.should be_a(Hash(String, Int32))\n      h2.should eq({\"a\" => 1, \"b\" => 2})\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n      h.compact[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n      h.compact[0]?.should be_nil\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.compact.compare_by_identity?.should be_true\n    end\n  end\n\n  it \"compacts!\" do\n    h1 = {\"a\" => 1, \"b\" => 2, \"c\" => nil}\n\n    h2 = h1.compact!\n    h2.should be_a(Hash(String, Int32 | Nil))\n    h2.should eq({\"a\" => 1, \"b\" => 2})\n    h2.should be(h1)\n  end\n\n  describe \"transform_keys\" do\n    it \"transforms keys\" do\n      h1 = {1 => \"a\", 2 => \"b\", 3 => \"c\"}\n\n      h2 = h1.transform_keys { |x| x + 1 }\n      h2.should eq({2 => \"a\", 3 => \"b\", 4 => \"c\"})\n    end\n\n    it \"transforms keys with type casting\" do\n      h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n      h2 = h1.transform_keys(&.to_s.upcase)\n      h2.should be_a(Hash(String, Int32))\n      h2.should eq({\"A\" => 1, \"B\" => 2, \"C\" => 3})\n    end\n\n    it \"returns empty hash when transforming keys of an empty hash\" do\n      h1 = {} of Int32 => String\n\n      h2 = h1.transform_keys { |x| x + 1 }\n      h2.should be_a(Hash(Int32, String))\n      h2.should be_empty\n    end\n\n    it \"transforms keys with values included\" do\n      h1 = {1 => \"a\", 2 => \"b\", 3 => \"c\"}\n\n      h2 = h1.transform_keys { |k, v| \"#{k}#{v}\" }\n      h2.should eq({\"1a\" => \"a\", \"2b\" => \"b\", \"3c\" => \"c\"})\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n      h.transform_keys(&.succ)[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n      h.transform_keys(&.succ)[0]?.should be_nil\n    end\n\n    it \"does not retain compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.transform_keys(&.to_s.upcase).compare_by_identity?.should be_false\n    end\n  end\n\n  describe \"transform_keys!\" do\n    it \"transforms keys in place\" do\n      h = {1 => \"a\", 2 => \"b\", 3 => \"c\"}\n\n      h.transform_keys! { |x| x + 1 }.should be(h)\n      h.should eq({2 => \"a\", 3 => \"b\", 4 => \"c\"})\n    end\n\n    it \"does nothing when empty hash\" do\n      h = {} of Int32 => String\n\n      h.transform_keys! { |x| x + 1 }\n      h.should be_empty\n    end\n\n    it \"transforms keys with values included\" do\n      h = {\"1\" => \"a\", \"2\" => \"b\", \"3\" => \"c\"}\n\n      h.transform_keys! { |k, v| \"#{k}#{v}\" }\n      h.should eq({\"1a\" => \"a\", \"2b\" => \"b\", \"3c\" => \"c\"})\n    end\n\n    it \"allows transformed key type to be a subtype of the original type\" do\n      h = {\"1\" => \"foo\", 2 => \"bar\"} # Hash(Int32 | String, String)\n\n      h.transform_keys!(&.to_i)\n      h.should eq({1 => \"foo\", 2 => \"bar\"})\n    end\n  end\n\n  describe \"transform_values\" do\n    it \"transforms values\" do\n      h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n      h2 = h1.transform_values { |x| x + 1 }\n      h2.should eq({\"a\" => 2, \"b\" => 3, \"c\" => 4})\n    end\n\n    it \"transforms values with type casting values\" do\n      h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n      h2 = h1.transform_values(&.to_s)\n      h2.should be_a(Hash(String, String))\n      h2.should eq({\"a\" => \"1\", \"b\" => \"2\", \"c\" => \"3\"})\n    end\n\n    it \"returns empty hash when transforming values of an empty hash\" do\n      h1 = {} of String => Int32\n\n      h2 = h1.transform_values { |x| x + 1 }\n      h2.should be_a(Hash(String, Int32))\n      h2.should be_empty\n    end\n\n    it \"transforms values with keys included\" do\n      h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n      h2 = h1.transform_values { |v, k| \"#{k}#{v}\" }\n      h2.should eq({\"a\" => \"a1\", \"b\" => \"b2\", \"c\" => \"c3\"})\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n      h.transform_values(&.succ)[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n      h.transform_values(&.succ)[0]?.should be_nil\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.transform_values(&.to_s).compare_by_identity?.should be_true\n    end\n  end\n\n  it \"transform values in place\" do\n    h = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n\n    h.transform_values!(&.+(1))\n    h.should eq({\"a\" => 2, \"b\" => 3, \"c\" => 4})\n  end\n\n  it \"transform values in place with keys included\" do\n    h = {\"a\" => \"1\", \"b\" => \"2\", \"c\" => \"3\"}\n\n    h.transform_values! { |v, k| \"#{k}#{v}\" }\n    h.should eq({\"a\" => \"a1\", \"b\" => \"b2\", \"c\" => \"c3\"})\n  end\n\n  it \"zips\" do\n    ary1 = [1, 2, 3]\n    ary2 = ['a', 'b', 'c']\n    hash = Hash.zip(ary1, ary2)\n    hash.should eq({1 => 'a', 2 => 'b', 3 => 'c'})\n  end\n\n  it \"gets first\" do\n    h = {1 => 2, 3 => 4}\n    h.first.should eq({1, 2})\n  end\n\n  describe \"first_key\" do\n    it \"gets first key\" do\n      h = {1 => 2, 3 => 4}\n      h.first_key.should eq(1)\n    end\n\n    it \"raises on first key (nilable key)\" do\n      h = {} of Int32? => Int32\n      expect_raises(Exception, \"Can't get first key of empty Hash\") do\n        h.first_key\n      end\n    end\n\n    it \"doesn't raise on first key (nilable key)\" do\n      h = {nil => 1} of Int32? => Int32\n      h.first_key.should be_nil\n    end\n  end\n\n  describe \"first_value\" do\n    it \"gets first value\" do\n      h = {1 => 2, 3 => 4}\n      h.first_value.should eq(2)\n    end\n\n    it \"raises on first value (nilable value)\" do\n      h = {} of Int32 => Int32?\n      expect_raises(Exception, \"Can't get first value of empty Hash\") do\n        h.first_value\n      end\n    end\n\n    it \"doesn't raise on first value (nilable value)\" do\n      h = {1 => nil} of Int32 => Int32?\n      h.first_value.should be_nil\n    end\n  end\n\n  describe \"last_key\" do\n    it \"gets last key\" do\n      h = {1 => 2, 3 => 4}\n      h.last_key.should eq(3)\n    end\n\n    it \"raises on last key (nilable key)\" do\n      h = {} of Int32? => Int32\n      expect_raises(Exception, \"Can't get last key of empty Hash\") do\n        h.last_key\n      end\n    end\n\n    it \"doesn't raise on last key (nilable key)\" do\n      h = {nil => 1} of Int32? => Int32\n      h.last_key.should be_nil\n    end\n  end\n\n  describe \"last_value\" do\n    it \"gets last value\" do\n      h = {1 => 2, 3 => 4}\n      h.last_value.should eq(4)\n    end\n\n    it \"raises on last value (nilable value)\" do\n      h = {} of Int32 => Int32?\n      expect_raises(Exception, \"Can't get last value of empty Hash\") do\n        h.last_value\n      end\n    end\n\n    it \"doesn't raise on last value (nilable value)\" do\n      h = {1 => nil} of Int32 => Int32?\n      h.last_value.should be_nil\n    end\n  end\n\n  it \"shifts\" do\n    h = {1 => 2, 3 => 4}\n\n    h.shift.should eq({1, 2})\n    h.should eq({3 => 4})\n    h.first_key.should eq(3)\n    h.first_value.should eq(4)\n    h[1]?.should be_nil\n    h[3].should eq(4)\n\n    h.each.to_a.should eq([{3, 4}])\n    h.each_key.to_a.should eq([3])\n    h.each_value.to_a.should eq([4])\n\n    h.shift.should eq({3, 4})\n    h.should be_empty\n\n    expect_raises(IndexError) do\n      h.shift\n    end\n\n    20.times do |i|\n      h[i] = i\n    end\n    h.size.should eq(20)\n\n    20.times do |i|\n      h.shift.should eq({i, i})\n    end\n    h.should be_empty\n  end\n\n  it \"shifts: delete elements in the middle position and then in the first position\" do\n    h = {1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd'}\n    h.delete(2)\n    h.delete(3)\n    h.delete(1)\n    h.size.should eq(1)\n    h.should eq({4 => 'd'})\n    h.first.should eq({4, 'd'})\n  end\n\n  it \"shifts?\" do\n    h = {1 => 2}\n    h.shift?.should eq({1, 2})\n    h.should be_empty\n    h.shift?.should be_nil\n  end\n\n  it \"inserts many\" do\n    times = 1000\n    h = {} of Int32 => Int32\n    times.times do |i|\n      h[i] = i\n      h.size.should eq(i + 1)\n    end\n    times.times do |i|\n      h[i].should eq(i)\n    end\n    h.first_key.should eq(0)\n    h.first_value.should eq(0)\n    times.times do |i|\n      h.delete(i).should eq(i)\n      h.has_key?(i).should be_false\n      h.size.should eq(times - i - 1)\n    end\n  end\n\n  it \"inserts in one bucket and deletes from the same one\" do\n    h = {11 => 1}\n    h.delete(0).should be_nil\n    h.has_key?(11).should be_true\n    h.size.should eq(1)\n  end\n\n  it \"does to_a\" do\n    h = {1 => \"hello\", 2 => \"bye\"}\n    h.to_a.should eq([{1, \"hello\"}, {2, \"bye\"}])\n  end\n\n  it \"does to_a after shift\" do\n    h = {1 => 'a', 2 => 'b', 3 => 'c'}\n    h.shift\n    h.to_a.should eq([{2, 'b'}, {3, 'c'}])\n  end\n\n  it \"does to_a after delete\" do\n    h = {1 => 'a', 2 => 'b', 3 => 'c'}\n    h.delete(2)\n    h.to_a.should eq([{1, 'a'}, {3, 'c'}])\n  end\n\n  it \"clears\" do\n    h = {1 => 2, 3 => 4}\n    h.clear\n    h.should be_empty\n    h.to_a.size.should eq(0)\n  end\n\n  it \"clears after shift\" do\n    h = {1 => 2, 3 => 4}\n    h.shift\n    h.clear\n    h.should be_empty\n    h.to_a.size.should eq(0)\n    h[5] = 6\n    h.should_not be_empty\n    h[5].should eq(6)\n    h.should eq({5 => 6})\n  end\n\n  it \"computes hash\" do\n    h1 = { {1 => 2} => {3 => 4} }\n    h2 = { {1 => 2} => {3 => 4} }\n    h1.hash.should eq(h2.hash)\n\n    h3 = {1 => 2, 3 => 4}\n    h4 = {3 => 4, 1 => 2}\n\n    h3.hash.should eq(h4.hash)\n  end\n\n  it \"fetches from empty hash with default value\" do\n    x = {} of Int32 => HashBreaker\n    breaker = x.fetch(10) { HashBreaker.new(1) }\n    breaker.x.should eq(1)\n  end\n\n  it \"does to to_s with instance that was never instantiated\" do\n    x = {} of Int32 => NeverInstantiated\n    x.to_s.should eq(\"{}\")\n  end\n\n  describe \"invert\" do\n    it \"inverts\" do\n      h1 = {\"one\" => 1, \"two\" => 2, \"three\" => 3}\n      h2 = {\"a\" => 1, \"b\" => 2, \"c\" => 1}\n\n      h1.invert.should eq({1 => \"one\", 2 => \"two\", 3 => \"three\"})\n\n      h3 = h2.invert\n      h3.size.should eq(2)\n      %w(a c).should contain h3[1]\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n      h.invert[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n      h.invert[0]?.should be_nil\n    end\n\n    it \"does not retain compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.invert.compare_by_identity?.should be_false\n    end\n  end\n\n  it \"does each\" do\n    hash = {\"foo\" => 1, \"bar\" => 2}\n    ks = [] of String\n    vs = [] of Int32\n    hash.each do |k, v|\n      ks << k\n      vs << v\n    end.should be_nil\n    ks.should eq([\"foo\", \"bar\"])\n    vs.should eq([1, 2])\n  end\n\n  it \"does each_key\" do\n    hash = {\"foo\" => 1, \"bar\" => 2}\n    ks = [] of String\n    hash.each_key do |k|\n      ks << k\n    end.should be_nil\n    ks.should eq([\"foo\", \"bar\"])\n  end\n\n  it \"does each_value\" do\n    hash = {\"foo\" => 1, \"bar\" => 2}\n    vs = [] of Int32\n    hash.each_value do |v|\n      vs << v\n    end.should be_nil\n    vs.should eq([1, 2])\n  end\n\n  it_iterates \"#each\", [{\"a\", 1}, {\"b\", 2}], {\"a\" => 1, \"b\" => 2}.each\n  it_iterates \"#each_key\", [\"a\", \"b\"], {\"a\" => 1, \"b\" => 2}.each_key\n  it_iterates \"#each_value\", [1, 2], {\"a\" => 1, \"b\" => 2}.each_value\n\n  it_iterates \"#each_with_index\", [{ {\"a\", 1}, 0 }, { {\"b\", 2}, 1 }], {\"a\" => 1, \"b\" => 2}.each_with_index, tuple: true\n  it_iterates \"#each_with_index(offset)\", [{ {\"a\", 1}, 2 }, { {\"b\", 2}, 3 }], {\"a\" => 1, \"b\" => 2}.each_with_index(2), tuple: true\n\n  describe \"#each_with_object\" do\n    it_iterates \"passes memo, key and value into block\", [{ {\"a\", 1}, \"memo\" }, { {\"b\", 2}, \"memo\" }], {\"a\" => 1, \"b\" => 2}.each_with_object(\"memo\"), tuple: true\n\n    it \"reduces the hash to the accumulated value of memo\" do\n      hash = {\"a\" => 'b', \"c\" => 'd', \"e\" => 'f'}\n      result = {} of Char => String\n      hash.each_with_object(result) do |(k, v), memo|\n        memo[v] = k\n      end.should be(result)\n      result.should eq({'b' => \"a\", 'd' => \"c\", 'f' => \"e\"})\n    end\n  end\n\n  describe \"all?\" do\n    it \"passes key and value into block\" do\n      hash = {\"a\" => 'b'}\n      hash.all? do |k, v|\n        k.should eq(\"a\")\n        v.should eq('b')\n      end\n    end\n\n    it \"returns true if the block evaluates truthy for every kv pair\" do\n      hash = {\"a\" => 'b', \"c\" => 'd'}\n      result = hash.all? { |k, v| v < 'e' ? \"truthy\" : nil }\n      result.should be_true\n      hash[\"d\"] = 'e'\n      result = hash.all? { |k, v| v < 'e' ? \"truthy\" : nil }\n      result.should be_false\n    end\n\n    it \"evaluates the block for only for as many kv pairs as necessary\" do\n      hash = {\"a\" => 'b', \"c\" => 'd'}\n      hash.all? do |k, v|\n        raise Exception.new(\"continued iterating\") if v == 'd'\n        v == 'a' # this is false for the first kv pair\n      end\n    end\n  end\n\n  describe \"any?\" do\n    it \"passes key and value into block\" do\n      hash = {\"a\" => 'b'}\n      hash.any? do |k, v|\n        k.should eq(\"a\")\n        v.should eq('b')\n      end\n    end\n\n    it \"returns true if the block evaluates truthy for at least one kv pair\" do\n      hash = {\"a\" => 'b', \"c\" => 'd'}\n      result = hash.any? { |k, v| v > 'b' ? \"truthy\" : nil }\n      result.should be_true\n      hash[\"d\"] = 'e'\n      result = hash.any? { |k, v| v > 'e' ? \"truthy\" : nil }\n      result.should be_false\n    end\n\n    it \"evaluates the block for only for as many kv pairs as necessary\" do\n      hash = {\"a\" => 'b', \"c\" => 'd'}\n      hash.any? do |k, v|\n        raise Exception.new(\"continued iterating\") if v == 'd'\n        v == 'b' # this is true for the first kv pair\n      end\n    end\n\n    it \"returns true if the hash contains at least one kv pair and no block is given\" do\n      hash = {\"a\" => 'b'}\n      result = hash.any?\n      result.should be_true\n\n      hash = {} of String => Char\n      result = hash.any?\n      result.should be_false\n    end\n  end\n\n  describe \"reduce\" do\n    it \"passes memo, key and value into block\" do\n      hash = {\"a\" => 'b'}\n      hash.reduce(\"\") do |memo, (k, v)|\n        memo.should eq(\"\")\n        k.should eq(\"a\")\n        v.should eq('b')\n      end\n    end\n\n    it \"reduces the hash to the accumulated value of memo\" do\n      hash = {\"a\" => 'b', \"c\" => 'd', \"e\" => 'f'}\n      result = hash.reduce(\"\") do |memo, (k, v)|\n        memo + v\n      end\n      result.should eq(\"bdf\")\n    end\n  end\n\n  describe \"reject\" do\n    it { {\"a\" => 2, \"b\" => 3}.reject(\"b\", \"d\").should eq({\"a\" => 2}) }\n    it { {\"a\" => 2, \"b\" => 3}.reject(Set{\"b\", \"d\"}).should eq({\"a\" => 2}) }\n    it { {\"a\" => 2, \"b\" => 3}.reject(\"b\", \"a\").should eq({} of String => Int32) }\n    it { {\"a\" => 2, \"b\" => 3}.reject([\"b\", \"a\"]).should eq({} of String => Int32) }\n    it \"does not change current hash\" do\n      h = {\"a\" => 3, \"b\" => 6, \"c\" => 9}\n      h.reject(\"b\", \"c\")\n      h.should eq({\"a\" => 3, \"b\" => 6, \"c\" => 9})\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n\n      h.reject(42)[0]?.should be_nil\n      h.reject([42])[0]?.should be_nil\n      h.reject { false }[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n\n      h.reject(42)[0]?.should be_nil\n      h.reject([42])[0]?.should be_nil\n      h.reject { false }[0]?.should be_nil\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.reject(\"a\").compare_by_identity?.should be_true\n      h.reject([\"a\"]).compare_by_identity?.should be_true\n      h.reject { false }.compare_by_identity?.should be_true\n    end\n  end\n\n  describe \"reject!\" do\n    it { {\"a\" => 2, \"b\" => 3}.reject!(\"b\", \"d\").should eq({\"a\" => 2}) }\n    it { {\"a\" => 2, \"b\" => 3}.reject!(Set{\"b\", \"d\"}).should eq({\"a\" => 2}) }\n    it { {\"a\" => 2, \"b\" => 3}.reject!(\"b\", \"a\").should eq({} of String => Int32) }\n    it { {\"a\" => 2, \"b\" => 3}.reject!([\"b\", \"a\"]).should eq({} of String => Int32) }\n    it \"changes current hash\" do\n      h = {\"a\" => 3, \"b\" => 6, \"c\" => 9}\n      h.reject!(\"b\", \"c\")\n      h.should eq({\"a\" => 3})\n    end\n  end\n\n  describe \"select\" do\n    it { {\"a\" => 2, \"b\" => 3}.select(\"b\", \"d\").should eq({\"b\" => 3}) }\n    it { {\"a\" => 2, \"b\" => 3}.select.should eq({} of String => Int32) }\n    it { {\"a\" => 2, \"b\" => 3}.select(\"b\", \"a\").should eq({\"a\" => 2, \"b\" => 3}) }\n    it { {\"a\" => 2, \"b\" => 3}.select([\"b\", \"a\"]).should eq({\"a\" => 2, \"b\" => 3}) }\n    it { {\"a\" => 2, \"b\" => 3}.select(Set{\"b\", \"a\"}).should eq({\"a\" => 2, \"b\" => 3}) }\n    it \"does not change current hash\" do\n      h = {\"a\" => 3, \"b\" => 6, \"c\" => 9}\n      h.select(\"b\", \"c\")\n      h.should eq({\"a\" => 3, \"b\" => 6, \"c\" => 9})\n    end\n\n    it \"does not retain default value\" do\n      h = Hash(Int32, String).new(\"a\")\n\n      h.select(42)[0]?.should be_nil\n      h.select([42])[0]?.should be_nil\n      h.select { true }[0]?.should be_nil\n    end\n\n    it \"does not retain default block\" do\n      h = Hash(Int32, String).new { \"b\" }\n\n      h.select(42)[0]?.should be_nil\n      h.select([42])[0]?.should be_nil\n      h.select { true }[0]?.should be_nil\n    end\n\n    it \"retains compare_by_identity\" do\n      h = ({} of String => Int32).compare_by_identity\n      h.select(\"a\").compare_by_identity?.should be_true\n      h.select([\"a\"]).compare_by_identity?.should be_true\n      h.select { true }.compare_by_identity?.should be_true\n    end\n  end\n\n  describe \"select!\" do\n    it { {\"a\" => 2, \"b\" => 3}.select!(\"b\", \"d\").should eq({\"b\" => 3}) }\n    it { {\"a\" => 2, \"b\" => 3}.select!.should eq({} of String => Int32) }\n    it { {\"a\" => 2, \"b\" => 3}.select!(\"b\", \"a\").should eq({\"a\" => 2, \"b\" => 3}) }\n    it { {\"a\" => 2, \"b\" => 3}.select!([\"b\", \"a\"]).should eq({\"a\" => 2, \"b\" => 3}) }\n    it { {\"a\" => 2, \"b\" => 3}.select!(Set{\"b\", \"a\"}).should eq({\"a\" => 2, \"b\" => 3}) }\n\n    it \"does change current hash\" do\n      h = {\"a\" => 3, \"b\" => 6, \"c\" => 9}\n      h.select!(\"b\", \"c\")\n      h.should eq({\"b\" => 6, \"c\" => 9})\n    end\n\n    it \"does not skip elements with an exhaustable enumerable argument (#12736)\" do\n      h = {1 => 'a', 2 => 'b', 3 => 'c'}.select!({1, 2, 3}.each)\n      h.should eq({1 => 'a', 2 => 'b', 3 => 'c'})\n    end\n  end\n\n  it \"doesn't generate a negative index for the bucket index (#2321)\" do\n    items = (0..100000).map { rand(100000).to_i16! }\n    items.uniq.size\n  end\n\n  it \"creates with initial capacity\" do\n    hash = Hash(Int32, Int32).new(initial_capacity: 1234)\n    hash.@indices_size_pow2.should eq(12)\n  end\n\n  it \"creates with initial capacity and default value\" do\n    hash = Hash(Int32, Int32).new(default_value: 3, initial_capacity: 1234)\n    hash[1].should eq(3)\n    hash.@indices_size_pow2.should eq(12)\n  end\n\n  it \"creates with initial capacity and block\" do\n    hash = Hash(Int32, Int32).new(initial_capacity: 1234) { |h, k| h[k] = 3 }\n    hash[1].should eq(3)\n    hash.@indices_size_pow2.should eq(12)\n  end\n\n  describe \"#rehash\" do\n    it \"rehashes\" do\n      a = [1]\n      h = {a => 0}\n      (10..100).each do |i|\n        h[[i]] = i\n      end\n      a << 2\n      h[a]?.should be_nil\n      h.rehash\n      h[a].should eq(0)\n    end\n\n    it \"resets @first (#14602)\" do\n      h = {\"a\" => 1, \"b\" => 2}\n      h.delete(\"a\")\n      h.rehash\n      # We cannot test direct equivalence here because `Hash#==(Hash)` does not depend on `@first`\n      h.to_s.should eq %({\"b\" => 2})\n    end\n  end\n\n  describe \"some edge cases while changing the implementation to open addressing\" do\n    it \"edge case 1\" do\n      h = {1 => 10}\n      h[1]?.should eq(10)\n      h.size.should eq(1)\n\n      h.delete(1)\n      h[1]?.should be_nil\n      h.size.should eq(0)\n\n      h[2] = 10\n      h[2]?.should eq(10)\n      h.size.should eq(1)\n\n      h[2] = 10\n      h[2]?.should eq(10)\n      h.size.should eq(1)\n    end\n\n    it \"edge case 2\" do\n      hash = Hash(Int32, Int32).new(initial_capacity: 0)\n      hash.@indices_size_pow2.should eq(0)\n      hash[1] = 2\n      hash[1].should eq(2)\n    end\n\n    it \"edge case 3\" do\n      h = {} of Int32 => Int32\n      (1 << 17).times do |i|\n        h[i] = i\n        h[i].should eq(i)\n      end\n    end\n  end\n\n  describe \"compare_by_identity\" do\n    it \"small hash\" do\n      string = \"foo\"\n      h = {string => 1}\n      h.compare_by_identity?.should be_false\n      h.compare_by_identity\n      h.compare_by_identity?.should be_true\n      h[string]?.should eq(1)\n      h[\"fo\" + \"o\"]?.should be_nil\n    end\n\n    it \"big hash\" do\n      h = {} of String => Int32\n      nums = (100..116).to_a\n      strings = nums.map(&.to_s)\n      strings.zip(nums) do |string, num|\n        h[string] = num\n      end\n      h.compare_by_identity\n      nums.each do |num|\n        h[num.to_s]?.should be_nil\n      end\n      strings.zip(nums) do |string, num|\n        h[string]?.should eq(num)\n      end\n    end\n  end\n\n  it \"can be wrapped\" do\n    HashWrapper(Int32, Int32).new.to_a.should be_empty\n  end\nend\n"
  },
  {
    "path": "spec/std/html_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"html\"\n\ndescribe \"HTML\" do\n  describe \".escape\" do\n    it \"does not change a safe string\" do\n      HTML.escape(\"safe_string\").should eq(\"safe_string\")\n    end\n\n    it \"escapes dangerous characters from a string\" do\n      HTML.escape(\"< & > ' \\\"\").should eq(\"&lt; &amp; &gt; &#39; &quot;\")\n    end\n  end\n\n  pending_wasm32 describe: \".unescape\" do\n    it \"identity\" do\n      HTML.unescape(\"safe_string\").should be(\"safe_string\")\n    end\n\n    it \"empty entity\" do\n      HTML.unescape(\"foo&;bar\").should eq \"foo&;bar\"\n    end\n\n    context \"numeric entities\" do\n      it \"decimal\" do\n        HTML.unescape(\"3 &#43; 2 &#00061 5 &#9;\").should eq(\"3 + 2 = 5 \\t\")\n      end\n\n      it \"hex\" do\n        HTML.unescape(\"&#x000033; &#x2B; 2 &#x0003D &#x000035; &#x9;\").should eq(\"3 + 2 = 5 \\t\")\n      end\n\n      it \"early termination\" do\n        HTML.unescape(\"&# &#x &#128;43 &#169f &#xa9\").should eq \"&# &#x €43 ©f ©\"\n      end\n\n      it \"ISO-8859-1 replacement\" do\n        HTML.unescape(\"&#x87;\").should eq \"‡\"\n      end\n\n      it \"does not unescape Char::MAX_CODEPOINT\" do\n        # U+10FFFF and U+10FFFE are noncharacter and are not replaced\n        HTML.unescape(\"limit &#x10FFFF;\").should eq(\"limit &#x10FFFF;\")\n        HTML.unescape(\"limit &#x10FFFE;\").should eq(\"limit &#x10FFFE;\")\n        HTML.unescape(\"limit &#x10FFFD;\").should eq(\"limit \\u{10FFFD}\")\n      end\n\n      it \"does not unescape characters above Char::MAX_CODEPOINT\" do\n        HTML.unescape(\"limit &#x110000;\").should eq(\"limit \\uFFFD\")\n        HTML.unescape(\"limit &#1114112;\").should eq(\"limit \\uFFFD\")\n        HTML.unescape(\"limit &#x110000000000000;\").should eq(\"limit \\uFFFD\")\n      end\n\n      it \"ignores leading zeros\" do\n        HTML.unescape(\"&#0000000065;\").should eq(\"A\")\n        HTML.unescape(\"&#x0000000065;\").should eq(\"e\")\n      end\n\n      it \"space characters\" do\n        HTML.unescape(\"&#x0020;&#32;&#x0009;&#x000A;&#x000C;&#x0080;&#x009F;\").should eq(\"  \\t\\n\\f\\u20AC\\u0178\")\n      end\n\n      it \"does not escape non-space unicode control characters\" do\n        HTML.unescape(\"&#x0001;-&#x001F; &#x000D; &#x007F;&#x000;\").should eq(\"&#x0001;-&#x001F; &#x000D; &#x007F;\\uFFFD\")\n      end\n\n      it \"does not escape noncharacter codepoints\" do\n        # noncharacters http://www.unicode.org/faq/private_use.html\n        string = \"&#xFDD0;-&#xFDEF; &#xFFFE; &#FFFF; &#x1FFFE; &#x1FFFF; &#x2FFFE; &#x10FFFF;\"\n        HTML.unescape(string).should eq(string)\n      end\n\n      it \"does not escape unicode surrogate characters\" do\n        HTML.unescape(\"&#xD800;-&#xDFFF;\").should eq(\"\\uFFFD-\\uFFFD\")\n      end\n    end\n\n    context \"named entities\" do\n      it \"simple named entities\" do\n        HTML.unescape(\"&lt; &amp; &gt;\").should eq(\"< & >\")\n        HTML.unescape(\"nbsp&nbsp;space \").should eq(\"nbsp\\u{0000A0}space \")\n      end\n\n      it \"without trailing semicolon\" do\n        HTML.unescape(\"&amphello\").should eq(\"&hello\")\n      end\n\n      it \"end of string\" do\n        HTML.unescape(\"&amp; &amp\").should eq(\"& &\")\n      end\n\n      it \"multi codepoint\" do\n        HTML.unescape(\" &NotSquareSuperset; \").should eq(\" ⊐̸ \")\n      end\n\n      it \"invalid entities\" do\n        HTML.unescape(\"&&lt;&amp&gt;&quot&abcdefghijklmn &ThisIsNotAnEntity;\").should eq(\"&<&>\\\"&abcdefghijklmn &ThisIsNotAnEntity;\")\n      end\n\n      it \"entity with numerical characters\" do\n        HTML.unescape(\"&frac34;\").should eq(\"\\u00BE\")\n      end\n    end\n\n    it \"unescapes javascript example from a string\" do\n      HTML.unescape(\"&lt;script&gt;alert&#40;&#39;You are being hacked&#39;&#41;&lt;/script&gt;\").should eq(\"<script>alert('You are being hacked')</script>\")\n    end\n\n    it \"invalid utf-8\" do\n      HTML.unescape(\"test \\xff\\xfe\").should eq \"test \\xff\\xfe\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/chunked_content_spec.cr",
    "content": "require \"spec\"\nrequire \"http/content\"\n\ndescribe HTTP::ChunkedContent do\n  it \"delays reading the next chunk as soon as one is consumed (#3270)\" do\n    mem = IO::Memory.new(\"4\\r\\n123\\n\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n    bytes = uninitialized UInt8[4]\n    bytes_read = content.read(bytes.to_slice)\n    bytes_read.should eq(4)\n    String.new(bytes.to_slice).should eq(\"123\\n\")\n    mem.pos.should eq(7) # only this chunk was read\n  end\n\n  it \"peeks\" do\n    mem = IO::Memory.new(\"4\\r\\n123\\n\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.peek.should eq(\"123\\n\".to_slice)\n    content.skip(4)\n    content.peek.should eq Bytes.empty\n  end\n\n  it \"peeks into next chunk\" do\n    mem = IO::Memory.new(\"4\\r\\n123\\n\\r\\n3\\r\\n456\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(4)\n    content.peek.should eq(\"456\".to_slice)\n    content.gets_to_end.should eq(\"456\")\n  end\n\n  it \"skips\" do\n    mem = IO::Memory.new(\"4\\r\\n123\\n\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(2)\n    content.read_char.should eq('3')\n\n    expect_raises(IO::EOFError) do\n      content.skip(10)\n    end\n  end\n\n  it \"skips (2)\" do\n    mem = IO::Memory.new(\"4\\r\\n123\\n\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(2)\n    content.gets_to_end.should eq(\"3\\n\")\n  end\n\n  it \"skips (3)\" do\n    mem = IO::Memory.new(\"4\\r\\n123\\n\\r\\n3\\r\\n456\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(4)\n    content.gets_to_end.should eq(\"456\")\n  end\n\n  it \"#gets reads multiple chunks\" do\n    mem = IO::Memory.new(\"1\\r\\nA\\r\\n1\\r\\nB\\r\\n0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.gets.should eq \"AB\"\n    mem.pos.should eq mem.bytesize\n  end\n\n  it \"#gets reads multiple chunks with \\n\" do\n    # The RFC format requires CRLF, but several standard implementations also\n    # accept LF, so we should too.\n    mem = IO::Memory.new(\"1\\nA\\n1\\nB\\n0\\n\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.gets.should eq \"AB\"\n    mem.pos.should eq mem.bytesize\n  end\n\n  it \"#read reads empty content\" do\n    mem = IO::Memory.new(\"0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.read(Bytes.new(5)).should eq 0\n    mem.pos.should eq mem.bytesize\n  end\n\n  it \"#read_byte reads empty content\" do\n    mem = IO::Memory.new(\"0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.read_byte.should be_nil\n    mem.pos.should eq mem.bytesize\n  end\n\n  it \"#peek reads empty content\" do\n    mem = IO::Memory.new(\"0\\r\\n\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.peek.should eq Bytes.empty\n    mem.pos.should eq mem.bytesize\n  end\n\n  it \"#read handles interrupted io\" do\n    mem = IO::Memory.new(\"3\\r\\n123\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(3)\n    expect_raises IO::EOFError do\n      content.read Bytes.new(5)\n    end\n  end\n\n  it \"#read_byte handles interrupted io\" do\n    mem = IO::Memory.new(\"3\\r\\n123\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(3)\n    expect_raises IO::EOFError do\n      content.read_byte\n    end\n  end\n\n  it \"#peek handles interrupted io\" do\n    mem = IO::Memory.new(\"3\\r\\n123\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    content.skip(3)\n    expect_raises IO::EOFError do\n      content.peek\n    end\n  end\n\n  it \"#read handles empty data\" do\n    mem = IO::Memory.new(\"3\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    expect_raises IO::EOFError do\n      content.read Bytes.new(1)\n    end\n  end\n\n  it \"#read_byte handles empty data\" do\n    mem = IO::Memory.new(\"3\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    expect_raises IO::Error do\n      content.read_byte\n    end\n  end\n\n  it \"#peek handles empty data\" do\n    mem = IO::Memory.new(\"3\\r\\n\")\n    content = HTTP::ChunkedContent.new(mem)\n\n    expect_raises IO::EOFError do\n      content.peek\n    end\n  end\n\n  it \"handles empty io\" do\n    mem = IO::Memory.new(\"\")\n    chunked = HTTP::ChunkedContent.new(mem)\n    expect_raises IO::EOFError do\n      chunked.gets\n    end\n  end\n\n  it \"reads chunk extensions\" do\n    mem = IO::Memory.new(%(1;progress=50;foo=\"bar\"\\r\\nA\\r\\n1;progress=100\\r\\nB\\r\\n0\\r\\n\\r\\n\"))\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    chunked.gets(2).should eq \"AB\"\n  end\n\n  it \"reads chunked trailer part\" do\n    mem = IO::Memory.new(\"0\\r\\nAdditional-Header: Foo\\r\\n\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    chunked.gets.should be_nil\n    mem.gets.should be_nil\n    chunked.headers.should eq HTTP::Headers{\"Additional-Header\" => \"Foo\"}\n  end\n\n  it \"fails if unterminated chunked trailer part\" do\n    mem = IO::Memory.new(\"0\\r\\nAdditional-Header: Foo\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    expect_raises IO::EOFError do\n      chunked.gets\n    end\n  end\n\n  describe \"long trailer part\" do\n    it \"fails for long single header\" do\n      mem = IO::Memory.new(\"0\\r\\nFoo: Bar Baz Qux\\r\\n\\r\\n\")\n\n      chunked = HTTP::ChunkedContent.new(mem, max_headers_size: 12)\n      expect_raises(IO::Error, \"Trailing headers too long\") do\n        chunked.gets\n      end\n      chunked.headers.should be_empty\n    end\n\n    it \"fails for long combined headers\" do\n      mem = IO::Memory.new(\"0\\r\\nFoo: Bar\\r\\nBaz: Qux\\r\\n\\r\\n\")\n\n      chunked = HTTP::ChunkedContent.new(mem, max_headers_size: 12)\n      expect_raises(IO::Error, \"Trailing headers too long\") do\n        chunked.gets\n      end\n      chunked.headers.should eq HTTP::Headers{\"Foo\" => \"Bar\"}\n    end\n  end\n\n  it \"fails if not properly delimited\" do\n    mem = IO::Memory.new(\"0\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    expect_raises IO::EOFError do\n      chunked.gets\n    end\n  end\n\n  it \"fails if not properly delimited\" do\n    mem = IO::Memory.new(\"1\\r\\nA\\r\\n0\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    expect_raises IO::EOFError do\n      chunked.gets\n    end\n  end\n\n  it \"fails if invalid chunk size\" do\n    mem = IO::Memory.new(\"G\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    expect_raises IO::Error, \"Invalid HTTP chunked content: invalid chunk size\" do\n      chunked.gets\n    end\n  end\n\n  it \"#read stops reading after final chunk\" do\n    mem = IO::Memory.new(\"0\\r\\n\\r\\n1\\r\\nA\\r\\n0\\r\\n\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    chunked.read(Bytes.new(8)).should eq 0\n    mem.pos.should eq 5\n    chunked.read(Bytes.new(8)).should eq 0\n    mem.pos.should eq 5\n  end\n\n  it \"#read_byte stops reading after final chunk\" do\n    mem = IO::Memory.new(\"0\\r\\n\\r\\n1\\r\\nA\\r\\n0\\r\\n\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    chunked.read_byte.should be_nil\n    mem.pos.should eq 5\n    chunked.read_byte.should be_nil\n    mem.pos.should eq 5\n  end\n\n  it \"#peek stops reading after final chunk\" do\n    mem = IO::Memory.new(\"0\\r\\n\\r\\n1\\r\\nA\\r\\n0\\r\\n\\r\\n\")\n\n    chunked = HTTP::ChunkedContent.new(mem)\n    chunked.peek.should eq Bytes.empty\n    mem.pos.should eq 5\n    chunked.peek.should eq Bytes.empty\n    mem.pos.should eq 5\n  end\nend\n"
  },
  {
    "path": "spec/std/http/client/client_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../socket/spec_helper\"\nrequire \"openssl\"\nrequire \"http/client\"\nrequire \"http/server\"\nrequire \"http/log\"\nrequire \"log/spec\"\n\nprivate def test_server(host, port, read_time = 0.seconds, content_type = \"text/plain\", write_response = true, &)\n  server = TCPServer.new(host, port)\n  begin\n    spawn do\n      io = server.accept\n      sleep read_time\n      if write_response\n        response = HTTP::Client::Response.new(200, headers: HTTP::Headers{\"Content-Type\" => content_type}, body: \"OK\")\n        response.to_io(io)\n        io.flush\n      end\n    end\n\n    yield server\n  ensure\n    server.close\n  end\nend\n\nprivate class TestClient < HTTP::Client\n  def set_defaults(request)\n    super\n  end\nend\n\nmodule HTTP\n  describe Client do\n    typeof(Client.new(\"host\"))\n    typeof(Client.new(\"host\", port: 8080))\n    typeof(Client.new(\"host\", tls: true))\n    typeof(Client.new(URI.new))\n    typeof(Client.new(URI.parse(\"http://www.example.com\")))\n\n    {% for method in %w(get post put head delete patch options) %}\n      typeof(Client.{{method.id}} \"url\")\n      typeof(Client.new(\"host\").{{method.id}}(\"uri\"))\n      typeof(Client.new(\"host\").{{method.id}}(\"uri\", headers: Headers {\"Content-Type\" => \"text/plain\"}))\n      typeof(Client.new(\"host\").{{method.id}}(\"uri\", body: \"body\"))\n    {% end %}\n\n    typeof(Client.post \"url\", form: {\"a\" => \"b\"})\n    typeof(Client.post(\"url\", form: {\"a\" => \"b\"}) { })\n    typeof(Client.put \"url\", form: {\"a\" => \"b\"})\n    typeof(Client.put(\"url\", form: {\"a\" => \"b\"}) { })\n    typeof(Client.new(\"host\").basic_auth(\"username\", \"password\"))\n    typeof(Client.new(\"host\").before_request { |req| })\n    typeof(Client.new(\"host\").close)\n    typeof(Client.new(\"host\").compress = true)\n    typeof(Client.new(\"host\").compress?)\n    typeof(Client.get(URI.parse(\"http://www.example.com\")))\n    typeof(Client.get(URI.parse(\"http://www.example.com\")))\n    typeof(Client.get(\"http://www.example.com\"))\n    typeof(Client.post(\"http://www.example.com\", body: IO::Memory.new))\n    typeof(Client.new(\"host\").post(\"/\", body: IO::Memory.new))\n    typeof(Client.post(\"http://www.example.com\", body: Bytes[65]))\n    typeof(Client.new(\"host\").post(\"/\", body: Bytes[65]))\n\n    describe \"from String\" do\n      it \"raises when not a host\" do\n        [\"http://www.example.com\",\n         \"www.example.com:8080\",\n         \"example.com/path\",\n         \"example.com?query\",\n         \"http://example.com:bad_port\",\n         \"user:pass@domain\"].each do |string|\n          expect_raises(ArgumentError, \"The string passed to create an HTTP::Client must be just a host, not #{string.inspect}\") do\n            Client.new(string)\n          end\n        end\n      end\n    end\n\n    describe \"from URI\" do\n      it \"has sane defaults\" do\n        cl = Client.new(URI.parse(\"http://example.com\"))\n        cl.tls?.should be_nil\n        cl.port.should eq(80)\n      end\n\n      {% if !flag?(:without_openssl) %}\n        it \"detects HTTPS\" do\n          cl = Client.new(URI.parse(\"https://example.com\"))\n          cl.tls?.should be_truthy\n          cl.port.should eq(443)\n        end\n\n        it \"keeps context\" do\n          ctx = OpenSSL::SSL::Context::Client.new\n          cl = Client.new(URI.parse(\"https://example.com\"), ctx)\n          cl.tls.should be(ctx)\n        end\n\n        it \"doesn't take context for HTTP\" do\n          ctx = OpenSSL::SSL::Context::Client.new\n          expect_raises(ArgumentError, \"TLS context given\") do\n            Client.new(URI.parse(\"http://example.com\"), ctx)\n          end\n        end\n\n        it \"allows for specified ports\" do\n          cl = Client.new(URI.parse(\"https://example.com:9999\"))\n          cl.tls?.should be_truthy\n          cl.port.should eq(9999)\n        end\n      {% else %}\n        it \"raises when trying to activate TLS\" do\n          expect_raises(Exception, \"TLS is disabled\") do\n            Client.new \"example.org\", 443, tls: true\n          end\n        end\n      {% end %}\n\n      it \"raises error if not http schema\" do\n        expect_raises(ArgumentError, \"Unsupported scheme: ssh\") do\n          Client.new(URI.parse(\"ssh://example.com\"))\n        end\n      end\n\n      it \"raises error if URI is missing host\" do\n        expect_raises(ArgumentError, \"must have host\") do\n          Client.new(URI.parse(\"http:/\"))\n        end\n      end\n\n      it \"yields to a block\" do\n        Client.new(URI.parse(\"http://example.com\")) do |client|\n          typeof(client)\n        end\n      end\n    end\n\n    context \"from a host\" do\n      it \"yields to a block\" do\n        Client.new(\"example.com\") do |client|\n          typeof(client)\n        end\n      end\n    end\n\n    pending_ipv6 \"sends the host header ipv6 with brackets\" do\n      server = HTTP::Server.new do |context|\n        context.response.print context.request.headers[\"Host\"]\n      end\n      address = server.bind_unused_port \"::1\"\n\n      run_server(server) do\n        HTTP::Client.get(\"http://[::1]:#{address.port}/\").body.should eq(\"[::1]:#{address.port}\")\n      end\n    end\n\n    it \"sends a 'connection: close' header on one-shot request\" do\n      server = HTTP::Server.new do |context|\n        context.response.print context.request.headers[\"connection\"]\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        HTTP::Client.get(\"http://127.0.0.1:#{address.port}/\").body.should eq(\"close\")\n      end\n    end\n\n    it \"sends a 'connection: close' header on one-shot request with block\" do\n      server = HTTP::Server.new do |context|\n        context.response.print context.request.headers[\"connection\"]\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        HTTP::Client.get(\"http://127.0.0.1:#{address.port}/\") do |response|\n          response.body_io.gets_to_end\n        end.should eq(\"close\")\n      end\n    end\n\n    it \"ensures closing the response when breaking out of block\" do\n      server = HTTP::Server.new { }\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        client = HTTP::Client.new(address.address, address.port)\n        response = nil\n\n        exc = Exception.new(\"\")\n        expect_raises Exception do\n          client.get(\"/\") do |r|\n            response = r\n            raise exc\n          end\n        end.should be exc\n\n        response.try(&.body_io?.try(&.closed?)).should be_true\n      end\n    end\n\n    it \"will retry a broken socket\" do\n      server = HTTP::Server.new do |context|\n        context.response.output.print \"foo\"\n        context.response.output.close\n        io = context.response.@io.as(Socket)\n        io.linger = 0 # with linger 0 the socket will be RST on close\n\n        {% if flag?(:win32) %}\n          # FIXME: calling #close will shutdown the socket before closing it,\n          # which results in WSARecv to raise an IO::Error in HTTP::Client and\n          # fail the spec; for the time being we bypass the shutdown by closing\n          # the socket only\n          io.socket_close\n        {% else %}\n          io.close\n        {% end %}\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        client = HTTP::Client.new(\"127.0.0.1\", address.port)\n        client.get(path: \"/\").body.should eq \"foo\"\n        client.get(path: \"/\").body.should eq \"foo\"\n        client.get(path: \"/\") do |resp|\n          resp.body_io.gets_to_end.should eq \"foo\"\n        end\n      end\n    end\n\n    it \"will retry once on connection error\" do\n      requests = 0\n      server = HTTP::Server.new do |context|\n        requests += 1\n        io = context.response.@io.as(Socket)\n        io.linger = 0 # with linger 0 the socket will be RST on close\n        io.close\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        client = HTTP::Client.new(\"127.0.0.1\", address.port)\n        expect_raises(IO::Error) do\n          client.get(path: \"/\")\n        end\n        requests.should eq 2\n      end\n    end\n\n    it \"will not retry if IO::Error in request handling\" do\n      requests = 0\n      server = HTTP::Server.new do |context|\n        requests += 1\n        context.response.puts \"foo\"\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        client = HTTP::Client.new(\"127.0.0.1\", address.port)\n        expect_raises(IO::Error) do\n          client.get(path: \"/\") do\n            raise IO::Error.new\n          end\n        end\n        requests.should eq 1\n      end\n    end\n\n    it \"will not retry when closed (non-block) (#12464)\" do\n      requests = 0\n\n      client = HTTP::Client.new(\"127.0.0.1\", 0)\n      client.before_request do\n        requests += 1\n        raise IO::Error.new(\"foobar\")\n      end\n\n      expect_raises(IO::Error, \"foobar\") do\n        client.not_nil!.get(path: \"/\")\n      end\n      requests.should eq 1\n    end\n\n    it \"will not retry when closed (block) (#12464)\" do\n      requests = 0\n\n      client = HTTP::Client.new(\"127.0.0.1\", 0)\n      client.before_request do\n        requests += 1\n        raise IO::Error.new(\"foobar\")\n      end\n\n      expect_raises(IO::Error, \"foobar\") do\n        client.not_nil!.get(path: \"/\") { }\n      end\n      requests.should eq 1\n    end\n\n    it \"retry does not affect implicit compression (#11354)\" do\n      server = HTTP::Server.new do |context|\n        context.response.headers[\"Content-Encoding\"] = \"gzip\"\n        context.response.output.print \"\\u001F\\x8B\\b\\u0000\\u0000\\u0000\\u0000\\u0000\\u0004\\u0003+\\xCFH,I-K-\\u0002\\u0000\\xB3C\\u0011N\\b\\u0000\\u0000\\u0000\"\n        context.response.output.close\n        io = context.response.@io.as(Socket)\n        io.linger = 0 # with linger 0 the socket will be RST on close\n        io.close\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        client = HTTP::Client.new(\"127.0.0.1\", address.port)\n        # First request establishes the server connection, but the server\n        # immediately closes it after sending the response.\n        client.get(path: \"/\")\n\n        # Second request tries to re-use the connection which fails (due to the\n        # server's hang up) and then it retries by establishing a new connection.\n        client.get(path: \"/\").body.should eq \"whatever\"\n      end\n    end\n\n    it \"retry does not call before_request callback again\" do\n      server = HTTP::Server.new do |context|\n        io = context.response.@io.as(Socket)\n        io.linger = 0 # with linger 0 the socket will be RST on close\n        io.close\n      end\n      address = server.bind_unused_port \"127.0.0.1\"\n\n      run_server(server) do\n        callback_counts = 0\n        client = HTTP::Client.new(\"127.0.0.1\", address.port)\n        client.before_request do\n          callback_counts += 1\n        end\n        expect_raises(IO::Error) do\n          client.get(path: \"/\")\n        end\n        callback_counts.should eq 1\n      end\n    end\n\n    it \"doesn't read the body if request was HEAD\" do\n      resp_get = test_server(\"localhost\", 0, 0.seconds) do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        break client.get(\"/\")\n      end\n\n      test_server(\"localhost\", 0, 0.seconds) do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        resp_head = client.head(\"/\")\n        resp_head.headers.should eq(resp_get.headers)\n        resp_head.body.should eq(\"\")\n      end\n    end\n\n    it \"raises if URI is missing scheme\" do\n      expect_raises(ArgumentError, \"Missing scheme\") do\n        HTTP::Client.get URI.parse(\"www.example.com\")\n      end\n    end\n\n    it \"raises if URI is missing host\" do\n      expect_raises(ArgumentError, \"must have host\") do\n        HTTP::Client.get URI.parse(\"http://\")\n      end\n    end\n\n    it \"tests read_timeout\" do\n      test_server(\"localhost\", 0, 0.seconds) do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        client.read_timeout = 1.second\n        client.get(\"/\")\n      end\n\n      # Here we don't want to write a response on the server side because\n      # it doesn't make sense to try to write because the client will already\n      # timeout on read. Writing a response could lead on an exception in\n      # the server if the socket is closed.\n      test_server(\"localhost\", 0, 0.5.seconds, write_response: false) do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        expect_raises(IO::TimeoutError, {% if flag?(:win32) %} \"WSARecv timed out\" {% else %} \"Read timed out\" {% end %}) do\n          client.read_timeout = 1.millisecond\n          client.get(\"/?sleep=1\")\n        end\n      end\n    end\n\n    it \"tests write_timeout\" do\n      # Here we don't want to write a response on the server side because\n      # it doesn't make sense to try to write because the client will already\n      # timeout on read. Writing a response could lead on an exception in\n      # the server if the socket is closed.\n      test_server(\"localhost\", 0, 0.seconds, write_response: false) do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        expect_raises(IO::TimeoutError, {% if flag?(:win32) %} \"WSASend timed out\" {% else %} \"Write timed out\" {% end %}) do\n          client.write_timeout = 1.millisecond\n          client.post(\"/\", body: \"a\" * 5_000_000)\n        end\n      end\n    end\n\n    it \"tests connect_timeout\" do\n      test_server(\"localhost\", 0, 0.seconds) do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        client.connect_timeout = 0.5.seconds\n        client.get(\"/\")\n      end\n    end\n\n    it \"tests empty Content-Type\" do\n      test_server(\"localhost\", 0, content_type: \"\") do |server|\n        client = Client.new(\"localhost\", server.local_address.port)\n        client.get(\"/\")\n      end\n    end\n\n    describe \"#set_defaults\" do\n      it \"sets default Host header\" do\n        client = TestClient.new \"www.example.com\"\n        request = HTTP::Request.new(\"GET\", \"/\")\n        client.set_defaults(request)\n        request.hostname.should eq \"www.example.com\"\n\n        request = HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"other.example.com\"})\n        client.set_defaults(request)\n        request.hostname.should eq \"other.example.com\"\n      end\n    end\n\n    it \"works with IO\" do\n      io_response = IO::Memory.new <<-HTTP.gsub('\\n', \"\\r\\n\")\n      HTTP/1.1 200 OK\n      Content-Type: text/plain\n      Content-Length: 3\n\n      Hi!\n      HTTP\n      io_request = IO::Memory.new\n      io = IO::Stapled.new(io_response, io_request)\n      client = Client.new(io)\n      response = client.get(\"/\")\n      response.body.should eq(\"Hi!\")\n\n      io_request.rewind\n      request = HTTP::Request.from_io(io_request).as(HTTP::Request)\n      request.hostname.should eq(\"\")\n    end\n\n    it \"can specify host and port when initialized with IO\" do\n      client = Client.new(IO::Memory.new, \"host\", 1234)\n      client.host.should eq(\"host\")\n      client.port.should eq(1234)\n    end\n\n    it \"cannot reconnect when initialized with IO\" do\n      io = IO::Memory.new\n      client = Client.new(io)\n      client.close\n      io.closed?.should be_true\n      expect_raises(Exception, \"This HTTP::Client cannot be reconnected\") do\n        client.get(\"/\")\n      end\n    end\n\n    describe \"logging\" do\n      it \"emit logs\" do\n        test_server(\"localhost\", 0, content_type: \"\") do |server|\n          client = Client.new(\"localhost\", server.local_address.port)\n          Log.capture(\"http.client\") do |logs|\n            client.get(\"/\")\n\n            logs.check(:debug, \"Performing request\")\n            logs.entry.data[:method].should eq(\"GET\")\n            logs.entry.data[:host].should eq(\"localhost\")\n            logs.entry.data[:port].should eq(server.local_address.port)\n            logs.entry.data[:resource].should eq(\"/\")\n          end\n        end\n      end\n\n      it \"emit logs with block\" do\n        test_server(\"localhost\", 0, content_type: \"\") do |server|\n          Client.new(\"localhost\", server.local_address.port) do |client|\n            Log.capture(\"http.client\") do |logs|\n              client.get(\"/\") do |response|\n                logs.check(:debug, \"Performing request\")\n                logs.entry.data[:method].should eq(\"GET\")\n                logs.entry.data[:host].should eq(\"localhost\")\n                logs.entry.data[:port].should eq(server.local_address.port)\n                logs.entry.data[:resource].should eq(\"/\")\n              end\n            end\n          end\n        end\n      end\n    end\n\n    it \"can be subclassed\" do\n      expect_raises(Exception, \"from subclass\") do\n        SubClient.get(\"http://localhost\")\n      end\n    end\n  end\n\n  class SubClient < HTTP::Client\n    def around_exec(request, &)\n      raise \"from subclass\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/client/response_spec.cr",
    "content": "require \"spec\"\nrequire \"http/client/response\"\n\nclass HTTP::Client::Response\n  describe Response do\n    it \"parses response with body\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\n\\r\\nhelloworld\"))\n      response.version.should eq(\"HTTP/1.1\")\n      response.status_code.should eq(200)\n      response.status_message.should eq(\"OK\")\n      response.headers[\"content-type\"].should eq(\"text/plain\")\n      response.headers[\"content-length\"].should eq(\"5\")\n      response.body.should eq(\"hello\")\n    end\n\n    it \"parses response with streamed body\" do\n      Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\n\\r\\nhelloworld\")) do |response|\n        response.version.should eq(\"HTTP/1.1\")\n        response.status_code.should eq(200)\n        response.status_message.should eq(\"OK\")\n        response.headers[\"content-type\"].should eq(\"text/plain\")\n        response.headers[\"content-length\"].should eq(\"5\")\n        response.body?.should be_nil\n        response.body_io.gets_to_end.should eq(\"hello\")\n      end\n    end\n\n    it \"parses response with streamed body, huge content-length\" do\n      Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: #{UInt64::MAX}\\r\\n\\r\\nhelloworld\")) do |response|\n        response.headers[\"content-length\"].should eq(\"#{UInt64::MAX}\")\n      end\n    end\n\n    it \"parses response with body without \\\\r\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\nContent-Type: text/plain\\nContent-Length: 5\\n\\nhelloworld\"))\n      response.version.should eq(\"HTTP/1.1\")\n      response.status_code.should eq(200)\n      response.status_message.should eq(\"OK\")\n      response.headers[\"content-type\"].should eq(\"text/plain\")\n      response.headers[\"content-length\"].should eq(\"5\")\n      response.body.should eq(\"hello\")\n    end\n\n    it \"parses response with body but without content-length\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\n\\r\\nhelloworld\"))\n      response.status_code.should eq(200)\n      response.status_message.should eq(\"OK\")\n      response.headers.size.should eq(0)\n      response.body.should eq(\"helloworld\")\n    end\n\n    it \"parses response with empty body but without content-length\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\"))\n      response.status_code.should eq(404)\n      response.status_message.should eq(\"Not Found\")\n      response.headers.size.should eq(0)\n      response.body.should eq(\"\")\n    end\n\n    it \"parses response without body\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 100 Continue\\r\\n\\r\\n\"))\n      response.status_code.should eq(100)\n      response.status_message.should eq(\"Continue\")\n      response.headers.size.should eq(0)\n      response.body?.should be_nil\n    end\n\n    it \"parses response without status message\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200\\r\\n\\r\\n\"))\n      response.status_code.should eq(200)\n      response.status_message.should eq(\"\")\n      response.headers.size.should eq(0)\n      response.body.should eq(\"\")\n    end\n\n    it \"parses response with duplicated headers\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\nWarning: 111 Revalidation failed\\r\\nWarning: 110 Response is stale\\r\\n\\r\\nhelloworld\"))\n      response.headers.get(\"Warning\").should eq([\"111 Revalidation failed\", \"110 Response is stale\"])\n    end\n\n    it \"parses response with cookies\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\nSet-Cookie: a=b\\r\\nSet-Cookie: c=d\\r\\n\\r\\nhelloworld\"))\n      response.cookies[\"a\"].value.should eq(\"b\")\n      response.cookies[\"c\"].value.should eq(\"d\")\n    end\n\n    it \"parses response with chunked body\" do\n      response = Response.from_io(io = IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nabcde\\r\\na\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\"))\n      response.body.should eq(\"abcde0123456789\")\n      io.gets.should be_nil\n    end\n\n    it \"parses response with streamed chunked body\" do\n      Response.from_io(io = IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nabcde\\r\\na\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\")) do |response|\n        response.body_io.gets_to_end.should eq(\"abcde0123456789\")\n        io.gets.should be_nil\n      end\n    end\n\n    it \"parses response with chunked body of size 0\" do\n      response = Response.from_io(io = IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n0\\r\\n\\r\\n\"))\n      response.body.should eq(\"\")\n      io.gets.should be_nil\n    end\n\n    it \"parses response ignoring body\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\n\\r\\nhelloworld\"), true)\n      response.version.should eq(\"HTTP/1.1\")\n      response.status_code.should eq(200)\n      response.status_message.should eq(\"OK\")\n      response.headers[\"content-type\"].should eq(\"text/plain\")\n      response.headers[\"content-length\"].should eq(\"5\")\n      response.body.should eq(\"\")\n    end\n\n    it \"parses 204 response without body but Content-Length == 0 (#2512)\" do\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 204 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 0\\r\\n\\r\\n\"))\n      response.version.should eq(\"HTTP/1.1\")\n      response.status_code.should eq(204)\n      response.status_message.should eq(\"OK\")\n      response.headers[\"content-type\"].should eq(\"text/plain\")\n      response.headers[\"content-length\"].should eq(\"0\")\n      response.body.should eq(\"\")\n    end\n\n    it \"parses response without body but Content-Length == 0, block form (#8461)\" do\n      Response.from_io(IO::Memory.new(\"HTTP/1.1 301 OK\\r\\nContent-Length: 0\\r\\n\\r\\n\")) do |response|\n        response.body_io.gets_to_end.should eq(\"\")\n      end\n    end\n\n    it \"parses long request lines\" do\n      request = Response.from_io?(IO::Memory.new(\"HTTP/1.1 200 #{\"OK\" * 600_000}\\r\\n\\r\\n\"))\n      request.should be_nil\n    end\n\n    it \"parses long headers\" do\n      request = Response.from_io?(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\n#{\"X-Test-Header: A pretty log header value\\r\\n\" * 100_000}\\r\\n\"))\n      request.should be_nil\n    end\n\n    describe \"handle invalid IO\" do\n      it \"missing HTTP header\" do\n        expect_raises(Exception, \"Invalid HTTP response\") do\n          Response.from_io?(IO::Memory.new(\"<html>\\n</html>\"))\n        end\n      end\n\n      it \"unsupported version\" do\n        expect_raises(Exception, \"Unsupported HTTP version: HTML/1.0\") do\n          Response.from_io?(IO::Memory.new(\"HTML/1.0 200 OK\\n\\nNot an HTTP response\"))\n        end\n      end\n\n      it \"missing status\" do\n        expect_raises(Exception, \"Invalid HTTP response\") do\n          Response.from_io?(IO::Memory.new(\"HTTP/1.0\\n\\nNot an HTTP response\"))\n        end\n      end\n\n      it \"invalid status\" do\n        expect_raises(Exception, \"Invalid HTTP status code: OK\") do\n          Response.from_io?(IO::Memory.new(\"HTTP/1.0 OK\\n\\nNot an HTTP response\"))\n        end\n        expect_raises(Exception, \"Invalid HTTP status code: 1000\") do\n          Response.from_io?(IO::Memory.new(\"HTTP/1.0 1000\\n\\nNot an HTTP response\"))\n        end\n        expect_raises(Exception, \"Invalid HTTP status code: -5\") do\n          Response.from_io?(IO::Memory.new(\"HTTP/1.0 -5\\n\\nNot an HTTP response\"))\n        end\n        expect_raises(Exception, \"Invalid HTTP status code: 42\") do\n          Response.from_io?(IO::Memory.new(\"HTTP/1.0 42\\n\\nNot an HTTP response\"))\n        end\n      end\n    end\n\n    it \"doesn't sets content length for 1xx, 204 or 304\" do\n      [HTTP::Status::CONTINUE, HTTP::Status::SWITCHING_PROTOCOLS, HTTP::Status::NO_CONTENT, HTTP::Status::NOT_MODIFIED].each do |status|\n        response = Response.new(status)\n        response.headers.size.should eq(0)\n      end\n    end\n\n    it \"raises when creating 1xx, 204 or 304 with body\" do\n      [HTTP::Status::CONTINUE, HTTP::Status::SWITCHING_PROTOCOLS, HTTP::Status::NO_CONTENT, HTTP::Status::NOT_MODIFIED].each do |status|\n        expect_raises ArgumentError do\n          Response.new(status, \"hello\")\n        end\n      end\n    end\n\n    it \"serialize with body\" do\n      headers = HTTP::Headers.new\n      headers[\"Content-Type\"] = \"text/plain\"\n      headers[\"Content-Length\"] = \"5\"\n\n      response = Response.new(:ok, \"hello\", headers)\n      io = IO::Memory.new\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\n\\r\\nhello\")\n    end\n\n    it \"serialize with body and cookies\" do\n      headers = HTTP::Headers.new\n      headers[\"Content-Type\"] = \"text/plain\"\n      headers[\"Content-Length\"] = \"5\"\n      headers[\"Set-Cookie\"] = \"foo=bar; path=/\"\n\n      response = Response.new(:ok, \"hello\", headers)\n\n      io = IO::Memory.new\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\nSet-Cookie: foo=bar; path=/\\r\\n\\r\\nhello\")\n\n      response.cookies[\"foo\"].value.should eq \"bar\" # Force lazy initialization\n\n      io.clear\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\nSet-Cookie: foo=bar; path=/\\r\\n\\r\\nhello\")\n\n      response.cookies[\"foo\"] = \"baz\"\n      response.cookies << Cookie.new(\"quux\", \"baz\")\n\n      io.clear\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\nSet-Cookie: foo=baz\\r\\nSet-Cookie: quux=baz\\r\\n\\r\\nhello\")\n    end\n\n    it \"sets content length from body\" do\n      response = Response.new(:ok, \"hello\")\n      io = IO::Memory.new\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nhello\")\n    end\n\n    it \"sets content length even without body\" do\n      response = Response.new(:ok)\n      io = IO::Memory.new\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n    end\n\n    it \"serialize as chunked with body_io\" do\n      response = Response.new(:ok, body_io: IO::Memory.new(\"hello\"))\n      io = IO::Memory.new\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nhello\\r\\n0\\r\\n\\r\\n\")\n    end\n\n    it \"serialize as not chunked with body_io if HTTP/1.0\" do\n      response = Response.new(:ok, version: \"HTTP/1.0\", body_io: IO::Memory.new(\"hello\"))\n      io = IO::Memory.new\n      response.to_io(io)\n      io.to_s.should eq(\"HTTP/1.0 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nhello\")\n    end\n\n    it \"returns no content_type when header is missing\" do\n      response = Response.new(:ok, \"\")\n      response.content_type.should be_nil\n      response.charset.should be_nil\n    end\n\n    it \"returns content type and no charset\" do\n      response = Response.new(:ok, \"\", headers: HTTP::Headers{\"Content-Type\" => \"application/octet-stream\"})\n      response.content_type.should eq(\"application/octet-stream\")\n      response.charset.should be_nil\n    end\n\n    it \"returns content type and charset, removes semicolon\" do\n      response = Response.new(:ok, \"\", headers: HTTP::Headers{\"Content-Type\" => \"text/plain ; charset=UTF-8\"})\n      response.content_type.should eq(\"text/plain\")\n      response.charset.should eq(\"UTF-8\")\n    end\n\n    it \"returns content type and charset, removes quotes\" do\n      response = Response.new(:ok, \"\", headers: HTTP::Headers{\"Content-Type\" => %(text/plain ; charset=\"UTF-8\")})\n      response.content_type.should eq(\"text/plain\")\n      response.charset.should eq(\"UTF-8\")\n    end\n\n    it \"returns content type and no charset, other parameter (#2520)\" do\n      response = Response.new(:ok, \"\", headers: HTTP::Headers{\"Content-Type\" => \"application/octet-stream ; colenc=U\"})\n      response.content_type.should eq(\"application/octet-stream\")\n      response.charset.should be_nil\n    end\n\n    it \"returns content type and charset, removes semicolon, with multiple parameters (#2520)\" do\n      response = Response.new(:ok, \"\", headers: HTTP::Headers{\"Content-Type\" => \"text/plain ; colenc=U ; charset=UTF-8\"})\n      response.content_type.should eq(\"text/plain\")\n      response.charset.should eq(\"UTF-8\")\n    end\n\n    it \"returns content type as nil when empty (#8398)\" do\n      response = Response.new(:ok, \"\", headers: HTTP::Headers{\"Content-Type\" => \"\"})\n      response.content_type.should be_nil\n    end\n\n    it \"returns status_code\" do\n      response = Response.new(:created)\n      response.status_code.should eq 201\n    end\n\n    it \"creates Response with status code 204, no body and Content-Length == 0 (#2512)\" do\n      response = Response.new(204, version: \"HTTP/1.0\", body: \"\", headers: HTTP::Headers{\"Content-Length\" => \"0\"})\n      response.status_code.should eq(204)\n      response.body.should eq(\"\")\n    end\n\n    it \"deletes Content-Encoding and Content-Length headers after gzip decompression\" do\n      body = String.build do |io|\n        Compress::Gzip::Writer.open(io, &.print(\"hello\"))\n      end\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Encoding: gzip\\r\\nContent-Length: #{body.bytesize}\\r\\n\\r\\n#{body}\"))\n      response.body.should eq(\"hello\")\n      response.headers[\"content-encoding\"]?.should be_nil\n      response.headers[\"content-length\"]?.should be_nil\n    end\n\n    it \"deletes Content-Encoding and Content-Length headers after deflate decompression\" do\n      body = String.build do |io|\n        Compress::Deflate::Writer.open(io, &.print(\"hello\"))\n      end\n      response = Response.from_io(IO::Memory.new(\"HTTP/1.1 200 OK\\r\\nContent-Encoding: deflate\\r\\nContent-Length: #{body.bytesize}\\r\\n\\r\\n#{body}\"))\n      response.body.should eq(\"hello\")\n      response.headers[\"content-encoding\"]?.should be_nil\n      response.headers[\"content-length\"]?.should be_nil\n    end\n\n    describe \"success?\" do\n      it \"returns true for the 2xx\" do\n        response = Response.new(:ok)\n\n        response.success?.should be_true\n      end\n\n      it \"returns false for other ranges\" do\n        response = Response.new(500)\n\n        response.success?.should be_false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/cookie_spec.cr",
    "content": "require \"spec\"\nrequire \"http/cookie\"\nrequire \"http/headers\"\nrequire \"spec/helpers/string\"\n\nprivate def parse_first_cookie(header)\n  cookies = HTTP::Cookie::Parser.parse_cookies(header)\n  cookies.size.should eq(1)\n  cookies.first\nend\n\nprivate def parse_set_cookie(header)\n  cookie = HTTP::Cookie::Parser.parse_set_cookie(header)\n  cookie.should_not be_nil\n  cookie.not_nil!\nend\n\n# invalid printable ascii characters, non-printable ascii characters and control characters\nprivate INVALID_COOKIE_VALUES = (\"\\x00\"..\"\\x08\").to_a + (\"\\x0A\"..\"\\x1F\").to_a + [\"\\r\", \"\\t\", \"\\n\", %(\" \"), %(\"), \",\", \";\", \"\\\\\", \"\\x7f\", \"\\xFF\", \"🍪\"]\n\nmodule HTTP\n  describe Cookie do\n    it \"#==\" do\n      cookie = Cookie.new(\"a\", \"b\", path: \"/path\", expires: Time.utc, domain: \"domain\", secure: true, http_only: true, samesite: :strict, extension: \"foo=bar\")\n      cookie.should eq(cookie.dup)\n      cookie.should_not eq(cookie.dup.tap { |c| c.name = \"c\" })\n      cookie.should_not eq(cookie.dup.tap { |c| c.value = \"c\" })\n      cookie.should_not eq(cookie.dup.tap { |c| c.path = \"/c\" })\n      cookie.should_not eq(cookie.dup.tap { |c| c.domain = \"c\" })\n      cookie.should_not eq(cookie.dup.tap { |c| c.expires = Time.utc(2021, 1, 1) })\n      cookie.should_not eq(cookie.dup.tap { |c| c.secure = false })\n      cookie.should_not eq(cookie.dup.tap { |c| c.http_only = false })\n      cookie.should_not eq(cookie.dup.tap { |c| c.samesite = :lax })\n      cookie.should_not eq(cookie.dup.tap { |c| c.extension = nil })\n    end\n\n    describe \".new\" do\n      it \"raises on invalid name\" do\n        expect_raises IO::Error, \"Invalid cookie name\" do\n          HTTP::Cookie.new(\"\", \"\")\n        end\n        expect_raises IO::Error, \"Invalid cookie name\" do\n          HTTP::Cookie.new(\"\\t\", \"\")\n        end\n      end\n\n      it \"raises on invalid value\" do\n        expect_raises IO::Error, \"Invalid cookie value\" do\n          HTTP::Cookie.new(\"x\", %(foo\\rbar))\n        end\n\n        INVALID_COOKIE_VALUES.each do |char|\n          expect_raises IO::Error, \"Invalid cookie value\" do\n            HTTP::Cookie.new(\"x\", char)\n          end\n        end\n      end\n\n      describe \"with a security prefix\" do\n        it \"raises on invalid cookie with prefix\" do\n          expect_raises ArgumentError, \"Invalid cookie name. Has '__Secure-' prefix, but is not secure.\" do\n            HTTP::Cookie.new(\"__Secure-foo\", \"bar\", secure: false)\n          end\n\n          expect_raises ArgumentError, \"Invalid cookie name. Does not meet '__Host-' prefix requirements of: secure: true, path: \\\"/\\\", domain: nil.\" do\n            HTTP::Cookie.new(\"__Host-foo\", \"bar\", domain: \"foo\")\n          end\n        end\n\n        it \"automatically makes the cookie secure if it has the __Secure- prefix and no explicit *secure* value is provided\" do\n          HTTP::Cookie.new(\"__Secure-foo\", \"bar\").secure.should be_true\n        end\n\n        it \"automatically configures the cookie if it has the __Host- prefix and no explicit values provided\" do\n          cookie = HTTP::Cookie.new \"__Host-foo\", \"bar\"\n          cookie.secure.should be_true\n          cookie.domain.should be_nil\n          cookie.path.should eq \"/\"\n        end\n      end\n    end\n\n    it \"#expire\" do\n      cookie = HTTP::Cookie.new(\"hello\", \"world\")\n      cookie.expire\n\n      cookie.value.empty?.should be_true\n      cookie.expired?.should be_true\n      cookie.max_age.should eq(Time::Span.zero)\n    end\n\n    describe \"#name=\" do\n      it \"raises on invalid name\" do\n        cookie = HTTP::Cookie.new(\"x\", \"\")\n        invalid_names = [\n          '\"', '(', ')', ',', '/',\n          ' ', '\\r', '\\t', '\\n',\n          '{', '}',\n          (':'..'@').each,\n          ('['..']').each,\n        ].flat_map { |c| \"a#{c}b\" }\n\n        # name cannot be empty\n        invalid_names << \"\"\n\n        invalid_names.each do |invalid_name|\n          expect_raises IO::Error, \"Invalid cookie name\" do\n            cookie.name = invalid_name\n          end\n        end\n      end\n\n      it \"doesn't raise on invalid cookie with __Secure- prefix\" do\n        cookie = HTTP::Cookie.new \"x\", \"\", secure: false\n\n        cookie.name = \"__Secure-x\"\n        cookie.name.should eq \"__Secure-x\"\n        cookie.secure.should be_false\n      end\n\n      it \"doesn't raise on invalid cookie with __Host- prefix\" do\n        cookie = HTTP::Cookie.new \"x\", \"\", path: \"/foo\"\n\n        cookie.name = \"__Host-x\"\n        cookie.name.should eq \"__Host-x\"\n        cookie.secure.should be_true\n        cookie.path.should eq \"/foo\"\n        cookie.valid?.should be_false\n      end\n\n      it \"automatically configures the cookie __Secure- prefix and related properties are unset\" do\n        cookie = HTTP::Cookie.new \"x\", \"\"\n\n        cookie.name = \"__Secure-x\"\n        cookie.name.should eq \"__Secure-x\"\n        cookie.secure.should be_true\n      end\n\n      it \"automatically configures the cookie __Host- prefix and related unset properties\" do\n        cookie = HTTP::Cookie.new \"x\", \"\"\n\n        cookie.name = \"__Host-x\"\n        cookie.name.should eq \"__Host-x\"\n        cookie.secure.should be_true\n        cookie.path.should eq \"/\"\n        cookie.domain.should be_nil\n      end\n    end\n\n    describe \"#value=\" do\n      it \"raises on invalid value\" do\n        cookie = HTTP::Cookie.new(\"x\", \"\")\n\n        INVALID_COOKIE_VALUES.each do |v|\n          expect_raises IO::Error, \"Invalid cookie value\" do\n            cookie.value = \"foo#{v}bar\"\n          end\n        end\n      end\n    end\n\n    describe \"#to_set_cookie_header\" do\n      it { assert_prints HTTP::Cookie.new(\"x\", \"v$1\").to_set_cookie_header, \"x=v$1\" }\n\n      it { assert_prints HTTP::Cookie.new(\"x\", \"seven\", domain: \"127.0.0.1\").to_set_cookie_header, \"x=seven; domain=127.0.0.1\" }\n\n      it { assert_prints HTTP::Cookie.new(\"x\", \"y\", path: \"/\").to_set_cookie_header, \"x=y; path=/\" }\n      it { assert_prints HTTP::Cookie.new(\"x\", \"y\", path: \"/example\").to_set_cookie_header, \"x=y; path=/example\" }\n\n      it { assert_prints HTTP::Cookie.new(\"x\", \"expiring\", expires: Time.unix(1257894000)).to_set_cookie_header, \"x=expiring; expires=Tue, 10 Nov 2009 23:00:00 GMT\" }\n      it { assert_prints HTTP::Cookie.new(\"x\", \"expiring-1601\", expires: Time.utc(1601, 1, 1, 1, 1, 1, nanosecond: 1)).to_set_cookie_header, \"x=expiring-1601; expires=Mon, 01 Jan 1601 01:01:01 GMT\" }\n\n      it \"samesite\" do\n        assert_prints HTTP::Cookie.new(\"x\", \"samesite-default\", samesite: nil).to_set_cookie_header, \"x=samesite-default\"\n        assert_prints HTTP::Cookie.new(\"x\", \"samesite-lax\", samesite: :lax).to_set_cookie_header, \"x=samesite-lax; SameSite=Lax\"\n        assert_prints HTTP::Cookie.new(\"x\", \"samesite-strict\", samesite: :strict).to_set_cookie_header, \"x=samesite-strict; SameSite=Strict\"\n        assert_prints HTTP::Cookie.new(\"x\", \"samesite-none\", samesite: :none).to_set_cookie_header, \"x=samesite-none; SameSite=None\"\n      end\n\n      it { assert_prints HTTP::Cookie.new(\"empty-value\", \"\").to_set_cookie_header, \"empty-value=\" }\n    end\n\n    describe \"#to_s\" do\n      it \"stringifies\" do\n        HTTP::Cookie.new(\"foo\", \"bar\").to_s.should eq \"foo=bar\"\n        HTTP::Cookie.new(\"x\", \"y\", domain: \"example.com\", path: \"/foo\", expires: Time.unix(1257894000), samesite: :lax).to_s.should eq \"x=y; domain=example.com; path=/foo; expires=Tue, 10 Nov 2009 23:00:00 GMT; SameSite=Lax\"\n      end\n    end\n\n    describe \"#inspect\" do\n      it \"stringifies\" do\n        HTTP::Cookie.new(\"foo\", \"bar\").inspect.should eq %(HTTP::Cookie[\"foo=bar\"])\n        HTTP::Cookie.new(\"x\", \"y\", domain: \"example.com\", path: \"/foo\", expires: Time.unix(1257894000), samesite: :lax).inspect.should eq %(HTTP::Cookie[\"x=y; domain=example.com; path=/foo; expires=Tue, 10 Nov 2009 23:00:00 GMT; SameSite=Lax\"])\n      end\n    end\n\n    describe \"#valid? & #validate!\" do\n      it \"raises on invalid cookie with __Secure- prefix\" do\n        cookie = HTTP::Cookie.new \"x\", \"\", secure: false\n        cookie.name = \"__Secure-x\"\n\n        cookie.valid?.should be_false\n\n        expect_raises ArgumentError, \"Invalid cookie name. Has '__Secure-' prefix, but is not secure.\" do\n          cookie.validate!\n        end\n\n        cookie.secure = true\n        cookie.valid?.should be_true\n      end\n\n      it \"with a __Secure- prefix, but @secure is somehow `nil`\" do\n        cookie = HTTP::Cookie.new \"__Secure-x\", \"\"\n\n        cookie.valid?.should be_true\n\n        pointerof(cookie.@secure).value = nil\n\n        cookie.valid?.should be_false\n      end\n\n      it \"raises on invalid cookie with __Host- prefix\" do\n        cookie = HTTP::Cookie.new \"x\", \"\", domain: \"example.com\", secure: false\n        cookie.name = \"__Host-x\"\n\n        cookie.valid?.should be_false\n\n        # Not secure\n        expect_raises ArgumentError, \"Invalid cookie name. Does not meet '__Host-' prefix requirements of: secure: true, path: \\\"/\\\", domain: nil.\" do\n          cookie.validate!\n        end\n\n        cookie.secure = true\n        cookie.valid?.should be_false\n\n        # Invalid path\n        expect_raises ArgumentError, \"Invalid cookie name. Does not meet '__Host-' prefix requirements of: secure: true, path: \\\"/\\\", domain: nil.\" do\n          cookie.validate!\n        end\n\n        cookie.path = \"/\"\n        cookie.valid?.should be_false\n\n        # Has domain\n        expect_raises ArgumentError, \"Invalid cookie name. Does not meet '__Host-' prefix requirements of: secure: true, path: \\\"/\\\", domain: nil.\" do\n          cookie.validate!\n        end\n\n        cookie.domain = nil\n\n        cookie.name = \"__Host-x\"\n        cookie.name.should eq \"__Host-x\"\n        cookie.valid?.should be_true\n      end\n    end\n  end\n\n  describe Cookie::Parser do\n    describe \"parse_cookies\" do\n      it \"parses key=value\" do\n        cookie = parse_first_cookie(\"key=value\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.to_set_cookie_header.should eq(\"key=value\")\n      end\n\n      it \"parses key=\" do\n        cookie = parse_first_cookie(\"key=\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"\")\n        cookie.to_set_cookie_header.should eq(\"key=\")\n      end\n\n      it \"parses key=key=value\" do\n        cookie = parse_first_cookie(\"key=key=value\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"key=value\")\n        cookie.to_set_cookie_header.should eq(\"key=key=value\")\n      end\n\n      it \"parses key=key%3Dvalue\" do\n        cookie = parse_first_cookie(\"key=key%3Dvalue\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"key%3Dvalue\")\n        cookie.to_set_cookie_header.should eq(\"key=key%3Dvalue\")\n      end\n\n      it \"parses special character in name\" do\n        cookie = parse_first_cookie(\"key%3Dvalue=value\")\n        cookie.name.should eq(\"key%3Dvalue\")\n        cookie.value.should eq(\"value\")\n        cookie.to_set_cookie_header.should eq(\"key%3Dvalue=value\")\n      end\n\n      it %(parses key=\"value\") do\n        cookie = parse_first_cookie(%(key=\"value\"))\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.to_set_cookie_header.should eq(\"key=value\")\n      end\n\n      it \"parses multiple cookies\" do\n        cookies = Cookie::Parser.parse_cookies(\"foo=bar; foobar=baz\")\n        cookies.size.should eq(2)\n        first, second = cookies\n        first.name.should eq(\"foo\")\n        second.name.should eq(\"foobar\")\n        first.value.should eq(\"bar\")\n        second.value.should eq(\"baz\")\n      end\n\n      it \"parses cookie with spaces in value\" do\n        parse_first_cookie(%[key=some value]).value.should eq \"some value\"\n        parse_first_cookie(%[key=\"some value\"]).value.should eq \"some value\"\n      end\n\n      it \"strips spaces around value only when it's unquoted\" do\n        parse_first_cookie(%[key= some value  ]).value.should eq \"some value\"\n        parse_first_cookie(%[key=\" some value  \"]).value.should eq \" some value  \"\n        parse_first_cookie(%[key=  \" some value  \"  ]).value.should eq \" some value  \"\n      end\n    end\n\n    describe \"parse_set_cookie\" do\n      it \"with space\" do\n        cookie = parse_set_cookie(\"key=value; path=/test\")\n        parse_set_cookie(\"key=value;path=/test\").should eq cookie\n        parse_set_cookie(\"key=value;  \\t\\npath=/test\").should eq cookie\n      end\n\n      it \"parses cookie with spaces in value\" do\n        parse_set_cookie(%[key=some value]).value.should eq \"some value\"\n        parse_set_cookie(%[key=\"some value\"]).value.should eq \"some value\"\n      end\n\n      it \"removes leading and trailing whitespaces\" do\n        cookie = parse_set_cookie(%[key= \\tvalue \\t;  \\t\\npath=/test])\n        cookie.name.should eq \"key\"\n        cookie.value.should eq \"value\"\n        cookie.path.should eq \"/test\"\n\n        cookie = parse_set_cookie(%[  key\\t  =value \\n;path=/test])\n        cookie.name.should eq \"key\"\n        cookie.value.should eq \"value\"\n        cookie.path.should eq \"/test\"\n      end\n\n      it \"strips spaces around value only when it's unquoted\" do\n        cookie = parse_set_cookie(%[key= value ;  \\tpath=/test])\n        cookie.name.should eq \"key\"\n        cookie.value.should eq \"value\"\n        cookie.path.should eq \"/test\"\n\n        cookie = parse_set_cookie(%[key=\" value  \";  \\tpath=/test])\n        cookie.name.should eq \"key\"\n        cookie.value.should eq \" value  \"\n        cookie.path.should eq \"/test\"\n\n        cookie = parse_set_cookie(%[key=  \" value  \"\\t ;  \\tpath=/test])\n        cookie.name.should eq \"key\"\n        cookie.value.should eq \" value  \"\n        cookie.path.should eq \"/test\"\n      end\n\n      it \"parses path\" do\n        cookie = parse_set_cookie(\"key=value; path=/test\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.path.should eq(\"/test\")\n        cookie.to_set_cookie_header.should eq(\"key=value; path=/test\")\n      end\n\n      it \"parses Secure\" do\n        cookie = parse_set_cookie(\"key=value; Secure\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.secure.should be_true\n        cookie.to_set_cookie_header.should eq(\"key=value; Secure\")\n      end\n\n      it \"parses HttpOnly\" do\n        cookie = parse_set_cookie(\"key=value; HttpOnly\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.http_only.should be_true\n        cookie.to_set_cookie_header.should eq(\"key=value; HttpOnly\")\n      end\n\n      describe \"SameSite\" do\n        context \"Lax\" do\n          it \"parses samesite\" do\n            cookie = parse_set_cookie(\"key=value; SameSite=Lax\")\n            cookie.name.should eq \"key\"\n            cookie.value.should eq \"value\"\n            cookie.samesite.should eq HTTP::Cookie::SameSite::Lax\n            cookie.to_set_cookie_header.should eq \"key=value; SameSite=Lax\"\n          end\n        end\n\n        context \"Strict\" do\n          it \"parses samesite\" do\n            cookie = parse_set_cookie(\"key=value; SameSite=Strict\")\n            cookie.name.should eq \"key\"\n            cookie.value.should eq \"value\"\n            cookie.samesite.should eq HTTP::Cookie::SameSite::Strict\n            cookie.to_set_cookie_header.should eq \"key=value; SameSite=Strict\"\n          end\n        end\n\n        context \"Invalid\" do\n          it \"parses samesite\" do\n            cookie = parse_set_cookie(\"key=value; SameSite=Foo\")\n            cookie.name.should eq \"key\"\n            cookie.value.should eq \"value\"\n            cookie.samesite.should be_nil\n            cookie.to_set_cookie_header.should eq \"key=value\"\n          end\n        end\n      end\n\n      it \"parses domain\" do\n        cookie = parse_set_cookie(\"key=value; domain=www.example.com\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.domain.should eq(\"www.example.com\")\n        cookie.to_set_cookie_header.should eq(\"key=value; domain=www.example.com\")\n      end\n\n      it \"leading dots in domain names are ignored\" do\n        cookie = parse_set_cookie(\"key=value; domain=.example.com\")\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.domain.should eq(\"example.com\")\n        cookie.to_set_cookie_header.should eq(\"key=value; domain=example.com\")\n      end\n\n      it \"parses expires iis\" do\n        cookie = parse_set_cookie(\"key=value; expires=Sun, 06-Nov-1994 08:49:37 GMT\")\n        time = Time.utc(1994, 11, 6, 8, 49, 37)\n\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.expires.should eq(time)\n      end\n\n      it \"parses expires rfc1123\" do\n        cookie = parse_set_cookie(\"key=value; expires=Sun, 06 Nov 1994 08:49:37 GMT\")\n        time = Time.utc(1994, 11, 6, 8, 49, 37)\n\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.expires.should eq(time)\n      end\n\n      it \"parses expires rfc1036\" do\n        cookie = parse_set_cookie(\"key=value; expires=Sunday, 06-Nov-94 08:49:37 GMT\")\n        time = Time.utc(1994, 11, 6, 8, 49, 37)\n\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.expires.should eq(time)\n      end\n\n      it \"parses expires ansi c\" do\n        cookie = parse_set_cookie(\"key=value; expires=Sun Nov  6 08:49:37 1994\")\n        time = Time.utc(1994, 11, 6, 8, 49, 37)\n\n        cookie.name.should eq(\"key\")\n        cookie.value.should eq(\"value\")\n        cookie.expires.should eq(time)\n      end\n\n      it \"parses expires ansi c, variant with zone\" do\n        cookie = parse_set_cookie(\"bla=; expires=Thu, 01 Jan 1970 00:00:00 -0000\")\n        cookie.expires.should eq(Time.utc(1970, 1, 1, 0, 0, 0))\n      end\n\n      it \"parses full\" do\n        cookie = parse_set_cookie(\"key=value; path=/test; domain=www.example.com; HttpOnly; Secure; expires=Sun, 06 Nov 1994 08:49:37 GMT; SameSite=Strict\")\n        time = Time.utc(1994, 11, 6, 8, 49, 37)\n\n        cookie.name.should eq \"key\"\n        cookie.value.should eq \"value\"\n        cookie.path.should eq \"/test\"\n        cookie.domain.should eq \"www.example.com\"\n        cookie.http_only.should be_true\n        cookie.secure.should be_true\n        cookie.expires.should eq time\n        cookie.samesite.should eq HTTP::Cookie::SameSite::Strict\n      end\n\n      it \"parse domain as IP\" do\n        parse_set_cookie(\"a=1; domain=127.0.0.1; HttpOnly\").domain.should eq \"127.0.0.1\"\n      end\n\n      it \"parse max-age as Time::Span\" do\n        cookie = parse_set_cookie(\"a=1; max-age=10\")\n        cookie.max_age.should eq 10.seconds\n\n        cookie = parse_set_cookie(\"a=1; max-age=0\")\n        cookie.max_age.should eq 0.seconds\n      end\n\n      it \"parses HttpOnly with trailing semicolon\" do\n        cookie = parse_set_cookie(\"test=value; HttpOnly;\")\n        cookie.name.should eq(\"test\")\n        cookie.value.should eq(\"value\")\n        cookie.http_only.should be_true\n      end\n\n      it \"parses Secure with trailing semicolon\" do\n        cookie = parse_set_cookie(\"test=value; Secure;\")\n        cookie.name.should eq(\"test\")\n        cookie.value.should eq(\"value\")\n        cookie.secure.should be_true\n      end\n\n      it \"parses both Secure and HttpOnly with trailing semicolon\" do\n        cookie = parse_set_cookie(\"test=value; Secure; HttpOnly;\")\n        cookie.name.should eq(\"test\")\n        cookie.value.should eq(\"value\")\n        cookie.secure.should be_true\n        cookie.http_only.should be_true\n      end\n\n      it \"parses cookie with multiple trailing semicolons\" do\n        cookie = parse_set_cookie(\"test=value; HttpOnly;;\")\n        cookie.name.should eq(\"test\")\n        cookie.value.should eq(\"value\")\n        cookie.http_only.should be_true\n      end\n\n      it \"parses cookie with whitespace and trailing semicolon\" do\n        cookie = parse_set_cookie(\"test=value; HttpOnly; \")\n        cookie.name.should eq(\"test\")\n        cookie.value.should eq(\"value\")\n        cookie.http_only.should be_true\n      end\n\n      it \"parses complex cookie with all attributes and trailing semicolon\" do\n        cookie = parse_set_cookie(\"sessionid=abc123; Path=/; Domain=example.com; Expires=Wed, 09 Jun 2021 10:18:14 GMT; Secure; HttpOnly; SameSite=Strict;\")\n        cookie.name.should eq(\"sessionid\")\n        cookie.value.should eq(\"abc123\")\n        cookie.path.should eq(\"/\")\n        cookie.domain.should eq(\"example.com\")\n        cookie.secure.should be_true\n        cookie.http_only.should be_true\n        cookie.samesite.should eq(HTTP::Cookie::SameSite::Strict)\n      end\n\n      it \"parses cookie without HttpOnly but with trailing semicolon\" do\n        cookie = parse_set_cookie(\"sessionid=abc123; Path=/; Domain=example.com; Secure;\")\n        cookie.name.should eq(\"sessionid\")\n        cookie.value.should eq(\"abc123\")\n        cookie.path.should eq(\"/\")\n        cookie.domain.should eq(\"example.com\")\n        cookie.secure.should be_true\n        cookie.http_only.should be_false\n      end\n    end\n\n    describe \"expiration_time\" do\n      it \"sets expiration_time to be current when max-age=0\" do\n        cookie = parse_set_cookie(\"bla=1; max-age=0\")\n        expiration_time = cookie.expiration_time.should_not be_nil\n        expiration_time.should be_close(Time.utc, 1.seconds)\n      end\n\n      it \"sets expiration_time with old date\" do\n        cookie = parse_set_cookie(\"bla=1; expires=Thu, 01 Jan 1970 00:00:00 -0000\")\n        cookie.expiration_time.should eq Time.utc(1970, 1, 1, 0, 0, 0)\n      end\n\n      it \"sets future expiration_time with max-age\" do\n        cookie = parse_set_cookie(\"bla=1; max-age=1\")\n        cookie.expiration_time.not_nil!.should be > Time.utc\n      end\n\n      it \"sets future expiration_time with max-age and future cookie creation time\" do\n        cookie = parse_set_cookie(\"bla=1; max-age=1\")\n        cookie_expiration = cookie.expiration_time.should_not be_nil\n        cookie_expiration.should be_close(Time.utc, 1.seconds)\n\n        cookie.expired?(time_reference: cookie.creation_time + 1.second).should be_true\n      end\n\n      it \"sets future expiration_time with expires\" do\n        cookie = parse_set_cookie(\"bla=1; expires=Thu, 01 Jan 2020 00:00:00 -0000\")\n        cookie.expiration_time.should eq Time.utc(2020, 1, 1, 0, 0, 0)\n      end\n\n      it \"returns nil expiration_time when expires and max-age are not set\" do\n        cookie = parse_set_cookie(\"bla=1\")\n        cookie.expiration_time.should be_nil\n      end\n    end\n\n    describe \"expired?\" do\n      it \"expired when max-age=0\" do\n        cookie = parse_set_cookie(\"bla=1; max-age=0\")\n        cookie.expired?.should be_true\n      end\n\n      it \"expired with old expires date\" do\n        cookie = parse_set_cookie(\"bla=1; expires=Thu, 01 Jan 1970 00:00:00 -0000\")\n        cookie.expired?.should be_true\n      end\n\n      it \"not expired with future max-age\" do\n        cookie = parse_set_cookie(\"bla=1; max-age=1\")\n        cookie.expired?.should be_false\n      end\n\n      it \"not expired with future expires\" do\n        cookie = parse_set_cookie(\"bla=1; expires=Thu, 01 Jan #{Time.utc.year + 2} 00:00:00 -0000\")\n        cookie.expired?.should be_false\n      end\n\n      it \"not expired when max-age and expires are not provided\" do\n        cookie = parse_set_cookie(\"bla=1\")\n        cookie.expired?.should be_false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/cookies_spec.cr",
    "content": "require \"spec\"\nrequire \"http/cookie\"\n\nmodule HTTP\n  describe Cookies do\n    describe \".from_client_headers\" do\n      it \"parses Cookie header\" do\n        cookies = Cookies.from_client_headers Headers{\"Cookie\" => \"a=b\"}\n        cookies.should eq HTTP::Cookies{Cookie.new(\"a\", \"b\")}\n      end\n      it \"does not accept Set-Cookie header\" do\n        cookies = Cookies.from_client_headers Headers{\"Cookie\" => \"a=b\", \"Set-Cookie\" => \"x=y\"}\n        cookies.should eq HTTP::Cookies{Cookie.new(\"a\", \"b\")}\n      end\n\n      it \"chops value at the first invalid byte\" do\n        HTTP::Cookies.from_client_headers(\n          HTTP::Headers{\"Cookie\" => \"ginger=snap; cookie=hm🍪delicious; snicker=doodle\"}\n        ).should eq HTTP::Cookies{\n          HTTP::Cookie.new(\"ginger\", \"snap\"),\n          HTTP::Cookie.new(\"cookie\", \"hm\"),\n          HTTP::Cookie.new(\"snicker\", \"doodle\"),\n        }\n      end\n\n      # FIXME: Should handle both: https://github.com/crystal-lang/crystal/issues/16069\n      it \"takes last one on duplicate keys\" do\n        HTTP::Cookies.from_client_headers(\n          HTTP::Headers{\"Cookie\" => \"foo=bar; foo=baz\"}\n        ).should eq HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"baz\"),\n        }\n        HTTP::Cookies.from_client_headers(\n          HTTP::Headers{\"Cookie\" => \"foo=baz; foo=bar\"}\n        ).should eq HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"bar\"),\n        }\n      end\n    end\n\n    describe \".from_server_headers\" do\n      it \"parses Set-Cookie header\" do\n        cookies = Cookies.from_server_headers Headers{\"Set-Cookie\" => \"a=b; path=/foo\"}\n        cookies.should eq HTTP::Cookies{Cookie.new(\"a\", \"b\", path: \"/foo\")}\n      end\n      it \"does not accept Cookie header\" do\n        cookies = Cookies.from_server_headers Headers{\"Set-Cookie\" => \"a=b\", \"Cookie\" => \"x=y\"}\n        cookies.should eq HTTP::Cookies{Cookie.new(\"a\", \"b\")}\n      end\n\n      it \"drops cookies with invalid byte in value\" do\n        HTTP::Cookies.from_server_headers(\n          HTTP::Headers{\"Set-Cookie\" => [\"ginger=snap\", \"cookie=hm🍪delicious\", \"snicker=doodle\"]}\n        ).should eq HTTP::Cookies{\n          HTTP::Cookie.new(\"ginger\", \"snap\"),\n          HTTP::Cookie.new(\"snicker\", \"doodle\"),\n        }\n      end\n\n      # FIXME: Should handle both: https://github.com/crystal-lang/crystal/issues/16069\n      it \"takes last one on duplicate keys\" do\n        HTTP::Cookies.from_server_headers(\n          HTTP::Headers{\"Set-Cookie\" => [\"foo=bar\", \"foo=baz; path=/baz\"]}\n        ).should eq HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"baz\", path: \"/baz\"),\n        }\n        HTTP::Cookies.from_server_headers(\n          HTTP::Headers{\"Set-Cookie\" => [\"foo=baz; path=/baz\", \"foo=bar\"]}\n        ).should eq HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"bar\"),\n        }\n      end\n    end\n\n    describe \"mutating\" do\n      it \"allows adding cookies and retrieving\" do\n        cookies = Cookies.new\n        cookies << Cookie.new(\"a\", \"b\")\n        cookies[\"c\"] = Cookie.new(\"c\", \"d\")\n        cookies[\"d\"] = \"e\"\n\n        cookies[\"a\"].value.should eq \"b\"\n        cookies[\"c\"].value.should eq \"d\"\n        cookies[\"d\"].value.should eq \"e\"\n        cookies[\"a\"]?.should_not be_nil\n        cookies[\"e\"]?.should be_nil\n        cookies.has_key?(\"a\").should be_true\n      end\n\n      describe \"#<<\" do\n        it \"overwrites existing key\" do\n          cookies = Cookies{\"a\" => \"b\"}\n          cookies << Cookie.new(\"a\", \"c\")\n          cookies.should eq Cookies{\"a\" => \"c\"}\n        end\n\n        it \"overwrites existing key with same value\" do\n          cookies = Cookies{\"a\" => \"b\"}\n          new_cookie = Cookie.new(\"a\", \"b\", path: \"/foo\")\n          cookies << new_cookie\n          cookies.should eq Cookies{new_cookie}\n        end\n      end\n\n      describe \"#[]=\" do\n        it \"disallows adding inconsistent state\" do\n          cookies = Cookies.new\n\n          expect_raises ArgumentError do\n            cookies[\"a\"] = Cookie.new(\"b\", \"c\")\n          end\n        end\n\n        it \"overwrites existing key\" do\n          cookies = Cookies{\"a\" => \"b\"}\n          cookies[\"a\"] = \"c\"\n          cookies.should eq Cookies{\"a\" => \"c\"}\n        end\n\n        it \"overwrites existing key with same value\" do\n          cookies = Cookies{Cookie.new(\"a\", \"b\", path: \"/foo\")}\n          cookies[\"a\"] = \"b\"\n          cookies.should eq Cookies{\"a\" => \"b\"}\n        end\n      end\n    end\n\n    it \"#size\" do\n      cookies = Cookies.new\n      cookies.size.should eq 0\n      cookies << Cookie.new(\"1\", \"2\")\n      cookies.size.should eq 1\n      cookies << Cookie.new(\"3\", \"4\")\n      cookies.size.should eq 2\n    end\n\n    it \"#clear\" do\n      cookies = Cookies.new\n      cookies << Cookie.new(\"test_key\", \"test_value\")\n      cookies << Cookie.new(\"a\", \"b\")\n      cookies << Cookie.new(\"c\", \"d\")\n      cookies.clear\n      cookies.should be_empty\n    end\n\n    it \"#delete\" do\n      cookies = Cookies.new\n      cookies << Cookie.new(\"the_key\", \"the_value\")\n      cookies << Cookie.new(\"not_the_key\", \"not_the_value\")\n      cookies << Cookie.new(\"a\", \"b\")\n      cookies.has_key?(\"the_key\").should be_true\n      cookies.delete(\"the_key\").not_nil!.value.should eq \"the_value\"\n      cookies.has_key?(\"the_key\").should be_false\n      cookies.size.should eq 2\n    end\n\n    describe \"#add_request_headers\" do\n      it \"overwrites a pre-existing Cookie header\" do\n        headers = Headers.new\n        headers[\"Cookie\"] = \"some_key=some_value\"\n\n        cookies = Cookies.new\n        cookies << Cookie.new(\"a\", \"b\")\n\n        headers[\"Cookie\"].should eq \"some_key=some_value\"\n\n        cookies.add_request_headers(headers)\n\n        headers[\"Cookie\"].should eq \"a=b\"\n      end\n\n      it \"use encode_www_form to write the cookie's value\" do\n        headers = Headers.new\n        cookies = Cookies.new\n        cookies << Cookie.new(\"a\", \"b+c\")\n        cookies.add_request_headers(headers)\n        headers[\"Cookie\"].should eq \"a=b+c\"\n      end\n\n      it \"merges multiple cookies into one Cookie header\" do\n        headers = Headers.new\n        cookies = Cookies.new\n        cookies << Cookie.new(\"a\", \"b\")\n        cookies << Cookie.new(\"c\", \"d\")\n\n        cookies.add_request_headers(headers)\n\n        headers[\"Cookie\"].should eq \"a=b; c=d\"\n      end\n\n      describe \"when no cookies are set\" do\n        it \"does not set a Cookie header\" do\n          headers = Headers.new\n          headers[\"Cookie\"] = \"a=b\"\n          cookies = Cookies.new\n\n          headers[\"Cookie\"]?.should_not be_nil\n          cookies.add_request_headers(headers)\n          headers[\"Cookie\"]?.should be_nil\n        end\n      end\n    end\n\n    describe \"#add_response_headers\" do\n      it \"overwrites all pre-existing Set-Cookie headers\" do\n        headers = Headers.new\n        headers.add(\"Set-Cookie\", \"a=b\")\n        headers.add(\"Set-Cookie\", \"c=d\")\n\n        cookies = Cookies.new\n        cookies << Cookie.new(\"x\", \"y\")\n\n        headers.get(\"Set-Cookie\").size.should eq 2\n        headers.get(\"Set-Cookie\").should contain(\"a=b\")\n        headers.get(\"Set-Cookie\").should contain(\"c=d\")\n\n        cookies.add_response_headers(headers)\n\n        headers.get(\"Set-Cookie\").size.should eq 1\n        headers.get(\"Set-Cookie\")[0].should eq \"x=y\"\n      end\n\n      it \"sets one Set-Cookie header per cookie\" do\n        headers = Headers.new\n        cookies = Cookies.new\n        cookies << Cookie.new(\"a\", \"b\")\n        cookies << Cookie.new(\"c\", \"d\")\n\n        headers.get?(\"Set-Cookie\").should be_nil\n        cookies.add_response_headers(headers)\n        headers.get?(\"Set-Cookie\").should_not be_nil\n\n        headers.get(\"Set-Cookie\").should contain(\"a=b\")\n        headers.get(\"Set-Cookie\").should contain(\"c=d\")\n      end\n\n      it \"uses encode_www_form on Set-Cookie value\" do\n        headers = Headers.new\n        cookies = Cookies.new\n        cookies << Cookie.new(\"a\", \"b+c\")\n        cookies.add_response_headers(headers)\n        headers.get(\"Set-Cookie\").should contain(\"a=b+c\")\n      end\n\n      describe \"when no cookies are set\" do\n        it \"does not set a Set-Cookie header\" do\n          headers = Headers.new\n          headers.add(\"Set-Cookie\", \"a=b\")\n          cookies = Cookies.new\n\n          headers.get?(\"Set-Cookie\").should_not be_nil\n          cookies.add_response_headers(headers)\n          headers.get?(\"Set-Cookie\").should be_nil\n        end\n      end\n    end\n\n    it \"#each\" do\n      cookies = Cookies.new\n      cookies[\"a\"] = \"b\"\n      cookies.each do |cookie|\n        cookie.name.should eq \"a\"\n        cookie.value.should eq \"b\"\n      end\n\n      cookie = cookies.each.next\n      cookie.should eq Cookie.new(\"a\", \"b\")\n    end\n\n    it \"#to_h\" do\n      cookies = Cookies.new\n      cookies << Cookie.new(\"a\", \"b\")\n      cookies[\"c\"] = Cookie.new(\"c\", \"d\")\n      cookies[\"d\"] = \"e\"\n      cookies_hash = cookies.to_h\n      compare_hash = {\"a\" => Cookie.new(\"a\", \"b\"), \"c\" => Cookie.new(\"c\", \"d\"), \"d\" => Cookie.new(\"d\", \"e\")}\n      cookies_hash.should eq(compare_hash)\n      cookies[\"x\"] = \"y\"\n      cookies.to_h.should_not eq(cookies_hash)\n    end\n\n    describe \"#==\" do\n      it \"equal\" do\n        Cookies.new.should eq Cookies.new\n        Cookies{Cookie.new(\"foo\", \"bar\")}.should eq Cookies{Cookie.new(\"foo\", \"bar\")}\n        cookies = Cookies{Cookie.new(\"foo\", \"bar\"), Cookie.new(\"baz\", \"qux\")}\n        cookies.should eq cookies.dup\n      end\n\n      it \"order doesn't matter\" do\n        Cookies{Cookie.new(\"foo\", \"bar\"), Cookie.new(\"baz\", \"qux\")}.should eq Cookies{Cookie.new(\"baz\", \"qux\"), Cookie.new(\"foo\", \"bar\")}\n      end\n\n      it \"unequal\" do\n        Cookies.new.should_not eq Cookies{Cookie.new(\"foo\", \"bar\")}\n        Cookies{Cookie.new(\"foo\", \"bar\")}.should_not eq Cookies.new\n\n        Cookies{Cookie.new(\"foo\", \"bar\")}.should_not eq Cookies{Cookie.new(\"foo\", \"baz\")}\n      end\n    end\n\n    describe \"#to_s\" do\n      it \"stringifies\" do\n        cookies = HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"bar\"),\n          HTTP::Cookie.new(\"x\", \"y\", domain: \"example.com\", path: \"/foo\", expires: Time.unix(1257894000), samesite: :lax),\n        }\n\n        cookies.to_s.should eq %(HTTP::Cookies{\"foo=bar\", \"x=y; domain=example.com; path=/foo; expires=Tue, 10 Nov 2009 23:00:00 GMT; SameSite=Lax\"})\n      end\n    end\n\n    describe \"#inspect\" do\n      it \"stringifies\" do\n        cookies = HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"bar\"),\n          HTTP::Cookie.new(\"x\", \"y\", domain: \"example.com\", path: \"/foo\", expires: Time.unix(1257894000), samesite: :lax),\n        }\n\n        cookies.inspect.should eq %(HTTP::Cookies{\"foo=bar\", \"x=y; domain=example.com; path=/foo; expires=Tue, 10 Nov 2009 23:00:00 GMT; SameSite=Lax\"})\n      end\n    end\n\n    describe \"#pretty_print\" do\n      it \"stringifies\" do\n        cookies = HTTP::Cookies{\n          HTTP::Cookie.new(\"foo\", \"bar\"),\n          HTTP::Cookie.new(\"x\", \"y\", domain: \"example.com\", path: \"/foo\", expires: Time.unix(1257894000), samesite: :lax),\n        }\n        cookies.pretty_inspect.should eq <<-CRYSTAL\n          HTTP::Cookies{\"foo=bar\",\n           \"x=y; domain=example.com; path=/foo; expires=Tue, 10 Nov 2009 23:00:00 GMT; SameSite=Lax\"}\n          CRYSTAL\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/formdata/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"http/formdata\"\nrequire \"http/server/response\"\n\ndescribe HTTP::FormData::Builder do\n  it \"builds valid form-data messages\" do\n    io = IO::Memory.new\n    HTTP::FormData.build(io, \"fixed-boundary\") do |g|\n      g.field(\"foo\", \"bar\")\n      g.field(\"baz\", \"qux\", HTTP::Headers{\"X-Testing\" => \"headers\"})\n\n      body = IO::Memory.new \"file content\"\n      time = Time.utc(2016, 1, 1, 12, 0, 0)\n      metadata = HTTP::FormData::FileMetadata.new(\"filename.txt \\\"\", time, time, time, 12_u64)\n      headers = HTTP::Headers{\"Foo\" => \"Bar\", \"Baz\" => \"Qux\"}\n      g.file(\"file-test\", body, metadata, headers)\n    end\n\n    generated = io.to_s\n    expected = <<-'MULTIPART'\n      --fixed-boundary\n      Content-Disposition: form-data; name=\"foo\"\n\n      bar\n      --fixed-boundary\n      X-Testing: headers\n      Content-Disposition: form-data; name=\"baz\"\n\n      qux\n      --fixed-boundary\n      Foo: Bar\n      Baz: Qux\n      Content-Disposition: form-data; name=\"file-test\"; filename=\"filename.txt\\ \\\"\"; creation-date=\"Fri, 01 Jan 2016 12:00:00 +0000\"; modification-date=\"Fri, 01 Jan 2016 12:00:00 +0000\"; read-date=\"Fri, 01 Jan 2016 12:00:00 +0000\"; size=12\n\n      file content\n      --fixed-boundary--\n      MULTIPART\n\n    generated.should eq(expected.gsub('\\n', \"\\r\\n\"))\n  end\n\n  describe \"#field\" do\n    it \"converts value to a string\" do\n      io = IO::Memory.new\n      HTTP::FormData.build(io, \"fixed-boundary\") do |g|\n        g.field(\"foo\", 12)\n      end\n\n      generated = io.to_s\n      expected = <<-'MULTIPART'\n        --fixed-boundary\n        Content-Disposition: form-data; name=\"foo\"\n\n        12\n        --fixed-boundary--\n        MULTIPART\n\n      generated.should eq(expected.gsub('\\n', \"\\r\\n\"))\n    end\n  end\n\n  describe \"#content_type\" do\n    it \"calculates the content type\" do\n      builder = HTTP::FormData::Builder.new(IO::Memory.new, \"a delimiter string with a quote in \\\"\")\n      builder.content_type.should eq(%q(multipart/form-data; boundary=\"a\\ delimiter\\ string\\ with\\ a\\ quote\\ in\\ \\\"\"))\n    end\n  end\n\n  describe \"#file\" do\n    it \"fails after finish\" do\n      builder = HTTP::FormData::Builder.new(IO::Memory.new)\n      builder.field(\"foo\", \"bar\")\n      builder.finish\n      expect_raises(HTTP::FormData::Error, \"Cannot add form part: already finished\") do\n        builder.field(\"foo\", \"bar\")\n      end\n    end\n  end\n\n  describe \"#finish\" do\n    it \"fails after finish\" do\n      builder = HTTP::FormData::Builder.new(IO::Memory.new)\n      builder.field(\"foo\", \"bar\")\n      builder.finish\n      expect_raises(HTTP::FormData::Error, \"Cannot finish form-data: already finished\") do\n        builder.finish\n      end\n    end\n\n    it \"fails when no body parts\" do\n      builder = HTTP::FormData::Builder.new(IO::Memory.new)\n      expect_raises(HTTP::FormData::Error, \"Cannot finish form-data: no body parts\") do\n        builder.finish\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/formdata/parser_spec.cr",
    "content": "require \"spec\"\nrequire \"http/formdata\"\n\ndescribe HTTP::FormData::Parser do\n  it \"parses formdata\" do\n    formdata = <<-FORMDATA\n      -----------------------------735323031399963166993862150\n      Content-Disposition: form-data; name=\"text\"\n\n      text\n      -----------------------------735323031399963166993862150\n      Content-Disposition: form-data; name=\"file\"; filename=\"a.txt\"\n      Content-Type: text/plain\n\n      Content of a.txt.\n\n      -----------------------------735323031399963166993862150\n      Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\"\n      Content-Type: text/html\n\n      <!DOCTYPE html><title>Content of a.html.</title>\n\n      -----------------------------735323031399963166993862150--\n      FORMDATA\n\n    parser = HTTP::FormData::Parser.new IO::Memory.new(formdata.gsub('\\n', \"\\r\\n\")), \"---------------------------735323031399963166993862150\"\n\n    runs = 0\n    while parser.has_next?\n      parser.next do |part|\n        case part.name\n        when \"text\"\n          part.body.gets_to_end.should eq(\"text\")\n          runs += 1\n        when \"file\"\n          part.body.gets_to_end.should eq(\"Content of a.txt.\\r\\n\")\n          part.filename.should eq(\"a.txt\")\n          part.headers[\"Content-Type\"].should eq(\"text/plain\")\n          runs += 1\n        when \"file2\"\n          part.body.gets_to_end.should eq(\"<!DOCTYPE html><title>Content of a.html.</title>\\r\\n\")\n          part.filename.should eq(\"a.html\")\n          part.headers[\"Content-Type\"].should eq(\"text/html\")\n          runs += 1\n        else\n          raise \"extra field\"\n        end\n      end\n    end\n    runs.should eq(3)\n  end\nend\n"
  },
  {
    "path": "spec/std/http/formdata_spec.cr",
    "content": "require \"spec\"\nrequire \"http/formdata\"\nrequire \"http/server/response\"\n\ndescribe HTTP::FormData do\n  describe \".parse(IO, String)\" do\n    it \"parses formdata\" do\n      formdata = <<-FORMDATA\n        --foo\n        Content-Disposition: form-data; name=\"foo\"\n\n        bar\n        --foo--\n        FORMDATA\n\n      res = nil\n      HTTP::FormData.parse(IO::Memory.new(formdata.gsub('\\n', \"\\r\\n\")), \"foo\") do |part|\n        res = part.body.gets_to_end if part.name == \"foo\"\n      end\n      res.should eq(\"bar\")\n    end\n  end\n\n  describe \".parse(HTTP::Request)\" do\n    it \"parses formdata\" do\n      formdata = <<-FORMDATA\n        --foo\n        Content-Disposition: form-data; name=\"foo\"\n\n        bar\n        --foo--\n        FORMDATA\n      headers = HTTP::Headers{\"Content-Type\" => \"multipart/form-data; boundary=foo\"}\n      request = HTTP::Request.new(\"GET\", \"/\", headers, formdata.gsub('\\n', \"\\r\\n\"))\n\n      res = nil\n      HTTP::FormData.parse(request) do |part|\n        res = part.body.gets_to_end if part.name == \"foo\"\n      end\n      res.should eq(\"bar\")\n    end\n\n    it \"raises on empty body\" do\n      headers = HTTP::Headers{\"Content-Type\" => \"multipart/form-data; boundary=foo\"}\n      req = HTTP::Request.new(\"GET\", \"/\", headers)\n      expect_raises(HTTP::FormData::Error, \"body is empty\") do\n        HTTP::FormData.parse(req) { }\n      end\n    end\n\n    it \"raises on no Content-Type\" do\n      req = HTTP::Request.new(\"GET\", \"/\", body: \"\")\n      expect_raises(HTTP::FormData::Error, \"could not find boundary in Content-Type\") do\n        HTTP::FormData.parse(req) { }\n      end\n    end\n\n    it \"raises on invalid Content-Type\" do\n      headers = HTTP::Headers{\"Content-Type\" => \"multipart/form-data; boundary=\"}\n      req = HTTP::Request.new(\"GET\", \"/\", headers, body: \"\")\n      expect_raises(HTTP::FormData::Error, \"could not find boundary in Content-Type\") do\n        HTTP::FormData.parse(req) { }\n      end\n    end\n  end\n\n  describe \".parse_content_disposition(String)\" do\n    it \"parses all Content-Disposition fields\" do\n      name, meta = HTTP::FormData.parse_content_disposition %q(form-data; name=foo; filename=\"foo\\\"\\\\bar\\ baz\\\\\"; creation-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"; modification-date=\"12 Feb 1997 16:29:51 -0500\"; read-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"; size=432334)\n\n      name.should eq(\"foo\")\n      meta.filename.should eq(%q(foo\"\\bar baz\\))\n      meta.creation_time.should eq(Time.utc(1997, 2, 12, 21, 29, 51, nanosecond: 0))\n      meta.modification_time.should eq(Time.utc(1997, 2, 12, 21, 29, 51, nanosecond: 0))\n      meta.read_time.should eq(Time.utc(1997, 2, 12, 21, 29, 51, nanosecond: 0))\n      meta.size.should eq(432334)\n    end\n  end\n\n  describe \".build(IO, String)\" do\n    it \"builds a message\" do\n      io = IO::Memory.new\n      HTTP::FormData.build(io, \"boundary\") do |g|\n        g.field(\"foo\", \"bar\")\n      end\n\n      expected = <<-MULTIPART\n        --boundary\n        Content-Disposition: form-data; name=\"foo\"\n\n        bar\n        --boundary--\n        MULTIPART\n      io.to_s.should eq(expected.gsub('\\n', \"\\r\\n\"))\n    end\n  end\n\n  describe \".build(HTTP::Server::Response, String)\" do\n    it \"builds a message\" do\n      io = IO::Memory.new\n      response = HTTP::Server::Response.new(io)\n      HTTP::FormData.build(response, \"boundary\") do |g|\n        g.field(\"foo\", \"bar\")\n      end\n      response.close\n\n      expected = <<-MULTIPART\n        HTTP/1.1 200 OK\n        Content-Type: multipart/form-data; boundary=\"boundary\"\n        Content-Length: 75\n\n        --boundary\n        Content-Disposition: form-data; name=\"foo\"\n\n        bar\n        --boundary--\n        MULTIPART\n\n      io.to_s.should eq(expected.gsub('\\n', \"\\r\\n\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/headers_spec.cr",
    "content": "require \"spec\"\nrequire \"http/headers\"\n\ndescribe HTTP::Headers do\n  it \"is empty\" do\n    headers = HTTP::Headers.new\n    headers.empty?.should be_true\n  end\n\n  it \"is case insensitive\" do\n    headers = HTTP::Headers{\"Foo\" => \"bar\"}\n    headers[\"foo\"].should eq(\"bar\")\n  end\n\n  it \"it allows indifferent access for underscore and dash separated keys\" do\n    headers = HTTP::Headers{\"foo_Bar\" => \"bar\", \"Foobar-foo\" => \"baz\"}\n    headers[\"foo-bar\"].should eq(\"bar\")\n    headers[\"foobar_foo\"].should eq(\"baz\")\n  end\n\n  it \"raises an error if header value contains invalid character\" do\n    expect_raises ArgumentError do\n      HTTP::Headers{\"invalid-header\" => \"\\r\\nLocation: http://example.com\"}\n    end\n  end\n\n  it \"should retain the input casing\" do\n    headers = HTTP::Headers{\"FOO_BAR\" => \"bar\", \"Foobar-foo\" => \"baz\"}\n    serialized = String.build do |io|\n      headers.each do |name, values|\n        io << name << \": \" << values.first << ';'\n      end\n    end\n\n    serialized.should eq(\"FOO_BAR: bar;Foobar-foo: baz;\")\n  end\n\n  it \"is gets with []?\" do\n    headers = HTTP::Headers.new\n    headers[\"foo\"]?.should be_nil\n\n    headers[\"Foo\"] = \"bar\"\n    headers[\"foo\"]?.should eq(\"bar\")\n  end\n\n  it \"fetches with default value\" do\n    headers = HTTP::Headers.new\n    headers.fetch(\"foo\", \"baz\").should eq(\"baz\")\n\n    headers[\"Foo\"] = \"bar\"\n    headers.fetch(\"foo\", \"baz\").should eq(\"bar\")\n  end\n\n  it \"fetches with block\" do\n    headers = HTTP::Headers.new\n    headers.fetch(\"foo\") { |k| \"#{k}baz\" }.should eq(\"foobaz\")\n\n    headers[\"Foo\"] = \"bar\"\n    headers.fetch(\"foo\") { \"baz\" }.should eq(\"bar\")\n  end\n\n  it \"has key\" do\n    headers = HTTP::Headers{\"Foo\" => \"bar\"}\n    headers.has_key?(\"foo\").should be_true\n    headers.has_key?(\"bar\").should be_false\n  end\n\n  it \"deletes\" do\n    headers = HTTP::Headers{\"Foo\" => \"bar\"}\n    headers.delete(\"foo\").should eq(\"bar\")\n    headers.should be_empty\n  end\n\n  describe \"#==\" do\n    it \"equals other instance\" do\n      a = HTTP::Headers{\"Foo\" => \"bar\"}\n      b = HTTP::Headers{\"Foo\" => \"bar\"}\n      c = HTTP::Headers{\"Foo\" => \"baz\"}\n      a.should eq b\n      a.hash.should eq b.hash\n      a.should_not eq c\n      a.hash.should_not eq c.hash\n    end\n\n    it \"case-insensitive keys\" do\n      a = HTTP::Headers{\"Foo\" => \"bar\"}\n      b = HTTP::Headers{\"foo\" => \"bar\"}\n      c = HTTP::Headers{\"voo\" => \"bar\"}\n      a.should eq b\n      a.hash.should eq b.hash\n      a.should_not eq c\n      a.hash.should_not eq c.hash\n    end\n\n    it \"different internal representation\" do\n      a = HTTP::Headers{\"Foo\" => \"bar\"}\n      b = HTTP::Headers{\"Foo\" => [\"bar\"]}\n      c = HTTP::Headers{\"Foo\" => [\"bar\", \"baz\"]}\n      a.should eq b\n      a.hash.should eq b.hash\n      a.should_not eq c\n      a.hash.should_not eq c.hash\n    end\n  end\n\n  it \"dups\" do\n    headers = HTTP::Headers{\"Foo\" => \"bar\"}\n    other = headers.dup\n    other.should be_a(HTTP::Headers)\n    other[\"foo\"].should eq(\"bar\")\n\n    other[\"Baz\"] = \"Qux\"\n    headers[\"baz\"]?.should be_nil\n  end\n\n  it \"clones\" do\n    headers = HTTP::Headers{\"Foo\" => \"bar\"}\n    other = headers.clone\n    other.should be_a(HTTP::Headers)\n    other[\"foo\"].should eq(\"bar\")\n\n    other[\"Baz\"] = \"Qux\"\n    headers[\"baz\"]?.should be_nil\n  end\n\n  it \"adds string\" do\n    headers = HTTP::Headers.new\n    headers.add(\"foo\", \"bar\")\n    headers.add(\"foo\", \"baz\")\n    headers[\"foo\"].should eq(\"bar,baz\")\n  end\n\n  it \"adds array of string\" do\n    headers = HTTP::Headers.new\n    headers.add(\"foo\", \"bar\")\n    headers.add(\"foo\", [\"baz\", \"qux\"])\n    headers[\"foo\"].should eq(\"bar,baz,qux\")\n  end\n\n  it \"gets all values\" do\n    headers = HTTP::Headers{\"foo\" => \"bar\"}\n    headers.get(\"foo\").should eq([\"bar\"])\n\n    headers.get?(\"foo\").should eq([\"bar\"])\n    headers.get?(\"qux\").should be_nil\n  end\n\n  it \"does to_s\" do\n    headers = HTTP::Headers{\"Foo_quux\" => \"bar\", \"Baz-Quux\" => [\"a\", \"b\"]}\n    headers.to_s.should eq(%(HTTP::Headers{\"Foo_quux\" => \"bar\", \"Baz-Quux\" => [\"a\", \"b\"]}))\n  end\n\n  it \"#serialize\" do\n    headers = HTTP::Headers{\"Foo_quux\" => \"bar\", \"Baz-Quux\" => [\"a\", \"b\"]}\n    headers.serialize.should eq(\"Foo_quux: bar\\r\\nBaz-Quux: a\\r\\nBaz-Quux: b\\r\\n\")\n  end\n\n  describe \"#merge!\" do\n    it \"merges and return self\" do\n      headers = HTTP::Headers.new\n      headers.should be headers.merge!({\"foo\" => \"bar\"})\n    end\n\n    it \"merges other headers\" do\n      headers = HTTP::Headers{\"foo\" => \"bar\", \"boo\" => \"baz\"}\n      headers.merge!(HTTP::Headers{\"foo\" => \"baz\", \"qux\" => \"quux\"})\n      headers.should eq HTTP::Headers{\"foo\" => \"baz\", \"boo\" => \"baz\", \"qux\" => \"quux\"}\n    end\n\n    it \"merges other hash\" do\n      headers = HTTP::Headers{\"foo\" => \"bar\", \"boo\" => \"baz\"}\n      headers.merge!({\"foo\" => \"baz\", \"qux\" => \"quux\"})\n      headers.should eq HTTP::Headers{\"foo\" => \"baz\", \"boo\" => \"baz\", \"qux\" => \"quux\"}\n    end\n\n    it \"raises an error if header value contains invalid character\" do\n      headers = HTTP::Headers.new\n      expect_raises ArgumentError do\n        headers.merge!({\"invalid-header\" => \"\\r\\nLocation: http://example.com\"})\n      end\n    end\n  end\n\n  it \"dispatch with union type (#16622)\" do\n    headers = HTTP::Headers.new\n    headers.merge!(HTTP::Headers.new)\n    headers[\"foo\"] = \"bar\".as(String | Array(String))\n  end\n\n  it \"matches word\" do\n    headers = HTTP::Headers{\"foo\" => \"bar\"}\n    headers.includes_word?(\"foo\", \"bar\").should be_true\n    headers.includes_word?(\"foo\", \"ba\").should be_false\n    headers.includes_word?(\"foo\", \"ar\").should be_false\n  end\n\n  it \"matches word with comma separated value\" do\n    headers = HTTP::Headers{\"foo\" => \"bar, baz\"}\n    headers.includes_word?(\"foo\", \"bar\").should be_true\n    headers.includes_word?(\"foo\", \"baz\").should be_true\n    headers.includes_word?(\"foo\", \"ba\").should be_false\n  end\n\n  it \"matches word with comma separated value, case insensitive (#3626)\" do\n    headers = HTTP::Headers{\"foo\" => \"BaR, BAZ\"}\n    headers.includes_word?(\"foo\", \"bar\").should be_true\n    headers.includes_word?(\"foo\", \"baz\").should be_true\n    headers.includes_word?(\"foo\", \"BAR\").should be_true\n    headers.includes_word?(\"foo\", \"ba\").should be_false\n  end\n\n  it \"doesn't match empty string\" do\n    headers = HTTP::Headers{\"foo\" => \"bar, baz\"}\n    headers.includes_word?(\"foo\", \"\").should be_false\n  end\n\n  it \"matches word with comma separated value, partial match\" do\n    headers = HTTP::Headers{\"foo\" => \"bar, bazo, baz\"}\n    headers.includes_word?(\"foo\", \"baz\").should be_true\n  end\n\n  it \"doesn't match word with comma separated value, partial match\" do\n    headers = HTTP::Headers{\"foo\" => \"bar, bazo\"}\n    headers.includes_word?(\"foo\", \"baz\").should be_false\n  end\n\n  it \"matches word with comma separated value, partial match (array)\" do\n    headers = HTTP::Headers{\"foo\" => [\"foo\", \"baz, bazo\"]}\n    headers.includes_word?(\"foo\", \"baz\").should be_true\n  end\n\n  it \"doesn't match word with comma separated value, partial match (array)\" do\n    headers = HTTP::Headers{\"foo\" => [\"foo\", \"bar, bazo\"]}\n    headers.includes_word?(\"foo\", \"baz\").should be_false\n  end\n\n  it \"matches word among headers\" do\n    headers = HTTP::Headers.new\n    headers.add(\"foo\", \"bar\")\n    headers.add(\"foo\", \"baz\")\n    headers.includes_word?(\"foo\", \"bar\").should be_true\n    headers.includes_word?(\"foo\", \"baz\").should be_true\n  end\n\n  it \"does not matches word if missing header\" do\n    headers = HTTP::Headers.new\n    headers.includes_word?(\"foo\", \"bar\").should be_false\n    headers.includes_word?(\"foo\", \"\").should be_false\n  end\n\n  it \"can create header value with all US-ASCII visible chars (#2999)\" do\n    headers = HTTP::Headers.new\n    value = (32..126).map(&.chr).join\n    headers.add(\"foo\", value)\n  end\n\n  it \"validates content\" do\n    headers = HTTP::Headers.new\n    valid_value = \"foo\"\n    invalid_value = \"\\r\\nLocation: http://example.com\"\n    headers.valid_value?(valid_value).should be_true\n    headers.valid_value?(invalid_value).should be_false\n    headers.add?(\"foo\", valid_value).should be_true\n    headers.add?(\"foo\", [valid_value]).should be_true\n    headers.add?(\"foobar\", invalid_value).should be_false\n    headers.add?(\"foobar\", [invalid_value]).should be_false\n  end\nend\n"
  },
  {
    "path": "spec/std/http/http_spec.cr",
    "content": "require \"spec\"\nrequire \"http\"\nrequire \"spec/helpers/string\"\n\nprivate def http_quote_string(io : IO, string)\n  HTTP.quote_string(string, io)\nend\n\nprivate def http_quote_string(string)\n  HTTP.quote_string(string)\nend\n\ndescribe HTTP do\n  describe \".parse_time\" do\n    it \"parses RFC 1123\" do\n      time = Time.utc(1994, 11, 6, 8, 49, 37)\n      HTTP.parse_time(\"Sun, 06 Nov 1994 08:49:37 GMT\").should eq(time)\n    end\n\n    it \"parses RFC 1123 without day name\" do\n      time = Time.utc(1994, 11, 6, 8, 49, 37)\n      HTTP.parse_time(\"06 Nov 1994 08:49:37 GMT\").should eq(time)\n    end\n\n    it \"parses RFC 1036\" do\n      time = Time.utc(1994, 11, 6, 8, 49, 37)\n      HTTP.parse_time(\"Sunday, 06-Nov-94 08:49:37 GMT\").should eq(time)\n    end\n\n    it \"parses ANSI C\" do\n      time = Time.utc(1994, 11, 6, 8, 49, 37)\n      HTTP.parse_time(\"Sun Nov  6 08:49:37 1994\").should eq(time)\n      time2 = Time.utc(1994, 11, 16, 8, 49, 37)\n      HTTP.parse_time(\"Sun Nov 16 08:49:37 1994\").should eq(time2)\n    end\n\n    it \"parses and is UTC (#2744)\" do\n      date = \"Mon, 09 Sep 2011 23:36:00 GMT\"\n      parsed_time = HTTP.parse_time(date).not_nil!\n      parsed_time.utc?.should be_true\n    end\n\n    it \"parses and is local (#2744)\" do\n      date = \"Mon, 09 Sep 2011 23:36:00 -0300\"\n      parsed_time = HTTP.parse_time(date).not_nil!\n      parsed_time.offset.should eq -3 * 3600\n      parsed_time.to_utc.to_s.should eq(\"2011-09-10 02:36:00 UTC\")\n    end\n\n    it \"handles errors\" do\n      HTTP.parse_time(\"Thu\").should be_nil\n    end\n  end\n\n  describe \"generates HTTP date\" do\n    it \"without time zone\" do\n      time = Time.utc(1994, 11, 6, 8, 49, 37, nanosecond: 0)\n      HTTP.format_time(time).should eq(\"Sun, 06 Nov 1994 08:49:37 GMT\")\n    end\n\n    it \"with local time zone\" do\n      time = Time.local(1994, 11, 6, 8, 49, 37, nanosecond: 0, location: Time::Location.fixed(3600))\n      HTTP.format_time(time).should eq(time.to_utc.to_s(\"%a, %d %b %Y %H:%M:%S GMT\"))\n    end\n  end\n\n  describe \".dequote_string\" do\n    it \"dequotes a string\" do\n      HTTP.dequote_string(%q(foo\\\"\\\\bar\\ baz\\\\)).should eq(%q(foo\"\\bar baz\\))\n    end\n  end\n\n  describe \".quote_string\" do\n    it \"quotes a string\" do\n      assert_prints http_quote_string(\"foo!#():;?~\"), \"foo!#():;?~\"\n      assert_prints http_quote_string(%q(foo\"bar\\baz)), %q(foo\\\"bar\\\\baz)\n      assert_prints http_quote_string(\"\\t \"), \"\\\\\\t\\\\ \"\n      assert_prints http_quote_string(\"it works 😂😂😂👌👌👌😂😂😂\"), \"it\\\\ works\\\\ 😂😂😂👌👌👌😂😂😂\"\n    end\n\n    it \"raises on invalid characters\" do\n      expect_raises(ArgumentError, \"String contained invalid character\") do\n        HTTP.quote_string(\"foo\\0bar\")\n      end\n\n      expect_raises(ArgumentError, \"String contained invalid character\") do\n        HTTP.quote_string(\"foo\\u{1B}bar\")\n      end\n\n      expect_raises(ArgumentError, \"String contained invalid character\") do\n        HTTP.quote_string(\"foo\\u{7F}bar\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/params_spec.cr",
    "content": "require \"http/params\"\nrequire \"spec\"\n\ndescribe \"HTTP::Params\" do\n  it \"is alias for URI::Params\" do\n    HTTP::Params.should eq URI::Params\n  end\nend\n"
  },
  {
    "path": "spec/std/http/request_spec.cr",
    "content": "require \"spec\"\nrequire \"http/request\"\n\nprivate class EmptyIO < IO\n  def read(slice : Bytes)\n    0\n  end\n\n  def write(slice : Bytes) : Nil\n  end\nend\n\nmodule HTTP\n  describe Request do\n    it \"serialize GET\" do\n      headers = HTTP::Headers.new\n      headers[\"Host\"] = \"host.example.org\"\n      original_headers = headers.dup\n      request = Request.new \"GET\", \"/\", headers\n\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\n\\r\\n\")\n      headers.should eq(original_headers)\n    end\n\n    it \"serialize GET (with query params)\" do\n      headers = HTTP::Headers.new\n      headers[\"Host\"] = \"host.example.org\"\n      original_headers = headers.dup\n      request = Request.new \"GET\", \"/greet?q=hello&name=world\", headers\n\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"GET /greet?q=hello&name=world HTTP/1.1\\r\\nHost: host.example.org\\r\\n\\r\\n\")\n      headers.should eq(original_headers)\n    end\n\n    it \"serialize GET (with cookie)\" do\n      headers = HTTP::Headers.new\n      headers[\"Host\"] = \"host.example.org\"\n      original_headers = headers.dup\n      request = Request.new \"GET\", \"/\", headers\n      request.cookies << Cookie.new(\"foo\", \"bar\")\n\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\nCookie: foo=bar\\r\\n\\r\\n\")\n      headers.should eq(original_headers)\n    end\n\n    it \"serialize GET (with cookies, from headers)\" do\n      headers = HTTP::Headers.new\n      headers[\"Host\"] = \"host.example.org\"\n      headers[\"Cookie\"] = \"foo=bar\"\n      original_headers = headers.dup\n\n      request = Request.new \"GET\", \"/\", headers\n\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\nCookie: foo=bar\\r\\n\\r\\n\")\n\n      request.cookies[\"foo\"].value.should eq \"bar\" # Force lazy initialization\n\n      io.clear\n      request.to_io(io)\n      io.to_s.should eq(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\nCookie: foo=bar\\r\\n\\r\\n\")\n\n      request.cookies[\"foo\"] = \"baz\"\n      request.cookies[\"quux\"] = \"baz\"\n\n      io.clear\n      request.to_io(io)\n      io.to_s.should eq(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\nCookie: foo=baz; quux=baz\\r\\n\\r\\n\")\n      headers.should eq(original_headers)\n    end\n\n    it \"serialize POST (with body)\" do\n      request = Request.new \"POST\", \"/\", body: \"thisisthebody\"\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"POST / HTTP/1.1\\r\\nContent-Length: 13\\r\\n\\r\\nthisisthebody\")\n    end\n\n    it \"serialize POST (with bytes body)\" do\n      request = Request.new \"POST\", \"/\", body: Bytes['a'.ord, 'b'.ord]\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"POST / HTTP/1.1\\r\\nContent-Length: 2\\r\\n\\r\\nab\")\n    end\n\n    it \"serialize POST (with io body, without content-length header)\" do\n      request = Request.new \"POST\", \"/\", body: IO::Memory.new(\"thisisthebody\")\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"POST / HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\nd\\r\\nthisisthebody\\r\\n0\\r\\n\\r\\n\")\n    end\n\n    it \"serialize POST (with io body, with content-length header)\" do\n      string = \"thisisthebody\"\n      request = Request.new \"POST\", \"/\", body: IO::Memory.new(string)\n      request.content_length = string.bytesize\n      io = IO::Memory.new\n      request.to_io(io)\n      io.to_s.should eq(\"POST / HTTP/1.1\\r\\nContent-Length: 13\\r\\n\\r\\nthisisthebody\")\n    end\n\n    it \"raises if serializing POST body with incorrect content-length (less then real)\" do\n      string = \"thisisthebody\"\n      request = Request.new \"POST\", \"/\", body: IO::Memory.new(string)\n      request.content_length = string.bytesize - 1\n      io = IO::Memory.new\n      expect_raises(ArgumentError) do\n        request.to_io(io)\n      end\n    end\n\n    it \"raises if serializing POST body with incorrect content-length (more then real)\" do\n      string = \"thisisthebody\"\n      request = Request.new \"POST\", \"/\", body: IO::Memory.new(string)\n      request.content_length = string.bytesize + 1\n      io = IO::Memory.new\n      expect_raises(ArgumentError) do\n        request.to_io(io)\n      end\n    end\n\n    describe \".from_io\" do\n      it \"parses GET\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\n\\r\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/\")\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\"})\n      end\n\n      it \"parses GET (just \\\\n instead of \\\\r\\\\n)\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\nHost: host.example.org\\n\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/\")\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\"})\n      end\n\n      it \"parses GET with query params\" do\n        request = Request.from_io(IO::Memory.new(\"GET /greet?q=hello&name=world HTTP/1.1\\r\\nHost: host.example.org\\r\\n\\r\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/greet\")\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\"})\n      end\n\n      it \"parses GET without \\\\r\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\nHost: host.example.org\\n\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/\")\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\"})\n      end\n\n      it \"parses empty string (EOF), returns nil\" do\n        Request.from_io(IO::Memory.new(\"\")).should be_nil\n      end\n\n      it \"parses empty string (EOF), returns nil (no peek)\" do\n        Request.from_io(EmptyIO.new).should be_nil\n      end\n\n      it \"parses GET with spaces in request line\" do\n        request = Request.from_io(IO::Memory.new(\"GET   /   HTTP/1.1  \\r\\nHost: host.example.org\\r\\n\\r\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/\")\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\"})\n      end\n\n      it \"parses empty header\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\nReferer:\\r\\n\\r\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/\")\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\", \"Referer\" => \"\"})\n      end\n\n      it \"parses GET with cookie\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\nCookie: a=b\\r\\n\\r\\n\")).as(Request)\n        request.method.should eq(\"GET\")\n        request.path.should eq(\"/\")\n        request.cookies[\"a\"].value.should eq(\"b\")\n\n        # Headers should not be modified (#2920)\n        request.headers.should eq(HTTP::Headers{\"Host\" => \"host.example.org\", \"Cookie\" => \"a=b\"})\n      end\n\n      it \"headers are case insensitive\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nHost: host.example.org\\r\\n\\r\\n\")).as(Request)\n        headers = request.headers.not_nil!\n        headers[\"HOST\"].should eq(\"host.example.org\")\n        headers[\"host\"].should eq(\"host.example.org\")\n        headers[\"Host\"].should eq(\"host.example.org\")\n      end\n\n      it \"parses POST (with body)\" do\n        request = Request.from_io(IO::Memory.new(\"POST /foo HTTP/1.1\\r\\nContent-Length: 13\\r\\n\\r\\nthisisthebody\")).as(Request)\n        request.method.should eq(\"POST\")\n        request.path.should eq(\"/foo\")\n        request.headers.should eq(HTTP::Headers{\"Content-Length\" => \"13\"})\n        request.body.not_nil!.gets_to_end.should eq(\"thisisthebody\")\n      end\n\n      it \"handles malformed request\" do\n        request = Request.from_io(IO::Memory.new(\"nonsense\"))\n        request.should eq HTTP::Status::BAD_REQUEST\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nX-Test-Header: \\u{0}\\r\\n\"))\n        request.should eq HTTP::Status::BAD_REQUEST\n      end\n\n      it \"handles unsupported HTTP version\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.2\\r\\nContent-Length: 0\\r\\n\\r\\n\"))\n        request.should eq HTTP::Status::BAD_REQUEST\n      end\n\n      it \"stores normalized case for common header name (lowercase) (#8060)\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\ncontent-type: foo\\r\\n\\r\\n\")).as(Request)\n        request.headers.to_s.should eq(%(HTTP::Headers{\"content-type\" => \"foo\"}))\n      end\n\n      it \"stores normalized case for common header name (capitalized) (#8060)\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nContent-Type: foo\\r\\n\\r\\n\")).as(Request)\n        request.headers.to_s.should eq(%(HTTP::Headers{\"Content-Type\" => \"foo\"}))\n      end\n\n      it \"stores normalized case for common header name (mixed) (#8060)\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nContent-type: foo\\r\\n\\r\\n\")).as(Request)\n        request.headers.to_s.should eq(%(HTTP::Headers{\"Content-type\" => \"foo\"}))\n      end\n\n      describe \"long request lines\" do\n        it \"handles long URI\" do\n          path = \"a\" * 8177\n          request = Request.from_io(IO::Memory.new(\"GET /#{path} HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n          request.path.count('a').should eq 8177\n        end\n\n        it \"fails for too-long URI\" do\n          request = Request.from_io(IO::Memory.new(\"GET /#{\"a\" * 8192} HTTP/1.1\\r\\n\\r\\n\"))\n          request.should eq HTTP::Status::URI_TOO_LONG\n        end\n\n        it \"handles long URI with custom size\" do\n          request = Request.from_io(IO::Memory.new(\"GET /12345 HTTP/1.1\\r\\n\\r\\n\"), max_request_line_size: 20).as(Request)\n          request.path.should eq \"/12345\"\n        end\n\n        it \"fails for too-long URI with custom size\" do\n          request = Request.from_io(IO::Memory.new(\"GET /1234567 HTTP/1.1\\r\\n\\r\\n\"), max_request_line_size: 20)\n          request.should eq HTTP::Status::URI_TOO_LONG\n        end\n      end\n\n      describe \"long headers\" do\n        it \"handles long headers\" do\n          request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\n#{\"X-Test-Header: A pretty log header value\\r\\n\" * 390}\\r\\n\"))\n          request.should be_a(Request)\n          request.as(Request).headers[\"X-Test-Header\"].should eq ([\"A pretty log header value\"] * 390).join(',')\n        end\n\n        it \"fails for too-long headers\" do\n          request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\n#{\"X-Test-Header: A pretty log header value\\r\\n\" * 391}\\r\\n\"))\n          request.should eq HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE\n        end\n\n        it \"handles long headers with custom size\" do\n          request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nFoo: Bar\\r\\n\\r\\n\"), max_headers_size: 10)\n          request.should be_a(Request)\n          request.as(Request).headers[\"Foo\"].should eq \"Bar\"\n        end\n\n        it \"fails for too-long headers with custom size\" do\n          request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nFoo: Bar!\\r\\n\\r\\n\"), max_headers_size: 10)\n          request.should eq HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE\n        end\n      end\n\n      describe \"long single header\" do\n        it \"handles long header\" do\n          request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nFoo: #{\"b\" * 16377}\\r\\n\\r\\n\"))\n          request.should be_a(Request)\n          request.as(Request).headers[\"Foo\"].size.should eq 16377\n        end\n\n        it \"fails for too-long header\" do\n          request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nFoo: #{\"b\" * 16378}\\r\\n\"))\n          request.should eq HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE\n        end\n      end\n    end\n\n    describe \"keep-alive\" do\n      it \"is false by default in HTTP/1.0\" do\n        request = Request.new \"GET\", \"/\", version: \"HTTP/1.0\"\n        request.keep_alive?.should be_false\n      end\n\n      it \"is true in HTTP/1.0 if `Connection: keep-alive` header is present\" do\n        headers = HTTP::Headers.new\n        headers[\"Connection\"] = \"keep-alive\"\n        original_headers = headers.dup\n        request = Request.new \"GET\", \"/\", headers: headers, version: \"HTTP/1.0\"\n        request.keep_alive?.should be_true\n        headers.should eq(original_headers)\n      end\n\n      it \"is true by default in HTTP/1.1\" do\n        request = Request.new \"GET\", \"/\", version: \"HTTP/1.1\"\n        request.keep_alive?.should be_true\n      end\n\n      it \"is false in HTTP/1.1 if `Connection: close` header is present\" do\n        headers = HTTP::Headers.new\n        headers[\"Connection\"] = \"close\"\n        original_headers = headers.dup\n        request = Request.new \"GET\", \"/\", headers: headers, version: \"HTTP/1.1\"\n        request.keep_alive?.should be_false\n        headers.should eq(original_headers)\n      end\n    end\n\n    describe \"#path\" do\n      it \"returns parsed path\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.path.should eq(\"/api/v3/some/resource\")\n      end\n\n      it \"falls back to /\" do\n        request = Request.new(\"GET\", \"\")\n        request.path.should eq(\"/\")\n      end\n\n      it \"parses with only leading with double slash\" do\n        HTTP::Request.new(\"GET\", \"//\").path.should eq \"//\"\n      end\n\n      it \"parses path leading with double slash\" do\n        Request.new(\"GET\", \"//foo:bar\").path.should eq \"//foo:bar\"\n      end\n\n      it \"parses path leading with scheme\" do\n        Request.new(\"GET\", \"http://example.com/foo/bar\").path.should eq \"http://example.com/foo/bar\"\n      end\n    end\n\n    describe \"#path=\" do\n      it \"sets path\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.path = \"/api/v2/greet\"\n        request.path.should eq(\"/api/v2/greet\")\n      end\n\n      it \"updates @resource\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.path = \"/api/v2/greet\"\n        request.resource.should eq(\"/api/v2/greet?filter=hello&world=test\")\n      end\n\n      it \"updates serialized form\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.path = \"/api/v2/greet\"\n\n        io = IO::Memory.new\n        request.to_io(io)\n        io.to_s.should eq(\"GET /api/v2/greet?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")\n      end\n    end\n\n    describe \"#query\" do\n      it \"returns request's query\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.query.should eq(\"filter=hello&world=test\")\n      end\n    end\n\n    describe \"#query=\" do\n      it \"sets query\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.query = \"q=isearchforsomething&locale=de\"\n        request.query.should eq(\"q=isearchforsomething&locale=de\")\n      end\n\n      it \"updates @resource\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.query = \"q=isearchforsomething&locale=de\"\n        request.resource.should eq(\"/api/v3/some/resource?q=isearchforsomething&locale=de\")\n      end\n\n      it \"updates serialized form\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?filter=hello&world=test HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.query = \"q=isearchforsomething&locale=de\"\n\n        io = IO::Memory.new\n        request.to_io(io)\n        io.to_s.should eq(\"GET /api/v3/some/resource?q=isearchforsomething&locale=de HTTP/1.1\\r\\n\\r\\n\")\n      end\n    end\n\n    describe \"#query_params\" do\n      it \"returns parsed URI::Params\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?foo=bar&foo=baz&baz=qux HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        params = request.query_params\n\n        params[\"foo\"].should eq(\"bar\")\n        params.fetch_all(\"foo\").should eq([\"bar\", \"baz\"])\n        params[\"baz\"].should eq(\"qux\")\n      end\n\n      it \"happily parses when query is not a canonical url-encoded string\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?{\\\"hello\\\":\\\"world\\\"} HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        params = request.query_params\n        params[\"{\\\"hello\\\":\\\"world\\\"}\"].should eq(\"\")\n        params.to_s.should eq(\"%7B%22hello%22%3A%22world%22%7D=\")\n      end\n\n      it \"affects #query when modified\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?foo=bar&foo=baz&baz=qux HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        params = request.query_params\n\n        params[\"foo\"] = \"not-bar\"\n        request.query.should eq(\"foo=not-bar&baz=qux\")\n      end\n\n      it \"updates @resource when modified\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?foo=bar&foo=baz&baz=qux HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        params = request.query_params\n\n        params[\"foo\"] = \"not-bar\"\n        request.resource.should eq(\"/api/v3/some/resource?foo=not-bar&baz=qux\")\n      end\n\n      it \"updates serialized form when modified\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?foo=bar&foo=baz&baz=qux HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        params = request.query_params\n\n        params[\"foo\"] = \"not-bar\"\n\n        io = IO::Memory.new\n        request.to_io(io)\n        io.to_s.should eq(\"GET /api/v3/some/resource?foo=not-bar&baz=qux HTTP/1.1\\r\\n\\r\\n\")\n      end\n\n      it \"is affected when #query is modified\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource?foo=bar&foo=baz&baz=qux HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n\n        new_query = \"foo=not-bar&foo=not-baz&not-baz=hello&name=world\"\n        request.query = new_query\n        request.query_params.to_s.should eq(new_query)\n      end\n    end\n\n    describe \"#form_params\" do\n      it \"returns can safely be called on get requests\" do\n        request = Request.from_io(IO::Memory.new(\"GET /api/v3/some/resource HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.form_params?.should be_nil\n        request.form_params.size.should eq(0)\n      end\n\n      it \"returns parsed HTTP::Params\" do\n        request = Request.new(\"POST\", \"/form\", HTTP::Headers{\"Content-Type\" => \"application/x-www-form-urlencoded\"}, HTTP::Params.encode({\"test\" => \"foobar\"}))\n        request.form_params?.should_not be_nil\n        request.form_params.size.should eq(1)\n        request.form_params[\"test\"].should eq(\"foobar\")\n      end\n\n      it \"ignores invalid content-type\" do\n        request = Request.new(\"POST\", \"/form\", nil, HTTP::Params.encode({\"test\" => \"foobar\"}))\n        request.form_params?.should be_nil\n        request.form_params.size.should eq(0)\n      end\n    end\n\n    describe \"#hostname\" do\n      it \"gets request hostname from the headers\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nHost: host.example.org:3000\\r\\nReferer:\\r\\n\\r\\n\")).as(Request)\n        request.hostname.should eq(\"host.example.org\")\n      end\n\n      it \"#hostname\" do\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"host.example.org\"})\n        request.hostname.should eq(\"host.example.org\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"0.0.0.0\"})\n        request.hostname.should eq(\"0.0.0.0\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"[1234:5678::1]\"})\n        request.hostname.should eq(\"1234:5678::1\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"[::1]\"})\n        request.hostname.should eq(\"::1\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"host.example.org:3000\"})\n        request.hostname.should eq(\"host.example.org\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"0.0.0.0:3000\"})\n        request.hostname.should eq(\"0.0.0.0\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"[1234:5678::1]:80\"})\n        request.hostname.should eq(\"1234:5678::1\")\n\n        request = Request.new(\"GET\", \"/\", HTTP::Headers{\"Host\" => \"[::1]:3000\"})\n        request.hostname.should eq(\"::1\")\n\n        request = Request.new(\"GET\", \"/\")\n        request.hostname.should be_nil\n      end\n    end\n\n    describe \"#host_with_port\" do\n      it \"gets request host with port from the headers\" do\n        request = Request.from_io(IO::Memory.new(\"GET / HTTP/1.1\\r\\nHost: host.example.org:3000\\r\\nReferer:\\r\\n\\r\\n\")).as(Request)\n        request.host_with_port.should eq(\"host.example.org:3000\")\n      end\n    end\n\n    describe \"#uri\" do\n      it \"returns request uri object\" do\n        raw_resource = \"/document?something=true#fragment\"\n        request = Request.from_io(IO::Memory.new(\"GET #{raw_resource} HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.uri.should eq(URI.parse(raw_resource))\n      end\n\n      it \"can change the results of #resource\" do\n        request = Request.from_io(IO::Memory.new(\"GET /route HTTP/1.1\\r\\n\\r\\n\")).as(Request)\n        request.resource.should eq(\"/route\")\n\n        request.uri.path = \"/some_other_route\"\n        request.resource.should eq(\"/some_other_route\")\n      end\n    end\n\n    it \"doesn't raise on request with multiple Content_length headers\" do\n      io = IO::Memory.new <<-HTTP\n        GET / HTTP/1.1\n        Host: host\n        Content-Length: 5\n        Content-Length: 5\n        Content-Type: text/plain\n\n        abcde\n        HTTP\n      HTTP::Request.from_io(io)\n    end\n\n    it \"raises if request has multiple and differing content-length headers\" do\n      io = IO::Memory.new <<-HTTP\n        GET / HTTP/1.1\n        Host: host\n        Content-Length: 5\n        Content-Length: 6\n        Content-Type: text/plain\n\n        abcde\n        HTTP\n      expect_raises(ArgumentError) do\n        HTTP::Request.from_io(io)\n      end\n    end\n\n    describe \"#if_none_match\" do\n      it \"reads single value\" do\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(W/\"1234567\")}).if_none_match.should eq [%(W/\"1234567\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(\"1234567\")}).if_none_match.should eq [%(\"1234567\")]\n      end\n\n      it \"reads *\" do\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => \"*\"}).if_none_match.should eq [\"*\"]\n      end\n\n      it \"reads multiple values\" do\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(,W/\"1234567\",)}).if_none_match.should eq [%(W/\"1234567\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(, , W/\"1234567\" , ,)}).if_none_match.should eq [%(W/\"1234567\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(W/\"1234567\",W/\"12345678\")}).if_none_match.should eq [%(W/\"1234567\"), %(W/\"12345678\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(W/\"1234567\" , W/\"12345678\")}).if_none_match.should eq [%(W/\"1234567\"), %(W/\"12345678\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(W/\"1234567\",\"12345678\")}).if_none_match.should eq [%(W/\"1234567\"), %(\"12345678\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-None-Match\" => %(W/\"1234567\" , \"12345678\")}).if_none_match.should eq [%(W/\"1234567\"), %(\"12345678\")]\n      end\n    end\n\n    describe \"#if_match\" do\n      it \"reads single value\" do\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(W/\"1234567\")}).if_match.should eq [%(W/\"1234567\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(\"1234567\")}).if_match.should eq [%(\"1234567\")]\n      end\n\n      it \"reads *\" do\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => \"*\"}).if_match.should eq [\"*\"]\n      end\n\n      it \"reads multiple values\" do\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(,W/\"1234567\",)}).if_match.should eq [%(W/\"1234567\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(, , W/\"1234567\" , ,)}).if_match.should eq [%(W/\"1234567\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(W/\"1234567\",W/\"12345678\")}).if_match.should eq [%(W/\"1234567\"), %(W/\"12345678\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(W/\"1234567\" , W/\"12345678\")}).if_match.should eq [%(W/\"1234567\"), %(W/\"12345678\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(W/\"1234567\",\"12345678\")}).if_match.should eq [%(W/\"1234567\"), %(\"12345678\")]\n        HTTP::Request.new(\"GET\", \"/\", HTTP::Headers{\"If-Match\" => %(W/\"1234567\" , \"12345678\")}).if_match.should eq [%(W/\"1234567\"), %(\"12345678\")]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/handlers/compress_handler_spec.cr",
    "content": "require \"spec\"\nrequire \"http/server\"\n\ndescribe HTTP::CompressHandler do\n  it \"doesn't deflates if doesn't have 'deflate' in Accept-Encoding header\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::CompressHandler.new\n    handler.next = HTTP::Handler::HandlerProc.new do |ctx|\n      ctx.response.print \"Hello\"\n    end\n    handler.call(context)\n    response.close\n\n    io.rewind\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  it \"deflates if has deflate in 'deflate' Accept-Encoding header\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.headers[\"Accept-Encoding\"] = \"foo, deflate, other\"\n\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::CompressHandler.new\n    handler.next = HTTP::Handler::HandlerProc.new do |ctx|\n      ctx.response.print \"Hello\"\n    end\n    handler.call(context)\n    response.close\n\n    io.rewind\n    response2 = HTTP::Client::Response.from_io(io, decompress: false)\n    body = response2.body\n\n    io2 = IO::Memory.new(body)\n    flate = Compress::Deflate::Reader.new(io2)\n    flate.gets_to_end.should eq(\"Hello\")\n  end\n\n  it \"deflates gzip if has deflate in 'deflate' Accept-Encoding header\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.headers[\"Accept-Encoding\"] = \"foo, gzip, other\"\n\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::CompressHandler.new\n    handler.next = HTTP::Handler::HandlerProc.new do |ctx|\n      ctx.response.print \"Hello\"\n    end\n    handler.call(context)\n    response.close\n\n    io.rewind\n    response2 = HTTP::Client::Response.from_io(io, decompress: false)\n    body = response2.body\n\n    io2 = IO::Memory.new(body)\n    gzip = Compress::Gzip::Reader.new(io2)\n    gzip.gets_to_end.should eq(\"Hello\")\n  end\n\n  it \"doesn't compress twice\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.headers[\"Accept-Encoding\"] = \"gzip\"\n\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler1 = HTTP::CompressHandler.new\n    handler2 = HTTP::CompressHandler.new\n    handler1.next = handler2\n    handler2.next = HTTP::Handler::HandlerProc.new do |ctx|\n      ctx.response.print \"Hello\"\n    end\n    handler1.call(context)\n    response.close\n\n    io.rewind\n    response2 = HTTP::Client::Response.from_io(io)\n    response2.body.should eq(\"Hello\")\n  end\n\n  it \"fix content-length header\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.headers[\"Accept-Encoding\"] = \"gzip\"\n\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::CompressHandler.new\n    handler.next = HTTP::Handler::HandlerProc.new do |ctx|\n      ctx.response.content_length = 5\n      ctx.response.print \"Hello\"\n      ctx.response.flush\n    end\n    handler.call(context)\n    response.close\n\n    io.rewind\n    response = HTTP::Client::Response.from_io(io)\n    response.body.should eq(\"Hello\")\n  end\n\n  it \"don't try to compress for empty body responses\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.headers[\"Accept-Encoding\"] = \"gzip\"\n\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::CompressHandler.new\n    handler.next = HTTP::Handler::HandlerProc.new do |ctx|\n      context.response.status = :not_modified\n    end\n    handler.call(context)\n    response.close\n\n    io.rewind\n    io.to_s.should eq(\"HTTP/1.1 304 Not Modified\\r\\n\\r\\n\")\n  end\n\n  it \"don't try to compress upgraded response\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.headers[\"Accept-Encoding\"] = \"gzip\"\n\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::CompressHandler.new\n    handler.next = HTTP::Handler::HandlerProc.new do |ctx|\n      response.status = :switching_protocols\n      response.upgrade do |io|\n      end\n    end\n    handler.call(context)\n    response.close\n\n    io.rewind\n    io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\n\\r\\n\")\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/handlers/error_handler_spec.cr",
    "content": "require \"spec\"\nrequire \"log/spec\"\nrequire \"http/server/handler\"\nrequire \"../../../../support/io\"\n\ndescribe HTTP::ErrorHandler do\n  it \"rescues from exception\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    exception = Exception.new(\"OH NO!\")\n    handler = HTTP::ErrorHandler.new(verbose: true)\n    handler.next = ->(ctx : HTTP::Server::Context) { raise exception }\n    logs = Log.capture(\"http.server\") { handler.call(context) }\n    response.close\n\n    client_response = HTTP::Client::Response.from_io(io.rewind)\n    client_response.status_code.should eq(500)\n    client_response.status_message.should eq(\"Internal Server Error\")\n    client_response.headers[\"content-type\"].should eq(\"text/plain\")\n    client_response.headers.has_key?(\"content-length\").should be_true\n    client_response.body.should match(/^ERROR: OH NO! \\(Exception\\)/)\n\n    logs.check(:error, \"Unhandled exception\")\n    logs.entry.exception.should eq(exception)\n  end\n\n  it \"logs to custom logger\" do\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(IO::Memory.new)\n    context = HTTP::Server::Context.new(request, response)\n\n    exception = Exception.new(\"OH NO!\")\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"custom\", backend, :info)\n    handler = HTTP::ErrorHandler.new(log: log)\n    handler.next = ->(ctx : HTTP::Server::Context) { raise exception }\n    handler.call(context)\n\n    logs = Log::EntriesChecker.new(backend.entries)\n    logs.check(:error, \"Unhandled exception\")\n    logs.entry.exception.should eq(exception)\n  end\n\n  it \"can return a generic error message\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    exception = Exception.new(\"OH NO!\")\n    handler = HTTP::ErrorHandler.new\n    handler.next = ->(ctx : HTTP::Server::Context) { raise exception }\n    logs = Log.capture(\"http.server\") { handler.call(context) }\n\n    client_response = HTTP::Client::Response.from_io(io.rewind)\n    client_response.status_code.should eq(500)\n    client_response.status_message.should eq(\"Internal Server Error\")\n    client_response.headers[\"content-type\"].should eq(\"text/plain\")\n    client_response.body.should eq(\"500 Internal Server Error\\n\")\n\n    logs.check(:error, \"Unhandled exception\")\n    logs.entry.exception.should eq(exception)\n  end\n\n  it \"log debug message when the output is closed\" do\n    io = IO::Memory.new\n    io.close\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::ErrorHandler.new\n    handler.next = ->(ctx : HTTP::Server::Context) { ctx.response.print \"Hi!\"; ctx.response.flush }\n    logs = Log.capture(\"http.server\") { handler.call(context) }\n\n    logs.check(:debug, \"Error while writing data to the client\")\n    logs.entry.exception.should be_a(IO::Error)\n  end\n\n  it \"doesn't write errors when there is some output already sent\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    exception = Exception.new(\"OH NO!\")\n    handler = HTTP::ErrorHandler.new\n    handler.next = ->(ctx : HTTP::Server::Context) do\n      ctx.response.print \"Hi\"\n      ctx.response.flush\n      raise exception\n    end\n    logs = Log.capture(\"http.server\") { handler.call(context) }\n    response.close\n\n    client_response = HTTP::Client::Response.from_io(io.rewind)\n    client_response.status_code.should eq(200)\n    client_response.status_message.should eq(\"OK\")\n    client_response.body.should eq(\"Hi\")\n\n    logs.check(:error, \"Unhandled exception\")\n    logs.entry.exception.should eq(exception)\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/handlers/handler_spec.cr",
    "content": "require \"spec\"\nrequire \"http/server/handler\"\n\nprivate class EmptyHTTPHandler\n  include HTTP::Handler\n\n  def call(context)\n    call_next(context)\n  end\nend\n\ndescribe HTTP::Handler do\n  it \"responds with not found if there's no next handler\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = EmptyHTTPHandler.new\n    handler.call(context)\n    response.close\n\n    io.rewind\n    response = HTTP::Client::Response.from_io(io)\n    response.status_code.should eq(404)\n    response.body.should eq(\"404 Not Found\\n\")\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/handlers/log_handler_spec.cr",
    "content": "require \"spec\"\nrequire \"log/spec\"\nrequire \"http/server/handler\"\nrequire \"../../../../support/io\"\nrequire \"../../../../support/retry\"\n\ndescribe HTTP::LogHandler do\n  it \"logs\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    request.remote_address = Socket::IPAddress.new(\"192.168.0.1\", 1234)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    called = false\n    handler = HTTP::LogHandler.new\n    handler.next = ->(ctx : HTTP::Server::Context) { called = true }\n    logs = Log.capture(\"http.server\") { handler.call(context) }\n    logs.check(:info, %r(^192.168.0.1 - GET / HTTP/1.1 - 200 \\(\\d+(\\.\\d+)?[mµn]s\\)$))\n    called.should be_true\n  end\n\n  it \"logs to custom logger\" do\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(IO::Memory.new)\n    context = HTTP::Server::Context.new(request, response)\n\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"custom\", backend, :info)\n    handler = HTTP::LogHandler.new(log)\n    handler.next = ->(ctx : HTTP::Server::Context) { }\n    handler.call(context)\n\n    logs = Log::EntriesChecker.new(backend.entries)\n    logs.check(:info, %r(^- - GET / HTTP/1.1 - 200 \\(\\d+(\\.\\d+)?[mµn]s\\)$))\n  end\n\n  it \"log failed request\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::LogHandler.new\n    handler.next = ->(ctx : HTTP::Server::Context) { raise \"foo\" }\n    logs = Log.capture(\"http.server\") do\n      expect_raises(Exception, \"foo\") do\n        handler.call(context)\n      end\n    end\n    logs.check(:info, %r(^- - GET / HTTP/1.1 - 200 \\(\\d+(\\.\\d+)?[mµn]s\\)$))\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/handlers/static_file_handler_spec.cr",
    "content": "require \"../../../spec_helper\"\nrequire \"http/server/handler\"\nrequire \"http/client/response\"\nrequire \"../../../../support/tempfile\"\n\nprivate def handle(request, *, fallthrough = true, directory_listing = true, ignore_body = false, decompress = true, directory = datapath(\"static_file_handler\"))\n  io = IO::Memory.new\n  response = HTTP::Server::Response.new(io)\n  context = HTTP::Server::Context.new(request, response)\n  handler = HTTP::StaticFileHandler.new directory, fallthrough, directory_listing\n  handler.call context\n  response.close\n  io.rewind\n  HTTP::Client::Response.from_io(io, ignore_body, decompress)\nend\n\ndescribe HTTP::StaticFileHandler do\n  file_text = File.read datapath(\"static_file_handler\", \"test.txt\")\n\n  it \"serves a file\" do\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt\"), ignore_body: false\n    response.status_code.should eq(200)\n    response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n  end\n\n  it \"handles forbidden characters in windows paths\" do\n    response = handle HTTP::Request.new(\"GET\", \"/foo\\\\bar.txt\"), ignore_body: false\n    response.status_code.should eq 404\n\n    # This file can't be checkout out from git on Windows, thus we need to create it here.\n    File.touch(Path[datapath(\"static_file_handler\"), Path.posix(\"back\\\\slash.txt\")])\n    response = handle HTTP::Request.new(\"GET\", \"/back\\\\slash.txt\"), ignore_body: false\n    response.status_code.should eq 200\n  ensure\n    File.delete(Path[datapath(\"static_file_handler\"), Path.posix(\"back\\\\slash.txt\")])\n  end\n\n  it \"adds Etag header\" do\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n    response.headers[\"Etag\"].should match(/W\\/\"\\d+\"$/)\n  end\n\n  it \"adds Last-Modified header\" do\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n    modification_time = File.info(datapath(\"static_file_handler\", \"test.txt\")).modification_time\n    HTTP.parse_time(response.headers[\"Last-Modified\"]).should eq(modification_time.at_beginning_of_second)\n  end\n\n  context \"with If-Modified-Since header\" do\n    it \"returns 304 Not Modified for equal to Last-Modified\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n\n      headers = HTTP::Headers.new\n      headers[\"If-Modified-Since\"] = initial_response.headers[\"Last-Modified\"]\n\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: true\n      response.status_code.should eq(304)\n\n      response.headers[\"Last-Modified\"].should eq initial_response.headers[\"Last-Modified\"]\n      response.headers[\"Content-Type\"]?.should be_nil\n      response.body.should eq \"\"\n    end\n\n    it \"returns 304 Not Modified for younger than Last-Modified\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n      last_modified = HTTP.parse_time(initial_response.headers[\"Last-Modified\"]).not_nil!\n\n      headers = HTTP::Headers.new\n      headers[\"If-Modified-Since\"] = HTTP.format_time(last_modified + 1.hour)\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: true\n\n      response.headers[\"Last-Modified\"].should eq initial_response.headers[\"Last-Modified\"]\n      response.status_code.should eq(304)\n      response.body.should eq \"\"\n    end\n\n    it \"serves content for older than Last-Modified\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n      last_modified = HTTP.parse_time(initial_response.headers[\"Last-Modified\"]).not_nil!\n\n      headers = HTTP::Headers.new\n      headers[\"If-Modified-Since\"] = HTTP.format_time(last_modified - 1.hour)\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: false\n\n      response.headers[\"Last-Modified\"].should eq initial_response.headers[\"Last-Modified\"]\n      response.status_code.should eq(200)\n      response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n    end\n  end\n\n  context \"with If-None-Match header\" do\n    it \"returns 304 Not Modified if header matches etag\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = initial_response.headers[\"Etag\"]\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: true\n      response.status_code.should eq(304)\n    end\n\n    it \"serves file if header does not match etag\" do\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = \"some random etag\"\n\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: false\n      response.status_code.should eq(200)\n      response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n    end\n\n    it \"returns 304 Not Modified if header is *\" do\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = \"*\"\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: true\n      response.status_code.should eq(304)\n    end\n\n    it \"serves file if header is empty\" do\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = \"\"\n\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: false\n      response.status_code.should eq(200)\n      response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n    end\n\n    it \"serves file if header does not contain valid etag\" do\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = \", foo\"\n\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: false\n      response.status_code.should eq(200)\n      response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n    end\n  end\n\n  context \"with multiple If-None-Match header\" do\n    it \"returns 304 Not Modified if at least one header matches etag\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = %(,, ,W/\"1234567\"   , , #{initial_response.headers[\"Etag\"]},\"12345678\",%)\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: true\n      response.status_code.should eq(304)\n    end\n\n    it \"serves file if no header matches etag\" do\n      headers = HTTP::Headers.new\n      headers[\"If-None-Match\"] = \"some random etag, 1234567\"\n\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: false\n      response.status_code.should eq(200)\n      response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n    end\n  end\n\n  context \"with both If-None-Match and If-Modified-Since headers\" do\n    it \"ignores If-Modified-Since as specified in RFC 7232\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n\n      headers = HTTP::Headers.new\n      headers[\"If-Modified-Since\"] = HTTP.format_time(File.info(datapath(\"static_file_handler\", \"test.txt\")).modification_time - 1.hour)\n      headers[\"If-None-Match\"] = initial_response.headers[\"Etag\"]\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: true\n\n      response.status_code.should eq(304)\n    end\n\n    it \"serves a file if header does not match etag even If-Modified-Since is fresh\" do\n      initial_response = handle HTTP::Request.new(\"GET\", \"/test.txt\")\n\n      headers = HTTP::Headers.new\n      headers[\"If-Modified-Since\"] = initial_response.headers[\"Last-Modified\"]\n      headers[\"If-None-Match\"] = \"some random etag\"\n      response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), ignore_body: false\n\n      response.status_code.should eq(200)\n      response.body.should eq(File.read(datapath(\"static_file_handler\", \"test.txt\")))\n    end\n  end\n\n  context \"when a Range header is provided\" do\n    context \"int range\" do\n      it \"serves a byte range\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=0-2\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should eq \"bytes 0-2/12\"\n        response.body.should eq \"Hel\"\n      end\n\n      it \"serves a single byte\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=0-0\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should eq \"bytes 0-0/12\"\n        response.body.should eq \"H\"\n      end\n\n      it \"serves zero bytes\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=0-0\"}\n        response = handle HTTP::Request.new(\"GET\", \"/empty.txt\", headers)\n\n        response.status_code.should eq(416)\n        response.headers[\"Content-Range\"]?.should eq \"bytes */0\"\n        response.body.should eq \"\"\n      end\n\n      it \"serves an open-ended byte range\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=6-\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should eq \"bytes 6-11/12\"\n        response.body.should eq \"world\\n\"\n      end\n\n      it \"serves multiple byte ranges (separator without whitespace)\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=0-1,6-7\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should be_nil\n        count = 0\n        MIME::Multipart.parse(response) do |headers, part|\n          chunk = part.gets_to_end\n          case range = headers[\"Content-Range\"]\n          when \"bytes 0-1/12\"\n            chunk.should eq \"He\"\n          when \"bytes 6-7/12\"\n            chunk.should eq \"wo\"\n          else\n            fail \"Unknown range: #{range.inspect}\"\n          end\n          count += 1\n        end\n        count.should eq 2\n      end\n\n      it \"serves multiple byte ranges (separator with whitespace)\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=0-1, 6-7\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should be_nil\n        count = 0\n        MIME::Multipart.parse(response) do |headers, part|\n          chunk = part.gets_to_end\n          case range = headers[\"Content-Range\"]\n          when \"bytes 0-1/12\"\n            chunk.should eq \"He\"\n          when \"bytes 6-7/12\"\n            chunk.should eq \"wo\"\n          else\n            fail \"Unknown range: #{range.inspect}\"\n          end\n          count += 1\n        end\n        count.should eq 2\n      end\n\n      it \"end of the range is larger than the file size\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=6-14\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq 206\n        response.headers[\"Content-Range\"]?.should eq \"bytes 6-11/12\"\n        response.body.should eq \"world\\n\"\n      end\n\n      it \"start of the range is larger than the file size\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=14-15\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq 416\n        response.headers[\"Content-Range\"]?.should eq \"bytes */12\"\n      end\n\n      it \"start >= file_size\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=12-\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(416)\n        response.headers[\"Content-Range\"]?.should eq \"bytes */12\"\n      end\n    end\n\n    describe \"suffix range\" do\n      it \"partial\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=-6\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should eq \"bytes 6-11/12\"\n        response.body.should eq \"world\\n\"\n      end\n\n      it \"more bytes than content\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=-15\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(206)\n        response.headers[\"Content-Range\"]?.should eq \"bytes 0-11/12\"\n        response.body.should eq \"Hello world\\n\"\n      end\n\n      it \"zero\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=-0\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n        response.headers[\"Content-Range\"]?.should be_nil\n      end\n\n      it \"zero\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=-0\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/empty.txt\", headers)\n\n        response.status_code.should eq(400)\n        response.headers[\"Content-Range\"]?.should be_nil\n      end\n\n      it \"empty file\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=-1\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/empty.txt\", headers)\n\n        response.status_code.should eq(200)\n        response.headers[\"Content-Range\"]?.should be_nil\n      end\n\n      it \"negative size\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=--2\"}\n\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n        response.headers[\"Content-Range\"]?.should be_nil\n      end\n    end\n\n    describe \"invalid Range syntax\" do\n      it \"byte number without dash\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=1\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"start > end\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=2-1\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"negative end\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=1--2\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"open range with negative end\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=--2\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"open range with negative end\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=--2\"}\n        response = handle HTTP::Request.new(\"GET\", \"/empty.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"unsupported unit\" do\n        headers = HTTP::Headers{\"Range\" => \"chars=1-2\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(416)\n        response.headers[\"Content-Range\"]?.should eq \"bytes */12\"\n      end\n\n      it \"multiple dashes\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=1-2-3\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"not a number\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=a-b\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n\n      it \"not a range\" do\n        headers = HTTP::Headers{\"Range\" => \"bytes=-\"}\n        response = handle HTTP::Request.new(\"GET\", \"/range.txt\", headers)\n\n        response.status_code.should eq(400)\n      end\n    end\n  end\n\n  it \"lists directory's entries\" do\n    response = handle HTTP::Request.new(\"GET\", \"/\")\n    response.status_code.should eq(200)\n    response.body.should match(/test.txt/)\n  end\n\n  it \"does not list directory's entries when directory_listing is set to false\" do\n    response = handle HTTP::Request.new(\"GET\", \"/\"), directory_listing: false\n    response.status_code.should eq(404)\n  end\n\n  it \"does not redirect directory when directory_listing=false\" do\n    response = handle HTTP::Request.new(\"GET\", \"/foo\"), directory_listing: false\n    response.status_code.should eq(404)\n  end\n\n  it \"redirect directory when directory_listing=true\" do\n    response = handle HTTP::Request.new(\"GET\", \"/foo\"), directory_listing: true\n    response.status_code.should eq(302)\n    response.headers[\"Location\"].should eq \"/foo/\"\n  end\n\n  it \"preserves uri components during redirect\" do\n    response = handle HTTP::Request.new(\"GET\", \"/foo?ami=kept#somefragment\"), directory_listing: true\n    response.status_code.should eq(302)\n    response.headers[\"Location\"].should eq \"/foo/?ami=kept#somefragment\"\n  end\n\n  it \"does not double encode query parameters when redirecting\" do\n    response = handle HTTP::Request.new(\"GET\", \"/foo?k=%26k%3Dv\"), directory_listing: true\n    response.status_code.should eq(302)\n    response.headers[\"Location\"].should eq \"/foo/?k=%26k%3Dv\"\n  end\n\n  it \"does not serve a not found file\" do\n    response = handle HTTP::Request.new(\"GET\", \"/not_found_file.txt\")\n    response.status_code.should eq(404)\n  end\n\n  it \"does not serve a not found directory\" do\n    response = handle HTTP::Request.new(\"GET\", \"/not_found_dir/\")\n    response.status_code.should eq(404)\n  end\n\n  it \"does not serve a file as directory\" do\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt/\")\n    response.status_code.should eq(404)\n  end\n\n  it \"handles only GET and HEAD method\" do\n    %w(GET HEAD).each do |method|\n      response = handle HTTP::Request.new(method, \"/test.txt\")\n      response.status_code.should eq(200)\n    end\n\n    %w(POST PUT DELETE).each do |method|\n      response = handle HTTP::Request.new(method, \"/test.txt\")\n      response.status_code.should eq(404)\n      response = handle HTTP::Request.new(method, \"/test.txt\"), fallthrough: false\n      response.status_code.should eq(405)\n      response.headers[\"Allow\"].should eq(\"GET, HEAD\")\n    end\n  end\n\n  it \"expands a request path\" do\n    %w(../test.txt ../../test.txt test.txt/../test.txt a/./b/../c/../../test.txt).each do |path|\n      response = handle HTTP::Request.new(\"GET\", \"/#{path}\")\n      response.status_code.should eq(302)\n      response.headers[\"Location\"].should eq(\"/test.txt\")\n    end\n\n    # directory\n    %w(.. ../ ../.. a/.. a/.././b/../).each do |path|\n      response = handle HTTP::Request.new(\"GET\", \"/#{path}\")\n      response.status_code.should eq(302)\n      response.headers[\"Location\"].should eq(\"/\")\n    end\n  end\n\n  it \"unescapes a request path\" do\n    %w(test%2Etxt %74%65%73%74%2E%74%78%74).each do |path|\n      response = handle HTTP::Request.new(\"GET\", \"/#{path}\")\n      response.status_code.should eq(200)\n      response.body.should eq(file_text)\n    end\n\n    %w(%2E%2E/test.txt found%2F%2E%2E%2Ftest%2Etxt).each do |path|\n      response = handle HTTP::Request.new(\"GET\", \"/#{path}\")\n      response.status_code.should eq(302)\n      response.headers[\"Location\"].should eq(\"/test.txt\")\n    end\n  end\n\n  it \"returns 400\" do\n    %w(%00 test.txt%00).each do |path|\n      response = handle HTTP::Request.new(\"GET\", \"/#{path}\")\n      response.status_code.should eq(400)\n    end\n  end\n\n  it \"handles invalid redirect path\" do\n    response = handle HTTP::Request.new(\"GET\", \"test.txt%0A\")\n    response.status_code.should eq(302)\n    response.headers[\"Location\"].should eq \"/test.txt%0A\"\n\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt%0A\")\n    response.status_code.should eq(404)\n  end\n\n  it \"serve compressed content\" do\n    modification_time = File.info(datapath(\"static_file_handler\", \"test.txt\")).modification_time\n    File.touch datapath(\"static_file_handler\", \"test.txt.gz\"), modification_time + 1.second\n\n    headers = HTTP::Headers{\"Accept-Encoding\" => \"gzip\"}\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), decompress: false\n    response.headers[\"Content-Encoding\"].should eq(\"gzip\")\n  end\n\n  it \"still serve compressed content when modification time is very close\" do\n    modification_time = File.info(datapath(\"static_file_handler\", \"test.txt\")).modification_time\n    File.touch datapath(\"static_file_handler\", \"test.txt.gz\"), modification_time - 1.millisecond\n\n    headers = HTTP::Headers{\"Accept-Encoding\" => \"gzip\"}\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers), decompress: false\n    response.headers[\"Content-Encoding\"].should eq(\"gzip\")\n  end\n\n  it \"doesn't serve compressed content if older than raw file\" do\n    modification_time = File.info(datapath(\"static_file_handler\", \"test.txt\")).modification_time\n    File.touch datapath(\"static_file_handler\", \"test.txt.gz\"), modification_time - 1.second\n\n    headers = HTTP::Headers{\"Accept-Encoding\" => \"gzip\"}\n    response = handle HTTP::Request.new(\"GET\", \"/test.txt\", headers)\n    response.headers[\"Content-Encoding\"]?.should be_nil\n  end\n\n  it \"returns 404 for file error\" do\n    with_tempdir do\n      File.symlink(\"nonexistent.txt\", \"broken-symlink.txt\")\n      response = handle HTTP::Request.new(\"GET\", \"/broken-symlink.txt\")\n      response.status_code.should eq(404)\n    end\n  end\n\n  it \"returns 404 for unreadable file\" do\n    with_tempdir do\n      File.write(\"forbidden.txt\", \"not for your eyes\")\n      File.chmod(\"forbidden.txt\", File::Permissions::None)\n\n      # FIXME: Setting permissions does not work on all systems. Even the\n      # permissions recheck is not sufficient (see https://github.com/crystal-lang/crystal/pull/16025#issuecomment-3112225515).\n      pending! if File.info(\"forbidden.txt\").permissions.owner_read? || (File.read(\"forbidden.txt\") rescue nil)\n      response = handle HTTP::Request.new(\"GET\", \"/forbidden.txt\"), directory: \".\"\n      response.status_code.should eq(404)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/handlers/websocket_handler_spec.cr",
    "content": "require \"spec\"\nrequire \"http/server\"\n\ndescribe HTTP::WebSocketHandler do\n  it \"returns not found if the request is not an websocket upgrade\" do\n    io = IO::Memory.new\n    request = HTTP::Request.new(\"GET\", \"/\")\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    invoked = false\n    handler = HTTP::WebSocketHandler.new { invoked = true }\n    handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n    handler.call context\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  it \"returns not found if the request Upgrade is invalid\" do\n    io = IO::Memory.new\n\n    headers = HTTP::Headers{\n      \"Upgrade\"               => \"WS\",\n      \"Connection\"            => \"Upgrade\",\n      \"Sec-WebSocket-Key\"     => \"dGhlIHNhbXBsZSBub25jZQ==\",\n      \"Sec-WebSocket-Version\" => \"13\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    invoked = false\n    handler = HTTP::WebSocketHandler.new { invoked = true }\n    handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n    handler.call context\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  {% for connection in [\"Upgrade\", \"keep-alive, Upgrade\"] %}\n    it \"gives upgrade response for websocket upgrade request with '{{connection.id}}' request\" do\n      io = IO::Memory.new\n      headers = HTTP::Headers{\n        \"Upgrade\" =>           \"websocket\",\n        \"Connection\" =>        {{connection}},\n        \"Sec-WebSocket-Key\" => \"dGhlIHNhbXBsZSBub25jZQ==\",\n        \"Sec-WebSocket-Version\" => \"13\",\n      }\n      request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n      response = HTTP::Server::Response.new(io)\n      context = HTTP::Server::Context.new(request, response)\n\n      handler = HTTP::WebSocketHandler.new { }\n      handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n\n      begin\n        handler.call context\n      rescue IO::Error\n        # Raises because the IO::Memory is empty\n      end\n\n      response.close\n\n      io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\\r\\n\\r\\n\")\n    end\n  {% end %}\n\n  it \"gives upgrade response for case-insensitive 'WebSocket' upgrade request\" do\n    io = IO::Memory.new\n    headers = HTTP::Headers{\n      \"Upgrade\"               => \"WebSocket\",\n      \"Connection\"            => \"Upgrade\",\n      \"Sec-WebSocket-Key\"     => \"dGhlIHNhbXBsZSBub25jZQ==\",\n      \"Sec-WebSocket-Version\" => \"13\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new { }\n    handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n\n    begin\n      handler.call context\n    rescue IO::Error\n      # Raises because the IO::Memory is empty\n    end\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\\r\\n\\r\\n\")\n  end\n\n  it \"returns bad request if Sec-WebSocket-Key is missing\" do\n    io = IO::Memory.new\n\n    headers = HTTP::Headers{\n      \"Upgrade\"               => \"websocket\",\n      \"Connection\"            => \"Upgrade\",\n      \"Sec-WebSocket-Version\" => \"13\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new { }\n    handler.call context\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 400 Bad Request\\r\\nContent-Type: text/plain\\r\\nContent-Length: 16\\r\\n\\r\\n400 Bad Request\\n\")\n  end\n\n  it \"returns upgrade required if Sec-WebSocket-Version is missing\" do\n    io = IO::Memory.new\n\n    headers = HTTP::Headers{\n      \"Upgrade\"           => \"websocket\",\n      \"Connection\"        => \"Upgrade\",\n      \"Sec-WebSocket-Key\" => \"dGhlIHNhbXBsZSBub25jZQ==\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new { }\n    handler.call context\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 426 Upgrade Required\\r\\nSec-WebSocket-Version: 13\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n  end\n\n  it \"returns upgrade required if Sec-WebSocket-Version is invalid\" do\n    io = IO::Memory.new\n\n    headers = HTTP::Headers{\n      \"Upgrade\"               => \"websocket\",\n      \"Connection\"            => \"Upgrade\",\n      \"Sec-WebSocket-Key\"     => \"dGhlIHNhbXBsZSBub25jZQ==\",\n      \"Sec-WebSocket-Version\" => \"12\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new { }\n    handler.call context\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 426 Upgrade Required\\r\\nSec-WebSocket-Version: 13\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n  end\n\n  it \"includes Sec-WebSocket-Protocol in upgrade response if it is valid\" do\n    io = IO::Memory.new\n    headers = HTTP::Headers{\n      \"Upgrade\"                => \"WebSocket\",\n      \"Connection\"             => \"Upgrade\",\n      \"Sec-WebSocket-Key\"      => \"dGhlIHNhbXBsZSBub25jZQ==\",\n      \"Sec-WebSocket-Version\"  => \"13\",\n      \"Sec-WebSocket-Protocol\" => \"chat\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new([\"chat\"]) { }\n    handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n\n    begin\n      handler.call context\n    rescue IO::Error\n      # Raises because the IO::Memory is empty\n    end\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\\r\\nSec-WebSocket-Protocol: chat\\r\\n\\r\\n\")\n  end\n\n  it \"excludes Sec-WebSocket-Protocol in upgrade response if it is not valid\" do\n    io = IO::Memory.new\n    headers = HTTP::Headers{\n      \"Upgrade\"                => \"WebSocket\",\n      \"Connection\"             => \"Upgrade\",\n      \"Sec-WebSocket-Key\"      => \"dGhlIHNhbXBsZSBub25jZQ==\",\n      \"Sec-WebSocket-Version\"  => \"13\",\n      \"Sec-WebSocket-Protocol\" => \"chat\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new([\"video\"]) { }\n    handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n\n    begin\n      handler.call context\n    rescue IO::Error\n      # Raises because the IO::Memory is empty\n    end\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\\r\\n\\r\\n\")\n  end\n\n  [\n    {\n      client_protocols: \"chat\",\n      expected:         \"chat\",\n      server_protocols: [\"chat\"],\n    },\n    {\n      client_protocols: \"chat,video\",\n      expected:         \"video\",\n      server_protocols: [\"video\"],\n    },\n    {\n      client_protocols: \"chat,video\",\n      expected:         \"chat\",\n      server_protocols: [\"video\", \"chat\"],\n    },\n    {\n      client_protocols: \"chat,video\",\n      expected:         \"chat\",\n      server_protocols: [\"audio\", \"chat\"],\n    },\n  ].each do |test_case|\n    it \"uses the first matched Sec-WebSocket-Protocol in upgrade response for '#{test_case[:client_protocols]}'\" do\n      io = IO::Memory.new\n      headers = HTTP::Headers{\n        \"Upgrade\"                => \"WebSocket\",\n        \"Connection\"             => \"Upgrade\",\n        \"Sec-WebSocket-Key\"      => \"dGhlIHNhbXBsZSBub25jZQ==\",\n        \"Sec-WebSocket-Version\"  => \"13\",\n        \"Sec-WebSocket-Protocol\" => test_case[:client_protocols],\n      }\n      request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n      response = HTTP::Server::Response.new(io)\n      context = HTTP::Server::Context.new(request, response)\n\n      handler = HTTP::WebSocketHandler.new(test_case[:server_protocols]) { }\n      handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n\n      begin\n        handler.call context\n      rescue IO::Error\n        # Raises because the IO::Memory is empty\n      end\n\n      response.close\n\n      io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\\r\\nSec-WebSocket-Protocol: #{test_case[:expected]}\\r\\n\\r\\n\")\n    end\n  end\n\n  it \"excludes Sec-WebSocket-Protocol in upgrade response if no protocol is provided to handler\" do\n    io = IO::Memory.new\n    headers = HTTP::Headers{\n      \"Upgrade\"                => \"WebSocket\",\n      \"Connection\"             => \"Upgrade\",\n      \"Sec-WebSocket-Key\"      => \"dGhlIHNhbXBsZSBub25jZQ==\",\n      \"Sec-WebSocket-Version\"  => \"13\",\n      \"Sec-WebSocket-Protocol\" => \"chat\",\n    }\n    request = HTTP::Request.new(\"GET\", \"/\", headers: headers)\n    response = HTTP::Server::Response.new(io)\n    context = HTTP::Server::Context.new(request, response)\n\n    handler = HTTP::WebSocketHandler.new() { }\n    handler.next = HTTP::Handler::HandlerProc.new &.response.print(\"Hello\")\n\n    begin\n      handler.call context\n    rescue IO::Error\n      # Raises because the IO::Memory is empty\n    end\n\n    response.close\n\n    io.to_s.should eq(\"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\\r\\n\\r\\n\")\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/request_processor_spec.cr",
    "content": "require \"spec\"\nrequire \"log/spec\"\nrequire \"http/server/request_processor\"\nrequire \"../../../support/io\"\n\nprivate def requestize(string)\n  string.gsub('\\n', \"\\r\\n\")\nend\n\ndescribe HTTP::Server::RequestProcessor do\n  it \"works\" do\n    processor = HTTP::Server::RequestProcessor.new do |context|\n      context.response.content_type = \"text/plain\"\n      context.response.print \"Hello world\"\n    end\n\n    input = IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\n\")\n    output = IO::Memory.new\n    processor.process(input, output)\n    output.rewind\n    output.gets_to_end.should eq(requestize(<<-HTTP\n      HTTP/1.1 200 OK\n      Connection: keep-alive\n      Content-Type: text/plain\n      Content-Length: 11\n\n      Hello world\n      HTTP\n    ))\n  end\n\n  describe \"reads consecutive requests\" do\n    it \"when body is consumed\" do\n      processor = HTTP::Server::RequestProcessor.new do |context|\n        context.response.content_type = \"text/plain\"\n        context.response << context.request.body.not_nil!.gets(chomp: true)\n        context.response << \"\\r\\n\"\n      end\n\n      input = IO::Memory.new(requestize(<<-HTTP\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        HTTP\n      ))\n      output = IO::Memory.new\n      processor.process(input, output)\n      output.rewind\n      output.gets_to_end.should eq(requestize(<<-HTTP\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Type: text/plain\n        Content-Length: 7\n\n        hello\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Type: text/plain\n        Content-Length: 7\n\n        hello\n\n        HTTP\n      ))\n    end\n\n    it \"with empty body\" do\n      processor = HTTP::Server::RequestProcessor.new do |context|\n        context.response.content_type = \"text/plain\"\n        context.response.puts \"Hello world\\r\"\n      end\n\n      input = IO::Memory.new(requestize(<<-HTTP\n        POST / HTTP/1.1\n\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        HTTP\n      ))\n      output = IO::Memory.new\n      processor.process(input, output)\n      output.rewind\n      output.gets_to_end.should eq(requestize(<<-HTTP\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Type: text/plain\n        Content-Length: 13\n\n        Hello world\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Type: text/plain\n        Content-Length: 13\n\n        Hello world\n\n        HTTP\n      ))\n    end\n\n    it \"fail if body is not consumed\" do\n      processor = HTTP::Server::RequestProcessor.new do |context|\n        context.response.content_type = \"text/plain\"\n        context.response.puts \"Hello world\\r\"\n      end\n\n      input = IO::Memory.new(requestize(<<-HTTP\n        POST / HTTP/1.1\n\n        hello\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        HTTP\n      ))\n      output = IO::Memory.new\n      processor.process(input, output)\n      output.rewind\n      output.gets_to_end.should eq(requestize(<<-HTTP\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Type: text/plain\n        Content-Length: 13\n\n        Hello world\n        HTTP/1.1 400 Bad Request\n        Content-Type: text/plain\n        Content-Length: 16\n\n        400 Bad Request\\\\n\n        HTTP\n      ).gsub(\"\\\\n\", \"\\n\"))\n    end\n\n    it \"closes connection when Connection: close\" do\n      processor = HTTP::Server::RequestProcessor.new do |context|\n        context.response.headers[\"Connection\"] = \"close\"\n      end\n\n      input = IO::Memory.new(requestize(<<-HTTP\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        HTTP\n      ))\n      output = IO::Memory.new\n      processor.process(input, output)\n      output.rewind\n      output.gets_to_end.should eq(requestize(<<-HTTP\n        HTTP/1.1 200 OK\n        Connection: close\n        Content-Length: 0\n\n\n        HTTP\n      ))\n    end\n\n    it \"closes connection when request body is not entirely consumed\" do\n      processor = HTTP::Server::RequestProcessor.new do |context|\n      end\n\n      input = IO::Memory.new(requestize(<<-HTTP\n        POST / HTTP/1.1\n        Content-Length: 4\n\n        1\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        HTTP\n      ))\n      output = IO::Memory.new\n      processor.process(input, output)\n      output.rewind\n      output.gets_to_end.should eq(requestize(<<-HTTP\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Length: 0\n\n\n        HTTP\n      ))\n    end\n\n    it \"continues when request body is entirely consumed\" do\n      processor = HTTP::Server::RequestProcessor.new do |context|\n        io = context.request.body.not_nil!\n        io.gets_to_end\n      end\n\n      input = IO::Memory.new(requestize(<<-HTTP\n        POST / HTTP/1.1\n        Content-Length: 16387\n\n        #{\"0\" * 16_384}1\n        POST / HTTP/1.1\n        Content-Length: 7\n\n        hello\n        HTTP\n      ))\n      output = IO::Memory.new\n      processor.process(input, output)\n      output.rewind\n      output.gets_to_end.should eq(requestize(<<-HTTP\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Length: 0\n\n        HTTP/1.1 200 OK\n        Connection: keep-alive\n        Content-Length: 0\n\n\n        HTTP\n      ))\n    end\n  end\n\n  it \"handles IO::Error while reading\" do\n    processor = HTTP::Server::RequestProcessor.new { }\n    input = RaiseIOError.new\n    output = IO::Memory.new\n    processor.process(input, output)\n    output.rewind.gets_to_end.should be_empty\n  end\n\n  it \"handles IO::Error while writing\" do\n    processor = HTTP::Server::RequestProcessor.new do |context|\n      context.response.content_type = \"text/plain\"\n      context.response.print \"Hello world\"\n      context.response.flush\n    end\n    input = IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\n\")\n    output = RaiseIOError.new(true)\n    logs = Log.capture(\"http.server\") do\n      processor.process(input, output)\n    end\n    logs.check(:debug, \"Error while writing data to the client\")\n    logs.entry.exception.should be_a(IO::Error)\n  end\n\n  it \"handles IO::Error while flushing\" do\n    processor = HTTP::Server::RequestProcessor.new do |context|\n      context.response.flush\n    end\n    input = IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\n\")\n    output = RaiseIOError.new(false)\n    logs = Log.capture(\"http.server\") do\n      processor.process(input, output)\n    end\n    logs.check(:debug, \"Error while flushing data to the client\")\n    logs.entry.exception.should be_a(IO::Error)\n  end\n\n  it \"catches raised error on handler and retains context from handler\" do\n    exception = Exception.new \"OH NO\"\n    processor = HTTP::Server::RequestProcessor.new { Log.context.set foo: \"bar\"; raise exception }\n    input = IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\n\")\n    output = IO::Memory.new\n    logs = Log.capture(\"http.server\") do\n      processor.process(input, output)\n    end\n\n    client_response = HTTP::Client::Response.from_io(output.rewind)\n    client_response.status_code.should eq(500)\n    client_response.status_message.should eq(\"Internal Server Error\")\n    client_response.headers[\"content-type\"].should eq(\"text/plain\")\n    client_response.headers.has_key?(\"content-length\").should be_true\n    client_response.body.should eq(\"500 Internal Server Error\\n\")\n\n    logs.check(:error, \"Unhandled exception on HTTP::Handler\")\n    logs.entry.exception.should eq(exception)\n    logs.entry.context[:foo].should eq \"bar\"\n  end\n\n  it \"doesn't respond with error when headers were already sent\" do\n    processor = HTTP::Server::RequestProcessor.new do |context|\n      context.response.content_type = \"text/plain\"\n      context.response.print \"Hello world\"\n      context.response.flush\n      raise \"OH NO\"\n    end\n    input = IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\n\")\n    output = IO::Memory.new\n    processor.process(input, output)\n\n    client_response = HTTP::Client::Response.from_io(output.rewind)\n    client_response.status_code.should eq(200)\n    client_response.body.should eq(\"Hello world\")\n  end\n\n  it \"flushes output buffer when an error happens and some content was already sent\" do\n    processor = HTTP::Server::RequestProcessor.new do |context|\n      context.response.content_type = \"text/plain\"\n      context.response.print \"Hello \"\n      context.response.flush\n      context.response.print \"world\"\n      raise \"OH NO\"\n    end\n    input = IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\n\")\n    output = IO::Memory.new\n    processor.process(input, output)\n\n    client_response = HTTP::Client::Response.from_io(output.rewind)\n    client_response.status_code.should eq(200)\n    client_response.body.should eq(\"Hello world\")\n  end\n\n  it \"does not bleed Log::Context between requests\" do\n    processor = HTTP::Server::RequestProcessor.new do |context|\n      Log.info { \"before\" }\n      Log.context.set foo: \"bar\"\n      Log.info { \"after\" }\n\n      context.response.content_type = \"text/plain\"\n      context.response.print \"Hello world\"\n    end\n\n    logs = Log.capture do\n      processor.process(\n        IO::Memory.new(\"GET / HTTP/1.1\\r\\n\\r\\nGET / HTTP/1.1\\r\\n\\r\\n\"),\n        IO::Memory.new,\n      )\n    end\n\n    logs.check :info, \"before\"\n    logs.entry.context.should be_empty\n    logs.check :info, \"after\"\n    logs.entry.context[:foo].should eq \"bar\"\n\n    logs.check :info, \"before\"\n    logs.entry.context.should be_empty\n    logs.check :info, \"after\"\n    logs.entry.context[:foo].should eq \"bar\"\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/response_spec.cr",
    "content": "require \"spec\"\nrequire \"http/server/response\"\nrequire \"http/headers\"\nrequire \"http/status\"\nrequire \"http/cookie\"\n\nprivate alias Response = HTTP::Server::Response\n\nprivate class ReverseResponseOutput < IO\n  @output : IO\n\n  def initialize(@output : IO)\n  end\n\n  def write(slice : Bytes) : Nil\n    slice.reverse_each do |byte|\n      @output.write_byte(byte)\n    end\n  end\n\n  def read(slice : Bytes)\n    raise \"Not implemented\"\n  end\n\n  def close\n    @output.close\n  end\n\n  def flush\n    @output.flush\n  end\nend\n\ndescribe HTTP::Server::Response do\n  it \"closes\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.close\n    response.closed?.should be_true\n    io.closed?.should be_false\n    expect_raises(IO::Error, \"Closed stream\") { response << \"foo\" }\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n  end\n\n  it \"does not automatically add the `content-length` header if the response is a 304\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :not_modified\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 304 Not Modified\\r\\n\\r\\n\")\n  end\n\n  it \"does not automatically add the `content-length` header if the response is a 204\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :no_content\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 204 No Content\\r\\n\\r\\n\")\n  end\n\n  it \"does not automatically add the `content-length` header if the response is informational\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :processing\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 102 Processing\\r\\n\\r\\n\")\n  end\n\n  # Case where the content-length represents the size of the data that would have been returned.\n  it \"allows specifying the content-length header explicitly\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :not_modified\n    response.headers[\"Content-Length\"] = \"5\"\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 304 Not Modified\\r\\nContent-Length: 5\\r\\n\\r\\n\")\n  end\n\n  it \"allow explicitly configuring a `Transfer-Encoding` response\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.headers[\"Transfer-Encoding\"] = \"chunked\"\n    response.print \"Hello\"\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nHello\\r\\n0\\r\\n\\r\\n\")\n  end\n\n  it \"prints less then buffer's size\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.print(\"Hello\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  it \"prints less then buffer's size to output\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.output.print(\"Hello\")\n    response.output.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  it \"prints more then buffer's size\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    str = \"1234567890\"\n    slices = (IO::DEFAULT_BUFFER_SIZE // 10)\n    slices.times do\n      response.print(str)\n    end\n    response.print(str)\n    response.close\n    first_chunk = str * slices\n    second_chunk = str\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n#{(first_chunk.bytesize).to_s(16)}\\r\\n#{first_chunk}\\r\\n#{(second_chunk.bytesize).to_s(16)}\\r\\n#{second_chunk}\\r\\n0\\r\\n\\r\\n\")\n  end\n\n  it \"prints with content length\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.headers[\"Content-Length\"] = \"10\"\n    response.print(\"1234\")\n    response.print(\"567890\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 10\\r\\n\\r\\n1234567890\")\n  end\n\n  it \"prints with content length (method)\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.content_length = 10\n    response.print(\"1234\")\n    response.print(\"567890\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 10\\r\\n\\r\\n1234567890\")\n  end\n\n  it \"doesn't override content-length when there's no body\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.content_length = 10\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 10\\r\\n\\r\\n\")\n  end\n\n  it \"adds header\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.headers[\"Content-Type\"] = \"text/plain\"\n    response.print(\"Hello\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  it \"sets content type\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.content_type = \"text/plain\"\n    response.print(\"Hello\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: 5\\r\\n\\r\\nHello\")\n  end\n\n  it \"gets content type\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.content_type.should be_nil\n    response.content_type = \"text/plain\"\n    response.content_type.should eq \"text/plain\"\n  end\n\n  it \"gets content length\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.content_length.should be_nil\n    response.content_length = 10\n    response.content_length.should eq 10\n  end\n\n  it \"sets content type after headers sent\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.print(\"Hello\")\n    response.flush\n    expect_raises(IO::Error, \"Headers already sent\") do\n      response.content_type = \"text/plain\"\n    end\n  end\n\n  it \"sets status code\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    return_value = response.status_code = 201\n    return_value.should eq 201\n    response.status.should eq HTTP::Status::CREATED\n    response.status_message.should eq \"Created\"\n    response.print(\"Hello\")\n    response.flush\n    expect_raises(IO::Error, \"Headers already sent\") do\n      response.status_code = 201\n    end\n  end\n\n  it \"retrieves status code\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :created\n    response.status_code.should eq 201\n  end\n\n  it \"changes status message\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :not_found\n    response.status_message = \"Custom status\"\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 404 Custom status\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n    response.status_message.should eq \"Custom status\"\n\n    expect_raises(IO::Error, \"Closed stream\") do\n      response.status_message = \"Other status\"\n    end\n  end\n\n  it \"changes status and others\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.status = :not_found\n    response.version = \"HTTP/1.0\"\n    response.close\n    io.to_s.should eq(\"HTTP/1.0 404 Not Found\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n  end\n\n  it \"changes status and others after headers sent\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.print(\"Foo\")\n    response.flush\n    expect_raises(IO::Error, \"Headers already sent\") do\n      response.status = :not_found\n    end\n    expect_raises(IO::Error, \"Headers already sent\") do\n      response.version = \"HTTP/1.0\"\n    end\n  end\n\n  it \"closes gracefully with replaced output that syncs close (#11389)\" do\n    output = IO::Memory.new\n    response = HTTP::Server::Response.new(output)\n\n    response.output = IO::Stapled.new(response.output, response.output, sync_close: true)\n    response.print \"some body\"\n\n    response.close\n\n    output.rewind.gets_to_end.should eq \"HTTP/1.1 200 OK\\r\\nContent-Length: 9\\r\\n\\r\\nsome body\"\n  end\n\n  it \"flushes\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.print(\"Hello\")\n    response.flush\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nHello\\r\\n\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nHello\\r\\n0\\r\\n\\r\\n\")\n  end\n\n  it \"wraps output\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.output = ReverseResponseOutput.new(response.output)\n    response.print(\"1234\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 4\\r\\n\\r\\n4321\")\n  end\n\n  it \"writes and flushes with HTTP 1.0\" do\n    io = IO::Memory.new\n    response = Response.new(io, \"HTTP/1.0\")\n    response.print(\"1234\")\n    response.flush\n    io.to_s.should eq(\"HTTP/1.0 200 OK\\r\\n\\r\\n1234\")\n  end\n\n  it \"resets and clears headers and cookies\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.headers[\"Foo\"] = \"Bar\"\n    response.cookies[\"Bar\"] = \"Foo\"\n    response.status = HTTP::Status::USE_PROXY\n    response.status_message = \"Baz\"\n    response.reset\n    response.headers.should be_empty\n    response.cookies.should be_empty\n    response.status.should eq HTTP::Status::OK\n    response.status_message.should eq \"OK\"\n  end\n\n  it \"writes cookie headers\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.cookies[\"Bar\"] = \"Foo\"\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 0\\r\\nSet-Cookie: Bar=Foo\\r\\n\\r\\n\")\n\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.cookies[\"Bar\"] = \"Foo\"\n    response.print(\"Hello\")\n    response.close\n    io.to_s.should eq(\"HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\nSet-Cookie: Bar=Foo\\r\\n\\r\\nHello\")\n  end\n\n  it \"closes when it fails to write\" do\n    io = IO::Memory.new\n    response = Response.new(io)\n    response.print(\"Hello\")\n    response.flush\n    io.close\n    response.print(\"Hello\")\n    expect_raises(HTTP::Server::ClientError) { response.flush }\n    response.closed?.should be_true\n  end\n\n  describe \"#respond_with_status\" do\n    it \"uses default values\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.content_type = \"text/html\"\n      response.respond_with_status(500)\n      io.to_s.should eq(\"HTTP/1.1 500 Internal Server Error\\r\\nContent-Type: text/plain\\r\\nContent-Length: 26\\r\\n\\r\\n500 Internal Server Error\\n\")\n      response.status_message.should eq \"Internal Server Error\"\n    end\n\n    it \"sends custom code and message\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.respond_with_status(400, \"Request Error\")\n      io.to_s.should eq(\"HTTP/1.1 400 Request Error\\r\\nContent-Type: text/plain\\r\\nContent-Length: 18\\r\\n\\r\\n400 Request Error\\n\")\n      response.status_message.should eq \"Request Error\"\n    end\n\n    it \"sends HTTP::Status\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.respond_with_status(HTTP::Status::URI_TOO_LONG)\n      io.to_s.should eq(\"HTTP/1.1 414 URI Too Long\\r\\nContent-Type: text/plain\\r\\nContent-Length: 17\\r\\n\\r\\n414 URI Too Long\\n\")\n      response.status_message.should eq \"URI Too Long\"\n    end\n\n    it \"sends HTTP::Status and custom message\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.respond_with_status(HTTP::Status::URI_TOO_LONG, \"Request Error\")\n      io.to_s.should eq(\"HTTP/1.1 414 Request Error\\r\\nContent-Type: text/plain\\r\\nContent-Length: 18\\r\\n\\r\\n414 Request Error\\n\")\n      response.status_message.should eq \"Request Error\"\n    end\n\n    it \"raises when response is closed\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.close\n      expect_raises(IO::Error, \"Closed stream\") do\n        response.respond_with_status(400)\n      end\n    end\n\n    it \"raises when headers written\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.print(\"Hello\")\n      response.flush\n      expect_raises(IO::Error, \"Headers already sent\") do\n        response.respond_with_status(400)\n      end\n    end\n  end\n\n  describe \"#redirect\" do\n    [\"/path\", URI.parse(\"/path\")].each do |location|\n      it \"#{location.class} location\" do\n        io = IO::Memory.new\n        response = Response.new(io)\n        response.redirect(location)\n        io.to_s.should eq(\"HTTP/1.1 302 Found\\r\\nLocation: /path\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n      end\n    end\n\n    it \"encodes special characters\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.redirect(\"https://example.com/path\\nfoo bar\")\n      io.to_s.should eq(\"HTTP/1.1 302 Found\\r\\nLocation: https://example.com/path%0Afoo%20bar\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n    end\n\n    it \"doesn't encode URIs twice\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      u = URI.new \"https\", host: \"example.com\", path: \"auth\",\n        query: URI::Params.new({\"redirect_uri\" => [\"http://example.com/callback\"]})\n      response.redirect(u)\n      io.to_s.should eq(\"HTTP/1.1 302 Found\\r\\nLocation: https://example.com/auth?redirect_uri=http%3A%2F%2Fexample.com%2Fcallback\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n    end\n\n    it \"permanent redirect\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.redirect(\"/path\", status: :moved_permanently)\n      io.to_s.should eq(\"HTTP/1.1 301 Moved Permanently\\r\\nLocation: /path\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n    end\n\n    it \"with header\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.headers[\"Foo\"] = \"Bar\"\n      response.redirect(\"/path\", status: :moved_permanently)\n      io.to_s.should eq(\"HTTP/1.1 301 Moved Permanently\\r\\nFoo: Bar\\r\\nLocation: /path\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n    end\n\n    it \"fails if headers already sent\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.puts \"foo\"\n      response.flush\n      expect_raises(IO::Error, \"Headers already sent\") do\n        response.redirect(\"/path\")\n      end\n    end\n\n    it \"fails if closed\" do\n      io = IO::Memory.new\n      response = Response.new(io)\n      response.close\n      expect_raises(IO::Error, \"Closed stream\") do\n        response.redirect(\"/path\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/server/server_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"http/server\"\nrequire \"http/client\"\nrequire \"../../../support/ssl\"\nrequire \"../../../support/channel\"\n\n# TODO: replace with `HTTP::Client.get` once it supports connecting to Unix socket (#2735)\nprivate def unix_request(path)\n  UNIXSocket.open(path) do |io|\n    HTTP::Client.new(io).get(path).body\n  end\nend\n\nprivate def unused_port\n  TCPServer.open(Socket::IPAddress::UNSPECIFIED, 0) do |server|\n    server.local_address.port\n  end\nend\n\nprivate class SilentErrorHTTPServer < HTTP::Server\n  private def handle_exception(e)\n  end\nend\n\nprivate def requestize(string)\n  string.gsub('\\n', \"\\r\\n\")\nend\n\ndescribe HTTP::Server do\n  it \"binds to unused port\" do\n    server = HTTP::Server.new { |ctx| }\n    address = server.bind_unused_port\n    address.port.should_not eq(0)\n\n    server.close\n\n    server = HTTP::Server.new { |ctx| }\n    port = server.bind_tcp(0).port\n    port.should_not eq(0)\n  ensure\n    server.close if server\n  end\n\n  it \"doesn't raise on accept after close #2692\" do\n    server = HTTP::Server.new { }\n    server.bind_unused_port\n\n    run_server(server) do\n      server.close\n    end\n  end\n\n  it \"closes the server\" do\n    server = HTTP::Server.new { }\n    address = server.bind_unused_port\n    ch = Channel(SpecChannelStatus).new\n\n    spawn do\n      server.listen\n      ch.send :end\n    end\n\n    # wait for the server to start listening, and a little longer\n    # so the spawn that performs the accept has chance to run\n    while !server.listening?\n      Fiber.yield\n    end\n    sleep 0.1.seconds\n\n    schedule_timeout ch\n\n    TCPSocket.open(address.address, address.port) { }\n\n    # wait before closing the server\n    sleep 0.1.seconds\n    server.close\n\n    ch.receive.should eq SpecChannelStatus::End\n  end\n\n  it \"reuses the TCP port (SO_REUSEPORT)\" do\n    s1 = HTTP::Server.new { |ctx| }\n    address = s1.bind_unused_port(reuse_port: true)\n\n    s2 = HTTP::Server.new { |ctx| }\n    s2.bind_tcp(address.port, reuse_port: true)\n\n    s1.close\n    s2.close\n  end\n\n  it \"binds to different ports\" do\n    server = HTTP::Server.new do |context|\n      context.response.print \"Test Server (#{context.request.local_address})\"\n    end\n\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server.bind tcp_server\n    address1 = tcp_server.local_address\n\n    address2 = server.bind_unused_port\n\n    address1.should_not eq address2\n\n    run_server(server) do\n      HTTP::Client.get(\"http://#{address2}/\").body.should eq \"Test Server (#{address2})\"\n      HTTP::Client.get(\"http://#{address1}/\").body.should eq \"Test Server (#{address1})\"\n      HTTP::Client.get(\"http://#{address1}/\").body.should eq \"Test Server (#{address1})\"\n    end\n  end\n\n  it \"handles Expect: 100-continue correctly when body is read\" do\n    server = HTTP::Server.new do |context|\n      context.response << context.request.body.not_nil!.gets_to_end\n    end\n\n    address = server.bind_unused_port\n\n    run_server(server) do\n      TCPSocket.open(address.address, address.port) do |socket|\n        socket << requestize(<<-HTTP\n          POST / HTTP/1.1\n          Expect: 100-continue\n          Content-Length: 5\n\n          HTTP\n        )\n        socket << \"\\r\\n\"\n        socket.flush\n\n        response = HTTP::Client::Response.from_io(socket)\n        response.status_code.should eq(100)\n\n        socket << \"hello\"\n        socket.flush\n\n        response = HTTP::Client::Response.from_io(socket)\n        response.status_code.should eq(200)\n        response.body.should eq(\"hello\")\n      end\n    end\n  end\n\n  it \"handles Expect: 100-continue correctly when body isn't read\" do\n    server = HTTP::Server.new do |context|\n      context.response.respond_with_status(400, \"I don't want your body\")\n    end\n\n    address = server.bind_unused_port\n\n    run_server(server) do\n      TCPSocket.open(address.address, address.port) do |socket|\n        socket << requestize(<<-HTTP\n          POST / HTTP/1.1\n          Expect: 100-continue\n          Content-Length: 5\n\n          HTTP\n        )\n        socket << \"\\r\\n\"\n        socket.flush\n\n        response = HTTP::Client::Response.from_io(socket)\n        response.status_code.should eq(400)\n        response.body.should eq(\"400 I don't want your body\\n\")\n      end\n    end\n  end\n\n  it \"lists addresses\" do\n    server = HTTP::Server.new { }\n\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    addresses = [server.bind_unused_port, server.bind_unused_port, tcp_server.local_address]\n    server.bind tcp_server\n\n    server.addresses.should eq addresses\n  ensure\n    server.try &.close\n  end\n\n  describe \"#bind\" do\n    it \"fails after listen\" do\n      server = HTTP::Server.new { }\n      server.bind_unused_port\n\n      run_server(server) do\n        expect_raises(Exception, \"Can't add socket to running server\") do\n          server.bind_unused_port\n        end\n      end\n    end\n\n    it \"fails after close\" do\n      server = HTTP::Server.new { }\n      server.bind_unused_port\n\n      run_server(server) do\n        server.close\n\n        expect_raises(Exception, \"Can't add socket to closed server\") do\n          server.bind_unused_port\n        end\n      end\n    end\n\n    describe \"with URI\" do\n      it \"accepts URI\" do\n        server = HTTP::Server.new { }\n\n        begin\n          port = unused_port\n          address = server.bind URI.parse(\"tcp://127.0.0.1:#{port}\")\n          address.should eq Socket::IPAddress.new(\"127.0.0.1\", port)\n        ensure\n          server.close\n        end\n      end\n\n      it \"accepts String\" do\n        server = HTTP::Server.new { }\n\n        begin\n          port = unused_port\n          address = server.bind \"tcp://127.0.0.1:#{port}\"\n          address.should eq Socket::IPAddress.new(\"127.0.0.1\", port)\n        ensure\n          server.close\n        end\n      end\n\n      it \"parses TCP\" do\n        server = HTTP::Server.new { }\n\n        begin\n          port = unused_port\n          address = server.bind \"tcp://127.0.0.1:#{port}\"\n          address.should eq Socket::IPAddress.new(\"127.0.0.1\", port)\n        ensure\n          server.close\n        end\n      end\n\n      it \"parses SSL\" do\n        server = HTTP::Server.new { }\n\n        private_key = datapath(\"openssl\", \"openssl.key\")\n        certificate = datapath(\"openssl\", \"openssl.crt\")\n\n        begin\n          port = unused_port\n          expect_raises(ArgumentError, \"missing CA certificate\") do\n            server.bind \"tls://127.0.0.1:#{port}?key=#{private_key}&cert=#{certificate}&verify_mode=force-peer\"\n          end\n\n          address = server.bind \"tls://127.0.0.1:#{port}?key=#{private_key}&cert=#{certificate}&ca=#{certificate}\"\n          address.should eq Socket::IPAddress.new(\"127.0.0.1\", port)\n\n          port = unused_port\n          address = server.bind \"ssl://127.0.0.1:#{port}?key=#{private_key}&cert=#{certificate}&ca=#{certificate}\"\n          address.should eq Socket::IPAddress.new(\"127.0.0.1\", port)\n        ensure\n          server.close\n        end\n      end\n\n      it \"fails SSL with invalid params\" do\n        server = HTTP::Server.new { }\n\n        private_key = datapath(\"openssl\", \"openssl.key\")\n\n        begin\n          expect_raises(ArgumentError, \"missing private key\") { server.bind \"tls://127.0.0.1:8081\" }\n          expect_raises(OpenSSL::Error, \"No such file or directory\") { server.bind \"tls://127.0.0.1:8081?key=foo.key\" }\n          expect_raises(ArgumentError, \"missing certificate\") { server.bind \"tls://127.0.0.1:8081?key=#{private_key}\" }\n        ensure\n          server.close\n        end\n      end\n\n      it \"fails with unknown scheme\" do\n        server = HTTP::Server.new { }\n\n        begin\n          expect_raises(ArgumentError, \"Unsupported socket type: udp\") do\n            server.bind \"udp://127.0.0.1:8081\"\n          end\n        ensure\n          server.close\n        end\n      end\n    end\n  end\n\n  describe \"#bind_tls\" do\n    it \"binds SSL server context\" do\n      server = HTTP::Server.new do |context|\n        context.response.puts \"Test Server (#{context.request.local_address})\"\n        context.response.close\n      end\n\n      server_context, client_context = ssl_context_pair\n\n      socket = OpenSSL::SSL::Server.new(TCPServer.new(\"127.0.0.1\", 0), server_context)\n      server.bind socket\n      ip_address1 = server.bind_tls \"127.0.0.1\", 0, server_context\n      ip_address2 = socket.local_address\n\n      run_server(server) do\n        HTTP::Client.get(\"https://#{ip_address1}\", tls: client_context).body.should eq \"Test Server (#{ip_address1})\\n\"\n        HTTP::Client.get(\"https://#{ip_address2}\", tls: client_context).body.should eq \"Test Server (#{ip_address2})\\n\"\n      end\n    end\n  end\n\n  describe \"#listen\" do\n    it \"fails after listen\" do\n      server = HTTP::Server.new { }\n      server.bind_unused_port\n\n      run_server(server) do\n        expect_raises(Exception, \"Can't start running server\") do\n          server.listen\n        end\n      end\n    end\n\n    it \"fails after close\" do\n      server = HTTP::Server.new { }\n      server.bind_unused_port\n\n      run_server(server) do\n        server.close\n        server.listening?.should be_false\n\n        expect_raises(Exception, \"Can't re-start closed server\") do\n          server.listen\n        end\n      end\n    end\n  end\n\n  {% if flag?(:unix) %}\n    describe \"#bind_unix\" do\n      it \"binds to different unix sockets\" do\n        path1 = File.tempname\n        path2 = File.tempname\n\n        begin\n          server = HTTP::Server.new do |context|\n            context.response.print \"Test Server (#{context.request.local_address})\"\n            context.response.close\n          end\n\n          socket1 = UNIXServer.new(path1)\n          server.bind socket1\n          socket2 = server.bind_unix path2\n\n          run_server(server) do\n            unix_request(path1).should eq \"Test Server (#{path1})\"\n            unix_request(path2).should eq \"Test Server (#{path2})\"\n          end\n\n          File.exists?(path1).should be_false\n          File.exists?(path2).should be_false\n        ensure\n          File.delete?(path1)\n          File.delete?(path2)\n        end\n      end\n    end\n  {% end %}\n\n  it \"handles exception during SSL handshake (#6577)\" do\n    server = SilentErrorHTTPServer.new do |context|\n      context.response.print \"ok\"\n      context.response.close\n    end\n\n    server_context, client_context = ssl_context_pair\n    address = server.bind_tls \"localhost\", server_context\n\n    run_server(server) do |server_done|\n      3.times do\n        # Perform multiple wrong calls together and check\n        # that the server is still able to respond.\n        3.times do\n          empty_context = OpenSSL::SSL::Context::Client.new\n          TCPSocket.open(address.address, address.port) do |socket|\n            expect_raises(OpenSSL::SSL::Error) do\n              OpenSSL::SSL::Socket::Client.new(socket, empty_context)\n            end\n          end\n        end\n\n        HTTP::Client.get(\"https://#{address}/\", tls: client_context).body.should eq \"ok\"\n      end\n\n      server.closed?.should be_false\n      select\n      when ret = server_done.receive\n        fail(\"Server finished with #{ret}\")\n      else\n      end\n    end\n  end\n\n  it \"can process simultaneous SSL handshakes\" do\n    {% if flag?(:win32) && flag?(:gnu) && flag?(:x86_64) %}\n      # FIXME: why does the spec causes the process to die with status code 67?\n      pending! \"process dies with exit code 67 on msys2-ucrt-x86_64 on CI\"\n    {% end %}\n\n    server = HTTP::Server.new do |context|\n      context.response.print \"ok\"\n    end\n\n    server_context, client_context = ssl_context_pair\n    address = server.bind_tls \"localhost\", server_context\n\n    run_server(server) do\n      ch = Channel(Nil).new\n\n      spawn do\n        TCPSocket.open(address.address, address.port) do |socket|\n          ch.send nil\n          ch.receive\n        end\n      end\n\n      begin\n        ch.receive\n        client = HTTP::Client.new(address.address, address.port, client_context)\n        client.read_timeout = client.connect_timeout = 3.seconds\n        client.get(\"/\").body.should eq \"ok\"\n      ensure\n        ch.send nil\n      end\n    end\n  end\n\n  describe \"#close\" do\n    it \"closes gracefully\" do\n      server = HTTP::Server.new do |context|\n        context.response.flush\n        context.response.puts \"foo\"\n        context.response.flush\n\n        context.response.puts \"bar\"\n      end\n\n      address = server.bind_unused_port\n\n      run_server(server) do\n        TCPSocket.open(address.address, address.port) do |socket|\n          socket << \"GET / HTTP/1.1\\r\\n\\r\\n\"\n\n          while true\n            line = socket.gets || break\n            break if line.empty?\n          end\n\n          socket = HTTP::ChunkedContent.new(socket)\n\n          socket.gets.should eq \"foo\"\n\n          server.close\n\n          socket.closed?.should be_false\n          socket.gets.should eq \"bar\"\n        end\n      end\n    end\n  end\n\n  describe \"#remote_address / #local_address\" do\n    it \"for http server\" do\n      remote_address = nil\n      local_address = nil\n\n      server = HTTP::Server.new do |context|\n        remote_address = context.request.remote_address\n        local_address = context.request.local_address\n      end\n\n      tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n      server.bind tcp_server\n      address1 = tcp_server.local_address\n\n      run_server(server) do\n        HTTP::Client.new(URI.parse(\"http://#{address1}/\")) do |client|\n          client.get(\"/\")\n\n          remote_address.should eq(client.@io.as(IPSocket).local_address)\n          local_address.should eq(client.@io.as(IPSocket).remote_address)\n        end\n      end\n    end\n\n    it \"for https server\" do\n      remote_address = nil\n      local_address = nil\n\n      server = HTTP::Server.new do |context|\n        remote_address = context.request.remote_address\n        local_address = context.request.local_address\n      end\n\n      server_context, client_context = ssl_context_pair\n\n      socket = OpenSSL::SSL::Server.new(TCPServer.new(\"127.0.0.1\", 0), server_context)\n      server.bind socket\n      ip_address1 = server.bind_tls \"127.0.0.1\", 0, server_context\n\n      run_server(server) do\n        HTTP::Client.new(\n          uri: URI.parse(\"https://#{ip_address1}\"),\n          tls: client_context) do |client|\n          client.get(\"/\")\n          remote_address.should eq(client.@io.as(OpenSSL::SSL::Socket).local_address)\n          local_address.should eq(client.@io.as(OpenSSL::SSL::Socket).remote_address)\n        end\n      end\n    end\n  end\n\n  describe \"#max_request_line_size\" do\n    it \"sets and gets size\" do\n      server = HTTP::Server.new { |ctx| }\n      server.max_request_line_size.should eq HTTP::MAX_REQUEST_LINE_SIZE\n      server.@processor.max_request_line_size.should eq HTTP::MAX_REQUEST_LINE_SIZE\n      server.max_request_line_size = 20\n      server.max_request_line_size.should eq 20\n      server.@processor.max_request_line_size.should eq 20\n    end\n\n    it \"respects size on request\" do\n      server = HTTP::Server.new { |ctx| }\n      read = IO::Memory.new(\"GET /1234567 HTTP/1.1\\r\\n\\r\\n\")\n      write = IO::Memory.new\n\n      io = IO::Stapled.new(read, write)\n      server.@processor.process(io, io)\n      write.rewind\n      HTTP::Client::Response.from_io(write).status.should eq HTTP::Status::OK\n\n      read.rewind\n      write.clear\n\n      server.max_request_line_size = 20\n\n      io = IO::Stapled.new(read, write)\n      server.@processor.process(io, io)\n      write.rewind\n      HTTP::Client::Response.from_io(write).status.should eq HTTP::Status::URI_TOO_LONG\n    end\n  end\n\n  describe \"#max_request_line_size\" do\n    it \"sets and gets size\" do\n      server = HTTP::Server.new { |ctx| }\n      server.max_request_line_size.should eq HTTP::MAX_REQUEST_LINE_SIZE\n      server.@processor.max_request_line_size.should eq HTTP::MAX_REQUEST_LINE_SIZE\n      server.max_request_line_size = 20\n      server.max_request_line_size.should eq 20\n      server.@processor.max_request_line_size.should eq 20\n    end\n\n    it \"respects size on request\" do\n      server = HTTP::Server.new { |ctx| }\n      read = IO::Memory.new(\"GET /1234567 HTTP/1.1\\r\\n\\r\\n\")\n      write = IO::Memory.new\n\n      io = IO::Stapled.new(read, write)\n      server.@processor.process(io, io)\n      write.rewind\n      HTTP::Client::Response.from_io(write).status.should eq HTTP::Status::OK\n\n      read.rewind\n      write.clear\n\n      server.max_request_line_size = 20\n\n      io = IO::Stapled.new(read, write)\n      server.@processor.process(io, io)\n      write.rewind\n      HTTP::Client::Response.from_io(write).status.should eq HTTP::Status::URI_TOO_LONG\n    end\n  end\n\n  describe \"#max_headers_size\" do\n    it \"sets and gets size\" do\n      server = HTTP::Server.new { |ctx| }\n      server.max_headers_size.should eq HTTP::MAX_HEADERS_SIZE\n      server.@processor.max_headers_size.should eq HTTP::MAX_HEADERS_SIZE\n      server.max_headers_size = 20\n      server.max_headers_size.should eq 20\n      server.@processor.max_headers_size.should eq 20\n    end\n\n    it \"respects size on request\" do\n      server = HTTP::Server.new { |ctx| }\n      read = IO::Memory.new(\"GET /foo HTTP/1.1\\r\\nFoo: Bar Baz\\r\\n\\r\\n\")\n      write = IO::Memory.new\n\n      io = IO::Stapled.new(read, write)\n      server.@processor.process(io, io)\n      write.rewind\n      HTTP::Client::Response.from_io(write).status.should eq HTTP::Status::OK\n\n      read.rewind\n      write.clear\n\n      server.max_headers_size = 10\n\n      io = IO::Stapled.new(read, write)\n      server.@processor.process(io, io)\n      write.rewind\n      HTTP::Client::Response.from_io(write).status.should eq HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE\n    end\n  end\n\n  typeof(begin\n    # Initialize with custom host\n    server = HTTP::Server.new { |ctx| }\n    server.bind_tcp \"0.0.0.0\", 0\n    server.listen\n    server.close\n\n    server = HTTP::Server.new([\n      HTTP::ErrorHandler.new,\n      HTTP::LogHandler.new,\n      HTTP::CompressHandler.new,\n      HTTP::StaticFileHandler.new(\".\"),\n    ]\n    )\n    server.bind_tcp \"0.0.0.0\", 0\n    server.listen\n    server.close\n\n    server = HTTP::Server.new([HTTP::StaticFileHandler.new(\".\")]) { |ctx| }\n    server.bind_tcp \"0.0.0.0\", 0\n    server.listen\n    server.close\n\n    # Initialize with default host\n    server = HTTP::Server.new { |ctx| }\n    server.bind_tcp 0\n    server.listen\n    server.close\n\n    server = HTTP::Server.new([\n      HTTP::ErrorHandler.new,\n      HTTP::LogHandler.new,\n      HTTP::CompressHandler.new,\n      HTTP::StaticFileHandler.new(\".\"),\n    ]\n    )\n    server.bind_tcp 0\n    server.listen\n    server.close\n\n    server = HTTP::Server.new([HTTP::StaticFileHandler.new(\".\")]) { |ctx| }\n    server.bind_tcp 0\n    server.listen\n    server.close\n  end)\nend\n"
  },
  {
    "path": "spec/std/http/spec_helper.cr",
    "content": "require \"spec\"\nrequire \"../spec_helper\"\nrequire \"../../support/fibers\"\n\nprivate def wait_for(&)\n  timeout = {% if flag?(:interpreted) %}\n              # TODO: it's not clear why some interpreter specs\n              # take more than 5 seconds to bind to a server.\n              # See #12429.\n              25.seconds\n            {% else %}\n              5.seconds\n            {% end %}\n  now = Time.instant\n\n  until yield\n    Fiber.yield\n\n    if now.elapsed > timeout\n      raise \"server failed to start within #{timeout}\"\n    end\n  end\nend\n\n# Helper method which runs *server*\n# 1. Spawns `server.listen` in a new fiber.\n# 2. Waits until `server.listening?`.\n# 3. Yields to the given block.\n# 4. Ensures the server is closed.\n# 5. After returning from the block, it waits for the server to gracefully\n#    shut down before continuing execution in the current fiber.\n# 6. If the listening fiber raises an exception, it is rescued and re-raised\n#    in the current fiber.\ndef run_server(server, &)\n  server_done = Channel(Exception?).new\n\n  f = spawn do\n    server.listen\n  rescue exc\n    server_done.send exc\n  else\n    server_done.send nil\n  end\n\n  begin\n    wait_for { server.listening? }\n    wait_until_blocked f\n\n    {% if flag?(:preview_mt) %}\n      # avoids fiber synchronization issues in specs, like closing the server\n      # before we properly listen, ...\n      sleep 1.millisecond\n    {% end %}\n    yield server_done\n  ensure\n    server.close unless server.closed?\n\n    if exc = server_done.receive\n      raise exc\n    end\n  end\nend\n\n# Helper method which runs a *handler*\n# Similar to `run_server` but doesn't go through the network stack.\ndef run_handler(handler, &)\n  done = Channel(Exception?).new\n\n  IO::Stapled.pipe do |server_io, client_io|\n    processor = HTTP::Server::RequestProcessor.new(handler)\n    f = spawn do\n      processor.process(server_io, server_io)\n    rescue exc\n      done.send exc\n    else\n      done.send nil\n    end\n\n    client = HTTP::Client.new(client_io)\n\n    begin\n      wait_until_blocked f\n\n      yield client\n    ensure\n      processor.close\n      server_io.close\n      if exc = done.receive\n        raise exc\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/status_spec.cr",
    "content": "require \"spec\"\nrequire \"http\"\n\ndescribe HTTP::Status do\n  describe \".new\" do\n    it \"raises when given invalid status code\" do\n      expect_raises(ArgumentError, \"Invalid HTTP status code: 1000\") do\n        HTTP::Status.new(1000)\n      end\n    end\n\n    it \"returns an instance when given defined status code\" do\n      HTTP::Status.new(201).should eq HTTP::Status::CREATED\n    end\n\n    it \"returns an instance when given undefined status code\" do\n      HTTP::Status.new(418).should eq HTTP::Status.new(418)\n    end\n  end\n\n  describe \"#code\" do\n    it \"returns the status code\" do\n      HTTP::Status::INTERNAL_SERVER_ERROR.code.should eq 500\n    end\n  end\n\n  describe \"#informational?\" do\n    it \"returns true when given 1xx status code\" do\n      HTTP::Status.new(100).informational?.should be_true\n    end\n\n    it \"returns false unless given 1xx status code\" do\n      HTTP::Status.new(999).informational?.should be_false\n    end\n  end\n\n  describe \"#success?\" do\n    it \"returns true when given 2xx status code\" do\n      HTTP::Status.new(200).success?.should be_true\n    end\n\n    it \"returns false unless given 2xx status code\" do\n      HTTP::Status.new(999).success?.should be_false\n    end\n  end\n\n  describe \"#redirection?\" do\n    it \"returns true when given 3xx status code\" do\n      HTTP::Status.new(300).redirection?.should be_true\n    end\n\n    it \"returns false unless given 3xx status code\" do\n      HTTP::Status.new(999).redirection?.should be_false\n    end\n  end\n\n  describe \"#client_error?\" do\n    it \"returns true when given 4xx status code\" do\n      HTTP::Status.new(400).client_error?.should be_true\n    end\n\n    it \"returns false unless given 4xx status code\" do\n      HTTP::Status.new(999).client_error?.should be_false\n    end\n  end\n\n  describe \"#server_error?\" do\n    it \"returns true when given 5xx status code\" do\n      HTTP::Status.new(500).server_error?.should be_true\n    end\n\n    it \"returns false unless given 5xx status code\" do\n      HTTP::Status.new(999).server_error?.should be_false\n    end\n  end\n\n  describe \"#description\" do\n    it \"returns default description for status 200\" do\n      HTTP::Status.new(200).description.should eq(\"OK\")\n    end\n\n    it \"returns nil on non-existent status\" do\n      HTTP::Status.new(999).description.should be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/http/web_socket_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../spec_helper\"\nrequire \"http/web_socket\"\nrequire \"http/server\"\nrequire \"random/secure\"\nrequire \"../../support/fibers\"\nrequire \"../../support/ssl\"\nrequire \"../socket/spec_helper.cr\"\n\nprivate def assert_text_packet(packet, size, final = false)\n  assert_packet packet, HTTP::WebSocket::Protocol::Opcode::TEXT, size, final: final\nend\n\nprivate def assert_binary_packet(packet, size, final = false)\n  assert_packet packet, HTTP::WebSocket::Protocol::Opcode::BINARY, size, final: final\nend\n\nprivate def assert_ping_packet(packet, size, final = false)\n  assert_packet packet, HTTP::WebSocket::Protocol::Opcode::PING, size, final: final\nend\n\nprivate def assert_close_packet(packet, size, final = false)\n  assert_packet packet, HTTP::WebSocket::Protocol::Opcode::CLOSE, size, final: final\nend\n\nprivate def assert_packet(packet, opcode, size, final = false)\n  packet.opcode.should eq(opcode)\n  packet.size.should eq(size)\n  packet.final.should eq(final)\nend\n\nprivate class MalformerHandler\n  include HTTP::Handler\n\n  def call(context)\n    context.response.headers[\"Transfer-Encoding\"] = \"chunked\"\n    call_next(context)\n  end\nend\n\ndescribe HTTP::WebSocket do\n  describe \"receive\" do\n    it \"can read a small text packet\" do\n      data = Bytes[0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(64)\n      result = ws.receive(buffer)\n      assert_text_packet result, 5, final: true\n      String.new(buffer[0, result.size]).should eq(\"Hello\")\n    end\n\n    it \"can read partial packets\" do\n      data = Bytes[0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,\n        0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(3)\n\n      2.times do\n        result = ws.receive(buffer)\n        assert_text_packet result, 3, final: false\n        String.new(buffer).should eq(\"Hel\")\n\n        result = ws.receive(buffer)\n        assert_text_packet result, 2, final: true\n        String.new(buffer[0, 2]).should eq(\"lo\")\n      end\n    end\n\n    it \"can read masked text message\" do\n      data = Bytes[0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58,\n        0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58]\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(3)\n\n      2.times do\n        result = ws.receive(buffer)\n        assert_text_packet result, 3, final: false\n        String.new(buffer).should eq(\"Hel\")\n\n        result = ws.receive(buffer)\n        assert_text_packet result, 2, final: true\n        String.new(buffer[0, 2]).should eq(\"lo\")\n      end\n    end\n\n    it \"can read fragmented packets\" do\n      data = Bytes[0x01, 0x03, 0x48, 0x65, 0x6c, 0x80, 0x02, 0x6c, 0x6f,\n        0x01, 0x03, 0x48, 0x65, 0x6c, 0x80, 0x02, 0x6c, 0x6f]\n\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(10)\n\n      2.times do\n        result = ws.receive(buffer)\n        assert_text_packet result, 3, final: false\n        String.new(buffer[0, 3]).should eq(\"Hel\")\n\n        result = ws.receive(buffer)\n        assert_text_packet result, 2, final: true\n        String.new(buffer[0, 2]).should eq(\"lo\")\n      end\n    end\n\n    it \"read ping packet\" do\n      data = Bytes[0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(64)\n      result = ws.receive(buffer)\n      assert_ping_packet result, 5, final: true\n      String.new(buffer[0, result.size]).should eq(\"Hello\")\n    end\n\n    it \"read ping packet in between fragmented packet\" do\n      data = Bytes[0x01, 0x03, 0x48, 0x65, 0x6c,\n        0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,\n        0x80, 0x02, 0x6c, 0x6f]\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(64)\n\n      result = ws.receive(buffer)\n      assert_text_packet result, 3, final: false\n      String.new(buffer[0, 3]).should eq(\"Hel\")\n\n      result = ws.receive(buffer)\n      assert_ping_packet result, 5, final: true\n      String.new(buffer[0, result.size]).should eq(\"Hello\")\n\n      result = ws.receive(buffer)\n      assert_text_packet result, 2, final: true\n      String.new(buffer[0, 2]).should eq(\"lo\")\n    end\n\n    it \"read long packet\" do\n      data = File.read(datapath(\"websocket_longpacket.bin\"))\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(2048)\n\n      result = ws.receive(buffer)\n      assert_text_packet result, 1023, final: true\n      String.new(buffer[0, 1023]).should eq(\"x\" * 1023)\n    end\n\n    it \"read very long packet\" do\n      data = Bytes.new(10 + 0x010000)\n\n      header = Bytes[0x82, 127_u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]\n      data.copy_from(header)\n\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(0x010000)\n\n      result = ws.receive(buffer)\n      assert_binary_packet result, 0x010000, final: true\n    end\n\n    it \"can read a close packet\" do\n      data = Bytes[0x88, 0x00]\n      io = IO::Memory.new(data)\n      ws = HTTP::WebSocket::Protocol.new(io)\n\n      buffer = Bytes.new(64)\n      result = ws.receive(buffer)\n      assert_close_packet result, 0, final: true\n    end\n  end\n\n  describe \"send\" do\n    it \"sends long data with correct header\" do\n      big_string = \"abcdefghijklmnopqrstuvwxyz\" * (IO::DEFAULT_BUFFER_SIZE // 4)\n      size = big_string.size\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.send(big_string)\n      bytes = io.to_slice\n      bytes.size.should eq(10 + size) # 2 bytes header, 8 bytes size, UInt16 + 1 bytes content\n      bytes[1].should eq(127)\n      received_size = 0\n      8.times { |i| received_size <<= 8; received_size += bytes[2 + i] }\n      received_size.should eq(size)\n      size.times do |i|\n        bytes[10 + i].should eq(big_string[i].ord)\n      end\n    end\n\n    it \"sets binary opcode if used with slice\" do\n      sent_bytes = uninitialized UInt8[4]\n\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io, masked: true)\n      ws.send(sent_bytes.to_slice)\n      bytes = io.to_slice\n      (bytes[0] & 0x0f).should eq(0x02)\n    end\n  end\n\n  describe \"stream\" do\n    it \"sends continuous data and splits it to frames\" do\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.stream do |io| # default frame size of 1024\n        3.times { io.write((\"a\" * 512).to_slice) }\n      end\n\n      bytes = io.to_slice\n      bytes.size.should eq(4 * 2 + 512 * 3) # two frames with 2 bytes header, 2 bytes size, 3 * 512 bytes content in total\n      first_frame, second_frame = {bytes[0, (4 + 1024)], bytes + (4 + 1024)}\n      (first_frame[0] & 0x80).should eq(0x00) # FINAL bit unset\n      (first_frame[0] & 0x0f).should eq(0x02) # BINARY frame\n      first_frame[1].should eq(126)           # extended size\n      received_size = 0\n      2.times { |i| received_size <<= 8; received_size += first_frame[2 + i] }\n      received_size.should eq(1024)\n      received_size.times do |i|\n        bytes[4 + i].should eq('a'.ord)\n      end\n\n      (second_frame[0] & 0x80).should_not eq(0x00) # FINAL bit set\n      (second_frame[0] & 0x0f).should eq(0x00)     # CONTINUATION frame\n      second_frame[1].should eq(126)               # extended size\n      received_size = 0\n      2.times { |i| received_size <<= 8; received_size += second_frame[2 + i] }\n      received_size.should eq(512)\n      received_size.times do |i|\n        bytes[4 + i].should eq('a'.ord)\n      end\n    end\n\n    it \"sends less data than the frame size if necessary\" do\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.stream do |io| # default frame size of 1024\n        io.write(\"hello world\".to_slice)\n      end\n\n      bytes = io.to_slice\n      bytes.size.should eq(2 + 11) # one frame with 1 byte header, 1 byte size, \"hello world\" bytes content in total\n      first_frame = bytes\n      (first_frame[0] & 0x80).should_not eq(0x00) # FINAL bit set\n      (first_frame[0] & 0x0f).should eq(0x02)     # BINARY frame\n      first_frame[1].should eq(11)                # non-extended size\n      (bytes + 2).should eq \"hello world\".to_slice\n    end\n\n    it \"sets opcode of first frame to binary if stream is called with binary = true\" do\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.stream(binary: true) { |io| }\n\n      bytes = io.to_slice\n      (bytes[0] & 0x0f).should eq(0x02) # BINARY frame\n    end\n\n    it \"should not send frame if stream is flushed\" do\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.stream do |stream|\n        stream.write \"foo\".to_slice\n        stream.flush\n        io.size.should eq 0\n      end # flushed/sent here\n      bytes = io.to_slice\n      bytes.size.should eq 5 # 1 byte header, 1 byte size, 3 byte payload \"foo\"\n      first_frame = bytes\n      (first_frame[0] & 0x80).should_not eq(0x00) # FINAL bit set\n      (first_frame[0] & 0x0f).should eq(0x02)     # BINARY frame\n      first_frame[1].should eq(3)\n    end\n  end\n\n  describe \"send_masked\" do\n    it \"sends the data with a bitmask\" do\n      sent_string = \"hello\"\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io, masked: true)\n      ws.send(sent_string)\n      bytes = io.to_slice\n      bytes.size.should eq(11)     # 2 bytes header, 4 bytes mask, 5 bytes content\n      bytes[1].bit(7).should eq(1) # For mask bit\n      (bytes[1] - 128).should eq(sent_string.size)\n      (bytes[2] ^ bytes[6]).should eq('h'.ord)\n      (bytes[3] ^ bytes[7]).should eq('e'.ord)\n      (bytes[4] ^ bytes[8]).should eq('l'.ord)\n      (bytes[5] ^ bytes[9]).should eq('l'.ord)\n      (bytes[2] ^ bytes[10]).should eq('o'.ord)\n    end\n\n    it \"sends long data with correct header\" do\n      big_string = \"abcdefghijklmnopqrstuvwxyz\" * (IO::DEFAULT_BUFFER_SIZE // 4)\n      size = big_string.size\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io, masked: true)\n      ws.send(big_string)\n      bytes = io.to_slice\n      bytes.size.should eq(size + 14) # 2 bytes header, 8 bytes size, 4 bytes mask, UInt16::MAX + 1 bytes content\n      bytes[1].bit(7).should eq(1)    # For mask bit\n      (bytes[1] - 128).should eq(127)\n      received_size = 0\n      8.times { |i| received_size <<= 8; received_size += bytes[2 + i] }\n      received_size.should eq(size)\n      size.times do |i|\n        (bytes[14 + i] ^ bytes[10 + (i % 4)]).should eq(big_string[i].ord)\n      end\n    end\n  end\n\n  describe \"close\" do\n    it \"closes with code\" do\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.close(4020)\n      bytes = io.to_slice\n      (bytes[0] & 0x0f).should eq(0x08) # CLOSE frame\n      bytes[1].should eq(0x02)          # 2 bytes code\n      bytes[2].should eq(0x0f)\n      bytes[3].should eq(0xb4)\n    end\n\n    it \"closes with message\" do\n      message = \"bye\"\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.close(nil, message)\n      bytes = io.to_slice\n      (bytes[0] & 0x0f).should eq(0x08) # CLOSE frame\n      bytes[1].should eq(0x05)          # 2 + message.bytesize\n      bytes[2].should eq(0x03)\n      bytes[3].should eq(0xe8)\n      String.new(bytes[4..6]).should eq(message)\n    end\n\n    it \"closes with message and code\" do\n      message = \"4020\"\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.close(4020, message)\n      bytes = io.to_slice\n      (bytes[0] & 0x0f).should eq(0x08) # CLOSE frame\n      bytes[1].should eq(0x06)          # 2 + message.bytesize\n      bytes[2].should eq(0x0f)\n      bytes[3].should eq(0xb4)\n      String.new(bytes[4..7]).should eq(message)\n    end\n\n    it \"closes without message\" do\n      io = IO::Memory.new\n      ws = HTTP::WebSocket::Protocol.new(io)\n      ws.close\n      bytes = io.to_slice\n      (bytes[0] & 0x0f).should eq(0x08) # CLOSE frame\n      bytes[1].should eq(0x00)\n    end\n  end\n\n  each_ip_family do |family, local_address|\n    it \"negotiates over HTTP correctly\" do\n      address_chan = Channel(Socket::IPAddress).new\n      close_chan = Channel({Int32, String}).new\n\n      f = spawn do\n        http_ref = nil\n        ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n          ctx.request.path.should eq(\"/foo/bar\")\n          ctx.request.query_params[\"query\"].should eq(\"arg\")\n          ctx.request.query_params[\"yes\"].should eq(\"please\")\n\n          ws.on_message do |str|\n            ws.send(\"pong #{str}\")\n          end\n\n          ws.on_close do |code, message|\n            http_ref.not_nil!.close\n            close_chan.send({code.to_i, message})\n          end\n        end\n\n        http_server = http_ref = HTTP::Server.new([ws_handler])\n        address = http_server.bind_tcp(local_address, 0)\n        address_chan.send(address)\n        http_server.listen\n      end\n\n      listen_address = address_chan.receive\n      wait_until_blocked f\n\n      ws2 = HTTP::WebSocket.new(\"ws://#{listen_address}/foo/bar?query=arg&yes=please\")\n\n      random = Random::Secure.hex\n      ws2.on_message do |str|\n        str.should eq(\"pong #{random}\")\n        ws2.close(4020, \"close message\")\n      end\n      ws2.send(random)\n\n      ws2.run\n\n      code, message = close_chan.receive\n      code.should eq(4020)\n      message.should eq(\"close message\")\n    end\n\n    it \"negotiates over HTTPS correctly\" do\n      address_chan = Channel(Socket::IPAddress).new\n\n      server_context, client_context = ssl_context_pair\n\n      f = spawn do\n        http_ref = nil\n        ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n          ctx.request.path.should eq(\"/\")\n\n          ws.on_message do |str|\n            ws.send(\"pong #{str}\")\n            ws.close\n          end\n\n          ws.on_close do\n            http_ref.not_nil!.close\n          end\n        end\n\n        http_server = http_ref = HTTP::Server.new([ws_handler])\n\n        address = http_server.bind_tls(local_address, context: server_context)\n        address_chan.send(address)\n        http_server.listen\n      end\n\n      listen_address = address_chan.receive\n      wait_until_blocked f\n\n      ws2 = HTTP::WebSocket.new(listen_address.address, port: listen_address.port, path: \"/\", tls: client_context)\n\n      random = Random::Secure.hex\n      ws2.on_message do |str|\n        str.should eq(\"pong #{random}\")\n      end\n      ws2.send(random)\n\n      ws2.run\n    end\n  end\n\n  it \"sends correct HTTP basic auth header\" do\n    ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n      ws.send ctx.request.headers[\"Authorization\"]\n      ws.close\n    end\n    http_server = HTTP::Server.new([ws_handler])\n    address = http_server.bind_unused_port\n\n    run_server(http_server) do\n      client = HTTP::WebSocket.new(\"ws://test_username:test_password@#{address}\")\n      message = nil\n      client.on_message do |msg|\n        message = msg\n      end\n      client.run\n      message.should eq(\n        \"Basic #{Base64.strict_encode(\"test_username:test_password\")}\")\n    end\n  end\n\n  it \"handshake fails if server does not switch protocols\" do\n    http_server = HTTP::Server.new do |context|\n      context.response.status_code = 200\n    end\n\n    address = http_server.bind_unused_port\n\n    run_server(http_server) do\n      expect_raises(Socket::Error, \"Handshake got denied. Status code was 200.\") do\n        HTTP::WebSocket::Protocol.new(address.address, port: address.port, path: \"/\")\n      end\n    end\n  end\n\n  it \"ignores body in upgrade response (malformed)\" do\n    malformer = MalformerHandler.new\n    ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n      ws.on_message do |str|\n        ws.send(str)\n      end\n    end\n    http_server = HTTP::Server.new([malformer, ws_handler])\n\n    address = http_server.bind_unused_port\n\n    run_server(http_server) do\n      client = HTTP::WebSocket.new(\"ws://#{address}\")\n      message = nil\n      client.on_message do |msg|\n        message = msg\n        client.close\n      end\n      client.send \"hello\"\n      client.run\n      message.should eq(\"hello\")\n    end\n  end\n\n  it \"doesn't compress upgrade response body\" do\n    compress_handler = HTTP::CompressHandler.new\n    ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n      ws.on_message do |str|\n        ws.send(str)\n      end\n    end\n    http_server = HTTP::Server.new([compress_handler, ws_handler])\n\n    address = http_server.bind_unused_port\n\n    run_server(http_server) do\n      client = HTTP::WebSocket.new(\"ws://#{address}\", headers: HTTP::Headers{\"Accept-Encoding\" => \"gzip\"})\n      message = nil\n      client.on_message do |msg|\n        message = msg\n        client.close\n      end\n      client.send \"hello\"\n      client.run\n      message.should eq(\"hello\")\n    end\n  end\n\n  describe \"Sec-WebSocket-Protocol\" do\n    it \"fails handshake if server does not include Sec-WebSocket-Protocol in response when requested\" do\n      http_server = HTTP::Server.new do |context|\n        response = context.response\n        key = context.request.headers[\"Sec-WebSocket-Key\"]\n        response.status_code = 101\n        response.headers[\"Upgrade\"] = \"websocket\"\n        response.headers[\"Connection\"] = \"Upgrade\"\n        response.headers[\"Sec-WebSocket-Accept\"] = HTTP::WebSocket::Protocol.key_challenge(key) if key\n      end\n\n      address = http_server.bind_unused_port\n\n      run_server(http_server) do\n        expect_raises(Socket::Error, \"Handshake got denied. Server did not respond with Sec-WebSocket-Protocol.\") do\n          HTTP::WebSocket::Protocol.new(\n            address.address,\n            port: address.port,\n            path: \"/\",\n            protocols: [\"chat\"]\n          )\n        end\n      end\n    end\n\n    it \"fails handshake if server responds with non-requested Sec-WebSocket-Protocol\" do\n      http_server = HTTP::Server.new do |context|\n        response = context.response\n        key = context.request.headers[\"Sec-WebSocket-Key\"]\n        response.status_code = 101\n        response.headers[\"Upgrade\"] = \"websocket\"\n        response.headers[\"Connection\"] = \"Upgrade\"\n        response.headers[\"Sec-WebSocket-Accept\"] = HTTP::WebSocket::Protocol.key_challenge(key) if key\n        response.headers[\"Sec-WebSocket-Protocol\"] = \"video\"\n      end\n\n      address = http_server.bind_unused_port\n\n      run_server(http_server) do\n        expect_raises(Socket::Error, \"Handshake got denied. Server responded with an invalid Sec-WebSocket-Protocol.\") do\n          HTTP::WebSocket::Protocol.new(\n            address.address,\n            port: address.port,\n            path: \"/\",\n            protocols: [\"chat\"]\n          )\n        end\n      end\n    end\n\n    it \"accepts handshake if server responds with one of the requested Sec-WebSocket-Protocol\" do\n      http_server = HTTP::Server.new do |context|\n        response = context.response\n        key = context.request.headers[\"Sec-WebSocket-Key\"]\n        response.status_code = 101\n        response.headers[\"Upgrade\"] = \"websocket\"\n        response.headers[\"Connection\"] = \"Upgrade\"\n        response.headers[\"Sec-WebSocket-Accept\"] = HTTP::WebSocket::Protocol.key_challenge(key) if key\n        response.headers[\"Sec-WebSocket-Protocol\"] = \"chat\"\n      end\n\n      address = http_server.bind_unused_port\n\n      run_server(http_server) do\n        ws = HTTP::WebSocket::Protocol.new(\n          address.address,\n          port: address.port,\n          path: \"/\",\n          protocols: [\"chat\", \"video\"]\n        )\n        ws.protocol.should eq(\"chat\")\n      end\n    end\n\n    it \"accepts handshake if neither provided nor responded Sec-WebSocket-Protocol\" do\n      http_server = HTTP::Server.new do |context|\n        response = context.response\n        key = context.request.headers[\"Sec-WebSocket-Key\"]\n        response.status_code = 101\n        response.headers[\"Upgrade\"] = \"websocket\"\n        response.headers[\"Connection\"] = \"Upgrade\"\n        response.headers[\"Sec-WebSocket-Accept\"] = HTTP::WebSocket::Protocol.key_challenge(key) if key\n      end\n\n      address = http_server.bind_unused_port\n\n      run_server(http_server) do\n        HTTP::WebSocket::Protocol.new(\n          address.address,\n          port: address.port,\n          path: \"/\"\n        )\n      end\n    end\n  end\n\n  describe \"handshake fails if server does not verify Sec-WebSocket-Key\" do\n    it \"Sec-WebSocket-Accept missing\" do\n      http_server = HTTP::Server.new do |context|\n        response = context.response\n        response.status_code = 101\n        response.headers[\"Upgrade\"] = \"websocket\"\n        response.headers[\"Connection\"] = \"Upgrade\"\n      end\n\n      address = http_server.bind_unused_port\n\n      run_server(http_server) do\n        expect_raises(Socket::Error, \"Handshake got denied. Server did not verify WebSocket challenge.\") do\n          HTTP::WebSocket::Protocol.new(address.address, port: address.port, path: \"/\")\n        end\n      end\n    end\n\n    it \"Sec-WebSocket-Accept incorrect\" do\n      http_server = HTTP::Server.new do |context|\n        response = context.response\n        response.status_code = 101\n        response.headers[\"Upgrade\"] = \"websocket\"\n        response.headers[\"Connection\"] = \"Upgrade\"\n        response.headers[\"Sec-WebSocket-Accept\"] = \"foobar\"\n      end\n\n      address = http_server.bind_unused_port\n\n      run_server(http_server) do\n        expect_raises(Socket::Error, \"Handshake got denied. Server did not verify WebSocket challenge.\") do\n          HTTP::WebSocket::Protocol.new(address.address, port: address.port, path: \"/\")\n        end\n      end\n    end\n  end\n\n  typeof(HTTP::WebSocket.new(URI.parse(\"ws://localhost\")))\n  typeof(HTTP::WebSocket.new(\"localhost\", \"/\"))\n  typeof(HTTP::WebSocket.new(\"ws://localhost\"))\n  typeof(HTTP::WebSocket.new(URI.parse(\"ws://localhost\"), headers: HTTP::Headers{\"X-TEST_HEADER\" => \"some-text\"}))\nend\n\nprivate def integration_setup(&)\n  bin_ch = Channel(Bytes).new\n  txt_ch = Channel(String).new\n  ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n    ws.on_binary { |bytes| bin_ch.send bytes }\n    ws.on_message { |bytes| txt_ch.send bytes }\n  end\n  server = HTTP::Server.new [ws_handler]\n  address = server.bind_unused_port\n  spawn server.listen\n  wsoc = HTTP::WebSocket.new(\"http://#{address}\")\n\n  yield wsoc, bin_ch, txt_ch\nensure\n  server.close if server\nend\n\ndescribe \"Websocket integration tests\" do\n  # default frame size is 1024, but be explicit here in case the default changes in the future\n\n  it \"streams less than the buffer frame size\" do\n    integration_setup do |wsoc, bin_ch, _|\n      bytes = \"hello test world\".to_slice\n      wsoc.stream(frame_size: 1024, &.write(bytes))\n      received = bin_ch.receive\n      received.should eq bytes\n    end\n  end\n\n  it \"streams single messages more than the buffer frame size\" do\n    integration_setup do |wsoc, bin_ch, _|\n      bytes = (\"hello test world\" * 80).to_slice\n      bytes.size.should be > 1024\n      wsoc.stream(frame_size: 1024, &.write(bytes))\n      received = bin_ch.receive\n      received.should eq bytes\n    end\n  end\n\n  it \"streams single messages made up of multiple parts that eventually become more than the buffer frame size\" do\n    integration_setup do |wsoc, bin_ch, _|\n      bytes = \"hello test world\".to_slice\n      wsoc.stream(frame_size: 1024) { |io| 80.times { io.write bytes } }\n      received = bin_ch.receive\n      received.size.should be > 1024\n      received.should eq (\"hello test world\" * 80).to_slice\n    end\n  end\n\n  it \"sends single text messages\" do\n    integration_setup do |wsoc, _, txt_ch|\n      wsoc.send \"hello text\"\n      wsoc.send \"hello again\"\n      txt_ch.receive.should eq \"hello text\"\n      txt_ch.receive.should eq \"hello again\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/humanize_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/string\"\nrequire \"big\"\n\nprivate LENGTH_UNITS = ->(magnitude : Int32, number : Float64) do\n  case magnitude\n  when -2, -1 then {-2, \" cm\"}\n  when .>=(4)\n    {3, \" km\"}\n  else\n    magnitude = Number.prefix_index(magnitude)\n    {magnitude, \" #{Number.si_prefix(magnitude)}m\"}\n  end\nend\n\nprivate CUSTOM_PREFIXES = [['a'], ['b', 'c', 'd']]\n\ndescribe Number do\n  describe \"#format\" do\n    it { assert_prints 1.format, \"1\" }\n    it { assert_prints 12.format, \"12\" }\n    it { assert_prints 123.format, \"123\" }\n    it { assert_prints 1234.format, \"1,234\" }\n\n    it { assert_prints 1.format(decimal_places: 1), \"1.0\" }\n    it { assert_prints 1.format(decimal_places: 1, only_significant: true), \"1.0\" }\n\n    it { assert_prints 0.format(decimal_places: 1), \"0.0\" }\n    it { assert_prints 0.format(decimal_places: 1, only_significant: true), \"0.0\" }\n\n    it { assert_prints 0.0.format(decimal_places: 1), \"0.0\" }\n    it { assert_prints 0.0.format(decimal_places: 1, only_significant: true), \"0.0\" }\n\n    it { assert_prints 0.01.format(decimal_places: 1), \"0.0\" }\n\n    it { assert_prints 123.45.format, \"123.45\" }\n    it { assert_prints 123.45.format(separator: ','), \"123,45\" }\n    it { assert_prints 123.45.format(decimal_places: 3), \"123.450\" }\n    it { assert_prints 123.45.format(decimal_places: 3, only_significant: true), \"123.45\" }\n    it { assert_prints 123.4567.format(decimal_places: 3), \"123.457\" }\n\n    it { assert_prints 123_456.format, \"123,456\" }\n    it { assert_prints 123_456.format(delimiter: '.'), \"123.456\" }\n\n    it { assert_prints 123_456.789.format, \"123,456.789\" }\n\n    it { assert_prints 1e15.format(decimal_places: 7), \"1,000,000,000,000,000.0000000\" }\n    it { assert_prints 1e15.to_i64.format(decimal_places: 7), \"1,000,000,000,000,000.0000000\" }\n    it { assert_prints 1e-5.format(decimal_places: 7), \"0.0000100\" }\n    it { assert_prints 1e-4.format(decimal_places: 7), \"0.0001000\" }\n\n    it { assert_prints -1.format, \"-1\" }\n    it { assert_prints -12.format, \"-12\" }\n    it { assert_prints -123.format, \"-123\" }\n    it { assert_prints -1234.format, \"-1,234\" }\n\n    it { assert_prints -1.format(decimal_places: 1), \"-1.0\" }\n    it { assert_prints -1.format(decimal_places: 1, only_significant: true), \"-1.0\" }\n\n    it { assert_prints -0.0.format(decimal_places: 1), \"-0.0\" }\n    it { assert_prints -0.0.format(decimal_places: 1, only_significant: true), \"-0.0\" }\n\n    it { assert_prints -0.0_f32.format(decimal_places: 1), \"-0.0\" }\n    it { assert_prints -0.0_f32.format(decimal_places: 1, only_significant: true), \"-0.0\" }\n\n    it { assert_prints -0.01.format(decimal_places: 1), \"-0.0\" }\n\n    it { assert_prints -123.45.format, \"-123.45\" }\n    it { assert_prints -123.45.format(separator: ','), \"-123,45\" }\n    it { assert_prints -123.45.format(decimal_places: 3), \"-123.450\" }\n    it { assert_prints -123.45.format(decimal_places: 3, only_significant: true), \"-123.45\" }\n    it { assert_prints -123.4567.format(decimal_places: 3), \"-123.457\" }\n\n    it { assert_prints -123_456.format, \"-123,456\" }\n    it { assert_prints -123_456.format(delimiter: '.'), \"-123.456\" }\n\n    it { assert_prints -123_456.789.format, \"-123,456.789\" }\n\n    it { assert_prints -1e15.format(decimal_places: 7), \"-1,000,000,000,000,000.0000000\" }\n    it { assert_prints -1e15.to_i64.format(decimal_places: 7), \"-1,000,000,000,000,000.0000000\" }\n    it { assert_prints -1e-5.format(decimal_places: 7), \"-0.0000100\" }\n    it { assert_prints -1e-4.format(decimal_places: 7), \"-0.0001000\" }\n\n    it { assert_prints Float64::MAX.format, \"179,769,313,486,231,570,814,527,423,731,704,356,798,070,567,525,844,996,598,917,476,803,157,260,780,028,538,760,589,558,632,766,878,171,540,458,953,514,382,464,234,321,326,889,464,182,768,467,546,703,537,516,986,049,910,576,551,282,076,245,490,090,389,328,944,075,868,508,455,133,942,304,583,236,903,222,948,165,808,559,332,123,348,274,797,826,204,144,723,168,738,177,180,919,299,881,250,404,026,184,124,858,368.0\" }\n    it { assert_prints Float64::MIN.format, \"-179,769,313,486,231,570,814,527,423,731,704,356,798,070,567,525,844,996,598,917,476,803,157,260,780,028,538,760,589,558,632,766,878,171,540,458,953,514,382,464,234,321,326,889,464,182,768,467,546,703,537,516,986,049,910,576,551,282,076,245,490,090,389,328,944,075,868,508,455,133,942,304,583,236,903,222,948,165,808,559,332,123,348,274,797,826,204,144,723,168,738,177,180,919,299,881,250,404,026,184,124,858,368.0\" }\n    it { assert_prints Float64::MAX.format(decimal_places: 0), \"179,769,313,486,231,570,814,527,423,731,704,356,798,070,567,525,844,996,598,917,476,803,157,260,780,028,538,760,589,558,632,766,878,171,540,458,953,514,382,464,234,321,326,889,464,182,768,467,546,703,537,516,986,049,910,576,551,282,076,245,490,090,389,328,944,075,868,508,455,133,942,304,583,236,903,222,948,165,808,559,332,123,348,274,797,826,204,144,723,168,738,177,180,919,299,881,250,404,026,184,124,858,368\" }\n    it { assert_prints Float64::MIN.format(decimal_places: 0), \"-179,769,313,486,231,570,814,527,423,731,704,356,798,070,567,525,844,996,598,917,476,803,157,260,780,028,538,760,589,558,632,766,878,171,540,458,953,514,382,464,234,321,326,889,464,182,768,467,546,703,537,516,986,049,910,576,551,282,076,245,490,090,389,328,944,075,868,508,455,133,942,304,583,236,903,222,948,165,808,559,332,123,348,274,797,826,204,144,723,168,738,177,180,919,299,881,250,404,026,184,124,858,368\" }\n    it { assert_prints Float64::MIN_POSITIVE.format, \"0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014\" }\n    it { assert_prints (-Float64::MIN_POSITIVE).format, \"-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014\" }\n\n    it { assert_prints Float32::INFINITY.format, \"Infinity\" }\n    it { assert_prints (-Float32::INFINITY).format, \"-Infinity\" }\n    it { assert_prints Float32::NAN.format, \"NaN\" }\n\n    it { assert_prints Float64::INFINITY.format, \"Infinity\" }\n    it { assert_prints (-Float64::INFINITY).format, \"-Infinity\" }\n    it { assert_prints Float64::NAN.format, \"NaN\" }\n\n    it { assert_prints \"12345678.90123\".to_big_f.format, \"12,345,678.90123\" }\n    it { assert_prints \"12345678.90123\".to_big_f.format(decimal_places: 10), \"12,345,678.9012300000\" }\n    it { assert_prints \"12345678.90123\".to_big_f.format(decimal_places: -4), \"12,350,000\" }\n\n    it { assert_prints (2.to_big_f ** 58).format, \"288,230,376,151,711,744.0\" }\n    it { assert_prints (2.to_big_f ** 58).format(decimal_places: 10), \"288,230,376,151,711,744.0000000000\" }\n    it { assert_prints (2.to_big_f ** 58).format(decimal_places: -5), \"288,230,376,151,700,000\" }\n\n    it { assert_prints (2.to_big_f ** -16).format, \"0.0000152587890625\" }\n    it { assert_prints (2.to_big_f ** -16).format(decimal_places: 20), \"0.00001525878906250000\" }\n    it { assert_prints (2.to_big_f ** -16).format(decimal_places: 10), \"0.0000152588\" }\n\n    it { assert_prints \"12345.67890123456789012345\".to_big_d.format, \"12,345.67890123456789012345\" }\n    it { assert_prints \"12345.67890123456789012345\".to_big_d.format(decimal_places: 10), \"12,345.6789012346\" }\n    it { assert_prints \"12345.67890123456789012345\".to_big_d.format(decimal_places: -2), \"12,300\" }\n\n    it { assert_prints \"12345.67890123456789012345e+20\".to_big_d.format, \"1,234,567,890,123,456,789,012,345.0\" }\n    it { assert_prints \"12345.67890123456789012345e+20\".to_big_d.format(decimal_places: 10), \"1,234,567,890,123,456,789,012,345.0000000000\" }\n    it { assert_prints \"12345.67890123456789012345e+20\".to_big_d.format(decimal_places: -20), \"1,234,600,000,000,000,000,000,000\" }\n\n    it { assert_prints \"12345.67890123456789012345e-10\".to_big_d.format, \"0.000001234567890123456789012345\" }\n    it { assert_prints \"12345.67890123456789012345e-10\".to_big_d.format(decimal_places: 40), \"0.0000012345678901234567890123450000000000\" }\n    it { assert_prints \"12345.67890123456789012345e-10\".to_big_d.format(decimal_places: 10), \"0.0000012346\" }\n\n    it \"extracts integer part correctly (#12997)\" do\n      assert_prints 1.9999998.format, \"1.9999998\"\n      assert_prints 1111111.999999998.format, \"1,111,111.999999998\"\n    end\n\n    it \"does not perform double rounding when decimal places are given\" do\n      assert_prints 1.2345.format(decimal_places: 24), \"1.234499999999999930722083\"\n      assert_prints 1.2345.format(decimal_places: 65), \"1.23449999999999993072208326339023187756538391113281250000000000000\"\n      assert_prints 1.2345.format(decimal_places: 71), \"1.23449999999999993072208326339023187756538391113281250000000000000000000\"\n      assert_prints 1.2345.format(decimal_places: 83), \"1.23449999999999993072208326339023187756538391113281250000000000000000000000000000000\"\n      assert_prints 1.2345.format(decimal_places: 99), \"1.234499999999999930722083263390231877565383911132812500000000000000000000000000000000000000000000000\"\n    end\n  end\n\n  describe \"#humanize\" do\n    it { assert_prints 0.humanize, \"0.0\" }\n    it { assert_prints 1.humanize, \"1.0\" }\n    it { assert_prints (-1).humanize, \"-1.0\" }\n    it { assert_prints 99.humanize, \"99.0\" }\n    it { assert_prints 100.humanize, \"100\" }\n    it { assert_prints 101.humanize, \"101\" }\n    it { assert_prints 123.humanize, \"123\" }\n    it { assert_prints 123.humanize(2), \"120\" }\n    it { assert_prints 999.humanize, \"999\" }\n    it { assert_prints 1000.humanize, \"1.0k\" }\n    it { assert_prints 1001.humanize, \"1.0k\" }\n    it { assert_prints 1234.humanize, \"1.23k\" }\n    it { assert_prints 12_345.humanize, \"12.3k\" }\n    it { assert_prints 12_345.humanize(2), \"12k\" }\n    it { assert_prints 1_234_567.humanize, \"1.23M\" }\n    it { assert_prints 1_234_567.humanize(5), \"1.2346M\" }\n    it { assert_prints 12_345_678.humanize(5), \"12.346M\" }\n    it { assert_prints 0.012_345.humanize, \"12.3m\" }\n    it { assert_prints 0.001_234_5.humanize, \"1.23m\" }\n    it { assert_prints 0.000_000_012_345.humanize, \"12.3n\" }\n    it { assert_prints 0.000_000_001.humanize, \"1.0n\" }\n    it { assert_prints 0.000_000_001_235.humanize, \"1.24n\" }\n    it { assert_prints 0.123_456_78.humanize, \"123m\" }\n    it { assert_prints 0.123_456_78.humanize(5), \"123.46m\" }\n\n    it { assert_prints 1.0e-35.humanize, \"0.00001q\" }\n    it { assert_prints 1.0e-34.humanize, \"0.0001q\" }\n    it { assert_prints 1.0e-33.humanize, \"0.001q\" }\n    it { assert_prints 1.0e-32.humanize, \"0.01q\" }\n    it { assert_prints 1.0e-31.humanize, \"0.1q\" }\n    it { assert_prints 1.0e-30.humanize, \"1.0q\" }\n    it { assert_prints 1.0e-29.humanize, \"10.0q\" }\n    it { assert_prints 1.0e-28.humanize, \"100q\" }\n    it { assert_prints 1.0e-27.humanize, \"1.0r\" }\n    it { assert_prints 1.0e-26.humanize, \"10.0r\" }\n    it { assert_prints 1.0e-25.humanize, \"100r\" }\n    it { assert_prints 1.0e-24.humanize, \"1.0y\" }\n    it { assert_prints 1.0e-23.humanize, \"10.0y\" }\n    it { assert_prints 1.0e-22.humanize, \"100y\" }\n    it { assert_prints 1.0e-21.humanize, \"1.0z\" }\n    it { assert_prints 1.0e-20.humanize, \"10.0z\" }\n    it { assert_prints 1.0e-19.humanize, \"100z\" }\n    it { assert_prints 1.0e-18.humanize, \"1.0a\" }\n    it { assert_prints 1.0e-17.humanize, \"10.0a\" }\n    it { assert_prints 1.0e-16.humanize, \"100a\" }\n    it { assert_prints 1.0e-15.humanize, \"1.0f\" }\n    it { assert_prints 1.0e-14.humanize, \"10.0f\" }\n    it { assert_prints 1.0e-13.humanize, \"100f\" }\n    it { assert_prints 1.0e-12.humanize, \"1.0p\" }\n    it { assert_prints 1.0e-11.humanize, \"10.0p\" }\n    it { assert_prints 1.0e-10.humanize, \"100p\" }\n    it { assert_prints 1.0e-9.humanize, \"1.0n\" }\n    it { assert_prints 1.0e-8.humanize, \"10.0n\" }\n    it { assert_prints 1.0e-7.humanize, \"100n\" }\n    it { assert_prints 1.0e-6.humanize, \"1.0µ\" }\n    it { assert_prints 1.0e-5.humanize, \"10.0µ\" }\n    it { assert_prints 1.0e-4.humanize, \"100µ\" }\n    it { assert_prints 1.0e-3.humanize, \"1.0m\" }\n    it { assert_prints 1.0e-2.humanize, \"10.0m\" }\n    it { assert_prints 1.0e-1.humanize, \"100m\" }\n    it { assert_prints 1.0e+0.humanize, \"1.0\" }\n    it { assert_prints 1.0e+1.humanize, \"10.0\" }\n    it { assert_prints 1.0e+2.humanize, \"100\" }\n    it { assert_prints 1.0e+3.humanize, \"1.0k\" }\n    it { assert_prints 1.0e+4.humanize, \"10.0k\" }\n    it { assert_prints 1.0e+5.humanize, \"100k\" }\n    it { assert_prints 1.0e+6.humanize, \"1.0M\" }\n    it { assert_prints 1.0e+7.humanize, \"10.0M\" }\n    it { assert_prints 1.0e+8.humanize, \"100M\" }\n    it { assert_prints 1.0e+9.humanize, \"1.0G\" }\n    it { assert_prints 1.0e+10.humanize, \"10.0G\" }\n    it { assert_prints 1.0e+11.humanize, \"100G\" }\n    it { assert_prints 1.0e+12.humanize, \"1.0T\" }\n    it { assert_prints 1.0e+13.humanize, \"10.0T\" }\n    it { assert_prints 1.0e+14.humanize, \"100T\" }\n    it { assert_prints 1.0e+15.humanize, \"1.0P\" }\n    it { assert_prints 1.0e+16.humanize, \"10.0P\" }\n    it { assert_prints 1.0e+17.humanize, \"100P\" }\n    it { assert_prints 1.0e+18.humanize, \"1.0E\" }\n    it { assert_prints 1.0e+19.humanize, \"10.0E\" }\n    it { assert_prints 1.0e+20.humanize, \"100E\" }\n    it { assert_prints 1.0e+21.humanize, \"1.0Z\" }\n    it { assert_prints 1.0e+22.humanize, \"10.0Z\" }\n    it { assert_prints 1.0e+23.humanize, \"100Z\" }\n    it { assert_prints 1.0e+24.humanize, \"1.0Y\" }\n    it { assert_prints 1.0e+25.humanize, \"10.0Y\" }\n    it { assert_prints 1.0e+26.humanize, \"100Y\" }\n    it { assert_prints 1.0e+27.humanize, \"1.0R\" }\n    it { assert_prints 1.0e+28.humanize, \"10.0R\" }\n    it { assert_prints 1.0e+29.humanize, \"100R\" }\n    it { assert_prints 1.0e+30.humanize, \"1.0Q\" }\n    it { assert_prints 1.0e+31.humanize, \"10.0Q\" }\n    it { assert_prints 1.0e+32.humanize, \"100Q\" }\n    it { assert_prints 1.0e+33.humanize, \"1,000Q\" }\n    it { assert_prints 1.0e+34.humanize, \"10,000Q\" }\n    it { assert_prints 1.0e+35.humanize, \"100,000Q\" }\n\n    it { assert_prints 0.humanize(unit_separator: '_'), \"0.0\" }\n    it { assert_prints 0.123_456_78.humanize(5, unit_separator: '\\u00A0'), \"123.46\\u00A0m\" }\n    it { assert_prints 1.0e-14.humanize(unit_separator: ' '), \"10.0 f\" }\n    it { assert_prints 0.000_001.humanize(unit_separator: '\\u2009'), \"1.0\\u2009µ\" }\n    it { assert_prints 1_000_000_000_000.humanize(unit_separator: \"__\"), \"1.0__T\" }\n    it { assert_prints 0.000_000_001.humanize(unit_separator: \".\"), \"1.0.n\" }\n    it { assert_prints 1.0e+9.humanize(unit_separator: \"\\t\"), \"1.0\\tG\" }\n    it { assert_prints 123_456_789_012.humanize(unit_separator: 0), \"1230G\" }\n    it { assert_prints 123_456_789_012.humanize(unit_separator: nil), \"123G\" }\n\n    it { assert_prints Float32::INFINITY.humanize, \"Infinity\" }\n    it { assert_prints (-Float32::INFINITY).humanize, \"-Infinity\" }\n    it { assert_prints Float32::NAN.humanize, \"NaN\" }\n\n    it { assert_prints Float64::INFINITY.humanize, \"Infinity\" }\n    it { assert_prints (-Float64::INFINITY).humanize, \"-Infinity\" }\n    it { assert_prints Float64::NAN.humanize, \"NaN\" }\n\n    it { assert_prints 1_234.567_890_123.humanize(precision: 2, significant: false), \"1.23k\" }\n    it { assert_prints 123.456_789_012_3.humanize(precision: 2, significant: false), \"123.46\" }\n    it { assert_prints 12.345_678_901_23.humanize(precision: 2, significant: false), \"12.35\" }\n    it { assert_prints 1.234_567_890_123.humanize(precision: 2, significant: false), \"1.23\" }\n\n    it { assert_prints 0.123_456_789_012.humanize(precision: 2, significant: false), \"123.46m\" }\n    it { assert_prints 0.012_345_678_901.humanize(precision: 2, significant: false), \"12.35m\" }\n    it { assert_prints 0.001_234_567_890.humanize(precision: 2, significant: false), \"1.23m\" }\n\n    it { assert_prints 0.000_123_456_789.humanize(precision: 2, significant: false), \"123.46µ\" }\n    it { assert_prints 0.000_012_345_678.humanize(precision: 2, significant: false), \"12.35µ\" }\n    it { assert_prints 0.000_001_234_567.humanize(precision: 2, significant: false), \"1.23µ\" }\n\n    it { assert_prints 0.000_000_123_456.humanize(precision: 2, significant: false), \"123.46n\" }\n    it { assert_prints 0.000_000_012_345.humanize(precision: 2, significant: false), \"12.34n\" }\n    it { assert_prints 0.000_000_001_234.humanize(precision: 2, significant: false), \"1.23n\" }\n    it { assert_prints 0.000_000_000_123.humanize(precision: 2, significant: false), \"123.00p\" }\n\n    describe \"using custom prefixes\" do\n      it { assert_prints 1_420_000_000.humanize(prefixes: LENGTH_UNITS), \"1,420,000 km\" }\n      it { assert_prints 1_420.humanize(prefixes: LENGTH_UNITS), \"1.42 km\" }\n      it { assert_prints 1.humanize(prefixes: LENGTH_UNITS), \"1.0 m\" }\n      it { assert_prints 0.1.humanize(prefixes: LENGTH_UNITS), \"10.0 cm\" }\n      it { assert_prints 0.01.humanize(prefixes: LENGTH_UNITS), \"1.0 cm\" }\n      it { assert_prints 0.001.humanize(prefixes: LENGTH_UNITS), \"1.0 mm\" }\n      it { assert_prints 0.000_01.humanize(prefixes: LENGTH_UNITS), \"10.0 µm\" }\n      it { assert_prints 0.000_000_001.humanize(prefixes: LENGTH_UNITS), \"1.0 nm\" }\n\n      it { assert_prints 1.0e-7.humanize(prefixes: CUSTOM_PREFIXES), \"0.0001a\" }\n      it { assert_prints 1.0e-6.humanize(prefixes: CUSTOM_PREFIXES), \"0.001a\" }\n      it { assert_prints 1.0e-5.humanize(prefixes: CUSTOM_PREFIXES), \"0.01a\" }\n      it { assert_prints 1.0e-4.humanize(prefixes: CUSTOM_PREFIXES), \"0.1a\" }\n      it { assert_prints 1.0e-3.humanize(prefixes: CUSTOM_PREFIXES), \"1.0a\" }\n      it { assert_prints 1.0e-2.humanize(prefixes: CUSTOM_PREFIXES), \"10.0a\" }\n      it { assert_prints 1.0e-1.humanize(prefixes: CUSTOM_PREFIXES), \"100a\" }\n      it { assert_prints 1.0e+0.humanize(prefixes: CUSTOM_PREFIXES), \"1.0b\" }\n      it { assert_prints 1.0e+1.humanize(prefixes: CUSTOM_PREFIXES), \"10.0b\" }\n      it { assert_prints 1.0e+2.humanize(prefixes: CUSTOM_PREFIXES), \"100b\" }\n      it { assert_prints 1.0e+3.humanize(prefixes: CUSTOM_PREFIXES), \"1.0c\" }\n      it { assert_prints 1.0e+4.humanize(prefixes: CUSTOM_PREFIXES), \"10.0c\" }\n      it { assert_prints 1.0e+5.humanize(prefixes: CUSTOM_PREFIXES), \"100c\" }\n      it { assert_prints 1.0e+6.humanize(prefixes: CUSTOM_PREFIXES), \"1.0d\" }\n      it { assert_prints 1.0e+7.humanize(prefixes: CUSTOM_PREFIXES), \"10.0d\" }\n      it { assert_prints 1.0e+8.humanize(prefixes: CUSTOM_PREFIXES), \"100d\" }\n      it { assert_prints 1.0e+9.humanize(prefixes: CUSTOM_PREFIXES), \"1,000d\" }\n      it { assert_prints 1.0e+10.humanize(prefixes: CUSTOM_PREFIXES), \"10,000d\" }\n      it { assert_prints 1.0e+10.humanize(prefixes: CUSTOM_PREFIXES, unit_separator: '\\u00A0'), \"10,000\\u00A0d\" }\n    end\n  end\nend\n\ndescribe Int do\n  describe \"#humanize_bytes\" do\n    # default IEC\n    it { assert_prints 1024.humanize_bytes, \"1.0kiB\" }\n\n    it { assert_prints 0.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"0B\" }\n    it { assert_prints 1.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1B\" }\n    it { assert_prints 999.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"999B\" }\n    it { assert_prints 1000.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"0.98KB\" }\n    it { assert_prints 1001.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"0.98KB\" }\n    it { assert_prints 1014.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"0.99KB\" }\n    it { assert_prints 1015.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0KB\" }\n    it { assert_prints 1024.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0KB\" }\n    it { assert_prints 1025.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0KB\" }\n    it { assert_prints 1026.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.01KB\" }\n    it { assert_prints 2048.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"2.0KB\" }\n    it { assert_prints 2048.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC, unit_separator: '\\u202F'), \"2.0\\u202FKB\" }\n\n    it { assert_prints 1536.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.5KB\" }\n    it { assert_prints 524288.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"512KB\" }\n    it { assert_prints 1048576.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0MB\" }\n    it { assert_prints 1073741824.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0GB\" }\n    it { assert_prints 1099511627776.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0TB\" }\n    it { assert_prints 1125899906842624.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0PB\" }\n    it { assert_prints 1152921504606846976.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC), \"1.0EB\" }\n    it { assert_prints 1.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC, unit_separator: '\\u2009'), \"1\\u2009B\" }\n    it { assert_prints 1152921504606846976.humanize_bytes(format: Int::BinaryPrefixFormat::JEDEC, unit_separator: '\\u2009'), \"1.0\\u2009EB\" }\n\n    it { assert_prints 1024.humanize_bytes(format: Int::BinaryPrefixFormat::IEC), \"1.0kiB\" }\n    it { assert_prints 1073741824.humanize_bytes(format: Int::BinaryPrefixFormat::IEC), \"1.0GiB\" }\n  end\nend\n"
  },
  {
    "path": "spec/std/indexable/mutable_spec.cr",
    "content": "require \"spec\"\n\nprivate class SafeIndexableMutable\n  include Indexable::Mutable(Int32)\n\n  getter size\n\n  # prevents `@values` from being interpretable as a raw slice of elements\n  @values : Array(Array(Int32))\n\n  def initialize(@size : Int32, *, offset = 0)\n    @values = Array.new(size) { |i| [i + offset] }\n  end\n\n  def unsafe_fetch(index)\n    raise IndexError.new unless 0 <= index < size\n    @values[index][0]\n  end\n\n  def unsafe_put(index, value : Int32)\n    raise IndexError.new unless 0 <= index < size\n    @values[index] = [value]\n  end\nend\n\nprivate class Foo\nend\n\nprivate class SafeIndexableMutableFoo\n  include Indexable::Mutable(Foo)\n\n  getter size\n\n  @values : Array(Array(Foo))\n\n  def initialize(@size : Int32)\n    @values = Array.new(size) { [Foo.new] }\n  end\n\n  def unsafe_fetch(index)\n    raise IndexError.new unless 0 <= index < size\n    @values[index][0]\n  end\n\n  def unsafe_put(index, value : Foo)\n    raise IndexError.new unless 0 <= index < size\n    @values[index] = [value]\n  end\nend\n\ndescribe Indexable::Mutable do\n  # General note: code that tests `#method!` must not rely on the results of\n  # `#method`, as otherwise those tests would trivially pass if the latter were\n  # implemented as `to_a.method!`\n\n  describe \"#[]=\" do\n    it \"sets the value at the given index\" do\n      coll = SafeIndexableMutable.new(5)\n      (coll[2] = 123).should eq(123)\n      coll[2].should eq(123)\n\n      coll = SafeIndexableMutableFoo.new(5)\n      foo = Foo.new\n      (coll[2] = foo).should be(foo)\n      coll[2].should be(foo)\n    end\n\n    it \"wraps negative indices\" do\n      coll = SafeIndexableMutable.new(5)\n      (coll[-2] = 123).should eq(123)\n      coll[3].should eq(123)\n\n      (coll[-5] = 456).should eq(456)\n      coll[0].should eq(456)\n    end\n\n    it \"raises on out-of-bound indices\" do\n      expect_raises(IndexError) { SafeIndexableMutable.new(5)[5] = 0 }\n      expect_raises(IndexError) { SafeIndexableMutable.new(5)[-6] = 0 }\n    end\n  end\n\n  describe \"#update\" do\n    it \"updates the value at the given index with the block\" do\n      coll = SafeIndexableMutable.new(5)\n      coll.update(2) { |x| x + 7 }.should eq(9)\n      coll[2].should eq(9)\n\n      coll = SafeIndexableMutableFoo.new(5)\n      foo = Foo.new\n      coll.update(2) { foo }.should be(foo)\n      coll[2].should be(foo)\n    end\n\n    it \"wraps negative indices\" do\n      coll = SafeIndexableMutable.new(5)\n      coll.update(-2) { |x| x + 7 }.should eq(10)\n      coll[3].should eq(10)\n\n      coll.update(-5) { |x| x + 14 }.should eq(14)\n      coll[0].should eq(14)\n    end\n\n    it \"raises on out-of-bound indices\" do\n      expect_raises(IndexError) { SafeIndexableMutable.new(5).update(5, &.itself) }\n      expect_raises(IndexError) { SafeIndexableMutable.new(5).update(-6, &.itself) }\n    end\n  end\n\n  describe \"#swap\" do\n    it \"exchanges the values at two indices\" do\n      coll = SafeIndexableMutable.new(12, offset: 50)\n      coll.swap(2, 7).should be(coll)\n      coll[2].should eq(57)\n      coll[7].should eq(52)\n\n      coll = SafeIndexableMutableFoo.new(12)\n      foo2 = coll[2]\n      foo7 = coll[7]\n      coll.swap(2, 7).should be(coll)\n      coll[2].should be(foo7)\n      coll[7].should be(foo2)\n    end\n\n    it \"wraps negative indices\" do\n      coll = SafeIndexableMutable.new(12, offset: 50)\n      coll.swap(-3, -7).should be(coll)\n      coll[9].should eq(55)\n      coll[5].should eq(59)\n    end\n\n    it \"raises on out-of-bound indices\" do\n      expect_raises(IndexError) { SafeIndexableMutable.new(5).swap(5, 5) }\n      expect_raises(IndexError) { SafeIndexableMutable.new(5).swap(-6, -6) }\n    end\n  end\n\n  describe \"#reverse!\" do\n    it \"reverses the order of all elements in place\" do\n      coll = SafeIndexableMutable.new(5)\n      coll.reverse!.should be(coll)\n      coll.to_a.should eq([4, 3, 2, 1, 0])\n\n      coll = SafeIndexableMutable.new(8)\n      coll.reverse!.should be(coll)\n      coll.to_a.should eq([7, 6, 5, 4, 3, 2, 1, 0])\n\n      coll = SafeIndexableMutableFoo.new(5)\n      foos = coll.to_a\n      coll.reverse!.should be(coll)\n      coll.to_a.should eq([foos[4], foos[3], foos[2], foos[1], foos[0]])\n\n      coll = SafeIndexableMutableFoo.new(8)\n      foos = coll.to_a\n      coll.reverse!.should be(coll)\n      coll.to_a.should eq([foos[7], foos[6], foos[5], foos[4], foos[3], foos[2], foos[1], foos[0]])\n    end\n  end\n\n  describe \"#fill\" do\n    context \"without block\" do\n      it \"sets all elements to the same value\" do\n        coll = SafeIndexableMutable.new(5)\n        coll.fill(4).should be(coll)\n        coll.to_a.should eq([4, 4, 4, 4, 4])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foo = Foo.new\n        coll.fill(foo).should be(coll)\n        coll.to_a.should eq([foo, foo, foo, foo, foo])\n      end\n    end\n\n    context \"without block, with start + count\" do\n      it \"sets a subrange of elements to the same value\" do\n        coll = SafeIndexableMutable.new(5)\n        coll.fill(7, 2, 2).should be(coll)\n        coll.to_a.should eq([0, 1, 7, 7, 4])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foos = coll.to_a\n        foo = Foo.new\n        coll.fill(foo, -4, 2).should be(coll)\n        coll.to_a.should eq([foos[0], foo, foo, foos[3], foos[4]])\n      end\n\n      it \"sets zero elements\" do\n        coll = SafeIndexableMutable.new(5)\n        coll.fill(7, 1, 0).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, 5, 0).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, -3, 0).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, -5, 0).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, 4, -1).to_a.should eq([0, 1, 2, 3, 4])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foo = Foo.new\n        coll.fill(foo, 1, 0).should_not contain(foo)\n        coll.fill(foo, 5, 0).should_not contain(foo)\n        coll.fill(foo, -3, 0).should_not contain(foo)\n        coll.fill(foo, -5, 0).should_not contain(foo)\n        coll.fill(foo, 4, -1).should_not contain(foo)\n      end\n\n      it \"raises on out of bound start index\" do\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(7, 6, 1) }\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(7, -6, 1) }\n      end\n    end\n\n    context \"without block, with range\" do\n      it \"sets a subrange of elements to the same value\" do\n        coll = SafeIndexableMutable.new(5)\n        coll.fill(7, 2..3).should be(coll)\n        coll.to_a.should eq([0, 1, 7, 7, 4])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foos = coll.to_a\n        foo = Foo.new\n        coll.fill(foo, -4...-2).should be(coll)\n        coll.to_a.should eq([foos[0], foo, foo, foos[3], foos[4]])\n      end\n\n      it \"sets zero elements\" do\n        coll = SafeIndexableMutable.new(5)\n        coll.fill(7, 1..0).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, 5...5).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, -3...1).to_a.should eq([0, 1, 2, 3, 4])\n        coll.fill(7, -5..-6).to_a.should eq([0, 1, 2, 3, 4])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foo = Foo.new\n        coll.fill(foo, 1..0).should_not contain(foo)\n        coll.fill(foo, 5...5).should_not contain(foo)\n        coll.fill(foo, -3...1).should_not contain(foo)\n        coll.fill(foo, -5..-6).should_not contain(foo)\n      end\n\n      it \"raises on out of bound start index\" do\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(7, 6..7) }\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(7, -6..-1) }\n      end\n    end\n\n    context \"with block\" do\n      it \"yields index to the block and sets all elements\" do\n        coll = SafeIndexableMutable.new(5, offset: 10)\n        coll.fill { |i| i * i }.should be(coll)\n        coll.to_a.should eq([0, 1, 4, 9, 16])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foo = Foo.new\n        coll.fill { foo }.should be(coll)\n        coll.to_a.should eq([foo, foo, foo, foo, foo])\n      end\n    end\n\n    context \"with block + offset\" do\n      it \"yields index plus offset to the block and sets all elements\" do\n        coll = SafeIndexableMutable.new(5, offset: 10)\n        coll.fill(offset: 7) { |i| i * i }.should be(coll)\n        coll.to_a.should eq([49, 64, 81, 100, 121])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foos = coll.to_a\n        coll.fill(offset: -2) { |i| foos[i] }.should be(coll)\n        coll.to_a.should eq([foos[-2], foos[-1], foos[0], foos[1], foos[2]])\n      end\n    end\n\n    context \"with block + start + count\" do\n      it \"yields index to the block and sets elements in a subrange\" do\n        coll = SafeIndexableMutable.new(5, offset: 10)\n        coll.fill(2, 2) { |i| i + 7 }.should be(coll)\n        coll.to_a.should eq([10, 11, 9, 10, 14])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foos = coll.to_a\n        foo = Foo.new\n        coll.fill(-4, 2) { foo }.should be(coll)\n        coll.to_a.should eq([foos[0], foo, foo, foos[3], foos[4]])\n      end\n\n      it \"sets zero elements\" do\n        coll = SafeIndexableMutable.new(5, offset: 10)\n        coll.fill(1, 0) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(5, 0) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(-3, 0) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(-5, 0) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(4, -1) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foo = Foo.new\n        coll.fill(1, 0) { foo }.should_not contain(foo)\n        coll.fill(5, 0) { foo }.should_not contain(foo)\n        coll.fill(-3, 0) { foo }.should_not contain(foo)\n        coll.fill(-5, 0) { foo }.should_not contain(foo)\n        coll.fill(4, -1) { foo }.should_not contain(foo)\n      end\n\n      it \"raises on out of bound start index\" do\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(6, 1, &.itself) }\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(-6, 1, &.itself) }\n      end\n    end\n\n    context \"with block + range\" do\n      it \"yields index to the block and sets elements in a subrange\" do\n        coll = SafeIndexableMutable.new(5, offset: 10)\n        coll.fill(2..3) { |i| i + 7 }.should be(coll)\n        coll.to_a.should eq([10, 11, 9, 10, 14])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foos = coll.to_a\n        foo = Foo.new\n        coll.fill(-4...-2) { foo }.should be(coll)\n        coll.to_a.should eq([foos[0], foo, foo, foos[3], foos[4]])\n      end\n\n      it \"sets zero elements\" do\n        coll = SafeIndexableMutable.new(5, offset: 10)\n        coll.fill(1..0) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(5...5) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(-3...1) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n        coll.fill(-5..-6) { |i| i + 7 }.to_a.should eq([10, 11, 12, 13, 14])\n\n        coll = SafeIndexableMutableFoo.new(5)\n        foo = Foo.new\n        coll.fill(1..0) { foo }.should_not contain(foo)\n        coll.fill(5...5) { foo }.should_not contain(foo)\n        coll.fill(-3...1) { foo }.should_not contain(foo)\n        coll.fill(-5..-6) { foo }.should_not contain(foo)\n      end\n\n      it \"raises on out of bound start index\" do\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(6..7, &.itself) }\n        expect_raises(IndexError) { SafeIndexableMutable.new(5).fill(-6..-1, &.itself) }\n      end\n    end\n  end\n\n  describe \"#map!\" do\n    it \"replaces each element with the block value\" do\n      coll = SafeIndexableMutable.new(5, offset: 2)\n      coll.map! { |i| 10 - i }\n      coll.to_a.should eq([8, 7, 6, 5, 4])\n\n      coll = SafeIndexableMutableFoo.new(5)\n      foos = coll.to_a\n      coll.map! &.itself\n      coll.to_a.should eq(foos)\n    end\n  end\n\n  describe \"#map_with_index!\" do\n    context \"without offset\" do\n      it \"yields each element and index to the block\" do\n        coll = SafeIndexableMutable.new(5)\n        coll[2] = 4\n        coll[4] = 7\n        coll.map_with_index! { |v, i| v * 100 + i }\n        coll.to_a.should eq([0, 101, 402, 303, 704])\n      end\n    end\n\n    context \"with offset\" do\n      it \"yields each element and index plus offset to the block\" do\n        coll = SafeIndexableMutable.new(5)\n        coll[2] = 4\n        coll[4] = 7\n        coll.map_with_index!(offset: 4) { |v, i| v * 100 + i }\n        coll.to_a.should eq([4, 105, 406, 307, 708])\n      end\n    end\n  end\n\n  describe \"#shuffle!\" do\n    it \"randomizes the order of all elements\" do\n      coll = SafeIndexableMutable.new(5)\n      coll.shuffle!(Random.new(42)).should be(coll)\n      coll.to_a.should eq([2, 1, 3, 4, 0])\n\n      coll = SafeIndexableMutableFoo.new(5)\n      foos = coll.to_a\n      coll.shuffle!(Random.new(42)).should be(coll)\n      coll.to_a.should eq([foos[2], foos[1], foos[3], foos[4], foos[0]])\n    end\n  end\n\n  describe \"#rotate!\" do\n    it \"left-shifts all elements\" do\n      coll = SafeIndexableMutable.new(5)\n      coll.rotate!(2).should be(coll)\n      coll.to_a.should eq([2, 3, 4, 0, 1])\n\n      coll = SafeIndexableMutableFoo.new(5)\n      foos = coll.to_a\n      coll.rotate!(3).should be(coll)\n      coll.to_a.should eq([foos[3], foos[4], foos[0], foos[1], foos[2]])\n\n      SafeIndexableMutable.new(6).rotate!(2).to_a.should eq([2, 3, 4, 5, 0, 1])\n      SafeIndexableMutable.new(6).rotate!(0).to_a.should eq([0, 1, 2, 3, 4, 5])\n      SafeIndexableMutable.new(6).rotate!(12).to_a.should eq([0, 1, 2, 3, 4, 5])\n      SafeIndexableMutable.new(6).rotate!(-1).to_a.should eq([5, 0, 1, 2, 3, 4])\n      SafeIndexableMutable.new(10).rotate!(6).to_a.should eq([6, 7, 8, 9, 0, 1, 2, 3, 4, 5])\n      SafeIndexableMutable.new(10).rotate!(12345678).to_a.should eq([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])\n      SafeIndexableMutable.new(10).rotate!(-1234567).to_a.should eq([3, 4, 5, 6, 7, 8, 9, 0, 1, 2])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/indexable_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\nmodule OtherInterface; end\n\nprivate record Three do\n  include OtherInterface\nend\n\nprivate record Four do\n  include OtherInterface\nend\n\nprivate struct InterfaceIndexable\n  include Indexable(OtherInterface)\n\n  def size\n    2\n  end\n\n  def unsafe_fetch(index : Int) : OtherInterface\n    case index\n    when 0 then Three.new\n    when 1 then Four.new\n    else\n      raise \"\"\n    end\n  end\nend\n\nprivate class SafeIndexable\n  include Indexable(Int32)\n\n  property size\n\n  def initialize(@size : Int32, @offset = 0_i32)\n  end\n\n  def unsafe_fetch(index) : Int32\n    raise IndexError.new unless 0 <= index < size\n    (index + @offset).to_i\n  end\nend\n\nprivate class SafeNestedIndexable\n  include Indexable(Indexable(Int32))\n\n  property size\n\n  def initialize(@size : Int32, @inner_size : Int32)\n  end\n\n  def unsafe_fetch(index)\n    raise IndexError.new unless 0 <= index < size\n    SafeIndexable.new(@inner_size)\n  end\nend\n\nprivate class SafeStringIndexable\n  include Indexable(String)\n\n  property size\n\n  def initialize(@size : Int32)\n  end\n\n  def unsafe_fetch(index) : String\n    raise IndexError.new unless 0 <= index < size\n    index.to_s\n  end\nend\n\nprivate class SafeMixedIndexable\n  include Indexable(String | Int32)\n\n  property size\n\n  def initialize(@size : Int32)\n  end\n\n  def unsafe_fetch(index) : String | Int32\n    raise IndexError.new unless 0 <= index < size\n    index.to_s\n  end\nend\n\nprivate class SafeRecursiveIndexable\n  include Indexable(SafeRecursiveIndexable | Int32)\n\n  property size\n\n  def initialize(@size : Int32)\n  end\n\n  def unsafe_fetch(index) : SafeRecursiveIndexable | Int32\n    raise IndexError.new unless 0 <= index < size\n    if (index % 2) == 0\n      SafeRecursiveIndexable.new(index)\n    else\n      index\n    end\n  end\nend\n\ndescribe Indexable do\n  describe \"#index\" do\n    it \"does index with big negative offset\" do\n      indexable = SafeIndexable.new(3)\n      indexable.index(0, -100).should be_nil\n    end\n\n    it \"does index with big offset\" do\n      indexable = SafeIndexable.new(3)\n      indexable.index(0, 100).should be_nil\n    end\n\n    it \"offset type\" do\n      indexable = SafeIndexable.new(3)\n      indexable.index(1, 0_i64).should eq 1\n      indexable.index(1, 0_i64).should be_a(Int64)\n    end\n  end\n\n  describe \"#index!\" do\n    it \"offset type\" do\n      indexable = SafeIndexable.new(3)\n      indexable.index!(1, 0_i64).should eq 1\n      indexable.index!(1, 0_i64).should be_a(Int64)\n    end\n\n    it \"raises if no element is found\" do\n      indexable = SafeIndexable.new(3)\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(0, -100) }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(0, -4) }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(0, 1) }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(0, 3) }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(0, 100) }\n\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(-4) { true } }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(3) { true } }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(2) { false } }\n      expect_raises(Enumerable::NotFoundError) { indexable.index!(-3) { false } }\n    end\n  end\n\n  describe \"#find\" do\n    it \"finds the element matching the block\" do\n      indexable = SafeIndexable.new(4)\n      indexable.find { |i| i > 2 }.should eq 3\n    end\n\n    it \"finds the element matching the block after given offset\" do\n      indexable = SafeIndexable.new(8)\n      indexable.find(offset: 5) { |i| i.even? }.should eq 6\n    end\n\n    it \"finds the element matching the block after given negative offset\" do\n      indexable = SafeIndexable.new(8)\n      indexable.find(offset: -6) { |i| i.even? }.should eq 2\n    end\n\n    it \"does not receive a valid negative offset, returns if_none value\" do\n      indexable = SafeIndexable.new(4)\n      indexable.find(-1, offset: -10) { |i| i > 2 }.should eq -1\n    end\n\n    it \"does not find the element matching the block\" do\n      indexable = SafeIndexable.new(4)\n      indexable.find { |i| i > 7 }.should be_nil\n    end\n\n    it \"does not find the element matching the block, returns custom if_none value\" do\n      indexable = SafeIndexable.new(4)\n      indexable.find(if_none: -1) { |i| i > 7 }.should eq -1\n    end\n\n    it \"does not find the element matching the block after given offset, returns custom if_none value\" do\n      indexable = SafeIndexable.new(5)\n      indexable.find(-3, 3) { |i| i > 15 }.should eq -3\n    end\n  end\n\n  describe \"#find!\" do\n    it \"finds the element matching the block\" do\n      indexable = SafeIndexable.new(4)\n      indexable.find! { |i| i > 2 }.should eq 3\n    end\n\n    it \"finds the element matching the block after given offset\" do\n      indexable = SafeIndexable.new(8)\n      indexable.find!(offset: 5) { |i| i.even? }.should eq 6\n    end\n\n    it \"finds the element matching the block after given negative offset\" do\n      indexable = SafeIndexable.new(8)\n      indexable.find!(offset: -6) { |i| i.even? }.should eq 2\n    end\n\n    it \"does not receive a valid negative offset, raises not found\" do\n      indexable = SafeIndexable.new(4)\n      expect_raises(Enumerable::NotFoundError) { indexable.find!(offset: -10) { |i| i > 2 } }\n    end\n\n    it \"does not find the element matching the block, raises not found\" do\n      indexable = SafeIndexable.new(4)\n      expect_raises(Enumerable::NotFoundError) { indexable.find! { |i| i > 7 } }\n    end\n  end\n\n  describe \"#rindex\" do\n    it \"does rindex with big negative offset\" do\n      indexable = SafeIndexable.new(3)\n      indexable.rindex(0, -100).should be_nil\n    end\n\n    it \"does rindex with big offset\" do\n      indexable = SafeIndexable.new(3)\n      indexable.rindex(0, 100).should be_nil\n    end\n\n    it \"offset type\" do\n      indexable = SafeIndexable.new(3)\n      indexable.rindex(1, 2_i64).should eq 1\n      indexable.rindex(1, 2_i64).should be_a(Int64)\n    end\n  end\n\n  describe \"#rindex!\" do\n    it \"does rindex with big negative offset\" do\n      indexable = SafeIndexable.new(3)\n      expect_raises Enumerable::NotFoundError do\n        indexable.rindex!(0, -100)\n      end\n    end\n\n    it \"does rindex with big offset\" do\n      indexable = SafeIndexable.new(3)\n      expect_raises Enumerable::NotFoundError do\n        indexable.rindex!(0, 100)\n      end\n    end\n\n    it \"offset type\" do\n      indexable = SafeIndexable.new(3)\n      indexable.rindex!(1, 2_i64).should eq 1\n      indexable.rindex!(1, 2_i64).should be_a(Int64)\n    end\n  end\n\n  it \"does each\" do\n    indexable = SafeIndexable.new(3)\n    is = [] of Int32\n    indexable.each do |i|\n      is << i\n    end.should be_nil\n    is.should eq([0, 1, 2])\n  end\n\n  it \"does each_index\" do\n    indexable = SafeIndexable.new(3)\n    is = [] of Int32\n    indexable.each_index do |i|\n      is << i\n    end.should be_nil\n    is.should eq([0, 1, 2])\n  end\n\n  it \"iterates through a subset of its elements (#3386)\" do\n    indexable = SafeIndexable.new(5)\n    elems = [] of Int32\n\n    return_value = indexable.each(start: 2, count: 3) do |elem|\n      elems << elem\n    end\n\n    elems.should eq([2, 3, 4])\n    return_value.should eq(indexable)\n  end\n\n  it \"iterates until its size (#3386)\" do\n    indexable = SafeIndexable.new(5)\n    elems = [] of Int32\n\n    indexable.each(start: 3, count: 999) do |elem|\n      elems << elem\n    end\n\n    elems.should eq([3, 4])\n  end\n\n  it \"iterates until its size, having mutated (#3386)\" do\n    indexable = SafeIndexable.new(10)\n    elems = [] of Int32\n\n    indexable.each(start: 3, count: 999) do |elem|\n      # size is incremented 3 times\n      indexable.size += 1 if elem <= 5\n      elems << elem\n    end\n\n    # last was 9, but now is 12.\n    elems.should eq([3, 4, 5, 6, 7, 8, 9, 10, 11, 12])\n  end\n\n  it \"iterates until its size, having mutated (#3386)\" do\n    indexable = SafeIndexable.new(10)\n    elems = [] of Int32\n\n    indexable.each(start: 3, count: 5) do |elem|\n      indexable.size += 1\n      elems << elem\n    end\n\n    # last element iterated is still 7.\n    elems.should eq([3, 4, 5, 6, 7])\n  end\n\n  it \"handles empty ranges, including those with exclude-end\" do\n    \"foobar\"[3..3].should eq(\"b\")\n    \"foobar\"[3...3].should eq(\"\")\n\n    \"foobar\"[3..-3].should eq(\"b\")\n    \"foobar\"[3...-3].should eq(\"\")\n    \"foobar\"[3...-5].should eq(\"\")\n\n    \"foobar\"[3_u32..3_u32].should eq(\"b\")\n    \"foobar\"[3_u32...3_u32].should eq(\"\")\n    \"foobar\"[3_u32...1_u32].should eq(\"\")\n  end\n\n  it \"handles extremely large ranges without overflowing\" do\n    \"foobar\"[3..Int32::MAX].should eq(\"bar\")\n\n    expect_raises IndexError, \"Index out of bounds\" do\n      \"foobar\"[Int32::MAX..]\n    end\n\n    Indexable.range_to_index_and_count(Int32::MAX.., 10).should eq({Int32::MAX, 0})\n  end\n\n  it \"handles extremely large collection sizes without overflowing\" do\n    Indexable.range_to_index_and_count((Int32::MAX - 2)..Int32::MAX, Int32::MAX).should eq({Int32::MAX - 2, 3})\n    Indexable.range_to_index_and_count((Int32::MAX - 4)...Int32::MAX, Int32::MAX).should eq({Int32::MAX - 4, 4})\n    Indexable.range_to_index_and_count((Int32::MAX - 4)..-2, Int32::MAX).should eq({Int32::MAX - 4, 3})\n  end\n\n  it \"errors on start indices that are off the end\" do\n    expect_raises IndexError, \"Index out of bounds\" do\n      \"foobar\"[100..1000]\n    end\n\n    expect_raises IndexError, \"Index out of bounds\" do\n      \"foobar\"[Int32::MIN..Int32::MAX]\n    end\n  end\n\n  it \"handles exclusive ranges that go off the end\" do\n    \"foobar\"[3...100].should eq(\"bar\")\n  end\n\n  it \"iterates within a range of indices (#3386)\" do\n    indexable = SafeIndexable.new(5)\n    elems = [] of Int32\n\n    return_value = indexable.each(within: 2..3) do |elem|\n      elems << elem\n    end\n\n    elems.should eq([2, 3])\n    return_value.should eq(indexable)\n  end\n\n  it \"iterates within a range of indices, no end\" do\n    indexable = SafeIndexable.new(5)\n    elems = [] of Int32\n\n    return_value = indexable.each(within: 2..nil) do |elem|\n      elems << elem\n    end\n\n    elems.should eq([2, 3, 4])\n    return_value.should eq(indexable)\n  end\n\n  it \"iterates within a range of indices, no beginning\" do\n    indexable = SafeIndexable.new(5)\n\n    elems = [] of Int32\n    return_value = indexable.each(within: nil..2) do |elem|\n      elems << elem\n    end\n\n    elems.should eq([0, 1, 2])\n    return_value.should eq(indexable)\n  end\n\n  describe \"#join\" do\n    it \"joins strings (empty case)\" do\n      indexable = SafeStringIndexable.new(0)\n      indexable.join.should eq(\"\")\n      indexable.join(\", \").should eq(\"\")\n    end\n\n    it \"joins strings (non-empty case)\" do\n      indexable = SafeStringIndexable.new(12)\n      indexable.join(\", \").should eq(\"0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11\")\n      indexable.join(98).should eq(\"098198298398498598698798898998109811\")\n    end\n\n    it \"joins non-strings\" do\n      indexable = SafeIndexable.new(12)\n      indexable.join(\", \").should eq(\"0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11\")\n      indexable.join(98).should eq(\"098198298398498598698798898998109811\")\n    end\n\n    it \"joins when T has String\" do\n      indexable = SafeMixedIndexable.new(12)\n      indexable.join(\", \").should eq(\"0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11\")\n      indexable.join(98).should eq(\"098198298398498598698798898998109811\")\n    end\n\n    it \"with IO\" do\n      String.build do |io|\n        indexable = SafeStringIndexable.new(12)\n        indexable.join(io)\n      end.should eq \"01234567891011\"\n    end\n  end\n\n  describe \"dig?\" do\n    it \"gets the value at given path given splat\" do\n      indexable = SafeRecursiveIndexable.new(30)\n      indexable.dig?(20, 10, 4, 3).should eq(3)\n    end\n\n    it \"returns nil if not found\" do\n      indexable = SafeRecursiveIndexable.new(30)\n      indexable.dig?(2, 4).should be_nil\n      indexable.dig?(3, 7).should be_nil\n    end\n  end\n\n  describe \"dig\" do\n    it \"gets the value at given path given splat\" do\n      indexable = SafeRecursiveIndexable.new(30)\n      indexable.dig(20, 10, 4, 3).should eq(3)\n    end\n\n    it \"raises IndexError if not found\" do\n      indexable = SafeRecursiveIndexable.new(30)\n      expect_raises IndexError, %(Index out of bounds) do\n        indexable.dig(2, 4)\n      end\n      expect_raises IndexError, %(Indexable value not diggable for index: 3) do\n        indexable.dig(3, 7)\n      end\n    end\n  end\n\n  describe \"fetch\" do\n    it \"fetches with default value\" do\n      indexable = SafeIndexable.new(3)\n      a = indexable.to_a\n\n      indexable.fetch(2, 4).should eq(2)\n      indexable.fetch(3, 4).should eq(4)\n      a.should eq([0, 1, 2])\n    end\n\n    it \"fetches with block\" do\n      indexable = SafeIndexable.new(3)\n      a = indexable.to_a\n\n      indexable.fetch(2) { |k| k * 3 }.should eq(2)\n      indexable.fetch(3) { |k| k * 3 }.should eq(9)\n      a.should eq([0, 1, 2])\n    end\n  end\n\n  describe \"#cartesian_product\" do\n    it \"does with 1 other Indexable\" do\n      elems = SafeIndexable.new(2).cartesian_product(SafeIndexable.new(3))\n      elems.should eq([{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}])\n\n      elems = SafeIndexable.new(2).cartesian_product(SafeIndexable.new(0))\n      elems.should be_empty\n\n      elems = SafeIndexable.new(0).cartesian_product(SafeIndexable.new(3))\n      elems.should be_empty\n    end\n\n    it \"does with >1 other Indexables\" do\n      elems = SafeIndexable.new(2).cartesian_product(SafeStringIndexable.new(2), SafeIndexable.new(2))\n      elems.should eq([\n        {0, \"0\", 0}, {0, \"0\", 1}, {0, \"1\", 0}, {0, \"1\", 1},\n        {1, \"0\", 0}, {1, \"0\", 1}, {1, \"1\", 0}, {1, \"1\", 1},\n      ])\n    end\n  end\n\n  describe \".cartesian_product\" do\n    it \"does with an Indexable of Indexables\" do\n      elems = Indexable.cartesian_product(SafeNestedIndexable.new(2, 3))\n      elems.should eq([[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]])\n\n      elems = Indexable.cartesian_product(SafeNestedIndexable.new(2, 0))\n      elems.should be_empty\n\n      elems = Indexable.cartesian_product(SafeNestedIndexable.new(0, 3))\n      elems.should eq([[] of Int32])\n\n      elems = Indexable.cartesian_product(SafeNestedIndexable.new(0, 0))\n      elems.should eq([[] of Int32])\n    end\n\n    it \"does with a Tuple of Tuples with mixed types\" do\n      elems = Indexable.cartesian_product({ {1, 'a'}, {\"\", 4}, {5, 6} })\n      elems.should be_a(Array(Array(Int32 | Char | String)))\n      elems.should eq([[1, \"\", 5], [1, \"\", 6], [1, 4, 5], [1, 4, 6], ['a', \"\", 5], ['a', \"\", 6], ['a', 4, 5], ['a', 4, 6]])\n    end\n  end\n\n  describe \"#each_cartesian\" do\n    it \"does with 1 other Indexable, with block\" do\n      r = [] of Int32 | String\n      indexable = SafeIndexable.new(3)\n      indexable.each_cartesian(SafeStringIndexable.new(2)) { |a, b| r << a; r << b }\n      r.should eq([0, \"0\", 0, \"1\", 1, \"0\", 1, \"1\", 2, \"0\", 2, \"1\"])\n\n      r = [] of Int32 | String\n      indexable = SafeIndexable.new(3)\n      indexable.each_cartesian(SafeStringIndexable.new(0)) { |a, b| r << a; r << b }\n      r.should be_empty\n\n      r = [] of Int32 | String\n      indexable = SafeIndexable.new(0)\n      indexable.each_cartesian(SafeStringIndexable.new(2)) { |a, b| r << a; r << b }\n      r.should be_empty\n    end\n\n    it \"does with 1 other Indexable, without block\" do\n      iter = SafeIndexable.new(3).each_cartesian(SafeStringIndexable.new(2))\n      iter.next.should eq({0, \"0\"})\n      iter.next.should eq({0, \"1\"})\n      iter.next.should eq({1, \"0\"})\n      iter.next.should eq({1, \"1\"})\n      iter.next.should eq({2, \"0\"})\n      iter.next.should eq({2, \"1\"})\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with 1 other Indexable, without block, combined with select\" do\n      iter = SafeIndexable.new(3).each_cartesian(SafeStringIndexable.new(2))\n      iter = iter.select { |(x, y)| x > 0 }\n      iter.next.should eq({1, \"0\"})\n      iter.next.should eq({1, \"1\"})\n      iter.next.should eq({2, \"0\"})\n      iter.next.should eq({2, \"1\"})\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with >1 other Indexables, with block\" do\n      r = [] of Int32 | String\n      i1 = SafeIndexable.new(2)\n      i2 = SafeIndexable.new(3)\n      i3 = SafeIndexable.new(4)\n      i1.each_cartesian(i2, i3) { |a, b, c| r << a + b + c }\n      r.should eq([0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6])\n    end\n\n    it \"does with >1 other Indexables, without block\" do\n      i1 = SafeStringIndexable.new(2)\n      i2 = SafeStringIndexable.new(2)\n      i3 = SafeIndexable.new(2)\n      iter = i1.each_cartesian(i2, i3)\n      iter.next.should eq({\"0\", \"0\", 0})\n      iter.next.should eq({\"0\", \"0\", 1})\n      iter.next.should eq({\"0\", \"1\", 0})\n      iter.next.should eq({\"0\", \"1\", 1})\n      iter.next.should eq({\"1\", \"0\", 0})\n      iter.next.should eq({\"1\", \"0\", 1})\n      iter.next.should eq({\"1\", \"1\", 0})\n      iter.next.should eq({\"1\", \"1\", 1})\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \".each_cartesian\" do\n    it \"does with an Indexable of Indexables, with block\" do\n      r = [] of Int32\n      Indexable.each_cartesian(SafeNestedIndexable.new(3, 2)) { |v| r.concat(v) }\n      r.should eq([0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1])\n\n      r = [] of Int32\n      Indexable.each_cartesian(SafeNestedIndexable.new(3, 0)) { |v| r.concat(v) }\n      r.should be_empty\n\n      r = [] of Int32\n      Indexable.each_cartesian(SafeNestedIndexable.new(0, 2)) { |v| r.concat(v) }\n      r.should be_empty\n\n      r = [] of Int32\n      Indexable.each_cartesian(SafeNestedIndexable.new(0, 0)) { |v| r.concat(v) }\n      r.should be_empty\n    end\n\n    it \"does with reuse = true, with block\" do\n      r = [] of Int32\n      object_ids = Set(UInt64).new\n      indexables = SafeNestedIndexable.new(3, 2)\n\n      Indexable.each_cartesian(indexables, reuse: true) do |v|\n        object_ids << v.object_id\n        r.concat(v)\n      end\n\n      r.should eq([0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1])\n      object_ids.size.should eq(1)\n    end\n\n    it \"does with reuse = array, with block\" do\n      r = [] of Int32\n      buf = [] of Int32\n      indexables = SafeNestedIndexable.new(3, 2)\n\n      Indexable.each_cartesian(indexables, reuse: buf) do |v|\n        v.should be(buf)\n        r.concat(v)\n      end\n\n      r.should eq([0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1])\n    end\n\n    it \"does with an Indexable of Indexables, without block\" do\n      iter = Indexable.each_cartesian(SafeNestedIndexable.new(2, 3))\n      iter.next.should eq([0, 0])\n      iter.next.should eq([0, 1])\n      iter.next.should eq([0, 2])\n      iter.next.should eq([1, 0])\n      iter.next.should eq([1, 1])\n      iter.next.should eq([1, 2])\n      iter.next.should eq([2, 0])\n      iter.next.should eq([2, 1])\n      iter.next.should eq([2, 2])\n      iter.next.should be_a(Iterator::Stop)\n\n      iter = Indexable.each_cartesian(SafeNestedIndexable.new(0, 3))\n      iter.next.should eq([] of Int32)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with an Indexable of Indexables, without block, combined with select\" do\n      iter = Indexable.each_cartesian(SafeNestedIndexable.new(2, 3))\n      iter = iter.select { |(x, y)| x > 0 }\n      iter.next.should eq([1, 0])\n      iter.next.should eq([1, 1])\n      iter.next.should eq([1, 2])\n      iter.next.should eq([2, 0])\n      iter.next.should eq([2, 1])\n      iter.next.should eq([2, 2])\n      iter.next.should be_a(Iterator::Stop)\n\n      iter = Indexable.each_cartesian(SafeNestedIndexable.new(0, 3))\n      iter.next.should eq([] of Int32)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with reuse = true, without block\" do\n      iter = Indexable.each_cartesian(SafeNestedIndexable.new(2, 2), reuse: true)\n      buf = iter.next\n      buf.should eq([0, 0])\n      iter.next.should be(buf)\n      buf.should eq([0, 1])\n      iter.next.should be(buf)\n      buf.should eq([1, 0])\n      iter.next.should be(buf)\n      buf.should eq([1, 1])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with reuse = array, without block\" do\n      buf = [] of Int32\n      iter = Indexable.each_cartesian(SafeNestedIndexable.new(2, 2), reuse: buf)\n      iter.next.should be(buf)\n      buf.should eq([0, 0])\n      iter.next.should be(buf)\n      buf.should eq([0, 1])\n      iter.next.should be(buf)\n      buf.should eq([1, 0])\n      iter.next.should be(buf)\n      buf.should eq([1, 1])\n    end\n  end\n\n  describe \"permutations\" do\n    it { [1, 2, 2].permutations.should eq([[1, 2, 2], [1, 2, 2], [2, 1, 2], [2, 2, 1], [2, 1, 2], [2, 2, 1]]) }\n    it { SafeIndexable.new(3, 1).permutations.should eq([[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]) }\n    it { SafeIndexable.new(3, 1).permutations(1).should eq([[1], [2], [3]]) }\n    it { SafeIndexable.new(3, 1).permutations(2).should eq([[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]) }\n    it { SafeIndexable.new(3, 1).permutations(3).should eq([[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]) }\n    it { SafeIndexable.new(3, 1).permutations(0).should eq([[] of Int32]) }\n    it { SafeIndexable.new(3, 1).permutations(4).should eq([] of Array(Int32)) }\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].permutations(-1) } }\n\n    it \"accepts a block\" do\n      sums = [] of Int32\n      SafeIndexable.new(3, 1).each_permutation(2) do |perm|\n        sums << perm.sum\n      end.should be_nil\n      sums.should eq([3, 4, 3, 5, 4, 5])\n    end\n\n    it \"yielding dup of arrays\" do\n      sums = [] of Int32\n      SafeIndexable.new(3, 1).each_permutation(3) do |perm|\n        perm.map! &.+(1)\n        sums << perm.sum\n      end.should be_nil\n      sums.should eq([9, 9, 9, 9, 9, 9])\n    end\n\n    it \"yields with reuse = true\" do\n      sums = [] of Int32\n      object_ids = Set(UInt64).new\n      SafeIndexable.new(3, 1).each_permutation(3, reuse: true) do |perm|\n        object_ids << perm.object_id\n        perm.map! &.+(1)\n        sums << perm.sum\n      end.should be_nil\n      sums.should eq([9, 9, 9, 9, 9, 9])\n      object_ids.size.should eq(1)\n    end\n\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].each_permutation(-1) { } } }\n\n    it \"returns iterator\" do\n      a = SafeIndexable.new(3, 1)\n      perms = a.permutations\n      iter = a.each_permutation\n      perms.each do |perm|\n        iter.next.should eq(perm)\n      end\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"returns iterator with given size\" do\n      a = SafeIndexable.new(3, 1)\n      perms = a.permutations(2)\n      iter = a.each_permutation(2)\n      perms.each do |perm|\n        iter.next.should eq(perm)\n      end\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"returns iterator with reuse = true\" do\n      a = SafeIndexable.new(3, 1)\n      object_ids = Set(UInt64).new\n      perms = a.permutations\n      iter = a.each_permutation(reuse: true)\n      perms.each do |perm|\n        b = iter.next.as(Array)\n        object_ids << b.object_id\n        b.should eq(perm)\n      end\n      iter.next.should be_a(Iterator::Stop)\n      object_ids.size.should eq(1)\n    end\n  end\n\n  describe \"combinations\" do\n    it { [1, 2, 2].combinations.should eq([[1, 2, 2]]) }\n    it { SafeIndexable.new(3, 1).combinations.should eq([[1, 2, 3]]) }\n    it { SafeIndexable.new(3, 1).combinations(1).should eq([[1], [2], [3]]) }\n    it { SafeIndexable.new(3, 1).combinations(2).should eq([[1, 2], [1, 3], [2, 3]]) }\n    it { SafeIndexable.new(3, 1).combinations(3).should eq([[1, 2, 3]]) }\n    it { SafeIndexable.new(3, 1).combinations(0).should eq([[] of Int32]) }\n    it { SafeIndexable.new(3, 1).combinations(4).should eq([] of Array(Int32)) }\n    it { SafeIndexable.new(4, 1).combinations(3).should eq([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]) }\n    it { SafeIndexable.new(4, 1).combinations(2).should eq([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]) }\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].combinations(-1) } }\n\n    it \"accepts a block\" do\n      sums = [] of Int32\n      SafeIndexable.new(3, 1).each_combination(2) do |comb|\n        sums << comb.sum\n      end.should be_nil\n      sums.should eq([3, 4, 5])\n    end\n\n    it \"yielding dup of arrays\" do\n      sums = [] of Int32\n      SafeIndexable.new(3, 1).each_combination(3) do |comb|\n        comb.map! &.+(1)\n        sums << comb.sum\n      end.should be_nil\n      sums.should eq([9])\n    end\n\n    it \"does with reuse = true\" do\n      sums = [] of Int32\n      object_ids = Set(UInt64).new\n      SafeIndexable.new(3, 1).each_combination(2, reuse: true) do |comb|\n        sums << comb.sum\n        object_ids << comb.object_id\n      end.should be_nil\n      sums.should eq([3, 4, 5])\n      object_ids.size.should eq(1)\n    end\n\n    it \"does with reuse = array\" do\n      sums = [] of Int32\n      reuse = [] of Int32\n      SafeIndexable.new(3, 1).each_combination(2, reuse: reuse) do |comb|\n        sums << comb.sum\n        comb.should be(reuse)\n      end.should be_nil\n      sums.should eq([3, 4, 5])\n    end\n\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].each_combination(-1) { } } }\n\n    it \"returns iterator\" do\n      a = [1, 2, 3, 4]\n      combs = a.combinations(2)\n      iter = a.each_combination(2)\n      combs.each do |comb|\n        iter.next.should eq(comb)\n      end\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"returns iterator with reuse = true\" do\n      a = [1, 2, 3, 4]\n      combs = a.combinations(2)\n      object_ids = Set(UInt64).new\n      iter = a.each_combination(2, reuse: true)\n      combs.each do |comb|\n        b = iter.next\n        object_ids << b.object_id\n        b.should eq(comb)\n      end\n      iter.next.should be_a(Iterator::Stop)\n      object_ids.size.should eq(1)\n    end\n\n    it \"returns iterator with reuse = array\" do\n      a = [1, 2, 3, 4]\n      reuse = [] of Int32\n      combs = a.combinations(2)\n      iter = a.each_combination(2, reuse: reuse)\n      combs.each do |comb|\n        b = iter.next\n        b.should be(reuse)\n        b.should eq(comb)\n      end\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"repeated_combinations\" do\n    it { [1, 2, 2].repeated_combinations.should eq([[1, 1, 1], [1, 1, 2], [1, 1, 2], [1, 2, 2], [1, 2, 2], [1, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]]) }\n    it { SafeIndexable.new(3, 1).repeated_combinations.should eq([[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 2], [2, 2, 3], [2, 3, 3], [3, 3, 3]]) }\n    it { SafeIndexable.new(3, 1).repeated_combinations(1).should eq([[1], [2], [3]]) }\n    it { SafeIndexable.new(3, 1).repeated_combinations(2).should eq([[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]) }\n    it { SafeIndexable.new(3, 1).repeated_combinations(3).should eq([[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 2], [2, 2, 3], [2, 3, 3], [3, 3, 3]]) }\n    it { SafeIndexable.new(3, 1).repeated_combinations(0).should eq([[] of Int32]) }\n    it { SafeIndexable.new(3, 1).repeated_combinations(4).should eq([[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 3, 3], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 3, 3], [1, 3, 3, 3], [2, 2, 2, 2], [2, 2, 2, 3], [2, 2, 3, 3], [2, 3, 3, 3], [3, 3, 3, 3]]) }\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].repeated_combinations(-1) } }\n\n    it \"accepts a block\" do\n      sums = [] of Int32\n      SafeIndexable.new(3, 1).each_repeated_combination(2) do |comb|\n        sums << comb.sum\n      end.should be_nil\n      sums.should eq([2, 3, 4, 4, 5, 6])\n    end\n\n    it \"yielding dup of arrays\" do\n      sums = [] of Int32\n      SafeIndexable.new(3, 1).each_repeated_combination(3) do |comb|\n        comb.map! &.+(1)\n        sums << comb.sum\n      end.should be_nil\n      sums.should eq([6, 7, 8, 8, 9, 10, 9, 10, 11, 12])\n    end\n\n    it { expect_raises(ArgumentError, \"Size must be positive\") { [1].each_repeated_combination(-1) { } } }\n\n    it \"yields with reuse = true\" do\n      sums = [] of Int32\n      object_ids = Set(UInt64).new\n      SafeIndexable.new(3, 1).each_repeated_combination(3, reuse: true) do |comb|\n        object_ids << comb.object_id\n        comb.map! &.+(1)\n        sums << comb.sum\n      end.should be_nil\n      sums.should eq([6, 7, 8, 8, 9, 10, 9, 10, 11, 12])\n      object_ids.size.should eq(1)\n    end\n\n    it \"yields with reuse = array\" do\n      sums = [] of Int32\n      reuse = [] of Int32\n      SafeIndexable.new(3, 1).each_repeated_combination(3, reuse: reuse) do |comb|\n        comb.should be(reuse)\n        comb.map! &.+(1)\n        sums << comb.sum\n      end.should be_nil\n      sums.should eq([6, 7, 8, 8, 9, 10, 9, 10, 11, 12])\n    end\n\n    it \"returns iterator\" do\n      a = [1, 2, 3, 4]\n      combs = a.repeated_combinations(2)\n      iter = a.each_repeated_combination(2)\n      combs.each do |comb|\n        iter.next.should eq(comb)\n      end\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"returns iterator with reuse = true\" do\n      a = [1, 2, 3, 4]\n      object_ids = Set(UInt64).new\n      combs = a.repeated_combinations(2)\n      iter = a.each_repeated_combination(2, reuse: true)\n      combs.each do |comb|\n        b = iter.next\n        object_ids << b.object_id\n        b.should eq(comb)\n      end\n      iter.next.should be_a(Iterator::Stop)\n      object_ids.size.should eq(1)\n    end\n\n    it \"returns iterator with reuse = array\" do\n      a = [1, 2, 3, 4]\n      reuse = [] of Int32\n      combs = a.repeated_combinations(2)\n      iter = a.each_repeated_combination(2, reuse: reuse)\n      combs.each do |comb|\n        b = iter.next\n        b.should be(reuse)\n        b.should eq(comb)\n      end\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    describe \"n > size (#14088)\" do\n      it_iterates \"#each_repeated_combination\", [[1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 2, 2]], SafeIndexable.new(2, 1).each_repeated_combination(3)\n      it_iterates \"#each_repeated_combination\", [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 2], [1, 2, 2, 2], [2, 2, 2, 2]], SafeIndexable.new(2, 1).each_repeated_combination(4)\n    end\n  end\n\n  describe \"#to_a\" do\n    it \"without a block of an interface type\" do\n      InterfaceIndexable.new.to_a.should eq [Three.new, Four.new]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/ini_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"ini\"\n\ndescribe \"INI\" do\n  describe \"parse\" do\n    context \"from String\" do\n      it \"fails on malformed section\" do\n        expect_raises(INI::ParseException, \"Unterminated section\") do\n          INI.parse(\"[section\")\n        end\n      end\n\n      it \"fails on data after section\" do\n        expect_raises(INI::ParseException, \"Data after section\") do\n          INI.parse(\"[section] foo  \")\n        end\n      end\n\n      it \"fails on malformed declaration\" do\n        expect_raises(INI::ParseException, \"Expected declaration\") do\n          INI.parse(\"foobar\")\n        end\n\n        expect_raises(INI::ParseException, \"Expected declaration\") do\n          INI.parse(\"foo: bar\")\n        end\n      end\n\n      it \"parses key = value\" do\n        INI.parse(\"key = value\").should eq({\"\" => {\"key\" => \"value\"}})\n      end\n\n      it \"parses empty values\" do\n        INI.parse(\"key = \").should eq({\"\" => {\"key\" => \"\"}})\n      end\n\n      it \"ignores whitespaces\" do\n        INI.parse(\"   key   =   value  \").should eq({\"\" => {\"key\" => \"value\"}})\n        INI.parse(\"  [foo]\").should eq({\"foo\" => Hash(String, String).new})\n      end\n\n      it \"ignores comments\" do\n        INI.parse(\"; foo\\n# bar\\nkey = value\").should eq({\"\" => {\"key\" => \"value\"}})\n      end\n\n      it \"parses sections\" do\n        INI.parse(\"[section]\\na = 1\").should eq({\"section\" => {\"a\" => \"1\"}})\n      end\n\n      it \"parses a reopened section\" do\n        INI.parse(\"[foo]\\na=1\\n[foo]\\nb=2\").should eq({\"foo\" => {\"a\" => \"1\", \"b\" => \"2\"}})\n      end\n\n      it \"parses empty section\" do\n        INI.parse(\"[section]\").should eq({\"section\" => Hash(String, String).new})\n      end\n    end\n\n    context \"from IO\" do\n      it \"parses a file\" do\n        File.open datapath(\"test_file.ini\") do |file|\n          INI.parse(file).should eq({\n            \"general\" => {\n              \"log_level\" => \"D\",\n            },\n            \"section1\" => {\n              \"foo\" => \"1.1\",\n              \"bar\" => \"2\",\n            },\n            \"section2\" => {\n              \"x.y.z\" => \"coco lala\",\n            },\n          })\n        end\n      end\n    end\n  end\n\n  describe \"build to an INI-formatted output\" do\n    it \"builds from a Hash\" do\n      INI.build({\n        \"general\" => {\n          \"log_level\" => 'D',\n        },\n        \"section1\" => {\n          \"foo\" => 1.1,\n          \"bar\" => 2,\n        },\n        \"section2\" => {\n          \"x.y.z\" => \"coco lala\",\n        },\n      }, true).should eq(File.read datapath(\"test_file.ini\"))\n    end\n    it \"builds from a NamedTuple\" do\n      INI.build({\n        \"general\": {\n          \"log_level\": 'D',\n        },\n        \"section1\": {\n          \"foo\": 1.1,\n          \"bar\": 2,\n        },\n        \"section2\": {\n          \"x.y.z\": \"coco lala\",\n        },\n      }, true).should eq(File.read datapath(\"test_file.ini\"))\n    end\n\n    it \"builds with no spaces around `=`\" do\n      INI.build({\"foo\" => {\"a\" => \"1\"}}, false).should eq(\"[foo]\\na=1\\n\\n\")\n    end\n\n    it \"builds with no sections\" do\n      INI.build({\"\" => {\"a\" => \"1\"}}, false).should eq(\"a=1\\n\")\n    end\n\n    it \"builds an empty section before non-empty sections\" do\n      INI.build({\"foo\" => {\"a\" => \"1\"}, \"\" => {\"b\" => \"2\"}}, false).should eq(\"b=2\\n[foo]\\na=1\\n\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/int_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"big\"\nrequire \"spec/helpers/iterate\"\nrequire \"../support/number\"\n\nprivate macro it_converts_to_s(num, str, **opts)\n  it {{ \"converts #{num} to #{str}\" }} do\n    num = {{ num }}\n    str = {{ str }}\n    num.to_s({{ opts.double_splat }}).should eq(str)\n    String.build { |io| num.to_s(io, {{ opts.double_splat }}) }.should eq(str)\n  end\nend\n\nprivate class IntEnumerable\n  include Enumerable(Int32)\n\n  def initialize(@elements : Array(Int32))\n  end\n\n  def each(&)\n    @elements.each do |e|\n      yield e\n    end\n  end\nend\n\ndescribe \"Int\" do\n  describe \"#integer?\" do\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it { {{ int }}::MIN.integer?.should be_true }\n      it { {{ int }}::MAX.integer?.should be_true }\n    {% end %}\n  end\n\n  describe \"**\" do\n    it \"with positive Int32\" do\n      x = 2 ** 2\n      x.should eq(4)\n      x.should be_a(Int32)\n\n      x = 2 ** 0\n      x.should eq(1)\n      x.should be_a(Int32)\n    end\n\n    it \"with positive UInt8\" do\n      x = 2_u8 ** 2\n      x.should eq(4)\n      x.should be_a(UInt8)\n    end\n\n    it \"raises with negative exponent\" do\n      expect_raises(ArgumentError, \"Cannot raise an integer to a negative integer power, use floats for that\") do\n        2 ** -1\n      end\n    end\n\n    it \"should work with large integers\" do\n      x = 51_i64 ** 11\n      x.should eq(6071163615208263051_i64)\n      x.should be_a(Int64)\n    end\n\n    describe \"with float\" do\n      it { (2 ** 2.0).should be_close(4, 0.0001) }\n      it { (2 ** 2.5_f32).should be_close(5.656854249492381, 0.0001) }\n      it { (2 ** 2.5).should be_close(5.656854249492381, 0.0001) }\n    end\n  end\n\n  describe \"&**\" do\n    it \"with positive Int32\" do\n      x = 2 &** 2\n      x.should eq(4)\n      x.should be_a(Int32)\n\n      x = 2 &** 0\n      x.should eq(1)\n      x.should be_a(Int32)\n    end\n\n    it \"with UInt8\" do\n      x = 2_u8 &** 2\n      x.should eq(4)\n      x.should be_a(UInt8)\n    end\n\n    it \"raises with negative exponent\" do\n      expect_raises(ArgumentError, \"Cannot raise an integer to a negative integer power, use floats for that\") do\n        2 &** -1\n      end\n    end\n\n    it \"works with large integers\" do\n      x = 51_i64 &** 11\n      x.should eq(6071163615208263051_i64)\n      x.should be_a(Int64)\n    end\n\n    it \"wraps with larger integers\" do\n      x = 51_i64 &** 12\n      x.should eq(-3965304877440961871_i64)\n      x.should be_a(Int64)\n    end\n  end\n\n  describe \"#===(:Char)\" do\n    it { (99 === 'c').should be_true }\n    it { (99_u8 === 'c').should be_true }\n    it { (99 === 'z').should be_false }\n    it { (37202 === '酒').should be_true }\n  end\n\n  describe \"divisible_by?\" do\n    it { 10.divisible_by?(5).should be_true }\n    it { 10.divisible_by?(3).should be_false }\n  end\n\n  describe \"even?\" do\n    it { 2.even?.should be_true }\n    it { 3.even?.should be_false }\n  end\n\n  describe \"odd?\" do\n    it { 2.odd?.should be_false }\n    it { 3.odd?.should be_true }\n  end\n\n  describe \"succ\" do\n    it { 8.succ.should eq(9) }\n    it { -2147483648.succ.should eq(-2147483647) }\n    it { 2147483646.succ.should eq(2147483647) }\n  end\n\n  describe \"pred\" do\n    it { 9.pred.should eq(8) }\n    it { -2147483647.pred.should eq(-2147483648) }\n    it { 2147483647.pred.should eq(2147483646) }\n  end\n\n  describe \"abs\" do\n    it \"does for signed\" do\n      1_i8.abs.should eq(1_i8)\n      -1_i8.abs.should eq(1_i8)\n      1_i16.abs.should eq(1_i16)\n      -1_i16.abs.should eq(1_i16)\n      1_i32.abs.should eq(1_i32)\n      -1_i32.abs.should eq(1_i32)\n      1_i64.abs.should eq(1_i64)\n      -1_i64.abs.should eq(1_i64)\n    end\n\n    it \"does for unsigned\" do\n      1_u8.abs.should eq(1_u8)\n      1_u16.abs.should eq(1_u16)\n      1_u32.abs.should eq(1_u32)\n      1_u64.abs.should eq(1_u64)\n    end\n  end\n\n  describe \"#to_signed\" do\n    {% for n in [8, 16, 32, 64, 128] %}\n      it \"does for Int{{n}}\" do\n        x = Int{{n}}.new(123).to_signed\n        x.should be_a(Int{{n}})\n        x.should eq(123)\n\n        Int{{n}}.new(-123).to_signed.should eq(-123)\n        Int{{n}}::MIN.to_signed.should eq(Int{{n}}::MIN)\n        Int{{n}}::MAX.to_signed.should eq(Int{{n}}::MAX)\n      end\n\n      it \"does for UInt{{n}}\" do\n        x = UInt{{n}}.new(123).to_signed\n        x.should be_a(Int{{n}})\n        x.should eq(123)\n\n        UInt{{n}}::MIN.to_signed.should eq(0)\n        expect_raises(OverflowError) { UInt{{n}}::MAX.to_signed }\n        expect_raises(OverflowError) { (UInt{{n}}.new(Int{{n}}::MAX) + 1).to_signed }\n      end\n    {% end %}\n  end\n\n  describe \"#to_signed!\" do\n    {% for n in [8, 16, 32, 64, 128] %}\n      it \"does for Int{{n}}\" do\n        x = Int{{n}}.new(123).to_signed!\n        x.should be_a(Int{{n}})\n        x.should eq(123)\n\n        Int{{n}}.new(-123).to_signed!.should eq(-123)\n        Int{{n}}::MIN.to_signed!.should eq(Int{{n}}::MIN)\n        Int{{n}}::MAX.to_signed!.should eq(Int{{n}}::MAX)\n      end\n\n      it \"does for UInt{{n}}\" do\n        x = UInt{{n}}.new(123).to_signed!\n        x.should be_a(Int{{n}})\n        x.should eq(123)\n\n        UInt{{n}}::MIN.to_signed!.should eq(0)\n        UInt{{n}}::MAX.to_signed!.should eq(-1)\n        (UInt{{n}}::MAX - 122).to_signed!.should eq(-123)\n        (UInt{{n}}.new(Int{{n}}::MAX) + 1).to_signed!.should eq(Int{{n}}::MIN)\n      end\n    {% end %}\n  end\n\n  describe \"#to_unsigned\" do\n    {% for n in [8, 16, 32, 64, 128] %}\n      it \"does for Int{{n}}\" do\n        x = Int{{n}}.new(123).to_unsigned\n        x.should be_a(UInt{{n}})\n        x.should eq(123)\n\n        Int{{n}}.zero.to_unsigned.should eq(UInt{{n}}::MIN)\n        Int{{n}}::MAX.to_unsigned.should eq(UInt{{n}}.new(Int{{n}}::MAX))\n        expect_raises(OverflowError) { Int{{n}}::MIN.to_unsigned }\n      end\n\n      it \"does for UInt{{n}}\" do\n        x = UInt{{n}}.new(123).to_unsigned\n        x.should be_a(UInt{{n}})\n        x.should eq(123)\n\n        UInt{{n}}::MIN.to_unsigned.should eq(UInt{{n}}::MIN)\n        UInt{{n}}::MAX.to_unsigned.should eq(UInt{{n}}::MAX)\n      end\n    {% end %}\n  end\n\n  describe \"#to_unsigned!\" do\n    {% for n in [8, 16, 32, 64, 128] %}\n      it \"does for Int{{n}}\" do\n        x = Int{{n}}.new(123).to_unsigned!\n        x.should be_a(UInt{{n}})\n        x.should eq(123)\n\n        Int{{n}}.new(-123).to_unsigned!.should eq(UInt{{n}}::MAX - 122)\n        Int{{n}}::MIN.to_unsigned!.should eq(UInt{{n}}::MAX // 2 + 1)\n        Int{{n}}::MAX.to_unsigned!.should eq(UInt{{n}}::MAX // 2)\n        Int{{n}}.new(-1).to_unsigned!.should eq(UInt{{n}}::MAX)\n      end\n\n      it \"does for UInt{{n}}\" do\n        x = UInt{{n}}.new(123).to_unsigned!\n        x.should be_a(UInt{{n}})\n        x.should eq(123)\n\n        UInt{{n}}::MIN.to_unsigned!.should eq(UInt{{n}}::MIN)\n        UInt{{n}}::MAX.to_unsigned!.should eq(UInt{{n}}::MAX)\n      end\n    {% end %}\n  end\n\n  describe \"#abs_unsigned\" do\n    {% for int in Int::Signed.union_types %}\n      it \"does for {{ int }}\" do\n        x = {{ int }}.new(123).abs_unsigned\n        x.should be_a(U{{ int }})\n        x.should eq(123)\n\n        x = {{ int }}.new(-123).abs_unsigned\n        x.should be_a(U{{ int }})\n        x.should eq(123)\n      end\n\n      it \"does for U{{ int }}\" do\n        x = U{{ int }}.new(123).abs_unsigned\n        x.should be_a(U{{ int }})\n        x.should eq(123)\n      end\n\n      it \"does not overflow on {{ int }}::MIN\" do\n        x = {{ int }}::MIN.abs_unsigned\n        x.should be_a(U{{ int }})\n        x.should eq(U{{ int }}.zero &- {{ int }}::MIN)\n      end\n    {% end %}\n  end\n\n  describe \"#neg_signed\" do\n    {% for int in Int::Signed.union_types %}\n      it \"does for {{ int }}\" do\n        x = {{ int }}.new(123).neg_signed\n        x.should be_a({{ int }})\n        x.should eq(-123)\n\n        x = {{ int }}.new(-123).neg_signed\n        x.should be_a({{ int }})\n        x.should eq(123)\n\n        expect_raises(OverflowError) { {{ int }}::MIN.neg_signed }\n      end\n\n      it \"does for U{{ int }}\" do\n        x = U{{ int }}.new(123).neg_signed\n        x.should be_a({{ int }})\n        x.should eq(-123)\n      end\n\n      it \"does not overflow on {{ int }}::MIN.abs_unsigned\" do\n        x = {{ int }}::MIN.abs_unsigned.neg_signed\n        x.should be_a({{ int }})\n        x.should eq({{ int }}::MIN)\n      end\n    {% end %}\n  end\n\n  describe \"gcd\" do\n    it { 14.gcd(0).should eq(14) }\n    it { 14.gcd(1).should eq(1) }\n    it { 10.gcd(75).should eq(5) }\n    it { 10.gcd(-75).should eq(5) }\n    it { -10.gcd(75).should eq(5) }\n\n    it { 7.gcd(5).should eq(1) }   # prime\n    it { 14.gcd(25).should eq(1) } # coprime\n    it { 24.gcd(40).should eq(8) } # common divisor\n\n    it \"doesn't silently overflow\" { 614_889_782_588_491_410_i64.gcd(53).should eq(1) }\n    it \"raises on too big result to fit in result type\" do\n      expect_raises(OverflowError, \"Arithmetic overflow\") { Int64::MIN.gcd(1) }\n    end\n  end\n\n  describe \"lcm\" do\n    it { 2.lcm(2).should eq(2) }\n    it { 3.lcm(-7).should eq(21) }\n    it { 4.lcm(6).should eq(12) }\n    it { 0.lcm(2).should eq(0) }\n    it { 2.lcm(0).should eq(0) }\n\n    it \"doesn't silently overflow\" { 2_000_000.lcm(3_000_000).should eq(6_000_000) }\n  end\n\n  describe \"#to_s\" do\n    it_converts_to_s 0, \"0\"\n    it_converts_to_s 1, \"1\"\n\n    context \"extrema for various int sizes\" do\n      it_converts_to_s 127_i8, \"127\"\n      it_converts_to_s -128_i8, \"-128\"\n\n      it_converts_to_s 32767_i16, \"32767\"\n      it_converts_to_s -32768_i16, \"-32768\"\n\n      it_converts_to_s 2147483647, \"2147483647\"\n      it_converts_to_s -2147483648, \"-2147483648\"\n\n      it_converts_to_s 9223372036854775807_i64, \"9223372036854775807\"\n      it_converts_to_s -9223372036854775808_i64, \"-9223372036854775808\"\n\n      it_converts_to_s 255_u8, \"255\"\n      it_converts_to_s 65535_u16, \"65535\"\n      it_converts_to_s 4294967295_u32, \"4294967295\"\n\n      it_converts_to_s 18446744073709551615_u64, \"18446744073709551615\"\n\n      it_converts_to_s UInt128::MAX, \"340282366920938463463374607431768211455\"\n      it_converts_to_s Int128::MAX, \"170141183460469231731687303715884105727\"\n      it_converts_to_s Int128::MIN, \"-170141183460469231731687303715884105728\"\n    end\n\n    context \"base and upcase parameters\" do\n      it_converts_to_s 12, \"1100\", base: 2\n      it_converts_to_s -12, \"-1100\", base: 2\n      it_converts_to_s -123456, \"-11110001001000000\", base: 2\n      it_converts_to_s 1234, \"4d2\", base: 16\n      it_converts_to_s -1234, \"-4d2\", base: 16\n      it_converts_to_s 1234, \"ya\", base: 36\n      it_converts_to_s -1234, \"-ya\", base: 36\n      it_converts_to_s 1234, \"4D2\", base: 16, upcase: true\n      it_converts_to_s -1234, \"-4D2\", base: 16, upcase: true\n      it_converts_to_s 1234, \"YA\", base: 36, upcase: true\n      it_converts_to_s -1234, \"-YA\", base: 36, upcase: true\n      it_converts_to_s 0, \"0\", base: 2\n      it_converts_to_s 0, \"0\", base: 16\n      it_converts_to_s 1, \"1\", base: 2\n      it_converts_to_s 1, \"1\", base: 16\n      it_converts_to_s 0, \"0\", base: 62\n      it_converts_to_s 1, \"1\", base: 62\n      it_converts_to_s 10, \"a\", base: 62\n      it_converts_to_s 35, \"z\", base: 62\n      it_converts_to_s 36, \"A\", base: 62\n      it_converts_to_s 61, \"Z\", base: 62\n      it_converts_to_s 62, \"10\", base: 62\n      it_converts_to_s 97, \"1z\", base: 62\n      it_converts_to_s 3843, \"ZZ\", base: 62\n      it_converts_to_s Int128::MIN, \"-1#{\"0\" * 127}\", base: 2\n\n      it \"raises on base 1\" do\n        expect_raises(ArgumentError, \"Invalid base 1\") { 123.to_s(1) }\n        expect_raises(ArgumentError, \"Invalid base 1\") { 123.to_s(IO::Memory.new, 1) }\n      end\n\n      it \"raises on base 37\" do\n        expect_raises(ArgumentError, \"Invalid base 37\") { 123.to_s(37) }\n        expect_raises(ArgumentError, \"Invalid base 37\") { 123.to_s(IO::Memory.new, 37) }\n      end\n\n      it \"raises on base 62 with upcase\" do\n        expect_raises(ArgumentError, \"upcase must be false for base 62\") { 123.to_s(62, upcase: true) }\n        expect_raises(ArgumentError, \"upcase must be false for base 62\") { 123.to_s(IO::Memory.new, 62, upcase: true) }\n      end\n    end\n\n    context \"precision parameter\" do\n      it_converts_to_s 0, \"\", precision: 0\n      it_converts_to_s 0, \"0\", precision: 1\n      it_converts_to_s 0, \"00\", precision: 2\n      it_converts_to_s 0, \"00000\", precision: 5\n      it_converts_to_s 0, \"0\" * 200, precision: 200\n\n      it_converts_to_s 1, \"1\", precision: 0\n      it_converts_to_s 1, \"1\", precision: 1\n      it_converts_to_s 1, \"01\", precision: 2\n      it_converts_to_s 1, \"00001\", precision: 5\n      it_converts_to_s 1, \"#{\"0\" * 199}1\", precision: 200\n\n      it_converts_to_s 2, \"2\", precision: 0\n      it_converts_to_s 2, \"2\", precision: 1\n      it_converts_to_s 2, \"02\", precision: 2\n      it_converts_to_s 2, \"00002\", precision: 5\n      it_converts_to_s 2, \"#{\"0\" * 199}2\", precision: 200\n\n      it_converts_to_s -1, \"-1\", precision: 0\n      it_converts_to_s -1, \"-1\", precision: 1\n      it_converts_to_s -1, \"-01\", precision: 2\n      it_converts_to_s -1, \"-00001\", precision: 5\n      it_converts_to_s -1, \"-#{\"0\" * 199}1\", precision: 200\n\n      it_converts_to_s 123, \"123\", precision: 0\n      it_converts_to_s 123, \"123\", precision: 1\n      it_converts_to_s 123, \"123\", precision: 2\n      it_converts_to_s 123, \"00123\", precision: 5\n      it_converts_to_s 123, \"#{\"0\" * 197}123\", precision: 200\n\n      it_converts_to_s 9223372036854775807_i64, \"#{\"1\" * 63}\", base: 2, precision: 62\n      it_converts_to_s 9223372036854775807_i64, \"#{\"1\" * 63}\", base: 2, precision: 63\n      it_converts_to_s 9223372036854775807_i64, \"0#{\"1\" * 63}\", base: 2, precision: 64\n      it_converts_to_s 9223372036854775807_i64, \"#{\"0\" * 137}#{\"1\" * 63}\", base: 2, precision: 200\n\n      it_converts_to_s -9223372036854775808_i64, \"-1#{\"0\" * 63}\", base: 2, precision: 63\n      it_converts_to_s -9223372036854775808_i64, \"-1#{\"0\" * 63}\", base: 2, precision: 64\n      it_converts_to_s -9223372036854775808_i64, \"-01#{\"0\" * 63}\", base: 2, precision: 65\n      it_converts_to_s -9223372036854775808_i64, \"-#{\"0\" * 136}1#{\"0\" * 63}\", base: 2, precision: 200\n\n      it \"raises on negative precision\" do\n        expect_raises(ArgumentError, \"Precision must be non-negative\") { 123.to_s(precision: -1) }\n        expect_raises(ArgumentError, \"Precision must be non-negative\") { 123.to_s(IO::Memory.new, precision: -1) }\n      end\n    end\n  end\n\n  describe \"#inspect\" do\n    it \"doesn't append the type\" do\n      23.inspect.should eq(\"23\")\n      23_i8.inspect.should eq(\"23\")\n      23_i16.inspect.should eq(\"23\")\n      -23_i64.inspect.should eq(\"-23\")\n      23_u8.inspect.should eq(\"23\")\n      23_u16.inspect.should eq(\"23\")\n      23_u32.inspect.should eq(\"23\")\n      23_u64.inspect.should eq(\"23\")\n    end\n\n    it \"doesn't append the type using IO\" do\n      str = String.build { |io| 23.inspect(io) }\n      str.should eq(\"23\")\n\n      str = String.build { |io| -23_i64.inspect(io) }\n      str.should eq(\"-23\")\n    end\n  end\n\n  describe \"bit\" do\n    it { 5.bit(0).should eq(1) }\n    it { 5.bit(1).should eq(0) }\n    it { 5.bit(2).should eq(1) }\n    it { 5.bit(3).should eq(0) }\n    it { 0.bit(63).should eq(0) }\n    it { Int64::MAX.bit(63).should eq(0) }\n    it { UInt64::MAX.bit(63).should eq(1) }\n    it { UInt64::MAX.bit(64).should eq(0) }\n  end\n\n  describe \"#bits\" do\n    # Basic usage\n    it { 0b10011.bits(0..0).should eq(0b1) }\n    it { 0b10011.bits(0..1).should eq(0b11) }\n    it { 0b10011.bits(0..2).should eq(0b11) }\n    it { 0b10011.bits(0..3).should eq(0b11) }\n    it { 0b10011.bits(0..4).should eq(0b10011) }\n    it { 0b10011.bits(0..5).should eq(0b10011) }\n    it { 0b10011.bits(1..5).should eq(0b1001) }\n\n    # no range start indicated\n    it { 0b10011.bits(..1).should eq(0b11) }\n    it { 0b10011.bits(..2).should eq(0b11) }\n    it { 0b10011.bits(..3).should eq(0b11) }\n    it { 0b10011.bits(..4).should eq(0b10011) }\n\n    # Check against limits\n    it { 0b10011_u8.bits(0..16).should eq(0b10011_u8) }\n    it { 0b10011_u8.bits(1..16).should eq(0b1001_u8) }\n\n    # Will work with signed values\n    it { -5_i8.bits(0..16).should eq(-5_i8) }\n    it { -5_i8.bits(1..16).should eq(-3_i8) }\n    it { -5_i8.bits(2..16).should eq(-2_i8) }\n    it { -5_i8.bits(3..16).should eq(-1_i8) }\n\n    it \"raises when invalid indexes are provided\" do\n      expect_raises(IndexError) { 0b10011.bits(0..-1) }\n      expect_raises(IndexError) { 0b10011.bits(-1..3) }\n      expect_raises(IndexError) { 0b10011.bits(4..2) }\n    end\n  end\n\n  describe \"divmod\" do\n    it \"returns a Tuple of two elements containing the quotient and modulus obtained by dividing self by argument\" do\n      5.divmod(3).should eq({1, 2})\n      -5.divmod(3).should eq({-2, 1})\n      5.divmod(-3).should eq({-2, -1})\n      -5.divmod(-3).should eq({1, -2})\n    end\n\n    it \"preserves type of lhs\" do\n      {% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}\n        {{type}}.new(7).divmod(2).should be_a(Tuple({{type}}, {{type}}))\n      {% end %}\n    end\n\n    it \"raises when divides by zero\" do\n      expect_raises(DivisionByZeroError) { 5.divmod(0) }\n    end\n\n    it \"raises when divides Int::MIN by -1\" do\n      expect_raises(ArgumentError) { Int8::MIN.divmod(-1) }\n      expect_raises(ArgumentError) { Int16::MIN.divmod(-1) }\n      expect_raises(ArgumentError) { Int32::MIN.divmod(-1) }\n      expect_raises(ArgumentError) { Int64::MIN.divmod(-1) }\n      expect_raises(ArgumentError) { Int128::MIN.divmod(-1) }\n\n      (UInt8::MIN.divmod(-1)).should eq({0, 0})\n    end\n  end\n\n  describe \"tdivmod\" do\n    it \"returns a Tuple of two elements containing the quotient and modulus obtained by dividing self by argument using truncated division\" do\n      5.tdivmod(3).should eq({1, 2})\n      -5.tdivmod(3).should eq({-1, -2})\n      5.tdivmod(-3).should eq({-1, 2})\n      -5.tdivmod(-3).should eq({1, -2})\n    end\n\n    it \"preserves type of lhs\" do\n      {% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}\n        {{type}}.new(7).tdivmod(2).should be_a(Tuple({{type}}, {{type}}))\n      {% end %}\n    end\n\n    it \"raises when divides by zero\" do\n      expect_raises(DivisionByZeroError) { 5.tdivmod(0) }\n    end\n\n    it \"raises when divides Int::MIN by -1\" do\n      expect_raises(ArgumentError) { Int8::MIN.tdivmod(-1) }\n      expect_raises(ArgumentError) { Int16::MIN.tdivmod(-1) }\n      expect_raises(ArgumentError) { Int32::MIN.tdivmod(-1) }\n      expect_raises(ArgumentError) { Int64::MIN.tdivmod(-1) }\n      expect_raises(ArgumentError) { Int128::MIN.tdivmod(-1) }\n\n      (UInt8::MIN.tdivmod(-1)).should eq({0, 0})\n    end\n  end\n\n  describe \"fdiv\" do\n    it { 1.fdiv(1).should eq 1.0 }\n    it { 1.fdiv(2).should eq 0.5 }\n    it { 1.fdiv(0.5).should eq 2.0 }\n    it { 0.fdiv(1).should eq 0.0 }\n    it { 1.fdiv(0).should eq 1.0/0.0 }\n  end\n\n  describe \"~\" do\n    it { (~1).should eq(-2) }\n    it { (~1_u32).should eq(4294967294) }\n  end\n\n  describe \">>\" do\n    it { (8000 >> 1).should eq(4000) }\n    it { (8000 >> 2).should eq(2000) }\n    it { (8000 >> 32).should eq(0) }\n    it { (8000 >> -1).should eq(16000) }\n  end\n\n  describe \"<<\" do\n    it { (8000 << 1).should eq(16000) }\n    it { (8000 << 2).should eq(32000) }\n    it { (8000 << 32).should eq(0) }\n    it { (8000 << -1).should eq(4000) }\n  end\n\n  describe \"#rotate_left\" do\n    it { 0x87654321_u32.rotate_left(1).should eq(0x0ECA8643_u32) }\n    it { 0x87654321_u32.rotate_left(2).should eq(0x1D950C86_u32) }\n    it { 0x87654321_u32.rotate_left(-1).should eq(0xC3B2A190_u32) }\n    it { 0x87654321_u32.rotate_left(-2).should eq(0x61D950C8_u32) }\n    it { 0x87654321_u32.rotate_left(32).should eq(0x87654321_u32) }\n\n    it { -0x789ABCDF.rotate_left(1).should eq(0x0ECA8643) }\n    it { -0x789ABCDF.rotate_left(2).should eq(0x1D950C86) }\n    it { -0x789ABCDF.rotate_left(-1).should eq(-0x3C4D5E70) }\n    it { -0x789ABCDF.rotate_left(-2).should eq(0x61D950C8) }\n    it { -0x789ABCDF.rotate_left(32).should eq(-0x789ABCDF) }\n\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it do\n        x = ({{ int }}.new(1) << (sizeof({{ int }}) * 8 - 1)).rotate_left(1)\n        x.should be_a({{ int }})\n        x.should eq({{ int }}.new(1))\n      end\n    {% end %}\n  end\n\n  describe \"#rotate_right\" do\n    it { 0x87654321_u32.rotate_right(1).should eq(0xC3B2A190_u32) }\n    it { 0x87654321_u32.rotate_right(2).should eq(0x61D950C8_u32) }\n    it { 0x87654321_u32.rotate_right(-1).should eq(0x0ECA8643_u32) }\n    it { 0x87654321_u32.rotate_right(-2).should eq(0x1D950C86_u32) }\n    it { 0x87654321_u32.rotate_right(32).should eq(0x87654321_u32) }\n\n    it { -0x789ABCDF.rotate_right(1).should eq(-0x3C4D5E70) }\n    it { -0x789ABCDF.rotate_right(2).should eq(0x61D950C8) }\n    it { -0x789ABCDF.rotate_right(-1).should eq(0x0ECA8643) }\n    it { -0x789ABCDF.rotate_right(-2).should eq(0x1D950C86) }\n    it { -0x789ABCDF.rotate_right(32).should eq(-0x789ABCDF) }\n\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it do\n        x = {{ int }}.new(1).rotate_right(1)\n        x.should be_a({{ int }})\n        x.should eq({{ int }}.new(1) << (sizeof({{ int }}) * 8 - 1))\n      end\n    {% end %}\n  end\n\n  describe \"to\" do\n    it \"does upwards\" do\n      a = 0\n      1.to(3) { |i| a += i }.should be_nil\n      a.should eq(6)\n    end\n\n    it \"does downwards\" do\n      a = 0\n      4.to(2) { |i| a += i }.should be_nil\n      a.should eq(9)\n    end\n\n    it \"does when same\" do\n      a = 0\n      2.to(2) { |i| a += i }.should be_nil\n      a.should eq(2)\n    end\n  end\n\n  describe \"step\" do\n    it \"steps through limit\" do\n      passed = false\n      1.step(to: 1) { |x| passed = true }\n      fail \"expected step to pass through 1\" unless passed\n    end\n  end\n\n  describe \".new\" do\n    it \"String overload\" do\n      Int8.new(\"1\").should be_a(Int8)\n      Int8.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid Int8: \" 1 \") do\n        Int8.new(\" 1 \", whitespace: false)\n      end\n\n      Int16.new(\"1\").should be_a(Int16)\n      Int16.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid Int16: \" 1 \") do\n        Int16.new(\" 1 \", whitespace: false)\n      end\n\n      Int32.new(\"1\").should be_a(Int32)\n      Int32.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid Int32: \" 1 \") do\n        Int32.new(\" 1 \", whitespace: false)\n      end\n\n      Int64.new(\"1\").should be_a(Int64)\n      Int64.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid Int64: \" 1 \") do\n        Int64.new(\" 1 \", whitespace: false)\n      end\n\n      UInt8.new(\"1\").should be_a(UInt8)\n      UInt8.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid UInt8: \" 1 \") do\n        UInt8.new(\" 1 \", whitespace: false)\n      end\n\n      UInt16.new(\"1\").should be_a(UInt16)\n      UInt16.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid UInt16: \" 1 \") do\n        UInt16.new(\" 1 \", whitespace: false)\n      end\n\n      UInt32.new(\"1\").should be_a(UInt32)\n      UInt32.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid UInt32: \" 1 \") do\n        UInt32.new(\" 1 \", whitespace: false)\n      end\n\n      UInt64.new(\"1\").should be_a(UInt64)\n      UInt64.new(\"1\").should eq(1)\n      expect_raises ArgumentError, %(Invalid UInt64: \" 1 \") do\n        UInt64.new(\" 1 \", whitespace: false)\n      end\n    end\n\n    it \"fallback overload\" do\n      Int8.new(1).should be_a(Int8)\n      Int8.new(1).should eq(1)\n\n      Int16.new(1).should be_a(Int16)\n      Int16.new(1).should eq(1)\n\n      Int32.new(1).should be_a(Int32)\n      Int32.new(1).should eq(1)\n\n      Int64.new(1).should be_a(Int64)\n      Int64.new(1).should eq(1)\n\n      Int128.new(1).should be_a(Int128)\n      Int128.new(1).should eq(1)\n\n      UInt8.new(1).should be_a(UInt8)\n      UInt8.new(1).should eq(1)\n\n      UInt16.new(1).should be_a(UInt16)\n      UInt16.new(1).should eq(1)\n\n      UInt32.new(1).should be_a(UInt32)\n      UInt32.new(1).should eq(1)\n\n      UInt64.new(1).should be_a(UInt64)\n      UInt64.new(1).should eq(1)\n\n      UInt128.new(1).should be_a(UInt128)\n      UInt128.new(1).should eq(1)\n    end\n  end\n\n  describe \"arithmetic division /\" do\n    it \"divides negative numbers\" do\n      (7 / 2).should eq(3.5)\n      (-7 / 2).should eq(-3.5)\n      (7 / -2).should eq(-3.5)\n      (-7 / -2).should eq(3.5)\n\n      (6 / 2).should eq(3.0)\n      (-6 / 2).should eq(-3.0)\n      (6 / -2).should eq(-3.0)\n      (-6 / -2).should eq(3.0)\n    end\n\n    it \"divides by zero\" do\n      (1 / 0).should eq(Float64::INFINITY)\n    end\n\n    it \"divides Int::MIN by -1\" do\n      (Int8::MIN / -1).should eq(-(Int8::MIN.to_f64))\n      (Int16::MIN / -1).should eq(-(Int16::MIN.to_f64))\n      (Int32::MIN / -1).should eq(-(Int32::MIN.to_f64))\n      (Int64::MIN / -1).should eq(-(Int64::MIN.to_f64))\n      (Int128::MIN / -1).should eq(-(Int128::MIN.to_f64))\n\n      (UInt8::MIN / -1).should eq(0)\n    end\n  end\n\n  describe \"floor division //\" do\n    it \"preserves type of lhs\" do\n      {% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}\n        ({{type}}.new(7) // 2).should be_a({{type}})\n        ({{type}}.new(7) // 2.0).should be_a({{type}})\n        ({{type}}.new(7) // 2.0_f32).should be_a({{type}})\n      {% end %}\n    end\n\n    it \"divides negative numbers\" do\n      (7 // 2).should eq(3)\n      (-7 // 2).should eq(-4)\n      (7 // -2).should eq(-4)\n      (-7 // -2).should eq(3)\n\n      (6 // 2).should eq(3)\n      (-6 // 2).should eq(-3)\n      (6 // -2).should eq(-3)\n      (-6 // -2).should eq(3)\n    end\n  end\n\n  describe \"tdiv\" do\n    it \"divides self by argument using truncated division\" do\n      5.tdiv(3).should eq(1)\n      -5.tdiv(3).should eq(-1)\n      5.tdiv(-3).should eq(-1)\n      -5.tdiv(-3).should eq(1)\n    end\n\n    it \"preserves type of lhs\" do\n      {% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}\n        {{type}}.new(7).tdiv(2).should be_a({{type}})\n      {% end %}\n    end\n\n    it \"raises when divides by zero\" do\n      expect_raises(DivisionByZeroError) { 5.tdiv(0) }\n    end\n\n    it \"raises when divides Int::MIN by -1\" do\n      expect_raises(ArgumentError) { Int8::MIN.tdiv(-1) }\n      expect_raises(ArgumentError) { Int16::MIN.tdiv(-1) }\n      expect_raises(ArgumentError) { Int32::MIN.tdiv(-1) }\n      expect_raises(ArgumentError) { Int64::MIN.tdiv(-1) }\n      expect_raises(ArgumentError) { Int128::MIN.tdiv(-1) }\n\n      (UInt8::MIN.tdiv(-1)).should eq(0)\n    end\n  end\n\n  it \"holds true that x == q*y + r\" do\n    [5, -5, 6, -6, 10, -10].each do |x|\n      [3, -3].each do |y|\n        q = x // y\n        r = x % y\n        (q*y + r).should eq(x)\n      end\n    end\n  end\n\n  it \"raises when divides by zero\" do\n    expect_raises(DivisionByZeroError) { 1 // 0 }\n    (4 // 2).should eq(2)\n  end\n\n  it \"raises when divides Int::MIN by -1\" do\n    expect_raises(ArgumentError) { Int8::MIN // -1 }\n    expect_raises(ArgumentError) { Int16::MIN // -1 }\n    expect_raises(ArgumentError) { Int32::MIN // -1 }\n    expect_raises(ArgumentError) { Int64::MIN // -1 }\n    expect_raises(ArgumentError) { Int128::MIN // -1 }\n\n    (UInt8::MIN // -1).should eq(0)\n  end\n\n  it \"raises when mods by zero\" do\n    expect_raises(DivisionByZeroError) { 1 % 0 }\n    (4 % 2).should eq(0)\n  end\n\n  it \"% doesn't overflow (#7979)\" do\n    (53 % 532_000_782_588_491_410).should eq(53)\n  end\n\n  it_iterates \"#times\", [0, 1, 2], 3.times\n  it_iterates \"#times for UInt32 (#5019)\", [0_u32, 1_u32, 2_u32, 3_u32], 4_u32.times\n\n  it \"does %\" do\n    (7 % 5).should eq(2)\n    (-7 % 5).should eq(3)\n\n    (13 % -4).should eq(-3)\n    (-13 % -4).should eq(-1)\n  end\n\n  it \"returns 0 when doing IntN::MIN % -1 (#8306)\" do\n    {% for n in [8, 16, 32, 64, 128] %}\n      (Int{{n}}::MIN % -1.to_i{{n}}).should eq(0)\n    {% end %}\n  end\n\n  it \"does remainder\" do\n    7.remainder(5).should eq(2)\n    -7.remainder(5).should eq(-2)\n\n    13.remainder(-4).should eq(1)\n    -13.remainder(-4).should eq(-1)\n  end\n\n  it \"returns 0 when doing IntN::MIN.remainder(-1) (#8306)\" do\n    {% for n in [8, 16, 32, 64, 128] %}\n      (Int{{n}}::MIN.remainder(-1.to_i{{n}})).should eq(0)\n    {% end %}\n  end\n\n  it \"does upto\" do\n    i = sum = 0\n    1.upto(3) do |n|\n      i += 1\n      sum += n\n    end.should be_nil\n    i.should eq(3)\n    sum.should eq(6)\n  end\n\n  it \"does upto max\" do\n    i = sum = 0\n    (Int32::MAX - 3).upto(Int32::MAX) do |n|\n      i += 1\n      sum += Int32::MAX - n\n      n.should be >= (Int32::MAX - 3)\n    end.should be_nil\n    i.should eq(4)\n    sum.should eq(6)\n  end\n\n  it \"gets upto iterator\" do\n    iter = 1.upto(3)\n    iter.next.should eq(1)\n    iter.next.should eq(2)\n    iter.next.should eq(3)\n    iter.next.should be_a(Iterator::Stop)\n  end\n\n  it \"gets upto iterator max\" do\n    iter = (Int32::MAX - 3).upto(Int32::MAX)\n    iter.next.should eq(Int32::MAX - 3)\n    iter.next.should eq(Int32::MAX - 2)\n    iter.next.should eq(Int32::MAX - 1)\n    iter.next.should eq(Int32::MAX)\n    iter.next.should be_a(Iterator::Stop)\n  end\n\n  it \"upto iterator ups and downs\" do\n    0.upto(3).to_a.should eq([0, 1, 2, 3])\n    3.upto(0).to_a.should eq([] of Int32)\n    res = [Int32::MAX - 3, Int32::MAX - 2, Int32::MAX - 1, Int32::MAX]\n    (Int32::MAX - 3).upto(Int32::MAX).to_a.should eq(res)\n    Int32::MAX.upto(0).to_a.should eq([] of Int32)\n  end\n\n  it \"does downto\" do\n    i = sum = 0\n    3.downto(1) do |n|\n      i += 1\n      sum += n\n    end.should be_nil\n    i.should eq(3)\n    sum.should eq(6)\n  end\n\n  it \"does downto min\" do\n    i = sum = 0\n    (Int32::MIN + 3).downto(Int32::MIN) do |n|\n      i += 1\n      sum += n - Int32::MIN\n      n.should be <= Int32::MIN + 3\n    end\n    i.should eq(4)\n    sum.should eq(6)\n  end\n\n  it \"does downto min unsigned\" do\n    i = sum = 0\n    3_u16.downto(0) do |n|\n      i += 1\n      sum += n\n      n.should be <= 3_u16\n    end\n    i.should eq(4)\n    sum.should eq(6)\n  end\n\n  it \"gets downto iterator\" do\n    iter = 3.downto(1)\n    iter.next.should eq(3)\n    iter.next.should eq(2)\n    iter.next.should eq(1)\n    iter.next.should be_a(Iterator::Stop)\n  end\n\n  it \"downto iterator ups and downs\" do\n    3.downto(0).to_a.should eq([3, 2, 1, 0])\n    3_u16.downto(0).to_a.should eq([3_u16, 2_u16, 1_u16, 0_u16])\n    3.downto(4).to_a.should eq([] of Int32)\n    3_u16.downto(4_u16).to_a.should eq([] of UInt16)\n    res = [Int32::MIN + 3, Int32::MIN + 2, Int32::MIN + 1, Int32::MIN]\n    (Int32::MIN + 3).downto(Int32::MIN).to_a.should eq(res)\n  end\n\n  it \"gets downto iterator unsigned\" do\n    iter = 3_u16.downto(0)\n    iter.next.should eq(3)\n    iter.next.should eq(2)\n    iter.next.should eq(1)\n    iter.next.should eq(0)\n    iter.next.should be_a(Iterator::Stop)\n  end\n\n  it \"gets to iterator\" do\n    iter = 1.to(3)\n    iter.next.should eq(1)\n    iter.next.should eq(2)\n    iter.next.should eq(3)\n    iter.next.should be_a(Iterator::Stop)\n  end\n\n  describe \"#bit_reverse\" do\n    it { 0x12_u8.bit_reverse.should eq(0x48_u8) }\n    it { 0x1234_u16.bit_reverse.should eq(0x2C48_u16) }\n    it { 0x12345678_u32.bit_reverse.should eq(0x1E6A2C48_u32) }\n    it { 0x123456789ABCDEF0_u64.bit_reverse.should eq(0x0F7B3D591E6A2C48_u64) }\n    it { 1.to_u128.bit_reverse.should eq(1.to_u128 << 127) }\n    it { (1.to_u128 << 127).bit_reverse.should eq(0x1.to_u128) }\n    it { 0x12345678.to_u128.bit_reverse.should eq(0x1E6A2C48.to_u128 << 96) }\n\n    it { 0x12_i8.bit_reverse.should eq(0x48_i8) }\n    it { 0x1234_i16.bit_reverse.should eq(0x2C48_i16) }\n    it { 0x12345678_i32.bit_reverse.should eq(0x1E6A2C48_i32) }\n    it { 0x123456789ABCDEF0_i64.bit_reverse.should eq(0x0F7B3D591E6A2C48_i64) }\n    it { 1.to_i128.bit_reverse.should eq(1.to_i128 << 127) }\n    it { (1.to_i128 << 127).bit_reverse.should eq(0x1.to_i128) }\n    it { 0x12345678.to_i128.bit_reverse.should eq(0x1E6A2C48.to_i128 << 96) }\n\n    {% for width in %w(8 16 32 64 128).map(&.id) %}\n      it { 0.to_i{{width}}.bit_reverse.should be_a(Int{{width}}) }\n      it { 0.to_u{{width}}.bit_reverse.should be_a(UInt{{width}}) }\n    {% end %}\n  end\n\n  describe \"#byte_swap\" do\n    it { 0x12_u8.byte_swap.should eq(0x12_u8) }\n    it { 0x1234_u16.byte_swap.should eq(0x3412_u16) }\n    it { 0x12345678_u32.byte_swap.should eq(0x78563412_u32) }\n    it { 0x123456789ABCDEF0_u64.byte_swap.should eq(0xF0DEBC9A78563412_u64) }\n    it { 1.to_u128.byte_swap.should eq(1.to_u128 << 120) }\n    it { (1.to_u128 << 127).byte_swap.should eq(0x80.to_u128) }\n    it { 0x12345678.to_u128.byte_swap.should eq(0x78563412.to_u128 << 96) }\n\n    it { 0x12_i8.byte_swap.should eq(0x12_i8) }\n    it { 0x1234_i16.byte_swap.should eq(0x3412_i16) }\n    it { 0x12345678_i32.byte_swap.should eq(0x78563412_i32) }\n    it { 0x123456789ABCDEF0_i64.byte_swap.should eq(0xF0DEBC9A78563412_u64.to_i64!) }\n    it { 1.to_i128.byte_swap.should eq(1.to_i128 << 120) }\n    it { (1.to_i128 << 127).byte_swap.should eq(0x80.to_i128) }\n    it { 0x12345678.to_i128.byte_swap.should eq(0x78563412.to_i128 << 96) }\n\n    {% for width in %w(8 16 32 64 128).map(&.id) %}\n      it { 0.to_i{{width}}.byte_swap.should be_a(Int{{width}}) }\n      it { 0.to_u{{width}}.byte_swap.should be_a(UInt{{width}}) }\n    {% end %}\n  end\n\n  describe \"#popcount\" do\n    it { 5_i8.popcount.should eq(2) }\n    it { 127_i8.popcount.should eq(7) }\n    it { -1_i8.popcount.should eq(8) }\n    it { -128_i8.popcount.should eq(1) }\n\n    it { 0_u8.popcount.should eq(0) }\n    it { 255_u8.popcount.should eq(8) }\n\n    it { 5_i16.popcount.should eq(2) }\n    it { -6_i16.popcount.should eq(14) }\n    it { 65535_u16.popcount.should eq(16) }\n\n    it { 0_i32.popcount.should eq(0) }\n    it { 2147483647_i32.popcount.should eq(31) }\n    it { 4294967295_u32.popcount.should eq(32) }\n\n    it { 5_i64.popcount.should eq(2) }\n    it { 9223372036854775807_i64.popcount.should eq(63) }\n    it { 18446744073709551615_u64.popcount.should eq(64) }\n\n    it { 0_i128.popcount.should eq(0) }\n    it { Int128::MAX.popcount.should eq(127) }\n    it { UInt128::MAX.popcount.should eq(128) }\n  end\n\n  describe \"#leading_zeros_count\" do\n    {% for width in %w(8 16 32 64 128).map(&.id) %}\n      it { -1.to_i{{width}}.leading_zeros_count.should eq(0) }\n      it { 0.to_i{{width}}.leading_zeros_count.should eq({{width}}) }\n      it { 0.to_u{{width}}.leading_zeros_count.should eq({{width}}) }\n    {% end %}\n  end\n\n  describe \"#trailing_zeros_count\" do\n    {% for width in %w(8 16 32 64 128).map(&.id) %}\n      it { -2.to_i{{width}}.trailing_zeros_count.should eq(1) }\n      it { 2.to_i{{width}}.trailing_zeros_count.should eq(1) }\n      it { 2.to_u{{width}}.trailing_zeros_count.should eq(1) }\n    {% end %}\n  end\n\n  it \"compares signed vs. unsigned integers\" do\n    {% begin %}\n      signed_ints = [\n        Int8::MAX, Int16::MAX, Int32::MAX, Int64::MAX, Int128::MAX,\n        Int8::MIN, Int16::MIN, Int32::MIN, Int64::MIN, Int128::MIN,\n        Int8.zero, Int16.zero, Int32.zero, Int64.zero, Int128.zero,\n      ]\n      unsigned_ints = [\n        UInt8::MAX, UInt16::MAX, UInt32::MAX, UInt64::MAX, UInt128::MAX,\n        UInt8.zero, UInt16.zero, UInt32.zero, UInt64.zero, UInt128.zero,\n      ]\n\n      big_signed_ints = signed_ints.map &.to_big_i\n      big_unsigned_ints = unsigned_ints.map &.to_big_i\n\n      signed_ints.zip(big_signed_ints) do |si, bsi|\n        unsigned_ints.zip(big_unsigned_ints) do |ui, bui|\n          {% for op in %w(< <= > >=).map(&.id) %}\n            if (si {{op}} ui) != (bsi {{op}} bui)\n              fail \"comparison of #{si} {{op}} #{ui} (#{si.class} {{op}} #{ui.class}) gave incorrect result\"\n            end\n          {% end %}\n        end\n      end\n    {% end %}\n  end\n\n  it \"compares equality and inequality of signed vs. unsigned integers\" do\n    x = -1\n    y = x.unsafe_as(UInt32)\n\n    (x == y).should be_false\n    (y == x).should be_false\n    (x != y).should be_true\n    (y != x).should be_true\n  end\n\n  it \"clones\" do\n    [1_u8, 2_u16, 3_u32, 4_u64, 5.to_u128, 6_i8, 7_i16, 8_i32, 9_i64, 10.to_i128].each do |value|\n      value.clone.should eq(value)\n    end\n  end\n\n  it \"#chr\" do\n    65.chr.should eq('A')\n\n    expect_raises(ArgumentError, \"0x110000 out of char range\") do\n      (0x10ffff + 1).chr\n    end\n\n    expect_raises(ArgumentError, \"0xd800 out of char range\") do\n      0xd800.chr\n    end\n\n    expect_raises(ArgumentError, \"0xdfff out of char range\") do\n      0xdfff.chr\n    end\n  end\n\n  it \"#unsafe_chr\" do\n    65.unsafe_chr.should eq('A')\n    (0x10ffff + 1).unsafe_chr.ord.should eq(0x10ffff + 1)\n  end\n\n  describe \"#bit_length\" do\n    it \"for primitive integers\" do\n      0.bit_length.should eq(0)\n      0b1.bit_length.should eq(1)\n      0b1001.bit_length.should eq(4)\n      0b1001001_i64.bit_length.should eq(7)\n      0b1111111111.bit_length.should eq(10)\n      0b1000000000.bit_length.should eq(10)\n      -1.bit_length.should eq(0)\n      -10.bit_length.should eq(4)\n    end\n\n    it \"for BigInt\" do\n      (10.to_big_i ** 20).bit_length.should eq(67)\n      (10.to_big_i ** 309).bit_length.should eq(1027)\n      (10.to_big_i ** 3010).bit_length.should eq(10000)\n    end\n  end\n\n  describe \"#digits\" do\n    it \"works for positive numbers or zero\" do\n      0.digits.should eq([0])\n      1.digits.should eq([1])\n      10.digits.should eq([0, 1])\n      123.digits.should eq([3, 2, 1])\n      123456789.digits.should eq([9, 8, 7, 6, 5, 4, 3, 2, 1])\n    end\n\n    it \"works for maximums\" do\n      Int32::MAX.digits.should eq(Int32::MAX.to_s.chars.map(&.to_i).reverse!)\n      Int64::MAX.digits.should eq(Int64::MAX.to_s.chars.map(&.to_i).reverse!)\n      UInt64::MAX.digits.should eq(UInt64::MAX.to_s.chars.map(&.to_i).reverse!)\n      Int128::MAX.digits.should eq(Int128::MAX.to_s.chars.map(&.to_i).reverse!)\n      UInt128::MAX.digits.should eq(UInt128::MAX.to_s.chars.map(&.to_i).reverse!)\n    end\n\n    it \"works for non-Int32\" do\n      digits = 123_i64.digits\n      digits.should eq([3, 2, 1])\n    end\n\n    it \"works with a base\" do\n      123.digits(16).should eq([11, 7])\n    end\n\n    it \"raises for invalid base\" do\n      [1, 0, -1].each do |base|\n        expect_raises(ArgumentError, \"Invalid base #{base}\") do\n          123.digits(base)\n        end\n      end\n    end\n\n    it \"raises for negative numbers\" do\n      expect_raises(ArgumentError, \"Can't request digits of negative number\") do\n        -123.digits\n      end\n    end\n  end\n\n  describe \"from_digits\" do\n    it \"returns Int composed from given digits\" do\n      Int32.from_digits([9, 8, 7, 6, 5, 4, 3, 2, 1]).should eq(123456789)\n    end\n\n    it \"works with a base\" do\n      Int32.from_digits([11, 7], 16).should eq(123)\n      Int32.from_digits([11, 7], base: 16).should eq(123)\n    end\n\n    it \"accepts digits as Enumerable\" do\n      enumerable = IntEnumerable.new([11, 7])\n      Int32.from_digits(enumerable, 16).should eq(123)\n    end\n\n    it \"raises for base less than 2\" do\n      [-1, 0, 1].each do |base|\n        expect_raises(ArgumentError, \"Invalid base #{base}\") do\n          Int32.from_digits([1, 2, 3], base)\n        end\n      end\n    end\n\n    it \"raises for digits greater than base\" do\n      expect_raises(ArgumentError, \"Invalid digit 2 for base 2\") do\n        Int32.from_digits([1, 0, 2], 2)\n      end\n\n      expect_raises(ArgumentError, \"Invalid digit 10 for base 2\") do\n        Int32.from_digits([1, 0, 10], 2)\n      end\n    end\n\n    it \"raises for negative digits\" do\n      expect_raises(ArgumentError, \"Invalid digit -1\") do\n        Int32.from_digits([1, 2, -1])\n      end\n    end\n\n    it \"works properly for values close to the upper limit\" do\n      UInt8.from_digits([5, 5, 2]).should eq(255)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/argf_spec.cr",
    "content": "require \"../spec_helper\"\n\ndescribe IO::ARGF do\n  it \"reads from STDIN if ARGV isn't specified\" do\n    argv = [] of String\n    stdin = IO::Memory.new(\"hello\")\n\n    argf = IO::ARGF.new argv, stdin\n    argf.path.should eq(\"-\")\n    argf.gets_to_end.should eq(\"hello\")\n    argf.read_byte.should be_nil\n  end\n\n  it \"reads from ARGV if specified\" do\n    path1 = datapath(\"argf_test_file_1.txt\")\n    path2 = datapath(\"argf_test_file_2.txt\")\n    stdin = IO::Memory.new(\"\")\n    argv = [path1, path2]\n\n    argf = IO::ARGF.new argv, stdin\n    argf.path.should eq(path1)\n    argv.should eq([path1, path2])\n\n    str = argf.gets(5)\n    str.should eq(\"12345\")\n\n    argv.should eq([path2])\n\n    str = argf.gets_to_end\n    str.should eq(\"\\n67890\\n\")\n\n    argv.should be_empty\n\n    argf.read_byte.should be_nil\n\n    argv << path1\n    str = argf.gets(5)\n    str.should eq(\"12345\")\n  end\n\n  it \"reads when is more data left to read\" do\n    argf = IO::ARGF.new [datapath(\"argf_test_file_3.xml\")], IO::Memory.new\n    argf.read(Bytes.new(4))\n    buf = Bytes.new(4096)\n    argf.read(buf)\n    argf.read(buf)\n    argf.read(buf)\n    z = argf.read(buf)\n    z.should_not eq 0\n    String.new(buf[0...z]).should_not be_empty\n  end\n\n  describe \"gets\" do\n    it \"reads from STDIN if ARGV isn't specified\" do\n      argv = [] of String\n      stdin = IO::Memory.new(\"hello\\nworld\\n\")\n\n      argf = IO::ARGF.new argv, stdin\n      argf.gets.should eq(\"hello\")\n      argf.gets.should eq(\"world\")\n      argf.gets.should be_nil\n    end\n\n    it \"reads from STDIN if ARGV isn't specified, chomp = false\" do\n      argv = [] of String\n      stdin = IO::Memory.new(\"hello\\nworld\\n\")\n\n      argf = IO::ARGF.new argv, stdin\n      argf.gets(chomp: false).should eq(\"hello\\n\")\n      argf.gets(chomp: false).should eq(\"world\\n\")\n      argf.gets(chomp: false).should be_nil\n    end\n\n    it \"reads from ARGV if specified\" do\n      path1 = datapath(\"argf_test_file_1.txt\")\n      path2 = datapath(\"argf_test_file_2.txt\")\n      stdin = IO::Memory.new(\"\")\n      argv = [path1, path2]\n\n      argf = IO::ARGF.new argv, stdin\n      argv.should eq([path1, path2])\n\n      argf.gets(chomp: false).should eq(\"12345\\n\")\n      argv.should eq([path2])\n\n      argf.gets(chomp: false).should eq(\"67890\\n\")\n      argv.should be_empty\n\n      argf.gets(chomp: false).should be_nil\n\n      argv << path1\n      str = argf.gets(chomp: false)\n      str.should eq(\"12345\\n\")\n    end\n  end\n\n  describe \"peek\" do\n    it \"peeks from STDIN if ARGV isn't specified\" do\n      argv = [] of String\n      stdin = IO::Memory.new(\"1234\")\n\n      argf = IO::ARGF.new argv, stdin\n      argf.peek.should eq(\"1234\".to_slice)\n\n      argf.gets_to_end.should eq(\"1234\")\n    end\n\n    it \"peeks from ARGV if specified\" do\n      path1 = datapath(\"argf_test_file_1.txt\")\n      path2 = datapath(\"argf_test_file_2.txt\")\n      stdin = IO::Memory.new(\"\")\n      argv = [path1, path2]\n\n      argf = IO::ARGF.new argv, stdin\n      argf.peek.should eq(\"12345\\n\".to_slice)\n\n      argf.read_string(6)\n      argf.read_byte\n\n      argf.peek.should eq(\"7890\\n\".to_slice)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/buffered_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/string\"\n\nprivate class BufferedWrapper < IO\n  include IO::Buffered\n\n  getter called_unbuffered_read\n\n  @io : IO\n  @called_unbuffered_read : Bool\n\n  def initialize(@io)\n    @called_unbuffered_read = false\n  end\n\n  def self.new(io, &)\n    buffered_io = new(io)\n    yield buffered_io\n    buffered_io.flush\n    io\n  end\n\n  private def unbuffered_read(slice : Bytes) : Int32\n    @called_unbuffered_read = true\n    @io.read(slice)\n  end\n\n  private def unbuffered_write(slice : Bytes) : Nil\n    @io.write(slice)\n  end\n\n  private def unbuffered_flush\n    @io.flush\n  end\n\n  def fd\n    @io.fd\n  end\n\n  private def unbuffered_close\n    @io.close\n  end\n\n  def closed?\n    @io.closed?\n  end\n\n  private def unbuffered_rewind\n    @io.rewind\n  end\nend\n\nDEFAULT_BUFFER_SIZE = BufferedWrapper.new(IO::Memory.new(\"\")).buffer_size\n\ndescribe \"IO::Buffered\" do\n  it \"can report buffer_size\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"\"))\n    io.buffer_size.should eq(DEFAULT_BUFFER_SIZE)\n  end\n\n  it \"can set buffer_size\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"\"))\n    io.buffer_size = 16_384\n    io.buffer_size.should eq(16_384)\n  end\n\n  it \"can not set buffer_size after first use\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\r\\nworld\\n\"))\n    io.gets\n    expect_raises ArgumentError, \"buffer_size\" do\n      io.buffer_size = 16_384\n    end\n  end\n\n  it \"can set buffer_size to the same value after first use\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\r\\nworld\\n\"))\n    io.buffer_size = 16_384\n    io.gets\n\n    io.buffer_size = 16_384\n    io.buffer_size.should eq(16_384)\n  end\n\n  it \"does gets\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\r\\nworld\\n\"))\n    io.gets.should eq(\"hello\")\n    io.gets.should eq(\"world\")\n    io.gets.should be_nil\n  end\n\n  it \"does gets with chomp = false\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\nworld\\n\"))\n    io.gets(chomp: false).should eq(\"hello\\n\")\n    io.gets(chomp: false).should eq(\"world\\n\")\n    io.gets(chomp: false).should be_nil\n  end\n\n  it \"does gets with big line\" do\n    big_line = \"a\" * 20_000\n    io = BufferedWrapper.new(IO::Memory.new(\"#{big_line}\\nworld\\n\"))\n    io.gets.should eq(big_line)\n  end\n\n  it \"does gets with big line and \\\\r\\\\n\" do\n    big_line = \"a\" * 20_000\n    io = BufferedWrapper.new(IO::Memory.new(\"#{big_line}\\r\\nworld\\n\"))\n    io.gets.should eq(big_line)\n  end\n\n  it \"does gets with big line and chomp = false\" do\n    big_line = \"a\" * 20_000\n    io = BufferedWrapper.new(IO::Memory.new(\"#{big_line}\\nworld\\n\"))\n    io.gets(chomp: false).should eq(\"#{big_line}\\n\")\n  end\n\n  it \"does gets with char delimiter\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello world\"))\n    io.gets('w').should eq(\"hello w\")\n    io.gets('r').should eq(\"or\")\n    io.gets('r').should eq(\"ld\")\n    io.gets('r').should be_nil\n  end\n\n  it \"does gets with unicode char delimiter\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"こんにちは\"))\n    io.gets('ち').should eq(\"こんにち\")\n    io.gets('ち').should eq(\"は\")\n    io.gets('ち').should be_nil\n  end\n\n  it \"does gets with limit\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\nworld\\n\"))\n    io.gets(3).should eq(\"hel\")\n    io.gets(10_000).should eq(\"lo\\n\")\n    io.gets(10_000).should eq(\"world\\n\")\n    io.gets(3).should be_nil\n  end\n\n  it \"does gets with char and limit\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\nworld\\n\"))\n    io.gets('o', 2).should eq(\"he\")\n    io.gets('w', 10_000).should eq(\"llo\\nw\")\n    io.gets('z', 10_000).should eq(\"orld\\n\")\n    io.gets('a', 3).should be_nil\n  end\n\n  it \"does gets with char and limit without off-by-one\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"test\\nabc\"))\n    io.gets('a', 5).should eq(\"test\\n\")\n    io = BufferedWrapper.new(IO::Memory.new(\"test\\nabc\"))\n    io.gets('a', 6).should eq(\"test\\na\")\n  end\n\n  it \"does gets with char and limit when not found in buffer\" do\n    io = BufferedWrapper.new(IO::Memory.new((\"a\" * (DEFAULT_BUFFER_SIZE + 10)) + \"b\"))\n    io.gets('b', 2).should eq(\"aa\")\n  end\n\n  it \"does gets with char and limit when not found in buffer (2)\" do\n    base = \"a\" * (DEFAULT_BUFFER_SIZE + 10)\n    io = BufferedWrapper.new(IO::Memory.new(base + \"aabaaa\"))\n    io.gets('b', DEFAULT_BUFFER_SIZE + 11).should eq(base + \"a\")\n  end\n\n  it \"raises if invoking gets with negative limit\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\\nworld\\n\"))\n    expect_raises ArgumentError, \"Negative limit\" do\n      io.gets(-1)\n    end\n  end\n\n  it \"writes bytes\" do\n    str = IO::Memory.new\n    io = BufferedWrapper.new(str)\n    10_000.times { io.write_byte 'a'.ord.to_u8 }\n    io.flush\n    str.to_s.should eq(\"a\" * 10_000)\n  end\n\n  it \"reads char\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hi 世界\"))\n    io.read_char.should eq('h')\n    io.read_char.should eq('i')\n    io.read_char.should eq(' ')\n    io.read_char.should eq('世')\n    io.read_char.should eq('界')\n    io.read_char.should be_nil\n\n    {% for bytes, char in VALID_UTF8_BYTE_SEQUENCES %}\n      BufferedWrapper.new(IO::Memory.new(Bytes{{ bytes }})).read_char.should eq({{ char }})\n    {% end %}\n\n    {% for bytes in INVALID_UTF8_BYTE_SEQUENCES %}\n      expect_raises(InvalidByteSequenceError) do\n        BufferedWrapper.new(IO::Memory.new(Bytes{{ bytes }})).read_char\n      end\n    {% end %}\n  end\n\n  it \"reads byte\" do\n    io = BufferedWrapper.new(IO::Memory.new(\"hello\"))\n    io.read_byte.should eq('h'.ord)\n    io.read_byte.should eq('e'.ord)\n    io.read_byte.should eq('l'.ord)\n    io.read_byte.should eq('l'.ord)\n    io.read_byte.should eq('o'.ord)\n    io.read_char.should be_nil\n  end\n\n  it \"does new with block\" do\n    str = IO::Memory.new\n    res = BufferedWrapper.new str, &.print \"Hello\"\n    res.should be(str)\n    str.to_s.should eq(\"Hello\")\n  end\n\n  it \"rewinds\" do\n    str = IO::Memory.new(\"hello\\nworld\\n\")\n    io = BufferedWrapper.new str\n    io.gets.should eq(\"hello\")\n    io.rewind\n    io.gets.should eq(\"hello\")\n  end\n\n  it \"reads more than the buffer's internal capacity\" do\n    s = String.build do |str|\n      900.times do\n        10.times do |i|\n          str << ('a' + i)\n        end\n      end\n    end\n    io = BufferedWrapper.new(IO::Memory.new(s))\n\n    slice = Bytes.new(9000)\n    count = io.read(slice)\n    count.should eq(9000)\n\n    900.times do\n      10.times do |i|\n        slice[i].should eq('a'.ord + i)\n      end\n    end\n  end\n\n  it \"writes more than the buffer's internal capacity\" do\n    s = String.build do |str|\n      900.times do\n        10.times do |i|\n          str << ('a' + i)\n        end\n      end\n    end\n    strio = IO::Memory.new\n    strio << s\n    strio.rewind\n    io = BufferedWrapper.new(strio)\n    io.write(s.to_slice)\n    strio.rewind.gets_to_end.should eq(s)\n  end\n\n  it \"does puts\" do\n    str = IO::Memory.new\n    io = BufferedWrapper.new(str)\n    io.puts \"Hello\"\n    str.to_s.should eq(\"\")\n    io.flush\n    str.to_s.should eq(\"Hello\\n\")\n  end\n\n  it \"does puts with big string\" do\n    str = IO::Memory.new\n    io = BufferedWrapper.new(str)\n    s = \"*\" * 20_000\n    io << \"hello\"\n    io << s\n    io.flush\n    str.to_s.should eq(\"hello#{s}\")\n  end\n\n  it \"does puts many times\" do\n    str = IO::Memory.new\n    io = BufferedWrapper.new(str)\n    10_000.times { io << \"hello\" }\n    io.flush\n    str.to_s.should eq(\"hello\" * 10_000)\n  end\n\n  describe \"flush_on_newline\" do\n    it \"flushes on \\n\" do\n      str = IO::Memory.new\n      io = BufferedWrapper.new(str)\n      io.flush_on_newline = true\n\n      io << \"hello\\nworld\"\n      str.to_s.should eq(\"hello\\n\")\n      io.flush\n      str.to_s.should eq(\"hello\\nworld\")\n    end\n\n    it \"doesn't write past count\" do\n      str = IO::Memory.new\n      io = BufferedWrapper.new(str)\n      io.flush_on_newline = true\n\n      slice = Slice.new(10) { |i| i == 9 ? '\\n'.ord.to_u8 : ('a'.ord + i).to_u8 }\n      io.write slice[0, 4]\n      io.flush\n      str.to_s.should eq(\"abcd\")\n    end\n  end\n\n  describe \"sync\" do\n    it \"syncs (write)\" do\n      str = IO::Memory.new\n\n      io = BufferedWrapper.new(str)\n      io.sync?.should be_false\n\n      io.sync = true\n      io.sync?.should be_true\n\n      io.write_byte 1_u8\n\n      str.rewind\n      str.read_byte.should eq(1_u8)\n    end\n  end\n\n  describe \"read_buffering\" do\n    it \"works with IO#read\" do\n      str = IO::Memory.new \"abc\"\n\n      io = BufferedWrapper.new(str)\n      io.read_buffering?.should be_true\n\n      io.read_buffering = false\n      io.read_buffering?.should be_false\n\n      byte = Bytes.new(1)\n      io.read_fully(byte)\n      byte[0].should eq('a'.ord.to_u8)\n\n      str.gets_to_end.should eq(\"bc\")\n    end\n\n    it \"works with IO#read (already buffered)\" do\n      str = IO::Memory.new\n      str << \"a\" * DEFAULT_BUFFER_SIZE\n      str.pos = 0\n\n      io = BufferedWrapper.new(str)\n      io.read_buffering?.should be_true\n\n      io.buffer_size.times do\n        byte = Bytes.new(1)\n        io.read_fully(byte)\n        byte[0].should eq('a'.ord.to_u8)\n      end\n\n      io.read_buffering = false\n      io.read_buffering?.should be_false\n\n      str << \"bcde\"\n      str.pos -= 4\n\n      byte = Bytes.new(1)\n      io.read_fully(byte)\n      byte[0].should eq('b'.ord.to_u8)\n\n      str.gets_to_end.should eq(\"cde\")\n    end\n\n    it \"works with IO#read_byte\" do\n      str = IO::Memory.new \"abc\"\n\n      io = BufferedWrapper.new(str)\n      io.read_buffering?.should be_true\n\n      io.read_buffering = false\n      io.read_buffering?.should be_false\n\n      io.read_byte.should eq('a'.ord.to_u8)\n\n      str.gets_to_end.should eq(\"bc\")\n    end\n\n    it \"works with IO#read_byte (already buffered)\" do\n      str = IO::Memory.new\n      str << \"a\" * DEFAULT_BUFFER_SIZE\n      str.pos = 0\n\n      io = BufferedWrapper.new(str)\n      io.read_buffering?.should be_true\n\n      io.buffer_size.times do\n        io.read_byte.should eq('a'.ord.to_u8)\n      end\n\n      io.read_buffering = false\n      io.read_buffering?.should be_false\n\n      str << \"bcde\"\n      str.pos -= 4\n\n      io.read_byte.should eq('b'.ord.to_u8)\n\n      str.gets_to_end.should eq(\"cde\")\n    end\n  end\n\n  it \"shouldn't call unbuffered read if reading to an empty slice\" do\n    str = IO::Memory.new(\"foo\")\n    io = BufferedWrapper.new(str)\n    io.read(Bytes[])\n    io.called_unbuffered_read.should be_false\n  end\n\n  it \"peeks\" do\n    str = IO::Memory.new(\"foo\")\n    io = BufferedWrapper.new(str)\n\n    io.peek.should eq(\"foo\".to_slice)\n\n    # Peek doesn't advance\n    io.gets_to_end.should eq(\"foo\")\n\n    # Returns EOF if no more data\n    io.peek.should eq(Bytes.empty)\n  end\n\n  it \"skips\" do\n    str = IO::Memory.new(\"123456789\")\n    io = BufferedWrapper.new(str)\n    io.skip(3)\n    io.read_char.should eq('4')\n  end\n\n  it \"skips big\" do\n    str = IO::Memory.new((\"a\" * 10_000) + \"b\")\n    io = BufferedWrapper.new(str)\n    io.skip(10_000)\n    io.read_char.should eq('b')\n  end\n\n  {% unless flag?(:without_iconv) %}\n    describe \"encoding\" do\n      describe \"decode\" do\n        it \"gets_to_end\" do\n          str = \"Hello world\" * 200\n          base_io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io = BufferedWrapper.new(base_io)\n          io.set_encoding(\"UCS-2LE\")\n          io.gets_to_end.should eq(str)\n        end\n\n        it \"gets\" do\n          str = \"Hello world\\nFoo\\nBar\\n\" + (\"1234567890\" * 1000)\n          base_io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io = BufferedWrapper.new(base_io)\n          io.set_encoding(\"UCS-2LE\")\n          io.gets.should eq(\"Hello world\")\n          io.gets.should eq(\"Foo\")\n          io.gets.should eq(\"Bar\")\n        end\n\n        it \"gets with chomp = false\" do\n          str = \"Hello world\\nFoo\\nBar\\n\" + (\"1234567890\" * 1000)\n          base_io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io = BufferedWrapper.new(base_io)\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(chomp: false).should eq(\"Hello world\\n\")\n          io.gets(chomp: false).should eq(\"Foo\\n\")\n          io.gets(chomp: false).should eq(\"Bar\\n\")\n        end\n\n        it \"gets big string\" do\n          str = \"Hello\\nWorld\\n\" * 10_000\n          base_io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io = BufferedWrapper.new(base_io)\n          io.set_encoding(\"UCS-2LE\")\n          10_000.times do |i|\n            io.gets(chomp: false).should eq(\"Hello\\n\")\n            io.gets(chomp: false).should eq(\"World\\n\")\n          end\n        end\n\n        it \"gets big EUC-JP string\" do\n          str = (\"好我是人\\n\" * 1000).encode(\"EUC-JP\")\n          base_io = IO::Memory.new(str)\n          io = BufferedWrapper.new(base_io)\n          io.set_encoding(\"EUC-JP\")\n          1000.times do\n            io.gets(chomp: false).should eq(\"好我是人\\n\")\n          end\n        end\n\n        it \"reads char\" do\n          str = \"x\\nHello world\" + (\"1234567890\" * 1000)\n          base_io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io = BufferedWrapper.new(base_io)\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(chomp: false).should eq(\"x\\n\")\n          str = str[2..-1]\n          str.each_char do |char|\n            io.read_char.should eq(char)\n          end\n          io.read_char.should be_nil\n        end\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/io/byte_format_spec.cr",
    "content": "require \"spec\"\n\nprivate def assert_bytes(io, *bytes)\n  io.rewind\n  bytes.each do |byte|\n    io.read_byte.should eq(byte)\n  end\n  io.read_byte.should be_nil\nend\n\nprivate def assert_bytes_reversed(io, *bytes)\n  assert_bytes io, *bytes.reverse\nend\n\nprivate def new_string_io(*bytes)\n  io = IO::Memory.new\n  bytes.each { |byte| io.write_byte byte.to_u8 }\n  io.rewind\n  io\nend\n\nprivate module ReadBytesConverter\n  def self.from_io(io : IO, format = IO::ByteFormat::NetworkEndian) : Int32\n    io.read_bytes(Int32, format: format)\n  end\nend\n\ndescribe IO::ByteFormat do\n  describe \"little endian\" do\n    describe \"encode\" do\n      describe \"to io\" do\n        it \"writes int8\" do\n          io = IO::Memory.new\n          io.write_bytes 0x12_i8, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0x12\n        end\n\n        it \"writes int16\" do\n          io = IO::Memory.new\n          io.write_bytes 0x1234_i16, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0x34, 0x12\n        end\n\n        it \"writes uint16\" do\n          io = IO::Memory.new\n          io.write_bytes 0x1234_u16, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0x34, 0x12\n        end\n\n        it \"writes int32\" do\n          io = IO::Memory.new\n          io.write_bytes 0x12345678_i32, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0x78, 0x56, 0x34, 0x12\n        end\n\n        it \"writes int64\" do\n          io = IO::Memory.new\n          io.write_bytes 0x123456789ABCDEF0_i64, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12\n        end\n\n        it \"writes float32\" do\n          io = IO::Memory.new\n          io.write_bytes 1.234_f32, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0xB6, 0xF3, 0x9D, 0x3F\n        end\n\n        it \"writes float64\" do\n          io = IO::Memory.new\n          io.write_bytes 1.234, IO::ByteFormat::LittleEndian\n          assert_bytes io, 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F\n        end\n      end\n\n      describe \"to slice\" do\n        it \"writes int8\" do\n          bytes = Bytes[0]\n          IO::ByteFormat::LittleEndian.encode(0x12_i8, bytes)\n          bytes.should eq(Bytes[0x12_i8])\n        end\n\n        it \"writes int16\" do\n          bytes = Bytes[0, 0]\n          IO::ByteFormat::LittleEndian.encode(0x1234_i16, bytes)\n          bytes.should eq(Bytes[0x34, 0x12])\n        end\n\n        it \"writes int16 to larger slice\" do\n          bytes = Bytes[0, 0, 0, 0]\n          IO::ByteFormat::LittleEndian.encode(0x1234_i16, bytes)\n          bytes.should eq(Bytes[0x34, 0x12, 0, 0])\n        end\n      end\n    end\n\n    describe \"decode\" do\n      describe \"from io\" do\n        it \"reads int8\" do\n          io = new_string_io(0x12)\n          int = io.read_bytes Int8, IO::ByteFormat::LittleEndian\n          int.should eq(0x12_i8)\n        end\n\n        it \"reads int16\" do\n          io = new_string_io(0x34, 0x12)\n          int = io.read_bytes Int16, IO::ByteFormat::LittleEndian\n          int.should eq(0x1234_i16)\n        end\n\n        it \"reads unt16\" do\n          io = new_string_io(0x34, 0x12)\n          int = io.read_bytes UInt16, IO::ByteFormat::LittleEndian\n          int.should eq(0x1234_u16)\n        end\n\n        it \"reads int32\" do\n          io = new_string_io(0x78, 0x56, 0x34, 0x12)\n          int = io.read_bytes Int32, IO::ByteFormat::LittleEndian\n          int.should eq(0x12345678_i32)\n        end\n\n        it \"reads int64\" do\n          io = new_string_io(0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12)\n          int = io.read_bytes Int64, IO::ByteFormat::LittleEndian\n          int.should eq(0x123456789ABCDEF0_i64)\n        end\n\n        it \"reads float32\" do\n          io = new_string_io(0xB6, 0xF3, 0x9D, 0x3F)\n          float = io.read_bytes Float32, IO::ByteFormat::LittleEndian\n          float.should be_close(1.234, 0.0001)\n        end\n\n        it \"reads float64\" do\n          io = new_string_io(0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F)\n          float = io.read_bytes Float64, IO::ByteFormat::LittleEndian\n          float.should be_close(1.234, 0.0001)\n        end\n\n        it \"reads with converter\" do\n          io = new_string_io(0x78, 0x56, 0x34, 0x12)\n          io.read_bytes(ReadBytesConverter, IO::ByteFormat::LittleEndian).should eq 0x12345678_i32\n        end\n      end\n\n      describe \"from slice\" do\n        it \"reads int8\" do\n          bytes = Bytes[0x12]\n          int = IO::ByteFormat::LittleEndian.decode(Int8, bytes)\n          int.should eq(0x12_i8)\n        end\n\n        it \"reads int16\" do\n          bytes = Bytes[0x34, 0x12]\n          int = IO::ByteFormat::LittleEndian.decode(Int16, bytes)\n          int.should eq(0x1234_i16)\n        end\n\n        it \"reads int16 from larger slice\" do\n          bytes = Bytes[0x34, 0x12, 0, 0]\n          int = IO::ByteFormat::LittleEndian.decode(Int16, bytes)\n          int.should eq(0x1234_i16)\n        end\n\n        it \"reads float32\" do\n          bytes = Bytes[0xB6, 0xF3, 0x9D, 0x3F]\n          float = IO::ByteFormat::LittleEndian.decode(Float32, bytes)\n          float.should be_close(1.234, 0.0001)\n        end\n\n        it \"reads float64\" do\n          bytes = Bytes[0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F]\n          float = IO::ByteFormat::LittleEndian.decode(Float64, bytes)\n          float.should be_close(1.234, 0.0001)\n        end\n      end\n    end\n  end\n\n  describe \"big endian\" do\n    describe \"encode\" do\n      it \"writes int8\" do\n        io = IO::Memory.new\n        io.write_bytes 0x12_i8, IO::ByteFormat::BigEndian\n        assert_bytes io, 0x12\n      end\n\n      it \"writes int16\" do\n        io = IO::Memory.new\n        io.write_bytes 0x1234_i16, IO::ByteFormat::BigEndian\n        assert_bytes_reversed io, 0x34, 0x12\n      end\n\n      it \"writes int32\" do\n        io = IO::Memory.new\n        io.write_bytes 0x12345678_i32, IO::ByteFormat::BigEndian\n        assert_bytes_reversed io, 0x78, 0x56, 0x34, 0x12\n      end\n\n      it \"writes int64\" do\n        io = IO::Memory.new\n        io.write_bytes 0x123456789ABCDEF0_i64, IO::ByteFormat::BigEndian\n        assert_bytes_reversed io, 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12\n      end\n\n      it \"writes float32\" do\n        io = IO::Memory.new\n        io.write_bytes 1.234_f32, IO::ByteFormat::BigEndian\n        assert_bytes_reversed io, 0xB6, 0xF3, 0x9D, 0x3F\n      end\n\n      it \"writes float64\" do\n        io = IO::Memory.new\n        io.write_bytes 1.234, IO::ByteFormat::BigEndian\n        assert_bytes_reversed io, 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F\n      end\n    end\n\n    describe \"decode\" do\n      describe \"from io\" do\n        it \"reads int8\" do\n          io = new_string_io(0x12)\n          int = io.read_bytes Int8, IO::ByteFormat::BigEndian\n          int.should eq(0x12_i8)\n        end\n\n        it \"reads int16\" do\n          io = new_string_io(0x12, 0x34)\n          int = io.read_bytes Int16, IO::ByteFormat::BigEndian\n          int.should eq(0x1234_i16)\n        end\n\n        it \"reads unt16\" do\n          io = new_string_io(0x12, 0x34)\n          int = io.read_bytes UInt16, IO::ByteFormat::BigEndian\n          int.should eq(0x1234_u16)\n        end\n\n        it \"reads int32\" do\n          io = new_string_io(0x12, 0x34, 0x56, 0x78)\n          int = io.read_bytes Int32, IO::ByteFormat::BigEndian\n          int.should eq(0x12345678_i32)\n        end\n\n        it \"reads int64\" do\n          io = new_string_io(0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0)\n          int = io.read_bytes Int64, IO::ByteFormat::BigEndian\n          int.should eq(0x123456789ABCDEF0_i64)\n        end\n\n        it \"reads float32\" do\n          io = new_string_io(0x3F, 0x9D, 0xF3, 0xB6)\n          float = io.read_bytes Float32, IO::ByteFormat::BigEndian\n          float.should be_close(1.234, 0.0001)\n        end\n\n        it \"reads float64\" do\n          io = new_string_io(0x3F, 0xF3, 0xBE, 0x76, 0xC8, 0xB4, 0x39, 0x58)\n          float = io.read_bytes Float64, IO::ByteFormat::BigEndian\n          float.should be_close(1.234, 0.0001)\n        end\n\n        it \"reads with converter\" do\n          io = new_string_io(0x12, 0x34, 0x56, 0x78)\n          io.read_bytes(ReadBytesConverter, IO::ByteFormat::BigEndian).should eq 0x12345678_i32\n        end\n      end\n\n      describe \"from slice\" do\n        it \"reads int8\" do\n          bytes = Bytes[0x12]\n          int = IO::ByteFormat::BigEndian.decode(Int8, bytes)\n          int.should eq(0x12_i8)\n        end\n\n        it \"reads int16\" do\n          bytes = Bytes[0x12, 0x34]\n          int = IO::ByteFormat::BigEndian.decode(Int16, bytes)\n          int.should eq(0x1234_i16)\n        end\n\n        it \"reads float32\" do\n          bytes = Bytes[0x3F, 0x9D, 0xF3, 0xB6]\n          float = IO::ByteFormat::BigEndian.decode(Float32, bytes)\n          float.should be_close(1.234, 0.0001)\n        end\n\n        it \"reads float64\" do\n          bytes = Bytes[0x3F, 0xF3, 0xBE, 0x76, 0xC8, 0xB4, 0x39, 0x58]\n          float = IO::ByteFormat::BigEndian.decode(Float64, bytes)\n          float.should be_close(1.234, 0.0001)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/delimited_spec.cr",
    "content": "require \"spec\"\n\nprivate class PartialReaderIO < IO\n  @slice : Bytes\n\n  def initialize(data : String)\n    @slice = data.to_slice\n  end\n\n  def read(slice : Bytes)\n    return 0 if @slice.size == 0\n    max_read_size = {slice.size, @slice.size}.min\n    read_size = rand(1..max_read_size)\n    slice.copy_from(@slice[0, read_size])\n    @slice += read_size\n    read_size\n  end\n\n  def write(slice : Bytes) : NoReturn\n    raise \"write\"\n  end\nend\n\nprivate class MemoryIOWithoutPeek < IO::Memory\n  def peek\n    nil\n  end\nend\n\nprivate class MemoryIOWithFixedPeek < IO::Memory\n  property peek_size = 0\n\n  def peek\n    Slice.new(@buffer + @pos, {@bytesize - @pos, peek_size}.min)\n  end\nend\n\ndescribe \"IO::Delimited\" do\n  describe \"#read\" do\n    context \"without peeking\" do\n      it \"doesn't read past the limit\" do\n        io = MemoryIOWithoutPeek.new(\"abcderzzrfgzr\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"zr\")\n\n        delimited.gets_to_end.should eq(\"abcderz\")\n        io.gets_to_end.should eq(\"fgzr\")\n      end\n\n      it \"doesn't read past the limit (char-by-char)\" do\n        io = MemoryIOWithoutPeek.new(\"abcderzzrfg\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"zr\")\n\n        delimited.read_char.should eq('a')\n        delimited.read_char.should eq('b')\n        delimited.read_char.should eq('c')\n        delimited.read_char.should eq('d')\n        delimited.read_char.should eq('e')\n        delimited.read_char.should eq('r')\n        delimited.read_char.should eq('z')\n        delimited.read_char.should be_nil\n        delimited.read_char.should be_nil\n        delimited.read_char.should be_nil\n        delimited.read_char.should be_nil\n\n        io.read_char.should eq('f')\n        io.read_char.should eq('g')\n      end\n\n      it \"doesn't clobber active_delimiter_buffer\" do\n        io = MemoryIOWithoutPeek.new(\"ab12312\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"12345\")\n\n        delimited.gets_to_end.should eq(\"ab12312\")\n      end\n\n      it \"handles the delimiter at the start\" do\n        io = MemoryIOWithoutPeek.new(\"ab12312\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"ab1\")\n\n        delimited.read_char.should be_nil\n      end\n\n      it \"handles the delimiter at the end\" do\n        io = MemoryIOWithoutPeek.new(\"ab12312z\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"z\")\n\n        delimited.gets_to_end.should eq(\"ab12312\")\n      end\n\n      it \"handles nearly a delimiter at the end\" do\n        io = MemoryIOWithoutPeek.new(\"ab12312\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"122\")\n\n        delimited.gets_to_end.should eq(\"ab12312\")\n      end\n\n      it \"doesn't clobber the buffer on closely-offset partial matches\" do\n        io = MemoryIOWithoutPeek.new(\"abab1234abcdefgh\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"abcdefgh\")\n\n        delimited.gets_to_end.should eq(\"abab1234\")\n      end\n    end\n\n    context \"with partial read\" do\n      it \"handles partial reads\" do\n        io = PartialReaderIO.new(\"abab1234abcdefgh\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"abcdefgh\")\n\n        delimited.gets_to_end.should eq(\"abab1234\")\n      end\n    end\n\n    context \"with peeking\" do\n      it \"returns empty when there's no data\" do\n        io = IO::Memory.new(\"\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"zr\")\n\n        delimited.peek.should eq(\"\".to_slice)\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"\")\n      end\n\n      it \"doesn't read past the limit\" do\n        io = IO::Memory.new(\"abcderzzrfgzr\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"zr\")\n\n        delimited.peek.should eq(\"abcderz\".to_slice)\n        delimited.gets_to_end.should eq(\"abcderz\")\n        io.gets_to_end.should eq(\"fgzr\")\n      end\n\n      it \"doesn't read past the limit, single byte\" do\n        io = IO::Memory.new(\"abcderzzrfgzr\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"f\")\n\n        delimited.peek.should eq(\"abcderzzr\".to_slice)\n        delimited.gets_to_end.should eq(\"abcderzzr\")\n        io.gets_to_end.should eq(\"gzr\")\n      end\n\n      it \"doesn't read past the limit (char-by-char)\" do\n        io = IO::Memory.new(\"abcderzzrfg\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"zr\")\n\n        delimited.read_char.should eq('a')\n        delimited.read_char.should eq('b')\n        delimited.read_char.should eq('c')\n        delimited.read_char.should eq('d')\n        delimited.read_char.should eq('e')\n        delimited.read_char.should eq('r')\n        delimited.read_char.should eq('z')\n        delimited.read_char.should be_nil\n        delimited.read_char.should be_nil\n        delimited.read_char.should be_nil\n        delimited.read_char.should be_nil\n\n        io.read_char.should eq('f')\n        io.read_char.should eq('g')\n      end\n\n      it \"doesn't clobber active_delimiter_buffer\" do\n        io = IO::Memory.new(\"ab12312\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"12345\")\n\n        delimited.peek.should eq(\"ab123\".to_slice)\n        delimited.gets_to_end.should eq(\"ab12312\")\n      end\n\n      it \"handles the delimiter at the start\" do\n        io = IO::Memory.new(\"ab12312\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"ab1\")\n\n        delimited.peek.should eq(Bytes.empty)\n        delimited.read_char.should be_nil\n      end\n\n      it \"handles the delimiter at the end\" do\n        io = IO::Memory.new(\"ab12312z\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"z\")\n\n        delimited.peek.should eq(\"ab12312\".to_slice)\n        delimited.gets_to_end.should eq(\"ab12312\")\n      end\n\n      it \"handles nearly a delimiter at the end\" do\n        io = IO::Memory.new(\"ab12312\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"122\")\n\n        delimited.peek.should eq(\"ab123\".to_slice)\n        delimited.gets_to_end.should eq(\"ab12312\")\n      end\n\n      it \"doesn't clobber the buffer on closely-offset partial matches\" do\n        io = IO::Memory.new(\"abab1234abcdefgh\")\n        delimited = IO::Delimited.new(io, read_delimiter: \"abcdefgh\")\n\n        delimited.peek.should eq(\"abab1234\".to_slice)\n        delimited.gets_to_end.should eq(\"abab1234\")\n      end\n\n      it \"handles the case of peek matching first byte, not having enough room, but rest not matching\" do\n        #                                 not a delimiter\n        #                                    ---\n        io = MemoryIOWithFixedPeek.new(\"abcdefwhi\")\n        #                               -------\n        #                                peek\n        io.peek_size = 7\n        delimited = IO::Delimited.new(io, read_delimiter: \"fgh\")\n\n        delimited.peek.should eq(\"abcde\".to_slice)\n        delimited.gets_to_end.should eq(\"abcdefwhi\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"\")\n      end\n\n      it \"handles the case of peek matching first byte, not having enough room, but rest not immediately matching (with a potential match afterwards)\" do\n        #                                 not a delimiter, but the second 'f' will actually match the delimiter\n        #                                    ---\n        io = MemoryIOWithFixedPeek.new(\"abcdeffghijk\")\n        #                               -------\n        #                                peek\n        io.peek_size = 7\n        delimited = IO::Delimited.new(io, read_delimiter: \"fgh\")\n\n        delimited.peek.should eq(\"abcde\".to_slice)\n        delimited.gets_to_end.should eq(\"abcdef\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"ijk\")\n      end\n\n      it \"handles the case of peek matching first byte, not having enough room, but later matching\" do\n        #                                  delimiter\n        #                                    ---\n        io = MemoryIOWithFixedPeek.new(\"abcdefghijk\")\n        #                               -------\n        #                                peek\n        io.peek_size = 7\n        delimited = IO::Delimited.new(io, read_delimiter: \"fgh\")\n\n        delimited.peek.should eq(\"abcde\".to_slice)\n        delimited.gets_to_end.should eq(\"abcde\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"ijk\")\n      end\n\n      it \"handles the case of peek matching first byte, not having enough room, but later not matching\" do\n        #                                 not a delimiter\n        #                                    ---\n        io = MemoryIOWithFixedPeek.new(\"abcdefgwijkfghhello\")\n        #                               -------    ---\n        #                                peek    delimiter\n        io.peek_size = 7\n        delimited = IO::Delimited.new(io, read_delimiter: \"fgh\")\n\n        delimited.peek.should eq(\"abcde\".to_slice)\n        delimited.gets_to_end.should eq(\"abcdefgwijk\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"hello\")\n      end\n\n      it \"handles the case of peek matching first byte, not having enough room, but later not matching (limited slice)\" do\n        #                                 not a delimiter\n        #                                    ---\n        io = MemoryIOWithFixedPeek.new(\"abcdefgwijkfghhello\")\n        #                               -------    ---\n        #                                peek    delimiter\n        io.peek_size = 7\n        delimited = IO::Delimited.new(io, read_delimiter: \"fgh\")\n\n        delimited.peek.should eq(\"abcde\".to_slice)\n        delimited.read_string(6).should eq \"abcdef\"\n        delimited.read_string(5).should eq(\"gwijk\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"hello\")\n      end\n\n      it \"handles the case of peek matching first byte, not having enough room, later only partially matching\" do\n        #                                  delimiter\n        #                                    ------------\n        io = MemoryIOWithFixedPeek.new(\"abcdefghijklmnopqrst\")\n        #                               -------~~~~~~~\n        #                                peek   peek\n        io.peek_size = 7\n        delimited = IO::Delimited.new(io, read_delimiter: \"fghijklmnopq\")\n\n        delimited.peek.should eq(\"abcde\".to_slice)\n        delimited.gets_to_end.should eq(\"abcde\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"rst\")\n      end\n\n      it \"peeks, everything matches but we can't know what will happen after that\" do\n        io = MemoryIOWithFixedPeek.new(\"fgh\")\n        io.peek_size = 2\n        delimited = IO::Delimited.new(io, read_delimiter: \"fgh\")\n\n        delimited.peek.should be_nil\n      end\n\n      it \"handles the case of the active delimited buffer including the delimiter\" do\n        #                              delimiter\n        #                                ---\n        io = MemoryIOWithFixedPeek.new(\"aaabcde\")\n        #                               --\n        #                              peek\n        io.peek_size = 2\n        delimited = IO::Delimited.new(io, read_delimiter: \"aab\")\n\n        delimited.peek.should be_nil\n        delimited.gets_to_end.should eq(\"a\")\n        delimited.gets_to_end.should eq(\"\")\n        io.gets_to_end.should eq(\"cde\")\n      end\n\n      it \"can peek if first byte found but doesn't fully match, and there's that first byte again in the peek buffer\" do\n        #                                 delimiter\n        #                                   -----\n        io = MemoryIOWithFixedPeek.new(\"holhhello\")\n        #                               ----\n        #                               peek\n        io.peek_size = 4\n        delimited = IO::Delimited.new(io, read_delimiter: \"hello\")\n\n        delimited.peek.should eq(\"hol\".to_slice)\n      end\n\n      it \"can peek if first byte found but doesn't fully match, and the byte isn't there in the peek buffer\" do\n        #                                 delimiter\n        #                                   -----\n        io = MemoryIOWithFixedPeek.new(\"holahello\")\n        #                               ----\n        #                               peek\n        io.peek_size = 4\n        delimited = IO::Delimited.new(io, read_delimiter: \"hello\")\n\n        delimited.peek.should eq(\"hola\".to_slice)\n      end\n    end\n  end\n\n  describe \"#gets with peeking when can't peek\" do\n    it \"gets\" do\n      io = MemoryIOWithFixedPeek.new(\"abcdefghel\")\n      io.peek_size = 7\n\n      delimited = IO::Delimited.new(io, read_delimiter: \"hello\")\n\n      # We first peek \"abcdefg\".\n      # The delimiter's first byte isn't there so we read everything.\n      # Next we peek \"hel\". It matches the delimiter's beginning\n      # but `peek` can't tell whether there's more content or not\n      # after that, and it can't return an empty buffer because that\n      # means EOF. So it must return `nil`. So `IO#gets` should\n      # handle this case where `peek` becomes `nil`.\n      delimited.gets.should eq(\"abcdefghel\")\n    end\n\n    it \"peeks\" do\n      io = MemoryIOWithFixedPeek.new(\"hel\")\n      io.peek_size = 1\n\n      delimited = IO::Delimited.new(io, read_delimiter: \"hello\")\n      delimited.read(Bytes.new(1))\n\n      delimited.peek.should eq(\"el\".to_slice)\n    end\n  end\n\n  describe \"#write\" do\n    it \"raises\" do\n      delimited = IO::Delimited.new(IO::Memory.new, read_delimiter: \"zr\")\n      expect_raises(IO::Error, \"Can't write to IO::Delimited\") do\n        delimited.puts \"test string\"\n      end\n    end\n  end\n\n  describe \"#close\" do\n    it \"stops reading\" do\n      io = IO::Memory.new \"abcdefg\"\n      delimited = IO::Delimited.new(io, read_delimiter: \"zr\")\n\n      delimited.read_char.should eq('a')\n      delimited.read_char.should eq('b')\n\n      delimited.close\n      delimited.closed?.should be_true\n      expect_raises(IO::Error, \"Closed stream\") do\n        delimited.read_char\n      end\n    end\n\n    it \"closes the underlying stream if sync_close is true\" do\n      io = IO::Memory.new \"abcdefg\"\n      delimited = IO::Delimited.new(io, read_delimiter: \"zr\", sync_close: true)\n      delimited.sync_close?.should be_true\n\n      io.closed?.should be_false\n      delimited.close\n      io.closed?.should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/file_descriptor_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/finalize\"\n\nclass IO::FileDescriptor\n  include FinalizeCounter\nend\n\nprivate CLOSE_ON_EXEC_AVAILABLE = {{ !flag?(:win32) }}\n\nprivate def shell_command(command)\n  {% if flag?(:win32) %}\n    \"cmd.exe /c #{Process.quote(command)}\"\n  {% else %}\n    \"/bin/sh -c #{Process.quote(command)}\"\n  {% end %}\nend\n\ndescribe IO::FileDescriptor do\n  describe \"#initialize\" do\n    it \"handles closed file descriptor gracefully\" do\n      a, b = IO.pipe\n      a.close\n      b.close\n\n      fd = IO::FileDescriptor.new(a.fd)\n      fd.closed?.should be_true\n    end\n  end\n\n  it \"reopen STDIN with the right mode\", tags: %w[slow] do\n    code = %q(puts \"#{STDIN.blocking} #{STDIN.info.type}\")\n    compile_source(code) do |binpath|\n      `#{shell_command %(#{Process.quote(binpath)} < #{Process.quote(binpath)})}`.chomp.should eq(\"true File\")\n      `#{shell_command %(echo \"\" | #{Process.quote(binpath)})}`.chomp.should eq(\"#{{{ flag?(:win32) }}} Pipe\")\n    end\n  end\n\n  describe \"#tty?\" do\n    it \"returns false for null device\" do\n      File.open(File::NULL) do |f|\n        f.tty?.should be_false\n      end\n    end\n\n    it \"returns false for standard streams redirected to null device\", tags: %w[slow] do\n      code = %q(print STDIN.tty?, ' ', STDERR.tty?)\n      compile_source(code) do |binpath|\n        `#{shell_command %(#{Process.quote(binpath)} < #{File::NULL} 2> #{File::NULL})}`.should eq(\"false false\")\n      end\n    end\n  end\n\n  describe \"#finalize\" do\n    it \"closes\" do\n      pipes = [] of IO::FileDescriptor\n      assert_finalizes(\"fd\") do\n        a, b = IO.pipe\n        pipes << b\n        a\n      end\n\n      expect_raises(IO::Error) do\n        pipes.each do |p|\n          p.puts \"123\"\n        end\n      end\n    end\n\n    it \"does not flush\" do\n      with_tempfile \"fd-finalize-flush\" do |path|\n        file = File.new(path, \"w\")\n        file << \"foo\"\n        file.flush\n        file << \"bar\"\n        file.finalize\n\n        File.read(path).should eq \"foo\"\n      ensure\n        file.try(&.close) rescue nil\n      end\n    end\n  end\n\n  it \"opens STDIN in binary mode\", tags: %w[slow] do\n    code = %q(print STDIN.gets_to_end.includes?('\\r'))\n    compile_source(code) do |binpath|\n      io_in = IO::Memory.new(\"foo\\r\\n\")\n      io_out = IO::Memory.new\n      Process.run(binpath, input: io_in, output: io_out)\n      io_out.to_s.should eq(\"true\")\n    end\n  end\n\n  it \"opens STDOUT in binary mode\", tags: %w[slow] do\n    code = %q(puts \"foo\")\n    compile_source(code) do |binpath|\n      io = IO::Memory.new\n      Process.run(binpath, output: io)\n      io.to_s.should eq(\"foo\\n\")\n    end\n  end\n\n  it \"opens STDERR in binary mode\", tags: %w[slow] do\n    code = %q(STDERR.puts \"foo\")\n    compile_source(code) do |binpath|\n      io = IO::Memory.new\n      Process.run(binpath, error: io)\n      io.to_s.should eq(\"foo\\n\")\n    end\n  end\n\n  it \"does not close if close_on_finalize is false\" do\n    pipes = [] of IO::FileDescriptor\n    assert_finalizes(\"fd\") do\n      a, b = IO.pipe\n      a.close_on_finalize = false\n      pipes << b\n      a\n    end\n\n    pipes.each do |p|\n      p.puts \"123\"\n    end\n  end\n\n  it \"reopens\" do\n    File.open(datapath(\"test_file.txt\")) do |file1|\n      File.open(datapath(\"test_file.ini\")) do |file2|\n        file2.reopen(file1)\n        file2.gets.should eq(\"Hello World\")\n      end\n    end\n  end\n\n  describe \"close_on_exec\" do\n    it \"sets close on exec on the reopened standard descriptors\" do\n      unless STDIN.fd == Crystal::System::FileDescriptor::STDIN_HANDLE\n        STDIN.close_on_exec?.should be_true\n      end\n\n      unless STDOUT.fd == Crystal::System::FileDescriptor::STDOUT_HANDLE\n        STDOUT.close_on_exec?.should be_true\n      end\n\n      unless STDERR.fd == Crystal::System::FileDescriptor::STDERR_HANDLE\n        STDERR.close_on_exec?.should be_true\n      end\n    end\n\n    it \"is enabled by default (open)\" do\n      File.open(datapath(\"test_file.txt\")) do |file|\n        file.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n      end\n    end\n\n    it \"is enabled by default (pipe)\" do\n      IO::FileDescriptor.pipe.each do |fd|\n        fd.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n        fd.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n      end\n    end\n\n    it \"can be disabled and reenabled\" do\n      File.open(datapath(\"test_file.txt\")) do |file|\n        file.close_on_exec = false\n        file.close_on_exec?.should be_false\n\n        if CLOSE_ON_EXEC_AVAILABLE\n          file.close_on_exec = true\n          file.close_on_exec?.should be_true\n        else\n          expect_raises(NotImplementedError) do\n            file.close_on_exec = true\n          end\n        end\n      end\n    end\n\n    if CLOSE_ON_EXEC_AVAILABLE\n      it \"is copied on reopen\" do\n        File.open(datapath(\"test_file.txt\")) do |file1|\n          file1.close_on_exec = true\n\n          File.open(datapath(\"test_file.ini\")) do |file2|\n            file2.reopen(file1)\n            file2.close_on_exec?.should be_true\n          end\n\n          file1.close_on_exec = false\n\n          File.open(datapath(\"test_file.ini\")) do |file3|\n            file3.reopen(file1)\n            file3.close_on_exec?.should be_false\n          end\n        end\n      end\n    end\n  end\n\n  it \".set_blocking and .get_blocking\" do\n    File.open(datapath(\"test_file.txt\"), \"r\") do |file|\n      fd = file.fd\n\n      {% if flag?(:win32) %}\n        expect_raises(NotImplementedError) { IO::FileDescriptor.set_blocking(fd, false) }\n        expect_raises(NotImplementedError) { IO::FileDescriptor.set_blocking(fd, true) }\n        expect_raises(NotImplementedError) { IO::FileDescriptor.get_blocking(fd) }\n      {% else %}\n        IO::FileDescriptor.set_blocking(fd, false)\n        IO::FileDescriptor.get_blocking(fd).should be_false\n\n        IO::FileDescriptor.set_blocking(fd, true)\n        IO::FileDescriptor.get_blocking(fd).should be_true\n      {% end %}\n    end\n  end\n\n  typeof(STDIN.noecho { })\n  typeof(STDIN.noecho!)\n  typeof(STDIN.echo { })\n  typeof(STDIN.echo!)\n  typeof(STDIN.cooked { })\n  typeof(STDIN.cooked!)\n  typeof(STDIN.raw { })\n  typeof(STDIN.raw!)\nend\n"
  },
  {
    "path": "spec/std/io/hexdump_spec.cr",
    "content": "require \"spec\"\nrequire \"io/hexdump\"\n\ndescribe IO::Hexdump do\n  describe \"read\" do\n    it \"prints hexdump\" do\n      ascii_table = <<-EOF\n        00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f   !\"#$%&'()*+,-./\n        00000010  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?\n        00000020  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO\n        00000030  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[\\\\]^_\n        00000040  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno\n        00000050  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.\n        00000060  80 81 82 83 84                                    .....\n        EOF\n\n      IO.pipe do |r, w|\n        io = IO::Memory.new(ascii_table.bytesize)\n        r = IO::Hexdump.new(r, output: io, read: true)\n\n        slice = Bytes.new(101) { |i| i.to_u8 + 32 }\n        w.write(slice)\n\n        buf = uninitialized UInt8[101]\n        r.read_fully(buf.to_slice).should eq(101)\n        buf.to_slice.should eq(slice)\n\n        io.to_s.should eq(\"#{ascii_table}\\n\")\n      end\n    end\n  end\n\n  describe \"write\" do\n    it \"prints hexdump\" do\n      ascii_table = <<-EOF\n        00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f   !\"#$%&'()*+,-./\n        00000010  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?\n        00000020  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO\n        00000030  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[\\\\]^_\n        00000040  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno\n        00000050  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.\n        EOF\n\n      IO.pipe do |r, w|\n        io = IO::Memory.new(ascii_table.bytesize)\n        w = IO::Hexdump.new(w, output: io, write: true)\n\n        slice = Bytes.new(96) { |i| i.to_u8 + 32 }\n        w.write(slice)\n\n        buf = uninitialized UInt8[96]\n        r.read_fully(buf.to_slice).should eq(96)\n        buf.to_slice.should eq(slice)\n\n        io.to_s.should eq(\"#{ascii_table}\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/io_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/channel\"\nrequire \"../../support/string\"\nrequire \"spec/helpers/iterate\"\n\nrequire \"socket\"\nrequire \"big\"\nrequire \"base64\"\n\n# This is a non-optimized version of IO::Memory so we can test\n# raw IO. Optimizations for specific IOs are tested separately\n# (for example in buffered_io_spec)\nprivate class SimpleIOMemory < IO\n  getter buffer : UInt8*\n  getter bytesize : Int32\n  @capacity : Int32\n  @pos : Int32\n  @max_read : Int32?\n\n  def initialize(capacity = 64, @max_read = nil)\n    @buffer = GC.malloc_atomic(capacity.to_u32).as(UInt8*)\n    @bytesize = 0\n    @capacity = capacity\n    @pos = 0\n  end\n\n  def self.new(string : String, max_read = nil)\n    io = new(string.bytesize, max_read: max_read)\n    io << string\n    io\n  end\n\n  def self.new(bytes : Bytes, max_read = nil)\n    io = new(bytes.size, max_read: max_read)\n    io.write(bytes)\n    io\n  end\n\n  def read(slice : Bytes)\n    count = slice.size\n    count = Math.min(count, @bytesize - @pos)\n    if max_read = @max_read\n      count = Math.min(count, max_read)\n    end\n    slice.copy_from(@buffer + @pos, count)\n    @pos += count\n    count\n  end\n\n  def write(slice : Bytes) : Nil\n    count = slice.size\n    new_bytesize = bytesize + count\n    if new_bytesize > @capacity\n      resize_to_capacity(Math.pw2ceil(new_bytesize))\n    end\n\n    slice.copy_to(@buffer + @bytesize, count)\n    @bytesize += count\n  end\n\n  def to_slice\n    Slice.new(@buffer, @bytesize)\n  end\n\n  def to_s\n    String.new @buffer, @bytesize\n  end\n\n  def rewind\n    @pos = 0\n    self\n  end\n\n  private def resize_to_capacity(capacity)\n    @capacity = capacity\n    @buffer = GC.realloc(@buffer, @capacity)\n  end\nend\n\nprivate class OneByOneIO < IO\n  @bytes : Bytes\n\n  def initialize(string)\n    @bytes = string.to_slice\n    @pos = 0\n  end\n\n  def read(slice : Bytes)\n    return 0 if slice.empty?\n    return 0 if @pos >= @bytes.size\n\n    slice[0] = @bytes[@pos]\n    @pos += 1\n    1\n  end\n\n  def write(slice : Bytes) : Nil\n  end\nend\n\ndescribe IO do\n  describe \"partial read\" do\n    it \"doesn't block on first read.  blocks on 2nd read\" do\n      IO.pipe do |read, write|\n        write.puts \"hello\"\n        slice = Bytes.new 1024\n\n        read.read_timeout = 1.second\n        read.read(slice).should eq(6)\n\n        expect_raises(IO::TimeoutError) do\n          read.read_timeout = 0.1.microseconds\n          read.read(slice)\n        end\n      end\n    end\n  end\n\n  it_iterates \"#each_line\", [\"hello\", \"bye\"], SimpleIOMemory.new(\"hello\\nbye\\n\").each_line\n  it_iterates \"#each_line(chomp: false)\", [\"hello\\n\", \"bye\\n\"], SimpleIOMemory.new(\"hello\\nbye\\n\").each_line(chomp: false)\n\n  it_iterates \"#char\", ['a', 'b', 'あ', 'ぼ'], SimpleIOMemory.new(\"abあぼ\").each_char\n  it_iterates \"#char\", ['a'.ord.to_u8, 'b'.ord.to_u8], SimpleIOMemory.new(\"ab\").each_byte\n\n  it \"copies\" do\n    string = \"abあぼ\"\n    src = SimpleIOMemory.new(string)\n    dst = SimpleIOMemory.new\n    IO.copy(src, dst).should eq(string.bytesize)\n    dst.to_s.should eq(string)\n  end\n\n  it \"copies with limit\" do\n    string = \"abcあぼ\"\n    src = SimpleIOMemory.new(string)\n    dst = SimpleIOMemory.new\n    IO.copy(src, dst, 3).should eq(3)\n    dst.to_s.should eq(\"abc\")\n  end\n\n  it \"raises on copy with negative limit\" do\n    string = \"abcあぼ\"\n    src = SimpleIOMemory.new(string)\n    dst = SimpleIOMemory.new\n    expect_raises(ArgumentError, \"Negative limit\") do\n      IO.copy(src, dst, -10)\n    end\n  end\n\n  describe \"read operations\" do\n    it \"does gets\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.gets.should eq(\"hello\")\n      io.gets.should eq(\"world\")\n      io.gets.should be_nil\n    end\n\n    it \"does gets with \\\\r\\\\n\" do\n      io = SimpleIOMemory.new(\"hello\\r\\nworld\\r\\nfoo\\rbar\\n\")\n      io.gets.should eq(\"hello\")\n      io.gets.should eq(\"world\")\n      io.gets.should eq(\"foo\\rbar\")\n      io.gets.should be_nil\n    end\n\n    it \"does gets with \\\\r\\\\n, chomp true goes past \\\\r\" do\n      io = SimpleIOMemory.new(\"hello\\rworld\\r\\nfoo\\rbar\\n\")\n      io.gets(chomp: true, limit: 8).should eq(\"hello\\rwo\")\n    end\n\n    it \"does gets with chomp false\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.gets(chomp: false).should eq(\"hello\\n\")\n      io.gets(chomp: false).should eq(\"world\\n\")\n      io.gets(chomp: false).should be_nil\n    end\n\n    it \"does gets with empty string (no peek)\" do\n      io = SimpleIOMemory.new(\"\")\n      io.gets(chomp: true).should be_nil\n    end\n\n    it \"does gets with empty string (with peek)\" do\n      io = IO::Memory.new(\"\")\n      io.gets(chomp: true).should be_nil\n    end\n\n    it \"does gets with \\\\n (no peek)\" do\n      io = SimpleIOMemory.new(\"\\n\")\n      io.gets(chomp: true).should eq(\"\")\n      io.gets(chomp: true).should be_nil\n    end\n\n    it \"does gets with \\\\n (with peek)\" do\n      io = IO::Memory.new(\"\\n\")\n      io.gets(chomp: true).should eq(\"\")\n      io.gets(chomp: true).should be_nil\n    end\n\n    it \"does gets with \\\\r\\\\n (no peek)\" do\n      io = SimpleIOMemory.new(\"\\r\\n\")\n      io.gets(chomp: true).should eq(\"\")\n      io.gets(chomp: true).should be_nil\n    end\n\n    it \"does gets with \\\\r\\\\n (with peek)\" do\n      io = IO::Memory.new(\"\\r\\n\")\n      io.gets(chomp: true).should eq(\"\")\n      io.gets(chomp: true).should be_nil\n    end\n\n    it \"does gets with big line\" do\n      big_line = \"a\" * 20_000\n      io = SimpleIOMemory.new(\"#{big_line}\\nworld\\n\")\n      io.gets.should eq(big_line)\n    end\n\n    it \"does gets with char delimiter\" do\n      io = SimpleIOMemory.new(\"hello world\")\n      io.gets('w').should eq(\"hello w\")\n      io.gets('r').should eq(\"or\")\n      io.gets('r').should eq(\"ld\")\n      io.gets('r').should be_nil\n    end\n\n    it \"does gets with unicode char delimiter\" do\n      io = SimpleIOMemory.new(\"こんにちは\")\n      io.gets('ち').should eq(\"こんにち\")\n      io.gets('ち').should eq(\"は\")\n      io.gets('ち').should be_nil\n    end\n\n    it \"gets with string as delimiter\" do\n      io = SimpleIOMemory.new(\"hello world\")\n      io.gets(\"lo\").should eq(\"hello\")\n      io.gets(\"rl\").should eq(\" worl\")\n      io.gets(\"foo\").should eq(\"d\")\n    end\n\n    it \"gets with string as delimiter and chomp = true\" do\n      io = SimpleIOMemory.new(\"hello world\")\n      io.gets(\"lo\", chomp: true).should eq(\"hel\")\n      io.gets(\"rl\", chomp: true).should eq(\" wo\")\n      io.gets(\"foo\", chomp: true).should eq(\"d\")\n    end\n\n    it \"gets with empty string as delimiter\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.gets(\"\").should eq(\"hello\\nworld\\n\")\n    end\n\n    it \"gets with single byte string as delimiter\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\nbye\")\n      io.gets('\\n').should eq(\"hello\\n\")\n      io.gets('\\n').should eq(\"world\\n\")\n      io.gets('\\n').should eq(\"bye\")\n    end\n\n    it \"does gets with limit\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.gets(3).should eq(\"hel\")\n      io.gets(10_000).should eq(\"lo\\n\")\n      io.gets(10_000).should eq(\"world\\n\")\n      io.gets(3).should be_nil\n    end\n\n    it \"does gets with char and limit\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.gets('o', 2).should eq(\"he\")\n      io.gets('w', 10_000).should eq(\"llo\\nw\")\n      io.gets('z', 10_000).should eq(\"orld\\n\")\n      io.gets('a', 3).should be_nil\n    end\n\n    it \"doesn't underflow when limit is unsigned\" do\n      io = IO::Memory.new(\"aїa\")\n      io.gets('є', 2u32).should eq(\"aї\")\n    end\n\n    it \"raises if invoking gets with negative limit\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      expect_raises ArgumentError, \"Negative limit\" do\n        io.gets(-1)\n      end\n    end\n\n    it \"does read_line with limit\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.read_line(3).should eq(\"hel\")\n      io.read_line(10_000).should eq(\"lo\\n\")\n      io.read_line(10_000).should eq(\"world\\n\")\n      expect_raises(IO::EOFError) { io.read_line(3) }\n    end\n\n    it \"does read_line with char and limit\" do\n      io = SimpleIOMemory.new(\"hello\\nworld\\n\")\n      io.read_line('o', 2).should eq(\"he\")\n      io.read_line('w', 10_000).should eq(\"llo\\nw\")\n      io.read_line('z', 10_000).should eq(\"orld\\n\")\n      expect_raises(IO::EOFError) { io.read_line('a', 3) }\n    end\n\n    it \"reads all remaining content\" do\n      io = SimpleIOMemory.new(\"foo\\nbar\\nbaz\\n\")\n      io.gets.should eq(\"foo\")\n      io.gets_to_end.should eq(\"bar\\nbaz\\n\")\n      io.gets_to_end.should eq(\"\")\n    end\n\n    it \"reads all remaining content as bytes\" do\n      io = SimpleIOMemory.new(Bytes[0, 1, 3, 6, 10, 15])\n      io.getb_to_end.should eq(Bytes[0, 1, 3, 6, 10, 15])\n      io.getb_to_end.should eq(Bytes[])\n      io.rewind\n      bytes = io.getb_to_end\n      bytes.should eq(Bytes[0, 1, 3, 6, 10, 15])\n      bytes.read_only?.should be_false\n\n      io.rewind\n      io.write(Bytes[2, 4, 5])\n      bytes.should eq(Bytes[0, 1, 3, 6, 10, 15])\n    end\n\n    it \"reads char\" do\n      io = SimpleIOMemory.new(\"hi 世界\")\n      io.read_char.should eq('h')\n      io.read_char.should eq('i')\n      io.read_char.should eq(' ')\n      io.read_char.should eq('世')\n      io.read_char.should eq('界')\n      io.read_char.should be_nil\n\n      {% for bytes, char in VALID_UTF8_BYTE_SEQUENCES %}\n        SimpleIOMemory.new(Bytes{{ bytes }}).read_char.should eq({{ char }})\n      {% end %}\n\n      {% for bytes in INVALID_UTF8_BYTE_SEQUENCES %}\n        expect_raises(InvalidByteSequenceError) { SimpleIOMemory.new(Bytes{{ bytes }}).read_char }\n      {% end %}\n    end\n\n    it \"reads byte\" do\n      io = SimpleIOMemory.new(\"hello\")\n      io.read_byte.should eq('h'.ord)\n      io.read_byte.should eq('e'.ord)\n      io.read_byte.should eq('l'.ord)\n      io.read_byte.should eq('l'.ord)\n      io.read_byte.should eq('o'.ord)\n      io.read_char.should be_nil\n    end\n\n    it \"reads string\" do\n      io = SimpleIOMemory.new(\"hello world\")\n      io.read_string(5).should eq(\"hello\")\n      io.read_string(1).should eq(\" \")\n      io.read_string(0).should eq(\"\")\n      expect_raises(IO::EOFError) do\n        io.read_string(6)\n      end\n    end\n\n    it \"does each_line\" do\n      lines = [] of String\n      io = SimpleIOMemory.new(\"a\\nbb\\ncc\")\n      io.each_line do |line|\n        lines << line\n      end\n      lines.should eq [\"a\", \"bb\", \"cc\"]\n    end\n\n    it \"does each_char\" do\n      chars = [] of Char\n      io = SimpleIOMemory.new(\"あいう\")\n      io.each_char do |c|\n        chars << c\n      end\n      chars.should eq ['あ', 'い', 'う']\n    end\n\n    it \"does each_byte\" do\n      bytes = [] of UInt8\n      io = SimpleIOMemory.new(\"abc\")\n      io.each_byte do |b|\n        bytes << b\n      end\n      bytes.should eq ['a'.ord.to_u8, 'b'.ord.to_u8, 'c'.ord.to_u8]\n    end\n\n    it \"raises on EOF with read_line\" do\n      str = SimpleIOMemory.new(\"hello\")\n      str.read_line.should eq(\"hello\")\n\n      expect_raises IO::EOFError, \"End of file reached\" do\n        str.read_line\n      end\n    end\n\n    it \"raises on EOF with readline and delimiter\" do\n      str = SimpleIOMemory.new(\"hello\")\n      str.read_line('e').should eq(\"he\")\n      str.read_line('e').should eq(\"llo\")\n\n      expect_raises IO::EOFError, \"End of file reached\" do\n        str.read_line\n      end\n    end\n\n    it \"does read_fully\" do\n      str = SimpleIOMemory.new(\"hello\")\n      slice = Bytes.new(4)\n      str.read_fully(slice).should eq(4)\n      String.new(slice).should eq(\"hell\")\n\n      expect_raises(IO::EOFError) do\n        str.read_fully(slice)\n      end\n    end\n\n    it \"does read_fully?\" do\n      str = SimpleIOMemory.new(\"hello\")\n      slice = Bytes.new(4)\n      str.read_fully?(slice).should eq(4)\n      String.new(slice).should eq(\"hell\")\n\n      str.read_fully?(slice).should be_nil\n    end\n\n    # pipe(2) returns bidirectional file descriptors on some platforms,\n    # gate this test behind the platform flag.\n    {% unless flag?(:freebsd) || flag?(:solaris) || flag?(:openbsd) || flag?(:dragonfly) %}\n      it \"raises if trying to read to an IO not opened for reading\" do\n        IO.pipe do |r, w|\n          expect_raises(IO::Error, \"File not open for reading\") do\n            w.gets\n          end\n        end\n      end\n    {% end %}\n\n    describe \".same_content?\" do\n      it \"compares two ios, one way (true)\" do\n        io1 = OneByOneIO.new(\"hello\")\n        io2 = IO::Memory.new(\"hello\")\n        IO.same_content?(io1, io2).should be_true\n      end\n\n      it \"compares two ios, second way (true)\" do\n        io1 = OneByOneIO.new(\"hello\")\n        io2 = IO::Memory.new(\"hello\")\n        IO.same_content?(io2, io1).should be_true\n      end\n\n      it \"compares two ios, one way (false)\" do\n        io1 = OneByOneIO.new(\"hello\")\n        io2 = IO::Memory.new(\"hella\")\n        IO.same_content?(io1, io2).should be_false\n      end\n\n      it \"compares two ios, second way (false)\" do\n        io1 = OneByOneIO.new(\"hello\")\n        io2 = IO::Memory.new(\"hella\")\n        IO.same_content?(io2, io1).should be_false\n      end\n\n      it \"refutes prefix match, one way\" do\n        io1 = OneByOneIO.new(\"hello\")\n        io2 = IO::Memory.new(\"hello again\")\n        IO.same_content?(io1, io2).should be_false\n      end\n\n      it \"refutes prefix match, second way\" do\n        io1 = IO::Memory.new(\"hello\")\n        io2 = OneByOneIO.new(\"hello again\")\n        IO.same_content?(io1, io2).should be_false\n      end\n\n      it \"refutes prefix match, one way\" do\n        io1 = OneByOneIO.new(\"hello again\")\n        io2 = IO::Memory.new(\"hello\")\n        IO.same_content?(io1, io2).should be_false\n      end\n\n      it \"refutes prefix match, second way\" do\n        io1 = IO::Memory.new(\"hello again\")\n        io2 = OneByOneIO.new(\"hello\")\n        IO.same_content?(io1, io2).should be_false\n      end\n    end\n\n    it \"combines multiple reads using #read_greedy\" do\n      bytes = Bytes.new 7\n\n      io = SimpleIOMemory.new(\"Hello World\", max_read: 2)\n      io.read_greedy(bytes).should eq(7)\n      bytes.should eq(\"Hello W\".to_slice)\n      io.read_greedy(bytes).should eq(4)\n      bytes[0, 4].should eq(\"orld\".to_slice)\n      io.read_greedy(bytes).should eq(0)\n    end\n  end\n\n  describe \"write operations\" do\n    it \"does puts\" do\n      io = SimpleIOMemory.new\n      io.puts \"Hello\"\n      io.gets_to_end.should eq(\"Hello\\n\")\n    end\n\n    it \"does puts with big string\" do\n      io = SimpleIOMemory.new\n      s = \"*\" * 20_000\n      io << \"hello\"\n      io << s\n      io.gets_to_end.should eq(\"hello#{s}\")\n    end\n\n    it \"does puts many times\" do\n      io = SimpleIOMemory.new\n      10_000.times { io << \"hello\" }\n      io.gets_to_end.should eq(\"hello\" * 10_000)\n    end\n\n    it \"puts several arguments\" do\n      io = SimpleIOMemory.new\n      io.puts(1, \"aaa\", \"\\n\")\n      io.gets_to_end.should eq(\"1\\naaa\\n\\n\")\n    end\n\n    it \"prints\" do\n      io = SimpleIOMemory.new\n      io.print \"foo\"\n      io.gets_to_end.should eq(\"foo\")\n    end\n\n    it \"prints several arguments\" do\n      io = SimpleIOMemory.new\n      io.print \"foo\", \"bar\", \"baz\"\n      io.gets_to_end.should eq(\"foobarbaz\")\n    end\n\n    it \"writes bytes\" do\n      io = SimpleIOMemory.new\n      10_000.times { io.write_byte 'a'.ord.to_u8 }\n      io.gets_to_end.should eq(\"a\" * 10_000)\n    end\n\n    it \"writes with printf\" do\n      io = SimpleIOMemory.new\n      io.printf \"Hello %d\", 123\n      io.gets_to_end.should eq(\"Hello 123\")\n    end\n\n    it \"writes with printf as an array\" do\n      io = SimpleIOMemory.new\n      io.printf \"Hello %d\", [123]\n      io.gets_to_end.should eq(\"Hello 123\")\n    end\n\n    it \"skips a few bytes\" do\n      io = SimpleIOMemory.new\n      io << \"hello world\"\n      io.skip(6)\n      io.gets_to_end.should eq(\"world\")\n    end\n\n    it \"skips but raises if not enough bytes\" do\n      io = SimpleIOMemory.new\n      io << \"hello\"\n      expect_raises(IO::EOFError) do\n        io.skip(6)\n      end\n    end\n\n    it \"skips more than 4096 bytes\" do\n      io = SimpleIOMemory.new\n      io << \"a\" * 4100\n      io.skip(4099)\n      io.gets_to_end.should eq(\"a\")\n    end\n\n    it \"skips to end\" do\n      io = SimpleIOMemory.new\n      io << \"hello\"\n      io.skip_to_end\n      io.read_byte.should be_nil\n    end\n\n    # pipe(2) returns bidirectional file descriptors on some platforms,\n    # gate this test behind the platform flag.\n    {% unless flag?(:freebsd) || flag?(:solaris) || flag?(:openbsd) || flag?(:dragonfly) %}\n      it \"raises if trying to write to an IO not opened for writing\" do\n        IO.pipe do |r, w|\n          # unless sync is used the flush on close triggers the exception again\n          r.sync = true\n\n          expect_raises(IO::Error, \"File not open for writing\") do\n            r << \"hello\"\n          end\n        end\n      end\n    {% end %}\n  end\n\n  {% unless flag?(:without_iconv) %}\n    describe \"encoding\" do\n      describe \"decode\" do\n        it \"gets_to_end\" do\n          str = \"Hello world\" * 200\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets_to_end.should eq(str)\n        end\n\n        it \"gets\" do\n          str = \"Hello world\\r\\nFoo\\nBar\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets.should eq(\"Hello world\")\n          io.gets.should eq(\"Foo\")\n          io.gets.should eq(\"Bar\")\n          io.gets.should be_nil\n        end\n\n        it \"gets with chomp = false\" do\n          str = \"Hello world\\r\\nFoo\\nBar\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(chomp: false).should eq(\"Hello world\\r\\n\")\n          io.gets(chomp: false).should eq(\"Foo\\n\")\n          io.gets(chomp: false).should eq(\"Bar\")\n          io.gets(chomp: false).should be_nil\n        end\n\n        it \"gets big string\" do\n          str = \"Hello\\nWorld\\n\" * 10_000\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          10_000.times do |i|\n            io.gets.should eq(\"Hello\")\n            io.gets.should eq(\"World\")\n          end\n        end\n\n        it \"gets big EUC-JP string\" do\n          2.times do\n            str = (\"好我是人\\n\" * 1000).encode(\"EUC-JP\")\n            io = SimpleIOMemory.new(str)\n            io.set_encoding(\"EUC-JP\")\n            1000.times do\n              io.gets.should eq(\"好我是人\")\n            end\n          end\n        end\n\n        it \"does gets on unicode with char and limit without off-by-one\" do\n          io = SimpleIOMemory.new(\"test\\nabc\".encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets('a', 5).should eq(\"test\\n\")\n          io = SimpleIOMemory.new(\"test\\nabc\".encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets('a', 6).should eq(\"test\\na\")\n        end\n\n        it \"gets with limit\" do\n          str = \"Hello\\nWorld\\n\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(3).should eq(\"Hel\")\n        end\n\n        it \"gets with limit (small, no newline)\" do\n          str = \"Hello world\" * 10_000\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(3).should eq(\"Hel\")\n        end\n\n        it \"gets with non-ascii\" do\n          str = \"你好我是人\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets('人').should eq(\"你好我是人\")\n        end\n\n        it \"gets with non-ascii and chomp: false\" do\n          str = \"你好我是人\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets('人', chomp: true).should eq(\"你好我是\")\n        end\n\n        it \"gets with limit (big)\" do\n          str = \"Hello world\" * 10_000\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(20_000).should eq(str[0, 20_000])\n        end\n\n        it \"gets with string delimiter\" do\n          str = \"Hello world\\nFoo\\nBar\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(\"wo\").should eq(\"Hello wo\")\n          io.gets(\"oo\").should eq(\"rld\\nFoo\")\n          io.gets(\"xx\").should eq(\"\\nBar\")\n          io.gets(\"zz\").should be_nil\n        end\n\n        it \"reads char\" do\n          str = \"Hello world\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          str.each_char do |char|\n            io.read_char.should eq(char)\n          end\n          io.read_char.should be_nil\n        end\n\n        it \"reads utf8 byte\" do\n          str = \"Hello world\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          str.each_byte do |byte|\n            io.read_utf8_byte.should eq(byte)\n          end\n          io.read_utf8_byte.should be_nil\n        end\n\n        it \"reads utf8\" do\n          io = IO::Memory.new(\"好\".encode(\"EUC-JP\"))\n          io.set_encoding(\"EUC-JP\")\n\n          buffer = uninitialized UInt8[1024]\n          bytes_read = io.read_utf8(buffer.to_slice) # => 3\n          bytes_read.should eq(3)\n          buffer.to_slice[0, bytes_read].to_a.should eq(\"好\".bytes)\n        end\n\n        it \"raises on incomplete byte sequence\" do\n          io = SimpleIOMemory.new(\"好\".byte_slice(0, 1))\n          io.set_encoding(\"EUC-JP\")\n          expect_raises ArgumentError, \"Incomplete multibyte sequence\" do\n            io.read_char\n          end\n        end\n\n        it \"says invalid byte sequence\" do\n          io = SimpleIOMemory.new(Slice.new(1, 255_u8))\n          io.set_encoding(\"EUC-JP\")\n          message =\n            {% if flag?(:musl) || flag?(:freebsd) || flag?(:netbsd) || flag?(:dragonfly) %}\n              \"Incomplete multibyte sequence\"\n            {% else %}\n              \"Invalid multibyte sequence\"\n            {% end %}\n          expect_raises(ArgumentError, message) { io.read_char }\n        end\n\n        it \"skips invalid byte sequences\" do\n          string = String.build do |str|\n            str.write \"好\".encode(\"EUC-JP\")\n            str.write_byte 255_u8\n            str.write \"是\".encode(\"EUC-JP\")\n          end\n          io = SimpleIOMemory.new(string)\n          io.set_encoding(\"EUC-JP\", invalid: :skip)\n          io.read_char.should eq('好')\n          io.read_char.should eq('是')\n          io.read_char.should be_nil\n        end\n\n        it \"says invalid 'invalid' option\" do\n          io = SimpleIOMemory.new\n          expect_raises ArgumentError, \"Valid values for `invalid` option are `nil` and `:skip`, not :foo\" do\n            io.set_encoding(\"EUC-JP\", invalid: :foo)\n          end\n        end\n\n        it \"says invalid encoding\" do\n          io = SimpleIOMemory.new(\"foo\")\n          io.set_encoding(\"FOO\")\n          expect_raises ArgumentError, \"Invalid encoding: FOO\" do\n            io.gets_to_end\n          end\n        end\n\n        it \"sets encoding to utf-8 and stays as UTF-8\" do\n          io = SimpleIOMemory.new(Base64.decode_string(\"ey8qx+Tl8fwg7+Dw4Ozl8vD7IOLo5+jy4CovfQ==\"))\n          io.set_encoding(\"utf-8\")\n          io.encoding.should eq(\"UTF-8\")\n        end\n\n        it \"sets encoding to utf8 and stays as UTF-8\" do\n          io = SimpleIOMemory.new(Base64.decode_string(\"ey8qx+Tl8fwg7+Dw4Ozl8vD7IOLo5+jy4CovfQ==\"))\n          io.set_encoding(\"utf8\")\n          io.encoding.should eq(\"UTF-8\")\n        end\n\n        it \"does skips when converting to UTF-8\" do\n          io = SimpleIOMemory.new(Base64.decode_string(\"ey8qx+Tl8fwg7+Dw4Ozl8vD7IOLo5+jy4CovfQ==\"))\n          io.set_encoding(\"UTF-8\", invalid: :skip)\n          io.gets_to_end.should eq \"{/*  */}\"\n        end\n\n        it \"decodes incomplete multibyte sequence with skip (#3285)\" do\n          bytes = Bytes[195, 229, 237, 229, 240, 224, 246, 232, 255, 32, 241, 234, 240, 232, 239, 242, 224, 32, 48, 46, 48, 49, 50, 54, 32, 241, 229, 234, 243, 237, 228, 10]\n          m = IO::Memory.new(bytes)\n          m.set_encoding(\"UTF-8\", invalid: :skip)\n          m.gets_to_end.should eq(\"  0.0126 \\n\")\n        end\n\n        it \"decodes incomplete multibyte sequence with skip (2) (#3285)\" do\n          str = File.read(datapath(\"io_data_incomplete_multibyte_sequence.txt\"))\n          m = IO::Memory.new(Base64.decode_string str)\n          m.set_encoding(\"UTF-8\", invalid: :skip)\n          m.gets_to_end.bytesize.should eq(4277)\n        end\n\n        it \"decodes incomplete multibyte sequence with skip (3) (#3285)\" do\n          str = File.read(datapath(\"io_data_incomplete_multibyte_sequence_2.txt\"))\n          m = IO::Memory.new(Base64.decode_string str)\n          m.set_encoding(\"UTF-8\", invalid: :skip)\n          m.gets_to_end.bytesize.should eq(8977)\n        end\n\n        it \"reads string\" do\n          str = \"Hello world\\r\\nFoo\\nBar\"\n          io = SimpleIOMemory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.read_string(11).should eq(\"Hello world\")\n          io.gets_to_end.should eq(\"\\r\\nFoo\\nBar\")\n        end\n\n        it \"gets ascii from socket (#9056)\" do\n          server = TCPServer.new \"localhost\", 0\n          sock = TCPSocket.new \"localhost\", server.local_address.port\n          begin\n            sock.set_encoding(\"ascii\")\n            spawn do\n              client = server.accept\n              message = client.gets\n              client << \"#{message}\\n\"\n            end\n            sock << \"K\\n\"\n            sock.gets.should eq(\"K\")\n          ensure\n            server.close\n            sock.close\n          end\n        end\n      end\n\n      describe \"encode\" do\n        it \"prints a string\" do\n          str = \"Hello world\"\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.print str\n          slice = io.to_slice\n          slice.should eq(str.encode(\"UCS-2LE\"))\n        end\n\n        it \"prints numbers\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.print 0\n          io.print 1_u8\n          io.print 2_u16\n          io.print 3_u32\n          io.print 4_u64\n          io.print 5_i8\n          io.print 6_i16\n          io.print 7_i32\n          io.print 8_i64\n          io.print 9.1_f32\n          io.print 10.11_f64\n          slice = io.to_slice\n          slice.should eq(\"0123456789.110.11\".encode(\"UCS-2LE\"))\n        end\n\n        it \"prints bool\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.print true\n          io.print false\n          slice = io.to_slice\n          slice.should eq(\"truefalse\".encode(\"UCS-2LE\"))\n        end\n\n        it \"prints char\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.print 'a'\n          slice = io.to_slice\n          slice.should eq(\"a\".encode(\"UCS-2LE\"))\n        end\n\n        it \"prints symbol\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.print :foo\n          slice = io.to_slice\n          slice.should eq(\"foo\".encode(\"UCS-2LE\"))\n        end\n\n        it \"prints big int\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.print 123_456.to_big_i\n          slice = io.to_slice\n          slice.should eq(\"123456\".encode(\"UCS-2LE\"))\n        end\n\n        it \"puts\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.puts 1\n          io.puts\n          slice = io.to_slice\n          slice.should eq(\"1\\n\\n\".encode(\"UCS-2LE\"))\n        end\n\n        it \"printf\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UCS-2LE\")\n          io.printf \"%s-%d-%.2f\", \"hi\", 123, 45.67\n          slice = io.to_slice\n          slice.should eq(\"hi-123-45.67\".encode(\"UCS-2LE\"))\n        end\n\n        it \"raises on invalid byte sequence\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"EUC-JP\")\n          expect_raises ArgumentError, \"Invalid multibyte sequence\" do\n            io.print \"\\xff\"\n          end\n        end\n\n        it \"skips on invalid byte sequence\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"EUC-JP\", invalid: :skip)\n          io.print \"ñ\"\n          io.print \"foo\"\n        end\n\n        it \"raises on incomplete byte sequence\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"EUC-JP\")\n          expect_raises ArgumentError, \"Incomplete multibyte sequence\" do\n            io.print \"好\".byte_slice(0, 1)\n          end\n        end\n\n        it \"says invalid encoding\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"FOO\")\n          expect_raises ArgumentError, \"Invalid encoding: FOO\" do\n            io.puts \"a\"\n          end\n        end\n      end\n\n      describe \"#encoding\" do\n        it \"returns \\\"UTF-8\\\" if the encoding is not manually set\" do\n          SimpleIOMemory.new.encoding.should eq(\"UTF-8\")\n        end\n\n        it \"returns the name of the encoding set via #set_encoding\" do\n          io = SimpleIOMemory.new\n          io.set_encoding(\"UTF-16LE\")\n          io.encoding.should eq(\"UTF-16LE\")\n        end\n      end\n    end\n  {% end %}\n\n  describe \"#close\" do\n    it \"aborts 'read' in a different fiber\" do\n      ch = Channel(SpecChannelStatus).new(1)\n\n      IO.pipe do |read, write|\n        f = spawn do\n          ch.send :begin\n          read.gets\n        rescue\n          ch.send :end\n        end\n\n        schedule_timeout ch\n\n        ch.receive.should eq SpecChannelStatus::Begin\n        wait_until_blocked f\n\n        read.close\n        ch.receive.should eq SpecChannelStatus::End\n      end\n    end\n\n    it \"aborts 'write' in a different fiber\" do\n      ch = Channel(SpecChannelStatus).new(1)\n\n      IO.pipe do |read, write|\n        f = spawn do\n          ch.send :begin\n          loop do\n            write.puts \"some line\"\n          end\n        rescue\n          ch.send :end\n        end\n\n        schedule_timeout ch\n\n        ch.receive.should eq SpecChannelStatus::Begin\n        wait_until_blocked f\n\n        write.close\n        ch.receive.should eq SpecChannelStatus::End\n      end\n    end\n  end\n\n  describe IO::Error do\n    describe \".new\" do\n      it \"accepts `cause` argument (#14241)\" do\n        cause = Exception.new(\"cause\")\n        error = IO::Error.new(\"foo\", cause: cause)\n        error.cause.should be cause\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/memory_spec.cr",
    "content": "require \"../spec_helper\"\n\ndescribe IO::Memory do\n  it \"writes\" do\n    io = IO::Memory.new\n    io.bytesize.should eq(0)\n    io.write Slice.new(\"hello\".to_unsafe, 3)\n    io.bytesize.should eq(3)\n    io.rewind\n    io.gets_to_end.should eq(\"hel\")\n  end\n\n  it \"writes big\" do\n    s = \"hi\" * 100\n    io = IO::Memory.new\n    io.write Slice.new(s.to_unsafe, s.bytesize)\n    io.rewind\n    io.gets_to_end.should eq(s)\n  end\n\n  it \"write raises EOFError\" do\n    io = IO::Memory.new\n    initial_capacity = io.@capacity\n    expect_raises(IO::EOFError) do\n      io.write Slice.new(Pointer(UInt8).null, Int32::MAX)\n    end\n    # nothing get's written\n    io.bytesize.should eq 0\n    io.@capacity.should eq initial_capacity\n  end\n\n  it \"reads byte\" do\n    io = IO::Memory.new(\"abc\")\n    io.read_byte.should eq('a'.ord)\n    io.read_byte.should eq('b'.ord)\n    io.read_byte.should eq('c'.ord)\n    io.read_byte.should be_nil\n  end\n\n  it \"raises if reading when closed\" do\n    io = IO::Memory.new(\"abc\")\n    io.close\n    expect_raises(IO::Error, \"Closed stream\") do\n      io.read(Slice.new(3, 0_u8))\n    end\n  end\n\n  it \"raises if clearing when closed\" do\n    io = IO::Memory.new(\"abc\")\n    io.close\n    expect_raises(IO::Error, \"Closed stream\") do\n      io.clear\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"appends to another buffer\" do\n      s1 = IO::Memory.new\n      s1 << \"hello\"\n\n      s2 = IO::Memory.new\n      s1.to_s(s2)\n      s2.to_s.should eq(\"hello\")\n    end\n\n    it \"appends to itself\" do\n      io = IO::Memory.new\n      io << \".\" * 33\n      old_capacity = io.@capacity\n      io.to_s(io)\n      io.to_s.should eq \".\" * 66\n      # Ensure that the buffer is resized, otherwise the spec doesn't work\n      io.@capacity.should_not eq old_capacity\n    end\n\n    {% if flag?(:without_iconv) %}\n      pending \"encoding\"\n    {% else %}\n      describe \"encoding\" do\n        it \"returns String\" do\n          io = IO::Memory.new\n          io.set_encoding \"UTF-16LE\"\n          io << \"abc\"\n          io.to_s.should eq \"abc\"\n          io.to_slice.should eq Bytes[0x61, 0, 0x62, 0, 0x63, 0]\n        end\n\n        it \"writes to IO\" do\n          io1 = IO::Memory.new\n          io1.set_encoding \"UTF-32LE\"\n\n          io2 = IO::Memory.new\n          io2.set_encoding \"UTF-16LE\"\n\n          io1.write_string \"abc😂\".to_slice\n          io1.to_s io2\n          byte_slice = io2.to_slice\n          utf16_slice = byte_slice.unsafe_slice_of(UInt16)\n\n          String.from_utf16(utf16_slice).should eq \"abc😂\"\n          byte_slice.should eq Bytes[0x61, 0, 0x62, 0, 0x63, 0, 0x3D, 0xD8, 0x02, 0xDE]\n          utf16_slice.should eq Slice[0x0061, 0x0062, 0x0063, 0xD83D, 0xDE02]\n        end\n      end\n    {% end %}\n  end\n\n  it \"reads single line content\" do\n    io = IO::Memory.new(\"foo\")\n    io.gets.should eq(\"foo\")\n  end\n\n  it \"reads each line\" do\n    io = IO::Memory.new(\"foo\\r\\nbar\\n\")\n    io.gets.should eq(\"foo\")\n    io.gets.should eq(\"bar\")\n    io.gets.should be_nil\n  end\n\n  it \"reads each line with chomp = false\" do\n    io = IO::Memory.new(\"foo\\r\\nbar\\r\\n\")\n    io.gets(chomp: false).should eq(\"foo\\r\\n\")\n    io.gets(chomp: false).should eq(\"bar\\r\\n\")\n    io.gets(chomp: false).should be_nil\n  end\n\n  it \"gets with char as delimiter\" do\n    io = IO::Memory.new(\"hello world\")\n    io.gets('w').should eq(\"hello w\")\n    io.gets('r').should eq(\"or\")\n    io.gets('r').should eq(\"ld\")\n    io.gets('r').should be_nil\n  end\n\n  it \"does gets with char and limit\" do\n    io = IO::Memory.new(\"hello\\nworld\\n\")\n    io.gets('o', 2).should eq(\"he\")\n    io.gets('w', 10_000).should eq(\"llo\\nw\")\n    io.gets('z', 10_000).should eq(\"orld\\n\")\n    io.gets('a', 3).should be_nil\n  end\n\n  it \"does gets with limit\" do\n    io = IO::Memory.new(\"hello\\nworld\")\n    io.gets(3).should eq(\"hel\")\n    io.gets(3).should eq(\"lo\\n\")\n    io.gets(3).should eq(\"wor\")\n    io.gets(3).should eq(\"ld\")\n    io.gets(3).should be_nil\n  end\n\n  it \"does gets with char and limit without off-by-one\" do\n    io = IO::Memory.new(\"test\\nabc\")\n    io.gets('a', 5).should eq(\"test\\n\")\n    io = IO::Memory.new(\"test\\nabc\")\n    io.gets('a', 6).should eq(\"test\\na\")\n  end\n\n  it \"raises if invoking gets with negative limit\" do\n    io = IO::Memory.new(\"hello\\nworld\\n\")\n    expect_raises ArgumentError, \"Negative limit\" do\n      io.gets(-1)\n    end\n  end\n\n  it \"write single byte\" do\n    io = IO::Memory.new\n    io.write_byte 97_u8\n    io.to_s.should eq(\"a\")\n  end\n\n  it \"writes and reads\" do\n    io = IO::Memory.new\n    io << \"foo\" << \"bar\"\n    io.rewind\n    io.gets.should eq(\"foobar\")\n  end\n\n  it \"can be converted to slice\" do\n    str = IO::Memory.new\n    str.write_byte 0_u8\n    str.write_byte 1_u8\n    slice = str.to_slice\n    slice.size.should eq(2)\n    slice[0].should eq(0_u8)\n    slice[1].should eq(1_u8)\n  end\n\n  it \"reads more than available (#1229)\" do\n    s = \"h\" * (10 * 1024)\n    str = IO::Memory.new(s)\n    str.gets(11 * 1024).should eq(s)\n  end\n\n  it \"writes after reading\" do\n    io = IO::Memory.new\n    io << \"abcdefghi\"\n    io.rewind\n    io.gets(3)\n    io.print(\"xyz\")\n    io.rewind\n    io.gets_to_end.should eq(\"abcxyzghi\")\n  end\n\n  it \"has a size\" do\n    IO::Memory.new(\"foo\").size.should eq(3)\n  end\n\n  it \"can tell\" do\n    io = IO::Memory.new(\"foo\")\n    io.tell.should eq(0)\n    io.gets(2)\n    io.tell.should eq(2)\n  end\n\n  it \"can seek set\" do\n    io = IO::Memory.new(\"abcdef\")\n    io.seek(3)\n    io.tell.should eq(3)\n    io.gets(1).should eq(\"d\")\n  end\n\n  it \"raises if seek set is negative\" do\n    io = IO::Memory.new(\"abcdef\")\n    expect_raises(ArgumentError, \"Negative pos\") do\n      io.seek(-1)\n    end\n  end\n\n  it \"can seek past the end\" do\n    io = IO::Memory.new\n    io << \"abc\"\n    io.rewind\n    io.seek(6)\n    io.gets_to_end.should eq(\"\")\n    io.print(\"xyz\")\n    io.rewind\n    io.gets_to_end.should eq(\"abc\\u{0}\\u{0}\\u{0}xyz\")\n  end\n\n  it \"can seek current\" do\n    io = IO::Memory.new(\"abcdef\")\n    io.seek(2)\n    io.seek(1, IO::Seek::Current)\n    io.gets(1).should eq(\"d\")\n  end\n\n  it \"raises if seek current leads to negative value\" do\n    io = IO::Memory.new(\"abcdef\")\n    io.seek(2)\n    expect_raises(ArgumentError, \"Negative pos\") do\n      io.seek(-3, IO::Seek::Current)\n    end\n  end\n\n  it \"can seek from the end\" do\n    io = IO::Memory.new(\"abcdef\")\n    io.seek(-2, IO::Seek::End)\n    io.gets(1).should eq(\"e\")\n  end\n\n  it \"can be closed\" do\n    io = IO::Memory.new\n    io << \"abc\"\n    io.close\n    io.closed?.should be_true\n\n    expect_raises(IO::Error, \"Closed stream\") { io.gets_to_end }\n    expect_raises(IO::Error, \"Closed stream\") { io.print \"hi\" }\n    expect_raises(IO::Error, \"Closed stream\") { io.seek(1) }\n    expect_raises(IO::Error, \"Closed stream\") { io.gets }\n    expect_raises(IO::Error, \"Closed stream\") { io.read_byte }\n  end\n\n  it \"seeks with pos and pos=\" do\n    io = IO::Memory.new(\"abcdef\")\n    io.pos = 4\n    io.gets(1).should eq(\"e\")\n    io.pos -= 2\n    io.gets(1).should eq(\"d\")\n  end\n\n  it \"clears\" do\n    io = IO::Memory.new\n    io << \"abc\"\n    io.rewind\n    io.gets(1)\n    io.clear\n    io.pos.should eq(0)\n    io.gets_to_end.should eq(\"\")\n  end\n\n  it \"raises if negative capacity\" do\n    expect_raises(ArgumentError, \"Negative capacity\") do\n      IO::Memory.new(-1)\n    end\n  end\n\n  it \"raises if capacity too big\" do\n    expect_raises(ArgumentError, \"Capacity too big\") do\n      IO::Memory.new(UInt32::MAX)\n    end\n  end\n\n  it \"creates from string\" do\n    io = IO::Memory.new \"abcdef\"\n    io.gets(2).should eq(\"ab\")\n    io.gets(3).should eq(\"cde\")\n\n    expect_raises(IO::Error, \"Read-only stream\") do\n      io.print 1\n    end\n  end\n\n  it \"creates from slice\" do\n    slice = Slice.new(6) { |i| ('a'.ord + i).to_u8 }\n    io = IO::Memory.new slice\n    io.gets(2).should eq(\"ab\")\n    io.gets(3).should eq(\"cde\")\n    io.print 'x'\n\n    String.new(slice).should eq(\"abcdex\")\n\n    expect_raises(IO::Error, \"Non-resizeable stream\") do\n      io.print 'z'\n    end\n  end\n\n  it \"creates from slice, non-writeable\" do\n    slice = Slice.new(6) { |i| ('a'.ord + i).to_u8 }\n    io = IO::Memory.new slice, writeable: false\n\n    expect_raises(IO::Error, \"Read-only stream\") do\n      io.print 'z'\n    end\n  end\n\n  it \"creates from read-only slice\" do\n    slice = Slice.new(6, read_only: true) { |i| ('a'.ord + i).to_u8 }\n    io = IO::Memory.new slice\n\n    expect_raises(IO::Error, \"Read-only stream\") do\n      io.print 'z'\n    end\n  end\n\n  it \"writes past end\" do\n    io = IO::Memory.new\n    io.pos = 1000\n    io.print 'a'\n    io.to_slice.should eq(Bytes.new(1001).tap { |bytes| bytes[-1] = 97 })\n  end\n\n  it \"writes past end with write_byte\" do\n    io = IO::Memory.new\n    io.pos = 1000\n    io.write_byte 'a'.ord.to_u8\n    io.to_slice.should eq(Bytes.new(1001).tap { |bytes| bytes[-1] = 97 })\n  end\n\n  it \"reads at offset\" do\n    io = IO::Memory.new(\"hello world\")\n\n    io.read_at(6, 3) do |sub|\n      sub.gets_to_end.should eq(\"wor\")\n\n      expect_raises(IO::Error, \"Read-only stream\") do\n        io << \"hello\"\n      end\n    end\n\n    io.read_at(0, 11) do |sub|\n      sub.gets_to_end.should eq(\"hello world\")\n    end\n\n    io.read_at(11, 0) do |sub|\n      sub.gets_to_end.should eq(\"\")\n    end\n  end\n\n  it \"raises when reading at offset outside of bounds\" do\n    io = IO::Memory.new(\"hello world\")\n\n    expect_raises(ArgumentError, \"Negative bytesize\") do\n      io.read_at(3, -1) { }\n    end\n\n    expect_raises(ArgumentError, \"Offset out of bounds\") do\n      io.read_at(12, 1) { }\n    end\n\n    expect_raises(ArgumentError, \"Bytesize out of bounds\") do\n      io.read_at(6, 6) { }\n    end\n  end\n\n  it \"consumes with gets_to_end\" do\n    io = IO::Memory.new(\"hello world\")\n    io.gets_to_end.should eq(\"hello world\")\n    io.gets_to_end.should eq(\"\")\n  end\n\n  it \"consumes with getb_to_end\" do\n    io = IO::Memory.new(Bytes[0, 1, 3, 6, 10, 15])\n    io.getb_to_end.should eq(Bytes[0, 1, 3, 6, 10, 15])\n    io.getb_to_end.should eq(Bytes[])\n    io.seek(3)\n    bytes = io.getb_to_end\n    bytes.should eq(Bytes[6, 10, 15])\n    bytes.read_only?.should be_false\n\n    io.seek(3)\n    io.write(Bytes[2, 4, 5])\n    bytes.should eq(Bytes[6, 10, 15])\n\n    io.seek(10)\n    io.getb_to_end.should eq(Bytes[])\n  end\n\n  it \"peeks\" do\n    str = \"hello world\"\n    io = IO::Memory.new(str)\n\n    io.peek.should eq(\"hello world\".to_slice)\n    io.pos.should eq(0)\n\n    io.skip(3)\n    io.peek.should eq(\"lo world\".to_slice)\n\n    io.skip_to_end\n    io.peek.should eq(Bytes.empty)\n  end\n\n  it \"peek readonly\" do\n    str = \"hello world\"\n    io = IO::Memory.new(str)\n\n    slice = io.peek\n    expect_raises(Exception) do\n      slice[0] = 0\n    end\n  end\n\n  it \"skips\" do\n    io = IO::Memory.new(\"hello\")\n    io.skip(2)\n    io.gets_to_end.should eq(\"llo\")\n\n    io.rewind\n    io.skip(5)\n    io.gets_to_end.should eq(\"\")\n\n    io.rewind\n\n    expect_raises(IO::EOFError) do\n      io.skip(6)\n    end\n  end\n\n  it \"skips_to_end\" do\n    io = IO::Memory.new(\"hello\")\n    io.skip_to_end\n    io.gets_to_end.should eq(\"\")\n  end\n\n  {% unless flag?(:without_iconv) %}\n    describe \"encoding\" do\n      describe \"decode\" do\n        it \"gets_to_end\" do\n          str = \"Hello world\" * 200\n          io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets_to_end.should eq(str)\n        end\n\n        it \"gets\" do\n          str = \"Hello world\\nFoo\\nBar\\n\" + (\"1234567890\" * 1000)\n          io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(chomp: false).should eq(\"Hello world\\n\")\n          io.gets(chomp: false).should eq(\"Foo\\n\")\n          io.gets(chomp: false).should eq(\"Bar\\n\")\n        end\n\n        it \"gets with chomp = false\" do\n          str = \"Hello world\\nFoo\\nBar\\n\" + (\"1234567890\" * 1000)\n          io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets.should eq(\"Hello world\")\n          io.gets.should eq(\"Foo\")\n          io.gets.should eq(\"Bar\")\n        end\n\n        it \"reads char\" do\n          str = \"x\\nHello world\" + (\"1234567890\" * 1000)\n          io = IO::Memory.new(str.encode(\"UCS-2LE\"))\n          io.set_encoding(\"UCS-2LE\")\n          io.gets(chomp: false).should eq(\"x\\n\")\n          str = str[2..-1]\n          str.each_char do |char|\n            io.read_char.should eq(char)\n          end\n          io.read_char.should be_nil\n        end\n      end\n    end\n  {% end %}\n\n  it \"allocates for > 1 GB\", tags: %w[slow] do\n    io = IO::Memory.new\n    mbstring = \"a\" * 1024 * 1024\n    1024.times { io << mbstring }\n\n    io.bytesize.should eq(1 << 30)\n    io.@capacity.should eq 1 << 30\n\n    io << mbstring\n\n    io.bytesize.should eq (1 << 30) + (1 << 20)\n    io.@capacity.should eq Int32::MAX\n\n    1022.times { io << mbstring }\n\n    io.write mbstring.to_slice[0..-4]\n    io << \"a\"\n    expect_raises(IO::EOFError) do\n      io << \"a\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/multi_writer_spec.cr",
    "content": "require \"spec\"\nrequire \"../spec_helper\"\n\ndescribe \"IO::MultiWriter\" do\n  describe \"#write\" do\n    it \"writes to multiple IOs\" do\n      io1 = IO::Memory.new\n      io2 = IO::Memory.new\n\n      writer = IO::MultiWriter.new(io1, io2)\n\n      writer.puts \"foo bar\"\n\n      io1.to_s.should eq(\"foo bar\\n\")\n      io2.to_s.should eq(\"foo bar\\n\")\n    end\n  end\n\n  describe \"#read\" do\n    it \"raises\" do\n      writer = IO::MultiWriter.new(Array(IO).new)\n\n      expect_raises(IO::Error, \"Can't read from IO::MultiWriter\") do\n        writer.read_byte\n      end\n    end\n  end\n\n  describe \"#close\" do\n    it \"stops reading\" do\n      io = IO::Memory.new\n      writer = IO::MultiWriter.new(io)\n\n      writer.close\n\n      expect_raises(IO::Error, \"Closed\") do\n        writer.puts \"foo\"\n      end\n\n      io.closed?.should be_false\n      io.to_s.should eq(\"\")\n    end\n\n    it \"closes the underlying stream if sync_close is true\" do\n      io = IO::Memory.new\n      writer = IO::MultiWriter.new(io, sync_close: true)\n\n      writer.close\n\n      io.closed?.should be_true\n    end\n  end\n\n  describe \"#flush\" do\n    it \"writes to IO and File\" do\n      with_tempfile(\"multiple_writer_spec\") do |path|\n        io = IO::Memory.new\n\n        File.open(path, \"w\") do |file|\n          writer = IO::MultiWriter.new(io, file)\n\n          writer.puts \"foo bar\"\n          writer.flush\n        end\n\n        io.to_s.should eq(\"foo bar\\n\")\n        File.read(path).should eq(\"foo bar\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/sized_spec.cr",
    "content": "require \"spec\"\n\nprivate class NoPeekIO < IO\n  def read(slice : Bytes)\n    0\n  end\n\n  def write(slice : Bytes) : Nil\n  end\n\n  def peek\n    raise \"shouldn't be invoked\"\n  end\nend\n\ndescribe \"IO::Sized\" do\n  describe \"#read\" do\n    it \"doesn't read past the limit when reading char-by-char\" do\n      io = IO::Memory.new \"abcdefg\"\n      sized = IO::Sized.new(io, read_size: 5)\n\n      sized.read_char.should eq('a')\n      sized.read_char.should eq('b')\n      sized.read_char.should eq('c')\n      sized.read_remaining.should eq(2)\n      sized.read_char.should eq('d')\n      sized.read_char.should eq('e')\n      sized.read_remaining.should eq(0)\n      sized.read_char.should be_nil\n      sized.read_remaining.should eq(0)\n      sized.read_char.should be_nil\n    end\n\n    it \"doesn't read past the limit when reading the correct size\" do\n      io = IO::Memory.new(\"1234567\")\n      sized = IO::Sized.new(io, read_size: 5)\n      slice = Bytes.new(5)\n\n      sized.read(slice).should eq(5)\n      String.new(slice).should eq(\"12345\")\n\n      sized.read(slice).should eq(0)\n      String.new(slice).should eq(\"12345\")\n    end\n\n    it \"reads partially when supplied with a larger slice\" do\n      io = IO::Memory.new(\"1234567\")\n      sized = IO::Sized.new(io, read_size: 5)\n      slice = Bytes.new(10)\n\n      sized.read(slice).should eq(5)\n      String.new(slice).should eq(\"12345\\0\\0\\0\\0\\0\")\n    end\n\n    it \"allows extending the size\" do\n      io = IO::Memory.new(\"1234567890\")\n      sized = IO::Sized.new(io, read_size: 5)\n      slice = Bytes.new(10)\n\n      sized.read(slice).should eq(5)\n      String.new(slice).should eq(\"12345\\0\\0\\0\\0\\0\")\n\n      sized.read_remaining = 5\n\n      sized.read(slice).should eq(5)\n      String.new(slice).should eq(\"67890\\0\\0\\0\\0\\0\")\n    end\n\n    it \"raises on negative numbers\" do\n      io = IO::Memory.new\n      expect_raises(ArgumentError, \"Negative read_size\") do\n        IO::Sized.new(io, read_size: -1)\n      end\n    end\n  end\n\n  describe \"#write\" do\n    it \"raises\" do\n      sized = IO::Sized.new(IO::Memory.new, read_size: 5)\n      expect_raises(IO::Error, \"Can't write to IO::Sized\") do\n        sized.puts \"test string\"\n      end\n    end\n  end\n\n  describe \"#close\" do\n    it \"stops reading\" do\n      io = IO::Memory.new \"abcdefg\"\n      sized = IO::Sized.new(io, read_size: 5)\n\n      sized.read_char.should eq('a')\n      sized.read_char.should eq('b')\n\n      sized.close\n      sized.closed?.should be_true\n      expect_raises(IO::Error, \"Closed stream\") do\n        sized.read_char\n      end\n    end\n\n    it \"closes the underlying stream if sync_close is true\" do\n      io = IO::Memory.new \"abcdefg\"\n      sized = IO::Sized.new(io, read_size: 5, sync_close: true)\n      sized.sync_close?.should be_true\n\n      io.closed?.should be_false\n      sized.close\n      io.closed?.should be_true\n    end\n  end\n\n  it \"read_byte\" do\n    io = IO::Memory.new \"abcdefg\"\n    sized = IO::Sized.new(io, read_size: 3)\n    sized.read_byte.should eq('a'.ord)\n    sized.read_byte.should eq('b'.ord)\n    sized.read_byte.should eq('c'.ord)\n    sized.read_byte.should be_nil\n  end\n\n  it \"gets\" do\n    io = IO::Memory.new \"foo\\nbar\\nbaz\"\n    sized = IO::Sized.new(io, read_size: 9)\n    sized.gets.should eq(\"foo\")\n    sized.gets.should eq(\"bar\")\n    sized.gets.should eq(\"b\")\n    sized.gets.should be_nil\n  end\n\n  it \"gets with chomp = false\" do\n    io = IO::Memory.new \"foo\\nbar\\nbaz\"\n    sized = IO::Sized.new(io, read_size: 9)\n    sized.gets(chomp: false).should eq(\"foo\\n\")\n    sized.gets(chomp: false).should eq(\"bar\\n\")\n    sized.gets(chomp: false).should eq(\"b\")\n    sized.gets(chomp: false).should be_nil\n  end\n\n  it \"peeks\" do\n    io = IO::Memory.new \"123456789\"\n    sized = IO::Sized.new(io, read_size: 6)\n    sized.peek.should eq(\"123456\".to_slice)\n    sized.gets_to_end.should eq(\"123456\")\n    sized.peek.should eq(Bytes.empty)\n  end\n\n  it \"doesn't peek when remaining = 0 (#4261)\" do\n    sized = IO::Sized.new(NoPeekIO.new, read_size: 0)\n    sized.peek.should eq(Bytes.empty)\n  end\n\n  it \"skips\" do\n    io = IO::Memory.new \"123456789\"\n    sized = IO::Sized.new(io, read_size: 6)\n    sized.skip(3)\n    sized.read_char.should eq('4')\n\n    expect_raises(IO::EOFError) do\n      sized.skip(6)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/io/stapled_spec.cr",
    "content": "require \"spec\"\n\ndescribe IO::Stapled do\n  it \"combines two IOs\" do\n    writer = IO::Memory.new\n    io = IO::Stapled.new IO::Memory.new(\"paul\"), writer\n    io.gets.should eq \"paul\"\n    io << \"peter\"\n    writer.to_s.should eq \"peter\"\n  end\n\n  it \"loops back\" do\n    io = IO::Stapled.new(*IO.pipe)\n    io.puts \"linus\"\n    io.gets.should eq \"linus\"\n  end\n\n  describe \"#close\" do\n    it \"does not close underlying IOs\" do\n      reader, writer = IO::Memory.new, IO::Memory.new\n      io = IO::Stapled.new reader, writer\n      io.sync_close?.should be_false\n      io.close\n      io.closed?.should be_true\n      reader.closed?.should be_false\n      writer.closed?.should be_false\n    end\n\n    it \"closes underlying IOs when sync_close is true\" do\n      reader, writer = IO::Memory.new, IO::Memory.new\n      io = IO::Stapled.new reader, writer, sync_close: true\n      io.sync_close?.should be_true\n      io.close\n      io.closed?.should be_true\n      reader.closed?.should be_true\n      writer.closed?.should be_true\n    end\n\n    it \"stops access to underlying IOs\" do\n      reader, writer = IO::Memory.new(\"cle\"), IO::Memory.new\n      io = IO::Stapled.new reader, writer\n      io.close\n      io.closed?.should be_true\n      reader.closed?.should be_false\n      writer.closed?.should be_false\n\n      expect_raises(IO::Error, \"Closed stream\") do\n        io.gets\n      end\n      expect_raises(IO::Error, \"Closed stream\") do\n        io.peek\n      end\n      expect_raises(IO::Error, \"Closed stream\") do\n        io << \"closed\"\n      end\n    end\n  end\n\n  it \"#sync_close?\" do\n    reader, writer = IO::Memory.new, IO::Memory.new\n    io = IO::Stapled.new reader, writer\n    io.sync_close = false\n    io.sync_close?.should be_false\n    io.sync_close = true\n    io.sync_close?.should be_true\n    io.close\n    reader.closed?.should be_true\n    writer.closed?.should be_true\n  end\n\n  it \"#peek delegates to reader\" do\n    reader = IO::Memory.new \"cletus\"\n    io = IO::Stapled.new reader, IO::Memory.new\n    io.peek.should eq \"cletus\".to_slice\n    io.gets\n    io.peek.should eq Bytes.empty\n  end\n\n  it \"#skip delegates to reader\" do\n    reader = IO::Memory.new \"cletus\"\n    io = IO::Stapled.new reader, IO::Memory.new\n    io.peek.should eq \"cletus\".to_slice\n    io.skip(4)\n    io.peek.should eq \"us\".to_slice\n  end\n\n  it \"#skip_to_end delegates to reader\" do\n    reader = IO::Memory.new \"cletus\"\n    io = IO::Stapled.new reader, IO::Memory.new\n    io.peek.should eq \"cletus\".to_slice\n    io.skip_to_end\n    io.peek.should eq Bytes.empty\n  end\n\n  describe \".pipe\" do\n    it \"creates a bidirectional pipe\" do\n      a, b = IO::Stapled.pipe\n      begin\n        a.sync_close?.should be_true\n        b.sync_close?.should be_true\n        a.puts \"john\"\n        b.gets.should eq \"john\"\n        b.puts \"paul\"\n        a.gets.should eq \"paul\"\n      ensure\n        a.close\n        b.close\n      end\n    end\n\n    it \"with block creates a bidirectional pipe\" do\n      ext_a, ext_b = nil, nil\n      IO::Stapled.pipe do |a, b|\n        ext_a, ext_b = a, b\n        a.sync_close?.should be_true\n        b.sync_close?.should be_true\n        a.puts \"john\"\n        b.gets.should eq \"john\"\n        b.puts \"paul\"\n        a.gets.should eq \"paul\"\n        a.sync_close = false\n        b.sync_close = false\n      end\n      ext_a.not_nil!.closed?.should be_true\n      ext_b.not_nil!.closed?.should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/iterator_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\nrequire \"iterator\"\n\nstruct StructIter\n  include Iterator(Int32)\n\n  def initialize(@a : Int32, @b : Int32); end\n\n  def next\n    if @a > @b\n      stop\n    else\n      cur = @a\n      @a += 1\n      cur\n    end\n  end\nend\n\nprivate class MockIterator\n  include Iterator(Int32)\n\n  def initialize\n    @x = 0\n    @y = Slice(Int32).new(5)\n  end\n\n  def next\n    return stop if @x >= 3\n    @x += 1\n  end\nend\n\ndescribe Iterator do\n  describe \"Iterator.empty\" do\n    it \"creates empty iterator\" do\n      iter = Iterator(String).empty\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"Iterator.of\" do\n    it \"creates singleton\" do\n      iter = Iterator.of(42)\n      iter.first(3).to_a.should eq([42, 42, 42])\n    end\n\n    it \"creates singleton from block\" do\n      a = 0\n      iter = Iterator.of { a += 1 }\n      iter.first(3).to_a.should eq([1, 2, 3])\n    end\n\n    it \"creates singleton from block can call Iterator.stop\" do\n      a = 0\n      iter = Iterator.of do\n        if a >= 5\n          Iterator.stop\n        else\n          a += 1\n        end\n      end\n      iter.should be_a(Iterator(Int32))\n      iter.first(10).to_a.should eq([1, 2, 3, 4, 5])\n    end\n  end\n\n  describe \"#accumulate\" do\n    context \"prefix sums\" do\n      it \"returns prefix sums\" do\n        iter = (1..4).each.accumulate\n        iter.next.should eq(1)\n        iter.next.should eq(3)\n        iter.next.should eq(6)\n        iter.next.should eq(10)\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"empty iterator stops immediately\" do\n        (1..0).each.accumulate.next.should be_a(Iterator::Stop)\n      end\n    end\n\n    context \"prefix sums, with init\" do\n      it \"returns prefix sums\" do\n        iter = (1..4).each.accumulate(5)\n        iter.next.should eq(5)\n        iter.next.should eq(6)\n        iter.next.should eq(8)\n        iter.next.should eq(11)\n        iter.next.should eq(15)\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"preserves initial type\" do\n        iter = {'a', 'b', 'c'}.each.accumulate(\"def\")\n        iter.next.should eq(\"def\")\n        iter.next.should eq(\"defa\")\n        iter.next.should eq(\"defab\")\n        iter.next.should eq(\"defabc\")\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"empty iterator returns only initial value\" do\n        iter = (1..0).each.accumulate(7)\n        iter.next.should eq(7)\n        iter.next.should be_a(Iterator::Stop)\n      end\n    end\n\n    context \"generic cumulative fold\" do\n      it \"accumulates values\" do\n        iter = (4..7).each.accumulate { |x, y| x * 10 + y }\n        iter.next.should eq(4)\n        iter.next.should eq(45)\n        iter.next.should eq(456)\n        iter.next.should eq(4567)\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"empty iterator stops immediately\" do\n        (1..0).each.accumulate { raise \"\" }.next.should be_a(Iterator::Stop)\n      end\n    end\n\n    context \"generic cumulative fold, with init\" do\n      it \"accumulates values\" do\n        iter = (4..7).each.accumulate(8) { |x, y| x * 10 + y }\n        iter.next.should eq(8)\n        iter.next.should eq(84)\n        iter.next.should eq(845)\n        iter.next.should eq(8456)\n        iter.next.should eq(84567)\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"preserves initial type\" do\n        iter = {4, 3, 2}.each.accumulate(\"X\") { |x, y| x * y }\n        iter.next.should eq(\"X\")\n        iter.next.should eq(\"XXXX\")\n        iter.next.should eq(\"XXXXXXXXXXXX\")\n        iter.next.should eq(\"XXXXXXXXXXXXXXXXXXXXXXXX\")\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"empty iterator returns only initial value\" do\n        iter = (1..0).each.accumulate(7) { raise \"\" }\n        iter.next.should eq(7)\n        iter.next.should be_a(Iterator::Stop)\n      end\n    end\n  end\n\n  describe \"compact_map\" do\n    it \"applies the function and removes nil values\" do\n      assert_iterates_iterator [1, 3], (1..3).each.compact_map { |e| e.odd? ? e : nil }\n    end\n\n    it \"sums after compact_map to_a\" do\n      (1..3).each.compact_map { |e| e.odd? ? e : nil }.to_a.sum.should eq(4)\n    end\n  end\n\n  describe \"chain\" do\n    it \"chains\" do\n      iter = (1..2).each.chain(('a'..'b').each)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq('a')\n      iter.next.should eq('b')\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    # NOTE: This spec would only fail in release mode.\n    it \"does not experience tuple upcase bug of #13411\" do\n      [{true}].each.chain([{1}].each).first(3).to_a.should eq [{true}, {1}]\n    end\n\n    describe \"chain indeterminate number of iterators\" do\n      it \"chains all together\" do\n        iters = [[0], [1], [2, 3], [4, 5, 6]].each.map &.each\n        iter = Iterator.chain iters\n        7.times { |i| iter.next.should eq i }\n        iter.next.should be_a Iterator::Stop\n      end\n\n      it \"chains empty\" do\n        arrs = [] of Array(Int32)\n        iter = Iterator.chain arrs.map(&.each)\n        iter.next.should be_a Iterator::Stop\n      end\n\n      it \"chains array of empty\" do\n        iters = [[0], [1], ([] of Int32), [2, 3], ([] of Int32), [4, 5, 6]].each.map &.each\n        iter = Iterator.chain iters\n        7.times { |i| iter.next.should eq i }\n        iter.next.should be_a Iterator::Stop\n      end\n\n      it \"rewinds\" do\n        iters = [[0], [1], ([] of Int32), [2, 3], ([] of Int32), [4, 5, 6]].each.map &.each\n        iter = Iterator.chain iters\n        7.times { |i| iter.next.should eq i }\n        iter.next.should be_a Iterator::Stop\n      end\n\n      it \"chains iterators of different type\" do\n        iters = [[1, 2], [\"string\"], [\"and number\", 3], [] of String,\n                 [\"or float\", 4.0_f64]].each.map &.each\n        iter = Iterator.chain iters\n        iter.next.should eq 1\n        iter.next.should eq 2\n        iter.next.should eq \"string\"\n        iter.next.should eq \"and number\"\n        iter.next.should eq 3\n        iter.next.should eq \"or float\"\n        iter.next.should eq 4.0_f64\n        iter.next.should be_a Iterator::Stop\n      end\n    end\n  end\n\n  describe \"compact_map\" do\n    it \"does not return nil values\" do\n      iter = [1, nil, 2, nil].each.compact_map { |e| e.try &.*(2) }\n      iter.next.should eq 2\n      iter.next.should eq 4\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"#cons\" do\n    it \"conses\" do\n      iter = (1..5).each.cons(3)\n      iter.next.should eq([1, 2, 3])\n      iter.next.should eq([2, 3, 4])\n      iter.next.should eq([3, 4, 5])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    describe \"reuse\" do\n      it \"reuse as nil\" do\n        iter = (1..5).each.cons(3, reuse: nil)\n        first = iter.next\n        first.should eq([1, 2, 3])\n        second = iter.next\n        second.should eq([2, 3, 4])\n        first.should_not be(second)\n        iter.next.should eq([3, 4, 5])\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"reuse as Bool\" do\n        iter = (1..5).each.cons(3, reuse: true)\n        first = iter.next\n        first.should eq([1, 2, 3])\n        second = iter.next\n        second.should eq([2, 3, 4])\n        first.should be(second)\n        iter.next.should eq([3, 4, 5])\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"reuse as Array\" do\n        reuse = [] of Int32\n        iter = (1..5).each.cons(3, reuse: reuse)\n        value = iter.next\n        value.should be(reuse)\n        value.should eq([1, 2, 3])\n        value = iter.next\n        value.should be(reuse)\n        value.should eq([2, 3, 4])\n        value = iter.next\n        value.should be(reuse)\n        value.should eq([3, 4, 5])\n        iter.next.should be_a(Iterator::Stop)\n      end\n\n      it \"reuse as deque\" do\n        reuse = Deque(Int32).new\n        iter = (1..5).each.cons(3, reuse: reuse)\n        value = iter.next\n        value.should be(reuse)\n        value.should eq(Deque{1, 2, 3})\n        value = iter.next\n        value.should be(reuse)\n        value.should eq(Deque{2, 3, 4})\n        value = iter.next\n        value.should be(reuse)\n        value.should eq(Deque{3, 4, 5})\n        iter.next.should be_a(Iterator::Stop)\n      end\n    end\n  end\n\n  describe \"#cons_pair\" do\n    it \"conses\" do\n      iter = (1..5).each.cons_pair\n      iter.next.should eq({1, 2})\n      iter.next.should eq({2, 3})\n      iter.next.should eq({3, 4})\n      iter.next.should eq({4, 5})\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"doesn't include stop in return type\" do\n      (1..3).each.cons_pair.to_a.should eq([{1, 2}, {2, 3}])\n    end\n  end\n\n  describe \"cycle\" do\n    it \"does cycle from range\" do\n      iter = (1..3).each.cycle\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n    end\n\n    it \"cycles an empty array\" do\n      ary = [] of Int32\n      values = ary.each.cycle.to_a\n      values.should be_empty\n    end\n\n    it \"cycles N times\" do\n      iter = (1..2).each.cycle(2)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does not cycle provided 0\" do\n      iter = (1..2).each.cycle(0)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does not cycle provided a negative size\" do\n      iter = (1..2).each.cycle(-1)\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"each\" do\n    it \"yields the individual elements to the block\" do\n      iter = [\"a\", \"b\", \"c\"].each\n      concatenated = \"\"\n      iter.each { |e| concatenated += e }.should be_nil\n      concatenated.should eq \"abc\"\n    end\n  end\n\n  describe \"each_slice\" do\n    it \"gets all the slices of the size n\" do\n      iter = (1..9).each.each_slice(3)\n      iter.next.should eq [1, 2, 3]\n      iter.next.should eq [4, 5, 6]\n      iter.next.should eq [7, 8, 9]\n      iter.next.should be_a Iterator::Stop\n    end\n\n    it \"also works if it does not add up\" do\n      iter = (1..4).each.each_slice(3)\n      iter.next.should eq [1, 2, 3]\n      iter.next.should eq [4]\n      iter.next.should be_a Iterator::Stop\n    end\n\n    it \"returns each_slice iterator with reuse = true\" do\n      iter = (1..5).each.each_slice(2, reuse: true)\n\n      a = iter.next\n      a.should eq([1, 2])\n\n      b = iter.next\n      b.should eq([3, 4])\n      b.should be(a)\n    end\n\n    it \"returns each_slice iterator with reuse = array\" do\n      reuse = [] of Int32\n      iter = (1..5).each.each_slice(2, reuse: reuse)\n\n      a = iter.next\n      a.should eq([1, 2])\n      a.should be(reuse)\n\n      b = iter.next\n      b.should eq([3, 4])\n      b.should be(reuse)\n    end\n  end\n\n  describe \"in_groups_of\" do\n    it \"creates groups of one\" do\n      iter = (1..3).each.in_groups_of(1)\n      iter.next.should eq([1])\n      iter.next.should eq([2])\n      iter.next.should eq([3])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"creates a group of two\" do\n      iter = (1..3).each.in_groups_of(2)\n      iter.next.should eq([1, 2])\n      iter.next.should eq([3, nil])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"fills up with the fill up argument\" do\n      iter = (1..3).each.in_groups_of(2, 'z')\n      iter.next.should eq([1, 2])\n      iter.next.should eq([3, 'z'])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"raises argument error if size is less than 0\" do\n      expect_raises ArgumentError, \"Size must be positive\" do\n        [1, 2, 3].each.in_groups_of(0)\n      end\n    end\n\n    it \"still works with other iterator methods like to_a\" do\n      iter = (1..3).each.in_groups_of(2, 'z')\n      iter.to_a.should eq [[1, 2], [3, 'z']]\n    end\n\n    it \"creates a group of two with reuse = true\" do\n      iter = (1..3).each.in_groups_of(2, reuse: true)\n\n      a = iter.next\n      a.should eq([1, 2])\n\n      b = iter.next\n      b.should eq([3, nil])\n      b.should be(a)\n\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"map\" do\n    it \"does map with Range iterator\" do\n      iter = (1..3).each.map &.*(2)\n      iter.next.should eq(2)\n      iter.next.should eq(4)\n      iter.next.should eq(6)\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"reject\" do\n    it \"does reject with Range iterator\" do\n      iter = (1..3).each.reject &.>=(2)\n      iter.next.should eq(1)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with pattern\" do\n      iter = (1..5).each.reject(2..4)\n      iter.next.should eq(1)\n      iter.next.should eq(5)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with type\" do\n      ary = [1, false, 3, true].each.reject(Bool).to_a\n      ary.should eq([1, 3])\n      ary.should be_a(Array(Int32))\n    end\n  end\n\n  describe \"select\" do\n    it \"does select with Range iterator\" do\n      iter = (1..3).each.select &.>=(2)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with pattern\" do\n      iter = (1..10).each.select(3..5)\n      iter.next.should eq(3)\n      iter.next.should eq(4)\n      iter.next.should eq(5)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with type\" do\n      ary = [1, nil, 3, false].each.select(Int32).to_a\n      ary.should eq([1, 3])\n      ary.should be_a(Array(Int32))\n    end\n  end\n\n  describe \"skip\" do\n    it \"does skip with Range iterator\" do\n      iter = (1..3).each.skip(2)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"is cool to skip 0 elements\" do\n      (1..3).each.skip(0).to_a.should eq [1, 2, 3]\n    end\n\n    it \"raises ArgumentError if negative size is provided\" do\n      expect_raises(ArgumentError) do\n        (1..3).each.skip(-1)\n      end\n    end\n  end\n\n  describe \"skip_while\" do\n    it \"does skip_while with an array\" do\n      iter = [1, 2, 3, 4, 0].each.skip_while { |i| i < 3 }\n      iter.next.should eq(3)\n      iter.next.should eq(4)\n      iter.next.should eq(0)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"can skip everything\" do\n      iter = (1..3).each.skip_while { true }\n      iter.to_a.should eq [] of Int32\n    end\n\n    it \"returns the full array if the condition is false for the first item\" do\n      iter = (1..2).each.skip_while { false }\n      iter.to_a.should eq [1, 2]\n    end\n\n    it \"only calls the block as much as needed\" do\n      called = 0\n      iter = (1..5).each.skip_while do |i|\n        called += 1\n        i < 3\n      end\n      5.times { iter.next }\n      called.should eq 3\n    end\n  end\n\n  describe \"slice\" do\n    it \"slices\" do\n      iter = (1..8).each.slice(3)\n      iter.next.should eq([1, 2, 3])\n      iter.next.should eq([4, 5, 6])\n      iter.next.should eq([7, 8])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"doesnt conflict with `::Slice` type\" do\n      assert_iterates_iterator [1, 2, 3], MockIterator.new.each\n    end\n  end\n\n  describe \"step\" do\n    it \"returns every element\" do\n      iter = (1..3).each.step(1)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"returns every other element\" do\n      iter = (1..5).each.step(2)\n      iter.next.should eq(1)\n      iter.next.should eq(3)\n      iter.next.should eq(5)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"returns every third element\" do\n      iter = (1..12).each.step(3)\n      iter.next.should eq(1)\n      iter.next.should eq(4)\n      iter.next.should eq(7)\n      iter.next.should eq(10)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"raises with nonsensical steps\" do\n      expect_raises(ArgumentError) do\n        (1..2).each.step(0)\n      end\n\n      expect_raises(ArgumentError) do\n        (1..2).each.step(-1)\n      end\n    end\n  end\n\n  describe \"first\" do\n    it \"does first with Range iterator\" do\n      iter = (1..3).each.first(2)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does first with more than available\" do\n      (1..3).each.first(10).to_a.should eq([1, 2, 3])\n    end\n\n    it \"is cool to first 0 elements\" do\n      iter = (1..3).each.first(0)\n      iter.next.should be_a Iterator::Stop\n    end\n\n    it \"raises ArgumentError if negative size is provided\" do\n      expect_raises(ArgumentError) do\n        (1..3).each.first(-1)\n      end\n    end\n  end\n\n  describe \"take_while\" do\n    it \"does take_while with Range iterator\" do\n      iter = (1..5).each.take_while { |i| i < 3 }\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does take_while with more than available\" do\n      (1..3).each.take_while { true }.to_a.should eq([1, 2, 3])\n    end\n\n    it \"only calls the block as much as needed\" do\n      called = 0\n      iter = (1..5).each.take_while do |i|\n        called += 1\n        i < 3\n      end\n      5.times { iter.next }\n      called.should eq 3\n    end\n  end\n\n  describe \"tap\" do\n    it \"taps\" do\n      a = 0\n\n      iter = (1..3).each.tap { |x| a += x }\n      iter.next.should eq(1)\n      a.should eq(1)\n\n      iter.next.should eq(2)\n      a.should eq(3)\n\n      iter.next.should eq(3)\n      a.should eq(6)\n\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"uniq\" do\n    it \"without block\" do\n      iter = (1..8).each.map { |x| x % 3 }.uniq # ameba:disable Performance/ChainedCallWithNoBang\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(0)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"with block\" do\n      iter = (1..8).each.uniq { |x| (x % 3).to_s }\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"#with_index\" do\n    it_iterates \"with default offset\", [{1, 0}, {2, 1}, {3, 2}], (1..3).each.with_index, tuple: true\n    it_iterates \"with explicit offset\", [{1, 10}, {2, 11}, {3, 12}], (1..3).each.with_index(10), tuple: true\n    it_iterates \"with non-Int32 offset\", [{1, Int64::MIN}, {2, Int64::MIN + 1}, {3, Int64::MIN + 2}], (1..3).each.with_index(Int64::MIN), tuple: true\n  end\n\n  describe \"with object\" do\n    it \"does with object\" do\n      iter = (1..3).each.with_object(\"a\")\n      iter.next.should eq({1, \"a\"})\n      iter.next.should eq({2, \"a\"})\n      iter.next.should eq({3, \"a\"})\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with object, with block\" do\n      tuples = [] of {Int32, String}\n      object = \"a\"\n      (1..3).each.with_object(object) do |value, obj|\n        tuples << {value, obj}\n      end.should be(object)\n      tuples.should eq([{1, object}, {2, object}, {3, object}])\n    end\n  end\n\n  describe \"zip\" do\n    it \"does skip with Range iterator\" do\n      r1 = (1..3).each\n      r2 = ('a'..'c').each\n      iter = r1.zip(r2)\n      iter.next.should eq({1, 'a'})\n      iter.next.should eq({2, 'b'})\n      iter.next.should eq({3, 'c'})\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"takes multiple Iterators\" do\n      r1 = (1..4).each\n      r2 = ('a'..'c').each\n      r3 = (\"U\"..\"Z\").each\n      iter = r1.zip(r2, r3)\n      iter.next.should eq({1, 'a', \"U\"})\n      iter.next.should eq({2, 'b', \"V\"})\n      iter.next.should eq({3, 'c', \"W\"})\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\n\n  describe \"integration\" do\n    it \"combines many iterators\" do\n      (1..100).each\n        .select { |x| 50 <= x < 60 }\n        .map { |x| x * 2 }\n        .first(3)\n        .to_a\n        .should eq([100, 102, 104])\n    end\n  end\n\n  describe \"flatten\" do\n    it \"flattens an iterator of mixed-type iterators\" do\n      iter = [(1..2).each, ('a'..'b').each, {\"c\" => 3}.each].each.flatten\n\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq('a')\n      iter.next.should eq('b')\n      iter.next.should eq({\"c\", 3})\n\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens an iterator of mixed-type elements and iterators\" do\n      iter = [(1..2).each, 'a'].each.flatten\n\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq('a')\n\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens an iterator of mixed-type elements and iterators and iterators of iterators\" do\n      iter = [(1..2).each, [['a', 'b'].each].each, \"foo\"].each.flatten\n\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq('a')\n      iter.next.should eq('b')\n      iter.next.should eq(\"foo\")\n\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens deeply-nested and mixed type iterators\" do\n      iter = [[[1], 2], [3, [[4, 5], 6], 7], \"a\"].each.flatten\n\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should eq(4)\n      iter.next.should eq(5)\n      iter.next.should eq(6)\n      iter.next.should eq(7)\n      iter.next.should eq(\"a\")\n\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens a variety of edge cases\" do\n      ([] of Nil).each.flatten.to_a.should eq([] of Nil)\n      ['a'].each.flatten.to_a.should eq(['a'])\n      [[[[[[\"hi\"]]]]]].each.flatten.to_a.should eq([\"hi\"])\n    end\n\n    it \"flattens a deeply-nested iterables and arrays (#3703)\" do\n      iter = [[1, {2, 3}, 4], [{5 => 6}, 7]].each.flatten\n\n      iter.next.should eq(1)\n      iter.next.should eq({2, 3})\n      iter.next.should eq(4)\n      iter.next.should eq({5 => 6})\n      iter.next.should eq(7)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens nested struct iterators with internal state being value types\" do\n      iter = (1..2).each.map { |i| StructIter.new(10 * i + 1, 10 * i + 3) }.flatten # ameba:disable Performance/FlattenAfterMap\n\n      iter.next.should eq(11)\n      iter.next.should eq(12)\n      iter.next.should eq(13)\n      iter.next.should eq(21)\n      iter.next.should eq(22)\n      iter.next.should eq(23)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"return iterator itself by rewind\" do\n      iter = [1, [2, 3], 4].each.flatten\n\n      iter.to_a.should eq([1, 2, 3, 4])\n    end\n  end\n\n  describe \"#flat_map\" do\n    it \"flattens returned arrays\" do\n      iter = [1, 2, 3].each.flat_map { |x| [x, x] }\n\n      iter.next.should eq(1)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens returned items\" do\n      iter = [1, 2, 3].each.flat_map { |x| x }\n\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens returned iterators\" do\n      iter = [1, 2, 3].each.flat_map { |x| [x, x].each }\n\n      iter.next.should eq(1)\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens returned values\" do\n      iter = [1, 2, 3].each.flat_map do |x|\n        case x\n        when 1\n          x\n        when 2\n          [x, x]\n        else\n          [x, x].each\n        end\n      end\n\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"flattens returned values of mixed element types in #to_a\" do\n      iter = [1, 'a', \"\"].each.flat_map do |x|\n        case x\n        when Int32\n          x\n        when Char\n          [x, x]\n        else\n          [x, x].each\n        end\n      end\n      iter.to_a.should eq([1, 'a', 'a', \"\", \"\"])\n    end\n  end\n\n  describe \"#slice_after\" do\n    it \"slices after\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_after(&.even?)\n      iter.next.should eq([1, 3, 5, 8])\n      iter.next.should eq([10])\n      iter.next.should eq([11, 13, 15, 16])\n      iter.next.should eq([17])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices after: #to_a\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      ary.slice_after(&.even?).to_a.should eq([\n        [1, 3, 5, 8],\n        [10],\n        [11, 13, 15, 16],\n        [17],\n      ])\n    end\n\n    it \"slices after: #rewind\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_after(&.even?)\n      iter.next.should eq([1, 3, 5, 8])\n      iter.next.should eq([10])\n    end\n\n    it \"slices after with reuse = true\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_after(reuse: true, &.even?)\n      a = iter.next\n      a.should eq([1, 3, 5, 8])\n\n      b = iter.next\n      b.should eq([10])\n\n      a.should be(b)\n    end\n\n    it \"slices after with reuse = array\" do\n      reuse = [] of Int32\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_after(reuse: reuse, &.even?)\n      a = iter.next\n      a.should eq([1, 3, 5, 8])\n\n      b = iter.next\n      b.should eq([10])\n\n      a.should be(b)\n      a.should be(reuse)\n    end\n\n    it \"slices after: non-bool block\" do\n      ary = [1, nil, nil, 2, 3, nil]\n      iter = ary.slice_after(&.itself)\n      iter.next.should eq([1])\n      iter.next.should eq([nil, nil, 2])\n      iter.next.should eq([3])\n      iter.next.should eq([nil])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices after pattern\" do\n      ary = [\"foo\", \"bar\", \"baz\\n\", \"qux\", \"other\\n\", \"end\"]\n      iter = ary.slice_after(/\\n/)\n      iter.next.should eq([\"foo\", \"bar\", \"baz\\n\"])\n      iter.next.should eq([\"qux\", \"other\\n\"])\n      iter.next.should eq([\"end\"])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices after pattern with reuse = true\" do\n      ary = [\"foo\", \"bar\", \"baz\\n\", \"qux\", \"other\\n\", \"end\"]\n      iter = ary.slice_after(/\\n/, reuse: true)\n\n      a = iter.next\n      a.should eq([\"foo\", \"bar\", \"baz\\n\"])\n\n      b = iter.next\n      b.should eq([\"qux\", \"other\\n\"])\n\n      a.should be(b)\n    end\n  end\n\n  describe \"#slice_before\" do\n    it \"slices before\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_before(&.even?)\n      iter.next.should eq([1, 3, 5])\n      iter.next.should eq([8])\n      iter.next.should eq([10, 11, 13, 15])\n      iter.next.should eq([16, 17])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices before: first element matches\" do\n      ary = [2, 3, 4]\n      iter = ary.slice_before(&.even?)\n      iter.next.should eq([2, 3])\n      iter.next.should eq([4])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices before nil\" do\n      ary = [1, 2, nil, 3, nil]\n      iter = ary.slice_before(&.nil?)\n      iter.next.should eq([1, 2])\n      iter.next.should eq([nil, 3])\n      iter.next.should eq([nil])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices before: #to_a\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      ary.slice_before(&.even?).to_a.should eq([\n        [1, 3, 5],\n        [8],\n        [10, 11, 13, 15],\n        [16, 17],\n      ])\n    end\n\n    it \"slices before: #rewind\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_before(&.even?)\n      iter.next.should eq([1, 3, 5])\n      iter.next.should eq([8])\n    end\n\n    it \"slices before with reuse = true\" do\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_before(reuse: true, &.even?)\n      a = iter.next\n      a.should eq([1, 3, 5])\n\n      b = iter.next\n      b.should eq([8])\n\n      a.should be(b)\n    end\n\n    it \"slices before with reuse = array\" do\n      reuse = [] of Int32\n      ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]\n      iter = ary.slice_before(reuse: reuse, &.even?)\n      a = iter.next\n      a.should eq([1, 3, 5])\n\n      b = iter.next\n      b.should eq([8])\n\n      a.should be(b)\n      a.should be(reuse)\n    end\n\n    it \"slices before: non-bool block\" do\n      ary = [1, nil, nil, 2, 3, nil]\n      iter = ary.slice_before(&.itself)\n      iter.next.should eq([1, nil, nil])\n      iter.next.should eq([2])\n      iter.next.should eq([3, nil])\n    end\n\n    it \"slices before pattern\" do\n      ary = [\"foo\", \"bar\", \"baz\\n\", \"qux\", \"other\\n\", \"end\"]\n      iter = ary.slice_before(/\\n/)\n      iter.next.should eq([\"foo\", \"bar\"])\n      iter.next.should eq([\"baz\\n\", \"qux\"])\n      iter.next.should eq([\"other\\n\", \"end\"])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices before pattern with reuse = true\" do\n      ary = [\"foo\", \"bar\", \"baz\\n\", \"qux\", \"other\\n\", \"end\"]\n      iter = ary.slice_before(/\\n/, reuse: true)\n\n      a = iter.next\n      a.should eq([\"foo\", \"bar\"])\n\n      b = iter.next\n      b.should eq([\"baz\\n\", \"qux\"])\n\n      a.should be(b)\n    end\n  end\n\n  describe \"#slice_when\" do\n    it \"slices when\" do\n      ary = [1, 1, 1, 2, 2, 3, 4, 4]\n      iter = ary.slice_when { |x, y| x != y }\n      iter.next.should eq([1, 1, 1])\n      iter.next.should eq([2, 2])\n      iter.next.should eq([3])\n      iter.next.should eq([4, 4])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices when: single value\" do\n      ary = [1]\n      iter = ary.slice_when { |x, y| x != y }\n      iter.next.should eq([1])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices when: two values\" do\n      ary = [1, 2]\n      iter = ary.slice_when { |x, y| x != y }\n      iter.next.should eq([1])\n      iter.next.should eq([2])\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"slices when: #to_a\" do\n      ary = [1, 1, 1, 2, 2, 3, 4, 4]\n      ary.slice_when { |x, y| x != y }.to_a.should eq([\n        [1, 1, 1],\n        [2, 2],\n        [3],\n        [4, 4],\n      ])\n    end\n\n    it \"slices when: #rewind\" do\n      ary = [1, 1, 1, 2, 2, 3, 4, 4]\n      iter = ary.slice_when { |x, y| x != y }\n      iter.next.should eq([1, 1, 1])\n      iter.next.should eq([2, 2])\n    end\n\n    it \"slices when with reuse = true\" do\n      ary = [1, 1, 1, 2, 2, 3, 4, 4]\n      iter = ary.slice_when(reuse: true) { |x, y| x != y }\n      a = iter.next\n      a.should eq([1, 1, 1])\n\n      b = iter.next\n      b.should eq([2, 2])\n\n      a.should be(b)\n    end\n\n    it \"slices when with reuse = array\" do\n      reuse = [] of Int32\n      ary = [1, 1, 1, 2, 2, 3, 4, 4]\n      iter = ary.slice_when(reuse) { |x, y| x != y }\n      a = iter.next\n      a.should eq([1, 1, 1])\n\n      b = iter.next\n      b.should eq([2, 2])\n\n      a.should be(b)\n      a.should be(reuse)\n    end\n\n    it \"slices when: non-bool block\" do\n      ary = [1, 2, nil, 3, nil, nil, 4]\n      ary.slice_when { |x, y| y }.to_a.should eq([\n        [1],\n        [2, nil],\n        [3, nil, nil],\n        [4],\n      ])\n    end\n  end\n\n  describe \"#chunk_while\" do\n    it \"chunks while\" do\n      ary = [1, 1, 1, 2, 2, 3, 4, 4]\n      iter = ary.chunk_while { |x, y| x == y }\n      iter.next.should eq([1, 1, 1])\n      iter.next.should eq([2, 2])\n      iter.next.should eq([3])\n      iter.next.should eq([4, 4])\n      iter.next.should be_a(Iterator::Stop)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/json/any_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"json\"\nrequire \"yaml\"\n\ndescribe JSON::Any do\n  it \".new\" do\n    JSON::Any.new(nil).raw.should be_nil\n    JSON::Any.new(true).raw.should be_true\n    JSON::Any.new(1_i64).raw.should eq 1_i64\n    JSON::Any.new(1).raw.should eq 1\n    JSON::Any.new(1_u8).raw.should eq 1\n    JSON::Any.new(0.0).raw.should eq 0.0\n    JSON::Any.new(0.0_f32).raw.should eq 0.0\n    JSON::Any.new(\"foo\").raw.should eq \"foo\"\n    JSON::Any.new([] of JSON::Any).raw.should eq [] of JSON::Any\n    JSON::Any.new({} of String => JSON::Any).raw.should eq({} of String => JSON::Any)\n  end\n\n  describe \"casts\" do\n    it \"gets nil\" do\n      JSON.parse(\"null\").as_nil.should be_nil\n    end\n\n    it \"gets bool\" do\n      JSON.parse(\"true\").as_bool.should be_true\n      JSON.parse(\"false\").as_bool.should be_false\n      JSON.parse(\"true\").as_bool?.should be_true\n      JSON.parse(\"false\").as_bool?.should be_false\n      JSON.parse(\"2\").as_bool?.should be_nil\n    end\n\n    it \"gets int32\" do\n      JSON.parse(\"123\").as_i.should eq(123)\n      JSON.parse(\"123\").as_i?.should eq(123)\n      JSON.parse(\"true\").as_i?.should be_nil\n    end\n\n    it \"gets int64\" do\n      JSON.parse(\"123456789123456\").as_i64.should eq(123456789123456)\n      JSON.parse(\"123456789123456\").as_i64?.should eq(123456789123456)\n      JSON.parse(\"true\").as_i64?.should be_nil\n    end\n\n    it \"gets float32\" do\n      JSON.parse(\"123.45\").as_f32.should eq(123.45_f32)\n      expect_raises(TypeCastError) { JSON.parse(\"true\").as_f32 }\n      JSON.parse(\"123.45\").as_f32?.should eq(123.45_f32)\n      JSON.parse(\"true\").as_f32?.should be_nil\n    end\n\n    it \"gets float32 from JSON integer (#8618)\" do\n      value = JSON.parse(\"123\").as_f32\n      value.should eq(123.0)\n      value.should be_a(Float32)\n\n      value = JSON.parse(\"123\").as_f32?\n      value.should eq(123.0)\n      value.should be_a(Float32)\n    end\n\n    it \"gets float64\" do\n      JSON.parse(\"123.45\").as_f.should eq(123.45)\n      expect_raises(TypeCastError) { JSON.parse(\"true\").as_f }\n      JSON.parse(\"123.45\").as_f?.should eq(123.45)\n      JSON.parse(\"true\").as_f?.should be_nil\n    end\n\n    it \"gets float64 from JSON integer (#8618)\" do\n      value = JSON.parse(\"123\").as_f\n      value.should eq(123.0)\n      value.should be_a(Float64)\n\n      value = JSON.parse(\"123\").as_f?\n      value.should eq(123.0)\n      value.should be_a(Float64)\n    end\n\n    it \"gets string\" do\n      JSON.parse(%(\"hello\")).as_s.should eq(\"hello\")\n      JSON.parse(%(\"hello\")).as_s?.should eq(\"hello\")\n      JSON.parse(\"true\").as_s?.should be_nil\n    end\n\n    it \"gets array\" do\n      JSON.parse(%([1, 2, 3])).as_a.should eq([1, 2, 3])\n      JSON.parse(%([1, 2, 3])).as_a?.should eq([1, 2, 3])\n      JSON.parse(\"true\").as_a?.should be_nil\n    end\n\n    it \"gets hash\" do\n      JSON.parse(%({\"foo\": \"bar\"})).as_h.should eq({\"foo\" => \"bar\"})\n      JSON.parse(%({\"foo\": \"bar\"})).as_h?.should eq({\"foo\" => \"bar\"})\n      JSON.parse(\"true\").as_h?.should be_nil\n    end\n  end\n\n  describe \"#size\" do\n    it \"of array\" do\n      JSON.parse(\"[1, 2, 3]\").size.should eq(3)\n    end\n\n    it \"of hash\" do\n      JSON.parse(%({\"foo\": \"bar\"})).size.should eq(1)\n    end\n  end\n\n  describe \"#[]\" do\n    it \"of array\" do\n      JSON.parse(\"[1, 2, 3]\")[1].raw.should eq(2)\n    end\n\n    it \"of hash\" do\n      JSON.parse(%({\"foo\": \"bar\"}))[\"foo\"].raw.should eq(\"bar\")\n    end\n  end\n\n  describe \"#[]?\" do\n    it \"of array\" do\n      JSON.parse(\"[1, 2, 3]\")[1]?.not_nil!.raw.should eq(2)\n      JSON.parse(\"[1, 2, 3]\")[3]?.should be_nil\n      JSON.parse(\"[true, false]\")[1]?.should be_false\n    end\n\n    it \"of hash\" do\n      JSON.parse(%({\"foo\": \"bar\"}))[\"foo\"]?.not_nil!.raw.should eq(\"bar\")\n      JSON.parse(%({\"foo\": \"bar\"}))[\"fox\"]?.should be_nil\n      JSON.parse(%q<{\"foo\": false}>)[\"foo\"]?.should be_false\n    end\n  end\n\n  describe \"#dig?\" do\n    it \"gets the value at given path given splat\" do\n      obj = JSON.parse(%({\"foo\": [1, {\"bar\": [2, 3]}]}))\n\n      obj.dig?(\"foo\", 0).should eq(1)\n      obj.dig?(\"foo\", 1, \"bar\", 1).should eq(3)\n    end\n\n    it \"returns nil if not found\" do\n      obj = JSON.parse(%({\"foo\": [1, {\"bar\": [2, 3]}]}))\n\n      obj.dig?(\"foo\", 10).should be_nil\n      obj.dig?(\"bar\", \"baz\").should be_nil\n      obj.dig?(\"\").should be_nil\n    end\n\n    it \"returns nil for non-Hash/Array intermediary values\" do\n      JSON::Any.new(nil).dig?(\"foo\").should be_nil\n      JSON::Any.new(0.0).dig?(\"foo\").should be_nil\n    end\n  end\n\n  describe \"dig\" do\n    it \"gets the value at given path given splat\" do\n      obj = JSON.parse(%({\"foo\": [1, {\"bar\": [2, 3]}]}))\n\n      obj.dig(\"foo\", 0).should eq(1)\n      obj.dig(\"foo\", 1, \"bar\", 1).should eq(3)\n    end\n\n    it \"raises if not found\" do\n      obj = JSON.parse(%({\"foo\": [1, {\"bar\": [2, 3]}]}))\n\n      expect_raises Exception, %(Expected Hash for #[](key : String), not Array(JSON::Any)) do\n        obj.dig(\"foo\", 1, \"bar\", \"baz\")\n      end\n      expect_raises KeyError, %(Missing hash key: \"z\") do\n        obj.dig(\"z\")\n      end\n      expect_raises KeyError, %(Missing hash key: \"\") do\n        obj.dig(\"\")\n      end\n    end\n  end\n\n  it \"traverses big structure\" do\n    obj = JSON.parse(%({\"foo\": [1, {\"bar\": [2, 3]}]}))\n    obj[\"foo\"][1][\"bar\"][1].as_i.should eq(3)\n  end\n\n  it \"compares to other objects\" do\n    obj = JSON.parse(%([1, 2]))\n    obj.should eq([1, 2])\n    obj[0].should eq(1)\n  end\n\n  it \"can compare with ===\" do\n    (1 === JSON.parse(\"1\")).should be_truthy\n  end\n\n  it \"exposes $~ when doing Regex#===\" do\n    (/o+/ === JSON.parse(%(\"foo\"))).should be_truthy\n    $~[0].should eq(\"oo\")\n  end\n\n  it \"dups\" do\n    any = JSON.parse(\"[1, 2, 3]\")\n    any2 = any.dup\n    any2.as_a.should_not be(any.as_a)\n  end\n\n  it \"clones\" do\n    any = JSON.parse(\"[[1], 2, 3]\")\n    any2 = any.clone\n    any2.as_a[0].as_a.should_not be(any.as_a[0].as_a)\n  end\n\n  it \"#to_yaml\" do\n    any = JSON.parse <<-JSON\n      {\n        \"foo\": \"bar\",\n        \"baz\": [1, 2.3, true, \"qux\", {\"qax\": \"qox\"}]\n      }\n      JSON\n    any.to_yaml.should eq <<-YAML\n      ---\n      foo: bar\n      baz:\n      - 1\n      - 2.3\n      - true\n      - qux\n      - qax: qox\n\n      YAML\n  end\n\n  it \"#inspect\" do\n    any = JSON.parse <<-JSON\n      {\n        \"foo\": \"bar\",\n        \"baz\": [1, 2.3, true, \"qux\", {\"qax\": \"qox\"}]\n      }\n    JSON\n\n    any.inspect.should eq %(JSON::Any({\"foo\" => \"bar\", \"baz\" => [1, 2.3, true, \"qux\", {\"qax\" => \"qox\"}]}))\n\n    # call multiple times to ensure state is reset properly\n    JSON.parse(\"null\").inspect.should eq \"JSON::Any(nil)\"\n    JSON.parse(\"true\").inspect.should eq \"JSON::Any(true)\"\n  end\nend\n"
  },
  {
    "path": "spec/std/json/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"json\"\nrequire \"spec/helpers/string\"\n\nprivate def assert_built(expected, *, file = __FILE__, line = __LINE__, &)\n  assert_prints JSON.build { |json| with json yield json }, expected, file: file, line: line\nend\n\nprivate class TestObject\n  def to_json(builder)\n    {\"int\" => 12}.to_json(builder)\n  end\nend\n\ndescribe JSON::Builder do\n  it \"writes null\" do\n    assert_built(\"null\") do\n      null\n    end\n  end\n\n  it \"writes bool\" do\n    assert_built(\"true\") do\n      bool(true)\n    end\n  end\n\n  it \"writes integer\" do\n    assert_built(\"123\") do\n      number(123)\n    end\n  end\n\n  it \"writes float\" do\n    assert_built(\"123.45\") do\n      number(123.45)\n    end\n  end\n\n  it \"errors on nan\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    json.start_document\n    expect_raises JSON::Error, \"NaN not allowed in JSON\" do\n      json.number(0.0/0.0)\n    end\n  end\n\n  it \"errors on infinity\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    json.start_document\n    expect_raises JSON::Error, \"Infinity not allowed in JSON\" do\n      json.number(1.0/0.0)\n    end\n  end\n\n  it \"writes string\" do\n    assert_built(%<\"hello\">) do\n      string(\"hello\")\n    end\n  end\n\n  it \"writes string with controls and slashes \" do\n    assert_built(\"\\\" \\\\\\\" \\\\\\\\ \\\\b \\\\f \\\\n \\\\r \\\\t \\\\u0019 \\\"\") do\n      string(\" \\\" \\\\ \\b \\f \\n \\r \\t \\u{19} \")\n    end\n  end\n\n  it \"errors if writing before document start\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    expect_raises JSON::Error, \"Write before start_document\" do\n      json.number(1)\n    end\n  end\n\n  it \"errors if writing two scalars\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    json.start_document\n    json.number(1)\n    expect_raises JSON::Error, \"Write past end_document and before start_document\" do\n      json.number(2)\n    end\n  end\n\n  it \"writes array\" do\n    assert_built(%<[1,\"hello\",true]>) do\n      array do\n        number(1)\n        string(\"hello\")\n        bool(true)\n      end\n    end\n  end\n\n  it \"writes nested array\" do\n    assert_built(%<[1,[\"hello\",true],2]>) do\n      array do\n        number(1)\n        array do\n          string(\"hello\")\n          bool(true)\n        end\n        number(2)\n      end\n    end\n  end\n\n  it \"writes object\" do\n    assert_built(%<{\"foo\":1,\"bar\":2}>) do\n      object do\n        string(\"foo\")\n        number(1)\n        string(\"bar\")\n        number(2)\n      end\n    end\n  end\n\n  it \"writes nested object\" do\n    assert_built(%<{\"foo\":{\"bar\":2,\"baz\":3},\"another\":{\"baz\":3}}>) do\n      object do\n        string(\"foo\")\n        object do\n          string(\"bar\")\n          number(2)\n          string(\"baz\")\n          number(3)\n        end\n        string(\"another\")\n        object do\n          string(\"baz\")\n          number(3)\n        end\n      end\n    end\n  end\n\n  it \"writes array with indent level\" do\n    assert_built(%<[\\n  1,\\n  2,\\n  3\\n]>) do |json|\n      json.indent = 2\n      array do\n        number(1)\n        number(2)\n        number(3)\n      end\n    end\n  end\n\n  it \"writes array with indent string\" do\n    assert_built(%<[\\n\\t1,\\n\\t2,\\n\\t3\\n]>) do |json|\n      json.indent = \"\\t\"\n      array do\n        number(1)\n        number(2)\n        number(3)\n      end\n    end\n  end\n\n  it \"writes object with indent level\" do\n    assert_built(%<{\\n  \"foo\": 1,\\n  \"bar\": 2\\n}>) do |json|\n      json.indent = 2\n      object do\n        string \"foo\"\n        number(1)\n        string \"bar\"\n        number(2)\n      end\n    end\n  end\n\n  it \"writes empty array with indent level\" do\n    assert_built(%<[]>) do |json|\n      json.indent = 2\n      array do\n      end\n    end\n  end\n\n  it \"writes empty object with indent level\" do\n    assert_built(%<{}>) do |json|\n      json.indent = 2\n      object do\n      end\n    end\n  end\n\n  it \"writes nested array\" do\n    assert_built(%<[\\n  []\\n]>) do |json|\n      json.indent = 2\n      array do\n        array do\n        end\n      end\n    end\n  end\n\n  it \"writes object with scalar and indent\" do\n    assert_built(%<{\\n  \"foo\": 1\\n}>) do |json|\n      json.indent = 2\n      object do\n        string \"foo\"\n        number 1\n      end\n    end\n  end\n\n  it \"writes object with array and scalar and indent\" do\n    assert_built(%<{\\n  \"foo\": [\\n    1\\n  ]\\n}>) do |json|\n      json.indent = 2\n      object do\n        string \"foo\"\n        array do\n          number 1\n        end\n      end\n    end\n  end\n\n  it \"writes raw\" do\n    assert_built(%<{\\n  \"foo\": [1, 2, 3],\\n  \"bar\": [\\n    [4, 5, 6]\\n  ]\\n}>) do |json|\n      json.indent = 2\n      object do\n        string \"foo\"\n        raw \"[1, 2, 3]\"\n        string \"bar\"\n        array do\n          raw \"[4, 5, 6]\"\n        end\n      end\n    end\n  end\n\n  it \"raises if nothing written\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    json.start_document\n    expect_raises JSON::Error, \"Empty JSON\" do\n      json.end_document\n    end\n  end\n\n  it \"raises if array is left open\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    json.start_document\n    json.start_array\n    expect_raises JSON::Error, \"Unterminated JSON array\" do\n      json.end_document\n    end\n  end\n\n  it \"raises if object is left open\" do\n    json = JSON::Builder.new(IO::Memory.new)\n    json.start_document\n    json.start_object\n    expect_raises JSON::Error, \"Unterminated JSON object\" do\n      json.end_document\n    end\n  end\n\n  it \"writes field with scalar in object\" do\n    assert_built(%<{\"int\":42,\"float\":0.815,\"null\":null,\"bool\":true,\"string\":\"string\"}>) do\n      object do\n        field \"int\", 42\n        field \"float\", 0.815\n        field \"null\", nil\n        field \"bool\", true\n        field \"string\", \"string\"\n      end\n    end\n  end\n\n  it \"writes field with arbitrary value in object\" do\n    assert_built(%<{\"hash\":{\"hash\":\"value\"},\"object\":{\"int\":12}}>) do\n      object do\n        field \"hash\", {\"hash\" => \"value\"}\n        field \"object\", TestObject.new\n      end\n    end\n  end\n\n  it \"errors on max nesting (array)\" do\n    io = IO::Memory.new\n    builder = JSON::Builder.new(io)\n    builder.max_nesting = 3\n    builder.start_document\n    3.times do\n      builder.start_array\n    end\n\n    expect_raises(JSON::Error, \"Nesting of 4 is too deep\") do\n      builder.start_array\n    end\n  end\n\n  it \"errors on max nesting (object)\" do\n    io = IO::Memory.new\n    builder = JSON::Builder.new(io)\n    builder.max_nesting = 3\n    builder.start_document\n    3.times do\n      builder.start_object\n      builder.string \"key\"\n    end\n\n    expect_raises(JSON::Error, \"Nesting of 4 is too deep\") do\n      builder.start_object\n    end\n  end\n\n  it \"#next_is_object_key?\" do\n    io = IO::Memory.new\n    builder = JSON::Builder.new(io)\n    builder.next_is_object_key?.should be_false\n    builder.start_document\n    builder.next_is_object_key?.should be_false\n    builder.start_object\n    builder.next_is_object_key?.should be_true\n    builder.scalar(\"foo\")\n    builder.next_is_object_key?.should be_false\n    builder.scalar(\"bar\")\n    builder.next_is_object_key?.should be_true\n  end\nend\n"
  },
  {
    "path": "spec/std/json/lexer_spec.cr",
    "content": "require \"spec\"\nrequire \"json\"\n\nprivate def it_lexes(string, expected_kind : JSON::Token::Kind, expected_to_s = string, file = __FILE__, line = __LINE__)\n  it \"lexes #{string} from string\", file, line do\n    lexer = JSON::Lexer.new string\n    token = lexer.next_token\n    token.kind.should eq(expected_kind)\n    token.to_s.should eq(expected_to_s)\n  end\n\n  it \"lexes #{string} from IO\", file, line do\n    lexer = JSON::Lexer.new IO::Memory.new(string)\n    token = lexer.next_token\n    token.kind.should eq(expected_kind)\n    token.to_s.should eq(expected_to_s)\n  end\nend\n\nprivate def it_lexes_string(string, string_value, file = __FILE__, line = __LINE__)\n  it \"lexes #{string} from String\", file, line do\n    lexer = JSON::Lexer.new string\n    token = lexer.next_token\n    token.kind.should eq(JSON::Token::Kind::String)\n    token.string_value.should eq(string_value)\n    token.to_s.should eq(token.string_value)\n  end\n\n  it \"lexes #{string} from IO\", file, line do\n    lexer = JSON::Lexer.new IO::Memory.new(string)\n    token = lexer.next_token\n    token.kind.should eq(JSON::Token::Kind::String)\n    token.string_value.should eq(string_value)\n    token.to_s.should eq(token.string_value)\n  end\nend\n\nprivate def it_lexes_int(string, int_value, file = __FILE__, line = __LINE__)\n  it \"lexes #{string} from String\", file, line do\n    lexer = JSON::Lexer.new string\n    token = lexer.next_token\n    token.kind.should eq(JSON::Token::Kind::Int)\n    token.int_value.should eq(int_value)\n    token.raw_value.should eq(string)\n    token.to_s.should eq(token.raw_value)\n  end\n\n  it \"lexes #{string} from IO\", file, line do\n    lexer = JSON::Lexer.new IO::Memory.new(string)\n    token = lexer.next_token\n    token.kind.should eq(JSON::Token::Kind::Int)\n    token.int_value.should eq(int_value)\n    token.raw_value.should eq(string)\n    token.to_s.should eq(token.raw_value)\n  end\nend\n\nprivate def it_lexes_float(string, float_value, file = __FILE__, line = __LINE__)\n  it \"lexes #{string} from String\", file, line do\n    lexer = JSON::Lexer.new string\n    token = lexer.next_token\n    token.kind.should eq(JSON::Token::Kind::Float)\n    token.float_value.should eq(float_value)\n    token.raw_value.should eq(string)\n    token.to_s.should eq(token.raw_value)\n  end\n\n  it \"lexes #{string} from IO\", file, line do\n    lexer = JSON::Lexer.new IO::Memory.new(string)\n    token = lexer.next_token\n    token.kind.should eq(JSON::Token::Kind::Float)\n    token.float_value.should eq(float_value)\n    token.raw_value.should eq(string)\n    token.to_s.should eq(token.raw_value)\n  end\nend\n\nprivate def it_errors_to_lex(string, *, file = __FILE__, line = __LINE__)\n  it \"errors if lexing #{string} from String\", file: file, line: line do\n    expect_raises(Exception) { JSON::Lexer.new(string).next_token }\n  end\n\n  it \"errors if lexing #{string} from IO\", file: file, line: line do\n    expect_raises(Exception) { JSON::Lexer.new(IO::Memory.new(string)).next_token }\n  end\nend\n\ndescribe JSON::Lexer do\n  it_lexes \"\", :EOF, expected_to_s: \"<EOF>\"\n  it_lexes \"{\", :begin_object\n  it_lexes \"}\", :end_object\n  it_lexes \"[\", :begin_array\n  it_lexes \"]\", :end_array\n  it_lexes \",\", :comma\n  it_lexes \":\", :colon\n  it_lexes \" \\n\\t\\r :\", :colon, expected_to_s: \":\"\n  it_lexes \"true\", :true\n  it_lexes \"false\", :false\n  it_lexes \"null\", :null\n  it_lexes_string \"\\\"hello\\\"\", \"hello\"\n  it_lexes_string \"\\\"hello\\\\\\\"world\\\"\", \"hello\\\"world\"\n  it_lexes_string \"\\\"hello\\\\\\\\world\\\"\", \"hello\\\\world\"\n  it_lexes_string \"\\\"hello\\\\/world\\\"\", \"hello/world\"\n  it_lexes_string \"\\\"hello\\\\bworld\\\"\", \"hello\\bworld\"\n  it_lexes_string \"\\\"hello\\\\fworld\\\"\", \"hello\\fworld\"\n  it_lexes_string \"\\\"hello\\\\nworld\\\"\", \"hello\\nworld\"\n  it_lexes_string \"\\\"hello\\\\rworld\\\"\", \"hello\\rworld\"\n  it_lexes_string \"\\\"hello\\\\tworld\\\"\", \"hello\\tworld\"\n  it_lexes_string \"\\\"\\\\u201chello world\\\\u201d\\\"\", \"“hello world”\"\n  it_lexes_string \"\\\"\\\\uD800\\\\uDC00\\\"\", 0x10000.unsafe_chr.to_s\n  it_lexes_string \"\\\"\\\\uD840\\\\uDC00\\\"\", 0x20000.unsafe_chr.to_s\n  it_lexes_string \"\\\"\\\\uDBFF\\\\uDFFF\\\"\", 0x10ffff.unsafe_chr.to_s\n  it_lexes_string \"\\\"\\\\uD834\\\\uDD1E\\\"\", \"𝄞\"\n  it_errors_to_lex %(\"\\\\uD800\")\n  it_errors_to_lex %(\"\\\\uDC00\")\n  it_errors_to_lex %(\"\\\\uD800\\\\u0020\")\n  it_lexes_int \"0\", 0\n  it_lexes_int \"1\", 1\n  it_lexes_int \"1234\", 1234\n  it_lexes_float \"0.123\", 0.123\n  it_lexes_float \"1234.567\", 1234.567\n  it_lexes_float \"0e1\", 0\n  it_lexes_float \"0E1\", 0\n  it_lexes_float \"0.1e1\", 0.1e1\n  it_lexes_float \"0e+12\", 0\n  it_lexes_float \"0e-12\", 0\n  it_lexes_float \"1e2\", 1e2\n  it_lexes_float \"1E2\", 1e2\n  it_lexes_float \"1e+12\", 1e12\n  it_lexes_float \"1.2e-3\", 1.2e-3\n  it_lexes_float \"9.91343313498688\", 9.91343313498688\n  it_lexes_int \"-1\", -1\n  it_lexes_float \"-1.23\", -1.23\n  it_lexes_float \"-1.23e4\", -1.23e4\n  it_lexes_float \"-1.23e4\", -1.23e4\n  it_lexes_float \"1000000000000000000.0\", 1000000000000000000.0\n  it_lexes_float \"6000000000000000000.0\", 6000000000000000000.0\n  it_lexes_float \"9000000000000000000.0\", 9000000000000000000.0\n  it_lexes_float \"9876543212345678987654321.0\", 9876543212345678987654321.0\n  it_lexes_float \"9876543212345678987654321e20\", 9876543212345678987654321e20\n  it_lexes_float \"10.100000000000000000000\", 10.1\nend\n"
  },
  {
    "path": "spec/std/json/parser_spec.cr",
    "content": "require \"spec\"\nrequire \"json\"\n\nprivate def it_parses(string, expected_value, file = __FILE__, line = __LINE__)\n  it \"parses #{string}\", file, line do\n    JSON.parse(string).raw.should eq(expected_value)\n  end\nend\n\nprivate def it_raises_on_parse(string, file = __FILE__, line = __LINE__)\n  it \"raises on parse #{string}\", file, line do\n    expect_raises JSON::ParseException do\n      JSON.parse(string)\n    end\n  end\nend\n\ndescribe JSON::Parser do\n  it_parses \"1\", 1\n  it_parses \"2.5\", 2.5\n  it_parses %(\"hello\"), \"hello\"\n  it_parses \"true\", true\n  it_parses \"false\", false\n  it_parses \"null\", nil\n  it_parses %(\"\\\\nПривет, мир!\"), \"\\nПривет, мир!\"\n\n  it_parses \"[]\", [] of Int32\n  it_parses \"[1]\", [1]\n  it_parses \"[1, 2, 3]\", [1, 2, 3]\n  it_parses \"[1.5]\", [1.5]\n  it_parses \"[null]\", [nil]\n  it_parses \"[true]\", [true]\n  it_parses \"[false]\", [false]\n  it_parses %([\"hello\"]), [\"hello\"]\n  it_parses \"[0]\", [0]\n  it_parses \" [ 0 ] \", [0]\n\n  it_parses \"{}\", {} of String => JSON::Any\n  it_parses %({\"foo\": 1}), {\"foo\" => 1}\n  it_parses %({\"foo\": 1, \"bar\": 1.5}), {\"foo\" => 1, \"bar\" => 1.5}\n  it_parses %({\"fo\\\\no\": 1}), {\"fo\\no\" => 1}\n\n  it_parses \"[[1]]\", [[1]]\n  it_parses %([{\"foo\": 1}]), [{\"foo\" => 1}]\n\n  it_parses \"[\\\"日\\\"]\", [\"日\"]\n\n  it_raises_on_parse \"[1,]\"\n  it_raises_on_parse %({\"foo\": 1,})\n  it_raises_on_parse \"{1}\"\n  it_raises_on_parse %({\"foo\"1})\n  it_raises_on_parse %(\"{\"foo\":})\n  it_raises_on_parse \"[0]1\"\n  it_raises_on_parse \"[0] 1 \"\n  it_raises_on_parse \"[\\\"\\\\u123z\\\"]\"\n  it_raises_on_parse \"[1 true]\"\n  it_raises_on_parse %({\"foo\": 1 \"bar\": 2})\n  it_raises_on_parse %([2.])\n  it_raises_on_parse %(\"hello\\nworld\")\n  it_raises_on_parse %(\"\\\\u201cello\\nworld\")\n  it_raises_on_parse %(\"hello\\tworld\")\n  it_raises_on_parse %(\"\\\\u201cello\\tworld\")\n\n  it_raises_on_parse \"1\\u{0}\"\n\n  it \"prevents stack overflow for arrays\" do\n    expect_raises JSON::ParseException, \"Nesting of 513 is too deep\" do\n      JSON.parse((\"[\" * 513) + (\"]\" * 513))\n    end\n  end\n\n  it \"prevents stack overflow for hashes\" do\n    expect_raises JSON::ParseException, \"Nesting of 513 is too deep\" do\n      JSON.parse((%({\"x\": ) * 513) + (\"}\" * 513))\n    end\n  end\n\n  it \"returns raw\" do\n    value = JSON.parse(\"1\").raw\n    value.should eq(1)\n    value.should be_a(Int64)\n  end\nend\n"
  },
  {
    "path": "spec/std/json/pull_parser_spec.cr",
    "content": "require \"spec\"\nrequire \"json\"\n\nclass JSON::PullParser\n  def assert(event_kind : Kind)\n    kind.should eq(event_kind)\n    read_next\n  end\n\n  def assert(value : Nil)\n    kind.should eq(JSON::PullParser::Kind::Null)\n    read_next\n  end\n\n  def assert(value : Int)\n    kind.should eq(JSON::PullParser::Kind::Int)\n    int_value.should eq(value)\n    read_next\n  end\n\n  def assert(value : Float)\n    kind.should eq(JSON::PullParser::Kind::Float)\n    float_value.should eq(value)\n    read_next\n  end\n\n  def assert(value : Bool)\n    kind.should eq(JSON::PullParser::Kind::Bool)\n    bool_value.should eq(value)\n    read_next\n  end\n\n  def assert(value : String)\n    kind.should eq(JSON::PullParser::Kind::String)\n    string_value.should eq(value)\n    read_next\n  end\n\n  def assert(value : String, &)\n    kind.should eq(JSON::PullParser::Kind::String)\n    string_value.should eq(value)\n    read_next\n    yield\n  end\n\n  def assert(array : Array)\n    assert_array do\n      array.each do |x|\n        assert x.raw\n      end\n    end\n  end\n\n  def assert(hash : Hash)\n    assert_object do\n      hash.each do |key, value|\n        assert(key) do\n          assert value.raw\n        end\n      end\n    end\n  end\n\n  def assert_array(&)\n    kind.should eq(JSON::PullParser::Kind::BeginArray)\n    read_next\n    yield\n    kind.should eq(JSON::PullParser::Kind::EndArray)\n    read_next\n  end\n\n  def assert_array\n    assert_array { }\n  end\n\n  def assert_object(&)\n    kind.should eq(JSON::PullParser::Kind::BeginObject)\n    read_next\n    yield\n    kind.should eq(JSON::PullParser::Kind::EndObject)\n    read_next\n  end\n\n  def assert_object\n    assert_object { }\n  end\n\n  def assert_error\n    expect_raises JSON::ParseException do\n      read_next\n    end\n  end\nend\n\nprivate def assert_pull_parse(string)\n  it \"parses #{string}\" do\n    parser = JSON::PullParser.new string\n    parser.assert JSON.parse(string).raw\n    parser.kind.should eq(JSON::PullParser::Kind::EOF)\n  end\nend\n\nprivate def assert_pull_parse_error(string)\n  it \"errors on #{string}\" do\n    expect_raises JSON::ParseException do\n      parser = JSON::PullParser.new string\n      until parser.kind.eof?\n        parser.read_next\n      end\n    end\n  end\nend\n\nprivate def assert_raw(string, file = __FILE__, line = __LINE__)\n  it \"parses raw #{string.inspect}\", file, line do\n    pull = JSON::PullParser.new(string)\n    pull.read_raw.should eq(string)\n  end\nend\n\nprivate def it_reads(value, file = __FILE__, line = __LINE__)\n  type = value.class\n  it \"reads #{type}: #{value.to_json}\", file: file, line: line do\n    pull = JSON::PullParser.new(value.to_json)\n    pull.read?(type).should eq(value)\n  end\nend\n\ndescribe JSON::PullParser do\n  assert_pull_parse \"null\"\n  assert_pull_parse \"false\"\n  assert_pull_parse \"true\"\n  assert_pull_parse \"1\"\n  assert_pull_parse \"1.5\"\n  assert_pull_parse %(\"hello\")\n  assert_pull_parse \"[]\"\n  assert_pull_parse \"[[]]\"\n  assert_pull_parse \"[1]\"\n  assert_pull_parse \"[1.5]\"\n  assert_pull_parse \"[null]\"\n  assert_pull_parse \"[true]\"\n  assert_pull_parse \"[false]\"\n  assert_pull_parse %([\"hello\"])\n  assert_pull_parse \"[1, 2]\"\n  assert_pull_parse \"{}\"\n  assert_pull_parse %({\"foo\": 1})\n  assert_pull_parse %({\"foo\": \"bar\"})\n  assert_pull_parse %({\"foo\": [1, 2]})\n  assert_pull_parse %({\"foo\": 1, \"bar\": 2})\n  assert_pull_parse %({\"foo\": \"foo1\", \"bar\": \"bar1\"})\n\n  assert_pull_parse_error \"[null 2]\"\n  assert_pull_parse_error \"[false 2]\"\n  assert_pull_parse_error \"[true 2]\"\n  assert_pull_parse_error \"[1 2]\"\n  assert_pull_parse_error \"[1.5 2]\"\n  assert_pull_parse_error %([\"hello\" 2])\n  assert_pull_parse_error \"[,1]\"\n  assert_pull_parse_error \"[}]\"\n  assert_pull_parse_error \"[\"\n  assert_pull_parse_error %({,\"foo\": 1})\n  assert_pull_parse_error \"[]]\"\n  assert_pull_parse_error \"{}}\"\n  assert_pull_parse_error %({\"foo\",1})\n  assert_pull_parse_error %({\"foo\"::1})\n  assert_pull_parse_error %([\"foo\":1])\n  assert_pull_parse_error %({\"foo\": []:1})\n  assert_pull_parse_error \"[[]\"\n  assert_pull_parse_error %({\"foo\": {})\n  assert_pull_parse_error %({\"name\": \"John\", \"age\", 1})\n  assert_pull_parse_error %({\"name\": \"John\", \"age\": \"foo\", \"bar\"})\n\n  it \"parses when the input IO is already empty\" do\n    JSON::PullParser.new(IO::Memory.new).kind.should eq JSON::PullParser::Kind::EOF\n  end\n\n  it \"prevents stack overflow for arrays\" do\n    parser = JSON::PullParser.new((\"[\" * 513) + (\"]\" * 513))\n    expect_raises JSON::ParseException, \"Nesting of 513 is too deep\" do\n      while true\n        break if parser.kind.eof?\n        parser.read_next\n      end\n    end\n  end\n\n  it \"prevents stack overflow for hashes\" do\n    parser = JSON::PullParser.new((%({\"x\": ) * 513) + (\"}\" * 513))\n    expect_raises JSON::ParseException, \"Nesting of 513 is too deep\" do\n      while true\n        break if parser.kind.eof?\n        parser.read_next\n      end\n    end\n  end\n\n  # Prevent too deep nesting (prevents stack overflow)\n  assert_pull_parse_error((\"[\" * 513) + (\"]\" * 513))\n  assert_pull_parse_error((\"{\" * 513) + (\"}\" * 513))\n\n  describe \"skip\" do\n    [\n      {\"null\", \"null\"},\n      {\"bool\", \"false\"},\n      {\"int\", \"3\"},\n      {\"float\", \"3.5\"},\n      {\"string\", %(\"hello\")},\n      {\"array\", %([10, 20, [30], [40]])},\n      {\"object\", %({\"foo\": [1, 2], \"bar\": {\"baz\": [3]}})},\n    ].each do |(desc, obj)|\n      it \"skips #{desc}\" do\n        pull = JSON::PullParser.new(\"[1, #{obj}, 2]\")\n        pull.read_array do\n          pull.read_int.should eq(1)\n          pull.skip\n          pull.read_int.should eq(2)\n        end\n      end\n    end\n  end\n\n  it \"reads bool or null\" do\n    JSON::PullParser.new(\"null\").read_bool_or_null.should be_nil\n    JSON::PullParser.new(\"false\").read_bool_or_null.should be_false\n  end\n\n  it \"reads int or null\" do\n    JSON::PullParser.new(\"null\").read_int_or_null.should be_nil\n    JSON::PullParser.new(\"1\").read_int_or_null.should eq(1)\n  end\n\n  it \"reads float or null\" do\n    JSON::PullParser.new(\"null\").read_float_or_null.should be_nil\n    JSON::PullParser.new(\"1.5\").read_float_or_null.should eq(1.5)\n  end\n\n  it \"reads string or null\" do\n    JSON::PullParser.new(\"null\").read_string_or_null.should be_nil\n    JSON::PullParser.new(%(\"hello\")).read_string_or_null.should eq(\"hello\")\n  end\n\n  it \"reads array or null\" do\n    JSON::PullParser.new(\"null\").read_array_or_null { fail \"expected block not to be called\" }\n\n    pull = JSON::PullParser.new(%([1]))\n    pull.read_array_or_null do\n      pull.read_int.should eq(1)\n    end\n  end\n\n  it \"reads object or null\" do\n    JSON::PullParser.new(\"null\").read_object_or_null { fail \"expected block not to be called\" }\n\n    pull = JSON::PullParser.new(%({\"foo\": 1}))\n    pull.read_object_or_null do |key|\n      key.should eq(\"foo\")\n      pull.read_int.should eq(1)\n    end\n  end\n\n  describe \"on key\" do\n    it \"finds key\" do\n      pull = JSON::PullParser.new(%({\"foo\": 1, \"bar\": 2}))\n\n      bar = nil\n      pull.on_key(\"bar\") do\n        bar = pull.read_int\n      end\n\n      bar.should eq(2)\n    end\n\n    it \"yields parser\" do\n      pull = JSON::PullParser.new(%({\"foo\": 1, \"bar\": 2}))\n\n      pull.on_key(\"bar\", &.read_int).should eq(2)\n    end\n\n    it \"doesn't find key\" do\n      pull = JSON::PullParser.new(%({\"foo\": 1, \"baz\": 2}))\n\n      bar = nil\n      pull.on_key(\"bar\") do\n        bar = pull.read_int\n      end\n\n      bar.should be_nil\n    end\n\n    it \"finds key with bang\" do\n      pull = JSON::PullParser.new(%({\"foo\": 1, \"bar\": 2}))\n\n      bar = nil\n      pull.on_key!(\"bar\") do\n        bar = pull.read_int\n      end\n\n      bar.should eq(2)\n    end\n\n    it \"yields parser with bang\" do\n      pull = JSON::PullParser.new(%({\"foo\": 1, \"bar\": 2}))\n\n      pull.on_key!(\"bar\", &.read_int).should eq(2)\n    end\n\n    it \"doesn't find key with bang\" do\n      pull = JSON::PullParser.new(%({\"foo\": 1, \"baz\": 2}))\n\n      expect_raises Exception, \"JSON key not found: bar\" do\n        pull.on_key!(\"bar\") do\n        end\n      end\n    end\n\n    it \"reads float when it is an int\" do\n      pull = JSON::PullParser.new(%(1))\n      f = pull.read_float\n      f.should be_a(Float64)\n      f.should eq(1.0)\n    end\n\n    [\"1\", \"[1]\", %({\"x\": [1]})].each do |value|\n      it \"yields all keys when skipping #{value}\" do\n        pull = JSON::PullParser.new(%({\"foo\": #{value}, \"bar\": 2}))\n        pull.read_object do |key|\n          key.should_not eq(\"\")\n          pull.skip\n        end\n      end\n    end\n  end\n\n  describe \"raw\" do\n    assert_raw \"null\"\n    assert_raw \"true\"\n    assert_raw \"false\"\n    assert_raw \"1234\"\n    assert_raw \"1234.5678\"\n    assert_raw %(\"hello\")\n    assert_raw %([1,\"hello\",true,false,null,[1,2,3]])\n    assert_raw %({\"foo\":[1,2,{\"bar\":[1,\"hello\",true,false,1.5]}]})\n    assert_raw %({\"foo\":\"bar\"})\n  end\n\n  describe \"#read?\" do\n    {% for pair in [[Int8, 1_i8],\n                    [Int16, 1_i16],\n                    [Int32, 1_i32],\n                    [Int64, 1_i64],\n                    [Int128, \"Int128.new(1)\".id],\n                    [UInt8, 1_u8],\n                    [UInt16, 1_u16],\n                    [UInt32, 1_u32],\n                    [UInt64, 1_u64],\n                    [UInt128, \"UInt128.new(1)\".id],\n                    [Float32, 1.0_f32],\n                    [Float64, 1.0],\n                    [String, \"foo\"],\n                    [Bool, true]] %}\n      {% type = pair[0] %}\n      {% value = pair[1] %}\n\n      it \"reads {{type}} when the token is a compatible kind\" do\n        pull = JSON::PullParser.new({{value}}.to_json)\n        pull.read?({{type}}).should eq({{value}})\n      end\n\n      it \"returns nil instead of {{type}} when the token is not compatible\" do\n        pull = JSON::PullParser.new(%({\"foo\": \"bar\"}))\n        pull.read?({{type}}).should be_nil\n      end\n    {% end %}\n\n    {% for num in Int::Primitive.union_types %}\n      it_reads {{ num }}::MIN\n      {% unless num < Int::Unsigned %}\n        it_reads {{ num }}.new(-10)\n        it_reads {{ num }}.zero\n      {% end %}\n      it_reads {{ num }}.new(10)\n      it_reads {{ num }}::MAX\n    {% end %}\n\n    {% for i in [8, 16, 32, 64, 128] %}\n      it \"returns nil in place of Int{{i}} when an overflow occurs\" do\n        JSON::PullParser.new(Int{{i}}::MAX.to_s + \"0\").read?(Int{{i}}).should be_nil\n        JSON::PullParser.new(Int{{i}}::MIN.to_s + \"0\").read?(Int{{i}}).should be_nil\n      end\n\n      it \"returns nil in place of UInt{{i}} when an overflow occurs\" do\n        JSON::PullParser.new(UInt{{i}}::MAX.to_s + \"0\").read?(UInt{{i}}).should be_nil\n        JSON::PullParser.new(\"-1\").read?(UInt{{i}}).should be_nil\n      end\n    {% end %}\n\n    it \"reads > Float32::MAX\" do\n      pull = JSON::PullParser.new(Float64::MAX.to_s)\n      pull.read?(Float32).should be_nil\n    end\n\n    it \"reads < Float32::MIN\" do\n      pull = JSON::PullParser.new(Float64::MIN.to_s)\n      pull.read?(Float32).should be_nil\n    end\n\n    it \"reads > Float64::MAX\" do\n      pull = JSON::PullParser.new(\"1\" + Float64::MAX.to_s)\n      pull.read?(Float64).should be_nil\n    end\n\n    it \"reads < Float64::MIN\" do\n      pull = JSON::PullParser.new(\"-1\" + Float64::MAX.to_s)\n      pull.read?(Float64).should be_nil\n    end\n\n    it \"doesn't accept nan or infinity\" do\n      pull = JSON::PullParser.new(%(\"nan\"))\n      pull.read?(Float64).should be_nil\n\n      pull = JSON::PullParser.new(%(\"infinity\"))\n      pull.read?(Float64).should be_nil\n\n      pull = JSON::PullParser.new(%(\"+infinity\"))\n      pull.read?(Float64).should be_nil\n\n      pull = JSON::PullParser.new(%(\"-infinity\"))\n      pull.read?(Float64).should be_nil\n    end\n  end\n\n  it \"#raise\" do\n    pull = JSON::PullParser.new(\"[1, 2, 3]\")\n    expect_raises(JSON::ParseException, \"foo bar at line 1, column 2\") do\n      pull.raise \"foo bar\"\n    end\n    pull.read_begin_array\n    expect_raises(JSON::ParseException, \"foo bar at line 1, column 3\") do\n      pull.raise \"foo bar\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/json/serializable_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"json\"\nrequire \"yaml\"\nrequire \"big\"\nrequire \"big/json\"\nrequire \"uuid\"\nrequire \"uuid/json\"\n\nenum JSONSerializableEnum\n  Zero\n  One\n  Two\n  OneHundred\nend\n\n@[Flags]\nenum JSONSerializableFlagEnum\n  One\n  Two\n  OneHundred\nend\n\nclass JSONAttrValue(T)\n  include JSON::Serializable\n\n  property value : T\nend\n\nrecord JSONAttrPoint, x : Int32, y : Int32 do\n  include JSON::Serializable\nend\n\nclass JSONAttrEmptyClass\n  include JSON::Serializable\n\n  def initialize; end\nend\n\nclass JSONAttrEmptyClassWithUnmapped\n  include JSON::Serializable\n  include JSON::Serializable::Unmapped\n\n  def initialize; end\nend\n\nclass JSONAttrPerson\n  include JSON::Serializable\n\n  property name : String\n  property age : Int32?\n\n  def_equals name, age\n\n  def initialize(@name : String)\n  end\nend\n\nstruct JSONAttrPersonWithTwoFieldInInitialize\n  include JSON::Serializable\n\n  property name : String\n  property age : Int32\n\n  def initialize(@name, @age)\n  end\nend\n\nclass StrictJSONAttrPerson\n  include JSON::Serializable\n  include JSON::Serializable::Strict\n\n  property name : String\n  property age : Int32?\nend\n\nclass JSONAttrPersonExtraFields\n  include JSON::Serializable\n  include JSON::Serializable::Unmapped\n\n  property name : String\n  property age : Int32?\nend\n\nclass JSONAttrPersonEmittingNull\n  include JSON::Serializable\n\n  property name : String\n\n  @[JSON::Field(emit_null: true)]\n  property age : Int32?\nend\n\n@[JSON::Serializable::Options(emit_nulls: true)]\nclass JSONAttrPersonEmittingNullsByOptions\n  include JSON::Serializable\n\n  property name : String\n  property age : Int32?\n  property value1 : Int32?\n\n  @[JSON::Field(emit_null: false)]\n  property value2 : Int32?\nend\n\nclass JSONAttrWithTime\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::Format.new(\"%F %T\"))]\n  property value : Time\nend\n\nclass JSONAttrWithNilableTime\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::Format.new(\"%F\"))]\n  property value : Time?\n\n  def initialize\n  end\nend\n\nclass JSONAttrWithNilableTimeEmittingNull\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::Format.new(\"%F\"), emit_null: true)]\n  property value : Time?\n\n  def initialize\n  end\nend\n\nclass JSONAttrWithTimeArray1\n  include JSON::Serializable\n\n  @[JSON::Field(converter: JSON::ArrayConverter(Time::EpochConverter))]\n  property value : Array(Time)\nend\n\nclass JSONAttrWithTimeArray2\n  include JSON::Serializable\n\n  @[JSON::Field(converter: JSON::ArrayConverter.new(Time::EpochConverter))]\n  property value : Array(Time)\nend\n\nclass JSONAttrWithTimeArray3\n  include JSON::Serializable\n\n  @[JSON::Field(converter: JSON::ArrayConverter.new(Time::Format.new(\"%F %T\")))]\n  property value : Array(Time)\nend\n\nclass JSONAttrWithTimeHash1\n  include JSON::Serializable\n\n  @[JSON::Field(converter: JSON::HashValueConverter(Time::EpochConverter))]\n  property value : Hash(String, Time)\nend\n\nclass JSONAttrWithTimeHash2\n  include JSON::Serializable\n\n  @[JSON::Field(converter: JSON::HashValueConverter.new(Time::EpochConverter))]\n  property value : Hash(String, Time)\nend\n\nclass JSONAttrWithTimeHash3\n  include JSON::Serializable\n\n  @[JSON::Field(converter: JSON::HashValueConverter.new(Time::Format.new(\"%F %T\")))]\n  property value : Hash(String, Time)\nend\n\nclass JSONAttrWithSimpleMapping\n  include JSON::Serializable\n\n  property name : String\n  property age : Int32\nend\n\nclass JSONAttrWithKeywordsMapping\n  include JSON::Serializable\n\n  property end : Int32\n  property abstract : Int32\nend\n\nclass JSONAttrWithAny\n  include JSON::Serializable\n\n  property name : String\n  property any : JSON::Any\nend\n\nclass JSONAttrWithProblematicKeys\n  include JSON::Serializable\n\n  property key : Int32\n  property pull : Int32\nend\n\nclass JSONAttrWithDefaults\n  include JSON::Serializable\n\n  property a = 11\n  property b = \"Haha\"\n  property c = true\n  property d = false\n  property e : Bool? = false\n  property f : Int32? = 1\n  property g : Int32?\n  property h = [1, 2, 3]\nend\n\nclass JSONAttrWithSmallIntegers\n  include JSON::Serializable\n\n  property foo : Int16\n  property bar : Int8\nend\n\nclass JSONAttrWithTimeEpoch\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::EpochConverter)]\n  property value : Time\nend\n\nclass JSONAttrNilableWithTimeEpoch\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::EpochConverter)]\n  property value : Time?\nend\n\nclass JSONAttrDefaultWithTimeEpoch\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::EpochConverter)]\n  property value : Time = Time.unix(0)\nend\n\nclass JSONAttrWithTimeEpochMillis\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Time::EpochMillisConverter)]\n  property value : Time\nend\n\nclass JSONAttrWithRaw\n  include JSON::Serializable\n\n  @[JSON::Field(converter: String::RawConverter)]\n  property value : String\nend\n\nclass JSONAttrWithRoot\n  include JSON::Serializable\n\n  @[JSON::Field(root: \"heroes\")]\n  property result : Array(JSONAttrPerson)\nend\n\nclass JSONAttrWithNilableRoot\n  include JSON::Serializable\n\n  @[JSON::Field(root: \"heroes\")]\n  property result : Array(JSONAttrPerson)?\nend\n\nclass JSONAttrWithNilableRootEmitNull\n  include JSON::Serializable\n\n  @[JSON::Field(root: \"heroes\", emit_null: true)]\n  property result : Array(JSONAttrPerson)?\nend\n\nclass JSONAttrWithPresence\n  include JSON::Serializable\n\n  @[JSON::Field(presence: true)]\n  property first_name : String?\n\n  @[JSON::Field(presence: true)]\n  property last_name : String?\n\n  @[JSON::Field(ignore: true)]\n  getter? first_name_present : Bool\n\n  @[JSON::Field(ignore: true)]\n  getter? last_name_present : Bool\nend\n\nclass JSONAttrWithPresenceAndIgnoreSerialize\n  include JSON::Serializable\n\n  @[JSON::Field(presence: true, ignore_serialize: ignore_first_name?)]\n  property first_name : String?\n\n  @[JSON::Field(presence: true, ignore_serialize: last_name.nil? && !last_name_present?, emit_null: true)]\n  property last_name : String?\n\n  @[JSON::Field(ignore: true)]\n  getter? first_name_present : Bool = false\n\n  @[JSON::Field(ignore: true)]\n  getter? last_name_present : Bool = false\n\n  def initialize(@first_name : String? = nil, @last_name : String? = nil)\n  end\n\n  def ignore_first_name?\n    first_name.nil? || first_name == \"\"\n  end\nend\n\nclass JSONAttrWithQueryAttributes\n  include JSON::Serializable\n\n  property? foo : Bool\n\n  @[JSON::Field(key: \"is_bar\", presence: true)]\n  property? bar : Bool = false\n\n  @[JSON::Field(ignore: true)]\n  getter? bar_present : Bool\nend\n\nclass JSONAttrWithKeyQueryAttribute\n  include JSON::Serializable\n\n  @[JSON::Field(key: \"is_foo\")]\n  property? foo : Bool\nend\n\nmodule JSONAttrModule\n  property moo : Int32 = 10\nend\n\nclass JSONAttrModuleTest\n  include JSONAttrModule\n  include JSON::Serializable\n\n  @[JSON::Field(key: \"phoo\")]\n  property foo = 15\n\n  def initialize; end\n\n  def to_tuple\n    {@moo, @foo}\n  end\nend\n\nclass JSONAttrModuleTest2 < JSONAttrModuleTest\n  property bar : Int32\n\n  def initialize(@bar : Int32); end\n\n  def to_tuple\n    {@moo, @foo, @bar}\n  end\nend\n\nstruct JSONAttrPersonWithYAML\n  include JSON::Serializable\n  include YAML::Serializable\n\n  property name : String\n  property age : Int32?\n\n  def initialize(@name : String, @age : Int32? = nil)\n  end\nend\n\nstruct JSONAttrPersonWithYAMLInitializeHook\n  include JSON::Serializable\n  include YAML::Serializable\n\n  property name : String\n  property age : Int32?\n\n  def initialize(@name : String, @age : Int32? = nil)\n    after_initialize\n  end\n\n  @[JSON::Field(ignore: true)]\n  @[YAML::Field(ignore: true)]\n  property msg : String?\n\n  def after_initialize\n    @msg = \"Hello \" + name\n  end\nend\n\nstruct JSONAttrPersonWithSelectiveSerialization\n  include JSON::Serializable\n\n  property name : String\n\n  @[JSON::Field(ignore_serialize: true)]\n  property password : String\n\n  @[JSON::Field(ignore_deserialize: true)]\n  property generated : String = \"generated-internally\"\n\n  def initialize(@name : String, @password : String)\n  end\nend\n\nstruct JSONAttrWithGenericConverter(T)\n  include JSON::Serializable\n\n  @[JSON::Field(converter: T)]\n  property value : Time\nend\n\nabstract class JSONShape\n  include JSON::Serializable\n\n  use_json_discriminator \"type\", {point: JSONPoint, circle: JSONCircle}\n\n  property type : String\nend\n\nclass JSONPoint < JSONShape\n  property x : Int32\n  property y : Int32\nend\n\nclass JSONCircle < JSONShape\n  property x : Int32\n  property y : Int32\n  property radius : Int32\nend\n\nenum JSONVariableDiscriminatorEnumFoo\n  Foo = 4\nend\n\nenum JSONVariableDiscriminatorEnumFoo8 : UInt8\n  Foo = 1_8\nend\n\nclass JSONVariableDiscriminatorValueType\n  include JSON::Serializable\n\n  use_json_discriminator \"type\", {\n                                         0 => JSONVariableDiscriminatorNumber,\n    \"1\"                                    => JSONVariableDiscriminatorString,\n    true                                   => JSONVariableDiscriminatorBoolTrue,\n    false                                  => JSONVariableDiscriminatorBoolFalse,\n    JSONVariableDiscriminatorEnumFoo::Foo  => JSONVariableDiscriminatorEnum,\n    JSONVariableDiscriminatorEnumFoo8::Foo => JSONVariableDiscriminatorEnum8,\n  }\nend\n\nclass JSONVariableDiscriminatorNumber < JSONVariableDiscriminatorValueType\nend\n\nclass JSONVariableDiscriminatorString < JSONVariableDiscriminatorValueType\nend\n\nclass JSONVariableDiscriminatorBoolTrue < JSONVariableDiscriminatorValueType\nend\n\nclass JSONVariableDiscriminatorBoolFalse < JSONVariableDiscriminatorValueType\nend\n\nclass JSONVariableDiscriminatorEnum < JSONVariableDiscriminatorValueType\nend\n\nclass JSONVariableDiscriminatorEnum8 < JSONVariableDiscriminatorValueType\nend\n\nclass JSONStrictDiscriminator\n  include JSON::Serializable\n  include JSON::Serializable::Strict\n\n  property type : String\n\n  use_json_discriminator \"type\", {foo: JSONStrictDiscriminatorFoo, bar: JSONStrictDiscriminatorBar}\nend\n\nclass JSONStrictDiscriminatorFoo < JSONStrictDiscriminator\nend\n\nclass JSONStrictDiscriminatorBar < JSONStrictDiscriminator\n  property x : JSONStrictDiscriminator\n  property y : JSONStrictDiscriminator\nend\n\nmodule JSONNamespace\n  struct FooRequest\n    include JSON::Serializable\n\n    getter foo : Foo\n    getter bar = Bar.new\n  end\n\n  struct Foo\n    include JSON::Serializable\n    getter id = \"id:foo\"\n  end\n\n  struct Bar\n    include JSON::Serializable\n    getter id = \"id:bar\"\n\n    def initialize # Allow for default value above\n    end\n  end\nend\n\nclass JSONSomething\n  include JSON::Serializable\n\n  property value : JSONSomething?\nend\n\nmodule JsonDiscriminatorBug\n  abstract class Base\n    include JSON::Serializable\n\n    use_json_discriminator(\"type\", {\"a\" => A, \"b\" => B, \"c\" => C})\n  end\n\n  class A < Base\n  end\n\n  class B < Base\n    property source : Base\n    property value : Int32 = 1\n  end\n\n  class C < B\n  end\nend\n\nrecord JSONAttrWithEnumValue, value : JSONSerializableEnum do\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Enum::ValueConverter(JSONSerializableEnum))]\n  @value : JSONSerializableEnum\nend\n\nrecord JSONAttrWithFlagEnumValue, value : JSONSerializableFlagEnum do\n  include JSON::Serializable\n\n  @[JSON::Field(converter: Enum::ValueConverter(JSONSerializableFlagEnum))]\n  @value : JSONSerializableFlagEnum\nend\n\nabstract class SerializableFoo\n  include JSON::Serializable\n\n  module Converter\n    def self.from_json(pull : JSON::PullParser) : SerializableFoo\n      SerializableFoo.find.from_json(\"{}\")\n    end\n  end\n\n  def self.find : SerializableFoo.class\n    SerializableBar.as(SerializableFoo.class)\n  end\nend\n\nclass SerializableBar < SerializableFoo\n  @[JSON::Field(converter: SerializableFoo::Converter)]\n  getter foo : SerializableFoo = SerializableBaz.new\nend\n\nclass SerializableBaz < SerializableFoo\n  def initialize\n  end\nend\n\nclass JSONInitializeOpts\n  include JSON::Serializable\n\n  property value : Int32\n\n  def initialize(**opts)\n    @value = opts.size\n  end\nend\n\nrecord Namespaced::JSON::Wrapper, name : String, options : Hash(String, ::JSON::Any::Type)? = nil do\n  include ::JSON::Serializable\nend\n\ndescribe \"JSON::Serializable\" do\n  it \"works with classes within `JSON` namespace\" do\n    Namespaced::JSON::Wrapper\n      .from_json(<<-JSON)\n        {\n          \"name\": \"foo\",\n          \"options\": {\n            \"foo\": true\n          }\n        }\n        JSON\n      .to_json\n  end\n\n  it \"works with record\" do\n    JSONAttrPoint.new(1, 2).to_json.should eq \"{\\\"x\\\":1,\\\"y\\\":2}\"\n    JSONAttrPoint.from_json(%({\"x\": 1, \"y\": 2})).should eq JSONAttrPoint.new(1, 2)\n  end\n\n  it \"empty class\" do\n    e = JSONAttrEmptyClass.new\n    e.to_json.should eq \"{}\"\n    JSONAttrEmptyClass.from_json(\"{}\")\n  end\n\n  it \"empty class with unmapped\" do\n    JSONAttrEmptyClassWithUnmapped.from_json(%({\"name\": \"John\", \"age\": 30})).json_unmapped.should eq({\"name\" => \"John\", \"age\" => 30})\n  end\n\n  it \"parses person\" do\n    person = JSONAttrPerson.from_json(%({\"name\": \"John\", \"age\": 30}))\n    person.should be_a(JSONAttrPerson)\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n  end\n\n  it \"parses person without age\" do\n    person = JSONAttrPerson.from_json(%({\"name\": \"John\"}))\n    person.should be_a(JSONAttrPerson)\n    person.name.should eq(\"John\")\n    person.name.size.should eq(4) # This verifies that name is not nilable\n    person.age.should be_nil\n  end\n\n  it \"parses array of people\" do\n    people = Array(JSONAttrPerson).from_json(%([{\"name\": \"John\"}, {\"name\": \"Doe\"}]))\n    people.size.should eq(2)\n  end\n\n  it \"works with class with two fields\" do\n    person1 = JSONAttrPersonWithTwoFieldInInitialize.from_json(%({\"name\": \"John\", \"age\": 30}))\n    person2 = JSONAttrPersonWithTwoFieldInInitialize.new(\"John\", 30)\n    person1.should eq person2\n  end\n\n  it \"does to_json\" do\n    person = JSONAttrPerson.from_json(%({\"name\": \"John\", \"age\": 30}))\n    person2 = JSONAttrPerson.from_json(person.to_json)\n    person2.should eq(person)\n  end\n\n  it \"parses person with unknown attributes\" do\n    person = JSONAttrPerson.from_json(%({\"name\": \"John\", \"age\": 30, \"foo\": \"bar\"}))\n    person.should be_a(JSONAttrPerson)\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n  end\n\n  it \"parses strict person with unknown attributes\" do\n    error_message = <<-'MSG'\n      Unknown JSON attribute: foo\n        parsing StrictJSONAttrPerson\n      MSG\n    ex = expect_raises ::JSON::SerializableError, error_message do\n      StrictJSONAttrPerson.from_json <<-JSON\n        {\n          \"name\": \"John\",\n          \"age\": 30,\n          \"foo\": \"bar\"\n        }\n        JSON\n    end\n    ex.location.should eq({4, 3})\n    ex.attribute.should eq \"foo\"\n  end\n\n  it \"should parse extra fields (JSONAttrPersonExtraFields with on_unknown_json_attribute)\" do\n    person = JSONAttrPersonExtraFields.from_json(%({\"name\": \"John\", \"age\": 30, \"x\": \"1\", \"y\": 2, \"z\": [1,2,3]}))\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n    person.json_unmapped.should eq({\"x\" => \"1\", \"y\" => 2_i64, \"z\" => [1, 2, 3]})\n  end\n\n  it \"should to store extra fields (JSONAttrPersonExtraFields with on_to_json)\" do\n    person = JSONAttrPersonExtraFields.from_json(%({\"name\": \"John\", \"age\": 30, \"x\": \"1\", \"y\": 2, \"z\": [1,2,3]}))\n    person.name = \"John1\"\n    person.json_unmapped.delete(\"y\")\n    person.json_unmapped[\"q\"] = JSON::Any.new(\"w\")\n    person.to_json.should eq \"{\\\"name\\\":\\\"John1\\\",\\\"age\\\":30,\\\"x\\\":\\\"1\\\",\\\"z\\\":[1,2,3],\\\"q\\\":\\\"w\\\"}\"\n  end\n\n  it \"raises if non-nilable attribute is nil\" do\n    error_message = <<-'MSG'\n      Missing JSON attribute: name\n        parsing JSONAttrPerson#name at line 1, column 1\n      MSG\n    ex = expect_raises ::JSON::SerializableError, error_message do\n      JSONAttrPerson.from_json(%({\"age\": 30}))\n    end\n    ex.location.should eq({1, 1})\n    ex.attribute.should eq \"name\"\n  end\n\n  it \"raises if not an object\" do\n    error_message = <<-'MSG'\n      Expected BeginObject but was String at line 1, column 1\n        parsing StrictJSONAttrPerson at line 0, column 0\n      MSG\n    ex = expect_raises ::JSON::SerializableError, error_message do\n      StrictJSONAttrPerson.from_json <<-JSON\n        \"foo\"\n        JSON\n    end\n    ex.location.should eq({1, 1})\n  end\n\n  it \"raises if data type does not match\" do\n    error_message = <<-MSG\n      Couldn't parse (Int32 | Nil) from \"foo\" at line 3, column 10\n      MSG\n    ex = expect_raises ::JSON::SerializableError, error_message do\n      StrictJSONAttrPerson.from_json <<-JSON\n        {\n          \"name\": \"John\",\n          \"age\": \"foo\",\n          \"foo\": \"bar\"\n        }\n        JSON\n    end\n    ex.location.should eq({3, 10})\n    ex.attribute.should eq \"age\"\n  end\n\n  it \"doesn't emit null by default when doing to_json\" do\n    person = JSONAttrPerson.from_json(%({\"name\": \"John\"}))\n    person.to_json.should_not match /age/\n  end\n\n  it \"emits null on request when doing to_json\" do\n    person = JSONAttrPersonEmittingNull.from_json(%({\"name\": \"John\"}))\n    person.to_json.should match /age/\n  end\n\n  it \"emit_nulls option\" do\n    person = JSONAttrPersonEmittingNullsByOptions.from_json(%({\"name\": \"John\"}))\n    person.to_json.should eq \"{\\\"name\\\":\\\"John\\\",\\\"age\\\":null,\\\"value1\\\":null}\"\n  end\n\n  it \"doesn't raises on false value when not-nil\" do\n    json = JSONAttrValue(Bool).from_json(%({\"value\": false}))\n    json.value.should be_false\n  end\n\n  it \"parses JSON integer into a float property (#8618)\" do\n    json = JSONAttrValue(Float64).from_json(%({\"value\": 123}))\n    json.value.should eq(123.0)\n  end\n\n  it \"parses UUID\" do\n    uuid = JSONAttrValue(UUID).from_json(%({\"value\": \"ba714f86-cac6-42c7-8956-bcf5105e1b81\"}))\n    uuid.should be_a(JSONAttrValue(UUID))\n    uuid.value.should eq(UUID.new(\"ba714f86-cac6-42c7-8956-bcf5105e1b81\"))\n  end\n\n  it \"parses json with Time::Format converter\" do\n    json = JSONAttrWithTime.from_json(%({\"value\": \"2014-10-31 23:37:16\"}))\n    json.value.should be_a(Time)\n    json.value.to_s.should eq(\"2014-10-31 23:37:16 UTC\")\n    json.to_json.should eq(%({\"value\":\"2014-10-31 23:37:16\"}))\n  end\n\n  it \"allows setting a nilable property to nil\" do\n    person = JSONAttrPerson.new(\"John\")\n    person.age = 1\n    person.age = nil\n  end\n\n  it \"parses simple mapping\" do\n    person = JSONAttrWithSimpleMapping.from_json(%({\"name\": \"John\", \"age\": 30}))\n    person.should be_a(JSONAttrWithSimpleMapping)\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n  end\n\n  it \"outputs with converter when nilable\" do\n    json = JSONAttrWithNilableTime.new\n    json.to_json.should eq(\"{}\")\n  end\n\n  it \"outputs with converter when nilable when emit_null is true\" do\n    json = JSONAttrWithNilableTimeEmittingNull.new\n    json.to_json.should eq(%({\"value\":null}))\n  end\n\n  it \"outputs JSON with Hash\" do\n    input = {\n      value: {\"foo\" => \"bar\"},\n    }.to_json\n    json = JSONAttrValue(Hash(String, String)).from_json(input)\n    json.to_json.should eq(input)\n  end\n\n  it \"parses json with keywords\" do\n    json = JSONAttrWithKeywordsMapping.from_json(%({\"end\": 1, \"abstract\": 2}))\n    json.end.should eq(1)\n    json.abstract.should eq(2)\n  end\n\n  it \"parses json with any\" do\n    json = JSONAttrWithAny.from_json(%({\"name\": \"Hi\", \"any\": [{\"x\": 1}, 2, \"hey\", true, false, 1.5, null]}))\n    json.name.should eq(\"Hi\")\n    json.any.raw.should eq([{\"x\" => 1}, 2, \"hey\", true, false, 1.5, nil])\n    json.to_json.should eq(%({\"name\":\"Hi\",\"any\":[{\"x\":1},2,\"hey\",true,false,1.5,null]}))\n  end\n\n  it \"parses json with problematic keys\" do\n    json = JSONAttrWithProblematicKeys.from_json(%({\"key\": 1, \"pull\": 2}))\n    json.key.should eq(1)\n    json.pull.should eq(2)\n  end\n\n  it \"parses json array as set\" do\n    json = JSONAttrValue(Set(String)).from_json(%({\"value\": [\"a\", \"a\", \"b\"]}))\n    json.value.should eq(Set(String){\"a\", \"b\"})\n  end\n\n  it \"allows small types of integer\" do\n    json = JSONAttrWithSmallIntegers.from_json(%({\"foo\": 23, \"bar\": 7}))\n\n    json.foo.should eq(23)\n    typeof(json.foo).should eq(Int16)\n\n    json.bar.should eq(7)\n    typeof(json.bar).should eq(Int8)\n  end\n\n  describe \"parses json with defaults\" do\n    it \"mixed\" do\n      json = JSONAttrWithDefaults.from_json(%({\"a\":1,\"b\":\"bla\"}))\n      json.a.should eq 1\n      json.b.should eq \"bla\"\n\n      json = JSONAttrWithDefaults.from_json(%({\"a\":1}))\n      json.a.should eq 1\n      json.b.should eq \"Haha\"\n\n      json = JSONAttrWithDefaults.from_json(%({\"b\":\"bla\"}))\n      json.a.should eq 11\n      json.b.should eq \"bla\"\n\n      json = JSONAttrWithDefaults.from_json(%({}))\n      json.a.should eq 11\n      json.b.should eq \"Haha\"\n\n      json = JSONAttrWithDefaults.from_json(%({\"a\":null,\"b\":null,\"f\":null}))\n      json.a.should eq 11\n      json.b.should eq \"Haha\"\n      json.f.should be_nil\n    end\n\n    it \"bool\" do\n      json = JSONAttrWithDefaults.from_json(%({}))\n      json.c.should be_true\n      typeof(json.c).should eq Bool\n      json.d.should be_false\n      typeof(json.d).should eq Bool\n\n      json = JSONAttrWithDefaults.from_json(%({\"c\":false}))\n      json.c.should be_false\n      json = JSONAttrWithDefaults.from_json(%({\"c\":true}))\n      json.c.should be_true\n\n      json = JSONAttrWithDefaults.from_json(%({\"d\":false}))\n      json.d.should be_false\n      json = JSONAttrWithDefaults.from_json(%({\"d\":true}))\n      json.d.should be_true\n    end\n\n    it \"with nilable\" do\n      json = JSONAttrWithDefaults.from_json(%({}))\n\n      json.e.should be_false\n      typeof(json.e).should eq(Bool | Nil)\n\n      json.f.should eq 1\n      typeof(json.f).should eq(Int32 | Nil)\n\n      json.g.should be_nil\n      typeof(json.g).should eq(Int32 | Nil)\n\n      json = JSONAttrWithDefaults.from_json(%({\"e\":false}))\n      json.e.should be_false\n      json = JSONAttrWithDefaults.from_json(%({\"e\":true}))\n      json.e.should be_true\n    end\n\n    it \"create new array every time\" do\n      json = JSONAttrWithDefaults.from_json(%({}))\n      json.h.should eq [1, 2, 3]\n      json.h << 4\n      json.h.should eq [1, 2, 3, 4]\n\n      json = JSONAttrWithDefaults.from_json(%({}))\n      json.h.should eq [1, 2, 3]\n    end\n  end\n\n  it \"converter with null value (#13655)\" do\n    JSONAttrNilableWithTimeEpoch.from_json(%({\"value\": null})).value.should be_nil\n    JSONAttrNilableWithTimeEpoch.from_json(%({\"value\":1459859781})).value.should eq Time.unix(1459859781)\n  end\n\n  it \"converter with default value\" do\n    JSONAttrDefaultWithTimeEpoch.from_json(%({\"value\": null})).value.should eq Time.unix(0)\n    JSONAttrDefaultWithTimeEpoch.from_json(%({\"value\":1459859781})).value.should eq Time.unix(1459859781)\n  end\n\n  it \"uses Time::EpochConverter\" do\n    string = %({\"value\":1459859781})\n    json = JSONAttrWithTimeEpoch.from_json(string)\n    json.value.should be_a(Time)\n    json.value.should eq(Time.unix(1459859781))\n    json.to_json.should eq(string)\n  end\n\n  it \"uses Time::EpochMillisConverter\" do\n    string = %({\"value\":1459860483856})\n    json = JSONAttrWithTimeEpochMillis.from_json(string)\n    json.value.should be_a(Time)\n    json.value.should eq(Time.unix_ms(1459860483856))\n    json.to_json.should eq(string)\n  end\n\n  describe JSON::ArrayConverter do\n    it \"uses converter metaclass\" do\n      string = %({\"value\":[1459859781]})\n      json = JSONAttrWithTimeArray1.from_json(string)\n      json.value.should be_a(Array(Time))\n      json.value.should eq([Time.unix(1459859781)])\n      json.to_json.should eq(string)\n    end\n\n    it \"uses converter instance with nested converter metaclass\" do\n      string = %({\"value\":[1459859781]})\n      json = JSONAttrWithTimeArray2.from_json(string)\n      json.value.should be_a(Array(Time))\n      json.value.should eq([Time.unix(1459859781)])\n      json.to_json.should eq(string)\n    end\n\n    it \"uses converter instance with nested converter instance\" do\n      string = %({\"value\":[\"2014-10-31 23:37:16\"]})\n      json = JSONAttrWithTimeArray3.from_json(string)\n      json.value.should be_a(Array(Time))\n      json.value.map(&.to_s).should eq([\"2014-10-31 23:37:16 UTC\"])\n      json.to_json.should eq(string)\n    end\n  end\n\n  describe JSON::HashValueConverter do\n    it \"uses converter metaclass\" do\n      string = %({\"value\":{\"foo\":1459859781}})\n      json = JSONAttrWithTimeHash1.from_json(string)\n      json.value.should be_a(Hash(String, Time))\n      json.value.should eq({\"foo\" => Time.unix(1459859781)})\n      json.to_json.should eq(string)\n    end\n\n    it \"uses converter instance with nested converter metaclass\" do\n      string = %({\"value\":{\"foo\":1459859781}})\n      json = JSONAttrWithTimeHash2.from_json(string)\n      json.value.should be_a(Hash(String, Time))\n      json.value.should eq({\"foo\" => Time.unix(1459859781)})\n      json.to_json.should eq(string)\n    end\n\n    it \"uses converter instance with nested converter instance\" do\n      string = %({\"value\":{\"foo\":\"2014-10-31 23:37:16\"}})\n      json = JSONAttrWithTimeHash3.from_json(string)\n      json.value.should be_a(Hash(String, Time))\n      json.value.transform_values(&.to_s).should eq({\"foo\" => \"2014-10-31 23:37:16 UTC\"})\n      json.to_json.should eq(string)\n    end\n  end\n\n  it \"parses raw value from int\" do\n    string = %({\"value\":123456789123456789123456789123456789})\n    json = JSONAttrWithRaw.from_json(string)\n    json.value.should eq(\"123456789123456789123456789123456789\")\n    json.to_json.should eq(string)\n  end\n\n  it \"parses raw value from float\" do\n    string = %({\"value\":123456789123456789.123456789123456789})\n    json = JSONAttrWithRaw.from_json(string)\n    json.value.should eq(\"123456789123456789.123456789123456789\")\n    json.to_json.should eq(string)\n  end\n\n  it \"parses raw value from object\" do\n    string = %({\"value\":[null,true,false,{\"x\":[1,1.5]}]})\n    json = JSONAttrWithRaw.from_json(string)\n    json.value.should eq(%([null,true,false,{\"x\":[1,1.5]}]))\n    json.to_json.should eq(string)\n  end\n\n  it \"parses with root\" do\n    json = %({\"result\":{\"heroes\":[{\"name\":\"Batman\"}]}})\n    result = JSONAttrWithRoot.from_json(json)\n    result.result.should be_a(Array(JSONAttrPerson))\n    result.result.first.name.should eq \"Batman\"\n    result.to_json.should eq(json)\n  end\n\n  it \"parses with nilable root\" do\n    json = %({\"result\":null})\n    result = JSONAttrWithNilableRoot.from_json(json)\n    result.result.should be_nil\n    result.to_json.should eq(\"{}\")\n  end\n\n  it \"parses with nilable root and emit null\" do\n    json = %({\"result\":null})\n    result = JSONAttrWithNilableRootEmitNull.from_json(json)\n    result.result.should be_nil\n    result.to_json.should eq(json)\n  end\n\n  it \"parses nilable union\" do\n    obj = JSONAttrValue(Int32?).from_json(%({\"value\": 1}))\n    obj.value.should eq(1)\n    obj.to_json.should eq(%({\"value\":1}))\n\n    obj = JSONAttrValue(Int32?).from_json(%({\"value\": null}))\n    obj.value.should be_nil\n    obj.to_json.should eq(%({}))\n\n    obj = JSONAttrValue(Int32?).from_json(%({}))\n    obj.value.should be_nil\n    obj.to_json.should eq(%({}))\n  end\n\n  describe \"parses JSON with presence markers\" do\n    it \"parses person with absent attributes\" do\n      json = JSONAttrWithPresence.from_json(%({\"first_name\": null}))\n      json.first_name.should be_nil\n      json.first_name_present?.should be_true\n      json.last_name.should be_nil\n      json.last_name_present?.should be_false\n    end\n  end\n\n  describe \"serializes JSON with presence markers and ignore_serialize\" do\n    context \"ignore_serialize is set to a method which returns true when value is nil or empty string\" do\n      it \"ignores field when value is empty string\" do\n        json = JSONAttrWithPresenceAndIgnoreSerialize.from_json(%({\"first_name\": \"\"}))\n        json.first_name_present?.should be_true\n        json.to_json.should eq(%({}))\n      end\n\n      it \"ignores field when value is nil\" do\n        json = JSONAttrWithPresenceAndIgnoreSerialize.from_json(%({\"first_name\": null}))\n        json.first_name_present?.should be_true\n        json.to_json.should eq(%({}))\n      end\n    end\n\n    context \"ignore_serialize is set to conditional expressions 'last_name.nil? && !last_name_present?'\" do\n      it \"emits null when value is null and @last_name_present is true\" do\n        json = JSONAttrWithPresenceAndIgnoreSerialize.from_json(%({\"last_name\": null}))\n        json.last_name_present?.should be_true\n        json.to_json.should eq(%({\"last_name\":null}))\n      end\n\n      it \"does not emit null when value is null and @last_name_present is false\" do\n        json = JSONAttrWithPresenceAndIgnoreSerialize.from_json(%({}))\n        json.last_name_present?.should be_false\n        json.to_json.should eq(%({}))\n      end\n\n      it \"emits field when value is not nil and @last_name_present is false\" do\n        json = JSONAttrWithPresenceAndIgnoreSerialize.new(last_name: \"something\")\n        json.last_name_present?.should be_false\n        json.to_json.should eq(%({\"last_name\":\"something\"}))\n      end\n\n      it \"emits field when value is not nil and @last_name_present is true\" do\n        json = JSONAttrWithPresenceAndIgnoreSerialize.from_json(%({\"last_name\":\"something\"}))\n        json.last_name_present?.should be_true\n        json.to_json.should eq(%({\"last_name\":\"something\"}))\n      end\n    end\n  end\n\n  describe \"with query attributes\" do\n    it \"defines query getter\" do\n      json = JSONAttrWithQueryAttributes.from_json(%({\"foo\": true}))\n      json.foo?.should be_true\n      json.bar?.should be_false\n    end\n\n    it \"defines query getter with class restriction\" do\n      {% begin %}\n        {% methods = JSONAttrWithQueryAttributes.methods %}\n        {{ methods.find(&.name.==(\"foo?\")).return_type }}.should eq(Bool)\n        {{ methods.find(&.name.==(\"bar?\")).return_type }}.should eq(Bool)\n      {% end %}\n    end\n\n    it \"defines non-query setter and presence methods\" do\n      json = JSONAttrWithQueryAttributes.from_json(%({\"foo\": false}))\n      json.bar_present?.should be_false\n      json.bar = true\n      json.bar?.should be_true\n    end\n\n    it \"maps non-query attributes\" do\n      json = JSONAttrWithQueryAttributes.from_json(%({\"foo\": false, \"is_bar\": false}))\n      json.bar_present?.should be_true\n      json.bar?.should be_false\n      json.bar = true\n      json.to_json.should eq(%({\"foo\":false,\"is_bar\":true}))\n    end\n\n    it \"raises if non-nilable attribute is nil\" do\n      error_message = <<-'MSG'\n        Missing JSON attribute: foo\n          parsing JSONAttrWithQueryAttributes#foo at line 1, column 1\n        MSG\n      ex = expect_raises ::JSON::SerializableError, error_message do\n        JSONAttrWithQueryAttributes.from_json(%({\"is_bar\": true}))\n      end\n      ex.location.should eq({1, 1})\n      ex.attribute.should eq \"foo\"\n    end\n\n    it \"raises with key as attribute if non-nilable attribute is nil\" do\n      error_message = <<-'MSG'\n        Missing JSON attribute: is_foo\n          parsing JSONAttrWithKeyQueryAttribute#is_foo at line 1, column 1\n        MSG\n      ex = expect_raises ::JSON::SerializableError, error_message do\n        JSONAttrWithKeyQueryAttribute.from_json(%({}))\n      end\n      ex.location.should eq({1, 1})\n      ex.attribute.should eq \"is_foo\"\n    end\n  end\n\n  describe \"BigDecimal\" do\n    it \"parses json string with BigDecimal\" do\n      json = JSONAttrValue(BigDecimal).from_json(%({\"value\": \"10.05\"}))\n      json.value.should eq(BigDecimal.new(\"10.05\"))\n    end\n\n    it \"parses large json ints with BigDecimal\" do\n      json = JSONAttrValue(BigDecimal).from_json(%({\"value\": 9223372036854775808}))\n      json.value.should eq(BigDecimal.new(\"9223372036854775808\"))\n    end\n\n    it \"parses json float with BigDecimal\" do\n      json = JSONAttrValue(BigDecimal).from_json(%({\"value\": 10.05}))\n      json.value.should eq(BigDecimal.new(\"10.05\"))\n    end\n\n    it \"parses large precision json floats with BigDecimal\" do\n      json = JSONAttrValue(BigDecimal).from_json(%({\"value\": 0.00045808999999999997}))\n      json.value.should eq(BigDecimal.new(\"0.00045808999999999997\"))\n    end\n  end\n\n  it \"parses 128-bit integer\" do\n    json = JSONAttrValue(Int128).from_json(%({\"value\": #{Int128::MAX}}))\n    json.value.should eq Int128::MAX\n  end\n\n  describe \"work with module and inheritance\" do\n    it { JSONAttrModuleTest.from_json(%({\"phoo\": 20})).to_tuple.should eq({10, 20}) }\n    it { JSONAttrModuleTest.from_json(%({\"phoo\": 20})).to_tuple.should eq({10, 20}) }\n    it { JSONAttrModuleTest2.from_json(%({\"phoo\": 20, \"bar\": 30})).to_tuple.should eq({10, 20, 30}) }\n    it { JSONAttrModuleTest2.from_json(%({\"bar\": 30, \"moo\": 40})).to_tuple.should eq({40, 15, 30}) }\n  end\n\n  it \"works together with yaml\" do\n    person = JSONAttrPersonWithYAML.new(\"Vasya\", 30)\n    person.to_json.should eq \"{\\\"name\\\":\\\"Vasya\\\",\\\"age\\\":30}\"\n    person.to_yaml.should eq \"---\\nname: Vasya\\nage: 30\\n\"\n\n    JSONAttrPersonWithYAML.from_json(person.to_json).should eq person\n    JSONAttrPersonWithYAML.from_yaml(person.to_yaml).should eq person\n  end\n\n  it \"yaml and json with after_initialize hook\" do\n    person = JSONAttrPersonWithYAMLInitializeHook.new(\"Vasya\", 30)\n    person.msg.should eq \"Hello Vasya\"\n\n    person.to_json.should eq \"{\\\"name\\\":\\\"Vasya\\\",\\\"age\\\":30}\"\n    person.to_yaml.should eq \"---\\nname: Vasya\\nage: 30\\n\"\n\n    JSONAttrPersonWithYAMLInitializeHook.from_json(person.to_json).msg.should eq \"Hello Vasya\"\n    JSONAttrPersonWithYAMLInitializeHook.from_yaml(person.to_yaml).msg.should eq \"Hello Vasya\"\n  end\n\n  it \"json with selective serialization\" do\n    person = JSONAttrPersonWithSelectiveSerialization.new(\"Vasya\", \"P@ssw0rd\")\n    person.to_json.should eq \"{\\\"name\\\":\\\"Vasya\\\",\\\"generated\\\":\\\"generated-internally\\\"}\"\n\n    person_json = \"{\\\"name\\\":\\\"Vasya\\\",\\\"generated\\\":\\\"should not set\\\",\\\"password\\\":\\\"update\\\"}\"\n    person = JSONAttrPersonWithSelectiveSerialization.from_json(person_json)\n    person.generated.should eq \"generated-internally\"\n    person.password.should eq \"update\"\n  end\n\n  describe \"use_json_discriminator\" do\n    it \"deserializes with discriminator\" do\n      point = JSONShape.from_json(%({\"type\": \"point\", \"x\": 1, \"y\": 2})).as(JSONPoint)\n      point.x.should eq(1)\n      point.y.should eq(2)\n\n      circle = JSONShape.from_json(%({\"type\": \"circle\", \"x\": 1, \"y\": 2, \"radius\": 3})).as(JSONCircle)\n      circle.x.should eq(1)\n      circle.y.should eq(2)\n      circle.radius.should eq(3)\n    end\n\n    it \"raises if missing discriminator\" do\n      expect_raises(::JSON::SerializableError, \"Missing JSON discriminator field 'type'\") do\n        JSONShape.from_json(\"{}\")\n      end\n    end\n\n    it \"raises if unknown discriminator value\" do\n      expect_raises(::JSON::SerializableError, %(Unknown 'type' discriminator value: \"unknown\")) do\n        JSONShape.from_json(%({\"type\": \"unknown\"}))\n      end\n    end\n\n    it \"deserializes with variable discriminator value type\" do\n      object_number = JSONVariableDiscriminatorValueType.from_json(%({\"type\": 0}))\n      object_number.should be_a(JSONVariableDiscriminatorNumber)\n\n      object_string = JSONVariableDiscriminatorValueType.from_json(%({\"type\": \"1\"}))\n      object_string.should be_a(JSONVariableDiscriminatorString)\n\n      object_bool = JSONVariableDiscriminatorValueType.from_json(%({\"type\": true}))\n      object_bool.should be_a(JSONVariableDiscriminatorBoolTrue)\n\n      object_bool = JSONVariableDiscriminatorValueType.from_json(%({\"type\": false}))\n      object_bool.should be_a(JSONVariableDiscriminatorBoolFalse)\n\n      object_enum = JSONVariableDiscriminatorValueType.from_json(%({\"type\": 4}))\n      object_enum.should be_a(JSONVariableDiscriminatorEnum)\n\n      object_enum = JSONVariableDiscriminatorValueType.from_json(%({\"type\": 18}))\n      object_enum.should be_a(JSONVariableDiscriminatorEnum8)\n    end\n\n    it \"deserializes with discriminator, strict recursive type\" do\n      foo = JSONStrictDiscriminator.from_json(%({\"type\": \"foo\"}))\n      foo = foo.should be_a(JSONStrictDiscriminatorFoo)\n\n      bar = JSONStrictDiscriminator.from_json(%({\"type\": \"bar\", \"x\": {\"type\": \"foo\"}, \"y\": {\"type\": \"foo\"}}))\n      bar = bar.should be_a(JSONStrictDiscriminatorBar)\n      bar.x.should be_a(JSONStrictDiscriminatorFoo)\n      bar.y.should be_a(JSONStrictDiscriminatorFoo)\n    end\n\n    it \"deserializes with discriminator, another recursive type, fixes: #13429\" do\n      c = JsonDiscriminatorBug::Base.from_json %q({\"type\": \"c\", \"source\": {\"type\": \"a\"}, \"value\": 2})\n      c.as(JsonDiscriminatorBug::C).value.should eq 2\n\n      c = JsonDiscriminatorBug::Base.from_json %q({\"type\": \"c\", \"source\": {\"type\": \"a\"}})\n      c.as(JsonDiscriminatorBug::C).value.should eq 1\n    end\n  end\n\n  describe \"Enum::ValueConverter.from_json\" do\n    it \"normal enum\" do\n      JSONAttrWithEnumValue.from_json(%({\"value\": 0})).value.should eq(JSONSerializableEnum::Zero)\n      JSONAttrWithEnumValue.from_json(%({\"value\": 1})).value.should eq(JSONSerializableEnum::One)\n      JSONAttrWithEnumValue.from_json(%({\"value\": 2})).value.should eq(JSONSerializableEnum::Two)\n      JSONAttrWithEnumValue.from_json(%({\"value\": 3})).value.should eq(JSONSerializableEnum::OneHundred)\n\n      expect_raises(JSON::ParseException, %(Expected Int but was String)) do\n        JSONAttrWithEnumValue.from_json(%({\"value\": \"3\"}))\n      end\n      expect_raises(JSON::ParseException, %(Unknown enum JSONSerializableEnum value: 4)) do\n        JSONAttrWithEnumValue.from_json(%({\"value\": 4}))\n      end\n      expect_raises(JSON::ParseException, %(Unknown enum JSONSerializableEnum value: -1)) do\n        JSONAttrWithEnumValue.from_json(%({\"value\": -1}))\n      end\n      expect_raises(JSON::ParseException, %(Expected Int but was String)) do\n        JSONAttrWithEnumValue.from_json(%({\"value\": \"\"}))\n      end\n\n      expect_raises(JSON::ParseException, \"Expected Int but was String\") do\n        JSONAttrWithEnumValue.from_json(%({\"value\": \"one\"}))\n      end\n\n      expect_raises(JSON::ParseException, \"Expected Int but was BeginObject\") do\n        JSONAttrWithEnumValue.from_json(%({\"value\": {}}))\n      end\n      expect_raises(JSON::ParseException, \"Expected Int but was BeginArray\") do\n        JSONAttrWithEnumValue.from_json(%({\"value\": []}))\n      end\n    end\n\n    it \"flag enum\" do\n      JSONAttrWithFlagEnumValue.from_json(%({\"value\": 0})).value.should eq(JSONSerializableFlagEnum::None)\n      JSONAttrWithFlagEnumValue.from_json(%({\"value\": 1})).value.should eq(JSONSerializableFlagEnum::One)\n      JSONAttrWithFlagEnumValue.from_json(%({\"value\": 2})).value.should eq(JSONSerializableFlagEnum::Two)\n      JSONAttrWithFlagEnumValue.from_json(%({\"value\": 4})).value.should eq(JSONSerializableFlagEnum::OneHundred)\n      JSONAttrWithFlagEnumValue.from_json(%({\"value\": 5})).value.should eq(JSONSerializableFlagEnum::OneHundred | JSONSerializableFlagEnum::One)\n      JSONAttrWithFlagEnumValue.from_json(%({\"value\": 7})).value.should eq(JSONSerializableFlagEnum::All)\n\n      expect_raises(JSON::ParseException, %(Unknown enum JSONSerializableFlagEnum value: 8)) do\n        JSONAttrWithFlagEnumValue.from_json(%({\"value\": 8}))\n      end\n      expect_raises(JSON::ParseException, %(Unknown enum JSONSerializableFlagEnum value: -1)) do\n        JSONAttrWithFlagEnumValue.from_json(%({\"value\": -1}))\n      end\n      expect_raises(JSON::ParseException, %(Expected Int but was String)) do\n        JSONAttrWithFlagEnumValue.from_json(%({\"value\": \"\"}))\n      end\n      expect_raises(JSON::ParseException, \"Expected Int but was String\") do\n        JSONAttrWithFlagEnumValue.from_json(%({\"value\": \"one\"}))\n      end\n      expect_raises(JSON::ParseException, \"Expected Int but was BeginObject\") do\n        JSONAttrWithFlagEnumValue.from_json(%({\"value\": {}}))\n      end\n      expect_raises(JSON::ParseException, \"Expected Int but was BeginArray\") do\n        JSONAttrWithFlagEnumValue.from_json(%({\"value\": []}))\n      end\n    end\n  end\n\n  describe \"Enum::ValueConverter.to_json\" do\n    it \"normal enum\" do\n      klass = JSONAttrWithEnumValue\n\n      klass.new(JSONSerializableEnum::One).to_json.should eq %({\"value\":1})\n      klass.from_json(klass.new(JSONSerializableEnum::One).to_json).value\n        .should eq(JSONSerializableEnum::One)\n\n      klass.new(JSONSerializableEnum::OneHundred).to_json.should eq %({\"value\":3})\n      klass.from_json(klass.new(JSONSerializableEnum::OneHundred).to_json).value\n        .should eq(JSONSerializableEnum::OneHundred)\n\n      # undefined members can't be parsed back because the standard converter only accepts\n      # named members\n      klass.new(JSONSerializableEnum.new(42)).to_json.should eq %({\"value\":42})\n    end\n\n    it \"flag enum\" do\n      klass = JSONAttrWithFlagEnumValue\n\n      klass.new(JSONSerializableFlagEnum::One).to_json.should eq %({\"value\":1})\n      klass.from_json(klass.new(JSONSerializableFlagEnum::One).to_json).value\n        .should eq(JSONSerializableFlagEnum::One)\n\n      klass.new(JSONSerializableFlagEnum::OneHundred).to_json.should eq %({\"value\":4})\n      klass.from_json(klass.new(JSONSerializableFlagEnum::OneHundred).to_json).value\n        .should eq(JSONSerializableFlagEnum::OneHundred)\n\n      combined = JSONSerializableFlagEnum::OneHundred | JSONSerializableFlagEnum::One\n\n      klass.new(combined).to_json.should eq %({\"value\":5})\n      klass.from_json(klass.new(combined).to_json).value.should eq(combined)\n\n      klass.new(JSONSerializableFlagEnum::None).to_json.should eq %({\"value\":0})\n      klass.from_json(klass.new(JSONSerializableFlagEnum::None).to_json).value\n        .should eq(JSONSerializableFlagEnum::None)\n\n      klass.new(JSONSerializableFlagEnum::All).to_json.should eq %({\"value\":7})\n      klass.from_json(klass.new(JSONSerializableFlagEnum::All).to_json).value\n        .should eq(JSONSerializableFlagEnum::All)\n\n      klass.new(JSONSerializableFlagEnum.new(42)).to_json.should eq %({\"value\":42})\n    end\n  end\n\n  describe \"namespaced classes\" do\n    it \"lets default values use the object's own namespace\" do\n      request = JSONNamespace::FooRequest.from_json(%({\"foo\":{}}))\n      request.foo.id.should eq \"id:foo\"\n      request.bar.id.should eq \"id:bar\"\n    end\n  end\n\n  it \"fixes #13337\" do\n    JSONSomething.from_json(%({\"value\":{}})).value.should_not be_nil\n  end\n\n  it \"works when type has constructor with double splat parameter (#16140)\" do\n    JSONInitializeOpts.from_json(%({\"value\":123})).value.should eq(123)\n  end\n\n  it \"supports generic type variables in converters\" do\n    JSONAttrWithGenericConverter(Time::EpochConverter).from_json(%({\"value\":1459859781})).value.should eq(Time.unix(1459859781))\n  end\n\n  it \"fixes #16141\" do\n    SerializableFoo.find.from_json(\"{}\").should be_a(SerializableBar)\n  end\nend\n"
  },
  {
    "path": "spec/std/json/serialization_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/number\"\nrequire \"spec/helpers/iterate\"\nrequire \"json\"\nrequire \"big\"\nrequire \"big/json\"\nrequire \"uuid\"\nrequire \"uuid/json\"\n\nenum JSONSpecEnum\n  Zero\n  One\n  Two\n  OneHundred\nend\n\n@[Flags]\nenum JSONSpecFlagEnum\n  One\n  Two\n  OneHundred\nend\n\nprivate record FooPrivate, x : Int32 do\n  def self.new(json : JSON::PullParser)\n    new(Int32.new(json))\n  end\nend\n\ndescribe \"JSON serialization\" do\n  describe \"from_json\" do\n    it \"does String.from_json\" do\n      String.from_json(%(\"foo bar\")).should eq \"foo bar\"\n    end\n\n    it \"does Path.from_json\" do\n      Path.from_json(%(\"foo/bar\")).should eq(Path.new(\"foo/bar\"))\n    end\n\n    it \"does Path.from_json_object_key\" do\n      Hash(Path, String).from_json(%({\"foo/bar\": \"baz\"})).should eq({Path.new(\"foo/bar\") => \"baz\"})\n    end\n\n    it \"does Time::Location.from_json_object_key\" do\n      Hash(Time::Location, String).from_json(%({\"UTC\": \"foo\"}))\n        .should eq({Time::Location::UTC => \"foo\"})\n    end\n\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it \"does {{ int }}.from_json\" do\n        {{ int }}.from_json(\"0\").should(be_a({{ int }})).should eq(0)\n        {{ int }}.from_json(\"123\").should(be_a({{ int }})).should eq(123)\n        {{ int }}.from_json({{ int }}::MIN.to_s).should(be_a({{ int }})).should eq({{ int }}::MIN)\n        {{ int }}.from_json({{ int }}::MAX.to_s).should(be_a({{ int }})).should eq({{ int }}::MAX)\n      end\n\n      # NOTE: \"Invalid\" shows up only for `Int64`\n      it \"raises if {{ int }}.from_json overflows\" do\n        expect_raises(JSON::ParseException, /(Can't read|Invalid) {{ int }}/) do\n          {{ int }}.from_json(({{ int }}::MIN.to_big_i - 1).to_s)\n        end\n        expect_raises(JSON::ParseException, /(Can't read|Invalid) {{ int }}/) do\n          {{ int }}.from_json(({{ int }}::MAX.to_big_i + 1).to_s)\n        end\n      end\n    {% end %}\n\n    it \"errors on non-base-10 ints\" do\n      expect_raises(JSON::ParseException) { Int32.from_json \"0b1\" }\n      expect_raises(JSON::ParseException) { Int32.from_json \"0o1\" }\n      expect_raises(JSON::ParseException) { Int32.from_json \"0x1\" }\n      expect_raises(JSON::ParseException) { Int32.from_json \"01\" }\n    end\n\n    it \"errors on underscores inside ints\" do\n      expect_raises(JSON::ParseException) { Int32.from_json \"1_2\" }\n    end\n\n    it \"does Array(Nil)#from_json\" do\n      Array(Nil).from_json(\"[null, null]\").should eq([nil, nil])\n    end\n\n    it \"does Array(Bool)#from_json\" do\n      Array(Bool).from_json(\"[true, false]\").should eq([true, false])\n    end\n\n    it \"does Array(Int32)#from_json\" do\n      Array(Int32).from_json(\"[1, 2, 3]\").should eq([1, 2, 3])\n    end\n\n    it \"does Array(Int64)#from_json\" do\n      Array(Int64).from_json(\"[1, 2, 3]\").should eq([1, 2, 3])\n    end\n\n    it \"does Array(Float32)#from_json\" do\n      Array(Float32).from_json(\"[1.5, 2, 3.5]\").should eq([1.5, 2.0, 3.5])\n    end\n\n    it \"does Array(Float64)#from_json\" do\n      Array(Float64).from_json(\"[1.5, 2, 3.5]\").should eq([1.5, 2, 3.5])\n    end\n\n    it \"does Deque(String)#from_json\" do\n      Deque(String).from_json(%([\"a\", \"b\"])).should eq(Deque.new([\"a\", \"b\"]))\n    end\n\n    it \"does Iterator(String)#from_json\" do\n      assert_iterates_iterator [\"a\", \"b\"], Iterator(String).from_json(%([\"a\", \"b\"]))\n    end\n\n    it \"raises an error Iterator(String)#from_json with invalid types\" do\n      expect_raises(JSON::ParseException) do\n        Iterator(String).from_json(%([1, 2])).to_a\n      end\n    end\n\n    it \"raises an error Iterator(String)#from_json with invalid JSON\" do\n      expect_raises(JSON::ParseException) do\n        Iterator(String).from_json(%([\"a\")).to_a\n      end\n    end\n\n    it \"does Hash(String, String)#from_json\" do\n      Hash(String, String).from_json(%({\"foo\": \"x\", \"bar\": \"y\"})).should eq({\"foo\" => \"x\", \"bar\" => \"y\"})\n    end\n\n    it \"does Hash(String, Int32)#from_json\" do\n      Hash(String, Int32).from_json(%({\"foo\": 1, \"bar\": 2})).should eq({\"foo\" => 1, \"bar\" => 2})\n    end\n\n    it \"does Hash(Int32, String)#from_json\" do\n      Hash(Int32, String).from_json(%({\"1\": \"x\", \"2\": \"y\"})).should eq({1 => \"x\", 2 => \"y\"})\n    end\n\n    it \"does Hash(Float32, String)#from_json\" do\n      Hash(Float32, String).from_json(%({\"1.23\": \"x\", \"4.56\": \"y\"})).should eq({1.23_f32 => \"x\", 4.56_f32 => \"y\"})\n    end\n\n    it \"does Hash(Float64, String)#from_json\" do\n      Hash(Float64, String).from_json(%({\"1.23\": \"x\", \"4.56\": \"y\"})).should eq({1.23 => \"x\", 4.56 => \"y\"})\n    end\n\n    it \"does Hash(BigInt, String)#from_json\" do\n      Hash(BigInt, String).from_json(%({\"12345678901234567890\": \"x\"})).should eq({\"12345678901234567890\".to_big_i => \"x\"})\n    end\n\n    it \"does Hash(BigFloat, String)#from_json\" do\n      Hash(BigFloat, String).from_json(%({\"1234567890.123456789\": \"x\"})).should eq({\"1234567890.123456789\".to_big_f => \"x\"})\n    end\n\n    it \"does Hash(BigDecimal, String)#from_json\" do\n      Hash(BigDecimal, String).from_json(%({\"1234567890.123456789\": \"x\"})).should eq({\"1234567890.123456789\".to_big_d => \"x\"})\n    end\n\n    describe \"Hash with union key (Union.from_json_object_key?)\" do\n      it \"string deprioritized\" do\n        Hash(String | Int32, Nil).from_json(%({\"1\": null})).should eq({1 => nil})\n        Hash(String | UInt32, Nil).from_json(%({\"1\": null})).should eq({1 => nil})\n      end\n\n      it \"string without alternative\" do\n        Hash(String | Int32, Nil).from_json(%({\"foo\": null})).should eq({\"foo\" => nil})\n      end\n\n      it \"no match\" do\n        expect_raises JSON::ParseException, %(Can't convert \"foo\" into (Float64 | Int32) at line 1, column 2) do\n          Hash(Float64 | Int32, Nil).from_json(%({\"foo\": null}))\n        end\n      end\n    end\n\n    it \"raises an error Hash(String, Int32)#from_json with null value\" do\n      expect_raises(JSON::ParseException, \"Expected Int but was Null\") do\n        Hash(String, Int32).from_json(%({\"foo\": 1, \"bar\": 2, \"baz\": null}))\n      end\n    end\n\n    it \"does for Array(Int32) from IO\" do\n      io = IO::Memory.new \"[1, 2, 3]\"\n      Array(Int32).from_json(io).should eq([1, 2, 3])\n    end\n\n    it \"does for Array(Int32) with block\" do\n      elements = [] of Int32\n      ret = Array(Int32).from_json(\"[1, 2, 3]\") do |element|\n        elements << element\n      end\n      ret.should be_nil\n      elements.should eq([1, 2, 3])\n    end\n\n    it \"does for tuple\" do\n      tuple = Tuple(Int32, String).from_json(%([1, \"hello\"]))\n      tuple.should eq({1, \"hello\"})\n      typeof(tuple).should eq(Tuple(Int32, String))\n    end\n\n    it \"does for tuple with file-private type\" do\n      tuple = Tuple(FooPrivate).from_json %([1])\n      tuple.should eq({FooPrivate.new(1)})\n      typeof(tuple).should eq(Tuple(FooPrivate))\n    end\n\n    it \"does for empty tuple\" do\n      typeof(Tuple.new).from_json(\"[]\").should eq(Tuple.new)\n    end\n\n    it \"does for named tuple\" do\n      tuple = NamedTuple(x: Int32, y: String).from_json(%({\"y\": \"hello\", \"x\": 1}))\n      tuple.should eq({x: 1, y: \"hello\"})\n      typeof(tuple).should eq(NamedTuple(x: Int32, y: String))\n    end\n\n    it \"does for empty named tuple\" do\n      tuple = typeof(NamedTuple.new).from_json(%({}))\n      tuple.should eq(NamedTuple.new)\n      tuple.should be_a(typeof(NamedTuple.new))\n    end\n\n    it \"does for named tuple with nilable fields (#8089)\" do\n      tuple = NamedTuple(x: Int32?, y: String).from_json(%({\"y\": \"hello\"}))\n      tuple.should eq({x: nil, y: \"hello\"})\n      typeof(tuple).should eq(NamedTuple(x: Int32?, y: String))\n    end\n\n    it \"does for named tuple with nilable fields and null (#8089)\" do\n      tuple = NamedTuple(x: Int32?, y: String).from_json(%({\"y\": \"hello\", \"x\": null}))\n      tuple.should eq({x: nil, y: \"hello\"})\n      typeof(tuple).should eq(NamedTuple(x: Int32?, y: String))\n    end\n\n    it \"does for named tuple with spaces in key (#10918)\" do\n      tuple = NamedTuple(a: Int32, \"xyz b-23\": Int32).from_json %{{\"a\": 1, \"xyz b-23\": 2}}\n      tuple.should eq({a: 1, \"xyz b-23\": 2})\n      typeof(tuple).should eq(NamedTuple(a: Int32, \"xyz b-23\": Int32))\n    end\n\n    it \"does for named tuple with spaces in key and quote char (#10918)\" do\n      tuple = NamedTuple(a: Int32, \"xyz \\\"foo\\\" b-23\": Int32).from_json %{{\"a\": 1, \"xyz \\\\\"foo\\\\\" b-23\": 2}}\n      tuple.should eq({a: 1, \"xyz \\\"foo\\\" b-23\": 2})\n      typeof(tuple).should eq(NamedTuple(a: Int32, \"xyz \\\"foo\\\" b-23\": Int32))\n    end\n\n    it \"does for named tuple with file-private type\" do\n      tuple = NamedTuple(a: FooPrivate).from_json %({\"a\": 1})\n      tuple.should eq({a: FooPrivate.new(1)})\n      typeof(tuple).should eq(NamedTuple(a: FooPrivate))\n    end\n\n    it \"does for BigInt\" do\n      big = BigInt.from_json(\"123456789123456789123456789123456789123456789\")\n      big.should be_a(BigInt)\n      big.should eq(BigInt.new(\"123456789123456789123456789123456789123456789\"))\n    end\n\n    it \"raises for BigInt from unsupported types\" do\n      expect_raises(JSON::ParseException) { BigInt.from_json(\"true\") }\n      expect_raises(JSON::ParseException) { BigInt.from_json(\"1.23\") }\n      expect_raises(JSON::ParseException) { BigInt.from_json(\"[]\") }\n      expect_raises(JSON::ParseException) { BigInt.from_json(\"{}\") }\n    end\n\n    it \"does for BigFloat\" do\n      big = BigFloat.from_json(\"1234.567891011121314\")\n      big.should be_a(BigFloat)\n      big.should eq(BigFloat.new(\"1234.567891011121314\"))\n    end\n\n    it \"does for BigFloat from int\" do\n      big = BigFloat.from_json(\"1234\")\n      big.should be_a(BigFloat)\n      big.should eq(BigFloat.new(\"1234\"))\n    end\n\n    it \"does for BigFloat from string\" do\n      big = BigFloat.from_json(%(\"1234\"))\n      big.should be_a(BigFloat)\n      big.should eq(BigFloat.new(\"1234\"))\n    end\n\n    it \"raises for BigFloat from unsupported types\" do\n      expect_raises(JSON::ParseException) { BigFloat.from_json(\"true\") }\n      expect_raises(JSON::ParseException) { BigFloat.from_json(\"[]\") }\n      expect_raises(JSON::ParseException) { BigFloat.from_json(\"{}\") }\n    end\n\n    it \"does for UUID (hyphenated)\" do\n      uuid = UUID.from_json(\"\\\"ee843b26-56d8-472b-b343-0b94ed9077ff\\\"\")\n      uuid.should be_a(UUID)\n      uuid.should eq(UUID.new(\"ee843b26-56d8-472b-b343-0b94ed9077ff\"))\n    end\n\n    it \"does for UUID (hex)\" do\n      uuid = UUID.from_json(\"\\\"ee843b2656d8472bb3430b94ed9077ff\\\"\")\n      uuid.should be_a(UUID)\n      uuid.should eq(UUID.new(\"ee843b26-56d8-472b-b343-0b94ed9077ff\"))\n    end\n\n    it \"does for UUID (urn)\" do\n      uuid = UUID.from_json(\"\\\"urn:uuid:ee843b26-56d8-472b-b343-0b94ed9077ff\\\"\")\n      uuid.should be_a(UUID)\n      uuid.should eq(UUID.new(\"ee843b26-56d8-472b-b343-0b94ed9077ff\"))\n    end\n\n    it \"does for BigDecimal from int\" do\n      big = BigDecimal.from_json(\"1234\")\n      big.should be_a(BigDecimal)\n      big.should eq(BigDecimal.new(\"1234\"))\n    end\n\n    it \"does for BigDecimal from float\" do\n      big = BigDecimal.from_json(\"1234.05\")\n      big.should be_a(BigDecimal)\n      big.should eq(BigDecimal.new(\"1234.05\"))\n    end\n\n    it \"does for BigDecimal from string\" do\n      big = BigDecimal.from_json(%(\"1234.05\"))\n      big.should be_a(BigDecimal)\n      big.should eq(BigDecimal.new(\"1234.05\"))\n    end\n\n    it \"raises for BigDecimal from unsupported types\" do\n      expect_raises(JSON::ParseException) { BigDecimal.from_json(\"true\") }\n      expect_raises(JSON::ParseException) { BigDecimal.from_json(\"[]\") }\n      expect_raises(JSON::ParseException) { BigDecimal.from_json(\"{}\") }\n    end\n\n    describe \"Enum\" do\n      it \"normal enum\" do\n        JSONSpecEnum.from_json(%(\"one\")).should eq(JSONSpecEnum::One)\n        JSONSpecEnum.from_json(%(\"One\")).should eq(JSONSpecEnum::One)\n        JSONSpecEnum.from_json(%(\"two\")).should eq(JSONSpecEnum::Two)\n        JSONSpecEnum.from_json(%(\"ONE_HUNDRED\")).should eq(JSONSpecEnum::OneHundred)\n        JSONSpecEnum.from_json(%(\"ONE-HUNDRED\")).should eq(JSONSpecEnum::OneHundred)\n\n        expect_raises(JSON::ParseException, %(Unknown enum JSONSpecEnum value: \" one \")) do\n          JSONSpecEnum.from_json(%(\" one \"))\n        end\n\n        expect_raises(JSON::ParseException, %(Unknown enum JSONSpecEnum value: \"three\")) do\n          JSONSpecEnum.from_json(%(\"three\"))\n        end\n        expect_raises(JSON::ParseException, %(Unknown enum JSONSpecEnum value: \"three\")) do\n          NamedTuple(foo: JSONSpecEnum).from_json(%({\"foo\": \"three\", \"other\": 1}))\n        end\n        expect_raises(JSON::ParseException, %(Expected String but was Int)) do\n          JSONSpecEnum.from_json(%(1))\n        end\n        expect_raises(JSON::ParseException, %(Unknown enum JSONSpecEnum value: \"1\")) do\n          JSONSpecEnum.from_json(%(\"1\"))\n        end\n\n        expect_raises(JSON::ParseException, \"Expected String but was BeginObject\") do\n          JSONSpecEnum.from_json(%({}))\n        end\n        expect_raises(JSON::ParseException, \"Expected String but was BeginArray\") do\n          JSONSpecEnum.from_json(%([]))\n        end\n      end\n\n      it \"flag enum\" do\n        JSONSpecFlagEnum.from_json(%([\"one\"])).should eq(JSONSpecFlagEnum::One)\n        JSONSpecFlagEnum.from_json(%([\"One\"])).should eq(JSONSpecFlagEnum::One)\n        JSONSpecFlagEnum.from_json(%([\"one\", \"one\"])).should eq(JSONSpecFlagEnum::One)\n        JSONSpecFlagEnum.from_json(%([\"one\", \"two\"])).should eq(JSONSpecFlagEnum::One | JSONSpecFlagEnum::Two)\n        JSONSpecFlagEnum.from_json(%([\"one\", \"two\", \"one_hundred\"])).should eq(JSONSpecFlagEnum::All)\n        JSONSpecFlagEnum.from_json(%([])).should eq(JSONSpecFlagEnum::None)\n\n        expect_raises(JSON::ParseException, \"Expected String but was BeginArray\") do\n          JSONSpecFlagEnum.from_json(%([\"one\", [\"two\"]]))\n        end\n\n        expect_raises(JSON::ParseException, %(Unknown enum JSONSpecFlagEnum value: \"three\")) do\n          JSONSpecFlagEnum.from_json(%([\"one\", \"three\"]))\n        end\n        expect_raises(JSON::ParseException, %(Expected String but was Int)) do\n          JSONSpecFlagEnum.from_json(%([1, 2]))\n        end\n        expect_raises(JSON::ParseException, %(Expected String but was Int)) do\n          JSONSpecFlagEnum.from_json(%([\"one\", 2]))\n        end\n        expect_raises(JSON::ParseException, \"Expected BeginArray but was BeginObject\") do\n          JSONSpecFlagEnum.from_json(%({}))\n        end\n        expect_raises(JSON::ParseException, \"Expected BeginArray but was String\") do\n          JSONSpecFlagEnum.from_json(%(\"one\"))\n        end\n      end\n    end\n\n    it \"deserializes with root\" do\n      Int32.from_json(%({\"foo\": 1}), root: \"foo\").should eq(1)\n      Array(Int32).from_json(%({\"foo\": [1, 2]}), root: \"foo\").should eq([1, 2])\n    end\n\n    it \"deserializes union\" do\n      Array(Int32 | String).from_json(%([1, \"hello\"])).should eq([1, \"hello\"])\n    end\n\n    it \"deserializes union with bool (fast path)\" do\n      Union(Bool, Array(Int32)).from_json(%(true)).should be_true\n    end\n\n    {% for type in Int::Primitive.union_types %}\n      it \"deserializes union with {{type}} (fast path)\" do\n        Union({{type}}, Array(Int32)).from_json({{type}}::MAX.to_s).should eq({{type}}::MAX)\n      end\n    {% end %}\n\n    it \"deserializes union with Float32 (fast path)\" do\n      Union(Float32, Array(Int32)).from_json(%(1)).should eq(1)\n      Union(Float32, Array(Int32)).from_json(%(1.23)).should eq(1.23_f32)\n    end\n\n    it \"deserializes union with Float64 (fast path)\" do\n      Union(Float64, Array(Int32)).from_json(%(1)).should eq(1)\n      Union(Float64, Array(Int32)).from_json(%(1.23)).should eq(1.23)\n    end\n\n    it \"deserializes union of Int32 and Float64 (#7333)\" do\n      value = Union(Int32, Float64).from_json(\"1\")\n      value.should be_a(Int32)\n      value.should eq(1)\n\n      value = Union(Int32, Float64).from_json(\"1.0\")\n      value.should be_a(Float64)\n      value.should eq(1.0)\n    end\n\n    it \"deserializes unions of the same kind and remains stable\" do\n      str = [Int32::MAX, Int64::MAX].to_json\n      value = Array(Int32 | Int64).from_json(str)\n      value.all?(&.should(be_a(Int64)))\n    end\n\n    it \"deserializes Time\" do\n      Time.from_json(%(\"2016-11-16T09:55:48-03:00\")).to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))\n      Time.from_json(%(\"2016-11-16T09:55:48-0300\")).to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))\n      Time.from_json(%(\"20161116T095548-03:00\")).to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))\n    end\n\n    it \"deserializes Time::Location\" do\n      Time::Location.from_json(%(\"UTC\")).should eq(Time::Location.load(\"UTC\"))\n    end\n\n    describe \"parse exceptions\" do\n      it \"has correct location when raises in NamedTuple#from_json\" do\n        ex = expect_raises(JSON::ParseException) do\n          Array({foo: Int32, bar: String}).from_json <<-JSON\n            [\n              {\"foo\": 1}\n            ]\n            JSON\n        end\n        ex.location.should eq({2, 3})\n      end\n\n      it \"has correct location when raises in Union#from_json\" do\n        ex = expect_raises(JSON::ParseException) do\n          Array(Int32 | Bool).from_json <<-JSON\n            [\n              {\"foo\": \"bar\"}\n            ]\n            JSON\n        end\n        ex.location.should eq({2, 3})\n      end\n\n      it \"captures overflows for integer types\" do\n        ex = expect_raises(JSON::ParseException) do\n          Array(Int32).from_json <<-JSON\n            [\n              #{Int64::MAX.to_json}\n            ]\n            JSON\n        end\n        ex.location.should eq({2, 3})\n      end\n    end\n  end\n\n  describe \"to_json\" do\n    it \"does for Nil\" do\n      nil.to_json.should eq(\"null\")\n    end\n\n    it \"does for Bool\" do\n      true.to_json.should eq(\"true\")\n    end\n\n    it \"does for Int32\" do\n      1.to_json.should eq(\"1\")\n    end\n\n    it \"does for Int128\" do\n      Int128::MAX.to_json.should eq(Int128::MAX.to_s)\n    end\n\n    it \"does for Float64\" do\n      1.5.to_json.should eq(\"1.5\")\n    end\n\n    it \"raises if Float is NaN\" do\n      expect_raises JSON::Error, \"NaN not allowed in JSON\" do\n        (0.0/0.0).to_json\n      end\n    end\n\n    it \"raises if Float is infinity\" do\n      expect_raises JSON::Error, \"Infinity not allowed in JSON\" do\n        Float64::INFINITY.to_json\n      end\n    end\n\n    it \"does for String\" do\n      \"hello\".to_json.should eq(\"\\\"hello\\\"\")\n    end\n\n    it \"does for String with quote\" do\n      \"hel\\\"lo\".to_json.should eq(\"\\\"hel\\\\\\\"lo\\\"\")\n    end\n\n    it \"does for String with slash\" do\n      \"hel\\\\lo\".to_json.should eq(\"\\\"hel\\\\\\\\lo\\\"\")\n    end\n\n    it \"does for String with control codes\" do\n      \"\\b\".to_json.should eq(\"\\\"\\\\b\\\"\")\n      \"\\f\".to_json.should eq(\"\\\"\\\\f\\\"\")\n      \"\\n\".to_json.should eq(\"\\\"\\\\n\\\"\")\n      \"\\r\".to_json.should eq(\"\\\"\\\\r\\\"\")\n      \"\\t\".to_json.should eq(\"\\\"\\\\t\\\"\")\n      \"\\u{19}\".to_json.should eq(\"\\\"\\\\u0019\\\"\")\n    end\n\n    it \"does for String with control codes in a few places\" do\n      \"\\fab\".to_json.should eq(%q(\"\\fab\"))\n      \"ab\\f\".to_json.should eq(%q(\"ab\\f\"))\n      \"ab\\fcd\".to_json.should eq(%q(\"ab\\fcd\"))\n      \"ab\\fcd\\f\".to_json.should eq(%q(\"ab\\fcd\\f\"))\n      \"ab\\fcd\\fe\".to_json.should eq(%q(\"ab\\fcd\\fe\"))\n      \"\\u{19}ab\".to_json.should eq(%q(\"\\u0019ab\"))\n      \"ab\\u{19}\".to_json.should eq(%q(\"ab\\u0019\"))\n      \"ab\\u{19}cd\".to_json.should eq(%q(\"ab\\u0019cd\"))\n      \"ab\\u{19}cd\\u{19}\".to_json.should eq(%q(\"ab\\u0019cd\\u0019\"))\n      \"ab\\u{19}cd\\u{19}e\".to_json.should eq(%q(\"ab\\u0019cd\\u0019e\"))\n    end\n\n    it \"does for Path\" do\n      Path.posix(\"foo\", \"bar\", \"baz\").to_json.should eq(%(\"foo/bar/baz\"))\n      Path.windows(\"foo\", \"bar\", \"baz\").to_json.should eq(%(\"foo\\\\\\\\bar\\\\\\\\baz\"))\n    end\n\n    it \"does for Array\" do\n      [1, 2, 3].to_json.should eq(\"[1,2,3]\")\n    end\n\n    it \"does for StaticArray\" do\n      StaticArray[1, 2, 3].to_json.should eq(\"[1,2,3]\")\n    end\n\n    it \"does for Deque\" do\n      Deque.new([1, 2, 3]).to_json.should eq(\"[1,2,3]\")\n    end\n\n    it \"does for Set\" do\n      Set(Int32).new([1, 1, 2]).to_json.should eq(\"[1,2]\")\n    end\n\n    it \"does for Iterator\" do\n      (1..3).each.to_json.should eq(\"[1,2,3]\")\n    end\n\n    it \"does for Hash\" do\n      {\"foo\" => 1, \"bar\" => 2}.to_json.should eq(%({\"foo\":1,\"bar\":2}))\n    end\n\n    it \"does for Hash with symbol keys\" do\n      {:foo => 1, :bar => 2}.to_json.should eq(%({\"foo\":1,\"bar\":2}))\n    end\n\n    it \"does for Hash with int keys\" do\n      {1 => 2, 3 => 6}.to_json.should eq(%({\"1\":2,\"3\":6}))\n    end\n\n    it \"does for Hash with Float32 keys\" do\n      {1.2_f32 => 2, 3.4_f32 => 6}.to_json.should eq(%({\"1.2\":2,\"3.4\":6}))\n    end\n\n    it \"does for Hash with Float64 keys\" do\n      {1.2 => 2, 3.4 => 6}.to_json.should eq(%({\"1.2\":2,\"3.4\":6}))\n    end\n\n    it \"does for Hash with BigInt keys\" do\n      {123.to_big_i => 2}.to_json.should eq(%({\"123\":2}))\n    end\n\n    it \"does for Hash with newlines\" do\n      {\"foo\\nbar\" => \"baz\\nqux\"}.to_json.should eq(%({\"foo\\\\nbar\":\"baz\\\\nqux\"}))\n    end\n\n    it \"does for Tuple\" do\n      {1, \"hello\"}.to_json.should eq(%([1,\"hello\"]))\n    end\n\n    it \"does for NamedTuple\" do\n      {x: 1, y: \"hello\"}.to_json.should eq(%({\"x\":1,\"y\":\"hello\"}))\n    end\n\n    describe \"Enum\" do\n      it \"normal enum\" do\n        JSONSpecEnum::One.to_json.should eq %(\"one\")\n        JSONSpecEnum.from_json(JSONSpecEnum::One.to_json).should eq(JSONSpecEnum::One)\n\n        JSONSpecEnum::OneHundred.to_json.should eq %(\"one_hundred\")\n        JSONSpecEnum.from_json(JSONSpecEnum::OneHundred.to_json).should eq(JSONSpecEnum::OneHundred)\n\n        # undefined members can't be parsed back because the standard converter only accepts named\n        # members\n        JSONSpecEnum.new(42).to_json.should eq %(\"42\")\n      end\n\n      it \"flag enum\" do\n        JSONSpecFlagEnum::One.to_json.should eq %([\"one\"])\n        JSONSpecFlagEnum.from_json(JSONSpecFlagEnum::One.to_json).should eq(JSONSpecFlagEnum::One)\n\n        JSONSpecFlagEnum::OneHundred.to_json.should eq %([\"one_hundred\"])\n        JSONSpecFlagEnum.from_json(JSONSpecFlagEnum::OneHundred.to_json).should eq(JSONSpecFlagEnum::OneHundred)\n\n        combined = JSONSpecFlagEnum::OneHundred | JSONSpecFlagEnum::One\n        combined.to_json.should eq %([\"one\",\"one_hundred\"])\n        JSONSpecFlagEnum.from_json(combined.to_json).should eq(combined)\n\n        JSONSpecFlagEnum::None.to_json.should eq %([])\n        JSONSpecFlagEnum.from_json(JSONSpecFlagEnum::None.to_json).should eq(JSONSpecFlagEnum::None)\n\n        JSONSpecFlagEnum::All.to_json.should eq %([\"one\",\"two\",\"one_hundred\"])\n        JSONSpecFlagEnum.from_json(JSONSpecFlagEnum::All.to_json).should eq(JSONSpecFlagEnum::All)\n\n        JSONSpecFlagEnum.new(42).to_json.should eq %([\"two\"])\n      end\n    end\n\n    it \"does for BigInt\" do\n      big = BigInt.new(\"123456789123456789123456789123456789123456789\")\n      big.to_json.should eq(\"123456789123456789123456789123456789123456789\")\n    end\n\n    it \"does for BigFloat\" do\n      big = BigFloat.new(\"1234.567891011121314\")\n      big.to_json.should eq(\"1234.567891011121314\")\n    end\n\n    it \"does for BigDecimal\" do\n      big = BigDecimal.new(\"1234.567891011121314\")\n      big.to_json.should eq(\"1234.567891011121314\")\n    end\n\n    it \"does for UUID\" do\n      uuid = UUID.new(\"ee843b26-56d8-472b-b343-0b94ed9077ff\")\n      uuid.to_json.should eq(\"\\\"ee843b26-56d8-472b-b343-0b94ed9077ff\\\"\")\n    end\n  end\n\n  describe \"to_pretty_json\" do\n    it \"does for Nil\" do\n      nil.to_pretty_json.should eq(\"null\")\n    end\n\n    it \"does for Bool\" do\n      true.to_pretty_json.should eq(\"true\")\n    end\n\n    it \"does for Int32\" do\n      1.to_pretty_json.should eq(\"1\")\n    end\n\n    it \"does for Float64\" do\n      1.5.to_pretty_json.should eq(\"1.5\")\n    end\n\n    it \"does for String\" do\n      \"hello\".to_pretty_json.should eq(\"\\\"hello\\\"\")\n    end\n\n    it \"does for Array\" do\n      [1, 2, 3].to_pretty_json.should eq(\"[\\n  1,\\n  2,\\n  3\\n]\")\n    end\n\n    it \"does for nested Array\" do\n      [[1, 2, 3]].to_pretty_json.should eq(\"[\\n  [\\n    1,\\n    2,\\n    3\\n  ]\\n]\")\n    end\n\n    it \"does for empty Array\" do\n      ([] of Nil).to_pretty_json.should eq(\"[]\")\n    end\n\n    it \"does for Hash\" do\n      {\"foo\" => 1, \"bar\" => 2}.to_pretty_json.should eq(%({\\n  \"foo\": 1,\\n  \"bar\": 2\\n}))\n    end\n\n    it \"does for nested Hash\" do\n      {\"foo\" => {\"bar\" => 1}}.to_pretty_json.should eq(%({\\n  \"foo\": {\\n    \"bar\": 1\\n  }\\n}))\n    end\n\n    it \"does for empty Hash\" do\n      ({} of Nil => Nil).to_pretty_json.should eq(%({}))\n    end\n\n    it \"does for Array with indent\" do\n      [1, 2, 3].to_pretty_json(indent: \" \").should eq(\"[\\n 1,\\n 2,\\n 3\\n]\")\n    end\n\n    it \"does for nested Hash with indent\" do\n      {\"foo\" => {\"bar\" => 1}}.to_pretty_json(indent: \" \").should eq(%({\\n \"foo\": {\\n  \"bar\": 1\\n }\\n}))\n    end\n\n    describe \"Time\" do\n      it \"#to_json\" do\n        Time.utc(2016, 11, 16, 12, 55, 48).to_json.should eq(%(\"2016-11-16T12:55:48Z\"))\n        Time.local(2016, 11, 16, 12, 55, 48, location: Time::Location.fixed(7200)).to_json.should eq(%(\"2016-11-16T12:55:48+02:00\"))\n      end\n\n      it \"omit sub-second precision\" do\n        Time.utc(2016, 11, 16, 12, 55, 48, nanosecond: 123456789).to_json.should eq(%(\"2016-11-16T12:55:48Z\"))\n      end\n    end\n  end\n\n  describe \"Time::Location\" do\n    it \"#to_json\" do\n      Time::Location.load(\"UTC\").to_json.should eq(%(\"UTC\"))\n    end\n  end\n\n  it \"provide symmetric encoding and decoding for Union types\" do\n    a = 1.as(Float64 | Int32)\n    b = (Float64 | Int32).from_json(a.to_json)\n    a.class.should eq(Int32)\n    a.class.should eq(b.class)\n\n    c = 1.0.as(Float64 | Int32)\n    d = (Float64 | Int32).from_json(c.to_json)\n    c.class.should eq(Float64)\n    c.class.should eq(d.class)\n  end\nend\n"
  },
  {
    "path": "spec/std/kernel_spec.cr",
    "content": "require \"spec\"\nrequire \"./spec_helper\"\n\ndescribe \"PROGRAM_NAME\" do\n  it \"works for UTF-8 name\", tags: %w[slow] do\n    with_tempfile(\"source_file\") do |source_file|\n      if ENV[\"IN_NIX_SHELL\"]?\n        pending! \"Example is broken in Nix shell (#12332)\"\n      end\n\n      # MSYS2: gcc/ld doesn't support unicode paths\n      # https://github.com/msys2/MINGW-packages/issues/17812\n      {% if flag?(:windows) %}\n        if ENV[\"MSYSTEM\"]?\n          pending! \"Example is broken in MSYS2 shell\"\n        end\n      {% end %}\n\n      File.write(source_file, \"File.basename(PROGRAM_NAME).inspect(STDOUT)\")\n\n      compile_file(source_file, bin_name: \"×‽😂\") do |executable_file|\n        output = IO::Memory.new\n        Process.run(executable_file, output: output).success?.should be_true\n        output.to_s.should eq(File.basename(executable_file).inspect)\n      end\n    end\n  end\nend\n\ndescribe \"ARGV\" do\n  it \"accepts UTF-8 command-line arguments\", tags: %w[slow] do\n    with_tempfile(\"source_file\") do |source_file|\n      File.write(source_file, \"ARGV.inspect(STDOUT)\")\n\n      compile_file(source_file) do |executable_file|\n        args = [\"×‽😂\", \"あ×‽😂い\"]\n        output = IO::Memory.new\n        Process.run(executable_file, args, output: output).success?.should be_true\n        output.to_s.should eq(args.inspect)\n      end\n    end\n  end\nend\n\ndescribe \"exit\" do\n  it \"exits normally with status 0\", tags: %w[slow] do\n    status, _, _ = compile_and_run_source \"exit\"\n    status.success?.should be_true\n  end\n\n  it \"exits with given error code\", tags: %w[slow] do\n    status, _, _ = compile_and_run_source \"exit 42\"\n    status.success?.should be_false\n    status.exit_code.should eq(42)\n  end\n\n  it \"exists with Process::Status\", tags: %w[slow] do\n    status, _, _ = compile_and_run_source \"exit Process::Status.new(0)\"\n    status.success?.should be_true\n  end\n\n  it \"exists with abnormal status\", tags: %w[slow] do\n    status, _, _ = compile_and_run_source \"exit Process::Status.new({% if flag?(:unix) %}Signal::INT.value{% else %}LibC::STATUS_CONTROL_C_EXIT{% end %})\"\n    status.exit_reason.should eq Process::ExitReason::Interrupted\n  end\nend\n\ndescribe \"at_exit\" do\n  it \"runs handlers on normal program ending\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-CRYSTAL\n      at_exit do\n        print \"handler code.\"\n      end\n    CRYSTAL\n\n    status.success?.should be_true\n    output.should eq(\"handler code.\")\n  end\n\n  it \"runs handlers on explicit program ending\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-'CRYSTAL'\n      at_exit do |exit_code|\n        print \"handler code, exit code: #{exit_code}.\"\n      end\n\n      exit 42\n    CRYSTAL\n\n    status.exit_code.should eq(42)\n    output.should eq(\"handler code, exit code: 42.\")\n  end\n\n  it \"runs handlers in reverse order\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-CRYSTAL\n      at_exit do\n        print \"first handler code.\"\n      end\n\n      at_exit do\n        print \"second handler code.\"\n      end\n    CRYSTAL\n\n    status.success?.should be_true\n    output.should eq(\"second handler code.first handler code.\")\n  end\n\n  it \"runs all handlers maximum once\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-CRYSTAL\n      at_exit do\n        print \"first handler code.\"\n      end\n\n      at_exit do\n        print \"second handler code, explicit exit!\"\n        exit\n\n        print \"not executed.\"\n      end\n\n      at_exit do\n        print \"third handler code.\"\n      end\n    CRYSTAL\n\n    status.success?.should be_true\n    output.should eq(\"third handler code.second handler code, explicit exit!first handler code.\")\n  end\n\n  it \"allows handlers to change the exit code with explicit `exit` call\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-'CRYSTAL'\n      at_exit do |exit_code|\n        print \"first handler code, exit code: #{exit_code}.\"\n      end\n\n      at_exit do\n        print \"second handler code, re-exiting.\"\n        exit 42\n\n        print \"not executed.\"\n      end\n\n      at_exit do |exit_code|\n        print \"third handler code, exit code: #{exit_code}.\"\n      end\n    CRYSTAL\n\n    status.success?.should be_false\n    status.exit_code.should eq(42)\n    output.should eq(\"third handler code, exit code: 0.second handler code, re-exiting.first handler code, exit code: 42.\")\n  end\n\n  it \"allows handlers to change the exit code with explicit `exit` call (2)\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-'CRYSTAL'\n      at_exit do |exit_code|\n        print \"first handler code, exit code: #{exit_code}.\"\n      end\n\n      at_exit do\n        print \"second handler code, re-exiting.\"\n        exit 42\n\n        print \"not executed.\"\n      end\n\n      at_exit do |exit_code|\n        print \"third handler code, exit code: #{exit_code}.\"\n      end\n\n      exit 21\n    CRYSTAL\n\n    status.success?.should be_false\n    status.exit_code.should eq(42)\n    output.should eq(\"third handler code, exit code: 21.second handler code, re-exiting.first handler code, exit code: 42.\")\n  end\n\n  it \"changes final exit code when an handler raises an error\", tags: %w[slow] do\n    status, output, error = compile_and_run_source <<-'CRYSTAL'\n      at_exit do |exit_code|\n        print \"first handler code, exit code: #{exit_code}.\"\n      end\n\n      at_exit do\n        print \"second handler code, raising.\"\n        raise \"Raised from at_exit handler!\"\n\n        print \"not executed.\"\n      end\n\n      at_exit do |exit_code|\n        print \"third handler code, exit code: #{exit_code}.\"\n      end\n    CRYSTAL\n\n    status.success?.should be_false\n    status.exit_code.should eq(1)\n    output.should eq(\"third handler code, exit code: 0.second handler code, raising.first handler code, exit code: 1.\")\n    error.should contain(\"Error running at_exit handler: Raised from at_exit handler!\")\n  end\n\n  it \"shows unhandled exceptions after at_exit handlers\", tags: %w[slow] do\n    status, _, error = compile_and_run_source <<-CRYSTAL\n      at_exit do\n        STDERR.print \"first handler code.\"\n      end\n\n      at_exit do\n        STDERR.print \"second handler code.\"\n      end\n\n      raise \"Kaboom!\"\n    CRYSTAL\n\n    status.success?.should be_false\n    error.should contain(\"second handler code.first handler code.Unhandled exception: Kaboom!\")\n  end\n\n  it \"can get unhandled exception in at_exit handler\", tags: %w[slow] do\n    status, _, error = compile_and_run_source <<-CRYSTAL\n      at_exit do |_, ex|\n        STDERR.print ex.try &.message\n      end\n\n      raise \"Kaboom!\"\n    CRYSTAL\n\n    status.success?.should be_false\n    error.should contain(\"Kaboom!Unhandled exception: Kaboom!\")\n  end\n\n  it \"allows at_exit inside at_exit\", tags: %w[slow] do\n    status, output, _ = compile_and_run_source <<-CRYSTAL\n      at_exit do\n        print \"1\"\n        at_exit do\n          print \"2\"\n        end\n      end\n\n      at_exit do\n        print \"3\"\n        at_exit do\n          print \"4\"\n        end\n      end\n    CRYSTAL\n\n    status.success?.should be_true\n    output.should eq(\"3412\")\n  end\n\n  it \"prints unhandled exception with cause\", tags: %w[slow] do\n    status, _, error = compile_and_run_source <<-CRYSTAL\n      raise Exception.new(\"secondary\", cause: Exception.new(\"primary\"))\n    CRYSTAL\n\n    status.success?.should be_false\n    error.should contain \"Unhandled exception: secondary\"\n    error.should contain \"Caused by: primary\"\n  end\nend\n\n{% if flag?(:openbsd) %}\n  # FIXME: the segfault handler doesn't work on OpenBSD\n  pending \"hardware exception\"\n{% else %}\n  describe \"hardware exception\" do\n    it \"reports invalid memory access\", tags: %w[slow] do\n      status, _, error = compile_and_run_source <<-'CRYSTAL'\n        puts Pointer(Int64).null.value\n      CRYSTAL\n\n      status.success?.should be_false\n      error.should contain(\"Invalid memory access\")\n      error.should_not contain(\"Stack overflow\")\n    end\n\n    {% if flag?(:netbsd) %}\n      # FIXME: on netbsd the process crashes with SIGILL after receiving SIGSEGV\n      pending \"detects stack overflow on the main stack\"\n      pending \"detects stack overflow on a fiber stack\"\n    {% else %}\n      it \"detects stack overflow on the main stack\", tags: %w[slow] do\n        # This spec can take some time under FreeBSD where\n        # the default stack size is 0.5G.  Setting a\n        # smaller stack size with `ulimit -s 8192`\n        # will address this.\n        status, _, error = compile_and_run_source <<-'CRYSTAL'\n          def foo\n            y = StaticArray(Int8, 512).new(0)\n            foo\n          end\n          foo\n        CRYSTAL\n\n        status.success?.should be_false\n        error.should contain(\"Stack overflow\")\n      end\n\n      it \"detects stack overflow on a fiber stack\", tags: %w[slow] do\n        status, _, error = compile_and_run_source <<-'CRYSTAL'\n          def foo\n            y = StaticArray(Int8, 512).new(0)\n            foo\n          end\n\n          spawn do\n            foo\n          end\n\n          sleep 60.seconds\n        CRYSTAL\n\n        status.success?.should be_false\n        error.should contain(\"Stack overflow\")\n      end\n    {% end %}\n  end\n{% end %}\n"
  },
  {
    "path": "spec/std/levenshtein_spec.cr",
    "content": "require \"spec\"\nrequire \"levenshtein\"\n\ndescribe \"levenshtein\" do\n  it { Levenshtein.distance(\"algorithm\", \"altruistic\").should eq(6) }\n  it { Levenshtein.distance(\"1638452297\", \"444488444\").should eq(9) }\n  it { Levenshtein.distance(\"\", \"\").should eq(0) }\n  it { Levenshtein.distance(\"\", \"a\").should eq(1) }\n  it { Levenshtein.distance(\"aaapppp\", \"\").should eq(7) }\n  it { Levenshtein.distance(\"frog\", \"fog\").should eq(1) }\n  it { Levenshtein.distance(\"fly\", \"ant\").should eq(3) }\n  it { Levenshtein.distance(\"elephant\", \"hippo\").should eq(7) }\n  it { Levenshtein.distance(\"hippo\", \"elephant\").should eq(7) }\n  it { Levenshtein.distance(\"hippo\", \"zzzzzzzz\").should eq(8) }\n  it { Levenshtein.distance(\"hello\", \"hallo\").should eq(1) }\n  it { Levenshtein.distance(\"こんにちは\", \"こんちは\").should eq(1) }\n  it { Levenshtein.distance(\"한자\", \"漢字\").should eq(2) }\n  it { Levenshtein.distance(\"abc\", \"cba\").should eq(2) }\n  it { Levenshtein.distance(\"かんじ\", \"じんか\").should eq(2) }\n  it { Levenshtein.distance(\"\", \"かんじ\").should eq(3) }\n\n  it \"finds with finder\" do\n    finder = Levenshtein::Finder.new \"hallo\"\n    finder.test \"hay\"\n    finder.test \"hall\"\n    finder.test \"hallo world\"\n    finder.best_match.should eq(\"hall\")\n  end\n\n  it \"finds with finder and other values\" do\n    finder = Levenshtein::Finder.new \"hallo\"\n    finder.test \"hay\", \"HAY\"\n    finder.test \"hall\", \"HALL\"\n    finder.test \"hallo world\", \"HALLO WORLD\"\n    finder.best_match.should eq(\"HALL\")\n  end\nend\n"
  },
  {
    "path": "spec/std/llvm/llvm_spec.cr",
    "content": "require \"spec\"\nrequire \"llvm\"\n\ndescribe LLVM do\n  it \".version\" do\n    LLVM.version.should eq LibLLVM::VERSION\n  end\n\n  describe \".normalize_triple\" do\n    it \"works\" do\n      LLVM.normalize_triple(\"x86_64-apple-macos\").should eq(\"x86_64-apple-macos\")\n    end\n\n    it \"substitutes unknown for empty components\" do\n      LLVM.normalize_triple(\"x86_64-linux-gnu\").should eq(\"x86_64-unknown-linux-gnu\")\n    end\n  end\n\n  it \".default_target_triple\" do\n    triple = LLVM.default_target_triple\n    {% if flag?(:darwin) %}\n      triple.should match(/-apple-(darwin|macosx)/)\n    {% elsif flag?(:android) %}\n      triple.should match(/-android$/)\n    {% elsif flag?(:linux) %}\n      triple.should match(/-linux/)\n    {% elsif flag?(:windows) %}\n      triple.should match(/-windows-/)\n    {% elsif flag?(:freebsd) %}\n      triple.should match(/-freebsd/)\n    {% elsif flag?(:openbsd) %}\n      triple.should match(/-openbsd/)\n    {% elsif flag?(:dragonfly) %}\n      triple.should match(/-dragonfly/)\n    {% elsif flag?(:netbsd) %}\n      triple.should match(/-netbsd/)\n    {% elsif flag?(:solaris) %}\n      triple.should match(/-solaris$/)\n    {% elsif flag?(:wasi) %}\n      triple.should match(/-wasi/)\n    {% else %}\n      pending! \"Unknown operating system\"\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/std/llvm/type_spec.cr",
    "content": "require \"spec\"\nrequire \"llvm\"\n\ndescribe LLVM::Type do\n  describe \".const_int\" do\n    it \"support Int64\" do\n      ctx = LLVM::Context.new\n      ctx.int(64).const_int(Int64::MAX).to_s.should eq(\"i64 9223372036854775807\")\n    end\n\n    it \"support Int128\" do\n      ctx = LLVM::Context.new\n      ctx.int(128).const_int(Int128::MAX).to_s.should eq(\"i128 170141183460469231731687303715884105727\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/broadcast_backend_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\n\nprivate def s(value : Log::Severity)\n  value\nend\n\ndescribe Log::BroadcastBackend do\n  it \"writes to the backend based on level\" do\n    main = Log::BroadcastBackend.new\n    backend_a = Log::MemoryBackend.new\n    backend_b = Log::MemoryBackend.new\n\n    main.append(backend_a, s(:info))\n    main.append(backend_b, s(:error))\n\n    debug_entry = Log::Entry.new(\"\", s(:debug), \"\", Log::Metadata.empty, nil)\n    info_entry = Log::Entry.new(\"\", s(:info), \"\", Log::Metadata.empty, nil)\n    error_entry = Log::Entry.new(\"\", s(:error), \"\", Log::Metadata.empty, nil)\n\n    main.write debug_entry\n    main.write info_entry\n    main.write error_entry\n\n    backend_a.entries.should eq([info_entry, error_entry])\n    backend_b.entries.should eq([error_entry])\n  end\n\n  it \"overwriting log level overwrites to all backends\" do\n    main = Log::BroadcastBackend.new\n    backend_a = Log::MemoryBackend.new\n    backend_b = Log::MemoryBackend.new\n\n    main.append(backend_a, s(:info))\n    main.append(backend_b, s(:error))\n\n    log = Log.new(\"\", main, s(:info))\n    log.level = s(:info)\n\n    log.info { \"lorem\" }\n\n    backend_a.entries.should_not be_empty\n    backend_b.entries.should_not be_empty\n\n    backend_a.entries.clear\n    backend_b.entries.clear\n\n    log.debug { \"lorem\" }\n\n    backend_a.entries.should be_empty\n    backend_b.entries.should be_empty\n\n    backend_a.entries.clear\n    backend_b.entries.clear\n\n    main.level = nil\n    log.info { \"lorem\" }\n\n    backend_a.entries.should_not be_empty\n    backend_b.entries.should be_empty\n  end\n\n  describe \"#min_level\" do\n    it \"on empty\" do\n      main = Log::BroadcastBackend.new\n      main.min_level.should eq(s(:none))\n    end\n\n    it \"single backend\" do\n      main = Log::BroadcastBackend.new\n      main.append(Log::MemoryBackend.new, s(:warn))\n\n      main.min_level.should eq(s(:warn))\n    end\n\n    it \"multiple backends\" do\n      main = Log::BroadcastBackend.new\n      main.append(Log::MemoryBackend.new, s(:info))\n      main.append(Log::MemoryBackend.new, s(:warn))\n\n      main.min_level.should eq(s(:info))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\n\nprivate def m(source : String, pattern : String) : Bool\n  Log::Builder.matches(source, pattern)\nend\n\nprivate def s(value : Log::Severity)\n  value\nend\n\ndescribe Log::Builder do\n  it \"creates a log with nil backend\" do\n    builder = Log::Builder.new\n\n    log = builder.for(\"db\")\n\n    log.backend.should be_nil\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:none))\n  end\n\n  it \"creates a log with single backend\" do\n    builder = Log::Builder.new\n    builder.bind(\"db\", :fatal, Log::MemoryBackend.new)\n\n    log = builder.for(\"db\")\n\n    log.backend.should be_a(Log::MemoryBackend)\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:fatal))\n  end\n\n  it \"creates a log with broadcast backend\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    b = Log::MemoryBackend.new\n    builder.bind(\"db\", :fatal, a)\n    builder.bind(\"db\", :info, b)\n\n    log = builder.for(\"db\")\n\n    backend = log.backend.should be_a(Log::BroadcastBackend)\n    backend.@backends[a].should eq(s(:fatal))\n    backend.@backends[b].should eq(s(:info))\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:info))\n  end\n\n  it \"does not alter user-provided broadcast backend\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    b = Log::MemoryBackend.new\n\n    broadcast = Log::BroadcastBackend.new\n    broadcast.append(a, :fatal)\n    previous_backends = broadcast.@backends.dup\n\n    builder.bind(\"db\", :trace, broadcast)\n    builder.bind(\"db\", :info, b)\n\n    log = builder.for(\"db\")\n\n    backend = log.backend.should be_a(Log::BroadcastBackend)\n    backend.should_not be broadcast\n    broadcast.@backends.should eq(previous_backends)\n  end\n\n  it \"creates a log for broadcast backend\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    b = Log::MemoryBackend.new\n\n    broadcast = Log::BroadcastBackend.new\n    broadcast.append(a, :fatal)\n\n    builder.bind(\"db\", :trace, broadcast)\n    builder.bind(\"db\", :info, b)\n\n    log = builder.for(\"db\")\n\n    backend = log.backend.should be_a(Log::BroadcastBackend)\n    backend.@backends.should eq({broadcast => s(:trace), b => s(:info)})\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:trace))\n  end\n\n  it \"creates a log for same broadcast backend added multiple times\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n\n    broadcast = Log::BroadcastBackend.new\n    broadcast.append(a, :fatal)\n\n    builder.bind(\"db\", :trace, broadcast)\n    builder.bind(\"db\", :info, broadcast)\n\n    log = builder.for(\"db\")\n\n    backend = log.backend.should be_a(Log::BroadcastBackend)\n    backend.should be(broadcast)\n    backend.@backends.should eq({a => s(:fatal)})\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:info))\n  end\n\n  it \"uses last level for a source x backend\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    b = Log::MemoryBackend.new\n    builder.bind(\"db\", :fatal, a)\n    builder.bind(\"db\", :info, b)\n    builder.bind(\"db\", :debug, a)\n\n    log = builder.for(\"db\")\n\n    backend = log.backend.should be_a(Log::BroadcastBackend)\n    backend.@backends[a].should eq(s(:debug))\n    backend.@backends[b].should eq(s(:info))\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:debug))\n  end\n\n  it \"uses last level for a source x backend (single-backend)\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    builder.bind(\"db\", :fatal, a)\n    builder.bind(\"db.*\", :debug, a)\n\n    log = builder.for(\"db\")\n\n    log.backend.should eq(a)\n    log.source.should eq(\"db\")\n    log.level.should eq(s(:debug))\n  end\n\n  it \"returns log with backend if pattern matches\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    builder.bind(\"db.*\", :fatal, a)\n\n    log = builder.for(\"db.pool\")\n\n    log.backend.should eq(a)\n    log.source.should eq(\"db.pool\")\n    log.level.should eq(s(:fatal))\n  end\n\n  it \"returns log without backend if pattern does not match\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    builder.bind(\"db.*\", :fatal, a)\n\n    log = builder.for(\"\")\n\n    log.backend.should be_nil\n    log.source.should eq(\"\")\n    log.level.should eq(s(:none))\n  end\n\n  it \"can turn off parent and allow child source\" do\n    builder = Log::Builder.new\n    a = Log::MemoryBackend.new\n    builder.bind(\"*\", :fatal, a)\n    builder.bind(\"db.*\", :warn, a)\n    builder.bind(\"db\", :error, a)\n    builder.bind(\"db.pool\", :none, a)\n\n    builder.for(\"\").level.should eq(s(:fatal))\n    builder.for(\"db\").level.should eq(s(:error))\n    builder.for(\"db.query\").level.should eq(s(:warn))\n    builder.for(\"db.pool\").level.should eq(s(:none))\n  end\n\n  it \"returns same instance\" do\n    builder = Log::Builder.new\n\n    builder.for(\"\").should be(builder.for(\"\"))\n    builder.for(\"db\").should be(builder.for(\"db\"))\n    builder.for(\"\").should_not be(builder.for(\"db\"))\n  end\n\n  it \"can reconfigures existing logs\" do\n    builder = Log::Builder.new\n    log = builder.for(\"\")\n    log.backend.should be_nil\n    log.level.should eq(s(:none))\n\n    a = Log::MemoryBackend.new\n    builder.bind(\"*\", :warn, a)\n\n    log.backend.should be(a)\n    log.level.should eq(s(:warn))\n  end\n\n  it \"removes all logs backends on .clear\" do\n    builder = Log::Builder.new\n    builder.bind(\"*\", :fatal, Log::MemoryBackend.new)\n    log = builder.for(\"\")\n    log_db = builder.for(\"db\")\n\n    log.backend.should_not be_nil\n    log.level.should eq(s(:fatal))\n    log_db.backend.should_not be_nil\n    log_db.level.should eq(s(:fatal))\n\n    builder.clear\n\n    log.backend.should be_nil\n    log.level.should eq(s(:none))\n    log_db.backend.should be_nil\n    log_db.level.should eq(s(:none))\n  end\n\n  it \"should allow collection of unreferenced logs\" do\n    builder = Log::Builder.new\n    10.times do |i|\n      builder.for(\"a.#{i}\")\n    end\n    original_size = builder.@logs.values.size\n    original_size.should be >= 10\n    GC.collect\n    builder.@logs.values.count(&.value.nil?).should be > 0\n\n    # force a cleanup\n    builder.bind(\"a.9\", :info, Log::MemoryBackend.new)\n    builder.@logs.values.size.should be < original_size\n  end\n\n  it \"should allow recreation of deallocated logs\" do\n    builder = Log::Builder.new\n    10.times do |i|\n      builder.for(\"a.#{i}\")\n    end\n    GC.collect\n    10.times do |i|\n      builder.for(\"a.#{i}\")\n    end\n    builder.@logs.values.size.should eq(10)\n    builder.@logs.values.count(&.value.nil?).should eq(0)\n  end\n\n  describe \".matches\" do\n    it \"on top-level\" do\n      m(\"\", \"\").should be_true\n      m(\"\", \"db\").should be_false\n      m(\"\", \"db.pool\").should be_false\n      m(\"\", \"*\").should be_true\n    end\n\n    it \"on first level\" do\n      m(\"db\", \"\").should be_false\n      m(\"db\", \"db\").should be_true\n      m(\"db\", \"*\").should be_true\n      m(\"db\", \"db.*\").should be_true\n      m(\"db\", \"db.pool\").should be_false\n      m(\"db\", \"other\").should be_false\n      m(\"db\", \"other.*\").should be_false\n    end\n\n    it \"on second level\" do\n      m(\"db.pool\", \"\").should be_false\n      m(\"db.pool\", \"db\").should be_false\n      m(\"db.pool\", \"*\").should be_true\n      m(\"db.pool\", \"db.*\").should be_true\n      m(\"db.pool\", \"db.pool\").should be_true\n      m(\"db.pool\", \"other\").should be_false\n      m(\"db.pool\", \"other.*\").should be_false\n    end\n\n    it \"on third level\" do\n      m(\"db.pool.foo\", \"db.*\").should be_true\n      m(\"db.pool.foo\", \"db.pool.*\").should be_true\n      m(\"db.pool.foo\", \"db.pool\").should be_false\n    end\n\n    it \"avoids prefix collision\" do\n      m(\"dbnot\", \"db.*\").should be_false\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/context_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\n\nprivate def m(value)\n  Log::Metadata.build(value)\nend\n\ndescribe Log do\n  before_each do\n    Log.context.clear\n  end\n\n  after_each do\n    Log.context.clear\n  end\n\n  describe \".context\" do\n    it \"can be set and cleared\" do\n      Log.context.metadata.should eq(Log::Metadata.new)\n\n      Log.context.set a: 1\n      Log.context.metadata.should eq(m({a: 1}))\n\n      Log.context.clear\n      Log.context.metadata.should eq(Log::Metadata.new)\n    end\n\n    it \"is extended by set\" do\n      Log.context.set a: 1\n      Log.context.set b: 2\n      Log.context.metadata.should eq(m({a: 1, b: 2}))\n    end\n\n    it \"existing keys are overwritten by set\" do\n      Log.context.set a: 1, b: 1\n      Log.context.set b: 2, c: 3\n      Log.context.metadata.should eq(m({a: 1, b: 2, c: 3}))\n    end\n\n    it \"is per fiber\" do\n      Log.context.set a: 1\n      done = Channel(Nil).new\n\n      spawn do\n        Log.context.metadata.should eq(Log::Metadata.new)\n        Log.context.set b: 2\n        Log.context.metadata.should eq(m({b: 2}))\n\n        done.receive\n        done.receive\n      end\n\n      done.send nil\n      Log.context.metadata.should eq(m({a: 1}))\n      done.send nil\n    end\n\n    it \"is assignable from a hash with symbol keys\" do\n      Log.context.set a: 1\n      extra = {:b => 2}\n      Log.context.set extra\n      Log.context.metadata.should eq(m({a: 1, b: 2}))\n    end\n\n    it \"is assignable from a named tuple\" do\n      Log.context.set a: 1\n      extra = {b: 2}\n      Log.context.set extra\n      Log.context.metadata.should eq(m({a: 1, b: 2}))\n    end\n  end\n\n  describe \"#with_context\" do\n    it \"with arguments restores context after the block\" do\n      Log.context.set a: 1\n      log = Log.for(\"temp\")\n\n      log.with_context(b: 2) do\n        log.context.set c: 3\n        log.context.metadata.should eq(m({a: 1, b: 2, c: 3}))\n      end\n\n      log.context.metadata.should eq(m({a: 1}))\n    end\n\n    it \"restores context after the block\" do\n      Log.context.set a: 1\n      log = Log.for(\"temp\")\n\n      log.with_context do\n        log.context.set b: 2\n        log.context.metadata.should eq(m({a: 1, b: 2}))\n      end\n\n      log.context.metadata.should eq(m({a: 1}))\n    end\n  end\n\n  describe \".with_context\" do\n    it \"with arguments restores context after the block\" do\n      Log.context.set a: 1\n      Log.with_context(b: 2) do\n        Log.context.set c: 3\n        Log.context.metadata.should eq(m({a: 1, b: 2, c: 3}))\n      end\n\n      Log.context.metadata.should eq(m({a: 1}))\n    end\n\n    it \"restores context after the block\" do\n      Log.context.set a: 1\n\n      Log.with_context do\n        Log.context.set b: 2\n        Log.context.metadata.should eq(m({a: 1, b: 2}))\n      end\n\n      Log.context.metadata.should eq(m({a: 1}))\n    end\n\n    it \"assigns context via a hash with symbol keys\" do\n      Log.context.set a: 1\n      extra = {:b => 2}\n      Log.with_context(extra) do\n        Log.context.set c: 3\n        Log.context.metadata.should eq(m({a: 1, b: 2, c: 3}))\n      end\n\n      Log.context.metadata.should eq(m({a: 1}))\n    end\n\n    it \"assigns context via a named tuple\" do\n      Log.context.set a: 1\n      extra = {b: 2}\n      Log.with_context(extra) do\n        Log.context.set c: 3\n        Log.context.metadata.should eq(m({a: 1, b: 2, c: 3}))\n      end\n\n      Log.context.metadata.should eq(m({a: 1}))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/dispatch_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\nrequire \"../../support/retry\"\n\nclass Log\n  describe Dispatcher do\n    it \"create dispatcher from enum\" do\n      Dispatcher.for(:direct).should eq(DirectDispatcher)\n      Dispatcher.for(:async).should be_a(AsyncDispatcher)\n      Dispatcher.for(:sync).should be_a(SyncDispatcher)\n    end\n  end\n\n  describe DirectDispatcher do\n    it \"dispatches entry\" do\n      backend = Log::MemoryBackend.new\n      backend.dispatcher = DirectDispatcher\n      backend.dispatch Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      backend.entries.size.should eq(1)\n    end\n  end\n\n  describe SyncDispatcher do\n    it \"dispatches entry\" do\n      backend = Log::MemoryBackend.new\n      backend.dispatcher = SyncDispatcher.new\n      backend.dispatch Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      backend.entries.size.should eq(1)\n    end\n  end\n\n  describe AsyncDispatcher do\n    it \"dispatches entry\" do\n      backend = Log::MemoryBackend.new\n      backend.dispatcher = AsyncDispatcher.new\n      backend.dispatch Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      retry { backend.entries.size.should eq(1) }\n    end\n\n    it \"wait for entries to flush before closing\" do\n      backend = Log::MemoryBackend.new\n      backend.dispatcher = AsyncDispatcher.new\n      backend.dispatch Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      backend.close\n      backend.entries.size.should eq(1)\n    end\n\n    it \"can be closed twice\" do\n      backend = Log::MemoryBackend.new\n      backend.dispatcher = AsyncDispatcher.new\n      backend.dispatch Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      backend.close\n      backend.close\n      backend.entries.size.should eq(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/env_config_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\nrequire \"log/spec\"\nrequire \"../../support/env\"\n\nprivate def s(value : Log::Severity)\n  value\nend\n\ndescribe \"Log.setup_from_env\" do\n  after_all do\n    # Setup logging in specs (again) since these specs perform Log.setup\n    Spec.cli.log_setup\n  end\n\n  describe \"backend\" do\n    it \"is a IOBackend\" do\n      with_env \"LOG_LEVEL\": nil do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder)\n\n        builder.for(\"\").backend.should be_a(Log::IOBackend)\n      end\n    end\n\n    it \"can be changed\" do\n      with_env \"LOG_LEVEL\": nil do\n        builder = Log::Builder.new\n        backend = Log::MemoryBackend.new\n        Log.setup_from_env(builder: builder, backend: backend)\n\n        builder.for(\"\").backend.should be(backend)\n      end\n    end\n  end\n\n  describe \"default_level\" do\n    it \"is info\" do\n      with_env \"LOG_LEVEL\": nil do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder)\n\n        builder.for(\"\").initial_level.should eq(s(:info))\n      end\n    end\n\n    it \"is used if no LOG_LEVEL is set\" do\n      with_env \"LOG_LEVEL\": nil do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder, default_level: :warn)\n\n        builder.for(\"\").initial_level.should eq(s(:warn))\n      end\n    end\n\n    it \"is used if LOG_LEVEL is empty\" do\n      with_env \"LOG_LEVEL\": \"\" do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder)\n\n        builder.for(\"\").initial_level.should eq(s(:info))\n      end\n    end\n\n    it \"is used if LOG_LEVEL is just whitespace\" do\n      with_env \"LOG_LEVEL\": \"  \" do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder)\n\n        builder.for(\"\").initial_level.should eq(s(:info))\n      end\n    end\n\n    it \"is not used if LOG_LEVEL is set\" do\n      with_env \"LOG_LEVEL\": \"DEBUG\" do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder, default_level: :error)\n\n        builder.for(\"\").initial_level.should eq(s(:debug))\n      end\n    end\n  end\n\n  describe \"default_sources\" do\n    it \"is *\" do\n      with_env \"LOG_LEVEL\": nil do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder)\n\n        builder.for(\"lorem.ipsum\").backend.should_not be_nil\n        builder.for(\"\").backend.should_not be_nil\n      end\n    end\n\n    it \"is used\" do\n      with_env \"LOG_LEVEL\": nil do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder, default_sources: \"foo.*\")\n\n        builder.for(\"\").backend.should be_nil\n        builder.for(\"lorem.ipsum\").backend.should be_nil\n\n        builder.for(\"foo\").backend.should_not be_nil\n        builder.for(\"foo.bar\").backend.should_not be_nil\n      end\n    end\n\n    it \"splits sources by comma\" do\n      with_env \"LOG_LEVEL\": \"info\" do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder, default_sources: \"db, , foo.*  \")\n\n        builder.for(\"db\").backend.should_not be_nil\n        builder.for(\"\").backend.should_not be_nil\n        builder.for(\"foo\").backend.should_not be_nil\n        builder.for(\"foo.bar.baz\").backend.should_not be_nil\n        builder.for(\"other\").backend.should be_nil\n      end\n    end\n  end\n\n  it \"raises on invalid level\" do\n    expect_raises(ArgumentError) do\n      with_env \"LOG_LEVEL\": \"invalid\" do\n        builder = Log::Builder.new\n        Log.setup_from_env(builder: builder)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/format_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\n\nclass Log\n  describe ShortFormat do\n    it \"formats an entry\" do\n      entry = Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      io = IO::Memory.new\n      ShortFormat.format(entry, io)\n      io.to_s.should match(/^[\\d\\-.:TZ]+\\s* INFO - source: message$/)\n    end\n\n    it \"hides the source if empty\" do\n      entry = Entry.new(\"\", :info, \"message\", Log::Metadata.empty, nil)\n      io = IO::Memory.new\n      ShortFormat.format(entry, io)\n      io.to_s.should match(/^[\\d\\-.:TZ]+\\s* INFO - message$/)\n    end\n\n    it \"shows the context data\" do\n      entry = Log.with_context do\n        Log.context.set a: 1, b: 2\n        Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      end\n      io = IO::Memory.new\n      ShortFormat.format(entry, io)\n      io.to_s.should match(/^[\\d\\-.:TZ]+\\s* INFO - source: message -- a: 1, b: 2$/)\n    end\n\n    it \"shows context and entry data\" do\n      entry = Log.with_context do\n        Log.context.set a: 1, b: 2\n        Entry.new(\"source\", :info, \"message\", Log::Metadata.build({c: 3, d: 4}), nil)\n      end\n      io = IO::Memory.new\n      ShortFormat.format(entry, io)\n      io.to_s.should match(/^[\\d\\-.:TZ]+\\s* INFO - source: message -- c: 3, d: 4 -- a: 1, b: 2$/)\n    end\n\n    it \"appends the exception\" do\n      exception = expect_raises(Exception) { raise \"foo\" }\n      entry = Entry.new(\"source\", :error, \"message\", Log::Metadata.empty, exception)\n      io = IO::Memory.new\n      ShortFormat.format(entry, io)\n      io.rewind\n      io.gets.should match(/^[\\d\\-.:TZ]+\\s* ERROR - source: message$/)\n      io.gets_to_end.should eq(exception.inspect_with_backtrace)\n    end\n  end\n\n  describe ProcFormatter do\n    it \"formats\" do\n      entry = Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      io = IO::Memory.new\n      formatter = Formatter.new do |entry, io|\n        io << \"[\" << entry.message << \"]\"\n      end\n      formatter.format(entry, io)\n      io.to_s.should eq(\"[message]\")\n    end\n  end\n\n  define_formatter TestFormatter, \"#{severity} #{source(before: '[', after: \"] \")}#{progname} #{message}\" \\\n                                  \"#{context(before: \" (\", after: ')')}#{exception}\"\n  Log.progname = \"test\"\n\n  describe TestFormatter do\n    it \"formats\" do\n      exception = expect_raises(Exception) { raise \"foo\" }\n      entry = Log.with_context do\n        Log.context.set a: 1, b: 2\n        Entry.new(\"source\", :info, \"message\", Log::Metadata.empty, nil)\n      end\n      io = IO::Memory.new\n      TestFormatter.format(entry, io)\n      io.puts\n      TestFormatter.format(Entry.new(\"\", :info, \"message\", Log::Metadata.empty, nil), io)\n      io.puts\n      TestFormatter.format(Entry.new(\"source\", :error, \"Oh, no\", Log::Metadata.empty, exception), io)\n      io.rewind\n\n      io.gets.should eq(\"  INFO [source] test message (a: 1, b: 2)\")\n      io.gets.should eq(\"  INFO test message\")\n      io.gets.should eq(\" ERROR [source] test Oh, no\")\n      io.gets_to_end.should eq(exception.inspect_with_backtrace)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/io_backend_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"log\"\n\nprivate def s(value : Log::Severity)\n  value\nend\n\nprivate def io_logger(*, stdout : IO, config = nil, source : String = \"\")\n  builder = Log::Builder.new\n  backend = Log::IOBackend.new\n  backend.io = stdout\n  builder.bind(\"*\", s(:info), backend)\n  builder.for(source)\nend\n\ndescribe Log::IOBackend do\n  it \"creates with defaults\" do\n    backend = Log::IOBackend.new\n    backend.io.should eq(STDOUT)\n    backend.formatter.should eq(Log::ShortFormat)\n    backend.dispatcher.should be_a(Log::AsyncDispatcher)\n  end\n\n  it \"logs messages\" do\n    IO.pipe do |r, w|\n      logger = io_logger(stdout: w)\n      logger.debug { \"debug:skip\" }\n      logger.info { \"info:show\" }\n\n      logger.level = s(:debug)\n      logger.debug { \"debug:show\" }\n\n      logger.level = s(:warn)\n      logger.debug { \"debug:skip:again\" }\n      logger.info { \"info:skip\" }\n      logger.error { \"error:show\" }\n\n      r.gets.should match(/info:show/)\n      r.gets.should match(/debug:show/)\n      r.gets.should match(/error:show/)\n    end\n  end\n\n  it \"logs context\" do\n    IO.pipe do |r, w|\n      logger = io_logger(stdout: w)\n      Log.context.clear\n      Log.with_context do\n        Log.context.set foo: \"bar\"\n        logger.info { \"info:show\" }\n      end\n\n      r.gets.should match(/info:show -- foo: \"bar\"/)\n    end\n  end\n\n  it \"logs any object\" do\n    IO.pipe do |r, w|\n      logger = io_logger(stdout: w)\n      logger.info { 12345 }\n\n      r.gets.should match(/12345/)\n    end\n  end\n\n  it \"formats message\" do\n    IO.pipe do |r, w|\n      logger = io_logger(stdout: w, source: \"db.pool\")\n      logger.warn { \"message\" }\n\n      r.gets(chomp: false).should match(/.+? WARN - db.pool: message\\n/)\n    end\n  end\n\n  it \"uses custom formatter\" do\n    IO.pipe do |r, w|\n      logger = io_logger(stdout: w)\n      logger.backend.as(Log::IOBackend).formatter = Log::Formatter.new do |entry, io|\n        io << entry.severity.to_s[0].upcase << \": \" << entry.message\n      end\n      logger.warn { \"message\" }\n\n      r.gets(chomp: false).should eq(\"W: message\\n\")\n    end\n  end\n\n  it \"allows setting formatter in initializer\" do\n    formatter = Log::Formatter.new { |_entry, io| io }\n    backend = Log::IOBackend.new(formatter: formatter)\n\n    log = Log.new(\"foo\", backend, :info)\n\n    log.backend.should eq(backend)\n  end\n\n  it \"yields message\" do\n    IO.pipe do |r, w|\n      logger = io_logger(stdout: w, source: \"db\")\n      logger.error { \"message\" }\n      logger.fatal { \"another message\" }\n\n      r.gets(chomp: false).should match(/ERROR - db: message\\n/)\n      r.gets(chomp: false).should match(/FATAL - db: another message\\n/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/log_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\n\nprivate def s(value : Log::Severity)\n  value\nend\n\nprivate def m(value)\n  Log::Metadata.build(value)\nend\n\ndescribe Log do\n  before_each do\n    Log.context.clear\n  end\n\n  after_each do\n    Log.context.clear\n  end\n\n  describe Log::Severity do\n    it \"values are ordered\" do\n      s(:trace).should be < s(:debug)\n      s(:debug).should be < s(:info)\n      s(:info).should be < s(:notice)\n      s(:notice).should be < s(:warn)\n      s(:warn).should be < s(:error)\n      s(:error).should be < s(:fatal)\n      s(:fatal).should be < s(:none)\n    end\n\n    it \"parses\" do\n      Log::Severity.parse(\"trace\").should eq s(:trace)\n      Log::Severity.parse(\"debug\").should eq s(:debug)\n      Log::Severity.parse(\"info\").should eq s(:info)\n      Log::Severity.parse(\"notice\").should eq s(:notice)\n      Log::Severity.parse(\"warn\").should eq s(:warn)\n      Log::Severity.parse(\"error\").should eq s(:error)\n      Log::Severity.parse(\"fatal\").should eq s(:fatal)\n      Log::Severity.parse(\"none\").should eq s(:none)\n\n      Log::Severity.parse(\"TRACE\").should eq s(:trace)\n      Log::Severity.parse(\"DEBUG\").should eq s(:debug)\n      Log::Severity.parse(\"INFO\").should eq s(:info)\n      Log::Severity.parse(\"NOTICE\").should eq s(:notice)\n      Log::Severity.parse(\"WARN\").should eq s(:warn)\n      Log::Severity.parse(\"ERROR\").should eq s(:error)\n      Log::Severity.parse(\"FATAL\").should eq s(:fatal)\n      Log::Severity.parse(\"NONE\").should eq s(:none)\n    end\n  end\n\n  it \"filter messages to the backend above level only\" do\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"a\", backend, :warn)\n\n    log.trace { \"trace message\" }\n    log.debug { \"debug message\" }\n    log.info { \"info message\" }\n    log.notice { \"notice message\" }\n    log.warn { \"warning message\" }\n    log.error { \"error message\" }\n    log.fatal { \"fatal message\" }\n\n    backend.entries.map { |e| {e.severity, e.message} }.should eq([\n      {s(:warn), \"warning message\"},\n      {s(:error), \"error message\"},\n      {s(:fatal), \"fatal message\"},\n    ])\n  end\n\n  it \"level can be changed\" do\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"a\", backend, :warn)\n\n    log.level = :error\n\n    log.trace { \"trace message\" }\n    log.debug { \"debug message\" }\n    log.info { \"info message\" }\n    log.notice { \"notice message\" }\n    log.warn { \"warning message\" }\n    log.error { \"error message\" }\n    log.fatal { \"fatal message\" }\n\n    backend.entries.map { |e| {e.severity, e.message} }.should eq([\n      {s(:error), \"error message\"},\n      {s(:fatal), \"fatal message\"},\n    ])\n  end\n\n  it \"can attach exception to entries\" do\n    ex = Exception.new\n\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"a\", backend, :debug)\n\n    log.trace(exception: ex) { \"trace message\" }\n    log.debug(exception: ex) { \"debug message\" }\n    log.info(exception: ex) { \"info message\" }\n    log.notice(exception: ex) { \"notice message\" }\n    log.warn(exception: ex) { \"warning message\" }\n    log.error(exception: ex) { \"error message\" }\n    log.fatal(exception: ex) { \"fatal message\" }\n\n    backend.entries.all? { |e| e.exception == ex }.should be_true\n  end\n\n  it \"can log exceptions without specifying a block\" do\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"a\", backend, :warn)\n    ex = Exception.new\n\n    log.trace(exception: ex)\n    log.debug(exception: ex)\n    log.info(exception: ex)\n    log.notice(exception: ex)\n    log.warn(exception: ex)\n    log.error(exception: ex)\n    log.fatal(exception: ex)\n\n    backend.entries.map { |e| {e.source, e.severity, e.message, e.data, e.exception} }.should eq([\n      {\"a\", s(:warn), \"\", Log::Metadata.empty, ex},\n      {\"a\", s(:error), \"\", Log::Metadata.empty, ex},\n      {\"a\", s(:fatal), \"\", Log::Metadata.empty, ex},\n    ])\n  end\n\n  it \"contains the current context\" do\n    Log.context.set a: 1\n\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"a\", backend, :debug)\n\n    log.info { \"info message\" }\n\n    backend.entries.first.context.should eq(Log::Metadata.build({a: 1}))\n  end\n\n  it \"context can be changed within the block, yet it's not restored\" do\n    Log.context.set a: 1\n\n    backend = Log::MemoryBackend.new\n    log = Log.new(\"a\", backend, :debug)\n\n    log.info { Log.context.set(b: 2); \"info message\" }\n\n    backend.entries.first.context.should eq(Log::Metadata.build({a: 1, b: 2}))\n    Log.context.metadata.should eq(Log::Metadata.build({a: 1, b: 2}))\n  end\n\n  it \"context supports unsigned values\" do\n    Log.context.set a: 1_u32, b: 2_u64\n\n    Log.context.metadata.should eq(Log::Metadata.build({a: 1_u32, b: 2_u64}))\n  end\n\n  describe \"emitter dsl\" do\n    it \"can be used with message\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :debug)\n\n      log.info &.emit(\"info message\")\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:info))\n      entry.message.should eq(\"info message\")\n      entry.data.should eq(Log::Metadata.empty)\n      entry.exception.should be_nil\n    end\n\n    it \"can be used with message and exception\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :debug)\n      ex = Exception.new \"the attached exception\"\n\n      log.debug exception: ex, &.emit(\"debug message\")\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:debug))\n      entry.message.should eq(\"debug message\")\n      entry.data.should eq(Log::Metadata.empty)\n      entry.exception.should eq(ex)\n    end\n\n    it \"can be used with message and metadata explicitly\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :notice)\n\n      log.notice &.emit(\"notice message\", m({a: 1}))\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:notice))\n      entry.message.should eq(\"notice message\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should be_nil\n    end\n\n    it \"can be used with message and data via named arguments\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :fatal)\n\n      log.fatal &.emit(\"fatal message\", a: 1)\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:fatal))\n      entry.message.should eq(\"fatal message\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should be_nil\n    end\n\n    it \"can be used with message and data via named tuple\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :fatal)\n\n      log.fatal &.emit(\"fatal message\", {a: 1})\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:fatal))\n      entry.message.should eq(\"fatal message\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should be_nil\n    end\n\n    it \"can be used with exception\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :fatal)\n      ex = Exception.new \"the attached exception\"\n\n      log.fatal exception: ex, &.emit(\"fatal message\", a: 1)\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:fatal))\n      entry.message.should eq(\"fatal message\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should eq(ex)\n    end\n\n    it \"can be used with data only explicitly\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :notice)\n\n      log.notice &.emit(m({a: 1}))\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:notice))\n      entry.message.should eq(\"\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should be_nil\n    end\n\n    it \"can be used with data only via named arguments\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :notice)\n\n      log.notice &.emit(a: 1)\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:notice))\n      entry.message.should eq(\"\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should be_nil\n    end\n\n    it \"can be used with data only via named tuple\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :notice)\n\n      log.notice &.emit(a: 1)\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:notice))\n      entry.message.should eq(\"\")\n      entry.data.should eq(m({a: 1}))\n      entry.exception.should be_nil\n    end\n\n    it \"does not emit when block returns nil\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :notice)\n\n      log.notice { nil }\n\n      backend.entries.should be_empty\n    end\n\n    it \"does emit when block returns nil but exception is provided\" do\n      backend = Log::MemoryBackend.new\n      log = Log.new(\"a\", backend, :notice)\n      ex = Exception.new \"the attached exception\"\n\n      log.notice(exception: ex) { nil }\n\n      entry = backend.entries.first\n      entry.source.should eq(\"a\")\n      entry.severity.should eq(s(:notice))\n      entry.message.should eq(\"\")\n      entry.data.should eq(Log::Metadata.empty)\n      entry.exception.should eq(ex)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/main_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\n\nprivate module Foo\n  Log = ::Log.for(\"foo\")\n\n  module Bar\n    Log         = Foo::Log.for(\"bar\")\n    LogFromType = ::Log.for(self)\n  end\n\n  class Generic(T)\n    Log = ::Log.for(self)\n  end\n\n  module Same\n    Log = Foo::Log.for(\"\")\n  end\nend\n\ndescribe Log do\n  it \"can build sources from nested\" do\n    Foo::Log.source.should eq(\"foo\")\n    Foo::Bar::Log.source.should eq(\"foo.bar\")\n    Foo::Same::Log.source.should eq(\"foo\")\n    Log.for(\"\").for(\"\").source.should eq(\"\")\n    Log.for(\"\").for(\"foo\").source.should eq(\"foo\")\n    Log.for(\"foo\").for(\"\").source.should eq(\"foo\")\n  end\n\n  it \"can build with level override\" do\n    top = Log.for(\"qux\", :info)\n    top.level.should eq(Log::Severity::Info)\n\n    Log.for(\"qux\", :warn)\n    top.level.should eq(Log::Severity::Warn)\n  end\n\n  it \"can build nested with level override\" do\n    foo_bar = Log.for(\"foo\").for(\"bar\", :info)\n    foo_bar.level.should eq(Log::Severity::Info)\n\n    Log.for(\"foo.bar\", :warn)\n    foo_bar.level.should eq(Log::Severity::Warn)\n  end\n\n  it \"can build for module type\" do\n    Log.for(Foo::Bar).source.should eq(\"foo.bar\")\n  end\n\n  it \"can build for class\" do\n    Log.for(String::Builder).source.should eq(\"string.builder\")\n  end\n\n  it \"can build for generic class (ignores generic args)\" do\n    Log.for(Array(Int32)).source.should eq(\"array\")\n    Foo::Generic::Log.source.should eq(\"foo.generic\")\n  end\n\n  it \"can build for structs\" do\n    Log.for(Time).source.should eq(\"time\")\n  end\n\n  it \"building for type ignores parent source (types are absolute sources)\" do\n    Log.for(\"foo\").for(String::Builder).source.should eq(\"string.builder\")\n  end\n\n  it \"can build with Log = ::Log.for(self)\" do\n    Foo::Bar::LogFromType.should eq(Foo::Bar::Log)\n  end\nend\n"
  },
  {
    "path": "spec/std/log/metadata_spec.cr",
    "content": "require \"spec\"\nrequire \"log\"\nrequire \"log/json\"\n\nprivate def m(value)\n  Log::Metadata.build(value)\nend\n\nprivate def v(value)\n  Log::Metadata::Value.new(value)\nend\n\ndescribe Log::Metadata do\n  it \"empty\" do\n    Log::Metadata.empty.should eq(Log::Metadata.new)\n    Log::Metadata.empty.object_id.should_not eq(Log::Metadata.new.object_id)\n    Log::Metadata.empty.object_id.should eq(Log::Metadata.empty.object_id)\n  end\n\n  it \"empty?\" do\n    Log::Metadata.empty.should be_empty\n    m({} of Symbol => String).should be_empty\n    Log::Metadata.new.should be_empty\n    m({} of Symbol => String).extend({} of Symbol => String).should be_empty\n\n    m({a: 1}).should_not be_empty\n    m({} of Symbol => String).extend({a: 1}).should_not be_empty\n    m({a: 1}).extend({} of Symbol => String).should_not be_empty\n  end\n\n  describe \"#dup\" do\n    it \"creates a shallow copy\" do\n      Log::Metadata.empty.dup.should eq(Log::Metadata.empty)\n      m({a: 1}).dup.should eq(m({a: 1}))\n      m({a: 1, b: 3}).dup.should eq(m({a: 1, b: 3}))\n    end\n  end\n\n  it \"extend\" do\n    m({a: 1}).extend({b: 2}).should eq(m({a: 1, b: 2}))\n    m({a: 1, b: 3}).extend({b: 2}).should eq(m({a: 1, b: 2}))\n    m({a: 1, b: 3}).extend({b: nil}).should eq(m({a: 1, b: nil}))\n  end\n\n  it \"extend against empty values without creating a new instance\" do\n    c1 = m({a: 1, b: 3})\n    c1.extend(NamedTuple.new).should be(c1)\n    c1.extend(Hash(Symbol, String).new).should be(c1)\n  end\n\n  it \"==\" do\n    m({} of Symbol => String).should eq(m({} of Symbol => String))\n    m({a: 1}).should eq(m({a: 1}))\n    m({a: 1, b: 2}).should eq(m({b: 2, a: 1}))\n\n    m({a: 1}).should_not eq(m({a: 2}))\n    m({a: 1}).should_not eq(m({b: 1}))\n\n    m({a: 1}).extend({b: 2}).should eq(m({b: 2}).extend({a: 1}))\n    m({a: 1, b: 1}).extend({b: 2}).should eq(m({b: 2}).extend({a: 1}))\n    m({a: 1, b: 2}).extend({b: 1}).should eq(m({a: 1, b: 1}))\n  end\n\n  it \"json\" do\n    m({a: 1}).to_json.should eq(%({\"a\":1}))\n    m({a: 1, b: 1}).extend({b: 2}).to_json.should eq(%({\"a\":1,\"b\":2}))\n  end\n\n  it \"defrags\" do\n    parent = m({a: 1, b: 2}).extend({a: 2})\n    md = parent.extend({a: 3})\n\n    md.@size.should eq(1)\n    md.@max_total_size.should eq(4)\n    md.@parent.should be(parent)\n\n    md.should eq(m({a: 3, b: 2}))\n\n    md.@size.should eq(2)\n    md.@max_total_size.should eq(2)\n    md.@parent.should be_nil\n  end\n\n  it \"defrags deep\" do\n    grandparent = m({x: 100, y: 200, z: 300})\n    parent = grandparent.extend({x: 101, a: 1, b: 2})\n    md = parent.extend({x: 102, b: 22, c: 3})\n\n    md.should eq(m({x: 102, y: 200, z: 300, a: 1, b: 22, c: 3}))\n  end\n\n  it \"[] find parent entry after defrag\" do\n    grandparent = m({x: 100, y: 200, z: 300})\n    parent = grandparent.extend({x: 101, a: 1, b: 2})\n    md = parent.extend({x: 102, b: 22, c: 3})\n\n    _ = md.to_a\n\n    md[:y].should eq(200)\n    md[:z].should eq(300)\n    md[:x].should eq(102)\n    md[:a].should eq(1)\n    md[:b].should eq(22)\n  end\n\n  it \"[]\" do\n    md = m({a: 1, b: 2}).extend({a: 3})\n\n    md[:a].should eq(3)\n    md[:b].should eq(2)\n    expect_raises(KeyError) { md[:c] }\n  end\n\n  it \"[]?\" do\n    md = m({a: 1, b: 2}).extend({a: 3})\n\n    md[:a]?.should eq(3)\n    md[:b]?.should eq(2)\n    md[:c]?.should be_nil\n  end\nend\n\ndescribe Log::Metadata::Value do\n  it \"initialize\" do\n    v({a: 1}).should eq(v({\"a\" => v(1)}))\n    v({a: 1, b: [\"str\", true], num: 1i64}).should eq(v({\"a\" => v(1), \"b\" => v([v(\"str\"), v(true)]), \"num\" => v(1i64)}))\n    v({a: 1f32, b: 1f64}).should eq(v({\"a\" => v(1f32), \"b\" => v(1f64)}))\n    t = Time.local\n    v({time: t}).should eq(v({\"time\" => v(t)}))\n    v({} of String => String).should eq(v(NamedTuple.new))\n  end\n\n  it \"accessors\" do\n    v(nil).as_nil.should be_nil\n\n    v(1).as_i.should eq(1)\n\n    v(\"a\").as_s.should eq(\"a\")\n    v(1).as_s?.should be_nil\n\n    v(true).as_bool.should be_true\n    v(false).as_bool.should be_false\n    v(true).as_bool?.should be_true\n    v(false).as_bool?.should be_false\n    v(nil).as_bool?.should be_nil\n  end\n\n  it \"json\" do\n    v({a: 1}).to_json.should eq(%({\"a\":1}))\n  end\n\n  describe \"#==\" do\n    it \"compares with String\" do\n      v(\"foo\").should eq \"foo\"\n      \"foo\".should eq v(\"foo\")\n\n      v(\"foo\").should_not eq \"bar\"\n      \"foo\".should_not eq v(\"bar\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/log/spec_spec.cr",
    "content": "require \"spec\"\nrequire \"log/spec\"\nrequire \"log\"\n\ndescribe \"log/spec\" do\n  it \"yield and returns the dsl\" do\n    helper = nil\n    returned = Log.capture do |yielded|\n      helper = yielded\n    end\n\n    helper.should eq(returned)\n  end\n\n  it \"allows matching logs\" do\n    Log.capture {\n      Log.error { \"this is an error\" }\n      Log.fatal { \"this is a fatal\" }\n    }.itself\n      .check(:error, \"this is an error\")\n      .check(:fatal, \"this is a fatal\")\n      .empty\n  end\n\n  it \"can get the entry matched by check\" do\n    Log.capture {\n      Log.error { \"this is an error\" }\n      Log.fatal { \"this is a fatal\" }\n    }.itself\n      .check(:error, \"this is an error\").tap { |d|\n      d.entry.message.should eq(\"this is an error\")\n    }.check(:fatal, \"this is a fatal\").tap { |d|\n      d.entry.message.should eq(\"this is a fatal\")\n    }.empty\n  end\n\n  it \"allows matching non-consecutive logs\" do\n    Log.capture {\n      Log.error { \"ignored\" }\n      Log.error { \"this is an error\" }\n      Log.fatal { \"also ignored\" }\n      Log.fatal { \"this is a fatal\" }\n    }.itself\n      .check(:error, \"this is an error\")\n      .check(:fatal, \"this is a fatal\")\n      .empty\n  end\n\n  it \"allows matching logs strictly\" do\n    Log.capture {\n      Log.error { \"this is an error\" }\n      Log.fatal { \"this is a fatal\" }\n    }.itself\n      .next(:error, \"this is an error\")\n      .next(:fatal, \"this is a fatal\")\n      .empty\n  end\n\n  it \"can get the entry matched by next\" do\n    Log.capture {\n      Log.error { \"this is an error\" }\n      Log.fatal { \"this is a fatal\" }\n    }.itself\n      .next(:error, \"this is an error\").tap { |d|\n      d.entry.message.should eq(\"this is an error\")\n    }.next(:fatal, \"this is a fatal\").tap { |d|\n      d.entry.message.should eq(\"this is a fatal\")\n    }.empty\n  end\n\n  it \"fails on non-consecutive logs\" do\n    expect_raises(Spec::AssertionFailed, /No matching entries found, expected Fatal with \"this is a second fatal\", but got Fatal with \"this is a fatal\"/) do\n      Log.capture {\n        Log.error { \"this is an error\" }\n        Log.fatal { \"this is a fatal\" }\n        Log.fatal { \"this is a second fatal\" }\n      }.itself\n        .next(:error, \"this is an error\")\n        .next(:fatal, \"this is a second fatal\")\n        .empty\n    end\n  end\n\n  it \"fails on non-empty logs\" do\n    expect_raises(Spec::AssertionFailed, /Expected no entries, but got Error with \"this is an error\" in a total of 1 entries/) do\n      Log.capture {\n        Log.error { \"this is an error\" }\n      }.itself\n        .empty\n    end\n  end\n\n  it \"entries can be cleared\" do\n    Log.capture do |l|\n      Log.error { \"this is an error\" }\n      l.clear\n      l.empty\n    end\n  end\n\n  it \"allows matching with regex\" do\n    Log.capture do |l|\n      Log.error { \"this is an error\" }\n      Log.error { \"this is a second error\" }\n\n      l.check(:error, /an error/)\n      l.next(:error, /second error/)\n    end\n  end\n\n  it \"can capture in different checkers\" do\n    Log.capture(\"foo\", :info) do |foo|\n      Log.capture(\"bar\", :info) do |bar|\n        Log.error { \"error in top\" }\n        Log.for(\"foo\").error { \"error in foo\" }\n        Log.for(\"bar\").error { \"error in bar\" }\n        Log.error { \"second error in top\" }\n\n        foo.next(:error, \"error in foo\")\n        foo.empty\n\n        bar.next(:error, \"error in bar\")\n        bar.empty\n      end\n    end\n  end\n\n  it \"can capture with source pattern\" do\n    Log.capture(\"foo.*\", :info) do |foo|\n      Log.for(\"foo\").error { \"error in foo\" }\n      Log.for(\"bar\").error { \"error in bar\" }\n      Log.for(\"foo.nested\").error { \"error in foo.nested\" }\n\n      foo.next(:error, \"error in foo\")\n      foo.next(:error, \"error in foo.nested\")\n      foo.empty\n    end\n  end\n\n  it \"can capture from all sources\" do\n    Log.capture(:info) do |logs|\n      Log.for(\"foo\").error { \"error in foo\" }\n      Log.for(\"bar\").error { \"error in bar\" }\n      Log.for(\"foo.nested\").error { \"error in foo.nested\" }\n\n      logs.next(:error, \"error in foo\")\n      logs.next(:error, \"error in bar\")\n      logs.next(:error, \"error in foo.nested\")\n      logs.empty\n    end\n  end\n\n  it \"it does not capture below level\" do\n    Log.capture(:info) do |logs|\n      Log.debug { \"ignored\" }\n      Log.error { \"first error\" }\n      Log.debug { \"ignored\" }\n      Log.error { \"second error\" }\n      Log.debug { \"ignored\" }\n\n      logs.next(:error, \"first error\")\n      logs.next(:error, \"second error\")\n      logs.empty\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/math_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Math\" do\n  describe \"Mathematical constants\" do\n    it \"E\" do\n      Math::E.should be_close(2.718281828459045, 1e-7)\n    end\n\n    it \"LOG2\" do\n      Math::LOG2.should be_close(0.6931471805599453, 1e-7)\n    end\n\n    it \"LOG10\" do\n      Math::LOG10.should be_close(2.302585092994046, 1e-7)\n    end\n  end\n\n  describe \"Functions manipulating signs\" do\n    it \"copysign\" do\n      Math.copysign(6.9, -0.2).should eq(-6.9)\n    end\n  end\n\n  describe \"Order-related functions\" do\n    it { Math.min(2.1, 2.11).should eq(2.1) }\n    it { Math.max(3.2, 3.11).should eq(3.2) }\n  end\n\n  pending \"Functions for computing quotient and remainder\" do\n  end\n\n  describe \"Roots\" do\n    it \"cbrt\" do\n      Math.cbrt(6.5_f32).should be_close(1.866255578408624, 1e-7)\n      Math.cbrt(6.5).should be_close(1.866255578408624, 1e-7)\n    end\n\n    it \"sqrt\" do\n      Math.sqrt(5.2_f32).should be_close(2.280350850198276, 1e-7)\n      Math.sqrt(5.2).should be_close(2.280350850198276, 1e-7)\n      Math.sqrt(4_f32).should eq(2)\n      Math.sqrt(4).should eq(2)\n    end\n\n    it \"isqrt\" do\n      Math.isqrt(9).should eq(3)\n      Math.isqrt(8).should eq(2)\n      Math.isqrt(4).should eq(2)\n      {% for type in [UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128] %}\n        %val = {{type}}.new 42\n        %exp = {{type}}.new 6\n        Math.isqrt(%val).should eq(%exp)\n      {% end %}\n    end\n  end\n\n  describe \"Exponents\" do\n    it \"exp\" do\n      Math.exp(0.211_f32).should be_close(1.2349123550613943, 1e-7)\n      Math.exp(0.211).should be_close(1.2349123550613943, 1e-7)\n    end\n\n    it \"exp2\" do\n      Math.exp2(0.41_f32).should be_close(1.3286858140965117, 1e-7)\n      Math.exp2(0.41).should be_close(1.3286858140965117, 1e-7)\n    end\n\n    it \"expm1\" do\n      Math.expm1(0.99_f32).should be_close(1.6912344723492623, 1e-6)\n      Math.expm1(0.99).should be_close(1.6912344723492623, 1e-7)\n    end\n\n    it \"ilogb\" do\n      Math.ilogb(0.5_f32).should eq(-1)\n      Math.ilogb(0.5).should eq(-1)\n    end\n\n    it \"ldexp\" do\n      Math.ldexp(0.27_f32, 2).should be_close(1.08, 1e-7)\n      Math.ldexp(0.27, 2).should be_close(1.08, 1e-7)\n    end\n\n    it \"logb\" do\n      Math.logb(10_f32).should be_close(3.0, 1e-7)\n      Math.logb(10.0).should be_close(3.0, 1e-7)\n    end\n\n    it \"scalbn\" do\n      Math.scalbn(0.2_f32, 3).should be_close(1.6, 1e-7)\n      Math.scalbn(0.2, 3).should be_close(1.6, 1e-7)\n    end\n\n    it \"scalbln\" do\n      Math.scalbln(0.11_f32, 2).should be_close(0.44, 1e-7)\n      Math.scalbln(0.11, 2).should be_close(0.44, 1e-7)\n    end\n\n    it \"frexp\" do\n      Math.frexp(0.2_f32).should eq({0.8_f32, -2})\n      Math.frexp(0.2).should eq({0.8, -2})\n    end\n  end\n\n  describe \"Logarithms\" do\n    it \"log\" do\n      Math.log(3.24_f32).should be_close(1.1755733298042381, 1e-7)\n      Math.log(3.24).should be_close(1.1755733298042381, 1e-7)\n      Math.log(0.3_f32, 3).should be_close(-1.0959032742893848, 1e-7)\n      Math.log(0.3, 3).should be_close(-1.0959032742893848, 1e-7)\n    end\n\n    it \"log2\" do\n      Math.log2(1.2_f32).should be_close(0.2630344058337938, 1e-7)\n      Math.log2(1.2).should be_close(0.2630344058337938, 1e-7)\n    end\n\n    it \"log10\" do\n      Math.log10(0.5_f32).should be_close(-0.3010299956639812, 1e-7)\n      Math.log10(0.5).should be_close(-0.3010299956639812, 1e-7)\n    end\n\n    it \"log1p\" do\n      Math.log1p(0.67_f32).should be_close(0.5128236264286637, 1e-7)\n      Math.log1p(0.67).should be_close(0.5128236264286637, 1e-7)\n    end\n  end\n\n  describe \"Trigonometric functions\" do\n    it \"cos\" do\n      Math.cos(2.23_f32).should be_close(-0.6124875656583851, 1e-7)\n      Math.cos(2.23).should be_close(-0.6124875656583851, 1e-7)\n    end\n\n    it \"sin\" do\n      Math.sin(3.3_f32).should be_close(-0.1577456941432482, 1e-7)\n      Math.sin(3.3).should be_close(-0.1577456941432482, 1e-7)\n    end\n\n    it \"tan\" do\n      Math.tan(0.91_f32).should be_close(1.2863693807208076, 1e-7)\n      Math.tan(0.91).should be_close(1.2863693807208076, 1e-7)\n    end\n\n    it \"hypot\" do\n      Math.hypot(2.1_f32, 1.5_f32).should be_close(2.5806975801127883, 1e-7)\n      Math.hypot(2.1, 1.5).should be_close(2.5806975801127883, 1e-7)\n    end\n  end\n\n  describe \"Inverse trigonometric functions\" do\n    it \"acos\" do\n      Math.acos(0.125_f32).should be_close(1.445468495626831, 1e-7)\n      Math.acos(0.125).should be_close(1.445468495626831, 1e-7)\n    end\n\n    it \"asin\" do\n      Math.asin(-0.4_f32).should be_close(-0.41151684606748806, 1e-7)\n      Math.asin(-0.4).should be_close(-0.41151684606748806, 1e-7)\n    end\n\n    it \"atan\" do\n      Math.atan(4.3_f32).should be_close(1.3422996875030344, 1e-7)\n      Math.atan(4.3).should be_close(1.3422996875030344, 1e-7)\n    end\n\n    it \"atan2\" do\n      Math.atan2(3.5_f32, 2.1_f32).should be_close(1.0303768265243125, 1e-7)\n      Math.atan2(3.5, 2.1).should be_close(1.0303768265243125, 1e-7)\n      Math.atan2(1, 0).should eq(Math.atan2(1.0, 0.0))\n    end\n  end\n\n  describe \"Hyperbolic functions\" do\n    it \"cosh\" do\n      Math.cosh(0.79_f32).should be_close(1.3286206107691463, 1e-7)\n      Math.cosh(0.79).should be_close(1.3286206107691463, 1e-7)\n    end\n\n    it \"sinh\" do\n      Math.sinh(0.12_f32).should be_close(0.12028820743110909, 1e-7)\n      Math.sinh(0.12).should be_close(0.12028820743110909, 1e-7)\n    end\n\n    it \"tanh\" do\n      Math.tanh(4.1_f32).should be_close(0.9994508436877974, 1e-7)\n      Math.tanh(4.1).should be_close(0.9994508436877974, 1e-7)\n    end\n  end\n\n  describe \"Inverse hyperbolic functions\" do\n    it \"acosh\" do\n      Math.acosh(1.1_f32).should be_close(0.4435682543851154, 1e-7)\n      Math.acosh(1.1).should be_close(0.4435682543851154, 1e-7)\n    end\n\n    it \"asinh\" do\n      Math.asinh(-2.3_f32).should be_close(-1.570278543484978, 1e-7)\n      Math.asinh(-2.3).should be_close(-1.570278543484978, 1e-7)\n    end\n\n    it \"atanh\" do\n      Math.atanh(0.13_f32).should be_close(0.13073985002887845, 1e-7)\n      Math.atanh(0.13).should be_close(0.13073985002887845, 1e-7)\n    end\n  end\n\n  describe \"Gamma functions\" do\n    it \"gamma\" do\n      Math.gamma(3.2_f32).should be_close(2.4239654799353683, 1e-6)\n      Math.gamma(3.2).should be_close(2.4239654799353683, 1e-7)\n      Math.gamma(5).should eq 24\n      Math.gamma(5_i8).should eq 24\n    end\n\n    it \"lgamma\" do\n      Math.lgamma(2.96_f32).should be_close(0.6565534110944214, 1e-7)\n      Math.lgamma(2.96).should be_close(0.6565534110944214, 1e-7)\n      Math.lgamma(3).should be_close(0.6931471805599454, 1e-7)\n      Math.lgamma(3_i8).should be_close(0.6931471805599454, 1e-7)\n    end\n  end\n\n  describe \"Bessel functions\" do\n    it \"besselj0\" do\n      Math.besselj0(9.1_f32).should be_close(-0.11423923268319867, 1e-7)\n      Math.besselj0(9.1).should be_close(-0.11423923268319867, 1e-7)\n    end\n\n    it \"besselj1\" do\n      Math.besselj1(-2.1_f32).should be_close(-0.5682921357570385, 1e-7)\n      Math.besselj1(-2.1).should be_close(-0.5682921357570385, 1e-7)\n    end\n\n    it \"besselj\" do\n      Math.besselj(4, -6.4_f32).should be_close(0.2945338623574655, 1e-7)\n      Math.besselj(4, -6.4).should be_close(0.2945338623574655, 1e-7)\n    end\n\n    it \"bessely0\" do\n      Math.bessely0(2.14_f32).should be_close(0.5199289108068015, 1e-7)\n      Math.bessely0(2.14).should be_close(0.5199289108068015, 1e-7)\n    end\n\n    it \"bessely1\" do\n      Math.bessely1(7.7_f32).should be_close(-0.2243184743430081, 1e-7)\n      Math.bessely1(7.7).should be_close(-0.2243184743430081, 1e-7)\n    end\n\n    it \"bessely\" do\n      Math.bessely(3, 2.7_f32).should be_close(-0.6600575162477298, 1e-7)\n      Math.bessely(3, 2.7).should be_close(-0.6600575162477298, 1e-7)\n    end\n  end\n\n  describe \"Gauss error functions\" do\n    it \"erf\" do\n      Math.erf(0.72_f32).should be_close(0.6914331231387512, 1e-7)\n      Math.erf(0.72).should be_close(0.6914331231387512, 1e-7)\n    end\n\n    it \"erfc\" do\n      Math.erfc(-0.66_f32).should be_close(1.6493766879629543, 1e-7)\n      Math.erfc(-0.66).should be_close(1.6493766879629543, 1e-7)\n    end\n  end\n\n  # div rem\n\n  it \"fma\" do\n    x = Math.fma(0.1, 10.0, -1.0)\n    x.should be_close(5.551115123125783e-17, 1e-25)\n    x.should_not eq(0.0)\n\n    x = Math.fma(0.1_f32, 10.0_f32, -1.0_f32)\n    x.should be_close(1.4901161e-8_f32, 1e-16_f32)\n    x.should_not eq(0.0_f32)\n  end\n\n  describe \".pw2ceil\" do\n    {% for int in %w(Int8 Int16 Int32 Int64 Int128) %}\n      it {{ int }} do\n        Math.pw2ceil({{ int.id }}::MIN).should eq 1\n        Math.pw2ceil({{ int.id }}::MIN + 1).should eq 1\n        Math.pw2ceil({{ int.id }}.new(-11)).should eq 1\n        Math.pw2ceil({{ int.id }}.new(-1)).should eq 1\n        Math.pw2ceil({{ int.id }}.new(0)).should eq 1\n        Math.pw2ceil({{ int.id }}.new(1)).should eq 1\n        Math.pw2ceil({{ int.id }}.new(2)).should eq 2\n        Math.pw2ceil({{ int.id }}.new(3)).should eq 4\n        Math.pw2ceil({{ int.id }}.new(4)).should eq 4\n        Math.pw2ceil({{ int.id }}.new(5)).should eq 8\n        Math.pw2ceil({{ int.id }}.new(32)).should eq(32)\n        Math.pw2ceil({{ int.id }}.new(33)).should eq(64)\n        Math.pw2ceil({{ int.id }}.new(64)).should eq(64)\n\n        Math.pw2ceil({{ int.id }}::MAX // 2).should eq({{ int.id }}::MAX // 2 + 1)\n        Math.pw2ceil({{ int.id }}::MAX // 2 + 1).should eq({{ int.id }}::MAX // 2 + 1)\n        expect_raises(OverflowError) { Math.pw2ceil({{ int.id }}::MAX // 2 + 2) }\n        expect_raises(OverflowError) { Math.pw2ceil({{ int.id }}::MAX) }\n      end\n    {% end %}\n\n    {% for uint in %w(UInt8 UInt16 UInt32 UInt64 UInt128) %}\n      it {{ uint }} do\n        Math.pw2ceil({{ uint.id }}.new(0)).should eq 1\n        Math.pw2ceil({{ uint.id }}.new(1)).should eq 1\n        Math.pw2ceil({{ uint.id }}.new(2)).should eq 2\n        Math.pw2ceil({{ uint.id }}.new(3)).should eq 4\n        Math.pw2ceil({{ uint.id }}.new(4)).should eq 4\n        Math.pw2ceil({{ uint.id }}.new(5)).should eq 8\n        Math.pw2ceil({{ uint.id }}.new(32)).should eq(32)\n        Math.pw2ceil({{ uint.id }}.new(33)).should eq(64)\n        Math.pw2ceil({{ uint.id }}.new(64)).should eq(64)\n\n        Math.pw2ceil({{ uint.id }}::MAX // 2).should eq({{ uint.id }}::MAX // 2 + 1)\n        Math.pw2ceil({{ uint.id }}::MAX // 2 + 1).should eq({{ uint.id }}::MAX // 2 + 1)\n        expect_raises(OverflowError) { Math.pw2ceil({{ uint.id }}::MAX // 2 + 2) }\n        expect_raises(OverflowError) { Math.pw2ceil({{ uint.id }}::MAX) }\n      end\n    {% end %}\n  end\n\n  # ** (float and int)\nend\n"
  },
  {
    "path": "spec/std/mime/media_type_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"mime/media_type\"\nrequire \"spec/helpers/string\"\n\nprivate def parse(string)\n  type = MIME::MediaType.parse(string)\n\n  {type.media_type, type.@params}\nend\n\nprivate def assert_format(string, format = string, file = __FILE__, line = __LINE__)\n  assert_prints MIME::MediaType.parse(string).to_s, format, file: file, line: line\nend\n\ndescribe MIME::MediaType do\n  describe \".new\" do\n    it \"create new instance\" do\n      media_type = MIME::MediaType.new(\"foo/bar\", {\"param\" => \"value\"})\n      media_type.media_type.should eq \"foo/bar\"\n      media_type[\"param\"].should eq \"value\"\n    end\n\n    it \"raises for invalid parameter name\" do\n      expect_raises(MIME::Error, %q(Invalid parameter name \"ß\")) do\n        MIME::MediaType.new(\"foo/bar\", {\"param\" => \"value\", \"ß\" => \"invalid\"})\n      end\n    end\n  end\n\n  describe \".parse\" do\n    it \"parses media type\" do\n      MIME::MediaType.parse(\"text/html; charset=utf-8\").media_type.should eq \"text/html\"\n    end\n\n    it \"parses params\" do\n      parse(\"text/html; charset=utf-8\").should eq({\"text/html\", {\"charset\" => \"utf-8\"}})\n\n      parse(\"text/html; charset=us-ascii\").should eq({\"text/html\", {\"charset\" => \"us-ascii\"}})\n\n      parse(\"text/html; foo = bar; bar= foo ;\").should eq({\"text/html\", {\"foo\" => \"bar\", \"bar\" => \"foo\", \"charset\" => \"utf-8\"}})\n\n      parse(%(form-data; name=\"foo\")).should eq({\"form-data\", {\"name\" => \"foo\"}})\n      parse(%(form-data; name =\"foo\")).should eq({\"form-data\", {\"name\" => \"foo\"}})\n      parse(%(form-data; name= \"foo\")).should eq({\"form-data\", {\"name\" => \"foo\"}})\n\n      parse(%( form-data ; name=foo)).should eq({\"form-data\", {\"name\" => \"foo\"}})\n\n      parse(%(FORM-DATA;name=\"foo\")).should eq({\"form-data\", {\"name\" => \"foo\"}})\n\n      parse(%( FORM-DATA ; name=\"foo\")).should eq({\"form-data\", {\"name\" => \"foo\"}})\n\n      expect_raises(MIME::Error, \"Missing media type\") { parse(\"\") }\n      expect_raises(MIME::Error, \"Missing media type\") { parse(\" \") }\n      expect_raises(MIME::Error, \"Missing media type\") { parse(\";\") }\n      expect_raises(MIME::Error, \"Missing media type\") { parse(\" ;\") }\n\n      expect_raises(MIME::Error, \"Missing attribute name at 10\") { parse(\"form-data;=foo\") }\n      expect_raises(MIME::Error, \"Missing attribute name at \") { parse(\"form-data; =foo\") }\n      expect_raises(MIME::Error, \"Missing attribute value\") { parse(\"form-data;foo\") }\n\n      parse(%(form-data; key=value;  blah=\"value\";name=\"foo\" )).should eq({\"form-data\", {\"key\" => \"value\", \"blah\" => \"value\", \"name\" => \"foo\"}})\n\n      expect_raises(MIME::Error, \"Duplicate key 'key' at 15\") { parse(%(foo; key=val1; key=the-key-appears-again-which-is-bogus)) }\n\n      parse(%(FORM-DATA;NAMe=\"foo\")).should eq({\"form-data\", {\"name\" => \"foo\"}})\n\n      parse(%(message/external-body; access-type=URL; URL*0=\"ftp://\";URL*1=\"cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar\")).should eq({\n        \"message/external-body\",\n        {\"access-type\" => \"URL\", \"url\" => \"ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar\"},\n      })\n\n      # Tests from http://greenbytes.de/tech/tc2231/\n\n      # attonly\n      parse(%(attachment)).should eq({\"attachment\", {} of String => String})\n      # attonlyucase\n      parse(%(ATTACHMENT)).should eq({\"attachment\", {} of String => String})\n      # attwithasciifilename\n      parse(%(attachment; filename=\"foo.html\")).should eq({\"attachment\", {\"filename\" => \"foo.html\"}})\n      # attwithasciifilename25\n      parse(%(attachment; filename=\"0000000000111111111122222\")).should eq({\"attachment\", {\"filename\" => \"0000000000111111111122222\"}})\n      # attwithasciifilename35\n      parse(%(attachment; filename=\"00000000001111111111222222222233333\")).should eq({\"attachment\", {\"filename\" => \"00000000001111111111222222222233333\"}})\n      # attwithasciifnescapedchar\n      parse(%(attachment; filename=\"f\\\\oo.html\")).should eq({\"attachment\", {\"filename\" => \"f\\\\oo.html\"}})\n      # attwithasciifnescapedquote\n      parse(%(attachment; filename=\"\\\\\"quoting\\\\\" tested.html\")).should eq({\"attachment\", {\"filename\" => %(\"quoting\" tested.html)}})\n      # attwithquotedsemicolon\n      parse(%(attachment; filename=\"Here's a semicolon;.html\")).should eq({\"attachment\", {\"filename\" => \"Here's a semicolon;.html\"}})\n      # attwithfilenameandextparam\n      parse(%(attachment; foo=\"bar\"; filename=\"foo.html\")).should eq({\"attachment\", {\"foo\" => \"bar\", \"filename\" => \"foo.html\"}})\n      # attwithfilenameandextparamescaped\n      parse(%(attachment; foo=\"\\\\\"\\\\\\\\\";filename=\"foo.html\")).should eq({\"attachment\", {\"foo\" => %(\"\\\\), \"filename\" => \"foo.html\"}})\n      # attwithasciifilenameucase\n      parse(%(attachment; FILENAME=\"foo.html\")).should eq({\"attachment\", {\"filename\" => \"foo.html\"}})\n      # attwithasciifilenamenq\n      parse(%(attachment; filename=foo.html)).should eq({\"attachment\", {\"filename\" => \"foo.html\"}})\n      # attwithasciifilenamenqs\n      parse(%(attachment; filename=foo.html ;)).should eq({\"attachment\", {\"filename\" => \"foo.html\"}})\n      # attwithfntokensq\n      parse(%(attachment; filename='foo.html')).should eq({\"attachment\", {\"filename\" => \"'foo.html'\"}})\n      # attwithisofnplain\n      parse(%(attachment; filename=\"foo-ä.html\")).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attwithutf8fnplain\n      parse(%(attachment; filename=\"foo-Ã¤.html\")).should eq({\"attachment\", {\"filename\" => \"foo-Ã¤.html\"}})\n      # attwithfnrawpctenca\n      parse(%(attachment; filename=\"foo-%41.html\")).should eq({\"attachment\", {\"filename\" => \"foo-%41.html\"}})\n      # attwithfnusingpct\n      parse(%(attachment; filename=\"50%.html\")).should eq({\"attachment\", {\"filename\" => \"50%.html\"}})\n      # attwithfnrawpctencaq\n      parse(%(attachment; filename=\"foo-%\\\\41.html\")).should eq({\"attachment\", {\"filename\" => \"foo-%\\\\41.html\"}})\n      # attwithnamepct\n      parse(%(attachment; name=\"foo-%41.html\")).should eq({\"attachment\", {\"name\" => \"foo-%41.html\"}})\n      # attwithfilenamepctandiso\n      parse(%(attachment; name=\"ä-%41.html\")).should eq({\"attachment\", {\"name\" => \"ä-%41.html\"}})\n      # attwithfnrawpctenclong\n      parse(%(attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\")).should eq({\"attachment\", {\"filename\" => \"foo-%c3%a4-%e2%82%ac.html\"}})\n      # attwithasciifilenamews1\n      parse(%(attachment; filename =\"foo.html\")).should eq({\"attachment\", {\"filename\" => \"foo.html\"}})\n      # attmissingdisposition\n      expect_raises(MIME::Error, \"Invalid character '=' at 8\") { parse(%(filename=foo.html)) }\n      # attmissingdisposition2\n      expect_raises(MIME::Error, \"Invalid character '=' at 1\") { parse(%(x=y; filename=foo.html)) }\n      # attmissingdisposition3\n      expect_raises(MIME::Error, \"Invalid character '\\\"' at 0\") { parse(%(\"foo; filename=bar;baz\"; filename=qux)) }\n      # attmissingdisposition4\n      expect_raises(MIME::Error, \"Invalid character '=' at 8\") { parse(%(filename=foo.html, filename=bar.html)) }\n      # emptydisposition\n      expect_raises(MIME::Error, \"Missing media type\") { parse(%(; filename=foo.html)) }\n      # doublecolon\n      expect_raises(MIME::Error, \"Invalid character ':' at 0\") { parse(%(: inline; attachment; filename=foo.html)) }\n      # attandinline\n      expect_raises(MIME::Error, \"Invalid ';' at 18, expecting '='\") { parse(%(inline; attachment; filename=foo.html)) }\n      # attandinline2\n      expect_raises(MIME::Error, \"Invalid ';' at 18, expecting '='\") { parse(%(attachment; inline; filename=foo.html)) }\n      # attbrokenquotedfn\n      expect_raises(MIME::Error, \"Invalid character '.' at 31, expecting ';'\") { parse(%(attachment; filename=\"foo.html\".txt)) }\n      # attbrokenquotedfn2\n      expect_raises(MIME::Error, \"Unclosed quote at 25\") { parse(%(attachment; filename=\"bar)) }\n      # attbrokenquotedfn3\n      expect_raises(MIME::Error, \"Unexpected '\\\"' at 24\") { parse(%(attachment; filename=foo\"bar;baz\"qux)) }\n      # attmultinstances\n      expect_raises(MIME::Error, \"Duplicate key 'filename' at 43\") { parse(%(attachment; filename=foo.html, attachment; filename=bar.html)) }\n      # attmissingdelim\n      expect_raises(MIME::Error, \"Unexpected '=' at 28\") { parse(%(attachment; foo=foo filename=bar)) }\n      # attmissingdelim2\n      expect_raises(MIME::Error, \"Unexpected '=' at 28\") { parse(%(attachment; filename=bar foo=foo)) }\n      # attmissingdelim3\n      expect_raises(MIME::Error, \"Invalid character '=' at 19\") { parse(%(attachment filename=bar)) }\n      # attreversed\n      expect_raises(MIME::Error, \"Invalid character '=' at 8\") { parse(%(filename=foo.html; attachment)) }\n      # attconfusedparam\n      parse(%(attachment; xfilename=foo.html)).should eq({\"attachment\", {\"xfilename\" => \"foo.html\"}})\n      # attcdate\n      parse(%(attachment; creation-date=\"Wed, 12 Feb 1997 16:29:51 -0500\")).should eq({\"attachment\", {\"creation-date\" => \"Wed, 12 Feb 1997 16:29:51 -0500\"}})\n      # attmdate\n      parse(%(attachment; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\")).should eq({\"attachment\", {\"modification-date\" => \"Wed, 12 Feb 1997 16:29:51 -0500\"}})\n      # dispext\n      parse(\"foobar\").should eq({\"foobar\", {} of String => String})\n      # dispextbadfn\n      parse(%(attachment; example=\"filename=example.txt\")).should eq({\"attachment\", {\"example\" => \"filename=example.txt\"}})\n      # # attwithfn2231utf8\n      parse(%(attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html)).should eq({\"attachment\", {\"filename\" => \"foo-ä-€.html\"}})\n      # attwithfn2231noc\n      parse(%(attachment; filename*=''foo-%c3%a4-%e2%82%ac.html)).should eq({\"attachment\", {} of String => String})\n      # attwithfn2231utf8comp\n      parse(%(attachment; filename*=UTF-8''foo-a%cc%88.html)).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attwithfn2231ws2\n      parse(%(attachment; filename*= UTF-8''foo-%c3%a4.html)).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attwithfn2231ws3\n      parse(%(attachment; filename* =UTF-8''foo-%c3%a4.html)).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attwithfn2231quot\n      parse(%(attachment; filename*=\"UTF-8''foo-%c3%a4.html\")).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attwithfn2231quot2\n      parse(%(attachment; filename*=\"foo%20bar.html\")).should eq({\"attachment\", {} of String => String})\n      # attwithfn2231singleqmissing\n      parse(%(attachment; filename*=UTF-8'foo-%c3%a4.html)).should eq({\"attachment\", {} of String => String})\n      # attwithfn2231nbadpct1\n      parse(%(attachment; filename*=UTF-8''foo%)).should eq({\"attachment\", {} of String => String})\n      # attwithfn2231nbadpct2\n      parse(%(attachment; filename*=UTF-8''f%oo.html)).should eq({\"attachment\", {} of String => String})\n      # attwithfn2231dpct\n      parse(%(attachment; filename*=UTF-8''A-%2541.html)).should eq({\"attachment\", {\"filename\" => \"A-%41.html\"}})\n      # attfncont\n      parse(%(attachment; filename*0=\"foo.\"; filename*1=\"html\")).should eq({\"attachment\", {\"filename\" => \"foo.html\"}})\n\n      expect_raises(MIME::Error, \"Duplicate key 'foo *0' at 25\") { parse(%(attachment; foo*0=\"foo\"; foo *0=\"bar\")) }\n      expect_raises(MIME::Error, \"Duplicate key 'foo* 0' at 25\") { parse(%(attachment; foo*0=\"foo\"; foo* 0=\"bar\")) }\n      expect_raises(MIME::Error, \"Invalid key 'foo*foo' at 12\") { parse(%(attachment; foo*foo=foo)) }\n\n      # attfncontenc\n      parse(%(attachment; filename*0*=UTF-8''foo-%c3%a4; filename*1=\".html\")).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attfncontlz\n      parse(%(attachment; filename*0=\"foo\"; filename*01=\"bar\")).should eq({\"attachment\", {\"filename\" => \"foo\"}})\n      # attfncontnc\n      parse(%(attachment; filename*0=\"foo\"; filename*2=\"bar\")).should eq({\"attachment\", {\"filename\" => \"foo\"}})\n      # attfnconts1\n      parse(%(attachment; filename*1=\"foo.\"; filename*2=\"html\")).should eq({\"attachment\", {} of String => String})\n      # attfncontord\n      parse(%(attachment; filename*1=\"bar\"; filename*0=\"foo\")).should eq({\"attachment\", {\"filename\" => \"foobar\"}})\n      # attfnboth\n      parse(%(attachment; filename=\"foo-ae.html\"; filename*=UTF-8''foo-%c3%a4.html)).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attfnboth2\n      parse(%(attachment; filename*=UTF-8''foo-%c3%a4.html; filename=\"foo-ae.html\")).should eq({\"attachment\", {\"filename\" => \"foo-ä.html\"}})\n      # attnewandfn\n      parse(%(attachment; foobar=x; filename=\"foo.html\")).should eq({\"attachment\", {\"foobar\" => \"x\", \"filename\" => \"foo.html\"}})\n\n      # Browsers also just send UTF-8 directly without RFC 2231,\n      # at least when the source page is served with UTF-8.\n      parse(%(form-data; firstname=\"Брэд\"; lastname=\"Фицпатрик\")).should eq({\"form-data\", {\"firstname\" => \"Брэд\", \"lastname\" => \"Фицпатрик\"}})\n\n      # Empty string used to be mishandled.\n      parse(%(foo; bar=\"\")).should eq({\"foo\", {\"bar\" => \"\"}})\n\n      # Microsoft browsers in intranet mode do not think they need to escape \\ in file name.\n      parse(%(form-data; name=\"file\"; filename=\"C:\\\\dev\\\\go\\\\robots.txt\")).should eq({\"form-data\", {\"name\" => \"file\", \"filename\" => \"C:\\\\dev\\\\go\\\\robots.txt\"}})\n\n      # Skip whitespace after =\n      parse(%(form-data; foo= foo)).should eq({\"form-data\", {\"foo\" => \"foo\"}})\n      parse(%(form-data; foo= \" foo \")).should eq({\"form-data\", {\"foo\" => \" foo \"}})\n    end\n\n    {% unless flag?(:without_iconv) %}\n      it \"parses params with encoding\" do\n        # From RFC 2231:\n        parse(%(application/x-stuff; title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A)).should eq({\"application/x-stuff\", {\"title\" => \"This is ***fun***\"}})\n        # attfnboth3\n        parse(%(attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; filename*=ISO-8859-1''currency-sign%3d%a4)).should eq({\"attachment\", {\"filename\" => \"currency-sign=¤\"}})\n      end\n    {% end %}\n\n    it \"sets default charset to utf-8 for text media types\" do\n      type = MIME::MediaType.parse(\"text/html\")\n      type[\"charset\"]?.should eq \"utf-8\"\n\n      type = MIME::MediaType.parse(\"application/html\")\n      type[\"charset\"]?.should be_nil\n    end\n  end\n\n  it \"#sub_type\" do\n    MIME::MediaType.new(\"text/plain\").sub_type.should eq \"plain\"\n    MIME::MediaType.new(\"foo\").sub_type.should be_nil\n  end\n\n  it \"#type\" do\n    MIME::MediaType.new(\"text/plain\").type.should eq \"text\"\n    MIME::MediaType.new(\"foo\").type.should eq \"foo\"\n  end\n\n  it \"#to_s\" do\n    assert_format \"foo/bar\"\n    assert_format \"text/plain; charset=utf-8\"\n    assert_format \"foo/bar; foo=bar; bar=foo;\", \"foo/bar; foo=bar; bar=foo\"\n    assert_format \"form-data; name=foo\"\n    assert_format \"attachment\"\n    assert_format %(attachment; faa=\"\\\\\"\\\\\\\\\"; filename=\"foo.html?\")\n    assert_format %(attachment; filename=\"foo-Ã¤.html\")\n    assert_format %(attachment; filename=foo-%41.html)\n    assert_format %(attachment; filename=\"\\\\\"quoting\\\\\" tested.html\")\n  end\n\n  it \"#fetch\" do\n    MIME::MediaType.parse(\"x-application/example\").fetch(\"foo\", \"baz\").should eq \"baz\"\n    MIME::MediaType.parse(\"x-application/example; foo=bar\").fetch(\"foo\", \"baz\").should eq \"bar\"\n    MIME::MediaType.parse(\"x-application/example\").fetch(\"foo\") { |key| key }.should eq \"foo\"\n    MIME::MediaType.parse(\"x-application/example; foo=bar\").fetch(\"foo\") { |key| key }.should eq \"bar\"\n  end\n\n  it \"#[]=\" do\n    mime_type = MIME::MediaType.parse(\"x-application/example\")\n    mime_type[\"foo\"] = \"bar\"\n    mime_type[\"foo\"].should eq \"bar\"\n\n    expect_raises(MIME::Error, \"Invalid parameter name\") do\n      mime_type[\"ä\"] = \"foo\"\n    end\n  end\n\n  it \"#each_parameter\" do\n    mime_type = MIME::MediaType.parse(\"x-application/example; foo=bar; bar=baz\")\n\n    arr = [] of String\n    mime_type.each_parameter do |key, value|\n      arr << \"#{key}=#{value}\"\n    end\n    arr.should eq [\"foo=bar\", \"bar=baz\"]\n\n    arr = [] of String\n    mime_type.each_parameter.each do |key, value|\n      arr << \"#{key}=#{value}\"\n    end\n    arr.should eq [\"foo=bar\", \"bar=baz\"]\n  end\nend\n"
  },
  {
    "path": "spec/std/mime/multipart/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"mime/multipart\"\n\ndescribe MIME::Multipart::Builder do\n  it \"generates valid multipart messages\" do\n    io = IO::Memory.new\n    builder = MIME::Multipart::Builder.new(io, \"fixed-boundary\")\n\n    headers = HTTP::Headers{\"X-Foo\" => \"bar\"}\n    builder.body_part headers, \"body part 1\"\n\n    headers = HTTP::Headers{\"X-Type\" => \"Empty-Body\", \"X-Foo\" => \"Bar\"}\n    builder.body_part headers\n\n    builder.finish\n\n    expected_message = <<-MULTIPART\n      --fixed-boundary\n      X-Foo: bar\n\n      body part 1\n      --fixed-boundary\n      X-Type: Empty-Body\n      X-Foo: Bar\n      --fixed-boundary--\n      MULTIPART\n\n    io.to_s.should eq(expected_message.gsub('\\n', \"\\r\\n\"))\n  end\n\n  it \"generates valid multipart messages with preamble and epilogue\" do\n    io = IO::Memory.new\n    builder = MIME::Multipart::Builder.new(io, \"fixed-boundary\")\n\n    builder.preamble \"Here is a preamble to explain why multipart/mixed \"\n    builder.preamble \"exists and why your mail client should support it\"\n\n    headers = HTTP::Headers{\"X-Foo\" => \"bar\"}\n    builder.body_part headers, \"body part 1\"\n\n    headers = HTTP::Headers{\"X-Type\" => \"Empty-Body\", \"X-Foo\" => \"Bar\"}\n    builder.body_part headers\n\n    builder.epilogue \"Irrelevant text\"\n    builder.epilogue \"Much more irrelevant text\"\n\n    builder.finish\n\n    expected_message = <<-MULTIPART\n      Here is a preamble to explain why multipart/mixed exists and why your mail client should support it\n      --fixed-boundary\n      X-Foo: bar\n\n      body part 1\n      --fixed-boundary\n      X-Type: Empty-Body\n      X-Foo: Bar\n      --fixed-boundary--\n      Irrelevant textMuch more irrelevant text\n      MULTIPART\n\n    io.to_s.should eq(expected_message.gsub('\\n', \"\\r\\n\"))\n  end\n\n  describe \"#content_type\" do\n    it \"calculates the content type\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new, \"a delimiter string with a quote in \\\"\")\n      builder.content_type(\"alternative\").should eq(%q(multipart/alternative; boundary=\"a delimiter string with a quote in \\\"\"))\n    end\n  end\n\n  describe \".preamble\" do\n    it \"accepts different data types\" do\n      io = IO::Memory.new\n      builder = MIME::Multipart::Builder.new(io, \"boundary\")\n\n      builder.preamble \"string\\r\\n\"\n      builder.preamble \"slice\\r\\n\".to_slice\n      preamble_io = IO::Memory.new \"io\\r\\n\"\n      builder.preamble preamble_io\n      builder.preamble do |io|\n        io.print \"io\"\n        io << ' '\n        io.print \"block\"\n        io << \"\\r\\n\"\n      end\n\n      builder.body_part(HTTP::Headers.new)\n      builder.finish\n\n      generated_multipart = io.to_s\n      expected_multipart = <<-MULTIPART\n        string\n        slice\n        io\n        io block\n\n        --boundary\n        --boundary--\n        MULTIPART\n\n      generated_multipart.should eq(expected_multipart.gsub('\\n', \"\\r\\n\"))\n    end\n\n    it \"raises when called after starting the body\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      builder.body_part HTTP::Headers.new, \"test\"\n      expect_raises(MIME::Multipart::Error, \"Cannot generate preamble: body already started\") do\n        builder.preamble \"test\"\n      end\n    end\n  end\n\n  describe \".body_part\" do\n    it \"accepts different data types\" do\n      io = IO::Memory.new\n      builder = MIME::Multipart::Builder.new(io, \"boundary\")\n\n      headers = HTTP::Headers{\"X-Foo\" => \"Bar\"}\n\n      builder.body_part headers, \"string\\r\\n\"\n      builder.body_part headers, \"slice\".to_slice\n      body_part_io = IO::Memory.new \"io\"\n      builder.body_part headers, body_part_io\n      builder.body_part(headers) do |io|\n        io.print \"io\"\n        io << ' '\n        io.print \"block\"\n      end\n      builder.body_part(headers)\n\n      builder.finish\n\n      generated_multipart = io.to_s\n      expected_multipart = <<-MULTIPART\n        --boundary\n        X-Foo: Bar\n\n        string\n\n        --boundary\n        X-Foo: Bar\n\n        slice\n        --boundary\n        X-Foo: Bar\n\n        io\n        --boundary\n        X-Foo: Bar\n\n        io block\n        --boundary\n        X-Foo: Bar\n        --boundary--\n        MULTIPART\n\n      generated_multipart.should eq(expected_multipart.gsub('\\n', \"\\r\\n\"))\n    end\n\n    it \"raises when called after finishing\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      builder.body_part HTTP::Headers.new, \"test\"\n      builder.finish\n      expect_raises(MIME::Multipart::Error, \"Cannot generate body part: already finished\") do\n        builder.body_part HTTP::Headers.new, \"test\"\n      end\n    end\n\n    it \"raises when called after epilogue\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      builder.body_part HTTP::Headers.new, \"test\"\n      builder.epilogue \"test\"\n      expect_raises(MIME::Multipart::Error, \"Cannot generate body part: after epilogue\") do\n        builder.body_part HTTP::Headers.new, \"test\"\n      end\n    end\n  end\n\n  describe \".epilogue\" do\n    it \"accepts different data types\" do\n      io = IO::Memory.new\n      builder = MIME::Multipart::Builder.new(io, \"boundary\")\n\n      builder.body_part(HTTP::Headers.new)\n\n      builder.epilogue \"string\\r\\n\"\n      builder.epilogue \"slice\\r\\n\".to_slice\n      epilogue_io = IO::Memory.new \"io\\r\\n\"\n      builder.epilogue epilogue_io\n      builder.epilogue do |io|\n        io.print \"io\"\n        io << ' '\n        io.print \"block\"\n        io << \"\\r\\n\"\n      end\n\n      builder.finish\n\n      generated_multipart = io.to_s\n      expected_multipart = <<-MULTIPART\n        --boundary\n        --boundary--\n        string\n        slice\n        io\n        io block\n\n        MULTIPART\n\n      generated_multipart.should eq(expected_multipart.gsub('\\n', \"\\r\\n\"))\n    end\n\n    it \"raises when called after finishing\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      builder.body_part HTTP::Headers.new, \"test\"\n      builder.finish\n\n      expect_raises(MIME::Multipart::Error, \"Cannot generate epilogue: already finished\") do\n        builder.epilogue \"test\"\n      end\n    end\n\n    it \"raises when called with no body parts\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      expect_raises(MIME::Multipart::Error, \"Cannot generate epilogue: no body parts\") do\n        builder.epilogue \"test\"\n      end\n\n      builder.preamble \"test\"\n\n      expect_raises(MIME::Multipart::Error, \"Cannot generate epilogue: no body parts\") do\n        builder.epilogue \"test\"\n      end\n    end\n  end\n\n  describe \".finish\" do\n    it \"raises if no body exists\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      expect_raises(MIME::Multipart::Error, \"Cannot finish multipart: no body parts\") do\n        builder.finish\n      end\n\n      builder.preamble \"test\"\n\n      expect_raises(MIME::Multipart::Error, \"Cannot finish multipart: no body parts\") do\n        builder.finish\n      end\n    end\n\n    it \"raises if already finished\" do\n      builder = MIME::Multipart::Builder.new(IO::Memory.new)\n\n      builder.body_part HTTP::Headers.new, \"test\"\n      builder.finish\n\n      expect_raises(MIME::Multipart::Error, \"Cannot finish multipart: already finished\") do\n        builder.finish\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/mime/multipart/parser_spec.cr",
    "content": "require \"spec\"\nrequire \"mime/multipart\"\n\nprivate def parse(delim, data, *, gsub = true)\n  data_io = IO::Memory.new(gsub ? data.gsub('\\n', \"\\r\\n\") : data)\n  parser = MIME::Multipart::Parser.new(data_io, delim)\n\n  parsed = [] of {headers: HTTP::Headers, body: String}\n  while parser.has_next?\n    parser.next { |h, io| parsed << {headers: h, body: io.gets_to_end} }\n  end\n  parsed\nend\n\ndescribe MIME::Multipart::Parser do\n  it \"parses basic multipart messages\" do\n    data = parse \"AaB03x\", <<-MULTIPART\n      --AaB03x\n      Content-Disposition: form-data; name=\"submit-name\"\n\n      Larry\n      --AaB03x\n      Content-Disposition: form-data; name=\"files\"; filename=\"file1.txt\"\n      Content-Type: text/plain\n\n      ... contents of file1.txt ...\n      --AaB03x--\n      MULTIPART\n\n    data[0][:body].should eq(\"Larry\")\n    data[0][:headers][\"Content-Disposition\"].should eq(\"form-data; name=\\\"submit-name\\\"\")\n\n    data[1][:body].should eq(\"... contents of file1.txt ...\")\n    data[1][:headers][\"Content-Disposition\"].should eq(\"form-data; name=\\\"files\\\"; filename=\\\"file1.txt\\\"\")\n    data[1][:headers][\"Content-Type\"].should eq(\"text/plain\")\n  end\n\n  it \"parses messages with preambles and epilogues\" do\n    data = parse \"AaB03x\", <<-MULTIPART\n      preamble\n      AaB03x\n      --AaB03x\n      Content-Disposition: form-data; name=\"foo\"\n\n      foo\n      --AaB03x\n      Content-Disposition: form-data; name=\"bar\"\n\n      bar\n      --AaB03x--\n      AaB03x\n      epilogue\n      MULTIPART\n\n    data[0][:body].should eq(\"foo\")\n    data[0][:headers][\"Content-Disposition\"].should eq(\"form-data; name=\\\"foo\\\"\")\n\n    data[1][:body].should eq(\"bar\")\n    data[1][:headers][\"Content-Disposition\"].should eq(\"form-data; name=\\\"bar\\\"\")\n  end\n\n  it \"handles invalid multipart data\" do\n    expect_raises(MIME::Multipart::Error, \"EOF reading delimiter\") do\n      parse \"AaB03x\", \"--AaB03x\", gsub: false\n    end\n\n    expect_raises(MIME::Multipart::Error, \"EOF reading delimiter\") do\n      parse \"AaB03x\", \"--AaB03x\\r\\n\\r\\n--AaB03x\", gsub: false\n    end\n\n    expect_raises(MIME::Multipart::Error, \"EOF reading delimiter padding\") do\n      parse \"AaB03x\", \"--AaB03x \", gsub: false\n    end\n\n    expect_raises(MIME::Multipart::Error, \"padding contained non-whitespace character\") do\n      parse \"AaB03x\", \"--AaB03x foo \\r\\n\\r\\n--AaB03x--\", gsub: false\n    end\n  end\n\n  it \"handles padding\" do\n    data = parse \"AaB03x\", \"--AaB03x  \\t\\t  \\r\\n\\r\\n--AaB03x--\", gsub: false\n    data[0][:body].should eq(\"\")\n  end\n\n  it \"raises calling #next after finished\" do\n    input = <<-MULTIPART\n      --AaB03x\n\n      Foo\n      --AaB03x--\n      MULTIPART\n    parser = MIME::Multipart::Parser.new(IO::Memory.new(input.gsub('\\n', \"\\r\\n\")), \"AaB03x\")\n\n    parser.next { }\n    parser.has_next?.should be_false\n\n    expect_raises(MIME::Multipart::Error, \"Multipart parser already finished parsing\") do\n      parser.next { }\n    end\n  end\n\n  it \"raises calling #next after errored\" do\n    parser = MIME::Multipart::Parser.new(IO::Memory.new(\"--AaB03x--\"), \"AaB03x\")\n\n    expect_raises(MIME::Multipart::Error, \"no parts\") do\n      parser.next { }\n    end\n\n    parser.has_next?.should be_false\n\n    expect_raises(MIME::Multipart::Error, \"Multipart parser is in an errored state\") do\n      parser.next { }\n    end\n  end\n\n  it \"handles break/next in blocks\" do\n    input = <<-MULTIPART\n      --b\n\n      --b\n\n      --b\n\n      --b\n\n      --b--\n      MULTIPART\n\n    parser = MIME::Multipart::Parser.new(IO::Memory.new(input.gsub('\\n', \"\\r\\n\")), \"b\")\n\n    ios = [] of IO\n\n    2.times do\n      parser.next do |headers, io|\n        ios << io\n        next\n      end\n    end\n\n    2.times do\n      parser.next do |headers, io|\n        ios << io\n        break\n      end\n    end\n\n    parser.@state.finished?.should be_true\n    ios.each(&.closed?.should(be_true))\n  end\nend\n"
  },
  {
    "path": "spec/std/mime/multipart_spec.cr",
    "content": "require \"spec\"\nrequire \"mime/multipart\"\n\ndescribe MIME::Multipart do\n  describe \".parse\" do\n    it \"parses multipart messages\" do\n      multipart = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nabcd\\r\\n--aA40--\"\n      MIME::Multipart.parse(IO::Memory.new(multipart), \"aA40\") do |headers, io|\n        headers[\"Content-Type\"].should eq(\"text/plain\")\n        io.gets_to_end.should eq(\"abcd\")\n      end\n    end\n  end\n\n  describe \".parse_boundary\" do\n    it \"parses unquoted boundaries\" do\n      content_type = \"multipart/mixed; boundary=a_-47HDS\"\n      MIME::Multipart.parse_boundary(content_type).should eq(\"a_-47HDS\")\n    end\n\n    it \"parses quoted boundaries\" do\n      content_type = %{multipart/mixed; boundary=\"aA_-<>()\"}\n      MIME::Multipart.parse_boundary(content_type).should eq(%{aA_-<>()})\n    end\n  end\n\n  describe \".parse\" do\n    it \"parses multipart messages\" do\n      headers = HTTP::Headers{\"Content-Type\" => \"multipart/mixed; boundary=aA40\"}\n      body = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n      request = HTTP::Request.new(\"POST\", \"/\", headers, body)\n\n      MIME::Multipart.parse(request) do |headers, io|\n        headers[\"Content-Type\"].should eq(\"text/plain\")\n        io.gets_to_end.should eq(\"body\")\n      end\n    end\n\n    it \"parses multipart messages from HTTP client responses\" do\n      headers = HTTP::Headers{\"Content-Type\" => \"multipart/byteranges; boundary=aA40\"}\n      body = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n      response = HTTP::Client::Response.new(\n        status: :ok,\n        headers: headers,\n        body: body,\n      )\n\n      MIME::Multipart.parse(response) do |headers, io|\n        headers[\"Content-Type\"].should eq(\"text/plain\")\n        io.gets_to_end.should eq(\"body\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/mime_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"mime\"\n\nmodule MIME\n  def self.initialized\n    @@initialized\n  end\n\n  def self.reset!\n    @@initialized = false\n    @@types = {} of String => String\n    @@types_lower = {} of String => String\n    @@extensions = {} of String => Set(String)\n  end\nend\n\ndescribe MIME do\n  before_each do\n    MIME.reset!\n  end\n\n  after_each do\n    MIME.reset!\n  end\n\n  it \".from_extension\" do\n    MIME.init\n    MIME.from_extension(\".html\").partition(';')[0].should eq \"text/html\"\n    MIME.from_extension(\".HTML\").partition(';')[0].should eq \"text/html\"\n\n    expect_raises KeyError do\n      MIME.from_extension(\".fooobar\")\n    end\n    MIME.from_extension(\".fooobar\", \"default/fooobar\").should eq \"default/fooobar\"\n    MIME.from_extension(\".fooobar\") { \"default/fooobar\" }.should eq \"default/fooobar\"\n  end\n\n  it \".from_extension?\" do\n    MIME.init\n    MIME.from_extension?(\".html\").should eq MIME.from_extension(\".html\")\n    MIME.from_extension?(\".HTML\").should eq MIME.from_extension(\".HTML\")\n\n    MIME.from_extension?(\".fooobar\").should be_nil\n  end\n\n  describe \".from_filename\" do\n    it String do\n      MIME.init\n      MIME.from_filename(\"test.html\").should eq MIME.from_extension(\".html\")\n      MIME.from_filename(\"foo/bar.not-exists\", \"foo/bar-exist\").should eq \"foo/bar-exist\"\n      MIME.from_filename(\"foo/bar.not-exists\") { \"foo/bar-exist\" }.should eq \"foo/bar-exist\"\n    end\n\n    it Path do\n      MIME.init\n      MIME.from_filename(Path[\"test.html\"]).should eq MIME.from_extension(\".html\")\n      MIME.from_filename(Path[\"foo/bar.not-exists\"], \"foo/bar-exist\").should eq \"foo/bar-exist\"\n      MIME.from_filename(Path[\"foo/bar.not-exists\"]) { \"foo/bar-exist\" }.should eq \"foo/bar-exist\"\n    end\n  end\n\n  describe \".from_filename?\" do\n    it String do\n      MIME.init\n      MIME.from_filename?(\"test.html\").should eq MIME.from_extension(\".html\")\n    end\n\n    it Path do\n      MIME.init\n      MIME.from_filename?(Path[\"test.html\"]).should eq MIME.from_extension(\".html\")\n    end\n  end\n\n  describe \".register\" do\n    it \"registers new type\" do\n      MIME.init(load_defaults: false)\n      MIME.register(\".Custom-Type\", \"text/custom-type\")\n\n      MIME.from_extension(\".Custom-Type\").should eq \"text/custom-type\"\n      MIME.from_extension(\".custom-type\").should eq \"text/custom-type\"\n      MIME.extensions(\"text/custom-type\").should eq Set{\".Custom-Type\"}\n\n      MIME.register(\".custom-type2\", \"text/custom-type\")\n      MIME.extensions(\"text/custom-type\").should eq Set{\".Custom-Type\", \".custom-type2\"}\n\n      MIME.register(\".custom-type\", \"text/custom-type-lower\")\n      MIME.from_extension(\".custom-type\").should eq \"text/custom-type-lower\"\n      MIME.from_extension(\".Custom-Type\").should eq \"text/custom-type\"\n    end\n\n    it \"fails for invalid extension\" do\n      expect_raises ArgumentError, \"Extension does not start with a dot\" do\n        MIME.register(\"foo\", \"text/foo\")\n      end\n\n      expect_raises ArgumentError, \"String contains null byte\" do\n        MIME.register(\".foo\\0\", \"text/foo\")\n      end\n    end\n  end\n\n  describe \".extensions\" do\n    it \"lists extensions\" do\n      MIME.init\n      MIME.extensions(\"text/html\").should contain \".htm\"\n      MIME.extensions(\"text/html\").should contain \".html\"\n    end\n\n    it \"returns empty set\" do\n      MIME.init(load_defaults: false)\n      MIME.extensions(\"foo/bar\").should eq Set(String).new\n    end\n\n    it \"recognizes overridden types\" do\n      MIME.init(load_defaults: false)\n      MIME.register(\".custom-type-overridden\", \"text/custom-type-overridden\")\n      MIME.register(\".custom-type-overridden\", \"text/custom-type-override\")\n\n      MIME.extensions(\"text/custom-type-overridden\").should eq Set(String).new\n    end\n  end\n\n  it \"parses media types\" do\n    MIME.init(load_defaults: false)\n    MIME.register(\".parse-media-type1\", \"text/html; charset=utf-8\")\n    MIME.extensions(\"text/html\").should contain(\".parse-media-type1\")\n\n    MIME.register(\".parse-media-type2\", \"text/html; foo = bar; bar= foo ;\")\n    MIME.extensions(\"text/html\").should contain(\".parse-media-type2\")\n\n    MIME.register(\".parse-media-type3\", \"foo/bar\")\n    MIME.extensions(\"foo/bar\").should contain(\".parse-media-type3\")\n\n    MIME.register(\".parse-media-type4\", \" form-data ; name=foo\")\n    MIME.extensions(\"form-data\").should contain(\".parse-media-type4\")\n\n    MIME.register(\".parse-media-type41\", %(FORM-DATA;name=\"foo\"))\n    MIME.extensions(\"form-data\").should contain(\".parse-media-type41\")\n\n    MIME.register(\".parse-media-type5\", %( FORM-DATA ; name=\"foo\"))\n    MIME.extensions(\"form-data\").should contain(\".parse-media-type5\")\n\n    expect_raises ArgumentError, \"Invalid media type\" do\n      MIME.register(\".parse-media-type6\", \": inline; attachment; filename=foo.html\")\n    end\n\n    expect_raises ArgumentError, \"Invalid media type\" do\n      MIME.register(\".parse-media-type7\", \"filename=foo.html, filename=bar.html\")\n    end\n\n    expect_raises ArgumentError, \"Invalid media type\" do\n      MIME.register(\".parse-media-type8\", %(\"foo; filename=bar;baz\"; filename=qux))\n    end\n\n    expect_raises ArgumentError, \"Invalid media type\" do\n      MIME.register(\".parse-media-type9\", \"x=y; filename=foo.html\")\n    end\n\n    expect_raises ArgumentError, \"Invalid media type\" do\n      MIME.register(\".parse-media-type10\", \"filename=foo.html\")\n    end\n  end\n\n  it \".load_mime_database\" do\n    MIME.init(load_defaults: false)\n    MIME.from_extension?(\".bar\").should be_nil\n    MIME.from_extension?(\".fbaz\").should be_nil\n\n    MIME.load_mime_database IO::Memory.new <<-EOF\n      foo/bar          bar\n      foo/baz          baz fbaz #foobaz\n      # foo/foo        foo\n    EOF\n\n    MIME.from_extension?(\".bar\").should eq \"foo/bar\"\n    MIME.from_extension?(\".fbaz\").should eq \"foo/baz\"\n    MIME.from_extension?(\".#foobaz\").should be_nil\n    MIME.from_extension?(\".foobaz\").should be_nil\n    MIME.from_extension?(\".foo\").should be_nil\n  end\n\n  describe \".init\" do\n    it \"loads defaults\" do\n      MIME.init\n      MIME.initialized.should be_true\n      MIME.from_extension(\".html\").partition(';')[0].should eq \"text/html\"\n    end\n\n    it \"skips loading defaults\" do\n      MIME.init(load_defaults: false)\n      MIME.initialized.should be_true\n      MIME.from_extension?(\".html\").should be_nil\n    end\n\n    it \"loads file\" do\n      MIME.initialized.should be_false\n      MIME.init(datapath(\"mime.types\"))\n      MIME.from_extension?(\".foo\").should eq \"foo/bar\"\n    end\n  end\n\n  {% if flag?(:win32) %}\n    it \"loads MIME data from registry\" do\n      MIME.register(\".wma\", \"non-initialized\")\n      MIME.init\n      MIME.from_extension?(\".wma\").should eq \"audio/x-ms-wma\"\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/mutex_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/fibers\"\n\ndescribe Mutex do\n  it \"locks and unlocks\" do\n    mutex = Mutex.new\n    mutex.lock\n    mutex.unlock\n  end\n\n  it \"works with multiple threads\" do\n    x = 0\n    mutex = Mutex.new\n\n    fibers = Array.new(10) do\n      spawn do\n        100.times do\n          mutex.synchronize { x += 1 }\n        end\n      end\n    end\n\n    fibers.each do |f|\n      wait_until_finished f\n    end\n\n    x.should eq(1000)\n  end\n\n  describe \"checked\" do\n    it \"raises if locked recursively\" do\n      mutex = Mutex.new\n      mutex.lock\n      expect_raises(Sync::Error::Deadlock, \"Can't lock mutex recursively\") do\n        mutex.lock\n      end\n    end\n\n    it \"raises if unlocks without lock\" do\n      mutex = Mutex.new\n      expect_raises(Sync::Error, \"Can't unlock Sync::Mutex that isn't locked\") do\n        mutex.unlock\n      end\n    end\n\n    it \"can't be unlocked by another fiber\" do\n      mutex = nil\n      done = false\n\n      spawn do\n        mutex = Mutex.new\n        mutex.not_nil!.lock\n        done = true\n      end\n\n      until done\n        Fiber.yield\n      end\n\n      expect_raises(Sync::Error, \"Can't unlock Sync::Mutex locked by another fiber\") do\n        mutex.not_nil!.unlock\n      end\n    end\n  end\n\n  describe \"reentrant\" do\n    it \"can be locked many times from the same fiber\" do\n      mutex = Mutex.new(:reentrant)\n      mutex.lock\n      mutex.lock\n      mutex.unlock\n      mutex.unlock\n    end\n\n    it \"raises if unlocks without lock\" do\n      mutex = Mutex.new(:reentrant)\n      expect_raises(Sync::Error, \"Can't unlock Sync::Mutex that isn't locked\") do\n        mutex.unlock\n      end\n    end\n\n    it \"can't be unlocked by another fiber\" do\n      mutex = nil\n      done = false\n\n      spawn do\n        mutex = Mutex.new(:reentrant)\n        mutex.not_nil!.lock\n        done = true\n      end\n\n      until done\n        Fiber.yield\n      end\n\n      expect_raises(Sync::Error, \"Can't unlock Sync::Mutex locked by another fiber\") do\n        mutex.not_nil!.unlock\n      end\n    end\n  end\n\n  describe \"unchecked\" do\n    it \"can lock and unlock from multiple fibers\" do\n      mutex = Mutex.new(:unchecked)\n\n      a = 1\n      two = false\n      three = false\n      four = false\n      ch = Channel(Nil).new\n\n      spawn do\n        mutex.synchronize do\n          a = 2\n          Fiber.yield\n          two = a == 2\n        end\n        ch.send(nil)\n      end\n\n      spawn do\n        mutex.synchronize do\n          a = 3\n          Fiber.yield\n          three = a == 3\n        end\n        ch.send(nil)\n      end\n\n      spawn do\n        mutex.synchronize do\n          a = 4\n          Fiber.yield\n          four = a == 4\n        end\n        ch.send(nil)\n      end\n\n      3.times { ch.receive }\n\n      two.should be_true\n      three.should be_true\n      four.should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/named_tuple_spec.cr",
    "content": "require \"spec\"\n\nprivate record NamedTupleSpecObj, x : Int32 do\n  def_equals @x\nend\n\ndescribe \"NamedTuple\" do\n  it \"does NamedTuple.new, without type vars\" do\n    NamedTuple.new(x: 1, y: 2).should eq({x: 1, y: 2})\n    NamedTuple.new(z: NamedTupleSpecObj.new(10)).should eq({z: NamedTupleSpecObj.new(10)})\n  end\n\n  it \"does NamedTuple.new, with type vars\" do\n    NamedTuple(foo: Int32, bar: String).new(foo: 1, bar: \"a\").should eq({foo: 1, bar: \"a\"})\n    NamedTuple(z: NamedTupleSpecObj).new(z: NamedTupleSpecObj.new(10)).should eq({z: NamedTupleSpecObj.new(10)})\n    typeof(NamedTuple.new).new.should eq(NamedTuple.new)\n\n    t = NamedTuple(foo: Int32 | String, bar: Int32 | String).new(foo: 1, bar: \"a\")\n    t.should eq({foo: 1, bar: \"a\"})\n    t.class.should_not eq(NamedTuple(foo: Int32, bar: String))\n  end\n\n  it \"does NamedTuple.new, with hyphen in key\" do\n    NamedTuple(\"a-b\": String).new(\"a-b\": \"foo\").should eq({\"a-b\": \"foo\"})\n  end\n\n  it \"does NamedTuple.from\" do\n    t = NamedTuple(foo: Int32, bar: Int32).from({:foo => 1, :bar => 2})\n    t.should eq({foo: 1, bar: 2})\n    t.class.should eq(NamedTuple(foo: Int32, bar: Int32))\n\n    t = NamedTuple(foo: Int32, bar: Int32).from({\"foo\" => 1, \"bar\" => 2})\n    t.should eq({foo: 1, bar: 2})\n    t.class.should eq(NamedTuple(foo: Int32, bar: Int32))\n\n    t = NamedTuple(\"foo bar\": Int32, \"baz qux\": Int32).from({\"foo bar\" => 1, \"baz qux\" => 2})\n    t.should eq({\"foo bar\": 1, \"baz qux\": 2})\n    t.class.should eq(NamedTuple(\"foo bar\": Int32, \"baz qux\": Int32))\n\n    t = NamedTuple(\"\\\"\": Int32, \"\\#{exit}\": Int32).from({\"\\\"\" => 2, \"\\#{exit}\" => 3})\n    t.should eq({\"\\\"\": 2, \"\\#{exit}\": 3})\n    t.class.should eq(NamedTuple(\"\\\"\": Int32, \"\\#{exit}\": Int32))\n\n    expect_raises ArgumentError do\n      NamedTuple(foo: Int32, bar: Int32).from({:foo => 1})\n    end\n\n    expect_raises KeyError do\n      NamedTuple(foo: Int32, bar: Int32).from({:foo => 1, :baz => 2})\n    end\n\n    expect_raises(TypeCastError, /[Cc]ast from String to Int32 failed/) do\n      NamedTuple(foo: Int32, bar: Int32).from({:foo => 1, :bar => \"foo\"})\n    end\n  end\n\n  it \"does NamedTuple#from\" do\n    t = {foo: Int32, bar: Int32}.from({:foo => 1, :bar => 2})\n    t.should eq({foo: 1, bar: 2})\n    t.class.should eq(NamedTuple(foo: Int32, bar: Int32))\n\n    t = {foo: Int32, bar: Int32}.from({\"foo\" => 1, \"bar\" => 2})\n    t.should eq({foo: 1, bar: 2})\n    t.class.should eq(NamedTuple(foo: Int32, bar: Int32))\n\n    expect_raises ArgumentError do\n      {foo: Int32, bar: Int32}.from({:foo => 1})\n    end\n\n    expect_raises KeyError do\n      {foo: Int32, bar: Int32}.from({:foo => 1, :baz => 2})\n    end\n\n    expect_raises(TypeCastError, /[Cc]ast from String to Int32 failed/) do\n      {foo: Int32, bar: Int32}.from({:foo => 1, :bar => \"foo\"})\n    end\n\n    t = {foo: Int32, bar: Int32}.from({\"foo\" => 1, :bar => 2})\n    t.should eq({foo: 1, bar: 2})\n    t.class.should eq(NamedTuple(foo: Int32, bar: Int32))\n\n    t = {foo: Int32, bar: Int32}.from({\"foo\" => 1, :bar => 2} of String | Int32 | Symbol => Int32)\n    t.should eq({foo: 1, bar: 2})\n    t.class.should eq(NamedTuple(foo: Int32, bar: Int32))\n\n    t = {\"\\\"\": Int32, \"\\#{exit}\": Int32}.from({\"\\\"\" => 2, \"\\#{exit}\" => 3})\n    t.should eq({\"\\\"\": 2, \"\\#{exit}\": 3})\n    t.class.should eq(NamedTuple(\"\\\"\": Int32, \"\\#{exit}\": Int32))\n  end\n\n  it \"gets size\" do\n    {a: 1, b: 3}.size.should eq(2)\n  end\n\n  describe \"#[] with non-literal index\" do\n    it \"gets named tuple value with Symbol key\" do\n      tup = {a: 1, b: 'a'}\n\n      key = :a\n      val = tup[key]\n      val.should eq(1)\n      typeof(val).should eq(Int32 | Char)\n\n      key = :b\n      val = tup[key]\n      val.should eq('a')\n      typeof(val).should eq(Int32 | Char)\n    end\n\n    it \"gets named tuple value with String key\" do\n      tup = {a: 1, b: 'a'}\n\n      key = \"a\"\n      val = tup[key]\n      val.should eq(1)\n      typeof(val).should eq(Int32 | Char)\n\n      key = \"b\"\n      val = tup[key]\n      val.should eq('a')\n      typeof(val).should eq(Int32 | Char)\n    end\n\n    it \"raises missing key\" do\n      tup = {a: 1, b: 'a'}\n      key = :c\n      expect_raises(KeyError) { tup[key] }\n      key = \"d\"\n      expect_raises(KeyError) { tup[key] }\n    end\n  end\n\n  describe \"#[]? with non-literal index\" do\n    it \"gets named tuple value or nil with Symbol key\" do\n      tup = {a: 1, b: 'a'}\n\n      key = :a\n      val = tup[key]?\n      val.should eq(1)\n      typeof(val).should eq(Int32 | Char | Nil)\n\n      key = :b\n      val = tup[key]?\n      val.should eq('a')\n      typeof(val).should eq(Int32 | Char | Nil)\n\n      key = :c\n      val = tup[key]?\n      val.should be_nil\n      typeof(val).should eq(Int32 | Char | Nil)\n    end\n\n    it \"gets named tuple value or nil with String key\" do\n      tup = {a: 1, b: 'a'}\n\n      key = \"a\"\n      val = tup[key]?\n      val.should eq(1)\n      typeof(val).should eq(Int32 | Char | Nil)\n\n      key = \"b\"\n      val = tup[key]?\n      val.should eq('a')\n      typeof(val).should eq(Int32 | Char | Nil)\n\n      key = \"c\"\n      val = tup[key]?\n      val.should be_nil\n      typeof(val).should eq(Int32 | Char | Nil)\n    end\n  end\n\n  describe \".[] with non-literal index\" do\n    it \"gets named tuple metaclass value with Symbol key\" do\n      tup = NamedTuple(a: Int32, b: Char)\n\n      key = :a\n      val = tup[key]\n      val.should eq(Int32)\n      typeof(val).should eq(Union(Int32.class, Char.class))\n\n      key = :b\n      val = tup[key]\n      val.should eq(Char)\n      typeof(val).should eq(Union(Int32.class, Char.class))\n    end\n\n    it \"gets named tuple metaclass value with String key\" do\n      tup = NamedTuple(a: Int32, b: Char)\n\n      key = \"a\"\n      val = tup[key]\n      val.should eq(Int32)\n      typeof(val).should eq(Union(Int32.class, Char.class))\n\n      key = \"b\"\n      val = tup[key]\n      val.should eq(Char)\n      typeof(val).should eq(Union(Int32.class, Char.class))\n    end\n\n    it \"raises missing key\" do\n      tup = NamedTuple(a: Int32, b: Char)\n      key = :c\n      expect_raises(KeyError) { tup[key] }\n      key = \"d\"\n      expect_raises(KeyError) { tup[key] }\n    end\n  end\n\n  describe \".[]? with non-literal index\" do\n    it \"gets named tuple metaclass value or nil with Symbol key\" do\n      tup = NamedTuple(a: Int32, b: Char)\n\n      key = :a\n      val = tup[key]?\n      val.should eq(Int32)\n      typeof(val).should eq(Union(Int32.class, Char.class, Nil))\n\n      key = :b\n      val = tup[key]?\n      val.should eq(Char)\n      typeof(val).should eq(Union(Int32.class, Char.class, Nil))\n\n      key = :c\n      val = tup[key]?\n      val.should be_nil\n      typeof(val).should eq(Union(Int32.class, Char.class, Nil))\n    end\n\n    it \"gets named tuple metaclass value or nil with String key\" do\n      tup = NamedTuple(a: Int32, b: Char)\n\n      key = \"a\"\n      val = tup[key]?\n      val.should eq(Int32)\n      typeof(val).should eq(Union(Int32.class, Char.class, Nil))\n\n      key = \"b\"\n      val = tup[key]?\n      val.should eq(Char)\n      typeof(val).should eq(Union(Int32.class, Char.class, Nil))\n\n      key = \"c\"\n      val = tup[key]?\n      val.should be_nil\n      typeof(val).should eq(Union(Int32.class, Char.class, Nil))\n    end\n  end\n\n  describe \"#dig?\" do\n    it \"gets the value at given path given splat\" do\n      h = {a: {b: {c: [10, 20]}}, x: {a: \"b\"}}\n\n      h.dig?(:a, :b, :c).should eq([10, 20])\n      h.dig?(\"x\", \"a\").should eq(\"b\")\n    end\n\n    it \"returns nil if not found\" do\n      h = {a: {b: {c: 300}}, x: {a: \"b\"}}\n\n      h.dig?(\"a\", \"b\", \"c\", \"d\", \"e\").should be_nil\n      h.dig?(\"z\").should be_nil\n      h.dig?(\"\").should be_nil\n    end\n  end\n\n  describe \"#dig\" do\n    it \"gets the value at given path given splat\" do\n      h = {a: {b: {c: [10, 20]}}, x: {a: \"b\", c: nil}}\n\n      h.dig(:a, :b, :c).should eq([10, 20])\n      h.dig(\"x\", \"a\").should eq(\"b\")\n      h.dig(\"x\", \"c\").should be_nil\n    end\n\n    it \"raises KeyError if not found\" do\n      h = {a: {b: {c: 300}}, x: {a: \"b\"}}\n\n      expect_raises KeyError, %(NamedTuple value not diggable for key: \"c\") do\n        h.dig(\"a\", \"b\", \"c\", \"d\", \"e\")\n      end\n      expect_raises KeyError, %(Missing named tuple key: \"z\") do\n        h.dig(\"z\")\n      end\n      expect_raises KeyError, %(Missing named tuple key: \"\") do\n        h.dig(\"\")\n      end\n    end\n  end\n\n  it \"computes a hash value\" do\n    tup1 = {a: 1, b: 'a'}\n    tup1.hash.should eq(tup1.dup.hash)\n\n    tup2 = {b: 'a', a: 1}\n    tup2.hash.should eq(tup1.hash)\n  end\n\n  it \"does each\" do\n    tup = {a: 1, b: \"hello\"}\n    i = 0\n    tup.each do |key, value|\n      case i\n      when 0\n        key.should eq(:a)\n        value.should eq(1)\n      when 1\n        key.should eq(:b)\n        value.should eq(\"hello\")\n      else\n        fail \"shouldn't happen\"\n      end\n      i += 1\n    end.should be_nil\n    i.should eq(2)\n  end\n\n  it \"does each_key\" do\n    tup = {a: 1, b: \"hello\"}\n    i = 0\n    tup.each_key do |key|\n      case i\n      when 0\n        key.should eq(:a)\n      when 1\n        key.should eq(:b)\n      else\n        fail \"shouldn't happen\"\n      end\n      i += 1\n    end.should be_nil\n    i.should eq(2)\n  end\n\n  it \"does each_value\" do\n    tup = {a: 1, b: \"hello\"}\n    i = 0\n    tup.each_value do |value|\n      case i\n      when 0\n        value.should eq(1)\n      when 1\n        value.should eq(\"hello\")\n      else\n        fail \"shouldn't happen\"\n      end\n      i += 1\n    end.should be_nil\n    i.should eq(2)\n  end\n\n  it \"does each_with_index\" do\n    tup = {a: 1, b: \"hello\"}\n    i = 0\n    tup.each_with_index do |key, value, index|\n      case i\n      when 0\n        key.should eq(:a)\n        value.should eq(1)\n        index.should eq(0)\n      when 1\n        key.should eq(:b)\n        value.should eq(\"hello\")\n        index.should eq(1)\n      else\n        fail \"shouldn't happen\"\n      end\n      i += 1\n    end.should be_nil\n    i.should eq(2)\n  end\n\n  it \"does has_key? with symbol\" do\n    tup = {a: 1, b: 'a'}\n    tup.has_key?(:a).should be_true\n    tup.has_key?(:b).should be_true\n    tup.has_key?(:c).should be_false\n  end\n\n  it \"does has_key? with string\" do\n    tup = {a: 1, b: 'a'}\n    tup.has_key?(\"a\").should be_true\n    tup.has_key?(\"b\").should be_true\n    tup.has_key?(\"c\").should be_false\n  end\n\n  it \"does empty\" do\n    {a: 1}.empty?.should be_false\n    NamedTuple.new.empty?.should be_true\n  end\n\n  describe \"#to_a\" do\n    it \"creates an array of key-value pairs\" do\n      tup = {a: 1, b: 'a'}\n      tup.to_a.should eq([{:a, 1}, {:b, 'a'}])\n    end\n\n    it \"preserves key type for empty named tuples\" do\n      tup = NamedTuple.new\n      arr = tup.to_a\n      arr.should be_empty\n      arr.should be_a(Array({Symbol, NoReturn}))\n    end\n  end\n\n  it \"does map\" do\n    tup = {a: 1, b: 'a'}\n    strings = tup.map { |k, v| \"#{k.inspect}-#{v.inspect}\" }\n    strings.should eq([\":a-1\", \":b-'a'\"])\n  end\n\n  it \"compares with same named tuple type\" do\n    tup1 = {a: 1, b: 'a'}\n    tup2 = {b: 'a', a: 1}\n    tup3 = {a: 1, b: 'b'}\n    tup1.should eq(tup2)\n    tup1.should_not eq(tup3)\n  end\n\n  it \"compares with other named tuple type\" do\n    tup1 = {a: 1, b: 'a'}\n    tup2 = {b: 'a', a: 1.0}\n    tup3 = {b: 'a', a: 1.1}\n    tup1.should eq(tup2)\n    tup1.should_not eq(tup3)\n  end\n\n  it \"compares with named tuple union (#5131)\" do\n    tup1 = {a: 1, b: 'a'}\n    tup2 = {a: 1, c: 'b'}\n    u = tup1 || tup2\n    u.should eq(u)\n\n    v = tup2 || tup1\n    u.should_not eq(v)\n  end\n\n  describe \"#to_h\" do\n    it \"creates a hash\" do\n      tup1 = {a: 1, b: \"hello\"}\n      hash = tup1.to_h\n      hash.should eq({:a => 1, :b => \"hello\"})\n    end\n\n    it \"creates an empty hash from an empty named tuple\" do\n      tup = NamedTuple.new\n      hash = tup.to_h\n      hash.should be_empty\n      hash.should be_a(Hash(Symbol, NoReturn))\n    end\n  end\n\n  it \"does to_s\" do\n    tup = {a: 1, b: \"hello\"}\n    tup.to_s.should eq(%({a: 1, b: \"hello\"}))\n  end\n\n  it \"dups\" do\n    tup1 = {a: 1, b: [1, 2, 3]}\n    tup2 = tup1.dup\n\n    tup1[:b] << 4\n    tup2[:b].should be(tup1[:b])\n  end\n\n  it \"clones\" do\n    tup1 = {a: 1, b: [1, 2, 3]}\n    tup2 = tup1.clone\n\n    tup1[:b] << 4\n    tup2[:b].should eq([1, 2, 3])\n\n    tup2 = {\"foo bar\": 1}\n    tup2.clone.should eq(tup2)\n\n    tup3 = NamedTuple.new\n    tup3.clone.should eq(tup3)\n  end\n\n  it \"does keys\" do\n    tup = {a: 1, b: 2}\n    tup.keys.should eq({:a, :b})\n  end\n\n  it \"does sorted_keys\" do\n    tup = {foo: 1, bar: 2, baz: 3}\n    tup.sorted_keys.should eq({:bar, :baz, :foo})\n  end\n\n  it \"does values\" do\n    tup = {a: 1, b: 'a'}\n    tup.values.should eq({1, 'a'})\n  end\n\n  it \"merges with other named tuple\" do\n    a = {one: 1, two: 2, three: 3, four: 4, five: 5, \"im \\\"string\": \"works\"}\n    b = {two: \"Two\", three: true, \"new one\": \"ok\"}\n    a.merge(b).merge(four: \"Four\").merge(NamedTuple.new).should eq({one: 1, two: \"Two\", three: true, four: \"Four\", five: 5, \"new one\": \"ok\", \"im \\\"string\": \"works\"})\n  end\n\n  it \"merges two empty named tuples\" do\n    NamedTuple.new.merge(NamedTuple.new).should eq(NamedTuple.new)\n  end\n\n  it \"does reverse merge with other named tuple\" do\n    a = {one: 1, two: 2, three: 3, four: 4, five: 5, \"im \\\"string\": \"works\"}\n    b = {two: \"Two\", three: true, \"new one\": \"ok\"}\n    a.reverse_merge(b).reverse_merge(four: \"Four\").reverse_merge(NamedTuple.new).should eq({one: 1, two: 2, three: 3, four: 4, five: 5, \"new one\": \"ok\", \"im \\\"string\": \"works\"})\n  end\n\n  it \"does reverse merge two empty named tuples\" do\n    NamedTuple.new.reverse_merge(NamedTuple.new).should eq(NamedTuple.new)\n  end\n\n  it \"does types\" do\n    tuple = {a: 1, b: 'a', c: \"hello\"}\n    tuple.class.types.to_s.should eq(\"{a: Int32, b: Char, c: String}\")\n  end\nend\n"
  },
  {
    "path": "spec/std/number_spec.cr",
    "content": "require \"spec\"\nrequire \"big\"\nrequire \"complex\"\nrequire \"../support/number\"\n\ndescribe \"Number\" do\n  {% for number_type in BUILTIN_NUMBER_TYPES %}\n    it_unchecked_initializes_from_value_to {{number_type}}\n    it_initializes_from_value_to {{number_type}}\n  {% end %}\n\n  it_can_convert_between({{BUILTIN_NUMBER_TYPES}}, {{BUILTIN_NUMBER_TYPES}})\n\n  describe \"significant\" do\n    it \"10 base\" do\n      1234.567.significant(1).should eq(1000)\n      1234.567.significant(2).should eq(1200)\n      1234.567.significant(3).should eq(1230)\n      1234.567.significant(4).should eq(1235)\n      1234.567.significant(5).should be_close(1234.6, 1e-7)\n      1234.567.significant(6).should eq(1234.57)\n      1234.567.significant(7).should eq(1234.567)\n\n      123.456789.significant(5).should eq(123.46)\n      0.000123456789.significant(3).should eq 0.000123\n      0.123456789.significant(4).should eq 0.1235\n      1.23456789.significant(2).should eq 1.2\n    end\n\n    it \"2 base\" do\n      -1763.116.significant(2, base: 2).should eq(-1536.0)\n      753.155.significant(3, base: 2).should eq(768.0)\n      15.159.significant(1, base: 2).should eq(16.0)\n    end\n\n    it \"8 base\" do\n      -1763.116.significant(2, base: 8).should eq(-1792.0)\n      753.155.significant(3, base: 8).should eq(752.0)\n      15.159.significant(1, base: 8).should eq(16.0)\n    end\n\n    it \"preserves type\" do\n      123.significant(2).should eq(120)\n      123.significant(2).should be_a(Int32)\n      0.significant(1).should be_a(Int32)\n    end\n  end\n\n  describe \"#round\" do\n    it \"rounds to nearest integer\" do\n      5.5.round.should eq(6)\n      0.4.round.should eq(0)\n      -2.8.round.should eq(-3)\n      0.0.round.should eq(0)\n      0.49999999999999994.round.should eq(0)\n      -1763.116.round.should eq(-1763)\n      753.155.round.should eq(753)\n      15.151.round.should eq(15)\n    end\n\n    it \"infinity Float64\" do\n      Float64::INFINITY.round.should eq Float64::INFINITY\n      Float64::INFINITY.round(digits: 0).should eq Float64::INFINITY\n      Float64::INFINITY.round(digits: 3).should eq Float64::INFINITY\n      Float64::INFINITY.round(digits: -3).should eq Float64::INFINITY\n      (-Float64::INFINITY).round.should eq -Float64::INFINITY\n      (-Float64::INFINITY).round(digits: 0).should eq -Float64::INFINITY\n      (-Float64::INFINITY).round(digits: 3).should eq -Float64::INFINITY\n      (-Float64::INFINITY).round(digits: -3).should eq -Float64::INFINITY\n    end\n\n    {% if compare_versions(Crystal::VERSION, \"0.36.1\") > 0 %}\n      it \"infinity Float32\" do\n        Float32::INFINITY.round.should eq Float32::INFINITY\n        Float32::INFINITY.round(digits: 0).should eq Float32::INFINITY\n        Float32::INFINITY.round(digits: 3).should eq Float32::INFINITY\n        Float32::INFINITY.round(digits: -3).should eq Float32::INFINITY\n        (-Float32::INFINITY).round.should eq -Float32::INFINITY\n        (-Float32::INFINITY).round(digits: 0).should eq -Float32::INFINITY\n        (-Float32::INFINITY).round(digits: 3).should eq -Float32::INFINITY\n        (-Float32::INFINITY).round(digits: -3).should eq -Float32::INFINITY\n      end\n    {% else %}\n      pending \"infinity Float32\"\n    {% end %}\n\n    it \"nan\" do\n      Float64::NAN.round.nan?.should be_true\n      Float32::NAN.round.nan?.should be_true\n    end\n\n    it \"rounds to digits\" do\n      5.5.round(0).should eq(6)\n      5.7.round(1).should eq(5.7)\n      1.2345678.round(2).should eq(1.23)\n      123456.78.round(-2).should eq(123500) # rounded up\n      -123456.78.round(-2).should eq(-123500)\n\n      -1763.116.round(2).should eq(-1763.12)\n      753.155.round(2).should eq(753.16)\n      15.151.round(2).should eq(15.15)\n\n      0.8346268.round(-1).should eq(0)\n    end\n\n    it { 42.0.round(300).should eq(42.0) }\n\n    pending \"edge cases\" do\n      42.0.round(308).should eq(42.0)\n      42.0.round(309).should eq(42.0)\n\n      1.0e307.round(2).should eq(1.0e307)\n      0.42.round(2**30).should eq(0.42)\n    end\n\n    it \"preserves type\" do\n      1.round.should be_a(Int32)\n      1_u8.round.should be_a(UInt8)\n      1.0_f32.round.should be_a(Float32)\n    end\n\n    it \"negative digits\" do\n      123.round(-2).should eq(100)\n      123.round(-3).should eq(0)\n      523.round(-3).should eq(1000)\n\n      123.456.round(-2).should eq(100)\n      123_456.123456.round(-5).should eq(100_000)\n    end\n\n    it \"accepts unsigned precision\" do\n      123.round(UInt8.new(3)).should eq(123)\n      11.308.round(UInt8.new(3)).should eq(11.308)\n      11.308.round(UInt8.new(2)).should eq(11.31)\n    end\n\n    it \"handle medium amount of digits\" do\n      1.098765432109876543210987654321.round(15).should eq(1.098765432109876)\n      1.098765432109876543210987654321.round(21).should eq(1.098765432109876543211)\n      6543210987654321.0.round(-15).should eq(7000000000000000.0)\n    end\n\n    describe \"rounding modes\" do\n      it \"to_zero\" do\n        -1.5.round(:to_zero).should eq -1.0\n        -1.0.round(:to_zero).should eq -1.0\n        -0.9.round(:to_zero).should eq 0.0\n        -0.5.round(:to_zero).should eq 0.0\n        -0.1.round(:to_zero).should eq 0.0\n        0.0.round(:to_zero).should eq 0.0\n        0.1.round(:to_zero).should eq 0.0\n        0.5.round(:to_zero).should eq 0.0\n        0.9.round(:to_zero).should eq 0.0\n        1.0.round(:to_zero).should eq 1.0\n        1.5.round(:to_zero).should eq 1.0\n      end\n\n      it \"to_positive\" do\n        -1.5.round(:to_positive).should eq -1.0\n        -1.0.round(:to_positive).should eq -1.0\n        -0.9.round(:to_positive).should eq 0.0\n        -0.5.round(:to_positive).should eq 0.0\n        -0.1.round(:to_positive).should eq 0.0\n        0.0.round(:to_positive).should eq 0.0\n        0.1.round(:to_positive).should eq 1.0\n        0.5.round(:to_positive).should eq 1.0\n        0.9.round(:to_positive).should eq 1.0\n        1.0.round(:to_positive).should eq 1.0\n        1.5.round(:to_positive).should eq 2.0\n      end\n\n      it \"to_negative\" do\n        -1.5.round(:to_negative).should eq -2.0\n        -1.0.round(:to_negative).should eq -1.0\n        -0.9.round(:to_negative).should eq -1.0\n        -0.5.round(:to_negative).should eq -1.0\n        -0.1.round(:to_negative).should eq -1.0\n        0.0.round(:to_negative).should eq 0.0\n        0.1.round(:to_negative).should eq 0.0\n        0.5.round(:to_negative).should eq 0.0\n        0.9.round(:to_negative).should eq 0.0\n        1.0.round(:to_negative).should eq 1.0\n        1.5.round(:to_negative).should eq 1.0\n      end\n\n      it \"ties_even\" do\n        -2.5.round(:ties_even).should eq -2.0\n        -1.5.round(:ties_even).should eq -2.0\n        -1.0.round(:ties_even).should eq -1.0\n        -0.9.round(:ties_even).should eq -1.0\n        -0.5.round(:ties_even).should eq 0.0\n        -0.1.round(:ties_even).should eq 0.0\n        0.0.round(:ties_even).should eq 0.0\n        0.1.round(:ties_even).should eq 0.0\n        0.5.round(:ties_even).should eq 0.0\n        0.9.round(:ties_even).should eq 1.0\n        1.0.round(:ties_even).should eq 1.0\n        1.5.round(:ties_even).should eq 2.0\n        2.5.round(:ties_even).should eq 2.0\n      end\n\n      it \"ties_away\" do\n        -2.5.round(:ties_away).should eq -3.0\n        -1.5.round(:ties_away).should eq -2.0\n        -1.0.round(:ties_away).should eq -1.0\n        -0.9.round(:ties_away).should eq -1.0\n        -0.5.round(:ties_away).should eq -1.0\n        -0.1.round(:ties_away).should eq 0.0\n        0.0.round(:ties_away).should eq 0.0\n        0.1.round(:ties_away).should eq 0.0\n        0.5.round(:ties_away).should eq 1.0\n        0.9.round(:ties_away).should eq 1.0\n        1.0.round(:ties_away).should eq 1.0\n        1.5.round(:ties_away).should eq 2.0\n        2.5.round(:ties_away).should eq 3.0\n      end\n\n      it \"default (=ties_even)\" do\n        -2.5.round.should eq -2.0\n        -1.5.round.should eq -2.0\n        -1.0.round.should eq -1.0\n        -0.9.round.should eq -1.0\n        -0.5.round.should eq 0.0\n        -0.1.round.should eq 0.0\n        0.0.round.should eq 0.0\n        0.1.round.should eq 0.0\n        0.5.round.should eq 0.0\n        0.9.round.should eq 1.0\n        1.0.round.should eq 1.0\n        1.5.round.should eq 2.0\n        2.5.round.should eq 2.0\n      end\n    end\n\n    describe \"with digits\" do\n      it \"to_zero\" do\n        12.345.round(-1, mode: :to_zero).should eq 10\n        12.345.round(0, mode: :to_zero).should eq 12\n        12.345.round(1, mode: :to_zero).should eq 12.3\n        12.345.round(2, mode: :to_zero).should eq 12.34\n        -12.345.round(-1, mode: :to_zero).should eq -10\n        -12.345.round(0, mode: :to_zero).should eq -12\n        -12.345.round(1, mode: :to_zero).should eq -12.3\n        -12.345.round(2, mode: :to_zero).should eq -12.34\n      end\n\n      it \"to_positive\" do\n        12.345.round(-1, mode: :to_positive).should eq 20\n        12.345.round(0, mode: :to_positive).should eq 13\n        12.345.round(1, mode: :to_positive).should eq 12.4\n        12.345.round(2, mode: :to_positive).should eq 12.35\n        -12.345.round(-1, mode: :to_positive).should eq -10\n        -12.345.round(0, mode: :to_positive).should eq -12\n        -12.345.round(1, mode: :to_positive).should eq -12.3\n        -12.345.round(2, mode: :to_positive).should eq -12.34\n      end\n\n      it \"to_negative\" do\n        12.345.round(-1, mode: :to_negative).should eq 10\n        12.345.round(0, mode: :to_negative).should eq 12\n        12.345.round(1, mode: :to_negative).should eq 12.3\n        12.345.round(2, mode: :to_negative).should eq 12.34\n        -12.345.round(-1, mode: :to_negative).should eq -20\n        -12.345.round(0, mode: :to_negative).should eq -13\n        -12.345.round(1, mode: :to_negative).should eq -12.4\n        -12.345.round(2, mode: :to_negative).should eq -12.35\n      end\n\n      it \"ties_away\" do\n        13.825.round(-1, mode: :ties_away).should eq 10\n        13.825.round(0, mode: :ties_away).should eq 14\n        13.825.round(1, mode: :ties_away).should eq 13.8\n        13.825.round(2, mode: :ties_away).should eq 13.83\n        -13.825.round(-1, mode: :ties_away).should eq -10\n        -13.825.round(0, mode: :ties_away).should eq -14\n        -13.825.round(1, mode: :ties_away).should eq -13.8\n        -13.825.round(2, mode: :ties_away).should eq -13.83\n      end\n\n      it \"ties_even\" do\n        15.255.round(-1, mode: :ties_even).should eq 20\n        15.255.round(0, mode: :ties_even).should eq 15\n        15.255.round(1, mode: :ties_even).should eq 15.3\n        15.255.round(2, mode: :ties_even).should eq 15.26\n        -15.255.round(-1, mode: :ties_even).should eq -20\n        -15.255.round(0, mode: :ties_even).should eq -15\n        -15.255.round(1, mode: :ties_even).should eq -15.3\n        -15.255.round(2, mode: :ties_even).should eq -15.26\n      end\n    end\n\n    describe \"base\" do\n      it \"2\" do\n        -1763.116.round(2, base: 2).should eq(-1763.0)\n        753.155.round(2, base: 2).should eq(753.25)\n        15.159.round(2, base: 2).should eq(15.25)\n        753.155.round(-5, base: 2).should eq(768)\n      end\n\n      it \"8\" do\n        -1763.116.round(2, base: 8).should eq(-1763.109375)\n        753.155.round(1, base: 8).should eq(753.125)\n        15.159.round(0, base: 8).should eq(15.0)\n      end\n    end\n  end\n\n  describe \"#round_even\" do\n    it { -2.5.round_even.should eq -2.0 }\n    it { -1.5.round_even.should eq -2.0 }\n    it { -1.0.round_even.should eq -1.0 }\n    it { -0.9.round_even.should eq -1.0 }\n    it { -0.5.round_even.should eq -0.0 }\n    it { -0.1.round_even.should eq 0.0 }\n    it { 0.0.round_even.should eq 0.0 }\n    it { 0.1.round_even.should eq 0.0 }\n    it { 0.5.round_even.should eq 0.0 }\n    it { 0.9.round_even.should eq 1.0 }\n    it { 1.0.round_even.should eq 1.0 }\n    it { 1.5.round_even.should eq 2.0 }\n    it { 2.5.round_even.should eq 2.0 }\n\n    it { 1.round_even.should eq 1 }\n    it { 1.round_even.should be_a(Int32) }\n    it { 1_u8.round_even.should be_a(UInt8) }\n    it { 1_f32.round_even.should be_a(Float32) }\n  end\n\n  describe \"#round_away\" do\n    it { -2.5.round_away.should eq -3.0 }\n    it { -1.5.round_away.should eq -2.0 }\n    it { -1.0.round_away.should eq -1.0 }\n    it { -0.9.round_away.should eq -1.0 }\n    it { -0.5.round_away.should eq -1.0 }\n    it { -0.1.round_away.should eq 0.0 }\n    it { 0.0.round_away.should eq 0.0 }\n    it { 0.1.round_away.should eq 0.0 }\n    it { 0.5.round_away.should eq 1.0 }\n    it { 0.9.round_away.should eq 1.0 }\n    it { 1.0.round_away.should eq 1.0 }\n    it { 1.5.round_away.should eq 2.0 }\n    it { 2.5.round_away.should eq 3.0 }\n\n    it { 1.round_away.should eq 1 }\n    it { 1.round_away.should be_a(Int32) }\n    it { 1_u8.round_away.should be_a(UInt8) }\n    it { 1_f32.round_away.should be_a(Float32) }\n  end\n\n  it \"gives the absolute value\" do\n    123.abs.should eq(123)\n    -123.abs.should eq(123)\n  end\n\n  it \"gives the square of a value\" do\n    2.abs2.should eq(4)\n    -2.abs2.should eq(4)\n    2.5.abs2.should eq(6.25)\n    -2.5.abs2.should eq(6.25)\n  end\n\n  it \"gives the sign\" do\n    123.sign.should eq(1)\n    -123.sign.should eq(-1)\n    0.sign.should eq(0)\n  end\n\n  it \"divides and calculates the modulo\" do\n    11.divmod(3).should eq({3, 2})\n    11.divmod(-3).should eq({-4, -1})\n\n    10.divmod(2).should eq({5, 0})\n    11.divmod(2).should eq({5, 1})\n\n    10.divmod(-2).should eq({-5, 0})\n    11.divmod(-2).should eq({-6, -1})\n\n    -10.divmod(2).should eq({-5, 0})\n    -11.divmod(2).should eq({-6, 1})\n\n    -10.divmod(-2).should eq({5, 0})\n    -11.divmod(-2).should eq({5, -1})\n  end\n\n  it \"compare the numbers\" do\n    10.<=>(10).should eq(0)\n    10.<=>(11).should eq(-1)\n    11.<=>(10).should eq(1)\n  end\n\n  it \"creates an array with [] and some elements\" do\n    ary = Int64[1, 2, 3]\n    ary.should eq([1, 2, 3])\n    ary[0].should be_a(Int64)\n  end\n\n  it \"creates an array with [] and no elements\" do\n    ary = Int64[]\n    ary.should eq([] of Int64)\n    ary << 1_i64\n    ary.should eq([1])\n  end\n\n  it \"creates a slice\" do\n    slice = Int8.slice(1, 2, 300)\n    slice.should be_a(Slice(Int8))\n    slice.size.should eq(3)\n    slice[0].should eq(1)\n    slice[1].should eq(2)\n    slice[2].should eq(300.to_u8!)\n  end\n\n  it \"creates a static array\" do\n    ary = Int8.static_array(1, 2, 300)\n    ary.should be_a(StaticArray(Int8, 3))\n    ary.size.should eq(3)\n    ary[0].should eq(1)\n    ary[1].should eq(2)\n    ary[2].should eq(300.to_u8!)\n  end\n\n  it \"#zero?\" do\n    0.zero?.should be_true\n    0.0.zero?.should be_true\n    0_f32.zero?.should be_true\n    1.zero?.should be_false\n    1.0.zero?.should be_false\n    1f32.zero?.should be_false\n  end\n\n  it \"#positive?\" do\n    1.positive?.should be_true\n    1.0.positive?.should be_true\n    0.positive?.should be_false\n    0.0.positive?.should be_false\n    -1.positive?.should be_false\n    -1.1.positive?.should be_false\n  end\n\n  it \"#negative?\" do\n    1.negative?.should be_false\n    1.0.negative?.should be_false\n    0.negative?.should be_false\n    0.0.negative?.should be_false\n    -1.negative?.should be_true\n    -1.1.negative?.should be_true\n  end\n\n  describe \"#step\" do\n    it_iterates \"basic Int\", [1, 2, 3, 4, 5], 1.step(to: 5)\n    it_iterates \"basic Float\", [1.0, 2.0, 3.0, 4.0, 5.0], 1.0.step(to: 5.0)\n\n    it_iterates \"single value Int\", [1], 1.step(to: 1)\n    it_iterates \"single value Float\", [1.0], 1.0.step(to: 1.0)\n\n    it_iterates \"single value by Int\", [1], 1.step(to: 1, by: 2)\n    it_iterates \"single value by Float\", [1.0], 1.0.step(to: 1.0, by: 2.0)\n    it_iterates \"single value Int by Float\", [1.0], 1.step(to: 1, by: 2.0)\n    it_iterates \"single value Float by Int\", [1.0], 1.0.step(to: 1.0, by: 2)\n\n    it_iterates \"negative Int\", [-1, -2, -3, -4, -5], -1.step(to: -5)\n    it_iterates \"negative Float\", [-1.0, -2.0, -3.0, -4.0, -5.0], -1.0.step(to: -5.0)\n\n    it_iterates \"downto Int\", [3, 2, 1, 0], 3.step(to: 0)\n    it_iterates \"downto Int by\", [3, 2, 1, 0], 3.step(to: 0, by: -1)\n    it_iterates \"downto UInt\", [3, 2, 1, 0] of UInt8, 3u8.step(to: 0)\n    it_iterates \"downto UInt by\", [3, 2, 1, 0] of UInt8, 3u8.step(to: 0, by: -1)\n    it_iterates \"downto Float\", [3.0, 2.0, 1.0, 0.0], 3.0.step(to: 0)\n    it_iterates \"downto Float by\", [3.0, 2.0, 1.0, 0.0], 3.0.step(to: 0, by: -1)\n\n    it_iterates \"by Int\", [1, 3, 5], 1.step(to: 5, by: 2)\n    it_iterates \"by Float\", [1.0, 3.0, 5.0], 1.0.step(to: 5.0, by: 2.0)\n    it_iterates \"by Float half\", [1.0, 2.5, 4.0], 1.0.step(to: 5.0, by: 1.5)\n\n    it_iterates \"negative by Int\", [-1, -3, -5], -1.step(to: -5, by: -2)\n    it_iterates \"negative by Float\", [-1.0, -3.0, -5.0], -1.0.step(to: -5.0, by: -2.0)\n    it_iterates \"negative by Float half\", [-1.0, -2.5, -4.0], -1.0.step(to: -5.0, by: -1.5)\n\n    it_iterates \"missing end Int\", [1, 3], 1.step(to: 4, by: 2)\n    it_iterates \"missing end Float\", [1.0, 3.0], 1.0.step(to: 4.0, by: 2.0)\n    it_iterates \"missing end UInt\", [3, 1] of UInt8, 3u8.step(to: 0, by: -2)\n\n    it_iterates \"Int to Float\", [1, 2, 3, 4, 5], 1.step(to: 5.0)\n    it_iterates \"Int to Float by\", [1, 2, 3, 4, 5], 1.step(to: 5.0, by: 1)\n    it_iterates \"Float to Int\", [1.0, 2.0, 3.0, 4.0, 5.0], 1.0.step(to: 5)\n    it_iterates \"Float to Int by\", [1.0, 2.0, 3.0, 4.0, 5.0], 1.0.step(to: 5, by: 1)\n\n    it_iterates \"Int by Float\", [1.0, 3.0, 5.0], 1.step(to: 5, by: 2.0)\n    it_iterates \"Float by Int\", [1.0, 3.0, 5.0], 1.0.step(to: 5.0, by: 2)\n\n    it_iterates \"over zero Int\", [-1, 0, 1], -1.step(to: 1)\n    it_iterates \"over zero Float\", [-1.0, 0.0, 1.0], -1.0.step(to: 1.0)\n\n    it_iterates \"at max Int\", [Int8::MAX - 2, Int8::MAX - 1, Int8::MAX], (Int8::MAX - 2).step(to: Int8::MAX)\n    it_iterates \"over max Int\", [Int8::MAX - 1], (Int8::MAX - 1).step(to: Int8::MAX, by: 2)\n\n    it_iterates \"at min Int\", [Int8::MIN + 2, Int8::MIN + 1, Int8::MIN], (Int8::MIN + 2).step(to: Int8::MIN)\n    it_iterates \"over min Int\", [Int8::MIN + 1], (Int8::MIN + 1).step(to: Int8::MIN, by: -2)\n\n    it \"by zero yielding\" do\n      yielded = false\n      expect_raises(ArgumentError, \"Zero step size\") do\n        0.step(to: 1, by: 0) { yielded = true }\n      end\n      yielded.should be_false\n    end\n\n    it \"by zero iterator\" do\n      expect_raises(ArgumentError, \"Zero step size\") do\n        0.step(to: 1, by: 0)\n      end\n    end\n\n    it_iterates \"empty if `by` and `to` are opposed\", [] of Int32, 1.step(to: 2, by: -1)\n\n    it_iterates \"empty if `to` can't be compared\", [] of Float64, 1.0.step(to: Float64::NAN)\n    it_iterates \"empty if `to` can't be compared by\", [] of Float64, 1.0.step(to: Float64::NAN, by: 1.0)\n    it_iterates \"empty if `self` can't be compared\", [] of Float64, Float64::NAN.step(to: 1.0)\n    it_iterates \"empty if `self` can't be compared by\", [] of Float64, Float64::NAN.step(to: 1.0, by: 1.0)\n\n    describe \"exclusive\" do\n      it_iterates \"basic Int\", [1, 2, 3, 4], 1.step(to: 5, exclusive: true)\n      it_iterates \"basic Float\", [1.0, 2.0, 3.0, 4.0], 1.0.step(to: 5.0, exclusive: true)\n\n      it_iterates \"single value Int\", [] of Int32, 1.step(to: 1, exclusive: true)\n      it_iterates \"single value Float\", [] of Float64, 1.0.step(to: 1.0, exclusive: true)\n    end\n\n    describe \"without limit\" do\n      describe \"iterator\" do\n        it \"basic\" do\n          iter = 0.step\n\n          5.times do\n            iter.next\n          end\n\n          iter.next.should eq(5)\n        end\n\n        it \"raises overflow error\" do\n          iter = (Int8::MAX - 1).step\n          iter.next.should eq Int8::MAX - 1\n          iter.next.should eq Int8::MAX\n          expect_raises(OverflowError) do\n            iter.next\n          end\n        end\n      end\n\n      describe \"yielding\" do\n        it \"basic\" do\n          i = 1\n          1.step do |x|\n            x.should eq(i)\n            break if x >= 10\n            i += 1\n          end\n          i.should eq 10\n        end\n\n        it \"raises overflow error\" do\n          ary = [] of Int8\n          expect_raises(OverflowError) do\n            (Int8::MAX - 1).step do |x|\n              ary << x\n            end\n          end\n          ary.should eq [Int8::MAX - 1, Int8::MAX]\n        end\n      end\n    end\n\n    describe \"whole range\" do\n      it { (UInt8::MIN..UInt8::MAX).each.count { true }.should eq(256) }\n      it_iterates \"UInt8 upwards\", (UInt8::MIN.to_i..UInt8::MAX.to_i).map(&.to_u8), (UInt8::MIN..UInt8::MAX).step(by: 1)\n      it_iterates \"UInt8 downwards\", (UInt8::MIN.to_i..UInt8::MAX.to_i).map(&.to_u8).reverse!, (UInt8::MAX..UInt8::MIN).step(by: -1)\n\n      it { (Int8::MIN..Int8::MAX).each.count { true }.should eq(256) }\n      it_iterates \"Int8 upwards\", (Int8::MIN.to_i..Int8::MAX.to_i).map(&.to_i8), (Int8::MIN..Int8::MAX).step(by: 1)\n      it_iterates \"Int8 downwards\", (Int8::MIN.to_i..Int8::MAX.to_i).map(&.to_i8).reverse!, (Int8::MAX..Int8::MIN).step(by: -1)\n\n      it { (Int16::MIN..Int16::MAX).each.count { true }.should eq(65536) }\n      it_iterates \"Int16 upwards\", (Int16::MIN.to_i..Int16::MAX.to_i).map(&.to_i16), (Int16::MIN..Int16::MAX).step(by: 1)\n      it_iterates \"Int16 downwards\", (Int16::MIN.to_i..Int16::MAX.to_i).map(&.to_i16).reverse!, (Int16::MAX..Int16::MIN).step(by: -1)\n    end\n\n    it_iterates \"towards limit [max-4, max-2, max]\", [Int32::MAX - 4, Int32::MAX - 2, Int32::MAX], (Int32::MAX - 4).step(to: Int32::MAX, by: 2)\n    it_iterates \"towards limit [max-4, max-2, max)\", [Int32::MAX - 4, Int32::MAX - 2], (Int32::MAX - 4).step(to: Int32::MAX, by: 2, exclusive: true)\n    it_iterates \"towards limit [max-3, max-1, max)\", [Int32::MAX - 3, Int32::MAX - 1], (Int32::MAX - 3).step(to: Int32::MAX, by: 2)\n  end\n\n  floor_division_returns_lhs_type {{BUILTIN_NUMBER_TYPES}}, {{BUILTIN_NUMBER_TYPES}}\n\n  division_between_returns {{BUILTIN_INTEGER_TYPES}}, {{BUILTIN_INTEGER_TYPES}}, Float64\n  division_between_returns {{BUILTIN_INTEGER_TYPES}}, [Float32], Float32\n  division_between_returns [Float32], {{BUILTIN_INTEGER_TYPES}}, Float32\n  division_between_returns {{BUILTIN_INTEGER_TYPES}}, [Float64], Float64\n  division_between_returns [Float64], {{BUILTIN_INTEGER_TYPES}}, Float64\n\n  division_between_returns [Float32], [Float32], Float32\n  division_between_returns {{BUILTIN_FLOAT_TYPES}}, [Float64], Float64\n  division_between_returns [Float64], {{BUILTIN_FLOAT_TYPES}}, Float64\nend\n"
  },
  {
    "path": "spec/std/oauth/access_token_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth\"\n\ndescribe OAuth::AccessToken do\n  it \"creates from response body\" do\n    access_token = OAuth::AccessToken.from_response(\"oauth_token=1234-nyi1G37179bVdYNZGZqKQEdO&oauth_token_secret=f7T6ibH25q4qkVTAUN&user_id=1234&screen_name=someuser\")\n    access_token.token.should eq(\"1234-nyi1G37179bVdYNZGZqKQEdO\")\n    access_token.secret.should eq(\"f7T6ibH25q4qkVTAUN\")\n    access_token.extra[\"user_id\"].should eq(\"1234\")\n    access_token.extra[\"screen_name\"].should eq(\"someuser\")\n  end\nend\n"
  },
  {
    "path": "spec/std/oauth/authorization_header_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth\"\n\ndescribe OAuth::AuthorizationHeader do\n  it \"builds\" do\n    params = OAuth::AuthorizationHeader.new\n    params.add \"foo\", \"value1\"\n    params.add \"bar\", \"a+b\"\n    params.add \"baz\", \"=/=\"\n    params.to_s.should eq(%(OAuth foo=\"value1\", bar=\"a%2Bb\", baz=\"%3D%2F%3D\"))\n  end\nend\n"
  },
  {
    "path": "spec/std/oauth/consumer_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth\"\n\ndescribe OAuth::Consumer do\n  describe \"gets authorize uri\" do\n    it \"without callback url\" do\n      consumer = OAuth::Consumer.new \"example.com\", \"consumer_key\", \"consumer_secret\"\n      request_token = OAuth::RequestToken.new \"request_token\", \"request_secret\"\n      uri = consumer.get_authorize_uri request_token\n      uri.should eq(\"https://example.com/oauth/authorize?oauth_token=request_token\")\n    end\n\n    it \"with callback url\" do\n      consumer = OAuth::Consumer.new \"example.com\", \"consumer_key\", \"consumer_secret\"\n      request_token = OAuth::RequestToken.new \"request_token\", \"request_secret\"\n      uri = consumer.get_authorize_uri request_token, oauth_callback: \"some_callback\"\n      uri.should eq(\"https://example.com/oauth/authorize?oauth_token=request_token&oauth_callback=some_callback\")\n    end\n\n    it \"without custom authorize uri\" do\n      consumer = OAuth::Consumer.new \"example.com\", \"consumer_key\", \"consumer_secret\", authorize_uri: \"/foo\"\n      request_token = OAuth::RequestToken.new \"request_token\", \"request_secret\"\n      uri = consumer.get_authorize_uri request_token\n      uri.should eq(\"https://example.com/foo?oauth_token=request_token\")\n    end\n\n    it \"without block\" do\n      consumer = OAuth::Consumer.new \"example.com\", \"consumer_key\", \"consumer_secret\"\n      request_token = OAuth::RequestToken.new \"request_token\", \"request_secret\"\n      uri = consumer.get_authorize_uri(request_token) do |form|\n        form.add \"baz\", \"qux\"\n      end\n      uri.should eq(\"https://example.com/oauth/authorize?oauth_token=request_token&baz=qux\")\n    end\n\n    it \"with absolute uri\" do\n      consumer = OAuth::Consumer.new \"example.com\", \"consumer_key\", \"consumer_secret\",\n        authorize_uri: \"https://example2.com:1234/foo?bar=baz\"\n      request_token = OAuth::RequestToken.new \"request_token\", \"request_secret\"\n      uri = consumer.get_authorize_uri request_token\n      uri.should eq(\"https://example2.com:1234/foo?oauth_token=request_token&bar=baz\")\n    end\n  end\n\n  typeof(begin\n    consumer = OAuth::Consumer.new \"example.com\", \"consumer_key\", \"consumer_secret\", authorize_uri: \"/foo\"\n    consumer.get_request_token(oauth_callback: \"foo.bar.baz\")\n\n    request_token = OAuth::RequestToken.new \"request_token\", \"request_secret\"\n    consumer.get_access_token(request_token, \"oauth_verifier\")\n    consumer.get_access_token(request_token, \"oauth_verifier\", {\"a\" => \"b\"})\n\n    access_token = OAuth::AccessToken.new \"token\", \"secret\"\n\n    http_client = HTTP::Client.new \"example.com\"\n    consumer.authenticate http_client, access_token\n  end)\nend\n"
  },
  {
    "path": "spec/std/oauth/request_token_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth\"\n\ndescribe OAuth::RequestToken do\n  describe \"creating from response\" do\n    it \"creates from a valid response\" do\n      token = OAuth::RequestToken.from_response(\"oauth_token_secret=p58A6bzyGaT8PR54gM0S4ZesOVC2ManiTmwHcho8&oauth_callback_confirmed=true&oauth_token=qyprd6Pe2PbnSxUcyHcWz0VnTF8bg1rxsBbUwOpkQ6bSQEyK\")\n      token.secret.should eq(\"p58A6bzyGaT8PR54gM0S4ZesOVC2ManiTmwHcho8\")\n      token.token.should eq(\"qyprd6Pe2PbnSxUcyHcWz0VnTF8bg1rxsBbUwOpkQ6bSQEyK\")\n    end\n\n    it \"raises an error when the token is missing\" do\n      expect_raises OAuth::Error, \"Missing token\" do\n        OAuth::RequestToken.from_response(\"oauth_token_secret=foo\")\n      end\n    end\n\n    it \"raises an error when the secret is missing\" do\n      expect_raises OAuth::Error, \"Missing secret\" do\n        OAuth::RequestToken.from_response(\"oauth_token=foo\")\n      end\n    end\n\n    it \"raises an error when the token AND secret are missing\" do\n      expect_raises OAuth::Error, \"Missing token and secret\" do\n        OAuth::RequestToken.from_response(\"error=oops\")\n      end\n    end\n  end\n\n  describe \"equality\" do\n    it \"checks token\" do\n      foo1 = OAuth::RequestToken.new(\"foo\", \"secret\")\n      foo2 = OAuth::RequestToken.new(\"foo\", \"secret\")\n      bar1 = OAuth::RequestToken.new(\"bar\", \"secret\")\n      bar2 = OAuth::RequestToken.new(\"bar\", \"secret\")\n\n      foo1.should eq(foo2)\n      foo1.should_not eq(bar2)\n      bar1.should_not eq(foo2)\n      bar1.should eq(bar2)\n\n      foo1.hash.should eq(foo2.hash)\n      foo1.hash.should_not eq(bar2.hash)\n      bar1.hash.should_not eq(foo2.hash)\n      bar1.hash.should eq(bar2.hash)\n    end\n\n    it \"checks secret\" do\n      foo1 = OAuth::RequestToken.new(\"token\", \"foo\")\n      foo2 = OAuth::RequestToken.new(\"token\", \"foo\")\n      bar1 = OAuth::RequestToken.new(\"token\", \"bar\")\n      bar2 = OAuth::RequestToken.new(\"token\", \"bar\")\n\n      foo1.should eq(foo2)\n      foo1.should_not eq(bar2)\n      bar1.should_not eq(foo2)\n      bar1.should eq(bar2)\n\n      foo1.hash.should eq(foo2.hash)\n      foo1.hash.should_not eq(bar2.hash)\n      bar1.hash.should_not eq(foo2.hash)\n      bar1.hash.should eq(bar2.hash)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/oauth/signature_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth\"\n\ndescribe OAuth::Signature do\n  describe \"key\" do\n    it \"gets when token secret is empty\" do\n      signature = OAuth::Signature.new \"consumer_key\", \"consumer secret\"\n      signature.key.should eq(\"consumer%20secret&\")\n    end\n\n    it \"gets when token secret is not empty\" do\n      signature = OAuth::Signature.new \"consumer_key\", \"consumer secret\", token_shared_secret: \"token secret\"\n      signature.key.should eq(\"consumer%20secret&token%20secret\")\n    end\n  end\n\n  describe \"base string\" do\n    it \"computes without port in host\" do\n      request = HTTP::Request.new \"POST\", \"/some/path\"\n      request.headers[\"Host\"] = \"some.host\"\n      tls = false\n      ts = \"1234\"\n\n      signature = OAuth::Signature.new \"consumer_key\", \"consumer secret\", extra_params: {\n        \"oauth_callback\" => \"some+callback\",\n      }\n      base_string = signature.base_string request, tls, ts, \"nonce\"\n      base_string.should eq(\"POST&http%3A%2F%2Fsome.host%2Fsome%2Fpath&oauth_callback%3Dsome%252Bcallback%26oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3Dnonce%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1234%26oauth_version%3D1.0\")\n    end\n\n    it \"computes with port in host\" do\n      request = HTTP::Request.new \"POST\", \"/some/path\"\n      request.headers[\"Host\"] = \"some.host:5678\"\n      tls = false\n      ts = \"1234\"\n\n      signature = OAuth::Signature.new \"consumer_key\", \"consumer secret\", extra_params: {\n        \"oauth_callback\" => \"some+callback\",\n      }\n      base_string = signature.base_string request, tls, ts, \"nonce\"\n      base_string.should eq(\"POST&http%3A%2F%2Fsome.host%3A5678%2Fsome%2Fpath&oauth_callback%3Dsome%252Bcallback%26oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3Dnonce%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1234%26oauth_version%3D1.0\")\n    end\n\n    it \"computes when TLS\" do\n      request = HTTP::Request.new \"POST\", \"/some/path\"\n      request.headers[\"Host\"] = \"some.host\"\n      tls = true\n      ts = \"1234\"\n\n      signature = OAuth::Signature.new \"consumer_key\", \"consumer secret\", extra_params: {\n        \"oauth_callback\" => \"some+callback\",\n      }\n      base_string = signature.base_string request, tls, ts, \"nonce\"\n      base_string.should eq(\"POST&https%3A%2F%2Fsome.host%2Fsome%2Fpath&oauth_callback%3Dsome%252Bcallback%26oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3Dnonce%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1234%26oauth_version%3D1.0\")\n    end\n  end\n\n  # https://dev.twitter.com/oauth/overview/creating-signatures\n  it \"does twitter sample\" do\n    request = HTTP::Request.new \"POST\", \"/1/statuses/update.json?include_entities=true\", body: \"status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21\"\n    request.headers[\"Host\"] = \"api.twitter.com\"\n    request.headers[\"Content-type\"] = \"application/x-www-form-urlencoded\"\n    tls = true\n    ts = \"1318622958\"\n    nonce = \"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\"\n    consumer_key = \"xvz1evFS4wEEPTGEFPHBog\"\n    consumer_secret = \"kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw\"\n    oauth_token = \"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\"\n    oauth_token_secret = \"LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE\"\n\n    signature = OAuth::Signature.new consumer_key, consumer_secret, oauth_token: oauth_token, token_shared_secret: oauth_token_secret\n    base_string = signature.base_string(request, tls, ts, nonce)\n    expected_base_string = \"POST&https%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521\"\n\n    base_string.should eq(expected_base_string)\n\n    computed = signature.compute request, tls, ts, nonce\n    expected_computed = \"tnnArxj06cWHq44gCs1OSKk/jLY=\"\n\n    computed.should eq(expected_computed)\n\n    header = signature.authorization_header request, tls, ts, nonce\n    expected_header = %(OAuth oauth_consumer_key=\"xvz1evFS4wEEPTGEFPHBog\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1318622958\", oauth_nonce=\"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\", oauth_signature=\"tnnArxj06cWHq44gCs1OSKk%2FjLY%3D\", oauth_token=\"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\", oauth_version=\"1.0\")\n\n    header.should eq(expected_header)\n  end\nend\n"
  },
  {
    "path": "spec/std/oauth2/access_token_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth2\"\n\nclass OAuth2::AccessToken\n  describe Bearer do\n    it \"builds from json\" do\n      token_value = \"some token value\"\n      token_type = \"Bearer\"\n      expires_in = 3600\n      refresh_token = \"some refresh token\"\n      scope = \"some scope\"\n      json = %({\n        \"access_token\" : \"#{token_value}\",\n        \"token_type\" : \"#{token_type}\",\n        \"expires_in\" : #{expires_in},\n        \"refresh_token\" : \"#{refresh_token}\",\n        \"scope\" : \"#{scope}\"\n        })\n\n      access_token = AccessToken.from_json(json)\n      access_token = access_token.as(Bearer)\n      access_token.token_type.should eq(\"Bearer\")\n      access_token.access_token.should eq(token_value)\n      access_token.expires_in.should eq(expires_in)\n      access_token.refresh_token.should eq(refresh_token)\n      access_token.scope.should eq(scope)\n\n      access_token = AccessToken::Bearer.from_json(json)\n      access_token = access_token.as(Bearer)\n      access_token.token_type.should eq(\"Bearer\")\n      access_token.access_token.should eq(token_value)\n      access_token.expires_in.should eq(expires_in)\n      access_token.refresh_token.should eq(refresh_token)\n      access_token.scope.should eq(scope)\n    end\n\n    it \"dumps to json\" do\n      token = Bearer.new(\"access token\", 3600, \"refresh token\")\n      token2 = AccessToken.from_json(token.to_json)\n      token2.should eq(token)\n    end\n\n    it \"authenticates request\" do\n      token = Bearer.new(\"access token\", 3600, \"refresh token\")\n      request = HTTP::Request.new \"GET\", \"/\"\n      token.authenticate request, false\n      request.headers[\"Authorization\"].should eq(\"Bearer access token\")\n    end\n\n    it \"builds from json without expires_in (#4041)\" do\n      access_token = AccessToken.from_json(%({\n        \"access_token\" : \"foo\",\n        \"token_type\" : \"Bearer\",\n        \"refresh_token\" : \"bar\",\n        \"scope\" : \"baz\"\n        }))\n      access_token.expires_in.should be_nil\n    end\n\n    it \"builds from json with unknown key (#4437)\" do\n      token = AccessToken.from_json(%({\n        \"access_token\" : \"foo\",\n        \"token_type\" : \"Bearer\",\n        \"refresh_token\" : \"bar\",\n        \"scope\" : \"baz\",\n        \"unknown\": [1, 2, 3]\n        }))\n      token.extra.not_nil![\"unknown\"].should eq(\"[1,2,3]\")\n    end\n\n    it \"builds from json without token_type, assumes Bearer (#4503)\" do\n      token = AccessToken.from_json(%({\n        \"access_token\" : \"foo\",\n        \"refresh_token\" : \"bar\",\n        \"scope\" : \"baz\"\n        }))\n      token.should be_a(AccessToken::Bearer)\n      token.access_token.should eq(\"foo\")\n    end\n  end\n\n  describe Mac do\n    it \"builds from json\" do\n      mac_algorithm = \"hmac-sha-256\"\n      expires_in = 3600\n      mac_key = \"secret key\"\n      refresh_token = \"some refresh token\"\n      token_value = \"some token value\"\n      scope = \"some scope\"\n      json = %({\n          \"token_type\": \"mac\",\n          \"mac_algorithm\": \"#{mac_algorithm}\",\n          \"expires_in\": #{expires_in},\n          \"mac_key\": \"#{mac_key}\",\n          \"refresh_token\":\"#{refresh_token}\",\n          \"access_token\":\"#{token_value}\",\n          \"scope\":\"#{scope}\"\n        })\n\n      access_token = AccessToken.from_json(json)\n      access_token = access_token.as(Mac)\n      access_token.token_type.should eq(\"Mac\")\n      access_token.access_token.should eq(token_value)\n      access_token.expires_in.should eq(expires_in)\n      access_token.refresh_token.should eq(refresh_token)\n      access_token.scope.should eq(scope)\n      access_token.mac_algorithm.should eq(mac_algorithm)\n      access_token.mac_key.should eq(mac_key)\n\n      access_token = AccessToken::Mac.from_json(json)\n      access_token = access_token.as(Mac)\n      access_token.token_type.should eq(\"Mac\")\n      access_token.access_token.should eq(token_value)\n      access_token.expires_in.should eq(expires_in)\n      access_token.refresh_token.should eq(refresh_token)\n      access_token.scope.should eq(scope)\n      access_token.mac_algorithm.should eq(mac_algorithm)\n      access_token.mac_key.should eq(mac_key)\n    end\n\n    it \"builds with null refresh token\" do\n      json = %({\n        \"token_type\": \"Mac\",\n        \"access_token\":\"WRN01OBN1gme8HxeRL5yJ8w05PjCvt-2vXOIle43w9s\",\n        \"expires_in\":899,\n        \"refresh_token\":null,\n        \"mac_algorithm\":\"hmac-sha-256\",\n        \"mac_key\":\"N-ATggO2ywqylWgIi3QZn40jWJmL2f9h6ZOGd3jqcxU\"\n        })\n      access_token = AccessToken.from_json(json)\n      access_token = access_token.as(Mac)\n      access_token.refresh_token.should be_nil\n    end\n\n    it \"dumps to json\" do\n      token = Mac.new(\"access token\", 3600, \"mac algorithm\", \"mac key\", \"refresh token\", \"scope\")\n      token2 = AccessToken.from_json(token.to_json)\n      token2.should eq(token)\n    end\n\n    it \"authenticates request\" do\n      headers = HTTP::Headers.new\n      headers[\"Host\"] = \"localhost:4000\"\n\n      token = Mac.new(\"3n2-YaAzH67YH9UJ-9CnJ_PS-vSy1MRLM-q7TZknPw\", 3600, \"hmac-sha-256\", \"i-pt1Lir-yAfUdXbt-AXM1gMupK7vDiOK1SZGWkASDc\")\n      request = HTTP::Request.new \"GET\", \"/some/resource.json\", headers\n      token.authenticate request, false\n      auth = request.headers[\"Authorization\"]\n      auth.should match /MAC id=\".+?\", nonce=\".+?\", ts=\".+?\", mac=\".+?\"/\n    end\n\n    it \"computes signature\" do\n      mac = Mac.signature 1, \"0:1234\", \"GET\", \"/resource.json\", \"localhost\", \"4000\", \"\", \"hmac-sha-256\", \"i-pt1Lir-yAfUdXbt-AXM1gMupK7vDiOK1SZGWkASDc\"\n      mac.should eq(\"21vVRFACz5NrO+zlVfFuxTjTx5Wb0qBMfKelMTtujpE=\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/oauth2/client_spec.cr",
    "content": "require \"spec\"\nrequire \"oauth2\"\nrequire \"http/server\"\nrequire \"../http/spec_helper\"\n\ndescribe OAuth2::Client do\n  describe \"authorization uri\" do\n    it \"gets with default endpoint\" do\n      client = OAuth2::Client.new \"localhost\", \"client_id\", \"client_secret\", redirect_uri: \"uri\"\n      uri = client.get_authorize_uri(scope: \"foo bar\")\n      uri.should eq(\"https://localhost/oauth2/authorize?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar\")\n    end\n\n    it \"gets with custom endpoint\" do\n      client = OAuth2::Client.new \"localhost\", \"client_id\", \"client_secret\", redirect_uri: \"uri\", authorize_uri: \"/baz\"\n      uri = client.get_authorize_uri(scope: \"foo bar\")\n      uri.should eq(\"https://localhost/baz?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar\")\n    end\n\n    it \"gets with state\" do\n      client = OAuth2::Client.new \"localhost\", \"client_id\", \"client_secret\", redirect_uri: \"uri\"\n      uri = client.get_authorize_uri(scope: \"foo bar\", state: \"xyz\")\n      uri.should eq(\"https://localhost/oauth2/authorize?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar&state=xyz\")\n    end\n\n    it \"gets with block\" do\n      client = OAuth2::Client.new \"localhost\", \"client_id\", \"client_secret\", redirect_uri: \"uri\"\n      uri = client.get_authorize_uri(scope: \"foo bar\") do |form|\n        form.add \"baz\", \"qux\"\n      end\n      uri.should eq(\"https://localhost/oauth2/authorize?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar&baz=qux\")\n    end\n\n    it \"gets with absolute uri\" do\n      client = OAuth2::Client.new \"localhost\", \"client_id\", \"client_secret\",\n        redirect_uri: \"uri\",\n        authorize_uri: \"https://example2.com:1234/foo?bar=baz\"\n      uri = client.get_authorize_uri(scope: \"foo bar\")\n      uri.should eq(\"https://example2.com:1234/foo?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar&bar=baz\")\n    end\n  end\n\n  describe \"get_access_token_using_*\" do\n    describe \"using HTTP Basic authentication to pass credentials\" do\n      describe \"#get_access_token_using_authorization_code\" do\n        it \"gets a valid token successfully\" do\n          handler = HTTP::Handler::HandlerProc.new do |context|\n            body = context.request.body.not_nil!.gets_to_end\n            response = {access_token: \"access_token\", body: body}\n            context.response.print response.to_json\n          end\n\n          run_handler(handler) do |http_client|\n            client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n            client.http_client = http_client\n\n            token = client.get_access_token_using_authorization_code(authorization_code: \"SDFhw39fwfg23flSfpawbef\")\n            token.extra.not_nil![\"body\"].should eq %(\"redirect_uri=&grant_type=authorization_code&code=SDFhw39fwfg23flSfpawbef\")\n            token.access_token.should eq \"access_token\"\n          end\n        end\n\n        it \"returns an error if the response has an error status\" do\n          handler = HTTP::Handler::HandlerProc.new do |context|\n            context.response.status = :bad_request\n          end\n\n          run_handler(handler) do |http_client|\n            client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n            client.http_client = http_client\n\n            expect_raises OAuth2::Error do\n              client.get_access_token_using_authorization_code(authorization_code: \"asdf\")\n            end\n          end\n        end\n\n        it \"returns an error if the payload is an error\" do\n          handler = HTTP::Handler::HandlerProc.new do |context|\n            {error: \"oops\"}.to_json context.response\n          end\n\n          run_handler(handler) do |http_client|\n            client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n            client.http_client = http_client\n\n            expect_raises OAuth2::Error do\n              client.get_access_token_using_authorization_code(authorization_code: \"asdf\")\n            end\n          end\n        end\n      end\n\n      it \"configures HTTP::Client\" do\n        server = HTTP::Server.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n        address = server.bind_tcp 0\n\n        run_server(server) do\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", port: address.port, scheme: \"http\"\n          client.http_client.port.should eq address.port\n          client.http_client.host.should eq \"127.0.0.1\"\n\n          token = client.get_access_token_using_authorization_code(authorization_code: \"SDFhw39fwfg23flSfpawbef\")\n          token.extra.not_nil![\"body\"].should eq %(\"redirect_uri=&grant_type=authorization_code&code=SDFhw39fwfg23flSfpawbef\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#get_access_token_using_resource_owner_credentials\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n          client.http_client = http_client\n\n          token = client.get_access_token_using_resource_owner_credentials(username: \"user123\", password: \"monkey\", scope: \"read_posts\")\n          token.extra.not_nil![\"body\"].should eq %(\"grant_type=password&username=user123&password=monkey&scope=read_posts\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#get_access_token_using_client_credentials\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n          client.http_client = http_client\n\n          token = client.get_access_token_using_client_credentials(scope: \"read_posts\")\n          token.extra.not_nil![\"body\"].should eq %(\"grant_type=client_credentials&scope=read_posts\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#get_access_token_using_refresh_token\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n          client.http_client = http_client\n\n          token = client.get_access_token_using_refresh_token(scope: \"read_posts\", refresh_token: \"some_refresh_token\")\n          token.extra.not_nil![\"body\"].should eq %(\"grant_type=refresh_token&refresh_token=some_refresh_token&scope=read_posts\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#make_token_request\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          dpop = context.request.headers.get?(\"DPoP\")\n          response = {access_token: \"access_token\", body: body, dpop: dpop}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\"\n          client.http_client = http_client\n\n          token_response = client.make_token_request do |form, headers|\n            form.add(\"redirect_uri\", client.redirect_uri)\n            form.add(\"grant_type\", \"authorization_code\")\n            form.add(\"code\", \"some_authorization_code\")\n            form.add(\"code_verifier\", \"a_code_verifier\")\n            form.add(\"nonce\", \"a_nonce\")\n            headers.add(\"DPoP\", \"a_DPoP_jwt_token\")\n          end\n          token_response.status_code.should eq(200)\n          token = OAuth2::AccessToken.from_json(token_response.body)\n          token.extra.not_nil![\"body\"].should eq %(\"redirect_uri=&grant_type=authorization_code&code=some_authorization_code&code_verifier=a_code_verifier&nonce=a_nonce\")\n          token.extra.not_nil![\"dpop\"].should eq %([\"a_DPoP_jwt_token\"])\n          token.access_token.should eq \"access_token\"\n        end\n      end\n    end\n    describe \"using Request Body to pass credentials\" do\n      it \"#get_access_token_using_authorization_code\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\", auth_scheme: OAuth2::AuthScheme::RequestBody\n          client.http_client = http_client\n\n          token = client.get_access_token_using_authorization_code(authorization_code: \"SDFhw39fwfg23flSfpawbef\")\n          token.extra.not_nil![\"body\"].should eq %(\"client_id=client_id&client_secret=client_secret&redirect_uri=&grant_type=authorization_code&code=SDFhw39fwfg23flSfpawbef\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#get_access_token_using_resource_owner_credentials\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\", auth_scheme: OAuth2::AuthScheme::RequestBody\n          client.http_client = http_client\n\n          token = client.get_access_token_using_resource_owner_credentials(username: \"user123\", password: \"monkey\", scope: \"read_posts\")\n          token.extra.not_nil![\"body\"].should eq %(\"client_id=client_id&client_secret=client_secret&grant_type=password&username=user123&password=monkey&scope=read_posts\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#get_access_token_using_client_credentials\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\", auth_scheme: OAuth2::AuthScheme::RequestBody\n          client.http_client = http_client\n\n          token = client.get_access_token_using_client_credentials(scope: \"read_posts\")\n          token.extra.not_nil![\"body\"].should eq %(\"client_id=client_id&client_secret=client_secret&grant_type=client_credentials&scope=read_posts\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#get_access_token_using_refresh_token\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          response = {access_token: \"access_token\", body: body}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\", auth_scheme: OAuth2::AuthScheme::RequestBody\n          client.http_client = http_client\n\n          token = client.get_access_token_using_refresh_token(scope: \"read_posts\", refresh_token: \"some_refresh_token\")\n          token.extra.not_nil![\"body\"].should eq %(\"client_id=client_id&client_secret=client_secret&grant_type=refresh_token&refresh_token=some_refresh_token&scope=read_posts\")\n          token.access_token.should eq \"access_token\"\n        end\n      end\n\n      it \"#make_token_request\" do\n        handler = HTTP::Handler::HandlerProc.new do |context|\n          body = context.request.body.not_nil!.gets_to_end\n          dpop = context.request.headers.get?(\"DPoP\")\n          response = {access_token: \"access_token\", body: body, dpop: dpop}\n          context.response.print response.to_json\n        end\n\n        run_handler(handler) do |http_client|\n          client = OAuth2::Client.new \"127.0.0.1\", \"client_id\", \"client_secret\", scheme: \"http\", auth_scheme: OAuth2::AuthScheme::RequestBody\n          client.http_client = http_client\n\n          token_response = client.make_token_request do |form, headers|\n            form.add(\"grant_type\", \"refresh_token\")\n            form.add(\"refresh_token\", \"some_refresh_token\")\n            form.add(\"scope\", \"read_posts\")\n            form.add(\"nonce\", \"a_nonce\")\n            headers.add(\"DPoP\", \"a_DPoP_jwt_token\")\n          end\n          token_response.status_code.should eq(200)\n          token = OAuth2::AccessToken.from_json(token_response.body)\n          token.extra.not_nil![\"body\"].should eq %(\"client_id=client_id&client_secret=client_secret&grant_type=refresh_token&refresh_token=some_refresh_token&scope=read_posts&nonce=a_nonce\")\n          token.extra.not_nil![\"dpop\"].should eq %([\"a_DPoP_jwt_token\"])\n          token.access_token.should eq \"access_token\"\n        end\n      end\n    end\n  end\n\n  typeof(begin\n    client = OAuth2::Client.new \"localhost\", \"client_id\", \"client_secret\", redirect_uri: \"uri\", authorize_uri: \"/baz\"\n    client.get_access_token_using_authorization_code(\"some_code\")\n    client.get_access_token_using_refresh_token(\"some_refresh_token\")\n    client.get_access_token_using_refresh_token(\"some_refresh_token\", scope: \"some scope\")\n    client.get_access_token_using_client_credentials(scope: \"some scope\")\n    client.get_access_token_using_resource_owner_credentials(username: \"user123\", password: \"monkey\")\n    client.get_access_token_using_resource_owner_credentials(username: \"user123\", password: \"monkey\", scope: \"foo\")\n  end)\nend\n"
  },
  {
    "path": "spec/std/oauth2/session_spec.cr",
    "content": "require \"oauth2\"\nrequire \"http/client\"\n\nmodule OAuth2\n  typeof(begin\n    client = Client.new \"localhost\", \"client_id\", \"client_secret\", redirect_uri: \"uri\", authorize_uri: \"/baz\"\n    token = OAuth2::AccessToken::Bearer.new(\"token\", 3600)\n    Session.new(client, token) { |_| }\n    session = Session.new(client, token, Time.utc) { |_| }\n    session.authenticate(HTTP::Client.new(\"localhost\"))\n  end)\nend\n"
  },
  {
    "path": "spec/std/object_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/finalize\"\n\nprivate class StringWrapper\n  delegate downcase, to: @string\n  delegate upcase, capitalize, char_at, scan, to: @string\n\n  @string : String\n\n  def initialize(@string)\n  end\nend\n\nprivate EQ_OPERATORS = %w(<= >= == != []= ===)\n\nprivate class TestObject\n  getter getter1\n  getter getter2 : Int32\n  getter getter3 : Int32 = 3\n  getter getter4 = 4\n\n  getter! getter5\n  @getter5 : Int32?\n\n  getter! getter6 : Int32\n\n  getter? getter7\n  getter? getter8 : Bool\n  getter? getter9 : Bool = true\n  getter? getter10 = true\n\n  getter(getter11) { 11 }\n\n  @@getter12_value = 12\n  getter getter12 : Int32 { @@getter12_value }\n\n  def self.getter12_value=(@@getter12_value)\n  end\n\n  @getter13_counter = 0\n  getter(getter13) { @getter13_counter += 1; false }\n\n  getter?(getter14 : Bool) { true }\n  getter?(getter15) { true }\n\n  setter setter1\n  setter setter2 : Int32\n  setter setter3 : Int32 = 3\n  setter setter4 = 4\n\n  property property1\n  property property2 : Int32\n  property property3 : Int32 = 3\n  property property4 = 4\n\n  property! property5\n  @property5 : Int32?\n\n  property! property6 : Int32\n\n  property? property7\n  property? property8 : Bool\n  property? property9 : Bool = true\n  property? property10 = true\n\n  property(property11) { 11 }\n  property property12 : Int32 { 10 + 2 }\n\n  @property13_counter = 0\n  property(property13) { @property13_counter += 1; false }\n\n  property?(property14 : Bool) { true }\n  property?(property15) { true }\n\n  def initialize\n    @getter1 = 1\n    @getter2 = 2\n\n    @getter7 = true\n    @getter8 = true\n\n    @setter1 = 1\n    @setter2 = 2\n\n    @property1 = 1\n    @property2 = 2\n\n    @property7 = true\n    @property8 = true\n  end\n\n  def getter5=(@getter5)\n  end\n\n  def getter6=(@getter6)\n  end\n\n  def setter1\n    @setter1\n  end\n\n  def setter2\n    @setter2\n  end\n\n  def setter3\n    @setter3\n  end\n\n  def setter4\n    @setter4\n  end\n\n  def []=(key, value)\n    {key, value}\n  end\n\n  # NOTE: these methods are a syntax error in older versions\n  {% if compare_versions(Crystal::VERSION, \"1.12.0-dev\") >= 0 %}\n    {% for op in EQ_OPERATORS %}\n      def {{ op.id }}(*args, **opts)\n        [args, opts]\n      end\n\n      def {{ op.id }}(*args, **opts, &)\n        [args, opts, yield]\n      end\n    {% end %}\n  {% end %}\n\n  annotation TestAnnotation\n  end\n\n  @[TestAnnotation]\n  property(x : Int32) { 1 }\n\n  def self.test_annotation_count\n    {{ @type.instance_vars.select(&.annotation(TestObject::TestAnnotation)).size }}\n  end\n\n  def self.do_set_crystal_type_id(ptr)\n    set_crystal_type_id(ptr)\n  end\nend\n\nprivate class DelegatedTestObject\n  delegate :property1=, to: @test_object\n\n  {% for op in EQ_OPERATORS %}\n    delegate {{ op.id.symbolize }}, to: @test_object\n  {% end %}\n\n  def initialize(@test_object : TestObject)\n  end\nend\n\nprivate class TestObjectWithFinalize\n  include FinalizeCounter\n\n  def_clone\nend\n\nprivate class HashedTestObject\n  property a : Int32\n  property b : Int32\n\n  def initialize(@a, @b)\n  end\n\n  def_hash :a, :b\nend\n\nprivate struct NonReflexive\n  def ==(other)\n    false\n  end\nend\n\nprivate class DefEquals\n  def initialize\n    @x = NonReflexive.new\n  end\n\n  def_equals @x\nend\n\nprivate struct TestMutableStruct\n  getter x = 0\n\n  def foo\n    @x += 1\n  end\nend\n\ndescribe Object do\n  describe \"delegate\" do\n    it \"delegates\" do\n      wrapper = StringWrapper.new(\"HellO\")\n      wrapper.downcase.should eq(\"hello\")\n      wrapper.upcase.should eq(\"HELLO\")\n      wrapper.capitalize.should eq(\"Hello\")\n\n      wrapper.char_at(0).should eq('H')\n      wrapper.char_at(index: 1).should eq('e')\n\n      wrapper.char_at(10) { 20 }.should eq(20)\n\n      matches = [] of String\n      wrapper.scan(/l/) do |match|\n        matches << match[0]\n      end\n      matches.should eq([\"l\", \"l\"])\n    end\n\n    it \"delegates setter\" do\n      test_object = TestObject.new\n      delegated = DelegatedTestObject.new(test_object)\n      delegated.property1 = 42\n      test_object.property1.should eq 42\n    end\n\n    it \"delegates []=\" do\n      test_object = TestObject.new\n      delegated = DelegatedTestObject.new(test_object)\n      (delegated[\"foo\"] = \"bar\").should eq({\"foo\", \"bar\"})\n    end\n\n    {% if compare_versions(Crystal::VERSION, \"1.12.0-dev\") >= 0 %}\n      {% for op in EQ_OPERATORS %}\n        it \"forwards \\#{{ op.id }} with multiple parameters\" do\n          test_object = TestObject.new\n          delegated = DelegatedTestObject.new(test_object)\n          delegated.{{ op.id }}(1, 2, a: 3, b: 4).should eq [{1, 2}, {a: 3, b: 4}]\n        end\n\n        it \"forwards \\#{{ op.id }} with multiple parameters and block parameter\" do\n          test_object = TestObject.new\n          delegated = DelegatedTestObject.new(test_object)\n          delegated.{{ op.id }}(1, 2, a: 3, b: 4) { 5 }.should eq [{1, 2}, {a: 3, b: 4}, 5]\n        end\n      {% end %}\n    {% end %}\n  end\n\n  describe \"getter\" do\n    it \"uses simple getter\" do\n      obj = TestObject.new\n      obj.getter1.should eq(1)\n      typeof(obj.@getter1).should eq(Int32)\n      typeof(obj.getter1).should eq(Int32)\n    end\n\n    it \"uses getter with type declaration\" do\n      obj = TestObject.new\n      obj.getter2.should eq(2)\n      typeof(obj.@getter2).should eq(Int32)\n      typeof(obj.getter2).should eq(Int32)\n    end\n\n    it \"uses getter with type declaration and default value\" do\n      obj = TestObject.new\n      obj.getter3.should eq(3)\n      typeof(obj.@getter3).should eq(Int32)\n      typeof(obj.getter3).should eq(Int32)\n    end\n\n    it \"uses getter with assignment\" do\n      obj = TestObject.new\n      obj.getter4.should eq(4)\n      typeof(obj.@getter4).should eq(Int32)\n      typeof(obj.getter4).should eq(Int32)\n    end\n\n    it \"defines lazy getter with block\" do\n      obj = TestObject.new\n      obj.getter11.should eq(11)\n      obj.getter12.should eq(12)\n      TestObject.getter12_value = 24\n      obj.getter12.should eq(12)\n\n      obj2 = TestObject.new\n      obj2.getter12.should eq(24)\n    end\n\n    it \"defines lazy getter with block returning false\" do\n      obj = TestObject.new\n      obj.@getter13_counter.should eq(0)\n      3.times { obj.getter13.should be_false }\n      obj.@getter13_counter.should eq(1)\n    end\n  end\n\n  describe \"getter!\" do\n    it \"uses getter!\" do\n      obj = TestObject.new\n      expect_raises(NilAssertionError, \"TestObject#getter5 cannot be nil\") do\n        obj.getter5\n      end\n      obj.getter5 = 5\n      obj.getter5.should eq(5)\n      typeof(obj.@getter5).should eq(Int32 | Nil)\n      typeof(obj.getter5).should eq(Int32)\n    end\n\n    it \"uses getter! with type declaration\" do\n      obj = TestObject.new\n      expect_raises(NilAssertionError, \"TestObject#getter6 cannot be nil\") do\n        obj.getter6\n      end\n      obj.getter6 = 6\n      obj.getter6.should eq(6)\n      typeof(obj.@getter6).should eq(Int32 | Nil)\n      typeof(obj.getter6).should eq(Int32)\n    end\n  end\n\n  describe \"getter?\" do\n    it \"uses getter?\" do\n      obj = TestObject.new\n      obj.getter7?.should be_true\n      typeof(obj.@getter7).should eq(Bool)\n      typeof(obj.getter7?).should eq(Bool)\n    end\n\n    it \"uses getter? with type declaration\" do\n      obj = TestObject.new\n      obj.getter8?.should be_true\n      typeof(obj.@getter8).should eq(Bool)\n      typeof(obj.getter8?).should eq(Bool)\n    end\n\n    it \"uses getter? with type declaration and default value\" do\n      obj = TestObject.new\n      obj.getter9?.should be_true\n      typeof(obj.@getter9).should eq(Bool)\n      typeof(obj.getter9?).should eq(Bool)\n    end\n\n    it \"uses getter? with default value\" do\n      obj = TestObject.new\n      obj.getter10?.should be_true\n      typeof(obj.@getter10).should eq(Bool)\n      typeof(obj.getter10?).should eq(Bool)\n    end\n\n    it \"uses getter? with type declaration and block\" do\n      obj = TestObject.new\n      typeof(obj.@getter14).should eq(Bool?)\n      typeof(obj.getter14?).should eq(Bool)\n      obj.@getter14.should be_nil\n      obj.getter14?.should be_true\n      obj.@getter14.should be_true\n    end\n\n    it \"uses getter? with block\" do\n      obj = TestObject.new\n      typeof(obj.@getter15).should eq(Bool?)\n      typeof(obj.getter15?).should eq(Bool)\n      obj.@getter15.should be_nil\n      obj.getter15?.should be_true\n      obj.@getter15.should be_true\n    end\n  end\n\n  describe \"setter\" do\n    it \"uses setter\" do\n      obj = TestObject.new\n      obj.setter1.should eq(1)\n      obj.setter1 = 2\n      obj.setter1.should eq(2)\n    end\n\n    it \"uses setter with type declaration\" do\n      obj = TestObject.new\n      obj.setter2.should eq(2)\n      obj.setter2 = 3\n      obj.setter2.should eq(3)\n    end\n\n    it \"uses setter with type declaration and default value\" do\n      obj = TestObject.new\n      obj.setter3.should eq(3)\n      obj.setter3 = 4\n      obj.setter3.should eq(4)\n    end\n\n    it \"uses setter with default value\" do\n      obj = TestObject.new\n      obj.setter4.should eq(4)\n      obj.setter4 = 5\n      obj.setter4.should eq(5)\n    end\n  end\n\n  describe \"property\" do\n    it \"uses property\" do\n      obj = TestObject.new\n      obj.property1.should eq(1)\n      obj.property1 = 2\n      obj.property1.should eq(2)\n    end\n\n    it \"uses property with type declaration\" do\n      obj = TestObject.new\n      obj.property2.should eq(2)\n      obj.property2 = 3\n      obj.property2.should eq(3)\n    end\n\n    it \"uses property with type declaration and default value\" do\n      obj = TestObject.new\n      obj.property3.should eq(3)\n      obj.property3 = 4\n      obj.property3.should eq(4)\n    end\n\n    it \"uses property with default value\" do\n      obj = TestObject.new\n      obj.property4.should eq(4)\n      obj.property4 = 5\n      obj.property4.should eq(5)\n    end\n\n    it \"defines lazy property with block\" do\n      obj = TestObject.new\n      obj.property11.should eq(11)\n      obj.property11 = 12\n      obj.property11.should eq(12)\n\n      obj.property12.should eq(12)\n      obj.property12 = 13\n      obj.property12.should eq(13)\n    end\n\n    it \"defines lazy property with block returning false\" do\n      obj = TestObject.new\n      obj.@property13_counter.should eq(0)\n      3.times { obj.property13.should be_false }\n      obj.@property13_counter.should eq(1)\n    end\n  end\n\n  describe \"property!\" do\n    it \"uses property!\" do\n      obj = TestObject.new\n      expect_raises(NilAssertionError, \"TestObject#property5 cannot be nil\") do\n        obj.property5\n      end\n      obj.property5 = 5\n      obj.property5.should eq(5)\n    end\n\n    it \"uses property! with type declaration\" do\n      obj = TestObject.new\n      expect_raises(NilAssertionError, \"TestObject#property6 cannot be nil\") do\n        obj.property6\n      end\n      obj.property6 = 6\n      obj.property6.should eq(6)\n    end\n  end\n\n  describe \"property?\" do\n    it \"uses property?\" do\n      obj = TestObject.new\n      obj.property7?.should be_true\n      obj.property7 = false\n      obj.property7?.should be_false\n    end\n\n    it \"uses property? with type declaration\" do\n      obj = TestObject.new\n      obj.property8?.should be_true\n      obj.property8 = false\n      obj.property8?.should be_false\n    end\n\n    it \"uses property? with type declaration and default value\" do\n      obj = TestObject.new\n      obj.property9?.should be_true\n      obj.property9 = false\n      obj.property9?.should be_false\n    end\n\n    it \"uses property? with default value\" do\n      obj = TestObject.new\n      obj.property10?.should be_true\n      obj.property10 = false\n      obj.property10?.should be_false\n    end\n\n    it \"uses property? with type declaration and block\" do\n      obj = TestObject.new\n      typeof(obj.@property14).should eq(Bool?)\n      typeof(obj.property14?).should eq(Bool)\n      obj.@property14.should be_nil\n      obj.property14?.should be_true\n      obj.@property14.should be_true\n    end\n\n    it \"uses property? with block\" do\n      obj = TestObject.new\n      typeof(obj.@property15).should eq(Bool?)\n      typeof(obj.property15?).should eq(Bool)\n      obj.@property15.should be_nil\n      obj.property15?.should be_true\n      obj.@property15.should be_true\n    end\n  end\n\n  describe \"#in?\" do\n    it \"works with Enumerable-s\" do\n      \"foo\".in?([\"foo\", \"bar\"]).should be_true\n      \"bar\".in?({\"foo\", \"baz\"}).should be_false\n      42.in?(0..100).should be_true\n      4242.in?(0..100).should be_false\n    end\n\n    it \"works with splatted arguments\" do\n      \"baz\".in?(\"foo\", \"bar\").should be_false\n      1.in?(1, 10, 100).should be_true\n    end\n\n    it \"works with other objects implementing #includes?\" do\n      \"o\".in?(\"foo\").should be_true\n      'o'.in?(\"foo\").should be_true\n      'x'.in?(\"foo\").should be_false\n    end\n  end\n\n  it \"#unsafe_as\" do\n    0x12345678.unsafe_as(Tuple(UInt8, UInt8, UInt8, UInt8)).should eq({0x78, 0x56, 0x34, 0x12})\n  end\n\n  it \"calls #finalize on #clone'd objects\" do\n    obj = TestObjectWithFinalize.new\n    assert_finalizes(\"clone\") { obj.clone }\n  end\n\n  describe \"def_hash\" do\n    it \"should return same hash for equal property values\" do\n      HashedTestObject.new(1, 2).hash.should eq HashedTestObject.new(1, 2).hash\n    end\n\n    it \"shouldn't return same hash for different property values\" do\n      HashedTestObject.new(1, 2).hash.should_not eq HashedTestObject.new(3, 4).hash\n    end\n  end\n\n  it \"applies annotation to lazy property (#9139)\" do\n    TestObject.test_annotation_count.should eq(1)\n  end\n\n  describe \"def_equals\" do\n    it \"compares by reference\" do\n      x = DefEquals.new\n      y = DefEquals.new\n      (x == x).should be_true\n      (x == y).should be_false\n    end\n  end\n\n  describe \"#not_nil!\" do\n    it \"basic\" do\n      1.not_nil!\n      expect_raises(NilAssertionError, \"Nil assertion failed\") do\n        nil.not_nil!\n      end\n    end\n\n    it \"removes Nil type\" do\n      x = TestObject.new.as(TestObject?)\n      typeof(x.not_nil!).should eq TestObject\n      x.not_nil!.should be x\n    end\n\n    it \"raises NilAssertionError\" do\n      x = nil.as(TestObject?)\n      typeof(x.not_nil!).should eq TestObject\n      expect_raises(NilAssertionError, \"Nil assertion failed\") do\n        x.not_nil!\n      end\n    end\n\n    it \"with message\" do\n      x = TestObject.new\n      x.not_nil!(\"custom message\").should be x\n      expect_raises(NilAssertionError, \"custom message\") do\n        nil.not_nil!(\"custom message\")\n      end\n    end\n\n    it \"does not copy its receiver when it is a value (#13263)\" do\n      x = TestMutableStruct.new\n      x.not_nil!.foo.should eq(1)\n      x.not_nil!.foo.should eq(2)\n      x.foo.should eq(3)\n    end\n  end\n\n  it \".set_crystal_type_id\" do\n    ary = StaticArray[Int32::MAX, Int32::MAX]\n    TestObject.do_set_crystal_type_id(pointerof(ary))\n    ary[0].should eq TestObject.crystal_instance_type_id\n    ary[1].should eq Int32::MAX\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/cipher_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"openssl/cipher\"\n\ndescribe OpenSSL::Cipher do\n  it \"encrypts/decrypts\" do\n    cipher = \"aes-128-cbc\"\n    c1 = OpenSSL::Cipher.new(cipher)\n    c2 = OpenSSL::Cipher.new(cipher)\n    key = \"\\0\" * 16\n    iv = \"\\0\" * 16\n    data = \"DATA\" * 5\n    ciphertext = File.read(datapath(\"cipher_spec.ciphertext\"))\n\n    c1.name.should eq(c2.name)\n\n    c1.encrypt\n    c2.encrypt\n    c1.key = c2.key = key\n    c1.iv = c2.iv = iv\n\n    s1 = IO::Memory.new\n    s2 = IO::Memory.new\n    s1.write(c1.update(\"DATA\"))\n    s1.write(c1.update(\"DATA\" * 4))\n    s1.write(c1.final)\n    s2.write(c2.update(data))\n    s2.write(c2.final)\n\n    s1.to_slice.should eq(ciphertext.to_slice)\n    s1.to_slice.should eq(s2.to_slice)\n\n    c1.decrypt\n    c2.decrypt\n    c1.key = c2.key = key\n    c1.iv = c2.iv = iv\n\n    s3 = IO::Memory.new\n    s4 = IO::Memory.new\n    s3.write(c1.update(s1.to_slice))\n    s3.write(c1.final)\n\n    s4.write(c2.update(s2.to_slice))\n    s4.write(c2.final)\n    s3.to_s.should eq(data)\n    s3.to_slice.should eq(s4.to_slice)\n  end\n\n  it \"authenticated?\" do\n    begin\n      cipher = OpenSSL::Cipher.new(\"aes-128-gcm\")\n      cipher.authenticated?.should be_true\n    rescue ArgumentError\n      # This system doesn't support GCM ciphers\n      # Silently skip, as this method will never return true\n      # Remove when macOS platforms target >= v10.13\n    end\n\n    cipher = OpenSSL::Cipher.new(\"aes-128-cbc\")\n    cipher.authenticated?.should be_false\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/digest_spec.cr",
    "content": "require \"spec\"\nrequire \"../spec_helper\"\nrequire \"../../../src/openssl\"\n\ndescribe OpenSSL::Digest do\n  [\n    {\"SHA1\", \"dcf4a1e3542b1a40a4ac2a3f7c92ffdb2d19812f\"},\n    {\"SHA256\", \"df81eea14671ce970fb1052e9f5dd6dbda652ed37423ed3624120ec1534784a7\"},\n    {\"SHA512\", \"082907b85fe25c33bba4765185b52993a493cfd24454edf4b977ccd9301a890659c52592456cbd8aeb5215055d9dd4a7d50a4db9961715fb764fb6c393a83192\"},\n  ].each do |algorithm, expected|\n    it \"should be able to calculate #{algorithm}\" do\n      digest = OpenSSL::Digest.new(algorithm)\n      digest << \"fooø\"\n      digest.hexfinal.should eq(expected)\n\n      digest.reset\n      digest << \"fooø\"\n      digest.final.hexstring.should eq(expected)\n    end\n  end\n\n  it \"can't call #final more than once\" do\n    digest = OpenSSL::Digest.new(\"SHA1\")\n    digest.final\n    expect_raises(Digest::FinalizedError) do\n      digest.final\n    end\n  end\n\n  it \"raises a UnsupportedError if digest is unsupported\" do\n    expect_raises OpenSSL::Digest::UnsupportedError do\n      OpenSSL::Digest.new(\"unsupported\")\n    end\n  end\n\n  it \"returns the digest size\" do\n    OpenSSL::Digest.new(\"SHA1\").digest_size.should eq 20\n    OpenSSL::Digest.new(\"SHA256\").digest_size.should eq 32\n  end\n\n  it \"returns the block size\" do\n    OpenSSL::Digest.new(\"SHA1\").block_size.should eq 64\n    OpenSSL::Digest.new(\"SHA256\").block_size.should eq 64\n  end\n\n  it \"correctly reads from IO\" do\n    r, w = IO.pipe\n    digest = OpenSSL::Digest.new(\"SHA256\")\n\n    w << \"foo\"\n    w.close\n    digest << r\n    r.close\n\n    digest.hexfinal.should eq(\"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\")\n  end\n\n  describe \".dup\" do\n    it \"preserves type\" do\n      OpenSSL::Digest.new(\"MD5\").dup.class.should eq(OpenSSL::Digest)\n    end\n\n    it \"preserves value\" do\n      digest1 = OpenSSL::Digest.new(\"MD5\")\n      digest1.update(\"a\")\n      digest2 = digest1.dup\n\n      digest1.final.should eq(digest2.final)\n    end\n\n    it \"leads to not sharing state\" do\n      digest1 = OpenSSL::Digest.new(\"MD5\")\n      digest1.update(\"a\")\n\n      digest2 = digest1.dup\n\n      digest1.update(\"b\")\n\n      digest1.final.should_not eq(digest2.final)\n    end\n\n    it \"leads to deterministic updates\" do\n      digest1 = OpenSSL::Digest.new(\"MD5\")\n      digest1.update(\"a\")\n\n      digest2 = digest1.dup\n\n      digest1.update(\"b\")\n      digest2.update(\"b\")\n\n      digest1.final.should eq(digest2.final)\n    end\n  end\n\n  it \"digest with file content\" do\n    path = datapath(\"test_file.txt\")\n    OpenSSL::Digest.new(\"MD5\").file(path).final.hexstring.should eq(\"a4f13879534d2b93a9a65a4b2d0dde9d\")\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/hmac_spec.cr",
    "content": "require \"spec\"\nrequire \"openssl/hmac\"\n\ndescribe OpenSSL::HMAC do\n  [\n    {OpenSSL::Algorithm::MD5, \"0c7a250281315ab863549f66cd8a3a53\"},\n    {OpenSSL::Algorithm::SHA1, \"46b4ec586117154dacd49d664e5d63fdc88efb51\"},\n    {OpenSSL::Algorithm::SHA224, \"4c1f774863acb63b7f6e9daa9b5c543fa0d5eccf61e3ffc3698eacdd\"},\n    {OpenSSL::Algorithm::SHA256, \"f9320baf0249169e73850cd6156ded0106e2bb6ad8cab01b7bbbebe6d1065317\"},\n    {OpenSSL::Algorithm::SHA384, \"3d10d391bee2364df2c55cf605759373e1b5a4ca9355d8f3fe42970471eca2e422a79271a0e857a69923839015877fc6\"},\n    {OpenSSL::Algorithm::SHA512, \"114682914c5d017dfe59fdc804118b56a3a652a0b8870759cf9e792ed7426b08197076bf7d01640b1b0684df79e4b67e37485669e8ce98dbab60445f0db94fce\"},\n  ].each do |(algorithm, expected)|\n    it \"computes #{algorithm}\" do\n      OpenSSL::HMAC.hexdigest(algorithm, \"foo\", \"bar\").should eq(expected)\n    end\n  end\n\n  {% if compare_versions(LibSSL::OPENSSL_VERSION, \"3.0.0\") < 0 %}\n    it \"computes MD4\" do\n      OpenSSL::HMAC.hexdigest(OpenSSL::Algorithm::MD4, \"foo\", \"bar\").should eq(\"f3593b56f00b25c8af31d02ddef6d2d0\")\n    end\n\n    it \"computes RIPEMD160\" do\n      OpenSSL::HMAC.hexdigest(OpenSSL::Algorithm::RIPEMD160, \"foo\", \"bar\").should eq(\"20d23140503df606c91bda9293f1ad4a23afe509\")\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/openssl/pkcs5_spec.cr",
    "content": "require \"spec\"\nrequire \"openssl/pkcs5\"\n\ndescribe OpenSSL::PKCS5 do\n  it \"computes pbkdf2_hmac_sha1\" do\n    [\n      {1, 16, \"0c60c80f961f0e71f3a9b524af601206\"},\n      {1, 32, \"0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164\"},\n      {2**16, 16, \"1b345dd55f62a35aecdb9229bc7ae95b\"},\n      {2**16, 32, \"1b345dd55f62a35aecdb9229bc7ae95b305a8d538940134627e46f82d3a41e5e\"},\n    ].each do |(iterations, key_size, expected)|\n      OpenSSL::PKCS5.pbkdf2_hmac_sha1(\"password\", \"salt\", iterations, key_size).hexstring.should eq expected\n    end\n  end\n\n  {% if compare_versions(LibSSL::OPENSSL_VERSION, \"3.0.0\") < 0 %}\n    [\n      {OpenSSL::Algorithm::MD4, 1, 16, \"1857f69412150bca4542581d0f9e7fd1\"},\n      {OpenSSL::Algorithm::MD4, 1, 32, \"1857f69412150bca4542581d0f9e7fd19332ff5c0b820cb0172457a29c5519be\"},\n      {OpenSSL::Algorithm::MD4, 2**16, 16, \"3d87c982c8c4223f4af39406ac3882e6\"},\n      {OpenSSL::Algorithm::MD4, 2**16, 32, \"3d87c982c8c4223f4af39406ac3882e6e6b92685dcf89f74df8caf7500b41883\"},\n      {OpenSSL::Algorithm::RIPEMD160, 1, 16, \"b725258b125e0bacb0e2307e34feb16a\"},\n      {OpenSSL::Algorithm::RIPEMD160, 1, 32, \"b725258b125e0bacb0e2307e34feb16a4d0d6aed6cb4b0eee458fc1829020428\"},\n      {OpenSSL::Algorithm::RIPEMD160, 2**16, 16, \"93a8e007de2608e54911684cbebe2780\"},\n      {OpenSSL::Algorithm::RIPEMD160, 2**16, 32, \"93a8e007de2608e54911684cbebe27808cc39fa59de9acdf74492155b46c4d2d\"},\n    ].each do |(algorithm, iterations, key_size, expected)|\n      it \"computes pbkdf2_hmac #{algorithm}\" do\n        OpenSSL::PKCS5.pbkdf2_hmac(\"password\", \"salt\", iterations, algorithm, key_size).hexstring.should eq expected\n      end\n    end\n  {% end %}\n\n  [\n    {OpenSSL::Algorithm::MD5, 1, 16, \"f31afb6d931392daa5e3130f47f9a9b6\"},\n    {OpenSSL::Algorithm::MD5, 1, 32, \"f31afb6d931392daa5e3130f47f9a9b6e8e72029d8350b9fb27a9e0e00b9d991\"},\n    {OpenSSL::Algorithm::MD5, 2**16, 16, \"8b4ffd76e400c3b74b3d0fbfd9232048\"},\n    {OpenSSL::Algorithm::MD5, 2**16, 32, \"8b4ffd76e400c3b74b3d0fbfd9232048762c86fe7684992c6f581f073f6625ee\"},\n    {OpenSSL::Algorithm::SHA1, 1, 16, \"0c60c80f961f0e71f3a9b524af601206\"},\n    {OpenSSL::Algorithm::SHA1, 1, 32, \"0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164\"},\n    {OpenSSL::Algorithm::SHA1, 2**16, 16, \"1b345dd55f62a35aecdb9229bc7ae95b\"},\n    {OpenSSL::Algorithm::SHA1, 2**16, 32, \"1b345dd55f62a35aecdb9229bc7ae95b305a8d538940134627e46f82d3a41e5e\"},\n    {OpenSSL::Algorithm::SHA224, 1, 16, \"3c198cbdb9464b7857966bd05b7bc92b\"},\n    {OpenSSL::Algorithm::SHA224, 1, 32, \"3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e63155d4e490557fd85989497\"},\n    {OpenSSL::Algorithm::SHA224, 2**16, 16, \"53a7f042a8154092058cfe87e7fbf1c1\"},\n    {OpenSSL::Algorithm::SHA224, 2**16, 32, \"53a7f042a8154092058cfe87e7fbf1c1f96826a9a2ffd8bcfda50bb9f60786f0\"},\n    {OpenSSL::Algorithm::SHA256, 1, 16, \"120fb6cffcf8b32c43e7225256c4f837\"},\n    {OpenSSL::Algorithm::SHA256, 1, 32, \"120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b\"},\n    {OpenSSL::Algorithm::SHA256, 2**16, 16, \"4156f668bb31db3a17f4d1b91424ef0d\"},\n    {OpenSSL::Algorithm::SHA256, 2**16, 32, \"4156f668bb31db3a17f4d1b91424ef0d417ad1f35d055aceaebd8da0f6a44b7e\"},\n    {OpenSSL::Algorithm::SHA384, 1, 16, \"c0e14f06e49e32d73f9f52ddf1d0c5c7\"},\n    {OpenSSL::Algorithm::SHA384, 1, 32, \"c0e14f06e49e32d73f9f52ddf1d0c5c7191609233631dadd76a567db42b78676\"},\n    {OpenSSL::Algorithm::SHA384, 2**16, 16, \"c7b5b0b726f6556587cced08d184253b\"},\n    {OpenSSL::Algorithm::SHA384, 2**16, 32, \"c7b5b0b726f6556587cced08d184253bc9d2eb802db134fb9029b86ab25e7cd0\"},\n    {OpenSSL::Algorithm::SHA512, 1, 16, \"867f70cf1ade02cff3752599a3a53dc4\"},\n    {OpenSSL::Algorithm::SHA512, 1, 32, \"867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252\"},\n    {OpenSSL::Algorithm::SHA512, 2**16, 16, \"6f64c3f8023813d8c2cab43cabfaa65e\"},\n    {OpenSSL::Algorithm::SHA512, 2**16, 32, \"6f64c3f8023813d8c2cab43cabfaa65ed061822afe974060d8079d122fb869f4\"},\n  ].each do |(algorithm, iterations, key_size, expected)|\n    it \"computes pbkdf2_hmac #{algorithm}\" do\n      OpenSSL::PKCS5.pbkdf2_hmac(\"password\", \"salt\", iterations, algorithm, key_size).hexstring.should eq expected\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/ssl/context_spec.cr",
    "content": "require \"../../spec_helper\"\nrequire \"openssl\"\nrequire \"../../../support/finalize\"\n\nclass OpenSSL::SSL::Context\n  include FinalizeCounter\nend\n\ndescribe OpenSSL::SSL::Context do\n  it \"new for client\" do\n    context = OpenSSL::SSL::Context::Client.new\n\n    (context.options & OpenSSL::SSL::Options::ALL).should eq(OpenSSL::SSL::Options::ALL)\n    (context.options & OpenSSL::SSL::Options::NO_SESSION_RESUMPTION_ON_RENEGOTIATION).should eq(OpenSSL::SSL::Options::NO_SESSION_RESUMPTION_ON_RENEGOTIATION)\n\n    context.modes.should eq(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n    context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::PEER)\n\n    OpenSSL::SSL::Context::Client.new(LibSSL.tlsv1_method)\n  end\n\n  it \"new for server\" do\n    context = OpenSSL::SSL::Context::Server.new\n\n    (context.options & OpenSSL::SSL::Options::ALL).should eq(OpenSSL::SSL::Options::ALL)\n    (context.options & OpenSSL::SSL::Options::NO_SESSION_RESUMPTION_ON_RENEGOTIATION).should eq(OpenSSL::SSL::Options::NO_SESSION_RESUMPTION_ON_RENEGOTIATION)\n    (context.options & OpenSSL::SSL::Options::NO_RENEGOTIATION).should eq(OpenSSL::SSL::Options::NO_RENEGOTIATION)\n\n    context.modes.should eq(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n    context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::NONE)\n\n    OpenSSL::SSL::Context::Server.new(LibSSL.tlsv1_method)\n  end\n\n  it \"insecure for client\" do\n    context = OpenSSL::SSL::Context::Client.insecure\n    context.should be_a(OpenSSL::SSL::Context::Client)\n    context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::NONE)\n    context.options.no_ssl_v3?.should_not be_true\n    context.modes.should eq(OpenSSL::SSL::Modes::AUTO_RETRY)\n\n    OpenSSL::SSL::Context::Client.insecure(LibSSL.tlsv1_method)\n  end\n\n  it \"insecure for server\" do\n    context = OpenSSL::SSL::Context::Server.insecure\n    context.should be_a(OpenSSL::SSL::Context::Server)\n    context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::NONE)\n    context.options.no_ssl_v3?.should_not be_true\n    context.modes.should eq(OpenSSL::SSL::Modes::AUTO_RETRY)\n\n    OpenSSL::SSL::Context::Server.insecure(LibSSL.tlsv1_method)\n  end\n\n  it \"sets certificate chain\" do\n    context = OpenSSL::SSL::Context::Client.new\n    context.certificate_chain = datapath(\"openssl\", \"openssl.crt\")\n  end\n\n  it \"fails to set certificate chain\" do\n    context = OpenSSL::SSL::Context::Client.new\n    expect_raises(OpenSSL::Error) { context.certificate_chain = datapath(\"nonexistent.crt\") }\n    expect_raises(OpenSSL::Error) { context.certificate_chain = datapath(\"test_file.txt\") }\n  end\n\n  it \"sets private key\" do\n    context = OpenSSL::SSL::Context::Client.new\n    context.private_key = datapath(\"openssl\", \"openssl.key\")\n  end\n\n  it \"fails to set private key\" do\n    context = OpenSSL::SSL::Context::Client.new\n    expect_raises(OpenSSL::Error) { context.private_key = datapath(\"nonexistent.key\") }\n    expect_raises(OpenSSL::Error) { context.private_key = datapath(\"test_file.txt\") }\n  end\n\n  describe \"ciphers\" do\n    pending \"uses intermediate default ciphers\" do\n      # Can't be checked because `Context#ciphers` is not implemented.\n      OpenSSL::SSL::Context::Client.new.ciphers.should eq OpenSSL::SSL::Context::CIPHERS_OLD\n      OpenSSL::SSL::Context::Server.new.ciphers.should eq OpenSSL::SSL::Context::CIPHERS_INTERMEDIATE\n    end\n\n    it \"sets ciphers\" do\n      ciphers = \"EDH+aRSA DES-CBC3-SHA !RC4\"\n      context = OpenSSL::SSL::Context::Client.new\n      (context.ciphers = ciphers).should eq(ciphers)\n    end\n\n    it \"sets cipher_suites\" do\n      cipher_suites = OpenSSL::SSL::Context::CIPHER_SUITES_MODERN\n      context = OpenSSL::SSL::Context::Client.new\n      (context.cipher_suites = cipher_suites).should eq(cipher_suites)\n    end\n\n    it \"sets modern ciphers\" do\n      OpenSSL::SSL::Context::Client.new.set_modern_ciphers\n    end\n\n    it \"sets intermediate ciphers\" do\n      OpenSSL::SSL::Context::Client.new.set_intermediate_ciphers\n    end\n\n    it \"sets old ciphers\" do\n      OpenSSL::SSL::Context::Client.new.set_old_ciphers\n    end\n  end\n\n  it \"changes security level\" do\n    context = OpenSSL::SSL::Context::Client.new\n    level = context.security_level\n    context.security_level = level + 1\n\n    if LibSSL.responds_to?(:ssl_ctx_set_security_level)\n      context.security_level.should eq(level + 1)\n    else\n      context.security_level.should eq 0\n    end\n  end\n\n  it \"adds temporary ecdh curve (P-256)\" do\n    context = OpenSSL::SSL::Context::Client.new\n    context.set_tmp_ecdh_key\n  end\n\n  it \"adds options\" do\n    context = OpenSSL::SSL::Context::Client.new\n    context.remove_options(context.options) # reset\n    default_options = context.options       # options we can't unset\n\n    context.add_options(OpenSSL::SSL::Options::ALL)\n      .should eq(default_options | OpenSSL::SSL::Options::ALL)\n\n    context.add_options(OpenSSL::SSL::Options.flags(NO_SSL_V2, NO_SSL_V3))\n      .should eq(OpenSSL::SSL::Options.flags(ALL, NO_SSL_V2, NO_SSL_V3))\n  end\n\n  it \"removes options\" do\n    context = OpenSSL::SSL::Context::Client.insecure\n    default_options = context.options\n    context.add_options(OpenSSL::SSL::Options.flags(NO_TLS_V1, NO_SSL_V2))\n    context.remove_options(OpenSSL::SSL::Options::NO_TLS_V1).should eq(default_options | OpenSSL::SSL::Options::NO_SSL_V2)\n  end\n\n  it \"returns options\" do\n    context = OpenSSL::SSL::Context::Client.insecure\n    default_options = context.options\n    context.add_options(OpenSSL::SSL::Options.flags(ALL, NO_SSL_V2))\n    context.options.should eq(default_options | OpenSSL::SSL::Options.flags(ALL, NO_SSL_V2))\n  end\n\n  it \"adds modes\" do\n    context = OpenSSL::SSL::Context::Client.insecure\n    context.add_modes(OpenSSL::SSL::Modes::AUTO_RETRY).should eq(OpenSSL::SSL::Modes::AUTO_RETRY)\n    context.add_modes(OpenSSL::SSL::Modes::RELEASE_BUFFERS)\n      .should eq(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n  end\n\n  it \"removes modes\" do\n    context = OpenSSL::SSL::Context::Client.insecure\n    context.add_modes(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n    context.remove_modes(OpenSSL::SSL::Modes::AUTO_RETRY).should eq(OpenSSL::SSL::Modes::RELEASE_BUFFERS)\n  end\n\n  it \"returns modes\" do\n    context = OpenSSL::SSL::Context::Client.insecure\n    context.add_modes(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n    context.modes.should eq(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n  end\n\n  it \"sets the verify mode\" do\n    context = OpenSSL::SSL::Context::Client.new\n    context.verify_mode = OpenSSL::SSL::VerifyMode::NONE\n    context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::NONE)\n    context.verify_mode = OpenSSL::SSL::VerifyMode::PEER\n    context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::PEER)\n  end\n\n  if LibSSL.responds_to?(:ssl_ctx_set_alpn_protos)\n    it \"alpn_protocol=\" do\n      context = OpenSSL::SSL::Context::Client.insecure\n      context.alpn_protocol = \"h2\"\n    end\n  end\n\n  it \"on_server_name\" do\n    context = OpenSSL::SSL::Context::Server.new\n    context.certificate_chain = datapath(\"openssl\", \"openssl.crt\")\n    context.private_key = datapath(\"openssl\", \"openssl.key\")\n\n    alt_context = OpenSSL::SSL::Context::Server.new\n    alt_context.certificate_chain = datapath(\"openssl\", \"openssl.crt\")\n    alt_context.private_key = datapath(\"openssl\", \"openssl.key\")\n\n    context.on_server_name do |hostname|\n      if hostname == \"alt.example.com\"\n        alt_context\n      else\n        nil\n      end\n    end\n  end\n\n  it \"calls #finalize on insecure client context\" do\n    assert_finalizes(\"insecure_client_ctx\") { OpenSSL::SSL::Context::Client.insecure }\n  end\n\n  it \"calls #finalize on insecure server context\" do\n    assert_finalizes(\"insecure_server_ctx\") { OpenSSL::SSL::Context::Server.insecure }\n  end\n\n  describe \".from_hash\" do\n    it \"builds\" do\n      private_key = datapath(\"openssl\", \"openssl.key\")\n      certificate = datapath(\"openssl\", \"openssl.crt\")\n\n      context = OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate, \"verify_mode\" => \"none\"})\n      context.verify_mode.should eq OpenSSL::SSL::VerifyMode::NONE\n\n      context = OpenSSL::SSL::Context::Server.from_hash({\"key\" => private_key, \"cert\" => certificate})\n      context.verify_mode.should eq OpenSSL::SSL::VerifyMode::NONE\n\n      context = OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate, \"ca\" => certificate})\n      context.verify_mode.should eq OpenSSL::SSL::VerifyMode::PEER\n\n      context = OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate, \"ca\" => File.dirname(certificate)})\n      context.verify_mode.should eq OpenSSL::SSL::VerifyMode::PEER\n    end\n\n    it \"errors\" do\n      private_key = datapath(\"openssl\", \"openssl.key\")\n      certificate = datapath(\"openssl\", \"openssl.crt\")\n      nonexistent = datapath(\"openssl\", \"nonexistent\")\n\n      expect_raises(ArgumentError, \"missing private key\") do\n        OpenSSL::SSL::Context::Client.from_hash({} of String => String)\n      end\n      expect_raises(OpenSSL::Error, /SSL_CTX_use_PrivateKey_file: error:.*:No such file or directory/) do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => nonexistent})\n      end\n      expect_raises(ArgumentError, \"missing certificate\") do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key})\n      end\n      expect_raises(OpenSSL::Error, /SSL_CTX_use_certificate_chain_file: error:.*:No such file or directory/) do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => nonexistent})\n      end\n      expect_raises(ArgumentError, \"Invalid SSL context: missing CA certificate\") do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate})\n      end\n      expect_raises(ArgumentError, %(Invalid SSL context: unknown verify mode \"foo\")) do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate, \"verify_mode\" => \"foo\"})\n      end\n      expect_raises(ArgumentError, \"Invalid SSL context: missing CA certificate\") do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate, \"verify_mode\" => \"peer\"})\n      end\n      expect_raises(OpenSSL::Error, /SSL_CTX_load_verify_locations: error:.*:No such file or directory/) do\n        OpenSSL::SSL::Context::Client.from_hash({\"key\" => private_key, \"cert\" => certificate, \"ca\" => nonexistent})\n      end\n    end\n  end\n\n  describe OpenSSL::SSL::VerifyMode do\n    it \".parse none (#7455)\" do\n      OpenSSL::SSL::VerifyMode.parse(\"none\").should eq OpenSSL::SSL::VerifyMode::NONE\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/ssl/hostname_validation_spec.cr",
    "content": "require \"spec\"\nrequire \"openssl/ssl/hostname_validation\"\n\nprivate def openssl_create_cert(subject = nil, san = nil)\n  cert = OpenSSL::X509::Certificate.new\n  cert.subject = subject if subject\n  cert.add_extension(OpenSSL::X509::Extension.new(\"subjectAltName\", san)) if san\n  cert.to_unsafe\nend\n\ndescribe OpenSSL::SSL::HostnameValidation do\n  describe \"validate_hostname\" do\n    it \"matches IP from certificate SAN entries\" do\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"192.168.1.1\", openssl_create_cert(san: \"IP:192.168.1.1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"192.168.1.2\", openssl_create_cert(san: \"IP:192.168.1.1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchNotFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"::1\", openssl_create_cert(san: \"IP:::1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"::1\", openssl_create_cert(san: \"IP:::2\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchNotFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"0:0:0:0:0:0:0:1\", openssl_create_cert(san: \"IP:::1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"fe80:0:0:0:0:0:0:1\", openssl_create_cert(san: \"IP:fe80::1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"fe80:0:0:0:0:0:0:2\", openssl_create_cert(san: \"IP:fe80::1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchNotFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"fe80:0:1\", openssl_create_cert(san: \"IP:fe80:0::1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchNotFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"fe80::0:1\", openssl_create_cert(san: \"IP:fe80:0::1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n    end\n\n    it \"matches domains from certificate SAN entries\" do\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.com\", openssl_create_cert(san: \"DNS:example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.org\", openssl_create_cert(san: \"DNS:example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchNotFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"foo.example.com\", openssl_create_cert(san: \"DNS:*.example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n    end\n\n    it \"verifies all SAN entries\" do\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.com\", openssl_create_cert(san: \"DNS:example.com,DNS:example.org\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"10.0.3.1\", openssl_create_cert(san: \"IP:192.168.1.1,IP:10.0.3.1\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.com\", openssl_create_cert(san: \"IP:192.168.1.1,DNS:example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n    end\n\n    it \"falls back to CN entry (unless SAN entry is defined)\" do\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.com\", openssl_create_cert(subject: \"CN=example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.com\", openssl_create_cert(san: \"DNS:example.org\", subject: \"CN=example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchNotFound)\n      OpenSSL::SSL::HostnameValidation.validate_hostname(\"example.org\", openssl_create_cert(san: \"DNS:example.org\", subject: \"CN=example.com\")).should eq(OpenSSL::SSL::HostnameValidation::Result::MatchFound)\n    end\n  end\n\n  describe \"matches_hostname?\" do\n    it \"skips trailing dot\" do\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"example.com.\", \"example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"example.com\", \"example.com.\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\".example.com\", \"example.com\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"example.com\", \".example.com\").should be_false\n    end\n\n    it \"normalizes case\" do\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"exAMPLE.cOM\", \"EXample.Com\").should be_true\n    end\n\n    it \"literal matches\" do\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"example.com\", \"example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"example.com\", \"www.example.com\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"www.example.com\", \"www.example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"foo.bar.example.com\", \"bar.example.com\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"foo.bar.example.com\", \"foo.bar.example.com\").should be_true\n    end\n\n    it \"wildcard matches according to RFC 6125, section 6.4.3\" do\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.com\", \"example.com\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"bar.*.example.com\", \"bar.foo.example.com\").should be_false\n\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.example.com\", \"foo.example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.example.com\", \"foo.example.org\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.example.com\", \"bar.foo.example.com\").should be_false\n\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"baz*.example.com\", \"baz1.example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"baz*.example.com\", \"baz.example.com\").should be_false\n\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*baz.example.com\", \"foobaz.example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*baz.example.com\", \"baz.example.com\").should be_false\n\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"b*z.example.com\", \"buzz.example.com\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"b*z.example.com\", \"bz.example.com\").should be_false\n\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"192.168.0.1\", \"192.168.0.1\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.168.0.1\", \"192.168.0.1\").should be_false\n    end\n\n    it \"matches IDNA label\" do\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.example.org\", \"xn--kcry6tjko.example.org\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"*.xn--kcry6tjko.example.org\", \"foo.xn--kcry6tjko.example.org\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"xn--*.example.org\", \"xn--kcry6tjko.example.org\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\"xn--kcry6tjko*.example.org\", \"xn--kcry6tjkofoo.example.org\").should be_false\n    end\n\n    it \"matches leading dot\" do\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\".example.org\", \"example.org\").should be_false\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\".example.org\", \"xn--kcry6tjko.example.org\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\".example.org\", \"foo.example.org\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\".example.org\", \"foo.bar.example.org\").should be_true\n      OpenSSL::SSL::HostnameValidation.matches_hostname?(\".example.org\", \"foo.example.com\").should be_false\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/ssl/server_spec.cr",
    "content": "require \"spec\"\nrequire \"socket\"\nrequire \"../../spec_helper\"\nrequire \"../../../support/ssl\"\n\ndescribe OpenSSL::SSL::Server do\n  it \"sync_close\" do\n    TCPServer.open(Socket::IPAddress::UNSPECIFIED, 0) do |tcp_server|\n      context = OpenSSL::SSL::Context::Server.new\n      ssl_server = OpenSSL::SSL::Server.new(tcp_server, context)\n\n      ssl_server.close\n\n      tcp_server.closed?.should be_true\n    end\n  end\n\n  it \"don't sync_close\" do\n    TCPServer.open(Socket::IPAddress::UNSPECIFIED, 0) do |tcp_server|\n      context = OpenSSL::SSL::Context::Server.new\n      ssl_server = OpenSSL::SSL::Server.new(tcp_server, context, sync_close: false)\n      ssl_server.context.should eq context\n\n      ssl_server.close\n\n      tcp_server.closed?.should be_false\n    end\n  end\n\n  it \".new\" do\n    context = OpenSSL::SSL::Context::Server.new\n    TCPServer.open(Socket::IPAddress::UNSPECIFIED, 0) do |tcp_server|\n      ssl_server = OpenSSL::SSL::Server.new tcp_server, context, sync_close: false\n\n      ssl_server.context.should eq context\n      ssl_server.wrapped.should eq tcp_server\n      ssl_server.sync_close?.should be_false\n    end\n  end\n\n  it \".open\" do\n    context = OpenSSL::SSL::Context::Server.new\n    TCPServer.open(Socket::IPAddress::UNSPECIFIED, 0) do |tcp_server|\n      ssl_server = nil\n      OpenSSL::SSL::Server.open tcp_server, context do |server|\n        server.wrapped.should eq tcp_server\n        ssl_server = server\n      end\n\n      ssl_server.try(&.closed?).should be_true\n      tcp_server.closed?.should be_true\n    end\n  end\n\n  describe \"#accept?\" do\n    it \"accepts\" do\n      tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n\n      server_context, client_context = ssl_context_pair\n\n      OpenSSL::SSL::Server.open tcp_server, server_context do |server|\n        spawn do\n          client = server.accept?\n          client.should be_a(OpenSSL::SSL::Socket::Server)\n          client = client.not_nil!\n          client.gets.should eq \"Hello, SSL!\"\n          client.puts \"Hello back, SSL!\"\n          client.close\n        end\n\n        OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context) do |socket|\n          socket.puts \"Hello, SSL!\"\n          socket.flush\n          socket.gets.should eq \"Hello back, SSL!\"\n        end\n      end\n    end\n  end\n\n  describe \"#accept\" do\n    it \"accepts and do handshake\" do\n      tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n\n      server_context, client_context = ssl_context_pair\n\n      OpenSSL::SSL::Server.open tcp_server, server_context do |server|\n        spawn do\n          client = server.accept\n          client.gets.should eq \"Hello, SSL!\"\n          client.puts \"Hello back, SSL!\"\n          client.close\n        end\n\n        OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context) do |socket|\n          socket.puts \"Hello, SSL!\"\n          socket.flush\n          socket.gets.should eq \"Hello back, SSL!\"\n        end\n      end\n    end\n\n    it \"doesn't to SSL handshake with start_immediately = false\" do\n      tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n\n      server_context, client_context = ssl_context_pair\n\n      OpenSSL::SSL::Server.open tcp_server, server_context do |server|\n        server.start_immediately = false\n\n        spawn do\n          client = server.accept\n          client.accept\n          client.gets.should eq \"Hello, SSL!\"\n          client.puts \"Hello back, SSL!\"\n          client.close\n        end\n\n        OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context) do |socket|\n          socket.puts \"Hello, SSL!\"\n          socket.flush\n          socket.gets.should eq \"Hello back, SSL!\"\n        end\n      end\n    end\n  end\n\n  it \"detects SNI hostname\" do\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n\n    OpenSSL::SSL::Server.open tcp_server, server_context do |server|\n      spawn do\n        OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"example.com\") do |socket|\n        end\n      end\n\n      client = server.accept\n      client.hostname.should eq(\"example.com\")\n      client.close\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/ssl/socket_spec.cr",
    "content": "require \"spec\"\nrequire \"socket\"\nrequire \"../../spec_helper\"\nrequire \"../../socket/spec_helper\"\nrequire \"../../../support/ssl\"\n\ndescribe OpenSSL::SSL::Socket do\n  describe OpenSSL::SSL::Socket::Server do\n    it \"auto accept client by default\" do\n      TCPServer.open(\"127.0.0.1\", 0) do |tcp_server|\n        server_context, client_context = ssl_context_pair\n\n        spawn do\n          OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"example.com\") do |socket|\n            socket.print \"hello\"\n          end\n        end\n\n        socket = tcp_server.accept\n        ssl_server = OpenSSL::SSL::Socket::Server.new(socket, server_context)\n        ssl_server.gets.should eq(\"hello\")\n        ssl_server.close\n      end\n    end\n\n    it \"doesn't accept client when specified\" do\n      TCPServer.open(\"127.0.0.1\", 0) do |tcp_server|\n        server_context, client_context = ssl_context_pair\n\n        spawn do\n          OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"example.com\") do |socket|\n            socket.print \"hello\"\n          end\n        end\n\n        socket = tcp_server.accept\n        ssl_server = OpenSSL::SSL::Socket::Server.new(socket, server_context, accept: false)\n        ssl_server.accept\n        ssl_server.gets.should eq(\"hello\")\n        ssl_server.close\n      end\n    end\n  end\nend\n\nprivate alias Server = OpenSSL::SSL::Socket::Server\nprivate alias Client = OpenSSL::SSL::Socket::Client\n\nprivate def socket_test(server_tests, client_tests)\n  tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n  server_context, client_context = ssl_context_pair\n\n  OpenSSL::SSL::Server.open(tcp_server, server_context) do |server|\n    spawn do\n      Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"example.com\") do |socket|\n        client_tests.call(socket)\n      end\n    end\n\n    client = server.accept\n    server_tests.call(client)\n    client.close\n  end\nend\n\ndescribe OpenSSL::SSL::Socket do\n  it \"returns the cipher that is currently in use\" do\n    socket_test(\n      server_tests: ->(client : Server) {\n        client.cipher.should_not be_empty\n      },\n      client_tests: ->(client : Client) { }\n    )\n  end\n\n  it \"returns the TLS version\" do\n    socket_test(\n      server_tests: ->(client : Server) {\n        client.tls_version.should contain \"TLS\"\n      },\n      client_tests: ->(client : Client) { }\n    )\n  end\n\n  it \"returns the peer certificate\" do\n    socket_test(\n      server_tests: ->(client : Server) {\n        client.peer_certificate.should be_nil\n      },\n      client_tests: ->(client : Client) {\n        client.peer_certificate.should_not be_nil\n      }\n    )\n  end\n\n  it \"returns selected alpn protocol\" do\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n\n    server_context.alpn_protocol = \"h2\"\n    client_context.alpn_protocol = \"h2\"\n\n    OpenSSL::SSL::Server.open(tcp_server, server_context) do |server|\n      spawn do\n        Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"example.com\") do |socket|\n          socket.alpn_protocol.should eq(\"h2\")\n        end\n      end\n\n      client = server.accept\n      client.alpn_protocol.should eq(\"h2\")\n      client.close\n    end\n  end\n\n  it \"accepts clients that only write then close the connection\" do\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n    # in tls 1.3, if clients don't read anything and close the connection\n    # the server still try and write to it a ticket, resulting in a \"pipe failure\"\n    # this context method disables the tickets which allows the behavior:\n    server_context.disable_session_resume_tickets\n\n    OpenSSL::SSL::Server.open(tcp_server, server_context) do |server|\n      spawn do\n        # the :sync_close aspect, as implemented in crystal, effects a unidirectional socket close from the client\n        OpenSSL::SSL::Socket::Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"example.com\", sync_close: true) do |socket|\n          # doesn't read anything, just write and close connection immediately\n          socket.puts \"hello\"\n        end\n      end\n\n      client = server.accept # shouldn't raise \"Broken pipe (Errno)\"\n      client.close\n    end\n  end\n\n  it \"closes connection to server that doesn't properly terminate SSL session\" do\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n    server_context.disable_session_resume_tickets # avoid Broken pipe\n\n    client_successfully_closed_socket = Channel(Nil).new\n    spawn do\n      OpenSSL::SSL::Server.open(tcp_server, server_context, sync_close: true) do |server|\n        server_client = server.accept\n        # require client to close the socket from its side, without the server closing it, IIS behave this way.\n        client_successfully_closed_socket.receive\n        server_client.close\n      end\n    end\n    socket = TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port)\n    socket = OpenSSL::SSL::Socket::Client.new(socket, client_context, hostname: \"example.com\", sync_close: true)\n    socket.close\n    client_successfully_closed_socket.send(nil)\n  end\n\n  it \"interprets graceful EOF of underlying socket as SSL termination\" do\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n    server_context.disable_session_resume_tickets # avoid Broken pipe\n\n    server_finished_reading = Channel(String | Exception).new\n    spawn do\n      OpenSSL::SSL::Server.open(tcp_server, server_context, sync_close: true) do |server|\n        server_socket = server.accept\n        received = server_socket.gets_to_end # interprets underlying socket close as a graceful EOF\n        server_finished_reading.send(received)\n      end\n    rescue exc\n      server_finished_reading.send exc\n    end\n\n    socket = TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port)\n    socket_ssl = OpenSSL::SSL::Socket::Client.new(socket, client_context, hostname: \"example.com\", sync_close: true)\n    socket_ssl.print \"hello\"\n    socket_ssl.flush # needed today see #5375\n    socket.close     # close underlying socket without gracefully shutting down SSL at all\n    server_received = server_finished_reading.receive\n    if server_received.is_a?(Exception)\n      raise server_received\n    end\n    server_received.should eq(\"hello\")\n  end\n\n  it \"calls on_server_name callback with client hostname\" do\n    pending_interpreted!(\"Leads to invalid memory access\")\n\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n\n    sni_hostname = nil\n\n    server_context.on_server_name do |hostname|\n      sni_hostname = hostname\n      nil # continue with default context\n    end\n\n    spawn do\n      Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"test.example.com\") do |socket|\n        socket.print \"hello\"\n      end\n    end\n\n    OpenSSL::SSL::Server.open(tcp_server, server_context) do |server|\n      client = server.accept\n      client.gets.should eq(\"hello\")\n      client.close\n    end\n\n    sni_hostname.should eq(\"test.example.com\")\n  end\n\n  it \"handles exception in on_server_name callback\" do\n    pending_interpreted!(\"Leads to invalid memory access\")\n\n    tcp_server = TCPServer.new(\"127.0.0.1\", 0)\n    server_context, client_context = ssl_context_pair\n\n    server_context.on_server_name do |hostname|\n      raise \"test error\"\n    end\n\n    client_error = Channel(Exception?).new\n\n    spawn do\n      begin\n        Client.open(TCPSocket.new(tcp_server.local_address.address, tcp_server.local_address.port), client_context, hostname: \"test.example.com\") do |socket|\n          socket.print \"hello\"\n        end\n        client_error.send(nil)\n      rescue ex\n        client_error.send(ex)\n      end\n    end\n\n    OpenSSL::SSL::Server.open(tcp_server, server_context) do |server|\n      expect_raises(OpenSSL::SSL::Error) do\n        server.accept\n      end\n    end\n\n    client_error.receive.should be_a(OpenSSL::SSL::Error)\n  end\nend\n"
  },
  {
    "path": "spec/std/openssl/x509/certificate_spec.cr",
    "content": "require \"spec\"\nrequire \"openssl\"\n\ndescribe OpenSSL::X509::Certificate do\n  it \"subject\" do\n    cert = OpenSSL::X509::Certificate.new\n    cert.subject = \"CN=Nobody/DC=example\"\n    cert.subject.to_a.should eq([{\"CN\", \"Nobody\"}, {\"DC\", \"example\"}])\n  end\n\n  it \"extension\" do\n    cert = OpenSSL::X509::Certificate.new\n\n    cert.add_extension OpenSSL::X509::Extension.new(\"subjectAltName\", \"IP:127.0.0.1\")\n    cert.extensions.map(&.oid).should eq [\"subjectAltName\"]\n    cert.extensions.map(&.value).should eq [\"IP Address:127.0.0.1\"]\n\n    cert.add_extension OpenSSL::X509::Extension.new(\"subjectAltName\", \"DNS:localhost.localdomain\")\n    cert.extensions.map(&.oid).should eq [\"subjectAltName\", \"subjectAltName\"]\n    cert.extensions.map(&.value).should eq [\"IP Address:127.0.0.1\", \"DNS:localhost.localdomain\"]\n  end\n\n  it \"#signature_algorithm\" do\n    cert = OpenSSL::X509::Certificate.new\n\n    expect_raises(Exception, \"Could not determine certificate signature algorithm\") do\n      cert.signature_algorithm\n    end\n  end\n\n  # Starting with 3.0.0 OpenSSL complains about generating a digest signature for an empty certificate\n  {% if compare_versions(LibSSL::OPENSSL_VERSION, \"3.0.0\") < 0 %}\n    it \"#digest\" do\n      cert = OpenSSL::X509::Certificate.new\n      expect_raises(ArgumentError, %(Could not find digest for \"not a real algo\")) { cert.digest(\"not a real algo\") }\n      cert.digest(\"SHA256\").should_not be_nil\n    end\n  {% else %}\n    pending \"#digest\"\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/openssl/x509/name_spec.cr",
    "content": "require \"spec\"\nrequire \"openssl\"\n\ndescribe \"OpenSSL::X509::Name\" do\n  it \"parse\" do\n    name = OpenSSL::X509::Name.parse(\"CN=nobody/DC=example\")\n    name.to_a.should eq([{\"CN\", \"nobody\"}, {\"DC\", \"example\"}])\n\n    expect_raises(OpenSSL::Error) do\n      OpenSSL::X509::Name.parse(\"CN=nobody/Unknown=Value\")\n    end\n  end\n\n  it \"add_entry\" do\n    name = OpenSSL::X509::Name.new\n    name.to_a.size.should eq(0)\n\n    name.add_entry \"CN\", \"Nobody\"\n    name.to_a.should eq([{\"CN\", \"Nobody\"}])\n\n    name.add_entry \"DC\", \"Example\"\n    name.to_a.should eq([{\"CN\", \"Nobody\"}, {\"DC\", \"Example\"}])\n\n    expect_raises(OpenSSL::Error) { name.add_entry \"UNKNOWN\", \"Value\" }\n  end\nend\n"
  },
  {
    "path": "spec/std/option_parser_spec.cr",
    "content": "require \"spec\"\nrequire \"option_parser\"\n\nprivate def expect_capture_option(args, option, value)\n  flag = nil\n  OptionParser.parse(args) do |opts|\n    opts.on(option, \"some flag\") do |flag_value|\n      flag = flag_value\n    end\n  end\n  flag.should eq(value)\n  args.size.should eq(0)\nend\n\nprivate def expect_doesnt_capture_option(args, option)\n  flag = false\n  OptionParser.parse(args) do |opts|\n    opts.on(option, \"some flag\") do\n      flag = true\n    end\n  end\n  flag.should be_false\nend\n\nprivate def expect_missing_option(option)\n  expect_raises OptionParser::MissingOption do\n    OptionParser.parse([] of String) do |opts|\n      opts.on(option, \"some flag\") do |flag_value|\n      end\n    end\n  end\nend\n\nprivate def expect_missing_option(args, option, flag)\n  expect_raises OptionParser::MissingOption, \"Missing option: #{flag}\" do\n    OptionParser.parse(args) do |opts|\n      opts.on(option, \"some flag\") do |flag_value|\n      end\n    end\n  end\nend\n\ndescribe \"OptionParser\" do\n  it \"has flag\" do\n    expect_capture_option [\"-f\"], \"-f\", \"\"\n  end\n\n  it \"has flag with many letters\" do\n    expect_capture_option [\"-ll\"], \"-ll\", \"l\"\n  end\n\n  it \"doesn't have flag\" do\n    expect_doesnt_capture_option [] of String, \"-f\"\n  end\n\n  it \"has flag with double dash\" do\n    expect_capture_option [\"--flag\"], \"--flag\", \"\"\n  end\n\n  it \"doesn't have flag with double dash\" do\n    expect_doesnt_capture_option [] of String, \"--flag\"\n  end\n\n  it \"has required option next to flag\" do\n    expect_capture_option [\"-f123\"], \"-fFLAG\", \"123\"\n  end\n\n  it \"has required option next to flag but given separated\" do\n    expect_capture_option [\"-f\", \"123\"], \"-fFLAG\", \"123\"\n  end\n\n  it \"raises if missing option next to flag\" do\n    expect_missing_option [\"-f\"], \"-fFLAG\", \"-f\"\n  end\n\n  it \"has required option separated from flag\" do\n    expect_capture_option [\"-f\", \"123\"], \"-f FLAG\", \"123\"\n  end\n\n  it \"has required option separated from flag but given together\" do\n    expect_capture_option [\"-f123\"], \"-f FLAG\", \"123\"\n  end\n\n  it \"gets short option with value that looks like flag\" do\n    expect_capture_option [\"-f\", \"-g -h\"], \"-f FLAG\", \"-g -h\"\n  end\n\n  describe \"Consumption of flags following an ungiven optional argument\" do\n    context \"Given a short option with an optional value\" do\n      it \"doesn't eat a following short option\" do\n        flag = nil\n        not_eaten = [] of String\n        args = [\"-f\", \"-g\", \"-i\"]\n        OptionParser.parse(args) do |opts|\n          opts.on(\"-f [FLAG]\", \"some flag\") do |flag_value|\n            flag = flag_value\n          end\n          opts.on(\"-g\", \"shouldn't be eaten\") do\n            not_eaten << \"-g\"\n          end\n          opts.on(\"-i\", \"shouldn't be eaten\") do\n            not_eaten << \"-i\"\n          end\n        end\n        flag.should eq(\"\")\n        not_eaten.should eq([\"-g\", \"-i\"])\n        args.size.should eq(0)\n      end\n\n      it \"doesn't eat a following long option\" do\n        flag = nil\n        not_eaten = [] of String\n        args = [\"-f\", \"--g-long\", \"-i\"]\n        OptionParser.parse(args) do |opts|\n          opts.on(\"-f [FLAG]\", \"some flag\") do |flag_value|\n            flag = flag_value\n          end\n          opts.on(\"-g\", \"--g-long\", \"shouldn't be eaten\") do\n            not_eaten << \"--g-long\"\n          end\n          opts.on(\"-i\", \"shouldn't be eaten\") do\n            not_eaten << \"-i\"\n          end\n        end\n        flag.should eq(\"\")\n        not_eaten.should eq([\"--g-long\", \"-i\"])\n        args.size.should eq(0)\n      end\n\n      it \"does eat a value that looks like an option\" do\n        flag = nil\n        not_eaten = [] of String\n        args = [\"-f\", \"--not-an-option--\", \"-i\"]\n        OptionParser.parse(args) do |opts|\n          opts.on(\"-f [FLAG]\", \"some flag\") do |flag_value|\n            flag = flag_value\n          end\n          opts.on(\"-i\", \"shouldn't be eaten\") do\n            not_eaten << \"-i\"\n          end\n        end\n        flag.should eq(\"--not-an-option--\")\n        not_eaten.should eq([\"-i\"])\n        args.size.should eq(0)\n      end\n    end\n\n    context \"Given a long option with an optional value\" do\n      it \"doesn't eat further short options\" do\n        flag = nil\n        not_eaten = [] of String\n        args = [\"--long-flag\", \"-g\", \"-i\"]\n        OptionParser.parse(args) do |opts|\n          opts.on(\"--long-flag [FLAG]\", \"some flag\") do |flag_value|\n            flag = flag_value\n          end\n          opts.on(\"-g\", \"shouldn't be eaten\") do\n            not_eaten << \"-g\"\n          end\n          opts.on(\"-i\", \"shouldn't be eaten\") do\n            not_eaten << \"-i\"\n          end\n        end\n        flag.should eq(\"\")\n        not_eaten.should eq([\"-g\", \"-i\"])\n        args.size.should eq(0)\n      end\n\n      it \"doesn't eat further long options\" do\n        flag = nil\n        not_eaten = [] of String\n        args = [\"--long-flag\", \"--g-long\", \"-i\"]\n        OptionParser.parse(args) do |opts|\n          opts.on(\"--long-flag [FLAG]\", \"some flag\") do |flag_value|\n            flag = flag_value\n          end\n          opts.on(\"--g-long\", \"shouldn't be eaten\") do\n            not_eaten << \"--g-long\"\n          end\n          opts.on(\"-i\", \"shouldn't be eaten\") do\n            not_eaten << \"-i\"\n          end\n        end\n        flag.should eq(\"\")\n        not_eaten.should eq([\"--g-long\", \"-i\"])\n        args.size.should eq(0)\n      end\n    end\n  end\n\n  it \"raises if missing required option with space\" do\n    expect_missing_option [\"-f\"], \"-f FLAG\", \"-f\"\n  end\n\n  it \"has required option separated from long flag\" do\n    expect_capture_option [\"--flag\", \"123\"], \"--flag FLAG\", \"123\"\n  end\n\n  it \"has required option with =\" do\n    expect_capture_option [\"--flag=123\"], \"--flag FLAG\", \"123\"\n  end\n\n  it \"has required option with = (2)\" do\n    expect_capture_option [\"--flag=123\"], \"--flag=FLAG\", \"123\"\n  end\n\n  it \"has required option with = (3) handles empty\" do\n    expect_capture_option [\"--flag=\"], \"--flag=FLAG\", \"\"\n  end\n\n  it \"raises if missing required argument separated from long flag\" do\n    expect_missing_option [\"--flag\"], \"--flag FLAG\", \"--flag\"\n  end\n\n  it \"has required option with space\" do\n    expect_capture_option [\"-f\", \"123\"], \"-f \", \"123\"\n  end\n\n  it \"has required option with long flag space\" do\n    expect_capture_option [\"--flag\", \"123\"], \"--flag \", \"123\"\n  end\n\n  it \"gets short option with value -- (#8937)\" do\n    expect_capture_option [\"-f\", \"--\"], \"-f ARG\", \"--\"\n  end\n\n  it \"gets long option with value -- (#8937)\" do\n    expect_capture_option [\"--flag\", \"--\"], \"--flag [ARG]\", \"--\"\n  end\n\n  it \"doesn't raise if required option is not specified\" do\n    expect_doesnt_capture_option [] of String, \"-f \"\n  end\n\n  it \"doesn't raise if optional option is not specified with short flag\" do\n    expect_doesnt_capture_option [] of String, \"-f[FLAG]\"\n  end\n\n  it \"doesn't raise if optional option is not specified with long flag\" do\n    expect_doesnt_capture_option [] of String, \"--flag [FLAG]\"\n  end\n\n  it \"doesn't raise if optional option is not specified with separated short flag\" do\n    expect_doesnt_capture_option [] of String, \"-f [FLAG]\"\n  end\n\n  it \"doesn't raise if required option is not specified with separated short flag\" do\n    expect_doesnt_capture_option [] of String, \"-f FLAG\"\n  end\n\n  describe \"gnu_optional_args\" do\n    it \"doesn't get optional argument for short flag after space\" do\n      flag = nil\n      args = %w(-f 123)\n      OptionParser.parse(args, gnu_optional_args: true) do |opts|\n        opts.on(\"-f [FLAG]\", \"some flag\") do |flag_value|\n          flag = flag_value\n        end\n      end\n      flag.should eq(\"\")\n      args.should eq(%w(123))\n    end\n\n    it \"doesn't get optional argument for long flag after space\" do\n      flag = nil\n      args = %w(--f 123)\n      OptionParser.parse(args, gnu_optional_args: true) do |opts|\n        opts.on(\"--f [FLAG]\", \"some flag\") do |flag_value|\n          flag = flag_value\n        end\n      end\n      flag.should eq(\"\")\n      args.should eq(%w(123))\n    end\n  end\n\n  it \"parses argument when only referenced in long flag\" do\n    captured = \"\"\n    parser = OptionParser.parse([] of String) do |opts|\n      opts.on(\"-f\", \"--flag X\", \"some flag\") { |x| captured = x }\n    end\n    parser.parse([\"-f\", \"12\"])\n    captured.should eq \"12\"\n    parser.to_s.should contain \"   -f, --flag X\"\n  end\n\n  it \"parses argument when referenced in long and short flag\" do\n    captured = \"\"\n    parser = OptionParser.parse([] of String) do |opts|\n      opts.on(\"-f X\", \"--flag X\", \"some flag\") { |x| captured = x }\n    end\n    parser.parse([\"-f\", \"12\"])\n    captured.should eq \"12\"\n    parser.to_s.should contain \"   -f X, --flag X\"\n  end\n\n  it \"does to_s with banner\" do\n    parser = OptionParser.parse([] of String) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.on(\"-f\", \"--flag\", \"some flag\") do\n      end\n      opts.on(\"-g[FLAG]\", \"some other flag\") do\n      end\n    end\n    parser.to_s.should eq <<-USAGE\n      Usage: foo\n          -f, --flag                       some flag\n          -g[FLAG]                         some other flag\n      USAGE\n  end\n\n  it \"does to_s with separators\" do\n    parser = OptionParser.parse([] of String) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.separator\n      opts.separator \"Type F flags:\"\n      opts.on(\"-f\", \"--flag\", \"some flag\") do\n      end\n      opts.separator\n      opts.separator \"Type G flags:\"\n      opts.on(\"-g[FLAG]\", \"some other flag\") do\n      end\n    end\n    parser.to_s.should eq <<-USAGE\n      Usage: foo\n\n      Type F flags:\n          -f, --flag                       some flag\n\n      Type G flags:\n          -g[FLAG]                         some other flag\n      USAGE\n  end\n\n  it \"does to_s with very long flag (#3305)\" do\n    parser = OptionParser.parse([] of String) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.on(\"--very_long_option_kills=formatter\", \"long\") do\n      end\n      opts.on(\"-f\", \"--flag\", \"some flag\") do\n      end\n      opts.on(\"-g[FLAG]\", \"some other flag\") do\n      end\n    end\n    parser.to_s.should eq <<-USAGE\n      Usage: foo\n          --very_long_option_kills=formatter\n                                           long\n          -f, --flag                       some flag\n          -g[FLAG]                         some other flag\n      USAGE\n  end\n\n  it \"does to_s with multi line description (#5832)\" do\n    parser = OptionParser.parse([] of String) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.on(\"--very_long_option_kills=formatter\", \"long flag with\\nmultiline description\") do\n      end\n      opts.on(\"-f\", \"--flag\", \"some flag with\\nmultiline description\") do\n      end\n      opts.on(\"-g[FLAG]\", \"some other flag\") do\n      end\n    end\n    parser.to_s.should eq <<-USAGE\n      Usage: foo\n          --very_long_option_kills=formatter\n                                           long flag with\n                                           multiline description\n          -f, --flag                       some flag with\n                                           multiline description\n          -g[FLAG]                         some other flag\n      USAGE\n  end\n\n  it \"raises on invalid option\" do\n    expect_raises OptionParser::InvalidOption, \"Invalid option: -j\" do\n      OptionParser.parse([\"-f\", \"-j\"]) do |opts|\n        opts.on(\"-f\", \"some flag\") { }\n      end\n    end\n  end\n\n  it \"raises on invalid option if value is given to none value handler (short flag, #9553) \" do\n    expect_raises OptionParser::InvalidOption, \"Invalid option: -foo\" do\n      OptionParser.parse([\"-foo\"]) do |opts|\n        opts.on(\"-f\", \"some flag\") { }\n      end\n    end\n  end\n\n  it \"raises on invalid option if value is given to none value handler (long flag, #9553)\" do\n    expect_raises OptionParser::InvalidOption, \"Invalid option: --foo=bar\" do\n      OptionParser.parse([\"--foo=bar\"]) do |opts|\n        opts.on(\"-foo\", \"some flag\") { }\n      end\n    end\n  end\n\n  it \"calls the handler for invalid options\" do\n    called = false\n    OptionParser.parse([\"-f\", \"-j\"]) do |opts|\n      opts.on(\"-f\", \"some flag\") { }\n      opts.invalid_option do |flag|\n        flag.should eq(\"-j\")\n        called = true\n      end\n    end\n\n    called.should be_true\n  end\n\n  it \"calls the handler for missing options\" do\n    called = false\n    OptionParser.parse([\"-f\"]) do |opts|\n      opts.on(\"-f FOO\", \"some flag\") { }\n      opts.missing_option do |flag|\n        flag.should eq(\"-f\")\n        called = true\n      end\n    end\n\n    called.should be_true\n  end\n\n  describe \"multiple times\" do\n    it \"gets an existence flag multiple times\" do\n      args = %w(-f -f -f)\n      count = 0\n      OptionParser.parse(args) do |opts|\n        opts.on(\"-f\", \"some flag\") do\n          count += 1\n        end\n      end\n      count.should eq(3)\n    end\n\n    it \"gets a single flag option multiple times\" do\n      args = %w(-f 1 -f 2)\n      values = [] of String\n      OptionParser.parse(args) do |opts|\n        opts.on(\"-f VALUE\", \"some flag\") do |value|\n          values << value\n        end\n      end\n      values.should eq(%w(1 2))\n    end\n\n    it \"gets a double flag option multiple times\" do\n      args = %w(--f 1 --f 2)\n      values = [] of String\n      OptionParser.parse(args) do |opts|\n        opts.on(\"--f VALUE\", \"some flag\") do |value|\n          values << value\n        end\n      end\n      values.should eq(%w(1 2))\n    end\n  end\n\n  describe \"--\" do\n    it \"ignores everything after -- with bool flag\" do\n      args = [\"-f\", \"bar\", \"--\", \"baz\", \"qux\", \"-g\"]\n      f = false\n      g = false\n      OptionParser.parse(args) do |opts|\n        opts.on(\"-f\", \"some flag\") do\n          f = true\n        end\n        opts.on(\"-g\", \"some flag\") do\n          g = true\n        end\n      end\n      f.should be_true\n      g.should be_false\n      args.should eq([\"bar\", \"baz\", \"qux\", \"-g\"])\n    end\n\n    it \"ignores everything after -- with single flag)\" do\n      args = [\"-f\", \"bar\", \"x\", \"--\", \"baz\", \"qux\", \"-g\", \"lala\"]\n      f = nil\n      g = nil\n      OptionParser.parse(args) do |opts|\n        opts.on(\"-f FLAG\", \"some flag\") do |v|\n          f = v\n        end\n        opts.on(\"-g FLAG\", \"some flag\") do |v|\n          g = v\n        end\n      end\n      f.should eq(\"bar\")\n      g.should be_nil\n      args.should eq([\"x\", \"baz\", \"qux\", \"-g\", \"lala\"])\n    end\n\n    it \"ignores everything after -- with double flag\" do\n      args = [\"--f\", \"bar\", \"x\", \"--\", \"baz\", \"qux\", \"--g\", \"lala\"]\n      f = nil\n      g = nil\n      OptionParser.parse(args) do |opts|\n        opts.on(\"--f FLAG\", \"some flag\") do |v|\n          f = v\n        end\n        opts.on(\"--g FLAG\", \"some flag\") do |v|\n          g = v\n        end\n      end\n      f.should eq(\"bar\")\n      g.should be_nil\n      args.should eq([\"x\", \"baz\", \"qux\", \"--g\", \"lala\"])\n    end\n\n    it \"returns a pair with things coming before and after --\" do\n      args = %w(--f bar baz -- qux)\n      f = nil\n      unknown_args = nil\n      OptionParser.parse(args) do |opts|\n        opts.on(\"--f FLAG\", \"some flag\") do |v|\n          f = v\n        end\n        opts.unknown_args do |before_dash, after_dash|\n          unknown_args = {before_dash, after_dash}\n        end\n      end\n      f.should eq(\"bar\")\n      args.should eq([\"baz\", \"qux\"])\n      unknown_args.should eq({[\"baz\"], [\"qux\"]})\n    end\n\n    it \"returns a pair with things coming before and after --, without --\" do\n      args = %w(--f bar baz)\n      f = nil\n      unknown_args = nil\n      OptionParser.parse(args) do |opts|\n        opts.on(\"--f FLAG\", \"some flag\") do |v|\n          f = v\n        end\n        opts.unknown_args do |before_dash, after_dash|\n          unknown_args = {before_dash, after_dash}\n        end\n      end\n      f.should eq(\"bar\")\n      args.should eq([\"baz\"])\n      unknown_args.should eq({[\"baz\"], [] of String})\n    end\n\n    it \"initializes without block and does parse!\" do\n      old_argv = ARGV.dup\n      begin\n        ARGV.clear\n        ARGV.concat %w(--f hi)\n        f = nil\n        OptionParser.new do |opts|\n          opts.on(\"--f FLAG\", \"some flag\") do |v|\n            f = v\n          end\n        end.parse\n        f.should eq(\"hi\")\n      ensure\n        ARGV.clear\n        ARGV.concat old_argv\n      end\n    end\n\n    it \"gets `-` as argument\" do\n      args = %w(-)\n      OptionParser.parse(args) do |opts|\n      end\n      args.should eq(%w(-))\n    end\n  end\n\n  describe \"forward-match\" do\n    it \"distinguishes between '--lamb VALUE' and '--lambda VALUE'\" do\n      args = %w(--lamb value1 --lambda value2)\n      value1 = nil\n      value2 = nil\n      OptionParser.parse(args) do |opts|\n        opts.on(\"--lamb VALUE\", \"\") { |v| value1 = v }\n        opts.on(\"--lambda VALUE\", \"\") { |v| value2 = v }\n      end\n      value1.should eq(\"value1\")\n      value2.should eq(\"value2\")\n    end\n\n    it \"distinguishes between '--lamb=VALUE' and '--lambda=VALUE'\" do\n      args = %w(--lamb=value1 --lambda=value2)\n      value1 = nil\n      value2 = nil\n      OptionParser.parse(args) do |opts|\n        opts.on(\"--lamb=VALUE\", \"\") { |v| value1 = v }\n        opts.on(\"--lambda=VALUE\", \"\") { |v| value2 = v }\n      end\n      value1.should eq(\"value1\")\n      value2.should eq(\"value2\")\n    end\n  end\n\n  it \"raises if flag pair doesn't start with dash (#4001)\" do\n    OptionParser.parse([] of String) do |opts|\n      expect_raises ArgumentError, %(Argument 'short_flag' (\"foo\") must start with a dash) do\n        opts.on(\"foo\", \"bar\", \"baz\") { }\n      end\n\n      expect_raises ArgumentError, %(Argument 'long_flag' (\"bar\") must start with a dash) do\n        opts.on(\"-foo\", \"bar\", \"baz\") { }\n      end\n\n      opts.on(\"\", \"-bar\", \"baz\") { }\n    end\n  end\n\n  it \"handles subcommands\" do\n    args = %w(--verbose subcommand --foo 1 --bar sub2 -z)\n    verbose = false\n    subcommand = false\n    foo = nil\n    bar = false\n    sub2 = false\n    z = false\n    OptionParser.parse(args) do |opts|\n      opts.on(\"subcommand\", \"\") do\n        subcommand = true\n        opts.on(\"--foo arg\", \"\") { |v| foo = v }\n        opts.on(\"--bar\", \"\") { bar = true }\n        opts.on(\"sub2\", \"\") { sub2 = true }\n      end\n      opts.on(\"--verbose\", \"\") { verbose = true }\n      opts.on(\"-z\", \"--baz\", \"\") { z = true }\n    end\n\n    verbose.should be_true\n    subcommand.should be_true\n    foo.should be(\"1\")\n    bar.should be_true\n    sub2.should be_true\n    z.should be_true\n  end\n\n  it \"parses with subcommands twice\" do\n    args = %w(--verbose subcommand --foo 1 --bar sub2 -z)\n    verbose = false\n    subcommand = false\n    foo = nil\n    bar = false\n    sub2 = false\n    z = false\n\n    parser = OptionParser.new do |opts|\n      opts.on(\"subcommand\", \"\") do\n        subcommand = true\n        opts.on(\"--foo arg\", \"\") { |v| foo = v }\n        opts.on(\"--bar\", \"\") { bar = true }\n        opts.on(\"sub2\", \"\") { sub2 = true }\n      end\n      opts.on(\"--verbose\", \"\") { verbose = true }\n      opts.on(\"-z\", \"--baz\", \"\") { z = true }\n    end\n\n    parser.parse args\n\n    verbose.should be_true\n    subcommand.should be_true\n    foo.should be(\"1\")\n    bar.should be_true\n    sub2.should be_true\n    z.should be_true\n\n    args = %w(--verbose subcommand --foo 1 --bar sub2 -z)\n    verbose = false\n    subcommand = false\n    foo = nil\n    bar = false\n    sub2 = false\n    z = false\n\n    parser.parse args\n\n    verbose.should be_true\n    subcommand.should be_true\n    foo.should be(\"1\")\n    bar.should be_true\n    sub2.should be_true\n    z.should be_true\n  end\n\n  it \"unregisters subcommands on call\" do\n    foo = false\n    bar = false\n    baz = false\n    OptionParser.parse(%w(foo baz)) do |opts|\n      opts.on(\"foo\", \"\") do\n        foo = true\n        opts.on(\"bar\", \"\") { bar = true }\n      end\n      opts.on(\"baz\", \"\") { baz = true }\n    end\n    foo.should be_true\n    bar.should be_false\n    baz.should be_false\n  end\n\n  it \"handles subcommand --help well (top level)\" do\n    help = nil\n    OptionParser.parse(%w(--help)) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.on(\"subcommand\", \"Subcommand Description\") do\n        opts.on(\"-f\", \"--foo\", \"Foo\") { }\n      end\n      opts.on(\"--verbose\", \"Verbose mode\") { }\n      opts.on(\"--help\", \"Help\") { help = opts.to_s }\n    end\n\n    help.should eq <<-USAGE\n      Usage: foo\n          subcommand                       Subcommand Description\n          --verbose                        Verbose mode\n          --help                           Help\n      USAGE\n  end\n\n  it \"handles subcommand --help well (subcommand)\" do\n    help = nil\n    OptionParser.parse(%w(subcommand --help)) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.on(\"subcommand\", \"Subcommand Description\") do\n        opts.banner = \"Usage: foo subcommand\"\n        opts.on(\"-f\", \"--foo\", \"Foo\") { }\n      end\n      opts.on(\"--verbose\", \"Verbose mode\") { }\n      opts.on(\"--help\", \"Help\") { help = opts.to_s }\n    end\n\n    help.should eq <<-USAGE\n      Usage: foo subcommand\n          --verbose                        Verbose mode\n          --help                           Help\n          -f, --foo                        Foo\n      USAGE\n  end\n\n  it \"handles subcommands with hyphen\" do\n    subcommand = false\n    OptionParser.parse(%w(sub-command)) do |opts|\n      opts.banner = \"Usage: foo\"\n      opts.on(\"sub-command\", \"Subcommand description\") { subcommand = true }\n    end\n\n    subcommand.should be_true\n  end\n\n  it \"stops when asked\" do\n    args = %w(--foo --stop --bar)\n    foo = false\n    bar = false\n    OptionParser.parse(args) do |opts|\n      opts.on(\"--foo\", \"\") { foo = true }\n      opts.on(\"--bar\", \"\") { bar = true }\n      opts.on(\"--stop\", \"\") { opts.stop }\n      opts.unknown_args do |before, after|\n        before.should eq(%w())\n        after.should eq(%w(--bar))\n      end\n    end\n    foo.should be_true\n    bar.should be_false\n    args.should eq(%w(--bar))\n  end\n\n  it \"can run a callback on every argument\" do\n    args = %w(--foo file --bar)\n    foo = false\n    bar = false\n    OptionParser.parse(args) do |opts|\n      opts.on(\"--foo\", \"\") { foo = true }\n      opts.on(\"--bar\", \"\") { bar = true }\n      opts.before_each do |arg|\n        if arg == \"file\"\n          opts.stop\n        end\n      end\n      opts.unknown_args do |before, after|\n        before.should eq(%w(file))\n        after.should eq(%w(--bar))\n      end\n    end\n\n    foo.should be_true\n    bar.should be_false\n    args.should eq(%w(file --bar))\n  end\nend\n\ndescribe \"OptionParser with summary_width and summary_indent\" do\n  it \"formats flags and descriptions with default summary_width and summary_indent\" do\n    parser = OptionParser.new\n\n    parser.on(\"-f\", \"--flag\", \"A short description\") { }\n    parser.on(\"-l\", \"--long-flag\", \"A long description for testing purposes\") { }\n\n    output = parser.to_s\n\n    expected_output = <<-USAGE\n        -f, --flag                       A short description\n        -l, --long-flag                  A long description for testing purposes\n    USAGE\n\n    output.should eq(expected_output)\n  end\n\n  it \"formats flags and descriptions with custom summary_width and summary_indent\" do\n    parser = OptionParser.new\n    parser.summary_width = 40\n    parser.summary_indent = \"|\" * 6\n\n    parser.on(\"-f\", \"--flag\", \"A short description\") { }\n    parser.on(\"-l\", \"--long-flag\", \"A long description for testing purposes\") { }\n\n    output = parser.to_s\n\n    expected_output = <<-USAGE\n    ||||||-f, --flag                               A short description\n    ||||||-l, --long-flag                          A long description for testing purposes\n    USAGE\n\n    output.should eq(expected_output)\n  end\n\n  it \"handles multiline descriptions correctly with summary_width\" do\n    parser = OptionParser.new\n    parser.summary_width = 20\n    parser.summary_indent = \" \" * 4\n\n    parser.on(\"--complex-option\", \"This is a detailed description\\nspanning multiple lines for testing\") { }\n\n    output = parser.to_s\n\n    expected_output = <<-USAGE\n        --complex-option     This is a detailed description\n                             spanning multiple lines for testing\n    USAGE\n\n    output.should eq(expected_output)\n  end\n\n  it \"adjusts formatting when flag length exceeds summary_width\" do\n    parser = OptionParser.new\n    parser.summary_width = 20\n    parser.summary_indent = \" \" * 4\n\n    parser.on(\"--very-very-long-option\", \"Description that follows a very\\nlong flag\") { }\n\n    output = parser.to_s\n\n    expected_output = <<-USAGE\n        --very-very-long-option\n                             Description that follows a very\n                             long flag\n    USAGE\n\n    output.should eq(expected_output)\n  end\n\n  it \"handles extreme summary_width values (e.g., 0)\" do\n    parser = OptionParser.new\n    parser.summary_width = 0\n    parser.summary_indent = \" \" * 4\n\n    parser.on(\"--short\", \"No space for flags!\") { }\n\n    output = parser.to_s\n\n    expected_output = <<-USAGE\n        --short\n         No space for flags!\n    USAGE\n\n    output.should eq(expected_output)\n  end\n\n  it \"handles extreme summary_indent values (e.g., empty string)\" do\n    parser = OptionParser.new\n    parser.summary_width = 20\n    parser.summary_indent = \"\"\n\n    parser.on(\"--test\", \"Indentation removed\") { }\n\n    output = parser.to_s\n\n    expected_output = <<-USAGE\n    --test               Indentation removed\n    USAGE\n\n    output.should eq(expected_output)\n  end\n\n  it \"raises if summary_width is negative\" do\n    parser = OptionParser.new\n    expect_raises(ArgumentError, \"Negative summary width: -10\") do\n      parser.summary_width = -10\n    end\n  end\n\n  it \"formats subcommand help with custom summary_indent\" do\n    help = nil\n    OptionParser.parse(%w(subcommand --help)) do |opts|\n      opts.summary_indent = \"||\"\n      opts.banner = \"Usage: foo\"\n\n      opts.on(\"subcommand\", \"Subcommand description\") do\n        opts.banner = \"Usage: foo subcommand\"\n        opts.on(\"--local\", \"Local flag\") { }\n      end\n      opts.on(\"other\", \"Other subcommand\") { }\n      opts.on(\"--help\", \"Help\") { help = opts.to_s }\n    end\n\n    help.should eq <<-USAGE\n      Usage: foo subcommand\n      ||--help                           Help\n      ||--local                          Local flag\n      USAGE\n  end\nend\n"
  },
  {
    "path": "spec/std/path_spec.cr",
    "content": "require \"spec\"\nrequire \"./spec_helper\"\nrequire \"../support/env\"\n\nprivate HOME_ENV_KEY = {% if flag?(:win32) %} \"USERPROFILE\" {% else %} \"HOME\" {% end %}\nprivate BASE_POSIX   = \"/default/base\"\nprivate BASE_WINDOWS = \"\\\\default\\\\base\"\nprivate HOME_WINDOWS = \"C:\\\\Users\\\\Crystal\"\nprivate HOME_POSIX   = \"/home/crystal\"\n\nprivate def it_normalizes_path(path, posix = path, windows = path, file = __FILE__, line = __LINE__)\n  assert_paths(path, posix, windows, \"normalizes\", file, line, &.normalize)\nend\n\nprivate def it_expands_path(path, posix, windows = posix, *, base = nil, env_home = nil, expand_base = false, home = false, file = __FILE__, line = __LINE__)\n  assert_paths(path, posix, windows, %((base: \"#{base}\")), file, line) do |path|\n    with_env({HOME_ENV_KEY => env_home}) do\n      base_arg = base || (path.windows? ? BASE_WINDOWS : BASE_POSIX)\n      base_arg = path.windows? ? Path.windows(base_arg) : Path.posix(base_arg) unless base_arg.is_a?(Path)\n      if home == true && env_home.nil?\n        myhome = path.windows? ? Path.windows(HOME_WINDOWS) : Path.posix(HOME_POSIX)\n      else\n        myhome = home\n      end\n      path.expand(base_arg, expand_base: !!expand_base, home: myhome)\n    end\n  end\nend\n\nprivate def it_joins_path(path, parts, posix, windows = posix, file = __FILE__, line = __LINE__)\n  assert_paths(path, posix, windows, %(resolving \"#{parts}\"), file, line, &.join(parts))\n  unless parts.is_a?(Enumerable)\n    # FIXME: Omitting the type cast results in Error: can't infer block return type, try to cast the block body with `as`.\n    assert_paths(path, posix, windows, %(resolving [\"#{parts}\"] ), file, line, &.join([parts]).as(Path))\n    assert_paths(path, posix, windows, %(resolving [\"#{parts}\"].each), file, line, &.join([parts].each).as(Path))\n  end\nend\n\nprivate def assert_paths(path, posix, windows = posix, label = nil, file = __FILE__, line = __LINE__, &block : Path -> _)\n  case posix\n  when Nil\n  when Tuple  then posix = Path.posix(*posix)\n  when String then posix = Path.posix(posix)\n  when Array  then posix = posix.map { |path| Path.posix(path) }\n  end\n  case windows\n  when Nil\n  when Tuple  then windows = Path.windows(*windows)\n  when String then windows = Path.windows(windows)\n  when Array  then windows = windows.map { |path| Path.windows(path) }\n  end\n  assert_paths_raw(path, posix, windows, label, file, line, &block)\nend\n\nprivate def assert_paths_raw(path, posix, windows = posix, label = nil, file = __FILE__, line = __LINE__, &block : Path -> _)\n  it %(#{label} \"#{path}\" (posix)), file, line do\n    block.call(Path.posix(path)).should eq(posix), file: file, line: line\n  end\n  it %(#{label} \"#{path}\" (windows)), file, line do\n    block.call(Path.windows(path)).should eq(windows), file: file, line: line\n  end\nend\n\nprivate def it_relativizes(base, target, posix, windows = posix, file = __FILE__, line = __LINE__)\n  assert_paths target, posix, windows, %(on \"#{base}\":), file, line do |path|\n    path.relative_to?(base)\n  end\nend\n\nprivate def it_iterates_parts(path, posix, windows = posix, file = __FILE__, line = __LINE__)\n  assert_paths_raw path, posix, windows, label: \"block\", file: file, line: line do |path|\n    array = [] of String\n    path.each_part do |part|\n      array << part\n    end\n    array\n  end\n  assert_paths_raw path, posix, windows, label: \"iterator\", file: file, line: line do |path|\n    array = [] of String\n    path.each_part.each do |part|\n      array << part\n    end\n    array\n  end\n  assert_paths_raw path, posix, windows, label: \"#parts\", file: file, line: line do |path|\n    path.parts\n  end\nend\n\ndescribe Path do\n  describe \".new\" do\n    it { Path.new(\"foo\").native?.should be_true }\n    it { Path.new(\"foo\").to_s.should eq \"foo\" }\n\n    it \"fails with null byte\" do\n      expect_raises ArgumentError, \"String contains null byte\" do\n        Path.new(\"foo\\0\")\n      end\n    end\n\n    it { Path.new.to_s.should eq \"\" }\n\n    it \"joins components\" do\n      Path.new(\"foo\", \"bar\").should eq Path.new(\"foo\").join(\"bar\")\n      Path.new(Path.new(\"foo\"), \"bar\").should eq Path.new(\"foo\", \"bar\")\n      Path.new(Path.posix(\"foo\"), \"bar\").should eq Path.new(\"foo\", \"bar\")\n      Path.new(Path.windows(\"foo\"), \"bar\").should eq Path.new(\"foo\", \"bar\")\n\n      # implicit conversion:\n      Path.windows(\"foo\", Path.posix(\"bar\\\\baz\")).should eq Path.windows(\"foo\").join(Path.posix(\"bar\\\\baz\").to_windows)\n    end\n  end\n\n  describe \".posix\" do\n    it { Path.posix(\"foo\").posix?.should be_true }\n    it { Path.posix(\"foo\").windows?.should be_false }\n    it { Path.posix(\"foo\").to_s.should eq \"foo\" }\n\n    it \"fails with null byte\" do\n      expect_raises ArgumentError, \"String contains null byte\" do\n        Path.posix(\"foo\\0\")\n      end\n    end\n\n    it { Path.posix.to_s.should eq \"\" }\n\n    it \"joins components\" do\n      Path.posix(\"foo\", \"bar\").should eq Path.posix(\"foo\").join(\"bar\")\n      Path.posix(Path.new(\"foo\"), \"bar\").should eq Path.posix(\"foo\", \"bar\")\n      Path.posix(Path.posix(\"foo\"), \"bar\").should eq Path.posix(\"foo\", \"bar\")\n      Path.posix(Path.windows(\"foo\"), \"bar\").should eq Path.posix(\"foo\", \"bar\")\n    end\n  end\n\n  describe \".windows\" do\n    it { Path.windows(\"foo\").posix?.should be_false }\n    it { Path.windows(\"foo\").windows?.should be_true }\n    it { Path.windows(\"foo\").to_s.should eq \"foo\" }\n\n    it \"fails with null byte\" do\n      expect_raises ArgumentError, \"String contains null byte\" do\n        Path.windows(\"foo\\0\")\n      end\n    end\n\n    it { Path.windows.to_s.should eq \"\" }\n\n    it \"joins components\" do\n      Path.windows(\"foo\", \"bar\").should eq Path.windows(\"foo\").join(\"bar\")\n      Path.windows(Path.new(\"foo\"), \"bar\").should eq Path.windows(\"foo\", \"bar\")\n      Path.windows(Path.posix(\"foo\"), \"bar\").should eq Path.windows(\"foo\", \"bar\")\n      Path.windows(Path.windows(\"foo\"), \"bar\").should eq Path.windows(\"foo\", \"bar\")\n    end\n  end\n\n  it \".[]\" do\n    Path[\"foo\"].should eq Path.new(\"foo\")\n    Path[\"foo\"].native?.should be_true\n    Path[\"foo\", \"bar\", \"baz\"].should eq Path.new(\"foo\", \"bar\", \"baz\")\n    Path[\"/foo\", \"bar\", \"baz\"].should eq Path.new(\"/foo\", \"bar\", \"baz\")\n  end\n\n  describe \"#parent\" do\n    assert_paths(\"/Users/foo/bar.cr\", \"/Users/foo\", &.parent)\n    assert_paths(\"Users/foo/bar.cr\", \"Users/foo\", &.parent)\n    assert_paths(\"foo/bar/\", \"foo\", &.parent)\n    assert_paths(\"foo/bar/.\", \"foo/bar\", &.parent)\n    assert_paths(\"foo/bar/..\", \"foo/bar\", &.parent)\n    assert_paths(\"foo\", \".\", &.parent)\n    assert_paths(\"foo/\", \".\", &.parent)\n    assert_paths(\"/\", \"/\", &.parent)\n    assert_paths(\"/.\", \"/\", &.parent)\n    assert_paths(\"////\", \"/\", &.parent)\n    assert_paths(\"foo//.//\", \"foo\", &.parent)\n    assert_paths(\"/.\", \"/\", &.parent)\n    assert_paths(\"/foo\", \"/\", &.parent)\n    assert_paths(\"\", \".\", &.parent)\n    assert_paths(\"./foo\", \".\", &.parent)\n    assert_paths(\".\", \".\", &.parent)\n    assert_paths(\"\\\\Users\\\\foo\\\\bar.cr\", \".\", \"\\\\Users\\\\foo\", &.parent)\n    assert_paths(\"\\\\Users/foo\\\\bar.cr\", \"\\\\Users\", \"\\\\Users/foo\", &.parent)\n    assert_paths(\"foo\\\\bar\\\\\", \".\", \"foo\", &.parent)\n    assert_paths(\"foo\\\\bar\\\\.\", \".\", \"foo\\\\bar\", &.parent)\n    assert_paths(\"foo\\\\bar\\\\..\", \".\", \"foo\\\\bar\", &.parent)\n    assert_paths(\"foo\\\\\", \".\", &.parent)\n    assert_paths(\"\\\\\", \".\", \"\\\\\", &.parent)\n    assert_paths(\"\\\\.\", \".\", \"\\\\\", &.parent)\n    assert_paths(\".\\\\foo\", \".\", &.parent)\n    assert_paths(\"C:\", \".\", \"C:\", &.parent)\n    assert_paths(\"C:/\", \".\", \"C:/\", &.parent)\n    assert_paths(\"C:\\\\\", \".\", \"C:\\\\\", &.parent)\n    assert_paths(\"C:/foo\", \"C:\", \"C:/\", &.parent)\n    assert_paths(\"C:\\\\foo\", \".\", \"C:\\\\\", &.parent)\n    assert_paths(\"\\\\\\\\.\", \".\", \"\\\\\\\\.\", &.parent)\n    assert_paths(\"\\\\/?\", \"\\\\\", \"\\\\/?\", &.parent)\n    assert_paths(\"//.\", \"/\", \"//.\", &.parent)\n    assert_paths(\"//./\", \"/\", \"//./\", &.parent)\n    assert_paths(\"//.\\\\\", \"/\", \"//.\\\\\", &.parent)\n    assert_paths(\"//./foo\", \"//.\", \"//./\", &.parent)\n    assert_paths(\"//.\\\\foo\", \"/\", \"//.\\\\\", &.parent)\n    assert_paths(\"//?/\", \"/\", \"//?/\", &.parent)\n    assert_paths(\"//?\\\\\", \"/\", \"//?\\\\\", &.parent)\n    assert_paths(\"//?/foo\", \"//?\", \"//?/\", &.parent)\n    assert_paths(\"//?\\\\foo\", \"/\", \"//?\\\\\", &.parent)\n    assert_paths(\"\\\\\\\\?/\", \".\", \"\\\\\\\\?/\", &.parent)\n    assert_paths(\"\\\\\\\\?\\\\\", \".\", \"\\\\\\\\?\\\\\", &.parent)\n    assert_paths(\"/foo/C:/bar\", \"/foo/C:\", \"/foo/C:\", &.parent)\n    assert_paths(\"//some/share\", \"//some\", \"//some/share\", &.parent)\n    assert_paths(\"//some/share/\", \"//some\", \"//some/share/\", &.parent)\n    assert_paths(\"//some/share/a\", \"//some/share\", \"//some/share/\", &.parent)\n    assert_paths(\"//some/share/a/\", \"//some/share\", \"//some/share/\", &.parent)\n  end\n\n  describe \"#parents\" do\n    assert_paths(\"/Users/foo/bar.cr\", [\"/\", \"/Users\", \"/Users/foo\"], &.parents)\n    assert_paths(\"Users/foo/bar.cr\", [\".\", \"Users\", \"Users/foo\"], &.parents)\n    assert_paths(\"foo/bar/\", [\".\", \"foo\"], &.parents)\n    assert_paths(\"foo/bar/.\", [\".\", \"foo\", \"foo/bar\"], &.parents)\n    assert_paths(\"foo\", [\".\"], &.parents)\n    assert_paths(\"foo/\", [\".\"], &.parents)\n    assert_paths(\"/\", [] of String, &.parents)\n    assert_paths(\"////\", [] of String, &.parents)\n    assert_paths(\"/.\", [\"/\"], &.parents)\n    assert_paths(\"/foo\", [\"/\"], &.parents)\n    assert_paths(\"\", [] of String, &.parents)\n    assert_paths(\"./foo\", [\".\"], &.parents)\n    assert_paths(\".\", [] of String, &.parents)\n    assert_paths(\"\\\\Users\\\\foo\\\\bar.cr\", [\".\"], [\"\\\\\", \"\\\\Users\", \"\\\\Users\\\\foo\"], &.parents)\n    assert_paths(\"\\\\Users/foo\\\\bar.cr\", [\".\", \"\\\\Users\"], [\"\\\\\", \"\\\\Users\", \"\\\\Users/foo\"], &.parents)\n    assert_paths(\"C:\\\\Users\\\\foo\\\\bar.cr\", [\".\"], [\"C:\\\\\", \"C:\\\\Users\", \"C:\\\\Users\\\\foo\"], &.parents)\n    assert_paths(\"foo\\\\bar\\\\\", [\".\"], [\".\", \"foo\"], &.parents)\n    assert_paths(\"foo\\\\\", [\".\"], &.parents)\n    assert_paths(\"\\\\\", [\".\"], [] of String, &.parents)\n    assert_paths(\".\\\\foo\", [\".\"], &.parents)\n    assert_paths(\"foo/../bar/\", [\".\", \"foo\", \"foo/..\"], &.parents)\n    assert_paths(\"foo/../bar/.\", [\".\", \"foo\", \"foo/..\", \"foo/../bar\"], &.parents)\n    assert_paths(\"foo/bar/..\", [\".\", \"foo\", \"foo/bar\"], &.parents)\n    assert_paths(\"foo/bar/../.\", [\".\", \"foo\", \"foo/bar\", \"foo/bar/..\"], &.parents)\n    assert_paths(\"foo/./bar/\", [\".\", \"foo\", \"foo/.\"], &.parents)\n    assert_paths(\"foo/./bar/.\", [\".\", \"foo\", \"foo/.\", \"foo/./bar\"], &.parents)\n    assert_paths(\"foo/bar/.\", [\".\", \"foo\", \"foo/bar\"], &.parents)\n    assert_paths(\"foo/bar/./.\", [\".\", \"foo\", \"foo/bar\", \"foo/bar/.\"], &.parents)\n    assert_paths(\"m/.gitignore\", [\".\", \"m\"], &.parents)\n    assert_paths(\"m\", [\".\"], &.parents)\n    assert_paths(\"m/\", [\".\"], &.parents)\n    assert_paths(\"m//\", [\".\"], &.parents)\n    assert_paths(\"m//a/b\", [\".\", \"m\", \"m//a\"], &.parents)\n    assert_paths(\"/m\", [\"/\"], &.parents)\n    assert_paths(\"/m/\", [\"/\"], &.parents)\n    assert_paths(\"C:\", [\".\"], [] of String, &.parents)\n    assert_paths(\"C:/\", [\".\"], [] of String, &.parents)\n    assert_paths(\"C:\\\\\", [\".\"], [] of String, &.parents)\n    assert_paths(\"C:folder\", [\".\"], [\"C:\"], &.parents)\n    assert_paths(\"C:\\\\folder\", [\".\"], [\"C:\\\\\"], &.parents)\n    assert_paths(\"C:\\\\\\\\folder\", [\".\"], [\"C:\\\\\\\\\"], &.parents)\n    assert_paths(\"C:\\\\.\", [\".\"], [\"C:\\\\\"], &.parents)\n    assert_paths(\"\\\\\\\\.\", [\".\"], [] of String, &.parents)\n    assert_paths(\"\\\\/?\", [\".\", \"\\\\\"], [] of String, &.parents)\n    assert_paths(\"//.\", [\"//\"], [] of String, &.parents)\n    assert_paths(\"//./\", [\"//\"], [] of String, &.parents)\n    assert_paths(\"//.\\\\\", [\"//\"], [] of String, &.parents)\n    assert_paths(\"//./foo\", [\"//\", \"//.\"], [\"//./\"], &.parents)\n    assert_paths(\"//.\\\\foo\", [\"//\"], [\"//.\\\\\"], &.parents)\n    assert_paths(\"//?/\", [\"//\"], [] of String, &.parents)\n    assert_paths(\"//?\\\\\", [\"//\"], [] of String, &.parents)\n    assert_paths(\"//?/foo\", [\"//\", \"//?\"], [\"//?/\"], &.parents)\n    assert_paths(\"//?\\\\foo\", [\"//\"], [\"//?\\\\\"], &.parents)\n    assert_paths(\"\\\\\\\\?/\", [\".\"], [] of String, &.parents)\n    assert_paths(\"\\\\\\\\?\\\\\", [\".\"], [] of String, &.parents)\n    assert_paths(\"//some/share\", [\"//\", \"//some\"], [] of String, &.parents)\n    assert_paths(\"//some/share/\", [\"//\", \"//some\"], [] of String, &.parents)\n    assert_paths(\"//some/share/a\", [\"//\", \"//some\", \"//some/share\"], [\"//some/share/\"], &.parents)\n    assert_paths(\"//some/share/a/\", [\"//\", \"//some\", \"//some/share\"], [\"//some/share/\"], &.parents)\n  end\n\n  describe \"#dirname\" do\n    assert_paths_raw(\"/Users/foo/bar.cr\", \"/Users/foo\", &.dirname)\n    assert_paths_raw(\"foo\", \".\", &.dirname)\n    assert_paths_raw(\"foo/\", \".\", &.dirname)\n    assert_paths_raw(\"/foo\", \"/\", &.dirname)\n    assert_paths_raw(\"/foo/\", \"/\", &.dirname)\n    assert_paths_raw(\"/foo//\", \"/\", &.dirname)\n    assert_paths_raw(\"m/.gitignore\", \"m\", &.dirname)\n    assert_paths_raw(\"m/\", \".\", &.dirname)\n    assert_paths_raw(\"m//\", \".\", &.dirname)\n    assert_paths_raw(\"m//a/b\", \"m//a\", &.dirname)\n    assert_paths_raw(\"m\", \".\", &.dirname)\n    assert_paths_raw(\"/m\", \"/\", &.dirname)\n    assert_paths_raw(\"/m/\", \"/\", &.dirname)\n    assert_paths_raw(\"C:\", \".\", \"C:\", &.dirname)\n    assert_paths_raw(\"C:/\", \".\", \"C:/\", &.dirname)\n    assert_paths_raw(\"C:\\\\\", \".\", \"C:\\\\\", &.dirname)\n    assert_paths_raw(\"C:/foo\", \"C:\", \"C:/\", &.dirname)\n    assert_paths_raw(\"C:\\\\foo\", \".\", \"C:\\\\\", &.dirname)\n    assert_paths_raw(\"\\\\\\\\.\", \".\", \"\\\\\\\\.\", &.dirname)\n    assert_paths_raw(\"\\\\/?\", \"\\\\\", \"\\\\/?\", &.dirname)\n    assert_paths_raw(\"//.\", \"/\", \"//.\", &.dirname)\n    assert_paths_raw(\"//./\", \"/\", \"//./\", &.dirname)\n    assert_paths_raw(\"//.\\\\\", \"/\", \"//.\\\\\", &.dirname)\n    assert_paths_raw(\"//./foo\", \"//.\", \"//./\", &.dirname)\n    assert_paths_raw(\"//.\\\\foo\", \"/\", \"//.\\\\\", &.dirname)\n    assert_paths_raw(\"//?/\", \"/\", \"//?/\", &.dirname)\n    assert_paths_raw(\"//?\\\\\", \"/\", \"//?\\\\\", &.dirname)\n    assert_paths_raw(\"//?/foo\", \"//?\", \"//?/\", &.dirname)\n    assert_paths_raw(\"//?\\\\foo\", \"/\", \"//?\\\\\", &.dirname)\n    assert_paths_raw(\"\\\\\\\\?/\", \".\", \"\\\\\\\\?/\", &.dirname)\n    assert_paths_raw(\"\\\\\\\\?\\\\\", \".\", \"\\\\\\\\?\\\\\", &.dirname)\n    assert_paths_raw(\"//some/share\", \"//some\", \"//some/share\", &.dirname)\n    assert_paths_raw(\"//some/share/\", \"//some\", \"//some/share/\", &.dirname)\n    assert_paths_raw(\"//some/share/a\", \"//some/share\", \"//some/share/\", &.dirname)\n    assert_paths_raw(\"//some/share/a/\", \"//some/share\", \"//some/share/\", &.dirname)\n  end\n\n  describe \"#basename\" do\n    assert_paths_raw(\"/foo/bar/baz.cr\", \"baz.cr\", &.basename)\n    assert_paths_raw(\"/foo/\", \"foo\", &.basename)\n    assert_paths_raw(\"foo\", \"foo\", &.basename)\n    assert_paths_raw(\"x\", \"x\", &.basename)\n    assert_paths_raw(\"\", \"\", &.basename)\n    assert_paths_raw(\".\", \".\", &.basename)\n    assert_paths_raw(\"/.\", \".\", &.basename)\n    assert_paths_raw(\"/\", \"/\", &.basename)\n    assert_paths_raw(\"////\", \"/\", &.basename)\n    assert_paths_raw(\"a/x\", \"x\", &.basename)\n    assert_paths_raw(\"a/.x\", \".x\", &.basename)\n    assert_paths_raw(\"a/x.\", \"x.\", &.basename)\n\n    assert_paths_raw(\"\\\\foo\\\\bar\\\\baz.cr\", \"\\\\foo\\\\bar\\\\baz.cr\", \"baz.cr\", &.basename)\n    assert_paths_raw(\"\\\\foo\\\\\", \"\\\\foo\\\\\", \"foo\", &.basename)\n    assert_paths_raw(\"\\\\\", \"\\\\\", \"\\\\\", &.basename)\n    assert_paths_raw(\"\\\\.\", \"\\\\.\", \".\", &.basename)\n\n    describe \"removes suffix\" do\n      assert_paths_raw(\"/foo/bar/baz.cr\", \"baz\", &.basename(\".cr\"))\n      assert_paths_raw(\"\\\\foo\\\\bar\\\\baz.cr\", \"\\\\foo\\\\bar\\\\baz\", \"baz\", &.basename(\".cr\"))\n      assert_paths_raw(\"\\\\foo/bar\\\\baz.cr\", \"bar\\\\baz\", \"baz\", &.basename(\".cr\"))\n      assert_paths_raw(\"/foo/bar/baz.cr.tmp\", \"baz.cr.tmp\", \"baz.cr.tmp\", &.basename(\".cr\"))\n      assert_paths_raw(\"\\\\foo\\\\bar\\\\baz.cr.tmp\", \"\\\\foo\\\\bar\\\\baz.cr.tmp\", \"baz.cr.tmp\", &.basename(\".cr\"))\n      assert_paths_raw(\"/foo/bar/baz.cr.tmp\", \"baz\", &.basename(\".cr.tmp\"))\n      assert_paths_raw(\"\\\\foo\\\\bar\\\\baz.cr.tmp\", \"\\\\foo\\\\bar\\\\baz\", \"baz\", &.basename(\".cr.tmp\"))\n      assert_paths_raw(\"/foo/bar/baz.cr.tmp\", \"baz.cr\", &.basename(\".tmp\"))\n      assert_paths_raw(\"\\\\foo\\\\bar\\\\baz.cr.tmp\", \"\\\\foo\\\\bar\\\\baz.cr\", \"baz.cr\", &.basename(\".tmp\"))\n      assert_paths_raw(\"a.txt\", \"a\", &.basename(\".txt\"))\n      assert_paths_raw(\"a.x\", \"a\", &.basename(\".x\"))\n    end\n  end\n\n  describe \"#each_part\" do\n    it_iterates_parts(\"/Users/foo/bar.cr\", [\"/\", \"Users\", \"foo\", \"bar.cr\"])\n    it_iterates_parts(\"Users/foo/bar.cr\", [\"Users\", \"foo\", \"bar.cr\"])\n    it_iterates_parts(\"foo/bar/\", [\"foo\", \"bar\"])\n    it_iterates_parts(\"foo/bar/.\", [\"foo\", \"bar\", \".\"])\n    it_iterates_parts(\"foo\", [\"foo\"])\n    it_iterates_parts(\"foo/\", [\"foo\"])\n    it_iterates_parts(\"/\", [\"/\"])\n    it_iterates_parts(\"////\", [\"////\"])\n    it_iterates_parts(\"/.\", [\"/\", \".\"])\n    it_iterates_parts(\"/foo\", [\"/\", \"foo\"])\n    it_iterates_parts(\"\", [] of String)\n    it_iterates_parts(\"./foo\", [\".\", \"foo\"])\n    it_iterates_parts(\".\", [\".\"])\n    it_iterates_parts(\"\\\\Users\\\\foo\\\\bar.cr\", [\"\\\\Users\\\\foo\\\\bar.cr\"], [\"\\\\\", \"Users\", \"foo\", \"bar.cr\"])\n    it_iterates_parts(\"\\\\Users/foo\\\\bar.cr\", [\"\\\\Users\", \"foo\\\\bar.cr\"], [\"\\\\\", \"Users\", \"foo\", \"bar.cr\"])\n    it_iterates_parts(\"C:\\\\Users\\\\foo\\\\bar.cr\", [\"C:\\\\Users\\\\foo\\\\bar.cr\"], [\"C:\\\\\", \"Users\", \"foo\", \"bar.cr\"])\n    it_iterates_parts(\"\\\\\\\\some\\\\share\\\\\", [\"\\\\\\\\some\\\\share\\\\\"], [\"\\\\\\\\some\\\\share\\\\\"])\n    it_iterates_parts(\"\\\\\\\\some\\\\share\", [\"\\\\\\\\some\\\\share\"])\n    it_iterates_parts(\"\\\\\\\\some\\\\share\\\\bar.cr\", [\"\\\\\\\\some\\\\share\\\\bar.cr\"], [\"\\\\\\\\some\\\\share\\\\\", \"bar.cr\"])\n    it_iterates_parts(\"//some/share\", [\"//\", \"some\", \"share\"], [\"//some/share\"])\n    it_iterates_parts(\"//some/share/\", [\"//\", \"some\", \"share\"], [\"//some/share/\"])\n    it_iterates_parts(\"//some/share/bar.cr\", [\"//\", \"some\", \"share\", \"bar.cr\"], [\"//some/share/\", \"bar.cr\"])\n    it_iterates_parts(\"foo\\\\bar\\\\\", [\"foo\\\\bar\\\\\"], [\"foo\", \"bar\"])\n    it_iterates_parts(\"foo\\\\\", [\"foo\\\\\"], [\"foo\"])\n    it_iterates_parts(\"\\\\\", [\"\\\\\"], [\"\\\\\"])\n    it_iterates_parts(\".\\\\foo\", [\".\\\\foo\"], [\".\", \"foo\"])\n    it_iterates_parts(\"foo/../bar/\", [\"foo\", \"..\", \"bar\"])\n    it_iterates_parts(\"foo/../bar/.\", [\"foo\", \"..\", \"bar\", \".\"])\n    it_iterates_parts(\"foo/bar/..\", [\"foo\", \"bar\", \"..\"])\n    it_iterates_parts(\"foo/bar/../.\", [\"foo\", \"bar\", \"..\", \".\"])\n    it_iterates_parts(\"foo/./bar/\", [\"foo\", \".\", \"bar\"])\n    it_iterates_parts(\"foo/./bar/.\", [\"foo\", \".\", \"bar\", \".\"])\n    it_iterates_parts(\"foo/bar/.\", [\"foo\", \"bar\", \".\"])\n    it_iterates_parts(\"foo/bar/./.\", [\"foo\", \"bar\", \".\", \".\"])\n    it_iterates_parts(\"m/.gitignore\", [\"m\", \".gitignore\"])\n    it_iterates_parts(\"m\", [\"m\"])\n    it_iterates_parts(\"m/\", [\"m\"])\n    it_iterates_parts(\"m//\", [\"m\"])\n    it_iterates_parts(\"m\\\\\", [\"m\\\\\"], [\"m\"])\n    it_iterates_parts(\"m//a/b\", [\"m\", \"a\", \"b\"])\n    it_iterates_parts(\"m\\\\a/b\", [\"m\\\\a\", \"b\"], [\"m\", \"a\", \"b\"])\n    it_iterates_parts(\"/m\", [\"/\", \"m\"])\n    it_iterates_parts(\"/m/\", [\"/\", \"m\"])\n    it_iterates_parts(\"C:\", [\"C:\"])\n    it_iterates_parts(\"C:/\", [\"C:\"], [\"C:/\"])\n    it_iterates_parts(\"C:\\\\\", [\"C:\\\\\"])\n    it_iterates_parts(\"C:folder\", [\"C:folder\"], [\"C:\", \"folder\"])\n    it_iterates_parts(\"C:\\\\folder\", [\"C:\\\\folder\"], [\"C:\\\\\", \"folder\"])\n    it_iterates_parts(\"C:\\\\\\\\folder\", [\"C:\\\\\\\\folder\"], [\"C:\\\\\\\\\", \"folder\"])\n    it_iterates_parts(\"C:\\\\.\", [\"C:\\\\.\"], [\"C:\\\\\", \".\"])\n    it_iterates_parts(\"//.\", [\"//\", \".\"], [\"//.\"])\n    it_iterates_parts(\"//?\", [\"//\", \"?\"], [\"//?\"])\n    it_iterates_parts(\"\\\\\\\\.\\\\\", [\"\\\\\\\\.\\\\\"], [\"\\\\\\\\.\\\\\"])\n    it_iterates_parts(\"\\\\\\\\?\\\\\", [\"\\\\\\\\?\\\\\"], [\"\\\\\\\\?\\\\\"])\n    it_iterates_parts(\"\\\\\\\\.\\\\foo\", [\"\\\\\\\\.\\\\foo\"], [\"\\\\\\\\.\\\\\", \"foo\"])\n    it_iterates_parts(\"\\\\\\\\?\\\\foo\", [\"\\\\\\\\?\\\\foo\"], [\"\\\\\\\\?\\\\\", \"foo\"])\n    it_iterates_parts(\"\\\\\\\\.\\\\foo\\\\bar\", [\"\\\\\\\\.\\\\foo\\\\bar\"], [\"\\\\\\\\.\\\\\", \"foo\", \"bar\"])\n    it_iterates_parts(\"\\\\\\\\?\\\\foo\\\\bar\", [\"\\\\\\\\?\\\\foo\\\\bar\"], [\"\\\\\\\\?\\\\\", \"foo\", \"bar\"])\n  end\n\n  describe \"#extension\" do\n    assert_paths_raw(\"/foo/bar/baz.cr\", \".cr\", &.extension)\n    assert_paths_raw(\"/foo/bar/baz.cr.cz\", \".cz\", &.extension)\n    assert_paths_raw(\"/foo/bar/.profile\", \"\", &.extension)\n    assert_paths_raw(\"/foo/bar/.profile.sh\", \".sh\", &.extension)\n    assert_paths_raw(\"/foo/bar/foo.\", \"\", &.extension)\n    assert_paths_raw(\"test\", \"\", &.extension)\n    assert_paths_raw(\"test.ext/foo\", \"\", &.extension)\n    assert_paths_raw(\"test.ext/foo/\", \"\", &.extension)\n    assert_paths_raw(\"test.ext/\", \".ext\", &.extension)\n    assert_paths_raw(\"test/.\", \"\", &.extension)\n    assert_paths_raw(\"test\\\\.\", \"\", &.extension)\n    assert_paths_raw(\"test.ext\\\\\", \".ext\\\\\", \".ext\", &.extension)\n  end\n\n  describe \"#absolute?\" do\n    assert_paths_raw(\"/foo\", true, false, &.absolute?)\n    assert_paths_raw(\"/./foo\", true, false, &.absolute?)\n\n    assert_paths_raw(\"foo\", false, &.absolute?)\n    assert_paths_raw(\"./foo\", false, &.absolute?)\n    assert_paths_raw(\"~/foo\", false, &.absolute?)\n\n    assert_paths_raw(\"\\\\foo\", false, &.absolute?)\n    assert_paths_raw(\"\\\\.\\\\foo\", false, &.absolute?)\n    assert_paths_raw(\"foo\", false, &.absolute?)\n    assert_paths_raw(\".\\\\foo\", false, &.absolute?)\n    assert_paths_raw(\"~\\\\foo\", false, &.absolute?)\n    assert_paths_raw(\"C:\", false, &.absolute?)\n    assert_paths_raw(\"C:foo\", false, &.absolute?)\n\n    assert_paths_raw(\"C:\\\\foo\", false, true, &.absolute?)\n    assert_paths_raw(\"C:/foo/bar\", false, true, &.absolute?)\n    assert_paths_raw(\"C:\\\\\", false, true, &.absolute?)\n    assert_paths_raw(\"C:/foo\", false, true, &.absolute?)\n    assert_paths_raw(\"C:/\", false, true, &.absolute?)\n    assert_paths_raw(\"c:\\\\\\\\\", false, true, &.absolute?)\n\n    assert_paths_raw(\"//some/share\", true, false, &.absolute?)\n    assert_paths_raw(\"\\\\\\\\some\\\\share\", false, false, &.absolute?)\n    assert_paths_raw(\"//some/share/\", true, true, &.absolute?)\n    assert_paths_raw(\"\\\\\\\\some\\\\share\\\\\", false, true, &.absolute?)\n\n    assert_paths_raw(\"//.\", true, false, &.absolute?)\n    assert_paths_raw(\"\\\\\\\\?\", false, false, &.absolute?)\n    assert_paths_raw(\"//./foo\", true, true, &.absolute?)\n    assert_paths_raw(\"\\\\\\\\.\\\\foo\", false, true, &.absolute?)\n    assert_paths_raw(\"//?/foo\", true, true, &.absolute?)\n    assert_paths_raw(\"\\\\\\\\?\\\\foo\", false, true, &.absolute?)\n  end\n\n  describe \"#drive\" do\n    assert_paths(\"C:\\\\foo\", nil, \"C:\", &.drive)\n    assert_paths(\"C:/foo\", nil, \"C:\", &.drive)\n    assert_paths(\"C:foo\", nil, \"C:\", &.drive)\n    assert_paths(\"/foo\", nil, nil, &.drive)\n    assert_paths(\"//foo\", nil, nil, &.drive)\n    assert_paths(\"//some/share\", nil, \"//some/share\", &.drive)\n    assert_paths(\"//some/share/\", nil, \"//some/share\", &.drive)\n    assert_paths(\"//some/share/foo/\", nil, \"//some/share\", &.drive)\n    assert_paths(\"///not-a/share/\", nil, nil, &.drive)\n    assert_paths(\"/not-a//share/\", nil, nil, &.drive)\n    assert_paths(\"\\\\\\\\some\\\\share\", nil, \"\\\\\\\\some\\\\share\", &.drive)\n    assert_paths(\"\\\\\\\\some\\\\share\\\\\", nil, \"\\\\\\\\some\\\\share\", &.drive)\n    assert_paths(\"\\\\\\\\some\\\\share\\\\foo\", nil, \"\\\\\\\\some\\\\share\", &.drive)\n    assert_paths(\"\\\\\\\\\\\\not-a\\\\share\", nil, nil, &.drive)\n    assert_paths(\"\\\\\\\\not-a\\\\\\\\share\", nil, nil, &.drive)\n    assert_paths(\"\\\\\\\\?\\\\\", nil, \"\\\\\\\\?\", &.drive)\n    assert_paths(\"\\\\\\\\.\\\\\", nil, \"\\\\\\\\.\", &.drive)\n    assert_paths(\"//?/\", nil, \"//?\", &.drive)\n    assert_paths(\"//./\", nil, \"//.\", &.drive)\n    assert_paths(\"//?\", nil, \"//?\", &.drive)\n    assert_paths(\"//.\", nil, \"//.\", &.drive)\n\n    assert_paths(\"\\\\\\\\some$\\\\share\\\\\", nil, \"\\\\\\\\some$\\\\share\", &.drive)\n    assert_paths(\"\\\\\\\\%10%20\\\\share\\\\\", nil, \"\\\\\\\\%10%20\\\\share\", &.drive)\n    assert_paths(\"\\\\\\\\_.-~!$;=&'()*+,aB1\\\\ !-.@^_`{}~#$%&'()aB1\\\\\", nil, \"\\\\\\\\_.-~!$;=&'()*+,aB1\\\\ !-.@^_`{}~#$%&'()aB1\", &.drive)\n    assert_paths(\"\\\\\\\\127.0.0.1\\\\share\\\\\", nil, \"\\\\\\\\127.0.0.1\\\\share\", &.drive)\n  end\n\n  describe \"#root\" do\n    assert_paths(\"C:\\\\foo\", nil, \"\\\\\", &.root)\n    assert_paths(\"C:/foo\", nil, \"/\", &.root)\n    assert_paths(\"C:foo\", nil, nil, &.root)\n    assert_paths(\"/foo\", \"/\", &.root)\n    assert_paths(\"//foo\", \"/\", &.root)\n    assert_paths(\"\\\\foo\", nil, \"\\\\\", &.root)\n    assert_paths(\"\\\\\\\\foo\", nil, \"\\\\\", &.root)\n    assert_paths(\"//some/share\", \"/\", nil, &.root)\n    assert_paths(\"\\\\\\\\some\\\\share\", nil, &.root)\n    assert_paths(\"//some/share/\", \"/\", \"/\", &.root)\n    assert_paths(\"\\\\\\\\some\\\\share\\\\\", nil, \"\\\\\", &.root)\n    assert_paths(\"\\\\\\\\?\\\\\", nil, \"\\\\\", &.root)\n    assert_paths(\"\\\\\\\\.\\\\\", nil, \"\\\\\", &.root)\n    assert_paths(\"//?/\", \"/\", \"/\", &.root)\n    assert_paths(\"//./\", \"/\", \"/\", &.root)\n    assert_paths(\"//?\", \"/\", nil, &.root)\n    assert_paths(\"//.\", \"/\", nil, &.root)\n  end\n\n  describe \"#anchor\" do\n    assert_paths(\"C:\\\\foo\", nil, \"C:\\\\\", &.anchor)\n    assert_paths(\"C:/foo\", nil, \"C:/\", &.anchor)\n    assert_paths(\"C:foo\", nil, \"C:\", &.anchor)\n    assert_paths(\"/foo\", \"/\", &.anchor)\n    assert_paths(\"\\\\foo\", nil, \"\\\\\", &.anchor)\n    assert_paths(\"//some/share\", \"/\", \"//some/share\", &.anchor)\n    assert_paths(\"//some/share/\", \"/\", \"//some/share/\", &.anchor)\n    assert_paths(\"\\\\\\\\some\\\\share\", nil, \"\\\\\\\\some\\\\share\", &.anchor)\n    assert_paths(\"\\\\\\\\some\\\\share\\\\\", nil, \"\\\\\\\\some\\\\share\\\\\", &.anchor)\n    assert_paths(\"\\\\\\\\?\\\\\", nil, \"\\\\\\\\?\\\\\", &.anchor)\n    assert_paths(\"\\\\\\\\.\\\\\", nil, \"\\\\\\\\.\\\\\", &.anchor)\n    assert_paths(\"//?/\", \"/\", \"//?/\", &.anchor)\n    assert_paths(\"//./\", \"/\", \"//./\", &.anchor)\n    assert_paths(\"//?\", \"/\", \"//?\", &.anchor)\n    assert_paths(\"//.\", \"/\", \"//.\", &.anchor)\n  end\n\n  describe \"#normalize\" do\n    describe \"path with forward slash\" do\n      describe \"already clean\" do\n        it_normalizes_path(\"\", \".\", \".\")\n        it_normalizes_path(\"abc\")\n        it_normalizes_path(\"abc/def\", windows: \"abc\\\\def\")\n        it_normalizes_path(\"a/b/c\", windows: \"a\\\\b\\\\c\")\n        it_normalizes_path(\".\")\n        it_normalizes_path(\"..\")\n        it_normalizes_path(\"../..\", windows: \"..\\\\..\")\n        it_normalizes_path(\"../../abc\", windows: \"..\\\\..\\\\abc\")\n        it_normalizes_path(\"/abc\", windows: \"\\\\abc\")\n        it_normalizes_path(\"/\", windows: \"\\\\\")\n      end\n\n      describe \"removes trailing slash\" do\n        it_normalizes_path(\"abc/\", \"abc\", \"abc\")\n        it_normalizes_path(\"abc/def/\", \"abc/def\", \"abc\\\\def\")\n        it_normalizes_path(\"a/b/c/\", \"a/b/c\", \"a\\\\b\\\\c\")\n        it_normalizes_path(\"./\", \".\", \".\")\n        it_normalizes_path(\"../\", \"..\", \"..\")\n        it_normalizes_path(\"../../\", \"../..\", \"..\\\\..\")\n        it_normalizes_path(\"/abc/\", \"/abc\", \"\\\\abc\")\n      end\n\n      describe \"removes double slash\" do\n        it_normalizes_path(\"abc//def//ghi\", \"abc/def/ghi\", \"abc\\\\def\\\\ghi\")\n        it_normalizes_path(\"//abc\", \"/abc\", \"\\\\abc\")\n        it_normalizes_path(\"///abc\", \"/abc\", \"\\\\abc\")\n        it_normalizes_path(\"//abc//\", \"/abc\", \"\\\\abc\")\n        it_normalizes_path(\"abc//\", \"abc\", \"abc\")\n      end\n\n      describe \"removes .\" do\n        it_normalizes_path(\"abc/./def\", \"abc/def\", \"abc\\\\def\")\n        it_normalizes_path(\"/./abc/def\", \"/abc/def\", \"\\\\abc\\\\def\")\n        it_normalizes_path(\"abc/.\", \"abc\", \"abc\")\n      end\n\n      describe \"removes ..\" do\n        it_normalizes_path(\"abc/def/ghi/../jkl\", \"abc/def/jkl\", \"abc\\\\def\\\\jkl\")\n        it_normalizes_path(\"abc/def/../ghi/../jkl\", \"abc/jkl\", \"abc\\\\jkl\")\n        it_normalizes_path(\"abc/def/..\", \"abc\", \"abc\")\n        it_normalizes_path(\"abc/def/../..\", \".\", \".\")\n        it_normalizes_path(\"/abc/def/../..\", \"/\", \"\\\\\")\n        it_normalizes_path(\"abc/def/../../..\", \"..\", \"..\")\n        it_normalizes_path(\"/abc/def/../../..\", \"/\", \"\\\\\")\n        it_normalizes_path(\"abc/def/../../../ghi/jkl/../../../mno\", \"../../mno\", \"..\\\\..\\\\mno\")\n      end\n\n      describe \"combinations\" do\n        it_normalizes_path(\"abc/./../def\", \"def\", \"def\")\n        it_normalizes_path(\"abc//./../def\", \"def\", \"def\")\n        it_normalizes_path(\"abc/../../././../def\", \"../../def\", \"..\\\\..\\\\def\")\n      end\n    end\n\n    describe \"paths with backslash\" do\n      describe \"already clean\" do\n        it_normalizes_path(\"abc\\\\def\")\n        it_normalizes_path(\"a\\\\b\\\\c\")\n        it_normalizes_path(\"..\\\\..\")\n        it_normalizes_path(\"..\\\\..\\\\abc\")\n        it_normalizes_path(\"\\\\abc\")\n        it_normalizes_path(\"\\\\\")\n      end\n\n      describe \"removes trailing slash\" do\n        it_normalizes_path(\"abc\\\\\", windows: \"abc\")\n        it_normalizes_path(\"abc\\\\def\\\\\", windows: \"abc\\\\def\")\n        it_normalizes_path(\"a\\\\b\\\\c\\\\\", windows: \"a\\\\b\\\\c\")\n        it_normalizes_path(\".\\\\\", windows: \".\")\n        it_normalizes_path(\"..\\\\\", windows: \"..\")\n        it_normalizes_path(\"..\\\\..\\\\\", windows: \"..\\\\..\")\n        it_normalizes_path(\"\\\\abc\\\\\", windows: \"\\\\abc\")\n      end\n\n      describe \"removes double slash\" do\n        it_normalizes_path(\"abc\\\\\\\\def\\\\\\\\ghi\", windows: \"abc\\\\def\\\\ghi\")\n        it_normalizes_path(\"\\\\\\\\abc\", windows: \"\\\\abc\")\n        it_normalizes_path(\"\\\\\\\\\\\\abc\", windows: \"\\\\abc\")\n        it_normalizes_path(\"\\\\\\\\abc\\\\\\\\\", windows: \"\\\\abc\")\n        it_normalizes_path(\"abc\\\\\\\\\", windows: \"abc\")\n      end\n\n      describe \"removes .\" do\n        it_normalizes_path(\"abc\\\\.\\\\def\", windows: \"abc\\\\def\")\n        it_normalizes_path(\"\\\\.\\\\abc\\\\def\", windows: \"\\\\abc\\\\def\")\n        it_normalizes_path(\"abc\\\\.\", windows: \"abc\")\n      end\n\n      describe \"removes ..\" do\n        it_normalizes_path(\"abc\\\\def\\\\ghi\\\\..\\\\jkl\", windows: \"abc\\\\def\\\\jkl\")\n        it_normalizes_path(\"abc\\\\def\\\\..\\\\ghi\\\\..\\\\jkl\", windows: \"abc\\\\jkl\")\n        it_normalizes_path(\"abc\\\\def\\\\..\", windows: \"abc\")\n        it_normalizes_path(\"abc\\\\def\\\\..\\\\..\", windows: \".\")\n        it_normalizes_path(\"\\\\abc\\\\def\\\\..\\\\..\", windows: \"\\\\\")\n        it_normalizes_path(\"abc\\\\def\\\\..\\\\..\\\\..\", windows: \"..\")\n        it_normalizes_path(\"\\\\abc\\\\def\\\\..\\\\..\\\\..\", windows: \"\\\\\")\n        it_normalizes_path(\"abc\\\\def\\\\..\\\\..\\\\..\\\\ghi\\\\jkl\\\\..\\\\..\\\\..\\\\mno\", windows: \"..\\\\..\\\\mno\")\n      end\n\n      describe \"combinations\" do\n        it_normalizes_path(\"abc\\\\.\\\\..\\\\def\", windows: \"def\")\n        it_normalizes_path(\"abc\\\\\\\\.\\\\..\\\\def\", windows: \"def\")\n        it_normalizes_path(\"abc\\\\..\\\\..\\\\.\\\\.\\\\..\\\\def\", windows: \"..\\\\..\\\\def\")\n      end\n    end\n\n    describe \"with drive\" do\n      it_normalizes_path(\"C:\", \"C:\")\n      it_normalizes_path(\"C:\\\\\", \"C:\\\\\")\n      it_normalizes_path(\"C:/\", \"C:\", \"C:\\\\\")\n      it_normalizes_path(\"C:foo\", \"C:foo\")\n      it_normalizes_path(\"C:\\\\foo\", \"C:\\\\foo\")\n      it_normalizes_path(\"C:/foo\", \"C:/foo\", \"C:\\\\foo\")\n    end\n\n    describe \"windows local device paths\" do\n      it_normalizes_path(\"\\\\\\\\.\\\\C:\\\\..\\\\D:\\\\foo\\\\.\\\\bar\", windows: \"\\\\\\\\.\\\\D:\\\\foo\\\\bar\")\n      it_normalizes_path(\"//./c:/\", \"/c:\", windows: \"\\\\\\\\.\\\\c:\\\\\")\n      it_normalizes_path(\"//?/c:\", \"/?/c:\", windows: \"\\\\\\\\?\\\\c:\")\n      it_normalizes_path(\"\\\\/.\\\\c:/\\\\\", windows: \"\\\\\\\\.\\\\c:\\\\\")\n      it_normalizes_path(\"\\\\\\\\?\\\\c:\\\\\")\n    end\n  end\n\n  describe \"#join\" do\n    it_joins_path(\"\", \"\", \"/\", \"\\\\\")\n    it_joins_path(\"/\", \"\", \"/\")\n    it_joins_path(\"\", \"/\", \"/\")\n    it_joins_path(\"foo\", {\"bar\", \"\"}, \"foo/bar/\", \"foo\\\\bar\\\\\")\n    it_joins_path(\"foo\", {\"bar\", \"\"}, \"foo/bar/\", \"foo\\\\bar\\\\\")\n    it_joins_path(\"///foo\", \"bar\", \"///foo/bar\", \"///foo\\\\bar\")\n    it_joins_path(\"///foo\", \"//bar\", \"///foo//bar\")\n    it_joins_path(\"/foo/\", \"/bar\", \"/foo/bar\")\n    it_joins_path(\"foo\", \"/\", \"foo/\")\n    it_joins_path(\"foo\", {\"bar\", \"baz\"}, \"foo/bar/baz\", \"foo\\\\bar\\\\baz\")\n    it_joins_path(\"foo\", {\"//bar//\", \"baz///\"}, \"foo//bar//baz///\")\n    it_joins_path(\"/foo/\", {\"/bar/\", \"/baz/\"}, \"/foo/bar/baz/\")\n    it_joins_path(\"\", \"a\", \"a\")\n    it_joins_path(\"/\", \"a\", \"/a\")\n    it_joins_path(\"\", \"/a\", \"/a\")\n    it_joins_path(\"foo\", {\"/\", \"bar\"}, \"foo/bar\")\n    it_joins_path(\"foo\", {\"/\", \"/\", \"bar\"}, \"foo/bar\")\n    it_joins_path(\"/\", {\"/foo\", \"/\", \"bar/\", \"/\"}, \"/foo/bar/\")\n    it_joins_path(\"c:/\", \"Program Files\", \"c:/Program Files\")\n    it_joins_path(\"c:\", \"Program Files\", \"c:/Program Files\", \"c:\\\\Program Files\")\n\n    it_joins_path(\"\\\\\\\\\\\\\\\\foo\", \"bar\", \"\\\\\\\\\\\\\\\\foo/bar\", \"\\\\\\\\\\\\\\\\foo\\\\bar\")\n    it_joins_path(\"\\\\\\\\\\\\foo\", \"\\\\\\\\bar\", \"\\\\\\\\\\\\foo/\\\\\\\\bar\", \"\\\\\\\\\\\\foo\\\\\\\\bar\")\n    it_joins_path(\"\\\\foo\\\\\", \"\\\\bar\", \"\\\\foo\\\\/\\\\bar\", \"\\\\foo\\\\bar\")\n    it_joins_path(\"foo\", \"\\\\\", \"foo/\\\\\", \"foo\\\\\")\n    it_joins_path(\"foo\", {\"\\\\\\\\bar\\\\\\\\\", \"baz\\\\\\\\\\\\\"}, \"foo/\\\\\\\\bar\\\\\\\\/baz\\\\\\\\\\\\\", \"foo\\\\\\\\bar\\\\\\\\baz\\\\\\\\\\\\\")\n    it_joins_path(\"\\\\foo\\\\\", {\"\\\\bar\\\\\", \"\\\\baz\\\\\"}, \"\\\\foo\\\\/\\\\bar\\\\/\\\\baz\\\\\", \"\\\\foo\\\\bar\\\\baz\\\\\")\n    it_joins_path(\"\\\\\", \"a\", \"\\\\/a\", \"\\\\a\")\n    it_joins_path(\"\", \"\\\\a\", \"\\\\a\")\n    it_joins_path(\"foo\", {\"\\\\\", \"bar\"}, \"foo/\\\\/bar\", \"foo\\\\bar\")\n    it_joins_path(\"foo\", {\"\\\\\", \"\\\\\", \"bar\"}, \"foo/\\\\/\\\\/bar\", \"foo\\\\bar\")\n    it_joins_path(\"\\\\\", {\"\\\\foo\", \"\\\\\", \"bar\\\\\", \"\\\\\"}, \"\\\\/\\\\foo/\\\\/bar\\\\/\\\\\", \"\\\\foo\\\\bar\\\\\")\n    it_joins_path(\"c:\\\\\", \"Program Files\", \"c:\\\\/Program Files\", \"c:\\\\Program Files\")\n\n    it_joins_path(\"foo\", Path.windows(\"bar\\\\baz\"), \"foo/bar/baz\", \"foo\\\\bar\\\\baz\")\n    it_joins_path(\"foo\", Path.posix(\"bar\\\\baz\"), \"foo/bar\\\\baz\", \"foo\\\\bar\\uF05Cbaz\")\n    it_joins_path(\"foo\", Path.posix(\"bar/baz\"), \"foo/bar/baz\", \"foo\\\\bar/baz\")\n  end\n\n  describe \"#expand\" do\n    describe \"converts a pathname to an absolute pathname\" do\n      it_expands_path(\"\", BASE_POSIX, BASE_WINDOWS)\n      it_expands_path(\"a\", {BASE_POSIX, \"a\"}, {BASE_WINDOWS, \"a\"})\n      it_expands_path(\"a\", {BASE_POSIX, \"a\"}, {BASE_WINDOWS, \"a\"}, base: nil)\n    end\n\n    describe \"converts a pathname to an absolute pathname, Ruby-Talk:18512\" do\n      it_expands_path(\".a\", {BASE_POSIX, \".a\"}, {BASE_WINDOWS, \".a\"})\n      it_expands_path(\"..a\", {BASE_POSIX, \"..a\"}, {BASE_WINDOWS, \"..a\"})\n      it_expands_path(\"a../b\", {BASE_POSIX, \"a../b\"}, {BASE_WINDOWS, \"a..\\\\b\"})\n    end\n\n    describe \"keeps trailing dots on absolute pathname\" do\n      it_expands_path(\"a.\", {BASE_POSIX, \"a.\"}, {BASE_WINDOWS, \"a.\"})\n      it_expands_path(\"a..\", {BASE_POSIX, \"a..\"}, {BASE_WINDOWS, \"a..\"})\n    end\n\n    describe \"converts a pathname to an absolute pathname, using a complete path\" do\n      it_expands_path(\"\", \"/tmp\", \"\\\\tmp\", base: Path.posix(\"/tmp\"))\n      it_expands_path(\"\", \"C:/tmp\", \"C:\\\\tmp\", base: Path.windows(\"C:\\\\tmp\"))\n      it_expands_path(\"a\", \"/tmp/a\", \"\\\\tmp\\\\a\", base: Path.posix(\"/tmp\"))\n      it_expands_path(\"a\", \"C:/tmp/a\", \"C:\\\\tmp\\\\a\", base: Path.windows(\"C:\\\\tmp\"))\n      it_expands_path(\"../a\", \"/tmp/a\", \"\\\\tmp\\\\a\", base: Path.posix(\"/tmp/xxx\"))\n      it_expands_path(\"../a\", \"C:/tmp/a\", \"C:\\\\tmp\\\\a\", base: Path.windows(\"C:\\\\tmp\\\\xxx\"))\n      it_expands_path(\"../a\", \"/tmp/a\", \"\\\\tmp\\\\a\", base: Path.posix(\"/tmp/xxx\"))\n      it_expands_path(\"../a\", \"C:/tmp/a\", \"C:\\\\tmp\\\\a\", base: Path.windows(\"C:\\\\tmp\\\\xxx\"))\n      it_expands_path(\".\", \"/\", \"\\\\\", base: Path.posix(\"/\"))\n      pending { it_expands_path(\".\", \"C:/\", \"C:\\\\\", base: Path.windows(\"C:\\\\\")) }\n    end\n\n    describe \"expands a path with multi-byte characters\" do\n      it_expands_path(\"Ångström\", \"#{BASE_POSIX}/Ångström\", \"#{BASE_WINDOWS}\\\\Ångström\")\n    end\n\n    describe \"expands /./dir to /dir\" do\n      it_expands_path(\"/./dir\", \"/dir\", \"\\\\dir\", base: \"/\")\n    end\n\n    describe \"replaces multiple / with a single /\" do\n      it_expands_path(\"//some/path\", \"/some/path\", \"\\\\\\\\some\\\\path#{BASE_WINDOWS}\\\\\") # Windows path is UNC share\n      it_expands_path(\"////some/path\", \"/some/path\", \"\\\\some\\\\path\")\n      it_expands_path(\"/some////path\", \"/some/path\", \"\\\\some\\\\path\")\n    end\n\n    describe \"expand path with ..\" do\n      it_expands_path(\"../../bin\", \"/bin\", \"\\\\bin\", base: \"/tmp/x\")\n      it_expands_path(\"../../bin\", \"/bin\", \"\\\\bin\", base: \"/tmp\")\n      it_expands_path(\"../../bin\", \"/bin\", \"\\\\bin\", base: \"/\")\n      it_expands_path(\"../bin\", {Dir.current.gsub('\\\\', '/'), \"tmp\", \"bin\"}, {Path.windows(Dir.current).normalize.to_s, \"tmp\", \"bin\"}, base: \"tmp/x\", expand_base: true)\n      it_expands_path(\"../bin\", {Dir.current.gsub('\\\\', '/'), \"bin\"}, {Path.windows(Dir.current).normalize.to_s, \"bin\"}, base: \"x/../tmp\", expand_base: true)\n    end\n\n    describe \"expand_path for common unix path gives a full path\" do\n      it_expands_path(\"/tmp/\", \"/tmp/\", \"\\\\tmp\\\\\")\n      it_expands_path(\"/tmp/../../../tmp\", \"/tmp\", \"\\\\tmp\")\n      it_expands_path(\"\", BASE_POSIX, BASE_WINDOWS)\n      it_expands_path(\"./////\", \"#{BASE_POSIX}/\", \"#{BASE_WINDOWS}\\\\\")\n      it_expands_path(\".\", BASE_POSIX, BASE_WINDOWS)\n      it_expands_path(BASE_POSIX, BASE_POSIX, BASE_POSIX.gsub('/', '\\\\'))\n    end\n\n    describe \"with drive\" do\n      it_expands_path(\"foo\", \"D:/foo\", \"D:foo\", base: \"D:\")\n      it_expands_path(\"/foo\", \"/foo\", \"D:\\\\foo\", base: \"D:\")\n      it_expands_path(\"\\\\foo\", \"D:/\\\\foo\", \"D:\\\\foo\", base: \"D:\")\n      it_expands_path(\"foo\", \"D:\\\\/foo\", \"D\\uF03A\\uF05C\\\\foo\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"foo\", \"D:/foo\", \"D:\\\\foo\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"foo\", \"D:/foo\", \"D:\\\\foo\", base: \"D:/\")\n      it_expands_path(\"/foo\", \"/foo\", \"D:\\\\foo\", base: \"D:\\\\\")\n      it_expands_path(\"\\\\foo\", \"D:\\\\/\\\\foo\", \"\\\\foo\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"\\\\foo\", \"D:/\\\\foo\", \"D:\\\\foo\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"/foo\", \"/foo\", \"D:\\\\foo\", base: \"D:/\")\n      it_expands_path(\"\\\\foo\", \"D:/\\\\foo\", \"D:\\\\foo\", base: \"D:/\")\n\n      it_expands_path(\"C:\", \"D:/C:\", \"C:\", base: \"D:\")\n      it_expands_path(\"C:\", \"D:/C:\", \"C:\\\\\", base: \"D:/\")\n      it_expands_path(\"C:\", \"D:\\\\/C:\", \"C:D\\uF03A\\uF05C\\\\\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"C:\", \"D:/C:\", \"C:\\\\\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"C:/\", \"D:/C:/\", \"C:\\\\\", base: \"D:\")\n      it_expands_path(\"C:/\", \"D:/C:/\", \"C:\\\\\", base: \"D:/\")\n      it_expands_path(\"C:/\", \"D:\\\\/C:/\", \"C:\\\\\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"C:/\", \"D:/C:/\", \"C:\\\\\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"C:\\\\\", \"D:/C:\\\\\", \"C:\\\\\", base: \"D:\")\n      it_expands_path(\"C:\\\\\", \"D:/C:\\\\\", \"C:\\\\\", base: \"D:/\")\n      it_expands_path(\"C:\\\\\", \"D:\\\\/C:\\\\\", \"C:\\\\\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"C:\\\\\", \"D:/C:\\\\\", \"C:\\\\\", base: Path.windows(\"D:\\\\\"))\n\n      it_expands_path(\"C:foo\", \"D:/C:foo\", \"C:foo\", base: \"D:\")\n      it_expands_path(\"C:/foo\", \"D:/C:/foo\", \"C:\\\\foo\", base: \"D:\")\n      it_expands_path(\"C:\\\\foo\", \"D:/C:\\\\foo\", \"C:\\\\foo\", base: \"D:\")\n      it_expands_path(\"C:foo\", \"D:\\\\/C:foo\", \"C:D\\uF03A\\uF05C\\\\foo\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"C:foo\", \"D:/C:foo\", \"C:\\\\foo\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"C:foo\", \"D:/C:foo\", \"C:\\\\foo\", base: \"D:/\")\n      it_expands_path(\"C:/foo\", \"D:\\\\/C:/foo\", \"C:\\\\foo\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"C:/foo\", \"D:/C:/foo\", \"C:\\\\foo\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"C:\\\\foo\", \"D:\\\\/C:\\\\foo\", \"C:\\\\foo\", base: Path.posix(\"D:\\\\\"))\n      it_expands_path(\"C:\\\\foo\", \"D:/C:\\\\foo\", \"C:\\\\foo\", base: Path.windows(\"D:\\\\\"))\n      it_expands_path(\"C:/foo\", \"D:/C:/foo\", \"C:\\\\foo\", base: \"D:/\")\n      it_expands_path(\"C:\\\\foo\", \"D:/C:\\\\foo\", \"C:\\\\foo\", base: \"D:/\")\n    end\n\n    describe \"UNC path\" do\n      it_expands_path(\"baz\", \"/foo/bar/baz\", \"\\\\\\\\foo\\\\bar\\\\baz\", base: Path.windows(\"\\\\\\\\foo\\\\bar\\\\\"))\n      it_expands_path(\"baz\", \"/foo$/bar/baz\", \"\\\\\\\\foo$\\\\bar\\\\baz\", base: Path.windows(\"\\\\\\\\foo$\\\\bar\\\\\"))\n    end\n\n    it \"doesn't expand ~\" do\n      [Path[\"~\"], Path[\"~\", \"foo\"]].each do |path|\n        path.expand(base: \"\", expand_base: false).should eq path\n      end\n    end\n\n    describe \"checks all possible types for expand(home:)\" do\n      home_posix2 = Path.posix(BASE_POSIX).join(\"foo\").to_s\n      home_windows2 = Path.windows(BASE_WINDOWS).join(\"foo\").to_s\n\n      home = Path[\"\"].windows? ? home_windows2 : home_posix2\n      it_expands_path(\"~/a\", {BASE_POSIX, \"~/a\"}, {BASE_WINDOWS, \"~\\\\a\"}, home: false)\n      it_expands_path(\"~/a\", {home_posix2, \"a\"}, {home_windows2, \"a\"}, home: home)\n      it_expands_path(\"~/a\", {home_posix2, \"a\"}, {home_windows2, \"a\"}, home: Path[home])\n    end\n\n    describe \"converts a pathname to an absolute pathname, using ~ (home) as base\" do\n      it_expands_path(\"~/\", {HOME_POSIX, \"\"}, {HOME_WINDOWS, \"\"}, home: true)\n      it_expands_path(\"~/..badfilename\", {HOME_POSIX, \"..badfilename\"}, {HOME_WINDOWS, \"..badfilename\"}, home: true)\n      it_expands_path(\"..\", \"/default\", \"\\\\default\", home: true)\n      it_expands_path(\"~/a\", {HOME_POSIX, \"a\"}, {HOME_WINDOWS, \"a\"}, base: \"~/b\", home: true)\n      it_expands_path(\"~\", HOME_POSIX, HOME_WINDOWS, home: true)\n      it_expands_path(\"~\", HOME_POSIX, HOME_WINDOWS, base: \"/tmp/gumby/ddd\", home: true)\n      it_expands_path(\"~/a\", {HOME_POSIX, \"a\"}, {HOME_WINDOWS, \"a\"}, base: \"/tmp/gumby/ddd\", home: true)\n    end\n\n    describe \"converts a pathname to an absolute pathname, using ~ (home) as base (trailing /)\" do\n      it_expands_path(\"~/\", {HOME_POSIX, \"\"}, {HOME_WINDOWS, \"\"}, home: true)\n      it_expands_path(\"~/..badfilename\", {\"#{HOME_POSIX}/\", \"..badfilename\"}, {\"#{HOME_WINDOWS}\\\\\", \"..badfilename\"}, base: \"\", home: true)\n      it_expands_path(\"~/..\", \"/home\", \"C:\\\\Users\", home: true)\n      it_expands_path(\"~/a\", {HOME_POSIX, \"a\"}, {HOME_WINDOWS, \"a\"}, base: \"~/b\", home: true)\n      it_expands_path(\"~\", HOME_POSIX, HOME_WINDOWS, home: true)\n      it_expands_path(\"~\", HOME_POSIX, HOME_WINDOWS, base: \"/tmp/gumby/ddd\", home: true)\n      it_expands_path(\"~/a\", {HOME_POSIX, \"a\"}, {HOME_WINDOWS, \"a\"}, base: \"/tmp/gumby/ddd\", home: true)\n    end\n\n    describe \"converts a pathname to an absolute pathname, using ~ (home) as base (HOME=/)\" do\n      it_expands_path(\"~/\", \"/\", \"\\\\\", env_home: \"/\", home: true)\n      it_expands_path(\"~/..badfilename\", \"/..badfilename\", \"\\\\..badfilename\", env_home: \"/\", home: true)\n      it_expands_path(\"..\", \"/default\", \"\\\\default\", env_home: \"/\", home: true)\n      it_expands_path(\"~/a\", \"/a\", \"\\\\a\", base: \"~/b\", env_home: \"/\", home: true)\n      it_expands_path(\"~\", \"/\", \"\\\\\", env_home: \"/\", home: true)\n      it_expands_path(\"~\", \"/\", \"\\\\\", base: \"/tmp/gumby/ddd\", env_home: \"/\", home: true)\n      it_expands_path(\"~/a\", \"/a\", \"\\\\a\", base: \"/tmp/gumby/ddd\", env_home: \"/\", home: true)\n    end\n\n    describe \"ignores name starting with ~\" do\n      it_expands_path(\"~foo.txt\", \"/current/~foo.txt\", \"\\\\current\\\\~foo.txt\", base: \"/current\", env_home: \"/\")\n    end\n\n    describe %q(supports ~\\ for Windows paths only) do\n      it_expands_path(\"~\\\\a\", {BASE_POSIX, \"~\\\\a\"}, {HOME_WINDOWS, \"a\"}, home: true)\n    end\n  end\n\n  describe \"#<=>\" do\n    it \"case sensitivity\" do\n      (Path.posix(\"foo\") <=> Path.posix(\"FOO\")).should eq 1\n      (Path.windows(\"foo\") <=> Path.windows(\"FOO\")).should eq 0\n      (Path.windows(\"foo\") <=> Path.posix(\"FOO\")).should eq 1\n      (Path.posix(\"foo\") <=> Path.windows(\"FOO\")).should eq -1\n    end\n  end\n\n  describe \"#==\" do\n    it \"simple\" do\n      Path.posix(\"foo\").should eq Path.posix(\"foo\")\n      Path.windows(\"foo\").should eq Path.windows(\"foo\")\n      Path.windows(\"foo\").should_not eq Path.posix(\"foo\")\n      Path.posix(\"foo\").should_not eq Path.windows(\"foo\")\n    end\n\n    it \"case sensitivity\" do\n      Path.posix(\"foo\").should_not eq Path.posix(\"FOO\")\n      Path.windows(\"foo\").should eq Path.windows(\"FOO\")\n      Path.windows(\"foo\").should_not eq Path.posix(\"FOO\")\n      Path.posix(\"foo\").should_not eq Path.windows(\"FOO\")\n    end\n  end\n\n  describe \"#ends_with_separator?\" do\n    assert_paths_raw(\"foo\", false, &.ends_with_separator?)\n    assert_paths_raw(\"foo/\", true, &.ends_with_separator?)\n    assert_paths_raw(\"foo\\\\\", false, true, &.ends_with_separator?)\n    assert_paths_raw(\"C:/\", true, &.ends_with_separator?)\n    assert_paths_raw(\"foo/bar\", false, &.ends_with_separator?)\n    assert_paths_raw(\"foo/.\", false, &.ends_with_separator?)\n  end\n\n  describe \"#to_windows\" do\n    assert_paths_raw(\"C:\\\\foo\\\\bar\", Path.windows(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\"), Path.windows(\"C:\\\\foo\\\\bar\"), label: \"default: mappings=true\", &.to_windows)\n\n    assert_paths_raw(\"foo/bar\", Path.windows(\"foo/bar\"), &.to_windows(mappings: true))\n    assert_paths_raw(\"C:\\\\foo\\\\bar\", Path.windows(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\"), Path.windows(\"C:\\\\foo\\\\bar\"), &.to_windows(mappings: true))\n    assert_paths_raw(%(\"*/:<>?\\\\| ), Path.windows(\"\\uF022\\uF02A/\\uF03A\\uF03C\\uF03E\\uF03F\\uF05C\\uF07C\\uF020\"), Path.windows(%(\"*/:<>?\\\\| )), &.to_windows(mappings: true))\n\n    assert_paths_raw(\"foo/bar\", Path.windows(\"foo/bar\"), &.to_windows(mappings: false))\n    assert_paths_raw(\"C:\\\\foo\\\\bar\", Path.windows(\"C:\\\\foo\\\\bar\"), &.to_windows(mappings: false))\n    assert_paths_raw(%(\"*/:<>?\\\\| ), Path.windows(%(\"*/:<>?\\\\| )), &.to_windows(mappings: false))\n  end\n\n  describe \"#to_posix\" do\n    assert_paths_raw(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\", Path.posix(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\"), Path.posix(\"C:\\\\foo\\\\bar\"), label: \"default: mappings=true\", &.to_posix)\n\n    assert_paths_raw(\"foo/bar\", Path.posix(\"foo/bar\"), &.to_posix(mappings: true))\n    assert_paths_raw(\"C:\\\\foo\\\\bar\", Path.posix(\"C:\\\\foo\\\\bar\"), Path.posix(\"C:/foo/bar\"), &.to_posix(mappings: true))\n    assert_paths_raw(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\", Path.posix(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\"), Path.posix(\"C:\\\\foo\\\\bar\"), &.to_posix(mappings: true))\n    assert_paths_raw(\"\\uF022\\uF02A/\\uF03A\\uF03C\\uF03E\\uF03F\\uF05C\\uF07C\\uF020\", Path.posix(\"\\uF022\\uF02A/\\uF03A\\uF03C\\uF03E\\uF03F\\uF05C\\uF07C\\uF020\"), Path.posix(%(\"*/:<>?\\\\| )), &.to_posix(mappings: true))\n\n    assert_paths_raw(\"foo/bar\", Path.posix(\"foo/bar\"), &.to_posix(mappings: false))\n    assert_paths_raw(\"C:\\\\foo\\\\bar\", Path.posix(\"C:\\\\foo\\\\bar\"), Path.posix(\"C:/foo/bar\"), &.to_posix(mappings: false))\n    assert_paths_raw(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\", Path.posix(\"C\\uF03A\\uF05Cfoo\\uF05Cbar\"), &.to_posix(mappings: false))\n    assert_paths_raw(\"\\uF022\\uF02A/\\uF03A\\uF03C\\uF03E\\uF03F\\uF05C\\uF07C\\uF020\", Path.posix(\"\\uF022\\uF02A/\\uF03A\\uF03C\\uF03E\\uF03F\\uF05C\\uF07C\\uF020\"), &.to_posix(mappings: false))\n  end\n\n  describe \"#relative_to?\" do\n    it_relativizes(\"a/b\", \"a/b/c\", \"c\")\n    it_relativizes(\"a/b\", \"a/b\", \".\")\n    it_relativizes(\"a/b/.\", \"a/b\", \".\")\n    it_relativizes(\"a/b\", \"a/b/.\", \".\")\n    it_relativizes(\"./a/b\", \"a/b\", \".\")\n    it_relativizes(\"a/b\", \"./a/b\", \".\")\n    it_relativizes(\"ab/cd\", \"ab/cde\", \"../cde\", \"..\\\\cde\")\n    it_relativizes(\"ab/cd\", \"ab/c\", \"../c\", \"..\\\\c\")\n    it_relativizes(\"a/b\", \"a/b/c/d\", \"c/d\", \"c\\\\d\")\n    it_relativizes(\"a/b\", \"a/b/../c\", \"../c\", \"..\\\\c\")\n    it_relativizes(\"a/b/../c\", \"a/b\", \"../b\", \"..\\\\b\")\n    it_relativizes(\"a/b/c\", \"a/c/d\", \"../../c/d\", \"..\\\\..\\\\c\\\\d\")\n    it_relativizes(\"a/b\", \"c/d\", \"../../c/d\", \"..\\\\..\\\\c\\\\d\")\n    it_relativizes(\"a/b/c/d\", \"a/b\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"a/b/c/d\", \"a/b/\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"a/b/c/d/\", \"a/b\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"a/b/c/d/\", \"a/b/\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"../../a/b\", \"../../a/b/c/d\", \"c/d\", \"c\\\\d\")\n    it_relativizes(\"/a/b\", \"/a/b\", \".\")\n    it_relativizes(\"/a/b/.\", \"/a/b\", \".\")\n    it_relativizes(\"/a/b\", \"/a/b/.\", \".\")\n    it_relativizes(\"/ab/cd\", \"/ab/cde\", \"../cde\", \"..\\\\cde\")\n    it_relativizes(\"/ab/cd\", \"/ab/c\", \"../c\", \"..\\\\c\")\n    it_relativizes(\"/a/b\", \"/a/b/c/d\", \"c/d\", \"c\\\\d\")\n    it_relativizes(\"/a/b\", \"/a/b/../c\", \"../c\", \"..\\\\c\")\n    it_relativizes(\"/a/b/../c\", \"/a/b\", \"../b\", \"..\\\\b\")\n    it_relativizes(\"/a/b/c\", \"/a/c/d\", \"../../c/d\", \"..\\\\..\\\\c\\\\d\")\n    it_relativizes(\"/a/b\", \"/c/d\", \"../../c/d\", \"..\\\\..\\\\c\\\\d\")\n    it_relativizes(\"/a/b/c/d\", \"/a/b\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"/a/b/c/d\", \"/a/b/\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"/a/b/c/d/\", \"/a/b\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"/a/b/c/d/\", \"/a/b/\", \"../..\", \"..\\\\..\")\n    it_relativizes(\"/../../a/b\", \"/../../a/b/c/d\", \"c/d\", \"c\\\\d\")\n    it_relativizes(\"/\", \"/a/c\", \"a/c\", \"a\\\\c\")\n    it_relativizes(\"/\", \"/\", \".\")\n    it_relativizes(\".\", \"a/b\", \"a/b\", \"a\\\\b\")\n    it_relativizes(\".\", \"..\", \"..\")\n    # can't do purely lexically\n    it_relativizes(\"..\", \".\", nil)\n    it_relativizes(\"..\", \"a\", nil)\n    it_relativizes(\"../..\", \"..\", nil)\n    it_relativizes(\"a\", \"/a\", nil)\n\n    describe \"special windows paths\" do\n      it_relativizes(\"/a\", \"a\", nil)\n      it_relativizes(\"C:a\\\\b\\\\c\", \"C:a/b/d\", \"../C:a/b/d\", \"..\\\\d\")\n      it_relativizes(\"C:a\\\\b\\\\c\", \"c:a/b/d\", \"../c:a/b/d\", \"..\\\\d\")\n      it_relativizes(\"C:\\\\\", \"D:\\\\\", \"../D:\\\\\", nil)\n      it_relativizes(\"C:\", \"D:\", \"../D:\", nil)\n      it_relativizes(\"C:\\\\Projects\", \"c:\\\\projects\\\\src\", \"../c:\\\\projects\\\\src\", \"src\")\n      it_relativizes(\"C:\\\\Projects\", \"c:\\\\projects\", \"../c:\\\\projects\", \".\")\n      it_relativizes(\"C:\\\\Projects\\\\a\\\\..\", \"c:\\\\projects\", \"../c:\\\\projects\", \".\")\n      it_relativizes(\"C:\\\\foo\", \"C:/bar\", \"../C:/bar\", \"..\\\\bar\")\n    end\n  end\n\n  describe \"#relative_to\" do\n    it \"relativizable paths\" do\n      Path.posix(\"a/b/c\").relative_to(\"a/b\").should eq Path.posix(\"c\")\n      Path.windows(\"a\\\\b\\\\c\").relative_to(\"a\\\\b\").should eq Path.windows(\"c\")\n    end\n\n    it \"mixed input paths\" do\n      Path.posix(\"a/b/c\").relative_to(Path.windows(\"a\\\\b\")).should eq Path.posix(\"c\")\n      Path.windows(\"a\\\\b\\\\c\").relative_to(Path.posix(\"a/b\")).should eq Path.windows(\"c\")\n    end\n\n    it \"paths that can't be relativized\" do\n      path = Path.posix(\".\")\n      path.relative_to(Path.posix(\"/cwd\")).should eq path\n      path = Path.windows(\".\")\n      path.relative_to(Path.windows(\"/cwd\")).should eq path\n      path = Path.windows(\".\")\n      path.relative_to(Path.windows(\"C:/cwd\")).should eq path\n      path = Path.windows(\".\")\n      path.relative_to(Path.windows(\"C:cwd\")).should eq path\n    end\n  end\n\n  describe \"#stem\" do\n    assert_paths_raw(\"foo.txt\", \"foo\", &.stem)\n    assert_paths_raw(\"foo.txt.txt\", \"foo.txt\", &.stem)\n    assert_paths_raw(\".txt\", \".txt\", &.stem)\n    assert_paths_raw(\".txt.txt\", \".txt\", &.stem)\n    assert_paths_raw(\"foo.\", \"foo.\", &.stem)\n    assert_paths_raw(\"foo.txt.\", \"foo.txt.\", &.stem)\n    assert_paths_raw(\"foo..txt\", \"foo.\", &.stem)\n\n    assert_paths_raw(\"bar/foo.txt\", \"foo\", &.stem)\n    assert_paths_raw(\"bar/foo.txt.txt\", \"foo.txt\", &.stem)\n    assert_paths_raw(\"bar/.txt\", \".txt\", &.stem)\n    assert_paths_raw(\"bar/.txt.txt\", \".txt\", &.stem)\n    assert_paths_raw(\"bar/foo.\", \"foo.\", &.stem)\n    assert_paths_raw(\"bar/foo.txt.\", \"foo.txt.\", &.stem)\n    assert_paths_raw(\"bar/foo..txt\", \"foo.\", &.stem)\n\n    assert_paths_raw(\"bar\\\\foo.txt\", \"bar\\\\foo\", \"foo\", &.stem)\n    assert_paths_raw(\"bar\\\\foo.txt.txt\", \"bar\\\\foo.txt\", \"foo.txt\", &.stem)\n    assert_paths_raw(\"bar\\\\.txt\", \"bar\\\\\", \".txt\", &.stem)\n    assert_paths_raw(\"bar\\\\.txt.txt\", \"bar\\\\.txt\", \".txt\", &.stem)\n    assert_paths_raw(\"bar\\\\foo.\", \"bar\\\\foo.\", \"foo.\", &.stem)\n    assert_paths_raw(\"bar\\\\foo.txt.\", \"bar\\\\foo.txt.\", \"foo.txt.\", &.stem)\n    assert_paths_raw(\"bar\\\\foo..txt\", \"bar\\\\foo.\", \"foo.\", &.stem)\n\n    assert_paths_raw(\"foo.txt/\", \"foo\", &.stem)\n    assert_paths_raw(\"foo.txt.txt/\", \"foo.txt\", &.stem)\n    assert_paths_raw(\".txt/\", \".txt\", &.stem)\n    assert_paths_raw(\".txt.txt/\", \".txt\", &.stem)\n    assert_paths_raw(\"foo./\", \"foo.\", &.stem)\n    assert_paths_raw(\"foo.txt./\", \"foo.txt.\", &.stem)\n    assert_paths_raw(\"foo..txt/\", \"foo.\", &.stem)\n  end\n\n  pending_wasm32 describe: \".home\" do\n    it \"uses home from environment variable if set\" do\n      with_env({HOME_ENV_KEY => \"foo/bar\"}) do\n        Path.home.should eq(Path.new(\"foo/bar\"))\n      end\n    end\n\n    # TODO: check that the cases below return the home of the current user (via #7829)\n    it \"doesn't return empty string if environment variable is empty\" do\n      with_env({HOME_ENV_KEY => \"\"}) do\n        Path.home.should_not eq(Path.new(\"\"))\n      end\n    end\n\n    it \"doesn't raise if environment variable is missing\" do\n      with_env({HOME_ENV_KEY => nil}) do\n        Path.home.should be_a(Path)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/pointer/appender_spec.cr",
    "content": "require \"spec\"\n\ndescribe Pointer::Appender do\n  it \".new\" do\n    Pointer::Appender.new(Pointer(Void).null)\n  end\n\n  it \"#<<\" do\n    data = Slice(Int32).new(5)\n    appender = data.to_unsafe.appender\n    4.times do |i|\n      appender << (i + 1) * 2\n    end\n\n    data.should eq Slice[2, 4, 6, 8, 0]\n  end\n\n  it \"#size\" do\n    data = Slice(Int32).new(5)\n    appender = data.to_unsafe.appender\n    appender.size.should eq 0\n    4.times do |i|\n      appender << 0\n      appender.size.should eq i + 1\n    end\n    appender.size.should eq 4\n  end\n\n  it \"#to_slice\" do\n    data = Slice(Int32).new(5)\n    appender = data.to_unsafe.appender\n    appender.to_slice.should eq Slice(Int32).new(0)\n    appender.to_slice.to_unsafe.should eq data.to_unsafe\n\n    4.times do |i|\n      appender << (i + 1) * 2\n      appender.to_slice.should eq data[0, i + 1]\n    end\n    appender.to_slice.should eq Slice[2, 4, 6, 8]\n    appender.to_slice.to_unsafe.should eq data.to_unsafe\n  end\nend\n"
  },
  {
    "path": "spec/std/pointer_spec.cr",
    "content": "require \"spec\"\n\nprivate lib LibPointerSpec\n  type A = Void\n  type B = Void\nend\n\nprivate def reset(p1, p2)\n  p1.value = 10\n  p2.value = 20\nend\n\ndescribe \"Pointer\" do\n  it \"does malloc with value\" do\n    p1 = Pointer.malloc(4, 1)\n    4.times do |i|\n      p1[i].should eq(1)\n    end\n  end\n\n  it \"does malloc with value from block\" do\n    p1 = Pointer.malloc(4) { |i| i }\n    4.times do |i|\n      p1[i].should eq(i)\n    end\n  end\n\n  it \"does index with count\" do\n    p1 = Pointer.malloc(4) { |i| i ** 2 }\n    p1.to_slice(4).index(4).should eq(2)\n    p1.to_slice(4).index(5).should be_nil\n  end\n\n  describe \"copy_from\" do\n    it \"performs\" do\n      p1 = Pointer.malloc(4) { |i| i }\n      p2 = Pointer.malloc(4) { 0 }\n      p2.copy_from(p1, 4)\n      4.times do |i|\n        p2[i].should eq(p1[i])\n      end\n    end\n\n    it \"raises on negative count\" do\n      p1 = Pointer.malloc(4, 0)\n      expect_raises(ArgumentError, \"Negative count\") do\n        p1.copy_from(p1, -1)\n      end\n    end\n\n    it \"copies from union of pointers\" do\n      p1 = Pointer.malloc(4, 1)\n      p2 = Pointer.malloc(4, 1.5)\n      p3 = Pointer.malloc(4, 0 || 0.0)\n      p3.copy_from(p1 || p2, 4)\n      4.times { |i| p3[i].should eq(p1[i]) }\n    end\n  end\n\n  describe \"realloc\" do\n    it \"raises on negative count\" do\n      p1 = Pointer(Int32).new(123)\n      expect_raises(ArgumentError) do\n        p1.realloc(-1)\n      end\n    end\n  end\n\n  describe \"copy_to\" do\n    it \"performs\" do\n      p1 = Pointer.malloc(4) { |i| i }\n      p2 = Pointer.malloc(4) { 0 }\n      p1.copy_to(p2, 4)\n      4.times do |i|\n        p2[i].should eq(p1[i])\n      end\n    end\n\n    it \"raises on negative count\" do\n      p1 = Pointer.malloc(4, 0)\n      expect_raises(ArgumentError, \"Negative count\") do\n        p1.copy_to(p1, -1)\n      end\n    end\n\n    it \"copies to union of pointers\" do\n      p1 = Pointer.malloc(4, 1)\n      p2 = Pointer.malloc(4, 0 || 1.5)\n      p3 = Pointer.malloc(4, 0 || 'a')\n      p1.copy_to(p2 || p3, 4)\n      4.times { |i| p2[i].should eq(p1[i]) }\n    end\n\n    it \"doesn't raise OverflowError on unsigned size and different target type\" do\n      p1 = Pointer.malloc(4, 1)\n      p2 = Pointer.malloc(4, 0 || nil)\n      p1.copy_to(p2, 4_u32)\n      4.times { |i| p2[i].should eq(p1[i]) }\n    end\n  end\n\n  describe \"move_from\" do\n    it \"performs with overlap right to left\" do\n      p1 = Pointer.malloc(4) { |i| i }\n      (p1 + 1).move_from(p1 + 2, 2)\n      p1[0].should eq(0)\n      p1[1].should eq(2)\n      p1[2].should eq(3)\n      p1[3].should eq(3)\n    end\n\n    it \"performs with overlap left to right\" do\n      p1 = Pointer.malloc(4) { |i| i }\n      (p1 + 2).move_from(p1 + 1, 2)\n      p1[0].should eq(0)\n      p1[1].should eq(1)\n      p1[2].should eq(1)\n      p1[3].should eq(2)\n    end\n\n    it \"raises on negative count\" do\n      p1 = Pointer.malloc(4, 0)\n      expect_raises(ArgumentError, \"Negative count\") do\n        p1.move_from(p1, -1)\n      end\n    end\n\n    it \"moves from union of pointers\" do\n      p1 = Pointer.malloc(4, 1)\n      p2 = Pointer.malloc(4, 1.5)\n      p3 = Pointer.malloc(4, 0 || 0.0)\n      p3.move_from(p1 || p2, 4)\n      4.times { |i| p3[i].should eq(p1[i]) }\n    end\n  end\n\n  describe \"move_to\" do\n    it \"performs with overlap right to left\" do\n      p1 = Pointer.malloc(4) { |i| i }\n      (p1 + 2).move_to(p1 + 1, 2)\n      p1[0].should eq(0)\n      p1[1].should eq(2)\n      p1[2].should eq(3)\n      p1[3].should eq(3)\n    end\n\n    it \"performs with overlap left to right\" do\n      p1 = Pointer.malloc(4) { |i| i }\n      (p1 + 1).move_to(p1 + 2, 2)\n      p1[0].should eq(0)\n      p1[1].should eq(1)\n      p1[2].should eq(1)\n      p1[3].should eq(2)\n    end\n\n    it \"raises on negative count\" do\n      p1 = Pointer.malloc(4, 0)\n      expect_raises(ArgumentError, \"Negative count\") do\n        p1.move_to(p1, -1)\n      end\n    end\n\n    it \"moves to union of pointers\" do\n      p1 = Pointer.malloc(4, 1)\n      p2 = Pointer.malloc(4, 0 || 1.5)\n      p3 = Pointer.malloc(4, 0 || 'a')\n      p1.move_to(p2 || p3, 4)\n      4.times { |i| p2[i].should eq(p1[i]) }\n    end\n  end\n\n  describe \"memcmp\" do\n    it do\n      p1 = Pointer.malloc(4) { |i| i }\n      p2 = Pointer.malloc(4) { |i| i }\n      p3 = Pointer.malloc(4) { |i| i + 1 }\n\n      p1.memcmp(p2, 4).should eq(0)\n      p1.memcmp(p3, 4).should be < 0\n      p3.memcmp(p1, 4).should be > 0\n    end\n  end\n\n  it \"compares two pointers by address\" do\n    p1 = Pointer(Int32).malloc(1)\n    p2 = Pointer(Int32).malloc(1)\n    p1.should eq(p1)\n    p1.should_not eq(p2)\n    p1.should_not eq(1)\n  end\n\n  it \"does to_s\" do\n    Pointer(Int32).null.to_s.should eq(\"Pointer(Int32).null\")\n    Pointer(Int32).new(1234_u64).to_s.should eq(\"Pointer(Int32)@0x4d2\")\n  end\n\n  it \"doesn't confuse lib typedefs (#16686)\" do\n    Pointer(LibPointerSpec::A).null.inspect.should eq \"Pointer(LibPointerSpec::A).null\"\n    Pointer(LibPointerSpec::B).null.inspect.should eq \"Pointer(LibPointerSpec::B).null\"\n  end\n\n  it \"creates from int\" do\n    Pointer(Int32).new(1234).address.should eq(1234)\n  end\n\n  it \"performs arithmetic with u64\" do\n    p = Pointer(Int8).new(1234)\n    d = 4_u64\n    (p + d).address.should eq(1238)\n    (p - d).address.should eq(1230)\n\n    p = Pointer(Int8).new(UInt64::MAX)\n    d = UInt64::MAX - 1\n    (p - d).address.should eq(1)\n  end\n\n  it \"performs arithmetic with u32\" do\n    p = Pointer(Int8).new(1234)\n    d = 4_u32\n    (p + d).address.should eq(1238)\n    (p - d).address.should eq(1230)\n  end\n\n  it \"shuffles!\" do\n    a = Pointer(Int32).malloc(3) { |i| i + 1 }\n    a.shuffle!(3)\n\n    (a[0] + a[1] + a[2]).should eq(6)\n\n    3.times do |i|\n      a.to_slice(3).should contain(i + 1)\n    end\n  end\n\n  it \"maps!\" do\n    a = Pointer(Int32).malloc(3) { |i| i + 1 }\n    a.map!(3) { |i| i + 1 }\n    a[0].should eq(2)\n    a[1].should eq(3)\n    a[2].should eq(4)\n  end\n\n  it \"maps_with_index!\" do\n    a = Pointer(Int32).malloc(3) { |i| i + 1 }\n    a.map_with_index!(3) { |e, i| e + i }\n    a[0].should eq(1)\n    a[1].should eq(3)\n    a[2].should eq(5)\n  end\n\n  it \"maps_with_index!, with offset\" do\n    a = Pointer(Int32).malloc(3) { |i| i + 1 }\n    a.map_with_index!(3, offset: 10) { |e, i| e + i }\n    a[0].should eq(11)\n    a[1].should eq(13)\n    a[2].should eq(15)\n  end\n\n  describe \"#fill\" do\n    it \"int\" do\n      slice = Slice[0, 1, 2, 3, 4]\n      ptr = slice.to_unsafe + 1\n      ptr.fill(3, 7)\n      slice.should eq Slice[0, 7, 7, 7, 4]\n    end\n\n    it \"string\" do\n      slice = Slice[\"a\", \"b\", \"c\", \"d\", \"e\"]\n      ptr = slice.to_unsafe + 1\n      ptr.fill(3, \" \")\n      slice.should eq Slice[\"a\", \" \", \" \", \" \", \"e\"]\n    end\n\n    it \"pointer\" do\n      slice = Slice[Pointer(Void).new(0x1_u64), Pointer(Void).new(0x2_u64), Pointer(Void).new(0x3_u64), Pointer(Void).new(0x4_u64), Pointer(Void).new(0x5_u64)]\n      ptr = slice.to_unsafe + 1\n      ptr.fill(3, Pointer(Void).new(0x10_u64))\n      slice.should eq Slice[Pointer(Void).new(0x1_u64), Pointer(Void).new(0x10_u64), Pointer(Void).new(0x10_u64), Pointer(Void).new(0x10_u64), Pointer(Void).new(0x5_u64)]\n    end\n\n    describe \"yielding\" do\n      it \"int\" do\n        slice = Slice[1, 1, 1, 1, 1]\n        ptr = slice.to_unsafe + 1\n        ptr.fill(3) { |i| i * i }\n        slice.should eq Slice[1, 0, 1, 4, 1]\n        ptr.fill(3, offset: 3) { |i| i * i }\n        slice.should eq Slice[1, 9, 16, 25, 1]\n      end\n    end\n  end\n\n  it \"raises if mallocs negative size\" do\n    expect_raises(ArgumentError) { Pointer.malloc(-1, 0) }\n  end\n\n  it \"copies/move with different types\" do\n    p1 = Pointer(Int32).malloc(1)\n    p2 = Pointer(Int32 | String).malloc(1)\n\n    reset p1, p2\n    p1.copy_from(p1, 1)\n    p1.value.should eq(10)\n\n    # p1.copy_from(p2, 10) # invalid\n\n    reset p1, p2\n    p2.copy_from(p1, 1)\n    p2.value.should eq(10)\n\n    reset p1, p2\n    p2.copy_from(p2, 1)\n    p2.value.should eq(20)\n\n    reset p1, p2\n    p1.move_from(p1, 1)\n    p1.value.should eq(10)\n\n    # p1.move_from(p2, 10) # invalid\n\n    reset p1, p2\n    p2.move_from(p1, 1)\n    p2.value.should eq(10)\n\n    reset p1, p2\n    p2.move_from(p2, 1)\n    p2.value.should eq(20)\n\n    # ---\n\n    reset p1, p2\n    p1.copy_to(p1, 1)\n    p1.value.should eq(10)\n\n    reset p1, p2\n    p1.copy_to(p2, 1)\n    p2.value.should eq(10)\n\n    # p2.copy_to(p1, 10) # invalid\n\n    reset p1, p2\n    p2.copy_to(p2, 1)\n    p2.value.should eq(20)\n\n    reset p1, p2\n    p1.move_to(p1, 1)\n    p1.value.should eq(10)\n\n    reset p1, p2\n    p1.move_to(p2, 1)\n    p2.value.should eq(10)\n\n    # p2.move_to(p1, 10) # invalid\n\n    reset p1, p2\n    p2.move_to(p2, 1)\n    p2.value.should eq(20)\n  end\n\n  describe \"clear\" do\n    it \"clears one\" do\n      ptr = Pointer(Int32).malloc(2)\n      ptr[0] = 10\n      ptr[1] = 20\n      ptr.clear\n      ptr[0].should eq(0)\n      ptr[1].should eq(20)\n    end\n\n    it \"clears many\" do\n      ptr = Pointer(Int32).malloc(4)\n      ptr[0] = 10\n      ptr[1] = 20\n      ptr[2] = 30\n      ptr[3] = 40\n      ptr.clear(2)\n      ptr[0].should eq(0)\n      ptr[1].should eq(0)\n      ptr[2].should eq(30)\n      ptr[3].should eq(40)\n    end\n\n    it \"clears with union\" do\n      ptr = Pointer(Int32 | Nil).malloc(4)\n      ptr[0] = 10\n      ptr[1] = 20\n      ptr[2] = 30\n      ptr[3] = 0\n      ptr.clear(2)\n      ptr[0].should be_nil\n      ptr[1].should be_nil\n      ptr[2].should eq(30)\n      ptr[3].should eq(0)\n      ptr[3].should_not be_nil\n    end\n  end\n\n  it \"does !\" do\n    (!Pointer(Int32).null).should be_true\n    (!Pointer(Int32).new(123)).should be_false\n  end\n\n  it \"clones\" do\n    ptr = Pointer(Int32).new(123)\n    ptr.clone.should eq(ptr)\n  end\n\n  it \"aligns small pointers using #align_down and #align_up\" do\n    ptr = Pointer(Void).new(0x30_u64)\n    ptr.align_down(16).should eq(Pointer(Void).new(0x30_u64))\n    ptr.align_down(32).should eq(Pointer(Void).new(0x20_u64))\n\n    ptr = Pointer(Void).new(0x30_u64)\n    ptr.align_up(16).should eq(Pointer(Void).new(0x30_u64))\n    ptr.align_up(32).should eq(Pointer(Void).new(0x40_u64))\n\n    ptr = Pointer(Void).new(1_u64)\n    ptr.align_up(1024).should eq(Pointer(Void).new(1024_u64))\n    ptr.align_down(1024).should eq(Pointer(Void).new(0_u64))\n\n    ptr = Pointer(Void).new(0xDEADC0DE_u64)\n    ptr.align_up(1u64 << 29).address.should eq(0xE0000000_u64)\n    ptr.align_down(1u64 << 29).address.should eq(0xC0000000_u64)\n\n    ptr = Pointer(Void).new(0xBADCAB1E_u64)\n    ptr.align_up(1u64 << 32).address.should eq({% if flag?(:bits32) %} 0u64 {% else %} 1u64 << 32 {% end %})\n    ptr.align_down(1u64 << 32).address.should eq(0_u64)\n    ptr.align_up(1u64 << 40).address.should eq({% if flag?(:bits32) %} 0u64 {% else %} 1u64 << 40 {% end %})\n    ptr.align_down(1u64 << 40).address.should eq(0_u64)\n  end\n\n  it \"only aligns pointers on byte boundaries using #align_down and #align_up\" do\n    ptr = Pointer(UInt128).new(10_u64)\n\n    ptr.align_up(4).should eq(Pointer(UInt128).new(12_u64))\n    ptr.align_down(4).should eq(Pointer(UInt128).new(8_u64))\n\n    ptr.align_up(16).should eq(Pointer(UInt128).new(16_u64))\n    ptr.align_down(16).should eq(Pointer(UInt128).new(0_u64))\n\n    ptr = Pointer(UInt32).new(3_u64)\n    ptr.align_down(2).should eq(Pointer(UInt32).new(2_u64))\n\n    ptr = Pointer(UInt32).new(1_u64)\n    ptr.align_up(2).should eq(Pointer(UInt32).new(2_u64))\n  end\n\n  it \"correctly wraps around zero using #align_up\" do\n    # On 32 bit platforms, this results in a Pointer with address UInt32::MAX (truncated)\n    ptr = Pointer(Void*).new(&-1_u64)\n\n    ptr.align_up(1).address.should eq({% if flag?(:bits32) %} (&-1u32).to_u64 {% else %} &-1u64 {% end %})\n\n    ptr.align_up(2).address.should eq(0_u64)\n\n    ptr.align_up(1u64 << 16).address.should eq(0_u64)\n  end\n\n  {% if flag?(:bits64) %}\n    it \"correctly aligns pointers using operands exceeding 32-bit range using #align_down and #align_up\" do\n      ptr = Pointer(Void).new(0xDECAFFEDC0FFEEEE_u64)\n\n      # align to 48 bits (281_474_976_710_656 byte boundary)\n      ptr.align_down(1u64 << 48).address.should eq(0xDECA000000000000_u64)\n      ptr.align_up(1u64 << 48).address.should eq(0xDECB000000000000_u64)\n\n      # align to 63 bits\n      ptr = Pointer(Void).new((1u64 << 63) + 1)\n      ptr.align_down(1u64 << 63).address.should eq(1u64 << 63)\n      ptr.align_up(1u64 << 63).address.should eq(0_u64)\n\n      ptr = Pointer(Void).new((1u64 << 63) - 1)\n      ptr.align_down(1u64 << 63).address.should eq(0_u64)\n      ptr.align_up(1u64 << 63).address.should eq(1u64 << 63)\n    end\n  {% end %}\n\n  {% if flag?(:bits32) %}\n    it \"raises on copy_from with size bigger than UInt32::MAX\" do\n      ptr = Pointer(Int32).new(123)\n\n      expect_raises(ArgumentError) do\n        ptr.copy_from(ptr, UInt32::MAX.to_u64 + 1)\n      end\n    end\n\n    it \"raises on move_from with size bigger than UInt32::MAX\" do\n      ptr = Pointer(Int32).new(123)\n\n      expect_raises(ArgumentError) do\n        ptr.move_from(ptr, UInt32::MAX.to_u64 + 1)\n      end\n    end\n\n    it \"raises on clear with size bigger than UInt32::MAX\" do\n      ptr = Pointer(Int32).new(123)\n\n      expect_raises(ArgumentError) do\n        ptr.clear(UInt32::MAX.to_u64 + 1)\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/pp_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"p\" do\n  it \"can be used with tuples\" do\n    typeof(p!({1, 2}))\n    typeof(p!({1, 2}, {3, 4}))\n  end\nend\n\ndescribe \"pp\" do\n  it \"can be used with tuples\" do\n    typeof(pp!({1, 2}))\n    typeof(pp!({1, 2}, {3, 4}))\n  end\nend\n"
  },
  {
    "path": "spec/std/pretty_print_spec.cr",
    "content": "require \"spec\"\n\ndescribe PrettyPrint do\n  assert_hello 0..6, <<-END\n    hello\n    a\n    b\n    c\n    d\n    END\n\n  assert_hello 7..8, <<-END\n    hello a\n    b\n    c\n    d\n    END\n\n  assert_hello 9..10, <<-END\n    hello a b\n    c\n    d\n    END\n\n  assert_hello 11..12, <<-END\n      hello a b c\n      d\n      END\n\n  assert_hello 13..13, <<-END\n      hello a b c d\n      END\n\n  assert_tree 0..19, <<-END\n    aaaa[bbbbb[ccc,\n               dd],\n         eee,\n         ffff[gg,\n              hhh,\n              ii]]\n    END\n\n  assert_tree 20..22, <<-END\n    aaaa[bbbbb[ccc, dd],\n         eee,\n         ffff[gg,\n              hhh,\n              ii]]\n    END\n\n  assert_tree 23..43, <<-END\n    aaaa[bbbbb[ccc, dd],\n         eee,\n         ffff[gg, hhh, ii]]\n    END\n\n  assert_tree 44..44, <<-END\n    aaaa[bbbbb[ccc, dd], eee, ffff[gg, hhh, ii]]\n    END\n\n  assert_tree_alt 0..18, <<-END\n    aaaa[\n      bbbbb[\n        ccc,\n        dd\n      ],\n      eee,\n      ffff[\n        gg,\n        hhh,\n        ii\n      ]\n    ]\n    END\n\n  assert_tree_alt 19..20, <<-END\n    aaaa[\n      bbbbb[ ccc, dd ],\n      eee,\n      ffff[\n        gg,\n        hhh,\n        ii\n      ]\n    ]\n    END\n\n  assert_tree_alt 21..49, <<-END\n    aaaa[\n      bbbbb[ ccc, dd ],\n      eee,\n      ffff[ gg, hhh, ii ]\n    ]\n    END\n\n  assert_tree_alt 50..50, <<-END\n    aaaa[ bbbbb[ ccc, dd ], eee, ffff[ gg, hhh, ii ] ]\n    END\n\n  assert_strict_pretty 0..4, <<-END\n    if\n      a\n        ==\n        b\n    then\n      a\n        <<\n        2\n    else\n      a\n        +\n        b\n    END\n\n  assert_strict_pretty 5..5, <<-END\n    if\n      a\n        ==\n        b\n    then\n      a\n        <<\n        2\n    else\n      a +\n        b\n    END\n\n  assert_strict_pretty 6..6, <<-END\n    if\n      a ==\n        b\n    then\n      a <<\n        2\n    else\n      a +\n        b\n    END\n\n  assert_strict_pretty 7..7, <<-END\n    if\n      a ==\n        b\n    then\n      a <<\n        2\n    else\n      a + b\n    END\n\n  assert_strict_pretty 8..8, <<-END\n    if\n      a == b\n    then\n      a << 2\n    else\n      a + b\n    END\n\n  assert_strict_pretty 9..9, <<-END\n    if a == b\n    then\n      a << 2\n    else\n      a + b\n    END\n\n  assert_strict_pretty 10..10, <<-END\n    if a == b\n    then\n      a << 2\n    else a + b\n    END\n\n  assert_strict_pretty 11..31, <<-END\n    if a == b\n    then a << 2\n    else a + b\n    END\n\n  assert_strict_pretty 32..32, <<-END\n    if a == b then a << 2 else a + b\n    END\n\n  assert_fill 0..6, <<-END\n    abc\n    def\n    ghi\n    jkl\n    mno\n    pqr\n    stu\n    END\n\n  assert_fill 7..10, <<-END\n    abc def\n    ghi jkl\n    mno pqr\n    stu\n    END\n\n  assert_fill 11..14, <<-END\n    abc def ghi\n    jkl mno pqr\n    stu\n    END\n\n  assert_fill 15..18, <<-END\n    abc def ghi jkl\n    mno pqr stu\n    END\n\n  assert_fill 19..22, <<-END\n    abc def ghi jkl mno\n    pqr stu\n    END\n\n  assert_fill 23..26, <<-END\n    abc def ghi jkl mno pqr\n    stu\n    END\n\n  assert_fill 27..27, <<-END\n    abc def ghi jkl mno pqr stu\n    END\n\n  assert_indent 0, 23, <<-END\n    x x x x x x x x x x x\n    END\n\n  assert_indent 5, 20, <<-END\n    x x x x x x x x\n         x x x\n    END\n\n  assert_indent 6, 20, <<-END\n    x x x x x x x\n          x x x x\n    END\n\n  assert_indent 7, 20, <<-END\n    x x x x x x x\n           x x x x\n    END\n\n  it \"tail group\" do\n    text = String.build do |io|\n      PrettyPrint.format(io, 10) do |q|\n        q.group do\n          q.group do\n            q.text \"abc\"\n            q.breakable\n            q.text \"def\"\n          end\n          q.group do\n            q.text \"ghi\"\n            q.breakable\n            q.text \"jkl\"\n          end\n        end\n      end\n    end\n    text.should eq(\"abc defghi\\njkl\")\n  end\nend\n\nprivate class Tree\n  @children : Array(Tree)\n\n  def initialize(@string : String)\n    @children = [] of Tree\n  end\n\n  def initialize(@string : String, *children)\n    @children = children.to_a\n  end\n\n  def show(q)\n    q.group do\n      q.text @string\n      q.nest(@string.size) do\n        unless @children.empty?\n          q.text '['\n          q.nest(1) do\n            first = true\n            @children.each do |t|\n              if first\n                first = false\n              else\n                q.text ','\n                q.breakable\n              end\n              t.show(q)\n            end\n          end\n          q.text ']'\n        end\n      end\n    end\n  end\n\n  def altshow(q)\n    q.group do\n      q.text @string\n      unless @children.empty?\n        q.text '['\n        q.nest(2) do\n          q.breakable\n          first = true\n          @children.each do |t|\n            if first\n              first = false\n            else\n              q.text ','\n              q.breakable\n            end\n            t.altshow(q)\n          end\n        end\n        q.breakable\n        q.text ']'\n      end\n    end\n  end\nend\n\nprivate def tree\n  Tree.new(\"aaaa\",\n    Tree.new(\"bbbbb\",\n      Tree.new(\"ccc\"),\n      Tree.new(\"dd\")),\n    Tree.new(\"eee\"),\n    Tree.new(\"ffff\",\n      Tree.new(\"gg\"),\n      Tree.new(\"hhh\"),\n      Tree.new(\"ii\")))\nend\n\nprivate def tree(width)\n  String.build do |io|\n    PrettyPrint.format(io, width) { |q| tree.show(q) }\n  end\nend\n\nprivate def tree_alt(width)\n  String.build do |io|\n    PrettyPrint.format(io, width) { |q| tree.altshow(q) }\n  end\nend\n\nprivate def hello(width)\n  String.build do |io|\n    PrettyPrint.format(io, width) do |hello|\n      hello.group do\n        hello.group do\n          hello.group do\n            hello.group do\n              hello.text \"hello\"\n              hello.breakable; hello.text \"a\"\n            end\n            hello.breakable; hello.text \"b\"\n          end\n          hello.breakable; hello.text \"c\"\n        end\n        hello.breakable; hello.text \"d\"\n      end\n    end\n  end\nend\n\nprivate def strict_pretty(width)\n  String.build do |io|\n    PrettyPrint.format(io, width) do |q|\n      q.group do\n        q.group do\n          q.nest(2) do\n            q.text \"if\"; q.breakable\n            q.group do\n              q.nest(2) do\n                q.group { q.text \"a\"; q.breakable; q.text \"==\" }\n                q.breakable; q.text \"b\"\n              end\n            end\n          end\n        end\n        q.breakable\n        q.group do\n          q.nest(2) do\n            q.text \"then\"; q.breakable\n            q.group do\n              q.nest(2) do\n                q.group { q.text \"a\"; q.breakable; q.text \"<<\" }\n                q.breakable; q.text \"2\"\n              end\n            end\n          end\n        end\n        q.breakable\n        q.group do\n          q.nest(2) do\n            q.text \"else\"; q.breakable\n            q.group do\n              q.nest(2) do\n                q.group { q.text \"a\"; q.breakable; q.text \"+\" }\n                q.breakable; q.text \"b\"\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n\nprivate def fill(width)\n  String.build do |io|\n    PrettyPrint.format(io, width) do |q|\n      q.group do\n        q.text \"abc\"\n        q.fill_breakable\n        q.text \"def\"\n        q.fill_breakable\n        q.text \"ghi\"\n        q.fill_breakable\n        q.text \"jkl\"\n        q.fill_breakable\n        q.text \"mno\"\n        q.fill_breakable\n        q.text \"pqr\"\n        q.fill_breakable\n        q.text \"stu\"\n      end\n    end\n  end\nend\n\nprivate def indent(indent, width)\n  String.build do |io|\n    PrettyPrint.format(io, width, indent: indent) do |q|\n      q.text \"x\"\n      10.times do |i|\n        q.group do\n          q.breakable\n          q.text \"x\"\n        end\n      end\n    end\n  end\nend\n\nprivate def assert_hello(range, expected)\n  it \"pretty prints hello #{range}\" do\n    range.each do |width|\n      hello(width).should eq(expected)\n    end\n  end\nend\n\nprivate def assert_tree(range, expected)\n  it \"pretty prints tree #{range}\" do\n    range.each do |width|\n      tree(width).should eq(expected)\n    end\n  end\nend\n\nprivate def assert_tree_alt(range, expected)\n  it \"pretty prints tree alt #{range}\" do\n    range.each do |width|\n      tree_alt(width).should eq(expected)\n    end\n  end\nend\n\nprivate def assert_strict_pretty(range, expected)\n  it \"pretty prints strict pretty #{range}\" do\n    range.each do |width|\n      strict_pretty(width).should eq(expected)\n    end\n  end\nend\n\nprivate def assert_fill(range, expected)\n  it \"pretty prints fill #{range}\" do\n    range.each do |width|\n      fill(width).should eq(expected)\n    end\n  end\nend\n\nprivate def assert_indent(width, indent, expected)\n  it \"pretty prints width #{width} indent #{indent}\" do\n    indent(width, indent).should eq(expected)\n  end\nend\n"
  },
  {
    "path": "spec/std/proc_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Proc\" do\n  it \"does to_s(io)\" do\n    str = IO::Memory.new\n    f = ->(x : Int32) { x.to_f }\n    f.to_s(str)\n    str.to_s.should eq(\"#<Proc(Int32, Float64):0x#{f.pointer.address.to_s(16)}>\")\n  end\n\n  it \"does to_s(io) when closured\" do\n    str = IO::Memory.new\n    a = 1.5\n    f = ->(x : Int32) { x + a }\n    f.to_s(str)\n    str.to_s.should eq(\"#<Proc(Int32, Float64):0x#{f.pointer.address.to_s(16)}:closure>\")\n  end\n\n  it \"does to_s\" do\n    f = ->(x : Int32) { x.to_f }\n    f.to_s.should eq(\"#<Proc(Int32, Float64):0x#{f.pointer.address.to_s(16)}>\")\n  end\n\n  it \"does to_s when closured\" do\n    a = 1.5\n    f = ->(x : Int32) { x + a }\n    f.to_s.should eq(\"#<Proc(Int32, Float64):0x#{f.pointer.address.to_s(16)}:closure>\")\n  end\n\n  it \"gets pointer\" do\n    f = -> { 1 }\n    f.pointer.address.should be > 0\n  end\n\n  it \"gets closure data for non-closure\" do\n    f = -> { 1 }\n    f.closure_data.address.should eq(0)\n    f.closure?.should be_false\n  end\n\n  it \"gets closure data for closure\" do\n    a = 1\n    f = -> { a }\n    f.closure_data.address.should be > 0\n    f.closure?.should be_true\n  end\n\n  it \"does new\" do\n    a = 1\n    f = ->(x : Int32) { x + a }\n    f2 = Proc(Int32, Int32).new(f.pointer, f.closure_data)\n    f2.call(3).should eq(4)\n  end\n\n  it \"does ==\" do\n    func = -> { 1 }\n    func.should eq(func)\n    func2 = -> { 1 }\n    func2.should_not eq(func)\n  end\n\n  it \"clones\" do\n    func = -> { 1 }\n    func.clone.should eq(func)\n  end\n\n  it \"#arity\" do\n    f = ->(x : Int32, y : Int32) { }\n    f.arity.should eq(2)\n  end\n\n  it \"#partial\" do\n    f = ->(x : Int32, y : Int32, z : Int32) { x + y + z }\n    f.call(1, 2, 3).should eq(6)\n\n    f2 = f.partial(10)\n    f2.call(2, 3).should eq(15)\n    f2.call(2, 10).should eq(22)\n\n    f3 = f2.partial(20)\n    f3.call(3).should eq(33)\n    f3.call(10).should eq(40)\n\n    f = ->(x : String, y : Char) { x.index(y) }\n    f.call(\"foo\", 'o').should eq(1)\n\n    f2 = f.partial(\"bar\")\n    f2.call('a').should eq(1)\n    f2.call('r').should eq(2)\n  end\n\n  it \"#[]\" do\n    f = ->(x : Int32) { x + 1 }\n    f[3].should eq(4)\n  end\n\n  typeof(-> { 1 }.hash)\nend\n"
  },
  {
    "path": "spec/std/process/exit_reason_spec.cr",
    "content": "require \"spec\"\n\ndescribe Process::ExitReason do\n  describe \"#description\" do\n    it \"with exit status\" do\n      Process::ExitReason::Normal.description.should eq \"Process exited normally\"\n      Process::ExitReason::Unknown.description.should eq \"Process terminated abnormally, the cause is unknown\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/process/find_executable_spec.cr",
    "content": "require \"spec\"\nrequire \"../../support/tempfile\"\n\n{% if flag?(:win32) %}\n  FIND_EXECUTABLE_TEST_FILES = {\n    [\n      \"inbase.exe\",\n      \"not_exe\",\n      \".exe\",\n      \"inboth.exe\",\n\n      \"inbasebat.bat\",\n      \"inbase.foo.exe\",\n      \".inbase.exe\",\n\n      \"sub/insub.exe\",\n      \"sub/not_exe\",\n      \"sub/.exe\",\n\n      \"../path/inpath.exe\",\n      \"../path/not_exe\",\n      \"../path/.exe\",\n      \"../path/inboth.exe\",\n    ], [] of String,\n  }\n\n  def find_executable_test_cases(pwd)\n    pwd_nodrive = \"\\\\#{pwd.relative_to(pwd.anchor.not_nil!)}\"\n    {\n      \"inbase.exe\"     => \"inbase.exe\",\n      \"inbase\"         => \"inbase.exe\",\n      \"sub\\\\insub.exe\" => \"sub/insub.exe\",\n      \"sub/insub\"      => \"sub/insub.exe\",\n      \"inpath.exe\"     => \"../path/inpath.exe\",\n      \"inpath\"         => \"../path/inpath.exe\",\n      \"sub/.exe\"       => \"sub/.exe\",\n      \"sub\\\\\"          => \"sub/.exe\",\n      \"sub/\"           => \"sub/.exe\",\n      \".exe\"           => \".exe\",\n      \"not_exe\"        => nil,\n      \"sub\\\\not_exe\"   => nil,\n      \"inbasebat\"      => nil,\n      \"inbase.foo.exe\" => \"inbase.foo.exe\",\n      \"inbase.foo\"     => nil,\n      \".inbase.exe\"    => \".inbase.exe\",\n      \".inbase\"        => nil,\n      \"\"               => nil,\n      \".\"              => nil,\n      \"inboth.exe\"     => \"inboth.exe\",\n      \"inboth\"         => \"inboth.exe\",\n      \"./inbase\"       => \"inbase.exe\",\n      \"../base/inbase\" => \"inbase.exe\",\n      \"./inpath\"       => nil,\n      \"sub\"            => nil,\n      \"#{pwd}\\\\sub\"    => nil,\n      \"#{pwd}\\\\sub\\\\\"  => nil,\n      # 'C:\\Temp\\base\\inbase', 'C:\\Temp\\base\\.exe', 'C:\\Temp\\base\\'\n      \"#{pwd}\\\\inbase\" => \"inbase.exe\",\n      \"#{pwd}\\\\.exe\"   => \".exe\",\n      \"#{pwd}\\\\\"       => nil,\n      # 'C:inbase', 'C:.exe', 'C:'\n      \"#{pwd.drive}inbase\" => \"inbase.exe\",\n      \"#{pwd.drive}.exe\"   => \".exe\",\n      \"#{pwd.drive}\"       => nil,\n      \"#{pwd.drive}sub\\\\\"  => nil,\n      # '\\Temp\\base\\inbase', '\\Temp\\base\\.exe', '\\Temp\\base\\'\n      \"#{pwd_nodrive}\\\\inbase\" => \"inbase.exe\",\n      \"#{pwd_nodrive}\\\\.exe\"   => \".exe\",\n      \"#{pwd_nodrive}\\\\\"       => nil,\n    }\n  end\n{% else %}\n  FIND_EXECUTABLE_TEST_FILES = {\n    [\n      \"inbase\",\n      \"sub/insub\",\n      \"../path/inpath\",\n    ], [\n      \"not_exe\",\n      \"sub/not_exe\",\n      \"../path/not_exe\",\n    ],\n  }\n\n  def find_executable_test_cases(pwd)\n    {\n      \"./inbase\"       => \"inbase\",\n      \"../base/inbase\" => \"inbase\",\n      \"inbase\"         => nil,\n      \"sub/insub\"      => \"sub/insub\",\n      \"inpath\"         => \"../path/inpath\",\n      \"./inpath\"       => nil,\n      \"inbase/\"        => nil,\n      \"sub/insub/\"     => nil,\n      \"./not_exe\"      => nil,\n      \"not_exe\"        => nil,\n      \"sub/not_exe\"    => nil,\n      \"\"               => nil,\n      \".\"              => nil,\n      \"#{pwd}/inbase\"  => \"inbase\",\n      \"#{pwd}/inbase/\" => nil,\n      \"#{pwd}/sub\"     => nil,\n      \"./sub\"          => nil,\n      \"sub\"            => nil,\n    }\n  end\n{% end %}\n\ndescribe \"Process.find_executable\" do\n  test_dir = Path[SPEC_TEMPFILE_PATH] / \"find_executable\"\n  base_dir = Path[test_dir] / \"base\"\n  path_dir = Path[test_dir] / \"path\"\n\n  around_all do |all|\n    exe_names, non_exe_names = FIND_EXECUTABLE_TEST_FILES\n    (exe_names + non_exe_names).each do |name|\n      Dir.mkdir_p((base_dir / name).parent)\n      File.write(base_dir / name, \"\")\n    end\n    exe_names.each do |name|\n      File.chmod(base_dir / name, 0o755)\n    end\n\n    all.run\n\n    FileUtils.rm_r(test_dir.to_s)\n  end\n\n  find_executable_test_cases(base_dir).each do |(command, exp)|\n    if exp\n      exp_path = File.expand_path(exp, base_dir)\n      it \"finds '#{command}' as '#{exp}'\" do\n        Process.find_executable(command, path: path_dir.to_s, pwd: base_dir).should eq exp_path\n      end\n    else\n      it \"fails to find '#{command}'\" do\n        Process.find_executable(command, path: path_dir.to_s, pwd: base_dir).should be_nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/process/status_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/string\"\n\nprivate def exit_status(status)\n  {% if flag?(:unix) %}\n    status << 8\n  {% else %}\n    status.to_u32!\n  {% end %}\nend\n\nprivate def status_for(exit_reason : Process::ExitReason)\n  exit_code = case exit_reason\n              when .interrupted?\n                {% if flag?(:unix) %}Signal::INT.value{% else %}LibC::STATUS_CONTROL_C_EXIT{% end %}\n              else\n                raise NotImplementedError.new(\"status_for\")\n              end\n  Process::Status.new(exit_code)\nend\n\ndescribe Process::Status do\n  it \"#exit_code\" do\n    Process::Status.new(exit_status(0)).exit_code.should eq 0\n    Process::Status.new(exit_status(1)).exit_code.should eq 1\n    Process::Status.new(exit_status(127)).exit_code.should eq 127\n    Process::Status.new(exit_status(128)).exit_code.should eq 128\n    Process::Status.new(exit_status(255)).exit_code.should eq 255\n\n    expect_raises(RuntimeError, \"Abnormal exit has no exit code\") do\n      status_for(:interrupted).exit_code\n    end\n  end\n\n  it \"#exit_code?\" do\n    Process::Status.new(exit_status(0)).exit_code?.should eq 0\n    Process::Status.new(exit_status(1)).exit_code?.should eq 1\n    Process::Status.new(exit_status(127)).exit_code?.should eq 127\n    Process::Status.new(exit_status(128)).exit_code?.should eq 128\n    Process::Status.new(exit_status(255)).exit_code?.should eq 255\n\n    status_for(:interrupted).exit_code?.should be_nil\n  end\n\n  it \"#system_exit_status\" do\n    Process::Status.new(exit_status(0)).system_exit_status.should eq 0_u32\n    Process::Status.new(exit_status(1)).system_exit_status.should eq({{ flag?(:unix) ? 0x0100_u32 : 1_u32 }})\n    Process::Status.new(exit_status(127)).system_exit_status.should eq({{ flag?(:unix) ? 0x7f00_u32 : 127_u32 }})\n    Process::Status.new(exit_status(128)).system_exit_status.should eq({{ flag?(:unix) ? 0x8000_u32 : 128_u32 }})\n    Process::Status.new(exit_status(255)).system_exit_status.should eq({{ flag?(:unix) ? 0xFF00_u32 : 255_u32 }})\n\n    status_for(:interrupted).system_exit_status.should eq({% if flag?(:unix) %}Signal::INT.value{% else %}LibC::STATUS_CONTROL_C_EXIT{% end %})\n  end\n\n  it \"#success?\" do\n    Process::Status.new(exit_status(0)).success?.should be_true\n    Process::Status.new(exit_status(1)).success?.should be_false\n    Process::Status.new(exit_status(127)).success?.should be_false\n    Process::Status.new(exit_status(128)).success?.should be_false\n    Process::Status.new(exit_status(255)).success?.should be_false\n\n    status_for(:interrupted).success?.should be_false\n  end\n\n  it \"#normal_exit?\" do\n    Process::Status.new(exit_status(0)).normal_exit?.should be_true\n    Process::Status.new(exit_status(1)).normal_exit?.should be_true\n    Process::Status.new(exit_status(127)).normal_exit?.should be_true\n    Process::Status.new(exit_status(128)).normal_exit?.should be_true\n    Process::Status.new(exit_status(255)).normal_exit?.should be_true\n\n    status_for(:interrupted).normal_exit?.should be_false\n  end\n\n  it \"#abnormal_exit?\" do\n    Process::Status.new(exit_status(0)).abnormal_exit?.should be_false\n    Process::Status.new(exit_status(1)).abnormal_exit?.should be_false\n    Process::Status.new(exit_status(127)).abnormal_exit?.should be_false\n    Process::Status.new(exit_status(128)).abnormal_exit?.should be_false\n    Process::Status.new(exit_status(255)).abnormal_exit?.should be_false\n\n    status_for(:interrupted).abnormal_exit?.should be_true\n  end\n\n  it \"#signal_exit?\" do\n    Process::Status.new(exit_status(0)).signal_exit?.should be_false\n    Process::Status.new(exit_status(1)).signal_exit?.should be_false\n    Process::Status.new(exit_status(127)).signal_exit?.should be_false\n    Process::Status.new(exit_status(128)).signal_exit?.should be_false\n    Process::Status.new(exit_status(255)).signal_exit?.should be_false\n\n    status_for(:interrupted).signal_exit?.should eq {{ !flag?(:win32) }}\n  end\n\n  it \"equality\" do\n    ok1 = Process::Status.new(exit_status(0))\n    ok2 = Process::Status.new(exit_status(0))\n    err1 = Process::Status.new(exit_status(1))\n    err2 = Process::Status.new(exit_status(1))\n\n    ok1.should eq(ok2)\n    ok1.should_not eq(err2)\n    err1.should_not eq(ok2)\n    err1.should eq(err2)\n\n    ok1.hash.should eq(ok2.hash)\n    ok1.hash.should_not eq(err2.hash)\n    err1.hash.should_not eq(ok2.hash)\n    err1.hash.should eq(err2.hash)\n  end\n\n  it \"#exit_signal?\" do\n    Process::Status.new(exit_status(0)).exit_signal?.should be_nil\n    Process::Status.new(exit_status(1)).exit_signal?.should be_nil\n\n    status_for(:interrupted).exit_signal?.should eq({% if flag?(:unix) %}Signal::INT{% else %}nil{% end %})\n  end\n\n  {% if flag?(:unix) && !flag?(:wasi) %}\n    it \"#exit_signal\" do\n      Process::Status.new(Signal::HUP.value).exit_signal.should eq Signal::HUP\n      Process::Status.new(Signal::INT.value).exit_signal.should eq Signal::INT\n      last_signal = Signal.values[-1]\n      Process::Status.new(last_signal.value).exit_signal.should eq last_signal\n\n      unknown_signal = Signal.new(126)\n      Process::Status.new(unknown_signal.value).exit_signal.should eq unknown_signal\n    end\n\n    it \"#exit_signal?\" do\n      Process::Status.new(Signal::HUP.value).exit_signal?.should eq Signal::HUP\n      Process::Status.new(Signal::INT.value).exit_signal?.should eq Signal::INT\n      last_signal = Signal.values[-1]\n      Process::Status.new(last_signal.value).exit_signal?.should eq last_signal\n\n      unknown_signal = Signal.new(126)\n      Process::Status.new(unknown_signal.value).exit_signal?.should eq unknown_signal\n    end\n\n    it \"#normal_exit? with signal code\" do\n      Process::Status.new(0x00).normal_exit?.should be_true\n      Process::Status.new(0x01).normal_exit?.should be_false\n      Process::Status.new(0x7e).normal_exit?.should be_false\n      Process::Status.new(0x7f).normal_exit?.should be_false\n    end\n\n    it \"#signal_exit? with signal code\" do\n      Process::Status.new(0x00).signal_exit?.should be_false\n      Process::Status.new(0x01).signal_exit?.should be_true\n      Process::Status.new(0x7e).signal_exit?.should be_true\n      Process::Status.new(0x7f).signal_exit?.should be_true\n    end\n  {% end %}\n\n  {% if flag?(:win32) %}\n    describe \"#exit_reason\" do\n      it \"returns Normal\" do\n        Process::Status.new(exit_status(0)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(1)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(127)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(128)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(255)).exit_reason.normal?.should be_true\n\n        Process::Status.new(0x3FFFFFFF_u32).exit_reason.normal?.should be_true\n        Process::Status.new(0x40001234_u32).exit_reason.normal?.should be_false\n        Process::Status.new(0x80001234_u32).exit_reason.normal?.should be_false\n        Process::Status.new(0xC0001234_u32).exit_reason.normal?.should be_false\n      end\n\n      it \"returns Aborted\" do\n        Process::Status.new(LibC::STATUS_FATAL_APP_EXIT).exit_reason.aborted?.should be_true\n      end\n\n      it \"returns Interrupted\" do\n        Process::Status.new(LibC::STATUS_CONTROL_C_EXIT).exit_reason.interrupted?.should be_true\n      end\n\n      it \"returns Breakpoint\" do\n        Process::Status.new(LibC::STATUS_BREAKPOINT).exit_reason.breakpoint?.should be_true\n      end\n\n      it \"returns AccessViolation\" do\n        Process::Status.new(LibC::STATUS_ACCESS_VIOLATION).exit_reason.access_violation?.should be_true\n        Process::Status.new(LibC::STATUS_STACK_OVERFLOW).exit_reason.access_violation?.should be_true\n      end\n\n      it \"returns BadMemoryAccess\" do\n        Process::Status.new(LibC::STATUS_DATATYPE_MISALIGNMENT).exit_reason.bad_memory_access?.should be_true\n      end\n\n      it \"returns BadInstruction\" do\n        Process::Status.new(LibC::STATUS_ILLEGAL_INSTRUCTION).exit_reason.bad_instruction?.should be_true\n        Process::Status.new(LibC::STATUS_PRIVILEGED_INSTRUCTION).exit_reason.bad_instruction?.should be_true\n      end\n\n      it \"returns FloatException\" do\n        Process::Status.new(LibC::STATUS_FLOAT_DIVIDE_BY_ZERO).exit_reason.float_exception?.should be_true\n        Process::Status.new(LibC::STATUS_FLOAT_INEXACT_RESULT).exit_reason.float_exception?.should be_true\n        Process::Status.new(LibC::STATUS_FLOAT_INVALID_OPERATION).exit_reason.float_exception?.should be_true\n        Process::Status.new(LibC::STATUS_FLOAT_OVERFLOW).exit_reason.float_exception?.should be_true\n        Process::Status.new(LibC::STATUS_FLOAT_UNDERFLOW).exit_reason.float_exception?.should be_true\n      end\n    end\n  {% elsif flag?(:unix) && !flag?(:wasi) %}\n    describe \"#exit_reason\" do\n      it \"returns Normal\" do\n        Process::Status.new(exit_status(0)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(1)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(127)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(128)).exit_reason.normal?.should be_true\n        Process::Status.new(exit_status(255)).exit_reason.normal?.should be_true\n\n        Process::Status.new(0x01).exit_reason.normal?.should be_false\n        Process::Status.new(0x7e).exit_reason.normal?.should be_false\n\n        Process::Status.new(0x017f).exit_reason.normal?.should be_false\n        Process::Status.new(0xffff).exit_reason.normal?.should be_false\n      end\n\n      it \"returns Aborted\" do\n        Process::Status.new(Signal::ABRT.value).exit_reason.aborted?.should be_true\n        Process::Status.new(Signal::KILL.value).exit_reason.aborted?.should be_true\n        Process::Status.new(Signal::QUIT.value).exit_reason.aborted?.should be_true\n      end\n\n      it \"returns TerminalDisconnected\" do\n        Process::Status.new(Signal::HUP.value).exit_reason.terminal_disconnected?.should be_true\n      end\n\n      it \"returns SessionEnded\" do\n        Process::Status.new(Signal::TERM.value).exit_reason.session_ended?.should be_true\n      end\n\n      it \"returns Interrupted\" do\n        Process::Status.new(Signal::INT.value).exit_reason.interrupted?.should be_true\n      end\n\n      it \"returns Breakpoint\" do\n        Process::Status.new(Signal::TRAP.value).exit_reason.breakpoint?.should be_true\n      end\n\n      it \"returns AccessViolation\" do\n        Process::Status.new(Signal::SEGV.value).exit_reason.access_violation?.should be_true\n      end\n\n      it \"returns BadMemoryAccess\" do\n        Process::Status.new(Signal::BUS.value).exit_reason.bad_memory_access?.should be_true\n      end\n\n      it \"returns BadInstruction\" do\n        Process::Status.new(Signal::ILL.value).exit_reason.bad_instruction?.should be_true\n      end\n\n      it \"returns FloatException\" do\n        Process::Status.new(Signal::FPE.value).exit_reason.float_exception?.should be_true\n      end\n    end\n  {% end %}\n\n  describe \"#to_s\" do\n    it \"with exit status\" do\n      assert_prints Process::Status.new(exit_status(0)).to_s, \"0\"\n      assert_prints Process::Status.new(exit_status(1)).to_s, \"1\"\n      assert_prints Process::Status.new(exit_status(127)).to_s, \"127\"\n      assert_prints Process::Status.new(exit_status(128)).to_s, \"128\"\n      assert_prints Process::Status.new(exit_status(255)).to_s, \"255\"\n    end\n\n    it \"on abnormal exit\" do\n      {% if flag?(:win32) %}\n        assert_prints status_for(:interrupted).to_s, \"STATUS_CONTROL_C_EXIT\"\n      {% else %}\n        assert_prints status_for(:interrupted).to_s, \"INT\"\n      {% end %}\n    end\n\n    {% if flag?(:unix) && !flag?(:wasi) %}\n      it \"with exit signal\" do\n        assert_prints Process::Status.new(Signal::HUP.value).to_s, \"HUP\"\n        last_signal = Signal.values[-1]\n        assert_prints Process::Status.new(last_signal.value).to_s, last_signal.to_s\n\n        assert_prints Process::Status.new(Signal.new(126).value).to_s, \"Signal[126]\"\n      end\n    {% end %}\n\n    {% if flag?(:win32) %}\n      it \"hex format\" do\n        assert_prints Process::Status.new(UInt16::MAX).to_s, \"0x0000FFFF\"\n        assert_prints Process::Status.new(0x01234567).to_s, \"0x01234567\"\n        assert_prints Process::Status.new(UInt32::MAX).to_s, \"0xFFFFFFFF\"\n      end\n    {% end %}\n  end\n\n  describe \"#inspect\" do\n    it \"with exit status\" do\n      assert_prints Process::Status.new(exit_status(0)).inspect, \"Process::Status[0]\"\n      assert_prints Process::Status.new(exit_status(1)).inspect, \"Process::Status[1]\"\n      assert_prints Process::Status.new(exit_status(127)).inspect, \"Process::Status[127]\"\n      assert_prints Process::Status.new(exit_status(128)).inspect, \"Process::Status[128]\"\n      assert_prints Process::Status.new(exit_status(255)).inspect, \"Process::Status[255]\"\n    end\n\n    it \"on abnormal exit\" do\n      {% if flag?(:win32) %}\n        assert_prints status_for(:interrupted).inspect, \"Process::Status[LibC::STATUS_CONTROL_C_EXIT]\"\n      {% else %}\n        assert_prints status_for(:interrupted).inspect, \"Process::Status[Signal::INT]\"\n      {% end %}\n    end\n\n    {% if flag?(:unix) && !flag?(:wasi) %}\n      it \"with exit signal\" do\n        assert_prints Process::Status.new(Signal::HUP.value).inspect, \"Process::Status[Signal::HUP]\"\n        last_signal = Signal.values[-1]\n        assert_prints Process::Status.new(last_signal.value).inspect, \"Process::Status[#{last_signal.inspect}]\"\n\n        unknown_signal = Signal.new(126)\n        assert_prints Process::Status.new(unknown_signal.value).inspect, \"Process::Status[Signal[126]]\"\n      end\n    {% end %}\n\n    {% if flag?(:win32) %}\n      it \"hex format\" do\n        assert_prints Process::Status.new(UInt16::MAX).inspect, \"Process::Status[0x0000FFFF]\"\n        assert_prints Process::Status.new(0x01234567).inspect, \"Process::Status[0x01234567]\"\n        assert_prints Process::Status.new(UInt32::MAX).inspect, \"Process::Status[0xFFFFFFFF]\"\n      end\n    {% end %}\n  end\n\n  describe \"#description\" do\n    it \"with exit status\" do\n      Process::Status.new(exit_status(0)).description.should eq \"Process exited normally\"\n      Process::Status.new(exit_status(255)).description.should eq \"Process exited normally\"\n    end\n\n    it \"on interrupt\" do\n      status_for(:interrupted).description.should eq \"Process was interrupted\"\n    end\n\n    {% if flag?(:unix) && !flag?(:wasi) %}\n      it \"with exit signal\" do\n        Process::Status.new(Signal::HUP.value).description.should eq \"Process terminated abnormally\"\n        Process::Status.new(Signal::KILL.value).description.should eq \"Process terminated abnormally\"\n        Process::Status.new(Signal::STOP.value).description.should eq \"Process received and didn't handle signal STOP\"\n        last_signal = Signal.values[-1]\n        Process::Status.new(last_signal.value).description.should eq \"Process received and didn't handle signal #{last_signal}\"\n\n        unknown_signal = Signal.new(126)\n        Process::Status.new(unknown_signal.value).description.should eq \"Process received and didn't handle signal 126\"\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/std/process/utils_spec.cr",
    "content": "require \"spec\"\n\ndescribe Process do\n  describe \".executable_path\" do\n    it \"searches executable\" do\n      Process.executable_path.should be_a(String | Nil)\n    end\n  end\n\n  describe \".quote_posix\" do\n    it { Process.quote_posix(\"\").should eq \"''\" }\n    it { Process.quote_posix(\" \").should eq \"' '\" }\n    it { Process.quote_posix(\"$hi\").should eq \"'$hi'\" }\n    it { Process.quote_posix(orig = \"aZ5+,-./:=@_\").should eq orig }\n    it { Process.quote_posix(orig = \"cafe\").should eq orig }\n    it { Process.quote_posix(\"café\").should eq \"'café'\" }\n    it { Process.quote_posix(\"I'll\").should eq %('I'\"'\"'ll') }\n    it { Process.quote_posix(\"'\").should eq %(''\"'\"'') }\n    it { Process.quote_posix(\"\\\\\").should eq \"'\\\\'\" }\n\n    context \"join\" do\n      it { Process.quote_posix([] of String).should eq \"\" }\n      it { Process.quote_posix([\"my file.txt\", \"another.txt\"]).should eq \"'my file.txt' another.txt\" }\n      it { Process.quote_posix([\"foo \", \"\", \" \", \" bar\"]).should eq \"'foo ' '' ' ' ' bar'\" }\n      it { Process.quote_posix([\"foo'\", \"\\\"bar\"]).should eq %('foo'\"'\"'' '\"bar') }\n    end\n  end\n\n  describe \".quote_windows\" do\n    it { Process.quote_windows(\"\").should eq %(\"\") }\n    it { Process.quote_windows(\" \").should eq %(\" \") }\n    it { Process.quote_windows(orig = \"%hi%\").should eq orig }\n    it { Process.quote_windows(%q(C:\\\"foo\" project.txt)).should eq %q(\"C:\\\\\\\"foo\\\" project.txt\") }\n    it { Process.quote_windows(%q(C:\\\"foo\"_project.txt)).should eq %q(C:\\\\\\\"foo\\\"_project.txt) }\n    it { Process.quote_windows(%q(C:\\Program Files\\Foo Bar\\foobar.exe)).should eq %q(\"C:\\Program Files\\Foo Bar\\foobar.exe\") }\n    it { Process.quote_windows(orig = \"café\").should eq orig }\n    it { Process.quote_windows(%(\")).should eq %q(\\\") }\n    it { Process.quote_windows(%q(a\\\\b\\ c\\)).should eq %q(\"a\\\\b\\ c\\\\\") }\n    it { Process.quote_windows(orig = %q(a\\\\b\\c\\)).should eq orig }\n\n    context \"join\" do\n      it { Process.quote_windows([] of String).should eq \"\" }\n      it { Process.quote_windows([\"my file.txt\", \"another.txt\"]).should eq %(\"my file.txt\" another.txt) }\n      it { Process.quote_windows([\"foo \", \"\", \" \", \" bar\"]).should eq %(\"foo \" \"\" \" \" \" bar\") }\n    end\n  end\n\n  {% if flag?(:unix) %}\n    describe \".parse_arguments\" do\n      it \"uses the native platform rules\" do\n        Process.parse_arguments(%q[a\\ b'c']).should eq [%q[a bc]]\n      end\n    end\n  {% elsif flag?(:win32) %}\n    describe \".parse_arguments\" do\n      it \"uses the native platform rules\" do\n        Process.parse_arguments(%q[a\\ b'c']).should eq [%q[a\\], %q[b'c']]\n      end\n    end\n  {% else %}\n    pending \".parse_arguments\"\n  {% end %}\n\n  describe \".parse_arguments_posix\" do\n    it { Process.parse_arguments_posix(%q[]).should eq([] of String) }\n    it { Process.parse_arguments_posix(%q[ ]).should eq([] of String) }\n    it { Process.parse_arguments_posix(%q[foo]).should eq [%q[foo]] }\n    it { Process.parse_arguments_posix(%q[foo bar]).should eq [%q[foo], %q[bar]] }\n    it { Process.parse_arguments_posix(%q[\"foo bar\" 'foo bar' baz]).should eq [%q[foo bar], %q[foo bar], %q[baz]] }\n    it { Process.parse_arguments_posix(%q[\"foo bar\"'foo bar'baz]).should eq [%q[foo barfoo barbaz]] }\n    it { Process.parse_arguments_posix(%q[foo\\ bar]).should eq [%q[foo bar]] }\n    it { Process.parse_arguments_posix(%q[\"foo\\ bar\"]).should eq [%q[foo\\ bar]] }\n    it { Process.parse_arguments_posix(%q['foo\\ bar']).should eq [%q[foo\\ bar]] }\n    it { Process.parse_arguments_posix(%q[\\]).should eq [%q[\\]] }\n    it { Process.parse_arguments_posix(%q[\"foo bar\" '\\hello/' Fizz\\ Buzz]).should eq [%q[foo bar], %q[\\hello/], %q[Fizz Buzz]] }\n    it { Process.parse_arguments_posix(%q[foo\"bar\"baz]).should eq [%q[foobarbaz]] }\n    it { Process.parse_arguments_posix(%q[foo'bar'baz]).should eq [%q[foobarbaz]] }\n    it { Process.parse_arguments_posix(%q[this 'is a \"'very wei\"rd co\"m\"mand please\" don't do t'h'a't p\"leas\"e]).should eq [%q[this], %q[is a \"very], %q[weird command please], %q[dont do that], %q[please]] }\n\n    it \"raises an error when double quote is unclosed\" do\n      expect_raises ArgumentError, \"Unmatched quote\" do\n        Process.parse_arguments_posix(%q[\"foo])\n      end\n    end\n\n    it \"raises an error if single quote is unclosed\" do\n      expect_raises ArgumentError, \"Unmatched quote\" do\n        Process.parse_arguments_posix(%q['foo])\n      end\n    end\n  end\n\n  describe \".parse_arguments_windows\" do\n    it { Process.parse_arguments_windows(%q[]).should eq([] of String) }\n    it { Process.parse_arguments_windows(%q[ ]).should eq([] of String) }\n    it { Process.parse_arguments_windows(%q[foo]).should eq [%q[foo]] }\n    it { Process.parse_arguments_windows(%q[foo bar]).should eq [%q[foo], %q[bar]] }\n    it { Process.parse_arguments_windows(%q[\"foo bar\" 'foo bar' baz]).should eq [%q[foo bar], %q['foo], %q[bar'], %q[baz]] }\n    it { Process.parse_arguments_windows(%q[\"foo bar\"baz]).should eq [%q[foo barbaz]] }\n    it { Process.parse_arguments_windows(%q[foo\"bar baz\"]).should eq [%q[foobar baz]] }\n    it { Process.parse_arguments_windows(%q[foo\\bar]).should eq [%q[foo\\bar]] }\n    it { Process.parse_arguments_windows(%q[foo\\ bar]).should eq [%q[foo\\], %q[bar]] }\n    it { Process.parse_arguments_windows(%q[foo\\\\bar]).should eq [%q[foo\\\\bar]] }\n    it { Process.parse_arguments_windows(%q[foo\\\\\\bar]).should eq [%q[foo\\\\\\bar]] }\n    it { Process.parse_arguments_windows(%q[ /LIBPATH:C:\\crystal\\lib ]).should eq [%q[/LIBPATH:C:\\crystal\\lib]] }\n    it { Process.parse_arguments_windows(%q[a\\\\\\b d\"e f\"g h]).should eq [%q[a\\\\\\b], %q[de fg], %q[h]] }\n    it { Process.parse_arguments_windows(%q[a\\\\\\\"b c d]).should eq [%q[a\\\"b], %q[c], %q[d]] }\n    it { Process.parse_arguments_windows(%q[a\\\\\\\\\"b c\" d e]).should eq [%q[a\\\\b c], %q[d], %q[e]] }\n    it { Process.parse_arguments_windows(%q[\"foo bar\" '\\hello/' Fizz\\ Buzz]).should eq [%q[foo bar], %q['\\hello/'], %q[Fizz\\], %q[Buzz]] }\n    it { Process.parse_arguments_windows(%q[this 'is a \"'very wei\"rd co\"m\"mand please\" don't do t'h'a't p\"leas\"e\"]).should eq [%q[this], %q['is], %q[a], %q['very weird], %q[command], %q[please don't do t'h'a't please]] }\n\n    it \"raises an error if double quote is unclosed\" do\n      expect_raises ArgumentError, \"Unmatched quote\" do\n        Process.parse_arguments_windows(%q[\"foo])\n        Process.parse_arguments_windows(%q[\\\"foo])\n        Process.parse_arguments_windows(%q[\"f\\\"oo\\\\\\\"])\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/process_spec.cr",
    "content": "{% skip_file if flag?(:wasm32) %}\n\nrequire \"spec\"\nrequire \"process\"\nrequire \"./spec_helper\"\nrequire \"../support/env\"\nrequire \"../support/wait_for\"\n\nprivate def exit_code_command(code)\n  {% if flag?(:win32) %}\n    {\"cmd.exe\", {\"/c\", \"exit #{code}\"}}\n  {% else %}\n    {\"/bin/sh\", {\"-c\", \"exit #{code}\"}}\n  {% end %}\nend\n\nprivate def shell_command(command)\n  {% if flag?(:win32) %}\n    {\"cmd.exe\", {\"/c\", command}}\n  {% else %}\n    {\"/bin/sh\", {\"-c\", command}}\n  {% end %}\nend\n\nprivate def stdin_to_stdout_command\n  {% if flag?(:win32) %}\n    {\"powershell.exe\", {\"-C\", \"$Input\"}}\n  {% else %}\n    {\"/bin/cat\", [] of String}\n  {% end %}\nend\n\nprivate def print_env_command\n  {% if flag?(:win32) %}\n    # cmd adds these by itself, clear them out before printing.\n    shell_command(\"set COMSPEC=& set PATHEXT=& set PROMPT=& set PROCESSOR_ARCHITECTURE=& set\")\n  {% else %}\n    {\"env\", [] of String}\n  {% end %}\nend\n\nprivate def standing_command\n  {% if flag?(:win32) %}\n    {\"cmd.exe\"}\n  {% else %}\n    {\"yes\"}\n  {% end %}\nend\n\nprivate def path_search_command\n  {% if flag?(:win32) %}\n    {\"cmd.exe\"}\n  {% else %}\n    {\"true\"}\n  {% end %}\nend\n\nprivate def newline\n  {% if flag?(:win32) %}\n    \"\\r\\n\"\n  {% else %}\n    \"\\n\"\n  {% end %}\nend\n\nprivate def to_ary(tuple)\n  [tuple[0]].concat(tuple[1])\nend\n\n# interpreted code doesn't receive SIGCHLD for `#wait` to work (#12241)\n{% if flag?(:interpreted) && !flag?(:win32) %}\n  pending Process\n  {% skip_file %}\n{% end %}\n\ndescribe Process do\n  describe \".new (args)\" do\n    it \"raises if args is empty\" do\n      expect_raises(File::NotFoundError, \"Error executing process: No command\") do\n        Process.new([] of String)\n      end\n    end\n\n    it \"raises if args[0] is empty\" do\n      expect_raises(IO::Error, /Error executing process: '(\"\"|\\\\\"\\\\\")?'/) do\n        Process.new([\"\"] of String)\n      end\n    end\n\n    it \"raises if command doesn't exist\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'foobarbaz'\") do\n        Process.new([\"foobarbaz\"])\n      end\n    end\n\n    it \"raises for long path\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'aaaaaaa\") do\n        Process.new([\"a\" * 1000])\n      end\n    end\n\n    it \"accepts nilable string for `chdir` (#13767)\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'foobarbaz'\") do\n        Process.new([\"foobarbaz\"], chdir: nil.as(String?))\n      end\n    end\n\n    it \"raises if command is a file path\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        File.touch path\n        expect_raises({% if flag?(:win32) %} File::BadExecutableError {% else %} File::AccessDeniedError {% end %}, \"Error executing process: '#{path.inspect_unquoted}'\") do\n          Process.new([path])\n        end\n      end\n    end\n\n    it \"raises if command is a dir path\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        Dir.mkdir path\n        expect_raises(File::AccessDeniedError, \"Error executing process: '#{path.inspect_unquoted}'\") do\n          Process.new([path])\n        end\n      end\n    end\n\n    it \"raises if command is a file's subpath\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        File.touch path\n        command = File.join(path, \"foo\")\n        expect_raises(IO::Error, \"Error executing process: '#{command.inspect_unquoted}'\") do\n          Process.new([command])\n        end\n      end\n    end\n\n    it \"doesn't break if process is collected before completion\", tags: %w[slow] do\n      200.times { Process.new(to_ary(exit_code_command(0))) }\n\n      # run the GC multiple times to unmap as much memory as possible\n      10.times { GC.collect }\n\n      # the processes above have now been queued after completion; if this last\n      # one finishes at all, nothing was broken by the GC\n      Process.run(*exit_code_command(0))\n    end\n  end\n\n  describe \".new (command + args)\" do\n    it \"raises if command doesn't exist\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'foobarbaz'\") do\n        Process.new(\"foobarbaz\")\n      end\n    end\n\n    it \"raises for long path\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'aaaaaaa\") do\n        Process.new(\"a\" * 1000)\n      end\n    end\n\n    it \"accepts nilable string for `chdir` (#13767)\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'foobarbaz'\") do\n        Process.new(\"foobarbaz\", chdir: nil.as(String?))\n      end\n    end\n\n    it \"raises if command is a file path\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        File.touch path\n        expect_raises({% if flag?(:win32) %} File::BadExecutableError {% else %} File::AccessDeniedError {% end %}, \"Error executing process: '#{path.inspect_unquoted}'\") do\n          Process.new(path)\n        end\n      end\n    end\n\n    it \"raises if command is a dir path\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        Dir.mkdir path\n        expect_raises(File::AccessDeniedError, \"Error executing process: '#{path.inspect_unquoted}'\") do\n          Process.new(path)\n        end\n      end\n    end\n\n    it \"raises if command is a file's subpath\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        File.touch path\n        command = File.join(path, \"foo\")\n        expect_raises(IO::Error, \"Error executing process: '#{command.inspect_unquoted}'\") do\n          Process.new(command)\n        end\n      end\n    end\n\n    it \"doesn't break if process is collected before completion\", tags: %w[slow] do\n      200.times { Process.new(*exit_code_command(0)) }\n\n      # run the GC multiple times to unmap as much memory as possible\n      10.times { GC.collect }\n\n      # the processes above have now been queued after completion; if this last\n      # one finishes at all, nothing was broken by the GC\n      Process.run(*exit_code_command(0))\n    end\n  end\n\n  describe \"#wait\" do\n    it \"successful exit code\" do\n      process = Process.new(*exit_code_command(0))\n      process.wait.exit_code.should eq(0)\n    end\n\n    it \"unsuccessful exit code\" do\n      process = Process.new(*exit_code_command(1))\n      process.wait.exit_code.should eq(1)\n    end\n  end\n\n  describe \".run?(args)\" do\n    it \"waits for successful process\" do\n      status = Process.run?(to_ary(exit_code_command(0))).should be_a(Process::Status)\n      status.exit_code.should eq(0)\n    end\n\n    it \"waits for unsuccessful process\" do\n      status = Process.run?(to_ary(exit_code_command(1))).should be_a(Process::Status)\n      status.exit_code.should eq(1)\n    end\n\n    it \"returns nil if args[0] is empty\" do\n      Process.run?([\"\"] of String).should be_nil\n    end\n\n    it \"returns nil command doesn't exist\" do\n      Process.run?([\"foobarbaz\"]).should be_nil\n    end\n\n    it \"returns nil for long path\" do\n      Process.run?([\"a\" * 1000]).should be_nil\n    end\n\n    it \"returns nil if command is a file path\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        File.touch path\n        Process.run?([path]).should be_nil\n      end\n    end\n\n    it \"returns nil if command is a dir path\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        Dir.mkdir path\n        Process.run?([path]).should be_nil\n      end\n    end\n\n    it \"returns nil if command is a file's subpath\" do\n      with_tempfile(\"crystal-spec-run\") do |path|\n        File.touch path\n        command = File.join(path, \"foo\")\n        Process.run?([command]).should be_nil\n      end\n    end\n  end\n\n  describe \".run\" do\n    it \"waits for the process\" do\n      Process.run(to_ary(exit_code_command(0))).exit_code.should eq(0)\n    end\n  end\n\n  describe \".run(args, &)\" do\n    it \"waits for the process\" do\n      Process.run(to_ary(exit_code_command(0))) { }[0].exit_code.should eq(0)\n    end\n\n    it \"returns block result\" do\n      Process.run(to_ary(exit_code_command(0))) { 42 }[1].should eq 42\n    end\n  end\n\n  describe \".run(command, args)\" do\n    it \"waits for the process\" do\n      Process.run(*exit_code_command(0)).exit_code.should eq(0)\n    end\n\n    it \"runs true in block\" do\n      Process.run(*exit_code_command(0)) { }\n      $?.exit_code.should eq(0)\n    end\n\n    it \"receives arguments in array\" do\n      command, args = exit_code_command(123)\n      Process.run(command, args.to_a).exit_code.should eq(123)\n    end\n\n    it \"receives arguments in tuple\" do\n      command, args = exit_code_command(123)\n      Process.run(command, args.as(Tuple)).exit_code.should eq(123)\n    end\n\n    it \"redirects output to /dev/null\" do\n      # This doesn't test anything but no output should be seen while running tests\n      command, args = {% if flag?(:win32) %}\n                        {\"cmd.exe\", {\"/c\", \"dir\"}}\n                      {% else %}\n                        {\"/bin/ls\", [] of String}\n                      {% end %}\n      Process.run(command, args, output: Process::Redirect::Close).exit_code.should eq(0)\n    end\n\n    it \"gets output\" do\n      value = Process.run(*shell_command(\"echo hello\")) do |proc|\n        proc.output.gets_to_end\n      end\n      value.should eq(\"hello#{newline}\")\n    end\n\n    it \"sends input in IO\" do\n      value = Process.run(*stdin_to_stdout_command, input: IO::Memory.new(\"hello\")) do |proc|\n        proc.input?.should be_nil\n        proc.output.gets_to_end\n      end\n      value.chomp.should eq(\"hello\")\n    end\n\n    it \"sends output to IO\" do\n      output = IO::Memory.new\n      Process.run(*shell_command(\"echo hello\"), output: output)\n      output.to_s.should eq(\"hello#{newline}\")\n    end\n\n    it \"sends error to IO\" do\n      error = IO::Memory.new\n      Process.run(*shell_command(\"1>&2 echo hello\"), error: error)\n      error.to_s.should eq(\"hello#{newline}\")\n    end\n\n    it \"sends long output and error to IO\" do\n      output = IO::Memory.new\n      error = IO::Memory.new\n      Process.run(*shell_command(\"echo #{\".\" * 8000}\"), output: output, error: error)\n      output.to_s.should eq(\".\" * 8000 + newline)\n      error.to_s.should be_empty\n    end\n\n    it \"controls process in block\" do\n      value = Process.run(*stdin_to_stdout_command, error: :inherit) do |proc|\n        proc.input.puts \"hello\"\n        proc.input.close\n        proc.output.gets_to_end\n      end\n      value.should eq(\"hello#{newline}\")\n    end\n\n    it \"closes input after block\" do\n      Process.run(*stdin_to_stdout_command) { }\n      $?.exit_code.should eq(0)\n    end\n\n    it \"closes output and error after block\" do\n      reader, writer = IO.pipe\n      channel = Channel(Process).new\n\n      spawn do\n        Process.run(*stdin_to_stdout_command, input: reader, output: :pipe, error: :pipe) do |process|\n          channel.send process\n          channel.receive\n        end\n        channel.close\n      end\n\n      process = channel.receive\n\n      process.output.closed?.should be_false\n      process.error.closed?.should be_false\n\n      channel.send process\n\n      # Wait a moment for the other fiber to continue and close the IOs\n      wait_for { process.output.closed? && process.error.closed? }\n\n      writer.close\n      channel.receive?.should be_nil\n    end\n\n    it \"forwards closed io\" do\n      closed_io = IO::Memory.new\n      closed_io.close\n      Process.run(*stdin_to_stdout_command, input: closed_io)\n      Process.run(*stdin_to_stdout_command, output: closed_io)\n      Process.run(*stdin_to_stdout_command, error: closed_io)\n    end\n\n    it \"forwards non-blocking file\" do\n      with_tempfile(\"non-blocking-process-input.txt\", \"non-blocking-process-output.txt\") do |in_path, out_path|\n        File.open(in_path, \"w+\", blocking: false) do |input|\n          File.open(out_path, \"w+\", blocking: false) do |output|\n            input.puts \"hello\"\n            input.rewind\n            Process.run(*stdin_to_stdout_command, input: input, output: output)\n            output.rewind\n            output.gets_to_end.chomp.should eq(\"hello\")\n          end\n        end\n      end\n    end\n\n    it \"sets working directory with string\" do\n      parent = File.dirname(Dir.current)\n      command = {% if flag?(:win32) %}\n                  \"cmd.exe /c echo %cd%\"\n                {% else %}\n                  \"pwd\"\n                {% end %}\n      value = Process.run(command, shell: true, chdir: parent, output: Process::Redirect::Pipe) do |proc|\n        proc.output.gets_to_end\n      end\n      value.should eq \"#{parent}#{newline}\"\n    end\n\n    it \"sets working directory with path\" do\n      parent = Path.new File.dirname(Dir.current)\n      command = {% if flag?(:win32) %}\n                  \"cmd.exe /c echo %cd%\"\n                {% else %}\n                  \"pwd\"\n                {% end %}\n      value = Process.run(command, shell: true, chdir: parent, output: Process::Redirect::Pipe) do |proc|\n        proc.output.gets_to_end\n      end\n      value.should eq \"#{parent}#{newline}\"\n    end\n\n    pending_win32 \"disallows passing arguments to nowhere\" do\n      expect_raises ArgumentError, /args.+@/ do\n        Process.run(\"foo bar\", {\"baz\"}, shell: true)\n      end\n    end\n\n    pending_win32 \"looks up programs in the $PATH with a shell\" do\n      proc = Process.run(*exit_code_command(0), shell: true, output: Process::Redirect::Close)\n      proc.exit_code.should eq(0)\n    end\n\n    pending_win32 \"allows passing huge argument lists to a shell\" do\n      proc = Process.new(%(echo \"${@}\"), {\"a\", \"b\"}, shell: true, output: Process::Redirect::Pipe)\n      output = proc.output.gets_to_end\n      proc.wait\n      output.should eq \"a b\\n\"\n    end\n\n    pending_win32 \"does not run shell code in the argument list\" do\n      proc = Process.new(\"echo\", {\"`echo hi`\"}, shell: true, output: Process::Redirect::Pipe)\n      output = proc.output.gets_to_end\n      proc.wait\n      output.should eq \"`echo hi`\\n\"\n    end\n\n    describe \"does not execute batch files\" do\n      %w[.bat .Bat .BAT .cmd .cmD .CmD .bat\\  .cmd\\ ... .bat.\\ .].each do |ext|\n        it ext do\n          with_tempfile \"process_run#{ext}\" do |path|\n            File.write(path, \"echo '#{ext}'\\n\")\n            expect_raises {{ flag?(:win32) ? File::BadExecutableError : File::AccessDeniedError }}, \"Error executing process\" do\n              Process.run(path)\n            end\n          end\n        end\n      end\n    end\n\n    describe \"environ\" do\n      it \"clears the environment\" do\n        value = Process.run(*print_env_command, clear_env: true) do |proc|\n          proc.output.gets_to_end\n        end\n        value.should eq(\"\")\n      end\n\n      it \"clears and sets an environment variable\" do\n        value = Process.run(*print_env_command, clear_env: true, env: {\"FOO\" => \"bar\"}) do |proc|\n          proc.output.gets_to_end\n        end\n        value.should eq(\"FOO=bar#{newline}\")\n      end\n\n      it \"sets an environment variable\" do\n        value = Process.run(*print_env_command, env: {\"FOO\" => \"bar\"}) do |proc|\n          proc.output.gets_to_end\n        end\n        value.should match /(*ANYCRLF)^FOO=bar$/m\n      end\n\n      it \"sets an empty environment variable\" do\n        value = Process.run(*print_env_command, env: {\"FOO\" => \"\"}) do |proc|\n          proc.output.gets_to_end\n        end\n        value.should match /(*ANYCRLF)^FOO=$/m\n      end\n\n      it \"deletes existing environment variable\" do\n        with_env(\"FOO\": \"bar\") do\n          value = Process.run(*print_env_command, env: {\"FOO\" => nil}) do |proc|\n            proc.output.gets_to_end\n          end\n          value.should_not match /(*ANYCRLF)^FOO=/m\n        end\n      end\n\n      {% if flag?(:win32) %}\n        it \"deletes existing environment variable case-insensitive\" do\n          with_env(\"FOO\": \"bar\") do\n            value = Process.run(*print_env_command, env: {\"foo\" => nil}) do |proc|\n              proc.output.gets_to_end\n            end\n            value.should_not match /(*ANYCRLF)^FOO=/mi\n          end\n        end\n      {% end %}\n\n      it \"preserves existing environment variable\" do\n        with_env(\"FOO\": \"bar\") do\n          value = Process.run(*print_env_command) do |proc|\n            proc.output.gets_to_end\n          end\n          value.should match /(*ANYCRLF)^FOO=bar$/m\n        end\n      end\n\n      it \"preserves and sets an environment variable\" do\n        with_env(\"FOO\": \"bar\") do\n          value = Process.run(*print_env_command, env: {\"FOO2\" => \"bar2\"}) do |proc|\n            proc.output.gets_to_end\n          end\n          value.should match /(*ANYCRLF)^FOO=bar$/m\n          value.should match /(*ANYCRLF)^FOO2=bar2$/m\n        end\n      end\n\n      it \"overrides existing environment variable\" do\n        with_env(\"FOO\": \"bar\") do\n          value = Process.run(*print_env_command, env: {\"FOO\" => \"different\"}) do |proc|\n            proc.output.gets_to_end\n          end\n          value.should match /(*ANYCRLF)^FOO=different$/m\n        end\n      end\n\n      {% if flag?(:win32) %}\n        it \"overrides existing environment variable case-insensitive\" do\n          with_env(\"FOO\": \"bar\") do\n            value = Process.run(*print_env_command, env: {\"fOo\" => \"different\"}) do |proc|\n              proc.output.gets_to_end\n            end\n            value.should_not match /(*ANYCRLF)^FOO=/m\n            value.should match /(*ANYCRLF)^fOo=different$/m\n          end\n        end\n      {% end %}\n\n      it \"finds binary in parent `$PATH`, not `env`\" do\n        Process.run(*print_env_command, env: {\"PATH\" => \"\"})\n      end\n\n      it \"errors on invalid key\" do\n        expect_raises(ArgumentError, %(Invalid env key \"\")) do\n          Process.run(*print_env_command, env: {\"\" => \"baz\"})\n        end\n        expect_raises(ArgumentError, %(Invalid env key \"foo=bar\")) do\n          Process.run(*print_env_command, env: {\"foo=bar\" => \"baz\"})\n        end\n      end\n\n      it \"errors on zero char in key\" do\n        expect_raises({{ flag?(:win32) }} ? ArgumentError : RuntimeError, \"String `key` contains null byte\") do\n          Process.run(*print_env_command, env: {\"foo\\0\" => \"baz\"})\n        end\n      end\n\n      it \"errors on zero char in value\" do\n        expect_raises({{ flag?(:win32) }} ? ArgumentError : RuntimeError, \"String `value` contains null byte\") do\n          Process.run(*print_env_command, env: {\"foo\" => \"baz\\0\"})\n        end\n      end\n    end\n\n    it \"errors with empty command\" do\n      {% begin %}\n        expect_raises({% if flag?(:win32) %} IO::Error, \"The parameter is incorrect\" {% else %} File::NotFoundError{% end %}) do\n          Process.run(\"\")\n        end\n      {% end %}\n    end\n\n    it \"errors with too long command\" do\n      pending! unless {{ flag?(:linux) }}\n\n      path_max = {% if LibC.has_constant?(:PATH_MAX) %}\n                   LibC::PATH_MAX\n                 {% else %}\n                   10_000\n                 {% end %}\n\n      expect_raises(IO::Error, /File ?name too long/) do\n        Process.run(\"a\" * (path_max + 1))\n      end\n\n      # The pathname itself is not too long, but it will be when combined with\n      # any path prefix.\n      expect_raises(IO::Error, /File ?name too long/) do\n        Process.run(\"a\" * path_max)\n      end\n    end\n\n    describe \"$PATH\" do\n      it \"works with unset $PATH\" do\n        with_env(\"PATH\": nil) do\n          Process.run(*path_search_command)\n        end\n      end\n\n      it \"errors with empty $PATH\" do\n        pending! if {{ flag?(:win32) }}\n        with_env(\"PATH\": \"\") do\n          expect_raises(File::NotFoundError) do\n            Process.run(*path_search_command)\n          end\n        end\n      end\n\n      it \"empty still finds in current directory\" do\n        pending! unless {{ flag?(:unix) }}\n\n        with_tempfile(\"crystal-spec-run\") do |dir|\n          Dir.mkdir dir\n          File.write(Path[dir, \"foo\"], \"#!/bin/sh\\necho bar\")\n          File.chmod(Path[dir, \"foo\"], 0o555)\n          if {{ flag?(:darwin) }}\n            String.build do |io|\n              Process.run(\"foo\", chdir: dir, output: io)\n            end.should eq \"bar\\n\"\n          else\n            expect_raises(File::NotFoundError) do\n              Process.run(\"foo\", chdir: dir)\n            end\n          end\n        end\n      end\n\n      it \"empty path entry means current directory\" do\n        pending! unless {{ flag?(:unix) }}\n\n        with_tempfile(\"crystal-spec-run\") do |dir|\n          Dir.mkdir dir\n          File.write(Path[dir, \"foo\"], \"#!/bin/sh\\necho bar\")\n          File.chmod(Path[dir, \"foo\"], 0o555)\n          with_env(\"PATH\": \":\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"::\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"/does/not/exist:\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \":/does/not/exist\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n        end\n      end\n\n      it \"finds path in relative directory\" do\n        pending! unless {{ flag?(:unix) }}\n\n        with_tempfile(\"crystal-spec-run\") do |dir|\n          Dir.mkdir_p Path[dir, \"bin\"]\n          Dir.mkdir_p Path[dir, \"empty\"]\n          File.write(Path[dir, \"bin\", \"foo\"], \"#!/bin/sh\\necho bar\")\n          File.chmod(Path[dir, \"bin\", \"foo\"], 0o555)\n          with_env(\"PATH\": \"bin\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"empty:bin\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"bin:empty\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"/does/not/exist:bin\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"bin:/does/not/exist\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \":bin\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"::bin\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"/does/not/exist::bin\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n          with_env(\"PATH\": \"bin:/does/not/exist\") do\n            Process.run(\"foo\", chdir: dir)\n          end\n        end\n      end\n\n      context \"with shell: true\" do\n        it \"errors with nonexist $PATH\" do\n          pending! unless {{ flag?(:unix) }}\n          Process.run(*print_env_command, shell: true, env: {\"PATH\" => \"/does/not/exist\"}).success?.should be_false\n        end\n\n        it \"empty path entry means current directory\" do\n          pending! unless {{ flag?(:unix) }}\n\n          with_tempfile(\"crystal-spec-run\") do |dir|\n            Dir.mkdir dir\n            File.write(Path[dir, \"foo\"], \"#!/bin/sh\\necho bar\")\n            File.chmod(Path[dir, \"foo\"], 0o555)\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \":\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"::\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"/does/not/exist:\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \":/does/not/exist\"}).success?.should be_true\n          end\n        end\n\n        it \"finds path in relative directory\" do\n          pending! unless {{ flag?(:unix) }}\n\n          with_tempfile(\"crystal-spec-run\") do |dir|\n            Dir.mkdir_p Path[dir, \"bin\"]\n            Dir.mkdir_p Path[dir, \"empty\"]\n            File.write(Path[dir, \"bin\", \"foo\"], \"#!/bin/sh\\necho bar\")\n            File.chmod(Path[dir, \"bin\", \"foo\"], 0o555)\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"bin\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"empty:bin\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"bin:empty\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"/does/not/exist:bin\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"bin:/does/not/exist\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \":bin\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"::bin\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"/does/not/exist::bin\"}).success?.should be_true\n            Process.run(\"foo\", chdir: dir, shell: true, env: {\"PATH\" => \"bin:/does/not/exist\"}).success?.should be_true\n          end\n        end\n      end\n    end\n\n    it \"can link processes together\" do\n      buffer = IO::Memory.new\n      Process.run(*stdin_to_stdout_command) do |cat|\n        Process.run(*stdin_to_stdout_command, input: cat.output, output: buffer) do\n          1000.times { cat.input.puts \"line\" }\n          cat.close\n        end\n      end\n      buffer.to_s.chomp.lines.size.should eq(1000)\n    end\n  end\n\n  describe \".on_interrupt\" do\n    it \"compiles\" do\n      typeof(Process.on_interrupt { })\n      typeof(Process.ignore_interrupts!)\n      typeof(Process.restore_interrupts!)\n    end\n  end\n\n  describe \".on_terminate\" do\n    it \"compiles\" do\n      typeof(Process.on_terminate { })\n      typeof(Process.ignore_interrupts!)\n      typeof(Process.restore_interrupts!)\n    end\n  end\n\n  {% unless flag?(:win32) %}\n    describe \"#signal(Signal::KILL)\" do\n      it \"kills a process\" do\n        process = Process.new(*standing_command)\n        process.signal(Signal::KILL).should be_nil\n      ensure\n        process.try &.wait\n      end\n\n      it \"kills many process\" do\n        process1 = Process.new(*standing_command)\n        process2 = Process.new(*standing_command)\n        process1.signal(Signal::KILL).should be_nil\n        process2.signal(Signal::KILL).should be_nil\n      ensure\n        process1.try &.wait\n        process2.try &.wait\n      end\n    end\n  {% end %}\n\n  it \"#terminate\" do\n    process = Process.new(*standing_command)\n    process.exists?.should be_true\n    process.terminated?.should be_false\n\n    process.terminate\n  ensure\n    process.try(&.wait)\n  end\n\n  typeof(Process.new(*standing_command).terminate(graceful: false))\n\n  describe \".debugger_present?\" do\n    it \"compiles\" do\n      typeof(Process.debugger_present?)\n    end\n  end\n\n  it \".exists?\" do\n    # On Windows killing a parent process does not reparent its children to\n    # another existing process, so the following isn't guaranteed to work\n    {% unless flag?(:win32) %}\n      # We can't reliably check whether it ever returns false, since we can't predict\n      # how PIDs are used by the system, a new process might be spawned in between\n      # reaping the one we would spawn and checking for it, using the now available\n      # pid.\n      Process.exists?(Process.ppid).should be_true\n    {% end %}\n\n    process = Process.new(*standing_command)\n    process.exists?.should be_true\n    process.terminated?.should be_false\n\n    # Kill, zombie now\n    process.terminate\n    {% if flag?(:win32) %}\n      # Windows has no concept of zombie processes\n      process.exists?.should be_false\n      process.terminated?.should be_true\n    {% else %}\n      process.exists?.should be_true\n      process.terminated?.should be_false\n    {% end %}\n\n    # Reap, gone now\n    process.wait\n    process.exists?.should be_false\n    process.terminated?.should be_true\n  end\n\n  {% unless flag?(:win32) %}\n    it \".pgid\" do\n      process = Process.new(*standing_command)\n      Process.pgid(process.pid).should be_a(Int64)\n      process.terminate\n      Process.pgid.should eq(Process.pgid(Process.pid))\n    ensure\n      process.try(&.wait)\n    end\n  {% end %}\n\n  {% unless flag?(:preview_mt) || flag?(:win32) %}\n    describe \".fork\" do\n      it \"executes the new process with exec\" do\n        with_tempfile(\"crystal-spec-exec\") do |path|\n          File.exists?(path).should be_false\n\n          fork = Process.fork do\n            Process.exec(\"/usr/bin/env\", {\"touch\", path})\n          end\n          fork.wait\n\n          File.exists?(path).should be_true\n        end\n      end\n\n      typeof(Process.fork)\n    end\n  {% end %}\n\n  describe \".exec\" do\n    it \"redirects STDIN and STDOUT to files\", tags: %w[slow] do\n      with_tempfile(\"crystal-exec-stdin\", \"crystal-exec-stdout\") do |stdin_path, stdout_path|\n        File.write(stdin_path, \"foobar\")\n\n        status, _, _ = compile_and_run_source <<-CRYSTAL\n          command = #{stdin_to_stdout_command[0].inspect}\n          args = #{stdin_to_stdout_command[1].to_a} of String\n          stdin_path = #{stdin_path.inspect}\n          stdout_path = #{stdout_path.inspect}\n          File.open(stdin_path) do |input|\n            File.open(stdout_path, \"w\") do |output|\n              Process.exec(command, args, input: input, output: output)\n            end\n          end\n          CRYSTAL\n\n        status.success?.should be_true\n        File.read(stdout_path).chomp.should eq(\"foobar\")\n      end\n    end\n\n    it \"gets error from exec\" do\n      expect_raises(File::NotFoundError, \"Error executing process: 'foobarbaz'\") do\n        Process.exec(\"foobarbaz\")\n      end\n    end\n\n    it \"raises if chdir doesn't exist\" do\n      expect_raises(File::NotFoundError, \"Error while changing directory: 'doesnotexist'\") do\n        Process.exec(*exit_code_command(1), chdir: \"doesnotexist\")\n      end\n    end\n\n    it \"does not change directory if exec fails\" do\n      with_tempfile(\"exec_chdir\") do |path|\n        Dir.mkdir_p(path)\n        previous_cwd = Dir.current\n        expect_raises(File::NotFoundError, \"Error executing process: 'doesnotexist':\") do\n          Process.exec(\"doesnotexist\", chdir: path)\n        end\n        Dir.current.should eq previous_cwd\n      end\n    end\n  end\n\n  describe \".chroot\" do\n    {% if flag?(:unix) && !flag?(:android) %}\n      it \"raises when unprivileged\", tags: %w[slow] do\n        status, output, _ = compile_and_run_source <<-'CRYSTAL'\n          # Try to drop privileges. Ignoring any errors because dropping is only\n          # necessary for a privileged user and it doesn't matter when it fails\n          # for an unprivileged one.\n          # This particular UID is often attributed to the `nobody` user.\n          LibC.setuid(65534)\n\n          begin\n            Process.chroot(\".\")\n            puts \"FAIL\"\n          rescue ex : RuntimeError\n            puts ex.os_error\n          end\n        CRYSTAL\n\n        status.success?.should be_true\n        output.should eq(\"EPERM\\n\")\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/std/raise_spec.cr",
    "content": "require \"./spec_helper\"\n\ndescribe \"raise\" do\n  callstack_on_rescue = nil\n\n  it \"should set exception's callstack\" do\n    exception = expect_raises Exception, \"without callstack\" do\n      raise \"without callstack\"\n    end\n    exception.callstack.should_not be_nil\n  end\n\n  it \"shouldn't overwrite the callstack on re-raise\" do\n    exception_after_reraise = expect_raises Exception, \"exception to be rescued\" do\n      begin\n        raise \"exception to be rescued\"\n      rescue exception_on_rescue\n        callstack_on_rescue = exception_on_rescue.callstack\n        raise exception_on_rescue\n      end\n    end\n    exception_after_reraise.callstack.should_not be_nil\n    exception_after_reraise.callstack.should eq(callstack_on_rescue)\n  end\nend\n"
  },
  {
    "path": "spec/std/random/isaac_spec.cr",
    "content": "require \"spec\"\nrequire \"random/isaac\"\n\ndescribe \"Random::ISAAC\" do\n  it \"generates random numbers as generated official implementation\" do\n    numbers = [\n      0xc9d3bc51, 0x5bc24339, 0x23e22e3a, 0x5659b89a, 0x21c6dcfd, 0x168e10a4, 0x1df755f6, 0x99d3a910,\n      0xf48f0656, 0xe9431f57, 0x839c384b, 0x238bac78, 0xd3693e2a, 0x96e06a6f, 0x1358bb9e, 0x6872ff7f,\n      0x75f9a391, 0x9d951a6f, 0x4460a8a1, 0x2818c604, 0x459b44fc, 0xe4eeacbf, 0xb13edb9c, 0x38f9a0c4,\n      0x9b6c882d, 0x44ddb798, 0x6a02781b, 0x464d8241, 0xb6e89c5b, 0xee627b94, 0x4b5cf183, 0x030800c9,\n      0x63e24cba, 0x9582bdaa, 0x8b038c2c, 0x5bcc29d7, 0xab4e8369, 0x7874b242, 0x1302a96d, 0xec44d5cc,\n      0x6cc59d03, 0x9abc6857, 0xea100737, 0xc567708f, 0xb25912b4, 0x53899438, 0xb33ba5c0, 0x08d848bc,\n      0xe32573ca, 0x1190acf5, 0xd015c2e7, 0xbe2f137f, 0x2f059bb6, 0x82ca6f0a, 0x39172da5, 0x9bcb3a5b,\n      0x8288cd54, 0x2f7a6e72, 0x371ac597, 0x3c9c00e1, 0x584ae462, 0x7420bf5e, 0xb3e7eeb3, 0xcb1f301d,\n      0x89f7548d, 0x5c758f6e, 0x5e5689f4, 0xfda0ec6b, 0xd080797e, 0xc8ce8e0e, 0x08ed5b1a, 0x75f4dca7,\n      0xc03c8d08, 0xad11d474, 0xcb4ee33a, 0x6588dd1e, 0xe71dd73d, 0x25b36d83, 0xc2a014ee, 0x1f1be022,\n      0x97748d52, 0xba47b4b2, 0xb5b0f69f, 0x9092902e, 0x8cc370f9, 0xa65b687f, 0xbb8ad147, 0x3c532186,\n      0x25ff761b, 0xf507c27c, 0xafb18108, 0x3b8e7ade, 0x3044df96, 0xf5b51be4, 0xb8b3895f, 0x56ad9f82,\n      0x13cf0045, 0xadbbcd41, 0xba984c48, 0xac14915f, 0x4dea8a1c, 0x70240f6e, 0x46e5085b, 0x44995e68,\n      0xd49a2785, 0xbec21184, 0x33bd3209, 0x28b6c25f, 0x8aaa592c, 0x642844eb, 0xb2a8bf4f, 0xb62c21b4,\n      0x1ed94071, 0x5047c204, 0x9966bf98, 0x54d6a1de, 0xd3b08718, 0x602cdd1e, 0x27d3b289, 0xf5284ba7,\n      0xe552480e, 0xb4317128, 0xa6a831c7, 0xef98ba77, 0x082e2387, 0xa60f8187, 0x1bdda376, 0xd11b59d2,\n      0x0b2adb58, 0x5f07968d, 0x63565555, 0x6eaaa2da, 0x43de6b6d, 0x86d498ff, 0xe3492290, 0x87aa3a05,\n      0x4ea8d3b5, 0xbb9fe9a1, 0x798b2222, 0x3e77c27e, 0xd263434e, 0x82d504cb, 0x5936c07b, 0x82b93bcb,\n      0x40e1ddc4, 0xfed24c09, 0x5e66d6e5, 0xb3f09f1d, 0x812b901c, 0x99b87e3b, 0x7ac6b7ed, 0x30d63060,\n      0x7508dc03, 0xa42248a9, 0xad313fdf, 0x3a4e945c, 0xac875460, 0x0940e817, 0x9f71db1f, 0xed35bebe,\n      0x29c77c31, 0x79e42f94, 0xa3dbcd79, 0x40651421, 0xd9af6853, 0x66b9ecc1, 0x9d93f3c4, 0xa38e3003,\n      0x181e1ab7, 0xc952f8ef, 0xdfaebb9e, 0x91a50215, 0x95590c72, 0xd2d2db40, 0x7a479242, 0x9ae6f3dc,\n      0x6d6ee596, 0xf0ccabd5, 0x50367e9e, 0xaf96bafa, 0xc4940ecd, 0x63a82778, 0xe40950a9, 0xfabf9e2c,\n      0xf91450e9, 0x1ad83713, 0x795209f6, 0x9f7d8ca0, 0xc4cd930c, 0x2ac7c086, 0xa24e2dab, 0x8b7a3616,\n      0xb691e3ec, 0xf30e7631, 0x3f09c258, 0x4ea46c5a, 0xd799e7d8, 0x75d3fa5d, 0x17966f6c, 0xb9f30b32,\n      0xda1e3c67, 0xab3dc36a, 0xd3a47ef3, 0x48301362, 0x0df21a5c, 0x38731862, 0xa8b52636, 0xf4b7ab4f,\n      0xb709addd, 0x0642b616, 0x645c68bb, 0x7defde20, 0xc7eb832e, 0xc5d9d39e, 0xc52256e5, 0x992300b4,\n      0xc581df99, 0xa642f4aa, 0xd4f0ba87, 0x94b9d830, 0x92c4ced6, 0xa74b776e, 0x87d32645, 0xdab3bd5f,\n      0x99f8eec0, 0xe0457735, 0xb44c5c92, 0x95688a53, 0x3856aae8, 0x3352431d, 0x77449906, 0x011d7f76,\n      0x936df33e, 0x5de7c346, 0x2f6039f8, 0x05795322, 0xd6b64887, 0x9f812dab, 0x416c484d, 0xc63687a5,\n      0xb0658c71, 0x772bfea5, 0x3ed63727, 0xcc03377f, 0x2d658374, 0x40597e84, 0xef62dfaa, 0x3ba989b7,\n      0xd1b26dc5, 0xd3a7f5e1, 0xe5de149f, 0x9c26e15a, 0x63477791, 0x3c7a0855, 0xf00990dd, 0xcb673179,\n\n      0x58759924, 0x2be2c273, 0x03165f2e, 0xe4f4832b, 0x88fa93d2, 0xcc096c83, 0xfb713a21, 0x99aa55bd,\n      0xeae7f35d, 0xddaf236c, 0xda0552ce, 0xd2fb442f, 0xfd1ac65e, 0xa680c86c, 0x7a9f36c0, 0xa5ccce35,\n      0x8060b929, 0xe2a6a2da, 0x68175335, 0x18859b40, 0xd2b4213b, 0x97a896c0, 0x119d3659, 0xbc89d7b1,\n      0x8feb1ca4, 0x68329ee6, 0x5881583d, 0xcd58805e, 0x2621ab01, 0xf0b07a6b, 0x88307d30, 0x75b6547d,\n      0x40c99197, 0x8916ae7d, 0x7f623b33, 0x951c0396, 0x2bc389d3, 0x80f0a93d, 0xfaa5640a, 0xbd5a6773,\n      0x86c411ae, 0x80171a7e, 0xcc27f2ec, 0x6ea7df33, 0x24bd0f91, 0xe5a1e0a9, 0x2f32057e, 0x3cb4da7c,\n      0x025b3f1a, 0x4f04f06d, 0xf6629668, 0x59e4708a, 0xf93cb92f, 0xbdefd4be, 0xa305c6cc, 0xd7aa0586,\n      0xc6a074a1, 0xfd3e7ab1, 0x5c3fe3a8, 0xec5ad004, 0xe5b2aedd, 0xe4b6e6b7, 0xef7a2144, 0x3f9e2ab2,\n      0x9e23140a, 0xa5f2733f, 0x1db7d2ab, 0x365a3698, 0x8d01cc58, 0xf31bf73b, 0xafcba5b3, 0xd2eaf84c,\n      0x54a0200d, 0x7df1f1ef, 0x6e6a858c, 0x0cc1c65c, 0xadd26e2d, 0x86e02783, 0x3aa30e3f, 0x221249e9,\n      0x0ca77c21, 0x1b4deef6, 0x72d63e5c, 0xdb48a367, 0x0dbc58d3, 0xd611e807, 0x4bb9d5ff, 0x445beeb0,\n      0x58326450, 0x91924ae2, 0x4027ff30, 0x5ed0ab34, 0x38580f9d, 0x4124eeac, 0x5ba2bd88, 0xbe7154aa,\n      0xb66952bd, 0xe6a08935, 0x115712da, 0xc27c05d0, 0xea230b0e, 0xcc80600a, 0xc670034c, 0x8dbb11bc,\n      0x42b780c1, 0x8491adf6, 0xe649f1c5, 0x9d39fc15, 0x63820ed6, 0x60e6a306, 0x38ebf6c4, 0xf537d52c,\n      0x98453ad1, 0x6958ce28, 0x91c47f60, 0xc791d92c, 0x9f7a347f, 0xa58743d0, 0x6b6739cf, 0x30bb95b1,\n      0x890c0c52, 0x15a8b715, 0x103cfae2, 0x46bb7f76, 0xd86585a4, 0xc1680e03, 0xb1aad2a1, 0xd56f19ad,\n      0xffa33b12, 0x6506373d, 0x16096bc8, 0xae81b350, 0x993096a2, 0x41b7a646, 0xf4f4e782, 0x0ba9ef2e,\n      0x0a90c635, 0x363b3142, 0x469afd16, 0x747bf4fb, 0xcc4d3f57, 0x343ca09e, 0x849c719d, 0xc26d6463,\n      0x3f9309b8, 0xf9d86bb6, 0x9eb17378, 0x41a37a96, 0x8c23612c, 0x7a6a50e7, 0x29c858ff, 0x01d94ceb,\n      0xfd154ca7, 0xf36e5b93, 0x41c24179, 0x7e850621, 0x27881f8c, 0xcbb4854e, 0x6c4bb075, 0x7a9b2efd,\n      0x30e57d5b, 0x3e21b866, 0x6be35753, 0x756b2486, 0x1e444d31, 0x19a4dc7b, 0xb2539523, 0x1ba56f3c,\n      0xa57cfeac, 0x2dca0894, 0x68d2dae4, 0x8eba2481, 0xecb9c405, 0x94bd6ba2, 0x7bb4392c, 0x16907ad2,\n      0x253f9a39, 0x10f05c59, 0x76300440, 0xdafe3594, 0x53c2a3bb, 0x6fb3b5a9, 0x6c598880, 0x6292e448,\n      0x81a97eb7, 0x31714189, 0x133bdb20, 0xa2438d54, 0x39481d9f, 0x07c72fc3, 0x2fa9698b, 0x6a6d2133,\n      0x8594d9b6, 0x71704614, 0x177a0b8e, 0x6e90b22c, 0x87f7aeff, 0x3c561f6d, 0xa923ddbd, 0x73250219,\n      0x3738d0d1, 0x9f765c9b, 0xb733529f, 0x215fa15b, 0x77308fef, 0xd1b2ad98, 0xb59441fd, 0x395882d2,\n      0x37e8cb59, 0xbbfc6d29, 0x4b860ceb, 0xe884d62a, 0xfcbed672, 0x44752a76, 0xa57a2a4d, 0x635b1a64,\n      0x2598ed26, 0x1f437c1d, 0xfb72da0a, 0xb3518deb, 0x79dcd406, 0x9614d1d1, 0x40a94e5a, 0xda4ef12f,\n      0xa986e219, 0xccc2276e, 0x7d3565aa, 0xa3e84df3, 0xf85ec416, 0x4647abf3, 0xf38179fd, 0x3aebddd0,\n      0x3b7b1612, 0xaaac1068, 0xe6e356e7, 0x0d42ed8e, 0x52802d42, 0xd404fa1f, 0xd02058d1, 0x87089208,\n      0xefcd8c83, 0x1580ea86, 0x3fadf252, 0xb136ccae, 0x1b1fe1f2, 0x4b120e96, 0x6721bb2d, 0x37408741,\n      0xc808db89, 0x67683003, 0xa432fad5, 0xd296460f, 0x5f7af670, 0x8c1c6a0e, 0x96f7e892, 0x34c2bee1,\n\n      0x82bfb110, 0x1f91280a, 0x3a62acb0, 0x4dd2dde6, 0x5f1bfa9e, 0xe0fa9943, 0x8afe9c4d, 0x7b4ed2eb,\n      0xa30c0d49, 0x6a5d63cb, 0x4b8cbd22, 0xfd29c18d, 0x9f1dea21, 0xcece3d8a, 0xa1e74191, 0x63a209df,\n      0xac074eee, 0xa68b1bb0, 0xd4d627ab, 0xbe495903, 0x33ceaf65, 0x849a94e3, 0x34ccfa61, 0x5c53d80d,\n      0xa1858372, 0x69ef266d, 0xaf68ef08, 0x56453458, 0xfaf982b7, 0xf85a0427, 0x322a55f2, 0x25ae65df,\n      0x2d9b56c8, 0x50db1350, 0xc20f1c2e, 0x3af50ad0, 0x45768784, 0xb220d550, 0x2e8a32f6, 0x176afab5,\n      0xba64e5bd, 0xb0e70111, 0xce9127d8, 0x725a471a, 0x88b2112d, 0x2392e3d8, 0x7b73526a, 0x7b406495,\n      0x07e522a7, 0x96edd53c, 0x417c9383, 0xa3e38188, 0x448c71ec, 0x3b8e482e, 0xd83e8c22, 0xd1e71c06,\n      0x91337040, 0x27bac997, 0x1b7bb428, 0x6e92c172, 0xe73118f8, 0x43c8a615, 0x23b7f25c, 0x73905a73,\n      0x45c28f39, 0x2824e125, 0x63ce182c, 0x18dcd917, 0x674e35af, 0x2234b403, 0xe68e96b8, 0x3d83a78e,\n      0x6f11c547, 0x4522dbfe, 0xfb3cd32e, 0x46d4febb, 0xd5eff693, 0xd0689b05, 0x11eedf6d, 0x5bc2a3b1,\n      0x18a45c4f, 0x75c74746, 0xdc1015d2, 0x794843c4, 0x0ac0f8bc, 0xa3378645, 0xc56522d6, 0x2a9679a3,\n      0x88498acd, 0xdc23aab2, 0x4b90528c, 0x0f427100, 0x3eac1a62, 0x09416e90, 0x3fbff552, 0xce02dd7a,\n      0xc66d9b68, 0x91f4dec5, 0x130d1303, 0x7c2e2487, 0xe770be71, 0xe8e7055c, 0xa7402dbb, 0x0aae257d,\n      0x6c5e6e10, 0xa95d3cd7, 0x666e884f, 0x3dc18b81, 0xd3f7b6d7, 0x0fe62b61, 0x0ae725c8, 0xfcffa37c,\n      0x500ee6bc, 0x44e82874, 0x3938bd47, 0xd9fddfd5, 0x651cf7d2, 0xb5830c4b, 0x143cc0ac, 0x04f252b7,\n      0x1b5e9396, 0x27dcbb2e, 0x832296f6, 0x2f67a163, 0x223a5600, 0x5ed5a24d, 0x633c4ccd, 0x7d91df05,\n      0x26bf80e4, 0x97d822b3, 0x27d5967e, 0xf55a625f, 0x932752fe, 0x310f3a4b, 0xc21a3530, 0x4acada1c,\n      0xcc29cb9f, 0x1328d6c1, 0x2ae2b1cb, 0x5da94c59, 0xd9b4c606, 0xbaec63d4, 0xfeb899ad, 0xdafae2ba,\n      0xde4b5293, 0x37c9aca6, 0xe5a1e7b7, 0x4de7e010, 0x994ce0b5, 0x8c3fb14c, 0xeba0b9b9, 0x1afcb533,\n      0xbd7a1946, 0xb7861a44, 0x8232f8d0, 0x91862386, 0xd47a81c2, 0xb73d99ce, 0x16d02747, 0x8a99a038,\n      0x69283fb0, 0xf36f12ca, 0x6f6f98d5, 0xaf374358, 0x3cff4879, 0xd832caeb, 0x3fe70258, 0xec27aa77,\n      0x92c18b79, 0xe2ab8c22, 0x2a614806, 0x294b0402, 0x33568196, 0xb98cd722, 0xdfe675a9, 0x1ce11ef9,\n      0xc607374c, 0x38f2cd25, 0xddb6c8ae, 0x79a8d47c, 0xae7de4ad, 0x6f4fec68, 0x0f8eb3f7, 0x096794a2,\n      0x957962f9, 0x146383ff, 0xc1a6caf5, 0x2959dd33, 0x9f365615, 0xee8a6df8, 0x75424149, 0x247facf0,\n      0x8ab708c3, 0x5d01ef1a, 0xd53bc193, 0xd47a15d8, 0xba6e2ac2, 0x5e2dbd75, 0x7e77d88d, 0xd8ec6f06,\n      0xf49bcc85, 0x9c20c879, 0x68335c41, 0xadf8cb04, 0xc96c3a61, 0x82116d16, 0xb94b3371, 0xe7a54a30,\n      0x3b4f850a, 0x87ab0f70, 0x653c12f3, 0x5a3fa796, 0xe4d21db0, 0xd900acd2, 0x9d368af7, 0x3a6439fd,\n      0xa0bfd498, 0x72e18ecf, 0x3f503e27, 0x573d1033, 0x4b2aa4de, 0x4ec87c39, 0x341ab923, 0xf878ea12,\n      0xbf660952, 0xbe7efdc0, 0xe285d42c, 0x6fab666b, 0xefcd9fc2, 0xb173ca19, 0x5872df57, 0x03c045ee,\n      0x6f4fa2a9, 0xe6af1827, 0x8536fcbb, 0x691d4ea5, 0x3223f217, 0x678ce439, 0x9a19d63f, 0x7c64d694,\n      0x2ca3ccfe, 0x8e1e5565, 0x56d7c18b, 0xb00d300e, 0x0b716925, 0xafb7f887, 0xf5102231, 0xf2799846,\n      0x1983ee20, 0xbfec746b, 0x6ddbaada, 0x4d769622, 0xe93dea27, 0x690fbdec, 0xbced48ce, 0x276a499e,\n\n      0xbf093124, 0x23838afa, 0x3f5c5d8b, 0xca56604f, 0x2700fdb7, 0xf4c740ed, 0x66aaadf4, 0x51e04296,\n      0x7b32efdd, 0x3a0ad2c8, 0x5242b4bc, 0x48696e9b, 0xfaacccf4, 0xb4a3c7fe, 0x6bbda953, 0x3da076e6,\n      0xe7561b1b, 0x38709d67, 0x66f7a62f, 0xd56018be, 0xe8060fd2, 0x1c916d7f, 0x68b6c825, 0x1a8b1f5c,\n      0xb19b41f8, 0x382d6a79, 0xc663c584, 0x12c7c27f, 0x421d940a, 0xe898845d, 0x73765c18, 0xd5cb7860,\n      0xbfe91037, 0x89cedb70, 0xd4377332, 0x9c4a7921, 0x5decd505, 0x95383a09, 0x06b1bb68, 0x9d8ce838,\n      0xe7c17b37, 0x0d53b7f3, 0x6744a32e, 0x06d730c4, 0x540b86f2, 0x525d02f1, 0x09b33d66, 0x9ae35843,\n      0x7e158d43, 0x69308bb3, 0xa796ccc9, 0x7d6f1f9e, 0x08e0b6fe, 0x06a26f58, 0xa4451e55, 0xdd51ff7e,\n      0x03976dac, 0x8d7d65be, 0x94bba358, 0x08eccc30, 0x417c2afd, 0xb5994b19, 0xecea3f75, 0x90a068dd,\n      0x947a43ca, 0x6c946efc, 0x7c639e05, 0x9e8cc79b, 0x969020e9, 0xd90c4bcc, 0x6d86ab67, 0xb0ac8cae,\n      0xeadf5da8, 0x82261e03, 0x0b8d0239, 0x3796f6a1, 0x75975ebd, 0x9e770049, 0xc7a0c2f9, 0xc88f8227,\n      0x3fc2846f, 0xb733d3e8, 0x66a4d3f5, 0x1b972522, 0x84f85127, 0x630db5a0, 0xfb0df03c, 0x246352ba,\n      0xebcd2c3c, 0x04d71c76, 0x451ff5d0, 0xa24e4936, 0x63767436, 0xfea963af, 0x0fee93aa, 0x12b1392e,\n      0xc658d0b3, 0x8d91d5d0, 0x389f9550, 0xa8fdc2e6, 0x6173acdd, 0x05b4c3eb, 0x1dc59f66, 0x933c2626,\n      0x39d8cb9c, 0x58530135, 0xae81570e, 0x06b28d9f, 0x824a7eda, 0x95dc52bb, 0x7fd2a088, 0x836e4aaa,\n      0xa3faba55, 0x4b22de53, 0x053e74f0, 0x66f33bfa, 0x892e58fb, 0xd9e6197d, 0x8986c877, 0xf754b340,\n      0xdfed5066, 0xca6d31d0, 0xfdab4f34, 0x96f73339, 0xfa94f181, 0x3829c769, 0x200975d2, 0x556c5516,\n      0xe69d214c, 0x1b2f377d, 0xca3043ee, 0x9a0650c9, 0xe4744d6e, 0x82c3b11d, 0x83da8b3e, 0xc888cec9,\n      0xb744dc12, 0xd59db035, 0x13323e1d, 0xc2750391, 0x3cf5e0a8, 0xa24e4f2c, 0xdd76c0e3, 0xcef10bc8,\n      0xe09f8ad3, 0xdef56528, 0xddf7555d, 0xe5a60292, 0x43e0fef5, 0xce5e3d76, 0x4d85e8cc, 0x43d50543,\n      0x5614f236, 0xb49730da, 0x8b0a119c, 0x7fa45199, 0x0f4b844d, 0xb07d5fcb, 0xc7e32e83, 0xe5e045db,\n      0xa5c18a5a, 0x9433dea9, 0xe374ac1a, 0xcfe5ba5f, 0xea249655, 0x5dc86bf0, 0x2db637ce, 0x76b12992,\n      0x43efac8e, 0xc09da4b1, 0x0d866d07, 0x70df34a9, 0x6900cf8a, 0x7f86895d, 0x7baea9dc, 0x76230ff3,\n      0x6e57c6bf, 0x6d900ec4, 0x82f04343, 0x70c19cc1, 0x7aedf1f9, 0xf1d50e4e, 0x3218b1d3, 0x156777a7,\n      0xc668e59f, 0x59b77c65, 0x37e6c832, 0xa6d25dd2, 0xd1d8dfef, 0x1a566e2e, 0x662937ff, 0x40256e65,\n      0xbca3cab0, 0x022837ed, 0x31bba0bd, 0x1cccd256, 0x887b4889, 0xa0c7f7ee, 0x4ec535a8, 0x641b2e12,\n      0x65f017a6, 0xaab4c47e, 0x2559ac73, 0xf31260b3, 0x9050014a, 0xfd52848e, 0x2e0ddbb3, 0x40edae6c,\n      0x62f498b4, 0xaeee2287, 0x10a5717d, 0xae9011b9, 0x088328ef, 0xa207177f, 0x2bd06251, 0x9612528e,\n      0x238a6b79, 0xfb7331fe, 0x605afd54, 0x7ce1474e, 0x9e8a5892, 0x51edec16, 0x48c80b8c, 0x93fef6d3,\n      0x70318b34, 0xaaa51ec0, 0x03797400, 0xf56c21f3, 0x6ccac30d, 0xe05f9da9, 0xa4f9a714, 0xbda709bb,\n      0x75ca2538, 0x4b2cf037, 0xb50e8e47, 0x5adc1d66, 0xb61057f9, 0x6092a2d8, 0x0facf24a, 0x814e5469,\n      0x5a254102, 0x808a5132, 0x459c59d1, 0x084b7a84, 0xd1f76be9, 0x9e0da4f6, 0xaca93892, 0xe4273720,\n      0x17cf3431, 0x485e6422, 0x90cf5794, 0x8be8e508, 0x59867098, 0xd6158c8f, 0xacead5ae, 0x89b82d35,\n\n      0x85f3a271, 0x33e29bcc, 0x19cbbd7c, 0x270f8bee, 0xbdd7a6f7, 0xa4cd6d85, 0xd041f8c9, 0x4298ed12,\n      0xff115237, 0x73051a2c, 0x0f171a8d, 0x95c41c72, 0x4ea9f45f, 0x9d8353d5, 0x05edcd5f, 0xd9642d8d,\n      0x2f3ee4f6, 0x041d823c, 0x75f4dca8, 0x6fe27698, 0x5482c748, 0xff74c84b, 0xfd0b15f2, 0x7293ffe0,\n      0xb2f8fe1b, 0x5b7e05b1, 0x52099c24, 0xc7d7f373, 0x6a4f4d3f, 0x587c1389, 0x0e10b7e9, 0x4b46d738,\n      0xd40643e8, 0x9125622c, 0x23797663, 0xbb8ed692, 0xac7bf97b, 0xe653a559, 0xc7fdb799, 0xd0d8eff5,\n      0x4f6aa37c, 0xc257bd62, 0xf4fc4c67, 0xe0134615, 0x31abc8ab, 0xf1f40499, 0x6e0c379e, 0xde0146ec,\n      0x5f43eb0a, 0x5d711c27, 0xc6226a16, 0xfa9a6d66, 0x1e4420f4, 0xd257fc82, 0xbf8ff660, 0x582da380,\n      0x458d55a7, 0xba7e3bf6, 0x558019f8, 0xb610fc74, 0x3799f1e9, 0x7483519a, 0x803afc7d, 0x07db3fc7,\n      0x8236b726, 0xedfbb74e, 0x52eb0bdd, 0xea642027, 0x248f85f2, 0x0c582c49, 0x82312222, 0x9f2ff69d,\n      0x2bec2a0e, 0x2daf3dd2, 0x679e9b7a, 0xeb35ae42, 0x185697ea, 0x393d0939, 0x5a5abc32, 0x4ac3d0f6,\n      0x77878265, 0xcc9bb851, 0x383cc75b, 0xb15d4035, 0xe2ededb8, 0x302855b3, 0x904061bb, 0x482e22c7,\n      0x71cbd2c7, 0xa9356aba, 0xf01d9bdc, 0xf2c123b8, 0x112337a9, 0xd44ba682, 0x498d6443, 0x07e64438,\n      0x1f5c9651, 0xb02a7f08, 0x8801a6b7, 0x76cec13d, 0x89306cfd, 0x127cabba, 0xf7a3f316, 0xcca8ffb7,\n      0x61d1af61, 0xabcc5d41, 0xee954691, 0x6877dd8e, 0x34120970, 0x47dbdda4, 0x802ce9a5, 0x904bfab9,\n      0x3165fbe2, 0x35e94c9e, 0xe7e884e7, 0x18388950, 0x5dc990ae, 0x86b2e2eb, 0x5d8b4fe8, 0xee782264,\n      0x53b3a6e2, 0xc38be31f, 0x6b9a8eb6, 0x6a5bab4c, 0xc88f8e96, 0x3cc2a563, 0x70311ed4, 0xae4e33f4,\n      0xa62808b8, 0x6df5280d, 0x8694818e, 0x96aff342, 0x9aaeddb4, 0x74b680e7, 0x7ac429fa, 0x9d8ed6d0,\n      0x94267aa2, 0x5180bb7c, 0xd2af1ffa, 0xb4be9992, 0xb6fa5e13, 0x72ce329f, 0xa5515829, 0xa347b435,\n      0xa4b1f92d, 0x274f4276, 0x6a29e239, 0x9cbbc43d, 0x8165727b, 0x4edcfa5c, 0x9bef5bad, 0xf1af9a5b,\n      0x2d64747d, 0x95554575, 0x5d09c903, 0x01c5d493, 0xf3b80fca, 0xba85b202, 0x8a73bbe5, 0xfd84501f,\n      0x52ce6867, 0x34c43428, 0xce8025f6, 0xa5df63ae, 0xdd8b2f3b, 0x7a830956, 0x1243804c, 0xbd046900,\n      0xfc796d9f, 0x32a4a0c7, 0x5e2d9837, 0xaf7143f1, 0xf2e7a6a0, 0x48cfb61b, 0xcbe0e7f1, 0xa489305b,\n      0xa748c9cf, 0x021fe513, 0xce10ca3f, 0x09774f22, 0xd364fd26, 0x7db83366, 0x7a28fed4, 0x06e727e8,\n      0x20188c5d, 0x6b85a86d, 0x60c2e299, 0x7fef9ea7, 0x1ba5fad4, 0xd1a21434, 0xf5271e9d, 0xc1d25786,\n      0x7a695f45, 0x9bd51a87, 0x477bf859, 0x7d6956bb, 0x89dc17c9, 0xca9ff278, 0x5c875bf3, 0x3a3a604b,\n      0x122cd226, 0x8d9fac92, 0x93118c5f, 0x45df161c, 0xf8ad087f, 0x9c935597, 0xe5decde2, 0x12cee2b3,\n      0xcaafd5ed, 0x76fd4a54, 0xb31fde7d, 0xa7a37ad9, 0xbce43857, 0xa04a5d0c, 0xf507d699, 0x470890a2,\n      0x459c9411, 0x0bf685f3, 0xb642bc2d, 0xceff08e8, 0xd323b228, 0x456f8c5b, 0x61c77e99, 0x50451742,\n      0xec37b849, 0x818a055d, 0xef4c354f, 0x507a6abc, 0x156cf8c1, 0x63c3986a, 0xdf988273, 0x5768018a,\n      0x6be28478, 0x9cba4cb7, 0x9572d2d2, 0x794133f1, 0xc28bd648, 0x26302b75, 0xfddf9755, 0x005f339a,\n      0xcffd2e5f, 0xc4d8a62e, 0x9b6f3331, 0x4420fbc9, 0x63bd0dfc, 0x5da9e6f2, 0x50386f62, 0x01dccaa5,\n      0xc2878f8f, 0x78808e3a, 0xb606ec22, 0x489dee71, 0xdcdfad1e, 0x56573e6c, 0x96bf86b2, 0x85a3e1e0,\n\n      0xd7e500c8, 0xb1b710bf, 0x14014a2f, 0x6dcb205c, 0x84760814, 0xff4c0b6a, 0xc6fc0d95, 0xd2e37fec,\n      0x947d7e29, 0x87034305, 0xcbd2e40b, 0x9ed31426, 0x65795f67, 0x6d886463, 0x24ca5721, 0xa189961c,\n      0x25965bbb, 0x449f5518, 0x69ab124c, 0x5e92550d, 0xfa6cd0b5, 0xa09bd53c, 0x061c4f21, 0xdd0787ed,\n      0x2badf5b5, 0xb1ee8404, 0xc9b139bd, 0x446b17f0, 0xd3a8ad77, 0x00db18ba, 0xd99a1fc5, 0xc88a2589,\n      0x3682fbe0, 0x3906800d, 0x330390d9, 0x3a24309f, 0x1e15d59f, 0x112a3945, 0x2655bf38, 0x662f145c,\n      0xa08091ba, 0x210c710a, 0x66ba1e76, 0xc135991b, 0x7c11e074, 0x245fdf71, 0x41986e27, 0x7308bc40,\n      0x1eed7462, 0xac6861ef, 0x0c1f47e1, 0xf2c9451d, 0x4b077bb1, 0x7cdf31c3, 0x09dfbe0c, 0x4db2d75c,\n      0x50483fa7, 0x0c402cbc, 0xa10fbe9b, 0xffeadf92, 0x038cd732, 0x893d954d, 0xa027cca5, 0xb4086433,\n      0xf7c1c735, 0x6a1e4a89, 0x0d63555f, 0x7b8f64ee, 0x624eefaa, 0xcde7dc5b, 0xc6ac2f05, 0xeca4dd48,\n      0x08c15349, 0xace3a116, 0x6d1c7182, 0x69617cc9, 0xadbad9cd, 0x624b955c, 0xef725d07, 0x216c9609,\n      0xac70f55e, 0x10851c19, 0x3768e0b7, 0xb0857be9, 0xb1e8a514, 0x3c8f9c61, 0x450b999a, 0xba3f623a,\n      0xbf3db9c1, 0xa87e5b1a, 0xe8edf426, 0x6b1e1e07, 0x47abb2ee, 0x91eb245d, 0x94ffce4c, 0x0cd6f90b,\n      0x51bff8ba, 0x6e169820, 0xcd530596, 0xd7666735, 0xb5338e62, 0xcd412881, 0xd235455e, 0xba0e2b24,\n      0x16cdcbe5, 0x51a4112b, 0xaea5e49b, 0x4717e79c, 0x1ba26991, 0x0f968182, 0x5e575fd5, 0xe9ebeb48,\n      0xe3043134, 0x1cc2971d, 0xde163e51, 0x25a0f7dc, 0xa6243182, 0xae7d8d99, 0x4bc62e48, 0x6c6820df,\n      0xed387c95, 0x175a8e05, 0x3e6c405d, 0x46be9398, 0x88f25b5d, 0xf1a0689a, 0x185bc685, 0xc0ca1341,\n      0xd4f58df4, 0xaf545c04, 0x9858ebbc, 0xdcdba061, 0x1e5d3c23, 0xa2a9bdc6, 0xa9dbaf2e, 0xfdc145ca,\n      0x9aa1a235, 0x3294f226, 0xd41be2d0, 0x12183332, 0xa85a973c, 0xa6f2ef84, 0xd41672d2, 0x3456ab6a,\n      0x0ea7dab3, 0xdad9a232, 0xeabf0fd5, 0xdf97da1b, 0x1c253238, 0xd3f63462, 0xbff10852, 0x25553329,\n      0xb7e83ce4, 0xd88ee43e, 0xba1e1ec3, 0x735b85c7, 0x3827618e, 0xfd753d4a, 0x3f69630a, 0xb2098f0b,\n      0x3dc18f64, 0x32535ead, 0xcd8460e2, 0xa3e1b570, 0xff36a508, 0x237f4641, 0xa11151ed, 0x6a25a236,\n      0xf1c46fbf, 0x2cdb30d8, 0x4aa22acc, 0x95d471fe, 0x43ecab6f, 0x54944166, 0xa140cb3e, 0x852957ec,\n      0x4b4646e0, 0x6ecd5ddd, 0x395f8ab5, 0x590cff23, 0x5a7c4318, 0xd9f5c6ca, 0x2032b12d, 0xe3283255,\n      0xbe329b88, 0x2ff64352, 0xf3efb86e, 0x5e73c4e5, 0x549479c0, 0x0ea61894, 0xf1db0250, 0x5050d378,\n      0x3b006062, 0xce0ecdd0, 0x134dab3d, 0x2556cc2e, 0xd78d5278, 0xa6fc08ab, 0x999f01bb, 0x3c31d252,\n      0x85b119d8, 0x31088dcb, 0xd474aab7, 0x3d774127, 0x17f19843, 0x5492aa08, 0xbfb72b87, 0x0076f366,\n      0x49c6bf28, 0xa0454cfd, 0x07b18806, 0xeae3fc26, 0x00cdf7b0, 0xe2a1ac66, 0x04e489f4, 0xdbf83b34,\n      0x97806e32, 0x34dbe9d4, 0x3838f555, 0xf19d40c1, 0x63290196, 0x4d72d76d, 0xc4e2ded6, 0x69bcdafc,\n      0xce6a8863, 0x47cb72af, 0x861f07c8, 0xe1f201ef, 0xd2c59529, 0xffeca87c, 0xf4f2c66b, 0x15560271,\n      0xfc01981f, 0x54374c06, 0x29888b52, 0x80ecb175, 0x978ebba6, 0xc1625604, 0x2a947eef, 0x99114020,\n      0x3ed96c92, 0x29b05341, 0x4b117e1d, 0x5d8ede98, 0x24fe195d, 0xcc59369e, 0x26d547a7, 0x336ca792,\n      0xa4951c6f, 0x05ca60b4, 0xe79ba4c4, 0x1977c433, 0xb74b6120, 0x27ef2699, 0xcbb472a3, 0x2e284181,\n\n      0x95670ef0, 0x966d7b3b, 0x773a7d01, 0x9c9446b8, 0x33418f0b, 0x0fd8ac87, 0x6bbe0fd1, 0x459d4e8a,\n      0xe0f48ceb, 0x39c8a071, 0x492b0385, 0xdc2d8106, 0x0d640a49, 0x0e488619, 0x0334a66d, 0x7a1fd6cf,\n      0x2de4ec65, 0x43cb3c36, 0xd0cb9f5d, 0xc9447608, 0x4aa45e43, 0xd36979f3, 0x90ab19c1, 0xa17b3710,\n      0xcfc9ca96, 0xecfca25d, 0xf6b4675c, 0x358840f9, 0xc2438e95, 0x2ba4c297, 0xc031157d, 0xcccbab77,\n      0x931e672d, 0x032b1544, 0x87493d48, 0x115914e4, 0xb3cac92d, 0x36ea3f94, 0x79befa66, 0xf6445c4e,\n      0x0ac194b0, 0x17aedf31, 0xf9abc1bf, 0x461f440a, 0xed0ea9d7, 0x0804b4d4, 0x15963a7a, 0xeb5d6dcd,\n      0x469cd45d, 0x1a04df48, 0x5c9c5096, 0xef2cbec2, 0x4f015e16, 0x89e9e7df, 0x789f59df, 0x4dfd7e25,\n      0xd80fdc9d, 0x9ea31b0e, 0xeaa1bcc4, 0x55199a64, 0x0ffe2196, 0xca4f0c73, 0xf41bf7d2, 0xfa3c594c,\n      0xd42300d3, 0x8ce4032f, 0xa0a1b50c, 0x58c0fa2a, 0x5e6c0bf3, 0xaa202af8, 0x788902c8, 0xbc9fc92c,\n      0xa46d3a64, 0xba0ee3de, 0x2cb98355, 0x48212242, 0x3207e644, 0x58d8754c, 0xbf85197b, 0xca4e1206,\n      0x5db644c0, 0xc4537c27, 0x6eb18644, 0xe1d4d97e, 0x978868b1, 0x44853c93, 0x01627bb5, 0x78d648b7,\n      0x88019cc3, 0x7f90b9a5, 0xfe10a325, 0xeebeaac2, 0xd1059821, 0x19a6db47, 0x709ef533, 0x0a91b078,\n      0xb9088308, 0x41025bb8, 0x55629de3, 0xe6829e3d, 0x66a88813, 0xf49b085d, 0x8007ae69, 0xf89012b0,\n      0x568ad64b, 0xef7c5836, 0x98b98e9f, 0xe2493494, 0x3fe71fe3, 0x8d9eafa5, 0x05c751a1, 0x076a0060,\n      0xf26a46f9, 0xe02ae45b, 0xcd778771, 0x176378e6, 0xea4c1fd2, 0x38b6812e, 0x9ef3c3ea, 0xd36fb051,\n      0xa659a750, 0x04a5e106, 0xe3354c3f, 0x091e149f, 0x50551101, 0x18d2fadf, 0x256a2666, 0x4be6ec5c,\n      0x9618a20c, 0xf013c1d8, 0x17935af2, 0xc8bc45e8, 0xad8c9f0b, 0xff98790a, 0x123e2e5b, 0xc3a3ce26,\n      0x2b40d93a, 0x62069e01, 0x874835cc, 0xa75c4a18, 0x142a8452, 0xed02ca3a, 0xd6261ceb, 0xf2ee3912,\n      0x190172b4, 0x647f7a4d, 0x08486967, 0xe88498f5, 0x8f05debe, 0x61a9d1c3, 0x1cc81029, 0xa241407d,\n      0xf264e0ba, 0x53b8c4a5, 0xee794fa3, 0x2a2c5298, 0x9b102fea, 0x7f14fcd4, 0x2ab75348, 0x113d6caa,\n      0xfe748b44, 0xb7b04fea, 0x14397082, 0x8c624a5d, 0x308a1b08, 0xc5e21f5c, 0x0bad41da, 0xf700fb15,\n      0xb6c6d022, 0x2703957d, 0x7cfba9c9, 0xf2f4c413, 0x2da9341a, 0x688877ca, 0xfd8552a3, 0x1c322698,\n      0xfe509b1c, 0x42cfa85b, 0x97e8d290, 0x3f68698b, 0x2dd551dc, 0x5422bfcf, 0x0ea7242c, 0xeb2a57ba,\n      0xbe4b6aac, 0x4d4ff5b9, 0xc8517763, 0x1455a465, 0x85e421b9, 0xffb407f0, 0xf943c9c7, 0xac6bea3c,\n      0x85173cd4, 0xccef5de3, 0x322dfdd8, 0x029975a1, 0x6dc9053b, 0xbf6a06f1, 0xc96e6205, 0x5e3f2e43,\n      0x98e031e8, 0x8783f11c, 0x91e08345, 0x09b3172b, 0x40c4a9e7, 0x4e200b1e, 0xf052be0c, 0xb3996e12,\n      0xae58176f, 0x0d5ce9f5, 0x498c1603, 0xfc9e2498, 0x955b974d, 0x0ddbd843, 0xc9f1c6d7, 0x321ba8fa,\n      0x4a1be0d9, 0x81ce91e6, 0x43d35f57, 0x3dbb7042, 0x76dbf18c, 0x9b8fc29c, 0x7ba93a93, 0x7bd1e93a,\n      0xe58ec417, 0xb5fff41e, 0x5f1d2df7, 0x051bd3a1, 0x2293e9c3, 0xdbfc52a4, 0xa13b3b49, 0xcc622596,\n      0x94ac3b7c, 0xad1f0613, 0x78775b92, 0xd095715d, 0x9db05bd8, 0x23d90a52, 0x329e0206, 0xadcde607,\n      0x5de3cf48, 0x552c1a6c, 0x51d68fe1, 0x33bb3178, 0xfa8337b3, 0x3ce33684, 0x16795e6b, 0x595c2668,\n      0x7a80a22c, 0x257f1b50, 0x70a49552, 0xaa4bd52a, 0x62769811, 0x316bf5a7, 0x6e3b7298, 0x18e6d130,\n\n      0xc85f8cd0, 0x55bb5c19, 0xb35a8372, 0x8aedb363, 0xdd1f2aba, 0x870a0079, 0x8991b4fa, 0x97870061,\n      0x4145c192, 0x0e214b78, 0x7c7adbc2, 0x3568cbc7, 0x3401d176, 0x960e13cb, 0xccc5b5c3, 0x1b37cce4,\n      0xba34998e, 0x7cf1c415, 0x850d9360, 0x893bfec0, 0x100203b5, 0x6ea5169c, 0x8d1d9bc7, 0xf54af568,\n      0x0d897530, 0xdf0a9502, 0x2744a96c, 0x152681c1, 0x9505c01d, 0xb05a4dcc, 0xa720af3f, 0x3b8e0bcd,\n      0x8c995fda, 0x227360cf, 0x7dfda437, 0x695547ac, 0xd54592a4, 0x2b21187a, 0xbab355df, 0xd13337e6,\n      0xade21480, 0x27e9a890, 0x6d1c139c, 0x48c1d794, 0x2f84b190, 0xa30db3d3, 0x43fcb2e2, 0xdee19a7d,\n      0x72dcdab8, 0x2b60180c, 0x5ec131a3, 0xb7a98701, 0x0dcf8888, 0x0e51f081, 0x6b35411d, 0x1ec8cf9d,\n      0x5d30ba25, 0x70eb86b9, 0xdcd3067e, 0x5038a362, 0x4372ba7f, 0x519acd12, 0x1a957ec1, 0xed3be91b,\n      0x3e0af349, 0x7217a72e, 0xd2448e74, 0xc506c024, 0xac823e92, 0x40c3cc6b, 0x24494058, 0xce6d5a7f,\n      0x1b49dba7, 0x585c0ca6, 0xd7ad8721, 0x1755a8a0, 0x2e84a31f, 0xc62a76f9, 0x867578d1, 0x216967e9,\n      0xf9736f4c, 0xd0438060, 0x9771e768, 0x57b56966, 0xe8b0685a, 0xa4e3bbc4, 0xca385706, 0xfd42c326,\n      0xf8278dd0, 0x152f7425, 0x435ea0c3, 0x1358f804, 0xe344b49c, 0xc2c2c265, 0xedb955e1, 0xe243a719,\n      0xaf79a012, 0xb28cdb93, 0x2738bcf4, 0x141e83d7, 0x85075da4, 0x967c380f, 0xa98d5846, 0x09900649,\n      0x3a59755a, 0xfad73306, 0xe3d1b112, 0x54d1cf2f, 0x7d8d8991, 0x56281574, 0x7d3b00f0, 0xc99b06f4,\n      0xa444dd51, 0x8e59fae9, 0x0a0e076f, 0xf5199ccb, 0xd4f27f02, 0x79e5be6f, 0xd3db7857, 0xe242c216,\n      0x31250c93, 0xd1b46a66, 0x1cba290c, 0x9850c30e, 0xbfefa0c7, 0x61f3e260, 0x60df83c7, 0x0b04e4c2,\n      0xafc1dd96, 0xead61518, 0x816f41e2, 0xaa957f49, 0xfc72605b, 0xc51508a1, 0x9712df82, 0x09c2f721,\n      0x35d0ba5a, 0x06537dd9, 0xa86bf74b, 0x1a89c8e1, 0x82fe165d, 0xd42f920a, 0x2a0b13dc, 0xbd926f61,\n      0xd9b4680d, 0x364e43b9, 0xcc51c9e5, 0xbba59f71, 0xbeb2e378, 0x95e3d022, 0x3d6320e9, 0xb21a2508,\n      0x5d3e1533, 0x81d5fd42, 0xf9fda71e, 0x1fb91b2a, 0xf733898b, 0x15dfaf9b, 0xdcce2668, 0xecacaeff,\n      0xc3bd0c52, 0x193e8d4d, 0xd77dfa27, 0xa2110dee, 0x7323ea1a, 0xfd7c210c, 0x767329bd, 0xef7f9ab1,\n      0xe4aa8eee, 0x35b9d7c9, 0xd0c9b92d, 0x9cbcee13, 0xe5de0bd0, 0xfcc3ed47, 0x6ee9f03f, 0xdea97483,\n      0x6212a2d4, 0x909f4e5c, 0x35a3bd49, 0x60fd5a28, 0x6d7f806b, 0x118981b7, 0x03b86a2d, 0x7cb2cdd1,\n      0x40cc7957, 0x677e0154, 0xd061a377, 0x97e3e18e, 0x1843e4b0, 0xeeeddc76, 0x75801eac, 0xebd3674c,\n      0xa8e9304b, 0x1698d0ef, 0xdeb4956e, 0xac9bca76, 0x6cd59737, 0x9e187ad3, 0x05d830f3, 0xb41558a4,\n      0xdead3b3c, 0x97bb020f, 0xbed2e29e, 0x82e36d9b, 0x2ee33d31, 0xfd2cfcb8, 0x72d274b7, 0x18460d5c,\n      0xca37fa01, 0x7cae93bc, 0xabe70b1b, 0x069cf149, 0xd8296b94, 0x3d4c000a, 0x866cd300, 0x49bd7d07,\n      0x59674a55, 0x9020388a, 0x486c54d0, 0x6509ea68, 0xccd4efb9, 0x0b3a7b1a, 0x2a4fb991, 0xd71a571d,\n      0xc7acd10e, 0x945ed65d, 0x4f01036a, 0x0f74648e, 0xb1b8a2a0, 0x338f0582, 0xf538dad9, 0x4f8cfaf3,\n      0x654bac01, 0x85d42b72, 0x0235ee54, 0x6aa57e45, 0xd3019482, 0xf710a9d3, 0x15f595f4, 0xe62cb2a5,\n      0xf23c9621, 0x02f326b9, 0x2b91b5c1, 0xdf661931, 0xf84051e6, 0x73ef77d9, 0x959f6fac, 0xe9ba3125,\n      0x35debf70, 0xa845be57, 0x3876135a, 0xd37ede86, 0x04f737e2, 0x7f23a9d0, 0xa4eaa6fa, 0xeab12d7a,\n\n      0xf3abadec, 0xbb13d480, 0xc20ffa91, 0x2f6a8f45, 0x877a094c, 0x8d1bab36, 0x063a2470, 0x5d651277,\n      0x448b9f2f, 0x9662bacc, 0x2a2e3487, 0xc109e925, 0xddff1e32, 0x3cdadf7e, 0xe1368518, 0xc09a4ecd,\n      0x5a2a7ab9, 0x58b49adf, 0xd1b128d8, 0xd1cd4427, 0x76a936a4, 0x26cc7caa, 0x13505281, 0x1fd0315c,\n      0x988fc609, 0xccd5a3a1, 0xd99c3264, 0x6d8a981a, 0xb70491fb, 0x3322c31f, 0x43c110fd, 0x102cb525,\n      0x336156ab, 0x2e29ae77, 0xc4dadf1b, 0x2ca1e105, 0x9c92a94b, 0x93cc3679, 0x4502bb92, 0xce0b8b1c,\n      0x5a9ff4d1, 0x058c7094, 0xf09c7cf7, 0x4806cbad, 0xd0e49028, 0xf99cb598, 0xa80b2ec5, 0x7093fd4b,\n      0xe65a8351, 0xab613222, 0x31cc8cd5, 0xc6573b12, 0x7dd9d283, 0x90a15cc7, 0xdfdc977b, 0x3fa23771,\n      0xc050843a, 0xfc8253eb, 0x9f0abe24, 0xf95f6007, 0x6a07b363, 0x54b4060a, 0xbf31efa4, 0x2c3aad79,\n      0xda668f25, 0x138e08f2, 0xb71ffacd, 0xfabb4315, 0xd9b09728, 0x518b069e, 0x58ef4db9, 0x25e5befd,\n      0xee556e36, 0x4dac0e7c, 0x1da563aa, 0x371e3ff1, 0xa66fa7c0, 0x42cf6640, 0xab5ed351, 0x6e775cbc,\n      0x3884e384, 0xbeb449dd, 0x89e83ebc, 0x5fa85af8, 0x2b82bb1d, 0xadc84e52, 0x40412b32, 0xc1efda9b,\n      0x4a1498dc, 0xf7f9802b, 0xc79d614e, 0x2fd1d562, 0x2c937e6b, 0x2cc67267, 0xe4269551, 0x0d64550f,\n      0xb484068c, 0xcf97aba4, 0x4f89c39b, 0x264ca7fd, 0xfb90a8bd, 0xe4a3f3e4, 0xd6193d63, 0x45241ebc,\n      0xb69758d7, 0x6f226586, 0x5e165f36, 0xbd3036a2, 0xef8b1076, 0xe9973a44, 0x6f1e8d4c, 0x63938221,\n      0x64e32c50, 0x2274c827, 0xb2a852d2, 0xade0cf8e, 0xdc754ec7, 0x4e0ee5b0, 0x3da5a33a, 0x3fbbe698,\n      0x4fdc13c9, 0xdafe50fc, 0x7eaee084, 0x2b4a4db6, 0x70ecc71f, 0x2d6466bf, 0x887238aa, 0xe352b233,\n      0xa51111df, 0x3ff037d7, 0x1e97bea0, 0x28d4a977, 0xa8f6d229, 0x2c7e028b, 0xc57a4ed8, 0xc1e3cebb,\n      0xe37b50e2, 0xc60bfd20, 0xb41c338f, 0x562630be, 0x733dae05, 0xec91d1c6, 0xefb8356d, 0xda119307,\n      0x089d15d3, 0xb162bc0c, 0xdb0f744e, 0xc4010858, 0xc609a665, 0xb843aa52, 0x6f404d13, 0xb50df2f2,\n      0x675f5afc, 0xba01e8ec, 0xaadf8be9, 0xe0f62804, 0x3e425609, 0x8528d4f9, 0x1447c4c2, 0x79fc1099,\n      0xef9ea8bc, 0x0b2fe3fc, 0xf751a4d0, 0xe344b5b5, 0xd5309cc8, 0x56d9941d, 0xdc49cafd, 0x5e853c0d,\n      0x506fac61, 0xf3544583, 0xfbee461b, 0x35f6d16c, 0x17609d3c, 0x47b1b4f8, 0x3cec48cb, 0x86a3dc26,\n      0x546198cf, 0xf92ec3eb, 0xb643204d, 0xc16022e3, 0x0fc65eb5, 0xccfdf0fe, 0xd373ff09, 0xaaecb85d,\n      0x09d646d2, 0xe493555b, 0x0e025f0f, 0x8c0f1589, 0xace87d02, 0x1b317914, 0x30572122, 0x89c2afb1,\n      0x739b8637, 0x515d809b, 0x7d8f0532, 0x278a4b0a, 0xe0cf650e, 0x7dc97523, 0x46412b87, 0x87a8ca0b,\n      0x6d38b509, 0x53f43053, 0xf518b807, 0x51fd2f74, 0x169a3162, 0xb41e3f3f, 0xfd1709ce, 0xd5b6842c,\n      0x08edc02e, 0x97ab605e, 0x376fd8b4, 0xd9f15175, 0x5b5c7895, 0xedfe87fa, 0x921fb79c, 0x1ef903b1,\n      0xb7d6306d, 0x9ad5651f, 0x1bfa727f, 0x0af45c03, 0x08fcbb0d, 0x75faa27a, 0x634667bb, 0x73a24f4b,\n      0x275ac6ab, 0x65b40924, 0x1823ed26, 0xf54b58ed, 0xe11783b7, 0x46e586ca, 0x68b2b761, 0x8eae9158,\n      0x98e4b860, 0x3cdfb24a, 0x233c5c46, 0xf3d88ed5, 0xce9a2f02, 0x1190e2ad, 0xa13dabc9, 0x324aaa43,\n      0x8432ffff, 0xe8f7e68f, 0x5735991e, 0x6c96148d, 0xd3b86a68, 0x443d6b96, 0x8c3cdcec, 0x3e2d193b,\n      0xd30fe0e8, 0x25644d66, 0xa5f431ba, 0xd6f4a5a4, 0xea31551f, 0x582090d7, 0x1a531766, 0xf059ae9d,\n\n      0xdb3ebcc4, 0x2bda4aaf, 0x20bac271, 0xb90d38df, 0x4b568da3, 0x4bcc6c15, 0xe80b5af3, 0x796ec8cf,\n      0xf155e70a, 0x9fd45cfb, 0xae4dd746, 0x453fc337, 0xf07f9efb, 0x62b57626, 0xdd5b92b8, 0x5688b82d,\n      0xbe6ff963, 0xd0c61163, 0x331ccd8a, 0x678c4445, 0x15dea0bb, 0x00d81b06, 0xfb08f804, 0xbca3d291,\n      0x4efb666a, 0x06b8f52d, 0xde7d0dd5, 0xcf2cb546, 0xeb721cc0, 0xd08cb6d3, 0x9de906fe, 0x1fef872a,\n      0x5a65715c, 0xcb5190f2, 0xef563029, 0xd8b66943, 0x61125692, 0x7db602b9, 0x0242a7d8, 0xfc3d05c1,\n      0x2b0b8d82, 0x1fe6c072, 0x54580d2a, 0xbffa360f, 0x1651ae85, 0xed9cfe06, 0x1ab2cfe1, 0x173cedfb,\n      0x507ae2b3, 0x5bd83711, 0xdf0269b4, 0x4b2c1cb5, 0xd8263e8e, 0x485c119b, 0x20aa9eed, 0xeed41013,\n      0x5d2e8181, 0xad33aff1, 0x86558428, 0x19b0c2d1, 0x56d19f32, 0x4b5074a7, 0xd9450d0d, 0xc2b75b04,\n      0x7303ebe4, 0xf635bf11, 0x208cbfee, 0x0fabca2f, 0xe5c30a06, 0x1b286f5a, 0x7a93211c, 0x7afdb3c3,\n      0x5e3f4d68, 0x2fb67e54, 0x8598008a, 0xbe1b93d3, 0x0f4ff9dd, 0x91579384, 0x053097b0, 0x3f459325,\n      0x75d649e1, 0xa0f4bd59, 0x80bf2d0f, 0x8bc32665, 0xf7ba8068, 0x6c8c0e11, 0xd2ebf7a5, 0x77a1f920,\n      0xa9550e7d, 0xf6671ce7, 0x012db171, 0x0a8b92af, 0x4f7551ab, 0xb0932b22, 0xf847f81e, 0x6113c942,\n      0x21a2961a, 0x247914b0, 0x2adb9fe0, 0x669264fa, 0xa134f6b2, 0x32d1e836, 0x1dfdb910, 0x21733f4e,\n      0x90bb64d8, 0xc0aaa01e, 0xc86d0355, 0x8741e77e, 0xf289393d, 0x105748d1, 0xc46c932e, 0x86a5f854,\n      0x7c8500b6, 0x93f37af0, 0x40d836fc, 0xf400590e, 0xbaf3a50e, 0xab2ce175, 0xbeb15ee5, 0x0f38b905,\n      0x49a088bc, 0x87279c86, 0xabdb5a50, 0x89f2feb5, 0x7947ba13, 0xdf7febd5, 0xce0bfde9, 0x9a813691,\n      0x37c636a8, 0x3acc1cb4, 0x23398068, 0x878f6c1f, 0x83326270, 0x8d83a4ec, 0x4e244c45, 0xb872dc11,\n      0x6b6c164c, 0x638766d7, 0x1d6f4194, 0x2091d85d, 0xe3024c88, 0x3f17a427, 0x4a01362b, 0xa835635a,\n      0x415347c1, 0x8ab934d7, 0xc1ea2c25, 0xcd5c9f2a, 0x5fe676f0, 0x4d6d433b, 0x67064cc3, 0x829392b8,\n      0xfe5028fc, 0xf828f95a, 0x62842ba3, 0xc8937a61, 0x9721369b, 0x50b4ee24, 0x26715742, 0xf1d63969,\n      0xd08d5060, 0xadc20379, 0xcc363a2e, 0xdec22480, 0x3617cce8, 0x212f6a17, 0x2a41052c, 0xdb26e527,\n      0x99798738, 0xc0812f39, 0xe7f4bdd2, 0x1c7c6c4e, 0x7b5021e1, 0xb4ca630d, 0x50493ff8, 0x9a6e1561,\n      0xd51539c8, 0x6692a2ea, 0x0c6c8ad8, 0xfbf8262c, 0x15a544ee, 0x7e9907fc, 0x1f69e99d, 0xdc89af7c,\n      0x4461d1d5, 0x7c8f2a65, 0xfe7eb38f, 0x5e1d2677, 0xaba4f1f2, 0x39240176, 0x7dc3701f, 0x315c2223,\n      0x20f8b1be, 0x589e1a02, 0xadfcdf3e, 0x530a6730, 0x5e5b1312, 0x29bfefe2, 0xc98d5f75, 0xf08fd234,\n      0xb032a4c7, 0x21d11bfa, 0x17fbb322, 0x518364ae, 0xfee830b6, 0x6768f078, 0xdc5fd237, 0x093d7780,\n      0x06a3bd70, 0x624d272d, 0x0888ad27, 0xe468defb, 0x536b554b, 0x0f42dba6, 0x6a82db06, 0xf936be6a,\n      0x49e0ba24, 0x989688e6, 0x8db88ed1, 0x007cb46f, 0x33322e88, 0x7755778e, 0x42591a84, 0xd25b0004,\n      0x41a82b9d, 0x54e17097, 0x3fdc168e, 0x42709cb2, 0xf1094441, 0x4c9405e5, 0x29c94482, 0x94268ccb,\n      0x94a73c65, 0x585d3ac3, 0x43b8ae00, 0x10ddbbf9, 0x0f00eff5, 0xd0d656ac, 0xac63368c, 0x9c9f7e8f,\n      0x07f892b5, 0xc481e22c, 0x6a2391d9, 0x2b4c127d, 0x5dcd9a72, 0x5f30d21f, 0xaaf0c397, 0xee7b6a83,\n      0x222a119c, 0xf3c42075, 0x533fb9ae, 0xaca74163, 0x0cba7998, 0x58e60778, 0x142e3a09, 0x8a685b95,\n    ]\n    seed = [\n      1936287828, 544434464, 1849583932,\n      792491119, 1948270185, 1914725736,\n      1952999273, 1954114848, 779384933,\n    ]\n\n    m = Random::ISAAC.new seed\n    numbers.each do |n|\n      m.next_u.should eq(n)\n    end\n  end\n\n  it \"can be initialized without explicit seed\" do\n    Random::ISAAC.new.should be_a Random::ISAAC\n  end\n\n  it \"different instances generate different numbers (#7976)\" do\n    isaacs = Array.new(1000) { Random::ISAAC.new }\n    values = isaacs.map(&.rand(10_000_000))\n    values.uniq.size.should be > 2\n  end\nend\n"
  },
  {
    "path": "spec/std/random/pcg32_spec.cr",
    "content": "require \"spec\"\nrequire \"random/pcg32\"\n\ndescribe \"Random::PCG32\" do\n  it \"generates random numbers as generated official implementation\" do\n    numbers = [\n      3152259133, 2489095755, 485973489, 739446704, 3084920751,\n      2161564962, 2655557215, 4238523805, 4127884210, 1729992006,\n      1964292282, 3213125726, 1096479421, 1281102065, 2428306580,\n      714078320, 2392099709, 1526439585, 402063061, 987620545,\n      3290157899, 3849428442, 3440034864, 979768042, 2581261330,\n      697552453, 2701760878, 1629908786, 2263909770, 2396572129,\n      2915312060, 1163641977, 3915471724, 2608873459, 926223418,\n      3195912237, 4227696621, 3135858064, 4281979580, 3933514590,\n      2043026875, 2382205407, 1676050888, 3865582612, 2156465812,\n      475741448, 1441007193, 1068412521, 3443013050, 2606006740,\n      3917347556, 3545411947, 1285806801, 3233203743, 83808518,\n      2316494619, 3094051679, 4232904961, 556837719, 2364763832,\n      2985639445, 2764876339, 3794480974, 3218650203, 394445985,\n      3653052187, 3871764828, 3652452072, 3211653627, 3072401364,\n      3312575786, 1434335361, 3589384991, 1880518631, 4154909753,\n      781856932, 2958234005, 2215042200, 3989578033, 2724202193,\n      3276287024, 3857960173, 3261972894, 1278127551, 2910186591,\n      1318442819, 1732089978, 1226233120, 1610882785, 2555776835,\n      2827209191, 2550397788, 879181699, 4111486503, 1424052202,\n      592749904, 181272803, 2052719404, 1464274999, 3191236557,\n      4035072851, 2447786701, 1508789148, 2949852022, 1596672675,\n      874908458, 1083828097, 4128737369, 2892752832, 4161975140,\n      4063766939, 116721858, 3549607777, 2875310346, 2521462935,\n      2611783422, 289182647, 3434103775, 3694998243, 1756960730,\n      424818697, 2125655315, 137915754, 308167565, 500350518,\n      4225198488, 3208597443, 4120593227, 886307125, 2810197833,\n      1046810634, 2248715633, 1732273418, 3297629623, 3909267631,\n      2814215577, 542536551, 3821842943, 2706123873, 4023610128,\n      3718248286, 2681796329, 552208177, 543429826, 1023723652,\n      926924173, 3304944984, 3498001447, 3910350161, 2173595067,\n      1572275538, 3746362970, 3921531055, 4275984385, 1107615065,\n      798885450, 593701507, 746831642, 2380742034, 2662553742,\n      784679570, 2898603226, 2654863246, 4188102213, 3404287149,\n      2457824074, 1611786778, 2774875692, 3214028852, 3474269753,\n      638991533, 2876947025, 224837420, 3427610759, 637796298,\n      872913304, 230900522, 2497845134, 1231922500, 3186038255,\n      2231895999, 4145622422, 4127438182, 123440798, 2445812013,\n      742589188, 1597895892, 136729449, 2068901076, 3483340503,\n      2142045847, 3370920004, 4110699246, 896685970, 2705758845,\n      2429759267, 2634038519, 3803282661, 102147536, 2662802352,\n      2928194559, 1789601722, 3273475405, 595247591, 1920554561,\n      1357591722, 1148716171, 2883327948, 3559357577, 2590089812,\n      3793391831, 4294783876, 2576298305, 1346833391, 616223245,\n      358458145, 2954013630, 2458708274, 3488150452, 2978472547,\n      3730992200, 1800033499, 1021364745, 3209833714, 532534773,\n      1759755310, 3848259511, 228695388, 1511557673, 1375696353,\n      4070021632, 853659457, 902039725, 3752410041, 663810756,\n      3640029827, 408533623, 979227213, 2241726534, 2011792749,\n      1554143644, 1777460466, 3941988208, 680305025, 2527117533,\n      2900559404, 149303676, 1469465181, 427361544, 3025171681,\n      1467079836, 4047295972, 1260499396, 923706251, 1622637410,\n      3088110035, 3598131931, 3212854712, 70471773, 2957552528,\n      3151895712, 1633306458, 2394973777, 3119598452, 3022402606,\n      255688883, 1168220288, 1460799618, 3267323738, 220323701,\n      1578402126, 4250490965, 3077459240, 1160973232, 351701933,\n      2784720542, 1394167841, 4051721212, 371952896, 1973783929,\n      3648107670, 1530583910, 1832329814, 4086713855, 3803165377,\n      2199487219, 198963646, 2272747406, 1279141576, 3437303601,\n      2092147571, 1420523853, 4008430746, 2521394822, 750977764,\n      4014788147, 885313409, 2183347471, 3910873069, 3206967476,\n      1343983830, 4090065941, 1080284715, 2544279422, 2536660066,\n      83397412, 376958746, 3342775199, 198656073, 3396903609,\n      3690557488, 935801910, 2874380889, 710255514, 1143584976,\n      1659398432, 1995953899, 3787661179, 279487606, 1884931545,\n      3722215215, 2804487811, 949797573, 270526480, 1999556782,\n      1705597493, 4194847978, 2994245091, 2200536670, 4043146221,\n      2300185633, 3315527543, 1806052579, 2147999232, 4283687644,\n      3966597571, 3030769992, 2829105814, 1990560906, 3809025954,\n      304899011, 1308237895, 918475845, 2863237781, 2950130665,\n      187348261, 44475868, 3752100000, 1438913640, 3698251096,\n      3058136087, 3870845878, 734965319, 3932015010, 2228969989,\n      1438075944, 4145352926, 1447116330, 987733624, 740850530,\n      545170763, 3490076022, 1824981371, 2897520939, 680528095,\n      1043360829, 2471400869, 2531242384, 1391866181, 3919001970,\n      2669153449, 2349636350, 1610505852, 2040603403, 1066161728,\n      1816628151, 891920597, 1618871135, 3722751414, 2572659388,\n      149615482, 422329046, 1346243966, 152859188, 3912095940,\n      474646586, 3355907871, 3507474089, 915715720, 89939149,\n      2088699533, 92635730, 3617261580, 671224575, 387783436,\n      1720494999, 865234509, 1391451431, 231801218, 3216862605,\n      3059903310, 191182363, 2064527674, 3976550135, 2775767657,\n      846710684, 3649614275, 459607853, 4042595156, 1746468842,\n      2169842137, 1133125375, 43074024, 557721941, 2327490727,\n      4291814753, 526549723, 3772505931, 2304757232, 3687648692,\n      3205823216, 4005885424, 1464992250, 677775161, 3713603155,\n      225238780, 3195539494, 2659534492, 4240749843, 4230190397,\n      2977774409, 1116631315, 2736451989, 2648644420, 4043663805,\n      3438094951, 2175224133, 1963295755, 1903595797, 1307902629,\n      1660488095, 3387264330, 373454731, 2028059229, 2054707911,\n      3718875197, 584167931, 1731683340, 3814377540, 4078714056,\n      2801037738, 623721081, 1009162266, 416339462, 1816872628,\n      4097234983, 3658943599, 3231023243, 2503322097, 779699657,\n      3466123881, 845081479, 3901529354, 1204428463, 4138880724,\n      4229825258, 421584796, 773918038, 1437363947, 2537882002,\n      3639042184, 1397752119, 1514231515, 3879786050, 468999209,\n      3501333333, 2147871873, 3691114017, 2106546687, 1098889611,\n      2605749152, 1535872144, 4051831975, 534053246, 2110496658,\n      2689253576, 21091593, 2537864165, 682029957, 3857012329,\n      3489705216, 3683450242, 2062023254, 526748247, 1651517044,\n      3483810301, 1731825610, 511563672, 683235416, 860886303,\n      1543813269, 3547121640, 19298476, 1162462965, 603414277,\n      1151994135, 767686929, 157925985, 1688172158, 244945727,\n      2140029812, 4239900639, 2476037922, 2487753835, 356695600,\n      1890908737, 2158904600, 582254963, 625209263, 2056668440,\n      2459210833, 3923461372, 1202261374, 1738126538, 658156162,\n      858757606, 2277093406, 3331417943, 2712495625, 308115472,\n      570054023, 3875689146, 616905744, 3023726697, 1221472646,\n      2014618057, 3501313970, 851294431, 552785953, 335861796,\n      2817015949, 872689902, 1290307640, 2875122071, 3685219344,\n      1621868258, 3956292840, 2687995934, 1453126073, 4264135243,\n      610747898, 2683682733, 3011149255, 903812326, 2893971745,\n      2125628997, 2928337946, 1366876677, 569570263, 3151177377,\n      4293600395, 3668009845, 1746584807, 1560785237, 3128259540,\n      2251458057, 980485191, 2801973792, 1838969331, 2378780894,\n      2946620064, 1387911045, 3020634335, 3570592525, 1395942741,\n      1049354413, 2264450723, 1224667241, 2172202524, 2112895235,\n      4165144955, 4287652636, 3450325967, 4195851782, 3548006139,\n      2711137513, 587010713, 215680752, 3843539991, 3224825991,\n      317923666, 3456980036, 3823856843, 2811490222, 3902228860,\n      1468943371, 2447539668, 390310094, 4118745819, 4283390143,\n      1491924070, 2524640914, 520462143, 340307971, 1085902276,\n      2183059148, 1441406677, 4156585458, 2883152945, 2219938415,\n      3949555055, 4066516196, 3870270320, 2162405907, 2122743940,\n      4181116623, 2788459158, 2282415566, 33547338, 1456478686,\n      2279378290, 1480109803, 2903111377, 3945426414, 2121062997,\n      113658340, 3952206955, 3771713683, 1980080742, 1320066490,\n      2189636694, 1540806808, 2626922735, 3101538891, 4204072960,\n      2394823297, 4260801048, 178857193, 233797511, 475398705,\n      2717487817, 2619203653, 3067296452, 975269041, 711727163,\n      1806019313, 972009279, 752642597, 2253913949, 378018363,\n      3692822806, 2291715909, 3797076961, 1720600028, 1090788534,\n      1902148720, 639105200, 451355803, 2514917140, 1623831419,\n      1960739521, 1603113582, 669561148, 495359823, 2575690747,\n      645045676, 3107783451, 2074524703, 5717861, 3494250223,\n      154312986, 1490018548, 3101998594, 923805398, 4084912428,\n      752872063, 4055978887, 1553913942, 3395247744, 271319475,\n      3230109488, 196118094, 3493398046, 3929019851, 1329081029,\n      1975590465, 4114658171, 3256365491, 4255284016, 2330054640,\n      1851548018, 2652996931, 3206244712, 2732300088, 3353208910,\n      2809477721, 1928547302, 830668350, 1824769219, 679926763,\n      3000806510, 2835860144, 773062291, 2592370757, 1624681417,\n      1621785075, 13878616, 3564256604, 2214646575, 1998415234,\n      1741843641, 106120338, 1149769343, 1985608948, 1758241877,\n      675727517, 1345269025, 2036015953, 4124573105, 50515534,\n      2824684386, 2257877229, 3401700697, 871828611, 1774662235,\n      459169519, 1950903537, 245645886, 2267227716, 3595508053,\n      2968185818, 974656384, 4120436926, 1255271843, 2111922304,\n      2590233652, 2271784423, 2611180179, 287613450, 984974680,\n      2034441466, 2950062628, 1315816115, 1455483087, 4102478979,\n      3536667676, 3938550701, 80175511, 3062472660, 4178983616,\n      2538339777, 2818121756, 1901212384, 1152628993, 2195262219,\n      3379127407, 3518457276, 3384024534, 3547607989, 1641066462,\n      2940569625, 352680641, 1816983234, 1278220099, 3565098247,\n      398748542, 1557771113, 1173437839, 679670453, 545521558,\n      3634129455, 2794896613, 2632809002, 4143379313, 3127719435,\n      2991220134, 1191706879, 922765839, 2935804138, 1459299172,\n      2913423027, 3519317577, 3051632296, 3515024263, 1205545960,\n      214870549, 714375770, 3731434477, 54164339, 1409785122,\n      2095886986, 2984072989, 2023326206, 182256043, 2369690875,\n      1417209127, 4287385144, 2928838638, 2290207244, 1758239167,\n      4265832369, 1039980687, 2055041523, 1686913680, 2572919193,\n      2898133024, 3315580132, 3631605988, 3335315781, 3651251993,\n      227705262, 2490767375, 4225816603, 2368328128, 1646548309,\n      3380964902, 1085548774, 3389065723, 4143107792, 2162197026,\n      428406666, 3970339861, 937788473, 25855215, 1768558175,\n      277451823, 3827742831, 1927491367, 2411140319, 3143966439,\n      3324023500, 472300900, 1766495680, 3732764466, 1142736941,\n      2264858589, 3566987973, 2584932467, 1718932167, 1324050561,\n      2149264288, 3352290801, 1681118604, 150507558, 884863601,\n      3603751545, 3176229599, 1721730780, 944156250, 3674847539,\n      2190475248, 1026568815, 2358223767, 1677593454, 3598600855,\n      2161822129, 1836298828, 2174358983, 2756163252, 11262953,\n      443286989, 195186846, 2259448910, 2925846765, 2317618658,\n      537955797, 1692642836, 1493395222, 1166712581, 2162822772,\n      2624157077, 261913075, 2517387571, 1959309016, 1919163400,\n      2838485297, 3063156449, 3806786018, 4156769566, 3949232856,\n      654739052, 3371283799, 2732421937, 669994169, 1791086420,\n      3338996392, 2288669419, 307009501, 1782206558, 3584439117,\n      1034505656, 4185306586, 2573525195, 1387536053, 2519189134,\n      1780764686, 827972414, 1357707228, 4125644428, 351534027,\n      1942211948, 1774218210, 1427464762, 558923619, 1691636337,\n      3969245170, 1120085618, 3619440303, 2298981826, 3298710585,\n      811275286, 1512365269, 570153323, 1259558917, 3970256463,\n      557482421, 1119928138, 3377181702, 2865140105, 1695073349,\n      1616464886, 3327154569, 1447770430, 3492782369, 105460008,\n      2218941048, 2932446555, 42703369, 2176925470, 3827613738,\n      3155341484, 3510028168, 2824636097, 889570783, 82606235,\n      1679203450, 1442980090, 1257995638, 3964360852, 306536899,\n      4265338980, 3087927660, 2400741951, 2530954085, 142645944,\n      2800445979, 1715385912, 2269077321, 674552406, 4170469914,\n      3626918117, 4069600062, 1412694875, 458758489, 4150188116,\n      1263319500, 1904437305, 3988056128, 3983207806, 3788270062,\n      3126263147, 752216999, 130388335, 1249773541, 2546985244,\n      3895008020, 3269892779, 3586920839, 1707477586, 1529026373,\n      907423954, 1466533845, 3979231884, 3830238796, 882835283,\n      305587680, 3809630881, 4272299545, 586326910, 2393718249,\n      1400136727, 4006138046, 2671692572, 306965616, 2706776810,\n      1931034729, 3437103677, 3885893079, 1451834709, 834357077,\n      1734264296, 1789084544, 2581055613, 2664601835, 423470342,\n      1806201888, 2950983606, 1725641904, 3802798623, 3660813410,\n    ]\n    seed = {123_u64, 456_u64}\n\n    m = Random::PCG32.new(*seed)\n    numbers.each do |n|\n      m.next_u.should eq(n)\n    end\n  end\n\n  it \"can jump ahead\" do\n    seed = {123_u64, 456_u64}\n\n    m1 = Random::PCG32.new(*seed)\n    m2 = Random::PCG32.new(*seed)\n    10.times { m1.next_u }\n    m2.jump(10)\n    m1.next_u.should eq m2.next_u\n  end\n  it \"can jump back\" do\n    seed = {123_u64, 456_u64}\n\n    m1 = Random::PCG32.new(*seed)\n    m2 = Random::PCG32.new(*seed)\n    10.times { m1.next_u }\n    m1.jump(-10)\n    m1.next_u.should eq m2.next_u\n  end\n\n  it \"#split\" do\n    rng0 = Random::PCG32.new\n    rng1 = rng0.split\n    rng2 = rng0.split\n    rng3 = rng1.split # split of split\n\n    seq0 = Array.new(5) { rng0.next_u }\n    seq1 = Array.new(5) { rng1.next_u }\n    seq2 = Array.new(5) { rng2.next_u }\n    seq3 = Array.new(5) { rng3.next_u }\n\n    seq1.should_not eq(seq0)\n    seq1.should_not eq(seq2)\n    seq1.should_not eq(seq3)\n    seq2.should_not eq(seq0)\n    seq2.should_not eq(seq3)\n    seq3.should_not eq(seq0)\n  end\n\n  it \"#split_internal\" do\n    rng0 = Random::PCG32.new(123_u64, 456_u64)\n    rng1 =\n      {% if compare_versions(Crystal::VERSION, \"1.12.0\") >= 0 %}\n        buf = uninitialized ReferenceStorage(Random::PCG32)\n        Random::PCG32.unsafe_construct(pointerof(buf), rng0)\n        buf.to_reference\n      {% else %}\n        rng0.dup\n      {% end %}\n    rng0.split_internal(rng1)\n    rng0.next_u.should eq(3152259133_u64)\n    rng1.next_u.should eq(2489095755_u64)\n  end\n\n  it \"can be initialized without explicit seed\" do\n    Random::PCG32.new.should be_a Random::PCG32\n  end\nend\n"
  },
  {
    "path": "spec/std/random/secure_spec.cr",
    "content": "require \"spec\"\nrequire \"random/secure\"\n\ndescribe \"Random::Secure\" do\n  it \"returns random number from a secure system source\" do\n    Random::Secure.next_u.should be_a(Int::Unsigned)\n\n    x = Random::Secure.rand(123456...654321)\n    x.should be >= 123456\n    x.should be < 654321\n\n    Random::Secure.rand(Int64::MAX // 2).should be <= (Int64::MAX // 2)\n  end\n\n  it \"fully fills a large buffer\" do\n    # mostly testing the linux getrandom calls\n    bytes = Random::Secure.random_bytes(10000)\n    bytes[9990, 10].should_not eq(Bytes.new(10))\n  end\n\n  it \"returns a random integer in range (#8219)\" do\n    {% for type in %w(Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64 Int128 UInt128).map(&.id) %}\n      value = Random::Secure.rand({{type}}::MIN..{{type}}::MAX)\n      typeof(value).should eq({{type}})\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/std/random_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"big\"\n\n# Copied here from compiler-rt's spec-helper\ndef make_tu(a : UInt128, b : UInt128)\n  (a.to_u128! << 64) + b\nend\n\nprivate class TestRNG(T)\n  include Random\n\n  def initialize(@data : Array(T))\n    @i = 0\n  end\n\n  def next_u : T\n    i = @i\n    @i = (i + 1) % @data.size\n    @data[i]\n  end\n\n  def reset\n    @i = 0\n  end\nend\n\nprivate RNG_DATA_8  = [234u8, 153u8, 0u8, 0u8, 127u8, 128u8, 255u8, 255u8]\nprivate RNG_DATA_32 = [31541451u32, 0u32, 1u32, 234u32, 342475672u32, 863u32, 0xffffffffu32, 50967465u32]\nprivate RNG_DATA_64 = [148763248732657823u64, 18446744073709551615u64, 0u64,\n                       32456325635673576u64, 2456245614625u64, 32452456246u64, 3956529762u64,\n                       9823674982364u64, 234253464546456u64, 14345435645646u64]\nprivate RNG_DATA_128 = [make_tu(1101449320907785530u128, 6979810955129606772u128), make_tu(12369252765162302003u128, 15575142252968787416u128),\n                        make_tu(13441498024203764035u128, 7030046732498433846u128), make_tu(6222142093523402848u128, 17154318143782852173u128),\n                        make_tu(9998452933988559113u128, 7429084970167000187u128), make_tu(14848715232994466871u128, 16710636589464800780u128),\n                        make_tu(16766909356445913125u128, 789971150692021876u128), make_tu(13481304315125825423u128, 16449966857661152872u128)]\n\ndescribe \"Random\" do\n  it \"limited number\" do\n    rand(1).should eq(0)\n\n    x = rand(2)\n    x.should be >= 0\n    x.should be < 2\n\n    # issue 3350\n    5.times do\n      rand(Int64::MAX).should be >= 0\n    end\n  end\n\n  it \"limited BigInt\" do\n    rand(1.to_big_i).should eq(0.to_big_i)\n\n    x = rand(2.to_big_i)\n    x.should be >= 0\n    x.should be < 2\n  end\n\n  it \"limited large BigInt\" do\n    max = \"1234567890123456789012345\".to_big_i\n    x = rand(max)\n    x.should be >= 0\n    x.should be < max\n  end\n\n  it \"float number\" do\n    x = rand\n    x.should be >= 0\n    x.should be <= 1\n  end\n\n  it \"limited Float32 number\" do\n    x = rand(3.5_f32)\n    x.should be_a Float32\n    x.should be >= 0\n    x.should be < 3.5\n  end\n\n  it \"limited Float64 number\" do\n    x = rand(3.5_f64)\n    x.should be_a Float64\n    x.should be >= 0\n    x.should be < 3.5\n  end\n\n  it \"raises on invalid number\" do\n    expect_raises ArgumentError, \"Invalid bound for rand: 0\" do\n      rand(0)\n    end\n    expect_raises ArgumentError, \"Invalid bound for rand: -1\" do\n      rand(-1)\n    end\n  end\n\n  it \"raises on invalid float number\" do\n    expect_raises ArgumentError, \"Invalid bound for rand: 0.0\" do\n      rand(0.0)\n    end\n    expect_raises ArgumentError, \"Invalid bound for rand: -1.0\" do\n      rand(-1.0)\n    end\n  end\n\n  it \"does with inclusive range\" do\n    [1..1, 1..3, 0u8..255u8, -1..1, Int64::MIN..7i64,\n     -7i64..Int64::MAX, 0u64..0u64].each do |range|\n      x = rand(range)\n      x.should be >= range.begin\n      x.should be <= range.end\n    end\n  end\n\n  it \"does with exclusive range\" do\n    [1...2, 1...4, 0u8...255u8, -1...1, Int64::MIN...7i64,\n     -7i64...Int64::MAX, -1i8...0i8].each do |range|\n      x = rand(range)\n      x.should be >= range.begin\n      x.should be < range.end\n    end\n  end\n\n  it \"does with BigInt range\" do\n    [1.to_big_i...2.to_big_i,\n     -\"1234567890123456789012345\".to_big_i...7.to_big_i,\n     -7.to_big_i...\"1234567890123456789012345\".to_big_i].each do |range|\n      x = rand(range)\n      x.should be >= range.begin\n      x.should be < range.end\n    end\n  end\n\n  it \"does with inclusive range of floats\" do\n    rand(1.0..1.0).should eq(1.0)\n    x = rand(1.8..3.2)\n    x.should be >= 1.8\n    x.should be <= 3.2\n\n    rand(1.0_f32..1.0_f32).should eq(1.0_f32)\n    x = rand(1.8_f32..3.2_f32)\n    x.should be >= 1.8_f32\n    x.should be <= 3.2_f32\n  end\n\n  it \"does with exclusive range of floats\" do\n    x = rand(1.8...3.3)\n    x.should be >= 1.8\n    x.should be < 3.3\n\n    x = rand(1.8_f32...3.3_f32)\n    x.should be >= 1.8_f32\n    x.should be < 3.3_f32\n  end\n\n  describe \"raises on invalid range\" do\n    it \"Int32 range\" do\n      expect_raises ArgumentError, \"Invalid range for rand: 1...1\" do\n        rand(1...1)\n      end\n      expect_raises ArgumentError, \"Invalid range for rand: 1..0\" do\n        rand(1..0)\n      end\n    end\n\n    it \"BigInt range\" do\n      expect_raises ArgumentError, \"Invalid range for rand: #{1.to_big_i...1.to_big_i}\" do\n        rand(1.to_big_i...1.to_big_i)\n      end\n      expect_raises ArgumentError, \"Invalid range for rand: #{1.to_big_i..0.to_big_i}\" do\n        rand(1.to_big_i..0.to_big_i)\n      end\n    end\n\n    it \"Float64 range\" do\n      expect_raises ArgumentError, \"Invalid range for rand: 1.0...1.0\" do\n        rand(1.0...1.0)\n      end\n      expect_raises ArgumentError, \"Invalid range for rand: 1.0..0.0\" do\n        rand(1.0..0.0)\n      end\n    end\n  end\n\n  it \"allows creating a new default random\" do\n    rand = Random.new\n    value = rand.rand\n    (0 <= value < 1).should be_true\n  end\n\n  it \"allows creating a new default random with a seed\" do\n    values = Array.new(2) do\n      rand = Random.new(1234)\n      {rand.rand, rand.rand(0xffffffffffffffffu64), rand.rand(2), rand.rand(-5i8..5i8)}\n    end\n\n    values[0].should eq values[1]\n  end\n\n  it \"splits\" do\n    Random.split.should be_a(Random)\n  end\n\n  it \"gets a random bool\" do\n    Random.next_bool.should be_a(Bool)\n  end\n\n  it \"gets a random int\" do\n    Random.next_int.should be_a(Int32)\n  end\n\n  it \"generates by accumulation\" do\n    rng = TestRNG.new([234u8, 153u8, 0u8, 0u8, 127u8, 128u8, 255u8, 255u8])\n    rng.rand(65536).should eq 60057    # 234*0x100 + 153\n    rng.rand(60000).should eq 0        # 0*0x100 + 0\n    rng.rand(30000).should eq 2640     # (127*0x100 + 128) % 30000\n    rng.rand(65535u16).should eq 60057 # 255*0x100 + 255 [skip]-> 234*0x100 + 153\n    rng.reset\n    rng.rand(65537).should eq 38934 # (234*0x10000 + 153*0x100 + 0) % 65537\n    rng.reset\n    rng.rand(32768u16).should eq 27289 # (234*0x100 + 153) % 32768\n  end\n\n  it \"generates by truncation\" do\n    rng = TestRNG.new([31541451u32, 0u32, 1u32, 234u32, 342475672u32])\n    rng.rand(1).should eq 0\n    rng.rand(10).should eq 0\n    rng.rand(2).should eq 1\n    rng.rand(256u64).should eq 234\n    rng.rand(255u8).should eq 217   # 342475672 % 255\n    rng.rand(65536).should eq 18635 # 31541451 % 65536\n    rng = TestRNG.new([0xffffffffu32, 0u32])\n    rng.rand(0x7fffffff).should eq 0\n  end\n\n  it \"generates full-range\" do\n    rng = TestRNG.new(RNG_DATA_64)\n    RNG_DATA_64.each do |a|\n      rng.rand(UInt64::MIN..UInt64::MAX).should eq a\n    end\n  end\n\n  it \"generates full-range by accumulation\" do\n    rng = TestRNG.new(RNG_DATA_8)\n    RNG_DATA_8.each_slice(2) do |(a, b)|\n      expected = a.to_u16 * 0x100u16 + b.to_u16\n      rng.rand(UInt16::MIN..UInt16::MAX).should eq expected\n    end\n  end\n\n  it \"generates full-range by truncation\" do\n    rng = TestRNG.new(RNG_DATA_32)\n    RNG_DATA_32.each do |a|\n      expected = a % 0x10000\n      rng.rand(UInt16::MIN..UInt16::MAX).should eq expected\n    end\n  end\n\n  it \"generates full-range by negation\" do\n    rng = TestRNG.new(RNG_DATA_8)\n    RNG_DATA_8.each do |a|\n      expected = a.to_i\n      expected -= 0x100 if a >= 0x80\n      rng.rand(Int8::MIN..Int8::MAX).should eq expected\n    end\n  end\n\n  it \"works with span exceeding int type's range\" do\n    rng = TestRNG.new(RNG_DATA_8)\n    rng.rand(-100_i8..100_i8).should eq(53_i8)\n  end\n\n  it \"works using U/Int128\" do\n    rng = TestRNG.new(RNG_DATA_128)\n    RNG_DATA_128.each do |a|\n      rng.rand(UInt128::MIN..UInt128::MAX).should eq a\n    end\n\n    # (234_u128 << 96) + (1_u128 << 64) + 31541451_u128\n    TestRNG.new(RNG_DATA_32).rand(UInt128).should eq make_tu(1005022347265u128, 31541451u128)\n\n    rand_in_range = TestRNG.new(RNG_DATA_32).rand(600_u128..700_u128)\n    (600..700).should contain(rand_in_range)\n  end\n\n  describe \"random_bytes\" do\n    it \"generates random bytes\" do\n      rng = TestRNG.new([0xfa19443eu32, 1u32, 0x12345678u32])\n      rng.random_bytes(9).should eq Bytes[0x3e, 0x44, 0x19, 0xfa, 1, 0, 0, 0, 0x78]\n      rng.random_bytes(1).should eq Bytes[0x3e]\n      rng.random_bytes(4).should eq Bytes[1, 0, 0, 0]\n      rng.random_bytes(3).should eq Bytes[0x78, 0x56, 0x34]\n      rng.random_bytes(0).should eq Bytes[]\n\n      rng = TestRNG.new([12u8, 255u8, 11u8, 5u8, 122u8, 200u8, 192u8])\n      rng.random_bytes(7).should eq Bytes[12, 255, 11, 5, 122, 200, 192]\n    end\n\n    it \"gets random bytes with default number of digits\" do\n      bytes = TestRNG.new(RNG_DATA_32).random_bytes\n      bytes.size.should eq(16)\n    end\n\n    it \"gets random bytes with requested number of digits\" do\n      bytes = TestRNG.new(RNG_DATA_32).random_bytes(50)\n      bytes.size.should eq(50)\n    end\n\n    it \"fills given buffer with random bytes\" do\n      bytes = Bytes.new(2000)\n      TestRNG.new(RNG_DATA_32).random_bytes(bytes)\n      bytes.size.should eq 2000\n      bytes[1990, 10].should eq(UInt8.slice(0, 0, 1, 0, 0, 0, 234, 0, 0, 0))\n    end\n  end\n\n  describe \"base64\" do\n    it \"gets base64 with default number of digits\" do\n      base64 = TestRNG.new(RNG_DATA_32).base64\n      base64.should eq(\"y0jhAQAAAAABAAAA6gAAAA==\")\n    end\n\n    it \"gets base64 with requested number of digits\" do\n      base64 = TestRNG.new(RNG_DATA_64).base64(50)\n      base64.should eq(\"n9hX9GKDEAL//////////wAAAAAAAAAA6I06MNtOcwAhuKXjOwIAADYvUY4HAAAAYto=\")\n    end\n  end\n\n  describe \"urlsafe_base64\" do\n    it \"gets urlsafe base64 with default number of digits\" do\n      base64 = TestRNG.new(RNG_DATA_32).urlsafe_base64\n      base64.should eq(\"y0jhAQAAAAABAAAA6gAAAA\")\n    end\n\n    it \"gets urlsafe base64 with requested number of digits\" do\n      base64 = TestRNG.new(RNG_DATA_64).urlsafe_base64(50)\n      base64.should eq(\"n9hX9GKDEAL__________wAAAAAAAAAA6I06MNtOcwAhuKXjOwIAADYvUY4HAAAAYto\")\n    end\n\n    it \"keeps padding\" do\n      base64 = TestRNG.new(RNG_DATA_32).urlsafe_base64(padding: true)\n      base64.should eq(\"y0jhAQAAAAABAAAA6gAAAA==\")\n    end\n  end\n\n  describe \"hex\" do\n    it \"gets hex with default number of digits\" do\n      hex = TestRNG.new(RNG_DATA_32).hex\n      hex.should eq(\"cb48e1010000000001000000ea000000\")\n    end\n\n    it \"gets hex with requested number of digits\" do\n      hex = TestRNG.new(RNG_DATA_64).hex(50)\n      hex.should eq(\"9fd857f462831002ffffffffffffffff0000000000000000e88d3a30db4e730021b8a5e33b020000362f518e0700000062da\")\n    end\n  end\n\n  it \"returns a random integer\" do\n    {% for type in %w(Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64).map(&.id) %}\n      value = TestRNG.new(RNG_DATA_32).rand({{type}})\n      typeof(value).should eq({{type}})\n    {% end %}\n  end\n\n  it \"returns a random static array\" do\n    {% for type in %w(Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64).map(&.id) %}\n      array = TestRNG.new(RNG_DATA_32).rand(StaticArray({{type}}, 4))\n      typeof(array).should eq(StaticArray({{type}}, 4))\n    {% end %}\n  end\n\n  it \"fails to split\" do\n    expect_raises(NotImplementedError, \"TestRNG(Int32)#split\") do\n      TestRNG(Int32).new([0]).split\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/range_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"spec/helpers/iterate\"\nrequire \"big\"\n\nstruct RangeSpecIntWrapper\n  include Comparable(self)\n\n  getter value : Int32\n\n  def initialize(@value)\n  end\n\n  def succ\n    RangeSpecIntWrapper.new(@value + 1)\n  end\n\n  def <=>(other)\n    value <=> other.value\n  end\n\n  def self.additive_identity\n    RangeSpecIntWrapper.new(0)\n  end\n\n  def +(other : RangeSpecIntWrapper)\n    RangeSpecIntWrapper.new(value + other.value)\n  end\nend\n\nprivate def range_endless_each\n  (2..).each do |x|\n    return x\n  end\nend\n\nprivate def range_beginless_reverse_each\n  (..2).reverse_each do |x|\n    return x\n  end\nend\n\ndescribe \"Range\" do\n  it \"initialized with new method\" do\n    Range.new(1, 10).should eq(1..10)\n    Range.new(1, 10, false).should eq(1..10)\n    Range.new(1, 10, true).should eq(1...10)\n  end\n\n  it \"gets basic properties\" do\n    r = 1..5\n    r.begin.should eq(1)\n    r.end.should eq(5)\n    r.excludes_end?.should be_false\n\n    r = 1...5\n    r.begin.should eq(1)\n    r.end.should eq(5)\n    r.excludes_end?.should be_true\n  end\n\n  it \"#==\" do\n    ((1..1) == (1..1)).should be_true\n    ((1...1) == (1..1)).should be_false\n    ((1...1) == (1...1)).should be_true\n    ((1..1) == (1...1)).should be_false\n\n    ((1..nil) == (1..nil)).should be_true\n\n    (1..1).should eq Range(Int32?, Int32?).new(1, 1)\n    ((1..1) == Range(Int32?, Int32?).new(1, 1)).should be_true\n    ((1.0..1.0) == (1..1)).should be_true\n  end\n\n  it \"includes?\" do\n    (1..5).includes?(1).should be_true\n    (1..5).includes?(5).should be_true\n\n    (1...5).includes?(1).should be_true\n    (1...5).includes?(5).should be_false\n  end\n\n  it \"does to_s\" do\n    (1...5).to_s.should eq(\"1...5\")\n    (1..5).to_s.should eq(\"1..5\")\n    (1..nil).to_s.should eq(\"1..\")\n    (nil..3).to_s.should eq(\"..3\")\n    (nil..nil).to_s.should eq(\"..\")\n  end\n\n  it \"does inspect\" do\n    (1...5).inspect.should eq(\"1...5\")\n  end\n\n  it \"is empty with .. and begin > end\" do\n    (1..0).to_a.should be_empty\n  end\n\n  it \"is empty with ... and begin > end\" do\n    (1...0).to_a.should be_empty\n  end\n\n  it \"is not empty with .. and begin == end\" do\n    (1..1).to_a.should eq([1])\n  end\n\n  it \"is not empty with ... and begin.succ == end\" do\n    (1...2).to_a.should eq([1])\n  end\n\n  describe \"sum\" do\n    it \"called with no block is specialized for performance\" do\n      (1..3).sum.should eq 6\n      (1...3).sum.should eq 3\n      (1..3).sum(4).should eq 10\n      (3..1).sum(4).should eq 4\n      (1..11).step(2).sum.should eq 36\n      (1...11).step(2).sum.should eq 25\n    end\n\n    it \"called with no block is specialized for performance (BigInt)\" do\n      (BigInt.new(\"1\")..BigInt.new(\"1 000 000 000\")).sum.should eq BigInt.new(\"500 000 000 500 000 000\")\n      (BigInt.new(\"1\")..BigInt.new(\"1 000 000 000\")).step(2).sum.should eq BigInt.new(\"250 000 000 000 000 000\")\n    end\n\n    it \"is equivalent to Enumerable#sum\" do\n      (1..3).sum { |x| x * 2 }.should eq 12\n      (1..3).step(2).sum { |x| x * 2 }.should eq 8\n      (RangeSpecIntWrapper.new(1)..RangeSpecIntWrapper.new(3)).sum.should eq RangeSpecIntWrapper.new(6)\n      (RangeSpecIntWrapper.new(1)..RangeSpecIntWrapper.new(3)).step(2).sum.should eq RangeSpecIntWrapper.new(4)\n    end\n  end\n\n  describe \"bsearch\" do\n    it \"Int\" do\n      ary = [3, 4, 7, 9, 12]\n      (0...ary.size).bsearch { |i| ary[i] >= 2 }.should eq 0\n      (0...ary.size).bsearch { |i| ary[i] >= 4 }.should eq 1\n      (0...ary.size).bsearch { |i| ary[i] >= 6 }.should eq 2\n      (0...ary.size).bsearch { |i| ary[i] >= 8 }.should eq 3\n      (0...ary.size).bsearch { |i| ary[i] >= 10 }.should eq 4\n      (0...ary.size).bsearch { |i| ary[i] >= 100 }.should be_nil\n      (0...ary.size).bsearch { |i| true }.should eq 0\n      (0...ary.size).bsearch { |i| false }.should be_nil\n\n      (0...ary.size).bsearch { |i| ary[i] >= 10 ? 1 : nil }.should eq 4\n\n      ary = [0, 100, 100, 100, 200]\n      (0...ary.size).bsearch { |i| ary[i] >= 100 }.should eq 1\n\n      (0_i8..10_i8).bsearch { |x| x >= 10 }.should eq 10_i8\n      (0_i8...10_i8).bsearch { |x| x >= 10 }.should be_nil\n      (-10_i8...10_i8).bsearch { |x| x >= -5 }.should eq -5_i8\n\n      (0_u8..10_u8).bsearch { |x| x >= 10 }.should eq 10_u8\n      (0_u8...10_u8).bsearch { |x| x >= 10 }.should be_nil\n      (0_u32..10_u32).bsearch { |x| x >= 10 }.should eq 10_u32\n      (0_u32...10_u32).bsearch { |x| x >= 10 }.should be_nil\n    end\n\n    it \"BigInt\" do\n      (BigInt.new(\"-10\")...BigInt.new(\"10\")).bsearch { |x| x >= -5 }.should eq BigInt.new(\"-5\")\n    end\n\n    it \"Float\" do\n      inf = Float64::INFINITY\n      (0.0...100.0).bsearch { |x| x > 0 && Math.log(x / 10) >= 0 }.not_nil!.should be_close(10.0, 0.0001)\n      (0.0...inf).bsearch { |x| x > 0 && Math.log(x / 10) >= 0 }.not_nil!.should be_close(10.0, 0.0001)\n      (-inf..100.0).bsearch { |x| x >= 0 || Math.log(-x / 10) < 0 }.not_nil!.should be_close(-10.0, 0.0001)\n      (-inf..inf).bsearch { |x| x > 0 && Math.log(x / 10) >= 0 }.not_nil!.should be_close(10.0, 0.0001)\n      (-inf..5).bsearch { |x| x > 0 && Math.log(x / 10) >= 0 }.should be_nil\n\n      (-inf..10).bsearch { |x| x > 0 && Math.log(x / 10) >= 0 }.not_nil!.should be_close(10.0, 0.0001)\n      (inf...10).bsearch { |x| x > 0 && Math.log(x / 10) >= 0 }.should be_nil\n\n      (-inf..inf).bsearch { false }.should be_nil\n      (-inf..inf).bsearch { true }.should eq -inf\n\n      (0..inf).bsearch { |x| x == inf }.should eq inf\n      (0...inf).bsearch { |x| x == inf }.should be_nil\n\n      v = (0.0..1.0).bsearch { |x| x > 0 }.not_nil!\n      v.should be_close(0, 0.0001)\n      v.should be > 0\n\n      (-1.0..0.0).bsearch { |x| x >= 0 }.should eq 0.0\n      (-1.0...0.0).bsearch { |x| x >= 0 }.should be_nil\n\n      (0.0..inf).bsearch { |x| Math.log(x) >= 0 }.not_nil!.should be_close(1.0, 0.0001)\n\n      (0.0..10).bsearch { |x| x >= 3.5 }.not_nil!.should be_close(3.5, 0.0001)\n      (0..10.0).bsearch { |x| x >= 3.5 }.not_nil!.should be_close(3.5, 0.0001)\n\n      (0_f32..5_f32).bsearch { |x| x >= 5_f32 }.not_nil!.should be_close(5_f32, 0.0001_f32)\n      (0_f32...5_f32).bsearch { |x| x >= 5_f32 }.should be_nil\n      (0_f32..5.0).bsearch { |x| x >= 5.0 }.not_nil!.should be_close(5.0, 0.0001)\n      (0..5.0_f32).bsearch { |x| x >= 5.0 }.not_nil!.should be_close(5.0, 0.0001)\n\n      inf32 = Float32::INFINITY\n      (0..inf32).bsearch { |x| x == inf32 }.should eq inf32\n      (0_f32..inf).bsearch { |x| x == inf }.should eq inf\n      (0.0..inf32).bsearch { |x| x == inf32 }.should eq inf32\n      (0_f32...5_f32).bsearch { |x| x >= 5_f32 }.should be_nil\n    end\n  end\n\n  describe \"#each\" do\n    it \"gives correct values with inclusive range\" do\n      range = -1..3\n      arr = [] of Int32\n      range.each { |x| arr << x }\n      arr.should eq([-1, 0, 1, 2, 3])\n    end\n\n    it \"gives correct values with exclusive range\" do\n      range = 'a'...'c'\n      arr = [] of Char\n      range.each { |x| arr << x }\n      arr.should eq(['a', 'b'])\n    end\n\n    it \"is empty with empty inclusive range\" do\n      range = 0..-1\n      any = false\n      range.each { any = true }\n      any.should be_false\n    end\n\n    it \"endless\" do\n      range = (3..nil)\n      ary = [] of Int32\n      range.each do |x|\n        ary << x\n        break if ary.size == 5\n      end\n      ary.should eq([3, 4, 5, 6, 7])\n    end\n\n    it \"raises on beginless\" do\n      expect_raises(ArgumentError, \"Can't each beginless range\") do\n        (..4).each { }\n      end\n      typeof((..4).each { |x| break x }).should eq Nil\n      expect_raises(ArgumentError, \"Can't each beginless range\") do\n        (nil.as(Int32?)..4).each { }\n      end\n      typeof((nil.as(Int32?)..4).each { |x| break x }).should eq Int32?\n    end\n\n    it \"doesn't have Nil as a type for endless each\" do\n      typeof(range_endless_each).should eq(Int32)\n    end\n\n    it \"doesn't have Nil as a type for beginless each\" do\n      typeof(range_beginless_reverse_each).should eq(Int32)\n    end\n  end\n\n  describe \"#reverse_each\" do\n    it \"gives correct values with inclusive range\" do\n      range = 'a'..'c'\n      arr = [] of Char\n      range.reverse_each { |x| arr << x }\n      arr.should eq(['c', 'b', 'a'])\n    end\n\n    it \"gives correct values with exclusive range\" do\n      range = -1...3\n      arr = [] of Int32\n      range.reverse_each { |x| arr << x }\n      arr.should eq([2, 1, 0, -1])\n    end\n\n    it \"is empty with empty inclusive range\" do\n      range = 0..-1\n      any = false\n      range.reverse_each { any = true }\n      any.should be_false\n    end\n\n    it \"raises on endless range\" do\n      expect_raises(ArgumentError, \"Can't reverse_each endless range\") do\n        (3..).reverse_each { }\n      end\n      expect_raises(ArgumentError, \"Can't reverse_each endless range\") do\n        (3..nil.as(Int32?)).reverse_each { }\n      end\n    end\n\n    it \"iterators on beginless range\" do\n      range = nil..2\n      arr = [] of Int32\n      range.reverse_each do |x|\n        arr << x\n        break if arr.size == 5\n      end\n      arr.should eq([2, 1, 0, -1, -2])\n    end\n  end\n\n  describe \"#each iterator\" do\n    it \"does next with inclusive range\" do\n      a = 1..3\n      iter = a.each\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should eq(3)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does next with exclusive range\" do\n      r = 1...3\n      iter = r.each\n      iter.next.should eq(1)\n      iter.next.should eq(2)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does with endless range\" do\n      r = (3..nil)\n      iter = r.each\n      iter.next.should eq(3)\n      iter.next.should eq(4)\n    end\n\n    it \"raises on beginless range\" do\n      expect_raises(ArgumentError, \"Can't each beginless range\") do\n        (..3).each\n      end\n      expect_raises(ArgumentError, \"Can't each beginless range\") do\n        (nil.as(Int32?)..3).each\n      end\n    end\n\n    it \"cycles\" do\n      (1..3).cycle.first(8).join.should eq(\"12312312\")\n    end\n\n    it \"is empty with .. and begin > end\" do\n      (1..0).each.to_a.should be_empty\n    end\n\n    it \"is empty with ... and begin > end\" do\n      (1...0).each.to_a.should be_empty\n    end\n\n    it \"is not empty with .. and begin == end\" do\n      (1..1).each.to_a.should eq([1])\n    end\n\n    it \"is not empty with ... and begin.succ == end\" do\n      (1...2).each.to_a.should eq([1])\n    end\n  end\n\n  describe \"#reverse_each iterator\" do\n    it \"does next with inclusive range\" do\n      a = 1..3\n      iter = a.reverse_each\n      iter.next.should eq(3)\n      iter.next.should eq(2)\n      iter.next.should eq(1)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does next with exclusive range\" do\n      r = 1...3\n      iter = r.reverse_each\n      iter.next.should eq(2)\n      iter.next.should eq(1)\n      iter.next.should be_a(Iterator::Stop)\n    end\n\n    it \"does next with beginless range\" do\n      r = nil...3\n      iter = r.reverse_each\n      iter.next.should eq(2)\n      iter.next.should eq(1)\n      iter.next.should eq(0)\n      iter.next.should eq(-1)\n    end\n\n    it \"reverse cycles\" do\n      (1..3).reverse_each.cycle.first(8).join.should eq(\"32132132\")\n    end\n\n    it \"is empty with .. and begin > end\" do\n      (1..0).reverse_each.to_a.should be_empty\n    end\n\n    it \"is empty with ... and begin > end\" do\n      (1...0).reverse_each.to_a.should be_empty\n    end\n\n    it \"is not empty with .. and begin == end\" do\n      (1..1).reverse_each.to_a.should eq([1])\n    end\n\n    it \"is not empty with ... and begin.succ == end\" do\n      (1...2).reverse_each.to_a.should eq([1])\n    end\n\n    it \"raises on endless range\" do\n      expect_raises(ArgumentError, \"Can't reverse_each endless range\") do\n        (1..).reverse_each\n      end\n      expect_raises(ArgumentError, \"Can't reverse_each endless range\") do\n        (1..nil.as(Int32?)).reverse_each\n      end\n    end\n  end\n\n  describe \"#sample\" do\n    it \"raises on open range\" do\n      expect_raises(ArgumentError, \"Can't sample an open range\") do\n        (1..).sample\n      end\n      expect_raises(ArgumentError, \"Can't sample an open range\") do\n        (1..nil.as(Int32?)).sample\n      end\n      expect_raises(ArgumentError, \"Can't sample an open range\") do\n        (..1).sample\n      end\n      expect_raises(ArgumentError, \"Can't sample an open range\") do\n        (nil.as(Int32?)..1).sample\n      end\n      expect_raises(ArgumentError, \"Can't sample an open range\") do\n        (..).sample\n      end\n      expect_raises(ArgumentError, \"Can't sample an open range\") do\n        (nil.as(Int32?)..nil.as(Int32?)).sample\n      end\n    end\n\n    it \"samples a float range as a distribution\" do\n      r = (1.2..3.4)\n      x = r.sample\n      r.should contain(x)\n\n      r.sample(Random.new(1)).should be_close(2.9317256017544837, 1e-12)\n    end\n\n    it \"samples a range with nilable types\" do\n      r = (1.as(Int32?)..4.as(Int32?))\n      x = r.sample\n      r.should contain(x)\n\n      (1.as(Int32?)...2.as(Int32?)).sample.should eq(1)\n\n      r = (1.2.as(Float64?)..3.4.as(Float64?))\n      x = r.sample\n      r.should contain(x)\n    end\n\n    it \"samples with n = 0\" do\n      (1..3).sample(0).empty?.should be_true\n    end\n\n    context \"for an integer range\" do\n      it \"samples an inclusive range without n\" do\n        value = (1..3).sample\n        (1 <= value <= 3).should be_true\n      end\n\n      it \"samples an exclusive range without n\" do\n        value = (1...3).sample\n        (1 <= value <= 2).should be_true\n      end\n\n      it \"samples an inclusive range with n = 1\" do\n        values = (1..3).sample(1)\n        values.size.should eq(1)\n        (1 <= values.first <= 3).should be_true\n      end\n\n      it \"samples an exclusive range with n = 1\" do\n        values = (1...3).sample(1)\n        values.size.should eq(1)\n        (1 <= values.first <= 2).should be_true\n      end\n\n      it \"samples an inclusive range with n > 1\" do\n        values = (1..10).sample(5)\n        values.size.should eq(5)\n        values.uniq.size.should eq(5)\n        values.all? { |value| 1 <= value <= 10 }.should be_true\n      end\n\n      it \"samples an exclusive range with n > 1\" do\n        values = (1...10).sample(5)\n        values.size.should eq(5)\n        values.uniq.size.should eq(5)\n        values.all? { |value| 1 <= value <= 9 }.should be_true\n      end\n\n      it \"samples an inclusive range with n > 16\" do\n        values = (1..1000).sample(100)\n        values.size.should eq(100)\n        values.uniq.size.should eq(100)\n        values.all? { |value| 1 <= value <= 1000 }.should be_true\n      end\n\n      it \"samples an inclusive range with n equal to or bigger than the available values\" do\n        values = (1..10).sample(20)\n        values.size.should eq(10)\n        values.uniq.size.should eq(10)\n        values.all? { |value| 1 <= value <= 10 }.should be_true\n      end\n\n      it \"raises on invalid range without n\" do\n        expect_raises ArgumentError do\n          (1..0).sample\n        end\n      end\n\n      it \"raises on invalid range with n = 0\" do\n        expect_raises ArgumentError do\n          (1..0).sample(0)\n        end\n      end\n\n      it \"raises on invalid range with n = 1\" do\n        expect_raises ArgumentError do\n          (1..0).sample(1)\n        end\n      end\n\n      it \"raises on invalid range with n > 1\" do\n        expect_raises ArgumentError do\n          (1..0).sample(10)\n        end\n      end\n\n      it \"raises on exclusive range that would underflow\" do\n        expect_raises ArgumentError do\n          (1_u8...0_u8).sample(10)\n        end\n      end\n    end\n\n    context \"for a float range\" do\n      it \"samples an inclusive range without n\" do\n        value = (1.0..2.0).sample\n        (1.0 <= value <= 2.0).should be_true\n      end\n\n      it \"samples an exclusive range without n\" do\n        value = (1.0...2.0).sample\n        (1.0 <= value < 2.0).should be_true\n      end\n\n      it \"samples an inclusive range with n = 1\" do\n        values = (1.0..2.0).sample(1)\n        values.size.should eq(1)\n        (1.0 <= values.first <= 2.0).should be_true\n      end\n\n      it \"samples an exclusive range with n = 1\" do\n        values = (1.0..2.0).sample(1)\n        values.size.should eq(1)\n        (1.0 <= values.first < 2.0).should be_true\n      end\n\n      it \"samples an inclusive range with n > 1\" do\n        values = (1.0..2.0).sample(10)\n        values.size.should eq(10)\n        values.all? { |value| 1.0 <= value <= 2.0 }.should be_true\n      end\n\n      it \"samples an exclusive range with n > 1\" do\n        values = (1.0...2.0).sample(10)\n        values.size.should eq(10)\n        values.all? { |value| 1.0 <= value < 2.0 }.should be_true\n      end\n\n      it \"samples an inclusive range with n >= 1 and begin == end\" do\n        values = (1.0..1.0).sample(3)\n        values.size.should eq(1)\n        values.first.should eq(1.0)\n      end\n\n      it \"samples an inclusive range with n > 16\" do\n        values = (1.0..2.0).sample(100)\n        values.size.should eq(100)\n        values.all? { |value| 1.0 <= value <= 2.0 }.should be_true\n      end\n\n      it \"raises on invalid range with n = 0\" do\n        expect_raises ArgumentError do\n          (1.0..0.0).sample(0)\n        end\n      end\n\n      it \"raises on invalid range with n = 1\" do\n        expect_raises ArgumentError do\n          (1.0..0.0).sample(1)\n        end\n      end\n\n      it \"raises on invalid range with n > 1\" do\n        expect_raises ArgumentError do\n          (1.0..0.0).sample(10)\n        end\n      end\n    end\n  end\n\n  describe \"#step\" do\n    it_iterates \"inclusive default\", [1, 2, 3, 4, 5], (1..5).step\n    it_iterates \"inclusive step\", [1, 3, 5], (1..5).step(2)\n    it_iterates \"inclusive step over\", [1, 3, 5], (1..6).step(2)\n\n    it_iterates \"exclusive default\", [1, 2, 3, 4], (1...5).step\n    it_iterates \"exclusive step\", [1, 3], (1...5).step(2)\n    it_iterates \"exclusive step over\", [1, 3, 5], (1...6).step(2)\n\n    it_iterates \"endless range\", [1, 3, 5, 7, 9], (1...nil).step(2), infinite: true\n\n    it \"raises on beginless range\" do\n      expect_raises(ArgumentError, \"Can't step beginless range\") do\n        (nil..3).step(2) { }\n      end\n    end\n\n    it_iterates \"begin > end inclusive\", [] of Int32, (1..0).step(1)\n    it_iterates \"begin > end exclusive\", [] of Int32, (1...0).step(1)\n\n    it_iterates \"begin == end inclusive\", [1], (1..1).step(1)\n    it_iterates \"begin == end exclusive\", [] of Int32, (1...1).step(1)\n    it_iterates \"begin.succ == end inclusive\", [1, 2] of Int32, (1..2).step(1)\n    it_iterates \"begin.succ == end exclusive\", [1] of Int32, (1...2).step(1)\n\n    it_iterates \"Float step\", [1.0, 1.5, 2.0, 2.5, 3.0], (1..3).step(by: 0.5)\n    it_iterates \"Time::Span step\", [1.minutes, 2.minutes, 3.minutes], (1.minutes..3.minutes).step(by: 1.minutes)\n\n    describe \"with #succ type\" do\n      range_basic = RangeSpecIntWrapper.new(1)..RangeSpecIntWrapper.new(5)\n      it_iterates \"basic\", [1, 2, 3, 4, 5].map(&->RangeSpecIntWrapper.new(Int32)), range_basic.step\n      it_iterates \"basic by\", [1, 3, 5].map(&->RangeSpecIntWrapper.new(Int32)), range_basic.step(by: 2)\n      it_iterates \"missing end by\", [1, 4].map(&->RangeSpecIntWrapper.new(Int32)), range_basic.step(by: 3)\n\n      it_iterates \"at definition range\",\n        [Int32::MAX - 2, Int32::MAX - 1, Int32::MAX].map(&->RangeSpecIntWrapper.new(Int32)),\n        (RangeSpecIntWrapper.new(Int32::MAX - 2)..RangeSpecIntWrapper.new(Int32::MAX)).step\n      it_iterates \"at definition range by\",\n        [RangeSpecIntWrapper.new(Int32::MAX - 2), RangeSpecIntWrapper.new(Int32::MAX)],\n        (RangeSpecIntWrapper.new(Int32::MAX - 2)..RangeSpecIntWrapper.new(Int32::MAX)).step(by: 2)\n      it_iterates \"at definition range missing by\",\n        [RangeSpecIntWrapper.new(Int32::MAX - 1)],\n        (RangeSpecIntWrapper.new(Int32::MAX - 1)..RangeSpecIntWrapper.new(Int32::MAX)).step(by: 2)\n      it_iterates \"at definition range by\",\n        [RangeSpecIntWrapper.new(Int32::MAX - 3), RangeSpecIntWrapper.new(Int32::MAX - 1)],\n        (RangeSpecIntWrapper.new(Int32::MAX - 3)..RangeSpecIntWrapper.new(Int32::MAX - 1)).step(by: 2)\n      it_iterates \"at definition range missing by\",\n        [RangeSpecIntWrapper.new(Int32::MAX - 2)],\n        (RangeSpecIntWrapper.new(Int32::MAX - 2)..RangeSpecIntWrapper.new(Int32::MAX - 1)).step(by: 2)\n    end\n  end\n\n  describe \"map\" do\n    it \"optimizes for int range\" do\n      (5..12).map(&.itself).should eq([5, 6, 7, 8, 9, 10, 11, 12])\n      (5...12).map(&.itself).should eq([5, 6, 7, 8, 9, 10, 11])\n      (5..4).map(&.itself).size.should eq(0)\n    end\n\n    it \"works for other types\" do\n      ('a'..'c').map(&.itself).should eq(['a', 'b', 'c'])\n    end\n  end\n\n  describe \"#size\" do\n    describe \"Int\" do\n      it { (5..12).size.should eq(8) }\n      it { (5...12).size.should eq(7) }\n      it { (5..4).size.should eq(0) }\n      it { (0..0).size.should eq(1) }\n      it { (0...0).size.should eq(0) }\n      it { (1..1).size.should eq(1) }\n      it { (1...1).size.should eq(0) }\n\n      it { (-12..-5).size.should eq(8) }\n      it { (-12...-5).size.should eq(7) }\n      it { (-4..-5).size.should eq(0) }\n      it { (-1..-1).size.should eq(1) }\n      it { (-1...-1).size.should eq(0) }\n\n      it { (-3..3).size.should eq(7) }\n      it { (-3...3).size.should eq(6) }\n      it { (-3..0).size.should eq(4) }\n      it { (-3...0).size.should eq(3) }\n      it { (3..-3).size.should eq(0) }\n      it { (3...-3).size.should eq(0) }\n      it { (-128_i8..0_i8).size.should eq(129) }\n      it { (-128_i8...0_i8).size.should eq(128) }\n\n      it { (Int32::MAX..Int32::MAX).size.should eq(1) }\n      it { (Int32::MAX...Int32::MAX).size.should eq(0) }\n      it { (-Int32::MAX..-Int32::MAX).size.should eq(1) }\n      it { (-Int32::MAX...-Int32::MAX).size.should eq(0) }\n\n      it { (5_u8..12_u8).size.should eq(8) }\n      it { (5_u8...12_u8).size.should eq(7) }\n      it { (5_u8..4_u8).size.should eq(0) }\n      it { (0_u8..0_u8).size.should eq(1) }\n      it { (0_u8...0_u8).size.should eq(0) }\n      it { (1_u8..1_u8).size.should eq(1) }\n      it { (1_u8...1_u8).size.should eq(0) }\n      it { (UInt8::MAX..UInt8::MAX).size.should eq(1) }\n      it { (UInt8::MAX...UInt8::MAX).size.should eq(0) }\n\n      it { (-32768_i16..254_u8).size.should eq(33023) }\n      it { (-128_i8..127_i8).size.should eq(256) }\n      it { (Int32::MIN..-127_i8).size.should eq(2_147_483_522) }\n      it { ((Int16::MIN.to_i32 - 1)..127_i16).size.should eq(32897) }\n    end\n\n    it \"works for other types\" do\n      ('a'..'c').size.should eq(3)\n    end\n\n    it \"raises on beginless range\" do\n      expect_raises(ArgumentError, \"Can't calculate size of an open range\") do\n        (..3).size\n      end\n      expect_raises(ArgumentError, \"Can't calculate size of an open range\") do\n        (nil.as(Int32?)..3).size\n      end\n    end\n\n    it \"raises on endless range\" do\n      expect_raises(ArgumentError, \"Can't calculate size of an open range\") do\n        (3..).size\n      end\n      expect_raises(ArgumentError, \"Can't calculate size of an open range\") do\n        (3..nil.as(Int32?)).size\n      end\n    end\n  end\n\n  it \"clones\" do\n    range = [1]..[2]\n    clone = range.clone\n    clone.should eq(range)\n    clone.begin.should_not be(range.begin)\n    clone.end.should_not be(range.end)\n  end\n\n  describe \"===\" do\n    it \"inclusive\" do\n      ((1..2) === 0).should be_false\n      ((1..2) === 1).should be_true\n      ((1..2) === 2).should be_true\n      ((1..2) === 3).should be_false\n    end\n\n    it \"exclusive\" do\n      ((1...2) === 0).should be_false\n      ((1...2) === 1).should be_true\n      ((1...2) === 2).should be_false\n    end\n\n    it \"endless\" do\n      ((1...nil) === 0).should be_false\n      ((1...nil) === 1).should be_true\n      ((1...nil) === 2).should be_true\n      ((1..nil) === 2).should be_true\n    end\n\n    it \"beginless\" do\n      ((nil..3) === -1).should be_true\n      ((nil..3) === 3).should be_true\n      ((nil..3) === 4).should be_false\n      ((nil...3) === 2).should be_true\n      ((nil...3) === 3).should be_false\n    end\n\n    it \"no limits\" do\n      ((nil..nil) === 1).should be_true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/record_spec.cr",
    "content": "require \"spec\"\n\nprivate module RecordSpec\n  record Record1,\n    x : Int32,\n    y : Array(Int32)\n\n  record Record2,\n    x : Int32 = 0,\n    y : Array(Int32) = [2, 3]\n\n  record Record3,\n    x = 0,\n    y = [2, 3]\nend\n\nprivate abstract struct Base\nend\n\nprivate record Sub < Base, x : Int32\n\nprivate record CustomInitializer, id : Int32, active : Bool = false do\n  def initialize(*, __id id : Int32)\n    @id = id\n  end\nend\n\ndescribe \"record\" do\n  it \"defines record with type declarations\" do\n    ary = [2, 3]\n    rec = RecordSpec::Record1.new(1, ary)\n    rec.x.should eq(1)\n    rec.y.should be(ary)\n\n    copy = rec.copy_with(x: 5)\n    copy.x.should eq(5)\n    copy.y.should be(rec.y)\n\n    cloned = rec.clone\n    cloned.x.should eq(1)\n    cloned.y.should eq(ary)\n    cloned.y.should_not be(ary)\n  end\n\n  it \"defines record with type declaration and initialization\" do\n    rec = RecordSpec::Record2.new\n    rec.x.should eq(0)\n    rec.y.should eq([2, 3])\n\n    copy = rec.copy_with(y: [7, 8])\n    copy.x.should eq(rec.x)\n    copy.y.should eq([7, 8])\n\n    cloned = rec.clone\n    cloned.x.should eq(0)\n    cloned.y.should eq(rec.y)\n    cloned.y.should_not be(rec.y)\n  end\n\n  it \"defines record with assignments\" do\n    rec = RecordSpec::Record3.new\n    rec.x.should eq(0)\n    rec.y.should eq([2, 3])\n\n    copy = rec.copy_with(y: [7, 8])\n    copy.x.should eq(rec.x)\n    copy.y.should eq([7, 8])\n\n    cloned = rec.clone\n    cloned.x.should eq(0)\n    cloned.y.should eq(rec.y)\n    cloned.y.should_not be(rec.y)\n  end\n\n  it \"can clone record with parent type\" do\n    rec = Sub.new 1\n    rec.clone.x.should eq(1)\n  end\n\n  it \"can copy_with record with parent type\" do\n    rec = Sub.new 1\n    rec.copy_with(x: 2).x.should eq(2)\n  end\n\n  it \"uses the default values on the ivars\" do\n    CustomInitializer.new(__id: 10).active.should be_false\n  end\nend\n"
  },
  {
    "path": "spec/std/reference_spec.cr",
    "content": "require \"spec\"\nrequire \"../support/finalize\"\n\nprivate module ReferenceSpec\n  class TestClass\n    @x : Int32\n    @y : String\n\n    def initialize(@x, @y)\n    end\n  end\n\n  class TestClassBase\n  end\n\n  class TestClassSubclass < TestClassBase\n  end\n\n  class DupCloneClass\n    getter x, y\n\n    def initialize\n      @x = 1\n      @y = \"y\"\n    end\n\n    def_clone\n  end\n\n  class DupCloneRecursiveClass\n    getter x, y, z\n\n    def initialize\n      @x = 1\n      @y = [1, 2, 3]\n      @z = self\n    end\n\n    def_clone\n  end\n\n  abstract class Abstract\n  end\n\n  class Concrete < Abstract\n    property x\n\n    def initialize(@x : Int32)\n    end\n  end\n\n  class TestClassWithFinalize\n    include FinalizeCounter\n  end\n\n  class NestedErrorTestClass\n    @x = RaisesOnInspectAndClone.new\n\n    def_clone\n  end\n\n  class RaisesOnInspectAndClone\n    def inspect(io : IO)\n      raise RuntimeError.new(\"I'm shy\")\n    end\n\n    def clone\n      raise RuntimeError.new(\"I'm unique\")\n    end\n  end\nend\n\nprivate def expect_empty_recursive_hashes\n  Fiber.current.exec_recursive_hash.dup.should be_empty\n  Fiber.current.exec_recursive_clone_hash.dup.should be_empty\nensure\n  Fiber.current.exec_recursive_clone_hash.clear\n  Fiber.current.exec_recursive_hash.clear\nend\n\ndescribe \"Reference\" do\n  it \"compares reference to other reference\" do\n    o1 = Reference.new\n    o2 = Reference.new\n    (o1 == o1).should be_true\n    (o1 == o2).should be_false\n    (o1 == 1).should be_false\n  end\n\n  it \"should not be nil\" do\n    Reference.new.nil?.should be_false\n  end\n\n  it \"should be false when negated\" do\n    (!Reference.new).should be_false\n  end\n\n  describe \"#inspect\" do\n    it \"does inspect\" do\n      r = ReferenceSpec::TestClass.new(1, \"hello\")\n      r.inspect.should eq(%(#<ReferenceSpec::TestClass:0x#{r.object_id.to_s(16)} @x=1, @y=\"hello\">))\n      expect_empty_recursive_hashes\n    end\n\n    it \"does inspect for class\" do\n      String.inspect.should eq(\"String\")\n      expect_empty_recursive_hashes\n    end\n\n    it \"handles error\" do\n      r = ReferenceSpec::NestedErrorTestClass.new\n      expect_raises RuntimeError, \"I'm shy\" do\n        r.inspect\n      end\n      expect_empty_recursive_hashes\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"does to_s\" do\n      r = ReferenceSpec::TestClass.new(1, \"hello\")\n      r.to_s.should eq(%(#<ReferenceSpec::TestClass:0x#{r.object_id.to_s(16)}>))\n    end\n\n    it \"does to_s for class\" do\n      String.to_s.should eq(\"String\")\n    end\n\n    it \"does to_s for class if virtual\" do\n      [ReferenceSpec::TestClassBase, ReferenceSpec::TestClassSubclass].to_s.should eq(\"[ReferenceSpec::TestClassBase, ReferenceSpec::TestClassSubclass]\")\n    end\n  end\n\n  it \"returns itself\" do\n    x = \"hello\"\n    x.itself.should be(x)\n  end\n\n  describe \"#dup\" do\n    it \"dups\" do\n      original = ReferenceSpec::DupCloneClass.new\n      duplicate = original.dup\n      duplicate.should_not be(original)\n      duplicate.x.should eq(original.x)\n      duplicate.y.should be(original.y)\n    end\n\n    it \"can dup class that inherits abstract class\" do\n      original = ReferenceSpec::Concrete.new(2).as(ReferenceSpec::Abstract)\n      duplicate = original.dup\n      duplicate.should be_a(ReferenceSpec::Concrete)\n      duplicate.should_not be(original)\n      duplicate.x.should eq(original.x)\n    end\n\n    it \"calls #finalize on #dup'ed objects\" do\n      obj = ReferenceSpec::TestClassWithFinalize.new\n      assert_finalizes(\"dup\") { obj.dup }\n    end\n  end\n\n  describe \"#clone\" do\n    it \"clones with def_clone\" do\n      original = ReferenceSpec::DupCloneClass.new\n      clone = original.clone\n      clone.should_not be(original)\n      clone.x.should eq(original.x)\n      expect_empty_recursive_hashes\n    end\n\n    it \"clones with def_clone (recursive type)\" do\n      original = ReferenceSpec::DupCloneRecursiveClass.new\n      clone = original.clone\n      clone.should_not be(original)\n      clone.x.should eq(original.x)\n      clone.y.should_not be(original.y)\n      clone.y.should eq(original.y)\n      clone.z.should be(clone)\n      expect_empty_recursive_hashes\n    end\n\n    it \"handles error\" do\n      r = ReferenceSpec::NestedErrorTestClass.new\n      expect_raises RuntimeError, \"I'm unique\" do\n        r.clone\n      end\n      expect_empty_recursive_hashes\n    end\n  end\n\n  it \"pretty_print\" do\n    ReferenceSpec::TestClassBase.new.pretty_inspect.should match(/\\A#<ReferenceSpec::TestClassBase:0x[0-9a-f]+>\\Z/)\n    ReferenceSpec::TestClass.new(42, \"foo\").pretty_inspect.should match(/\\A#<ReferenceSpec::TestClass:0x[0-9a-f]+ @x=42, @y=\"foo\">\\Z/)\n    expect_empty_recursive_hashes\n  end\nend\n"
  },
  {
    "path": "spec/std/regex/match_data_spec.cr",
    "content": "require \"spec\"\n\nprivate def matchdata(re, string)\n  re.match(string).should_not be_nil\nend\n\ndescribe \"Regex::MatchData\" do\n  it \"#regex\" do\n    regex = /foo/\n    matchdata(regex, \"foo\").regex.should be(regex)\n  end\n\n  it \"#string\" do\n    string = \"foo\"\n    matchdata(/foo/, string).string.should be(string)\n  end\n\n  it \"#inspect\" do\n    matchdata(/f(o)(x)/, \"the fox\").inspect.should eq(%(Regex::MatchData(\"fox\" 1:\"o\" 2:\"x\")))\n    matchdata(/f(o)(x)?/, \"the fort\").inspect.should eq(%(Regex::MatchData(\"fo\" 1:\"o\" 2:nil)))\n    matchdata(/fox/, \"the fox\").inspect.should eq(%(Regex::MatchData(\"fox\")))\n  end\n\n  it \"#to_s\" do\n    matchdata(/f(o)(x)/, \"the fox\").to_s.should eq(\"fox\")\n    matchdata(/f(?<lettero>o)(?<letterx>x)/, \"the fox\").to_s.should eq(\"fox\")\n    matchdata(/fox/, \"the fox\").to_s.should eq(\"fox\")\n  end\n\n  it \"#pretty_print\" do\n    matchdata(/f(o)(x)?/, \"the fo\").pretty_inspect.should eq(%(Regex::MatchData(\"fo\" 1:\"o\" 2:nil)))\n\n    expected = <<-REGEX\n      Regex::MatchData(\"foooo\"\n       first:\"f\"\n       second:\"oooo\"\n       third:\"ooo\"\n       fourth:\"oo\"\n       fifth:\"o\")\n      REGEX\n\n    matchdata(/(?<first>f)(?<second>o(?<third>o(?<fourth>o(?<fifth>o))))/, \"fooooo\").pretty_inspect.should eq(expected)\n  end\n\n  it \"#size\" do\n    matchdata(/[p-s]/, \"Crystal\").size.should eq(1)\n    matchdata(/r(ys)/, \"Crystal\").size.should eq(2)\n    matchdata(/r(ys)(?<ok>ta)/, \"Crystal\").size.should eq(3)\n    matchdata(/foo(bar)?/, \"foo\").size.should eq(2)\n    matchdata(/foo(bar)?/, \"foobar\").size.should eq(2)\n  end\n\n  describe \"#begin\" do\n    it \"no captures\" do\n      matchdata(/foo/, \"foo\").begin.should eq 0\n      matchdata(/foo/, \"foo\").begin(-1).should eq 0\n      matchdata(/foo/, \".foo.\").begin.should eq 1\n      matchdata(/foo/, \".foo.\").begin(-1).should eq 1\n    end\n\n    it \"out of range\" do\n      expect_raises(IndexError) do\n        matchdata(/foo/, \"foo\").begin(1)\n      end\n    end\n\n    it \"with capture\" do\n      md = matchdata(/f(o)o/, \"foo\")\n      md.begin.should eq 0\n      md.begin(1).should eq 1\n      md.begin(-1).should eq 1\n\n      md = matchdata(/f(o)o/, \".foo.\")\n      md.begin.should eq 1\n      md.begin(1).should eq 2\n      md.begin(-1).should eq 2\n    end\n\n    it \"with unmatched capture\" do\n      md = matchdata(/f(x)?o/, \"foo\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.begin(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.begin(-1)\n      end\n\n      md = matchdata(/f(x)?o/, \".foo.\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.begin(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.begin(-1)\n      end\n    end\n\n    it \"char index\" do\n      matchdata(/foo/, \"öfoo\").begin.should eq 1\n    end\n  end\n\n  describe \"#byte_begin\" do\n    it \"char index\" do\n      matchdata(/foo/, \"öfoo\").byte_begin.should eq 2\n    end\n\n    it \"with unmatched capture\" do\n      md = matchdata(/f(x)?o/, \"foo\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_begin(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_begin(-1)\n      end\n\n      md = matchdata(/f(x)?o/, \".foo.\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_begin(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_begin(-1)\n      end\n    end\n  end\n\n  describe \"#end\" do\n    it \"no captures\" do\n      matchdata(/foo/, \"foo\").end.should eq 3\n      matchdata(/foo/, \"foo\").end(-1).should eq 3\n      matchdata(/foo/, \".foo.\").end.should eq 4\n      matchdata(/foo/, \".foo.\").end(-1).should eq 4\n    end\n\n    it \"out of range\" do\n      expect_raises(IndexError) do\n        matchdata(/foo/, \"foo\").end(1)\n      end\n    end\n\n    it \"with capture\" do\n      md = matchdata(/f(o)o/, \"foo\")\n      md.end.should eq 3\n      md.end(1).should eq 2\n      md.end(-1).should eq 2\n\n      md = matchdata(/f(o)o/, \".foo.\")\n      md.end.should eq 4\n      md.end(1).should eq 3\n      md.end(-1).should eq 3\n    end\n\n    it \"with unmatched capture\" do\n      md = matchdata(/f(x)?o/, \"foo\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.end(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.end(-1)\n      end\n\n      md = matchdata(/f(x)?o/, \".foo.\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.end(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.end(-1)\n      end\n    end\n\n    it \"char index\" do\n      matchdata(/foo/, \"öfoo\").end.should eq 4\n    end\n  end\n\n  describe \"#byte_end\" do\n    it \"char index\" do\n      matchdata(/foo/, \"öfoo\").byte_end.should eq 5\n    end\n\n    it \"with unmatched capture\" do\n      md = matchdata(/f(x)?o/, \"foo\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_end(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_end(-1)\n      end\n\n      md = matchdata(/f(x)?o/, \".foo.\")\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_end(1)\n      end\n      expect_raises(IndexError, \"Capture group 1 was not matched\") do\n        md.byte_end(-1)\n      end\n    end\n  end\n\n  describe \"#[]\" do\n    describe \"String\" do\n      it \"capture named group\" do\n        md = matchdata(/f(?<g1>o+)(?<g2>bar?)/, \"fooba\")\n        md[\"g1\"].should eq(\"oo\")\n        md[\"g2\"].should eq(\"ba\")\n      end\n\n      it \"captures duplicated named group\" do\n        re = /(?:(?<g1>foo)|(?<g1>bar))*/\n\n        matchdata(re, \"foo\")[\"g1\"].should eq(\"foo\")\n        matchdata(re, \"bar\")[\"g1\"].should eq(\"bar\")\n        matchdata(re, \"foobar\")[\"g1\"].should eq(\"bar\")\n        matchdata(re, \"barfoo\")[\"g1\"].should eq(\"foo\")\n      end\n\n      it \"named groups with same prefix\" do\n        md = matchdata(/KEY_(?<key>\\w+)\\s+(?<keycode>.*)/, \"KEY_POWER 116\")\n        md[\"key\"].should eq \"POWER\"\n        md[\"keycode\"].should eq \"116\"\n      end\n\n      it \"raises exception when named group doesn't exist\" do\n        md = matchdata(/foo/, \"foo\")\n        expect_raises(KeyError, \"Capture group 'group' does not exist\") { md[\"group\"] }\n\n        expect_raises(KeyError, \"Capture group 'groupwithlongname' does not exist\") { md[\"groupwithlongname\"] }\n      end\n\n      it \"captures empty group\" do\n        matchdata(/(?<g1>z?)foo/, \"foo\")[\"g1\"].should eq(\"\")\n      end\n\n      it \"raises exception on optional empty group\" do\n        md = matchdata(/(?<g1>z)?foo/, \"foo\")\n        expect_raises(KeyError, \"Capture group 'g1' was not matched\") { md[\"g1\"] }\n      end\n    end\n\n    describe \"Int\" do\n      it \"can use negative index\" do\n        md = matchdata(/(f)(oo)/, \"foo\")\n        md[-1].should eq(\"oo\")\n        md[-2].should eq(\"f\")\n        md[-3].should eq(\"foo\")\n        expect_raises(IndexError, \"Invalid capture group index: -4\") { md[-4] }\n      end\n\n      it \"raises if outside match range with []\" do\n        md = matchdata(/foo/, \"foo\")\n        expect_raises(IndexError, \"Invalid capture group index: 1\") { md[1] }\n      end\n\n      it \"raises if special variable accessed on invalid capture group\" do\n        md = matchdata(/spice(s)?/, \"spice\")\n        expect_raises(IndexError, \"Capture group 1 was not matched\") { md[1] }\n        expect_raises(IndexError, \"Invalid capture group index: 3\") { md[3] }\n      end\n\n      it \"captures empty group\" do\n        matchdata(/(?<g1>z?)foo/, \"foo\")[1].should eq(\"\")\n      end\n\n      it \"raises exception on optional empty group\" do\n        md = matchdata(/(?<g1>z)?foo/, \"foo\")\n        expect_raises(IndexError, \"Capture group 1 was not matched\") { md[1] }\n      end\n    end\n\n    describe \"Range\" do\n      it \"can use range\" do\n        md = matchdata(/(a)(b)/, \"ab\")\n        md[1..2].should eq([\"a\", \"b\"])\n        md[1..].should eq([\"a\", \"b\"])\n        md[..].should eq([\"ab\", \"a\", \"b\"])\n        expect_raises(IndexError) { md[4..] }\n      end\n\n      it \"can use start and count\" do\n        md = matchdata(/(a)(b)/, \"ab\")\n        md[1, 2].should eq([\"a\", \"b\"])\n        expect_raises(IndexError) { md[4, 1] }\n      end\n    end\n  end\n\n  describe \"#[]?\" do\n    describe \"String\" do\n      it \"capture named group\" do\n        md = matchdata(/f(?<g1>o+)(?<g2>bar?)/, \"fooba\")\n        md[\"g1\"]?.should eq(\"oo\")\n        md[\"g2\"]?.should eq(\"ba\")\n      end\n\n      it \"captures duplicated named group\" do\n        re = /(?:(?<g1>foo)|(?<g1>bar))*/\n\n        md = matchdata(re, \"foo\")\n        md[\"g1\"]?.should eq(\"foo\")\n\n        md = matchdata(re, \"bar\")\n        md[\"g1\"]?.should eq(\"bar\")\n\n        md = matchdata(re, \"foobar\")\n        md[\"g1\"]?.should eq(\"bar\")\n\n        md = matchdata(re, \"barfoo\")\n        md[\"g1\"]?.should eq(\"foo\")\n      end\n\n      it \"returns nil exception when named group doesn't exist\" do\n        md = matchdata(/foo/, \"foo\")\n        md[\"group\"]?.should be_nil\n        md[\"groupwithlongname\"]?.should be_nil\n      end\n\n      it \"capture empty group\" do\n        matchdata(/(?<g1>z?)foo/, \"foo\")[\"g1\"]?.should eq(\"\")\n      end\n\n      it \"capture optional empty group\" do\n        matchdata(/(?<g1>z)?foo/, \"foo\")[\"g1\"]?.should be_nil\n      end\n    end\n\n    describe \"Int\" do\n      it \"can use negative index\" do\n        md = matchdata(/(b)?(f)(oo)/, \"foo\")\n        md[-1]?.should eq(\"oo\")\n        md[-2]?.should eq(\"f\")\n        md[-3]?.should be_nil\n        md[-4]?.should eq(\"foo\")\n      end\n\n      it \"returns nil if outside match range with []\" do\n        md = matchdata(/foo/, \"foo\")\n        md[1]?.should be_nil\n      end\n\n      it \"capture empty group\" do\n        matchdata(/(?<g1>z?)foo/, \"foo\")[1]?.should eq(\"\")\n      end\n\n      it \"capture optional empty group\" do\n        matchdata(/(?<g1>z)?foo/, \"foo\")[1]?.should be_nil\n      end\n    end\n\n    describe \"Range\" do\n      it \"can use range\" do\n        md = matchdata(/(a)(b)/, \"ab\")\n        md[1..2]?.should eq([\"a\", \"b\"])\n        md[1..]?.should eq([\"a\", \"b\"])\n        md[..]?.should eq([\"ab\", \"a\", \"b\"])\n        md[4..]?.should be_nil\n        md[-4..]?.should be_nil\n      end\n\n      it \"can use start and count\" do\n        md = matchdata(/(a)(b)/, \"ab\")\n        md[1, 2]?.should eq([\"a\", \"b\"])\n        md[4, 1]?.should be_nil\n      end\n    end\n  end\n\n  describe \"#post_match\" do\n    it \"returns an empty string when there's nothing after\" do\n      matchdata(/ystal/, \"Crystal\").post_match.should eq \"\"\n    end\n\n    it \"returns the part of the string after the match\" do\n      matchdata(/yst/, \"Crystal\").post_match.should eq \"al\"\n    end\n\n    it \"works with unicode\" do\n      matchdata(/本/, \"há日本語\").post_match.should eq \"語\"\n    end\n  end\n\n  describe \"#pre_match\" do\n    it \"returns an empty string when there's nothing before\" do\n      matchdata(/Cryst/, \"Crystal\").pre_match.should eq \"\"\n    end\n\n    it \"returns the part of the string before the match\" do\n      matchdata(/yst/, \"Crystal\").pre_match.should eq \"Cr\"\n    end\n\n    it \"works with unicode\" do\n      matchdata(/本/, \"há日本語\").pre_match.should eq \"há日\"\n    end\n  end\n\n  describe \"#captures\" do\n    it \"gets an array of unnamed captures\" do\n      matchdata(/(Cr)y/, \"Crystal\").captures.should eq([\"Cr\"])\n      matchdata(/(Cr)(?<name1>y)(st)(?<name2>al)/, \"Crystal\").captures.should eq([\"Cr\", \"st\"])\n    end\n\n    it \"gets an array of unnamed captures with optional\" do\n      matchdata(/(Cr)(s)?/, \"Crystal\").captures.should eq([\"Cr\", nil])\n      matchdata(/(Cr)(?<name1>s)?(tal)?/, \"Crystal\").captures.should eq([\"Cr\", nil])\n    end\n\n    it \"doesn't get named captures when there are more than 255\" do\n      regex = Regex.new(Array.new(256) { |i| \"(?<c#{i}>.)\" }.join)\n      matchdata(regex, \"x\" * 256).captures.should eq([] of String)\n    end\n  end\n\n  describe \"#named_captures\" do\n    it \"gets a hash of named captures\" do\n      matchdata(/(?<name1>Cr)y/, \"Crystal\").named_captures.should eq({\"name1\" => \"Cr\"})\n      matchdata(/(Cr)(?<name1>y)(st)(?<name2>al)/, \"Crystal\").named_captures.should eq({\"name1\" => \"y\", \"name2\" => \"al\"})\n    end\n\n    it \"gets a hash of named captures with optional\" do\n      matchdata(/(?<name1>Cr)(?<name2>s)?/, \"Crystal\").named_captures.should eq({\"name1\" => \"Cr\", \"name2\" => nil})\n      matchdata(/(Cr)(?<name1>s)?(t)?(?<name2>al)?/, \"Crystal\").named_captures.should eq({\"name1\" => nil, \"name2\" => nil})\n    end\n\n    it \"gets a hash of named captures with duplicated name\" do\n      matchdata(/(?<name>Cr)y(?<name>s)/, \"Crystal\").named_captures.should eq({\"name\" => \"s\"})\n    end\n\n    it \"gets more than 127 named captures\" do\n      regex = Regex.new(Array.new(128) { |i| \"(?<c#{i}>.)\" }.join)\n      captures = matchdata(regex, \"x\" * 128).named_captures\n      captures.size.should eq(128)\n      128.times { |i| captures[\"c#{i}\"].should eq(\"x\") }\n    end\n  end\n\n  describe \"#to_a\" do\n    it \"converts into an array\" do\n      matchdata(/(?<name1>Cr)(y)/, \"Crystal\").to_a.should eq([\"Cry\", \"Cr\", \"y\"])\n      matchdata(/(Cr)(?<name1>y)(st)(?<name2>al)/, \"Crystal\").to_a.should eq([\"Crystal\", \"Cr\", \"y\", \"st\", \"al\"])\n    end\n\n    it \"converts into an array having nil\" do\n      matchdata(/(?<name1>Cr)(s)?/, \"Crystal\").to_a.should eq([\"Cr\", \"Cr\", nil])\n      matchdata(/(Cr)(?<name1>s)?(yst)?(?<name2>al)?/, \"Crystal\").to_a.should eq([\"Crystal\", \"Cr\", nil, \"yst\", \"al\"])\n    end\n  end\n\n  describe \"#to_h\" do\n    it \"converts into a hash\" do\n      matchdata(/(?<name1>Cr)(y)/, \"Crystal\").to_h.should eq({\n              0 => \"Cry\",\n        \"name1\" => \"Cr\",\n              2 => \"y\",\n      })\n      matchdata(/(Cr)(?<name1>y)(st)(?<name2>al)/, \"Crystal\").to_h.should eq({\n              0 => \"Crystal\",\n              1 => \"Cr\",\n        \"name1\" => \"y\",\n              3 => \"st\",\n        \"name2\" => \"al\",\n      })\n    end\n\n    it \"converts into a hash having nil\" do\n      matchdata(/(?<name1>Cr)(s)?/, \"Crystal\").to_h.should eq({\n              0 => \"Cr\",\n        \"name1\" => \"Cr\",\n              2 => nil,\n      })\n      matchdata(/(Cr)(?<name1>s)?(yst)?(?<name2>al)?/, \"Crystal\").to_h.should eq({\n              0 => \"Crystal\",\n              1 => \"Cr\",\n        \"name1\" => nil,\n              3 => \"yst\",\n        \"name2\" => \"al\",\n      })\n    end\n\n    it \"converts into a hash with duplicated names\" do\n      matchdata(/(Cr)(?<name>s)?(yst)?(?<name>al)?/, \"Crystal\").to_h.should eq({\n             0 => \"Crystal\",\n             1 => \"Cr\",\n        \"name\" => \"al\",\n             3 => \"yst\",\n      })\n    end\n  end\n\n  it \"#==\" do\n    re = /((?<hello>he)llo)/\n    m1 = re.match(\"hello\")\n    m2 = re.match(\"hello\")\n    m1.should be_truthy\n    m2.should be_truthy\n    m1.should eq(m2)\n  end\n\n  it \"#hash\" do\n    re = /(a|b)/\n    hash = re.match(\"a\").hash\n    hash.should eq(re.match(\"a\").hash)\n    hash.should_not eq(re.match(\"b\").hash)\n  end\nend\n"
  },
  {
    "path": "spec/std/regex_spec.cr",
    "content": "require \"./spec_helper\"\n\ndescribe \"Regex\" do\n  describe \".new\" do\n    it \"doesn't crash when PCRE tries to free some memory (#771)\" do\n      expect_raises(ArgumentError) { Regex.new(\"foo)\") }\n    end\n\n    it \"raises exception with invalid regex\" do\n      expect_raises(ArgumentError) { Regex.new(\"+\") }\n    end\n\n    describe \"options\" do\n      it \"regular\" do\n        Regex.new(\"\", Regex::CompileOptions::ANCHORED).options.anchored?.should be_true\n      end\n\n      it \"unnamed option\" do\n        {% if Regex::Engine.resolve.name == \"Regex::PCRE\" %}\n          Regex.new(\"^/foo$\", Regex::CompileOptions.new(0x00000020)).matches?(\"/foo\\n\").should be_false\n        {% else %}\n          expect_raises ArgumentError, \"Unknown Regex::Option value: 64\" do\n            Regex.new(\"\", Regex::CompileOptions.new(0x00000040))\n          end\n        {% end %}\n      end\n    end\n\n    it \"raises on invalid UTF-8\" do\n      expect_raises(ArgumentError, /invalid UTF-8 string|UTF-8 error/) do\n        Regex.new(\"\\x96\")\n      end\n      Regex.new(\"\\x96\", :NO_UTF_CHECK).should be_a(Regex)\n    end\n  end\n\n  it \".literal\" do\n    Regex.literal(\"foo\").should eq /foo/\n    Regex.literal(\"foo\", i: true).should eq /foo/i\n    Regex.literal(\"foo\", i: true, m: true).should eq /foo/im\n    Regex.literal(\"foo\", i: true, m: true, x: true).should eq /foo/imx\n    Regex.literal(\"foo\", x: true).should eq /foo/x\n  end\n\n  it \"#options\" do\n    /cat/.options.ignore_case?.should be_false\n    /cat/i.options.ignore_case?.should be_true\n    /cat/.options.multiline?.should be_false\n    /cat/m.options.multiline?.should be_true\n    /cat/.options.extended?.should be_false\n    /cat/x.options.extended?.should be_true\n    /cat/mx.options.multiline?.should be_true\n    /cat/mx.options.extended?.should be_true\n    /cat/mx.options.ignore_case?.should be_false\n    /cat/xi.options.ignore_case?.should be_true\n    /cat/xi.options.extended?.should be_true\n    /cat/xi.options.multiline?.should be_false\n  end\n\n  it \"#source\" do\n    /foo/.source.should eq \"foo\"\n    /(foo|bar)*/.source.should eq \"(foo|bar)*\"\n    /foo\\x96/.source.should eq \"foo\\\\x96\"\n    Regex.new(\"\").source.should eq \"\"\n  end\n\n  describe \"#match\" do\n    it \"returns matchdata\" do\n      md = /(?<bar>.)(?<foo>.)/.match(\"Crystal\").should_not be_nil\n      md[0].should eq \"Cr\"\n      md.captures.should eq [] of String\n      md.named_captures.should eq({\"bar\" => \"C\", \"foo\" => \"r\"})\n    end\n\n    it \"assigns captures\" do\n      matchdata = /foo/.match(\"foo\")\n      $~.should eq(matchdata)\n\n      /foo/.match(\"bar\")\n      expect_raises(NilAssertionError) { $~ }\n    end\n\n    it \"returns nil on non-match\" do\n      /Crystal/.match(\"foo\").should be_nil\n    end\n\n    describe \"with pos\" do\n      it \"positive\" do\n        /foo/.match(\"foo\", 0).should_not be_nil\n        /foo/.match(\"foo\", 1).should be_nil\n        /foo/.match(\".foo\", 1).should_not be_nil\n        /foo/.match(\"..foo\", 1).should_not be_nil\n\n        /foo/.match(\"bar\", 0).should be_nil\n        /foo/.match(\"bar\", 1).should be_nil\n      end\n\n      it \"char index\" do\n        /foo/.match(\"öfoo\", 1).should_not be_nil\n      end\n\n      pending \"negative\" do\n        /foo/.match(\"..foo\", -3).should_not be_nil\n        /foo/.match(\"..foo\", -2).should be_nil\n      end\n    end\n\n    context \"with options\" do\n      it \"deprecated Regex::Options\" do\n        /foo/.match(\".foo\", options: Regex::Options::ANCHORED).should be_nil\n        /foo/.match(\"foo\", options: Regex::Options::ANCHORED).should_not be_nil\n      end\n\n      it \"Regex::Match options\" do\n        /foo/.match(\".foo\", options: Regex::MatchOptions::ANCHORED).should be_nil\n        /foo/.match(\"foo\", options: Regex::MatchOptions::ANCHORED).should_not be_nil\n      end\n    end\n\n    it \"with invalid UTF-8\" do\n      expect_raises(ArgumentError, \"UTF-8 error\") do\n        /([\\w_\\.@#\\/\\*])+/.match(\"\\xFF\\xFE\")\n      end\n\n      if Regex::Engine.version_number >= {10, 36}\n        Regex.new(\"([\\\\w_\\\\.@#\\\\/\\\\*])+\", options: Regex::Options::MATCH_INVALID_UTF).match(\"\\xFF\\xFE\").should be_nil\n      end\n    end\n\n    it \"skip invalid UTF check\" do\n      # no exception raised\n      /f.o/.matches?(\"f\\xFFo\", options: Regex::MatchOptions::NO_UTF_CHECK)\n    end\n  end\n\n  describe \"#match!\" do\n    it \"returns match data\" do\n      md = /(?<bar>.)(?<foo>.)/.match!(\"Crystal\")\n      md[0].should eq \"Cr\"\n      md.captures.should eq [] of String\n      md.named_captures.should eq({\"bar\" => \"C\", \"foo\" => \"r\"})\n    end\n\n    it \"assigns captures\" do\n      md = /foo/.match!(\"foo\")\n      $~.should eq md\n    end\n\n    it \"raises on non-match\" do\n      expect_raises(Regex::Error, \"Match not found\") { /Crystal/.match!(\"foo\") }\n      expect_raises(NilAssertionError) { $~ }\n    end\n\n    context \"with options\" do\n      it \"Regex::Match options\" do\n        expect_raises(Regex::Error, \"Match not found\") do\n          /foo/.match!(\".foo\", options: Regex::MatchOptions::ANCHORED)\n        end\n      end\n    end\n  end\n\n  describe \"#match_at_byte_index\" do\n    it \"assigns captures\" do\n      matchdata = /foo/.match_at_byte_index(\"..foo\", 1)\n      $~.should eq(matchdata)\n\n      /foo/.match_at_byte_index(\"foo\", 1)\n      expect_raises(NilAssertionError) { $~ }\n\n      /foo/.match(\"foo\") # make sure $~ is assigned\n      $~.should_not be_nil\n\n      /foo/.match_at_byte_index(\"foo\", 5)\n      expect_raises(NilAssertionError) { $~ }\n    end\n\n    it \"positive index\" do\n      md = /foo/.match_at_byte_index(\"foo\", 0).should_not be_nil\n      md.begin.should eq 0\n      /foo/.match_at_byte_index(\"foo\", 1).should be_nil\n      md = /foo/.match_at_byte_index(\".foo\", 1).should_not be_nil\n      md.begin.should eq 1\n      md = /foo/.match_at_byte_index(\"..foo\", 1).should_not be_nil\n      md.begin.should eq 2\n      /foo/.match_at_byte_index(\"foo\", 5).should be_nil\n\n      /foo/.match_at_byte_index(\"bar\", 0).should be_nil\n      /foo/.match_at_byte_index(\"bar\", 1).should be_nil\n    end\n\n    it \"multibyte index\" do\n      expect_raises(ArgumentError, \"bad offset into UTF string\") do\n        /foo/.match_at_byte_index(\"öfoo\", 1)\n      end\n\n      if Regex::Engine.version_number >= {10, 36}\n        md = Regex.new(\"foo\", options: Regex::CompileOptions::MATCH_INVALID_UTF).match_at_byte_index(\"öfoo\", 1).should_not be_nil\n        md.begin.should eq 1\n        md.byte_begin.should eq 2\n      end\n\n      md = /foo/.match_at_byte_index(\"öfoo\", 2).should_not be_nil\n      md.begin.should eq 1\n      md.byte_begin.should eq 2\n    end\n\n    pending \"negative\" do\n      md = /foo/.match_at_byte_index(\"..foo\", -3).should_not be_nil\n      md.begin.should eq 0\n      /foo/.match_at_byte_index(\"..foo\", -2).should be_nil\n    end\n\n    context \"with options\" do\n      it \"deprecated Regex::Options\" do\n        /foo/.match_at_byte_index(\"..foo\", 1, options: Regex::Options::ANCHORED).should be_nil\n        /foo/.match_at_byte_index(\".foo\", 1, options: Regex::Options::ANCHORED).should_not be_nil\n      end\n\n      it \"Regex::MatchOptions\" do\n        /foo/.match_at_byte_index(\"..foo\", 1, options: Regex::MatchOptions::ANCHORED).should be_nil\n        /foo/.match_at_byte_index(\".foo\", 1, options: Regex::MatchOptions::ANCHORED).should_not be_nil\n      end\n    end\n  end\n\n  describe \"#matches?\" do\n    it \"basic\" do\n      /foo/.matches?(\"foo\").should be_true\n      expect_raises(NilAssertionError) { $~ }\n      /foo/.matches?(\"bar\").should be_false\n      expect_raises(NilAssertionError) { $~ }\n    end\n\n    describe \"options\" do\n      it \"ignore case\" do\n        /hello/.matches?(\"HeLlO\").should be_false\n        /hello/i.matches?(\"HeLlO\").should be_true\n      end\n\n      describe \"multiline\" do\n        it \"anchor\" do\n          /^bar/.matches?(\"foo\\nbar\").should be_false\n          /^bar/m.matches?(\"foo\\nbar\").should be_true\n        end\n\n        it \"span\" do\n          /<bar.*?>/.matches?(\"foo\\n<bar\\n>baz\").should be_false\n          /<bar.*?>/m.matches?(\"foo\\n<bar\\n>baz\").should be_true\n        end\n      end\n\n      describe \"multiline_only\" do\n        it \"anchor\" do\n          ((/^foo.*$/m).match(\"foo\\nbar\")).try(&.[](0)).should eq \"foo\\nbar\"\n          ((Regex.new(\"^foo.*?\", Regex::Options::MULTILINE_ONLY)).match(\"foo\\nbar\")).try(&.[](0)).should eq \"foo\"\n        end\n      end\n\n      describe \"extended\" do\n        it \"ignores white space\" do\n          /foo   bar/.matches?(\"foobar\").should be_false\n          /foo   bar/x.matches?(\"foobar\").should be_true\n        end\n\n        it \"ignores comments\" do\n          /foo#comment\\nbar/.matches?(\"foobar\").should be_false\n          /foo#comment\\nbar/x.matches?(\"foobar\").should be_true\n        end\n      end\n\n      it \"anchored\" do\n        Regex.new(\"foo\", Regex::CompileOptions::ANCHORED).matches?(\"foo\").should be_true\n        Regex.new(\"foo\", Regex::CompileOptions::ANCHORED).matches?(\".foo\").should be_false\n      end\n    end\n\n    describe \"unicode\" do\n      it \"unicode support\" do\n        /ん/.matches?(\"こんに\").should be_true\n      end\n\n      it \"matches unicode char against [[:alnum:]] (#4704)\" do\n        /[[:alnum:]]/.matches?(\"à\").should be_true\n      end\n\n      it \"matches unicode char against [[:print:]] (#11262)\" do\n        /[[:print:]]/.matches?(\"\\n☃\").should be_true\n      end\n\n      it \"invalid codepoint\" do\n        expect_raises(ArgumentError, \"UTF-8 error\") do\n          /foo/.matches?(\"f\\x96o\")\n        end\n\n        if Regex::Engine.version_number >= {10, 36}\n          Regex.new(\"foo\", options: Regex::CompileOptions::MATCH_INVALID_UTF).matches?(\"f\\x96o\").should be_false\n          Regex.new(\"f\\x96o\", options: Regex::CompileOptions::MATCH_INVALID_UTF | Regex::CompileOptions::NO_UTF_CHECK).matches?(\"f\\x96o\").should be_false\n          Regex.new(\"f.o\", options: Regex::CompileOptions::MATCH_INVALID_UTF).matches?(\"f\\x96o\").should be_false\n          Regex.new(\"\\\\bf\\\\b\", options: Regex::CompileOptions::MATCH_INVALID_UTF).matches?(\"f\\x96o\").should be_true\n          Regex.new(\"\\\\bo\\\\b\", options: Regex::CompileOptions::MATCH_INVALID_UTF).matches?(\"f\\x96o\").should be_true\n        end\n      end\n    end\n\n    context \"with options\" do\n      it \"deprecated Regex::Options\" do\n        /foo/.matches?(\".foo\", options: Regex::Options::ANCHORED).should be_false\n        /foo/.matches?(\"foo\", options: Regex::Options::ANCHORED).should be_true\n      end\n\n      it \"Regex::MatchOptions\" do\n        /foo/.matches?(\".foo\", options: Regex::MatchOptions::ANCHORED).should be_false\n        /foo/.matches?(\"foo\", options: Regex::MatchOptions::ANCHORED).should be_true\n      end\n    end\n\n    pending_wasm32 \"doesn't crash with a large single line string\" do\n      str = File.read(datapath(\"large_single_line_string.txt\"))\n\n      {% if Regex::Engine.resolve.name == \"Regex::PCRE\" %}\n        jit_enabled = uninitialized LibC::Int\n        LibPCRE.config LibPCRE::CONFIG_JIT, pointerof(jit_enabled)\n        pending! \"PCRE JIT mode not available.\" unless 1 == jit_enabled\n\n        # This match may raise on JIT stack limit or not. If it raises, the error message should be the expected one.\n        begin\n          str.matches?(/^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)?$/)\n        rescue exc : Exception\n          exc.to_s.should eq(\"Regex match error: JIT_STACKLIMIT\")\n        end\n      {% else %}\n        # Can't use regex literal because the *LIMIT_DEPTH verb is not supported in libpcre (only libpcre2)\n        # and thus the compiler doesn't recognize it.\n        regex = Regex.new(\"(*LIMIT_DEPTH=8192)^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$\")\n        pending! \"PCRE2 JIT mode not available.\" unless regex.@jit\n        str.matches?(regex)\n      {% end %}\n      # We don't care whether this actually matches or not, it's just to make\n      # sure the engine does not stack overflow with a large string.\n    end\n  end\n\n  describe \"#matches_at_byte_index?\" do\n    it \"positive index\" do\n      /foo/.matches_at_byte_index?(\"foo\", 0).should be_true\n      /foo/.matches_at_byte_index?(\"foo\", 1).should be_false\n      /foo/.matches_at_byte_index?(\".foo\", 1).should be_true\n      /foo/.matches_at_byte_index?(\"..foo\", 1).should be_true\n      /foo/.matches_at_byte_index?(\"foo\", 5).should be_false\n\n      /foo/.matches_at_byte_index?(\"bar\", 0).should be_false\n      /foo/.matches_at_byte_index?(\"bar\", 1).should be_false\n    end\n\n    it \"multibyte index\" do\n      expect_raises(ArgumentError, \"bad offset into UTF string\") do\n        /foo/.matches_at_byte_index?(\"öfoo\", 1)\n      end\n\n      if Regex::Engine.version_number >= {10, 36}\n        Regex.new(\"foo\", options: Regex::CompileOptions::MATCH_INVALID_UTF).matches_at_byte_index?(\"öfoo\", 1).should be_true\n      end\n      /foo/.matches_at_byte_index?(\"öfoo\", 2).should be_true\n    end\n\n    pending \"negative\" do\n      /foo/.matches_at_byte_index?(\"..foo\", -3).should be_true\n      /foo/.matches_at_byte_index?(\"..foo\", -2).should be_false\n    end\n\n    context \"with options\" do\n      it \"deprecated Regex::Options\" do\n        /foo/.matches_at_byte_index?(\"..foo\", 1, options: Regex::Options::ANCHORED).should be_false\n        /foo/.matches_at_byte_index?(\".foo\", 1, options: Regex::Options::ANCHORED).should be_true\n      end\n\n      it \"Regex::MatchOptions\" do\n        /foo/.matches_at_byte_index?(\"..foo\", 1, options: Regex::MatchOptions::ANCHORED).should be_false\n        /foo/.matches_at_byte_index?(\".foo\", 1, options: Regex::MatchOptions::ANCHORED).should be_true\n      end\n    end\n  end\n\n  describe \"#===\" do\n    it \"basic\" do\n      (/f(o+)(bar?)/ === \"fooba\").should be_true\n      (/f(o+)(bar?)/ === \"pooba\").should be_false\n    end\n\n    it \"assigns captures\" do\n      /f(o+)(bar?)/ === \"fooba\"\n      $~.group_size.should eq(2)\n      $1.should eq(\"oo\")\n      $2.should eq(\"ba\")\n\n      /f(o+)(bar?)/ === \"pooba\"\n      expect_raises(NilAssertionError) { $~ }\n    end\n  end\n\n  describe \"#=~\" do\n    it \"returns match index or nil\" do\n      (/foo/ =~ \"bar foo baz\").should eq(4)\n      (/foo/ =~ \"bar boo baz\").should be_nil\n    end\n\n    it \"assigns captures\" do\n      \"fooba\" =~ /f(o+)(bar?)/\n      $~.group_size.should eq(2)\n      $1.should eq(\"oo\")\n      $2.should eq(\"ba\")\n\n      /foo/ =~ \"bar boo baz\"\n      expect_raises(NilAssertionError) { $~ }\n    end\n\n    it \"accepts any type\" do\n      (/foo/ =~ nil).should be_nil\n      (/foo/ =~ 1).should be_nil\n      (/foo/ =~ [1, 2]).should be_nil\n      (/foo/ =~ true).should be_nil\n    end\n  end\n\n  describe \"#name_table\" do\n    it \"is a map of capture group number to name\" do\n      (/(?<date> (?<year>(\\d\\d)?\\d\\d) - (?<month>\\d\\d) - (?<day>\\d\\d) )/x).name_table.should eq({\n        1 => \"date\",\n        2 => \"year\",\n        4 => \"month\",\n        5 => \"day\",\n      })\n    end\n\n    it \"alphanumeric\" do\n      /(?<f1>)/.name_table.should eq({1 => \"f1\"})\n    end\n\n    it \"duplicate name\" do\n      /(?<foo>)(?<foo>)/.name_table.should eq({1 => \"foo\", 2 => \"foo\"})\n    end\n\n    it \"more than 255 groups\" do\n      regex = Regex.new(Array.new(1000) { |i| \"(?<c#{i}>.)\" }.join)\n      name_table = Array.new(1000) { |i| {i + 1, \"c#{i}\"} }.to_h\n      regex.name_table.should eq(name_table)\n    end\n  end\n\n  it \"#capture_count\" do\n    /(?:.)/x.capture_count.should eq(0)\n    /(?<foo>.+)/.capture_count.should eq(1)\n    /(.)?/x.capture_count.should eq(1)\n    /(.)|(.)/x.capture_count.should eq(2)\n  end\n\n  describe \"#inspect\" do\n    context \"with literal-compatible options\" do\n      it \"prints flags\" do\n        /foo/.inspect.should eq(\"/foo/\")\n        /foo/im.inspect.should eq(\"/foo/im\")\n        /foo/imx.inspect.should eq(\"/foo/imx\")\n      end\n\n      it \"escapes\" do\n        %r(/).inspect.should eq(\"/\\\\//\")\n        %r(\\/).inspect.should eq(\"/\\\\//\")\n      end\n    end\n\n    context \"with non-literal-compatible options\" do\n      it \"prints flags\" do\n        Regex.new(\"foo\", :anchored).inspect.should eq %(Regex.new(\"foo\", Regex::Options::ANCHORED))\n        Regex.new(\"foo\", :no_utf_check).inspect.should eq %(Regex.new(\"foo\", Regex::Options::NO_UTF8_CHECK))\n        Regex.new(\"foo\", Regex::CompileOptions[IGNORE_CASE, ANCHORED]).inspect.should eq %(Regex.new(\"foo\", Regex::Options[IGNORE_CASE, ANCHORED]))\n      end\n\n      it \"escapes\" do\n        Regex.new(%(\"), :anchored).inspect.should eq %(Regex.new(\"\\\\\"\", Regex::Options::ANCHORED))\n      end\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"with options\" do\n      /foo/.to_s.should eq(\"(?-imsx:foo)\")\n      /foo/im.to_s.should eq(\"(?ims-x:foo)\")\n      /foo/imx.to_s.should eq(\"(?imsx-:foo)\")\n    end\n\n    it \"with slash\" do\n      %r(/).to_s.should eq(\"(?-imsx:\\\\/)\")\n      %r(\\/).to_s.should eq(\"(?-imsx:\\\\/)\")\n    end\n\n    it \"interpolation\" do\n      regex = /(?<foo>R)/i\n      /(?<bar>C)#{regex}/.should eq /(?<bar>C)(?i-msx:(?<foo>R))/\n      /(?<bar>C)#{regex}/i.should eq /(?<bar>C)(?i-msx:(?<foo>R))/i\n    end\n  end\n\n  it \"#==\" do\n    regex = Regex.new(\"foo\", Regex::CompileOptions::IGNORE_CASE)\n    (regex == Regex.new(\"foo\", Regex::CompileOptions::IGNORE_CASE)).should be_true\n    (regex == Regex.new(\"foo\")).should be_false\n    (regex == Regex.new(\"bar\", Regex::CompileOptions::IGNORE_CASE)).should be_false\n    (regex == Regex.new(\"bar\")).should be_false\n  end\n\n  it \"#hash\" do\n    hash = Regex.new(\"foo\", Regex::CompileOptions::IGNORE_CASE).hash\n    hash.should eq(Regex.new(\"foo\", Regex::CompileOptions::IGNORE_CASE).hash)\n    hash.should_not eq(Regex.new(\"foo\").hash)\n    hash.should_not eq(Regex.new(\"bar\", Regex::CompileOptions::IGNORE_CASE).hash)\n    hash.should_not eq(Regex.new(\"bar\").hash)\n  end\n\n  it \"#dup\" do\n    regex = /foo/\n    regex.dup.should be(regex)\n  end\n\n  it \"#clone\" do\n    regex = /foo/\n    regex.clone.should be(regex)\n  end\n\n  describe \".needs_escape?\" do\n    it \"Char\" do\n      Regex.needs_escape?('*').should be_true\n      Regex.needs_escape?('|').should be_true\n      Regex.needs_escape?('@').should be_false\n    end\n\n    it \"String\" do\n      Regex.needs_escape?(\"10$\").should be_true\n      Regex.needs_escape?(\"foo\").should be_false\n    end\n  end\n\n  it \".escape\" do\n    Regex.escape(\" .\\\\+*?[^]$(){}=!<>|:-hello\").should eq(\"\\\\ \\\\.\\\\\\\\\\\\+\\\\*\\\\?\\\\[\\\\^\\\\]\\\\$\\\\(\\\\)\\\\{\\\\}\\\\=\\\\!\\\\<\\\\>\\\\|\\\\:\\\\-hello\")\n  end\n\n  describe \".union\" do\n    it \"constructs a Regex that matches things any of its arguments match\" do\n      re = Regex.union(/skiing/i, \"sledding\")\n      re.match!(\"Skiing\")[0].should eq \"Skiing\"\n      re.match!(\"sledding\")[0].should eq \"sledding\"\n    end\n\n    it \"returns a regular expression that will match passed arguments\" do\n      Regex.union(\"penzance\").should eq /penzance/\n      Regex.union(\"skiing\", \"sledding\").should eq /skiing|sledding/\n      Regex.union(/dogs/, /cats/i).should eq /(?-imsx:dogs)|(?i-msx:cats)/\n    end\n\n    it \"quotes any string arguments\" do\n      Regex.union(\"n\", \".\").should eq /n|\\./\n    end\n\n    it \"returns a Regex with an Array(String) with special characters\" do\n      Regex.union([\"+\", \"-\"]).should eq /\\+|\\-/\n    end\n\n    it \"accepts a single Array(String | Regex) argument\" do\n      Regex.union([\"skiing\", \"sledding\"]).should eq /skiing|sledding/\n      Regex.union([/dogs/, /cats/i]).should eq /(?-imsx:dogs)|(?i-msx:cats)/\n      (/dogs/ + /cats/i).should eq /(?-imsx:dogs)|(?i-msx:cats)/\n    end\n\n    it \"accepts a single Tuple(String | Regex) argument\" do\n      Regex.union({\"skiing\", \"sledding\"}).should eq /skiing|sledding/\n      Regex.union({/dogs/, /cats/i}).should eq /(?-imsx:dogs)|(?i-msx:cats)/\n      (/dogs/ + /cats/i).should eq /(?-imsx:dogs)|(?i-msx:cats)/\n    end\n\n    it \"combines Regex objects in the same way as Regex#+\" do\n      Regex.union(/skiing/i, /sledding/).should eq(/skiing/i + /sledding/)\n    end\n  end\n\n  it \"#+\" do\n    (/dogs/ + /cats/i).should eq /(?-imsx:dogs)|(?i-msx:cats)/\n  end\n\n  it \".error?\" do\n    Regex.error?(\"(foo|bar)\").should be_nil\n    Regex.error?(\"(foo|bar\").should eq(\n      if Regex::Engine.to_s == \"Regex::PCRE2\"\n        \"missing closing parenthesis at 8\"\n      else\n        \"missing ) at 8\"\n      end\n    )\n  end\n\n  it \".supports_compile_options?\" do\n    Regex.supports_compile_options?(:anchored).should be_true\n    Regex.supports_compile_options?(:endanchored).should eq Regex::Engine.version_number >= {10, 0}\n  end\n\n  it \".supports_match_options?\" do\n    Regex.supports_match_options?(:anchored).should be_true\n    Regex.supports_match_options?(:endanchored).should eq Regex::Engine.version_number >= {10, 0}\n  end\nend\n"
  },
  {
    "path": "spec/std/semantic_version_spec.cr",
    "content": "require \"spec\"\nrequire \"semantic_version\"\n\ndescribe SemanticVersion do\n  it \"compares <\" do\n    sversions = %w(\n      1.2.3-2\n      1.2.3-10\n      1.2.3-alpha\n      1.2.3-alpha.2\n      1.2.3-alpha.10\n      1.2.3-beta\n      1.2.3\n      1.2.4-alpha\n      1.2.4-beta\n      1.2.4\n    )\n    versions = sversions.map { |s| SemanticVersion.parse(s) }.to_a\n\n    versions.each_with_index do |v, i|\n      v.to_s.should eq(sversions[i])\n    end\n\n    versions.each_cons(2) do |pair|\n      pair[0].should be < pair[1]\n    end\n  end\n\n  it \"compares build equivalence\" do\n    sversions = %w(\n      1.2.3+1\n      1.2.3+999\n      1.2.3+a\n      1.2.3+a.b\n      1.2.3+a.b.c\n    )\n    versions = sversions.map { |s| SemanticVersion.parse(s) }.to_a\n\n    versions.each_with_index do |v, i|\n      v.to_s.should eq(sversions[i])\n    end\n\n    versions.each_cons(2) do |pair|\n      (pair[0] <=> pair[1]).should eq 0\n    end\n  end\n\n  it \"#valid?\" do\n    SemanticVersion.valid?(\"1.2.3\").should be_true\n    SemanticVersion.valid?(\"1.2.3-2\").should be_true\n    SemanticVersion.valid?(\"1.2.3-10\").should be_true\n    SemanticVersion.valid?(\"1.2.3-alpha\").should be_true\n    SemanticVersion.valid?(\"1.2.3-alpha.2\").should be_true\n    SemanticVersion.valid?(\"1.2.3-alpha.10\").should be_true\n\n    SemanticVersion.valid?(\"\").should be_false\n    SemanticVersion.valid?(\"1\").should be_false\n    SemanticVersion.valid?(\"foo\").should be_false\n  end\n\n  it \"#parse?\" do\n    SemanticVersion.parse?(\"1.2.3\").should eq SemanticVersion.new(1, 2, 3)\n    SemanticVersion.parse?(\"1.2.3-2\").should eq SemanticVersion.new(1, 2, 3, \"2\")\n    SemanticVersion.parse?(\"1.2.3-10\").should eq SemanticVersion.new(1, 2, 3, \"10\")\n    SemanticVersion.parse?(\"1.2.3-alpha\").should eq SemanticVersion.new(1, 2, 3, \"alpha\")\n    SemanticVersion.parse?(\"1.2.3-alpha.2\").should eq SemanticVersion.new(1, 2, 3, SemanticVersion::Prerelease.parse(\"alpha.2\"))\n    SemanticVersion.parse?(\"1.2.3-alpha.10\").should eq SemanticVersion.new(1, 2, 3, SemanticVersion::Prerelease.parse(\"alpha.10\"))\n\n    SemanticVersion.parse?(\"\").should be_nil\n    SemanticVersion.parse?(\"1\").should be_nil\n    SemanticVersion.parse?(\"foo\").should be_nil\n  end\n\n  it \"does not accept bad versions\" do\n    sversions = %w(\n      1\n      1.2\n      1.2.3-0123\n      1.2.3-0123.0123\n      0.0.4--.\n      1.1.2+.123\n      +invalid\n      -invalid\n      -invalid+invalid\n      -invalid.01\n      alpha\n      alpha.beta\n      alpha.beta.1\n      alpha.1\n      alpha+beta\n      alpha_beta\n      alpha.\n      alpha..\n      1.0.0-alpha_beta\n      -alpha.\n      1.0.0-alpha..\n      1.0.0-alpha..1\n      1.0.0-alpha...1\n      01.1.1\n      1.01.1\n      1.1.01\n      1.2.3.DEV\n      1.2-SNAPSHOT\n      1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788\n      1.2-RC-SNAPSHOT\n      -1.0.3-gamma+b7718\n      +justmeta\n      9.8.7+meta+meta\n      9.8.7-whatever+meta+meta\n      99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12\n    )\n    sversions.each do |s|\n      SemanticVersion.valid?(s).should be_false\n      SemanticVersion.parse?(s).should be_nil\n      expect_raises(ArgumentError) { SemanticVersion.parse(s) }\n    end\n  end\n\n  it \"copies with specified modifications\" do\n    base_version = SemanticVersion.new(1, 2, 3, \"rc\", \"0000\")\n    base_version.copy_with(major: 0).should eq SemanticVersion.new(0, 2, 3, \"rc\", \"0000\")\n    base_version.copy_with(minor: 0).should eq SemanticVersion.new(1, 0, 3, \"rc\", \"0000\")\n    base_version.copy_with(patch: 0).should eq SemanticVersion.new(1, 2, 0, \"rc\", \"0000\")\n    base_version.copy_with(prerelease: \"alpha\").should eq SemanticVersion.new(1, 2, 3, \"alpha\", \"0000\")\n    base_version.copy_with(build: \"0001\").should eq SemanticVersion.new(1, 2, 3, \"rc\", \"0001\")\n    base_version.copy_with(prerelease: nil, build: nil).should eq SemanticVersion.new(1, 2, 3)\n  end\n\n  it \"bumps to the correct version\" do\n    SemanticVersion.new(1, 1, 1).bump_minor.should eq SemanticVersion.new(1, 2, 0)\n    SemanticVersion.new(1, 2, 0).bump_patch.should eq SemanticVersion.new(1, 2, 1)\n    SemanticVersion.new(1, 2, 1).bump_major.should eq SemanticVersion.new(2, 0, 0)\n    SemanticVersion.new(2, 0, 0).bump_patch.should eq SemanticVersion.new(2, 0, 1)\n    SemanticVersion.new(2, 0, 1).bump_minor.should eq SemanticVersion.new(2, 1, 0)\n    SemanticVersion.new(2, 1, 0).bump_major.should eq SemanticVersion.new(3, 0, 0)\n\n    version_with_prerelease = SemanticVersion.new(1, 2, 3, \"rc\", \"0001\")\n    version_with_prerelease.bump_major.should eq SemanticVersion.new(2, 0, 0)\n    version_with_prerelease.bump_minor.should eq SemanticVersion.new(1, 3, 0)\n    version_with_prerelease.bump_patch.should eq SemanticVersion.new(1, 2, 3)\n  end\n\n  describe SemanticVersion::Prerelease do\n    it \"compares <\" do\n      sprereleases = %w(\n        alpha.1\n        beta.1\n        beta.2\n      )\n      prereleases = sprereleases.map { |s|\n        SemanticVersion::Prerelease.parse(s)\n      }\n\n      prereleases.each_cons(2) do |pair|\n        pair[0].should be < pair[1]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/set_spec.cr",
    "content": "require \"spec\"\nrequire \"set\"\nrequire \"spec/helpers/iterate\"\n\ndescribe \"Set\" do\n  describe \"an empty set\" do\n    it \"is empty\" do\n      Set(Nil).new.empty?.should be_true\n    end\n\n    it \"has size 0\" do\n      Set(Nil).new.size.should eq(0)\n    end\n  end\n\n  describe \"new\" do\n    it \"creates new set with enumerable without block\" do\n      set_from_array = Set.new([2, 4, 6, 4])\n      set_from_array.size.should eq(3)\n      set_from_array.to_a.sort.should eq([2, 4, 6])\n\n      set_from_tuple = Set.new({1, \"hello\", 'x'})\n      set_from_tuple.size.should eq(3)\n      set_from_tuple.to_a.should contain(1)\n      set_from_tuple.to_a.should contain(\"hello\")\n      set_from_tuple.to_a.should contain('x')\n    end\n  end\n\n  describe \"add\" do\n    it \"adds and includes\" do\n      set = Set(Int32).new\n      set.add 1\n      set.includes?(1).should be_true\n      set.size.should eq(1)\n    end\n\n    it \"returns self\" do\n      set = Set(Int32).new\n      set.add(1).should be(set)\n    end\n  end\n\n  describe \"add?\" do\n    it \"returns true when object is not in the set\" do\n      set = Set(Int32).new\n      set.add?(1).should be_true\n    end\n\n    it \"returns false when object is in the set\" do\n      set = Set(Int32).new\n      set.add?(1).should be_true\n      set.should contain(1)\n      set.add?(1).should be_false\n    end\n  end\n\n  describe \"delete\" do\n    it \"deletes an object\" do\n      set = Set{1, 2, 3}\n      set.delete 2\n      set.size.should eq(2)\n      set.should contain(1)\n      set.should contain(3)\n    end\n\n    it \"returns true when the object was present\" do\n      set = Set{1, 2, 3}\n      set.delete(2).should be_true\n    end\n\n    it \"returns false when the object was absent\" do\n      set = Set{1, 2, 3}\n      set.delete(0).should be_false\n    end\n  end\n\n  describe \"dup\" do\n    it \"creates a dup\" do\n      set1 = Set{[1, 2]}\n      set2 = set1.dup\n\n      set1.should eq(set2)\n      set1.should_not be(set2)\n\n      set1.to_a.first.should be(set2.to_a.first)\n\n      set1 << [3]\n      set2 << [4]\n\n      set2.should eq(Set{[1, 2], [4]})\n    end\n  end\n\n  describe \"clone\" do\n    it \"creates a clone\" do\n      set1 = Set{[1, 2]}\n      set2 = set1.clone\n\n      set1.should eq(set2)\n      set1.should_not be(set2)\n\n      set1.to_a.first.should_not be(set2.to_a.first)\n\n      set1 << [3]\n      set2 << [4]\n\n      set2.should eq(Set{[1, 2], [4]})\n    end\n  end\n\n  describe \"==\" do\n    it \"compares two sets\" do\n      set1 = Set{1, 2, 3}\n      set2 = Set{1, 2, 3}\n      set3 = Set{1, 2, 3, 4}\n\n      set1.should eq(set1)\n      set1.should eq(set2)\n      set1.should_not eq(set3)\n    end\n  end\n\n  describe \"concat\" do\n    it \"adds all the other elements\" do\n      set = Set{1, 4, 8}\n      set.concat [1, 9, 10]\n      set.should eq(Set{1, 4, 8, 9, 10})\n    end\n\n    it \"returns self\" do\n      set = Set{1, 4, 8}\n      set.concat([1, 9, 10]).should be(set)\n    end\n  end\n\n  it \"does &\" do\n    set1 = Set{1, 2, 3}\n    set2 = Set{4, 2, 5, 3}\n    set3 = set1 & set2\n    set3.should eq(Set{2, 3})\n  end\n\n  it \"does |\" do\n    set1 = Set{1, 2, 3}\n    set2 = Set{4, 2, 5, \"3\"}\n    set3 = set1 | set2\n    set3.should eq(Set{1, 2, 3, 4, 5, \"3\"})\n  end\n\n  it \"aliases + to |\" do\n    set1 = Set{1, 1, 2, 3}\n    set2 = Set{3, 4, 5}\n    set3 = set1 + set2\n    set4 = set1 | set2\n    set3.should eq(set4)\n  end\n\n  it \"does -\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = Set{2, 4, 6}\n    set3 = set1 - set2\n    set3.should eq(Set{1, 3, 5})\n  end\n\n  it \"does -\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = Set{2, 4, 'a'}\n    set3 = set1 - set2\n    set3.should eq(Set{1, 3, 5})\n  end\n\n  it \"does -\" do\n    set1 = Set{1, 2, 3, 4, 'b'}\n    set2 = Set{2, 4, 5}\n    set3 = set1 - set2\n    set3.should eq(Set{1, 3, 'b'})\n  end\n\n  it \"does -\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = [2, 4, 6]\n    set3 = set1 - set2\n    set3.should eq(Set{1, 3, 5})\n  end\n\n  it \"does -\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = [2, 4, 'a']\n    set3 = set1 - set2\n    set3.should eq(Set{1, 3, 5})\n  end\n\n  it \"does -\" do\n    set1 = Set{1, 2, 3, 4, 'b'}\n    set2 = [2, 4, 5]\n    set3 = set1 - set2\n    set3.should eq(Set{1, 3, 'b'})\n  end\n\n  it \"does ^\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = Set{2, 4, 6}\n    set3 = set1 ^ set2\n    set3.should eq(Set{1, 3, 5, 6})\n  end\n\n  it \"does ^\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = Set{2, 4, 'a'}\n    set3 = set1 ^ set2\n    set3.should eq(Set{1, 3, 5, 'a'})\n  end\n\n  it \"does ^\" do\n    set1 = Set{1, 2, 3, 4, 'b'}\n    set2 = Set{2, 4, 5}\n    set3 = set1 ^ set2\n    set3.should eq(Set{1, 3, 5, 'b'})\n  end\n\n  it \"does ^\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = [2, 4, 6]\n    set3 = set1 ^ set2\n    set3.should eq(Set{1, 3, 5, 6})\n  end\n\n  it \"does ^\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = [2, 4, 'a']\n    set3 = set1 ^ set2\n    set3.should eq(Set{1, 3, 5, 'a'})\n  end\n\n  it \"does ^\" do\n    set1 = Set{1, 2, 3, 4, 'b'}\n    set2 = [2, 4, 5]\n    set3 = set1 ^ set2\n    set3.should eq(Set{1, 3, 5, 'b'})\n  end\n\n  it \"does subtract\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = Set{2, 4, 6}\n    set1.subtract set2\n    set1.should eq(Set{1, 3, 5})\n  end\n\n  it \"does subtract\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = Set{2, 4, 'a'}\n    set1.subtract set2\n    set1.should eq(Set{1, 3, 5})\n  end\n\n  it \"does subtract\" do\n    set1 = Set{1, 2, 3, 4, 'b'}\n    set2 = Set{2, 4, 5}\n    set1.subtract set2\n    set1.should eq(Set{1, 3, 'b'})\n  end\n\n  it \"does subtract\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = [2, 4, 6]\n    set1.subtract set2\n    set1.should eq(Set{1, 3, 5})\n  end\n\n  it \"does subtract\" do\n    set1 = Set{1, 2, 3, 4, 5}\n    set2 = [2, 4, 'a']\n    set1.subtract set2\n    set1.should eq(Set{1, 3, 5})\n  end\n\n  it \"does subtract\" do\n    set1 = Set{1, 2, 3, 4, 'b'}\n    set2 = [2, 4, 5]\n    set1.subtract set2\n    set1.should eq(Set{1, 3, 'b'})\n  end\n\n  it \"does to_a\" do\n    Set{1, 2, 3}.to_a.should eq([1, 2, 3])\n  end\n\n  it \"does support giving a block to to_a\" do\n    Set{1, 2, 3}.to_a { |x| x + 1 }.should eq([2, 3, 4])\n  end\n\n  it \"does to_s\" do\n    Set{1, 2, 3}.to_s.should eq(\"Set{1, 2, 3}\")\n    Set{\"foo\"}.to_s.should eq(%(Set{\"foo\"}))\n  end\n\n  it \"does clear\" do\n    x = Set{1, 2, 3}\n    x.to_a.should eq([1, 2, 3])\n    x.clear.should be(x)\n    x << 1\n    x.to_a.should eq([1])\n  end\n\n  it \"checks intersects\" do\n    set = Set{3, 4, 5}\n    empty_set = Set(Int32).new\n\n    set.intersects?(set).should be_true\n    set.intersects?(Set{2, 4}).should be_true\n    set.intersects?(Set{5, 6, 7}).should be_true\n    set.intersects?(Set{1, 2, 6, 8, 4}).should be_true\n\n    set.intersects?(empty_set).should be_false\n    set.intersects?(Set{0, 2}).should be_false\n    set.intersects?(Set{0, 2, 6}).should be_false\n    set.intersects?(Set{0, 2, 6, 8, 10}).should be_false\n\n    # Make sure set hasn't changed\n    set.should eq(Set{3, 4, 5})\n  end\n\n  it \"compares hashes of sets\" do\n    h1 = {Set{1, 2, 3} => 1}\n    h2 = {Set{1, 2, 3} => 1}\n    h1.should eq(h2)\n  end\n\n  it \"does each\" do\n    set = Set{1, 2, 3}\n    i = 1\n    set.each do |v|\n      v.should eq(i)\n      i += 1\n    end.should be_nil\n    i.should eq(4)\n  end\n\n  it_iterates \"#each\", [1, 2, 3], Set{1, 2, 3}.each\n\n  it \"#subset_of?\" do\n    set = Set{1, 2, 3}\n    empty_set = Set(Int32).new\n\n    set.subset_of?(Set{1, 2, 3, 4}).should be_true\n    set.subset_of?(Set{1, 2, 3, \"4\"}).should be_true\n    set.subset_of?(Set{1, 2, 3}).should be_true\n    set.subset_of?(Set{1, 2}).should be_false\n    set.subset_of?(empty_set).should be_false\n\n    empty_set.subset_of?(Set{1}).should be_true\n    empty_set.subset_of?(empty_set).should be_true\n  end\n\n  it \"#proper_subset_of?\" do\n    set = Set{1, 2, 3}\n    empty_set = Set(Int32).new\n\n    set.proper_subset_of?(Set{1, 2, 3, 4}).should be_true\n    set.proper_subset_of?(Set{1, 2, 3, \"4\"}).should be_true\n    set.proper_subset_of?(Set{1, 2, 3}).should be_false\n    set.proper_subset_of?(Set{1, 2}).should be_false\n    set.proper_subset_of?(empty_set).should be_false\n\n    empty_set.proper_subset_of?(Set{1}).should be_true\n    empty_set.proper_subset_of?(empty_set).should be_false\n  end\n\n  it \"#superset_of?\" do\n    set = Set{1, 2, \"3\"}\n    empty_set = Set(Int32).new\n\n    set.superset_of?(empty_set).should be_true\n    set.superset_of?(Set{1, 2}).should be_true\n    set.superset_of?(Set{1, 2, \"3\"}).should be_true\n    set.superset_of?(Set{1, 2, 3}).should be_false\n    set.superset_of?(Set{1, 2, 3, 4}).should be_false\n    set.superset_of?(Set{1, 4}).should be_false\n\n    empty_set.superset_of?(empty_set).should be_true\n  end\n\n  it \"#proper_superset_of?\" do\n    set = Set{1, 2, \"3\"}\n    empty_set = Set(Int32).new\n\n    set.proper_superset_of?(empty_set).should be_true\n    set.proper_superset_of?(Set{1, 2}).should be_true\n    set.proper_superset_of?(Set{1, 2, \"3\"}).should be_false\n    set.proper_superset_of?(Set{1, 2, 3}).should be_false\n    set.proper_superset_of?(Set{1, 2, 3, 4}).should be_false\n    set.proper_superset_of?(Set{1, 4}).should be_false\n\n    empty_set.proper_superset_of?(empty_set).should be_false\n  end\n\n  it \"has object_id\" do\n    Set(Int32).new.object_id.should be > 0\n  end\n\n  typeof(Set(Int32).new(initial_capacity: 1234))\n\n  describe \"compare_by_identity\" do\n    it \"compares by identity\" do\n      string = \"foo\"\n      set = Set{string, \"bar\", \"baz\"}\n      set.compare_by_identity?.should be_false\n      set.should contain(string)\n\n      set.compare_by_identity\n      set.compare_by_identity?.should be_true\n\n      set.should_not contain(\"fo\" + \"o\")\n      set.should contain(string)\n    end\n\n    it \"retains compare_by_identity on dup\" do\n      set = Set(String).new.compare_by_identity\n      set.dup.compare_by_identity?.should be_true\n    end\n\n    it \"retains compare_by_identity on clone\" do\n      set = Set(String).new.compare_by_identity\n      set.clone.compare_by_identity?.should be_true\n    end\n  end\n\n  describe \"#map!\" do\n    it \"replaces elements with the block's return values\" do\n      set = Set{1, 2, 3}\n      set.map! { |n| n * -1 }.should be(set)\n      set.should eq(Set{-1, -2, -3})\n    end\n\n    it \"exhibits reference semantic\" do\n      set = Set{1, 2, 3}\n      copy = set\n\n      set.map! { |n| n * -1 }\n      set.should eq(copy)\n    end\n  end\n\n  describe \"#select!\" do\n    it \"keeps only elements that evaluate to true\" do\n      set = Set{1, 2, 3}\n      set.select! { |n| n < 2 }.should be(set)\n      set.should eq(Set{1})\n    end\n  end\n\n  describe \"#reject!\" do\n    it \"returns self if changes were made\" do\n      set = Set{1, 2, 3}\n      set.reject! { |n| n < 2 }.should be(set)\n      set.should eq(Set{2, 3})\n    end\n  end\n\n  describe \"#rehash\" do\n    it \"rehashes\" do\n      a = [1]\n      s = Set{a}\n      (10..100).each do |i|\n        s << [i]\n      end\n      a << 2\n      s.should_not contain(a)\n      s.rehash\n      s.should contain(a)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/signal_spec.cr",
    "content": "{% skip_file if flag?(:wasm32) %}\n\nrequire \"./spec_helper\"\nrequire \"signal\"\n\n{% skip_file if flag?(:interpreted) && !Crystal::Interpreter.class.has_method?(:signal) %}\n\ndescribe \"Signal\" do\n  typeof(Signal::ABRT.reset)\n  typeof(Signal::ABRT.ignore)\n  typeof(Signal::ABRT.trap { 1 })\n\n  it \"has constants required by C\" do\n    Signal::INT.should be_a(Signal)\n    Signal::ILL.should be_a(Signal)\n    Signal::FPE.should be_a(Signal)\n    Signal::SEGV.should be_a(Signal)\n    Signal::TERM.should be_a(Signal)\n    Signal::ABRT.should be_a(Signal)\n  end\n\n  {% if flag?(:dragonfly) %}\n    # FIXME: can't use SIGUSR1/SIGUSR2 because Boehm uses them + no\n    # SIRTMIN/SIGRTMAX support => figure which signals we could use\n    pending \"runs a signal handler\"\n    pending \"ignores a signal\"\n    pending \"allows chaining of signals\"\n    pending \"CHLD.reset sets default Crystal child handler\"\n    pending \"CHLD.ignore sets default Crystal child handler\"\n    pending \"CHLD.trap is called after default Crystal child handler\"\n    pending \"CHLD.reset removes previously set trap\"\n  {% end %}\n\n  {% unless flag?(:win32) || flag?(:dragonfly) %}\n    # can't use SIGUSR1/SIGUSR2 on FreeBSD because Boehm uses them to suspend/resume threads\n    signal1 = {% if flag?(:freebsd) %} Signal.new(LibC::SIGRTMAX - 1) {% else %} Signal::USR1 {% end %}\n    signal2 = {% if flag?(:freebsd) %} Signal.new(LibC::SIGRTMAX - 2) {% else %} Signal::USR2 {% end %}\n\n    it \"runs a signal handler\" do\n      ran = false\n      signal1.trap do\n        ran = true\n      end\n      Process.signal signal1, Process.pid\n      10.times do |i|\n        break if ran\n        sleep 0.1.seconds\n      end\n      ran.should be_true\n    ensure\n      signal1.reset\n    end\n\n    it \"ignores a signal\" do\n      signal2.ignore\n      Process.signal signal2, Process.pid\n    end\n\n    it \"allows chaining of signals\" do\n      ran_first = false\n      ran_second = false\n\n      signal1.trap { ran_first = true }\n      existing = signal1.trap_handler?\n\n      signal1.trap do |signal|\n        existing.try &.call(signal)\n        ran_second = true\n      end\n\n      Process.signal signal1, Process.pid\n      sleep 0.1.seconds\n      ran_first.should be_true\n      ran_second.should be_true\n    ensure\n      signal1.reset\n    end\n\n    it \"CHLD.reset sets default Crystal child handler\" do\n      Signal::CHLD.reset\n      child = Process.new(\"true\", shell: true)\n      child.wait # doesn't block forever\n    end\n\n    it \"CHLD.ignore sets default Crystal child handler\" do\n      Signal::CHLD.ignore\n      child = Process.new(\"true\", shell: true)\n      child.wait # doesn't block forever\n    end\n\n    it \"CHLD.trap is called after default Crystal child handler\" do\n      chan = Channel(Process).new\n      existed = Channel(Bool).new\n\n      Signal::CHLD.trap do\n        child_process = chan.receive\n        existed.send(Process.exists?(child_process.pid))\n      end\n\n      child = Process.new(\"true\", shell: true)\n      child.wait # doesn't block forever\n      chan.send(child)\n      existed.receive.should be_false\n    ensure\n      Signal::CHLD.reset\n    end\n\n    it \"CHLD.reset removes previously set trap\" do\n      call_count = 0\n\n      Signal::CHLD.trap do\n        call_count += 1\n      end\n\n      Process.new(\"true\", shell: true).wait\n      Fiber.yield\n\n      call_count.should eq(1)\n\n      Signal::CHLD.reset\n\n      Process.new(\"true\", shell: true).wait\n      Fiber.yield\n\n      call_count.should eq(1)\n    end\n\n    # TODO: test Signal::X.reset\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/slice_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\nrequire \"spec/helpers/string\"\n\nprivate class BadSortingClass\n  include Comparable(self)\n\n  def <=>(other)\n    1\n  end\nend\n\nprivate class Spaceship\n  getter value : Float64\n\n  def initialize(@value : Float64, @return_nil = false)\n  end\n\n  def <=>(other : Spaceship)\n    return nil if @return_nil\n\n    value <=> other.value\n  end\nend\n\nprivate def is_stable_sort(mutable, &block)\n  n = 42\n  # [Spaceship.new(0), ..., Spaceship.new(n - 1), Spaceship.new(0), ..., Spaceship.new(n - 1)]\n  slice = Slice.new(n * 2) { |i| Spaceship.new((i % n).to_f) }\n  # [Spaceship.new(0), Spaceship.new(0), ..., Spaceship.new(n - 1), Spaceship.new(n - 1)]\n  expected = Slice.new(n * 2) { |i| slice[i % 2 * n + i // 2] }\n\n  if mutable\n    yield slice\n    result = slice\n  else\n    result = yield slice\n    result.should_not eq(slice)\n  end\n\n  result.size.should eq(expected.size)\n  expected.zip(result) do |exp, res|\n    res.should be(exp) # reference-equality is necessary to check sorting is stable.\n  end\nend\n\ndescribe \"Slice\" do\n  describe \".new\" do\n    describe \"initializes with default value\" do\n      it \"integer\" do\n        Slice(Int32).new(3, 0).should eq Slice[0, 0, 0]\n        Slice(Int32).new(3, 1).should eq Slice[1, 1, 1]\n      end\n\n      it \"float\" do\n        Slice(Float32).new(3, 0.0).should eq Slice[0.0, 0.0, 0.0]\n        Slice(Float32).new(3, 1.0).should eq Slice[1.0, 1.0, 1.0]\n      end\n\n      it \"pointer\" do\n        null = Pointer(Void).null\n        Slice(Pointer(Void)).new(3, null).should eq Slice[null, null, null]\n\n        value = Pointer(Void).new(0x123_u64)\n        Slice(Pointer(Void)).new(3, value).should eq Slice[value, value, value]\n      end\n    end\n  end\n\n  it \"gets pointer and size\" do\n    pointer = Pointer.malloc(1, 0)\n    slice = Slice.new(pointer, 1)\n    slice.to_unsafe.should eq(pointer)\n    slice.size.should eq(1)\n  end\n\n  it \"does []?\" do\n    slice = Slice.new(3) { |i| i + 1 }\n    3.times do |i|\n      slice[i]?.should eq(i + 1)\n    end\n    slice[-1]?.should eq(3)\n    slice[-2]?.should eq(2)\n    slice[-3]?.should eq(1)\n\n    slice[-4]?.should be_nil\n    slice[3]?.should be_nil\n  end\n\n  it \"does []\" do\n    slice = Slice.new(3) { |i| i + 1 }\n    3.times do |i|\n      slice[i].should eq(i + 1)\n    end\n    slice[-1].should eq(3)\n    slice[-2].should eq(2)\n    slice[-3].should eq(1)\n\n    expect_raises(IndexError) { slice[-4] }\n    expect_raises(IndexError) { slice[3] }\n  end\n\n  it \"does []=\" do\n    slice = Slice.new(3, 0)\n    slice[0] = 1\n    slice[0].should eq(1)\n\n    expect_raises(IndexError) { slice[-4] = 1 }\n    expect_raises(IndexError) { slice[3] = 1 }\n  end\n\n  it \"#+(Int)\" do\n    slice = Slice.new(3) { |i| i + 1 }\n\n    slice1 = slice + 1\n    slice1.size.should eq(2)\n    slice1[0].should eq(2)\n    slice1[1].should eq(3)\n\n    slice3 = slice + 3\n    slice3.size.should eq(0)\n\n    expect_raises(IndexError) { slice + 4 }\n    expect_raises(IndexError) { slice + (-1) }\n  end\n\n  it \"does []? with start and count\" do\n    slice = Slice.new(4) { |i| i + 1 }\n\n    slice1 = slice[1, 2]?\n    slice1.should_not be_nil\n    slice1 = slice1.not_nil!\n    slice1.size.should eq(2)\n    slice1.to_unsafe.should eq(slice.to_unsafe + 1)\n    slice1[0].should eq(2)\n    slice1[1].should eq(3)\n\n    slice2 = slice[-1, 1]?\n    slice2.should_not be_nil\n    slice2 = slice2.not_nil!\n    slice2.size.should eq(1)\n    slice2.to_unsafe.should eq(slice.to_unsafe + 3)\n\n    slice[3, 2]?.should be_nil\n    slice[0, 5]?.should be_nil\n    expect_raises(ArgumentError, \"Negative count: -1\") { slice[3, -1]? }\n  end\n\n  it \"does []? with range\" do\n    slice = Slice.new(4) { |i| i + 1 }\n\n    slice1 = slice[1..2]?\n    slice1.should_not be_nil\n    slice1 = slice1.not_nil!\n    slice1.size.should eq(2)\n    slice1.to_unsafe.should eq(slice.to_unsafe + 1)\n    slice1[0].should eq(2)\n    slice1[1].should eq(3)\n\n    slice[4..7]?.should be_nil\n    slice[3..4]?.should be_nil\n    slice[-2..4]?.should be_nil\n    slice[-6..-5]?.should be_nil\n  end\n\n  it \"does [] with start and count\" do\n    slice = Slice.new(4) { |i| i + 1 }\n\n    slice1 = slice[1, 2]\n    slice1.size.should eq(2)\n    slice1.to_unsafe.should eq(slice.to_unsafe + 1)\n    slice1[0].should eq(2)\n    slice1[1].should eq(3)\n\n    slice2 = slice[-1, 1]\n    slice2.size.should eq(1)\n    slice2.to_unsafe.should eq(slice.to_unsafe + 3)\n\n    expect_raises(IndexError) { slice[3, 2] }\n    expect_raises(IndexError) { slice[0, 5] }\n    expect_raises(ArgumentError, \"Negative count: -1\") { slice[3, -1] }\n  end\n\n  it \"does empty?\" do\n    Slice.new(0, 0).empty?.should be_true\n    Slice.new(1, 0).empty?.should be_false\n  end\n\n  it \"raises if size is negative on new\" do\n    expect_raises(ArgumentError) { Slice.new(-1, 0) }\n  end\n\n  it \"does to_s\" do\n    slice = Slice.new(4) { |i| i + 1 }\n    slice.to_s.should eq(\"Slice[1, 2, 3, 4]\")\n  end\n\n  it \"does to_s for bytes\" do\n    slice = Bytes[1, 2, 3]\n    slice.to_s.should eq(\"Bytes[1, 2, 3]\")\n  end\n\n  describe \"#fill\" do\n    it \"replaces values in a subrange\" do\n      slice = Slice[0, 1, 2, 3, 4]\n      slice.fill(7)\n      slice.should eq(Slice[7, 7, 7, 7, 7])\n\n      slice = Slice[0, 1, 2, 3, 4]\n      slice.fill(7, 1, 2)\n      slice.should eq(Slice[0, 7, 7, 3, 4])\n      slice.fill(8, 4, 10)\n      slice.should eq(Slice[0, 7, 7, 3, 8])\n\n      slice = Slice[0, 1, 2, 3, 4]\n      slice.fill(7, 2..3)\n      slice.should eq(Slice[0, 1, 7, 7, 4])\n      slice.fill(8, -2..10)\n      slice.should eq(Slice[0, 1, 7, 8, 8])\n\n      slice = Slice[0, 0, 0, 0, 0]\n      slice.fill { |i| i + 7 }\n      slice.should eq(Slice[7, 8, 9, 10, 11])\n\n      slice = Slice[0, 0, 0, 0, 0]\n      slice.fill(offset: 2) { |i| i * i }\n      slice.should eq(Slice[4, 9, 16, 25, 36])\n\n      slice = Slice[0, 0, 0, 0, 0]\n      slice.fill(1, 2) { |i| i + 7 }\n      slice.should eq(Slice[0, 8, 9, 0, 0])\n\n      slice = Slice[0, 0, 0, 0, 0]\n      slice.fill(2..3) { |i| i + 7 }\n      slice.should eq(Slice[0, 0, 9, 10, 0])\n      slice.fill(-2..10, &.itself)\n      slice.should eq(Slice[0, 0, 9, 3, 4])\n    end\n\n    it \"works for bytes\" do\n      slice = Bytes[0, 1, 2, 3, 4]\n      slice.fill(7)\n      slice.should eq(Bytes[7, 7, 7, 7, 7])\n\n      slice = Bytes[0, 1, 2, 3, 4]\n      slice.fill(7, 1, 2)\n      slice.should eq(Bytes[0, 7, 7, 3, 4])\n\n      slice = Bytes[0, 1, 2, 3, 4]\n      slice.fill(7, 2..3)\n      slice.should eq(Bytes[0, 1, 7, 7, 4])\n    end\n  end\n\n  it \"does copy_from pointer\" do\n    pointer = Pointer.malloc(4) { |i| i + 1 }\n    slice = Slice.new(4, 0)\n    slice.copy_from(pointer, 4)\n    4.times { |i| slice[i].should eq(i + 1) }\n\n    expect_raises(IndexError) { slice.copy_from(pointer, 5) }\n  end\n\n  it \"does copy_to pointer\" do\n    pointer = Pointer.malloc(4, 0)\n    slice = Slice.new(4) { |i| i + 1 }\n    slice.copy_to(pointer, 4)\n    4.times { |i| pointer[i].should eq(i + 1) }\n\n    expect_raises(IndexError) { slice.copy_to(pointer, 5) }\n  end\n\n  describe \".copy_to(Slice)\" do\n    it \"copies bytes\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      src.copy_to(dst)\n      dst.should eq(src)\n    end\n\n    it \"raises if dst is smaller\" do\n      src = Slice.new(8) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      expect_raises(IndexError) { src.copy_to(dst) }\n    end\n\n    it \"copies at most src.size\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(8) { 'b' }\n\n      src.copy_to(dst)\n      dst.should eq(Slice['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'])\n    end\n  end\n\n  describe \".copy_from(Slice)\" do\n    it \"copies bytes\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      dst.copy_from(src)\n      dst.should eq(src)\n    end\n\n    it \"raises if dst is smaller\" do\n      src = Slice.new(8) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      expect_raises(IndexError) { dst.copy_from(src) }\n    end\n\n    it \"copies at most src.size\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(8) { 'b' }\n\n      dst.copy_from(src)\n      dst.should eq(Slice['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'])\n    end\n  end\n\n  describe \".move_to(Slice)\" do\n    it \"moves bytes\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      src.move_to(dst)\n      dst.should eq(src)\n    end\n\n    it \"raises if dst is smaller\" do\n      src = Slice.new(8) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      expect_raises(IndexError) { src.move_to(dst) }\n    end\n\n    it \"moves most src.size\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(8) { 'b' }\n\n      src.move_to(dst)\n      dst.should eq(Slice['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'])\n    end\n\n    it \"handles intersecting ranges\" do\n      # Test with ranges offset by 0 to 8 bytes\n      (0..8).each do |offset|\n        buf = Slice.new(16) { |i| 'a' + i }\n        dst = buf[0, 8]\n        src = buf[offset, 8]\n\n        src.move_to(dst)\n\n        result = (0..7).map { |i| 'a' + i + offset }\n        dst.should eq(Slice.new(result.to_unsafe, result.size))\n      end\n    end\n  end\n\n  describe \".move_from(Slice)\" do\n    it \"moves bytes\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      dst.move_from(src)\n      dst.should eq(src)\n    end\n\n    it \"raises if dst is smaller\" do\n      src = Slice.new(8) { 'a' }\n      dst = Slice.new(4) { 'b' }\n\n      expect_raises(IndexError) { dst.move_from(src) }\n    end\n\n    it \"moves at most src.size\" do\n      src = Slice.new(4) { 'a' }\n      dst = Slice.new(8) { 'b' }\n\n      dst.move_from(src)\n      dst.should eq(Slice['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'])\n    end\n\n    it \"handles intersecting ranges\" do\n      # Test with ranges offset by 0 to 8 bytes\n      (0..8).each do |offset|\n        buf = Slice.new(16) { |i| 'a' + i }\n        dst = buf[0, 8]\n        src = buf[offset, 8]\n\n        dst.move_from(src)\n\n        result = (0..7).map { |i| 'a' + i + offset }\n        dst.should eq(Slice.new(result.to_unsafe, result.size))\n      end\n    end\n  end\n\n  describe \"#unsafe_slice_of\" do\n    it \"reinterprets a slice's elements\" do\n      slice = Bytes.new(10) { |i| i.to_u8 + 1 }\n\n      {% if IO::ByteFormat::SystemEndian == IO::ByteFormat::LittleEndian %}\n        slice.unsafe_slice_of(Int16).should eq(Int16.slice(0x0201, 0x0403, 0x0605, 0x0807, 0x0A09))\n        slice.unsafe_slice_of(Int32).should eq(Int32.slice(0x04030201, 0x08070605))\n\n        slice.unsafe_slice_of(UInt64)[0] = 0x1122_3344_5566_7788_u64\n        slice.should eq(Bytes[0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x09, 0x0A])\n      {% else %}\n        slice.unsafe_slice_of(Int16).should eq(Int16.slice(0x0102, 0x0304, 0x0506, 0x0708, 0x090A))\n        slice.unsafe_slice_of(Int32).should eq(Int32.slice(0x01020304, 0x05060708))\n\n        slice.unsafe_slice_of(UInt64)[0] = 0x1122_3344_5566_7788_u64\n        slice.should eq(Bytes[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x09, 0x0A])\n      {% end %}\n    end\n  end\n\n  describe \"#to_unsafe_bytes\" do\n    it \"reinterprets a slice's elements as bytes\" do\n      slice = Slice[0x01020304, -0x01020304]\n      bytes = slice.to_unsafe_bytes\n\n      {% if IO::ByteFormat::SystemEndian == IO::ByteFormat::LittleEndian %}\n        bytes.should eq(Bytes[0x04, 0x03, 0x02, 0x01, 0xFC, 0xFC, 0xFD, 0xFE])\n        bytes[3] = 0x55\n        slice[0].should eq(0x55020304)\n      {% else %}\n        bytes.should eq(Bytes[0x01, 0x02, 0x03, 0x04, 0xFE, 0xFD, 0xFC, 0xFC])\n        bytes[3] = 0x55\n        slice[0].should eq(0x01020355)\n      {% end %}\n    end\n  end\n\n  describe \"#hexstring\" do\n    it \"works for Bytes\" do\n      slice = Bytes.new(4) { |i| i.to_u8 + 1 }\n      slice.hexstring.should eq(\"01020304\")\n    end\n  end\n\n  describe \"#hexdump\" do\n    it \"works for empty slice\" do\n      Bytes.empty.hexdump.should eq(\"\")\n\n      io = IO::Memory.new\n      Bytes.empty.hexdump(io).should eq(0)\n      io.to_s.should eq(\"\")\n    end\n\n    it \"works for Bytes\" do\n      slice = Bytes.new(96) { |i| i.to_u8 + 32 }\n      assert_prints slice.hexdump, <<-EOF\n        00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f   !\"#$%&'()*+,-./\n        00000010  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?\n        00000020  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO\n        00000030  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[\\\\]^_\n        00000040  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno\n        00000050  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.\\n\n        EOF\n\n      plus = Bytes.new(101) { |i| i.to_u8 + 32 }\n      assert_prints plus.hexdump, <<-EOF\n        00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f   !\"#$%&'()*+,-./\n        00000010  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?\n        00000020  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO\n        00000030  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[\\\\]^_\n        00000040  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno\n        00000050  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.\n        00000060  80 81 82 83 84                                    .....\\n\n        EOF\n\n      num = Bytes.new(10) { |i| i.to_u8 + 48 }\n      assert_prints num.hexdump, <<-EOF\n        00000000  30 31 32 33 34 35 36 37  38 39                    0123456789\\n\n        EOF\n    end\n  end\n\n  it_iterates \"#each\", [1, 2, 3], Slice[1, 2, 3].each\n  it_iterates \"#reverse_each\", [3, 2, 1], Slice[1, 2, 3].reverse_each\n  it_iterates \"#each_index\", [0, 1, 2], Slice[1, 2, 3].each_index\n\n  it \"does to_a\" do\n    slice = Slice.new(3) { |i| i }\n    ary = slice.to_a\n    ary.should eq([0, 1, 2])\n  end\n\n  it \"does rindex\" do\n    slice = \"foobar\".to_slice\n    slice.rindex('o'.ord.to_u8).should eq(2)\n    slice.rindex('z'.ord.to_u8).should be_nil\n  end\n\n  it \"does bytesize\" do\n    slice = Slice(Int32).new(2)\n    slice.bytesize.should eq(8)\n  end\n\n  describe \"==\" do\n    it \"does ==\" do\n      a = Slice.new(3) { |i| i }\n      b = Slice.new(3) { |i| i }\n      c = Slice.new(3) { |i| i + 1 }\n      a.should eq(b)\n      a.should_not eq(c)\n    end\n\n    it \"does == with same type, different runtime instances\" do\n      a = Slice.new(3, &.to_s)\n      b = Slice.new(3, &.to_s)\n      a.should eq(b)\n    end\n\n    it \"does == for bytes\" do\n      a = Bytes[1, 2, 3]\n      b = Bytes[1, 2, 3]\n      c = Bytes[1, 2, 4]\n      a.should eq(b)\n      a.should_not eq(c)\n    end\n  end\n\n  it \"#same?\" do\n    slice = Slice[1, 2, 3]\n\n    slice.should be slice\n    slice.should_not be slice.dup\n    slice.should_not be Slice[1, 2, 3]\n\n    (slice + 1).should be slice + 1\n    slice.should_not be slice + 1\n\n    (slice[0, 2]).should be slice[0, 2]\n    slice.should_not be slice[0, 2]\n  end\n\n  it \"does macro []\" do\n    slice = Slice[1, 'a', \"foo\"]\n    slice.should be_a(Slice(Int32 | Char | String))\n    slice.size.should eq(3)\n    slice[0].should eq(1)\n    slice[1].should eq('a')\n    slice[2].should eq(\"foo\")\n  end\n\n  it \"does macro [] with numbers (#3055)\" do\n    slice = Bytes[1, 2, 3]\n    slice.should be_a(Bytes)\n    slice.should eq(Bytes[1, 2, 3])\n  end\n\n  it \"does Bytes[]\" do\n    slice = Bytes[]\n    slice.should be_a(Bytes)\n    slice.should be_empty\n  end\n\n  it \"uses percent vars in [] macro (#2954)\" do\n    slices = itself(Slice[1, 2], Slice[3])\n    slices[0].to_a.should eq([1, 2])\n    slices[1].to_a.should eq([3])\n  end\n\n  it \"reverses\" do\n    slice = Bytes[1, 2, 3]\n    slice.reverse!\n    slice.should eq(Bytes[3, 2, 1])\n  end\n\n  it \"shuffles\" do\n    a = Bytes[1, 2, 3]\n    a.shuffle!\n    b = [1, 2, 3]\n    3.times { a.should contain(b.shift) }\n  end\n\n  it \"does map\" do\n    a = Slice[1, 2, 3]\n    b = a.map { |x| x * 2 }\n    b.should eq(Slice[2, 4, 6])\n  end\n\n  it \"does map!\" do\n    a = Slice[1, 2, 3]\n    b = a.map! { |x| x * 2 }\n    a.should eq(Slice[2, 4, 6])\n    a.to_unsafe.should eq(b.to_unsafe)\n  end\n\n  it \"does map_with_index\" do\n    a = Slice[1, 1, 2, 2]\n    b = a.map_with_index { |e, i| e + i }\n    b.should eq(Slice[1, 2, 4, 5])\n  end\n\n  it \"does map_with_index, with offset\" do\n    a = Slice[1, 1, 2, 2]\n    b = a.map_with_index(10) { |e, i| e + i }\n    b.should eq(Slice[11, 12, 14, 15])\n  end\n\n  it \"does map_with_index!\" do\n    a = Slice[1, 1, 2, 2]\n    b = a.map_with_index! { |e, i| e + i }\n    a.should eq(Slice[1, 2, 4, 5])\n    a.to_unsafe.should eq(b.to_unsafe)\n  end\n\n  it \"does map_with_index!, with offset\" do\n    a = Slice[1, 1, 2, 2]\n    b = a.map_with_index!(10) { |e, i| e + i }\n    a.should eq(Slice[11, 12, 14, 15])\n    a.to_unsafe.should eq(b.to_unsafe)\n  end\n\n  describe \"rotate!\" do\n    it do\n      a = Slice[1, 2, 3]\n      a.rotate!.to_unsafe.should eq(a.to_unsafe); a.should eq(Slice[2, 3, 1])\n      a.rotate!.to_unsafe.should eq(a.to_unsafe); a.should eq(Slice[3, 1, 2])\n      a.rotate!.to_unsafe.should eq(a.to_unsafe); a.should eq(Slice[1, 2, 3])\n      a.rotate!.to_unsafe.should eq(a.to_unsafe); a.should eq(Slice[2, 3, 1])\n    end\n\n    it { a = Slice[1, 2, 3]; a.rotate!(0); a.should eq(Slice[1, 2, 3]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(1); a.should eq(Slice[2, 3, 1]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(2); a.should eq(Slice[3, 1, 2]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(3); a.should eq(Slice[1, 2, 3]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(4); a.should eq(Slice[2, 3, 1]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(3001); a.should eq(Slice[2, 3, 1]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(-1); a.should eq(Slice[3, 1, 2]) }\n    it { a = Slice[1, 2, 3]; a.rotate!(-3001); a.should eq(Slice[3, 1, 2]) }\n\n    it do\n      a = Slice(Int32).new(50) { |i| i }\n      a.rotate!(5)\n      a.should eq(Slice[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, 0, 1, 2, 3, 4])\n    end\n\n    it do\n      a = Slice(Int32).new(50) { |i| i }\n      a.rotate!(-5)\n      a.should eq(Slice[45, 46, 47, 48, 49, 0, 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])\n    end\n\n    it do\n      a = Slice(Int32).new(50) { |i| i }\n      a.rotate!(20)\n      a.should eq(Slice[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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])\n    end\n\n    it do\n      a = Slice(Int32).new(50) { |i| i }\n      a.rotate!(-20)\n      a.should eq(Slice[30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 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])\n    end\n  end\n\n  it \"creates empty slice\" do\n    slice = Slice(Int32).empty\n    slice.should be_empty\n  end\n\n  it \"creates read-only slice\" do\n    slice = Slice.new(3, 0, read_only: true)\n    slice.read_only?.should be_true\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice[0] = 1 }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.update(0, &.itself) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.swap(0, 1) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.reverse! }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(0) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(0, 0, 0) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(0, 0..0) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(&.itself) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(offset: 0, &.itself) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(0, 0, &.itself) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.fill(0..0, &.itself) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.map!(&.itself) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.map_with_index! { |v, i| v } }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.map_with_index!(offset: 0) { |v, i| v } }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.shuffle! }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.rotate!(0) }\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice.copy_from(slice) }\n\n    subslice = slice[0, 1]\n    subslice.read_only?.should be_true\n    expect_raises(Exception, \"Can't write to read-only Slice\") { subslice[0] = 1 }\n\n    slice = Bytes[1, 2, 3, read_only: true]\n    slice.read_only?.should be_true\n    expect_raises(Exception, \"Can't write to read-only Slice\") { slice[0] = 0_u8 }\n  end\n\n  it \"hashes each item in collection\" do\n    Slice[1, 2, 3].hash.should eq(Slice[1_u64, 2_u64, 3_u64].hash)\n  end\n\n  it \"optimizes hash for Bytes\" do\n    Bytes[1, 2, 3].hash.should_not eq(Slice[1, 2, 3].hash)\n  end\n\n  it \"#[]\" do\n    slice = Slice.new(6) { |i| i + 1 }\n    subslice = slice[2..4]\n    subslice.read_only?.should be_false\n    subslice.size.should eq(3)\n    subslice.to_unsafe.should eq(slice.to_unsafe + 2)\n    subslice.should eq(Slice.new(3) { |i| i + 3 })\n  end\n\n  it \"#[] keeps read-only value\" do\n    slice = Slice.new(6, read_only: true) { |i| i + 1 }\n    slice[2..4].read_only?.should be_true\n  end\n\n  describe \"#clone\" do\n    it \"clones primitive\" do\n      slice = Slice[1, 2]\n      slice.clone.should eq slice\n    end\n\n    it \"clones non-primitive\" do\n      slice = Slice[\"abc\", \"a\"]\n      slice.clone.should eq slice\n    end\n\n    it \"buffer copy\" do\n      slice = Slice[\"foo\"]\n      copy = slice.clone\n      slice[0] = \"bar\"\n      copy.should_not eq slice\n    end\n\n    it \"deep copy\" do\n      slice = Slice[[\"foo\"]]\n      copy = slice.clone\n      slice[0] << \"bar\"\n      copy.should_not eq slice\n    end\n  end\n\n  describe \"#dup\" do\n    it \"buffer copy\" do\n      slice = Slice[\"foo\"]\n      copy = slice.dup\n      slice[0] = \"bar\"\n      copy.should_not eq slice\n    end\n\n    it \"don't deep copy\" do\n      slice = Slice[[\"foo\"]]\n      copy = slice.dup\n      slice[0] << \"bar\"\n      copy.should eq slice\n    end\n  end\n\n  describe \"sort\" do\n    {% for sort in [\"sort\".id, \"unstable_sort\".id] %}\n      describe {{ \"##{sort}\" }} do\n        it \"without block\" do\n          slice = Slice[3, 4, 1, 2, 5, 6]\n          sorted_slice = slice.{{ sort }}\n          sorted_slice.to_a.should eq([1, 2, 3, 4, 5, 6])\n          slice.should_not eq(sorted_slice)\n        end\n\n        it \"with a block\" do\n          a = Slice[\"foo\", \"a\", \"hello\"]\n          b = a.{{ sort }} { |x, y| x.size <=> y.size }\n          b.to_a.should eq([\"a\", \"foo\", \"hello\"])\n          a.should_not eq(b)\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort without a block\" do\n            is_stable_sort(mutable: false, &.sort)\n          end\n\n          it \"stable sort with a block\" do\n            is_stable_sort(mutable: false, &.sort { |a, b| a.value <=> b.value })\n          end\n        {% end %}\n      end\n\n      describe {{ \"##{sort}!\" }} do\n        it \"without block\" do\n          a = [3, 4, 1, 2, 5, 6]\n          a.{{ sort }}!\n          a.should eq([1, 2, 3, 4, 5, 6])\n        end\n\n        it \"with a block\" do\n          a = [\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}! { |x, y| x.size <=> y.size }\n          a.should eq([\"a\", \"foo\", \"hello\"])\n        end\n\n        it \"sorts with invalid block (#4379)\" do\n          a = [1] * 17\n          b = a.{{ sort }} { -1 }\n          a.should eq(b)\n        end\n\n        it \"can sort! just by using <=> (#6608)\" do\n          spaceships = Slice[\n            Spaceship.new(2),\n            Spaceship.new(0),\n            Spaceship.new(1),\n            Spaceship.new(3),\n          ]\n\n          spaceships.{{ sort }}!\n          4.times do |i|\n            spaceships[i].value.should eq(i)\n          end\n        end\n\n        it \"raises if <=> returns nil\" do\n          spaceships = Slice[\n            Spaceship.new(2, return_nil: true),\n            Spaceship.new(0, return_nil: true),\n          ]\n\n          expect_raises(ArgumentError) do\n            spaceships.{{ sort }}!\n          end\n        end\n\n        it \"raises if sort! block returns nil\" do\n          expect_raises(ArgumentError) do\n            Slice[1, 2].{{ sort }}! { nil }\n          end\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort without a block\" do\n            is_stable_sort(mutable: true, &.sort!)\n          end\n\n          it \"stable sort with a block\" do\n            is_stable_sort(mutable: true, &.sort! { |a, b| a.value <=> b.value })\n          end\n        {% end %}\n      end\n\n      describe {{ \"##{sort}_by\" }} do\n        it \"sorts\" do\n          a = Slice[\"foo\", \"a\", \"hello\"]\n          b = a.{{ sort }}_by(&.size)\n          b.to_a.should eq([\"a\", \"foo\", \"hello\"])\n          a.should_not eq(b)\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort\" do\n            is_stable_sort(mutable: false, &.sort_by(&.value))\n          end\n        {% end %}\n      end\n\n      describe {{ \"##{sort}_by\" }} do\n        it \"sorts\" do\n          a = Slice[\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}_by!(&.size)\n          a.to_a.should eq([\"a\", \"foo\", \"hello\"])\n        end\n\n        it \"calls given block exactly once for each element\" do\n          calls = Hash(String, Int32).new(0)\n          a = Slice[\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}_by! { |e| calls[e] += 1; e.size }\n          calls.should eq({\"foo\" => 1, \"a\" => 1, \"hello\" => 1})\n        end\n\n        {% if sort == \"sort\" %}\n          it \"stable sort\" do\n            is_stable_sort(mutable: true, &.sort_by!(&.value))\n          end\n        {% end %}\n      end\n    {% end %}\n  end\n\n  describe \"<=>\" do\n    it \"is comparable\" do\n      Bytes[1].is_a?(Comparable).should be_true\n    end\n\n    it \"compares\" do\n      (Int32.slice(1, 2, 3) <=> Int32.slice(1, 2, 3)).should eq(0)\n      (Int32.slice(1, 2, 3) <=> Int32.slice(1, 3, 3)).should be < 0\n      (Int32.slice(1, 3, 3) <=> Int32.slice(1, 2, 3)).should be > 0\n      (Int32.slice(1, 2, 3) <=> Int32.slice(1, 2, 3, 4)).should be < 0\n      (Int32.slice(1, 2, 3, 4) <=> Int32.slice(1, 2, 3)).should be > 0\n    end\n\n    it \"compares (UInt8)\" do\n      (Bytes[1, 2, 3] <=> Bytes[1, 2, 3]).should eq(0)\n      (Bytes[1, 2, 3] <=> Bytes[1, 3, 3]).should be < 0\n      (Bytes[1, 3, 3] <=> Bytes[1, 2, 3]).should be > 0\n      (Bytes[1, 2, 3] <=> Bytes[1, 2, 3, 4]).should be < 0\n      (Bytes[1, 2, 3, 4] <=> Bytes[1, 2, 3]).should be > 0\n    end\n  end\n\n  describe \"#+(Slice)\" do\n    it \"concatenates two slices\" do\n      a = Slice[1, 2]\n      b = a + Slice[3, 4, 5]\n      b.should be_a(Slice(Int32))\n      b.should eq(Slice[1, 2, 3, 4, 5])\n      a.should eq(Slice[1, 2])\n\n      c = Slice[1, 2] + Slice['a', 'b', 'c']\n      c.should be_a(Slice(Int32 | Char))\n      c.should eq(Slice[1, 2, 'a', 'b', 'c'])\n    end\n  end\n\n  describe \".join\" do\n    it \"concatenates an indexable of slices\" do\n      a = Slice.join([Slice[1, 2], Slice[3, 4, 5]])\n      a.should be_a(Slice(Int32))\n      a.should eq(Slice[1, 2, 3, 4, 5])\n\n      b = Slice.join({Slice[1, 2], Slice['a', 'b', 'c']})\n      b.should be_a(Slice(Int32 | Char))\n      b.should eq(Slice[1, 2, 'a', 'b', 'c'])\n\n      c = Slice.join(Deque{Slice[1, 2], Slice['a', 'b', 'c'], Slice[\"d\", \"e\"], Slice[3, \"f\"]})\n      c.should be_a(Slice(Int32 | Char | String))\n      c.should eq(Slice[1, 2, 'a', 'b', 'c', \"d\", \"e\", 3, \"f\"])\n    end\n\n    it \"concatenates a slice of slices\" do\n      a = Slice[1]\n      b = Slice['a']\n      c = Slice[\"xyz\"]\n\n      Slice.join(Slice[a, b, c]).should eq(Slice[1, 'a', \"xyz\"])\n    end\n\n    it \"concatenates an empty indexable of slices\" do\n      a = Slice.join(Array(Slice(Int32)).new)\n      a.should be_a(Slice(Int32))\n      a.should be_empty\n\n      b = Slice.join(Deque(Slice(Int32)).new)\n      b.should be_a(Slice(Int32))\n      b.should be_empty\n    end\n  end\n\n  describe \".additive_identity\" do\n    it \"returns an empty slice\" do\n      a = Slice(Int32).additive_identity\n      a.should be_a(Slice(Int32))\n      a.should be_empty\n    end\n  end\nend\n\nprivate def itself(*args)\n  args\nend\n"
  },
  {
    "path": "spec/std/socket/address_spec.cr",
    "content": "require \"spec\"\nrequire \"socket\"\nrequire \"../../support/win32\"\nrequire \"spec/helpers/string\"\n\ndescribe Socket::Address do\n  describe \".parse\" do\n    it \"accepts URI\" do\n      address = Socket::Address.parse URI.parse(\"tcp://192.168.0.1:8081\")\n      address.should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n    end\n\n    it \"parses TCP\" do\n      address = Socket::Address.parse \"tcp://192.168.0.1:8081\"\n      address.should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n    end\n\n    it \"parses UDP\" do\n      address = Socket::Address.parse \"udp://192.168.0.1:8081\"\n      address.should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n    end\n\n    it \"parses UNIX\" do\n      address = Socket::Address.parse \"unix://socket.sock\"\n      address.should eq Socket::UNIXAddress.new(\"socket.sock\")\n    end\n\n    it \"fails with unknown scheme\" do\n      expect_raises(Socket::Error, \"Unsupported address type: ssl\") do\n        Socket::Address.parse \"ssl://192.168.0.1:8081\"\n      end\n    end\n  end\nend\n\ndescribe Socket::IPAddress do\n  c_port = {% if IO::ByteFormat::NetworkEndian != IO::ByteFormat::SystemEndian %}\n             36895 # 0x901F\n           {% else %}\n             8080 # 0x1F90\n           {% end %}\n\n  it \"transforms an IPv4 address into a C struct and back\" do\n    addr1 = Socket::IPAddress.new(\"127.0.0.1\", 8080)\n\n    addr1_c = addr1.to_unsafe\n    addr1_c.as(LibC::SockaddrIn*).value.sin_port.should eq(c_port)\n\n    addr2 = Socket::IPAddress.from(addr1_c, addr1.size)\n    addr2.family.should eq(addr1.family)\n    addr2.port.should eq(addr1.port)\n    typeof(addr2.address).should eq(String)\n    addr2.address.should eq(addr1.address)\n    addr2.should eq(Socket::IPAddress.from(addr1_c))\n  end\n\n  it \"transforms an IPv6 address into a C struct and back\" do\n    addr1 = Socket::IPAddress.new(\"2001:db8:8714:3a90::12\", 8080)\n\n    addr1_c = addr1.to_unsafe\n    addr1_c.as(LibC::SockaddrIn6*).value.sin6_port.should eq(c_port)\n\n    addr2 = Socket::IPAddress.from(addr1_c, addr1.size)\n    addr2.family.should eq(addr1.family)\n    addr2.port.should eq(addr1.port)\n    typeof(addr2.address).should eq(String)\n    addr2.address.should eq(addr1.address)\n    addr2.should eq(Socket::IPAddress.from(addr1_c))\n  end\n\n  it \"won't resolve domains\" do\n    expect_raises(Socket::Error, /Invalid IP address/) do\n      Socket::IPAddress.new(\"localhost\", 1234)\n    end\n  end\n\n  it \"errors on out of range port numbers\" do\n    expect_raises(Socket::Error, /Invalid port number/) do\n      Socket::IPAddress.new(\"localhost\", -1)\n    end\n\n    expect_raises(Socket::Error, /Invalid port number/) do\n      Socket::IPAddress.new(\"localhost\", 65536)\n    end\n  end\n\n  it \"#to_s\" do\n    assert_prints Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 80).to_s, \"127.0.0.1:80\"\n\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0x8714, 0x3a90, 0, 0, 0, 0x12), 443).to_s, \"[2001:db8:8714:3a90::12]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 0xffff).to_s, \"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 1, 1, 1, 1, 1), 443).to_s, \"[2001:db8:0:1:1:1:1:1]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 1, 0, 0, 0, 1), 443).to_s, \"[2001:0:0:1::1]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 0, 1, 0, 0, 1), 443).to_s, \"[2001::1:0:0:1]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1), 443).to_s, \"[2001:db8::1:0:0:1]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1), 443).to_s, \"[::1]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(1, 0, 0, 0, 0, 0, 0, 0), 443).to_s, \"[1::]:443\"\n    assert_prints Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0), 443).to_s, \"[::]:443\"\n\n    assert_prints Socket::IPAddress.v4_mapped_v6(UInt8.static_array(0, 0, 0, 0), 443).to_s, \"[::ffff:0.0.0.0]:443\"\n    assert_prints Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 1, 15), 443).to_s, \"[::ffff:192.168.1.15]:443\"\n\n    assert_prints Socket::IPAddress.new(\"0:0:0:0:0:0:0:1\", 443).to_s, \"[::1]:443\"\n    assert_prints Socket::IPAddress.new(\"0:0:0:0:0:ffff:c0a8:010f\", 443).to_s, \"[::ffff:192.168.1.15]:443\"\n    assert_prints Socket::IPAddress.new(\"::ffff:0:0\", 443).to_s, \"[::ffff:0.0.0.0]:443\"\n  end\n\n  it \"#address\" do\n    Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 80).address.should eq \"127.0.0.1\"\n\n    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0x8714, 0x3a90, 0, 0, 0, 0x12), 443).address.should eq \"2001:db8:8714:3a90::12\"\n    Socket::IPAddress.v6(UInt16.static_array(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 0xffff).address.should eq \"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\"\n    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 1, 1, 1, 1, 1), 443).address.should eq \"2001:db8:0:1:1:1:1:1\"\n    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 1, 0, 0, 0, 1), 443).address.should eq \"2001:0:0:1::1\"\n    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 0, 1, 0, 0, 1), 443).address.should eq \"2001::1:0:0:1\"\n    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1), 443).address.should eq \"2001:db8::1:0:0:1\"\n    Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1), 443).address.should eq \"::1\"\n    Socket::IPAddress.v6(UInt16.static_array(1, 0, 0, 0, 0, 0, 0, 0), 443).address.should eq \"1::\"\n    Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0), 443).address.should eq \"::\"\n\n    Socket::IPAddress.v4_mapped_v6(UInt8.static_array(0, 0, 0, 0), 443).address.should eq \"::ffff:0.0.0.0\"\n    Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 1, 15), 443).address.should eq \"::ffff:192.168.1.15\"\n\n    Socket::IPAddress.new(\"0:0:0:0:0:0:0:1\", 443).address.should eq \"::1\"\n    Socket::IPAddress.new(\"0:0:0:0:0:ffff:c0a8:010f\", 443).address.should eq \"::ffff:192.168.1.15\"\n    Socket::IPAddress.new(\"::ffff:0:0\", 443).address.should eq \"::ffff:0.0.0.0\"\n  end\n\n  describe \"#zone_id\" do\n    # loopback interface \"lo\" is supposed to *always* be the first interface and\n    # enumerated with index 1\n    loopback_iface = {% if flag?(:windows) %}\n                       \"loopback_0\"\n                     {% elsif flag?(:darwin) || flag?(:bsd) || flag?(:solaris) %}\n                       \"lo0\"\n                     {% else %}\n                       \"lo\"\n                     {% end %}\n\n    it \"parses link-local IPv6 with interface scope\" do\n      address = Socket::IPAddress.new(\"fe80::3333:4444%3\", 8081)\n      address.address.should eq \"fe80::3333:4444\"\n      address.zone_id.should eq 3\n      address.inspect.should eq \"Socket::IPAddress([fe80::3333:4444%3]:8081)\"\n    end\n\n    it \"looks up loopback interface index by name\" do\n      address = Socket::IPAddress.new(\"fe80::1111%#{loopback_iface}\", 0)\n      address.address.should eq \"fe80::1111\"\n      address.zone_id.should eq 1\n    end\n\n    it \"looks up loopback interface name by index\" do\n      # loopback interface \"lo\" is supposed to *always* be the first interface and\n      # enumerated with index 1\n      address = Socket::IPAddress.new(\"fe80::1111%#{loopback_iface}\", 0)\n      address.link_local_interface.should eq loopback_iface\n    end\n\n    it \"fails interface name lookup for non-existent interfaces\" do\n      exc_suff = {% if flag?(:windows) %}\n                   \"\"\n                 {% elsif flag?(:darwin) || flag?(:bsd) %}\n                   \": Device not configured\"\n                 {% else %}\n                   \": No such device or address\"\n                 {% end %}\n      expect_raises(Socket::Error, \"Failed to look up interface name for index 333#{exc_suff}\") do\n        Socket::IPAddress.new(\"fe80::d00d:1%333\", 0).link_local_interface\n      end\n    end\n\n    it \"interface name lookup returns nil in unsupported cases\" do\n      Socket::IPAddress.new(\"fd03::3333\", 0).link_local_interface.should be_nil\n      Socket::IPAddress.new(\"192.168.10.10\", 0).link_local_interface.should be_nil\n      Socket::IPAddress.new(\"169.254.0.3\", 0).link_local_interface.should be_nil\n      Socket::IPAddress.new(\"fe80::4545\", 0).link_local_interface.should be_nil\n    end\n\n    it \"fails link-local zone identifier on non-LL v6 addrs\" do\n      expect_raises(Socket::Error, \"Zoned/scoped IPv6 addresses are only allowed for link-local (supplied 'fd00::abcd%5' is not within fe80::/10)\") do\n        Socket::IPAddress.new(\"fd00::abcd%5\", 443)\n      end\n    end\n\n    it \"fails link-local zone identifier on v4 addrs\" do\n      expect_raises(Socket::Error, \"Invalid IP address: 169.254.11.11%eth0\") do\n        Socket::IPAddress.new(\"169.254.11.11%eth0\", 0)\n      end\n\n      expect_raises(Socket::Error, \"Invalid IP address: 192.168.11.11%3\") do\n        Socket::IPAddress.new(\"192.168.11.11%3\", 0)\n      end\n    end\n\n    it \"fails on invalid link-local zone identifier\" do\n      expect_raises(Socket::Error, \"Invalid IPv6 link-local zone index '0' in address 'fe80::c0ff:ee%0'\") do\n        Socket::IPAddress.new(\"fe80::c0ff:ee%0\", port: 0)\n      end\n    end\n\n    it \"fails on non-existent link-local zone interface\" do\n      # looking up an interface index obviously requires for said interface device to exist\n      expect_raises(Socket::Error, \"IPv6 link-local zone interface 'zzzzzzzzzzzzzzz' not found (in address 'fe80::0f0f:abcd%zzzzzzzzzzzzzzz')\") do\n        Socket::IPAddress.new(\"fe80::0f0f:abcd%zzzzzzzzzzzzzzz\", port: 0)\n      end\n    end\n  end\n\n  describe \".parse\" do\n    it \"parses IPv4\" do\n      address = Socket::IPAddress.parse \"ip://192.168.0.1:8081\"\n      address.should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n    end\n\n    it \"parses IPv6\" do\n      address = Socket::IPAddress.parse \"ip://[::1]:8081\"\n      address.should eq Socket::IPAddress.new(\"::1\", 8081)\n    end\n\n    it \"fails host name\" do\n      expect_raises(Socket::Error, \"Invalid IP address: example.com\") do\n        Socket::IPAddress.parse \"ip://example.com:8081\"\n      end\n    end\n\n    it \"ignores path and params\" do\n      address = Socket::IPAddress.parse \"ip://192.168.0.1:8081/foo?bar=baz\"\n      address.should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n    end\n\n    it \"fails with missing host\" do\n      expect_raises(Socket::Error, \"Invalid IP address: missing host\") do\n        Socket::IPAddress.parse \"ip:///path\"\n      end\n    end\n\n    it \"fails with missing port\" do\n      expect_raises(Socket::Error, \"Invalid IP address: missing port\") do\n        Socket::IPAddress.parse \"ip://127.0.0.1\"\n      end\n    end\n  end\n\n  # Tests from libc-test:\n  # https://repo.or.cz/libc-test.git/blob/2113a3ed8217775797dd9a82aa420c10ef1712d5:/src/functional/inet_pton.c\n  describe \".parse_v4_fields?\" do\n    # dotted-decimal notation\n    it { Socket::IPAddress.parse_v4_fields?(\"0.0.0.0\").should eq UInt8.static_array(0, 0, 0, 0) }\n    it { Socket::IPAddress.parse_v4_fields?(\"127.0.0.1\").should eq UInt8.static_array(127, 0, 0, 1) }\n    it { Socket::IPAddress.parse_v4_fields?(\"10.0.128.31\").should eq UInt8.static_array(10, 0, 128, 31) }\n    it { Socket::IPAddress.parse_v4_fields?(\"255.255.255.255\").should eq UInt8.static_array(255, 255, 255, 255) }\n\n    # numbers-and-dots notation, but not dotted-decimal\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.03.4\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.0x33.4\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.0XAB.4\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.0xabcd\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.0xabcdef\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"00377.0x0ff.65534\").should be_nil }\n\n    # invalid\n    it { Socket::IPAddress.parse_v4_fields?(\".1.2.3\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1..2.3\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.3.\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.3.4.5\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.3.a\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.256.2.3\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.4294967296.3\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2.-4294967295.3\").should be_nil }\n    it { Socket::IPAddress.parse_v4_fields?(\"1.2. 3.4\").should be_nil }\n  end\n\n  describe \".parse_v6_fields?\" do\n    it { Socket::IPAddress.parse_v6_fields?(\":\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"::\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0) }\n    it { Socket::IPAddress.parse_v6_fields?(\"::1\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1) }\n    it { Socket::IPAddress.parse_v6_fields?(\":::\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"192.168.1.1\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\":192.168.1.1\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"::192.168.1.1\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0xc0a8, 0x0101) }\n    it { Socket::IPAddress.parse_v6_fields?(\"0:0:0:0:0:0:192.168.1.1\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0xc0a8, 0x0101) }\n    it { Socket::IPAddress.parse_v6_fields?(\"0:0::0:0:0:192.168.1.1\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0xc0a8, 0x0101) }\n    it { Socket::IPAddress.parse_v6_fields?(\"::012.34.56.78\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\":ffff:192.168.1.1\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"::ffff:192.168.1.1\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0101) }\n    it { Socket::IPAddress.parse_v6_fields?(\".192.168.1.1\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\":.192.168.1.1\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"a:0b:00c:000d:E:F::\").should eq UInt16.static_array(0xa, 0x0b, 0x00c, 0x000d, 0xE, 0xF, 0, 0) }\n    it { Socket::IPAddress.parse_v6_fields?(\"a:0b:00c:000d:0000e:f::\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"1:2:3:4:5:6::\").should eq UInt16.static_array(1, 2, 3, 4, 5, 6, 0, 0) }\n    it { Socket::IPAddress.parse_v6_fields?(\"1:2:3:4:5:6:7::\").should eq UInt16.static_array(1, 2, 3, 4, 5, 6, 7, 0) }\n    it { Socket::IPAddress.parse_v6_fields?(\"1:2:3:4:5:6:7:8::\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"1:2:3:4:5:6:7::9\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"::1:2:3:4:5:6\").should eq UInt16.static_array(0, 0, 1, 2, 3, 4, 5, 6) }\n    it { Socket::IPAddress.parse_v6_fields?(\"::1:2:3:4:5:6:7\").should eq UInt16.static_array(0, 1, 2, 3, 4, 5, 6, 7) }\n    it { Socket::IPAddress.parse_v6_fields?(\"::1:2:3:4:5:6:7:8\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"a:b::c:d:e:f\").should eq UInt16.static_array(0xa, 0xb, 0, 0, 0xc, 0xd, 0xe, 0xf) }\n    it { Socket::IPAddress.parse_v6_fields?(\"ffff:c0a8:5e4\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\":ffff:c0a8:5e4\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"0:0:0:0:0:ffff:c0a8:5e4\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x5e4) }\n    it { Socket::IPAddress.parse_v6_fields?(\"0:0:0:0:ffff:c0a8:5e4\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"0::ffff:c0a8:5e4\").should eq UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x5e4) }\n    it { Socket::IPAddress.parse_v6_fields?(\"::0::ffff:c0a8:5e4\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"c0a8\").should be_nil }\n    it { Socket::IPAddress.parse_v6_fields?(\"fe80::a:b%eth0\").should eq UInt16.static_array(0xfe80, 0, 0, 0, 0, 0, 0xa, 0xb) }\n    it { Socket::IPAddress.parse_v6_fields?(\"fe80:0:0:0:ffff:c0a8:5e4%lo\").should eq UInt16.static_array(0xfe80, 0, 0, 0, 0xffff, 0xc0a8, 0x5e4, 0) }\n  end\n\n  describe \".v4\" do\n    it \"constructs an IPv4 address\" do\n      Socket::IPAddress.v4(0, 0, 0, 0, port: 0).should eq Socket::IPAddress.new(\"0.0.0.0\", 0)\n      Socket::IPAddress.v4(127, 0, 0, 1, port: 1234).should eq Socket::IPAddress.new(\"127.0.0.1\", 1234)\n      Socket::IPAddress.v4(192, 168, 0, 1, port: 8081).should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n      Socket::IPAddress.v4(255, 255, 255, 254, port: 65535).should eq Socket::IPAddress.new(\"255.255.255.254\", 65535)\n    end\n\n    it \"raises on out of bound field\" do\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4(256, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4(0, 256, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4(0, 0, 256, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4(0, 0, 0, 256, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4(-1, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4(0, -1, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4(0, 0, -1, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4(0, 0, 0, -1, port: 0) }\n    end\n\n    it \"raises on out of bound port number\" do\n      expect_raises(Socket::Error, \"Invalid port number: 65536\") { Socket::IPAddress.v4(0, 0, 0, 0, port: 65536) }\n      expect_raises(Socket::Error, \"Invalid port number: -1\") { Socket::IPAddress.v4(0, 0, 0, 0, port: -1) }\n    end\n\n    it \"constructs from StaticArray\" do\n      Socket::IPAddress.v4(UInt8.static_array(0, 0, 0, 0), 0).should eq Socket::IPAddress.new(\"0.0.0.0\", 0)\n      Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 1234).should eq Socket::IPAddress.new(\"127.0.0.1\", 1234)\n      Socket::IPAddress.v4(UInt8.static_array(192, 168, 0, 1), 8081).should eq Socket::IPAddress.new(\"192.168.0.1\", 8081)\n      Socket::IPAddress.v4(UInt8.static_array(255, 255, 255, 254), 65535).should eq Socket::IPAddress.new(\"255.255.255.254\", 65535)\n    end\n  end\n\n  describe \".v6\" do\n    it \"constructs an IPv6 address\" do\n      Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 0, port: 0).should eq Socket::IPAddress.new(\"::\", 0)\n      Socket::IPAddress.v6(1, 2, 3, 4, 5, 6, 7, 8, port: 8080).should eq Socket::IPAddress.new(\"1:2:3:4:5:6:7:8\", 8080)\n      Socket::IPAddress.v6(0xfe80, 0, 0, 0, 0x4860, 0x4860, 0x4860, 0x1234, port: 55001).should eq Socket::IPAddress.new(\"fe80::4860:4860:4860:1234\", 55001)\n      Socket::IPAddress.v6(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfffe, port: 65535).should eq Socket::IPAddress.new(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe\", 65535)\n      Socket::IPAddress.v6(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0001, port: 0).should eq Socket::IPAddress.new(\"::ffff:192.168.0.1\", 0)\n      Socket::IPAddress.v6(0xfe80, 0, 0, 0, 0x5971, 0x5971, 0x5971, 0xabcd, port: 44444, zone_id: 3).should eq Socket::IPAddress.new(\"fe80::5971:5971:5971:abcd%3\", 44444)\n    end\n\n    it \"raises on out of bound field\" do\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(65536, 0, 0, 0, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 65536, 0, 0, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 0, 65536, 0, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 0, 0, 65536, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 0, 0, 0, 65536, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 65536, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 65536, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: 65536\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 65536, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(-1, 0, 0, 0, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, -1, 0, 0, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, 0, -1, 0, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, 0, 0, -1, 0, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, 0, 0, 0, -1, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, -1, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, -1, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv6 field: -1\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, -1, port: 0) }\n    end\n\n    it \"raises on out of bound port number\" do\n      expect_raises(Socket::Error, \"Invalid port number: 65536\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 0, port: 65536) }\n      expect_raises(Socket::Error, \"Invalid port number: -1\") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 0, port: -1) }\n    end\n\n    it \"constructs from StaticArray\" do\n      Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0), 0).should eq Socket::IPAddress.new(\"::\", 0)\n      Socket::IPAddress.v6(UInt16.static_array(1, 2, 3, 4, 5, 6, 7, 8), 8080).should eq Socket::IPAddress.new(\"1:2:3:4:5:6:7:8\", 8080)\n      Socket::IPAddress.v6(UInt16.static_array(0xfe80, 0, 0, 0, 0x4860, 0x4860, 0x4860, 0x1234), 55001).should eq Socket::IPAddress.new(\"fe80::4860:4860:4860:1234\", 55001)\n      Socket::IPAddress.v6(UInt16.static_array(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfffe), 65535).should eq Socket::IPAddress.new(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe\", 65535)\n      Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0001), 0).should eq Socket::IPAddress.new(\"::ffff:192.168.0.1\", 0)\n    end\n  end\n\n  describe \".v4_mapped_v6\" do\n    it \"constructs an IPv4-mapped IPv6 address\" do\n      Socket::IPAddress.v4_mapped_v6(0, 0, 0, 0, port: 0).should eq Socket::IPAddress.new(\"::ffff:0.0.0.0\", 0)\n      Socket::IPAddress.v4_mapped_v6(127, 0, 0, 1, port: 1234).should eq Socket::IPAddress.new(\"::ffff:127.0.0.1\", 1234)\n      Socket::IPAddress.v4_mapped_v6(192, 168, 0, 1, port: 8081).should eq Socket::IPAddress.new(\"::ffff:192.168.0.1\", 8081)\n      Socket::IPAddress.v4_mapped_v6(255, 255, 255, 254, port: 65535).should eq Socket::IPAddress.new(\"::ffff:255.255.255.254\", 65535)\n    end\n\n    it \"raises on out of bound field\" do\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4_mapped_v6(256, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4_mapped_v6(0, 256, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4_mapped_v6(0, 0, 256, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: 256\") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, 256, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4_mapped_v6(-1, 0, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4_mapped_v6(0, -1, 0, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4_mapped_v6(0, 0, -1, 0, port: 0) }\n      expect_raises(Socket::Error, \"Invalid IPv4 field: -1\") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, -1, port: 0) }\n    end\n\n    it \"raises on out of bound port number\" do\n      expect_raises(Socket::Error, \"Invalid port number: 65536\") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, 0, port: 65536) }\n      expect_raises(Socket::Error, \"Invalid port number: -1\") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, 0, port: -1) }\n    end\n\n    it \"constructs from StaticArray\" do\n      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(0, 0, 0, 0), 0).should eq Socket::IPAddress.new(\"::ffff:0.0.0.0\", 0)\n      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(127, 0, 0, 1), 1234).should eq Socket::IPAddress.new(\"::ffff:127.0.0.1\", 1234)\n      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 0, 1), 8081).should eq Socket::IPAddress.new(\"::ffff:192.168.0.1\", 8081)\n      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(255, 255, 255, 254), 65535).should eq Socket::IPAddress.new(\"::ffff:255.255.255.254\", 65535)\n    end\n  end\n\n  it \".valid_v6?\" do\n    Socket::IPAddress.valid_v6?(\"::1\").should be_true\n    Socket::IPAddress.valid_v6?(\"x\").should be_false\n    Socket::IPAddress.valid_v6?(\"127.0.0.1\").should be_false\n  end\n\n  it \".valid_v4?\" do\n    Socket::IPAddress.valid_v4?(\"127.0.0.1\").should be_true\n    Socket::IPAddress.valid_v4?(\"::1\").should be_false\n    Socket::IPAddress.valid_v4?(\"x\").should be_false\n  end\n\n  it \".valid?\" do\n    Socket::IPAddress.valid?(\"127.0.0.1\").should be_true\n    Socket::IPAddress.valid?(\"::1\").should be_true\n    Socket::IPAddress.valid?(\"x\").should be_false\n  end\n\n  it \"#loopback?\" do\n    Socket::IPAddress.new(\"127.0.0.1\", 0).loopback?.should be_true\n    Socket::IPAddress.new(\"127.255.255.254\", 0).loopback?.should be_true\n    Socket::IPAddress.new(\"128.0.0.1\", 0).loopback?.should be_false\n    Socket::IPAddress.new(\"0.0.0.0\", 0).loopback?.should be_false\n    Socket::IPAddress.new(\"::1\", 0).loopback?.should be_true\n    Socket::IPAddress.new(\"0000:0000:0000:0000:0000:0000:0000:0001\", 0).loopback?.should be_true\n    Socket::IPAddress.new(\"::2\", 0).loopback?.should be_false\n    Socket::IPAddress.new(Socket::IPAddress::LOOPBACK, 0).loopback?.should be_true\n    Socket::IPAddress.new(Socket::IPAddress::LOOPBACK6, 0).loopback?.should be_true\n    Socket::IPAddress.new(\"::ffff:127.0.0.1\", 0).loopback?.should be_true\n    Socket::IPAddress.new(\"::ffff:127.0.1.1\", 0).loopback?.should be_true\n    Socket::IPAddress.new(\"::ffff:1.0.0.1\", 0).loopback?.should be_false\n  end\n\n  it \"#unspecified?\" do\n    Socket::IPAddress.new(\"0.0.0.0\", 0).unspecified?.should be_true\n    Socket::IPAddress.new(\"127.0.0.1\", 0).unspecified?.should be_false\n    Socket::IPAddress.new(\"::\", 0).unspecified?.should be_true\n    Socket::IPAddress.new(\"0000:0000:0000:0000:0000:0000:0000:0000\", 0).unspecified?.should be_true\n    Socket::IPAddress.new(Socket::IPAddress::UNSPECIFIED, 0).unspecified?.should be_true\n    Socket::IPAddress.new(Socket::IPAddress::UNSPECIFIED6, 0).unspecified?.should be_true\n  end\n\n  it \".valid_port?\" do\n    Socket::IPAddress.valid_port?(0).should be_true\n    Socket::IPAddress.valid_port?(80).should be_true\n    Socket::IPAddress.valid_port?(65_535).should be_true\n\n    Socket::IPAddress.valid_port?(-1).should be_false\n    Socket::IPAddress.valid_port?(65_536).should be_false\n  end\n\n  it \"#private?\" do\n    Socket::IPAddress.new(\"192.168.0.1\", 0).private?.should be_true\n    Socket::IPAddress.new(\"192.100.0.1\", 0).private?.should be_false\n    Socket::IPAddress.new(\"172.16.0.1\", 0).private?.should be_true\n    Socket::IPAddress.new(\"172.10.0.1\", 0).private?.should be_false\n    Socket::IPAddress.new(\"10.0.0.1\", 0).private?.should be_true\n    Socket::IPAddress.new(\"1.1.1.1\", 0).private?.should be_false\n    Socket::IPAddress.new(\"fd00::1\", 0).private?.should be_true\n    Socket::IPAddress.new(\"fb00::1\", 0).private?.should be_false\n    Socket::IPAddress.new(\"2001:4860:4860::8888\", 0).private?.should be_false\n  end\n\n  it \"#link_local?\" do\n    Socket::IPAddress.new(\"0.0.0.0\", 0).link_local?.should be_false\n    Socket::IPAddress.new(\"127.0.0.1\", 0).link_local?.should be_false\n    Socket::IPAddress.new(\"10.0.0.0\", 0).link_local?.should be_false\n    Socket::IPAddress.new(\"172.16.0.0\", 0).link_local?.should be_false\n    Socket::IPAddress.new(\"192.168.0.0\", 0).link_local?.should be_false\n\n    Socket::IPAddress.new(\"169.254.1.1\", 0).link_local?.should be_true\n    Socket::IPAddress.new(\"169.254.254.255\", 0).link_local?.should be_true\n\n    Socket::IPAddress.new(\"::1\", 0).link_local?.should be_false\n    Socket::IPAddress.new(\"::\", 0).link_local?.should be_false\n    Socket::IPAddress.new(\"fb84:8bf7:e905::1\", 0).link_local?.should be_false\n\n    Socket::IPAddress.new(\"fe80::4860:4860:4860:1234\", 0).link_local?.should be_true\n  end\n\n  it \"#==\" do\n    Socket::IPAddress.new(\"127.0.0.1\", 8080).should eq Socket::IPAddress.new(\"127.0.0.1\", 8080)\n    Socket::IPAddress.new(\"127.0.0.1\", 8080).hash.should eq Socket::IPAddress.new(\"127.0.0.1\", 8080).hash\n\n    Socket::IPAddress.new(\"127.0.0.1\", 8080).should_not eq Socket::IPAddress.new(\"127.0.0.1\", 8081)\n    Socket::IPAddress.new(\"127.0.0.1\", 8080).hash.should_not eq Socket::IPAddress.new(\"127.0.0.1\", 8081).hash\n\n    Socket::IPAddress.new(\"127.0.0.1\", 8080).should_not eq Socket::IPAddress.new(\"127.0.0.2\", 8080)\n    Socket::IPAddress.new(\"127.0.0.1\", 8080).hash.should_not eq Socket::IPAddress.new(\"127.0.0.2\", 8080).hash\n  end\nend\n\n{% if flag?(:unix) %}\n  describe Socket::UNIXAddress do\n    it \"transforms into a C struct and back\" do\n      path = \"unix_address.sock\"\n\n      addr1 = Socket::UNIXAddress.new(path)\n      addr2 = Socket::UNIXAddress.from(addr1.to_unsafe, addr1.size)\n\n      addr2.family.should eq(addr1.family)\n      addr2.path.should eq(addr1.path)\n      addr2.to_s.should eq(path)\n      addr2 = Socket::UNIXAddress.from(addr1.to_unsafe)\n    end\n\n    it \"raises when path is too long\" do\n      path = \"unix_address-too-long-#{(\"a\" * 2048)}.sock\"\n\n      expect_raises(ArgumentError, \"Path size exceeds the maximum size\") do\n        Socket::UNIXAddress.new(path)\n      end\n    end\n\n    it \"to_s\" do\n      Socket::UNIXAddress.new(\"some_path\").to_s.should eq(\"some_path\")\n    end\n\n    it \"#==\" do\n      Socket::UNIXAddress.new(\"some_path\").should eq Socket::UNIXAddress.new(\"some_path\")\n      Socket::UNIXAddress.new(\"some_path\").hash.should eq Socket::UNIXAddress.new(\"some_path\").hash\n\n      Socket::UNIXAddress.new(\"some_path\").should_not eq Socket::UNIXAddress.new(\"other_path\")\n      Socket::UNIXAddress.new(\"some_path\").hash.should_not eq Socket::UNIXAddress.new(\"other_path\").hash\n    end\n\n    it \"accepts `Path` input\" do\n      Socket::UNIXAddress.new(Path.new(\"some_path\")).should eq Socket::UNIXAddress.new(\"some_path\")\n    end\n\n    describe \".parse\" do\n      it \"parses relative\" do\n        address = Socket::UNIXAddress.parse \"unix://foo.sock\"\n        address.should eq Socket::UNIXAddress.new(\"foo.sock\")\n      end\n\n      it \"parses relative subpath\" do\n        address = Socket::UNIXAddress.parse \"unix://foo/bar.sock\"\n        address.should eq Socket::UNIXAddress.new(\"foo/bar.sock\")\n      end\n\n      it \"parses relative dot\" do\n        address = Socket::UNIXAddress.parse \"unix://./bar.sock\"\n        address.should eq Socket::UNIXAddress.new(\"./bar.sock\")\n      end\n\n      it \"relative with\" do\n        address = Socket::UNIXAddress.parse \"unix://foo:21/bar.sock\"\n        address.should eq Socket::UNIXAddress.new(\"foo:21/bar.sock\")\n      end\n\n      it \"parses absolute\" do\n        address = Socket::UNIXAddress.parse \"unix:///foo.sock\"\n        address.should eq Socket::UNIXAddress.new(\"/foo.sock\")\n      end\n\n      it \"ignores params\" do\n        address = Socket::UNIXAddress.parse \"unix:///foo.sock?bar=baz\"\n        address.should eq Socket::UNIXAddress.new(\"/foo.sock\")\n      end\n\n      it \"fails with missing path\" do\n        expect_raises(Socket::Error, \"Invalid UNIX address: missing path\") do\n          Socket::UNIXAddress.parse \"unix://?foo=bar\"\n        end\n      end\n    end\n  end\n{% end %}\n\ndescribe Socket do\n  # Most of the specs are moved to `.parse_v4_fields?` and `.parse_v6_fields?`,\n  # which are implemented in pure Crystal; the remaining ones here are test\n  # cases that were once known to break on certain platforms when `Socket.ip?`\n  # was still using the system `inet_pton`\n  it \".ip?\" do\n    Socket.ip?(\"1.2.03.4\").should be_false\n    Socket.ip?(\"::012.34.56.78\").should be_false\n    Socket.ip?(\"a:0b:00c:000d:0000e:f::\").should be_false\n    Socket.ip?(\"::1:2:3:4:5:6:7\").should be_true\n  end\n\n  it \"==\" do\n    a = Socket::IPAddress.new(\"127.0.0.1\", 8080)\n    b = Socket::UNIXAddress.new(\"some_path\")\n    c = \"some_path\"\n    (a == a).should be_true\n    (b == b).should be_true\n    (a == b).should be_false\n    (a == c).should be_false\n    (b == c).should be_false\n  end\nend\n"
  },
  {
    "path": "spec/std/socket/addrinfo_spec.cr",
    "content": "require \"spec\"\nrequire \"socket\"\n\ndescribe Socket::Addrinfo, tags: \"network\" do\n  describe \".resolve\" do\n    it \"returns an array\" do\n      addrinfos = Socket::Addrinfo.resolve(\"localhost\", 80, type: Socket::Type::STREAM)\n      typeof(addrinfos).should eq(Array(Socket::Addrinfo))\n      addrinfos.size.should_not eq(0)\n    end\n\n    it \"yields each result\" do\n      Socket::Addrinfo.resolve(\"localhost\", 80, type: Socket::Type::DGRAM) do |addrinfo|\n        typeof(addrinfo).should eq(Socket::Addrinfo)\n      end\n    end\n\n    it \"eventually raises returned error\" do\n      expect_raises(Socket::Error) do\n        Socket::Addrinfo.resolve(\"localhost\", 80, type: Socket::Type::DGRAM) do |addrinfo|\n          Socket::Error.new(\"please fail\")\n        end\n      end\n    end\n\n    it \"raises helpful message on getaddrinfo failure\" do\n      expect_raises(Socket::Addrinfo::Error, \"Hostname lookup for badhostname.unknown failed: \") do\n        Socket::Addrinfo.resolve(\"badhostname.unknown\", 80, type: Socket::Type::DGRAM)\n      end\n    end\n\n    {% if flag?(:win32) %}\n      it \"raises timeout error\" do\n        expect_raises(IO::TimeoutError) do\n          Socket::Addrinfo.resolve(\"badhostname\", 80, type: Socket::Type::STREAM, timeout: 0.milliseconds)\n        end\n      end\n    {% end %}\n  end\n\n  describe \".tcp\" do\n    it \"returns an array\" do\n      addrinfos = Socket::Addrinfo.tcp(\"localhost\", 80)\n      typeof(addrinfos).should eq(Array(Socket::Addrinfo))\n      addrinfos.size.should_not eq(0)\n    end\n\n    it \"yields each result\" do\n      Socket::Addrinfo.tcp(\"localhost\", 80) do |addrinfo|\n        typeof(addrinfo).should eq(Socket::Addrinfo)\n      end\n    end\n\n    {% if flag?(:win32) %}\n      it \"raises timeout error\" do\n        expect_raises(IO::TimeoutError) do\n          Socket::Addrinfo.tcp(\"badhostname\", 80, timeout: 0.milliseconds)\n        end\n      end\n    {% end %}\n  end\n\n  describe \".udp\" do\n    it \"returns an array\" do\n      addrinfos = Socket::Addrinfo.udp(\"localhost\", 80)\n      typeof(addrinfos).should eq(Array(Socket::Addrinfo))\n      addrinfos.size.should_not eq(0)\n    end\n\n    it \"yields each result\" do\n      Socket::Addrinfo.udp(\"localhost\", 80) do |addrinfo|\n        typeof(addrinfo).should eq(Socket::Addrinfo)\n      end\n    end\n\n    {% if flag?(:win32) %}\n      it \"raises timeout error\" do\n        expect_raises(IO::TimeoutError) do\n          Socket::Addrinfo.udp(\"badhostname\", 80, timeout: 0.milliseconds)\n        end\n      end\n    {% end %}\n  end\n\n  describe \"#ip_address\" do\n    it do\n      addrinfos = Socket::Addrinfo.udp(\"localhost\", 80)\n      typeof(addrinfos.first.ip_address).should eq(Socket::IPAddress)\n    end\n  end\n\n  it \"#inspect\" do\n    addrinfos = Socket::Addrinfo.tcp(\"127.0.0.1\", 12345)\n    addrinfos.first.inspect.should eq \"Socket::Addrinfo(127.0.0.1:12345, INET, STREAM, TCP)\"\n\n    addrinfos = Socket::Addrinfo.udp(\"127.0.0.1\", 12345)\n    addrinfos.first.inspect.should eq \"Socket::Addrinfo(127.0.0.1:12345, INET, DGRAM, UDP)\"\n  end\n\n  describe \"Error\" do\n    {% unless flag?(:win32) || flag?(:wasm32) %}\n      # This method is not available on windows/wasm because windows/wasm support was introduced after deprecation.\n      it \".new (deprecated)\" do\n        error = Socket::Addrinfo::Error.new(LibC::EAI_NONAME, \"No address found\", \"foobar.com\")\n        error.os_error.should eq Errno.new(LibC::EAI_NONAME)\n        error.message.not_nil!.should eq \"Hostname lookup for foobar.com failed: No address found\"\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/std/socket/socket_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../../support/tempfile\"\nrequire \"../../support/win32\"\n\ndescribe Socket, tags: \"network\" do\n  describe \".unix\" do\n    it \"creates a unix socket\" do\n      sock = Socket.unix\n      sock.should be_a(Socket)\n      sock.family.should eq(Socket::Family::UNIX)\n      sock.type.should eq(Socket::Type::STREAM)\n\n      # Datagram socket type is not supported on Windows yet:\n      # https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable\n      # https://github.com/microsoft/WSL/issues/5272\n      {% unless flag?(:win32) %}\n        sock = Socket.unix(Socket::Type::DGRAM)\n        sock.type.should eq(Socket::Type::DGRAM)\n      {% end %}\n\n      {% unless flag?(:freebsd) %}\n        # for some reason this doesn't fail on freebsd\n        error = expect_raises(Socket::Error) do\n          TCPSocket.new(family: :unix)\n        end\n        error.os_error.should eq({% if flag?(:win32) %}\n          WinError::WSAEPROTONOSUPPORT\n        {% elsif flag?(:wasi) %}\n          WasiError::PROTONOSUPPORT\n        {% else %}\n          Errno.new(LibC::EPROTONOSUPPORT)\n        {% end %})\n      {% end %}\n    end\n  end\n\n  describe \"#tty?\" do\n    it \"with non TTY\" do\n      Socket.new(Socket::Family::INET, Socket::Type::STREAM, Socket::Protocol::TCP).tty?.should be_false\n    end\n  end\n\n  it \".accept\" do\n    client_done = Channel(Nil).new\n    server = Socket.new(Socket::Family::INET, Socket::Type::STREAM, Socket::Protocol::TCP)\n\n    begin\n      port = unused_local_tcp_port\n      server.bind(\"0.0.0.0\", port)\n      server.listen\n\n      spawn do\n        TCPSocket.new(\"127.0.0.1\", port).close\n      ensure\n        client_done.send nil\n      end\n\n      client = server.accept\n      begin\n        client.family.should eq(Socket::Family::INET)\n        client.type.should eq(Socket::Type::STREAM)\n        client.protocol.should eq(Socket::Protocol::TCP)\n        client.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n      ensure\n        client.close\n      end\n    ensure\n      server.close\n      client_done.receive\n    end\n  end\n\n  it \"accept raises timeout error if read_timeout is specified\" do\n    server = Socket.new(Socket::Family::INET, Socket::Type::STREAM, Socket::Protocol::TCP)\n    port = unused_local_tcp_port\n    server.bind(\"0.0.0.0\", port)\n    server.read_timeout = 0.1.seconds\n    server.listen\n\n    expect_raises(IO::TimeoutError) { server.accept }\n    expect_raises(IO::TimeoutError) { server.accept? }\n  ensure\n    server.try &.close\n  end\n\n  it \"sends messages\" do\n    port = unused_local_tcp_port\n    server = Socket.tcp(Socket::Family::INET)\n    server.bind(\"127.0.0.1\", port)\n    server.listen\n    address = Socket::IPAddress.new(\"127.0.0.1\", port)\n    spawn do\n      client = server.not_nil!.accept\n      client.gets.should eq \"foo\"\n      client.puts \"bar\"\n    ensure\n      client.try &.close\n    end\n    socket = Socket.tcp(Socket::Family::INET)\n    socket.connect(address)\n    socket.puts \"foo\"\n    socket.gets.should eq \"bar\"\n  ensure\n    socket.try &.close\n    server.try &.close\n  end\n\n  # Datagram socket type is not supported on Windows yet\n  {% unless flag?(:win32) %}\n    it \"sends datagram over unix socket\" do\n      with_tempfile(\"datagram_unix\") do |path|\n        server = Socket.unix(Socket::Type::DGRAM)\n        server.bind Socket::UNIXAddress.new(path)\n\n        client = Socket.unix(Socket::Type::DGRAM)\n        client.connect Socket::UNIXAddress.new(path)\n        client.send \"foo\"\n\n        message, _ = server.receive\n        message.should eq \"foo\"\n      end\n    end\n  {% end %}\n\n  describe \"#bind\" do\n    each_ip_family do |family, _, any_address|\n      it \"binds to port\" do\n        socket = TCPSocket.new family\n        socket.bind(any_address, 0)\n        socket.listen\n\n        address = socket.local_address.as(Socket::IPAddress)\n        address.address.should eq(any_address)\n        address.port.should be > 0\n      ensure\n        socket.try &.close\n      end\n\n      it \"binds to port using Socket::IPAddress\" do\n        socket = TCPSocket.new family\n        socket.bind Socket::IPAddress.new(any_address, 0)\n        socket.listen\n\n        address = socket.local_address.as(Socket::IPAddress)\n        address.address.should eq(any_address)\n        address.port.should be > 0\n      ensure\n        socket.try &.close\n      end\n\n      it \"binds to port using default IP\" do\n        socket = TCPSocket.new family\n        socket.bind unused_local_tcp_port\n        socket.listen\n\n        address = socket.local_address.as(Socket::IPAddress)\n        address.address.should eq(any_address)\n        address.port.should be > 0\n\n        socket.close\n\n        socket = UDPSocket.new family\n        socket.bind unused_local_udp_port\n        socket.close\n      end\n    end\n  end\n\n  it \"closes on exec by default\" do\n    socket = Socket.new(Socket::Family::INET, Socket::Type::STREAM, Socket::Protocol::TCP)\n    socket.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n  end\n\n  it \".set_blocking and .get_blocking\" do\n    socket = Socket.tcp(Socket::Family::INET)\n    fd = socket.fd\n\n    Socket.set_blocking(fd, true)\n    {% if flag?(:win32) %}\n      expect_raises(NotImplementedError) { IO::FileDescriptor.get_blocking(fd) }\n    {% else %}\n      Socket.get_blocking(fd).should be_true\n    {% end %}\n\n    Socket.set_blocking(fd, false)\n    {% if flag?(:win32) %}\n      expect_raises(NotImplementedError) { IO::FileDescriptor.get_blocking(fd) }\n    {% else %}\n      Socket.get_blocking(fd).should be_false\n    {% end %}\n  end\n\n  describe \"#finalize\" do\n    it \"does not flush\" do\n      port = unused_local_tcp_port\n      server = Socket.tcp(Socket::Family::INET)\n      server.bind(\"127.0.0.1\", port)\n      server.listen\n\n      spawn do\n        client = server.not_nil!.accept\n        client.sync = false\n        client << \"foo\"\n        client.flush\n        client << \"bar\"\n        client.finalize\n      ensure\n        client.try(&.close) rescue nil\n      end\n\n      socket = Socket.tcp(Socket::Family::INET)\n      socket.connect(Socket::IPAddress.new(\"127.0.0.1\", port))\n\n      socket.gets.should eq \"foo\"\n    ensure\n      socket.try &.close\n      server.try &.close\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/socket/spec_helper.cr",
    "content": "require \"spec\"\nrequire \"socket\"\n\nCLOSE_ON_EXEC_AVAILABLE = {{ !flag?(:win32) }}\n\nmodule SocketSpecHelper\n  class_getter?(supports_ipv6 : Bool) { detect_supports_ipv6? }\n\n  private def self.detect_supports_ipv6? : Bool\n    TCPServer.open(\"::1\", 0) { return true }\n    false\n  rescue Socket::Error\n    false\n  end\nend\n\ndef pending_ipv6(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n  if SocketSpecHelper.supports_ipv6?\n    it(description, file: file, line: line, end_line: end_line, &block)\n  else\n    pending(description, file: file, line: line, end_line: end_line)\n  end\nend\n\ndef each_ip_family(&block : Socket::Family, String, String ->)\n  describe \"using IPv4\" do\n    block.call Socket::Family::INET, \"127.0.0.1\", \"0.0.0.0\"\n  end\n\n  if SocketSpecHelper.supports_ipv6?\n    describe \"using IPv6\" do\n      block.call Socket::Family::INET6, \"::1\", \"::\"\n    end\n  else\n    pending \"using IPv6\"\n  end\nend\n\ndef unused_local_tcp_port\n  TCPServer.open(Socket::IPAddress::UNSPECIFIED, 0) do |server|\n    server.local_address.port\n  end\nend\n\ndef unused_local_udp_port\n  socket = UDPSocket.new\n  begin\n    socket.bind(Socket::IPAddress::UNSPECIFIED, 0)\n    socket.local_address.port\n  ensure\n    socket.close\n  end\nend\n"
  },
  {
    "path": "spec/std/socket/tcp_server_spec.cr",
    "content": "{% skip_file if flag?(:wasm32) %}\n\nrequire \"./spec_helper\"\n\ndescribe TCPServer, tags: \"network\" do\n  describe \".new\" do\n    each_ip_family do |family, address|\n      it \"listens on local address\" do\n        port = unused_local_tcp_port\n\n        server = TCPServer.new(address, port)\n\n        server.reuse_port?.should be_false\n        server.reuse_address?.should be_true\n\n        local_address = Socket::IPAddress.new(address, port)\n        server.local_address.should eq local_address\n\n        server.closed?.should be_false\n\n        server.close\n\n        server.closed?.should be_true\n        expect_raises(Socket::Error, \"getsockname: \") do\n          server.local_address\n        end\n      end\n\n      it \"binds to port 0\" do\n        server = TCPServer.new(address, 0)\n\n        begin\n          server.local_address.address.should eq(address)\n          server.local_address.port.should be > 0\n        ensure\n          server.close\n        end\n      end\n\n      it \"raises when port is negative\" do\n        error = expect_raises(Socket::Addrinfo::Error) do\n          TCPServer.new(address, -12)\n        end\n        error.os_error.should eq({% if flag?(:win32) %}\n          WinError::WSATYPE_NOT_FOUND\n        {% elsif (flag?(:linux) && !flag?(:android)) || flag?(:openbsd) %}\n          Errno.new(LibC::EAI_SERVICE)\n        {% else %}\n          Errno.new(LibC::EAI_NONAME)\n        {% end %})\n      end\n\n      describe \"reuse_port\" do\n        it \"raises when port is in use\" do\n          TCPServer.open(address, 0) do |server|\n            expect_raises(Socket::BindError, \"Could not bind to '#{address}:#{server.local_address.port}': \") do\n              TCPServer.open(address, server.local_address.port) { }\n            end\n          end\n        end\n\n        it \"raises when not binding with reuse_port\" do\n          TCPServer.open(address, 0, reuse_port: true) do |server|\n            expect_raises(Socket::BindError) do\n              TCPServer.open(address, server.local_address.port) { }\n            end\n          end\n        end\n\n        it \"raises when port is not ready to be reused\" do\n          TCPServer.open(address, 0) do |server|\n            expect_raises(Socket::BindError) do\n              TCPServer.open(address, server.local_address.port, reuse_port: true) { }\n            end\n          end\n        end\n\n        it \"binds to used port with reuse_port = true\" do\n          TCPServer.open(address, 0, reuse_port: true) do |server|\n            TCPServer.open(address, server.local_address.port, reuse_port: true) { }\n          end\n        end\n      end\n    end\n\n    describe \"address resolution\" do\n      it \"binds to localhost\" do\n        server = TCPServer.new(\"localhost\", unused_local_tcp_port)\n        server.close\n      end\n\n      it \"raises when host doesn't exist\" do\n        err = expect_raises(Socket::Error, \"Hostname lookup for doesnotexist.example.org. failed\") do\n          TCPServer.new(\"doesnotexist.example.org.\", 12345)\n        end\n        # FIXME: Resolve special handling for win32. The error code handling should be identical.\n        {% if flag?(:win32) %}\n          [WinError::WSAHOST_NOT_FOUND, WinError::WSATRY_AGAIN].should contain err.os_error\n        {% elsif flag?(:android) || flag?(:netbsd) || flag?(:openbsd) %}\n          err.os_error.should eq(Errno.new(LibC::EAI_NODATA))\n        {% else %}\n          [Errno.new(LibC::EAI_NONAME), Errno.new(LibC::EAI_NODATA), Errno.new(LibC::EAI_AGAIN)].should contain err.os_error\n        {% end %}\n      end\n\n      it \"raises (rather than segfault on darwin) when host doesn't exist and port is 0\" do\n        err = expect_raises(Socket::Error, \"Hostname lookup for doesnotexist.example.org. failed\") do\n          TCPServer.new(\"doesnotexist.example.org.\", 0)\n        end\n        # FIXME: Resolve special handling for win32. The error code handling should be identical.\n        {% if flag?(:win32) %}\n          [WinError::WSAHOST_NOT_FOUND, WinError::WSATRY_AGAIN].should contain err.os_error\n        {% elsif flag?(:android) || flag?(:netbsd) || flag?(:openbsd) %}\n          err.os_error.should eq(Errno.new(LibC::EAI_NODATA))\n        {% else %}\n          [Errno.new(LibC::EAI_NONAME), Errno.new(LibC::EAI_NODATA), Errno.new(LibC::EAI_AGAIN)].should contain err.os_error\n        {% end %}\n      end\n    end\n\n    it \"binds to all interfaces\" do\n      port = unused_local_tcp_port\n      TCPServer.open(Socket::IPAddress::UNSPECIFIED, port) do |server|\n        server.local_address.port.should eq port\n      end\n    end\n  end\n\n  {% if flag?(:linux) || flag?(:solaris) %}\n    pending \"settings\"\n  {% else %}\n    it \"settings\" do\n      TCPServer.open(\"::\", unused_local_tcp_port) do |server|\n        (server.recv_buffer_size = 42).should eq 42\n        server.recv_buffer_size.should eq 42\n      end\n    end\n  {% end %}\n\n  describe \"accept\" do\n    it \"sets close on exec flag\" do\n      TCPServer.open(\"localhost\", 0) do |server|\n        TCPSocket.open(\"localhost\", server.local_address.port) do |client|\n          server.accept? do |sock|\n            sock.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n          end\n        end\n      end\n    end\n\n    it \"supports IPv6 dual stack\" do\n      server = TCPServer.new(:inet6)\n      server.ipv6_only = false\n      server.bind(\"::\", 0)\n      server.listen\n\n      TCPSocket.open(\"127.0.0.1\", server.local_address.port) do |client|\n        server.accept? do |sock|\n          sock.ipv6_only?.should be_false\n\n          # should raise when changing ipv6_only when not applicable\n          expect_raises(Socket::Error, /invalid argument/i) do\n            sock.ipv6_only = true\n          end\n        end\n      end\n\n      server.close\n    end\n\n    it \"supports IPv6 only\" do\n      server = TCPServer.new(:inet6)\n      server.ipv6_only = true\n      server.bind(\"::\", 0)\n      server.listen\n\n      expect_raises(Socket::ConnectError) do\n        TCPSocket.new(\"127.0.0.1\", server.local_address.port, connect_timeout: 1.second)\n      end\n      server.close\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/socket/tcp_socket_spec.cr",
    "content": "{% skip_file if flag?(:wasm32) %}\n\nrequire \"./spec_helper\"\nrequire \"../../support/win32\"\n\ndescribe TCPSocket, tags: \"network\" do\n  describe \"#connect\" do\n    each_ip_family do |family, address|\n      it \"connects to server\" do\n        port = unused_local_tcp_port\n\n        TCPServer.open(address, port) do |server|\n          TCPSocket.open(address, port) do |client|\n            client.local_address.address.should eq address\n\n            sock = server.accept\n\n            sock.closed?.should be_false\n            client.closed?.should be_false\n\n            sock.local_address.port.should eq(port)\n            sock.local_address.address.should eq(address)\n\n            client.remote_address.port.should eq(port)\n            sock.remote_address.address.should eq address\n          end\n        end\n      end\n\n      {% if flag?(:dragonfly) %}\n        # FIXME: this spec regularly hangs in a vagrant/libvirt VM\n        pending \"raises when connection is refused\"\n      {% else %}\n        it \"raises when connection is refused\" do\n          port = unused_local_tcp_port\n\n          expect_raises(Socket::ConnectError, \"Error connecting to '#{address}:#{port}'\") do\n            TCPSocket.new(address, port)\n          end\n        end\n      {% end %}\n\n      it \"raises when port is negative\" do\n        error = expect_raises(Socket::Addrinfo::Error) do\n          TCPSocket.new(address, -12)\n        end\n        error.os_error.should eq({% if flag?(:win32) %}\n          WinError::WSATYPE_NOT_FOUND\n        {% elsif (flag?(:linux) && !flag?(:android)) || flag?(:openbsd) %}\n          Errno.new(LibC::EAI_SERVICE)\n        {% else %}\n          Errno.new(LibC::EAI_NONAME)\n        {% end %})\n      end\n\n      {% if flag?(:dragonfly) %}\n        # FIXME: this spec regularly hangs in a vagrant/libvirt VM\n        pending \"raises when port is zero\"\n      {% else %}\n        it \"raises when port is zero\" do\n          expect_raises(Socket::ConnectError) do\n            TCPSocket.new(address, 0)\n          end\n        end\n      {% end %}\n    end\n\n    describe \"address resolution\" do\n      it \"connects to localhost\" do\n        port = unused_local_tcp_port\n\n        TCPServer.open(\"localhost\", port) do |server|\n          TCPSocket.open(\"localhost\", port) do |client|\n            server.accept\n          end\n        end\n      end\n\n      it \"raises when host doesn't exist\" do\n        err = expect_raises(Socket::Error, \"Hostname lookup for doesnotexist.example.org. failed\") do\n          TCPSocket.new(\"doesnotexist.example.org.\", 12345)\n        end\n        # FIXME: Resolve special handling for win32. The error code handling should be identical.\n        {% if flag?(:win32) %}\n          [WinError::WSAHOST_NOT_FOUND, WinError::WSATRY_AGAIN].should contain err.os_error\n        {% elsif flag?(:android) || flag?(:netbsd) || flag?(:openbsd) %}\n          err.os_error.should eq(Errno.new(LibC::EAI_NODATA))\n        {% else %}\n          [Errno.new(LibC::EAI_NONAME), Errno.new(LibC::EAI_NODATA), Errno.new(LibC::EAI_AGAIN)].should contain err.os_error\n        {% end %}\n      end\n\n      it \"raises (rather than segfault on darwin) when host doesn't exist and port is 0\" do\n        err = expect_raises(Socket::Error, \"Hostname lookup for doesnotexist.example.org. failed\") do\n          TCPSocket.new(\"doesnotexist.example.org.\", 0)\n        end\n        # FIXME: Resolve special handling for win32. The error code handling should be identical.\n        {% if flag?(:win32) %}\n          [WinError::WSAHOST_NOT_FOUND, WinError::WSATRY_AGAIN].should contain err.os_error\n        {% elsif flag?(:android) || flag?(:netbsd) || flag?(:openbsd) %}\n          err.os_error.should eq(Errno.new(LibC::EAI_NODATA))\n        {% else %}\n          [Errno.new(LibC::EAI_NONAME), Errno.new(LibC::EAI_NODATA), Errno.new(LibC::EAI_AGAIN)].should contain err.os_error\n        {% end %}\n      end\n    end\n\n    it \"fails to connect IPv6 to IPv4 server\" do\n      pending! \"IPv6 is unavailable\" unless SocketSpecHelper.supports_ipv6?\n\n      port = unused_local_tcp_port\n\n      TCPServer.open(\"0.0.0.0\", port) do |server|\n        expect_raises(Socket::ConnectError, \"Error connecting to '::1:#{port}'\") do\n          TCPSocket.new(\"::1\", port)\n        end\n      end\n    end\n  end\n\n  {% if flag?(:dragonfly) %}\n    # FIXME: these specs regularly hang in a vagrant/libvirt VM\n    pending \"sync from server\"\n    pending \"settings\"\n    pending \"fails when connection is refused\"\n    pending \"sends and receives messages\"\n    pending \"sends and receives messages (fibers & channels)\"\n  {% else %}\n    it \"sync from server\" do\n      port = unused_local_tcp_port\n\n      TCPServer.open(Socket::IPAddress::UNSPECIFIED, port) do |server|\n        TCPSocket.open(\"localhost\", port) do |client|\n          sock = server.accept\n          sock.sync?.should eq(server.sync?)\n        end\n\n        # test sync flag propagation after accept\n        server.sync = !server.sync?\n\n        TCPSocket.open(\"localhost\", port) do |client|\n          sock = server.accept\n          sock.sync?.should eq(server.sync?)\n        end\n      end\n    end\n\n    it \"settings\" do\n      port = unused_local_tcp_port\n\n      TCPServer.open(Socket::IPAddress::UNSPECIFIED, port) do |server|\n        TCPSocket.open(\"localhost\", port) do |client|\n          # test protocol specific socket options\n          (client.tcp_nodelay = true).should be_true\n          client.tcp_nodelay?.should be_true\n          (client.tcp_nodelay = false).should be_false\n          client.tcp_nodelay?.should be_false\n\n          {% unless flag?(:openbsd) || flag?(:netbsd) %}\n            (client.tcp_keepalive_idle = 42).should eq 42\n            client.tcp_keepalive_idle.should eq 42\n            (client.tcp_keepalive_interval = 42).should eq 42\n            client.tcp_keepalive_interval.should eq 42\n            (client.tcp_keepalive_count = 42).should eq 42\n            client.tcp_keepalive_count.should eq 42\n          {% end %}\n        end\n      end\n    end\n\n    it \"fails when connection is refused\" do\n      port = TCPServer.open(\"localhost\", 0) do |server|\n        server.local_address.port\n      end\n\n      expect_raises(Socket::ConnectError, \"Error connecting to 'localhost:#{port}'\") do\n        TCPSocket.new(\"localhost\", port)\n      end\n    end\n\n    it \"sends and receives messages\" do\n      port = unused_local_tcp_port\n\n      TCPServer.open(\"::\", port) do |server|\n        TCPSocket.open(\"localhost\", port) do |client|\n          sock = server.accept\n\n          client << \"ping\"\n          sock.gets(4).should eq(\"ping\")\n          sock << \"pong\"\n          client.gets(4).should eq(\"pong\")\n        end\n      end\n    end\n\n    it \"sends and receives messages (fibers & channels)\" do\n      port = unused_local_tcp_port\n\n      channel = Channel(Exception?).new\n      spawn do\n        TCPServer.open(Socket::IPAddress::UNSPECIFIED, port) do |server|\n          channel.send nil\n          sock = server.accept\n          sock.read_timeout = 3.second\n          sock.write_timeout = 3.second\n\n          sock.gets(4).should eq(\"ping\")\n          sock << \"pong\"\n          channel.send nil\n        end\n      rescue exc\n        channel.send exc\n      end\n\n      if exc = channel.receive\n        raise exc\n      end\n\n      TCPSocket.open(\"localhost\", port) do |client|\n        client.read_timeout = 3.second\n        client.write_timeout = 3.second\n        client << \"ping\"\n        client.gets(4).should eq(\"pong\")\n      end\n\n      if exc = channel.receive\n        raise exc\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/socket/udp_socket_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"socket\"\n\ndescribe UDPSocket, tags: \"network\" do\n  # Note: This spec fails with a IPv6 address. See pending below.\n  it \"#remote_address resets after connect\" do\n    socket = UDPSocket.new\n    socket.connect(\"127.0.0.1\", 1)\n    socket.remote_address.port.should eq 1\n    socket.connect(\"127.0.0.1\", 2)\n    socket.remote_address.port.should eq 2\n    socket.close\n  end\n\n  pending \"#connect with a IPv6 address\" do\n    socket = UDPSocket.new\n    socket.connect(\"::1\", 1)\n    socket.close\n  end\n\n  it \"supports IPv6 dual stack\" do\n    socket = UDPSocket.new(:inet6)\n\n    socket.ipv6_only = false\n    socket.ipv6_only?.should be_false\n\n    socket.ipv6_only = true\n    socket.ipv6_only?.should be_true\n\n    socket = UDPSocket.new\n    expect_raises(Socket::Error, \"Unsupported IP address family: INET. For use with IPv6 only\") do\n      socket.ipv6_only?\n    end\n    expect_raises(Socket::Error, \"Unsupported IP address family: INET. For use with IPv6 only\") do\n      socket.ipv6_only = false\n    end\n  end\n\n  each_ip_family do |family, address, unspecified_address|\n    it \"#bind\" do\n      port = unused_local_udp_port\n      socket = UDPSocket.new(family)\n      socket.bind(address, port)\n      socket.local_address.should eq(Socket::IPAddress.new(address, port))\n      socket.close\n      socket = UDPSocket.new(family)\n      socket.bind(address, 0)\n      socket.local_address.address.should eq address\n    ensure\n      socket.try &.close\n    end\n\n    it \"sends and receives messages\" do\n      port = unused_local_udp_port\n\n      server = UDPSocket.new(family)\n      server.bind(address, port)\n      server.local_address.should eq(Socket::IPAddress.new(address, port))\n\n      client = UDPSocket.new(family)\n      client.bind(address, 0)\n\n      client.send \"message\", to: server.local_address\n      server.receive.should eq({\"message\", client.local_address})\n\n      client.connect(address, port)\n      client.local_address.family.should eq(family)\n      client.local_address.address.should eq(address)\n      client.remote_address.should eq(Socket::IPAddress.new(address, port))\n\n      client.send \"message\"\n      server.receive.should eq({\"message\", client.local_address})\n\n      client.send(\"laus deo semper\")\n\n      buffer = uninitialized UInt8[256]\n\n      bytes_read, client_addr = server.receive(buffer.to_slice)\n      message = String.new(buffer.to_slice[0, bytes_read])\n      message.should eq(\"laus deo semper\")\n\n      client.send(\"laus deo semper\")\n\n      # WSA errors with WSAEMSGSIZE if the buffer is not large enough to receive the message\n      {% unless flag?(:win32) %}\n        bytes_read, client_addr = server.receive(buffer.to_slice[0, 4])\n        message = String.new(buffer.to_slice[0, bytes_read])\n        message.should eq(\"laus\")\n      {% end %}\n\n      client.close\n      server.close\n    end\n\n    if {{ flag?(:darwin) }}\n      # Darwin is failing to join IPv6 multicast groups on older versions.\n      # However this is known to work on macOS Mojave with Darwin 18.2.0.\n      # Darwin also has a bug that prevents selecting the \"default\" interface.\n      # https://lists.apple.com/archives/darwin-kernel/2014/Mar/msg00012.html\n      # Since macOS Sequoia (version 15), the ability to send UDP multicasts\n      # seems to have some extra requirements.\n      pending \"joins and transmits to multicast groups\"\n    elsif {{ flag?(:dragonfly) }} && family == Socket::Family::INET6\n      # TODO: figure out why updating `multicast_loopback` produces a\n      # `setsockopt 9: Can't assign requested address\n      pending \"joins and transmits to multicast groups\"\n    elsif {{ flag?(:solaris) }} && family == Socket::Family::INET\n      # TODO: figure out why updating `multicast_loopback` produces a\n      # `setsockopt 18: Invalid argument` error\n      pending \"joins and transmits to multicast groups\"\n    elsif {{ flag?(:freebsd) }} && family == Socket::Family::INET6\n      # FIXME: fails with \"Error sending datagram to [ipv6]:port: Network is unreachable\"\n      pending \"joins and transmits to multicast groups\"\n    elsif {{ flag?(:netbsd) }} && family == Socket::Family::INET6\n      # FIXME: fails with \"setsockopt: EADDRNOTAVAIL\"\n      pending \"joins and transmits to multicast groups\"\n    elsif {{ flag?(:openbsd) }}\n      # FIXME: fails with \"setsockopt: EINVAL (ipv4) or EADDRNOTAVAIL (ipv6)\"\n      pending \"joins and transmits to multicast groups\"\n    else\n      it \"joins and transmits to multicast groups\" do\n        udp = UDPSocket.new(family)\n        port = unused_local_udp_port\n        udp.bind(unspecified_address, port)\n\n        udp.multicast_loopback = false\n        udp.multicast_loopback?.should be_false\n\n        udp.multicast_hops = 4\n        udp.multicast_hops.should eq(4)\n        udp.multicast_hops = 0\n        udp.multicast_hops.should eq(0)\n\n        addr = case family\n               when Socket::Family::INET\n                 expect_raises(Socket::Error, \"Unsupported IP address family: INET. For use with IPv6 only\") do\n                   udp.multicast_interface 0\n                 end\n\n                 begin\n                   udp.multicast_interface Socket::IPAddress.new(unspecified_address, 0)\n                 rescue e : Socket::Error\n                   if e.os_error == Errno::ENOPROTOOPT\n                     pending!(\"Multicast device selection not available on this host\")\n                   else\n                     raise e\n                   end\n                 end\n\n                 Socket::IPAddress.new(\"224.0.0.254\", port)\n               when Socket::Family::INET6\n                 expect_raises(Socket::Error, \"Unsupported IP address family: INET6. For use with IPv4 only\") do\n                   udp.multicast_interface(Socket::IPAddress.new(unspecified_address, 0))\n                 end\n\n                 begin\n                   udp.multicast_interface(0)\n                 rescue e : Socket::Error\n                   if e.os_error == Errno::ENOPROTOOPT\n                     pending!(\"Multicast device selection not available on this host\")\n                   else\n                     raise e\n                   end\n                 end\n\n                 Socket::IPAddress.new(\"ff02::102\", port)\n               else\n                 raise \"Unsupported IP address family: #{family}\"\n               end\n\n        begin\n          udp.join_group(addr)\n        rescue e : Socket::Error\n          if e.os_error == Errno::ENODEV\n            pending!(\"Multicast device selection not available on this host\")\n          else\n            raise e\n          end\n        end\n\n        udp.multicast_loopback = true\n        udp.multicast_loopback?.should be_true\n\n        udp.send(\"testing\", addr)\n        udp.read_timeout = 1.second\n        begin\n          udp.receive[0].should eq(\"testing\")\n        rescue IO::TimeoutError\n          # Since this test doesn't run over the loopback interface, this test\n          # fails when there is a firewall in use. Don't fail in that case.\n        end\n\n        udp.leave_group(addr)\n        udp.send(\"testing\", addr)\n\n        # Test that nothing was received after leaving the multicast group\n        spawn do\n          sleep 100.milliseconds\n          udp.close\n        end\n        expect_raises(IO::Error) { udp.receive }\n        udp.closed?.should be_true\n      end\n    end\n  end\n\n  {% if flag?(:darwin) %}\n    # Fails since macOS Sequoia (version 15) with EHOSTUNREACH. The ability to\n    # send UDP broadcasts might need extra requirements.\n    pending \"sends broadcast message\"\n  {% else %}\n    it \"sends broadcast message\" do\n      server = UDPSocket.new(Socket::Family::INET)\n      server.bind(\"0.0.0.0\", 0)\n      addr = Socket::IPAddress.new(\"255.255.255.255\", server.local_address.port)\n\n      client = UDPSocket.new(Socket::Family::INET)\n      client.broadcast = true\n      client.broadcast?.should be_true\n\n      client.send(\"broadcast\", to: addr).should eq(9)\n      client.close\n\n      server.read_timeout = 1.second\n      begin\n        message, _ = server.receive\n        message.should eq(\"broadcast\")\n      rescue IO::TimeoutError\n        # Since this test doesn't run over the loopback interface, this test\n        # fails when there is a firewall in use. Don't fail in that case.\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/socket/unix_server_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"socket\"\nrequire \"../../support/fibers\"\nrequire \"../../support/channel\"\nrequire \"../../support/tempfile\"\n\ndescribe UNIXServer do\n  describe \".new\" do\n    it \"raises when path is too long\" do\n      with_tempfile(\"unix_server-too_long-#{(\"a\" * 2048)}.sock\") do |path|\n        expect_raises(ArgumentError, \"Path size exceeds the maximum size\") do\n          UNIXServer.new(path)\n        end\n\n        File.exists?(path).should be_false\n      end\n    end\n\n    it \"creates the socket file\" do\n      with_tempfile(\"unix_server.sock\") do |path|\n        UNIXServer.open(path) do\n          File.exists?(path).should be_true\n          File.info(path).type.socket?.should be_true\n        end\n\n        File.exists?(path).should be_false\n      end\n    end\n\n    it \"creates the socket file from `Path`\" do\n      with_tempfile(\"unix_server.sock\") do |path|\n        path = Path.new(path)\n        UNIXServer.open(path) do\n          File.exists?(path).should be_true\n          File.info(path).type.socket?.should be_true\n        end\n\n        File.exists?(path).should be_false\n      end\n    end\n\n    it \"deletes socket file on close\" do\n      with_tempfile(\"unix_server-close.sock\") do |path|\n        server = UNIXServer.new(path)\n        server.close\n\n        File.exists?(path).should be_false\n      end\n    end\n\n    it \"raises when socket file already exists\" do\n      with_tempfile(\"unix_server-twice.sock\") do |path|\n        server = UNIXServer.new(path)\n\n        begin\n          expect_raises(Socket::BindError) do\n            UNIXServer.new(path)\n          end\n        ensure\n          server.close\n        end\n      end\n    end\n\n    it \"won't delete existing file on bind failure\" do\n      with_tempfile(\"unix_server-exist.sock\") do |path|\n        File.write(path, \"\")\n        File.exists?(path).should be_true\n\n        expect_raises(Socket::BindError) do\n          UNIXServer.new(path)\n        end\n\n        File.exists?(path).should be_true\n      end\n    end\n  end\n\n  describe \"accept\" do\n    it \"returns the client UNIXSocket\" do\n      with_tempfile(\"unix_server-accept.sock\") do |path|\n        UNIXServer.open(path) do |server|\n          UNIXSocket.open(path) do |_|\n            client = server.accept\n            client.should be_a(UNIXSocket)\n            client.close\n          end\n        end\n      end\n    end\n\n    it \"raises when server is closed\" do\n      with_tempfile(\"unix_server-closed.sock\") do |path|\n        server = UNIXServer.new(path)\n        ch = Channel(SpecChannelStatus).new(1)\n        exception = nil\n\n        schedule_timeout ch\n\n        f = spawn do\n          begin\n            ch.send(:begin)\n            server.accept\n          rescue ex\n            exception = ex\n          end\n          ch.send(:end)\n        end\n\n        ch.receive.should eq SpecChannelStatus::Begin\n\n        # wait for the server to call accept\n        wait_until_blocked f\n\n        server.close\n        ch.receive.should eq SpecChannelStatus::End\n\n        exception.should be_a(IO::Error)\n        exception.try(&.message).should eq(\"Closed stream\")\n      end\n    end\n  end\n\n  describe \"accept?\" do\n    it \"returns the client UNIXSocket\" do\n      with_tempfile(\"unix_server-accept_.sock\") do |path|\n        UNIXServer.open(path) do |server|\n          UNIXSocket.open(path) do |_|\n            client = server.accept?.not_nil!\n            client.should be_a(UNIXSocket)\n            client.close\n          end\n        end\n      end\n    end\n\n    it \"returns nil when server is closed\" do\n      with_tempfile(\"unix_server-accept2.sock\") do |path|\n        server = UNIXServer.new(path)\n        ch = Channel(SpecChannelStatus).new(1)\n        ret = \"initial\"\n\n        schedule_timeout ch\n\n        f = spawn do\n          ch.send :begin\n          ret = server.accept?\n          ch.send :end\n        end\n\n        ch.receive.should eq SpecChannelStatus::Begin\n\n        # wait for the server to call accept\n        wait_until_blocked f\n\n        server.close\n        ch.receive.should eq SpecChannelStatus::End\n\n        ret.should be_nil\n      end\n    end\n\n    it \"sets close on exec flag\" do\n      with_tempfile(\"unix_socket-accept.sock\") do |path|\n        UNIXServer.open(path) do |server|\n          UNIXSocket.open(path) do |client|\n            server.accept? do |sock|\n              sock.close_on_exec?.should eq CLOSE_ON_EXEC_AVAILABLE\n            end\n          end\n        end\n      end\n    end\n  end\n\n  # Datagram socket type is not supported on Windows yet:\n  # https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable\n  # https://github.com/microsoft/WSL/issues/5272\n  {% unless flag?(:win32) %}\n    describe \"datagrams\" do\n      it \"can send and receive datagrams\" do\n        with_tempfile(\"unix_dgram_server.sock\") do |path|\n          UNIXServer.open(path, Socket::Type::DGRAM) do |s|\n            UNIXSocket.open(path, Socket::Type::DGRAM) do |c|\n              c.send(\"foobar\")\n              msg, _addr = s.receive(512)\n              msg.should eq \"foobar\"\n            end\n          end\n        end\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/socket/unix_socket_spec.cr",
    "content": "require \"spec\"\nrequire \"socket\"\nrequire \"../../support/tempfile\"\n\ndescribe UNIXSocket do\n  it \"raises when path is too long\" do\n    with_tempfile(\"unix_socket-too_long-#{(\"a\" * 2048)}.sock\") do |path|\n      expect_raises(ArgumentError, \"Path size exceeds the maximum size\") { UNIXSocket.new(path) }\n      File.exists?(path).should be_false\n    end\n  end\n\n  it \"sends and receives messages\" do\n    with_tempfile(\"unix_socket.sock\") do |path|\n      UNIXServer.open(path) do |server|\n        server.local_address.family.should eq(Socket::Family::UNIX)\n        server.local_address.path.should eq(path)\n\n        UNIXSocket.open(path) do |client|\n          client.local_address.family.should eq(Socket::Family::UNIX)\n          client.local_address.path.should eq(path)\n\n          server.accept do |sock|\n            sock.local_address.family.should eq(Socket::Family::UNIX)\n            sock.local_address.path.should eq(path)\n\n            sock.remote_address.family.should eq(Socket::Family::UNIX)\n            sock.remote_address.path.should eq(path)\n\n            client << \"ping\"\n            sock.gets(4).should eq(\"ping\")\n            sock << \"pong\"\n            client.gets(4).should eq(\"pong\")\n          end\n        end\n      end\n    end\n  end\n\n  it \"initializes with `Path` paths\" do\n    with_tempfile(\"unix_socket.sock\") do |path|\n      path_path = Path.new(path)\n      UNIXServer.open(path_path) do |server|\n        server.local_address.family.should eq(Socket::Family::UNIX)\n        server.local_address.path.should eq(path)\n\n        UNIXSocket.open(path_path) do |client|\n          client.local_address.family.should eq(Socket::Family::UNIX)\n          client.local_address.path.should eq(path)\n\n          server.accept do |sock|\n            sock.local_address.family.should eq(Socket::Family::UNIX)\n            sock.local_address.path.should eq(path)\n\n            sock.remote_address.family.should eq(Socket::Family::UNIX)\n            sock.remote_address.path.should eq(path)\n          end\n        end\n      end\n    end\n  end\n\n  it \"sync flag after accept\" do\n    with_tempfile(\"unix_socket-accept.sock\") do |path|\n      UNIXServer.open(path) do |server|\n        UNIXSocket.open(path) do |client|\n          server.accept do |sock|\n            sock.sync?.should eq(server.sync?)\n          end\n        end\n\n        server.sync = !server.sync?\n\n        UNIXSocket.open(path) do |client|\n          server.accept do |sock|\n            sock.sync?.should eq(server.sync?)\n          end\n        end\n      end\n    end\n  end\n\n  it \"#send, #receive\" do\n    with_tempfile(\"unix_socket-receive.sock\") do |path|\n      UNIXServer.open(path) do |server|\n        UNIXSocket.open(path) do |client|\n          server.accept do |sock|\n            client.send \"ping\"\n            message, address = sock.receive\n            message.should eq(\"ping\")\n            typeof(address).should eq(Socket::UNIXAddress)\n            address.path.should eq \"\"\n\n            sock.send \"pong\"\n            message, address = client.receive\n            message.should eq(\"pong\")\n            typeof(address).should eq(Socket::UNIXAddress)\n            # The value of path seems to be system-specific. Some implementations\n            # return the socket path, others an empty path.\n            [\"\", path].should contain address.path\n          end\n        end\n      end\n    end\n  end\n\n  # `LibC.socketpair` is not supported in Winsock 2.0 yet:\n  # https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable\n  {% unless flag?(:win32) %}\n    it \"creates a pair of sockets\" do\n      UNIXSocket.pair do |left, right|\n        left.local_address.family.should eq(Socket::Family::UNIX)\n        left.local_address.path.should eq(\"\")\n\n        left << \"ping\"\n        right.gets(4).should eq(\"ping\")\n\n        right << \"pong\"\n        left.gets(4).should eq(\"pong\")\n      end\n    end\n\n    it \"tests read and write timeouts\" do\n      UNIXSocket.pair do |left, right|\n        # BUG: shrink the socket buffers first\n        left.write_timeout = 0.1.milliseconds\n        right.read_timeout = 0.1.milliseconds\n        buf = (\"a\" * IO::DEFAULT_BUFFER_SIZE).to_slice\n\n        expect_raises(IO::TimeoutError, \"Write timed out\") do\n          loop { left.write buf }\n        end\n\n        expect_raises(IO::TimeoutError, \"Read timed out\") do\n          loop { right.read buf }\n        end\n      end\n    end\n\n    it \"tests socket options\" do\n      UNIXSocket.pair do |left, right|\n        size = 12000\n        # linux returns size * 2\n        sizes = [size, size * 2]\n\n        (left.send_buffer_size = size).should eq(size)\n        sizes.should contain(left.send_buffer_size)\n\n        (left.recv_buffer_size = size).should eq(size)\n        sizes.should contain(left.recv_buffer_size)\n\n        left.close_on_exec?.should be_true\n        right.close_on_exec?.should be_true\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/spec/context_spec.cr",
    "content": "require \"./spec_helper\"\n\ndescribe Spec::ExampleGroup do\n  describe \"#randomize\" do\n    it \"by default\" do\n      root = build_spec(\"f.cr\", count: 20)\n\n      before_randomize = all_spec_descriptions(root)\n      root.randomize(Random.new)\n      after_randomize = all_spec_descriptions(root)\n\n      after_randomize.should_not eq before_randomize\n      after_randomize.sort.should eq before_randomize.sort\n    end\n\n    it \"with a seed\" do\n      seed = 12345_u64\n\n      root = build_spec(\"f.cr\", count: 20)\n      root.randomize(Random::PCG32.new(seed))\n      after_randomize1 = all_spec_descriptions(root)\n\n      root = build_spec(\"f.cr\", count: 20)\n      root.randomize(Random::PCG32.new(seed))\n      after_randomize2 = all_spec_descriptions(root)\n\n      after_randomize1.should eq after_randomize2\n    end\n  end\n\n  describe \"#report\" do\n    it \"should include parent's description\" do\n      root = FakeRootContext.new\n      child = Spec::ExampleGroup.new(root, \"child\", \"f.cr\", 1, 10, false, nil)\n      grand_child = Spec::ExampleGroup.new(child, \"grand_child\", \"f.cr\", 2, 9, false, nil)\n\n      grand_child.report(:fail, \"oops\", \"f.cr\", 3, nil, nil)\n\n      root.results_for(:fail).first.description.should eq(\"child grand_child oops\")\n    end\n  end\n\n  describe \"#all_tags\" do\n    it \"should include ancestor tags\" do\n      root = FakeRootContext.new\n      child = Spec::ExampleGroup.new(root, \"child\", \"f.cr\", 1, 10, false, Set{\"A\"})\n      grand_child = Spec::ExampleGroup.new(child, \"grand_child\", \"f.cr\", 2, 9, false, Set{\"B\"})\n      example = Spec::Example.new(grand_child, \"example\", \"f.cr\", 3, 8, false, Set{\"C\"}, nil)\n      other_group = Spec::ExampleGroup.new(root, \"other_group\", \"f.cr\", 11, 20, false, nil)\n\n      child.all_tags.should eq(Set{\"A\"})\n      grand_child.all_tags.should eq(Set{\"A\", \"B\"})\n      example.all_tags.should eq(Set{\"A\", \"B\", \"C\"})\n      other_group.all_tags.should be_empty\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/spec/expectations_spec.cr",
    "content": "require \"spec\"\n\nprivate module MyModule; end\n\nprivate class Foo\n  include MyModule\nend\n\nprivate record NoObjectId, to_unsafe : Int32 do\n  def same?(other : self) : Bool\n    to_unsafe == other.to_unsafe\n  end\nend\n\nprivate class ExceptionWithOverriddenToS < Exception\n  def initialize(message : String, @to_s : String)\n    super(message)\n  end\n\n  def to_s\n    @to_s\n  end\nend\n\ndescribe \"expectations\" do\n  describe \"accept a custom failure message\" do\n    it { 1.should be < 3, \"custom message!\" }\n    it do\n      expect_raises(Spec::AssertionFailed, \"custom message!\") do\n        1.should_not be < 3, \"custom message!\"\n      end\n    end\n  end\n\n  describe \"be\" do\n    it { 1.should be < 3 }\n    it { 2.should be <= 3 }\n    it { 3.should be <= 3 }\n    it { 3.should be >= 3 }\n    it { 4.should be >= 3 }\n    it { 5.should be > 3 }\n  end\n\n  describe \"be\" do\n    it { \"hello\".should be \"hello\" }\n    it do\n      array = [1]\n      array.should_not be [1]\n    end\n\n    it \"works with type that does not implement `#object_id`\" do\n      a = NoObjectId.new(1)\n      a.should be a\n      a.should_not be NoObjectId.new(2)\n    end\n\n    it \"works with module type (#14920)\" do\n      a = Foo.new\n      a.as(MyModule).should be a.as(MyModule)\n    end\n  end\n\n  describe \"be_a\" do\n    it { \"Hello\".should be_a(String) }\n    it { 100_000.should_not be_a(String) }\n    it { 100_000.should be_a(Int32) }\n    it { \"Hello\".should_not be_a(Int32) }\n\n    it \"restricts type on should\" do\n      x = 1 || 'a'\n      y = x.should be_a(Int32)\n      typeof(x).should eq(Int32 | Char)\n      typeof(y).should eq(Int32)\n    end\n\n    it \"restricts type on should_not\" do\n      x = 1 || 'a'\n      y = x.should_not be_a(Char)\n      typeof(x).should eq(Int32 | Char)\n      typeof(y).should eq(Int32)\n    end\n  end\n\n  describe \"be_close\" do\n    it { 8.5.should be_close(9, 0.5) }\n    it { 7.5.should_not be_close(9, 0.5) }\n  end\n\n  describe \"be_nil\" do\n    it { nil.should be_nil }\n    it { \"\".should_not be_nil }\n    it { 10.should_not be_nil }\n\n    it \"restricts type on should_not\" do\n      x = 1 || nil\n      y = x.should_not be_nil\n      typeof(x).should eq(Int32?)\n      typeof(y).should eq(Int32)\n    end\n  end\n\n  describe \"be_falsey\" do\n    it { nil.should be_falsey }\n    it { false.should be_falsey }\n    it { true.should_not be_falsey }\n    it { \"crystal\".should_not be_falsey }\n  end\n\n  describe \"be_truthy\" do\n    it { true.should be_truthy }\n    it { \"crystal\".should be_truthy }\n    it { nil.should_not be_truthy }\n    it { false.should_not be_truthy }\n  end\n\n  describe \"be_false\" do\n    it { false.should be_false }\n    it { nil.should_not be_false }\n    it { true.should_not be_false }\n    it { \"crystal\".should_not be_false }\n  end\n\n  describe \"be_true\" do\n    it { true.should be_true }\n    it { nil.should_not be_true }\n    it { false.should_not be_true }\n    it { \"crystal\".should_not be_true }\n  end\n\n  describe \"contain\" do\n    it { [1, 2, 3].should contain(1) }\n    it { [1, 2, 3].should contain(2) }\n    it { [1, 2, 3].should contain(3) }\n    it { [1, 2, 3].should_not contain(4) }\n    it { \"crystal\".should contain(\"c\") }\n    it { \"crystal\".should contain(\"crys\") }\n    it { \"crystal\".should contain(\"crystal\") }\n    it { \"crystal\".should_not contain(\"o\") }\n    it { \"crystal\".should_not contain(\"world\") }\n  end\n\n  describe \"eq\" do\n    it { 10.should eq(10) }\n    it { 10.should_not eq(1) }\n  end\n\n  describe \"match\" do\n    it { \"Crystal\".should match(/Crystal/) }\n    it { \"Crystal\".should match(/ysta/) }\n    it { \"Crystal\".should_not match(/hello/) }\n  end\n\n  describe \"start_with\" do\n    it { \"1-2-3\".should start_with(\"\") }\n    it { \"1-2-3\".should start_with(\"1\") }\n    it { \"1-2-3\".should start_with(\"1-\") }\n    it { \"1-2-3\".should start_with(\"1-2-3\") }\n    it { \"1-2-3\".should_not start_with(\"2-\") }\n    it { \"1-2-3\".should_not start_with(\"1-2-3-4\") }\n  end\n\n  describe \"end_with\" do\n    it { \"1-2-3\".should end_with(\"\") }\n    it { \"1-2-3\".should end_with(\"3\") }\n    it { \"1-2-3\".should end_with(\"-3\") }\n    it { \"1-2-3\".should end_with(\"1-2-3\") }\n    it { \"1-2-3\".should_not end_with(\"-2\") }\n    it { \"1-2-3\".should_not end_with(\"0-1-2-3\") }\n  end\n\n  context \"empty\" do\n    it { \"\".should be_empty }\n    it { Array(String).new.should be_empty }\n    it { Hash(String, String).new.should be_empty }\n    it { \"foo\".should_not be_empty }\n    it { [\"foo\", \"bar\"].should_not be_empty }\n    it { {\"foo\" => \"bar\"}.should_not be_empty }\n    it { {\"foo\", \"bar\"}.should_not be_empty }\n  end\n\n  describe \"expect_raises\" do\n    it \"passes if expected message equals actual message and expected class equals actual class\" do\n      expect_raises(Exception, \"Ops\") { raise Exception.new(\"Ops\") }\n    end\n\n    it \"passes if expected message equals actual message and expected class is an ancestor of actual class\" do\n      expect_raises(Exception, \"Ops\") { raise ArgumentError.new(\"Ops\") }\n    end\n\n    it \"passes if expected message is a substring of actual message and expected class equals actual class\" do\n      expect_raises(Exception, \"Ops\") { raise Exception.new(\"Black Ops\") }\n    end\n\n    it \"passes if expected message is a substring of actual message and expected class is an ancestor of actual class\" do\n      expect_raises(Exception, \"Ops\") { raise ArgumentError.new(\"Black Ops\") }\n    end\n\n    it \"passes if expected regex matches actual message and expected class equals actual class\" do\n      expect_raises(Exception, /Ops/) { raise Exception.new(\"Black Ops\") }\n    end\n\n    it \"passes if expected regex matches actual message and expected class is an ancestor of actual class\" do\n      expect_raises(Exception, /Ops/) { raise ArgumentError.new(\"Black Ops\") }\n    end\n\n    it \"passes if given no message expectation and expected class equals actual class\" do\n      expect_raises(Exception) { raise Exception.new(\"Ops\") }\n    end\n\n    it \"passes if given no message expectation and expected class is an ancestor of actual class\" do\n      expect_raises(Exception) { raise ArgumentError.new(\"Ops\") }\n    end\n\n    it \"passes if given no message expectation, actual message is nil and expected class equals actual class\" do\n      expect_raises(Exception) { raise Exception.new(nil) }\n    end\n\n    it \"passes if given no message expectation, actual message is nil and expected class is an ancestor of actual class\" do\n      expect_raises(Exception) { raise ArgumentError.new(nil) }\n    end\n\n    unless {{ flag?(:wasm32) }}\n      it \"fails if expected message does not equal actual message and expected class equals actual class\" do\n        expect_raises(Exception, \"Ops\") { raise Exception.new(\"Hm\") }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"fails if given expected message, actual message is nil and expected class equals actual class\" do\n        expect_raises(Exception, \"Ops\") { raise Exception.new(nil) }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"fails if expected regex does not match actual message and expected class equals actual class\" do\n        expect_raises(Exception, /Ops/) { raise Exception.new(\"Hm\") }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"fails if given expected regex, actual message is nil and expected class equals actual class\" do\n        expect_raises(Exception, /Ops/) { raise Exception.new(nil) }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"fails if given no message expectation and expected class does not equal and is not an ancestor of actual class\" do\n        expect_raises(IndexError) { raise ArgumentError.new(\"Ops\") }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"fails if given no message expectation, actual message is nil and expected class does not equal and is not an ancestor of actual class\" do\n        expect_raises(IndexError) { raise ArgumentError.new(nil) }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"fails if nothing was raised\" do\n        expect_raises(IndexError) { raise ArgumentError.new(\"Ops\") }\n      rescue Spec::AssertionFailed\n        # success\n      else\n        fail \"expected Spec::AssertionFailed but nothing was raised\"\n      end\n\n      it \"uses the exception's #to_s output to match a given String\" do\n        expect_raises(Exception, \"Hm\") { raise ExceptionWithOverriddenToS.new(\"Ops\", to_s: \"Hm\") }\n      end\n\n      it \"uses the exception's #to_s output to match a given Regex\" do\n        expect_raises(Exception, /Hm/) { raise ExceptionWithOverriddenToS.new(\"Ops\", to_s: \"Hm\") }\n      end\n\n      describe \"failure message format\" do\n        context \"given string to compare with message\" do\n          it \"contains expected exception, actual exception and backtrace\" do\n            expect_raises(Exception, \"digits should be non-negative\") do\n              raise IndexError.new(\"Index out of bounds\")\n            end\n          rescue e : Spec::AssertionFailed\n            # don't check backtrace items because they are platform specific\n            e.message.as(String).should contain(<<-MESSAGE)\n              Expected Exception with message containing: \"digits should be non-negative\"\n                   got IndexError with message: \"Index out of bounds\"\n              Backtrace:\n              MESSAGE\n          else\n            fail \"expected Spec::AssertionFailed but nothing is raised\"\n          end\n\n          it \"contains expected class, actual exception and backtrace when expected class does not match actual class\" do\n            expect_raises(ArgumentError, \"digits should be non-negative\") do\n              raise IndexError.new(\"Index out of bounds\")\n            end\n          rescue e : Spec::AssertionFailed\n            # don't check backtrace items because they are platform specific\n            e.message.as(String).should contain(<<-MESSAGE)\n              Expected ArgumentError\n                   got IndexError with message: \"Index out of bounds\"\n              Backtrace:\n              MESSAGE\n          else\n            fail \"expected Spec::AssertionFailed but nothing is raised\"\n          end\n\n          it \"escapes expected and actual messages in the same way\" do\n            expect_raises(Exception, %q(a\\tb\\nc)) do\n              raise %q(a\\tb\\nc).inspect\n            end\n          rescue e : Spec::AssertionFailed\n            e.message.as(String).should contain(\"Expected Exception with message containing: #{%q(a\\tb\\nc).inspect}\")\n            e.message.as(String).should contain(\"got Exception with message: #{%q(a\\tb\\nc).inspect.inspect}\")\n          else\n            fail \"expected Spec::AssertionFailed but nothing is raised\"\n          end\n        end\n\n        context \"given regex to match a message\" do\n          it \"contains expected exception, actual exception and backtrace\" do\n            expect_raises(Exception, /digits should be non-negative/) do\n              raise IndexError.new(\"Index out of bounds\")\n            end\n          rescue e : Spec::AssertionFailed\n            # don't check backtrace items because they are platform specific\n            e.message.as(String).should contain(<<-MESSAGE)\n              Expected Exception with message matching: /digits should be non-negative/\n                   got IndexError with message: \"Index out of bounds\"\n              Backtrace:\n              MESSAGE\n          else\n            fail \"expected Spec::AssertionFailed but nothing is raised\"\n          end\n\n          it \"contains expected class, actual exception and backtrace when expected class does not match actual class\" do\n            expect_raises(ArgumentError, /digits should be non-negative/) do\n              raise IndexError.new(\"Index out of bounds\")\n            end\n          rescue e : Spec::AssertionFailed\n            # don't check backtrace items because they are platform specific\n            e.message.as(String).should contain(<<-MESSAGE)\n              Expected ArgumentError\n                   got IndexError with message: \"Index out of bounds\"\n              Backtrace:\n              MESSAGE\n          else\n            fail \"expected Spec::AssertionFailed but nothing is raised\"\n          end\n        end\n\n        context \"given nil to allow any message\" do\n          it \"contains expected class, actual exception and backtrace when expected class does not match actual class\" do\n            expect_raises(ArgumentError, nil) do\n              raise IndexError.new(\"Index out of bounds\")\n            end\n          rescue e : Spec::AssertionFailed\n            # don't check backtrace items because they are platform specific\n            e.message.as(String).should contain(<<-MESSAGE)\n              Expected ArgumentError\n                   got IndexError with message: \"Index out of bounds\"\n              Backtrace:\n              MESSAGE\n          else\n            fail \"expected Spec::AssertionFailed but nothing is raised\"\n          end\n        end\n\n        context \"nothing was raises\" do\n          it \"contains expected class\" do\n            expect_raises(IndexError) { }\n          rescue e : Spec::AssertionFailed\n            e.message.as(String).should contain(\"Expected IndexError but nothing was raised\")\n          else\n            fail \"expected Spec::AssertionFailed but nothing was raised\"\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/spec/filters_spec.cr",
    "content": "require \"./spec_helper\"\n\nmodule Spec::Item\n  setter focus : Bool\n  setter tags : Set(String)?\nend\n\ndescribe Spec::RootContext do\n  describe \"#run_filters\" do\n    describe \"by pattern\" do\n      it \"on an example\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(pattern: /example_f_2_2/)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on a context\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(pattern: /context_f_2/)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n    end\n\n    describe \"by line\" do\n      it \"on a context's start line'\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(line: 11)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n\n      it \"between examples\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(line: 15)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n\n      it \"on an example's start line\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(line: 16)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"in an example\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(line: 17)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on an example's end line\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(line: 18)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on a context's end line'\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(line: 19)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n    end\n\n    describe \"by locations\" do\n      it \"on a context's start line'\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(locations: {\"f.cr\" => [11]})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n\n      it \"between examples\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(locations: {\"f.cr\" => [15]})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n\n      it \"on an example's start line\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(locations: {\"f.cr\" => [16]})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"in an example\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(locations: {\"f.cr\" => [17]})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on an example's end line\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(locations: {\"f.cr\" => [18]})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on a context's end line'\" do\n        root = build_spec(\"f.cr\")\n        root.run_filters(locations: {\"f.cr\" => [19]})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n    end\n\n    describe \"by focus\" do\n      it \"on an example\" do\n        root = build_spec(\"f.cr\")\n        root.children[1].as(Spec::ExampleGroup).children[1].as(Spec::Example).focus = true\n        root.run_filters(focus: true)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on a context\" do\n        root = build_spec(\"f.cr\")\n        root.children[1].as(Spec::ExampleGroup).focus = true\n        root.run_filters(focus: true)\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n    end\n\n    describe \"by tags\" do\n      it \"on an example\" do\n        root = build_spec(\"f.cr\")\n        root.children[1].as(Spec::ExampleGroup).children[1].as(Spec::Example).tags = Set{\"fast\"}\n        root.run_filters(tags: Set{\"fast\"})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_2]\n      end\n\n      it \"on a context\" do\n        root = build_spec(\"f.cr\")\n        root.children[1].as(Spec::ExampleGroup).tags = Set{\"fast\"}\n        root.run_filters(tags: Set{\"fast\"})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n    end\n\n    describe \"by anti_tags\" do\n      it \"on an example\" do\n        root = build_spec(\"f.cr\")\n        root.children[0].as(Spec::ExampleGroup).children[0].as(Spec::Example).tags = Set{\"slow\"}\n        root.children[0].as(Spec::ExampleGroup).children[1].as(Spec::Example).tags = Set{\"slow\"}\n        root.run_filters(anti_tags: Set{\"slow\"})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n\n      it \"on a context\" do\n        root = build_spec(\"f.cr\")\n        root.children[0].as(Spec::ExampleGroup).tags = Set{\"slow\"}\n        root.run_filters(anti_tags: Set{\"slow\"})\n        all_spec_descriptions(root).should eq %w[root context_f_2 example_f_2_1 example_f_2_2]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/spec/helpers/iterate_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\ndescribe Spec::Methods do\n  describe \".assert_iterates_yielding\" do\n    it \"basic\" do\n      assert_iterates_yielding [1, 2, 3], (1..3).each\n    end\n\n    it \"more than expected elements\" do\n      expect_raises Spec::AssertionFailed, \"Reached iteration limit 3 receiving value 4\" do\n        assert_iterates_yielding [1, 2, 3], (1..4).each\n      end\n    end\n\n    it \"less than expected elements\" do\n      expect_raises Spec::AssertionFailed, /Expected: \\[1, 2, 3\\]\\n\\s+got: \\[1, 2\\]/ do\n        assert_iterates_yielding [1, 2, 3], (1..2).each\n      end\n    end\n\n    it \"ensures type equality\" do\n      expect_raises Spec::AssertionFailed, \"Mismatching type, expected: 1.0 (Float64), got: 1 (Int32) at 0\" do\n        assert_iterates_yielding [1.0, 2.0, 3.0] of Int32 | Float64, (1..3).each\n      end\n    end\n\n    it \"infinite\" do\n      assert_iterates_yielding [1, 2, 3], (1..).each, infinite: true\n\n      expect_raises Spec::AssertionFailed, \"Reached iteration limit 3 receiving value 4\" do\n        assert_iterates_yielding [1, 2, 3], (1..).each, infinite: false\n      end\n\n      assert_iterates_yielding [] of Int32, (1..).each, infinite: true\n    end\n\n    it \"tuple\" do\n      assert_iterates_yielding [{1, 0}, {2, 1}, {3, 2}], (1..3).each_with_index, tuple: true\n    end\n  end\n\n  describe \".assert_iterates_iterator\" do\n    it \"basic\" do\n      assert_iterates_iterator [1, 2, 3], (1..3).each\n    end\n\n    it \"more than expected elements\" do\n      expect_raises Spec::AssertionFailed, \"Expected 4 (Int32) to be a Iterator::Stop\" do\n        assert_iterates_iterator [1, 2, 3], (1..4).each\n      end\n    end\n\n    it \"less than expected elements\" do\n      expect_raises Spec::AssertionFailed, /Expected: \\[1, 2, 3\\]\\n\\s+got: \\[1, 2\\]/ do\n        assert_iterates_iterator [1, 2, 3], (1..2).each\n      end\n    end\n\n    it \"ensures type equality\" do\n      expect_raises Spec::AssertionFailed, \"Mismatching type, expected: 1.0 (Float64), got: 1 (Int32) at 0\" do\n        assert_iterates_iterator [1.0, 2.0, 3.0] of Int32 | Float64, (1..3).each\n      end\n    end\n\n    it \"infinite\" do\n      assert_iterates_iterator [1, 2, 3], (1..).each, infinite: true\n\n      expect_raises Spec::AssertionFailed, \"Expected 4 (Int32) to be a Iterator::Stop\" do\n        assert_iterates_iterator [1, 2, 3], (1..).each, infinite: false\n      end\n\n      assert_iterates_iterator [] of Int32, (1..).each, infinite: true\n    end\n\n    it \"tuple\" do\n      assert_iterates_iterator [{1, 0}, {2, 1}, {3, 2}], (1..3).each_with_index\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/spec/hooks_spec.cr",
    "content": "require \"./spec_helper\"\n\ndescribe Spec do\n  describe \"hooks\" do\n    it \"runs in correct order\", tags: %w[slow] do\n      compile_and_run_source(<<-CRYSTAL, flags: %w(--no-debug))[1].lines[..-5].should eq <<-OUT.lines\n        require \"prelude\"\n        require \"spec\"\n\n        begin\n          before_all {}\n        rescue exc\n          puts exc.message\n        end\n        begin\n          before_each {}\n        rescue exc\n          puts exc.message\n        end\n        begin\n          after_all {}\n        rescue exc\n          puts exc.message\n        end\n        begin\n          after_each {}\n        rescue exc\n          puts exc.message\n        end\n        begin\n          around_all {}\n        rescue exc\n          puts exc.message\n        end\n        begin\n          around_each {}\n        rescue exc\n          puts exc.message\n        end\n\n        Spec.before_suite { puts \"Spec:before_suite\" }\n        Spec.before_each { puts \"Spec:before_each\" }\n        Spec.after_suite { puts \"Spec:after_all\" }\n        Spec.after_each { puts \"Spec:after_each\" }\n        Spec.around_each do |example|\n          puts \"Spec:around_each:before\"\n          example.run\n          puts \"Spec:around_each:after\"\n        end\n\n        describe \"foo\" do\n          before_all { puts \"foo:before_all\" }\n          before_each { puts \"foo:before_each\" }\n          after_all { puts \"foo:after_all\" }\n          after_each { puts \"foo:after_each\" }\n          around_all do |example|\n            puts \"foo:around_all:before\"\n            example.run\n            puts \"foo:around_all:after\"\n          end\n          around_each do |example|\n            puts \"foo:around_each:before\"\n            example.run\n            puts \"foo:around_each:after\"\n          end\n\n          it {}\n          it {}\n\n          describe \"foofoo\" do\n            it {}\n          end\n        end\n\n        describe \"bar\" do\n          before_all { puts \"bar:before_all\" }\n          before_each { puts \"bar:before_each\" }\n          after_all { puts \"bar:after_all\" }\n          after_each { puts \"bar:after_each\" }\n          around_all do |example|\n            puts \"bar:around_all:before\"\n            example.run\n            puts \"bar:around_all:after\"\n          end\n          around_each do |example|\n            puts \"bar:around_each:before\"\n            example.run\n            puts \"bar:around_each:after\"\n          end\n\n          it {}\n        end\n        CRYSTAL\n        Can't call `before_all` outside of a describe/context\n        Can't call `before_each` outside of a describe/context\n        Can't call `after_all` outside of a describe/context\n        Can't call `after_each` outside of a describe/context\n        Can't call `around_all` outside of a describe/context\n        Can't call `around_each` outside of a describe/context\n        Spec:before_suite\n        foo:around_all:before\n        foo:before_all\n        Spec:around_each:before\n        foo:around_each:before\n        Spec:before_each\n        foo:before_each\n        .foo:after_each\n        Spec:after_each\n        foo:around_each:after\n        Spec:around_each:after\n        Spec:around_each:before\n        foo:around_each:before\n        Spec:before_each\n        foo:before_each\n        .foo:after_each\n        Spec:after_each\n        foo:around_each:after\n        Spec:around_each:after\n        Spec:around_each:before\n        foo:around_each:before\n        Spec:before_each\n        foo:before_each\n        .foo:after_each\n        Spec:after_each\n        foo:around_each:after\n        Spec:around_each:after\n        foo:after_all\n        foo:around_all:after\n        bar:around_all:before\n        bar:before_all\n        Spec:around_each:before\n        bar:around_each:before\n        Spec:before_each\n        bar:before_each\n        .bar:after_each\n        Spec:after_each\n        bar:around_each:after\n        Spec:around_each:after\n        bar:after_all\n        bar:around_all:after\n        Spec:after_all\n        OUT\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/spec/junit_formatter_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"xml\"\n\nclass Spec::JUnitFormatter\n  property started_at\nend\n\nprivate class MyException < Exception\nend\n\ndescribe \"JUnit Formatter\" do\n  it \"reports successful results\" do\n    output = build_report_with_no_timestamp do |f|\n      f.report Spec::Result.new(:success, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n      f.report Spec::Result.new(:success, \"should do something else\", \"spec/some_spec.cr\", 50, nil, nil)\n    end\n\n    expected = <<-XML\n                 <?xml version=\"1.0\"?>\n                 <testsuite tests=\"2\" skipped=\"0\" errors=\"0\" failures=\"0\" time=\"0.0\" hostname=\"#{System.hostname}\">\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something\" line=\"33\"/>\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something else\" line=\"50\"/>\n                 </testsuite>\n                 XML\n\n    output.should eq(expected)\n  end\n\n  it \"reports skipped\" do\n    output = build_report_with_no_timestamp do |f|\n      f.report Spec::Result.new(:pending, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n    end\n\n    expected = <<-XML\n                 <?xml version=\"1.0\"?>\n                 <testsuite tests=\"1\" skipped=\"1\" errors=\"0\" failures=\"0\" time=\"0.0\" hostname=\"#{System.hostname}\">\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something\" line=\"33\">\n                     <skipped/>\n                   </testcase>\n                 </testsuite>\n                 XML\n\n    output.should eq(expected)\n  end\n\n  it \"reports failures\" do\n    output = build_report_with_no_timestamp do |f|\n      f.report Spec::Result.new(:fail, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n    end\n\n    expected = <<-XML\n                 <?xml version=\"1.0\"?>\n                 <testsuite tests=\"1\" skipped=\"0\" errors=\"0\" failures=\"1\" time=\"0.0\" hostname=\"#{System.hostname}\">\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something\" line=\"33\">\n                     <failure/>\n                   </testcase>\n                 </testsuite>\n                 XML\n\n    output.should eq(expected)\n  end\n\n  it \"reports errors\" do\n    output = build_report_with_no_timestamp do |f|\n      f.report Spec::Result.new(:error, \"should do something\", \"spec/some_spec.cr\", 33, nil, MyException.new(\"foo\"))\n    end\n\n    expected = <<-XML\n                 <?xml version=\"1.0\"?>\n                 <testsuite tests=\"1\" skipped=\"0\" errors=\"1\" failures=\"0\" time=\"0.0\" hostname=\"#{System.hostname}\">\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something\" line=\"33\">\n                     <error message=\"foo\" type=\"MyException\"></error>\n                   </testcase>\n                 </testsuite>\n                 XML\n\n    output.should eq(expected)\n  end\n\n  it \"reports mixed results\" do\n    output = build_report_with_no_timestamp do |f|\n      f.report Spec::Result.new(:success, \"should do something1\", \"spec/some_spec.cr\", 33, 2.seconds, nil)\n      f.report Spec::Result.new(:fail, \"should do something2\", \"spec/some_spec.cr\", 50, 0.5.seconds, nil)\n      f.report Spec::Result.new(:error, \"should do something3\", \"spec/some_spec.cr\", 65, nil, nil)\n      f.report Spec::Result.new(:pending, \"should do something4\", \"spec/some_spec.cr\", 80, nil, nil)\n    end\n\n    expected = <<-XML\n                 <?xml version=\"1.0\"?>\n                 <testsuite tests=\"4\" skipped=\"1\" errors=\"1\" failures=\"1\" time=\"0.0\" hostname=\"#{System.hostname}\">\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something1\" line=\"33\" time=\"2.0\"/>\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something2\" line=\"50\" time=\"0.5\">\n                     <failure/>\n                   </testcase>\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something3\" line=\"65\">\n                     <error/>\n                   </testcase>\n                   <testcase file=\"spec/some_spec.cr\" classname=\"spec.some_spec\" name=\"should do something4\" line=\"80\">\n                     <skipped/>\n                   </testcase>\n                 </testsuite>\n                 XML\n\n    output.should eq(expected)\n  end\n\n  it \"encodes class names from the relative file path\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:success, \"foo\", __FILE__, __LINE__, nil, nil)\n    end\n\n    classname = XML.parse(output).xpath_string(\"string(//testsuite/testcase[1]/@classname)\")\n    classname.should eq(\"spec.std.spec.junit_formatter_spec\")\n  end\n\n  it \"outputs timestamp according to RFC 3339\" do\n    now = Time.utc\n\n    output = build_report(timestamp: now) do |f|\n      f.report Spec::Result.new(:success, \"foo\", __FILE__, __LINE__, nil, nil)\n    end\n\n    classname = XML.parse(output).xpath_string(\"string(//testsuite[1]/@timestamp)\")\n    classname.should eq(now.to_rfc3339)\n  end\n\n  it \"escapes spec names\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:success, %(complicated \" <n>'&ame), __FILE__, __LINE__, nil, nil)\n      f.report Spec::Result.new(:success, %(ctrl characters follow - \\r\\n), __FILE__, __LINE__, nil, nil)\n    end\n\n    name = XML.parse(output).xpath_string(\"string(//testsuite/testcase[1]/@name)\")\n    name.should eq(%(complicated \" <n>'&ame))\n\n    name = XML.parse(output).xpath_string(\"string(//testsuite/testcase[2]/@name)\")\n    name.should eq(%(ctrl characters follow - \\\\r\\\\n))\n  end\n\n  it \"report failure stacktrace if present\" do\n    cause = exception_with_backtrace(\"Something happened\")\n\n    output = build_report do |f|\n      f.report Spec::Result.new(:fail, \"foo\", __FILE__, __LINE__, nil, cause)\n    end\n\n    xml = XML.parse(output)\n    name = xml.xpath_string(\"string(//testsuite/testcase[1]/failure/@message)\")\n    name.should eq(\"Something happened\")\n\n    backtrace = xml.xpath_string(\"string(//testsuite/testcase[1]/failure/text())\")\n    backtrace.should eq(cause.backtrace.join('\\n'))\n  end\n\n  it \"report error stacktrace if present\" do\n    cause = exception_with_backtrace(\"Something happened\")\n\n    output = build_report do |f|\n      f.report Spec::Result.new(:error, \"foo\", __FILE__, __LINE__, nil, cause)\n    end\n\n    xml = XML.parse(output)\n    name = xml.xpath_string(\"string(//testsuite/testcase[1]/error/@message)\")\n    name.should eq(\"Something happened\")\n\n    backtrace = xml.xpath_string(\"string(//testsuite/testcase[1]/error/text())\")\n    backtrace.should eq(cause.backtrace.join('\\n'))\n  end\nend\n\nprivate def build_report(timestamp = nil, &)\n  output = String::Builder.new\n  cli = Spec::CLI.new\n  formatter = Spec::JUnitFormatter.new(cli, output)\n  formatter.started_at = timestamp if timestamp\n  yield formatter\n  formatter.finish(Time::Span.zero, false)\n  output.to_s.chomp\nend\n\nprivate def build_report_with_no_timestamp(&)\n  output = build_report do |formatter|\n    yield formatter\n  end\n  output.gsub(/\\s*timestamp=\"(.+?)\"/, \"\")\nend\n\nprivate def exception_with_backtrace(msg)\n  raise Exception.new(msg)\nrescue e\n  e\nend\n"
  },
  {
    "path": "spec/std/spec/list_tags_spec.cr",
    "content": "require \"./spec_helper\"\n\ndescribe Spec do\n  describe \"list_tags\" do\n    it \"lists the count of all tags\", tags: %w[slow] do\n      compile_and_run_source(<<-CRYSTAL, flags: %w(--no-debug), runtime_args: %w(--list-tags))[1].lines.should eq <<-OUT.lines\n        require \"spec\"\n\n        it \"untagged #1\" do\n        end\n        it \"untagged #2\" do\n        end\n        it \"untagged #3\" do\n        end\n\n        it \"slow #1\", tags: \"slow\" do\n        end\n        it \"slow #2\", tags: \"slow\" do\n        end\n\n        it \"untagged #4\" do\n        end\n\n        it \"flakey #1\", tags: \"flakey\" do\n        end\n        it \"flakey #2, slow #3\", tags: [\"flakey\", \"slow\"] do\n        end\n\n        it \"untagged #5\" do\n        end\n\n        pending \"untagged #6\"\n\n        pending \"untagged #7\" do\n        end\n\n        pending \"slow #5\", tags: \"slow\"\n\n        describe \"describe specs\", tags: \"describe\" do\n          it \"describe #1\" do\n          end\n          it \"describe #2\" do\n          end\n          it \"describe #3, slow #4\", tags: \"slow\" do\n          end\n          it \"describe #4, flakey #3\", tags: \"flakey\" do\n          end\n        end\n        CRYSTAL\n        untagged: 7\n            slow: 5\n        describe: 4\n          flakey: 3\n        OUT\n    end\n\n    it \"lists the count of slow tags\", tags: %w[slow] do\n      compile_and_run_source(<<-CRYSTAL, flags: %w(--no-debug), runtime_args: %w(--list-tags --tag slow))[1].lines.should eq <<-OUT.lines\n        require \"spec\"\n\n        it \"untagged #1\" do\n        end\n        it \"untagged #2\" do\n        end\n        it \"untagged #3\" do\n        end\n\n        it \"slow #1\", tags: \"slow\" do\n        end\n        it \"slow #2\", tags: \"slow\" do\n        end\n\n        it \"untagged #4\" do\n        end\n\n        it \"flakey #1\", tags: \"flakey\" do\n        end\n        it \"flakey #2, slow #3\", tags: [\"flakey\", \"slow\"] do\n        end\n\n        it \"untagged #5\" do\n        end\n\n        pending \"untagged #6\"\n\n        pending \"untagged #7\" do\n        end\n\n        pending \"slow #5\", tags: \"slow\"\n\n        describe \"describe specs\", tags: \"describe\" do\n          it \"describe #1\" do\n          end\n          it \"describe #2\" do\n          end\n          it \"describe #3, slow #4\", tags: \"slow\" do\n          end\n          it \"describe #4, flakey #3\", tags: \"flakey\" do\n          end\n        end\n        CRYSTAL\n            slow: 5\n        describe: 1\n          flakey: 1\n        OUT\n    end\n\n    it \"does nothing if there are no examples\", tags: %w[slow] do\n      compile_and_run_source(<<-CRYSTAL, flags: %w(--no-debug), runtime_args: %w(--list-tags))[1].lines.should eq <<-OUT.lines\n        require \"spec\"\n\n        describe \"describe specs\", tags: \"describe\" do\n        end\n        CRYSTAL\n        OUT\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/spec/spec_helper.cr",
    "content": "require \"../spec_helper\"\n\nclass FakeRootContext < Spec::RootContext\n  def initialize\n    super(Spec::CLI.new)\n  end\n\n  def description\n    \"root\"\n  end\n\n  def report_formatters(result)\n  end\nend\n\ndef all_spec_descriptions(item) : Array(String)\n  child_descriptions = item.responds_to?(:children) ? item.children.flat_map { |c| all_spec_descriptions(c) } : [] of String\n  child_descriptions.unshift(item.description)\nend\n\ndef build_spec(filename, root = nil, count = 2)\n  root ||= FakeRootContext.new\n  name = filename.chomp(\".cr\")\n\n  1.upto(count) do |i|\n    line = (i - 1) * 10\n    root.children << Spec::ExampleGroup.new(root, \"context_#{name}_#{i}\", filename, line + 1, line + 9, false, nil).tap do |c|\n      c.children << Spec::Example.new(c, \"example_#{name}_#{i}_1\", filename, line + 2, line + 4, false, nil, nil)\n      c.children << Spec::Example.new(c, \"example_#{name}_#{i}_2\", filename, line + 6, line + 8, false, nil, nil)\n    end\n  end\n\n  root\nend\n"
  },
  {
    "path": "spec/std/spec/tap_formatter_spec.cr",
    "content": "require \"spec\"\n\nprivate def build_report(&)\n  String.build do |io|\n    cli = Spec::CLI.new(io)\n    formatter = Spec::TAPFormatter.new(cli)\n    yield formatter\n    formatter.finish(Time::Span.zero, false)\n  end\nend\n\nprivate def exception_with_backtrace(msg)\n  raise Exception.new(msg)\nrescue e\n  e\nend\n\ndescribe Spec::TAPFormatter do\n  it \"reports successful results\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:success, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n      f.report Spec::Result.new(:success, \"should do something else\", \"spec/some_spec.cr\", 50, nil, nil)\n    end\n\n    output.chomp.should eq <<-TAP\n      ok 1 - should do something\n      ok 2 - should do something else\n      1..2\n      TAP\n  end\n\n  it \"reports failures\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:fail, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n    end\n\n    output.chomp.should eq <<-TAP\n      not ok 1 - should do something\n      1..1\n      TAP\n  end\n\n  it \"reports errors\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:error, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n    end\n\n    output.chomp.should eq <<-TAP\n      not ok 1 - should do something\n      1..1\n      TAP\n  end\n\n  it \"reports pending\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:pending, \"should do something\", \"spec/some_spec.cr\", 33, nil, nil)\n    end\n\n    output.chomp.should eq <<-TAP\n      ok 1 - # SKIP should do something\n      1..1\n      TAP\n  end\n\n  it \"reports mixed results\" do\n    output = build_report do |f|\n      f.report Spec::Result.new(:success, \"should do something1\", \"spec/some_spec.cr\", 33, 2.seconds, nil)\n      f.report Spec::Result.new(:fail, \"should do something2\", \"spec/some_spec.cr\", 50, 0.5.seconds, nil)\n      f.report Spec::Result.new(:error, \"should do something3\", \"spec/some_spec.cr\", 65, nil, nil)\n      f.report Spec::Result.new(:error, \"should do something4\", \"spec/some_spec.cr\", 80, nil, nil)\n      f.report Spec::Result.new(:pending, \"should do something5\", \"spec/some_spec.cr\", 33, nil, nil)\n    end\n\n    output.chomp.should eq <<-TAP\n      ok 1 - should do something1\n      not ok 2 - should do something2\n      not ok 3 - should do something3\n      not ok 4 - should do something4\n      ok 5 - # SKIP should do something5\n      1..5\n      TAP\n  end\nend\n"
  },
  {
    "path": "spec/std/spec_helper.cr",
    "content": "require \"spec\"\nrequire \"../support/tempfile\"\nrequire \"../support/fibers\"\nrequire \"../support/win32\"\nrequire \"../support/wasm32\"\nrequire \"../support/interpreted\"\n\ndef datapath(*components)\n  File.join(\"spec\", \"std\", \"data\", *components)\nend\n\nprivate class Witness\n  @checked = false\n\n  def check\n    @checked = true\n  end\n\n  def checked?\n    @checked\n  end\nend\n\ndef spawn_and_wait(before : Proc(_), file = __FILE__, line = __LINE__, &block)\n  spawn_and_check(before, file, line) do |w|\n    block.call\n    w.check\n  end\nend\n\ndef spawn_and_check(before : Proc(_), file = __FILE__, line = __LINE__, &block : Witness -> _)\n  done = Channel(Exception?).new\n  w = Witness.new\n\n  # State of the \"before\" filter:\n  # 0 - not started\n  # 1 - started\n  # 2 - completed\n  x = Atomic(Int32).new(0)\n\n  before_fiber = spawn do\n    x.set(1)\n\n    # This is a workaround to ensure the \"before\" fiber\n    # is unscheduled. Otherwise it might stay alive running the event loop\n    spawn(same_thread: !{{flag?(:execution_context)}}) do\n      while x.get != 2\n        Fiber.yield\n      end\n    end\n\n    before.call\n    x.set(2)\n  end\n\n  spawn do\n    begin\n      # Wait until the \"before\" fiber starts\n      while x.get == 0\n        Fiber.yield\n      end\n\n      # Now wait until the \"before\" fiber is blocked\n      wait_until_blocked before_fiber\n      block.call w\n\n      done.send nil\n    rescue e\n      done.send e\n    end\n  end\n\n  ex = done.receive\n  raise ex if ex\n  unless w.checked?\n    fail \"Failed to stress expected path\", file, line\n  end\nend\n\ndef compile_file(source_file, *, bin_name = \"executable_file\", flags = %w(), file = __FILE__, &)\n  # can't use backtick in interpreted code (#12241)\n  pending_interpreted! \"Unable to compile Crystal code in interpreted code\"\n\n  with_temp_executable(bin_name, file: file) do |executable_file|\n    compiler = ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"]? || \"bin/crystal\"\n    args = [\"build\"] + flags + [\"-o\", executable_file, source_file]\n    output = IO::Memory.new\n    status = Process.run(compiler, args, env: {\n      \"CRYSTAL_PATH\"         => Crystal::PATH,\n      \"CRYSTAL_LIBRARY_PATH\" => Crystal::LIBRARY_PATH,\n      \"CRYSTAL_CACHE_DIR\"    => Crystal::CACHE_DIR,\n    }, output: output, error: output)\n\n    unless status.success?\n      fail \"Compiler command `#{compiler} #{args.join(\" \")}` failed with status #{status}.#{\"\\n\" if output}#{output}\"\n    end\n\n    File.exists?(executable_file).should be_true\n\n    yield executable_file\n  end\nend\n\ndef assert_compile_error(source, expected_error, *, flags = %w(), file = __FILE__, line = __LINE__)\n  # can't use backtick in interpreted code (#12241)\n  pending_interpreted! \"Unable to compile Crystal code in interpreted code\"\n\n  with_tempfile(\"source_file\", file: file) do |source_file|\n    File.write(source_file, source)\n\n    bin_name = \"executable_file\"\n    with_temp_executable(bin_name, file: file) do |executable_file|\n      compiler = ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"]? || \"bin/crystal\"\n      args = [\"build\"] + flags + [\"-o\", executable_file, source_file]\n      output = IO::Memory.new\n      status = Process.run(compiler, args, env: {\n        \"CRYSTAL_PATH\"         => Crystal::PATH,\n        \"CRYSTAL_LIBRARY_PATH\" => Crystal::LIBRARY_PATH,\n        \"CRYSTAL_CACHE_DIR\"    => Crystal::CACHE_DIR,\n        \"NO_COLOR\"             => \"1\",\n      }, output: output, error: output)\n\n      output.to_s.should contain(expected_error)\n\n      status.success?.should be_false\n      File.exists?(executable_file).should be_false\n    end\n  end\nend\n\ndef compile_source(source, flags = %w(), file = __FILE__, &)\n  with_tempfile(\"source_file\", file: file) do |source_file|\n    File.write(source_file, source)\n    compile_file(source_file, flags: flags, file: file) do |executable_file|\n      yield executable_file\n    end\n  end\nend\n\ndef compile_and_run_file(source_file, flags = %w(), runtime_args = %w(), file = __FILE__)\n  compile_file(source_file, flags: flags, file: file) do |executable_file|\n    output, error = IO::Memory.new, IO::Memory.new\n    status = Process.run executable_file, args: runtime_args, output: output, error: error\n\n    {status, output.to_s, error.to_s}\n  end\nend\n\ndef compile_and_run_source(source, flags = %w(), runtime_args = %w(), file = __FILE__)\n  with_tempfile(\"source_file\", file: file) do |source_file|\n    File.write(source_file, source)\n    compile_and_run_file(source_file, flags, runtime_args, file: file)\n  end\nend\n\ndef compile_and_run_source_with_c(c_code, crystal_code, flags = %w(--debug), file = __FILE__, &)\n  with_temp_c_object_file(c_code, file: file) do |o_filename|\n    yield compile_and_run_source(%(\n    require \"prelude\"\n\n    @[Link(ldflags: #{o_filename.inspect})]\n    #{crystal_code}\n    ))\n  end\nend\n"
  },
  {
    "path": "spec/std/spec_spec.cr",
    "content": "require \"spec\"\n\nprivate class SpecException < Exception\n  getter value : Int32\n\n  def initialize(@value, msg)\n    super(msg)\n  end\nend\n\nprivate class NilMimicker\n  def ==(a_nil : Nil)\n    true\n  end\nend\n\ndescribe \"Spec matchers\" do\n  describe \"should be_truthy\" do\n    it \"passes for true\" do\n      true.should be_truthy\n    end\n\n    it \"passes for some non-nil, non-false value\" do\n      42.should be_truthy\n    end\n  end\n\n  describe \"should_not be_truthy\" do\n    it \"passes for false\" do\n      false.should_not be_truthy\n    end\n\n    it \"passes for nil\" do\n      nil.should_not be_truthy\n    end\n  end\n\n  describe \"should be_falsey\" do\n    it \"passes for false\" do\n      false.should be_falsey\n    end\n\n    it \"passes for nil\" do\n      nil.should be_falsey\n    end\n  end\n\n  describe \"should_not be_falsey\" do\n    it \"passes for true\" do\n      true.should_not be_falsey\n    end\n\n    it \"passes for some non-nil, non-false value\" do\n      42.should_not be_falsey\n    end\n  end\n\n  describe \"be_nil\" do\n    it \"passes for nil\" do\n      nil.should be_nil\n    end\n\n    it \"does not pass for overwritten `==`\" do\n      NilMimicker.new.should_not be_nil\n    end\n  end\n\n  describe \"should contain\" do\n    it \"passes when string includes? specified substring\" do\n      \"hello world!\".should contain(\"hello\")\n    end\n\n    it \"works with array\" do\n      [1, 2, 3, 5, 8].should contain(5)\n    end\n\n    it \"works with set\" do\n      [1, 2, 3, 5, 8].to_set.should contain(8)\n    end\n\n    it \"works with range\" do\n      (50..55).should contain(53)\n    end\n\n    it \"does not pass when string does not includes? specified substring\" do\n      expect_raises Spec::AssertionFailed, %{Expected:   \"hello world!\"\\nto include: \"crystal\"} do\n        \"hello world!\".should contain(\"crystal\")\n      end\n    end\n  end\n\n  describe \"should_not contain\" do\n    it \"passes when string does not includes? specified substring\" do\n      \"hello world!\".should_not contain(\"crystal\")\n    end\n\n    it \"does not pass when string does not includes? specified substring\" do\n      expect_raises Spec::AssertionFailed, %{Expected: value \"hello world!\"\\nto not include: \"world\"} do\n        \"hello world!\".should_not contain(\"world\")\n      end\n    end\n  end\n\n  describe \"expect_raises\" do\n    it \"return exception\" do\n      ex = expect_raises(SpecException) { raise SpecException.new(11, \"O_o\") }\n      ex.value.should eq 11\n      ex.message.should eq \"O_o\"\n    end\n  end\n\n  context \"should work like describe\" do\n    it \"is true\" do\n      true.should be_truthy\n    end\n  end\n\n  it \"detects a nesting `it`\" do\n    ex = expect_raises(Spec::NestingSpecError) { it { } }\n    ex.message.should eq \"Can't nest `it` or `pending`\"\n    ex.file.should eq __FILE__\n  end\n\n  it \"detects a nesting `pending`\" do\n    ex = expect_raises(Spec::NestingSpecError) { pending }\n    ex.message.should eq \"Can't nest `it` or `pending`\"\n    ex.file.should eq __FILE__\n  end\n\n  describe \"pending block is not compiled\" do\n    pending \"pending has block with valid syntax, but invalid semantics\" do\n      UndefinedConstant.undefined_method\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/sprintf_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../support/number\"\nrequire \"spec/helpers/string\"\nrequire \"big\"\n\n# use same name for `sprintf` and `IO#printf` so that `assert_prints` can be leveraged\nprivate def fprintf(format, *args)\n  sprintf(format, *args)\nend\n\nprivate def fprintf(io : IO, format, *args)\n  io.printf(format, *args)\nend\n\nprivate def assert_sprintf(format, args, result, *, file = __FILE__, line = __LINE__)\n  assert_prints fprintf(format, args), result, file: file, line: line\nend\n\ndescribe \"::sprintf\" do\n  it \"works\" do\n    assert_sprintf \"Hello %d world\", 123, \"Hello 123 world\"\n    assert_sprintf \"Hello %d world\", [123], \"Hello 123 world\"\n    assert_sprintf \"foo %d bar %s baz %d goo\", [1, \"hello\", 2], \"foo 1 bar hello baz 2 goo\"\n  end\n\n  it \"accepts multiple positional arguments\" do\n    assert_prints fprintf(\"%d %d %d\", 1, 23, 456), \"1 23 456\"\n    assert_prints fprintf(\"%*.*d,%*s\", 10, 6, 123, 10, \"foo\"), \"    000123,       foo\"\n    assert_prints fprintf(\"foo\"), \"foo\"\n  end\n\n  it \"doesn't format %%\" do\n    assert_sprintf \"%%%d\", 1, \"%1\"\n    assert_sprintf \"%%*%%\", [1, 2, 3], \"%*%\"\n  end\n\n  it \"doesn't accept modifiers for %%\" do\n    expect_raises(ArgumentError) { sprintf(\"%0%\") }\n    expect_raises(ArgumentError) { sprintf(\"%+%\") }\n    expect_raises(ArgumentError) { sprintf(\"%-%\") }\n    expect_raises(ArgumentError) { sprintf(\"% %\") }\n    expect_raises(ArgumentError) { sprintf(\"%#%\") }\n    expect_raises(ArgumentError) { sprintf(\"%.0%\") }\n    expect_raises(ArgumentError) { sprintf(\"%*%\", 1) }\n\n    expect_raises(ArgumentError) { sprintf(\"%<a>0%\") }\n    expect_raises(ArgumentError) { sprintf(\"%<a>+%\") }\n    expect_raises(ArgumentError) { sprintf(\"%<a>-%\") }\n    expect_raises(ArgumentError) { sprintf(\"%<a> %\") }\n    expect_raises(ArgumentError) { sprintf(\"%<a>#%\") }\n    expect_raises(ArgumentError) { sprintf(\"%<a>.0%\") }\n    expect_raises(ArgumentError) { sprintf(\"%<a>*%\", 1) }\n  end\n\n  context \"integers\" do\n    context \"base specifier\" do\n      it \"supports base 2\" do\n        assert_sprintf \"%b\", 123, \"1111011\"\n        assert_sprintf \"%+b\", 123, \"+1111011\"\n        assert_sprintf \"% b\", 123, \" 1111011\"\n        assert_sprintf \"%-b\", 123, \"1111011\"\n        assert_sprintf \"%10b\", 123, \"   1111011\"\n        assert_sprintf \"%-10b\", 123, \"1111011   \"\n      end\n\n      it \"supports base 8\" do\n        assert_sprintf \"%o\", 123, \"173\"\n        assert_sprintf \"%+o\", 123, \"+173\"\n        assert_sprintf \"% o\", 123, \" 173\"\n        assert_sprintf \"%-o\", 123, \"173\"\n        assert_sprintf \"%6o\", 123, \"   173\"\n        assert_sprintf \"%-6o\", 123, \"173   \"\n      end\n\n      it \"supports base 10\" do\n        assert_sprintf \"%d\", 123, \"123\"\n        assert_sprintf \"%+d\", 123, \"+123\"\n        assert_sprintf \"% d\", 123, \" 123\"\n        assert_sprintf \"%-d\", 123, \"123\"\n        assert_sprintf \"%6d\", 123, \"   123\"\n        assert_sprintf \"%-6d\", 123, \"123   \"\n\n        assert_sprintf \"%i\", 123, \"123\"\n        assert_sprintf \"%+i\", 123, \"+123\"\n        assert_sprintf \"% i\", 123, \" 123\"\n        assert_sprintf \"%-i\", 123, \"123\"\n        assert_sprintf \"%6i\", 123, \"   123\"\n        assert_sprintf \"%-6i\", 123, \"123   \"\n      end\n\n      it \"supports base 16\" do\n        assert_sprintf \"%x\", 123, \"7b\"\n        assert_sprintf \"%+x\", 123, \"+7b\"\n        assert_sprintf \"% x\", 123, \" 7b\"\n        assert_sprintf \"%-x\", 123, \"7b\"\n        assert_sprintf \"%6x\", 123, \"    7b\"\n        assert_sprintf \"%-6x\", 123, \"7b    \"\n\n        assert_sprintf \"%X\", 123, \"7B\"\n        assert_sprintf \"%+X\", 123, \"+7B\"\n        assert_sprintf \"% X\", 123, \" 7B\"\n        assert_sprintf \"%-X\", 123, \"7B\"\n        assert_sprintf \"%6X\", 123, \"    7B\"\n        assert_sprintf \"%-6X\", 123, \"7B    \"\n\n        assert_sprintf \"こんに%xちは\", 123, \"こんに7bちは\"\n        assert_sprintf \"こんに%Xちは\", 123, \"こんに7Bちは\"\n      end\n    end\n\n    context \"width specifier\" do\n      it \"sets the minimum length of the string\" do\n        assert_sprintf \"%20d\", 123, \"                 123\"\n        assert_sprintf \"%20d\", -123, \"                -123\"\n        assert_sprintf \"%20d\", 0, \"                   0\"\n\n        assert_sprintf \"%4d\", 123, \" 123\"\n        assert_sprintf \"%4d\", -123, \"-123\"\n        assert_sprintf \"%4d\", 0, \"   0\"\n\n        assert_sprintf \"%2d\", 123, \"123\"\n        assert_sprintf \"%2d\", -123, \"-123\"\n        assert_sprintf \"%2d\", 0, \" 0\"\n\n        assert_sprintf \"%*d\", [20, 123], \"                 123\"\n        assert_sprintf \"%*d\", [20, -123], \"                -123\"\n        assert_sprintf \"%*d\", [20, 0], \"                   0\"\n\n        assert_sprintf \"%*d\", [0, 123], \"123\"\n        assert_sprintf \"%*d\", [0, -123], \"-123\"\n        assert_sprintf \"%*d\", [0, 0], \"0\"\n      end\n\n      it \"left-justifies on negative width\" do\n        assert_sprintf \"%*d\", [-20, 123], \"123                 \"\n        assert_sprintf \"%*d\", [-20, -123], \"-123                \"\n        assert_sprintf \"%*d\", [-20, 0], \"0                   \"\n\n        assert_sprintf \"%*d\", [-4, 123], \"123 \"\n        assert_sprintf \"%*d\", [-4, -123], \"-123\"\n        assert_sprintf \"%*d\", [-4, 0], \"0   \"\n\n        assert_sprintf \"%*d\", [-2, 123], \"123\"\n        assert_sprintf \"%*d\", [-2, -123], \"-123\"\n        assert_sprintf \"%*d\", [-2, 0], \"0 \"\n\n        assert_sprintf \"%-*d\", [-20, 123], \"123                 \"\n        assert_sprintf \"%-*d\", [-20, -123], \"-123                \"\n        assert_sprintf \"%-*d\", [-20, 0], \"0                   \"\n      end\n    end\n\n    context \"precision specifier\" do\n      it \"sets the minimum length of the number part\" do\n        assert_sprintf \"%.12d\", 123, \"000000000123\"\n        assert_sprintf \"%.12d\", -123, \"-000000000123\"\n        assert_sprintf \"%.12d\", 0, \"000000000000\"\n\n        assert_sprintf \"%.4d\", 123, \"0123\"\n        assert_sprintf \"%.4d\", -123, \"-0123\"\n        assert_sprintf \"%.4d\", 0, \"0000\"\n\n        assert_sprintf \"%.2d\", 123, \"123\"\n        assert_sprintf \"%.2d\", -123, \"-123\"\n        assert_sprintf \"%.2d\", 0, \"00\"\n\n        assert_sprintf \"%.0d\", 123, \"123\"\n        assert_sprintf \"%.0d\", -123, \"-123\"\n        assert_sprintf \"%.0d\", 0, \"\"\n      end\n\n      it \"can be used with width\" do\n        assert_sprintf \"%20.12d\", 123, \"        000000000123\"\n        assert_sprintf \"%20.12d\", -123, \"       -000000000123\"\n        assert_sprintf \"%20.12d\", 0, \"        000000000000\"\n\n        assert_sprintf \"%-20.12d\", 123, \"000000000123        \"\n        assert_sprintf \"%-20.12d\", -123, \"-000000000123       \"\n        assert_sprintf \"%-20.12d\", 0, \"000000000000        \"\n\n        assert_sprintf \"%8.12d\", 123, \"000000000123\"\n        assert_sprintf \"%8.12d\", -123, \"-000000000123\"\n        assert_sprintf \"%8.12d\", 0, \"000000000000\"\n\n        assert_sprintf \"%+13.12d\", 123, \"+000000000123\"\n        assert_sprintf \"%+13.12d\", -123, \"-000000000123\"\n        assert_sprintf \"%+13.12d\", 0, \"+000000000000\"\n\n        assert_sprintf \"%- 20.12d\", 123, \" 000000000123       \"\n        assert_sprintf \"%- 20.12d\", -123, \"-000000000123       \"\n        assert_sprintf \"%- 20.12d\", 0, \" 000000000000       \"\n\n        assert_sprintf \"%*.*d\", [20, 12, 123], \"        000000000123\"\n        assert_sprintf \"%*.*d\", [20, 12, -123], \"       -000000000123\"\n        assert_sprintf \"%*.*d\", [20, 12, 0], \"        000000000000\"\n\n        assert_sprintf \"%*.*d\", [-20, -12, 123], \"123                 \"\n        assert_sprintf \"%*.*d\", [-20, -12, -123], \"-123                \"\n        assert_sprintf \"%*.*d\", [-20, -12, 0], \"0                   \"\n      end\n\n      it \"is ignored if precision argument is negative\" do\n        assert_sprintf \"%.*d\", [-2, 123], \"123\"\n        assert_sprintf \"%.*d\", [-2, -123], \"-123\"\n        assert_sprintf \"%.*d\", [-2, 0], \"0\"\n\n        assert_sprintf \"%020.*d\", [-2, 123], \"00000000000000000123\"\n        assert_sprintf \"%020.*d\", [-2, -123], \"-0000000000000000123\"\n        assert_sprintf \"%020.*d\", [-2, 0], \"00000000000000000000\"\n      end\n    end\n\n    context \"sharp flag\" do\n      it \"adds a base prefix\" do\n        assert_sprintf \"%#b\", 123, \"0b1111011\"\n        assert_sprintf \"%#o\", 123, \"0o173\"\n        assert_sprintf \"%#x\", 123, \"0x7b\"\n        assert_sprintf \"%#X\", 123, \"0X7B\"\n\n        assert_sprintf \"%#b\", -123, \"-0b1111011\"\n        assert_sprintf \"%#o\", -123, \"-0o173\"\n        assert_sprintf \"%#x\", -123, \"-0x7b\"\n        assert_sprintf \"%#X\", -123, \"-0X7B\"\n      end\n\n      it \"omits the base prefix for 0\" do\n        assert_sprintf \"%#b\", 0, \"0\"\n        assert_sprintf \"%#o\", 0, \"0\"\n        assert_sprintf \"%#x\", 0, \"0\"\n        assert_sprintf \"%#X\", 0, \"0\"\n      end\n    end\n\n    context \"plus flag\" do\n      it \"writes a plus sign for positive integers\" do\n        assert_sprintf \"%+d\", 123, \"+123\"\n        assert_sprintf \"%+d\", -123, \"-123\"\n        assert_sprintf \"%+d\", 0, \"+0\"\n      end\n\n      it \"writes plus sign after left space-padding\" do\n        assert_sprintf \"%+20d\", 123, \"                +123\"\n        assert_sprintf \"%+20d\", -123, \"                -123\"\n        assert_sprintf \"%+20d\", 0, \"                  +0\"\n      end\n\n      it \"writes plus sign before left zero-padding\" do\n        assert_sprintf \"%+020d\", 123, \"+0000000000000000123\"\n        assert_sprintf \"%+020d\", -123, \"-0000000000000000123\"\n        assert_sprintf \"%+020d\", 0, \"+0000000000000000000\"\n      end\n    end\n\n    context \"space flag\" do\n      it \"writes a space for positive integers\" do\n        assert_sprintf \"% d\", 123, \" 123\"\n        assert_sprintf \"% d\", -123, \"-123\"\n        assert_sprintf \"% d\", 0, \" 0\"\n      end\n\n      it \"writes space before left padding\" do\n        assert_sprintf \"% 20d\", 123, \"                 123\"\n        assert_sprintf \"% 20d\", -123, \"                -123\"\n        assert_sprintf \"% 20d\", 0, \"                   0\"\n\n        assert_sprintf \"% 020d\", 123, \" 0000000000000000123\"\n        assert_sprintf \"% 020d\", -123, \"-0000000000000000123\"\n        assert_sprintf \"% 020d\", 0, \" 0000000000000000000\"\n      end\n\n      it \"is ignored if plus flag is also specified\" do\n        assert_sprintf \"%+ d\", 123, \"+123\"\n        assert_sprintf \"% +d\", 123, \"+123\"\n        assert_sprintf \"%+ 20d\", 123, \"                +123\"\n        assert_sprintf \"% +20d\", 123, \"                +123\"\n        assert_sprintf \"%+ 020d\", 123, \"+0000000000000000123\"\n        assert_sprintf \"% +020d\", 123, \"+0000000000000000123\"\n      end\n    end\n\n    context \"zero flag\" do\n      it \"left-pads the result with zeros\" do\n        assert_sprintf \"%020d\", 123, \"00000000000000000123\"\n        assert_sprintf \"%020d\", -123, \"-0000000000000000123\"\n        assert_sprintf \"%020d\", 0, \"00000000000000000000\"\n\n        assert_sprintf \"%+020d\", 123, \"+0000000000000000123\"\n        assert_sprintf \"%+020d\", -123, \"-0000000000000000123\"\n        assert_sprintf \"%+020d\", 0, \"+0000000000000000000\"\n\n        assert_sprintf \"% 020d\", 123, \" 0000000000000000123\"\n        assert_sprintf \"% 020d\", -123, \"-0000000000000000123\"\n        assert_sprintf \"% 020d\", 0, \" 0000000000000000000\"\n      end\n\n      it \"is ignored if string is left-justified\" do\n        assert_sprintf \"%-020d\", 123, \"123                 \"\n        assert_sprintf \"%-020d\", -123, \"-123                \"\n        assert_sprintf \"%-020d\", 0, \"0                   \"\n\n        assert_sprintf \"%0-20d\", 123, \"123                 \"\n        assert_sprintf \"%0-20d\", -123, \"-123                \"\n        assert_sprintf \"%0-20d\", 0, \"0                   \"\n\n        assert_sprintf \"%0*d\", [-20, 123], \"123                 \"\n        assert_sprintf \"%0*d\", [-20, -123], \"-123                \"\n        assert_sprintf \"%0*d\", [-20, 0], \"0                   \"\n\n        assert_sprintf \"%-0*d\", [-20, 123], \"123                 \"\n        assert_sprintf \"%-0*d\", [-20, -123], \"-123                \"\n        assert_sprintf \"%-0*d\", [-20, 0], \"0                   \"\n      end\n\n      it \"is ignored if precision is specified\" do\n        assert_sprintf \"%020.12d\", 123, \"        000000000123\"\n        assert_sprintf \"%020.12d\", -123, \"       -000000000123\"\n        assert_sprintf \"%020.12d\", 0, \"        000000000000\"\n\n        assert_sprintf \"%020.*d\", [12, 123], \"        000000000123\"\n        assert_sprintf \"%020.*d\", [12, -123], \"       -000000000123\"\n        assert_sprintf \"%020.*d\", [12, 0], \"        000000000000\"\n\n        assert_sprintf \"%020.*d\", [-12, 123], \"00000000000000000123\"\n        assert_sprintf \"%020.*d\", [-12, -123], \"-0000000000000000123\"\n        assert_sprintf \"%020.*d\", [-12, 0], \"00000000000000000000\"\n      end\n    end\n\n    context \"minus flag\" do\n      it \"left-justifies the string\" do\n        assert_sprintf \"%-d\", 123, \"123\"\n        assert_sprintf \"%-d\", -123, \"-123\"\n        assert_sprintf \"%-d\", 0, \"0\"\n\n        assert_sprintf \"%-20d\", 123, \"123                 \"\n        assert_sprintf \"%-20d\", -123, \"-123                \"\n        assert_sprintf \"%-20d\", 0, \"0                   \"\n\n        assert_sprintf \"%-4d\", 123, \"123 \"\n        assert_sprintf \"%-4d\", -123, \"-123\"\n        assert_sprintf \"%-4d\", 0, \"0   \"\n\n        assert_sprintf \"%-2d\", 123, \"123\"\n        assert_sprintf \"%-2d\", -123, \"-123\"\n        assert_sprintf \"%-2d\", 0, \"0 \"\n      end\n\n      it \"reserves space for the number prefix\" do\n        assert_sprintf \"%-+20d\", 123, \"+123                \"\n        assert_sprintf \"%-+20d\", -123, \"-123                \"\n        assert_sprintf \"%-+20d\", 0, \"+0                  \"\n\n        assert_sprintf \"%- 20d\", 123, \" 123                \"\n        assert_sprintf \"%- 20d\", -123, \"-123                \"\n        assert_sprintf \"%- 20d\", 0, \" 0                  \"\n\n        assert_sprintf \"%-#20b\", 123, \"0b1111011           \"\n        assert_sprintf \"%-#20b\", -123, \"-0b1111011          \"\n        assert_sprintf \"%-#20b\", 0, \"0                   \"\n      end\n    end\n\n    it \"works with Int*::MIN\" do\n      assert_sprintf \"%d\", Int8::MIN, \"-128\"\n      assert_sprintf \"%d\", Int16::MIN, \"-32768\"\n      assert_sprintf \"%d\", Int32::MIN, \"-2147483648\"\n      assert_sprintf \"%d\", Int64::MIN, \"-9223372036854775808\"\n    end\n\n    it \"works with BigInt\" do\n      assert_sprintf \"%d\", 123.to_big_i, \"123\"\n      assert_sprintf \"%300.250d\", 10.to_big_i ** 200, \"#{\" \" * 50}#{\"0\" * 49}1#{\"0\" * 200}\"\n      assert_sprintf \"%- #300.250X\", 16.to_big_i ** 200 - 1, \" 0X#{\"0\" * 50}#{\"F\" * 200}#{\" \" * 47}\"\n    end\n\n    it \"works with BigFloat\" do\n      assert_sprintf \"%d\", 123.to_big_f, \"123\"\n      assert_sprintf \"%80.70d\", 2.to_big_i ** 200, \"          0000000001606938044258990275541962092341162602522202993782792835301376\"\n      assert_sprintf \"%- #70.60X\", 2.to_big_f ** 200 - 2.to_big_f ** 120, \" 0X0000000000FFFFFFFFFFFFFFFFFFFF000000000000000000000000000000       \"\n    end\n\n    it \"works with BigDecimal\" do\n      assert_sprintf \"%d\", 123.to_big_d, \"123\"\n      assert_sprintf \"%300.250d\", 10.to_big_d ** 200, \"#{\" \" * 50}#{\"0\" * 49}1#{\"0\" * 200}\"\n      assert_sprintf \"%- #300.250X\", 16.to_big_d ** 200 - 1, \" 0X#{\"0\" * 50}#{\"F\" * 200}#{\" \" * 47}\"\n    end\n  end\n\n  it \"doesn't stop at null character when doing '%'\" do\n    assert_sprintf \"1\\u{0}%i\\u{0}3\", 2, \"1\\u00002\\u00003\"\n  end\n\n  if String::Formatter::HAS_RYU_PRINTF\n    describe \"floats\" do\n      context \"fixed format\" do\n        it \"works\" do\n          assert_sprintf \"%f\", 123, \"123.000000\"\n\n          assert_sprintf \"%12f\", 123.45, \"  123.450000\"\n          assert_sprintf \"%-12f\", 123.45, \"123.450000  \"\n          assert_sprintf \"% f\", 123.45, \" 123.450000\"\n          assert_sprintf \"%+f\", 123, \"+123.000000\"\n          assert_sprintf \"%012f\", 123, \"00123.000000\"\n          assert_sprintf \"%.f\", 1234.56, \"1235\"\n          assert_sprintf \"%.2f\", 1234.5678, \"1234.57\"\n          assert_sprintf \"%10.2f\", 1234.5678, \"   1234.57\"\n          assert_sprintf \"%*.2f\", [10, 1234.5678], \"   1234.57\"\n          assert_sprintf \"%*.*f\", [10, 2, 1234.5678], \"   1234.57\"\n          assert_sprintf \"%.2f\", 2.536_f32, \"2.54\"\n          assert_sprintf \"%+0*.*f\", [10, 2, 2.536_f32], \"+000002.54\"\n          assert_sprintf \"%#.0f\", 1234.56, \"1235.\"\n          assert_sprintf \"%#.1f\", 1234.56, \"1234.6\"\n\n          expect_raises(ArgumentError, \"Expected dynamic value '*' to be an Int - \\\"not a number\\\" (String)\") do\n            sprintf(\"%*f\", [\"not a number\", 2.536_f32])\n          end\n\n          assert_sprintf \"%12.2f %12.2f %6.2f %.2f\", [2.0, 3.0, 4.0, 5.0], \"        2.00         3.00   4.00 5.00\"\n\n          assert_sprintf \"%f\", 1e15, \"1000000000000000.000000\"\n        end\n      end\n\n      context \"scientific format\" do\n        it \"works\" do\n          assert_sprintf \"%e\", 123.45, \"1.234500e+02\"\n          assert_sprintf \"%E\", 123.45, \"1.234500E+02\"\n\n          assert_sprintf \"%e\", Float64::MAX, \"1.797693e+308\"\n          assert_sprintf \"%e\", Float64::MIN_POSITIVE, \"2.225074e-308\"\n          assert_sprintf \"%e\", Float64::MIN_SUBNORMAL, \"4.940656e-324\"\n          assert_sprintf \"%e\", 0.0, \"0.000000e+00\"\n          assert_sprintf \"%e\", -0.0, \"-0.000000e+00\"\n          assert_sprintf \"%e\", -Float64::MIN_SUBNORMAL, \"-4.940656e-324\"\n          assert_sprintf \"%e\", -Float64::MIN_POSITIVE, \"-2.225074e-308\"\n          assert_sprintf \"%e\", Float64::MIN, \"-1.797693e+308\"\n        end\n\n        context \"width specifier\" do\n          it \"sets the minimum length of the string\" do\n            assert_sprintf \"%20e\", 123.45, \"        1.234500e+02\"\n            assert_sprintf \"%20e\", -123.45, \"       -1.234500e+02\"\n            assert_sprintf \"%+20e\", 123.45, \"       +1.234500e+02\"\n\n            assert_sprintf \"%13e\", 123.45, \" 1.234500e+02\"\n            assert_sprintf \"%13e\", -123.45, \"-1.234500e+02\"\n            assert_sprintf \"%+13e\", 123.45, \"+1.234500e+02\"\n\n            assert_sprintf \"%12e\", 123.45, \"1.234500e+02\"\n            assert_sprintf \"%12e\", -123.45, \"-1.234500e+02\"\n            assert_sprintf \"%+12e\", 123.45, \"+1.234500e+02\"\n\n            assert_sprintf \"%2e\", 123.45, \"1.234500e+02\"\n            assert_sprintf \"%2e\", -123.45, \"-1.234500e+02\"\n            assert_sprintf \"%+2e\", 123.45, \"+1.234500e+02\"\n          end\n\n          it \"left-justifies on negative width\" do\n            assert_sprintf \"%*e\", [-20, 123.45], \"1.234500e+02        \"\n          end\n        end\n\n        context \"precision specifier\" do\n          it \"sets the minimum length of the fractional part\" do\n            assert_sprintf \"%.0e\", 2.0, \"2e+00\"\n            assert_sprintf \"%.0e\", 2.5.prev_float, \"2e+00\"\n            assert_sprintf \"%.0e\", 2.5, \"2e+00\"\n            assert_sprintf \"%.0e\", 2.5.next_float, \"3e+00\"\n            assert_sprintf \"%.0e\", 3.0, \"3e+00\"\n            assert_sprintf \"%.0e\", 3.5.prev_float, \"3e+00\"\n            assert_sprintf \"%.0e\", 3.5, \"4e+00\"\n            assert_sprintf \"%.0e\", 3.5.next_float, \"4e+00\"\n            assert_sprintf \"%.0e\", 4.0, \"4e+00\"\n\n            assert_sprintf \"%.0e\", 9.5, \"1e+01\"\n\n            assert_sprintf \"%.100e\", 1.1, \"1.1000000000000000888178419700125232338905334472656250000000000000000000000000000000000000000000000000e+00\"\n\n            assert_sprintf \"%.10000e\", 1.0, \"1.#{\"0\" * 10000}e+00\"\n\n            assert_sprintf \"%.1000e\", Float64::MIN_POSITIVE.prev_float,\n              \"2.2250738585072008890245868760858598876504231122409594654935248025624400092282356951\" \\\n              \"787758888037591552642309780950434312085877387158357291821993020294379224223559819827\" \\\n              \"501242041788969571311791082261043971979604000454897391938079198936081525613113376149\" \\\n              \"842043271751033627391549782731594143828136275113838604094249464942286316695429105080\" \\\n              \"201815926642134996606517803095075913058719846423906068637102005108723282784678843631\" \\\n              \"944515866135041223479014792369585208321597621066375401613736583044193603714778355306\" \\\n              \"682834535634005074073040135602968046375918583163124224521599262546494300836851861719\" \\\n              \"422417646455137135420132217031370496583210154654068035397417906022589503023501937519\" \\\n              \"773030945763173210852507299305089761582519159720757232455434770912461317493580281734\" \\\n              \"466552734375000000000000000000000000000000000000000000000000000000000000000000000000\" \\\n              \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" \\\n              \"000000000000000000000000000000000000000000000000000000000000000000000000000000e-308\"\n          end\n\n          it \"can be used with width\" do\n            assert_sprintf \"%20.13e\", 123.45, \" 1.2345000000000e+02\"\n            assert_sprintf \"%20.13e\", -123.45, \"-1.2345000000000e+02\"\n            assert_sprintf \"%20.13e\", 0.0, \" 0.0000000000000e+00\"\n\n            assert_sprintf \"%-20.13e\", 123.45, \"1.2345000000000e+02 \"\n            assert_sprintf \"%-20.13e\", -123.45, \"-1.2345000000000e+02\"\n            assert_sprintf \"%-20.13e\", 0.0, \"0.0000000000000e+00 \"\n\n            assert_sprintf \"%8.13e\", 123.45, \"1.2345000000000e+02\"\n            assert_sprintf \"%8.13e\", -123.45, \"-1.2345000000000e+02\"\n            assert_sprintf \"%8.13e\", 0.0, \"0.0000000000000e+00\"\n          end\n\n          it \"is ignored if precision argument is negative\" do\n            assert_sprintf \"%.*e\", [-2, 123.45], \"1.234500e+02\"\n          end\n        end\n\n        context \"sharp flag\" do\n          it \"prints a decimal point even if no digits follow\" do\n            assert_sprintf \"%#.0e\", 1.0, \"1.e+00\"\n            assert_sprintf \"%#.0e\", 10000.0, \"1.e+04\"\n            assert_sprintf \"%#.0e\", 1.0e+23, \"1.e+23\"\n            assert_sprintf \"%#.0e\", 1.0e-100, \"1.e-100\"\n            assert_sprintf \"%#.0e\", 0.0, \"0.e+00\"\n            assert_sprintf \"%#.0e\", -0.0, \"-0.e+00\"\n          end\n        end\n\n        context \"plus flag\" do\n          it \"writes a plus sign for positive values\" do\n            assert_sprintf \"%+e\", 123.45, \"+1.234500e+02\"\n            assert_sprintf \"%+e\", -123.45, \"-1.234500e+02\"\n            assert_sprintf \"%+e\", 0.0, \"+0.000000e+00\"\n          end\n\n          it \"writes plus sign after left space-padding\" do\n            assert_sprintf \"%+20e\", 123.45, \"       +1.234500e+02\"\n            assert_sprintf \"%+20e\", -123.45, \"       -1.234500e+02\"\n            assert_sprintf \"%+20e\", 0.0, \"       +0.000000e+00\"\n          end\n\n          it \"writes plus sign before left zero-padding\" do\n            assert_sprintf \"%+020e\", 123.45, \"+00000001.234500e+02\"\n            assert_sprintf \"%+020e\", -123.45, \"-00000001.234500e+02\"\n            assert_sprintf \"%+020e\", 0.0, \"+00000000.000000e+00\"\n          end\n        end\n\n        context \"space flag\" do\n          it \"writes a space for positive values\" do\n            assert_sprintf \"% e\", 123.45, \" 1.234500e+02\"\n            assert_sprintf \"% e\", -123.45, \"-1.234500e+02\"\n            assert_sprintf \"% e\", 0.0, \" 0.000000e+00\"\n          end\n\n          it \"writes space before left space-padding\" do\n            assert_sprintf \"% 20e\", 123.45, \"        1.234500e+02\"\n            assert_sprintf \"% 20e\", -123.45, \"       -1.234500e+02\"\n            assert_sprintf \"% 20e\", 0.0, \"        0.000000e+00\"\n\n            assert_sprintf \"% 020e\", 123.45, \" 00000001.234500e+02\"\n            assert_sprintf \"% 020e\", -123.45, \"-00000001.234500e+02\"\n            assert_sprintf \"% 020e\", 0.0, \" 00000000.000000e+00\"\n          end\n\n          it \"is ignored if plus flag is also specified\" do\n            assert_sprintf \"% +e\", 123.45, \"+1.234500e+02\"\n            assert_sprintf \"%+ e\", -123.45, \"-1.234500e+02\"\n          end\n        end\n\n        context \"zero flag\" do\n          it \"left-pads the result with zeros\" do\n            assert_sprintf \"%020e\", 123.45, \"000000001.234500e+02\"\n            assert_sprintf \"%020e\", -123.45, \"-00000001.234500e+02\"\n            assert_sprintf \"%020e\", 0.0, \"000000000.000000e+00\"\n          end\n\n          it \"is ignored if string is left-justified\" do\n            assert_sprintf \"%-020e\", 123.45, \"1.234500e+02        \"\n            assert_sprintf \"%-020e\", -123.45, \"-1.234500e+02       \"\n            assert_sprintf \"%-020e\", 0.0, \"0.000000e+00        \"\n          end\n\n          it \"can be used with precision\" do\n            assert_sprintf \"%020.12e\", 123.45, \"001.234500000000e+02\"\n            assert_sprintf \"%020.12e\", -123.45, \"-01.234500000000e+02\"\n            assert_sprintf \"%020.12e\", 0.0, \"000.000000000000e+00\"\n          end\n        end\n\n        context \"minus flag\" do\n          it \"left-justifies the string\" do\n            assert_sprintf \"%-20e\", 123.45, \"1.234500e+02        \"\n            assert_sprintf \"%-20e\", -123.45, \"-1.234500e+02       \"\n            assert_sprintf \"%-20e\", 0.0, \"0.000000e+00        \"\n          end\n        end\n      end\n\n      context \"general format\" do\n        it \"works\" do\n          assert_sprintf \"%g\", 123.45, \"123.45\"\n          assert_sprintf \"%G\", 123.45, \"123.45\"\n\n          assert_sprintf \"%g\", 1.2345e-5, \"1.2345e-05\"\n          assert_sprintf \"%G\", 1.2345e-5, \"1.2345E-05\"\n\n          assert_sprintf \"%g\", 1.2345e+25, \"1.2345e+25\"\n          assert_sprintf \"%G\", 1.2345e+25, \"1.2345E+25\"\n\n          assert_sprintf \"%g\", Float64::MAX, \"1.79769e+308\"\n          assert_sprintf \"%g\", Float64::MIN_POSITIVE, \"2.22507e-308\"\n          assert_sprintf \"%g\", Float64::MIN_SUBNORMAL, \"4.94066e-324\"\n          assert_sprintf \"%g\", 0.0, \"0\"\n          assert_sprintf \"%g\", -0.0, \"-0\"\n          assert_sprintf \"%g\", -Float64::MIN_SUBNORMAL, \"-4.94066e-324\"\n          assert_sprintf \"%g\", -Float64::MIN_POSITIVE, \"-2.22507e-308\"\n          assert_sprintf \"%g\", Float64::MIN, \"-1.79769e+308\"\n        end\n\n        context \"width specifier\" do\n          it \"sets the minimum length of the string\" do\n            assert_sprintf \"%10g\", 123.45, \"    123.45\"\n            assert_sprintf \"%10g\", -123.45, \"   -123.45\"\n            assert_sprintf \"%+10g\", 123.45, \"   +123.45\"\n\n            assert_sprintf \"%7g\", 123.45, \" 123.45\"\n            assert_sprintf \"%7g\", -123.45, \"-123.45\"\n            assert_sprintf \"%+7g\", 123.45, \"+123.45\"\n\n            assert_sprintf \"%6g\", 123.45, \"123.45\"\n            assert_sprintf \"%6g\", -123.45, \"-123.45\"\n            assert_sprintf \"%+6g\", 123.45, \"+123.45\"\n\n            assert_sprintf \"%2g\", 123.45, \"123.45\"\n            assert_sprintf \"%2g\", -123.45, \"-123.45\"\n            assert_sprintf \"%+2g\", 123.45, \"+123.45\"\n          end\n\n          it \"left-justifies on negative width\" do\n            assert_sprintf \"%*g\", [-10, 123.45], \"123.45    \"\n          end\n        end\n\n        context \"precision specifier\" do\n          it \"sets the precision of the value\" do\n            assert_sprintf \"%.0g\", 123.45, \"1e+02\"\n            assert_sprintf \"%.1g\", 123.45, \"1e+02\"\n            assert_sprintf \"%.2g\", 123.45, \"1.2e+02\"\n            assert_sprintf \"%.3g\", 123.45, \"123\"\n            assert_sprintf \"%.4g\", 123.45, \"123.5\"\n            assert_sprintf \"%.5g\", 123.45, \"123.45\"\n            assert_sprintf \"%.6g\", 123.45, \"123.45\"\n            assert_sprintf \"%.7g\", 123.45, \"123.45\"\n            assert_sprintf \"%.8g\", 123.45, \"123.45\"\n\n            assert_sprintf \"%.1000g\", 123.45, \"123.4500000000000028421709430404007434844970703125\"\n\n            assert_sprintf \"%.0g\", 1.23e-45, \"1e-45\"\n            assert_sprintf \"%.1g\", 1.23e-45, \"1e-45\"\n            assert_sprintf \"%.2g\", 1.23e-45, \"1.2e-45\"\n            assert_sprintf \"%.3g\", 1.23e-45, \"1.23e-45\"\n            assert_sprintf \"%.4g\", 1.23e-45, \"1.23e-45\"\n            assert_sprintf \"%.5g\", 1.23e-45, \"1.23e-45\"\n            assert_sprintf \"%.6g\", 1.23e-45, \"1.23e-45\"\n\n            assert_sprintf \"%.1000g\", 1e-5, \"1.0000000000000000818030539140313095458623138256371021270751953125e-05\"\n          end\n\n          it \"can be used with width\" do\n            assert_sprintf \"%10.1g\", 123.45, \"     1e+02\"\n            assert_sprintf \"%10.2g\", 123.45, \"   1.2e+02\"\n            assert_sprintf \"%10.3g\", 123.45, \"       123\"\n            assert_sprintf \"%10.4g\", 123.45, \"     123.5\"\n            assert_sprintf \"%10.5g\", 123.45, \"    123.45\"\n            assert_sprintf \"%10.1g\", -123.45, \"    -1e+02\"\n            assert_sprintf \"%10.2g\", -123.45, \"  -1.2e+02\"\n            assert_sprintf \"%10.3g\", -123.45, \"      -123\"\n            assert_sprintf \"%10.4g\", -123.45, \"    -123.5\"\n            assert_sprintf \"%10.5g\", -123.45, \"   -123.45\"\n            assert_sprintf \"%10.5g\", 0, \"         0\"\n\n            assert_sprintf \"%-10.1g\", 123.45, \"1e+02     \"\n            assert_sprintf \"%-10.2g\", 123.45, \"1.2e+02   \"\n            assert_sprintf \"%-10.3g\", 123.45, \"123       \"\n            assert_sprintf \"%-10.4g\", 123.45, \"123.5     \"\n            assert_sprintf \"%-10.5g\", 123.45, \"123.45    \"\n            assert_sprintf \"%-10.1g\", -123.45, \"-1e+02    \"\n            assert_sprintf \"%-10.2g\", -123.45, \"-1.2e+02  \"\n            assert_sprintf \"%-10.3g\", -123.45, \"-123      \"\n            assert_sprintf \"%-10.4g\", -123.45, \"-123.5    \"\n            assert_sprintf \"%-10.5g\", -123.45, \"-123.45   \"\n            assert_sprintf \"%-10.5g\", 0, \"0         \"\n\n            assert_sprintf \"%3.1g\", 123.45, \"1e+02\"\n            assert_sprintf \"%3.2g\", 123.45, \"1.2e+02\"\n            assert_sprintf \"%3.3g\", 123.45, \"123\"\n            assert_sprintf \"%3.4g\", 123.45, \"123.5\"\n            assert_sprintf \"%3.5g\", 123.45, \"123.45\"\n            assert_sprintf \"%3.1g\", -123.45, \"-1e+02\"\n            assert_sprintf \"%3.2g\", -123.45, \"-1.2e+02\"\n            assert_sprintf \"%3.3g\", -123.45, \"-123\"\n            assert_sprintf \"%3.4g\", -123.45, \"-123.5\"\n            assert_sprintf \"%3.5g\", -123.45, \"-123.45\"\n\n            assert_sprintf \"%1000.800g\", 123.45, \"#{\" \" * 950}123.4500000000000028421709430404007434844970703125\"\n          end\n\n          it \"is ignored if precision argument is negative\" do\n            assert_sprintf \"%.*g\", [-2, 123.45], \"123.45\"\n          end\n        end\n\n        context \"sharp flag\" do\n          it \"prints decimal point and trailing zeros\" do\n            assert_sprintf \"%#.0g\", 12345, \"1.e+04\"\n            assert_sprintf \"%#.6g\", 12345, \"12345.0\"\n            assert_sprintf \"%#.10g\", 12345, \"12345.00000\"\n            assert_sprintf \"%#.100g\", 12345, \"12345.#{\"0\" * 95}\"\n            assert_sprintf \"%#.1000g\", 12345, \"12345.#{\"0\" * 995}\"\n\n            assert_sprintf \"%#.0g\", 1e-5, \"1.e-05\"\n            assert_sprintf \"%#.6g\", 1e-5, \"1.00000e-05\"\n            assert_sprintf \"%#.10g\", 1e-5, \"1.000000000e-05\"\n            assert_sprintf \"%#.100g\", 1e-5, \"1.0000000000000000818030539140313095458623138256371021270751953125#{\"0\" * 35}e-05\"\n            assert_sprintf \"%#.1000g\", 1e-5, \"1.0000000000000000818030539140313095458623138256371021270751953125#{\"0\" * 935}e-05\"\n\n            assert_sprintf \"%#15.0g\", 12345, \"         1.e+04\"\n            assert_sprintf \"%#15.6g\", 12345, \"        12345.0\"\n            assert_sprintf \"%#15.10g\", 12345, \"    12345.00000\"\n          end\n        end\n\n        context \"plus flag\" do\n          it \"writes a plus sign for positive values\" do\n            assert_sprintf \"%+g\", 123.45, \"+123.45\"\n            assert_sprintf \"%+g\", -123.45, \"-123.45\"\n            assert_sprintf \"%+g\", 0.0, \"+0\"\n          end\n\n          it \"writes plus sign after left space-padding\" do\n            assert_sprintf \"%+10g\", 123.45, \"   +123.45\"\n            assert_sprintf \"%+10g\", -123.45, \"   -123.45\"\n            assert_sprintf \"%+10g\", 0.0, \"        +0\"\n          end\n\n          it \"writes plus sign before left zero-padding\" do\n            assert_sprintf \"%+010g\", 123.45, \"+000123.45\"\n            assert_sprintf \"%+010g\", -123.45, \"-000123.45\"\n            assert_sprintf \"%+010g\", 0.0, \"+000000000\"\n          end\n        end\n\n        context \"space flag\" do\n          it \"writes a space for positive values\" do\n            assert_sprintf \"% g\", 123.45, \" 123.45\"\n            assert_sprintf \"% g\", -123.45, \"-123.45\"\n            assert_sprintf \"% g\", 0.0, \" 0\"\n          end\n\n          it \"writes space before left space-padding\" do\n            assert_sprintf \"% 10g\", 123.45, \"    123.45\"\n            assert_sprintf \"% 10g\", -123.45, \"   -123.45\"\n            assert_sprintf \"% 10g\", 0.0, \"         0\"\n\n            assert_sprintf \"% 010g\", 123.45, \" 000123.45\"\n            assert_sprintf \"% 010g\", -123.45, \"-000123.45\"\n            assert_sprintf \"% 010g\", 0.0, \" 000000000\"\n          end\n\n          it \"is ignored if plus flag is also specified\" do\n            assert_sprintf \"% +g\", 123.45, \"+123.45\"\n            assert_sprintf \"%+ g\", -123.45, \"-123.45\"\n          end\n        end\n\n        context \"zero flag\" do\n          it \"left-pads the result with zeros\" do\n            assert_sprintf \"%010g\", 123.45, \"0000123.45\"\n            assert_sprintf \"%010g\", -123.45, \"-000123.45\"\n            assert_sprintf \"%010g\", 0.0, \"0000000000\"\n          end\n\n          it \"is ignored if string is left-justified\" do\n            assert_sprintf \"%-010g\", 123.45, \"123.45    \"\n            assert_sprintf \"%-010g\", -123.45, \"-123.45   \"\n            assert_sprintf \"%-010g\", 0.0, \"0         \"\n          end\n\n          it \"can be used with precision\" do\n            assert_sprintf \"%010.2g\", 123.45, \"0001.2e+02\"\n            assert_sprintf \"%010.2g\", -123.45, \"-001.2e+02\"\n            assert_sprintf \"%010.2g\", 0.0, \"0000000000\"\n          end\n        end\n\n        context \"minus flag\" do\n          it \"left-justifies the string\" do\n            assert_sprintf \"%-10g\", 123.45, \"123.45    \"\n            assert_sprintf \"%-10g\", -123.45, \"-123.45   \"\n            assert_sprintf \"%-10g\", 0.0, \"0         \"\n\n            assert_sprintf \"%- 10g\", 123.45, \" 123.45   \"\n            assert_sprintf \"%- 10g\", -123.45, \"-123.45   \"\n            assert_sprintf \"%- 10g\", 0.0, \" 0        \"\n          end\n        end\n      end\n\n      context \"hex format\" do\n        it \"works\" do\n          assert_sprintf \"%a\", 1194684.0, \"0x1.23abcp+20\"\n          assert_sprintf \"%A\", 1194684.0, \"0X1.23ABCP+20\"\n          assert_sprintf \"%a\", 12345678.45, \"0x1.78c29ce666666p+23\"\n          assert_sprintf \"%A\", 12345678.45, \"0X1.78C29CE666666P+23\"\n\n          assert_sprintf \"%a\", Float64::MAX, \"0x1.fffffffffffffp+1023\"\n          assert_sprintf \"%a\", Float64::MIN_POSITIVE, \"0x1p-1022\"\n          assert_sprintf \"%a\", Float64::MIN_SUBNORMAL, \"0x0.0000000000001p-1022\"\n          assert_sprintf \"%a\", 0.0, \"0x0p+0\"\n          assert_sprintf \"%a\", -0.0, \"-0x0p+0\"\n          assert_sprintf \"%a\", -Float64::MIN_SUBNORMAL, \"-0x0.0000000000001p-1022\"\n          assert_sprintf \"%a\", -Float64::MIN_POSITIVE, \"-0x1p-1022\"\n          assert_sprintf \"%a\", Float64::MIN, \"-0x1.fffffffffffffp+1023\"\n        end\n\n        context \"width specifier\" do\n          it \"sets the minimum length of the string\" do\n            assert_sprintf \"%20a\", hexfloat(\"0x1p+0\"), \"              0x1p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.2p+0\"), \"            0x1.2p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.23p+0\"), \"           0x1.23p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.234p+0\"), \"          0x1.234p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.2345p+0\"), \"         0x1.2345p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.23456p+0\"), \"        0x1.23456p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.234567p+0\"), \"       0x1.234567p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.2345678p+0\"), \"      0x1.2345678p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.23456789p+0\"), \"     0x1.23456789p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.23456789ap+0\"), \"    0x1.23456789ap+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.23456789abp+0\"), \"   0x1.23456789abp+0\"\n            assert_sprintf \"%20a\", hexfloat(\"0x1.23456789abcp+0\"), \"  0x1.23456789abcp+0\"\n\n            assert_sprintf \"%20a\", hexfloat(\"-0x1p+0\"), \"             -0x1p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.2p+0\"), \"           -0x1.2p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.23p+0\"), \"          -0x1.23p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.234p+0\"), \"         -0x1.234p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.2345p+0\"), \"        -0x1.2345p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.23456p+0\"), \"       -0x1.23456p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.234567p+0\"), \"      -0x1.234567p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.2345678p+0\"), \"     -0x1.2345678p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.23456789p+0\"), \"    -0x1.23456789p+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.23456789ap+0\"), \"   -0x1.23456789ap+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.23456789abp+0\"), \"  -0x1.23456789abp+0\"\n            assert_sprintf \"%20a\", hexfloat(\"-0x1.23456789abcp+0\"), \" -0x1.23456789abcp+0\"\n\n            assert_sprintf \"%+20a\", 1194684.0, \"      +0x1.23abcp+20\"\n\n            assert_sprintf \"%14a\", 1194684.0, \" 0x1.23abcp+20\"\n            assert_sprintf \"%14a\", -1194684.0, \"-0x1.23abcp+20\"\n            assert_sprintf \"%+14a\", 1194684.0, \"+0x1.23abcp+20\"\n\n            assert_sprintf \"%13a\", 1194684.0, \"0x1.23abcp+20\"\n            assert_sprintf \"%13a\", -1194684.0, \"-0x1.23abcp+20\"\n            assert_sprintf \"%+13a\", 1194684.0, \"+0x1.23abcp+20\"\n\n            assert_sprintf \"%2a\", 1194684.0, \"0x1.23abcp+20\"\n            assert_sprintf \"%2a\", -1194684.0, \"-0x1.23abcp+20\"\n            assert_sprintf \"%+2a\", 1194684.0, \"+0x1.23abcp+20\"\n          end\n\n          it \"left-justifies on negative width\" do\n            assert_sprintf \"%*a\", [-20, 1194684.0], \"0x1.23abcp+20       \"\n          end\n        end\n\n        context \"precision specifier\" do\n          it \"sets the minimum length of the fractional part\" do\n            assert_sprintf \"%.0a\", 0.0, \"0x0p+0\"\n\n            assert_sprintf \"%.0a\", (Float64::MIN_POSITIVE / 2).prev_float, \"0x0p-1022\"\n            assert_sprintf \"%.0a\", Float64::MIN_POSITIVE / 2, \"0x0p-1022\"\n            assert_sprintf \"%.0a\", (Float64::MIN_POSITIVE / 2).next_float, \"0x1p-1022\"\n            assert_sprintf \"%.0a\", Float64::MIN_POSITIVE.prev_float, \"0x1p-1022\"\n            assert_sprintf \"%.0a\", Float64::MIN_POSITIVE, \"0x1p-1022\"\n\n            assert_sprintf \"%.0a\", 0.0625, \"0x1p-4\"\n            assert_sprintf \"%.0a\", 0.0625.next_float, \"0x1p-4\"\n            assert_sprintf \"%.0a\", 0.09375.prev_float, \"0x1p-4\"\n            assert_sprintf \"%.0a\", 0.09375, \"0x2p-4\"\n            assert_sprintf \"%.0a\", 0.09375.next_float, \"0x2p-4\"\n            assert_sprintf \"%.0a\", 0.125.prev_float, \"0x2p-4\"\n            assert_sprintf \"%.0a\", 0.125, \"0x1p-3\"\n\n            assert_sprintf \"%.1a\", 2.0, \"0x1.0p+1\"\n            assert_sprintf \"%.1a\", 2.0.next_float, \"0x1.0p+1\"\n            assert_sprintf \"%.1a\", 2.0625.prev_float, \"0x1.0p+1\"\n            assert_sprintf \"%.1a\", 2.0625, \"0x1.0p+1\"\n            assert_sprintf \"%.1a\", 2.0625.next_float, \"0x1.1p+1\"\n            assert_sprintf \"%.1a\", 2.125.prev_float, \"0x1.1p+1\"\n            assert_sprintf \"%.1a\", 2.125, \"0x1.1p+1\"\n            assert_sprintf \"%.1a\", 2.125.next_float, \"0x1.1p+1\"\n            assert_sprintf \"%.1a\", 2.1875.prev_float, \"0x1.1p+1\"\n            assert_sprintf \"%.1a\", 2.1875, \"0x1.2p+1\"\n            assert_sprintf \"%.1a\", 2.1875.next_float, \"0x1.2p+1\"\n            assert_sprintf \"%.1a\", 2.25.prev_float, \"0x1.2p+1\"\n            assert_sprintf \"%.1a\", 2.25, \"0x1.2p+1\"\n\n            assert_sprintf \"%.1a\", 60.0, \"0x1.ep+5\"\n            assert_sprintf \"%.1a\", 60.0.next_float, \"0x1.ep+5\"\n            assert_sprintf \"%.1a\", 61.0.prev_float, \"0x1.ep+5\"\n            assert_sprintf \"%.1a\", 61.0, \"0x1.ep+5\"\n            assert_sprintf \"%.1a\", 61.0.next_float, \"0x1.fp+5\"\n            assert_sprintf \"%.1a\", 62.0.prev_float, \"0x1.fp+5\"\n            assert_sprintf \"%.1a\", 62.0, \"0x1.fp+5\"\n            assert_sprintf \"%.1a\", 62.0.next_float, \"0x1.fp+5\"\n            assert_sprintf \"%.1a\", 63.0.prev_float, \"0x1.fp+5\"\n            assert_sprintf \"%.1a\", 63.0, \"0x2.0p+5\"\n            assert_sprintf \"%.1a\", 63.0.next_float, \"0x2.0p+5\"\n            assert_sprintf \"%.1a\", 64.0.prev_float, \"0x2.0p+5\"\n            assert_sprintf \"%.1a\", 64.0, \"0x1.0p+6\"\n\n            assert_sprintf \"%.4a\", 65536.0, \"0x1.0000p+16\"\n            assert_sprintf \"%.4a\", 65536.0.next_float, \"0x1.0000p+16\"\n            assert_sprintf \"%.4a\", 65536.5.prev_float, \"0x1.0000p+16\"\n            assert_sprintf \"%.4a\", 65536.5, \"0x1.0000p+16\"\n            assert_sprintf \"%.4a\", 65536.5.next_float, \"0x1.0001p+16\"\n            assert_sprintf \"%.4a\", 65537.0.prev_float, \"0x1.0001p+16\"\n            assert_sprintf \"%.4a\", 65537.0, \"0x1.0001p+16\"\n            assert_sprintf \"%.4a\", 65537.0.next_float, \"0x1.0001p+16\"\n            assert_sprintf \"%.4a\", 65537.5.prev_float, \"0x1.0001p+16\"\n            assert_sprintf \"%.4a\", 65537.5, \"0x1.0002p+16\"\n            assert_sprintf \"%.4a\", 65537.5.next_float, \"0x1.0002p+16\"\n            assert_sprintf \"%.4a\", 65538.0.prev_float, \"0x1.0002p+16\"\n            assert_sprintf \"%.4a\", 65538.0, \"0x1.0002p+16\"\n\n            assert_sprintf \"%.4a\", 131070.0, \"0x1.fffep+16\"\n            assert_sprintf \"%.4a\", 131070.0.next_float, \"0x1.fffep+16\"\n            assert_sprintf \"%.4a\", 131070.5.prev_float, \"0x1.fffep+16\"\n            assert_sprintf \"%.4a\", 131070.5, \"0x1.fffep+16\"\n            assert_sprintf \"%.4a\", 131070.5.next_float, \"0x1.ffffp+16\"\n            assert_sprintf \"%.4a\", 131071.0.prev_float, \"0x1.ffffp+16\"\n            assert_sprintf \"%.4a\", 131071.0, \"0x1.ffffp+16\"\n            assert_sprintf \"%.4a\", 131071.0.next_float, \"0x1.ffffp+16\"\n            assert_sprintf \"%.4a\", 131071.5.prev_float, \"0x1.ffffp+16\"\n            assert_sprintf \"%.4a\", 131071.5, \"0x2.0000p+16\"\n            assert_sprintf \"%.4a\", 131071.5.next_float, \"0x2.0000p+16\"\n            assert_sprintf \"%.4a\", 131072.0.prev_float, \"0x2.0000p+16\"\n            assert_sprintf \"%.4a\", 131072.0, \"0x1.0000p+17\"\n\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x01, \"0x0.000000000000p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x07, \"0x0.000000000000p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x08, \"0x0.000000000000p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x09, \"0x0.000000000001p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x0f, \"0x0.000000000001p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x10, \"0x0.000000000001p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x11, \"0x0.000000000001p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x17, \"0x0.000000000001p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x18, \"0x0.000000000002p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x19, \"0x0.000000000002p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x1f, \"0x0.000000000002p-1022\"\n            assert_sprintf \"%.12a\", Float64::MIN_SUBNORMAL * 0x20, \"0x0.000000000002p-1022\"\n\n            assert_sprintf \"%.17a\", Float64::MAX, \"0x1.fffffffffffff0000p+1023\"\n            assert_sprintf \"%.16a\", Float64::MAX, \"0x1.fffffffffffff000p+1023\"\n            assert_sprintf \"%.15a\", Float64::MAX, \"0x1.fffffffffffff00p+1023\"\n            assert_sprintf \"%.14a\", Float64::MAX, \"0x1.fffffffffffff0p+1023\"\n            assert_sprintf \"%.13a\", Float64::MAX, \"0x1.fffffffffffffp+1023\"\n            assert_sprintf \"%.12a\", Float64::MAX, \"0x2.000000000000p+1023\"\n            assert_sprintf \"%.11a\", Float64::MAX, \"0x2.00000000000p+1023\"\n            assert_sprintf \"%.10a\", Float64::MAX, \"0x2.0000000000p+1023\"\n            assert_sprintf \"%.9a\", Float64::MAX, \"0x2.000000000p+1023\"\n            assert_sprintf \"%.8a\", Float64::MAX, \"0x2.00000000p+1023\"\n            assert_sprintf \"%.7a\", Float64::MAX, \"0x2.0000000p+1023\"\n            assert_sprintf \"%.6a\", Float64::MAX, \"0x2.000000p+1023\"\n            assert_sprintf \"%.5a\", Float64::MAX, \"0x2.00000p+1023\"\n            assert_sprintf \"%.4a\", Float64::MAX, \"0x2.0000p+1023\"\n            assert_sprintf \"%.3a\", Float64::MAX, \"0x2.000p+1023\"\n            assert_sprintf \"%.2a\", Float64::MAX, \"0x2.00p+1023\"\n            assert_sprintf \"%.1a\", Float64::MAX, \"0x2.0p+1023\"\n            assert_sprintf \"%.0a\", Float64::MAX, \"0x2p+1023\"\n\n            assert_sprintf \"%.1000a\", 1194684.0, \"0x1.23abc#{\"0\" * 995}p+20\"\n          end\n\n          it \"can be used with width\" do\n            assert_sprintf \"%20.8a\", 1194684.0, \"    0x1.23abc000p+20\"\n            assert_sprintf \"%20.8a\", -1194684.0, \"   -0x1.23abc000p+20\"\n            assert_sprintf \"%20.8a\", 0.0, \"     0x0.00000000p+0\"\n\n            assert_sprintf \"%-20.8a\", 1194684.0, \"0x1.23abc000p+20    \"\n            assert_sprintf \"%-20.8a\", -1194684.0, \"-0x1.23abc000p+20   \"\n            assert_sprintf \"%-20.8a\", 0.0, \"0x0.00000000p+0     \"\n\n            assert_sprintf \"%4.8a\", 1194684.0, \"0x1.23abc000p+20\"\n            assert_sprintf \"%4.8a\", -1194684.0, \"-0x1.23abc000p+20\"\n            assert_sprintf \"%4.8a\", 0.0, \"0x0.00000000p+0\"\n          end\n\n          it \"is ignored if precision argument is negative\" do\n            assert_sprintf \"%.*a\", [-2, 1194684.0], \"0x1.23abcp+20\"\n          end\n        end\n\n        context \"sharp flag\" do\n          it \"prints a decimal point even if no digits follow\" do\n            assert_sprintf \"%#a\", 1.0, \"0x1.p+0\"\n            assert_sprintf \"%#a\", Float64::MIN_POSITIVE, \"0x1.p-1022\"\n            assert_sprintf \"%#a\", 2.0 ** -234, \"0x1.p-234\"\n            assert_sprintf \"%#a\", 2.0 ** 1021, \"0x1.p+1021\"\n            assert_sprintf \"%#a\", 0.0, \"0x0.p+0\"\n            assert_sprintf \"%#a\", -0.0, \"-0x0.p+0\"\n\n            assert_sprintf \"%#.0a\", 1.0, \"0x1.p+0\"\n            assert_sprintf \"%#.0a\", Float64::MIN_POSITIVE, \"0x1.p-1022\"\n            assert_sprintf \"%#.0a\", 2.0 ** -234, \"0x1.p-234\"\n            assert_sprintf \"%#.0a\", 2.0 ** 1021, \"0x1.p+1021\"\n            assert_sprintf \"%#.0a\", 1194684.0, \"0x1.p+20\"\n            assert_sprintf \"%#.0a\", 0.0, \"0x0.p+0\"\n            assert_sprintf \"%#.0a\", -0.0, \"-0x0.p+0\"\n          end\n        end\n\n        context \"plus flag\" do\n          it \"writes a plus sign for positive values\" do\n            assert_sprintf \"%+a\", 1194684.0, \"+0x1.23abcp+20\"\n            assert_sprintf \"%+a\", -1194684.0, \"-0x1.23abcp+20\"\n            assert_sprintf \"%+a\", 0.0, \"+0x0p+0\"\n          end\n\n          it \"writes plus sign after left space-padding\" do\n            assert_sprintf \"%+20a\", 1194684.0, \"      +0x1.23abcp+20\"\n            assert_sprintf \"%+20a\", -1194684.0, \"      -0x1.23abcp+20\"\n            assert_sprintf \"%+20a\", 0.0, \"             +0x0p+0\"\n          end\n\n          it \"writes plus sign before left zero-padding\" do\n            assert_sprintf \"%+020a\", 1194684.0, \"+0x0000001.23abcp+20\"\n            assert_sprintf \"%+020a\", -1194684.0, \"-0x0000001.23abcp+20\"\n            assert_sprintf \"%+020a\", 0.0, \"+0x00000000000000p+0\"\n          end\n        end\n\n        context \"space flag\" do\n          it \"writes a space for positive values\" do\n            assert_sprintf \"% a\", 1194684.0, \" 0x1.23abcp+20\"\n            assert_sprintf \"% a\", -1194684.0, \"-0x1.23abcp+20\"\n            assert_sprintf \"% a\", 0.0, \" 0x0p+0\"\n          end\n\n          it \"writes space before left space-padding\" do\n            assert_sprintf \"% 20a\", 1194684.0, \"       0x1.23abcp+20\"\n            assert_sprintf \"% 20a\", -1194684.0, \"      -0x1.23abcp+20\"\n            assert_sprintf \"% 20a\", 0.0, \"              0x0p+0\"\n\n            assert_sprintf \"% 020a\", 1194684.0, \" 0x0000001.23abcp+20\"\n            assert_sprintf \"% 020a\", -1194684.0, \"-0x0000001.23abcp+20\"\n            assert_sprintf \"% 020a\", 0.0, \" 0x00000000000000p+0\"\n          end\n\n          it \"is ignored if plus flag is also specified\" do\n            assert_sprintf \"% +a\", 1194684.0, \"+0x1.23abcp+20\"\n            assert_sprintf \"%+ a\", -1194684.0, \"-0x1.23abcp+20\"\n          end\n        end\n\n        context \"zero flag\" do\n          it \"left-pads the result with zeros\" do\n            assert_sprintf \"%020a\", 1194684.0, \"0x00000001.23abcp+20\"\n            assert_sprintf \"%020a\", -1194684.0, \"-0x0000001.23abcp+20\"\n            assert_sprintf \"%020a\", 0.0, \"0x000000000000000p+0\"\n          end\n\n          it \"is ignored if string is left-justified\" do\n            assert_sprintf \"%-020a\", 1194684.0, \"0x1.23abcp+20       \"\n            assert_sprintf \"%-020a\", -1194684.0, \"-0x1.23abcp+20      \"\n            assert_sprintf \"%-020a\", 0.0, \"0x0p+0              \"\n          end\n\n          it \"can be used with precision\" do\n            assert_sprintf \"%020.8a\", 1194684.0, \"0x00001.23abc000p+20\"\n            assert_sprintf \"%020.8a\", -1194684.0, \"-0x0001.23abc000p+20\"\n            assert_sprintf \"%020.8a\", 0.0, \"0x000000.00000000p+0\"\n          end\n        end\n\n        context \"minus flag\" do\n          it \"left-justifies the string\" do\n            assert_sprintf \"%-20a\", 1194684.0, \"0x1.23abcp+20       \"\n            assert_sprintf \"%-20a\", -1194684.0, \"-0x1.23abcp+20      \"\n            assert_sprintf \"%-20a\", 0.0, \"0x0p+0              \"\n          end\n        end\n      end\n\n      [Float32, Float64].each do |float|\n        it \"infinities\" do\n          pos_inf = float.new(1) / float.new(0)\n          neg_inf = float.new(-1) / float.new(0)\n\n          assert_sprintf \"%f\", pos_inf, \"inf\"\n          assert_sprintf \"%a\", pos_inf, \"inf\"\n          assert_sprintf \"%e\", pos_inf, \"inf\"\n          assert_sprintf \"%g\", pos_inf, \"inf\"\n          assert_sprintf \"%A\", pos_inf, \"INF\"\n          assert_sprintf \"%E\", pos_inf, \"INF\"\n          assert_sprintf \"%G\", pos_inf, \"INF\"\n\n          assert_sprintf \"%f\", neg_inf, \"-inf\"\n          assert_sprintf \"%G\", neg_inf, \"-INF\"\n\n          assert_sprintf \"%2f\", pos_inf, \"inf\"\n          assert_sprintf \"%4f\", pos_inf, \" inf\"\n          assert_sprintf \"%6f\", pos_inf, \"   inf\"\n          assert_sprintf \"%2f\", neg_inf, \"-inf\"\n          assert_sprintf \"%4f\", neg_inf, \"-inf\"\n          assert_sprintf \"%6f\", neg_inf, \"  -inf\"\n\n          assert_sprintf \"% f\", pos_inf, \" inf\"\n          assert_sprintf \"% 2f\", pos_inf, \" inf\"\n          assert_sprintf \"% 4f\", pos_inf, \" inf\"\n          assert_sprintf \"% 6f\", pos_inf, \"   inf\"\n          assert_sprintf \"% f\", neg_inf, \"-inf\"\n          assert_sprintf \"% 2f\", neg_inf, \"-inf\"\n          assert_sprintf \"% 4f\", neg_inf, \"-inf\"\n          assert_sprintf \"% 6f\", neg_inf, \"  -inf\"\n\n          assert_sprintf \"%+f\", pos_inf, \"+inf\"\n          assert_sprintf \"%+2f\", pos_inf, \"+inf\"\n          assert_sprintf \"%+4f\", pos_inf, \"+inf\"\n          assert_sprintf \"%+6f\", pos_inf, \"  +inf\"\n          assert_sprintf \"%+f\", neg_inf, \"-inf\"\n          assert_sprintf \"%+2f\", neg_inf, \"-inf\"\n          assert_sprintf \"%+4f\", neg_inf, \"-inf\"\n          assert_sprintf \"%+6f\", neg_inf, \"  -inf\"\n\n          assert_sprintf \"%+ f\", pos_inf, \"+inf\"\n\n          assert_sprintf \"%-4f\", pos_inf, \"inf \"\n          assert_sprintf \"%-6f\", pos_inf, \"inf   \"\n          assert_sprintf \"%-4f\", neg_inf, \"-inf\"\n          assert_sprintf \"%-6f\", neg_inf, \"-inf  \"\n\n          assert_sprintf \"% -4f\", pos_inf, \" inf\"\n          assert_sprintf \"% -6f\", pos_inf, \" inf  \"\n          assert_sprintf \"% -4f\", neg_inf, \"-inf\"\n          assert_sprintf \"% -6f\", neg_inf, \"-inf  \"\n\n          assert_sprintf \"%-+4f\", pos_inf, \"+inf\"\n          assert_sprintf \"%-+6f\", pos_inf, \"+inf  \"\n          assert_sprintf \"%-+4f\", neg_inf, \"-inf\"\n          assert_sprintf \"%-+6f\", neg_inf, \"-inf  \"\n\n          assert_sprintf \"%-+ 6f\", pos_inf, \"+inf  \"\n\n          assert_sprintf \"%06f\", pos_inf, \"   inf\"\n          assert_sprintf \"%-06f\", pos_inf, \"inf   \"\n          assert_sprintf \"%06f\", neg_inf, \"  -inf\"\n          assert_sprintf \"%-06f\", neg_inf, \"-inf  \"\n\n          assert_sprintf \"%.1f\", pos_inf, \"inf\"\n\n          assert_sprintf \"%#f\", pos_inf, \"inf\"\n        end\n\n        it \"not-a-numbers\" do\n          pos_nan = Math.copysign(float.new(0) / float.new(0), 1)\n          neg_nan = Math.copysign(float.new(0) / float.new(0), -1)\n\n          assert_sprintf \"%f\", pos_nan, \"nan\"\n          assert_sprintf \"%a\", pos_nan, \"nan\"\n          assert_sprintf \"%e\", pos_nan, \"nan\"\n          assert_sprintf \"%g\", pos_nan, \"nan\"\n          assert_sprintf \"%A\", pos_nan, \"NAN\"\n          assert_sprintf \"%E\", pos_nan, \"NAN\"\n          assert_sprintf \"%G\", pos_nan, \"NAN\"\n\n          assert_sprintf \"%f\", neg_nan, \"nan\"\n          assert_sprintf \"%a\", neg_nan, \"nan\"\n          assert_sprintf \"%e\", neg_nan, \"nan\"\n          assert_sprintf \"%g\", neg_nan, \"nan\"\n          assert_sprintf \"%A\", neg_nan, \"NAN\"\n          assert_sprintf \"%E\", neg_nan, \"NAN\"\n          assert_sprintf \"%G\", neg_nan, \"NAN\"\n\n          assert_sprintf \"%+f\", pos_nan, \"+nan\"\n          assert_sprintf \"%+f\", neg_nan, \"+nan\"\n        end\n      end\n    end\n  else\n    pending \"floats\"\n  end\n\n  context \"chars\" do\n    it \"works\" do\n      assert_sprintf \"%c\", 'a', \"a\"\n      assert_sprintf \"%3c\", 'R', \"  R\"\n      assert_sprintf \"%-3c\", 'L', \"L  \"\n      assert_sprintf \"%c\", '▞', \"▞\"\n      assert_sprintf \"%c\", 65, \"A\"\n      assert_sprintf \"%c\", 66_i8, \"B\"\n      assert_sprintf \"%c\", 67_i16, \"C\"\n      assert_sprintf \"%c\", 68_i32, \"D\"\n      assert_sprintf \"%c\", 69_i64, \"E\"\n      assert_sprintf \"%c\", 97_u8, \"a\"\n      assert_sprintf \"%c\", 98_u16, \"b\"\n      assert_sprintf \"%c\", 99_u32, \"c\"\n      assert_sprintf \"%c\", 100_u64, \"d\"\n      assert_sprintf \"%c\", 0x259E, \"▞\"\n    end\n\n    it \"raises if not a Char or Int\" do\n      expect_raises(ArgumentError, \"Expected a char or integer\") { sprintf(\"%c\", \"this\") }\n      expect_raises(ArgumentError, \"Expected a char or integer\") { sprintf(\"%c\", 17.34) }\n    end\n  end\n\n  context \"strings\" do\n    it \"works\" do\n      assert_sprintf \"%s\", 'a', \"a\"\n      assert_sprintf \"%-s\", 'a', \"a\"\n      assert_sprintf \"%20s\", 'a', \"                   a\"\n      assert_sprintf \"%-20s\", 'a', \"a                   \"\n      assert_sprintf \"%*s\", [10, 123], \"       123\"\n      assert_sprintf \"%*s\", [-10, 123], \"123       \"\n      assert_sprintf \"%.5s\", \"foo bar baz\", \"foo b\"\n      assert_sprintf \"%.*s\", [5, \"foo bar baz\"], \"foo b\"\n      assert_sprintf \"%*.*s\", [20, 5, \"foo bar baz\"], \"               foo b\"\n      assert_sprintf \"%-*.*s\", [20, 5, \"foo bar baz\"], \"foo b               \"\n    end\n\n    it \"calls to_s on non-strings\" do\n      span = 1.second\n      assert_sprintf \"%s\", span, span.to_s\n    end\n  end\n\n  context \"plain substitution\" do\n    it \"substitutes one placeholder\" do\n      assert_sprintf \"change %{this}\", {\"this\" => \"nothing\"}, \"change nothing\"\n      assert_sprintf \"change %{this}\", {this: \"nothing\"}, \"change nothing\"\n    end\n\n    it \"substitutes multiple placeholder\" do\n      assert_sprintf \"change %{this} and %{more}\", {\"this\" => \"nothing\", \"more\" => \"something\"}, \"change nothing and something\"\n      assert_sprintf \"change %{this} and %{more}\", {this: \"nothing\", more: \"something\"}, \"change nothing and something\"\n    end\n\n    it \"throws an error when the key is not found\" do\n      expect_raises(KeyError) { sprintf(\"change %{this}\", {\"that\" => \"wrong key\"}) }\n      expect_raises(KeyError) { sprintf(\"change %{this}\", {that: \"wrong key\"}) }\n    end\n\n    it \"raises if expecting hash or named tuple but not given\" do\n      expect_raises(ArgumentError, \"One hash or named tuple required\") { sprintf(\"change %{this}\", \"this\") }\n    end\n\n    it \"doesn't raise if 1-element list of hash or named tuple given\" do\n      assert_sprintf \"change %{this}\", [{\"this\" => \"nothing\"}], \"change nothing\"\n      assert_sprintf \"change %{this}\", [{this: \"nothing\"}], \"change nothing\"\n      assert_sprintf \"change %{this}\", { {\"this\" => \"nothing\"} }, \"change nothing\"\n      assert_sprintf \"change %{this}\", { {this: \"nothing\"} }, \"change nothing\"\n    end\n\n    it \"raises on unbalanced curly\" do\n      expect_raises(ArgumentError, \"Malformed name - unmatched parenthesis\") { sprintf(\"change %{this\", {\"this\" => 1}) }\n    end\n\n    it \"doesn't raise on balanced curly with null byte\" do\n      assert_sprintf \"change %{this\\u{0}}\", {\"this\\u{0}\" => 1}, \"change 1\"\n    end\n\n    it \"raises if sequential parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix named parameters with sequential ones\") { sprintf(\"%{this}%d\", {\"this\" => 1}) }\n    end\n\n    it \"raises if numbered parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix named parameters with numbered ones\") { sprintf(\"%{this} %1$d\", {\"this\" => 1}) }\n    end\n\n    it \"doesn't raise if formatted substitution also given\" do\n      assert_sprintf \"%{foo}%<bar>s\", {\"foo\" => \"x\", \"bar\" => \"y\"}, \"xy\"\n    end\n  end\n\n  context \"formatted substitution\" do\n    it \"applies formatting to %<...> placeholder\" do\n      assert_sprintf \"change %<this>.2f\", {\"this\" => 23.456}, \"change 23.46\"\n      assert_sprintf \"change %<this>.2f\", {this: 23.456}, \"change 23.46\"\n    end\n\n    it \"raises if sequential parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix named parameters with sequential ones\") { sprintf(\"%<this>d%d\", {\"this\" => 1}) }\n    end\n\n    it \"raises if numbered parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix named parameters with numbered ones\") { sprintf(\"%<this>1$d\", {\"this\" => 1}) }\n      expect_raises(ArgumentError, \"Cannot mix named parameters with numbered ones\") { sprintf(\"%<this>*1$d\", {\"this\" => 1}) }\n      expect_raises(ArgumentError, \"Cannot mix named parameters with numbered ones\") { sprintf(\"%<this>.*1$d\", {\"this\" => 1}) }\n      expect_raises(ArgumentError, \"Cannot mix named parameters with numbered ones\") { sprintf(\"%<this>d %1$d\", {\"this\" => 1}) }\n    end\n\n    it \"doesn't raise if plain substitution also given\" do\n      assert_sprintf \"%<foo>s%{bar}\", {\"foo\" => \"x\", \"bar\" => \"y\"}, \"xy\"\n    end\n  end\n\n  context \"sequential parameters\" do\n    it \"raises if named parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix sequential parameters with named ones\") { sprintf(\"%d%{this}\", 1) }\n      expect_raises(ArgumentError, \"Cannot mix sequential parameters with named ones\") { sprintf(\"%d%<this>d\", 1) }\n    end\n\n    it \"raises if numbered parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix sequential parameters with numbered ones\") { sprintf(\"%d %1$d\", 1) }\n    end\n  end\n\n  context \"numbered parameters\" do\n    it \"gets argument at specified index\" do\n      assert_sprintf \"%2$d %3$x %1$s\", [\"foo\", 123, 0xabc], \"123 abc foo\"\n    end\n\n    it \"gets width and precision specifier at specified index\" do\n      assert_sprintf \"%2$*1$d\", [5, 123], \"  123\"\n      assert_sprintf \"%2$.*1$s\", [5, \"abcdefghij\"], \"abcde\"\n      assert_sprintf \"%-3$*1$.*2$s\", [10, 5, \"abcdefghij\"], \"abcde     \"\n    end\n\n    it \"raises if index is out of bounds\" do\n      expect_raises(ArgumentError, \"Too few arguments\") { sprintf(\"%1$d\") }\n      expect_raises(ArgumentError, \"Too few arguments\") { sprintf(\"%5$d\", 1, 2, 3, 4) }\n    end\n\n    it \"raises if index is zero\" do\n      expect_raises(ArgumentError) { sprintf(\"%0$d\") }\n      expect_raises(ArgumentError) { sprintf(\"%1$*0$d\", 1) }\n      expect_raises(ArgumentError) { sprintf(\"%1$.*0$d\", 1) }\n    end\n\n    it \"can be used before flags\" do\n      assert_sprintf \"%1$ d\", 123, \" 123\"\n      assert_sprintf \"%1$+d\", 123, \"+123\"\n      assert_sprintf \"%1$5d\", 123, \"  123\"\n      assert_sprintf \"%1$-5d\", 123, \"123  \"\n      assert_sprintf \"%1$#x\", 123, \"0x7b\"\n    end\n\n    it \"raises if multiple indices specified\" do\n      expect_raises(ArgumentError, \"Cannot specify parameter number more than once\") { sprintf(\"%1$2$d\", 1, 2) }\n      expect_raises(ArgumentError, \"Cannot specify parameter number more than once\") { sprintf(\"%1$-2$d\", 1, 2) }\n    end\n\n    it \"raises if used as width or precision specifier of a sequential parameter\" do\n      expect_raises(ArgumentError, \"Cannot mix numbered parameters with sequential ones\") { sprintf(\"%*1$d\", 1) }\n      expect_raises(ArgumentError, \"Cannot mix numbered parameters with sequential ones\") { sprintf(\"%.*1$d\", 1) }\n    end\n\n    it \"raises if sequential parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix numbered parameters with sequential ones\") { sprintf(\"%1$d %d\", 1) }\n    end\n\n    it \"raises if named parameters also given\" do\n      expect_raises(ArgumentError, \"Cannot mix numbered parameters with named ones\") { sprintf(\"%1$d %{this}\", 1) }\n      expect_raises(ArgumentError, \"Cannot mix numbered parameters with named ones\") { sprintf(\"%1$d %<this>d\", 1) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/static_array_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\ndescribe \"StaticArray\" do\n  it \"creates with new\" do\n    a = StaticArray(Int32, 3).new 0\n    a.size.should eq(3)\n  end\n\n  it \"creates with new and value\" do\n    a = StaticArray(Int32, 3).new 1\n    a.size.should eq(3)\n    a[0].should eq(1)\n    a[1].should eq(1)\n    a[2].should eq(1)\n  end\n\n  it \"creates with new and block\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.size.should eq(3)\n    a[0].should eq(1)\n    a[1].should eq(2)\n    a[2].should eq(3)\n  end\n\n  it \"raises index out of bounds on read\" do\n    a = StaticArray(Int32, 3).new 0\n    expect_raises IndexError do\n      a[4]\n    end\n  end\n\n  it \"raises index out of bounds on write\" do\n    a = StaticArray(Int32, 3).new 0\n    expect_raises IndexError do\n      a[4] = 1\n    end\n  end\n\n  it \"allows using negative indices\" do\n    a = StaticArray(Int32, 3).new 0\n    a[-1] = 2\n    a[-1].should eq(2)\n    a[2].should eq(2)\n  end\n\n  describe \"==\" do\n    it \"compares empty\" do\n      (StaticArray(Int32, 0).new(0)).should eq(StaticArray(Int32, 0).new(0))\n      (StaticArray(Int32, 1).new(0)).should_not eq(StaticArray(Int32, 0).new(0))\n      (StaticArray(Int32, 0).new(0)).should_not eq(StaticArray(Int32, 1).new(0))\n    end\n\n    it \"compares elements\" do\n      a = StaticArray(Int32, 3).new { |i| i * 2 }\n      a.should eq(StaticArray(Int32, 3).new { |i| i * 2 })\n      a.should_not eq(StaticArray(Int32, 3).new { |i| i * 3 })\n    end\n\n    it \"compares other\" do\n      (StaticArray(Int32, 0).new(0)).should_not be_nil\n      (StaticArray(Int32, 3).new(0)).should eq(StaticArray(Int8, 3).new(0_i8))\n    end\n  end\n\n  describe \"<=>\" do\n    it \"correctly compares two static arrays\" do\n      array1 = StaticArray(Int32, 3).new(5)\n      array2 = StaticArray(Int32, 3).new(7)\n      (array1 <=> array2).should be < 0\n      (array2 <=> array1).should be > 0\n      (array1 <=> array1).should eq 0\n    end\n  end\n\n  describe \"values_at\" do\n    it \"returns the given indexes\" do\n      StaticArray(Int32, 4).new { |i| i + 1 }.values_at(1, 0, 2).should eq({2, 1, 3})\n    end\n\n    it \"raises when passed an invalid index\" do\n      expect_raises IndexError do\n        StaticArray(Int32, 1).new { |i| i + 1 }.values_at(10)\n      end\n    end\n  end\n\n  it \"does to_s\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.to_s.should eq(\"StaticArray[1, 2, 3]\")\n  end\n\n  describe \"#fill\" do\n    it \"replaces values in a subrange\" do\n      a = StaticArray[0, 1, 2, 3, 4]\n      a.fill(7)\n      a.should eq(StaticArray[7, 7, 7, 7, 7])\n\n      a = StaticArray[0, 1, 2, 3, 4]\n      a.fill(7, 1, 2)\n      a.should eq(StaticArray[0, 7, 7, 3, 4])\n\n      a = StaticArray[0, 1, 2, 3, 4]\n      a.fill(7, 2..3)\n      a.should eq(StaticArray[0, 1, 7, 7, 4])\n\n      a = StaticArray[0, 0, 0, 0, 0]\n      a.fill { |i| i + 7 }\n      a.should eq(StaticArray[7, 8, 9, 10, 11])\n\n      a = StaticArray[0, 0, 0, 0, 0]\n      a.fill(offset: 2) { |i| i * i }\n      a.should eq(StaticArray[4, 9, 16, 25, 36])\n\n      a = StaticArray[0, 0, 0, 0, 0]\n      a.fill(1, 2) { |i| i + 7 }\n      a.should eq(StaticArray[0, 8, 9, 0, 0])\n\n      a = StaticArray[0, 0, 0, 0, 0]\n      a.fill(2..3) { |i| i + 7 }\n      a.should eq(StaticArray[0, 0, 9, 10, 0])\n    end\n  end\n\n  it \"shuffles\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.shuffle!\n\n    (a[0] + a[1] + a[2]).should eq(6)\n\n    3.times do |i|\n      a.should contain(i + 1)\n    end\n  end\n\n  it \"shuffles with a seed\" do\n    a = StaticArray(Int32, 10).new { |i| i + 1 }\n    b = StaticArray(Int32, 10).new { |i| i + 1 }\n    a.shuffle!(Random.new(42))\n    b.shuffle!(Random.new(42))\n\n    10.times do |i|\n      a[i].should eq(b[i])\n    end\n  end\n\n  it \"reverse\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.reverse!\n    a[0].should eq(3)\n    a[1].should eq(2)\n    a[2].should eq(1)\n  end\n\n  it \"does map\" do\n    a = StaticArray[0, 1, 2]\n    b = a.map { |e| e * 2 }\n    b.should eq(StaticArray[0, 2, 4])\n  end\n\n  it \"does map!\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.map! { |i| i + 1 }\n    a[0].should eq(2)\n    a[1].should eq(3)\n    a[2].should eq(4)\n  end\n\n  it \"does map_with_index\" do\n    a = StaticArray[1, 1, 2, 2]\n    b = a.map_with_index { |e, i| e + i }\n    b.should eq(StaticArray[1, 2, 4, 5])\n  end\n\n  it \"does map_with_index, with offset\" do\n    a = StaticArray[1, 1, 2, 2]\n    b = a.map_with_index(10) { |e, i| e + i }\n    b.should eq(StaticArray[11, 12, 14, 15])\n  end\n\n  it \"does map_with_index!\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.map_with_index! { |e, i| i * 2 }\n    a[0].should eq(0)\n    a[1].should eq(2)\n    a[2].should eq(4)\n    a.should be_a(StaticArray(Int32, 3))\n  end\n\n  it \"does map_with_index!, with offset\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.map_with_index!(10) { |e, i| i * 2 }\n    a[0].should eq(20)\n    a[1].should eq(22)\n    a[2].should eq(24)\n    a.should be_a(StaticArray(Int32, 3))\n  end\n\n  describe \"rotate!\" do\n    it do\n      a = StaticArray[1, 2, 3]\n      a.rotate!; a.should eq(StaticArray[2, 3, 1])\n      a.rotate!; a.should eq(StaticArray[3, 1, 2])\n      a.rotate!; a.should eq(StaticArray[1, 2, 3])\n      a.rotate!; a.should eq(StaticArray[2, 3, 1])\n    end\n\n    it { a = StaticArray[1, 2, 3]; a.rotate!(0); a.should eq(StaticArray[1, 2, 3]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(1); a.should eq(StaticArray[2, 3, 1]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(2); a.should eq(StaticArray[3, 1, 2]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(3); a.should eq(StaticArray[1, 2, 3]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(4); a.should eq(StaticArray[2, 3, 1]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(3001); a.should eq(StaticArray[2, 3, 1]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(-1); a.should eq(StaticArray[3, 1, 2]) }\n    it { a = StaticArray[1, 2, 3]; a.rotate!(-3001); a.should eq(StaticArray[3, 1, 2]) }\n\n    it do\n      a = StaticArray(Int32, 50).new { |i| i }\n      a.rotate!(5)\n      a.should eq(StaticArray[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, 0, 1, 2, 3, 4])\n    end\n\n    it do\n      a = StaticArray(Int32, 50).new { |i| i }\n      a.rotate!(-5)\n      a.should eq(StaticArray[45, 46, 47, 48, 49, 0, 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])\n    end\n\n    it do\n      a = StaticArray(Int32, 50).new { |i| i }\n      a.rotate!(20)\n      a.should eq(StaticArray[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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])\n    end\n\n    it do\n      a = StaticArray(Int32, 50).new { |i| i }\n      a.rotate!(-20)\n      a.should eq(StaticArray[30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 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])\n    end\n  end\n\n  it \"updates value\" do\n    a = StaticArray(Int32, 3).new { |i| i + 1 }\n    a.update(1) { |x| x * 2 }\n    a[0].should eq(1)\n    a[1].should eq(4)\n    a[2].should eq(3)\n  end\n\n  it \"clones\" do\n    a = StaticArray(Array(Int32), 1).new { |i| [1] }\n    b = a.clone\n    b[0].should eq(a[0])\n    b[0].should_not be(a[0])\n  end\n\n  {% for sort in [\"sort\".id, \"unstable_sort\".id] %}\n    describe {{ \"##{sort}\" }} do\n      it \"sort without block\" do\n        a = StaticArray[3, 4, 1, 2, 5, 6]\n        b = a.{{ sort }}\n        b.should eq(StaticArray[1, 2, 3, 4, 5, 6])\n        a.should_not eq(b)\n      end\n\n      it \"sort with a block\" do\n        a = StaticArray[\"foo\", \"a\", \"hello\"]\n        b = a.{{ sort }} { |x, y| x.size <=> y.size }\n        b.should eq(StaticArray[\"a\", \"foo\", \"hello\"])\n        a.should_not eq(b)\n      end\n    end\n\n    describe {{ \"##{sort}!\" }} do\n      it \"sort! without block\" do\n        a = StaticArray[3, 4, 1, 2, 5, 6]\n        a.{{ sort }}!\n        a.should eq(StaticArray[1, 2, 3, 4, 5, 6])\n      end\n\n      it \"sort! with a block\" do\n        a = StaticArray[\"foo\", \"a\", \"hello\"]\n        a.{{ sort }}! { |x, y| x.size <=> y.size }\n        a.should eq(StaticArray[\"a\", \"foo\", \"hello\"])\n      end\n    end\n\n    # StaticArray#sort_by and #sort_by! don't compile on aarch64-darwin and\n    # aarch64-linux-musl due to a codegen error caused by LLVM < 13.0.0.\n    # See https://github.com/crystal-lang/crystal/issues/11358 for details.\n    {% unless compare_versions(Crystal::LLVM_VERSION, \"13.0.0\") < 0 && flag?(:aarch64) && (flag?(:musl) || flag?(:darwin) || flag?(:android)) %}\n      describe \"{{ sort }}_by\" do\n        it \"sorts by\" do\n          a = StaticArray[\"foo\", \"a\", \"hello\"]\n          b = a.{{ sort }}_by(&.size)\n          b.should eq(StaticArray[\"a\", \"foo\", \"hello\"])\n          a.should_not eq(b)\n        end\n      end\n\n      describe \"{{ sort }}_by!\" do\n        it \"sorts by!\" do\n          a = StaticArray[\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}_by!(&.size)\n          a.should eq(StaticArray[\"a\", \"foo\", \"hello\"])\n        end\n\n        it \"calls given block exactly once for each element\" do\n          calls = Hash(String, Int32).new(0)\n          a = StaticArray[\"foo\", \"a\", \"hello\"]\n          a.{{ sort }}_by! { |e| calls[e] += 1; e.size }\n          calls.should eq({\"foo\" => 1, \"a\" => 1, \"hello\" => 1})\n        end\n      end\n    {% else %}\n      pending \"{{ sort }}_by\"\n      pending \"{{ sort }}_by!\"\n    {% end %}\n  {% end %}\n\n  it_iterates \"#each\", [1, 2, 3], StaticArray[1, 2, 3].each\n  it_iterates \"#reverse_each\", [3, 2, 1], StaticArray[1, 2, 3].reverse_each\n  it_iterates \"#each_index\", [0, 1, 2], StaticArray[1, 2, 3].each_index\nend\n"
  },
  {
    "path": "spec/std/string/grapheme_break_spec.cr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_grapheme_break_spec.cr\n#\n# See https://www.unicode.org/license.html for the Unicode license agreement.\n# DO NOT EDIT\n\nrequire \"./spec_helper\"\n\ndescribe \"String#each_grapheme\" do\n  it_iterates_graphemes \"\\r\\r\", ['\\r', '\\r']                                                                                                                                                                                            #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\r\", ['\\r', '\\u0308', '\\r']                                                                                                                                                                            #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\n\", [\"\\r\\n\"]                                                                                                                                                                                                #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\n\", ['\\r', '\\u0308', '\\n']                                                                                                                                                                            #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0000\", ['\\r', '\\0']                                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u0000\", ['\\r', '\\u0308', '\\0']                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u094D\", ['\\r', '\\u094D']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u094D\", ['\\r', \"\\u0308\\u094D\"]                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0300\", ['\\r', '\\u0300']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u0300\", ['\\r', \"\\u0308\\u0300\"]                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u200C\", ['\\r', '\\u200C']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u200C\", ['\\r', \"\\u0308\\u200C\"]                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u200D\", ['\\r', '\\u200D']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u200D\", ['\\r', \"\\u0308\\u200D\"]                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u{1F1E6}\", ['\\r', '\\u{1F1E6}']                                                                                                                                                                              #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u{1F1E6}\", ['\\r', '\\u0308', '\\u{1F1E6}']                                                                                                                                                              #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u06DD\", ['\\r', '\\u06DD']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u06DD\", ['\\r', '\\u0308', '\\u06DD']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0903\", ['\\r', '\\u0903']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u0903\", ['\\r', \"\\u0308\\u0903\"]                                                                                                                                                                        #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u1100\", ['\\r', '\\u1100']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u1100\", ['\\r', '\\u0308', '\\u1100']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u1160\", ['\\r', '\\u1160']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u1160\", ['\\r', '\\u0308', '\\u1160']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u11A8\", ['\\r', '\\u11A8']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u11A8\", ['\\r', '\\u0308', '\\u11A8']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\uAC00\", ['\\r', '\\uAC00']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\uAC00\", ['\\r', '\\u0308', '\\uAC00']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\uAC01\", ['\\r', '\\uAC01']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\uAC01\", ['\\r', '\\u0308', '\\uAC01']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0915\", ['\\r', '\\u0915']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u0915\", ['\\r', '\\u0308', '\\u0915']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u00A9\", ['\\r', '\\u00A9']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u00A9\", ['\\r', '\\u0308', '\\u00A9']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\r \", ['\\r', ' ']                                                                                                                                                                                              #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308 \", ['\\r', '\\u0308', ' ']                                                                                                                                                                              #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0378\", ['\\r', '\\u0378']                                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\u0308\\u0378\", ['\\r', '\\u0308', '\\u0378']                                                                                                                                                                    #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\r\", ['\\n', '\\r']                                                                                                                                                                                            #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\r\", ['\\n', '\\u0308', '\\r']                                                                                                                                                                            #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\n\", ['\\n', '\\n']                                                                                                                                                                                            #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\n\", ['\\n', '\\u0308', '\\n']                                                                                                                                                                            #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0000\", ['\\n', '\\0']                                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u0000\", ['\\n', '\\u0308', '\\0']                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u094D\", ['\\n', '\\u094D']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u094D\", ['\\n', \"\\u0308\\u094D\"]                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0300\", ['\\n', '\\u0300']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u0300\", ['\\n', \"\\u0308\\u0300\"]                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u200C\", ['\\n', '\\u200C']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u200C\", ['\\n', \"\\u0308\\u200C\"]                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u200D\", ['\\n', '\\u200D']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u200D\", ['\\n', \"\\u0308\\u200D\"]                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u{1F1E6}\", ['\\n', '\\u{1F1E6}']                                                                                                                                                                              #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u{1F1E6}\", ['\\n', '\\u0308', '\\u{1F1E6}']                                                                                                                                                              #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u06DD\", ['\\n', '\\u06DD']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u06DD\", ['\\n', '\\u0308', '\\u06DD']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0903\", ['\\n', '\\u0903']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u0903\", ['\\n', \"\\u0308\\u0903\"]                                                                                                                                                                        #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u1100\", ['\\n', '\\u1100']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u1100\", ['\\n', '\\u0308', '\\u1100']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u1160\", ['\\n', '\\u1160']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u1160\", ['\\n', '\\u0308', '\\u1160']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u11A8\", ['\\n', '\\u11A8']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u11A8\", ['\\n', '\\u0308', '\\u11A8']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\uAC00\", ['\\n', '\\uAC00']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\uAC00\", ['\\n', '\\u0308', '\\uAC00']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\uAC01\", ['\\n', '\\uAC01']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\uAC01\", ['\\n', '\\u0308', '\\uAC01']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0915\", ['\\n', '\\u0915']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u0915\", ['\\n', '\\u0308', '\\u0915']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u00A9\", ['\\n', '\\u00A9']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u00A9\", ['\\n', '\\u0308', '\\u00A9']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\n \", ['\\n', ' ']                                                                                                                                                                                              #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308 \", ['\\n', '\\u0308', ' ']                                                                                                                                                                              #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0378\", ['\\n', '\\u0378']                                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\n\\u0308\\u0378\", ['\\n', '\\u0308', '\\u0378']                                                                                                                                                                    #   ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\r\", ['\\0', '\\r']                                                                                                                                                                                        #   ÷ [0.2] <NULL> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\r\", ['\\0', '\\u0308', '\\r']                                                                                                                                                                        #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\n\", ['\\0', '\\n']                                                                                                                                                                                        #   ÷ [0.2] <NULL> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\n\", ['\\0', '\\u0308', '\\n']                                                                                                                                                                        #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0000\", ['\\0', '\\0']                                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u0000\", ['\\0', '\\u0308', '\\0']                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u094D\", ['\\0', '\\u094D']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u094D\", ['\\0', \"\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0300\", ['\\0', '\\u0300']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u0300\", ['\\0', \"\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u200C\", ['\\0', '\\u200C']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u200C\", ['\\0', \"\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u200D\", ['\\0', '\\u200D']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u200D\", ['\\0', \"\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u{1F1E6}\", ['\\0', '\\u{1F1E6}']                                                                                                                                                                          #   ÷ [0.2] <NULL> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u{1F1E6}\", ['\\0', '\\u0308', '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u06DD\", ['\\0', '\\u06DD']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u06DD\", ['\\0', '\\u0308', '\\u06DD']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0903\", ['\\0', '\\u0903']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u0903\", ['\\0', \"\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u1100\", ['\\0', '\\u1100']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u1100\", ['\\0', '\\u0308', '\\u1100']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u1160\", ['\\0', '\\u1160']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u1160\", ['\\0', '\\u0308', '\\u1160']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u11A8\", ['\\0', '\\u11A8']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u11A8\", ['\\0', '\\u0308', '\\u11A8']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\uAC00\", ['\\0', '\\uAC00']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\uAC00\", ['\\0', '\\u0308', '\\uAC00']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\uAC01\", ['\\0', '\\uAC01']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\uAC01\", ['\\0', '\\u0308', '\\uAC01']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0915\", ['\\0', '\\u0915']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u0915\", ['\\0', '\\u0308', '\\u0915']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u00A9\", ['\\0', '\\u00A9']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u00A9\", ['\\0', '\\u0308', '\\u00A9']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000 \", ['\\0', ' ']                                                                                                                                                                                          #   ÷ [0.2] <NULL> (Control) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308 \", ['\\0', '\\u0308', ' ']                                                                                                                                                                          #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0378\", ['\\0', '\\u0378']                                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0000\\u0308\\u0378\", ['\\0', '\\u0308', '\\u0378']                                                                                                                                                                #   ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\r\", ['\\u094D', '\\r']                                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\r\", [\"\\u094D\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\n\", ['\\u094D', '\\n']                                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\n\", [\"\\u094D\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0000\", ['\\u094D', '\\0']                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u0000\", [\"\\u094D\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u094D\", [\"\\u094D\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u094D\", [\"\\u094D\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0300\", [\"\\u094D\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u0300\", [\"\\u094D\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u200C\", [\"\\u094D\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u200C\", [\"\\u094D\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u200D\", [\"\\u094D\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u200D\", [\"\\u094D\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u{1F1E6}\", ['\\u094D', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u{1F1E6}\", [\"\\u094D\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u06DD\", ['\\u094D', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u06DD\", [\"\\u094D\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0903\", [\"\\u094D\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u0903\", [\"\\u094D\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u1100\", ['\\u094D', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u1100\", [\"\\u094D\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u1160\", ['\\u094D', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u1160\", [\"\\u094D\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u11A8\", ['\\u094D', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u11A8\", [\"\\u094D\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\uAC00\", ['\\u094D', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\uAC00\", [\"\\u094D\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\uAC01\", ['\\u094D', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\uAC01\", [\"\\u094D\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0915\", ['\\u094D', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u0915\", [\"\\u094D\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u00A9\", ['\\u094D', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u00A9\", [\"\\u094D\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D \", ['\\u094D', ' ']                                                                                                                                                                                      #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308 \", [\"\\u094D\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0378\", ['\\u094D', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u094D\\u0308\\u0378\", [\"\\u094D\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\r\", ['\\u0300', '\\r']                                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\r\", [\"\\u0300\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\n\", ['\\u0300', '\\n']                                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\n\", [\"\\u0300\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0000\", ['\\u0300', '\\0']                                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u0000\", [\"\\u0300\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u094D\", [\"\\u0300\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u094D\", [\"\\u0300\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0300\", [\"\\u0300\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u0300\", [\"\\u0300\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u200C\", [\"\\u0300\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u200C\", [\"\\u0300\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u200D\", [\"\\u0300\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u200D\", [\"\\u0300\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u{1F1E6}\", ['\\u0300', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u{1F1E6}\", [\"\\u0300\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u06DD\", ['\\u0300', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u06DD\", [\"\\u0300\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0903\", [\"\\u0300\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u0903\", [\"\\u0300\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u1100\", ['\\u0300', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u1100\", [\"\\u0300\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u1160\", ['\\u0300', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u1160\", [\"\\u0300\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u11A8\", ['\\u0300', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u11A8\", [\"\\u0300\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\uAC00\", ['\\u0300', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\uAC00\", [\"\\u0300\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\uAC01\", ['\\u0300', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\uAC01\", [\"\\u0300\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0915\", ['\\u0300', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u0915\", [\"\\u0300\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u00A9\", ['\\u0300', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u00A9\", [\"\\u0300\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300 \", ['\\u0300', ' ']                                                                                                                                                                                      #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308 \", [\"\\u0300\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0378\", ['\\u0300', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0300\\u0308\\u0378\", [\"\\u0300\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\r\", ['\\u200C', '\\r']                                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\r\", [\"\\u200C\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\n\", ['\\u200C', '\\n']                                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\n\", [\"\\u200C\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0000\", ['\\u200C', '\\0']                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u0000\", [\"\\u200C\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u094D\", [\"\\u200C\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u094D\", [\"\\u200C\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0300\", [\"\\u200C\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u0300\", [\"\\u200C\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u200C\", [\"\\u200C\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u200C\", [\"\\u200C\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u200D\", [\"\\u200C\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u200D\", [\"\\u200C\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u{1F1E6}\", ['\\u200C', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u{1F1E6}\", [\"\\u200C\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u06DD\", ['\\u200C', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u06DD\", [\"\\u200C\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0903\", [\"\\u200C\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u0903\", [\"\\u200C\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u1100\", ['\\u200C', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u1100\", [\"\\u200C\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u1160\", ['\\u200C', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u1160\", [\"\\u200C\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u11A8\", ['\\u200C', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u11A8\", [\"\\u200C\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\uAC00\", ['\\u200C', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\uAC00\", [\"\\u200C\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\uAC01\", ['\\u200C', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\uAC01\", [\"\\u200C\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0915\", ['\\u200C', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u0915\", [\"\\u200C\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u00A9\", ['\\u200C', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u00A9\", [\"\\u200C\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C \", ['\\u200C', ' ']                                                                                                                                                                                      #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308 \", [\"\\u200C\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0378\", ['\\u200C', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200C\\u0308\\u0378\", [\"\\u200C\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\r\", ['\\u200D', '\\r']                                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\r\", [\"\\u200D\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\n\", ['\\u200D', '\\n']                                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\n\", [\"\\u200D\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0000\", ['\\u200D', '\\0']                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u0000\", [\"\\u200D\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u094D\", [\"\\u200D\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u094D\", [\"\\u200D\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0300\", [\"\\u200D\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u0300\", [\"\\u200D\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u200C\", [\"\\u200D\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u200C\", [\"\\u200D\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u200D\", [\"\\u200D\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u200D\", [\"\\u200D\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u{1F1E6}\", ['\\u200D', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u{1F1E6}\", [\"\\u200D\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u06DD\", ['\\u200D', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u06DD\", [\"\\u200D\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0903\", [\"\\u200D\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u0903\", [\"\\u200D\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u1100\", ['\\u200D', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u1100\", [\"\\u200D\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u1160\", ['\\u200D', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u1160\", [\"\\u200D\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u11A8\", ['\\u200D', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u11A8\", [\"\\u200D\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\uAC00\", ['\\u200D', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\uAC00\", [\"\\u200D\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\uAC01\", ['\\u200D', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\uAC01\", [\"\\u200D\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0915\", ['\\u200D', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u0915\", [\"\\u200D\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u00A9\", ['\\u200D', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u00A9\", [\"\\u200D\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D \", ['\\u200D', ' ']                                                                                                                                                                                      #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308 \", [\"\\u200D\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0378\", ['\\u200D', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u200D\\u0308\\u0378\", [\"\\u200D\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\r\", ['\\u{1F1E6}', '\\r']                                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\r\", [\"\\u{1F1E6}\\u0308\", '\\r']                                                                                                                                                                  #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\n\", ['\\u{1F1E6}', '\\n']                                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\n\", [\"\\u{1F1E6}\\u0308\", '\\n']                                                                                                                                                                  #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0000\", ['\\u{1F1E6}', '\\0']                                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u0000\", [\"\\u{1F1E6}\\u0308\", '\\0']                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u094D\", [\"\\u{1F1E6}\\u094D\"]                                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u094D\", [\"\\u{1F1E6}\\u0308\\u094D\"]                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0300\", [\"\\u{1F1E6}\\u0300\"]                                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u0300\", [\"\\u{1F1E6}\\u0308\\u0300\"]                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u200C\", [\"\\u{1F1E6}\\u200C\"]                                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u200C\", [\"\\u{1F1E6}\\u0308\\u200C\"]                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u200D\", [\"\\u{1F1E6}\\u200D\"]                                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u200D\", [\"\\u{1F1E6}\\u0308\\u200D\"]                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u{1F1E6}\", [\"\\u{1F1E6}\\u{1F1E6}\"]                                                                                                                                                                    #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u{1F1E6}\", [\"\\u{1F1E6}\\u0308\", '\\u{1F1E6}']                                                                                                                                                    #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u06DD\", ['\\u{1F1E6}', '\\u06DD']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u06DD\", [\"\\u{1F1E6}\\u0308\", '\\u06DD']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0903\", [\"\\u{1F1E6}\\u0903\"]                                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u0903\", [\"\\u{1F1E6}\\u0308\\u0903\"]                                                                                                                                                              #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u1100\", ['\\u{1F1E6}', '\\u1100']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u1100\", [\"\\u{1F1E6}\\u0308\", '\\u1100']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u1160\", ['\\u{1F1E6}', '\\u1160']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u1160\", [\"\\u{1F1E6}\\u0308\", '\\u1160']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u11A8\", ['\\u{1F1E6}', '\\u11A8']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u11A8\", [\"\\u{1F1E6}\\u0308\", '\\u11A8']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\uAC00\", ['\\u{1F1E6}', '\\uAC00']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\uAC00\", [\"\\u{1F1E6}\\u0308\", '\\uAC00']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\uAC01\", ['\\u{1F1E6}', '\\uAC01']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\uAC01\", [\"\\u{1F1E6}\\u0308\", '\\uAC01']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0915\", ['\\u{1F1E6}', '\\u0915']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u0915\", [\"\\u{1F1E6}\\u0308\", '\\u0915']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u00A9\", ['\\u{1F1E6}', '\\u00A9']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u00A9\", [\"\\u{1F1E6}\\u0308\", '\\u00A9']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6} \", ['\\u{1F1E6}', ' ']                                                                                                                                                                                #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308 \", [\"\\u{1F1E6}\\u0308\", ' ']                                                                                                                                                                    #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0378\", ['\\u{1F1E6}', '\\u0378']                                                                                                                                                                      #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u0308\\u0378\", [\"\\u{1F1E6}\\u0308\", '\\u0378']                                                                                                                                                          #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\r\", ['\\u06DD', '\\r']                                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\r\", [\"\\u06DD\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\n\", ['\\u06DD', '\\n']                                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\n\", [\"\\u06DD\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0000\", ['\\u06DD', '\\0']                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u0000\", [\"\\u06DD\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u094D\", [\"\\u06DD\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u094D\", [\"\\u06DD\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0300\", [\"\\u06DD\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u0300\", [\"\\u06DD\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u200C\", [\"\\u06DD\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u200C\", [\"\\u06DD\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u200D\", [\"\\u06DD\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u200D\", [\"\\u06DD\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u{1F1E6}\", [\"\\u06DD\\u{1F1E6}\"]                                                                                                                                                                          #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u{1F1E6}\", [\"\\u06DD\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u06DD\", [\"\\u06DD\\u06DD\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u06DD\", [\"\\u06DD\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0903\", [\"\\u06DD\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u0903\", [\"\\u06DD\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u1100\", [\"\\u06DD\\u1100\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u1100\", [\"\\u06DD\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u1160\", [\"\\u06DD\\u1160\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u1160\", [\"\\u06DD\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u11A8\", [\"\\u06DD\\u11A8\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u11A8\", [\"\\u06DD\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\uAC00\", [\"\\u06DD\\uAC00\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\uAC00\", [\"\\u06DD\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\uAC01\", [\"\\u06DD\\uAC01\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\uAC01\", [\"\\u06DD\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0915\", [\"\\u06DD\\u0915\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u0915\", [\"\\u06DD\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u00A9\", [\"\\u06DD\\u00A9\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u00A9\", [\"\\u06DD\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD \", [\"\\u06DD \"]                                                                                                                                                                                          #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308 \", [\"\\u06DD\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0378\", [\"\\u06DD\\u0378\"]                                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u06DD\\u0308\\u0378\", [\"\\u06DD\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\r\", ['\\u0903', '\\r']                                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\r\", [\"\\u0903\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\n\", ['\\u0903', '\\n']                                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\n\", [\"\\u0903\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0000\", ['\\u0903', '\\0']                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u0000\", [\"\\u0903\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u094D\", [\"\\u0903\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u094D\", [\"\\u0903\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0300\", [\"\\u0903\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u0300\", [\"\\u0903\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u200C\", [\"\\u0903\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u200C\", [\"\\u0903\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u200D\", [\"\\u0903\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u200D\", [\"\\u0903\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u{1F1E6}\", ['\\u0903', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u{1F1E6}\", [\"\\u0903\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u06DD\", ['\\u0903', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u06DD\", [\"\\u0903\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0903\", [\"\\u0903\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u0903\", [\"\\u0903\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u1100\", ['\\u0903', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u1100\", [\"\\u0903\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u1160\", ['\\u0903', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u1160\", [\"\\u0903\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u11A8\", ['\\u0903', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u11A8\", [\"\\u0903\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\uAC00\", ['\\u0903', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\uAC00\", [\"\\u0903\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\uAC01\", ['\\u0903', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\uAC01\", [\"\\u0903\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0915\", ['\\u0903', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u0915\", [\"\\u0903\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u00A9\", ['\\u0903', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u00A9\", [\"\\u0903\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903 \", ['\\u0903', ' ']                                                                                                                                                                                      #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308 \", [\"\\u0903\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0378\", ['\\u0903', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0903\\u0308\\u0378\", [\"\\u0903\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\r\", ['\\u1100', '\\r']                                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\r\", [\"\\u1100\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\n\", ['\\u1100', '\\n']                                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\n\", [\"\\u1100\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0000\", ['\\u1100', '\\0']                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u0000\", [\"\\u1100\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u094D\", [\"\\u1100\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u094D\", [\"\\u1100\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0300\", [\"\\u1100\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u0300\", [\"\\u1100\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u200C\", [\"\\u1100\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u200C\", [\"\\u1100\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u200D\", [\"\\u1100\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u200D\", [\"\\u1100\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u{1F1E6}\", ['\\u1100', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u{1F1E6}\", [\"\\u1100\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u06DD\", ['\\u1100', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u06DD\", [\"\\u1100\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0903\", [\"\\u1100\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u0903\", [\"\\u1100\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u1100\", [\"\\u1100\\u1100\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u1100\", [\"\\u1100\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u1160\", [\"\\u1100\\u1160\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u1160\", [\"\\u1100\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u11A8\", ['\\u1100', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u11A8\", [\"\\u1100\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\uAC00\", [\"\\u1100\\uAC00\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\uAC00\", [\"\\u1100\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\uAC01\", [\"\\u1100\\uAC01\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\uAC01\", [\"\\u1100\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0915\", ['\\u1100', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u0915\", [\"\\u1100\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u00A9\", ['\\u1100', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u00A9\", [\"\\u1100\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100 \", ['\\u1100', ' ']                                                                                                                                                                                      #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308 \", [\"\\u1100\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0378\", ['\\u1100', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u0308\\u0378\", [\"\\u1100\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\r\", ['\\u1160', '\\r']                                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\r\", [\"\\u1160\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\n\", ['\\u1160', '\\n']                                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\n\", [\"\\u1160\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0000\", ['\\u1160', '\\0']                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u0000\", [\"\\u1160\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u094D\", [\"\\u1160\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u094D\", [\"\\u1160\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0300\", [\"\\u1160\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u0300\", [\"\\u1160\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u200C\", [\"\\u1160\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u200C\", [\"\\u1160\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u200D\", [\"\\u1160\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u200D\", [\"\\u1160\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u{1F1E6}\", ['\\u1160', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u{1F1E6}\", [\"\\u1160\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u06DD\", ['\\u1160', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u06DD\", [\"\\u1160\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0903\", [\"\\u1160\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u0903\", [\"\\u1160\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u1100\", ['\\u1160', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u1100\", [\"\\u1160\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u1160\", [\"\\u1160\\u1160\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u1160\", [\"\\u1160\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u11A8\", [\"\\u1160\\u11A8\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u11A8\", [\"\\u1160\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\uAC00\", ['\\u1160', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\uAC00\", [\"\\u1160\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\uAC01\", ['\\u1160', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\uAC01\", [\"\\u1160\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0915\", ['\\u1160', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u0915\", [\"\\u1160\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u00A9\", ['\\u1160', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u00A9\", [\"\\u1160\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160 \", ['\\u1160', ' ']                                                                                                                                                                                      #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308 \", [\"\\u1160\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0378\", ['\\u1160', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1160\\u0308\\u0378\", [\"\\u1160\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\r\", ['\\u11A8', '\\r']                                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\r\", [\"\\u11A8\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\n\", ['\\u11A8', '\\n']                                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\n\", [\"\\u11A8\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0000\", ['\\u11A8', '\\0']                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u0000\", [\"\\u11A8\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u094D\", [\"\\u11A8\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u094D\", [\"\\u11A8\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0300\", [\"\\u11A8\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u0300\", [\"\\u11A8\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u200C\", [\"\\u11A8\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u200C\", [\"\\u11A8\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u200D\", [\"\\u11A8\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u200D\", [\"\\u11A8\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u{1F1E6}\", ['\\u11A8', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u{1F1E6}\", [\"\\u11A8\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u06DD\", ['\\u11A8', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u06DD\", [\"\\u11A8\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0903\", [\"\\u11A8\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u0903\", [\"\\u11A8\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u1100\", ['\\u11A8', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u1100\", [\"\\u11A8\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u1160\", ['\\u11A8', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u1160\", [\"\\u11A8\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u11A8\", [\"\\u11A8\\u11A8\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u11A8\", [\"\\u11A8\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\uAC00\", ['\\u11A8', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\uAC00\", [\"\\u11A8\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\uAC01\", ['\\u11A8', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\uAC01\", [\"\\u11A8\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0915\", ['\\u11A8', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u0915\", [\"\\u11A8\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u00A9\", ['\\u11A8', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u00A9\", [\"\\u11A8\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8 \", ['\\u11A8', ' ']                                                                                                                                                                                      #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308 \", [\"\\u11A8\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0378\", ['\\u11A8', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u11A8\\u0308\\u0378\", [\"\\u11A8\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\r\", ['\\uAC00', '\\r']                                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\r\", [\"\\uAC00\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\n\", ['\\uAC00', '\\n']                                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\n\", [\"\\uAC00\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0000\", ['\\uAC00', '\\0']                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u0000\", [\"\\uAC00\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u094D\", [\"\\uAC00\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u094D\", [\"\\uAC00\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0300\", [\"\\uAC00\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u0300\", [\"\\uAC00\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u200C\", [\"\\uAC00\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u200C\", [\"\\uAC00\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u200D\", [\"\\uAC00\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u200D\", [\"\\uAC00\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u{1F1E6}\", ['\\uAC00', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u{1F1E6}\", [\"\\uAC00\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u06DD\", ['\\uAC00', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u06DD\", [\"\\uAC00\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0903\", [\"\\uAC00\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u0903\", [\"\\uAC00\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u1100\", ['\\uAC00', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u1100\", [\"\\uAC00\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u1160\", [\"\\uAC00\\u1160\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u1160\", [\"\\uAC00\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u11A8\", [\"\\uAC00\\u11A8\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u11A8\", [\"\\uAC00\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\uAC00\", ['\\uAC00', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\uAC00\", [\"\\uAC00\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\uAC01\", ['\\uAC00', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\uAC01\", [\"\\uAC00\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0915\", ['\\uAC00', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u0915\", [\"\\uAC00\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u00A9\", ['\\uAC00', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u00A9\", [\"\\uAC00\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00 \", ['\\uAC00', ' ']                                                                                                                                                                                      #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308 \", [\"\\uAC00\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0378\", ['\\uAC00', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u0308\\u0378\", [\"\\uAC00\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\r\", ['\\uAC01', '\\r']                                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\r\", [\"\\uAC01\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\n\", ['\\uAC01', '\\n']                                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\n\", [\"\\uAC01\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0000\", ['\\uAC01', '\\0']                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u0000\", [\"\\uAC01\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u094D\", [\"\\uAC01\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u094D\", [\"\\uAC01\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0300\", [\"\\uAC01\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u0300\", [\"\\uAC01\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u200C\", [\"\\uAC01\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u200C\", [\"\\uAC01\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u200D\", [\"\\uAC01\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u200D\", [\"\\uAC01\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u{1F1E6}\", ['\\uAC01', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u{1F1E6}\", [\"\\uAC01\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u06DD\", ['\\uAC01', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u06DD\", [\"\\uAC01\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0903\", [\"\\uAC01\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u0903\", [\"\\uAC01\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u1100\", ['\\uAC01', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u1100\", [\"\\uAC01\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u1160\", ['\\uAC01', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u1160\", [\"\\uAC01\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u11A8\", [\"\\uAC01\\u11A8\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u11A8\", [\"\\uAC01\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\uAC00\", ['\\uAC01', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\uAC00\", [\"\\uAC01\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\uAC01\", ['\\uAC01', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\uAC01\", [\"\\uAC01\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0915\", ['\\uAC01', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u0915\", [\"\\uAC01\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u00A9\", ['\\uAC01', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u00A9\", [\"\\uAC01\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01 \", ['\\uAC01', ' ']                                                                                                                                                                                      #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308 \", [\"\\uAC01\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0378\", ['\\uAC01', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u0308\\u0378\", [\"\\uAC01\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\r\", ['\\u0915', '\\r']                                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\r\", [\"\\u0915\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\n\", ['\\u0915', '\\n']                                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\n\", [\"\\u0915\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0000\", ['\\u0915', '\\0']                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u0000\", [\"\\u0915\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u094D\", [\"\\u0915\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u094D\", [\"\\u0915\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0300\", [\"\\u0915\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u0300\", [\"\\u0915\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u200C\", [\"\\u0915\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u200C\", [\"\\u0915\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u200D\", [\"\\u0915\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u200D\", [\"\\u0915\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u{1F1E6}\", ['\\u0915', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u{1F1E6}\", [\"\\u0915\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u06DD\", ['\\u0915', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u06DD\", [\"\\u0915\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0903\", [\"\\u0915\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u0903\", [\"\\u0915\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u1100\", ['\\u0915', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u1100\", [\"\\u0915\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u1160\", ['\\u0915', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u1160\", [\"\\u0915\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u11A8\", ['\\u0915', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u11A8\", [\"\\u0915\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\uAC00\", ['\\u0915', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\uAC00\", [\"\\u0915\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\uAC01\", ['\\u0915', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\uAC01\", [\"\\u0915\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0915\", ['\\u0915', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u0915\", [\"\\u0915\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u00A9\", ['\\u0915', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u00A9\", [\"\\u0915\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915 \", ['\\u0915', ' ']                                                                                                                                                                                      #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308 \", [\"\\u0915\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0378\", ['\\u0915', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0308\\u0378\", [\"\\u0915\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\r\", ['\\u00A9', '\\r']                                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\r\", [\"\\u00A9\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\n\", ['\\u00A9', '\\n']                                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\n\", [\"\\u00A9\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0000\", ['\\u00A9', '\\0']                                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u0000\", [\"\\u00A9\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u094D\", [\"\\u00A9\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u094D\", [\"\\u00A9\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0300\", [\"\\u00A9\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u0300\", [\"\\u00A9\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u200C\", [\"\\u00A9\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u200C\", [\"\\u00A9\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u200D\", [\"\\u00A9\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u200D\", [\"\\u00A9\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u{1F1E6}\", ['\\u00A9', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u{1F1E6}\", [\"\\u00A9\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u06DD\", ['\\u00A9', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u06DD\", [\"\\u00A9\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0903\", [\"\\u00A9\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u0903\", [\"\\u00A9\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u1100\", ['\\u00A9', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u1100\", [\"\\u00A9\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u1160\", ['\\u00A9', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u1160\", [\"\\u00A9\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u11A8\", ['\\u00A9', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u11A8\", [\"\\u00A9\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\uAC00\", ['\\u00A9', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\uAC00\", [\"\\u00A9\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\uAC01\", ['\\u00A9', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\uAC01\", [\"\\u00A9\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0915\", ['\\u00A9', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u0915\", [\"\\u00A9\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u00A9\", ['\\u00A9', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u00A9\", [\"\\u00A9\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9 \", ['\\u00A9', ' ']                                                                                                                                                                                      #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308 \", [\"\\u00A9\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0378\", ['\\u00A9', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u00A9\\u0308\\u0378\", [\"\\u00A9\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \" \\r\", [' ', '\\r']                                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\r\", [\" \\u0308\", '\\r']                                                                                                                                                                                  #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \" \\n\", [' ', '\\n']                                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\n\", [\" \\u0308\", '\\n']                                                                                                                                                                                  #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \" \\u0000\", [' ', '\\0']                                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u0000\", [\" \\u0308\", '\\0']                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \" \\u094D\", [\" \\u094D\"]                                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u094D\", [\" \\u0308\\u094D\"]                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \" \\u0300\", [\" \\u0300\"]                                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u0300\", [\" \\u0308\\u0300\"]                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \" \\u200C\", [\" \\u200C\"]                                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u200C\", [\" \\u0308\\u200C\"]                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \" \\u200D\", [\" \\u200D\"]                                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u200D\", [\" \\u0308\\u200D\"]                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \" \\u{1F1E6}\", [' ', '\\u{1F1E6}']                                                                                                                                                                                #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u{1F1E6}\", [\" \\u0308\", '\\u{1F1E6}']                                                                                                                                                                    #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \" \\u06DD\", [' ', '\\u06DD']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u06DD\", [\" \\u0308\", '\\u06DD']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \" \\u0903\", [\" \\u0903\"]                                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u0903\", [\" \\u0308\\u0903\"]                                                                                                                                                                              #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \" \\u1100\", [' ', '\\u1100']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u1100\", [\" \\u0308\", '\\u1100']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \" \\u1160\", [' ', '\\u1160']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u1160\", [\" \\u0308\", '\\u1160']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \" \\u11A8\", [' ', '\\u11A8']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u11A8\", [\" \\u0308\", '\\u11A8']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \" \\uAC00\", [' ', '\\uAC00']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\uAC00\", [\" \\u0308\", '\\uAC00']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \" \\uAC01\", [' ', '\\uAC01']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\uAC01\", [\" \\u0308\", '\\uAC01']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \" \\u0915\", [' ', '\\u0915']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u0915\", [\" \\u0308\", '\\u0915']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \" \\u00A9\", [' ', '\\u00A9']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u00A9\", [\" \\u0308\", '\\u00A9']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"  \", [' ', ' ']                                                                                                                                                                                                #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308 \", [\" \\u0308\", ' ']                                                                                                                                                                                    #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \" \\u0378\", [' ', '\\u0378']                                                                                                                                                                                      #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \" \\u0308\\u0378\", [\" \\u0308\", '\\u0378']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\r\", ['\\u0378', '\\r']                                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\r\", [\"\\u0378\\u0308\", '\\r']                                                                                                                                                                        #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\n\", ['\\u0378', '\\n']                                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\n\", [\"\\u0378\\u0308\", '\\n']                                                                                                                                                                        #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0000\", ['\\u0378', '\\0']                                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u0000\", [\"\\u0378\\u0308\", '\\0']                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u094D\", [\"\\u0378\\u094D\"]                                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u094D\", [\"\\u0378\\u0308\\u094D\"]                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0300\", [\"\\u0378\\u0300\"]                                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u0300\", [\"\\u0378\\u0308\\u0300\"]                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u200C\", [\"\\u0378\\u200C\"]                                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u200C\", [\"\\u0378\\u0308\\u200C\"]                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u200D\", [\"\\u0378\\u200D\"]                                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u200D\", [\"\\u0378\\u0308\\u200D\"]                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u{1F1E6}\", ['\\u0378', '\\u{1F1E6}']                                                                                                                                                                      #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u{1F1E6}\", [\"\\u0378\\u0308\", '\\u{1F1E6}']                                                                                                                                                          #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u06DD\", ['\\u0378', '\\u06DD']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u06DD\", [\"\\u0378\\u0308\", '\\u06DD']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0903\", [\"\\u0378\\u0903\"]                                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u0903\", [\"\\u0378\\u0308\\u0903\"]                                                                                                                                                                    #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u1100\", ['\\u0378', '\\u1100']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u1100\", [\"\\u0378\\u0308\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u1160\", ['\\u0378', '\\u1160']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u1160\", [\"\\u0378\\u0308\", '\\u1160']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u11A8\", ['\\u0378', '\\u11A8']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u11A8\", [\"\\u0378\\u0308\", '\\u11A8']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\uAC00\", ['\\u0378', '\\uAC00']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\uAC00\", [\"\\u0378\\u0308\", '\\uAC00']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\uAC01\", ['\\u0378', '\\uAC01']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\uAC01\", [\"\\u0378\\u0308\", '\\uAC01']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0915\", ['\\u0378', '\\u0915']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u0915\", [\"\\u0378\\u0308\", '\\u0915']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u00A9\", ['\\u0378', '\\u00A9']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u00A9\", [\"\\u0378\\u0308\", '\\u00A9']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378 \", ['\\u0378', ' ']                                                                                                                                                                                      #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308 \", [\"\\u0378\\u0308\", ' ']                                                                                                                                                                          #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0378\", ['\\u0378', '\\u0378']                                                                                                                                                                            #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0378\\u0308\\u0378\", [\"\\u0378\\u0308\", '\\u0378']                                                                                                                                                                #   ÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\r\\na\\n\\u0308\", [\"\\r\\n\", 'a', '\\n', '\\u0308']                                                                                                                                                                  #   ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"a\\u0308\", [\"a\\u0308\"]                                                                                                                                                                                          #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \" \\u200D\\u0646\", [\" \\u200D\", '\\u0646']                                                                                                                                                                          #   ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC LETTER NOON (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0646\\u200D \", [\"\\u0646\\u200D\", ' ']                                                                                                                                                                          #   ÷ [0.2] ARABIC LETTER NOON (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u1100\\u1100\", [\"\\u1100\\u1100\"]                                                                                                                                                                                #   ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC00\\u11A8\\u1100\", [\"\\uAC00\\u11A8\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\uAC01\\u11A8\\u1100\", [\"\\uAC01\\u11A8\", '\\u1100']                                                                                                                                                                #   ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F1E6}\\u{1F1E7}\\u{1F1E8}b\", [\"\\u{1F1E6}\\u{1F1E7}\", '\\u{1F1E8}', 'b']                                                                                                                                        #   ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u{1F1E6}\\u{1F1E7}\\u{1F1E8}b\", ['a', \"\\u{1F1E6}\\u{1F1E7}\", '\\u{1F1E8}', 'b']                                                                                                                                  #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u{1F1E6}\\u{1F1E7}\\u200D\\u{1F1E8}b\", ['a', \"\\u{1F1E6}\\u{1F1E7}\\u200D\", '\\u{1F1E8}', 'b']                                                                                                                      #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u{1F1E6}\\u200D\\u{1F1E7}\\u{1F1E8}b\", ['a', \"\\u{1F1E6}\\u200D\", \"\\u{1F1E7}\\u{1F1E8}\", 'b']                                                                                                                      #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u{1F1E6}\\u{1F1E7}\\u{1F1E8}\\u{1F1E9}b\", ['a', \"\\u{1F1E6}\\u{1F1E7}\", \"\\u{1F1E8}\\u{1F1E9}\", 'b']                                                                                                                #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u200D\", [\"a\\u200D\"]                                                                                                                                                                                          #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\n  it_iterates_graphemes \"a\\u0308b\", [\"a\\u0308\", 'b']                                                                                                                                                                                    #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u0903b\", [\"a\\u0903\", 'b']                                                                                                                                                                                    #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u0600b\", ['a', \"\\u0600b\"]                                                                                                                                                                                    #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F476}\\u{1F3FF}\\u{1F476}\", [\"\\u{1F476}\\u{1F3FF}\", '\\u{1F476}']                                                                                                                                              #   ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u{1F3FF}\\u{1F476}\", [\"a\\u{1F3FF}\", '\\u{1F476}']                                                                                                                                                              #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u{1F3FF}\\u{1F476}\\u200D\\u{1F6D1}\", [\"a\\u{1F3FF}\", \"\\u{1F476}\\u200D\\u{1F6D1}\"]                                                                                                                                #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F476}\\u{1F3FF}\\u0308\\u200D\\u{1F476}\\u{1F3FF}\", [\"\\u{1F476}\\u{1F3FF}\\u0308\\u200D\\u{1F476}\\u{1F3FF}\"]                                                                                                        #   ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  it_iterates_graphemes \"\\u{1F6D1}\\u200D\\u{1F6D1}\", [\"\\u{1F6D1}\\u200D\\u{1F6D1}\"]                                                                                                                                                        #   ÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u200D\\u{1F6D1}\", [\"a\\u200D\", '\\u{1F6D1}']                                                                                                                                                                    #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u2701\\u200D\\u2701\", [\"\\u2701\\u200D\", '\\u2701']                                                                                                                                                                #   ÷ [0.2] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u200D\\u2701\", [\"a\\u200D\", '\\u2701']                                                                                                                                                                          #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u0924\", ['\\u0915', '\\u0924']                                                                                                                                                                            #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u094D\\u0924\", [\"\\u0915\\u094D\\u0924\"] }                                                                                                                                                 #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u094D\\u094D\\u0924\", [\"\\u0915\\u094D\\u094D\\u0924\"] }                                                                                                                                     #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u094D\\u200D\\u0924\", [\"\\u0915\\u094D\\u200D\\u0924\"] }                                                                                                                                     #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u093C\\u200D\\u094D\\u0924\", [\"\\u0915\\u093C\\u200D\\u094D\\u0924\"] }                                                                                                                         #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u093C\\u094D\\u200D\\u0924\", [\"\\u0915\\u093C\\u094D\\u200D\\u0924\"] }                                                                                                                         #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u094D\\u0924\\u094D\\u092F\", [\"\\u0915\\u094D\\u0924\\u094D\\u092F\"] }                                                                                                                         #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER YA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"\\u0915\\u094Da\", [\"\\u0915\\u094D\", 'a']                                                                                                                                                                          #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [0.3]\n  it_iterates_graphemes \"a\\u094D\\u0924\", [\"a\\u094D\", '\\u0924']                                                                                                                                                                          #   ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  it_iterates_graphemes \"?\\u094D\\u0924\", [\"?\\u094D\", '\\u0924']                                                                                                                                                                          #   ÷ [0.2] QUESTION MARK (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0915\\u094D\\u094D\\u0924\", [\"\\u0915\\u094D\\u094D\\u0924\"] }                                                                                                                                     #   ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u0AB8\\u0AFB\\u0ACD\\u0AB8\\u0AFB\", [\"\\u0AB8\\u0AFB\\u0ACD\\u0AB8\\u0AFB\"] }                                                                                                                         #   ÷ [0.2] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtendermConjunctLinker) × [9.0] GUJARATI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u1019\\u1039\\u1018\\u102C\\u1037\", [\"\\u1019\\u1039\\u1018\", \"\\u102C\\u1037\"] }                                                                                                                     #   ÷ [0.2] MYANMAR LETTER MA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER BHA (LinkingConsonant) ÷ [999.0] MYANMAR VOWEL SIGN AA (XXmLinkingConsonantmExtPict) × [9.0] MYANMAR SIGN DOT BELOW (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u1004\\u103A\\u1039\\u1011\\u1039\\u1011\", [\"\\u1004\\u103A\\u1039\\u1011\\u1039\\u1011\"] }                                                                                                             #   ÷ [0.2] MYANMAR LETTER NGA (LinkingConsonant) × [9.0] MYANMAR SIGN ASAT (Extend_ConjunctExtendermConjunctLinker) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u1B12\\u1B01\\u1B32\\u1B44\\u1B2F\\u1B32\\u1B44\\u1B22\\u1B44\\u1B2C\\u1B32\\u1B44\\u1B22\\u1B38\", [\"\\u1B12\\u1B01\", \"\\u1B32\\u1B44\\u1B2F\", \"\\u1B32\\u1B44\\u1B22\\u1B44\\u1B2C\", \"\\u1B32\\u1B44\\u1B22\\u1B38\"] } #   ÷ [0.2] BALINESE LETTER OKARA TEDUNG (XXmLinkingConsonantmExtPict) × [9.0] BALINESE SIGN ULU CANDRA (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER WA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER YA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE VOWEL SIGN SUKU (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u179F\\u17D2\\u178F\\u17D2\\u179A\\u17B8\", [\"\\u179F\\u17D2\\u178F\\u17D2\\u179A\\u17B8\"] }                                                                                                             #   ÷ [0.2] KHMER LETTER SA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER TA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER RO (LinkingConsonant) × [9.0] KHMER VOWEL SIGN II (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u1B26\\u1B17\\u1B44\\u1B13\", ['\\u1B26', \"\\u1B17\\u1B44\\u1B13\"] }                                                                                                                                 #   ÷ [0.2] BALINESE LETTER NA (LinkingConsonant) ÷ [999.0] BALINESE LETTER NGA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER KA (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u1B27\\u1B13\\u1B44\\u1B0B\\u1B0B\\u1B04\", ['\\u1B27', \"\\u1B13\\u1B44\\u1B0B\", \"\\u1B0B\\u1B04\"] }                                                                                                     #   ÷ [0.2] BALINESE LETTER PA (LinkingConsonant) ÷ [999.0] BALINESE LETTER KA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER RA REPA (LinkingConsonant) ÷ [999.0] BALINESE LETTER RA REPA (LinkingConsonant) × [9.1] BALINESE SIGN BISAH (SpacingMark) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u1795\\u17D2\\u17AF\\u1798\", [\"\\u1795\\u17D2\\u17AF\", '\\u1798'] }                                                                                                                                 #   ÷ [0.2] KHMER LETTER PHA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL QE (LinkingConsonant) ÷ [999.0] KHMER LETTER MO (LinkingConsonant) ÷ [0.3]\n  pending \"GB9c\" { it_iterates_graphemes \"\\u17A0\\u17D2\\u17AB\\u1791\\u17D0\\u1799\", [\"\\u17A0\\u17D2\\u17AB\", \"\\u1791\\u17D0\", '\\u1799'] }                                                                                                     #   ÷ [0.2] KHMER LETTER HA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL RY (LinkingConsonant) ÷ [999.0] KHMER LETTER TO (LinkingConsonant) × [9.0] KHMER SIGN SAMYOK SANNYA (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] KHMER LETTER YO (LinkingConsonant) ÷ [0.3]\nend\n"
  },
  {
    "path": "spec/std/string/grapheme_spec.cr",
    "content": "require \"./spec_helper\"\n\ndescribe String::Grapheme do\n  it \".new\" do\n    String::Grapheme.new(\"foo\", 0...1, 'f').@cluster.should eq 'f'\n    String::Grapheme.new(\"foo\", 0...2, 'o').@cluster.should eq \"fo\"\n    String::Grapheme.new(\"foo\", 1...3, 'o').@cluster.should eq \"oo\"\n  end\n\n  it \"#to_s\" do\n    String::Grapheme.new(\"foo\").to_s.should eq \"foo\"\n    String::Grapheme.new('f').to_s.should eq \"f\"\n\n    String.build do |io|\n      String::Grapheme.new(\"foo\").to_s(io)\n    end.should eq \"foo\"\n    String.build do |io|\n      String::Grapheme.new('f').to_s(io)\n    end.should eq \"f\"\n  end\n\n  it \"#inspect\" do\n    String::Grapheme.new(\"foo\").inspect.should eq %(String::Grapheme(\"foo\"))\n    String::Grapheme.new('f').inspect.should eq %(String::Grapheme('f'))\n  end\n\n  it \"#size\" do\n    String::Grapheme.new(\"foo\").size.should eq 3\n    String::Grapheme.new(\"🙂🙂\").size.should eq 2\n    String::Grapheme.new('f').size.should eq 1\n    String::Grapheme.new('🙂').size.should eq 1\n  end\n\n  it \"#bytesize\" do\n    String::Grapheme.new(\"foo\").bytesize.should eq 3\n    String::Grapheme.new(\"🙂🙂\").bytesize.should eq 8\n    String::Grapheme.new('f').bytesize.should eq 1\n    String::Grapheme.new('🙂').bytesize.should eq 4\n  end\n\n  it \"#==\" do\n    String::Grapheme.new('f').should eq String::Grapheme.new('f')\n    String::Grapheme.new('f').should_not eq String::Grapheme.new(\"f\")\n    String::Grapheme.new(\"foo\").should eq String::Grapheme.new(\"foo\")\n  end\n\n  it \".break?\" do\n    String::Grapheme.break?('a', 'b').should be_true\n\n    String::Grapheme.break?('\\r', '\\n').should be_false\n    String::Grapheme.break?('\\r', 'a').should be_true\n    String::Grapheme.break?('a', '\\n').should be_true\n\n    String::Grapheme.break?('o', '\\u0308').should be_false\n  end\nend\n\ndescribe String do\n  it \"#grapheme_size\" do\n    \"foo\".grapheme_size.should eq 3\n    \"🙂🙂\".grapheme_size.should eq 2\n    \"f\".grapheme_size.should eq 1\n    \"🙂\".grapheme_size.should eq 1\n  end\n\n  it \"#graphemes\" do\n    \"abc\".graphemes.map(&.to_s).should eq [\"a\", \"b\", \"c\"]\n    \"möp\".graphemes.map(&.to_s).should eq [\"m\", \"ö\", \"p\"]\n    \"möp\".graphemes.map(&.to_s).should eq [\"m\", \"o\\u0308\", \"p\"]\n    \"뢴\".graphemes.map(&.to_s).should eq [\"\\u1105\\u116c\\u11ab\"]\n    \"\\r\\n\".graphemes.map(&.to_s).should eq [\"\\r\\n\"]\n  end\n\n  # These are just a couple of manual tests, the lot of automated specs is in grapheme_break_spec.cr\n  describe \"#each_grapheme\" do\n    it_iterates_graphemes \"\", [] of String\n    it_iterates_graphemes \"\\x00\", [Char::ZERO]\n    it_iterates_graphemes \"x\", ['x']\n    it_iterates_graphemes \"basic\", ['b', 'a', 's', 'i', 'c']\n    it_iterates_graphemes \"möp\", ['m', \"o\\u0308\", 'p']\n    it_iterates_graphemes \"\\r\\n\", [\"\\r\\n\"]\n    it_iterates_graphemes \"\\n\\n\", ['\\n', '\\n']\n    it_iterates_graphemes \"\\t*\", ['\\t', '*']\n    it_iterates_graphemes \"뢴\", [\"\\u1105\\u116C\\u11AB\"]\n    it_iterates_graphemes \"ܐ܏ܒܓܕ\", ['\\u0710', \"\\u070F\\u0712\", '\\u0713', '\\u0715']\n    it_iterates_graphemes \"ำ\", ['\\u0E33']\n    it_iterates_graphemes \"ำำ\", [\"\\u0E33\\u0E33\"]\n    it_iterates_graphemes \"สระอำ\", ['\\u0E2A', '\\u0E23', '\\u0E30', \"\\u0E2D\\u0E33\"]\n    it_iterates_graphemes \"*뢴*\", ['*', \"\\u1105\\u116C\\u11AB\", '*']\n    it_iterates_graphemes \"*👩‍❤️‍💋‍👩*\", ['*', \"\\u{1F469}\\u200D\\u2764\\uFE0F\\u200D\\u{1F48B}\\u200D\\u{1F469}\", '*']\n    it_iterates_graphemes \"👩‍❤️‍💋‍👩\", [\"\\u{1F469}\\u200D\\u2764\\uFE0F\\u200D\\u{1F48B}\\u200D\\u{1F469}\"]\n    it_iterates_graphemes \"🏋🏽‍♀️\", [\"\\u{1F3CB}\\u{1F3FD}\\u200D\\u2640\\uFE0F\"]\n    it_iterates_graphemes \"🙂\", ['\\u{1F642}']\n    it_iterates_graphemes \"🙂🙂\", ['\\u{1F642}', '\\u{1F642}']\n    it_iterates_graphemes \"🇩🇪\", [\"\\u{1F1E9}\\u{1F1EA}\"]\n    it_iterates_graphemes \"🏳️‍🌈\", [\"\\u{1F3F3}\\uFE0F\\u200D\\u{1F308}\"]\n  end\nend\n"
  },
  {
    "path": "spec/std/string/spec_helper.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\ndef it_iterates_graphemes(string, graphemes, *, file = __FILE__, line = __LINE__)\n  graphemes = graphemes.map { |grapheme| String::Grapheme.new(grapheme) }\n\n  it_iterates string.dump, graphemes, string.each_grapheme, file: file, line: line\nend\n"
  },
  {
    "path": "spec/std/string/utf16_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"String UTF16\" do\n  describe \"to_utf16\" do\n    it \"in the range U+0000..U+FF\" do\n      encoded = \"\\u{0}hello\\u{ff}\".to_utf16\n      encoded.should eq(Slice[0_u16, 0x68_u16, 0x65_u16, 0x6c_u16, 0x6c_u16, 0x6f_u16, 0xff_u16])\n      encoded.unsafe_fetch(encoded.size).should eq 0_u16\n    end\n\n    it \"in the range U+0000..U+D7FF\" do\n      encoded = \"\\u{0}hello\\u{d7ff}\".to_utf16\n      encoded.should eq(Slice[0_u16, 0x68_u16, 0x65_u16, 0x6c_u16, 0x6c_u16, 0x6f_u16, 0xd7ff_u16])\n      encoded.unsafe_fetch(encoded.size).should eq 0_u16\n    end\n\n    it \"in the range U+E000 to U+FFFF\" do\n      encoded = \"\\u{e000}\\u{ffff}\".to_utf16\n      encoded.should eq(Slice[0xe000_u16, 0xffff_u16])\n      encoded.unsafe_fetch(encoded.size).should eq 0_u16\n    end\n\n    it \"in the range U+10000..U+10FFFF\" do\n      encoded = \"\\u{10000}\\u{10FFFF}\".to_utf16\n      encoded.should eq(Slice[0xd800_u16, 0xdc00_u16, 0xdbff_u16, 0xdfff_u16])\n      encoded.unsafe_fetch(encoded.size).should eq 0_u16\n    end\n\n    it \"in the range U+D800..U+DFFF\" do\n      encoded = String.new(Bytes[0xED, 0xA0, 0x80, 0xED, 0xBF, 0xBF]).to_utf16\n      encoded.should eq(Slice[0xFFFD_u16, 0xFFFD_u16, 0xFFFD_u16, 0xFFFD_u16, 0xFFFD_u16, 0xFFFD_u16])\n      encoded.unsafe_fetch(encoded.size).should eq 0_u16\n    end\n  end\n\n  describe \".from_utf16\" do\n    it \"in the range U+0000..U+D7FF\" do\n      input = Slice[0_u16, 0x68_u16, 0x65_u16, 0x6c_u16, 0x6c_u16, 0x6f_u16, 0xd7ff_u16]\n      String.from_utf16(input).should eq(\"\\u{0}hello\\u{d7ff}\")\n      String.from_utf16(input.to_unsafe).should eq({\"\", input.to_unsafe + 1})\n    end\n\n    it \"in the range U+E000 to U+FFFF\" do\n      input = Slice[0xe000_u16, 0xffff_u16]\n      String.from_utf16(input).should eq(\"\\u{e000}\\u{ffff}\")\n\n      pointer = Slice[0xe000_u16, 0xffff_u16, 0_u16].to_unsafe\n      String.from_utf16(pointer).should eq({\"\\u{e000}\\u{ffff}\", pointer + 3})\n    end\n\n    it \"in the range U+10000..U+10FFFF\" do\n      input = Slice[0xd800_u16, 0xdc00_u16]\n      String.from_utf16(input).should eq(\"\\u{10000}\")\n\n      pointer = Slice[0xd800_u16, 0xdc00_u16, 0_u16].to_unsafe\n      String.from_utf16(pointer).should eq({\"\\u{10000}\", pointer + 3})\n    end\n\n    it \"in the range U+D800..U+DFFF\" do\n      input = Slice[0xdc00_u16, 0xd800_u16]\n      String.from_utf16(input).should eq(\"\\u{fffd}\\u{fffd}\")\n\n      pointer = Slice[0xdc00_u16, 0xd800_u16, 0_u16].to_unsafe\n      String.from_utf16(pointer).should eq({\"\\u{fffd}\\u{fffd}\", pointer + 3})\n    end\n\n    it \"handles null bytes\" do\n      slice = Slice[104_u16, 105_u16, 0_u16, 55296_u16, 56485_u16]\n      String.from_utf16(slice).should eq(\"hi\\0000𐂥\")\n      String.from_utf16(slice.to_unsafe).should eq({\"hi\", slice.to_unsafe + 3})\n    end\n\n    it \"with pointer reads multiple strings\" do\n      input = Slice[0_u16, 0x68_u16, 0x65_u16, 0x6c_u16, 0x6c_u16, 0x6f_u16, 0xd7ff_u16, 0_u16]\n      pointer = input.to_unsafe\n      string, pointer = String.from_utf16(pointer)\n      string.should eq(\"\")\n      string, _ = String.from_utf16(pointer)\n      string.should eq(\"hello\\u{d7ff}\")\n    end\n\n    it \"allows creating from a null-terminated slice\" do\n      String.from_utf16(Slice(UInt16).empty, truncate_at_null: true).should eq(\"\")\n      String.from_utf16(UInt16.slice(102, 111, 111, 98, 97, 114), truncate_at_null: true).should eq(\"foobar\")\n      String.from_utf16(UInt16.slice(102, 111, 111, 0, 98, 97, 114), truncate_at_null: true).should eq(\"foo\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/string_builder_spec.cr",
    "content": "require \"spec\"\n\ndescribe String::Builder do\n  it \"builds\" do\n    str = String::Builder.build do |builder|\n      builder << 123\n      builder << 456\n    end\n    str.should eq(\"123456\")\n  end\n\n  it \"raises if invokes to_s twice\" do\n    builder = String::Builder.new\n    builder << 123\n    builder.to_s.should eq(\"123\")\n\n    expect_raises(Exception, \"Can only invoke 'to_s' once on String::Builder\") { builder.to_s }\n  end\n\n  it \"goes back\" do\n    s = String::Builder.build do |str|\n      str << 12\n      str.back(1)\n    end\n    s.should eq(\"1\")\n  end\n\n  it \"goes back all\" do\n    s = String::Builder.build do |str|\n      str << 12\n      str.back(2)\n    end\n    s.should eq(\"\")\n  end\n\n  describe \"#chomp!\" do\n    it \"returns self\" do\n      str = String::Builder.new\n      str << \"a,b,c,\"\n      str.chomp!(44).to_s.should eq(\"a,b,c\")\n    end\n  end\n\n  it \"raises EOFError\" do\n    builder = String::Builder.new\n    initial_capacity = builder.capacity\n    expect_raises(IO::EOFError) do\n      builder.write Slice.new(Pointer(UInt8).null, Int32::MAX)\n    end\n    # nothing get's written\n    builder.bytesize.should eq 0\n    builder.capacity.should eq initial_capacity\n  end\n\n  # FIXME(wasm32): https://github.com/crystal-lang/crystal/issues/14057\n  {% if flag?(:wasm32) %}\n    pending \"allocation for > 1 GB\"\n  {% else %}\n    it \"allocates for > 1 GB\", tags: %w[slow] do\n      String::Builder.build do |str|\n        mbstring = \"a\" * 1024 * 1024\n        1023.times { str << mbstring }\n\n        str.bytesize.should eq (1 << 30) - (1 << 20)\n        str.capacity.should eq 1 << 30\n\n        str << mbstring\n\n        str.bytesize.should eq 1 << 30\n        str.capacity.should eq Int32::MAX\n\n        1023.times { str << mbstring }\n\n        str.write mbstring.to_slice[0..(-4 - String::HEADER_SIZE)]\n        str << \"a\"\n        expect_raises(IO::EOFError) do\n          str << \"a\"\n        end\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/string_pool_spec.cr",
    "content": "require \"spec\"\nrequire \"string_pool\"\n\ndescribe StringPool do\n  it \"is empty\" do\n    pool = StringPool.new\n    pool.empty?.should be_true\n    pool.size.should eq(0)\n  end\n\n  it \"gets string\" do\n    pool = StringPool.new\n    s1 = pool.get \"foo\"\n    s2 = pool.get \"foo\"\n\n    s1.should eq(\"foo\")\n    s2.should eq(\"foo\")\n    s1.should be(s2)\n    pool.size.should eq(1)\n  end\n\n  it \"gets string IO\" do\n    pool = StringPool.new\n    io = IO::Memory.new \"foo\"\n\n    s1 = pool.get io\n    s2 = pool.get \"foo\"\n\n    s1.should eq(\"foo\")\n    s2.should eq(\"foo\")\n    s1.should be(s2)\n    pool.size.should eq(1)\n  end\n\n  it \"gets slice\" do\n    pool = StringPool.new\n    slice = Bytes.new(3, 'a'.ord.to_u8)\n\n    s1 = pool.get(slice)\n    s2 = pool.get(slice)\n\n    s1.should eq(\"aaa\")\n    s2.should eq(\"aaa\")\n    s1.should be(s2)\n    pool.size.should eq(1)\n  end\n\n  it \"gets pointer with size\" do\n    pool = StringPool.new\n    slice = Bytes.new(3, 'a'.ord.to_u8)\n\n    s1 = pool.get(slice.to_unsafe, slice.size)\n    s2 = pool.get(slice.to_unsafe, slice.size)\n\n    s1.should eq(\"aaa\")\n    s2.should eq(\"aaa\")\n    s1.should be(s2)\n    pool.size.should eq(1)\n  end\n\n  it \"#get?\" do\n    pool = StringPool.new\n    str = \"foo\"\n\n    pool.get?(str).should be_nil\n    pool.get?(str.to_slice).should be_nil\n    pool.get?(str.to_unsafe, str.bytesize).should be_nil\n    pool.get?(IO::Memory.new(str)).should be_nil\n\n    s = pool.get(str)\n\n    pool.get?(str).should be(s)\n    pool.get?(str.to_slice).should be(s)\n    pool.get?(str.to_unsafe, str.bytesize).should be(s)\n    pool.get?(IO::Memory.new(str)).should be(s)\n\n    pool.size.should eq(1)\n  end\n\n  it \"puts many\" do\n    pool = StringPool.new\n    10_000.times do |i|\n      pool.get(i.to_s)\n    end\n    pool.size.should eq(10_000)\n  end\n\n  it \"can be created with larger initial capacity\" do\n    pool = StringPool.new(initial_capacity: 32)\n    s1 = pool.get \"foo\"\n    s2 = pool.get \"foo\"\n    s1.should be(s2)\n    pool.size.should eq(1)\n  end\n\n  it \"doesn't fail if initial capacity is too small\" do\n    pool = StringPool.new(initial_capacity: 0)\n    100.times do |i|\n      pool.get(i.to_s)\n    end\n    pool.size.should eq(100)\n  end\n\n  it \"doesn't fail if initial capacity is not a power of 2\" do\n    pool = StringPool.new(initial_capacity: 17)\n    100.times do |i|\n      pool.get(i.to_s)\n    end\n    pool.size.should eq(100)\n  end\nend\n"
  },
  {
    "path": "spec/std/string_scanner_spec.cr",
    "content": "require \"spec\"\nrequire \"string_scanner\"\n\ndescribe StringScanner do\n  describe \"#scan\" do\n    it \"returns the string matched and advances the offset\" do\n      s = StringScanner.new(\"this is a string\")\n      s.scan(/\\w+/).should eq(\"this\")\n      s.scan(' ').should eq(\" \")\n      s.scan(\"is \").should eq(\"is \")\n      s.scan(/\\w+\\s/).should eq(\"a \")\n      s.scan(2).should eq(\"st\")\n      s.scan(/\\w+/).should eq(\"ring\")\n    end\n\n    it \"returns nil if it can't match from the offset\" do\n      s = StringScanner.new(\"test string\")\n      s.scan(/\\w+/).should_not be_nil # => \"test\"\n      s.scan(/\\w+/).should be_nil\n      s.scan('s').should be_nil\n      s.scan(\"string\").should be_nil\n      s.scan(/\\s\\w\\w/).should_not be_nil # => \" string\"\n      s.scan(4).should eq(\"ring\")\n      s.scan(/.*/).should_not be_nil # => \"\"\n      s.scan(1).should be_nil\n    end\n\n    it \"errors on negative ints\" do\n      s = StringScanner.new(\"testy mctesterson\")\n      s.scan(10)\n      expect_raises(ArgumentError, \"Negative lookahead count: -10\") { s.scan(-10) }\n    end\n\n    it \"works on multi-byte strings\" do\n      s = StringScanner.new(\"テストの文字列\")\n      s.scan(/\\w\\w\\w/).should eq(\"テスト\")\n      s.scan(/[a-z]+/).should be_nil\n      s.scan('ト').should be_nil\n      s.scan('の').should eq(\"の\")\n      s.scan(10).should be_nil\n      s.scan(2).should eq(\"文字\")\n      s.scan(\"不在\").should be_nil\n      s.scan(\"列\").should eq(\"列\")\n    end\n  end\n\n  describe \"#scan_until\" do\n    it \"returns the string matched and advances the offset\" do\n      s = StringScanner.new(\"test string\")\n      s.scan_until(/t /).should eq(\"test \")\n      s.offset.should eq(5)\n      s.scan_until(\"tr\").should eq(\"str\")\n      s.offset.should eq(8)\n      s.scan_until('n').should eq(\"in\")\n      s.offset.should eq(10)\n    end\n\n    it \"returns nil if it can't match from the offset\" do\n      s = StringScanner.new(\"test string\")\n      s.offset = 8\n      s.scan_until(/tr/).should be_nil\n      s.scan_until('r').should be_nil\n      s.scan_until(\"tr\").should be_nil\n    end\n  end\n\n  describe \"#skip\" do\n    describe \"with single byte strings\" do\n      it \"advances the offset but does not return the string matched\" do\n        s = StringScanner.new(\"this is a string\")\n\n        s.skip(/\\w+\\s/).should eq(5)\n        s.offset.should eq(5)\n        s[0]?.should eq(\"this \")\n\n        s.skip(/\\d+/).should be_nil\n        s.offset.should eq(5)\n        s[0]?.should be_nil\n\n        s.skip('i').should eq(1)\n        s.offset.should eq(6)\n        s[0]?.should eq(\"i\")\n\n        s.skip(\"s \").should eq(2)\n        s.offset.should eq(8)\n        s[0]?.should eq(\"s \")\n\n        s.skip(/\\w+\\s/).should eq(2)\n        s.offset.should eq(10)\n        s[0]?.should eq(\"a \")\n\n        s.skip(5).should eq(5)\n        s.offset.should eq(15)\n        s[0]?.should eq(\"strin\")\n\n        s.skip(100).should be_nil\n        s.skip(2).should be_nil\n        s.skip(1).should eq(1)\n        s.skip(1).should be_nil\n        s.skip(0).should eq(0)\n      end\n    end\n\n    describe \"with multibyte strings\" do\n      it \"advances the offset but does not return the string matched\" do\n        s = StringScanner.new(\"これは文字列である\")\n\n        s.skip(/\\w\\w\\w/).should eq(3)\n        s.offset.should eq(3)\n        s[0]?.should eq(\"これは\")\n\n        s.skip(/\\d+/).should be_nil\n        s.offset.should eq(3)\n\n        s.skip(100).should be_nil\n\n        s.skip(\"文字\").should eq(2)\n        s.offset.should eq(5)\n        s[0]?.should eq(\"文字\")\n\n        s.skip('列').should eq(1)\n        s.offset.should eq(6)\n        s[0]?.should eq(\"列\")\n\n        s.skip(2).should eq(2)\n        s.offset.should eq(8)\n        s[0]?.should eq(\"であ\")\n\n        s.skip(2).should be_nil\n        s.skip(0).should eq(0)\n        s[0]?.should eq(\"\")\n\n        s.skip(1).should eq(1)\n        s[0]?.should eq(\"る\")\n\n        s.eos?.should be_true\n        s.skip(1).should be_nil\n        s.skip(0).should eq(0)\n        s[0]?.should eq(\"\")\n      end\n    end\n  end\n\n  describe \"#skip_until\" do\n    it \"advances the offset but does not return the string matched\" do\n      s = StringScanner.new(\"this is a string\")\n\n      s.skip_until(/not/).should be_nil\n      s.offset.should eq(0)\n      s[0]?.should be_nil\n\n      s.skip_until(/\\sis\\s/).should eq(8)\n      s.offset.should eq(8)\n      s[0]?.should_not be_nil\n\n      s.skip_until(\"st\").should eq(4)\n      s.offset.should eq(12)\n      s[0]?.should_not be_nil\n\n      s.skip_until(\"ng\").should eq(4)\n      s.offset.should eq(16)\n      s[0]?.should_not be_nil\n    end\n  end\n\n  describe \"#eos\" do\n    it \"it is true when the offset is at the end\" do\n      s = StringScanner.new(\"this is a string\")\n      s.eos?.should be_false\n      s.skip(/(\\w+\\s?){4}/)\n      s.eos?.should be_true\n    end\n  end\n\n  describe \"#check\" do\n    it \"returns the string matched but does not advances the offset\" do\n      s = StringScanner.new(\"this is a string\")\n      s.offset = 5\n\n      s.check(/\\w+\\s/).should eq(\"is \")\n      s.offset.should eq(5)\n      s[0].should eq(\"is \")\n\n      s.check(/\\w+\\s/).should eq(\"is \")\n      s.offset.should eq(5)\n      s[0].should eq(\"is \")\n\n      s.check('i').should eq(\"i\")\n      s.offset.should eq(5)\n      s[0].should eq(\"i\")\n\n      s.check(\"is a str\").should eq(\"is a str\")\n      s.offset.should eq(5)\n      s[0].should eq(\"is a str\")\n\n      s.check(4).should eq(\"is a\")\n      s.offset.should eq(5)\n      s[0].should eq(\"is a\")\n\n      s.check(100).should be_nil\n      s.offset.should eq(5)\n      s[0]?.should be_nil\n    end\n\n    it \"returns nil if it can't match from the offset\" do\n      s = StringScanner.new(\"test string\")\n      s.check(/\\d+/).should be_nil\n      s.check('0').should be_nil\n      s.check(\"01\").should be_nil\n      s.check(100).should be_nil\n    end\n  end\n\n  describe \"#check_until\" do\n    it \"returns the string matched and advances the offset\" do\n      s = StringScanner.new(\"test string\")\n      s.check_until(/tr/).should eq(\"test str\")\n      s.offset.should eq(0)\n      s.check_until('r').should eq(\"test str\")\n      s.offset.should eq(0)\n      s.check_until(\"tr\").should eq(\"test str\")\n      s.offset.should eq(0)\n      s.check_until(/g/).should eq(\"test string\")\n      s.offset.should eq(0)\n      s.check_until('g').should eq(\"test string\")\n      s.offset.should eq(0)\n      s.check_until(\"ng\").should eq(\"test string\")\n      s.offset.should eq(0)\n    end\n\n    it \"returns nil if it can't match from the offset\" do\n      s = StringScanner.new(\"test string\")\n      s.offset = 8\n      s.check_until(/tr/).should be_nil\n      s.check_until('r').should be_nil\n      s.check_until(\"tr\").should be_nil\n    end\n  end\n\n  describe \"#rest\" do\n    it \"returns the remainder of the string from the offset\" do\n      s = StringScanner.new(\"this is a string\")\n      s.rest.should eq(\"this is a string\")\n\n      s.scan(/this is a /)\n      s.rest.should eq(\"string\")\n\n      s.scan(/string/)\n      s.rest.should eq(\"\")\n    end\n  end\n\n  describe \"#[]\" do\n    it \"allows access to subgroups of the last match\" do\n      s = StringScanner.new(\"Fri Dec 12 1975 14:39\")\n      regex = /(?<wday>\\w+) (?<month>\\w+) (?<day>\\d+)/\n      s.scan(regex).should eq(\"Fri Dec 12\")\n      s[0].should eq(\"Fri Dec 12\")\n      s[1].should eq(\"Fri\")\n      s[2].should eq(\"Dec\")\n      s[3].should eq(\"12\")\n      s[\"wday\"].should eq(\"Fri\")\n      s[\"month\"].should eq(\"Dec\")\n      s[\"day\"].should eq(\"12\")\n\n      s.scan(' ').should eq(\" \")\n      s[0].should eq(\" \")\n      s.scan(\"1975\").should eq(\"1975\")\n      s[0].should eq(\"1975\")\n    end\n\n    it \"raises when there is no last match\" do\n      s = StringScanner.new(\"Fri Dec 12 1975 14:39\")\n\n      s.scan(/this is not there/)\n      expect_raises(Exception, \"Nil assertion failed\") { s[0] }\n\n      s.scan('t')\n      expect_raises(Exception, \"Nil assertion failed\") { s[0] }\n\n      s.scan(\"this is not there\")\n      expect_raises(Exception, \"Nil assertion failed\") { s[0] }\n    end\n\n    it \"raises when there is no subgroup\" do\n      s = StringScanner.new(\"Fri Dec 12 1975 14:39\")\n      regex = /(?<wday>\\w+) (?<month>\\w+) (?<day>\\d+)/\n\n      s.scan(regex)\n\n      s[0].should_not be_nil\n      expect_raises(IndexError) { s[5] }\n      expect_raises(KeyError, \"Capture group 'something' does not exist\") { s[\"something\"] }\n\n      s.scan(' ')\n\n      s[0].should_not be_nil\n      expect_raises(IndexError) { s[1] }\n      expect_raises(KeyError, \"Capture group 'something' does not exist\") { s[\"something\"] }\n\n      s.scan(\"1975\")\n\n      s[0].should_not be_nil\n      expect_raises(IndexError) { s[1] }\n      expect_raises(KeyError, \"Capture group 'something' does not exist\") { s[\"something\"] }\n    end\n  end\n\n  describe \"#[]?\" do\n    it \"allows access to subgroups of the last match\" do\n      s = StringScanner.new(\"Fri Dec 12 1975 14:39\")\n      result = s.scan(/(?<wday>\\w+) (?<month>\\w+) (?<day>\\d+)/)\n\n      result.should eq(\"Fri Dec 12\")\n      s[0]?.should eq(\"Fri Dec 12\")\n      s[1]?.should eq(\"Fri\")\n      s[2]?.should eq(\"Dec\")\n      s[3]?.should eq(\"12\")\n      s[\"wday\"]?.should eq(\"Fri\")\n      s[\"month\"]?.should eq(\"Dec\")\n      s[\"day\"]?.should eq(\"12\")\n\n      s.scan(' ').should eq(\" \")\n      s[0]?.should eq(\" \")\n      s.scan(\"1975\").should eq(\"1975\")\n      s[0]?.should eq(\"1975\")\n    end\n\n    it \"returns nil when there is no last match\" do\n      s = StringScanner.new(\"Fri Dec 12 1975 14:39\")\n      s.scan(/this is not there/)\n\n      s[0]?.should be_nil\n\n      s.scan('t')\n      s[0]?.should be_nil\n\n      s.scan(\"this is not there\")\n      s[0]?.should be_nil\n    end\n\n    it \"raises when there is no subgroup\" do\n      s = StringScanner.new(\"Fri Dec 12 1975 14:39\")\n\n      s.scan(/(?<wday>\\w+) (?<month>\\w+) (?<day>\\d+)/)\n\n      s[0]?.should_not be_nil\n      s[5]?.should be_nil\n      s[\"something\"]?.should be_nil\n\n      s.scan(' ')\n\n      s[0]?.should_not be_nil\n      s[1]?.should be_nil\n      s[\"something\"]?.should be_nil\n\n      s.scan(\"1975\")\n\n      s[0]?.should_not be_nil\n      s[1]?.should be_nil\n      s[\"something\"]?.should be_nil\n    end\n  end\n\n  describe \"#string\" do\n    it { StringScanner.new(\"foo\").string.should eq(\"foo\") }\n  end\n\n  describe \"#offset\" do\n    it \"returns the current position\" do\n      s = StringScanner.new(\"this is a string\")\n      s.offset.should eq(0)\n      s.scan(/\\w+/)\n      s.offset.should eq(4)\n    end\n  end\n\n  describe \"#offset=\" do\n    it \"sets the current position\" do\n      s = StringScanner.new(\"this is a string\")\n      s.offset = 5\n      s.scan(/\\w+/).should eq(\"is\")\n    end\n\n    it \"raises on negative positions\" do\n      s = StringScanner.new(\"this is a string\")\n      expect_raises(IndexError) { s.offset = -2 }\n    end\n  end\n\n  describe \"#inspect\" do\n    it \"has information on the scanner\" do\n      s = StringScanner.new(\"this is a string\")\n      s.inspect.should eq(%(#<StringScanner 0/16 \"‣this …>))\n      s.scan(/\\w+\\s/)\n      s.inspect.should eq(%(#<StringScanner 5/16 …s ‣is …>))\n      s.scan(1)\n      s.inspect.should eq(%(#<StringScanner 6/16 … i‣s a…>))\n      s.scan(/\\w+\\s/)\n      s.inspect.should eq(%(#<StringScanner 8/16 …s ‣a s…>))\n      s.scan(/\\w+\\s\\w+/)\n      s.inspect.should eq(%(#<StringScanner 16/16 …tring‣\">))\n    end\n\n    it \"works with small strings\" do\n      s = StringScanner.new(\"hi\")\n      s.inspect.should eq(%(#<StringScanner 0/2 \"‣hi\">))\n      s.scan(/\\w/)\n      s.inspect.should eq(%(#<StringScanner 1/2 \"h‣i\">))\n      s.scan(/\\w/)\n      s.inspect.should eq(%(#<StringScanner 2/2 \"hi‣\">))\n\n      s = StringScanner.new(\" a\\n\\t \")\n      s.skip(1)\n      s.inspect.should eq(%(#<StringScanner 1/5 \" ‣a\\\\n\\\\t \">))\n\n      s = StringScanner.new(\"\\0\\e\")\n      s.skip(1)\n      s.inspect.should eq(%(#<StringScanner 1/2 \"\\\\u0000‣\\\\e\">))\n    end\n\n    it \"works with multi-byte strings\" do\n      s = StringScanner.new(\"これは文字列である\")\n      s.inspect.should eq(%(#<StringScanner 0/9 \"‣これは文字…>))\n      s.scan(1)\n      s.inspect.should eq(%(#<StringScanner 1/9 \"こ‣れは文字…>))\n      s.scan(1)\n      s.inspect.should eq(%(#<StringScanner 2/9 \"これ‣は文字…>))\n      s.scan(1)\n      s.inspect.should eq(%(#<StringScanner 3/9 …れは‣文字列…>))\n      s.scan(3)\n      s.inspect.should eq(%(#<StringScanner 6/9 …字列‣である\">))\n      s.terminate\n      s.inspect.should eq(%(#<StringScanner 9/9 …字列である‣\">))\n    end\n\n    it \"works with empty string\" do\n      s = StringScanner.new(\"\")\n      s.inspect.should eq(%(#<StringScanner 0/0 \"‣\">))\n    end\n  end\n\n  describe \"#peek\" do\n    it \"shows the next len characters without advancing the offset\" do\n      s = StringScanner.new(\"this is a string\")\n      s.offset.should eq(0)\n      s.peek(4).should eq(\"this\")\n      s.offset.should eq(0)\n      s.peek(7).should eq(\"this is\")\n      s.offset.should eq(0)\n    end\n\n    it \"shows the next len characters for multi-byte strings\" do\n      s = StringScanner.new(\"これは文字列である\")\n      s.offset.should eq(0)\n      s.peek(3).should eq(\"これは\")\n      s.offset.should eq(0)\n      s.peek(6).should eq(\"これは文字列\")\n      s.offset.should eq(0)\n    end\n\n    it \"errors on negative input\" do\n      s = StringScanner.new(\"abcde\")\n      s.scan(2)\n      expect_raises(ArgumentError, \"Negative lookahead count: -1\") { s.peek(-1) }\n    end\n  end\n\n  describe \"#peek_behind\" do\n    it \"shows characters behind the scan head\" do\n      s = StringScanner.new(\"abcdefg\")\n      s.peek_behind(10).should eq(\"\")\n      s.scan(3)\n      s.peek_behind(10).should eq(\"abc\")\n      s.peek_behind(2).should eq(\"bc\")\n    end\n\n    it \"shows characters behind the scan head for multi-byte strings\" do\n      s = StringScanner.new(\"あいうえお\")\n      s.peek_behind(10).should eq(\"\")\n      s.scan(3)\n      s.peek_behind(10).should eq(\"あいう\")\n      s.peek_behind(2).should eq(\"いう\")\n    end\n\n    it \"errors on negative input\" do\n      s = StringScanner.new(\"abcde\")\n      s.scan(3)\n      expect_raises(ArgumentError, \"Negative lookbehind count\") { s.peek_behind(-1) }\n    end\n  end\n\n  describe \"#rewind\" do\n    it \"rewinds a single byte optimizable string\" do\n      s = StringScanner.new(\"abcde\")\n\n      expect_raises(IndexError, \"Index out of range\") { s.rewind(10) }\n\n      s.offset.should eq(0)\n\n      expect_raises(ArgumentError, \"Negative lookbehind count\") { s.rewind(-1) }\n\n      s.skip(3)\n      s.current_char.should eq('d')\n      s.offset.should eq(3)\n\n      s.rewind(0)\n      s.offset.should eq(3)\n\n      s.rewind(2)\n      s.offset.should eq(1)\n\n      expect_raises(IndexError, \"Index out of range\") { s.rewind(1000) }\n      s.offset.should eq(1)\n    end\n\n    it \"rewinds a multibyte char string\" do\n      s = StringScanner.new(\"あいうえお\")\n      expect_raises(IndexError, \"Index out of range\") { s.rewind(10) }\n      s.offset.should eq(0)\n\n      s.skip(3)\n      s.current_char.should eq('え')\n      s.offset.should eq(3)\n\n      s.rewind(2)\n      s.offset.should eq(1)\n\n      expect_raises(IndexError, \"Index out of range\") { s.rewind(1000) }\n      s.offset.should eq(1)\n    end\n  end\n\n  describe \"#previous_char? and #previous_byte?\" do\n    it \"finds the previous byte or char for single-byte strings\" do\n      s = StringScanner.new(\"abcde\")\n      s.previous_byte?.should be_nil\n      expect_raises(IndexError, \"No previous byte\") { s.previous_byte }\n      s.previous_char?.should be_nil\n      expect_raises(IndexError) { s.previous_char }\n\n      s.scan(1)\n      s.previous_byte?.should eq('a'.ord)\n      s.previous_byte.should eq('a'.ord)\n      s.previous_char?.should eq('a')\n      s.previous_char.should eq('a')\n    end\n\n    it \"finds the previous byte or char for multi-byte strings\" do\n      s = StringScanner.new(\"あいうえお\")\n      s.previous_byte?.should be_nil\n      expect_raises(IndexError, \"No previous byte\") { s.previous_byte }\n      s.previous_char?.should be_nil\n      expect_raises(IndexError) { s.previous_char }\n\n      s.scan(1)\n      s.previous_byte?.should eq('あ'.bytes.last)\n      s.previous_byte.should eq('あ'.bytes.last)\n      s.previous_char?.should eq('あ')\n      s.previous_char.should eq('あ')\n    end\n  end\n\n  describe \"#current_char and #current_byte\" do\n    it \"finds the current byte and char for single-byte strings\" do\n      s = StringScanner.new(\"abcde\")\n      s.current_char.should eq('a')\n      s.current_char?.should eq('a')\n      s.current_byte.should eq('a'.ord)\n      s.current_byte?.should eq('a'.ord)\n\n      s.scan(2)\n      s.current_char.should eq('c')\n      s.current_char?.should eq('c')\n      s.current_byte.should eq('c'.ord)\n      s.current_byte?.should eq('c'.ord)\n\n      s.terminate\n      s.current_char?.should be_nil\n      expect_raises(IndexError) { s.current_char }\n      s.current_byte?.should be_nil\n      expect_raises(IndexError) { s.current_byte }\n    end\n\n    it \"finds the current byte and char for multi-byte strings\" do\n      s = StringScanner.new(\"あいうえお\")\n      s.current_char.should eq('あ')\n      s.current_char?.should eq('あ')\n      s.current_byte.should eq('あ'.bytes.first)\n      s.current_byte?.should eq('あ'.bytes.first)\n\n      s.scan(2)\n      s.current_char.should eq('う')\n      s.current_char?.should eq('う')\n      s.current_byte.should eq('う'.bytes.first)\n      s.current_byte?.should eq('う'.bytes.first)\n\n      s.terminate\n      s.current_char?.should be_nil\n      expect_raises(IndexError) { s.current_char }\n      s.current_byte?.should be_nil\n      expect_raises(IndexError) { s.current_byte }\n    end\n  end\n\n  describe \"#beginning_of_line?\" do\n    it \"checks backwards for a newline or start of string\" do\n      s = StringScanner.new(\"a\\nb\\nc\\n\")\n      s.beginning_of_line?.should be_true\n      s.skip(1)\n      s.beginning_of_line?.should be_false\n      s.skip(1)\n      s.beginning_of_line?.should be_true\n      s.terminate\n      s.beginning_of_line?.should be_false\n    end\n  end\n\n  describe \"#reset\" do\n    it \"resets the scan offset to the beginning and clears the last match\" do\n      s = StringScanner.new(\"this is a string\")\n      s.scan_until(/str/)\n      s[0]?.should_not be_nil\n      s.offset.should_not eq(0)\n\n      s.reset\n      s[0]?.should be_nil\n      s.offset.should eq(0)\n    end\n  end\n\n  describe \"#terminate\" do\n    it \"moves the scan offset to the end of the string and clears the last match\" do\n      s = StringScanner.new(\"this is a string\")\n      s.scan_until(/str/)\n      s[0]?.should_not be_nil\n      s.eos?.should be_false\n\n      s.terminate\n      s[0]?.should be_nil\n      s.eos?.should be_true\n    end\n  end\n\n  describe \"#matched?\" do\n    s = StringScanner.new(\"sphinx of black quartz, judge my vow\")\n    s.matched?.should eq(false)\n\n    s.check(1000)\n    s.matched?.should eq(false)\n    s.check(10)\n    s.matched?.should eq(true)\n\n    s.check(/Sphinx/)\n    s.matched?.should eq(false)\n    s.check(/sphinx/)\n    s.matched?.should eq(true)\n\n    s.skip(\"nonsense\")\n    s.matched?.should eq(false)\n    s.skip(\"sphinx \")\n    s.matched?.should eq(true)\n\n    s.skip(1000)\n    s.matched?.should eq(false)\n    s.skip(3)\n    s.matched?.should eq(true)\n\n    s.scan(/\\d+/)\n    s.matched?.should eq(false)\n    s.scan(/\\w+/)\n    s.matched?.should eq(true)\n\n    s.scan('b')\n    s.matched?.should eq(false)\n    s.scan(' ')\n    s.matched?.should eq(true)\n\n    # unaffected by #peek\n    s.scan(1000)\n    s.matched?.should eq(false)\n    s.peek(10)\n    s.matched?.should eq(false)\n  end\nend\n"
  },
  {
    "path": "spec/std/string_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../support/string\"\nrequire \"spec/helpers/iterate\"\nrequire \"spec/helpers/string\"\n\ndescribe \"String\" do\n  describe \"[]\" do\n    it \"gets with positive index\" do\n      c = \"hello!\"[1]\n      c.should be_a(Char)\n      c.should eq('e')\n    end\n\n    it \"gets with negative index\" do\n      \"hello!\"[-1].should eq('!')\n    end\n\n    it \"gets with inclusive range\" do\n      \"hello!\"[1..4].should eq(\"ello\")\n    end\n\n    it \"gets with inclusive range with negative indices\" do\n      \"hello!\"[-5..-2].should eq(\"ello\")\n    end\n\n    it \"gets with exclusive range\" do\n      \"hello!\"[1...4].should eq(\"ell\")\n    end\n\n    it \"gets with start and count\" do\n      \"hello\"[1, 3].should eq(\"ell\")\n    end\n\n    it \"gets with exclusive range with unicode\" do\n      \"há日本語\"[1..3].should eq(\"á日本\")\n    end\n\n    it \"gets with range without end\" do\n      \"há日本語\"[1..nil].should eq(\"á日本語\")\n    end\n\n    it \"gets with range without beginning\" do\n      \"há日本語\"[nil..2].should eq(\"há日\")\n    end\n\n    it \"gets when index is last and count is zero\" do\n      \"foo\"[3, 0].should eq(\"\")\n    end\n\n    it \"gets when index is last and count is positive\" do\n      \"foo\"[3, 10].should eq(\"\")\n    end\n\n    it \"gets when index is last and count is negative at last\" do\n      expect_raises(ArgumentError) do\n        \"foo\"[3, -1]\n      end\n    end\n\n    it { \"foo\"[3..-10].should eq(\"\") }\n\n    it \"gets when index is last and count is negative at last with utf-8\" do\n      expect_raises(ArgumentError) do\n        \"há日本語\"[5, -1]\n      end\n    end\n\n    it \"gets when index is last and count is zero in utf-8\" do\n      \"há日本語\"[5, 0].should eq(\"\")\n    end\n\n    it \"gets when index is last and count is positive in utf-8\" do\n      \"há日本語\"[5, 10].should eq(\"\")\n    end\n\n    it \"raises IndexError if pointing after last char which is non-ASCII\" do\n      expect_raises(IndexError) do\n        \"ß\"[1]\n      end\n    end\n\n    it \"raises index out of bound on index out of range with range\" do\n      expect_raises(IndexError) do\n        \"foo\"[4..1]\n      end\n    end\n\n    it \"raises index out of bound on index out of range with range and utf-8\" do\n      expect_raises(IndexError) do\n        \"há日本語\"[6..1]\n      end\n    end\n\n    it \"gets with exclusive with start and count\" do\n      \"há日本語\"[1, 3].should eq(\"á日本\")\n    end\n\n    it \"gets with exclusive with start and count to end\" do\n      \"há日本語\"[1, 4].should eq(\"á日本語\")\n    end\n\n    it \"gets with start and count with negative start\" do\n      \"こんにちは\"[-3, 2].should eq(\"にち\")\n    end\n\n    it \"raises if index out of bounds\" do\n      expect_raises(IndexError) do\n        \"foo\"[4, 1]\n      end\n    end\n\n    it \"raises if index out of bounds with utf-8\" do\n      expect_raises(IndexError) do\n        \"こんにちは\"[6, 1]\n      end\n    end\n\n    it \"raises if count is negative\" do\n      expect_raises(ArgumentError) do\n        \"foo\"[1, -1]\n      end\n    end\n\n    it \"raises if count is negative with utf-8\" do\n      expect_raises(ArgumentError) do\n        \"こんにちは\"[3, -1]\n      end\n    end\n\n    it \"gets with single char\" do\n      \";\"[0..-2].should eq(\"\")\n    end\n\n    it \"raises on too negative left bound\" do\n      expect_raises IndexError do\n        \"foo\"[-4..0]\n      end\n    end\n\n    describe \"with a regex\" do\n      it { \"FooBar\"[/o+/].should eq \"oo\" }\n      it { \"FooBar\"[/([A-Z])/, 1].should eq \"F\" }\n      it { \"FooBar\"[/x/]?.should be_nil }\n      it { \"FooBar\"[/x/, 1]?.should be_nil }\n      it { \"FooBar\"[/(x)/, 1]?.should be_nil }\n      it { \"FooBar\"[/o(o)/, 2]?.should be_nil }\n      it { \"FooBar\"[/o(?<this>o)/, \"this\"].should eq \"o\" }\n      it { \"FooBar\"[/(?<this>x)/, \"that\"]?.should be_nil }\n    end\n\n    it \"gets with a string\" do\n      \"FooBar\"[\"Bar\"].should eq \"Bar\"\n      expect_raises(Exception, \"Nil assertion failed\") { \"FooBar\"[\"Baz\"] }\n    end\n\n    it \"gets with a char\" do\n      expect_raises(Exception, \"Nil assertion failed\") { \"foo/bar\"['-'] }\n    end\n  end\n\n  describe \"[]?\" do\n    it \"gets with a string\" do\n      \"FooBar\"[\"Bar\"]?.should eq \"Bar\"\n      \"FooBar\"[\"Baz\"]?.should be_nil\n    end\n\n    it \"gets with a char\" do\n      \"foo/bar\"['/'].should eq '/'\n      expect_raises(Exception, \"Nil assertion failed\") { \"foo/bar\"['-'] }\n      \"foo/bar\"['/']?.should eq '/'\n      \"foo/bar\"['-']?.should be_nil\n    end\n\n    it \"gets with index\" do\n      \"hello\"[1]?.should eq('e')\n      \"hello\"[5]?.should be_nil\n      \"hello\"[-1]?.should eq('o')\n      \"hello\"[-6]?.should be_nil\n    end\n\n    it \"returns nil if pointing after last char which is non-ASCII\" do\n      \"ß\"[1]?.should be_nil\n    end\n\n    it \"gets with range\" do\n      \"hello\"[1..2]?.should eq \"el\"\n      \"hello\"[6..-1]?.should be_nil\n      \"hello\"[-6..-1]?.should be_nil\n      \"hello\"[-6..]?.should be_nil\n    end\n\n    it \"gets with start and count\" do\n      \"hello\"[1, 3]?.should eq(\"ell\")\n      \"hello\"[6, 3]?.should be_nil\n    end\n\n    it \"gets with range without end\" do\n      \"hello\"[1..nil]?.should eq(\"ello\")\n      \"hello\"[6..nil]?.should be_nil\n    end\n\n    it \"gets with range without beginning\" do\n      \"hello\"[nil..2]?.should eq(\"hel\")\n    end\n  end\n\n  describe \"#byte_slice\" do\n    it \"gets byte_slice\" do\n      \"hello\".byte_slice(1, 3).should eq(\"ell\")\n      \"hello\".byte_slice(1..3).should eq(\"ell\")\n    end\n\n    it \"gets byte_slice with negative count\" do\n      expect_raises(ArgumentError) do\n        \"hello\".byte_slice(1, -10)\n      end\n    end\n\n    it \"gets byte_slice with negative count at last\" do\n      expect_raises(ArgumentError) do\n        \"hello\".byte_slice(5, -1)\n      end\n    end\n\n    it \"gets byte_slice with start out of bounds\" do\n      expect_raises(IndexError) do\n        \"hello\".byte_slice(10, 3)\n      end\n      expect_raises(IndexError) do\n        \"hello\".byte_slice(10..13)\n      end\n    end\n\n    it \"gets byte_slice with large count\" do\n      \"hello\".byte_slice(1, 10).should eq(\"ello\")\n      \"hello\".byte_slice(1..10).should eq(\"ello\")\n    end\n\n    it \"gets byte_slice with negative index\" do\n      \"hello\".byte_slice(-2, 3).should eq(\"lo\")\n      \"hello\".byte_slice(-2..-1).should eq(\"lo\")\n    end\n\n    it \"gets byte_slice(Int) with start out of bounds\" do\n      expect_raises(IndexError) do\n        \"hello\".byte_slice(10)\n      end\n      expect_raises(IndexError) do\n        \"hello\".byte_slice(-10)\n      end\n    end\n  end\n\n  describe \"#byte_slice?\" do\n    it \"gets byte_slice\" do\n      \"hello\".byte_slice?(1, 3).should eq(\"ell\")\n      \"hello\".byte_slice?(1..3).should eq(\"ell\")\n    end\n\n    it \"gets byte_slice with negative count\" do\n      expect_raises(ArgumentError) do\n        \"hello\".byte_slice?(1, -10)\n      end\n    end\n\n    it \"gets byte_slice with negative count at last\" do\n      expect_raises(ArgumentError) do\n        \"hello\".byte_slice?(5, -1)\n      end\n    end\n\n    it \"gets byte_slice with start out of bounds\" do\n      \"hello\".byte_slice?(10, 3).should be_nil\n      \"hello\".byte_slice?(10..13).should be_nil\n    end\n\n    it \"gets byte_slice with large count\" do\n      \"hello\".byte_slice?(1, 10).should eq(\"ello\")\n      \"hello\".byte_slice?(1..11).should eq(\"ello\")\n    end\n\n    it \"gets byte_slice with negative index\" do\n      \"hello\".byte_slice?(-2, 3).should eq(\"lo\")\n      \"hello\".byte_slice?(-2..-1).should eq(\"lo\")\n    end\n\n    it \"gets byte_slice(Int) with start out of bounds\" do\n      \"hello\".byte_slice?(10).should be_nil\n      \"hello\".byte_slice?(-10).should be_nil\n    end\n  end\n\n  describe \"to_i\" do\n    it { \"1234\".to_i.should eq(1234) }\n    it { \"-128\".to_i8.should eq(-128) }\n    it { \"   +1234   \".to_i.should eq(1234) }\n    it { \"   -1234   \".to_i.should eq(-1234) }\n    it { \"   +1234   \".to_i.should eq(1234) }\n    it { \"   -00001234\".to_i.should eq(-1234) }\n    it { \"\\u00A01234\\u00A0\".to_i.should eq(1234) }\n    it { \"1_234\".to_i(underscore: true).should eq(1234) }\n    it { \"1101\".to_i(base: 2).should eq(13) }\n    it { \"12ab\".to_i(16).should eq(4779) }\n    it { \"0x123abc\".to_i(prefix: true).should eq(1194684) }\n    it { \"0X123abc\".to_i(prefix: true).should eq(1194684) }\n    it { \"0b1101\".to_i(prefix: true).should eq(13) }\n    it { \"0B1101\".to_i(prefix: true).should eq(13) }\n    it { \"0b001101\".to_i(prefix: true).should eq(13) }\n    it { \"0123\".to_i(prefix: true).should eq(123) }\n    it { \"0o123\".to_i(prefix: true).should eq(83) }\n    it { \"0O123\".to_i(prefix: true).should eq(83) }\n    it { \"0123\".to_i(leading_zero_is_octal: true).should eq(83) }\n    it { \"123\".to_i(leading_zero_is_octal: true).should eq(123) }\n    it { \"0o755\".to_i(prefix: true, leading_zero_is_octal: true).should eq(493) }\n    it { \"5\".to_i(prefix: true).should eq(5) }\n    it { \"0\".to_i(prefix: true).should eq(0) }\n    it { \"00\".to_i(prefix: true).should eq(0) }\n    it { \"00\".to_i(leading_zero_is_octal: true).should eq(0) }\n    it { \"00\".to_i(prefix: true, leading_zero_is_octal: true).should eq(0) }\n    it { \"123hello\".to_i(strict: false).should eq(123) }\n    it { \"99 red balloons\".to_i(strict: false).should eq(99) }\n    it { \"   99 red balloons\".to_i(strict: false).should eq(99) }\n    it { expect_raises(ArgumentError) { \"hello\".to_i } }\n    it { expect_raises(ArgumentError) { \"1__234\".to_i } }\n    it { expect_raises(ArgumentError) { \"1_234\".to_i } }\n    it { expect_raises(ArgumentError) { \"   1234   \".to_i(whitespace: false) } }\n    it { expect_raises(ArgumentError) { \"\".to_i(whitespace: false) } }\n    it { expect_raises(ArgumentError) { \"0x123\".to_i } }\n    it { expect_raises(ArgumentError) { \"0b123\".to_i } }\n    it { expect_raises(ArgumentError) { \"000b123\".to_i(prefix: true) } }\n    it { expect_raises(ArgumentError) { \"000x123\".to_i(prefix: true) } }\n    it { expect_raises(ArgumentError) { \"000o89a\".to_i(prefix: true) } }\n    it { expect_raises(ArgumentError) { \"123hello\".to_i } }\n    it { expect_raises(ArgumentError) { \"0\".to_i(leading_zero_is_octal: true) } }\n    it { expect_raises(ArgumentError) { \"0o755\".to_i(leading_zero_is_octal: true) } }\n    it { \"z\".to_i(36).should eq(35) }\n    it { \"Z\".to_i(36).should eq(35) }\n    it { \"0\".to_i(62).should eq(0) }\n    it { \"1\".to_i(62).should eq(1) }\n    it { \"a\".to_i(62).should eq(10) }\n    it { \"z\".to_i(62).should eq(35) }\n    it { \"A\".to_i(62).should eq(36) }\n    it { \"Z\".to_i(62).should eq(61) }\n    it { \"10\".to_i(62).should eq(62) }\n    it { \"1z\".to_i(62).should eq(97) }\n    it { \"ZZ\".to_i(62).should eq(3843) }\n\n    # Test for #11671\n    it { \"0_1\".to_i(underscore: true).should eq(1) }\n\n    describe \"to_i8\" do\n      it { \"127\".to_i8.should eq(127) }\n      it { \"-128\".to_i8.should eq(-128) }\n      it { expect_raises(ArgumentError) { \"128\".to_i8 } }\n      it { expect_raises(ArgumentError) { \"-129\".to_i8 } }\n\n      it { \"127\".to_i8?.should eq(127) }\n      it { \"128\".to_i8?.should be_nil }\n      it { \"128\".to_i8 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_i8 } }\n    end\n\n    describe \"to_u8\" do\n      it { \"255\".to_u8.should eq(255) }\n      it { \"0\".to_u8.should eq(0) }\n      it { expect_raises(ArgumentError) { \"256\".to_u8 } }\n      it { expect_raises(ArgumentError) { \"-1\".to_u8 } }\n\n      it { \"255\".to_u8?.should eq(255) }\n      it { \"256\".to_u8?.should be_nil }\n      it { \"256\".to_u8 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_u8 } }\n    end\n\n    describe \"to_i16\" do\n      it { \"32767\".to_i16.should eq(32767) }\n      it { \"-32768\".to_i16.should eq(-32768) }\n      it { expect_raises(ArgumentError) { \"32768\".to_i16 } }\n      it { expect_raises(ArgumentError) { \"-32769\".to_i16 } }\n\n      it { \"32767\".to_i16?.should eq(32767) }\n      it { \"32768\".to_i16?.should be_nil }\n      it { \"32768\".to_i16 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_i16 } }\n    end\n\n    describe \"to_u16\" do\n      it { \"65535\".to_u16.should eq(65535) }\n      it { \"0\".to_u16.should eq(0) }\n      it { expect_raises(ArgumentError) { \"65536\".to_u16 } }\n      it { expect_raises(ArgumentError) { \"-1\".to_u16 } }\n\n      it { \"65535\".to_u16?.should eq(65535) }\n      it { \"65536\".to_u16?.should be_nil }\n      it { \"65536\".to_u16 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_u16 } }\n    end\n\n    describe \"to_i32\" do\n      it { \"2147483647\".to_i32.should eq(2147483647) }\n      it { \"-2147483648\".to_i32.should eq(-2147483648) }\n      it { expect_raises(ArgumentError) { \"2147483648\".to_i32 } }\n      it { expect_raises(ArgumentError) { \"-2147483649\".to_i32 } }\n\n      it { \"2147483647\".to_i32?.should eq(2147483647) }\n      it { \"2147483648\".to_i32?.should be_nil }\n      it { \"2147483648\".to_i32 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_i32 } }\n    end\n\n    describe \"to_u32\" do\n      it { \"4294967295\".to_u32.should eq(4294967295) }\n      it { \"0\".to_u32.should eq(0) }\n      it { expect_raises(ArgumentError) { \"4294967296\".to_u32 } }\n      it { expect_raises(ArgumentError) { \"-1\".to_u32 } }\n\n      it { \"4294967295\".to_u32?.should eq(4294967295) }\n      it { \"4294967296\".to_u32?.should be_nil }\n      it { \"4294967296\".to_u32 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_u32 } }\n    end\n\n    describe \"to_i64\" do\n      it { \"9223372036854775807\".to_i64.should eq(9223372036854775807) }\n      it { \"-9223372036854775808\".to_i64.should eq(-9223372036854775808) }\n      it { expect_raises(ArgumentError) { \"9223372036854775808\".to_i64 } }\n      it { expect_raises(ArgumentError) { \"-9223372036854775809\".to_i64 } }\n\n      it { \"9223372036854775807\".to_i64?.should eq(9223372036854775807) }\n      it { \"9223372036854775808\".to_i64?.should be_nil }\n      it { \"9223372036854775808\".to_i64 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_i64 } }\n    end\n\n    describe \"to_u64\" do\n      it { \"18446744073709551615\".to_u64.should eq(18446744073709551615u64) }\n      it { \"0\".to_u64.should eq(0) }\n      it { expect_raises(ArgumentError) { \"18446744073709551616\".to_u64 } }\n      it { expect_raises(ArgumentError) { \"-1\".to_u64 } }\n\n      it { \"18446744073709551615\".to_u64?.should eq(18446744073709551615u64) }\n      it { \"18446744073709551616\".to_u64?.should be_nil }\n      it { \"18446744073709551616\".to_u64 { 0 }.should eq(0) }\n    end\n\n    describe \"to_i128\" do\n      it { \"170141183460469231731687303715884105727\".to_i128.should eq(Int128::MAX) }\n      it { \"-170141183460469231731687303715884105728\".to_i128.should eq(Int128::MIN) }\n      it { expect_raises(ArgumentError) { \"170141183460469231731687303715884105728\".to_i128 } }\n      it { expect_raises(ArgumentError) { \"-170141183460469231731687303715884105729\".to_i128 } }\n\n      it { \"170141183460469231731687303715884105727\".to_i128?.should eq(Int128::MAX) }\n      it { \"170141183460469231731687303715884105728\".to_i128?.should be_nil }\n      it { \"170141183460469231731687303715884105728\".to_i128 { 0 }.should eq(0) }\n\n      it { expect_raises(ArgumentError) { \"340282366920938463463374607431768211456\".to_i128 } }\n    end\n\n    describe \"to_u128\" do\n      it { \"340282366920938463463374607431768211455\".to_u128.should eq(UInt128::MAX) }\n      it { \"0\".to_u128.should eq(0) }\n      it { expect_raises(ArgumentError) { \"340282366920938463463374607431768211456\".to_u128 } }\n      it { expect_raises(ArgumentError) { \"-1\".to_u128 } }\n\n      it { \"340282366920938463463374607431768211455\".to_u128?.should eq(UInt128::MAX) }\n      it { \"340282366920938463463374607431768211456\".to_u128?.should be_nil }\n      it { \"340282366920938463463374607431768211456\".to_u128 { 0 }.should eq(0) }\n    end\n\n    it { \"1234\".to_i32.should eq(1234) }\n    it { \"1234123412341234\".to_i64.should eq(1234123412341234_i64) }\n    it { \"9223372036854775808\".to_u64.should eq(9223372036854775808_u64) }\n\n    it { expect_raises(ArgumentError, \"Invalid base 1\") { \"12ab\".to_i(1) } }\n    it { expect_raises(ArgumentError, \"Invalid base 37\") { \"12ab\".to_i(37) } }\n\n    it { expect_raises(ArgumentError, \"Invalid Int32\") { \"1Y2P0IJ32E8E7\".to_i(36) } }\n    it { \"1Y2P0IJ32E8E7\".to_i64(36).should eq(9223372036854775807) }\n  end\n\n  # more specs are available in `spec/manual/string_to_f_supplemental_spec.cr`\n  it \"does to_f\" do\n    expect_raises(ArgumentError) { \"\".to_f }\n    \"\".to_f?.should be_nil\n    expect_raises(ArgumentError) { \" \".to_f }\n    \" \".to_f?.should be_nil\n    \"0\".to_f.should eq(0_f64)\n    \"0.0\".to_f.should eq(0_f64)\n    \"+0.0\".to_f.should eq(0_f64)\n    \"-0.0\".to_f.should eq(0_f64)\n    \"1234.56\".to_f.should eq(1234.56_f64)\n    \"1234.56\".to_f?.should eq(1234.56_f64)\n    \"+1234.56\".to_f?.should eq(1234.56_f64)\n    \"-1234.56\".to_f?.should eq(-1234.56_f64)\n    expect_raises(ArgumentError) { \"foo\".to_f }\n    \"foo\".to_f?.should be_nil\n    \"  1234.56  \".to_f.should eq(1234.56_f64)\n    \"  1234.56  \".to_f?.should eq(1234.56_f64)\n    expect_raises(ArgumentError) { \"  1234.56  \".to_f(whitespace: false) }\n    \"  1234.56  \".to_f?(whitespace: false).should be_nil\n    expect_raises(ArgumentError) { \"  1234.56foo\".to_f }\n    \"  1234.56foo\".to_f?.should be_nil\n    \"\\u{A0}\\u{2028}\\u{2029}1234.56\\u{A0}\\u{2028}\\u{2029}\".to_f.should eq(1234.56_f64)\n    \"123.45 x\".to_f64(strict: false).should eq(123.45_f64)\n    expect_raises(ArgumentError) { \"x1.2\".to_f64 }\n    \"x1.2\".to_f64?.should be_nil\n    expect_raises(ArgumentError) { \"x1.2\".to_f64(strict: false) }\n    \"x1.2\".to_f64?(strict: false).should be_nil\n    \"1#{Float64::MAX}\".to_f?.should be_nil\n    \"-1#{Float64::MAX}\".to_f?.should be_nil\n    \" NaN\".to_f?.try(&.nan?).should be_true\n    \"NaN\".to_f?.try(&.nan?).should be_true\n    \"-NaN\".to_f?.try(&.nan?).should be_true\n    \"nan\".to_f?(whitespace: false).try(&.nan?).should be_true\n    \" nan\".to_f?(whitespace: false).should be_nil\n    \"nan \".to_f?(whitespace: false).should be_nil\n    expect_raises(ArgumentError) { \"\".to_f(whitespace: false) }\n    \"nani\".to_f?(strict: true).should be_nil\n    \" INF\".to_f?.should eq Float64::INFINITY\n    \"INF\".to_f?.should eq Float64::INFINITY\n    \"-INF\".to_f?.should eq -Float64::INFINITY\n    \" +INF\".to_f?.should eq Float64::INFINITY\n    \"inf\".to_f?(whitespace: false).should eq Float64::INFINITY\n    \"info\".to_f?(strict: true).should be_nil\n  end\n\n  it \"does to_f32\" do\n    expect_raises(ArgumentError) { \"\".to_f32 }\n    \"\".to_f32?.should be_nil\n    expect_raises(ArgumentError) { \" \".to_f32 }\n    \" \".to_f32?.should be_nil\n    \"0\".to_f32.should eq(0_f32)\n    \"0.0\".to_f32.should eq(0_f32)\n    \"+0.0\".to_f32.should eq(0_f32)\n    \"-0.0\".to_f32.should eq(0_f32)\n    \"1234.56\".to_f32.should eq(1234.56_f32)\n    \"1234.56\".to_f32?.should eq(1234.56_f32)\n    \"+1234.56\".to_f32?.should eq(1234.56_f32)\n    \"-1234.56\".to_f32?.should eq(-1234.56_f32)\n    expect_raises(ArgumentError) { \"foo\".to_f32 }\n    \"foo\".to_f32?.should be_nil\n    \"  1234.56  \".to_f32.should eq(1234.56_f32)\n    \"  1234.56  \".to_f32?.should eq(1234.56_f32)\n    expect_raises(ArgumentError) { \"  1234.56  \".to_f32(whitespace: false) }\n    \"  1234.56  \".to_f32?(whitespace: false).should be_nil\n    expect_raises(ArgumentError) { \"  1234.56foo\".to_f32 }\n    \"  1234.56foo\".to_f32?.should be_nil\n    \"\\u{A0}\\u{2028}\\u{2029}1234.56\\u{A0}\\u{2028}\\u{2029}\".to_f32.should eq(1234.56_f32)\n    \"123.45 x\".to_f32(strict: false).should eq(123.45_f32)\n    expect_raises(ArgumentError) { \"x1.2\".to_f32 }\n    \"x1.2\".to_f32?.should be_nil\n    expect_raises(ArgumentError) { \"x1.2\".to_f32(strict: false) }\n    \"x1.2\".to_f32?(strict: false).should be_nil\n    \"1#{Float32::MAX}\".to_f32?.should be_nil\n    \"-1#{Float32::MAX}\".to_f32?.should be_nil\n    \" NaN\".to_f32?.try(&.nan?).should be_true\n    \"NaN\".to_f32?.try(&.nan?).should be_true\n    \"-NaN\".to_f32?.try(&.nan?).should be_true\n    \"nan\".to_f32?(whitespace: false).try(&.nan?).should be_true\n    \" nan\".to_f32?(whitespace: false).should be_nil\n    \"nan \".to_f32?(whitespace: false).should be_nil\n    \"nani\".to_f32?(strict: true).should be_nil\n    \" INF\".to_f32?.should eq Float32::INFINITY\n    \"INF\".to_f32?.should eq Float32::INFINITY\n    \"-INF\".to_f32?.should eq -Float32::INFINITY\n    \" +INF\".to_f32?.should eq Float32::INFINITY\n    \"inf\".to_f32?(whitespace: false).should eq Float32::INFINITY\n    \"info\".to_f32?(strict: true).should be_nil\n  end\n\n  it \"does to_f64\" do\n    expect_raises(ArgumentError) { \"\".to_f64 }\n    \"\".to_f64?.should be_nil\n    expect_raises(ArgumentError) { \" \".to_f64 }\n    \" \".to_f64?.should be_nil\n    \"0\".to_f64.should eq(0_f64)\n    \"0.0\".to_f64.should eq(0_f64)\n    \"+0.0\".to_f64.should eq(0_f64)\n    \"-0.0\".to_f64.should eq(0_f64)\n    \"1234.56\".to_f64.should eq(1234.56_f64)\n    \"1234.56\".to_f64?.should eq(1234.56_f64)\n    \"+1234.56\".to_f?.should eq(1234.56_f64)\n    \"-1234.56\".to_f?.should eq(-1234.56_f64)\n    expect_raises(ArgumentError) { \"foo\".to_f64 }\n    \"foo\".to_f64?.should be_nil\n    \"  1234.56  \".to_f64.should eq(1234.56_f64)\n    \"  1234.56  \".to_f64?.should eq(1234.56_f64)\n    expect_raises(ArgumentError) { \"  1234.56  \".to_f64(whitespace: false) }\n    \"  1234.56  \".to_f64?(whitespace: false).should be_nil\n    expect_raises(ArgumentError) { \"  1234.56foo\".to_f64 }\n    \"  1234.56foo\".to_f64?.should be_nil\n    \"\\u{A0}\\u{2028}\\u{2029}1234.56\\u{A0}\\u{2028}\\u{2029}\".to_f64.should eq(1234.56_f64)\n    \"123.45 x\".to_f64(strict: false).should eq(123.45_f64)\n    expect_raises(ArgumentError) { \"x1.2\".to_f64 }\n    \"x1.2\".to_f64?.should be_nil\n    expect_raises(ArgumentError) { \"x1.2\".to_f64(strict: false) }\n    \"x1.2\".to_f64?(strict: false).should be_nil\n    \"1#{Float64::MAX}\".to_f64?.should be_nil\n    \"-1#{Float64::MAX}\".to_f64?.should be_nil\n    \" NaN\".to_f64?.try(&.nan?).should be_true\n    \"NaN\".to_f64?.try(&.nan?).should be_true\n    \"-NaN\".to_f64?.try(&.nan?).should be_true\n    \"nan\".to_f64?(whitespace: false).try(&.nan?).should be_true\n    \" nan\".to_f64?(whitespace: false).should be_nil\n    \"nan \".to_f64?(whitespace: false).should be_nil\n    \"nani\".to_f64?(strict: true).should be_nil\n    \" INF\".to_f64?.should eq Float64::INFINITY\n    \"INF\".to_f64?.should eq Float64::INFINITY\n    \"-INF\".to_f64?.should eq -Float64::INFINITY\n    \" +INF\".to_f64?.should eq Float64::INFINITY\n    \"inf\".to_f64?(whitespace: false).should eq Float64::INFINITY\n    \"info\".to_f64?(strict: true).should be_nil\n  end\n\n  it \"compares strings: different size\" do\n    \"foo\".should_not eq(\"fo\")\n  end\n\n  it \"compares strings: same object\" do\n    f = \"foo\"\n    f.should eq(f)\n  end\n\n  it \"compares strings: same size, same string\" do\n    \"foo\".should eq(\"fo\" + \"o\")\n  end\n\n  it \"compares strings: same size, different string\" do\n    \"foo\".should_not eq(\"bar\")\n  end\n\n  it \"interpolates string\" do\n    foo = \"<foo>\"\n    bar = 123\n    \"foo #{bar}\".should eq(\"foo 123\")\n    \"foo #{bar}\".should eq(\"foo 123\")\n    \"#{foo} bar\".should eq(\"<foo> bar\")\n  end\n\n  it \"multiplies\" do\n    str = \"foo\"\n    (str * 0).should eq(\"\")\n    (str * 3).should eq(\"foofoofoo\")\n  end\n\n  it \"multiplies with size one\" do\n    str = \"f\"\n    (str * 0).should eq(\"\")\n    (str * 10).should eq(\"ffffffffff\")\n  end\n\n  it \"multiplies with negative size\" do\n    expect_raises(ArgumentError, \"Negative argument\") do\n      \"f\" * -1\n    end\n  end\n\n  describe \"#downcase\" do\n    it { assert_prints \"HELLO!\".downcase, \"hello!\" }\n    it { assert_prints \"HELLO MAN!\".downcase, \"hello man!\" }\n    it { assert_prints \"ÁÉÍÓÚĀ\".downcase, \"áéíóúā\" }\n    it { assert_prints \"AEIİOU\".downcase(Unicode::CaseOptions::Turkic), \"aeıiou\" }\n    it { assert_prints \"ÁEÍOÚ\".downcase(Unicode::CaseOptions::ASCII), \"ÁeÍoÚ\" }\n    it { assert_prints \"İ\".downcase, \"i̇\" }\n    it { assert_prints \"Baﬄe\".downcase(Unicode::CaseOptions::Fold), \"baffle\" }\n    it { assert_prints \"ﬀ\".downcase(Unicode::CaseOptions::Fold), \"ff\" }\n    it { assert_prints \"tschüß\".downcase(Unicode::CaseOptions::Fold), \"tschüss\" }\n    it { assert_prints \"ΣίσυφοςﬁÆ\".downcase(Unicode::CaseOptions::Fold), \"σίσυφοσfiæ\" }\n    it { assert_prints \"ꭰ\".downcase(Unicode::CaseOptions::Fold), \"Ꭰ\" }\n    it { assert_prints \"Ꭰ\".downcase(Unicode::CaseOptions::Fold), \"Ꭰ\" }\n\n    it \"does not touch invalid code units in an otherwise ascii string\" do\n      \"\\xB5!\\xE0\\xC1\\xB5?\".downcase.should eq(\"\\xB5!\\xE0\\xC1\\xB5?\")\n    end\n  end\n\n  describe \"#upcase\" do\n    it { \"hello!\".upcase.should eq(\"HELLO!\") }\n    it { \"hello man!\".upcase.should eq(\"HELLO MAN!\") }\n    it { \"áéíóúā\".upcase.should eq(\"ÁÉÍÓÚĀ\") }\n    it { \"aeıiou\".upcase(Unicode::CaseOptions::Turkic).should eq(\"AEIİOU\") }\n    it { \"áeíoú\".upcase(Unicode::CaseOptions::ASCII).should eq(\"áEíOú\") }\n    it { \"aeiou\".upcase(Unicode::CaseOptions::Turkic).should eq(\"AEİOU\") }\n    it { \"baﬄe\".upcase.should eq(\"BAFFLE\") }\n    it { \"ﬀ\".upcase.should eq(\"FF\") }\n    it { \"ňž\".upcase.should eq(\"ŇŽ\") } # #7922\n\n    it \"does not touch invalid code units in an otherwise ascii string\" do\n      \"\\xB5!\\xE0\\xC1\\xB5?\".upcase.should eq(\"\\xB5!\\xE0\\xC1\\xB5?\")\n    end\n\n    describe \"with IO\" do\n      it { String.build { |io| \"hello!\".upcase io }.should eq \"HELLO!\" }\n      it { String.build { |io| \"hello man!\".upcase io }.should eq \"HELLO MAN!\" }\n      it { String.build { |io| \"áéíóúā\".upcase io }.should eq \"ÁÉÍÓÚĀ\" }\n      it { String.build { |io| \"aeıiou\".upcase io, Unicode::CaseOptions::Turkic }.should eq \"AEIİOU\" }\n      it { String.build { |io| \"áeíoú\".upcase io, Unicode::CaseOptions::ASCII }.should eq \"áEíOú\" }\n      it { String.build { |io| \"aeiou\".upcase io, Unicode::CaseOptions::Turkic }.should eq \"AEİOU\" }\n      it { String.build { |io| \"baﬄe\".upcase io }.should eq \"BAFFLE\" }\n      it { String.build { |io| \"ff\".upcase io }.should eq \"FF\" }\n      it { String.build { |io| \"ňž\".upcase io }.should eq \"ŇŽ\" }\n    end\n  end\n\n  describe \"#capitalize\" do\n    it { assert_prints \"HELLO!\".capitalize, \"Hello!\" }\n    it { assert_prints \"HELLO MAN!\".capitalize, \"Hello man!\" }\n    it { assert_prints \"\".capitalize, \"\" }\n    it { assert_prints \"iO\".capitalize(Unicode::CaseOptions::Turkic), \"İo\" }\n\n    it \"handles multi-character mappings correctly (#13533)\" do\n      assert_prints \"ﬄİ\".capitalize, \"Ffli̇\"\n    end\n\n    it \"does not touch invalid code units in an otherwise ascii string\" do\n      \"\\xB5!\\xE0\\xC1\\xB5?\".capitalize.should eq(\"\\xB5!\\xE0\\xC1\\xB5?\")\n      String.build { |io| \"\\xB5!\\xE0\\xC1\\xB5?\".capitalize(io) }.should eq(\"\\xB5!\\xE0\\xC1\\xB5?\".scrub)\n    end\n  end\n\n  describe \"#titleize\" do\n    it { assert_prints \"hEllO tAb\\tworld\".titleize, \"Hello Tab\\tWorld\" }\n    it { assert_prints \"  spaces before\".titleize, \"  Spaces Before\" }\n    it { assert_prints \"testa-se muito\".titleize, \"Testa-se Muito\" }\n    it { assert_prints \"hÉllÕ tAb\\tworld\".titleize, \"Héllõ Tab\\tWorld\" }\n    it { assert_prints \"  spáçes before\".titleize, \"  Spáçes Before\" }\n    it { assert_prints \"testá-se múitô\".titleize, \"Testá-se Múitô\" }\n    it { assert_prints \"iO iO\".titleize(Unicode::CaseOptions::Turkic), \"İo İo\" }\n    it { assert_prints \"foo_Bar\".titleize, \"Foo_bar\" }\n    it { assert_prints \"foo_bar\".titleize, \"Foo_bar\" }\n    it { assert_prints \"testá_se múitô\".titleize(underscore_to_space: true), \"Testá Se Múitô\" }\n    it { assert_prints \"foo_bar\".titleize(underscore_to_space: true), \"Foo Bar\" }\n\n    it \"handles multi-character mappings correctly (#13533)\" do\n      assert_prints \"ﬄİ İﬄ ǳ Ǳ\".titleize, \"Ffli̇ İﬄ ǲ ǲ\"\n    end\n\n    it \"does not touch invalid code units in an otherwise ascii string\" do\n      \"\\xB5!\\xE0\\xC1\\xB5?\".titleize.should eq(\"\\xB5!\\xE0\\xC1\\xB5?\")\n      \"a\\xA0b\".titleize.should eq(\"A\\xA0b\")\n      String.build { |io| \"\\xB5!\\xE0\\xC1\\xB5?\".titleize(io) }.should eq(\"\\xB5!\\xE0\\xC1\\xB5?\".scrub)\n      String.build { |io| \"a\\xA0b\".titleize(io) }.should eq(\"A\\xA0b\".scrub)\n    end\n\n    describe \"with IO\" do\n      it { String.build { |io| \"foo_Bar\".titleize io }.should eq \"Foo_bar\" }\n      it { String.build { |io| \"foo_bar\".titleize io }.should eq \"Foo_bar\" }\n      it { String.build { |io| \"foo_bar\".titleize(io, underscore_to_space: true) }.should eq \"Foo Bar\" }\n    end\n  end\n\n  describe \"chomp\" do\n    it { \"hello\\n\".chomp.should eq(\"hello\") }\n    it { \"hello\\r\".chomp.should eq(\"hello\") }\n    it { \"hello\\r\\n\".chomp.should eq(\"hello\") }\n    it { \"hello\".chomp.should eq(\"hello\") }\n    it { \"hello\".chomp.should eq(\"hello\") }\n    it { \"かたな\\n\".chomp.should eq(\"かたな\") }\n    it { \"かたな\\r\".chomp.should eq(\"かたな\") }\n    it { \"かたな\\r\\n\".chomp.should eq(\"かたな\") }\n    it { \"hello\\n\\n\".chomp.should eq(\"hello\\n\") }\n    it { \"hello\\r\\n\\n\".chomp.should eq(\"hello\\r\\n\") }\n    it { \"hello\\r\\n\".chomp('\\n').should eq(\"hello\") }\n\n    it { \"hello\".chomp('a').should eq(\"hello\") }\n    it { \"hello\".chomp('o').should eq(\"hell\") }\n    it { \"かたな\".chomp('な').should eq(\"かた\") }\n\n    it { \"hello\".chomp(\"good\").should eq(\"hello\") }\n    it { \"hello\".chomp(\"llo\").should eq(\"he\") }\n    it { \"かたな\".chomp(\"たな\").should eq(\"か\") }\n\n    it { \"hello\\n\\n\\n\\n\".chomp(\"\").should eq(\"hello\\n\\n\\n\\n\") }\n\n    it { \"hello\\r\\n\".chomp(\"\\n\").should eq(\"hello\") }\n\n    it \"pre-computes string size if possible\" do\n      {\"!hello!\", \"\\u{1f602}hello\\u{1f602}\", \"\\xFEhello\\xFF\"}.each do |str|\n        {\"\", \"\\n\", \"\\r\", \"\\r\\n\"}.each do |newline|\n          x = str + newline\n          x.size_known?.should be_true\n          y = x.chomp\n          y.@length.should eq(7)\n        end\n      end\n    end\n\n    it \"does not pre-compute string size if not possible\" do\n      x = String.build &.<< \"abc\\n\"\n      x.size_known?.should be_false\n      y = x.chomp\n      y.size_known?.should be_false\n    end\n  end\n\n  describe \"lchop\" do\n    it { \"\".lchop.should eq(\"\") }\n    it { \"h\".lchop.should eq(\"\") }\n    it { \"hello\".lchop.should eq(\"ello\") }\n    it { \"かたな\".lchop.should eq(\"たな\") }\n\n    it { \"hello\".lchop('g').should eq(\"hello\") }\n    it { \"hello\".lchop('h').should eq(\"ello\") }\n    it { \"かたな\".lchop('か').should eq(\"たな\") }\n\n    it { \"\".lchop(\"\").should eq(\"\") }\n    it { \"hello\".lchop(\"good\").should eq(\"hello\") }\n    it { \"hello\".lchop(\"hel\").should eq(\"lo\") }\n    it { \"かたな\".lchop(\"かた\").should eq(\"な\") }\n\n    it { \"\\n\\n\\n\\nhello\".lchop(\"\").should eq(\"\\n\\n\\n\\nhello\") }\n  end\n\n  describe \"lchop?\" do\n    it { \"\".lchop?.should be_nil }\n    it { \"h\".lchop?.should eq(\"\") }\n    it { \"hello\".lchop?.should eq(\"ello\") }\n    it { \"かたな\".lchop?.should eq(\"たな\") }\n\n    it { \"hello\".lchop?('g').should be_nil }\n    it { \"hello\".lchop?('h').should eq(\"ello\") }\n    it { \"かたな\".lchop?('か').should eq(\"たな\") }\n\n    it { \"\".lchop?(\"\").should eq(\"\") }\n    it { \"hello\".lchop?(\"good\").should be_nil }\n    it { \"hello\".lchop?(\"hel\").should eq(\"lo\") }\n    it { \"かたな\".lchop?(\"かた\").should eq(\"な\") }\n\n    it { \"\\n\\n\\n\\nhello\".lchop(\"\").should eq(\"\\n\\n\\n\\nhello\") }\n  end\n\n  describe \"rchop\" do\n    it { \"\".rchop.should eq(\"\") }\n    it { \"foo\".rchop.should eq(\"fo\") }\n    it { \"foo\\n\".rchop.should eq(\"foo\") }\n    it { \"foo\\r\".rchop.should eq(\"foo\") }\n    it { \"foo\\r\\n\".rchop.should eq(\"foo\\r\") }\n    it { \"\\r\\n\".rchop.should eq(\"\\r\") }\n    it { \"かたな\".rchop.should eq(\"かた\") }\n    it { \"かたな\\n\".rchop.should eq(\"かたな\") }\n    it { \"かたな\\r\\n\".rchop.should eq(\"かたな\\r\") }\n\n    it { \"foo\".rchop('o').should eq(\"fo\") }\n    it { \"foo\".rchop('x').should eq(\"foo\") }\n\n    it { \"\".rchop(\"\").should eq(\"\") }\n    it { \"foobar\".rchop(\"bar\").should eq(\"foo\") }\n    it { \"foobar\".rchop(\"baz\").should eq(\"foobar\") }\n  end\n\n  describe \"rchop?\" do\n    it { \"\".rchop?.should be_nil }\n    it { \"\\n\".rchop?.should eq(\"\") }\n    it { \"foo\".rchop?.should eq(\"fo\") }\n    it { \"foo\\n\".rchop?.should eq(\"foo\") }\n    it { \"foo\\r\".rchop?.should eq(\"foo\") }\n    it { \"foo\\r\\n\".rchop?.should eq(\"foo\\r\") }\n    it { \"\\r\\n\".rchop?.should eq(\"\\r\") }\n    it { \"かたな\".rchop?.should eq(\"かた\") }\n    it { \"かたな\\n\".rchop?.should eq(\"かたな\") }\n    it { \"かたな\\r\\n\".rchop?.should eq(\"かたな\\r\") }\n\n    it { \"foo\".rchop?('o').should eq(\"fo\") }\n    it { \"foo\".rchop?('x').should be_nil }\n\n    it { \"\".rchop?(\"\").should eq(\"\") }\n    it { \"foobar\".rchop?(\"bar\").should eq(\"foo\") }\n    it { \"foobar\".rchop?(\"baz\").should be_nil }\n  end\n\n  describe \"strip\" do\n    it { \"  hello  \\n\\t\\f\\v\\r\".strip.should eq(\"hello\") }\n    it { \"hello\".strip.should eq(\"hello\") }\n    it { \"かたな \\n\\f\\v\".strip.should eq(\"かたな\") }\n    it { \"  \\n\\t かたな \\n\\f\\v\".strip.should eq(\"かたな\") }\n    it { \"  \\n\\t かたな\".strip.should eq(\"かたな\") }\n    it { \"かたな\".strip.should eq(\"かたな\") }\n    it { \"\".strip.should eq(\"\") }\n    it { \"\\n\".strip.should eq(\"\") }\n    it { \"\\n\\t  \".strip.should eq(\"\") }\n    it { \"\\u00A0\".strip.should eq(\"\") }\n\n    it(tags: %w[slow]) { (\" \" * 167772160).strip.should eq(\"\") }\n\n    it { \"\".strip(\"xyz\").should eq(\"\") }\n    it { \"foobar\".strip(\"\").should eq(\"foobar\") }\n    it { \"rrfoobarr\".strip(\"r\").should eq(\"fooba\") }\n    it { \"rfoobar\".strip(\"x\").should eq(\"rfoobar\") }\n    it { \"rrrfooba\".strip(\"r\").should eq(\"fooba\") }\n    it { \"foobarrr\".strip(\"r\").should eq(\"fooba\") }\n    it { \"rabfooabr\".strip(\"bar\").should eq(\"foo\") }\n    it { \"rabfooabr\".strip(\"xyz\").should eq(\"rabfooabr\") }\n    it { \"fooabr\".strip(\"bar\").should eq(\"foo\") }\n    it { \"rabfoo\".strip(\"bar\").should eq(\"foo\") }\n    it { \"rababr\".strip(\"bar\").should eq(\"\") }\n\n    it { \"aaabcdaaa\".strip('a').should eq(\"bcd\") }\n    it { \"bcdaaa\".strip('a').should eq(\"bcd\") }\n    it { \"aaabcd\".strip('a').should eq(\"bcd\") }\n\n    it { \"ababcdaba\".strip(&.in?('a', 'b')).should eq(\"cd\") }\n  end\n\n  describe \"rstrip\" do\n    it { \"\".rstrip.should eq(\"\") }\n    it { \"  hello  \".rstrip.should eq(\"  hello\") }\n    it { \"hello\".rstrip.should eq(\"hello\") }\n    it { \"  かたな \\n\\f\\v\".rstrip.should eq(\"  かたな\") }\n    it { \"かたな\".rstrip.should eq(\"かたな\") }\n\n    it { \"\".rstrip(\"xyz\").should eq(\"\") }\n    it { \"foobar\".rstrip(\"\").should eq(\"foobar\") }\n    it { \"foobarrrr\".rstrip(\"r\").should eq(\"fooba\") }\n    it { \"foobars\".rstrip(\"r\").should eq(\"foobars\") }\n    it { \"foobar\".rstrip(\"rab\").should eq(\"foo\") }\n    it { \"foobar\".rstrip(\"foo\").should eq(\"foobar\") }\n    it { \"bararbr\".rstrip(\"bar\").should eq(\"\") }\n\n    it { \"foobarrrr\".rstrip('r').should eq(\"fooba\") }\n    it { \"foobar\".rstrip('x').should eq(\"foobar\") }\n\n    it { \"foobar\".rstrip(&.in?('a', 'r')).should eq(\"foob\") }\n\n    it \"does not touch invalid code units in an otherwise ascii string\" do\n      \" \\xA0 \".rstrip.should eq(\" \\xA0\")\n    end\n  end\n\n  describe \"lstrip\" do\n    it { \"  hello  \".lstrip.should eq(\"hello  \") }\n    it { \"hello\".lstrip.should eq(\"hello\") }\n    it { \"  \\n\\v かたな  \".lstrip.should eq(\"かたな  \") }\n    it { \"  かたな\".lstrip.should eq(\"かたな\") }\n\n    it { \"\".lstrip(\"xyz\").should eq(\"\") }\n    it { \"barfoo\".lstrip(\"\").should eq(\"barfoo\") }\n    it { \"bbbarfoo\".lstrip(\"b\").should eq(\"arfoo\") }\n    it { \"sbarfoo\".lstrip(\"r\").should eq(\"sbarfoo\") }\n    it { \"barfoo\".lstrip(\"rab\").should eq(\"foo\") }\n    it { \"barfoo\".lstrip(\"foo\").should eq(\"barfoo\") }\n    it { \"b\".lstrip(\"bar\").should eq(\"\") }\n\n    it { \"bbbbarfoo\".lstrip('b').should eq(\"arfoo\") }\n    it { \"barfoo\".lstrip('x').should eq(\"barfoo\") }\n\n    it { \"barfoo\".lstrip(&.in?('a', 'b')).should eq(\"rfoo\") }\n\n    it \"does not touch invalid code units in an otherwise ascii string\" do\n      \" \\xA0 \".lstrip.should eq(\"\\xA0 \")\n    end\n  end\n\n  describe \"empty?\" do\n    it { \"a\".empty?.should be_false }\n    it { \"\".empty?.should be_true }\n  end\n\n  describe \"blank?\" do\n    it { \" \\t\\n\".blank?.should be_true }\n    it { \"\\u{1680}\\u{2029}\".blank?.should be_true }\n    it { \"hello\".blank?.should be_false }\n  end\n\n  describe \"presence\" do\n    it { \" \\t\\n\".presence.should be_nil }\n    it { \"\\u{1680}\\u{2029}\".presence.should be_nil }\n    it { \"hello\".presence.should eq(\"hello\") }\n  end\n\n  describe \"#index\" do\n    describe \"by char\" do\n      it { \"foo\".index('o').should eq(1) }\n      it { \"foo\".index('g').should be_nil }\n      it { \"bar\".index('r').should eq(2) }\n      it { \"日本語\".index('本').should eq(1) }\n      it { \"bar\".index('あ').should be_nil }\n      it { \"あいう_えお\".index('_').should eq(3) }\n      it { \"xyz\\xFFxyz\".index('\\u{FFFD}').should eq(3) }\n      it { \"日\\xFF語\".index('\\u{FFFD}').should eq(1) }\n\n      describe \"with offset\" do\n        it { \"foobarbaz\".index('a', 5).should eq(7) }\n        it { \"foobarbaz\".index('a', -4).should eq(7) }\n        it { \"foo\".index('g', 1).should be_nil }\n        it { \"foo\".index('g', -20).should be_nil }\n        it { \"日本語日本語\".index('本', 2).should eq(4) }\n        it { \"xyz\\xFFxyz\".index('\\u{FFFD}', 2).should eq(3) }\n        it { \"xyz\\xFFxyz\".index('\\u{FFFD}', 4).should be_nil }\n        it { \"日本\\xFF語\".index('\\u{FFFD}', 2).should eq(2) }\n        it { \"日本\\xFF語\".index('\\u{FFFD}', 3).should be_nil }\n\n        # Check offset type\n        it { \"foobarbaz\".index('a', 5_i64).should eq(7) }\n        it { \"foobarbaz\".index('a', 5_i64).should be_a(Int32) }\n        it { \"日本語日本語\".index('本', 2_i64).should eq(4) }\n        it { \"日本語日本語\".index('本', 2_i64).should be_a(Int32) }\n      end\n    end\n\n    describe \"by string\" do\n      it { \"foo bar\".index(\"o b\").should eq(2) }\n      it { \"foo\".index(\"fg\").should be_nil }\n      it { \"foo\".index(\"\").should eq(0) }\n      it { \"foo\".index(\"foo\").should eq(0) }\n      it { \"日本語日本語\".index(\"本語\").should eq(1) }\n      it { \"\\xFF\\xFFcrystal\".index(\"crystal\").should eq(2) }\n      it { \"\\xFD\\x9A\\xAD\\x50NG\".index(\"PNG\").should eq(3) }\n      it { \"🧲$\".index(\"✅\").should be_nil } # #11745\n\n      describe \"with offset\" do\n        it { \"foobarbaz\".index(\"ba\", 4).should eq(6) }\n        it { \"foobarbaz\".index(\"ba\", -5).should eq(6) }\n        it { \"foo\".index(\"ba\", 1).should be_nil }\n        it { \"foo\".index(\"ba\", -20).should be_nil }\n        it { \"foo\".index(\"\", 3).should eq(3) }\n        it { \"foo\".index(\"\", 4).should be_nil }\n        it { \"日本語日本語\".index(\"本語\", 2).should eq(4) }\n        it { \"\\xFD\\x9A\\xAD\\x50NG\".index(\"PNG\", 2).should eq(3) }\n        it { \"\\xFD\\x9A\\xAD\\x50NG\".index(\"PNG\", 4).should be_nil }\n\n        # Check offset type\n        it { \"foobarbaz\".index(\"a\", 5_i64).should eq(7) }\n        it { \"foobarbaz\".index(\"a\", 5_i64).should be_a(Int32) }\n        it { \"日本語日本語\".index(\"本\", 2_i64).should eq(4) }\n        it { \"日本語日本語\".index(\"本\", 2_i64).should be_a(Int32) }\n        it { \"日本語日本語\".index(\"\", 2_i64).should eq 2 }\n        it { \"日本語日本語\".index(\"\", 2_i64).should be_a(Int64) }\n      end\n    end\n\n    describe \"by regex\" do\n      it { \"string 12345\".index(/\\d+/).should eq(7) }\n      it { \"12345\".index(/\\d/).should eq(0) }\n      it { \"Hello\\xFF\".index(/l/, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(2) }\n      it { \"Hello, world!\".index(/\\d/).should be_nil }\n      it { \"abcdef\".index(/[def]/).should eq(3) }\n      it { \"日本語日本語\".index(/本語/).should eq(1) }\n\n      describe \"with offset\" do\n        it { \"abcDef\".index(/[A-Z]/).should eq(3) }\n        it { \"foobarbaz\".index(/ba/, -5).should eq(6) }\n        it { \"Hello\\xFF\".index(/l/, 3, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(3) }\n        it { \"Foo\".index(/[A-Z]/, 1).should be_nil }\n        it { \"foo\".index(/o/, 2).should eq(2) }\n        it { \"foo\".index(//, 3).should eq(3) }\n        it { \"foo\".index(//, 4).should be_nil }\n        it { \"日本語日本語\".index(/本語/, 2).should eq(4) }\n      end\n    end\n  end\n\n  describe \"#index!\" do\n    describe \"by char\" do\n      it { \"foo\".index!('o').should eq(1) }\n      it do\n        expect_raises(Enumerable::NotFoundError) do\n          \"foo\".index!('g')\n        end\n      end\n\n      describe \"with offset\" do\n        it { \"foobarbaz\".index!('a', 5).should eq(7) }\n        it { \"foobarbaz\".index!('a', -4).should eq(7) }\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"foo\".index!('f', 1)\n          end\n        end\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"foo\".index!('g', -20)\n          end\n        end\n      end\n    end\n\n    describe \"by string\" do\n      it { \"foo bar\".index!(\"o b\").should eq(2) }\n      it { \"foo\".index!(\"\").should eq(0) }\n      it { \"foo\".index!(\"foo\").should eq(0) }\n      it do\n        expect_raises(Enumerable::NotFoundError) do\n          \"foo\".index!(\"fg\")\n        end\n      end\n\n      describe \"with offset\" do\n        it { \"foobarbaz\".index!(\"ba\", 4).should eq(6) }\n        it { \"foobarbaz\".index!(\"ba\", -5).should eq(6) }\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"foo\".index!(\"ba\", 1)\n          end\n        end\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"foo\".index!(\"ba\", -20)\n          end\n        end\n      end\n    end\n\n    describe \"by regex\" do\n      it { \"string 12345\".index!(/\\d+/).should eq(7) }\n      it { \"12345\".index!(/\\d/).should eq(0) }\n      it { \"Hello\\xFF\".index!(/l/, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(2) }\n      it do\n        expect_raises(Enumerable::NotFoundError) do\n          \"Hello, world!\".index!(/\\d/)\n        end\n      end\n\n      describe \"with offset\" do\n        it { \"abcDef\".index!(/[A-Z]/).should eq(3) }\n        it { \"foobarbaz\".index!(/ba/, -5).should eq(6) }\n        it { \"Hello\\xFF\".index!(/l/, 3, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(3) }\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"Foo\".index!(/[A-Z]/, 1)\n          end\n        end\n      end\n    end\n  end\n\n  describe \"#rindex\" do\n    describe \"by char\" do\n      it { \"bbbb\".rindex('b').should eq(3) }\n      it { \"foobar\".rindex('a').should eq(4) }\n      it { \"foobar\".rindex('g').should be_nil }\n      it { \"日本語日本語\".rindex('本').should eq(4) }\n      it { \"あいう_えお\".rindex('_').should eq(3) }\n      it { \"xyz\\xFFxyz\".rindex('\\u{FFFD}').should eq(3) }\n      it { \"日\\xFF語\".rindex('\\u{FFFD}').should eq(1) }\n\n      describe \"with offset\" do\n        it { \"bbbb\".rindex('b', 2).should eq(2) }\n        it { \"abbbb\".rindex('b', 0).should be_nil }\n        it { \"abbbb\".rindex('b', 1).should eq(1) }\n        it { \"abbbb\".rindex('a', 0).should eq(0) }\n        it { \"bbbb\".rindex('b', -2).should eq(2) }\n        it { \"bbbb\".rindex('b', -5).should be_nil }\n        it { \"bbbb\".rindex('b', -4).should eq(0) }\n        it { \"faobar\".rindex('a', 3).should eq(1) }\n        it { \"faobarbaz\".rindex('a', -3).should eq(4) }\n        it { \"日本語日本語\".rindex('本', 3).should eq(1) }\n        it { \"xyz\\xFFxyz\".rindex('\\u{FFFD}', 4).should eq(3) }\n        it { \"xyz\\xFFxyz\".rindex('\\u{FFFD}', 2).should be_nil }\n        it { \"日本\\xFF語\".rindex('\\u{FFFD}', 2).should eq(2) }\n        it { \"日本\\xFF語\".rindex('\\u{FFFD}', 1).should be_nil }\n\n        # Check offset type\n        it { \"bbbb\".rindex('b', 2_i64).should eq(2) }\n        it { \"bbbb\".rindex('b', 2_i64).should be_a(Int64) }\n        it { \"日本語日本語\".rindex('本', 3_i64).should eq(1) }\n        it { \"日本語日本語\".rindex('本', 3_i64).should be_a(Int64) }\n      end\n    end\n\n    describe \"by string\" do\n      it { \"bbbb\".rindex(\"b\").should eq(3) }\n      it { \"foo baro baz\".rindex(\"o b\").should eq(7) }\n      it { \"foo baro baz\".rindex(\"fg\").should be_nil }\n      it { \"日本語日本語\".rindex(\"日本\").should eq(3) }\n\n      describe \"with offset\" do\n        it { \"bbbb\".rindex(\"b\", 2).should eq(2) }\n        it { \"abbbb\".rindex(\"b\", 0).should be_nil }\n        it { \"abbbb\".rindex(\"b\", 1).should eq(1) }\n        it { \"abbbb\".rindex(\"a\", 0).should eq(0) }\n        it { \"bbbb\".rindex(\"b\", -2).should eq(2) }\n        it { \"bbbb\".rindex(\"b\", -5).should be_nil }\n        it { \"bbbb\".rindex(\"b\", -4).should eq(0) }\n        it { \"foo baro baz\".rindex(\"o b\", 6).should eq(2) }\n        it { \"foo\".rindex(\"\", 3).should eq(3) }\n        it { \"foo\".rindex(\"\", 4).should eq(3) }\n        it { \"日本語日本語\".rindex(\"日本\", 2).should eq(0) }\n\n        # Check offset type\n        it { \"bbbb\".rindex(\"b\", 2_i64).should eq(2) }\n        it { \"bbbb\".rindex(\"b\", 2_i64).should be_a(Int32) }\n        it { \"日本語日本語\".rindex(\"本\", 3_i64).should eq(1) }\n        it { \"日本語日本語\".rindex(\"本\", 3_i64).should be_a(Int32) }\n        it { \"日本語日本語\".rindex(\"\", 3_i64).should eq(3) }\n        it { \"日本語日本語\".rindex(\"\", 3_i64).should be_a(Int32) }\n      end\n    end\n\n    describe \"by regex\" do\n      it { \"bbbb\".rindex(/b/).should eq(3) }\n      it { \"\\xFFbbb\".rindex(/b/, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(3) }\n      it { \"a43b53\".rindex(/\\d+/).should eq(4) }\n      it { \"bbbb\".rindex(/\\d/).should be_nil }\n\n      describe \"which matches empty string\" do\n        it { \"foo\".rindex(/o*/).should eq(3) }\n        it { \"foo\".rindex(//).should eq(3) }\n        it { \"foo\".rindex(/\\b/).should eq(3) }\n      end\n\n      describe \"with offset\" do\n        it { \"bbbb\".rindex(/b/, 2).should eq(2) }\n        it { \"\\xFFbbb\".rindex(/b/, 2, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(2) }\n        it { \"abbbb\".rindex(/b/, 0).should be_nil }\n        it { \"abbbb\".rindex(/a/, 0).should eq(0) }\n        it { \"bbbb\".rindex(/b/, -2).should eq(2) }\n        it { \"bbbb\".rindex(/b/, -5).should be_nil }\n        it { \"bbbb\".rindex(/b/, -4).should eq(0) }\n        it { \"日本語日本語\".rindex(/日本/, 2).should eq(0) }\n      end\n    end\n  end\n\n  describe \"#rindex!\" do\n    describe \"by char\" do\n      it { \"bbbb\".rindex!('b').should eq(3) }\n      it { \"foobar\".rindex!('a').should eq(4) }\n      it do\n        expect_raises(Enumerable::NotFoundError) do\n          \"foobar\".rindex!('g')\n        end\n      end\n\n      describe \"with offset\" do\n        it { \"bbbb\".rindex!('b', 2).should eq(2) }\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"abbbb\".rindex!('b', 0)\n          end\n        end\n      end\n    end\n\n    describe \"by string\" do\n      it { \"bbbb\".rindex!(\"b\").should eq(3) }\n      it { \"foo baro baz\".rindex!(\"o b\").should eq(7) }\n      it do\n        expect_raises(Enumerable::NotFoundError) do\n          \"foo baro baz\".rindex!(\"fg\")\n        end\n      end\n\n      describe \"with offset\" do\n        it { \"bbbb\".rindex!(\"b\", 2).should eq(2) }\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"abbbb\".rindex!(\"b\", 0)\n          end\n        end\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"bbbb\".rindex!(\"b\", -5)\n          end\n        end\n      end\n    end\n\n    describe \"by regex\" do\n      it { \"bbbb\".rindex!(/b/).should eq(3) }\n      it { \"\\xFFbbb\".rindex!(/b/, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(3) }\n      it { \"a43b53\".rindex!(/\\d+/).should eq(4) }\n      it do\n        expect_raises(Enumerable::NotFoundError) do\n          \"bbbb\".rindex!(/\\d/)\n        end\n      end\n\n      describe \"with offset\" do\n        it { \"bbbb\".rindex!(/b/, 2).should eq(2) }\n        it { \"\\xFFbbb\".rindex!(/b/, 2, options: Regex::MatchOptions::NO_UTF_CHECK).should eq(2) }\n        it do\n          expect_raises(Enumerable::NotFoundError) do\n            \"abbbb\".rindex!(/b/, 0)\n          end\n        end\n      end\n    end\n  end\n\n  describe \"partition\" do\n    describe \"by char\" do\n      it { \"hello\".partition('h').should eq({\"\", \"h\", \"ello\"}) }\n      it { \"hello\".partition('o').should eq({\"hell\", \"o\", \"\"}) }\n      it { \"hello\".partition('l').should eq({\"he\", \"l\", \"lo\"}) }\n      it { \"hello\".partition('x').should eq({\"hello\", \"\", \"\"}) }\n    end\n\n    describe \"by string\" do\n      it { \"hello\".partition(\"h\").should eq({\"\", \"h\", \"ello\"}) }\n      it { \"hello\".partition(\"o\").should eq({\"hell\", \"o\", \"\"}) }\n      it { \"hello\".partition(\"l\").should eq({\"he\", \"l\", \"lo\"}) }\n      it { \"hello\".partition(\"ll\").should eq({\"he\", \"ll\", \"o\"}) }\n      it { \"hello\".partition(\"x\").should eq({\"hello\", \"\", \"\"}) }\n    end\n\n    describe \"by regex\" do\n      it { \"hello\".partition(/h/).should eq({\"\", \"h\", \"ello\"}) }\n      it { \"hello\".partition(/o/).should eq({\"hell\", \"o\", \"\"}) }\n      it { \"hello\".partition(/l/).should eq({\"he\", \"l\", \"lo\"}) }\n      it { \"hello\".partition(/ll/).should eq({\"he\", \"ll\", \"o\"}) }\n      it { \"hello\".partition(/.l/).should eq({\"h\", \"el\", \"lo\"}) }\n      it { \"hello\".partition(/.h/).should eq({\"hello\", \"\", \"\"}) }\n      it { \"hello\".partition(/h./).should eq({\"\", \"he\", \"llo\"}) }\n      it { \"hello\".partition(/o./).should eq({\"hello\", \"\", \"\"}) }\n      it { \"hello\".partition(/.o/).should eq({\"hel\", \"lo\", \"\"}) }\n      it { \"hello\".partition(/x/).should eq({\"hello\", \"\", \"\"}) }\n    end\n  end\n\n  describe \"rpartition\" do\n    describe \"by char\" do\n      it { \"hello\".rpartition('l').should eq({\"hel\", \"l\", \"o\"}) }\n      it { \"hello\".rpartition('o').should eq({\"hell\", \"o\", \"\"}) }\n      it { \"hello\".rpartition('h').should eq({\"\", \"h\", \"ello\"}) }\n    end\n\n    describe \"by string\" do\n      it { \"hello\".rpartition(\"l\").should eq({\"hel\", \"l\", \"o\"}) }\n      it { \"hello\".rpartition(\"x\").should eq({\"\", \"\", \"hello\"}) }\n      it { \"hello\".rpartition(\"o\").should eq({\"hell\", \"o\", \"\"}) }\n      it { \"hello\".rpartition(\"h\").should eq({\"\", \"h\", \"ello\"}) }\n      it { \"hello\".rpartition(\"ll\").should eq({\"he\", \"ll\", \"o\"}) }\n      it { \"hello\".rpartition(\"lo\").should eq({\"hel\", \"lo\", \"\"}) }\n      it { \"hello\".rpartition(\"he\").should eq({\"\", \"he\", \"llo\"}) }\n    end\n\n    describe \"by regex\" do\n      it { \"hello\".rpartition(/.l/).should eq({\"he\", \"ll\", \"o\"}) }\n      it { \"hello\".rpartition(/ll/).should eq({\"he\", \"ll\", \"o\"}) }\n      it { \"hello\".rpartition(/.o/).should eq({\"hel\", \"lo\", \"\"}) }\n      it { \"hello\".rpartition(/.e/).should eq({\"\", \"he\", \"llo\"}) }\n      it { \"hello\".rpartition(/l./).should eq({\"hel\", \"lo\", \"\"}) }\n    end\n  end\n\n  describe \"byte_index\" do\n    it { \"foo\".byte_index('o'.ord).should eq(1) }\n    it { \"foo bar booz\".byte_index('o'.ord, 3).should eq(9) }\n    it { \"foo\".byte_index('a'.ord).should be_nil }\n    it { \"foo\".byte_index('a'.ord).should be_nil }\n    it { \"foo\".byte_index('o'.ord, 3).should be_nil }\n    it {\n      \"Dizzy Miss Lizzy\".byte_index('z'.ord).should eq(2)\n      \"Dizzy Miss Lizzy\".byte_index('z'.ord, 3).should eq(3)\n      \"Dizzy Miss Lizzy\".byte_index('z'.ord, -4).should eq(13)\n      \"Dizzy Miss Lizzy\".byte_index('z'.ord, -17).should be_nil\n    }\n\n    it { \"foo\".byte_index('o').should eq(1) }\n    it { \"foo bar booz\".byte_index('o', 3).should eq(9) }\n    it { \"foo\".byte_index('a').should be_nil }\n    it { \"foo\".byte_index('a').should be_nil }\n    it { \"foo\".byte_index('o', 3).should be_nil }\n    it { \"Hi, 💣\".byte_index('💣').should eq(4) }\n    it {\n      \"Dizzy Miss Lizzy\".byte_index('z').should eq(2)\n      \"Dizzy Miss Lizzy\".byte_index('z', 3).should eq(3)\n      \"Dizzy Miss Lizzy\".byte_index('z', -4).should eq(13)\n      \"Dizzy Miss Lizzy\".byte_index('z', -17).should be_nil\n    }\n\n    it \"gets byte index of string\" do\n      \"hello world\".byte_index(\"he\").should eq(0)\n      \"hello world\".byte_index(\"lo\").should eq(3)\n      \"hello world\".byte_index(\"world\", 7).should be_nil\n      \"foo foo\".byte_index(\"oo\").should eq(1)\n      \"foo foo\".byte_index(\"oo\", 2).should eq(5)\n      \"こんにちは世界\".byte_index(\"ちは\").should eq(9)\n    end\n\n    it \"gets byte index of regex\" do\n      str = \"0123x\"\n      pattern = /x/\n\n      str.byte_index(pattern).should eq(4)\n      str.byte_index(pattern, offset: 4).should eq(4)\n      str.byte_index(pattern, offset: 5).should be_nil\n      str.byte_index(pattern, offset: -1).should eq(4)\n      str.byte_index(/y/).should be_nil\n\n      str = \"012abc678\"\n      pattern = /[abc]/\n\n      str.byte_index(pattern).should eq(3)\n      str.byte_index(pattern, offset: 2).should eq(3)\n      str.byte_index(pattern, offset: 5).should eq(5)\n      str.byte_index(pattern, offset: -4).should eq(5)\n      str.byte_index(pattern, offset: -1).should be_nil\n      str.byte_index(/y/).should be_nil\n    end\n  end\n\n  describe \"includes?\" do\n    describe \"by char\" do\n      it { \"foo\".includes?('o').should be_true }\n      it { \"foo\".includes?('g').should be_false }\n    end\n\n    describe \"by string\" do\n      it { \"foo bar\".includes?(\"o b\").should be_true }\n      it { \"foo\".includes?(\"fg\").should be_false }\n      it { \"foo\".includes?(\"\").should be_true }\n    end\n  end\n\n  describe \"split\" do\n    describe \"by whitespace\" do\n      it { \"   foo   bar\\n\\t  baz   \".split.should eq([\"foo\", \"bar\", \"baz\"]) }\n      it { \"   foo   bar\\n\\t  baz   \".split(1).should eq([\"   foo   bar\\n\\t  baz   \"]) }\n      it { \"   foo   bar\\n\\t  baz   \".split(2).should eq([\"foo\", \"bar\\n\\t  baz   \"]) }\n      it { \"日本語 \\n\\t 日本 \\n\\n 語\".split.should eq([\"日本語\", \"日本\", \"語\"]) }\n\n      it { \" foo\\u00A0bar baz\".split.should eq([\"foo\", \"bar\", \"baz\"]) }\n    end\n\n    describe \"by char\" do\n      it { \"\".split(',').should eq([\"\"]) }\n      it { \"\".split(',', remove_empty: true).should eq([] of String) }\n      it { \"foo,bar,,baz,\".split(',').should eq([\"foo\", \"bar\", \"\", \"baz\", \"\"]) }\n      it { \"foo,bar,,baz,\".split(',', remove_empty: true).should eq([\"foo\", \"bar\", \"baz\"]) }\n      it { \"foo,bar,,baz\".split(',').should eq([\"foo\", \"bar\", \"\", \"baz\"]) }\n      it { \"foo,bar,,baz\".split(',', remove_empty: true).should eq([\"foo\", \"bar\", \"baz\"]) }\n      it { \"foo\".split(',').should eq([\"foo\"]) }\n      it { \"foo\".split(' ').should eq([\"foo\"]) }\n      it { \"   foo\".split(' ').should eq([\"\", \"\", \"\", \"foo\"]) }\n      it { \"foo   \".split(' ').should eq([\"foo\", \"\", \"\", \"\"]) }\n      it { \"   foo  bar\".split(' ').should eq([\"\", \"\", \"\", \"foo\", \"\", \"bar\"]) }\n      it { \"   foo   bar\\n\\t  baz   \".split(' ').should eq([\"\", \"\", \"\", \"foo\", \"\", \"\", \"bar\\n\\t\", \"\", \"baz\", \"\", \"\", \"\"]) }\n      it { \"   foo   bar\\n\\t  baz   \".split(' ').should eq([\"\", \"\", \"\", \"foo\", \"\", \"\", \"bar\\n\\t\", \"\", \"baz\", \"\", \"\", \"\"]) }\n      it { \"foo,bar,baz,qux\".split(',', 1).should eq([\"foo,bar,baz,qux\"]) }\n      it { \"foo,bar,baz,qux\".split(',', 3).should eq([\"foo\", \"bar\", \"baz,qux\"]) }\n      it { \"foo,bar,baz,qux\".split(',', 30).should eq([\"foo\", \"bar\", \"baz\", \"qux\"]) }\n      it { \"foo bar baz qux\".split(' ', 1).should eq([\"foo bar baz qux\"]) }\n      it { \"foo bar baz qux\".split(' ', 3).should eq([\"foo\", \"bar\", \"baz qux\"]) }\n      it { \"foo bar baz qux\".split(' ', 30).should eq([\"foo\", \"bar\", \"baz\", \"qux\"]) }\n      it { \"a,b,\".split(',', 3).should eq([\"a\", \"b\", \"\"]) }\n      it { \"日本ん語日本ん語\".split('ん').should eq([\"日本\", \"語日本\", \"語\"]) }\n      it { \"=\".split('=').should eq([\"\", \"\"]) }\n      it { \"a=\".split('=').should eq([\"a\", \"\"]) }\n      it { \"=b\".split('=').should eq([\"\", \"b\"]) }\n      it { \"=\".split('=', 2).should eq([\"\", \"\"]) }\n      it { \"=\".split('=', 2, remove_empty: true).should eq([] of String) }\n    end\n\n    describe \"by string\" do\n      it { \"\".split(\",\").should eq([\"\"]) }\n      it { \"\".split(\":-\").should eq([\"\"]) }\n      it { \"\".split(\":-\", remove_empty: true).should eq([] of String) }\n      it { \"foo:-bar:-:-baz:-\".split(\":-\").should eq([\"foo\", \"bar\", \"\", \"baz\", \"\"]) }\n      it { \"foo:-bar:-:-baz:-\".split(\":-\", remove_empty: true).should eq([\"foo\", \"bar\", \"baz\"]) }\n      it { \"foo:-bar:-:-baz\".split(\":-\").should eq([\"foo\", \"bar\", \"\", \"baz\"]) }\n      it { \"foo\".split(\":-\").should eq([\"foo\"]) }\n      it { \"foo\".split(\"\").should eq([\"f\", \"o\", \"o\"]) }\n      it { \"日本さん語日本さん語\".split(\"さん\").should eq([\"日本\", \"語日本\", \"語\"]) }\n      it { \"foo,bar,baz,qux\".split(\",\", 1).should eq([\"foo,bar,baz,qux\"]) }\n      it { \"foo,bar,baz,qux\".split(\",\", 3).should eq([\"foo\", \"bar\", \"baz,qux\"]) }\n      it { \"foo,bar,baz,qux\".split(\",\", 30).should eq([\"foo\", \"bar\", \"baz\", \"qux\"]) }\n      it { \"a b c\".split(\" \", 2).should eq([\"a\", \"b c\"]) }\n      it { \"=\".split(\"=\").should eq([\"\", \"\"]) }\n      it { \"a=\".split(\"=\").should eq([\"a\", \"\"]) }\n      it { \"=b\".split(\"=\").should eq([\"\", \"b\"]) }\n      it { \"=\".split(\"=\", 2).should eq([\"\", \"\"]) }\n      it { \"=\".split(\"=\", 2, remove_empty: true).should eq([] of String) }\n    end\n\n    describe \"by regex\" do\n      it { \"\".split(/\\n\\t/).should eq([\"\"]) }\n      it { \"\".split(/\\n\\t/, remove_empty: true).should eq([] of String) }\n      it { \"foo\\n\\tbar\\n\\t\\n\\tbaz\".split(/\\n\\t/).should eq([\"foo\", \"bar\", \"\", \"baz\"]) }\n      it { \"foo\\n\\tbar\\n\\t\\n\\tbaz\".split(/\\n\\t/, remove_empty: true).should eq([\"foo\", \"bar\", \"baz\"]) }\n      it { \"foo\\n\\tbar\\n\\t\\n\\tbaz\".split(/(?:\\n\\t)+/).should eq([\"foo\", \"bar\", \"baz\"]) }\n      it { \"foo,bar\".split(/,/, 1).should eq([\"foo,bar\"]) }\n      it { \"foo,bar,\".split(/,/).should eq([\"foo\", \"bar\", \"\"]) }\n      it { \"foo,bar,baz,qux\".split(/,/, 1).should eq([\"foo,bar,baz,qux\"]) }\n      it { \"foo,bar,baz,qux\".split(/,/, 3).should eq([\"foo\", \"bar\", \"baz,qux\"]) }\n      it { \"foo,bar,baz,qux\".split(/,/, 30).should eq([\"foo\", \"bar\", \"baz\", \"qux\"]) }\n      it { \"a b c\".split(Regex.new(\" \"), 2).should eq([\"a\", \"b c\"]) }\n      it { \"日本ん語日本ん語\".split(/ん/).should eq([\"日本\", \"語日本\", \"語\"]) }\n      it { \"九十九十九\".split(/(?=十)/).should eq([\"九\", \"十九\", \"十九\"]) }\n      it { \"hello world\".split(/\\b/).should eq([\"hello\", \" \", \"world\", \"\"]) }\n      it { \"hello world\".split(/\\w+|(?= )/).should eq([\"\", \" \", \"\"]) }\n      it { \"abc\".split(//).should eq([\"a\", \"b\", \"c\"]) }\n      it { \"hello\".split(/\\w+/).should eq([\"\", \"\"]) }\n      it { \"foo\".split(/o/).should eq([\"f\", \"\", \"\"]) }\n      it { \"=\".split(/\\=/).should eq([\"\", \"\"]) }\n      it { \"a=\".split(/\\=/).should eq([\"a\", \"\"]) }\n      it { \"=b\".split(/\\=/).should eq([\"\", \"b\"]) }\n      it { \"=\".split(/\\=/, 2).should eq([\"\", \"\"]) }\n      it { \"=\".split(/\\=/, 2, remove_empty: true).should eq([] of String) }\n      it { \",\".split(/(?:(x)|(,))/).should eq([\"\", \",\", \"\"]) }\n      it { \"ba\".split(/a/, options: :anchored).should eq [\"ba\"] }\n\n      it \"keeps groups\" do\n        s = \"split on the word on okay?\"\n        s.split(/(on)/).should eq([\"split \", \"on\", \" the word \", \"on\", \" okay?\"])\n        s.split(/o(?:(n)|(r))/).should eq([\"split \", \"n\", \" the w\", \"r\", \"d \", \"n\", \" okay?\"])\n        s.split(/()/, limit: 4, remove_empty: true).should eq([\"s\", \"\", \"p\", \"\", \"l\", \"\", \"it on the word on okay?\"])\n      end\n    end\n  end\n\n  describe \"starts_with?\" do\n    it { \"foobar\".starts_with?(\"foo\").should be_true }\n    it { \"foobar\".starts_with?(\"\").should be_true }\n    it { \"foobar\".starts_with?(\"foobarbaz\").should be_false }\n    it { \"foobar\".starts_with?(\"foox\").should be_false }\n    it { \"foobar\".starts_with?(/foo/).should be_true }\n    it { \"foobar\".starts_with?(/bar/).should be_false }\n    it { \"foobar\".starts_with?('f').should be_true }\n    it { \"foobar\".starts_with?('g').should be_false }\n    it { \"よし\".starts_with?('よ').should be_true }\n    it { \"よし!\".starts_with?(\"よし\").should be_true }\n\n    it \"treats first char as replacement char if invalid in an otherwise ascii string\" do\n      \"\\xEEfoo\".starts_with?('\\u{EE}').should be_false\n      \"\\xEEfoo\".starts_with?(Char::REPLACEMENT).should be_true\n    end\n  end\n\n  describe \"ends_with?\" do\n    it { \"foobar\".ends_with?(\"bar\").should be_true }\n    it { \"foobar\".ends_with?(\"\").should be_true }\n    it { \"foobar\".ends_with?(\"foobarbaz\").should be_false }\n    it { \"foobar\".ends_with?(\"xbar\").should be_false }\n    it { \"foobar\".ends_with?(/bar/).should be_true }\n    it { \"foobar\".ends_with?(/foo|baz/).should be_false }\n    it { \"foobar\".ends_with?('r').should be_true }\n    it { \"foobar\".ends_with?('x').should be_false }\n    it { \"よし\".ends_with?('し').should be_true }\n    it { \"よし\".ends_with?('な').should be_false }\n    it { \"あいう_\".ends_with?('_').should be_true }\n\n    it \"treats last char as replacement char if invalid in an otherwise ascii string\" do\n      \"foo\\xEE\".ends_with?('\\u{EE}').should be_false\n      \"foo\\xEE\".ends_with?(Char::REPLACEMENT).should be_true\n    end\n  end\n\n  describe \"=~\" do\n    it \"matches with group\" do\n      \"foobar\" =~ /(o+)ba(r?)/\n      $1.should eq(\"oo\")\n      $2.should eq(\"r\")\n    end\n\n    it \"returns nil with string\" do\n      (\"foo\" =~ \"foo\").should be_nil\n    end\n\n    it \"returns nil with regex and regex\" do\n      (/foo/ =~ /foo/).should be_nil\n    end\n  end\n\n  describe \"delete\" do\n    it { \"foobar\".delete { |char| char == 'o' }.should eq(\"fbar\") }\n    it { \"hello world\".delete(\"lo\").should eq(\"he wrd\") }\n    it { \"hello world\".delete(\"lo\", \"o\").should eq(\"hell wrld\") }\n    it { \"hello world\".delete(\"hello\", \"^l\").should eq(\"ll wrld\") }\n    it { \"hello world\".delete(\"ej-m\").should eq(\"ho word\") }\n    it { \"hello^world\".delete(\"\\\\^aeiou\").should eq(\"hllwrld\") }\n    it { \"hello-world\".delete(\"a\\\\-eo\").should eq(\"hllwrld\") }\n    it { \"hello world\\\\r\\\\n\".delete(\"\\\\\").should eq(\"hello worldrn\") }\n    it { \"hello world\\\\r\\\\n\".delete(\"\\\\A\").should eq(\"hello world\\\\r\\\\n\") }\n    it { \"hello world\\\\r\\\\n\".delete(\"X-\\\\w\").should eq(\"hello orldrn\") }\n\n    it \"deletes one char\" do\n      \"foobar\".delete('o').should eq(\"fbar\")\n      \"foobar\".delete('x').should eq(\"foobar\")\n    end\n  end\n\n  describe \"#reverse\" do\n    it \"reverses string\" do\n      \"foobar\".reverse.should eq(\"raboof\")\n    end\n\n    it \"reverses utf-8 string\" do\n      \"こんにちは\".reverse.should eq(\"はちにんこ\")\n    end\n\n    it \"reverses taking grapheme clusters into account\" do\n      \"noël\".reverse.should eq(\"lëon\")\n    end\n\n    pending \"converts invalid code units to replacement char\" do\n      \"!\\xB0\\xC2?\".reverse.chars.should eq(\"?\\uFFFD!\".chars)\n      \"\\xC2\\xB0\\xB0\\xC2?\".reverse.chars.should eq(\"?\\uFFFD\\xC2\\xB0\".chars)\n    end\n  end\n\n  describe \"sub\" do\n    it \"subs char with char\" do\n      \"foobar\".sub('o', 'e').should eq(\"feobar\")\n    end\n\n    it \"subs char with string\" do\n      \"foobar\".sub('o', \"ex\").should eq(\"fexobar\")\n    end\n\n    it \"subs char with string\" do\n      \"foobar\".sub do |char|\n        char.should eq 'f'\n        \"some\"\n      end.should eq(\"someoobar\")\n\n      empty = \"\"\n      empty.sub { 'f' }.should be(empty)\n    end\n\n    it \"subs with regex and block\" do\n      actual = \"foo booor booooz\".sub(/o+/) do |str|\n        \"#{str}#{str.size}\"\n      end\n      actual.should eq(\"foo2 booor booooz\")\n    end\n\n    it \"subs with regex and block with group\" do\n      actual = \"foo booor booooz\".sub(/(o+).*?(o+)/) do |str, match|\n        \"#{match[1].size}#{match[2].size}\"\n      end\n      actual.should eq(\"f23r booooz\")\n    end\n\n    it \"subs with regex and string\" do\n      \"foo boor booooz\".sub(/o+/, \"a\").should eq(\"fa boor booooz\")\n    end\n\n    it \"subs with regex and string, returns self if no match\" do\n      str = \"hello\"\n      str.sub(/a/, \"b\").should be(str)\n    end\n\n    it \"subs with regex and string (utf-8)\" do\n      \"fここ bここr bここここz\".sub(/こ+/, \"そこ\").should eq(\"fそこ bここr bここここz\")\n    end\n\n    it \"subs with empty string\" do\n      \"foo\".sub(\"\", \"x\").should eq(\"xfoo\")\n    end\n\n    it \"subs with empty regex\" do\n      \"foo\".sub(//, \"x\").should eq(\"xfoo\")\n    end\n\n    it \"subs null character\" do\n      null = \"\\u{0}\"\n      \"f\\u{0}\\u{0}\".sub(/#{null}/, \"o\").should eq(\"fo\\u{0}\")\n    end\n\n    it \"subs with string and string\" do\n      \"foo boor booooz\".sub(\"oo\", \"a\").should eq(\"fa boor booooz\")\n    end\n\n    it \"subs with string and string return self if no match\" do\n      str = \"hello\"\n      str.sub(\"a\", \"b\").should be(str)\n    end\n\n    it \"subs with string and string (utf-8)\" do\n      \"fここ bここr bここここz\".sub(\"ここ\", \"そこ\").should eq(\"fそこ bここr bここここz\")\n    end\n\n    it \"subs with string and string (#3258)\" do\n      \"私は日本人です\".sub(\"日本\", \"スペイン\").should eq(\"私はスペイン人です\")\n    end\n\n    it \"subs with string and block\" do\n      result = \"foo boo\".sub(\"oo\") do |value|\n        value.should eq(\"oo\")\n        \"a\"\n      end\n      result.should eq(\"fa boo\")\n    end\n\n    it \"subs with char hash\" do\n      str = \"hello\"\n      str.sub({'e' => 'a', 'l' => 'd'}).should eq(\"hallo\")\n\n      empty = \"\"\n      empty.sub({'a' => 'b'}).should be(empty)\n    end\n\n    it \"subs with regex and hash\" do\n      str = \"hello\"\n      str.sub(/(he|l|o)/, {\"he\" => \"ha\", \"l\" => \"la\"}).should eq(\"hallo\")\n      str.sub(/(he|l|o)/, {\"l\" => \"la\"}).should be(str)\n    end\n\n    it \"subs with regex and named tuple\" do\n      str = \"hello\"\n      str.sub(/(he|l|o)/, {he: \"ha\", l: \"la\"}).should eq(\"hallo\")\n      str.sub(/(he|l|o)/, {l: \"la\"}).should be(str)\n    end\n\n    it \"subs using $~\" do\n      \"foo\".sub(/(o)/) { \"x#{$1}x\" }.should eq(\"fxoxo\")\n    end\n\n    it \"subs using with \\\\\" do\n      \"foo\".sub(/(o)/, \"\\\\\").should eq(\"f\\\\o\")\n    end\n\n    it \"subs using with z\\\\w\" do\n      \"foo\".sub(/(o)/, \"z\\\\w\").should eq(\"fz\\\\wo\")\n    end\n\n    it \"replaces with numeric back-reference\" do\n      \"foo\".sub(/o/, \"x\\\\0x\").should eq(\"fxoxo\")\n      \"foo\".sub(/(o)/, \"x\\\\1x\").should eq(\"fxoxo\")\n      \"foo\".sub(/(o)/, \"\\\\\\\\1\").should eq(\"f\\\\1o\")\n      \"hello\".sub(/[aeiou]/, \"(\\\\0)\").should eq(\"h(e)llo\")\n    end\n\n    it \"replaces with incomplete named back-reference (1)\" do\n      \"foo\".sub(/(oo)/, \"|\\\\k|\").should eq(\"f|\\\\k|\")\n    end\n\n    it \"replaces with incomplete named back-reference (2)\" do\n      \"foo\".sub(/(oo)/, \"|\\\\k\\\\1|\").should eq(\"f|\\\\koo|\")\n    end\n\n    it \"replaces with named back-reference\" do\n      \"foo\".sub(/(?<bar>oo)/, \"|\\\\k<bar>|\").should eq(\"f|oo|\")\n    end\n\n    it \"replaces with multiple named back-reference\" do\n      \"fooxx\".sub(/(?<bar>oo)(?<baz>x+)/, \"|\\\\k<bar>|\\\\k<baz>|\").should eq(\"f|oo|xx|\")\n    end\n\n    it \"replaces with \\\\a\" do\n      \"foo\".sub(/(oo)/, \"|\\\\a|\").should eq(\"f|\\\\a|\")\n    end\n\n    it \"replaces with \\\\\\\\\\\\1\" do\n      \"foo\".sub(/(oo)/, \"|\\\\\\\\\\\\1|\").should eq(\"f|\\\\oo|\")\n    end\n\n    it \"ignores if backreferences: false\" do\n      \"foo\".sub(/o/, \"x\\\\0x\", backreferences: false).should eq(\"fx\\\\0xo\")\n    end\n\n    it \"subs at index with char\" do\n      \"hello\".sub(1, 'a').should eq(\"hallo\")\n    end\n\n    it \"subs at index with char, non-ascii\" do\n      \"あいうえお\".sub(2, 'の').should eq(\"あいのえお\")\n    end\n\n    it \"subs at negative index with char\" do\n      string = \"abc\".sub(-1, 'd')\n      string.should eq(\"abd\")\n      string = string.sub(-2, 'n')\n      string.should eq(\"and\")\n    end\n\n    it \"subs at index with string\" do\n      \"hello\".sub(1, \"eee\").should eq(\"heeello\")\n    end\n\n    it \"subs at negative index with string\" do\n      \"hello\".sub(-1, \"ooo\").should eq(\"hellooo\")\n    end\n\n    it \"subs at index with string, non-ascii\" do\n      \"あいうえお\".sub(2, \"けくこ\").should eq(\"あいけくこえお\")\n    end\n\n    it \"raises if index is out of bounds\" do\n      expect_raises(IndexError) { \"hello\".sub(5, 'x') }\n      expect_raises(IndexError) { \"hello\".sub(6, \"\") }\n      expect_raises(IndexError) { \"hello\".sub(-6, 'x') }\n      expect_raises(IndexError) { \"hello\".sub(-7, \"\") }\n\n      expect_raises(IndexError) { \"あいうえお\".sub(5, 'x') }\n      expect_raises(IndexError) { \"あいうえお\".sub(6, \"\") }\n      expect_raises(IndexError) { \"あいうえお\".sub(-6, 'x') }\n      expect_raises(IndexError) { \"あいうえお\".sub(-7, \"\") }\n    end\n\n    it \"subs range with char\" do\n      \"hello\".sub(1..2, 'a').should eq(\"halo\")\n    end\n\n    it \"subs range with char, non-ascii\" do\n      \"あいうえお\".sub(1..2, 'け').should eq(\"あけえお\")\n    end\n\n    it \"subs range with string\" do\n      \"hello\".sub(1..2, \"eee\").should eq(\"heeelo\")\n    end\n\n    it \"subs range with string, non-ascii\" do\n      \"あいうえお\".sub(1..2, \"けくこ\").should eq(\"あけくこえお\")\n    end\n\n    it \"subs endless range with char\" do\n      \"hello\".sub(2..nil, 'a').should eq(\"hea\")\n    end\n\n    it \"subs endless range with string\" do\n      \"hello\".sub(2..nil, \"ya\").should eq(\"heya\")\n    end\n\n    it \"subs beginless range with char\" do\n      \"hello\".sub(nil..2, 'a').should eq(\"alo\")\n    end\n\n    it \"subs beginless range with string\" do\n      \"hello\".sub(nil..2, \"ye\").should eq(\"yelo\")\n    end\n\n    it \"subs the last char\" do\n      str = \"hello\"\n      str.sub('o', 'a').should eq(\"hella\")\n      str.sub('o', \"ad\").should eq(\"hellad\")\n      str.sub(4, 'a').should eq(\"hella\")\n      str.sub(4, \"ad\").should eq(\"hellad\")\n      str.sub(4..4, 'a').should eq(\"hella\")\n      str.sub(4..4, \"ad\").should eq(\"hellad\")\n      str.sub({'a' => 'b', 'o' => 'a'}).should eq(\"hella\")\n      str.sub({'a' => 'b', 'o' => \"ad\"}).should eq(\"hellad\")\n      str.sub(/o/, 'a').should eq(\"hella\")\n      str.sub(/o/, \"ad\").should eq(\"hellad\")\n      str.sub(/o/) { 'a' }.should eq(\"hella\")\n      str.sub(/o/) { \"ad\" }.should eq(\"hellad\")\n      str.sub(/(o)/, {\"o\" => 'a'}).should eq(\"hella\")\n      str.sub(/(o)/, {\"o\" => \"ad\"}).should eq(\"hellad\")\n      str.sub(/(o)/) { 'a' }.should eq(\"hella\")\n      str.sub(/(o)/) { \"ad\" }.should eq(\"hellad\")\n    end\n  end\n\n  describe \"#gsub\" do\n    it \"gsubs char with char\" do\n      \"foobar\".gsub('o', 'e').should eq(\"feebar\")\n    end\n\n    it \"gsubs char with string\" do\n      \"foobar\".gsub('o', \"ex\").should eq(\"fexexbar\")\n    end\n\n    it \"gsubs char with string (nop)\" do\n      s = \"foobar\"\n      s.gsub('x', \"yz\").should be(s)\n    end\n\n    it \"gsubs char with char in non-ascii string\" do\n      \"/ä\".gsub('/', '-').should eq(\"-ä\")\n    end\n\n    it \"gsubs char with string depending on the char\" do\n      replaced = \"foobar\".gsub do |char|\n        case char\n        when 'f'\n          \"some\"\n        when 'o'\n          \"thing\"\n        when 'a'\n          \"ex\"\n        else\n          char\n        end\n      end\n      replaced.should eq(\"somethingthingbexr\")\n    end\n\n    it \"gsubs with regex and block\" do\n      actual = \"foo booor booooz\".gsub(/o+/) do |str|\n        \"#{str}#{str.size}\"\n      end\n      actual.should eq(\"foo2 booo3r boooo4z\")\n    end\n\n    it \"gsubs with regex and block with group\" do\n      actual = \"foo booor booooz\".gsub(/(o+).*?(o+)/) do |str, match|\n        \"#{match[1].size}#{match[2].size}\"\n      end\n      actual.should eq(\"f23r b31z\")\n    end\n\n    it \"gsubs with regex and string\" do\n      \"foo boor booooz\".gsub(/o+/, \"a\").should eq(\"fa bar baz\")\n    end\n\n    it \"gsubs with regex and string, returns self if no match\" do\n      str = \"hello\"\n      str.gsub(/a/, \"b\").should be(str)\n    end\n\n    it \"gsubs with regex and string (utf-8)\" do\n      \"fここ bここr bここここz\".gsub(/こ+/, \"そこ\").should eq(\"fそこ bそこr bそこz\")\n    end\n\n    it \"gsubs with empty string\" do\n      \"foo\".gsub(\"\", \"x\").should eq(\"xfxoxox\")\n    end\n\n    it \"gsubs with empty regex\" do\n      \"foo\".gsub(//, \"x\").should eq(\"xfxoxox\")\n    end\n\n    it \"gsubs null character\" do\n      null = \"\\u{0}\"\n      \"f\\u{0}\\u{0}\".gsub(/#{null}/, \"o\").should eq(\"foo\")\n    end\n\n    it \"gsubs with string and string\" do\n      \"foo boor booooz\".gsub(\"oo\", \"a\").should eq(\"fa bar baaz\")\n    end\n\n    it \"gsubs with string and string return self if no match\" do\n      str = \"hello\"\n      str.gsub(\"a\", \"b\").should be(str)\n    end\n\n    it \"gsubs with string and string (utf-8)\" do\n      \"fここ bここr bここここz\".gsub(\"ここ\", \"そこ\").should eq(\"fそこ bそこr bそこそこz\")\n    end\n\n    it \"gsubs with string and block\" do\n      i = 0\n      result = \"foo boo\".gsub(\"oo\") do |value|\n        value.should eq(\"oo\")\n        i += 1\n        i == 1 ? \"a\" : \"e\"\n      end\n      result.should eq(\"fa be\")\n    end\n\n    it \"gsubs with char hash\" do\n      str = \"hello\"\n      str.gsub({'e' => 'a', 'l' => 'd'}).should eq(\"haddo\")\n    end\n\n    it \"gsubs with char named tuple\" do\n      str = \"hello\"\n      str.gsub({e: 'a', l: 'd'}).should eq(\"haddo\")\n    end\n\n    it \"gsubs with regex and hash\" do\n      str = \"hello\"\n      str.gsub(/(he|l|o)/, {\"he\" => \"ha\", \"l\" => \"la\"}).should eq(\"halala\")\n    end\n\n    it \"gsubs with regex and named tuple\" do\n      str = \"hello\"\n      str.gsub(/(he|l|o)/, {he: \"ha\", l: \"la\"}).should eq(\"halala\")\n    end\n\n    it \"gsubs using $~\" do\n      \"foo\".gsub(/(o)/) { \"x#{$1}x\" }.should eq(\"fxoxxox\")\n    end\n\n    it \"replaces with numeric back-reference\" do\n      \"foo\".gsub(/o/, \"x\\\\0x\").should eq(\"fxoxxox\")\n      \"foo\".gsub(/(o)/, \"x\\\\1x\").should eq(\"fxoxxox\")\n      \"foo\".gsub(/(ここ)|(oo)/, \"x\\\\1\\\\2x\").should eq(\"fxoox\")\n    end\n\n    it \"replaces with named back-reference\" do\n      \"foo\".gsub(/(?<bar>oo)/, \"|\\\\k<bar>|\").should eq(\"f|oo|\")\n      \"foo\".gsub(/(?<x>ここ)|(?<bar>oo)/, \"|\\\\k<bar>|\").should eq(\"f|oo|\")\n    end\n\n    it \"replaces with incomplete back-reference (1)\" do\n      \"foo\".gsub(/o/, \"\\\\\").should eq(\"f\\\\\\\\\")\n    end\n\n    it \"replaces with incomplete back-reference (2)\" do\n      \"foo\".gsub(/o/, \"\\\\\\\\\").should eq(\"f\\\\\\\\\")\n    end\n\n    it \"replaces with incomplete back-reference (3)\" do\n      \"foo\".gsub(/o/, \"\\\\k\").should eq(\"f\\\\k\\\\k\")\n    end\n\n    it \"raises with incomplete back-reference (1)\" do\n      expect_raises(ArgumentError) do\n        \"foo\".gsub(/(?<bar>oo)/, \"|\\\\k<bar|\")\n      end\n    end\n\n    it \"raises with incomplete back-reference (2)\" do\n      expect_raises(ArgumentError, \"Missing ending '>' for '\\\\\\\\k<...\") do\n        \"foo\".gsub(/o/, \"\\\\k<\")\n      end\n    end\n\n    it \"replaces with back-reference to missing capture group\" do\n      \"foo\".gsub(/o/, \"\\\\1\").should eq(\"f\")\n\n      expect_raises(IndexError, \"Undefined group name reference: \\\"bar\\\"\") do\n        \"foo\".gsub(/o/, \"\\\\k<bar>\").should eq(\"f\")\n      end\n\n      expect_raises(IndexError, \"Undefined group name reference: \\\"\\\"\") do\n        \"foo\".gsub(/o/, \"\\\\k<>\")\n      end\n    end\n\n    it \"replaces with escaped back-reference\" do\n      \"foo\".gsub(/o/, \"\\\\\\\\0\").should eq(\"f\\\\0\\\\0\")\n      \"foo\".gsub(/oo/, \"\\\\\\\\k<bar>\").should eq(\"f\\\\k<bar>\")\n    end\n\n    it \"ignores if backreferences: false\" do\n      \"foo\".gsub(/o/, \"x\\\\0x\", backreferences: false).should eq(\"fx\\\\0xx\\\\0x\")\n    end\n\n    it \"empty match\" do\n      \"a  b\".gsub(/\\B/, \"-\").should eq \"a - b\"\n      \"┬  7\".gsub(/\\B/, \"-\").should eq \"-┬- - 7\"\n    end\n\n    it \"empty string\" do\n      \"ab\".gsub(\"\", \"-\").should eq \"-a-b-\"\n      \"┬7\".gsub(\"\", \"-\").should eq \"-┬-7-\"\n    end\n  end\n\n  it \"scans using $~\" do\n    str = String.build do |str|\n      \"fooxooo\".scan(/(o+)/) { str << $1 }\n    end\n    str.should eq(\"ooooo\")\n  end\n\n  it \"#dump\" do\n    assert_prints \"a\".dump, %(\"a\")\n    assert_prints \"\\\\\".dump, %(\"\\\\\\\\\")\n    assert_prints \"\\\"\".dump, %(\"\\\\\"\")\n    assert_prints \"\\0\".dump, %(\"\\\\u0000\")\n    assert_prints \"\\x01\".dump, %(\"\\\\u0001\")\n    assert_prints \"\\xFF\".dump, %(\"\\\\xFF\")\n    assert_prints \"\\a\".dump, %(\"\\\\a\")\n    assert_prints \"\\b\".dump, %(\"\\\\b\")\n    assert_prints \"\\e\".dump, %(\"\\\\e\")\n    assert_prints \"\\f\".dump, %(\"\\\\f\")\n    assert_prints \"\\n\".dump, %(\"\\\\n\")\n    assert_prints \"\\r\".dump, %(\"\\\\r\")\n    assert_prints \"\\t\".dump, %(\"\\\\t\")\n    assert_prints \"\\v\".dump, %(\"\\\\v\")\n    assert_prints \"\\#{\".dump, %(\"\\\\\\#{\")\n    assert_prints \"á\".dump, %(\"\\\\u00E1\")\n    assert_prints \"\\u{81}\".dump, %(\"\\\\u0081\")\n    assert_prints \"\\u{1F48E}\".dump, %(\"\\\\u{1F48E}\")\n    assert_prints \"\\uF8FF\".dump, %(\"\\\\uF8FF\")       # private use character (Co)\n    assert_prints \"\\u202A\".dump, %(\"\\\\u202A\")       # bidi control character (Cf)\n    assert_prints \"\\u{110BD}\".dump, %(\"\\\\u{110BD}\") # Format character > U+FFFF (Cf)\n    assert_prints \"\\u00A0\".dump, %(\"\\\\u00A0\")       # white space (Zs)\n    assert_prints \"\\u200D\".dump, %(\"\\\\u200D\")       # format character (Cf)\n    assert_prints \" \".dump, %(\" \")\n  end\n\n  it \"#dump_unquoted\" do\n    assert_prints \"a\".dump_unquoted, %(a)\n    assert_prints \"\\\\\".dump_unquoted, %(\\\\\\\\)\n    assert_prints \"á\".dump_unquoted, %(\\\\u00E1)\n    assert_prints \"\\u{81}\".dump_unquoted, %(\\\\u0081)\n    assert_prints \"\\u{1F48E}\".dump_unquoted, %(\\\\u{1F48E})\n  end\n\n  it \"#inspect\" do\n    assert_prints \"a\".inspect, %(\"a\")\n    assert_prints \"\\\\\".inspect, %(\"\\\\\\\\\")\n    assert_prints \"\\\"\".inspect, %(\"\\\\\"\")\n    assert_prints \"\\0\".inspect, %(\"\\\\u0000\")\n    assert_prints \"\\x01\".inspect, %(\"\\\\u0001\")\n    assert_prints \"\\xFF\".inspect, %(\"\\\\xFF\")\n    assert_prints \"\\a\".inspect, %(\"\\\\a\")\n    assert_prints \"\\b\".inspect, %(\"\\\\b\")\n    assert_prints \"\\e\".inspect, %(\"\\\\e\")\n    assert_prints \"\\f\".inspect, %(\"\\\\f\")\n    assert_prints \"\\n\".inspect, %(\"\\\\n\")\n    assert_prints \"\\r\".inspect, %(\"\\\\r\")\n    assert_prints \"\\t\".inspect, %(\"\\\\t\")\n    assert_prints \"\\v\".inspect, %(\"\\\\v\")\n    assert_prints \"\\#{\".inspect, %(\"\\\\\\#{\")\n    assert_prints \"á\".inspect, %(\"á\")\n    assert_prints \"\\u{81}\".inspect, %(\"\\\\u0081\")\n    assert_prints \"\\u{1F48E}\".inspect, %(\"\\u{1F48E}\")\n    assert_prints \"\\uF8FF\".inspect, %(\"\\\\uF8FF\")       # private use character (Co)\n    assert_prints \"\\u202A\".inspect, %(\"\\\\u202A\")       # bidi control character (Cf)\n    assert_prints \"\\u{110BD}\".inspect, %(\"\\\\u{110BD}\") # Format character > U+FFFF (Cf)\n    assert_prints \"\\u00A0\".inspect, %(\"\\\\u00A0\")       # white space (Zs)\n    assert_prints \"\\u200D\".inspect, %(\"\\\\u200D\")       # format character (Cf)\n    assert_prints \" \".inspect, %(\" \")\n  end\n\n  it \"#inspect_unquoted\" do\n    assert_prints \"a\".inspect_unquoted, %(a)\n    assert_prints \"\\\\\".inspect_unquoted, %(\\\\\\\\)\n    assert_prints \"á\".inspect_unquoted, %(á)\n    assert_prints \"\\u{81}\".inspect_unquoted, %(\\\\u0081)\n    assert_prints \"\\u{1F48E}\".inspect_unquoted, %(\\u{1F48E})\n  end\n\n  it \"does pretty_inspect\" do\n    \"a\".pretty_inspect.should eq(%(\"a\"))\n    \"hello\\nworld\".pretty_inspect.should eq(%(\"hello\\\\n\" + \"world\"))\n    \"hello\\nworld\".pretty_inspect(width: 9).should eq(%(\"hello\\\\n\" +\\n\"world\"))\n    \"hello\\nworld\\n\".pretty_inspect(width: 9).should eq(%(\"hello\\\\n\" +\\n\"world\\\\n\"))\n  end\n\n  it \"does *\" do\n    (\"foo\" * 10).should eq(\"foofoofoofoofoofoofoofoofoofoo\")\n  end\n\n  describe \"+\" do\n    it \"does for both ascii\" do\n      str = \"foo\" + \"bar\"\n      str.@length.should eq(6) # Check that it was pre-computed\n      str.should eq(\"foobar\")\n    end\n\n    it \"does for both unicode\" do\n      str = \"青い\" + \"旅路\"\n      str.@length.should eq(4) # Check that it was pre-computed\n      str.should eq(\"青い旅路\")\n    end\n\n    it \"does with ascii char\" do\n      str = \"foo\"\n      str2 = str + '/'\n      str2.should eq(\"foo/\")\n    end\n\n    it \"does with unicode char\" do\n      str = \"fooba\"\n      str2 = str + 'る'\n      str2.should eq(\"foobaる\")\n    end\n\n    it \"does when right is empty\" do\n      str1 = \"foo\"\n      str2 = \"\"\n      (str1 + str2).should be(str1)\n    end\n\n    it \"does when left is empty\" do\n      str1 = \"\"\n      str2 = \"foo\"\n      (str1 + str2).should be(str2)\n    end\n  end\n\n  it \"escapes chars\" do\n    \"\\a\"[0].should eq('\\a')\n    \"\\b\"[0].should eq('\\b')\n    \"\\t\"[0].should eq('\\t')\n    \"\\n\"[0].should eq('\\n')\n    \"\\v\"[0].should eq('\\v')\n    \"\\f\"[0].should eq('\\f')\n    \"\\r\"[0].should eq('\\r')\n    \"\\e\"[0].should eq('\\e')\n    \"\\\"\"[0].should eq('\"')\n    \"\\\\\"[0].should eq('\\\\')\n  end\n\n  it \"escapes with octal\" do\n    \"\\3\"[0].ord.should eq(3)\n    \"\\23\"[0].ord.should eq((2 * 8) + 3)\n    \"\\123\"[0].ord.should eq((1 * 8 * 8) + (2 * 8) + 3)\n    \"\\033\"[0].ord.should eq((3 * 8) + 3)\n    \"\\033a\"[1].should eq('a')\n  end\n\n  it \"escapes with unicode\" do\n    \"\\u{12}\".codepoint_at(0).should eq(1 * 16 + 2)\n    \"\\u{A}\".codepoint_at(0).should eq(10)\n    \"\\u{AB}\".codepoint_at(0).should eq(10 * 16 + 11)\n    \"\\u{AB}1\".codepoint_at(1).should eq('1'.ord)\n  end\n\n  it \"does char_at\" do\n    \"いただきます\".char_at(2).should eq('だ')\n    \"foo\".char_at(0).should eq('f')\n    \"foo\".char_at(4) { 'x' }.should eq('x')\n\n    expect_raises(IndexError) do\n      \"foo\".char_at(4)\n    end\n  end\n\n  it \"does byte_at\" do\n    \"hello\".byte_at(1).should eq('e'.ord)\n    expect_raises(IndexError) { \"hello\".byte_at(5) }\n  end\n\n  it \"does byte_at?\" do\n    \"hello\".byte_at?(1).should eq('e'.ord)\n    \"hello\".byte_at?(5).should be_nil\n  end\n\n  it \"does chars\" do\n    \"ぜんぶ\".chars.should eq(['ぜ', 'ん', 'ぶ'])\n  end\n\n  describe \"creating from a pointer\" do\n    it \"allows creating a string with zeros\" do\n      p = Pointer(UInt8).malloc(3)\n      p[0] = 'a'.ord.to_u8\n      p[1] = '\\0'.ord.to_u8\n      p[2] = 'b'.ord.to_u8\n      s = String.new(p, 3)\n      s[0].should eq('a')\n      s[1].should eq('\\0')\n      s[2].should eq('b')\n      s.bytesize.should eq(3)\n    end\n\n    it \"raises an exception when creating a string with a null pointer and no size\" do\n      expect_raises ArgumentError do\n        String.new(Pointer(UInt8).null)\n      end\n    end\n\n    it \"raises when creating from a null pointer with a nonzero size\" do\n      expect_raises ArgumentError, \"Cannot create a string with a null pointer and a non-zero (3) bytesize\" do\n        String.new(Pointer(UInt8).null, 3)\n      end\n    end\n\n    it \"doesn't raise creating from a null pointer with size 0\" do\n      String.new(Pointer(UInt8).null, 0).should eq \"\"\n    end\n  end\n\n  describe \"creating from a slice\" do\n    it \"allows creating from an empty slice\" do\n      String.new(Bytes.empty).should eq(\"\")\n    end\n\n    it \"allows creating from a non-empty slice\" do\n      String.new(UInt8.slice(102, 111, 111, 0, 98, 97, 114)).should eq(\"foo\\0bar\")\n    end\n\n    it \"allows creating from a null-terminated slice\" do\n      String.new(Bytes.empty, truncate_at_null: true).should eq(\"\")\n      String.new(UInt8.slice(102, 111, 111, 98, 97, 114), truncate_at_null: true).should eq(\"foobar\")\n      String.new(UInt8.slice(102, 111, 111, 0, 98, 97, 114), truncate_at_null: true).should eq(\"foo\")\n    end\n  end\n\n  describe \"tr\" do\n    it \"translates\" do\n      \"bla\".tr(\"a\", \"h\").should eq(\"blh\")\n      \"bla\".tr(\"a\", \"⊙\").should eq(\"bl⊙\")\n      \"bl⊙a\".tr(\"⊙\", \"a\").should eq(\"blaa\")\n      \"bl⊙a\".tr(\"⊙\", \"ⓧ\").should eq(\"blⓧa\")\n      \"bl⊙a⊙asdfd⊙dsfsdf⊙⊙⊙\".tr(\"a⊙\", \"ⓧt\").should eq(\"bltⓧtⓧsdfdtdsfsdfttt\")\n      \"hello\".tr(\"aeiou\", \"*\").should eq(\"h*ll*\")\n      \"hello\".tr(\"el\", \"ip\").should eq(\"hippo\")\n      \"Lisp\".tr(\"Lisp\", \"Crys\").should eq(\"Crys\")\n      \"hello\".tr(\"helo\", \"1212\").should eq(\"12112\")\n      \"this\".tr(\"this\", \"ⓧ\").should eq(\"ⓧⓧⓧⓧ\")\n      \"über\".tr(\"ü\", \"u\").should eq(\"uber\")\n      \"aabbcc\".tr(\"a\", \"xyz\").should eq(\"xxbbcc\")\n      \"aabbcc\".tr(\"a\", \"いろは\").should eq(\"いいbbcc\")\n    end\n\n    context \"given no replacement characters\" do\n      it \"acts as #delete\" do\n        \"foo\".tr(\"o\", \"\").should eq(\"foo\".delete(\"o\"))\n      end\n    end\n  end\n\n  describe \"compare\" do\n    it \"compares with == when same string\" do\n      \"foo\".should eq(\"foo\")\n    end\n\n    it \"compares with == when different strings same contents\" do\n      (\"fo\" + \"o\").should eq(\"fo\" + \"o\")\n    end\n\n    it \"compares with == when different contents\" do\n      (\"fo\" + \"o\").should_not eq(\"bo\" + \"o\")\n    end\n\n    it \"sorts strings\" do\n      [\"foo1\", \"foo\", \"bar\"].sort.should eq([\"bar\", \"foo\", \"foo1\"])\n    end\n  end\n\n  describe \"#underscore\" do\n    it { assert_prints \"Foo\".underscore, \"foo\" }\n    it { assert_prints \"FooBar\".underscore, \"foo_bar\" }\n    it { assert_prints \"ABCde\".underscore, \"ab_cde\" }\n    it { assert_prints \"FOO_bar\".underscore, \"foo_bar\" }\n    it { assert_prints \"Char_S\".underscore, \"char_s\" }\n    it { assert_prints \"Char_\".underscore, \"char_\" }\n    it { assert_prints \"C_\".underscore, \"c_\" }\n    it { assert_prints \"HTTP\".underscore, \"http\" }\n    it { assert_prints \"HTTP_CLIENT\".underscore, \"http_client\" }\n    it { assert_prints \"CSS3\".underscore, \"css3\" }\n    it { assert_prints \"HTTP1.1\".underscore, \"http1.1\" }\n    it { assert_prints \"3.14IsPi\".underscore, \"3.14_is_pi\" }\n    it { assert_prints \"I2C\".underscore, \"i2_c\" }\n\n    it \"handles multi-character mappings correctly\" do\n      assert_prints \"İxİİ0İİxİ0\".underscore, \"i̇x_i̇i̇0_i̇_i̇x_i̇0\"\n    end\n  end\n\n  describe \"#camelcase\" do\n    it { assert_prints \"foo\".camelcase, \"Foo\" }\n    it { assert_prints \"foo_bar\".camelcase, \"FooBar\" }\n    it { assert_prints \"foo\".camelcase(lower: true), \"foo\" }\n    it { assert_prints \"foo_bar\".camelcase(lower: true), \"fooBar\" }\n    it { assert_prints \"Foo\".camelcase, \"Foo\" }\n    it { assert_prints \"Foo_bar\".camelcase, \"FooBar\" }\n    it { assert_prints \"Foo\".camelcase(lower: true), \"foo\" }\n    it { assert_prints \"Foo_bar\".camelcase(lower: true), \"fooBar\" }\n\n    it \"handles multi-character mappings correctly (#13533)\" do\n      assert_prints \"ﬄ_xﬄ\".camelcase, \"FflXﬄ\"\n      assert_prints \"İ_xﬄ\".camelcase(lower: true), \"i̇Xﬄ\"\n    end\n  end\n\n  describe \"ascii_only?\" do\n    it \"answers ascii_only?\" do\n      \"a\".ascii_only?.should be_true\n      \"あ\".ascii_only?.should be_false\n\n      str = String.new(1) do |buffer|\n        buffer.value = 'a'.ord.to_u8\n        {1, 0}\n      end\n      str.ascii_only?.should be_true\n\n      str = String.new(4) do |buffer|\n        count = 0\n        'あ'.each_byte do |byte|\n          buffer[count] = byte\n          count += 1\n        end\n        {count, 0}\n      end\n      str.ascii_only?.should be_false\n    end\n\n    it \"broken UTF-8 is not ascii_only\" do\n      \"\\xED\\xA0\\x80\\xED\\xBF\\xBF\".ascii_only?.should be_false\n    end\n  end\n\n  describe \"#scan\" do\n    it \"does without block\" do\n      a = \"cruel world\"\n      a.scan(/\\w+/).map(&.[0]).should eq([\"cruel\", \"world\"])\n      a.scan(/.../).map(&.[0]).should eq([\"cru\", \"el \", \"wor\"])\n      a.scan(/(...)/).map(&.[1]).should eq([\"cru\", \"el \", \"wor\"])\n      a.scan(/(..)(..)/).map { |m| {m[1], m[2]} }.should eq([{\"cr\", \"ue\"}, {\"l \", \"wo\"}])\n    end\n\n    it \"does with block\" do\n      a = \"foo goo\"\n      i = 0\n      a.scan(/\\w(o+)/) do |match|\n        case i\n        when 0\n          match[0].should eq(\"foo\")\n          match[1].should eq(\"oo\")\n        when 1\n          match[0].should eq(\"goo\")\n          match[1].should eq(\"oo\")\n        else\n          fail \"expected two matches\"\n        end\n        i += 1\n      end\n    end\n\n    it \"does with utf-8\" do\n      a = \"こん こん\"\n      a.scan(/こ/).map(&.[0]).should eq([\"こ\", \"こ\"])\n    end\n\n    it \"works when match is empty\" do\n      r = %r([\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\\s\\[\\]{}('\"`,;)]*))\n      \"hello\".scan(r).map(&.[0]).should eq([\"hello\", \"\"])\n      \"hello world\".scan(/\\w+|(?= )/).map(&.[0]).should eq([\"hello\", \"\", \"world\"])\n    end\n\n    it \"works when match is empty, multibyte char\" do\n      \"\\u{80}\\u{800}\\u{10000}\".scan(/()/).map(&.begin).should eq([0, 1, 2, 3])\n      \" Äa\".scan(/(?=(\\S))/).map(&.[1]).should eq([\"Ä\", \"a\"])\n    end\n\n    it \"works with strings with block\" do\n      res = [] of String\n      \"bla bla ablf\".scan(\"bl\") { |s| res << s }\n      res.should eq([\"bl\", \"bl\", \"bl\"])\n    end\n\n    it \"works with strings\" do\n      \"bla bla ablf\".scan(\"bl\").should eq([\"bl\", \"bl\", \"bl\"])\n      \"hello\".scan(\"world\").should eq([] of String)\n      \"bbb\".scan(\"bb\").should eq([\"bb\"])\n      \"ⓧⓧⓧ\".scan(\"ⓧⓧ\").should eq([\"ⓧⓧ\"])\n      \"ⓧ\".scan(\"ⓧ\").should eq([\"ⓧ\"])\n      \"ⓧ ⓧ ⓧ\".scan(\"ⓧ\").should eq([\"ⓧ\", \"ⓧ\", \"ⓧ\"])\n      \"\".scan(\"\").should eq([] of String)\n      \"a\".scan(\"\").should eq([] of String)\n      \"\".scan(\"a\").should eq([] of String)\n    end\n\n    it \"does with number and string\" do\n      \"1ab4\".scan(/\\d+/).map(&.[0]).should eq([\"1\", \"4\"])\n    end\n\n    it \"options parameter\" do\n      \"ba\".scan(/a/, options: :anchored).map(&.[0]).should eq [] of String\n    end\n  end\n\n  describe \"#match\" do\n    it \"has match\" do\n      match = \"FooBar\".match(/oo/).should_not be_nil\n      match[0].should eq(\"oo\")\n    end\n\n    it \"matches with position\" do\n      match = \"こんにちは\".match(/./, 1).should_not be_nil\n      match[0].should eq(\"ん\")\n    end\n\n    it \"matches empty string\" do\n      match = \"\".match(/.*/).should_not be_nil\n      match.group_size.should eq(0)\n      match[0].should eq(\"\")\n    end\n\n    it \"returns nil\" do\n      \"foo\".match(/bar/).should be_nil\n    end\n  end\n\n  it \"matches, but returns Bool\" do\n    \"foo\".matches?(/foo/).should be_true\n    \"foo\".matches?(/bar/).should be_false\n  end\n\n  it \"#matches_full?\" do\n    pending! if {{ Regex::Engine.resolve.name == \"Regex::PCRE\" }}\n    \"foo\".matches_full?(/foo/).should be_true\n    \"fooo\".matches_full?(/foo/).should be_false\n    \"ofoo\".matches_full?(/foo/).should be_false\n    \"pattern\".matches_full?(/(\\A)?pattern(\\z)?/).should be_true\n    \"_pattern_\".matches_full?(/(\\A)?pattern(\\z)?/).should be_false\n  end\n\n  it \"#match_full\" do\n    pending! if {{ Regex::Engine.resolve.name == \"Regex::PCRE\" }}\n    \"foo\".match_full(/foo/).not_nil![0].should eq \"foo\"\n    \"fooo\".match_full(/foo/).should be_nil\n    \"ofoo\".match_full(/foo/).should be_nil\n    \"pattern\".match_full(/(\\A)?pattern(\\z)?/).not_nil![0].should eq \"pattern\"\n    \"_pattern_\".match_full(/(\\A)?pattern(\\z)?/).should be_nil\n  end\n\n  it \"#match_full!\" do\n    pending! if {{ Regex::Engine.resolve.name == \"Regex::PCRE\" }}\n    \"foo\".match_full!(/foo/).not_nil![0].should eq \"foo\"\n    expect_raises(Regex::Error) { \"fooo\".match_full!(/foo/) }\n    expect_raises(Regex::Error) { \"ofoo\".match_full!(/foo/) }\n    \"pattern\".match_full!(/(\\A)?pattern(\\z)?/).not_nil![0].should eq \"pattern\"\n    expect_raises(Regex::Error) { \"_pattern_\".match_full!(/(\\A)?pattern(\\z)?/) }\n  end\n\n  it \"has size (same as size)\" do\n    \"テスト\".size.should eq(3)\n  end\n\n  describe \"count\" do\n    it { \"hello world\".count(\"lo\").should eq(5) }\n    it { \"hello world\".count(\"lo\", \"o\").should eq(2) }\n    it { \"hello world\".count(\"hello\", \"^l\").should eq(4) }\n    it { \"hello world\".count(\"ej-m\").should eq(4) }\n    it { \"hello^world\".count(\"\\\\^aeiou\").should eq(4) }\n    it { \"hello-world\".count(\"a\\\\-eo\").should eq(4) }\n    it { \"hello world\\\\r\\\\n\".count(\"\\\\\").should eq(2) }\n    it { \"hello world\\\\r\\\\n\".count(\"\\\\A\").should eq(0) }\n    it { \"hello world\\\\r\\\\n\".count(\"X-\\\\w\").should eq(3) }\n    it { \"aabbcc\".count('a').should eq(2) }\n    it { \"aabbcc\".count(&.in?('a', 'b')).should eq(4) }\n  end\n\n  describe \"squeeze\" do\n    it { \"aaabbbccc\".squeeze(&.in?('a', 'b')).should eq(\"abccc\") }\n    it { \"aaabbbccc\".squeeze(&.in?('a', 'c')).should eq(\"abbbc\") }\n    it { \"a       bbb\".squeeze.should eq(\"a b\") }\n    it { \"a    bbb\".squeeze(' ').should eq(\"a bbb\") }\n    it { \"aaabbbcccddd\".squeeze(\"b-d\").should eq(\"aaabcd\") }\n  end\n\n  describe \"ljust\" do\n    it { \"123\".ljust(2).should eq(\"123\") }\n    it { \"123\".ljust(5).should eq(\"123  \") }\n    it { \"12\".ljust(7, '-').should eq(\"12-----\") }\n    it { \"12\".ljust(7, 'あ').should eq(\"12あああああ\") }\n\n    describe \"to io\" do\n      it { String.build { |io| \"123\".ljust(io, 2) }.should eq(\"123\") }\n      it { String.build { |io| \"123\".ljust(io, 5) }.should eq(\"123  \") }\n      it { String.build { |io| \"12\".ljust(io, 7, '-') }.should eq(\"12-----\") }\n      it { String.build { |io| \"12\".ljust(io, 7, 'あ') }.should eq(\"12あああああ\") }\n    end\n  end\n\n  describe \"rjust\" do\n    it { \"123\".rjust(2).should eq(\"123\") }\n    it { \"123\".rjust(5).should eq(\"  123\") }\n    it { \"12\".rjust(7, '-').should eq(\"-----12\") }\n    it { \"12\".rjust(7, 'あ').should eq(\"あああああ12\") }\n\n    describe \"to io\" do\n      it { String.build { |io| \"123\".rjust(io, 2) }.should eq(\"123\") }\n      it { String.build { |io| \"123\".rjust(io, 5) }.should eq(\"  123\") }\n      it { String.build { |io| \"12\".rjust(io, 7, '-') }.should eq(\"-----12\") }\n      it { String.build { |io| \"12\".rjust(io, 7, 'あ') }.should eq(\"あああああ12\") }\n    end\n  end\n\n  describe \"center\" do\n    it { \"123\".center(2).should eq(\"123\") }\n    it { \"123\".center(5).should eq(\" 123 \") }\n    it { \"12\".center(7, '-').should eq(\"--12---\") }\n    it { \"12\".center(7, 'あ').should eq(\"ああ12あああ\") }\n\n    describe \"to io\" do\n      it { String.build { |io| \"123\".center(io, 2) }.should eq(\"123\") }\n      it { String.build { |io| \"123\".center(io, 5) }.should eq(\" 123 \") }\n      it { String.build { |io| \"12\".center(io, 7, '-') }.should eq(\"--12---\") }\n      it { String.build { |io| \"12\".center(io, 7, 'あ') }.should eq(\"ああ12あああ\") }\n    end\n  end\n\n  describe \"succ\" do\n    it \"returns an empty string for empty strings\" do\n      \"\".succ.should eq(\"\")\n    end\n\n    it \"returns the successor by increasing the rightmost alphanumeric (digit => digit, letter => letter with same case)\" do\n      \"abcd\".succ.should eq(\"abce\")\n      \"THX1138\".succ.should eq(\"THX1139\")\n\n      \"<<koala>>\".succ.should eq(\"<<koalb>>\")\n      \"==A??\".succ.should eq(\"==B??\")\n    end\n\n    it \"increases non-alphanumerics (via ascii rules) if there are no alphanumerics\" do\n      \"***\".succ.should eq(\"**+\")\n      \"**`\".succ.should eq(\"**a\")\n    end\n\n    it \"increases the next best alphanumeric (jumping over non-alphanumerics) if there is a carry\" do\n      \"dz\".succ.should eq(\"ea\")\n      \"HZ\".succ.should eq(\"IA\")\n      \"49\".succ.should eq(\"50\")\n\n      \"izz\".succ.should eq(\"jaa\")\n      \"IZZ\".succ.should eq(\"JAA\")\n      \"699\".succ.should eq(\"700\")\n\n      \"6Z99z99Z\".succ.should eq(\"7A00a00A\")\n\n      \"1999zzz\".succ.should eq(\"2000aaa\")\n      \"NZ/[]ZZZ9999\".succ.should eq(\"OA/[]AAA0000\")\n    end\n\n    it \"adds an additional character (just left to the last increased one) if there is a carry and no character left to increase\" do\n      \"z\".succ.should eq(\"aa\")\n      \"Z\".succ.should eq(\"AA\")\n      \"9\".succ.should eq(\"10\")\n\n      \"zz\".succ.should eq(\"aaa\")\n      \"ZZ\".succ.should eq(\"AAA\")\n      \"99\".succ.should eq(\"100\")\n\n      \"9Z99z99Z\".succ.should eq(\"10A00a00A\")\n\n      \"ZZZ9999\".succ.should eq(\"AAAA0000\")\n      \"/[]ZZZ9999\".succ.should eq(\"/[]AAAA0000\")\n      \"Z/[]ZZZ9999\".succ.should eq(\"AA/[]AAA0000\")\n    end\n  end\n\n  describe \"match!\" do\n    it \"returns matchdata\" do\n      md = \"Crystal\".match! /(?<bar>.)(?<foo>.)/\n      md[0].should eq \"Cr\"\n      md.captures.should eq [] of String\n      md.named_captures.should eq({\"bar\" => \"C\", \"foo\" => \"r\"})\n    end\n\n    it \"assigns captures\" do\n      md = \"foo\".match! /foo/\n      $~.should eq md\n    end\n\n    it \"raises on non-match\" do\n      expect_raises(Regex::Error, \"Match not found\") { \"foo\".match! /Crystal/ }\n      expect_raises(NilAssertionError) { $~ }\n    end\n\n    context \"with options\" do\n      it \"Regex::Match options\" do\n        expect_raises(Regex::Error, \"Match not found\") do\n          \".foo\".match!(/foo/, options: :anchored)\n        end\n        \"foo\".match!(/foo/, options: :anchored)\n      end\n    end\n  end\n\n  it \"does %\" do\n    (\"Hello %d world\" % 123).should eq(\"Hello 123 world\")\n    (\"Hello %d world\" % [123]).should eq(\"Hello 123 world\")\n  end\n\n  it \"does each_char\" do\n    s = \"abc\"\n    i = 0\n    s.each_char do |c|\n      case i\n      when 0\n        c.should eq('a')\n      when 1\n        c.should eq('b')\n      when 2\n        c.should eq('c')\n      else\n        fail \"shouldn't happen\"\n      end\n      i += 1\n    end.should be_nil\n    i.should eq(3)\n  end\n\n  it \"does each_char_with_index\" do\n    s = \"abc\"\n    values = [] of {Char, Int32}\n    s.each_char_with_index do |c, i|\n      values << {c, i}\n    end\n    values.should eq([{'a', 0}, {'b', 1}, {'c', 2}])\n  end\n\n  it \"does each_char_with_index, with offset\" do\n    s = \"abc\"\n    values = [] of {Char, Int32}\n    s.each_char_with_index(10) do |c, i|\n      values << {c, i}\n    end\n    values.should eq([{'a', 10}, {'b', 11}, {'c', 12}])\n  end\n\n  it_iterates \"#each_char\", ['a', 'b', 'c'], \"abc\".each_char\n  it_iterates \"#each_char with empty string\", [] of Char, \"\".each_char\n  it_iterates \"#each_byte\", ['a'.ord.to_u8, 'b'.ord.to_u8, 'c'.ord.to_u8], \"abc\".each_byte\n\n  it \"gets lines\" do\n    \"\".lines.should eq([] of String)\n    \"\\n\".lines.should eq([\"\"] of String)\n    \"\\r\".lines.should eq([\"\\r\"] of String)\n    \"\\r\\n\".lines.should eq([\"\"] of String)\n    \"foo\".lines.should eq([\"foo\"])\n    \"foo\\n\".lines.should eq([\"foo\"])\n    \"foo\\r\\n\".lines.should eq([\"foo\"])\n    \"foo\\nbar\\r\\nbaz\\n\".lines.should eq([\"foo\", \"bar\", \"baz\"])\n    \"foo\\nbar\\r\\nbaz\\r\\n\".lines.should eq([\"foo\", \"bar\", \"baz\"])\n  end\n\n  it \"gets lines with chomp = false\" do\n    \"foo\".lines(chomp: false).should eq([\"foo\"])\n    \"foo\\nbar\\r\\nbaz\\n\".lines(chomp: false).should eq([\"foo\\n\", \"bar\\r\\n\", \"baz\\n\"])\n    \"foo\\nbar\\r\\nbaz\\r\\n\".lines(chomp: false).should eq([\"foo\\n\", \"bar\\r\\n\", \"baz\\r\\n\"])\n  end\n\n  it \"gets each_line\" do\n    lines = [] of String\n    \"foo\\n\\nbar\\r\\nbaz\\n\".each_line do |line|\n      lines << line\n    end.should be_nil\n    lines.should eq([\"foo\", \"\", \"bar\", \"baz\"])\n  end\n\n  it \"gets each_line with chomp = false\" do\n    lines = [] of String\n    \"foo\\n\\nbar\\r\\nbaz\\r\\n\".each_line(chomp: false) do |line|\n      lines << line\n    end.should be_nil\n    lines.should eq([\"foo\\n\", \"\\n\", \"bar\\r\\n\", \"baz\\r\\n\"])\n  end\n\n  it \"gets each_line with remove_empty = true\" do\n    lines = [] of String\n    \"\\nfoo\\n\\nbar\\r\\nbaz\\n\\n\".each_line(remove_empty: true) do |line|\n      lines << line\n    end.should be_nil\n    lines.should eq([\"foo\", \"bar\", \"baz\"])\n  end\n\n  it \"gets each_line with remove_empty = true and chomp = false\" do\n    lines = [] of String\n    \"\\nfoo\\n\\nbar\\r\\n\\r\\nbaz\".each_line(remove_empty: true, chomp: false) do |line|\n      lines << line\n    end.should be_nil\n    lines.should eq([\"foo\\n\", \"bar\\r\\n\", \"baz\"])\n  end\n\n  it_iterates \"#each_line\", [\"foo\", \"bar\", \"baz\"], \"foo\\nbar\\r\\nbaz\\r\\n\".each_line\n  it_iterates \"#each_line(chomp: false)\", [\"foo\\n\", \"bar\\r\\n\", \"baz\\r\\n\"], \"foo\\nbar\\r\\nbaz\\r\\n\".each_line(chomp: false)\n  it_iterates \"#each_line(remove_empty: true)\", [\"foo\", \"bar\", \"baz\"], \"\\nfoo\\n\\nbar\\r\\n\\r\\nbaz\".each_line(remove_empty: true)\n  it_iterates \"#each_line(remove_empty: true, chomp: false)\", [\"foo\\n\", \"bar\\r\\n\", \"baz\"], \"\\nfoo\\n\\nbar\\r\\n\\r\\nbaz\".each_line(remove_empty: true, chomp: false)\n\n  it_iterates \"#each_codepoint\", [97, 98, 9731], \"ab☃\".each_codepoint\n\n  it \"has codepoints\" do\n    \"ab☃\".codepoints.should eq [97, 98, 9731]\n  end\n\n  it \"gets size of \\\\0 string\" do\n    \"\\0\\0\".size.should eq(2)\n  end\n\n  describe \"char_index_to_byte_index\" do\n    it \"with ascii\" do\n      \"foo\".char_index_to_byte_index(0).should eq(0)\n      \"foo\".char_index_to_byte_index(1).should eq(1)\n      \"foo\".char_index_to_byte_index(2).should eq(2)\n      \"foo\".char_index_to_byte_index(3).should eq(3)\n      \"foo\".char_index_to_byte_index(4).should be_nil\n    end\n\n    it \"with utf-8\" do\n      \"これ\".char_index_to_byte_index(0).should eq(0)\n      \"これ\".char_index_to_byte_index(1).should eq(3)\n      \"これ\".char_index_to_byte_index(2).should eq(6)\n      \"これ\".char_index_to_byte_index(3).should be_nil\n    end\n  end\n\n  describe \"byte_index_to_char_index\" do\n    it \"with ascii\" do\n      \"foo\".byte_index_to_char_index(0).should eq(0)\n      \"foo\".byte_index_to_char_index(1).should eq(1)\n      \"foo\".byte_index_to_char_index(2).should eq(2)\n      \"foo\".byte_index_to_char_index(3).should eq(3)\n      \"foo\".byte_index_to_char_index(4).should be_nil\n    end\n\n    it \"with utf-8\" do\n      \"これ\".byte_index_to_char_index(0).should eq(0)\n      \"これ\".byte_index_to_char_index(3).should eq(1)\n      \"これ\".byte_index_to_char_index(6).should eq(2)\n      \"これ\".byte_index_to_char_index(7).should be_nil\n      \"これ\".byte_index_to_char_index(1).should be_nil\n    end\n  end\n\n  describe \"String.new(&)\" do\n    it \"creates with matching capacity\" do\n      String.new(3) { |buf|\n        buf[0] = 'f'.ord.to_u8\n        buf[1] = 'o'.ord.to_u8\n        buf[2] = 'o'.ord.to_u8\n        {3, 3}\n      }.should eq \"foo\"\n    end\n\n    it \"creates with excess capacity\" do\n      String.new(5) { |buf|\n        buf[0] = 'f'.ord.to_u8\n        buf[1] = 'o'.ord.to_u8\n        buf[2] = 'o'.ord.to_u8\n        {3, 3}\n      }.should eq \"foo\"\n    end\n\n    it \"raises if string capacity is negative\" do\n      expect_raises(ArgumentError, \"Negative capacity\") do\n        String.new(-1) { |buf| {0, 0} }\n      end\n    end\n\n    it \"raises if capacity too big with UInt32::MAX\" do\n      expect_raises(ArgumentError, \"Capacity too big\") do\n        String.new(UInt32::MAX) { {0, 0} }\n      end\n    end\n\n    it \"raises if capacity too big with UInt32::MAX - String::HEADER_SIZE - 1\" do\n      expect_raises(ArgumentError, \"Capacity too big\") do\n        String.new(UInt32::MAX - String::HEADER_SIZE) { {0, 0} }\n      end\n    end\n\n    it \"raises if capacity too big with UInt64::MAX\" do\n      expect_raises(ArgumentError, \"Capacity too big\") do\n        String.new(UInt64::MAX) { {0, 0} }\n      end\n    end\n\n    pending_wasm32 \"allocates buffer of correct size (#3332)\" do\n      String.new(255_u8) do |buffer|\n        LibGC.size(buffer).should be > 255\n        {255, 0}\n      end\n    end\n\n    it \"raises if returned bytesize is greater than capacity\" do\n      expect_raises ArgumentError, \"Bytesize out of capacity bounds\" do\n        String.new(123) do |buffer|\n          {124, 0}\n        end\n      end\n    end\n  end\n\n  describe \"#compare\" do\n    it \"compares case-sensitive\" do\n      \"fo\".compare(\"foo\").should eq(-1)\n      \"foo\".compare(\"fo\").should eq(1)\n      \"foo\".compare(\"foo\").should eq(0)\n      \"foo\".compare(\"fox\").should eq(-1)\n      \"fox\".compare(\"foo\").should eq(1)\n      \"foo\".compare(\"Foo\").should eq(1)\n      \"hällo\".compare(\"Hällo\").should eq(1)\n      \"\".compare(\"\").should eq(0)\n    end\n\n    it \"compares case-insensitive\" do\n      \"foo\".compare(\"FO\", case_insensitive: true).should eq(1)\n      \"FOO\".compare(\"fo\", case_insensitive: true).should eq(1)\n      \"fo\".compare(\"FOO\", case_insensitive: true).should eq(-1)\n      \"FOX\".compare(\"foo\", case_insensitive: true).should eq(1)\n      \"foo\".compare(\"FOX\", case_insensitive: true).should eq(-1)\n      \"foo\".compare(\"FOO\", case_insensitive: true).should eq(0)\n      \"hELLo\".compare(\"HellO\", case_insensitive: true).should eq(0)\n      \"fo\\u{0}\".compare(\"FO\", case_insensitive: true).should eq(1)\n      \"fo\".compare(\"FO\\u{0}\", case_insensitive: true).should eq(-1)\n      \"\\u{0}\".compare(\"\\u{0}\", case_insensitive: true).should eq(0)\n      \"z\".compare(\"hello\", case_insensitive: true).should eq(1)\n      \"h\".compare(\"zzz\", case_insensitive: true).should eq(-1)\n      \"ä\".compare(\"äA\", case_insensitive: true).should eq(-1)\n      \"äÄ\".compare(\"äÄ\", case_insensitive: true).should eq(0)\n      \"heIIo\".compare(\"heııo\", case_insensitive: true, options: Unicode::CaseOptions::Turkic).should eq(0)\n      \"\".compare(\"abc\", case_insensitive: true).should eq(-1)\n      \"abc\".compare(\"\", case_insensitive: true).should eq(1)\n      \"abcA\".compare(\"abca\", case_insensitive: true).should eq(0)\n    end\n\n    it \"compares case-insensitive, multiple chars after case conversion (#4513)\" do\n      \"ﬄ\".compare(\"ffl\", case_insensitive: true, options: Unicode::CaseOptions::Fold).should eq(0)\n      \"FFL\".compare(\"ﬄ\", case_insensitive: true, options: Unicode::CaseOptions::Fold).should eq(0)\n      \"sß\".compare(\"ßs\", case_insensitive: true, options: Unicode::CaseOptions::Fold).should eq(0)\n    end\n\n    it \"treats invalid code units as replacement char in an otherwise ascii string\" do\n      \"\\xC0\".compare(\"\\xE0\", case_insensitive: true).should eq(0)\n      \"\\xE0\".compare(\"\\xC0\", case_insensitive: true).should eq(0)\n      \"\\xC0\".compare(\"a\", case_insensitive: true).should eq(1)\n      \"a\".compare(\"\\xC0\", case_insensitive: true).should eq(-1)\n    end\n  end\n\n  it \"builds with write_byte\" do\n    string = String.build do |io|\n      255_u8.times do |byte|\n        io.write_byte(byte)\n      end\n    end\n    255.times do |i|\n      string.byte_at(i).should eq(i)\n    end\n  end\n\n  it \"raises if String.build negative capacity\" do\n    expect_raises(ArgumentError, \"Negative capacity\") do\n      String.build(-1) { }\n    end\n  end\n\n  it \"raises if String.build capacity too big\" do\n    expect_raises(ArgumentError, \"Capacity too big\") do\n      String.build(UInt32::MAX) { }\n    end\n  end\n\n  {% unless flag?(:without_iconv) %}\n    describe \"encode\" do\n      it \"encodes\" do\n        bytes = \"Hello\".encode(\"UCS-2LE\")\n        bytes.should eq Bytes[72, 0, 101, 0, 108, 0, 108, 0, 111, 0]\n      end\n\n      {% unless flag?(:musl) || flag?(:solaris) || flag?(:freebsd) || flag?(:dragonfly) || flag?(:netbsd) %}\n        it \"flushes the shift state (#11992)\" do\n          \"\\u{00CA}\".encode(\"BIG5-HKSCS\").should eq(Bytes[0x88, 0x66])\n          \"\\u{00CA}\\u{0304}\".encode(\"BIG5-HKSCS\").should eq(Bytes[0x88, 0x62])\n        end\n      {% end %}\n\n      # FreeBSD iconv encoder expects ISO/IEC 10646 compatibility code points,\n      # see https://www.ccli.gov.hk/doc/e_hkscs_2008.pdf for details.\n      {% if flag?(:freebsd) || flag?(:dragonfly) || flag?(:netbsd) %}\n        it \"flushes the shift state (#11992)\" do\n          \"\\u{F329}\".encode(\"BIG5-HKSCS\").should eq(Bytes[0x88, 0x66])\n          \"\\u{F325}\".encode(\"BIG5-HKSCS\").should eq(Bytes[0x88, 0x62])\n        end\n      {% end %}\n\n      it \"raises if wrong encoding\" do\n        expect_raises ArgumentError, \"Invalid encoding: FOO\" do\n          \"Hello\".encode(\"FOO\")\n        end\n      end\n\n      it \"raises if wrong encoding with skip\" do\n        expect_raises ArgumentError, \"Invalid encoding: FOO\" do\n          \"Hello\".encode(\"FOO\", invalid: :skip)\n        end\n      end\n\n      it \"raises if illegal byte sequence\" do\n        expect_raises ArgumentError, \"Invalid multibyte sequence\" do\n          \"\\xff\".encode(\"EUC-JP\")\n        end\n      end\n\n      it \"doesn't raise on invalid byte sequence\" do\n        \"好\\xff是\".encode(\"EUC-JP\", invalid: :skip).should eq(Bytes[185, 165, 192, 167])\n      end\n\n      it \"raises if incomplete byte sequence\" do\n        expect_raises ArgumentError, \"Incomplete multibyte sequence\" do\n          \"好\".byte_slice(0, 1).encode(\"EUC-JP\")\n        end\n      end\n\n      it \"doesn't raise if incomplete byte sequence\" do\n        (\"好\".byte_slice(0, 1) + \"是\").encode(\"EUC-JP\", invalid: :skip).should eq(Bytes[192, 167])\n      end\n\n      it \"decodes\" do\n        bytes = \"Hello\".encode(\"UTF-16LE\")\n        String.new(bytes, \"UTF-16LE\").should eq(\"Hello\")\n      end\n\n      {% unless flag?(:solaris) || flag?(:freebsd) || flag?(:dragonfly) || flag?(:netbsd) %}\n        it \"decodes with shift state\" do\n          String.new(Bytes[0x88, 0x66], \"BIG5-HKSCS\").should eq(\"\\u{00CA}\")\n          String.new(Bytes[0x88, 0x62], \"BIG5-HKSCS\").should eq(\"\\u{00CA}\\u{0304}\")\n        end\n      {% end %}\n\n      # FreeBSD iconv decoder returns ISO/IEC 10646-1:2000 code points,\n      # see https://www.ccli.gov.hk/doc/e_hkscs_2008.pdf for details.\n      {% if flag?(:freebsd) || flag?(:dragonfly) || flag?(:netbsd) %}\n        it \"decodes with shift state\" do\n          String.new(Bytes[0x88, 0x66], \"BIG5-HKSCS\").should eq(\"\\u{00CA}\")\n          String.new(Bytes[0x88, 0x62], \"BIG5-HKSCS\").should eq(\"\\u{F325}\")\n        end\n      {% end %}\n\n      it \"decodes with skip\" do\n        bytes = Bytes[186, 195, 255, 202, 199]\n        String.new(bytes, \"EUC-JP\", invalid: :skip).should eq(\"挫頁\")\n      end\n    end\n  {% end %}\n\n  it \"inserts\" do\n    \"bar\".insert(0, \"foo\").should eq(\"foobar\")\n    \"bar\".insert(1, \"foo\").should eq(\"bfooar\")\n    \"bar\".insert(2, \"foo\").should eq(\"bafoor\")\n    \"bar\".insert(3, \"foo\").should eq(\"barfoo\")\n\n    \"bar\".insert(-1, \"foo\").should eq(\"barfoo\")\n    \"bar\".insert(-2, \"foo\").should eq(\"bafoor\")\n\n    \"ともだち\".insert(0, \"ねこ\").should eq(\"ねこともだち\")\n    \"ともだち\".insert(1, \"ねこ\").should eq(\"とねこもだち\")\n    \"ともだち\".insert(2, \"ねこ\").should eq(\"ともねこだち\")\n    \"ともだち\".insert(4, \"ねこ\").should eq(\"ともだちねこ\")\n\n    \"ともだち\".insert(0, 'ね').should eq(\"ねともだち\")\n    \"ともだち\".insert(1, 'ね').should eq(\"とねもだち\")\n    \"ともだち\".insert(2, 'ね').should eq(\"ともねだち\")\n    \"ともだち\".insert(4, 'ね').should eq(\"ともだちね\")\n\n    \"ともだち\".insert(-1, 'ね').should eq(\"ともだちね\")\n    \"ともだち\".insert(-2, 'ね').should eq(\"ともだねち\")\n\n    expect_raises(IndexError) { \"bar\".insert(4, \"foo\") }\n    expect_raises(IndexError) { \"bar\".insert(-5, \"foo\") }\n    expect_raises(IndexError) { \"bar\".insert(4, 'f') }\n    expect_raises(IndexError) { \"bar\".insert(-5, 'f') }\n\n    \"barbar\".insert(0, \"foo\").size.should eq(9)\n    \"ともだち\".insert(0, \"ねこ\").size.should eq(6)\n\n    \"foo\".insert(0, 'a').ascii_only?.should be_true\n    \"foo\".insert(0, 'あ').ascii_only?.should be_false\n    \"\".insert(0, 'a').ascii_only?.should be_true\n    \"\".insert(0, 'あ').ascii_only?.should be_false\n  end\n\n  it \"hexbytes\" do\n    expect_raises(ArgumentError) { \"abc\".hexbytes }\n    expect_raises(ArgumentError) { \"abc \".hexbytes }\n    \"abcd\".hexbytes.should eq(Bytes[171, 205])\n  end\n\n  it \"hexbytes?\" do\n    \"abc\".hexbytes?.should be_nil\n    \"abc \".hexbytes?.should be_nil\n    \"abcd\".hexbytes?.should eq(Bytes[171, 205])\n  end\n\n  it \"dups\" do\n    string = \"foo\"\n    dup = string.dup\n    string.should be(dup)\n  end\n\n  it \"clones\" do\n    string = \"foo\"\n    clone = string.clone\n    string.should be(clone)\n  end\n\n  describe \"invalid UTF-8 byte sequence\" do\n    it \"gets size\" do\n      string = String.new(Bytes[255, 0, 0, 0, 65])\n      string.size.should eq(5)\n    end\n\n    it \"gets size (2)\" do\n      string = String.new(Bytes[104, 101, 108, 108, 111, 32, 255, 32, 255, 32, 119, 111, 114, 108, 100, 33])\n      string.size.should eq(16)\n    end\n\n    it \"gets chars\" do\n      string = String.new(Bytes[255, 0, 0, 0, 65])\n      string.chars.should eq([Char::REPLACEMENT, 0.chr, 0.chr, 0.chr, 65.chr])\n    end\n\n    it \"gets chars (2)\" do\n      string = String.new(Bytes[255, 0])\n      string.chars.should eq([Char::REPLACEMENT, 0.chr])\n    end\n\n    it \"valid_encoding?\" do\n      \"hello\".valid_encoding?.should be_true\n      \"hello\\u{80}\\u{7FF}\\u{800}\\u{FFFF}\\u{10000}\\u{10FFFF}\".valid_encoding?.should be_true\n\n      {% for bytes in VALID_UTF8_BYTE_SEQUENCES %}\n        String.new(Bytes{{ bytes }}).valid_encoding?.should be_true\n      {% end %}\n\n      {% for bytes in INVALID_UTF8_BYTE_SEQUENCES %}\n        String.new(Bytes{{ bytes }}).valid_encoding?.should be_false\n      {% end %}\n    end\n\n    it \"scrubs\" do\n      string = String.new(Bytes[255, 129, 97, 255, 97])\n      string.scrub.bytes.should eq([239, 191, 189, 239, 191, 189, 97, 239, 191, 189, 97])\n\n      string.scrub(\"?\").should eq(\"??a?a\")\n\n      \"hello\".scrub.should eq(\"hello\")\n    end\n  end\n\n  describe \"interpolation\" do\n    it \"of a single string\" do\n      string = \"hello\"\n      interpolated = String.interpolation(string)\n      interpolated.should be(string)\n    end\n\n    it \"of a single non-string\" do\n      String.interpolation(123).should eq(\"123\")\n    end\n\n    it \"of string and char\" do\n      String.interpolation(\"hello\", '!').should eq(\"hello!\")\n    end\n\n    it \"of char and string\" do\n      String.interpolation('!', \"hello\").should eq(\"!hello\")\n    end\n\n    it \"of multiple strings\" do\n      String.interpolation(\"a\", \"bcd\", \"ef\").should eq(\"abcdef\")\n    end\n\n    it \"of multiple possibly non-strings\" do\n      String.interpolation(\"a\", 123, \"b\", 456, \"cde\").should eq(\"a123b456cde\")\n    end\n  end\n\n  describe \"delete_at\" do\n    describe \"char\" do\n      it { \"abcde\".delete_at(0).should eq(\"bcde\") }\n      it { \"abcde\".delete_at(1).should eq(\"acde\") }\n      it { \"abcde\".delete_at(2).should eq(\"abde\") }\n      it { \"abcde\".delete_at(4).should eq(\"abcd\") }\n      it { \"abcde\".delete_at(-2).should eq(\"abce\") }\n      it { expect_raises(IndexError) { \"abcde\".delete_at(5) } }\n      it { expect_raises(IndexError) { \"abcde\".delete_at(-6) } }\n\n      it { \"二ノ国\".delete_at(0).should eq(\"ノ国\") }\n      it { \"二ノ国\".delete_at(1).should eq(\"二国\") }\n      it { \"二ノ国\".delete_at(2).should eq(\"二ノ\") }\n      it { \"二ノ国\".delete_at(-2).should eq(\"二国\") }\n      it { expect_raises(IndexError) { \"二ノ国\".delete_at(3) } }\n      it { expect_raises(IndexError) { \"二ノ国\".delete_at(-4) } }\n    end\n\n    describe \"start, count\" do\n      it { \"abcdefg\".delete_at(0, 2).should eq(\"cdefg\") }\n      it { \"abcdefg\".delete_at(1, 2).should eq(\"adefg\") }\n      it { \"abcdefg\".delete_at(3, 10).should eq(\"abc\") }\n      it { \"abcdefg\".delete_at(-3, 2).should eq(\"abcdg\") }\n      it { \"abcdefg\".delete_at(7, 10).should eq(\"abcdefg\") }\n      it { expect_raises(IndexError) { \"abcdefg\".delete_at(8, 1) } }\n      it { expect_raises(IndexError) { \"abcdefg\".delete_at(-8, 1) } }\n\n      it \"raises on negative count\" do\n        expect_raises(ArgumentError, \"Negative count: -1\") {\n          \"abcdefg\".delete_at(1, -1)\n        }\n      end\n\n      it { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(4, 6).should eq(\"セキロ：ダイ トゥワイス\") }\n      it { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(0, 4).should eq(\"シャドウズ ダイ トゥワイス\") }\n      it { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(3, 20).should eq(\"セキロ\") }\n      it { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(-14, 6).should eq(\"セキロ：ダイ トゥワイス\") }\n      it { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(18, 3).should eq(\"セキロ：シャドウズ ダイ トゥワイス\") }\n      it { expect_raises(IndexError) { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(19, 1) } }\n      it { expect_raises(IndexError) { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(-19, 1) } }\n\n      it \"raises on negative count\" do\n        expect_raises(ArgumentError, \"Negative count: -1\") {\n          \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(1, -1)\n        }\n      end\n    end\n\n    describe \"range\" do\n      it { \"abcdefg\".delete_at(0..1).should eq(\"cdefg\") }\n      it { \"abcdefg\".delete_at(0...2).should eq(\"cdefg\") }\n      it { \"abcdefg\".delete_at(1..3).should eq(\"aefg\") }\n      it { \"abcdefg\".delete_at(3..10).should eq(\"abc\") }\n      it { \"abcdefg\".delete_at(-3..-2).should eq(\"abcdg\") }\n      it { \"abcdefg\".delete_at(3..).should eq(\"abc\") }\n      it { \"abcdefg\".delete_at(..-3).should eq(\"fg\") }\n      it { expect_raises(IndexError) { \"abcdefg\".delete_at(8..1) } }\n      it { expect_raises(IndexError) { \"abcdefg\".delete_at(-8..1) } }\n\n      it { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(4...10).should eq(\"セキロ：ダイ トゥワイス\") }\n      it { expect_raises(IndexError) { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(19..1) } }\n      it { expect_raises(IndexError) { \"セキロ：シャドウズ ダイ トゥワイス\".delete_at(-19..1) } }\n    end\n  end\n\n  describe \"ensure_suffix\" do\n    context \"with string suffix\" do\n      it \"adds suffix if not present\" do\n        \"foo\".ensure_suffix(\"bar\").should eq(\"foobar\")\n        \"foo\".ensure_suffix(\"FOO\").should eq(\"fooFOO\")\n        \"foo\".ensure_suffix(\"\").should eq(\"foo\")\n        \"foobar\".ensure_suffix(\"arr\").should eq(\"foobararr\")\n      end\n\n      it \"does not add suffix if already present\" do\n        \"foobar\".ensure_suffix(\"bar\").should eq(\"foobar\")\n        \"FOOBAR\".ensure_suffix(\"BAR\").should eq(\"FOOBAR\")\n      end\n    end\n\n    context \"with char suffix\" do\n      it \"adds suffix if not present\" do\n        \"foo\".ensure_suffix('b').should eq(\"foob\")\n        \"foo\".ensure_suffix('O').should eq(\"fooO\")\n      end\n\n      it \"does not add suffix if already present\" do\n        \"foob\".ensure_suffix('b').should eq(\"foob\")\n        \"FOOB\".ensure_suffix('B').should eq(\"FOOB\")\n      end\n    end\n  end\n\n  describe \"ensure_prefix\" do\n    context \"with string prefix\" do\n      it \"adds prefix if not present\" do\n        \"foo\".ensure_prefix(\"bar\").should eq(\"barfoo\")\n        \"foo\".ensure_prefix(\"FOO\").should eq(\"FOOfoo\")\n        \"foo\".ensure_prefix(\"\").should eq(\"foo\")\n        \"foo\".ensure_prefix(\"barf\").should eq(\"barffoo\")\n      end\n\n      it \"does not add prefix if already present\" do\n        \"foobar\".ensure_prefix(\"foo\").should eq(\"foobar\")\n        \"FOOBAR\".ensure_prefix(\"FOO\").should eq(\"FOOBAR\")\n      end\n    end\n\n    context \"with char prefix\" do\n      it \"adds prefix if not present\" do\n        \"foo\".ensure_prefix('b').should eq(\"bfoo\")\n        \"foo\".ensure_prefix('F').should eq(\"Ffoo\")\n      end\n\n      it \"does not add prefix if already present\" do\n        \"bfoo\".ensure_prefix('b').should eq(\"bfoo\")\n        \"BFOO\".ensure_prefix('B').should eq(\"BFOO\")\n      end\n    end\n  end\n\n  it \".additive_identity\" do\n    String.additive_identity.should be \"\"\n  end\nend\n\nclass String\n  describe String do\n    it \".char_bytesize_at\" do\n      {% for bytes, char in VALID_UTF8_BYTE_SEQUENCES %}\n        String.char_bytesize_at(Bytes[{{ bytes.splat }}, 0].to_unsafe).should eq({{ bytes.size }})\n      {% end %}\n\n      {% for bytes in INVALID_UTF8_BYTE_SEQUENCES %}\n        String.char_bytesize_at(Bytes[{{ bytes.splat }}, 0].to_unsafe).should eq 1\n      {% end %}\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/struct_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"big\"\n\nprivate module StructSpec\n  struct TestClass\n    @x : Int32\n    @y : String\n\n    def initialize(@x, @y)\n    end\n  end\n\n  struct BigIntWrapper\n    @value : BigInt\n\n    def initialize(@value : BigInt)\n    end\n  end\n\n  struct DupCloneStruct\n    property x, y\n\n    def initialize\n      @x = 1\n      @y = [1, 2, 3]\n    end\n\n    def_clone\n  end\n\n  abstract struct GeneralStruct\n  end\n\n  struct FooStruct < GeneralStruct\n  end\n\n  struct BarStruct < GeneralStruct\n  end\nend\n\ndescribe \"Struct\" do\n  it \"does to_s\" do\n    s = StructSpec::TestClass.new(1, \"hello\")\n    s.to_s.should eq(%(StructSpec::TestClass(@x=1, @y=\"hello\")))\n  end\n\n  it \"does ==\" do\n    s = StructSpec::TestClass.new(1, \"hello\")\n    s.should eq(s)\n  end\n\n  it \"does hash\" do\n    s = StructSpec::TestClass.new(1, \"hello\")\n    s.hash.should eq(s.dup.hash)\n  end\n\n  it \"does hash for struct wrapper (#1940)\" do\n    s = StructSpec::BigIntWrapper.new(BigInt.new(0))\n    s.hash.should eq(s.dup.hash)\n  end\n\n  it \"does dup\" do\n    original = StructSpec::DupCloneStruct.new\n    duplicate = original.dup\n    duplicate.x.should eq(original.x)\n    duplicate.y.should be(original.y)\n\n    original.x = 10\n    duplicate.x.should_not eq(10)\n  end\n\n  it \"clones with def_clone\" do\n    original = StructSpec::DupCloneStruct.new\n    clone = original.clone\n    clone.x.should eq(original.x)\n    clone.y.should_not be(original.y)\n    clone.y.should eq(original.y)\n\n    original.x = 10\n    clone.x.should_not eq(10)\n  end\n\n  it \"should retrieve multiple descendants from hashed data structure\" do\n    foo = StructSpec::FooStruct.new\n    bar = StructSpec::BarStruct.new\n    set = Set{foo, bar}\n    set.should contain(foo)\n    set.should contain(bar)\n  end\nend\n"
  },
  {
    "path": "spec/std/symbol_spec.cr",
    "content": "require \"spec\"\n\ndescribe Symbol do\n  it \"inspects\" do\n    :foo.inspect.should eq(%(:foo))\n    :\"{\".inspect.should eq(%(:\"{\"))\n    :\"hi there\".inspect.should eq(%(:\"hi there\"))\n    :\"1a\".inspect.should eq(%(:\"1a\"))\n    # :かたな.inspect.should eq(%(:かたな))\n  end\n\n  it \"can be compared with another symbol\" do\n    (:foo > :bar).should be_true\n    (:foo < :bar).should be_false\n\n    a = %i(q w e r t y u i o p a s d f g h j k l z x c v b n m)\n    b = %i(a b c d e f g h i j k l m n o p q r s t u v w x y z)\n    a.sort.should eq(b)\n  end\n\n  it \"displays symbols that don't need quotes without quotes\" do\n    a = %i(+ - * / == < <= > >= ! != =~ !~ & | ^ ~ ** &** >> << % [] <=> === []? []=)\n    b = \"[:+, :-, :*, :/, :==, :<, :<=, :>, :>=, :!, :!=, :=~, :!~, :&, :|, :^, :~, :**, :&**, :>>, :<<, :%, :[], :<=>, :===, :[]?, :[]=]\"\n    a.inspect.should eq(b)\n  end\n\n  it \"displays the empty symbol with quotes\" do\n    :\"\".inspect.should eq(%(:\"\"))\n  end\n\n  describe \"clone\" do\n    it { :foo.clone.should eq(:foo) }\n  end\nend\n"
  },
  {
    "path": "spec/std/sync/condition_variable_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"sync/mutex\"\nrequire \"sync/rw_lock\"\nrequire \"sync/condition_variable\"\n\ndescribe Sync::ConditionVariable do\n  it \"#signal\" do\n    m = Sync::Mutex.new\n    c = Sync::ConditionVariable.new(m)\n\n    m.synchronize do\n      spawn do\n        m.synchronize { c.signal }\n      end\n\n      c.wait\n    end\n  end\n\n  it \"#signal mutex\" do\n    m = Sync::Mutex.new\n    c = Sync::ConditionVariable.new(m)\n    done = waiting = 0\n    n = 100\n\n    n.times do\n      spawn do\n        m.synchronize do\n          waiting += 1\n          c.wait\n          done += 1\n        end\n      end\n    end\n    Sync.eventually { waiting.should eq(n) }\n\n    # resume fibers one by one\n    n.times do |i|\n      Sync.eventually { done.should eq(i) }\n      c.signal\n      Fiber.yield\n    end\n\n    Sync.eventually { done.should eq(n) }\n  end\n\n  it \"#signal rwlock\" do\n    l = Sync::RWLock.new\n    c = Sync::ConditionVariable.new(l)\n\n    r = 50\n    w = 10\n    done = waiting = 0\n\n    r.times do\n      spawn do\n        until done == w\n          l.read { c.wait }\n        end\n      end\n    end\n\n    w.times do\n      spawn do\n        l.write do\n          waiting += 1\n          c.wait\n          done += 1\n        end\n      end\n    end\n\n    Sync.eventually { waiting.should eq(w) }\n\n    # resumes at most one writer per signal\n    w.times do |i|\n      Sync.eventually { done.should eq(i) }\n      c.signal\n      Fiber.yield\n    end\n\n    # wake any pending readers\n    c.broadcast\n  end\n\n  it \"#broadcast mutex\" do\n    m = Sync::Mutex.new\n    c = Sync::ConditionVariable.new(m)\n    done = waiting = 0\n\n    100.times do\n      spawn do\n        m.synchronize do\n          waiting += 1\n          c.wait\n          done += 1\n        end\n      end\n    end\n    Sync.eventually { waiting.should eq(100) }\n    done.should eq(0)\n\n    # resume all fibers at once (synchronize so all fibers are waiting)\n    m.synchronize { c.broadcast }\n    Sync.eventually { done.should eq(100) }\n  end\n\n  it \"#broadcast rwlock\" do\n    l = Sync::RWLock.new\n    c = Sync::ConditionVariable.new(l)\n    done = waiting = 0\n    r = 50\n    w = 100\n\n    r.times do |i|\n      spawn(name: \"cv:read_#{i}\") do\n        l.read { c.wait }\n      end\n    end\n\n    w.times do |i|\n      spawn(name: \"cv:write_#{i}\") do\n        l.write do\n          waiting += 1\n          c.wait\n          done += 1\n        end\n      end\n    end\n    Sync.eventually { waiting.should eq(w) }\n    done.should eq(0)\n\n    # resume all fibers at once (lock read so all writers are waiting)\n    l.read { c.broadcast }\n\n    Sync.eventually { done.should eq(w) }\n  end\n\n  it \"producer consumer pattern\" do\n    m = Sync::Mutex.new\n    c = Sync::ConditionVariable.new(m)\n\n    state = -1\n    ready = false\n\n    spawn(name: \"cv:consumer\") do\n      m.synchronize do\n        ready = true\n        c.wait\n        state.should eq(1)\n        state = 2\n      end\n    end\n\n    spawn(name: \"cv:producer\") do\n      Sync.eventually { ready.should be_true, \"expected consumer to eventually be ready\" }\n      m.synchronize { state = 1 }\n      c.signal\n    end\n\n    Sync.eventually { state.should eq(2) }\n  end\n\n  it \"reentrant mutex\" do\n    m = Sync::Mutex.new(:reentrant)\n    c = Sync::ConditionVariable.new(m)\n\n    m.lock\n    m.lock\n\n    spawn do\n      m.lock\n      c.signal\n      m.unlock\n    end\n\n    c.wait\n\n    m.unlock\n    m.unlock # musn't raise (can't unlock Sync::Mutex that isn't locked)\n  end\n\n  it \"reentrant rwlock\" do\n    m = Sync::RWLock.new(:reentrant)\n    c = Sync::ConditionVariable.new(m)\n\n    m.lock_write\n    m.lock_write\n\n    spawn do\n      m.lock_write\n      c.signal\n      m.unlock_write\n    end\n\n    c.wait\n\n    m.unlock_write\n    m.unlock_write # musn't raise (can't unlock Sync::RWLock that isn't locked)\n  end\nend\n"
  },
  {
    "path": "spec/std/sync/exclusive_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"sync/exclusive\"\n\nprivate class Foo\n  INSTANCE = Foo.new\n  class_getter foo = Sync::Exclusive(Int64 | Foo).new(0_i64)\n  @value = 123\nend\n\ndescribe Sync::Exclusive do\n  it \"#lock(&)\" do\n    ary = [1, 2, 3, 4, 5]\n    var = Sync::Exclusive.new(ary)\n    var.lock { |val| val.should be(ary) }\n  end\n\n  it \"#get\" do\n    ary = [1, 2, 3, 4, 5]\n    var = Sync::Exclusive.new(ary)\n    var.get.should be(ary)\n  end\n\n  it \"#set\" do\n    ary1 = [1, 2, 3, 4, 5]\n    ary2 = [4, 5, 8]\n\n    var = Sync::Exclusive.new(ary1)\n    var.set(ary2)\n    var.get.should be(ary2)\n  end\n\n  it \"#replace\" do\n    ary1 = [1, 2, 3, 4, 5]\n    ary2 = [4, 5, 8]\n\n    var = Sync::Exclusive.new(ary1)\n    var.replace do |value|\n      value.should be(ary1)\n      ary2\n    end\n    var.get.should be(ary2)\n  end\n\n  it \"#unsafe_get\" do\n    ary = [1, 2, 3, 4, 5]\n    var = Sync::Exclusive.new(ary)\n    var.unsafe_get.should be(ary)\n  end\n\n  it \"#unsafe_set\" do\n    ary1 = [1, 2, 3, 4, 5]\n    ary2 = [1, 2, 3, 4]\n\n    var = Sync::Exclusive.new(ary1)\n    var.unsafe_set(ary2)\n    var.get.should be(ary2)\n  end\n\n  it \"synchronizes\" do\n    var = Sync::Exclusive.new([] of Int32)\n    wg = WaitGroup.new\n\n    counter = Atomic(Int64).new(0)\n\n    10.times do\n      spawn(name: \"exclusive-read\") do\n        100.times do\n          var.lock do |value|\n            value.each { counter.add(1, :relaxed) }\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    5.times do\n      wg.spawn(name: \"exclusive-write\") do\n        100.times do\n          var.lock do |value|\n            100.times { value << value.size }\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    4.times do\n      wg.spawn(name: \"set-replace\") do\n        50.times do |i|\n          if i % 2 == 1\n            var.set([] of Int32)\n          else\n            var.replace { |value| value[0...10] }\n          end\n          Fiber.yield\n        end\n      end\n\n      wg.spawn(name: \"dup-clone\") do\n        100.times do |i|\n          if i % 2 == 0\n            var.lock(&.dup)\n          else\n            var.lock(&.clone)\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    wg.wait\n\n    counter.get(:relaxed).should be > 0\n  end\n\n  {% if flag?(:execution_context) %}\n    # see https://github.com/crystal-lang/crystal/issues/15085\n    it \"synchronizes reads/writes of mixed unions\" do\n      ready = WaitGroup.new(1)\n      running = true\n      contexts = Array(Fiber::ExecutionContext::Isolated).new(3)\n\n      contexts << Fiber::ExecutionContext::Isolated.new(\"set:foo\") do\n        ready.wait\n        while running\n          Foo.foo.set(Foo::INSTANCE)\n        end\n      end\n\n      contexts << Fiber::ExecutionContext::Isolated.new(\"set:zero\") do\n        ready.wait\n        while running\n          Foo.foo.set(0_i64)\n        end\n      end\n\n      contexts << Fiber::ExecutionContext::Isolated.new(\"get\") do\n        ready.wait\n        while running\n          Foo.foo.lock do |value|\n            case value\n            in Foo\n              value.as(Void*).address.should eq(Foo::INSTANCE.as(Void*).address)\n            in Int64\n              value.should eq(0_i64)\n            end\n          end\n        end\n      end\n\n      ready.done\n\n      sleep 100.milliseconds\n      running = false\n\n      contexts.each(&.wait)\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/sync/mutex_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"sync/mutex\"\n\ndescribe Sync::Mutex do\n  Sync::Type.each do |type|\n    describe type do\n      it \"locks and unlocks\" do\n        state = Atomic.new(0)\n        m = Sync::Mutex.new(type)\n        m.lock\n\n        spawn do\n          state.set(1)\n          m.lock\n          state.set(2)\n        end\n\n        Sync.eventually { state.get.should eq(1) }\n        m.unlock\n        Sync.eventually { state.get.should eq(2) }\n      end\n\n      unless type.unchecked?\n        it \"unlock raises when not locked\" do\n          m = Sync::Mutex.new(type)\n          expect_raises(Sync::Error) { m.unlock }\n        end\n\n        it \"unlock raises when another fiber tries to unlock\" do\n          m = Sync::Mutex.new(:reentrant)\n          m.lock\n\n          Sync.async do\n            expect_raises(Sync::Error) { m.unlock }\n          end\n        end\n      end\n\n      it \"synchronizes\" do\n        m = Sync::Mutex.new(type)\n        counter = 0\n\n        IO.pipe do |r, w|\n          consumer = WaitGroup.new\n          publishers = WaitGroup.new\n\n          # no races when writing to pipe (concurrency)\n          consumer.spawn do\n            c = 0\n            while line = r.gets\n              line.to_i?.should eq(c += 1)\n            end\n          end\n\n          # no races when incrementing counter (parallelism)\n          100.times do |i|\n            publishers.spawn do\n              500.times do\n                m.synchronize do\n                  w.puts (counter += 1).to_s\n                end\n              end\n            end\n          end\n\n          publishers.wait\n          w.close\n          counter.should eq(100 * 500)\n\n          consumer.wait\n        end\n      end\n    end\n  end\n\n  describe \"unchecked\" do\n    it \"hangs on deadlock\" do\n      m = Sync::Mutex.new(:unchecked)\n      done = started = locked = false\n\n      spawn do\n        started = true\n\n        m.lock\n        locked = true\n\n        m.lock # deadlock\n        raise \"ERROR: unreachable\" unless done\n      end\n\n      Sync.eventually { started.should be_true }\n      Sync.eventually { locked.should be_true }\n      sleep 10.milliseconds\n\n      # unlock the fiber (cleanup)\n      done = true\n      m.unlock\n    end\n\n    it \"unlocks from other fiber\" do\n      m = Sync::Mutex.new(:unchecked)\n      m.lock\n      Sync.async { m.unlock }\n    end\n  end\n\n  describe \"checked\" do\n    it \"raises on deadlock\" do\n      m = Sync::Mutex.new(:checked)\n      m.lock\n      expect_raises(Sync::Error::Deadlock) { m.lock }\n    end\n  end\n\n  describe \"reentrant\" do\n    it \"re-locks\" do\n      m = Sync::Mutex.new(:reentrant)\n      m.lock\n      m.lock # nothing raised\n    end\n\n    it \"unlocks as many times as it locked\" do\n      m = Sync::Mutex.new(:reentrant)\n      100.times { m.lock }\n      100.times { m.unlock }\n      expect_raises(Sync::Error) { m.unlock }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/sync/rw_lock_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"sync/rw_lock\"\n\ndescribe Sync::RWLock do\n  it \"lock write waits for all read locks to be unlocked\" do\n    done = false\n\n    lock = Sync::RWLock.new\n    lock.lock_read\n    lock.lock_read\n\n    spawn do\n      lock.lock_write\n      done = true\n    end\n\n    sleep(10.milliseconds)\n    done.should be_false\n\n    lock.unlock_read\n    sleep(10.milliseconds)\n    done.should be_false\n\n    lock.unlock_read\n    sleep(10.milliseconds)\n    done.should be_true\n  end\n\n  it \"can't lock read while locked for write\" do\n    done = false\n\n    lock = Sync::RWLock.new\n    lock.lock_write\n\n    spawn do\n      lock.lock_read\n      done = true\n    end\n\n    sleep(10.milliseconds)\n    done.should be_false\n\n    lock.unlock_write\n    sleep(10.milliseconds)\n    done.should be_true\n  end\n\n  it \"synchronizes locks\" do\n    lock = Sync::RWLock.new\n    wg = WaitGroup.new\n\n    ary = [] of Int32\n    counter = Atomic(Int64).new(0)\n\n    # readers can run concurrently, but are mutually exclusive to writers (the\n    # array can be safely read from):\n\n    10.times do\n      spawn(name: \"reader\") do\n        100.times do\n          lock.read do\n            ary.each { counter.add(1) }\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    # writers are mutually exclusive: they can safely mutate the array\n\n    5.times do\n      wg.spawn(name: \"writer:increment\") do\n        100.times do\n          lock.write { 100.times { ary << ary.size } }\n          Fiber.yield\n        end\n      end\n    end\n\n    4.times do\n      wg.spawn(name: \"writer:decrement\") do\n        100.times do\n          lock.write { 100.times { ary.pop? } }\n          Fiber.yield\n        end\n      end\n    end\n\n    wg.wait\n\n    ary.should eq((0..(ary.size - 1)).to_a)\n    counter.lazy_get.should be > 0\n  end\n\n  describe \"unchecked\" do\n    it \"deadlocks on re-lock write\" do\n      done = started = locked = false\n      lock = Sync::RWLock.new(:unchecked)\n\n      spawn do\n        started = true\n        lock.lock_write\n        locked = true\n        lock.lock_write # deadlock\n        raise \"ERROR: unreachable\" unless done\n      end\n\n      Sync.eventually { started.should be_true }\n      Sync.eventually { locked.should be_true }\n      sleep 10.milliseconds\n\n      # unlock the fiber (cleanup)\n      done = true\n      lock.unlock_write\n    end\n\n    it \"unlocks write despite not being locked\" do\n      lock = Sync::RWLock.new(:unchecked)\n      expect_raises(RuntimeError) { lock.unlock_write } # MU has a safety check\n    end\n\n    it \"unlocks write from another fiber\" do\n      lock = Sync::RWLock.new(:unchecked)\n      lock.lock_write\n      Sync.async { lock.unlock_write } # nothing raised\n    end\n  end\n\n  describe \"checked\" do\n    it \"raises on re-kock write\" do\n      lock = Sync::RWLock.new(:checked)\n      lock.lock_write\n      expect_raises(Sync::Error::Deadlock) { lock.lock_write }\n    end\n\n    it \"raises on unlock_write when not locked\" do\n      lock = Sync::RWLock.new(:checked)\n      expect_raises(Sync::Error) { lock.unlock_write }\n    end\n\n    it \"raises on unlock_write from another fiber\" do\n      lock = Sync::RWLock.new(:checked)\n      lock.lock_write\n      Sync.async do\n        expect_raises(Sync::Error) { lock.unlock_write }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/sync/shared_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"sync/shared\"\n\nprivate class Foo\n  INSTANCE = Foo.new\n  class_getter foo = Sync::Shared(Int64 | Foo).new(0_i64)\n  @value = 123\nend\n\ndescribe Sync::Shared do\n  it \"#shared(&)\" do\n    ary = [1, 2, 3, 4, 5]\n    var = Sync::Shared.new(ary)\n    var.shared { |val| val.should be(ary) }\n  end\n\n  it \"#lock(&)\" do\n    ary1 = [1, 2, 3, 4, 5]\n    var = Sync::Shared.new(ary1)\n    var.lock { |val| val << 6 }\n    var.get.should be(ary1)\n    ary1.should eq([1, 2, 3, 4, 5, 6])\n  end\n\n  it \"#get\" do\n    ary = [1, 2, 3, 4, 5]\n    var = Sync::Shared.new(ary)\n    var.get.should be(ary)\n  end\n\n  it \"#set\" do\n    ary1 = [1, 2, 3, 4, 5]\n    ary2 = [4, 5, 8]\n\n    var = Sync::Shared.new(ary1)\n    var.set(ary2)\n    var.get.should be(ary2)\n  end\n\n  it \"#replace\" do\n    ary1 = [1, 2, 3, 4, 5]\n    ary2 = [4, 5, 8]\n\n    var = Sync::Shared.new(ary1)\n    var.replace do |value|\n      value.should be(ary1)\n      ary2\n    end\n    var.get.should be(ary2)\n  end\n\n  it \"#unsafe_get\" do\n    ary = [1, 2, 3, 4, 5]\n    var = Sync::Shared.new(ary)\n    var.unsafe_get.should be(ary)\n  end\n\n  it \"#unsafe_set\" do\n    ary1 = [1, 2, 3, 4, 5]\n    ary2 = [1, 2, 3, 4]\n\n    var = Sync::Shared.new(ary1)\n    var.unsafe_set(ary2)\n    var.get.should be(ary2)\n  end\n\n  it \"synchronizes\" do\n    var = Sync::Shared.new([] of Int32)\n    wg = WaitGroup.new\n\n    counter = Atomic(Int64).new(0)\n\n    10.times do\n      spawn(name: \"shared-read\") do\n        100.times do\n          var.shared do |value|\n            value.each { counter.add(1, :relaxed) }\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    5.times do\n      wg.spawn(name: \"exclusive-write\") do\n        100.times do\n          var.lock do |value|\n            100.times { value << value.size }\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    4.times do\n      wg.spawn(name: \"set-replace\") do\n        50.times do |i|\n          if i % 2 == 1\n            var.set([] of Int32)\n          else\n            var.replace { |value| value[0...10] }\n          end\n          Fiber.yield\n        end\n      end\n\n      wg.spawn(name: \"dup-clone\") do\n        100.times do |i|\n          if i % 2 == 0\n            var.lock(&.dup)\n          else\n            var.lock(&.clone)\n          end\n          Fiber.yield\n        end\n      end\n    end\n\n    wg.wait\n\n    counter.get(:relaxed).should be > 0\n  end\n\n  {% if flag?(:execution_context) %}\n    # see https://github.com/crystal-lang/crystal/issues/15085\n    it \"synchronizes reads/writes of mixed unions\" do\n      ready = WaitGroup.new(1)\n      running = true\n      contexts = Array(Fiber::ExecutionContext::Isolated).new(3)\n\n      contexts << Fiber::ExecutionContext::Isolated.new(\"set:foo\") do\n        ready.wait\n        while running\n          Foo.foo.set(Foo::INSTANCE)\n        end\n      end\n\n      contexts << Fiber::ExecutionContext::Isolated.new(\"set:zero\") do\n        ready.wait\n        while running\n          Foo.foo.set(0_i64)\n        end\n      end\n\n      contexts << Fiber::ExecutionContext::Isolated.new(\"get\") do\n        ready.wait\n        while running\n          Foo.foo.shared do |value|\n            case value\n            in Foo\n              value.as(Void*).address.should eq(Foo::INSTANCE.as(Void*).address)\n            in Int64\n              value.should eq(0_i64)\n            end\n          end\n        end\n      end\n\n      ready.done\n\n      sleep 100.milliseconds\n      running = false\n\n      contexts.each(&.wait)\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/sync/spec_helper.cr",
    "content": "require \"../spec_helper\"\nrequire \"wait_group\"\n\nmodule Sync\n  def self.eventually(timeout : Time::Span = 1.second, &)\n    start = Time.instant\n\n    loop do\n      Fiber.yield\n\n      begin\n        yield\n      rescue ex\n        raise ex if start.elapsed > timeout\n      else\n        break\n      end\n    end\n  end\n\n  def self.async(&block) : Nil\n    done = false\n    exception = nil\n\n    spawn do\n      block.call\n    rescue ex\n      exception = ex\n    ensure\n      done = true\n    end\n\n    eventually { done.should be_true, \"Expected async fiber to have finished\" }\n\n    if ex = exception\n      raise ex\n    end\n  end\n\n  module FakeContext\n    def self.spawn(*, name : String? = nil, &block : ->) : Fiber\n      ::spawn(name: name, &block)\n    end\n  end\n\n  CONCURRENT =\n    {% if flag?(:execution_context) %}\n      ctx = Fiber::ExecutionContext.current\n      if ctx.is_a?(Fiber::ExecutionContext::Parallel) && ctx.capacity > 1\n        ctx = Fiber::ExecutionContext::Concurrent.new(\"CONCURRENT\")\n      end\n      ctx\n    {% else %}\n      FakeContext\n    {% end %}\nend\n"
  },
  {
    "path": "spec/std/syscall_spec.cr",
    "content": "{% skip_file unless flag?(:linux) && !flag?(:interpreted) %}\n\nrequire \"spec\"\nrequire \"syscall\"\n\nprivate module TestSyscall\n  Syscall.def_syscall pipe2, Int32, pipefd : Int32[2]*, flags : Int32\n  Syscall.def_syscall write, Int32, fd : Int32, buf : UInt8*, count : LibC::SizeT\n  Syscall.def_syscall read, Int32, fd : Int32, buf : UInt8*, count : LibC::SizeT\n  Syscall.def_syscall close, Int32, fd : Int32\nend\n\ndescribe Syscall do\n  it \"can call into the system successfully\" do\n    pair = uninitialized Int32[2]\n    TestSyscall.pipe2(pointerof(pair), 0).should eq(0)\n\n    str = \"Hello\"\n    TestSyscall.write(pair[1], str.to_unsafe, LibC::SizeT.new(str.bytesize)).should eq(str.bytesize)\n\n    buf = Bytes.new(64)\n    TestSyscall.read(pair[0], buf.to_unsafe, LibC::SizeT.new(buf.size)).should eq(str.bytesize)\n\n    String.new(buf.to_unsafe, str.bytesize).should eq(str)\n\n    TestSyscall.close(pair[0]).should eq(0)\n    TestSyscall.close(pair[1]).should eq(0)\n  end\nend\n"
  },
  {
    "path": "spec/std/system/group_spec.cr",
    "content": "require \"spec\"\nrequire \"system/group\"\n\n{% if flag?(:win32) %}\n  GROUP_NAME = \"BUILTIN\\\\Administrators\"\n  GROUP_ID   = \"S-1-5-32-544\"\n{% else %}\n  GROUP_NAME = {{ `id -gn`.stringify.chomp }}\n  GROUP_ID   = {{ `id -g`.stringify.chomp }}\n{% end %}\n\nINVALID_GROUP_NAME = \"this_group_does_not_exist\"\nINVALID_GROUP_ID   = {% if flag?(:android) %}\"8888\"{% else %}\"1234567\"{% end %}\n\ndescribe System::Group do\n  describe \".find_by(*, name)\" do\n    it \"returns a group by name\" do\n      group = System::Group.find_by(name: GROUP_NAME)\n\n      group.should be_a(System::Group)\n      group.name.should eq(GROUP_NAME)\n      group.id.should eq(GROUP_ID)\n    end\n\n    it \"raises on nonexistent group\" do\n      expect_raises System::Group::NotFoundError, \"No such group\" do\n        System::Group.find_by(name: INVALID_GROUP_NAME)\n      end\n    end\n  end\n\n  describe \".find_by(*, id)\" do\n    it \"returns a group by id\" do\n      group = System::Group.find_by(id: GROUP_ID)\n\n      group.should be_a(System::Group)\n      group.id.should eq(GROUP_ID)\n      group.name.should eq(GROUP_NAME)\n    end\n\n    it \"raises on nonexistent group name\" do\n      expect_raises System::Group::NotFoundError, \"No such group\" do\n        System::Group.find_by(id: INVALID_GROUP_ID)\n      end\n    end\n  end\n\n  describe \".find_by?(*, name)\" do\n    it \"returns a group by name\" do\n      group = System::Group.find_by?(name: GROUP_NAME).not_nil!\n\n      group.should be_a(System::Group)\n      group.name.should eq(GROUP_NAME)\n      group.id.should eq(GROUP_ID)\n    end\n\n    it \"returns nil on nonexistent group\" do\n      group = System::Group.find_by?(name: INVALID_GROUP_NAME)\n      group.should be_nil\n    end\n  end\n\n  describe \".find_by?(*, id)\" do\n    it \"returns a group by id\" do\n      group = System::Group.find_by?(id: GROUP_ID).not_nil!\n\n      group.should be_a(System::Group)\n      group.id.should eq(GROUP_ID)\n      group.name.should eq(GROUP_NAME)\n    end\n\n    it \"returns nil on nonexistent group id\" do\n      group = System::Group.find_by?(id: INVALID_GROUP_ID)\n      group.should be_nil\n    end\n  end\n\n  describe \"#name\" do\n    it \"is the same as the source name\" do\n      System::Group.find_by(name: GROUP_NAME).name.should eq(GROUP_NAME)\n    end\n  end\n\n  describe \"#id\" do\n    it \"is the same as the source ID\" do\n      System::Group.find_by(id: GROUP_ID).id.should eq(GROUP_ID)\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"returns a string representation\" do\n      System::Group.find_by(name: GROUP_NAME).to_s.should eq(\"#{GROUP_NAME} (#{GROUP_ID})\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/system/user_spec.cr",
    "content": "require \"spec\"\nrequire \"system/user\"\n\n{% if flag?(:win32) %}\n  {% parts = `whoami /USER /FO TABLE /NH`.stringify.chomp.split(\" \") %}\n  USER_NAME = {{ parts[0..-2].join(\" \") }}\n  USER_ID   = {{ parts[-1] }}\n{% else %}\n  USER_NAME = {{ `id -un`.stringify.chomp }}\n  USER_ID   = {{ `id -u`.stringify.chomp }}\n{% end %}\n\nINVALID_USER_NAME = \"this_user_does_not_exist\"\nINVALID_USER_ID   = {% if flag?(:android) %}\"8888\"{% else %}\"1234567\"{% end %}\n\ndef normalized_username(username)\n  # on Windows, domain names are case-insensitive, so we unify the letter case\n  # from sources like `whoami`, `hostname`, or Win32 APIs\n  {% if flag?(:win32) %}\n    username.upcase\n  {% else %}\n    username\n  {% end %}\nend\n\ndescribe System::User do\n  describe \".find_by(*, name)\" do\n    it \"returns a user by name\" do\n      user = System::User.find_by(name: USER_NAME)\n\n      user.should be_a(System::User)\n      normalized_username(user.username).should eq(normalized_username(USER_NAME))\n      user.id.should eq(USER_ID)\n    end\n\n    it \"raises on a nonexistent user\" do\n      expect_raises System::User::NotFoundError, \"No such user\" do\n        System::User.find_by(name: INVALID_USER_NAME)\n      end\n    end\n  end\n\n  describe \".find_by(*, id)\" do\n    it \"returns a user by id\" do\n      user = System::User.find_by(id: USER_ID)\n\n      user.should be_a(System::User)\n      user.id.should eq(USER_ID)\n      normalized_username(user.username).should eq(normalized_username(USER_NAME))\n    end\n\n    it \"raises on nonexistent user id\" do\n      expect_raises System::User::NotFoundError, \"No such user\" do\n        System::User.find_by(id: INVALID_USER_ID)\n      end\n    end\n  end\n\n  describe \".find_by?(*, name)\" do\n    it \"returns a user by name\" do\n      user = System::User.find_by?(name: USER_NAME).not_nil!\n\n      user.should be_a(System::User)\n      normalized_username(user.username).should eq(normalized_username(USER_NAME))\n      user.id.should eq(USER_ID)\n    end\n\n    it \"returns nil on nonexistent user\" do\n      user = System::User.find_by?(name: INVALID_USER_NAME)\n      user.should be_nil\n    end\n  end\n\n  describe \".find_by?(*, id)\" do\n    it \"returns a user by id\" do\n      user = System::User.find_by?(id: USER_ID).not_nil!\n\n      user.should be_a(System::User)\n      user.id.should eq(USER_ID)\n      normalized_username(user.username).should eq(normalized_username(USER_NAME))\n    end\n\n    it \"returns nil on nonexistent user id\" do\n      user = System::User.find_by?(id: INVALID_USER_ID)\n      user.should be_nil\n    end\n  end\n\n  describe \"#username\" do\n    it \"is the same as the source name\" do\n      user = System::User.find_by(name: USER_NAME)\n      normalized_username(user.username).should eq(normalized_username(USER_NAME))\n    end\n  end\n\n  describe \"#id\" do\n    it \"is the same as the source ID\" do\n      System::User.find_by(id: USER_ID).id.should eq(USER_ID)\n    end\n  end\n\n  describe \"#group_id\" do\n    it \"calls without raising\" do\n      System::User.find_by(name: USER_NAME).group_id\n    end\n  end\n\n  describe \"#name\" do\n    it \"calls without raising\" do\n      System::User.find_by(name: USER_NAME).name\n    end\n  end\n\n  describe \"#home_directory\" do\n    it \"calls without raising\" do\n      System::User.find_by(name: USER_NAME).home_directory\n    end\n  end\n\n  describe \"#shell\" do\n    it \"calls without raising\" do\n      System::User.find_by(name: USER_NAME).shell\n    end\n  end\n\n  describe \"#to_s\" do\n    it \"returns a string representation\" do\n      user = System::User.find_by(name: USER_NAME)\n      user.to_s.should eq(\"#{user.username} (#{user.id})\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/system_error_spec.cr",
    "content": "require \"spec\"\n\ndescribe SystemError do\n  describe \".from_os_error\" do\n    it \"Can build an error from an errno\" do\n      errno = Errno::ENOENT\n      error = ::RuntimeError.from_os_error(message: nil, os_error: errno)\n      error.message.should eq(\"No such file or directory\")\n    end\n  end\n\n  describe \".from_errno\" do\n    it \"captures `Errno.value`\" do\n      Errno.value = :ENOENT\n      error = ::RuntimeError.from_errno \"foobar\"\n      error.os_error.should eq Errno::ENOENT\n    end\n\n    it \"avoid reset from message\" do\n      Errno.value = :ENOENT\n      error = ::RuntimeError.from_errno(\"foobar\".tap { Errno.value = :EPERM })\n      error.os_error.should eq Errno::ENOENT\n    end\n  end\n\n  {% if flag?(:win32) %}\n    describe \".from_winerror\" do\n      it \"avoid reset from message\" do\n        WinError.value = :ERROR_FILE_NOT_FOUND\n        error = ::RuntimeError.from_winerror(\"foobar\".tap { WinError.value = :ERROR_ACCESS_DENIED })\n        error.os_error.should eq WinError::ERROR_FILE_NOT_FOUND\n      end\n    end\n\n    describe \".from_wsa_error\" do\n      it \"avoid reset from message\" do\n        WinError.wsa_value = :ERROR_FILE_NOT_FOUND\n        error = ::RuntimeError.from_wsa_error(\"foobar\".tap { WinError.wsa_value = :ERROR_ACCESS_DENIED })\n        error.os_error.should eq WinError::ERROR_FILE_NOT_FOUND\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/system_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"system\"\n\ndescribe System do\n  describe \"hostname\" do\n    # can't use backtick in interpreted code (#12241)\n    pending_interpreted \"returns current hostname\" do\n      shell_hostname = `hostname`.strip\n      pending! \"`hostname` command was unsuccessful\" unless $?.success?\n\n      hostname = System.hostname\n      hostname.should eq(shell_hostname)\n    end\n  end\n\n  describe \"cpu_count\" do\n    # can't use backtick in interpreted code (#12241)\n    pending_interpreted \"returns current CPU count\" do\n      shell_cpus =\n        {% if flag?(:win32) %}\n          ENV[\"NUMBER_OF_PROCESSORS\"].to_i\n        {% elsif flag?(:unix) %}\n          `getconf _NPROCESSORS_ONLN 2>/dev/null || nproc --all 2>/dev/null || grep -sc '^processor' /proc/cpuinfo || sysctl -n hw.ncpu 2>/dev/null`.to_i\n        {% end %}\n      cpu_count = System.cpu_count\n      cpu_count.should be_a(Int32)\n      cpu_count.should eq(shell_cpus)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/thread/condition_variable_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/thread\"\n\n# interpreter doesn't support threads yet (#14287)\npending_interpreted describe: Thread::ConditionVariable do\n  it \"signals\" do\n    mutex = Thread::Mutex.new\n    cond = Thread::ConditionVariable.new\n\n    mutex.synchronize do\n      Thread.new do\n        mutex.synchronize { cond.signal }\n      end\n\n      cond.wait(mutex)\n    end\n  end\n\n  it \"signals & broadcasts\" do\n    mutex = Thread::Mutex.new\n    cv1 = Thread::ConditionVariable.new\n    cv2 = Thread::ConditionVariable.new\n    waiting = 0\n\n    5.times do\n      Thread.new do\n        mutex.synchronize do\n          waiting += 1\n          cv1.wait(mutex)\n\n          waiting -= 1\n          cv2.signal\n        end\n      end\n    end\n\n    # wait for all threads to have acquired the mutex and be waiting on the\n    # condition variable, otherwise the main thread could acquire the lock\n    # first, and signal into the void.\n    #\n    # since increments to waiting are synchronized, at least 4 threads are\n    # guaranteed to be waiting when waiting is 5, which is enough for further\n    # tests to never hangup.\n    until waiting == 5\n      Thread.yield\n    end\n\n    # wakes up at least 1 thread:\n    mutex.synchronize do\n      cv1.signal\n      cv2.wait(mutex)\n    end\n    waiting.should be < 5\n\n    # wakes up all waiting threads:\n    mutex.synchronize do\n      cv1.broadcast\n\n      until waiting == 0\n        cv2.wait(mutex, 1.second) { raise \"unexpected wait timeout\" }\n      end\n    end\n  end\n\n  it \"timeouts\" do\n    timedout = false\n    mutex = Thread::Mutex.new\n    cond = Thread::ConditionVariable.new\n\n    mutex.synchronize do\n      cond.wait(mutex, 1.microsecond) { timedout = true }\n    end\n    timedout.should be_true\n  end\n\n  it \"resumes before timeout\" do\n    timedout = false\n    mutex = Thread::Mutex.new\n    cond = Thread::ConditionVariable.new\n\n    mutex.synchronize do\n      Thread.new do\n        mutex.synchronize { cond.signal }\n      end\n\n      cond.wait(mutex, 1.second) { timedout = true }\n    end\n\n    timedout.should be_false\n  end\nend\n"
  },
  {
    "path": "spec/std/thread/mutex_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/thread\"\n\n# interpreter doesn't support threads yet (#14287)\npending_interpreted describe: Thread::Mutex do\n  it \"synchronizes\" do\n    a = 0\n    mutex = Thread::Mutex.new\n\n    threads = Array.new(10) do\n      Thread.new do\n        mutex.synchronize { a += 1 }\n      end\n    end\n\n    threads.each(&.join)\n    a.should eq(10)\n  end\n\n  it \"won't lock recursively\" do\n    mutex = Thread::Mutex.new\n    mutex.try_lock.should be_true\n    mutex.try_lock.should be_false\n    expect_raises(RuntimeError) { mutex.lock }\n    mutex.unlock\n    Thread.new { mutex.synchronize { } }.join\n  end\n\n  it \"won't unlock from another thread\" do\n    mutex = Thread::Mutex.new\n    mutex.lock\n\n    expect_raises(RuntimeError) do\n      Thread.new { mutex.unlock }.join\n    end\n\n    mutex.unlock\n  end\nend\n"
  },
  {
    "path": "spec/std/thread_spec.cr",
    "content": "require \"./spec_helper\"\nrequire \"../support/thread\"\n\n# interpreter doesn't support threads yet (#14287)\npending_interpreted describe: Thread do\n  it \"allows passing an argumentless fun to execute\" do\n    a = 0\n    thread = Thread.new { a = 1; 10 }\n    thread.join\n    a.should eq(1)\n  end\n\n  it \"raises inside thread and gets it on join\" do\n    thread = Thread.new { raise \"OH NO\" }\n    expect_raises Exception, \"OH NO\" do\n      thread.join\n    end\n  end\n\n  it \"returns current thread object\" do\n    current = nil\n    thread = Thread.new { current = Thread.current }\n    thread.join\n    current.should be(thread)\n    current.should_not be(Thread.current)\n  ensure\n    # avoids a \"GC Warning: Finalization cycle\" caused by *current*\n    # referencing the thread itself, preventing the finalizer to run:\n    current = nil\n  end\n\n  it \"yields the processor\" do\n    done = false\n\n    thread = Thread.new do\n      3.times { Thread.yield }\n      done = true\n    end\n\n    until done\n      Thread.yield\n    end\n\n    thread.join\n  end\n\n  it \"names the thread\" do\n    {% if flag?(:execution_context) %}\n      Thread.current.name.should match(/DEFAULT-\\d+/)\n    {% else %}\n      Thread.current.name.should be_nil\n    {% end %}\n\n    name = nil\n    thread = Thread.new(name: \"some-name\") do\n      name = Thread.current.name\n    end\n    thread.name.should eq(\"some-name\")\n\n    thread.join\n    name.should eq(\"some-name\")\n  end\nend\n"
  },
  {
    "path": "spec/std/time/custom_formats_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Time::Format\" do\n  describe \"RFC_3339\" do\n    it \"parses regular format\" do\n      time = Time.utc(2016, 2, 15)\n      Time::Format::RFC_3339.format(time).should eq \"2016-02-15T00:00:00Z\"\n      Time::Format::RFC_3339.format(Time.local(2016, 2, 15, location: Time::Location.fixed(3600))).should eq \"2016-02-15T00:00:00+01:00\"\n      Time::Format::RFC_3339.parse(\"2016-02-15T00:00:00+00:00\").should eq time\n      Time::Format::RFC_3339.parse(\"2016-02-15t00:00:00+00:00\").should eq time\n      Time::Format::RFC_3339.parse(\"2016-02-15 00:00:00+00:00\").should eq time\n      Time::Format::RFC_3339.parse(\"2016-02-15T00:00:00Z\").should eq time\n      Time::Format::RFC_3339.parse(\"2016-02-15T00:00:00.0000000+00:00\").should eq time\n    end\n  end\n\n  describe \"RFC_2822\" do\n    it \"parses regular format\" do\n      time = Time.utc(2016, 2, 15)\n      Time::Format::RFC_2822.format(time).should eq \"Mon, 15 Feb 2016 00:00:00 +0000\"\n      Time::Format::RFC_2822.parse(\"Mon, 15 Feb 2016 00:00:00 +0000\").should eq time\n      Time::Format::RFC_2822.parse(\"Mon, 15 Feb 16 00:00 UT\").should eq time\n      Time::Format::RFC_2822.parse(\" Mon , 14 Feb 2016 20 : 00 : 00 EDT (comment)\").to_utc.should eq time\n    end\n  end\n\n  describe \"ISO_8601_DATE\" do\n    it \"formats default format\" do\n      time = Time.utc(1985, 4, 12)\n      Time::Format::ISO_8601_DATE.format(time).should eq \"1985-04-12\"\n    end\n\n    it \"parses calendar date\" do\n      time = Time.utc(1985, 4, 12)\n      Time::Format::ISO_8601_DATE.parse(\"1985-04-12\").should eq(time)\n      Time::Format::ISO_8601_DATE.parse(\"19850412\").should eq(time)\n    end\n\n    it \"parses ordinal date\" do\n      time = Time.utc(1985, 4, 12)\n      Time::Format::ISO_8601_DATE.parse(\"1985-102\").should eq(time)\n      Time::Format::ISO_8601_DATE.parse(\"1985102\").should eq(time)\n    end\n\n    it \"parses week date\" do\n      time = Time.utc(1985, 4, 12)\n      Time::Format::ISO_8601_DATE.parse(\"1985-W15-5\").should eq(time)\n      Time::Format::ISO_8601_DATE.parse(\"1985W155\").should eq(time)\n\n      Time::Format::ISO_8601_DATE.parse(\"2004-W53-6\").should eq(Time.utc(2005, 1, 1))\n      Time::Format::ISO_8601_DATE.parse(\"2004-W53-7\").should eq(Time.utc(2005, 1, 2))\n      Time::Format::ISO_8601_DATE.parse(\"2005-W52-6\").should eq(Time.utc(2005, 12, 31))\n      Time::Format::ISO_8601_DATE.parse(\"2005-W52-7\").should eq(Time.utc(2006, 1, 1))\n      Time::Format::ISO_8601_DATE.parse(\"2006-W01-1\").should eq(Time.utc(2006, 1, 2))\n      Time::Format::ISO_8601_DATE.parse(\"2006-W52-7\").should eq(Time.utc(2006, 12, 31))\n      Time::Format::ISO_8601_DATE.parse(\"2007-W01-1\").should eq(Time.utc(2007, 1, 1))\n      Time::Format::ISO_8601_DATE.parse(\"2007-W52-7\").should eq(Time.utc(2007, 12, 30))\n      Time::Format::ISO_8601_DATE.parse(\"2008-W01-1\").should eq(Time.utc(2007, 12, 31))\n      Time::Format::ISO_8601_DATE.parse(\"2008-W01-2\").should eq(Time.utc(2008, 1, 1))\n      Time::Format::ISO_8601_DATE.parse(\"2008-W52-7\").should eq(Time.utc(2008, 12, 28))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W01-1\").should eq(Time.utc(2008, 12, 29))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W01-2\").should eq(Time.utc(2008, 12, 30))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W01-3\").should eq(Time.utc(2008, 12, 31))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W01-4\").should eq(Time.utc(2009, 1, 1))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W53-4\").should eq(Time.utc(2009, 12, 31))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W53-5\").should eq(Time.utc(2010, 1, 1))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W53-6\").should eq(Time.utc(2010, 1, 2))\n      Time::Format::ISO_8601_DATE.parse(\"2009-W53-7\").should eq(Time.utc(2010, 1, 3))\n    end\n  end\n\n  describe \"ISO_8601_DATE_TIME\" do\n    it \"formats default format\" do\n      time = Time.utc(1985, 4, 12, 23, 20, 50)\n      Time::Format::ISO_8601_DATE_TIME.format(time).should eq \"1985-04-12T23:20:50Z\"\n    end\n\n    it \"parses calendar date\" do\n      time = Time.utc(1985, 4, 12, 23, 20, 50)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-04-12T23:20:50Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"19850412T232050Z\").should eq(time)\n    end\n\n    it \"parses ordinal date\" do\n      time = Time.utc(1985, 4, 12, 23, 20, 50)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-102T23:20:50Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985102T232050Z\").should eq(time)\n    end\n\n    it \"parses hour:minutes\" do\n      time = Time.utc(1985, 4, 12, 23, 20)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-102T23:20Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985102T2320Z\").should eq(time)\n    end\n\n    it \"parses decimal fractions\" do\n      time = Time.utc(1985, 4, 12, 23, 30)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-4-12T23.5Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-4-12T23.5Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-4-12T23.50000000000Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-4-12T23.50000000000Z\").should eq(time)\n    end\n\n    it \"parses hour\" do\n      time = Time.utc(1985, 4, 12, 23)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-102T23Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985102T23Z\").should eq(time)\n    end\n\n    it \"week date\" do\n      time = Time.utc(1985, 4, 12, 23, 20, 50)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985-W15-5T23:20:50Z\").should eq(time)\n      Time::Format::ISO_8601_DATE_TIME.parse(\"1985W155T23:20:50Z\").should eq(time)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/time/format_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/time\"\nrequire \"spec/helpers/string\"\n\ndef parse_time(format, string)\n  Time.parse_utc(format, string)\nend\n\ndef parse_time(string)\n  Time.parse_utc(string, \"%F %T.%N\")\nend\n\ndescribe Time::Format do\n  it \"formats\" do\n    with_zoneinfo do\n      t = Time.utc 2014, 1, 2, 3, 4, 5, nanosecond: 6_000_000\n      t2 = Time.utc 2014, 1, 2, 15, 4, 5, nanosecond: 6_000_000\n      t3 = Time.utc 2014, 1, 2, 12, 4, 5, nanosecond: 6_000_000\n\n      assert_prints t.to_s(\"%Y\"), \"2014\"\n      assert_prints Time.utc(1, 1, 2, 3, 4, 5, nanosecond: 6).to_s(\"%Y\"), \"0001\"\n\n      assert_prints t.to_s(\"%C\"), \"20\"\n      assert_prints t.to_s(\"%y\"), \"14\"\n      assert_prints t.to_s(\"%m\"), \"01\"\n      assert_prints t.to_s(\"%_m\"), \" 1\"\n      assert_prints t.to_s(\"%_%_m2\"), \"%_ 12\"\n      assert_prints t.to_s(\"%-m\"), \"1\"\n      assert_prints t.to_s(\"%-%-m2\"), \"%-12\"\n      assert_prints t.to_s(\"%B\"), \"January\"\n      assert_prints t.to_s(\"%^B\"), \"JANUARY\"\n      assert_prints t.to_s(\"%^%^B2\"), \"%^JANUARY2\"\n      assert_prints t.to_s(\"%b\"), \"Jan\"\n      assert_prints t.to_s(\"%^b\"), \"JAN\"\n      assert_prints t.to_s(\"%h\"), \"Jan\"\n      assert_prints t.to_s(\"%^h\"), \"JAN\"\n      assert_prints t.to_s(\"%d\"), \"02\"\n      assert_prints t.to_s(\"%-d\"), \"2\"\n      assert_prints t.to_s(\"%e\"), \" 2\"\n      assert_prints t.to_s(\"%j\"), \"002\"\n      assert_prints t.to_s(\"%H\"), \"03\"\n\n      assert_prints t.to_s(\"%k\"), \" 3\"\n      assert_prints t2.to_s(\"%k\"), \"15\"\n\n      assert_prints t.to_s(\"%I\"), \"03\"\n      assert_prints t2.to_s(\"%I\"), \"03\"\n      assert_prints t3.to_s(\"%I\"), \"12\"\n\n      assert_prints t.to_s(\"%l\"), \" 3\"\n      assert_prints t2.to_s(\"%l\"), \" 3\"\n      assert_prints t3.to_s(\"%l\"), \"12\"\n\n      # Note: we purposely match %p to am/pm and %P to AM/PM (makes more sense)\n      assert_prints t.to_s(\"%p\"), \"am\"\n      assert_prints t2.to_s(\"%p\"), \"pm\"\n\n      assert_prints t.to_s(\"%P\"), \"AM\"\n      assert_prints t2.to_s(\"%P\"), \"PM\"\n\n      assert_prints t.to_s(\"%M\"), \"04\"\n      assert_prints t.to_s(\"%S\"), \"05\"\n      assert_prints t.to_s(\"%L\"), \"006\"\n      assert_prints t.to_s(\"%N\"), \"006000000\"\n      assert_prints t.to_s(\"%3N\"), \"006\"\n      assert_prints t.to_s(\"%6N\"), \"006000\"\n      assert_prints t.to_s(\"%9N\"), \"006000000\"\n\n      assert_prints t.to_s(\"%z\"), \"+0000\"\n      assert_prints t.to_s(\"%:z\"), \"+00:00\"\n      assert_prints t.to_s(\"%::z\"), \"+00:00:00\"\n      assert_prints t.to_s(\"%^Z\"), \"UTC\"\n      assert_prints t.to_s(\"%Z\"), \"UTC\"\n\n      with_zoneinfo do\n        zoned = Time.local(2017, 11, 24, 13, 5, 6, location: Time::Location.load(\"Europe/Berlin\"))\n        assert_prints zoned.to_s(\"%z\"), \"+0100\"\n        assert_prints zoned.to_s(\"%:z\"), \"+01:00\"\n        assert_prints zoned.to_s(\"%::z\"), \"+01:00:00\"\n        assert_prints zoned.to_s(\"%^Z\"), \"CET\"\n        assert_prints zoned.to_s(\"%Z\"), \"Europe/Berlin\"\n\n        zoned = Time.local(2017, 11, 24, 13, 5, 6, location: Time::Location.load(\"America/Argentina/Buenos_Aires\"))\n        assert_prints zoned.to_s(\"%z\"), \"-0300\"\n        assert_prints zoned.to_s(\"%:z\"), \"-03:00\"\n        assert_prints zoned.to_s(\"%::z\"), \"-03:00:00\"\n        assert_prints zoned.to_s(\"%^Z\"), \"-03\"\n        assert_prints zoned.to_s(\"%Z\"), \"America/Argentina/Buenos_Aires\"\n      end\n\n      offset = Time.local(2017, 11, 24, 13, 5, 6, location: Time::Location.fixed(9000))\n      assert_prints offset.to_s(\"%z\"), \"+0230\"\n      assert_prints offset.to_s(\"%:z\"), \"+02:30\"\n      assert_prints offset.to_s(\"%::z\"), \"+02:30:00\"\n      assert_prints offset.to_s(\"%^Z\"), \"+02:30\"\n      assert_prints offset.to_s(\"%Z\"), \"+02:30\"\n\n      offset = Time.local(2017, 11, 24, 13, 5, 6, location: Time::Location.fixed(9001))\n      assert_prints offset.to_s(\"%z\"), \"+0230\"\n      assert_prints offset.to_s(\"%:z\"), \"+02:30\"\n      assert_prints offset.to_s(\"%::z\"), \"+02:30:01\"\n      assert_prints offset.to_s(\"%^Z\"), \"+02:30:01\"\n      assert_prints offset.to_s(\"%Z\"), \"+02:30:01\"\n\n      assert_prints t.to_s(\"%A\"), \"Thursday\"\n      assert_prints t.to_s(\"%^A\"), \"THURSDAY\"\n      assert_prints t.to_s(\"%a\"), \"Thu\"\n      assert_prints t.to_s(\"%^a\"), \"THU\"\n      assert_prints t.to_s(\"%u\"), \"4\"\n      assert_prints t.to_s(\"%w\"), \"4\"\n\n      t4 = Time.utc 2014, 1, 5 # A Sunday\n      assert_prints t4.to_s(\"%u\"), \"7\"\n      assert_prints t4.to_s(\"%w\"), \"0\"\n\n      assert_prints Time.utc(1985, 4, 12).to_s(\"%G-W%V-%u\"), \"1985-W15-5\"\n      assert_prints Time.utc(2005, 1, 1).to_s(\"%G-W%V-%u\"), \"2004-W53-6\"\n      assert_prints Time.utc(2005, 1, 2).to_s(\"%G-W%V-%u\"), \"2004-W53-7\"\n      assert_prints Time.utc(2005, 12, 31).to_s(\"%G-W%V-%u\"), \"2005-W52-6\"\n      assert_prints Time.utc(2006, 1, 1).to_s(\"%G-W%V-%u\"), \"2005-W52-7\"\n      assert_prints Time.utc(2006, 1, 2).to_s(\"%G-W%V-%u\"), \"2006-W01-1\"\n      assert_prints Time.utc(2006, 12, 31).to_s(\"%G-W%V-%u\"), \"2006-W52-7\"\n      assert_prints Time.utc(2007, 1, 1).to_s(\"%G-W%V-%u\"), \"2007-W01-1\"\n      assert_prints Time.utc(2007, 12, 30).to_s(\"%G-W%V-%u\"), \"2007-W52-7\"\n      assert_prints Time.utc(2007, 12, 31).to_s(\"%G-W%V-%u\"), \"2008-W01-1\"\n      assert_prints Time.utc(2008, 1, 1).to_s(\"%G-W%V-%u\"), \"2008-W01-2\"\n      assert_prints Time.utc(2008, 12, 28).to_s(\"%G-W%V-%u\"), \"2008-W52-7\"\n      assert_prints Time.utc(2008, 12, 29).to_s(\"%G-W%V-%u\"), \"2009-W01-1\"\n      assert_prints Time.utc(2008, 12, 30).to_s(\"%G-W%V-%u\"), \"2009-W01-2\"\n      assert_prints Time.utc(2008, 12, 31).to_s(\"%G-W%V-%u\"), \"2009-W01-3\"\n      assert_prints Time.utc(2009, 1, 1).to_s(\"%G-W%V-%u\"), \"2009-W01-4\"\n      assert_prints Time.utc(2009, 12, 31).to_s(\"%G-W%V-%u\"), \"2009-W53-4\"\n      assert_prints Time.utc(2010, 1, 1).to_s(\"%G-W%V-%u\"), \"2009-W53-5\"\n      assert_prints Time.utc(2010, 1, 2).to_s(\"%G-W%V-%u\"), \"2009-W53-6\"\n      assert_prints Time.utc(2010, 1, 3).to_s(\"%G-W%V-%u\"), \"2009-W53-7\"\n      assert_prints Time.utc(1985, 4, 12).to_s(\"%g-W%V-%u\"), \"85-W15-5\"\n      # TODO %U\n      # TODO %W\n      # TODO %s\n      assert_prints t.to_s(\"%n\"), \"\\n\"\n      assert_prints t.to_s(\"%t\"), \"\\t\"\n      # TODO %%\n\n      assert_prints t.to_s(\"%%\"), \"%\"\n      assert_prints t.to_s(\"%c\"), t.to_s(\"%a %b %e %T %Y\")\n      assert_prints t.to_s(\"%D\"), t.to_s(\"%m/%d/%y\")\n      assert_prints t.to_s(\"%F\"), t.to_s(\"%Y-%m-%d\")\n      # TODO %v\n      assert_prints t.to_s(\"%x\"), t.to_s(\"%D\")\n      assert_prints t.to_s(\"%X\"), t.to_s(\"%T\")\n      assert_prints t.to_s(\"%r\"), t.to_s(\"%I:%M:%S %P\")\n      assert_prints t.to_s(\"%R\"), t.to_s(\"%H:%M\")\n      assert_prints t.to_s(\"%T\"), t.to_s(\"%H:%M:%S\")\n\n      assert_prints t.to_s(\"%Y-%m-hello\"), \"2014-01-hello\"\n\n      t5 = Time.utc 2014, 1, 2, 3, 4, 5, nanosecond: 6\n      assert_prints t5.to_s(\"%s\"), \"1388631845\"\n    end\n  end\n\n  it \"formats standard formats\" do\n    time = Time.utc(2016, 2, 15)\n    assert_prints time.to_rfc3339, \"2016-02-15T00:00:00Z\"\n    Time.parse_rfc3339(time.to_rfc3339).should eq time\n    assert_prints time.to_rfc2822, \"Mon, 15 Feb 2016 00:00:00 +0000\"\n    Time.parse_rfc2822(time.to_rfc2822).should eq time\n  end\n\n  it \"formats rfc3339 with different fraction digits\" do\n    time = Time.utc(2016, 2, 15, 8, 23, 45, nanosecond: 123456789)\n    assert_prints time.to_rfc3339, \"2016-02-15T08:23:45Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 0), \"2016-02-15T08:23:45Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 3), \"2016-02-15T08:23:45.123Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 6), \"2016-02-15T08:23:45.123456Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 9), \"2016-02-15T08:23:45.123456789Z\"\n    expect_raises(ArgumentError, \"Invalid fraction digits: 5\") { time.to_rfc3339(fraction_digits: 5) }\n    expect_raises(ArgumentError, \"Invalid fraction digits: -1\") { time.to_rfc3339(fraction_digits: -1) }\n\n    time = Time.utc(2016, 2, 15, 8, 23, 45)\n    assert_prints time.to_rfc3339, \"2016-02-15T08:23:45Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 0), \"2016-02-15T08:23:45Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 3), \"2016-02-15T08:23:45.000Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 6), \"2016-02-15T08:23:45.000000Z\"\n    assert_prints time.to_rfc3339(fraction_digits: 9), \"2016-02-15T08:23:45.000000000Z\"\n  end\n\n  it \"parses empty\" do\n    t = Time.parse(\"\", \"\", Time::Location.local)\n    t.year.should eq(1)\n    t.month.should eq(1)\n    t.day.should eq(1)\n    t.hour.should eq(0)\n    t.minute.should eq(0)\n    t.second.should eq(0)\n    t.millisecond.should eq(0)\n    t.local?.should be_true\n  end\n\n  it \"parse fails without time zone\" do\n    expect_raises(Time::Format::Error, \"no default location provided\") do\n      Time.parse!(\"2017-12-01 20:15:13\", \"%F %T\")\n    end\n    Time.parse(\"2017-12-01 20:15:13\", \"%F %T\", Time::Location.local).to_s(\"%F %T\").should eq \"2017-12-01 20:15:13\"\n    Time.parse!(\"2017-12-01 20:15:13 +01:00\", \"%F %T %:z\").to_s(\"%F %T %:z\").should eq \"2017-12-01 20:15:13 +01:00\"\n  end\n\n  it \"gives nice error message when end of input is reached (#12047)\" do\n    expect_raises(Time::Format::Error, \"Expected '-' but the end of the input was reached\") do\n      Time.parse!(\"2021-01\", \"%F\")\n    end\n  end\n\n  it \"parses\" do\n    parse_time(\"2014\", \"%Y\").year.should eq(2014)\n    parse_time(\"19\", \"%C\").year.should eq(1900)\n    parse_time(\"14\", \"%y\").year.should eq(2014)\n    parse_time(\"09\", \"%m\").month.should eq(9)\n    parse_time(\" 9\", \"%_m\").month.should eq(9)\n    parse_time(\"9\", \"%-m\").month.should eq(9)\n    parse_time(\"February\", \"%B\").month.should eq(2)\n    parse_time(\"March\", \"%B\").month.should eq(3)\n    parse_time(\"MaRcH\", \"%B\").month.should eq(3)\n    parse_time(\"MaR\", \"%B\").month.should eq(3)\n    parse_time(\"MARCH\", \"%^B\").month.should eq(3)\n    parse_time(\"Mar\", \"%b\").month.should eq(3)\n    parse_time(\"Mar\", \"%^b\").month.should eq(3)\n    parse_time(\"MAR\", \"%^b\").month.should eq(3)\n    parse_time(\"MAR\", \"%h\").month.should eq(3)\n    parse_time(\"MAR\", \"%^h\").month.should eq(3)\n    parse_time(\"2\", \"%d\").day.should eq(2)\n    parse_time(\"02\", \"%d\").day.should eq(2)\n    parse_time(\"02\", \"%-d\").day.should eq(2)\n    parse_time(\" 2\", \"%e\").day.should eq(2)\n    parse_time(\"9\", \"%H\").hour.should eq(9)\n    parse_time(\" 9\", \"%k\").hour.should eq(9)\n    parse_time(\"09\", \"%I\").hour.should eq(9)\n    parse_time(\" 9\", \"%l\").hour.should eq(9)\n    parse_time(\"9pm\", \"%l%p\").hour.should eq(21)\n    parse_time(\"9PM\", \"%l%P\").hour.should eq(21)\n    parse_time(\"12PM\", \"%l%P\").hour.should eq(12)\n    parse_time(\"9am\", \"%l%p\").hour.should eq(9)\n    parse_time(\"9AM\", \"%l%P\").hour.should eq(9)\n    parse_time(\"12AM\", \"%l%P\").hour.should eq(0)\n    parse_time(\"09\", \"%M\").minute.should eq(9)\n    parse_time(\"09\", \"%S\").second.should eq(9)\n    parse_time(\"123\", \"%L\").millisecond.should eq(123)\n    parse_time(\"1\", \"%L\").millisecond.should eq(100)\n    parse_time(\"000000321\", \"%N\").nanosecond.should eq(321)\n    parse_time(\"321\", \"%N\").nanosecond.should eq(321000000)\n    parse_time(\"321999\", \"%3N\").nanosecond.should eq(321000000)\n    parse_time(\"321\", \"%6N\").nanosecond.should eq(321000000)\n    parse_time(\"000321999\", \"%6N\").nanosecond.should eq(321000)\n    parse_time(\"000000321999\", \"%9N\").nanosecond.should eq(321)\n    parse_time(\"321\", \"%9N\").nanosecond.should eq(321000000)\n    parse_time(\"3214569879999\", \"%N\").nanosecond.should eq(321456987)\n    parse_time(\"Fri Oct 31 23:00:24 2014\", \"%c\").to_s.should eq(\"2014-10-31 23:00:24 UTC\")\n    parse_time(\"10/31/14\", \"%D\").to_s.should eq(\"2014-10-31 00:00:00 UTC\")\n    parse_time(\"10/31/69\", \"%D\").to_s.should eq(\"1969-10-31 00:00:00 UTC\")\n    parse_time(\"2014-10-31\", \"%F\").to_s.should eq(\"2014-10-31 00:00:00 UTC\")\n    parse_time(\"2014-10-31\", \"%F\").to_s.should eq(\"2014-10-31 00:00:00 UTC\")\n    parse_time(\"10/31/14\", \"%x\").to_s.should eq(\"2014-10-31 00:00:00 UTC\")\n    parse_time(\"10:11:12\", \"%X\").to_s.should eq(\"0001-01-01 10:11:12 UTC\")\n    parse_time(\"11:14:01 PM\", \"%r\").to_s.should eq(\"0001-01-01 23:14:01 UTC\")\n    parse_time(\"11:14\", \"%R\").to_s.should eq(\"0001-01-01 11:14:00 UTC\")\n    parse_time(\"11:12:13\", \"%T\").to_s.should eq(\"0001-01-01 11:12:13 UTC\")\n    parse_time(\"This was done on Friday, October 31, 2014\", \"This was done on %A, %B %d, %Y\").to_s.should eq(\"2014-10-31 00:00:00 UTC\")\n    parse_time(\"今は Friday, October 31, 2014\", \"今は %A, %B %d, %Y\").to_s.should eq(\"2014-10-31 00:00:00 UTC\")\n    parse_time(\"epoch: 1459864667\", \"epoch: %s\").to_unix.should eq(1459864667)\n    parse_time(\"epoch: -1459864667\", \"epoch: %s\").to_unix.should eq(-1459864667)\n\n    parse_time(\"1985-W15-5\", \"%G-W%V-%u\").should eq(Time.utc(1985, 4, 12))\n    parse_time(\"2004-W53-6\", \"%G-W%V-%u\").should eq(Time.utc(2005, 1, 1))\n    parse_time(\"2004-W53-7\", \"%G-W%V-%u\").should eq(Time.utc(2005, 1, 2))\n    parse_time(\"2005-W52-6\", \"%G-W%V-%u\").should eq(Time.utc(2005, 12, 31))\n    parse_time(\"2005-W52-7\", \"%G-W%V-%u\").should eq(Time.utc(2006, 1, 1))\n    parse_time(\"2006-W01-1\", \"%G-W%V-%u\").should eq(Time.utc(2006, 1, 2))\n    parse_time(\"2006-W52-7\", \"%G-W%V-%u\").should eq(Time.utc(2006, 12, 31))\n    parse_time(\"2007-W01-1\", \"%G-W%V-%u\").should eq(Time.utc(2007, 1, 1))\n    parse_time(\"2007-W52-7\", \"%G-W%V-%u\").should eq(Time.utc(2007, 12, 30))\n    parse_time(\"2008-W01-1\", \"%G-W%V-%u\").should eq(Time.utc(2007, 12, 31))\n    parse_time(\"2008-W01-2\", \"%G-W%V-%u\").should eq(Time.utc(2008, 1, 1))\n    parse_time(\"2008-W52-7\", \"%G-W%V-%u\").should eq(Time.utc(2008, 12, 28))\n    parse_time(\"2009-W01-1\", \"%G-W%V-%u\").should eq(Time.utc(2008, 12, 29))\n    parse_time(\"2009-W01-2\", \"%G-W%V-%u\").should eq(Time.utc(2008, 12, 30))\n    parse_time(\"2009-W01-3\", \"%G-W%V-%u\").should eq(Time.utc(2008, 12, 31))\n    parse_time(\"2009-W01-4\", \"%G-W%V-%u\").should eq(Time.utc(2009, 1, 1))\n    parse_time(\"2009-W53-4\", \"%G-W%V-%u\").should eq(Time.utc(2009, 12, 31))\n    parse_time(\"2009-W53-5\", \"%G-W%V-%u\").should eq(Time.utc(2010, 1, 1))\n    parse_time(\"2009-W53-6\", \"%G-W%V-%u\").should eq(Time.utc(2010, 1, 2))\n    parse_time(\"2009-W53-7\", \"%G-W%V-%u\").should eq(Time.utc(2010, 1, 3))\n  end\n\n  it \"parses am/pm\" do\n    parse_time(\"12:00 am\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"12:01 am\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\"01:00 am\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00 am\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"00:00 pm\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"00:01 pm\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"12:00 pm\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01 pm\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"01:00 pm\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"11:00 pm\", \"%I:%M %P\").to_s(\"%H:%M\").should eq(\"23:00\")\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"00:00 am\", \"%I:%M %P\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"00:01 am\", \"%I:%M %P\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"13:00 am\", \"%I:%M %P\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"13:00 pm\", \"%I:%M %P\")\n    end\n\n    parse_time(\"12:00\", \"%I:%M\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"12:01\", \"%I:%M\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\"01:00\", \"%I:%M\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00\", \"%I:%M\").to_s(\"%H:%M\").should eq(\"11:00\")\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"00:00\", \"%I:%M\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"00:01\", \"%I:%M\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"13:00\", \"%I:%M\")\n    end\n\n    parse_time(\"12:00 am\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"12:01 am\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\" 1:00 am\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00 am\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\" 0:00 pm\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\" 0:01 pm\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"12:00 pm\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01 pm\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\" 1:00 pm\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"11:00 pm\", \"%l:%M %P\").to_s(\"%H:%M\").should eq(\"23:00\")\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\" 0:00 am\", \"%l:%M %P\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\" 0:01 am\", \"%l:%M %P\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"13:00 am\", \"%l:%M %P\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"13:00 pm\", \"%l:%M %P\")\n    end\n\n    parse_time(\"12:00\", \"%l:%M\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"12:01\", \"%l:%M\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\"01:00\", \"%l:%M\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00\", \"%l:%M\").to_s(\"%H:%M\").should eq(\"11:00\")\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\" 0:00\", \"%l:%M\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\" 0:01\", \"%l:%M\")\n    end\n    expect_raises Time::Format::Error, \"Invalid hour for 12-hour clock\" do\n      parse_time(\"13:00\", \"%l:%M\")\n    end\n  end\n\n  it \"parses 24h clock\" do\n    parse_time(\"00:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"00:01\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\"01:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"12:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"13:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"23:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"23:00\")\n    parse_time(\"24:00\", \"%H:%M\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"2020-05-21 24:00\", \"%F %H:%M\").should eq(Time.utc(2020, 5, 22, 0, 0))\n\n    parse_time(\" 0:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\" 0:01\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\" 1:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"12:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"13:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"23:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"23:00\")\n    parse_time(\"24:00\", \"%k:%M\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"2020-05-21 24:00\", \"%F %k:%M\").should eq(Time.utc(2020, 5, 22, 0, 0))\n  end\n\n  it \"parses 24h clock with am/pm\" do\n    parse_time(\"00:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"00:01 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\"01:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"12:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"13:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"23:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"23:00\")\n    parse_time(\"24:00 AM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n\n    parse_time(\" 0:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\" 0:01 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\" 1:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"12:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"13:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"23:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"23:00\")\n    parse_time(\"24:00 AM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n\n    parse_time(\"00:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\"00:01 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\"01:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"12:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"13:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"23:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"23:00\")\n    parse_time(\"24:00 PM\", \"%H:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n\n    parse_time(\" 0:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n    parse_time(\" 0:01 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"00:01\")\n    parse_time(\" 1:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"01:00\")\n    parse_time(\"11:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"11:00\")\n    parse_time(\"12:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"12:00\")\n    parse_time(\"12:01 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"12:01\")\n    parse_time(\"13:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"13:00\")\n    parse_time(\"23:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"23:00\")\n    parse_time(\"24:00 PM\", \"%k:%M %P\").to_s(\"%H:%M\").should eq(\"00:00\")\n  end\n\n  it \"parses timezone\" do\n    patterns = {\"%z\", \"%:z\", \"%::z\"}\n\n    {\"+0000\", \"+00:00\", \"+00:00:00\"}.zip(patterns) do |string, pattern|\n      time = Time.parse!(string, pattern)\n      time.offset.should eq 0\n      time.utc?.should be_false\n      time.location.fixed?.should be_true\n    end\n\n    {\"-0000\", \"-00:00\", \"-00:00:00\"}.zip(patterns) do |string, pattern|\n      time = Time.parse!(string, pattern)\n      time.offset.should eq 0\n      time.utc?.should be_false\n      time.location.fixed?.should be_true\n    end\n\n    {\"-0200\", \"-02:00\", \"-02:00:00\"}.zip(patterns) do |string, pattern|\n      time = Time.parse!(string, pattern)\n      time.offset.should eq -2 * 3600\n      time.utc?.should be_false\n      time.location.fixed?.should be_true\n    end\n\n    {\"Z\", \"Z\", \"Z\"}.zip(patterns) do |string, pattern|\n      time = Time.parse!(string, pattern)\n      time.offset.should eq 0\n      time.utc?.should be_true\n      time.location.fixed?.should be_true\n    end\n\n    {\"UTC\", \"UTC\", \"UTC\"}.zip(patterns) do |string, pattern|\n      time = Time.parse!(string, pattern)\n      time.offset.should eq 0\n      time.utc?.should be_true\n      time.location.fixed?.should be_true\n    end\n\n    time = Time.parse!(\"+04:12:39\", \"%::z\")\n    time.offset.should eq 4 * 3600 + 12 * 60 + 39\n    time.utc?.should be_false\n    time.location.fixed?.should be_true\n\n    time = Time.parse!(\"-04:12:39\", \"%::z\")\n    time.offset.should eq -1 * (4 * 3600 + 12 * 60 + 39)\n    time.utc?.should be_false\n    time.location.fixed?.should be_true\n  end\n\n  it \"parses zone name\" do\n    [\"%^Z\", \"%Z\"].each do |pattern|\n      time = Time.parse!(\"UTC\", pattern)\n      time.offset.should eq 0\n      time.utc?.should be_true\n      time.location.fixed?.should be_true\n\n      time = Time.parse!(\"-00:00\", pattern)\n      time.offset.should eq 0\n      time.utc?.should be_false\n      time.location.fixed?.should be_true\n\n      time = Time.parse!(\"+00:00\", pattern)\n      time.offset.should eq 0\n      time.utc?.should be_false\n      time.location.fixed?.should be_true\n\n      time = Time.parse!(\"+00:00:00\", pattern)\n      time.offset.should eq 0\n      time.utc?.should be_false\n      time.location.fixed?.should be_true\n\n      with_zoneinfo do\n        time = Time.parse!(\"CET\", pattern)\n        time.location.should eq Time::Location.load(\"CET\")\n\n        time = Time.parse!(\"Europe/Berlin\", pattern)\n        time.location.should eq Time::Location.load(\"Europe/Berlin\")\n\n        expect_raises(Time::Location::InvalidLocationNameError) do\n          Time.parse!(\"INVALID\", pattern)\n        end\n      end\n    end\n  end\n\n  it \"raises when time zone missing\" do\n    expect_raises(Time::Format::Error, \"Invalid timezone\") do\n      Time.parse!(\"\", \"%z\")\n    end\n    expect_raises(Time::Format::Error, \"Invalid timezone\") do\n      Time.parse!(\"123456+01:00\", \"%3N%z\")\n    end\n  end\n\n  it \"parses day of year\" do\n    parse_time(\"2006-001\", \"%Y-%j\").should eq(Time.utc(2006, 1, 1))\n    parse_time(\"2006-032\", \"%Y-%j\").should eq(Time.utc(2006, 2, 1))\n    parse_time(\"2006-059\", \"%Y-%j\").should eq(Time.utc(2006, 2, 28))\n    parse_time(\"2006-060\", \"%Y-%j\").should eq(Time.utc(2006, 3, 1))\n    parse_time(\"2006-200\", \"%Y-%j\").should eq(Time.utc(2006, 7, 19))\n    parse_time(\"2006-365\", \"%Y-%j\").should eq(Time.utc(2006, 12, 31))\n\n    parse_time(\"2004-001\", \"%Y-%j\").should eq(Time.utc(2004, 1, 1))\n    parse_time(\"2004-032\", \"%Y-%j\").should eq(Time.utc(2004, 2, 1))\n    parse_time(\"2004-059\", \"%Y-%j\").should eq(Time.utc(2004, 2, 28))\n    parse_time(\"2004-060\", \"%Y-%j\").should eq(Time.utc(2004, 2, 29))\n    parse_time(\"2004-061\", \"%Y-%j\").should eq(Time.utc(2004, 3, 1))\n    parse_time(\"2004-200\", \"%Y-%j\").should eq(Time.utc(2004, 7, 18))\n    parse_time(\"2004-365\", \"%Y-%j\").should eq(Time.utc(2004, 12, 30))\n    parse_time(\"2004-366\", \"%Y-%j\").should eq(Time.utc(2004, 12, 31))\n\n    expect_raises(Time::Format::Error, \"Invalid day of year\") do\n      parse_time(\"2006-000\", \"%Y-%j\")\n    end\n    expect_raises(Time::Format::Error, \"Invalid day of year\") do\n      parse_time(\"2004-000\", \"%Y-%j\")\n    end\n    expect_raises(Time::Format::Error, \"Invalid day of year\") do\n      parse_time(\"2006-366\", \"%Y-%j\")\n    end\n    expect_raises(Time::Format::Error, \"Invalid day of year\") do\n      parse_time(\"2004-367\", \"%Y-%j\")\n    end\n  end\n\n  # TODO %Z\n  # TODO %G\n  # TODO %g\n  # TODO %V\n  # TODO %U\n  # TODO %W\n  # TODO %s\n\n  it \"parses whitespace\" do\n    [\" \", \"\\t\", \"\\n\", \"\\v\", \"\\f\", \"\\r\", \"%n\", \"%t\"].each do |space|\n      parse_time(\"20250530\", \"%Y#{space}%m#{space}%d\").should eq(Time.utc(2025, 5, 30))\n      parse_time(\"2025  05  30\", \"%Y#{space}%m#{space}%d\").should eq(Time.utc(2025, 5, 30))\n      parse_time(\"2025 \\t\\n\\v\\f\\r05 \\t\\n\\v\\f\\r30\", \"%Y#{space}%m#{space}%d\").should eq(Time.utc(2025, 5, 30))\n    end\n\n    parse_time(\"20250530\", \"%Y \\t\\n\\v\\f\\r%n%t%m \\t\\n\\v\\f\\r%n%t%d\").should eq(Time.utc(2025, 5, 30))\n    parse_time(\"2025  05  30\", \"%Y \\t\\n\\v\\f\\r%n%t%m \\t\\n\\v\\f\\r%n%t%d\").should eq(Time.utc(2025, 5, 30))\n    parse_time(\"2025 \\t\\n\\v\\f\\r05 \\t\\n\\v\\f\\r30\", \"%Y \\t\\n\\v\\f\\r%n%t%m \\t\\n\\v\\f\\r%n%t%d\").should eq(Time.utc(2025, 5, 30))\n\n    parse_time(\"Fri  Oct  31  23:00:24  2014\", \"%c\").should eq(Time.utc(2014, 10, 31, 23, 0, 24))\n    parse_time(\"Fri\\tOct\\n31\\v23:00:24\\f\\r 2014\", \"%c\").should eq(Time.utc(2014, 10, 31, 23, 0, 24))\n\n    parse_time(\"11:14:01PM\", \"%r\").should eq(Time.utc(1, 1, 1, 23, 14, 1))\n    parse_time(\"11:14:01  PM\", \"%r\").should eq(Time.utc(1, 1, 1, 23, 14, 1))\n    parse_time(\"11:14:01 \\t\\n\\v\\f\\rPM\", \"%r\").should eq(Time.utc(1, 1, 1, 23, 14, 1))\n  end\n\n  # TODO %%\n  # TODO %v\n\n  it do\n    time = Time.parse!(\"2014-10-31 10:11:12 Z hi\", \"%F %T %z hi\")\n    time.utc?.should be_true\n    time.to_utc.to_s.should eq(\"2014-10-31 10:11:12 UTC\")\n  end\n\n  it do\n    time = Time.parse!(\"2014-10-31 10:11:12 UTC hi\", \"%F %T %z hi\")\n    time.utc?.should be_true\n    time.to_utc.to_s.should eq(\"2014-10-31 10:11:12 UTC\")\n  end\n\n  it do\n    time = Time.parse!(\"2014-10-31 10:11:12 -06:00 hi\", \"%F %T %z hi\")\n    time.utc?.should be_false\n    time.location.fixed?.should be_true\n    time.offset.should eq -6 * 3600\n    time.to_utc.to_s.should eq(\"2014-10-31 16:11:12 UTC\")\n  end\n\n  it do\n    time = Time.parse!(\"2014-10-31 10:11:12 +05:00 hi\", \"%F %T %z hi\")\n    time.utc?.should be_false\n    time.location.fixed?.should be_true\n    time.offset.should eq 5 * 3600\n    time.to_utc.to_s.should eq(\"2014-10-31 05:11:12 UTC\")\n  end\n\n  it do\n    time = Time.parse!(\"2014-10-31 10:11:12 -06:00:00 hi\", \"%F %T %z hi\")\n    time.utc?.should be_false\n    time.location.fixed?.should be_true\n    time.offset.should eq -6 * 3600\n    time.to_utc.to_s.should eq(\"2014-10-31 16:11:12 UTC\")\n  end\n\n  it do\n    time = Time.parse!(\"2014-10-31 10:11:12 -060000 hi\", \"%F %T %z hi\")\n    time.utc?.should be_false\n    time.location.fixed?.should be_true\n    time.offset.should eq -6 * 3600\n    time.to_utc.to_s.should eq(\"2014-10-31 16:11:12 UTC\")\n  end\n\n  it \"parses centiseconds\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.45+01:00\", \"%FT%T.%L%z\").to_utc\n    time.to_s.should eq(\"2016-09-09 16:03:28 UTC\")\n    time.millisecond.should eq(450)\n    time.nanosecond.should eq(450000000)\n  end\n\n  it \"parses milliseconds with %L\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.456+01:00\", \"%FT%T.%L%z\").to_utc\n    time.to_s.should eq(\"2016-09-09 16:03:28 UTC\")\n    time.millisecond.should eq(456)\n    time.nanosecond.should eq(456000000)\n  end\n\n  it \"parses milliseconds with %3N\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.456+01:00\", \"%FT%T.%3N%z\").to_utc\n    time.to_s.should eq(\"2016-09-09 16:03:28 UTC\")\n    time.millisecond.should eq(456)\n    time.nanosecond.should eq(456000000)\n  end\n\n  it \"parses microseconds with %6N\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.456789+01:00\", \"%FT%T.%6N%z\").to_utc\n    time.to_s.should eq(\"2016-09-09 16:03:28 UTC\")\n    time.millisecond.should eq(456)\n    time.nanosecond.should eq(456789000)\n  end\n\n  it \"parses nanoseconds\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.456789123+01:00\", \"%FT%T.%N%z\").to_utc\n    time.to_s.should eq(\"2016-09-09 16:03:28 UTC\")\n    time.nanosecond.should eq(456789123)\n  end\n\n  it \"parses nanoseconds with %9N\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.456789123+01:00\", \"%FT%T.%9N%z\").to_utc\n    time.to_s.should eq(\"2016-09-09 16:03:28 UTC\")\n    time.nanosecond.should eq(456789123)\n  end\n\n  it \"parses discarding additional decimals\" do\n    time = Time.parse(\"2016-09-09T17:03:28.456789123999\", \"%FT%T.%3N\", Time::Location::UTC)\n    time.nanosecond.should eq(456000000)\n\n    time = Time.parse(\"2016-09-09T17:03:28.456789123999\", \"%FT%T.%6N\", Time::Location::UTC)\n    time.nanosecond.should eq(456789000)\n\n    time = Time.parse(\"2016-09-09T17:03:28.456789123990\", \"%FT%T.%9N\", Time::Location::UTC)\n    time.nanosecond.should eq(456789123)\n\n    time = Time.parse!(\"2016-09-09T17:03:28.456789123999999+01:00\", \"%FT%T.%N%z\")\n    time.to_s.should eq(\"2016-09-09 17:03:28 +01:00\")\n    time.nanosecond.should eq(456789123)\n\n    time = Time.parse(\"4567892016-09-09T17:03:28\", \"%6N%FT%T\", Time::Location::UTC)\n    time.year.should eq(2016)\n    time.nanosecond.should eq(456789000)\n  end\n\n  it \"parses if some decimals are missing\" do\n    time = Time.parse!(\"2016-09-09T17:03:28.45+01:00\", \"%FT%T.%3N%z\").to_utc\n    time.nanosecond.should eq(450000000)\n\n    time = Time.parse!(\"2016-09-09T17:03:28.45678+01:00\", \"%FT%T.%6N%z\").to_utc\n    time.nanosecond.should eq(456780000)\n\n    time = Time.parse!(\"2016-09-09T17:03:28.4567891+01:00\", \"%FT%T.%9N%z\").to_utc\n    time.nanosecond.should eq(456789100)\n  end\n\n  it \"parses the correct amount of digits (#853)\" do\n    time = Time.parse(\"20150624\", \"%Y%m%d\", Time::Location::UTC)\n    time.year.should eq(2015)\n    time.month.should eq(6)\n    time.day.should eq(24)\n  end\n\n  it \"parses month blank padded\" do\n    time = Time.parse(\"2015 624\", \"%Y%_m%d\", Time::Location::UTC)\n    time.year.should eq(2015)\n    time.month.should eq(6)\n    time.day.should eq(24)\n  end\n\n  it \"parses day of month blank padded\" do\n    time = Time.parse(\"201506 4\", \"%Y%m%e\", Time::Location::UTC)\n    time.year.should eq(2015)\n    time.month.should eq(6)\n    time.day.should eq(4)\n  end\n\n  it \"parses hour 24 blank padded\" do\n    time = Time.parse(\" 31112\", \"%k%M%S\", Time::Location::UTC)\n    time.hour.should eq(3)\n    time.minute.should eq(11)\n    time.second.should eq(12)\n  end\n\n  it \"parses hour 12 blank padded\" do\n    time = Time.parse(\" 31112\", \"%l%M%S\", Time::Location::UTC)\n    time.hour.should eq(3)\n    time.minute.should eq(11)\n    time.second.should eq(12)\n  end\n\n  it \"can parse in location\" do\n    with_zoneinfo do\n      time = Time.parse(\"2014-10-31 11:12:13\", \"%F %T\", Time::Location::UTC)\n      time.utc?.should be_true\n\n      location = Time::Location.load(\"Europe/Berlin\")\n      time = Time.parse(\"2016-11-24 14:32:02\", \"%F %T\", location)\n      time.location.should eq location\n\n      time = Time.parse(\"2016-11-24 14:32:02 +01:00\", \"%F %T %:z\", location)\n      time.location.should eq Time::Location.fixed(3600)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/time/instant_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"Time.instant\" do\n  it \"returns always increasing monotonic clock\" do\n    start = Time.instant\n    loop do\n      reading = Time.instant\n      reading.should be >= start\n      break if reading > start\n    end\n  end\nend\n\ndescribe Time::Instant do\n  describe \"#<=>\" do\n    it \"compares\" do\n      t1 = Time.instant\n      sleep(1.nanosecond)\n      t2 = Time.instant\n\n      (t1 <=> t2).should eq(-1)\n      (t1 == t2).should be_false\n      (t1 < t2).should be_true\n    end\n  end\n\n  describe \"math\" do\n    it do\n      t1 = Time.instant\n\n      (t1 + 1.second - 1.second).should eq t1\n    end\n\n    it \"associative\" do\n      t1 = Time.instant\n      offset = 5.milliseconds\n\n      ((t1 + offset) - t1).should eq (t1 - t1) + offset\n    end\n\n    it \"nanosecond precision\" do\n      t1 = Time.instant\n      offset = 1.nanosecond\n\n      ((t1 + offset) - t1).should eq offset\n    end\n  end\n\n  describe \"#duration_since\" do\n    it \"calculates\" do\n      t1 = Time.instant\n      t2 = Time.instant\n      duration = t2.duration_since(t1)\n\n      (t2 - duration).should eq(t1)\n      (t1 + duration).should eq(t2)\n    end\n\n    it \"saturates\" do\n      t2 = Time.instant\n      t1 = t2 - 1.second\n      t1.duration_since(t2).should eq Time::Span::ZERO\n    end\n  end\n\n  describe \"#elapsed\" do\n    it \"calculates\" do\n      t1 = Time.instant - 12.microseconds\n      t1.elapsed.should be >= 12.microseconds\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/time/location_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/time\"\n\nprivate def assert_tz_boundaries(tz : String, t0 : Time, t1 : Time, t2 : Time, t3 : Time, *, file = __FILE__, line = __LINE__)\n  location = Time::Location.posix_tz(\"Local\", tz)\n  std_zone = location.zones.find(&.dst?.!).should_not be_nil, file: file, line: line\n  dst_zone = location.zones.find(&.dst?).should_not be_nil, file: file, line: line\n  assert_tz_boundaries(location, std_zone, dst_zone, t0, t1, t2, t3, file: file, line: line)\nend\n\nprivate def assert_tz_boundaries(\n  location : Time::Location, std_zone : Time::Location::Zone, dst_zone : Time::Location::Zone,\n  t0 : Time, t1 : Time, t2 : Time, t3 : Time, *, file = __FILE__, line = __LINE__,\n)\n  t0, t1, t2, t3 = t0.to_unix, t1.to_unix, t2.to_unix, t3.to_unix\n\n  location.lookup_with_boundaries(t1 - 1).should eq({std_zone, {t0, t1}}), file: file, line: line\n  location.lookup_with_boundaries(t1).should eq({dst_zone, {t1, t2}}), file: file, line: line\n  location.lookup_with_boundaries(t1 + (t2 - t1) // 2).should eq({dst_zone, {t1, t2}}), file: file, line: line\n  location.lookup_with_boundaries(t2 - 1).should eq({dst_zone, {t1, t2}}), file: file, line: line\n  location.lookup_with_boundaries(t2).should eq({std_zone, {t2, t3}}), file: file, line: line\nend\n\nprivate def assert_tz_raises(str, *, file = __FILE__, line = __LINE__)\n  expect_raises(ArgumentError, \"Invalid TZ string: #{str}\", file: file, line: line) do\n    Time::Location.posix_tz(\"\", str)\n  end\nend\n\nclass Time::Location\n  describe Time::Location do\n    describe \".load\" do\n      it \"loads Europe/Berlin\" do\n        with_zoneinfo do\n          location = Location.load(\"Europe/Berlin\")\n\n          location.name.should eq \"Europe/Berlin\"\n          standard_time = location.lookup(Time.utc(2017, 11, 22))\n          standard_time.name.should eq \"CET\"\n          standard_time.offset.should eq 3600\n          standard_time.dst?.should be_false\n\n          summer_time = location.lookup(Time.utc(2017, 10, 22))\n          summer_time.name.should eq \"CEST\"\n          summer_time.offset.should eq 7200\n          summer_time.dst?.should be_true\n\n          location.utc?.should be_false\n          location.fixed?.should be_false\n\n          with_tz(\"UTC\") do\n            location.local?.should be_false\n          end\n\n          with_tz(\"Europe/Berlin\") do\n            location.local?.should be_true\n          end\n\n          Location.load(\"Europe/Berlin\", {ZONEINFO_ZIP}).should eq location\n        end\n      end\n\n      {% if flag?(:win32) %}\n        it \"maps IANA timezone identifier to Windows name (#13166)\" do\n          location = Location.load(\"Europe/Berlin\")\n          location.name.should eq \"Europe/Berlin\"\n          location.utc?.should be_false\n          location.fixed?.should be_false\n        end\n\n        it \"uses IANA zone names for Windows system time zones (#15911)\" do\n          location = Location.load(\"Europe/Lisbon\")\n          std_zone = location.zones.find(&.dst?.!).should_not be_nil\n          dst_zone = location.zones.find(&.dst?).should_not be_nil\n          std_zone.name.should eq(\"WET\")\n          dst_zone.name.should eq(\"WEST\")\n        end\n      {% end %}\n\n      it \"invalid timezone identifier\" do\n        with_zoneinfo(datapath(\"zoneinfo\")) do\n          expect_raises(InvalidLocationNameError, \"Foobar/Baz\") do\n            Location.load(\"Foobar/Baz\")\n          end\n        end\n\n        Location.load?(\"Foobar/Baz\", [datapath(\"zoneinfo\")]).should be_nil\n      end\n\n      it \"name is folder\" do\n        Location.load?(\"Foo\", [datapath(\"zoneinfo\")]).should be_nil\n      end\n\n      it \"invalid zone file\" do\n        expect_raises(Time::Location::InvalidTZDataError) do\n          Location.load?(\"Foo/invalid\", [datapath(\"zoneinfo\")])\n        end\n      end\n\n      it \"treats UTC as special case\" do\n        with_zoneinfo do\n          Location.load(\"UTC\").should eq Location::UTC\n          Location.load(\"\").should eq Location::UTC\n          Location.load(\"Etc/UTC\").should eq Location::UTC\n        end\n\n        with_zoneinfo(\"nonexistent_zipfile.zip\") do\n          Location.load(\"UTC\").should eq Location::UTC\n          Location.load(\"\").should eq Location::UTC\n          Location.load(\"Etc/UTC\").should eq Location::UTC\n        end\n      end\n\n      it \"treats GMT as special case\" do\n        with_zoneinfo do\n          Location.load(\"GMT\").should eq Location::UTC\n          Location.load(\"Etc/GMT\").should eq Location::UTC\n        end\n\n        with_zoneinfo(\"nonexistent_zipfile.zip\") do\n          Location.load(\"GMT\").should eq Location::UTC\n          Location.load(\"Etc/GMT\").should eq Location::UTC\n        end\n      end\n\n      describe \"validating name\" do\n        it \"absolute path\" do\n          with_zoneinfo do\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"/America/New_York\")\n            end\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"\\\\Zulu\")\n            end\n          end\n        end\n        it \"dot dot\" do\n          with_zoneinfo do\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"../zoneinfo/America/New_York\")\n            end\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"a..\")\n            end\n          end\n        end\n      end\n\n      context \"with $TZDIR\" do\n        it \"loads from custom directory\" do\n          with_zoneinfo(datapath(\"zoneinfo\")) do\n            location = Location.load(\"Foo/Bar\")\n            location.name.should eq \"Foo/Bar\"\n          end\n        end\n\n        it \"loads from custom zipfile\" do\n          with_zoneinfo(ZONEINFO_ZIP) do\n            location = Location.load(\"Asia/Jerusalem\")\n            location.not_nil!.name.should eq \"Asia/Jerusalem\"\n          end\n        end\n\n        it \"raises if not available\" do\n          with_zoneinfo(ZONEINFO_ZIP) do\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"Foo/Bar\")\n            end\n            Location.load?(\"Foo/Bar\", Crystal::System::Time.zone_sources).should be_nil\n          end\n        end\n\n        it \"does not fall back to default sources\" do\n          with_zoneinfo(datapath(\"zoneinfo\")) do\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"Europe/Berlin\")\n            end\n          end\n\n          with_zoneinfo(\"nonexistent_zipfile.zip\") do\n            expect_raises(InvalidLocationNameError) do\n              Location.load(\"Europe/Berlin\")\n            end\n          end\n        end\n\n        it \"caches result\" do\n          with_zoneinfo do\n            location = Location.load(\"Europe/Berlin\")\n            Location.load(\"Europe/Berlin\").should be location\n          end\n        end\n\n        it \"loads new data if file was changed\" do\n          zoneinfo_path = datapath(\"zoneinfo\")\n          with_zoneinfo(zoneinfo_path) do\n            location1 = Location.load(\"Foo/Bar\")\n            File.touch(File.join(zoneinfo_path, \"Foo/Bar\"))\n            location2 = Location.load(\"Foo/Bar\")\n\n            location1.should eq location2\n            location1.should_not be location2\n          end\n        end\n\n        it \"loads new data if ZIP file was changed\" do\n          with_zoneinfo(ZONEINFO_ZIP) do\n            location1 = Location.load(\"Europe/Berlin\")\n            File.touch(ZONEINFO_ZIP)\n            location2 = Location.load(\"Europe/Berlin\")\n\n            location1.should eq location2\n            location1.should_not be location2\n          end\n        end\n      end\n\n      context \"with $ZONEINFO\" do\n        it \"loads from custom directory\" do\n          with_env(\"ZONEINFO\": datapath(\"zoneinfo\")) do\n            Time::Location.__clear_location_cache\n            location = Location.load(\"Foo/Bar\")\n            location.name.should eq \"Foo/Bar\"\n          end\n        end\n      end\n    end\n\n    describe \".load?\" do\n      it \"loads Europe/Berlin\" do\n        with_zoneinfo do\n          Time::Location.load?(\"Europe/Berlin\").should eq Time::Location.load(\"Europe/Berlin\")\n        end\n      end\n\n      it \"returns nil if unavailable\" do\n        Location.load?(\"Foobar/Baz\").should be_nil\n\n        with_zoneinfo(datapath(\"zoneinfo\")) do\n          Location.load?(\"Foobar/Baz\").should be_nil\n        end\n      end\n\n      it \"invalid zone file\" do\n        expect_raises(Time::Location::InvalidTZDataError) do\n          Location.load?(\"Foo/invalid\", [datapath(\"zoneinfo\")])\n        end\n      end\n\n      it \"treats UTC as special case\" do\n        with_zoneinfo do\n          Location.load?(\"UTC\").should eq Location::UTC\n          Location.load?(\"\").should eq Location::UTC\n          Location.load?(\"Etc/UTC\").should eq Location::UTC\n        end\n      end\n\n      describe \"raises on invalid location name\" do\n        it \"absolute path\" do\n          with_zoneinfo do\n            expect_raises(InvalidLocationNameError) do\n              Location.load?(\"/America/New_York\")\n            end\n            expect_raises(InvalidLocationNameError) do\n              Location.load?(\"\\\\Zulu\")\n            end\n          end\n        end\n\n        it \"dot dot\" do\n          with_zoneinfo do\n            expect_raises(InvalidLocationNameError) do\n              Location.load?(\"../zoneinfo/America/New_York\")\n            end\n            expect_raises(InvalidLocationNameError) do\n              Location.load?(\"a..\")\n            end\n          end\n        end\n      end\n\n      it \"caches result\" do\n        with_zoneinfo do\n          location = Location.load?(\"Europe/Berlin\")\n          Location.load?(\"Europe/Berlin\").should be location\n        end\n      end\n    end\n\n    describe \".load_android\" do\n      it \"loads Europe/Berlin\" do\n        Location.__clear_location_cache\n        location = Location.load_android(\"Europe/Berlin\", {datapath(\"android_tzdata\")}).should_not be_nil\n\n        location.name.should eq \"Europe/Berlin\"\n        standard_time = location.lookup(Time.utc(2017, 11, 22))\n        standard_time.name.should eq \"CET\"\n        standard_time.offset.should eq 3600\n        standard_time.dst?.should be_false\n\n        summer_time = location.lookup(Time.utc(2017, 10, 22))\n        summer_time.name.should eq \"CEST\"\n        summer_time.offset.should eq 7200\n        summer_time.dst?.should be_true\n\n        location.utc?.should be_false\n        location.fixed?.should be_false\n      end\n\n      it \"loads new data if tzdata file was changed\" do\n        tzdata_path = datapath(\"android_tzdata\")\n        Location.__clear_location_cache\n        location1 = Location.load_android(\"Europe/Berlin\", {tzdata_path})\n        File.touch(tzdata_path)\n        location2 = Location.load_android(\"Europe/Berlin\", {tzdata_path})\n\n        location1.should eq location2\n        location1.should_not be location2\n      end\n    end\n\n    it \"UTC\" do\n      location = Location::UTC\n      location.name.should eq \"UTC\"\n\n      location.utc?.should be_true\n      location.fixed?.should be_true\n\n      # this could fail if no source for localtime is available\n      unless Location.local.utc?\n        location.local?.should be_false\n      end\n\n      zone = location.lookup(Time.utc)\n      zone.name.should eq \"UTC\"\n      zone.offset.should eq 0\n      zone.dst?.should be_false\n    end\n\n    it \".local\" do\n      with_zoneinfo do\n        Location.local.should eq Location.load_local\n      end\n\n      Location.local = Location::UTC\n      Location.local.should be Location::UTC\n    end\n\n    describe \".load_local\" do\n      context \"with unset TZ\" do\n        it \"is #local?\" do\n          with_tz(nil) do\n            Location.load_local.local?.should be_true\n          end\n        end\n\n        it \"derives location name from system (e.g. /etc/localtime)\" do\n          with_tz(nil) do\n            local = Location.load_local\n\n            # This expectation might fail on unix in case /etc/localtime is not\n            # a symlink into the zoneinfo database.\n            local.name.should_not eq \"Local\"\n\n            local.should eq Location.load(local.name)\n          end\n        end\n      end\n\n      it \"with TZ\" do\n        with_tz(\"Europe/Berlin\") do\n          with_zoneinfo do\n            Location.load_local.name.should eq \"Europe/Berlin\"\n          end\n        end\n        with_tz(\"Foo/Bar\") do\n          with_zoneinfo(datapath(\"zoneinfo\")) do\n            Location.load_local.name.should eq \"Foo/Bar\"\n          end\n        end\n      end\n\n      it \"with empty TZ\" do\n        with_zoneinfo do\n          with_tz(\"\") do\n            Location.load_local.utc?.should be_true\n          end\n        end\n      end\n\n      it \"with POSIX TZ string\" do\n        with_tz(\"EST5EDT,M3.2.0,M11.1.0\") do\n          location = Location.load_local\n          location.name.should eq(\"Local\")\n          location.zones.should eq [\n            Location::Zone.new(\"EST\", -18000, false),\n            Location::Zone.new(\"EDT\", -14400, true),\n          ]\n          location.transitions.should be_empty\n        end\n      end\n\n      {% if flag?(:win32) %}\n        it \"loads time zone information from registry\" do\n          info = LibC::DYNAMIC_TIME_ZONE_INFORMATION.new(\n            bias: -60,\n            standardBias: 0,\n            daylightBias: -60,\n            standardDate: LibC::SYSTEMTIME.new(wYear: 0, wMonth: 10, wDayOfWeek: 0, wDay: 5, wHour: 3, wMinute: 0, wSecond: 0, wMilliseconds: 0),\n            daylightDate: LibC::SYSTEMTIME.new(wYear: 0, wMonth: 3, wDayOfWeek: 0, wDay: 5, wHour: 2, wMinute: 0, wSecond: 0, wMilliseconds: 0),\n          )\n          info.standardName.to_slice.copy_from \"Central Europe Standard Time\".to_utf16\n          info.daylightName.to_slice.copy_from \"Central Europe Summer Time\".to_utf16\n          info.timeZoneKeyName.to_slice.copy_from \"Central Europe Standard Time\".to_utf16\n\n          with_system_time_zone(info) do\n            location = Location.load_local\n            std_zone = Time::Location::Zone.new(\"CET\", 3600, false)\n            dst_zone = Time::Location::Zone.new(\"CEST\", 7200, true)\n            location.zones.should eq [std_zone, dst_zone]\n\n            location.lookup(Time.utc(2000, 10, 29, 0, 59, 59)).should eq(dst_zone)\n            location.lookup(Time.utc(2000, 10, 29, 1, 0, 0)).should eq(std_zone)\n            location.lookup(Time.utc(2001, 3, 25, 0, 59, 59)).should eq(std_zone)\n            location.lookup(Time.utc(2001, 3, 25, 1, 0, 0)).should eq(dst_zone)\n\n            location.lookup(Time.utc(3000, 10, 26, 0, 59, 59)).should eq(dst_zone)\n            location.lookup(Time.utc(3000, 10, 26, 1, 0, 0)).should eq(std_zone)\n            location.lookup(Time.utc(3001, 3, 29, 0, 59, 59)).should eq(std_zone)\n            location.lookup(Time.utc(3001, 3, 29, 1, 0, 0)).should eq(dst_zone)\n          end\n        end\n\n        it \"loads time zone without DST (#13502)\" do\n          info = LibC::DYNAMIC_TIME_ZONE_INFORMATION.new(bias: -480)\n          info.standardName.to_slice.copy_from \"China Standard Time\".to_utf16\n          info.daylightName.to_slice.copy_from \"China Daylight Time\".to_utf16\n          info.timeZoneKeyName.to_slice.copy_from \"China Standard Time\".to_utf16\n\n          with_system_time_zone(info) do\n            location = Location.load_local\n            location.zones.should eq [Time::Location::Zone.new(\"CST\", 28800, false)]\n          end\n        end\n      {% end %}\n    end\n\n    describe \".fixed\" do\n      it \"without name\" do\n        location = Location.fixed -9012\n        location.name.should eq \"-02:30:12\"\n        location.zones.should eq [Zone.new(nil, -9012, false)]\n        location.transitions.size.should eq 0\n\n        location.utc?.should be_false\n        location.fixed?.should be_true\n        location.local?.should be_false\n      end\n\n      it \"with name\" do\n        location = Location.fixed \"Fixed\", 1800\n        location.name.should eq \"Fixed\"\n        location.zones.should eq [Zone.new(\"Fixed\", 1800, false)]\n        location.transitions.size.should eq 0\n\n        location.utc?.should be_false\n        location.fixed?.should be_true\n        location.local?.should be_false\n      end\n\n      it \"positive\" do\n        location = Location.fixed 8000\n        location.name.should eq \"+02:13:20\"\n        location.zones.first.offset.should eq 8000\n      end\n\n      it \"negative\" do\n        location = Location.fixed -7539\n        location.name.should eq \"-02:05:39\"\n        location.zones.first.offset.should eq -7539\n      end\n\n      it \"exactly 24 hours\" do\n        location = Location.fixed 86400\n        location.name.should eq \"+24:00\"\n        location.zones.first.offset.should eq 86400\n\n        location = Location.fixed -86400\n        location.name.should eq \"-24:00\"\n        location.zones.first.offset.should eq -86400\n      end\n\n      it \"raises if offset to large\" do\n        expect_raises(InvalidTimezoneOffsetError, \"93600\") do\n          Location.fixed(93600)\n        end\n        expect_raises(InvalidTimezoneOffsetError, \"-90000\") do\n          Location.fixed(-90000)\n        end\n      end\n    end\n\n    describe \".tz\" do\n      it \"parses string with standard time only\" do\n        location = Location.posix_tz(\"America/New_York\", \"EST5\")\n        location.name.should eq(\"America/New_York\")\n        location.zones.should eq [\n          Location::Zone.new(\"EST\", -18000, false),\n        ]\n        location.transitions.should be_empty\n      end\n\n      it \"parses string with both standard time and DST\" do\n        location = Location.posix_tz(\"America/New_York\", \"EST5EDT,M3.2.0,M11.1.0\")\n        location.name.should eq(\"America/New_York\")\n        location.zones.should eq [\n          Location::Zone.new(\"EST\", -18000, false),\n          Location::Zone.new(\"EDT\", -14400, true),\n        ]\n        location.transitions.should be_empty\n\n        location = Location.posix_tz(\"America/New_York\", \"EST5EDT-24:59:59,M3.2.0,M11.1.0\")\n        location.name.should eq(\"America/New_York\")\n        location.zones.should eq [\n          Location::Zone.new(\"EST\", -18000, false),\n          Location::Zone.new(\"EDT\", 89999, true),\n        ]\n        location.transitions.should be_empty\n\n        location = Location.posix_tz(\"America/New_York\", \"EST-24:59:59EDT,M3.2.0,M11.1.0\")\n        location.name.should eq(\"America/New_York\")\n        location.zones.should eq [\n          Location::Zone.new(\"EST\", 89999, false),\n          Location::Zone.new(\"EDT\", 93599, true),\n        ]\n        location.transitions.should be_empty\n      end\n\n      it \"parses string with all-year DST\" do\n        location = Location.posix_tz(\"America/New_York\", \"EST5EDT,0/0,J365/25\")\n        location.name.should eq(\"America/New_York\")\n        location.zones.should eq [\n          Location::Zone.new(\"EDT\", -14400, true),\n        ]\n        location.transitions.should be_empty\n\n        location = Location.posix_tz(\"America/New_York\", \"XXX-6EDT-4:30:10,J1/0,J365/22:30:10\")\n        location.name.should eq(\"America/New_York\")\n        location.zones.should eq [\n          Location::Zone.new(\"EDT\", 16210, true),\n        ]\n        location.transitions.should be_empty\n      end\n\n      it \"errors on invalid TZ strings\" do\n        # std\n        assert_tz_raises \"\"\n        assert_tz_raises \"G\"\n        assert_tz_raises \"GM\"\n        assert_tz_raises \"<>\"\n        assert_tz_raises \"<G>\"\n        assert_tz_raises \"<GM>\"\n        assert_tz_raises \"<GMT\"\n        assert_tz_raises \"012\"\n        assert_tz_raises \"+00\"\n        assert_tz_raises \"-00\"\n        assert_tz_raises \"<$aa>\"\n        assert_tz_raises \"?\"\n        assert_tz_raises \":foobar\"\n        assert_tz_raises \"/foo/bar\"\n        assert_tz_raises \"Europe/Berlin\"\n\n        # std offset\n        assert_tz_raises \"EST\"\n        assert_tz_raises \"EST \"\n        assert_tz_raises \"EST 5\"\n        assert_tz_raises \"EST25\"\n        assert_tz_raises \"EST123\"\n        assert_tz_raises \"EST00123\"\n        assert_tz_raises \"EST-25\"\n        assert_tz_raises \"EST-123\"\n        assert_tz_raises \"EST-00123\"\n        assert_tz_raises \"EST4:\"\n        assert_tz_raises \"EST4:60\"\n        assert_tz_raises \"EST4:+30\"\n        assert_tz_raises \"EST4:-01\"\n        assert_tz_raises \"EST4:20:\"\n        assert_tz_raises \"EST4:20:60\"\n        assert_tz_raises \"EST4:20:+30\"\n        assert_tz_raises \"EST4:20:-01\"\n\n        # dst\n        assert_tz_raises \"EST5 \"\n        assert_tz_raises \"EST5G\"\n        assert_tz_raises \"EST5GM\"\n        assert_tz_raises \"EST5<>\"\n        assert_tz_raises \"EST5<GM>\"\n        assert_tz_raises \"EST5<GMT\"\n        assert_tz_raises \"EST5<$aa>\"\n        assert_tz_raises \"EST5+00\"\n        assert_tz_raises \"EST5-00\"\n\n        # dst offset\n        assert_tz_raises \"EST5EDT4:\"\n        assert_tz_raises \"EST5EDT4:60\"\n        assert_tz_raises \"EST5EDT4:+30\"\n        assert_tz_raises \"EST5EDT4:-01\"\n        assert_tz_raises \"EST5EDT4:20:\"\n        assert_tz_raises \"EST5EDT4:20:60\"\n        assert_tz_raises \"EST5EDT4:20:+30\"\n        assert_tz_raises \"EST5EDT4:20:-01\"\n\n        # start\n        assert_tz_raises \"EST5EDT\"\n        assert_tz_raises \"EST5EDT,\"\n        assert_tz_raises \"EST5EDT,A\"\n        assert_tz_raises \"EST5EDT,J0\"\n        assert_tz_raises \"EST5EDT,J366\"\n        assert_tz_raises \"EST5EDT,-1\"\n        assert_tz_raises \"EST5EDT,366\"\n        assert_tz_raises \"EST5EDT,M3\"\n        assert_tz_raises \"EST5EDT,M3.\"\n        assert_tz_raises \"EST5EDT,M3.2\"\n        assert_tz_raises \"EST5EDT,M3.2.\"\n        assert_tz_raises \"EST5EDT,M0.2.0\"\n        assert_tz_raises \"EST5EDT,M13.2.0\"\n        assert_tz_raises \"EST5EDT,M3.0.0\"\n        assert_tz_raises \"EST5EDT,M3.6.0\"\n        assert_tz_raises \"EST5EDT,M3.2.-1\"\n        assert_tz_raises \"EST5EDT,M3.2.7\"\n        assert_tz_raises \"EST5EDT,M3.2.0/\"\n        assert_tz_raises \"EST5EDT,M3.2.0/168\"\n        assert_tz_raises \"EST5EDT,M3.2.0/-168\"\n\n        # end\n        assert_tz_raises \"EST5EDT,M3.2.0\"\n        assert_tz_raises \"EST5EDT,M3.2.0,\"\n        assert_tz_raises \"EST5EDT,M3.2.0,A\"\n        assert_tz_raises \"EST5EDT,M3.2.0,J0\"\n        assert_tz_raises \"EST5EDT,M3.2.0,J366\"\n        assert_tz_raises \"EST5EDT,M3.2.0,-1\"\n        assert_tz_raises \"EST5EDT,M3.2.0,366\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M0.1.0\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M13.1.0\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.0.0\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.6.0\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.-1\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.7\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.0/\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.0/168\"\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.0/-168\"\n\n        # trailing characters\n        assert_tz_raises \"EST5EDT,M3.2.0,M11.1.0 \"\n        assert_tz_raises \"EST5EDT,M3.2.0/2,M11.1.0/2 \"\n      end\n    end\n\n    describe \"#lookup\" do\n      it \"looks up\" do\n        with_zoneinfo do\n          location = Location.load(\"Europe/Berlin\")\n          zone, range = location.lookup_with_boundaries(Time.utc(2017, 11, 23, 22, 6, 12).to_unix)\n          zone.should eq Zone.new(\"CET\", 3600, false)\n          range.should eq({1509238800_i64, 1521939600_i64})\n        end\n      end\n\n      it \"handles dst change\" do\n        with_zoneinfo do\n          location = Location.load(\"Europe/Berlin\")\n          time = Time.utc(2017, 10, 29, 1, 0, 0)\n\n          summer = location.lookup(time - 1.second)\n          summer.name.should eq \"CEST\"\n          summer.offset.should eq 2 * SECONDS_PER_HOUR\n          summer.dst?.should be_true\n\n          winter = location.lookup(time)\n          winter.name.should eq \"CET\"\n          winter.offset.should eq 1 * SECONDS_PER_HOUR\n          winter.dst?.should be_false\n\n          last_ns = location.lookup(time - 1.nanosecond)\n          last_ns.name.should eq \"CEST\"\n          last_ns.offset.should eq 2 * SECONDS_PER_HOUR\n          last_ns.dst?.should be_true\n        end\n      end\n\n      it \"handles value after last transition\" do\n        with_zoneinfo do\n          location = Location.load(\"America/Argentina/Buenos_Aires\")\n          zone = location.lookup(Time.utc(5000, 1, 1))\n          zone.name.should eq \"-03\"\n          zone.offset.should eq -3 * 3600\n        end\n      end\n\n      # Test that we get the correct results for times before the first\n      # transition time. To do this we explicitly check early dates in a\n      # couple of specific timezones.\n      context \"first zone\" do\n        it \"PST8PDT\" do\n          with_zoneinfo do\n            location = Location.load(\"PST8PDT\")\n            zone1 = location.lookup(-1633269601)\n            zone2 = location.lookup(-1633269601 + 1)\n            zone1.name.should eq \"PST\"\n            zone1.offset.should eq -8 * SECONDS_PER_HOUR\n            zone2.name.should eq \"PDT\"\n            zone2.offset.should eq -7 * SECONDS_PER_HOUR\n          end\n        end\n\n        it \"Pacific/Fakaofo\" do\n          with_zoneinfo do\n            location = Location.load(\"Pacific/Fakaofo\")\n            zone1 = location.lookup(1325242799)\n            zone2 = location.lookup(1325242799 + 1)\n            zone1.name.should eq \"-11\"\n            zone1.offset.should eq -11 * SECONDS_PER_HOUR\n            zone2.name.should eq \"+13\"\n            zone2.offset.should eq 13 * SECONDS_PER_HOUR\n          end\n        end\n      end\n\n      it \"caches last zone\" do\n        with_zoneinfo do\n          location = Time::Location.load(\"Europe/Berlin\")\n\n          location.@cached_range.should eq({Int64::MIN, Int64::MIN})\n          location.@cached_zone.should eq Zone.new(\"LMT\", 3208, false)\n\n          expected_zone = Zone.new(\"CET\", 3600, false)\n\n          location.lookup(Time.utc(2017, 11, 23, 22, 6, 12)).should eq expected_zone\n\n          location.@cached_range.should eq({1509238800_i64, 1521939600_i64})\n          location.@cached_zone.should eq expected_zone\n        end\n      end\n\n      it \"reads from cache\" do\n        with_zoneinfo do\n          location = Location.load(\"Europe/Berlin\")\n          location.lookup(Time.utc(2017, 11, 23, 22, 6, 12)).should eq Zone.new(\"CET\", 3600, false)\n          cached_zone = Zone.new(\"MyZone\", 1234, true)\n          location.__cached_zone = cached_zone\n\n          location.lookup(Time.utc(2017, 11, 23, 22, 6, 12)).should eq cached_zone\n        end\n      end\n\n      context \"TZ string\" do\n        it \"looks up location with standard time only\" do\n          location = Location.posix_tz(\"Local\", \"EST5\")\n          zone, range = location.lookup_with_boundaries(Time.utc(2025, 1, 1, 22, 6, 12).to_unix)\n          zone.should eq(Zone.new(\"EST\", -18000, false))\n          range.should eq({Int64::MIN, Int64::MAX})\n        end\n\n        it \"looks up location with all-year DST\" do\n          location = Location.posix_tz(\"Local\", \"EST5EDT4,0/0,J365/25\")\n          zone, range = location.lookup_with_boundaries(Time.utc(2025, 1, 1, 22, 6, 12).to_unix)\n          zone.should eq(Zone.new(\"EDT\", -14400, true))\n          range.should eq({Int64::MIN, Int64::MAX})\n        end\n\n        context \"transition dates\" do\n          it \"supports one-based ordinal days\" do\n            assert_tz_boundaries \"EST5EDT4,J1/2,J365/2\",\n              Time.utc(2025, 12, 31, 6, 0, 0), Time.utc(2026, 1, 1, 7, 0, 0),\n              Time.utc(2026, 12, 31, 6, 0, 0), Time.utc(2027, 1, 1, 7, 0, 0)\n\n            assert_tz_boundaries \"EST5EDT4,J1/2,J365/2\",\n              Time.utc(2027, 12, 31, 6, 0, 0), Time.utc(2028, 1, 1, 7, 0, 0),\n              Time.utc(2028, 12, 31, 6, 0, 0), Time.utc(2029, 1, 1, 7, 0, 0)\n          end\n\n          it \"excludes Feb 29 if one-based\" do\n            assert_tz_boundaries \"EST5EDT4,J59/2,J60/2\",\n              Time.utc(2027, 3, 1, 6, 0, 0), Time.utc(2028, 2, 28, 7, 0, 0),\n              Time.utc(2028, 3, 1, 6, 0, 0), Time.utc(2029, 2, 28, 7, 0, 0)\n          end\n\n          it \"supports zero-based ordinal days\" do\n            assert_tz_boundaries \"EST5EDT4,50/2,280/2\",\n              Time.utc(2025, 10, 8, 6, 0, 0), Time.utc(2026, 2, 20, 7, 0, 0),\n              Time.utc(2026, 10, 8, 6, 0, 0), Time.utc(2027, 2, 20, 7, 0, 0)\n\n            assert_tz_boundaries \"EST5EDT4,50/2,280/2\",\n              Time.utc(2027, 10, 8, 6, 0, 0), Time.utc(2028, 2, 20, 7, 0, 0),\n              Time.utc(2028, 10, 7, 6, 0, 0), Time.utc(2029, 2, 20, 7, 0, 0)\n          end\n\n          it \"includes Feb 29 if zero-based\" do\n            assert_tz_boundaries \"EST5EDT4,59/2,60/2\",\n              Time.utc(2027, 3, 2, 6, 0, 0), Time.utc(2028, 2, 29, 7, 0, 0),\n              Time.utc(2028, 3, 1, 6, 0, 0), Time.utc(2029, 3, 1, 7, 0, 0)\n          end\n\n          it \"supports month + week + day of week\" do\n            tz = \"EST5EDT4,M3.2.0/2,M11.1.0/2\"\n\n            trans = [\n              {Time.utc(2020, 11, 1, 6, 0, 0), Time.utc(2021, 3, 14, 7, 0, 0)},\n              {Time.utc(2021, 11, 7, 6, 0, 0), Time.utc(2022, 3, 13, 7, 0, 0)},\n              {Time.utc(2022, 11, 6, 6, 0, 0), Time.utc(2023, 3, 12, 7, 0, 0)},\n              {Time.utc(2023, 11, 5, 6, 0, 0), Time.utc(2024, 3, 10, 7, 0, 0)},\n              {Time.utc(2024, 11, 3, 6, 0, 0), Time.utc(2025, 3, 9, 7, 0, 0)},\n              {Time.utc(2025, 11, 2, 6, 0, 0), Time.utc(2026, 3, 8, 7, 0, 0)},\n              {Time.utc(2026, 11, 1, 6, 0, 0), Time.utc(2027, 3, 14, 7, 0, 0)},\n              {Time.utc(2027, 11, 7, 6, 0, 0), Time.utc(2028, 3, 12, 7, 0, 0)},\n              {Time.utc(2028, 11, 5, 6, 0, 0), Time.utc(2029, 3, 11, 7, 0, 0)},\n            ]\n\n            trans.each_cons_pair do |(t0, t1), (t2, t3)|\n              assert_tz_boundaries(tz, t0, t1, t2, t3)\n            end\n          end\n\n          it \"handles time zone differences other than 1 hour\" do\n            assert_tz_boundaries \"EST4:30EDT-1:23:45,M3.2.0,M11.1.0\",\n              Time.utc(2024, 11, 3, 0, 36, 15), Time.utc(2025, 3, 9, 6, 30, 0),\n              Time.utc(2025, 11, 2, 0, 36, 15), Time.utc(2026, 3, 8, 6, 30, 0)\n          end\n\n          it \"defaults transition times to 02:00:00 local time\" do\n            assert_tz_boundaries \"EST5EDT,M3.2.0,M11.1.0\",\n              Time.utc(2024, 11, 3, 6, 0, 0), Time.utc(2025, 3, 9, 7, 0, 0),\n              Time.utc(2025, 11, 2, 6, 0, 0), Time.utc(2026, 3, 8, 7, 0, 0)\n          end\n\n          it \"supports transition times from -167 to 167 hours\" do\n            assert_tz_boundaries \"EST5EDT,M3.2.0/-167,M11.1.0/167\",\n              Time.utc(2024, 11, 10, 3, 0, 0), Time.utc(2025, 3, 2, 6, 0, 0),\n              Time.utc(2025, 11, 9, 3, 0, 0), Time.utc(2026, 3, 1, 6, 0, 0)\n          end\n\n          it \"handles years beginning and ending in DST\" do\n            tz = \"AEST-10AEDT,M10.1.0,M4.1.0/3\"\n\n            trans = [\n              {Time.utc(2020, 4, 4, 16, 0, 0), Time.utc(2020, 10, 3, 16, 0, 0)},\n              {Time.utc(2021, 4, 3, 16, 0, 0), Time.utc(2021, 10, 2, 16, 0, 0)},\n              {Time.utc(2022, 4, 2, 16, 0, 0), Time.utc(2022, 10, 1, 16, 0, 0)},\n              {Time.utc(2023, 4, 1, 16, 0, 0), Time.utc(2023, 9, 30, 16, 0, 0)},\n              {Time.utc(2024, 4, 6, 16, 0, 0), Time.utc(2024, 10, 5, 16, 0, 0)},\n              {Time.utc(2025, 4, 5, 16, 0, 0), Time.utc(2025, 10, 4, 16, 0, 0)},\n              {Time.utc(2026, 4, 4, 16, 0, 0), Time.utc(2026, 10, 3, 16, 0, 0)},\n              {Time.utc(2027, 4, 3, 16, 0, 0), Time.utc(2027, 10, 2, 16, 0, 0)},\n              {Time.utc(2028, 4, 1, 16, 0, 0), Time.utc(2028, 9, 30, 16, 0, 0)},\n              {Time.utc(2029, 3, 31, 16, 0, 0), Time.utc(2029, 10, 6, 16, 0, 0)},\n            ]\n\n            trans.each_cons_pair do |(t0, t1), (t2, t3)|\n              assert_tz_boundaries(tz, t0, t1, t2, t3)\n            end\n          end\n\n          it \"handles very distant years\" do\n            assert_tz_boundaries \"EST5EDT4,M3.2.0/2,M11.1.0/2\",\n              Time.utc(1583, 11, 6, 6, 0, 0), Time.utc(1584, 3, 11, 7, 0, 0),\n              Time.utc(1584, 11, 4, 6, 0, 0), Time.utc(1585, 3, 10, 7, 0, 0)\n\n            assert_tz_boundaries \"EST5EDT4,M3.2.0/2,M11.1.0/2\",\n              Time.utc(3332, 11, 2, 6, 0, 0), Time.utc(3333, 3, 8, 7, 0, 0),\n              Time.utc(3333, 11, 1, 6, 0, 0), Time.utc(3334, 3, 14, 7, 0, 0)\n          end\n        end\n      end\n\n      context \"zoneinfo + POSIX TZ string\" do\n        it \"looks up location beyond last transition time\" do\n          with_zoneinfo do\n            # \"CET-1CEST,M3.5.0,M10.5.0/3\"\n            # last transition is in year 2037\n            location = Location.load(\"Europe/Berlin\")\n            Time.unix(location.@transitions.last.when).year.should eq(2037)\n\n            assert_tz_boundaries location,\n              Zone.new(\"CET\", 3600, false), Zone.new(\"CEST\", 7200, true),\n              Time.utc(2037, 10, 25, 1, 0, 0), Time.utc(2038, 3, 28, 1, 0, 0),\n              Time.utc(2038, 10, 31, 1, 0, 0), Time.utc(2039, 3, 27, 1, 0, 0)\n\n            assert_tz_boundaries location,\n              Zone.new(\"CET\", 3600, false), Zone.new(\"CEST\", 7200, true),\n              Time.utc(3003, 10, 30, 1, 0, 0), Time.utc(3004, 3, 25, 1, 0, 0),\n              Time.utc(3004, 10, 28, 1, 0, 0), Time.utc(3005, 3, 31, 1, 0, 0)\n          end\n        end\n\n        it \"looks up location if TZ string has no transitions\" do\n          with_zoneinfo do\n            # Paraguay stopped observing DST since 2024\n            location = Location.load(\"America/Asuncion\")\n\n            zone, range = location.lookup_with_boundaries(Time.utc(2024, 10, 15, 2, 59, 59).to_unix)\n            zone.should eq(Zone.new(\"-03\", -10800, true))\n            range.should eq({Time.utc(2024, 10, 6, 4, 0, 0).to_unix, Time.utc(2024, 10, 15, 3, 0, 0).to_unix})\n\n            zone, range = location.lookup_with_boundaries(Time.utc(2024, 10, 15, 3, 0, 0).to_unix)\n            zone.should eq(Zone.new(\"-03\", -10800, false))\n            range.should eq({Time.utc(2024, 10, 15, 3, 0, 0).to_unix, Int64::MAX})\n          end\n        end\n      end\n    end\n  end\n\n  describe Time::Location::Zone do\n    it \"#inspect\" do\n      Time::Location::Zone.new(\"CET\", 3600, false).inspect.should eq \"Time::Location::Zone(CET +01:00 (3600s) STD)\"\n      Time::Location::Zone.new(\"CEST\", 7200, true).inspect.should eq \"Time::Location::Zone(CEST +02:00 (7200s) DST)\"\n      Time::Location::Zone.new(nil, 9000, true).inspect.should eq \"Time::Location::Zone(+02:30 (9000s) DST)\"\n      Time::Location::Zone.new(nil, 9012, true).inspect.should eq \"Time::Location::Zone(+02:30:12 (9012s) DST)\"\n    end\n\n    it \"#name\" do\n      Time::Location::Zone.new(\"CEST\", 7200, true).name.should eq \"CEST\"\n      Time::Location::Zone.new(nil, 9000, true).name.should eq \"+02:30\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/time/span_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\nprivate def expect_overflow(&)\n  expect_raises ArgumentError, \"Time::Span too big or too small\" do\n    yield\n  end\nend\n\ndescribe Time::Span do\n  it \"initializes\" do\n    t1 = Time::Span.new nanoseconds: 123_456_789_123\n    t1.to_s.should eq(\"00:02:03.456789123\")\n\n    t1 = Time::Span.new hours: 1, minutes: 2, seconds: 3\n    t1.to_s.should eq(\"01:02:03\")\n\n    t1 = Time::Span.new minutes: 2, seconds: 3\n    t1.to_s.should eq(\"00:02:03\")\n\n    t1 = Time::Span.new seconds: 3\n    t1.to_s.should eq(\"00:00:03\")\n\n    t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4\n    t1.to_s.should eq(\"1.02:03:04\")\n\n    t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000\n    t1.to_s.should eq(\"1.02:03:04.005000000\")\n\n    t1 = Time::Span.new days: -1, hours: 2, minutes: -3, seconds: 4, nanoseconds: -5_000_000\n    t1.to_s.should eq(\"-22:02:56.005000000\")\n\n    t1 = Time::Span.new weeks: 1, days: 2, hours: 3, minutes: 4, seconds: 5, nanoseconds: 6_000_000\n    t1.to_s.should eq(\"9.03:04:05.006000000\")\n\n    t1 = Time::Span.new hours: 25\n    t1.to_s.should eq(\"1.01:00:00\")\n  end\n\n  it \"initializes with type restrictions\" do\n    t = Time::Span.new seconds: 1_u8, nanoseconds: 1_u8\n    t.should eq(Time::Span.new seconds: 1, nanoseconds: 1)\n\n    t = Time::Span.new seconds: 127_i8, nanoseconds: 1_000_000_000\n    t.should eq(Time::Span.new seconds: 128)\n\n    t = Time::Span.new seconds: -128_i8, nanoseconds: -1_000_000_000\n    t.should eq(Time::Span.new seconds: -129)\n\n    t = Time::Span.new seconds: 255_u8, nanoseconds: 1_000_000_000\n    t.should eq(Time::Span.new seconds: 256)\n\n    t = Time::Span.new seconds: 0_u8, nanoseconds: -1_000_000_000\n    t.should eq(Time::Span.new seconds: -1)\n  end\n\n  it \"initializes with big seconds value\" do\n    t = Time::Span.new hours: 0, minutes: 0, seconds: 1231231231231\n    t.total_seconds.should eq(1231231231231)\n  end\n\n  it \"days overflows\" do\n    expect_overflow do\n      days = 106751991167301\n      Time::Span.new days: days\n    end\n  end\n\n  it \"max days\" do\n    expect_overflow do\n      Int64::MAX.days\n    end\n  end\n\n  it \"min days\" do\n    expect_overflow do\n      Int64::MIN.days\n    end\n  end\n\n  it \"max seconds\" do\n    ts = Int32::MAX.seconds\n    ts.days.should eq(24855)\n    ts.hours.should eq(3)\n    ts.minutes.should eq(14)\n    ts.seconds.should eq(7)\n    ts.milliseconds.should eq(0)\n    ts.nanoseconds.should eq(0)\n  end\n\n  it \"min seconds\" do\n    ts = Int32::MIN.seconds\n    ts.days.should eq(-24855)\n    ts.hours.should eq(-3)\n    ts.minutes.should eq(-14)\n    ts.seconds.should eq(-8)\n    ts.milliseconds.should eq(0)\n  end\n\n  it \"max milliseconds\" do\n    ts = Int32::MAX.milliseconds\n    ts.days.should eq(24)\n    ts.hours.should eq(20)\n    ts.minutes.should eq(31)\n    ts.seconds.should eq(23)\n    ts.milliseconds.should eq(647)\n  end\n\n  it \"min milliseconds\" do\n    ts = Int32::MIN.milliseconds\n    ts.days.should eq(-24)\n    ts.hours.should eq(-20)\n    ts.minutes.should eq(-31)\n    ts.seconds.should eq(-23)\n    ts.milliseconds.should eq(-648)\n  end\n\n  it \"negative timespan\" do\n    ts = Time::Span.new hours: -23, minutes: -59, seconds: -59\n    ts.days.should eq(0)\n    ts.hours.should eq(-23)\n    ts.minutes.should eq(-59)\n    ts.seconds.should eq(-59)\n    ts.milliseconds.should eq(0)\n  end\n\n  it \"test properties\" do\n    t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000\n    t2 = -t1\n\n    t1.days.should eq(1)\n    t1.hours.should eq(2)\n    t1.minutes.should eq(3)\n    t1.seconds.should eq(4)\n    t1.milliseconds.should eq(5)\n    t1.microseconds.should eq(5_000)\n    t1.nanoseconds.should eq(5_000_000)\n\n    t2.days.should eq(-1)\n    t2.hours.should eq(-2)\n    t2.minutes.should eq(-3)\n    t2.seconds.should eq(-4)\n    t2.milliseconds.should eq(-5)\n    t2.microseconds.should eq(-5_000)\n    t2.nanoseconds.should eq(-5_000_000)\n  end\n\n  it \"test compare\" do\n    t1 = Time::Span.new nanoseconds: -1\n    t2 = Time::Span.new nanoseconds: 1\n\n    (t1 <=> t2).should eq(-1)\n    (t2 <=> t1).should eq(1)\n    (t2 <=> t2).should eq(0)\n    (Time::Span::MIN <=> Time::Span::MAX).should eq(-1)\n\n    (t1 == t2).should be_false\n    (t1 > t2).should be_false\n    (t1 >= t2).should be_false\n    (t1 != t2).should be_true\n    (t1 < t2).should be_true\n    (t1 <= t2).should be_true\n  end\n\n  it \"test equals\" do\n    t1 = Time::Span.new nanoseconds: 1\n    t2 = Time::Span.new nanoseconds: 2\n\n    (t1 == t1).should be_true\n    (t1 == t2).should be_false\n    (t1 == \"hello\").should be_false\n  end\n\n  describe \"#step\" do\n    it_iterates \"basic\", [1.day, 2.days, 3.days, 4.days, 5.days], 1.days.step(to: 5.days, by: 1.day)\n  end\n\n  it \"test int extension methods\" do\n    1_000_000.days.to_s.should eq(\"1000000.00:00:00\")\n    12.microseconds.to_s.should eq(\"00:00:00.000012000\")\n    -12.microseconds.to_s.should eq(\"-00:00:00.000012000\")\n  end\n\n  it \"test float extension methods\" do\n    12.345.days.to_s.should eq(\"12.08:16:48\")\n    12.345.hours.to_s.should eq(\"12:20:42\")\n    12.345.minutes.to_s.should eq(\"00:12:20.700000000\")\n    12.345.seconds.to_s.should eq(\"00:00:12.345000000\")\n    12.345.milliseconds.to_s.should eq(\"00:00:00.012345000\")\n    -0.5.milliseconds.to_s.should eq(\"-00:00:00.000500000\")\n    0.5.milliseconds.to_s.should eq(\"00:00:00.000500000\")\n    -2.5.milliseconds.to_s.should eq(\"-00:00:00.002500000\")\n    2.5.milliseconds.to_s.should eq(\"00:00:00.002500000\")\n    -2.5.microseconds.to_s.should eq(\"-00:00:00.000002500\")\n    2.5.microseconds.to_s.should eq(\"00:00:00.000002500\")\n    0.0005.seconds.to_s.should eq(\"00:00:00.000500000\")\n\n    1_000_000.5.days.to_s.should eq(\"1000000.12:00:00\")\n  end\n\n  it \"test negate and abs\" do\n    (-Time::Span.new(nanoseconds: 1234500)).to_s.should eq(\"-00:00:00.001234500\")\n    Time::Span.new(nanoseconds: -1234500).abs.to_s.should eq(\"00:00:00.001234500\")\n    (-Time::Span.new(nanoseconds: 7700)).to_s.should eq(\"-00:00:00.000007700\")\n    (+Time::Span.new(nanoseconds: 7700)).to_s.should eq(\"00:00:00.000007700\")\n  end\n\n  it \"test hash code\" do\n    t1 = Time::Span.new(nanoseconds: 77)\n    t2 = Time::Span.new(nanoseconds: 77)\n    t1.hash.should eq(t2.hash)\n  end\n\n  describe \"arithmetic\" do\n    it \"#+\" do\n      t1 = Time::Span.new days: 2, hours: 3, minutes: 4, seconds: 5, nanoseconds: 6_000_000\n      t2 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000\n      t3 = t1 + t2\n\n      t3.days.should eq(3)\n      t3.hours.should eq(5)\n      t3.minutes.should eq(7)\n      t3.seconds.should eq(9)\n      t3.milliseconds.should eq(11)\n      t3.nanoseconds.should eq(11_000_000)\n      t3.to_s.should eq(\"3.05:07:09.011000000\")\n\n      expect_raises(OverflowError) do\n        Time::Span::MAX + Time::Span.new(seconds: 1)\n      end\n      expect_raises(OverflowError) do\n        Time::Span.new(seconds: Int64::MAX) + Time::Span.new(seconds: 1)\n      end\n      (Time::Span.new(nanoseconds: Int64::MAX) + Time::Span.new(nanoseconds: 1)).should eq Time::Span.new days: 106751, hours: 23, minutes: 47, seconds: 16, nanoseconds: 854775808\n    end\n\n    it \"#-\" do\n      t1 = Time::Span.new days: 2, hours: 3, minutes: 4, seconds: 5, nanoseconds: 6_000_000\n      t2 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000\n      t3 = t1 - t2\n\n      t3.to_s.should eq(\"1.01:01:01.001000000\")\n\n      expect_raises(OverflowError) do\n        Time::Span::MIN - Time::Span.new(seconds: 1)\n      end\n      expect_raises(OverflowError) do\n        Time::Span.new(seconds: Int64::MIN) - Time::Span.new(seconds: 1)\n      end\n      (Time::Span.new(nanoseconds: Int64::MIN) - Time::Span.new(nanoseconds: 1)).should eq -Time::Span.new days: 106751, hours: 23, minutes: 47, seconds: 16, nanoseconds: 854775809\n    end\n\n    it \"#*\" do\n      t1 = Time::Span.new days: 5, hours: 4, minutes: 3, seconds: 2, nanoseconds: 1_000_000\n      t2 = t1 * 61\n      t3 = t1 * 0.5\n\n      t2.should eq(Time::Span.new days: 315, hours: 7, minutes: 5, seconds: 2, nanoseconds: 61_000_000)\n      t3.should eq(Time::Span.new days: 2, hours: 14, minutes: 1, seconds: 31, nanoseconds: 500_000)\n\n      expect_raises(OverflowError) do\n        Time::Span::MAX * 2\n      end\n      t = Time::Span.new(seconds: Int64::MAX // 2 + 1)\n      expect_raises(OverflowError) do\n        t * 2\n      end\n      t = Time::Span.new(nanoseconds: Int64::MAX // 2 + 1)\n      (t * 2).should eq Time::Span.new days: 106751, hours: 23, minutes: 47, seconds: 16, nanoseconds: 854775808\n    end\n\n    it \"#/(Number)\" do\n      t1 = Time::Span.new days: 3, hours: 3, minutes: 3, seconds: 3, nanoseconds: 3_000_000\n      t2 = t1 / 2\n      t3 = t1 / 1.5\n\n      t2.should eq(Time::Span.new(days: 1, hours: 13, minutes: 31, seconds: 31, nanoseconds: 501_000_000) + Time::Span.new(nanoseconds: 500_000))\n      t3.should eq(Time::Span.new days: 2, hours: 2, minutes: 2, seconds: 2, nanoseconds: 2_000_000)\n\n      expect_raises(DivisionByZeroError) do\n        Time::Span::MAX / 0\n      end\n    end\n\n    it \"#/(self)\" do\n      ratio = 20.minutes / 15.seconds\n      ratio.should eq(80.0)\n\n      ratio2 = 45.seconds / 1.minute\n      ratio2.should eq(0.75)\n    end\n\n    it \"#sign\" do\n      Time::Span.new(days: 2).sign.should eq 1\n      Time::Span.new(days: -2).sign.should eq -1\n      Time::Span.new.sign.should eq 0\n      Time::Span.new(nanoseconds: -2).sign.should eq -1\n      Time::Span.new(nanoseconds: 2).sign.should eq 1\n    end\n  end\n\n  it \"test to_s\" do\n    t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000\n    t2 = -t1\n\n    t1.to_s.should eq(\"1.02:03:04.005000000\")\n    t2.to_s.should eq(\"-1.02:03:04.005000000\")\n    Time::Span::MAX.to_s.should eq(\"106751991167300.15:30:07.999999999\")\n    Time::Span::MIN.to_s.should eq(\"-106751991167300.15:30:08.999999999\")\n    Time::Span::ZERO.to_s.should eq(\"00:00:00\")\n  end\n\n  it \"test totals\" do\n    t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000\n    t1.total_days.should be_close(1.08546, 1e-05)\n    t1.total_hours.should be_close(26.0511, 1e-04)\n    t1.total_minutes.should be_close(1563.07, 1e-02)\n    t1.total_seconds.should be_close(93784, 1e-01)\n    t1.total_milliseconds.should be_close(9.3784e+07, 1e+01)\n    t1.total_microseconds.should be_close(9.3784e+10, 1e+04)\n    t1.total_nanoseconds.should be_close(9.3784e+13, 1e+07)\n    t1.to_f.should be_close(93784, 1e-01)\n    t1.to_i.should eq(93784)\n\n    t2 = Time::Span.new nanoseconds: 123456\n    t2.total_seconds.should be_close(0.000123456, 1e-06)\n  end\n\n  it \"should sum\" do\n    [1.second, 5.seconds].sum.should eq(6.seconds)\n  end\n\n  it \"#zero?\" do\n    Time::Span.zero.zero?.should be_true\n    Time::Span::ZERO.zero?.should be_true\n    Time::Span.new(nanoseconds: 123456789).zero?.should be_false\n  end\n\n  it \"#positive?\" do\n    Time::Span.new(nanoseconds: 123456789).positive?.should be_true\n    Time::Span.zero.positive?.should be_false\n    Time::Span.new(nanoseconds: -123456789).positive?.should be_false\n  end\n\n  it \"#negative?\" do\n    Time::Span.new(nanoseconds: 123456789).negative?.should be_false\n    Time::Span.zero.negative?.should be_false\n    Time::Span.new(nanoseconds: -123456789).negative?.should be_true\n  end\n\n  it \"converts units\" do\n    1.nanoseconds.should eq(Time::Span.new(nanoseconds: 1))\n    1.millisecond.should eq(1_000_000.nanoseconds)\n    1.milliseconds.should eq(1_000_000.nanoseconds)\n    1.second.should eq(1000.milliseconds)\n    1.seconds.should eq(1000.milliseconds)\n    1.minute.should eq(60.seconds)\n    1.minutes.should eq(60.seconds)\n    1.hour.should eq(60.minutes)\n    1.hours.should eq(60.minutes)\n    1.week.should eq(7.days)\n    2.weeks.should eq(14.days)\n    1.1.weeks.should eq(7.7.days)\n  end\n\n  it \"can subtract big amount using microseconds\" do\n    jan_1_2k = Time.utc(2000, 1, 1)\n    past = Time.utc(5, 2, 3, 0, 0, 0)\n    delta = (past - jan_1_2k).total_microseconds.to_i64\n    past2 = jan_1_2k + delta.microseconds\n    past2.should eq(past)\n  end\n\n  it \"can subtract big amount using milliseconds\" do\n    jan_1_2k = Time.utc(2000, 1, 1)\n    past = Time.utc(5, 2, 3, 0, 0, 0)\n    delta = (past - jan_1_2k).total_milliseconds.to_i64\n    past2 = jan_1_2k + delta.milliseconds\n    past2.should eq(past)\n  end\nend\n"
  },
  {
    "path": "spec/std/time/time_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/time\"\nrequire \"spec/helpers/iterate\"\n\nCALENDAR_WEEK_TEST_DATA = [\n  { {1981, 1, 1}, {1981, 1, 4} },\n  { {1982, 1, 1}, {1981, 53, 5} },\n  { {1983, 1, 1}, {1982, 52, 6} },\n  { {1984, 1, 1}, {1983, 52, 7} },\n  { {1985, 1, 1}, {1985, 1, 2} },\n  { {1985, 4, 12}, {1985, 15, 5} },\n  { {1986, 1, 1}, {1986, 1, 3} },\n  { {1987, 1, 1}, {1987, 1, 4} },\n  { {1988, 1, 1}, {1987, 53, 5} },\n  { {1989, 1, 1}, {1988, 52, 7} },\n  { {1990, 1, 1}, {1990, 1, 1} },\n  { {1991, 1, 1}, {1991, 1, 2} },\n  { {1992, 1, 1}, {1992, 1, 3} },\n  { {1993, 1, 1}, {1992, 53, 5} },\n  { {1994, 1, 1}, {1993, 52, 6} },\n  { {1995, 1, 2}, {1995, 1, 1} },\n  { {1996, 1, 1}, {1996, 1, 1} },\n  { {1996, 1, 7}, {1996, 1, 7} },\n  { {1996, 1, 8}, {1996, 2, 1} },\n  { {1997, 1, 1}, {1997, 1, 3} },\n  { {1998, 1, 1}, {1998, 1, 4} },\n  { {1999, 1, 1}, {1998, 53, 5} },\n  { {2000, 1, 1}, {1999, 52, 6} },\n  { {2001, 1, 1}, {2001, 1, 1} },\n  { {2002, 1, 1}, {2002, 1, 2} },\n  { {2003, 1, 1}, {2003, 1, 3} },\n  { {2004, 1, 1}, {2004, 1, 4} },\n  { {2005, 1, 1}, {2004, 53, 6} },\n  { {2006, 1, 1}, {2005, 52, 7} },\n  { {2007, 1, 1}, {2007, 1, 1} },\n  { {2008, 1, 1}, {2008, 1, 2} },\n  { {2009, 1, 1}, {2009, 1, 4} },\n  { {2010, 1, 1}, {2009, 53, 5} },\n  { {2010, 1, 1}, {2009, 53, 5} },\n  { {2011, 1, 1}, {2010, 52, 6} },\n  { {2011, 1, 2}, {2010, 52, 7} },\n  { {2011, 1, 3}, {2011, 1, 1} },\n  { {2011, 1, 4}, {2011, 1, 2} },\n  { {2011, 1, 5}, {2011, 1, 3} },\n  { {2011, 1, 6}, {2011, 1, 4} },\n  { {2011, 1, 7}, {2011, 1, 5} },\n  { {2011, 1, 8}, {2011, 1, 6} },\n  { {2011, 1, 9}, {2011, 1, 7} },\n  { {2011, 1, 10}, {2011, 2, 1} },\n  { {2011, 1, 11}, {2011, 2, 2} },\n  { {2011, 6, 12}, {2011, 23, 7} },\n  { {2011, 6, 13}, {2011, 24, 1} },\n  { {2011, 12, 25}, {2011, 51, 7} },\n  { {2011, 12, 26}, {2011, 52, 1} },\n  { {2011, 12, 27}, {2011, 52, 2} },\n  { {2011, 12, 28}, {2011, 52, 3} },\n  { {2011, 12, 29}, {2011, 52, 4} },\n  { {2011, 12, 30}, {2011, 52, 5} },\n  { {2011, 12, 31}, {2011, 52, 6} },\n  { {1995, 1, 1}, {1994, 52, 7} },\n  { {2012, 1, 1}, {2011, 52, 7} },\n  { {2012, 1, 2}, {2012, 1, 1} },\n  { {2012, 1, 8}, {2012, 1, 7} },\n  { {2012, 1, 9}, {2012, 2, 1} },\n  { {2012, 12, 23}, {2012, 51, 7} },\n  { {2012, 12, 24}, {2012, 52, 1} },\n  { {2012, 12, 30}, {2012, 52, 7} },\n  { {2012, 12, 31}, {2013, 1, 1} },\n  { {2013, 1, 1}, {2013, 1, 2} },\n  { {2013, 1, 6}, {2013, 1, 7} },\n  { {2013, 1, 7}, {2013, 2, 1} },\n  { {2013, 12, 22}, {2013, 51, 7} },\n  { {2013, 12, 23}, {2013, 52, 1} },\n  { {2013, 12, 29}, {2013, 52, 7} },\n  { {2013, 12, 30}, {2014, 1, 1} },\n  { {2014, 1, 1}, {2014, 1, 3} },\n  { {2014, 1, 5}, {2014, 1, 7} },\n  { {2014, 1, 6}, {2014, 2, 1} },\n  { {2015, 1, 1}, {2015, 1, 4} },\n  { {2016, 1, 1}, {2015, 53, 5} },\n  { {2017, 1, 1}, {2016, 52, 7} },\n  { {2018, 1, 1}, {2018, 1, 1} },\n  { {2019, 1, 1}, {2019, 1, 2} },\n  { {2020, 1, 1}, {2020, 1, 3} },\n  { {2021, 1, 1}, {2020, 53, 5} },\n  { {2022, 1, 1}, {2021, 52, 6} },\n  { {2023, 1, 1}, {2022, 52, 7} },\n  { {2024, 1, 1}, {2024, 1, 1} },\n  { {2025, 1, 1}, {2025, 1, 3} },\n  { {2026, 1, 1}, {2026, 1, 4} },\n  { {2027, 1, 1}, {2026, 53, 5} },\n  { {2028, 1, 1}, {2027, 52, 6} },\n  { {2029, 1, 1}, {2029, 1, 1} },\n  { {2030, 1, 1}, {2030, 1, 2} },\n  { {2031, 1, 1}, {2031, 1, 3} },\n  { {2032, 1, 1}, {2032, 1, 4} },\n  { {2033, 1, 1}, {2032, 53, 6} },\n  { {2034, 1, 1}, {2033, 52, 7} },\n  { {2035, 1, 1}, {2035, 1, 1} },\n  { {2036, 1, 1}, {2036, 1, 2} },\n  { {2037, 1, 1}, {2037, 1, 4} },\n  { {2038, 1, 1}, {2037, 53, 5} },\n  { {2039, 1, 1}, {2038, 52, 6} },\n  { {2040, 1, 1}, {2039, 52, 7} },\n]\n\ndescribe Time do\n  describe \".local\" do\n    it \"initializes\" do\n      t1 = Time.local 2002, 2, 25\n      t1.date.should eq({2002, 2, 25})\n      t1.year.should eq(2002)\n      t1.month.should eq(2)\n      t1.day.should eq(25)\n      t1.hour.should eq(0)\n      t1.minute.should eq(0)\n      t1.second.should eq(0)\n      t1.local?.should be_true\n\n      t2 = Time.local 2002, 2, 25, 15, 25, 13, nanosecond: 8\n      t2.date.should eq({2002, 2, 25})\n      t2.year.should eq(2002)\n      t2.month.should eq(2)\n      t2.day.should eq(25)\n      t2.hour.should eq(15)\n      t2.minute.should eq(25)\n      t2.second.should eq(13)\n      t2.nanosecond.should eq(8)\n      t2.local?.should be_true\n    end\n\n    it \"initializes max value\" do\n      time = Time.local(9999, 12, 31, 23, 59, 59, nanosecond: 999_999_999)\n      time.year.should eq(9999)\n      time.month.should eq(12)\n      time.day.should eq(31)\n      time.hour.should eq(23)\n      time.minute.should eq(59)\n      time.second.should eq(59)\n      time.nanosecond.should eq(999_999_999)\n\n      time = Time.local(9999, 12, 31, 23, 59, 59, nanosecond: 999_999_999, location: Time::Location.posix_tz(\"Local\", \"EST5EDT,M3.2.0,M11.1.0\"))\n      time.year.should eq(9999)\n      time.month.should eq(12)\n      time.day.should eq(31)\n      time.hour.should eq(23)\n      time.minute.should eq(59)\n      time.second.should eq(59)\n      time.nanosecond.should eq(999_999_999)\n    end\n\n    it \"fails with negative nanosecond\" do\n      expect_raises ArgumentError, \"Invalid time\" do\n        Time.local(9999, 12, 31, 23, 59, 59, nanosecond: -1)\n      end\n    end\n\n    it \"fails with too big nanoseconds\" do\n      expect_raises ArgumentError, \"Invalid time\" do\n        Time.local(9999, 12, 31, 23, 59, 59, nanosecond: 1_000_000_000)\n      end\n    end\n\n    it \"checks boundary at time min\" do\n      {-5 * 3600, -1, 0, 1, 5 * 3600}.each do |offset|\n        seconds = -offset.to_i64\n        location = Time::Location.fixed(offset)\n        Time.new(seconds: seconds + 1, nanoseconds: 0, location: location)\n        Time.new(seconds: seconds, nanoseconds: 0, location: location)\n        expect_raises ArgumentError, \"Invalid time\" do\n          Time.new(seconds: seconds - 1, nanoseconds: 0, location: location)\n        end\n      end\n    end\n\n    it \"checks boundary at time max\" do\n      {-5 * 3600, -1, 0, 1, 5 * 3600}.each do |offset|\n        seconds = Time::MAX_SECONDS - offset.to_i64\n        location = Time::Location.fixed(offset)\n        Time.new(seconds: seconds - 1, nanoseconds: 0, location: location)\n        Time.new(seconds: seconds, nanoseconds: 0, location: location)\n        expect_raises ArgumentError, \"Invalid time\" do\n          Time.new(seconds: seconds + 1, nanoseconds: 0, location: location)\n        end\n      end\n    end\n\n    it \"accepts midnight 24:00\" do\n      Time.utc(2020, 5, 21, 24, 0, 0).should eq Time.utc(2020, 5, 22, 0, 0, 0)\n\n      expect_raises ArgumentError, \"Invalid time\" do\n        Time.utc(2020, 5, 21, 24, 0, 0, nanosecond: 1)\n      end\n\n      expect_raises ArgumentError, \"Invalid time\" do\n        Time.utc(2020, 5, 21, 24, 0, 1)\n      end\n\n      expect_raises ArgumentError, \"Invalid time\" do\n        Time.utc(2020, 5, 21, 24, 1, 0)\n      end\n    end\n  end\n\n  it \"UNIX_EPOCH\" do\n    Time::UNIX_EPOCH.should eq(Time.utc(1970, 1, 1))\n  end\n\n  it \".unix\" do\n    seconds = 1439404155\n    time = Time.unix(seconds)\n    time.should eq(Time.utc(2015, 8, 12, 18, 29, 15))\n    time.to_unix.should eq(seconds)\n    time.utc?.should be_true\n  end\n\n  it \".unix_ms\" do\n    milliseconds = 1439404155000\n    time = Time.unix_ms(milliseconds)\n    time.should eq(Time.utc(2015, 8, 12, 18, 29, 15))\n    time.to_unix_ms.should eq(milliseconds)\n    time.utc?.should be_true\n  end\n\n  describe \".unix_ns\" do\n    it \"supports Int64 values\" do\n      nanoseconds = 1439404155001457425i64\n      time = Time.unix_ns(nanoseconds)\n      time.should eq(Time.utc(2015, 8, 12, 18, 29, 15, nanosecond: 1457425))\n      time.to_unix_ns.should eq(nanoseconds)\n      time.utc?.should be_true\n    end\n\n    it \"supports maximum valid time\" do\n      nanoseconds = Int128.new(\"253402300799999999999\")\n      time = Time.unix_ns(nanoseconds)\n      time.should eq(Time.utc(9999, 12, 31, 23, 59, 59, nanosecond: 999999999))\n      time.to_unix_ns.should eq(nanoseconds)\n      time.utc?.should be_true\n    end\n\n    it \"supports minimum valid time\" do\n      nanoseconds = Int128.new(\"-62135596800000000000\")\n      time = Time.unix_ns(nanoseconds)\n      time.should eq(Time.utc(1, 1, 1, 0, 0, 0, nanosecond: 0))\n      time.to_unix_ns.should eq(nanoseconds)\n      time.utc?.should be_true\n    end\n  end\n\n  describe \".local without arguments\" do\n    it \"current time is similar in different locations\" do\n      (Time.local - Time.utc).should be_close(0.seconds, 1.second)\n      (Time.local - Time.local(Time::Location.fixed(1234))).should be_close(0.seconds, 1.second)\n    end\n  end\n\n  describe \".monotonic\" do\n    it \"returns always increasing monotonic clock\" do\n      clock = Time.monotonic\n      Time.monotonic.should be >= clock\n    end\n  end\n\n  describe \".measure\" do\n    it \"measures elapsed time\" do\n      # NOTE: On some systems, the sleep may not always wait for 1ms and the fiber\n      #       be resumed early. We thus merely test that the method returns a\n      #       positive time span.\n      elapsed = Time.measure { sleep 1.millisecond }\n      elapsed.should be >= 0.seconds\n    end\n  end\n\n  it \"#clone\" do\n    time = Time.local\n    time.clone.should eq(time)\n  end\n\n  describe \"#shift\" do\n    it \"adds hours, minutes, seconds\" do\n      t1 = Time.utc(2002, 2, 25, 15, 25, 13)\n      t2 = t1 + Time::Span.new(hours: 3, minutes: 54, seconds: 1)\n\n      t2.should eq Time.utc(2002, 2, 25, 19, 19, 14)\n    end\n\n    it \"raises out of range min\" do\n      t1 = Time.utc(9980, 2, 25, 15, 25, 13)\n\n      expect_raises ArgumentError do\n        t1 + Time::Span.new(nanoseconds: Int64::MAX)\n      end\n    end\n\n    it \"raises out of range max\" do\n      t1 = Time.utc(1, 2, 25, 15, 25, 13)\n\n      expect_raises ArgumentError do\n        t1 + Time::Span.new(nanoseconds: Int64::MIN)\n      end\n    end\n\n    it \"checks boundary at time min\" do\n      {5 * 3600, 1, 0, -1, -5 * 3600}.each do |offset|\n        location = Time::Location.fixed(offset)\n\n        time = Time.local(1, 1, 1, location: location)\n        time.shift(0, 1).should eq Time.local(1, 1, 1, nanosecond: 1, location: location)\n        time.shift(0, 0).should eq time\n        expect_raises(ArgumentError) do\n          time.shift(0, -1)\n        end\n      end\n    end\n\n    it \"checks boundary at time max\" do\n      {5 * 3600, 1, 0, -1, -5 * 3600}.each do |offset|\n        location = Time::Location.fixed(offset)\n\n        time = Time.local(9999, 12, 31, 23, 59, 59, nanosecond: 999_999_999, location: location)\n        time.shift(0, -1).should eq Time.local(9999, 12, 31, 23, 59, 59, nanosecond: 999_999_998, location: location)\n        time.shift(0, 0).should eq time\n        expect_raises(ArgumentError) do\n          time.shift(0, 1)\n        end\n      end\n    end\n\n    it \"adds zero span\" do\n      time = Time.utc\n      time.shift(0, 0).should eq time\n    end\n\n    describe \"irregular calendaric unit ratios\" do\n      it \"shifts by a week if one day is left out\" do\n        # The week from 2011-12-25 to 2012-01-01 for example lasted only 6 days in Samoa,\n        # because it skipped 2011-12-28 due to changing time zone from -11:00 to +13:00.\n        with_zoneinfo do\n          samoa = Time::Location.load(\"Pacific/Apia\")\n          start = Time.local(2011, 12, 25, 0, 0, 0, location: samoa)\n\n          plus_one_week = start.shift days: 7\n          plus_one_week.should eq start + 6.days\n\n          plus_one_year = start.shift years: 1\n          plus_one_year.should eq start + 365.days # 2012 is a leap year so it should've been 366 days, but 2011-12-28 was skipped\n        end\n      end\n\n      it \"shifts by conceptual hour even if elapsed time is less\" do\n        # Venezuela switched from -4:30 to -4:00 on 2016-05-01, the hour between 2:00 and 3:00 lasted only 30 minutes\n        with_zoneinfo do\n          venezuela = Time::Location.load(\"America/Caracas\")\n          start = Time.local(2016, 5, 1, 2, 0, 0, location: venezuela)\n          plus_one_hour = start.shift hours: 1\n          plus_one_hour.should eq start + 30.minutes\n        end\n      end\n    end\n\n    describe \"adds days\" do\n      it \"simple\" do\n        time = Time.utc(2002, 2, 25, 15, 25, 13)\n        time = time + 3.days\n\n        time.should eq Time.utc(2002, 2, 28, 15, 25, 13)\n\n        time = time + 1.9.days\n        time.should eq Time.utc(2002, 3, 2, 13, 1, 13)\n\n        time = time + 0.2.days\n        time.should eq Time.utc(2002, 3, 2, 17, 49, 13)\n      end\n\n      it \"over dst\" do\n        with_zoneinfo do\n          location = Time::Location.load(\"Europe/Berlin\")\n          reference = Time.local(2017, 10, 28, 13, 37, location: location)\n          next_day = reference.shift days: 1\n\n          next_day.should eq reference + 25.hours\n        end\n      end\n\n      it \"out of range max\" do\n        time = Time.utc(2002, 2, 25, 15, 25, 13)\n        expect_raises ArgumentError do\n          time + 10000000.days\n        end\n      end\n\n      it \"out of range max (shift days)\" do\n        time = Time.utc(2002, 2, 25, 15, 25, 13)\n        expect_raises OverflowError do\n          time.shift days: 10000000\n        end\n      end\n\n      it \"out of range min\" do\n        time = Time.utc(2002, 2, 25, 15, 25, 13)\n        expect_raises ArgumentError do\n          time - 10000000.days\n        end\n      end\n\n      it \"out of range min (shift days)\" do\n        time = Time.utc(2002, 2, 25, 15, 25, 13)\n        expect_raises OverflowError do\n          time.shift days: -10000000\n        end\n      end\n    end\n\n    it \"adds months\" do\n      t = Time.utc 2014, 10, 30, 21, 18, 13\n\n      t2 = t.shift months: 1\n      t2.should eq Time.utc(2014, 11, 30, 21, 18, 13)\n\n      t2 = t.shift months: -1\n      t2.should eq Time.utc(2014, 9, 30, 21, 18, 13)\n\n      t = Time.utc 2014, 10, 31, 21, 18, 13\n      t2 = t.shift months: 1\n      t2.should eq Time.utc(2014, 11, 30, 21, 18, 13)\n\n      t = Time.utc 2014, 10, 31, 21, 18, 13\n      t2 = t.shift months: -1\n      t2.should eq Time.utc(2014, 9, 30, 21, 18, 13)\n\n      t = Time.utc 2014, 10, 31, 21, 18, 13\n      t2 = t.shift months: 6\n      t2.should eq Time.utc(2015, 4, 30, 21, 18, 13)\n    end\n\n    it \"adds years\" do\n      t = Time.utc 2014, 10, 30, 21, 18, 13\n      t2 = t.shift years: 1\n      t2.should eq Time.utc(2015, 10, 30, 21, 18, 13)\n\n      t = Time.utc 2014, 10, 30, 21, 18, 13\n      t2 = t.shift years: -2\n      t2.should eq Time.utc(2012, 10, 30, 21, 18, 13)\n    end\n\n    it \"adds hours\" do\n      time = Time.utc(2002, 2, 25, 15, 25, 13)\n\n      time = time + 10.hours\n      time.should eq Time.utc(2002, 2, 26, 1, 25, 13)\n\n      time = time - 3.7.hours\n      time.should eq Time.utc(2002, 2, 25, 21, 43, 13)\n\n      time = time + 3.732.hours\n      time.should eq Time.utc(2002, 2, 26, 1, 27, 8, nanosecond: 200_000_000)\n    end\n\n    it \"adds nanoseconds\" do\n      t1 = Time.utc(2002, 2, 25, 15, 25, 13)\n      t1 = t1.shift nanoseconds: 10_000_000_000_000_000\n\n      t1.should eq Time.utc(2002, 6, 21, 9, 11, 53)\n\n      t1 = t1.shift nanoseconds: -190_000_000_000_000_000\n      t1.should eq Time.utc(1996, 6, 13, 7, 25, 13)\n\n      t1 = t1.shift nanoseconds: 15_623_000\n      t1.should eq Time.utc(1996, 6, 13, 7, 25, 13, nanosecond: 15_623_000)\n    end\n\n    it \"preserves location when adding\" do\n      time = Time.utc\n      time.utc?.should be_true\n\n      (time + 5.minutes).utc?.should be_true\n\n      location = Time::Location.fixed(1234)\n      time = Time.local(location)\n      (time + 5.minutes).location.should eq location\n    end\n\n    it \"covers date boundaries with zone offset (#8741)\" do\n      zone = Time::Location.fixed(7 * 3600)\n\n      Time.local(2020, 2, 5, 0, 13, location: zone).shift(months: 3).should eq Time.local(2020, 5, 5, 0, 13, location: zone)\n    end\n\n    it \"covers date boundaries with zone offset (#10869)\" do\n      location = Time::Location.fixed(2 * 3600)\n      Time.local(2021, 7, 1, location: location).shift(months: 1).should eq Time.local(2021, 8, 1, location: location)\n    end\n  end\n\n  it \"#time_of_day\" do\n    t = Time.utc 2014, 10, 30, 21, 18, 13\n    t.time_of_day.should eq(Time::Span.new(hours: 21, minutes: 18, seconds: 13))\n  end\n\n  describe \"#day_of_week\" do\n    it \"gets day of week\" do\n      t = Time.utc 2014, 10, 30, 21, 18, 13\n      t.day_of_week.should eq(Time::DayOfWeek::Thursday)\n    end\n\n    CALENDAR_WEEK_TEST_DATA.each do |date, week_date|\n      it \"#{date.join('-')} is #{week_date[2]}\" do\n        Time.utc(*date).day_of_week.should eq Time::DayOfWeek.from_value(week_date[2])\n      end\n    end\n  end\n\n  it \"answers day name predicates\" do\n    7.times do |i|\n      time = Time.utc(2015, 2, 15 + i)\n      time.sunday?.should eq(i == 0)\n      time.monday?.should eq(i == 1)\n      time.tuesday?.should eq(i == 2)\n      time.wednesday?.should eq(i == 3)\n      time.thursday?.should eq(i == 4)\n      time.friday?.should eq(i == 5)\n      time.saturday?.should eq(i == 6)\n    end\n  end\n\n  describe \"#calendar_week\" do\n    CALENDAR_WEEK_TEST_DATA.each do |date, week_date|\n      it \"#{date.join('-')} to #{week_date[0]}-#{week_date[1]}\" do\n        Time.utc(*date).calendar_week.should eq({week_date[0], week_date[1]})\n      end\n    end\n  end\n\n  it \"#day_of_year\" do\n    t = Time.utc 2014, 10, 30, 21, 18, 13\n    t.day_of_year.should eq(303)\n  end\n\n  describe \"#<=>\" do\n    it \"compares\" do\n      t1 = Time.utc 2014, 10, 30, 21, 18, 13\n      t2 = Time.utc 2014, 10, 30, 21, 18, 14\n\n      (t1 <=> t2).should eq(-1)\n      (t1 == t2).should be_false\n      (t1 < t2).should be_true\n    end\n\n    it \"compares different locations\" do\n      time = Time.local(Time::Location.fixed(1234))\n      (time.to_utc <=> time).should eq(0)\n    end\n  end\n\n  describe \"#step\" do\n    days = (1..24).map { |d| Time.utc(2020, 12, d) }.to_a\n    it_iterates \"advent\", days, Time.utc(2020, 12, 1).step(to: Time.utc(2020, 12, 24), by: 1.day)\n  end\n\n  describe \"#to_unix\" do\n    it \"gets unix seconds\" do\n      t1 = Time.utc 2014, 10, 30, 21, 18, 13, nanosecond: 0\n      t1.to_unix.should eq(1414703893)\n      t1.to_unix_f.should be_close(1414703893, 1e-01)\n    end\n\n    it \"gets unix seconds at GMT\" do\n      t1 = Time.local(Time::Location.fixed(1234))\n      t1.to_unix.should eq(t1.to_utc.to_unix)\n      t1.to_unix_f.should be_close(t1.to_utc.to_unix_f, 1e-01)\n    end\n  end\n\n  it \"#year\" do\n    Time.utc(2008, 12, 31).year.should eq 2008\n    Time.utc(2000, 12, 31).year.should eq 2000\n    Time.utc(1900, 12, 31).year.should eq 1900\n    Time.utc(1800, 12, 31).year.should eq 1800\n    Time.utc(1700, 12, 31).year.should eq 1700\n    Time.utc(1600, 12, 31).year.should eq 1600\n    Time.utc(400, 12, 31).year.should eq 400\n    Time.utc(100, 12, 31).year.should eq 100\n    Time.utc(4, 12, 31).year.should eq 4\n    Time.utc(1, 1, 1).year.should eq 1\n  end\n\n  describe \"#to_s\" do\n    it \"prints string\" do\n      with_zoneinfo do\n        time = Time.local(2017, 11, 25, 22, 6, 17, location: Time::Location::UTC)\n        time.to_s.should eq \"2017-11-25 22:06:17 UTC\"\n\n        time = Time.local(2017, 11, 25, 22, 6, 17, location: Time::Location.fixed(-7200))\n        time.to_s.should eq \"2017-11-25 22:06:17 -02:00\"\n\n        time = Time.local(2017, 11, 25, 22, 6, 17, location: Time::Location.fixed(-7259))\n        time.to_s.should eq \"2017-11-25 22:06:17 -02:00:59\"\n\n        location = Time::Location.load(\"Europe/Berlin\")\n        time = Time.local(2017, 11, 25, 22, 6, 17, location: location)\n        time.to_s.should eq \"2017-11-25 22:06:17 +01:00\"\n      end\n    end\n\n    it \"prints date-time fields\" do\n      Time.utc(2014, 1, 30, 21, 18, 13).to_s.should eq(\"2014-01-30 21:18:13 UTC\")\n      Time.utc(2014, 10, 1, 21, 18, 13).to_s.should eq(\"2014-10-01 21:18:13 UTC\")\n      Time.utc(2014, 10, 30, 1, 18, 13).to_s.should eq(\"2014-10-30 01:18:13 UTC\")\n      Time.utc(2014, 10, 30, 21, 1, 13).to_s.should eq(\"2014-10-30 21:01:13 UTC\")\n      Time.utc(2014, 10, 30, 21, 18, 1).to_s.should eq(\"2014-10-30 21:18:01 UTC\")\n    end\n\n    it \"omits nanoseconds\" do\n      Time.utc(2014, 10, 30, 21, 18, 13).to_s.should eq(\"2014-10-30 21:18:13 UTC\")\n      Time.utc(2014, 10, 30, 21, 18, 13, nanosecond: 12345).to_s.should eq(\"2014-10-30 21:18:13 UTC\")\n    end\n\n    it \"prints offset for location\" do\n      with_zoneinfo do\n        location = Time::Location.load(\"Europe/Berlin\")\n        Time.local(2014, 10, 30, 21, 18, 13, location: location).to_s.should eq(\"2014-10-30 21:18:13 +01:00\")\n        Time.local(2014, 10, 30, 21, 18, 13, nanosecond: 123_456, location: location).to_s.should eq(\"2014-10-30 21:18:13 +01:00\")\n\n        Time.local(2014, 10, 10, 21, 18, 13, location: location).to_s.should eq(\"2014-10-10 21:18:13 +02:00\")\n        Time.local(2014, 10, 10, 21, 18, 13, nanosecond: 123_456, location: location).to_s.should eq(\"2014-10-10 21:18:13 +02:00\")\n      end\n    end\n\n    it \"prints offset for fixed location\" do\n      location = Time::Location.fixed(3601)\n      Time.local(2014, 1, 2, 3, 4, 5, location: location).to_s.should eq \"2014-01-02 03:04:05 +01:00:01\"\n      Time.local(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789, location: location).to_s.should eq \"2014-01-02 03:04:05 +01:00:01\"\n\n      t = Time.local 2014, 10, 30, 21, 18, 13, location: Time::Location.fixed(-9000)\n      t.to_s.should eq(\"2014-10-30 21:18:13 -02:30\")\n    end\n\n    it \"prints local time\" do\n      # Simulates loading non-fixed offset local time from /etc/localtime\n      old_local = Time::Location.local\n      begin\n        location = Time::Location.new \"Local\", [Time::Location::Zone.new(\"STZ\", 3600, false), Time::Location::Zone.new(\"DTZ\", -3600, false)], [] of Time::Location::ZoneTransition\n        Time::Location.local = location\n\n        Time.local(2014, 10, 30, 21, 18, 13).to_s.should eq(\"2014-10-30 21:18:13 +01:00\")\n      ensure\n        Time::Location.local = old_local\n      end\n    end\n  end\n\n  it \"#inspect\" do\n    Time.utc(2014, 1, 2, 3, 4, 5).inspect.should eq \"2014-01-02 03:04:05Z\"\n    Time.utc(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789).inspect.should eq \"2014-01-02 03:04:05.123456789Z\"\n\n    with_zoneinfo do\n      location = Time::Location.load(\"Europe/Berlin\")\n      Time.local(2014, 1, 2, 3, 4, 5, location: location).inspect.should eq \"2014-01-02 03:04:05+01:00[Europe/Berlin]\"\n      Time.local(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789, location: location).inspect.should eq \"2014-01-02 03:04:05.123456789+01:00[Europe/Berlin]\"\n    end\n\n    location = Time::Location.fixed(3601)\n    Time.local(2014, 1, 2, 3, 4, 5, location: location).inspect.should eq \"2014-01-02 03:04:05+01:00:01\"\n    Time.local(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789, location: location).inspect.should eq \"2014-01-02 03:04:05.123456789+01:00:01\"\n  end\n\n  it \"at methods\" do\n    t1 = Time.utc 2014, 11, 25, 10, 11, 12, nanosecond: 13\n    t2 = Time.utc 2014, 6, 25, 10, 11, 12, nanosecond: 13\n\n    t1.at_beginning_of_year.should eq Time.utc(2014, 1, 1)\n\n    1.upto(3) do |i|\n      Time.utc(2014, i, 10).at_beginning_of_quarter.should eq Time.utc(2014, 1, 1)\n      Time.utc(2014, i, 10).at_end_of_quarter.should eq Time.utc(2014, 3, 31, 23, 59, 59, nanosecond: 999_999_999)\n    end\n    4.upto(6) do |i|\n      Time.utc(2014, i, 10).at_beginning_of_quarter.should eq Time.utc(2014, 4, 1)\n      Time.utc(2014, i, 10).at_end_of_quarter.should eq Time.utc(2014, 6, 30, 23, 59, 59, nanosecond: 999_999_999)\n    end\n    7.upto(9) do |i|\n      Time.utc(2014, i, 10).at_beginning_of_quarter.should eq Time.utc(2014, 7, 1)\n      Time.utc(2014, i, 10).at_end_of_quarter.should eq Time.utc(2014, 9, 30, 23, 59, 59, nanosecond: 999_999_999)\n    end\n    10.upto(12) do |i|\n      Time.utc(2014, i, 10).at_beginning_of_quarter.should eq Time.utc(2014, 10, 1)\n      Time.utc(2014, i, 10).at_end_of_quarter.should eq Time.utc(2014, 12, 31, 23, 59, 59, nanosecond: 999_999_999)\n    end\n\n    t1.at_beginning_of_quarter.should eq Time.utc(2014, 10, 1)\n    t1.at_beginning_of_month.should eq Time.utc(2014, 11, 1)\n\n    3.upto(9) do |i|\n      Time.utc(2014, 11, i).at_beginning_of_week.should eq Time.utc(2014, 11, 3)\n    end\n\n    sunday_day_of_week = Time::DayOfWeek::Sunday\n    Time.utc(2014, 11, 1).at_beginning_of_week(sunday_day_of_week).should eq Time.utc(2014, 10, 26)\n    2.upto(8) do |i|\n      Time.utc(2014, 11, i).at_beginning_of_week(sunday_day_of_week).should eq Time.utc(2014, 11, 2)\n    end\n    Time.utc(2014, 11, 9).at_beginning_of_week(sunday_day_of_week).should eq Time.utc(2014, 11, 9)\n\n    Time.utc(2014, 11, 1).at_beginning_of_week(:sunday).should eq Time.utc(2014, 10, 26)\n    2.upto(8) do |i|\n      Time.utc(2014, 11, i).at_beginning_of_week(:sunday).should eq Time.utc(2014, 11, 2)\n    end\n    Time.utc(2014, 11, 9).at_beginning_of_week(:sunday).should eq Time.utc(2014, 11, 9)\n\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Sunday).should eq Time.utc(2014, 11, 9)\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Monday).should eq Time.utc(2014, 11, 10)\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Tuesday).should eq Time.utc(2014, 11, 4)\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Wednesday).should eq Time.utc(2014, 11, 5)\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Thursday).should eq Time.utc(2014, 11, 6)\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Friday).should eq Time.utc(2014, 11, 7)\n    Time.utc(2014, 11, 10).at_beginning_of_week(Time::DayOfWeek::Saturday).should eq Time.utc(2014, 11, 8)\n\n    at_beginning_of_week_default = Time.local.at_beginning_of_week\n    at_beginning_of_week_default.hour.should eq 0\n    at_beginning_of_week_default.minute.should eq 0\n    at_beginning_of_week_default.second.should eq 0\n\n    at_beginning_of_week_sunday = Time.local.at_beginning_of_week(:sunday)\n    at_beginning_of_week_sunday.hour.should eq 0\n    at_beginning_of_week_sunday.minute.should eq 0\n    at_beginning_of_week_sunday.second.should eq 0\n\n    with_zoneinfo do\n      # Observes time zone (https://github.com/crystal-lang/crystal/issues/16112)\n      is = Time::Location.load(\"Asia/Jerusalem\")\n      Time.local(2025, 3, 27, 23, 34, location: is).at_end_of_week\n        .should eq Time.local(2025, 3, 30, 23, 59, 59, nanosecond: 999_999_999, location: is)\n\n      ny = Time::Location.load(\"America/New_York\")\n      Time.local(2024, 11, 3, 23, 34, 8, location: ny).at_beginning_of_week\n        .should eq Time.local(2024, 10, 28, 0, location: ny)\n    end\n\n    t1.at_beginning_of_day.should eq Time.utc(2014, 11, 25)\n    t1.at_beginning_of_hour.should eq Time.utc(2014, 11, 25, 10)\n    t1.at_beginning_of_minute.should eq Time.utc(2014, 11, 25, 10, 11)\n    t1.at_beginning_of_second.should eq Time.utc(2014, 11, 25, 10, 11, 12)\n\n    t1.at_end_of_year.should eq Time.utc(2014, 12, 31, 23, 59, 59, nanosecond: 999_999_999)\n\n    t1.at_end_of_quarter.should eq Time.utc(2014, 12, 31, 23, 59, 59, nanosecond: 999_999_999)\n    t2.at_end_of_quarter.should eq Time.utc(2014, 6, 30, 23, 59, 59, nanosecond: 999_999_999)\n\n    t1.at_end_of_month.should eq Time.utc(2014, 11, 30, 23, 59, 59, nanosecond: 999_999_999)\n    t1.at_end_of_week.should eq Time.utc(2014, 11, 30, 23, 59, 59, nanosecond: 999_999_999)\n\n    Time.utc(2014, 11, 2).at_end_of_week.should eq Time.utc(2014, 11, 2, 23, 59, 59, nanosecond: 999_999_999)\n    3.upto(9) do |i|\n      Time.utc(2014, 11, i).at_end_of_week.should eq Time.utc(2014, 11, 9, 23, 59, 59, nanosecond: 999_999_999)\n    end\n\n    t1.at_end_of_day.should eq Time.utc(2014, 11, 25, 23, 59, 59, nanosecond: 999_999_999)\n    t1.at_end_of_hour.should eq Time.utc(2014, 11, 25, 10, 59, 59, nanosecond: 999_999_999)\n    t1.at_end_of_minute.should eq Time.utc(2014, 11, 25, 10, 11, 59, nanosecond: 999_999_999)\n    t1.at_end_of_second.should eq Time.utc(2014, 11, 25, 10, 11, 12, nanosecond: 999_999_999)\n\n    t1.at_midday.should eq Time.utc(2014, 11, 25, 12)\n\n    t1.at_beginning_of_semester.should eq Time.utc(2014, 7, 1)\n    t2.at_beginning_of_semester.should eq Time.utc(2014, 1, 1)\n\n    t1.at_end_of_semester.should eq Time.utc(2014, 12, 31, 23, 59, 59, nanosecond: 999_999_999)\n    t2.at_end_of_semester.should eq Time.utc(2014, 6, 30, 23, 59, 59, nanosecond: 999_999_999)\n  end\n\n  it \"does diff of utc vs local time\" do\n    local = Time.local(Time::Location.fixed(1234))\n    utc = local.to_utc\n    (utc - local).should eq(0.seconds)\n    (local - utc).should eq(0.seconds)\n  end\n\n  describe \"#in\" do\n    it \"changes location\" do\n      location = Time::Location.fixed(3600)\n      location2 = Time::Location.fixed(12345)\n      time1 = Time.local(location)\n      time1.location.should eq(location)\n\n      time2 = time1.in(location2)\n      time2.should eq(time1)\n      time2.location.should eq(location2)\n    end\n  end\n\n  describe \"#to_local_in\" do\n    it \"keeps wall clock\" do\n      location = Time::Location.fixed(3600)\n      location2 = Time::Location.fixed(12345)\n      time1 = Time.local(location)\n      time1.location.should eq(location)\n\n      time2 = time1.to_local_in(location2)\n      time2.location.should eq(location2)\n      time2.year.should eq time1.year\n      time2.month.should eq time1.month\n      time2.day.should eq time1.day\n      time2.hour.should eq time1.hour\n      time2.minute.should eq time1.minute\n      time2.second.should eq time1.second\n      time2.nanosecond.should eq time1.nanosecond\n    end\n\n    it \"is the difference of offsets apart\" do\n      location = Time::Location.fixed(3600)\n      location2 = Time::Location.fixed(12345)\n      time1 = Time.local(location)\n      time2 = time1.to_local_in(location2)\n\n      (time2 - time1).should eq (time1.offset - time2.offset).seconds\n    end\n  end\n\n  it \"#to_s\" do\n    with_zoneinfo do\n      time = Time.local(2017, 11, 25, 22, 6, 17, location: Time::Location::UTC)\n      time.to_s.should eq \"2017-11-25 22:06:17 UTC\"\n\n      time = Time.local(2017, 11, 25, 22, 6, 17, location: Time::Location.fixed(-7200))\n      time.to_s.should eq \"2017-11-25 22:06:17 -02:00\"\n\n      time = Time.local(2017, 11, 25, 22, 6, 17, location: Time::Location.fixed(-7259))\n      time.to_s.should eq \"2017-11-25 22:06:17 -02:00:59\"\n\n      location = Time::Location.load(\"Europe/Berlin\")\n      time = Time.local(2017, 11, 25, 22, 6, 17, location: location)\n      time.to_s.should eq \"2017-11-25 22:06:17 +01:00\"\n    end\n  end\n\n  describe \".days_in_month\" do\n    it \"returns days for valid month and year\" do\n      Time.days_in_month(2016, 2).should eq(29)\n      Time.days_in_month(1990, 4).should eq(30)\n    end\n\n    it \"raises exception for invalid month\" do\n      expect_raises(ArgumentError, \"Invalid month\") do\n        Time.days_in_month(2016, 13)\n      end\n    end\n\n    it \"raises exception for invalid year\" do\n      expect_raises(ArgumentError, \"Invalid year\") do\n        Time.days_in_month(10000, 11)\n      end\n    end\n  end\n\n  it \".days_in_year\" do\n    Time.days_in_year(2005).should eq(365)\n    Time.days_in_year(2004).should eq(366)\n    Time.days_in_year(2000).should eq(366)\n    Time.days_in_year(1990).should eq(365)\n  end\n\n  describe \".leap_year?\" do\n    it \"knows that 400-year centuries are leap years\" do\n      {1600, 2000, 2400}.each do |year|\n        Time.leap_year?(year).should be_true\n      end\n    end\n\n    it \"knows that 100-year centuries are normal years\" do\n      {1700, 1800, 1900, 2100, 2200, 2300}.each do |year|\n        Time.leap_year?(year).should be_false\n      end\n    end\n\n    it \"knows that typical non-century leap years are divisible by 4\" do\n      {1968, 1972, 2004, 2020}.each do |year|\n        Time.leap_year?(year).should be_true\n      end\n    end\n\n    it \"knows years *not* divisible by 4 are normal\" do\n      {1965, 1999, 2001, 2018, 2019, 2021, 2099, 2101}.each do |year|\n        Time.leap_year?(year).should be_false\n      end\n    end\n  end\n\n  describe Time::DayOfWeek do\n    it \"#value\" do\n      Time::DayOfWeek::Monday.value.should eq 1\n      Time::DayOfWeek::Tuesday.value.should eq 2\n      Time::DayOfWeek::Wednesday.value.should eq 3\n      Time::DayOfWeek::Thursday.value.should eq 4\n      Time::DayOfWeek::Friday.value.should eq 5\n      Time::DayOfWeek::Saturday.value.should eq 6\n      Time::DayOfWeek::Sunday.value.should eq 7\n    end\n\n    it \".from_value\" do\n      Time::DayOfWeek.from_value(1).should eq Time::DayOfWeek::Monday\n      Time::DayOfWeek.from_value(2).should eq Time::DayOfWeek::Tuesday\n      Time::DayOfWeek.from_value(3).should eq Time::DayOfWeek::Wednesday\n      Time::DayOfWeek.from_value(4).should eq Time::DayOfWeek::Thursday\n      Time::DayOfWeek.from_value(5).should eq Time::DayOfWeek::Friday\n      Time::DayOfWeek.from_value(6).should eq Time::DayOfWeek::Saturday\n      Time::DayOfWeek.from_value(7).should eq Time::DayOfWeek::Sunday\n\n      # Special case: Identify 0 as Sunday\n      Time::DayOfWeek.from_value(0).should eq Time::DayOfWeek::Sunday\n\n      expect_raises(Exception, \"Unknown enum Time::DayOfWeek value: 8\") do\n        Time::DayOfWeek.from_value(8)\n      end\n    end\n\n    it \".new does not identify 0 as Sunday\" do\n      Time::DayOfWeek.new(0).should_not eq Time::DayOfWeek::Sunday\n    end\n  end\n\n  describe \".week_date\" do\n    describe \"verify test data\" do\n      with_zoneinfo do\n        location = Time::Location.load(\"Europe/Berlin\")\n\n        CALENDAR_WEEK_TEST_DATA.each do |date, week_date|\n          it \"W#{week_date.join('-')} eq #{date.join('-')}\" do\n            Time.week_date(*week_date, location: Time::Location::UTC).should eq(Time.utc(*date))\n            Time.week_date(week_date[0], week_date[1], Time::DayOfWeek.from_value(week_date[2]), location: Time::Location::UTC).should eq(Time.utc(*date))\n            Time.week_date(*week_date).should eq(Time.local(*date))\n            Time.week_date(*week_date, location: location).should eq(Time.local(*date, location: location))\n          end\n        end\n      end\n    end\n\n    it \"accepts time arguments\" do\n      with_zoneinfo do\n        location = Time::Location.load(\"Europe/Berlin\")\n        Time.week_date(*CALENDAR_WEEK_TEST_DATA[0][1], 11, 57, 32, nanosecond: 123_567, location: location).should eq(\n          Time.local(*CALENDAR_WEEK_TEST_DATA[0][0], 11, 57, 32, nanosecond: 123_567, location: location))\n\n        location = Time::Location.load(\"America/Argentina/Buenos_Aires\")\n        Time.week_date(*CALENDAR_WEEK_TEST_DATA[0][1], 11, 57, 32, nanosecond: 123_567, location: location).should eq(\n          Time.local(*CALENDAR_WEEK_TEST_DATA[0][0], 11, 57, 32, nanosecond: 123_567, location: location))\n      end\n    end\n  end\n\n  describe \".month_week_date\" do\n    it \"works with DayOfWeek\" do\n      Time.month_week_date(2025, 4, 1, Time::DayOfWeek::Tuesday, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 1))\n      Time.month_week_date(2025, 4, 1, Time::DayOfWeek::Sunday, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 6))\n      Time.month_week_date(2025, 4, 1, Time::DayOfWeek::Monday, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 7))\n      Time.month_week_date(2025, 4, 2, Time::DayOfWeek::Wednesday, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 9))\n      Time.month_week_date(2025, 4, 5, Time::DayOfWeek::Thursday, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 24))\n      Time.month_week_date(2025, 2, 5, Time::DayOfWeek::Saturday, location: Time::Location::UTC).should eq(Time.utc(2025, 2, 22))\n    end\n\n    it \"works with integer day of week\" do\n      Time.month_week_date(2025, 4, 1, 2, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 1))\n      Time.month_week_date(2025, 4, 1, 7, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 6))\n      Time.month_week_date(2025, 4, 1, 0, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 6))\n      Time.month_week_date(2025, 4, 1, 1, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 7))\n      Time.month_week_date(2025, 4, 2, 3, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 9))\n      Time.month_week_date(2025, 4, 5, 4, location: Time::Location::UTC).should eq(Time.utc(2025, 4, 24))\n      Time.month_week_date(2025, 2, 5, 6, location: Time::Location::UTC).should eq(Time.utc(2025, 2, 22))\n    end\n\n    it \"accepts time arguments\" do\n      with_zoneinfo do\n        location = Time::Location.load(\"Europe/Berlin\")\n        Time.month_week_date(2025, 3, 3, 7, 11, 57, 32, nanosecond: 123_567, location: location).should eq(\n          Time.local(2025, 3, 16, 11, 57, 32, nanosecond: 123_567, location: location))\n\n        location = Time::Location.load(\"America/Argentina/Buenos_Aires\")\n        Time.month_week_date(2025, 3, 3, 7, 11, 57, 32, nanosecond: 123_567, location: location).should eq(\n          Time.local(2025, 3, 16, 11, 57, 32, nanosecond: 123_567, location: location))\n      end\n    end\n\n    it \"raises on invalid week or day of week\" do\n      expect_raises(Exception) { Time.month_week_date(2025, 1, 0, Time::DayOfWeek::Monday) }\n      expect_raises(Exception) { Time.month_week_date(2025, 1, 6, Time::DayOfWeek::Monday) }\n      expect_raises(Exception) { Time.month_week_date(2025, 1, 1, -1) }\n      expect_raises(Exception) { Time.month_week_date(2025, 1, 1, 8) }\n    end\n  end\n\n  typeof(Time.local.year)\n  typeof(1.minute.from_now.year)\n  typeof(1.minute.ago.year)\n  typeof(1.month.from_now.year)\n  typeof(1.month.ago.year)\n  typeof(Time.local.to_utc)\n  typeof(Time.local.to_local)\nend\n"
  },
  {
    "path": "spec/std/tuple_spec.cr",
    "content": "require \"spec\"\nrequire \"spec/helpers/iterate\"\n\nprivate class TupleSpecObj\n  getter x : Int32\n\n  def initialize(@x)\n  end\n\n  def clone\n    TupleSpecObj.new(@x)\n  end\n\n  def_equals @x\nend\n\ndescribe \"Tuple\" do\n  it \"does size\" do\n    {1, 2, 1, 2}.size.should eq(4)\n  end\n\n  it \"checks empty?\" do\n    Tuple.new.empty?.should be_true\n    {1}.empty?.should be_false\n  end\n\n  describe \"#[] with non-literal index\" do\n    it \"gets tuple element\" do\n      a = {1, 2.5}\n      i = 0\n      a[i].should eq(1)\n      i = 1\n      a[i].should eq(2.5)\n      i = -1\n      a[i].should eq(2.5)\n      i = -2\n      a[i].should eq(1)\n      typeof(a[i]).should eq(Int32 | Float64)\n    end\n\n    it \"raises index out of bounds\" do\n      a = {1, 2.5}\n      i = 2\n      expect_raises(IndexError) { a[i] }\n      i = -3\n      expect_raises(IndexError) { a[i] }\n    end\n  end\n\n  describe \"#[]? with non-literal index\" do\n    it \"gets tuple element or nil\" do\n      a = {1, 2.5}\n      i = 0\n      a[i]?.should eq(1)\n      i = -1\n      a[i]?.should eq(2.5)\n      i = 2\n      a[i]?.should be_nil\n      i = -3\n      a[i]?.should be_nil\n      typeof(a[i]?).should eq(Int32 | Float64 | Nil)\n    end\n  end\n\n  describe \".[] with non-literal index\" do\n    it \"gets tuple metaclass element\" do\n      a = Tuple(Int32, Float64)\n      i = 0\n      a[i].should eq(Int32)\n      i = 1\n      a[i].should eq(Float64)\n      i = -1\n      a[i].should eq(Float64)\n      i = -2\n      a[i].should eq(Int32)\n    end\n\n    it \"raises index out of bounds\" do\n      a = Tuple(Int32, Float64)\n      i = 2\n      expect_raises(IndexError) { a[i] }\n      i = -3\n      expect_raises(IndexError) { a[i] }\n    end\n  end\n\n  describe \".[]? with non-literal index\" do\n    it \"gets tuple metaclass element or nil\" do\n      a = Tuple(Int32, Float64)\n      i = 0\n      a[i]?.should eq(Int32)\n      i = -1\n      a[i]?.should eq(Float64)\n      i = 2\n      a[i]?.should be_nil\n      i = -3\n      a[i]?.should be_nil\n      typeof(a[i]?).should eq(Union(Int32.class, Float64.class, Nil))\n    end\n  end\n\n  it \"does at\" do\n    a = {1, 2}\n    a.at(1).should eq(2)\n    a.at(-1).should eq(2)\n\n    expect_raises(IndexError) { a.at(2) }\n    expect_raises(IndexError) { a.at(-3) }\n\n    a.at(2) { 3 }.should eq(3)\n    a.at(-3) { 3 }.should eq(3)\n  end\n\n  describe \"values_at\" do\n    it \"returns the given indexes\" do\n      {\"a\", \"b\", \"c\", \"d\"}.values_at(1, 0, 2).should eq({\"b\", \"a\", \"c\"})\n    end\n\n    it \"raises when passed an invalid index\" do\n      expect_raises IndexError do\n        {\"a\"}.values_at(10)\n      end\n    end\n\n    it \"works with mixed types\" do\n      {1, \"a\", 1.0, false}.values_at(0, 1, 2, 3).should eq({1, \"a\", 1.0, false})\n    end\n  end\n\n  it \"does ==\" do\n    a = {1, 2}\n    b = {3, 4}\n    c = {1, 2, 3}\n    d = {1}\n    e = {1, 2}\n    a.should eq(a)\n    a.should eq(e)\n    a.should_not eq(b)\n    a.should_not eq(c)\n    a.should_not eq(d)\n  end\n\n  it \"does == with different types but same size\" do\n    {1, 2}.should eq({1.0, 2.0})\n  end\n\n  it \"does == with another type\" do\n    {1, 2}.should_not eq(1)\n  end\n\n  it \"does compare\" do\n    a = {1, 2}\n    b = {3, 4}\n    c = {1, 6}\n    d = {3, 5}\n    e = {0, 8}\n    [a, b, c, d, e].sort.should eq([e, a, c, b, d])\n    [a, b, c, d, e].min.should eq(e)\n  end\n\n  it \"does compare with different sizes\" do\n    a = {2}\n    b = {1, 2, 3}\n    c = {1, 2}\n    d = {1, 1}\n    e = {1, 1, 3}\n    [a, b, c, d, e].sort.should eq([d, e, c, b, a])\n    [a, b, c, d, e].min.should eq(d)\n  end\n\n  describe \"#to_s\" do\n    it \"returns string representation\" do\n      {1, 2, 3}.to_s.should eq(\"{1, 2, 3}\")\n    end\n\n    context \"when the first element starts with '{'\" do\n      it \"inserts a space after '{' and before '}' when the first element is a Hash, preventing macro interpolation ({{ ... }})\" do\n        tuple = { {1 => 2} }\n        tuple.to_s.should eq(\"{ {1 => 2} }\")\n      end\n\n      it \"inserts a space after '{' and before '}' when the first element is a Tuple, preventing macro interpolation ({{ ... }})\" do\n        tuple = { {1, 2, 3} }\n        tuple.to_s.should eq(\"{ {1, 2, 3} }\")\n      end\n\n      it \"inserts a space after '{' and before '}' when the first element is a NamedTuple, preventing macro interpolation ({{ ... }})\" do\n        tuple = { {a: 1} }\n        tuple.to_s.should eq(\"{ {a: 1} }\")\n      end\n    end\n  end\n\n  it \"does each\" do\n    a = 0\n    {1, 2, 3}.each do |i|\n      a += i\n    end.should be_nil\n    a.should eq(6)\n  end\n\n  it \"does dup\" do\n    r1, r2 = TupleSpecObj.new(10), TupleSpecObj.new(20)\n    t = {r1, r2}\n    u = t.dup\n    u.size.should eq(2)\n    u[0].should be(r1)\n    u[1].should be(r2)\n  end\n\n  it \"does clone\" do\n    r1, r2 = TupleSpecObj.new(10), TupleSpecObj.new(20)\n    t = {r1, r2}\n    u = t.clone\n    u.size.should eq(2)\n    u[0].x.should eq(r1.x)\n    u[0].should_not be(r1)\n    u[1].x.should eq(r2.x)\n    u[1].should_not be(r2)\n  end\n\n  it \"does Tuple.new, without type vars\" do\n    Tuple.new(1, 2, 3).should eq({1, 2, 3})\n    Tuple.new([1, 2, 3]).should eq({[1, 2, 3]})\n    Tuple.new(TupleSpecObj.new(10)).should eq({TupleSpecObj.new(10)})\n  end\n\n  it \"does Tuple.new, with type vars\" do\n    Tuple(Int32, String).new(1, \"a\").should eq({1, \"a\"})\n    Tuple(TupleSpecObj).new(TupleSpecObj.new(10)).should eq({TupleSpecObj.new(10)})\n    typeof(Tuple.new).new.should eq(Tuple.new)\n\n    t = Tuple(Int32 | String, Int32 | String).new(1, \"a\")\n    t.should eq({1, \"a\"})\n    t.class.should_not eq(Tuple(Int32, String))\n  end\n\n  it \"does Tuple.from\" do\n    t = Tuple(Int32, Float64).from([1_i32, 2.0_f64])\n    t.should eq({1_i32, 2.0_f64})\n    t.class.should eq(Tuple(Int32, Float64))\n\n    expect_raises ArgumentError do\n      Tuple(Int32).from([1, 2])\n    end\n\n    expect_raises(TypeCastError, /[Cc]ast from String to Int32 failed/) do\n      Tuple(Int32, String).from([\"foo\", 1])\n    end\n  end\n\n  it \"does Tuple#from\" do\n    t = {Int32, Float64}.from([1_i32, 2.0_f64])\n    t.should eq({1_i32, 2.0_f64})\n    t.class.should eq(Tuple(Int32, Float64))\n\n    expect_raises ArgumentError do\n      {Int32}.from([1, 2])\n    end\n\n    expect_raises(TypeCastError, /[Cc]ast from String to Int32 failed/) do\n      {Int32, String}.from([\"foo\", 1])\n    end\n  end\n\n  it \"clones empty tuple\" do\n    Tuple.new.clone.should eq(Tuple.new)\n  end\n\n  it_iterates \"#each\", [1, 2, 3], {1, 2, 3}.each\n\n  it \"does map\" do\n    tuple = {1, 2.5, \"a\"}\n    tuple2 = tuple.map &.to_s\n    tuple2.is_a?(Tuple).should be_true\n    tuple2.should eq({\"1\", \"2.5\", \"a\"})\n  end\n\n  it \"does map_with_index\" do\n    tuple = {1, 1, 2, 2}\n    tuple2 = tuple.map_with_index { |e, i| e + i }\n    tuple2.should eq({1, 2, 4, 5})\n  end\n\n  it \"does map_with_index, with offset\" do\n    tuple = {1, 1, 2, 2}\n    tuple2 = tuple.map_with_index(10) { |e, i| e + i }\n    tuple2.should eq({11, 12, 14, 15})\n  end\n\n  it \"does reverse\" do\n    {1, 2.5, \"a\", 'c'}.reverse.should eq({'c', \"a\", 2.5, 1})\n  end\n\n  it_iterates \"#reverse_each\", [3, 2, 1], {1, 2, 3}.reverse_each\n\n  it \"gets first element\" do\n    tuple = {1, 2.5}\n    tuple.first.should eq(1)\n    typeof(tuple.first).should eq(Int32)\n  end\n\n  it \"gets first? element\" do\n    tuple = {1, 2.5}\n    tuple.first?.should eq(1)\n\n    Tuple.new.first?.should be_nil\n  end\n\n  it \"gets last element\" do\n    tuple = {1, 2.5, \"a\"}\n    tuple.last.should eq(\"a\")\n    typeof(tuple.last).should eq(String)\n  end\n\n  it \"gets last? element\" do\n    tuple = {1, 2.5, \"a\"}\n    tuple.last?.should eq(\"a\")\n\n    Tuple.new.last?.should be_nil\n  end\n\n  it \"does comparison\" do\n    tuple1 = {\"a\", \"a\", \"c\"}\n    tuple2 = {\"a\", \"b\", \"c\"}\n    (tuple1 <=> tuple2).should eq(-1)\n    (tuple2 <=> tuple1).should eq(1)\n  end\n\n  it \"does <=> for equality\" do\n    tuple1 = {0, 1}\n    tuple2 = {0.0, 1}\n    (tuple1 <=> tuple2).should eq(0)\n  end\n\n  it \"does <=> with the same beginning and different size\" do\n    tuple1 = {1, 2, 3}\n    tuple2 = {1, 2}\n    (tuple1 <=> tuple2).should eq(1)\n  end\n\n  it \"does types\" do\n    tuple = {1, 'a', \"hello\"}\n    tuple.class.types.to_s.should eq(\"{Int32, Char, String}\")\n  end\n\n  it \"does ===\" do\n    ({1, 2} === {1, 2}).should be_true\n    ({1, 2} === {1, 3}).should be_false\n    ({1, 2, 3} === {1, 2}).should be_false\n    ({/o+/, \"bar\"} === {\"fox\", \"bar\"}).should be_true\n    ({1, 2} === nil).should be_false\n  end\n\n  describe \"#to_a\" do\n    describe \"without block\" do\n      it \"basic\" do\n        ary = {1, 'a', true}.to_a\n        ary.should eq([1, 'a', true])\n        ary.size.should eq(3)\n      end\n\n      it \"empty\" do\n        ary = Tuple.new.to_a\n        ary.size.should eq(0)\n      end\n    end\n\n    describe \"with block\" do\n      it \"basic\" do\n        {-1, -2, -3}.to_a(&.abs).should eq [1, 2, 3]\n      end\n\n      it \"different type\" do\n        {1, 2, true}.to_a(&.to_s).should eq [\"1\", \"2\", \"true\"]\n      end\n    end\n  end\n\n  # Tuple#to_static_array don't compile on aarch64-darwin and\n  # aarch64-linux-musl due to a codegen error caused by LLVM < 13.0.0.\n  # See https://github.com/crystal-lang/crystal/issues/11358 for details.\n  {% unless compare_versions(Crystal::LLVM_VERSION, \"13.0.0\") < 0 && flag?(:aarch64) && (flag?(:musl) || flag?(:darwin) || flag?(:android)) %}\n    it \"#to_static_array\" do\n      ary = {1, 'a', true}.to_static_array\n      ary.should be_a(StaticArray(Int32 | Char | Bool, 3))\n      ary.should eq(StaticArray[1, 'a', true])\n      ary.size.should eq(3)\n\n      ary = Tuple.new.to_static_array\n      ary.should be_a(StaticArray(NoReturn, 0))\n      ary.size.should eq(0)\n\n      ary = Tuple(String | Int32).new(1).to_static_array\n      ary.should be_a(StaticArray(String | Int32, 1))\n      ary.should eq StaticArray[1.as(String | Int32)]\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/uint_spec.cr",
    "content": "require \"spec\"\n\ndescribe \"UInt\" do\n  it \"compares with <=>\" do\n    (1_u32 <=> 0_u32).should eq(1)\n    (0_u32 <=> 0_u32).should eq(0)\n    (0_u32 <=> 1_u32).should eq(-1)\n  end\n\n  describe \"&-\" do\n    it \"returns the wrapped negation\" do\n      x = &-0_u32\n      x.should eq(0_u32)\n      x.should be_a(UInt32)\n\n      x = &-100_u8\n      x.should eq(156_u8)\n      x.should be_a(UInt8)\n\n      x = &-1_u8\n      x.should eq(255_u8)\n      x.should be_a(UInt8)\n\n      x = &-255_u8\n      x.should eq(1_u8)\n      x.should be_a(UInt8)\n\n      x = &-1_u16\n      x.should eq(65535_u16)\n      x.should be_a(UInt16)\n\n      x = &-65535_u16\n      x.should eq(1_u16)\n      x.should be_a(UInt16)\n\n      x = &-1_u32\n      x.should eq(4294967295_u32)\n      x.should be_a(UInt32)\n\n      x = &-4294967295_u32\n      x.should eq(1_u32)\n      x.should be_a(UInt32)\n\n      x = &-1_u64\n      x.should eq(18446744073709551615_u64)\n      x.should be_a(UInt64)\n\n      x = &-18446744073709551615_u64\n      x.should eq(1_u64)\n      x.should be_a(UInt64)\n\n      x = &-1_u128\n      x.should eq(UInt128::MAX) # TODO: Change to literal once supported\n      x.should be_a(UInt128)\n\n      x = &-(UInt128::MAX) # TODO: Change to literal once supported\n      x.should eq(1_u128)\n      x.should be_a(UInt128)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uri/json_spec.cr",
    "content": "require \"spec\"\nrequire \"uri/json\"\n\ndescribe \"URI\" do\n  describe \"serializes\" do\n    it \"#to_json\" do\n      URI.parse(\"https://example.com\").to_json.should eq %q(\"https://example.com\")\n    end\n\n    it \"from_json_object_key?\" do\n      URI.from_json_object_key?(\"https://example.com\").should eq(URI.parse(\"https://example.com\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uri/params/from_www_form_spec.cr",
    "content": "require \"spec\"\nrequire \"uri/params/serializable\"\n\nprivate enum Color\n  Red\n  Green\n  Blue\nend\n\ndescribe \".from_www_form\" do\n  it Array do\n    Array(Int32).from_www_form(URI::Params.new({\"values\" => [\"1\", \"2\"]}), \"values\").should eq [1, 2]\n    Array(Int32).from_www_form(URI::Params.new({\"values[]\" => [\"1\", \"2\"]}), \"values\").should eq [1, 2]\n    Array(String).from_www_form(URI::Params.new({\"values\" => [\"\", \"\"]}), \"values\").should eq [\"\", \"\"]\n  end\n\n  describe Bool do\n    it \"a truthy value\" do\n      Bool.from_www_form(\"true\").should be_true\n      Bool.from_www_form(\"on\").should be_true\n      Bool.from_www_form(\"yes\").should be_true\n      Bool.from_www_form(\"1\").should be_true\n    end\n\n    it \"a falsey value\" do\n      Bool.from_www_form(\"false\").should be_false\n      Bool.from_www_form(\"off\").should be_false\n      Bool.from_www_form(\"no\").should be_false\n      Bool.from_www_form(\"0\").should be_false\n    end\n\n    it \"any other value\" do\n      Bool.from_www_form(\"foo\").should be_nil\n      Bool.from_www_form(\"\").should be_nil\n    end\n  end\n\n  describe String do\n    it \"scalar string\" do\n      String.from_www_form(\"John Doe\").should eq \"John Doe\"\n    end\n\n    it \"with key\" do\n      String.from_www_form(URI::Params.new({\"name\" => [\"John Doe\"]}), \"name\").should eq \"John Doe\"\n    end\n\n    it \"with missing key\" do\n      String.from_www_form(URI::Params.new({\"\" => [\"John Doe\"]}), \"name\").should be_nil\n    end\n\n    it \"with alternate casing\" do\n      String.from_www_form(URI::Params.new({\"Name\" => [\"John Doe\"]}), \"name\").should be_nil\n    end\n\n    it \"empty value\" do\n      String.from_www_form(URI::Params.new({\"name\" => [\"\"]}), \"name\").should eq \"\"\n    end\n  end\n\n  describe Enum do\n    it \"valid value\" do\n      Color.from_www_form(\"green\").should eq Color::Green\n    end\n\n    it \"invalid value\" do\n      expect_raises ArgumentError do\n        Color.from_www_form \"\"\n      end\n    end\n  end\n\n  describe Time do\n    it \"valid value\" do\n      Time.from_www_form(\"2016-11-16T09:55:48-03:00\").to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))\n      Time.from_www_form(\"2016-11-16T09:55:48-0300\").to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))\n      Time.from_www_form(\"20161116T095548-03:00\").to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))\n    end\n\n    it \"invalid value\" do\n      expect_raises Time::Format::Error do\n        Time.from_www_form \"\"\n      end\n    end\n  end\n\n  describe Nil do\n    it \"valid values\" do\n      Nil.from_www_form(\"\").should be_nil\n    end\n\n    it \"invalid value\" do\n      expect_raises ArgumentError do\n        Nil.from_www_form \"null\"\n      end\n    end\n  end\n\n  describe Number do\n    describe Int do\n      it \"valid numbers\" do\n        Int64.from_www_form(\"123\").should eq 123_i64\n        UInt8.from_www_form(\"7\").should eq 7_u8\n        Int64.from_www_form(\"-12\").should eq -12_i64\n      end\n\n      it \"with whitespace\" do\n        expect_raises ArgumentError do\n          Int32.from_www_form(\" 123\")\n        end\n      end\n\n      it \"empty value\" do\n        expect_raises ArgumentError do\n          Int16.from_www_form \"\"\n        end\n      end\n    end\n\n    describe Float do\n      it \"valid numbers\" do\n        Float32.from_www_form(\"123.0\").should eq 123_f32\n        Float64.from_www_form(\"123.0\").should eq 123_f64\n      end\n\n      it \"with whitespace\" do\n        expect_raises ArgumentError do\n          Float64.from_www_form(\" 123.0\")\n        end\n      end\n\n      it \"empty value\" do\n        expect_raises Exception do\n          Float64.from_www_form \"\"\n        end\n      end\n    end\n  end\n\n  describe Union do\n    it \"valid\" do\n      String?.from_www_form(URI::Params.parse(\"name=John Doe\"), \"name\").should eq \"John Doe\"\n      String?.from_www_form(URI::Params.parse(\"name=\"), \"name\").should eq \"\"\n    end\n\n    it \"invalid\" do\n      expect_raises ArgumentError do\n        (Int32 | Float64).from_www_form(URI::Params.parse(\"name=John Doe\"), \"name\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uri/params/serializable_spec.cr",
    "content": "require \"spec\"\nrequire \"uri/params/serializable\"\n\nprivate record SimpleType, page : Int32, strict : Bool, per_page : UInt8 do\n  include URI::Params::Serializable\nend\n\nprivate record SimpleTypeDefaults, page : Int32, strict : Bool, per_page : Int32 = 10 do\n  include URI::Params::Serializable\nend\n\nprivate record SimpleTypeNilable, page : Int32, strict : Bool, per_page : Int32? = nil do\n  include URI::Params::Serializable\nend\n\nprivate record SimpleTypeNilableDefault, page : Int32, strict : Bool, per_page : Int32? = 20 do\n  include URI::Params::Serializable\nend\n\nrecord Filter, status : String?, total : Float64? do\n  include URI::Params::Serializable\nend\n\nrecord Search, filter : Filter?, limit : Int32 = 25, offset : Int32 = 0 do\n  include URI::Params::Serializable\nend\n\nrecord GrandChild, name : String do\n  include URI::Params::Serializable\nend\n\nrecord Child, status : String?, grand_child : GrandChild do\n  include URI::Params::Serializable\nend\n\nrecord Parent, child : Child do\n  include URI::Params::Serializable\nend\n\nmodule MyConverter\n  def self.from_www_form(params : URI::Params, name : String)\n    params[name].to_i * 10\n  end\nend\n\nprivate record ConverterType, value : Int32 do\n  include URI::Params::Serializable\n\n  @[URI::Params::Field(converter: MyConverter)]\n  @value : Int32\nend\n\nprivate record GenericConverterType(T), value : Int32 do\n  include URI::Params::Serializable\n\n  @[URI::Params::Field(converter: T)]\n  @value : Int32\nend\n\nclass ParentType\n  include URI::Params::Serializable\n\n  getter name : String\nend\n\nclass ChildType < ParentType\nend\n\nprivate record SimpleTypeInitializeOpts, value : Int32 do\n  include URI::Params::Serializable\n\n  def initialize(**opts)\n    @value = opts.size\n  end\nend\n\ndescribe URI::Params::Serializable do\n  describe \".from_www_form\" do\n    it \"simple type\" do\n      SimpleType.from_www_form(\"page=10&strict=true&per_page=5\").should eq SimpleType.new(10, true, 5)\n    end\n\n    it \"missing required property\" do\n      expect_raises URI::SerializableError, \"Missing required property: 'page'.\" do\n        SimpleType.from_www_form(\"strict=true&per_page=5\")\n      end\n    end\n\n    it \"with default values\" do\n      SimpleTypeDefaults.from_www_form(\"page=10&strict=off\").should eq SimpleTypeDefaults.new(10, false, 10)\n    end\n\n    it \"with nilable values\" do\n      SimpleTypeNilable.from_www_form(\"page=10&strict=true\").should eq SimpleTypeNilable.new(10, true, nil)\n    end\n\n    it \"with nilable default\" do\n      SimpleTypeNilableDefault.from_www_form(\"page=10&strict=true\").should eq SimpleTypeNilableDefault.new(10, true, 20)\n    end\n\n    it \"with custom converter\" do\n      ConverterType.from_www_form(\"value=10\").should eq ConverterType.new(100)\n    end\n\n    it \"child type\" do\n      ChildType.from_www_form(\"name=Fred\").name.should eq \"Fred\"\n    end\n\n    describe \"nested type\" do\n      it \"happy path\" do\n        Search.from_www_form(\"offset=10&filter[status]=active&filter[total]=3.14\")\n          .should eq Search.new Filter.new(\"active\", 3.14), offset: 10\n      end\n\n      it \"missing nilable nested data\" do\n        Search.from_www_form(\"offset=10\")\n          .should eq Search.new Filter.new(nil, nil), offset: 10\n      end\n\n      it \"missing required nested property\" do\n        expect_raises URI::SerializableError, \"Missing required property: 'child[grand_child][name]'.\" do\n          Parent.from_www_form(\"child[status]=active\")\n        end\n      end\n\n      it \"doubly nested\" do\n        Parent.from_www_form(\"child[status]=active&child[grand_child][name]=Fred\")\n          .should eq Parent.new Child.new(\"active\", GrandChild.new(\"Fred\"))\n      end\n    end\n  end\n\n  describe \"#to_www_form\" do\n    it \"simple type\" do\n      SimpleType.new(10, true, 5).to_www_form.should eq \"page=10&strict=true&per_page=5\"\n    end\n\n    it \"nested type path\" do\n      Search.new(Filter.new(\"active\", 3.14), offset: 10).to_www_form\n        .should eq \"filter%5Bstatus%5D=active&filter%5Btotal%5D=3.14&limit=25&offset=10\"\n    end\n\n    it \"doubly nested\" do\n      Parent.new(Child.new(\"active\", GrandChild.new(\"Fred\"))).to_www_form\n        .should eq \"child%5Bstatus%5D=active&child%5Bgrand_child%5D%5Bname%5D=Fred\"\n    end\n  end\n\n  it \"works when type has constructor with double splat parameter (#16140)\" do\n    SimpleTypeInitializeOpts.from_www_form(\"value=123\").value.should eq(123)\n  end\n\n  it \"supports generic type variables in converters\" do\n    GenericConverterType(MyConverter).from_www_form(\"value=123\").value.should eq(1230)\n  end\nend\n"
  },
  {
    "path": "spec/std/uri/params/to_www_form_spec.cr",
    "content": "require \"spec\"\nrequire \"uri/params/serializable\"\n\nprivate enum Color\n  Red\n  Green\n  BlueGreen\nend\n\ndescribe \"#to_www_form\" do\n  it Number do\n    URI::Params.build do |builder|\n      12.to_www_form builder, \"value\"\n    end.should eq \"value=12\"\n  end\n\n  it Enum do\n    URI::Params.build do |builder|\n      Color::BlueGreen.to_www_form builder, \"value\"\n    end.should eq \"value=blue_green\"\n  end\n\n  it String do\n    URI::Params.build do |builder|\n      \"12\".to_www_form builder, \"value\"\n    end.should eq \"value=12\"\n  end\n\n  it Bool do\n    URI::Params.build do |builder|\n      false.to_www_form builder, \"value\"\n    end.should eq \"value=false\"\n  end\n\n  it Nil do\n    URI::Params.build do |builder|\n      nil.to_www_form builder, \"value\"\n    end.should eq \"value=\"\n  end\n\n  it Time do\n    URI::Params.build do |builder|\n      Time.utc(2024, 8, 6, 9, 48, 10).to_www_form builder, \"value\"\n    end.should eq \"value=2024-08-06T09%3A48%3A10Z\"\n  end\n\n  describe Array do\n    it \"of a single type\" do\n      URI::Params.build do |builder|\n        [1, 2, 3].to_www_form builder, \"value\"\n      end.should eq \"value=1&value=2&value=3\"\n    end\n\n    it \"of a union of types\" do\n      URI::Params.build do |builder|\n        [1, false, \"foo\"].to_www_form builder, \"value\"\n      end.should eq \"value=1&value=false&value=foo\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uri/params_spec.cr",
    "content": "require \"spec\"\nrequire \"uri/params\"\n\nclass URI\n  describe Params do\n    describe \".new\" do\n      it { Params.new.should eq(Params.parse(\"\")) }\n    end\n\n    describe \".parse\" do\n      {\n        {\"\", {} of String => Array(String)},\n        {\"&&\", {} of String => Array(String)},\n        {\"   \", {\"   \" => [\"\"]}},\n        {\"foo=bar\", {\"foo\" => [\"bar\"]}},\n        {\"foo=bar&foo=baz\", {\"foo\" => [\"bar\", \"baz\"]}},\n        {\"foo=bar&baz=qux\", {\"foo\" => [\"bar\"], \"baz\" => [\"qux\"]}},\n        {\"foo=bar;baz=qux\", {\"foo\" => [\"bar\"], \"baz\" => [\"qux\"]}},\n        {\"foo=hello%2Bworld\", {\"foo\" => [\"hello+world\"]}},\n        {\"foo=hello+world\", {\"foo\" => [\"hello world\"]}},\n        {\"foo=\", {\"foo\" => [\"\"]}},\n        {\"foo\", {\"foo\" => [\"\"]}},\n        {\"foo=&bar\", {\"foo\" => [\"\"], \"bar\" => [\"\"]}},\n        {\"bar&foo\", {\"bar\" => [\"\"], \"foo\" => [\"\"]}},\n        {\"foo=bar=qux\", {\"foo\" => [\"bar=qux\"]}},\n      }.each do |(from, to)|\n        it \"parses #{from}\" do\n          Params.parse(from).should eq(Params.new(to))\n        end\n      end\n    end\n\n    describe \".build\" do\n      {\n        {\"foo=bar\", {\"foo\" => [\"bar\"]}},\n        {\"foo=bar&foo=baz\", {\"foo\" => [\"bar\", \"baz\"]}},\n        {\"foo=bar&baz=qux\", {\"foo\" => [\"bar\"], \"baz\" => [\"qux\"]}},\n        {\"foo=hello%2Bworld\", {\"foo\" => [\"hello+world\"]}},\n        {\"foo=hello+world\", {\"foo\" => [\"hello world\"]}},\n        {\"foo=\", {\"foo\" => [\"\"]}},\n        {\"foo=&bar=\", {\"foo\" => [\"\"], \"bar\" => [\"\"]}},\n        {\"bar=&foo=\", {\"bar\" => [\"\"], \"foo\" => [\"\"]}},\n      }.each do |(to, from)|\n        it \"builds form from #{from}\" do\n          encoded = Params.build do |form|\n            from.each do |key, values|\n              values.each do |value|\n                form.add(key, value)\n              end\n            end\n          end\n\n          encoded.should eq(to)\n        end\n      end\n\n      it \"turns spaces to %20 if wanted\" do\n        encoded = Params.build(space_to_plus: false) do |form|\n          form.add(\"foo bar\", \"hello world\")\n        end\n\n        encoded.should eq(\"foo%20bar=hello%20world\")\n      end\n\n      it \"builds with IO\" do\n        io = IO::Memory.new\n        Params.build(io) do |form|\n          form.add(\"custom\", \"key\")\n        end\n        io.to_s.should eq(\"custom=key\")\n      end\n    end\n\n    describe \".encode\" do\n      it \"builds from hash\" do\n        encoded = Params.encode({\"foo\" => \"bar\", \"baz\" => [\"quux\", \"quuz\"]})\n        encoded.should eq(\"foo=bar&baz=quux&baz=quuz\")\n      end\n\n      it \"builds from hash with IO\" do\n        io = IO::Memory.new\n        Params.encode(io, {\"foo\" => \"bar\", \"baz\" => [\"quux\", \"quuz\"]})\n        io.to_s.should eq(\"foo=bar&baz=quux&baz=quuz\")\n      end\n\n      it \"builds from named tuple\" do\n        encoded = Params.encode({foo: \"bar\", baz: [\"quux\", \"quuz\"]})\n        encoded.should eq(\"foo=bar&baz=quux&baz=quuz\")\n      end\n\n      it \"builds from named tuple with IO\" do\n        io = IO::Memory.new\n        Params.encode(io, {foo: \"bar\", baz: [\"quux\", \"quuz\"]})\n        io.to_s.should eq(\"foo=bar&baz=quux&baz=quuz\")\n      end\n    end\n\n    describe \"#to_s\" do\n      it \"serializes params to http form\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.to_s.should eq(\"foo=bar&foo=baz&baz=qux\")\n      end\n\n      it \"turns spaces to + by default\" do\n        params = Params.parse(\"foo+bar=hello+world\")\n        params.to_s.should eq(\"foo+bar=hello+world\")\n      end\n\n      it \"turns spaces to %20 if space_to_plus is false\" do\n        params = Params.parse(\"foo+bar=hello+world\")\n        params.to_s(space_to_plus: false).should eq(\"foo%20bar=hello%20world\")\n      end\n    end\n\n    it \"#inspect\" do\n      URI::Params.new.inspect.should eq \"URI::Params{}\"\n\n      URI::Params{\"foo\" => [\"bar\", \"baz\"], \"baz\" => [\"qux\"]}.inspect.should eq %(URI::Params{\"foo\" => [\"bar\", \"baz\"], \"baz\" => [\"qux\"]})\n    end\n\n    describe \"#[](name)\" do\n      it \"returns first value for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params[\"foo\"].should eq(\"bar\")\n        params[\"baz\"].should eq(\"qux\")\n      end\n\n      it \"raises KeyError when there is no such param\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        expect_raises KeyError do\n          params[\"non_existent_param\"]\n        end\n      end\n    end\n\n    describe \"#[]?(name)\" do\n      it \"returns first value for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params[\"foo\"]?.should eq(\"bar\")\n        params[\"baz\"]?.should eq(\"qux\")\n      end\n\n      it \"return nil when there is no such param\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params[\"non_existent_param\"]?.should be_nil\n      end\n    end\n\n    describe \"#has_key?(name)\" do\n      it \"returns true if param with provided name exists\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.has_key?(\"foo\").should be_true\n        params.has_key?(\"baz\").should be_true\n      end\n\n      it \"return false if param with provided name does not exist\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.has_key?(\"non_existent_param\").should be_false\n      end\n    end\n\n    describe \"#[]=(name, value)\" do\n      it \"sets value for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params[\"foo\"] = \"notfoo\"\n        params.fetch_all(\"foo\").should eq([\"notfoo\"])\n      end\n\n      it \"adds new name => value pair if there is no such param\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params[\"non_existent_param\"] = \"test\"\n        params.fetch_all(\"non_existent_param\").should eq([\"test\"])\n      end\n\n      it \"sets value for provided param name (array)\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params[\"non_existent_param\"] = [\"test\", \"something\"]\n        params.fetch_all(\"non_existent_param\").should eq([\"test\", \"something\"])\n      end\n    end\n\n    describe \"#fetch(name, default)\" do\n      it \"returns first value for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.fetch(\"foo\", \"aDefault\").should eq(\"bar\")\n        params.fetch(\"baz\", \"aDefault\").should eq(\"qux\")\n      end\n\n      it \"return default value when there is no such param\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.fetch(\"non_existent_param\", \"aDefault\").should eq(\"aDefault\")\n      end\n    end\n\n    describe \"#fetch(name, &block)\" do\n      it \"returns first value for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.fetch(\"foo\") { \"fromBlock\" }.should eq(\"bar\")\n        params.fetch(\"baz\") { \"fromBlock\" }.should eq(\"qux\")\n      end\n\n      it \"return default value when there is no such param\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.fetch(\"non_existent_param\") { \"fromBlock\" }.should eq(\"fromBlock\")\n      end\n    end\n\n    describe \"#fetch_all(name)\" do\n      it \"fetches list of all values for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux&iamempty\")\n        params.fetch_all(\"foo\").should eq([\"bar\", \"baz\"])\n        params.fetch_all(\"baz\").should eq([\"qux\"])\n        params.fetch_all(\"iamempty\").should eq([\"\"])\n        params.fetch_all(\"non_existent_param\").should eq([] of String)\n      end\n    end\n\n    describe \"#add(name, value)\" do\n      it \"appends new value for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux&iamempty\")\n\n        params.add(\"foo\", \"zeit\")\n        params.fetch_all(\"foo\").should eq([\"bar\", \"baz\", \"zeit\"])\n\n        params.add(\"baz\", \"exit\")\n        params.fetch_all(\"baz\").should eq([\"qux\", \"exit\"])\n\n        params.add(\"iamempty\", \"not_empty_anymore\")\n        params.fetch_all(\"iamempty\").should eq([\"not_empty_anymore\"])\n\n        params.add(\"non_existent_param\", \"something\")\n        params.fetch_all(\"non_existent_param\").should eq([\"something\"])\n      end\n    end\n\n    describe \"#set_all(name, values)\" do\n      it \"sets values for provided param name\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n\n        params.set_all(\"baz\", [\"hello\", \"world\"])\n        params.fetch_all(\"baz\").should eq([\"hello\", \"world\"])\n\n        params.set_all(\"foo\", [\"something\"])\n        params.fetch_all(\"foo\").should eq([\"something\"])\n\n        params.set_all(\"non_existent_param\", [\"something\", \"else\"])\n        params.fetch_all(\"non_existent_param\").should eq([\"something\", \"else\"])\n      end\n    end\n\n    describe \"#dup\" do\n      it \"gives a whole new set of params\" do\n        ary = [\"bar\"]\n        params = Params{\"foo\" => ary}\n        duped = params.dup\n\n        ary << \"baz\"\n\n        duped.fetch_all(\"foo\").should eq [\"bar\"]\n        params.fetch_all(\"foo\").should eq [\"bar\", \"baz\"]\n      end\n    end\n\n    describe \"#clone\" do\n      it \"gives a whole new set of params\" do\n        ary = [\"bar\"]\n        params = Params{\"foo\" => ary}\n        duped = params.clone\n\n        ary << \"baz\"\n\n        duped.fetch_all(\"foo\").should eq [\"bar\"]\n        params.fetch_all(\"foo\").should eq [\"bar\", \"baz\"]\n      end\n    end\n\n    describe \"#each\" do\n      it \"calls provided proc for each name, value pair, including multiple values per one param name\" do\n        received = [] of {String, String}\n\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n        params.each do |name, value|\n          received << {name, value}\n        end\n\n        received.should eq([\n          {\"foo\", \"bar\"},\n          {\"foo\", \"baz\"},\n          {\"baz\", \"qux\"},\n        ])\n      end\n    end\n\n    describe \"#delete\" do\n      it \"deletes first value for provided param name and returns it\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n\n        params.delete(\"foo\").should eq(\"bar\")\n        params.fetch_all(\"foo\").should eq([\"baz\"])\n\n        params.delete(\"baz\").should eq(\"qux\")\n        expect_raises KeyError do\n          params[\"baz\"]\n        end\n      end\n    end\n\n    describe \"#delete_all\" do\n      it \"deletes all values for provided param name and returns them\" do\n        params = Params.parse(\"foo=bar&foo=baz&baz=qux\")\n\n        params.delete_all(\"foo\").should eq([\"bar\", \"baz\"])\n        expect_raises KeyError do\n          params[\"foo\"]\n        end\n      end\n    end\n\n    describe \"#merge!\" do\n      it \"modifies the receiver\" do\n        params = Params.parse(\"foo=bar&foo=baz&qux=zoo\")\n        other_params = Params.parse(\"foo=buzz&foo=extra\")\n\n        params.merge!(other_params, replace: false)\n\n        params.to_s.should eq(\"foo=bar&foo=baz&foo=buzz&foo=extra&qux=zoo\")\n      end\n\n      describe \"does not modify the other params\" do\n        it \"with replace: true\" do\n          params = Params.parse(\"foo=bar\")\n          other_params = Params.parse(\"foo=buzz&foo=extra\")\n\n          params.merge!(other_params, replace: true)\n          params.add(\"foo\", \"another\")\n\n          other_params.to_s.should eq(\"foo=buzz&foo=extra\")\n        end\n\n        it \"with replace: false\" do\n          params = Params.parse(\"foo=bar\")\n          other_params = Params.parse(\"foo=buzz&foo=extra\")\n\n          params.merge!(other_params, replace: false)\n          params.add(\"foo\", \"another\")\n\n          other_params.to_s.should eq(\"foo=buzz&foo=extra\")\n        end\n      end\n    end\n\n    describe \"#merge\" do\n      it \"replaces all values with the same key by default\" do\n        params = Params.parse(\"foo=bar&foo=baz&qux=zoo\")\n        other_params = Params.parse(\"foo=buzz&foo=extra\")\n\n        params.merge(other_params).to_s.should eq(\"foo=buzz&foo=extra&qux=zoo\")\n      end\n\n      it \"appends values with the same key with replace: false\" do\n        params = Params.parse(\"foo=bar&foo=baz&qux=zoo\")\n        other_params = Params.parse(\"foo=buzz&foo=extra\")\n\n        params.merge(other_params, replace: false).to_s.should eq(\"foo=bar&foo=baz&foo=buzz&foo=extra&qux=zoo\")\n      end\n\n      it \"does not modify the receiver\" do\n        params = Params.parse(\"foo=bar&foo=baz&qux=zoo\")\n        other_params = Params.parse(\"foo=buzz&foo=extra\")\n\n        params.merge(other_params)\n\n        params.to_s.should eq(\"foo=bar&foo=baz&qux=zoo\")\n      end\n    end\n\n    describe \"#empty?\" do\n      it \"test empty?\" do\n        Params.parse(\"foo=bar&foo=baz&baz=qux\").empty?.should be_false\n        Params.parse(\"\").empty?.should be_true\n        Params.new.empty?.should be_true\n      end\n    end\n\n    describe \"#==\" do\n      it \"compares other\" do\n        a = Params.parse(\"a=foo&b=bar\")\n        b = Params.parse(\"a=bar&b=foo\")\n        (a == a).should be_true\n        (b == b).should be_true\n        (a == b).should be_false\n      end\n\n      it \"compares other types\" do\n        a = Params.parse(\"a=foo&b=bar\")\n        b = \"other type\"\n        (a == b).should be_false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uri/punycode_spec.cr",
    "content": "require \"spec\"\nrequire \"uri/punycode\"\n\ndescribe URI::Punycode do\n  [\n    {\"3年B組金八先生\", \"3B-ww4c5e180e575a65lsy2b\"},\n    {\"安室奈美恵-with-SUPER-MONKEYS\", \"-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n\"},\n    {\"Hello-Another-Way-それぞれの場所\", \"Hello-Another-Way--fc4qua05auwb3674vfr0b\"},\n    {\"ひとつ屋根の下2\", \"2-u9tlzr9756bt3uc0v\"},\n    {\"MajiでKoiする5秒前\", \"MajiKoi5-783gue6qz075azm5e\"},\n    {\"パフィーdeルンバ\", \"de-jg4avhby1noc0d\"},\n    {\"そのスピードで\", \"d9juau41awczczp\"},\n    {\"Hello-Another-Way-それぞれ\", \"Hello-Another-Way--fc4qua97gba\"},\n  ].each do |example|\n    dec, enc = example\n\n    it \"encodes #{dec} to #{enc}\" do\n      URI::Punycode.encode(dec).should eq enc\n    end\n\n    it \"decodes #{enc} to #{dec}\" do\n      URI::Punycode.decode(enc).should eq dec\n    end\n  end\n\n  it \"translate to ascii only host name\" do\n    URI::Punycode.to_ascii(\"test.テスト.テスト\").should eq \"test.xn--zckzah.xn--zckzah\"\n  end\nend\n"
  },
  {
    "path": "spec/std/uri_spec.cr",
    "content": "require \"spec\"\nrequire \"uri\"\nrequire \"uri/json\"\nrequire \"uri/yaml\"\nrequire \"spec/helpers/string\"\n\nprivate def assert_uri(string, file = __FILE__, line = __LINE__, **args)\n  it \"`#{string}`\", file, line do\n    URI.parse(string).should eq URI.new(**args)\n    URI.parse(string).to_s.should eq string\n  end\nend\n\n# rearrange parameters for `assert_prints`\n{% for method in %w(encode encode_www_form decode decode_www_form) %}\n  private def uri_{{ method.id }}(string, **options)\n    URI.{{ method.id }}(string, **options)\n  end\n\n  private def uri_{{ method.id }}(io : IO, string, **options)\n    URI.{{ method.id }}(string, io, **options)\n  end\n\n  private def it_{{ method.gsub(/code/, \"codes\").id }}(string, expected_result, file = __FILE__, line = __LINE__, **options)\n    it \"{{ method[0...6].id }}s #{string.inspect}\", file: file, line: line do\n      assert_prints uri_{{ method.id }}(string, **options), expected_result, file: file, line: line\n    end\n  end\n{% end %}\n\n# This helper method is used in the specs for #relativize and also ensures the\n# reversibility of #relativize and #resolve.\nprivate def assert_relativize(base, uri, relative)\n  base = URI.parse(base)\n  relative = URI.parse(relative)\n  base.relativize(uri).should eq relative\n\n  # Reversibility is only guaranteed on normalized URIs\n  uri = URI.parse(uri).normalize\n  base.normalize!\n  relative.normalize!\n  base.relativize(base.resolve(relative)).should eq relative\n  base.resolve(base.relativize(uri)).should eq uri\nend\n\ndescribe \"URI\" do\n  describe \".parse\" do\n    # scheme\n    assert_uri(\"http:\", scheme: \"http\")\n    it { URI.parse(\"HttP:\").should eq(URI.new(scheme: \"http\")) }\n\n    # host\n    assert_uri(\"http://www.example.com\", scheme: \"http\", host: \"www.example.com\")\n    assert_uri(\"http://www.foo-bar.example.com\", scheme: \"http\", host: \"www.foo-bar.example.com\")\n    assert_uri(\"http://www.example.com:81\", scheme: \"http\", host: \"www.example.com\", port: 81)\n    assert_uri(\"http://[::1]:81\", scheme: \"http\", host: \"[::1]\", port: 81)\n    assert_uri(\"http://192.0.2.16:81\", scheme: \"http\", host: \"192.0.2.16\", port: 81)\n    it { URI.parse(\"http://[fe80::1%en0]:8080/\").should eq(URI.new(scheme: \"http\", host: \"[fe80::1%en0]\", port: 8080, path: \"/\")) }\n    assert_uri(\"http://[fe80::1%25en0]:8080/\", scheme: \"http\", host: \"[fe80::1%en0]\", port: 8080, path: \"/\")\n    assert_uri(\"mysql://a,b,c/bar\", scheme: \"mysql\", host: \"a,b,c\", path: \"/bar\")\n    assert_uri(\"scheme://!$&'()*+,;=hello!:12/path\", scheme: \"scheme\", host: \"!$&'()*+,;=hello!\", port: 12, path: \"/path\")\n    it { URI.parse(\"http://hello.世界.com\").should eq(URI.new(scheme: \"http\", host: \"hello.世界.com\")) }\n    assert_uri(\"tcp://[2020::2020:20:2020:2020%25Windows%20Loves%20Spaces]:2020\", scheme: \"tcp\", host: \"[2020::2020:20:2020:2020%Windows Loves Spaces]\", port: 2020)\n\n    # host with trailing slash\n    assert_uri(\"http://www.example.com/\", scheme: \"http\", host: \"www.example.com\", path: \"/\")\n    assert_uri(\"http://www.example.com:81/\", scheme: \"http\", host: \"www.example.com\", port: 81, path: \"/\")\n    assert_uri(\"http://[::1]:81/\", scheme: \"http\", host: \"[::1]\", port: 81, path: \"/\")\n    assert_uri(\"http://192.0.2.16:81/\", scheme: \"http\", host: \"192.0.2.16\", port: 81, path: \"/\")\n\n    # preserves fully-qualified host with trailing dot\n    assert_uri(\"https://example.com./\", scheme: \"https\", host: \"example.com.\", path: \"/\")\n    assert_uri(\"https://example.com.:8443/\", scheme: \"https\", host: \"example.com.\", port: 8443, path: \"/\")\n\n    # port\n    it { URI.parse(\"http://192.168.0.2:/foo\").should eq URI.new(scheme: \"http\", host: \"192.168.0.2\", path: \"/foo\") }\n\n    # path\n    assert_uri(\"http://www.example.com/foo\", scheme: \"http\", host: \"www.example.com\", path: \"/foo\")\n    assert_uri(\"http:.\", scheme: \"http\", path: \".\")\n    assert_uri(\"http:..\", scheme: \"http\", path: \"..\")\n    assert_uri(\"http://host/!$&'()*+,;=:@[hello]\", scheme: \"http\", host: \"host\", path: \"/!$&'()*+,;=:@[hello]\")\n    assert_uri(\"http://example.com//foo\", scheme: \"http\", host: \"example.com\", path: \"//foo\")\n    assert_uri(\"///foo\", host: \"\", path: \"/foo\")\n\n    # query\n    assert_uri(\"http://www.example.com/foo?q=1\", scheme: \"http\", host: \"www.example.com\", path: \"/foo\", query: \"q=1\")\n    assert_uri(\"http://www.example.com/foo?\", scheme: \"http\", host: \"www.example.com\", path: \"/foo\", query: \"\")\n    assert_uri(\"?q=1\", query: \"q=1\")\n    assert_uri(\"?q=1?\", query: \"q=1?\")\n    assert_uri(\"?a+b=c%2Bd\", query: \"a+b=c%2Bd\")\n    assert_uri(\"?query=http://example.com\", query: \"query=http://example.com\")\n\n    # userinfo\n    assert_uri(\"https://alice:pa55w0rd@www.example.com\", scheme: \"https\", host: \"www.example.com\", user: \"alice\", password: \"pa55w0rd\")\n    assert_uri(\"https://alice@www.example.com\", scheme: \"https\", host: \"www.example.com\", user: \"alice\", password: nil)\n    assert_uri(\"https://alice:@www.example.com\", scheme: \"https\", host: \"www.example.com\", user: \"alice\", password: \"\")\n    assert_uri(\"https://%3AD:%40_%40@www.example.com\", scheme: \"https\", host: \"www.example.com\", user: \":D\", password: \"@_@\")\n\n    pending \"unescaped @ in user/password should not confuse host\" do\n      assert_uri(\"http://j@ne:password@example.com\", scheme: \"http\", host: \"example.com\", user: \"j@ne\", password: \"password\")\n      assert_uri(\"http://jane:p@ssword@example.com\", scheme: \"http\", host: \"example.com\", user: \"jane\", password: \"p@ssword\")\n    end\n\n    # fragment\n    assert_uri(\"https://www.example.com/#top\", scheme: \"https\", host: \"www.example.com\", path: \"/\", fragment: \"top\")\n\n    # relative URL\n    assert_uri(\"/foo\", path: \"/foo\")\n    assert_uri(\"/foo?q=1\", path: \"/foo\", query: \"q=1\")\n    assert_uri(\"//foo\", host: \"foo\")\n    assert_uri(\"//user@foo/path?q=b\", host: \"foo\", user: \"user\", path: \"/path\", query: \"q=b\")\n\n    # various schemes\n    assert_uri(\"mailto:foo@example.org\", scheme: \"mailto\", path: \"foo@example.org\")\n    assert_uri(\"news:comp.infosystems.www.servers.unix\", scheme: \"news\", path: \"comp.infosystems.www.servers.unix\")\n    assert_uri(\"tel:+1-816-555-1212\", scheme: \"tel\", path: \"+1-816-555-1212\")\n    assert_uri(\"urn:oasis:names:specification:docbook:dtd:xml:4.1.2\", scheme: \"urn\", path: \"oasis:names:specification:docbook:dtd:xml:4.1.2\")\n    assert_uri(\"telnet://192.0.2.16:80/\", scheme: \"telnet\", host: \"192.0.2.16\", port: 80, path: \"/\")\n    assert_uri(\"ldap://[2001:db8::7]/c=GB?objectClass?one\", scheme: \"ldap\", host: \"[2001:db8::7]\", path: \"/c=GB\", query: \"objectClass?one\")\n    assert_uri(\"magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn\", scheme: \"magnet\", query: \"xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn\")\n\n    # opaque\n    assert_uri(\"http:example.com/?q=foo\", scheme: \"http\", path: \"example.com/\", query: \"q=foo\")\n\n    # no hierarchical part\n    assert_uri(\"http:\", scheme: \"http\")\n    assert_uri(\"http:?\", scheme: \"http\", query: \"\")\n    assert_uri(\"http:?#\", scheme: \"http\", query: \"\", fragment: \"\")\n    assert_uri(\"http:#\", scheme: \"http\", fragment: \"\")\n    assert_uri(\"http://\", scheme: \"http\", host: \"\")\n    assert_uri(\"http://?\", scheme: \"http\", host: \"\", query: \"\")\n    assert_uri(\"http://?#\", scheme: \"http\", host: \"\", query: \"\", fragment: \"\")\n    assert_uri(\"http://#\", scheme: \"http\", host: \"\", fragment: \"\")\n\n    # empty host, but port\n    assert_uri(\"http://:8000\", scheme: \"http\", host: \"\", port: 8000)\n    assert_uri(\"http://:8000/foo\", scheme: \"http\", host: \"\", port: 8000, path: \"/foo\")\n\n    # empty host, but user\n    assert_uri(\"http://user@\", scheme: \"http\", host: \"\", user: \"user\")\n    assert_uri(\"http://user@/foo\", scheme: \"http\", host: \"\", user: \"user\", path: \"/foo\")\n\n    # path with illegal characters\n    assert_uri(\"foo/another@url/[]and{}\", path: \"foo/another@url/[]and{}\")\n\n    # complex examples\n    assert_uri(\"http://user:pass@bitfission.com:8080/path?a=b#frag\",\n      scheme: \"http\", user: \"user\", password: \"pass\", host: \"bitfission.com\", port: 8080, path: \"/path\", query: \"a=b\", fragment: \"frag\")\n    assert_uri(\"//user:pass@bitfission.com:8080/path?a=b#frag\",\n      user: \"user\", password: \"pass\", host: \"bitfission.com\", port: 8080, path: \"/path\", query: \"a=b\", fragment: \"frag\")\n    assert_uri(\"/path?a=b#frag\", path: \"/path\", query: \"a=b\", fragment: \"frag\")\n    assert_uri(\"file://localhost/etc/fstab\", scheme: \"file\", host: \"localhost\", path: \"/etc/fstab\")\n    assert_uri(\"file:///etc/fstab\", scheme: \"file\", host: \"\", path: \"/etc/fstab\")\n    assert_uri(\"file:///C:/FooBar/Baz.txt\", scheme: \"file\", host: \"\", path: \"/C:/FooBar/Baz.txt\")\n    assert_uri(\"test:/test\", scheme: \"test\", path: \"/test\")\n\n    context \"bad urls\" do\n      it { expect_raises(URI::Error) { URI.parse(\"http://some.com:8f80/path\") } }\n    end\n  end\n\n  describe \".new\" do\n    it \"with query params\" do\n      URI.new(query: URI::Params.parse(\"foo=bar&foo=baz\")).should eq URI.parse(\"?foo=bar&foo=baz\")\n    end\n  end\n\n  describe \"#hostname\" do\n    it { URI.new(\"http\", \"www.example.com\", path: \"/foo\").hostname.should eq(\"www.example.com\") }\n    it { URI.new(\"http\", \"[::1]\", path: \"foo\").hostname.should eq(\"::1\") }\n    it { URI.new(path: \"/foo\").hostname.should be_nil }\n  end\n\n  describe \"#host\" do\n    it \"works with IPv6 address literals\" do\n      uri = URI.new(\"http\", \"[::1]\", path: \"foo\")\n      uri.hostname.should eq(\"::1\")\n      uri.host.should eq(\"[::1]\")\n      uri.to_s.should eq \"http://[::1]/foo\"\n      uri.host = \"[::1]\"\n      uri.to_s.should eq \"http://[::1]/foo\"\n\n      uri = URI.new(\"http\", \"::1\", path: \"foo\")\n      uri.hostname.should eq(\"::1\")\n      uri.host.should eq(\"[::1]\")\n      uri.to_s.should eq \"http://[::1]/foo\"\n      uri.host = \"::1\"\n      uri.to_s.should eq \"http://[::1]/foo\"\n    end\n\n    it \"works with IPv4 addresses\" do\n      uri = URI.new(\"http\", \"192.168.0.2\", path: \"foo\")\n      uri.hostname.should eq(\"192.168.0.2\")\n      uri.host.should eq(\"192.168.0.2\")\n      uri.to_s.should eq \"http://192.168.0.2/foo\"\n      uri.host = \"192.168.0.2\"\n      uri.to_s.should eq \"http://192.168.0.2/foo\"\n    end\n\n    it \"works with domain names\" do\n      uri = URI.new(\"http\", \"test.domain\", path: \"foo\")\n      uri.hostname.should eq(\"test.domain\")\n      uri.host.should eq(\"test.domain\")\n      uri.to_s.should eq \"http://test.domain/foo\"\n      uri.host = \"test.domain\"\n      uri.to_s.should eq \"http://test.domain/foo\"\n    end\n  end\n\n  describe \"#authority\" do\n    it { URI.new.authority.should be_nil }\n    it { URI.new(scheme: \"scheme\").authority.should be_nil }\n    it { URI.new(scheme: \"scheme\", host: \"example.com\").authority.should eq \"example.com\" }\n    it { URI.new(scheme: \"scheme\", host: \"example.com\", port: 123).authority.should eq \"example.com:123\" }\n    it { URI.new(scheme: \"scheme\", user: \"user\", host: \"example.com\").authority.should eq \"user@example.com\" }\n    it { URI.new(scheme: \"scheme\", user: \"user\").authority.should eq \"user@\" }\n    it { URI.new(scheme: \"scheme\", port: 123).authority.should eq \":123\" }\n    it { URI.new(scheme: \"scheme\", user: \"user\", port: 123).authority.should eq \"user@:123\" }\n    it { URI.new(scheme: \"scheme\", user: \"user\", password: \"pass\", host: \"example.com\").authority.should eq \"user:pass@example.com\" }\n    it { URI.new(scheme: \"scheme\", user: \"user\", password: \"pass\", host: \"example.com\", port: 123).authority.should eq \"user:pass@example.com:123\" }\n    it { URI.new(scheme: \"scheme\", password: \"pass\", host: \"example.com\").authority.should eq \"example.com\" }\n    it { URI.new(scheme: \"scheme\", path: \"opaque\").authority.should be_nil }\n    it { URI.new(scheme: \"scheme\", path: \"/path\").authority.should be_nil }\n  end\n\n  describe \"#request_target\" do\n    it { URI.new(path: \"/foo\").request_target.should eq(\"/foo\") }\n    it { URI.new.request_target.should eq(\"/\") }\n    it { URI.new(scheme: \"https\", host: \"example.com\").request_target.should eq(\"/\") }\n    it { URI.new(scheme: \"https\", host: \"example.com\", path: \"/%2F/%2F/\").request_target.should eq(\"/%2F/%2F/\") }\n    it { URI.new(scheme: \"scheme\", path: \"opaque\").request_target.should eq \"opaque\" }\n    it { URI.new(scheme: \"scheme\", query: \"foo=bar&foo=baz\").request_target.should eq \"?foo=bar&foo=baz\" }\n\n    it { URI.new(path: \"//foo\").request_target.should eq(\"//foo\") }\n    it { URI.new(path: \"/foo\", query: \"q=1\").request_target.should eq(\"/foo?q=1\") }\n    it { URI.new(path: \"/\", query: \"q=1\").request_target.should eq(\"/?q=1\") }\n    it { URI.new(query: \"q=1\").request_target.should eq(\"/?q=1\") }\n    it { URI.new(path: \"/a%3Ab\").request_target.should eq(\"/a%3Ab\") }\n    it { URI.new(\"scheme\").request_target.should eq \"\" }\n\n    it \"does not add '?' to the end if the query params are empty\" do\n      uri = URI.parse(\"http://www.example.com/foo\")\n      uri.query = \"\"\n      uri.request_target.should eq(\"/foo\")\n    end\n  end\n\n  describe \"#absolute?\" do\n    it { URI.parse(\"http://www.example.com/foo\").absolute?.should be_true }\n    it { URI.parse(\"http://www.example.com\").absolute?.should be_true }\n    it { URI.parse(\"http://127.0.0.1\").absolute?.should be_true }\n    it { URI.parse(\"http://[::1]/\").absolute?.should be_true }\n    it { URI.parse(\"/foo\").absolute?.should be_false }\n    it { URI.parse(\"foo\").absolute?.should be_false }\n  end\n\n  describe \"#relative?\" do\n    it { URI.parse(\"/foo\").relative?.should be_true }\n  end\n\n  describe \"#normalize\" do\n    it \"doesn't modify instance\" do\n      uri = URI.parse(\"HTTP://example.COM:80/./foo/../bar/\")\n      uri.normalize.should eq URI.parse(\"http://example.com/bar/\")\n      uri.should eq URI.parse(\"HTTP://example.COM:80/./foo/../bar/\")\n    end\n\n    it \"normalizes scheme\" do\n      URI.parse(\"HtTp://\").normalize.should eq URI.parse(\"http://\")\n    end\n\n    it \"normalizes host\" do\n      URI.parse(\"http://FoO.cOm/\").normalize.should eq URI.parse(\"http://foo.com/\")\n    end\n\n    it \"removes default port\" do\n      URI.new(\"http\", \"www.example.com\", 80).normalize.to_s.should eq(\"http://www.example.com\")\n      URI.new(\"https\", \"www.example.com\", 443).normalize.to_s.should eq(\"https://www.example.com\")\n      URI.new(\"ftp\", \"www.example.com\", 21).normalize.to_s.should eq(\"ftp://www.example.com\")\n      URI.new(\"sftp\", \"www.example.com\", 22).normalize.to_s.should eq(\"sftp://www.example.com\")\n      URI.new(\"ldap\", \"www.example.com\", 389).normalize.to_s.should eq(\"ldap://www.example.com\")\n      URI.new(\"ldaps\", \"www.example.com\", 636).normalize.to_s.should eq(\"ldaps://www.example.com\")\n    end\n\n    it \"removes dot notation from path\" do\n      URI.new(path: \"../bar\").normalize.path.should eq \"bar\"\n      URI.new(path: \"./bar\").normalize.path.should eq \"bar\"\n      URI.new(path: \".././bar\").normalize.path.should eq \"bar\"\n      URI.new(path: \"/foo/./bar\").normalize.path.should eq \"/foo/bar\"\n      URI.new(path: \"/bar/./\").normalize.path.should eq \"/bar/\"\n      URI.new(path: \"/.\").normalize.path.should eq \"/\"\n      URI.new(path: \"/bar/.\").normalize.path.should eq \"/bar/\"\n      URI.new(path: \"/foo/../bar\").normalize.path.should eq \"/bar\"\n      URI.new(path: \"/bar/../\").normalize.path.should eq \"/\"\n      URI.new(path: \"/..\").normalize.path.should eq \"/\"\n      URI.new(path: \"/bar/..\").normalize.path.should eq \"/\"\n      URI.new(path: \"/foo/bar/..\").normalize.path.should eq \"/foo/\"\n      URI.new(path: \".\").normalize.path.should eq \"\"\n      URI.new(path: \"..\").normalize.path.should eq \"\"\n    end\n\n    it \"prefixes relative path with colon with `./`\" do\n      URI.parse(\"./a:b\").normalize.should eq URI.parse(\"./a:b\")\n      URI.parse(\"http:a:b\").normalize.should eq URI.parse(\"http:./a:b\")\n    end\n  end\n\n  describe \"#normalize!\" do\n    it \"modifies the instance\" do\n      uri = URI.parse(\"HTTP://example.COM:80/./foo/../bar/\")\n      uri.normalize!\n      uri.should eq URI.parse(\"http://example.com/bar/\")\n    end\n  end\n\n  describe \"#opaque?\" do\n    it { URI.new.opaque?.should be_false }\n    it { URI.new(\"foo\").opaque?.should be_true }\n    it { URI.new(\"foo\", \"example.com\").opaque?.should be_false }\n    it { URI.new(\"foo\", \"\").opaque?.should be_false }\n    it { URI.new(\"foo\", path: \"foo\").opaque?.should be_true }\n    it { URI.new(\"foo\", path: \"/foo\").opaque?.should be_false }\n  end\n\n  describe \"#userinfo\" do\n    it { URI.parse(\"http://www.example.com\").userinfo.should be_nil }\n    it { URI.parse(\"http://foo@www.example.com\").userinfo.should eq(\"foo\") }\n    it { URI.parse(\"http://foo:bar@www.example.com\").userinfo.should eq(\"foo:bar\") }\n    it { URI.new(user: \"ä /\", password: \"ö :\").userinfo.should eq(\"%C3%A4+%2F:%C3%B6+%3A\") }\n  end\n\n  describe \"#to_s\" do\n    it { assert_prints URI.new(\"http\", \"www.example.com\").to_s, \"http://www.example.com\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", 80).to_s, \"http://www.example.com:80\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", user: \"alice\").to_s, \"http://alice@www.example.com\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", user: \"alice\", password: \"s3cr3t\").to_s, \"http://alice:s3cr3t@www.example.com\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", user: \":D\").to_s, \"http://%3AD@www.example.com\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", user: \":D\", password: \"@_@\").to_s, \"http://%3AD:%40_%40@www.example.com\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", user: \"@al:ce\", password: \"s/cr3t\").to_s, \"http://%40al%3Ace:s%2Fcr3t@www.example.com\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", fragment: \"top\").to_s, \"http://www.example.com#top\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", 80, \"/hello\").to_s, \"http://www.example.com:80/hello\" }\n    it { assert_prints URI.new(\"http\", \"www.example.com\", 80, \"/hello\", \"a=1\").to_s, \"http://www.example.com:80/hello?a=1\" }\n    it { assert_prints URI.new(\"mailto\", path: \"foo@example.com\").to_s, \"mailto:foo@example.com\" }\n    it { assert_prints URI.new(\"file\", path: \"/foo.html\").to_s, \"file:/foo.html\" }\n    it { assert_prints URI.new(\"file\", path: \"foo.html\").to_s, \"file:foo.html\" }\n    it { assert_prints URI.new(\"file\", host: \"host\", path: \"foo.html\").to_s, \"file://host/foo.html\" }\n    it { assert_prints URI.new(path: \"//foo\").to_s, \"/.//foo\" }\n    it { assert_prints URI.new(host: \"host\", path: \"//foo\").to_s, \"//host//foo\" }\n\n    it \"preserves non-default port\" do\n      assert_prints URI.new(\"http\", \"www.example.com\", 1234).to_s, \"http://www.example.com:1234\"\n      assert_prints URI.new(\"https\", \"www.example.com\", 1234).to_s, \"https://www.example.com:1234\"\n      assert_prints URI.new(\"ftp\", \"www.example.com\", 1234).to_s, \"ftp://www.example.com:1234\"\n      assert_prints URI.new(\"sftp\", \"www.example.com\", 1234).to_s, \"sftp://www.example.com:1234\"\n      assert_prints URI.new(\"ldap\", \"www.example.com\", 1234).to_s, \"ldap://www.example.com:1234\"\n      assert_prints URI.new(\"ldaps\", \"www.example.com\", 1234).to_s, \"ldaps://www.example.com:1234\"\n    end\n\n    it \"preserves port for unknown scheme\" do\n      assert_prints URI.new(\"xyz\", \"www.example.com\").to_s, \"xyz://www.example.com\"\n      assert_prints URI.new(\"xyz\", \"www.example.com\", 1234).to_s, \"xyz://www.example.com:1234\"\n    end\n\n    it \"preserves port for nil scheme\" do\n      assert_prints URI.new(nil, \"www.example.com\", 1234).to_s, \"//www.example.com:1234\"\n    end\n  end\n\n  describe \"#query_params\" do\n    context \"when there is no query parameters\" do\n      it \"returns an empty instance of URI::Params\" do\n        uri = URI.parse(\"http://foo.com\")\n        uri.query_params.should be_a(URI::Params)\n        uri.query_params.should eq(URI::Params.new)\n      end\n    end\n\n    it \"returns a URI::Params instance based on the query parameters\" do\n      expected_params = URI::Params{\"id\" => \"30\", \"limit\" => \"5\"}\n\n      uri = URI.parse(\"http://foo.com?id=30&limit=5#time=1305298413\")\n      uri.query_params.should eq(expected_params)\n\n      uri = URI.parse(\"?id=30&limit=5#time=1305298413\")\n      uri.query_params.should eq(expected_params)\n    end\n  end\n\n  describe \"#query_params=\" do\n    it \"empty\" do\n      uri = URI.new\n      params = URI::Params.new\n      uri.query_params = params\n      uri.query_params.should eq params\n      uri.query.should eq \"\"\n    end\n\n    it \"params with values\" do\n      uri = URI.new\n      params = URI::Params.parse(\"foo=bar&foo=baz\")\n      uri.query_params = params\n      uri.query_params.should eq params\n      uri.query.should eq \"foo=bar&foo=baz\"\n    end\n  end\n\n  describe \"#update_query_params\" do\n    it \"returns self\" do\n      expected_params = URI::Params{\"id\" => \"30\"}\n\n      uri = URI.parse(\"http://foo.com?id=30&limit=5#time=1305298413\")\n      uri.update_query_params { |params| params.delete(\"limit\") }.should be(uri)\n      uri.query_params.should eq(expected_params)\n    end\n\n    it \"commits changes to the URI::Object\" do\n      uri = URI.parse(\"http://foo.com?id=30&limit=5#time=1305298413\")\n      uri.update_query_params { |params| params.delete(\"limit\") }\n\n      uri.to_s.should eq(\"http://foo.com?id=30#time=1305298413\")\n    end\n  end\n\n  describe \"#==\" do\n    it { URI.parse(\"http://example.com\").should eq(URI.parse(\"http://example.com\")) }\n  end\n\n  describe \"#hash\" do\n    it { URI.parse(\"http://example.com\").hash.should eq(URI.parse(\"http://example.com\").hash) }\n  end\n\n  describe \".default_port\" do\n    it \"returns default port for well known schemes\" do\n      URI.default_port(\"http\").should eq(80)\n      URI.default_port(\"https\").should eq(443)\n    end\n\n    it \"returns nil for unknown schemes\" do\n      URI.default_port(\"xyz\").should be_nil\n    end\n\n    it \"treats scheme case insensitively\" do\n      URI.default_port(\"Http\").should eq(80)\n      URI.default_port(\"HTTP\").should eq(80)\n    end\n  end\n\n  describe \".set_default_port\" do\n    it \"registers port for scheme\" do\n      URI.set_default_port(\"ponzi\", 9999)\n      URI.default_port(\"ponzi\").should eq(9999)\n    ensure\n      URI.set_default_port(\"ponzi\", nil)\n    end\n\n    it \"unregisters port for scheme\" do\n      old_port = URI.default_port(\"ftp\")\n      begin\n        URI.set_default_port(\"ftp\", nil)\n        URI.default_port(\"ftp\").should be_nil\n      ensure\n        URI.set_default_port(\"ftp\", old_port)\n      end\n    end\n\n    it \"treats scheme case insensitively\" do\n      URI.set_default_port(\"UNKNOWN\", 1234)\n      URI.default_port(\"unknown\").should eq(1234)\n    ensure\n      URI.set_default_port(\"UNKNOWN\", nil)\n    end\n  end\n\n  describe \".decode\" do\n    it_decodes(\"hello\", \"hello\")\n    it_decodes(\"hello%20world\", \"hello world\")\n    it_decodes(\"hello+world\", \"hello+world\")\n    it_decodes(\"hello%\", \"hello%\")\n    it_decodes(\"hello%2\", \"hello%2\")\n    it_decodes(\"hello%2B\", \"hello+\")\n    it_decodes(\"hello%2Bworld\", \"hello+world\")\n    it_decodes(\"hello%2%2Bworld\", \"hello%2+world\")\n    it_decodes(\"%E3%81%AA%E3%81%AA\", \"なな\")\n    it_decodes(\"%e3%81%aa%e3%81%aa\", \"なな\")\n    it_decodes(\"%27Stop%21%27+said+Fred\", \"'Stop!'+said+Fred\")\n    it_decodes(\"hello+world\", \"hello world\", plus_to_space: true)\n    it_decodes(\"+%2B %20\", \"++  \")\n\n    it \"does not decode string when block returns true\" do\n      String.build do |io|\n        URI.decode(\"hello%26world\", io) { |byte| URI.reserved? byte }\n      end.should eq(\"hello%26world\")\n    end\n  end\n\n  it \".encode_path_segment\" do\n    assert_prints URI.encode_path_segment(\"hello\"), \"hello\"\n    assert_prints URI.encode_path_segment(\"hello world\"), \"hello%20world\"\n    assert_prints URI.encode_path_segment(\"hello%\"), \"hello%25\"\n    assert_prints URI.encode_path_segment(\"hello%2\"), \"hello%252\"\n    assert_prints URI.encode_path_segment(\"hello+\"), \"hello%2B\"\n    assert_prints URI.encode_path_segment(\"hello+world\"), \"hello%2Bworld\"\n    assert_prints URI.encode_path_segment(\"hello%2+world\"), \"hello%252%2Bworld\"\n    assert_prints URI.encode_path_segment(\"なな\"), \"%E3%81%AA%E3%81%AA\"\n    assert_prints URI.encode_path_segment(\" !\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~qй\"), \"%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~q%D0%B9\"\n    assert_prints URI.encode_path_segment(\"'Stop!' said Fred\"), \"%27Stop%21%27%20said%20Fred\"\n    assert_prints URI.encode_path_segment(\"\\n\"), \"%0A\"\n    assert_prints URI.encode_path_segment(\"https://en.wikipedia.org/wiki/Crystal (programming language)\"), \"https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FCrystal%20%28programming%20language%29\"\n    assert_prints URI.encode_path_segment(\"\\xFF\"), \"%FF\" # escapes invalid UTF-8 character\n    assert_prints URI.encode_path_segment(\"foo;bar;baz\"), \"foo%3Bbar%3Bbaz\"\n    assert_prints URI.encode_path_segment(\"foo/bar/baz\"), \"foo%2Fbar%2Fbaz\"\n    assert_prints URI.encode_path_segment(\"foo,bar,baz\"), \"foo%2Cbar%2Cbaz\"\n  end\n\n  it \".encode_path\" do\n    assert_prints URI.encode_path(\"hello\"), \"hello\"\n    assert_prints URI.encode_path(\"hello world\"), \"hello%20world\"\n    assert_prints URI.encode_path(\"hello%\"), \"hello%25\"\n    assert_prints URI.encode_path(\"hello%2\"), \"hello%252\"\n    assert_prints URI.encode_path(\"hello+\"), \"hello%2B\"\n    assert_prints URI.encode_path(\"hello+world\"), \"hello%2Bworld\"\n    assert_prints URI.encode_path(\"hello%2+world\"), \"hello%252%2Bworld\"\n    assert_prints URI.encode_path(\"なな\"), \"%E3%81%AA%E3%81%AA\"\n    assert_prints URI.encode_path(\" !\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~qй\"), \"%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-./%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~q%D0%B9\"\n    assert_prints URI.encode_path(\"'Stop!' said Fred\"), \"%27Stop%21%27%20said%20Fred\"\n    assert_prints URI.encode_path(\"\\n\"), \"%0A\"\n    assert_prints URI.encode_path(\"https://en.wikipedia.org/wiki/Crystal (programming language)\"), \"https%3A//en.wikipedia.org/wiki/Crystal%20%28programming%20language%29\"\n    assert_prints URI.encode_path(\"\\xFF\"), \"%FF\" # escapes invalid UTF-8 character\n    assert_prints URI.encode_path(\"foo/bar/baz\"), \"foo/bar/baz\"\n  end\n\n  describe \".encode\" do\n    it_encodes(\"hello\", \"hello\")\n    it_encodes(\"hello world\", \"hello%20world\")\n    it_encodes(\"hello%\", \"hello%25\")\n    it_encodes(\"hello%2\", \"hello%252\")\n    it_encodes(\"hello+\", \"hello+\")\n    it_encodes(\"hello+world\", \"hello+world\")\n    it_encodes(\"hello%2+world\", \"hello%252+world\")\n    it_encodes(\"なな\", \"%E3%81%AA%E3%81%AA\")\n    it_encodes(\"'Stop!' said Fred\", \"'Stop!'%20said%20Fred\")\n    it_encodes(\"\\n\", \"%0A\")\n    it_encodes(\"https://en.wikipedia.org/wiki/Crystal (programming language)\", \"https://en.wikipedia.org/wiki/Crystal%20(programming%20language)\")\n    it_encodes(\"\\xFF\", \"%FF\") # encodes invalid UTF-8 character\n    it_encodes(\"hello world\", \"hello+world\", space_to_plus: true)\n    it_encodes(\"'Stop!' said Fred\", \"'Stop!'+said+Fred\", space_to_plus: true)\n\n    it \"does not encode character when block returns true\" do\n      String.build do |io|\n        URI.decode(\"hello&world\", io) { |byte| URI.reserved? byte }\n      end.should eq(\"hello&world\")\n    end\n  end\n\n  describe \".encode_www_form\" do\n    it_encodes_www_form(\"\", \"\")\n    it_encodes_www_form(\"abc\", \"abc\")\n    it_encodes_www_form(\"1%41\", \"1%2541\")\n    it_encodes_www_form(\"a b+\", \"a+b%2B\")\n    it_encodes_www_form(\"a b+\", \"a%20b%2B\", space_to_plus: false)\n    it_encodes_www_form(\"10%\", \"10%25\")\n    it_encodes_www_form(\" ?&=#+%!<>#\\\"{}|\\\\^[]`☺\\t:/@$'()*,;\", \"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B\")\n    it_encodes_www_form(\"* foo=bar baz&hello/\", \"%2A+foo%3Dbar+baz%26hello%2F\")\n  end\n\n  describe \".decode_www_form\" do\n    it_decodes_www_form(\"\", \"\")\n    it_decodes_www_form(\"abc\", \"abc\")\n    it_decodes_www_form(\"1%41\", \"1A\")\n    it_decodes_www_form(\"1%41%42%43\", \"1ABC\")\n    it_decodes_www_form(\"%4a\", \"J\")\n    it_encodes_www_form(\"hello+\", \"hello%2B\")\n    it_encodes_www_form(\"hello+world\", \"hello%2Bworld\")\n    it_encodes_www_form(\"hello%2+world\", \"hello%252%2Bworld\")\n    it_encodes_www_form(\"'Stop!' said Fred\", \"%27Stop%21%27+said+Fred\")\n    it_decodes_www_form(\"a+b\", \"a b\")\n    it_decodes_www_form(\"a%20b\", \"a b\")\n    it_decodes_www_form(\"%20%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B\", \" ?&=#+%!<>#\\\"{}|\\\\^[]`☺\\t:/@$'()*,;\")\n    it_decodes_www_form(\"+%2B %20\", \" +  \")\n\n    it_decodes_www_form(\"%\", \"%\")\n    it_decodes_www_form(\"%1\", \"%1\")\n    it_decodes_www_form(\"123%45%6\", \"123E%6\")\n    it_decodes_www_form(\"%zzzzz\", \"%zzzzz\")\n  end\n\n  it \".reserved?\" do\n    reserved_chars = Set{':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\\'', '(', ')', '*', '+', ',', ';', '='}\n\n    ('\\u{00}'..'\\u{7F}').each do |char|\n      URI.reserved?(char.ord.to_u8).should eq(reserved_chars.includes?(char))\n    end\n  end\n\n  it \".unreserved?\" do\n    unreserved_chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['_', '.', '-', '~']\n\n    ('\\u{00}'..'\\u{7F}').each do |char|\n      URI.unreserved?(char.ord.to_u8).should eq(unreserved_chars.includes?(char))\n    end\n  end\n\n  describe \"#resolve\" do\n    it \"absolute URI references\" do\n      URI.parse(\"http://foo.com?a=b\").resolve(\"https://bar.com/\").should eq URI.parse(\"https://bar.com/\")\n      URI.parse(\"http://foo.com/\").resolve(\"https://bar.com/?a=b\").should eq URI.parse(\"https://bar.com/?a=b\")\n      URI.parse(\"http://foo.com/\").resolve(\"https://bar.com/?\").should eq URI.parse(\"https://bar.com/?\")\n      URI.parse(\"http://foo.com/bar\").resolve(\"mailto:urbi@orbi.va\").should eq URI.parse(\"mailto:urbi@orbi.va\")\n    end\n\n    it \"path-absolute URI references\" do\n      URI.parse(\"http://foo.com/bar\").resolve(\"/baz\").should eq URI.parse(\"http://foo.com/baz\")\n      URI.parse(\"http://foo.com/bar?a=b#f\").resolve(\"/baz\").should eq URI.parse(\"http://foo.com/baz\")\n      URI.parse(\"http://foo.com/bar?a=b\").resolve(\"/baz?\").should eq URI.parse(\"http://foo.com/baz?\")\n      URI.parse(\"http://foo.com/bar?a=b\").resolve(\"/baz?c=d\").should eq URI.parse(\"http://foo.com/baz?c=d\")\n    end\n\n    it \"multiple slashes\" do\n      URI.parse(\"http://foo.com/bar\").resolve(\"http://foo.com//baz\").should eq URI.parse(\"http://foo.com//baz\")\n      URI.parse(\"http://foo.com/bar\").resolve(\"http://foo.com///baz/quux\").should eq URI.parse(\"http://foo.com///baz/quux\")\n    end\n\n    it \"scheme-relative\" do\n      URI.parse(\"https://foo.com/bar?a=b\").resolve(\"//bar.com/quux\").should eq URI.parse(\"https://bar.com/quux\")\n    end\n\n    it \"path relative references\" do\n      # same depth\n      URI.parse(\"http://foo.com\").resolve(\".\").should eq URI.parse(\"http://foo.com/\")\n      URI.parse(\"http://foo.com/bar\").resolve(\".\").should eq URI.parse(\"http://foo.com/\")\n      URI.parse(\"http://foo.com/bar/\").resolve(\".\").should eq URI.parse(\"http://foo.com/bar/\")\n\n      # deeper\n      URI.parse(\"http://foo.com\").resolve(\"bar\").should eq URI.parse(\"http://foo.com/bar\")\n      URI.parse(\"http://foo.com/\").resolve(\"bar\").should eq URI.parse(\"http://foo.com/bar\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux\").should eq URI.parse(\"http://foo.com/bar/quux\")\n\n      # higher\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"../quux\").should eq URI.parse(\"http://foo.com/quux\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"../../../../../quux\").should eq URI.parse(\"http://foo.com/quux\")\n      URI.parse(\"http://foo.com/bar\").resolve(\"..\").should eq URI.parse(\"http://foo.com/\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"./..\").should eq URI.parse(\"http://foo.com/\")\n\n      # \"..\" in the middle\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/dotdot/../tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/../tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/.././tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/./../tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/dotdot/././../../tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/dotdot/./.././../tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/dotdot/dotdot/./../../.././././tail\").should eq URI.parse(\"http://foo.com/bar/quux/tail\")\n      URI.parse(\"http://foo.com/bar/baz\").resolve(\"quux/./dotdot/../dotdot/../dot/./tail/..\").should eq URI.parse(\"http://foo.com/bar/quux/dot/\")\n    end\n\n    it \"removes dot-segments\" do\n      # http://tools.ietf.org/html/rfc3986#section-5.2.4\n      URI.parse(\"http://foo.com/dot/./dotdot/../foo/bar\").resolve(\"../baz\").should eq URI.parse(\"http://foo.com/dot/baz\")\n    end\n\n    it \"...\" do\n      URI.parse(\"http://foo.com/bar\").resolve(\"...\").should eq URI.parse(\"http://foo.com/...\")\n    end\n\n    it \"fragment\" do\n      URI.parse(\"http://foo.com/bar\").resolve(\".#frag\").should eq URI.parse(\"http://foo.com/#frag\")\n      URI.parse(\"http://example.org/bar\").resolve(\"#!$&%27()*+,;=\").should eq URI.parse(\"http://example.org/bar#!$&%27()*+,;=\")\n    end\n\n    it \"encoded characters\" do\n      URI.parse(\"http://foo.com/foo%2fbar/\").resolve(\"../baz\").should eq URI.parse(\"http://foo.com/baz\")\n      URI.parse(\"http://foo.com/1/2%2f/3%2f4/5\").resolve(\"../../a/b/c\").should eq URI.parse(\"http://foo.com/1/a/b/c\")\n      URI.parse(\"http://foo.com/1/2/3\").resolve(\"./a%2f../../b/..%2fc\").should eq URI.parse(\"http://foo.com/1/2/b/..%2fc\")\n      URI.parse(\"http://foo.com/1/2%2f/3%2f4/5\").resolve(\"./a%2f../b/../c\").should eq URI.parse(\"http://foo.com/1/2%2f/3%2f4/a%2f../c\")\n      URI.parse(\"http://foo.com/foo%20bar/\").resolve(\"../baz\").should eq URI.parse(\"http://foo.com/baz\")\n      URI.parse(\"http://foo.com/foo\").resolve(\"../bar%2fbaz\").should eq URI.parse(\"http://foo.com/bar%2fbaz\")\n      URI.parse(\"http://foo.com/foo%2dbar/\").resolve(\"./baz-quux\").should eq URI.parse(\"http://foo.com/foo%2dbar/baz-quux\")\n    end\n\n    it \"RFC 3986: 5.4.1. Normal Examples\" do\n      # http://tools.ietf.org/html/rfc3986#section-5.4.1\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g:h\").should eq URI.parse(\"g:h\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g\").should eq URI.parse(\"http://a/b/c/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"./g\").should eq URI.parse(\"http://a/b/c/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g/\").should eq URI.parse(\"http://a/b/c/g/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"/g\").should eq URI.parse(\"http://a/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"//g\").should eq URI.parse(\"http://g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"?y\").should eq URI.parse(\"http://a/b/c/d;p?y\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g?y\").should eq URI.parse(\"http://a/b/c/g?y\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"#s\").should eq URI.parse(\"http://a/b/c/d;p?q#s\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g#s\").should eq URI.parse(\"http://a/b/c/g#s\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g?y#s\").should eq URI.parse(\"http://a/b/c/g?y#s\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\";x\").should eq URI.parse(\"http://a/b/c/;x\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g;x\").should eq URI.parse(\"http://a/b/c/g;x\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g;x?y#s\").should eq URI.parse(\"http://a/b/c/g;x?y#s\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"\").should eq URI.parse(\"http://a/b/c/d;p?q\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\".\").should eq URI.parse(\"http://a/b/c/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"./\").should eq URI.parse(\"http://a/b/c/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"..\").should eq URI.parse(\"http://a/b/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../\").should eq URI.parse(\"http://a/b/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../g\").should eq URI.parse(\"http://a/b/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../..\").should eq URI.parse(\"http://a/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../../\").should eq URI.parse(\"http://a/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../../g\").should eq URI.parse(\"http://a/g\")\n    end\n\n    it \"RFC 3986: 5.4.2. Abnormal Examples\" do\n      # http://tools.ietf.org/html/rfc3986#section-5.4.2\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../../../g\").should eq URI.parse(\"http://a/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"../../../../g\").should eq URI.parse(\"http://a/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"/./g\").should eq URI.parse(\"http://a/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"/../g\").should eq URI.parse(\"http://a/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g.\").should eq URI.parse(\"http://a/b/c/g.\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\".g\").should eq URI.parse(\"http://a/b/c/.g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g..\").should eq URI.parse(\"http://a/b/c/g..\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"..g\").should eq URI.parse(\"http://a/b/c/..g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"./../g\").should eq URI.parse(\"http://a/b/g\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"./g/.\").should eq URI.parse(\"http://a/b/c/g/\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g/./h\").should eq URI.parse(\"http://a/b/c/g/h\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g/../h\").should eq URI.parse(\"http://a/b/c/h\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g;x=1/./y\").should eq URI.parse(\"http://a/b/c/g;x=1/y\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g;x=1/../y\").should eq URI.parse(\"http://a/b/c/y\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g?y/./x\").should eq URI.parse(\"http://a/b/c/g?y/./x\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g?y/../x\").should eq URI.parse(\"http://a/b/c/g?y/../x\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g#s/./x\").should eq URI.parse(\"http://a/b/c/g#s/./x\")\n      URI.parse(\"http://a/b/c/d;p?q\").resolve(\"g#s/../x\").should eq URI.parse(\"http://a/b/c/g#s/../x\")\n    end\n\n    it \"Extras\" do\n      URI.parse(\"https://a/b/c/d;p?q\").resolve(\"//g?q\").should eq URI.parse(\"https://g?q\")\n      URI.parse(\"https://a/b/c/d;p?q\").resolve(\"//g#s\").should eq URI.parse(\"https://g#s\")\n      URI.parse(\"https://a/b/c/d;p?q\").resolve(\"//g/d/e/f?y#s\").should eq URI.parse(\"https://g/d/e/f?y#s\")\n      URI.parse(\"https://a/b/c/d;p#s\").resolve(\"?y\").should eq URI.parse(\"https://a/b/c/d;p?y\")\n      URI.parse(\"https://a/b/c/d;p?q#s\").resolve(\"?y\").should eq URI.parse(\"https://a/b/c/d;p?y\")\n    end\n\n    it \"relative base\" do\n      URI.parse(\"a/b/c\").resolve(\"bar/baz\").should eq URI.parse(\"a/b/bar/baz\")\n    end\n\n    it \"opaque URIs\" do\n      URI.parse(\"mailto:urbi@orbi.va\").resolve(\"bar/baz\").should eq URI.parse(\"bar/baz\")\n      URI.parse(\"bar/baz\").resolve(\"mailto:urbi@orbi.va\").should eq URI.parse(\"mailto:urbi@orbi.va\")\n    end\n  end\n\n  describe \"#relativize\" do\n    it \"absolute URI references\" do\n      assert_relativize(\"http://foo.com?a=b\", \"https://bar.com/\", \"https://bar.com/\")\n      assert_relativize(\"http://foo.com/\", \"https://bar.com/?a=b\", \"https://bar.com/?a=b\")\n      assert_relativize(\"http://foo.com/\", \"https://bar.com/?\", \"https://bar.com/?\")\n      assert_relativize(\"http://foo.com/bar\", \"mailto:urbi@orbi.va\", \"mailto:urbi@orbi.va\")\n    end\n\n    it \"path relative references\" do\n      # same depth\n      assert_relativize(\"http://foo.com\", \"http://foo.com/\", \"./\")\n      assert_relativize(\"http://foo.com/bar\", \"http://foo.com/\", \"./\")\n      assert_relativize(\"http://foo.com/bar\", \"http://foo.com/bar/\", \"bar/\")\n      assert_relativize(\"http://foo.com/bar\", \"http://foo.com/baz\", \"baz\")\n      assert_relativize(\"http://foo.com/bar?a=b#f\", \"http://foo.com/baz\", \"baz\")\n      assert_relativize(\"http://foo.com/bar?a=b\", \"http://foo.com/baz?\", \"baz?\")\n      assert_relativize(\"http://foo.com/bar?a=b\", \"http://foo.com/baz?c=d\", \"baz?c=d\")\n\n      # deeper\n      assert_relativize(\"http://foo.com\", \"http://foo.com/bar\", \"bar\")\n      assert_relativize(\"http://foo.com/\", \"http://foo.com/bar\", \"bar\")\n      assert_relativize(\"http://foo.com/bar/baz\", \"http://foo.com/bar/quux\", \"quux\")\n\n      # higher\n      assert_relativize(\"http://foo.com/bar/baz\", \"http://foo.com/quux\", \"../quux\")\n      assert_relativize(\"http://foo.com/bar/baz/\", \"http://foo.com/quux\", \"../../quux\")\n      assert_relativize(\"http://foo.com/bar\", \"http://foo.com/\", \"./\")\n      assert_relativize(\"http://foo.com/bar/baz\", \"http://foo.com/\", \"../\")\n      assert_relativize(\"http://foo.com/bar/\", \"http://foo.com/qux/\", \"../qux/\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/\", \"../\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/g\", \"../g\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/\", \"../../\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/g\", \"../../g\")\n    end\n\n    it \"identical\" do\n      assert_relativize(\"http://foo.com/a\", \"http://foo.com/a\", \"\")\n      assert_relativize(\"http://foo.com/a\", \"http://FOO.com/a\", \"\")\n    end\n\n    it \"ignore base path with dot-segments\" do\n      # These specs don't use assert_relativize because they explicitly not reversible as thy perform on non-normalized paths\n      URI.parse(\"http://foo.com/dot/./dotdot/../foo/bar\").relativize(\"http://foo.com/dot/baz\").should eq URI.parse(\"/dot/baz\")\n      URI.parse(\"http://foo.com/dot/./dotdot/../foo/bar\").relativize(\"dot/baz\").should eq URI.parse(\"dot/baz\")\n    end\n\n    it \"...\" do\n      assert_relativize(\"http://foo.com/bar\", \"http://foo.com/.../\", \".../\")\n    end\n\n    it \"fragment\" do\n      assert_relativize(\"http://foo.com/bar\", \"http://foo.com/#frag\", \"./#frag\")\n      assert_relativize(\"http://example.org/bar\", \"http://example.org/bar#!$&%27()*+,;=\", \"#!$&%27()*+,;=\")\n    end\n\n    it \"encoded characters\" do\n      assert_relativize(\"http://foo.com/foo%2fbar/\", \"http://foo.com/baz\", \"../baz\")\n      assert_relativize(\"http://foo.com/1/2%2f/3%2f4/5\", \"http://foo.com/1/a/b/c\", \"../../a/b/c\")\n      assert_relativize(\"http://foo.com/1/2/3\", \"http://foo.com/1/2/b/..%2fc\", \"b/..%2fc\")\n      assert_relativize(\"http://foo.com/1/2%2f/3%2f4/5\", \"http://foo.com/1/2%2f/3%2f4/a%2f../c\", \"a%2f../c\")\n      assert_relativize(\"http://foo.com/foo%20bar/\", \"http://foo.com/baz\", \"../baz\")\n      assert_relativize(\"http://foo.com/foo\", \"http://foo.com/bar%2fbaz\", \"bar%2fbaz\")\n      assert_relativize(\"http://foo.com/foo%2dbar/\", \"http://foo.com/foo%2dbar/baz-quux\", \"baz-quux\")\n    end\n\n    it \"RFC 3986: 5.4.1. Normal Examples\" do\n      # http://tools.ietf.org/html/rfc3986#section-5.4.1\n      assert_relativize(\"http://a/b/c/d;p?q\", \"g:h\", \"g:h\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g\", \"g\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g/\", \"g/\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/d;p?y\", \"?y\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g?y\", \"g?y\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/d;p?q#s\", \"#s\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g#s\", \"g#s\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g?y#s\", \"g?y#s\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/;x\", \";x\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g;x\", \"g;x\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g;x?y#s\", \"g;x?y#s\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/d;p?q\", \"\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/\", \"./\")\n    end\n\n    it \"RFC 3986: 5.4.2. Abnormal Examples\" do\n      # http://tools.ietf.org/html/rfc3986#section-5.4.2\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g.\", \"g.\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/.g\", \".g\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g..\", \"g..\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/..g\", \"..g\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g;x=1/y\", \"g;x=1/y\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g?y/./x\", \"g?y/./x\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g?y/../x\", \"g?y/../x\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g#s/./x\", \"g#s/./x\")\n      assert_relativize(\"http://a/b/c/d;p?q\", \"http://a/b/c/g#s/../x\", \"g#s/../x\")\n      assert_relativize(\"https://a/b/c/d;p#s\", \"https://a/b/c/d;p?y\", \"?y\")\n      assert_relativize(\"https://a/b/c/d;p?q#s\", \"https://a/b/c/d;p?y\", \"?y\")\n    end\n\n    it \"relative base\" do\n      assert_relativize(\"a/b/c\", \"a/b/bar/baz\", \"bar/baz\")\n      assert_relativize(\"foo/\", \"foo/a:b\", \"./a:b\")\n    end\n\n    it \"opaque base\" do\n      assert_relativize(\"mailto:urbi@orbi.va\", \"bar/baz\", \"bar/baz\")\n      assert_relativize(\"mailto:urbi@orbi.va\", \"mailto:urbi@orbi.va#bar\", \"mailto:urbi@orbi.va#bar\")\n      assert_relativize(\"mailto:urbi@orbi.va#bar\", \"mailto:urbi@orbi.va\", \"mailto:urbi@orbi.va\")\n    end\n  end\n\n  it \".unwrap_ipv6\" do\n    URI.unwrap_ipv6(\"[::1]\").should eq(\"::1\")\n    URI.unwrap_ipv6(\"127.0.0.1\").should eq(\"127.0.0.1\")\n    URI.unwrap_ipv6(\"example.com\").should eq(\"example.com\")\n    URI.unwrap_ipv6(\"[1234:5678::1]\").should eq \"1234:5678::1\"\n  end\n\n  it \".from_json\" do\n    URI.from_json(%(\"https://example.com\")).should eq URI.new(scheme: \"https\", host: \"example.com\")\n  end\n\n  it \"#to_json\" do\n    URI.new(scheme: \"https\", host: \"example.com\").to_json.should eq %(\"https://example.com\")\n  end\n\n  it \".from_yaml\" do\n    URI.from_yaml(%(\"https://example.com\")).should eq URI.new(scheme: \"https\", host: \"example.com\")\n  end\n\n  it \"#to_yaml\" do\n    URI.new(scheme: \"https\", host: \"example.com\").to_yaml.rchop(\"...\\n\").should eq %(--- https://example.com\\n)\n  end\nend\n"
  },
  {
    "path": "spec/std/uuid/json_spec.cr",
    "content": "require \"spec\"\nrequire \"uuid/json\"\n\ndescribe \"UUID\" do\n  describe \"serializes\" do\n    it \"#to_json\" do\n      UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\").to_json.should eq \"\\\"50a11da6-377b-4bdf-b9f0-076f9db61c93\\\"\"\n    end\n\n    it \"from_json_object_key?\" do\n      UUID.from_json_object_key?(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\").should eq UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uuid/yaml_spec.cr",
    "content": "require \"spec\"\nrequire \"uuid/yaml\"\n\nstruct TestUuidYaml\n  include YAML::Serializable\n  getter uuid : UUID\n\n  def initialize(@uuid); end\nend\n\ndescribe \"UUID\" do\n  describe \"serializes\" do\n    it \"#to_yaml\" do\n      obj = TestUuidYaml.new(UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\"))\n      obj.to_yaml.should match /uuid: 50a11da6-377b-4bdf-b9f0-076f9db61c93/\n    end\n\n    it \"#from_yaml\" do\n      obj = TestUuidYaml.from_yaml(\"uuid: 50a11da6-377b-4bdf-b9f0-076f9db61c93\")\n      obj.uuid.should eq UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/uuid_spec.cr",
    "content": "require \"spec\"\nrequire \"uuid\"\nrequire \"spec/helpers/string\"\nrequire \"../support/wasm32\"\n\ndescribe \"UUID\" do\n  describe \"#==\" do\n    it \"matches identical UUIDs\" do\n      UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\").should eq UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\")\n      UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\").hash.should eq UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\").hash\n    end\n  end\n\n  describe \"#<=>\" do\n    it \"correctly compares two UUIDs\" do\n      uuid_1 = UUID.new(\"00330000-0000-0000-0000-000000000000\")\n      uuid_2 = UUID.new(\"00000011-0000-0000-5500-000099000000\")\n      (uuid_1 <=> uuid_2).should be > 0\n      (uuid_2 <=> uuid_1).should be < 0\n      (uuid_1 <=> uuid_1).should eq 0\n    end\n  end\n\n  describe \"random initialize\" do\n    it \"works with no options\" do\n      subject = UUID.random\n      subject.variant.should eq UUID::Variant::RFC4122\n      subject.version.should eq UUID::Version::V4\n    end\n\n    it \"does inspect\" do\n      subject = UUID.random\n      subject.inspect.should eq \"UUID(#{subject})\"\n    end\n\n    it \"works with variant\" do\n      subject = UUID.random(variant: UUID::Variant::NCS)\n      subject.variant.should eq UUID::Variant::NCS\n      subject.version.should eq UUID::Version::V4\n    end\n\n    it \"works with version\" do\n      subject = UUID.random(version: UUID::Version::V3)\n      subject.variant.should eq UUID::Variant::RFC4122\n      subject.version.should eq UUID::Version::V3\n    end\n  end\n\n  describe \"initialize from static array\" do\n    it \"works with static array only\" do\n      subject = UUID.new(StaticArray(UInt8, 16).new(0_u8))\n      subject.to_s.should eq \"00000000-0000-0000-0000-000000000000\"\n    end\n\n    it \"works with static array and variant\" do\n      subject = UUID.new(StaticArray(UInt8, 16).new(0_u8), variant: UUID::Variant::RFC4122)\n      subject.to_s.should eq \"00000000-0000-0000-8000-000000000000\"\n      subject.variant.should eq UUID::Variant::RFC4122\n    end\n\n    it \"works with static array and version\" do\n      subject = UUID.new(StaticArray(UInt8, 16).new(0_u8), version: UUID::Version::V3)\n      subject.to_s.should eq \"00000000-0000-3000-0000-000000000000\"\n      subject.version.should eq UUID::Version::V3\n    end\n\n    it \"works with static array, variant and version\" do\n      subject = UUID.new(StaticArray(UInt8, 16).new(0_u8), variant: UUID::Variant::Microsoft, version: UUID::Version::V3)\n      subject.to_s.should eq \"00000000-0000-3000-c000-000000000000\"\n      subject.variant.should eq UUID::Variant::Microsoft\n      subject.version.should eq UUID::Version::V3\n    end\n  end\n\n  it \"initializes with slice\" do\n    subject = UUID.new(Slice(UInt8).new(16, 0_u8), variant: UUID::Variant::RFC4122, version: UUID::Version::V4)\n    subject.to_s.should eq \"00000000-0000-4000-8000-000000000000\"\n    subject.variant.should eq UUID::Variant::RFC4122\n    subject.version.should eq UUID::Version::V4\n  end\n\n  describe \"initialize with String\" do\n    it \"works with string only\" do\n      subject = UUID.new(\"00000000-0000-0000-0000-000000000000\")\n      subject.to_s.should eq \"00000000-0000-0000-0000-000000000000\"\n    end\n\n    it \"works with string and variant\" do\n      subject = UUID.new(\"00000000-0000-0000-0000-000000000000\", variant: UUID::Variant::Future)\n      subject.to_s.should eq \"00000000-0000-0000-e000-000000000000\"\n      subject.variant.should eq UUID::Variant::Future\n    end\n\n    it \"works with string and version\" do\n      subject = UUID.new(\"00000000-0000-0000-0000-000000000000\", version: UUID::Version::V5)\n      subject.to_s.should eq \"00000000-0000-5000-0000-000000000000\"\n      subject.version.should eq UUID::Version::V5\n    end\n\n    it \"can be built from strings\" do\n      UUID.new(\"c20335c3-7f46-4126-aae9-f665434ad12b\").to_s.should eq(\"c20335c3-7f46-4126-aae9-f665434ad12b\")\n      UUID.new(\"c20335c37f464126aae9f665434ad12b\").to_s.should eq(\"c20335c3-7f46-4126-aae9-f665434ad12b\")\n      UUID.new(\"C20335C3-7F46-4126-AAE9-F665434AD12B\").to_s.should eq(\"c20335c3-7f46-4126-aae9-f665434ad12b\")\n      UUID.new(\"C20335C37F464126AAE9F665434AD12B\").to_s.should eq(\"c20335c3-7f46-4126-aae9-f665434ad12b\")\n      UUID.new(\"urn:uuid:1ed1ee2f-ef9a-4f9c-9615-ab14d8ef2892\").to_s.should eq(\"1ed1ee2f-ef9a-4f9c-9615-ab14d8ef2892\")\n    end\n  end\n\n  describe \"parsing strings\" do\n    it \"returns a properly parsed UUID\" do\n      UUID.parse?(\"c20335c3-7f46-4126-aae9-f665434ad12b\").to_s.should eq(\"c20335c3-7f46-4126-aae9-f665434ad12b\")\n    end\n\n    it \"returns nil if it has the wrong number of characters\" do\n      UUID.parse?(\"nope\").should be_nil\n    end\n\n    it \"returns nil if it has incorrect characters\" do\n      UUID.parse?(\"c20335c3-7f46-4126-aae9-f665434ad12?\").should be_nil\n      UUID.parse?(\"lol!wut?-asdf-fork-typo-omglolwtfbbq\").should be_nil\n      UUID.parse?(\"lol!wut?asdfforktypoomglolwtfbbq\").should be_nil\n      UUID.parse?(\"urn:uuid:lol!wut?-asdf-fork-typo-omglolwtfbbq\").should be_nil\n    end\n  end\n\n  it \"initializes from UUID\" do\n    uuid = UUID.new(\"50a11da6-377b-4bdf-b9f0-076f9db61c93\")\n    uuid = UUID.new(uuid, version: UUID::Version::V2, variant: UUID::Variant::Microsoft)\n    uuid.version.should eq UUID::Version::V2\n    uuid.variant.should eq UUID::Variant::Microsoft\n    uuid.bytes.should eq(UInt8.static_array(80, 161, 29, 166, 55, 123, 43, 223, 217, 240, 7, 111, 157, 182, 28, 147))\n    uuid.to_s.should eq \"50a11da6-377b-2bdf-d9f0-076f9db61c93\"\n  end\n\n  it \"initializes zeroed UUID\" do\n    UUID.empty.should eq UUID.new(StaticArray(UInt8, 16).new(0_u8), UUID::Variant::NCS, UUID::Version::V4)\n    UUID.empty.to_s.should eq \"00000000-0000-4000-0000-000000000000\"\n    UUID.empty.variant.should eq UUID::Variant::NCS\n    UUID.empty.version.should eq UUID::Version::V4\n  end\n\n  describe \"supports different string formats\" do\n    it \"normal output\" do\n      assert_prints UUID.new(\"ee843b2656d8472bb3430b94ed9077ff\").to_s, \"ee843b26-56d8-472b-b343-0b94ed9077ff\"\n    end\n\n    it \"hexstring\" do\n      UUID.new(\"3e806983-eca4-4fc5-b581-f30fb03ec9e5\").hexstring.should eq \"3e806983eca44fc5b581f30fb03ec9e5\"\n    end\n\n    it \"urn\" do\n      UUID.new(\"1ed1ee2f-ef9a-4f9c-9615-ab14d8ef2892\").urn.should eq \"urn:uuid:1ed1ee2f-ef9a-4f9c-9615-ab14d8ef2892\"\n    end\n  end\n\n  it \"fails on invalid arguments when creating\" do\n    expect_raises(ArgumentError) { UUID.new \"25d6f843?cf8e-44fb-9f84-6062419c4330\" }\n    expect_raises(ArgumentError) { UUID.new \"67dc9e24-0865 474b-9fe7-61445bfea3b5\" }\n    expect_raises(ArgumentError) { UUID.new \"5942cde5-10d1-416b+85c4-9fc473fa1037\" }\n    expect_raises(ArgumentError) { UUID.new \"0f02a229-4898-4029-926f=94be5628a7fd\" }\n    expect_raises(ArgumentError) { UUID.new \"cda08c86-6413-474f-8822-a6646e0fb19G\" }\n    expect_raises(ArgumentError) { UUID.new \"2b1bfW06368947e59ac07c3ffdaf514c\" }\n    expect_raises(ArgumentError) { UUID.new \"xyz:uuid:1ed1ee2f-ef9a-4f9c-9615-ab14d8ef2892\" }\n  end\n\n  describe \"v1\" do\n    it \"returns true if UUID is v1, false otherwise\" do\n      uuid = UUID.v1\n      uuid.v1?.should be_true\n      uuid = UUID.v1(node_id: StaticArray(UInt8, 6).new { |i| (i*10).to_u8 })\n      uuid.to_s[24..36].should eq(\"000a141e2832\")\n    end\n  end\n\n  describe \"v2\" do\n    it \"returns true if UUID is v2, false otherwise\" do\n      uuid = UUID.v2(UUID::Domain::Person, 42)\n      uuid.v2?.should be_true\n      uuid = UUID.v2(UUID::Domain::Person, 42, node_id: StaticArray(UInt8, 6).new { |i| (i*10).to_u8 })\n      uuid.to_s[24..36].should eq(\"000a141e2832\")\n    end\n  end\n\n  describe \"v4?\" do\n    it \"returns true if UUID is v4, false otherwise\" do\n      uuid = UUID.random\n      uuid.v4?.should be_true\n      uuid = UUID.v4\n      uuid.v4?.should be_true\n      uuid = UUID.new(\"00000000-0000-0000-0000-000000000000\", version: UUID::Version::V5)\n      uuid.v4?.should be_false\n    end\n  end\n\n  describe \"v4!\" do\n    it \"returns true if UUID is v4, raises otherwise\" do\n      uuid = UUID.random\n      uuid.v4!.should be_true\n      uuid = UUID.v4\n      uuid.v4!.should be_true\n      uuid = UUID.new(\"00000000-0000-0000-0000-000000000000\", version: UUID::Version::V5)\n      expect_raises(UUID::Error) { uuid.v4! }\n    end\n  end\n\n  describe \"v3\" do\n    it \"generates DNS based names correctly\" do\n      data = \"crystal-lang.org\"\n      expected = \"60a4b7b5-3333-3f1e-a2cd-30d8a2d0b83b\"\n      UUID.v3(data, UUID::Namespace::DNS).to_s.should eq(expected)\n      UUID.v3_dns(data).to_s.should eq(expected)\n      UUID.v3_dns(data).v3?.should be_true\n    end\n\n    it \"generates URL based names correctly\" do\n      data = \"https://crystal-lang.org\"\n      expected = \"c25c7b79-5f5f-3844-98a4-2548f5d0e7f9\"\n      UUID.v3(data, UUID::Namespace::URL).to_s.should eq(expected)\n      UUID.v3_url(data).to_s.should eq(expected)\n      UUID.v3_url(data).v3?.should be_true\n    end\n\n    it \"generates OID based names correctly\" do\n      data = \"1.3.6.1.4.1.343\"\n      expected = \"77bc1dc3-0a9f-3e7e-bfa5-3f611a660c80\"\n      UUID.v3(data, UUID::Namespace::OID).to_s.should eq(expected)\n      UUID.v3_oid(data).to_s.should eq(expected)\n      UUID.v3_oid(data).v3?.should be_true\n    end\n\n    it \"generates X500 based names correctly\" do\n      data = \"cn=John Doe, ou=People, o=example, c=com\"\n      expected = \"fcab1a4c-fc81-3ebc-9874-9a8b931911d3\"\n      UUID.v3(data, UUID::Namespace::X500).to_s.should eq(expected)\n      UUID.v3_x500(data).to_s.should eq(expected)\n      UUID.v3_x500(data).v3?.should be_true\n    end\n  end\n\n  describe \"v5\" do\n    it \"generates DNS based names correctly\" do\n      data = \"crystal-lang.org\"\n      expected = \"11caf27c-b803-5e62-9c4b-15332b04047e\"\n      UUID.v5(data, UUID::Namespace::DNS).to_s.should eq(expected)\n      UUID.v5_dns(data).to_s.should eq(expected)\n      UUID.v5_dns(data).v5?.should be_true\n    end\n\n    it \"generates URL based names correctly\" do\n      data = \"https://crystal-lang.org\"\n      expected = \"29fec3f0-9ad0-5e8a-a42e-214ff695f50e\"\n      UUID.v5(data, UUID::Namespace::URL).to_s.should eq(expected)\n      UUID.v5_url(data).to_s.should eq(expected)\n      UUID.v5_url(data).v5?.should be_true\n    end\n\n    it \"generates OID based names correctly\" do\n      data = \"1.3.6.1.4.1.343\"\n      expected = \"6aab0456-7392-582a-b92a-ba5a7096945d\"\n      UUID.v5(data, UUID::Namespace::OID).to_s.should eq(expected)\n      UUID.v5_oid(data).to_s.should eq(expected)\n      UUID.v5_oid(data).v5?.should be_true\n    end\n\n    it \"generates X500 based names correctly\" do\n      data = \"cn=John Doe, ou=People, o=example, c=com\"\n      expected = \"bc10b2d9-f370-5c65-9561-5e3f6d9b236d\"\n      UUID.v5(data, UUID::Namespace::X500).to_s.should eq(expected)\n      UUID.v5_x500(data).to_s.should eq(expected)\n      UUID.v5_x500(data).v5?.should be_true\n    end\n  end\n\n  describe \"v7\" do\n    it \"generates a v7 UUID\" do\n      uuid = UUID.v7\n      uuid.v7?.should be_true\n      uuid.variant.rfc9562?.should be_true\n    end\n\n    pending_wasm32 \"generates UUIDs that are sortable with 1ms precision\" do\n      uuids = Array.new(10) do\n        sleep 1.millisecond\n        UUID.v7\n      end\n\n      uuids.should eq uuids.sort\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/va_list_spec.cr",
    "content": "{% skip_file if flag?(:win32) || flag?(:aarch64) %}\n\nrequire \"./spec_helper\"\n\ndescribe VaList do\n  it \"works with C code\", tags: %w[slow] do\n    compile_and_run_source_with_c(\n      %(\n          #include <stdarg.h>\n          extern int foo_f(int,...);\n          int foo() {\n            return foo_f(3,1,2,3);\n          }\n\n          int read_arg(va_list *ap) {\n            return va_arg(*ap, int);\n          }\n        ),\n      %(\n        lib LibFoo\n          fun foo : LibC::Int\n          fun read_arg(ap : LibC::VaList*) : LibC::Int\n        end\n\n        fun foo_f(count : LibC::Int, ...) : LibC::Int\n          sum = 0\n          VaList.open do |list|\n            ap = list.to_unsafe\n            count.times do |i|\n              sum += LibFoo.read_arg(pointerof(ap))\n            end\n          end\n          sum\n        end\n\n        puts LibFoo.foo\n      )) do |status, output|\n      status.success?.should be_true\n      output.to_i.should eq(6)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/wait_group_spec.cr",
    "content": "require \"spec\"\nrequire \"wait_group\"\n\nprivate def block_until_pending_waiter(wg)\n  while wg.@waiting.empty?\n    Fiber.yield\n  end\nend\n\nprivate def forge_counter(wg, value)\n  wg.@counter.set(value)\nend\n\ndescribe WaitGroup do\n  describe \"#add\" do\n    it \"can't decrement to a negative counter\" do\n      wg = WaitGroup.new\n      wg.add(5)\n      wg.add(-3)\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.add(-5) }\n    end\n\n    it \"resumes waiters when reaching negative counter\" do\n      wg = WaitGroup.new(1)\n      spawn do\n        block_until_pending_waiter(wg)\n        wg.add(-2)\n      rescue RuntimeError\n      end\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.wait }\n    end\n\n    it \"can't increment after reaching negative counter\" do\n      wg = WaitGroup.new\n      forge_counter(wg, -1)\n\n      # check twice, to make sure the waitgroup counter wasn't incremented back\n      # to a positive value!\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.add(5) }\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.add(3) }\n    end\n  end\n\n  describe \"#done\" do\n    it \"can't decrement to a negative counter\" do\n      wg = WaitGroup.new\n      wg.add(1)\n      wg.done\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.done }\n    end\n\n    it \"resumes waiters when reaching negative counter\" do\n      wg = WaitGroup.new(1)\n      spawn do\n        block_until_pending_waiter(wg)\n        forge_counter(wg, 0)\n        wg.done\n      rescue RuntimeError\n      end\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.wait }\n    end\n  end\n\n  describe \"#wait\" do\n    it \"immediately returns when counter is zero\" do\n      channel = Channel(Nil).new(1)\n\n      spawn do\n        wg = WaitGroup.new(0)\n        wg.wait\n        channel.send(nil)\n      end\n\n      select\n      when channel.receive\n        # success\n      when timeout(1.second)\n        fail \"expected #wait to not block the fiber\"\n      end\n    end\n\n    it \"immediately raises when counter is negative\" do\n      wg = WaitGroup.new(0)\n      expect_raises(RuntimeError) { wg.done }\n      expect_raises(RuntimeError, \"Negative WaitGroup counter\") { wg.wait }\n    end\n\n    it \"raises when counter is positive after wake up\" do\n      wg = WaitGroup.new(1)\n      waiter = Fiber.current\n\n      spawn do\n        block_until_pending_waiter(wg)\n        waiter.enqueue\n      end\n\n      expect_raises(RuntimeError, \"Positive WaitGroup counter (early wake up?)\") { wg.wait }\n    end\n  end\n\n  it \"waits until concurrent executions are finished\" do\n    wg1 = WaitGroup.new\n    wg2 = WaitGroup.new\n\n    8.times do\n      wg1.add(16)\n      wg2.add(16)\n      exited = Channel(Bool).new(16)\n\n      16.times do\n        spawn do\n          wg1.done\n          wg2.wait\n          exited.send(true)\n        end\n      end\n\n      wg1.wait\n\n      16.times do\n        select\n        when exited.receive\n          fail \"WaitGroup released group too soon\"\n        else\n        end\n        wg2.done\n      end\n\n      16.times do\n        select\n        when x = exited.receive\n          x.should be_true\n        when timeout(1.millisecond)\n          fail \"Expected channel to receive value\"\n        end\n      end\n    end\n  end\n\n  it \"increments the counter from executing fibers\" do\n    wg = WaitGroup.new(16)\n    extra = Atomic(Int32).new(0)\n\n    16.times do\n      spawn do\n        wg.add(2)\n\n        2.times do\n          spawn do\n            extra.add(1)\n            wg.done\n          end\n        end\n\n        wg.done\n      end\n    end\n\n    wg.wait\n    extra.get.should eq(32)\n  end\n\n  it \"takes a block to WaitGroup.wait\" do\n    fiber_count = 10\n    completed = Array.new(fiber_count) { false }\n\n    WaitGroup.wait do |wg|\n      fiber_count.times do |i|\n        wg.spawn { completed[i] = true }\n      end\n    end\n\n    completed.should eq [true] * 10\n  end\n\n  # the test takes far too much time for the interpreter to complete\n  {% unless flag?(:interpreted) %}\n    it \"stress add/done/wait\" do\n      wg = WaitGroup.new\n\n      1000.times do\n        counter = Atomic(Int32).new(0)\n\n        2.times do\n          wg.add(1)\n\n          spawn do\n            counter.add(1)\n            wg.done\n          end\n        end\n\n        wg.wait\n        counter.get.should eq(2)\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/std/weak_ref_spec.cr",
    "content": "require \"spec\"\nrequire \"weak_ref\"\nrequire \"../support/finalize\"\n\nprivate class Foo\n  include FinalizeCounter\n\n  def initialize(@key : String)\n  end\nend\n\ndescribe WeakRef do\n  it \"should get dereferenced object\" do\n    foo = Foo.new(\"foo\")\n    ref = WeakRef.new(foo)\n    ref.should_not be_nil\n    ref.value.should be(foo)\n  end\n\n  it \"should get dereferenced object in data section\" do\n    foo = \"foo\"\n    ref = WeakRef.new(foo)\n    ref.value.should be(foo)\n  end\n\n  it \"should not crash with object in data section during GC\" do\n    foo = \"foo\"\n    ref = WeakRef.new(foo)\n    GC.collect\n    ref.value.should be(foo)\n  end\n\n  it \"FinalizeState counts released objects\" do\n    FinalizeState.reset\n    FinalizeState.count(\"foo\").should eq 0\n    10.times do\n      Foo.new(\"foo\")\n    end\n    GC.collect\n    FinalizeState.count(\"foo\").should be > 0\n  end\n\n  it \"Referenced object should not be released\" do\n    FinalizeState.reset\n    instances = [] of Foo\n    FinalizeState.count(\"strong_foo_ref\").should eq 0\n    10.times do\n      instances << Foo.new(\"strong_foo_ref\")\n    end\n    GC.collect\n    FinalizeState.count(\"strong_foo_ref\").should eq 0\n  end\n\n  it \"Weak referenced object should be released if no other reference\" do\n    FinalizeState.reset\n    instances = [] of WeakRef(Foo)\n    last = nil\n    10.times do\n      last = Foo.new(\"weak_foo_ref\")\n      instances << WeakRef.new(last)\n    end\n    GC.collect\n    FinalizeState.count(\"weak_foo_ref\").should be > 0\n    instances.count { |wr| wr.value.nil? }.should be > 0\n    instances[-1].value.should_not be_nil\n\n    # Use `last` to stop the variable from being optimised away in release mode.\n    last.to_s\n  end\nend\n"
  },
  {
    "path": "spec/std/winerror_spec.cr",
    "content": "require \"spec\"\n\ndescribe WinError do\n  it \".value\" do\n    {% if flag?(:win32) %}\n      WinError.value = WinError::ERROR_SUCCESS\n      WinError.value.should eq WinError::ERROR_SUCCESS\n      WinError.value = WinError::ERROR_BROKEN_PIPE\n      WinError.value.should eq WinError::ERROR_BROKEN_PIPE\n    {% else %}\n      expect_raises(NotImplementedError) do\n        WinError.value = WinError::ERROR_SUCCESS\n      end\n      expect_raises(NotImplementedError) do\n        WinError.value\n      end\n    {% end %}\n  end\n\n  it \".wsa_value\" do\n    {% if flag?(:win32) %}\n      WinError.wsa_value = WinError::ERROR_SUCCESS\n      WinError.wsa_value.should eq WinError::ERROR_SUCCESS\n      WinError.wsa_value = WinError::WSAEBADF\n      WinError.wsa_value.should eq WinError::WSAEBADF\n    {% else %}\n      expect_raises(NotImplementedError) do\n        WinError.wsa_value = WinError::ERROR_SUCCESS\n      end\n      expect_raises(NotImplementedError) do\n        WinError.wsa_value\n      end\n    {% end %}\n  end\n\n  it \"#message\" do\n    message = WinError::ERROR_SUCCESS.message\n    {% if flag?(:win32) %}\n      # Not testing for specific content because the result is locale-specific\n      # and currently the message uses only default `LANGID`.\n      message.should_not be_empty\n    {% else %}\n      message.should eq \"\"\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "spec/std/xml/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"xml\"\nrequire \"spec/helpers/string\"\n\nprivate def assert_built(expected, quote_char = nil, *, file = __FILE__, line = __LINE__, &)\n  assert_prints XML.build(quote_char: quote_char) { |xml| with xml yield xml }, expected, file: file, line: line\nend\n\ndescribe XML::Builder do\n  it \"writes document\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n\\n]) do\n    end\n  end\n\n  it \"writes element\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo/>\\n]) do\n      element(\"foo\") { }\n    end\n  end\n\n  it \"errors on invalid element names\" do\n    expect_raises(XML::Error, \"Invalid element name: '1'\") do\n      XML.build do |xml|\n        xml.element(\"1\") do\n        end\n      end\n    end\n\n    expect_raises(XML::Error, \"Invalid element name: 'a b=\\\"c\\\"'\") do\n      XML.build do |xml|\n        xml.element(\"a b=\\\"c\\\"\") do\n        end\n      end\n    end\n  end\n\n  it \"writes nested element\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo><bar/></foo>\\n]) do\n      element(\"foo\") do\n        element(\"bar\") { }\n      end\n    end\n  end\n\n  it \"writes element with namespace\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<x:foo id=\"1\" xmlns:x=\"http://foo.com\"/>\\n]) do\n      element(\"x\", \"foo\", \"http://foo.com\", id: 1) { }\n    end\n  end\n\n  it \"writes element with namespace, without block\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<x:foo id=\"1\" xmlns:x=\"http://foo.com\"/>\\n]) do\n      element(\"x\", \"foo\", \"http://foo.com\", id: 1)\n    end\n  end\n\n  it \"writes attribute\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo id=\"1\"/>\\n]) do\n      element(\"foo\") do\n        attribute(\"id\", 1)\n      end\n    end\n  end\n\n  it \"writes attribute with namespace\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo x:id=\"1\" xmlns:x=\"http://ww.foo.com\"/>\\n]) do\n      element(\"foo\") do\n        attribute(\"x\", \"id\", \"http://ww.foo.com\", 1)\n      end\n    end\n  end\n\n  it \"writes element with namespace\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo xmlns=\"bar\">baz</foo>\\n]) do\n      element(nil, \"foo\", \"bar\") do\n        text \"baz\"\n      end\n    end\n  end\n\n  it \"writes element with prefix\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo:bar>baz</foo:bar>\\n]) do\n      element(\"foo\", \"bar\", nil) do\n        text \"baz\"\n      end\n    end\n  end\n\n  it \"errors on invalid element name with prefix\" do\n    expect_raises(XML::Error, \"Invalid prefix: 'foo='\") do\n      XML.build do |xml|\n        xml.element(\"foo=\", \"bar\", nil) do\n          xml.text \"baz\"\n        end\n      end\n    end\n  end\n\n  it \"errors on invalid element name with prefix and namespace\" do\n    expect_raises(XML::Error, \"Invalid prefix: 'foo '\") do\n      XML.build do |xml|\n        xml.element(\"foo \", \"bar\", \"ns\") do\n          xml.text \"baz\"\n        end\n      end\n    end\n  end\n\n  it \"writes text\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo>1 &lt; 2</foo>\\n]) do\n      element(\"foo\") do\n        text \"1 < 2\"\n      end\n    end\n  end\n\n  it \"sets indent with string\" do\n    assert_built(\"<?xml version=\\\"1.0\\\"?>\\n<foo>\\n\\t<bar/>\\n</foo>\\n\") do |xml|\n      xml.indent = \"\\t\"\n      element(\"foo\") do\n        element(\"bar\")\n      end\n    end\n  end\n\n  it \"sets indent with count\" do\n    assert_built(\"<?xml version=\\\"1.0\\\"?>\\n<foo>\\n  <bar/>\\n</foo>\\n\") do |xml|\n      xml.indent = 2\n      element(\"foo\") do\n        element(\"bar\")\n      end\n    end\n  end\n\n  it \"sets quote char\" do\n    assert_built(\"<?xml version='1.0'?>\\n<foo id='1'/>\\n\", quote_char: '\\'') do |xml|\n      element(\"foo\") do\n        attribute(\"id\", 1)\n      end\n    end\n  end\n\n  it \"writes element with attributes as named tuple\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo id=\"1\" name=\"foo\"/>\\n]) do |xml|\n      element(\"foo\", id: 1, name: \"foo\")\n    end\n  end\n\n  it \"writes element with attributes as named tuple, nesting\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo id=\"1\" name=\"foo\" baz=\"2\"/>\\n]) do |xml|\n      element(\"foo\", id: 1, name: \"foo\") do\n        attribute \"baz\", 2\n      end\n    end\n  end\n\n  it \"writes element with attributes as hash\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo id=\"1\" name=\"foo\"/>\\n]) do |xml|\n      element(\"foo\", {\"id\" => 1, \"name\" => \"foo\"})\n    end\n  end\n\n  it \"writes element with attributes as hash, nesting\" do\n    assert_built(%[<?xml version=\"1.0\"?>\\n<foo id=\"1\" name=\"foo\" baz=\"2\"/>\\n]) do |xml|\n      element(\"foo\", {\"id\" => 1, \"name\" => \"foo\"}) do\n        attribute \"baz\", 2\n      end\n    end\n  end\n\n  describe \"#cdata\" do\n    it \"writes cdata\" do\n      assert_built(%{<?xml version=\"1.0\"?>\\n<foo><![CDATA[hello]]></foo>\\n}) do |xml|\n        element(\"foo\") do\n          cdata(\"hello\")\n        end\n      end\n    end\n\n    it \"escapes ]]> sequences\" do\n      assert_built(%{<?xml version=\"1.0\"?>\\n<foo><![CDATA[One]]]]><![CDATA[>Two]]]]><![CDATA[>Three]]></foo>\\n}) do |xml|\n        element(\"foo\") do\n          cdata(\"One]]>Two]]>Three\")\n        end\n      end\n    end\n\n    it \"writes cdata with block\" do\n      assert_built(%{<?xml version=\"1.0\"?>\\n<foo><![CDATA[hello]]></foo>\\n}) do |xml|\n        element(\"foo\") do\n          cdata do\n            text \"hello\"\n          end\n        end\n      end\n    end\n  end\n\n  it \"writes comment\" do\n    assert_built(%{<?xml version=\"1.0\"?>\\n<foo><!--hello--></foo>\\n}) do |xml|\n      element(\"foo\") do\n        comment(\"hello\")\n      end\n    end\n  end\n\n  it \"writes comment with block\" do\n    assert_built(%{<?xml version=\"1.0\"?>\\n<foo><!--hello--></foo>\\n}) do |xml|\n      element(\"foo\") do\n        comment do\n          text \"hello\"\n        end\n      end\n    end\n  end\n\n  it \"writes DTD\" do\n    assert_built(%{<?xml version=\"1.0\"?>\\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [subset]>\\n}) do |xml|\n      dtd \"html\", \"-//W3C//DTD XHTML 1.0 Transitional//EN\", \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\", \"subset\"\n    end\n  end\n\n  it \"writes DTD with block\" do\n    assert_built(%{<?xml version=\"1.0\"?>\\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [subset]>\\n}) do |xml|\n      dtd \"html\", \"-//W3C//DTD XHTML 1.0 Transitional//EN\", \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" do\n        text \"subset\"\n      end\n    end\n  end\n\n  it \"writes namespace\" do\n    assert_built(%{<?xml version=\"1.0\"?>\\n<foo xmlns:x=\"http://foo.com\"/>\\n}) do |xml|\n      element(\"foo\") do\n        namespace \"x\", \"http://foo.com\"\n      end\n    end\n  end\n\n  it \"writes to string\" do\n    str = XML.build do |xml|\n      xml.element(\"foo\", id: 1) do\n        xml.text \"hello\"\n      end\n    end\n    str.should eq(\"<?xml version=\\\"1.0\\\"?>\\n<foo id=\\\"1\\\">hello</foo>\\n\")\n  end\n\n  it \"writes to IO\" do\n    io = IO::Memory.new\n    XML.build(io) do |xml|\n      xml.element(\"foo\", id: 1) do\n        xml.text \"hello\"\n      end\n    end\n    io.rewind\n    io.to_s.should eq(\"<?xml version=\\\"1.0\\\"?>\\n<foo id=\\\"1\\\">hello</foo>\\n\")\n  end\n\n  it \"errors on null byte\" do\n    expect_raises(XML::Error, \"String cannot contain null character\") do\n      XML.build do |xml|\n        xml.element(\"example\", number: \"1\") do\n          xml.text \"foo\\0bar\"\n        end\n      end\n    end\n\n    expect_raises(XML::Error, \"String cannot contain null character\") do\n      XML.build do |xml|\n        xml.element(\"exam\\0ple\", number: \"1\") do\n          xml.text \"foobar\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/xml/html_spec.cr",
    "content": "require \"spec\"\nrequire \"xml\"\n\ndescribe XML do\n  it \"parses HTML\" do\n    doc = XML.parse_html(%(\\\n      <!doctype html>\n      <html>\n      <head>\n          <title>Samantha</title>\n      </head>\n      <body>\n          <h1 class=\"large\">Boat</h1>\n      </body>\n      </html>\n    ))\n\n    html = doc.children[1]\n    html.name.should eq(\"html\")\n\n    head = html.children.find! { |node| node.name == \"head\" }\n    head.name.should eq(\"head\")\n\n    title = head.children.find! { |node| node.name == \"title\" }\n    title.text.should eq(\"Samantha\")\n\n    body = html.children.find! { |node| node.name == \"body\" }\n\n    h1 = body.children.find! { |node| node.name == \"h1\" }\n\n    attrs = h1.attributes\n    attrs.should_not be_empty\n    attrs.size.should eq(1)\n\n    attr = attrs[0]\n    attr.name.should eq(\"class\")\n    attr.content.should eq(\"large\")\n    attr.text.should eq(\"large\")\n    attr.inner_text.should eq(\"large\")\n  end\n\n  it \"parses HTML from IO\" do\n    io = IO::Memory.new(%(\\\n      <!doctype html>\n      <html>\n      <head>\n          <title>Samantha</title>\n      </head>\n      <body>\n          <h1 class=\"large\">Boat</h1>\n      </body>\n      </html>\n    ))\n\n    doc = XML.parse_html(io)\n    html = doc.children[1]\n    html.name.should eq(\"html\")\n  end\n\n  it \"parses html5 (#1404)\" do\n    html5 = \"<html><body><nav>Test</nav></body></html>\"\n    xml = XML.parse_html(html5)\n    xml.xpath_node(\"//html/body/nav\").should_not be_nil\n  end\n\n  it \"raises error when parsing empty string (#2752)\" do\n    expect_raises XML::Error, \"Document is empty\" do\n      XML.parse_html(\"\")\n    end\n  end\n\n  it \"gets name of HTML document node (#4040)\" do\n    doc = XML.parse_html(%(\\\n      <!doctype html>\n      <html>\n      </html>\n    ))\n    doc.document?.should be_true\n    doc.name.should eq(\"document\")\n  end\nend\n"
  },
  {
    "path": "spec/std/xml/reader_spec.cr",
    "content": "require \"spec\"\nrequire \"xml\"\n\nprivate def xml\n  <<-XML\n  <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n  <people>\n    <person id=\"1\">\n      <name>John</name>\n    </person>\n    <person id=\"2\">\n      <name>Peter</name>\n    </person>\n  </people>\n  XML\nend\n\nmodule XML\n  describe Reader do\n    describe \".new\" do\n      context \"with default parser options\" do\n        it \"can be initialized from a string\" do\n          reader = Reader.new(xml)\n          reader.should be_a(XML::Reader)\n          reader.read.should be_true\n          reader.name.should eq(\"people\")\n          reader.read.should be_true\n          reader.name.should eq(\"#text\")\n        end\n\n        it \"can be initialized from an io\" do\n          io = IO::Memory.new(xml)\n          reader = Reader.new(io)\n          reader.should be_a(XML::Reader)\n          reader.read.should be_true\n          reader.name.should eq(\"people\")\n          reader.read.should be_true\n          reader.name.should eq(\"#text\")\n        end\n      end\n\n      context \"with custom parser options\" do\n        it \"can be initialized from a string\" do\n          reader = Reader.new(xml, XML::ParserOptions::NOBLANKS)\n          reader.should be_a(XML::Reader)\n          reader.read.should be_true\n          reader.name.should eq(\"people\")\n          reader.read.should be_true\n          reader.name.should eq(\"person\")\n        end\n\n        it \"can be initialized from an io\" do\n          io = IO::Memory.new(xml)\n          reader = Reader.new(io, XML::ParserOptions::NOBLANKS)\n          reader.should be_a(XML::Reader)\n          reader.read.should be_true\n          reader.name.should eq(\"people\")\n          reader.read.should be_true\n          reader.name.should eq(\"person\")\n        end\n      end\n    end\n\n    describe \"#read\" do\n      it \"reads all nodes\" do\n        reader = Reader.new(xml)\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"people\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"1\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::TEXT)\n        reader.name.should eq(\"#text\")\n        reader.value.should eq(\"John\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"person\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"2\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::TEXT)\n        reader.name.should eq(\"#text\")\n        reader.value.should eq(\"Peter\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"person\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"people\")\n        reader.read.should be_false\n      end\n\n      it \"reads all non-blank nodes with NOBLANKS option\" do\n        reader = Reader.new(xml, XML::ParserOptions::NOBLANKS)\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"people\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"1\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::TEXT)\n        reader.name.should eq(\"#text\")\n        reader.value.should eq(\"John\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"person\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"2\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::TEXT)\n        reader.name.should eq(\"#text\")\n        reader.value.should eq(\"Peter\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"person\")\n        reader.read.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"people\")\n        reader.read.should be_false\n      end\n    end\n\n    describe \"#next\" do\n      it \"reads next node in doc order, skipping subtrees\" do\n        reader = Reader.new(xml)\n        while reader.read\n          break if reader.depth == 2\n        end\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"name\")\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"1\")\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"2\")\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.next.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"people\")\n        reader.next.should be_false\n      end\n    end\n\n    describe \"#next_sibling\" do\n      it \"reads next sibling node in doc order, skipping subtrees\" do\n        reader = Reader.new(xml)\n        while reader.read\n          break if reader.depth == 1\n        end\n        reader.next_sibling.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"1\")\n        reader.next_sibling.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.next_sibling.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"person\")\n        reader[\"id\"].should eq(\"2\")\n        reader.next_sibling.should be_true\n        reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)\n        reader.name.should eq(\"#text\")\n        reader.next_sibling.should be_false\n      end\n    end\n\n    describe \"#node_type\" do\n      it \"returns the node type\" do\n        reader = Reader.new(\"<root/>\")\n        reader.node_type.should eq(XML::Reader::Type::NONE)\n        reader.read\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n      end\n    end\n\n    describe \"#name\" do\n      it \"reads node name\" do\n        reader = Reader.new(\"<root/>\")\n        reader.name.should eq(\"\")\n        reader.read\n        reader.name.should eq(\"root\")\n      end\n    end\n\n    describe \"#empty_element?\" do\n      it \"checks if the node is empty\" do\n        reader = Reader.new(\"<root/>\")\n        reader.empty_element?.should be_false\n        reader.read\n        reader.empty_element?.should be_true\n        reader = Reader.new(\"<root></root>\")\n        reader.read\n        reader.empty_element?.should be_false\n      end\n    end\n\n    describe \"#has_attributes?\" do\n      it \"checks if the node has attributes\" do\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.has_attributes?.should be_false\n        reader.read # <root id=\"1\">\n        reader.has_attributes?.should be_true\n        reader.read # <child/>\n        reader.has_attributes?.should be_false\n        reader.read # </root>\n        reader.has_attributes?.should be_true\n      end\n    end\n\n    describe \"#attributes_count\" do\n      it \"returns the node's number of attributes\" do\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.attributes_count.should eq(0)\n        reader.read # <root id=\"1\">\n        reader.attributes_count.should eq(1)\n        reader.read # <child/>\n        reader.attributes_count.should eq(0)\n        reader.read # </root>\n        # This is weird, since has_attributes? will be true.\n        reader.attributes_count.should eq(0)\n      end\n    end\n\n    describe \"#move_to_first_attribute\" do\n      it \"moves to the first attribute of the node\" do\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.move_to_first_attribute.should be_false\n        reader.read # <root id=\"1\">\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.move_to_first_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.value.should eq(\"1\")\n        reader.read # <child/>\n        reader.move_to_first_attribute.should be_false\n        reader.read # </root>\n        reader.move_to_first_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.value.should eq(\"1\")\n        reader.read.should be_false\n      end\n    end\n\n    describe \"#move_to_next_attribute\" do\n      it \"moves to the next attribute of the node\" do\n        reader = Reader.new(%{<root id=\"1\" id2=\"2\"><child/></root>})\n        reader.move_to_next_attribute.should be_false\n        reader.read # <root id=\"1\" id2=\"2\">\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.move_to_next_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.value.should eq(\"1\")\n        reader.move_to_next_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id2\")\n        reader.value.should eq(\"2\")\n        reader.move_to_next_attribute.should be_false\n        reader.read # <child/>\n        reader.move_to_next_attribute.should be_false\n        reader.read # </root>\n        reader.move_to_next_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.value.should eq(\"1\")\n        reader.read.should be_false\n      end\n    end\n\n    describe \"#move_to_attribute\" do\n      it \"moves to attribute with the specified name\" do\n        reader = Reader.new(%{<root id=\"1\" id2=\"2\"><child/></root>})\n        reader.move_to_attribute(\"id2\").should be_false\n        reader.read # <root id=\"1\" id2=\"2\">\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.move_to_attribute(\"id2\").should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id2\")\n        reader.value.should eq(\"2\")\n        reader.move_to_attribute(\"id\").should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.value.should eq(\"1\")\n        reader.move_to_attribute(\"bogus\").should be_false\n        reader.read # <child/>\n        reader.move_to_attribute(\"id2\").should be_false\n        reader.read # </root>\n        reader.move_to_attribute(\"id2\").should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id2\")\n        reader.value.should eq(\"2\")\n        reader.read.should be_false\n      end\n\n      it \"raises if attribute contains null byte\" do\n        reader = Reader.new(\"<root/>\")\n        expect_raises(Exception) { reader.move_to_attribute(\"\\0\") }\n      end\n    end\n\n    describe \"#[]\" do\n      it \"reads node attributes\" do\n        reader = Reader.new(\"<root/>\")\n        expect_raises(KeyError) { reader[\"id\"] }\n        reader.read\n        expect_raises(KeyError) { reader[\"id\"] }\n        reader = Reader.new(%{<root id=\"1\"/>})\n        reader.read\n        reader[\"id\"].should eq(\"1\")\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.read # <root id=\"1\">\n        reader[\"id\"].should eq(\"1\")\n        reader.read # <child/>\n        expect_raises(KeyError) { reader[\"id\"] }\n        reader.read # </root>\n        reader[\"id\"].should eq(\"1\")\n      end\n\n      it \"raises if attribute contains null byte\" do\n        reader = Reader.new(\"<root/>\")\n        expect_raises(Exception) { reader[\"\\0\"] }\n      end\n    end\n\n    describe \"#[]?\" do\n      it \"reads node attributes\" do\n        reader = Reader.new(\"<root/>\")\n        reader[\"id\"]?.should be_nil\n        reader.read\n        reader[\"id\"]?.should be_nil\n        reader = Reader.new(%{<root id=\"1\"/>})\n        reader.read\n        reader[\"id\"]?.should eq(\"1\")\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.read # <root id=\"1\">\n        reader[\"id\"]?.should eq(\"1\")\n        reader.read # <child/>\n        reader[\"id\"]?.should be_nil\n        reader.read # </root>\n        reader[\"id\"]?.should eq(\"1\")\n      end\n\n      it \"raises if attribute contains null byte\" do\n        reader = Reader.new(\"<root/>\")\n        expect_raises(Exception) { reader[\"\\0\"]? }\n      end\n    end\n\n    describe \"#move_to_element\" do\n      it \"moves to the element node that contains the current attribute node\" do\n        reader = Reader.new(%{<root id=\"1\"></root>})\n        reader.move_to_element.should be_false\n        reader.read # <root id=\"1\">\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"root\")\n        reader.move_to_element.should be_false\n        reader.move_to_first_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.move_to_element.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ELEMENT)\n        reader.name.should eq(\"root\")\n        reader.read # </root>\n        reader.move_to_element.should be_false\n        reader.move_to_first_attribute.should be_true\n        reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)\n        reader.name.should eq(\"id\")\n        reader.move_to_element.should be_true\n        reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)\n        reader.name.should eq(\"root\")\n        reader.read.should be_false\n      end\n    end\n\n    describe \"#depth\" do\n      it \"returns the depth of the node\" do\n        reader = Reader.new(\"<root><child/></root>\")\n        reader.depth.should eq(0)\n        reader.read # <root>\n        reader.depth.should eq(0)\n        reader.read # <child/>\n        reader.depth.should eq(1)\n        reader.read # </root>\n        reader.depth.should eq(0)\n      end\n    end\n\n    describe \"#read_inner_xml\" do\n      it \"reads the contents of the node including child nodes and markup\" do\n        reader = Reader.new(\"<root>\\n<child/>\\n</root>\\n\")\n        reader.read_inner_xml.should eq(\"\")\n        reader.read # <root>\n        reader.read_inner_xml.should eq(\"\\n<child/>\\n\")\n        reader.read # \\n\n        reader.read_inner_xml.should eq(\"\")\n        reader.read # <child/>\n        reader.read_inner_xml.should eq(\"\")\n        reader.read # \\n\n        reader.read_inner_xml.should eq(\"\")\n        reader.read # </root>\n        reader.read_inner_xml.should eq(\"\")\n        reader.read.should be_false\n      end\n    end\n\n    describe \"#read_outer_xml\" do\n      it \"reads the xml of the node including child nodes and markup\" do\n        reader = Reader.new(\"<root>\\n<child/>\\n</root>\\n\")\n        reader.read_outer_xml.should eq(\"\")\n        reader.read # <root>\n        reader.read_outer_xml.should eq(\"<root>\\n<child/>\\n</root>\")\n        reader.read # \\n\n        reader.read_outer_xml.should eq(\"\\n\")\n        reader.read # <child/>\n        reader.read_outer_xml.should eq(\"<child/>\")\n        reader.read # \\n\n        reader.read_outer_xml.should eq(\"\\n\")\n        reader.read # </root>\n        # Note that the closing element is transformed into a self-closing one.\n        reader.read_outer_xml.should eq(\"<root/>\")\n        reader.read.should be_false\n      end\n    end\n\n    describe \"#expand\" do\n      it \"raises an exception if the node could not be expanded\" do\n        reader = Reader.new(%{<root id=\"1<child/></root>}) # Invalid XML\n        reader.read\n        expect_raises XML::Error, \"Couldn't find end of Start Tag root\" do\n          reader.expand\n        end\n      end\n\n      it \"parses the content of the node and subtree\" do\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.read # <root id=\"1\">\n        node = reader.expand\n        node.should be_a(XML::Node)\n        node.attributes[\"id\"].content.should eq(\"1\")\n        node.xpath_node(\"child\").should be_a(XML::Node)\n      end\n\n      it \"is only available until the next read\" do\n        reader = Reader.new(%{<root><child><subchild/></child></root>})\n        reader.read # <root>\n        reader.read # <child>\n        node = reader.expand\n        node.should be_a(XML::Node)\n        node.xpath_node(\"subchild\").should be_a(XML::Node)\n        reader.read # <subchild/>\n        reader.read # </child>\n        node.xpath_node(\"subchild\").should be_nil\n      end\n    end\n\n    describe \"#expand?\" do\n      it \"parses the content of the node and subtree\" do\n        reader = Reader.new(%{<root id=\"1\"><child/></root>})\n        reader.expand?.should be_nil\n        reader.read # <root id=\"1\">\n        node = reader.expand?\n        node.should be_a(XML::Node)\n        node.not_nil!.attributes[\"id\"].content.should eq(\"1\")\n        node.not_nil!.xpath_node(\"child\").should be_a(XML::Node)\n      end\n\n      it \"is only available until the next read\" do\n        reader = Reader.new(%{<root><child><subchild/></child></root>})\n        reader.read # <root>\n        reader.read # <child>\n        node = reader.expand?\n        node.should be_a(XML::Node)\n        node.not_nil!.xpath_node(\"subchild\").should be_a(XML::Node)\n        reader.read # <subchild/>\n        reader.read # </child>\n        node.not_nil!.xpath_node(\"subchild\").should be_nil\n      end\n    end\n\n    describe \"#value\" do\n      it \"reads node text value\" do\n        reader = Reader.new(%{<root id=\"1\">hello<!-- world --></root>})\n        reader.value.should eq(\"\")\n        reader.read # <root>\n        reader.value.should eq(\"\")\n        reader.read # hello\n        reader.value.should eq(\"hello\")\n        reader.read # <!-- world -->\n        reader.value.should eq(\" world \")\n        reader.read # </root>\n        reader.move_to_first_attribute.should be_true\n        reader.value.should eq(\"1\")\n      end\n    end\n\n    describe \"#to_unsafe\" do\n      it \"returns a pointer to the underlying LibXML::XMLTextReader\" do\n        reader = Reader.new(\"<root/>\")\n        reader.to_unsafe.should be_a(LibXML::XMLTextReader)\n      end\n    end\n\n    describe \"#errors\" do\n      it \"makes errors accessible\" do\n        options = XML::ParserOptions::RECOVER | XML::ParserOptions::NONET\n        reader = XML::Reader.new(%(<people></foo>), options)\n        reader.read\n        reader.expand?\n\n        reader.errors.map(&.to_s).should eq [\"Opening and ending tag mismatch: people line 1 and foo\"]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/xml/xml_spec.cr",
    "content": "require \"spec\"\nrequire \"xml\"\nrequire \"spec/helpers/string\"\nrequire \"semantic_version\"\n\ndescribe XML do\n  it \"parses\" do\n    doc = XML.parse(<<-XML\n      <?xml version='1.0' encoding='UTF-8'?>\n      <people>\n        <person id=\"1\" id2=\"2\">\n          <name>John</name>\n        </person>\n      </people>\n      XML\n    )\n    doc.document.should eq(doc)\n    doc.name.should eq(\"document\")\n    doc.attributes.should be_empty\n    doc.namespace.should be_nil\n\n    people = doc.root.not_nil!\n    people.name.should eq(\"people\")\n    people.type.should eq(XML::Node::Type::ELEMENT_NODE)\n\n    people.attributes.should be_empty\n\n    children = doc.children\n    children.size.should eq(1)\n    children.should_not be_empty\n\n    people = children[0]\n    people.name.should eq(\"people\")\n\n    people.document.should eq(doc)\n\n    children = people.children\n    children.size.should eq(3)\n\n    text = children[0]\n    text.name.should eq(\"text\")\n    text.content.should eq(\"\\n  \")\n\n    person = children[1]\n    person.name.should eq(\"person\")\n\n    text = children[2]\n    text.content.should eq(\"\\n\")\n\n    attrs = person.attributes\n    attrs.should_not be_empty\n    attrs.size.should eq(2)\n\n    attr = attrs[0]\n    attr.name.should eq(\"id\")\n    attr.content.should eq(\"1\")\n    attr.text.should eq(\"1\")\n    attr.inner_text.should eq(\"1\")\n\n    attr = attrs[1]\n    attr.name.should eq(\"id2\")\n    attr.content.should eq(\"2\")\n\n    attrs[\"id\"].content.should eq(\"1\")\n    attrs[\"id2\"].content.should eq(\"2\")\n\n    attrs[\"id3\"]?.should be_nil\n    expect_raises(KeyError) { attrs[\"id3\"] }\n\n    person[\"id\"].should eq(\"1\")\n    person[\"id2\"].should eq(\"2\")\n    person[\"id3\"]?.should be_nil\n    expect_raises(KeyError) { person[\"id3\"] }\n\n    name = person.children.find! { |node| node.name == \"name\" }\n    name.content.should eq(\"John\")\n\n    name.parent.should eq(person)\n  end\n\n  it \"parses from io\" do\n    io = IO::Memory.new(<<-XML\n      <?xml version='1.0' encoding='UTF-8'?>\n      <people>\n        <person id=\"1\" id2=\"2\">\n          <name>John</name>\n        </person>\n      </people>\n      XML\n    )\n\n    doc = XML.parse(io)\n    doc.document.should eq(doc)\n    doc.name.should eq(\"document\")\n\n    people = doc.children.find! { |node| node.name == \"people\" }\n    person = people.children.find! { |node| node.name == \"person\" }\n    person[\"id\"].should eq(\"1\")\n  end\n\n  it \"raises exception on empty string\" do\n    expect_raises XML::Error, \"Document is empty\" do\n      XML.parse(\"\")\n    end\n  end\n\n  it \"does to_s\" do\n    string = <<-XML\n      <?xml version='1.0' encoding='UTF-8'?>\\\n      <people>\n        <person id=\"1\" id2=\"2\">\n          <name>John</name>\n        </person>\n      </people>\n      XML\n\n    doc = XML.parse(string)\n    doc.to_s.strip.should eq(<<-XML\n      <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n      <people>\n        <person id=\"1\" id2=\"2\">\n          <name>John</name>\n        </person>\n      </people>\n      XML\n    )\n  end\n\n  it \"navigates in tree\" do\n    doc = XML.parse(<<-XML\n      <?xml version='1.0' encoding='UTF-8'?>\n      <people>\n        <person id=\"1\" />\n        <person id=\"2\" />\n      </people>\n      XML\n    )\n\n    people = doc.first_element_child.not_nil!\n    people.name.should eq(\"people\")\n\n    person = people.first_element_child.not_nil!\n    person.name.should eq(\"person\")\n    person[\"id\"].should eq(\"1\")\n\n    text = person.next.not_nil!\n    text.content.should eq(\"\\n  \")\n\n    text.previous.should eq(person)\n    text.previous_sibling.should eq(person)\n\n    person.next_sibling.should eq(text)\n\n    person2 = text.next.not_nil!\n    person2.name.should eq(\"person\")\n    person2[\"id\"].should eq(\"2\")\n\n    person.next_element.should eq(person2)\n    person2.previous_element.should eq(person)\n  end\n\n  describe \"#errors\" do\n    it do\n      options = XML::ParserOptions::RECOVER | XML::ParserOptions::NONET\n\n      xml = XML.parse(%(<people></foo>), options)\n      xml.root.not_nil!.name.should eq(\"people\")\n      xml.errors.try(&.map(&.to_s)).should eq [\"Opening and ending tag mismatch: people line 1 and foo\"]\n\n      xml = XML.parse(%(<foo></foo>))\n      xml.errors.should be_nil\n    end\n\n    describe \"NOERROR option (https://github.com/crystal-lang/crystal/issues/16090)\" do\n      it \"is unset by default\" do\n        XML.parse(\"<people></foo>\").errors.try(&.map(&.message)).should eq [\"Opening and ending tag mismatch: people line 1 and foo\"]\n      end\n\n      it \"if set, may suppress context-error handler\" do\n        if SemanticVersion.parse(XML.libxml2_version) < SemanticVersion.parse(\"2.13.0\")\n          XML.parse(\"<people></foo>\", XML::ParserOptions[RECOVER, NOERROR]).errors.try(&.map(&.message)).should eq [\"Opening and ending tag mismatch: people line 1 and foo\"]\n        else\n          XML.parse(\"<people></foo>\", XML::ParserOptions[RECOVER, NOERROR]).errors.try(&.map(&.message)).should be_nil\n        end\n      end\n\n      it \"explicitly unset\" do\n        XML.parse(\"<people></foo>\", XML::ParserOptions[RECOVER]).errors.try(&.map(&.message)).should eq [\"Opening and ending tag mismatch: people line 1 and foo\"]\n      end\n    end\n  end\n\n  describe \"#namespace\" do\n    describe \"when the node has a namespace\" do\n      describe \"with a prefix\" do\n        it \"return the prefixed namespace\" do\n          doc = XML.parse(<<-XML)\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <openSearch:feed xmlns:foo=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\"></feed>\n            XML\n\n          namespace = doc.root.not_nil!.namespace.should be_a XML::Namespace\n          namespace.href.should eq \"http://a9.com/-/spec/opensearchrss/1.0/\"\n          namespace.prefix.should eq \"openSearch\"\n        end\n      end\n\n      describe \"with a default prefix\" do\n        it \"return the default namespace\" do\n          doc = XML.parse(<<-XML)\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <feed xmlns:foo=\"http://www.w3.org/2005/Atom\" xmlns=\"http://a9.com/-/spec/opensearchrss/1.0/\"></feed>\n            XML\n\n          namespace = doc.root.not_nil!.namespace.should be_a XML::Namespace\n          namespace.href.should eq \"http://a9.com/-/spec/opensearchrss/1.0/\"\n          namespace.prefix.should be_nil\n        end\n      end\n\n      describe \"without an explicit declaration on the node\" do\n        it \"returns the related namespace\" do\n          doc = XML.parse(<<-XML)\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:a=\"https://a-namespace\">\n              <name></name>\n              <a:age></a:age>\n            </feed>\n            XML\n\n          root = doc.root.not_nil!\n\n          namespace = root.children[1].namespace.should be_a XML::Namespace\n          namespace.href.should eq \"http://www.w3.org/2005/Atom\"\n          namespace.prefix.should be_nil\n\n          namespace = root.children[3].namespace.should be_a XML::Namespace\n          namespace.href.should eq \"https://a-namespace\"\n          namespace.prefix.should eq \"a\"\n        end\n      end\n    end\n\n    describe \"when the node does not have namespace\" do\n      it \"should return nil\" do\n        doc = XML.parse(<<-XML)\n          <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n          <feed></feed>\n          XML\n\n        doc.root.not_nil!.namespace.should be_nil\n      end\n    end\n\n    describe \"when the element does not have a namespace, but has namespace declarations\" do\n      it \"should return nil\" do\n        doc = XML.parse(<<-XML)\n          <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n          <feed xmlns:foo=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\"></feed>\n          XML\n\n        doc.root.not_nil!.namespace.should be_nil\n      end\n    end\n  end\n\n  describe \"#namespace_definitions\" do\n    it \"returns namespaces explicitly defined\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n          <item xmlns:c=\"http://c\"></item>\n        </feed>\n        XML\n\n      namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespace_definitions\n\n      namespaces.size.should eq(1)\n      namespaces[0].href.should eq(\"http://c\")\n      namespaces[0].prefix.should eq \"c\"\n    end\n\n    it \"returns an empty array if no namespaces are defined\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n          <item></item>\n        </feed>\n        XML\n\n      doc.root.not_nil!.first_element_child.not_nil!.namespace_definitions.should be_empty\n    end\n  end\n\n  describe \"#namespace_scopes\" do\n    it \"gets root namespaces scopes\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n        </feed>\n        XML\n\n      namespaces = doc.root.not_nil!.namespace_scopes\n\n      namespaces.size.should eq(2)\n      namespaces[0].href.should eq(\"http://www.w3.org/2005/Atom\")\n      namespaces[0].prefix.should be_nil\n      namespaces[1].href.should eq(\"http://a9.com/-/spec/opensearchrss/1.0/\")\n      namespaces[1].prefix.should eq(\"openSearch\")\n    end\n\n    it \"returns empty array if no namespaces scopes exists\" do\n      doc = XML.parse(<<-XML)\n        <?xml version='1.0' encoding='UTF-8'?>\n        <name>John</name>\n        XML\n\n      namespaces = doc.root.not_nil!.namespace_scopes\n\n      namespaces.size.should eq(0)\n    end\n\n    it \"includes parent namespaces\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n          <item xmlns:c=\"http://c\"></item>\n        </feed>\n        XML\n\n      namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespace_scopes\n\n      namespaces.size.should eq(3)\n      namespaces[0].href.should eq(\"http://c\")\n      namespaces[0].prefix.should eq \"c\"\n      namespaces[1].href.should eq(\"http://www.w3.org/2005/Atom\")\n      namespaces[1].prefix.should be_nil\n      namespaces[2].href.should eq(\"http://a9.com/-/spec/opensearchrss/1.0/\")\n      namespaces[2].prefix.should eq(\"openSearch\")\n    end\n  end\n\n  describe \"#namespaces\" do\n    it \"gets root namespaces as hash\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n        </feed>\n        XML\n\n      namespaces = doc.root.not_nil!.namespaces\n      namespaces.should eq({\n        \"xmlns\"            => \"http://www.w3.org/2005/Atom\",\n        \"xmlns:openSearch\" => \"http://a9.com/-/spec/opensearchrss/1.0/\",\n      })\n    end\n\n    it \"includes parent namespaces\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n          <item xmlns:c=\"http://c\"></item>\n        </feed>\n        XML\n\n      namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespaces\n      namespaces.should eq({\n        \"xmlns:c\"          => \"http://c\",\n        \"xmlns\"            => \"http://www.w3.org/2005/Atom\",\n        \"xmlns:openSearch\" => \"http://a9.com/-/spec/opensearchrss/1.0/\",\n      })\n    end\n\n    it \"returns an empty hash if there are no namespaces\" do\n      doc = XML.parse(<<-XML)\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <item></item>\n        </feed>\n        XML\n\n      namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespaces\n      namespaces.should eq({} of String => String?)\n    end\n  end\n\n  it \"reads big xml file (#1455)\" do\n    content = \".\" * 20_000\n    string = %(<?xml version=\"1.0\"?><root>#{content}</root>)\n    parsed = XML.parse(IO::Memory.new(string))\n    parsed.root.not_nil!.children[0].text.should eq(content)\n  end\n\n  it \"sets node text/content\" do\n    doc = XML.parse(<<-XML)\n      <?xml version='1.0' encoding='UTF-8'?>\n      <name>John</name>\n      XML\n\n    root = doc.root.not_nil!\n    root.text = \"Peter\"\n    root.text.should eq(\"Peter\")\n\n    root.content = \"Foo 👌\"\n    root.content.should eq(\"Foo 👌\")\n  end\n\n  it \"doesn't set invalid node content\" do\n    doc = XML.parse(<<-XML)\n      <?xml version='1.0' encoding='UTF-8'?>\n      <name>John</name>\n      XML\n\n    root = doc.root.not_nil!\n    expect_raises(Exception, \"Cannot escape\") do\n      root.content = \"\\0\"\n    end\n  end\n\n  it \"escapes content\" do\n    doc = XML.parse(<<-XML)\n      <?xml version='1.0' encoding='UTF-8'?>\n      <name>John</name>\n      XML\n\n    root = doc.root.not_nil!\n    root.text = \"<foo>\"\n    root.text.should eq(\"<foo>\")\n\n    assert_prints root.to_xml, %(<name>&lt;foo&gt;</name>)\n  end\n\n  it \"escapes content HTML fragment\" do\n    doc = XML.parse_html(<<-XML, XML::HTMLParserOptions.default | XML::HTMLParserOptions::NOIMPLIED | XML::HTMLParserOptions::NODEFDTD)\n      <p>foo</p>\n      XML\n\n    node = doc.children.first\n    node.text = \"<foo>\"\n    node.text.should eq(\"<foo>\")\n\n    assert_prints node.to_xml, %(<p>&lt;foo&gt;</p>)\n  end\n\n  it \"parses HTML UTF-8 from memory (#13703)\" do\n    doc = XML.parse_html(\"<p>České psaní</p>\")\n\n    node = doc.root.try(&.children.first).should_not be_nil\n\n    node.text.should eq \"České psaní\"\n  end\n\n  it \"parses HTML UTF-8 from IO (#13703)\" do\n    doc = XML.parse_html(IO::Memory.new(\"<p>České psaní</p>\"))\n\n    node = doc.root.try(&.children.first).should_not be_nil\n\n    node.text.should eq \"České psaní\"\n  end\n\n  it \"parses XML UTF-8 from memory (#13703)\" do\n    doc = XML.parse(\"<p>České psaní</p>\")\n\n    node = doc.root.try(&.children.first).should_not be_nil\n\n    node.text.should eq \"České psaní\"\n  end\n\n  it \"parses XML UTF-8 from IO (#13703)\" do\n    doc = XML.parse(IO::Memory.new(\"<p>České psaní</p>\"))\n\n    node = doc.root.try(&.children.first).should_not be_nil\n\n    node.text.should eq \"České psaní\"\n  end\n\n  it \"gets empty content\" do\n    doc = XML.parse(\"<foo/>\")\n    doc.children.first.content.should eq(\"\")\n  end\n\n  it \"sets node name\" do\n    doc = XML.parse(<<-XML\n      <?xml version='1.0' encoding='UTF-8'?>\n      <name>John</name>\n      XML\n    )\n    root = doc.root.not_nil!\n    root.name = \"last-name\"\n    root.name.should eq(\"last-name\")\n  end\n\n  it \"doesn't set invalid node name\" do\n    doc = XML.parse(<<-XML\n      <?xml version='1.0' encoding='UTF-8'?>\n      <name>John</name>\n      XML\n    )\n    root = doc.root.not_nil!\n\n    expect_raises(XML::Error, \"Invalid node name\") do\n      root.name = \" foo bar\"\n    end\n\n    expect_raises(XML::Error, \"Invalid node name\") do\n      root.name = \"foo bar\"\n    end\n\n    expect_raises(XML::Error, \"Invalid node name\") do\n      root.name = \"1foo\"\n    end\n\n    expect_raises(XML::Error, \"Invalid node name\") do\n      root.name = \"\\0foo\"\n    end\n  end\n\n  it \"gets encoding\" do\n    doc = XML.parse(<<-XML\n        <?xml version='1.0' encoding='UTF-8'?>\n        <people>\n        </people>\n        XML\n    )\n    doc.encoding.should eq(\"UTF-8\")\n  end\n\n  it \"gets encoding when nil\" do\n    doc = XML.parse(<<-XML\n        <?xml version='1.0'>\n        <people>\n        </people>\n        XML\n    )\n    doc.encoding.should be_nil\n  end\n\n  it \"gets version\" do\n    doc = XML.parse(<<-XML\n        <?xml version='1.0' encoding='UTF-8'?>\n        <people>\n        </people>\n        XML\n    )\n    doc.version.should eq(\"1.0\")\n  end\n\n  it \"unlinks nodes\" do\n    xml = <<-XML\n        <person id=\"1\">\n          <firstname>Jane</firstname>\n          <lastname>Doe</lastname>\n        </person>\n        XML\n    document = XML.parse(xml)\n\n    node = document.xpath_node(\"//lastname\").not_nil!\n    node.unlink\n\n    document.xpath_node(\"//lastname\").should be_nil\n  end\n\n  it \"does to_s with correct encoding (#2319)\" do\n    xml_str = <<-XML\n    <?xml version='1.0' encoding='UTF-8'?>\n    <person>\n      <name>たろう</name>\n    </person>\n    XML\n\n    doc = XML.parse(xml_str)\n    doc.root.to_s.should eq(\"<person>\\n  <name>たろう</name>\\n</person>\")\n  end\n\n  it \"sets an attribute\" do\n    doc = XML.parse(%{<foo />})\n    root = doc.root.not_nil!\n\n    root[\"bar\"] = \"baz\"\n    root[\"bar\"].should eq(\"baz\")\n    root.to_s.should eq(%{<foo bar=\"baz\"/>})\n  end\n\n  it \"changes an attribute\" do\n    doc = XML.parse(%{<foo bar=\"baz\"></foo>})\n    root = doc.root.not_nil!\n\n    root[\"bar\"] = \"baz\"\n    root[\"bar\"].should eq(\"baz\")\n    root.to_s.should eq(%{<foo bar=\"baz\"/>})\n\n    root[\"bar\"] = 1\n    root[\"bar\"].should eq(\"1\")\n  end\n\n  it \"deletes an attribute\" do\n    doc = XML.parse(%{<foo bar=\"baz\"></foo>})\n    root = doc.root.not_nil!\n\n    res = root.delete(\"bar\")\n    root[\"bar\"]?.should be_nil\n    root.to_s.should eq(%{<foo/>})\n    res.should eq \"baz\"\n\n    res = root.delete(\"biz\")\n    res.should be_nil\n  end\n\n  it \"shows content when inspecting attribute\" do\n    doc = XML.parse(%{<foo bar=\"baz\"></foo>})\n    attr = doc.root.not_nil!.attributes.first\n    attr.inspect.should contain(%(content=\"baz\"))\n  end\n\n  it \".build\" do\n    XML.build do |builder|\n      builder.element \"foo\" { }\n    end.should eq %[<?xml version=\"1.0\"?>\\n<foo/>\\n]\n  end\n\n  describe \".build_fragment\" do\n    it \"builds fragment without XML declaration\" do\n      XML.build_fragment do |builder|\n        builder.element \"foo\" { }\n      end.should eq %[<foo/>\\n]\n    end\n\n    it \"closes open elements\" do\n      XML.build_fragment do |builder|\n        builder.start_element \"foo\"\n        builder.start_element \"bar\"\n      end.should eq %[<foo><bar/></foo>\\n]\n    end\n  end\n\n  it \".libxml2_version\" do\n    XML.libxml2_version.should match /2\\.\\d+\\.\\d+/\n  end\nend\n"
  },
  {
    "path": "spec/std/xml/xpath_spec.cr",
    "content": "require \"spec\"\nrequire \"xml\"\n\nprivate def doc\n  XML.parse(%(\\\n    <?xml version='1.0' encoding='UTF-8'?>\n    <people>\n      <person id=\"1\">\n        <name>John</name>\n      </person>\n      <person id=\"2\">\n        <name>Peter</name>\n      </person>\n    </people>\n    ))\nend\n\nmodule XML\n  describe XPathContext do\n    it \"finds nodes\" do\n      doc = doc()\n\n      nodes = doc.xpath(\"//people/person\").as(NodeSet)\n      nodes.size.should eq(2)\n\n      nodes[0].name.should eq(\"person\")\n      nodes[0][\"id\"].should eq(\"1\")\n\n      nodes[1].name.should eq(\"person\")\n      nodes[1][\"id\"].should eq(\"2\")\n\n      nodes = doc.xpath_nodes(\"//people/person\")\n      nodes.size.should eq(2)\n    end\n\n    it \"finds string\" do\n      doc = doc()\n\n      id = doc.xpath(\"string(//people/person[1]/@id)\").as(String)\n      id.should eq(\"1\")\n\n      id = doc.xpath_string(\"string(//people/person[1]/@id)\")\n      id.should eq(\"1\")\n    end\n\n    it \"finds number\" do\n      doc = doc()\n\n      count = doc.xpath(\"count(//people/person)\").as(Float64)\n      count.should eq(2)\n\n      count = doc.xpath_float(\"count(//people/person)\")\n      count.should eq(2)\n    end\n\n    it \"finds boolean\" do\n      doc = doc()\n\n      id = doc.xpath(\"boolean(//people/person[1]/@id)\").as(Bool)\n      id.should be_true\n\n      id = doc.xpath_bool(\"boolean(//people/person[1]/@id)\")\n      id.should be_true\n    end\n\n    it \"raises on invalid xpath\" do\n      expect_raises XML::Error do\n        doc = doc()\n        doc.xpath(\"coco()\")\n      end\n    end\n\n    it \"returns nil when xpath fails to match\" do\n      doc = doc()\n      doc.xpath_node(\"//invalid\").should be_nil\n    end\n\n    it \"returns nil when invalid fails to match\" do\n      doc = doc()\n      doc.xpath_node(\"//invalid/text()\").should be_nil\n    end\n\n    it \"finds with explicit namespace\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n        </feed>\n        ))\n      nodes = doc.xpath(\"//atom:feed\", namespaces: {\"atom\" => \"http://www.w3.org/2005/Atom\"}).as(NodeSet)\n      nodes.size.should eq(1)\n      nodes[0].name.should eq(\"feed\")\n      ns = nodes[0].namespace.not_nil!\n      ns.href.should eq(\"http://www.w3.org/2005/Atom\")\n      ns.prefix.should be_nil\n    end\n\n    it \"finds with implicit (root) namespaces\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <openSearch:feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n          <openSearch:something>\n          </openSearch:something>\n        </openSearch:feed>\n        ))\n      nodes = doc.xpath(\"//openSearch:feed/openSearch:something\").as(NodeSet)\n      nodes.size.should eq(1)\n      nodes[0].name.should eq(\"something\")\n      ns = nodes[0].namespace.not_nil!\n      ns.href.should eq(\"http://a9.com/-/spec/opensearchrss/1.0/\")\n      ns.prefix.should eq(\"openSearch\")\n    end\n\n    it \"finds with root namespaces\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n        </feed>\n        ))\n      nodes = doc.xpath(\"//xmlns:feed\", namespaces: doc.root.not_nil!.namespaces).as(NodeSet)\n      nodes.size.should eq(1)\n      nodes[0].name.should eq(\"feed\")\n      ns = nodes[0].namespace.not_nil!\n      ns.href.should eq(\"http://www.w3.org/2005/Atom\")\n      ns.prefix.should be_nil\n    end\n\n    it \"finds with root namespaces (using prefix)\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <openSearch:feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\">\n        </openSearch:feed>\n        ))\n      nodes = doc.xpath(\"//openSearch:feed\", namespaces: doc.root.not_nil!.namespaces).as(NodeSet)\n      nodes.size.should eq(1)\n      nodes[0].name.should eq(\"feed\")\n      ns = nodes[0].namespace.not_nil!\n      ns.href.should eq(\"http://a9.com/-/spec/opensearchrss/1.0/\")\n      ns.prefix.should eq(\"openSearch\")\n    end\n\n    it \"finds with variable binding\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <person id=\"1\"/>\n          <person id=\"2\"/>\n        </feed>\n        ))\n      nodes = doc.xpath(\"//feed/person[@id=$value]\", variables: {\"value\" => 2}).as(NodeSet)\n      nodes.size.should eq(1)\n      nodes[0][\"id\"].should eq(\"2\")\n    end\n\n    it \"finds with variable binding (bool)\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <person id=\"1\"/>\n          <person id=\"2\"/>\n        </feed>\n        ))\n      result = doc.xpath_bool(\"count(//feed/person[@id=$value]) = 1\", variables: {\"value\" => 2})\n      result.should be_true\n    end\n\n    it \"finds with variable binding (float)\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <person id=\"1\"/>\n          <person id=\"2\"/>\n        </feed>\n        ))\n      result = doc.xpath_float(\"count(//feed/person[@id=$value])\", variables: {\"value\" => 2})\n      result.should eq(1.0)\n    end\n\n    it \"finds with variable binding (nodes)\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <person id=\"1\"/>\n          <person id=\"2\"/>\n        </feed>\n        ))\n      nodes = doc.xpath_nodes(\"//feed/person[@id=$value]\", variables: {\"value\" => 2})\n      nodes.size.should eq(1)\n      nodes[0][\"id\"].should eq(\"2\")\n    end\n\n    it \"finds with variable binding (node)\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <person id=\"1\"/>\n          <person id=\"2\"/>\n        </feed>\n        ))\n      node = doc.xpath_node(\"//feed/person[@id=$value]\", variables: {\"value\" => 2}).not_nil!\n      node[\"id\"].should eq(\"2\")\n    end\n\n    it \"finds with variable binding (string)\" do\n      doc = XML.parse(%(\\\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <feed>\n          <person id=\"1\"/>\n          <person id=\"2\"/>\n        </feed>\n        ))\n      result = doc.xpath_string(\"string(//feed/person[@id=$value]/@id)\", variables: {\"value\" => 2})\n      result.should eq(\"2\")\n    end\n\n    it \"NodeSet#to_s\" do\n      doc = doc()\n      doc.xpath(\"//people/person\").to_s\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/any_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\nrequire \"json\"\n\nprivate def it_fetches_from_hash(key, *equivalent_keys)\n  it \"fetches #{key.class}\" do\n    any = YAML::Any.new({YAML::Any.new(key) => YAML::Any.new(\"bar\")})\n\n    any[key].raw.should eq(\"bar\")\n    any[YAML::Any.new(key)].raw.should eq(\"bar\")\n\n    equivalent_keys.each do |k|\n      any[k].raw.should eq(\"bar\")\n      # FIXME: Can't do `YAML::Any.new` with arbitrary number types (#11645)\n      if k.is_a?(YAML::Any::Type)\n        any[YAML::Any.new(k)].raw.should eq(\"bar\")\n      end\n    end\n\n    unless key.nil?\n      expect_raises(KeyError, %(Missing hash key: nil)) do\n        any[nil]\n      end\n\n      expect_raises(KeyError, %(Missing hash key: nil)) do\n        any[YAML::Any.new(nil)]\n      end\n    end\n\n    expect_raises(KeyError, %(Missing hash key: \"fox\")) do\n      any[\"fox\"]\n    end\n\n    expect_raises(KeyError, %(Missing hash key: \"fox\")) do\n      any[YAML::Any.new(\"fox\")]\n    end\n\n    expect_raises(KeyError, %(Missing hash key: 2)) do\n      any[2]\n    end\n\n    expect_raises(KeyError, %(Missing hash key: 2)) do\n      any[YAML::Any.new(2i64)]\n    end\n\n    expect_raises(KeyError, %(Missing hash key: 2)) do\n      any[2.0]\n    end\n\n    expect_raises(KeyError, %(Missing hash key: 2)) do\n      any[YAML::Any.new(2.0f64)]\n    end\n\n    expect_raises(KeyError, %(Missing hash key: 'c')) do\n      any['c']\n    end\n  end\nend\n\nprivate def it_fetches_from_hash?(key, *equivalent_keys)\n  it \"fetches #{key.class}\" do\n    any = YAML::Any.new({YAML::Any.new(key) => YAML::Any.new(\"bar\")})\n\n    any[key]?.try(&.raw).should eq(\"bar\")\n    any[YAML::Any.new(key)]?.try(&.raw).should eq(\"bar\")\n\n    equivalent_keys.each do |k|\n      any[k]?.try(&.raw).should eq(\"bar\")\n      # FIXME: Can't do `YAML::Any.new` with arbitrary number types (#11645)\n      if k.is_a?(YAML::Any::Type)\n        any[YAML::Any.new(k)]?.try(&.raw).should eq(\"bar\")\n      end\n    end\n\n    unless key.nil?\n      any[nil]?.should be_nil\n      any[YAML::Any.new(nil)]?.should be_nil\n    end\n\n    any[\"fox\"]?.should be_nil\n    any[YAML::Any.new(\"fox\")]?.should be_nil\n    any[2]?.should be_nil\n    any[YAML::Any.new(2i64)]?.should be_nil\n    any[2.0]?.should be_nil\n    any[YAML::Any.new(2.0f64)]?.should be_nil\n\n    any['c']?.should be_nil\n  end\nend\n\ndescribe YAML::Any do\n  it \".new\" do\n    YAML::Any.new(nil).raw.should be_nil\n    YAML::Any.new(true).raw.should be_true\n    YAML::Any.new(1_i64).raw.should eq 1_i64\n    YAML::Any.new(1).raw.should eq 1\n    YAML::Any.new(1_u8).raw.should eq 1\n    YAML::Any.new(0.0).raw.should eq 0.0\n    YAML::Any.new(0.0_f32).raw.should eq 0.0\n    YAML::Any.new(\"foo\").raw.should eq \"foo\"\n    YAML::Any.new(Time.utc(2023, 7, 2)).raw.should eq Time.utc(2023, 7, 2)\n    YAML::Any.new(Bytes[1, 2, 3]).raw.should eq Bytes[1, 2, 3]\n    YAML::Any.new([] of YAML::Any).raw.should eq [] of YAML::Any\n    YAML::Any.new({} of YAML::Any => YAML::Any).raw.should eq({} of YAML::Any => YAML::Any)\n    YAML::Any.new(Set(YAML::Any).new).raw.should eq Set(YAML::Any).new\n  end\n\n  describe \"casts\" do\n    it \"gets nil\" do\n      YAML.parse(\"\").as_nil.should be_nil\n    end\n\n    it \"gets bool\" do\n      YAML.parse(\"true\").as_bool.should be_true\n      YAML.parse(\"false\").as_bool.should be_false\n      YAML.parse(\"true\").as_bool?.should be_true\n      YAML.parse(\"false\").as_bool?.should be_false\n      YAML.parse(\"2\").as_bool?.should be_nil\n    end\n\n    it \"gets string\" do\n      YAML.parse(\"hello\").as_s.should eq(\"hello\")\n      YAML.parse(\"hello\").as_s?.should eq(\"hello\")\n      YAML.parse(\"hello:\\n- cruel\\n- world\\n\").as_s?.should be_nil\n    end\n\n    it \"gets array\" do\n      YAML.parse(\"- foo\\n- bar\\n\").as_a.should eq([\"foo\", \"bar\"])\n      YAML.parse(\"- foo\\n- bar\\n\").as_a?.should eq([\"foo\", \"bar\"])\n      YAML.parse(\"hello\").as_a?.should be_nil\n    end\n\n    it \"gets hash\" do\n      YAML.parse(\"foo: bar\").as_h.should eq({\"foo\" => \"bar\"})\n      YAML.parse(\"foo: bar\").as_h?.should eq({\"foo\" => \"bar\"})\n      YAML.parse(\"foo: bar\")[\"foo\"].as_h?.should be_nil\n    end\n\n    it \"gets int32\" do\n      value = YAML.parse(\"1\").as_i\n      value.should eq(1)\n      value.should be_a(Int32)\n\n      value = YAML.parse(\"1\").as_i?\n      value.should eq(1)\n      value.should be_a(Int32)\n\n      value = YAML.parse(\"true\").as_i?\n      value.should be_nil\n    end\n\n    it \"gets int64\" do\n      value = YAML.parse(\"1\").as_i64\n      value.should eq(1)\n      value.should be_a(Int64)\n\n      value = YAML.parse(\"1\").as_i64?\n      value.should eq(1)\n      value.should be_a(Int64)\n\n      value = YAML.parse(\"true\").as_i64?\n      value.should be_nil\n    end\n\n    it \"gets float32\" do\n      value = YAML.parse(\"1.2\").as_f32\n      value.should eq(1.2_f32)\n      value.should be_a(Float32)\n\n      expect_raises(TypeCastError) { YAML.parse(\"true\").as_f32 }\n\n      value = YAML.parse(\"1.2\").as_f32?\n      value.should eq(1.2_f32)\n      value.should be_a(Float32)\n\n      value = YAML.parse(\"true\").as_f32?\n      value.should be_nil\n    end\n\n    it \"gets float32 from JSON integer (#8618)\" do\n      value = YAML.parse(\"123\").as_f32\n      value.should eq(123.0)\n      value.should be_a(Float32)\n\n      value = YAML.parse(\"123\").as_f32?\n      value.should eq(123.0)\n      value.should be_a(Float32)\n    end\n\n    it \"gets float64\" do\n      value = YAML.parse(\"1.2\").as_f\n      value.should eq(1.2)\n      value.should be_a(Float64)\n\n      expect_raises(TypeCastError) { YAML.parse(\"true\").as_f }\n\n      value = YAML.parse(\"1.2\").as_f?\n      value.should eq(1.2)\n      value.should be_a(Float64)\n\n      value = YAML.parse(\"true\").as_f?\n      value.should be_nil\n    end\n\n    it \"gets float64 from JSON integer (#8618)\" do\n      value = YAML.parse(\"123\").as_f\n      value.should eq(123.0)\n      value.should be_a(Float64)\n\n      value = YAML.parse(\"123\").as_f?\n      value.should eq(123.0)\n      value.should be_a(Float64)\n    end\n\n    it \"gets time\" do\n      value = YAML.parse(\"2010-01-02\").as_time\n      value.should eq(Time.utc(2010, 1, 2))\n\n      value = YAML.parse(\"2010-01-02\").as_time?\n      value.should eq(Time.utc(2010, 1, 2))\n\n      value = YAML.parse(\"hello\").as_time?\n      value.should be_nil\n    end\n\n    it \"gets bytes\" do\n      value = YAML.parse(\"!!binary aGVsbG8=\").as_bytes\n      value.should eq(\"hello\".to_slice)\n\n      value = YAML.parse(\"!!binary aGVsbG8=\").as_bytes?\n      value.should eq(\"hello\".to_slice)\n\n      value = YAML.parse(\"1\").as_bytes?\n      value.should be_nil\n    end\n\n    it \"gets anchor\" do\n      value = YAML.parse(\"&foo bar\").as_s\n      value.should eq \"bar\"\n\n      value = YAML.parse(\"- &foo bar\\n- *foo\").as_a.map(&.as_s)\n      value.should eq [\"bar\", \"bar\"]\n\n      value = YAML.parse(\"foo: &foo\\n  bar: *foo\").as_h\n      foo = {YAML::Any.new(\"bar\") => YAML::Any.new(nil)}\n      foo[YAML::Any.new(\"bar\")] = YAML::Any.new(foo)\n      hash = YAML::Any.new({YAML::Any.new(\"foo\") => YAML::Any.new(foo)})\n      # FIXME: Using to_s here because comparison of recursive YAML structures seems to be broken.\n      value.to_s.should eq hash.to_s\n\n      expect_raises YAML::ParseException, \"Unknown anchor 'foo' at line 1, column 1\" do\n        YAML.parse(\"*foo\")\n      end\n    end\n\n    it \"splats anchor\" do\n      value = YAML::Any.from_yaml <<-YAML\n      map: &an\n        inner: 4\n      aliased: *an\n      splatted:\n        <<: *an\n        extra: 5\n      YAML\n      h = value[\"splatted\"].as_h\n      h.keys.should eq([\"inner\", \"extra\"])\n      h[\"inner\"].should eq(4)\n      h[\"extra\"].should eq(5)\n\n      h = value[\"aliased\"].as_h\n      h.keys.should eq([\"inner\"])\n      h[\"inner\"].should eq(4)\n    end\n\n    it \"gets yes/no unquoted booleans\" do\n      YAML.parse(\"yes\").as_bool.should be_true\n      YAML.parse(\"no\").as_bool.should be_false\n      YAML.parse(\"'yes'\").as_bool?.should be_nil\n      YAML.parse(\"'no'\").as_bool?.should be_nil\n      YAML::Any.from_yaml(\"yes\").as_bool.should be_true\n      YAML::Any.from_yaml(\"no\").as_bool.should be_false\n      YAML::Any.from_yaml(\"'yes'\").as_bool?.should be_nil\n      YAML::Any.from_yaml(\"'no'\").as_bool?.should be_nil\n    end\n\n    it \"doesn't get quoted numbers\" do\n      YAML.parse(\"1\").as_i64.should eq(1)\n      YAML.parse(\"'1'\").as_i64?.should be_nil\n      YAML.parse(\"'1'\").as_s?.should eq(\"1\")\n      YAML::Any.from_yaml(\"1\").as_i64.should eq(1)\n      YAML::Any.from_yaml(\"'1'\").as_i64?.should be_nil\n      YAML::Any.from_yaml(\"'1'\").as_s?.should eq(\"1\")\n    end\n  end\n\n  describe \"#size\" do\n    it \"of array\" do\n      YAML.parse(\"- foo\\n- bar\\n\").size.should eq(2)\n    end\n\n    it \"of hash\" do\n      YAML.parse(\"foo: bar\").size.should eq(1)\n    end\n  end\n\n  describe \"#[]\" do\n    it \"of array\" do\n      YAML.parse(\"- foo\\n- bar\\n\")[1].raw.should eq(\"bar\")\n\n      any = YAML::Any.new([YAML::Any.new(\"baz\"), YAML::Any.new(\"bar\")])\n\n      any[1i64].raw.should eq(\"bar\")\n      any[1i32].raw.should eq(\"bar\")\n      any[1u8].raw.should eq(\"bar\")\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[nil]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[YAML::Any.new(nil)]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[\"fox\"]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[YAML::Any.new(\"fox\")]\n      end\n\n      expect_raises(IndexError, %(Index out of bounds)) do\n        any[2]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[YAML::Any.new(2i64)]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[2.0f64]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[YAML::Any.new(2.0f64)]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[2.0f32]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any[YAML::Any.new(2.0f32)]\n      end\n\n      expect_raises(Exception, %(Expected int key for Array#[], not Array(YAML::Any))) do\n        any['c']\n      end\n    end\n\n    context \"hash\" do\n      it_fetches_from_hash nil\n      it_fetches_from_hash true\n      it_fetches_from_hash 1i64, 1.0f64, 1i32, 1u8, 1.0f32\n      it_fetches_from_hash 1.0f64, 1i64, 1i32, 1u8, 1.0f32\n      it_fetches_from_hash \"foo\"\n      it_fetches_from_hash Time.utc\n      it_fetches_from_hash \"foo\".to_slice\n      it_fetches_from_hash [YAML::Any.new(\"foo\")]\n      it_fetches_from_hash({YAML::Any.new(\"foo\") => YAML::Any.new(\"baz\")})\n      it_fetches_from_hash Set{YAML::Any.new(\"foo\")}\n    end\n  end\n\n  describe \"#[]?\" do\n    it \"of array\" do\n      YAML.parse(\"- foo\\n- bar\\n\")[1]?.not_nil!.raw.should eq(\"bar\")\n      YAML.parse(\"- foo\\n- bar\\n\")[3]?.should be_nil\n\n      any = YAML::Any.new([YAML::Any.new(\"baz\"), YAML::Any.new(\"bar\")])\n\n      any[1i64]?.try(&.raw).should eq(\"bar\")\n      any[1i32]?.try(&.raw).should eq(\"bar\")\n      any[1u8]?.try(&.raw).should eq(\"bar\")\n      any[1.0f64]?.try(&.raw).should be_nil\n      any[1.0f32]?.try(&.raw).should be_nil\n\n      any[nil]?.should be_nil\n      any[YAML::Any.new(nil)]?.should be_nil\n      any[\"fox\"]?.should be_nil\n      any[YAML::Any.new(\"fox\")]?.should be_nil\n      any[2]?.should be_nil\n      any[YAML::Any.new(2i64)]?.should be_nil\n      any[2.0f64]?.should be_nil\n      any[YAML::Any.new(2.0f64)]?.should be_nil\n      any[2.0f32]?.should be_nil\n      any[YAML::Any.new(2.0f32)]?.should be_nil\n      any['c']?.should be_nil\n    end\n\n    it \"of hash\" do\n      YAML.parse(\"foo: bar\")[\"foo\"]?.not_nil!.raw.should eq(\"bar\")\n      YAML.parse(\"foo: bar\")[\"fox\"]?.should be_nil\n    end\n\n    it \"of hash with integer keys\" do\n      YAML.parse(\"1: bar\")[1]?.not_nil!.raw.should eq(\"bar\")\n      YAML.parse(\"1: bar\")[2]?.should be_nil\n    end\n\n    context \"hash\" do\n      it_fetches_from_hash? nil\n      it_fetches_from_hash? true\n      it_fetches_from_hash? 1i64, 1.0\n      it_fetches_from_hash? 1.0, 1i64\n      it_fetches_from_hash? \"foo\"\n      it_fetches_from_hash? Time.utc\n      it_fetches_from_hash? \"foo\".to_slice\n      it_fetches_from_hash? [YAML::Any.new(\"foo\")]\n      it_fetches_from_hash?({YAML::Any.new(\"foo\") => YAML::Any.new(\"baz\")})\n      it_fetches_from_hash? Set{YAML::Any.new(\"foo\")}\n    end\n  end\n\n  describe \"#dig?\" do\n    it \"gets the value at given path given splat\" do\n      obj = YAML.parse(\"--- \\nfoo: \\n  bar: \\n    baz: \\n      - qux\\n      - fox\")\n\n      obj.dig?(\"foo\", \"bar\", \"baz\").should eq(%w(qux fox))\n      obj.dig?(\"foo\", \"bar\", \"baz\", 1).should eq(\"fox\")\n    end\n\n    it \"returns nil if not found\" do\n      obj = YAML.parse(\"--- \\nfoo: \\n  bar: \\n    baz: \\n      - qux\\n      - fox\")\n\n      obj.dig?(\"foo\", 10).should be_nil\n      obj.dig?(\"bar\", \"baz\").should be_nil\n      obj.dig?(\"\").should be_nil\n    end\n\n    it \"returns nil for non-Hash/Array intermediary values\" do\n      YAML::Any.new(nil).dig?(\"foo\").should be_nil\n      YAML::Any.new(0.0).dig?(\"foo\").should be_nil\n    end\n  end\n\n  describe \"dig\" do\n    it \"gets the value at given path given splat\" do\n      obj = YAML.parse(\"--- \\nfoo: \\n  bar: \\n    baz: \\n      - qux\\n      - fox\")\n\n      obj.dig(\"foo\", \"bar\", \"baz\").should eq(%w(qux fox))\n      obj.dig(\"foo\", \"bar\", \"baz\", 1).should eq(\"fox\")\n    end\n\n    it \"raises if not found\" do\n      obj = YAML.parse(\"--- \\nfoo: \\n  bar: \\n    baz: \\n      - qux\\n      - fox\")\n\n      expect_raises KeyError, %(Missing hash key: 1) do\n        obj.dig(\"foo\", 1, \"bar\", \"baz\")\n      end\n      expect_raises KeyError, %(Missing hash key: \"bar\") do\n        obj.dig(\"bar\", \"baz\")\n      end\n      expect_raises KeyError, %(Missing hash key: \"\") do\n        obj.dig(\"\")\n      end\n    end\n  end\n\n  it \"traverses big structure\" do\n    obj = YAML.parse(\"--- \\nfoo: \\n  bar: \\n    baz: \\n      - qux\\n      - fox\")\n    obj[\"foo\"][\"bar\"][\"baz\"][1].as_s.should eq(\"fox\")\n  end\n\n  describe \"#==\" do\n    it \"compares to other objects\" do\n      obj = YAML.parse(\"- foo\\n- bar \\n\")\n      obj.should eq(%w(foo bar))\n      obj[0].should eq(\"foo\")\n    end\n\n    it \"compares with Set\" do\n      Set{1, 2, 3}.should eq YAML.parse(\"!!set { 1, 2, 3 }\")\n      YAML.parse(\"!!set { 1, 2, 3 }\").should eq Set{1, 2, 3}\n\n      Set{1, 2}.should_not eq YAML.parse(\"!!set { 1, 2, 3 }\")\n      YAML.parse(\"!!set { 1, 2 }\").should_not eq Set{1, 2, 3}\n    end\n  end\n\n  it \"returns array of any when doing parse all\" do\n    docs = YAML.parse_all(\"---\\nfoo\\n---\\nbar\\n\")\n    docs[0].as_s.should eq(\"foo\")\n    docs[1].as_s.should eq(\"bar\")\n  end\n\n  it \"can compare with ===\" do\n    (1 === YAML.parse(\"1\")).should be_truthy\n  end\n\n  it \"exposes $~ when doing Regex#===\" do\n    (/o+/ === YAML.parse(%(\"foo\"))).should be_truthy\n    $~[0].should eq(\"oo\")\n  end\n\n  it \"is enumerable\" do\n    nums = YAML.parse(\"[1, 2, 3]\")\n    nums.as_a.each_with_index do |x, i|\n      x.should be_a(YAML::Any)\n      x.raw.should eq(i + 1)\n    end\n  end\n\n  it \"dups\" do\n    any = YAML.parse(\"[1, 2, 3]\")\n    any2 = any.dup\n    any2.as_a.should_not be(any.as_a)\n  end\n\n  it \"clones\" do\n    any = YAML.parse(\"[[1], 2, 3]\")\n    any2 = any.clone\n    any2.as_a[0].as_a.should_not be(any.as_a[0].as_a)\n  end\n\n  it \"#to_json\" do\n    any = YAML.parse <<-YAML\n      foo: bar\n      baz: [1, 2.3, true, \"qux\", {\"qax\": \"qox\"}]\n      YAML\n    any.to_json.should eq %({\"foo\":\"bar\",\"baz\":[1,2.3,true,\"qux\",{\"qax\":\"qox\"}]})\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\nrequire \"spec/helpers/string\"\n\nprivate def assert_built(expected, expect_document_end = false, *, file = __FILE__, line = __LINE__, &)\n  if expect_document_end\n    expected = append_stream_end_indicator(expected)\n  end\n\n  assert_prints YAML.build { |yaml| with yaml yield yaml }, expected, file: file, line: line\nend\n\nprivate def append_stream_end_indicator(yaml)\n  # libyaml 0.2.1 removed the erroneously written document end marker (`...`) after some scalars in root context (see https://github.com/yaml/libyaml/pull/18).\n  # Earlier libyaml releases still write the document end marker and this is hard to fix on Crystal's side.\n  # So we just ignore it and adopt the specs accordingly to coincide with the used libyaml version.\n  if YAML.libyaml_version < SemanticVersion.new(0, 2, 1)\n    yaml + \"...\\n\"\n  else\n    yaml\n  end\nend\n\nprivate def build_stream(&)\n  String.build do |io|\n    YAML::Builder.build(io) do |yaml|\n      yaml.stream do\n        yield yaml\n      end\n    end\n  end\nend\n\ndescribe YAML::Builder do\n  describe \"#document\" do\n    describe \"implicit_start_indicator\" do\n      it \"explicit\" do\n        build_stream do |yaml|\n          yaml.document(implicit_start_indicator: false) do\n            yaml.scalar(1)\n          end\n        end.should eq append_stream_end_indicator(\"--- 1\\n\")\n\n        build_stream do |yaml|\n          yaml.document(implicit_start_indicator: false) do\n            yaml.scalar(1)\n          end\n          yaml.document(implicit_start_indicator: false) do\n            yaml.scalar(2)\n          end\n        end.should eq append_stream_end_indicator(\"--- 1\\n--- 2\\n\")\n      end\n\n      it \"implicit\" do\n        build_stream do |yaml|\n          yaml.document(implicit_start_indicator: true) do\n            yaml.scalar(1)\n          end\n        end.should eq append_stream_end_indicator(\"1\\n\")\n\n        build_stream do |yaml|\n          yaml.document(implicit_start_indicator: true) do\n            yaml.scalar(1)\n          end\n          yaml.document(implicit_start_indicator: true) do\n            yaml.scalar(2)\n          end\n        end.should eq append_stream_end_indicator(\"1\\n--- 2\\n\")\n      end\n    end\n  end\n\n  it \"writes scalar\" do\n    assert_built(\"--- 1\\n\", expect_document_end: true) do\n      scalar(1)\n    end\n  end\n\n  it \"writes alias\" do\n    assert_built(\"--- *key\\n\") do\n      itself.alias \"key\"\n    end\n  end\n\n  it \"writes scalar with style\" do\n    assert_built(%(--- \"1\"\\n)) do\n      scalar(1, style: YAML::ScalarStyle::DOUBLE_QUOTED)\n    end\n  end\n\n  it \"writes scalar with tag\" do\n    assert_built(%(--- !foo 1\\n), expect_document_end: true) do\n      scalar(1, tag: \"!foo\")\n    end\n  end\n\n  it \"writes scalar with anchor\" do\n    assert_built(%(--- &foo 1\\n), expect_document_end: true) do\n      scalar(1, anchor: \"foo\")\n    end\n  end\n\n  it \"writes sequence\" do\n    assert_built(\"---\\n- 1\\n- 2\\n- 3\\n\") do\n      sequence do\n        scalar(1)\n        scalar(2)\n        scalar(3)\n      end\n    end\n  end\n\n  it \"writes sequence with tag\" do\n    assert_built(\"--- !foo\\n- 1\\n- 2\\n- 3\\n\") do\n      sequence(tag: \"!foo\") do\n        scalar(1)\n        scalar(2)\n        scalar(3)\n      end\n    end\n  end\n\n  it \"writes sequence with anchor\" do\n    assert_built(\"--- &foo\\n- 1\\n- 2\\n- 3\\n\") do\n      sequence(anchor: \"foo\") do\n        scalar(1)\n        scalar(2)\n        scalar(3)\n      end\n    end\n  end\n\n  it \"writes sequence with style\" do\n    assert_built(\"--- [1, 2, 3]\\n\") do\n      sequence(style: YAML::SequenceStyle::FLOW) do\n        scalar(1)\n        scalar(2)\n        scalar(3)\n      end\n    end\n  end\n\n  it \"writes mapping\" do\n    assert_built(\"---\\nfoo: 1\\nbar: 2\\n\") do\n      mapping do\n        scalar(\"foo\")\n        scalar(1)\n        scalar(\"bar\")\n        scalar(2)\n      end\n    end\n  end\n\n  it \"writes mapping with tag\" do\n    assert_built(\"--- !foo\\nfoo: 1\\nbar: 2\\n\") do\n      mapping(tag: \"!foo\") do\n        scalar(\"foo\")\n        scalar(1)\n        scalar(\"bar\")\n        scalar(2)\n      end\n    end\n  end\n\n  it \"writes mapping with anchor\" do\n    assert_built(\"--- &foo\\nfoo: 1\\nbar: 2\\n\") do\n      mapping(anchor: \"foo\") do\n        scalar(\"foo\")\n        scalar(1)\n        scalar(\"bar\")\n        scalar(2)\n      end\n    end\n  end\n\n  it \"writes mapping with alias\" do\n    assert_built(\"---\\nfoo: *bar\\n\") do\n      mapping do\n        scalar \"foo\"\n        itself.alias \"bar\"\n      end\n    end\n  end\n\n  it \"writes mapping with merge\" do\n    assert_built(\"---\\n<<: *key\\n\") do\n      mapping do\n        merge \"key\"\n      end\n    end\n  end\n\n  it \"writes mapping with style\" do\n    assert_built(\"--- {foo: 1, bar: 2}\\n\") do\n      mapping(style: YAML::MappingStyle::FLOW) do\n        scalar(\"foo\")\n        scalar(1)\n        scalar(\"bar\")\n        scalar(2)\n      end\n    end\n  end\n\n  it \"errors on max nesting (sequence)\" do\n    io = IO::Memory.new\n    builder = YAML::Builder.new(io)\n    builder.max_nesting = 3\n    builder.start_stream\n    builder.start_document\n    3.times do\n      builder.start_sequence\n    end\n\n    expect_raises(YAML::Error, \"Nesting of 4 is too deep\") do\n      builder.start_sequence\n    end\n  end\n\n  it \"errors on max nesting (mapping)\" do\n    io = IO::Memory.new\n    builder = YAML::Builder.new(io)\n    builder.max_nesting = 3\n    builder.start_stream\n    builder.start_document\n    3.times do\n      builder.start_mapping\n    end\n\n    expect_raises(YAML::Error, \"Nesting of 4 is too deep\") do\n      builder.start_mapping\n    end\n  end\n\n  it \".build (with block)\" do\n    String.build do |io|\n      YAML::Builder.build(io) do |builder|\n        builder.stream do\n          builder.document do\n            builder.scalar(1)\n          end\n        end\n      end\n    end.should eq 1.to_yaml\n  end\n\n  it \"errors on invalid state\" do\n    String.build do |io|\n      YAML::Builder.build(io) do |builder|\n        expect_raises YAML::Error, \"Error emitting document_end: expected STREAM-START\" do\n          builder.end_document\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/nodes/builder_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\n\nprivate def assert_built(expected, expect_document_end = false, &)\n  # libyaml 0.2.1 removed the erroneously written document end marker (`...`) after some scalars in root context (see https://github.com/yaml/libyaml/pull/18).\n  # Earlier libyaml releases still write the document end marker and this is hard to fix on Crystal's side.\n  # So we just ignore it and adopt the specs accordingly to coincide with the used libyaml version.\n  if expect_document_end\n    if YAML.libyaml_version < SemanticVersion.new(0, 2, 1)\n      expected += \"...\\n\"\n    end\n  end\n\n  nodes_builder = YAML::Nodes::Builder.new\n\n  with nodes_builder yield nodes_builder\n\n  string = YAML.build do |builder|\n    nodes_builder.document.to_yaml builder\n  end\n  string.should eq(expected)\nend\n\ndescribe YAML::Nodes::Builder do\n  describe \"#alias\" do\n    describe \"as a scalar value\" do\n      it \"writes correctly\" do\n        assert_built(\"--- *key\\n\") do\n          itself.alias \"key\"\n        end\n      end\n    end\n\n    describe \"within a mapping\" do\n      it \"writes correctly\" do\n        assert_built(\"---\\nfoo: *bar\\n\") do\n          mapping do\n            scalar \"foo\"\n            itself.alias \"bar\"\n          end\n        end\n      end\n    end\n  end\n\n  describe \"#merge\" do\n    describe \"within a mapping\" do\n      it \"writes correctly\" do\n        assert_built(\"---\\n<<: *bar\\n\") do\n          mapping do\n            merge \"bar\"\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/nodes/parser_spec.cr",
    "content": "require \"yaml\"\nrequire \"spec\"\n\ndescribe YAML::Nodes do\n  describe \".parse\" do\n    it \"attaches location to scalar nodes\" do\n      doc = YAML::Nodes.parse %(1)\n      node = doc.nodes[0]\n      node.location.should eq({1, 1})\n      node.end_line.should eq(1)\n      node.end_column.should eq(2)\n    end\n\n    it \"attaches location to sequence nodes\" do\n      doc = YAML::Nodes.parse %([1])\n      node = doc.nodes[0]\n      node.location.should eq({1, 1})\n      node.end_line.should eq(1)\n      node.end_column.should eq(4)\n    end\n\n    it \"attaches location to mapping nodes\" do\n      doc = YAML::Nodes.parse %({\"a\":1})\n      node = doc.nodes[0]\n      node.location.should eq({1, 1})\n      node.end_line.should eq(1)\n      node.end_column.should eq(8)\n    end\n\n    it \"attaches location to alias nodes\" do\n      doc = YAML::Nodes.parse %([&a 1, *a])\n      node = doc.nodes[0].as(YAML::Nodes::Sequence).nodes[1]\n      node.location.should eq({1, 8})\n      node.end_line.should eq(1)\n      node.end_column.should eq(10)\n    end\n  end\n\n  describe \".parse_all\" do\n    it \"returns all documents\" do\n      docs = YAML::Nodes.parse_all <<-YAML\n        ---\n        1\n        ---\n        [2]\n        YAML\n\n      docs.size.should eq(2)\n      docs[0].location.should eq({1, 1})\n      docs[0].end_line.should eq(3)\n      docs[0].end_column.should eq(1)\n      docs[1].location.should eq({3, 1})\n      docs[1].end_line.should eq(5)\n      docs[1].end_column.should eq(1)\n\n      node = docs[0].nodes[0].should be_a(YAML::Nodes::Scalar)\n      node.value.should eq(\"1\")\n      node.location.should eq({2, 1})\n      node.end_line.should eq(2)\n      node.end_column.should eq(2)\n\n      node = docs[1].nodes[0].should be_a(YAML::Nodes::Sequence)\n      node.nodes[0].should(be_a(YAML::Nodes::Scalar)).value.should eq(\"2\")\n      node.location.should eq({4, 1})\n      node.end_line.should eq(4)\n      node.end_column.should eq(4)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/schema/core_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\n\nprivate def it_parses(string, expected, file = __FILE__, line = __LINE__)\n  it \"parses #{string.inspect}\", file, line do\n    YAML::Schema::Core.parse(string).should eq(expected)\n  end\nend\n\nprivate def it_raises_on_parse(string, message, file = __FILE__, line = __LINE__)\n  it \"raises on parse #{string.inspect}\", file, line do\n    expect_raises(YAML::ParseException, message) do\n      YAML::Schema::Core.parse(string)\n    end\n  end\nend\n\nprivate def it_parses_scalar(string, expected, file = __FILE__, line = __LINE__)\n  it \"parses #{string.inspect}\", file, line do\n    YAML::Schema::Core.parse_scalar(string).should eq(expected)\n  end\nend\n\nprivate def it_parses_string(string, file = __FILE__, line = __LINE__)\n  it_parses_scalar(string, string, file, line)\nend\n\nprivate def it_parses_scalar_from_pull(string, expected, file = __FILE__, line = __LINE__)\n  it_parses_scalar_from_pull(string, file, line) do |value|\n    value.should eq(expected)\n  end\nend\n\nprivate def it_parses_scalar_from_pull(string, file = __FILE__, line = __LINE__, &block : YAML::Any::Type ->)\n  it \"parses #{string.inspect}\", file, line do\n    pull = YAML::PullParser.new(%(value: #{string}))\n    pull.read_stream_start\n    pull.read_document_start\n    pull.read_mapping_start\n    pull.read_scalar # key\n\n    block.call(YAML::Schema::Core.parse_scalar(pull).as(YAML::Any::Type))\n  end\nend\n\nprivate def parse_first_node(content)\n  parser = YAML::Nodes::Parser.new(%(value: #{content}))\n  parser.parse.nodes.first.as(YAML::Nodes::Mapping).nodes[1]\nend\n\ndescribe YAML::Schema::Core do\n  # nil\n  it_parses_scalar \"~\", nil\n  it_parses_scalar \"null\", nil\n  it_parses_scalar \"Null\", nil\n  it_parses_scalar \"NULL\", nil\n\n  # true\n  it_parses_scalar \"yes\", true\n  it_parses_scalar \"Yes\", true\n  it_parses_scalar \"YES\", true\n  it_parses_scalar \"true\", true\n  it_parses_scalar \"True\", true\n  it_parses_scalar \"TRUE\", true\n  it_parses_scalar \"on\", true\n  it_parses_scalar \"On\", true\n  it_parses_scalar \"ON\", true\n\n  # false\n  it_parses_scalar \"no\", false\n  it_parses_scalar \"No\", false\n  it_parses_scalar \"NO\", false\n  it_parses_scalar \"false\", false\n  it_parses_scalar \"False\", false\n  it_parses_scalar \"FALSE\", false\n  it_parses_scalar \"off\", false\n  it_parses_scalar \"Off\", false\n  it_parses_scalar \"OFF\", false\n\n  # +infinity\n  it_parses_scalar \".inf\", Float64::INFINITY\n  it_parses_scalar \".Inf\", Float64::INFINITY\n  it_parses_scalar \".INF\", Float64::INFINITY\n  it_parses_scalar \"+.inf\", Float64::INFINITY\n  it_parses_scalar \"+.Inf\", Float64::INFINITY\n  it_parses_scalar \"+.INF\", Float64::INFINITY\n\n  # -infinity\n  it_parses_scalar \"-.inf\", -Float64::INFINITY\n  it_parses_scalar \"-.Inf\", -Float64::INFINITY\n  it_parses_scalar \"-.INF\", -Float64::INFINITY\n\n  # nan\n  it \"parses nan\" do\n    {\".nan\", \".NaN\", \".NAN\"}.each do |string|\n      value = YAML::Schema::Core.parse_scalar(string)\n      value.as(Float64).nan?.should be_true\n    end\n  end\n\n  # integer (base 10)\n  it_parses_scalar \"0\", 0\n  it_parses_scalar \"123\", 123\n  it_parses_scalar \"+123\", 123\n  it_parses_scalar \"-123\", -123\n\n  # integer (binary)\n  it_parses_scalar \"0b0\", 0\n  it_parses_scalar \"0b10110\", 0b10110\n\n  # integer (octal)\n  it_parses_scalar \"00\", 0\n  it_parses_scalar \"0o0\", 0\n  it_parses_scalar \"0o123\", 0o123\n  it_parses_scalar \"0755\", 0o755\n\n  # integer (hex)\n  it_parses_scalar \"0x0\", 0\n  it_parses_scalar \"0x123abc\", 0x123abc\n  it_parses_scalar \"-0x123abc\", -0x123abc\n\n  # float\n  it_parses_scalar \"1.2\", 1.2\n  it_parses_scalar \"0.815\", 0.815\n  it_parses_scalar \"0.\", 0.0\n  it_parses_scalar \"-0.0\", 0.0\n  it_parses_scalar \"1_234.2\", 1_234.2\n  it_parses_scalar \"-2E+05\", -2e05\n  it_parses_scalar \"+12.3\", 12.3\n  it_parses_scalar \".5\", 0.5\n  it_parses_scalar \"+.5\", 0.5\n  it_parses_scalar \"-.5\", -0.5\n\n  # time\n  it_parses_scalar \"2002-12-14\", Time.utc(2002, 12, 14)\n  it_parses_scalar \"2002-1-2\", Time.utc(2002, 1, 2)\n  it_parses_scalar \"2002-1-2T10:11:12\", Time.utc(2002, 1, 2, 10, 11, 12)\n  it_parses_scalar \"2002-1-2   10:11:12\", Time.utc(2002, 1, 2, 10, 11, 12)\n  it_parses_scalar \"2002-1-2   1:11:12\", Time.utc(2002, 1, 2, 1, 11, 12)\n  it_parses_scalar \"2002-1-2T10:11:12.3\", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 300_000_000)\n  it_parses_scalar \"2002-1-2T10:11:12.34\", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 340_000_000)\n  it_parses_scalar \"2002-1-2T10:11:12.345\", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 345_000_000)\n  it_parses_scalar \"2002-1-2T10:11:12.3456\", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 345_600_000)\n  it_parses_scalar \"2002-1-2T10:11:12Z\", Time.utc(2002, 1, 2, 10, 11, 12)\n  it_parses_scalar \"2002-1-2T10:11:12 Z\", Time.utc(2002, 1, 2, 10, 11, 12)\n  it_parses_scalar \"2002-1-2T10:11:12 +3\", Time.utc(2002, 1, 2, 7, 11, 12)\n  it_parses_scalar \"2002-1-2T10:11:12 +03:00\", Time.utc(2002, 1, 2, 7, 11, 12)\n  it_parses_scalar \"2002-1-2T10:11:12 -03:00\", Time.utc(2002, 1, 2, 13, 11, 12)\n  it_parses_scalar \"2002-1-2T10:11:12 -03:31\", Time.utc(2002, 1, 2, 13, 42, 12)\n  it_parses_scalar \"2002-1-2T10:11:12-03:31\", Time.utc(2002, 1, 2, 13, 42, 12)\n  it_parses_scalar \"2002-1-2T10:11:12 +0300\", Time.utc(2002, 1, 2, 7, 11, 12)\n\n  # invalid time\n  it_parses_string \"2002-34-45\"\n  it_parses_string \"2002-12-14 x\"\n  it_parses_string \"2002-1-2T10:11:12x\"\n  it_parses_string \"2002-1-2T10:11:12Zx\"\n  it_parses_string \"2002-1-2T10:11:12+03x\"\n\n  # non-plain style\n  it_parses_scalar_from_pull %(\"1\"), \"1\"\n\n  # bools according to the spec, but parsed as strings in Python and Ruby,\n  # so we do the same in Crystal for \"compatibility\"\n  it_parses_scalar \"y\", \"y\"\n  it_parses_scalar \"Y\", \"Y\"\n  it_parses_scalar \"n\", \"n\"\n  it_parses_scalar \"N\", \"N\"\n\n  # !!map\n  it_parses \"!!map {1: 2}\", {1 => 2}\n  it_raises_on_parse \"!!map 1\", \"Expected MAPPING_START\"\n\n  # !!omap\n  it_parses \"!!omap {1: 2}\", {1 => 2}\n  it_raises_on_parse \"!!omap 1\", \"Expected MAPPING_START\"\n\n  # !!pairs\n  it_parses \"!!pairs [{1: 2}, {3: 4}]\", [{1 => 2}, {3 => 4}]\n  it_raises_on_parse \"!!pairs 1\", \"Expected SEQUENCE_START\"\n  it_raises_on_parse \"!!pairs [{1: 2, 3: 4}]\", \"Expected MAPPING_END\"\n\n  # !!set\n  it_parses \"!!set { 1, 2, 3 }\", Set{1, 2, 3}\n  it_raises_on_parse \"!!set 1\", \"Expected MAPPING_START\"\n\n  # !!seq\n  it_parses \"!!seq [ 1, 2, 3 ]\", [1, 2, 3]\n  it_raises_on_parse \"!!seq 1\", \"Expected SEQUENCE_START\"\n\n  # !!binary\n  it_parses \"!!binary aGVsbG8=\", \"hello\".to_slice\n  it_raises_on_parse \"!!binary [1]\", \"Expected SCALAR\"\n  it_raises_on_parse \"!!binary 1\", \"Error decoding Base64\"\n\n  # !!bool\n  it_parses \"!!bool yes\", true\n  it_raises_on_parse \"!!bool 1\", \"Invalid bool\"\n\n  # !!float\n  it_parses \"!!float '1.2'\", 1.2\n  it_parses \"!!float '0.5'\", 0.5\n  it_parses \"!!float '1_234.2'\", 1_234.2\n\n  it_parses \"!!float -1\", -1.0\n  it_parses \"!!float 0\", 0.0\n  it_parses \"!!float 2.3e4\", 2.3e4\n\n  it \"parses !!float .nan\" do\n    YAML::Schema::Core.parse(\"!!float .nan\").as_f.nan?.should be_true\n  end\n\n  it_parses \"!!float .inf\", Float64::INFINITY\n  it_raises_on_parse \"!!float 'hello'\", \"Invalid float\"\n\n  # !!int\n  it_parses \"!!int 0\", 0\n  it_parses \"!!int 123\", 123\n  it_parses \"!!int 0b10\", 0b10\n  it_parses \"!!int 0o123\", 0o123\n  it_parses \"!!int 0755\", 0o755\n  it_parses \"!!int 0xabc\", 0xabc\n  it_parses \"!!int -123\", -123\n  it_raises_on_parse \"!!int 'hello'\", \"Invalid int\"\n\n  # !!null\n  it_parses \"!!null ~\", nil\n  it_raises_on_parse \"!!null 1\", \"Invalid null\"\n\n  # !!str\n  it_parses \"!!str 1\", \"1\"\n  it_raises_on_parse \"!!str [1]\", \"Expected SCALAR\"\n\n  # # !!timestamp\n  it_parses \"!!timestamp 2010-01-02\", Time.utc(2010, 1, 2)\n  it_raises_on_parse \"!!timestamp foo\", \"Invalid timestamp\"\n\n  it \".parse_null_or\" do\n    YAML::Schema::Core.parse_null_or(parse_first_node(%())) { true }.should be_nil\n    YAML::Schema::Core.parse_null_or(parse_first_node(%(~))) { true }.should be_nil\n    YAML::Schema::Core.parse_null_or(parse_first_node(%(\"\"))) { true }.should be_true\n    YAML::Schema::Core.parse_null_or(parse_first_node(%(''))) { true }.should be_true\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/schema/fail_safe_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\n\nprivate def it_parses(string, expected, file = __FILE__, line = __LINE__)\n  it \"parses #{string.inspect}\", file, line do\n    YAML::Schema::FailSafe.parse(string).should eq(expected)\n  end\nend\n\nprivate def it_raises_on_parse(string, message, file = __FILE__, line = __LINE__)\n  it \"raises on parse #{string.inspect}\", file, line do\n    expect_raises(YAML::ParseException, message) do\n      YAML::Schema::FailSafe.parse(string)\n    end\n  end\nend\n\nprivate def it_parses_all(string, expected, file = __FILE__, line = __LINE__)\n  it \"parses all #{string.inspect}\", file, line do\n    YAML::Schema::FailSafe.parse_all(string).should eq(expected)\n  end\nend\n\nprivate def it_raises_on_parse_all(string, message, file = __FILE__, line = __LINE__)\n  it \"raises on parse all #{string.inspect}\", file, line do\n    expect_raises(YAML::ParseException, message) do\n      YAML::Schema::FailSafe.parse_all(string)\n    end\n  end\nend\n\ndescribe YAML::Schema::FailSafe do\n  # parse\n  it_parses \"123\", \"123\"\n  it_parses %(\n    context:\n        replace_me: \"Yes please!\"\n  ), {\"context\" => {\"replace_me\" => \"Yes please!\"}}\n  it_parses %(\n    first:\n      document:\n\n    second:\n      document:\n  ), {\"first\" => {\"document\" => \"\"}, \"second\" => {\"document\" => \"\"}}\n  it_raises_on_parse %(\n    this: \"gives\"\n      an: \"error\"\n  ), \"did not find expected key at line 3, column 7, while parsing a block mapping at line 2, column 5\"\n  it_raises_on_parse \":\", \"did not find expected key at line 1, column 1, while parsing a block mapping at line 1, column 1\"\n\n  # parse_all\n  it_parses \"321\", \"321\"\n  it_parses_all %(\n    context:\n        replace_me: \"Yes please!\"\n  ), [{\"context\" => {\"replace_me\" => \"Yes please!\"}}]\n  it_parses_all %(\n    foo:\n      bar: 123\n\n    bar:\n      foo: 321\n  ), [{\"foo\" => {\"bar\" => \"123\"}, \"bar\" => {\"foo\" => \"321\"}}]\n  it_raises_on_parse_all %(\n    this: \"raises\"\n      an: \"yaml\"\n        parse: \"exception\"\n  ), \"did not find expected key at line 3, column 7, while parsing a block mapping at line 2, column 5\"\n  it_raises_on_parse_all \":\", \"did not find expected key at line 1, column 1, while parsing a block mapping at line 1, column 1\"\nend\n"
  },
  {
    "path": "spec/std/yaml/serializable_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\nrequire \"../../support/finalize\"\n\nclass YAMLAttrValue(T)\n  include YAML::Serializable\n\n  property value : T\nend\n\nrecord YAMLAttrPoint, x : Int32, y : Int32 do\n  include YAML::Serializable\nend\n\nclass YAMLAttrEmptyClass\n  include YAML::Serializable\n\n  def initialize; end\nend\n\nclass YAMLAttrEmptyClassWithUnmapped\n  include YAML::Serializable\n  include YAML::Serializable::Unmapped\n\n  def initialize; end\nend\n\nclass YAMLAttrPerson\n  include YAML::Serializable\n\n  property name : String\n  property age : Int32?\n\n  def_equals name, age\n\n  def initialize(@name : String)\n  end\nend\n\nstruct YAMLAttrPersonWithThreeFieldInInitialize\n  include YAML::Serializable\n\n  property name : String\n  property bla : Int32\n  property age : Int32\n\n  def initialize(@name, @bla, @age)\n  end\nend\n\nclass StrictYAMLAttrPerson\n  include YAML::Serializable\n  include YAML::Serializable::Strict\n\n  property name : String\n  property age : Int32?\nend\n\nclass YAMLAttrPersonExtraFields\n  include YAML::Serializable\n  include YAML::Serializable::Unmapped\n\n  property name : String\n  property age : Int32?\nend\n\nclass YAMLAttrPersonEmittingNull\n  include YAML::Serializable\n\n  property name : String\n\n  @[YAML::Field(emit_null: true)]\n  property age : Int32?\nend\n\nstruct YAMLAttrPersonWithSelectiveSerialization\n  include YAML::Serializable\n\n  property name : String\n\n  @[YAML::Field(ignore_serialize: true)]\n  property password : String\n\n  @[YAML::Field(ignore_deserialize: true)]\n  property generated : String = \"generated-internally\"\n\n  def initialize(@name : String, @password : String)\n  end\nend\n\n@[YAML::Serializable::Options(emit_nulls: true)]\nclass YAMLAttrPersonEmittingNullsByOptions\n  include YAML::Serializable\n\n  property name : String\n  property age : Int32?\n  property value1 : Int32?\n\n  @[YAML::Field(emit_null: false)]\n  property value2 : Int32?\nend\n\nclass YAMLAttrWithTime\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::Format.new(\"%F %T\"))]\n  property value : Time\nend\n\nclass YAMLAttrWithNilableTime\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::Format.new(\"%F\"))]\n  property value : Time?\n\n  def initialize\n  end\nend\n\nclass YAMLAttrWithNilableTimeEmittingNull\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::Format.new(\"%F\"), emit_null: true)]\n  property value : Time?\n\n  def initialize\n  end\nend\n\nclass YAMLAttrWithTimeArray1\n  include YAML::Serializable\n\n  @[YAML::Field(converter: YAML::ArrayConverter(Time::EpochConverter))]\n  property value : Array(Time)\nend\n\nclass YAMLAttrWithTimeArray2\n  include YAML::Serializable\n\n  @[YAML::Field(converter: YAML::ArrayConverter.new(Time::EpochConverter))]\n  property value : Array(Time)\nend\n\nclass YAMLAttrWithTimeArray3\n  include YAML::Serializable\n\n  @[YAML::Field(converter: YAML::ArrayConverter.new(Time::Format.new(\"%F %T\")))]\n  property value : Array(Time)\nend\n\nmodule YAMLAttrPointConverter\n  def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node)\n    YAMLAttrPoint.new(ctx, node)\n  end\n\n  def self.to_yaml(value, yaml : YAML::Nodes::Builder)\n    value.to_yaml(yaml)\n  end\nend\n\nclass YAMLAttrWithSerializableArray\n  include YAML::Serializable\n\n  @[YAML::Field(converter: YAML::ArrayConverter(YAMLAttrPointConverter))]\n  property value : Array(YAMLAttrPoint)\nend\n\nclass YAMLAttrWithSimpleMapping\n  include YAML::Serializable\n\n  property name : String\n  property age : Int32\nend\n\nclass YAMLAttrWithKeywordsMapping\n  include YAML::Serializable\n\n  property end : Int32\n  property abstract : Int32\nend\n\nclass YAMLAttrWithProblematicKeys\n  include YAML::Serializable\n\n  property key : Int32\n  property pull : Int32\nend\n\nclass YAMLAttrRecursive\n  include YAML::Serializable\n\n  property name : String\n  property other : YAMLAttrRecursive\nend\n\nclass YAMLAttrRecursiveNilable\n  include YAML::Serializable\n  property name : String\n  property other : YAMLAttrRecursiveNilable?\nend\n\nclass YAMLAttrRecursiveArray\n  include YAML::Serializable\n  property name : String\n  property other : Array(YAMLAttrRecursiveArray)\nend\n\nclass YAMLAttrRecursiveHash\n  include YAML::Serializable\n  property name : String\n  property other : Hash(String, YAMLAttrRecursiveHash)\nend\n\nclass YAMLAttrWithDefaults\n  include YAML::Serializable\n\n  property a = 11\n  property b = \"Haha\"\n  property c = true\n  property d = false\n  property e : Bool? = false\n  property f : Int32? = 1\n  property g : Int32?\n  property h = [1, 2, 3]\nend\n\nclass YAMLAttrWithSmallIntegers\n  include YAML::Serializable\n\n  property foo : Int16\n  property bar : Int8\nend\n\nclass YAMLAttrWithTimeEpoch\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::EpochConverter)]\n  property value : Time\nend\n\nclass YAMLAttrNilableWithTimeEpoch\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::EpochConverter)]\n  property value : Time?\nend\n\nclass YAMLAttrDefaultWithTimeEpoch\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::EpochConverter)]\n  property value : Time = Time.unix(0)\nend\n\nclass YAMLAttrWithTimeEpochMillis\n  include YAML::Serializable\n\n  @[YAML::Field(converter: Time::EpochMillisConverter)]\n  property value : Time\nend\n\nclass YAMLAttrWithPresence\n  include YAML::Serializable\n\n  @[YAML::Field(presence: true)]\n  property first_name : String?\n\n  @[YAML::Field(presence: true)]\n  property last_name : String?\n\n  @[YAML::Field(ignore: true)]\n  getter? first_name_present : Bool\n\n  @[YAML::Field(ignore: true)]\n  getter? last_name_present : Bool\nend\n\nclass YAMLAttrWithPresenceAndIgnoreSerialize\n  include YAML::Serializable\n\n  @[YAML::Field(presence: true, ignore_serialize: ignore_first_name?)]\n  property first_name : String?\n\n  @[YAML::Field(presence: true, ignore_serialize: last_name.nil? && !last_name_present?, emit_null: true)]\n  property last_name : String?\n\n  @[YAML::Field(ignore: true)]\n  getter? first_name_present : Bool = false\n\n  @[YAML::Field(ignore: true)]\n  getter? last_name_present : Bool = false\n\n  def initialize(@first_name : String? = nil, @last_name : String? = nil)\n  end\n\n  def ignore_first_name?\n    first_name.nil? || first_name == \"\"\n  end\nend\n\nclass YAMLAttrWithQueryAttributes\n  include YAML::Serializable\n\n  property? foo : Bool\n\n  @[YAML::Field(key: \"is_bar\", presence: true)]\n  property? bar : Bool = false\n\n  @[YAML::Field(ignore: true)]\n  getter? bar_present : Bool\nend\n\nprivate class YAMLAttrWithFinalize\n  include YAML::Serializable\n  include FinalizeCounter\n\n  property value : YAML::Any\n\n  @[YAML::Field(ignore: true)]\n  property key : String?\nend\n\nmodule YAMLAttrModule\n  property moo : Int32 = 10\nend\n\nclass YAMLAttrModuleTest\n  include YAMLAttrModule\n  include YAML::Serializable\n\n  @[YAML::Field(key: \"phoo\")]\n  property foo = 15\n\n  def initialize; end\n\n  def to_tuple\n    {@moo, @foo}\n  end\nend\n\nclass YAMLAttrModuleTest2 < YAMLAttrModuleTest\n  property bar : Int32\n\n  def initialize(@bar : Int32); end\n\n  def to_tuple\n    {@moo, @foo, @bar}\n  end\nend\n\nmodule YAMLAttrModuleWithSameNameClass\n  class YAMLAttrModuleWithSameNameClass\n  end\n\n  class Test\n    include YAML::Serializable\n\n    property foo = 42\n  end\nend\n\nstruct YAMLAttrWithGenericConverter(T)\n  include YAML::Serializable\n\n  @[YAML::Field(converter: T)]\n  property value : Time\nend\n\nabstract class YAMLShape\n  include YAML::Serializable\n\n  use_yaml_discriminator \"type\", {point: YAMLPoint, circle: YAMLCircle}\n\n  property type : String\nend\n\nclass YAMLPoint < YAMLShape\n  property x : Int32\n  property y : Int32\nend\n\nclass YAMLCircle < YAMLShape\n  property x : Int32\n  property y : Int32\n  property radius : Int32\nend\n\nmodule YAMLNamespace\n  struct FooRequest\n    include YAML::Serializable\n\n    getter foo : Foo\n    getter bar = Bar.new\n  end\n\n  struct Foo\n    include YAML::Serializable\n    getter id = \"id:foo\"\n  end\n\n  struct Bar\n    include YAML::Serializable\n    getter id = \"id:bar\"\n\n    def initialize # Allow for default value above\n    end\n  end\nend\n\nenum YAMLVariableDiscriminatorEnumFoo\n  Foo = 4\nend\n\nenum YAMLVariableDiscriminatorEnumFoo8 : UInt8\n  Foo = 1_8\nend\n\nclass YAMLVariableDiscriminatorValueType\n  include YAML::Serializable\n\n  use_yaml_discriminator \"type\", {\n                                         0 => YAMLVariableDiscriminatorNumber,\n    \"1\"                                    => YAMLVariableDiscriminatorString,\n    true                                   => YAMLVariableDiscriminatorBool,\n    YAMLVariableDiscriminatorEnumFoo::Foo  => YAMLVariableDiscriminatorEnum,\n    YAMLVariableDiscriminatorEnumFoo8::Foo => YAMLVariableDiscriminatorEnum8,\n  }\nend\n\nclass YAMLVariableDiscriminatorNumber < YAMLVariableDiscriminatorValueType\nend\n\nclass YAMLVariableDiscriminatorString < YAMLVariableDiscriminatorValueType\nend\n\nclass YAMLVariableDiscriminatorBool < YAMLVariableDiscriminatorValueType\nend\n\nclass YAMLVariableDiscriminatorEnum < YAMLVariableDiscriminatorValueType\nend\n\nclass YAMLVariableDiscriminatorEnum8 < YAMLVariableDiscriminatorValueType\nend\n\nclass YAMLStrictDiscriminator\n  include YAML::Serializable\n  include YAML::Serializable::Strict\n\n  property type : String\n\n  use_yaml_discriminator \"type\", {foo: YAMLStrictDiscriminatorFoo, bar: YAMLStrictDiscriminatorBar}\nend\n\nclass YAMLStrictDiscriminatorFoo < YAMLStrictDiscriminator\nend\n\nclass YAMLStrictDiscriminatorBar < YAMLStrictDiscriminator\n  property x : YAMLStrictDiscriminator\n  property y : YAMLStrictDiscriminator\nend\n\nclass YAMLSomething\n  include YAML::Serializable\n\n  property value : YAMLSomething?\nend\n\nmodule YAMLDiscriminatorBug\n  abstract class Base\n    include YAML::Serializable\n\n    use_yaml_discriminator(\"type\", {\"a\" => A, \"b\" => B, \"c\" => C})\n  end\n\n  class A < Base\n  end\n\n  class B < Base\n    property source : Base\n    property value : Int32 = 1\n  end\n\n  class C < B\n  end\nend\n\nclass YAMLInitializeOpts\n  include YAML::Serializable\n\n  property value : Int32\n\n  def initialize(**opts)\n    @value = opts.size\n  end\nend\n\nrecord Namespaced::YAML::Wrapper, name : String, options : Hash(String, ::YAML::Any::Type)? = nil do\n  include ::YAML::Serializable\nend\n\ndescribe \"YAML::Serializable\" do\n  it \"works with classes within `YAML` namespace\" do\n    Namespaced::YAML::Wrapper\n      .from_yaml(<<-YAML)\n          name: foo\n          options:\n            foo: true\n        YAML\n      .to_yaml\n  end\n\n  it \"works with record\" do\n    YAMLAttrPoint.new(1, 2).to_yaml.should eq \"---\\nx: 1\\ny: 2\\n\"\n    YAMLAttrPoint.from_yaml(\"---\\nx: 1\\ny: 2\\n\").should eq YAMLAttrPoint.new(1, 2)\n  end\n\n  it \"works with anchors of value types\" do\n    YAMLAttrPoint.from_yaml(<<-YAML).should eq YAMLAttrPoint.new(123, 123)\n      ---\n      x: &foo 123\n      y: *foo\n      YAML\n  end\n\n  it \"empty class\" do\n    e = YAMLAttrEmptyClass.new\n    e.to_yaml.should eq \"--- {}\\n\"\n    YAMLAttrEmptyClass.from_yaml(\"---\\n\")\n  end\n\n  it \"empty class with unmapped\" do\n    YAMLAttrEmptyClassWithUnmapped.from_yaml(\"---\\nname: John\\nage: 30\\n\").yaml_unmapped.should eq({\"name\" => \"John\", \"age\" => 30})\n  end\n\n  it \"parses person\" do\n    person = YAMLAttrPerson.from_yaml(\"---\\nname: John\\nage: 30\\n\")\n    person.should be_a(YAMLAttrPerson)\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n  end\n\n  it \"parses person without age\" do\n    person = YAMLAttrPerson.from_yaml(\"---\\nname: John\\n\")\n    person.should be_a(YAMLAttrPerson)\n    person.name.should eq(\"John\")\n    person.name.size.should eq(4) # This verifies that name is not nilable\n    person.age.should be_nil\n  end\n\n  it \"parses person with blank age\" do\n    person = YAMLAttrPerson.from_yaml(\"---\\nname: John\\nage:\\n\")\n    person.should be_a(YAMLAttrPerson)\n    person.name.should eq(\"John\")\n    person.name.size.should eq(4) # This verifies that name is not nilable\n    person.age.should be_nil\n  end\n\n  it \"parses array of people\" do\n    people = Array(YAMLAttrPerson).from_yaml(\"---\\n- name: John\\n- name: Doe\\n\")\n    people.size.should eq(2)\n    people[0].name.should eq(\"John\")\n    people[1].name.should eq(\"Doe\")\n  end\n\n  it \"parses array of people with merge\" do\n    yaml = <<-YAML\n      - &1\n        name: foo\n        age: 1\n      -\n        <<: *1\n        age: 2\n      YAML\n\n    people = Array(YAMLAttrPerson).from_yaml(yaml)\n    people[1].name.should eq(\"foo\")\n    people[1].age.should eq(2)\n  end\n\n  it \"parses array of people with merge, doesn't hang on infinite recursion\" do\n    yaml = <<-YAML\n      - &1\n        name: foo\n        <<: *1\n        <<: [ *1, *1 ]\n        age: 1\n      YAML\n\n    people = Array(YAMLAttrPerson).from_yaml(yaml)\n    people[0].name.should eq(\"foo\")\n    people[0].age.should eq(1)\n  end\n\n  it \"works with class with three fields\" do\n    person1 = YAMLAttrPersonWithThreeFieldInInitialize.from_yaml(\"---\\nname: John\\nbla: 1\\nage: 30\\n\")\n    person2 = YAMLAttrPersonWithThreeFieldInInitialize.new(\"John\", 1, 30)\n    person1.should eq person2\n  end\n\n  it \"parses person with unknown attributes\" do\n    person = YAMLAttrPerson.from_yaml(\"---\\nname: John\\nunknown: [1, 2, 3]\\nage: 30\\n\")\n    person.should be_a(YAMLAttrPerson)\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n  end\n\n  it \"parses strict person with unknown attributes\" do\n    ex = expect_raises YAML::ParseException, \"Unknown yaml attribute: foo\" do\n      StrictYAMLAttrPerson.from_yaml <<-YAML\n        ---\n        name: John\n        foo: [1, 2, 3]\n        age: 30\n        YAML\n    end\n    ex.location.should eq({3, 1})\n  end\n\n  it \"works with selective serialization\" do\n    person = YAMLAttrPersonWithSelectiveSerialization.new(\"Vasya\", \"P@ssw0rd\")\n    person.to_yaml.should eq \"---\\nname: Vasya\\ngenerated: generated-internally\\n\"\n\n    person_yaml = \"---\\nname: Vasya\\ngenerated: should not set\\npassword: update\\n\"\n    person = YAMLAttrPersonWithSelectiveSerialization.from_yaml(person_yaml)\n    person.generated.should eq \"generated-internally\"\n    person.password.should eq \"update\"\n  end\n\n  it \"does to_yaml\" do\n    person = YAMLAttrPerson.from_yaml(\"---\\nname: John\\nage: 30\\n\")\n    person2 = YAMLAttrPerson.from_yaml(person.to_yaml)\n    person2.should eq(person)\n  end\n\n  it \"doesn't emit null when doing to_yaml\" do\n    person = YAMLAttrPerson.from_yaml(\"---\\nname: John\\n\")\n    person.to_yaml.should_not match /age/\n  end\n\n  it \"raises if non-nilable attribute is nil\" do\n    error_message = \"Missing YAML attribute: name at line 2, column 1\"\n    ex = expect_raises YAML::ParseException, error_message do\n      YAMLAttrPerson.from_yaml <<-YAML\n        ---\n        age: 30\n        YAML\n    end\n    ex.location.should eq({2, 1})\n  end\n\n  it \"doesn't raises on false value when not-nil\" do\n    yaml = YAMLAttrValue(Bool).from_yaml(\"---\\nvalue: false\\n\")\n    yaml.value.should be_false\n  end\n\n  it \"should parse extra fields (YAMLAttrPersonExtraFields with on_unknown_yaml_attribute)\" do\n    person = YAMLAttrPersonExtraFields.from_yaml(<<-YAML)\n        ---\n        name: John\n        x: 1\n        y: \"1-2\"\n        age: 30\n        z:\n          - 1\n          - 2\n          - 3\n        YAML\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n    person.yaml_unmapped.should eq({\"x\" => 1_i64, \"y\" => \"1-2\", \"z\" => [1_i64, 2_i64, 3_i64]})\n  end\n\n  it \"should to store extra fields (YAMLAttrPersonExtraFields with on_to_yaml)\" do\n    person = YAMLAttrPersonExtraFields.from_yaml(<<-YAML)\n        ---\n        name: John\n        x: 1\n        y: \"1-2\"\n        age: 30\n        z:\n          - 1\n          - 2\n          - 3\n        YAML\n    person.name = \"John1\"\n    person.yaml_unmapped.delete(\"y\")\n    person.yaml_unmapped[\"q\"] = YAML::Any.new(\"w\")\n    person.to_yaml.should eq \"---\\nname: John1\\nage: 30\\nx: 1\\nz:\\n- 1\\n- 2\\n- 3\\nq: w\\n\"\n  end\n\n  it \"raises if not an object\" do\n    error_message = \"Expected mapping, not YAML::Nodes::Scalar at line 1, column 1\"\n    ex = expect_raises YAML::ParseException, error_message do\n      StrictYAMLAttrPerson.from_yaml <<-YAML\n        \"foo\"\n        YAML\n    end\n    ex.location.should eq({1, 1})\n  end\n\n  it \"raises if data type does not match\" do\n    error_message = \"Couldn't parse (Int32 | Nil) at line 3, column 10\"\n    ex = expect_raises YAML::ParseException, error_message do\n      StrictYAMLAttrPerson.from_yaml <<-YAML\n        {\n          \"name\": \"John\",\n          \"age\": \"foo\",\n          \"foo\": \"bar\"\n        }\n        YAML\n    end\n    ex.location.should eq({3, 10})\n  end\n\n  it \"emits null on request when doing to_yaml\" do\n    person = YAMLAttrPersonEmittingNull.from_yaml(\"---\\nname: John\\n\")\n    person.to_yaml.should match /age/\n  end\n\n  it \"emit_nulls option\" do\n    person = YAMLAttrPersonEmittingNullsByOptions.from_yaml(\"---\\nname: John\\n\")\n    person.to_yaml.should match /\\A---\\nname: John\\nage: ?\\nvalue1: ?\\n\\z/\n  end\n\n  it \"parses yaml with Time::Format converter\" do\n    yaml = YAMLAttrWithTime.from_yaml(\"---\\nvalue: 2014-10-31 23:37:16\\n\")\n    yaml.value.should be_a(Time)\n    yaml.value.to_s.should eq(\"2014-10-31 23:37:16 UTC\")\n    yaml.value.should eq(Time.utc(2014, 10, 31, 23, 37, 16))\n    yaml.to_yaml.should eq(\"---\\nvalue: 2014-10-31 23:37:16\\n\")\n  end\n\n  it \"allows setting a nilable property to nil\" do\n    person = YAMLAttrPerson.new(\"John\")\n    person.age = 1\n    person.age = nil\n  end\n\n  it \"parses simple mapping\" do\n    person = YAMLAttrWithSimpleMapping.from_yaml(\"---\\nname: John\\nage: 30\\n\")\n    person.should be_a(YAMLAttrWithSimpleMapping)\n    person.name.should eq(\"John\")\n    person.age.should eq(30)\n  end\n\n  it \"outputs with converter when nilable\" do\n    yaml = YAMLAttrWithNilableTime.new\n    yaml.to_yaml.should eq(\"--- {}\\n\")\n  end\n\n  it \"outputs with converter when nilable when emit_null is true\" do\n    yaml = YAMLAttrWithNilableTimeEmittingNull.new\n    yaml.to_yaml.should match(/\\A---\\nvalue: ?\\n\\z/)\n  end\n\n  it \"outputs YAML with Hash\" do\n    input = {\n      value: {\"foo\" => \"bar\"},\n    }.to_yaml\n    yaml = YAMLAttrValue(Hash(String, String)).from_yaml(input)\n    yaml.to_yaml.should eq(input)\n  end\n\n  it \"parses yaml with keywords\" do\n    yaml = YAMLAttrWithKeywordsMapping.from_yaml(%({\"end\": 1, \"abstract\": 2}))\n    yaml.end.should eq(1)\n    yaml.abstract.should eq(2)\n  end\n\n  it \"parses yaml with any\" do\n    yaml = YAMLAttrValue(YAML::Any).from_yaml(\"value: hello\")\n    yaml.value.as_s.should eq(\"hello\")\n\n    yaml = YAMLAttrValue(YAML::Any).from_yaml({:value => [\"foo\", \"bar\"]}.to_yaml)\n    yaml.value[1].as_s.should eq(\"bar\")\n\n    yaml = YAMLAttrValue(YAML::Any).from_yaml({:value => {:foo => :bar}}.to_yaml)\n    yaml.value[\"foo\"].as_s.should eq(\"bar\")\n\n    yaml = YAMLAttrValue(YAML::Any).from_yaml(\"extra: &foo hello\\nvalue: *foo\")\n    yaml.value.as_s.should eq(\"hello\")\n\n    expect_raises YAML::ParseException, \"Unknown anchor 'foo' at line 1, column 8\" do\n      YAMLAttrValue(YAML::Any).from_yaml(\"value: *foo\")\n    end\n  end\n\n  it \"parses yaml with problematic keys\" do\n    yaml = YAMLAttrWithProblematicKeys.from_yaml(%({\"key\": 1, \"pull\": 2}))\n    yaml.key.should eq(1)\n    yaml.pull.should eq(2)\n  end\n\n  it \"allows small types of integer\" do\n    yaml = YAMLAttrWithSmallIntegers.from_yaml(%({\"foo\": 21, \"bar\": 7}))\n\n    yaml.foo.should eq(21)\n    typeof(yaml.foo).should eq(Int16)\n\n    yaml.bar.should eq(7)\n    typeof(yaml.bar).should eq(Int8)\n  end\n\n  it \"checks that values fit into integer types\" do\n    expect_raises(YAML::ParseException, /Can't read Int16/) do\n      YAMLAttrWithSmallIntegers.from_yaml(%({\"foo\": 21000000, \"bar\": 7}))\n    end\n\n    expect_raises(YAML::ParseException, /Can't read Int8/) do\n      YAMLAttrWithSmallIntegers.from_yaml(%({\"foo\": 21, \"bar\": 7000}))\n    end\n  end\n\n  it \"checks that non-integer values for integer fields report the expected type\" do\n    expect_raises(YAML::ParseException, /Can't read Int16/) do\n      YAMLAttrWithSmallIntegers.from_yaml(%({\"foo\": \"a\", \"bar\": 7}))\n    end\n\n    expect_raises(YAML::ParseException, /Can't read Int8/) do\n      YAMLAttrWithSmallIntegers.from_yaml(%({\"foo\": 21, \"bar\": \"a\"}))\n    end\n  end\n\n  it \"parses recursive\" do\n    yaml = <<-YAML\n      --- &1\n      name: foo\n      other: *1\n      YAML\n\n    rec = YAMLAttrRecursive.from_yaml(yaml)\n    rec.name.should eq(\"foo\")\n    rec.other.should be(rec)\n  end\n\n  it \"parses recursive nilable (1)\" do\n    yaml = <<-YAML\n      --- &1\n      name: foo\n      other: *1\n      YAML\n\n    rec = YAMLAttrRecursiveNilable.from_yaml(yaml)\n    rec.name.should eq(\"foo\")\n    rec.other.should be(rec)\n  end\n\n  it \"parses recursive nilable (2)\" do\n    yaml = <<-YAML\n      --- &1\n      name: foo\n      YAML\n\n    rec = YAMLAttrRecursiveNilable.from_yaml(yaml)\n    rec.name.should eq(\"foo\")\n    rec.other.should be_nil\n  end\n\n  it \"parses recursive array\" do\n    yaml = <<-YAML\n      ---\n      name: foo\n      other: &1\n        - name: bar\n          other: *1\n      YAML\n\n    rec = YAMLAttrRecursiveArray.from_yaml(yaml)\n    rec.other[0].other.should be(rec.other)\n  end\n\n  it \"parses recursive hash\" do\n    yaml = <<-YAML\n      ---\n      name: foo\n      other: &1\n        foo:\n          name: bar\n          other: *1\n      YAML\n\n    rec = YAMLAttrRecursiveHash.from_yaml(yaml)\n    rec.other[\"foo\"].other.should be(rec.other)\n  end\n\n  describe \"parses yaml with defaults\" do\n    it \"mixed\" do\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"a\":1,\"b\":\"bla\"}))\n      yaml.a.should eq 1\n      yaml.b.should eq \"bla\"\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"a\":1}))\n      yaml.a.should eq 1\n      yaml.b.should eq \"Haha\"\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"b\":\"bla\"}))\n      yaml.a.should eq 11\n      yaml.b.should eq \"bla\"\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({}))\n      yaml.a.should eq 11\n      yaml.b.should eq \"Haha\"\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"a\":null,\"b\":null,\"f\":null}))\n      yaml.a.should eq 11\n      yaml.b.should eq \"Haha\"\n      yaml.f.should be_nil\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"b\":\"\"}))\n      yaml.b.should eq \"\"\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"b\":''}))\n      yaml.b.should eq \"\"\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"b\":}))\n      yaml.b.should eq \"Haha\"\n    end\n\n    it \"bool\" do\n      yaml = YAMLAttrWithDefaults.from_yaml(%({}))\n      yaml.c.should be_true\n      typeof(yaml.c).should eq Bool\n      yaml.d.should be_false\n      typeof(yaml.d).should eq Bool\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"c\":false}))\n      yaml.c.should be_false\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"c\":true}))\n      yaml.c.should be_true\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"d\":false}))\n      yaml.d.should be_false\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"d\":true}))\n      yaml.d.should be_true\n    end\n\n    it \"with nilable\" do\n      yaml = YAMLAttrWithDefaults.from_yaml(%({}))\n\n      yaml.e.should be_false\n      typeof(yaml.e).should eq(Bool | Nil)\n\n      yaml.f.should eq 1\n      typeof(yaml.f).should eq(Int32 | Nil)\n\n      yaml.g.should be_nil\n      typeof(yaml.g).should eq(Int32 | Nil)\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"e\":false}))\n      yaml.e.should be_false\n      yaml = YAMLAttrWithDefaults.from_yaml(%({\"e\":true}))\n      yaml.e.should be_true\n    end\n\n    it \"create new array every time\" do\n      yaml = YAMLAttrWithDefaults.from_yaml(%({}))\n      yaml.h.should eq [1, 2, 3]\n      yaml.h << 4\n      yaml.h.should eq [1, 2, 3, 4]\n\n      yaml = YAMLAttrWithDefaults.from_yaml(%({}))\n      yaml.h.should eq [1, 2, 3]\n    end\n  end\n\n  it \"converter with null value (#13655)\" do\n    YAMLAttrNilableWithTimeEpoch.from_yaml(%({\"value\": null})).value.should be_nil\n    YAMLAttrNilableWithTimeEpoch.from_yaml(%({\"value\":1459859781})).value.should eq Time.unix(1459859781)\n  end\n\n  it \"converter with default value\" do\n    YAMLAttrDefaultWithTimeEpoch.from_yaml(%({\"value\": null})).value.should eq Time.unix(0)\n    YAMLAttrDefaultWithTimeEpoch.from_yaml(%({\"value\":1459859781})).value.should eq Time.unix(1459859781)\n  end\n\n  it \"uses Time::EpochConverter\" do\n    string = %({\"value\":1459859781})\n    yaml = YAMLAttrWithTimeEpoch.from_yaml(string)\n    yaml.value.should be_a(Time)\n    yaml.value.should eq(Time.unix(1459859781))\n    yaml.to_yaml.should eq(\"---\\nvalue: 1459859781\\n\")\n  end\n\n  it \"uses Time::EpochMillisConverter\" do\n    string = %({\"value\":1459860483856})\n    yaml = YAMLAttrWithTimeEpochMillis.from_yaml(string)\n    yaml.value.should be_a(Time)\n    yaml.value.should eq(Time.unix_ms(1459860483856))\n    yaml.to_yaml.should eq(\"---\\nvalue: 1459860483856\\n\")\n  end\n\n  describe YAML::ArrayConverter do\n    it \"uses converter metaclass\" do\n      string = %(---\\nvalue:\\n- 1459859781\\n)\n      yaml = YAMLAttrWithTimeArray1.from_yaml(string)\n      yaml.value.should be_a(Array(Time))\n      yaml.value.should eq([Time.unix(1459859781)])\n      yaml.to_yaml.should eq(string)\n    end\n\n    it \"uses converter instance with nested converter metaclass\" do\n      string = %(---\\nvalue:\\n- 1459859781\\n)\n      yaml = YAMLAttrWithTimeArray2.from_yaml(string)\n      yaml.value.should be_a(Array(Time))\n      yaml.value.should eq([Time.unix(1459859781)])\n      yaml.to_yaml.should eq(string)\n    end\n\n    it \"uses converter instance with nested converter instance\" do\n      string = %(---\\nvalue:\\n- 2014-10-31 23:37:16\\n)\n      yaml = YAMLAttrWithTimeArray3.from_yaml(string)\n      yaml.value.should be_a(Array(Time))\n      yaml.value.map(&.to_s).should eq([\"2014-10-31 23:37:16 UTC\"])\n      yaml.to_yaml.should eq(string)\n    end\n\n    it \"uses correct array element type\" do\n      string = %(---\\nvalue:\\n- x: 1\\n  y: 2\\n)\n      yaml = YAMLAttrWithSerializableArray.from_yaml(string)\n      yaml.value.should be_a(Array(YAMLAttrPoint))\n      yaml.value.should eq([YAMLAttrPoint.new(1, 2)])\n      yaml.to_yaml.should eq(string)\n    end\n  end\n\n  it \"parses nilable union\" do\n    obj = YAMLAttrValue(Int32?).from_yaml(%({\"value\": 1}))\n    obj.value.should eq(1)\n    obj.to_yaml.should eq(\"---\\nvalue: 1\\n\")\n\n    obj = YAMLAttrValue(Int32?).from_yaml(%({\"value\": null}))\n    obj.value.should be_nil\n    obj.to_yaml.should eq(\"--- {}\\n\")\n\n    obj = YAMLAttrValue(Int32?).from_yaml(%({}))\n    obj.value.should be_nil\n    obj.to_yaml.should eq(\"--- {}\\n\")\n  end\n\n  describe \"parses YAML with presence markers\" do\n    it \"parses person with absent attributes\" do\n      yaml = YAMLAttrWithPresence.from_yaml(\"---\\nfirst_name:\\n\")\n      yaml.first_name.should be_nil\n      yaml.first_name_present?.should be_true\n      yaml.last_name.should be_nil\n      yaml.last_name_present?.should be_false\n    end\n  end\n\n  describe \"serializes YAML with presence markers and ignore_serialize\" do\n    context \"ignore_serialize is set to a method which returns true when value is nil or empty string\" do\n      it \"ignores field when value is empty string\" do\n        yaml = YAMLAttrWithPresenceAndIgnoreSerialize.from_yaml(%({\"first_name\": \"\"}))\n        yaml.first_name_present?.should be_true\n        yaml.to_yaml.should eq(\"--- {}\\n\")\n      end\n\n      it \"ignores field when value is nil\" do\n        yaml = YAMLAttrWithPresenceAndIgnoreSerialize.from_yaml(%({\"first_name\": null}))\n        yaml.first_name_present?.should be_true\n        yaml.to_yaml.should eq(\"--- {}\\n\")\n      end\n    end\n\n    context \"ignore_serialize is set to conditional expressions 'last_name.nil? && !last_name_present?'\" do\n      it \"emits null when value is null and @last_name_present is true\" do\n        yaml = YAMLAttrWithPresenceAndIgnoreSerialize.from_yaml(%({\"last_name\": null}))\n        yaml.last_name_present?.should be_true\n\n        # libyaml 0.2.5 removes trailing space for empty scalar nodes\n        if YAML.libyaml_version >= SemanticVersion.new(0, 2, 5)\n          yaml.to_yaml.should eq(\"---\\nlast_name:\\n\")\n        else\n          yaml.to_yaml.should eq(\"---\\nlast_name: \\n\")\n        end\n      end\n      it \"does not emit null when value is null and @last_name_present is false\" do\n        yaml = YAMLAttrWithPresenceAndIgnoreSerialize.from_yaml(%({}))\n        yaml.last_name_present?.should be_false\n        yaml.to_yaml.should eq(\"--- {}\\n\")\n      end\n\n      it \"emits field when value is not nil and @last_name_present is false\" do\n        yaml = YAMLAttrWithPresenceAndIgnoreSerialize.new(last_name: \"something\")\n        yaml.last_name_present?.should be_false\n        yaml.to_yaml.should eq(\"---\\nlast_name: something\\n\")\n      end\n\n      it \"emits field when value is not nil and @last_name_present is true\" do\n        yaml = YAMLAttrWithPresenceAndIgnoreSerialize.from_yaml(%({\"last_name\":\"something\"}))\n        yaml.last_name_present?.should be_true\n        yaml.to_yaml.should eq(\"---\\nlast_name: something\\n\")\n      end\n    end\n  end\n\n  describe \"with query attributes\" do\n    it \"defines query getter\" do\n      yaml = YAMLAttrWithQueryAttributes.from_yaml(%({\"foo\": true}))\n      yaml.foo?.should be_true\n      yaml.bar?.should be_false\n    end\n\n    it \"defines query getter with class restriction\" do\n      {% begin %}\n        {% methods = YAMLAttrWithQueryAttributes.methods %}\n        {{ methods.find(&.name.==(\"foo?\")).return_type }}.should eq(Bool)\n        {{ methods.find(&.name.==(\"bar?\")).return_type }}.should eq(Bool)\n      {% end %}\n    end\n\n    it \"defines non-query setter and presence methods\" do\n      yaml = YAMLAttrWithQueryAttributes.from_yaml(%({\"foo\": false}))\n      yaml.bar_present?.should be_false\n      yaml.bar = true\n      yaml.bar?.should be_true\n    end\n\n    it \"maps non-query attributes\" do\n      yaml = YAMLAttrWithQueryAttributes.from_yaml(%({\"foo\": false, \"is_bar\": false}))\n      yaml.bar_present?.should be_true\n      yaml.bar?.should be_false\n      yaml.bar = true\n      yaml.to_yaml.should eq(\"---\\nfoo: false\\nis_bar: true\\n\")\n    end\n\n    it \"raises if non-nilable attribute is nil\" do\n      error_message = \"Missing YAML attribute: foo at line 1, column 1\"\n      ex = expect_raises YAML::ParseException, error_message do\n        YAMLAttrWithQueryAttributes.from_yaml(%({\"is_bar\": true}))\n      end\n      ex.location.should eq({1, 1})\n    end\n  end\n\n  it \"calls #finalize\" do\n    assert_finalizes(\"yaml\") { YAMLAttrWithFinalize.from_yaml(\"---\\nvalue: 1\\n\") }\n  end\n\n  describe \"work with module and inheritance\" do\n    it { YAMLAttrModuleTest.from_yaml(%({\"phoo\": 20})).to_tuple.should eq({10, 20}) }\n    it { YAMLAttrModuleTest.from_yaml(%({\"phoo\": 20})).to_tuple.should eq({10, 20}) }\n    it { YAMLAttrModuleTest2.from_yaml(%({\"phoo\": 20, \"bar\": 30})).to_tuple.should eq({10, 20, 30}) }\n    it { YAMLAttrModuleTest2.from_yaml(%({\"bar\": 30, \"moo\": 40})).to_tuple.should eq({40, 15, 30}) }\n  end\n\n  describe \"work with inned class using same module name\" do\n    it { YAMLAttrModuleWithSameNameClass::Test.from_yaml(%({\"foo\": 42})).foo.should eq(42) }\n  end\n\n  describe \"use_yaml_discriminator\" do\n    it \"deserializes with discriminator\" do\n      point = YAMLShape.from_yaml(%({\"type\": \"point\", \"x\": 1, \"y\": 2})).as(YAMLPoint)\n      point.x.should eq(1)\n      point.y.should eq(2)\n\n      circle = YAMLShape.from_yaml(%({\"type\": \"circle\", \"x\": 1, \"y\": 2, \"radius\": 3})).as(YAMLCircle)\n      circle.x.should eq(1)\n      circle.y.should eq(2)\n      circle.radius.should eq(3)\n    end\n\n    it \"raises if missing discriminator\" do\n      expect_raises(YAML::ParseException, \"Missing YAML discriminator field 'type'\") do\n        YAMLShape.from_yaml(\"{}\")\n      end\n    end\n\n    it \"raises if unknown discriminator value\" do\n      expect_raises(YAML::ParseException, %(Unknown 'type' discriminator value: \"unknown\")) do\n        YAMLShape.from_yaml(%({\"type\": \"unknown\"}))\n      end\n    end\n\n    it \"deserializes type which nests type with discriminator (#9849)\" do\n      container = YAMLAttrValue(YAMLShape).from_yaml(%({\"value\": {\"type\": \"point\", \"x\": 1, \"y\": 2}}))\n      point = container.value.as(YAMLPoint)\n      point.x.should eq(1)\n      point.y.should eq(2)\n    end\n\n    it \"deserializes with variable discriminator value type\" do\n      object_number = YAMLVariableDiscriminatorValueType.from_yaml(%({\"type\": 0}))\n      object_number.should be_a(YAMLVariableDiscriminatorNumber)\n\n      object_string = YAMLVariableDiscriminatorValueType.from_yaml(%({\"type\": \"1\"}))\n      object_string.should be_a(YAMLVariableDiscriminatorString)\n\n      object_bool = YAMLVariableDiscriminatorValueType.from_yaml(%({\"type\": true}))\n      object_bool.should be_a(YAMLVariableDiscriminatorBool)\n\n      object_enum = YAMLVariableDiscriminatorValueType.from_yaml(%({\"type\": 4}))\n      object_enum.should be_a(YAMLVariableDiscriminatorEnum)\n\n      object_enum = YAMLVariableDiscriminatorValueType.from_yaml(%({\"type\": 18}))\n      object_enum.should be_a(YAMLVariableDiscriminatorEnum8)\n    end\n\n    it \"deserializes with discriminator, strict recursive type\" do\n      foo = YAMLStrictDiscriminator.from_yaml(%({\"type\": \"foo\"}))\n      foo = foo.should be_a(YAMLStrictDiscriminatorFoo)\n\n      bar = YAMLStrictDiscriminator.from_yaml(%({\"type\": \"bar\", \"x\": {\"type\": \"foo\"}, \"y\": {\"type\": \"foo\"}}))\n      bar = bar.should be_a(YAMLStrictDiscriminatorBar)\n      bar.x.should be_a(YAMLStrictDiscriminatorFoo)\n      bar.y.should be_a(YAMLStrictDiscriminatorFoo)\n    end\n\n    it \"deserializes with discriminator, another recursive type, fixes: #13429\" do\n      c = YAMLDiscriminatorBug::Base.from_yaml %q({\"type\": \"c\", \"source\": {\"type\": \"a\"}, \"value\": 2})\n      c.as(YAMLDiscriminatorBug::C).value.should eq 2\n\n      c = YAMLDiscriminatorBug::Base.from_yaml %q({\"type\": \"c\", \"source\": {\"type\": \"a\"}})\n      c.as(YAMLDiscriminatorBug::C).value.should eq 1\n    end\n  end\n\n  describe \"namespaced classes\" do\n    it \"lets default values use the object's own namespace\" do\n      request = YAMLNamespace::FooRequest.from_yaml(%({\"foo\":{}}))\n      request.foo.id.should eq \"id:foo\"\n      request.bar.id.should eq \"id:bar\"\n    end\n  end\n\n  it \"fixes #13337\" do\n    YAMLSomething.from_yaml(%({\"value\":{}})).value.should_not be_nil\n  end\n\n  it \"works when type has constructor with double splat parameter (#16140)\" do\n    YAMLInitializeOpts.from_yaml(%({\"value\":123})).value.should eq(123)\n  end\n\n  it \"supports generic type variables in converters\" do\n    YAMLAttrWithGenericConverter(Time::EpochConverter).from_yaml(%({\"value\":1459859781})).value.should eq(Time.unix(1459859781))\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/serialization_spec.cr",
    "content": "require \"../spec_helper\"\nrequire \"../../support/number\"\nrequire \"yaml\"\nrequire \"big\"\nrequire \"big/yaml\"\n\nenum YAMLSpecEnum\n  Zero\n  One\n  Two\n  OneHundred\nend\n\n@[Flags]\nenum YAMLSpecFlagEnum\n  One\n  Two\n  OneHundred\nend\n\nprivate record FooPrivate, x : Int32 do\n  def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)\n    new(Int32.new(ctx, node))\n  end\nend\n\nalias YamlRec = Int32 | Array(YamlRec) | Hash(YamlRec, YamlRec)\n\n# libyaml 0.2.1 removed the erroneously written document end marker (`...`) after some scalars in root context (see https://github.com/yaml/libyaml/pull/18).\n# Earlier libyaml releases still write the document end marker and this is hard to fix on Crystal's side.\n# So we just ignore it and adopt the specs accordingly to coincide with the used libyaml version.\nprivate def assert_yaml_document_end(actual, expected, file = __FILE__, line = __LINE__)\n  actual.rchop(\"...\\n\").should eq(expected), file: file, line: line\nend\n\ndescribe \"YAML serialization\" do\n  describe \"from_yaml\" do\n    it \"does Nil#from_yaml\" do\n      %w(~ null Null NULL).each do |string|\n        Nil.from_yaml(string).should be_nil\n      end\n      Nil.from_yaml(\"--- \\n...\\n\").should be_nil\n    end\n\n    it \"does Bool#from_yaml\" do\n      %w(yes Yes YES true True TRUE on On ON).each do |string|\n        Bool.from_yaml(string).should be_true\n      end\n\n      %w(no No NO false False FALSE off Off OFF).each do |string|\n        Bool.from_yaml(string).should be_false\n      end\n    end\n\n    {% for int in BUILTIN_INTEGER_TYPES %}\n      it \"does {{ int }}.from_yaml\" do\n        {{ int }}.from_yaml(\"0\").should(be_a({{ int }})).should eq(0)\n        {{ int }}.from_yaml(\"123\").should(be_a({{ int }})).should eq(123)\n        {{ int }}.from_yaml({{ int }}::MIN.to_s).should(be_a({{ int }})).should eq({{ int }}::MIN)\n        {{ int }}.from_yaml({{ int }}::MAX.to_s).should(be_a({{ int }})).should eq({{ int }}::MAX)\n      end\n\n      it \"raises if {{ int }}.from_yaml overflows\" do\n        expect_raises(YAML::ParseException, \"Can't read {{ int }}\") do\n          {{ int }}.from_yaml(({{ int }}::MIN.to_big_i - 1).to_s)\n        end\n        expect_raises(YAML::ParseException, \"Can't read {{ int }}\") do\n          {{ int }}.from_yaml(({{ int }}::MAX.to_big_i + 1).to_s)\n        end\n      end\n    {% end %}\n\n    it \"does Int.from_yaml with prefixes\" do\n      Int32.from_yaml(\"0xabc\").should eq(0xabc)\n      Int32.from_yaml(\"0b10110\").should eq(0b10110)\n      Int32.from_yaml(\"0777\").should eq(0o777)\n    end\n\n    it \"does Int.from_yaml with underscores\" do\n      Int32.from_yaml(\"1_2_34\").should eq(1_2_34)\n    end\n\n    it \"does String#from_yaml\" do\n      String.from_yaml(\"hello\").should eq(\"hello\")\n    end\n\n    it \"does String#from_yaml (empty string)\" do\n      String.from_yaml(\"\").should eq(\"\")\n    end\n\n    it \"can parse string that looks like a number\" do\n      String.from_yaml(%(1.2)).should eq(\"1.2\")\n    end\n\n    it \"does Path.from_yaml\" do\n      Path.from_yaml(%(\"foo/bar\")).should eq(Path.new(\"foo/bar\"))\n    end\n\n    it \"does Float32#from_yaml\" do\n      Float32.from_yaml(\"1.5\").should eq(1.5_f32)\n      Float32.from_yaml(\".nan\").nan?.should be_true\n      Float32.from_yaml(\".inf\").should eq(Float32::INFINITY)\n      Float32.from_yaml(\"-.inf\").should eq(-Float32::INFINITY)\n    end\n\n    it \"does Float64#from_yaml\" do\n      value = Float64.from_yaml(\"1.5\")\n      value.should eq(1.5)\n      value.should be_a(Float64)\n\n      Float64.from_yaml(\".nan\").nan?.should be_true\n      Float64.from_yaml(\".inf\").should eq(Float64::INFINITY)\n      Float64.from_yaml(\"-.inf\").should eq(-Float64::INFINITY)\n    end\n\n    it \"does Array#from_yaml\" do\n      Array(Int32).from_yaml(\"---\\n- 1\\n- 2\\n- 3\\n\").should eq([1, 2, 3])\n    end\n\n    it \"does Set#from_yaml\" do\n      Set(Int32).from_yaml(\"---\\n- 1\\n- 2\\n- 2\\n\").should eq(Set.new([1, 2]))\n    end\n\n    it \"does Array#from_yaml from IO\" do\n      io = IO::Memory.new \"---\\n- 1\\n- 2\\n- 3\\n\"\n      Array(Int32).from_yaml(io).should eq([1, 2, 3])\n    end\n\n    it \"does Array#from_yaml with block\" do\n      elements = [] of Int32\n      Array(Int32).from_yaml(\"---\\n- 1\\n- 2\\n- 3\\n\") do |element|\n        elements << element\n      end\n      elements.should eq([1, 2, 3])\n    end\n\n    it \"does Hash#from_yaml\" do\n      Hash(Int32, Bool).from_yaml(\"---\\n1: true\\n2: false\\n\").should eq({1 => true, 2 => false})\n    end\n\n    it \"does Hash#from_yaml with value type anchors\" do\n      Hash(String, Int32).from_yaml(<<-YAML).should eq({\"x\" => 123, \"y\" => 123})\n        ---\n        x: &foo 123\n        y: *foo\n        YAML\n    end\n\n    it \"does Hash#from_yaml with merge\" do\n      yaml = <<-YAML\n        - &foo\n          bar: 1\n          baz: 2\n        -\n          <<: *foo\n        YAML\n\n      array = Array(Hash(String, Int32)).from_yaml(yaml)\n      array[1].should eq(array[0])\n    end\n\n    it \"does Hash#from_yaml with merge (recursive)\" do\n      yaml = <<-YAML\n        - &foo\n          foo: 1\n\n        - &bar\n          bar: 2\n          <<: *foo\n        -\n          <<: *bar\n        YAML\n\n      array = Array(Hash(String, Int32)).from_yaml(yaml)\n      array[2].should eq({\"foo\" => 1, \"bar\" => 2})\n    end\n\n    it \"does for tuple\" do\n      tuple = Tuple(Int32, String, Bool).from_yaml(\"---\\n- 1\\n- foo\\n- true\\n\")\n      tuple.should eq({1, \"foo\", true})\n      typeof(tuple).should eq(Tuple(Int32, String, Bool))\n    end\n\n    it \"does for tuple with file-private type\" do\n      tuple = Tuple(FooPrivate).from_yaml %([1])\n      tuple.should eq({FooPrivate.new(1)})\n      typeof(tuple).should eq(Tuple(FooPrivate))\n    end\n\n    it \"does for empty tuple\" do\n      typeof(Tuple.new).from_yaml(\"[]\").should eq(Tuple.new)\n    end\n\n    it \"does for named tuple\" do\n      tuple = NamedTuple(x: Int32, y: String).from_yaml(%({\"y\": \"hello\", \"x\": 1}))\n      tuple.should eq({x: 1, y: \"hello\"})\n      typeof(tuple).should eq(NamedTuple(x: Int32, y: String))\n    end\n\n    it \"does for empty named tuple\" do\n      tuple = typeof(NamedTuple.new).from_yaml(%({}))\n      tuple.should eq(NamedTuple.new)\n      tuple.should be_a(typeof(NamedTuple.new))\n    end\n\n    it \"does for named tuple with nilable fields (#8089)\" do\n      tuple = NamedTuple(x: Int32?, y: String).from_yaml(%({\"y\": \"hello\"}))\n      tuple.should eq({x: nil, y: \"hello\"})\n      typeof(tuple).should eq(NamedTuple(x: Int32?, y: String))\n    end\n\n    it \"does for named tuple with nilable fields and null (#8089)\" do\n      tuple = NamedTuple(x: Int32?, y: String).from_yaml(%({\"y\": \"hello\", \"x\": null}))\n      tuple.should eq({x: nil, y: \"hello\"})\n      typeof(tuple).should eq(NamedTuple(x: Int32?, y: String))\n    end\n\n    it \"does for named tuple with spaces in key (#10918)\" do\n      tuple = NamedTuple(a: Int32, \"xyz b-23\": Int32).from_yaml %{{\"a\": 1, \"xyz b-23\": 2}}\n      tuple.should eq({a: 1, \"xyz b-23\": 2})\n      typeof(tuple).should eq(NamedTuple(a: Int32, \"xyz b-23\": Int32))\n    end\n\n    it \"does for named tuple with spaces in key and quote char (#10918)\" do\n      tuple = NamedTuple(a: Int32, \"xyz \\\"foo\\\" b-23\": Int32).from_yaml %{{\"a\": 1, \"xyz \\\\\"foo\\\\\" b-23\": 2}}\n      tuple.should eq({a: 1, \"xyz \\\"foo\\\" b-23\": 2})\n      typeof(tuple).should eq(NamedTuple(a: Int32, \"xyz \\\"foo\\\" b-23\": Int32))\n    end\n\n    it \"does for named tuple with file-private type\" do\n      tuple = NamedTuple(a: FooPrivate).from_yaml %({\"a\": 1})\n      tuple.should eq({a: FooPrivate.new(1)})\n      typeof(tuple).should eq(NamedTuple(a: FooPrivate))\n    end\n\n    it \"does for BigInt\" do\n      big = BigInt.from_yaml(\"123456789123456789123456789123456789123456789\")\n      big.should be_a(BigInt)\n      big.should eq(BigInt.new(\"123456789123456789123456789123456789123456789\"))\n    end\n\n    it \"does for BigFloat\" do\n      big = BigFloat.from_yaml(\"1234.567891011121314\")\n      big.should be_a(BigFloat)\n      big.should eq(BigFloat.new(\"1234.567891011121314\"))\n    end\n\n    it \"does for BigDecimal\" do\n      big = BigDecimal.from_yaml(\"1234.567891011121314\")\n      big.should be_a(BigDecimal)\n      big.should eq(BigDecimal.new(\"1234.567891011121314\"))\n    end\n\n    describe \"Enum\" do\n      it \"normal enum\" do\n        YAMLSpecEnum.from_yaml(%(\"one\")).should eq(YAMLSpecEnum::One)\n        YAMLSpecEnum.from_yaml(%(\"One\")).should eq(YAMLSpecEnum::One)\n        YAMLSpecEnum.from_yaml(%(\"two\")).should eq(YAMLSpecEnum::Two)\n        YAMLSpecEnum.from_yaml(%(\"ONE_HUNDRED\")).should eq(YAMLSpecEnum::OneHundred)\n        YAMLSpecEnum.from_yaml(%(\"ONE-HUNDRED\")).should eq(YAMLSpecEnum::OneHundred)\n\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: \" one \")) do\n          YAMLSpecEnum.from_yaml(%(\" one \"))\n        end\n\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: \"three\")) do\n          YAMLSpecEnum.from_yaml(%(\"three\"))\n        end\n        expect_raises(YAML::ParseException, %(Expected String, not \"1\")) do\n          YAMLSpecEnum.from_yaml(%(1))\n        end\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: \"1\")) do\n          YAMLSpecEnum.from_yaml(%(\"1\"))\n        end\n\n        expect_raises(YAML::ParseException, \"Expected scalar, not mapping\") do\n          YAMLSpecEnum.from_yaml(%({}))\n        end\n        expect_raises(YAML::ParseException, \"Expected scalar, not sequence\") do\n          YAMLSpecEnum.from_yaml(%([]))\n        end\n      end\n\n      it \"flag enum\" do\n        YAMLSpecFlagEnum.from_yaml(%([\"one\"])).should eq(YAMLSpecFlagEnum::One)\n        YAMLSpecFlagEnum.from_yaml(%([\"One\"])).should eq(YAMLSpecFlagEnum::One)\n        YAMLSpecFlagEnum.from_yaml(%([one])).should eq(YAMLSpecFlagEnum::One)\n        YAMLSpecFlagEnum.from_yaml(%([\"one\", \"one\"])).should eq(YAMLSpecFlagEnum::One)\n        YAMLSpecFlagEnum.from_yaml(%([\"one\", \"two\"])).should eq(YAMLSpecFlagEnum::One | YAMLSpecFlagEnum::Two)\n        YAMLSpecFlagEnum.from_yaml(%([one, two])).should eq(YAMLSpecFlagEnum::One | YAMLSpecFlagEnum::Two)\n        YAMLSpecFlagEnum.from_yaml(%([\"one\", \"two\", \"one_hundred\"])).should eq(YAMLSpecFlagEnum::All)\n        YAMLSpecFlagEnum.from_yaml(%([])).should eq(YAMLSpecFlagEnum::None)\n\n        expect_raises(YAML::ParseException, \"Expected scalar, not sequence\") do\n          YAMLSpecFlagEnum.from_yaml(%([\"one\", [\"two\"]]))\n        end\n\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecFlagEnum value: \"three\")) do\n          YAMLSpecFlagEnum.from_yaml(%([\"one\", \"three\"]))\n        end\n        expect_raises(YAML::ParseException, %(Expected String, not \"1\")) do\n          YAMLSpecFlagEnum.from_yaml(%([1, 2]))\n        end\n        expect_raises(YAML::ParseException, %(Expected String, not \"2\")) do\n          YAMLSpecFlagEnum.from_yaml(%([\"one\", 2]))\n        end\n        expect_raises(YAML::ParseException, \"Expected sequence, not mapping\") do\n          YAMLSpecFlagEnum.from_yaml(%({}))\n        end\n        expect_raises(YAML::ParseException, \"Expected sequence, not scalar\") do\n          YAMLSpecFlagEnum.from_yaml(%(\"one\"))\n        end\n      end\n    end\n\n    describe \"Enum::ValueConverter.from_yaml\" do\n      it \"normal enum\" do\n        Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"0\").should eq(YAMLSpecEnum::Zero)\n        Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"1\").should eq(YAMLSpecEnum::One)\n        Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"2\").should eq(YAMLSpecEnum::Two)\n        Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"3\").should eq(YAMLSpecEnum::OneHundred)\n\n        expect_raises(YAML::ParseException, %(Expected Int64, not \"3\")) do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(%(\"3\"))\n        end\n\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: 4)) do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"4\")\n        end\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: -1)) do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"-1\")\n        end\n        expect_raises(YAML::ParseException, %(Expected Int64, not )) do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(\"\")\n        end\n\n        expect_raises(YAML::ParseException, %(Expected Int64, not \"one\")) do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(%(\"one\"))\n        end\n\n        expect_raises(YAML::ParseException, \"Expected scalar, not mapping\") do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(%({}))\n        end\n        expect_raises(YAML::ParseException, \"Expected scalar, not sequence\") do\n          Enum::ValueConverter(YAMLSpecEnum).from_yaml(%([]))\n        end\n      end\n\n      it \"flag enum\" do\n        Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"0\").should eq(YAMLSpecFlagEnum::None)\n        Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"1\").should eq(YAMLSpecFlagEnum::One)\n        Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"2\").should eq(YAMLSpecFlagEnum::Two)\n        Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"4\").should eq(YAMLSpecFlagEnum::OneHundred)\n        Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"5\").should eq(YAMLSpecFlagEnum::OneHundred | YAMLSpecFlagEnum::One)\n        Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"7\").should eq(YAMLSpecFlagEnum::All)\n\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecFlagEnum value: 8)) do\n          Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"8\")\n        end\n        expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecFlagEnum value: -1)) do\n          Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"-1\")\n        end\n        expect_raises(YAML::ParseException, %(Expected Int64, not \"\")) do\n          Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(\"\")\n        end\n\n        expect_raises(YAML::ParseException, %(Expected Int64, not \"one\")) do\n          Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(%(\"one\"))\n        end\n\n        expect_raises(YAML::ParseException, \"Expected scalar, not mapping\") do\n          Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(%({}))\n        end\n        expect_raises(YAML::ParseException, \"Expected scalar, not sequence\") do\n          Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(%([]))\n        end\n      end\n    end\n\n    it \"does Time::Format#from_yaml\" do\n      ctx = YAML::ParseContext.new\n      nodes = YAML::Nodes.parse(\"--- 2014-01-02\\n...\\n\").nodes.first\n      value = Time::Format.new(\"%F\").from_yaml(ctx, nodes)\n      value.should eq(Time.utc(2014, 1, 2))\n    end\n\n    it \"deserializes union with nil, string and int (#7936)\" do\n      Array(Int32 | String | Nil).from_yaml(%([1, \"hello\", null])).should eq([1, \"hello\", nil])\n    end\n\n    it \"deserializes time\" do\n      Time.from_yaml(\"2010-11-12\").should eq(Time.utc(2010, 11, 12))\n\n      t = Time.local(2001, 12, 14, 21, 59, 43, nanosecond: 100000000, location: Time::Location.fixed(-18000))\n      Time.from_yaml(\"2001-12-14t21:59:43.10-05:00\").should eq(t)\n      Time.from_yaml(\"2001-12-14 21:59:43.10 -5\").should eq(t)\n      Time.from_yaml(\"2001-12-14  21:59:43.10\\t\\t -5\").should eq(t)\n      Time.from_yaml(%(!!timestamp \"2001-12-14  21:59:43.10\\t\\\\t -5\")).should eq(t)\n\n      expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp \"2001-12-14\\\\f21:59:43.10 -5\")) }\n      expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp \"2001-12-14\\\\n21:59:43.10 -5\")) }\n      expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp \"2001-12-14\\\\r21:59:43.10 -5\")) }\n      expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp \"2001-12-14\\\\v21:59:43.10 -5\")) }\n    end\n\n    it \"deserializes Time::Location\" do\n      Time::Location.from_yaml(\"UTC\").should eq(Time::Location.load(\"UTC\"))\n    end\n\n    it \"deserializes bytes\" do\n      Bytes.from_yaml(\"!!binary aGVsbG8=\").should eq(\"hello\".to_slice)\n    end\n\n    describe \"Union.from_yaml\" do\n      it \"String prioritization\" do\n        (Int32 | String).from_yaml(%(42)).should eq 42\n        (Int32 | String).from_yaml(%(\"42\")).should eq \"42\"\n\n        (String | UInt32).from_yaml(%(42)).should eq 42\n        (String | UInt32).from_yaml(%(\"42\")).should eq \"42\"\n\n        (Int32 | UInt32).from_yaml(%(\"42\")).should eq 42\n      end\n    end\n\n    describe \"parse exceptions\" do\n      it \"has correct location when raises in Nil#from_yaml\" do\n        ex = expect_raises(YAML::ParseException) do\n          Array(Nil).from_yaml <<-YAML\n            [\n              1\n            ]\n            YAML\n        end\n        ex.message.should eq(%(Expected Nil, not \"1\" at line 2, column 3))\n        ex.location.should eq({2, 3})\n      end\n\n      it \"has correct location when raises in Int32#from_yaml\" do\n        ex = expect_raises(YAML::ParseException) do\n          Array(Int32).from_yaml <<-YAML\n            [\n              \"hello\"\n            ]\n            YAML\n        end\n        ex.location.should eq({2, 3})\n      end\n\n      it \"has correct location when raises in NamedTuple#from_yaml\" do\n        ex = expect_raises(YAML::ParseException) do\n          Array({foo: Int32, bar: String}).from_yaml <<-YAML\n            [\n              {\"foo\": 1}\n            ]\n            YAML\n        end\n        ex.location.should eq({2, 3})\n      end\n\n      it \"has correct location when raises in Union#from_yaml\" do\n        ex = expect_raises(YAML::ParseException) do\n          Array(Int32 | Bool).from_yaml <<-YAML\n            [\n              {\"foo\": \"bar\"}\n            ]\n            YAML\n        end\n        ex.location.should eq({2, 3})\n      end\n    end\n  end\n\n  describe \"to_yaml\" do\n    it \"does for Nil\" do\n      Nil.from_yaml(nil.to_yaml).should be_nil\n    end\n\n    it \"does for Nil (empty string)\" do\n      Nil.from_yaml(\"\").should be_nil\n    end\n\n    it \"does for Bool\" do\n      Bool.from_yaml(true.to_yaml).should be_true\n      Bool.from_yaml(false.to_yaml).should be_false\n    end\n\n    it \"does for Int32\" do\n      Int32.from_yaml(1.to_yaml).should eq(1)\n    end\n\n    it \"does for Float32\" do\n      Float32.from_yaml(1.5_f32.to_yaml).should eq(1.5_f32)\n    end\n\n    it \"does for Float32 (infinity)\" do\n      Float32.from_yaml(Float32::INFINITY.to_yaml).should eq(Float32::INFINITY)\n    end\n\n    it \"does for Float32 (-infinity)\" do\n      Float32.from_yaml((-Float32::INFINITY).to_yaml).should eq(-Float32::INFINITY)\n    end\n\n    it \"does for Float32 (nan)\" do\n      Float32.from_yaml(Float32::NAN.to_yaml).nan?.should be_true\n    end\n\n    it \"does for Float64\" do\n      Float64.from_yaml(1.5.to_yaml).should eq(1.5)\n    end\n\n    it \"does for Float64 (infinity)\" do\n      Float64.from_yaml(Float64::INFINITY.to_yaml).should eq(Float64::INFINITY)\n    end\n\n    it \"does for Float64 (-infinity)\" do\n      Float64.from_yaml((-Float64::INFINITY).to_yaml).should eq(-Float64::INFINITY)\n    end\n\n    it \"does for Float64 (nan)\" do\n      Float64.from_yaml(Float64::NAN.to_yaml).nan?.should be_true\n    end\n\n    it \"does for String\" do\n      String.from_yaml(\"hello\".to_yaml).should eq(\"hello\")\n    end\n\n    it \"does for String with stars (#3353)\" do\n      String.from_yaml(\"***\".to_yaml).should eq(\"***\")\n    end\n\n    it \"does for String with quote\" do\n      String.from_yaml(\"hel\\\"lo\".to_yaml).should eq(\"hel\\\"lo\")\n    end\n\n    it \"does for String with slash\" do\n      String.from_yaml(\"hel\\\\lo\".to_yaml).should eq(\"hel\\\\lo\")\n    end\n\n    it \"does for String with unicode characters (#8131)\" do\n      \"你好\".to_yaml.should contain(\"你好\")\n    end\n\n    it \"quotes string if reserved\" do\n      [\"1\", \"1.2\", \"true\", \"2010-11-12\"].each do |string|\n        string.to_yaml.should eq(%(--- \"#{string}\"\\n))\n      end\n    end\n\n    it \"does for Path\" do\n      Path.from_yaml(Path.new(\"foo\", \"bar\", \"baz\").to_yaml).should eq(Path.new(\"foo\", \"bar\", \"baz\"))\n    end\n\n    it \"does for Array\" do\n      Array(Int32).from_yaml([1, 2, 3].to_yaml).should eq([1, 2, 3])\n    end\n\n    it \"does for Set\" do\n      Array(Int32).from_yaml(Set(Int32).new([1, 1, 2]).to_yaml).should eq([1, 2])\n    end\n\n    it \"does for Hash\" do\n      Hash(String, Int32).from_yaml({\"foo\" => 1, \"bar\" => 2}.to_yaml).should eq({\"foo\" => 1, \"bar\" => 2})\n    end\n\n    it \"does for Hash with symbol keys\" do\n      Hash(String, Int32).from_yaml({:foo => 1, :bar => 2}.to_yaml).should eq({\"foo\" => 1, \"bar\" => 2})\n    end\n\n    it \"does for Tuple\" do\n      Tuple(Int32, String).from_yaml({1, \"hello\"}.to_yaml).should eq({1, \"hello\"})\n    end\n\n    it \"does for NamedTuple\" do\n      {x: 1, y: \"hello\"}.to_yaml.should eq({:x => 1, :y => \"hello\"}.to_yaml)\n    end\n\n    it \"does for BigInt\" do\n      big = BigInt.new(\"123456789123456789123456789123456789123456789\")\n      BigInt.from_yaml(big.to_yaml).should eq(big)\n    end\n\n    it \"does for BigFloat\" do\n      big = BigFloat.new(\"1234.567891011121314\")\n      BigFloat.from_yaml(big.to_yaml).should eq(big)\n    end\n\n    it \"does for BigDecimal\" do\n      big = BigDecimal.new(\"1234.567891011121314\")\n      BigDecimal.from_yaml(big.to_yaml).should eq(big)\n    end\n\n    describe \"Enum\" do\n      it \"normal enum\" do\n        assert_yaml_document_end(YAMLSpecEnum::One.to_yaml, \"--- one\\n\")\n        YAMLSpecEnum.from_yaml(YAMLSpecEnum::One.to_yaml).should eq(YAMLSpecEnum::One)\n\n        assert_yaml_document_end(YAMLSpecEnum::OneHundred.to_yaml, \"--- one_hundred\\n\")\n        YAMLSpecEnum.from_yaml(YAMLSpecEnum::OneHundred.to_yaml).should eq(YAMLSpecEnum::OneHundred)\n\n        # undefined members can't be parsed back because the standard converter only accepts named\n        # members\n        assert_yaml_document_end(YAMLSpecEnum.new(42).to_yaml, %(--- \"42\"\\n))\n      end\n\n      it \"flag enum\" do\n        assert_yaml_document_end(YAMLSpecFlagEnum::One.to_yaml, %(--- [one]\\n))\n        YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::One.to_yaml).should eq(YAMLSpecFlagEnum::One)\n\n        assert_yaml_document_end(YAMLSpecFlagEnum::OneHundred.to_yaml, %(--- [one_hundred]\\n))\n        YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::OneHundred.to_yaml).should eq(YAMLSpecFlagEnum::OneHundred)\n\n        combined = YAMLSpecFlagEnum::OneHundred | YAMLSpecFlagEnum::One\n        assert_yaml_document_end(combined.to_yaml, %(--- [one, one_hundred]\\n))\n        YAMLSpecFlagEnum.from_yaml(combined.to_yaml).should eq(combined)\n\n        assert_yaml_document_end(YAMLSpecFlagEnum::None.to_yaml, %(--- []\\n))\n        YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::None.to_yaml).should eq(YAMLSpecFlagEnum::None)\n\n        assert_yaml_document_end(YAMLSpecFlagEnum::All.to_yaml, %(--- [one, two, one_hundred]\\n))\n        YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::All.to_yaml).should eq(YAMLSpecFlagEnum::All)\n\n        assert_yaml_document_end(YAMLSpecFlagEnum.new(42).to_yaml, \"--- [two]\\n\")\n      end\n    end\n\n    describe \"Enum::ValueConverter\" do\n      it \"normal enum\" do\n        converter = Enum::ValueConverter(YAMLSpecEnum)\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecEnum::One), \"--- 1\\n\")\n        converter.from_yaml(converter.to_yaml(YAMLSpecEnum::One)).should eq(YAMLSpecEnum::One)\n\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecEnum::OneHundred), \"--- 3\\n\")\n        converter.from_yaml(converter.to_yaml(YAMLSpecEnum::OneHundred)).should eq(YAMLSpecEnum::OneHundred)\n\n        # undefined members can't be parsed back because the standard converter only accepts named\n        # members\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecEnum.new(42)), %(--- 42\\n))\n      end\n\n      it \"flag enum\" do\n        converter = Enum::ValueConverter(YAMLSpecFlagEnum)\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::One), %(--- 1\\n))\n        converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::One)).should eq(YAMLSpecFlagEnum::One)\n\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::OneHundred), %(--- 4\\n))\n        converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::OneHundred)).should eq(YAMLSpecFlagEnum::OneHundred)\n\n        combined = YAMLSpecFlagEnum::OneHundred | YAMLSpecFlagEnum::One\n        assert_yaml_document_end(converter.to_yaml(combined), %(--- 5\\n))\n        converter.from_yaml(converter.to_yaml(combined)).should eq(combined)\n\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::None), %(--- 0\\n))\n        converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::None)).should eq(YAMLSpecFlagEnum::None)\n\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::All), %(--- 7\\n))\n        converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::All)).should eq(YAMLSpecFlagEnum::All)\n\n        assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum.new(42)), \"--- 42\\n\")\n      end\n    end\n\n    it \"does for utc time\" do\n      time = Time.utc(2010, 11, 12, 1, 2, 3)\n      assert_yaml_document_end(time.to_yaml, \"--- 2010-11-12 01:02:03.000000000\\n\")\n    end\n\n    it \"does for time at date\" do\n      time = Time.utc(2010, 11, 12)\n      assert_yaml_document_end(time.to_yaml, \"--- 2010-11-12\\n\")\n    end\n\n    it \"does for utc time with nanoseconds\" do\n      time = Time.utc(2010, 11, 12, 1, 2, 3, nanosecond: 456_000_000)\n      assert_yaml_document_end(time.to_yaml, \"--- 2010-11-12 01:02:03.456000000\\n\")\n    end\n\n    it \"does for time location\" do\n      location = Time::Location.load(\"UTC\")\n      assert_yaml_document_end(location.to_yaml, \"--- UTC\\n\")\n    end\n\n    it \"does for bytes\" do\n      yaml = \"hello\".to_slice.to_yaml\n\n      if YAML.libyaml_version < SemanticVersion.new(0, 2, 2)\n        yaml.should eq(\"--- !!binary 'aGVsbG8=\\n\\n'\\n\")\n      else\n        yaml.should eq(\"--- !!binary 'aGVsbG8=\\n\\n  '\\n\")\n      end\n    end\n\n    it \"does a full document\" do\n      data = {\n        :hello   => \"World\",\n        :integer => 2,\n        :float   => 3.5,\n        :hash    => {\n          :a => 1,\n          :b => 2,\n        },\n        :array => [1, 2, 3],\n        :null  => nil,\n      }\n\n      expected = /\\A---\\nhello: World\\ninteger: 2\\nfloat: 3.5\\nhash:\\n  a: 1\\n  b: 2\\narray:\\n- 1\\n- 2\\n- 3\\nnull: ?\\n\\z/\n\n      data.to_yaml.should match(expected)\n    end\n\n    it \"writes to a stream\" do\n      string = String.build do |str|\n        %w(a b c).to_yaml(str)\n      end\n      string.should eq(\"---\\n- a\\n- b\\n- c\\n\")\n    end\n\n    it \"serializes recursive data structures\" do\n      a = [] of YamlRec\n      a << 1\n      a << a\n\n      a.to_yaml.should eq(\"--- &1\\n- 1\\n- *1\\n\")\n\n      h = {} of YamlRec => YamlRec\n      h[1] = 2\n      h[h] = h\n\n      h.to_yaml.should match(/\\A--- &1\\n1: 2\\n\\*1 ?: \\*1\\n\\z/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/yaml_pull_parser_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\n\nmodule YAML\n  describe PullParser do\n    it \"reads empty stream\" do\n      parser = PullParser.new(\"\")\n      parser.kind.should eq(EventKind::STREAM_START)\n      parser.read_next.should eq(EventKind::STREAM_END)\n      parser.kind.should eq(EventKind::STREAM_END)\n    end\n\n    it \"reads an empty document\" do\n      parser = PullParser.new(\"---\\n...\\n\")\n      parser.read_stream do\n        parser.read_document do\n          parser.read_scalar.should eq(\"\")\n        end\n      end\n    end\n\n    it \"reads a scalar\" do\n      parser = PullParser.new(\"--- foo\\n...\\n\")\n      parser.read_stream do\n        parser.read_document do\n          parser.read_scalar.should eq(\"foo\")\n        end\n      end\n    end\n\n    it \"reads a scalar having a null character\" do\n      parser = PullParser.new(%(--- \"foo\\\\0bar\"\\n...\\n))\n      parser.read_stream do\n        parser.read_document do\n          parser.read_scalar.should eq(\"foo\\0bar\")\n        end\n      end\n    end\n\n    it \"reads a sequence\" do\n      parser = PullParser.new(\"---\\n- 1\\n- 2\\n- 3\\n\")\n      parser.read_stream do\n        parser.read_document do\n          parser.read_sequence do\n            parser.read_scalar.should eq(\"1\")\n            parser.read_scalar.should eq(\"2\")\n            parser.read_scalar.should eq(\"3\")\n          end\n        end\n      end\n    end\n\n    it \"reads a scalar with an anchor\" do\n      parser = PullParser.new(\"--- &foo bar\\n...\\n\")\n      parser.read_stream do\n        parser.read_document do\n          parser.anchor.should eq(\"foo\")\n          parser.read_scalar.should eq(\"bar\")\n        end\n      end\n    end\n\n    it \"reads a sequence with an anchor\" do\n      parser = PullParser.new(\"--- &foo []\\n\")\n      parser.read_stream do\n        parser.read_document do\n          parser.anchor.should eq(\"foo\")\n          parser.read_sequence do\n          end\n        end\n      end\n    end\n\n    it \"reads a mapping\" do\n      parser = PullParser.new(%(---\\nfoo: 1\\nbar: 2\\n))\n      parser.read_stream do\n        parser.read_document do\n          parser.read_mapping do\n            parser.read_scalar.should eq(\"foo\")\n            parser.read_scalar.should eq(\"1\")\n            parser.read_scalar.should eq(\"bar\")\n            parser.read_scalar.should eq(\"2\")\n          end\n        end\n      end\n    end\n\n    it \"reads a mapping with an anchor\" do\n      parser = PullParser.new(%(---\\n&lala {}\\n))\n      parser.read_stream do\n        parser.read_document do\n          parser.anchor.should eq(\"lala\")\n          parser.read_mapping do\n          end\n        end\n      end\n    end\n\n    it \"parses alias\" do\n      parser = PullParser.new(\"--- *foo\\n\")\n      parser.read_stream do\n        parser.read_document do\n          parser.read_alias.should eq(\"foo\")\n        end\n      end\n    end\n\n    it \"raises exception at correct location\" do\n      parser = PullParser.new(\"[1]\")\n      parser.read_stream do\n        parser.read_document do\n          parser.read_sequence do\n            ex = expect_raises(YAML::ParseException) do\n              parser.read_mapping do\n              end\n            end\n            ex.location.should eq({1, 2})\n\n            parser.read_scalar\n          end\n        end\n      end\n    end\n\n    describe \"skip\" do\n      it \"scalar\" do\n        parser = PullParser.new(\"[1, 2]\")\n        parser.read_stream do\n          parser.read_document do\n            parser.read_sequence do\n              parser.skip\n              parser.read_scalar.should eq(\"2\")\n            end\n          end\n        end\n      end\n\n      it \"alias\" do\n        parser = PullParser.new(<<-YAML)\n          - &value 1\n          - *value\n          - 2\n          YAML\n        parser.read_stream do\n          parser.read_document do\n            parser.read_sequence do\n              parser.read_scalar.should eq(\"1\")\n              parser.skip\n              parser.read_scalar.should eq(\"2\")\n            end\n          end\n        end\n      end\n\n      it \"sequence\" do\n        parser = PullParser.new(\"[[1, [2]], 3]\")\n        parser.read_stream do\n          parser.read_document do\n            parser.read_sequence do\n              parser.skip\n              parser.read_scalar.should eq(\"3\")\n            end\n          end\n        end\n      end\n\n      it \"mapping\" do\n        parser = PullParser.new(%([{\"foo\": [1, 2]}, 3]))\n        parser.read_stream do\n          parser.read_document do\n            parser.read_sequence do\n              parser.skip\n              parser.read_scalar.should eq(\"3\")\n            end\n          end\n        end\n      end\n\n      it \"stream\" do\n        parser = PullParser.new(\"[1]\")\n        parser.skip\n        parser.read_next.should eq(EventKind::NONE)\n      end\n\n      it \"document\" do\n        parser = PullParser.new(\"[1]\")\n        parser.read_stream do\n          parser.skip\n        end\n        parser.read_next.should eq(EventKind::NONE)\n      end\n\n      it \"skips event in other cases\" do\n        parser = PullParser.new(%([ {\"foo\": 1}]))\n        parser.kind.should eq(EventKind::STREAM_START)\n        parser.read_next.should eq(EventKind::DOCUMENT_START)\n        parser.read_next.should eq(EventKind::SEQUENCE_START)\n        parser.read_next.should eq(EventKind::MAPPING_START)\n        parser.read_next.should eq(EventKind::SCALAR)\n        parser.read_next.should eq(EventKind::SCALAR)\n        parser.skip\n        parser.kind.should eq(EventKind::MAPPING_END)\n        parser.skip\n        parser.kind.should eq(EventKind::SEQUENCE_END)\n        parser.skip\n        parser.kind.should eq(EventKind::DOCUMENT_END)\n        parser.skip\n        parser.kind.should eq(EventKind::STREAM_END)\n        parser.skip\n        parser.kind.should eq(EventKind::NONE)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std/yaml/yaml_spec.cr",
    "content": "require \"spec\"\nrequire \"yaml\"\n\ndescribe \"YAML\" do\n  describe \"parser\" do\n    it { YAML.parse(\"foo\").should eq(\"foo\") }\n    it { YAML.parse(\"- foo\\n- bar\").should eq([\"foo\", \"bar\"]) }\n    it { YAML.parse_all(\"---\\nfoo\\n---\\nbar\\n\").should eq([\"foo\", \"bar\"]) }\n    it { YAML.parse(\"foo: bar\").should eq({\"foo\" => \"bar\"}) }\n    it { YAML.parse(\"--- []\\n\").should eq([] of YAML::Any) }\n    it { YAML.parse(\"---\\n...\").should eq YAML::Any.new(nil) }\n\n    it \"parses recursive sequence\" do\n      doc = YAML.parse(\"--- &foo\\n- *foo\\n\")\n      doc[0].as_a.should be(doc.raw.as(Array))\n    end\n\n    it \"parses recursive mapping\" do\n      doc = YAML.parse(%(--- &1\n        friends:\n        - *1\n        ))\n      doc[\"friends\"][0].as_h.should be(doc.as_h)\n    end\n\n    it \"parses alias to scalar\" do\n      doc = YAML.parse(\"---\\n- &x foo\\n- *x\\n\")\n      doc.should eq([\"foo\", \"foo\"])\n      doc[0].as_s.should be(doc[1].as_s)\n    end\n\n    describe \"merging with << key\" do\n      it \"merges other mapping\" do\n        doc = YAML.parse(%(---\n          foo: bar\n          <<:\n            baz: foobar\n          ))\n        doc[\"baz\"]?.should eq(\"foobar\")\n      end\n\n      it \"raises if merging with missing alias\" do\n        expect_raises(YAML::ParseException, \"Unknown anchor 'bar'\") do\n          YAML.parse(%(---\n            foo:\n              <<: *bar\n          ))\n        end\n      end\n\n      it \"merges other mapping with alias\" do\n        doc = YAML.parse(%(---\n          foo: &x\n            bar: 1\n            baz: 2\n          bar:\n            <<: *x\n          ))\n        doc[\"bar\"].should eq({\"bar\" => 1, \"baz\" => 2})\n      end\n\n      it \"merges other mapping with array of alias\" do\n        doc = YAML.parse(%(---\n          foo: &x\n            bar: 1\n          bar: &y\n            baz: 2\n          bar:\n            <<: [*x, *y]\n          ))\n        doc[\"bar\"].should eq({\"bar\" => 1, \"baz\" => 2})\n      end\n\n      it \"doesn't merge explicit string key <<\" do\n        doc = YAML.parse(%(---\n          foo: &foo\n            hello: world\n          bar:\n            !!str '<<': *foo\n        ))\n        doc.should eq({\"foo\" => {\"hello\" => \"world\"}, \"bar\" => {\"<<\" => {\"hello\" => \"world\"}}})\n      end\n\n      it \"doesn't merge empty mapping\" do\n        doc = YAML.parse(%(---\n          foo: &foo\n          bar:\n            <<: *foo\n        ))\n        doc[\"bar\"].should eq({\"<<\" => nil})\n      end\n\n      it \"doesn't merge arrays\" do\n        doc = YAML.parse(%(---\n          foo: &foo\n            - 1\n          bar:\n            <<: *foo\n        ))\n        doc[\"bar\"].should eq({\"<<\" => [1]})\n      end\n\n      it \"has correct line/number info (#2585)\" do\n        begin\n          YAML.parse <<-YAML\n            ---\n            level_one:\n            - name: \"test\"\n               attributes:\n                 one: \"broken\"\n            YAML\n          fail \"expected YAML.parse to raise\"\n        rescue ex : YAML::ParseException\n          ex.line_number.should eq(4)\n          ex.column_number.should eq(4)\n        end\n      end\n\n      it \"has correct line/number info (2)\" do\n        begin\n          parser = YAML::PullParser.new <<-MSG\n\n              authors:\n                - [foo] bar\n            MSG\n\n          parser.read_stream do\n            parser.read_document do\n              parser.read_scalar\n            end\n          end\n        rescue ex : YAML::ParseException\n          ex.line_number.should eq(2)\n          ex.column_number.should eq(3)\n        end\n      end\n\n      it \"has correct message (#4006)\" do\n        expect_raises YAML::ParseException, \"could not find expected ':' at line 4, column 1, while scanning a simple key at line 3, column 5\" do\n          YAML.parse <<-YAML\n            a:\n              - \"b\": >\n                c\n            YAML\n        end\n      end\n\n      it \"parses from IO\" do\n        YAML.parse(IO::Memory.new(\"- foo\\n- bar\")).should eq([\"foo\", \"bar\"])\n      end\n    end\n  end\n\n  describe \"dump\" do\n    it \"returns YAML as a string\" do\n      YAML.dump(%w(1 2 3)).should eq(%(---\\n- \"1\"\\n- \"2\"\\n- \"3\"\\n))\n    end\n\n    it \"writes YAML to a stream\" do\n      string = String.build do |str|\n        YAML.dump(%w(1 2 3), str)\n      end\n      string.should eq(%(---\\n- \"1\"\\n- \"2\"\\n- \"3\"\\n))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/std_spec.cr",
    "content": "require \"./support/mt_abort_timeout\"\nrequire \"./std/**\"\n"
  },
  {
    "path": "spec/support/channel.cr",
    "content": "enum SpecChannelStatus\n  Begin\n  End\n  Timeout\nend\n\ndef schedule_timeout(c : Channel(SpecChannelStatus))\n  spawn do\n    {% if flag?(:interpreted) %}\n      # TODO: it's not clear why some interpreter specs\n      # take more than 1 second in some cases.\n      # See #12429.\n      sleep 5.seconds\n    {% else %}\n      sleep 1.second\n    {% end %}\n    c.send(SpecChannelStatus::Timeout)\n  end\nend\n"
  },
  {
    "path": "spec/support/env.cr",
    "content": "def with_env(values : Hash, &)\n  old_values = {} of String => String?\n  begin\n    values.each do |key, value|\n      key = key.to_s\n      old_values[key] = ENV[key]?\n      ENV[key] = value\n    end\n\n    yield\n  ensure\n    old_values.each do |key, old_value|\n      ENV[key] = old_value\n    end\n  end\nend\n\ndef with_env(**values, &)\n  with_env(values.to_h) { yield }\nend\n"
  },
  {
    "path": "spec/support/fibers.cr",
    "content": "def wait_until_blocked(f : Fiber, timeout = 5.seconds)\n  now = Time.instant\n\n  until f.resumable?\n    Fiber.yield\n    raise \"Fiber failed to block within #{timeout}\" if now.elapsed > timeout\n  end\nend\n\ndef wait_until_finished(f : Fiber, timeout = 5.seconds)\n  now = Time.instant\n  until f.dead?\n    Fiber.yield\n    raise \"Fiber failed to finish within #{timeout}\" if now.elapsed > timeout\n  end\nend\n\n# Fake stack for `makecontext` to have somewhere to write in #initialize. We\n# don't actually run the fiber. The worst case is windows with ~300 bytes (with\n# shadow space and alignment taken into account). We allocate more to be safe.\nFAKE_FIBER_STACK = GC.malloc(512)\n\ndef new_fake_fiber(name = nil)\n  stack = Fiber::Stack.new(FAKE_FIBER_STACK, FAKE_FIBER_STACK + 512)\n  Fiber.new(name, stack) { }\nend\n"
  },
  {
    "path": "spec/support/finalize.cr",
    "content": "module FinalizeState\n  @@count = {} of String => Int64\n\n  def self.inc(key : String)\n    @@count[key] = @@count.fetch(key, 0i64) + 1\n  end\n\n  def self.count(key : String)\n    @@count.fetch(key, 0i64)\n  end\n\n  def self.reset\n    @@count.clear\n  end\nend\n\nmodule FinalizeCounter\n  macro included\n    property key : String?\n\n    def finalize\n      if key = @key\n        FinalizeState.inc(key)\n      end\n\n      {% if @type.has_method?(:finalize) %}\n        previous_def\n      {% end %}\n    end\n  end\nend\n\ndef assert_finalizes(key : String, &)\n  FinalizeState.reset\n  FinalizeState.count(key).should eq(0)\n\n  10.times do\n    obj = yield\n    obj.key = key\n  end\n\n  GC.collect\n\n  FinalizeState.count(key).should be > 0\nend\n"
  },
  {
    "path": "spec/support/interpreted.cr",
    "content": "require \"spec\"\n\n{% if flag?(:interpreted) %}\n  def pending_interpreted(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    pending(\"#{description} [interpreted]\", file, line, end_line)\n  end\n\n  def pending_interpreted(*, describe, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    pending_interpreted(describe, file, line, end_line) { }\n  end\n\n  def pending_interpreted!(msg = \"Cannot run example\", file = __FILE__, line = __LINE__)\n    pending!(msg, file, line)\n  end\n{% else %}\n  def pending_interpreted(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    it(description, file, line, end_line, &block)\n  end\n\n  def pending_interpreted(*, describe, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    describe(describe, file, line, end_line, &block)\n  end\n\n  def pending_interpreted!(msg = \"Cannot run example\", file = __FILE__, line = __LINE__)\n  end\n{% end %}\n"
  },
  {
    "path": "spec/support/io.cr",
    "content": "class RaiseIOError < IO\n  getter writes = 0\n\n  def initialize(@raise_on_write = false)\n  end\n\n  def read(slice : Bytes)\n    raise IO::Error.new(\"...\")\n  end\n\n  def write(slice : Bytes) : Nil\n    @writes += 1\n    raise IO::Error.new(\"...\") if @raise_on_write\n  end\n\n  def flush\n    raise IO::Error.new(\"...\")\n  end\nend\n"
  },
  {
    "path": "spec/support/mt_abort_timeout.cr",
    "content": "{% skip_file unless flag?(:preview_mt) %}\n\nprivate SPEC_TIMEOUT = 15.seconds\n\nSpec.around_each do |example|\n  done = Channel(Exception?).new\n\n  spawn(same_thread: !{{flag?(:execution_context)}}) do\n    begin\n      example.run\n    rescue e\n      done.send(e)\n    else\n      done.send(nil)\n    end\n  end\n\n  timeout = SPEC_TIMEOUT\n  if example.example.all_tags.includes?(\"slow\")\n    timeout *= 4\n  end\n\n  select\n  when res = done.receive\n    raise res if res\n  when timeout(timeout)\n    _it = example.example\n    ex = Spec::AssertionFailed.new(\"spec timed out after #{timeout}\", _it.file, _it.line)\n    _it.parent.report(:fail, _it.description, _it.file, _it.line, timeout, ex)\n  end\nend\n"
  },
  {
    "path": "spec/support/number.cr",
    "content": "require \"spec/helpers/iterate\"\n\n# Helper methods to describe the behavior of numbers of different types\nBUILTIN_NUMBER_TYPES =\n  [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, Float32, Float64]\nBUILTIN_INTEGER_TYPES =\n  [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128]\nBUILTIN_INT_CONVERSIONS = {\n  to_i: Int32, to_u: UInt32,\n  to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,\n  to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,\n}\nBUILTIN_NUMBER_TYPES_LTE_64 =\n  [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64]\nBUILTIN_FLOAT_TYPES =\n  [Float32, Float64]\nBUILTIN_NUMBER_SUFFIXES = {\n  Int8    => \"i8\",\n  Int16   => \"i16\",\n  Int32   => \"i32\",\n  Int64   => \"i64\",\n  Int128  => \"i128\",\n  UInt8   => \"u8\",\n  UInt16  => \"u16\",\n  UInt32  => \"u32\",\n  UInt64  => \"u64\",\n  UInt128 => \"u128\",\n  Float32 => \"f32\",\n  Float64 => \"f64\",\n}\n\nmacro it_can_convert_between(a_types, b_types)\n  {% for a_type in a_types %}\n    {% for b_type in b_types %}\n      it \"converts from {{a_type}} to {{b_type}}\" do\n        {{b_type}}.new({{a_type}}.new(1)).should be_a({{b_type}})\n      end\n\n      it \"converts from {{b_type}} to {{a_type}}\" do\n        {{a_type}}.new({{b_type}}.new(1)).should be_a({{a_type}})\n      end\n    {% end %}\n  {% end %}\nend\n\nmacro it_initializes_from_value_to(number_type)\n  it \"initialize from value to {{number_type}}\" do\n    {{number_type}}.new(1).should be_a({{number_type}})\n    {{number_type}}.new(1).should eq(1)\n\n    {{number_type}}.new(1u32).should be_a({{number_type}})\n    {{number_type}}.new(1u32).should eq(1)\n\n    {{number_type}}.new(1.0).should be_a({{number_type}})\n    {{number_type}}.new(1.0).should eq(1)\n  end\nend\n\nmacro it_unchecked_initializes_from_value_to(number_type)\n  it \"unchecked initialize from value to {{number_type}}\" do\n    {{number_type}}.new!(1).should be_a({{number_type}})\n    {{number_type}}.new!(1).should eq(1)\n\n    {{number_type}}.new!(1u32).should be_a({{number_type}})\n    {{number_type}}.new!(1u32).should eq(1)\n\n    {{number_type}}.new!(1.0).should be_a({{number_type}})\n    {{number_type}}.new!(1.0).should eq(1)\n  end\nend\n\nmacro division_between_returns(a_types, b_types, result_type)\n  {% for a_type in a_types %}\n    {% for b_type in b_types %}\n      it \"division between {{a_type}} / {{b_type}} returns {{result_type}}\" do\n        r = {{a_type}}.new(1) / {{b_type}}.new(1)\n        r.should be_a({{result_type}})\n        r.should eq(1)\n      end\n    {% end %}\n  {% end %}\nend\n\nmacro floor_division_returns_lhs_type(a_types, b_types)\n  {% for a_type in a_types %}\n    {% for b_type in b_types %}\n      it \"floor_division {{a_type}} // {{b_type}} returns {{a_type}}\" do\n        r = {{a_type}}.new(5) // {{b_type}}.new(2)\n        r.should be_a({{a_type}})\n        r.should eq(2)\n      end\n    {% end %}\n  {% end %}\nend\n\n# TODO test to_X conversions return types\n# TODO test zero? comparisons\n# TODO test <=> comparisons between types\n\n# Calls either `Float64.parse_hexfloat` or `Float32.parse_hexfloat`. The default\n# is `Float64` unless *str* ends with `_f32`, in which case that suffix is\n# stripped and `Float32` is chosen.\nmacro hexfloat(str)\n  {% raise \"`str` must be a StringLiteral, not #{str.class_name}\" unless str.is_a?(StringLiteral) %}\n  {% if str.ends_with?(\"_f32\") %}\n    ::Float32.parse_hexfloat({{ str[0...-4] }})\n  {% else %}\n    ::Float64.parse_hexfloat({{ str }})\n  {% end %}\nend\n\n# See also: https://github.com/crystal-lang/crystal/issues/15192\nlib LibC\n  {% if flag?(:win32) %}\n    FE_TONEAREST  = 0x00000000\n    FE_DOWNWARD   = 0x00000100\n    FE_UPWARD     = 0x00000200\n    FE_TOWARDZERO = 0x00000300\n  {% else %}\n    FE_TONEAREST  = 0x00000000\n    FE_DOWNWARD   = 0x00000400\n    FE_UPWARD     = 0x00000800\n    FE_TOWARDZERO = 0x00000C00\n  {% end %}\n\n  fun fegetround : Int\n  fun fesetround(round : Int) : Int\nend\n\ndef with_hardware_rounding_mode(mode, &)\n  old_mode = LibC.fegetround\n  LibC.fesetround(mode)\n  yield ensure LibC.fesetround(old_mode)\nend\n\ndef each_hardware_rounding_mode(&)\n  {% for mode in %w(FE_TONEAREST FE_DOWNWARD FE_UPWARD FE_TOWARDZERO) %}\n    with_hardware_rounding_mode(LibC::{{ mode.id }}) do\n      yield LibC::{{ mode.id }}, {{ mode }}\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "spec/support/retry.cr",
    "content": "def retry(n = 5, &)\n  exception = nil\n  n.times do |i|\n    yield\n  rescue ex\n    exception = ex\n    if i == 0\n      Fiber.yield\n    else\n      sleep 10.milliseconds * (2**i)\n    end\n  else\n    return\n  end\n\n  raise exception.not_nil!\nend\n"
  },
  {
    "path": "spec/support/ssl.cr",
    "content": "require \"openssl\"\n\ndef ssl_context_pair\n  server_context = OpenSSL::SSL::Context::Server.new\n  server_context.certificate_chain = datapath(\"openssl\", \"openssl.crt\")\n  server_context.private_key = datapath(\"openssl\", \"openssl.key\")\n\n  client_context = OpenSSL::SSL::Context::Client.new\n  client_context.verify_mode = OpenSSL::SSL::VerifyMode::NONE\n\n  {server_context, client_context}\nend\n"
  },
  {
    "path": "spec/support/string.cr",
    "content": "VALID_UTF8_BYTE_SEQUENCES = {\n  [0x00] => '\\u{0000}',\n  [0x7F] => '\\u{007F}',\n\n  [0xC2, 0x80] => '\\u{0080}',\n  [0xC2, 0xBF] => '\\u{00BF}',\n  [0xDF, 0x80] => '\\u{07C0}',\n  [0xDF, 0xBF] => '\\u{07FF}',\n\n  [0xE0, 0xA0, 0x80] => '\\u{0800}',\n  [0xE0, 0xA0, 0xBF] => '\\u{083F}',\n  [0xE0, 0xBF, 0x80] => '\\u{0FC0}',\n  [0xE0, 0xBF, 0xBF] => '\\u{0FFF}',\n  [0xE1, 0x80, 0x80] => '\\u{1000}',\n  [0xE1, 0x80, 0xBF] => '\\u{103F}',\n  [0xE1, 0x9F, 0x80] => '\\u{17C0}',\n  [0xE1, 0x9F, 0xBF] => '\\u{17FF}',\n  [0xE1, 0xA0, 0x80] => '\\u{1800}',\n  [0xE1, 0xA0, 0xBF] => '\\u{183F}',\n  [0xE1, 0xBF, 0x80] => '\\u{1FC0}',\n  [0xE1, 0xBF, 0xBF] => '\\u{1FFF}',\n  [0xED, 0x80, 0x80] => '\\u{D000}',\n  [0xED, 0x80, 0xBF] => '\\u{D03F}',\n  [0xED, 0x9F, 0x80] => '\\u{D7C0}',\n  [0xED, 0x9F, 0xBF] => '\\u{D7FF}',\n  [0xEF, 0x80, 0x80] => '\\u{F000}',\n  [0xEF, 0x80, 0xBF] => '\\u{F03F}',\n  [0xEF, 0x9F, 0x80] => '\\u{F7C0}',\n  [0xEF, 0x9F, 0xBF] => '\\u{F7FF}',\n  [0xEF, 0xA0, 0x80] => '\\u{F800}',\n  [0xEF, 0xA0, 0xBF] => '\\u{F83F}',\n  [0xEF, 0xBF, 0x80] => '\\u{FFC0}',\n  [0xEF, 0xBF, 0xBF] => '\\u{FFFF}',\n\n  [0xF0, 0x90, 0x80, 0x80] => '\\u{10000}',\n  [0xF0, 0x90, 0x80, 0xBF] => '\\u{1003F}',\n  [0xF0, 0x90, 0xBF, 0x80] => '\\u{10FC0}',\n  [0xF0, 0x90, 0xBF, 0xBF] => '\\u{10FFF}',\n  [0xF0, 0xBF, 0x80, 0x80] => '\\u{3F000}',\n  [0xF0, 0xBF, 0x80, 0xBF] => '\\u{3F03F}',\n  [0xF0, 0xBF, 0xBF, 0x80] => '\\u{3FFC0}',\n  [0xF0, 0xBF, 0xBF, 0xBF] => '\\u{3FFFF}',\n  [0xF1, 0x80, 0x80, 0x80] => '\\u{40000}',\n  [0xF1, 0x80, 0x80, 0xBF] => '\\u{4003F}',\n  [0xF1, 0x80, 0xBF, 0x80] => '\\u{40FC0}',\n  [0xF1, 0x80, 0xBF, 0xBF] => '\\u{40FFF}',\n  [0xF1, 0x8F, 0x80, 0x80] => '\\u{4F000}',\n  [0xF1, 0x8F, 0x80, 0xBF] => '\\u{4F03F}',\n  [0xF1, 0x8F, 0xBF, 0x80] => '\\u{4FFC0}',\n  [0xF1, 0x8F, 0xBF, 0xBF] => '\\u{4FFFF}',\n  [0xF3, 0x90, 0x80, 0x80] => '\\u{D0000}',\n  [0xF3, 0xBF, 0xBF, 0xBF] => '\\u{FFFFF}',\n  [0xF4, 0x80, 0x80, 0x80] => '\\u{100000}',\n  [0xF4, 0x80, 0x80, 0xBF] => '\\u{10003F}',\n  [0xF4, 0x80, 0xBF, 0x80] => '\\u{100FC0}',\n  [0xF4, 0x80, 0xBF, 0xBF] => '\\u{100FFF}',\n  [0xF4, 0x8F, 0x80, 0x80] => '\\u{10F000}',\n  [0xF4, 0x8F, 0xBF, 0xBF] => '\\u{10FFFF}',\n}\n\nINVALID_UTF8_BYTE_SEQUENCES = [\n  # non-starters\n  [0x80],\n  [0x8F],\n  [0x90],\n  [0x9F],\n  [0xA0],\n  [0xAF],\n  [0xB0],\n  [0xBF],\n  [0xFE],\n  [0xFF],\n\n  # incomplete, 2-byte\n  [0xC2],\n  [0xC2, 0x00],\n  [0xC2, 0xC2],\n\n  # overlong, 2-byte\n  [0xC0, 0x80],\n  [0xC1, 0xBF],\n\n  # incomplete, 3-byte\n  [0xE1],\n  [0xE1, 0x00],\n  [0xE1, 0xC2],\n  [0xE1, 0x80],\n  [0xE1, 0x80, 0x00],\n  [0xE1, 0x80, 0xC2],\n\n  # overlong, 3-byte\n  [0xE0, 0x80, 0x80],\n  [0xE0, 0x9F, 0xBF],\n\n  # surrogate pairs\n  [0xED, 0xA0, 0x80],\n  [0xED, 0xBF, 0xBF],\n\n  # incomplete, 4-byte\n  [0xF1],\n  [0xF1, 0x00],\n  [0xF1, 0xC2],\n  [0xF1, 0x80],\n  [0xF1, 0x80, 0x00],\n  [0xF1, 0x80, 0xC2],\n  [0xF1, 0x80, 0x80],\n  [0xF1, 0x80, 0x80, 0x00],\n  [0xF1, 0x80, 0x80, 0xC2],\n\n  # overlong, 4-byte\n  [0xF0, 0x80, 0x80, 0x80],\n  [0xF0, 0x8F, 0xBF, 0xBF],\n\n  # upper boundary, 4-byte\n  [0xF4, 0x90, 0x80, 0x80],\n  [0xF5],\n\n  # 5-byte (obsolete)\n  [0xF8],\n  [0xF8, 0x80, 0x80, 0x80, 0x80],\n\n  # 6-byte (obsolete)\n  [0xFC],\n  [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80],\n]\n"
  },
  {
    "path": "spec/support/syntax.cr",
    "content": "require \"spec\"\nrequire \"../../src/compiler/crystal/syntax\"\n\ninclude Crystal\n\nstruct Number\n  def int32\n    NumberLiteral.new to_s, :i32\n  end\n\n  def int64\n    NumberLiteral.new to_s, :i64\n  end\n\n  def int128\n    NumberLiteral.new to_s, :i128\n  end\n\n  def uint128\n    NumberLiteral.new to_s, :u128\n  end\n\n  def float32\n    NumberLiteral.new to_f32.to_s, :f32\n  end\n\n  def float64\n    NumberLiteral.new to_f64.to_s, :f64\n  end\nend\n\nstruct Bool\n  def bool\n    BoolLiteral.new self\n  end\nend\n\nclass Array\n  def array\n    ArrayLiteral.new self\n  end\n\n  def array_of(type)\n    ArrayLiteral.new self, type\n  end\n\n  def path\n    Crystal::Path.new self\n  end\nend\n\nclass String\n  def var\n    Var.new self\n  end\n\n  def ann\n    Annotation.new path\n  end\n\n  def arg(default_value = nil, restriction = nil, external_name = nil, annotations = nil)\n    Arg.new self, default_value: default_value, restriction: restriction, external_name: external_name, parsed_annotations: annotations\n  end\n\n  def call\n    Call.new nil, self\n  end\n\n  def call(args : Array)\n    Call.new nil, self, args\n  end\n\n  def call(arg : ASTNode)\n    Call.new nil, self, [arg] of ASTNode\n  end\n\n  def call(arg1 : ASTNode, arg2 : ASTNode)\n    Call.new nil, self, [arg1, arg2] of ASTNode\n  end\n\n  def path(global = false)\n    Crystal::Path.new self, global\n  end\n\n  def instance_var\n    InstanceVar.new self\n  end\n\n  def class_var\n    ClassVar.new self\n  end\n\n  def string\n    StringLiteral.new self\n  end\n\n  def string_interpolation\n    StringInterpolation.new([self.string] of ASTNode)\n  end\n\n  def float32\n    NumberLiteral.new self, :f32\n  end\n\n  def float64\n    NumberLiteral.new self, :f64\n  end\n\n  def symbol\n    SymbolLiteral.new self\n  end\n\n  def static_array_of(size : Int)\n    static_array_of NumberLiteral.new(size)\n  end\n\n  def static_array_of(size : ASTNode)\n    Generic.new(Crystal::Path.global(\"StaticArray\"), [path, size] of ASTNode)\n  end\n\n  def macro_literal\n    MacroLiteral.new(self)\n  end\nend\n\nclass Crystal::ASTNode\n  def pointer_of\n    Generic.new(Crystal::Path.global(\"Pointer\"), [self] of ASTNode)\n  end\n\n  def splat\n    Splat.new(self)\n  end\nend\n\ndef assert_syntax_error(str, message = nil, line = nil, column = nil, metafile = __FILE__, metaline = __LINE__, metaendline = __END_LINE__, *, focus : Bool = false)\n  it \"says syntax error on #{str.inspect}\", metafile, metaline, metaendline, focus: focus do\n    ex = expect_raises(SyntaxException, message, file: metafile, line: metaline) do\n      parse str\n    end\n    if line\n      unless ex.line_number == line\n        fail \"Expected line number to be #{line} but got #{ex.line_number}\", metafile, metaline\n      end\n    end\n    if column\n      unless ex.column_number == column\n        fail \"Expected column number to be #{column} but got #{ex.column_number}\", metafile, metaline\n      end\n    end\n  end\nend\n\ndef assert_syntax_warning(str, message, *, metafile = __FILE__, metaline = __LINE__, metaendline = __END_LINE__)\n  it \"says syntax warning on #{str.inspect}\", metafile, metaline, metaendline do\n    warnings = WarningCollection.new\n    parse str, false, nil, warnings\n    unless warnings.infos.find(&.ends_with?(message))\n      fail \"Expected warnings to include '#{message}'\"\n    end\n  end\nend\n\ndef parse(string, wants_doc = false, filename = nil, warnings = nil)\n  parser = Parser.new(string, warnings: warnings)\n  parser.warnings = warnings if warnings\n  parser.wants_doc = wants_doc\n  if filename\n    parser.filename = filename\n  end\n  parser.parse\nend\n"
  },
  {
    "path": "spec/support/tempfile.cr",
    "content": "require \"file_utils\"\nrequire \"./interpreted\"\n{% if flag?(:msvc) %}\n  require \"crystal/system/win32/visual_studio\"\n{% end %}\n\nSPEC_TEMPFILE_PATH    = File.join(Dir.tempdir, \"cr-spec-#{Random.new.hex(4)}\")\nSPEC_TEMPFILE_CLEANUP = ENV[\"SPEC_TEMPFILE_CLEANUP\"]? != \"0\"\n\n# Expands *paths* in a unique temp folder and yield them to the block.\n#\n# The *paths* are interpreted relative to a unique folder for every spec run and\n# prefixed by the name of the spec file that requests them.\n#\n# The constructed path is yielded to the block and cleaned up afterwards.\n#\n# Paths should still be uniquely chosen inside a spec file. This helper\n# ensures they're placed in the temporary location (`Dir.tempdir`),\n# avoids name clashes between parallel spec runs and cleans up afterwards.\n#\n# The unique directory for the spec run is removed `at_exit`.\n#\n# If the environment variable `SPEC_TEMPFILE_CLEANUP` is set to `0`, no paths\n# will be cleaned up, enabling easier debugging.\ndef with_tempfile(*paths, file = __FILE__, &)\n  calling_spec = File.basename(file).rchop(\"_spec.cr\")\n  paths = paths.map { |path| File.join(SPEC_TEMPFILE_PATH, calling_spec, path) }\n  FileUtils.mkdir_p(File.join(SPEC_TEMPFILE_PATH, calling_spec))\n\n  begin\n    yield *paths\n  ensure\n    if SPEC_TEMPFILE_CLEANUP\n      paths.each do |path|\n        FileUtils.rm_rf(path) if File.exists?(path)\n      end\n    end\n  end\nend\n\n# Creates a temporary directory and exposes it as working directory in the\n# block.\n#\n# Use the `name` parameter to specify the name of the directory.\n# It should be unique per spec file. If nil, a random name is chosen\n# (`File.tempname`).\n#\n# The lifetime of the temporary directory is the same was for `.with_tempfile`.\ndef with_tempdir(name : String? = nil, *, file = __FILE__, &)\n  name ||= File.tempname(dir: \".\")\n  with_tempfile(name, file: file) do |path|\n    Dir.mkdir_p(path)\n    Dir.cd(path) do\n      yield\n    end\n  end\nend\n\ndef with_temp_executable(name, file = __FILE__, &)\n  {% if flag?(:win32) %}\n    name += \".exe\"\n  {% end %}\n  with_tempfile(name, file: file) do |tempname|\n    yield tempname\n  end\nend\n\ndef with_temp_c_object_file(c_code, *, filename = \"temp_c\", file = __FILE__, &)\n  # can't use backtick in interpreted code (#12241)\n  pending_interpreted! \"Unable to compile C code in interpreted code\"\n\n  obj_ext = {{ flag?(:msvc) ? \".obj\" : \".o\" }}\n  with_tempfile(\"#{filename}.c\", \"#{filename}#{obj_ext}\", file: file) do |c_filename, o_filename|\n    File.write(c_filename, c_code)\n\n    {% if flag?(:msvc) %}\n      # following is based on `Crystal::Compiler#linker_command`\n      unless cl = ENV[\"CC\"]?\n        cl = \"cl.exe\"\n        if msvc_path = Crystal::System::VisualStudio.find_latest_msvc_path\n          # we won't be cross-compiling the specs binaries, so host and target\n          # bits are identical\n          bits = {{ flag?(:bits64) ? \"x64\" : \"x86\" }}\n          cl = Process.quote(msvc_path.join(\"bin\", \"Host#{bits}\", bits, cl).to_s)\n        end\n      end\n\n      `#{cl} /nologo /c /MD #{Process.quote(c_filename)} #{Process.quote(\"/Fo#{o_filename}\")}`.should be_truthy\n    {% else %}\n      `#{ENV[\"CC\"]? || \"cc\"} #{Process.quote(c_filename)} -c -o #{Process.quote(o_filename)}`.should be_truthy\n    {% end %}\n\n    yield o_filename\n  end\nend\n\nif SPEC_TEMPFILE_CLEANUP\n  at_exit do\n    FileUtils.rm_rf(SPEC_TEMPFILE_PATH) if Dir.exists?(SPEC_TEMPFILE_PATH)\n  end\nend\n"
  },
  {
    "path": "spec/support/thread.cr",
    "content": "{% begin %}\n  def new_thread(name = nil, &block)\n    {% if flag?(:execution_context) %}\n      Fiber::ExecutionContext::Isolated.new(name: name || \"SPEC\") { block.call }\n    {% else %}\n      Thread.new(name) { block.call }\n    {% end %}\n  end\n{% end %}\n"
  },
  {
    "path": "spec/support/time.cr",
    "content": "require \"./env\"\n\nclass Time::Location\n  def __cached_zone=(zone)\n    @cached_zone = zone\n  end\n\n  def self.__clear_location_cache\n    @@local = nil\n    @@location_cache.clear\n  end\nend\n\nZONEINFO_ZIP = datapath(\"zoneinfo.zip\")\n\ndef with_zoneinfo(path = ZONEINFO_ZIP, &)\n  with_env(\"TZDIR\": path) do\n    Time::Location.__clear_location_cache\n\n    yield\n  end\nend\n\ndef with_tz(tz, &)\n  old_local = Time::Location.local\n  begin\n    with_env(\"TZ\": tz) do\n      # Reset local time zone\n      Time::Location.local = Time::Location.load_local\n      yield\n    end\n  ensure\n    Time::Location.local = old_local\n  end\nend\n\n{% if flag?(:win32) %}\n  lib LibC\n    struct LUID\n      lowPart : DWORD\n      highPart : Long\n    end\n\n    struct LUID_AND_ATTRIBUTES\n      luid : LUID\n      attributes : DWORD\n    end\n\n    struct TOKEN_PRIVILEGES\n      privilegeCount : DWORD\n      privileges : LUID_AND_ATTRIBUTES[1]\n    end\n\n    TOKEN_QUERY             = 0x0008\n    TOKEN_ADJUST_PRIVILEGES = 0x0020\n\n    TokenPrivileges = 3\n\n    SE_PRIVILEGE_ENABLED = 0x00000002_u32\n\n    fun OpenProcessToken(processHandle : HANDLE, desiredAccess : DWORD, tokenHandle : HANDLE*) : BOOL\n    fun GetTokenInformation(tokenHandle : HANDLE, tokenInformationClass : Int, tokenInformation : Void*, tokenInformationLength : DWORD, returnLength : DWORD*) : BOOL\n    fun LookupPrivilegeValueW(lpSystemName : LPWSTR, lpName : LPWSTR, lpLuid : LUID*) : BOOL\n    fun AdjustTokenPrivileges(tokenHandle : HANDLE, disableAllPrivileges : BOOL, newState : TOKEN_PRIVILEGES*, bufferLength : DWORD, previousState : TOKEN_PRIVILEGES*, returnLength : DWORD*) : BOOL\n\n    fun SetDynamicTimeZoneInformation(lpTimeZoneInformation : DYNAMIC_TIME_ZONE_INFORMATION*) : BOOL\n  end\n\n  module Crystal::System::Time\n    private SeTimeZonePrivilege = System.wstr_literal \"SeTimeZonePrivilege\"\n\n    # Enable the `SeTimeZonePrivilege` privilege before changing the system time\n    # zone. This is necessary because the privilege is by default granted but\n    # disabled for any new process. This only needs to be done once per run.\n    class_getter?(time_zone_privilege_enabled : Bool) { detect_time_zone_privilege_enabled? }\n\n    private def self.detect_time_zone_privilege_enabled? : Bool\n      if LibC.LookupPrivilegeValueW(nil, SeTimeZonePrivilege, out time_zone_luid) == 0\n        raise RuntimeError.from_winerror(\"LookupPrivilegeValueW\")\n      end\n\n      # if the process token already has the privilege, and the privilege is already enabled,\n      # we don't need to do anything else\n      if LibC.OpenProcessToken(LibC.GetCurrentProcess, LibC::TOKEN_QUERY, out token) != 0\n        begin\n          LibC.GetTokenInformation(token, LibC::TokenPrivileges, nil, 0, out len)\n          buf = Pointer(UInt8).malloc(len).as(LibC::TOKEN_PRIVILEGES*)\n          LibC.GetTokenInformation(token, LibC::TokenPrivileges, buf, len, out _)\n          privileges = Slice.new(pointerof(buf.value.@privileges).as(LibC::LUID_AND_ATTRIBUTES*), buf.value.privilegeCount)\n          # if the process token doesn't have the privilege, there is no way\n          # `AdjustTokenPrivileges` could grant or enable it\n          privilege = privileges.find(&.luid.== time_zone_luid)\n          return false unless privilege\n          return true if privilege.attributes.bits_set?(LibC::SE_PRIVILEGE_ENABLED)\n        ensure\n          LibC.CloseHandle(token)\n        end\n      end\n\n      if LibC.OpenProcessToken(LibC.GetCurrentProcess, LibC::TOKEN_ADJUST_PRIVILEGES, out adjust_token) != 0\n        new_privileges = LibC::TOKEN_PRIVILEGES.new(\n          privilegeCount: 1,\n          privileges: StaticArray[\n            LibC::LUID_AND_ATTRIBUTES.new(\n              luid: time_zone_luid,\n              attributes: LibC::SE_PRIVILEGE_ENABLED,\n            ),\n          ],\n        )\n        if LibC.AdjustTokenPrivileges(adjust_token, 0, pointerof(new_privileges), 0, nil, nil) != 0\n          return true if WinError.value.error_success?\n        end\n      end\n\n      false\n    end\n  end\n\n  def with_system_time_zone(dtzi : LibC::DYNAMIC_TIME_ZONE_INFORMATION, *, file = __FILE__, line = __LINE__, &)\n    unless Crystal::System::Time.time_zone_privilege_enabled?\n      pending! \"Unable to set system time zone\", file: file, line: line\n    end\n\n    LibC.GetDynamicTimeZoneInformation(out old_dtzi)\n    unless LibC.SetDynamicTimeZoneInformation(pointerof(dtzi)) != 0\n      error = WinError.value\n      raise RuntimeError.from_os_error(\"Failed to set system time zone\", error) unless error.error_privilege_not_held?\n      pending! \"Unable to set system time zone\", file: file, line: line\n    end\n\n    begin\n      yield\n    ensure\n      LibC.SetDynamicTimeZoneInformation(pointerof(old_dtzi))\n    end\n  end\n{% end %}\n"
  },
  {
    "path": "spec/support/wait_for.cr",
    "content": "def wait_for(timeout = 100.milliseconds, sleeping = 10.microseconds, &)\n  now = Time.instant\n\n  Fiber.yield\n\n  until value = yield\n    sleep sleeping\n\n    if now.elapsed > timeout\n      return nil\n    end\n\n    sleeping *= 2\n  end\n  value\nend\n"
  },
  {
    "path": "spec/support/wasm32.cr",
    "content": "require \"spec\"\n\n{% if flag?(:wasm32) %}\n  def pending_wasm32(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil, &block)\n    pending(\"#{description} [wasm32]\", file, line, end_line, focus: focus, tags: tags)\n  end\n\n  def pending_wasm32(*, describe, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    pending_wasm32(describe, file, line, end_line) { }\n  end\n{% else %}\n  def pending_wasm32(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil, &block)\n    it(description, file, line, end_line, focus: focus, tags: tags, &block)\n  end\n\n  def pending_wasm32(*, describe, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    describe(describe, file, line, end_line, &block)\n  end\n{% end %}\n"
  },
  {
    "path": "spec/support/win32.cr",
    "content": "require \"spec\"\n\n{% if flag?(:win32) %}\n  def pending_win32(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    pending(\"#{description} [win32]\", file, line, end_line)\n  end\n\n  def pending_win32(*, describe, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    pending_win32(describe, file, line, end_line) { }\n  end\n{% else %}\n  def pending_win32(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    it(description, file, line, end_line, &block)\n  end\n\n  def pending_win32(*, describe, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)\n    describe(describe, file, line, end_line, &block)\n  end\n{% end %}\n"
  },
  {
    "path": "spec/syntax_spec.cr",
    "content": "# This is a spec entry point to run all specs related to syntax (parsing, formatting, tools).\n\nrequire \"./compiler/lexer/**\"\nrequire \"./compiler/parser/**\"\nrequire \"./compiler/formatter/**\"\n\nrequire \"./compiler/crystal/tools/doc_spec.cr\"\nrequire \"./compiler/crystal/tools/doc/**\"\nrequire \"./compiler/crystal/tools/flags_spec.cr\"\nrequire \"./compiler/crystal/tools/format_spec.cr\"\n\nrequire \"./std/crystal/syntax_highlighter/**\"\n"
  },
  {
    "path": "spec/wasm32_std_spec.cr",
    "content": "# This file is autogenerated by `spec/generate_wasm32_spec.sh`\n# 2022-10-06 08:12:57-03:00\n\nrequire \"./std/array_spec.cr\"\nrequire \"./std/atomic_spec.cr\"\nrequire \"./std/base64_spec.cr\"\n# require \"./std/benchmark_spec.cr\" (failed to run)\n# require \"./std/big/big_decimal_spec.cr\" (failed linking)\n# require \"./std/big/big_float_spec.cr\" (failed linking)\n# require \"./std/big/big_int_spec.cr\" (failed linking)\n# require \"./std/big/big_rational_spec.cr\" (failed linking)\nrequire \"./std/big/number_spec.cr\"\nrequire \"./std/bit_array_spec.cr\"\nrequire \"./std/bool_spec.cr\"\nrequire \"./std/box_spec.cr\"\n# require \"./std/channel_spec.cr\" (failed to run)\nrequire \"./std/char/reader_spec.cr\"\n# require \"./std/char_spec.cr\" (failed to run)\nrequire \"./std/class_spec.cr\"\nrequire \"./std/colorize_spec.cr\"\nrequire \"./std/comparable_spec.cr\"\nrequire \"./std/complex_spec.cr\"\n# require \"./std/compress/deflate/deflate_spec.cr\" (failed linking)\n# require \"./std/compress/gzip/gzip_spec.cr\" (failed linking)\n# require \"./std/compress/zip/zip_file_spec.cr\" (failed linking)\n# require \"./std/compress/zip/zip_spec.cr\" (failed linking)\n# require \"./std/compress/zlib/reader_spec.cr\" (failed linking)\n# require \"./std/compress/zlib/stress_spec.cr\" (failed linking)\n# require \"./std/compress/zlib/writer_spec.cr\" (failed linking)\n# require \"./std/concurrent/select_spec.cr\" (failed to run)\n# require \"./std/concurrent_spec.cr\" (failed to run)\nrequire \"./std/crypto/bcrypt/base64_spec.cr\"\nrequire \"./std/crypto/bcrypt/password_spec.cr\"\nrequire \"./std/crypto/bcrypt_spec.cr\"\nrequire \"./std/crypto/blowfish_spec.cr\"\nrequire \"./std/crypto/subtle_spec.cr\"\nrequire \"./std/crystal/compiler_rt/ashlti3_spec.cr\"\nrequire \"./std/crystal/compiler_rt/ashrti3_spec.cr\"\nrequire \"./std/crystal/compiler_rt/divmod128_spec.cr\"\nrequire \"./std/crystal/compiler_rt/fixint_spec.cr\"\nrequire \"./std/crystal/compiler_rt/float_spec.cr\"\nrequire \"./std/crystal/compiler_rt/lshrti3_spec.cr\"\nrequire \"./std/crystal/compiler_rt/mulodi4_spec.cr\"\nrequire \"./std/crystal/compiler_rt/mulosi4_spec.cr\"\nrequire \"./std/crystal/compiler_rt/muloti4_spec.cr\"\nrequire \"./std/crystal/compiler_rt/multi3_spec.cr\"\nrequire \"./std/crystal/compiler_rt/powidf2_spec.cr\"\nrequire \"./std/crystal/compiler_rt/powisf2_spec.cr\"\n# require \"./std/crystal/digest/md5_spec.cr\" (failed to run)\n# require \"./std/crystal/digest/sha1_spec.cr\" (failed to run)\n# require \"./std/crystal/hasher_spec.cr\" (failed linking)\nrequire \"./std/crystal/pointer_linked_list_spec.cr\"\n# require \"./std/crystal/syntax_highlighter/colorize_spec.cr\" (failed to run)\n# require \"./std/crystal/syntax_highlighter/html_spec.cr\" (failed linking)\nrequire \"./std/csv/csv_build_spec.cr\"\nrequire \"./std/csv/csv_lex_spec.cr\"\nrequire \"./std/csv/csv_parse_spec.cr\"\nrequire \"./std/csv/csv_spec.cr\"\nrequire \"./std/deque_spec.cr\"\n# require \"./std/digest/adler32_spec.cr\" (failed linking)\n# require \"./std/digest/crc32_spec.cr\" (failed linking)\n# require \"./std/digest/io_digest_spec.cr\" (failed linking)\n# require \"./std/digest/md5_spec.cr\" (failed linking)\n# require \"./std/digest/sha1_spec.cr\" (failed linking)\n# require \"./std/digest/sha256_spec.cr\" (failed linking)\n# require \"./std/digest/sha512_spec.cr\" (failed linking)\n# require \"./std/dir_spec.cr\" (failed to run)\nrequire \"./std/double_spec.cr\"\nrequire \"./std/ecr/ecr_lexer_spec.cr\"\n# require \"./std/ecr/ecr_spec.cr\" (failed linking)\nrequire \"./std/enum_spec.cr\"\nrequire \"./std/enumerable_spec.cr\"\n# require \"./std/env_spec.cr\" (failed to run)\n# require \"./std/errno_spec.cr\" (failed to run)\n# require \"./std/exception/call_stack_spec.cr\" (failed to run)\n# require \"./std/exception_spec.cr\" (failed codegen)\n# require \"./std/file/tempfile_spec.cr\" (failed to run)\n# require \"./std/file_spec.cr\" (failed linking)\n# require \"./std/file_utils_spec.cr\" (failed linking)\nrequire \"./std/float_printer/diy_fp_spec.cr\"\nrequire \"./std/float_printer/grisu3_spec.cr\"\nrequire \"./std/float_printer/ieee_spec.cr\"\n# require \"./std/float_printer_spec.cr\" (failed to run)\nrequire \"./std/float_spec.cr\"\nrequire \"./std/gc_spec.cr\"\nrequire \"./std/hash_spec.cr\"\nrequire \"./std/html_spec.cr\"\n# require \"./std/http/chunked_content_spec.cr\" (failed linking)\n# require \"./std/http/client/client_spec.cr\" (failed linking)\n# require \"./std/http/client/response_spec.cr\" (failed linking)\n# require \"./std/http/cookie_spec.cr\" (failed linking)\n# require \"./std/http/formdata/builder_spec.cr\" (failed linking)\n# require \"./std/http/formdata/parser_spec.cr\" (failed linking)\n# require \"./std/http/formdata_spec.cr\" (failed linking)\nrequire \"./std/http/headers_spec.cr\"\n# require \"./std/http/http_spec.cr\" (failed linking)\nrequire \"./std/http/params_spec.cr\"\n# require \"./std/http/request_spec.cr\" (failed linking)\n# require \"./std/http/server/handlers/compress_handler_spec.cr\" (failed linking)\n# require \"./std/http/server/handlers/error_handler_spec.cr\" (failed linking)\n# require \"./std/http/server/handlers/handler_spec.cr\" (failed linking)\n# require \"./std/http/server/handlers/log_handler_spec.cr\" (failed linking)\n# require \"./std/http/server/handlers/static_file_handler_spec.cr\" (failed linking)\n# require \"./std/http/server/handlers/websocket_handler_spec.cr\" (failed linking)\n# require \"./std/http/server/request_processor_spec.cr\" (failed linking)\n# require \"./std/http/server/response_spec.cr\" (failed linking)\n# require \"./std/http/server/server_spec.cr\" (failed linking)\n# require \"./std/http/status_spec.cr\" (failed linking)\n# require \"./std/http/web_socket_spec.cr\" (failed linking)\n# require \"./std/humanize_spec.cr\" (failed to run)\nrequire \"./std/indexable/mutable_spec.cr\"\nrequire \"./std/indexable_spec.cr\"\n# require \"./std/ini_spec.cr\" (failed to run)\n# require \"./std/int_spec.cr\" (failed linking)\n# require \"./std/io/argf_spec.cr\" (failed to run)\n# require \"./std/io/buffered_spec.cr\" (failed to run)\nrequire \"./std/io/byte_format_spec.cr\"\nrequire \"./std/io/delimited_spec.cr\"\n# require \"./std/io/file_descriptor_spec.cr\" (failed to run)\n# require \"./std/io/hexdump_spec.cr\" (failed to run)\n# require \"./std/io/io_spec.cr\" (failed linking)\n# require \"./std/io/memory_spec.cr\" (failed to run)\n# require \"./std/io/multi_writer_spec.cr\" (failed to run)\nrequire \"./std/io/sized_spec.cr\"\n# require \"./std/io/stapled_spec.cr\" (failed to run)\nrequire \"./std/iterator_spec.cr\"\n# require \"./std/json/any_spec.cr\" (failed linking)\nrequire \"./std/json/builder_spec.cr\"\nrequire \"./std/json/lexer_spec.cr\"\nrequire \"./std/json/parser_spec.cr\"\n# require \"./std/json/pull_parser_spec.cr\" (failed to run)\n# require \"./std/json/serializable_spec.cr\" (failed codegen)\n# require \"./std/json/serialization_spec.cr\" (failed codegen)\n# require \"./std/kernel_spec.cr\" (failed to run)\nrequire \"./std/levenshtein_spec.cr\"\n# require \"./std/llvm/aarch64_spec.cr\" (failed linking)\n# require \"./std/llvm/arm_abi_spec.cr\" (failed linking)\n# require \"./std/llvm/llvm_spec.cr\" (failed linking)\n# require \"./std/llvm/type_spec.cr\" (failed linking)\n# require \"./std/llvm/x86_64_abi_spec.cr\" (failed linking)\n# require \"./std/llvm/x86_abi_spec.cr\" (failed linking)\n# require \"./std/log/broadcast_backend_spec.cr\" (failed to run)\n# require \"./std/log/builder_spec.cr\" (failed to run)\n# require \"./std/log/context_spec.cr\" (failed to run)\n# require \"./std/log/dispatch_spec.cr\" (failed to run)\nrequire \"./std/log/env_config_spec.cr\"\n# require \"./std/log/format_spec.cr\" (failed codegen)\n# require \"./std/log/io_backend_spec.cr\" (failed to run)\n# require \"./std/log/log_spec.cr\" (failed to run)\nrequire \"./std/log/main_spec.cr\"\n# require \"./std/log/metadata_spec.cr\" (failed to run)\n# require \"./std/log/spec_spec.cr\" (failed to run)\nrequire \"./std/regex/match_data_spec.cr\"\n# require \"./std/math_spec.cr\" (failed to run)\n# require \"./std/mime/media_type_spec.cr\" (failed linking)\n# require \"./std/mime/multipart/builder_spec.cr\" (failed linking)\n# require \"./std/mime/multipart/parser_spec.cr\" (failed linking)\n# require \"./std/mime/multipart_spec.cr\" (failed linking)\n# require \"./std/mime_spec.cr\" (failed to run)\n# require \"./std/mutex_spec.cr\" (failed to run)\nrequire \"./std/named_tuple_spec.cr\"\n# require \"./std/number_spec.cr\" (failed linking)\n# require \"./std/oauth/access_token_spec.cr\" (failed linking)\n# require \"./std/oauth/authorization_header_spec.cr\" (failed linking)\n# require \"./std/oauth/consumer_spec.cr\" (failed linking)\n# require \"./std/oauth/params_spec.cr\" (failed linking)\n# require \"./std/oauth/request_token_spec.cr\" (failed linking)\n# require \"./std/oauth/signature_spec.cr\" (failed linking)\n# require \"./std/oauth2/access_token_spec.cr\" (failed linking)\n# require \"./std/oauth2/client_spec.cr\" (failed linking)\n# require \"./std/oauth2/session_spec.cr\" (failed linking)\n# require \"./std/object_spec.cr\" (failed to run)\n# require \"./std/openssl/cipher_spec.cr\" (failed linking)\n# require \"./std/openssl/digest_spec.cr\" (failed linking)\n# require \"./std/openssl/hmac_spec.cr\" (failed linking)\n# require \"./std/openssl/pkcs5_spec.cr\" (failed linking)\n# require \"./std/openssl/ssl/context_spec.cr\" (failed linking)\n# require \"./std/openssl/ssl/hostname_validation_spec.cr\" (failed linking)\n# require \"./std/openssl/ssl/server_spec.cr\" (failed linking)\n# require \"./std/openssl/ssl/socket_spec.cr\" (failed linking)\n# require \"./std/openssl/x509/certificate_spec.cr\" (failed linking)\n# require \"./std/openssl/x509/name_spec.cr\" (failed linking)\nrequire \"./std/option_parser_spec.cr\"\nrequire \"./std/path_spec.cr\"\nrequire \"./std/pointer_spec.cr\"\nrequire \"./std/pp_spec.cr\"\nrequire \"./std/pretty_print_spec.cr\"\nrequire \"./std/proc_spec.cr\"\n# require \"./std/process/find_executable_spec.cr\" (failed to run)\nrequire \"./std/process_spec.cr\"\n# require \"./std/raise_spec.cr\" (failed codegen)\nrequire \"./std/random/isaac_spec.cr\"\nrequire \"./std/random/pcg32_spec.cr\"\nrequire \"./std/random/secure_spec.cr\"\n# require \"./std/random_spec.cr\" (failed linking)\n# require \"./std/range_spec.cr\" (failed linking)\nrequire \"./std/record_spec.cr\"\n# require \"./std/reference_spec.cr\" (failed to run)\nrequire \"./std/regex_spec.cr\"\nrequire \"./std/semantic_version_spec.cr\"\nrequire \"./std/set_spec.cr\"\nrequire \"./std/signal_spec.cr\"\nrequire \"./std/slice_spec.cr\"\n# require \"./std/socket/address_spec.cr\" (failed to run)\n# require \"./std/socket/addrinfo_spec.cr\" (failed to run)\n# require \"./std/socket/socket_spec.cr\" (failed to run)\nrequire \"./std/socket/tcp_server_spec.cr\"\nrequire \"./std/socket/tcp_socket_spec.cr\"\n# require \"./std/socket/udp_socket_spec.cr\" (failed to run)\n# require \"./std/socket/unix_server_spec.cr\" (failed to run)\n# require \"./std/socket/unix_socket_spec.cr\" (failed to run)\n# require \"./std/spec/context_spec.cr\" (failed to run)\nrequire \"./std/spec/expectations_spec.cr\"\n# require \"./std/spec/filters_spec.cr\" (failed to run)\nrequire \"./std/spec/helpers/iterate_spec.cr\"\n# require \"./std/spec/hooks_spec.cr\" (failed to run)\n# require \"./std/spec/junit_formatter_spec.cr\" (failed linking)\nrequire \"./std/spec/tap_formatter_spec.cr\"\n# require \"./std/spec_spec.cr\" (failed codegen)\n# require \"./std/sprintf_spec.cr\" (failed linking)\nrequire \"./std/static_array_spec.cr\"\nrequire \"./std/string/grapheme_break_spec.cr\"\nrequire \"./std/string/grapheme_spec.cr\"\nrequire \"./std/string/utf16_spec.cr\"\nrequire \"./std/string_builder_spec.cr\"\nrequire \"./std/string_pool_spec.cr\"\nrequire \"./std/string_scanner_spec.cr\"\n# require \"./std/string_spec.cr\" (failed to run)\n# require \"./std/struct_spec.cr\" (failed linking)\nrequire \"./std/symbol_spec.cr\"\nrequire \"./std/syscall_spec.cr\"\n# require \"./std/system/group_spec.cr\" (failed to run)\n# require \"./std/system/user_spec.cr\" (failed to run)\n# require \"./std/system_error_spec.cr\" (failed to run)\n# require \"./std/system_spec.cr\" (failed to run)\n# require \"./std/thread/condition_variable_spec.cr\" (failed to run)\n# require \"./std/thread/mutex_spec.cr\" (failed to run)\n# require \"./std/thread_spec.cr\" (failed to run)\nrequire \"./std/time/custom_formats_spec.cr\"\n# require \"./std/time/format_spec.cr\" (failed to run)\n# require \"./std/time/location_spec.cr\" (failed to run)\nrequire \"./std/time/span_spec.cr\"\n# require \"./std/time/time_spec.cr\" (failed to run)\nrequire \"./std/tuple_spec.cr\"\nrequire \"./std/uint_spec.cr\"\nrequire \"./std/uri/params_spec.cr\"\nrequire \"./std/uri/punycode_spec.cr\"\n# require \"./std/uri_spec.cr\" (failed linking)\nrequire \"./std/uuid/json_spec.cr\"\n# require \"./std/uuid/yaml_spec.cr\" (failed linking)\nrequire \"./std/uuid_spec.cr\"\n# require \"./std/va_list_spec.cr\" (failed to run)\n# require \"./std/weak_ref_spec.cr\" (failed to run)\nrequire \"./std/winerror_spec.cr\"\n# require \"./std/xml/builder_spec.cr\" (failed linking)\n# require \"./std/xml/html_spec.cr\" (failed linking)\n# require \"./std/xml/reader_spec.cr\" (failed linking)\n# require \"./std/xml/xml_spec.cr\" (failed linking)\n# require \"./std/xml/xpath_spec.cr\" (failed linking)\n# require \"./std/yaml/any_spec.cr\" (failed linking)\n# require \"./std/yaml/builder_spec.cr\" (failed codegen)\n# require \"./std/yaml/nodes/builder_spec.cr\" (failed codegen)\n# require \"./std/yaml/schema/core_spec.cr\" (failed codegen)\n# require \"./std/yaml/schema/fail_safe_spec.cr\" (failed codegen)\n# require \"./std/yaml/serializable_spec.cr\" (failed codegen)\n# require \"./std/yaml/serialization_spec.cr\" (failed codegen)\n# require \"./std/yaml/yaml_pull_parser_spec.cr\" (failed codegen)\n# require \"./std/yaml/yaml_spec.cr\" (failed codegen)\n"
  },
  {
    "path": "src/VERSION",
    "content": "1.20.0-dev\n"
  },
  {
    "path": "src/annotations.cr",
    "content": "# This annotation marks features as deprecated.\n#\n# It can annotate methods, macros, types, constants, aliases and method parameters.\n#\n# It receives an optional `StringLiteral` as single argument containing a\n# deprecation notice.\n#\n# ```\n# @[Deprecated(\"Use `#bar` instead\")]\n# def foo\n# end\n#\n# @[Deprecated(\"Here may be dragons\")]\n# class Foo\n# end\n#\n# def foo(bar, @[Deprecated(\"Do not try this at home\")] baz)\n# end\n# ```\n#\n# Deprecations are shown in the API docs and the compiler prints a warning when\n# using a deprecated feature.\n#\n# Deprecated types only trigger a warning when they are actually _used_ (e.g.\n# calling a class method), not when they're just part of type restriction, for\n# example.\n# Deprecated parameters only trigger a warning when the particular parameter is\n# passed in a call. Calls without this parameter are unaffected.\nannotation Deprecated\nend\n\n# An enum can be marked with `@[Flags]`. This changes the default values.\n# The first constant's value is 1, and successive constants are multiplied by 2.\n#\n# ```\n# @[Flags]\n# enum IOMode\n#   Read  # 1\n#   Write # 2\n#   Async # 4\n# end\n#\n# (IOMode::Write | IOMode::Async).value # => 6\n# (IOMode::Write | IOMode::Async).to_s  # => \"Write | Async\"\n# ```\nannotation Flags\nend\n\n# A `lib` can be marked with `@[Link(lib : String, *, ldflags : String, static : Bool, framework : String, pkg_config : String, wasm_import_module : String, dll : String)]`\n# to declare the library that should be linked when compiling the program.\n#\n# At least one of the *lib*, *ldflags*, *framework* arguments needs to be specified.\n#\n# `@[Link(ldflags: \"-lpcre\")]` will pass `-lpcre` straight to the linker.\n#\n# `@[Link(\"pcre\")]` will lookup for a shared library.\n# 1. will lookup `pcre` using `pkg-config`, if not found\n# 2. will pass `-lpcre` to the linker.\n#\n# `@[Link(\"pcre\", pkg_config: \"libpcre\")]` will lookup for a shared library.\n# 1. will lookup `libpcre` using `pkg-config`, if not found\n# 2. will lookup `pcre` using `pkg-config`, if not found\n# 3. will pass `-lpcre` to the linker.\n#\n# `@[Link(framework: \"Cocoa\")]` will pass `-framework Cocoa` to the linker.\n#\n# `@[Link(dll: \"gc.dll\")]` will copy `gc.dll` to any built program. The DLL name\n# must use `.dll` as its file extension and cannot contain any directory\n# separators. The actual DLL is searched among `CRYSTAL_LIBRARY_PATH`, the\n# compiler's own directory, and `PATH` in that order; a warning is printed if\n# the DLL isn't found, although it might still run correctly if the DLLs are\n# available in other DLL search paths on the system.\n#\n# When an `-l` option is passed to the linker, it will lookup the libraries in\n# paths passed with the `-L` option. Any paths in `CRYSTAL_LIBRARY_PATH` are\n# added by default. Custom paths can be passed using `ldflags`:\n# `@[Link(ldflags: \"-Lvendor/bin\")]`.\nannotation Link\nend\n\n# This annotation marks methods, classes, constants, and macros as experimental.\n#\n# Experimental features are subject to change or be removed despite the\n# [https://semver.org/](https://semver.org/) guarantees.\n#\n# ```\n# @[Experimental(\"Join discussion about this topic at ...\")]\n# def foo\n# end\n# ```\nannotation Experimental\nend\n"
  },
  {
    "path": "src/array.cr",
    "content": "# An `Array` is an ordered, integer-indexed collection of objects of type T.\n#\n# Array indexing starts at 0. A negative index is assumed to be\n# relative to the end of the array: -1 indicates the last element,\n# -2 is the next to last element, and so on.\n#\n# An `Array` can be created using the usual `new` method (several are provided), or with an array literal:\n#\n# ```\n# Array(Int32).new  # => []\n# [1, 2, 3]         # Array(Int32)\n# [1, \"hello\", 'x'] # Array(Int32 | String | Char)\n# ```\n#\n# See [`Array` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/array.html) in the language reference.\n#\n# An `Array` can have mixed types, meaning T will be a union of types, but these are determined\n# when the array is created, either by specifying T or by using an array literal. In the latter\n# case, T will be set to the union of the array literal elements' types.\n#\n# When creating an empty array you must always specify T:\n#\n# ```\n# [] of Int32 # same as Array(Int32)\n# []          # syntax error\n# ```\n#\n# An `Array` is implemented using an internal buffer of some capacity\n# and is reallocated when elements are pushed to it when more capacity\n# is needed. This is normally known as a [dynamic array](http://en.wikipedia.org/wiki/Dynamic_array).\n#\n# You can use a special array literal syntax with other types too, as long as they define an argless\n# `new` method and a `<<` method. `Set` is one such type:\n#\n# ```\n# set = Set{1, 2, 3} # => Set{1, 2, 3}\n# set.class          # => Set(Int32)\n# ```\n#\n# The above is the same as this:\n#\n# ```\n# set = Set(typeof(1, 2, 3)).new\n# set << 1\n# set << 2\n# set << 3\n# ```\nclass Array(T)\n  include Indexable::Mutable(T)\n  include Comparable(Array)\n\n  # Size of an Array that we consider small to do linear scans or other optimizations.\n  private SMALL_ARRAY_SIZE = 16\n\n  # The initial capacity reserved for new arrays; just a lucky number\n  private INITIAL_CAPACITY = 3\n\n  # The capacity threshold before we stop doubling array during resize.\n  private CAPACITY_THRESHOLD = 256\n\n  # The size of this array.\n  @size : Int32\n\n  # The capacity of `@buffer`.\n  # Note that, because `@buffer` moves on shift, the actual\n  # capacity (the allocated memory) starts at `@buffer - @offset_to_buffer`.\n  # The actual capacity is also given by the `remaining_capacity` internal method.\n  @capacity : Int32\n\n  # Offset to the buffer that was originally allocated, and which needs to\n  # be reallocated on resize. On shift this value gets increased, together with\n  # `@buffer`. To reach the root buffer you have to do `@buffer - @offset_to_buffer`,\n  # and this is also provided by the `root_buffer` internal method.\n  @offset_to_buffer : Int32 = 0\n\n  # The buffer where elements start.\n  @buffer : Pointer(T)\n\n  # In 64 bits the Array is composed then by:\n  # - type_id            : Int32   # 4 bytes -|\n  # - size               : Int32   # 4 bytes  |- packed as 8 bytes\n  #\n  # - capacity           : Int32   # 4 bytes -|\n  # - offset_to_buffer   : Int32   # 4 bytes  |- packed as 8 bytes\n  #\n  # - buffer             : Pointer # 8 bytes  |- another 8 bytes\n  #\n  # So in total 24 bytes. Without offset_to_buffer it's the same,\n  # because of aligning to 8 bytes (at least in 64 bits), and that's\n  # why we chose to include this value, because with it we can optimize\n  # `shift` to let Array be used as a queue/deque.\n\n  # Creates a new empty `Array`.\n  def initialize\n    @size = 0\n    @capacity = 0\n    @buffer = Pointer(T).null\n  end\n\n  # Creates a new empty `Array` backed by a buffer that is initially\n  # `initial_capacity` big.\n  #\n  # The *initial_capacity* is useful to avoid unnecessary reallocations\n  # of the internal buffer in case of growth. If you have an estimate\n  # of the maximum number of elements an array will hold, the array should\n  # be initialized with that capacity for improved performance.\n  #\n  # ```\n  # ary = Array(Int32).new(5)\n  # ary.size # => 0\n  # ```\n  def initialize(initial_capacity : Int)\n    if initial_capacity < 0\n      raise ArgumentError.new(\"Negative array size: #{initial_capacity}\")\n    end\n\n    @size = 0\n    @capacity = initial_capacity.to_i\n    if initial_capacity == 0\n      @buffer = Pointer(T).null\n    else\n      @buffer = Pointer(T).malloc(initial_capacity)\n    end\n  end\n\n  # Creates a new `Array` of the given *size* filled with the same *value* in each position.\n  #\n  # ```\n  # Array.new(3, 'a') # => ['a', 'a', 'a']\n  # ```\n  #\n  # WARNING: The initial value is filled into the array as-is. It gets neither\n  # duplicated nor cloned. For types with reference semantics this means every\n  # item will point to the *same* object.\n  #\n  # ```\n  # ary = Array.new(3, [1])\n  # ary # => [[1], [1], [1]]\n  # ary[0][0] = 2\n  # ary # => [[2], [2], [2]]\n  # ```\n  #\n  # * `.new(Int, & : Int32 -> T)` is an alternative that allows using a\n  #   different initial value for each position.\n  def initialize(size : Int, value : T)\n    if size < 0\n      raise ArgumentError.new(\"Negative array size: #{size}\")\n    end\n\n    @size = size.to_i\n    @capacity = size.to_i\n\n    if size == 0\n      @buffer = Pointer(T).null\n    else\n      @buffer = Pointer(T).malloc(size, value)\n    end\n  end\n\n  # Creates a new `Array` of the given *size* and invokes the given block once\n  # for each index of `self`, assigning the block's value in that index.\n  #\n  # ```\n  # Array.new(3) { |i| (i + 1) ** 2 } # => [1, 4, 9]\n  #\n  # ary = Array.new(3) { [1] }\n  # ary # => [[1], [1], [1]]\n  # ary[0][0] = 2\n  # ary # => [[2], [1], [1]]\n  # ```\n  def self.new(size : Int, & : Int32 -> T)\n    Array(T).build(size) do |buffer|\n      size.to_i.times do |i|\n        buffer[i] = yield i\n      end\n      size\n    end\n  end\n\n  # Creates a new `Array`, allocating an internal buffer with the given *capacity*,\n  # and yielding that buffer. The given block must return the desired size of the array.\n  #\n  # This method is **unsafe**, but is usually used to initialize the buffer\n  # by passing it to a C function.\n  #\n  # ```\n  # Array.build(3) do |buffer|\n  #   LibSome.fill_buffer_and_return_number_of_elements_filled(buffer)\n  # end\n  # ```\n  def self.build(capacity : Int, & : Pointer(T) ->) : self\n    ary = Array(T).new(capacity)\n    ary.size = (yield ary.to_unsafe).to_i\n    ary\n  end\n\n  # :nodoc:\n  #\n  # This method is used by LiteralExpander to efficiently create an Array\n  # instance from a literal.\n  def self.unsafe_build(capacity : Int) : self\n    ary = Array(T).new(capacity)\n    ary.size = capacity\n    ary\n  end\n\n  # Returns the number of elements in the array.\n  #\n  # ```\n  # [:foo, :bar].size # => 2\n  # ```\n  getter size : Int32\n\n  # Equality. Returns `true` if each element in `self` is equal to each\n  # corresponding element in *other*.\n  #\n  # ```\n  # ary = [1, 2, 3]\n  # ary == [1, 2, 3] # => true\n  # ary == [2, 3]    # => false\n  # ```\n  def ==(other : Array) : Bool\n    equals?(other) { |x, y| x == y }\n  end\n\n  def ==(other) : Bool\n    false\n  end\n\n  # Combined comparison operator.\n  #\n  # Returns `-1`, `0` or `1` depending on whether `self` is less than *other*, equals *other*\n  # or is greater than *other*.\n  #\n  # It compares the elements of both arrays in the same position using the\n  # `<=>` operator. As soon as one of such comparisons returns a non-zero\n  # value, that result is the return value of the comparison.\n  #\n  # If all elements are equal, the comparison is based on the size of the arrays.\n  #\n  # ```\n  # [8] <=> [1, 2, 3] # => 1\n  # [2] <=> [4, 2, 3] # => -1\n  # [1, 2] <=> [1, 2] # => 0\n  # ```\n  def <=>(other : Array)\n    min_size = Math.min(size, other.size)\n    0.upto(min_size - 1) do |i|\n      n = @buffer[i] <=> other.to_unsafe[i]\n      return n if n != 0\n    end\n    size <=> other.size\n  end\n\n  # Set intersection: returns a new `Array` containing elements common to `self`\n  # and *other*, excluding any duplicates. The order is preserved from `self`.\n  #\n  # ```\n  # [1, 1, 3, 5] & [1, 2, 3]               # => [ 1, 3 ]\n  # ['a', 'b', 'b', 'z'] & ['a', 'b', 'c'] # => [ 'a', 'b' ]\n  # ```\n  #\n  # See also: `#uniq`.\n  def &(other : Array(U)) : Array(T) forall U\n    return Array(T).new if self.empty? || other.empty?\n\n    # Heuristic: for small arrays we do a linear scan, which is usually\n    # faster than creating an intermediate Set.\n    if self.size + other.size <= SMALL_ARRAY_SIZE * 2\n      ary = Array(T).new\n      each do |elem|\n        ary << elem if !ary.includes?(elem) && other.includes?(elem)\n      end\n      return ary\n    end\n\n    set = other.to_set\n    Array(T).build(Math.min(size, other.size)) do |buffer|\n      appender = buffer.appender\n      each do |obj|\n        appender << obj if set.delete(obj)\n      end\n      appender.size.to_i\n    end\n  end\n\n  # Set union: returns a new `Array` by joining `self` with *other*, excluding\n  # any duplicates, and preserving the order from `self`.\n  #\n  # ```\n  # [\"a\", \"b\", \"c\"] | [\"c\", \"d\", \"a\"] # => [ \"a\", \"b\", \"c\", \"d\" ]\n  # ```\n  #\n  # See also: `#uniq`.\n  def |(other : Array(U)) : Array(T | U) forall U\n    # Heuristic: if the combined size is small we just do a linear scan\n    # instead of using a Set for lookup.\n    if size + other.size <= SMALL_ARRAY_SIZE\n      ary = Array(T | U).new\n      each do |elem|\n        ary << elem unless ary.includes?(elem)\n      end\n      other.each do |elem|\n        ary << elem unless ary.includes?(elem)\n      end\n      return ary\n    end\n\n    Array(T | U).build(size + other.size) do |buffer|\n      set = Set(T | U).new\n      appender = buffer.appender\n      each do |obj|\n        appender << obj if set.add?(obj)\n      end\n      other.each do |obj|\n        appender << obj if set.add?(obj)\n      end\n      appender.size.to_i\n    end\n  end\n\n  # Concatenation. Returns a new `Array` built by concatenating `self` and *other*.\n  # The type of the new array is the union of the types of both the original arrays.\n  #\n  # ```\n  # [1, 2] + [\"a\"]  # => [1,2,\"a\"] of (Int32 | String)\n  # [1, 2] + [2, 3] # => [1,2,2,3]\n  # ```\n  def +(other : Array(U)) : Array(T | U) forall U\n    new_size = size + other.size\n    Array(T | U).build(new_size) do |buffer|\n      buffer.copy_from(@buffer, size)\n      (buffer + size).copy_from(other.to_unsafe, other.size)\n      new_size\n    end\n  end\n\n  # Returns the additive identity of this type.\n  #\n  # This is an empty array.\n  def self.additive_identity : self\n    self.new\n  end\n\n  # Difference. Returns a new `Array` that is a copy of `self`, removing any items\n  # that appear in *other*. The order of `self` is preserved.\n  #\n  # ```\n  # [1, 2, 3] - [2, 1] # => [3]\n  # ```\n  def -(other : Array(U)) : Array(T) forall U\n    # Heuristic: if any of the arrays is small we just do a linear scan\n    # instead of using a Set for lookup.\n    if size <= SMALL_ARRAY_SIZE || other.size <= SMALL_ARRAY_SIZE\n      ary = Array(T).new\n      each do |elem|\n        ary << elem unless other.includes?(elem)\n      end\n      return ary\n    end\n\n    ary = Array(T).new(Math.max(size - other.size, 0))\n    set = other.to_set\n    each do |obj|\n      ary << obj unless set.includes?(obj)\n    end\n    ary\n  end\n\n  # Repetition: Returns a new `Array` built by concatenating *times* copies of `self`.\n  #\n  # ```\n  # [\"a\", \"b\", \"c\"] * 2 # => [ \"a\", \"b\", \"c\", \"a\", \"b\", \"c\" ]\n  # ```\n  def *(times : Int) : Array(T)\n    if times == 0 || empty?\n      return Array(T).new\n    end\n\n    if times == 1\n      return dup\n    end\n\n    if size == 1\n      return Array(T).new(times, first)\n    end\n\n    new_size = size * times\n    Array(T).build(new_size) do |buffer|\n      buffer.copy_from(to_unsafe, size)\n      n = size\n\n      while n <= new_size // 2\n        (buffer + n).copy_from(buffer, n)\n        n *= 2\n      end\n\n      (buffer + n).copy_from(buffer, new_size - n)\n      new_size\n    end\n  end\n\n  # Append. Alias for `push`.\n  #\n  # ```\n  # a = [1, 2]\n  # a << 3 # => [1,2,3]\n  # ```\n  def <<(value : T) : self\n    push(value)\n  end\n\n  # Replaces a subrange with a single value. All elements in the range\n  # `start...start+count` are removed and replaced by a single element\n  # *value*.\n  #\n  # If *count* is zero, *value* is inserted at *start*.\n  #\n  # Negative values of *start* count from the end of the array.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a[1, 3] = 6\n  # a # => [1, 6, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[1, 0] = 6\n  # a # => [1, 6, 2, 3, 4, 5]\n  # ```\n  def []=(start : Int, count : Int, value : T) : T\n    start, count = normalize_start_and_count(start, count)\n\n    case count\n    when 0\n      insert start, value\n    when 1\n      @buffer[start] = value\n    else\n      diff = count - 1\n\n      # If *start* is 0 we can avoid a memcpy by doing a shift.\n      # For example if we have:\n      #\n      #    a = ['a', 'b', 'c', 'd']\n      #\n      # and someone does:\n      #\n      #    a[0..2] = 'x'\n      #\n      # we can change the value at 2 to 'x' and repoint `@offset_to_buffer`:\n      #\n      #    [-, -, 'x', 'd']\n      #           ^\n      #\n      # (we also have to clear the elements before that)\n      if start == 0\n        @buffer.clear(diff)\n        shift_buffer_by(diff)\n        @buffer.value = value\n      else\n        (@buffer + start + 1).move_from(@buffer + start + count, size - start - count)\n        (@buffer + @size - diff).clear(diff)\n        @buffer[start] = value\n      end\n\n      @size -= diff\n    end\n\n    value\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#[]=(start, count, value)` instead\")]\n  def []=(value : T, *, index start : Int, count : Int)\n    self[start, count] = value\n  end\n\n  # Replaces a subrange with a single value.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a[1..3] = 6\n  # a # => [1, 6, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[1...1] = 6\n  # a # => [1, 6, 2, 3, 4, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[2...] = 6\n  # a # => [1, 2, 6]\n  # ```\n  def []=(range : Range, value : T)\n    self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new] = value\n  end\n\n  # Replaces a subrange with the elements of the given array.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a[1, 3] = [6, 7, 8]\n  # a # => [1, 6, 7, 8, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[1, 3] = [6, 7]\n  # a # => [1, 6, 7, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[1, 3] = [6, 7, 8, 9, 10]\n  # a # => [1, 6, 7, 8, 9, 10, 5]\n  # ```\n  def []=(start : Int, count : Int, values : Array(T))\n    start, count = normalize_start_and_count(start, count)\n    diff = values.size - count\n\n    if diff == 0\n      # Replace values directly\n      (@buffer + start).copy_from(values.to_unsafe, values.size)\n    elsif diff < 0\n      # Need to shrink\n      diff = -diff\n      (@buffer + start).copy_from(values.to_unsafe, values.size)\n      (@buffer + start + values.size).move_from(@buffer + start + count, size - start - count)\n      (@buffer + @size - diff).clear(diff)\n      @size -= diff\n    else\n      # Need to grow\n      resize_if_cant_insert(diff)\n      (@buffer + start + values.size).move_from(@buffer + start + count, size - start - count)\n      (@buffer + start).copy_from(values.to_unsafe, values.size)\n      @size += diff\n    end\n\n    values\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#[]=(start, count, values)` instead\")]\n  def []=(values : Array(T), *, index start : Int, count : Int)\n    self[start, count] = values\n  end\n\n  # Replaces a subrange with the elements of the given array.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a[1..3] = [6, 7, 8]\n  # a # => [1, 6, 7, 8, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[1..3] = [6, 7]\n  # a # => [1, 6, 7, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[1..3] = [6, 7, 8, 9, 10]\n  # a # => [1, 6, 7, 8, 9, 10, 5]\n  #\n  # a = [1, 2, 3, 4, 5]\n  # a[2..] = [6, 7, 8, 9, 10]\n  # a # => [1, 2, 6, 7, 8, 9, 10]\n  # ```\n  def []=(range : Range, values : Array(T))\n    self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new] = values\n  end\n\n  # Returns all elements that are within the given range.\n  #\n  # The first element in the returned array is `self[range.begin]` followed\n  # by the next elements up to index `range.end` (or `self[range.end - 1]` if\n  # the range is exclusive).\n  # If there are fewer elements in `self`, the returned array is shorter than\n  # `range.size`.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # a[1..3] # => [\"b\", \"c\", \"d\"]\n  # # range.end > array.size\n  # a[3..7] # => [\"d\", \"e\"]\n  # ```\n  #\n  # Open ended ranges are clamped at the start and end of the array, respectively.\n  #\n  # ```\n  # # open ended ranges\n  # a[2..] # => [\"c\", \"d\", \"e\"]\n  # a[..2] # => [\"a\", \"b\", \"c\"]\n  # ```\n  #\n  # Negative range values are added to `self.size`, thus they are treated as\n  # indices counting from the end of the array, `-1` designating the last element.\n  #\n  # ```\n  # # negative indices, both ranges are equivalent for `a`\n  # a[1..3]   # => [\"b\", \"c\", \"d\"]\n  # a[-4..-2] # => [\"b\", \"c\", \"d\"]\n  # # Mixing negative and positive indices, both ranges are equivalent for `a`\n  # a[1..-2] # => [\"b\", \"c\", \"d\"]\n  # a[-4..3] # => [\"b\", \"c\", \"d\"]\n  # ```\n  #\n  # Raises `IndexError` if the start index is out of range (`range.begin >\n  # self.size || range.begin < -self.size`). If `range.begin == self.size` an\n  # empty array is returned. If `range.begin > range.end`, an empty array is\n  # returned.\n  #\n  # ```\n  # # range.begin > array.size\n  # a[6..10] # raise IndexError\n  # # range.begin == array.size\n  # a[5..10] # => []\n  # # range.begin > range.end\n  # a[3..1]   # => []\n  # a[-2..-4] # => []\n  # a[-2..1]  # => []\n  # a[3..-4]  # => []\n  # ```\n  def [](range : Range) : Array(T)\n    self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new]\n  end\n\n  # Like `#[](Range)`, but returns `nil` if `range.begin` is out of range.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # a[6..10]? # => nil\n  # a[6..]?   # => nil\n  # ```\n  def []?(range : Range) : Array(T)?\n    self[*Indexable.range_to_index_and_count(range, size) || return nil]?\n  end\n\n  # Returns count or less (if there aren't enough) elements starting at the\n  # given start index.\n  #\n  # Negative *start* is added to `self.size`, thus it's treated as\n  # index counting from the end of the array, `-1` designating the last element.\n  #\n  # Raises `IndexError` if *start* index is out of bounds.\n  # Raises `ArgumentError` if *count* is negative.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # a[-3, 3] # => [\"c\", \"d\", \"e\"]\n  # a[1, 2]  # => [\"b\", \"c\"]\n  # a[5, 1]  # => []\n  # a[6, 1]  # raises IndexError\n  # ```\n  def [](start : Int, count : Int) : Array(T)\n    self[start, count]? || raise IndexError.new\n  end\n\n  # Like `#[](Int, Int)` but returns `nil` if the *start* index is out of range.\n  def []?(start : Int, count : Int) : Array(T)?\n    start, count = normalize_start_and_count(start, count) { return nil }\n    return Array(T).new if count == 0\n\n    Array(T).build(count) do |buffer|\n      buffer.copy_from(@buffer + start, count)\n      count\n    end\n  end\n\n  @[AlwaysInline]\n  def unsafe_fetch(index : Int) : T\n    @buffer[index]\n  end\n\n  @[AlwaysInline]\n  def unsafe_put(index : Int, value : T)\n    @buffer[index] = value\n  end\n\n  # Removes all elements from `self`.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # a.clear # => []\n  # ```\n  def clear : self\n    @buffer.clear(@size)\n    @size = 0\n    self\n  end\n\n  # Returns a new `Array` that has `self`'s elements cloned.\n  # That is, it returns a deep copy of `self`.\n  #\n  # Use `#dup` if you want a shallow copy.\n  #\n  # ```\n  # ary = [[1, 2], [3, 4]]\n  # ary2 = ary.clone\n  # ary[0][0] = 5\n  # ary  # => [[5, 2], [3, 4]]\n  # ary2 # => [[1, 2], [3, 4]]\n  #\n  # ary2 << [7, 8]\n  # ary  # => [[5, 2], [3, 4]]\n  # ary2 # => [[1, 2], [3, 4], [7, 8]]\n  # ```\n  def clone : Array(T)\n    {% if T == ::Bool || T == ::Char || T == ::String || T == ::Symbol || T < ::Number::Primitive %}\n      Array(T).new(size) { |i| @buffer[i].clone.as(T) }\n    {% else %}\n      exec_recursive_clone do |hash|\n        clone = Array(T).new(size)\n        hash[object_id] = clone.object_id\n        each do |element|\n          clone << element.clone.as(T)\n        end\n        clone\n      end\n    {% end %}\n  end\n\n  # Returns a copy of `self` with all `nil` elements removed.\n  #\n  # ```\n  # [\"a\", nil, \"b\", nil, \"c\", nil].compact # => [\"a\", \"b\", \"c\"]\n  # ```\n  def compact\n    compact_map &.itself\n  end\n\n  # Removes all `nil` elements from `self` and returns `self`.\n  #\n  # ```\n  # ary = [\"a\", nil, \"b\", nil, \"c\"]\n  # ary.compact!\n  # ary # => [\"a\", \"b\", \"c\"]\n  # ```\n  def compact! : self\n    reject! &.nil?\n  end\n\n  # Appends the elements of *other* to `self`, and returns `self`.\n  #\n  # ```\n  # ary = [\"a\", \"b\"]\n  # ary.concat([\"c\", \"d\"])\n  # ary # => [\"a\", \"b\", \"c\", \"d\"]\n  # ```\n  def concat(other : Indexable) : self\n    other_size = other.size\n\n    resize_if_cant_insert(other_size)\n\n    insert_elements_at(other, @size)\n\n    @size += other_size\n\n    self\n  end\n\n  # :ditto:\n  def concat(other : Enumerable) : self\n    left_before_resize = remaining_capacity - @size\n    len = @size\n    buf = @buffer + len\n    other.each do |elem|\n      if left_before_resize == 0\n        increase_capacity\n        left_before_resize = remaining_capacity - len\n        buf = @buffer + len\n      end\n      buf.value = elem\n      buf += 1\n      len += 1\n      left_before_resize -= 1\n    end\n\n    @size = len\n\n    self\n  end\n\n  # Removes all items from `self` that are equal to *obj*.\n  #\n  # Returns the last found element that was equal to *obj*,\n  # if any, or `nil` if not found.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"b\", \"b\", \"c\"]\n  # a.delete(\"b\") # => \"b\"\n  # a             # => [\"a\", \"c\"]\n  #\n  # a.delete(\"x\") # => nil\n  # a             # => [\"a\", \"c\"]\n  # ```\n  def delete(obj) : T?\n    internal_delete { |e| e == obj }[1]\n  end\n\n  # Removes the element at *index*, returning that element.\n  # Raises `IndexError` if *index* is out of range.\n  #\n  # ```\n  # a = [\"ant\", \"bat\", \"cat\", \"dog\"]\n  # a.delete_at(2)  # => \"cat\"\n  # a               # => [\"ant\", \"bat\", \"dog\"]\n  # a.delete_at(99) # raises IndexError\n  # ```\n  def delete_at(index : Int) : T\n    index = check_index_out_of_bounds index\n\n    # Deleting the first element is the same as a shift\n    if index == 0\n      return shift_when_not_empty\n    end\n\n    elem = @buffer[index]\n    (@buffer + index).move_from(@buffer + index + 1, size - index - 1)\n    @size -= 1\n    (@buffer + @size).clear\n    elem\n  end\n\n  # Removes all elements within the given *range*.\n  # Returns an array of the removed elements with the original order of `self` preserved.\n  # Raises `IndexError` if the index is out of range.\n  #\n  # ```\n  # a = [\"ant\", \"bat\", \"cat\", \"dog\"]\n  # a.delete_at(1..2)    # => [\"bat\", \"cat\"]\n  # a                    # => [\"ant\", \"dog\"]\n  # a.delete_at(99..100) # raises IndexError\n  # ```\n  def delete_at(range : Range) : self\n    delete_at(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new)\n  end\n\n  # Removes *count* elements from `self` starting at *start*.\n  # If the size of `self` is less than *count*, removes values to the end of the array without error.\n  # Returns an array of the removed elements with the original order of `self` preserved.\n  # Raises `IndexError` if *start* is out of range.\n  #\n  # ```\n  # a = [\"ant\", \"bat\", \"cat\", \"dog\"]\n  # a.delete_at(1, 2)  # => [\"bat\", \"cat\"]\n  # a                  # => [\"ant\", \"dog\"]\n  # a.delete_at(99, 1) # raises IndexError\n  # ```\n  def delete_at(start : Int, count : Int) : self\n    start, count = normalize_start_and_count(start, count)\n\n    val = self[start, count]\n    (@buffer + start).move_from(@buffer + start + count, size - start - count)\n    @size -= count\n    (@buffer + @size).clear(count)\n    val\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#delete_at(start, count)` instead\")]\n  def delete_at(*, index start : Int, count : Int) : self\n    delete_at(start, count)\n  end\n\n  # Returns a new `Array` that has exactly `self`'s elements.\n  # That is, it returns a shallow copy of `self`.\n  #\n  # Use `#clone` if you want a deep copy.\n  #\n  # ```\n  # ary = [[1, 2], [3, 4]]\n  # ary2 = ary.dup\n  # ary[0][0] = 5\n  # ary  # => [[5, 2], [3, 4]]\n  # ary2 # => [[5, 2], [3, 4]]\n  #\n  # ary2 << [7, 8]\n  # ary  # => [[5, 2], [3, 4]]\n  # ary2 # => [[5, 2], [3, 4], [7, 8]]\n  # ```\n  def dup : Array(T)\n    Array(T).build(@size) do |buffer|\n      buffer.copy_from(@buffer, size)\n      size\n    end\n  end\n\n  # Yields each index of `self`, starting at *start*, to the given block and then assigns\n  # the block's value in that position. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the array.\n  #\n  # Raises `IndexError` if *start* is outside the array range.\n  #\n  # ```\n  # a = [1, 2, 3, 4]\n  # a.fill(2) { |i| i * i } # => [1, 2, 4, 9]\n  # ```\n  @[Deprecated(\"Use `fill(start.., &)` instead\")]\n  def fill(start : Int, & : Int32 -> T) : self\n    fill(start..) { |i| yield i }\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `fill(start.., &)` instead\")]\n  def fill(*, from start : Int, & : Int32 -> T) : self\n    fill(start..) { |i| yield i }\n  end\n\n  # Yields each index of `self`, starting at *start* and just *count* times,\n  # to the given block and then assigns the block's value in that position. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the array.\n  #\n  # Raises `IndexError` if *start* is outside the array range.\n  #\n  # Has no effect if *count* is zero or negative.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5, 6]\n  # a.fill(2, 2) { |i| i * i } # => [1, 2, 4, 9, 5, 6]\n  # ```\n  @[Deprecated(\"Use `Indexable::Mutable#fill(start, count, &)` instead\")]\n  def fill(*, from start : Int, count : Int, & : Int32 -> T) : self\n    fill(start, count) { |i| yield i }\n  end\n\n  # :inherit:\n  def fill(value : T) : self\n    # enable memset optimization\n    to_unsafe_slice.fill(value)\n    self\n  end\n\n  # Replaces every element in `self`, starting at *start*, with the given *value*. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the array.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a.fill(9, 2) # => [1, 2, 9, 9, 9]\n  # ```\n  @[Deprecated(\"Use `fill(value, start..)` instead\")]\n  def fill(value : T, start : Int) : self\n    fill(value, start..)\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `fill(value, start..)` instead\")]\n  def fill(value : T, *, from start : Int) : self\n    fill(value, start..)\n  end\n\n  # Replaces *count* or less (if there aren't enough) elements starting at the\n  # given *start* index with *value*. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the container.\n  #\n  # Raises `IndexError` if the *start* index is out of range.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  #\n  # ```\n  # array = [1, 2, 3, 4, 5]\n  # array.fill(9, 2, 2) # => [1, 2, 9, 9, 5]\n  # array               # => [1, 2, 9, 9, 5]\n  # ```\n  def fill(value : T, start : Int, count : Int) : self\n    to_unsafe_slice.fill(value, start, count)\n    self\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#fill(value, start, count)` instead\")]\n  def fill(value : T, *, from start : Int, count : Int) : self\n    fill(value, start, count)\n  end\n\n  # Replaces every element in *range* with *value*. Returns `self`.\n  #\n  # Negative values of *from* count from the end of the array.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a.fill(9, 2..3) # => [1, 2, 9, 9, 5]\n  # ```\n  def fill(value : T, range : Range) : self\n    to_unsafe_slice.fill(value, range)\n    self\n  end\n\n  # Returns the first *n* elements of the array.\n  #\n  # ```\n  # [1, 2, 3].first(2) # => [1, 2]\n  # [1, 2, 3].first(4) # => [1, 2, 3]\n  # ```\n  def first(n : Int) : Array(T)\n    self[0, n]\n  end\n\n  # Insert *object* before the element at *index* and shifting successive elements, if any.\n  # Returns `self`.\n  #\n  # Negative values of *index* count from the end of the array.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.insert(0, \"x\")  # => [\"x\", \"a\", \"b\", \"c\"]\n  # a.insert(2, \"y\")  # => [\"x\", \"a\", \"y\", \"b\", \"c\"]\n  # a.insert(-1, \"z\") # => [\"x\", \"a\", \"y\", \"b\", \"c\", \"z\"]\n  # ```\n  def insert(index : Int, object : T) : self\n    if index == 0\n      return unshift(object)\n    end\n\n    if index < 0\n      index += size + 1\n    end\n\n    unless 0 <= index <= size\n      raise IndexError.new\n    end\n\n    check_needs_resize\n    (@buffer + index + 1).move_from(@buffer + index, size - index)\n    @buffer[index] = object\n\n    @size += 1\n\n    self\n  end\n\n  # Inserts all of the elements from *other* before the element at *index*.\n  #\n  # This method shifts the element currently at *index* (if any) and any\n  # subsequent elements to the right, increasing their indices. If the value\n  # of *index* is negative, counting starts from the end of the array.\n  # For example, `-1` indicates insertion after the last element, `-2` before\n  # the last element.\n  #\n  # Raises `IndexError` if the *index* is out of bounds.\n  #\n  # ```\n  # fruits = [\"Apple\"]\n  # newFruits = [\"Dragonfruit\", \"Elderberry\"]\n  #\n  # fruits.insert_all(1, newFruits)             # => [\"Apple\", \"Dragonfruit\", \"Elderberry\"]\n  # fruits.insert_all(-3, [\"Banana\", \"Cherry\"]) # => [\"Apple\", \"Banana\", \"Cherry\", \"Dragonfruit\", \"Elderberry\"]\n  #\n  # fruits.insert_all(6, [\"invalid\"])  # raises IndexError\n  # fruits.insert_all(-7, [\"indices\"]) # raises IndexError\n  # ```\n  def insert_all(index : Int, other : Indexable) : self\n    other_size = other.size\n\n    return self if other_size == 0\n\n    if index < 0\n      index += size + 1\n    end\n\n    unless 0 <= index <= size\n      raise IndexError.new\n    end\n\n    resize_if_cant_insert(other_size)\n    (@buffer + index).move_to(@buffer + index + other_size, @size - index)\n\n    insert_elements_at(other, index)\n\n    @size += other_size\n\n    self\n  end\n\n  private def insert_elements_at(other : Array | Slice | StaticArray, index : Int) : Nil\n    (@buffer + index).copy_from(other.to_unsafe, other.size)\n  end\n\n  private def insert_elements_at(other : Deque, index : Int) : Nil\n    ptr = @buffer + index\n    Deque.half_slices(other) do |slice|\n      ptr.copy_from(slice.to_unsafe, slice.size)\n      ptr += slice.size\n    end\n  end\n\n  private def insert_elements_at(other, index : Int) : Nil\n    appender = (@buffer + index).appender\n    other.each do |elem|\n      appender << elem\n    end\n  end\n\n  def inspect(io : IO) : Nil\n    to_s io\n  end\n\n  # Returns the last *n* elements of the array.\n  #\n  # ```\n  # [1, 2, 3].last(2) # => [2, 3]\n  # [1, 2, 3].last(4) # => [1, 2, 3]\n  # ```\n  def last(n : Int) : Array(T)\n    if n < @size\n      self[@size - n, n]\n    else\n      dup\n    end\n  end\n\n  # :nodoc:\n  protected def size=(size : Int)\n    @size = size.to_i\n  end\n\n  # Optimized version of `Enumerable#map`.\n  def map(& : T -> U) : Array(U) forall U\n    Array(U).new(size) { |i| yield @buffer[i] }\n  end\n\n  # Modifies `self`, keeping only the elements in the collection for which the\n  # passed block is truthy. Returns `self`.\n  #\n  # ```\n  # ary = [1, 6, 2, 4, 8]\n  # ary.select! { |x| x > 3 }\n  # ary # => [6, 4, 8]\n  # ```\n  #\n  # See also: `Array#select`.\n  def select!(& : T ->) : self\n    reject! { |elem| !yield(elem) }\n  end\n\n  # Modifies `self`, keeping only the elements in the collection for which\n  # `pattern === element`.\n  #\n  # ```\n  # ary = [1, 6, 2, 4, 8]\n  # ary.select!(3..7)\n  # ary # => [6, 4]\n  # ```\n  #\n  # See also: `Array#reject!`.\n  def select!(pattern) : self\n    self.select! { |elem| pattern === elem }\n  end\n\n  # Modifies `self`, deleting the elements in the collection for which the\n  # passed block is truthy. Returns `self`.\n  #\n  # ```\n  # ary = [1, 6, 2, 4, 8]\n  # ary.reject! { |x| x > 3 }\n  # ary # => [1, 2]\n  # ```\n  #\n  # See also: `Array#reject`.\n  def reject!(& : T ->) : self\n    internal_delete { |e| yield e }\n    self\n  end\n\n  # Modifies `self`, deleting the elements in the collection for which\n  # `pattern === element`.\n  #\n  # ```\n  # ary = [1, 6, 2, 4, 8]\n  # ary.reject!(3..7)\n  # ary # => [1, 2, 8]\n  # ```\n  #\n  # See also: `Array#select!`.\n  def reject!(pattern) : self\n    reject! { |elem| pattern === elem }\n    self\n  end\n\n  # `reject!` and `delete` implementation: returns a tuple {x, y}\n  # with x being self/nil (modified, not modified)\n  # and y being the last matching element, or nil\n  private def internal_delete(&)\n    i1 = 0\n    i2 = 0\n    match = nil\n    while i1 < @size\n      e = @buffer[i1]\n      if yield e, i1\n        match = e\n      else\n        if i1 != i2\n          @buffer[i2] = e\n        end\n        i2 += 1\n      end\n\n      i1 += 1\n    end\n\n    if i2 != i1\n      count = i1 - i2\n      @size -= count\n      (@buffer + @size).clear(count)\n      {self, match}\n    else\n      {nil, match}\n    end\n  end\n\n  # Optimized version of `Enumerable#map_with_index`.\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there.\n  #\n  # ```\n  # gems = [\"crystal\", \"pearl\", \"diamond\"]\n  # results = gems.map_with_index { |gem, i| \"#{i}: #{gem}\" }\n  # results # => [\"0: crystal\", \"1: pearl\", \"2: diamond\"]\n  # ```\n  def map_with_index(offset = 0, & : T, Int32 -> _)\n    Array.new(size) { |i| yield @buffer[i], offset + i }\n  end\n\n  # Returns an `Array` with the first *count* elements removed\n  # from the original array.\n  #\n  # If *count* is bigger than the number of elements in the array, returns an empty array.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].skip(3) # => [4, 5, 6]\n  # ```\n  def skip(count : Int) : Array(T)\n    raise ArgumentError.new(\"Attempt to skip negative size\") if count < 0\n\n    new_size = Math.max(size - count, 0)\n    Array(T).build(new_size) do |buffer|\n      buffer.copy_from(to_unsafe + count, new_size)\n      new_size\n    end\n  end\n\n  # Returns a new `Array` that is a one-dimensional flattening of `self` (recursively).\n  #\n  # That is, for every element that is an array or an iterator, extract its elements into the new array.\n  #\n  # ```\n  # s = [1, 2, 3]          # => [1, 2, 3]\n  # t = [4, 5, 6, [7, 8]]  # => [4, 5, 6, [7, 8]]\n  # u = [9, [10, 11].each] # => [9, #<Indexable::ItemIterator>]\n  # a = [s, t, u, 12, 13]  # => [[1, 2, 3], [4, 5, 6, [7, 8]], 9, #<Indexable::ItemIterator>, 12, 13]\n  # a.flatten              # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]\n  # ```\n  def flatten\n    FlattenHelper(typeof(FlattenHelper.element_type(self))).flatten(self)\n  end\n\n  # Returns an `Array` of all ordered combinations of elements taken from each\n  # of the *arrays* as `Array`s.\n  # Traversal of elements starts from the last given array.\n  @[Deprecated(\"Use `Indexable.cartesian_product(indexables : Indexable(Indexable))` instead\")]\n  def self.product(arrays : Array(Array))\n    Indexable.cartesian_product(arrays)\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `Indexable.cartesian_product(indexables : Indexable(Indexable))` instead\")]\n  def self.product(*arrays : Array)\n    Indexable.cartesian_product(arrays)\n  end\n\n  # Yields each ordered combination of the elements taken from each of the\n  # *arrays* as `Array`s.\n  # Traversal of elements starts from the last given array.\n  @[Deprecated(\"Use `Indexable.each_cartesian(indexables : Indexable(Indexable), reuse = false, &block)` instead\")]\n  def self.each_product(arrays : Array(Array), reuse = false, &)\n    Indexable.each_cartesian(arrays, reuse: reuse) { |r| yield r }\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `Indexable.each_cartesian(indexables : Indexable(Indexable), reuse = false, &block)` instead\")]\n  def self.each_product(*arrays : Array, reuse = false, &)\n    Indexable.each_cartesian(arrays, reuse: reuse) { |r| yield r }\n  end\n\n  def repeated_permutations(size : Int = self.size) : Array(Array(T))\n    ary = [] of Array(T)\n    each_repeated_permutation(size) do |a|\n      ary << a\n    end\n    ary\n  end\n\n  def each_repeated_permutation(size : Int = self.size, reuse = false, &) : Nil\n    n = self.size\n    return if size != 0 && n == 0\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    if size == 0\n      yield([] of T)\n    else\n      Indexable.each_cartesian(Array.new(size, self), reuse: reuse) { |r| yield r }\n    end\n  end\n\n  # Removes the last value from `self`, at index *size - 1*.\n  # This method returns the removed value.\n  # Raises `IndexError` if array is of 0 size.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.pop # => \"c\"\n  # a     # => [\"a\", \"b\"]\n  # ```\n  #\n  # See also: `#truncate`.\n  def pop : T\n    pop { raise IndexError.new }\n  end\n\n  # Removes the last value from `self`.\n  # If the array is empty, the given block is called.\n  #\n  # ```\n  # a = [1]\n  # a.pop { \"Testing\" } # => 1\n  # a.pop { \"Testing\" } # => \"Testing\"\n  # ```\n  #\n  # See also: `#truncate`.\n  def pop(&)\n    if @size == 0\n      yield\n    else\n      @size -= 1\n      value = @buffer[@size]\n      (@buffer + @size).clear\n\n      # If we remain empty we also take the chance to\n      # reset the buffer to its original position.\n      if empty? && @offset_to_buffer != 0\n        reset_buffer_to_root_buffer\n      end\n\n      value\n    end\n  end\n\n  # Removes the last *n* values from `self`, at index *size - 1*.\n  # This method returns an array of the removed values, with the original order preserved.\n  #\n  # If *n* is greater than the size of `self`, all values will be removed from `self`\n  # without raising an error.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.pop(2) # => [\"b\", \"c\"]\n  # a        # => [\"a\"]\n  #\n  # a = [\"a\", \"b\", \"c\"]\n  # a.pop(4) # => [\"a\", \"b\", \"c\"]\n  # a        # => []\n  # ```\n  #\n  # See also: `#truncate`.\n  def pop(n : Int) : Array(T)\n    if n < 0\n      raise ArgumentError.new(\"Can't pop negative count\")\n    end\n\n    n = Math.min(n, @size)\n    ary = Array(T).new(n) { |i| @buffer[@size - n + i] }\n\n    @size -= n\n    (@buffer + @size).clear(n)\n\n    ary\n  end\n\n  # Like `pop`, but returns `nil` if `self` is empty.\n  #\n  # See also: `#truncate`.\n  def pop? : T?\n    pop { nil }\n  end\n\n  # Returns an `Array` of all ordered combinations of elements taken from each\n  # of `self` and *ary* as `Tuple`s.\n  # Traversal of elements starts from *ary*.\n  @[Deprecated(\"Use `Indexable#cartesian_product(*others : Indexable)` instead\")]\n  def product(ary : Array(U)) forall U\n    cartesian_product(ary)\n  end\n\n  # Yields each ordered combination of the elements taken from each of `self`\n  # and *enumerable* as a `Tuple`.\n  # Traversal of elements starts from *enumerable*.\n  @[Deprecated(\"Use `Indexable#each_cartesian(*others : Indexable, &block)` instead\")]\n  def product(enumerable : Enumerable, &)\n    self.each { |a| enumerable.each { |b| yield a, b } }\n  end\n\n  # Append. Pushes one value to the end of `self`, given that the type of the value is *T*\n  # (which might be a single type or a union of types).\n  # This method returns `self`, so several calls can be chained.\n  # See `pop` for the opposite effect.\n  #\n  # ```\n  # a = [\"a\", \"b\"]\n  # a.push(\"c\") # => [\"a\", \"b\", \"c\"]\n  # a.push(1)   # Errors, because the array only accepts String.\n  #\n  # a = [\"a\", \"b\"] of (Int32 | String)\n  # a.push(\"c\") # => [\"a\", \"b\", \"c\"]\n  # a.push(1)   # => [\"a\", \"b\", \"c\", 1]\n  # ```\n  def push(value : T) : self\n    check_needs_resize\n    @buffer[@size] = value\n    @size += 1\n    self\n  end\n\n  # Append multiple values. The same as `push`, but takes an arbitrary number\n  # of values to push into `self`. Returns `self`.\n  #\n  # ```\n  # a = [\"a\"]\n  # a.push(\"b\", \"c\") # => [\"a\", \"b\", \"c\"]\n  # ```\n  def push(*values : T) : self\n    resize_if_cant_insert(values.size)\n\n    values.each_with_index do |value, i|\n      @buffer[@size + i] = value\n    end\n    @size += values.size\n    self\n  end\n\n  # Replaces the contents of `self` with the contents of *other*.\n  # This resizes the Array to a greater capacity but does not free memory if the given array is smaller.\n  #\n  # ```\n  # a1 = [1, 2, 3]\n  # a1.replace([1])\n  # a1                    # => [1]\n  # a1.remaining_capacity # => 3\n  # a2 = [1]\n  # a2.replace([1, 2, 3])\n  # a2 # => [1, 2, 3]\n  # ```\n  def replace(other : Array) : self\n    if other.size > @capacity\n      reset_buffer_to_root_buffer\n      resize_to_capacity(calculate_new_capacity(other.size))\n    elsif other.size > remaining_capacity\n      shift_buffer_by(remaining_capacity - other.size)\n    elsif other.size < @size\n      (@buffer + other.size).clear(@size - other.size)\n    end\n\n    @buffer.copy_from(other.to_unsafe, other.size)\n    @size = other.size\n    self\n  end\n\n  # Returns an array with all the elements in the collection reversed.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.reverse # => [3, 2, 1]\n  # ```\n  def reverse : Array(T)\n    Array(T).new(size) { |i| @buffer[size - i - 1] }\n  end\n\n  # :inherit:\n  def rotate!(n : Int = 1) : self\n    to_unsafe_slice.rotate!(n)\n    self\n  end\n\n  # Returns an array with all the elements shifted to the left `n` times.\n  #\n  # ```\n  # a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n  # a.rotate    # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]\n  # a.rotate(1) # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]\n  # a.rotate(3) # => [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]\n  # a           # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n  # ```\n  def rotate(n = 1) : Array(T)\n    return self if size == 0\n    n %= size\n    return self if n == 0\n    res = Array(T).new(size)\n    res.to_unsafe.copy_from(@buffer + n, size - n)\n    (res.to_unsafe + size - n).copy_from(@buffer, n)\n    res.size = size\n    res\n  end\n\n  # Removes the first value of `self`, at index 0. This method returns the removed value.\n  # If the array is empty, it raises `IndexError`.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.shift # => \"a\"\n  # a       # => [\"b\", \"c\"]\n  # ```\n  #\n  # See also: `#truncate`.\n  def shift : T\n    shift { raise IndexError.new }\n  end\n\n  # Removes the first value of `self`, at index 0, or otherwise invokes the given block.\n  # This method returns the removed value.\n  # If the array is empty, it invokes the given block and returns its value.\n  #\n  # ```\n  # a = [\"a\"]\n  # a.shift { \"empty!\" } # => \"a\"\n  # a                    # => []\n  # a.shift { \"empty!\" } # => \"empty!\"\n  # a                    # => []\n  # ```\n  #\n  # See also: `#truncate`.\n  def shift(&)\n    if @size == 0\n      yield\n    else\n      shift_when_not_empty\n    end\n  end\n\n  # Internal implementation of shift when we are sure the array is not empty\n  private def shift_when_not_empty\n    value = @buffer[0]\n    @size -= 1\n    @buffer.clear(1)\n\n    if empty?\n      reset_buffer_to_root_buffer\n    else\n      shift_buffer_by(1)\n    end\n\n    value\n  end\n\n  # Removes the first *n* values of `self`, starting at index 0.\n  # This method returns an array of the removed values.\n  #\n  # If *n* is greater than the size of `self`, all values will be removed from `self`\n  # without raising an error.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.shift # => \"a\"\n  # a       # => [\"b\", \"c\"]\n  #\n  # a = [\"a\", \"b\", \"c\"]\n  # a.shift(4) # => [\"a\", \"b\", \"c\"]\n  # a          # => []\n  # ```\n  #\n  # See also: `#truncate`.\n  def shift(n : Int) : Array(T)\n    if n < 0\n      raise ArgumentError.new(\"Can't shift negative count\")\n    end\n\n    n = Math.min(n, @size)\n    ary = Array(T).new(n) { |i| @buffer[i] }\n\n    @size -= n\n\n    @buffer.clear(n)\n\n    if empty?\n      reset_buffer_to_root_buffer\n    else\n      shift_buffer_by(n)\n    end\n\n    ary\n  end\n\n  # Removes the first value of `self`, at index 0. This method returns the removed value.\n  # If the array is empty, it returns `nil` without raising any error.\n  #\n  # ```\n  # a = [\"a\", \"b\"]\n  # a.shift? # => \"a\"\n  # a        # => [\"b\"]\n  # a.shift? # => \"b\"\n  # a        # => []\n  # a.shift? # => nil\n  # a        # => []\n  # ```\n  #\n  # See also: `#truncate`.\n  def shift? : T?\n    shift { nil }\n  end\n\n  # Returns a new instance with all elements in the collection randomized.\n  #\n  # See `Indexable::Mutable#shuffle!` for details.\n  def shuffle(random : Random? = nil) : Array(T)\n    dup.shuffle!(random)\n  end\n\n  # Returns a new instance with all elements sorted based on the return value of\n  # their comparison method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # a.sort # => [1, 2, 3]\n  # a      # => [3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#sort!` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def sort : Array(T)\n    dup.sort!\n  end\n\n  # Returns a new instance with all elements sorted based on the return value of\n  # their comparison method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # a.unstable_sort # => [1, 2, 3]\n  # a               # => [3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#unstable_sort!` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def unstable_sort : Array(T)\n    dup.unstable_sort!\n  end\n\n  # Returns a new instance with all elements sorted based on the comparator in the\n  # given block, using a stable sort algorithm.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # b = a.sort { |a, b| b <=> a }\n  #\n  # b # => [3, 2, 1]\n  # a # => [3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#sort!(&block : T, T -> U)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def sort(&block : T, T -> U) : Array(T) forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#sort_by`?\" %}\n    {% end %}\n\n    dup.sort! &block\n  end\n\n  # Returns a new instance with all elements sorted based on the comparator in the\n  # given block, using an unstable sort algorithm.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # b = a.unstable_sort { |a, b| b <=> a }\n  #\n  # b # => [3, 2, 1]\n  # a # => [3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#unstable_sort!(&block : T, T -> U)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def unstable_sort(&block : T, T -> U) : Array(T) forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#unstable_sort_by`?\" %}\n    {% end %}\n\n    dup.unstable_sort!(&block)\n  end\n\n  # :inherit:\n  def sort! : Array(T)\n    to_unsafe_slice.sort!\n    self\n  end\n\n  # :inherit:\n  def unstable_sort! : self\n    to_unsafe_slice.unstable_sort!\n    self\n  end\n\n  # :inherit:\n  def sort!(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#sort_by!`?\" %}\n    {% end %}\n\n    to_unsafe_slice.sort!(&block)\n    self\n  end\n\n  # :inherit:\n  def unstable_sort!(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#unstable_sort_by!`?\" %}\n    {% end %}\n\n    to_unsafe_slice.unstable_sort!(&block)\n    self\n  end\n\n  # Returns a new instance with all elements sorted by the output value of the\n  # block. The output values are compared via the comparison method `T#<=>`\n  # (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # a = %w(apple pear fig)\n  # b = a.sort_by { |word| word.size }\n  # b # => [\"fig\", \"pear\", \"apple\"]\n  # a # => [\"apple\", \"pear\", \"fig\"]\n  # ```\n  #\n  # If stability is expendable, `#unstable_sort_by(&block : T -> _)` provides a\n  # performance advantage over stable sort.\n  #\n  # See `Indexable::Mutable#sort_by!(&block : T -> _)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`.\n  def sort_by(&block : T -> _) : Array(T)\n    dup.sort_by! { |e| yield(e) }\n  end\n\n  # Returns a new instance with all elements sorted by the output value of the\n  # block. The output values are compared via the comparison method `#<=>`\n  # (see `Comparable#<=>`), using an unstable sort algorithm.\n  #\n  # ```\n  # a = %w(apple pear fig)\n  # b = a.unstable_sort_by { |word| word.size }\n  # b # => [\"fig\", \"pear\", \"apple\"]\n  # a # => [\"apple\", \"pear\", \"fig\"]\n  # ```\n  #\n  # If stability is necessary, use `#sort_by(&block : T -> _)` instead.\n  #\n  # See `Indexable::Mutable#unstable_sort!(&block : T -> _)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`.\n  def unstable_sort_by(&block : T -> _) : Array(T)\n    dup.unstable_sort_by! { |e| yield(e) }\n  end\n\n  # :inherit:\n  def sort_by!(&block : T -> _) : Array(T)\n    sorted = map { |e| {e, yield(e)} }.sort! { |x, y| x[1] <=> y[1] }\n    @size.times do |i|\n      @buffer[i] = sorted.to_unsafe[i][0]\n    end\n    self\n  end\n\n  # :inherit:\n  def unstable_sort_by!(&block : T -> _) : Array(T)\n    sorted = map { |e| {e, yield(e)} }.unstable_sort! { |x, y| x[1] <=> y[1] }\n    @size.times do |i|\n      @buffer[i] = sorted.to_unsafe[i][0]\n    end\n    self\n  end\n\n  def to_a : self\n    self\n  end\n\n  # Prints a nicely readable and concise string representation of this array\n  # to *io*.\n  #\n  # The result resembles an array literal but it does not necessarily compile.\n  #\n  # Each element is presented using its `#inspect(io)` result to avoid ambiguity.\n  def to_s(io : IO) : Nil\n    executed = exec_recursive(:to_s) do\n      io << '['\n      join io, \", \", &.inspect(io)\n      io << ']'\n    end\n    io << \"[...]\" unless executed\n  end\n\n  def pretty_print(pp) : Nil\n    executed = exec_recursive(:pretty_print) do\n      pp.list(\"[\", self, \"]\")\n    end\n    pp.text \"[...]\" unless executed\n  end\n\n  # Returns a pointer to the internal buffer where `self`'s elements are stored.\n  #\n  # This method is **unsafe** because it returns a pointer, and the pointed might eventually\n  # not be that of `self` if the array grows and its internal buffer is reallocated.\n  #\n  # ```\n  # ary = [1, 2, 3]\n  # ary.to_unsafe[0] # => 1\n  # ```\n  def to_unsafe : Pointer(T)\n    @buffer\n  end\n\n  # Assumes that `self` is an array of arrays and transposes the rows and columns.\n  #\n  # ```\n  # a = [[:a, :b], [:c, :d], [:e, :f]]\n  # a.transpose # => [[:a, :c, :e], [:b, :d, :f]]\n  # a           # => [[:a, :b], [:c, :d], [:e, :f]]\n  # ```\n  def transpose\n    return Array(Array(typeof(Enumerable.element_type Enumerable.element_type self))).new if empty?\n\n    len = self[0].size\n    (1...@size).each do |i|\n      l = self[i].size\n      raise IndexError.new if len != l\n    end\n\n    Array(Array(typeof(Enumerable.element_type Enumerable.element_type self))).new(len) do |i|\n      Array(typeof(Enumerable.element_type Enumerable.element_type self)).new(@size) do |j|\n        self[j][i]\n      end\n    end\n  end\n\n  # Removes all elements except the *count* or less (if there aren't enough)\n  # elements starting at the given *start* index. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the array.\n  #\n  # Raises `IndexError` if the *start* index is out of range.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  #\n  # ```\n  # a = [0, 1, 4, 9, 16, 25]\n  # a.truncate(2, 3) # => [4, 9, 16]\n  # a                # => [4, 9, 16]\n  # ```\n  #\n  # See also: `#pop`, `#shift`.\n  def truncate(start : Int, count : Int) : self\n    start, count = normalize_start_and_count(start, count)\n\n    if count == 0\n      clear\n      reset_buffer_to_root_buffer\n    else\n      @buffer.clear(start)\n      (@buffer + start + count).clear(size - start - count)\n      @size = count\n      shift_buffer_by(start)\n    end\n\n    self\n  end\n\n  # Removes all elements except those within the given *range*. Returns `self`.\n  #\n  # ```\n  # a = [0, 1, 4, 9, 16, 25]\n  # a.truncate(1..-3) # => [1, 4, 9]\n  # a                 # => [1, 4, 9]\n  # ```\n  def truncate(range : Range) : self\n    truncate(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new)\n  end\n\n  # Returns a new `Array` by removing duplicate values in `self`.\n  #\n  # ```\n  # a = [\"a\", \"a\", \"b\", \"b\", \"c\"]\n  # a.uniq # => [\"a\", \"b\", \"c\"]\n  # a      # => [ \"a\", \"a\", \"b\", \"b\", \"c\" ]\n  # ```\n  def uniq : Array(T)\n    if size <= 1\n      return dup\n    end\n\n    # Heuristic: for a small array it's faster to do a linear scan\n    # than creating a Set to find out duplicates.\n    if size <= SMALL_ARRAY_SIZE\n      ary = Array(T).new\n      each do |elem|\n        ary << elem unless ary.includes?(elem)\n      end\n      return ary\n    end\n\n    # Convert the Array into a Set and then ask for its values\n    to_set.to_a\n  end\n\n  # Returns a new `Array` by removing duplicate values in `self`, using the block's\n  # value for comparison.\n  #\n  # ```\n  # a = [{\"student\", \"sam\"}, {\"student\", \"george\"}, {\"teacher\", \"matz\"}]\n  # a.uniq { |s| s[0] } # => [{\"student\", \"sam\"}, {\"teacher\", \"matz\"}]\n  # a                   # => [{\"student\", \"sam\"}, {\"student\", \"george\"}, {\"teacher\", \"matz\"}]\n  # ```\n  def uniq(& : T ->) : Array(T)\n    if size <= 1\n      dup\n    else\n      hash = to_lookup_hash { |elem| yield elem }\n      hash.values\n    end\n  end\n\n  # Removes duplicate elements from `self`. Returns `self`.\n  #\n  # ```\n  # a = [\"a\", \"a\", \"b\", \"b\", \"c\"]\n  # a.uniq! # => [\"a\", \"b\", \"c\"]\n  # a       # => [\"a\", \"b\", \"c\"]\n  # ```\n  def uniq! : self\n    if size <= 1\n      return self\n    end\n\n    # Heuristic: for small arrays we do a linear scan, which is usually\n    # faster than creating an intermediate Hash.\n    if size <= SMALL_ARRAY_SIZE\n      # We simply delete elements we've seen before\n      internal_delete do |elem, index|\n        (0...index).any? { |subindex| elem == to_unsafe[subindex] }\n      end\n      return self\n    end\n\n    uniq! &.itself\n  end\n\n  # Removes duplicate elements from `self`, using the block's value for comparison. Returns `self`.\n  #\n  # ```\n  # a = [{\"student\", \"sam\"}, {\"student\", \"george\"}, {\"teacher\", \"matz\"}]\n  # a.uniq! { |s| s[0] } # => [{\"student\", \"sam\"}, {\"teacher\", \"matz\"}]\n  # a                    # => [{\"student\", \"sam\"}, {\"teacher\", \"matz\"}]\n  # ```\n  def uniq!(& : T ->) : self\n    if size <= 1\n      return self\n    end\n\n    hash = to_lookup_hash { |elem| yield elem }\n    if size == hash.size\n      return self\n    end\n\n    old_size = @size\n    @size = hash.size\n    removed = old_size - @size\n    return self if removed == 0\n\n    ptr = @buffer\n    hash.each do |k, v|\n      ptr.value = v\n      ptr += 1\n    end\n\n    (@buffer + @size).clear(removed)\n\n    self\n  end\n\n  # Prepend. Adds *object* to the beginning of `self`, given that the type of the value is *T*\n  # (which might be a single type or a union of types).\n  # This method returns `self`, so several calls can be chained.\n  # See `shift` for the opposite effect.\n  #\n  # ```\n  # a = [\"a\", \"b\"]\n  # a.unshift(\"c\") # => [\"c\", \"a\", \"b\"]\n  # a.unshift(1)   # Errors, because the array only accepts String.\n  #\n  # a = [\"a\", \"b\"] of (Int32 | String)\n  # a.unshift(\"c\") # => [\"c\", \"a\", \"b\"]\n  # a.unshift(1)   # => [1, \"c\", \"a\", \"b\"]\n  # ```\n  def unshift(object : T) : self\n    check_needs_resize_for_unshift\n    shift_buffer_by(-1)\n    @buffer.value = object\n    @size += 1\n\n    self\n  end\n\n  # Prepend multiple values. The same as `unshift`, but takes an arbitrary number\n  # of values to add to the array. Returns `self`.\n  def unshift(*values : T) : self\n    values.reverse_each do |value|\n      unshift(value)\n    end\n    self\n  end\n\n  private def check_needs_resize\n    # We have to compare against the actual capacity in case `@buffer` was moved\n    return unless needs_resize?\n\n    # If the array is not empty and more than half of the elements were shifted\n    # then we avoid a resize and just move the elements to the left.\n    # This is an heuristic. We could always try to move the elements if\n    # `@offset_to_buffer` is positive but it might happen that a user does\n    # `shift` + `push` in succession and it will produce a lot of memcopies.\n    #\n    # Note: `@offset_to_buffer != 0` is not redundant because `@capacity` might be 1.\n    # and so `@capacity / 2` is 0 and `@offset_to_buffer >= @capacity / 2` would hold\n    # without it.\n    if @capacity != 0 && @offset_to_buffer != 0 && @offset_to_buffer >= @capacity / 2\n      # Given\n      #\n      #     [-, -, -, 'c', 'd', -]\n      #      |         |\n      #      |         ^-- `@buffer`\n      #      |\n      #      ^-- root_buffer\n      #\n      # and:\n      # - @size is 2\n      # - @capacity is 6\n      # - @offset_to_buffer is 3\n      # - remaining_capacity is 3\n\n      # First copy the remaining elements in the array to the front\n      #\n      #     [-, -, -, 'c', 'd', -]\n      #               ^-------^\n      #                   |\n      #                   ^-- copy this\n      #\n      #     [-, -, -, 'c', 'd', -]\n      #     ^----^\n      #       |\n      #       ^-- here\n      #\n      # We get:\n      #\n      #     ['c', 'd', '-', 'c', 'd', -]\n      root_buffer.copy_from(@buffer, @size)\n\n      # Then after that we have to clear the rest of the elements\n      #\n      #     ['c', 'd', '-', 'c', 'd', -]\n      #              ^-------------^\n      #                     |\n      #                     ^-- clear this\n      # We get:\n      #\n      #     ['c', 'd', -, -, -, -]\n      (root_buffer + @size).clear(@offset_to_buffer)\n\n      # Move the buffer pointer to where it was originally allocated,\n      # and now we don't have any offset to the root buffer\n      reset_buffer_to_root_buffer\n    else\n      increase_capacity\n    end\n  end\n\n  private def needs_resize?\n    @size == remaining_capacity\n  end\n\n  private def check_needs_resize_for_unshift\n    return unless @offset_to_buffer == 0\n\n    # If we have no more room left before the beginning of the array\n    # we make the array larger, but point the buffer to start at the middle\n    # of the entire allocated memory. In this way, if more elements are unshift\n    # later we won't need a reallocation right away. This is similar to what\n    # happens when we push and we don't have more room, except that toward\n    # the beginning.\n\n    half_capacity = @capacity // 2\n    if @capacity != 0 && half_capacity != 0 && @size <= half_capacity\n      # Apply the same heuristic as the case for pushing elements to the array,\n      # but in backwards: (note that `@size` can be 0 here)\n\n      # `['c', 'd', -, -, -, -] (@size = 2)`\n      (root_buffer + half_capacity).copy_from(@buffer, @size)\n\n      # `['c', 'd', -, 'c', 'd', -]`\n      root_buffer.clear(@size)\n\n      # `[-, -, -, 'c', 'd', -]`\n      shift_buffer_by(half_capacity)\n    else\n      increase_capacity_for_unshift\n    end\n  end\n\n  def remaining_capacity : Int32\n    @capacity - @offset_to_buffer\n  end\n\n  # behaves like `calculate_new_capacity(@capacity + 1)`\n  private def calculate_new_capacity\n    return INITIAL_CAPACITY if @capacity == 0\n\n    if @capacity < CAPACITY_THRESHOLD\n      @capacity * 2\n    else\n      @capacity + (@capacity + 3 * CAPACITY_THRESHOLD) // 4\n    end\n  end\n\n  private def calculate_new_capacity(new_size)\n    # Resizing is done via `Pointer#realloc` on the root buffer, so the space\n    # between the root and real buffers remains untouched\n    new_size += @offset_to_buffer\n\n    new_capacity = @capacity == 0 ? INITIAL_CAPACITY : @capacity\n    while new_capacity < new_size\n      if new_capacity < CAPACITY_THRESHOLD\n        new_capacity *= 2\n      else\n        new_capacity += (new_capacity + 3 * CAPACITY_THRESHOLD) // 4\n      end\n    end\n    new_capacity\n  end\n\n  private def increase_capacity\n    resize_to_capacity(calculate_new_capacity)\n  end\n\n  private def resize_to_capacity(capacity)\n    @capacity = capacity\n    if @buffer\n      @buffer = root_buffer.realloc(@capacity) + @offset_to_buffer\n    else\n      @buffer = Pointer(T).malloc(@capacity)\n    end\n  end\n\n  # Similar to `increase_capacity`, except that after reallocating the buffer\n  # we point it to the middle of the buffer in case more unshifts come right away.\n  # This assumes @offset_to_buffer is zero.\n  private def increase_capacity_for_unshift\n    resize_to_capacity_for_unshift(calculate_new_capacity)\n  end\n\n  private def resize_to_capacity_for_unshift(capacity)\n    old_capacity, @capacity = @capacity, capacity\n    offset = @capacity - old_capacity\n\n    if @buffer\n      @buffer = root_buffer.realloc(@capacity)\n      @buffer.move_to(@buffer + offset, old_capacity)\n      @buffer.clear(offset)\n    else\n      @buffer = Pointer(T).malloc(@capacity)\n    end\n\n    shift_buffer_by(offset)\n  end\n\n  private def resize_if_cant_insert(insert_size)\n    # Resize if we exceed the remaining capacity. This is less than `@capacity`\n    # if the array has been shifted and `@offset_to_buffer` is nonzero\n    new_size = @size + insert_size\n    if new_size > remaining_capacity\n      resize_to_capacity(calculate_new_capacity(new_size))\n    end\n  end\n\n  # Returns a pointer to the buffer that was originally allocated/reallocated\n  # for this array.\n  private def root_buffer\n    @buffer - @offset_to_buffer\n  end\n\n  # Moves `@buffer` by n while at the same time increments `@offset_to_buffer`\n  private def shift_buffer_by(n)\n    @offset_to_buffer += n\n    @buffer += n\n  end\n\n  # Makes `@buffer` point at the original buffer that was allocated/reallocated.\n  private def reset_buffer_to_root_buffer\n    @buffer = root_buffer\n    @offset_to_buffer = 0\n  end\n\n  private def to_unsafe_slice\n    Slice.new(@buffer, size)\n  end\n\n  private def to_unsafe_slice(start : Int, count : Int)\n    start, count = normalize_start_and_count(start, count)\n    Slice.new(@buffer + start, count)\n  end\n\n  protected def to_lookup_hash(& : T -> U) forall U\n    each_with_object(Hash(U, T).new) do |o, h|\n      key = yield o\n      unless h.has_key?(key)\n        h[key] = o\n      end\n    end\n  end\n\n  def index(object, offset : Int = 0)\n    # Optimize for the case of looking for a byte in a byte slice\n    if T.is_a?(UInt8.class) &&\n       (object.is_a?(UInt8) || (object.is_a?(Int) && 0 <= object < 256))\n      return to_unsafe_slice.fast_index(object, offset)\n    end\n\n    super\n  end\n\n  private struct FlattenHelper(T)\n    def self.flatten(ary)\n      result = [] of T\n      flatten ary, result\n      result\n    end\n\n    def self.flatten(ary : Array, result)\n      ary.each do |elem|\n        flatten elem, result\n      end\n    end\n\n    def self.flatten(iter : Iterator, result)\n      iter.each do |elem|\n        flatten elem, result\n      end\n    end\n\n    def self.flatten(other : T, result)\n      result << other\n    end\n\n    def self.element_type(ary)\n      case ary\n      when Array, Iterator\n        ary.each { |elem| return element_type(elem) }\n        ::raise \"\"\n      else\n        ary\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/atomic.cr",
    "content": "require \"llvm/enums/atomic\"\n\n# A value that may be updated atomically.\n#\n# * If `T` is a reference type, or a union type containing only\n#   reference types or `Nil`, then only `#compare_and_set`, `#swap`, `#set`,\n#   `#lazy_set`, `#get`, and `#lazy_get` are available.\n# * If `T` is a pointer type, then the above methods plus `#max` and `#min` are\n#   available.\n# * If `T` is a non-union primitive integer type or enum type, then all\n#   operations are supported.\nstruct Atomic(T)\n  # Specifies how memory accesses, including non atomic, are to be reordered\n  # around atomics. Follows the C/C++ semantics:\n  # <https://en.cppreference.com/w/c/atomic/memory_order>.\n  #\n  # By default atomics use the sequentially consistent ordering, which has the\n  # strongest guarantees. If all you need is to increment a counter, a relaxed\n  # ordering may be enough. If you need to synchronize access to other memory\n  # (e.g. locks) you may try the acquire/release semantics that may be faster on\n  # some architectures (e.g. X86) but remember that an acquire must be paired\n  # with a release for the ordering to be guaranteed.\n  #\n  # The code generation always enforces the selected memory order, even on\n  # weak CPU architectures (e.g. ARM32), with the exception of the Relaxed\n  # memory order where only the operation itself is atomic.\n  enum Ordering\n    Relaxed                = LLVM::AtomicOrdering::Monotonic\n    Acquire                = LLVM::AtomicOrdering::Acquire\n    Release                = LLVM::AtomicOrdering::Release\n    AcquireRelease         = LLVM::AtomicOrdering::AcquireRelease\n    SequentiallyConsistent = LLVM::AtomicOrdering::SequentiallyConsistent\n  end\n\n  # Adds an explicit memory barrier with the specified memory order guarantee.\n  macro fence(ordering = :sequentially_consistent)\n    ::Atomic::Ops.fence({{ordering}}, false)\n  end\n\n  # Creates an Atomic with the given initial value.\n  def initialize(@value : T)\n    {% if !T.union? && (T == Bool || T == Char || T < Int::Primitive || T < Enum) %}\n      # Support integer types, enum types, bool or char (because it's represented as an integer)\n    {% elsif T < Pointer %}\n      # Support pointer types\n    {% elsif T.union_types.all? { |t| t == Nil || t < Reference } && T != Nil %}\n      # Support reference types, or union types with only nil or reference types\n    {% else %}\n      {% raise \"Can only create Atomic with primitive integer types, pointer types, reference types or nilable reference types, not #{T}\" %}\n    {% end %}\n  end\n\n  # Compares this atomic's value with *cmp*:\n  #\n  # * if they are equal, sets the value to *new*, and returns `{old_value, true}`\n  # * if they are not equal the value remains the same, and returns `{old_value, false}`\n  #\n  # Reference types are compared by `#same?`, not `#==`.\n  #\n  # ```\n  # atomic = Atomic.new(1)\n  #\n  # atomic.compare_and_set(2, 3) # => {1, false}\n  # atomic.get                   # => 1\n  #\n  # atomic.compare_and_set(1, 3) # => {1, true}\n  # atomic.get                   # => 3\n  # ```\n  def compare_and_set(cmp : T, new : T) : {T, Bool}\n    cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :sequentially_consistent, :sequentially_consistent)\n  end\n\n  # Compares this atomic's value with *cmp* using explicit memory orderings:\n  #\n  # * if they are equal, sets the value to *new*, and returns `{old_value, true}`\n  # * if they are not equal the value remains the same, and returns `{old_value, false}`\n  #\n  # Reference types are compared by `#same?`, not `#==`.\n  #\n  # ```\n  # atomic = Atomic.new(0_u32)\n  #\n  # value = atomic.get(:acquire)\n  # loop do\n  #   value, success = atomic.compare_and_set(value, value &+ 1, :acquire_release, :acquire)\n  #   break if success\n  # end\n  # ```\n  def compare_and_set(cmp : T, new : T, success_ordering : Ordering, failure_ordering : Ordering) : {T, Bool}\n    case {success_ordering, failure_ordering}\n    when {.relaxed?, .relaxed?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :monotonic, :monotonic)\n    when {.acquire?, .relaxed?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :acquire, :monotonic)\n    when {.acquire?, .acquire?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :acquire, :acquire)\n    when {.release?, .relaxed?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :release, :monotonic)\n    when {.release?, .acquire?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :release, :acquire)\n    when {.acquire_release?, .relaxed?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :acquire_release, :monotonic)\n    when {.acquire_release?, .acquire?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :acquire_release, :acquire)\n    when {.sequentially_consistent?, .relaxed?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :sequentially_consistent, :monotonic)\n    when {.sequentially_consistent?, .acquire?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :sequentially_consistent, :acquire)\n    when {.sequentially_consistent?, .sequentially_consistent?}\n      cast_from Ops.cmpxchg(as_pointer, cast_to(cmp), cast_to(new), :sequentially_consistent, :sequentially_consistent)\n    else\n      if failure_ordering.release? || failure_ordering.acquire_release?\n        raise ArgumentError.new(\"Failure ordering cannot include release semantics\")\n      end\n      raise ArgumentError.new(\"Failure ordering shall be no stronger than success ordering\")\n    end\n  end\n\n  # Performs `atomic_value &+= value`. Returns the old value.\n  #\n  # `T` cannot contain any pointer or reference types.\n  #\n  # ```\n  # atomic = Atomic.new(1)\n  # atomic.add(2) # => 1\n  # atomic.get    # => 3\n  # ```\n  def add(value : T, ordering : Ordering = :sequentially_consistent) : T\n    check_pointer_type\n    check_reference_type\n    check_bool_type\n    atomicrmw(:add, pointerof(@value), value, ordering)\n  end\n\n  # Performs `atomic_value &-= value`. Returns the old value.\n  #\n  # `T` cannot contain any pointer or reference types.\n  #\n  # ```\n  # atomic = Atomic.new(9)\n  # atomic.sub(2) # => 9\n  # atomic.get    # => 7\n  # ```\n  def sub(value : T, ordering : Ordering = :sequentially_consistent) : T\n    check_pointer_type\n    check_reference_type\n    check_bool_type\n    atomicrmw(:sub, pointerof(@value), value, ordering)\n  end\n\n  # Performs `atomic_value &= value`. Returns the old value.\n  #\n  # `T` cannot contain any pointer or reference types.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.and(3) # => 5\n  # atomic.get    # => 1\n  # ```\n  def and(value : T, ordering : Ordering = :sequentially_consistent) : T\n    check_pointer_type\n    check_reference_type\n    check_bool_type\n    atomicrmw(:and, pointerof(@value), value, ordering)\n  end\n\n  # Performs `atomic_value = ~(atomic_value & value)`. Returns the old value.\n  #\n  # `T` cannot contain any pointer or reference types.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.nand(3) # => 5\n  # atomic.get     # => -2\n  # ```\n  def nand(value : T, ordering : Ordering = :sequentially_consistent) : T\n    check_pointer_type\n    check_reference_type\n    check_bool_type\n    atomicrmw(:nand, pointerof(@value), value, ordering)\n  end\n\n  # Performs `atomic_value |= value`. Returns the old value.\n  #\n  # `T` cannot contain any pointer or reference types.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.or(2) # => 5\n  # atomic.get   # => 7\n  # ```\n  def or(value : T, ordering : Ordering = :sequentially_consistent) : T\n    check_pointer_type\n    check_reference_type\n    check_bool_type\n    atomicrmw(:or, pointerof(@value), value, ordering)\n  end\n\n  # Performs `atomic_value ^= value`. Returns the old value.\n  #\n  # `T` cannot contain any pointer or reference types.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.xor(3) # => 5\n  # atomic.get    # => 6\n  # ```\n  def xor(value : T, ordering : Ordering = :sequentially_consistent) : T\n    check_pointer_type\n    check_reference_type\n    check_bool_type\n    atomicrmw(:xor, pointerof(@value), value, ordering)\n  end\n\n  # Performs `atomic_value = {atomic_value, value}.max`. Returns the old value.\n  #\n  # `T` cannot contain any reference types.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  #\n  # atomic.max(3) # => 5\n  # atomic.get    # => 5\n  #\n  # atomic.max(10) # => 5\n  # atomic.get     # => 10\n  # ```\n  def max(value : T, ordering : Ordering = :sequentially_consistent)\n    check_reference_type\n    check_bool_type\n    {% if T < Enum %}\n      if @value.value.is_a?(Int::Signed)\n        atomicrmw(:max, pointerof(@value), value, ordering)\n      else\n        atomicrmw(:umax, pointerof(@value), value, ordering)\n      end\n    {% elsif T < Pointer %}\n      T.new(atomicrmw(:umax, pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new!(value.address), ordering))\n    {% elsif T < Int::Signed %}\n      atomicrmw(:max, pointerof(@value), value, ordering)\n    {% else %}\n      atomicrmw(:umax, pointerof(@value), value, ordering)\n    {% end %}\n  end\n\n  # Performs `atomic_value = {atomic_value, value}.min`. Returns the old value.\n  #\n  # `T` cannot contain any reference types.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  #\n  # atomic.min(10) # => 5\n  # atomic.get     # => 5\n  #\n  # atomic.min(3) # => 5\n  # atomic.get    # => 3\n  # ```\n  def min(value : T, ordering : Ordering = :sequentially_consistent)\n    check_reference_type\n    check_bool_type\n    {% if T < Enum %}\n      if @value.value.is_a?(Int::Signed)\n        atomicrmw(:min, pointerof(@value), value, ordering)\n      else\n        atomicrmw(:umin, pointerof(@value), value, ordering)\n      end\n    {% elsif T < Pointer %}\n      T.new(atomicrmw(:umin, pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new!(value.address), ordering))\n    {% elsif T < Int::Signed %}\n      atomicrmw(:min, pointerof(@value), value, ordering)\n    {% else %}\n      atomicrmw(:umin, pointerof(@value), value, ordering)\n    {% end %}\n  end\n\n  # Atomically sets this atomic's value to *value*. Returns the **old** value.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.swap(10) # => 5\n  # atomic.get      # => 10\n  # ```\n  def swap(value : T, ordering : Ordering = :sequentially_consistent)\n    {% if T < Pointer %}\n      T.new(atomicrmw(:xchg, pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new!(value.address), ordering))\n    {% elsif T.union_types.all? { |t| t == Nil || t < Reference } && T != Nil %}\n      address = atomicrmw(:xchg, pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new(value.as(Void*).address), ordering)\n      Pointer(T).new(address).as(T)\n    {% else %}\n      cast_from atomicrmw(:xchg, as_pointer, cast_to(value), ordering)\n    {% end %}\n  end\n\n  # Atomically sets this atomic's value to *value*. Returns the **new** value.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.set(10) # => 10\n  # atomic.get     # => 10\n  # ```\n  def set(value : T, ordering : Ordering = :sequentially_consistent) : T\n    case ordering\n    in .relaxed?\n      Ops.store(as_pointer, cast_to(value), :monotonic, true)\n    in .release?\n      Ops.store(as_pointer, cast_to(value), :release, true)\n    in .sequentially_consistent?\n      Ops.store(as_pointer, cast_to(value), :sequentially_consistent, true)\n    in .acquire?, .acquire_release?\n      raise ArgumentError.new(\"Atomic store cannot have acquire semantic\")\n    end\n    value\n  end\n\n  # **Non-atomically** sets this atomic's value to *value*. Returns the **new** value.\n  #\n  # ```\n  # atomic = Atomic.new(5)\n  # atomic.lazy_set(10) # => 10\n  # atomic.get          # => 10\n  # ```\n  #\n  # NOTE: use with caution, this may break atomic guarantees.\n  def lazy_set(@value : T) : T\n  end\n\n  # Atomically returns this atomic's value.\n  def get(ordering : Ordering = :sequentially_consistent) : T\n    case ordering\n    in .relaxed?\n      cast_from Ops.load(as_pointer, :monotonic, true)\n    in .acquire?\n      cast_from Ops.load(as_pointer, :acquire, true)\n    in .sequentially_consistent?\n      cast_from Ops.load(as_pointer, :sequentially_consistent, true)\n    in .release?, .acquire_release?\n      raise ArgumentError.new(\"Atomic load cannot have release semantic\")\n    end\n  end\n\n  # **Non-atomically** returns this atomic's value.\n  #\n  # NOTE: use with caution, this may break atomic guarantees.\n  def lazy_get\n    @value\n  end\n\n  private macro check_bool_type\n    {% if T == Bool %}\n      {% raise \"Cannot call `#{@type}##{@def.name}` for `#{T}` type\" %}\n    {% end %}\n  end\n\n  private macro check_pointer_type\n    {% if T < Pointer %}\n      {% raise \"Cannot call `#{@type}##{@def.name}` as `#{T}` is a pointer type\" %}\n    {% end %}\n  end\n\n  private macro check_reference_type\n    {% if T.union_types.all? { |t| t == Nil || t < Reference } && T != Nil %}\n      {% raise \"Cannot call `#{@type}##{@def.name}` as `#{T}` is a reference type\" %}\n    {% end %}\n  end\n\n  private macro atomicrmw(operation, pointer, value, ordering)\n    case ordering\n    in .relaxed?\n      Ops.atomicrmw({{operation}}, {{pointer}}, {{value}}, :monotonic, false)\n    in .acquire?\n      Ops.atomicrmw({{operation}}, {{pointer}}, {{value}}, :acquire, false)\n    in .release?\n      Ops.atomicrmw({{operation}}, {{pointer}}, {{value}}, :release, false)\n    in .acquire_release?\n      Ops.atomicrmw({{operation}}, {{pointer}}, {{value}}, :acquire_release, false)\n    in .sequentially_consistent?\n      Ops.atomicrmw({{operation}}, {{pointer}}, {{value}}, :sequentially_consistent, false)\n    end\n  end\n\n  # :nodoc:\n  module Ops\n    # Defines methods that directly map to LLVM instructions related to atomic operations.\n\n    @[Primitive(:cmpxchg)]\n    def self.cmpxchg(ptr : T*, cmp : T, new : T, success_ordering : LLVM::AtomicOrdering, failure_ordering : LLVM::AtomicOrdering) : {T, Bool} forall T\n    end\n\n    @[Primitive(:atomicrmw)]\n    def self.atomicrmw(op : LLVM::AtomicRMWBinOp, ptr : T*, val : T, ordering : LLVM::AtomicOrdering, singlethread : Bool) : T forall T\n    end\n\n    @[Primitive(:fence)]\n    def self.fence(ordering : LLVM::AtomicOrdering, singlethread : Bool) : Nil\n    end\n\n    @[Primitive(:load_atomic)]\n    def self.load(ptr : T*, ordering : LLVM::AtomicOrdering, volatile : Bool) : T forall T\n    end\n\n    @[Primitive(:store_atomic)]\n    def self.store(ptr : T*, value : T, ordering : LLVM::AtomicOrdering, volatile : Bool) : Nil forall T\n    end\n  end\n\n  @[AlwaysInline]\n  private def as_pointer\n    {% if T == Bool %}\n      # assumes that a bool sizeof/alignof is 1 byte (and that a struct wrapping\n      # a single boolean ivar has a sizeof/alignof of at least 1 byte, too) so\n      # there is enough padding, and we can safely cast the int1 representation\n      # of a bool as an int8\n      pointerof(@value).as(Int8*)\n    {% else %}\n      pointerof(@value)\n    {% end %}\n  end\n\n  @[AlwaysInline]\n  private def cast_to(value)\n    {% if T == Bool %}\n      value.unsafe_as(Int8)\n    {% else %}\n      value.as(T)\n    {% end %}\n  end\n\n  @[AlwaysInline]\n  private def cast_from(value : Tuple)\n    {% if T == Bool %}\n      {value[0].unsafe_as(Bool), value[1]}\n    {% else %}\n      value\n    {% end %}\n  end\n\n  @[AlwaysInline]\n  private def cast_from(value)\n    {% if T == Bool %}\n      value.unsafe_as(Bool)\n    {% else %}\n      value\n    {% end %}\n  end\nend\n\n# An atomic flag, that can be set or not.\n#\n# Concurrency safe. If many fibers try to set the atomic in parallel, only one\n# will succeed.\n#\n# Example:\n# ```\n# flag = Atomic::Flag.new\n# flag.test_and_set # => true\n# flag.test_and_set # => false\n# flag.clear\n# flag.test_and_set # => true\n# ```\n@[Deprecated(\"Use Atomic(Bool) instead.\")]\nstruct Atomic::Flag\n  def initialize\n    @value = Atomic(Bool).new(false)\n  end\n\n  # Atomically tries to set the flag. Only succeeds and returns `true` if the\n  # flag wasn't previously set; returns `false` otherwise.\n  def test_and_set : Bool\n    @value.swap(true, :sequentially_consistent) == false\n  end\n\n  # Atomically clears the flag.\n  def clear : Nil\n    @value.set(false, :sequentially_consistent)\n  end\nend\n"
  },
  {
    "path": "src/base64.cr",
    "content": "# The `Base64` module provides for the encoding (`encode`, `strict_encode`,\n# `urlsafe_encode`) and decoding (`decode`)\n# of binary data using a base64 representation.\n#\n# ### Example\n#\n# A simple encoding and decoding.\n#\n# ```\n# require \"base64\"\n#\n# enc = Base64.encode(\"Send reinforcements\") # => \"U2VuZCByZWluZm9yY2VtZW50cw==\\n\"\n# plain = Base64.decode_string(enc)          # => \"Send reinforcements\"\n# ```\n#\n# The purpose of using base64 to encode data is that it translates any binary\n# data into purely printable characters.\nmodule Base64\n  extend self\n\n  class Error < Exception; end\n\n  private CHARS_STD  = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n  private CHARS_SAFE = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\"\n  private LINE_SIZE  = 60\n  private PAD        = '='.ord.to_u8\n  private NL         = '\\n'.ord.to_u8\n  private NR         = '\\r'.ord.to_u8\n\n  # Returns the base64-encoded version of *data*.\n  # This method complies with [RFC 2045](https://tools.ietf.org/html/rfc2045).\n  # Line feeds are added to every 60 encoded characters.\n  #\n  # ```\n  # puts Base64.encode(\"Now is the time for all good coders\\nto learn Crystal\")\n  # ```\n  #\n  # Generates:\n  #\n  # ```text\n  # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\n  # Q3J5c3RhbA==\n  # ```\n  def encode(data) : String\n    slice = data.to_slice\n    String.new(encode_size(slice.size, new_lines: true)) do |buf|\n      appender = buf.appender\n      encode_with_new_lines(slice) { |byte| appender << byte }\n      size = appender.size\n      {size, size}\n    end\n  end\n\n  # Writes the base64-encoded version of *data* to *io*.\n  # This method complies with [RFC 2045](https://tools.ietf.org/html/rfc2045).\n  # Line feeds are added to every 60 encoded characters.\n  #\n  # ```\n  # Base64.encode(\"Now is the time for all good coders\\nto learn Crystal\", STDOUT)\n  # ```\n  def encode(data, io : IO)\n    count = 0\n    encode_with_new_lines(data.to_slice) do |byte|\n      io << byte.unsafe_chr\n      count += 1\n    end\n    io.flush\n    count\n  end\n\n  private def encode_with_new_lines(data, &)\n    inc = 0\n    to_base64(data.to_slice, CHARS_STD, pad: true) do |byte|\n      yield byte\n      inc += 1\n      if inc >= LINE_SIZE\n        yield NL\n        inc = 0\n      end\n    end\n    if inc > 0\n      yield NL\n    end\n  end\n\n  # Returns the base64-encoded version of *data* with no newlines.\n  # This method complies with [RFC 4648](https://tools.ietf.org/html/rfc4648).\n  #\n  # ```\n  # puts Base64.strict_encode(\"Now is the time for all good coders\\nto learn Crystal\")\n  # ```\n  #\n  # Generates:\n  #\n  # ```text\n  # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4gQ3J5c3RhbA==\n  # ```\n  def strict_encode(data) : String\n    strict_encode data, CHARS_STD, pad: true\n  end\n\n  private def strict_encode(data, alphabet, pad = false)\n    slice = data.to_slice\n    String.new(encode_size(slice.size)) do |buf|\n      appender = buf.appender\n      to_base64(slice, alphabet, pad: pad) { |byte| appender << byte }\n      size = appender.size\n      {size, size}\n    end\n  end\n\n  # Writes the base64-encoded version of *data* with no newlines to *io*.\n  # This method complies with [RFC 4648](https://tools.ietf.org/html/rfc4648).\n  #\n  # ```\n  # Base64.strict_encode(\"Now is the time for all good coders\\nto learn Crystal\", STDOUT)\n  # ```\n  def strict_encode(data, io : IO)\n    strict_encode_to_io_internal(data, io, CHARS_STD, pad: true)\n  end\n\n  private def strict_encode_to_io_internal(data, io, alphabet, pad)\n    count = 0\n    to_base64(data.to_slice, alphabet, pad: pad) do |byte|\n      count += 1\n      io << byte.unsafe_chr\n    end\n    io.flush\n    count\n  end\n\n  # Returns the base64-encoded version of *data* using a urlsafe alphabet.\n  # This method complies with \"Base 64 Encoding with URL and Filename Safe\n  # Alphabet\" in [RFC 4648](https://tools.ietf.org/html/rfc4648).\n  #\n  # The alphabet uses `'-'` instead of `'+'` and `'_'` instead of `'/'`.\n  #\n  # The *padding* parameter defaults to `true`. When `false`, enough `=` characters\n  # are not added to make the output divisible by 4.\n  def urlsafe_encode(data, padding = true) : String\n    slice = data.to_slice\n    String.new(encode_size(slice.size)) do |buf|\n      appender = buf.appender\n      to_base64(slice, CHARS_SAFE, pad: padding) { |byte| appender << byte }\n      size = appender.size\n      {size, size}\n    end\n  end\n\n  # Writes the base64-encoded version of *data* using a urlsafe alphabet to *io*.\n  # This method complies with \"Base 64 Encoding with URL and Filename Safe\n  # Alphabet\" in [RFC 4648](https://tools.ietf.org/html/rfc4648).\n  #\n  # The alphabet uses `'-'` instead of `'+'` and `'_'` instead of `'/'`.\n  def urlsafe_encode(data, io : IO)\n    strict_encode_to_io_internal(data, io, CHARS_SAFE, pad: true)\n  end\n\n  # Returns the base64-decoded version of *data* as a `Bytes`.\n  # This will decode either the normal or urlsafe alphabets.\n  def decode(data) : Bytes\n    slice = data.to_slice\n    buf = Pointer(UInt8).malloc(decode_size(slice.size))\n    appender = buf.appender\n    from_base64(slice) { |byte| appender << byte }\n    appender.to_slice\n  end\n\n  # Writes the base64-decoded version of *data* to *io*.\n  # This will decode either the normal or urlsafe alphabets.\n  def decode(data, io : IO)\n    count = 0\n    from_base64(data.to_slice) do |byte|\n      io.write_byte byte\n      count += 1\n    end\n    io.flush\n    count\n  end\n\n  # Returns the base64-decoded version of *data* as a string.\n  # This will decode either the normal or urlsafe alphabets.\n  def decode_string(data) : String\n    slice = data.to_slice\n    String.new(decode_size(slice.size)) do |buf|\n      appender = buf.appender\n      from_base64(slice) { |byte| appender << byte }\n      {appender.size, 0}\n    end\n  end\n\n  private def encode_size(str_size, new_lines = false)\n    size = (str_size * 4 / 3.0).to_i + 4\n    size += size // LINE_SIZE if new_lines\n    size\n  end\n\n  private def decode_size(str_size)\n    (str_size * 3 / 4.0).to_i + 4\n  end\n\n  private def to_base64(data, chars, pad = false, &)\n    bytes = chars.to_unsafe\n    size = data.size\n    cstr = data.to_unsafe\n    return if cstr.null? || size == 0\n    endcstr = cstr + size - size % 3 - 3\n\n    # process bunch of full triples\n    while cstr < endcstr\n      n = cstr.as(UInt32*).value.byte_swap\n      yield bytes[(n >> 26) & 63]\n      yield bytes[(n >> 20) & 63]\n      yield bytes[(n >> 14) & 63]\n      yield bytes[(n >> 8) & 63]\n      cstr += 3\n    end\n\n    # process last full triple manually, because reading UInt32 not correct for guarded memory\n    if size >= 3\n      n = (cstr.value.to_u32 << 16) | ((cstr + 1).value.to_u32 << 8) | (cstr + 2).value\n      yield bytes[(n >> 18) & 63]\n      yield bytes[(n >> 12) & 63]\n      yield bytes[(n >> 6) & 63]\n      yield bytes[(n) & 63]\n      cstr += 3\n    end\n\n    # process last partial triple\n    pd = size % 3\n    if pd == 1\n      n = (cstr.value.to_u32 << 16)\n      yield bytes[(n >> 18) & 63]\n      yield bytes[(n >> 12) & 63]\n      if pad\n        yield PAD\n        yield PAD\n      end\n    elsif pd == 2\n      n = (cstr.value.to_u32 << 16) | ((cstr + 1).value.to_u32 << 8)\n      yield bytes[(n >> 18) & 63]\n      yield bytes[(n >> 12) & 63]\n      yield bytes[(n >> 6) & 63]\n      yield PAD if pad\n    end\n  end\n\n  # Processes the given data and yields each byte.\n  private def from_base64(data : Bytes, &block : UInt8 -> Nil)\n    size = data.size\n    bytes = data.to_unsafe\n    bytes_begin = bytes\n\n    # Get the position of the last valid base64 character (rstrip '\\n', '\\r' and '=')\n    while (size > 0) && (sym = bytes[size - 1]) && sym.in?(NL, NR, PAD)\n      size -= 1\n    end\n\n    # Process combinations of four characters until there aren't any left\n    fin = bytes + size - 4\n    while true\n      break if bytes > fin\n\n      # Move the pointer by one byte until there is a valid base64 character\n      while bytes.value.in?(NL, NR)\n        bytes += 1\n      end\n      break if bytes > fin\n\n      yield_decoded_chunk_bytes(bytes[0], bytes[1], bytes[2], bytes[3], chunk_pos: bytes - bytes_begin)\n      bytes += 4\n    end\n\n    # Move the pointer by one byte until there is a valid base64 character or the end of `bytes` was reached\n    while (bytes < fin + 4) && bytes.value.in?(NL, NR)\n      bytes += 1\n    end\n\n    # If the amount of base64 characters is not divisible by 4, the remainder of the previous loop is handled here\n    unread_bytes = (fin - bytes) % 4\n    case unread_bytes\n    when 1\n      raise Base64::Error.new(\"Wrong size\")\n    when 2\n      yield_decoded_chunk_bytes(bytes[0], bytes[1], chunk_pos: bytes - bytes_begin)\n    when 3\n      yield_decoded_chunk_bytes(bytes[0], bytes[1], bytes[2], chunk_pos: bytes - bytes_begin)\n    end\n  end\n\n  # This macro decodes the given chunk of (2-4) base64 characters.\n  # The argument chunk_pos is only used for the resulting error message.\n  # The resulting bytes are then each yielded.\n  private macro yield_decoded_chunk_bytes(*bytes, chunk_pos)\n    %buffer = 0_u32\n    {% for byte, i in bytes %}\n      %decoded = DECODE_TABLE.unsafe_fetch({{byte}})\n      %buffer = (%buffer << 6) + %decoded\n      raise Base64::Error.new(\"Unexpected byte 0x#{{{byte}}.to_s(16)} at #{{{chunk_pos}} + {{i}}}\") if %decoded == 255_u8\n    {% end %}\n\n    # Each byte in the buffer is shifted to rightmost position of the buffer, then casted to a UInt8\n    {% for i in 2..(bytes.size) %}\n      yield (%buffer >> {{ (4 - bytes.size) * 2 + (8 * (bytes.size - i)) }}).to_u8!\n    {% end %}\n  end\n\n  private DECODE_TABLE = Array(UInt8).new(size: 256) do |i|\n    case i.unsafe_chr\n    when 'A'..'Z' then (i - 0x41).to_u8!\n    when 'a'..'z' then (i - 0x47).to_u8!\n    when '0'..'9' then (i + 0x04).to_u8!\n    when '+', '-' then 0x3E_u8\n    when '/', '_' then 0x3F_u8\n    else               255_u8\n    end\n  end\nend\n"
  },
  {
    "path": "src/benchmark/bm.cr",
    "content": "module Benchmark\n  module BM\n    # A data object, representing the times associated with a benchmark measurement.\n    class Tms\n      # User CPU time\n      getter utime : Float64\n\n      # System CPU time\n      getter stime : Float64\n\n      # User CPU time of children\n      getter cutime : Float64\n\n      # System CPU time of children\n      getter cstime : Float64\n\n      # Elapsed real time\n      getter real : Float64\n\n      # The label associated with this measure\n      getter label : String\n\n      # :nodoc:\n      def initialize(@utime, @stime, @cutime, @cstime, @real, @label)\n      end\n\n      # Total time, that is `utime` + `stime` + `cutime` + `cstime`\n      def total : Float64\n        utime + stime + cutime + cstime\n      end\n\n      # Prints `utime`, `stime`, `total` and `real` to *io*.\n      def to_s(io : IO) : Nil\n        io.printf \"  %.6f   %.6f   %.6f (  %.6f)\", utime, stime, total, real\n      end\n    end\n\n    # Yielded by `Benchmark#bm`, use `#report` to report benchmarks.\n    class Job\n      # :nodoc:\n      def initialize\n        @reports = [] of {String, ->}\n        @label_width = 0\n      end\n\n      # Reports a single benchmark unit.\n      def report(label : String = \" \", &block : ->) : Nil\n        @label_width = label.size if label.size > @label_width\n        @reports << {label, block}\n      end\n\n      # :nodoc:\n      def execute : Nil\n        if @label_width > 0\n          print \" \" * @label_width\n        end\n        puts \"       user     system      total        real\"\n\n        @reports.each do |report|\n          label, block = report\n          print label\n          diff = @label_width - label.size + 1\n          if diff > 0\n            print \" \" * diff\n          end\n          print Benchmark.measure(label, &block)\n          puts\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/benchmark/ips.cr",
    "content": "module Benchmark\n  # Benchmark IPS calculates the number of iterations per second for a given\n  # block of code. The strategy is to use two stages: a warmup stage and a\n  # calculation stage.\n  #\n  # The warmup phase defaults to 2 seconds. During this stage we figure out how\n  # many cycles are needed to run the block for roughly 100ms, and record it.\n  #\n  # The calculation defaults to 5 seconds. During this stage we run the block\n  # in sets of the size calculated in the warmup stage. The measurements for\n  # those sets are then used to calculate the mean and standard deviation,\n  # which are then reported. Additionally we compare the means to that of the\n  # fastest.\n  module IPS\n    class Job\n      # List of all entries in the benchmark.\n      # After `#execute`, these are populated with the resulting statistics.\n      property items : Array(Entry)\n\n      @warmup_time : Time::Span\n      @calculation_time : Time::Span\n\n      def initialize(calculation @calculation_time : Time::Span = 5.seconds, warmup @warmup_time : Time::Span = 2.seconds, interactive : Bool = STDOUT.tty?)\n        @interactive = !!interactive\n        @items = [] of Entry\n      end\n\n      @[Deprecated(\"Use `.new(Time::Span, Time::Span, Bool)` instead.\")]\n      def self.new(calculation = 5, warmup = 2, interactive = STDOUT.tty?)\n        new(calculation.seconds, warmup.seconds, !!interactive)\n      end\n\n      # Adds code to be benchmarked\n      def report(label : String = \"\", &action : ->) : Benchmark::IPS::Entry\n        item = Entry.new(label, action)\n        @items << item\n        item\n      end\n\n      def execute : Nil\n        run_warmup\n        run_calculation\n        run_comparison\n      end\n\n      def report : Nil\n        max_label = ran_items.max_of &.label.size\n        max_compare = ran_items.max_of &.human_compare.size\n        max_bytes_per_op = ran_items.max_of &.bytes_per_op.humanize(base: 1024).size\n\n        ran_items.each do |item|\n          printf \"%s %s (%s) (±%5.2f%%)  %sB/op  %s\\n\",\n            item.label.rjust(max_label),\n            item.human_mean,\n            item.human_iteration_time,\n            item.relative_stddev,\n            item.bytes_per_op.humanize(base: 1024).rjust(max_bytes_per_op),\n            item.human_compare.rjust(max_compare)\n        end\n      end\n\n      # The warmup stage gathers information about the items that is later used\n      # in the calculation stage\n      private def run_warmup\n        @items.each do |item|\n          GC.collect\n\n          count = 0_u64\n          elapsed = Time.measure do\n            target = Time.instant + @warmup_time\n\n            while Time.instant < target\n              item.call\n              count += 1\n            end\n          end\n\n          item.set_cycles(elapsed, count)\n        end\n      end\n\n      private def run_calculation\n        @items.each do |item|\n          GC.collect\n\n          measurements = [] of Time::Span\n          bytes = 0_i64\n          cycles = 0_i64\n\n          target = Time.instant + @calculation_time\n\n          loop do\n            elapsed = nil\n            bytes_taken = Benchmark.memory do\n              elapsed = Time.measure { item.call_for_100ms }\n            end\n            bytes += bytes_taken\n            cycles += item.cycles\n            measurements << elapsed.not_nil!\n            break if Time.instant >= target\n          end\n\n          ips = measurements.map { |m| item.cycles.to_f / m.total_seconds }\n          item.calculate_stats(ips)\n\n          item.bytes_per_op = (bytes.to_f / cycles.to_f).round.to_u64\n\n          if @interactive\n            run_comparison\n            report\n            print \"\\e[#{ran_items.size}A\"\n          end\n        end\n      end\n\n      private def ran_items\n        @items.select(&.ran?)\n      end\n\n      private def run_comparison\n        fastest = ran_items.max_by(&.mean)\n        ran_items.each do |item|\n          item.slower = (fastest.mean / item.mean).to_f\n        end\n      end\n    end\n\n    class Entry\n      # Label of the benchmark\n      property label : String\n\n      # Code to be benchmarked\n      property action : ->\n\n      # Number of cycles needed to run `action` for approximately 100ms.\n      # Calculated during the warmup stage\n      property! cycles : Int32\n\n      # Number of 100ms runs during the calculation stage\n      property! size : Int32\n\n      # Statistical mean from calculation stage\n      property! mean : Float64\n\n      # Statistical variance from calculation stage\n      property! variance : Float64\n\n      # Statistical standard deviation from calculation stage\n      property! stddev : Float64\n\n      # Relative standard deviation as a percentage\n      property! relative_stddev : Float64\n\n      # Multiple slower than the fastest entry\n      property! slower : Float64\n\n      # Number of bytes allocated per operation\n      property! bytes_per_op : UInt64\n\n      @ran : Bool\n      @ran = false\n\n      def initialize(@label : String, @action : ->)\n      end\n\n      def ran? : Bool\n        @ran\n      end\n\n      def call : Nil\n        action.call\n      end\n\n      def call_for_100ms : Nil\n        cycles.times { action.call }\n      end\n\n      def set_cycles(duration, iterations) : Nil\n        @cycles = (iterations / duration.total_milliseconds * 100).to_i\n        @cycles = 1 if cycles <= 0\n      end\n\n      def calculate_stats(samples) : Nil\n        @ran = true\n        @size = samples.size\n        @mean = samples.sum.to_f / size.to_f\n        @variance = (samples.reduce(0) { |acc, i| acc + ((i - mean) ** 2) }).to_f / size.to_f\n        @stddev = Math.sqrt(variance)\n        @relative_stddev = 100.0 * (stddev / mean)\n      end\n\n      def human_mean : String\n        mean.humanize(precision: 2, significant: false, prefixes: Number::SI_PREFIXES_PADDED).rjust(7)\n      end\n\n      def human_iteration_time : String\n        iteration_time = 1.0 / mean\n\n        iteration_time.humanize(precision: 2, significant: false) do |magnitude, _|\n          magnitude = Number.prefix_index(magnitude).clamp(-9..0)\n          {magnitude, magnitude == 0 ? \"s \" : \"#{Number.si_prefix(magnitude)}s\"}\n        end.rjust(8)\n      end\n\n      def human_compare : String\n        if slower == 1.0\n          \"fastest\"\n        else\n          sprintf \"%5.2f× slower\", slower\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/benchmark.cr",
    "content": "require \"./benchmark/**\"\n\n# The Benchmark module provides methods for benchmarking Crystal code, giving\n# detailed reports on the time and memory taken for each task.\n#\n# NOTE: To use `Benchmark`, you must explicitly import it with `require \"benchmark\"`\n#\n# ### Measure the number of iterations per second of each task\n#\n# ```\n# require \"benchmark\"\n#\n# Benchmark.ips do |x|\n#   x.report(\"short sleep\") { sleep 10.milliseconds }\n#   x.report(\"shorter sleep\") { sleep 1.millisecond }\n# end\n# ```\n#\n# This generates the following output showing the mean iterations per second,\n# the mean times per iteration, the standard deviation relative to the mean, and a comparison:\n#\n# ```text\n#   short sleep   88.7  ( 11.27ms) (± 3.33%)  8.90× slower\n# shorter sleep  789.7  (  1.27ms) (± 3.02%)       fastest\n# ```\n#\n# `Benchmark::IPS` defaults to 2 seconds of warmup time and 5 seconds of\n# calculation time. This can be configured:\n#\n# ```\n# require \"benchmark\"\n#\n# Benchmark.ips(warmup: 4, calculation: 10) do |x|\n#   x.report(\"sleep\") { sleep 10.milliseconds }\n# end\n# ```\n#\n# ### Measure the time to construct the string given by the expression: `\"a\"*1_000_000_000`\n#\n# ```\n# require \"benchmark\"\n#\n# puts Benchmark.measure { \"a\"*1_000_000_000 }\n# ```\n#\n# This generates the following output:\n#\n# ```text\n#  0.190000   0.220000   0.410000 (  0.420185)\n# ```\n#\n# This report shows the user CPU time, system CPU time, the sum of\n# the user and system CPU times, and the elapsed real time. The unit\n# of time is seconds.\n#\n# ### Do some experiments sequentially using the `#bm` method:\n#\n# ```\n# require \"benchmark\"\n#\n# n = 5000000\n# Benchmark.bm do |x|\n#   x.report(\"times:\") do\n#     n.times do\n#       a = \"1\"\n#     end\n#   end\n#   x.report(\"upto:\") do\n#     1.upto(n) do\n#       a = \"1\"\n#     end\n#   end\n# end\n# ```\n#\n# The result:\n#\n# ```text\n#            user     system      total        real\n# times:   0.010000   0.000000   0.010000 (  0.008976)\n# upto:    0.010000   0.000000   0.010000 (  0.010466)\n# ```\n#\n# NOTE: Make sure to always benchmark code by compiling with the `--release` flag.\nmodule Benchmark\n  extend self\n\n  # Main interface of the `Benchmark` module. Yields a `Job` to which\n  # one can report the benchmarks. See the module's description.\n  def bm(&)\n    {% if !flag?(:release) %}\n      puts \"Warning: benchmarking without the `--release` flag won't yield useful results\"\n    {% end %}\n\n    report = BM::Job.new\n    yield report\n    report.execute\n    report\n  end\n\n  # Instruction per second interface of the `Benchmark` module. Yields a `Job`\n  # to which one can report the benchmarks. See the module's description.\n  #\n  # The optional parameters *calculation* and *warmup* set the duration of\n  # those stages. For more detail on these stages see\n  # `Benchmark::IPS`. When the *interactive* parameter is `true`, results are\n  # displayed and updated as they are calculated, otherwise all at once after they finished.\n  def ips(calculation : Time::Span = 5.seconds, warmup : Time::Span = 2.seconds, interactive : Bool = STDOUT.tty?, &)\n    {% if !flag?(:release) %}\n      puts \"Warning: benchmarking without the `--release` flag won't yield useful results\"\n    {% end %}\n\n    job = IPS::Job.new(calculation, warmup, interactive)\n    yield job\n    job.execute\n    job.report\n    job\n  end\n\n  # Instruction per second interface of the `Benchmark` module. Yields a `Job`\n  # to which one can report the benchmarks. See the module's description.\n  #\n  # The optional parameters *calculation* and *warmup* set the duration of\n  # those stages in seconds. For more detail on these stages see\n  # `Benchmark::IPS`. When the *interactive* parameter is `true`, results are\n  # displayed and updated as they are calculated, otherwise all at once after they finished.\n  @[Deprecated(\"Use `#ips(Time::Span, Time::Span, Bool, &)` instead.\")]\n  def ips(calculation = 5, warmup = 2, interactive = STDOUT.tty?, &)\n    ips(calculation.seconds, warmup.seconds, !!interactive) { |job| yield job }\n  end\n\n  # Returns the time used to execute the given block.\n  def measure(label = \"\", &) : BM::Tms\n    t0, r0 = Process.times, Time.instant\n    yield\n    t1, r1 = Process.times, Time.instant\n    BM::Tms.new(t1.utime - t0.utime,\n      t1.stime - t0.stime,\n      t1.cutime - t0.cutime,\n      t1.cstime - t0.cstime,\n      (r1 - r0).total_seconds,\n      label)\n  end\n\n  # Returns the elapsed real time used to execute the given block.\n  #\n  # ```\n  # Benchmark.realtime { \"a\" * 100_000 } # => 00:00:00.0005840\n  # ```\n  def realtime(&) : Time::Span\n    Time.measure { yield }\n  end\n\n  # Returns the memory in bytes that the given block consumes.\n  #\n  # ```\n  # Benchmark.memory { Array(Int32).new } # => 32\n  # ```\n  def memory(&)\n    bytes_before_measure = GC.stats.total_bytes\n    yield\n    (GC.stats.total_bytes - bytes_before_measure).to_i64\n  end\nend\n"
  },
  {
    "path": "src/big/big_decimal.cr",
    "content": "require \"big\"\n\nclass InvalidBigDecimalException < Exception\n  def initialize(big_decimal_str : String, reason : String)\n    super(\"Invalid BigDecimal: #{big_decimal_str} (#{reason})\")\n  end\nend\n\n# A `BigDecimal` can represent arbitrarily large precision decimals.\n#\n# It is internally represented by a pair of `BigInt` and `UInt64`: value and scale.\n# Value contains the actual value, and scale tells the decimal point place.\n# E.g. when value is `1234` and scale `2`, the result is `12.34`.\n#\n# NOTE: To use `BigDecimal`, you must explicitly import it with `require \"big\"`\n#\n# The general idea and some of the arithmetic algorithms were adapted from\n# the MIT/APACHE-licensed [bigdecimal-rs](https://github.com/akubera/bigdecimal-rs).\nstruct BigDecimal < Number\n  private TWO_I  = BigInt.new(2)\n  private FIVE_I = BigInt.new(5)\n  private TEN_I  = BigInt.new(10)\n\n  DEFAULT_PRECISION = 100_u64\n\n  @[Deprecated(\"Use `DEFAULT_PRECISION` instead\")]\n  DEFAULT_MAX_DIV_ITERATIONS = 100_u64\n\n  include Comparable(Int)\n  include Comparable(Float)\n  include Comparable(BigRational)\n  include Comparable(BigDecimal)\n\n  getter value : BigInt\n  getter scale : UInt64\n\n  # Creates a new `BigDecimal` from `Float`.\n  #\n  # NOTE: Floats are fundamentally less precise than BigDecimals,\n  # which makes initialization from them risky.\n  def self.new(num : Float) : self\n    raise ArgumentError.new \"Can only construct from a finite number\" unless num.finite?\n    new(num.to_s)\n  end\n\n  # Creates a new `BigDecimal` from `BigRational`.\n  #\n  # NOTE: BigRational are fundamentally more precise than BigDecimals,\n  # which makes initialization from them risky.\n  def self.new(num : BigRational) : self\n    num.numerator.to_big_d / num.denominator.to_big_d\n  end\n\n  # Returns *num*. Useful for generic code that does `T.new(...)` with `T`\n  # being a `Number`.\n  def self.new(num : BigDecimal) : self\n    num\n  end\n\n  # Creates a new `BigDecimal` from `BigInt` *value* and `UInt64` *scale*,\n  # which matches the internal representation.\n  def initialize(@value : BigInt, @scale : UInt64)\n  end\n\n  # Creates a new `BigDecimal` from `Int`.\n  def initialize(num : Int = 0, scale : Int = 0)\n    initialize(num.to_big_i, scale.to_u64)\n  end\n\n  # Creates a new `BigDecimal` from a `String`.\n  #\n  # Allows only valid number strings with an optional negative sign.\n  def initialize(str : String)\n    # Strip leading '+' char to smooth out cases with strings like \"+123\"\n    str = str.lchop('+')\n    # Strip '_' to make it compatible with int literals like \"1_000_000\"\n    str = str.delete('_')\n\n    raise InvalidBigDecimalException.new(str, \"Zero size\") if str.bytesize == 0\n\n    # Check str's validity and find index of '.'\n    decimal_index = nil\n    # Check str's validity and find index of 'e'\n    exponent_index = nil\n\n    input_length = str.bytesize\n\n    str.each_char_with_index do |char, index|\n      final_character = index == input_length - 1\n      first_character = index == 0\n      case char\n      when '-'\n        unless (first_character && !final_character) || (exponent_index == index - 1 && !final_character)\n          raise InvalidBigDecimalException.new(str, \"Unexpected '-' character\")\n        end\n      when '+'\n        if final_character || exponent_index != index - 1\n          raise InvalidBigDecimalException.new(str, \"Unexpected '+' character\")\n        end\n      when '.'\n        if decimal_index || exponent_index\n          raise InvalidBigDecimalException.new(str, \"Unexpected '.' character\")\n        end\n        decimal_index = index\n      when 'e', 'E'\n        if first_character || final_character || exponent_index || decimal_index == index - 1\n          raise InvalidBigDecimalException.new(str, \"Unexpected #{char.inspect} character\")\n        end\n        exponent_index = index\n      when '0'..'9'\n        # Pass\n      else\n        raise InvalidBigDecimalException.new(str, \"Unexpected #{char.inspect} character\")\n      end\n    end\n\n    decimal_end_index = (exponent_index || input_length) - 1\n    if decimal_index\n      decimal_count = (decimal_end_index - decimal_index).to_u64\n\n      value_str = String.build do |builder|\n        # We know this is ASCII, so we can slice by index\n        builder.write(str.to_slice[0, decimal_index])\n        builder.write(str.to_slice[decimal_index + 1, decimal_count])\n      end\n      @value = value_str.to_big_i\n    else\n      decimal_count = 0_u64\n      @value = str[0..decimal_end_index].to_big_i\n    end\n\n    if exponent_index\n      exponent_postfix = str[exponent_index + 1]\n      case exponent_postfix\n      when '+', '-'\n        exponent_positive = exponent_postfix == '+'\n        exponent = str[(exponent_index + 2)..-1].to_u64\n      else\n        exponent_positive = true\n        exponent = str[(exponent_index + 1)..-1].to_u64\n      end\n\n      @scale = exponent\n      if exponent_positive\n        if @scale < decimal_count\n          @scale = decimal_count - @scale\n        else\n          @scale -= decimal_count\n          @value *= 10.to_big_i ** @scale\n          @scale = 0_u64\n        end\n      else\n        @scale += decimal_count\n      end\n    else\n      @scale = decimal_count\n    end\n  end\n\n  def - : BigDecimal\n    BigDecimal.new(-@value, @scale)\n  end\n\n  def +(other : BigDecimal) : BigDecimal\n    if @scale > other.scale\n      scaled = other.scale_to(self)\n      BigDecimal.new(@value + scaled.value, @scale)\n    elsif @scale < other.scale\n      scaled = scale_to(other)\n      BigDecimal.new(scaled.value + other.value, other.scale)\n    else\n      BigDecimal.new(@value + other.value, @scale)\n    end\n  end\n\n  def +(other : Number) : BigDecimal\n    self + BigDecimal.new(other)\n  end\n\n  def -(other : BigDecimal) : BigDecimal\n    if @scale > other.scale\n      scaled = other.scale_to(self)\n      BigDecimal.new(@value - scaled.value, @scale)\n    elsif @scale < other.scale\n      scaled = scale_to(other)\n      BigDecimal.new(scaled.value - other.value, other.scale)\n    else\n      BigDecimal.new(@value - other.value, @scale)\n    end\n  end\n\n  def -(other : Number) : BigDecimal\n    self - BigDecimal.new(other)\n  end\n\n  def *(other : BigDecimal) : BigDecimal\n    BigDecimal.new(@value * other.value, @scale + other.scale)\n  end\n\n  def *(other : Number) : BigDecimal\n    self * BigDecimal.new(other)\n  end\n\n  def %(other : BigDecimal) : BigDecimal\n    if @scale > other.scale\n      scaled = other.scale_to(self)\n      BigDecimal.new(@value % scaled.value, @scale)\n    elsif @scale < other.scale\n      scaled = scale_to(other)\n      BigDecimal.new(scaled.value % other.value, other.scale)\n    else\n      BigDecimal.new(@value % other.value, @scale)\n    end\n  end\n\n  def %(other : Int)\n    self % BigDecimal.new(other)\n  end\n\n  def /(other : BigDecimal) : BigDecimal\n    div other\n  end\n\n  Number.expand_div [BigInt, BigFloat], BigDecimal\n  Number.expand_div [BigRational], BigRational\n\n  # Divides `self` with another `BigDecimal`, with an optionally configurable\n  # *precision*.\n  #\n  # When the division is inexact, the returned value rounds towards negative\n  # infinity, and its scale is never greater than\n  # `scale - other.scale + precision`.\n  #\n  # ```\n  # BigDecimal.new(1).div(BigDecimal.new(2))    # => BigDecimal(@value=5, @scale=2)\n  # BigDecimal.new(1).div(BigDecimal.new(3), 5) # => BigDecimal(@value=33333, @scale=5)\n  # ```\n  def div(other : BigDecimal, precision : Int = DEFAULT_PRECISION) : BigDecimal\n    check_division_by_zero other\n    return self if @value.zero?\n    other.factor_powers_of_ten\n\n    # ```\n    #    (a / 10 ** b) / (c / 10 ** d)\n    # == (a / c) / 10 ** (b - d)\n    # == (a * 10 ** scale_add // c) / 10 ** (b - d + scale_add)\n    # ```\n    #\n    # We want to find the minimum `scale_add` such that:\n    #\n    # - `b - d + scale_add >= 0`\n    # - `a * 10 ** scale_add % c == 0`\n    #\n    # If this is not possible, we let the returned number's scale be\n    # `{b - d, 0}.max + precision`.\n\n    numerator, denominator = @value, other.@value\n    scale = if @scale >= other.scale\n              @scale - other.scale\n            else\n              numerator *= power_ten_to(other.scale - @scale)\n              0\n            end\n\n    # Attempt division first; if `a % c == 0`, we're done.\n    quotient, remainder = numerator.divmod(denominator)\n    if remainder.zero?\n      return BigDecimal.new(normalize_quotient(other, quotient), scale)\n    end\n\n    # `c == denominator_reduced * 2 ** denominator_exp2 * 5 ** denominator_exp5`\n    denominator_reduced, denominator_exp2 = denominator.factor_by(TWO_I)\n\n    # Heuristic: for low powers of 5 we perform the divisions ourselves, since\n    # `BigInt#factor_by` can be slower\n    case denominator_reduced\n    when 1\n      denominator_exp5 = 0_u64\n    when 5\n      denominator_reduced = denominator_reduced // FIVE_I\n      denominator_exp5 = 1_u64\n    when 25\n      denominator_reduced = denominator_reduced // FIVE_I // FIVE_I\n      denominator_exp5 = 2_u64\n    else\n      denominator_reduced, denominator_exp5 = denominator_reduced.factor_by(FIVE_I)\n    end\n\n    if denominator_reduced != 1\n      # If `c` has any prime factor other than 2 or 5, then division will always\n      # be inexact; use *precision*.\n      scale_add = precision.to_u64\n    elsif denominator_exp2 <= 1 && denominator_exp5 <= 1\n      # Heuristic: if `denominator` is one of 2, 5, or 10, then `scale_add` must\n      # be 1 because `remainder` can never be divisible by 10. Thus we could\n      # skip the `factor_by` and `power_ten_to` calls here.\n      quotient = numerator * TEN_I // denominator\n      return BigDecimal.new(normalize_quotient(other, quotient), scale + 1)\n    else\n      # `a = ... * 10 ** numerator_exp10`\n      # For `a * 10 ** scale_add` to be divisible by `c`, it must be the case\n      # `numerator_exp10 + scale_add` is greater than `denominator_exp2` and\n      # `denominator_exp5`\n      _, numerator_exp10 = remainder.factor_by(TEN_I)\n      scale_add = {denominator_exp2, denominator_exp5}.max - numerator_exp10\n      scale_add = precision.to_u64 if scale_add > precision\n    end\n\n    quotient = numerator * power_ten_to(scale_add) // denominator\n    BigDecimal.new(normalize_quotient(other, quotient), scale + scale_add)\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#div(other : BigDecimal, precision = DEFAULT_PRECISION)` instead\")]\n  def div(other : BigDecimal, *, max_div_iterations = DEFAULT_MAX_DIV_ITERATIONS) : BigDecimal\n    div(other, max_div_iterations)\n  end\n\n  def <=>(other : BigDecimal) : Int32\n    if @scale > other.scale\n      @value <=> other.scale_to(self).value\n    elsif @scale < other.scale\n      scale_to(other).value <=> other.value\n    else\n      @value <=> other.value\n    end\n  end\n\n  def <=>(other : BigRational) : Int32\n    if @scale == 0\n      @value <=> other\n    else\n      # `@value / power_ten_to(@scale) <=> other.numerator / other.denominator`\n      @value * other.denominator <=> power_ten_to(@scale) * other.numerator\n    end\n  end\n\n  def <=>(other : Float::Primitive) : Int32?\n    return nil if other.nan?\n\n    if sign = other.infinite?\n      return -sign\n    end\n\n    self <=> other.to_big_r\n  end\n\n  def <=>(other : BigFloat) : Int32\n    self <=> other.to_big_r\n  end\n\n  def <=>(other : Int) : Int32\n    self <=> BigDecimal.new(other)\n  end\n\n  def ==(other : BigDecimal) : Bool\n    case @scale\n    when .>(other.scale)\n      scaled = other.value * power_ten_to(@scale - other.scale)\n      @value == scaled\n    when .<(other.scale)\n      scaled = @value * power_ten_to(other.scale - @scale)\n      scaled == other.value\n    else\n      @value == other.value\n    end\n  end\n\n  def zero? : Bool\n    @value.zero?\n  end\n\n  # Scales a `BigDecimal` to another `BigDecimal`, so they can be\n  # computed easier.\n  def scale_to(new_scale : BigDecimal) : BigDecimal\n    in_scale(new_scale.scale)\n  end\n\n  # :nodoc:\n  private def in_scale(new_scale : UInt64) : BigDecimal\n    if @value == 0\n      BigDecimal.new(0.to_big_i, new_scale)\n    elsif @scale > new_scale\n      scale_diff = @scale - new_scale.to_big_i\n      BigDecimal.new(@value // power_ten_to(scale_diff), new_scale)\n    elsif @scale < new_scale\n      scale_diff = new_scale - @scale.to_big_i\n      BigDecimal.new(@value * power_ten_to(scale_diff), new_scale)\n    else\n      self\n    end\n  end\n\n  # Raises the decimal to the *other*th power\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigDecimal.new(1234, 2) ** 2 # => 152.2756\n  # ```\n  def **(other : Int) : BigDecimal\n    return (to_big_r ** other).to_big_d if other < 0\n    BigDecimal.new(@value ** other, @scale * other)\n  end\n\n  # Rounds towards positive infinity.\n  def ceil : BigDecimal\n    round_impl { |rem| rem > 0 }\n  end\n\n  # Rounds towards negative infinity.\n  def floor : BigDecimal\n    round_impl { |rem| rem < 0 }\n  end\n\n  # Rounds towards zero.\n  def trunc : BigDecimal\n    round_impl { false }\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds towards the even neighbor (Banker's rounding).\n  def round_even : BigDecimal\n    round_impl do |rem, rem_range, mantissa|\n      case rem.abs <=> rem_range // 2\n      when .<(0)\n        false\n      when .>(0)\n        true\n      else\n        # `to_i!` is safe as GMP explicitly states the \"least significant part\"\n        # is returned and that always preserves `mantissa`'s parity modulo 2\n        mantissa.to_i!.odd?\n      end\n    end\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds away from zero.\n  def round_away : BigDecimal\n    round_impl { |rem, rem_range| rem.abs >= rem_range // 2 }\n  end\n\n  private def round_impl(&)\n    return self if @scale <= 0 || zero?\n\n    # `self == @value / 10 ** @scale == mantissa + (rem / 10 ** @scale)`\n    #\n    # Where:\n    # - `mantissa` and `rem` are both integers\n    # - `rem.abs < 10 ** @scale`\n    # - if `self` is negative, so are `mantissa` and `rem`\n    multiplier = power_ten_to(@scale)\n    mantissa, rem = @value.unsafe_truncated_divmod(multiplier)\n\n    round_away = yield rem, multiplier, mantissa\n    mantissa += self.sign if round_away\n\n    BigDecimal.new(mantissa, 0)\n  end\n\n  # :inherit:\n  def integer? : Bool\n    factor_powers_of_ten\n    scale == 0\n  end\n\n  def round(digits : Number, base = 10, *, mode : RoundingMode = :ties_even) : BigDecimal\n    return self if zero?\n\n    if base == 10\n      return self if @scale <= digits\n\n      # optimized version that skips `#div` completely, always exact\n      shifted = mul_power_of_ten(digits)\n      rounded = shifted.round(mode)\n      rounded.mul_power_of_ten(-digits)\n    else\n      # the following is same as the overload in `Number` except `base.to_f`\n      # becomes `base.to_big_d`; note that the `#/` calls always use\n      # `DEFAULT_PRECISION`\n      if digits < 0\n        multiplier = base.to_big_d ** digits.abs\n        shifted = self / multiplier\n      else\n        multiplier = base.to_big_d ** digits\n        shifted = self * multiplier\n      end\n\n      rounded = shifted.round(mode)\n\n      if digits < 0\n        result = rounded * multiplier\n      else\n        result = rounded / multiplier\n      end\n\n      BigDecimal.new result\n    end\n  end\n\n  def to_s(io : IO) : Nil\n    to_s_impl(io, point_range: -3..15)\n  end\n\n  protected def to_s_impl(*, point_range : Range) : String\n    String.build { |io| to_s_impl(io, point_range: point_range) }\n  end\n\n  protected def to_s_impl(io : IO, *, point_range : Range) : Nil\n    factor_powers_of_ten\n\n    cstr = LibGMP.get_str(nil, 10, @value)\n    buffer = Slice.new(cstr, LibC.strlen(cstr))\n\n    # add negative sign\n    if buffer[0]? == 45 # '-'\n      io << '-'\n      buffer = buffer[1..]\n    end\n\n    Float::Printer.decimal(io, buffer, -@scale.to_i, point_range, :remove_extra_zeros)\n  end\n\n  # :inherit:\n  def format(io : IO, separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : Nil\n    number = self\n    if decimal_places\n      number = number.round(decimal_places)\n    end\n\n    if decimal_places && decimal_places >= 0\n      string = number.abs.to_s_impl(point_range: ..)\n      integer, _, decimals = string.partition('.')\n    else\n      string = number.to_s_impl(point_range: ..)\n      _, _, decimals = string.partition(\".\")\n      integer = number.trunc.to_big_i.abs.to_s\n    end\n\n    is_negative = number < 0\n\n    format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant)\n  end\n\n  # Converts to `BigInt`. Truncates anything on the right side of the decimal point.\n  def to_big_i : BigInt\n    trunc.value\n  end\n\n  # Converts to `BigFloat`.\n  def to_big_f\n    BigFloat.new(to_s)\n  end\n\n  def to_big_d\n    self\n  end\n\n  def to_big_r : BigRational\n    BigRational.new(@value, power_ten_to(@scale))\n  end\n\n  # Converts to `Int64`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_i64 : Int64\n    to_big_i.to_i64\n  end\n\n  # Converts to `Int32`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_i32 : Int32\n    to_big_i.to_i32\n  end\n\n  # Converts to `Int16`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_i16 : Int16\n    to_big_i.to_i16\n  end\n\n  # Converts to `Int8`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_i8 : Int8\n    to_big_i.to_i8\n  end\n\n  # Converts to `Int32`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_i : Int32\n    to_i32\n  end\n\n  # Converts to `Int8`. Truncates anything on the right side of the decimal point.\n  # In case of overflow a wrapping is performed.\n  def to_i8!\n    to_big_i.to_i8!\n  end\n\n  # Converts to `Int16`. Truncates anything on the right side of the decimal point.\n  # In case of overflow a wrapping is performed.\n  def to_i16!\n    to_big_i.to_i16!\n  end\n\n  # Converts to `Int32`. Truncates anything on the right side of the decimal point.\n  # In case of overflow a wrapping is performed.\n  def to_i32! : Int32\n    to_big_i.to_i32!\n  end\n\n  # Converts to `Int64`. Truncates anything on the right side of the decimal point.\n  # In case of overflow a wrapping is performed.\n  def to_i64!\n    to_big_i.to_i64!\n  end\n\n  # Converts to `Int32`. Truncates anything on the right side of the decimal point.\n  # In case of overflow a wrapping is performed.\n  def to_i! : Int32\n    to_i32!\n  end\n\n  private def to_big_u\n    raise OverflowError.new if self < 0\n    to_big_u!\n  end\n\n  private def to_big_u!\n    (@value.abs // power_ten_to(@scale))\n  end\n\n  # Converts to `UInt64`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_u64 : UInt64\n    to_big_u.to_u64\n  end\n\n  # Converts to `UInt32`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_u32 : UInt32\n    to_big_u.to_u32\n  end\n\n  # Converts to `UInt16`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_u16 : UInt16\n    to_big_u.to_u16\n  end\n\n  # Converts to `UInt8`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_u8 : UInt8\n    to_big_u.to_u8\n  end\n\n  # Converts to `UInt32`. Truncates anything on the right side of the decimal point.\n  # Raises `OverflowError` in case of overflow.\n  def to_u : UInt32\n    to_u32\n  end\n\n  # Converts to `UInt8`. Truncates anything on the right side of the decimal point,\n  # converting negative to positive.\n  # In case of overflow a wrapping is performed.\n  def to_u8!\n    to_big_u!.to_u8!\n  end\n\n  # Converts to `UInt16`. Truncates anything on the right side of the decimal point,\n  # converting negative to positive.\n  # In case of overflow a wrapping is performed.\n  def to_u16!\n    to_big_u!.to_u16!\n  end\n\n  # Converts to `UInt32`. Truncates anything on the right side of the decimal point,\n  # converting negative to positive.\n  # In case of overflow a wrapping is performed.\n  def to_u32! : UInt32\n    to_big_u!.to_u32!\n  end\n\n  # Converts to `UInt64`. Truncates anything on the right side of the decimal point,\n  # converting negative to positive.\n  # In case of overflow a wrapping is performed.\n  def to_u64!\n    to_big_u!.to_u64!\n  end\n\n  # Converts to `UInt32`. Truncates anything on the right side of the decimal point,\n  # converting negative to positive.\n  # In case of overflow a wrapping is performed.\n  def to_u! : UInt32\n    to_u32!\n  end\n\n  # Converts to `Float64`.\n  # Raises `OverflowError` in case of overflow.\n  def to_f64 : Float64\n    to_s.to_f64\n  end\n\n  # Converts to `Float32`.\n  # Raises `OverflowError` in case of overflow.\n  def to_f32 : Float32\n    to_f64.to_f32\n  end\n\n  # Converts to `Float64`.\n  # Raises `OverflowError` in case of overflow.\n  def to_f : Float64\n    to_f64\n  end\n\n  # Converts to `Float32`.\n  # In case of overflow a wrapping is performed.\n  def to_f32!\n    to_f64.to_f32!\n  end\n\n  # Converts to `Float64`.\n  # In case of overflow a wrapping is performed.\n  def to_f64! : Float64\n    to_f64\n  end\n\n  # Converts to `Float64`.\n  # In case of overflow a wrapping is performed.\n  def to_f! : Float64\n    to_f64!\n  end\n\n  def clone\n    self\n  end\n\n  # Returns the *quotient* as absolutely negative if `self` and *other* have\n  # different signs, otherwise returns the *quotient*.\n  def normalize_quotient(other : BigDecimal, quotient : BigInt) : BigInt\n    if (@value < 0 && other.value > 0) || (other.value < 0 && @value > 0)\n      -quotient.abs\n    else\n      quotient\n    end\n  end\n\n  private def check_division_by_zero(bd : BigDecimal)\n    raise DivisionByZeroError.new if bd.value == 0\n  end\n\n  private def power_ten_to(x : Int) : Int\n    TEN_I ** x\n  end\n\n  # returns `self * 10 ** exponent`\n  protected def mul_power_of_ten(exponent : Int) : BigDecimal\n    if exponent <= scale\n      BigDecimal.new(@value, @scale - exponent)\n    else\n      BigDecimal.new(@value * power_ten_to(exponent - scale), 0_u64)\n    end\n  end\n\n  # Factors out any extra powers of ten in the internal representation.\n  # For instance, value=100 scale=2 => value=1 scale=0\n  protected def factor_powers_of_ten : Nil\n    if @scale > 0\n      reduced, exp = value.factor_by(TEN_I)\n      if exp <= @scale\n        @value = reduced\n        @scale -= exp\n      else\n        @value //= power_ten_to(@scale)\n        @scale = 0\n      end\n    end\n  end\nend\n\nstruct Int\n  include Comparable(BigDecimal)\n\n  # Converts `self` to `BigDecimal`.\n  # ```\n  # require \"big\"\n  # 123456789012345678.to_big_d\n  # ```\n  def to_big_d : BigDecimal\n    BigDecimal.new(self)\n  end\n\n  def <=>(other : BigDecimal)\n    to_big_d <=> other\n  end\n\n  def +(other : BigDecimal) : BigDecimal\n    other + self\n  end\n\n  def -(other : BigDecimal) : BigDecimal\n    to_big_d - other\n  end\n\n  def *(other : BigDecimal) : BigDecimal\n    other * self\n  end\nend\n\nstruct Float\n  include Comparable(BigDecimal)\n\n  def <=>(other : BigDecimal)\n    cmp = other <=> self\n    -cmp if cmp\n  end\n\n  # Converts `self` to `BigDecimal`.\n  #\n  # NOTE: Floats are fundamentally less precise than BigDecimals,\n  # which makes conversion to them risky.\n  # ```\n  # require \"big\"\n  # 1212341515125412412412421.0.to_big_d\n  # ```\n  def to_big_d : BigDecimal\n    BigDecimal.new(self)\n  end\nend\n\nstruct BigFloat\n  def <=>(other : BigDecimal)\n    -(other <=> self)\n  end\nend\n\nstruct BigRational\n  include Comparable(BigDecimal)\n\n  def <=>(other : BigDecimal)\n    -(other <=> self)\n  end\n\n  # Converts `self` to `BigDecimal`.\n  def to_big_d : BigDecimal\n    BigDecimal.new(self)\n  end\nend\n\nclass String\n  # Converts `self` to `BigDecimal`.\n  # ```\n  # require \"big\"\n  # \"1212341515125412412412421\".to_big_d\n  # ```\n  def to_big_d : BigDecimal\n    BigDecimal.new(self)\n  end\nend\n\n# :nodoc:\nstruct Crystal::Hasher\n  def self.reduce_num(value : BigDecimal) : UInt64\n    v = reduce_num(value.value.abs)\n\n    # v = UInt64.mulmod(v, 10_u64.powmod(-scale, HASH_MODULUS), HASH_MODULUS)\n    # TODO: consider #7516 or similar\n    scale = value.scale\n    x = 0x1ccc_cccc_cccc_cccc_u64 # 10^-1 (mod HASH_MODULUS)\n    while scale > 0\n      v = UInt64.mulmod(v, x, HASH_MODULUS) if scale.bits_set?(1)\n      scale = scale.unsafe_shr(1)\n      x = UInt64.mulmod(x, x, HASH_MODULUS)\n    end\n\n    v &* value.sign\n  end\nend\n\n# :nodoc:\nstruct String::Formatter(A)\n  def int(flags, arg : BigDecimal) : Nil\n    int(flags, arg.to_big_i)\n  end\nend\n"
  },
  {
    "path": "src/big/big_float.cr",
    "content": "require \"c/string\"\nrequire \"big\"\n\n# A `BigFloat` can represent arbitrarily large floats.\n#\n# It is implemented under the hood with [GMP](https://gmplib.org/).\n#\n# NOTE: To use `BigFloat`, you must explicitly import it with `require \"big\"`\nstruct BigFloat < Float\n  include Comparable(Int)\n  include Comparable(BigFloat)\n  include Comparable(Float)\n\n  def initialize\n    LibGMP.mpf_init(out @mpf)\n  end\n\n  def initialize(str : String)\n    # Strip leading '+' char to smooth out cases with strings like \"+123\"\n    str = str.lchop('+')\n    # Strip '_' to make it compatible with int literals like \"1_000_000\"\n    str = str.delete('_')\n    if LibGMP.mpf_init_set_str(out @mpf, str, 10) == -1\n      raise ArgumentError.new(\"Invalid BigFloat: #{str.inspect}\")\n    end\n  end\n\n  def initialize(num : BigInt)\n    LibGMP.mpf_init(out @mpf)\n    LibGMP.mpf_set_z(self, num)\n  end\n\n  def initialize(num : BigRational)\n    LibGMP.mpf_init(out @mpf)\n    LibGMP.mpf_set_q(self, num)\n  end\n\n  def initialize(num : BigFloat)\n    LibGMP.mpf_init(out @mpf)\n    LibGMP.mpf_set(self, num)\n  end\n\n  def initialize(num : Int)\n    Int.primitive_si_ui_check(num) do |si, ui, big_i|\n      {\n        si:    LibGMP.mpf_init_set_si(out @mpf, {{ si }}),\n        ui:    LibGMP.mpf_init_set_ui(out @mpf, {{ ui }}),\n        big_i: begin\n          LibGMP.mpf_init(out @mpf)\n          LibGMP.mpf_set_z(self, {{ big_i }})\n        end,\n      }\n    end\n  end\n\n  def initialize(num : Float::Primitive)\n    raise ArgumentError.new \"Can only construct from a finite number\" unless num.finite?\n    LibGMP.mpf_init_set_d(out @mpf, num)\n  end\n\n  def initialize(num : Number)\n    LibGMP.mpf_init_set_d(out @mpf, num.to_f64)\n  end\n\n  def initialize(num : Float, precision : Int)\n    raise ArgumentError.new \"Can only construct from a finite number\" unless num.finite?\n    LibGMP.mpf_init2(out @mpf, precision.to_u64)\n    LibGMP.mpf_set_d(self, num.to_f64)\n  end\n\n  def initialize(@mpf : LibGMP::MPF)\n  end\n\n  def self.new(&)\n    LibGMP.mpf_init(out mpf)\n    yield pointerof(mpf)\n    new(mpf)\n  end\n\n  def self.default_precision\n    LibGMP.mpf_get_default_prec\n  end\n\n  def self.default_precision=(prec : Int) : Nil\n    LibGMP.mpf_set_default_prec(prec.to_u64)\n  end\n\n  # :inherit:\n  def nan? : Bool\n    # there are no NaNs in GMP\n    false\n  end\n\n  # :inherit:\n  def infinite? : Int32?\n    # there are no infinities in GMP\n    nil\n  end\n\n  def <=>(other : BigFloat)\n    LibGMP.mpf_cmp(self, other)\n  end\n\n  def <=>(other : BigInt)\n    LibGMP.mpf_cmp_z(self, other)\n  end\n\n  def <=>(other : Float::Primitive) : Int32?\n    LibGMP.mpf_cmp_d(self, other) unless other.nan?\n  end\n\n  def <=>(other : Int) : Int32\n    Int.primitive_si_ui_check(other) do |si, ui, big_i|\n      {\n        si:    LibGMP.mpf_cmp_si(self, {{ si }}),\n        ui:    LibGMP.mpf_cmp_ui(self, {{ ui }}),\n        big_i: self <=> {{ big_i }},\n      }\n    end\n  end\n\n  def <=>(other : Number)\n    LibGMP.mpf_cmp(self, other.to_big_f)\n  end\n\n  def - : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_neg(mpf, self) }\n  end\n\n  def +(other : Int::Primitive) : BigFloat\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_add_ui(mpf, self, {{ ui }}) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_sub_ui(mpf, self, {{ neg_ui }}) },\n        big_i:  self + {{ big_i }},\n      }\n    end\n  end\n\n  def +(other : Number) : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_add(mpf, self, other.to_big_f) }\n  end\n\n  def -(other : Int::Primitive) : BigFloat\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_sub_ui(mpf, self, {{ ui }}) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_add_ui(mpf, self, {{ neg_ui }}) },\n        big_i:  self - {{ big_i }},\n      }\n    end\n  end\n\n  def -(other : Number) : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_sub(mpf, self, other.to_big_f) }\n  end\n\n  def *(other : Int::Primitive) : BigFloat\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_mul_ui(mpf, self, {{ ui }}) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_mul_ui(mpf, self, {{ neg_ui }}); LibGMP.mpf_neg(mpf, mpf) },\n        big_i:  self + {{ big_i }},\n      }\n    end\n  end\n\n  def *(other : Number) : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_mul(mpf, self, other.to_big_f) }\n  end\n\n  def /(other : Int::Primitive) : BigFloat\n    # Division by 0 in BigFloat is not allowed, there is no BigFloat::Infinity\n    raise DivisionByZeroError.new if other == 0\n    Int.primitive_ui_check(other) do |ui, neg_ui, _|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, {{ ui }}) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, {{ neg_ui }}); LibGMP.mpf_neg(mpf, mpf) },\n        big_i:  BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, BigFloat.new(other)) },\n      }\n    end\n  end\n\n  def /(other : BigFloat) : BigFloat\n    # Division by 0 in BigFloat is not allowed, there is no BigFloat::Infinity\n    raise DivisionByZeroError.new if other == 0\n    BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, other) }\n  end\n\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\n\n  def **(other : BigInt) : BigFloat\n    is_zero = self.zero?\n    if is_zero\n      case other\n      when .>(0)\n        return self\n      when .<(0)\n        # there is no BigFloat::Infinity\n        raise ArgumentError.new \"Cannot raise 0 to a negative power\"\n      end\n    end\n\n    BigFloat.new do |result|\n      LibGMP.mpf_init_set_si(result, 1)\n      next if is_zero # define `0 ** 0 == 1`\n\n      # these are mutated and must be copies of `other` and `self`!\n      exponent = BigInt.new { |mpz| LibGMP.abs(mpz, other) } # `other.abs`\n      k = BigFloat.new { |mpf| LibGMP.mpf_set(mpf, self) }   # `self`\n\n      while exponent > 0\n        LibGMP.mpf_mul(result, result, k) if exponent.to_i!.odd? # `result *= k`\n        LibGMP.fdiv_q_2exp(exponent, exponent, 1)                # `exponent /= 2`\n        LibGMP.mpf_mul(k, k, k) if exponent > 0                  # `k *= k`\n      end\n\n      LibGMP.mpf_ui_div(result, 1, result) if other < 0 # `result = 1 / result`\n    end\n  end\n\n  def **(other : Int) : BigFloat\n    # there is no BigFloat::Infinity\n    if zero? && other < 0\n      raise ArgumentError.new \"Cannot raise 0 to a negative power\"\n    end\n\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_pow_ui(mpf, self, {{ ui }}) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_pow_ui(mpf, self, {{ neg_ui }}); LibGMP.mpf_ui_div(mpf, 1, mpf) },\n        big_i:  self ** {{ big_i }},\n      }\n    end\n  end\n\n  def abs : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_abs(mpf, self) }\n  end\n\n  # Rounds towards positive infinity.\n  def ceil : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_ceil(mpf, self) }\n  end\n\n  # Rounds towards negative infinity.\n  def floor : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_floor(mpf, self) }\n  end\n\n  # Rounds towards zero.\n  def trunc : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_trunc(mpf, self) }\n  end\n\n  def to_f64 : Float64\n    LibGMP.mpf_get_d(self)\n  end\n\n  def to_f32 : Float32\n    to_f64.to_f32\n  end\n\n  def to_f : Float64\n    to_f64\n  end\n\n  def to_f32! : Float32\n    to_f64.to_f32!\n  end\n\n  def to_f64! : Float64\n    to_f64\n  end\n\n  def to_f! : Float64\n    to_f64!\n  end\n\n  def to_big_f : BigFloat\n    self\n  end\n\n  def to_big_i : BigInt\n    BigInt.new { |mpz| LibGMP.set_f(mpz, mpf) }\n  end\n\n  def to_i64 : Int64\n    raise OverflowError.new unless LibGMP::Long::MIN <= self <= LibGMP::Long::MAX\n    LibGMP.mpf_get_si(self).to_i64!\n  end\n\n  def to_i32 : Int32\n    to_i64.to_i32\n  end\n\n  def to_i16 : Int16\n    to_i64.to_i16\n  end\n\n  def to_i8 : Int8\n    to_i64.to_i8\n  end\n\n  def to_i : Int32\n    to_i32\n  end\n\n  def to_i! : Int32\n    to_i32!\n  end\n\n  def to_i8! : Int8\n    LibGMP.mpf_get_si(self).to_i8!\n  end\n\n  def to_i16! : Int16\n    LibGMP.mpf_get_si(self).to_i16!\n  end\n\n  def to_i32! : Int32\n    LibGMP.mpf_get_si(self).to_i32!\n  end\n\n  def to_i64! : Int64\n    LibGMP.mpf_get_si(self).to_i64!\n  end\n\n  def to_u64 : UInt64\n    raise OverflowError.new unless 0 <= self <= LibGMP::ULong::MAX\n    LibGMP.mpf_get_ui(self).to_u64!\n  end\n\n  def to_u32 : UInt32\n    to_u64.to_u32\n  end\n\n  def to_u16 : UInt16\n    to_u64.to_u16\n  end\n\n  def to_u8 : UInt8\n    to_u64.to_u8\n  end\n\n  def to_u : UInt32\n    to_u32\n  end\n\n  def to_u! : UInt32\n    to_u32!\n  end\n\n  def to_u8! : UInt8\n    LibGMP.mpf_get_ui(self).to_u8!\n  end\n\n  def to_u16! : UInt16\n    LibGMP.mpf_get_ui(self).to_u16!\n  end\n\n  def to_u32! : UInt32\n    LibGMP.mpf_get_ui(self).to_u32!\n  end\n\n  def to_u64! : UInt64\n    LibGMP.mpf_get_ui(self).to_u64!\n  end\n\n  def to_unsafe : Pointer(LibGMP::MPF)\n    mpf\n  end\n\n  def to_s(io : IO) : Nil\n    to_s_impl(io, point_range: -3..15, int_trailing_zeros: true)\n  end\n\n  protected def to_s_impl(*, point_range : Range, int_trailing_zeros : Bool) : String\n    String.build { |io| to_s_impl(io, point_range: point_range, int_trailing_zeros: int_trailing_zeros) }\n  end\n\n  protected def to_s_impl(io : IO, *, point_range : Range, int_trailing_zeros : Bool) : Nil\n    cstr = LibGMP.mpf_get_str(nil, out orig_decimal_exponent, 10, 0, self)\n    buffer = Slice.new(cstr, LibC.strlen(cstr))\n\n    # add negative sign\n    if buffer[0]? == 45 # '-'\n      io << '-'\n      buffer = buffer[1..]\n    end\n\n    decimal_exponent = fix_exponent_overflow(orig_decimal_exponent) - buffer.size\n    if int_trailing_zeros\n      # GMP ensures `cstr` contains no trailing zeros\n      fraction = Float::Printer::FractionMode::WriteAll\n    else\n      # used by the Ryu Printf specs only\n      fraction = Float::Printer::FractionMode::RemoveIfZero\n    end\n\n    Float::Printer.decimal(io, buffer, decimal_exponent, point_range, fraction)\n  end\n\n  # The same `LibGMP::MpExp` is used in `LibGMP::MPF` to represent a\n  # `BigFloat`'s exponent in base `256 ** sizeof(LibGMP::MpLimb)`, and to return\n  # a base-10 exponent in `LibGMP.mpf_get_str`. The latter is around 9.6x the\n  # former when `MpLimb` is 32-bit, or around 19.3x when `MpLimb` is 64-bit.\n  # This means the base-10 exponent will overflow for the majority of `MpExp`'s\n  # domain, even though `BigFloat`s will work correctly in this exponent range\n  # otherwise. This method exists to recover the original exponent for `#to_s`.\n  #\n  # Note that if `MpExp` is 64-bit, which is the case for non-Windows 64-bit\n  # targets, then `mpf_get_str` will simply crash for values above\n  # `2 ** 0x1_0000_0000_0000_0080`; here `exponent10` is around 5.553e+18, and\n  # never overflows. Thus there is no need to check for overflow in that case.\n  private def fix_exponent_overflow(exponent10)\n    {% if LibGMP::MpExp == Int64 %}\n      exponent10\n    {% else %}\n      # When `self` is non-zero,\n      #\n      #     @mpf.@_mp_exp == Math.log(abs, 256.0 ** sizeof(LibGMP::MpLimb)).floor + 1\n      #     @mpf.@_mp_exp - 1 <= Math.log(abs, 256.0 ** sizeof(LibGMP::MpLimb)) < @mpf.@_mp_exp\n      #     @mpf.@_mp_exp - 1 <= Math.log10(abs) / Math.log10(256.0 ** sizeof(LibGMP::MpLimb)) < @mpf.@_mp_exp\n      #     Math.log10(abs) >= (@mpf.@_mp_exp - 1) * Math.log10(256.0 ** sizeof(LibGMP::MpLimb))\n      #     Math.log10(abs) <   @mpf.@_mp_exp      * Math.log10(256.0 ** sizeof(LibGMP::MpLimb))\n      #\n      # And also,\n      #\n      #     exponent10 == Math.log10(abs).floor + 1\n      #     exponent10 - 1 <= Math.log10(abs) < exponent10\n      #\n      # When `exponent10` overflows, it differs from its real value by an\n      # integer multiple of `256.0 ** sizeof(LibGMP::MpExp)`. We have to recover\n      # the integer `overflow_n` such that:\n      #\n      #     LibGMP::MpExp::MIN <= exponent10 <= LibGMP::MpExp::MAX\n      #     Math.log10(abs) ~= exponent10 + overflow_n * 256.0 ** sizeof(LibGMP::MpExp)\n      #                     ~= @mpf.@_mp_exp * Math.log10(256.0 ** sizeof(LibGMP::MpLimb))\n      #\n      # Because the possible intervals for the real `exponent10` are so far apart,\n      # it suffices to approximate `overflow_n` as follows:\n      #\n      #     overflow_n ~= (@mpf.@_mp_exp * Math.log10(256.0 ** sizeof(LibGMP::MpLimb)) - exponent10) / 256.0 ** sizeof(LibGMP::MpExp)\n      #\n      # This value will be very close to an integer, which we then obtain with\n      # `#round`.\n      overflow_n = ((@mpf.@_mp_exp * Math.log10(256.0 ** sizeof(LibGMP::MpLimb)) - exponent10) / 256.0 ** sizeof(LibGMP::MpExp))\n      exponent10.to_i64 + overflow_n.round.to_i64 * (256_i64 ** sizeof(LibGMP::MpExp))\n    {% end %}\n  end\n\n  # :inherit:\n  def format(io : IO, separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : Nil\n    number = self\n    if decimal_places\n      number = number.round(decimal_places)\n    end\n\n    if decimal_places && decimal_places >= 0\n      string = number.abs.to_s_impl(point_range: .., int_trailing_zeros: true)\n      integer, _, decimals = string.partition('.')\n    else\n      string = number.to_s_impl(point_range: .., int_trailing_zeros: true)\n      _, _, decimals = string.partition(\".\")\n      integer = number.trunc.to_big_i.abs.to_s\n    end\n\n    is_negative = number < 0\n\n    format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant)\n  end\n\n  def clone : BigFloat\n    self\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds towards the even neighbor (Banker's rounding).\n  def round_even : self\n    if self >= 0\n      halfway = self + 0.5\n    else\n      halfway = self - 0.5\n    end\n    if halfway.integer?\n      if halfway == (halfway / 2).trunc * 2\n        halfway\n      else\n        halfway - sign\n      end\n    else\n      if self >= 0\n        halfway.floor\n      else\n        halfway.ceil\n      end\n    end\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds away from zero.\n  def round_away : self\n    if self >= 0\n      (self + 0.5).floor\n    else\n      (self - 0.5).ceil\n    end\n  end\n\n  # :inherit:\n  def integer? : Bool\n    !LibGMP.mpf_integer_p(mpf).zero?\n  end\n\n  private def mpf\n    pointerof(@mpf)\n  end\nend\n\nstruct Number\n  include Comparable(BigFloat)\n\n  def +(other : BigFloat)\n    other + self\n  end\n\n  def -(other : BigFloat)\n    to_big_f - other\n  end\n\n  def *(other : BigFloat) : BigFloat\n    other * self\n  end\n\n  def /(other : BigFloat) : BigFloat\n    to_big_f / other\n  end\n\n  def to_big_f : BigFloat\n    BigFloat.new(self)\n  end\nend\n\nstruct Int\n  def <=>(other : BigFloat) : Int32\n    -(other <=> self)\n  end\n\n  def -(other : BigFloat) : BigFloat\n    Int.primitive_ui_check(self) do |ui, neg_ui, _|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_neg(mpf, other); LibGMP.mpf_add_ui(mpf, mpf, {{ ui }}) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_neg(mpf, other); LibGMP.mpf_sub_ui(mpf, mpf, {{ neg_ui }}) },\n        big_i:  BigFloat.new { |mpf| LibGMP.mpf_sub(mpf, BigFloat.new(self), other) },\n      }\n    end\n  end\n\n  def /(other : BigFloat) : BigFloat\n    # Division by 0 in BigFloat is not allowed, there is no BigFloat::Infinity\n    raise DivisionByZeroError.new if other == 0\n\n    Int.primitive_ui_check(self) do |ui, neg_ui, _|\n      {\n        ui:     BigFloat.new { |mpf| LibGMP.mpf_ui_div(mpf, {{ ui }}, other) },\n        neg_ui: BigFloat.new { |mpf| LibGMP.mpf_ui_div(mpf, {{ neg_ui }}, other); LibGMP.mpf_neg(mpf, mpf) },\n        big_i:  BigFloat.new { |mpf| LibGMP.mpf_div(mpf, BigFloat.new(self), other) },\n      }\n    end\n  end\nend\n\nstruct Float\n  def <=>(other : BigFloat)\n    cmp = other <=> self\n    -cmp if cmp\n  end\nend\n\nclass String\n  # Converts `self` to a `BigFloat`.\n  #\n  # ```\n  # require \"big\"\n  # \"1234.0\".to_big_f\n  # ```\n  def to_big_f : BigFloat\n    BigFloat.new(self)\n  end\nend\n\nmodule Math\n  # Returns the unbiased base 2 exponent of the given floating-point *value*.\n  #\n  # Raises `ArgumentError` if *value* is zero.\n  def ilogb(value : BigFloat) : Int64\n    raise ArgumentError.new \"Cannot get exponent of zero\" if value.zero?\n    leading_zeros = value.@mpf._mp_d[value.@mpf._mp_size.abs - 1].leading_zeros_count\n    8_i64 * sizeof(LibGMP::MpLimb) * value.@mpf._mp_exp - leading_zeros - 1\n  end\n\n  # Returns the unbiased radix-independent exponent of the given floating-point *value*.\n  #\n  # For `BigFloat` this is equivalent to `ilogb`.\n  #\n  # Raises `ArgumentError` is *value* is zero.\n  def logb(value : BigFloat) : BigFloat\n    ilogb(value).to_big_f\n  end\n\n  # Multiplies the given floating-point *value* by 2 raised to the power *exp*.\n  def ldexp(value : BigFloat, exp : Int) : BigFloat\n    BigFloat.new do |mpf|\n      if exp >= 0\n        LibGMP.mpf_mul_2exp(mpf, value, exp.to_u64)\n      else\n        LibGMP.mpf_div_2exp(mpf, value, exp.abs.to_u64)\n      end\n    end\n  end\n\n  # Returns the floating-point *value* with its exponent raised by *exp*.\n  #\n  # For `BigFloat` this is equivalent to `ldexp`.\n  def scalbn(value : BigFloat, exp : Int) : BigFloat\n    ldexp(value, exp)\n  end\n\n  # :ditto:\n  def scalbln(value : BigFloat, exp : Int) : BigFloat\n    ldexp(value, exp)\n  end\n\n  # Decomposes the given floating-point *value* into a normalized fraction and an integral power of two.\n  def frexp(value : BigFloat) : {BigFloat, Int64}\n    return {BigFloat.zero, 0_i64} if value.zero?\n\n    # We compute this ourselves since `LibGMP.mpf_get_d_2exp` only returns a\n    # `LibC::Long` exponent, which is not sufficient for 32-bit `LibC::Long` and\n    # 32-bit `LibGMP::MpExp`, e.g. on 64-bit Windows.\n    # Since `0.5 <= frac.abs < 1.0`, the radix point should be just above the\n    # most significant limb, and there should be no leading zeros in that limb.\n    leading_zeros = value.@mpf._mp_d[value.@mpf._mp_size.abs - 1].leading_zeros_count\n    exp = 8_i64 * sizeof(LibGMP::MpLimb) * value.@mpf._mp_exp - leading_zeros\n\n    frac = BigFloat.new do |mpf|\n      # remove leading zeros in the most significant limb\n      LibGMP.mpf_mul_2exp(mpf, value, leading_zeros)\n      # reset the exponent manually\n      mpf.value._mp_exp = 0\n    end\n\n    {frac, exp}\n  end\n\n  # Returns the floating-point value with the magnitude of *value1* and the sign of *value2*.\n  #\n  # `BigFloat` does not support signed zeros; if `value2 == 0`, the returned value is non-negative.\n  def copysign(value1 : BigFloat, value2 : BigFloat) : BigFloat\n    if value1.negative? != value2.negative? # opposite signs\n      -value1\n    else\n      value1\n    end\n  end\n\n  # Calculates the square root of *value*.\n  #\n  # ```\n  # require \"big\"\n  #\n  # Math.sqrt(1_000_000_000_000.to_big_f * 1_000_000_000_000.to_big_f) # => 1000000000000.0\n  # ```\n  def sqrt(value : BigFloat) : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_sqrt(mpf, value) }\n  end\nend\n\n# :nodoc:\nstruct String::Formatter(A)\n  def int(flags, arg : BigFloat) : Nil\n    int(flags, arg.to_big_i)\n  end\nend\n"
  },
  {
    "path": "src/big/big_int.cr",
    "content": "require \"c/string\"\nrequire \"big\"\nrequire \"random\"\n\n# A `BigInt` can represent arbitrarily large integers.\n#\n# It is implemented under the hood with [GMP](https://gmplib.org/).\n#\n# NOTE: To use `BigInt`, you must explicitly import it with `require \"big\"`\nstruct BigInt < Int\n  include Comparable(Int::Signed)\n  include Comparable(Int::Unsigned)\n  include Comparable(BigInt)\n  include Comparable(Float)\n\n  # Creates a `BigInt` with the value zero.\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigInt.new # => 0\n  # ```\n  def initialize\n    LibGMP.init(out @mpz)\n  end\n\n  # Creates a `BigInt` with the value denoted by *str* in the given *base*.\n  #\n  # Raises `ArgumentError` if the string doesn't denote a valid integer.\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigInt.new(\"123456789123456789123456789123456789\") # => 123456789123456789123456789123456789\n  # BigInt.new(\"123_456_789_123_456_789_123_456_789\")  # => 123456789123456789123456789\n  # BigInt.new(\"1234567890ABCDEF\", base: 16)           # => 1311768467294899695\n  # ```\n  def initialize(str : String, base : Int32 = 10)\n    # Strip leading '+' char to smooth out cases with strings like \"+123\"\n    str = str.lchop('+')\n    # Strip '_' to make it compatible with int literals like \"1_000_000\"\n    str = str.delete('_')\n    err = LibGMP.init_set_str(out @mpz, str, base)\n    if err == -1\n      raise ArgumentError.new(\"Invalid BigInt: #{str}\")\n    end\n  end\n\n  # Creates a `BigInt` from the given *num*.\n  def self.new(num : Int::Primitive) : self\n    Int.primitive_si_ui_check(num) do |si, ui, _|\n      {\n        si: begin\n          LibGMP.init_set_si(out mpz1, {{ si }})\n          new(mpz1)\n        end,\n        ui: begin\n          LibGMP.init_set_ui(out mpz2, {{ ui }})\n          new(mpz2)\n        end,\n        big_i: begin\n          negative = num < 0\n          num = num.abs_unsigned\n          capacity = (num.bit_length - 1) // (sizeof(LibGMP::MpLimb) * 8) + 1\n\n          # This assumes GMP wasn't built with its experimental nails support:\n          # https://gmplib.org/manual/Low_002dlevel-Functions\n          unsafe_build(capacity) do |limbs|\n            appender = limbs.to_unsafe.appender\n            limbs.size.times do\n              appender << LibGMP::MpLimb.new!(num)\n              num = num.unsafe_shr(sizeof(LibGMP::MpLimb) * 8)\n            end\n            {capacity, negative}\n          end\n        end,\n      }\n    end\n  end\n\n  private def self.unsafe_build(capacity : Int, & : Slice(LibGMP::MpLimb) -> {Int, Bool})\n    # https://gmplib.org/manual/Initializing-Integers:\n    #\n    # > In preparation for an operation, GMP often allocates one limb more than\n    # > ultimately needed. To make sure GMP will not perform reallocation for x,\n    # > you need to add the number of bits in mp_limb_t to n.\n    LibGMP.init2(out mpz, (capacity + 1) * sizeof(LibGMP::MpLimb) * 8)\n    limbs = LibGMP.limbs_write(pointerof(mpz), capacity)\n    size, negative = yield Slice.new(limbs, capacity)\n    LibGMP.limbs_finish(pointerof(mpz), size * (negative ? -1 : 1))\n    new(mpz)\n  end\n\n  # Returns a read-only `Slice` of the limbs that make up this integer, which\n  # is effectively `abs.digits(2 ** N)` where `N` is the number of bits in\n  # `LibGMP::MpLimb`, except that an empty `Slice` is returned for zero.\n  #\n  # This assumes GMP wasn't built with its experimental nails support:\n  # https://gmplib.org/manual/Low_002dlevel-Functions\n  private def limbs\n    Slice.new(LibGMP.limbs_read(self), LibGMP.size(self), read_only: true)\n  end\n\n  # :ditto:\n  #\n  # *num* must be finite.\n  def initialize(num : Float::Primitive)\n    raise ArgumentError.new \"Can only construct from a finite number\" unless num.finite?\n    LibGMP.init_set_d(out @mpz, num)\n  end\n\n  # :ditto:\n  def self.new(num : BigFloat) : self\n    num.to_big_i\n  end\n\n  # :ditto:\n  def self.new(num : BigDecimal) : self\n    num.to_big_i\n  end\n\n  # :ditto:\n  def self.new(num : BigRational) : self\n    num.to_big_i\n  end\n\n  # Returns *num*. Useful for generic code that does `T.new(...)` with `T`\n  # being a `Number`.\n  def self.new(num : BigInt) : self\n    num\n  end\n\n  # :nodoc:\n  def initialize(@mpz : LibGMP::MPZ)\n  end\n\n  # :nodoc:\n  def self.new(&)\n    LibGMP.init(out mpz)\n    yield pointerof(mpz)\n    new(mpz)\n  end\n\n  # Returns a number for given *digits* and *base*. The digits are expected as\n  # an `Enumerable` with the least significant digit as the first element.\n  #\n  # *base* must not be less than 2.\n  #\n  # All digits must be within `0...base`.\n  def self.from_digits(digits : Enumerable(Int), base : Int = 10) : self\n    if base < 2\n      raise ArgumentError.new(\"Invalid base #{base}\")\n    end\n\n    new do |mpz|\n      multiplier = new(1)\n      first_element = true\n\n      digits.each do |digit|\n        if digit < 0\n          raise ArgumentError.new(\"Invalid digit #{digit}\")\n        end\n\n        if digit >= base\n          raise ArgumentError.new(\"Invalid digit #{digit} for base #{base}\")\n        end\n\n        # don't calculate multiplier upfront for the next digit\n        # to avoid overflow at the last iteration\n        if first_element\n          first_element = false\n\n          # mpz = digit\n          Int.primitive_ui_check(digit) do |ui, _, big_i|\n            {\n              ui:    LibGMP.set_ui(mpz, {{ ui }}),\n              big_i: LibGMP.set(mpz, {{ big_i }}),\n            }\n          end\n        else\n          # multiplier *= base\n          Int.primitive_ui_check(base) do |ui, _, big_i|\n            {\n              ui:    LibGMP.mul_ui(multiplier, multiplier, {{ ui }}),\n              big_i: LibGMP.mul(multiplier, multiplier, {{ big_i }}),\n            }\n          end\n\n          # mpz += base * digits\n          Int.primitive_ui_check(digit) do |ui, _, big_i|\n            {\n              ui:    LibGMP.addmul_ui(mpz, multiplier, {{ ui }}),\n              big_i: LibGMP.addmul(mpz, multiplier, {{ big_i }}),\n            }\n          end\n        end\n      end\n    end\n  end\n\n  def <=>(other : BigInt)\n    LibGMP.cmp(mpz, other)\n  end\n\n  def <=>(other : Int)\n    Int.primitive_si_ui_check(other) do |si, ui, big_i|\n      {\n        si:    LibGMP.cmp_si(self, {{ si }}),\n        ui:    LibGMP.cmp_ui(self, {{ ui }}),\n        big_i: self <=> {{ big_i }},\n      }\n    end\n  end\n\n  def <=>(other : Float::Primitive) : Int32?\n    LibGMP.cmp_d(mpz, other) unless other.nan?\n  end\n\n  def +(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.add(mpz, self, other) }\n  end\n\n  def +(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.add_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, {{ neg_ui }}) },\n        big_i:  self + {{ big_i }},\n      }\n    end\n  end\n\n  def &+(other) : BigInt\n    self + other\n  end\n\n  def -(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.sub(mpz, self, other) }\n  end\n\n  def -(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.add_ui(mpz, self, {{ neg_ui }}) },\n        big_i:  self - {{ big_i }},\n      }\n    end\n  end\n\n  def &-(other) : BigInt\n    self - other\n  end\n\n  def - : BigInt\n    BigInt.new { |mpz| LibGMP.neg(mpz, self) }\n  end\n\n  def abs : BigInt\n    BigInt.new { |mpz| LibGMP.abs(mpz, self) }\n  end\n\n  def factorial : BigInt\n    if self < 0\n      raise ArgumentError.new(\"Factorial not defined for negative values\")\n    elsif self > LibGMP::UI::MAX\n      raise ArgumentError.new(\"Factorial not supported for numbers bigger than #{LibGMP::UI::MAX}\")\n    end\n    BigInt.new { |mpz| LibGMP.fac_ui(mpz, LibGMP::UI.new!(self)) }\n  end\n\n  def *(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.mul(mpz, self, other) }\n  end\n\n  def *(other : Int) : BigInt\n    Int.primitive_si_ui_check(other) do |si, ui, big_i|\n      {\n        si:    BigInt.new { |mpz| LibGMP.mul_si(mpz, self, {{ si }}) },\n        ui:    BigInt.new { |mpz| LibGMP.mul_ui(mpz, self, {{ ui }}) },\n        big_i: self * {{ big_i }},\n      }\n    end\n  end\n\n  def &*(other) : BigInt\n    self * other\n  end\n\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\n\n  def //(other : Int) : BigInt\n    check_division_by_zero other\n\n    unsafe_floored_div(other)\n  end\n\n  def tdiv(other : Int) : BigInt\n    check_division_by_zero other\n\n    unsafe_truncated_div(other)\n  end\n\n  def unsafe_floored_div(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.fdiv_q(mpz, self, other) }\n  end\n\n  def unsafe_floored_div(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, -self, {{ neg_ui }}) },\n        big_i:  unsafe_floored_div({{ big_i }}),\n      }\n    end\n  end\n\n  def unsafe_truncated_div(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.tdiv_q(mpz, self, other) }\n  end\n\n  def unsafe_truncated_div(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, {{ neg_ui }}); LibGMP.neg(mpz, mpz) },\n        big_i:  unsafe_truncated_div({{ big_i }}),\n      }\n    end\n  end\n\n  def %(other : Int) : BigInt\n    check_division_by_zero other\n\n    unsafe_floored_mod(other)\n  end\n\n  def remainder(other : Int) : BigInt\n    check_division_by_zero other\n\n    unsafe_truncated_mod(other)\n  end\n\n  def divmod(number : Int) : {BigInt, BigInt}\n    check_division_by_zero number\n\n    unsafe_floored_divmod(number)\n  end\n\n  def unsafe_floored_mod(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.fdiv_r(mpz, self, other) }\n  end\n\n  def unsafe_floored_mod(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, {{ neg_ui }}); LibGMP.neg(mpz, mpz) },\n        big_i:  unsafe_floored_mod({{ big_i }}),\n      }\n    end\n  end\n\n  def unsafe_truncated_mod(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.tdiv_r(mpz, self, other) }\n  end\n\n  def unsafe_truncated_mod(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, {{ neg_ui }}) },\n        big_i:  unsafe_truncated_mod({{ big_i }}),\n      }\n    end\n  end\n\n  def unsafe_floored_divmod(number : BigInt) : {BigInt, BigInt}\n    the_q = BigInt.new\n    the_r = BigInt.new { |r| LibGMP.fdiv_qr(the_q, r, self, number) }\n    {the_q, the_r}\n  end\n\n  def unsafe_floored_divmod(number : Int) : {BigInt, BigInt}\n    the_q = BigInt.new\n    the_r = Int.primitive_ui_check(number) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, self, {{ ui }}) },\n        neg_ui: BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, -self, {{ neg_ui }}); LibGMP.neg(r, r) },\n        big_i:  BigInt.new { |r| LibGMP.fdiv_qr(the_q, r, self, {{ big_i }}) },\n      }\n    end\n    {the_q, the_r}\n  end\n\n  def unsafe_truncated_divmod(number : BigInt) : Tuple(BigInt, BigInt)\n    the_q = BigInt.new\n    the_r = BigInt.new { |r| LibGMP.tdiv_qr(the_q, r, self, number) }\n    {the_q, the_r}\n  end\n\n  def unsafe_truncated_divmod(number : Int)\n    the_q = BigInt.new\n    the_r = Int.primitive_ui_check(number) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, {{ ui }}) },\n        neg_ui: BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, {{ neg_ui }}); LibGMP.neg(the_q, the_q) },\n        big_i:  BigInt.new { |r| LibGMP.tdiv_qr(the_q, r, self, {{ big_i }}) },\n      }\n    end\n    {the_q, the_r}\n  end\n\n  def divisible_by?(number : BigInt) : Bool\n    LibGMP.divisible_p(self, number) != 0\n  end\n\n  def divisible_by?(number : Int) : Bool\n    Int.primitive_ui_check(number) do |ui, neg_ui, big_i|\n      {\n        ui:     LibGMP.divisible_ui_p(self, {{ ui }}) != 0,\n        neg_ui: LibGMP.divisible_ui_p(self, {{ neg_ui }}) != 0,\n        big_i:  divisible_by?({{ big_i }}),\n      }\n    end\n  end\n\n  # :nodoc:\n  # returns `{reduced, count}` such that `self % (number ** count) == 0`,\n  # `self % (number ** (count + 1)) != 0`, and `reduced == self / (number ** count)`\n  def factor_by(number : Int) : {BigInt, UInt64}\n    return {self, 0_u64} unless divisible_by?(number)\n\n    reduced = BigInt.new\n    count = LibGMP.remove(reduced, self, number.to_big_i)\n    {reduced, count.to_u64}\n  end\n\n  def ~ : BigInt\n    BigInt.new { |mpz| LibGMP.com(mpz, self) }\n  end\n\n  def bit(bit : Int) : Int32\n    return 0 if bit < 0\n    return self < 0 ? 1 : 0 if bit > LibGMP::BitcntT::MAX\n    LibGMP.tstbit(self, LibGMP::BitcntT.new!(bit))\n  end\n\n  def &(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.and(mpz, self, other) }\n  end\n\n  def &(other : Int) : BigInt\n    ret = other.to_big_i\n    LibGMP.and(ret, ret, self)\n    ret\n  end\n\n  def |(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.ior(mpz, self, other) }\n  end\n\n  def |(other : Int) : BigInt\n    ret = other.to_big_i\n    LibGMP.ior(ret, ret, self)\n    ret\n  end\n\n  def ^(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.xor(mpz, self, other) }\n  end\n\n  def ^(other : Int) : BigInt\n    ret = other.to_big_i\n    LibGMP.xor(ret, ret, self)\n    ret\n  end\n\n  def >>(other : Int) : BigInt\n    BigInt.new { |mpz| LibGMP.fdiv_q_2exp(mpz, self, other) }\n  end\n\n  # :nodoc:\n  #\n  # Because every Int needs this method.\n  def unsafe_shr(count : Int) : self\n    self >> count\n  end\n\n  def <<(other : Int) : BigInt\n    BigInt.new { |mpz| LibGMP.mul_2exp(mpz, self, other) }\n  end\n\n  def **(other : Int) : BigInt\n    if other < 0\n      raise ArgumentError.new(\"Negative exponent isn't supported\")\n    elsif other == 1\n      self\n    else\n      BigInt.new { |mpz| LibGMP.pow_ui(mpz, self, other) }\n    end\n  end\n\n  # Returns the greatest common divisor of `self` and *other*.\n  def gcd(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.gcd(mpz, self, other) }\n  end\n\n  # :ditto:\n  def gcd(other : Int) : Int\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui: begin\n          result = LibGMP.gcd_ui(nil, self, {{ ui }})\n          result == 0 ? self : result\n        end,\n        neg_ui: begin\n          result = LibGMP.gcd_ui(nil, self, {{ neg_ui }})\n          result == 0 ? self : result\n        end,\n        big_i: gcd({{ big_i }}),\n      }\n    end\n  end\n\n  # Returns the least common multiple of `self` and *other*.\n  def lcm(other : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.lcm(mpz, self, other) }\n  end\n\n  # :ditto:\n  def lcm(other : Int) : BigInt\n    Int.primitive_ui_check(other) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, {{ neg_ui }}) },\n        big_i:  lcm({{ big_i }}),\n      }\n    end\n  end\n\n  def bit_length : Int32\n    LibGMP.sizeinbase(self, 2).to_i\n  end\n\n  def to_s(base : Int = 10, *, precision : Int = 1, upcase : Bool = false) : String\n    raise ArgumentError.new(\"Invalid base #{base}\") unless 2 <= base <= 36 || base == 62\n    raise ArgumentError.new(\"upcase must be false for base 62\") if upcase && base == 62\n    raise ArgumentError.new(\"Precision must be non-negative\") unless precision >= 0\n\n    case {self, precision}\n    when {0, 0}\n      \"\"\n    when {0, 1}\n      \"0\"\n    when {1, 1}\n      \"1\"\n    else\n      count = LibGMP.sizeinbase(self, base).to_i\n      negative = self < 0\n\n      if precision <= count\n        len = count + (negative ? 1 : 0)\n        String.new(len + 1) do |buffer| # null terminator required by GMP\n          buffer[len - 1] = 0\n          LibGMP.get_str(buffer, upcase ? -base : base, self)\n\n          # `sizeinbase` may be 1 greater than the exact value\n          if buffer[len - 1] == 0\n            if precision == count\n              # In this case the exact `count` is `precision - 1`, i.e. one zero\n              # should be inserted at the beginning of the number\n              # e.g. precision = 3, count = 3, exact count = 2\n              # \"85\\0\\0\" -> \"085\\0\" for positive\n              # \"-85\\0\\0\" -> \"-085\\0\" for negative\n              start = buffer + (negative ? 1 : 0)\n              start.move_to(start + 1, count - 1)\n              start.value = '0'.ord.to_u8\n            else\n              len -= 1\n            end\n          end\n\n          base62_swapcase(Slice.new(buffer, len)) if base == 62\n          {len, len}\n        end\n      else\n        len = precision + (negative ? 1 : 0)\n        String.new(len + 1) do |buffer|\n          # e.g. precision = 13, count = 8\n          # \"_____12345678\\0\" for positive\n          # \"_____-12345678\\0\" for negative\n          buffer[len - 1] = 0\n          start = buffer + precision - count\n          LibGMP.get_str(start, upcase ? -base : base, self)\n\n          # `sizeinbase` may be 1 greater than the exact value\n          if buffer[len - 1] == 0\n            # e.g. precision = 7, count = 3, exact count = 2\n            # \"____85\\0\\0\" -> \"____885\\0\" for positive\n            # \"____-85\\0\\0\" -> \"____-885\\0\" for negative\n            # `start` will be zero-filled later\n            count -= 1\n            start += 1 if negative\n            start.move_to(start + 1, count)\n          end\n\n          base62_swapcase(Slice.new(buffer + len - count, count)) if base == 62\n\n          if negative\n            buffer.value = '-'.ord.to_u8\n            buffer += 1\n          end\n          Slice.new(buffer, precision - count).fill('0'.ord.to_u8)\n\n          {len, len}\n        end\n      end\n    end\n  end\n\n  def to_s(io : IO, base : Int = 10, *, precision : Int = 1, upcase : Bool = false) : Nil\n    raise ArgumentError.new(\"Invalid base #{base}\") unless 2 <= base <= 36 || base == 62\n    raise ArgumentError.new(\"upcase must be false for base 62\") if upcase && base == 62\n    raise ArgumentError.new(\"Precision must be non-negative\") unless precision >= 0\n\n    case {self, precision}\n    when {0, 0}\n      # do nothing\n    when {0, 1}\n      io << '0'\n    when {1, 1}\n      io << '1'\n    else\n      count = LibGMP.sizeinbase(self, base).to_i\n      ptr = LibGMP.get_str(nil, upcase ? -base : base, self)\n      negative = self < 0\n\n      # `sizeinbase` may be 1 greater than the exact value\n      count -= 1 if ptr[count + (negative ? 0 : -1)] == 0\n\n      if precision <= count\n        buffer = Slice.new(ptr, count + (negative ? 1 : 0))\n      else\n        if negative\n          io << '-'\n          ptr += 1 # this becomes the absolute value\n        end\n\n        (precision - count).times { io << '0' }\n        buffer = Slice.new(ptr, count)\n      end\n\n      base62_swapcase(buffer) if base == 62\n      io.write_string buffer\n    end\n  end\n\n  private def base62_swapcase(buffer)\n    buffer.map! do |x|\n      # for ASCII integers as returned by GMP the only possible characters are\n      # '\\0', '-', '0'..'9', 'A'..'Z', and 'a'..'z'\n      if x & 0x40 != 0 # 'A'..'Z', 'a'..'z'\n        x ^ 0x20\n      else # '\\0', '-', '0'..'9'\n        x\n      end\n    end\n  end\n\n  def digits(base = 10) : Array(Int32)\n    if self < 0\n      raise ArgumentError.new(\"Can't request digits of negative number\")\n    end\n\n    ary = [] of Int32\n    self.to_s(base).each_char { |c| ary << c.to_i(base) }\n    ary.reverse!\n    ary\n  end\n\n  def popcount : Int\n    LibGMP.popcount(self)\n  end\n\n  def trailing_zeros_count : Int\n    LibGMP.scan1(self, 0)\n  end\n\n  # :nodoc:\n  def next_power_of_two : self\n    one = BigInt.new(1)\n    return one if self <= 0\n\n    popcount == 1 ? self : one << bit_length\n  end\n\n  def to_i : Int32\n    to_i32\n  end\n\n  def to_i! : Int32\n    to_i32!\n  end\n\n  def to_u : UInt32\n    to_u32\n  end\n\n  def to_u! : UInt32\n    to_u32!\n  end\n\n  {% for n in [8, 16, 32, 64, 128] %}\n    def to_i{{n}} : Int{{n}}\n      \\{% if Int{{n}} == LibGMP::SI %}\n        LibGMP.{{ flag?(:win32) && !flag?(:gnu) ? \"fits_si_p\".id : \"fits_slong_p\".id }}(self) != 0 ? LibGMP.get_si(self) : raise OverflowError.new\n      \\{% elsif Int{{n}}::MAX.is_a?(NumberLiteral) && Int{{n}}::MAX < LibGMP::SI::MAX %}\n        LibGMP::SI.new(self).to_i{{n}}\n      \\{% else %}\n        to_primitive_i(Int{{n}})\n      \\{% end %}\n    end\n\n    def to_u{{n}} : UInt{{n}}\n      \\{% if UInt{{n}} == LibGMP::UI %}\n        LibGMP.{{ flag?(:win32) && !flag?(:gnu) ? \"fits_ui_p\".id : \"fits_ulong_p\".id }}(self) != 0 ? LibGMP.get_ui(self) : raise OverflowError.new\n      \\{% elsif UInt{{n}}::MAX.is_a?(NumberLiteral) && UInt{{n}}::MAX < LibGMP::UI::MAX %}\n        LibGMP::UI.new(self).to_u{{n}}\n      \\{% else %}\n        to_primitive_u(UInt{{n}})\n      \\{% end %}\n    end\n\n    def to_i{{n}}! : Int{{n}}\n      to_u{{n}}!.to_i{{n}}!\n    end\n\n    def to_u{{n}}! : UInt{{n}}\n      \\{% if UInt{{n}} == LibGMP::UI %}\n        LibGMP.get_ui(self) &* sign\n      \\{% elsif UInt{{n}}::MAX.is_a?(NumberLiteral) && UInt{{n}}::MAX < LibGMP::UI::MAX %}\n        LibGMP::UI.new!(self).to_u{{n}}!\n      \\{% else %}\n        to_primitive_u!(UInt{{n}})\n      \\{% end %}\n    end\n  {% end %}\n\n  private def to_primitive_i(type : T.class) : T forall T\n    self >= 0 ? to_primitive_i_positive(T) : to_primitive_i_negative(T)\n  end\n\n  private def to_primitive_u(type : T.class) : T forall T\n    self >= 0 ? to_primitive_i_positive(T) : raise OverflowError.new\n  end\n\n  private def to_primitive_u!(type : T.class) : T forall T\n    limbs = self.limbs\n    max_bits = sizeof(T) * 8\n    bits_per_limb = sizeof(LibGMP::MpLimb) * 8\n\n    x = T.zero\n    limbs.each_with_index do |limb, i|\n      break if i * bits_per_limb >= max_bits\n      x |= T.new!(limb) << (i * bits_per_limb)\n    end\n    x &* sign\n  end\n\n  private def to_primitive_i_positive(type : T.class) : T forall T\n    limbs = self.limbs\n    bits_per_limb = sizeof(LibGMP::MpLimb) * 8\n\n    highest_limb_index = (sizeof(T) * 8 - 1) // bits_per_limb\n    raise OverflowError.new if limbs.size > highest_limb_index + 1\n    if highest_limb = limbs[highest_limb_index]?\n      mask = LibGMP::MpLimb.new!(T::MAX >> (bits_per_limb * highest_limb_index))\n      raise OverflowError.new if highest_limb > mask\n    end\n\n    x = T.zero\n    limbs.reverse_each do |limb|\n      x <<= bits_per_limb\n      x |= limb\n    end\n    x\n  end\n\n  private def to_primitive_i_negative(type : T.class) : T forall T\n    limbs = self.limbs\n    bits_per_limb = sizeof(LibGMP::MpLimb) * 8\n\n    x = T.zero.abs_unsigned\n    limit = T::MIN.abs_unsigned\n    preshift_limit = limit >> bits_per_limb\n    limbs.reverse_each do |limb|\n      raise OverflowError.new if x > preshift_limit\n      x <<= bits_per_limb\n\n      # precondition: T must be larger than LibGMP::MpLimb, otherwise overflows\n      # like `0_i8 | 256` would happen and `x += limb` should be called instead\n      x |= limb\n      raise OverflowError.new if x > limit\n    end\n    x.neg_signed\n  end\n\n  def to_f : Float64\n    to_f64\n  end\n\n  def to_f32 : Float32\n    to_f64.to_f32\n  end\n\n  def to_f64 : Float64\n    LibGMP.get_d(self)\n  end\n\n  def to_f!\n    to_f64!\n  end\n\n  def to_f32!\n    LibGMP.get_d(self).to_f32!\n  end\n\n  def to_f64!\n    LibGMP.get_d(self)\n  end\n\n  def to_big_i : BigInt\n    self\n  end\n\n  def to_big_f : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_set_z(mpf, mpz) }\n  end\n\n  def to_big_d : BigDecimal\n    BigDecimal.new(self)\n  end\n\n  def to_big_r : BigRational\n    BigRational.new(self)\n  end\n\n  def clone : BigInt\n    self\n  end\n\n  private def check_division_by_zero(value)\n    if value == 0\n      raise DivisionByZeroError.new\n    end\n  end\n\n  private def mpz\n    pointerof(@mpz)\n  end\n\n  def to_unsafe : Pointer(LibGMP::MPZ)\n    mpz\n  end\nend\n\nstruct Int\n  include Comparable(BigInt)\n\n  def <=>(other : BigInt)\n    -(other <=> self)\n  end\n\n  def +(other : BigInt) : BigInt\n    other + self\n  end\n\n  def &+(other : BigInt) : BigInt\n    self + other\n  end\n\n  def -(other : BigInt) : BigInt\n    Int.primitive_ui_check(self) do |ui, neg_ui, big_i|\n      {\n        ui:     BigInt.new { |mpz| LibGMP.neg(mpz, other); LibGMP.add_ui(mpz, mpz, {{ ui }}) },\n        neg_ui: BigInt.new { |mpz| LibGMP.neg(mpz, other); LibGMP.sub_ui(mpz, mpz, {{ neg_ui }}) },\n        big_i:  {{ big_i }} - other,\n      }\n    end\n  end\n\n  def &-(other : BigInt) : BigInt\n    self - other\n  end\n\n  def *(other : BigInt) : BigInt\n    other * self\n  end\n\n  def &*(other : BigInt) : BigInt\n    self * other\n  end\n\n  def %(other : BigInt) : BigInt\n    to_big_i % other\n  end\n\n  # Returns the greatest common divisor of `self` and *other*.\n  def gcd(other : BigInt) : Int\n    other.gcd(self)\n  end\n\n  # Returns the least common multiple of `self` and *other*.\n  def lcm(other : BigInt) : BigInt\n    other.lcm(self)\n  end\n\n  # Returns a `BigInt` representing this integer.\n  # ```\n  # require \"big\"\n  #\n  # 123.to_big_i\n  # ```\n  def to_big_i : BigInt\n    BigInt.new(self)\n  end\nend\n\nstruct Float\n  include Comparable(BigInt)\n\n  def <=>(other : BigInt)\n    cmp = other <=> self\n    -cmp if cmp\n  end\n\n  # Returns a `BigInt` representing this float (rounded using `floor`).\n  # ```\n  # require \"big\"\n  #\n  # 1212341515125412412412421.0.to_big_i\n  # ```\n  def to_big_i : BigInt\n    BigInt.new(self)\n  end\nend\n\nclass String\n  # Returns a `BigInt` from this string, in the given *base*.\n  #\n  # Raises `ArgumentError` if this string doesn't denote a valid integer.\n  # ```\n  # require \"big\"\n  #\n  # \"3a060dbf8d1a5ac3e67bc8f18843fc48\".to_big_i(16)\n  # ```\n  def to_big_i(base : Int32 = 10) : BigInt\n    BigInt.new(self, base)\n  end\nend\n\nmodule Math\n  # Calculates the square root of *value*.\n  #\n  # ```\n  # require \"big\"\n  #\n  # Math.sqrt(1_000_000_000_000.to_big_i * 1_000_000_000_000.to_big_i) # => 1000000000000.0\n  # ```\n  def sqrt(value : BigInt) : BigFloat\n    sqrt(value.to_big_f)\n  end\n\n  # Calculates the integer square root of *value*.\n  def isqrt(value : BigInt) : BigInt\n    BigInt.new { |mpz| LibGMP.sqrt(mpz, value) }\n  end\n\n  # Computes the smallest nonnegative power of 2 that is greater than or equal\n  # to *v*.\n  #\n  # The returned value has the same type as the argument.\n  #\n  # ```\n  # Math.pw2ceil(33) # => 64\n  # Math.pw2ceil(64) # => 64\n  # Math.pw2ceil(-5) # => 1\n  # ```\n  def pw2ceil(v : BigInt) : BigInt\n    v.next_power_of_two\n  end\nend\n\nmodule Random\n  private def rand_int(max : BigInt) : BigInt\n    # This is a copy of the algorithm in random.cr but with fewer special cases.\n    unless max > 0\n      raise ArgumentError.new \"Invalid bound for rand: #{max}\"\n    end\n\n    rand_max = BigInt.new(1) << (sizeof(typeof(next_u))*8)\n    needed_parts = 1\n    while rand_max < max && rand_max > 0\n      rand_max <<= sizeof(typeof(next_u))*8\n      needed_parts += 1\n    end\n\n    limit = rand_max // max * max\n\n    loop do\n      result = BigInt.new(next_u)\n      (needed_parts - 1).times do\n        result <<= sizeof(typeof(next_u))*8\n        result |= BigInt.new(next_u)\n      end\n\n      # For a uniform distribution we may need to throw away some numbers.\n      if result < limit\n        return result % max\n      end\n    end\n  end\n\n  private def rand_range(range : Range(BigInt, BigInt)) : BigInt\n    span = range.end - range.begin\n    unless range.excludes_end?\n      span += 1\n    end\n    unless span > 0\n      raise ArgumentError.new \"Invalid range for rand: #{range}\"\n    end\n    range.begin + rand_int(span)\n  end\nend\n\n# :nodoc:\nstruct Crystal::Hasher\n  private HASH_MODULUS_INT_P = BigInt.new(HASH_MODULUS)\n\n  def self.reduce_num(value : BigInt) : UInt64\n    {% if LibGMP::UI == UInt64 %}\n      v = LibGMP.tdiv_ui(value, HASH_MODULUS)\n      value < 0 ? &-v : v\n    {% else %}\n      value.remainder(HASH_MODULUS_INT_P).to_u64!\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/big/big_rational.cr",
    "content": "require \"big\"\n\n# Rational numbers are represented as the quotient of arbitrarily large\n# numerators and denominators. Rationals are canonicalized such that the\n# denominator and the numerator have no common factors, and that the\n# denominator is positive. Zero has the unique representation 0/1.\n#\n# NOTE: To use `BigRational`, you must explicitly import it with `require \"big\"`\n#\n# ```\n# require \"big\"\n#\n# r = BigRational.new(7.to_big_i, 3.to_big_i)\n# r.to_s # => \"7/3\"\n#\n# r = BigRational.new(3, -9)\n# r.to_s # => \"-1/3\"\n# ```\n#\n# It is implemented under the hood with [GMP](https://gmplib.org/).\nstruct BigRational < Number\n  include Comparable(BigRational)\n  include Comparable(Int)\n  include Comparable(Float)\n\n  # Creates a new `BigRational`.\n  #\n  # If *denominator* is 0, this will raise an exception.\n  def initialize(numerator : Int, denominator : Int)\n    check_division_by_zero denominator\n\n    numerator = BigInt.new(numerator) unless numerator.is_a?(BigInt)\n    denominator = BigInt.new(denominator) unless denominator.is_a?(BigInt)\n\n    LibGMP.mpq_init(out @mpq)\n    LibGMP.mpq_set_num(mpq, numerator.to_unsafe)\n    LibGMP.mpq_set_den(mpq, denominator.to_unsafe)\n    LibGMP.mpq_canonicalize(mpq)\n  end\n\n  # Creates a new `BigRational` with *num* as the numerator and 1 for denominator.\n  def initialize(num : Int)\n    initialize(num, 1)\n  end\n\n  # Creates an exact representation of float as rational.\n  #\n  # Raises `ArgumentError` if *num* is not finite.\n  def self.new(num : Float::Primitive) : self\n    raise ArgumentError.new \"Can only construct from a finite number\" unless num.finite?\n    new { |mpq| LibGMP.mpq_set_d(mpq, num) }\n  end\n\n  # Creates an exact representation of float as rational.\n  def self.new(num : BigFloat) : self\n    new { |mpq| LibGMP.mpq_set_f(mpq, num) }\n  end\n\n  # Creates a `BigRational` from the given *num*.\n  def self.new(num : BigRational) : self\n    num\n  end\n\n  # :ditto:\n  def self.new(num : BigDecimal) : self\n    num.to_big_r\n  end\n\n  # :nodoc:\n  def initialize(@mpq : LibGMP::MPQ)\n  end\n\n  # :nodoc:\n  def self.new(&)\n    LibGMP.mpq_init(out mpq)\n    yield pointerof(mpq)\n    new(mpq)\n  end\n\n  def numerator : BigInt\n    # Returns `LibGMP.mpq_numref(self)`, whose C macro expansion effectively\n    # produces a raw member access. This is only as safe as copying `BigInt`s by\n    # value, as both involve copying `LibGMP::MPZ` around which has reference\n    # semantics, and `BigInt`s cannot be safely mutated in-place this way; see\n    # #9825 for details. Ditto for `#denominator`.\n    BigInt.new(@mpq._mp_num)\n  end\n\n  def denominator : BigInt\n    BigInt.new(@mpq._mp_den)\n  end\n\n  def <=>(other : BigRational) : Int32\n    LibGMP.mpq_cmp(mpq, other)\n  end\n\n  def <=>(other : Float::Primitive) : Int32?\n    self <=> BigRational.new(other) unless other.nan?\n  end\n\n  def <=>(other : BigFloat) : Int32\n    self <=> other.to_big_r\n  end\n\n  def <=>(other : Int) : Int32\n    Int.primitive_si_ui_check(other) do |si, ui, big_i|\n      {\n        si:    LibGMP.mpq_cmp_si(self, {{ si }}, 1),\n        ui:    LibGMP.mpq_cmp_ui(self, {{ ui }}, 1),\n        big_i: self <=> {{ big_i }},\n      }\n    end\n  end\n\n  def <=>(other : BigInt) : Int32\n    LibGMP.mpq_cmp_z(self, other)\n  end\n\n  def ==(other : BigRational) : Bool\n    LibGMP.mpq_equal(self, other) != 0\n  end\n\n  def +(other : BigRational) : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_add(mpq, self, other) }\n  end\n\n  def +(other : Int) : BigRational\n    self + other.to_big_r\n  end\n\n  def -(other : BigRational) : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_sub(mpq, self, other) }\n  end\n\n  def -(other : Int) : BigRational\n    self - other.to_big_r\n  end\n\n  def *(other : BigRational) : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_mul(mpq, self, other) }\n  end\n\n  def *(other : Int) : BigRational\n    self * other.to_big_r\n  end\n\n  def /(other : BigRational) : BigRational\n    check_division_by_zero other\n    BigRational.new { |mpq| LibGMP.mpq_div(mpq, self, other) }\n  end\n\n  Number.expand_div [BigInt, BigFloat, BigDecimal], BigRational\n\n  def //(other : BigRational) : BigRational\n    check_division_by_zero other\n    BigRational.new((numerator * other.denominator) // (denominator * other.numerator))\n  end\n\n  def //(other : Int) : BigRational\n    check_division_by_zero other\n    BigRational.new(numerator // (denominator * other))\n  end\n\n  def %(other : BigRational) : BigRational\n    check_division_by_zero other\n    BigRational.new(\n      (numerator * other.denominator) % (denominator * other.numerator),\n      denominator * other.denominator,\n    )\n  end\n\n  def %(other : Int) : BigRational\n    check_division_by_zero other\n    BigRational.new(numerator % (denominator * other), denominator)\n  end\n\n  def tdiv(other : BigRational) : BigRational\n    check_division_by_zero other\n    BigRational.new((numerator * other.denominator).tdiv(denominator * other.numerator))\n  end\n\n  def tdiv(other : Int) : BigRational\n    check_division_by_zero other\n    BigRational.new(numerator.tdiv(denominator * other))\n  end\n\n  def remainder(other : BigRational) : BigRational\n    check_division_by_zero other\n    BigRational.new(\n      (numerator * other.denominator).remainder(denominator * other.numerator),\n      denominator * other.denominator,\n    )\n  end\n\n  def remainder(other : Int) : BigRational\n    check_division_by_zero other\n    BigRational.new(numerator.remainder(denominator * other), denominator)\n  end\n\n  def ceil : BigRational\n    BigRational.new(-(-numerator // denominator))\n  end\n\n  def floor : BigRational\n    BigRational.new(numerator // denominator)\n  end\n\n  def trunc : BigRational\n    BigRational.new(numerator.tdiv(denominator))\n  end\n\n  def round_away : BigRational\n    rem2 = numerator.remainder(denominator).abs * 2\n    x = BigRational.new(numerator.tdiv(denominator))\n    x += sign if rem2 >= denominator\n    x\n  end\n\n  def round_even : BigRational\n    rem2 = numerator.remainder(denominator).abs * 2\n    x = BigRational.new(numerator.tdiv(denominator))\n    x += sign if rem2 > denominator || (rem2 == denominator && x.numerator.odd?)\n    x\n  end\n\n  # :inherit:\n  def integer? : Bool\n    # since all `BigRational`s are canonicalized, the denominator must be\n    # positive and coprime with the numerator\n    denominator == 1\n  end\n\n  # Divides the rational by (2 ** *other*)\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigRational.new(2, 3) >> 2 # => 1/6\n  # ```\n  def >>(other : Int) : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_div_2exp(mpq, self, other) }\n  end\n\n  # Multiplies the rational by (2 ** *other*)\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigRational.new(2, 3) << 2 # => 8/3\n  # ```\n  def <<(other : Int) : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_mul_2exp(mpq, self, other) }\n  end\n\n  def - : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_neg(mpq, self) }\n  end\n\n  # Raises the rational to the *other*th power\n  #\n  # This will raise `DivisionByZeroError` if rational is 0 and *other* is negative.\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigRational.new(2, 3) ** 2  # => 4/9\n  # BigRational.new(2, 3) ** -1 # => 3/2\n  # ```\n  def **(other : Int) : BigRational\n    if other < 0\n      return (self ** other.abs).inv\n    end\n    BigRational.new(numerator ** other, denominator ** other)\n  end\n\n  # Returns a new `BigRational` as 1/r.\n  #\n  # This will raise an exception if rational is 0.\n  def inv : BigRational\n    check_division_by_zero self\n    BigRational.new { |mpq| LibGMP.mpq_inv(mpq, self) }\n  end\n\n  def abs : BigRational\n    BigRational.new { |mpq| LibGMP.mpq_abs(mpq, self) }\n  end\n\n  # Returns the `Float64` representing this rational.\n  def to_f : Float64\n    to_f64\n  end\n\n  def to_f32 : Float32\n    to_f64.to_f32\n  end\n\n  def to_f64 : Float64\n    LibGMP.mpq_get_d(mpq)\n  end\n\n  def to_f32! : Float32\n    to_f64.to_f32!\n  end\n\n  def to_f64! : Float64\n    to_f64\n  end\n\n  def to_f! : Float64\n    to_f64!\n  end\n\n  def to_i : Int32\n    to_i32\n  end\n\n  delegate to_i8, to_i16, to_i32, to_i64, to_u8, to_u16, to_u32, to_u64, to: to_f64\n\n  # Returns `self`.\n  #\n  # ```\n  # require \"big\"\n  #\n  # BigRational.new(4, 5).to_big_r # => 4/5\n  # ```\n  def to_big_r : BigRational\n    self\n  end\n\n  def to_big_f : BigFloat\n    BigFloat.new { |mpf| LibGMP.mpf_set_q(mpf, mpq) }\n  end\n\n  def to_big_i : BigInt\n    BigInt.new { |mpz| LibGMP.set_q(mpz, mpq) }\n  end\n\n  # Returns the string representing this rational.\n  #\n  # Optionally takes a radix base (2 through 36).\n  #\n  # ```\n  # require \"big\"\n  #\n  # r = BigRational.new(8243243, 562828882)\n  # r.to_s     # => \"8243243/562828882\"\n  # r.to_s(16) # => \"7dc82b/218c1652\"\n  # r.to_s(36) # => \"4woiz/9b3djm\"\n  # ```\n  def to_s(base : Int = 10) : String\n    String.new(to_cstr(base))\n  end\n\n  def to_s(io : IO, base : Int = 10) : Nil\n    str = to_cstr(base)\n    io.write_string Slice.new(str, LibC.strlen(str))\n  end\n\n  def inspect : String\n    to_s\n  end\n\n  def inspect(io : IO) : Nil\n    to_s io\n  end\n\n  # :inherit:\n  def format(io : IO, separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : Nil\n    numerator.format(io, separator, delimiter, decimal_places, group: group, only_significant: only_significant)\n    io << '/'\n    denominator.format(io, separator, delimiter, decimal_places, group: group, only_significant: only_significant)\n  end\n\n  def clone : BigRational\n    self\n  end\n\n  private def mpq\n    pointerof(@mpq)\n  end\n\n  def to_unsafe : Pointer(LibGMP::MPQ)\n    mpq\n  end\n\n  private def to_cstr(base = 10)\n    raise \"Invalid base #{base}\" unless 2 <= base <= 36\n    LibGMP.mpq_get_str(nil, base, mpq)\n  end\n\n  private def check_division_by_zero(value)\n    raise DivisionByZeroError.new if value == 0\n  end\nend\n\nstruct Int\n  include Comparable(BigRational)\n\n  # Returns a `BigRational` representing this integer.\n  # ```\n  # require \"big\"\n  #\n  # 123.to_big_r\n  # ```\n  def to_big_r : BigRational\n    BigRational.new(self, 1)\n  end\n\n  def <=>(other : BigRational) : Int32\n    -(other <=> self)\n  end\n\n  def +(other : BigRational) : BigRational\n    other + self\n  end\n\n  def -(other : BigRational) : BigRational\n    self.to_big_r - other\n  end\n\n  def /(other : BigRational)\n    self.to_big_r / other\n  end\n\n  def *(other : BigRational) : BigRational\n    other * self\n  end\nend\n\nstruct Float\n  include Comparable(BigRational)\n\n  # Returns a `BigRational` representing this float.\n  # ```\n  # require \"big\"\n  #\n  # 123.0.to_big_r\n  # ```\n  def to_big_r : BigRational\n    BigRational.new(self)\n  end\n\n  def <=>(other : BigRational)\n    cmp = other <=> self\n    -cmp if cmp\n  end\nend\n\nstruct BigFloat\n  def <=>(other : BigRational)\n    -(other <=> self)\n  end\nend\n\nmodule Math\n  # Calculates the square root of *value*.\n  #\n  # ```\n  # require \"big\"\n  #\n  # Math.sqrt(1_000_000_000_000.to_big_r * 1_000_000_000_000.to_big_r) # => 1000000000000.0\n  # ```\n  def sqrt(value : BigRational) : BigFloat\n    sqrt(value.to_big_f)\n  end\nend\n\n# :nodoc:\nstruct Crystal::Hasher\n  def self.reduce_num(value : BigRational) : UInt64\n    inverse = BigInt.new do |mpz|\n      if LibGMP.invert(mpz, value.denominator, HASH_MODULUS_INT_P) == 0\n        # inverse doesn't exist, i.e. denominator is a multiple of HASH_MODULUS\n        return value >= 0 ? HASH_INF_PLUS : HASH_INF_MINUS\n      end\n    end\n    UInt64.mulmod(reduce_num(value.numerator.abs), inverse.to_u64!, HASH_MODULUS) &* value.sign\n  end\nend\n"
  },
  {
    "path": "src/big/json.cr",
    "content": "require \"json\"\nrequire \"big\"\n\nclass JSON::Builder\n  # Writes a big decimal.\n  def number(number : BigDecimal) : Nil\n    scalar do\n      @io << number\n    end\n  end\nend\n\nstruct BigInt\n  def self.new(pull : JSON::PullParser) : self\n    case pull.kind\n    when .int?\n      value = pull.raw_value\n      pull.read_next\n    else\n      value = pull.read_string\n    end\n    new(value)\n  end\n\n  def self.from_json_object_key?(key : String) : BigInt?\n    new(key)\n  rescue ArgumentError\n    nil\n  end\n\n  def to_json_object_key : String\n    to_s\n  end\n\n  def to_json(json : JSON::Builder) : Nil\n    json.number(self)\n  end\nend\n\nstruct BigFloat\n  def self.new(pull : JSON::PullParser) : self\n    case pull.kind\n    when .int?, .float?\n      value = pull.raw_value\n      pull.read_next\n    else\n      value = pull.read_string\n    end\n    new(value)\n  end\n\n  def self.from_json_object_key?(key : String) : BigFloat?\n    new(key)\n  rescue ArgumentError\n    nil\n  end\n\n  def to_json_object_key\n    to_s\n  end\n\n  def to_json(json : JSON::Builder) : Nil\n    json.number(self)\n  end\nend\n\nstruct BigDecimal\n  def self.new(pull : JSON::PullParser) : self\n    case pull.kind\n    when .int?, .float?\n      value = pull.raw_value\n      pull.read_next\n    else\n      value = pull.read_string\n    end\n    new(value)\n  end\n\n  def self.from_json_object_key?(key : String) : BigDecimal?\n    new(key)\n  rescue InvalidBigDecimalException\n    nil\n  end\n\n  def to_json_object_key\n    to_s\n  end\n\n  def to_json(json : JSON::Builder) : Nil\n    json.number(self)\n  end\nend\n"
  },
  {
    "path": "src/big/lib_gmp.cr",
    "content": "# Supported library versions:\n#\n# * libgmp\n# * libmpir\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#big-numbers\n{% if flag?(:win32) && !flag?(:gnu) %}\n  @[Link(\"mpir\")]\n  {% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n    @[Link(dll: \"mpir.dll\")]\n  {% end %}\n{% else %}\n  @[Link(\"gmp\")]\n{% end %}\nlib LibGMP\n  alias Int = LibC::Int\n  alias Long = LibC::Long\n  alias ULong = LibC::ULong\n\n  # MPIR uses its own `mpir_si` and `mpir_ui` typedefs in places where GMP uses\n  # the LibC types, when the function name has `si` or `ui`; we follow this\n  # distinction\n  {% if flag?(:win32) && !flag?(:gnu) && flag?(:bits64) %}\n    alias SI = LibC::LongLong\n    alias UI = LibC::ULongLong\n  {% else %}\n    alias SI = LibC::Long\n    alias UI = LibC::ULong\n  {% end %}\n\n  alias SizeT = LibC::SizeT\n  alias Double = LibC::Double\n  alias BitcntT = UI\n\n  alias MpExp = LibC::Long\n\n  {% if flag?(:win32) && !flag?(:gnu) %}\n    alias MpSize = LibC::LongLong\n  {% else %}\n    alias MpSize = LibC::Long\n  {% end %}\n\n  # NOTE: this assumes GMP is configured by build time to define\n  # `_LONG_LONG_LIMB=1` on Windows\n  {% if flag?(:win32) %}\n    alias MpLimb = LibC::ULongLong\n  {% else %}\n    alias MpLimb = LibC::ULong\n  {% end %}\n\n  struct MPZ\n    _mp_alloc : Int\n    _mp_size : Int\n    _mp_d : MpLimb*\n  end\n\n  # # Initialization\n\n  fun init = __gmpz_init(x : MPZ*)\n  fun init2 = __gmpz_init2(x : MPZ*, bits : BitcntT)\n  fun init_set_ui = __gmpz_init_set_ui(rop : MPZ*, op : UI)\n  fun init_set_si = __gmpz_init_set_si(rop : MPZ*, op : SI)\n  fun init_set_d = __gmpz_init_set_d(rop : MPZ*, op : Double)\n  fun init_set_str = __gmpz_init_set_str(rop : MPZ*, str : UInt8*, base : Int) : Int\n\n  # # I/O\n\n  fun set = __gmpz_set(rop : MPZ*, op : MPZ*)\n  fun set_ui = __gmpz_set_ui(rop : MPZ*, op : UI)\n  fun set_si = __gmpz_set_si(rop : MPZ*, op : SI)\n  fun set_d = __gmpz_set_d(rop : MPZ*, op : Double)\n  fun set_q = __gmpz_set_q(rop : MPZ*, op : MPQ*)\n  fun set_f = __gmpz_set_f(rop : MPZ*, op : MPF*)\n  fun set_str = __gmpz_set_str(rop : MPZ*, str : UInt8*, base : Int) : Int\n  fun get_str = __gmpz_get_str(str : UInt8*, base : Int, op : MPZ*) : UInt8*\n  fun get_si = __gmpz_get_si(op : MPZ*) : SI\n  fun get_ui = __gmpz_get_ui(op : MPZ*) : UI\n  fun get_d = __gmpz_get_d(op : MPZ*) : Double\n  fun get_d_2exp = __gmpz_get_d_2exp(exp : Long*, op : MPZ*) : Double\n\n  # # Arithmetic\n\n  fun add = __gmpz_add(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun add_ui = __gmpz_add_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n\n  fun sub = __gmpz_sub(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun sub_ui = __gmpz_sub_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n  fun ui_sub = __gmpz_ui_sub(rop : MPZ*, op1 : UI, op2 : MPZ*)\n\n  fun mul = __gmpz_mul(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun mul_si = __gmpz_mul_si(rop : MPZ*, op1 : MPZ*, op2 : SI)\n  fun mul_ui = __gmpz_mul_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n\n  fun addmul = __gmpz_addmul(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun addmul_ui = __gmpz_addmul_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n\n  fun fdiv_q = __gmpz_fdiv_q(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun fdiv_q_ui = __gmpz_fdiv_q_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n\n  fun tdiv_q = __gmpz_tdiv_q(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun tdiv_q_ui = __gmpz_tdiv_q_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n\n  fun fdiv_r = __gmpz_fdiv_r(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun fdiv_r_ui = __gmpz_fdiv_r_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n\n  fun fdiv_qr = __gmpz_fdiv_qr(q : MPZ*, r : MPZ*, n : MPZ*, d : MPZ*)\n  fun fdiv_qr_ui = __gmpz_fdiv_qr_ui(q : MPZ*, r : MPZ*, n : MPZ*, d : UI) : UI\n\n  fun tdiv_r = __gmpz_tdiv_r(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun tdiv_r_ui = __gmpz_tdiv_r_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n  fun tdiv_ui = __gmpz_tdiv_ui(op1 : MPZ*, op2 : UI) : UI\n\n  fun tdiv_qr = __gmpz_tdiv_qr(q : MPZ*, r : MPZ*, n : MPZ*, d : MPZ*)\n  fun tdiv_qr_ui = __gmpz_tdiv_qr_ui(q : MPZ*, r : MPZ*, n : MPZ*, d : UI) : UI\n\n  fun divisible_p = __gmpz_divisible_p(n : MPZ*, d : MPZ*) : Int\n  fun divisible_ui_p = __gmpz_divisible_ui_p(n : MPZ*, d : UI) : Int\n\n  fun neg = __gmpz_neg(rop : MPZ*, op : MPZ*)\n  fun abs = __gmpz_abs(rop : MPZ*, op : MPZ*)\n\n  fun pow_ui = __gmpz_pow_ui(rop : MPZ*, base : MPZ*, exp : UI)\n  fun fac_ui = __gmpz_fac_ui(rop : MPZ*, n : UI)\n\n  fun sqrt = __gmpz_sqrt(rop : MPZ*, op : MPZ*)\n\n  # # Bitwise operations\n\n  fun and = __gmpz_and(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun ior = __gmpz_ior(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun xor = __gmpz_xor(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun com = __gmpz_com(rop : MPZ*, op : MPZ*)\n\n  fun tstbit = __gmpz_tstbit(op : MPZ*, bit_index : BitcntT) : Int\n\n  fun fdiv_q_2exp = __gmpz_fdiv_q_2exp(q : MPZ*, n : MPZ*, b : BitcntT)\n  fun mul_2exp = __gmpz_mul_2exp(rop : MPZ*, op1 : MPZ*, op2 : BitcntT)\n\n  # # Logic\n\n  fun popcount = __gmpz_popcount(op : MPZ*) : BitcntT\n  fun scan0 = __gmpz_scan0(op : MPZ*, starting_bit : BitcntT) : BitcntT\n  fun scan1 = __gmpz_scan1(op : MPZ*, starting_bit : BitcntT) : BitcntT\n  fun sizeinbase = __gmpz_sizeinbase(op : MPZ*, base : Int) : SizeT\n\n  # # Comparison\n\n  fun cmp = __gmpz_cmp(op1 : MPZ*, op2 : MPZ*) : Int\n  fun cmp_si = __gmpz_cmp_si(op1 : MPZ*, op2 : SI) : Int\n  fun cmp_ui = __gmpz_cmp_ui(op1 : MPZ*, op2 : UI) : Int\n  fun cmp_d = __gmpz_cmp_d(op1 : MPZ*, op2 : Double) : Int\n\n  # # Number Theoretic Functions\n\n  fun gcd = __gmpz_gcd(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun gcd_ui = __gmpz_gcd_ui(rop : MPZ*, op1 : MPZ*, op2 : UI) : UI\n  fun lcm = __gmpz_lcm(rop : MPZ*, op1 : MPZ*, op2 : MPZ*)\n  fun lcm_ui = __gmpz_lcm_ui(rop : MPZ*, op1 : MPZ*, op2 : UI)\n  fun invert = __gmpz_invert(rop : MPZ*, op1 : MPZ*, op2 : MPZ*) : Int\n  fun remove = __gmpz_remove(rop : MPZ*, op : MPZ*, f : MPZ*) : BitcntT\n\n  # # Miscellaneous Functions\n\n  {% if flag?(:win32) && !flag?(:gnu) %}\n    fun fits_ui_p = __gmpz_fits_ui_p(op : MPZ*) : Int\n    fun fits_si_p = __gmpz_fits_si_p(op : MPZ*) : Int\n  {% else %}\n    fun fits_ulong_p = __gmpz_fits_ulong_p(op : MPZ*) : Int\n    fun fits_slong_p = __gmpz_fits_slong_p(op : MPZ*) : Int\n  {% end %}\n\n  # # Special Functions\n\n  fun size = __gmpz_size(op : MPZ*) : SizeT\n  fun limbs_read = __gmpz_limbs_read(x : MPZ*) : MpLimb*\n  fun limbs_write = __gmpz_limbs_write(x : MPZ*, n : MpSize) : MpLimb*\n  fun limbs_finish = __gmpz_limbs_finish(x : MPZ*, s : MpSize)\n\n  # MPQ\n  struct MPQ\n    _mp_num : MPZ\n    _mp_den : MPZ\n  end\n\n  # # Initialization\n  fun mpq_init = __gmpq_init(x : MPQ*)\n  fun mpq_set_num = __gmpq_set_num(x : MPQ*, num : MPZ*)\n  fun mpq_set_den = __gmpq_set_den(x : MPQ*, den : MPZ*)\n  fun mpq_get_num = __gmpq_get_num(rop : MPZ*, op : MPQ*)\n  fun mpq_get_den = __gmpq_get_den(rop : MPZ*, op : MPQ*)\n  fun mpq_canonicalize = __gmpq_canonicalize(x : MPQ*)\n\n  # # Conversion\n  fun mpq_get_str = __gmpq_get_str(str : UInt8*, base : Int, op : MPQ*) : UInt8*\n  fun mpq_get_d = __gmpq_get_d(op : MPQ*) : Double\n  fun mpq_set_d = __gmpq_set_d(rop : MPQ*, op : Double)\n  fun mpq_set_f = __gmpq_set_f(rop : MPQ*, op : MPF*)\n\n  # # Arithmetic\n  fun mpq_add = __gmpq_add(rop : MPQ*, op1 : MPQ*, op2 : MPQ*)\n  fun mpq_sub = __gmpq_sub(rop : MPQ*, op1 : MPQ*, op2 : MPQ*)\n  fun mpq_mul = __gmpq_mul(rop : MPQ*, op1 : MPQ*, op2 : MPQ*)\n  fun mpq_div = __gmpq_div(rop : MPQ*, op1 : MPQ*, op2 : MPQ*)\n  fun mpq_inv = __gmpq_inv(rop : MPQ*, op1 : MPQ*)\n  fun mpq_neg = __gmpq_neg(rop : MPQ*, op1 : MPQ*)\n  fun mpq_abs = __gmpq_abs(rop : MPQ*, op1 : MPQ*)\n\n  fun mpq_div_2exp = __gmpq_div_2exp(q : MPQ*, n : MPQ*, b : BitcntT)\n  fun mpq_mul_2exp = __gmpq_mul_2exp(rop : MPQ*, op1 : MPQ*, op2 : BitcntT)\n\n  # # Compare\n  fun mpq_cmp = __gmpq_cmp(op1 : MPQ*, op2 : MPQ*) : Int\n  fun mpq_cmp_z = __gmpq_cmp_z(op1 : MPQ*, op2 : MPZ*) : Int\n  fun mpq_cmp_ui = __gmpq_cmp_ui(op1 : MPQ*, num2 : UI, den2 : UI) : Int\n  fun mpq_cmp_si = __gmpq_cmp_si(op1 : MPQ*, num2 : SI, den2 : SI) : Int\n  fun mpq_equal = __gmpq_equal(op1 : MPQ*, op2 : MPQ*) : Int\n\n  # MPF\n  struct MPF\n    _mp_prec : Int\n    _mp_size : Int\n    _mp_exp : MpExp\n    _mp_d : MpLimb*\n  end\n\n  # # Initialization\n  fun mpf_init = __gmpf_init(x : MPF*)\n  fun mpf_init2 = __gmpf_init2(x : MPF*, prec : BitcntT)\n  fun mpf_init_set_d = __gmpf_init_set_d(rop : MPF*, op : Double)\n  fun mpf_init_set_str = __gmpf_init_set_str(rop : MPF*, str : UInt8*, base : Int) : Int\n  fun mpf_init_set_ui = __gmpf_init_set_ui(rop : MPF*, op : UI)\n  fun mpf_init_set_si = __gmpf_init_set_si(rop : MPF*, op : SI)\n\n  # # Precision\n  fun mpf_set_default_prec = __gmpf_set_default_prec(prec : BitcntT)\n  fun mpf_get_default_prec = __gmpf_get_default_prec : BitcntT\n  fun mpf_get_prec = __gmpf_get_prec(op : MPF*) : BitcntT\n\n  # # Conversion\n  fun mpf_get_str = __gmpf_get_str(str : UInt8*, expptr : MpExp*, base : Int, n_digits : LibC::SizeT, op : MPF*) : UInt8*\n  fun mpf_get_d = __gmpf_get_d(op : MPF*) : Double\n  fun mpf_set_d = __gmpf_set_d(rop : MPF*, op : Double)\n  fun mpf_set = __gmpf_set(rop : MPF*, op : MPF*)\n  fun mpf_set_z = __gmpf_set_z(rop : MPF*, op : MPZ*)\n  fun mpf_set_q = __gmpf_set_q(rop : MPF*, op : MPQ*)\n  fun mpf_get_si = __gmpf_get_si(op : MPF*) : SI\n  fun mpf_get_ui = __gmpf_get_ui(op : MPF*) : UI\n  fun mpf_get_d_2exp = __gmpf_get_d_2exp(exp : Long*, op : MPF*) : Double\n\n  # # Arithmetic\n  fun mpf_add = __gmpf_add(rop : MPF*, op1 : MPF*, op2 : MPF*)\n  fun mpf_add_ui = __gmpf_add_ui(rop : MPF*, op1 : MPF*, op2 : UI)\n  fun mpf_sub = __gmpf_sub(rop : MPF*, op1 : MPF*, op2 : MPF*)\n  fun mpf_sub_ui = __gmpf_sub_ui(rop : MPF*, op1 : MPF*, op2 : UI)\n  fun mpf_mul = __gmpf_mul(rop : MPF*, op1 : MPF*, op2 : MPF*)\n  fun mpf_mul_ui = __gmpf_mul_ui(rop : MPF*, op1 : MPF*, op2 : UI)\n  fun mpf_div = __gmpf_div(rop : MPF*, op1 : MPF*, op2 : MPF*)\n  fun mpf_div_ui = __gmpf_div_ui(rop : MPF*, op1 : MPF*, op2 : UI)\n  fun mpf_ui_div = __gmpf_ui_div(rop : MPF*, op1 : UI, op2 : MPF*)\n  fun mpf_neg = __gmpf_neg(rop : MPF*, op : MPF*)\n  fun mpf_abs = __gmpf_abs(rop : MPF*, op : MPF*)\n  fun mpf_sqrt = __gmpf_sqrt(rop : MPF*, op : MPF*)\n  fun mpf_pow_ui = __gmpf_pow_ui(rop : MPF*, op1 : MPF*, op2 : SI)\n  fun mpf_mul_2exp = __gmpf_mul_2exp(rop : MPF*, op1 : MPF*, op2 : BitcntT)\n  fun mpf_div_2exp = __gmpf_div_2exp(rop : MPF*, op1 : MPF*, op2 : BitcntT)\n\n  # # Comparison\n  fun mpf_cmp = __gmpf_cmp(op1 : MPF*, op2 : MPF*) : Int\n  fun mpf_cmp_d = __gmpf_cmp_d(op1 : MPF*, op2 : Double) : Int\n  fun mpf_cmp_ui = __gmpf_cmp_ui(op1 : MPF*, op2 : UI) : Int\n  fun mpf_cmp_si = __gmpf_cmp_si(op1 : MPF*, op2 : SI) : Int\n  fun mpf_cmp_z = __gmpf_cmp_z(op1 : MPF*, op2 : MPZ*) : Int\n\n  # # Miscellaneous\n  fun mpf_ceil = __gmpf_ceil(rop : MPF*, op : MPF*)\n  fun mpf_floor = __gmpf_floor(rop : MPF*, op : MPF*)\n  fun mpf_trunc = __gmpf_trunc(rop : MPF*, op : MPF*)\n  fun mpf_integer_p = __gmpf_integer_p(op : MPF*) : Int\n\n  # # Memory\n\n  fun set_memory_functions = __gmp_set_memory_functions(malloc : SizeT -> Void*, realloc : Void*, SizeT, SizeT -> Void*, free : Void*, SizeT ->)\nend\n\nLibGMP.set_memory_functions(\n  ->(size) { GC.malloc(size) },\n  ->(ptr, old_size, new_size) { GC.realloc(ptr, new_size) },\n  ->(ptr, size) { GC.free(ptr) }\n)\n"
  },
  {
    "path": "src/big/number.cr",
    "content": "struct BigInt\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], BigFloat\n  Number.expand_div [Float32, Float64], BigFloat\nend\n\nstruct BigFloat\n  def fdiv(other : Number::Primitive) : self\n    self.class.new(self / other)\n  end\n\n  Number.expand_div [Float32, Float64], BigFloat\nend\n\nstruct BigDecimal\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], BigDecimal\n  Number.expand_div [Float32, Float64], BigDecimal\nend\n\nstruct BigRational\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], BigRational\n  Number.expand_div [Float32, Float64], BigRational\nend\n\nstruct Int\n  # :nodoc:\n  # Yields 3 expressions: `Call`s nodes that convert *var* into a `LibGMP::SI`,\n  # a `LibGMP::UI`, and a `BigInt` respectively. These expressions are not\n  # evaluated unless they are interpolated in *block*.\n  #\n  # *block* should return a named tuple: the value for `:si` is returned by the\n  # macro if *var* fits into a `LibGMP::SI`, the value for `:ui` returned if\n  # *var* fits into a `LibGMP::UI`, and the value for `:big_i` otherwise.\n  macro primitive_si_ui_check(var, &block)\n    {%\n      exps = yield(\n        \"::LibGMP::SI.new!(#{var.id})\".id,\n        \"::LibGMP::UI.new!(#{var.id})\".id,\n        \"::BigInt.new(#{var.id})\".id,\n      )\n    %}\n    if ::LibGMP::SI::MIN <= {{ var }} <= ::LibGMP::UI::MAX\n      if {{ var }} <= ::LibGMP::SI::MAX\n        {{ exps[:si] }}\n      else\n        {{ exps[:ui] }}\n      end\n    else\n      {{ exps[:big_i] }}\n    end\n  end\n\n  # :nodoc:\n  # Yields 3 expressions: `Call`s nodes that convert *var* into a `LibGMP::UI`,\n  # the negative of *var* into a `LibGMP::UI`, and *var* into a `BigInt`,\n  # respectively. These expressions are not evaluated unless they are\n  # interpolated in *block*.\n  #\n  # *block* should return a named tuple: the value for `:ui` is returned by the\n  # macro if *var* fits into a `LibGMP::UI`, the value for `:neg_ui` returned if\n  # the negative of *var* fits into a `LibGMP::UI`, and the value for `:big_i`\n  # otherwise.\n  macro primitive_ui_check(var, &block)\n    {%\n      exps = yield(\n        \"::LibGMP::UI.new!(#{var.id})\".id,\n        \"::LibGMP::UI.new!((#{var.id}).abs_unsigned)\".id,\n        \"::BigInt.new(#{var.id})\".id,\n      )\n    %}\n    if ::LibGMP::UI::MIN <= {{ var }} <= ::LibGMP::UI::MAX\n      {{ exps[:ui] }}\n    elsif {{ var }}.responds_to?(:abs_unsigned) && {{ var }}.abs_unsigned <= ::LibGMP::UI::MAX\n      {{ exps[:neg_ui] }}\n    else\n      {{ exps[:big_i] }}\n    end\n  end\nend\n\nstruct Int8\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct Int16\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct Int32\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct Int64\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct Int128\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct UInt8\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct UInt16\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct UInt32\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct UInt64\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct UInt128\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct Float\n  def fdiv(other : BigInt | BigFloat | BigDecimal | BigRational) : self\n    self.class.new(self / other)\n  end\nend\n\nstruct Float32\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigFloat], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n\nstruct Float64\n  Number.expand_div [BigInt], BigFloat\n  Number.expand_div [BigFloat], BigFloat\n  Number.expand_div [BigDecimal], BigDecimal\n  Number.expand_div [BigRational], BigRational\nend\n"
  },
  {
    "path": "src/big/yaml.cr",
    "content": "require \"yaml\"\nrequire \"big\"\n\ndef BigInt.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : self\n  unless node.is_a?(YAML::Nodes::Scalar)\n    node.raise \"Expected scalar, not #{node.class}\"\n  end\n\n  BigInt.new(node.value)\nend\n\ndef BigFloat.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : self\n  unless node.is_a?(YAML::Nodes::Scalar)\n    node.raise \"Expected scalar, not #{node.class}\"\n  end\n\n  BigFloat.new(node.value)\nend\n\ndef BigDecimal.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : self\n  unless node.is_a?(YAML::Nodes::Scalar)\n    node.raise \"Expected scalar, not #{node.class}\"\n  end\n\n  BigDecimal.new(node.value)\nend\n"
  },
  {
    "path": "src/big.cr",
    "content": "# :nodoc: Forward declarations\nstruct BigInt < Int\nend\n\nstruct BigFloat < Float\nend\n\nstruct BigRational < Number\nend\n\nstruct BigDecimal < Number\nend\n\nrequire \"./big/lib_gmp\"\nrequire \"./big/big_int\"\nrequire \"./big/big_float\"\nrequire \"./big/big_rational\"\nrequire \"./big/big_decimal\"\nrequire \"./big/number\"\n"
  },
  {
    "path": "src/bit_array.cr",
    "content": "# `BitArray` is an array data structure that compactly stores bits.\n#\n# Bits externally represented as `Bool`s are stored internally as\n# `UInt32`s. The total number of bits stored is set at creation and is\n# immutable.\n#\n# NOTE: To use `BitArray`, you must explicitly import it with `require \"bit_array\"`\n#\n# ### Example\n#\n# ```\n# require \"bit_array\"\n#\n# ba = BitArray.new(12) # => \"BitArray[000000000000]\"\n# ba[2]                 # => false\n# 0.upto(5) { |i| ba[i * 2] = true }\n# ba    # => \"BitArray[101010101010]\"\n# ba[2] # => true\n# ```\nstruct BitArray\n  include Indexable::Mutable(Bool)\n\n  # The number of bits the BitArray stores\n  getter size : Int32\n\n  # Creates a new `BitArray` of *size* bits.\n  #\n  # *initial* optionally sets the starting value, `true` or `false`, for all bits\n  # in the array.\n  def initialize(size : Int, initial : Bool = false)\n    raise ArgumentError.new(\"Negative bit array size: #{size}\") if size < 0\n    @size = size.to_i\n    value = initial ? UInt32::MAX : UInt32::MIN\n    @bits = Pointer(UInt32).malloc(malloc_size, value)\n    clear_unused_bits if initial\n  end\n\n  # Creates a new `BitArray` of *size* bits and invokes the given block once\n  # for each index of `self`, setting the bit at that index to `true` if the\n  # block is truthy.\n  #\n  # ```\n  # BitArray.new(5) { |i| i >= 3 }     # => BitArray[00011]\n  # BitArray.new(6) { |i| i if i < 2 } # => BitArray[110000]\n  # ```\n  def self.new(size : Int, & : Int32 -> _)\n    arr = new(size)\n    size.to_i.times do |i|\n      arr.unsafe_put(i, true) if yield i\n    end\n    arr\n  end\n\n  def ==(other : BitArray)\n    return false if size != other.size\n    # NOTE: If BitArray implements resizing, there may be more than 1 binary\n    # representation and their hashes for equivalent BitArrays after a downsize as the\n    # discarded bits may not have been zeroed.\n    LibC.memcmp(@bits, other.@bits, bytesize) == 0\n  end\n\n  def unsafe_fetch(index : Int) : Bool\n    bit_index, sub_index = index.divmod(32)\n    (@bits[bit_index] & (1 << sub_index)) > 0\n  end\n\n  def unsafe_put(index : Int, value : Bool)\n    bit_index, sub_index = index.divmod(32)\n    if value\n      @bits[bit_index] |= 1 << sub_index\n    else\n      @bits[bit_index] &= ~(1 << sub_index)\n    end\n  end\n\n  # :inherit:\n  def []=(index : Int, value : Bool) : Bool\n    bit_index, sub_index = bit_index_and_sub_index(index)\n    if value\n      @bits[bit_index] |= 1 << sub_index\n    else\n      @bits[bit_index] &= ~(1 << sub_index)\n    end\n    value\n  end\n\n  # Returns all elements that are within the given range.\n  #\n  # Negative indices count backward from the end of the array (-1 is the last\n  # element). Additionally, an empty array is returned when the starting index\n  # for an element range is at the end of the array.\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba[0] = true; ba[2] = true; ba[4] = true\n  # ba # => BitArray[10101]\n  #\n  # ba[1..3]    # => BitArray[010]\n  # ba[4..7]    # => BitArray[1]\n  # ba[6..10]   # raise IndexError\n  # ba[5..10]   # => BitArray[]\n  # ba[-2...-1] # => BitArray[0]\n  # ```\n  def [](range : Range) : BitArray\n    self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new]\n  end\n\n  # Returns count or less (if there aren't enough) elements starting at the\n  # given start index.\n  #\n  # Negative indices count backward from the end of the array (-1 is the last\n  # element). Additionally, an empty array is returned when the starting index\n  # for an element range is at the end of the array.\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba[0] = true; ba[2] = true; ba[4] = true\n  # ba # => BitArray[10101]\n  #\n  # ba[-3, 3] # => BitArray[101]\n  # ba[6, 1]  # raise indexError\n  # ba[1, 2]  # => BitArray[01]\n  # ba[5, 1]  # => BitArray[]\n  # ```\n  def [](start : Int, count : Int) : BitArray\n    start, count = normalize_start_and_count(start, count)\n\n    if count == 0\n      return BitArray.new(0)\n    end\n\n    if size <= 32\n      # Result *and* original fit in a single int32, we can use only bitshifts\n      bits = @bits[0]\n\n      bits >>= start\n      bits &= ~(UInt32::MAX << count)\n\n      BitArray.new(count).tap { |ba| ba.@bits[0] = bits }\n    elsif size <= 64\n      # Original fits in int64, we can use bitshifts\n      bits = @bits.as(UInt64*)[0]\n\n      bits >>= start\n      bits &= ~(UInt64::MAX << count)\n\n      if count <= 32\n        BitArray.new(count).tap { |ba| ba.@bits[0] = bits.to_u32! }\n      else\n        BitArray.new(count).tap { |ba| ba.@bits.as(UInt64*)[0] = bits }\n      end\n    else\n      ba = BitArray.new(count)\n      start_bit_index, start_sub_index = start.divmod(32)\n      end_bit_index = (start + count) // 32\n\n      i = 0\n      bits = @bits[start_bit_index]\n      while start_bit_index + i <= end_bit_index\n        low_bits = bits\n        low_bits >>= start_sub_index\n\n        bits = @bits[start_bit_index + i + 1]\n\n        high_bits = bits\n        high_bits &= ~(UInt32::MAX << start_sub_index)\n        high_bits <<= 32 - start_sub_index\n\n        ba.@bits[i] = low_bits | high_bits\n        i += 1\n      end\n\n      # The last assignment to `bits` might refer to a `UInt32` in the middle of\n      # the buffer, so the last `UInt32` of `ba` might contain unused bits.\n      ba.clear_unused_bits\n      ba\n    end\n  end\n\n  # :inherit:\n  def all? : Bool\n    bit_index, sub_index = @size.divmod(32)\n\n    bit_index.times do |i|\n      return false unless @bits[i] == UInt32::MAX\n    end\n\n    return true if sub_index == 0\n    mask = ~(UInt32::MAX << sub_index)\n    @bits[bit_index] & mask == mask\n  end\n\n  # :inherit:\n  def any? : Bool\n    Slice.new(@bits, malloc_size).any? { |bits| bits != 0 }\n  end\n\n  # :inherit:\n  def none? : Bool\n    !any?\n  end\n\n  # Returns `true` if the collection contains *obj*, `false` otherwise.\n  #\n  # ```\n  # ba = BitArray.new(8, true)\n  # ba.includes?(true)  # => true\n  # ba.includes?(false) # => false\n  # ```\n  def includes?(obj : Bool) : Bool\n    obj ? any? : !all?\n  end\n\n  # :inherit:\n  def one? : Bool\n    c = 0\n    malloc_size.times do |i|\n      c += @bits[i].popcount\n      return false if c > 1\n    end\n    c == 1\n  end\n\n  # Returns the index of the first appearance of *obj* in `self`\n  # starting from the given *offset*, or `nil` if the value is not in `self`.\n  #\n  # ```\n  # ba = BitArray.new(16)\n  # ba[5] = ba[11] = true\n  # ba.index(true)             # => 5\n  # ba.index(true, offset: 8)  # => 11\n  # ba.index(true, offset: 12) # => nil\n  # ```\n  def index(obj : Bool, offset : Int = 0) : Int32?\n    offset = check_index_out_of_bounds(offset) { return nil }\n    start_bit_index, start_sub_index = offset.divmod(32)\n    end_bit_index, end_sub_index = (@size - 1).divmod(32)\n\n    if start_bit_index == end_bit_index\n      check_index_in_bits(start_bit_index, start_sub_index, end_sub_index)\n    else\n      check_index_in_bits(start_bit_index, start_sub_index, 31)\n      (start_bit_index + 1..end_bit_index - 1).each do |i|\n        check_index_in_bits(i, 0, 31)\n      end\n      check_index_in_bits(end_bit_index, 0, end_sub_index)\n    end\n  end\n\n  private macro check_index_in_bits(bits_index, from, to)\n    bits = @bits[{{ bits_index }}]\n    bits = ~bits if !obj\n    bits &= uint32_mask({{ from }}, {{ to }})\n    return {{ bits_index }} * 32 + bits.trailing_zeros_count if bits != 0\n  end\n\n  # Returns the index of the last appearance of *obj* in `self`, or\n  # `nil` if *obj* is not in `self`.\n  #\n  # If *offset* is given, the search starts from that index towards the\n  # first elements in `self`.\n  #\n  # ```\n  # ba = BitArray.new(16)\n  # ba[5] = ba[11] = true\n  # ba.rindex(true)            # => 11\n  # ba.rindex(true, offset: 8) # => 5\n  # ba.rindex(true, offset: 4) # => nil\n  # ```\n  def rindex(obj : Bool, offset : Int = size - 1) : Int32?\n    offset = check_index_out_of_bounds(offset) { return nil }\n    start_bit_index, start_sub_index = offset.divmod(32)\n\n    check_rindex_in_bits(start_bit_index, 0, start_sub_index)\n    (start_bit_index - 1).downto(0) do |i|\n      check_rindex_in_bits(i, 0, 31)\n    end\n  end\n\n  private macro check_rindex_in_bits(bits_index, from, to)\n    bits = @bits[{{ bits_index }}]\n    bits = ~bits if !obj\n    bits &= uint32_mask({{ from }}, {{ to }})\n    return {{ bits_index }} * 32 + 31 - bits.leading_zeros_count if bits != 0\n  end\n\n  # Returns the number of times that *item* is present in the bit array.\n  #\n  # ```\n  # ba = BitArray.new(12, true)\n  # ba[3] = false\n  # ba[7] = false\n  # ba.count(true)  # => 10\n  # ba.count(false) # => 2\n  # ```\n  def count(item : Bool) : Int32\n    ones_count = Slice.new(@bits, malloc_size).sum(&.popcount)\n    item ? ones_count : @size - ones_count\n  end\n\n  # :inherit:\n  def tally : Hash(Bool, Int32)\n    tally(Hash(Bool, Int32).new)\n  end\n\n  # :inherit:\n  def tally(hash)\n    ones_count = count(true)\n    if ones_count > 0\n      count = hash.fetch(true) { typeof(hash[true]).zero }\n      hash[true] = count + ones_count\n    end\n    if ones_count < @size\n      count = hash.fetch(false) { typeof(hash[false]).zero }\n      hash[false] = count + @size - ones_count\n    end\n    hash\n  end\n\n  # :inherit:\n  def fill(value : Bool) : self\n    return self if size == 0\n\n    if size <= 64\n      @bits.as(UInt64*).value = value ? ~(UInt64::MAX << size) : 0_u64\n    else\n      to_slice.fill(value ? 0xFF_u8 : 0x00_u8)\n      clear_unused_bits if value\n    end\n\n    self\n  end\n\n  # :inherit:\n  def fill(value : Bool, start : Int, count : Int) : self\n    start, count = normalize_start_and_count(start, count)\n    return self if count <= 0\n    bytes = to_slice\n\n    start_bit_index, start_sub_index = start.divmod(8)\n    end_bit_index, end_sub_index = (start + count - 1).divmod(8)\n\n    if start_bit_index == end_bit_index\n      # same UInt8, don't perform the loop at all\n      mask = uint8_mask(start_sub_index, end_sub_index)\n      set_bits(bytes, value, start_bit_index, mask)\n    else\n      mask = uint8_mask(start_sub_index, 7)\n      set_bits(bytes, value, start_bit_index, mask)\n\n      bytes[start_bit_index + 1..end_bit_index - 1].fill(value ? 0xFF_u8 : 0x00_u8)\n\n      mask = uint8_mask(0, end_sub_index)\n      set_bits(bytes, value, end_bit_index, mask)\n    end\n\n    self\n  end\n\n  @[AlwaysInline]\n  private def set_bits(bytes : Slice(UInt8), value, index, mask)\n    if value\n      bytes[index] |= mask\n    else\n      bytes[index] &= ~mask\n    end\n  end\n\n  # returns (1 << from) | (1 << (from + 1)) | ... | (1 << to)\n  @[AlwaysInline]\n  private def uint8_mask(from, to)\n    (Int8::MIN >> (to - from)).to_u8! >> (7 - to)\n  end\n\n  # Toggles the bit at the given *index*. A `false` bit becomes a `true` bit,\n  # and vice versa.\n  #\n  # Negative indices count backward from the end of the array (-1 is the last\n  # element).\n  #\n  # Raises `IndexError` if *index* is out of range.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba[3] # => false\n  # ba.toggle(3)\n  # ba[3] # => true\n  # ```\n  def toggle(index) : Nil\n    bit_index, sub_index = bit_index_and_sub_index(index)\n    @bits[bit_index] ^= 1 << sub_index\n  end\n\n  # Toggles all bits that are within the given *range*. A `false` bit becomes a\n  # `true` bit, and vice versa.\n  #\n  # Negative indices count backward from the end of the array (-1 is the last\n  # element).\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba.to_s # => \"BitArray[00000]\"\n  # ba.toggle(1..-2)\n  # ba.to_s # => \"BitArray[01110]\"\n  # ```\n  def toggle(range : Range)\n    toggle(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new)\n  end\n\n  # Toggles *count* or less (if there aren't enough) bits starting at the given\n  # *start* index. A `false` bit becomes a `true` bit, and vice versa.\n  #\n  # Negative indices count backward from the end of the array (-1 is the last\n  # element).\n  #\n  # Raises `IndexError` if *index* is out of range.\n  # Raises `ArgumentError` if *count* is a negative number.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba.to_s # => \"BitArray[00000]\"\n  # ba.toggle(1, 3)\n  # ba.to_s # => \"BitArray[01110]\"\n  # ```\n  def toggle(start : Int, count : Int)\n    start, count = normalize_start_and_count(start, count)\n    return if count == 0\n\n    start_bit_index, start_sub_index = start.divmod(32)\n    end_bit_index, end_sub_index = (start + count - 1).divmod(32)\n\n    if start_bit_index == end_bit_index\n      # same UInt32, don't perform the loop at all\n      @bits[start_bit_index] ^= uint32_mask(start_sub_index, end_sub_index)\n    else\n      @bits[start_bit_index] ^= uint32_mask(start_sub_index, 31)\n      (start_bit_index + 1..end_bit_index - 1).each do |i|\n        @bits[i] = ~@bits[i]\n      end\n      @bits[end_bit_index] ^= uint32_mask(0, end_sub_index)\n    end\n  end\n\n  # returns (1 << from) | (1 << (from + 1)) | ... | (1 << to)\n  @[AlwaysInline]\n  private def uint32_mask(from, to)\n    (Int32::MIN >> (to - from)).to_u32! >> (31 - to)\n  end\n\n  # Inverts all bits in the array. Falses become `true` and vice versa.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba[2] = true; ba[3] = true\n  # ba # => BitArray[00110]\n  # ba.invert\n  # ba # => BitArray[11001]\n  # ```\n  def invert : Nil\n    malloc_size.times do |i|\n      @bits[i] = ~@bits[i]\n    end\n    clear_unused_bits\n  end\n\n  # :inherit:\n  def reverse! : self\n    return self if size <= 1\n\n    if size <= 32\n      @bits.value = @bits.value.bit_reverse >> (32 - size)\n    elsif size <= 64\n      more_bits = @bits.as(UInt64*)\n      more_bits.value = more_bits.value.bit_reverse >> (64 - size)\n    else\n      # 3 or more groups of bits\n      offset = (-size) % 32\n      if offset != 0\n        # left-shifting, followed by bit-reversing in each group\n        # simplified bit pattern example using a group size of 8: (offset = 3)\n        #\n        #     hgfedcba ponmlkji 000utsrq\n        #     hgfedcba ponmlkji utsrqpon\n        #     hgfedcba ponmlkji nopqrstu\n        #     hgfedcba mlkjihgf nopqrstu\n        #     hgfedcba fghijklm nopqrstu\n        (malloc_size - 1).downto(1) do |i|\n          # fshl(a, b, count) = (a << count) | (b >> (N - count))\n          @bits[i] = Intrinsics.fshl32(@bits[i], @bits[i - 1], offset).bit_reverse\n        end\n\n        # last group:\n        #\n        #     edcba000 fghijklm nopqrstu\n        #     000abcde fghijklm nopqrstu\n        @bits[0] = (@bits[0] << offset).bit_reverse\n      else\n        # no padding; do only the bit reverses\n        Slice.new(@bits, malloc_size).map! &.bit_reverse\n      end\n\n      # reversing all groups themselves:\n      #\n      #     nopqrstu fghijklm 000abcde\n      Slice.new(@bits, malloc_size).reverse!\n    end\n\n    self\n  end\n\n  # :inherit:\n  def rotate!(n : Int = 1) : self\n    return self if size <= 1\n    n %= size\n    return self if n == 0\n\n    if size % 8 == 0 && n % 8 == 0\n      to_slice.rotate!(n // 8)\n    elsif size <= 32\n      @bits[0] = (@bits[0] >> n) | (@bits[0] << (size - n))\n      clear_unused_bits\n    elsif n <= 32\n      temp = @bits[0]\n      malloc_size = self.malloc_size\n      (malloc_size - 1).times do |i|\n        # fshr(a, b, count) = (b >> count) | (a << (N - count))\n        @bits[i] = Intrinsics.fshr32(@bits[i + 1], @bits[i], n)\n      end\n\n      end_sub_index = (size - 1) % 32 + 1\n      if n <= end_sub_index\n        # n = 3: (bit patterns here are little-endian)\n        #\n        #     ........ ........ ........ .....CBA -> ........ ........ ........ ........\n        #     ........ ........ ........ ........ -> cba..... ........ ........ ........\n        #     00000000 00000000 00000000 000edcba -> 00000000 00000000 00000000 000CBAed\n        @bits[malloc_size - 1] = (@bits[malloc_size - 1] >> n) | (temp << (end_sub_index - n))\n      else\n        # n = 7:\n        #\n        #     ........ ........ ........ .GFEDCBA -> ........ ........ ........ ........\n        #     ........ ........ ........ ........ -> BAedcba. ........ ........ ........\n        #     00000000 00000000 00000000 000edcba -> 00000000 00000000 00000000 000GFEDC\n        @bits[malloc_size - 2] |= temp << (32 + end_sub_index - n)\n        @bits[malloc_size - 1] = temp << (end_sub_index - n)\n      end\n\n      clear_unused_bits\n    elsif n >= size - 32\n      n = size - n\n      malloc_size = self.malloc_size\n\n      end_sub_index = (size - 1) % 32 + 1\n      if n <= end_sub_index\n        # n = 3:\n        #\n        #     ........ ........ ........ ........ -> ........ ........ ........ .....CBA\n        #     00000000 00000000 00000000 000CBA.. -> 00000000 00000000 00000000 000.....\n        temp = @bits[malloc_size - 1] >> (end_sub_index - n)\n      else\n        # n = 7:\n        #\n        #     BA...... ........ ........ ........ -> ........ ........ ........ .GFEDCBA\n        #     00000000 00000000 00000000 000GFEDC -> 00000000 00000000 00000000 000.....\n        temp = Intrinsics.fshl32(@bits[malloc_size - 1], @bits[malloc_size - 2], n - end_sub_index)\n      end\n\n      (malloc_size - 1).downto(1) do |i|\n        @bits[i] = Intrinsics.fshl32(@bits[i], @bits[i - 1], n)\n      end\n      @bits[0] = (@bits[0] << n) | temp\n\n      clear_unused_bits\n    else\n      super\n    end\n\n    self\n  end\n\n  # Creates a string representation of `self`.\n  #\n  # ```\n  # require \"bit_array\"\n  #\n  # ba = BitArray.new(5)\n  # ba.to_s # => \"BitArray[00000]\"\n  # ```\n  def to_s(io : IO) : Nil\n    io << \"BitArray[\"\n    each do |value|\n      io << (value ? '1' : '0')\n    end\n    io << ']'\n  end\n\n  # :ditto:\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  # Returns a `Bytes` able to read and write bytes from a buffer.\n  # The slice will be long enough to hold all the bits groups in bytes despite the `UInt32` internal representation.\n  # It's useful for reading and writing a bit array from a byte buffer directly.\n  #\n  # WARNING: It is undefined behaviour to set any of the unused bits of a bit array to\n  # `true` via a slice.\n  def to_slice : Bytes\n    Slice.new(@bits.as(Pointer(UInt8)), bytesize)\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher = size.hash(hasher)\n    hasher = to_slice.hash(hasher)\n    hasher\n  end\n\n  # Returns a new `BitArray` with all of the same elements.\n  def dup\n    bit_array = BitArray.new(@size)\n    @bits.copy_to(bit_array.@bits, malloc_size)\n    bit_array\n  end\n\n  private def bit_index_and_sub_index(index)\n    bit_index_and_sub_index(index) { raise IndexError.new }\n  end\n\n  private def bit_index_and_sub_index(index, &)\n    index = check_index_out_of_bounds(index) do\n      return yield\n    end\n    index.divmod(32)\n  end\n\n  protected def clear_unused_bits\n    # There are no unused bits if `size` is a multiple of 32.\n    bit_index, sub_index = @size.divmod(32)\n    @bits[bit_index] &= ~(UInt32::MAX << sub_index) unless sub_index == 0\n  end\n\n  private def bytesize\n    (@size - 1) // 8 + 1\n  end\n\n  private def malloc_size\n    (@size - 1) // 32 + 1\n  end\nend\n"
  },
  {
    "path": "src/bool.cr",
    "content": "# Bool has only two possible values: `true` and `false`. They are constructed using these literals:\n#\n# ```\n# true  # A Bool that is true\n# false # A Bool that is false\n# ```\n#\n# See [`Bool` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/bool.html) in the language reference.\nstruct Bool\n  # Bitwise OR. Returns `true` if this bool or *other* is `true`, otherwise returns `false`.\n  #\n  # ```\n  # false | false # => false\n  # false | true  # => true\n  # true | false  # => true\n  # true | true   # => true\n  # ```\n  def |(other : Bool) : Bool\n    self ? true : other\n  end\n\n  # Bitwise AND. Returns `true` if this bool and *other* are `true`, otherwise returns `false`.\n  #\n  # ```\n  # false & false # => false\n  # false & true  # => false\n  # true & false  # => false\n  # true & true   # => true\n  # ```\n  def &(other : Bool) : Bool\n    self ? other : false\n  end\n\n  # Exclusive OR. Returns `true` if this bool is different from *other*, otherwise returns `false`.\n  #\n  # ```\n  # false ^ false # => false\n  # false ^ true  # => true\n  # true ^ false  # => true\n  # true ^ true   # => false\n  # ```\n  def ^(other : Bool) : Bool\n    self != other\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.bool(self)\n  end\n\n  # Returns an integer derived from the boolean value, for interoperability with C-style booleans.\n  #\n  # The value is `1` for `true` and `0` for `false`.\n  def to_unsafe : LibC::Int\n    LibC::Int.new(self ? 1 : 0)\n  end\n\n  # Returns `\"true\"` for `true` and `\"false\"` for `false`.\n  def to_s : String\n    self ? \"true\" : \"false\"\n  end\n\n  # Appends `\"true\"` for `true` and `\"false\"` for `false` to the given IO.\n  def to_s(io : IO) : Nil\n    io << to_s\n  end\n\n  def clone : Bool\n    self\n  end\nend\n"
  },
  {
    "path": "src/box.cr",
    "content": "# A Box allows turning any object to a `Void*` and back.\n#\n# A Box's purpose is passing data to C as a `Void*` and then converting that\n# back to the original data type.\n#\n# For an example usage, see `Proc`'s explanation about sending Procs to C.\nclass Box(T)\n  # :nodoc:\n  #\n  # Returns the original object\n  getter object : T\n\n  # :nodoc:\n  #\n  # Creates a `Box` with the given object.\n  #\n  # This method isn't usually used directly. Instead, `Box.box` is used.\n  def initialize(@object : T)\n  end\n\n  # Turns *object* into a `Void*`.\n  #\n  # If `T` is not a reference or pointer type, nor a union between reference\n  # types and `Nil`, this method effectively copies *object* to the dynamic\n  # heap.\n  #\n  # NOTE: The returned pointer might not be a null pointer even when *object* is\n  # `nil`.\n  def self.box(object : T) : Void*\n    {% if T < Pointer || T.union_types.all? { |t| t == Nil || t < Reference } %}\n      object.as(Void*)\n    {% else %}\n      # NOTE: if `T` is explicitly specified and `typeof(object) < T` (e.g.\n      # `Box(Int32?).box(1)`, then `.new` will perform the appropriate upcast\n      new(object).as(Void*)\n    {% end %}\n  end\n\n  # Unboxes a `Void*` into an object of type `T`. Note that for this you must\n  # specify T: `Box(T).unbox(data)`.\n  #\n  # Raises `NilAssertionError` if *pointer* is null and `T` is not nilable.\n  #\n  # WARNING: It is undefined behavior to box an object in one type and unbox it\n  # via a different type; in particular, when boxing a `T` and unboxing it as a\n  # `T?`, or vice-versa.\n  def self.unbox(pointer : Void*) : T\n    {% if T < Pointer %}\n      pointer.as(T)\n    {% elsif T.union_types.all? { |t| t == Nil || t < Reference } %}\n      {% unless T.union_types.any? { |t| t == Nil } %}\n        if pointer.null?\n          raise NilAssertionError.new(\"Unboxing null pointer\")\n        end\n      {% end %}\n      pointer.as(T)\n    {% else %}\n      if pointer.null?\n        raise NilAssertionError.new(\"Unboxing null pointer in mixed union\")\n      end\n\n      pointer.as(self).object\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/channel/select/select_action.cr",
    "content": "class Channel(T)\n  # :nodoc:\n  module SelectAction(S)\n    abstract def execute : DeliveryState\n    abstract def wait(context : SelectContext(S))\n    abstract def wait_result_impl(context : SelectContext(S))\n    abstract def unwait_impl(context : SelectContext(S))\n    abstract def result : S\n    abstract def lock_object_id\n    abstract def lock\n    abstract def unlock\n\n    def create_context_and_wait(shared_state)\n      context = SelectContext.new(shared_state, self)\n      self.wait(context)\n      context\n    end\n\n    # wait_result overload allow implementors to define\n    # wait_result_impl with the right type and Channel.select_impl\n    # to allow dispatching over unions that will not happen\n    def wait_result(context : SelectContext)\n      raise \"BUG: Unexpected call to #{typeof(self)}#wait_result(context : #{typeof(context)})\"\n    end\n\n    def wait_result(context : SelectContext(S))\n      wait_result_impl(context)\n    end\n\n    # idem wait_result/wait_result_impl\n    def unwait(context : SelectContext)\n      raise \"BUG: Unexpected call to #{typeof(self)}#unwait(context : #{typeof(context)})\"\n    end\n\n    def unwait(context : SelectContext(S))\n      unwait_impl(context)\n    end\n\n    # Implementor that returns `Channel::UseDefault` in `#execute`\n    # must redefine `#default_result`\n    def default_result\n      raise \"Unreachable\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/channel/select/timeout_action.cr",
    "content": "# Timeout keyword for use in `select`.\n#\n# ```\n# select\n# when x = ch.receive\n#   puts \"got #{x}\"\n# when timeout(1.seconds)\n#   puts \"timeout\"\n# end\n# ```\n#\n# NOTE: It won't trigger if the `select` has an `else` case (i.e.: a non-blocking select).\ndef timeout_select_action(timeout : Time::Span) : Channel::TimeoutAction\n  Channel::TimeoutAction.new(timeout)\nend\n\nclass Channel(T)\n  # :nodoc:\n  class TimeoutAction\n    include SelectAction(Nil)\n\n    # Total amount of time to wait\n    @timeout : Time::Span\n    @select_context : SelectContext(Nil)?\n\n    def initialize(@timeout : Time::Span)\n    end\n\n    def execute : DeliveryState\n      DeliveryState::None\n    end\n\n    def result : Nil\n      nil\n    end\n\n    def wait(context : SelectContext(Nil)) : Nil\n      @select_context = context\n      Fiber.timeout(@timeout, self)\n    end\n\n    def wait_result_impl(context : SelectContext(Nil))\n      nil\n    end\n\n    def unwait_impl(context : SelectContext(Nil))\n      Fiber.cancel_timeout\n    end\n\n    def lock_object_id : UInt64\n      self.object_id\n    end\n\n    def lock\n    end\n\n    def unlock\n    end\n\n    def time_expired(fiber : Fiber) : Nil\n      fiber.enqueue if time_expired?\n    end\n\n    def time_expired? : Bool\n      @select_context.try &.try_trigger || false\n    end\n  end\nend\n"
  },
  {
    "path": "src/channel/select.cr",
    "content": "class Channel(T)\n  # :nodoc:\n  record NotReady\n\n  private enum SelectState\n    None   = 0\n    Active = 1\n    Done   = 2\n  end\n\n  private class SelectContextSharedState\n    @state : Atomic(SelectState)\n\n    def initialize(value : SelectState)\n      @state = Atomic(SelectState).new(value)\n    end\n\n    def compare_and_set(cmp : SelectState, new : SelectState) : {SelectState, Bool}\n      @state.compare_and_set(cmp, new)\n    end\n  end\n\n  private class SelectContext(S)\n    @state : SelectContextSharedState\n    property action : SelectAction(S)\n    @activated = false\n\n    def initialize(@state, @action : SelectAction(S))\n    end\n\n    def activated? : Bool\n      @activated\n    end\n\n    def try_trigger : Bool\n      _, succeed = @state.compare_and_set(:active, :done)\n      if succeed\n        @activated = true\n      end\n      succeed\n    end\n  end\n\n  private enum DeliveryState\n    None\n    Delivered\n    Closed\n  end\n\n  # :nodoc:\n  def self.select(*ops : SelectAction)\n    self.select ops\n  end\n\n  # :nodoc:\n  def self.select(ops : Indexable(SelectAction))\n    i, m = select_impl(ops, false)\n    raise \"BUG: Blocking select returned not ready status\" if m.is_a?(NotReady)\n    return i, m\n  end\n\n  # :nodoc:\n  def self.non_blocking_select(*ops : SelectAction)\n    self.non_blocking_select ops\n  end\n\n  # :nodoc:\n  def self.non_blocking_select(ops : Indexable(SelectAction))\n    select_impl(ops, true)\n  end\n\n  private def self.select_impl(ops : Indexable(SelectAction), non_blocking)\n    # ops_locks is a duplicate of ops that can be sorted without disturbing the\n    # index positions of ops\n    if ops.responds_to?(:unstable_sort_by!)\n      # If the collection type implements `unstable_sort_by!` we can dup it.\n      # This applies to two types:\n      # * `Array`: `Array#to_a` does not dup and would return the same instance,\n      #   thus we'd be sorting ops and messing up the index positions.\n      # * `StaticArray`: This avoids a heap allocation because we can dup a\n      #   static array on the stack.\n      ops_locks = ops.dup\n    elsif ops.responds_to?(:to_static_array)\n      # If the collection type implements `to_static_array` we can create a\n      # copy without allocating an array. This applies to `Tuple` types, which\n      # the compiler generates for `select` expressions.\n      ops_locks = ops.to_static_array\n    else\n      ops_locks = ops.to_a\n    end\n\n    # Sort the operations by the channel they contain\n    # This is to avoid deadlocks between concurrent `select` calls\n    ops_locks.unstable_sort_by!(&.lock_object_id)\n\n    each_skip_duplicates(ops_locks, &.lock)\n\n    ops.each_with_index do |op, index|\n      state = op.execute\n\n      case state\n      in .delivered?\n        each_skip_duplicates(ops_locks, &.unlock)\n        return index, op.result\n      in .closed?\n        each_skip_duplicates(ops_locks, &.unlock)\n        return index, op.default_result\n      in .none?\n        # do nothing\n      end\n    end\n\n    if non_blocking\n      each_skip_duplicates(ops_locks, &.unlock)\n      return ops.size, NotReady.new\n    end\n\n    # Because `channel#close` may clean up a long list, `select_context.try_trigger` may\n    # be called after the select return. In order to prevent invalid address access,\n    # the state is allocated in the heap.\n    shared_state = SelectContextSharedState.new(SelectState::Active)\n    contexts = ops.map &.create_context_and_wait(shared_state)\n\n    each_skip_duplicates(ops_locks, &.unlock)\n    Fiber.suspend\n\n    contexts.each_with_index do |context, index|\n      op = ops[index]\n      op.lock\n      op.unwait(context)\n      op.unlock\n    end\n\n    contexts.each_with_index do |context, index|\n      if context.activated?\n        return index, ops[index].wait_result(context)\n      end\n    end\n\n    raise \"BUG: Fiber was awaken from select but no action was activated\"\n  end\n\n  private def self.each_skip_duplicates(ops_locks, &)\n    # Avoid deadlocks from trying to lock the same lock twice.\n    # `ops_lock` is sorted by `lock_object_id`, so identical ones will be in\n    # a row and we skip repeats while iterating.\n    last_lock_id = nil\n    ops_locks.each do |op|\n      if op.lock_object_id != last_lock_id\n        last_lock_id = op.lock_object_id\n        yield op\n      end\n    end\n  end\nend\n\nrequire \"./select/select_action\"\nrequire \"./select/timeout_action\"\n"
  },
  {
    "path": "src/channel.cr",
    "content": "require \"fiber\"\nrequire \"crystal/spin_lock\"\nrequire \"crystal/pointer_linked_list\"\nrequire \"channel/select\"\n\n# A `Channel` enables concurrent communication between fibers.\n#\n# They allow communicating data between fibers without sharing memory and without having to worry about locks, semaphores or other special structures.\n#\n# ```\n# channel = Channel(Int32).new\n#\n# spawn do\n#   channel.send(0)\n#   channel.send(1)\n# end\n#\n# channel.receive # => 0\n# channel.receive # => 1\n# ```\n#\n# NOTE: Although a `Channel(Nil)` or any other nilable types like `Channel(Int32?)` are valid\n# they are discouraged since from certain methods or constructs it receiving a `nil` as data\n# will be indistinguishable from a closed channel.\n#\nclass Channel(T)\n  @lock = Crystal::SpinLock.new\n  @queue : Deque(T)?\n\n  # :nodoc:\n  record UseDefault\n\n  class ClosedError < Exception\n    def initialize(msg = \"Channel is closed\")\n      super(msg)\n    end\n  end\n\n  private module SenderReceiverCloseAction\n    def close\n      self.state = DeliveryState::Closed\n      _select_context = self.select_context\n      if _select_context.nil? || _select_context.try_trigger\n        self.fiber.enqueue\n      end\n    end\n  end\n\n  private struct Sender(T)\n    include Crystal::PointerLinkedList::Node\n    include SenderReceiverCloseAction\n\n    property fiber : Fiber\n    property data : T\n    property state : DeliveryState\n    property select_context : SelectContext(Nil)?\n\n    def initialize\n      @fiber = uninitialized Fiber\n      @data = uninitialized T\n      @state = DeliveryState::None\n    end\n  end\n\n  private struct Receiver(T)\n    include Crystal::PointerLinkedList::Node\n    include SenderReceiverCloseAction\n\n    property fiber : Fiber\n    property data : T\n    property state : DeliveryState\n    property select_context : SelectContext(T)?\n\n    def initialize\n      @fiber = uninitialized Fiber\n      @data = uninitialized T\n      @state = DeliveryState::None\n    end\n  end\n\n  def initialize(@capacity = 0)\n    @closed = false\n\n    @senders = Crystal::PointerLinkedList(Sender(T)).new\n    @receivers = Crystal::PointerLinkedList(Receiver(T)).new\n\n    if capacity > 0\n      @queue = Deque(T).new(capacity)\n    end\n  end\n\n  # Closes the channel.\n  # The method prevents any new value from being sent to the channel.\n  #\n  # If the channel has buffered values, then subsequent calls to `receive` will succeed\n  # and consume the buffer until it is empty.\n  #\n  # All fibers blocked in `send` or `receive` will be awakened with `Channel::ClosedError`.\n  # All subsequent calls to `#send` will consider the channel closed.\n  # Subsequent calls to `#receive` will consider the channel closed if the buffer is empty.\n  #\n  # Calling `#close` on a closed channel does not have any effect.\n  #\n  # It returns `true` when the channel was successfully closed, or `false` if it was already closed.\n  def close : Bool\n    sender_list = Crystal::PointerLinkedList(Sender(T)).new\n    receiver_list = Crystal::PointerLinkedList(Receiver(T)).new\n\n    @lock.sync do\n      return false if @closed\n      @closed = true\n\n      @senders, sender_list = sender_list, @senders\n      @receivers, receiver_list = receiver_list, @receivers\n    end\n\n    sender_list.each(&.value.close)\n    receiver_list.each(&.value.close)\n    true\n  end\n\n  def closed? : Bool\n    @closed\n  end\n\n  # Sends a value to the channel.\n  # If the channel has spare capacity, then the method returns immediately.\n  # Otherwise, this method blocks the calling fiber until another fiber calls `#receive` on the channel.\n  #\n  # Raises `ClosedError` if the channel is closed or closes while waiting on a full channel.\n  def send(value : T) : self\n    sender = Sender(T).new\n\n    @lock.lock\n\n    case send_internal(value)\n    in .delivered?\n      @lock.unlock\n    in .closed?\n      @lock.unlock\n      raise ClosedError.new\n    in .none?\n      sender.fiber = Fiber.current\n      sender.data = value\n      @senders.push pointerof(sender)\n      @lock.unlock\n\n      Fiber.suspend\n\n      case sender.state\n      in .delivered?\n        # ignore\n      in .closed?\n        raise ClosedError.new\n      in .none?\n        raise \"BUG: Fiber was awaken without channel delivery state set\"\n      end\n    end\n\n    self\n  end\n\n  protected def send_internal(value : T)\n    if @closed\n      DeliveryState::Closed\n    elsif receiver_ptr = dequeue_receiver\n      receiver_ptr.value.data = value\n      receiver_ptr.value.state = DeliveryState::Delivered\n      receiver_ptr.value.fiber.enqueue\n\n      DeliveryState::Delivered\n    elsif (queue = @queue) && queue.size < @capacity\n      queue << value\n\n      DeliveryState::Delivered\n    else\n      DeliveryState::None\n    end\n  end\n\n  # Receives a value from the channel.\n  # If there is a value waiting, then it is returned immediately. Otherwise, this method blocks until a value is sent to the channel.\n  #\n  # Raises `ClosedError` if the channel is closed or closes while waiting for receive.\n  #\n  # ```\n  # channel = Channel(Int32).new\n  # spawn do\n  #   channel.send(1)\n  # end\n  # channel.receive # => 1\n  # ```\n  def receive : T\n    receive_impl { raise ClosedError.new }\n  end\n\n  # Receives a value from the channel.\n  # If there is a value waiting, it is returned immediately. Otherwise, this method blocks until a value is sent to the channel.\n  #\n  # Returns `nil` if the channel is closed or closes while waiting for receive.\n  def receive? : T?\n    receive_impl { return nil }\n  end\n\n  private def receive_impl(&)\n    receiver = Receiver(T).new\n\n    @lock.lock\n\n    state, value = receive_internal\n\n    case state\n    in .delivered?\n      @lock.unlock\n      raise \"BUG: Unexpected UseDefault value for delivered receive\" if value.is_a?(UseDefault)\n      value\n    in .closed?\n      @lock.unlock\n      yield\n    in .none?\n      receiver.fiber = Fiber.current\n      @receivers.push pointerof(receiver)\n      @lock.unlock\n\n      Fiber.suspend\n\n      case receiver.state\n      in .delivered?\n        receiver.data\n      in .closed?\n        yield\n      in .none?\n        raise \"BUG: Fiber was awaken without channel delivery state set\"\n      end\n    end\n  end\n\n  protected def receive_internal\n    if (queue = @queue) && !queue.empty?\n      deque_value = queue.shift\n      if sender_ptr = dequeue_sender\n        queue << sender_ptr.value.data\n        sender_ptr.value.state = DeliveryState::Delivered\n        sender_ptr.value.fiber.enqueue\n      end\n\n      {DeliveryState::Delivered, deque_value}\n    elsif sender_ptr = dequeue_sender\n      value = sender_ptr.value.data\n      sender_ptr.value.state = DeliveryState::Delivered\n      sender_ptr.value.fiber.enqueue\n\n      {DeliveryState::Delivered, value}\n    elsif @closed\n      {DeliveryState::Closed, UseDefault.new}\n    else\n      {DeliveryState::None, UseDefault.new}\n    end\n  end\n\n  private def dequeue_receiver\n    while receiver_ptr = @receivers.shift?\n      select_context = receiver_ptr.value.select_context\n      if select_context && !select_context.try_trigger\n        receiver_ptr.value.state = DeliveryState::Delivered\n        next\n      end\n\n      break\n    end\n\n    receiver_ptr\n  end\n\n  private def dequeue_sender\n    while sender_ptr = @senders.shift?\n      select_context = sender_ptr.value.select_context\n      if select_context && !select_context.try_trigger\n        sender_ptr.value.state = DeliveryState::Delivered\n        next\n      end\n\n      break\n    end\n\n    sender_ptr\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def pretty_print(pp)\n    pp.text inspect\n  end\n\n  def self.receive_first(*channels)\n    receive_first channels\n  end\n\n  def self.receive_first(channels : Enumerable(Channel))\n    _, value = self.select(channels.map(&.receive_select_action))\n    value\n  end\n\n  def self.send_first(value, *channels) : Nil\n    send_first value, channels\n  end\n\n  def self.send_first(value, channels : Enumerable(Channel)) : Nil\n    self.select(channels.map(&.send_select_action(value)))\n    nil\n  end\n\n  # :nodoc:\n  def send_select_action(value : T)\n    SendAction.new(self, value)\n  end\n\n  # :nodoc:\n  def receive_select_action\n    StrictReceiveAction.new(self)\n  end\n\n  # :nodoc:\n  def receive_select_action?\n    LooseReceiveAction.new(self)\n  end\n\n  private class StrictReceiveAction(T)\n    include SelectAction(T)\n    property receiver : Receiver(T)\n\n    def initialize(@channel : Channel(T))\n      @receiver = Receiver(T).new\n    end\n\n    def execute : DeliveryState\n      state, value = @channel.receive_internal\n\n      if state.delivered?\n        @receiver.data = value.as(T)\n      end\n\n      state\n    end\n\n    def result : T\n      @receiver.data\n    end\n\n    def wait(context : SelectContext(T)) : Nil\n      @receiver.fiber = Fiber.current\n      @receiver.select_context = context\n      @channel.@receivers.push pointerof(@receiver)\n    end\n\n    def wait_result_impl(context : SelectContext(T))\n      case @receiver.state\n      in .delivered?\n        context.action.result\n      in .closed?\n        raise ClosedError.new\n      in .none?\n        raise \"BUG: StrictReceiveAction.wait_result_impl called with DeliveryState::None\"\n      end\n    end\n\n    def unwait_impl(context : SelectContext(T))\n      if !@channel.closed? && @receiver.state.none?\n        @channel.@receivers.delete pointerof(@receiver)\n      end\n    end\n\n    def lock_object_id : UInt64\n      @channel.object_id\n    end\n\n    def lock : Nil\n      @channel.@lock.lock\n    end\n\n    def unlock : Nil\n      @channel.@lock.unlock\n    end\n\n    def default_result\n      raise ClosedError.new\n    end\n  end\n\n  private class LooseReceiveAction(T)\n    include SelectAction(T)\n    property receiver : Receiver(T)\n\n    def initialize(@channel : Channel(T))\n      @receiver = Receiver(T).new\n    end\n\n    def execute : DeliveryState\n      state, value = @channel.receive_internal\n\n      if state.delivered?\n        @receiver.data = value.as(T)\n      end\n\n      state\n    end\n\n    def result : T\n      @receiver.data\n    end\n\n    def wait(context : SelectContext(T)) : Nil\n      @receiver.fiber = Fiber.current\n      @receiver.select_context = context\n      @channel.@receivers.push pointerof(@receiver)\n    end\n\n    def wait_result_impl(context : SelectContext(T))\n      case @receiver.state\n      in .delivered?\n        context.action.result\n      in .closed?\n        nil\n      in .none?\n        raise \"BUG: LooseReceiveAction.wait_result_impl called with DeliveryState::None\"\n      end\n    end\n\n    def unwait_impl(context : SelectContext(T))\n      if !@channel.closed? && @receiver.state.none?\n        @channel.@receivers.delete pointerof(@receiver)\n      end\n    end\n\n    def lock_object_id : UInt64\n      @channel.object_id\n    end\n\n    def lock : Nil\n      @channel.@lock.lock\n    end\n\n    def unlock : Nil\n      @channel.@lock.unlock\n    end\n\n    def default_result\n      nil\n    end\n  end\n\n  private class SendAction(T)\n    include SelectAction(Nil)\n    property sender : Sender(T)\n\n    def initialize(@channel : Channel(T), value : T)\n      @sender = Sender(T).new\n      @sender.data = value\n    end\n\n    def execute : DeliveryState\n      @channel.send_internal(@sender.data)\n    end\n\n    def result : Nil\n      nil\n    end\n\n    def wait(context : SelectContext(Nil)) : Nil\n      @sender.fiber = Fiber.current\n      @sender.select_context = context\n      @channel.@senders.push pointerof(@sender)\n    end\n\n    def wait_result_impl(context : SelectContext(Nil))\n      case @sender.state\n      in .delivered?\n        context.action.result\n      in .closed?\n        raise ClosedError.new\n      in .none?\n        raise \"BUG: SendAction.wait_result_impl called with DeliveryState::None\"\n      end\n    end\n\n    def unwait_impl(context : SelectContext(Nil))\n      if !@channel.closed? && @sender.state.none?\n        @channel.@senders.delete pointerof(@sender)\n      end\n    end\n\n    def lock_object_id : UInt64\n      @channel.object_id\n    end\n\n    def lock : Nil\n      @channel.@lock.lock\n    end\n\n    def unlock : Nil\n      @channel.@lock.unlock\n    end\n\n    def default_result\n      raise ClosedError.new\n    end\n  end\nend\n"
  },
  {
    "path": "src/char/reader.cr",
    "content": "struct Char\n  # A `Char::Reader` allows iterating a `String` by Chars.\n  #\n  # As soon as you instantiate a `Char::Reader` it will decode the first\n  # char in the `String`, which can be accessed by invoking `current_char`.\n  # At this point `pos`, the current position in the string, will equal zero.\n  # Successive calls to `next_char` return the next chars in the string,\n  # advancing `pos`.\n  #\n  # NOTE: The null character `'\\0'` will be returned in `current_char` when\n  # the end is reached (as well as when the string is empty). Thus, `has_next?`\n  # will return `false` only when `pos` is equal to the string's bytesize, in which\n  # case `current_char` will always be `'\\0'`.\n  #\n  # NOTE: For performance reasons, `Char::Reader` has value semantics, so care\n  # must be taken when a reader is declared as a local variable and passed to\n  # another method:\n  #\n  # ```\n  # def lstrip(reader)\n  #   until reader.current_char.whitespace?\n  #     reader.next_char\n  #   end\n  #   reader\n  # end\n  #\n  # # caller's internal state is untouched\n  # reader = Char::Reader.new(\"   abc\")\n  # lstrip(reader)\n  # reader.current_char # => ' '\n  #\n  # # to modify caller's internal state, the method must return a new reader\n  # reader = lstrip(reader)\n  # reader.current_char # => 'a'\n  # ```\n  struct Reader\n    include Enumerable(Char)\n\n    # Returns the reader's String.\n    getter string : String\n\n    # Returns the current character, or `'\\0'` if the reader is at the end of\n    # the string.\n    #\n    # ```\n    # reader = Char::Reader.new(\"ab\")\n    # reader.current_char # => 'a'\n    # reader.next_char\n    # reader.current_char # => 'b'\n    # reader.next_char\n    # reader.current_char # => '\\0'\n    # ```\n    getter current_char : Char\n\n    # Returns the size of the `#current_char` (in bytes) as if it were encoded in UTF-8.\n    #\n    # ```\n    # reader = Char::Reader.new(\"aé\")\n    # reader.current_char_width # => 1\n    # reader.next_char\n    # reader.current_char_width # => 2\n    # ```\n    getter current_char_width : Int32\n\n    # Returns the byte position of the current character.\n    #\n    # ```\n    # reader = Char::Reader.new(\"ab\")\n    # reader.pos # => 0\n    # reader.next_char\n    # reader.pos # => 1\n    # ```\n    getter pos : Int32\n\n    # If there was an error decoding the current char because\n    # of an invalid UTF-8 byte sequence, returns the byte\n    # that produced the invalid encoding. Returns `0` if the char would've been\n    # out of bounds. Otherwise returns `nil`.\n    getter error : UInt8?\n\n    # Creates a reader with the specified *string* positioned at\n    # byte index *pos*.\n    def initialize(@string : String, pos = 0)\n      @pos = pos.to_i\n      @current_char = '\\0'\n      @current_char_width = 0\n      decode_current_char\n    end\n\n    # Creates a reader that will be positioned at the last char\n    # of the given string.\n    def initialize(*, at_end @string : String)\n      @pos = @string.bytesize\n      @current_char = '\\0'\n      @current_char_width = 0\n      decode_previous_char\n    end\n\n    # Returns the current character.\n    #\n    # Returns `nil` if the reader is at the end of the string.\n    def current_char? : Char?\n      if has_next?\n        current_char\n      end\n    end\n\n    # Returns `true` if the reader is not at the end of the string.\n    #\n    # NOTE: This only means `#next_char` will successfully increment `#pos`; if\n    # the reader is already at the last character, `#next_char` will return the\n    # terminating null byte because there isn't really a next character.\n    #\n    # ```\n    # reader = Char::Reader.new(\"ab\")\n    # reader.has_next? # => true\n    # reader.next_char # => 'b'\n    # reader.has_next? # => true\n    # reader.next_char # => '\\0'\n    # reader.has_next? # => false\n    # ```\n    def has_next? : Bool\n      @pos < @string.bytesize\n    end\n\n    # Tries to read the next character in the string.\n    #\n    # If the reader is at the end of the string before or after incrementing\n    # `#pos`, returns `nil`.\n    #\n    # ```\n    # reader = Char::Reader.new(\"abc\")\n    # reader.next_char?   # => 'b'\n    # reader.next_char?   # => 'c'\n    # reader.next_char?   # => nil\n    # reader.current_char # => '\\0'\n    # ```\n    def next_char? : Char?\n      next_pos = @pos + @current_char_width\n      if next_pos <= @string.bytesize\n        @pos = next_pos\n        decode_current_char\n        current_char?\n      end\n    end\n\n    # Reads the next character in the string.\n    #\n    # If the reader is at the end of the string after incrementing `#pos`,\n    # returns `'\\0'`. If the reader is already at the end beforehand, raises\n    # `IndexError`.\n    #\n    # ```\n    # reader = Char::Reader.new(\"abc\")\n    # reader.next_char # => 'b'\n    # reader.next_char # => 'c'\n    # reader.next_char # => '\\0'\n    # reader.next_char # raise IndexError\n    # ```\n    def next_char : Char\n      next_pos = @pos + @current_char_width\n      if next_pos <= @string.bytesize\n        @pos = next_pos\n        decode_current_char\n      else\n        raise IndexError.new\n      end\n    end\n\n    # Returns the next character in the `#string` without incrementing `#pos`.\n    #\n    # Returns `'\\0'` if the reader is at the last character of the string.\n    # Raises `IndexError` if the reader is at the end.\n    #\n    # ```\n    # reader = Char::Reader.new(\"ab\")\n    # reader.peek_next_char # => 'b'\n    # reader.current_char   # => 'a'\n    # ```\n    def peek_next_char : Char\n      next_pos = @pos + @current_char_width\n\n      if next_pos > @string.bytesize\n        raise IndexError.new\n      end\n\n      decode_char_at(next_pos) do |code_point|\n        code_point.unsafe_chr\n      end\n    end\n\n    # Returns `true` if the reader is not at the beginning of the string.\n    def has_previous? : Bool\n      @pos > 0\n    end\n\n    # Tries to read the previous character in the string.\n    #\n    # Returns `nil` if the reader is already at the beginning of the string.\n    # Otherwise decrements `#pos`.\n    #\n    # ```\n    # reader = Char::Reader.new(at_end: \"abc\")\n    # reader.previous_char? # => 'b'\n    # reader.previous_char? # => 'a'\n    # reader.previous_char? # => nil\n    # ```\n    def previous_char? : Char?\n      if has_previous?\n        decode_previous_char\n      end\n    end\n\n    # Reads the previous character in the string.\n    #\n    # Raises `IndexError` if the reader is already at the beginning of the\n    # string. Otherwise decrements `#pos`.\n    #\n    # ```\n    # reader = Char::Reader.new(at_end: \"abc\")\n    # reader.previous_char # => 'b'\n    # reader.previous_char # => 'a'\n    # reader.previous_char # raises IndexError\n    # ```\n    def previous_char : Char\n      unless has_previous?\n        raise IndexError.new\n      end\n\n      decode_previous_char.as(Char)\n    end\n\n    # Sets `#pos` to *pos*.\n    #\n    # ```\n    # reader = Char::Reader.new(\"abc\")\n    # reader.next_char\n    # reader.next_char\n    # reader.pos = 0\n    # reader.current_char # => 'a'\n    # ```\n    def pos=(pos)\n      if pos > @string.bytesize\n        raise IndexError.new\n      end\n\n      @pos = pos\n      decode_current_char\n      pos\n    end\n\n    # Yields successive characters from `#string` starting from `#pos`.\n    #\n    # ```\n    # reader = Char::Reader.new(\"abc\")\n    # reader.next_char\n    # reader.each do |c|\n    #   puts c.upcase\n    # end\n    # ```\n    #\n    # ``` text\n    # B\n    # C\n    # ```\n    def each(&) : Nil\n      while has_next?\n        yield current_char\n\n        @pos += @current_char_width\n        decode_current_char\n      end\n    end\n\n    # :nodoc:\n    # See also: `IO#read_char_with_bytesize`.\n    private def decode_char_at(pos, & : UInt32, Int32, UInt8? ->)\n      first = byte_at(pos)\n      if first < 0x80\n        return yield first, 1, nil\n      end\n\n      if first < 0xc2\n        invalid_byte_sequence\n      end\n\n      second = byte_at(pos + 1)\n      if (second & 0xc0) != 0x80\n        invalid_byte_sequence\n      end\n\n      if first < 0xe0\n        return yield (first << 6) &+ (second &- 0x3080), 2, nil\n      end\n\n      third = byte_at(pos + 2)\n      if (third & 0xc0) != 0x80\n        invalid_byte_sequence\n      end\n\n      if first < 0xf0\n        if first == 0xe0 && second < 0xa0\n          invalid_byte_sequence\n        end\n\n        if first == 0xed && second >= 0xa0\n          invalid_byte_sequence\n        end\n\n        return yield (first << 12) &+ (second << 6) &+ (third &- 0xE2080), 3, nil\n      end\n\n      if first == 0xf0 && second < 0x90\n        invalid_byte_sequence\n      end\n\n      if first == 0xf4 && second >= 0x90\n        invalid_byte_sequence\n      end\n\n      fourth = byte_at(pos + 3)\n      if (fourth & 0xc0) != 0x80\n        invalid_byte_sequence\n      end\n\n      if first < 0xf5\n        return yield (first << 18) &+ (second << 12) &+ (third << 6) &+ (fourth &- 0x3C82080), 4, nil\n      end\n\n      invalid_byte_sequence\n    end\n\n    private macro invalid_byte_sequence\n      return yield Char::REPLACEMENT.ord.to_u32!, 1, first.to_u8!\n    end\n\n    @[AlwaysInline]\n    private def decode_current_char\n      decode_char_at(@pos) do |code_point, width, error|\n        @current_char_width = width\n        @error = error\n        @current_char = code_point.unsafe_chr\n      end\n    end\n\n    # The reverse UTF-8 DFA transition table for reference: (contrast with\n    # `Unicode::UTF8_ENCODING_DFA`)\n    #\n    #              accepted (initial state)\n    #              | 1 continuation byte\n    #              | | 2 continuation bytes; disallow overlong encodings up to U+07FF\n    #              | | | 2 continuation bytes; disallow surrogate pairs\n    #              | | | | 3 continuation bytes; disallow overlong encodings up to U+FFFF\n    #              | | | | | 3 continuation bytes; disallow codepoints above U+10FFFF\n    #              v v v v v v\n    #\n    #            | 0 2 3 4 5 6\n    # -----------+------------\n    # 0x00..0x7F | 0 _ _ _ _ _\n    # 0x80..0x8F | 2 3 5 5 _ _\n    # 0x90..0x9F | 2 3 6 6 _ _\n    # 0xA0..0xBF | 2 4 6 6 _ _\n    # 0xC2..0xDF | _ 0 _ _ _ _\n    # 0xE0..0xE0 | _ _ _ 0 _ _\n    # 0xE1..0xEC | _ _ 0 0 _ _\n    # 0xED..0xED | _ _ 0 _ _ _\n    # 0xEE..0xEF | _ _ 0 0 _ _\n    # 0xF0..0xF0 | _ _ _ _ _ 0\n    # 0xF1..0xF3 | _ _ _ _ 0 0\n    # 0xF4..0xF4 | _ _ _ _ 0 _\n    private def decode_char_before(pos, & : UInt32, Int32, UInt8? ->)\n      fourth = byte_at(pos - 1)\n      if fourth <= 0x7f\n        return yield fourth, 1, nil\n      end\n\n      if fourth > 0xbf || pos < 2\n        invalid_byte_sequence_before\n      end\n\n      third = byte_at(pos - 2)\n      if 0xc2 <= third <= 0xdf\n        return yield (third << 6) &+ (fourth &- 0x3080), 2, nil\n      end\n\n      if (third & 0xc0) != 0x80 || pos < 3\n        invalid_byte_sequence_before\n      end\n\n      second = byte_at(pos - 3)\n      if second & 0xf0 == 0xe0\n        if second == 0xe0 && third <= 0x9f\n          invalid_byte_sequence_before\n        end\n\n        if second == 0xed && third >= 0xa0\n          invalid_byte_sequence_before\n        end\n\n        return yield (second << 12) &+ (third << 6) &+ (fourth &- 0xE2080), 3, nil\n      end\n\n      if (second & 0xc0) != 0x80 || pos < 4\n        invalid_byte_sequence_before\n      end\n\n      first = byte_at(pos - 4)\n      if second <= 0x8f\n        unless 0xf1 <= first <= 0xf4\n          invalid_byte_sequence_before\n        end\n      else\n        unless 0xf0 <= first <= 0xf3\n          invalid_byte_sequence_before\n        end\n      end\n\n      return yield (first << 18) &+ (second << 12) &+ (third << 6) &+ (fourth &- 0x3C82080), 4, nil\n    end\n\n    private macro invalid_byte_sequence_before\n      return yield Char::REPLACEMENT.ord.to_u32!, 1, fourth.to_u8!\n    end\n\n    @[AlwaysInline]\n    private def decode_previous_char\n      return nil if @pos == 0\n\n      decode_char_before(@pos) do |code_point, width, error|\n        @current_char_width = width\n        @pos -= width\n        @error = error\n        @current_char = code_point.unsafe_chr\n      end\n    end\n\n    private def byte_at(i)\n      @string.to_unsafe[i].to_u32\n    end\n  end\nend\n"
  },
  {
    "path": "src/char.cr",
    "content": "require \"comparable\"\nrequire \"steppable\"\n\n# A `Char` represents a [Unicode](http://en.wikipedia.org/wiki/Unicode) [code point](http://en.wikipedia.org/wiki/Code_point).\n# It occupies 32 bits.\n#\n# It is created by enclosing an UTF-8 character in single quotes.\n#\n# ```\n# 'a'\n# 'z'\n# '0'\n# '_'\n# 'あ'\n# ```\n#\n# You can use a backslash to denote some characters:\n#\n# ```\n# '\\'' # single quote\n# '\\\\' # backslash\n# '\\e' # escape\n# '\\f' # form feed\n# '\\n' # newline\n# '\\r' # carriage return\n# '\\t' # tab\n# '\\v' # vertical tab\n# ```\n#\n# You can use a backslash followed by an *u* and four hexadecimal characters to denote a unicode codepoint written:\n#\n# ```\n# '\\u0041' # == 'A'\n# ```\n#\n# Or you can use curly braces and specify up to four hexadecimal numbers:\n#\n# ```\n# '\\u{41}' # == 'A'\n# ```\n#\n# See [`Char` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/char.html) in the language reference.\nstruct Char\n  include Comparable(Char)\n  include Steppable\n\n  # The character representing the end of a C string.\n  ZERO = '\\0'\n\n  # The maximum character.\n  MAX = 0x10ffff.unsafe_chr\n\n  # The maximum valid codepoint for a character.\n  MAX_CODEPOINT = 0x10ffff\n\n  # The replacement character, used on invalid UTF-8 byte sequences.\n  REPLACEMENT = '\\ufffd'\n\n  # Returns the difference of the codepoint values of this char and *other*.\n  #\n  # ```\n  # 'a' - 'a' # => 0\n  # 'b' - 'a' # => 1\n  # 'c' - 'a' # => 2\n  # ```\n  def -(other : Char) : Int32\n    ord - other.ord\n  end\n\n  # Concatenates this char and *string*.\n  #\n  # ```\n  # 'f' + \"oo\" # => \"foo\"\n  # ```\n  def +(str : String) : String\n    bytesize = str.bytesize + self.bytesize\n    String.new(bytesize) do |buffer|\n      count = 0\n      each_byte do |byte|\n        buffer[count] = byte\n        count += 1\n      end\n\n      (buffer + count).copy_from(str.to_unsafe, str.bytesize)\n\n      {bytesize, str.size + 1}\n    end\n  end\n\n  # Returns a char that has this char's codepoint plus *other*.\n  #\n  # ```\n  # 'a' + 1 # => 'b'\n  # 'a' + 2 # => 'c'\n  # ```\n  def +(other : Int) : Char\n    (ord + other).chr\n  end\n\n  # Returns a char that has this char's codepoint minus *other*.\n  #\n  # ```\n  # 'c' - 1 # => 'b'\n  # 'c' - 2 # => 'a'\n  # ```\n  def -(other : Int) : Char\n    (ord - other).chr\n  end\n\n  # The comparison operator.\n  #\n  # Returns the difference of the codepoint values of `self` and *other*.\n  # The result is either negative, `0` or positive based on whether `other`'s codepoint is\n  # less, equal, or greater than `self`'s codepoint.\n  #\n  # ```\n  # 'a' <=> 'c' # => -2\n  # 'z' <=> 'z' # => 0\n  # 'c' <=> 'a' # => 2\n  # ```\n  def <=>(other : Char)\n    self - other\n  end\n\n  # Performs a `#step` in the direction of the _limit_. For instance:\n  #\n  # ```\n  # 'd'.step(to: 'a').to_a # => ['d', 'c', 'b', 'a']\n  # 'a'.step(to: 'd').to_a # => ['a', 'b', 'c', 'd']\n  # ```\n  def step(*, to limit = nil, exclusive : Bool = false, &)\n    if limit\n      direction = limit <=> self\n    end\n    step = direction.try(&.sign) || 1\n\n    step(to: limit, by: step, exclusive: exclusive) do |x|\n      yield x\n    end\n  end\n\n  # :ditto:\n  def step(*, to limit = nil, exclusive : Bool = false)\n    if limit\n      direction = limit <=> self\n    end\n    step = direction.try(&.sign) || 1\n\n    step(to: limit, by: step, exclusive: exclusive)\n  end\n\n  # Returns `true` if this char is an ASCII character\n  # (codepoint is in (0..127))\n  def ascii? : Bool\n    ord < 128\n  end\n\n  # Returns `true` if this char is an ASCII number in specified base.\n  #\n  # Base can be from 2 to 36 with digits from '0' to '9' and 'a' to 'z' or 'A' to 'Z'.\n  #\n  # ```\n  # '4'.ascii_number?     # => true\n  # 'z'.ascii_number?     # => false\n  # 'z'.ascii_number?(36) # => true\n  # ```\n  def ascii_number?(base : Int = 10) : Bool\n    !!to_i?(base)\n  end\n\n  # Returns `true` if this char is a number according to unicode.\n  #\n  # ```\n  # '1'.number? # => true\n  # 'a'.number? # => false\n  # ```\n  def number? : Bool\n    ascii? ? ascii_number? : Unicode.number?(self)\n  end\n\n  # Returns `true` if this char is a lowercase ASCII letter.\n  #\n  # ```\n  # 'c'.ascii_lowercase? # => true\n  # 'ç'.lowercase?       # => true\n  # 'G'.ascii_lowercase? # => false\n  # '.'.ascii_lowercase? # => false\n  # ```\n  def ascii_lowercase? : Bool\n    'a' <= self <= 'z'\n  end\n\n  # Returns `true` if this char is a lowercase letter.\n  #\n  # ```\n  # 'c'.lowercase? # => true\n  # 'ç'.lowercase? # => true\n  # 'G'.lowercase? # => false\n  # '.'.lowercase? # => false\n  # 'ǲ'.lowercase? # => false\n  # ```\n  def lowercase? : Bool\n    ascii? ? ascii_lowercase? : Unicode.lowercase?(self)\n  end\n\n  # Returns `true` if this char is an ASCII uppercase letter.\n  #\n  # ```\n  # 'H'.ascii_uppercase? # => true\n  # 'Á'.ascii_uppercase? # => false\n  # 'c'.ascii_uppercase? # => false\n  # '.'.ascii_uppercase? # => false\n  # ```\n  def ascii_uppercase? : Bool\n    'A' <= self <= 'Z'\n  end\n\n  # Returns `true` if this char is an uppercase letter.\n  #\n  # ```\n  # 'H'.uppercase? # => true\n  # 'Á'.uppercase? # => true\n  # 'c'.uppercase? # => false\n  # '.'.uppercase? # => false\n  # 'ǲ'.uppercase? # => false\n  # ```\n  def uppercase? : Bool\n    ascii? ? ascii_uppercase? : Unicode.uppercase?(self)\n  end\n\n  # Returns `true` if this char is a titlecase character, i.e. a ligature\n  # consisting of an uppercase letter followed by lowercase characters.\n  #\n  # ```\n  # 'ǲ'.titlecase? # => true\n  # 'H'.titlecase? # => false\n  # 'c'.titlecase? # => false\n  # ```\n  def titlecase? : Bool\n    !ascii? && Unicode.titlecase?(self)\n  end\n\n  # Returns `true` if this char is an ASCII letter ('a' to 'z', 'A' to 'Z').\n  #\n  # ```\n  # 'c'.ascii_letter? # => true\n  # 'á'.ascii_letter? # => false\n  # '8'.ascii_letter? # => false\n  # ```\n  def ascii_letter? : Bool\n    ascii_lowercase? || ascii_uppercase?\n  end\n\n  # Returns `true` if this char is a letter.\n  #\n  # All codepoints in the Unicode General Category `L` (Letter) are considered\n  # a letter.\n  #\n  # ```\n  # 'c'.letter? # => true\n  # 'á'.letter? # => true\n  # '8'.letter? # => false\n  # ```\n  def letter? : Bool\n    ascii? ? ascii_letter? : Unicode.letter?(self)\n  end\n\n  # Returns `true` if this char is an ASCII letter or number ('0' to '9', 'a' to 'z', 'A' to 'Z').\n  #\n  # ```\n  # 'c'.ascii_alphanumeric? # => true\n  # '8'.ascii_alphanumeric? # => true\n  # '.'.ascii_alphanumeric? # => false\n  # ```\n  def ascii_alphanumeric? : Bool\n    ascii_letter? || ascii_number?\n  end\n\n  # Returns `true` if this char is a letter or a number according to unicode.\n  #\n  # ```\n  # 'c'.alphanumeric? # => true\n  # '8'.alphanumeric? # => true\n  # '.'.alphanumeric? # => false\n  # ```\n  def alphanumeric? : Bool\n    letter? || number?\n  end\n\n  # Returns `true` if this char is an ASCII whitespace.\n  #\n  # ```\n  # ' '.ascii_whitespace?  # => true\n  # '\\t'.ascii_whitespace? # => true\n  # 'b'.ascii_whitespace?  # => false\n  # ```\n  def ascii_whitespace? : Bool\n    self == ' ' || 9 <= ord <= 13\n  end\n\n  # Returns `true` if this char is a whitespace according to unicode.\n  #\n  # ```\n  # ' '.whitespace?  # => true\n  # '\\t'.whitespace? # => true\n  # 'b'.whitespace?  # => false\n  # ```\n  def whitespace? : Bool\n    ascii? ? ascii_whitespace? : Unicode.whitespace?(self)\n  end\n\n  # Returns `true` if this char is an ASCII hex digit ('0' to '9', 'a' to 'f', 'A' to 'F').\n  #\n  # ```\n  # '5'.hex? # => true\n  # 'a'.hex? # => true\n  # 'F'.hex? # => true\n  # 'g'.hex? # => false\n  # ```\n  def hex? : Bool\n    ascii_number? 16\n  end\n\n  # Returns `true` if this char is matched by the given *sets*.\n  #\n  # Each parameter defines a set, the character is matched against\n  # the intersection of those, in other words it needs to\n  # match all sets.\n  #\n  # If a set starts with a ^, it is negated. The sequence c1-c2\n  # means all characters between and including c1 and c2\n  # and is known as a range.\n  #\n  # The backslash character \\ can be used to escape ^ or - and\n  # is otherwise ignored unless it appears at the end of a range\n  # or set.\n  #\n  # ```\n  # 'l'.in_set? \"lo\"          # => true\n  # 'l'.in_set? \"lo\", \"o\"     # => false\n  # 'l'.in_set? \"hello\", \"^l\" # => false\n  # 'l'.in_set? \"j-m\"         # => true\n  #\n  # '^'.in_set? \"\\\\^aeiou\" # => true\n  # '-'.in_set? \"a\\\\-eo\"   # => true\n  #\n  # '\\\\'.in_set? \"\\\\\"    # => true\n  # '\\\\'.in_set? \"\\\\A\"   # => false\n  # '\\\\'.in_set? \"X-\\\\w\" # => true\n  # ```\n  def in_set?(*sets : String) : Bool\n    if sets.size > 1\n      return sets.all? { |set| in_set?(set) }\n    end\n\n    set = sets.first\n    not_negated = true\n    range = false\n    previous = nil\n\n    set.each_char do |char|\n      case char\n      when '^'\n        unless previous # beginning of set\n          not_negated = false\n          previous = char\n          next\n        end\n      when '-'\n        if previous && previous != '\\\\'\n          range = true\n\n          if previous == '^' # ^- at the beginning\n            previous = '^'\n            not_negated = true\n          end\n\n          next\n        else # at the beginning of the set or escaped\n          return not_negated if self == char\n        end\n      end\n\n      if range && previous\n        raise ArgumentError.new \"Invalid range #{previous}-#{char}\" if previous > char\n\n        return not_negated if previous <= self <= char\n\n        range = false\n      elsif char != '\\\\'\n        return not_negated if self == char\n      end\n\n      previous = char\n    end\n\n    return not_negated if range && self == '-'\n    return not_negated if previous == '\\\\' && self == previous\n\n    !not_negated\n  end\n\n  # Returns the downcase equivalent of this char.\n  #\n  # Note that this only works for characters whose downcase\n  # equivalent yields a single codepoint. There are a few\n  # characters, like 'İ', than when downcased result in multiple\n  # characters (in this case: 'I' and the dot mark).\n  #\n  # For more correct behavior see the overloads that receive a block or an `IO`.\n  #\n  # ```\n  # 'Z'.downcase # => 'z'\n  # 'x'.downcase # => 'x'\n  # '.'.downcase # => '.'\n  # ```\n  #\n  # If `options.fold?` is true, then returns the case-folded equivalent instead.\n  # Note that this will return `self` if a multiple-character case folding\n  # exists, even if a separate single-character transformation is also defined\n  # in Unicode.\n  #\n  # ```\n  # 'Z'.downcase(Unicode::CaseOptions::Fold) # => 'z'\n  # 'x'.downcase(Unicode::CaseOptions::Fold) # => 'x'\n  # 'ς'.downcase(Unicode::CaseOptions::Fold) # => 'σ'\n  # 'ꭰ'.downcase(Unicode::CaseOptions::Fold) # => 'Ꭰ'\n  # 'ẞ'.downcase(Unicode::CaseOptions::Fold) # => 'ẞ' # not U+00DF 'ß'\n  # 'ᾈ'.downcase(Unicode::CaseOptions::Fold) # => \"ᾈ\" # not U+1F80 'ᾀ'\n  # ```\n  def downcase(options : Unicode::CaseOptions = :none) : Char\n    if options.fold?\n      Unicode.foldcase(self, options)\n    else\n      Unicode.downcase(self, options)\n    end\n  end\n\n  # Yields each char for the downcase equivalent of this char.\n  #\n  # This method takes into account the possibility that an downcase\n  # version of a char might result in multiple chars, like for\n  # 'İ', which results in 'i' and a dot mark.\n  #\n  # ```\n  # 'Z'.downcase { |v| puts v }                             # prints 'z'\n  # 'ς'.downcase(Unicode::CaseOptions::Fold) { |v| puts v } # prints 'σ'\n  # 'ẞ'.downcase(Unicode::CaseOptions::Fold) { |v| puts v } # prints 's', 's'\n  # 'ᾈ'.downcase(Unicode::CaseOptions::Fold) { |v| puts v } # prints 'ἀ', 'ι'\n  # ```\n  def downcase(options : Unicode::CaseOptions = :none, &)\n    if options.fold?\n      Unicode.foldcase(self, options) { |char| yield char }\n    else\n      Unicode.downcase(self, options) { |char| yield char }\n    end\n  end\n\n  # Writes the downcase equivalent of this char to the given *io*.\n  #\n  # This method takes into account the possibility that an downcase\n  # version of a char might result in multiple chars, like for\n  # 'İ', which results in 'i' and a dot mark.\n  #\n  # ```\n  # 'Z'.downcase(STDOUT)                             # prints \"z\"\n  # 'ς'.downcase(STDOUT, Unicode::CaseOptions::Fold) # prints \"σ\"\n  # 'ẞ'.downcase(STDOUT, Unicode::CaseOptions::Fold) # prints \"ss\"\n  # 'ᾈ'.downcase(STDOUT, Unicode::CaseOptions::Fold) # prints \"ἀι\"\n  # ```\n  def downcase(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    downcase(options) { |char| io << char }\n  end\n\n  # Returns the upcase equivalent of this char.\n  #\n  # Note that this only works for characters whose upcase\n  # equivalent yields a single codepoint. There are a few\n  # characters, like 'ﬄ', than when upcased result in multiple\n  # characters (in this case: 'F', 'F', 'L').\n  #\n  # For more correct behavior see the overloads that receive a block or an `IO`.\n  #\n  # ```\n  # 'z'.upcase # => 'Z'\n  # 'X'.upcase # => 'X'\n  # '.'.upcase # => '.'\n  # ```\n  def upcase(options : Unicode::CaseOptions = :none) : Char\n    Unicode.upcase(self, options)\n  end\n\n  # Yields each char for the upcase equivalent of this char.\n  #\n  # This method takes into account the possibility that an upcase\n  # version of a char might result in multiple chars, like for\n  # 'ﬄ', which results in 'F', 'F' and 'L'.\n  #\n  # ```\n  # 'z'.upcase { |v| puts v } # prints 'Z'\n  # 'ﬄ'.upcase { |v| puts v } # prints 'F', 'F', 'L'\n  # ```\n  def upcase(options : Unicode::CaseOptions = :none, &)\n    Unicode.upcase(self, options) { |char| yield char }\n  end\n\n  # Writes the upcase equivalent of this char to the given *io*.\n  #\n  # This method takes into account the possibility that an upcase\n  # version of a char might result in multiple chars, like for\n  # 'ﬄ', which results in 'F', 'F' and 'L'.\n  #\n  # ```\n  # 'z'.upcase(STDOUT) # prints \"Z\"\n  # 'ﬄ'.upcase(STDOUT) # prints \"FFL\"\n  # ```\n  def upcase(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    upcase(options) { |char| io << char }\n  end\n\n  # Returns the titlecase equivalent of this char.\n  #\n  # Usually this is equivalent to `#upcase`, but a few precomposed characters\n  # consisting of multiple letters may return a different character where only\n  # the first letter is uppercase and the rest lowercase.\n  #\n  # Note that this only works for characters whose titlecase\n  # equivalent yields a single codepoint. There are a few\n  # characters, like 'ﬄ', than when titlecased result in multiple\n  # characters (in this case: 'F', 'f', 'l').\n  #\n  # For more correct behavior see the overloads that receive a block or an `IO`.\n  #\n  # ```\n  # 'z'.titlecase # => 'Z'\n  # 'X'.titlecase # => 'X'\n  # '.'.titlecase # => '.'\n  # 'Ǳ'.titlecase # => 'ǲ'\n  # 'ǳ'.titlecase # => 'ǲ'\n  # ```\n  def titlecase(options : Unicode::CaseOptions = :none) : Char\n    Unicode.titlecase(self, options)\n  end\n\n  # Yields each char for the titlecase equivalent of this char.\n  #\n  # Usually this is equivalent to `#upcase`, but a few precomposed characters\n  # consisting of multiple letters may yield a different character sequence\n  # where only the first letter is uppercase and the rest lowercase.\n  #\n  # This method takes into account the possibility that a titlecase\n  # version of a char might result in multiple chars, like for\n  # 'ﬄ', which results in 'F', 'f' and 'l'.\n  #\n  # ```\n  # 'z'.titlecase { |v| puts v } # prints 'Z'\n  # 'Ǳ'.titlecase { |v| puts v } # prints 'ǲ'\n  # 'ﬄ'.titlecase { |v| puts v } # prints 'F', 'f', 'l'\n  # ```\n  def titlecase(options : Unicode::CaseOptions = :none, &)\n    Unicode.titlecase(self, options) { |char| yield char }\n  end\n\n  # Writes the titlecase equivalent of this char to the given *io*.\n  #\n  # Usually this is equivalent to `#upcase`, but a few precomposed characters\n  # consisting of multiple letters may yield a different character sequence\n  # where only the first letter is uppercase and the rest lowercase.\n  #\n  # This method takes into account the possibility that a titlecase\n  # version of a char might result in multiple chars, like for\n  # 'ﬄ', which results in 'F', 'f' and 'l'.\n  #\n  # ```\n  # 'z'.titlecase(STDOUT) # prints \"Z\"\n  # 'Ǳ'.titlecase(STDOUT) # prints \"ǲ\"\n  # 'ﬄ'.titlecase(STDOUT) # prints \"Ffl\"\n  # ```\n  def titlecase(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    titlecase(options) { |char| io << char }\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.char(self)\n  end\n\n  # Returns the successor codepoint after this one.\n  #\n  # This can be used for iterating a range of characters (see `Range#each`).\n  #\n  # ```\n  # 'a'.succ # => 'b'\n  # 'あ'.succ # => 'ぃ'\n  # ```\n  #\n  # This does not always return `codepoint + 1`. There is a gap in the\n  # range of Unicode scalars: The surrogate codepoints `U+D800` through `U+DFFF`.\n  #\n  # ```\n  # '\\uD7FF'.succ # => '\\uE000'\n  # ```\n  #\n  # Raises `OverflowError` for `Char::MAX`.\n  #\n  # * `#pred` returns the predecessor codepoint.\n  def succ : Char\n    case self\n    when '\\uD7FF'\n      '\\uE000'\n    when MAX\n      raise OverflowError.new(\"Out of Char range\")\n    else\n      (ord + 1).unsafe_chr\n    end\n  end\n\n  # Returns the predecessor codepoint before this one.\n  #\n  # This can be used for iterating a range of characters (see `Range#each`).\n  #\n  # ```\n  # 'b'.pred # => 'a'\n  # 'ぃ'.pred # => 'あ'\n  # ```\n  #\n  # This does not always return `codepoint - 1`. There is a gap in the\n  # range of Unicode scalars: The surrogate codepoints `U+D800` through `U+DFFF`.\n  #\n  # ```\n  # '\\uE000'.pred # => '\\uD7FF'\n  # ```\n  #\n  # Raises `OverflowError` for `Char::ZERO`.\n  #\n  # * `#succ` returns the successor codepoint.\n  def pred : Char\n    case self\n    when '\\uE000'\n      '\\uD7FF'\n    when ZERO\n      raise OverflowError.new(\"Out of Char range\")\n    else\n      (ord - 1).unsafe_chr\n    end\n  end\n\n  # Returns `true` if this char is an ASCII control character.\n  #\n  # This includes the *C0 control codes* (`U+0000` through `U+001F`) and the\n  # *Delete* character (`U+007F`).\n  #\n  # ```\n  # ('\\u0000'..'\\u0019').each do |char|\n  #   char.control? # => true\n  # end\n  #\n  # ('\\u007F'..'\\u009F').each do |char|\n  #   char.control? # => true\n  # end\n  # ```\n  def ascii_control? : Bool\n    ord < 0x20 || ord == 0x7F\n  end\n\n  # Returns `true` if this char is a control character according to unicode.\n  def control? : Bool\n    ascii? ? ascii_control? : Unicode.control?(self)\n  end\n\n  # Returns `true` if this char is a mark character according to unicode.\n  def mark? : Bool\n    Unicode.mark?(self)\n  end\n\n  # Returns `true` if this char is a printable character.\n  #\n  # There is no universal definition of printable characters in Unicode.\n  # For the purpose of this method, all characters with a visible glyph and the\n  # ASCII whitespace (` `) are considered printable.\n  #\n  # This means characters which are `control?` or `whitespace?` (except for ` `)\n  # are non-printable.\n  def printable?\n    !control? && (!whitespace? || self == ' ')\n  end\n\n  # Returns a representation of `self` as a Crystal char literal, wrapped in single\n  # quotes.\n  #\n  # Non-printable characters (see `#printable?`) are escaped.\n  #\n  # ```\n  # 'a'.inspect      # => \"'a'\"\n  # '\\t'.inspect     # => \"'\\\\t'\"\n  # 'あ'.inspect      # => \"'あ'\"\n  # '\\u0012'.inspect # => \"'\\\\u0012'\"\n  # '😀'.inspect      # => \"'\\u{1F600}'\"\n  # ```\n  #\n  # See `#unicode_escape` for the format used to escape characters without a\n  # special escape sequence.\n  #\n  # * `#dump` additionally escapes all non-ASCII characters.\n  def inspect : String\n    dump_or_inspect do |io|\n      if printable?\n        to_s(io)\n      else\n        unicode_escape(io)\n      end\n    end\n  end\n\n  # :ditto:\n  def inspect(io : IO) : Nil\n    io << inspect\n  end\n\n  # Returns a representation of `self` as an ASCII-compatible Crystal char literal,\n  # wrapped in single quotes.\n  #\n  # Non-printable characters (see `#printable?`) and non-ASCII characters\n  # (codepoints larger `U+007F`) are escaped.\n  #\n  # ```\n  # 'a'.dump      # => \"'a'\"\n  # '\\t'.dump     # => \"'\\\\t'\"\n  # 'あ'.dump      # => \"'\\\\u3042'\"\n  # '\\u0012'.dump # => \"'\\\\u0012'\"\n  # '😀'.dump      # => \"'\\\\u{1F600}'\"\n  # ```\n  #\n  # See `#unicode_escape` for the format used to escape characters without a\n  # special escape sequence.\n  #\n  # * `#inspect` only escapes non-printable characters.\n  def dump : String\n    dump_or_inspect do |io|\n      if ascii_control? || ord >= 0x80\n        unicode_escape(io)\n      else\n        to_s(io)\n      end\n    end\n  end\n\n  # :ditto:\n  def dump(io)\n    io << dump\n  end\n\n  private def dump_or_inspect(&)\n    case self\n    when '\\'' then \"'\\\\''\"\n    when '\\\\' then \"'\\\\\\\\'\"\n    when '\\a' then \"'\\\\a'\"\n    when '\\b' then \"'\\\\b'\"\n    when '\\e' then \"'\\\\e'\"\n    when '\\f' then \"'\\\\f'\"\n    when '\\n' then \"'\\\\n'\"\n    when '\\r' then \"'\\\\r'\"\n    when '\\t' then \"'\\\\t'\"\n    when '\\v' then \"'\\\\v'\"\n    when '\\0' then \"'\\\\0'\"\n    else\n      String.build do |io|\n        io << '\\''\n        yield io\n        io << '\\''\n      end\n    end\n  end\n\n  # Returns the Unicode escape sequence representing this character.\n  #\n  # The codepoints are expressed as hexadecimal digits with uppercase letters.\n  # Unicode escapes always use the four digit style for codepoints `U+FFFF`\n  # and lower, adding leading zeros when necessary. Higher codepoints have their\n  # digits wrapped in curly braces and no leading zeros.\n  #\n  # ```\n  # 'a'.unicode_escape      # => \"\\\\u0061\"\n  # '\\t'.unicode_escape     # => \"\\\\u0009\"\n  # 'あ'.unicode_escape      # => \"\\\\u3042\"\n  # '\\u0012'.unicode_escape # => \"\\\\u0012\"\n  # '😀'.unicode_escape      # => \"\\\\u{1F600}\"\n  # ```\n  def unicode_escape : String\n    String.build do |io|\n      unicode_escape(io)\n    end\n  end\n\n  # :ditto:\n  def unicode_escape(io : IO) : Nil\n    io << \"\\\\u\"\n    io << '{' if ord > 0xFFFF\n    io << '0' if ord < 0x1000\n    io << '0' if ord < 0x0100\n    io << '0' if ord < 0x0010\n    ord.to_s(io, 16, upcase: true)\n    io << '}' if ord > 0xFFFF\n  end\n\n  # Returns the integer value of this char if it's an ASCII char denoting a digit\n  # in *base*, raises otherwise.\n  #\n  # ```\n  # '1'.to_i     # => 1\n  # '8'.to_i     # => 8\n  # 'c'.to_i     # raises ArgumentError\n  # '1'.to_i(16) # => 1\n  # 'a'.to_i(16) # => 10\n  # 'f'.to_i(16) # => 15\n  # 'z'.to_i(16) # raises ArgumentError\n  # ```\n  def to_i(base : Int = 10) : Int32\n    to_i?(base) || raise ArgumentError.new(\"Invalid integer: #{self}\")\n  end\n\n  # Returns the integer value of this char if it's an ASCII char denoting a digit\n  # in *base*, `nil` otherwise.\n  #\n  # ```\n  # '1'.to_i?     # => 1\n  # '8'.to_i?     # => 8\n  # 'c'.to_i?     # => nil\n  # '1'.to_i?(16) # => 1\n  # 'a'.to_i?(16) # => 10\n  # 'f'.to_i?(16) # => 15\n  # 'z'.to_i?(16) # => nil\n  # ```\n  def to_i?(base : Int = 10) : Int32?\n    raise ArgumentError.new \"Invalid base #{base}, expected 2 to 36\" unless 2 <= base <= 36\n\n    if base == 10\n      return unless '0' <= self <= '9'\n      self - '0'\n    else\n      ord = ord()\n      if 0 <= ord < 256\n        digit = String::CHAR_TO_DIGIT.to_unsafe[ord]\n        return if digit == -1 || digit >= base\n        digit.to_i32\n      end\n    end\n  end\n\n  # Same as `to_i`.\n  def to_i32(base : Int = 10) : Int32\n    to_i(base)\n  end\n\n  # Same as `to_i?`.\n  def to_i32?(base : Int = 10) : Int32?\n    to_i?(base)\n  end\n\n  {% for type in %w(i8 i16 i64 i128 u8 u16 u32 u64 u128) %}\n    # See also: `to_i`.\n    def to_{{type.id}}(base : Int = 10)\n      to_i(base).to_{{type.id}}\n    end\n\n    # See also: `to_i?`.\n    def to_{{type.id}}?(base : Int = 10)\n      to_i?(base).try &.to_{{type.id}}\n    end\n  {% end %}\n\n  # Returns the integer value of this char as a float if it's an ASCII char denoting a digit,\n  # raises otherwise.\n  #\n  # ```\n  # '1'.to_f # => 1.0\n  # '8'.to_f # => 8.0\n  # 'c'.to_f # raises ArgumentError\n  # ```\n  def to_f : Float64\n    to_f64\n  end\n\n  # Returns the integer value of this char as a float if it's an ASCII char denoting a digit,\n  # `nil` otherwise.\n  #\n  # ```\n  # '1'.to_f? # => 1.0\n  # '8'.to_f? # => 8.0\n  # 'c'.to_f? # => nil\n  # ```\n  def to_f? : Float64?\n    to_f64?\n  end\n\n  # See also: `to_f`.\n  def to_f32 : Float32\n    to_i.to_f32\n  end\n\n  # See also: `to_f?`.\n  def to_f32? : Float32?\n    to_i?.try &.to_f32\n  end\n\n  # Same as `to_f`.\n  def to_f64 : Float64\n    to_i.to_f64\n  end\n\n  # Same as `to_f?`.\n  def to_f64? : Float64?\n    to_i?.try &.to_f64\n  end\n\n  # Yields each of the bytes of this char as encoded by UTF-8.\n  #\n  # ```\n  # puts \"'a'\"\n  # 'a'.each_byte do |byte|\n  #   puts byte\n  # end\n  # puts\n  #\n  # puts \"'あ'\"\n  # 'あ'.each_byte do |byte|\n  #   puts byte\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # 'a'\n  # 97\n  #\n  # 'あ'\n  # 227\n  # 129\n  # 130\n  # ```\n  def each_byte(&) : Nil\n    # See http://en.wikipedia.org/wiki/UTF-8#Sample_code\n\n    c = ord\n    if c < 0x80\n      # 0xxxxxxx\n      yield c.to_u8\n    elsif c <= 0x7ff\n      # 110xxxxx  10xxxxxx\n      yield (0xc0 | c >> 6).to_u8\n      yield (0x80 | c & 0x3f).to_u8\n    elsif c <= 0xffff\n      # 1110xxxx  10xxxxxx  10xxxxxx\n      yield (0xe0 | (c >> 12)).to_u8\n      yield (0x80 | ((c >> 6) & 0x3f)).to_u8\n      yield (0x80 | (c & 0x3f)).to_u8\n    else\n      # 11110xxx  10xxxxxx  10xxxxxx  10xxxxxx\n      yield (0xf0 | (c >> 18)).to_u8\n      yield (0x80 | ((c >> 12) & 0x3f)).to_u8\n      yield (0x80 | ((c >> 6) & 0x3f)).to_u8\n      yield (0x80 | (c & 0x3f)).to_u8\n    end\n  end\n\n  # Returns the number of UTF-8 bytes in this char.\n  #\n  # ```\n  # 'a'.bytesize # => 1\n  # '好'.bytesize # => 3\n  # ```\n  def bytesize : Int32\n    # See http://en.wikipedia.org/wiki/UTF-8#Sample_code\n\n    c = ord\n    if c < 0x80\n      # 0xxxxxxx\n      1\n    elsif c <= 0x7ff\n      # 110xxxxx  10xxxxxx\n      2\n    elsif c <= 0xffff\n      # 1110xxxx  10xxxxxx  10xxxxxx\n      3\n    else\n      # 11110xxx  10xxxxxx  10xxxxxx  10xxxxxx\n      4\n    end\n  end\n\n  # Returns this char bytes as encoded by UTF-8, as an `Array(UInt8)`.\n  #\n  # ```\n  # 'a'.bytes # => [97]\n  # 'あ'.bytes # => [227, 129, 130]\n  # ```\n  def bytes : Array(UInt8)\n    bytes = [] of UInt8\n    each_byte do |byte|\n      bytes << byte\n    end\n    bytes\n  end\n\n  # Returns this char as a string containing this char as a single character.\n  #\n  # ```\n  # 'a'.to_s # => \"a\"\n  # 'あ'.to_s # => \"あ\"\n  # ```\n  def to_s : String\n    bytesize = self.bytesize\n    String.new(bytesize) do |buffer|\n      appender = buffer.appender\n      each_byte { |byte| appender << byte }\n      {bytesize, 1}\n    end\n  end\n\n  # Appends this char to the given `IO`.\n  #\n  # This appends this char's bytes as encoded by UTF-8 to the given `IO`.\n  def to_s(io : IO) : Nil\n    if ascii?\n      byte = ord.to_u8\n\n      # Optimization: writing a slice is much slower than writing a byte\n      if io.has_non_utf8_encoding?\n        io.write_string Slice.new(pointerof(byte), 1)\n      else\n        io.write_byte byte\n      end\n    else\n      chars = uninitialized UInt8[4]\n      i = 0\n      each_byte do |byte|\n        chars[i] = byte\n        i += 1\n      end\n      io.write_string chars.to_slice[0, i]\n    end\n  end\n\n  # Returns `true` if the codepoint is equal to *byte* ignoring the type.\n  #\n  # ```\n  # 'c'.ord       # => 99\n  # 'c' === 99_u8 # => true\n  # 'c' === 99    # => true\n  # 'z' === 99    # => false\n  # ```\n  def ===(byte : Int)\n    ord === byte\n  end\n\n  def clone\n    self\n  end\nend\n"
  },
  {
    "path": "src/class.cr",
    "content": "class Class\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.class(self)\n  end\n\n  # Returns whether this class is the same as *other*.\n  #\n  # ```\n  # Int32 == Int32  # => true\n  # Int32 == String # => false\n  # ```\n  def ==(other : Class) : Bool\n    crystal_type_id == other.crystal_type_id\n  end\n\n  # Returns whether this class inherits or includes *other*.\n  #\n  # ```\n  # Int32 < Number  # => true\n  # Int32 < Value   # => true\n  # Int32 < Int32   # => false\n  # Int32 <= String # => false\n  # ```\n  def <(other : T.class) : Bool forall T\n    # This is so that the method is expanded differently for each type\n    {% @type %}\n    other._gt(self)\n  end\n\n  # Returns whether this class inherits or includes *other*, or\n  # is equal to *other*.\n  #\n  # ```\n  # Int32 < Number  # => true\n  # Int32 < Value   # => true\n  # Int32 <= Int32  # => true\n  # Int32 <= String # => false\n  # ```\n  def <=(other : T.class) : Bool forall T\n    # This is so that the method is expanded differently for each type\n    {% @type %}\n    other._gte(self)\n  end\n\n  # Returns whether *other* inherits or includes `self`.\n  #\n  # ```\n  # Number > Int32  # => true\n  # Number > Number # => false\n  # Number > Object # => false\n  # ```\n  def >(other : T.class) : Bool forall T\n    # This is so that the method is expanded differently for each type\n    {% @type %}\n    other._lt(self)\n  end\n\n  # Returns whether *other* inherits or includes `self`, or is equal\n  # to `self`.\n  #\n  # ```\n  # Number >= Int32  # => true\n  # Number >= Number # => true\n  # Number >= Object # => false\n  # ```\n  def >=(other : T.class) forall T\n    # This is so that the method is expanded differently for each type\n    {% @type %}\n    other._lte(self)\n  end\n\n  # :nodoc:\n  def _lt(other : T.class) forall T\n    {{ @type < T }}\n  end\n\n  # :nodoc:\n  def _lte(other : T.class) forall T\n    {{ @type <= T }}\n  end\n\n  # :nodoc:\n  def _gt(other : T.class) forall T\n    {{ @type > T }}\n  end\n\n  # :nodoc:\n  def _gte(other : T.class) forall T\n    {{ @type >= T }}\n  end\n\n  def ===(other)\n    # This branch handles `Int32.class === 1` case.\n    # In this case, `@type` is `Class` and `other.is_a?(self)` means `other.is_a?(Object)`\n    # because type of `self` is an instance type of the scope type and the instance type of `Class` is `Object`.\n    # See https://github.com/crystal-lang/crystal/issues/10736.\n    {% if @type == Class %}\n      other.is_a?(Class)\n    {% else %}\n      other.is_a?(self)\n    {% end %}\n  end\n\n  # Returns the name of this class.\n  #\n  # ```\n  # String.name # => \"String\"\n  # ```\n  def name : String\n    {{ @type.name.stringify }}\n  end\n\n  # Casts *other* to this class.\n  #\n  # This is the same as using `as`, but allows the class to be passed around as\n  # an argument. See the\n  # [documentation on as](//crystal-lang.org/docs/syntax_and_semantics/as.html)\n  # for more information.\n  #\n  # ```\n  # klass = Int32\n  # number = [99, \"str\"][0]\n  # typeof(number)             # => (String | Int32)\n  # typeof(klass.cast(number)) # => Int32\n  # ```\n  def cast(other) : self\n    other.as(self)\n  end\n\n  # Returns the union type of `self` and *other*.\n  #\n  # ```\n  # Int32 | Char # => (Int32 | Char)\n  # ```\n  def self.|(other : U.class) forall U\n    t = uninitialized self\n    u = uninitialized U\n    typeof(t, u)\n  end\n\n  # Returns `true` if `nil` is an instance of this type.\n  #\n  # ```\n  # Int32.nilable?            # => false\n  # Nil.nilable?              # => true\n  # (Int32 | String).nilable? # => false\n  # (Int32 | Nil).nilable?    # => true\n  # NoReturn.nilable?         # => false\n  # Value.nilable?            # => true\n  # ```\n  def nilable? : Bool\n    {{ @type >= Nil }}\n  end\n\n  def to_s(io : IO) : Nil\n    io << {{ @type.name.stringify }}\n  end\n\n  def dup\n    self\n  end\n\n  def clone\n    self\n  end\nend\n"
  },
  {
    "path": "src/colorize.cr",
    "content": "# With `Colorize` you can change the fore- and background colors and text decorations when rendering text\n# on terminals supporting ANSI escape codes. It adds the `colorize` method to `Object` and thus all classes\n# as its main interface, which calls `to_s` and surrounds it with the necessary escape codes\n# when it comes to obtaining a string representation of the object.\n#\n# NOTE: To use `Colorize`, you must explicitly import it with `require \"colorize\"`\n#\n# Its first argument changes the foreground color:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize(:green)\n# 100.colorize(:red)\n# [1, 2, 3].colorize(:blue)\n# ```\n#\n# There are alternative ways to change the foreground color:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize.fore(:green)\n# \"foo\".colorize.green\n# ```\n#\n# To change the background color, the following methods are available:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize.back(:green)\n# \"foo\".colorize.on(:green)\n# \"foo\".colorize.on_green\n# ```\n#\n# You can also pass an RGB color to `colorize`:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize(0, 255, 255)      # => \"foo\" in aqua\n# \"foo\".colorize.fore(0, 255, 255) # => \"foo\" in aqua\n#\n# # This is the same as:\n#\n# \"foo\".colorize(Colorize::ColorRGB.new(0, 255, 255))      # => \"foo\" in aqua\n# \"foo\".colorize.fore(Colorize::ColorRGB.new(0, 255, 255)) # => \"foo\" in aqua\n# ```\n#\n# Or an 8-bit color:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize(Colorize::Color256.new(208))      # => \"foo\" in orange\n# \"foo\".colorize.fore(Colorize::Color256.new(208)) # => \"foo\" in orange\n# ```\n#\n# It's also possible to change the text decoration:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize.mode(:underline)\n# \"foo\".colorize.underline\n# ```\n#\n# The `colorize` method returns a `Colorize::Object` instance,\n# which allows chaining methods together:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize.fore(:yellow).back(:blue).mode(:underline)\n# ```\n#\n# With the `toggle` method you can temporarily disable adding the escape codes.\n# Settings of the instance are preserved however and can be turned back on later:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize(:red).toggle(false)              # => \"foo\" without color\n# \"foo\".colorize(:red).toggle(false).toggle(true) # => \"foo\" in red\n# ```\n#\n# The color `:default` leaves the object's representation as it is but the object is a `Colorize::Object` then\n# which is handy in conditions such as:\n#\n# ```\n# require \"colorize\"\n#\n# \"foo\".colorize(Random.next_bool ? :green : :default)\n# ```\n#\n# Available colors are:\n# ```\n# :default\n# :black\n# :red\n# :green\n# :yellow\n# :blue\n# :magenta\n# :cyan\n# :light_gray\n# :dark_gray\n# :light_red\n# :light_green\n# :light_yellow\n# :light_blue\n# :light_magenta\n# :light_cyan\n# :white\n# ```\n#\n# See `Colorize::Mode` for available text decorations.\nmodule Colorize\n  # Objects will only be colored if this is `true`, unless overridden by\n  # `Colorize::Object#toggle`.\n  #\n  # ```\n  # require \"colorize\"\n  #\n  # Colorize.enabled = true\n  # \"hello\".colorize.red.to_s # => \"\\e[31mhello\\e[0m\"\n  #\n  # Colorize.enabled = false\n  # \"hello\".colorize.red.to_s # => \"hello\"\n  # ```\n  #\n  # NOTE: This is by default enabled if `.default_enabled?` is true for `STDOUT`\n  # and `STDERR`.\n  class_property? enabled : Bool { default_enabled?(STDOUT, STDERR) }\n\n  # Resets `Colorize.enabled?` to its initial default value, i.e. whether\n  # `.default_enabled?` is true for `STDOUT` and `STDERR`. Returns this new\n  # value.\n  #\n  # This can be used to revert `Colorize.enabled?` to its initial state after\n  # colorization is explicitly enabled or disabled.\n  def self.on_tty_only! : Bool\n    @@enabled = nil\n    enabled?\n  end\n\n  # Returns whether colorization should be enabled by default on the given\n  # standard output and error streams.\n  #\n  # This is true if both streams are terminals (i.e. `IO#tty?` returns true),\n  # the `TERM` environment variable is not equal to `dumb`, and the\n  # [`NO_COLOR` environment variable](https://no-color.org) is not set to a\n  # non-empty string.\n  def self.default_enabled?(stdout : IO, stderr : IO = stdout) : Bool\n    stdout.tty? && (stderr == stdout || stderr.tty?) &&\n      ENV[\"TERM\"]? != \"dumb\" && !ENV[\"NO_COLOR\"]?.try(&.empty?.!)\n  end\n\n  # Resets the color and text decoration of the *io*.\n  #\n  # ```\n  # io = IO::Memory.new\n  # Colorize.with.green.surround(io) do\n  #   io << \"green\"\n  #   Colorize.reset(io)\n  #   io << \" default\"\n  # end\n  # ```\n  def self.reset(io = STDOUT)\n    io << \"\\e[0m\" if enabled?\n  end\n\n  # Helper method to use colorize with `IO`.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io << \"not-green\"\n  # Colorize.with.green.bold.surround(io) do\n  #   io << \"green and bold if Colorize.enabled\"\n  # end\n  # ```\n  def self.with : Colorize::Object(String)\n    \"\".colorize\n  end\nend\n\nmodule Colorize::ObjectExtensions\n  # Turns `self` into a `Colorize::Object`.\n  def colorize : Colorize::Object\n    Colorize::Object.new(self)\n  end\n\n  # Wraps `self` in a `Colorize::Object` and colors it with the given `Color256`\n  # made up from the single *fore* byte.\n  def colorize(fore : UInt8)\n    Colorize::Object.new(self).fore(fore)\n  end\n\n  # Wraps `self` in a `Colorize::Object` and colors it with the given `ColorRGB` made\n  # up from the given *r*ed, *g*reen and *b*lue values.\n  def colorize(r : UInt8, g : UInt8, b : UInt8)\n    Colorize::Object.new(self).fore(r, g, b)\n  end\n\n  # Wraps `self` in a `Colorize::Object` and colors it with the given *fore* `Color`.\n  def colorize(fore : Color)\n    Colorize::Object.new(self).fore(fore)\n  end\n\n  # Wraps `self` in a `Colorize::Object` and colors it with the given *fore* color.\n  def colorize(fore : Symbol)\n    Colorize::Object.new(self).fore(fore)\n  end\nend\n\nclass Object\n  include Colorize::ObjectExtensions\nend\n\nmodule Colorize\n  alias Color = ColorANSI | Color256 | ColorRGB\n\n  # One color of a fixed set of colors.\n  enum ColorANSI\n    Default      = 39\n    Black        = 30\n    Red          = 31\n    Green        = 32\n    Yellow       = 33\n    Blue         = 34\n    Magenta      = 35\n    Cyan         = 36\n    LightGray    = 37\n    DarkGray     = 90\n    LightRed     = 91\n    LightGreen   = 92\n    LightYellow  = 93\n    LightBlue    = 94\n    LightMagenta = 95\n    LightCyan    = 96\n    White        = 97\n\n    def fore(io : IO) : Nil\n      to_i.to_s io\n    end\n\n    def back(io : IO) : Nil\n      (to_i + 10).to_s io\n    end\n  end\n\n  # An 8-bit color.\n  record Color256,\n    value : UInt8 do\n    def fore(io : IO) : Nil\n      io << \"38;5;\"\n      value.to_s io\n    end\n\n    def back(io : IO) : Nil\n      io << \"48;5;\"\n      value.to_s io\n    end\n  end\n\n  # An RGB color.\n  record ColorRGB,\n    red : UInt8,\n    green : UInt8,\n    blue : UInt8 do\n    def fore(io : IO) : Nil\n      io << \"38;2;\"\n      io << red << \";\"\n      io << green << \";\"\n      io << blue\n    end\n\n    def back(io : IO) : Nil\n      io << \"48;2;\"\n      io << red << \";\"\n      io << green << \";\"\n      io << blue\n    end\n  end\n\n  # A text decoration.\n  #\n  # Note that not all text decorations are supported in all terminals.\n  # When a text decoration is not supported, it will leave the text unaffected.\n  @[Flags]\n  enum Mode\n    # Makes the text bold.\n    #\n    # Same as `Bright`.\n    Bold = 1\n    # Makes the text color bright.\n    #\n    # Same as `Bold`.\n    Bright = 1\n    # Dims the text color.\n    Dim\n    # Draws a line below the text.\n    Underline\n    # Makes the text blink slowly.\n    Blink\n    # Swaps the foreground and background colors of the text.\n    Reverse\n    # Makes the text invisible.\n    Hidden\n    # Italicizes the text.\n    Italic\n    # Makes the text blink quickly.\n    BlinkFast\n    # Crosses out the text.\n    Strikethrough\n    # Draws two lines below the text.\n    DoubleUnderline\n    # Draws a line above the text.\n    Overline\n  end\nend\n\nprivate def each_code(mode : Colorize::Mode, &)\n  yield \"1\" if mode.bold?\n  yield \"2\" if mode.dim?\n  yield \"3\" if mode.italic?\n  yield \"4\" if mode.underline?\n  yield \"5\" if mode.blink?\n  yield \"6\" if mode.blink_fast?\n  yield \"7\" if mode.reverse?\n  yield \"8\" if mode.hidden?\n  yield \"9\" if mode.strikethrough?\n  yield \"21\" if mode.double_underline?\n  yield \"53\" if mode.overline?\nend\n\nprivate def each_reset_code(mode : Colorize::Mode, &)\n  yield \"22\" if mode.bold?\n  yield \"22\" if mode.dim?\n  yield \"23\" if mode.italic?\n  yield \"24\" if mode.underline?\n  yield \"25\" if mode.blink?\n  yield \"26\" if mode.blink_fast?\n  yield \"27\" if mode.reverse?\n  yield \"28\" if mode.hidden?\n  yield \"29\" if mode.strikethrough?\n  yield \"24\" if mode.double_underline?\n  yield \"55\" if mode.overline?\nend\n\n# A colorized object. Colors and text decorations can be modified.\nstruct Colorize::Object(T)\n  private COLORS = %w(default black red green yellow blue magenta cyan light_gray dark_gray light_red light_green light_yellow light_blue light_magenta light_cyan white)\n\n  @fore : Color\n  @back : Color\n\n  def initialize(@object : T)\n    @fore = ColorANSI::Default\n    @back = ColorANSI::Default\n    @mode = Mode::None\n    @enabled = Colorize.enabled?\n  end\n\n  {% for name in COLORS %}\n    def {{name.id}}\n      @fore = ColorANSI::{{name.camelcase.id}}\n      self\n    end\n\n    def on_{{name.id}}\n      @back = ColorANSI::{{name.camelcase.id}}\n      self\n    end\n  {% end %}\n\n  {% for mode in Mode.constants.reject { |constant| constant == \"All\" || constant == \"None\" } %}\n    # Apply text decoration `Mode::{{ mode }}`.\n    def {{mode.underscore.id}}\n      mode Mode::{{mode.id}}\n    end\n  {% end %}\n\n  def fore(color : Symbol) : self\n    {% for name in COLORS %}\n      if color == :{{name.id}}\n        @fore = ColorANSI::{{name.camelcase.id}}\n        return self\n      end\n    {% end %}\n\n    raise ArgumentError.new \"Unknown color: #{color}\"\n  end\n\n  def fore(@fore : Color) : self\n    self\n  end\n\n  def fore(fore : UInt8)\n    @fore = Color256.new(fore)\n    self\n  end\n\n  def fore(r : UInt8, g : UInt8, b : UInt8)\n    @fore = ColorRGB.new(r, g, b)\n    self\n  end\n\n  def back(color : Symbol) : self\n    {% for name in COLORS %}\n      if color == :{{name.id}}\n        @back = ColorANSI::{{name.camelcase.id}}\n        return self\n      end\n    {% end %}\n\n    raise ArgumentError.new \"Unknown color: #{color}\"\n  end\n\n  def back(@back : Color) : self\n    self\n  end\n\n  def back(back : UInt8)\n    @back = Color256.new(back)\n    self\n  end\n\n  def back(r : UInt8, g : UInt8, b : UInt8)\n    @back = ColorRGB.new(r, g, b)\n    self\n  end\n\n  # Adds *mode* to the text's decorations.\n  def mode(mode : Mode) : self\n    @mode |= mode\n    self\n  end\n\n  def on(color : Symbol)\n    back color\n  end\n\n  # Enables or disables colors and text decoration on this object.\n  def toggle(flag)\n    @enabled = !!flag\n    self\n  end\n\n  # Appends this object colored and with text decoration to *io*.\n  def to_s(io : IO) : Nil\n    surround(io) do\n      io << @object\n    end\n  end\n\n  # Inspects this object and makes the ANSI escape codes visible.\n  def inspect(io : IO) : Nil\n    surround(io) do\n      @object.inspect(io)\n    end\n  end\n\n  # Surrounds *io* by the ANSI escape codes and lets you build colored strings:\n  #\n  # ```\n  # require \"colorize\"\n  #\n  # io = IO::Memory.new\n  #\n  # Colorize.with.red.surround(io) do\n  #   io << \"colorful\"\n  #   Colorize.with.green.bold.surround(io) do\n  #     io << \" hello \"\n  #   end\n  #   Colorize.with.blue.surround(io) do\n  #     io << \"world\"\n  #   end\n  #   io << \" string\"\n  # end\n  #\n  # io.to_s # returns a colorful string where \"colorful\" is red, \"hello\" green, \"world\" blue and \" string\" red again\n  # ```\n  def surround(io = STDOUT, &)\n    return yield io unless @enabled\n\n    Object.surround(io, to_named_tuple) do |io|\n      yield io\n    end\n  end\n\n  # Prints the ANSI escape codes for an object. Note that this has no effect on a `Colorize::Object` with content,\n  # only the escape codes.\n  #\n  # ```\n  # require \"colorize\"\n  #\n  # Colorize.with.red.ansi_escape        # => \"\\e[31m\"\n  # \"hello world\".green.bold.ansi_escape # => \"\\e[32;1m\"\n  # ```\n  def ansi_escape : String\n    String.build do |io|\n      ansi_escape io\n    end\n  end\n\n  # Same as `ansi_escape` but writes to a given *io*.\n  def ansi_escape(io : IO) : Nil\n    self.class.ansi_escape(io, to_named_tuple)\n  end\n\n  private def to_named_tuple\n    {\n      fore: @fore,\n      back: @back,\n      mode: @mode,\n    }\n  end\n\n  @@last_color = {\n    fore: ColorANSI::Default.as(Color),\n    back: ColorANSI::Default.as(Color),\n    mode: Mode::None,\n  }\n\n  protected def self.ansi_escape(io : IO, color : {fore: Color, back: Color, mode: Mode}) : Nil\n    last_color = @@last_color\n    append_start(io, color)\n    @@last_color = last_color\n  end\n\n  protected def self.surround(io, color, &)\n    last_color = @@last_color\n    must_append_end = append_start(io, color)\n    @@last_color = color\n\n    begin\n      yield io\n    ensure\n      append_start(io, last_color) if must_append_end\n      @@last_color = last_color\n    end\n  end\n\n  private def self.append_start(io, color)\n    last_color_is_default =\n      @@last_color[:fore] == ColorANSI::Default &&\n        @@last_color[:back] == ColorANSI::Default &&\n        @@last_color[:mode].none?\n\n    fore = color[:fore]\n    back = color[:back]\n    mode = color[:mode]\n\n    fore_is_default = fore == ColorANSI::Default\n    back_is_default = back == ColorANSI::Default\n\n    if fore_is_default && back_is_default && mode.none? && last_color_is_default || @@last_color == color\n      false\n    else\n      io << \"\\e[\"\n\n      printed = false\n\n      unless last_color_is_default\n        unless @@last_color[:fore] == ColorANSI::Default\n          io << 39\n          printed = true\n        end\n\n        unless @@last_color[:back] == ColorANSI::Default\n          io << ';' if printed\n          io << 49\n          printed = true\n        end\n\n        unless @@last_color[:mode].none?\n          each_reset_code(@@last_color[:mode]) do |code|\n            io << ';' if printed\n            io << code\n            printed = true\n          end\n        end\n      end\n\n      unless fore_is_default\n        io << ';' if printed\n        fore.fore io\n        printed = true\n      end\n\n      unless back_is_default\n        io << ';' if printed\n        back.back io\n        printed = true\n      end\n\n      each_code(mode) do |code|\n        io << ';' if printed\n        io << code\n        printed = true\n      end\n\n      io << 'm'\n\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "src/comparable.cr",
    "content": "# The `Comparable` mixin is used by classes whose objects may be ordered.\n#\n# Including types must provide an `<=>` method, which compares the receiver against\n# another object, returning:\n# - a negative number if `self` is less than the other object\n# - a positive number if `self` is greater than the other object\n# - `0` if `self` is equal to the other object\n# - `nil` if `self` and the other object are not comparable\n#\n# `Comparable` uses `<=>` to implement the conventional comparison operators\n# (`<`, `<=`, `==`, `>=`, and `>`). All of these return `false` when `<=>`\n# returns `nil`.\n#\n# Note that returning `nil` is only useful when defining a partial comparable\n# relationship. One such example is float values: they are generally comparable,\n# except for `NaN`. If none of the values of a type are comparable between each\n# other, `Comparable` shouldn't be included.\n#\n# NOTE: When `nil` is returned from `<=>`, `Array#sort` and related sorting\n# methods will perform slightly slower.\nmodule Comparable(T)\n  # Compares this object to *other* based on the receiver’s `<=>` method,\n  # returning `true` if it returns a negative number.\n  def <(other : T) : Bool\n    cmp = self <=> other\n    cmp ? cmp < 0 : false\n  end\n\n  # Compares this object to *other* based on the receiver’s `<=>` method,\n  # returning `true` if it returns a value equal or less then `0`.\n  def <=(other : T)\n    cmp = self <=> other\n    cmp ? cmp <= 0 : false\n  end\n\n  # Compares this object to *other* based on the receiver’s `<=>` method,\n  # returning `true` if it returns `0`.\n  #\n  # Also returns `true` if this and *other* are the same object.\n  def ==(other : T)\n    if self.is_a?(Reference)\n      # Need to do two different comparisons because the compiler doesn't yet\n      # restrict something like `other.is_a?(Reference) || other.nil?`.\n      # See #2461\n      return true if other.is_a?(Reference) && self.same?(other)\n      return true if other.nil? && self.same?(other)\n    end\n\n    cmp = self <=> other\n    cmp ? cmp == 0 : false\n  end\n\n  # Compares this object to *other* based on the receiver’s `<=>` method,\n  # returning `true` if it returns a value greater then `0`.\n  def >(other : T) : Bool\n    cmp = self <=> other\n    cmp ? cmp > 0 : false\n  end\n\n  # Compares this object to *other* based on the receiver’s `<=>` method,\n  # returning `true` if it returns a value equal or greater than `0`.\n  def >=(other : T)\n    cmp = self <=> other\n    cmp ? cmp >= 0 : false\n  end\n\n  # The comparison operator. Returns `0` if the two objects are equal,\n  # a negative number if this object is considered less than *other*,\n  # a positive number if this object is considered greater than *other*,\n  # or `nil` if the two objects are not comparable.\n  #\n  # Subclasses define this method to provide class-specific ordering.\n  #\n  # The comparison operator is usually used to sort values:\n  #\n  # ```\n  # # Sort in a descending way:\n  # [3, 1, 2].sort { |x, y| y <=> x } # => [3, 2, 1]\n  #\n  # # Sort in an ascending way:\n  # [3, 1, 2].sort { |x, y| x <=> y } # => [1, 2, 3]\n  # ```\n  abstract def <=>(other : T)\n\n  # Clamps a value within *range*.\n  #\n  # ```\n  # 5.clamp(10..100)   # => 10\n  # 50.clamp(10..100)  # => 50\n  # 500.clamp(10..100) # => 100\n  #\n  # 5.clamp(10..)  # => 10\n  # 50.clamp(10..) # => 50\n  #\n  # 5.clamp(..10)  # => 5\n  # 50.clamp(..10) # => 10\n  # ```\n  def clamp(range : Range)\n    raise ArgumentError.new(\"Can't clamp an exclusive range\") if !range.end.nil? && range.exclusive?\n    clamp range.begin, range.end\n  end\n\n  # Clamps a value between *min* and *max*.\n  #\n  # ```\n  # 5.clamp(10, 100)   # => 10\n  # 50.clamp(10, 100)  # => 50\n  # 500.clamp(10, 100) # => 100\n  #\n  # 5.clamp(10, nil)  # => 10\n  # 50.clamp(10, nil) # => 50\n  #\n  # 5.clamp(nil, 10)  # => 5\n  # 50.clamp(nil, 10) # => 10\n  # ```\n  def clamp(min, max)\n    return max if !max.nil? && self > max\n    return min if !min.nil? && self < min\n    self\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/annotatable.cr",
    "content": "module Crystal\n  module Annotatable\n    # Annotations on this instance\n    property annotations : Hash(AnnotationType, Array(Annotation))?\n\n    # Adds an annotation with the given type and value\n    def add_annotation(annotation_type : AnnotationType, value : Annotation)\n      annotations = @annotations ||= {} of AnnotationType => Array(Annotation)\n      annotations[annotation_type] ||= [] of Annotation\n      annotations[annotation_type] << value\n    end\n\n    # Returns the last defined annotation with the given type, if any, or `nil` otherwise\n    def annotation(annotation_type : AnnotationType) : Annotation?\n      @annotations.try &.[annotation_type]?.try &.last?\n    end\n\n    # Returns all annotations with the given type, if any, or `nil` otherwise\n    def annotations(annotation_type : AnnotationType) : Array(Annotation)?\n      @annotations.try &.[annotation_type]?\n    end\n\n    # Returns all annotations on this type, if any, or `nil` otherwise\n    def all_annotations : Array(Annotation)?\n      @annotations.try &.values.flatten\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/aarch64.cr",
    "content": "require \"../abi\"\n\n# Based on\n# https://github.com/rust-lang/rust/blob/master/src/librustc_trans/cabi_aarch64.rs\nclass Crystal::ABI::AArch64 < Crystal::ABI\n  def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : LLVM::Context) : Crystal::ABI::FunctionType\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = atys.map { |aty| compute_arg_type(aty, context) }\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  def align(type : LLVM::Type) : Int32\n    align(type, 8)\n  end\n\n  def size(type : LLVM::Type) : Int32\n    size(type, 8)\n  end\n\n  def homogeneous_aggregate?(type)\n    homog_agg : {LLVM::Type, UInt64}? = case type.kind\n    when LLVM::Type::Kind::Float\n      return {type, 1_u64}\n    when LLVM::Type::Kind::Double\n      return {type, 1_u64}\n    when LLVM::Type::Kind::Array\n      check_array(type)\n    when LLVM::Type::Kind::Struct\n      check_struct(type)\n    end\n\n    # Ensure we have at most four uniquely addressable members\n    if homog_agg\n      if 0 < homog_agg[1] <= 4\n        return homog_agg\n      end\n    end\n  end\n\n  private def check_array(type)\n    len = type.array_size.to_u64\n    return if len == 0\n    element = type.element_type\n\n    # if our element is an HFA/HVA, so are we; multiply members by our len\n    if homog_agg = homogeneous_aggregate?(element)\n      base_type, members = homog_agg\n      {base_type, len * members}\n    end\n  end\n\n  private def check_struct(type)\n    elements = type.struct_element_types\n    return if elements.empty?\n\n    base_type = nil\n    members = 0_u64\n\n    elements.each do |element|\n      opt_homog_agg = homogeneous_aggregate?(element)\n\n      # field isn't itself an HFA, so we aren't either\n      return unless opt_homog_agg\n      field_type, field_members = opt_homog_agg\n\n      if !base_type\n        # first field - store its type and number of members\n        base_type = field_type\n        members = field_members\n      else\n        # 2nd or later field - give up if it's a different type; otherwise incr. members\n        return unless base_type == field_type\n        members += field_members\n      end\n    end\n\n    return unless base_type\n\n    if size(type) == size(base_type) * members\n      {base_type, members}\n    end\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif register?(rty)\n      non_struct(rty, context)\n    elsif homog_agg = homogeneous_aggregate?(rty)\n      base_type, members = homog_agg\n      ArgType.direct(rty, base_type.array(members))\n    else\n      size = size(rty)\n      if size <= 16\n        cast = if size <= 1\n                 context.int8\n               elsif size <= 2\n                 context.int16\n               elsif size <= 4\n                 context.int32\n               elsif size <= 8\n                 context.int64\n               else\n                 context.int64.array(((size + 7) // 8).to_u64)\n               end\n        ArgType.direct(rty, cast)\n      else\n        ArgType.indirect(rty, LLVM::Attribute::StructRet)\n      end\n    end\n  end\n\n  private def compute_arg_type(aty, context)\n    if register?(aty)\n      non_struct(aty, context)\n    elsif homog_agg = homogeneous_aggregate?(aty)\n      base_type, members = homog_agg\n      ArgType.direct(aty, base_type.array(members))\n    else\n      size = size(aty)\n      if size <= 16\n        cast = if size == 0\n                 context.int64.array(0)\n               elsif size <= 1\n                 context.int8\n               elsif size <= 2\n                 context.int16\n               elsif size <= 4\n                 context.int32\n               elsif size <= 8\n                 context.int64\n               else\n                 context.int64.array(((size + 7) // 8).to_u64)\n               end\n        ArgType.direct(aty, cast)\n      else\n        ArgType.indirect(aty, nil)\n      end\n    end\n  end\n\n  def register?(type) : Bool\n    case type.kind\n    when LLVM::Type::Kind::Integer,\n         LLVM::Type::Kind::Float,\n         LLVM::Type::Kind::Double,\n         LLVM::Type::Kind::Pointer\n      true\n    else\n      false\n    end\n  end\n\n  private def non_struct(type, context)\n    attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n    ArgType.direct(type, attr: attr)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/arm.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/dfe8bd10fe6763e0a1d5d55fa2574ecba27d3e2e/src/librustc_trans/cabi_arm.rs\nclass Crystal::ABI::ARM < Crystal::ABI\n  def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : LLVM::Context) : Crystal::ABI::FunctionType\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  def align(type : LLVM::Type) : Int32\n    align(type, 4)\n  end\n\n  def size(type : LLVM::Type) : Int32\n    size(type, 4)\n  end\n\n  def register?(type) : Bool\n    case type.kind\n    when LLVM::Type::Kind::Integer, LLVM::Type::Kind::Float, LLVM::Type::Kind::Double, LLVM::Type::Kind::Pointer\n      true\n    else\n      false\n    end\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif register?(rty)\n      non_struct(rty, context)\n    else\n      case size(rty)\n      when 1\n        ArgType.direct(rty, context.int8)\n      when 2\n        ArgType.direct(rty, context.int16)\n      when 3, 4\n        ArgType.direct(rty, context.int32)\n      else\n        ArgType.indirect(rty, LLVM::Attribute::StructRet)\n      end\n    end\n  end\n\n  private def compute_arg_types(atys, context)\n    atys.map do |aty|\n      if register?(aty)\n        non_struct(aty, context)\n      else\n        if align(aty) <= 4\n          ArgType.direct(aty, context.int32.array(((size(aty) + 3) // 4).to_u64))\n        else\n          ArgType.direct(aty, context.int64.array(((size(aty) + 7) // 8).to_u64))\n        end\n      end\n    end\n  end\n\n  private def non_struct(type, context)\n    attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n    ArgType.direct(type, attr: attr)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/avr.cr",
    "content": "require \"../abi\"\n\nclass Crystal::ABI::AVR < Crystal::ABI\n  AVRTINY = StaticArray[\n    \"attiny4\",\n    \"attiny5\",\n    \"attiny9\",\n    \"attiny10\",\n    \"attiny102\",\n    \"attiny104\",\n    \"attiny20\",\n    \"attiny40\",\n  ]\n\n  def initialize(target_machine : LLVM::TargetMachine, mcpu : String? = nil)\n    super target_machine\n\n    # \"Reduced Tiny\" core devices only have 16 General Purpose Registers\n    if mcpu.in?(AVRTINY)\n      @rsize = 4 # values above 4 bytes are returned by memory\n      @rmin = 20 # 6 registers for call arguments (R25..R20)\n    else\n      @rsize = 8 # values above 8 bytes are returned by memory\n      @rmin = 8  # 18 registers for call arguments (R25..R8)\n    end\n  end\n\n  def align(type : LLVM::Type) : Int32\n    target_data.abi_alignment(type).to_i32\n  end\n\n  def size(type : LLVM::Type) : Int32\n    target_data.abi_size(type).to_i32\n  end\n\n  # Follows AVR GCC, while Clang (and Rust) are incompatible, despite LLVM\n  # itself being compliant.\n  #\n  # - <https://gcc.gnu.org/wiki/avr-gcc>\n  # - <https://bugs.llvm.org/show_bug.cgi?id=46140>\n  def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : LLVM::Context) : Crystal::ABI::FunctionType\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  # Pass in registers unless the returned type is a struct larger than 8 bytes\n  # (4 bytes on reduced tiny cores).\n  #\n  # Rust & Clang always return a struct _indirectly_.\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif size(rty) > @rsize\n      ArgType.indirect(rty, LLVM::Attribute::StructRet)\n    else\n      # let the LLVM AVR backend handle the pw2ceil padding of structs\n      ArgType.direct(rty)\n    end\n  end\n\n  # Fill the R25 -> R8 registers (R20 on reduced tiny cores), rounding odd byte\n  # sizes to the next even number, then pass by memory (indirect), so {i8, i32}\n  # are passed as R24 then R20..R23 (LSB -> MSB) for example.\n  #\n  # Rust & Clang always pass structs _indirectly_.\n  private def compute_arg_types(atys, context)\n    rn = 26\n    atys.map do |aty|\n      bytes = size(aty)\n      bytes += 1 if bytes.odd?\n      rn -= bytes\n\n      if bytes == 0 || rn < @rmin\n        ArgType.indirect(aty, LLVM::Attribute::StructRet)\n      else\n        # let the LLVM AVR backend handle the odd to next even number padding\n        ArgType.direct(aty)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/wasm32.cr",
    "content": "require \"../abi\"\n\nclass Crystal::ABI::Wasm32 < Crystal::ABI\n  def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : LLVM::Context)\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  def align(type : LLVM::Type)\n    target_data.abi_alignment(type).to_i32\n  end\n\n  def size(type : LLVM::Type)\n    target_data.abi_size(type).to_i32\n  end\n\n  private def aggregate?(type)\n    case type.kind\n    when .struct?, .array?\n      true\n    else\n      false\n    end\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if aggregate?(rty)\n      ArgType.indirect(rty, LLVM::Attribute::ByVal)\n    else\n      ArgType.direct(rty)\n    end\n  end\n\n  private def compute_arg_types(atys, context)\n    atys.map do |t|\n      if aggregate?(t)\n        ArgType.indirect(t, LLVM::Attribute::ByVal)\n      else\n        ArgType.direct(t)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/x86.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi_x86.rs\nclass Crystal::ABI::X86 < Crystal::ABI\n  def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : LLVM::Context)\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new arg_tys, ret_ty\n  end\n\n  def size(type : LLVM::Type)\n    target_data.abi_size(type).to_i32\n  end\n\n  def align(type : LLVM::Type)\n    target_data.abi_alignment(type).to_i32\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif rty.kind == LLVM::Type::Kind::Struct\n      # Returning a structure. Most often, this will use\n      # a hidden first argument. On some platforms, though,\n      # small structs are returned as integers.\n      #\n      # Some links:\n      # http://www.angelcode.com/dev/callconv/callconv.html\n      # Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp\n\n      if osx? || windows?\n        case target_data.abi_size(rty)\n        when 1 then ret_value(rty, context.int8)\n        when 2 then ret_value(rty, context.int16)\n        when 4 then ret_value(rty, context.int32)\n        when 8 then ret_value(rty, context.int64)\n        else        ret_pointer(rty)\n        end\n      else\n        ret_pointer(rty)\n      end\n    else\n      non_struct(rty, context)\n    end\n  end\n\n  private def compute_arg_types(atys, context)\n    atys.map do |t|\n      case t.kind\n      when LLVM::Type::Kind::Struct\n        size = target_data.abi_size(t)\n        if size == 0\n          ArgType.ignore(t)\n        else\n          ArgType.indirect(t, LLVM::Attribute::ByVal)\n        end\n      else\n        non_struct(t, context)\n      end\n    end\n  end\n\n  private def ret_value(type, cast)\n    ArgType.direct(type, cast)\n  end\n\n  private def ret_pointer(type)\n    ArgType.indirect(type, LLVM::Attribute::StructRet)\n  end\n\n  private def non_struct(type, context)\n    attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n    ArgType.direct(type, attr: attr)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/x86_64.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi_x86_64.rs\n# See also, section 3.2.3 of the System V Application Binary Interface AMD64 Architecture Processor Supplement\nclass Crystal::ABI::X86_64 < Crystal::ABI\n  MAX_INT_REGS = 6 # %rdi, %rsi, %rdx, %rcx, %r8, %r9\n  MAX_SSE_REGS = 8 # %xmm0-%xmm7\n\n  def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : LLVM::Context) : Crystal::ABI::FunctionType\n    # registers available to pass arguments directly: int_regs can hold integers\n    # and pointers, sse_regs can hold floats and doubles\n    available_int_regs = MAX_INT_REGS\n    available_sse_regs = MAX_SSE_REGS\n\n    if ret_def\n      ret_ty, _, _ = x86_64_type(rty, LLVM::Attribute::StructRet, context) { |cls| sret?(cls) }\n      if ret_ty.kind.indirect?\n        # return value is a caller-allocated struct which is passed in %rdi,\n        # so we have 1 less register available for passing arguments\n        available_int_regs -= 1\n      end\n    else\n      ret_ty = ArgType.direct(context.void)\n    end\n\n    arg_tys = atys.map do |arg_type|\n      abi_type, needed_int_regs, needed_sse_regs = x86_64_type(arg_type, LLVM::Attribute::ByVal, context) { |cls| pass_by_val?(cls) }\n      if available_int_regs >= needed_int_regs && available_sse_regs >= needed_sse_regs\n        available_int_regs -= needed_int_regs\n        available_sse_regs -= needed_sse_regs\n        abi_type\n      elsif !register?(arg_type)\n        # no available registers to pass the argument, but only mark aggregates\n        # as indirect byval types because LLVM will automatically pass register\n        # types in the stack\n        ArgType.indirect(arg_type, LLVM::Attribute::ByVal)\n      else\n        abi_type\n      end\n    end\n\n    FunctionType.new arg_tys, ret_ty\n  end\n\n  # returns the LLVM type (with attributes) and the number of integer and SSE\n  # registers needed to pass this value directly (ie. not using the stack)\n  def x86_64_type(type, ind_attr, context, &) : Tuple(ArgType, Int32, Int32)\n    if int_register?(type)\n      attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n      {ArgType.direct(type, attr: attr), 1, 0}\n    elsif sse_register?(type)\n      {ArgType.direct(type), 0, 1}\n    else\n      cls = classify(type)\n      if yield cls\n        {ArgType.indirect(type, ind_attr), 0, 0}\n      else\n        needed_int_regs = cls.count(&.int?)\n        needed_sse_regs = cls.count(&.sse?)\n        {ArgType.direct(type, llreg(context, cls)), needed_int_regs, needed_sse_regs}\n      end\n    end\n  end\n\n  def register?(type) : Bool\n    int_register?(type) || sse_register?(type)\n  end\n\n  def int_register?(type) : Bool\n    case type.kind\n    when LLVM::Type::Kind::Integer, LLVM::Type::Kind::Pointer\n      true\n    else\n      false\n    end\n  end\n\n  def sse_register?(type) : Bool\n    case type.kind\n    when LLVM::Type::Kind::Float, LLVM::Type::Kind::Double\n      true\n    else\n      false\n    end\n  end\n\n  def pass_by_val?(cls) : Bool\n    return false if cls.empty?\n\n    cl = cls.first\n    cl.in?(RegClass::Memory, RegClass::X87, RegClass::ComplexX87)\n  end\n\n  def sret?(cls) : Bool\n    return false if cls.empty?\n\n    cls.first == RegClass::Memory\n  end\n\n  def classify(type)\n    words = (size(type) + 7) // 8\n    reg_classes = Array.new(words, RegClass::NoClass)\n    if words > 4 || has_misaligned_fields?(type)\n      all_mem(reg_classes)\n    else\n      classify(type, reg_classes, 0, 0)\n      fixup(type, reg_classes)\n    end\n    reg_classes\n  end\n\n  def classify(ty, cls, ix, off)\n    t_align = align(ty)\n    t_size = size(ty)\n\n    misalign = off % t_align\n    if misalign != 0\n      i = off // 8\n      e = (off + t_size + 7) // 8\n      while i < e\n        unify(cls, ix + i, RegClass::Memory)\n        i += 1\n      end\n      return\n    end\n\n    case ty.kind\n    when LLVM::Type::Kind::Integer, LLVM::Type::Kind::Pointer\n      unify(cls, ix + off // 8, RegClass::Int)\n    when LLVM::Type::Kind::Float\n      unify(cls, ix + off // 8, (off % 8 == 4) ? RegClass::SSEFv : RegClass::SSEFs)\n    when LLVM::Type::Kind::Double\n      unify(cls, ix + off // 8, RegClass::SSEDs)\n    when LLVM::Type::Kind::Struct\n      classify_struct(ty.struct_element_types, cls, ix, off, ty.packed_struct?)\n    when LLVM::Type::Kind::Array\n      len = ty.array_size\n      elt = ty.element_type\n      eltsz = size(elt)\n      i = 0\n      while i < len\n        classify(elt, cls, ix, off + i * eltsz)\n        i += 1\n      end\n    else\n      raise \"Unhandled LLVM::Type::Kind in classify: #{ty.kind}\"\n    end\n  end\n\n  def classify_struct(tys, cls, i, off, packed) : Nil\n    field_off = off\n    tys.each do |ty|\n      field_off = align_offset(field_off, ty) unless packed\n      classify(ty, cls, i, field_off)\n      field_off += size(ty)\n    end\n  end\n\n  def fixup(ty, cls) : Nil\n    i = 0\n    ty_kind = ty.kind\n    e = cls.size\n    if e > 2 && ty_kind.in?(LLVM::Type::Kind::Struct, LLVM::Type::Kind::Array)\n      if cls[i].sse?\n        i += 1\n        while i < e\n          if cls[i] != RegClass::SSEUp\n            all_mem(cls)\n            return\n          end\n          i += 1\n        end\n      else\n        all_mem(cls)\n        return\n      end\n    else\n      while i < e\n        case\n        when cls[i] == RegClass::Memory\n          all_mem(cls)\n          return\n        when cls[i] == RegClass::X87Up\n          # for darwin\n          # cls[i] = RegClass::SSEDs\n          all_mem(cls)\n          return\n        when cls[i] == RegClass::SSEUp\n          cls[i] = RegClass::SSEDv\n        when cls[i].sse?\n          i += 1\n          while i != e && cls[i] == RegClass::SSEUp\n            i += 1\n          end\n        when cls[i] == RegClass::X87\n          i += 1\n          while i != e && cls[i] == RegClass::X87Up\n            i += 1\n          end\n        else\n          i += 1\n        end\n      end\n    end\n  end\n\n  def unify(cls, i, newv)\n    case\n    when cls[i] == newv\n      return\n    when cls[i] == RegClass::NoClass\n      cls[i] = newv\n    when newv == RegClass::NoClass\n      return\n    when cls[i] == RegClass::Memory, newv == RegClass::Memory\n      return\n    when cls[i] == RegClass::Int, newv == RegClass::Int\n      return\n    when cls[i] == RegClass::X87,\n         cls[i] == RegClass::X87Up,\n         cls[i] == RegClass::ComplexX87,\n         newv == RegClass::X87,\n         newv == RegClass::X87Up,\n         newv == RegClass::ComplexX87\n      cls[i] = RegClass::Memory\n    else\n      cls[i] = newv\n    end\n  end\n\n  def all_mem(reg_classes)\n    reg_classes.fill(RegClass::Memory)\n  end\n\n  def llreg(context, reg_classes) : LLVM::Type\n    types = Array(LLVM::Type).new\n    i = 0\n    e = reg_classes.size\n    while i < e\n      case reg_classes[i]\n      when RegClass::Int\n        types << context.int64\n      when RegClass::SSEFv\n        vec_len = llvec_len(reg_classes[i + 1..-1])\n        vec_type = context.float.vector(vec_len * 2)\n        types << vec_type\n        i += vec_len\n        next\n      when RegClass::SSEFs\n        types << context.float\n      when RegClass::SSEDs\n        types << context.double\n      else\n        raise \"Unhandled RegClass: #{reg_classes[i]}\"\n      end\n      i += 1\n    end\n    context.struct(types)\n  end\n\n  def llvec_len(reg_classes) : Int32\n    len = 1\n    reg_classes.each do |reg_class|\n      break if reg_class != RegClass::SSEUp\n      len += 1\n    end\n    len\n  end\n\n  def align(type : LLVM::Type) : Int32\n    align(type, 8)\n  end\n\n  def size(type : LLVM::Type) : Int32\n    size(type, 8)\n  end\n\n  def has_misaligned_fields?(type : LLVM::Type, offset : Int = 0) : Bool\n    case type.kind\n    when LLVM::Type::Kind::Struct\n      type.struct_element_types.each do |elem|\n        offset = align_offset(offset, elem) unless type.packed_struct?\n        return true unless offset.divisible_by?(align(elem))\n        return true if has_misaligned_fields?(elem, offset)\n        offset += size(elem)\n      end\n      false\n    when LLVM::Type::Kind::Array\n      # Given:\n      #\n      # ```\n      # @[Packed]\n      # struct Foo\n      #   x : Int16\n      #   y : Int8\n      # end\n      # ```\n      #\n      # the types `Foo` and `Foo[1]` have no misaligned fields, but `Foo[2]`\n      # does, because the field `.[1].x` has offset 3 and a natural alignment of\n      # 2. Checking for the first two elements is sufficient; if both contain no\n      # misaligned fields, then `size(elem) % align(elem) == 0` must be true,\n      # meaning array indices have no effect on element alignment.\n      elem = type.element_type\n      has_misaligned_fields?(elem, offset) || type.array_size > 1 && has_misaligned_fields?(elem, offset + size(elem))\n    else\n      false\n    end\n  end\n\n  enum RegClass\n    NoClass\n    Int\n    SSEFs\n    SSEFv\n    SSEDs\n    SSEDv\n    SSEInt\n    SSEUp\n    X87\n    X87Up\n    ComplexX87\n    Memory\n\n    def sse? : Bool\n      case self\n      when SSEFs, SSEFv, SSEDs\n        true\n      else\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi/x86_win64.cr",
    "content": "require \"./x86\"\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi_x86_win64.rs\nclass Crystal::ABI::X86_Win64 < Crystal::ABI::X86\n  private def compute_arg_types(atys, context)\n    atys.map do |t|\n      case t.kind\n      when LLVM::Type::Kind::Struct\n        size = target_data.abi_size(t)\n        case size\n        when 1 then ArgType.direct(t, context.int8)\n        when 2 then ArgType.direct(t, context.int16)\n        when 4 then ArgType.direct(t, context.int32)\n        when 8 then ArgType.direct(t, context.int64)\n        else        ArgType.indirect(t, LLVM::Attribute::ByVal)\n        end\n      else\n        non_struct(t, context)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/abi.cr",
    "content": "# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi.rs\nabstract class Crystal::ABI\n  getter target_data : LLVM::TargetData\n  getter? osx : Bool\n  getter? windows : Bool\n\n  def initialize(target_machine : LLVM::TargetMachine)\n    @target_data = target_machine.data_layout\n    triple = target_machine.triple\n    @osx = !!(triple =~ /apple/)\n    @windows = !!(triple =~ /windows/)\n  end\n\n  def self.from(target_machine : LLVM::TargetMachine) : self\n    triple = target_machine.triple\n    case triple\n    when /x86_64.+windows-(?:msvc|gnu)/\n      X86_Win64.new(target_machine)\n    when /x86_64|amd64/\n      X86_64.new(target_machine)\n    when /i386|i486|i586|i686/\n      X86.new(target_machine)\n    when /aarch64|arm64/\n      AArch64.new(target_machine)\n    when /arm/\n      ARM.new(target_machine)\n    when /avr/\n      AVR.new(target_machine, target_machine.cpu)\n    when /wasm32/\n      Wasm32.new(target_machine)\n    else\n      raise \"Unsupported ABI for target triple: #{triple}\"\n    end\n  end\n\n  abstract def abi_info(atys : Array(LLVM::Type), rty : LLVM::Type, ret_def : Bool, context : Context)\n  abstract def size(type : LLVM::Type)\n  abstract def align(type : LLVM::Type)\n\n  def size(type : LLVM::Type, pointer_size) : Int32\n    case type.kind\n    when LLVM::Type::Kind::Integer\n      (type.int_width + 7) // 8\n    when LLVM::Type::Kind::Float\n      4\n    when LLVM::Type::Kind::Double\n      8\n    when LLVM::Type::Kind::Pointer\n      pointer_size\n    when LLVM::Type::Kind::Struct\n      if type.packed_struct?\n        type.struct_element_types.reduce(0) do |memo, elem|\n          memo + size(elem)\n        end\n      else\n        size = type.struct_element_types.reduce(0) do |memo, elem|\n          align_offset(memo, elem) + size(elem)\n        end\n        align_offset(size, type)\n      end\n    when LLVM::Type::Kind::Array\n      size(type.element_type) * type.array_size\n    else\n      raise \"Unhandled LLVM::Type::Kind in size: #{type.kind}\"\n    end\n  end\n\n  def align_offset(offset, type) : Int32\n    align = align(type)\n    (offset + align - 1) // align * align\n  end\n\n  def align(type : LLVM::Type, pointer_size) : Int32\n    case type.kind\n    when LLVM::Type::Kind::Integer\n      (type.int_width + 7) // 8\n    when LLVM::Type::Kind::Float\n      4\n    when LLVM::Type::Kind::Double\n      8\n    when LLVM::Type::Kind::Pointer\n      pointer_size\n    when LLVM::Type::Kind::Struct\n      if type.packed_struct?\n        1\n      else\n        type.struct_element_types.reduce(1) do |memo, elem|\n          Math.max(memo, align(elem))\n        end\n      end\n    when LLVM::Type::Kind::Array\n      align(type.element_type)\n    else\n      raise \"Unhandled LLVM::Type::Kind in align: #{type.kind}\"\n    end\n  end\n\n  enum ArgKind\n    Direct\n    Indirect\n    Ignore\n  end\n\n  struct ArgType\n    getter kind : ArgKind\n    getter type : LLVM::Type\n    getter cast : LLVM::Type?\n    getter pad : Nil\n    getter attr : LLVM::Attribute?\n\n    def self.direct(type, cast = nil, pad = nil, attr = nil)\n      new ArgKind::Direct, type, cast, pad, attr\n    end\n\n    def self.indirect(type, attr) : self\n      new ArgKind::Indirect, type, attr: attr\n    end\n\n    def self.ignore(type) : self\n      new ArgKind::Ignore, type\n    end\n\n    def initialize(@kind, @type, @cast = nil, @pad = nil, @attr = nil)\n    end\n  end\n\n  class FunctionType\n    getter arg_types : Array(ArgType)\n    getter return_type : ArgType\n\n    def initialize(@arg_types, @return_type)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/asm.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  def visit(node : Asm)\n    constraints = IO::Memory.new\n\n    if outputs = node.outputs\n      ptrofs = node.output_ptrofs.not_nil!\n\n      output_types = [] of LLVM::Type\n      outputs.each_with_index do |output, i|\n        constraints << ',' if i > 0\n        constraints << output.constraint\n\n        output_types << llvm_type(ptrofs[i].type.as(PointerInstanceType).element_type)\n      end\n\n      if output_types.size > 1\n        output_type = @llvm_context.struct(output_types)\n      else\n        output_type = output_types[0]\n      end\n    else\n      output_type = llvm_context.void\n    end\n\n    input_types = [] of LLVM::Type\n    input_values = [] of LLVM::Value\n\n    if inputs = node.inputs\n      constraints << ',' unless constraints.empty?\n\n      inputs.each_with_index do |input, i|\n        accept input.exp\n        input_types << llvm_type(input.exp.type)\n        input_values << @last\n        constraints << ',' if i > 0\n        constraints << input.constraint\n      end\n    end\n\n    if clobbers = node.clobbers\n      constraints << ',' unless constraints.empty?\n\n      clobbers.each_with_index do |clobber, i|\n        constraints << ',' if i > 0\n        constraints << \"~{\"\n        constraints << clobber\n        constraints << '}'\n      end\n    end\n\n    fun_type = LLVM::Type.function(input_types, output_type)\n    constraints = constraints.to_s\n\n    value = fun_type.inline_asm(node.text, constraints, node.volatile?, node.alignstack?, node.can_throw?, node.dialect)\n    value = LLVM::Function.from_value(value)\n    asm_value = call LLVMTypedFunction.new(fun_type, value), input_values\n\n    if ptrofs = node.output_ptrofs\n      if ptrofs.size > 1\n        ptrofs.each_with_index do |ptrof, i|\n          accept ptrof\n          store extract_value(asm_value, i), @last\n        end\n      else\n        accept ptrofs[0]\n        store asm_value, @last\n      end\n    end\n\n    @last = llvm_nil\n\n    false\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/ast.cr",
    "content": "require \"../syntax/ast\"\n\nmodule Crystal\n  class ASTNode\n    def no_returns?\n      !!type?.try &.no_return?\n    end\n  end\n\n  class Def\n    property? abi_info = false\n\n    def mangled_name(program, self_type)\n      name = String.build do |str|\n        str << '*'\n\n        if owner = @owner\n          if owner.metaclass?\n            self_type.instance_type.llvm_name(str)\n            if original_owner != self_type\n              str << '@'\n              original_owner.instance_type.llvm_name(str)\n            end\n            str << \"::\"\n          elsif !owner.is_a?(Crystal::Program)\n            self_type.llvm_name(str)\n            if original_owner != self_type\n              str << '@'\n              original_owner.llvm_name(str)\n            end\n            str << '#'\n          end\n        end\n\n        str << self.name.gsub('@', '.')\n\n        next_def = self.next\n        while next_def\n          str << '\\''\n          next_def = next_def.next\n        end\n\n        if args.size > 0 || uses_block_arg?\n          str << '<'\n          if args.size > 0\n            args.each_with_index do |arg, i|\n              str << \", \" if i > 0\n              arg.type.llvm_name(str)\n            end\n          end\n          if uses_block_arg?\n            str << \", \" if args.size > 0\n            str << '&'\n            block_arg.not_nil!.type.llvm_name(str)\n          end\n          str << '>'\n        end\n        if return_type = @type\n          str << ':'\n          return_type.llvm_name(str)\n        end\n      end\n\n      Crystal.safe_mangling(program, name)\n    end\n\n    def varargs?\n      false\n    end\n\n    def call_convention\n      nil\n    end\n\n    @c_calling_convention : Bool? = nil\n    property c_calling_convention\n\n    # Returns `self` as an `External` if this Def is an External\n    # that must respect the C calling convention.\n    def c_calling_convention?\n      if @c_calling_convention.nil?\n        @c_calling_convention = compute_c_calling_convention\n      end\n\n      @c_calling_convention ? self : nil\n    end\n\n    def llvm_intrinsic?\n      self.is_a?(External) && self.real_name.starts_with?(\"llvm.\")\n    end\n\n    private def compute_c_calling_convention\n      # One case where this is not true if for LLVM intrinsics.\n      # For example overflow intrinsics return a tuple, like {i32, i1}:\n      # in C ABI that is represented as i64, but we need to keep the original\n      # type here, respecting LLVM types, not the C ABI.\n      if self.is_a?(External)\n        return !self.real_name.starts_with?(\"llvm.\")\n      end\n\n      # Another case is when an argument is an external struct, in which\n      # case we must respect the C ABI (this applies to Crystal methods\n      # and procs too)\n\n      # Only applicable to procs (no owner) for now\n      owner = @owner\n      if owner\n        return false\n      end\n\n      proc_c_calling_convention?\n    end\n\n    def proc_c_calling_convention?\n      # We use C ABI if:\n      # - all arguments are allowed in lib calls (because then it can be passed to C)\n      # - at least one argument type, or the return type, is an extern struct\n      found_extern = false\n\n      if (type = self.type?)\n        type = type.remove_alias\n        if type.extern?\n          found_extern = true\n        elsif !type.void? && !type.nil_type? && !type.allowed_in_lib?\n          return false\n        end\n      end\n\n      args.each do |arg|\n        arg_type = arg.type.remove_alias\n        if arg_type.extern?\n          found_extern = true\n        elsif !arg_type.allowed_in_lib?\n          return false\n        end\n      end\n\n      found_extern\n    end\n  end\n\n  class Asm\n    def dialect : LLVM::InlineAsmDialect\n      intel? ? LLVM::InlineAsmDialect::Intel : LLVM::InlineAsmDialect::ATT\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/cache_dir.cr",
    "content": "module Crystal\n  # Manages cache files in the \".crystal\" directory.\n  #\n  # For each compiled program a directory is created in the cache\n  # that stores .bc and .o files that could possibly be reused\n  # from a previous compilation.\n  #\n  # To keep the cache dir small, only the 10 most recently used\n  # directories are kept. We use the directory's modification\n  # time for this.\n  class CacheDir\n    def self.instance\n      @@instance ||= new\n    end\n\n    @dir : String?\n\n    private def initialize\n    end\n\n    # Returns the directory where cache files related to the\n    # given sources will be stored. The directory will be\n    # created if it doesn't exist.\n    def directory_for(sources : Array(Compiler::Source))\n      directory_for(sources.first.filename)\n    end\n\n    # Returns the directory where cache files related to the\n    # given filenames will be stored. The directory will be\n    # created if it doesn't exist.\n    def directory_for(filename : String)\n      dir = compute_dir\n\n      filename = ::Path[filename]\n      name = String.build do |io|\n        filename.each_part do |part|\n          if io.empty?\n            if part == \"#{filename.anchor}\"\n              part = \"#{filename.drive}\"[..0]\n            end\n          else\n            io << '-'\n          end\n          io << part\n        end\n      end\n      output_dir = File.join(dir, name)\n      Dir.mkdir_p(output_dir)\n      output_dir\n    end\n\n    # Keeps the 10 most recently used directories in the cache,\n    # and removes all others.\n    def cleanup\n      dir = compute_dir\n      entries = gather_cache_entries(dir)\n      cleanup_dirs(entries)\n    end\n\n    # Returns a filename that has prepended the cache directory.\n    def join(filename)\n      dir = compute_dir\n      File.join(dir, filename)\n    end\n\n    # Returns the cache directory.\n    def dir\n      compute_dir\n    end\n\n    private def compute_dir\n      dir = @dir\n      return dir if dir\n\n      # Try to use one of these as a cache directory, in order\n      candidates = {% begin %}\n        [\n          ENV[\"CRYSTAL_CACHE_DIR\"]?,\n          {% if flag?(:windows) %}\n            ENV[\"LOCALAPPDATA\"]?.try { |dir| \"#{dir}/crystal/cache\" },\n            ENV[\"USERPROFILE\"]?.try { |home| \"#{home}/.cache/crystal\" },\n            ENV[\"USERPROFILE\"]?.try { |home| \"#{home}/.crystal\" },\n          {% else %}\n            ENV[\"XDG_CACHE_HOME\"]?.try { |home| \"#{home}/crystal\" },\n            ENV[\"HOME\"]?.try { |home| \"#{home}/.cache/crystal\" },\n            ENV[\"HOME\"]?.try { |home| \"#{home}/.crystal\" },\n          {% end %}\n          \".crystal\",\n        ]\n      {% end %}\n      candidates = candidates\n        .compact\n        .map! { |file| File.expand_path(file) }\n        .uniq!\n\n      # Return the first one for which we could create a directory\n      candidates.each do |candidate|\n        Dir.mkdir_p(candidate)\n        return @dir = candidate\n      rescue File::Error\n        # Try next one\n      end\n\n      msg = String.build do |io|\n        io.puts \"Error: can't create cache directory.\"\n        io.puts\n        io.puts \"Crystal needs a cache directory. These directories were candidates for it:\"\n        io.puts\n        candidates.each do |candidate|\n          io << \" - \" << candidate << '\\n'\n        end\n        io.puts\n        io.puts \"but none of them are writable.\"\n        io.puts\n        io.puts \"Please specify a writable cache directory by setting the CRYSTAL_CACHE_DIR environment variable.\"\n      end\n\n      puts msg\n      exit 1\n    end\n\n    private def cleanup_dirs(entries)\n      entries\n        .select { |dir| Dir.exists?(dir) }\n        .sort_by! { |dir| File.info?(dir).try(&.modification_time) || Time.unix(0) }\n        .reverse!\n        .skip(10)\n        .each { |name| FileUtils.rm_rf(name) }\n    end\n\n    private def gather_cache_entries(dir)\n      Dir.children(dir).map! { |name| File.join(dir, name) }\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/call.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  def visit(node : Call)\n    if node.expanded\n      raise \"BUG: #{node} at #{node.location} should have been expanded\"\n    end\n\n    target_defs = node.target_defs\n    unless target_defs\n      node.raise \"BUG: no target defs\"\n    end\n\n    if target_defs.size > 1\n      codegen_dispatch node, target_defs\n      return false\n    end\n\n    owner = node.super? ? node.scope : node.target_def.owner\n\n    call_args = prepare_call_args node, owner\n\n    # It can happen that one of the arguments caused an unreachable\n    # to happen, so we must stop here\n    return false if @builder.end\n\n    if block = node.block\n      # A block might turn into a proc literal but not be used if it participates in a dispatch\n      if (fun_literal = block.fun_literal) && node.target_def.uses_block_arg?\n        codegen_call_with_block_as_fun_literal(node, fun_literal, owner, call_args)\n      else\n        codegen_call_with_block(node, block, owner, call_args)\n      end\n    else\n      codegen_call(node, node.target_def, owner, call_args)\n    end\n\n    false\n  end\n\n  def prepare_call_args(node, owner)\n    target_def = node.target_def\n    if external = target_def.c_calling_convention?\n      prepare_call_args_external(node, external, owner)\n    else\n      prepare_call_args_non_external(node, target_def, owner)\n    end\n  end\n\n  def prepare_call_args_non_external(node, target_def, owner)\n    is_primitive = target_def.body.is_a?(Primitive)\n\n    call_args = Array(LLVM::Value).new(node.args.size + 1)\n    old_needs_value = @needs_value\n\n    obj = node.obj\n\n    case obj\n    when Path\n      # Non-generic metaclasses and lib types do not need a self argument,\n      # reading them should not have side effects unless `obj` turns out to be\n      # a constant with an initializer (e.g. `A = (puts 1; Int32)` has a side\n      # effect).\n      if obj.type.passed_as_self? || obj.target_const\n        request_value(obj)\n      end\n    when ASTNode\n      # Always accept obj: even if it's not passed as self this might\n      # involve intermediate calls with side effects.\n      request_value(obj)\n    end\n\n    # First self.\n    if owner.passed_as_self?\n      if owner.nil_type?\n        call_args << llvm_nil\n      else\n        if obj && obj.type.passed_as_self?\n          call_args << downcast(@last, target_def.owner, obj.type, true)\n        else\n          if node.uses_with_scope? && (yield_scope = context.vars[\"%scope\"]?)\n            call_args << downcast(yield_scope.pointer, target_def.owner, node.with_scope.not_nil!, true)\n          else\n            call_args << llvm_self(owner)\n          end\n        end\n      end\n    end\n\n    c_calling_convention = target_def.c_calling_convention?\n\n    # Then the arguments.\n    node.args.zip(target_def.args) do |arg, def_arg|\n      request_value(arg)\n\n      if arg.type.void?\n        call_arg = int8(0)\n      else\n        call_arg = @last\n        call_arg = llvm_nil if arg.type.nil_type?\n        call_arg = downcast(call_arg, def_arg.type, arg.type, true)\n      end\n\n      # - C calling convention passing needs a separate handling of pass-by-value\n      # - Primitives might need a separate handling (for example invoking a Proc)\n      if arg.type.passed_by_value? && !c_calling_convention && !is_primitive\n        call_arg = load(llvm_type(arg.type), call_arg)\n      end\n\n      call_args << call_arg\n    end\n\n    # Then special variables ($~, $?)\n    target_def.special_vars.try &.each do |special_var_name|\n      call_args << context.vars[special_var_name].pointer\n    end\n\n    # Then magic constants (__LINE__, __FILE__, __DIR__)\n    node.args.size.upto(target_def.args.size - 1) do |index|\n      arg = target_def.args[index]\n      default_value = arg.default_value.as(MagicConstant)\n      location = node.location\n      end_location = node.end_location\n      case default_value.name\n      when .magic_line?\n        call_args << int32(MagicConstant.expand_line(location))\n      when .magic_end_line?\n        call_args << int32(MagicConstant.expand_line(end_location))\n      when .magic_file?\n        call_args << build_string_constant(MagicConstant.expand_file(location))\n      when .magic_dir?\n        call_args << build_string_constant(MagicConstant.expand_dir(location))\n      else\n        default_value.raise \"BUG: unknown magic constant: #{default_value.name}\"\n      end\n    end\n\n    @needs_value = old_needs_value\n\n    call_args\n  end\n\n  def call_abi_info(target_def, node)\n    # For varargs we need to compute abi info for the arguments, which may be more\n    # than those specified in the function definition\n    if target_def.varargs?\n      abi_info(target_def, node)\n    else\n      abi_info(target_def)\n    end\n  end\n\n  def prepare_call_args_external(node, target_def, owner)\n    abi_info = call_abi_info(target_def, node)\n\n    call_args = Array(LLVM::Value).new(node.args.size + 1)\n    old_needs_value = @needs_value\n\n    if abi_info.return_type.attr == LLVM::Attribute::StructRet\n      sret_value = @sret_value = alloca abi_info.return_type.type\n      call_args << sret_value\n    end\n\n    target_def_args_size = target_def.args.size\n\n    node.args.each_with_index do |arg, i|\n      if arg.is_a?(Out)\n        case exp = arg.exp\n        when Var\n          # Just get a pointer to the variable\n          call_arg = context.vars[exp.name].pointer\n        when InstanceVar\n          # Just get a pointer to the instance variable\n          call_arg = instance_var_ptr(type, exp.name, llvm_self_ptr)\n        when Underscore\n          # Allocate stack memory, but discard the value\n          call_arg = alloca(llvm_type(arg.type))\n        else\n          arg.raise \"BUG: out argument was #{exp}\"\n        end\n      else\n        request_value(arg)\n\n        if arg.type.void?\n          call_arg = int8(0)\n        else\n          def_arg = target_def.args[i]?\n\n          call_arg = @last\n          call_arg = llvm_nil if arg.type.nil_type?\n\n          if def_arg && arg.type.nil_type? && (def_arg.type.pointer? || def_arg.type.proc?)\n            # Nil to pointer\n            call_arg = llvm_c_type(def_arg.type).null\n          else\n            if def_arg\n              call_arg = downcast(call_arg, def_arg.type, arg.type, true)\n            else\n              # Def argument might be missing if it's a variadic call\n              if arg.type.nil_type?\n                call_arg = llvm_context.void_pointer.null\n              end\n            end\n          end\n        end\n      end\n\n      if arg.type.proc?\n        # Try first with the def arg type (might be a proc pointer that return void,\n        # while the argument's type a proc pointer that return something else)\n        call_arg = check_proc_is_not_closure(call_arg, def_arg.try(&.type) || arg.type)\n      end\n\n      abi_arg_type = abi_info.arg_types[i]\n      case abi_arg_type.kind\n      in .direct?\n        call_arg = codegen_direct_abi_call(arg.type, call_arg, abi_arg_type) unless arg.type.nil_type?\n      in .indirect?\n        call_arg = codegen_indirect_abi_call(call_arg, abi_arg_type) unless arg.type.nil_type?\n      in .ignore?\n        # Ignore\n        next\n      end\n\n      # If we are passing variadic arguments there are some special rules\n      if i >= target_def_args_size\n        arg_type = arg.type.remove_indirection\n        if arg_type.is_a?(FloatType) && arg_type.kind.bytesize < 64\n          # Floats must be passed as doubles (there are no float varargs)\n          call_arg = extend_float @program.float64, call_arg\n        elsif arg_type.is_a?(IntegerType) && arg_type.kind.bytesize < 32\n          # Integer with a size less that `int` must be converted to `int`\n          call_arg = extend_int arg_type, @program.int32, call_arg\n        end\n      end\n\n      call_args << call_arg\n    end\n\n    @needs_value = old_needs_value\n\n    call_args\n  end\n\n  def codegen_direct_abi_call(call_arg_type, call_arg, abi_arg_type)\n    if cast = abi_arg_type.cast\n      final_value = alloca cast\n      final_value_casted = cast_to_void_pointer final_value\n      gep_call_arg = cast_to_void_pointer gep(llvm_type(call_arg_type), call_arg, 0, 0)\n      size = @abi.size(abi_arg_type.type)\n      align = @abi.align(abi_arg_type.type)\n      memcpy(final_value_casted, gep_call_arg, size_t(size), align, int1(0))\n      call_arg = load cast, final_value\n    else\n      # Keep same call arg\n    end\n    call_arg\n  end\n\n  # For an indirect argument we need to make a copy of the value in the stack\n  # and *replace* the call argument by a pointer to the allocated memory\n  def codegen_indirect_abi_call(call_arg, abi_arg_type)\n    final_value = alloca abi_arg_type.type\n    final_value_casted = cast_to_void_pointer final_value\n    call_arg_casted = cast_to_void_pointer call_arg\n    size = @abi.size(abi_arg_type.type)\n    align = @abi.align(abi_arg_type.type)\n    memcpy(final_value_casted, call_arg_casted, size_t(size), align, int1(0))\n    final_value\n  end\n\n  def codegen_call_with_block(node, block, self_type, call_args)\n    set_current_debug_location node if @debug.line_numbers?\n\n    with_cloned_context do |old_block_context|\n      context.vars = old_block_context.vars.dup\n      context.closure_parent_context = old_block_context\n\n      # Allocate block vars, but first undefine variables outside\n      # the block with the same name. This can only happen in this case:\n      #\n      #     a = foo { |a| }\n      #\n      # that is, when assigning to a variable with the same name as\n      # a block argument (no shadowing here)\n      undef_vars block.vars, block\n      alloca_non_closured_vars block.vars, block\n\n      with_cloned_context do |old_context|\n        context.block = block\n        context.block_context = old_context\n        context.vars = LLVMVars.new\n        context.type = self_type\n        context.reset_closure\n\n        target_def = node.target_def\n\n        set_ensure_exception_handler(node)\n        set_ensure_exception_handler(target_def)\n\n        args_base_index = create_local_copy_of_block_self(self_type, call_args)\n\n        # Don't reset nilable vars here because we do it right before inlining the method body\n        alloca_vars target_def.vars, target_def, reset_nilable_vars: false\n\n        create_local_copy_of_block_args(target_def, self_type, call_args, args_base_index)\n\n        Phi.open(self, node) do |phi|\n          context.return_phi = phi\n\n          # Reset vars that are declared inside the def and are nilable\n          reset_nilable_vars(target_def)\n\n          request_value(target_def.body)\n\n          phi.add @last, target_def.body.type?, last: true\n        end\n      end\n    end\n  end\n\n  def codegen_call_with_block_as_fun_literal(node, fun_literal, self_type, call_args)\n    accept fun_literal\n    call_args.push @last\n\n    target_def = node.target_def\n    func = target_def_fun(target_def, self_type)\n\n    codegen_call_or_invoke(node, target_def, self_type, func, call_args, target_def.raises?, target_def.type)\n  end\n\n  def codegen_dispatch(node, target_defs)\n    new_vars = context.vars.dup\n    old_needs_value = @needs_value\n\n    # Get type_id of obj or owner\n    if node_obj = node.obj\n      owner = node_obj.type\n      request_value(node_obj)\n      obj_type_id = @last\n    elsif node.uses_with_scope? && (with_scope = node.with_scope)\n      owner = with_scope\n      obj_type_id = context.vars[\"%scope\"].pointer\n    else\n      owner = node.scope\n      obj_type_id = llvm_self\n    end\n    obj_type_id = type_id(obj_type_id, owner)\n\n    # Create self var if available\n    if node_obj\n      # call `#remove_indirection` here so that the downcast call in\n      # `#visit(Var)` doesn't spend time expanding module types again and again\n      # (it should be the only use site of `node_obj.type`)\n      new_vars[\"%self\"] = LLVMVar.new(@last, node_obj.type.remove_indirection, true)\n    end\n\n    # Get type if of args and create arg vars\n    arg_type_ids = node.args.map_with_index do |arg, i|\n      request_value(arg)\n      new_vars[\"%arg#{i}\"] = LLVMVar.new(@last, arg.type, true)\n      type_id(@last, arg.type)\n    end\n\n    # Reuse this call for each dispatch branch\n    call = Call.new(node_obj ? Var.new(\"%self\") : nil, node.name, node.args.map_with_index { |arg, i| Var.new(\"%arg#{i}\").as(ASTNode) }, node.block).at(node)\n    call.scope = with_scope || node.scope\n    call.with_scope = with_scope\n    call.uses_with_scope = node.uses_with_scope?\n    call.name_location = node.name_location\n\n    is_super = node.super?\n\n    # call `#remove_indirection` here so that the `match_type_id` below doesn't\n    # spend time expanding module types again and again\n    owner = owner.remove_indirection unless is_super\n\n    with_cloned_context do\n      context.vars = new_vars\n\n      Phi.open(self, node, old_needs_value) do |phi|\n        # Iterate all defs and check if any match the current types, given their ids (obj_type_id and arg_type_ids)\n        target_defs.each do |a_def|\n          if is_super\n            # A super call always matches the obj type\n            result = int1(1)\n          else\n            result = match_type_id(owner, a_def.owner, obj_type_id)\n          end\n          node.args.each_with_index do |node_arg, i|\n            a_def_arg = a_def.args[i]\n            if node_arg.supports_autocast?(!@program.has_flag?(\"no_number_autocast\"))\n              # If a call argument is a literal like 1 or :foo then\n              # it will match all the multidispatch overloads because\n              # it has a single type and there's no way some overload\n              # (from the ones we decided that match) won't match,\n              # because if it doesn't match then we wouldn't have included\n              # it in the match list.\n              # Matches, so nothing to do\n            else\n              result = and(result, match_type_id(node_arg.type, a_def_arg.type, arg_type_ids[i]))\n            end\n          end\n\n          current_def_label, next_def_label = new_blocks \"current_def\", \"next_def\"\n          cond result, current_def_label, next_def_label\n\n          position_at_end current_def_label\n\n          # Prepare this specific call\n          call.target_defs = [a_def] of Def\n          call.obj.try &.set_type(a_def.owner)\n          call.args.zip(a_def.args) do |call_arg, a_def_arg|\n            call_arg.set_type(a_def_arg.type)\n          end\n          if (node_block = node.block) && node_block.break.type?\n            call.set_type(@program.type_merge [a_def.type, node_block.break.type] of Type)\n          else\n            call.set_type(a_def.type)\n          end\n          accept call\n\n          phi.add @last, call.type\n          position_at_end next_def_label\n        end\n        unreachable\n      end\n    end\n\n    @needs_value = old_needs_value\n  end\n\n  def codegen_call(node, target_def, self_type, call_args)\n    body = target_def.body\n\n    # Try to inline the call\n    if try_inline_call(target_def, body, self_type, call_args)\n      return\n    end\n\n    # We also always inline primitives\n    if body.is_a?(Primitive)\n      # Change context type: faster then creating a new context\n      old_type = context.type\n      context.type = self_type\n      codegen_primitive(node, body, target_def, call_args)\n      context.type = old_type\n      return true\n    end\n\n    func = target_def_fun(target_def, self_type)\n    codegen_call_or_invoke(node, target_def, self_type, func, call_args, target_def.raises?, target_def.type)\n  end\n\n  # If a method's body is just a simple literal, \"self\", or an instance variable,\n  # we always inline it: less code generated, easier job for LLVM to optimize, and\n  # avoid a call in non-release builds.\n  #\n  # Do this even in debug mode, because there's not much use in stepping\n  # to read a constant value or the value of an instance variable.\n  # Additionally, not inlining instance variable getters changes the semantic\n  # a program, so we must always inline these.\n  def try_inline_call(target_def, body, self_type, call_args)\n    return false if target_def.is_a?(External)\n\n    case body\n    when Nop, NilLiteral, BoolLiteral, CharLiteral, StringLiteral, NumberLiteral, SymbolLiteral\n      return true unless @needs_value\n\n      accept body\n      inline_call_return_value target_def, body\n      true\n    when Var\n      if body.name == \"self\"\n        return true unless @needs_value\n\n        @last = self_type.passed_as_self? ? call_args.first : type_id(self_type)\n        inline_call_return_value target_def, body\n        true\n      else\n        false\n      end\n    when InstanceVar\n      return true unless @needs_value\n\n      read_instance_var(body.type, self_type, body.name, call_args.first)\n      inline_call_return_value target_def, body\n      true\n    else\n      false\n    end\n  end\n\n  def inline_call_return_value(target_def, body)\n    if target_def.type.no_return?\n      unreachable\n    elsif target_def.type.nil_type?\n      @last = llvm_nil\n    else\n      @last = upcast(@last, target_def.type, body.type)\n    end\n  end\n\n  def codegen_call_or_invoke(node, target_def, self_type, func, call_args, raises, type, is_closure = false, fun_type = nil)\n    # If *fun_type* is not nil, then this method is being called from\n    # `codegen_primitive_proc_call` and *node* is simply `Proc#call`'s \"body\";\n    # in that case do not replace line numbers so that call stacks will continue\n    # using the original invocation\n    set_current_debug_location node if @debug.line_numbers? && fun_type.nil?\n\n    if raises && (rescue_block = @rescue_block)\n      invoke_out_block = new_block \"invoke_out\"\n      @last = invoke func, call_args, invoke_out_block, rescue_block\n      position_at_end invoke_out_block\n    else\n      @last = call func, call_args\n    end\n\n    if target_def.is_a?(External) && (call_convention = target_def.call_convention)\n      @last.call_convention = call_convention\n    end\n\n    if @builder.end\n      return @last\n    end\n\n    set_call_attributes node, target_def, self_type, is_closure, fun_type\n\n    external = target_def.try &.c_calling_convention?\n\n    if external && (external.type.proc? || external.type.is_a?(NilableProcType))\n      fun_ptr = cast_to_void_pointer(@last)\n      ctx_ptr = llvm_context.void_pointer.null\n      return @last = make_fun(external.type, fun_ptr, ctx_ptr)\n    end\n\n    if external\n      if type.no_return?\n        unreachable\n      else\n        abi_return = abi_info(external).return_type\n        case abi_return.kind\n        in .direct?\n          if cast = abi_return.cast\n            cast1 = alloca cast\n            store @last, cast1\n            cast2 = cast_to_void_pointer(cast1)\n            final_value = alloca abi_return.type\n            final_value_casted = cast_to_void_pointer final_value\n            size = @abi.size(abi_return.type)\n            align = @abi.align(abi_return.type)\n            memcpy(final_value_casted, cast2, size_t(size), align, int1(0))\n            @last = final_value\n          end\n        in .indirect?\n          @last = @sret_value.not_nil!\n        in .ignore?\n          # Nothing\n        end\n      end\n    else\n      case type\n      when .no_return?\n        unreachable\n      when .passed_by_value?\n        if @needs_value\n          union = alloca llvm_type(type)\n          store @last, union\n          @last = union\n        else\n          @last = llvm_nil\n        end\n      end\n    end\n\n    @last\n  end\n\n  def set_call_attributes(node : Call, target_def, self_type, is_closure, fun_type)\n    if external = target_def.c_calling_convention?\n      set_call_attributes_external(node, external)\n    else\n      # Non-external methods/functions have no arguments attributes\n    end\n  end\n\n  def set_call_attributes_external(node, target_def)\n    abi_info = call_abi_info(target_def, node)\n\n    sret = sret?(abi_info)\n    arg_offset = 1\n    arg_offset += 1 if sret\n\n    node.args.each_with_index do |arg, i|\n      # If the argument is out the type might be a struct but we don't pass anything byval\n      next if node.args[i]?.try &.is_a?(Out)\n\n      abi_arg_type = abi_info.arg_types[i]?\n      if abi_arg_type && (attr = abi_arg_type.attr)\n        @last.add_instruction_attribute(i + arg_offset, attr, llvm_context, abi_arg_type.type)\n      end\n    end\n\n    if sret\n      @last.add_instruction_attribute(1, LLVM::Attribute::StructRet, llvm_context, abi_info.return_type.type)\n    end\n  end\n\n  # This is for function pointer calls and exception handler re-raise\n  def set_call_attributes(node, target_def, self_type, is_closure, fun_type)\n    if target_def && target_def.abi_info?\n      abi_info = abi_info(target_def)\n    end\n\n    sret = abi_info && sret?(abi_info)\n    arg_offset = is_closure ? 2 : 1\n    arg_offset += 1 if sret\n\n    arg_types = fun_type.try(&.arg_types) || target_def.try &.args.map &.type\n    arg_types.try &.each_with_index do |arg_type, i|\n      if abi_info && (abi_arg_type = abi_info.arg_types[i]?) && (attr = abi_arg_type.attr)\n        @last.add_instruction_attribute(i + arg_offset, attr, llvm_context, abi_arg_type.type)\n      end\n    end\n\n    if abi_info && sret\n      @last.add_instruction_attribute(1, LLVM::Attribute::StructRet, llvm_context, abi_info.return_type.type)\n    end\n  end\n\n  def sret?(abi_info)\n    abi_info.return_type.attr == LLVM::Attribute::StructRet\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/cast.cr",
    "content": "require \"./codegen\"\n\n# Here lies the logic to cast values between different types. There are three operations:\n#\n# ## Assign\n#\n# ```\n# target_pointer : target_type <- value : value_type\n# ```\n#\n# This happens when we store a value inside a variable (a variable is represented as\n# pointer to the real value).\n#\n# If the type of the target and value are the same we can simply store the value inside\n# the pointer.\n#\n# Otherwise, it's the case of a value having a \"smaller\" type than the variable's type,\n# for example when assigning an Int32 into a union of Int32 | String, or when assigning\n# a Bar into a Foo, with Bar < Foo, etc. In those cases we need to do some extra stuff,\n# for example store the value's type id in the union and then the real value in the second\n# slot of a union, casted to the union's type.\n#\n# ## Upcast\n#\n# ```\n# (value : from_type).as(to_type)\n# ```\n#\n# This happens when a value is \"boxed\" inside a \"bigger\" one. For example in this method:\n#\n# ```\n# def foo\n#   condition ? 1 : nil\n# end\n# ```\n#\n# foo's type is Int32 | Nil, with one branch of the 'if' being Int32 and the other Nil.\n# In this case we need to \"box\" the Int32 value inside the union, and the same for Nil.\n#\n# This is different than doing an assign because we don't assign the value, we simply\n# box it. Later that value might be stored inside a value with such type, but we keep\n# it as two different operations because assigning involves fewer operations (to store\n# a value inside a union we simply store the type id and the value, instead of allocating\n# a union in the stack and the copying the union inside the final destination).\n#\n# ## Downcast\n#\n# ```\n# (value : from_type).as(to_type)\n# ```\n#\n# This happens when a value is casted from a \"bigger\" type to a \"smaller\" type. For example:\n#\n# ```\n# def foo\n#   condition ? 1 : nil\n# end\n#\n# # 1.\n# foo.as(Int32) # here a downcast happens, from `Int32 | Nil` to `Int32`\n#\n# # 2.\n# if foo.is_a?(Int32)\n#   foo # here a downcast happens, from `Int32 | Nil` to `Int32`\n# end\n# ```\n#\n# In this case we usually need to unbox a value from a union, or cast a more general\n# type into a specific type (such as when casting a Foo to a Bar, with Bar < Foo).\n\nclass Crystal::CodeGenVisitor\n  def assign(target_pointer, target_type, value_type, value)\n    return if @builder.end\n\n    target_type = target_type.remove_indirection\n    value_type = value_type.remove_indirection\n\n    if target_type == value_type\n      if target_type.nil_type?\n        value\n      else\n        store to_rhs(value, target_type), target_pointer\n      end\n    else\n      assign_distinct target_pointer, target_type, value_type, value\n    end\n  end\n\n  def assign_distinct(target_pointer, target_type : NilableType, value_type : Type, value)\n    store upcast(value, target_type, value_type), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : ReferenceUnionType, value_type : ReferenceUnionType, value)\n    store value, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : ReferenceUnionType, value_type : VirtualType, value)\n    store value, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : ReferenceUnionType, value_type : Type, value)\n    store cast_to(value, target_type), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : NilableReferenceUnionType, value_type : Type, value)\n    store upcast(value, target_type, value_type), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : MixedUnionType, value)\n    # It might happen that some types inside the union `value_type` are not inside `target_type`,\n    # for example with named tuple of same keys with different order. In that case we need cast\n    # those value to the correct type before finally storing them in the target union.\n    needs_union_value_cast = value_type.union_types.any? do |vt|\n      needs_value_cast_inside_union?(vt, target_type)\n    end\n\n    if needs_union_value_cast # Compute the values that need a cast\n      types_needing_cast = value_type.union_types.select do |vt|\n        needs_value_cast_inside_union?(vt, target_type)\n      end\n      # Fetch the value's type id\n      value_type_id, union_value_ptr = union_type_and_value_pointer(value, value_type)\n\n      exit_label = new_block \"exit\"\n\n      types_needing_cast.each_with_index do |type_needing_cast, i|\n        # Find compatible type\n        compatible_type = target_type.union_types.find! { |ut| type_needing_cast.implements?(ut) }\n        llvm_compatible_type = llvm_type(compatible_type)\n\n        matches_label, doesnt_match_label = new_blocks \"matches\", \"doesnt_match_label\"\n        cmp_result = equal?(value_type_id, type_id(type_needing_cast))\n        cond cmp_result, matches_label, doesnt_match_label\n\n        position_at_end matches_label\n\n        # Store value\n        casted_value = cast_to_pointer(union_value_ptr, type_needing_cast)\n        compatible_ptr = alloca llvm_compatible_type\n        assign(compatible_ptr, compatible_type, type_needing_cast, casted_value)\n        store_in_union target_type, target_pointer, compatible_type, load(llvm_compatible_type, compatible_ptr)\n        br exit_label\n\n        position_at_end doesnt_match_label\n      end\n\n      assign_distinct_union_types(target_pointer, target_type, value_type, value)\n      br exit_label\n\n      position_at_end exit_label\n    else\n      assign_distinct_union_types(target_pointer, target_type, value_type, value)\n    end\n  end\n\n  def needs_value_cast_inside_union?(value_type, union_type)\n    # A type needs a special cast if:\n    # 1. It's a tuple or named tuple\n    # 2. It's not inside the target union\n    # 3. There's a compatible type inside the target union\n    return false unless value_type.is_a?(TupleInstanceType) || value_type.is_a?(NamedTupleInstanceType)\n    !union_type.union_types.any?(&.==(value_type)) &&\n      union_type.union_types.any? { |ut| value_type.implements?(ut) || ut.implements?(value_type) }\n  end\n\n  def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : NilableType, value)\n    store_in_union target_type, target_pointer, value_type, value\n  end\n\n  def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : VoidType, value)\n    store_void_in_union target_type, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : BoolType, value)\n    store_bool_in_union target_type, target_pointer, value\n  end\n\n  def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : NilType, value)\n    store_nil_in_union target_type, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : Type, value)\n    case value_type\n    when TupleInstanceType, NamedTupleInstanceType\n      # It might happen that `value_type` is not of the union but it's compatible with one of them.\n      # We need to first cast the value to the compatible type and then store it in the value.\n      unless target_type.union_types.any? &.==(value_type)\n        compatible_type = target_type.union_types.find! { |ut| value_type.implements?(ut) }\n        value = upcast(value, compatible_type, value_type)\n        return assign(target_pointer, target_type, compatible_type, value)\n      end\n    end\n\n    value = to_rhs(value, value_type)\n    store_in_union target_type, target_pointer, value_type, value\n  end\n\n  def assign_distinct(target_pointer, target_type : VirtualType, value_type : MixedUnionType, value)\n    union_value_ptr = union_value(llvm_type(value_type), value)\n    casted_value = cast_to_pointer(union_value_ptr, target_type)\n    store load(llvm_type(target_type), casted_value), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : VirtualType, value_type : Type, value)\n    store cast_to(value, target_type), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : VirtualMetaclassType, value_type : MetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType | VirtualMetaclassType, value)\n    store value, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : VirtualMetaclassType, value_type : UnionType, value)\n    # Can happen when assigning Foo+.class <- Bar.class | Baz.class with Bar < Foo and Baz < Foo\n    union_value_ptr = union_value(llvm_type(value_type), value)\n    casted_value = cast_to_pointer(union_value_ptr, target_type)\n    store load(llvm_type(target_type), casted_value), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : NilableProcType, value_type : NilType, value)\n    nilable_fun = make_nilable_fun target_type\n    store nilable_fun, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : NilableProcType, value_type : ProcInstanceType, value)\n    store value, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : NilableProcType, value_type : TypeDefType, value)\n    assign_distinct target_pointer, target_type, value_type.typedef, value\n  end\n\n  def assign_distinct(target_pointer, target_type : TupleInstanceType, value_type : TupleInstanceType, value)\n    target_struct_type = llvm_type(target_type)\n    value_struct_type = llvm_type(value_type)\n\n    index = 0\n    target_type.tuple_types.zip(value_type.tuple_types) do |target_tuple_type, value_tuple_type|\n      target_ptr = aggregate_index(target_struct_type, target_pointer, index)\n      value_ptr = aggregate_index(value_struct_type, value, index)\n      loaded_value = to_lhs(value_ptr, value_tuple_type)\n      assign(target_ptr, target_tuple_type, value_tuple_type, loaded_value)\n      index += 1\n    end\n    value\n  end\n\n  def assign_distinct(target_pointer, target_type : NamedTupleInstanceType, value_type : NamedTupleInstanceType, value)\n    target_struct_type = llvm_type(target_type)\n    value_struct_type = llvm_type(value_type)\n\n    value_type.entries.each_with_index do |entry, index|\n      value_ptr = aggregate_index(value_struct_type, value, index)\n      value_at_index = to_lhs(value_ptr, entry.type)\n      target_index = target_type.name_index(entry.name).not_nil!\n      target_index_type = target_type.name_type(entry.name)\n      target_ptr = aggregate_index(target_struct_type, target_pointer, target_index)\n      assign target_ptr, target_index_type, entry.type, value_at_index\n    end\n  end\n\n  def assign_distinct(target_pointer, target_type : ProcInstanceType, value_type : ProcInstanceType, value)\n    # Cast of a non-void proc to a void proc\n    value = to_rhs(value, target_type)\n    store value, target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : ProcInstanceType, value_type : MixedUnionType, value)\n    # The only case when a union is assigned to a proc is when\n    # target_type is Proc(*T, Nil) and all the types in the union are Proc(*T, R).\n    # In that case we can simply get the union value and cast it to the target type.\n    # Cast of a non-void proc to a void proc\n    union_value_ptr = union_value(llvm_type(value_type), value)\n    value = cast_to_pointer(union_value_ptr, target_type)\n    store load(llvm_type(target_type), value), target_pointer\n  end\n\n  def assign_distinct(target_pointer, target_type : Type, value_type : Type, value)\n    raise \"BUG: trying to assign #{target_type} (#{target_type.class}) <- #{value_type} (#{value_type.class})\"\n  end\n\n  def downcast(value, to_type, from_type : VoidType, already_loaded, *, extern = false)\n    value\n  end\n\n  # *extern* is only used in `CodeGenVisitor#read_instance_var`, and it is\n  # irrelevant whenever `already_loaded == true`\n  def downcast(value, to_type, from_type : Type, already_loaded, *, extern = false)\n    return llvm_nil if @builder.end\n\n    from_type = from_type.remove_indirection\n    to_type = to_type.remove_indirection\n\n    unless already_loaded\n      value = extern ? extern_to_lhs(value, from_type) : to_lhs(value, from_type)\n    end\n    if from_type != to_type\n      value = downcast_distinct value, to_type, from_type\n    end\n    value\n  end\n\n  def downcast_distinct(value, to_type : NilType, from_type : Type)\n    llvm_nil\n  end\n\n  def downcast_distinct(value, to_type, from_type : MetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType | VirtualMetaclassType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : VirtualType, from_type : VirtualType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : ReferenceUnionType, from_type : VirtualType)\n    # This happens if the restriction is a union:\n    # we keep each of the union types as the result, we don't fully merge\n    value\n  end\n\n  def downcast_distinct(value, to_type : NonGenericClassType | GenericClassInstanceType, from_type : VirtualType)\n    cast_to value, to_type\n  end\n\n  def downcast_distinct(value, to_type : VirtualType, from_type : NilableType)\n    cast_to value, to_type\n  end\n\n  def downcast_distinct(value, to_type : Type, from_type : NilableType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : ProcInstanceType, from_type : NilableProcType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : TypeDefType, from_type : NilableProcType)\n    downcast_distinct value, to_type.typedef, from_type\n  end\n\n  def downcast_distinct(value, to_type : PointerInstanceType, from_type : PointerInstanceType)\n    # cast of a pointer being cast to Void*\n    cast_to_void_pointer value\n  end\n\n  def downcast_distinct(value, to_type : ReferenceUnionType, from_type : ReferenceUnionType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : VirtualType, from_type : ReferenceUnionType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : Type, from_type : ReferenceUnionType)\n    cast_to value, to_type\n  end\n\n  def downcast_distinct(value, to_type : VirtualType, from_type : NilableReferenceUnionType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : ReferenceUnionType, from_type : NilableReferenceUnionType)\n    value\n  end\n\n  def downcast_distinct(value, to_type : NilableType, from_type : NilableReferenceUnionType)\n    cast_to value, to_type\n  end\n\n  def downcast_distinct(value, to_type : Type, from_type : NilableReferenceUnionType)\n    cast_to value, to_type\n  end\n\n  def downcast_distinct(value, to_type : MixedUnionType, from_type : MixedUnionType)\n    # It might happen that some types inside the union `from_type` are not inside `to_type`,\n    # for example with named tuple of same keys with different order. In that case we need cast\n    # those value to the correct type before finally storing them in the target union.\n    needs_union_value_cast = from_type.union_types.any? do |vt|\n      needs_value_cast_inside_union?(vt, to_type)\n    end\n\n    if needs_union_value_cast\n      # Compute the values that need a cast\n      types_needing_cast = from_type.union_types.select do |vt|\n        needs_value_cast_inside_union?(vt, to_type)\n      end\n\n      # Fetch the value's type id\n      from_type_id, union_value_ptr = union_type_and_value_pointer(value, from_type)\n\n      Phi.open(self, to_type, @needs_value) do |phi|\n        types_needing_cast.each_with_index do |type_needing_cast, i|\n          # Find compatible type\n          compatible_type = to_type.union_types.find!(&.implements?(type_needing_cast))\n\n          matches_label, doesnt_match_label = new_blocks \"matches\", \"doesnt_match_label\"\n          cmp_result = equal?(from_type_id, type_id(type_needing_cast))\n          cond cmp_result, matches_label, doesnt_match_label\n\n          position_at_end matches_label\n\n          casted_value = cast_to_pointer(union_value_ptr, type_needing_cast)\n          downcasted_value = downcast(casted_value, compatible_type, type_needing_cast, true)\n          final_value = upcast(downcasted_value, to_type, compatible_type)\n          phi.add final_value, to_type\n\n          position_at_end doesnt_match_label\n        end\n\n        final_value = cast_to_pointer value, to_type\n        phi.add final_value, to_type, last: true\n      end\n    else\n      downcast_distinct_union_types(value, to_type, from_type)\n    end\n  end\n\n  def downcast_distinct(value, to_type : NilableType, from_type : MixedUnionType)\n    value_ptr = union_value(llvm_type(from_type), value)\n    load(llvm_type(to_type), cast_to_pointer(value_ptr, to_type))\n  end\n\n  def downcast_distinct(value, to_type : BoolType, from_type : MixedUnionType)\n    value_ptr = union_value(llvm_type(from_type), value)\n    value = cast_to_pointer(value_ptr, @program.int8)\n    value = load(llvm_context.int8, value)\n    trunc value, llvm_context.int1\n  end\n\n  def downcast_distinct(value, to_type : Type, from_type : MixedUnionType)\n    # It might happen that to_type is not of the union but it's compatible with one of them.\n    # We need to first cast the value to the compatible type and to to_type\n    case to_type\n    when TupleInstanceType, NamedTupleInstanceType\n      unless from_type.union_types.any? &.==(to_type)\n        compatible_type = from_type.union_types.find! { |ut| to_type.implements?(ut) }\n        value = downcast(value, compatible_type, from_type, true)\n        value = downcast(value, to_type, compatible_type, true)\n        return value\n      end\n    end\n\n    value_ptr = union_value(llvm_type(from_type), value)\n    value = cast_to_pointer(value_ptr, to_type)\n    to_lhs value, to_type\n  end\n\n  def downcast_distinct(value, to_type : ProcInstanceType, from_type : ProcInstanceType)\n    # Nothing to do\n    value\n  end\n\n  def downcast_distinct(value, to_type : TupleInstanceType, from_type : TupleInstanceType)\n    target_struct_type = llvm_type(to_type)\n    value_struct_type = llvm_type(from_type)\n    target_pointer = alloca(target_struct_type)\n\n    index = 0\n    to_type.tuple_types.zip(from_type.tuple_types) do |target_tuple_type, value_tuple_type|\n      target_ptr = aggregate_index(target_struct_type, target_pointer, index)\n      value_ptr = aggregate_index(value_struct_type, value, index)\n      loaded_value = to_lhs(value_ptr, value_tuple_type)\n      downcasted_value = downcast(loaded_value, target_tuple_type, value_tuple_type, true)\n      downcasted_value = to_rhs(downcasted_value, target_tuple_type)\n      store downcasted_value, target_ptr\n      index += 1\n    end\n    target_pointer\n  end\n\n  def downcast_distinct(value, to_type : NamedTupleInstanceType, from_type : NamedTupleInstanceType)\n    target_struct_type = llvm_type(to_type)\n    value_struct_type = llvm_type(from_type)\n    target_pointer = alloca(target_struct_type)\n\n    from_type.entries.each_with_index do |entry, index|\n      value_ptr = aggregate_index(value_struct_type, value, index)\n      value_at_index = to_lhs(value_ptr, entry.type)\n      target_index = to_type.name_index(entry.name).not_nil!\n      target_index_type = to_type.name_type(entry.name)\n      downcasted_value = downcast(value_at_index, target_index_type, entry.type, true)\n      downcasted_value = to_rhs(downcasted_value, target_index_type)\n      store downcasted_value, aggregate_index(target_struct_type, target_pointer, target_index)\n    end\n    target_pointer\n  end\n\n  # This is the case of the automatic cast between integer types\n  def downcast_distinct(value, to_type : IntegerType, from_type : IntegerType)\n    codegen_cast(from_type, to_type, value)\n  end\n\n  # This is the case of the automatic cast between integer type and float type\n  def downcast_distinct(value, to_type : FloatType, from_type : IntegerType)\n    codegen_cast(from_type, to_type, value)\n  end\n\n  # This is the case of the automatic cast between float types\n  def downcast_distinct(value, to_type : FloatType, from_type : FloatType)\n    codegen_cast(from_type, to_type, value)\n  end\n\n  # This is the case of the automatic cast between symbol and enum\n  def downcast_distinct(value, to_type : EnumType, from_type : SymbolType)\n    # value has the value of the symbol inside the symbol table,\n    # so we first get which symbol name that is, and then match\n    # it to one of the enum members\n    index = value.const_int_get_sext_value\n    symbol = @symbols_by_index[index].underscore\n\n    to_type.types.each do |name, value|\n      if name.underscore == symbol\n        accept(value.as(Const).value)\n        return @last\n      end\n    end\n\n    raise \"Bug: expected to find enum member of #{to_type} matching symbol #{symbol}\"\n  end\n\n  def downcast_distinct(value, to_type : Type, from_type : Type)\n    raise \"BUG: trying to downcast #{to_type} (#{to_type.class}) <- #{from_type} (#{from_type.class})\"\n  end\n\n  def upcast(value, to_type, from_type)\n    return llvm_nil if @builder.end\n\n    from_type = from_type.remove_indirection\n    to_type = to_type.remove_indirection\n\n    if to_type != from_type\n      value = upcast_distinct(value, to_type, from_type)\n    end\n    value\n  end\n\n  def upcast_distinct(value, to_type : MetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType | VirtualMetaclassType, from_type)\n    value\n  end\n\n  def upcast_distinct(value, to_type : VirtualType, from_type)\n    cast_to value, to_type\n  end\n\n  def upcast_distinct(value, to_type : NilableType, from_type : NilType?)\n    llvm_type(to_type).null\n  end\n\n  def upcast_distinct(value, to_type : NilableType, from_type : Type)\n    value\n  end\n\n  def upcast_distinct(value, to_type : NilableReferenceUnionType, from_type : NilType?)\n    llvm_type(to_type).null\n  end\n\n  def upcast_distinct(value, to_type : NilableReferenceUnionType, from_type : Type)\n    cast_to value, to_type\n  end\n\n  def upcast_distinct(value, to_type : NilableProcType, from_type : NilType)\n    make_nilable_fun to_type\n  end\n\n  def upcast_distinct(value, to_type : NilableProcType, from_type : ProcInstanceType)\n    value\n  end\n\n  def upcast_distinct(value, to_type : NilableProcType, from_type : TypeDefType)\n    upcast_distinct value, to_type, from_type.typedef\n  end\n\n  def upcast_distinct(value, to_type : ReferenceUnionType, from_type)\n    cast_to value, to_type\n  end\n\n  def upcast_distinct(value, to_type : MixedUnionType, from_type : MixedUnionType)\n    # It might happen that some types inside the union `from_type` are not inside `to_type`,\n    # for example with named tuple of same keys with different order. In that case we need cast\n    # those value to the correct type before finally storing them in the target union.\n    needs_union_value_cast = from_type.union_types.any? do |vt|\n      needs_value_cast_inside_union?(vt, to_type)\n    end\n\n    if needs_union_value_cast\n      # Compute the values that need a cast\n      types_needing_cast = from_type.union_types.select do |vt|\n        needs_value_cast_inside_union?(vt, to_type)\n      end\n\n      # Fetch the value's type id\n      from_type_id, union_value_ptr = union_type_and_value_pointer(value, from_type)\n\n      Phi.open(self, to_type, @needs_value) do |phi|\n        types_needing_cast.each_with_index do |type_needing_cast, i|\n          # Find compatible type\n          compatible_type = to_type.union_types.find! { |ut| type_needing_cast.implements?(ut) }\n\n          matches_label, doesnt_match_label = new_blocks \"matches\", \"doesnt_match_label\"\n          cmp_result = equal?(from_type_id, type_id(type_needing_cast))\n          cond cmp_result, matches_label, doesnt_match_label\n\n          position_at_end matches_label\n\n          casted_value = cast_to_pointer(union_value_ptr, type_needing_cast)\n          upcasted_value = upcast(casted_value, compatible_type, type_needing_cast)\n          final_value = upcast(upcasted_value, to_type, compatible_type)\n          phi.add final_value, to_type\n\n          position_at_end doesnt_match_label\n        end\n\n        final_value = cast_to_pointer value, to_type\n        phi.add final_value, to_type, last: true\n      end\n    else\n      upcast_distinct_union_types(value, to_type, from_type)\n    end\n  end\n\n  def upcast_distinct(value, to_type : MixedUnionType, from_type : VoidType)\n    union_ptr = alloca(llvm_type(to_type))\n    store_void_in_union to_type, union_ptr\n    union_ptr\n  end\n\n  def upcast_distinct(value, to_type : MixedUnionType, from_type : BoolType)\n    union_ptr = alloca(llvm_type(to_type))\n    store_bool_in_union to_type, union_ptr, value\n    union_ptr\n  end\n\n  def upcast_distinct(value, to_type : MixedUnionType, from_type : NilType)\n    union_ptr = alloca(llvm_type(to_type))\n    store_nil_in_union to_type, union_ptr\n    union_ptr\n  end\n\n  def upcast_distinct(value, to_type : MixedUnionType, from_type : Type)\n    # It might happen that from_type is not of the union but it's compatible with one of them.\n    # We need to first cast the value to the compatible type and to to_type\n    case from_type\n    when TupleInstanceType, NamedTupleInstanceType\n      unless to_type.union_types.any? &.==(from_type)\n        compatible_type = to_type.union_types.find! { |ut| from_type.implements?(ut) }\n        value = upcast(value, compatible_type, from_type)\n        return upcast(value, to_type, compatible_type)\n      end\n    end\n\n    union_ptr = alloca(llvm_type(to_type))\n    store_in_union(to_type, union_ptr, from_type, to_rhs(value, from_type))\n    union_ptr\n  end\n\n  def upcast_distinct(value, to_type : EnumType, from_type : Type)\n    value\n  end\n\n  def upcast_distinct(value, to_type : TupleInstanceType, from_type : TupleInstanceType)\n    target_ptr = alloca llvm_type(to_type)\n    assign(target_ptr, to_type, from_type, value)\n    target_ptr\n  end\n\n  def upcast_distinct(value, to_type : NamedTupleInstanceType, from_type : NamedTupleInstanceType)\n    target_ptr = alloca llvm_type(to_type)\n    assign(target_ptr, to_type, from_type, value)\n    target_ptr\n  end\n\n  def upcast_distinct(value, to_type : GenericClassInstanceType, from_type : Type)\n    cast_to value, to_type\n  end\n\n  def upcast_distinct(value, to_type : Type, from_type : Type)\n    raise \"BUG: trying to upcast #{to_type} (#{to_type.class}) <- #{from_type} (#{from_type.class})\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/class_var.cr",
    "content": "require \"./codegen\"\n\n# The logic for class vars is similar to that of constants (in const.cr):\n# if a class variable has an initializer, we execute it the moment the codegen\n# visits that assignment. We also initialize it with that value if the class\n# variable is read. There's an \"initialized\" flag too.\n\nclass Crystal::CodeGenVisitor\n  def declare_class_var(class_var : MetaTypeVar)\n    global_name = class_var_global_name(class_var)\n    global = @main_mod.globals[global_name]?\n    unless global\n      main_llvm_type = @main_llvm_typer.llvm_type(class_var.type)\n      global = @main_mod.globals.add(main_llvm_type, global_name)\n      global.linkage = LLVM::Linkage::Internal if @single_module\n      global.thread_local = true if class_var.thread_local?\n      if !global.initializer && type.includes_type?(@program.nil_type)\n        global.initializer = main_llvm_type.null\n      end\n    end\n    global\n  end\n\n  def declare_class_var_initialized_flag(class_var : MetaTypeVar)\n    initialized_flag_name = class_var_global_initialized_name(class_var)\n    initialized_flag = @main_mod.globals[initialized_flag_name]?\n    unless initialized_flag\n      initialized_flag = @main_mod.globals.add(@main_llvm_context.int1, initialized_flag_name)\n      initialized_flag.initializer = @main_llvm_context.int1.const_int(0)\n      initialized_flag.linkage = LLVM::Linkage::Internal if @single_module\n      initialized_flag.thread_local = true if class_var.thread_local?\n    end\n    initialized_flag\n  end\n\n  def declare_class_var_and_initialized_flag(class_var : MetaTypeVar)\n    {declare_class_var(class_var), declare_class_var_initialized_flag(class_var)}\n  end\n\n  def declare_class_var_and_initialized_flag_in_this_module(class_var : MetaTypeVar)\n    global, initialized_flag = declare_class_var_and_initialized_flag(class_var)\n    global = ensure_class_var_in_this_module(global, class_var)\n    initialized_flag = ensure_class_var_initialized_flag_in_this_module(initialized_flag, class_var)\n    {global, initialized_flag}\n  end\n\n  def ensure_class_var_in_this_module(global, class_var)\n    if @llvm_mod != @main_mod\n      global_name = class_var_global_name(class_var)\n      global = @llvm_mod.globals[global_name]?\n      unless global\n        global = @llvm_mod.globals.add(llvm_type(class_var.type), global_name)\n        global.thread_local = true if class_var.thread_local?\n      end\n    end\n    global\n  end\n\n  def ensure_class_var_initialized_flag_in_this_module(initialized_flag, class_var)\n    if @llvm_mod != @main_mod\n      initialized_flag_name = class_var_global_initialized_name(class_var)\n      initialized_flag = @llvm_mod.globals[initialized_flag_name]?\n      unless initialized_flag\n        initialized_flag = @llvm_mod.globals.add(llvm_context.int1, initialized_flag_name)\n        initialized_flag.thread_local = true if class_var.thread_local?\n      end\n    end\n    initialized_flag\n  end\n\n  def initialize_class_var(class_var : ClassVar)\n    initialize_class_var(class_var.var)\n  end\n\n  def initialize_class_var(class_var : MetaTypeVar)\n    initializer = class_var.initializer\n\n    if initializer\n      initialize_class_var(class_var, initializer)\n    end\n  end\n\n  def initialize_class_var(class_var : MetaTypeVar, initializer : ClassVarInitializer)\n    init_func = create_initialize_class_var_function(class_var, initializer)\n    init_func = check_main_fun(init_func.func.name, init_func) if init_func\n\n    # For unsafe class var we just initialize them without\n    # using a flag to know if they were initialized\n    if class_var.uninitialized? || !init_func || !class_var.read?\n      class_var.no_init_flag = true\n\n      global = declare_class_var(class_var)\n      global = ensure_class_var_in_this_module(global, class_var)\n      if init_func\n        set_current_debug_location initializer.node if @debug.line_numbers?\n        call init_func\n      end\n      return global\n    end\n\n    global, initialized_flag = declare_class_var_and_initialized_flag_in_this_module(class_var)\n\n    lazy_initialize_class_var(initializer.node, init_func.not_nil!, global, initialized_flag)\n  end\n\n  def lazy_initialize_class_var(node, init_func, global, initialized_flag)\n    set_current_debug_location node if @debug.line_numbers?\n    run_once(initialized_flag, init_func)\n\n    global\n  end\n\n  def create_initialize_class_var_function(class_var, initializer)\n    return nil if class_var.simple_initializer?\n    type = class_var.type\n    node = initializer.node\n    init_function_name = \"~#{class_var_global_initialized_name(class_var)}\"\n\n    typed_fun?(@main_mod, init_function_name) || begin\n      global = declare_class_var(class_var)\n\n      discard = false\n      new_func = in_main do\n        define_main_function(init_function_name, ([] of LLVM::Type), llvm_context.void, needs_alloca: true) do |func|\n          set_internal_fun_debug_location(func, init_function_name, node.location)\n\n          with_cloned_context do\n            # \"self\" in a constant is the class_var owner\n            context.type = class_var.owner\n\n            # Start with fresh variables\n            context.vars = LLVMVars.new\n\n            alloca_vars initializer.meta_vars\n\n            request_value(node)\n\n            node_type = node.type\n\n            if node_type.nil_type? && !type.nil_type?\n              global.initializer = llvm_type(type).null\n              discard = true\n            elsif @last.constant? && (type.is_a?(PrimitiveType) || type.is_a?(EnumType))\n              global.initializer = @last\n              discard = type.is_a?(EnumType) || node.simple_literal?\n            else\n              global.initializer = llvm_type(type).null\n              assign global, type, node.type, @last\n            end\n\n            ret\n          end\n        end\n      end\n\n      if discard\n        class_var.simple_initializer = true\n        new_func.func.delete\n        nil\n      else\n        new_func\n      end\n    end\n  end\n\n  def read_class_var(node : ClassVar)\n    set_current_debug_location node if @debug.line_numbers?\n    read_class_var(node.var)\n  end\n\n  def read_class_var(class_var : MetaTypeVar)\n    last = read_class_var_ptr(class_var)\n    to_lhs last, class_var.type\n  end\n\n  def read_class_var_ptr(node : ClassVar)\n    class_var = node.var\n    read_class_var_ptr(class_var)\n  end\n\n  def read_class_var_ptr(class_var : MetaTypeVar)\n    class_var.read = true\n\n    owner = class_var.owner\n    case owner\n    when VirtualType\n      return read_virtual_class_var_ptr(class_var, owner)\n    when VirtualMetaclassType\n      return read_virtual_metaclass_class_var_ptr(class_var, owner)\n    end\n\n    initializer = class_var.initializer\n    if !initializer || class_var.uninitialized? || class_var.no_init_flag?\n      # Read directly without init flag, but make sure to declare the global in this module too\n      return get_class_var_global(class_var)\n    end\n\n    initializer = initializer.not_nil!\n\n    func = create_read_class_var_function(class_var, initializer)\n    if func\n      func = check_main_fun func.func.name, func\n      call func\n    else\n      get_class_var_global(class_var)\n    end\n  end\n\n  def get_class_var_global(class_var)\n    global_name = class_var_global_name(class_var)\n    global = get_global global_name, class_var.type, class_var\n    global = ensure_class_var_in_this_module(global, class_var)\n    global\n  end\n\n  def read_virtual_class_var_ptr(class_var, owner)\n    self_type_id = type_id(llvm_self, owner)\n    read_function_name = \"~#{class_var_global_name(class_var)}:read\"\n    func = typed_fun?(@main_mod, read_function_name) ||\n           create_read_virtual_class_var_ptr_function(read_function_name, class_var, owner)\n    func = check_main_fun read_function_name, func\n    call func, self_type_id\n  end\n\n  def create_read_virtual_class_var_ptr_function(fun_name, class_var, owner)\n    in_main do\n      define_main_function(fun_name, [llvm_context.int32], llvm_type(class_var.type).pointer) do |func|\n        set_internal_fun_debug_location(func, fun_name)\n\n        self_type_id = func.params[0]\n\n        cmp = equal?(self_type_id, type_id(owner.base_type))\n\n        current_type_label, next_type_label = new_blocks \"current_type\", \"next_type\"\n        cond cmp, current_type_label, next_type_label\n\n        position_at_end current_type_label\n        last = read_class_var_ptr(owner.base_type.lookup_class_var(class_var.name))\n        ret last\n\n        position_at_end next_type_label\n\n        owner.base_type.all_subclasses.each do |subclass|\n          next unless subclass.is_a?(ClassVarContainer)\n\n          cmp = equal?(self_type_id, type_id(subclass))\n\n          current_type_label, next_type_label = new_blocks \"current_type\", \"next_type\"\n          cond cmp, current_type_label, next_type_label\n\n          position_at_end current_type_label\n          last = read_class_var_ptr(subclass.lookup_class_var(class_var.name))\n          ret last\n\n          position_at_end next_type_label\n        end\n\n        unreachable\n      end\n    end\n  end\n\n  def read_virtual_metaclass_class_var_ptr(class_var, owner)\n    self_type_id = type_id(llvm_self, owner)\n    read_function_name = \"~#{class_var_global_name(class_var)}:read\"\n    func = typed_fun?(@main_mod, read_function_name) ||\n           create_read_virtual_metaclass_var_ptr_function(read_function_name, class_var, owner)\n    func = check_main_fun read_function_name, func\n    call func, self_type_id\n  end\n\n  def create_read_virtual_metaclass_var_ptr_function(fun_name, class_var, owner)\n    in_main do\n      define_main_function(fun_name, [llvm_context.int32], llvm_type(class_var.type).pointer) do |func|\n        set_internal_fun_debug_location(func, fun_name)\n\n        self_type_id = func.params[0]\n\n        cmp = equal?(self_type_id, type_id(owner.base_type.metaclass))\n\n        current_type_label, next_type_label = new_blocks \"current_type\", \"next_type\"\n        cond cmp, current_type_label, next_type_label\n\n        position_at_end current_type_label\n        last = read_class_var_ptr(owner.base_type.lookup_class_var(class_var.name))\n        ret last\n\n        position_at_end next_type_label\n\n        owner.base_type.instance_type.all_subclasses.each do |subclass|\n          next unless subclass.is_a?(ClassVarContainer)\n\n          cmp = equal?(self_type_id, type_id(subclass.metaclass))\n\n          current_type_label, next_type_label = new_blocks \"current_type\", \"next_type\"\n          cond cmp, current_type_label, next_type_label\n\n          position_at_end current_type_label\n          last = read_class_var_ptr(subclass.lookup_class_var(class_var.name))\n          ret last\n\n          position_at_end next_type_label\n        end\n        unreachable\n      end\n    end\n  end\n\n  def create_read_class_var_function(class_var, initializer)\n    fun_name = \"~#{class_var_global_name(class_var)}:read\"\n    if func = typed_fun?(@main_mod, fun_name)\n      return func\n    end\n\n    init_func = create_initialize_class_var_function(class_var, initializer)\n    return nil if !init_func\n\n    global, initialized_flag = declare_class_var_and_initialized_flag(class_var)\n\n    in_main do\n      define_main_function(fun_name, ([] of LLVM::Type), llvm_type(class_var.type).pointer) do |func|\n        set_internal_fun_debug_location(func, fun_name, initializer.node.location)\n        init_func = check_main_fun init_func.func.name, init_func\n        ret lazy_initialize_class_var(initializer.node, init_func, global, initialized_flag)\n      end\n    end\n  end\n\n  def class_var_global_name(class_var : MetaTypeVar)\n    \"#{class_var.owner.llvm_name}#{class_var.name.gsub('@', ':')}\"\n  end\n\n  def class_var_global_initialized_name(class_var : MetaTypeVar)\n    \"#{class_var.owner.llvm_name}#{class_var.name.gsub('@', ':')}:init\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/codegen.cr",
    "content": "require \"llvm\"\nrequire \"../syntax/parser\"\nrequire \"../syntax/visitor\"\nrequire \"../semantic\"\nrequire \"../program\"\nrequire \"./llvm_builder_helper\"\nrequire \"./abi/*\"\n\nmodule Crystal\n  MAIN_NAME              = \"__crystal_main\"\n  RAISE_NAME             = \"__crystal_raise\"\n  RAISE_OVERFLOW_NAME    = \"__crystal_raise_overflow\"\n  RAISE_CAST_FAILED_NAME = \"__crystal_raise_cast_failed\"\n  MALLOC_NAME            = \"__crystal_malloc64\"\n  MALLOC_ATOMIC_NAME     = \"__crystal_malloc_atomic64\"\n  REALLOC_NAME           = \"__crystal_realloc64\"\n  GET_EXCEPTION_NAME     = \"__crystal_get_exception\"\n  ONCE_INIT              = \"__crystal_once_init\"\n  ONCE                   = \"__crystal_once\"\n\n  class Program\n    def run(code, filename : String? = nil, debug = Debug::Default)\n      parser = new_parser(code)\n      parser.filename = filename\n      node = parser.parse\n      node = normalize node\n      node = semantic node\n      evaluate node, debug: debug\n    end\n\n    def evaluate(node, debug = Debug::Default)\n      visitor = CodeGenVisitor.new self, node, single_module: true, debug: debug\n      visitor.accept node\n      visitor.process_finished_hooks\n      visitor.finish\n\n      llvm_mod = visitor.modules[\"\"].mod\n      llvm_mod.target = target_machine.triple\n\n      main = visitor.typed_fun?(llvm_mod, MAIN_NAME).not_nil!\n\n      # It seems the JIT doesn't like it if we return an empty type (struct {})\n      llvm_context = llvm_mod.context\n      main_return_type = main.type.return_type\n      main_return_type = llvm_context.void if node.type.nil_type?\n\n      wrapper_type = LLVM::Type.function([] of LLVM::Type, main_return_type)\n      wrapper = llvm_mod.functions.add(\"__evaluate_wrapper\", wrapper_type) do |func|\n        func.basic_blocks.append \"entry\" do |builder|\n          argc = llvm_context.int32.const_int(0)\n          argv = llvm_context.void_pointer.pointer.null\n          ret = builder.call(main.type, main.func, [argc, argv])\n          (node.type.void? || node.type.nil_type?) ? builder.ret : builder.ret(ret)\n        end\n      end\n\n      llvm_mod.verify\n\n      # We use the block form so we dispose the JIT as soon as possible.\n      # This isn't really needed, but on LLVM 3.9 it sometimes messes the\n      # stack trace for unwind, or so it seems, or there's something else\n      # that for now we can't understand.\n      #\n      # Since this only affects specs (not the code that is generated by\n      # the compiler) we can postpone the understanding and continue\n      # running specs just fine.\n      #\n      # See https://github.com/crystal-lang/crystal/pull/3439\n      LLVM::JITCompiler.new(llvm_mod) do |jit|\n        jit.run_function wrapper, [] of LLVM::GenericValue, llvm_context\n      end\n    end\n\n    def run(code, return_type : T.class, filename : String? = nil, debug = Debug::Default) forall T\n      parser = new_parser(code)\n      parser.filename = filename\n      node = parser.parse\n      node = normalize node\n      node = semantic node\n      evaluate node, T, debug: debug\n    end\n\n    def evaluate(node, return_type : T.class, debug = Debug::Default) : T forall T\n      llvm_context =\n        {% if LibLLVM::IS_LT_110 %}\n          LLVM::Context.new\n        {% elsif LibLLVM::IS_LT_210 %}\n          begin\n            ts_ctx = LLVM::Orc::ThreadSafeContext.new\n            ts_ctx.context\n          end\n        {% else %}\n          LLVM::Context.new(dispose_on_finalize: false)\n        {% end %}\n\n      visitor = CodeGenVisitor.new self, node, single_module: true, debug: debug, llvm_context: llvm_context\n      visitor.accept node\n      visitor.process_finished_hooks\n      visitor.finish\n\n      llvm_mod = visitor.modules[\"\"].mod\n      llvm_mod.target = target_machine.triple\n\n      main = visitor.typed_fun?(llvm_mod, MAIN_NAME).not_nil!\n\n      # void (*__evaluate_wrapper)(void*)\n      wrapper_type = LLVM::Type.function([llvm_context.void_pointer], llvm_context.void)\n      wrapper = llvm_mod.functions.add(\"__evaluate_wrapper\", wrapper_type) do |func|\n        func.basic_blocks.append \"entry\" do |builder|\n          argc = llvm_context.int32.const_int(0)\n          argv = llvm_context.void_pointer.pointer.null\n          ret = builder.call(main.type, main.func, [argc, argv])\n          unless node.type.void? || node.type.nil_type?\n            out_ptr = func.params[0]\n            {% if LibLLVM::IS_LT_150 %}\n              out_ptr = builder.bit_cast out_ptr, main.type.return_type.pointer\n            {% end %}\n            builder.store(ret, out_ptr)\n          end\n          builder.ret\n        end\n      end\n\n      llvm_mod.verify\n\n      result = uninitialized T\n\n      {% if LibLLVM::IS_LT_110 %}\n        LLVM::JITCompiler.new(llvm_mod) do |jit|\n          func_ptr = jit.function_address(\"__evaluate_wrapper\")\n          func = Proc(T*, Nil).new(func_ptr, Pointer(Void).null)\n          func.call(pointerof(result))\n        end\n      {% else %}\n        lljit_builder = LLVM::Orc::LLJITBuilder.new\n        lljit = LLVM::Orc::LLJIT.new(lljit_builder)\n\n        {% unless LibLLVM::IS_LT_210 %}\n          ts_ctx = LLVM::Orc::ThreadSafeContext.new(llvm_context)\n        {% end %}\n\n        dylib = lljit.main_jit_dylib\n        dylib.link_symbols_from_current_process(lljit.global_prefix)\n        tsm = LLVM::Orc::ThreadSafeModule.new(llvm_mod, ts_ctx)\n        lljit.add_llvm_ir_module(dylib, tsm)\n\n        func_ptr = lljit.lookup(\"__evaluate_wrapper\")\n        func = Proc(T*, Nil).new(func_ptr, Pointer(Void).null)\n        func.call(pointerof(result))\n      {% end %}\n\n      result\n    end\n\n    def codegen(node, single_module = false, debug = Debug::Default,\n                frame_pointers = FramePointers::Auto)\n      visitor = CodeGenVisitor.new self, node, single_module: single_module,\n        debug: debug, frame_pointers: frame_pointers\n      visitor.accept node\n      visitor.process_finished_hooks\n      visitor.finish\n\n      visitor.modules\n    end\n\n    def llvm_id\n      @llvm_id ||= LLVMId.new(self)\n    end\n\n    def llvm_typer\n      @llvm_typer ||= LLVMTyper.new(self, LLVM::Context.new)\n    end\n\n    def size_of(type)\n      if type.void?\n        # We need `sizeof(Void)` to be 1 because doing\n        # `Pointer(Void).malloc` must work like `Pointer(UInt8).malloc`,\n        # that is, consider Void like the size of a byte.\n        1_u64\n      else\n        llvm_typer.size_of(llvm_typer.llvm_type(type))\n      end\n    end\n\n    def instance_size_of(type)\n      llvm_typer.size_of(llvm_typer.llvm_struct_type(type))\n    end\n\n    def align_of(type)\n      if type.void?\n        # We need `alignof(Void)` to be 1 because it is effectively the smallest\n        # possible alignment for any type.\n        1_u32\n      else\n        llvm_typer.align_of(llvm_typer.llvm_type(type))\n      end\n    end\n\n    def instance_align_of(type)\n      llvm_typer.align_of(llvm_typer.llvm_struct_type(type))\n    end\n\n    def offset_of(type, element_index)\n      return 0_u64 if type.extern_union? || type.is_a?(StaticArrayInstanceType)\n      llvm_typer.offset_of(llvm_typer.llvm_type(type), element_index)\n    end\n\n    def instance_offset_of(type, element_index)\n      # extern unions and static arrays must be value types, which always use\n      # the above `offset_of` instead\n      llvm_typer.offset_of(llvm_typer.llvm_struct_type(type), element_index + 1)\n    end\n  end\n\n  class CodeGenVisitor < Visitor\n    SYMBOL_TABLE_NAME = \":symbol_table\"\n\n    include LLVMBuilderHelper\n\n    getter llvm_mod : LLVM::Module\n    getter builder : CrystalLLVMBuilder\n    getter main : LLVM::Function\n    getter modules : Hash(String, ModuleInfo)\n    getter context : Context\n    getter llvm_typer : LLVMTyper\n    getter alloca_block : LLVM::BasicBlock\n    getter entry_block : LLVM::BasicBlock\n    getter personality_name : String\n    property last : LLVM::Value\n\n    class LLVMVar\n      getter pointer : LLVM::Value\n      getter type : Type\n\n      # Normally a variable is associated with an alloca.\n      # So for example, if you have a \"x = Reference.new\" you will have\n      # an \"Reference**\" llvm value and you need to load that value\n      # to access it.\n      # However, the \"self\" argument is not copied to a local variable:\n      # it's accessed from the arguments list, and is a \"Reference*\"\n      # llvm value, so in a way it's \"already loaded\".\n      # This field is true if that's the case.\n      getter already_loaded : Bool\n      getter debug_variable_created : Bool\n\n      def initialize(@pointer, @type, @already_loaded = false, @debug_variable_created = false)\n      end\n    end\n\n    alias LLVMVars = Hash(String, LLVMVar)\n\n    record Handler, node : ExceptionHandler, context : Context\n    record StringKey, mod : LLVM::Module, string : String\n    record ModuleInfo, mod : LLVM::Module, typer : LLVMTyper, builder : CrystalLLVMBuilder\n\n    @abi : ABI\n    @main_ret_type : Type\n    @argc : LLVM::Value\n    @argv : LLVM::Value\n    @rescue_block : LLVM::BasicBlock?\n    @catch_pad : LLVM::Value?\n    @fun_types : Hash({LLVM::Module, String}, LLVM::Type)\n    @sret_value : LLVM::Value?\n    @cant_pass_closure_to_c_exception_call : Call?\n    @main_llvm_context : LLVM::Context\n    @main_llvm_typer : LLVMTyper\n    @main_module_info : ModuleInfo\n    @main_builder : CrystalLLVMBuilder\n    @call_location : Location?\n\n    @malloc_fun : LLVMTypedFunction?\n    @malloc_atomic_fun : LLVMTypedFunction?\n    @realloc_fun : LLVMTypedFunction?\n    @raise_overflow_fun : LLVMTypedFunction?\n    @raise_cast_failed_fun : LLVMTypedFunction?\n    @c_malloc_fun : LLVMTypedFunction?\n    @c_realloc_fun : LLVMTypedFunction?\n\n    def initialize(@program : Program, @node : ASTNode,\n                   @single_module : Bool = false,\n                   @debug = Debug::Default,\n                   @frame_pointers : FramePointers = :auto,\n                   @llvm_context : LLVM::Context = LLVM::Context.new)\n      @abi = ABI.from(@program.target_machine)\n      # LLVM::Context.register(@llvm_context, \"main\")\n      @llvm_mod = configure_module(@llvm_context.new_module(\"main_module\"))\n      @main_mod = @llvm_mod\n      @main_llvm_context = @main_mod.context\n      @llvm_typer = LLVMTyper.new(@program, @llvm_context)\n      @main_llvm_typer = @llvm_typer\n      @main_ret_type = node.type? || @program.nil_type\n      ret_type = @llvm_typer.llvm_return_type(@main_ret_type)\n      main_type = LLVM::Type.function([llvm_context.int32, llvm_context.void_pointer.pointer], ret_type)\n      @main = @llvm_mod.functions.add(MAIN_NAME, main_type)\n      @main.linkage = LLVM::Linkage::Internal if @single_module\n      @fun_types = { {@llvm_mod, MAIN_NAME} => main_type }\n\n      if @program.has_flag?(\"msvc\")\n        @personality_name = \"__CxxFrameHandler3\"\n        @main.personality_function = windows_personality_fun.func\n      else\n        @personality_name = \"__crystal_personality\"\n      end\n\n      @context = Context.new @main, main_type, @program\n      @context.return_type = @main_ret_type\n\n      @argc = @main.params[0]\n      @argc.name = \"argc\"\n\n      @argv = @main.params[1]\n      @argv.name = \"argv\"\n\n      @builder = new_builder(@main_llvm_context)\n      @main_builder = @builder\n\n      @main_module_info = ModuleInfo.new(@main_mod, @main_llvm_typer, @builder)\n      @modules = {\"\" => @main_module_info} of String => ModuleInfo\n      @types_to_modules = {} of Type => ModuleInfo\n\n      set_internal_fun_debug_location(@main, MAIN_NAME, nil)\n\n      @alloca_block, @entry_block = new_entry_block_chain \"alloca\", \"entry\"\n\n      @in_lib = false\n      @strings = {} of StringKey => LLVM::Value\n      @symbols = {} of String => Int32\n      @symbols_by_index = [] of String\n      @symbol_table_values = [] of LLVM::Value\n      program.symbols.each_with_index do |sym, index|\n        @symbols[sym] = index\n        @symbols_by_index << sym\n        @symbol_table_values << build_string_constant(sym, sym)\n      end\n\n      unless program.symbols.empty?\n        symbol_table = define_symbol_table @llvm_mod, @llvm_typer\n        symbol_table.initializer = llvm_type(@program.string).const_array(@symbol_table_values)\n      end\n\n      program.const_slices.each_value do |info|\n        define_slice_constant(info)\n      end\n\n      @last = llvm_nil\n      @fun_literal_count = 0\n\n      # This flag is to generate less code. If there's an if in the middle\n      # of a series of expressions we don't need the result, so there's no\n      # need to build a phi for it.\n      # Also, we don't need the value of unions returned from calls if they\n      # are not going to be used.\n      @needs_value = true\n\n      @unused_fun_defs = [] of FunDef\n      @proc_counts = Hash(String, Int32).new(0)\n\n      # We need to define __crystal_malloc and __crystal_realloc as soon as possible,\n      # to avoid some memory being allocated with plain malloc.\n      codegen_well_known_functions @node\n\n      initialize_predefined_constants\n\n      if @debug.line_numbers?\n        set_current_debug_location Location.new(@program.filename || \"(no name)\", 1, 1)\n      end\n\n      once_init\n      initialize_simple_constants\n\n      alloca_vars @program.vars, @program\n\n      emit_vars_debug_info(@program.vars) if @debug.variables?\n    end\n\n    getter llvm_context\n\n    def configure_module(llvm_mod)\n      llvm_mod.data_layout = @program.target_machine.data_layout\n\n      # enable branch authentication instructions (BTI)\n      if @program.has_flag?(\"aarch64\")\n        if @program.has_flag?(\"branch-protection=bti\")\n          llvm_mod.add_flag(:override, \"branch-target-enforcement\", 1)\n        end\n      end\n\n      # enable control flow enforcement protection (CET): IBT and/or SHSTK\n      if @program.has_flag?(\"x86_64\") || @program.has_flag?(\"i386\")\n        if @program.has_flag?(\"cf-protection=branch\") || @program.has_flag?(\"cf-protection=full\")\n          llvm_mod.add_flag(:override, \"cf-protection-branch\", 1)\n        end\n\n        if @program.has_flag?(\"cf-protection=return\") || @program.has_flag?(\"cf-protection=full\")\n          llvm_mod.add_flag(:override, \"cf-protection-return\", 1)\n        end\n      end\n\n      llvm_mod\n    end\n\n    def new_builder(llvm_context)\n      wrap_builder(llvm_context.new_builder)\n    end\n\n    # Here we only initialize simple constants, those\n    # that has simple values like 1, \"foo\" and other literals.\n    def initialize_simple_constants\n      @program.const_initializers.each do |initializer|\n        # Simple constants are never initialized: they are always inlined\n        next if initializer.compile_time_value\n        next unless initializer.simple?\n\n        initialize_simple_const(initializer)\n      end\n    end\n\n    def wrap_builder(builder)\n      CrystalLLVMBuilder.new builder, llvm_typer, c_printf_fun\n    end\n\n    def define_symbol_table(llvm_mod, llvm_typer)\n      llvm_mod.globals[SYMBOL_TABLE_NAME]? || begin\n        global = llvm_mod.globals.add llvm_typer.llvm_type(@program.string).array(@symbol_table_values.size), SYMBOL_TABLE_NAME\n        if llvm_mod != @main_mod\n          global.linkage = LLVM::Linkage::External\n        elsif @single_module\n          global.linkage = LLVM::Linkage::Internal\n        end\n        global.global_constant = true\n        global\n      end\n    end\n\n    def define_slice_constant(info : Program::ConstSliceInfo)\n      initializer = const_slice_data_array(info)\n      global = @llvm_mod.globals.add(initializer.type, info.name)\n      if @llvm_mod != @main_mod\n        global.linkage = LLVM::Linkage::External\n      elsif @single_module\n        global.linkage = LLVM::Linkage::Internal\n      end\n      global.global_constant = true\n      global.initializer = initializer\n    end\n\n    private def const_slice_data_array(info : Program::ConstSliceInfo)\n      args = info.args.to_unsafe\n      kind = info.element_type\n      llvm_element_type = llvm_type(@program.type_from_literal_kind(kind))\n\n      {% unless LibLLVM::IS_LT_210 %}\n        case kind\n        when .u8?, .u16?, .u32?, .u64?, .i8?, .i16?, .i32?, .i64?, .f32?, .f64?\n          return llvm_element_type.const_data_array(info.to_bytes)\n        end\n      {% end %}\n\n      case kind\n      when .u8?, .i8?\n        return llvm_context.const_bytes(info.to_bytes)\n      end\n\n      llvm_elements = Array.new(info.args.size) do |i|\n        num = args[i].as(NumberLiteral)\n        case kind\n        in .i8?   then llvm_element_type.const_int(num.value.to_i8)\n        in .i16?  then llvm_element_type.const_int(num.value.to_i16)\n        in .i32?  then llvm_element_type.const_int(num.value.to_i32)\n        in .i64?  then llvm_element_type.const_int(num.value.to_i64)\n        in .i128? then llvm_element_type.const_int(num.value.to_i128)\n        in .u8?   then llvm_element_type.const_int(num.value.to_u8)\n        in .u16?  then llvm_element_type.const_int(num.value.to_u16)\n        in .u32?  then llvm_element_type.const_int(num.value.to_u32)\n        in .u64?  then llvm_element_type.const_int(num.value.to_u64)\n        in .u128? then llvm_element_type.const_int(num.value.to_u128)\n        in .f32?  then llvm_element_type.const_float(num.value)\n        in .f64?  then llvm_element_type.const_double(num.value)\n        end\n      end\n      llvm_element_type.const_array(llvm_elements)\n    end\n\n    class CodegenWellKnownFunctions < Visitor\n      @codegen : CodeGenVisitor\n\n      def initialize(@codegen)\n      end\n\n      def visit(node : FileNode)\n        true\n      end\n\n      def visit(node : Expressions)\n        true\n      end\n\n      def visit(node : FunDef)\n        case node.name\n        when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME,\n             @codegen.personality_name, GET_EXCEPTION_NAME, RAISE_OVERFLOW_NAME,\n             RAISE_CAST_FAILED_NAME, ONCE_INIT, ONCE\n          @codegen.accept node\n        end\n\n        false\n      end\n\n      def visit(node : ASTNode)\n        false\n      end\n    end\n\n    def codegen_well_known_functions(node)\n      visitor = CodegenWellKnownFunctions.new(self)\n      node.accept visitor\n    end\n\n    def visit_any(node)\n      !@builder.end\n    end\n\n    def type\n      context.type.not_nil!\n    end\n\n    def finish\n      clear_current_debug_location if @debug.line_numbers?\n      codegen_return @main_ret_type\n\n      # If there are no instructions in the alloca block and the\n      # const block, we just removed them (less noise)\n      if alloca_block.instructions.empty?\n        alloca_block.delete\n      else\n        br_block_chain alloca_block, entry_block\n      end\n\n      @unused_fun_defs.each do |node|\n        codegen_fun node.real_name, node.external, @program, is_exported_fun: true\n      end\n\n      env_dump = ENV[\"DUMP\"]?\n      case env_dump\n      when Nil\n        # Nothing\n      when \"1\"\n        dump_all_llvm = true\n      else\n        dump_llvm_regex = Regex.new(env_dump)\n      end\n\n      @modules.each do |name, info|\n        mod = info.mod\n        push_debug_info_metadata(mod) unless @debug.none?\n\n        mod.dump if dump_all_llvm || name =~ dump_llvm_regex\n\n        # Always run verifications so we can catch bugs earlier and more often.\n        # We can probably remove this, or only enable this when compiling in\n        # release mode, once we reach 1.0.\n        mod.verify\n      end\n\n      if type_info_path = ENV[\"CRYSTAL_DUMP_TYPE_INFO\"]?.presence\n        dump_type_info(type_info_path)\n      end\n      dump_type_id if ENV[\"CRYSTAL_DUMP_TYPE_ID\"]? == \"1\"\n    end\n\n    def visit(node : Annotation)\n      false\n    end\n\n    def visit(node : FunDef)\n      if @in_lib\n        return false\n      end\n\n      unless node.external.dead?\n        # Mark as dead so we don't generate it twice\n        # (can happen with well known functions like __crystal_raise)\n        node.external.dead = true\n\n        if node.external.used?\n          codegen_fun node.real_name, node.external, @program, is_exported_fun: true\n        else\n          # If the fun is not invoked we codegen it at the end so\n          # we don't have issues with constants being used before\n          # they are declared.\n          # But, apparently, llvm requires us to define them so that\n          # calls can find them, so we do so.\n          codegen_fun node.real_name, node.external, @program, is_exported_fun: false\n          @unused_fun_defs << node\n        end\n      end\n\n      false\n    end\n\n    def visit(node : FileNode)\n      with_context(Context.new(context.fun, context.fun_type, context.type)) do\n        file_module = @program.file_module(node.filename)\n        if vars = file_module.vars?\n          set_current_debug_location Location.new(node.filename, 1, 1) if @debug.line_numbers?\n          alloca_vars vars, file_module\n\n          emit_vars_debug_info(vars) if @debug.variables?\n        end\n        accept node.node\n        @last = llvm_nil\n      end\n\n      false\n    end\n\n    def visit(node : Nop)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : NilLiteral)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : BoolLiteral)\n      @last = int1(node.value ? 1 : 0)\n      false\n    end\n\n    def visit(node : CharLiteral)\n      @last = int32(node.value.ord)\n      false\n    end\n\n    def visit(node : NumberLiteral)\n      case node.kind\n      in .i8?\n        @last = int8(node.value.to_i8)\n      in .u8?\n        @last = int8(node.value.to_u8)\n      in .i16?\n        @last = int16(node.value.to_i16)\n      in .u16?\n        @last = int16(node.value.to_u16)\n      in .i32?\n        @last = int32(node.value.to_i32)\n      in .u32?\n        @last = int32(node.value.to_u32)\n      in .i64?\n        @last = int64(node.value.to_i64)\n      in .u64?\n        @last = int64(node.value.to_u64)\n      in .i128?\n        @last = int128(node.value.to_i128)\n      in .u128?\n        @last = int128(node.value.to_u128)\n      in .f32?\n        @last = float32(node.value)\n      in .f64?\n        @last = float64(node.value)\n      end\n      false\n    end\n\n    def visit(node : StringLiteral)\n      @last = build_string_constant(node.value, node.value)\n      false\n    end\n\n    def visit(node : SymbolLiteral)\n      @last = int(@symbols[node.value])\n      false\n    end\n\n    def visit(node : TupleLiteral)\n      request_value do\n        type = node.type.as(TupleInstanceType)\n\n        if node.elements.any?(Splat)\n          tuple_size = node.elements.sum do |exp|\n            exp.is_a?(Splat) ? exp.type.as(TupleInstanceType).tuple_types.size : 1\n          end\n          exp_values = Array({Type, LLVM::Value}).new(tuple_size)\n\n          node.elements.each do |exp|\n            accept exp\n\n            if exp.is_a?(Splat)\n              tuple_type = exp.type.as(TupleInstanceType)\n              tuple_type.tuple_types.each_with_index do |subtype, j|\n                exp_values << {subtype, codegen_tuple_indexer(tuple_type, @last, j)}\n              end\n            else\n              exp_values << {exp.type, @last}\n            end\n          end\n\n          @last = allocate_tuple(type) do |_, i|\n            exp_values[i]\n          end\n        else\n          @last = allocate_tuple(type) do |tuple_type, i|\n            exp = node.elements[i]\n            accept exp\n            {exp.type, @last}\n          end\n        end\n      end\n      false\n    end\n\n    def visit(node : NamedTupleLiteral)\n      request_value do\n        type = node.type.as(NamedTupleInstanceType)\n        struct_type = llvm_type(type)\n        tuple = alloca struct_type\n        node.entries.each do |entry|\n          accept entry.value\n          index = type.name_index(entry.key).not_nil!\n          entry_type = type.entries[index].type\n          assign aggregate_index(struct_type, tuple, index), entry_type, entry.value.type, @last\n        end\n        @last = tuple\n      end\n      false\n    end\n\n    def visit(node : PointerOf)\n      @last = case node_exp = node.exp\n              when Var\n                context.vars[node_exp.name].pointer\n              when InstanceVar\n                instance_var_ptr context.type.remove_typedef, node_exp.name, llvm_self_ptr\n              when ClassVar\n                # Make sure the class var is initializer before taking a pointer of it\n                if node_exp.var.initializer\n                  initialize_class_var(node_exp)\n                end\n                get_global class_var_global_name(node_exp.var), node_exp.type, node_exp.var\n              when Global\n                node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n              when Path\n                # Make sure the constant is initialized before taking a pointer of it\n                const = node_exp.target_const.not_nil!\n                read_const_pointer(const)\n              when ReadInstanceVar\n                accept node_exp.obj\n                instance_var_ptr (node_exp.obj.type), node_exp.name, @last\n              when Call\n                # lib external var\n                extern = node_exp.dependencies.first.as(External)\n                var = get_external_var(extern)\n                check_c_fun extern.type, var\n              else\n                raise \"BUG: #{node}\"\n              end\n      false\n    end\n\n    def visit(node : ProcLiteral)\n      fun_literal_name = fun_literal_name(node)\n      is_closure = node.def.closure?\n\n      # If we don't care about a proc literal's return type then we mark the associated\n      # def as returning void. This can't be done in the type inference phase because\n      # of bindings and type propagation.\n      if node.force_nil?\n        node.def.set_type @program.nil\n      else\n        # Use proc literal's type, which might have a broader type then the body\n        # (for example, return type: Int32 | String, body: String)\n        node.def.set_type node.return_type\n      end\n\n      the_fun = codegen_fun fun_literal_name, node.def, context.type, fun_module_info: @main_module_info, is_fun_literal: true, is_closure: is_closure\n      the_fun = check_main_fun fun_literal_name, the_fun\n\n      set_current_debug_location(node) if @debug.line_numbers?\n      fun_ptr = cast_to_void_pointer(the_fun.func)\n      if is_closure\n        ctx_ptr = cast_to_void_pointer(context.closure_ptr.not_nil!)\n      else\n        ctx_ptr = llvm_context.void_pointer.null\n      end\n      @last = make_fun node.type, fun_ptr, ctx_ptr\n\n      false\n    end\n\n    def fun_literal_name(node : ProcLiteral)\n      location = node.location.try &.expanded_location\n      if location && (type = node.type?)\n        proc_name = true\n        filename = location.filename.as(String)\n        fun_literal_name = Crystal.safe_mangling(@program, \"~proc#{type}@#{Crystal.relative_filename(filename)}:#{location.line_number}\")\n      else\n        proc_name = false\n        fun_literal_name = \"~fun_literal\"\n      end\n      proc_count = @proc_counts[fun_literal_name]\n      proc_count += 1\n      @proc_counts[fun_literal_name] = proc_count\n\n      if proc_count > 1\n        if proc_name\n          fun_literal_name = \"#{fun_literal_name[0...5]}#{proc_count}#{fun_literal_name[5..-1]}\"\n        else\n          fun_literal_name = \"#{fun_literal_name}#{proc_count}\"\n        end\n      end\n\n      fun_literal_name\n    end\n\n    def visit(node : ProcPointer)\n      owner = node.call.target_def.owner\n\n      if obj = node.obj\n        accept obj\n        call_self = @last\n      elsif owner.passed_as_self?\n        call_self = llvm_self\n      end\n\n      last_fun = target_def_fun(node.call.target_def, owner)\n\n      set_current_debug_location(node) if @debug.line_numbers?\n      fun_ptr = cast_to_void_pointer(last_fun.func)\n      if call_self && !owner.metaclass? && !owner.is_a?(LibType)\n        ctx_ptr = cast_to_void_pointer(call_self)\n      else\n        ctx_ptr = llvm_context.void_pointer.null\n      end\n      @last = make_fun node.type, fun_ptr, ctx_ptr\n\n      false\n    end\n\n    def visit(node : Expressions)\n      old_needs_value = @needs_value\n      @needs_value = false\n\n      last_index = node.expressions.size - 1\n      node.expressions.each_with_index do |exp, i|\n        @needs_value = true if old_needs_value && i == last_index\n        accept exp\n      end\n\n      @needs_value = old_needs_value\n      false\n    end\n\n    def visit(node : Return)\n      node_type = accept_control_expression(node)\n\n      codegen_return_node(node, node_type)\n\n      false\n    end\n\n    def codegen_return_node(node, node_type)\n      old_last = @last\n\n      execute_ensures_until(node.target.as(Def))\n\n      @last = old_last\n\n      if return_phi = context.return_phi\n        return_phi.add @last, node_type\n      else\n        codegen_return node_type\n      end\n    end\n\n    def codegen_return(type : NoReturnType | Nil)\n      unreachable\n    end\n\n    def codegen_return(type : Type)\n      return if @builder.end\n\n      method_type = context.return_type.not_nil!\n      if method_type.void?\n        ret\n      elsif method_type.nil_type?\n        ret\n      elsif method_type.no_return?\n        unreachable\n      else\n        value = upcast(@last, method_type, type)\n        ret to_rhs(value, method_type)\n      end\n    end\n\n    def visit(node : ClassDef)\n      node.hook_expansions.try &.each do |hook|\n        accept hook\n      end\n      accept node.body\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : ModuleDef)\n      accept node.body\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : LibDef)\n      @in_lib = true\n      accept node.body\n      @in_lib = false\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : CStructOrUnionDef)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : EnumDef)\n      node.members.each do |member|\n        if member.is_a?(Assign)\n          accept member\n        end\n      end\n\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : ExternalVar)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : TypeDef)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : Alias)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : TypeOf)\n      # convert virtual metaclasses to non-virtual ones, because only the\n      # non-virtual type IDs are needed\n      set_current_debug_location(node) if @debug.line_numbers?\n      @last = type_id(node.type.devirtualize)\n      false\n    end\n\n    def visit(node : SizeOf)\n      @last = trunc(llvm_size(node.exp.type.sizeof_type), llvm_context.int32)\n      false\n    end\n\n    def visit(node : InstanceSizeOf)\n      @last = trunc(llvm_struct_size(node.exp.type.sizeof_type), llvm_context.int32)\n      false\n    end\n\n    def visit(node : AlignOf)\n      @last = trunc(llvm_alignment(node.exp.type.sizeof_type), llvm_context.int32)\n      false\n    end\n\n    def visit(node : InstanceAlignOf)\n      @last = trunc(llvm_struct_alignment(node.exp.type.sizeof_type), llvm_context.int32)\n      false\n    end\n\n    def visit(node : Include)\n      node.hook_expansions.try &.each do |hook|\n        accept hook\n      end\n\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : Extend)\n      node.hook_expansions.try &.each do |hook|\n        accept hook\n      end\n\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : If)\n      if node.truthy?\n        accept node.cond\n        accept node.then\n        if @needs_value && (node_type = node.type?) && (then_type = node.then.type?)\n          @last = upcast(@last, node_type, then_type)\n        end\n        return false\n      end\n\n      if node.falsey?\n        accept node.cond\n        accept node.else\n        if @needs_value && (node_type = node.type?) && (else_type = node.else.type?)\n          @last = upcast(@last, node_type, else_type)\n        end\n        return false\n      end\n\n      then_block, else_block = new_blocks \"then\", \"else\"\n\n      request_value do\n        set_current_debug_location(node) if @debug.line_numbers?\n        codegen_cond_branch node.cond, then_block, else_block\n      end\n\n      Phi.open(self, node, @needs_value) do |phi|\n        codegen_if_branch phi, node.then, then_block, false\n        codegen_if_branch phi, node.else, else_block, true\n      end\n\n      false\n    end\n\n    def codegen_if_branch(phi, node, branch_block, last)\n      position_at_end branch_block\n      accept node\n      phi.add @last, node.type?, last\n    end\n\n    def visit(node : While)\n      set_ensure_exception_handler(node)\n\n      with_cloned_context do\n        cond = node.cond.single_expression\n        endless_while = cond.true_literal?\n\n        if endless_while\n          while_block = new_block \"while\"\n\n          Phi.open(self, node, @needs_value) do |phi|\n            context.while_block = while_block\n            context.break_phi = phi\n            context.next_phi = nil\n\n            br while_block\n\n            position_at_end while_block\n\n            discard_value(node.body)\n            br while_block\n          end\n        else\n          while_block, body_block, fail_block = new_blocks \"while\", \"body\", \"fail\"\n\n          Phi.open(self, node, @needs_value) do |phi|\n            context.while_block = while_block\n            context.break_phi = phi\n            context.next_phi = nil\n\n            br while_block\n\n            position_at_end while_block\n\n            request_value do\n              set_current_debug_location node.cond if @debug.line_numbers?\n              codegen_cond_branch node.cond, body_block, fail_block\n            end\n\n            position_at_end body_block\n\n            discard_value(node.body)\n            br while_block\n\n            position_at_end fail_block\n\n            phi.add llvm_nil, @program.nil, last: true\n          end\n        end\n      end\n\n      false\n    end\n\n    def codegen_cond_branch(node_cond, then_block, else_block)\n      cond codegen_cond(node_cond), then_block, else_block\n\n      nil\n    end\n\n    def codegen_cond(node : ASTNode)\n      accept node\n      codegen_cond node.type.remove_indirection\n    end\n\n    def visit(node : Not)\n      request_value(node.exp)\n      @last = codegen_cond node.exp.type.remove_indirection\n      @last = not @last\n      false\n    end\n\n    def visit(node : Break)\n      set_current_debug_location(node) if @debug.line_numbers?\n      node_type = accept_control_expression(node)\n\n      case target = node.target\n      when Call\n        if break_phi = context.break_phi\n          old_last = @last\n          execute_ensures_until(target)\n          @last = old_last\n\n          break_phi.add @last, node_type\n          return false\n        end\n      when While\n        if break_phi = context.break_phi\n          old_last = @last\n          execute_ensures_until(target)\n          @last = old_last\n\n          break_phi.add @last, node_type\n          return false\n        end\n      end\n\n      node.raise \"BUG: unknown exit for break\"\n    end\n\n    def visit(node : Next)\n      set_current_debug_location(node) if @debug.line_numbers?\n      node_type = accept_control_expression(node)\n\n      case target = node.target\n      when Block\n        if next_phi = context.next_phi\n          old_last = @last\n          execute_ensures_until(target)\n          @last = old_last\n\n          next_phi.add @last, node_type\n          return false\n        end\n      when While\n        if while_block = context.while_block\n          execute_ensures_until(target)\n          br while_block\n          return false\n        end\n      else\n        # The only possibility is that we are in a captured block,\n        # so this is the same as a return\n        codegen_return_node(node, node_type)\n        return false\n      end\n\n      node.raise \"BUG: unknown exit for next\"\n    end\n\n    def accept_control_expression(node)\n      if exp = node.exp\n        request_value(exp)\n        exp.type? || @program.nil\n      else\n        @last = llvm_nil\n        @program.nil\n      end\n    end\n\n    def visit(node : Assign)\n      return false if node.discarded?\n\n      target, value = node.target, node.value\n      codegen_assign(target, value, node)\n    end\n\n    def codegen_assign(target : Underscore, value, node)\n      accept value\n      false\n    end\n\n    def codegen_assign(target : Path, value, node)\n      const = target.target_const.not_nil!\n      if const.used? && !const.simple? && !const.compile_time_value\n        initialize_const(const)\n      end\n      @last = llvm_nil\n      false\n    end\n\n    def codegen_assign(target, value, node)\n      target_type = target.type?\n\n      # This means it's an instance variable initialize of a generic type,\n      # or a class variable initializer\n      unless target_type\n        if target.is_a?(ClassVar)\n          # This is the case of a class var initializer\n          initialize_class_var(target)\n        end\n        return false\n      end\n\n      # This is the case of an instance variable initializer\n      if target.is_a?(InstanceVar) && !context.type.is_a?(InstanceVarContainer)\n        return false\n      end\n\n      request_value(value)\n\n      return false if value.no_returns?\n\n      last = @last\n\n      set_current_debug_location node if @debug.line_numbers?\n      ptr = case target\n            when InstanceVar\n              instance_var_ptr context.type, target.name, llvm_self_ptr\n            when Global\n              node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n            when ClassVar\n              read_class_var_ptr(target)\n            when Var\n              # Can't assign void\n              return false if target.type.void?\n\n              # If assigning to a special variable in a method that yields,\n              # assign to that variable too.\n              check_assign_to_special_var_in_block(target, value)\n\n              var = context.vars[target.name]?\n              if var\n                target_type = var.type\n                var.pointer\n              else\n                target.raise \"BUG: missing var #{target}\"\n              end\n            else\n              node.raise \"Unknown assign target in codegen: #{target}\"\n            end\n\n      @last = last\n      llvm_value = last\n\n      # When setting an instance variable of an extern type, if it's a Proc\n      # type we need to check that the value is not a closure and just get\n      # the function pointer\n      if target.is_a?(InstanceVar) && context.type.extern? && target.type.proc?\n        llvm_value = check_proc_is_not_closure(llvm_value, target.type)\n      end\n\n      assign ptr, target_type, value.type, llvm_value\n\n      false\n    end\n\n    def check_assign_to_special_var_in_block(target, value)\n      if (block_context = context.block_context?) && target.special_var?\n        var = block_context.vars[target.name]\n        assign var.pointer, var.type, value.type, @last\n      end\n    end\n\n    def get_global(name, type, real_var, initial_value = nil)\n      if real_var.thread_local?\n        get_thread_local(name, type, real_var)\n      else\n        get_global_var(name, type, real_var, initial_value)\n      end\n    end\n\n    def get_global_var(name, type, real_var, initial_value = nil)\n      ptr = @llvm_mod.globals[name]?\n      unless ptr\n        llvm_type = llvm_type(type)\n\n        thread_local = real_var.thread_local?\n\n        # Declare global in this module as external\n        ptr = @llvm_mod.globals.add(llvm_type, name)\n        ptr.thread_local = true if thread_local\n\n        if @llvm_mod == @main_mod\n          ptr.initializer = initial_value || llvm_type.null\n        else\n          ptr.linkage = LLVM::Linkage::External\n\n          # Define it in main if it's not already defined\n          main_ptr = @main_mod.globals[name]?\n          unless main_ptr\n            main_llvm_type = @main_llvm_typer.llvm_type(type)\n            main_ptr = @main_mod.globals.add(main_llvm_type, name)\n            main_ptr.initializer = initial_value || main_llvm_type.null\n            main_ptr.thread_local = true if thread_local\n          end\n        end\n      end\n\n      ptr\n    end\n\n    def get_thread_local(name, type, real_var)\n      # If it's thread local, we use a NoInline function to access it\n      # because of http://lists.llvm.org/pipermail/llvm-dev/2016-February/094736.html\n      #\n      # So, we basically make a function like this (assuming the global is a i32):\n      #\n      # define void @\"*$foo\"(i32**) noinline {\n      #   store i32* @\"$foo\", i32** %0\n      #   ret void\n      # }\n      #\n      # And then in the caller we alloca an i32*, pass it, and then load the pointer,\n      # which is the same as the global, but through a non-inlined function.\n      #\n      # Making a function that just returns the pointer doesn't work: LLVM inlines it.\n      fun_name = \"*#{name}\"\n      thread_local_fun = typed_fun?(@main_mod, fun_name)\n      unless thread_local_fun\n        thread_local_fun = in_main do\n          define_main_function(fun_name, [llvm_type(type).pointer.pointer], llvm_context.void) do |func|\n            set_internal_fun_debug_location(func, fun_name, real_var.location)\n            builder.store get_global_var(name, type, real_var), func.params[0]\n            builder.ret\n          end\n        end\n        thread_local_fun.func.add_attribute LLVM::Attribute::NoInline\n      end\n      thread_local_fun = check_main_fun(fun_name, thread_local_fun)\n      pointer_type = llvm_type(type).pointer\n      indirection_ptr = alloca pointer_type\n      call thread_local_fun, indirection_ptr\n      load pointer_type, indirection_ptr\n    end\n\n    def visit(node : TypeDeclaration)\n      return false if node.discarded?\n\n      var = node.var\n      case var\n      when Var\n        declare_var var\n\n        if value = node.value\n          codegen_assign(var, value, node)\n          return false\n        end\n      when Global\n        node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n      when ClassVar\n        # This is the case of a class var initializer\n        initialize_class_var(var)\n      end\n\n      @last = llvm_nil\n\n      false\n    end\n\n    def visit(node : UninitializedVar)\n      var = node.var\n\n      case var\n      when Var\n        llvm_var = declare_var var\n        if node.type.nil_type? || !@needs_value\n          @last = llvm_nil\n        else\n          @last = to_lhs(llvm_var.pointer, node.type)\n        end\n      else\n        @last = llvm_nil\n      end\n\n      false\n    end\n\n    def visit(node : Var)\n      # It can happen that a variable ends up with no type, as in:\n      #\n      #     i = 0\n      #     i.is_a?(Int32) ? 1 : i # here\n      #\n      # In that case we treat it as NoReturn.\n      return unreachable unless node.type?\n\n      var = context.vars[node.name]?\n      if var\n        return unreachable if var.type.no_return?\n\n        # Special variables always have an extra pointer\n        already_loaded = (node.special_var? ? false : var.already_loaded)\n        @last = downcast var.pointer, node.type, var.type, already_loaded, extern: false\n      elsif node.name == \"self\"\n        if node.type.metaclass?\n          @last = type_id(node.type)\n        else\n          @last = downcast llvm_self_ptr, node.type, context.type, true\n        end\n      else\n        node.raise \"BUG: missing context var: #{node.name}\"\n      end\n      false\n    end\n\n    def visit(node : Global)\n      node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n    end\n\n    def visit(node : ClassVar)\n      @last = read_class_var(node)\n      false\n    end\n\n    def visit(node : InstanceVar)\n      read_instance_var node.type, context.type, node.name, llvm_self_ptr\n    end\n\n    def end_visit(node : ReadInstanceVar)\n      obj_type = node.obj.type\n      if obj_type.is_a?(UnionType)\n        union_ptr = @last\n        union_type_id = type_id(union_ptr, obj_type)\n\n        Phi.open(self, node, @needs_value) do |phi|\n          obj_type.union_types.each do |union_type|\n            id_matches = match_type_id(node.type, union_type, union_type_id)\n\n            current_match_label, next_match_label = new_blocks \"current_match\", \"next_match\"\n            cond id_matches, current_match_label, next_match_label\n            position_at_end current_match_label\n\n            value_ptr = downcast union_ptr, union_type, obj_type, true\n            ivar_type = union_type.lookup_instance_var(node.name).type\n            read_instance_var ivar_type, union_type, node.name, value_ptr\n\n            phi.add @last, ivar_type\n\n            position_at_end next_match_label\n          end\n          unreachable\n        end\n      else\n        read_instance_var node.type, node.obj.type, node.name, @last\n      end\n    end\n\n    def read_instance_var(node_type, type, name, value)\n      type = type.remove_typedef\n      ivar = type.lookup_instance_var(name)\n      ivar_ptr = instance_var_ptr type, name, value\n      @last = downcast ivar_ptr, node_type, ivar.type, false, extern: type.extern?\n      if type.extern?\n        # When reading the instance variable of a C struct or union\n        # we need to convert C functions to Crystal procs. This\n        # can happen for example in Struct#to_s, where all fields\n        # are inspected.\n        @last = check_c_fun node_type, @last\n      end\n      false\n    end\n\n    def visit(node : Cast)\n      request_value(node.obj)\n\n      last_value = @last\n\n      obj_type = node.obj.type\n      to_type = node.to.type.virtual_type\n\n      if to_type.pointer?\n        if obj_type.nil_type?\n          @last = llvm_type(to_type).null\n        else\n          @last = cast_to last_value, to_type\n        end\n      elsif obj_type.pointer?\n        # Special case: for `ptr.as(Nil)` there's no bitcast involved\n        if to_type.nil_type?\n          @last = llvm_nil\n        else\n          @last = cast_to last_value, to_type\n        end\n      else\n        resulting_type = node.type\n        if node.upcast?\n          @last = upcast last_value, resulting_type, obj_type\n        elsif obj_type != resulting_type\n          type_id = type_id last_value, obj_type\n          cmp = match_type_id obj_type, resulting_type, type_id\n\n          matches_block, doesnt_match_block = new_blocks \"matches\", \"doesnt_match\"\n          cond cmp, matches_block, doesnt_match_block\n\n          position_at_end doesnt_match_block\n          codegen_raise_cast_failed(last_value, type_id, obj_type, to_type, node)\n\n          position_at_end matches_block\n          @last = downcast last_value, resulting_type, obj_type, true\n        end\n      end\n\n      false\n    end\n\n    # fallback for stdlib before 1.16.2 when using a 1.16.2 or later compiler\n    def type_cast_exception_call(from_type, to_type, node, var_name)\n      pieces = [\n        StringLiteral.new(\"Cast from \").at(node),\n        Call.new(Var.new(var_name).at(node), \"class\").at(node),\n        StringLiteral.new(\" to #{to_type} failed\").at(node),\n      ] of ASTNode\n\n      if location = node.location\n        pieces << StringLiteral.new(\", at #{location.expanded_location}:#{location.line_number}\").at(node)\n      end\n\n      ex = Call.new(Path.global(\"TypeCastError\").at(node), \"new\", StringInterpolation.new(pieces).at(node)).at(node)\n      call = Call.global(\"raise\", ex).at(node)\n      call = @program.normalize(call)\n\n      meta_vars = MetaVars.new\n      meta_vars[var_name] = MetaVar.new(var_name, type: from_type)\n      visitor = MainVisitor.new(@program, meta_vars)\n      @program.visit_main call, visitor: visitor\n      call\n    end\n\n    def visit(node : NilableCast)\n      request_value(node.obj)\n\n      last_value = @last\n\n      obj_type = node.obj.type\n      to_type = node.to.type\n\n      resulting_type = node.type\n\n      filtered_type = obj_type.filter_by(to_type)\n\n      unless filtered_type\n        @last = upcast llvm_nil, resulting_type, @program.nil\n        return false\n      end\n\n      non_nilable_type = node.non_nilable_type\n\n      if node.upcast?\n        @last = upcast last_value, non_nilable_type, obj_type\n        @last = upcast @last, resulting_type, non_nilable_type\n      elsif obj_type != non_nilable_type\n        type_id = type_id last_value, obj_type\n        cmp = match_type_id obj_type, non_nilable_type, type_id\n\n        Phi.open(self, node, @needs_value) do |phi|\n          matches_block, doesnt_match_block = new_blocks \"matches\", \"doesnt_match\"\n          cond cmp, matches_block, doesnt_match_block\n\n          position_at_end doesnt_match_block\n          @last = upcast llvm_nil, resulting_type, @program.nil\n          phi.add @last, resulting_type\n\n          position_at_end matches_block\n          @last = downcast last_value, non_nilable_type, obj_type, true\n          @last = upcast @last, resulting_type, non_nilable_type\n          phi.add @last, resulting_type, last: true\n        end\n      else\n        @last = upcast last_value, resulting_type, obj_type\n      end\n\n      false\n    end\n\n    def cant_pass_closure_to_c_exception_call\n      @cant_pass_closure_to_c_exception_call ||= begin\n        call = Call.global(\"raise\", StringLiteral.new(\"passing a closure to C is not allowed\")).at(UNKNOWN_LOCATION)\n        @program.visit_main call\n        call.raise \"::raise must be of NoReturn return type!\" unless call.type.is_a?(NoReturnType)\n        call\n      end\n    end\n\n    def codegen_raise_cast_failed(value, type_id, obj_type, to_type, node)\n      location = node.location\n      set_current_debug_location(location) if location && @debug.line_numbers?\n\n      if func = crystal_raise_cast_failed_fun\n        call_args = [\n          cast_to_void_pointer(type_id_to_class_name(type_id)),\n          cast_to_void_pointer(build_string_constant(to_type.to_s)),\n          location ? cast_to_void_pointer(build_string_constant(location.expanded_location.to_s)) : llvm_context.void_pointer.null,\n        ] of LLVM::Value\n\n        if (rescue_block = @rescue_block)\n          invoke_out_block = new_block \"invoke_out\"\n          invoke func, call_args, invoke_out_block, rescue_block\n          position_at_end invoke_out_block\n        else\n          call func, call_args\n        end\n\n        unreachable\n      else\n        # fallback for stdlib before 1.16.2 when using a 1.16.2 or later compiler\n        temp_var_name = @program.new_temp_var_name\n        context.vars[temp_var_name] = LLVMVar.new(value, obj_type, already_loaded: true)\n        accept type_cast_exception_call(obj_type, to_type, node, temp_var_name)\n        context.vars.delete temp_var_name\n      end\n    end\n\n    def type_id_to_class_name(type_id)\n      map_name = \"__crystal_type_id_to_class_name_map\"\n\n      global = @main_mod.globals[map_name]?\n      unless global\n        global = @main_mod.globals.add(@main_llvm_typer.llvm_type(@program.string).array(@program.llvm_id.@ids.size), map_name)\n        global.linkage = LLVM::Linkage::Internal if @single_module\n        global.initializer = create_type_id_to_class_name_map\n        global.global_constant = true\n      end\n\n      if @llvm_mod != @main_mod\n        global = @llvm_mod.globals[map_name]?\n        unless global\n          global = @llvm_mod.globals.add(@llvm_typer.llvm_type(@program.string).array(@program.llvm_id.@ids.size), map_name)\n          global.linkage = LLVM::Linkage::External\n          global.global_constant = true\n        end\n      end\n\n      str_ptr = gep llvm_type(@program.string).array(@program.llvm_id.@ids.size), global, 0, type_id\n      load llvm_type(@program.string), str_ptr\n    end\n\n    def create_type_id_to_class_name_map\n      ids = @program.llvm_id.@ids\n      id_map = Array(LLVM::Value).new(size: ids.size, value: LLVM::Value.null)\n      ids.each do |type, (_, type_id)|\n        id_map[type_id] = build_string_constant(type.to_s, llvm_mod: @main_mod, llvm_typer: @main_llvm_typer)\n      end\n\n      @main_llvm_typer.llvm_type(@program.string).const_array(id_map)\n    end\n\n    def visit(node : IsA)\n      codegen_type_filter node, &.filter_by(node.const.type)\n    end\n\n    def visit(node : RespondsTo)\n      codegen_type_filter node, &.filter_by_responds_to(node.name)\n    end\n\n    def codegen_type_filter(node, &)\n      accept node.obj\n\n      if @needs_value\n        obj_type = node.obj.type\n\n        type_id = type_id @last, obj_type\n        filtered_type = yield(obj_type).not_nil!\n\n        @last = match_type_id obj_type, filtered_type, type_id\n      end\n\n      false\n    end\n\n    def declare_var(var)\n      context.vars[var.name] ||= begin\n        pointer = var.no_returns? ? llvm_nil : alloca(llvm_type(var.type), var.name)\n        debug_variable_created =\n          if context.fun.naked?\n            # Naked functions must not have debug info associated with them\n            false\n          else\n            declare_variable(var.name, var.type, pointer, var.location)\n          end\n        LLVMVar.new(pointer, var.type, debug_variable_created: debug_variable_created)\n      end\n    end\n\n    def declare_lib_var(name, type, thread_local)\n      var = @llvm_mod.globals[name]?\n      unless var\n        var = llvm_mod.globals.add(llvm_c_return_type(type), name)\n        var.linkage = LLVM::Linkage::External\n        if @program.has_flag?(\"win32\") && !@program.has_flag?(\"static\")\n          var.dll_storage_class = LLVM::DLLStorageClass::DLLImport\n        end\n        var.thread_local = thread_local\n      end\n      var\n    end\n\n    def visit(node : Def)\n      node.hook_expansions.try &.each do |hook|\n        accept hook\n      end\n\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : Macro)\n      @last = llvm_nil\n      false\n    end\n\n    def visit(node : Path)\n      if const = node.target_const\n        read_const(const, node)\n      elsif replacement = node.syntax_replacement\n        accept replacement\n      else\n        node_type = node.type\n        set_current_debug_location(node) if @debug.line_numbers?\n        # Special case: if the type is a type tuple we need to create a tuple for it\n        if node_type.is_a?(TupleInstanceType)\n          @last = allocate_tuple(node_type) do |tuple_type, i|\n            {tuple_type, type_id(tuple_type)}\n          end\n        else\n          @last = type_id(node.type)\n        end\n      end\n      false\n    end\n\n    def visit(node : Generic)\n      set_current_debug_location(node) if @debug.line_numbers?\n      @last = type_id(node.type)\n      false\n    end\n\n    def visit(node : Yield)\n      if node.expanded\n        raise \"BUG: #{node} at #{node.location} should have been expanded\"\n      end\n\n      block_context = context.block_context.not_nil!\n      block = context.block\n      splat_index = block.splat_index\n\n      closured_vars = closured_vars(block.vars, block)\n\n      malloc_closure closured_vars, block_context, block_context.closure_parent_context\n\n      old_scope = block_context.vars[\"%scope\"]?\n\n      if node_scope = node.scope\n        request_value(node_scope)\n        block_context.vars[\"%scope\"] = LLVMVar.new(@last, node_scope.type)\n      end\n\n      # First accept all yield expressions and assign them to block vars\n      unless node.exps.empty?\n        exp_values = Array({LLVM::Value, Type}).new(node.exps.size)\n\n        # We first accept the expressions and store the values, without\n        # assigning them to the block vars yet because we might have\n        # a nested yield that would override a block argument's value\n        node.exps.each_with_index do |exp, i|\n          request_value(exp)\n\n          if exp.is_a?(Splat)\n            tuple_type = exp.type.as(TupleInstanceType)\n            tuple_type.tuple_types.each_with_index do |subtype, j|\n              exp_values << {codegen_tuple_indexer(tuple_type, @last, j), subtype}\n            end\n          else\n            exp_values << {@last, exp.type}\n          end\n        end\n\n        # Now assign exp values to block arguments\n        if splat_index\n          exp_index = 0\n          block.args.each_with_index do |arg, i|\n            if arg.name != \"_\"\n              block_var = block_context.vars[arg.name]\n              if i == splat_index\n                exp_value = allocate_tuple(arg.type.as(TupleInstanceType)) do\n                  exp_value2, exp_type = exp_values[exp_index]\n                  exp_index += 1\n                  {exp_type, exp_value2}\n                end\n                exp_type = arg.type\n              else\n                exp_value, exp_type = exp_values[exp_index]\n                exp_index += 1\n              end\n              assign block_var.pointer, block_var.type, exp_type, exp_value\n            else\n              exp_index += (i == splat_index ? arg.type.as(TupleInstanceType).size : 1)\n            end\n          end\n        else\n          # Check if tuple unpacking is needed\n          if exp_values.size == 1 &&\n             (exp_type = exp_values.first[1]).is_a?(TupleInstanceType) &&\n             block.args.size > 1\n            exp_value = exp_values.first[0]\n            exp_type.tuple_types.each_with_index do |tuple_type, i|\n              arg = block.args[i]?\n              if arg && arg.name != \"_\"\n                t_type = tuple_type\n                t_value = codegen_tuple_indexer(exp_type, exp_value, i)\n                block_var = block_context.vars[arg.name]\n                assign block_var.pointer, block_var.type, t_type, t_value\n              end\n            end\n          else\n            exp_values.each_with_index do |(exp_value, exp_type), i|\n              if (arg = block.args[i]?) && arg.name != \"_\"\n                block_var = block_context.vars[arg.name]\n                assign block_var.pointer, block_var.type, exp_type, exp_value\n              end\n            end\n          end\n        end\n      end\n\n      Phi.open(self, block, @needs_value) do |phi|\n        with_cloned_context(block_context) do |old|\n          # Reset vars that are declared inside the block and are nilable\n          reset_nilable_vars block\n\n          context.break_phi = old.return_phi\n          context.next_phi = phi\n          context.closure_parent_context = block_context.closure_parent_context\n\n          set_ensure_exception_handler(block)\n\n          request_value(block.body)\n        end\n\n        phi.add @last, block.body.type?, last: true\n      end\n\n      if old_scope\n        block_context.vars[\"%scope\"] = old_scope\n      end\n\n      false\n    end\n\n    def visit(node : Unreachable)\n      builder.unreachable\n      false\n    end\n\n    def check_proc_is_not_closure(value, type)\n      check_fun_name = \"~check_proc_is_not_closure\"\n      func = typed_fun?(@main_mod, check_fun_name) || create_check_proc_is_not_closure_fun(check_fun_name)\n      func = check_main_fun check_fun_name, func\n      value = call func, [value] of LLVM::Value\n      pointer_cast value, llvm_proc_type(type).pointer\n    end\n\n    def create_check_proc_is_not_closure_fun(fun_name)\n      in_main do\n        define_main_function(fun_name, [llvm_typer.proc_type], llvm_context.void_pointer) do |func|\n          set_internal_fun_debug_location(func, fun_name)\n\n          param = func.params.first\n\n          fun_ptr = extract_value param, 0\n          ctx_ptr = extract_value param, 1\n\n          ctx_is_null_block = new_block \"ctx_is_null\"\n          ctx_is_not_null_block = new_block \"ctx_is_not_null\"\n\n          ctx_is_null = equal? ctx_ptr, llvm_context.void_pointer.null\n          cond ctx_is_null, ctx_is_null_block, ctx_is_not_null_block\n\n          position_at_end ctx_is_null_block\n          ret fun_ptr\n\n          position_at_end ctx_is_not_null_block\n          accept cant_pass_closure_to_c_exception_call\n        end\n      end\n    end\n\n    def make_fun(type, fun_ptr, ctx_ptr)\n      struct_type = llvm_type(type)\n      closure_ptr = alloca struct_type\n      store fun_ptr, aggregate_index(struct_type, closure_ptr, 0)\n      store ctx_ptr, aggregate_index(struct_type, closure_ptr, 1)\n      load(struct_type, closure_ptr)\n    end\n\n    def make_nilable_fun(type)\n      null = llvm_context.void_pointer.null\n      make_fun type, null, null\n    end\n\n    def in_main(&)\n      old_builder = self.builder\n      old_position = old_builder.insert_block\n      old_llvm_mod = @llvm_mod\n      old_llvm_context = @llvm_context\n      old_llvm_typer = @llvm_typer\n      old_fun = context.fun\n      old_fun_type = context.fun_type\n      old_ensure_exception_handlers = @ensure_exception_handlers\n      old_rescue_block = @rescue_block\n      old_catch_pad = @catch_pad\n      old_entry_block = @entry_block\n      old_alloca_block = @alloca_block\n      old_needs_value = @needs_value\n      old_debug_location = @current_debug_location\n\n      @llvm_mod = @main_mod\n      @llvm_context = @main_llvm_context\n      @llvm_typer = @main_llvm_typer\n      @builder = @main_builder\n\n      @ensure_exception_handlers = nil\n      @rescue_block = nil\n      @catch_pad = nil\n\n      clear_current_debug_location if @debug.line_numbers?\n\n      block_value = yield\n\n      @builder = old_builder\n      position_at_end old_position\n\n      @llvm_mod = old_llvm_mod\n      @llvm_context = old_llvm_context\n      @llvm_typer = old_llvm_typer\n      @ensure_exception_handlers = old_ensure_exception_handlers\n      @rescue_block = old_rescue_block\n      @catch_pad = old_catch_pad\n      @entry_block = old_entry_block\n      @alloca_block = old_alloca_block\n      @needs_value = old_needs_value\n      context.fun = old_fun\n      context.fun_type = old_fun_type\n      set_current_debug_location old_debug_location if @debug.line_numbers?\n\n      block_value\n    end\n\n    def define_main_function(name, arg_types : Array(LLVM::Type), return_type : LLVM::Type, needs_alloca : Bool = false, &)\n      define_main_function(name, LLVM::Type.function(arg_types, return_type), needs_alloca) { |func| yield func }\n    end\n\n    def define_main_function(name, type : LLVM::Type, needs_alloca : Bool = false, &)\n      if @llvm_mod != @main_mod\n        raise \"wrong usage of define_main_function: you must put it inside an `in_main` block\"\n      end\n\n      func = add_typed_fun(@main_mod, name, type)\n      context.fun = func.func\n      context.fun_type = type\n      context.fun.linkage = LLVM::Linkage::Internal if @single_module\n      if needs_alloca\n        new_entry_block\n        yield func.func\n        br_from_alloca_to_entry\n      else\n        block = func.func.basic_blocks.append \"entry\"\n        position_at_end block\n        yield func.func\n      end\n      func\n    end\n\n    # used for generated internal functions like `~metaclass` and `~match`\n    def set_internal_fun_debug_location(func, name, location = nil)\n      return if @debug.none?\n      location ||= UNKNOWN_LOCATION\n      emit_fun_debug_metadata(func, name, location)\n      set_current_debug_location(location) if @debug.line_numbers?\n    end\n\n    private UNKNOWN_LOCATION = Location.new(\"??\", 0, 0)\n\n    def llvm_self(type = context.type)\n      self_var = context.vars[\"self\"]?\n      if self_var\n        downcast self_var.pointer, type, self_var.type, true\n      else\n        type_id(type.not_nil!)\n      end\n    end\n\n    def llvm_self_ptr\n      type = context.type\n      if type.is_a?(VirtualType)\n        if type.struct?\n          # A virtual struct doesn't need a cast to a more generic pointer\n          # (it's the union already)\n          llvm_self\n        else\n          cast_to llvm_self, type.base_type\n        end\n      else\n        llvm_self\n      end\n    end\n\n    def new_entry_block\n      @alloca_block, @entry_block = new_entry_block_chain \"alloca\", \"entry\"\n    end\n\n    def new_entry_block_chain(*names)\n      blocks = new_blocks *names\n      position_at_end blocks.last\n      blocks\n    end\n\n    def br_from_alloca_to_entry\n      # If there are no instructions in the alloca we can delete\n      # it and just keep the entry block (less noise).\n      if alloca_block.instructions.empty?\n        alloca_block.delete\n      else\n        br_block_chain alloca_block, entry_block\n      end\n    end\n\n    def br_block_chain(*blocks)\n      old_block = insert_block\n\n      0.upto(blocks.size - 2) do |i|\n        position_at_end blocks[i]\n        clear_current_debug_location if @debug.line_numbers?\n        br blocks[i + 1]\n      end\n\n      position_at_end old_block\n    end\n\n    def new_block(name = \"\")\n      context.fun.basic_blocks.append name\n    end\n\n    def new_blocks(*names)\n      names.map { |name| new_block name }\n    end\n\n    def alloca_vars(vars, obj = nil, args = nil, parent_context = nil, reset_nilable_vars = true)\n      self_closured = obj.is_a?(Def) && obj.self_closured?\n      closured_vars = closured_vars(vars, obj)\n      alloca_non_closured_vars(vars, obj, args, reset_nilable_vars)\n      malloc_closure closured_vars, context, parent_context, self_closured\n    end\n\n    def alloca_non_closured_vars(vars, obj = nil, args = nil, reset_nilable_vars = true)\n      return unless vars\n\n      in_alloca_block do\n        # Allocate all variables which are not closured and don't belong to an outer closure\n        vars.each do |name, var|\n          next if name == \"self\" || context.vars[name]?\n\n          var_type = var.type? || @program.nil\n\n          if var_type.void?\n            context.vars[name] = LLVMVar.new(llvm_nil, @program.void)\n          elsif var_type.no_return?\n            # No alloca for NoReturn\n          elsif var.closure_in?(obj)\n            # We deal with closured vars later\n          elsif !obj || var.belongs_to?(obj)\n            # We deal with arguments later\n            is_arg = args.try &.any? { |arg| arg.name == var.name }\n            next if is_arg\n\n            ptr = alloca llvm_type(var_type), name\n\n            location = var.location\n            if location.nil? && obj.is_a?(ASTNode)\n              location = obj.location\n            end\n\n            debug_variable_created =\n              if location && !context.fun.naked?\n                declare_variable name, var_type, ptr, location, alloca_block\n              else\n                false\n              end\n            context.vars[name] = LLVMVar.new(ptr, var_type, debug_variable_created: debug_variable_created)\n\n            # Assign default nil for variables that are bound to the nil variable\n            if reset_nilable_vars && bound_to_mod_nil?(var)\n              assign ptr, var_type, @program.nil, llvm_nil\n            end\n          else\n            # The variable belong to an outer closure\n          end\n        end\n      end\n    end\n\n    def closured_vars(vars, obj = nil)\n      return unless vars\n\n      closure_vars = nil\n\n      vars.each_value do |var|\n        # It might be the case that a closured variable ends up without\n        # a type, as in #2196, because a branch can't be typed and is\n        # finally removed before codegen. In that case we just assume\n        # Nil as a type.\n        if var.closure_in?(obj) && var.type?\n          closure_vars ||= [] of MetaVar\n          closure_vars << var\n        end\n      end\n\n      closure_vars\n    end\n\n    def malloc_closure(closure_vars, current_context, parent_context = nil, self_closured = false)\n      parent_closure_type = parent_context.try &.closure_type\n\n      if closure_vars || self_closured\n        closure_vars ||= [] of MetaVar\n\n        closure_type, closure_has_inner_pointers =\n          @llvm_typer.closure_context_type(closure_vars, parent_closure_type, (self_closured ? current_context.type : nil))\n        closure_ptr = closure_has_inner_pointers ? malloc(closure_type) : malloc_atomic(closure_type)\n\n        closure_vars.each_with_index do |var, i|\n          current_context.vars[var.name] = LLVMVar.new(gep(closure_type, closure_ptr, 0, i, var.name), var.type)\n        end\n        closure_skip_parent = false\n\n        if parent_closure_type\n          store parent_context.not_nil!.closure_ptr.not_nil!, gep(closure_type, closure_ptr, 0, closure_vars.size, \"parent\")\n        end\n\n        if self_closured\n          offset = parent_closure_type ? 1 : 0\n          self_value = to_rhs(llvm_self, current_context.type)\n\n          store self_value, gep(closure_type, closure_ptr, 0, closure_vars.size + offset, \"self\")\n\n          current_context.closure_self = current_context.type\n        end\n      elsif parent_context && parent_context.closure_type\n        closure_vars = parent_context.closure_vars\n        closure_type = parent_context.closure_type\n        closure_ptr = parent_context.closure_ptr\n        closure_skip_parent = true\n      else\n        closure_skip_parent = false\n      end\n\n      current_context.closure_vars = closure_vars\n      current_context.closure_type = closure_type\n      current_context.closure_ptr = closure_ptr\n      current_context.closure_skip_parent = closure_skip_parent\n    end\n\n    def undef_vars(vars, obj)\n      return unless vars\n\n      vars.each do |name, var|\n        # Don't remove special vars because they are local for the entire method\n        if var.belongs_to?(obj) && !var.special_var?\n          context.vars.delete(name)\n        end\n      end\n    end\n\n    # Sets to nil any variable in node that is nilable.\n    def reset_nilable_vars(node)\n      vars = node.vars\n      return unless vars\n\n      vars.each do |name, var|\n        if var.context == node && bound_to_mod_nil?(var)\n          context_var = context.vars[name]\n          assign context_var.pointer, context_var.type, @program.nil, llvm_nil\n        end\n      end\n    end\n\n    def bound_to_mod_nil?(var)\n      var.dependencies.any? &.same?(@program.nil_var)\n    end\n\n    def alloca(type, name = \"\")\n      in_alloca_block { builder.alloca type, name }\n    end\n\n    def in_alloca_block(&)\n      old_block = insert_block\n      position_at_end alloca_block\n      value = yield\n      position_at_end old_block\n      value\n    end\n\n    def printf(format, args = [] of LLVM::Value)\n      call c_printf_fun, [builder.global_string_pointer(format)] + args\n    end\n\n    # Emits a debug message that shows the current llvm basic block name,\n    # the location within the codegen that was used to emit this log.\n    #\n    # The message is only generated if `CRYSTAL_DEBUG_CODEGEN` is set\n    #\n    # The block given to this method should yield `printf` arguments to show\n    # additional information. The following forms are all valid and helps to\n    # allocate the arguments only if the message is to be generated.\n    #\n    # ```\n    # debug_codegen_log\n    # debug_codegen_log { }\n    # debug_codegen_log { \"Lorem\" }\n    # debug_codegen_log { {\"Lorem\"} }\n    # debug_codegen_log { {\"Lorem %d\", [an_int_llvm_value] of LLVM::Value} }\n    # ```\n    #\n    def debug_codegen_log(file = __FILE__, line = __LINE__, &)\n      return unless ENV[\"CRYSTAL_DEBUG_CODEGEN\"]?\n      printf_args = yield || \"\"\n      printf_args = {printf_args, [] of LLVM::Value} if printf_args.is_a?(String)\n      printf_args = {printf_args[0], [] of LLVM::Value} if printf_args.is_a?({String})\n      msg, args = printf_args\n      printf(\"<function=#{insert_block.parent.try(&.name) || \"???\"} block=#{insert_block.name || \"???\"} source=#{Crystal.relative_filename(file)}:#{line}> #{msg}\\n\", args)\n    end\n\n    # :ditto:\n    def debug_codegen_log(file = __FILE__, line = __LINE__)\n      debug_codegen_log(file, line) { }\n    end\n\n    def unreachable(file = __FILE__, line = __LINE__)\n      debug_codegen_log(file, line) { \"Reached the unreachable!\" }\n      builder.unreachable\n      false\n    end\n\n    def allocate_aggregate(type)\n      struct_type = llvm_struct_type(type)\n      if type.passed_by_value?\n        type_ptr = alloca struct_type\n      else\n        if type.is_a?(InstanceVarContainer) && !type.struct? &&\n           type.all_instance_vars.each_value.any? &.type.has_inner_pointers?\n          type_ptr = malloc struct_type\n        else\n          type_ptr = malloc_atomic struct_type\n        end\n      end\n\n      pre_initialize_aggregate(type, struct_type, type_ptr)\n    end\n\n    def pre_initialize_aggregate(type, struct_type, ptr)\n      memset ptr, int8(0), size_t(struct_type.size)\n      run_instance_vars_initializers(type, type, ptr)\n\n      unless type.struct?\n        type_id_ptr = aggregate_index(struct_type, ptr, 0)\n        store type_id(type), type_id_ptr\n      end\n\n      @last = ptr\n    end\n\n    def allocate_tuple(type, &)\n      struct_type = llvm_type(type)\n      tuple = alloca struct_type\n      type.tuple_types.each_with_index do |tuple_type, i|\n        exp_type, value = yield tuple_type, i\n        assign aggregate_index(struct_type, tuple, i), tuple_type, exp_type, value\n      end\n      tuple\n    end\n\n    def run_instance_vars_initializers(real_type, type : ClassType | GenericClassInstanceType, type_ptr)\n      if superclass = type.superclass\n        run_instance_vars_initializers(real_type, superclass, type_ptr)\n      end\n\n      run_instance_vars_initializers_non_recursive real_type, type, type_ptr\n    end\n\n    def run_instance_vars_initializers(real_type, type : Type, type_ptr)\n      # Nothing to do\n    end\n\n    def run_instance_vars_initializers_non_recursive(real_type, type, type_ptr)\n      initializers = type.instance_vars_initializers\n      return unless initializers\n\n      initializers.each do |init|\n        ivar = real_type.lookup_instance_var(init.name)\n\n        with_cloned_context do\n          # Instance var initializers must run with \"self\"\n          # properly set up to the type being allocated\n          context.type = real_type.metaclass\n          context.vars = LLVMVars.new\n          alloca_vars init.meta_vars\n\n          request_value(init.value)\n\n          ivar_ptr = instance_var_ptr real_type, init.name, type_ptr\n          assign ivar_ptr, ivar.type, init.value.type, @last\n        end\n      end\n    end\n\n    def malloc(type)\n      generic_malloc(type) { crystal_malloc_fun }\n    end\n\n    def malloc_atomic(type)\n      generic_malloc(type) { crystal_malloc_atomic_fun }\n    end\n\n    def generic_malloc(type, &)\n      size = type.size\n\n      if malloc_fun = yield\n        pointer = call malloc_fun, size\n      else\n        pointer = call c_malloc_fun, size_t(size)\n      end\n\n      pointer_cast pointer, type.pointer\n    end\n\n    def array_malloc(type, count)\n      generic_array_malloc(type, count) { crystal_malloc_fun }\n    end\n\n    def array_malloc_atomic(type, count)\n      generic_array_malloc(type, count) { crystal_malloc_atomic_fun }\n    end\n\n    def generic_array_malloc(type, count, &)\n      size = builder.mul type.size, count\n\n      if malloc_fun = yield\n        pointer = call malloc_fun, size\n      else\n        pointer = call c_malloc_fun, size_t(size)\n      end\n\n      memset pointer, int8(0), size_t(size)\n      pointer_cast pointer, type.pointer\n    end\n\n    def crystal_malloc_fun\n      @malloc_fun ||= typed_fun?(@main_mod, MALLOC_NAME)\n      if malloc_fun = @malloc_fun\n        check_main_fun MALLOC_NAME, malloc_fun\n      else\n        nil\n      end\n    end\n\n    def crystal_malloc_atomic_fun\n      @malloc_atomic_fun ||= typed_fun?(@main_mod, MALLOC_ATOMIC_NAME)\n      if malloc_fun = @malloc_atomic_fun\n        check_main_fun MALLOC_ATOMIC_NAME, malloc_fun\n      else\n        nil\n      end\n    end\n\n    def crystal_realloc_fun\n      @realloc_fun ||= typed_fun?(@main_mod, REALLOC_NAME)\n      if realloc_fun = @realloc_fun\n        check_main_fun REALLOC_NAME, realloc_fun\n      else\n        nil\n      end\n    end\n\n    def crystal_raise_overflow_fun\n      @raise_overflow_fun ||= typed_fun?(@main_mod, RAISE_OVERFLOW_NAME)\n      if raise_overflow_fun = @raise_overflow_fun\n        check_main_fun RAISE_OVERFLOW_NAME, raise_overflow_fun\n      else\n        raise Error.new(\"Missing __crystal_raise_overflow function, either use std-lib's prelude or define it\")\n      end\n    end\n\n    def crystal_raise_cast_failed_fun\n      @raise_cast_failed_fun ||= typed_fun?(@main_mod, RAISE_CAST_FAILED_NAME)\n      if raise_cast_failed_fun = @raise_cast_failed_fun\n        check_main_fun RAISE_CAST_FAILED_NAME, raise_cast_failed_fun\n      else\n        nil\n      end\n    end\n\n    # Fallbacks to libc malloc and realloc when the expected __crystal_*\n    # functions aren't defined (e.g. empty prelude). We only use them in tests\n    # that don't require the prelude, so they don't require the GC.\n    #\n    # Outside tests the __crystal_* functions should have been defined and will\n    # be invoked instead.\n\n    def c_malloc_fun\n      malloc_fun = @c_malloc_fun = fetch_typed_fun(@main_mod, \"malloc\") do\n        LLVM::Type.function([main_llvm_context_size_t], @main_llvm_context.void_pointer)\n      end\n\n      check_main_fun \"malloc\", malloc_fun\n    end\n\n    def c_realloc_fun\n      realloc_fun = @c_realloc_fun = fetch_typed_fun(@main_mod, \"realloc\") do\n        LLVM::Type.function([@main_llvm_context.void_pointer, main_llvm_context_size_t], @main_llvm_context.void_pointer)\n      end\n\n      check_main_fun \"realloc\", realloc_fun\n    end\n\n    # can't use `#size_t` because it targets @llvm_context instead of\n    # @main_llvm_context which confuses LLVM that considers them as distinct\n    # types despite the dumped LLVM IR looking identical\n    private def main_llvm_context_size_t\n      @main_llvm_context.int(@program.size_bit_width)\n    end\n\n    def memset(pointer, value, size)\n      pointer = cast_to_void_pointer pointer\n      res = call c_memset_fun, [pointer, value, size, int1(0)]\n      LibLLVM.set_instr_param_alignment(res, 1, 4)\n\n      res\n    end\n\n    def memcpy(dest, src, len, align, volatile, *, src_align = align)\n      res = call c_memcpy_fun, [dest, src, len, volatile]\n\n      LibLLVM.set_instr_param_alignment(res, 1, align)\n      LibLLVM.set_instr_param_alignment(res, 2, src_align)\n\n      res\n    end\n\n    def realloc(buffer, size)\n      if realloc_fun = crystal_realloc_fun\n        call realloc_fun, [buffer, size]\n      else\n        call c_realloc_fun, [buffer, size_t(size)]\n      end\n    end\n\n    private def c_printf_fun\n      fetch_typed_fun(@llvm_mod, \"printf\") do\n        LLVM::Type.function([@llvm_context.void_pointer], @llvm_context.int32, true)\n      end\n    end\n\n    private def c_memset_fun\n      name = {% if LibLLVM::IS_LT_150 %}\n               \"llvm.memset.p0i8.i#{@program.size_bit_width}\"\n             {% else %}\n               \"llvm.memset.p0.i#{@program.size_bit_width}\"\n             {% end %}\n\n      fetch_typed_fun(@llvm_mod, name) do\n        arg_types = [@llvm_context.void_pointer, @llvm_context.int8, size_t, @llvm_context.int1]\n        LLVM::Type.function(arg_types, @llvm_context.void)\n      end\n    end\n\n    private def c_memcpy_fun\n      name = {% if LibLLVM::IS_LT_150 %}\n               \"llvm.memcpy.p0i8.p0i8.i#{@program.size_bit_width}\"\n             {% else %}\n               \"llvm.memcpy.p0.p0.i#{@program.size_bit_width}\"\n             {% end %}\n\n      fetch_typed_fun(@llvm_mod, name) do\n        arg_types = [@llvm_context.void_pointer, @llvm_context.void_pointer, size_t, @llvm_context.int1]\n        LLVM::Type.function(arg_types, @llvm_context.void)\n      end\n    end\n\n    def to_lhs(value, type)\n      # `llvm_embedded_type` needed for void-like types\n      type.passed_by_value? ? value : load(llvm_embedded_type(type), value)\n    end\n\n    def to_rhs(value, type)\n      type.passed_by_value? ? load(llvm_embedded_type(type), value) : value\n    end\n\n    def extern_to_lhs(value, type)\n      type.passed_by_value? ? value : load(llvm_embedded_c_type(type), value)\n    end\n\n    def extern_to_rhs(value, type)\n      type.passed_by_value? ? load(llvm_embedded_c_type(type), value) : value\n    end\n\n    # *type* is the pointee type of *ptr* (not the type of the returned\n    # element)\n    def aggregate_index(type : LLVM::Type, ptr : LLVM::Value, index : Int32)\n      gep type, ptr, 0, index\n    end\n\n    def instance_var_ptr(type, name, pointer)\n      if type.extern_union?\n        return union_field_ptr(type, type.instance_vars[name].type, pointer)\n      end\n\n      index = type.index_of_instance_var(name).not_nil!\n\n      unless type.struct?\n        index += 1\n      end\n\n      target_type = type\n      if type.is_a?(VirtualType)\n        if type.struct?\n          if (actual_type = type.remove_indirection).is_a?(UnionType)\n            # For a struct we need to cast the second part of the union to the base type\n            value_ptr = union_value(llvm_type(actual_type), pointer)\n            target_type = type.base_type\n            pointer = cast_to_pointer value_ptr, target_type\n          else\n            # Nothing, there's only one subclass so it's the struct already\n          end\n        else\n          target_type = type.base_type\n          pointer = cast_to pointer, target_type\n        end\n      end\n\n      aggregate_index llvm_struct_type(target_type), pointer, index\n    end\n\n    def process_finished_hooks\n      last = @last\n      @program.process_finished_hooks(self)\n      @last = last\n    end\n\n    def build_string_constant(str, name = \"str\", *, llvm_mod = @llvm_mod, llvm_typer = @llvm_typer)\n      name = \"#{name[0..18]}...\" if name.bytesize > 18\n      name = name.gsub '@', '.'\n      name = \"'#{name}'\"\n      key = StringKey.new(llvm_mod, str)\n      @strings.put_if_absent(key) do\n        llvm_context = llvm_mod.context\n        global = llvm_mod.globals.add(llvm_typer.llvm_string_type(str.bytesize), name.gsub('\\\\', \"\\\\\\\\\"))\n        global.linkage = LLVM::Linkage::Private\n        global.global_constant = true\n        global.initializer = llvm_context.const_struct [\n          llvm_context.int32.const_int(@program.llvm_id.type_id(@program.string)), # in practice, should always be 1\n          llvm_context.int32.const_int(str.bytesize),\n          llvm_context.int32.const_int(str.size),\n          llvm_context.const_string(str),\n        ]\n        pointer_cast global, llvm_typer.llvm_type(@program.string)\n      end\n    end\n\n    def request_value(request : Bool = true, &)\n      old_needs_value = @needs_value\n      @needs_value = request\n      begin\n        yield\n      ensure\n        @needs_value = old_needs_value\n      end\n    end\n\n    def request_value(node : ASTNode)\n      request_value do\n        accept node\n      end\n    end\n\n    def discard_value(node : ASTNode)\n      request_value(false) do\n        accept node\n      end\n    end\n\n    def accept(node)\n      node.accept self\n    end\n\n    def visit(node : ExpandableNode)\n      raise \"BUG: #{node} (#{node.class}) at #{node.location} should have been expanded\"\n    end\n\n    def visit(node : ASTNode)\n      true\n    end\n  end\n\n  def self.safe_mangling(program, name)\n    if program.has_flag?(\"msvc\")\n      String.build do |str|\n        name.each_char do |char|\n          if char.ascii_alphanumeric? || char == '_'\n            str << char\n          else\n            str << '.'\n            char.ord.to_s(str, 16, upcase: true)\n            str << '.'\n          end\n        end\n      end\n    else\n      name\n    end\n  end\nend\n\nrequire \"./*\"\n"
  },
  {
    "path": "src/compiler/crystal/codegen/cond.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  def codegen_cond(type)\n    codegen_cond_impl(type.remove_indirection)\n  end\n\n  private def codegen_cond_impl(type : NilType)\n    llvm_false\n  end\n\n  private def codegen_cond_impl(type : BoolType)\n    @last\n  end\n\n  private def codegen_cond_impl(type : TypeDefType)\n    codegen_cond type.typedef\n  end\n\n  private def codegen_cond_impl(type : NilableType | NilableReferenceUnionType | PointerInstanceType)\n    not_null_pointer? @last\n  end\n\n  private def codegen_cond_impl(type : NilableProcType)\n    fun_ptr = extract_value @last, 0\n    not_null_pointer? fun_ptr\n  end\n\n  private def codegen_cond_impl(type : MixedUnionType)\n    union_types = type.expand_union_types\n\n    has_nil = union_types.any? &.nil_type?\n    has_bool = union_types.any? &.bool_type?\n    has_pointer = union_types.any?(PointerInstanceType)\n\n    cond = llvm_true\n\n    if has_nil || has_bool || has_pointer\n      type_id, value_ptr = union_type_and_value_pointer(@last, type)\n\n      if has_nil\n        is_nil = equal? type_id, type_id(@program.nil)\n        cond = and cond, not(is_nil)\n      end\n\n      if has_bool\n        value = load(llvm_context.int1, pointer_cast value_ptr, llvm_context.int1.pointer)\n        is_bool = equal? type_id, type_id(@program.bool)\n        cond = and cond, not(and(is_bool, not(value)))\n      end\n\n      if has_pointer\n        union_types.each do |union_type|\n          next unless union_type.is_a?(PointerInstanceType)\n\n          is_pointer = equal? type_id, type_id(union_type)\n          pointer_value = load(llvm_type(union_type), cast_to_pointer(value_ptr, union_type))\n          pointer_null = null_pointer?(pointer_value)\n          cond = and cond, not(and(is_pointer, pointer_null))\n        end\n      end\n    end\n\n    cond\n  end\n\n  private def codegen_cond_impl(type : Type)\n    llvm_true\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/const.cr",
    "content": "require \"./codegen\"\n\n# Constants are represented with two LLVM global variables: one has the constant's\n# value and the other has a flag that indicates whether the constant was already\n# initialized.\n#\n# When the visitor goes through a `CONST = exp` node (assignment to constant),\n# it first checks if the flag is set. If it's not set, it initializes it.\n# Otherwise, it does nothing. This is needed because a constant can be read\n# before it is declared (hoisting), which is valid because an early pass declares\n# constant and the main pass only types them when they are used.\n#\n# This initialization also detects whether the constant is actually a real constant\n# (a number, a string) and then stores that information in the Const type so that\n# later reading it is a simple load (doesn't go through the function described below).\n#\n# When a visitor goes through a `CONST` node (read constant), it first check\n# if the flag is set. If it's not set, it initializes it. Then it returns a pointer\n# to the global variable. To avoid all this code being generated each time, a function\n# is created for each constant and that function is invoked whenever a constant\n# need to be read.\n#\n# In this way we can have main code and constant execute in the order that they\n# are defined. Reading a constant before assigning a value to it should be pretty\n# rare, but it's still handled here to avoid crashes.\n#\n# Finally, simple constants like numbers, chars and strings are declared in the\n# beginning of the program because they don't involve a complex initialization\n# and can be done in any order (they have no side effects).\n\nclass Crystal::CodeGenVisitor\n  @const_mutex : LLVM::Value?\n\n  # The special constants ARGC_UNSAFE and ARGV_UNSAFE (and others) need to be initialized\n  # as soon as the program starts, because we have access to argc and argv\n  # in the main function.\n  def initialize_predefined_constants\n    @program.predefined_constants.each do |const|\n      initialize_no_init_flag_const(const)\n    end\n  end\n\n  def declare_const(const)\n    global_name = const.llvm_name\n    global = @main_mod.globals[global_name]? ||\n             @main_mod.globals.add(@main_llvm_typer.llvm_type(const.value.type), global_name)\n\n    type = const.value.type\n    # TODO: LLVM < 9.0.0 has a bug that prevents us from having internal globals of type i128 or u128:\n    # https://bugs.llvm.org/show_bug.cgi?id=42932\n    # so we just use global in that case.\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") < 0 %}\n      if @single_module && !(type.is_a?(IntegerType) && (type.kind.i128? || type.kind.u128?))\n        global.linkage = LLVM::Linkage::Internal\n      end\n    {% else %}\n      global.linkage = LLVM::Linkage::Internal if @single_module\n    {% end %}\n\n    global\n  end\n\n  def declare_const_initialized_flag(const)\n    initialized_flag_name = const.initialized_llvm_name\n    initialized_flag = @main_mod.globals[initialized_flag_name]?\n    unless initialized_flag\n      initialized_flag = @main_mod.globals.add(@main_llvm_context.int1, initialized_flag_name)\n      initialized_flag.initializer = @main_llvm_context.int1.const_int(0)\n      initialized_flag.linkage = LLVM::Linkage::Internal if @single_module\n    end\n    initialized_flag\n  end\n\n  def declare_const_and_initialized_flag(const)\n    {declare_const(const), declare_const_initialized_flag(const)}\n  end\n\n  def initialize_simple_const(const)\n    set_current_debug_location const.locations.try &.first? if @debug.line_numbers?\n\n    global = declare_const(const)\n    request_value(const.value)\n\n    const_type = const.value.type\n    if const_type.passed_by_value?\n      @last = load llvm_type(const_type), @last\n    end\n\n    global.initializer = @last\n    global.global_constant = true\n\n    if const_type.is_a?(PrimitiveType) || const_type.is_a?(EnumType)\n      const.initializer = @last\n    end\n  end\n\n  def initialize_no_init_flag_const(const)\n    global = declare_const(const)\n\n    with_cloned_context do\n      # \"self\" in a constant is the constant's namespace\n      context.type = const.namespace\n\n      # Start with fresh variables\n      context.vars = LLVMVars.new\n\n      set_current_debug_location const.locations.try &.first? if @debug.line_numbers?\n\n      alloca_vars const.fake_def.try(&.vars), const.fake_def\n      request_value(const.value)\n    end\n\n    const_type = const.value.type\n    if const_type.passed_by_value?\n      @last = load llvm_type(const_type), @last\n    end\n\n    store @last, global\n\n    global.initializer = @last.type.null\n\n    global\n  end\n\n  def initialize_const(const)\n    # If the constant wasn't read yet, we can initialize it right now and\n    # avoid checking an \"initialized\" flag every time we read it.\n    const.no_init_flag = true unless const.read?\n\n    # Maybe the constant was simple and doesn't need a real initialization\n    global, initialized_flag = declare_const_and_initialized_flag(const)\n    return global if const.initializer\n\n    init_function_name = \"~#{const.initialized_llvm_name}\"\n    func = typed_fun?(@main_mod, init_function_name) || create_initialize_const_function(init_function_name, const)\n    func = check_main_fun init_function_name, func\n\n    set_current_debug_location const.locations.try &.first? if @debug.line_numbers?\n\n    if const.no_init_flag?\n      call func\n    else\n      run_once(initialized_flag, func)\n    end\n    global\n  end\n\n  def create_initialize_const_function(fun_name, const)\n    global, _ = declare_const_and_initialized_flag(const)\n\n    in_main do\n      define_main_function(fun_name, ([] of LLVM::Type), llvm_context.void, needs_alloca: true) do |func|\n        set_internal_fun_debug_location(func, fun_name, const.locations.try &.first?)\n\n        with_cloned_context do\n          # \"self\" in a constant is the constant's namespace\n          context.type = const.namespace\n\n          # Start with fresh variables\n          context.vars = LLVMVars.new\n\n          alloca_vars const.fake_def.try(&.vars), const.fake_def\n\n          request_value(const.value)\n\n          const_type = const.value.type\n          if const_type.passed_by_value?\n            @last = load llvm_type(const_type), @last\n          end\n\n          if @last.constant?\n            global.initializer = @last\n            global.global_constant = true\n\n            if const_type.is_a?(PrimitiveType) || const_type.is_a?(EnumType)\n              const.initializer = @last\n            end\n          else\n            global.initializer = llvm_type(const_type).null\n            unless const_type.nil_type? || const_type.void?\n              store @last, global\n            end\n          end\n\n          ret\n        end\n      end\n    end\n  end\n\n  def read_const(const, node)\n    # We inline constants. Otherwise we use an LLVM const global.\n    @last =\n      case value = const.compile_time_value\n      when Bool    then int1(value ? 1 : 0)\n      when Char    then int32(value.ord)\n      when Int8    then int8(value)\n      when Int16   then int16(value)\n      when Int32   then int32(value)\n      when Int64   then int64(value)\n      when Int128  then int128(value)\n      when UInt8   then int8(value)\n      when UInt16  then int16(value)\n      when UInt32  then int32(value)\n      when UInt64  then int64(value)\n      when UInt128 then int128(value)\n      else\n        set_current_debug_location node if @debug.line_numbers?\n        last = read_const_pointer(const)\n        to_lhs last, const.value.type\n      end\n  end\n\n  def read_const_pointer(const)\n    const.read = true\n\n    if !const.needs_init_flag?\n      global_name = const.llvm_name\n      global = declare_const(const)\n\n      if @llvm_mod != @main_mod\n        global = @llvm_mod.globals[global_name]?\n        global ||= @llvm_mod.globals.add(llvm_type(const.value.type), global_name)\n      end\n\n      return global\n    end\n\n    read_function_name = \"~#{const.llvm_name}:const_read\"\n    func = typed_fun?(@main_mod, read_function_name) || create_read_const_function(read_function_name, const)\n    func = check_main_fun read_function_name, func\n    call func\n  end\n\n  def create_read_const_function(fun_name, const)\n    in_main do\n      define_main_function(fun_name, ([] of LLVM::Type), llvm_type(const.value.type).pointer) do |func|\n        set_internal_fun_debug_location(func, fun_name, const.locations.try &.first?)\n        global = initialize_const(const)\n        ret global\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/context.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  class Context\n    property fun : LLVM::Function\n    property fun_type : LLVM::Type\n    property fun_debug_params = [] of LibLLVM::MetadataRef\n    property type : Type\n    property vars : Hash(String, LLVMVar)\n    property return_type : Type?\n    property return_phi : Phi?\n    property break_phi : Phi?\n    property next_phi : Phi?\n    property while_block : LLVM::BasicBlock?\n    property! block : Block\n    property! block_context : Context\n    property closure_vars : Array(MetaVar)?\n    property closure_type : LLVM::Type?\n    property closure_ptr : LLVM::Value?\n    property closure_skip_parent : Bool\n    property closure_parent_context : Context?\n    property closure_self : Type?\n\n    def initialize(@fun, @fun_type, @type, @vars = LLVMVars.new)\n      @closure_skip_parent = false\n    end\n\n    def block_context=(@block_context : Nil)\n    end\n\n    def reset_closure\n      @closure_vars = nil\n      @closure_type = nil\n      @closure_ptr = nil\n      @closure_skip_parent = false\n      @closure_parent_context = nil\n      @closure_self = nil\n    end\n\n    def clone\n      context = Context.new @fun, @fun_type, @type, @vars\n      context.return_type = @return_type\n      context.return_phi = @return_phi\n      context.break_phi = @break_phi\n      context.next_phi = @next_phi\n      context.while_block = @while_block\n      if block = @block\n        context.block = block\n      end\n      context.block_context = @block_context\n      context.closure_vars = @closure_vars\n      context.closure_type = @closure_type\n      context.closure_ptr = @closure_ptr\n      context.closure_skip_parent = @closure_skip_parent\n      context.closure_parent_context = @closure_parent_context\n      context.closure_self = @closure_self\n      context\n    end\n\n    def add_fun_debug_param(debug_type)\n      fun_debug_params << debug_type if debug_type\n    end\n  end\n\n  def with_cloned_context(new_context = @context, &)\n    with_context(new_context.clone) { |ctx| yield ctx }\n  end\n\n  def with_context(new_context, &)\n    old_context = @context\n    @context = new_context\n    value = yield old_context\n    @context = old_context\n    value\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/crystal_llvm_builder.cr",
    "content": "module Crystal\n  class CrystalLLVMBuilder\n    property end : Bool\n\n    def initialize(@builder : LLVM::Builder, @llvm_typer : LLVMTyper, @printf : LLVMTypedFunction)\n      @end = false\n    end\n\n    def llvm_nil\n      @llvm_typer.nil_value\n    end\n\n    def ret\n      return llvm_nil if @end\n      value = @builder.ret\n      @end = true\n      value\n    end\n\n    def ret(value)\n      return llvm_nil if @end\n      value = @builder.ret(value)\n      @end = true\n      value\n    end\n\n    def br(block)\n      return llvm_nil if @end\n      value = @builder.br(block)\n      @end = true\n      value\n    end\n\n    def unreachable\n      return if @end\n      value = @builder.unreachable\n      @end = true\n      value\n    end\n\n    def printf(format, args = [] of LLVM::Value, catch_pad = nil)\n      if catch_pad\n        funclet = build_operand_bundle_def(\"funclet\", [catch_pad])\n      else\n        funclet = LLVM::OperandBundleDef.null\n      end\n\n      begin\n        call @printf, [global_string_pointer(format)] + args, bundle: funclet\n      ensure\n        funclet.dispose\n      end\n    end\n\n    def position_at_end(block)\n      @builder.position_at_end block\n      @end = false\n    end\n\n    def insert_block\n      @builder.insert_block\n    end\n\n    def build_operand_bundle_def(name, values : Array(LLVM::Value))\n      @builder.build_operand_bundle_def(name, values)\n    end\n\n    def current_debug_location_metadata\n      {% if LibLLVM::IS_LT_90 %}\n        LibLLVM.value_as_metadata LibLLVM.get_current_debug_location(@builder)\n      {% else %}\n        LibLLVM.get_current_debug_location2(@builder)\n      {% end %}\n    end\n\n    def to_unsafe\n      @builder.to_unsafe\n    end\n\n    {% for name in %w(add add_handler alloca and ashr atomicrmw bit_cast build_catch_ret call\n                     catch_pad catch_switch clear_current_debug_location cmpxchg cond\n                     current_debug_location exact_sdiv extract_value fadd fcmp fdiv fence fmul\n                     fp2si fp2ui fpext fptrunc fsub global_string_pointer icmp inbounds_gep int2ptr\n                     invoke landing_pad load lshr mul not or phi ptr2int sdiv select\n                     set_current_debug_location sext shl si2fp srem store store_volatile sub switch\n                     trunc udiv ui2fp urem va_arg xor zext) %}\n      def {{name.id}}(*args, **kwargs)\n        return llvm_nil if @end\n\n        @builder.{{name.id}}(*args, **kwargs)\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/debug.cr",
    "content": "require \"./codegen\"\n\nmodule Crystal\n  class CodeGenVisitor\n    # workaround for `LLVM::Builder` not being GC'ed (#13250)\n    private class DIBuilder\n      def initialize(mod : LLVM::Module)\n        @builder = LLVM::DIBuilder.new(mod)\n      end\n\n      def finalize\n        @builder.dispose\n      end\n\n      forward_missing_to @builder\n    end\n\n    record FunMetadata, filename : String, metadata : LibLLVM::MetadataRef\n\n    alias DebugFilename = Crystal::VirtualFile | String?\n\n    @current_debug_location : Location?\n\n    # We cache these either for performance, memory use, or protection from the GC\n    @debug_files_per_module = {} of LLVM::Module => Hash(DebugFilename, LibLLVM::MetadataRef)\n    @debug_types_per_module = {} of LLVM::Module => Hash(Type, LibLLVM::MetadataRef?)\n\n    def di_builder(llvm_module = @llvm_mod || @main_mod)\n      di_builders = @di_builders ||= {} of LLVM::Module => DIBuilder\n      di_builders[llvm_module] ||= DIBuilder.new(llvm_module).tap do |di_builder|\n        file, dir = file_and_dir(llvm_module.name == \"\" ? \"main\" : llvm_module.name)\n        # @debug.variables? is set to true if parameter --debug is set in command line.\n        # This flag affects only debug variables generation. It sets Optimized parameter to false.\n        is_optimised = !@debug.variables?\n        # TODO: switch to Crystal's language code for LLVM 16+ (#13174)\n        di_builder.create_compile_unit(LLVM::DwarfSourceLanguage::C_plus_plus, file, dir, \"Crystal\", is_optimised, \"\", 0_u32)\n      end\n    end\n\n    def push_debug_info_metadata(mod)\n      di_builder(mod).end\n\n      if @program.has_flag?(\"msvc\")\n        # Windows uses CodeView instead of DWARF\n        mod.add_flag(LibLLVM::ModuleFlagBehavior::Warning, \"CodeView\", 1)\n      end\n\n      mod.add_flag(\n        LibLLVM::ModuleFlagBehavior::Warning,\n        \"Debug Info Version\",\n        LLVM::DEBUG_METADATA_VERSION\n      )\n    end\n\n    def fun_metadatas\n      @fun_metadatas ||= {} of LLVM::Function => Array(FunMetadata)\n    end\n\n    def fun_metadata_type(debug_types = [] of LibLLVM::MetadataRef)\n      if debug_types.empty?\n        int = di_builder.create_basic_type(\"int\", 32, 32, LLVM::DwarfTypeEncoding::Signed)\n        debug_types << int\n      end\n      di_builder.create_subroutine_type(nil, debug_types)\n    end\n\n    def debug_type_cache\n      # We must cache debug types per module so metadata of a type\n      # from one module isn't incorrectly used in another module.\n      @debug_types_per_module[@llvm_mod] ||= {} of Type => LibLLVM::MetadataRef?\n    end\n\n    def debug_files_cache\n      # We must cache debug files per module so metadata of a type\n      # from one module isn't incorrectly used in another module.\n      @debug_files_per_module[@llvm_mod] ||= {} of DebugFilename => LibLLVM::MetadataRef\n    end\n\n    private def current_debug_file\n      # These debug files are only used for `DIBuilder#create_union_type`, even\n      # though they are unneeded here, just as struct types don't need a file;\n      # LLVM 12 or below produces an assertion failure that is now removed\n      # (https://github.com/llvm/llvm-project/commit/ad60802a7187aa39b0374536be3fa176fe3d6256)\n      {% if LibLLVM::IS_LT_130 %}\n        filename = @current_debug_location.try(&.filename) || \"??\"\n        debug_files_cache[filename] ||= begin\n          file, dir = file_and_dir(filename)\n          di_builder.create_file(file, dir)\n        end\n      {% else %}\n        Pointer(Void).null.as(LibLLVM::MetadataRef)\n      {% end %}\n    end\n\n    def get_debug_type(type, original_type : Type)\n      type = type.remove_indirection\n      debug_type_cache[original_type] ||= create_debug_type(type, original_type)\n    end\n\n    def create_debug_type(type : NilType, original_type : Type)\n      di_builder.create_unspecified_type(\"decltype(nullptr)\")\n    end\n\n    def create_debug_type(type : VoidType, original_type : Type)\n      di_builder.create_basic_type(\"Void\", 8u64 * llvm_typer.pointer_size, 8u64 * llvm_typer.pointer_size, LLVM::DwarfTypeEncoding::Address)\n    end\n\n    def create_debug_type(type : CharType, original_type : Type)\n      di_builder.create_basic_type(\"char32_t\", 32, 32, LLVM::DwarfTypeEncoding::Utf)\n    end\n\n    def create_debug_type(type : IntegerType, original_type : Type)\n      di_builder.create_basic_type(type.to_s, type.bits, type.bits,\n        type.signed? ? LLVM::DwarfTypeEncoding::Signed : LLVM::DwarfTypeEncoding::Unsigned)\n    end\n\n    def create_debug_type(type : SymbolType, original_type : Type)\n      di_builder.create_basic_type(type.to_s, 32, 32, LLVM::DwarfTypeEncoding::Unsigned)\n    end\n\n    def create_debug_type(type : FloatType, original_type : Type)\n      di_builder.create_basic_type(type.to_s, 8u64 * type.bytes, 8u64 * type.bytes, LLVM::DwarfTypeEncoding::Float)\n    end\n\n    def create_debug_type(type : BoolType, original_type : Type)\n      di_builder.create_basic_type(type.to_s, 8, 8, LLVM::DwarfTypeEncoding::Boolean)\n    end\n\n    def create_debug_type(type : EnumType, original_type : Type)\n      elements = type.types.map do |name, item|\n        value = item.as?(Const).try &.value.as?(NumberLiteral).try &.integer_value\n        di_builder.create_enumerator(name, value || 0)\n      end\n\n      size_in_bits = type.base_type.kind.bytesize\n      align_in_bits = align_of(type.base_type)\n      di_builder.create_enumeration_type(nil, original_type.to_s, nil, 1, size_in_bits, align_in_bits, elements, get_debug_type(type.base_type))\n    end\n\n    def create_debug_type(type : InstanceVarContainer, original_type : Type)\n      ivars = type.all_instance_vars\n      element_types = [] of LibLLVM::MetadataRef\n      struct_type = llvm_struct_type(type)\n\n      tmp_debug_type = di_builder.create_replaceable_composite_type(nil, original_type.to_s, nil, 1)\n      debug_type_cache[original_type] = tmp_debug_type\n\n      ivars.each_with_index do |(name, ivar), idx|\n        next if ivar.type.is_a?(NilType)\n        if (ivar_type = ivar.type?) && (ivar_debug_type = get_debug_type(ivar_type))\n          offset = type.extern_union? ? 0_u64 : @program.target_machine.data_layout.offset_of_element(struct_type, idx &+ (type.struct? ? 0 : 1))\n          size = @program.target_machine.data_layout.size_in_bits(llvm_embedded_type(ivar_type))\n\n          member = di_builder.create_member_type(nil, name[1..-1], nil, 1, size, size, 8u64 * offset, LLVM::DIFlags::Zero, ivar_debug_type)\n          element_types << member\n        end\n      end\n\n      size = @program.target_machine.data_layout.size_in_bits(struct_type)\n      if type.extern_union?\n        debug_type = di_builder.create_union_type(nil, original_type.to_s, current_debug_file, 1, size, size, LLVM::DIFlags::Zero, element_types)\n      else\n        debug_type = di_builder.create_struct_type(nil, original_type.to_s, nil, 1, size, size, LLVM::DIFlags::Zero, nil, element_types)\n        unless type.struct?\n          debug_type = di_builder.create_pointer_type(debug_type, 8u64 * llvm_typer.pointer_size, 8u64 * llvm_typer.pointer_size, original_type.to_s)\n        end\n      end\n      di_builder.replace_temporary(tmp_debug_type, debug_type)\n      debug_type\n    end\n\n    def create_debug_type(type : PointerInstanceType, original_type : Type)\n      element_type = get_debug_type(type.element_type)\n      return unless element_type\n      di_builder.create_pointer_type(element_type, 8u64 * llvm_typer.pointer_size, 8u64 * llvm_typer.pointer_size, original_type.to_s)\n    end\n\n    def create_debug_type(type : MixedUnionType, original_type : Type)\n      element_types = [] of LibLLVM::MetadataRef\n      struct_type = llvm_type(type)\n      struct_type_size = @program.target_machine.data_layout.size_in_bits(struct_type)\n      is_struct = struct_type.struct_element_types.size == 1\n\n      tmp_debug_type = di_builder.create_replaceable_composite_type(nil, original_type.to_s, nil, 1)\n      debug_type_cache[original_type] = tmp_debug_type\n\n      type.expand_union_types.each do |ivar_type|\n        next if ivar_type.is_a?(NilType)\n        if ivar_debug_type = get_debug_type(ivar_type)\n          embedded_type = llvm_type(ivar_type)\n          size = @program.target_machine.data_layout.size_in_bits(embedded_type)\n          align = align_of(ivar_type)\n          member = di_builder.create_member_type(nil, ivar_type.to_s, nil, 1, size, align, 0, LLVM::DIFlags::Zero, ivar_debug_type)\n          element_types << member\n        end\n      end\n\n      size = @program.target_machine.data_layout.size_in_bits(struct_type.struct_element_types[is_struct ? 0 : 1])\n      offset = @program.target_machine.data_layout.offset_of_element(struct_type, 1) * 8u64\n      debug_type = di_builder.create_union_type(nil, \"\", current_debug_file, 1, size, size, LLVM::DIFlags::Zero, element_types)\n      unless is_struct\n        element_types.clear\n        element_types << di_builder.create_member_type(nil, \"type_id\", nil, 1, 32, 32, 0, LLVM::DIFlags::Zero, get_debug_type(@program.uint32))\n        element_types << di_builder.create_member_type(nil, \"union\", nil, 1, size, size, offset, LLVM::DIFlags::Zero, debug_type)\n        debug_type = di_builder.create_struct_type(nil, original_type.to_s, nil, 1, struct_type_size, struct_type_size, LLVM::DIFlags::Zero, nil, element_types)\n      end\n      di_builder.replace_temporary(tmp_debug_type, debug_type)\n      debug_type\n    end\n\n    def create_debug_type(type : NilableReferenceUnionType | ReferenceUnionType, original_type : Type)\n      element_types = [] of LibLLVM::MetadataRef\n      struct_type = llvm_type(type)\n      tmp_debug_type = di_builder.create_replaceable_composite_type(nil, original_type.to_s, nil, 1)\n      debug_type_cache[original_type] = tmp_debug_type\n\n      type.expand_union_types.each do |ivar_type|\n        next if ivar_type.is_a?(NilType)\n        if ivar_debug_type = get_debug_type(ivar_type)\n          embedded_type = llvm_type(ivar_type)\n          size = @program.target_machine.data_layout.size_in_bits(embedded_type)\n          member = di_builder.create_member_type(nil, ivar_type.to_s, nil, 1, size, size, 0, LLVM::DIFlags::Zero, ivar_debug_type)\n          element_types << member\n        end\n      end\n\n      size = @program.target_machine.data_layout.size_in_bits(struct_type)\n      debug_type = di_builder.create_union_type(nil, original_type.to_s, current_debug_file, 1, size, size, LLVM::DIFlags::Zero, element_types)\n      di_builder.replace_temporary(tmp_debug_type, debug_type)\n      debug_type\n    end\n\n    def create_debug_type(type : NilableType, original_type : Type)\n      get_debug_type(type.not_nil_type, original_type)\n    end\n\n    def create_debug_type(type : StaticArrayInstanceType, original_type : Type)\n      debug_type = get_debug_type(type.element_type)\n      return unless debug_type\n      subrange = di_builder.get_or_create_array_subrange(0, type.size.as(NumberLiteral).value.to_i)\n      di_builder.create_array_type(type.size.as(NumberLiteral).value.to_i, llvm_typer.pointer_size, debug_type, [subrange])\n    end\n\n    def create_debug_type(type : TypeDefType, original_type : Type)\n      get_debug_type(type.typedef, original_type)\n    end\n\n    def create_debug_type(type : TupleInstanceType, original_type : Type)\n      ivars = type.tuple_types\n      element_types = [] of LibLLVM::MetadataRef\n      struct_type = llvm_struct_type(type)\n\n      tmp_debug_type = di_builder.create_replaceable_composite_type(nil, original_type.to_s, nil, 1)\n      debug_type_cache[original_type] = tmp_debug_type\n\n      ivars.each_with_index do |ivar_type, idx|\n        next if ivar_type.is_a?(NilType)\n        if ivar_debug_type = get_debug_type(ivar_type)\n          offset = @program.target_machine.data_layout.offset_of_element(struct_type, idx &+ (type.struct? ? 0 : 1))\n          size = @program.target_machine.data_layout.size_in_bits(llvm_embedded_type(ivar_type))\n\n          member = di_builder.create_member_type(nil, \"[#{idx}]\", nil, 1, size, size, 8u64 * offset, LLVM::DIFlags::Zero, ivar_debug_type)\n          element_types << member\n        end\n      end\n\n      size = @program.target_machine.data_layout.size_in_bits(struct_type)\n      debug_type = di_builder.create_struct_type(nil, original_type.to_s, nil, 1, size, size, LLVM::DIFlags::Zero, nil, element_types)\n      unless type.struct?\n        debug_type = di_builder.create_pointer_type(debug_type, 8u64 * llvm_typer.pointer_size, 8u64 * llvm_typer.pointer_size, original_type.to_s)\n      end\n      di_builder.replace_temporary(tmp_debug_type, debug_type)\n      debug_type\n    end\n\n    def create_debug_type(type : NamedTupleInstanceType, original_type : Type)\n      ivars = type.entries\n      element_types = [] of LibLLVM::MetadataRef\n      struct_type = llvm_struct_type(type)\n\n      tmp_debug_type = di_builder.create_replaceable_composite_type(nil, original_type.to_s, nil, 1)\n      debug_type_cache[original_type] = tmp_debug_type\n\n      ivars.each_with_index do |ivar, idx|\n        next if (ivar_type = ivar.type).is_a?(NilType)\n        if ivar_debug_type = get_debug_type(ivar_type)\n          offset = @program.target_machine.data_layout.offset_of_element(struct_type, idx &+ (type.struct? ? 0 : 1))\n          size = @program.target_machine.data_layout.size_in_bits(llvm_embedded_type(ivar_type))\n\n          member = di_builder.create_member_type(nil, ivar.name, nil, 1, size, size, 8u64 * offset, LLVM::DIFlags::Zero, ivar_debug_type)\n          element_types << member\n        end\n      end\n\n      size = @program.target_machine.data_layout.size_in_bits(struct_type)\n      debug_type = di_builder.create_struct_type(nil, original_type.to_s, nil, 1, size, size, LLVM::DIFlags::Zero, nil, element_types)\n      unless type.struct?\n        debug_type = di_builder.create_pointer_type(debug_type, 8u64 * llvm_typer.pointer_size, 8u64 * llvm_typer.pointer_size, original_type.to_s)\n      end\n      di_builder.replace_temporary(tmp_debug_type, debug_type)\n      debug_type\n    end\n\n    # This is a sinkhole for debug types that most likely does not need to be implemented\n    def create_debug_type(type : NonGenericModuleType | GenericClassInstanceMetaclassType | MetaclassType | NilableProcType | VirtualMetaclassType, original_type : Type)\n    end\n\n    def create_debug_type(type, original_type : Type)\n      # \"Unsupported type for debugging: #{type} (#{type.class}), original_type=#{original_type.to_s}\" }\n    end\n\n    def get_debug_type(type)\n      get_debug_type(type, type)\n    end\n\n    def declare_parameter(arg_name, arg_type, arg_no, alloca, location)\n      return alloca unless @debug.variables?\n\n      declare_local(arg_type, alloca, location) do |scope, file, line_number, debug_type|\n        di_builder.create_parameter_variable scope, arg_name, arg_no, file, line_number, debug_type\n      end\n    end\n\n    def declare_variable(var_name, var_type, alloca, location, basic_block : LLVM::BasicBlock? = nil, offset : Int = 0)\n      return false unless @debug.variables?\n      declare_local(var_type, alloca, location, basic_block, offset) do |scope, file, line_number, debug_type|\n        di_builder.create_auto_variable scope, var_name, file, line_number, debug_type, align_of(var_type)\n      end\n    end\n\n    private def align_of(type)\n      @program.target_machine.data_layout.abi_alignment(llvm_type(type)) * 8\n    end\n\n    # see also the other DWARF enums in `crystal/dwarf/abbrev.cr` (note that\n    # LLVM defines several custom opcodes outside the user extension range)\n    DW_OP_plus_uconst = 0x23\n\n    private def declare_local(type, alloca, location, basic_block : LLVM::BasicBlock? = nil, offset : Int = 0, &)\n      location = location.try &.expanded_location\n      return false unless location\n\n      file, dir = file_and_dir(location.filename)\n      file = debug_files_cache[location.filename] ||= di_builder.create_file(file, dir)\n\n      debug_type = get_debug_type(type)\n      return false unless debug_type\n\n      scope = get_current_debug_scope(location)\n      return false unless scope\n\n      var = yield scope, file, location.line_number, debug_type\n\n      if offset != 0\n        expr =\n          {% if LibLLVM::IS_LT_140 %}\n            di_builder.create_expression(Int64.static_array(DW_OP_plus_uconst, offset), 2)\n          {% else %}\n            di_builder.create_expression(UInt64.static_array(DW_OP_plus_uconst, offset), 2)\n          {% end %}\n      else\n        expr = di_builder.create_expression(nil, 0)\n      end\n\n      if basic_block\n        block = basic_block\n      else\n        block = context.fun.basic_blocks.last? || new_block(\"alloca\")\n      end\n      old_debug_location = @current_debug_location\n      set_current_debug_location location\n      if builder.current_debug_location != llvm_nil && (ptr = alloca)\n        di_builder.insert_declare_at_end(ptr, var, expr, builder.current_debug_location_metadata, block)\n        set_current_debug_location old_debug_location\n        true\n      else\n        set_current_debug_location old_debug_location\n        false\n      end\n    end\n\n    private def do_nothing_fun\n      fetch_typed_fun(@llvm_mod, \"llvm.donothing\") do\n        LLVM::Type.function([] of LLVM::Type, @llvm_context.void)\n      end\n    end\n\n    # Emit debug info for toplevel variables. Used for the main module and all\n    # required files.\n    def emit_vars_debug_info(vars)\n      return if @debug.none?\n      in_alloca_block do\n        vars.each do |name, var|\n          # If a variable is deduced to have type `NoReturn` it might not be\n          # allocated at all\n          next unless (llvm_var = context.vars[name]?)\n          next if llvm_var.debug_variable_created\n          set_current_debug_location var.location\n          declare_variable name, var.type, llvm_var.pointer, var.location, alloca_block\n        end\n        clear_current_debug_location\n      end\n    end\n\n    def file_and_dir(filename)\n      # We should have expanded locations with VirtualFiles in them to\n      # the location where they expanded. Debug locations will point\n      # to the single line where the macro was expanded. This is not\n      # convenient for debugging macro code, but the other solution\n      # involves creating temporary files to hold the expanded macro\n      # code, but that prevents reusing previous compilations. In\n      # any case, macro code *should* be simple so that it doesn't\n      # need to be debugged at runtime (because macros work at compile-time.)\n      unless filename.is_a?(String)\n        raise \"BUG: expected debug filename to be a String, not #{filename.class}\"\n      end\n\n      {\n        File.basename(filename), # File\n        File.dirname(filename),  # Directory\n      }\n    end\n\n    def set_current_debug_location(node : ASTNode)\n      location = node.location\n      if location\n        set_current_debug_location(location)\n      else\n        clear_current_debug_location\n      end\n    end\n\n    def get_current_debug_scope(location)\n      if context.fun.name == MAIN_NAME\n        main_scopes = (@main_scopes ||= {} of {String, String} => LibLLVM::MetadataRef)\n        file, dir = file_and_dir(location.filename)\n        main_scope = main_scopes[{file, dir}] ||= begin\n          di_builder = di_builder(@main_mod)\n          file = di_builder.create_file(file, dir)\n          di_builder.create_lexical_block(fun_metadatas[context.fun][0].metadata, file, 1, 1)\n        end\n        main_scope\n      else\n        scope = nil\n        if array = fun_metadatas[context.fun]?\n          array.each do |scope_pair|\n            return scope_pair.metadata if scope_pair.filename == location.filename\n          end\n          file, dir = file_and_dir(location.filename)\n          di_builder = di_builder()\n          file_scope = di_builder.create_file(file, dir)\n          scope = di_builder.create_lexical_block_file(fun_metadatas[context.fun][0].metadata, file_scope)\n          array << FunMetadata.new(location.original_filename || \"??\", scope)\n        end\n        scope\n      end\n    end\n\n    def set_current_debug_location(location)\n      location = location.try &.expanded_location\n      return unless location\n\n      @current_debug_location = location\n\n      scope = get_current_debug_scope(location)\n\n      if scope\n        debug_loc = di_builder.create_debug_location(location.line_number || 1, location.column_number, scope)\n        # NOTE: `di_builder.context` is only necessary for LLVM 8\n        builder.set_current_debug_location(debug_loc, di_builder.context)\n      else\n        clear_current_debug_location\n      end\n    end\n\n    def clear_current_debug_location\n      @current_debug_location = nil\n\n      builder.clear_current_debug_location\n    end\n\n    def emit_fun_debug_metadata(func, fun_name, location, *, debug_types = [] of LibLLVM::MetadataRef, is_optimized = false)\n      filename = location.try(&.original_filename) || \"??\"\n      line_number = location.try(&.line_number) || 0\n\n      file, dir = file_and_dir(filename)\n      scope = di_builder.create_file(file, dir)\n      fn_metadata = di_builder.create_function(scope, fun_name, fun_name, scope,\n        line_number, fun_metadata_type(debug_types), true, true,\n        line_number, LLVM::DIFlags::Zero, is_optimized, func)\n      fun_metadatas[func] = [FunMetadata.new(filename, fn_metadata)]\n    end\n\n    def emit_def_debug_metadata(target_def)\n      location = target_def.location.try &.expanded_location\n      return unless location\n\n      emit_fun_debug_metadata(context.fun, target_def.name, location,\n        debug_types: context.fun_debug_params,\n        is_optimized: !@debug.variables?)\n    end\n\n    def declare_debug_for_function_argument(arg_name, arg_type, arg_no, alloca, location)\n      return alloca unless @debug.variables?\n      old_debug_location = @current_debug_location\n      set_current_debug_location location\n      debug_alloca = alloca alloca.type, \"dbg.#{arg_name}\"\n      store alloca, debug_alloca\n      declare_parameter(arg_name, arg_type, arg_no, debug_alloca, location)\n      alloca = load alloca.type, debug_alloca\n      set_current_debug_location old_debug_location\n      alloca\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/dump_type_info.cr",
    "content": "module Crystal\n  class CodeGenVisitor\n    private def dump_type_info(path)\n      ids = @program.llvm_id.@ids.to_a\n      ids.sort_by! { |_, (_, id)| id }\n\n      File.open(path, \"w\") do |f|\n        JSON.build(f) do |j|\n          j.object do\n            j.field \"types\" do\n              j.array do\n                ids.each do |type, (min_subtype_id, id)|\n                  dump_single_type(j, type, min_subtype_id, id)\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n\n    private def dump_single_type(j : JSON::Builder, type, min_subtype_id, id)\n      unless type.is_a?(GenericClassType)\n        llvm_type = llvm_type(type)\n        if type.is_a?(InstanceVarContainer)\n          llvm_struct_type = llvm_struct_type(type)\n        end\n      end\n\n      j.object do\n        j.field \"name\" do\n          j.string do |io|\n            type.to_s_with_options(io, codegen: true, generic_args: true)\n          end\n        end\n        j.field \"id\", id\n        j.field \"min_subtype_id\", min_subtype_id\n        if supertype = type.superclass\n          j.field \"supertype_id\", @program.llvm_id.type_id(supertype)\n        end\n        if type.metaclass?\n          j.field \"instance_type_id\", @program.llvm_id.type_id(type.instance_type)\n        end\n\n        has_inner_pointers =\n          if type.struct?\n            type.has_inner_pointers?\n          else\n            type.is_a?(InstanceVarContainer) && type.all_instance_vars.each_value.any? &.type.has_inner_pointers?\n          end\n        j.field \"has_inner_pointers\", has_inner_pointers\n\n        if llvm_type\n          j.field \"size\", @llvm_typer.size_of(llvm_type)\n          j.field \"align\", @llvm_typer.align_of(llvm_type)\n        end\n\n        if llvm_struct_type\n          unless type.struct?\n            j.field \"instance_size\", @llvm_typer.size_of(llvm_struct_type)\n            j.field \"instance_align\", @llvm_typer.align_of(llvm_struct_type)\n          end\n\n          if type.allows_instance_vars?\n            j.field \"instance_vars\" do\n              j.array do\n                type.all_instance_vars.each do |name, ivar|\n                  ivar_offset, ivar_size = ivar_offset_and_size(type, llvm_struct_type, name, ivar.type)\n                  j.object do\n                    j.field \"name\", name\n                    j.field \"type_name\" do\n                      j.string do |io|\n                        ivar.type.to_s_with_options(io, codegen: true)\n                      end\n                    end\n                    j.field \"offset\", ivar_offset\n                    j.field \"size\", ivar_size\n                  end\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n\n    private def ivar_offset_and_size(type, llvm_type, ivar_name, ivar_type) : {UInt64, UInt64}\n      if type.extern_union? || type.is_a?(StaticArrayInstanceType)\n        return 0_u64, @llvm_typer.size_of(llvm_type)\n      end\n\n      element_index = type.index_of_instance_var(ivar_name).not_nil!\n      element_index += 1 unless type.struct?\n\n      ivar_llvm_type =\n        if type.extern?\n          @llvm_typer.llvm_embedded_c_type(ivar_type, wants_size: true)\n        else\n          @llvm_typer.llvm_embedded_type(ivar_type, wants_size: true)\n        end\n\n      {\n        @llvm_typer.offset_of(llvm_type, element_index),\n        @llvm_typer.size_of(ivar_llvm_type),\n      }\n    end\n\n    private def dump_type_id\n      ids = @program.llvm_id.@ids.to_a\n      ids.sort_by! { |_, (min, max)| {min, -max} }\n\n      puts \"CRYSTAL_DUMP_TYPE_ID\"\n      parent_ids = [] of {Int32, Int32}\n      ids.each do |type, (min, max)|\n        while parent_id = parent_ids.last?\n          break if min >= parent_id[0] && max <= parent_id[1]\n          parent_ids.pop\n        end\n        indent = \" \" * (2 * parent_ids.size)\n\n        show_generic_args = type.is_a?(GenericInstanceType) ||\n                            type.is_a?(GenericClassInstanceMetaclassType) ||\n                            type.is_a?(GenericModuleInstanceMetaclassType)\n        puts \"#{indent}{#{min} - #{max}}: #{type.to_s(generic_args: show_generic_args)}\"\n        parent_ids << {min, max}\n      end\n      puts\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/exception.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  @node_ensure_exception_handlers : Hash(ASTNode, Handler) = ({} of ASTNode => Handler).compare_by_identity\n\n  def visit(node : ExceptionHandler)\n    # In this codegen, we assume that LLVM only provides us with a basic try/catch abstraction with no\n    # type restrictions on the exception caught. The basic strategy is to codegen this\n    #\n    # ```\n    # begin\n    #   body\n    # else\n    #   else_body\n    # rescue ex : Ex1\n    #   rescue_1_body\n    # rescue ex : Ex2\n    #   rescue_2_body\n    # rescue ex\n    #   rescue_3_body\n    # ensure\n    #   ensure_body\n    # end\n    # ```\n    #\n    # Into something like (assuming goto is implemented in crystal):\n    #\n    # ```\n    # begin\n    #   body\n    # rescue ex\n    #   begin\n    #     if ex.is_a? Ex1\n    #       rescue_1_body\n    #     elsif ex.is_a? Ex2\n    #       rescue_2_body\n    #     else\n    #       if rescue_3_body\n    #         rescue_3_body\n    #       else\n    #         # If no handlers match and there is no generic handler, re-raise\n    #         ensure_body\n    #         raise ex\n    #       end\n    #     end\n    #\n    #     # Skip else_body if we ran an exception handler\n    #     goto exit\n    #   rescue ex2\n    #    ensure_body\n    #    raise ex2\n    #   end\n    # end\n    #\n    # else_body\n    #\n    # exit:\n    # ensure_body\n    # ```\n    #\n    # Note we codegen the ensure body three times! In practice this isn't a big deal, since ensure bodies are typically small.\n\n    msvc = @program.has_flag?(\"msvc\")\n\n    context.fun.personality_function = windows_personality_fun.func if msvc\n\n    # This is the block which is entered when the body raises an exception\n    rescue_block = new_block \"rescue\"\n\n    node_rescues = node.rescues\n    node_ensure = node.ensure\n    node_else = node.else\n    rescue_ensure_block = nil\n\n    Phi.open(self, node, @needs_value) do |phi|\n      # If there's an ensure block, even if the body/else\n      # and all rescues are NoReturn we must still generate the\n      # ensure part, which is done in the exit block.\n      phi.force_exit_block = !!node_ensure\n\n      # Keep track of the stack of ensure blocks inside the current function\n      # This is used in codegenning returns to run the ensure blocks before returning\n      ensure_exception_handlers = (@ensure_exception_handlers ||= [] of Handler)\n      ensure_exception_handlers.push Handler.new(node, context)\n\n      # Codegen the body of the exception handler\n      # All exceptions raised in here will enter rescue_block. We tell the codegen to make this happen\n      # by setting `@rescue_block`.\n      old_rescue_block = @rescue_block\n      @rescue_block = rescue_block\n      accept node.body\n      @rescue_block = old_rescue_block\n\n      # Codegen the `else` block directly after the body of the rescue, except without\n      # the rescue block set.\n      # If there's an else, we take the value from it. Otherwise, the value is taken from the body.\n      if node_else\n        accept node_else\n        phi.add @last, node_else.type?\n      else\n        phi.add @last, node.body.type?\n      end\n\n      # Here we start codegenning what happens once an exception is raised.\n      # This code is responsible for finding the type of the exception, and\n      # deciding which `rescue` block is entered (if any).\n      # If no rescue block is entered, the ensure block is ran then the exception is re-raised.\n      position_at_end rescue_block\n\n      old_catch_pad = @catch_pad\n\n      if msvc\n        # Windows structured exception handling must enter a catch_switch instruction\n        # which decides which catch body block to enter. Crystal only ever generates one catch body\n        # which is used for all exceptions. For more information on how structured exception handling works in LLVM,\n        # see https://llvm.org/docs/ExceptionHandling.html#exception-handling-using-the-windows-runtime\n        catch_body = new_block \"catch_body\"\n        catch_switch = builder.catch_switch(@catch_pad || LLVM::Value.null, @rescue_block || LLVM::BasicBlock.null, 1)\n        builder.add_handler catch_switch, catch_body\n\n        # We're now generating the catch body, which must begin with a catchpad instruction\n        position_at_end catch_body\n\n        # Allocate space for the caught exception\n        exception_type = @program.exception.virtual_type\n        exception_llvm_type = llvm_type(exception_type)\n        caught_exception_ptr = alloca exception_llvm_type\n\n        # The catchpad instruction dictates which types of exceptions this block handles,\n        # we want all of them, so we rescue all void* by passing the void_ptr_type_descriptor.\n        # We also need to record the catch pad instruction in `@catch_pad` to refer to the parent catch\n        # pad in nested rescue blocks, and to generate funclet information for function calls which are\n        # \"inside\" this catchpad. More information on this is available in the link above.\n        @catch_pad = builder.catch_pad catch_switch, [void_ptr_type_descriptor, int32(0), caught_exception_ptr]\n\n        # builder.printf(\"catchpad entered #{node.location}\\n\", catch_pad: @catch_pad)\n\n        caught_exception = load exception_llvm_type, caught_exception_ptr\n        exception_type_id = type_id(caught_exception, exception_type)\n      else\n        # Unwind exception handling code - used on non-MSVC platforms (essentially the Itanium\n        # C++ ABI) - is a lot simpler.\n        # First we generate the landing pad instruction, this returns a tuple of the libunwind\n        # exception object and the type ID of the exception. This tuple is set up in the crystal\n        # personality function in raise.cr\n        lp_ret_type = llvm_typer.landing_pad_type\n        lp = builder.landing_pad lp_ret_type, main_fun(personality_name).func, [] of LLVM::Value\n        unwind_ex_obj = extract_value lp, 0\n        exception_type_id = extract_value lp, 1\n\n        # We call __crystal_get_exception to get the actual crystal `Exception` object.\n        get_exception_fun = main_fun(GET_EXCEPTION_NAME)\n        get_exception_arg_type = get_exception_fun.type.params_types.first # Void* or LibUnwind::Exception*\n        get_exception_arg = pointer_cast(unwind_ex_obj, get_exception_arg_type)\n\n        set_current_debug_location node if @debug.line_numbers?\n        caught_exception_ptr = call get_exception_fun, [get_exception_arg]\n        caught_exception = int2ptr caught_exception_ptr, llvm_typer.type_id_pointer\n      end\n\n      if node_rescues\n        old_rescue_block = @rescue_block\n\n        # Exceptions raised in a `rescue` block must be caught so that the `ensure` block can be ran.\n        # Here we set up the rescue block for that purpose.\n        if node_ensure\n          rescue_ensure_block = new_block \"rescue_ensure\"\n        else\n          rescue_ensure_block = @rescue_block\n        end\n\n        node_rescues.each do |a_rescue|\n          # For every rescue block, we generate a type ID check which branches to\n          # `this_rescue_block` if the exception type is covered by the type restriction, or\n          # branches to `next_rescue_block` if the restriction doesn't match.\n          this_rescue_block, next_rescue_block = new_blocks \"this_rescue\", \"next_rescue\"\n          if a_rescue_types = a_rescue.types\n            cond = nil\n            a_rescue_types.each do |type|\n              rescue_type = type.type.instance_type.virtual_type\n              rescue_type_cond = match_any_type_id(rescue_type, exception_type_id)\n              cond = cond ? or(cond, rescue_type_cond) : rescue_type_cond\n            end\n            cond cond.not_nil!, this_rescue_block, next_rescue_block\n          else\n            br this_rescue_block\n          end\n\n          # If the rescue restriction matches, codegen the rescue block.\n          position_at_end this_rescue_block\n\n          # On MSVC, we are \"inside\" the catchpad block. It's difficult to track when to catch_ret when\n          # codegenning the entire rescue body, so we catch_ret early and execute the rescue bodies \"outside\" the\n          # rescue block.\n          if catch_pad = @catch_pad\n            catch_ret_target_block = new_block \"this_rescue_target\"\n            builder.build_catch_ret catch_pad, catch_ret_target_block\n            position_at_end catch_ret_target_block\n          end\n\n          saved_catch_pad = @catch_pad\n          @catch_pad = old_catch_pad\n\n          # We are generating code for rescues, so set up the rescue block.\n          @rescue_block = rescue_ensure_block\n\n          with_cloned_context do\n            if a_rescue_name = a_rescue.name\n              context.vars = context.vars.dup\n\n              # Cast the caught exception to the type restriction, then assign it\n              cast_caught_exception = cast_to caught_exception, a_rescue.type\n              var = context.vars[a_rescue_name]\n              assign var.pointer, var.type, a_rescue.type, cast_caught_exception\n            end\n\n            accept a_rescue.body\n          end\n          phi.add @last, a_rescue.body.type?\n\n          @rescue_block = old_rescue_block\n\n          # If the rescue restriction doesn't match, program flow falls through to the next\n          # iteration of the loop, i.e. the next rescue block (or ensure block if this is the last iteration)\n          position_at_end next_rescue_block\n          @catch_pad = saved_catch_pad\n        end\n      end\n\n      # Codegen the ensure block. We are currently inside the last `next_rescue_block`,\n      # and none of the `rescue` blocks have matched.\n\n      # We are generating the ensure block, so returns should no longer go via the ensure block,\n      # or it would execute twice (and segfault).\n      ensure_exception_handlers.pop\n\n      # We codegen the ensure block, unlike the rescue blocks, inside the catchpad block.\n      # This means we can re-raise efficiently, and is safe, since ensures cannot use return, next,\n      # break or any other construct to jump outside of the ensure block.\n      accept node_ensure if node_ensure\n\n      # ensure finished, re-raise the current exception.\n      codegen_re_raise(node, unwind_ex_obj)\n\n      # This is the block in which all exceptions raised in any `rescue` block end up.\n      # We want to run the ensure and re-raise.\n      if node_ensure && rescue_ensure_block\n        position_at_end rescue_ensure_block\n\n        # Codegen catchswitch+pad or landing pad as described above.\n        # This code is simpler because we never need to extract the exception type\n        if msvc\n          rescue_ensure_body = new_block \"rescue_ensure_body\"\n          catch_switch = builder.catch_switch(old_catch_pad || LLVM::Value.null, @rescue_block || LLVM::BasicBlock.null, 1)\n          builder.add_handler catch_switch, rescue_ensure_body\n\n          position_at_end rescue_ensure_body\n\n          @catch_pad = builder.catch_pad catch_switch, [void_ptr_type_descriptor, int32(0), llvm_context.void_pointer.null]\n        else\n          lp_ret_type = llvm_typer.landing_pad_type\n          lp = builder.landing_pad lp_ret_type, main_fun(personality_name).func, [] of LLVM::Value\n          unwind_ex_obj = extract_value lp, 0\n        end\n\n        # Codegen ensure, then make sure we re-raise the exception.\n        accept node_ensure\n        codegen_re_raise(node, unwind_ex_obj)\n      end\n\n      # We are no longer inside the catch pad\n      @catch_pad = old_catch_pad\n    end\n\n    # This is where codegen ends up if either no exception was raised or the exception was caught by a\n    # `rescue` and the rescue didn't raise. In this case, we need to run the ensure block if any.\n    # However, ensure blocks do not affect the return type of an exception handler, so we need to\n    # save and restore @last to preserve the correct return value.\n    old_last = @last\n    accept node_ensure if node_ensure\n    @last = old_last\n\n    false\n  end\n\n  def codegen_re_raise(node, unwind_ex_obj)\n    if @program.has_flag?(\"msvc\")\n      # On the MSVC C++ ABI we can re-raise by calling _CxxThrowException with two null arguments\n      call windows_throw_fun, [llvm_context.void_pointer.null, llvm_context.void_pointer.null]\n      unreachable\n    else\n      raise_fun = main_fun(RAISE_NAME)\n      raise_fun_arg_type = raise_fun.func.params.first.type # Void* or LibUnwind::Exception*\n      raise_fun_arg = pointer_cast(unwind_ex_obj.not_nil!, raise_fun_arg_type)\n      codegen_call_or_invoke(node, nil, nil, raise_fun, [raise_fun_arg], true, @program.no_return)\n    end\n  end\n\n  def execute_ensures_until(node)\n    stop_exception_handler = @node_ensure_exception_handlers[node]?.try &.node\n\n    @ensure_exception_handlers.try &.reverse_each do |exception_handler|\n      break if exception_handler.node.same?(stop_exception_handler)\n\n      target_ensure = exception_handler.node.ensure\n      next unless target_ensure\n\n      with_context(exception_handler.context) do\n        accept target_ensure\n      end\n    end\n  end\n\n  def set_ensure_exception_handler(node)\n    if eh = @ensure_exception_handlers.try &.last?\n      @node_ensure_exception_handlers[node] = eh\n    end\n  end\n\n  private def windows_throw_fun\n    fetch_typed_fun(@llvm_mod, \"_CxxThrowException\") do\n      LLVM::Type.function([@llvm_context.void_pointer, @llvm_context.void_pointer], @llvm_context.void, false)\n    end\n  end\n\n  private def windows_personality_fun\n    fetch_typed_fun(@llvm_mod, \"__CxxFrameHandler3\") do\n      LLVM::Type.function([] of LLVM::Type, @llvm_context.int32, true)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/experimental.cr",
    "content": "module Crystal\n  struct ExperimentalAnnotation\n    getter message : String?\n\n    def initialize(@message = nil)\n    end\n\n    def self.from(ann : Annotation)\n      args = ann.args\n      named_args = ann.named_args\n\n      if named_args\n        ann.raise \"too many named arguments (given #{named_args.size}, expected maximum 0)\"\n      end\n\n      message = nil\n      count = 0\n\n      args.each do |arg|\n        case count\n        when 0\n          arg.raise \"first argument must be a String\" unless arg.is_a?(StringLiteral)\n          message = arg.value\n        else\n          ann.wrong_number_of \"experimental annotation arguments\", args.size, \"1\"\n        end\n\n        count += 1\n      end\n\n      new(message)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/fun.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  def typed_fun?(mod : LLVM::Module, name : String) : LLVMTypedFunction?\n    if func = mod.functions[name]?\n      LLVMTypedFunction.new(@fun_types[{mod, name}], func)\n    end\n  end\n\n  def add_typed_fun(mod : LLVM::Module, name : String, type : LLVM::Type) : LLVMTypedFunction\n    func = mod.functions.add(name, type)\n    @fun_types[{mod, name}] = type\n    LLVMTypedFunction.new(type, func)\n  end\n\n  def fetch_typed_fun(mod : LLVM::Module, name : String, & : -> LLVM::Type) : LLVMTypedFunction\n    typed_fun?(mod, name) || add_typed_fun(mod, name, yield)\n  end\n\n  def target_def_fun(target_def, self_type) : LLVMTypedFunction\n    mangled_name = target_def.mangled_name(@program, self_type)\n    self_type_mod = type_module(self_type).mod\n\n    func = typed_fun?(self_type_mod, mangled_name) || codegen_fun(mangled_name, target_def, self_type)\n    check_mod_fun self_type_mod, mangled_name, func\n  end\n\n  def main_fun(name)\n    func = typed_fun?(@main_mod, name) || raise \"BUG: #{name} is not defined\"\n    check_main_fun name, func\n  end\n\n  def check_main_fun(name, func : LLVMTypedFunction) : LLVMTypedFunction\n    check_mod_fun @main_mod, name, func\n  end\n\n  def check_mod_fun(mod, name, func : LLVMTypedFunction) : LLVMTypedFunction\n    if @llvm_mod == mod\n      func\n    elsif existing_func = @llvm_mod.functions[name]?\n      LLVMTypedFunction.new(@fun_types[{@llvm_mod, name}], existing_func)\n    else\n      declare_fun(name, func)\n    end\n  end\n\n  def declare_fun(mangled_name, func : LLVMTypedFunction) : LLVMTypedFunction\n    type = @llvm_typer.copy_type(func.type)\n    typed_fun = add_typed_fun(@llvm_mod, mangled_name, type)\n\n    source_fun = func.func\n    new_fun = typed_fun.func\n    source_fun.params.each_index do |index|\n      attrs = source_fun.attributes(index + 1)\n      if attrs.value != 0\n        param_type = new_fun.params[index].type\n        new_fun.add_attribute(attrs, index + 1, param_type)\n      end\n    end\n\n    typed_fun\n  end\n\n  def codegen_fun(mangled_name, target_def, self_type, is_exported_fun = false, fun_module_info = type_module(self_type), is_fun_literal = false, is_closure = false)\n    old_position = insert_block\n    old_entry_block = @entry_block\n    old_alloca_block = @alloca_block\n    old_ensure_exception_handlers = @ensure_exception_handlers\n    old_rescue_block = @rescue_block\n    old_catch_pad = @catch_pad\n    old_llvm_mod = @llvm_mod\n    old_llvm_context = @llvm_context\n    old_llvm_typer = @llvm_typer\n    old_builder = self.builder\n    old_debug_location = @current_debug_location\n    old_fun = context.fun\n\n    old_needs_value = @needs_value\n\n    with_cloned_context do |old_context|\n      context.type = self_type\n      context.vars = LLVMVars.new\n      context.block_context = nil\n\n      @llvm_mod = fun_module_info.mod\n      @llvm_context = @llvm_mod.context\n      @llvm_typer = fun_module_info.typer\n      @builder = fun_module_info.builder\n\n      @ensure_exception_handlers = nil\n      @rescue_block = nil\n      @catch_pad = nil\n      @needs_value = true\n\n      args = codegen_fun_signature(mangled_name, target_def, self_type, is_fun_literal, is_closure)\n\n      needs_body = !target_def.is_a?(External) || is_exported_fun\n      if needs_body\n        emit_def_debug_metadata target_def unless @debug.none?\n        set_current_debug_location target_def if @debug.line_numbers?\n\n        {% if LibLLVM::IS_LT_150 %}\n          context.fun.add_attribute LLVM::Attribute::UWTable\n        {% else %}\n          context.fun.add_attribute LLVM::Attribute::UWTable, value: @program.has_flag?(\"aarch64\") ? LLVM::UWTableKind::Sync : LLVM::UWTableKind::Async\n        {% end %}\n\n        if @frame_pointers.always?\n          context.fun.add_attribute \"frame-pointer\", value: \"all\"\n        elsif @program.has_flag?(\"darwin\") || @program.has_flag?(\"solaris\")\n          # Disable frame pointer elimination, as it causes issues during stack unwind\n          context.fun.add_target_dependent_attribute \"frame-pointer\", \"all\"\n        elsif @frame_pointers.non_leaf?\n          context.fun.add_attribute \"frame-pointer\", value: \"non-leaf\"\n        end\n\n        new_entry_block\n\n        if is_closure\n          clear_current_debug_location if @debug.line_numbers?\n          void_ptr = context.fun.params.first\n          closure_type = llvm_typer.copy_type(context.closure_type.not_nil!)\n          closure_ptr = pointer_cast void_ptr, closure_type.pointer\n          setup_closure_vars target_def, context.closure_vars.not_nil!, self.context, closure_type, closure_ptr\n        else\n          context.reset_closure\n        end\n\n        if !is_fun_literal && self_type.passed_as_self?\n          context.vars[\"self\"] = LLVMVar.new(context.fun.params.first, self_type, true)\n        end\n\n        if is_closure\n          # In the case of a closure proc literal (-> { ... }), the closure_ptr is not\n          # the one of the parent context, it's the last parameter of this proc literal.\n          closure_parent_context = old_context.clone\n          closure_parent_context.closure_ptr = closure_ptr.not_nil!\n          context.closure_parent_context = closure_parent_context\n        end\n\n        set_current_debug_location target_def if @debug.line_numbers?\n        alloca_vars target_def.vars, target_def, args, context.closure_parent_context\n\n        create_local_copy_of_fun_args(target_def, self_type, args, is_fun_literal, is_closure)\n\n        if @debug.variables? && !target_def.naked?\n          in_alloca_block do\n            location = target_def.location\n            context.vars.each do |name, var|\n              next if var.debug_variable_created\n\n              # Self always comes as the first parameter, unless it's a closure:\n              # then it will be fetched from the closure data.\n              if name == \"self\" && !is_closure\n                declare_debug_for_function_argument(name, var.type, 1, var.pointer, location)\n                # Method debug parameters are skipped as they were defined in create_local_copy_of_fun_args()\n                # due to LLVM variable dominance issue in some closure cases\n              elsif args.none? { |arg| arg.name == name }\n                declare_variable(name, var.type, var.pointer, location, alloca_block)\n              end\n            end\n          end\n        end\n\n        context.return_type = target_def.type?\n        context.return_phi = nil\n\n        body = target_def.body\n\n        # If the body is a primitive it means this is a function\n        # that was generated by taking a pointer to it, for example\n        # `a = 1; ->a.+(Int32)`.\n        # In that case we need to inline the primitive code (as usual).\n        if body.is_a?(Primitive)\n          primitive_params = context.fun.params.to_a\n\n          # If self is a primitive type it's actually passed as a pointer\n          # (because the closure data is a pointer) and so we must load the\n          # real value first.\n          if args.first.type.is_a?(PrimitiveType)\n            primitive_params[0] = load(llvm_type(args.first.type), primitive_params[0])\n          end\n\n          codegen_primitive(nil, body, target_def, primitive_params)\n        else\n          accept target_def.body\n        end\n\n        codegen_return(target_def)\n\n        br_from_alloca_to_entry\n      end\n\n      @last = llvm_nil\n\n      @llvm_mod = old_llvm_mod\n      @llvm_context = old_llvm_context\n      @llvm_typer = old_llvm_typer\n      @builder = old_builder\n      position_at_end old_position\n\n      @ensure_exception_handlers = old_ensure_exception_handlers\n      @rescue_block = old_rescue_block\n      @catch_pad = old_catch_pad\n      @entry_block = old_entry_block\n      @alloca_block = old_alloca_block\n      @needs_value = old_needs_value\n\n      if @debug.line_numbers?\n        # set_current_debug_location associates a scope from the current fun,\n        # and at this point the current one should be the old one before\n        # defining the fun. We do that. We also clear the debug location\n        # if there was none before.\n        if old_debug_location\n          new_fun = context.fun\n          context.fun = old_fun\n          set_current_debug_location(old_debug_location)\n          context.fun = new_fun\n        else\n          clear_current_debug_location\n        end\n      end\n\n      if @program.has_flag?(\"wasm32\")\n        if target_def.is_a?(External) && (wasm_import_module = target_def.wasm_import_module)\n          context.fun.add_target_dependent_attribute(\"wasm-import-name\", target_def.real_name)\n          context.fun.add_target_dependent_attribute(\"wasm-import-module\", wasm_import_module)\n        end\n\n        if is_exported_fun\n          context.fun.add_target_dependent_attribute(\"wasm-export-name\", mangled_name)\n        end\n      end\n\n      LLVMTypedFunction.new(context.fun_type, context.fun)\n    end\n  end\n\n  def codegen_return(target_def : Def)\n    # Check if this def must use the C calling convention and the return\n    # value must be either casted or passed by sret\n    if target_def.c_calling_convention? && target_def.abi_info?\n      return_type = target_def.body.type\n      if return_type.proc?\n        @last = check_proc_is_not_closure(@last, return_type)\n      end\n\n      abi_info = abi_info(target_def)\n      abi_ret_type = abi_info.return_type\n      if cast = abi_ret_type.cast\n        casted_last = pointer_cast @last, cast.pointer\n        last = load cast, casted_last\n        ret last\n        return\n      end\n\n      if (attr = abi_ret_type.attr) && attr == LLVM::Attribute::StructRet\n        store load(llvm_type(return_type), @last), context.fun.params[0]\n        ret\n        return\n      end\n    end\n\n    codegen_return target_def.body.type?\n  end\n\n  def codegen_fun_signature(mangled_name, target_def, self_type, is_fun_literal, is_closure)\n    if !is_closure && (external = target_def.c_calling_convention?)\n      codegen_fun_signature_external(mangled_name, external)\n    else\n      codegen_fun_signature_non_external(mangled_name, target_def, self_type, is_fun_literal, is_closure)\n    end\n  end\n\n  def codegen_fun_signature_non_external(mangled_name, target_def, self_type, is_fun_literal, is_closure)\n    is_primitive = target_def.body.is_a?(Primitive)\n\n    args = Array(Arg).new(target_def.args.size + 1)\n\n    if !is_fun_literal && self_type.passed_as_self?\n      args.push Arg.new(\"self\", type: self_type)\n    end\n\n    args.concat target_def.args\n\n    if target_def.uses_block_arg?\n      block_arg = target_def.block_arg.not_nil!\n      args.push Arg.new(block_arg.name, type: block_arg.type)\n    end\n\n    target_def.special_vars.try &.each do |special_var_name|\n      args.push Arg.new(special_var_name, type: target_def.vars.not_nil![special_var_name].type)\n    end\n\n    llvm_args_types = args.map_with_index do |arg, i|\n      arg_type = arg.type\n      if arg_type.void?\n        llvm_arg_type = llvm_context.int8\n      else\n        llvm_arg_type = llvm_type(arg_type)\n\n        # We need an extra pointer for special vars (they always have an extra pointer)\n        if arg.special_var?\n          llvm_arg_type = llvm_arg_type.pointer\n        end\n\n        # Self is always passed by reference (pointer),\n        # even if the type is passed by value (like a struct).\n        # In case the body is a Primitive it means it's something like\n        # `a = 1; ->a.+(Int32)` in which case we must use a pointer for\n        # the first argument (because the closure data is a pointer).\n        if i == 0 && !is_fun_literal && self_type.passed_as_self? && (self_type.passed_by_value? || (is_primitive && arg_type.is_a?(PrimitiveType)))\n          llvm_arg_type = llvm_arg_type.pointer\n        end\n      end\n      llvm_arg_type\n    end\n\n    llvm_return_type = {% if LibLLVM::IS_LT_150 %}\n                         llvm_return_type(target_def.type)\n                       {% else %}\n                         target_def.llvm_intrinsic? ? llvm_intrinsic_return_type(target_def.type) : llvm_return_type(target_def.type)\n                       {% end %}\n\n    if is_closure\n      llvm_args_types.insert(0, llvm_context.void_pointer)\n      offset = 1\n    else\n      offset = 0\n    end\n\n    setup_context_fun(mangled_name, target_def, llvm_args_types, llvm_return_type)\n\n    if @debug.variables?\n      context.fun_debug_params.clear\n      if context.fun\n        context.add_fun_debug_param(get_debug_type(target_def.type))\n        args.each do |arg|\n          context.add_fun_debug_param(get_debug_type(arg.type))\n        end\n      end\n    end\n\n    if @single_module && !target_def.is_a?(External)\n      context.fun.linkage = LLVM::Linkage::Internal\n    end\n\n    args.each_with_index do |arg, i|\n      param = context.fun.params[i + offset]\n      param.name = arg.name\n    end\n\n    args\n  end\n\n  def codegen_fun_signature_external(mangled_name, target_def)\n    args = target_def.args.dup\n\n    # This is the case where we declared a fun that was not used and now we\n    # are defining its body.\n    if existing_fun = typed_fun?(@llvm_mod, mangled_name)\n      context.fun = existing_fun.func\n      context.fun_type = existing_fun.type\n      return args\n    end\n\n    offset = 0\n\n    abi_info = abi_info(target_def)\n\n    llvm_args_types = Array(LLVM::Type).new(abi_info.arg_types.size)\n    abi_info.arg_types.each do |arg_type|\n      case arg_type.kind\n      in .direct?\n        llvm_args_types << (arg_type.cast || arg_type.type)\n      in .indirect?\n        llvm_args_types << arg_type.type.pointer\n      in .ignore?\n        # ignore\n      end\n    end\n\n    ret_type = abi_info.return_type\n    llvm_return_type =\n      case ret_type.kind\n      in .direct?\n        ret_type.cast || ret_type.type\n      in .indirect?\n        offset += 1\n        llvm_args_types.insert 0, ret_type.type.pointer\n        llvm_context.void\n      in .ignore?\n        llvm_context.void\n      end\n\n    setup_context_fun(mangled_name, target_def, llvm_args_types, llvm_return_type)\n\n    if call_convention = target_def.call_convention\n      context.fun.call_convention = call_convention\n    end\n\n    if @single_module && mangled_name.starts_with?(\"__crystal_\")\n      # FIXME: macos ld fails to link when the personality fun is internal; it\n      # might work with lld so we might want to check the linker?\n      unless @program.has_flag?(\"darwin\") && mangled_name.starts_with?(\"__crystal_personality\")\n        context.fun.linkage = LLVM::Linkage::Internal\n      end\n    end\n\n    i = 0\n    args.each do |arg|\n      param = context.fun.params[i + offset]\n      param.name = arg.name\n\n      abi_arg_type = abi_info.arg_types[i]\n\n      if attr = abi_arg_type.attr\n        context.fun.add_attribute(attr, i + offset + 1, abi_arg_type.type)\n      end\n\n      i += 1 unless abi_arg_type.kind == ABI::ArgKind::Ignore\n    end\n\n    # This is for sret\n    if (attr = abi_info.return_type.attr) && attr == LLVM::Attribute::StructRet\n      context.fun.add_attribute(attr, 1, abi_info.return_type.type)\n    end\n\n    args\n  end\n\n  def abi_info(external : Def)\n    external.abi_info = true\n\n    llvm_args_types = external.args.map { |arg| llvm_c_type(arg.type) }\n    llvm_return_type = llvm_c_return_type(external.type)\n    @abi.abi_info(llvm_args_types, llvm_return_type, !llvm_return_type.void?, llvm_context)\n  end\n\n  def abi_info(external : Def, node : Call)\n    llvm_args_types = node.args.map_with_index do |arg, i|\n      llvm_c_type((external.args[i]? || arg).type)\n    end\n    llvm_return_type = llvm_c_return_type(external.type)\n    @abi.abi_info(llvm_args_types, llvm_return_type, !llvm_return_type.void?, llvm_context)\n  end\n\n  def setup_context_fun(mangled_name, target_def, llvm_args_types, llvm_return_type) : Nil\n    fun_type = LLVM::Type.function(llvm_args_types, llvm_return_type, target_def.varargs?)\n    typed_fun = add_typed_fun(@llvm_mod, mangled_name, fun_type)\n    context.fun = typed_fun.func\n    context.fun_type = typed_fun.type\n\n    if @debug.variables?\n      context.fun.add_attribute LLVM::Attribute::NoInline\n      context.fun.add_attribute LLVM::Attribute::OptimizeNone\n    else\n      context.fun.add_attribute LLVM::Attribute::AlwaysInline if target_def.always_inline?\n    end\n    context.fun.add_attribute LLVM::Attribute::ReturnsTwice if target_def.returns_twice?\n    context.fun.add_attribute LLVM::Attribute::Naked if target_def.naked?\n    context.fun.add_attribute LLVM::Attribute::NoReturn if target_def.no_returns?\n    context.fun.add_attribute LLVM::Attribute::NoInline if target_def.no_inline?\n  end\n\n  def setup_closure_vars(target_def, closure_vars, context, closure_type, closure_ptr)\n    if context.closure_skip_parent\n      parent_context = context.closure_parent_context.not_nil!\n      setup_closure_vars(target_def, parent_context.closure_vars.not_nil!, parent_context, closure_type, closure_ptr)\n    else\n      def_vars = target_def.vars\n      closure_vars.each_with_index do |var, i|\n        # A closured var in this context might have the same name as\n        # a local var in another context, for example if the local var\n        # was defined before the closured var. In this case, don't\n        # consider the local var as closured.\n        def_var = def_vars.try &.[var.name]?\n        next if def_var && !def_var.closured?\n\n        if context.fun.naked?\n          debug_variable_created = false\n        else\n          var_offset = llvm_typer.offset_of(closure_type, i)\n          debug_variable_created = declare_variable(var.name, var.type, closure_ptr, target_def.location, offset: var_offset)\n        end\n        var_ptr = gep(closure_type, closure_ptr, 0, i, var.name)\n        self.context.vars[var.name] = LLVMVar.new(var_ptr, var.type, debug_variable_created: debug_variable_created)\n      end\n\n      if (closure_parent_context = context.closure_parent_context) &&\n         (parent_vars = closure_parent_context.closure_vars)\n        parent_closure_type = llvm_typer.copy_type(closure_parent_context.closure_type.not_nil!)\n        parent_closure_ptr = gep(closure_type, closure_ptr, 0, closure_vars.size, \"parent_ptr\")\n        parent_closure = load(parent_closure_type.pointer, parent_closure_ptr, \"parent\")\n        setup_closure_vars(target_def, parent_vars, closure_parent_context, parent_closure_type, parent_closure)\n      elsif closure_self = context.closure_self\n        self_index = closure_vars.size + (context.closure_parent_context ? 1 : 0)\n        if context.fun.naked?\n          debug_variable_created = false\n        else\n          self_offset = llvm_typer.offset_of(closure_type, self_index)\n          debug_variable_created = declare_variable(\"self\", closure_self, closure_ptr, target_def.location, offset: self_offset)\n        end\n        self_value = gep(closure_type, closure_ptr, 0, self_index, \"self\")\n        self_value = load(llvm_type(closure_self), self_value) unless closure_self.passed_by_value?\n        self.context.vars[\"self\"] = LLVMVar.new(self_value, closure_self, true, debug_variable_created: debug_variable_created)\n      end\n    end\n  end\n\n  def create_local_copy_of_fun_args(target_def, self_type, args, is_fun_literal, is_closure)\n    offset = is_closure ? 1 : 0\n\n    abi_info = target_def.abi_info? ? abi_info(target_def) : nil\n    sret = abi_info && sret?(abi_info)\n    offset += 1 if sret\n\n    target_def_vars = target_def.vars\n    args.each_with_index do |arg, i|\n      param = context.fun.params[i + offset]\n      if !is_fun_literal && (i == 0 && self_type.passed_as_self?)\n        # here self is already in context.vars\n      else\n        create_local_copy_of_arg(target_def, target_def_vars, arg, param, i + offset)\n      end\n    end\n  end\n\n  def create_local_copy_of_block_self(self_type, call_args)\n    args_base_index = 0\n    if self_type.passed_as_self?\n      context.vars[\"self\"] = LLVMVar.new(call_args[0], self_type, true)\n      args_base_index = 1\n    end\n    args_base_index\n  end\n\n  def create_local_copy_of_block_args(target_def, self_type, call_args, args_base_index)\n    target_def.args.each_with_index do |arg, i|\n      create_local_copy_of_arg(target_def, target_def.vars, arg, call_args[args_base_index + i], args_base_index + i)\n    end\n  end\n\n  def create_local_copy_of_arg(target_def, target_def_vars, arg, value, index)\n    # An argument name can be \"_\" in the case of a captured block,\n    # and we must ignore these\n    return if arg.name == \"_\"\n\n    target_def_var = target_def_vars.try &.[arg.name]\n    location = target_def_var.try(&.location) || target_def.location\n\n    var_type = (target_def_var || arg).type\n    return if var_type.void?\n\n    if closure_var = context.vars[arg.name]?\n      pointer = closure_var.pointer\n\n      if arg.type.passed_by_value?\n        # Create an alloca and store it there, so assign works well\n        pointer2 = alloca(llvm_type(arg.type))\n        store value, pointer2\n        value = pointer2\n      end\n    else\n      abi_info = target_def.abi_info? ? abi_info(target_def) : nil\n      sret = abi_info && sret?(abi_info)\n\n      # If it's an extern struct on a def that must be codegened with C ABI\n      # compatibility, and it's not passed byval, we must cast the value\n      if target_def.c_calling_convention? && arg.type.extern? && !context.fun.attributes(index + 1).by_val?\n        # ... unless it's passed indirectly (ie. as a pointer to memory allocated by the caller)\n        if abi_info.try &.arg_types[index - (sret ? 1 : 0)].kind.indirect?\n          value = declare_debug_for_function_argument(arg.name, var_type, index + 1, value, location) unless target_def.naked?\n          context.vars[arg.name] = LLVMVar.new(value, var_type)\n        else\n          pointer = alloca(llvm_type(var_type), arg.name)\n          casted_pointer = pointer_cast pointer, value.type.pointer\n          store value, casted_pointer\n          pointer = declare_debug_for_function_argument(arg.name, var_type, index + 1, pointer, location) unless target_def.naked?\n          context.vars[arg.name] = LLVMVar.new(pointer, var_type)\n        end\n        return\n      elsif arg.special_var?\n        value = declare_debug_for_function_argument(arg.name, var_type, index + 1, value, location) unless target_def.naked?\n        context.vars[arg.name] = LLVMVar.new(value, var_type)\n        return\n      else\n        # If an argument is a Proc inside a C function, we need to cast it to Proc\n        fun_proc = var_type.is_a?(ProcInstanceType) && target_def.is_a?(External)\n\n        # We don't need to create a copy of the argument if it's never\n        # assigned a value inside the function.\n        needs_copy = target_def_var.try &.assigned_to?\n        needs_copy ||= fun_proc\n\n        if needs_copy\n          pointer = alloca(llvm_type(var_type), arg.name)\n          pointer = declare_debug_for_function_argument(arg.name, var_type, index + 1, pointer, location) unless target_def.naked?\n\n          if fun_proc\n            fun_ptr = cast_to_void_pointer(value)\n            value = make_fun(var_type, fun_ptr, llvm_context.void_pointer.null)\n          end\n\n          context.vars[arg.name] = LLVMVar.new(pointer, var_type)\n\n          if arg.type.passed_by_value? && !context.fun.attributes(index + 1).by_val?\n            # Create an alloca and store it there, so assign works well\n            pointer2 = alloca(llvm_type(arg.type))\n            store value, pointer2\n            value = pointer2\n          end\n        else\n          if arg.type.passed_by_value? && !context.fun.attributes(index + 1).by_val?\n            # For pass-by-value we create an alloca so the value\n            # is behind a pointer, as everywhere else\n            pointer = alloca(llvm_type(var_type), arg.name)\n            store value, pointer\n            pointer = declare_debug_for_function_argument(arg.name, var_type, index + 1, pointer, location) unless target_def.naked?\n            context.vars[arg.name] = LLVMVar.new(pointer, var_type)\n            return\n          else\n            value = declare_debug_for_function_argument(arg.name, var_type, index + 1, value, location) unless target_def.naked?\n            context.vars[arg.name] = LLVMVar.new(value, var_type, true)\n            return\n          end\n        end\n      end\n    end\n\n    assign pointer, var_type, arg.type, value\n  end\n\n  def type_module(type)\n    return @main_module_info if @single_module\n\n    @types_to_modules[type] ||= begin\n      type = type.remove_typedef\n      case type\n      when Nil, Program, LibType\n        type_name = \"\"\n      else\n        type_name = type.instance_type.to_s\n      end\n\n      @modules[type_name] ||= begin\n        llvm_context = LLVM::Context.new\n        # LLVM::Context.register(llvm_context, type_name)\n\n        llvm_typer = LLVMTyper.new(@program, llvm_context)\n        llvm_mod = configure_module(llvm_context.new_module(type_name))\n        llvm_builder = new_builder(llvm_context)\n\n        ModuleInfo.new(llvm_mod, llvm_typer, llvm_builder)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/link.cr",
    "content": "{% if flag?(:msvc) %}\n  require \"crystal/system/win32/visual_studio\"\n  require \"crystal/system/win32/windows_sdk\"\n{% end %}\n\nmodule Crystal\n  struct LinkAnnotation\n    getter lib : String?\n    getter pkg_config : String?\n    getter ldflags : String?\n    getter framework : String?\n    getter wasm_import_module : String?\n    getter dll : String?\n\n    def initialize(@lib = nil, @pkg_config = @lib, @ldflags = nil, @static = false, @framework = nil, @wasm_import_module = nil, @dll = nil)\n    end\n\n    def static?\n      @static\n    end\n\n    def self.from(ann : Annotation)\n      args = ann.args\n      named_args = ann.named_args\n\n      if args.empty? && !named_args\n        ann.raise \"missing link arguments: must at least specify a library name\"\n      end\n\n      lib_name = nil\n      lib_ldflags = nil\n      lib_static = false\n      lib_pkg_config = nil\n      lib_framework = nil\n      lib_wasm_import_module = nil\n      lib_dll = nil\n      count = 0\n\n      args.each do |arg|\n        case count\n        when 0\n          arg.raise \"'lib' link argument must be a String\" unless arg.is_a?(StringLiteral)\n          lib_name = arg.value\n        when 1\n          arg.raise \"'ldflags' link argument must be a String\" unless arg.is_a?(StringLiteral)\n          lib_ldflags = arg.value\n        when 2\n          arg.raise \"'static' link argument must be a Bool\" unless arg.is_a?(BoolLiteral)\n          lib_static = arg.value\n        when 3\n          arg.raise \"'framework' link argument must be a String\" unless arg.is_a?(StringLiteral)\n          lib_framework = arg.value\n        else\n          ann.wrong_number_of \"link arguments\", args.size, \"1..4\"\n        end\n\n        count += 1\n      end\n\n      named_args.try &.each do |named_arg|\n        value = named_arg.value\n\n        case named_arg.name\n        when \"lib\"\n          named_arg.raise \"'lib' link argument already specified\" if count > 0\n          named_arg.raise \"'lib' link argument must be a String\" unless value.is_a?(StringLiteral)\n          lib_name = value.value\n        when \"ldflags\"\n          named_arg.raise \"'ldflags' link argument already specified\" if count > 1\n          named_arg.raise \"'ldflags' link argument must be a String\" unless value.is_a?(StringLiteral)\n          lib_ldflags = value.value\n        when \"static\"\n          named_arg.raise \"'static' link argument already specified\" if count > 2\n          named_arg.raise \"'static' link argument must be a Bool\" unless value.is_a?(BoolLiteral)\n          lib_static = value.value\n        when \"framework\"\n          named_arg.raise \"'framework' link argument already specified\" if count > 3\n          named_arg.raise \"'framework' link argument must be a String\" unless value.is_a?(StringLiteral)\n          lib_framework = value.value\n        when \"pkg_config\"\n          named_arg.raise \"'pkg_config' link argument must be a String\" unless value.is_a?(StringLiteral)\n          lib_pkg_config = value.value\n        when \"wasm_import_module\"\n          named_arg.raise \"'wasm_import_module' link argument must be a String\" unless value.is_a?(StringLiteral)\n          lib_wasm_import_module = value.value\n        when \"dll\"\n          named_arg.raise \"'dll' link argument must be a String\" unless value.is_a?(StringLiteral)\n          lib_dll = value.value\n          unless lib_dll.size >= 4 && lib_dll[-4..].compare(\".dll\", case_insensitive: true) == 0\n            named_arg.raise \"'dll' link argument must use a '.dll' file extension\"\n          end\n          if ::Path.separators(::Path::Kind::WINDOWS).any? { |separator| lib_dll.includes?(separator) }\n            named_arg.raise \"'dll' link argument must not include directory separators\"\n          end\n        else\n          named_arg.raise \"unknown link argument: '#{named_arg.name}' (valid arguments are 'lib', 'ldflags', 'static', 'pkg_config', 'framework', 'wasm_import_module', and 'dll')\"\n        end\n      end\n\n      new(lib_name, lib_pkg_config, lib_ldflags, lib_static, lib_framework, lib_wasm_import_module, lib_dll)\n    end\n  end\n\n  module CrystalLibraryPath\n    def self.default_paths : Array(String)\n      paths = ENV.fetch(\"CRYSTAL_LIBRARY_PATH\", Crystal::Config.library_path).split(Process::PATH_DELIMITER, remove_empty: true)\n\n      CrystalPath.expand_paths(paths)\n\n      paths\n    end\n\n    def self.default_path : String\n      default_paths.join(Process::PATH_DELIMITER)\n    end\n\n    class_getter paths : Array(String) do\n      default_paths\n    end\n  end\n\n  class Program\n    def lib_flags(cross_compiling : Bool = false)\n      has_flag?(\"msvc\") ? lib_flags_windows(cross_compiling) : lib_flags_posix(cross_compiling)\n    end\n\n    private def lib_flags_windows(cross_compiling)\n      flags = [] of String\n\n      # Add CRYSTAL_LIBRARY_PATH locations, so the linker preferentially\n      # searches user-given library paths.\n      if has_flag?(\"msvc\")\n        CrystalLibraryPath.paths.each do |path|\n          flags << quote_flag(\"/LIBPATH:#{path}\", cross_compiling)\n        end\n      end\n\n      link_annotations.reverse_each do |ann|\n        if ldflags = ann.ldflags\n          flags << ldflags\n        end\n\n        if libname = ann.lib\n          flags << quote_flag(\"#{libname}.lib\", cross_compiling)\n        end\n      end\n\n      flags.join(\" \")\n    end\n\n    private def lib_flags_posix(cross_compiling)\n      flags = [] of String\n      static_build = has_flag?(\"static\")\n\n      # Instruct the linker to link statically if the user asks\n      flags << \"-static\" if static_build\n\n      # Add CRYSTAL_LIBRARY_PATH locations, so the linker preferentially\n      # searches user-given library paths.\n      CrystalLibraryPath.paths.each do |path|\n        flags << quote_flag(\"-L#{path}\", cross_compiling)\n      end\n\n      link_annotations.reverse_each do |ann|\n        if ldflags = ann.ldflags\n          flags << ldflags\n        end\n\n        # First, check pkg-config for the pkg-config module name if provided, then\n        # check pkg-config with the lib name, then fall back to -lname\n        if (pkg_config_name = ann.pkg_config) && (flag = pkg_config(pkg_config_name, static_build))\n          flags << flag\n        elsif (lib_name = ann.lib) && (flag = pkg_config(lib_name, static_build))\n          flags << flag\n        elsif (lib_name = ann.lib)\n          flags << quote_flag(\"-l#{lib_name}\", cross_compiling)\n        end\n\n        if framework = ann.framework\n          flags << \"-framework\" << quote_flag(framework, cross_compiling)\n        end\n      end\n\n      flags.join(\" \")\n    end\n\n    private def quote_flag(flag, cross_compiling)\n      if cross_compiling\n        has_flag?(\"windows\") ? Process.quote_windows(flag) : Process.quote_posix(flag)\n      else\n        Process.quote(flag)\n      end\n    end\n\n    # Searches among CRYSTAL_LIBRARY_PATH, the compiler's directory, and PATH\n    # for every DLL specified in the used `@[Link]` annotations. Yields the\n    # absolute path and `true` if found, the base name and `false` if not found.\n    # The directories should match `Crystal::Repl::Context#dll_search_paths`\n    def each_dll_path(& : String, Bool ->)\n      executable_path = nil\n      compiler_origin = nil\n      paths = nil\n\n      link_annotations.each do |ann|\n        next unless dll = ann.dll\n\n        dll_path = CrystalLibraryPath.paths.each do |path|\n          full_path = File.join(path, dll)\n          break full_path if File.file?(full_path)\n        end\n\n        unless dll_path\n          executable_path ||= Process.executable_path\n          compiler_origin ||= File.dirname(executable_path) if executable_path\n\n          if compiler_origin\n            full_path = File.join(compiler_origin, dll)\n            dll_path = full_path if File.file?(full_path)\n          end\n        end\n\n        unless dll_path\n          paths ||= ENV[\"PATH\"]?.try &.split(Process::PATH_DELIMITER, remove_empty: true)\n\n          dll_path = paths.try &.each do |path|\n            full_path = File.join(path, dll)\n            break full_path if File.file?(full_path)\n          end\n        end\n\n        yield dll_path || dll, !dll_path.nil?\n      end\n    end\n\n    # Detects the current MSVC linker and the relevant linker flags that\n    # recreate the MSVC developer prompt's standard library paths. If both MSVC\n    # and the Windows SDK are available, the linker will be an absolute path and\n    # the linker flags will contain the `/LIBPATH`s for the system libraries.\n    #\n    # Has no effect if the host compiler is not using MSVC.\n    def msvc_compiler_and_flags : {String, Array(String)}\n      linker = Compiler::MSVC_LINKER\n      link_args = [] of String\n\n      {% if flag?(:msvc) %}\n        if msvc_path = Crystal::System::VisualStudio.find_latest_msvc_path\n          if win_sdk_libpath = Crystal::System::WindowsSDK.find_win10_sdk_libpath\n            host_bits = {{ flag?(:aarch64) ? \"ARM64\" : flag?(:bits64) ? \"x64\" : \"x86\" }}\n            target_bits = has_flag?(\"aarch64\") ? \"arm64\" : has_flag?(\"bits64\") ? \"x64\" : \"x86\"\n\n            # MSVC build tools and Windows SDK found; recreate `LIB` environment variable\n            # that is normally expected on the MSVC developer command prompt\n            link_args << \"/LIBPATH:#{msvc_path.join(\"atlmfc\", \"lib\", target_bits)}\"\n            link_args << \"/LIBPATH:#{msvc_path.join(\"lib\", target_bits)}\"\n            link_args << \"/LIBPATH:#{win_sdk_libpath.join(\"ucrt\", target_bits)}\"\n            link_args << \"/LIBPATH:#{win_sdk_libpath.join(\"um\", target_bits)}\"\n\n            # use exact path for compiler instead of relying on `PATH`, unless\n            # explicitly overridden by `%CC%`\n            # (letter case shouldn't matter in most cases but being exact doesn't hurt here)\n            unless ENV.has_key?(\"CC\")\n              target_bits = target_bits.sub(\"arm\", \"ARM\")\n              linker = msvc_path.join(\"bin\", \"Host#{host_bits}\", target_bits, \"cl.exe\").to_s\n            end\n          end\n        end\n      {% end %}\n\n      {linker, link_args}\n    end\n\n    PKG_CONFIG_PATH = Process.find_executable(\"pkg-config\")\n\n    # Returns the result of running `pkg-config mod` but returns nil if\n    # pkg-config is not installed, or the module does not exist.\n    private def pkg_config(mod, static = false) : String?\n      return unless pkg_config_path = PKG_CONFIG_PATH\n      return unless (Process.run(pkg_config_path, {mod}).success? rescue nil)\n\n      args = [\"--libs\"]\n      args << \"--static\" if static\n      args << mod\n\n      process = Process.new(pkg_config_path, args, input: :close, output: :pipe, error: :inherit)\n      flags = process.output.gets_to_end.chomp\n      status = process.wait\n      if status.success?\n        flags\n      else\n        nil\n      end\n    end\n\n    # Returns every @[Link] annotation in the program parsed as `LinkAnnotation`\n    def link_annotations\n      annotations = [] of LinkAnnotation\n      add_link_annotations @types, annotations\n      annotations\n    end\n\n    private def add_link_annotations(types, annotations)\n      types.try &.each_value do |type|\n        if type.is_a?(LibType) && type.used? && (link_annotations = type.link_annotations)\n          annotations.concat link_annotations\n        end\n\n        add_link_annotations type.types?, annotations\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/llvm_builder_helper.cr",
    "content": "module Crystal\n  record LLVMTypedFunction, type : LLVM::Type, func : LLVM::Function\n\n  module LLVMBuilderHelper\n    def int1(n)\n      llvm_context.int1.const_int(n)\n    end\n\n    def int8(n)\n      llvm_context.int8.const_int(n)\n    end\n\n    def int16(n)\n      llvm_context.int16.const_int(n)\n    end\n\n    def int32(n)\n      llvm_context.int32.const_int(n)\n    end\n\n    def int64(n)\n      llvm_context.int64.const_int(n)\n    end\n\n    def int128(n)\n      llvm_context.int128.const_int(n)\n    end\n\n    def int(n)\n      int32(n)\n    end\n\n    def size_t\n      llvm_context.int(@program.size_bit_width)\n    end\n\n    def size_t(n)\n      size_t.const_int(n)\n    end\n\n    def size_t(value : LLVM::Value)\n      case value.type.int_width <=> @program.size_bit_width\n      when .zero?\n        value\n      when .positive?\n        builder.trunc(value, size_t)\n      else\n        builder.zext(value, size_t)\n      end\n    end\n\n    def int(n, type)\n      llvm_type(type).const_int(n)\n    end\n\n    def float32(value)\n      llvm_context.float.const_float(value)\n    end\n\n    def float64(value)\n      llvm_context.double.const_double(value)\n    end\n\n    def float(value, type)\n      case type.kind\n      when .f32?\n        float32(value.to_f32)\n      when .f64?\n        float64(value.to_f64)\n      else\n        raise \"Unsupported float type\"\n      end\n    end\n\n    def llvm_nil\n      llvm_typer.nil_value\n    end\n\n    def llvm_false\n      int1(0)\n    end\n\n    def llvm_true\n      int1(1)\n    end\n\n    def equal?(value1, value2)\n      builder.icmp LLVM::IntPredicate::EQ, value1, value2\n    end\n\n    def not_equal?(value1, value2)\n      builder.icmp LLVM::IntPredicate::NE, value1, value2\n    end\n\n    def null_pointer?(value)\n      builder.icmp LLVM::IntPredicate::EQ, value, value.type.null\n    end\n\n    def not_null_pointer?(value)\n      builder.icmp LLVM::IntPredicate::NE, value, value.type.null\n    end\n\n    def gep(ptr : LLVM::Value, index0 : Int32, name = \"\")\n      gep ptr, int32(index0), name\n    end\n\n    def gep(ptr : LLVM::Value, index0 : LLVM::Value, name = \"\")\n      builder.inbounds_gep ptr, index0, name\n    end\n\n    def gep(ptr : LLVM::Value, index0 : Int32, index1 : Int32, name = \"\")\n      gep ptr, int32(index0), int32(index1), name\n    end\n\n    def gep(ptr : LLVM::Value, index0 : LLVM::Value, index1 : LLVM::Value, name = \"\")\n      builder.inbounds_gep ptr, index0, index1, name\n    end\n\n    def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : Int32, name = \"\")\n      gep type, ptr, int32(index0), name\n    end\n\n    def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : LLVM::Value, name = \"\")\n      builder.inbounds_gep type, ptr, index0, name\n    end\n\n    def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : Int32, index1 : Int32, name = \"\")\n      gep type, ptr, int32(index0), int32(index1), name\n    end\n\n    def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : LLVM::Value, index1 : LLVM::Value, name = \"\")\n      builder.inbounds_gep type, ptr, index0, index1, name\n    end\n\n    def call(func : LLVMTypedFunction, name : String = \"\")\n      call(func, [] of LLVM::Value, name)\n    end\n\n    def call(func : LLVMTypedFunction, arg : LLVM::Value, name : String = \"\")\n      call(func, [arg], name)\n    end\n\n    def call(func : LLVMTypedFunction, args : Array(LLVM::Value), name : String = \"\")\n      if catch_pad = @catch_pad\n        funclet = builder.build_operand_bundle_def(\"funclet\", [catch_pad])\n      else\n        funclet = LLVM::OperandBundleDef.null\n      end\n\n      begin\n        builder.call(func.type, func.func, args, bundle: funclet, name: name)\n      ensure\n        funclet.dispose\n      end\n    end\n\n    def invoke(func : LLVMTypedFunction, args : Array(LLVM::Value), a_then, a_catch, name : String = \"\")\n      if catch_pad = @catch_pad\n        funclet = builder.build_operand_bundle_def(\"funclet\", [catch_pad])\n      else\n        funclet = LLVM::OperandBundleDef.null\n      end\n\n      begin\n        builder.invoke(func.type, func.func, args, a_then, a_catch, bundle: funclet, name: name)\n      ensure\n        funclet.dispose\n      end\n    end\n\n    delegate ptr2int, int2ptr, and, or, not, bit_cast,\n      trunc, load, store, load_volatile, store_volatile, br, insert_block, position_at_end,\n      cond, phi, extract_value, switch, to: builder\n\n    def ret\n      builder.ret\n    end\n\n    def ret(value : Nil)\n      ret\n    end\n\n    def ret(value)\n      builder.ret value\n    end\n\n    def extend_int(from_type, to_type, value)\n      from_type.signed? ? builder.sext(value, llvm_type(to_type)) : builder.zext(value, llvm_type(to_type))\n    end\n\n    def extend_float(to_type, value)\n      builder.fpext value, llvm_type(to_type)\n    end\n\n    def trunc_float(to_type, value)\n      builder.fptrunc value, llvm_type(to_type)\n    end\n\n    def int_to_float(from_type, to_type, value)\n      if from_type.signed?\n        builder.si2fp value, llvm_type(to_type)\n      else\n        builder.ui2fp value, llvm_type(to_type)\n      end\n    end\n\n    def float_to_int(from_type, to_type, value)\n      if to_type.signed?\n        builder.fp2si value, llvm_type(to_type)\n      else\n        builder.fp2ui value, llvm_type(to_type)\n      end\n    end\n\n    def cast_to(value : LLVM::ValueMethods, type : Type)\n      pointer_cast value, llvm_type(type)\n    end\n\n    def cast_to_pointer(value : LLVM::ValueMethods, type : Type)\n      pointer_cast value, llvm_type(@program.pointer_of(type))\n    end\n\n    def cast_to_void_pointer(pointer : LLVM::ValueMethods)\n      pointer_cast pointer, llvm_context.void_pointer\n    end\n\n    # *type* must be a pointer type; on LLVM 15.0 or above *type* is not\n    # evaluated at all and *value* is returned unchanged, because all opaque\n    # pointer types (in the same context) are identical\n    macro pointer_cast(value, type)\n      {% if LibLLVM::IS_LT_150 %}\n        bit_cast({{ value }}, {{ type }})\n      {% else %}\n        {{ value }}\n      {% end %}\n    end\n\n    delegate llvm_type, llvm_struct_type, llvm_arg_type, llvm_embedded_type,\n      llvm_c_type, llvm_c_return_type, llvm_return_type, llvm_embedded_c_type,\n      llvm_intrinsic_return_type, to: llvm_typer\n\n    def llvm_proc_type(type)\n      llvm_typer.proc_type(type.as(ProcInstanceType))\n    end\n\n    def llvm_closure_type(type)\n      llvm_typer.closure_type(type.as(ProcInstanceType))\n    end\n\n    def llvm_size(type)\n      llvm_type(type).size\n    end\n\n    def llvm_struct_size(type)\n      llvm_struct_type(type).size\n    end\n\n    def llvm_alignment(type)\n      llvm_type(type).alignment\n    end\n\n    def llvm_struct_alignment(type)\n      llvm_struct_type(type).alignment\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/llvm_id.cr",
    "content": "require \"../types\"\n\nmodule Crystal\n  # This class assigns a pair of IDs `{min, max}` to every type in the program.\n  #\n  # For a regular class or struct (not metaclass), this pair\n  # of ids is such that `min` is the minimum ID of its subtypes,\n  # and `max` is 1 + the maximum ID of its subtypes. In this way\n  # we can quickly know if a type implements another type: its ID\n  # must be between {min, max} of such type.\n  #\n  # For example, ids could be assigned like this:\n  #\n  # - Foo: {1, 8}\n  #   - Bar: {1, 2}\n  #     - Baz: {1, 1}\n  #   - Qux: {3, 3}\n  #   - Gen(T): {5, 7}\n  #     - Gen(Int32): {4, 4}\n  #     - Gen2(T): {5, 6}\n  #       - Gen2(Char): {5, 5}\n  #\n  # Note that generic instances and generic subtypes are considered\n  # as subtypes of a generic type.\n  #\n  # For metaclasses IDs are assigned sequentially as they are needed,\n  # because dispatch on metaclasses is less often.\n  class LLVMId\n    getter id_to_metaclass : Hash(Int32, Int32)\n\n    def initialize(program)\n      @ids = {} of Type => {Int32, Int32}\n      @id_to_metaclass = {} of Int32 => Int32\n      @next_id = 0\n      assign_id(program.object)\n      assign_id_to_metaclass(program.object)\n    end\n\n    def type_id(type : TypeDefType)\n      type_id(type.typedef)\n    end\n\n    def type_id(type : VirtualType | VirtualMetaclassType)\n      raise \"BUG: called type_id for #{type} (#{type.class})\"\n    end\n\n    def type_id(type)\n      min_max = @ids[type]?\n      if min_max\n        min_max[1]\n      else\n        id = next_id\n        put_id type, id, id\n        id\n      end\n    end\n\n    def min_max_type_id(type)\n      @ids[type]?\n    end\n\n    private def assign_id(type)\n      min_max_type_id = min_max_type_id(type)\n      if min_max_type_id\n        min_max_type_id[1]\n      else\n        assign_id_impl(type)\n      end\n    end\n\n    private def assign_id_impl(type : NonGenericClassType)\n      assign_id_from_subtypes type, subclasses_of(type)\n    end\n\n    private def assign_id_impl(type : MetaclassType)\n      # Skip for now\n      0\n    end\n\n    private def assign_id_impl(type : GenericClassType)\n      subtypes = type.instantiated_types.reject(&.unbound?)\n      subtypes.concat(subclasses_of(type))\n      assign_id_from_subtypes type, subtypes\n    end\n\n    private def assign_id_impl(type : PrimitiveType)\n      id = next_id\n      put_id type, id, id\n      id\n    end\n\n    private def assign_id_impl(type : GenericClassInstanceType)\n      assign_id_from_subtypes type, type.subclasses\n    end\n\n    private def assign_id_impl(type : NilType)\n      put_id type, 0, 0\n      0\n    end\n\n    private def assign_id_impl(type)\n      raise \"BUG: unhandled type in assign id: #{type}\"\n    end\n\n    private def assign_id_from_subtypes(type, subtypes)\n      if subtypes.empty?\n        id = next_id\n        put_id type, id, id\n        id\n      else\n        min_id = uninitialized Int32\n        first = true\n        subtypes.each do |subtype|\n          sub_id = assign_id(subtype)\n          if first && sub_id != 0\n            min_id = sub_id\n            first = false\n          end\n        end\n        id = next_id\n        put_id type, min_id, id\n        min_id\n      end\n    end\n\n    private def assign_id_to_metaclass(type : NonGenericClassType)\n      assign_id_to_metaclass(type, type.metaclass)\n      type.subclasses.each do |subclass|\n        assign_id_to_metaclass(subclass)\n      end\n    end\n\n    private def assign_id_to_metaclass(type : GenericClassInstanceType | PrimitiveType)\n      assign_id_to_metaclass(type, type.metaclass)\n      type.subclasses.each do |subclass|\n        assign_id_to_metaclass(subclass)\n      end\n    end\n\n    private def assign_id_to_metaclass(type : GenericClassType)\n      assign_id_to_metaclass(type, type.metaclass)\n      type.each_instantiated_type do |instance|\n        assign_id_to_metaclass(instance) unless instance.unbound?\n      end\n      type.subclasses.each do |subclass|\n        assign_id_to_metaclass(subclass)\n      end\n    end\n\n    private def assign_id_to_metaclass(type : MetaclassType)\n      # Nothing\n    end\n\n    private def assign_id_to_metaclass(type)\n      raise \"BUG: unhandled type in assign id to metaclass: #{type}\"\n    end\n\n    private def assign_id_to_metaclass(type, metaclass)\n      @id_to_metaclass[type_id(type)] = type_id(metaclass)\n    end\n\n    private def put_id(type, min, max)\n      @ids[type] = {min, max}\n    end\n\n    private def next_id\n      @next_id += 1\n    end\n\n    private def subclasses_of(type)\n      type.subclasses.reject(GenericInstanceType)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/llvm_typer.cr",
    "content": "require \"../types\"\nrequire \"llvm\"\n\nmodule Crystal\n  class LLVMTyper\n    getter landing_pad_type : LLVM::Type\n\n    alias TypeCache = Hash(Type, LLVM::Type)\n\n    @layout : LLVM::TargetData\n    @landing_pad_type : LLVM::Type\n\n    @@closure_counter = 0\n\n    def initialize(@program : Program, @llvm_context : LLVM::Context)\n      @cache = TypeCache.new\n      @struct_cache = TypeCache.new\n\n      # For union types we just need to know the maximum size of their types.\n      # It might happen that we have a recursive type, for example:\n      #\n      # ```\n      # struct Foo\n      #   def initialize\n      #     @x = uninitialized Pointer(Int32 | Foo)\n      #   end\n      # end\n      # ```\n      #\n      # In that case, when we are computing the llvm type of Foo, we will\n      # need to compute the llvm type of `@x`. Its type is a pointer to\n      # a union. In order to compute the llvm type of a union we need\n      # to compute the size of each type. For this, we compute the llvm\n      # type of each type in the union and then get their size. The problem\n      # here is that we are computing `Foo`, so we can't know its size yet.\n      #\n      # To solve this, when computing the llvm type of the union types,\n      # we do it with a `wants_size` flag. In the case of pointers we\n      # can just return a word size (using size_of(LLVM::VoidPointer)) instead\n      # of computing the llvm type of the pointer element. This avoids the\n      # recursion.\n      #\n      # We still need a separate cache for this types that we use to compute\n      # types, because there can be cycles.\n      @wants_size_cache = TypeCache.new\n      @wants_size_struct_cache = TypeCache.new\n\n      @structs = {} of String => LLVM::Type\n\n      machine = program.target_machine\n      @layout = machine.data_layout\n      @landing_pad_type = @llvm_context.struct([@llvm_context.void_pointer, @llvm_context.int32], \"landing_pad\")\n    end\n\n    def type_id_pointer\n      @llvm_context.int32.pointer\n    end\n\n    @proc_type : LLVM::Type?\n\n    def proc_type\n      @proc_type ||= @structs[\"->\"] ||= @llvm_context.struct [@llvm_context.void_pointer, @llvm_context.void_pointer], \"->\"\n    end\n\n    @nil_type : LLVM::Type?\n\n    def nil_type\n      @nil_type ||= @structs[\"Nil\"] ||= @llvm_context.struct([] of LLVM::Type, \"Nil\")\n    end\n\n    def nil_value\n      nil_type.null\n    end\n\n    def llvm_string_type(bytesize)\n      @llvm_context.struct [\n        @llvm_context.int32,                    # type_id\n        @llvm_context.int32,                    # @bytesize\n        @llvm_context.int32,                    # @length\n        @llvm_context.int8.array(bytesize + 1), # @c\n      ]\n    end\n\n    def llvm_type(type, wants_size = false)\n      type = type.remove_indirection\n\n      if wants_size\n        @wants_size_cache[type] ||= create_llvm_type(type, wants_size: true)\n      else\n        @cache[type] ||= create_llvm_type(type, wants_size)\n      end\n    end\n\n    private def create_llvm_type(type : NoReturnType, wants_size)\n      @llvm_context.void\n    end\n\n    private def create_llvm_type(type : VoidType, wants_size)\n      @llvm_context.void\n    end\n\n    private def create_llvm_type(type : NilType, wants_size)\n      nil_type\n    end\n\n    private def create_llvm_type(type : BoolType, wants_size)\n      @llvm_context.int1\n    end\n\n    private def create_llvm_type(type : CharType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : IntegerType, wants_size)\n      @llvm_context.int(8 * type.bytes)\n    end\n\n    private def create_llvm_type(type : FloatType, wants_size)\n      type.bytes == 4 ? @llvm_context.float : @llvm_context.double\n    end\n\n    private def create_llvm_type(type : SymbolType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : EnumType, wants_size)\n      llvm_type(type.base_type)\n    end\n\n    private def create_llvm_type(type : ProcInstanceType, wants_size)\n      proc_type\n    end\n\n    private def create_llvm_type(type : InstanceVarContainer, wants_size)\n      # The size of a class is the same as the size of a pointer\n      if wants_size && !type.struct?\n        return @llvm_context.void_pointer\n      end\n\n      final_type = llvm_struct_type(type, wants_size)\n      unless type.struct?\n        final_type = final_type.pointer\n      end\n      final_type\n    end\n\n    private def create_llvm_type(type : MetaclassType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : LibType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : GenericClassInstanceMetaclassType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : GenericModuleInstanceMetaclassType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : VirtualMetaclassType, wants_size)\n      @llvm_context.int32\n    end\n\n    private def create_llvm_type(type : PointerInstanceType, wants_size)\n      if wants_size\n        return @llvm_context.void_pointer\n      end\n\n      pointed_type = llvm_embedded_type(type.element_type, wants_size)\n      pointed_type = @llvm_context.int8 if pointed_type.void?\n      pointed_type.pointer\n    end\n\n    private def create_llvm_type(type : StaticArrayInstanceType, wants_size)\n      pointed_type = llvm_embedded_type(type.element_type, wants_size)\n      pointed_type = @llvm_context.int8 if pointed_type.void?\n      pointed_type.array type.size.as(NumberLiteral).value.to_i\n    end\n\n    private def create_llvm_type(type : TupleInstanceType, wants_size)\n      llvm_name = llvm_name(type, wants_size)\n\n      if s = @structs[llvm_name]?\n        return s\n      end\n\n      @llvm_context.struct(llvm_name) do |a_struct|\n        if wants_size\n          @wants_size_cache[type] = a_struct\n        else\n          @cache[type] = a_struct\n          @structs[llvm_name] = a_struct\n        end\n\n        type.tuple_types.map { |tuple_type| llvm_embedded_type(tuple_type, wants_size).as(LLVM::Type) }\n      end\n    end\n\n    private def create_llvm_type(type : NamedTupleInstanceType, wants_size)\n      llvm_name = llvm_name(type, wants_size)\n      if s = @structs[llvm_name]?\n        return s\n      end\n\n      @llvm_context.struct(llvm_name) do |a_struct|\n        if wants_size\n          @wants_size_cache[type] = a_struct\n        else\n          @cache[type] = a_struct\n          @structs[llvm_name] = a_struct\n        end\n\n        type.entries.map { |entry| llvm_embedded_type(entry.type, wants_size).as(LLVM::Type) }\n      end\n    end\n\n    private def create_llvm_type(type : NilableType, wants_size)\n      llvm_type(type.not_nil_type, wants_size)\n    end\n\n    private def create_llvm_type(type : ReferenceUnionType, wants_size)\n      type_id_pointer\n    end\n\n    private def create_llvm_type(type : NilableReferenceUnionType, wants_size)\n      type_id_pointer\n    end\n\n    private def create_llvm_type(type : NilableProcType, wants_size)\n      proc_type\n    end\n\n    private def create_llvm_type(type : TypeDefType, wants_size)\n      llvm_type(type.typedef, wants_size)\n    end\n\n    private def create_llvm_type(type : VirtualType, wants_size)\n      type_id_pointer\n    end\n\n    private def create_llvm_type(type : AliasType, wants_size)\n      llvm_type(type.remove_alias, wants_size)\n    end\n\n    private def create_llvm_type(type : ReferenceStorageType, wants_size)\n      llvm_struct_type(type.reference_type, wants_size)\n    end\n\n    private def create_llvm_type(type : NonGenericModuleType | GenericClassType, wants_size)\n      # This can only be reached if the module or generic class don't have implementors\n      @llvm_context.int1\n    end\n\n    private def create_llvm_type(type : Type, wants_size)\n      raise \"BUG: called create_llvm_type for #{type}\"\n    end\n\n    def llvm_struct_type(type, wants_size = false)\n      type = type.remove_indirection\n\n      if wants_size\n        @wants_size_struct_cache[type] ||= create_llvm_struct_type(type, wants_size: true)\n      else\n        @struct_cache[type] ||= create_llvm_struct_type(type, wants_size)\n      end\n    end\n\n    private def create_llvm_struct_type(type : StaticArrayInstanceType, wants_size)\n      llvm_type(type, wants_size)\n    end\n\n    private def create_llvm_struct_type(type : TupleInstanceType, wants_size)\n      llvm_type(type, wants_size)\n    end\n\n    private def create_llvm_struct_type(type : NamedTupleInstanceType, wants_size)\n      llvm_type(type, wants_size)\n    end\n\n    private def create_llvm_struct_type(type : InstanceVarContainer, wants_size)\n      if type.extern_union?\n        return create_llvm_c_union_struct_type(type, wants_size)\n      end\n\n      llvm_name = llvm_name(type, wants_size)\n      if s = @structs[llvm_name]?\n        return s\n      end\n\n      @llvm_context.struct(llvm_name, type.packed?) do |a_struct|\n        if wants_size\n          @wants_size_struct_cache[type] = a_struct\n        else\n          @struct_cache[type] = a_struct\n          @structs[llvm_name] = a_struct\n        end\n\n        ivars = type.all_instance_vars\n        ivars_size = ivars.size\n        ivars_size += 1 unless type.struct?\n\n        element_types = Array(LLVM::Type).new(ivars_size)\n        element_types.push @llvm_context.int32 unless type.struct? # For the type id\n\n        ivars.each do |name, ivar|\n          if type.extern?\n            element_types.push llvm_embedded_c_type(ivar.type, wants_size)\n          else\n            element_types.push llvm_embedded_type(ivar.type, wants_size)\n          end\n        end\n\n        element_types\n      end\n    end\n\n    private def create_llvm_c_union_struct_type(type, wants_size)\n      llvm_name = llvm_name(type, wants_size)\n      if s = @structs[llvm_name]?\n        return s\n      end\n\n      @llvm_context.struct(llvm_name) do |a_struct|\n        if wants_size\n          @wants_size_struct_cache[type] = a_struct\n        else\n          @struct_cache[type] = a_struct\n          @structs[llvm_name] = a_struct\n        end\n\n        # We are going to represent the union like this:\n        # 1. Find out what's the type with the largest alignment\n        # 2. Find out what's the type's size\n        # 3. Have the first member of the union be an array\n        #    of ints that match that alignment, up to that type's\n        #    size, followed by another member that is just bytes\n        #    to fill the rest of the union's size.\n        #\n        # So for example if we have this:\n        #\n        # struct Foo\n        #   x : Int8\n        #   y : Int32\n        # end\n        #\n        # union Bar\n        #   foo : Foo\n        #   padding : UInt8[24]\n        # end\n        #\n        # We have that for Bar, the largest alignment of its types\n        # is 4 (for Foo's Int32). Foo's size is 8 bytes (4 for x, 4 for y).\n        # Then for the first union member we'll have [2 x i32].\n        # The total size of the union is 24 bytes. We already filled\n        # 8 bytes so we still need 16 bytes.\n        # The resulting union is { [2 x i32], [16 x i8] }.\n\n        max_size = 0\n        max_align = 0\n        max_align_type = nil\n        max_align_type_size = 0\n\n        type.instance_vars.each do |name, var|\n          var_type = var.type\n          unless var_type.void?\n            llvm_type = llvm_embedded_c_type(var_type, wants_size: true)\n            size = size_of(llvm_type)\n            align = align_of(llvm_type)\n\n            if size > max_size\n              max_size = size\n            end\n\n            if align > max_align\n              max_align = align\n              max_align_type = llvm_type\n              max_align_type_size = size\n            end\n          end\n        end\n\n        filler = @llvm_context.int(max_align * 8)\n        filler_size = max_align_type_size // max_align\n\n        union_fill = [filler.array(filler_size)] of LLVM::Type\n        if max_align_type_size < max_size\n          union_fill << @llvm_context.int8.array(max_size - max_align_type_size)\n        end\n\n        union_fill\n      end\n    end\n\n    private def create_llvm_struct_type(type : Type, wants_size)\n      raise \"BUG: called llvm_struct_type for #{type}\"\n    end\n\n    def llvm_embedded_type(type, wants_size = false)\n      type = type.remove_indirection\n      case type\n      when NoReturnType, VoidType\n        @llvm_context.int8\n      else\n        llvm_type(type, wants_size)\n      end\n    end\n\n    def llvm_embedded_c_type(type : ProcInstanceType, wants_size = false)\n      proc_type(type).pointer\n    end\n\n    def llvm_embedded_c_type(type, wants_size = false)\n      llvm_embedded_type(type, wants_size)\n    end\n\n    def llvm_c_type(type : ProcInstanceType)\n      proc_type(type).pointer\n    end\n\n    def llvm_c_type(type : NilableProcType)\n      proc_type(type.proc_type).pointer\n    end\n\n    def llvm_c_type(type : TupleInstanceType)\n      llvm_struct_type(type)\n    end\n\n    def llvm_c_type(type)\n      if type.extern?\n        llvm_struct_type(type)\n      elsif type.passed_by_value?\n        # C types that are passed by value must be considered,\n        # for the ABI, as being passed behind a pointer\n        llvm_type(type).pointer\n      else\n        llvm_type(type)\n      end\n    end\n\n    # StaticArray can only be \"returned\" in lib externs,\n    # not in C functions, and there it must not be a pointer.\n    def llvm_c_return_type(type : StaticArrayInstanceType)\n      llvm_struct_type(type)\n    end\n\n    def llvm_c_return_type(type : NilType)\n      @llvm_context.void\n    end\n\n    def llvm_c_return_type(type)\n      llvm_c_type(type)\n    end\n\n    def llvm_return_type(type : NilType)\n      @llvm_context.void\n    end\n\n    def llvm_return_type(type)\n      llvm_type(type)\n    end\n\n    # Since LLVM 15, LLVM intrinsics must return unnamed structs, and instances\n    # of the named `Tuple` struct are no longer substitutable\n    # This happens when binding to intrinsics like `llvm.sadd.with.overflow.*`\n    # as lib funs directly\n    def llvm_intrinsic_return_type(type : TupleInstanceType)\n      @llvm_context.struct(type.tuple_types.map { |tuple_type| llvm_embedded_type(tuple_type).as(LLVM::Type) })\n    end\n\n    def llvm_intrinsic_return_type(type : NamedTupleInstanceType)\n      @llvm_context.struct(type.entries.map { |entry| llvm_embedded_type(entry.type).as(LLVM::Type) })\n    end\n\n    def llvm_intrinsic_return_type(type : Type)\n      llvm_return_type(type)\n    end\n\n    def closure_type(type : ProcInstanceType)\n      arg_types = type.arg_types.map { |arg_type| llvm_type(arg_type) }\n      arg_types.insert(0, @llvm_context.void_pointer)\n      LLVM::Type.function(arg_types, llvm_type(type.return_type))\n    end\n\n    def proc_type(type : ProcInstanceType)\n      arg_types = type.arg_types.map { |arg_type| llvm_type(arg_type).as(LLVM::Type) }\n      LLVM::Type.function(arg_types, llvm_type(type.return_type))\n    end\n\n    def closure_context_type(vars, parent_llvm_type, self_type)\n      @@closure_counter += 1\n      llvm_name = \"closure_#{@@closure_counter}\"\n\n      has_inner_pointers = !parent_llvm_type.nil?\n      closure_type = @llvm_context.struct(llvm_name) do |a_struct|\n        @structs[llvm_name] = a_struct\n\n        elems = Array(LLVM::Type).new(vars.size + (parent_llvm_type ? 1 : 0) + (self_type ? 1 : 0))\n\n        vars.each do |var|\n          elems << llvm_type(var.type)\n          has_inner_pointers ||= var.type.has_inner_pointers?\n        end\n\n        # Make sure to copy the given LLVM::Type to this context\n        if parent_llvm_type\n          elems << copy_type(parent_llvm_type).pointer\n        end\n\n        if self_type\n          elems << llvm_type(self_type)\n          has_inner_pointers ||= self_type.has_inner_pointers?\n        end\n\n        elems\n      end\n\n      {closure_type, has_inner_pointers}\n    end\n\n    # Copy existing LLVM types, possibly from another context,\n    # into this typer's context.\n    def copy_types(types : Array(LLVM::Type))\n      types.map do |type|\n        copy_type(type).as(LLVM::Type)\n      end\n    end\n\n    # Copy an existing LLVM type, possibly from another context,\n    # into this typer's context.\n    def copy_type(type : LLVM::Type)\n      case type.kind\n      when .void?\n        @llvm_context.void\n      when .integer?\n        @llvm_context.int(type.int_width)\n      when .float?\n        @llvm_context.float\n      when .double?\n        @llvm_context.double\n      when .pointer?\n        {% if LibLLVM::IS_LT_150 %}\n          copy_type(type.element_type).pointer\n        {% else %}\n          @llvm_context.pointer\n        {% end %}\n      when .array?\n        copy_type(type.element_type).array(type.array_size)\n      when .vector?\n        copy_type(type.element_type).vector(type.vector_size)\n      when .function?\n        params_types = copy_types(type.params_types)\n        ret_type = copy_type(type.return_type)\n        LLVM::Type.function(params_types, ret_type, type.varargs?)\n      when .struct?\n        llvm_name = type.struct_name\n        if llvm_name\n          @structs[llvm_name] ||= begin\n            @llvm_context.struct(llvm_name, type.packed_struct?) do |the_struct|\n              @structs[llvm_name] = the_struct\n              copy_types(type.struct_element_types)\n            end\n          end\n        else\n          # The case of an anonymous struct (only happens with C bindings and C ABI,\n          # where structs like `{ double, double }` are generated)\n          @llvm_context.struct(copy_types(type.struct_element_types), packed: type.packed_struct?)\n        end\n      else\n        raise \"don't know how to copy type: #{type} (#{type.kind})\"\n      end\n    end\n\n    def llvm_name(type, wants_size)\n      llvm_name = type.llvm_name\n      llvm_name = \"#{llvm_name}.wants_size\" if wants_size\n      llvm_name\n    end\n\n    def size_of(type)\n      if type.void?\n        0_u64\n      else\n        @layout.size_in_bytes type\n      end\n    end\n\n    def offset_of(type, element_index)\n      @layout.offset_of_element(type, element_index)\n    end\n\n    def align_of(type)\n      if type.void?\n        1_u32\n      else\n        @layout.abi_alignment(type)\n      end\n    end\n\n    def size_t\n      @llvm_context.int(@program.size_bit_width)\n    end\n\n    @pointer_size : UInt64?\n\n    def pointer_size\n      @pointer_size ||= size_of(@llvm_context.void_pointer)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/match.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  def match_type_id(type, restriction, type_id)\n    match_type_id_impl(type.remove_indirection, restriction.remove_indirection, type_id)\n  end\n\n  private def match_type_id_impl(type, restriction : Program, type_id)\n    llvm_true\n  end\n\n  private def match_type_id_impl(type, restriction : FileModule, type_id)\n    llvm_true\n  end\n\n  private def match_type_id_impl(type : UnionType | VirtualType | VirtualMetaclassType, restriction, type_id)\n    match_any_type_id(restriction, type_id)\n  end\n\n  private def match_type_id_impl(type : AliasType, restriction, type_id)\n    match_type_id type.aliased_type, restriction, type_id\n  end\n\n  private def match_type_id_impl(type, restriction, type_id)\n    equal? type_id(restriction), type_id\n  end\n\n  def match_any_type_id(type, type_id)\n    match_any_type_id_impl(type.remove_indirection, type_id)\n  end\n\n  private def match_any_type_id_impl(type : UnionType | VirtualType | VirtualMetaclassType, type_id)\n    match_any_type_id_with_function(type, type_id)\n  end\n\n  private def match_any_type_id_impl(type, type_id)\n    equal? type_id(type), type_id\n  end\n\n  private def match_any_type_id_with_function(type, type_id)\n    match_fun_name = \"~match<#{type.llvm_name}>\"\n    func = typed_fun?(@main_mod, match_fun_name) || create_match_fun(match_fun_name, type)\n    func = check_main_fun match_fun_name, func\n    call func, [type_id] of LLVM::Value\n  end\n\n  private def create_match_fun(name, type)\n    in_main do\n      define_main_function(name, ([llvm_context.int32]), llvm_context.int1) do |func|\n        set_internal_fun_debug_location(func, name)\n        type_id = func.params.first\n        create_match_fun_body(type, type_id)\n      end\n    end\n  end\n\n  private def create_match_fun_body(type : UnionType, type_id)\n    result = nil\n    type.expand_union_types.each do |sub_type|\n      sub_type_cond = match_any_type_id(sub_type, type_id)\n      result = result ? or(result, sub_type_cond) : sub_type_cond\n    end\n    ret result.not_nil!\n  end\n\n  private def create_match_fun_body(type : VirtualType, type_id)\n    min, max = @program.llvm_id.min_max_type_id(type.base_type).not_nil!\n    ret(\n      and(\n        builder.icmp(LLVM::IntPredicate::SGE, type_id, int(min)),\n        builder.icmp(LLVM::IntPredicate::SLE, type_id, int(max))\n      )\n    )\n  end\n\n  private def create_match_fun_body(type, type_id)\n    result = nil\n    type.each_concrete_type do |sub_type|\n      sub_type_cond = equal? type_id(sub_type), type_id\n      result = result ? or(result, sub_type_cond) : sub_type_cond\n    end\n    ret result.not_nil!\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/once.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  ONCE_STATE = \"~ONCE_STATE\"\n\n  def once_init\n    if once_init_fun = typed_fun?(@main_mod, ONCE_INIT)\n      # legacy (kept for backward compatibility): the compiler must save the\n      # state returned by __crystal_once_init\n      once_init_fun = check_main_fun ONCE_INIT, once_init_fun\n\n      once_state_global = @main_mod.globals.add(once_init_fun.type.return_type, ONCE_STATE)\n      once_state_global.linkage = LLVM::Linkage::Internal if @single_module\n      once_state_global.initializer = once_init_fun.type.return_type.null\n\n      state = call once_init_fun\n      store state, once_state_global\n    end\n  end\n\n  def run_once(flag, func : LLVMTypedFunction)\n    once_fun = main_fun(ONCE)\n    once_fun_params = once_fun.func.params\n    once_initializer_type = once_fun_params.last.type # must be Void*\n    initializer = pointer_cast(func.func.to_value, once_initializer_type)\n\n    if once_fun_params.size == 2\n      args = [flag, initializer]\n    else\n      # legacy (kept for backward compatibility): the compiler must pass the\n      # state returned by __crystal_once_init to __crystal_once as the first\n      # argument\n      once_init_fun = main_fun(ONCE_INIT)\n      once_state_type = once_init_fun.type.return_type # must be Void*\n\n      once_state_global = @llvm_mod.globals[ONCE_STATE]? || begin\n        global = @llvm_mod.globals.add(once_state_type, ONCE_STATE)\n        global.linkage = LLVM::Linkage::External\n        global\n      end\n\n      state = load(once_state_type, once_state_global)\n      args = [state, flag, initializer]\n    end\n\n    call once_fun, args\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/phi.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  class Phi\n    include LLVMBuilderHelper\n\n    property? force_exit_block = false\n\n    def self.open(codegen, node, needs_value = true, &)\n      block = new codegen, node, needs_value\n      yield block\n      block.close\n    end\n\n    def self.new(codegen : CodeGenVisitor, node : ASTNode, needs_value : Bool)\n      new codegen, node.type?, needs_value\n    end\n\n    def initialize(@codegen : CodeGenVisitor, @node_type : Type?, @needs_value : Bool)\n      @phi_table = @needs_value ? LLVM::PhiTable.new : nil\n      @count = 0\n    end\n\n    getter exit_block : LLVM::BasicBlock do\n      @codegen.new_block \"exit\"\n    end\n\n    def builder\n      @codegen.builder\n    end\n\n    def llvm_typer\n      @codegen.llvm_typer\n    end\n\n    def unreachable\n      @codegen.unreachable\n    end\n\n    def add(value, type : Nil, last = false)\n      unreachable\n    end\n\n    def add(value, type : NoReturnType, last = false)\n      unreachable\n    end\n\n    def add(value, type : Type, last = false)\n      if @node_type.try &.no_return?\n        if @force_exit_block\n          br exit_block\n        else\n          unreachable\n        end\n        return\n      end\n\n      if @codegen.builder.end\n        return\n      end\n\n      if @needs_value\n        unless @node_type.try(&.void?) || @node_type.try(&.nil_type?)\n          value = @codegen.upcast value, @node_type.not_nil!, type\n          @phi_table.not_nil!.add insert_block, value\n        end\n      end\n\n      @count += 1\n\n      if last && @count == 1\n        # Don't create exit block for just one value\n      else\n        br exit_block\n      end\n    end\n\n    def close\n      if @exit_block\n        position_at_end exit_block\n      end\n\n      if @count == 0\n        if @force_exit_block\n          @codegen.last = llvm_nil\n        else\n          unreachable\n        end\n      elsif @needs_value\n        phi_table = @phi_table.not_nil!\n        if phi_table.empty?\n          # All branches are void or no return\n          @codegen.last = llvm_nil\n        else\n          if @exit_block\n            node_type = @node_type.not_nil!\n            type = llvm_type(node_type)\n            type = type.pointer if node_type.passed_by_value?\n            @codegen.last = phi type, phi_table\n          else\n            @codegen.last = phi_table.values.first\n          end\n        end\n      else\n        @codegen.last = llvm_nil\n      end\n\n      @codegen.last\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/primitives.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  # Can only happen in a Const or as an argument cast.\n  def visit(node : Primitive)\n    @last = case node.name\n            when \"argc\"\n              @argc\n            when \"argv\"\n              @argv\n            else\n              raise \"BUG: unhandled primitive in codegen visit: #{node.name}\"\n            end\n\n    false\n  end\n\n  def codegen_primitive(call, node, target_def, call_args)\n    @call_location = call.try &.name_location\n\n    @last = case node.name\n            when \"binary\"\n              codegen_primitive_binary node, target_def, call_args\n            when \"convert\"\n              codegen_primitive_convert node, target_def, call_args, checked: true\n            when \"unchecked_convert\"\n              codegen_primitive_convert node, target_def, call_args, checked: false\n            when \"allocate\"\n              codegen_primitive_allocate node, target_def, call_args\n            when \"pre_initialize\"\n              codegen_primitive_pre_initialize node, target_def, call_args\n            when \"pointer_malloc\"\n              codegen_primitive_pointer_malloc node, target_def, call_args\n            when \"pointer_set\"\n              codegen_primitive_pointer_set node, target_def, call_args\n            when \"pointer_get\"\n              codegen_primitive_pointer_get node, target_def, call_args\n            when \"pointer_address\"\n              codegen_primitive_pointer_address node, target_def, call_args\n            when \"pointer_new\"\n              codegen_primitive_pointer_new node, target_def, call_args\n            when \"pointer_realloc\"\n              codegen_primitive_pointer_realloc node, target_def, call_args\n            when \"pointer_add\"\n              codegen_primitive_pointer_add node, target_def, call_args\n            when \"pointer_diff\"\n              codegen_primitive_pointer_diff node, target_def, call_args\n            when \"struct_or_union_set\"\n              codegen_primitive_struct_or_union_set node, target_def, call_args\n            when \"external_var_set\"\n              codegen_primitive_external_var_set node, target_def, call_args\n            when \"external_var_get\"\n              codegen_primitive_external_var_get node, target_def, call_args\n            when \"object_id\"\n              codegen_primitive_object_id node, target_def, call_args\n            when \"object_crystal_type_id\"\n              codegen_primitive_object_crystal_type_id node, target_def, call_args\n            when \"class_crystal_instance_type_id\"\n              codegen_primitive_class_crystal_instance_type_id node, target_def, call_args\n            when \"symbol_to_s\"\n              codegen_primitive_symbol_to_s node, target_def, call_args\n            when \"class\"\n              codegen_primitive_class node, target_def, call_args\n            when \"proc_call\"\n              codegen_primitive_proc_call node, target_def, call_args\n            when \"tuple_indexer_known_index\"\n              codegen_primitive_tuple_indexer_known_index node, target_def, call_args\n            when \"enum_value\", \"enum_new\"\n              call_args[0]\n            when \"cmpxchg\"\n              codegen_primitive_cmpxchg call, node, target_def, call_args\n            when \"atomicrmw\"\n              codegen_primitive_atomicrmw call, node, target_def, call_args\n            when \"fence\"\n              codegen_primitive_fence call, node, target_def, call_args\n            when \"load_atomic\"\n              codegen_primitive_load_atomic call, node, target_def, call_args\n            when \"store_atomic\"\n              codegen_primitive_store_atomic call, node, target_def, call_args\n            when \"throw_info\"\n              cast_to_void_pointer void_ptr_throwinfo\n            when \"va_arg\"\n              codegen_va_arg call, node, target_def, call_args\n            else\n              raise \"BUG: unhandled primitive in codegen: #{node.name}\"\n            end\n\n    @call_location = nil\n  end\n\n  def codegen_primitive_binary(node, target_def, call_args)\n    p1, p2 = call_args\n    t1, t2 = target_def.owner, target_def.args[0].type\n    codegen_binary_op target_def.name, t1, t2, p1, p2\n  end\n\n  def codegen_binary_op(op, t1 : BoolType, t2 : BoolType, p1, p2)\n    case op\n    when \"==\" then builder.icmp LLVM::IntPredicate::EQ, p1, p2\n    when \"!=\" then builder.icmp LLVM::IntPredicate::NE, p1, p2\n    else           raise \"BUG: trying to codegen #{t1} #{op} #{t2}\"\n    end\n  end\n\n  def codegen_binary_op(op, t1 : CharType, t2 : CharType, p1, p2)\n    case op\n    when \"==\" then builder.icmp LLVM::IntPredicate::EQ, p1, p2\n    when \"!=\" then builder.icmp LLVM::IntPredicate::NE, p1, p2\n    when \"<\"  then builder.icmp LLVM::IntPredicate::ULT, p1, p2\n    when \"<=\" then builder.icmp LLVM::IntPredicate::ULE, p1, p2\n    when \">\"  then builder.icmp LLVM::IntPredicate::UGT, p1, p2\n    when \">=\" then builder.icmp LLVM::IntPredicate::UGE, p1, p2\n    else           raise \"BUG: trying to codegen #{t1} #{op} #{t2}\"\n    end\n  end\n\n  def codegen_binary_op(op, t1 : SymbolType, t2 : SymbolType, p1, p2)\n    case op\n    when \"==\" then builder.icmp LLVM::IntPredicate::EQ, p1, p2\n    when \"!=\" then builder.icmp LLVM::IntPredicate::NE, p1, p2\n    else           raise \"BUG: trying to codegen #{t1} #{op} #{t2}\"\n    end\n  end\n\n  def codegen_binary_op(op, t1 : IntegerType, t2 : IntegerType, p1, p2)\n    # Comparisons are a bit trickier because we want to get comparisons\n    # between signed and unsigned integers right.\n    case op\n    when \"<\"  then return codegen_binary_op_lt(t1, t2, p1, p2)\n    when \"<=\" then return codegen_binary_op_lte(t1, t2, p1, p2)\n    when \">\"  then return codegen_binary_op_gt(t1, t2, p1, p2)\n    when \">=\" then return codegen_binary_op_gte(t1, t2, p1, p2)\n    when \"==\" then return codegen_binary_op_eq(t1, t2, p1, p2)\n    when \"!=\" then return codegen_binary_op_ne(t1, t2, p1, p2)\n    end\n\n    case op\n    when \"+\", \"-\" then return codegen_addsub_with_overflow(op, t1, t2, p1, p2)\n    when \"*\"      then return codegen_mul_with_overflow(t1, t2, p1, p2)\n    end\n\n    _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n\n    case op\n    when \"&+\"              then codegen_trunc_binary_op_result(t1, t2, builder.add(p1, p2))\n    when \"&-\"              then codegen_trunc_binary_op_result(t1, t2, builder.sub(p1, p2))\n    when \"&*\"              then codegen_trunc_binary_op_result(t1, t2, builder.mul(p1, p2))\n    when \"/\", \"unsafe_div\" then codegen_trunc_binary_op_result(t1, t2, t1.signed? ? builder.sdiv(p1, p2) : builder.udiv(p1, p2))\n    when \"%\", \"unsafe_mod\" then codegen_trunc_binary_op_result(t1, t2, t1.signed? ? builder.srem(p1, p2) : builder.urem(p1, p2))\n    when \"unsafe_shl\"      then codegen_trunc_binary_op_result(t1, t2, builder.shl(p1, p2))\n    when \"unsafe_shr\"      then codegen_trunc_binary_op_result(t1, t2, t1.signed? ? builder.ashr(p1, p2) : builder.lshr(p1, p2))\n    when \"|\"               then codegen_trunc_binary_op_result(t1, t2, or(p1, p2))\n    when \"&\"               then codegen_trunc_binary_op_result(t1, t2, and(p1, p2))\n    when \"^\"               then codegen_trunc_binary_op_result(t1, t2, builder.xor(p1, p2))\n    else                        raise \"BUG: trying to codegen #{t1} #{op} #{t2}\"\n    end\n  end\n\n  def codegen_addsub_with_overflow(op, t1, t2, p1, p2)\n    if t1.signed? != t2.signed?\n      # Convert `p1` to the opposite signedness, while simultaneously applying\n      # a bias equal to half the integer range: add bias if `p1` is unsigned,\n      # subtract bias if `p1` is signed, which for two's complement is\n      # equivalent to a bitwise XOR on the sign bit. Thus, if `t1` is signed:\n      #\n      # ```\n      # p1_biased = (p1 ^ t1::MIN).to_unsigned!\n      # result_biased = ...(p1_biased, p2)\n      # result_biased.to_signed! ^ t1::MIN\n      # ```\n      #\n      # If `t1` is unsigned:\n      #\n      # ```\n      # bias = typeof(p1.to_signed!)::MIN\n      # p1_biased = p1.to_signed! ^ bias\n      # result_biased = ...(p1_biased, p2)\n      # (result_biased ^ bias).to_unsigned!\n      # ```\n      t1_biased = @program.int_type(!t1.signed?, t1.bytes)\n      sign_bit, _ = (t1.signed? ? t1 : t1_biased).range\n      bias = int(sign_bit, t1)\n      p1_biased = builder.xor(p1, bias)\n\n      # now the overflow criterion is identical to that of the respective\n      # same-signedness operation\n      result_biased = codegen_addsub_same_signedness_with_overflow(op, t1_biased, t2, p1_biased, p2)\n\n      # revert the bias\n      builder.xor(result_biased, bias)\n    else\n      codegen_addsub_same_signedness_with_overflow(op, t1, t2, p1, p2)\n    end\n  end\n\n  def codegen_addsub_same_signedness_with_overflow(op, t1, t2, p1, p2)\n    if t2.bytes > t1.bytes\n      if t2.signed?\n        # e.g. Int8+Int16\n        # t1.new(t2.new!(p1) &+ p2)\n        p1 = extend_int(t1, t2, p1)\n\n        # use unchecked arithmetic; the signed overflow here cannot result in\n        # any value that fits into `t1`'s range\n        result = op == \"+\" ? builder.add(p1, p2) : builder.sub(p1, p2)\n\n        # catch the overflow via truncation instead\n        codegen_convert(t2, t1, result, checked: true)\n      else\n        # e.g. UInt8+UInt16\n        # p1 + t1.new!(p2)\n        p2_trunc = trunc(p2, llvm_type(t1))\n        result, overflow = call_binary_overflow_fun op, t1, p1, p2_trunc\n        codegen_raise_overflow_cond overflow\n\n        # if `p2` is outside `t1`'s range, any addition or subtraction must\n        # overflow regardless of `p1`'s value\n        _, max = t1.range\n        p2_too_large = builder.icmp(LLVM::IntPredicate::UGT, p2, int(max, t2))\n        codegen_raise_overflow_cond p2_too_large\n\n        result\n      end\n    else\n      # e.g. Int8+Int8, UInt8+UInt8, Int16+Int8, UInt16+UInt8\n      if t2.bytes < t1.bytes\n        # p1 + t1.new!(p2)\n        p2 = extend_int(t2, t1, p2)\n      end\n\n      result, overflow = call_binary_overflow_fun op, t1, p1, p2\n      codegen_raise_overflow_cond overflow\n      result\n    end\n  end\n\n  def codegen_mul_with_overflow(t1, t2, p1, p2)\n    if t1.unsigned? && t2.signed?\n      codegen_mul_unsigned_signed_with_overflow(t1, t2, p1, p2)\n    elsif t1.signed? && t2.unsigned?\n      codegen_mul_signed_unsigned_with_overflow(t1, t2, p1, p2)\n    else\n      codegen_mul_same_signedness_with_overflow(t1, t2, p1, p2)\n    end\n  end\n\n  def codegen_mul_unsigned_signed_with_overflow(t1, t2, p1, p2)\n    overflow = and(\n      codegen_binary_op_ne(t1, t1, p1, int(0, t1)), # self != 0\n      codegen_binary_op_lt(t2, t2, p2, int(0, t2))  # other < 0\n    )\n    codegen_raise_overflow_cond overflow\n\n    codegen_mul_same_signedness_with_overflow(t1, @program.int_type(false, t2.bytes), p1, p2)\n  end\n\n  def codegen_mul_signed_unsigned_with_overflow(t1, t2, p1, p2)\n    negative = codegen_binary_op_lt(t1, t1, p1, int(0, t1)) # self < 0\n    minus_p1 = builder.sub int(0, t1), p1\n    abs = builder.select negative, minus_p1, p1\n    u1 = @program.int_type(false, t1.bytes)\n\n    # tmp is the abs value of the result\n    # there is overflow when |result| > max + (negative ? 1 : 0)\n    tmp = codegen_mul_same_signedness_with_overflow(u1, t2, abs, p2)\n    _, max = t1.range\n    max_result = builder.add(int(max, t1), builder.zext(negative, llvm_type(t1)))\n    overflow = codegen_binary_op_gt(u1, u1, tmp, max_result)\n    codegen_raise_overflow_cond overflow\n\n    # negate back the result if p1 was negative\n    minus_tmp = builder.sub int(0, t1), tmp\n    builder.select negative, minus_tmp, tmp\n  end\n\n  def codegen_mul_same_signedness_with_overflow(t1, t2, p1, p2)\n    tmax, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n\n    result, overflow = call_binary_overflow_fun \"*\", tmax, p1, p2\n\n    if tmax.bytes > t1.bytes\n      result_trunc = trunc result, llvm_type(t1)\n      result_trunc_ext = extend_int(t1, tmax, result_trunc)\n      overflow = or(overflow, builder.icmp LLVM::IntPredicate::NE, result, result_trunc_ext)\n    end\n\n    codegen_raise_overflow_cond overflow\n\n    trunc result, llvm_type(t1)\n  end\n\n  def codegen_binary_extend_int(t1, t2, p1, p2)\n    if t1.normal_rank == t2.normal_rank\n      # Nothing to do\n      tmax = t1\n    elsif t1.rank < t2.rank\n      p1 = extend_int t1, t2, p1\n      tmax = t2\n    else\n      p2 = extend_int t2, t1, p2\n      tmax = t1\n    end\n    {tmax, p1, p2}\n  end\n\n  # Ensures the result is returned in the type of the left hand side operand t1.\n  # This is only needed if the operation was carried in the realm of t2\n  # because it was of higher rank\n  def codegen_trunc_binary_op_result(t1, t2, result)\n    if t1.normal_rank != t2.normal_rank && t1.rank < t2.rank\n      result = trunc result, llvm_type(t1)\n    else\n      result\n    end\n  end\n\n  private def codegen_out_of_range(target_type : IntegerType, arg_type : IntegerType, arg)\n    min_value, max_value = target_type.range\n    # arg < min_value || arg > max_value\n    or(\n      codegen_binary_op_lt(arg_type, target_type, arg, int(min_value, target_type)),\n      codegen_binary_op_gt(arg_type, target_type, arg, int(max_value, target_type))\n    )\n  end\n\n  private def codegen_out_of_range(target_type : IntegerType, arg_type : FloatType, arg)\n    min_value, max_value = target_type.range\n    max_value = case arg_type.kind\n                when .f32?\n                  float32_upper_bound(max_value)\n                when .f64?\n                  float64_upper_bound(max_value)\n                else\n                  raise \"BUG: unknown float type\"\n                end\n\n    # we allow one comparison to be unordered so that NaNs are caught\n    # !(arg >= min_value) || arg > max_value\n    or(\n      builder.fcmp(LLVM::RealPredicate::ULT, arg, int_to_float(target_type, arg_type, int(min_value, target_type))),\n      builder.fcmp(LLVM::RealPredicate::OGT, arg, int_to_float(target_type, arg_type, int(max_value, target_type)))\n    )\n  end\n\n  private def float32_upper_bound(int_max_value)\n    case int_max_value\n    when UInt128\n      # `Float32::MAX < UInt128::MAX`, so we use `Float32::MAX` instead as the\n      # upper bound in order to reject positive infinity\n      int_max_value.class.new(Float32::MAX)\n    when Int32, UInt32, Int64, UInt64, Int128\n      # if the float type has fewer bits of precision than the integer type\n      # then the upper bound would mistakenly allow values near the upper limit,\n      # e.g. 2147483647_i32 -> 2147483648_f32, because the bound itself is\n      # rounded to the nearest even-significand number in the `int_to_float`\n      # call above; we choose the predecessor as the upper bound, i.e.\n      # 2147483520_f32, ensuring it is exact when converted back to an integer\n      int_max_value.class.new(int_max_value.to_f32.prev_float)\n    else\n      int_max_value\n    end\n  end\n\n  private def float64_upper_bound(int_max_value)\n    case int_max_value\n    when Int64, UInt64, Int128, UInt128\n      int_max_value.class.new(int_max_value.to_f64.prev_float)\n    else\n      int_max_value\n    end\n  end\n\n  private def codegen_out_of_range(target_type : FloatType, arg_type : IntegerType, arg)\n    if arg_type.kind.u128? && target_type.kind.f32?\n      # since Float32::MAX < UInt128::MAX\n      # the value will be outside of the float range if\n      # arg > Float32::MAX\n      _, max_value = target_type.range\n      max_value_as_int = float_to_int(target_type, arg_type, float(max_value, target_type))\n\n      codegen_binary_op_gt(arg_type, arg_type, arg, max_value_as_int)\n    else\n      # for all other possibilities the integer value fit within the float range\n      llvm_false\n    end\n  end\n\n  private def codegen_out_of_range(target_type : FloatType, arg_type : FloatType, arg)\n    min_value, max_value = target_type.range\n    # checks for arg being outside of range and not infinity\n    # (arg < min_value || arg > max_value) && arg != 2 * arg\n    and(\n      or(\n        builder.fcmp(LLVM::RealPredicate::OLT, arg, float(min_value, arg_type)),\n        builder.fcmp(LLVM::RealPredicate::OGT, arg, float(max_value, arg_type))\n      ),\n      builder.fcmp(LLVM::RealPredicate::ONE, arg, builder.fmul(float(2, arg_type), arg))\n    )\n  end\n\n  private def codegen_raise_overflow\n    location = @call_location\n    set_current_debug_location(location) if location && @debug.line_numbers?\n\n    func = crystal_raise_overflow_fun\n    call_args = [] of LLVM::Value\n\n    if (rescue_block = @rescue_block)\n      invoke_out_block = new_block \"invoke_out\"\n      invoke func, call_args, invoke_out_block, rescue_block\n      position_at_end invoke_out_block\n    else\n      call func, call_args\n    end\n\n    unreachable\n  end\n\n  private def codegen_raise_overflow_cond(overflow_condition)\n    op_overflow = new_block \"overflow\"\n    op_normal = new_block \"normal\"\n\n    overflow_condition = builder.call(llvm_expect_i1_fun.type, llvm_expect_i1_fun.func, [overflow_condition, llvm_false])\n    cond overflow_condition, op_overflow, op_normal\n\n    position_at_end op_overflow\n    codegen_raise_overflow\n\n    position_at_end op_normal\n  end\n\n  private def call_binary_overflow_fun(op, t, p1, p2)\n    llvm_op =\n      case {t.signed?, op}\n      when {false, \"+\"} then \"uadd\"\n      when {false, \"-\"} then \"usub\"\n      when {false, \"*\"} then \"umul\"\n      when {true, \"+\"}  then \"sadd\"\n      when {true, \"-\"}  then \"ssub\"\n      when {true, \"*\"}  then \"smul\"\n      else                   raise \"BUG: unknown overflow op\"\n      end\n\n    fun_name = \"llvm.#{llvm_op}.with.overflow.i#{t.bytes * 8}\"\n\n    llvm_operand_type = llvm_type(t)\n    llvm_fun = fetch_typed_fun(@llvm_mod, fun_name) do\n      LLVM::Type.function(\n        [llvm_operand_type, llvm_operand_type],\n        @llvm_context.struct([llvm_operand_type, @llvm_context.int1]),\n      )\n    end\n    res_with_overflow = builder.call(llvm_fun.type, llvm_fun.func, [p1, p2])\n    result = extract_value res_with_overflow, 0\n    overflow = extract_value res_with_overflow, 1\n    {result, overflow}\n  end\n\n  private def llvm_expect_i1_fun\n    fetch_typed_fun(@llvm_mod, \"llvm.expect.i1\") do\n      LLVM::Type.function([@llvm_context.int1, @llvm_context.int1], @llvm_context.int1)\n    end\n  end\n\n  # The below methods (lt, lte, gt, gte, eq, ne) perform\n  # comparisons on two integers x and y,\n  # where t1, t2 are their types and p1, p2 are their values.\n  #\n  # In LLVM, Int32 and UInt32 are represented as the same type\n  # (i32) and although integer operations have a sign\n  # (SGE, UGE, signed/unsigned greater than or equal)\n  # when we have one signed integer and one unsigned integer\n  # we can't choose a signedness for the operation. In that\n  # case we need to perform some additional checks.\n  #\n  # Equality and inequality operations for integers in LLVM don't have\n  # signedness, they just compare bit patterns. But for example\n  # the Int32 with value -1 and the UInt32 with value\n  # 4294967295 have the same bit pattern, and yet they are not\n  # equal, so again we must perform some additional checks\n  # (mainly, if the signed value is negative then there's\n  # no way they are equal, and for positive values we can\n  # perform the usual bit equality).\n\n  def codegen_binary_op_lt(t1, t2, p1, p2)\n    if t1.signed? == t2.signed?\n      _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n      builder.icmp (t1.signed? ? LLVM::IntPredicate::SLT : LLVM::IntPredicate::ULT), p1, p2\n    else\n      if t1.signed? && t2.unsigned?\n        if t1.bytes > t2.bytes\n          # x < 0 || x < x.class.new(y)\n          or(\n            builder.icmp(LLVM::IntPredicate::SLT, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::SLT, p1, extend_int(t2, t1, p2))\n          )\n        else\n          # x < 0 || y.class.new(x) < y\n          or(\n            builder.icmp(LLVM::IntPredicate::SLT, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::ULT, extend_int(t1, t2, p1), p2)\n          )\n        end\n      else\n        # t1.unsigned? && t2.signed?\n        if t1.bytes < t2.bytes\n          # y >= 0 && y.class.new(x) < y\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::SLT, extend_int(t1, t2, p1), p2)\n          )\n        else\n          # y >= 0 && x < x.class.new(y)\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::ULT, p1, extend_int(t2, t1, p2))\n          )\n        end\n      end\n    end\n  end\n\n  def codegen_binary_op_lte(t1, t2, p1, p2)\n    if t1.signed? == t2.signed?\n      _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n      builder.icmp (t1.signed? ? LLVM::IntPredicate::SLE : LLVM::IntPredicate::ULE), p1, p2\n    else\n      if t1.signed? && t2.unsigned?\n        if t1.bytes > t2.bytes\n          # x <= 0 || x <= x.class.new(y)\n          or(\n            builder.icmp(LLVM::IntPredicate::SLE, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::SLE, p1, extend_int(t2, t1, p2))\n          )\n        else\n          # x <= 0 || y.class.new(x) <= y\n          or(\n            builder.icmp(LLVM::IntPredicate::SLE, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::ULE, extend_int(t1, t2, p1), p2)\n          )\n        end\n      else\n        # t1.unsigned? && t2.signed?\n        if t1.bytes < t2.bytes\n          # y >= 0 && y.class.new(x) <= y\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::SLE, extend_int(t1, t2, p1), p2)\n          )\n        else\n          # y >= 0 && x <= x.class.new(y)\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::ULE, p1, extend_int(t2, t1, p2))\n          )\n        end\n      end\n    end\n  end\n\n  def codegen_binary_op_gt(t1, t2, p1, p2)\n    if t1.signed? == t2.signed?\n      _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n      builder.icmp (t1.signed? ? LLVM::IntPredicate::SGT : LLVM::IntPredicate::UGT), p1, p2\n    else\n      if t1.signed? && t2.unsigned?\n        if t1.bytes > t2.bytes\n          # x >= 0 && x > x.class.new(y)\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::SGT, p1, extend_int(t2, t1, p2))\n          )\n        else\n          # x >= 0 && y.class.new(x) > y\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::UGT, extend_int(t1, t2, p1), p2)\n          )\n        end\n      else\n        # t1.unsigned? && t2.signed?\n        if t1.bytes < t2.bytes\n          # y < 0 || y.class.new(x) > y\n          or(\n            builder.icmp(LLVM::IntPredicate::SLT, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::SGT, extend_int(t1, t2, p1), p2)\n          )\n        else\n          # y < 0 || x > x.class.new(y)\n          or(\n            builder.icmp(LLVM::IntPredicate::SLT, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::UGT, p1, extend_int(t2, t1, p2))\n          )\n        end\n      end\n    end\n  end\n\n  def codegen_binary_op_gte(t1, t2, p1, p2)\n    if t1.signed? == t2.signed?\n      _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n      builder.icmp (t1.signed? ? LLVM::IntPredicate::SGE : LLVM::IntPredicate::UGE), p1, p2\n    else\n      if t1.signed? && t2.unsigned?\n        if t1.bytes > t2.bytes\n          # x >= 0 && x >= x.class.new(y)\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::SGE, p1, extend_int(t2, t1, p2))\n          )\n        else\n          # x >= 0 && y.class.new(x) >= y\n          and(\n            builder.icmp(LLVM::IntPredicate::SGE, p1, int(0, t1)),\n            builder.icmp(LLVM::IntPredicate::UGE, extend_int(t1, t2, p1), p2)\n          )\n        end\n      else\n        # t1.unsigned? && t2.signed?\n        if t1.bytes < t2.bytes\n          # y <= 0 || y.class.new(x) >= y\n          or(\n            builder.icmp(LLVM::IntPredicate::SLE, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::SGE, extend_int(t1, t2, p1), p2)\n          )\n        else\n          # y <= 0 || x >= x.class.new(y)\n          or(\n            builder.icmp(LLVM::IntPredicate::SLE, p2, int(0, t2)),\n            builder.icmp(LLVM::IntPredicate::UGE, p1, extend_int(t2, t1, p2))\n          )\n        end\n      end\n    end\n  end\n\n  def codegen_binary_op_eq(t1, t2, p1, p2)\n    _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n\n    if t1.signed? == t2.signed?\n      builder.icmp(LLVM::IntPredicate::EQ, p1, p2)\n    elsif t1.signed? && t2.unsigned?\n      # x >= 0 && x == y\n      and(\n        builder.icmp(LLVM::IntPredicate::SGE, p1, p1.type.const_int(0)),\n        builder.icmp(LLVM::IntPredicate::EQ, p1, p2)\n      )\n    else # t1.unsigned? && t2.signed?\n      # y >= 0 && x == y\n      and(\n        builder.icmp(LLVM::IntPredicate::SGE, p2, p2.type.const_int(0)),\n        builder.icmp(LLVM::IntPredicate::EQ, p1, p2)\n      )\n    end\n  end\n\n  def codegen_binary_op_ne(t1, t2, p1, p2)\n    _, p1, p2 = codegen_binary_extend_int(t1, t2, p1, p2)\n\n    if t1.signed? == t2.signed?\n      builder.icmp(LLVM::IntPredicate::NE, p1, p2)\n    elsif t1.signed? && t2.unsigned?\n      # x < 0 || x != y\n      or(\n        builder.icmp(LLVM::IntPredicate::SLT, p1, p1.type.const_int(0)),\n        builder.icmp(LLVM::IntPredicate::NE, p1, p2)\n      )\n    else # t1.unsigned? && t2.signed?\n      # y < 0 || x != y\n      or(\n        builder.icmp(LLVM::IntPredicate::SLT, p2, p2.type.const_int(0)),\n        builder.icmp(LLVM::IntPredicate::NE, p1, p2)\n      )\n    end\n  end\n\n  def codegen_binary_op(op, t1 : IntegerType, t2 : FloatType, p1, p2)\n    p1 = codegen_cast(t1, t2, p1)\n    codegen_binary_op(op, t2, t2, p1, p2)\n  end\n\n  def codegen_binary_op(op, t1 : FloatType, t2 : IntegerType, p1, p2)\n    p2 = codegen_cast(t2, t1, p2)\n    codegen_binary_op(op, t1, t1, p1, p2)\n  end\n\n  def codegen_binary_op(op, t1 : FloatType, t2 : FloatType, p1, p2)\n    if t1.rank < t2.rank\n      p1 = extend_float t2, p1\n    elsif t1.rank > t2.rank\n      p2 = extend_float t1, p2\n    end\n\n    @last = case op\n            when \"+\"         then builder.fadd p1, p2\n            when \"-\"         then builder.fsub p1, p2\n            when \"*\"         then builder.fmul p1, p2\n            when \"/\", \"fdiv\" then builder.fdiv p1, p2\n            when \"==\"        then return builder.fcmp LLVM::RealPredicate::OEQ, p1, p2\n            when \"!=\"        then return builder.fcmp LLVM::RealPredicate::UNE, p1, p2\n            when \"<\"         then return builder.fcmp LLVM::RealPredicate::OLT, p1, p2\n            when \"<=\"        then return builder.fcmp LLVM::RealPredicate::OLE, p1, p2\n            when \">\"         then return builder.fcmp LLVM::RealPredicate::OGT, p1, p2\n            when \">=\"        then return builder.fcmp LLVM::RealPredicate::OGE, p1, p2\n            else                  raise \"BUG: trying to codegen #{t1} #{op} #{t2}\"\n            end\n    @last = trunc_float t1, @last if t1.rank < t2.rank\n    @last\n  end\n\n  def codegen_binary_op(op, t1 : TypeDefType, t2, p1, p2)\n    codegen_binary_op(op, t1.remove_typedef, t2, p1, p2)\n  end\n\n  def codegen_binary_op(op, t1, t2 : TypeDefType, p1, p2)\n    codegen_binary_op(op, t1, t2.remove_typedef, p1, p2)\n  end\n\n  def codegen_binary_op(op, t1, t2, p1, p2)\n    raise \"BUG: codegen_binary_op called with #{t1} #{op} #{t2}\"\n  end\n\n  def codegen_primitive_convert(node, target_def, call_args, *, checked : Bool)\n    p1 = call_args[0]\n    from_type, to_type = target_def.owner, target_def.type\n    codegen_convert(from_type, to_type, p1, checked: checked)\n  end\n\n  def codegen_convert(from_type : IntegerType, to_type : IntegerType, arg, *, checked : Bool)\n    case\n    when from_type.normal_rank == to_type.normal_rank\n      # if the normal_rank is the same (eg: UInt64 / Int64)\n      # there is still chance for overflow\n      if from_type.kind != to_type.kind && checked\n        overflow = codegen_out_of_range(to_type, from_type, arg)\n        codegen_raise_overflow_cond(overflow)\n      end\n      arg\n    when from_type.rank < to_type.rank\n      # extending a signed integer to an unsigned one (eg: Int8 to UInt16)\n      # may still lead to underflow\n      if checked\n        if from_type.signed? && to_type.unsigned?\n          overflow = codegen_out_of_range(to_type, from_type, arg)\n          codegen_raise_overflow_cond(overflow)\n        end\n      end\n      extend_int from_type, to_type, arg\n    else\n      if checked\n        overflow = codegen_out_of_range(to_type, from_type, arg)\n        codegen_raise_overflow_cond(overflow)\n      end\n      trunc arg, llvm_type(to_type)\n    end\n  end\n\n  def codegen_convert(from_type : IntegerType, to_type : FloatType, arg, *, checked : Bool)\n    if checked\n      if from_type.kind.u128? && to_type.kind.f32?\n        overflow = codegen_out_of_range(to_type, from_type, arg)\n        codegen_raise_overflow_cond(overflow)\n      end\n    end\n    int_to_float from_type, to_type, arg\n  end\n\n  def codegen_convert(from_type : FloatType, to_type : IntegerType, arg, *, checked : Bool)\n    if checked\n      overflow = codegen_out_of_range(to_type, from_type, arg)\n      codegen_raise_overflow_cond(overflow)\n    end\n    float_to_int from_type, to_type, arg\n  end\n\n  def codegen_convert(from_type : FloatType, to_type : FloatType, arg, *, checked : Bool)\n    case\n    when from_type.rank < to_type.rank\n      extend_float to_type, arg\n    when from_type.rank > to_type.rank\n      if checked\n        overflow = codegen_out_of_range(to_type, from_type, arg)\n        codegen_raise_overflow_cond(overflow)\n      end\n      trunc_float to_type, arg\n    else\n      arg\n    end\n  end\n\n  def codegen_convert(from_type : IntegerType, to_type : CharType, arg, *, checked : Bool)\n    codegen_convert from_type, @program.int32, arg, checked: checked\n  end\n\n  def codegen_convert(from_type : CharType, to_type : IntegerType, arg, *, checked : Bool)\n    builder.zext arg, llvm_type(to_type)\n  end\n\n  def codegen_convert(from_type : SymbolType, to_type : IntegerType, arg, *, checked : Bool)\n    arg\n  end\n\n  def codegen_convert(from_type : TypeDefType, to_type, arg, *, checked : Bool)\n    codegen_convert from_type.remove_typedef, to_type, arg, checked: checked\n  end\n\n  def codegen_convert(from_type, to_type, arg, *, checked : Bool)\n    raise \"BUG: codegen_convert called from #{from_type} to #{to_type}\"\n  end\n\n  def codegen_cast(from_type, to_type, arg)\n    codegen_convert(from_type, to_type, arg, checked: false)\n  end\n\n  def codegen_primitive_allocate(node, target_def, call_args)\n    type = node.type\n\n    base_type = type.is_a?(VirtualType) ? type.base_type : type\n\n    allocate_aggregate base_type\n\n    if type.is_a?(VirtualType)\n      @last = upcast(@last, type, base_type)\n    end\n\n    @last\n  end\n\n  def codegen_primitive_pre_initialize(node, target_def, call_args)\n    type = context.type.instance_type\n\n    base_type = type.is_a?(VirtualType) ? type.base_type : type\n\n    ptr = call_args[target_def.owner.passed_as_self? ? 1 : 0]\n    pre_initialize_aggregate base_type, llvm_struct_type(base_type), ptr\n\n    @last = type.struct? ? llvm_nil : cast_to ptr, type\n  end\n\n  def codegen_primitive_pointer_malloc(node, target_def, call_args)\n    type = node.type.as(PointerInstanceType)\n    llvm_type = llvm_embedded_type(type.element_type)\n\n    old_debug_location = @current_debug_location\n    if @debug.line_numbers? && (location = node.location)\n      set_current_debug_location(location)\n    end\n\n    if type.element_type.has_inner_pointers?\n      last = array_malloc(llvm_type, call_args[1])\n    else\n      last = array_malloc_atomic(llvm_type, call_args[1])\n    end\n\n    if @debug.line_numbers?\n      set_current_debug_location(old_debug_location)\n    end\n\n    last\n  end\n\n  def codegen_primitive_pointer_set(node, target_def, call_args)\n    type = context.type.remove_typedef.as(PointerInstanceType)\n\n    # Assigning to a Pointer(Void) has no effect\n    return llvm_nil if type.element_type.void?\n\n    value = call_args[1]\n    assign call_args[0], type.element_type, node.type, value\n    value\n  end\n\n  def codegen_primitive_pointer_get(node, target_def, call_args)\n    type = context.type.remove_typedef.as(PointerInstanceType)\n    to_lhs call_args[0], type.element_type\n  end\n\n  def codegen_primitive_pointer_address(node, target_def, call_args)\n    ptr2int call_args[0], llvm_context.int64\n  end\n\n  def codegen_primitive_pointer_new(node, target_def, call_args)\n    int2ptr(call_args[1], llvm_type(node.type))\n  end\n\n  def codegen_primitive_pointer_realloc(node, target_def, call_args)\n    type = context.type.remove_typedef.as(PointerInstanceType)\n\n    casted_ptr = cast_to_void_pointer(call_args[0])\n    size = builder.mul call_args[1], llvm_size(type.element_type)\n    reallocated_ptr = realloc casted_ptr, size\n    cast_to_pointer reallocated_ptr, type.element_type\n  end\n\n  def codegen_primitive_pointer_add(node, target_def, call_args)\n    type = context.type.remove_typedef.as(PointerInstanceType)\n\n    # `llvm_embedded_type` needed to treat `Void*` like `UInt8*`\n    gep llvm_embedded_type(type.element_type), call_args[0], call_args[1]\n  end\n\n  def struct_field_ptr(type, field_name, pointer)\n    index = type.index_of_instance_var('@' + field_name).not_nil!\n    aggregate_index llvm_type(type), pointer, index\n  end\n\n  def codegen_primitive_struct_or_union_set(node, target_def, call_args)\n    set_aggregate_field(node, target_def, call_args) do |field_type|\n      type = context.type.remove_typedef.as(NonGenericClassType)\n      if type.extern_union?\n        union_field_ptr(type, field_type, call_args[0])\n      else\n        name = target_def.name.rchop\n        struct_field_ptr(type, name, call_args[0])\n      end\n    end\n  end\n\n  def set_aggregate_field(node, target_def, call_args, &)\n    call_arg = call_args[1]\n    original_call_arg = call_arg\n\n    # Check if we need to do a numeric conversion\n    if (extra = node.extra)\n      existing_value = context.vars[\"value\"]?\n      context.vars[\"value\"] = LLVMVar.new(call_arg, node.type, true)\n      request_value(extra)\n      call_arg = @last\n      context.vars[\"value\"] = existing_value if existing_value\n    end\n\n    var_name = '@' + target_def.name.rchop\n    scope = context.type.remove_typedef.as(NonGenericClassType)\n    field_type = scope.instance_vars[var_name].type\n\n    # Check assigning nil to a field of type pointer or Proc\n    if node.type.nil_type? && (field_type.pointer? || field_type.proc?)\n      call_arg = llvm_c_type(field_type).null\n    elsif field_type.proc?\n      call_arg = check_proc_is_not_closure(call_arg, field_type)\n    end\n\n    value = to_rhs call_arg, field_type\n    store value, yield(field_type)\n\n    original_call_arg\n  end\n\n  def union_field_ptr(union_type, field_type, pointer)\n    ptr = aggregate_index llvm_type(union_type), pointer, 0\n    if field_type.is_a?(ProcInstanceType)\n      pointer_cast ptr, @llvm_typer.proc_type(field_type).pointer.pointer\n    else\n      cast_to_pointer ptr, field_type\n    end\n  end\n\n  def codegen_primitive_external_var_set(node, target_def, call_args)\n    external = target_def.as(External)\n    name = external.real_name\n    var = declare_lib_var name, node.type, external.thread_local?\n\n    @last = extern_to_rhs(call_args[0], external.type)\n\n    store @last, var\n\n    @last = check_c_fun node.type, @last\n\n    @last\n  end\n\n  def codegen_primitive_external_var_get(node, target_def, call_args)\n    external = target_def.as(External)\n    var = get_external_var(external)\n\n    @last = extern_to_lhs(var, external.type)\n\n    @last = check_c_fun node.type, @last\n\n    @last\n  end\n\n  def get_external_var(external)\n    name = external.as(External).real_name\n    declare_lib_var name, external.type, external.thread_local?\n  end\n\n  def codegen_primitive_object_id(node, external, call_args)\n    ptr2int call_args[0], llvm_context.int64\n  end\n\n  def codegen_primitive_object_crystal_type_id(node, target_def, call_args)\n    if context.type.is_a?(MetaclassType)\n      type_id(type)\n    else\n      type_id(call_args[0], type)\n    end\n  end\n\n  def codegen_primitive_class_crystal_instance_type_id(node, target_def, call_args)\n    type_id(context.type.instance_type)\n  end\n\n  def codegen_primitive_symbol_to_s(node, target_def, call_args)\n    string = llvm_type(@program.string)\n    table_type = string.array(@symbol_table_values.size)\n    table = define_symbol_table(@llvm_mod, @llvm_typer)\n    string_ptr = gep table_type, table, int(0), call_args[0]\n    load(string, string_ptr)\n  end\n\n  def codegen_primitive_class(node, target_def, call_args)\n    value = call_args.first?\n    if value\n      codegen_primitive_class_with_type(type, value)\n    else\n      type_id(node.type)\n    end\n  end\n\n  def codegen_primitive_class_with_type(type : VirtualType, value)\n    type_id = type_id(value, type)\n    metaclass_fun_name = \"~metaclass\"\n    func = typed_fun?(@main_mod, metaclass_fun_name) || create_metaclass_fun(metaclass_fun_name)\n    func = check_main_fun metaclass_fun_name, func\n    call func, [type_id] of LLVM::Value\n  end\n\n  def create_metaclass_fun(name)\n    id_to_metaclass = @program.llvm_id.id_to_metaclass.to_a.sort_by! &.[0]\n\n    in_main do\n      define_main_function(name, ([llvm_context.int32]), llvm_context.int32) do |func|\n        set_internal_fun_debug_location(func, name)\n\n        arg = func.params.first\n\n        current_block = insert_block\n\n        cases = {} of LLVM::Value => LLVM::BasicBlock\n        id_to_metaclass.each do |(type_id, metaclass_id)|\n          block = new_block \"type_#{type_id}\"\n          cases[int32(type_id)] = block\n          position_at_end block\n          ret int32(metaclass_id)\n        end\n\n        otherwise = new_block \"otherwise\"\n        position_at_end otherwise\n        unreachable\n\n        position_at_end current_block\n        @builder.switch arg, otherwise, cases\n      end\n    end\n  end\n\n  def codegen_primitive_class_with_type(type : Type, value)\n    type_id(type.metaclass)\n  end\n\n  def codegen_primitive_proc_call(node, target_def, call_args)\n    location = @call_location\n    set_current_debug_location(location) if location && @debug.line_numbers?\n\n    closure_ptr = call_args[0]\n\n    # For non-closure args we use byval attribute and other things\n    # that the C ABI dictates, if needed (args).\n    # Otherwise we load the values (closure_args).\n    args = call_args[1..-1]\n    closure_args = Array(LLVM::Value).new(args.size + 1)\n\n    c_calling_convention = target_def.proc_c_calling_convention?\n\n    proc_type = context.type.remove_typedef.as(ProcInstanceType)\n    target_def.args.size.times do |i|\n      proc_arg_type = proc_type.arg_types[i]\n      target_def_arg_type = target_def.args[i].type\n      args[i] = arg = upcast args[i], proc_arg_type, target_def_arg_type\n      closure_args << to_rhs(arg, proc_arg_type)\n    end\n\n    fun_ptr = builder.extract_value closure_ptr, 0\n    ctx_ptr = builder.extract_value closure_ptr, 1\n\n    ctx_is_null_block = new_block \"ctx_is_null\"\n    ctx_is_not_null_block = new_block \"ctx_is_not_null\"\n\n    ctx_is_null = equal? ctx_ptr, llvm_context.void_pointer.null\n    cond ctx_is_null, ctx_is_null_block, ctx_is_not_null_block\n\n    Phi.open(self, node, @needs_value) do |phi|\n      position_at_end ctx_is_null_block\n      real_fun_llvm_type = llvm_proc_type(context.type)\n      real_fun_ptr = pointer_cast fun_ptr, real_fun_llvm_type.pointer\n\n      # When invoking a Proc that has extern structs as arguments or return type, it's tricky:\n      # closures are never generated with C ABI because C doesn't support closures.\n      # But non-closures use C ABI, so if the target Proc is not a closure we cast the\n      # arguments according to the ABI.\n      # For this we temporarily set the target_def's `abi_info` and `c_calling_convention`\n      # properties for the non-closure branch, and then reset it.\n      old_abi_info = target_def.abi_info?\n      old_c_calling_convention = target_def.c_calling_convention\n\n      if c_calling_convention\n        null_fun_ptr, null_fun_llvm_type, null_args = codegen_extern_primitive_proc_call(target_def, args, fun_ptr)\n      else\n        null_fun_ptr, null_fun_llvm_type, null_args = real_fun_ptr, real_fun_llvm_type, closure_args\n      end\n      null_fun = LLVMTypedFunction.new(null_fun_llvm_type, LLVM::Function.from_value(null_fun_ptr))\n\n      value = codegen_call_or_invoke(node, target_def, nil, null_fun, null_args, true, target_def.type, false, proc_type)\n      phi.add value, node.type\n\n      # Reset abi_info + c_calling_convention so the closure part is generated as usual\n      target_def.abi_info = false\n      target_def.c_calling_convention = nil\n\n      position_at_end ctx_is_not_null_block\n      real_fun_llvm_type = llvm_closure_type(context.type)\n      real_fun_ptr = pointer_cast fun_ptr, real_fun_llvm_type.pointer\n      real_fun = LLVMTypedFunction.new(real_fun_llvm_type, LLVM::Function.from_value(real_fun_ptr))\n      closure_args.insert(0, ctx_ptr)\n      value = codegen_call_or_invoke(node, target_def, nil, real_fun, closure_args, true, target_def.type, true, proc_type)\n      phi.add value, node.type, true\n\n      target_def.abi_info = old_abi_info\n      target_def.c_calling_convention = old_c_calling_convention\n    end\n  end\n\n  def codegen_extern_primitive_proc_call(target_def, args, fun_ptr)\n    null_fun_types = [] of LLVM::Type\n\n    null_args = [] of LLVM::Value\n    abi_info = abi_info(target_def)\n\n    if abi_info.return_type.attr == LLVM::Attribute::StructRet\n      sret_value = @sret_value = alloca abi_info.return_type.type\n      null_args << sret_value\n      null_fun_types << abi_info.return_type.type.pointer\n      null_fun_return_type = llvm_context.void\n    else\n      if cast = abi_info.return_type.cast\n        null_fun_return_type = cast\n      else\n        null_fun_return_type = abi_info.return_type.type\n      end\n    end\n\n    target_def.args.each_with_index do |arg, index|\n      call_arg = args[index]\n\n      abi_arg_type = abi_info.arg_types[index]\n      case abi_arg_type.kind\n      in .direct?\n        call_arg = codegen_direct_abi_call(arg.type, call_arg, abi_arg_type)\n        if cast = abi_arg_type.cast\n          null_fun_types << cast\n        else\n          null_fun_types << abi_arg_type.type\n        end\n        null_args << call_arg\n      in .indirect?\n        # Pass argument as is (will be passed byval)\n        null_args << call_arg\n        null_fun_types << abi_arg_type.type.pointer\n      in .ignore?\n        # Ignore\n      end\n    end\n\n    null_fun_llvm_type = LLVM::Type.function(null_fun_types, null_fun_return_type)\n    null_fun_ptr = pointer_cast fun_ptr, null_fun_llvm_type.pointer\n    target_def.c_calling_convention = true\n\n    {null_fun_ptr, null_fun_llvm_type, null_args}\n  end\n\n  def codegen_primitive_pointer_diff(node, target_def, call_args)\n    type = context.type.remove_typedef.as(PointerInstanceType)\n    p0 = ptr2int(call_args[0], llvm_context.int64)\n    p1 = ptr2int(call_args[1], llvm_context.int64)\n    sub = builder.sub p0, p1\n    # `llvm_embedded_type` needed to treat `Void*` like `UInt8*`\n    offsetted = gep(llvm_embedded_type(type.element_type), call_args[0].type.null_pointer, 1)\n    builder.exact_sdiv sub, ptr2int(offsetted, llvm_context.int64)\n  end\n\n  def codegen_primitive_tuple_indexer_known_index(node, target_def, call_args)\n    index = node.as(TupleIndexer).index\n    codegen_tuple_indexer(context.type, call_args[0], index)\n  end\n\n  def codegen_tuple_indexer(type, value, index : Range)\n    case type\n    when TupleInstanceType\n      struct_type = llvm_type(type)\n      tuple_types = type.tuple_types[index].map &.as(Type)\n      allocate_tuple(@program.tuple_of(tuple_types).as(TupleInstanceType)) do |tuple_type, i|\n        ptr = aggregate_index struct_type, value, index.begin + i\n        tuple_value = to_lhs ptr, tuple_type\n        {tuple_type, tuple_value}\n      end\n    else\n      type = type.instance_type\n      case type\n      when TupleInstanceType\n        type_id(@program.tuple_of(type.tuple_types[index].map &.as(Type)).metaclass)\n      else\n        raise \"BUG: unsupported codegen for tuple_indexer\"\n      end\n    end\n  end\n\n  def codegen_tuple_indexer(type, value, index : Int32)\n    case type\n    when TupleInstanceType\n      ptr = aggregate_index llvm_type(type), value, index\n      to_lhs ptr, type.tuple_types[index]\n    when NamedTupleInstanceType\n      ptr = aggregate_index llvm_type(type), value, index\n      to_lhs ptr, type.entries[index].type\n    else\n      type = type.instance_type\n      case type\n      when TupleInstanceType\n        type_id(type.tuple_types[index].as(Type).metaclass)\n      when NamedTupleInstanceType\n        type_id(type.entries[index].type.as(Type).metaclass)\n      else\n        raise \"BUG: unsupported codegen for tuple_indexer\"\n      end\n    end\n  end\n\n  def check_c_fun(type, value)\n    if type.proc?\n      make_fun(type, cast_to_void_pointer(value), llvm_context.void_pointer.null)\n    else\n      value\n    end\n  end\n\n  def codegen_primitive_cmpxchg(call, node, target_def, call_args)\n    call = check_atomic_call(call, target_def)\n    ptr, cmp, new, success_ordering, failure_ordering = call_args\n\n    success_node = call.args[-2]\n    failure_node = call.args[-1]\n    success_ordering = atomic_ordering_get_const(success_node, success_ordering)\n    failure_ordering = atomic_ordering_get_const(failure_node, failure_ordering)\n    validate_atomic_cmpxchg_ordering(success_node, success_ordering, failure_node, failure_ordering)\n\n    value = builder.cmpxchg(ptr, cmp, new, success_ordering, failure_ordering)\n    value_type = node.type.as(TupleInstanceType)\n    struct_type = llvm_type(value_type)\n    value_ptr = alloca struct_type\n    store extract_value(value, 0), gep(struct_type, value_ptr, 0, 0)\n    store extract_value(value, 1), gep(struct_type, value_ptr, 0, 1)\n    value_ptr\n  end\n\n  def codegen_primitive_atomicrmw(call, node, target_def, call_args)\n    call = check_atomic_call(call, target_def)\n    op, ptr, val, ordering, _ = call_args\n\n    op = atomicrwm_bin_op_get_const(call.args[0], op)\n    ordering = atomic_ordering_get_const(call.args[-2], ordering)\n    singlethread = bool_from_bool_literal(call.args[-1])\n\n    builder.atomicrmw(op, ptr, val, ordering, singlethread)\n  end\n\n  def codegen_primitive_fence(call, node, target_def, call_args)\n    call = check_atomic_call(call, target_def)\n    ordering, _ = call_args\n\n    ordering_node = call.args[0]\n    ordering = atomic_ordering_get_const(ordering_node, ordering)\n    singlethread = bool_from_bool_literal(call.args[1])\n\n    ordering_node.raise \"must be atomic\" if ordering.not_atomic?\n    ordering_node.raise \"cannot be unordered\" if ordering.unordered?\n    ordering_node.raise \"must have acquire, release, acquire_release or sequentially_consistent ordering\" if ordering.monotonic?\n\n    builder.fence(ordering, singlethread)\n    llvm_nil\n  end\n\n  def codegen_primitive_load_atomic(call, node, target_def, call_args)\n    call = check_atomic_call(call, target_def)\n    ptr, ordering, _ = call_args\n\n    ordering = atomic_ordering_get_const(call.args[-2], ordering)\n    volatile = bool_from_bool_literal(call.args[-1])\n\n    inst = builder.load(llvm_type(node.type), ptr)\n    inst.ordering = ordering\n    inst.volatile = true if volatile\n    set_alignment inst, node.type\n    inst\n  end\n\n  def codegen_primitive_store_atomic(call, node, target_def, call_args)\n    call = check_atomic_call(call, target_def)\n    ptr, value, ordering, _ = call_args\n\n    ordering = atomic_ordering_get_const(call.args[-2], ordering)\n    volatile = bool_from_bool_literal(call.args[-1])\n\n    inst = builder.store(value, ptr)\n    inst.ordering = ordering\n    inst.volatile = true if volatile\n    set_alignment inst, node.type\n    inst\n  end\n\n  def codegen_va_arg(call, node, target_def, call_args)\n    ptr = call_args.first\n    builder.va_arg(ptr, llvm_type(node.type))\n  end\n\n  def check_atomic_call(call, target_def)\n    # This could only happen when taking a proc pointer to an atomic\n    # primitive: it could be fixed but it's probably not important for now.\n    if call.nil?\n      target_def.raise \"can't take proc pointer of atomic call\"\n    end\n\n    call\n  end\n\n  def set_alignment(inst, type)\n    case type\n    when IntegerType, FloatType\n      inst.alignment = type.bytes\n    when CharType\n      inst.alignment = 4\n    else\n      inst.alignment = @program.bits64? ? 8 : 4\n    end\n  end\n\n  def atomic_ordering_get_const(node, llvm_arg)\n    node.raise \"atomic ordering must be a constant\" unless llvm_arg.constant?\n\n    if node.type.implements?(@program.enum) && llvm_arg.type.kind.integer? && llvm_arg.type.int_width == 32\n      # any `Int32` enum will do, it is up to `Atomic::Ops` to use appropriate\n      # parameter restrictions so that things don't go bad\n      LLVM::AtomicOrdering.new(llvm_arg.const_int_get_sext_value.to_i32!)\n    elsif node.is_a?(SymbolLiteral)\n      # TODO: remove once support for 1.4 is dropped\n      op = LLVM::AtomicOrdering.parse?(node.value)\n      unless op\n        node.raise \"unknown atomic ordering: #{node.value}\"\n      end\n      op\n    else\n      node.raise \"BUG: unknown atomic ordering: #{node}\"\n    end\n  end\n\n  def atomicrwm_bin_op_get_const(node, llvm_arg)\n    node.raise \"atomic rwm bin op must be a constant\" unless llvm_arg.constant?\n\n    if node.type.implements?(@program.enum) && llvm_arg.type.kind.integer? && llvm_arg.type.int_width == 32\n      LLVM::AtomicRMWBinOp.new(llvm_arg.const_int_get_sext_value.to_i32!)\n    elsif node.is_a?(SymbolLiteral)\n      op = LLVM::AtomicRMWBinOp.parse?(node.value)\n      unless op\n        node.raise \"unknown atomic rwm bin op: #{node.value}\"\n      end\n      op\n    else\n      node.raise \"BUG: unknown atomic rwm bin op: #{node}\"\n    end\n  end\n\n  def validate_atomic_cmpxchg_ordering(success_node, success_ordering, failure_node, failure_ordering)\n    success_node.raise \"must be atomic\" if success_ordering.not_atomic?\n    success_node.raise \"cannot be unordered\" if success_ordering.unordered?\n\n    failure_node.raise \"must be atomic\" if failure_ordering.not_atomic?\n    failure_node.raise \"cannot be unordered\" if failure_ordering.unordered?\n    failure_node.raise \"cannot include release semantics\" if failure_ordering.release? || failure_ordering.acquire_release?\n\n    {% if LibLLVM::IS_LT_130 %}\n      # Atomic(T) macros enforce this rule to provide a consistent public API\n      # regardless of which LLVM version crystal was compiled with. The compiler,\n      # however, only needs to make sure that the codegen is correct for the LLVM\n      # version\n      failure_node.raise \"shall be no stronger than success ordering\" if failure_ordering > success_ordering\n    {% end %}\n  end\n\n  def bool_from_bool_literal(node)\n    unless node.is_a?(BoolLiteral)\n      node.raise \"BUG: expected bool literal\"\n    end\n\n    node.value\n  end\n\n  def void_ptr_type_descriptor\n    void_ptr_type_descriptor_name = \"\\u{1}??_R0PEAX@8\"\n\n    if existing = @llvm_mod.globals[void_ptr_type_descriptor_name]?\n      return existing\n    end\n\n    type_descriptor = llvm_context.struct([\n      llvm_context.void_pointer.pointer,\n      llvm_context.void_pointer,\n      llvm_context.int8.array(6),\n    ])\n\n    if !@main_mod.globals[void_ptr_type_descriptor_name]?\n      base_type_descriptor = external_constant(llvm_context.void_pointer, \"\\u{1}??_7type_info@@6B@\")\n\n      # .PEAX is void*\n      void_ptr_type_descriptor = @main_mod.globals.add(\n        type_descriptor, void_ptr_type_descriptor_name)\n      void_ptr_type_descriptor.initializer = llvm_context.const_struct [\n        base_type_descriptor,\n        llvm_context.void_pointer.null,\n        llvm_context.const_string(\".PEAX\"),\n      ]\n    end\n\n    # if @llvm_mod == @main_mod, this will find the previously created void_ptr_type_descriptor\n    external_constant(type_descriptor, void_ptr_type_descriptor_name)\n  end\n\n  def void_ptr_throwinfo\n    void_ptr_throwinfo_name = \"_TI1PEAX\"\n\n    if existing = @llvm_mod.globals[void_ptr_throwinfo_name]?\n      return existing\n    end\n\n    eh_throwinfo = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32])\n\n    if !@main_mod.globals[void_ptr_throwinfo_name]?\n      catchable_type = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32])\n      void_ptr_catchable_type = @main_mod.globals.add(\n        catchable_type, \"_CT??_R0PEAX@88\")\n      void_ptr_catchable_type.initializer = llvm_context.const_struct [\n        int32(1),\n        sub_image_base(void_ptr_type_descriptor),\n        int32(0),\n        int32(-1),\n        int32(0),\n        int32(8),\n        int32(0),\n      ]\n\n      catchable_type_array = llvm_context.struct([llvm_context.int32, llvm_context.int32.array(1)])\n      catchable_void_ptr = @main_mod.globals.add(\n        catchable_type_array, \"_CTA1PEAX\")\n      catchable_void_ptr.initializer = llvm_context.const_struct [\n        int32(1),\n        llvm_context.int32.const_array([sub_image_base(void_ptr_catchable_type)]),\n      ]\n\n      void_ptr_throwinfo = @main_mod.globals.add(\n        eh_throwinfo, void_ptr_throwinfo_name)\n      void_ptr_throwinfo.initializer = llvm_context.const_struct [\n        int32(0),\n        int32(0),\n        int32(0),\n        sub_image_base(catchable_void_ptr),\n      ]\n    end\n\n    # if @llvm_mod == @main_mod, this will find the previously created void_ptr_throwinfo\n    external_constant(eh_throwinfo, void_ptr_throwinfo_name)\n  end\n\n  def external_constant(type, name)\n    @llvm_mod.globals[name]? || begin\n      c = @llvm_mod.globals.add(type, name)\n      c.global_constant = true\n      c\n    end\n  end\n\n  def sub_image_base(value)\n    image_base = external_constant(llvm_context.int8, \"__ImageBase\")\n\n    @builder.trunc(\n      @builder.sub(\n        @builder.ptr2int(value, llvm_context.int64),\n        @builder.ptr2int(image_base, llvm_context.int64)),\n      llvm_context.int32)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/target.cr",
    "content": "require \"llvm\"\nrequire \"../error\"\n\nclass Crystal::Codegen::Target\n  class Error < Crystal::Error\n  end\n\n  getter architecture : String\n  getter vendor : String\n  getter environment : String\n\n  def initialize(target_triple : String)\n    # Let LLVM convert the user-inputted target triple into at least a target\n    # triple with the architecture, vendor and OS in the correct place.\n    target_triple = LLVM.normalize_triple(target_triple.downcase)\n\n    if target_triple.count('-') < 2\n      raise Target::Error.new(\"Invalid target triple: #{target_triple}\")\n    end\n    @architecture, @vendor, @environment = target_triple.split('-', 3)\n\n    # Perform additional normalization and parsing\n    case @architecture\n    when \"i486\", \"i586\", \"i686\"\n      @architecture = \"i386\"\n    when \"amd64\"\n      @architecture = \"x86_64\"\n    when \"arm64\"\n      @architecture = \"aarch64\"\n    when .starts_with?(\"arm\")\n      @architecture = \"arm\"\n    else\n      # no need to tweak the architecture\n    end\n\n    if linux? && environment_parts.size == 1\n      case @vendor\n      when \"suse\", \"redhat\", \"slackware\", \"amazon\", \"unknown\", \"montavista\", \"mti\"\n        # Build string instead of setting it as \"linux-gnu\"\n        # since \"linux6E\" & \"linuxspe\" are available.\n        @environment = \"#{@environment}-gnu\"\n      else\n        # no need to tweak the environment\n      end\n    end\n  end\n\n  def environment_parts\n    @environment.split('-')\n  end\n\n  def pointer_bit_width\n    case @architecture\n    when \"aarch64\", \"x86_64\"\n      64\n    when \"arm\", \"i386\", \"wasm32\"\n      32\n    when \"avr\"\n      16\n    else\n      raise \"BUG: unknown Target#pointer_bit_width for #{@architecture} target architecture\"\n    end\n  end\n\n  def size_bit_width\n    case @architecture\n    when \"aarch64\", \"x86_64\"\n      64\n    when \"arm\", \"i386\", \"wasm32\"\n      32\n    when \"avr\"\n      16\n    else\n      raise \"BUG: unknown Target#size_bit_width for #{@architecture} target architecture\"\n    end\n  end\n\n  def os_name\n    case self\n    when .macos?\n      \"darwin\"\n    when .freebsd?\n      \"freebsd\"\n    when .dragonfly?\n      \"dragonfly\"\n    when .openbsd?\n      \"openbsd\"\n    when .netbsd?\n      \"netbsd\"\n    when .solaris?\n      \"solaris\"\n    when .android?\n      \"android\"\n    else\n      environment\n    end\n  end\n\n  def executable_extension\n    case\n    when windows? then \".exe\"\n    when avr?     then \".elf\"\n    else               \"\"\n    end\n  end\n\n  def object_extension\n    case\n    when windows?                  then \".obj\"\n    when @architecture == \"wasm32\" then \".wasm\"\n    else                                \".o\"\n    end\n  end\n\n  def macos?\n    @environment.starts_with?(\"darwin\") || @environment.starts_with?(\"macos\")\n  end\n\n  def freebsd?\n    @environment.starts_with?(\"freebsd\")\n  end\n\n  def freebsd_version\n    if @environment =~ /freebsd(\\d+)\\.\\d+/\n      $1.to_i\n    else\n      nil\n    end\n  end\n\n  def dragonfly?\n    @environment.starts_with?(\"dragonfly\")\n  end\n\n  def openbsd?\n    @environment.starts_with?(\"openbsd\")\n  end\n\n  def netbsd?\n    @environment.starts_with?(\"netbsd\")\n  end\n\n  def android?\n    environment_parts.any? &.starts_with?(\"android\")\n  end\n\n  def linux?\n    @environment.starts_with?(\"linux\")\n  end\n\n  def solaris?\n    @environment.starts_with?(\"solaris\")\n  end\n\n  def wasi?\n    @environment.starts_with?(\"wasi\")\n  end\n\n  def bsd?\n    freebsd? || netbsd? || openbsd? || dragonfly?\n  end\n\n  def unix?\n    macos? || bsd? || linux? || wasi? || solaris?\n  end\n\n  def gnu?\n    environment_parts.any? &.in?(\"gnu\", \"gnueabi\", \"gnueabihf\")\n  end\n\n  def musl?\n    environment_parts.any? &.in?(\"musl\", \"musleabi\", \"musleabihf\")\n  end\n\n  def windows?\n    @environment.starts_with?(\"win32\") || @environment.starts_with?(\"windows\")\n  end\n\n  def msvc?\n    windows? && environment_parts.includes?(\"msvc\")\n  end\n\n  def win32?\n    windows? && (msvc? || gnu?)\n  end\n\n  def armhf?\n    environment_parts.any? &.in?(\"gnueabihf\", \"musleabihf\")\n  end\n\n  def avr?\n    @architecture == \"avr\"\n  end\n\n  def embedded?\n    environment_parts.any? { |part| part == \"eabi\" || part == \"eabihf\" }\n  end\n\n  def to_target_machine(cpu = \"\", features = \"\", optimization_mode = Compiler::OptimizationMode::O0,\n                        code_model = LLVM::CodeModel::Default) : LLVM::TargetMachine\n    case @architecture\n    when \"i386\", \"x86_64\"\n      LLVM.init_x86\n    when \"aarch64\"\n      LLVM.init_aarch64\n    when \"arm\"\n      LLVM.init_arm\n\n      # Enable most conservative FPU for hard-float capable targets, unless a\n      # CPU is defined (it will most certainly enable a better FPU) or\n      # features contains a floating-point definition.\n      if cpu.empty? && !features.includes?(\"fp\") && armhf?\n        features += \"+vfp2\"\n      end\n    when \"avr\"\n      LLVM.init_avr\n\n      if cpu.blank?\n        # the ABI call convention, codegen and the linker need to known the CPU model\n        raise Target::Error.new(\"AVR targets must declare a CPU model, for example --mcpu=atmega328p\")\n      end\n    when \"wasm32\"\n      LLVM.init_webassembly\n    else\n      raise Target::Error.new(\"Unsupported architecture for target triple: #{self}\")\n    end\n\n    opt_level = case optimization_mode\n                in .o3?             then LLVM::CodeGenOptLevel::Aggressive\n                in .o2?, .os?, .oz? then LLVM::CodeGenOptLevel::Default\n                in .o1?             then LLVM::CodeGenOptLevel::Less\n                in .o0?             then LLVM::CodeGenOptLevel::None\n                end\n\n    if embedded?\n      reloc = LLVM::RelocMode::Static\n    else\n      reloc = LLVM::RelocMode::PIC\n    end\n\n    target = LLVM::Target.from_triple(self.to_s)\n    machine = target.create_target_machine(self.to_s, cpu: cpu, features: features, opt_level: opt_level, reloc: reloc, code_model: code_model).not_nil!\n    # FIXME: We need to disable global isel until https://reviews.llvm.org/D80898 is released,\n    # or we fixed generating values for 0 sized types.\n    # When removing this, also remove it from the ABI specs and jit compiler.\n    # See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270\n    # for background info\n    machine.enable_global_isel = false\n    machine\n  end\n\n  def to_s(io : IO) : Nil\n    io << architecture << '-' << vendor << '-' << environment\n  end\n\n  def ==(other : self)\n    return false unless architecture == other.architecture\n\n    # If any vendor is unknown, we can skip it. But if both are known, they must\n    # match.\n    if vendor != \"unknown\" && other.vendor != \"unknown\"\n      return false unless vendor == other.vendor\n    end\n\n    environment == other.environment\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/type_id.cr",
    "content": "require \"./codegen\"\n\nclass Crystal::CodeGenVisitor\n  def type_id(value, type)\n    type_id_impl(value, type.remove_indirection)\n  end\n\n  def type_id(type)\n    type_id_impl(type.remove_indirection)\n  end\n\n  private def type_id_impl(value, type : NilableType)\n    builder.select null_pointer?(value), type_id(@program.nil), type_id(type.not_nil_type)\n  end\n\n  private def type_id_impl(value, type : ReferenceUnionType)\n    load(llvm_context.int32, value)\n  end\n\n  private def type_id_impl(value, type : VirtualType)\n    load(llvm_context.int32, value)\n  end\n\n  private def type_id_impl(value, type : NilableReferenceUnionType)\n    nil_block, not_nil_block, exit_block = new_blocks \"nil\", \"not_nil\", \"exit\"\n    phi_table = LLVM::PhiTable.new\n\n    cond null_pointer?(value), nil_block, not_nil_block\n\n    position_at_end nil_block\n    phi_table.add insert_block, type_id(@program.nil)\n    br exit_block\n\n    position_at_end not_nil_block\n    phi_table.add insert_block, load(llvm_context.int32, value)\n    br exit_block\n\n    position_at_end exit_block\n    phi llvm_context.int32, phi_table\n  end\n\n  private def type_id_impl(value, type : NilableProcType)\n    fun_ptr = extract_value value, 0\n    builder.select null_pointer?(fun_ptr), type_id(@program.nil), type_id(type.proc_type)\n  end\n\n  private def type_id_impl(value, type : VirtualMetaclassType)\n    value\n  end\n\n  private def type_id_impl(value, type : Program)\n    type_id(type)\n  end\n\n  private def type_id_impl(value, type : FileModule)\n    type_id(type)\n  end\n\n  private def type_id_impl(value, type : AliasType)\n    type_id value, type.aliased_type\n  end\n\n  private def type_id_impl(value, type)\n    type_id(type)\n  end\n\n  private def type_id_impl(type)\n    type_id_name = \"#{type.llvm_name}:type_id\"\n\n    global = @main_mod.globals[type_id_name]?\n    unless global\n      global = @main_mod.globals.add(@main_llvm_context.int32, type_id_name)\n      global.linkage = LLVM::Linkage::Internal if @single_module\n      global.initializer = @main_llvm_context.int32.const_int(@program.llvm_id.type_id(type))\n      global.global_constant = true\n    end\n\n    if @llvm_mod != @main_mod\n      global = @llvm_mod.globals[type_id_name]?\n      unless global\n        global = @llvm_mod.globals.add(@llvm_context.int32, type_id_name)\n        global.linkage = LLVM::Linkage::External\n        global.global_constant = true\n      end\n    end\n\n    load(@llvm_context.int32, global)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/types.cr",
    "content": "module Crystal\n  class Type\n    # Returns `true` if this type is passed as a `self` argument\n    # in the codegen phase. For example a method whose receiver is\n    # the Program, or a non-generic metaclass, doesn't have a `self` argument.\n    def passed_as_self?\n      case self\n      when Program, FileModule, LibType\n        false\n      when MetaclassType\n        # Given `type T = Void*`, `T.class` is not necessarily a non-generic\n        # metaclass, so we must resolve any typedefs here (we don't need to\n        # check for the cases above because `#remove_typedef` must return a\n        # metaclass)\n        !self.remove_typedef.is_a?(MetaclassType)\n      else\n        true\n      end\n    end\n\n    # Returns `true` if this type passed by value (if it's not a primitive type).\n    # In the codegen phase these types are passed as byval pointers.\n    def passed_by_value?\n      case self\n      when PrimitiveType, PointerInstanceType, ProcInstanceType\n        false\n      when TupleInstanceType, NamedTupleInstanceType, MixedUnionType\n        true\n      when VirtualType\n        self.struct?\n      when NonGenericModuleType\n        !!self.including_types.try &.passed_by_value?\n      when GenericModuleInstanceType\n        !!self.including_types.try &.passed_by_value?\n      when GenericClassInstanceType\n        self.generic_type.passed_by_value?\n      when TypeDefType\n        self.typedef.passed_by_value?\n      when AliasType\n        self.aliased_type.passed_by_value?\n      when ClassType\n        self.struct?\n      else\n        false\n      end\n    end\n\n    # Returns `true` if the type has inner pointers.\n    # This is useful to know because if a type doesn't have\n    # inner pointers we can use `malloc_atomic` instead of\n    # `malloc` in `Pointer.malloc` for a tiny performance boost.\n    #\n    # This behaviour is documented in Pointer.malloc\n    def has_inner_pointers?\n      case self\n      when .void?\n        # We consider Void to have pointers, so doing\n        # Pointer(Void).malloc(...).as(ReferenceType)\n        # will consider potential inner pointers as such.\n        true\n      when PointerInstanceType\n        true\n      when ProcInstanceType\n        # A proc can have closure data which might have pointers\n        true\n      when StaticArrayInstanceType\n        self.element_type.has_inner_pointers?\n      when TupleInstanceType\n        self.tuple_types.any? &.has_inner_pointers?\n      when NamedTupleInstanceType\n        self.entries.any? &.type.has_inner_pointers?\n      when ReferenceStorageType\n        self.reference_type.all_instance_vars.each_value.any? &.type.has_inner_pointers?\n      when PrimitiveType\n        false\n      when EnumType\n        false\n      when UnionType\n        self.union_types.any? &.has_inner_pointers?\n      when AliasType\n        self.aliased_type.has_inner_pointers?\n      when TypeDefType\n        self.typedef.has_inner_pointers?\n      when VirtualType\n        if struct?\n          self.subtypes.any? &.has_inner_pointers?\n        else\n          true\n        end\n      when InstanceVarContainer\n        if struct?\n          all_instance_vars.each_value.any? &.type.has_inner_pointers?\n        else\n          true\n        end\n      else\n        true\n      end\n    end\n\n    def llvm_name\n      String.build do |io|\n        llvm_name io\n      end\n    end\n\n    def llvm_name(io)\n      to_s_with_options io, codegen: true\n    end\n\n    def append_to_expand_union_types(types)\n      types << self\n    end\n  end\n\n  class PrimitiveType\n    def llvm_name(io)\n      io << name\n    end\n  end\n\n  class AliasType\n    def llvm_name(io)\n      io << \"alias.\"\n      to_s_with_options io, codegen: true\n    end\n  end\n\n  class NonGenericClassType\n    def llvm_name(io)\n      if extern?\n        io << (extern_union? ? \"union\" : \"struct\")\n        io << '.'\n      end\n      to_s_with_options io, codegen: true\n    end\n  end\n\n  class NonGenericModuleType\n    def append_to_expand_union_types(types)\n      if including_types = @including_types\n        including_types.each &.virtual_type.append_to_expand_union_types(types)\n      else\n        types << self\n      end\n    end\n  end\n\n  class GenericModuleInstanceType\n    def append_to_expand_union_types(types)\n      if including_types = @including_types\n        including_types.each &.virtual_type.append_to_expand_union_types(types)\n      else\n        types << self\n      end\n    end\n  end\n\n  class UnionType\n    def expand_union_types\n      if union_types.any?(NonGenericModuleType)\n        types = [] of Type\n        union_types.each &.append_to_expand_union_types(types)\n        types\n      else\n        union_types\n      end\n    end\n  end\n\n  class TypeDefType\n    def llvm_name(io)\n      typedef.llvm_name(io)\n    end\n  end\n\n  class Const\n    property initializer : LLVM::Value?\n\n    # Was this constant already read during the codegen phase?\n    # If not, and we are at the place that declares the constant, we can\n    # directly initialize the constant now, without checking for an `init` flag.\n    property? read = false\n\n    # If true, there's no need to check whether the constant was initialized or\n    # not when reading it.\n    property? no_init_flag = false\n\n    def initialized_llvm_name\n      \"#{llvm_name}:const_init\"\n    end\n\n    # Returns `true` if this constant's value is a simple literal, like\n    # `nil`, a number, char, string or symbol literal.\n    def simple?\n      return false if pointer_read?\n\n      value.simple_literal?\n    end\n\n    def needs_init_flag?\n      return true if pointer_read?\n\n      !(initializer || no_init_flag? || simple?)\n    end\n\n    @compile_time_value : (Int128 | Int16 | Int32 | Int64 | Int8 | UInt128 | UInt16 | UInt32 | UInt64 | UInt8 | Bool | Char | Nil)\n    @computed_compile_time_value = false\n\n    # Returns a value if this constant's value can be evaluated at\n    # compile time (things like `1 + 2` and such). Returns nil otherwise.\n    def compile_time_value\n      unless @computed_compile_time_value\n        @computed_compile_time_value = true\n\n        case value = self.value\n        when BoolLiteral\n          @compile_time_value = value.value\n        when CharLiteral\n          @compile_time_value = value.value\n        else\n          case value.type?\n          when IntegerType, EnumType\n            interpreter = MathInterpreter.new(namespace, visitor)\n            @compile_time_value = interpreter.interpret(value) rescue nil\n          end\n        end\n      end\n\n      @compile_time_value\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/codegen/unions.cr",
    "content": "# Here lies the logic of the representation of the MixedUnionType.\n#\n# Which structure is used to represent them is defined in `LLVMTyper#create_llvm_type`.\n#\n# The `#union_type_and_value_pointer` will allow to read the current value of the union.\n# The `#store*_in_union` operations allow to write the value in a unions.\n# The `#{assign|downcast|upcast}_distinct_union_types` operation matches the\n# semantics described in `./casts.cr`\n#\n# Together these operations should encapsulate the binary representation of the MixedUnionType.\n#\n# Other unions like ReferenceUnionType that have a more trivial\n# representation are not handled here.\n#\nmodule Crystal\n  class LLVMTyper\n    private def create_llvm_type(type : MixedUnionType, wants_size)\n      llvm_name = llvm_name(type, wants_size)\n      if s = @structs[llvm_name]?\n        return s\n      end\n\n      @llvm_context.struct(llvm_name) do |a_struct|\n        if wants_size\n          @wants_size_cache[type] = a_struct\n        else\n          @cache[type] = a_struct\n          @structs[llvm_name] = a_struct\n        end\n\n        max_size = 0_u64\n        max_alignment = pointer_size.to_u32!\n\n        type.expand_union_types.each do |subtype|\n          unless subtype.void?\n            llvm_type = llvm_type(subtype, wants_size: true)\n            max_size = {size_of(llvm_type), max_size}.max\n            max_alignment = {align_of(llvm_type), max_alignment}.max\n          end\n        end\n\n        value_size = {(max_size + (max_alignment - 1)) // max_alignment, 1_u64}.max\n        llvm_value_type = @llvm_context.int(max_alignment * 8).array(value_size)\n\n        [@llvm_context.int32, llvm_value_type]\n      end\n    end\n\n    def union_value_type(type : MixedUnionType)\n      llvm_type(type).struct_element_types[1]\n    end\n  end\n\n  class CodeGenVisitor\n    def union_type_and_value_pointer(union_pointer, type : UnionType)\n      raise \"BUG: trying to access union_type_and_value_pointer of a #{type} from #{union_pointer}\"\n    end\n\n    def union_type_and_value_pointer(union_pointer, type : MixedUnionType)\n      struct_type = llvm_type(type)\n      {\n        load(llvm_context.int32, union_type_id(struct_type, union_pointer)),\n        union_value(struct_type, union_pointer),\n      }\n    end\n\n    def union_type_id(struct_type, union_pointer)\n      aggregate_index struct_type, union_pointer, 0\n    end\n\n    def union_value(struct_type, union_pointer)\n      aggregate_index struct_type, union_pointer, 1\n    end\n\n    def store_in_union(union_type, union_pointer, value_type, value)\n      struct_type = llvm_type(union_type)\n      store type_id(value, value_type), union_type_id(struct_type, union_pointer)\n      casted_value_ptr = cast_to_pointer(union_value(struct_type, union_pointer), value_type)\n      store value, casted_value_ptr\n    end\n\n    def store_bool_in_union(target_type, union_pointer, value)\n      struct_type = llvm_type(target_type)\n      union_value_type = struct_type.struct_element_types[1]\n      store type_id(value, @program.bool), union_type_id(struct_type, union_pointer)\n\n      # To store a boolean in a union\n      # we zero-extend it to the size in bits of the union\n      union_size = @llvm_typer.size_of(union_value_type)\n      int_type = llvm_context.int((union_size * 8).to_i32)\n\n      bool_as_extended_int = builder.zext(value, int_type)\n      casted_value_ptr = pointer_cast(union_value(struct_type, union_pointer), int_type.pointer)\n      inst = store bool_as_extended_int, casted_value_ptr\n      set_alignment(inst, @llvm_typer.align_of(union_value_type))\n      inst\n    end\n\n    def store_nil_in_union(target_type, union_pointer)\n      struct_type = llvm_type(target_type)\n      union_value_type = struct_type.struct_element_types[1]\n      value = union_value_type.null\n\n      store type_id(value, @program.nil), union_type_id(struct_type, union_pointer)\n      casted_value_ptr = pointer_cast union_value(struct_type, union_pointer), union_value_type.pointer\n      store value, casted_value_ptr\n    end\n\n    def store_void_in_union(target_type, union_pointer)\n      struct_type = llvm_type(target_type)\n      store type_id(@program.void), union_type_id(struct_type, union_pointer)\n    end\n\n    # this is needed if `union_type` and `value_type` have different alignments,\n    # i.e. their `#union_value`s do not have the same offsets\n    def store_union_in_union(union_type, union_pointer, value_type, value)\n      to_llvm_type = llvm_type(union_type)\n      from_llvm_type = llvm_type(value_type)\n      to_value_type = to_llvm_type.struct_element_types[1]\n      from_value_type = from_llvm_type.struct_element_types[1]\n\n      store type_id(value, value_type), union_type_id(to_llvm_type, union_pointer)\n\n      size = {\n        @llvm_typer.size_of(from_value_type),\n        @llvm_typer.size_of(to_value_type),\n      }.min\n      memcpy(\n        cast_to_void_pointer(union_value(to_llvm_type, union_pointer)),\n        cast_to_void_pointer(union_value(from_llvm_type, value)),\n        size_t(size),\n        align: @llvm_typer.align_of(to_value_type),\n        src_align: @llvm_typer.align_of(from_value_type),\n        volatile: int1(0),\n      )\n    end\n\n    def assign_distinct_union_types(to_pointer, to_type, from_type, from_pointer)\n      # If we have:\n      # - to_pointer: Pointer(A | B | C)\n      # - to_type: A | B | C\n      # - from_type: A | B\n      # - from_pointer: Pointer(A | B)\n      #\n      # Then it might happen that from_type and to_type have the same alignment.\n      # In this case, the two pointers are interchangeable, so we can simply:\n      if align_of(to_type) == align_of(from_type)\n        # - load the value, we get A | B\n        # - cast the target pointer to Pointer(A | B)\n        # - store the A | B from the value pointer into the casted target pointer\n        casted_target_pointer = cast_to_pointer to_pointer, from_type\n        store load(llvm_type(from_type), from_pointer), casted_target_pointer\n      else\n        # Otherwise, the type ID and the value must be stored separately\n        store_union_in_union to_type, to_pointer, from_type, from_pointer\n      end\n    end\n\n    def downcast_distinct_union_types(value, to_type : MixedUnionType, from_type : MixedUnionType)\n      # If from_type and to_type have the same alignment, we don't need a\n      # separate value; just cast the larger value pointer to the smaller one\n      if align_of(to_type) == align_of(from_type)\n        cast_to_pointer value, to_type\n      else\n        # This is the same as upcasting and we need that separate, newly aligned\n        # union value\n        target_pointer = alloca llvm_type(to_type)\n        store_union_in_union to_type, target_pointer, from_type, value\n        target_pointer\n      end\n    end\n\n    def upcast_distinct_union_types(value, to_type : MixedUnionType, from_type : MixedUnionType)\n      # Because we are casting a union to a bigger union, we need new space\n      # for that, hence the alloca. Then we simply reuse `assign_distinct_union_types`.\n      target_pointer = alloca llvm_type(to_type)\n      assign_distinct_union_types target_pointer, to_type, from_type, value\n      target_pointer\n    end\n\n    private def type_id_impl(value, type : MixedUnionType)\n      struct_type = llvm_type(type)\n      load(llvm_context.int32, union_type_id(struct_type, value))\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/clear_cache.cr",
    "content": "# Implementation of the `crystal clear_cache` command\n\nclass Crystal::Command\n  private def clear_cache\n    verbose = false\n    OptionParser.parse(@options) do |opts|\n      opts.banner = <<-'BANNER'\n        Usage: crystal clear_cache\n\n        Clears the compiler cache\n\n        Options:\n        BANNER\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      opts.on(\"-v\", \"--verbose\", \"Display detailed information\") do\n        verbose = true\n      end\n    end\n    puts \"Clearing compiler cache at #{CacheDir.instance.dir.inspect}\" if verbose\n    FileUtils.rm_rf(CacheDir.instance.dir)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/cursor.cr",
    "content": "# Implementation of commands that depend on the cursor location:\n# `crystal tool implementations` and `crystal tool context`\n#\n# This is just the command-line part. The tools logic is in\n# `crystal/tools/context.cr` and `crystal/tools/implementations.cr`\n\nclass Crystal::Command\n  private def implementations\n    cursor_command(\"tool implementations\") do |location, config, result|\n      ImplementationsVisitor.new(location).process(result)\n    end\n  end\n\n  private def context\n    cursor_command(\"tool context\") do |location, config, result|\n      ContextVisitor.new(location).process(result)\n    end\n  end\n\n  private def expand\n    cursor_command(\"tool expand\", no_cleanup: true, wants_doc: true) do |location, config, result|\n      ExpandVisitor.new(location).process(result)\n    end\n  end\n\n  private def cursor_command(command, no_cleanup = false, wants_doc = false, &)\n    config, result = compile_no_codegen command,\n      cursor_command: true,\n      no_cleanup: no_cleanup,\n      wants_doc: wants_doc\n\n    format = config.output_format\n\n    begin\n      loc = Location.parse(config.cursor_location.not_nil!, expand: true)\n    rescue ex : ArgumentError\n      error ex.message\n    end\n\n    result = @progress_tracker.stage(\"Tool (#{command.split(' ')[1]})\") do\n      yield loc, config, result\n    end\n\n    case format\n    when \"json\"\n      result.to_json(STDOUT)\n    else\n      result.to_text(STDOUT)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/docs.cr",
    "content": "# Implementation of the `crystal docs` command\n#\n# This is just the command-line part. Everything else\n# is in `crystal/tools/doc/`\n\nclass Crystal::Command\n  private VALID_OUTPUT_FORMATS = %w(html json)\n\n  private def docs\n    output_format = \"html\"\n    output_directory = File.join(\".\", \"docs\")\n    sitemap_base_url = nil\n    sitemap_priority = \"1.0\"\n    sitemap_changefreq = \"never\"\n    project_info = Doc::ProjectInfo.new\n\n    compiler = new_compiler\n\n    OptionParser.parse(@options) do |opts|\n      opts.banner = <<-'BANNER'\n        Usage: crystal docs [options]\n\n        Generates API documentation from inline docstrings in all Crystal files inside ./src directory.\n\n        Options:\n        BANNER\n\n      opts.on(\"--project-name=NAME\", \"Set project name\") do |value|\n        project_info.name = value\n      end\n\n      opts.on(\"--project-version=VERSION\", \"Set project version\") do |value|\n        project_info.version = value\n      end\n\n      opts.on(\"--source-refname=REFNAME\", \"Set source refname (e.g. git tag, commit hash)\") do |value|\n        project_info.refname = value\n      end\n\n      opts.on(\"--source-url-pattern=REFNAME\", \"Set URL pattern for source code links\") do |value|\n        project_info.source_url_pattern = value\n      end\n\n      opts.on(\"--output=DIR\", \"-o DIR\", \"Set the output directory (default: #{output_directory})\") do |value|\n        output_directory = value\n      end\n      opts.on(\"--format=FORMAT\", \"-f FORMAT\", \"Set the output format [#{VALID_OUTPUT_FORMATS.join(\", \")}] (default: #{output_format})\") do |value|\n        if !VALID_OUTPUT_FORMATS.includes? value\n          STDERR.puts \"Invalid format '#{value}'\"\n          abort opts\n        end\n        output_format = value\n      end\n\n      opts.on(\"--json-config-url=URL\", \"Set the URL pointing to a config file (used for discovering versions)\") do |value|\n        project_info.json_config_url = value\n      end\n\n      opts.on(\"--canonical-base-url=URL\", %(Indicate the preferred URL with rel=\"canonical\" link element)) do |value|\n        project_info.canonical_base_url = value\n      end\n\n      opts.on(\"--base-path=PATH\", \"Set the base path - must be absolute for finding assets on the 404 page\") do |value|\n        project_info.base_path = value\n      end\n\n      opts.on(\"--sitemap-base-url=URL\", \"-b URL\", \"Set the sitemap base URL and generates sitemap\") do |value|\n        sitemap_base_url = value\n      end\n\n      opts.on(\"--sitemap-priority=PRIO\", \"Set the sitemap priority (default: 1.0)\") do |value|\n        sitemap_priority = value\n      end\n\n      opts.on(\"--sitemap-changefreq=FREQ\", \"Set the sitemap changefreq (default: never)\") do |value|\n        sitemap_changefreq = value\n      end\n\n      opts.on(\"-D FLAG\", \"--define FLAG\", \"Define a compile-time flag\") do |flag|\n        compiler.flags << flag\n      end\n\n      opts.on(\"--error-trace\", \"Show full error trace\") do\n        compiler.show_error_trace = true\n      end\n\n      opts.on(\"--no-color\", \"Disable colored output\") do\n        @color = false\n        compiler.color = false\n      end\n\n      opts.on(\"--prelude \", \"Use given file as prelude\") do |prelude|\n        compiler.prelude = prelude\n      end\n\n      opts.on(\"-s\", \"--stats\", \"Enable statistics output\") do\n        @progress_tracker.stats = true\n      end\n\n      opts.on(\"-p\", \"--progress\", \"Enable progress output\") do\n        @progress_tracker.progress = true\n      end\n\n      opts.on(\"-t\", \"--time\", \"Enable execution time output\") do\n        @time = true\n      end\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      setup_compiler_warning_options(opts, compiler)\n    end\n\n    project_info.fill_with_defaults\n\n    unless project_info.name?\n      STDERR.puts \"Couldn't determine name from shard.yml, please provide --project-name option\"\n    end\n\n    unless project_info.version?\n      STDERR.puts \"Couldn't determine version from git or shard.yml, please provide --project-version option\"\n    end\n\n    if Crystal::Doc::MarkdDocRenderer::SANITIZER.nil?\n      STDERR.puts \"Crystal built without LibXML2 support, documentation sanitization disabled\"\n    end\n\n    unless project_info.name? && project_info.version?\n      abort\n    end\n\n    if options.empty?\n      sources = [Compiler::Source.new(\"require\", %(require \"./src/**\"))]\n      included_dirs = [] of String\n    else\n      filenames = options\n      sources = gather_sources(filenames)\n      included_dirs = sources.map { |source| File.dirname(source.filename) }\n    end\n\n    included_dirs << File.expand_path(\"./src\")\n\n    compiler.flags << \"docs\"\n    compiler.wants_doc = true\n    result = compiler.top_level_semantic sources\n\n    Doc::Generator.new(result.program, included_dirs, output_directory, output_format, sitemap_base_url, sitemap_priority, sitemap_changefreq, project_info).run\n\n    report_warnings\n    exit 1 if warnings_fail_on_exit?\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/env.cr",
    "content": "# Implementation of the `crystal env` command\n\nclass Crystal::Command\n  private def env\n    var_names = [] of String\n\n    OptionParser.parse(@options) do |opts|\n      opts.banner = env_usage\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      opts.unknown_args do |before, after|\n        var_names = before\n      end\n    end\n\n    vars = {\n      \"CRYSTAL_CACHE_DIR\"    => CacheDir.instance.dir,\n      \"CRYSTAL_EXEC_PATH\"    => Crystal::Config.exec_path || \"\",\n      \"CRYSTAL_PATH\"         => CrystalPath.default_path,\n      \"CRYSTAL_VERSION\"      => Config.version || \"\",\n      \"CRYSTAL_LIBRARY_PATH\" => CrystalLibraryPath.default_path,\n      \"CRYSTAL_OPTS\"         => ENV.fetch(\"CRYSTAL_OPTS\", \"\"),\n    }\n\n    if var_names.empty?\n      vars.each do |key, value|\n        puts \"#{key}=#{Process.quote(value)}\"\n      end\n    else\n      var_names.each do |key|\n        puts vars[key]?\n      end\n    end\n  end\n\n  private def env_usage\n    <<-USAGE\n    Usage: crystal env [var ...]\n\n    Prints Crystal environment information.\n\n    By default it prints information as a shell script.\n    If one or more variable names is given as arguments,\n    it prints the value of each named variable on its own line.\n\n    Options:\n    USAGE\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/eval.cr",
    "content": "# Implementation of the `crystal eval` command\n\nclass Crystal::Command\n  private def eval\n    compiler = new_compiler\n    opt_program_source = nil\n    program_args = [] of String\n\n    parse_with_crystal_opts do |opts|\n      opts.banner = \"Usage: crystal eval [options] [source]\\n\\nOptions:\"\n      setup_simple_compiler_options compiler, opts\n\n      opts.unknown_args do |before_dash, after_dash|\n        unless before_dash.empty?\n          opt_program_source = before_dash.join \" \"\n        end\n        program_args = after_dash\n      end\n    end\n\n    program_source = opt_program_source\n    if program_source.nil?\n      program_source = STDIN.gets_to_end\n    end\n\n    sources = [Compiler::Source.new(\"eval\", program_source)]\n\n    output_filename = Crystal.temp_executable \"eval\"\n\n    compiler.compile sources, output_filename\n    execute output_filename, program_args, compiler\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/format.cr",
    "content": "# Implementation of the `crystal tool format` command\n#\n# This is just the command-line part. The formatter\n# logic is in `crystal/tools/formatter.cr`.\n\nclass Crystal::Command\n  private def format\n    excludes = [\"lib\"] of String\n    includes = [] of String\n    check = false\n    show_backtrace = false\n\n    OptionParser.parse(@options) do |opts|\n      opts.banner = <<-USAGE\n        Usage: crystal tool format [options] [- | file or directory ...]\n\n        Formats Crystal code in place.\n\n        If a file or directory is omitted,\n        Crystal source files beneath the working directory are formatted.\n\n        To format STDIN to STDOUT, use '-' in place of any path arguments.\n\n        Options:\n        USAGE\n\n      opts.on(\"--check\", \"Checks that formatting code produces no changes\") do |f|\n        check = true\n      end\n\n      opts.on(\"-i <path>\", \"--include <path>\", \"Include path\") do |f|\n        includes << f\n      end\n\n      opts.on(\"-e <path>\", \"--exclude <path>\", \"Exclude path (default: lib)\") do |f|\n        excludes << f\n      end\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      opts.on(\"--no-color\", \"Disable colored output\") do\n        @color = false\n      end\n\n      opts.on(\"--show-backtrace\", \"Show backtrace on a bug (used only for debugging)\") do\n        show_backtrace = true\n      end\n    end\n\n    files = options\n\n    format_command = FormatCommand.new(\n      files,\n      includes,\n      excludes,\n      check,\n      show_backtrace,\n      @color,\n    )\n    format_command.run\n    exit format_command.status_code\n  end\n\n  class FormatCommand\n    @format_stdin : Bool\n    @files : Array(String)\n    @excludes : Array(String)\n\n    getter status_code = 0\n\n    def initialize(\n      files : Array(String),\n      includes = [] of String, excludes = [] of String,\n      @check : Bool = false,\n      @show_backtrace : Bool = false,\n      @color : Bool = true,\n      # stdio is injectable for testing\n      @stdin : IO = STDIN, @stdout : IO = STDOUT, @stderr : IO = STDERR,\n    )\n      @format_stdin = files.size == 1 && files[0] == \"-\"\n\n      includes.map! { |p| Crystal.normalize_path p }\n      excludes.map! { |p| Crystal.normalize_path p }\n      excludes = excludes - includes\n      if files.empty?\n        files = Dir[\"./**/*.cr\"]\n      else\n        files.map! { |p| Crystal.normalize_path p }\n      end\n\n      @files = files\n      @excludes = excludes\n    end\n\n    def run\n      if @format_stdin\n        format_stdin\n      else\n        format_many @files\n      end\n    end\n\n    private def format_stdin\n      source = @stdin.gets_to_end\n      format_source \"STDIN\", source\n    end\n\n    private def format_many(files)\n      files.each do |filename|\n        format_file_or_directory filename\n      end\n    end\n\n    private def format_file_or_directory(filename)\n      if File.file?(filename)\n        unless @excludes.any? { |exclude| filename.starts_with?(exclude) }\n          format_file filename\n        end\n      elsif Dir.exists?(filename)\n        filename = ::Path[filename.chomp('/')].to_posix\n        filenames = Dir[\"#{filename}/**/*.cr\"]\n        format_many filenames\n      else\n        error \"file or directory does not exist: #{filename}\"\n      end\n    end\n\n    private def format_file(filename)\n      source = File.read(filename)\n      format_source filename, source\n    end\n\n    private def format_source(filename, source)\n      result = format(filename, source)\n      @stdout.print result if @format_stdin\n      return if result == source\n\n      if @check\n        error \"formatting '#{filename}' produced changes\"\n        @status_code = 1\n      else\n        unless @format_stdin\n          File.write filename, result\n          @stdout << \"Format\".colorize(:green).toggle(@color) << ' ' << filename << '\\n'\n        end\n      end\n    rescue ex : InvalidByteSequenceError\n      error \"file '#{filename}' is not a valid Crystal source file: #{ex.message}\"\n      @status_code = 1\n    rescue ex : Crystal::SyntaxException\n      error \"syntax error in '#{filename}:#{ex.line_number}:#{ex.column_number}': #{ex.message}\"\n      @status_code = 1\n    rescue ex\n      if @show_backtrace\n        ex.inspect_with_backtrace @stderr\n        @stderr.puts\n        error \"couldn't format '#{filename}', please report a bug including the contents of it: https://github.com/crystal-lang/crystal/issues\"\n      else\n        error \"there's a bug formatting '#{filename}', to show more information, please run:\\n\\n  $ crystal tool format --show-backtrace #{@format_stdin ? \"-\" : \"'#{filename}'\"}\\n\"\n      end\n      @status_code = 1\n    end\n\n    # This method is for mocking `Crystal.format` in test.\n    private def format(filename, source)\n      Crystal.format(source, filename: filename, report_warnings: STDERR)\n    end\n\n    private def error(msg)\n      Crystal.error msg, @color, exit_code: nil, stderr: @stderr, leading_error: false\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/playground.cr",
    "content": "# Implementation of the `crystal tool playground` command\n#\n# This is just the command-line part. The playground is\n# implemented in `crystal/tools/playground/`\n\nclass Crystal::Command\n  private def playground\n    server = Playground::Server.new\n\n    OptionParser.parse(@options) do |opts|\n      opts.banner = \"Usage: crystal play [options] [file]\\n\\nOptions:\"\n\n      opts.on(\"-p PORT\", \"--port PORT\", \"Runs the playground on the specified port\") do |port|\n        port = port.to_i?\n        raise Error.new(\"Invalid port number: #{port}\") unless port && Socket::IPAddress.valid_port?(port)\n        server.port = port\n      end\n\n      opts.on(\"-b HOST\", \"--binding HOST\", \"Binds the playground to the specified IP\") do |host|\n        server.host = host\n      end\n\n      opts.on(\"-v\", \"--verbose\", \"Display detailed information of executed code\") do\n        Crystal::Playground::Log.level = :debug\n      end\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      opts.unknown_args do |before, after|\n        if before.size > 0\n          server.source = gather_sources([before.first]).first\n        end\n      end\n    end\n\n    server.start\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/repl.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"../config\"\n\n# Implementation of the `crystal repl` command\n\nclass Crystal::Command\n  private def repl\n    repl = Repl.new\n\n    parse_with_crystal_opts do |opts|\n      opts.banner = \"Usage: crystal i [options] [programfile] [arguments]\\n\\nOptions:\"\n\n      opts.on(\"-D FLAG\", \"--define FLAG\", \"Define a compile-time flag\") do |flag|\n        repl.program.flags << flag\n      end\n\n      opts.on(\"--error-trace\", \"Show full error trace\") do\n        repl.program.show_error_trace = true\n        @error_trace = true\n      end\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      opts.on(\"--no-color\", \"Disable colored output\") do\n        @color = false\n        repl.program.color = false\n      end\n\n      opts.on(\"--prelude \", \"Use given file as prelude\") do |prelude|\n        repl.prelude = prelude\n      end\n    end\n\n    if options.empty?\n      show_banner\n      repl.run\n    else\n      filename = options.shift\n      unless File.file?(filename)\n        error \"File '#{filename}' doesn't exist\"\n      end\n\n      show_banner\n      repl.run_file(filename, options)\n    end\n  end\n\n  private def show_banner\n    return unless STDERR.tty? && !ENV.has_key?(\"CRYSTAL_INTERPRETER_SKIP_BANNER\")\n\n    formatted_sha = \"[#{Config.build_commit}] \" if Config.build_commit\n    STDERR.puts \"Crystal interpreter #{Config.version} #{formatted_sha}(#{Config.date}).\\n\" \\\n                \"EXPERIMENTAL SOFTWARE: if you find a bug, please consider opening an issue in\\n\" \\\n                \"https://github.com/crystal-lang/crystal/issues/new/\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command/spec.cr",
    "content": "# Implementation of the `crystal spec` command\n#\n# This ends up compiling and running some or all files\n# inside the `spec` directory of the current project, passing\n# `--location file:line` if line numbers were specified.\n#\n# The spec framework is chosen by the files inside the `spec`\n# directory, which usually is just `require \"spec\"` but could\n# be anything else (for example the `minitest` shard).\n\n# Gain access to OptionParser for spec runner to include it in the usage\n# instructions.\nrequire \"spec/cli\"\n\nclass Crystal::Command\n  private def spec\n    compiler = new_compiler\n    link_flags = [] of String\n    parse_with_crystal_opts do |opts|\n      opts.banner = \"Usage: crystal spec [options] [files] [runtime_options]\\n\\nOptions:\"\n      setup_simple_compiler_options compiler, opts\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        puts\n\n        runtime_options = Spec::CLI.new.option_parser\n        runtime_options.banner = \"Runtime options (passed to spec runner):\"\n        puts runtime_options\n        exit\n      end\n\n      opts.on(\"--link-flags FLAGS\", \"Additional flags to pass to the linker\") do |some_link_flags|\n        link_flags << some_link_flags\n      end\n    end\n\n    compiler.link_flags = link_flags.join(' ') unless link_flags.empty?\n\n    # Assume spec files end with \".cr\" and optionally with a colon and a number\n    # (for the target line number), or is a directory. Everything else is an option we forward.\n    filenames = options.select do |option|\n      option =~ /\\.cr(\\:\\d+)?\\Z/ || Dir.exists?(option)\n    end\n    options.reject! { |option| filenames.includes?(option) }\n\n    locations = [] of {String, String}\n\n    if filenames.empty?\n      target_filenames = Dir[\"spec/**/*_spec.cr\"]\n    else\n      target_filenames = [] of String\n      filenames.each do |filename|\n        if filename =~ /\\A(.+?)\\:(\\d+)\\Z/\n          file, line = $1, $2\n          unless File.file?(file)\n            error \"'#{file}' is not a file\"\n          end\n          target_filenames << file\n          locations << {file, line}\n        else\n          if Dir.exists?(filename)\n            filename = ::Path[filename].to_posix\n            target_filenames.concat Dir[\"#{filename}/**/*_spec.cr\"]\n          elsif File.file?(filename)\n            target_filenames << filename\n          else\n            error \"'#{filename}' is not a file\"\n          end\n        end\n      end\n    end\n\n    if target_filenames.size == 1\n      if locations.size == 1\n        # This is in case other spec runners use `-l`, we keep compatibility\n        options << \"-l\" << locations.first[1]\n      end\n    else\n      locations.each do |(file, line)|\n        options << \"--location\" << \"#{file}:#{line}\"\n      end\n    end\n\n    unless @color\n      options << \"--no-color\"\n    end\n\n    source_filename = File.expand_path(\"spec\")\n\n    source = target_filenames.join('\\n') do |filename|\n      %(require \"./#{::Path[filename].relative_to(Dir.current).to_posix.to_s.inspect_unquoted}\")\n    end\n    sources = [Compiler::Source.new(source_filename, source)]\n\n    output_filename = Crystal.temp_executable \"spec\"\n\n    ENV[\"CRYSTAL_SPEC_COMPILER_BIN\"] ||= if crystal_exec_path = ENV[\"CRYSTAL_EXEC_PATH\"]?\n                                           File.join(crystal_exec_path, \"crystal\")\n                                         else\n                                           Process.executable_path\n                                         end\n\n    compiler.compile sources, output_filename\n    report_warnings\n    execute output_filename, options, compiler, error_on_exit: warnings_fail_on_exit?\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/command.cr",
    "content": "# Here we process the compiler's command line options and\n# execute the relevant commands.\n#\n# Some commands are implemented in the `commands` directory,\n# some in `tools`, some here, and some create a Compiler and\n# manipulate it.\n#\n# Other commands create a `Compiler` and use it to build\n# an executable.\n\nrequire \"json\"\nrequire \"./command/*\"\nrequire \"./tools/*\"\n\nclass Crystal::Command\n  USAGE = <<-USAGE\n    Usage: crystal [command] [switches] [program file] [--] [arguments]\n\n    Command:\n        init                     generate a new project\n        build                    build an executable\n        clear_cache              clear the compiler cache\n        docs                     generate documentation\n        env                      print Crystal environment information\n        eval                     eval code from args or standard input\n        i/interactive            starts interactive Crystal\n        play                     starts Crystal playground server\n        run (default)            build and run program\n        spec                     build and run specs (in spec directory)\n        tool                     run a tool\n        help, --help, -h         show this help\n        version, --version, -v   show version\n\n    Run a command followed by --help to see command specific information, ex:\n        crystal <command> --help\n    USAGE\n\n  COMMANDS_USAGE = <<-USAGE\n    Usage: crystal tool [tool] [switches] [program file] [--] [arguments]\n\n    Tool:\n        context                  show context for given location\n        dependencies             show file dependency tree\n        expand                   show macro expansion for given location\n        flags                    print all macro `flag?` values\n        format                   format project, directories and/or files\n        hierarchy                show type hierarchy\n        implementations          show implementations for given call in location\n        macro_code_coverage      generate a macro code coverage report\n        types                    show type of main variables\n        unreachable              show methods that are never called\n        --help, -h               show this help\n    USAGE\n\n  def self.run(options = ARGV)\n    new(options).run\n  end\n\n  private getter options\n  @compiler : Compiler?\n\n  def initialize(@options : Array(String))\n    @color = Colorize.default_enabled?(STDOUT, STDERR)\n    @error_trace = false\n    @progress_tracker = ProgressTracker.new\n  end\n\n  def run\n    command = options.first?\n    case\n    when !command\n      puts USAGE\n      exit\n    when command == \"init\"\n      options.shift\n      init\n    when \"build\".starts_with?(command)\n      options.shift\n      build\n      report_warnings\n      exit 1 if warnings_fail_on_exit?\n    when \"play\".starts_with?(command)\n      options.shift\n      {% if flag?(:without_playground) %}\n        puts \"Crystal was compiled without playground support\"\n        puts \"Try the online code evaluation and sharing tool at https://play.crystal-lang.org\"\n        exit 1\n      {% else %}\n        playground\n      {% end %}\n    when \"deps\".starts_with?(command)\n      STDERR.puts \"Please use 'shards': 'crystal deps' has been removed\"\n      exit 1\n    when \"docs\".starts_with?(command)\n      options.shift\n      docs\n    when command == \"env\"\n      options.shift\n      env\n    when command == \"eval\"\n      options.shift\n      eval\n    when command.in?(\"i\", \"interactive\")\n      options.shift\n      {% if flag?(:without_interpreter) %}\n        STDERR.puts \"Crystal was compiled without interpreter support\"\n        exit 1\n      {% else %}\n        repl\n      {% end %}\n    when command == \"run\"\n      options.shift\n      run_command(single_file: false)\n    when \"spec/\".starts_with?(command)\n      options.shift\n      spec\n    when \"tool\".starts_with?(command)\n      options.shift\n      tool\n    when command == \"clear_cache\"\n      options.shift\n      clear_cache\n    when \"help\".starts_with?(command), \"--help\" == command, \"-h\" == command\n      puts USAGE\n      exit\n    when \"version\".starts_with?(command), \"--version\" == command, \"-v\" == command\n      puts Crystal::Config.description\n      exit\n    when File.file?(command)\n      run_command(single_file: true)\n    else\n      if command.ends_with?(\".cr\")\n        error \"file '#{command}' does not exist\"\n      elsif external_command = Process.find_executable(\"crystal-#{command}\")\n        options.shift\n\n        crystal_exec_path = Crystal::Config.exec_path\n        path = [crystal_exec_path, ENV[\"PATH\"]?].compact!.join(Process::PATH_DELIMITER)\n\n        Process.exec(external_command, options, env: {\n          \"PATH\"              => path,\n          \"CRYSTAL_EXEC_PATH\" => crystal_exec_path,\n        })\n      else\n        error \"unknown command: #{command}\"\n      end\n    end\n  rescue ex : Crystal::CodeError\n    report_warnings\n\n    ex.color = @color\n    ex.error_trace = @error_trace\n    if @config.try(&.output_format) == \"json\"\n      STDERR.puts ex.to_json\n    else\n      STDERR.puts ex\n    end\n    exit 1\n  rescue ex : Crystal::Error\n    report_warnings\n\n    # This unwraps nested errors which could be caused by `require` which wraps\n    # errors in order to trace the require path. The causes are listed similarly\n    # to `#inspect_with_backtrace` but without the backtrace.\n    while cause = ex.cause\n      error ex.message, exit_code: nil\n      ex = cause\n    end\n\n    error ex.message\n  rescue ex : OptionParser::Exception\n    error ex.message\n  rescue ex\n    report_warnings\n\n    ex.inspect_with_backtrace STDERR\n    error \"you've found a bug in the Crystal compiler. Please open an issue, including source code that will allow us to reproduce the bug: https://github.com/crystal-lang/crystal/issues\"\n  end\n\n  private def tool\n    tool = options.first?\n    case\n    when !tool\n      puts COMMANDS_USAGE\n      exit\n    when \"context\".starts_with?(tool)\n      options.shift\n      context\n    when \"format\".starts_with?(tool)\n      options.shift\n      format\n    when \"flags\" == tool\n      options.shift\n      flags\n    when \"expand\".starts_with?(tool)\n      options.shift\n      expand\n    when \"hierarchy\".starts_with?(tool)\n      options.shift\n      hierarchy\n    when \"dependencies\".starts_with?(tool)\n      options.shift\n      dependencies\n    when \"implementations\".starts_with?(tool)\n      options.shift\n      implementations\n    when \"types\".starts_with?(tool)\n      options.shift\n      types\n    when \"unreachable\".starts_with?(tool)\n      options.shift\n      unreachable\n    when \"macro_code_coverage\".starts_with?(tool)\n      options.shift\n      macro_code_coverage\n    when \"--help\" == tool, \"-h\" == tool\n      puts COMMANDS_USAGE\n      exit\n    else\n      error \"unknown tool: #{tool}\"\n    end\n  end\n\n  private def init\n    Init.run(options)\n  end\n\n  private def build\n    config = create_compiler \"build\"\n    config.compile\n  end\n\n  private def hierarchy\n    config, result = compile_no_codegen \"tool hierarchy\", hierarchy: true, top_level: true\n    @progress_tracker.stage(\"Tool (hierarchy)\") do\n      Crystal.print_hierarchy result.program, STDOUT, config.hierarchy_exp, config.output_format\n    end\n  end\n\n  private def run_command(single_file = false)\n    config = create_compiler \"run\", run: true, single_file: single_file\n    if config.specified_output\n      config.compile\n      report_warnings\n      exit 1 if warnings_fail_on_exit?\n      return\n    end\n\n    output_filename = Crystal.temp_executable(config.output_filename)\n\n    config.compile output_filename\n\n    unless config.compiler.no_codegen?\n      report_warnings\n      exit 1 if warnings_fail_on_exit?\n\n      execute output_filename, config.arguments, config.compiler\n    end\n  end\n\n  private def types\n    _, result = compile_no_codegen \"tool types\"\n    @progress_tracker.stage(\"Tool (types)\") do\n      Crystal.print_types result.node\n    end\n  end\n\n  private def compile_no_codegen(command, wants_doc = false, hierarchy = false, no_cleanup = false, cursor_command = false, top_level = false, path_filter = false, unreachable_command = false, allowed_formats = [\"text\", \"json\"])\n    config = create_compiler command, no_codegen: true, hierarchy: hierarchy, cursor_command: cursor_command, path_filter: path_filter, unreachable_command: unreachable_command, allowed_formats: allowed_formats\n    config.compiler.no_codegen = true\n    config.compiler.no_cleanup = no_cleanup\n    config.compiler.wants_doc = wants_doc\n    result = top_level ? config.top_level_semantic : config.compile\n    {config, result}\n  end\n\n  private def execute(output_filename, run_args, compiler, *, error_on_exit = false)\n    time = @time && !@progress_tracker.stats?\n    status, elapsed_time = @progress_tracker.stage(\"Execute\") do\n      elapsed = Time.measure do\n        Process.run(output_filename, args: run_args, input: Process::Redirect::Inherit, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit) do |process|\n          {% unless flag?(:wasm32) %}\n            # Ignore the signal so we don't exit the running process\n            # (the running process can still handle this signal)\n            Process.ignore_interrupts!\n          {% end %}\n        end\n      end\n      {$?, elapsed}\n    ensure\n      File.delete?(output_filename)\n\n      # Delete related PDB generated by MSVC, if any exist\n      {% if flag?(:msvc) %}\n        unless compiler.debug.none?\n          basename = output_filename.rchop(\".exe\")\n          File.delete?(\"#{basename}.pdb\")\n        end\n      {% end %}\n\n      # Delete related dwarf generated by dsymutil, if any exist\n      {% if flag?(:darwin) %}\n        unless compiler.debug.none?\n          File.delete?(\"#{output_filename}.dwarf\")\n        end\n      {% end %}\n    end\n\n    if time\n      puts \"Execute: #{elapsed_time}\"\n    end\n\n    if (exit_code = status.exit_code?) && !error_on_exit\n      exit exit_code\n    end\n\n    unless status.exit_reason.normal?\n      STDERR.puts status.description\n      STDERR.flush\n    end\n\n    exit 1\n  end\n\n  record CompilerConfig,\n    compiler : Compiler,\n    sources : Array(Compiler::Source),\n    output_filename : String,\n    emit_base_filename : String?,\n    arguments : Array(String),\n    specified_output : Bool,\n    hierarchy_exp : String?,\n    cursor_location : String?,\n    output_format : String,\n    includes : Array(String),\n    excludes : Array(String),\n    verbose : Bool,\n    check : Bool,\n    tallies : Bool do\n    def compile(output_filename = self.output_filename)\n      compiler.emit_base_filename = emit_base_filename || output_filename.rchop(File.extname(output_filename))\n      compiler.compile sources, output_filename\n    end\n\n    def compile_configure_program(output_filename = self.output_filename, &)\n      compiler.emit_base_filename = emit_base_filename || output_filename.rchop(File.extname(output_filename))\n      compiler.compile_configure_program sources, output_filename do |program|\n        yield program\n      end\n    end\n\n    def top_level_semantic\n      compiler.top_level_semantic sources\n    end\n  end\n\n  private def create_compiler(command, no_codegen = false, run = false,\n                              hierarchy = false, cursor_command = false,\n                              single_file = false, dependencies = false,\n                              path_filter = false, unreachable_command = false,\n                              allowed_formats = [\"text\", \"json\"])\n    compiler = new_compiler\n    compiler.progress_tracker = @progress_tracker\n    compiler.no_codegen = no_codegen\n    link_flags = [] of String\n    filenames = [] of String\n    has_stdin_filename = false\n    opt_filenames = nil\n    opt_arguments = nil\n    opt_output_filename = nil\n    specified_output = false\n    hierarchy_exp = nil\n    cursor_location = nil\n    output_format = nil\n    excludes = [] of String\n    includes = [] of String\n    verbose = false\n    check = false\n    tallies = false\n\n    option_parser = parse_with_crystal_opts do |opts|\n      opts.banner = \"Usage: crystal #{command} [options] [programfile] [--] [arguments]\\n\\nOptions:\"\n\n      unless no_codegen\n        unless run\n          opts.on(\"--cross-compile\", \"cross-compile\") do |cross_compile|\n            compiler.cross_compile = true\n          end\n        end\n        opts.on(\"-d\", \"--debug\", \"Add full symbolic debug info\") do\n          compiler.debug = Crystal::Debug::All\n        end\n        opts.on(\"--no-debug\", \"Skip any symbolic debug info\") do\n          compiler.debug = Crystal::Debug::None\n        end\n\n        opts.on(\"--frame-pointers auto|always|non-leaf\", \"Control the preservation of frame pointers\") do |value|\n          if frame_pointers = FramePointers.parse?(value)\n            compiler.frame_pointers = frame_pointers\n          else\n            error \"Invalid value `#{value}` for frame-pointers\"\n          end\n        end\n      end\n\n      opts.on(\"-D FLAG\", \"--define FLAG\", \"Define a compile-time flag\") do |flag|\n        compiler.flags << flag\n      end\n\n      unless no_codegen\n        valid_emit_values = Compiler::EmitTarget.names\n        valid_emit_values.map!(&.gsub('_', '-').downcase)\n\n        opts.on(\"--emit [#{valid_emit_values.join('|')}]\", \"Comma separated list of types of output for the compiler to emit\") do |emit_values|\n          compiler.emit_targets |= validate_emit_values(emit_values.split(',').map(&.strip))\n        end\n\n        opts.on(\"--x86-asm-syntax att|intel\", \"X86 dialect for --emit=asm: AT&T (default), Intel\") do |value|\n          case value = LLVM::InlineAsmDialect.parse?(value)\n          in Nil\n            error \"Invalid value `#{value}` for x86-asm-syntax\"\n          in .att?\n            # Do nothing\n          in .intel?\n            LLVM.parse_command_line_options({\"\", \"-x86-asm-syntax=intel\"})\n          end\n        end\n      end\n\n      if hierarchy\n        opts.on(\"-e NAME\", \"Filter types by NAME regex\") do |exp|\n          hierarchy_exp = exp\n        end\n      end\n\n      if cursor_command\n        opts.on(\"-c LOC\", \"--cursor LOC\", \"Cursor location with LOC as path/to/file.cr:line:column\") do |cursor|\n          cursor_location = cursor\n        end\n      end\n\n      if dependencies\n        opts.on(\"-i <path>\", \"--include <path>\", \"Include path\") do |f|\n          includes << f\n        end\n\n        opts.on(\"-e <path>\", \"--exclude <path>\", \"Exclude path (default: lib)\") do |f|\n          excludes << f\n        end\n\n        opts.on(\"--verbose\", \"Show skipped and filtered paths\") do\n          verbose = true\n        end\n      end\n\n      opts.on(\"-f #{allowed_formats.join(\"|\")}\", \"--format #{allowed_formats.join(\"|\")}\", \"Output format: #{allowed_formats[0]} (default), #{allowed_formats[1..].join(\", \")}\") do |f|\n        output_format = f\n      end\n\n      if unreachable_command\n        opts.on(\"--tallies\", \"Print reachable methods and their call counts as well\") do\n          tallies = true\n        end\n\n        opts.on(\"--check\", \"Exits with error if there is any unreachable code\") do |f|\n          check = true\n        end\n      end\n\n      opts.on(\"--error-trace\", \"Show full error trace\") do\n        compiler.show_error_trace = true\n        @error_trace = true\n      end\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      if path_filter\n        opts.on(\"-i <path>\", \"--include <path>\", \"Include path\") do |f|\n          includes << f\n        end\n\n        opts.on(\"-e <path>\", \"--exclude <path>\", \"Exclude path (default: lib)\") do |f|\n          excludes << f\n        end\n      end\n\n      unless no_codegen\n        opts.on(\"--ll\", \"Dump ll to Crystal's cache directory\") do\n          compiler.dump_ll = true\n        end\n        opts.on(\"--link-flags FLAGS\", \"Additional flags to pass to the linker\") do |some_link_flags|\n          link_flags << some_link_flags\n        end\n        target_specific_opts(opts, compiler)\n        setup_compiler_warning_options(opts, compiler)\n      end\n\n      opts.on(\"--no-color\", \"Disable colored output\") do\n        @color = false\n        compiler.color = false\n      end\n\n      unless no_codegen\n        opts.on(\"--no-codegen\", \"Don't do code generation\") do\n          compiler.no_codegen = true\n        end\n        opts.on(\"-o FILE\", \"--output FILE\", \"Output path. If a directory, the filename is derived from the first source file (default: ./)\") do |an_output_filename|\n          opt_output_filename = an_output_filename\n          specified_output = true\n        end\n      end\n\n      opts.on(\"--prelude \", \"Use given file as prelude\") do |prelude|\n        compiler.prelude = prelude\n      end\n\n      unless no_codegen\n        opts.on(\"--release\", \"Compile in release mode (-O3 --single-module)\") do\n          compiler.release!\n        end\n        opts.on(\"-O LEVEL\", \"Optimization mode: 0 (default), 1, 2, 3, s, z\") do |level|\n          if mode = Compiler::OptimizationMode.from_level?(level)\n            compiler.optimization_mode = mode\n          else\n            raise Error.new(\"Invalid optimization mode: O#{level}\")\n          end\n        end\n      end\n\n      opts.on(\"-s\", \"--stats\", \"Enable statistics output\") do\n        @progress_tracker.stats = true\n      end\n\n      opts.on(\"-p\", \"--progress\", \"Enable progress output\") do\n        @progress_tracker.progress = true\n      end\n\n      opts.on(\"-t\", \"--time\", \"Enable execution time output\") do\n        @time = true\n      end\n\n      unless no_codegen\n        opts.on(\"--single-module\", \"Generate a single LLVM module\") do\n          compiler.single_module = true\n        end\n        opts.on(\"--threads NUM\", \"Maximum number of threads to use\") do |n_threads|\n          compiler.n_threads = n_threads.to_i? || raise Error.new(\"Invalid thread count: #{n_threads}\")\n        end\n        unless run\n          opts.on(\"--target TRIPLE\", \"Target triple\") do |triple|\n            compiler.codegen_target = Codegen::Target.new(triple)\n          end\n        end\n        opts.on(\"--verbose\", \"Display executed commands\") do\n          compiler.verbose = true\n        end\n        opts.on(\"--static\", \"Link statically\") do\n          compiler.static = true\n        end\n      end\n\n      opts.on(\"--stdin-filename \", \"Source file name to be read from STDIN\") do |stdin_filename|\n        has_stdin_filename = true\n        filenames << stdin_filename\n      end\n\n      if single_file\n        opts.before_each do |arg|\n          opts.stop if !arg.starts_with?('-') && arg.ends_with?(\".cr\")\n          opts.stop if File.file?(arg)\n        end\n      end\n\n      opts.unknown_args do |before, after|\n        opt_filenames = before\n        opt_arguments = after\n      end\n    end\n\n    compiler.link_flags = link_flags.join(' ') unless link_flags.empty?\n\n    filenames += opt_filenames.not_nil!\n    arguments = opt_arguments.not_nil!\n\n    if single_file && (files = filenames[1..-1]?)\n      arguments = files + arguments\n      filenames = [filenames[0]]\n    end\n\n    if filenames.size == 0 || (cursor_command && cursor_location.nil?)\n      STDERR.puts option_parser\n      exit 1\n    end\n\n    sources = [] of Compiler::Source\n    if has_stdin_filename\n      sources << Compiler::Source.new(filenames.shift, STDIN.gets_to_end)\n    end\n    sources.concat gather_sources(filenames)\n\n    output_extension = compiler.cross_compile? ? compiler.codegen_target.object_extension : compiler.codegen_target.executable_extension\n\n    # FIXME: The explicit cast should not be necessary (#15472)\n    output_path = ::Path[opt_output_filename.as?(String) || \"./\"]\n\n    if output_path.ends_with_separator? || File.directory?(output_path)\n      first_filename = sources.first.filename\n      output_filename = (output_path / \"#{::Path[first_filename].stem}#{output_extension}\").normalize.to_s\n\n      # Check if we'll overwrite the main source file\n      if !compiler.no_codegen? && !run && first_filename == File.expand_path(output_filename)\n        error \"compilation will overwrite source file '#{Crystal.relative_filename(first_filename)}', either change its extension to '.cr' or specify an output file with '-o'\"\n      end\n    else\n      output_filename = output_path.to_s\n      if output_path.extension.empty?\n        output_filename += output_extension\n      end\n    end\n\n    output_format ||= allowed_formats[0]\n    unless output_format.in?(allowed_formats)\n      error \"You have input an invalid format: #{output_format}. Supported formats: #{allowed_formats.join(\", \")}\"\n    end\n\n    error \"maximum number of threads cannot be lower than 1\" if compiler.n_threads < 1\n\n    if !compiler.no_codegen? && !run && Dir.exists?(output_filename)\n      error \"can't use `#{output_filename}` as output filename because it's a directory\"\n    end\n\n    if run\n      emit_base_filename = ::Path[sources.first.filename].stem\n    end\n\n    @config = CompilerConfig.new compiler, sources, output_filename, emit_base_filename,\n      arguments, specified_output, hierarchy_exp, cursor_location, output_format.not_nil!,\n      includes, excludes, verbose, check, tallies\n  end\n\n  private def gather_sources(filenames)\n    filenames.map do |filename|\n      filename = File.expand_path(filename)\n      Compiler::Source.new(filename, File.read(filename))\n    end\n  rescue exc : IO::Error\n    error exc\n  end\n\n  private def setup_simple_compiler_options(compiler, opts)\n    opts.on(\"-d\", \"--debug\", \"Add full symbolic debug info\") do\n      compiler.debug = Crystal::Debug::All\n    end\n    opts.on(\"--no-debug\", \"Skip any symbolic debug info\") do\n      compiler.debug = Crystal::Debug::None\n    end\n    opts.on(\"-D FLAG\", \"--define FLAG\", \"Define a compile-time flag\") do |flag|\n      compiler.flags << flag\n    end\n    opts.on(\"--error-trace\", \"Show full error trace\") do\n      @error_trace = true\n      compiler.show_error_trace = true\n    end\n    opts.on(\"--release\", \"Compile in release mode (-O3 --single-module)\") do\n      compiler.release!\n    end\n    opts.on(\"-O LEVEL\", \"Optimization mode: 0 (default), 1, 2, 3, s, z\") do |level|\n      if mode = Compiler::OptimizationMode.from_level?(level)\n        compiler.optimization_mode = mode\n      else\n        raise Error.new(\"Invalid optimization mode: O#{level}\")\n      end\n    end\n    opts.on(\"--single-module\", \"Generate a single LLVM module\") do\n      compiler.single_module = true\n    end\n    opts.on(\"--threads NUM\", \"Maximum number of threads to use\") do |n_threads|\n      compiler.n_threads = n_threads.to_i? || raise Error.new(\"Invalid thread count: #{n_threads}\")\n    end\n    opts.on(\"-s\", \"--stats\", \"Enable statistics output\") do\n      compiler.progress_tracker.stats = true\n    end\n    opts.on(\"-p\", \"--progress\", \"Enable progress output\") do\n      compiler.progress_tracker.progress = true\n    end\n    opts.on(\"-t\", \"--time\", \"Enable execution time output\") do\n      @time = true\n    end\n    opts.on(\"-h\", \"--help\", \"Show this message\") do\n      puts opts\n      exit\n    end\n    opts.on(\"--no-color\", \"Disable colored output\") do\n      @color = false\n      compiler.color = false\n    end\n    target_specific_opts(opts, compiler)\n    setup_compiler_warning_options(opts, compiler)\n    opts.invalid_option { }\n  end\n\n  private def target_specific_opts(opts, compiler)\n    opts.on(\"--mcpu CPU\", \"Target specific cpu type\") do |cpu|\n      if cpu == \"native\"\n        compiler.mcpu = LLVM.host_cpu_name\n      else\n        compiler.mcpu = cpu\n        if cpu == \"help\"\n          # LLVM will display a help message the moment the target machine is\n          # created, but \"help\" is not a valid CPU name, so exit immediately\n          compiler.create_target_machine\n          exit\n        end\n      end\n    end\n    opts.on(\"--mattr CPU\", \"Target specific features\") do |features|\n      compiler.mattr = features\n    end\n    opts.on(\"--mcmodel MODEL\", \"Target specific code model\") do |mcmodel|\n      compiler.mcmodel = case mcmodel\n                         when \"default\" then LLVM::CodeModel::Default\n                         when \"tiny\"    then LLVM::CodeModel::Tiny\n                         when \"small\"   then LLVM::CodeModel::Small\n                         when \"kernel\"  then LLVM::CodeModel::Kernel\n                         when \"medium\"  then LLVM::CodeModel::Medium\n                         when \"large\"   then LLVM::CodeModel::Large\n                         else\n                           error \"--mcmodel should be one of: default, kernel, tiny, small, medium, large\"\n                           raise \"unreachable\"\n                         end\n    end\n  end\n\n  private def setup_compiler_warning_options(opts, compiler)\n    opts.on(\"--warnings all|none\", \"Which warnings to detect. (default: all)\") do |w|\n      compiler.warnings.level = case w\n                                when \"all\"\n                                  Crystal::WarningLevel::All\n                                when \"none\"\n                                  Crystal::WarningLevel::None\n                                else\n                                  error \"--warnings should be all, or none\"\n                                  raise \"unreachable\"\n                                end\n    end\n    opts.on(\"--error-on-warnings\", \"Treat warnings as errors.\") do |w|\n      compiler.warnings.error_on_warnings = true\n    end\n    opts.on(\"--exclude-warnings <path>\", \"Exclude warnings from path (default: lib)\") do |f|\n      compiler.warnings.exclude_lib_path = false\n      compiler.warnings.exclude_path(f)\n    end\n\n    compiler.warnings.exclude_lib_path = true\n  end\n\n  private def validate_emit_values(values)\n    emit_targets = Compiler::EmitTarget::None\n    values.each do |value|\n      if target = Compiler::EmitTarget.parse?(value.gsub('-', '_'))\n        emit_targets |= target\n      else\n        error \"invalid emit value '#{value}'\"\n      end\n    end\n    emit_targets\n  end\n\n  private def error(msg, exit_code = 1)\n    # This is for the case where the main command is wrong\n    @color = false if ARGV.includes?(\"--no-color\") || !Colorize.default_enabled?(STDOUT, STDERR)\n    Crystal.error msg, @color, exit_code: exit_code\n  end\n\n  private def self.crystal_opts\n    ENV[\"CRYSTAL_OPTS\"]?.try { |opts| Process.parse_arguments(opts) }\n  rescue ex\n    raise Error.new(\"Failed to parse CRYSTAL_OPTS: #{ex.message}\")\n  end\n\n  # Constructs an `OptionParser` from the given block and runs it twice, first\n  # time with `CRYSTAL_OPTS`, second time with the given *options*.\n  #\n  # Only flags are accepted in the first run; positional arguments, invalid\n  # options (where they might be treated as normal arguments), and `--` are all\n  # disallowed. The option parser should not define any subcommands.\n  def self.parse_with_crystal_opts(options, & : OptionParser ->)\n    option_parser = OptionParser.new { |opts| yield opts }\n\n    if crystal_opts = self.crystal_opts\n      old_unknown_args = option_parser.@unknown_args\n      old_invalid_option = option_parser.@invalid_option\n      old_before_each = option_parser.@before_each\n\n      option_parser.unknown_args { }\n      option_parser.invalid_option { |opt| raise OptionParser::InvalidOption.new(opt) }\n      option_parser.before_each do |opt|\n        raise Error.new \"CRYSTAL_OPTS may not contain --\" if opt == \"--\"\n      end\n\n      option_parser.parse(crystal_opts)\n      unless crystal_opts.empty?\n        raise Error.new \"CRYSTAL_OPTS may not contain positional arguments\"\n      end\n\n      option_parser.unknown_args(&old_unknown_args) if old_unknown_args\n      option_parser.invalid_option(&old_invalid_option)\n      if old_before_each\n        option_parser.before_each(&old_before_each)\n      else\n        option_parser.before_each { }\n      end\n    end\n\n    option_parser.parse(options)\n    option_parser\n  end\n\n  private def parse_with_crystal_opts(& : OptionParser ->)\n    Command.parse_with_crystal_opts(@options) { |opts| yield opts }\n  end\n\n  private def new_compiler\n    @compiler = Compiler.new\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/compiler.cr",
    "content": "require \"option_parser\"\nrequire \"file_utils\"\nrequire \"colorize\"\nrequire \"crystal/digest/md5\"\n{% if flag?(:msvc) %}\n  require \"./loader\"\n{% end %}\n{% if flag?(:preview_mt) %}\n  require \"wait_group\"\n{% end %}\n\nmodule Crystal\n  @[Flags]\n  enum Debug\n    LineNumbers\n    Variables\n    Default     = LineNumbers\n  end\n\n  enum FramePointers\n    Auto\n    Always\n    NonLeaf\n  end\n\n  # Main interface to the compiler.\n  #\n  # A Compiler parses source code, type checks it and\n  # optionally generates an executable.\n  class Compiler\n    DEFAULT_LINKER = ENV[\"CC\"]? || {{ env(\"CRYSTAL_CONFIG_CC\") || \"cc\" }}\n    MSVC_LINKER    = ENV[\"CC\"]? || {{ env(\"CRYSTAL_CONFIG_CC\") || \"cl.exe\" }}\n\n    # A source to the compiler: its filename and source code.\n    record Source,\n      filename : String,\n      code : String\n\n    # The result of a compilation: the program containing all\n    # the type and method definitions, and the parsed program\n    # as an ASTNode.\n    record Result,\n      program : Program,\n      node : ASTNode\n\n    # If `true`, doesn't generate an executable but instead\n    # creates a `.o` file and outputs a command line to link\n    # it in the target machine.\n    property? cross_compile = false\n\n    # Compiler flags. These will be true when checked in macro\n    # code by the `flag?(...)` macro method.\n    property flags = [] of String\n\n    # Controls generation of frame pointers.\n    property frame_pointers = FramePointers::Auto\n\n    # If `true`, the executable will be generated with debug code\n    # that can be understood by `gdb` and `lldb`.\n    property debug = Debug::Default\n\n    # If `true`, `.ll` files will be generated in the default cache\n    # directory for each generated LLVM module.\n    property? dump_ll = false\n\n    # Additional link flags to pass to the linker.\n    property link_flags : String?\n\n    # Sets the mcpu. Check LLVM docs to learn about this.\n    property mcpu : String?\n\n    # Sets the mattr (features). Check LLVM docs to learn about this.\n    property mattr : String?\n\n    # If `false`, color won't be used in output messages.\n    property? color = true\n\n    # If `true`, skip cleanup process on semantic analysis.\n    property? no_cleanup = false\n\n    # If `true`, no executable will be generated after compilation\n    # (useful to type-check a program)\n    property? no_codegen = false\n\n    # Maximum number of LLVM modules that are compiled in parallel\n    property n_threads : Int32 = {% if flag?(:execution_context) %}\n      Fiber::ExecutionContext.default_workers_count\n    {% elsif flag?(:preview_mt) %}\n      ENV[\"CRYSTAL_WORKERS\"]?.try(&.to_i?) || 4\n    {% elsif flag?(:win32) %}\n      1\n    {% else %}\n      8\n    {% end %}\n\n    # Default prelude file to use. This ends up adding a\n    # `require \"prelude\"` (or whatever name is set here) to\n    # the source file to compile.\n    property prelude = \"prelude\"\n\n    # Optimization mode\n    enum OptimizationMode\n      # [default] no optimization, fastest compilation, slowest runtime\n      O0 = 0\n\n      # low, compilation slower than O0, runtime faster than O0\n      O1 = 1\n\n      # middle, compilation slower than O1, runtime faster than O1\n      O2 = 2\n\n      # high, slowest compilation, fastest runtime\n      # enables with --release flag\n      O3 = 3\n\n      # optimize for size, enables most O2 optimizations but aims for smaller\n      # code size\n      Os\n\n      # optimize aggressively for size rather than speed\n      Oz\n\n      def suffix\n        \".#{to_s.downcase}\"\n      end\n\n      def self.from_level?(level : String) : self?\n        case level\n        when \"0\" then O0\n        when \"1\" then O1\n        when \"2\" then O2\n        when \"3\" then O3\n        when \"s\" then Os\n        when \"z\" then Oz\n        end\n      end\n    end\n\n    # Sets the Optimization mode.\n    property optimization_mode = OptimizationMode::O0\n\n    # Sets the code model. Check LLVM docs to learn about this.\n    property mcmodel = LLVM::CodeModel::Default\n\n    # If `true`, generates a single LLVM module. By default\n    # one LLVM module is created for each type in a program.\n    # --release automatically enable this option\n    property? single_module = false\n\n    # A `ProgressTracker` object which tracks compilation progress.\n    property progress_tracker = ProgressTracker.new\n\n    # Codegen target to use in the compilation.\n    # If not set, asks LLVM the default one for the current machine.\n    property codegen_target = Config.host_target\n\n    # If `true`, prints the link command line that is performed\n    # to create the executable.\n    property? verbose = false\n\n    # If `true`, doc comments are attached to types and methods\n    # and can later be used to generate API docs.\n    property? wants_doc = false\n\n    # Warning settings and all detected warnings.\n    property warnings = WarningCollection.new\n\n    @[Flags]\n    enum EmitTarget\n      ASM\n      OBJ\n      LLVM_BC\n      LLVM_IR\n    end\n\n    # Can be set to a set of flags to emit other files other\n    # than the executable file:\n    # * asm: assembly files\n    # * llvm-bc: LLVM bitcode\n    # * llvm-ir: LLVM IR\n    # * obj: object file\n    property emit_targets : EmitTarget = EmitTarget::None\n\n    # Base filename to use for `emit` output.\n    property emit_base_filename : String?\n\n    # By default the compiler cleans up the default cache directory\n    # to keep the most recent 10 directories used. If this is set\n    # to `false` that cleanup is not performed.\n    property? cleanup = true\n\n    # Default standard output to use in a compilation.\n    property stdout : IO = STDOUT\n\n    # Default standard error to use in a compilation.\n    property stderr : IO = STDERR\n\n    # Whether to show error trace\n    property? show_error_trace = false\n\n    # Whether to link statically\n    property? static = false\n\n    property dependency_printer : DependencyPrinter? = nil\n\n    # Program that was created for the last compilation.\n    property! program : Program\n\n    # Compiles the given *source*, with *output_filename* as the name\n    # of the generated executable.\n    #\n    # Raises `Crystal::CodeError` if there's an error in the\n    # source code.\n    #\n    # Raises `InvalidByteSequenceError` if the source code is not\n    # valid UTF-8.\n    def compile(source : Source | Array(Source), output_filename : String) : Result\n      compile_configure_program(source, output_filename) { }\n    end\n\n    # :ditto:\n    #\n    # Yields a `Program` instance before compiling.\n    def compile_configure_program(source : Source | Array(Source), output_filename : String, & : Program -> Nil) : Result\n      source = [source] unless source.is_a?(Array)\n      program = new_program(source)\n      yield program\n      node = parse program, source\n\n      begin\n        node = program.semantic node, cleanup: !no_cleanup?\n      rescue ex : SkipMacroCodeCoverageException\n        program.macro_expansion_error_hook.try &.call(ex.cause)\n      end\n\n      units = codegen program, node, source, output_filename unless @no_codegen\n\n      @progress_tracker.clear\n      print_macro_run_stats(program)\n      print_codegen_stats(units)\n\n      Result.new program, node\n    end\n\n    # Runs the semantic pass on the given source, without generating an\n    # executable nor analyzing methods. The returned `Program` in the result will\n    # contain all types and methods. This can be useful to generate\n    # API docs, analyze type relationships, etc.\n    #\n    # Raises `Crystal::CodeError` if there's an error in the\n    # source code.\n    #\n    # Raises `InvalidByteSequenceError` if the source code is not\n    # valid UTF-8.\n    def top_level_semantic(source : Source | Array(Source)) : Result\n      source = [source] unless source.is_a?(Array)\n      program = new_program(source)\n      node = parse program, source\n      node, _ = program.top_level_semantic(node)\n\n      @progress_tracker.clear\n      print_macro_run_stats(program)\n\n      Result.new program, node\n    end\n\n    # Set maximum level of optimization.\n    def release!\n      @optimization_mode = OptimizationMode::O3\n      @single_module = true\n    end\n\n    def release?\n      @optimization_mode.o3? && @single_module\n    end\n\n    private def new_program(sources)\n      @program = program = Program.new\n      program.compiler = self\n      program.filename = sources.first.filename\n      program.codegen_target = codegen_target\n      program.target_machine = create_target_machine\n      program.flags << \"release\" if release?\n      program.flags << \"debug\" unless debug.none?\n      program.flags << \"static\" if static?\n      program.flags.concat @flags\n      program.wants_doc = wants_doc?\n      program.color = color?\n      program.stdout = stdout\n      program.show_error_trace = show_error_trace?\n      program.progress_tracker = @progress_tracker\n      program.warnings = @warnings\n      program\n    end\n\n    private def parse(program, sources : Array)\n      @progress_tracker.stage(\"Parse\") do\n        nodes = sources.map do |source|\n          # We add the source to the list of required file,\n          # so it can't be required again\n          program.requires.add source.filename\n          parse(program, source).as(ASTNode)\n        end\n        nodes = Expressions.from(nodes)\n\n        # Prepend the prelude to the parsed program\n        location = Location.new(program.filename, 1, 1)\n        nodes = Expressions.new([Require.new(prelude).at(location), nodes] of ASTNode)\n\n        # And normalize\n        program.normalize(nodes)\n      end\n    end\n\n    private def parse(program, source : Source)\n      parser = program.new_parser(source.code)\n      parser.filename = source.filename\n      parser.wants_doc = wants_doc?\n      parser.parse\n    rescue ex : InvalidByteSequenceError\n      stderr.print colorize(\"Error: \").red.bold\n      stderr.print colorize(\"file '#{Crystal.relative_filename(source.filename)}' is not a valid Crystal source file: \").bold\n      stderr.puts ex.message\n      exit 1\n    end\n\n    private def bc_flags_changed?(output_dir)\n      bc_flags_changed = true\n      current_bc_flags = \"#{@codegen_target}|#{@mcpu}|#{@mattr}|#{@link_flags}|#{@mcmodel}\"\n      bc_flags_filename = \"#{output_dir}/bc_flags#{optimization_mode.suffix}\"\n      if File.file?(bc_flags_filename)\n        previous_bc_flags = File.read(bc_flags_filename).strip\n        bc_flags_changed = previous_bc_flags != current_bc_flags\n      end\n      File.write(bc_flags_filename, current_bc_flags)\n      bc_flags_changed\n    end\n\n    private def codegen(program, node : ASTNode, sources, output_filename)\n      {% if LibLLVM::IS_LT_130 %}\n        if @codegen_target.architecture == \"aarch64\"\n          stderr.puts \"Error: Target #{@codegen_target} requires a Crystal compiler built with LLVM 13 or a later version.\"\n          exit 1\n        end\n      {% end %}\n\n      llvm_modules = @progress_tracker.stage(\"Codegen (crystal)\") do\n        program.codegen node, debug: debug, frame_pointers: frame_pointers,\n          single_module: @single_module || @cross_compile || !@emit_targets.none?\n      end\n\n      output_dir = CacheDir.instance.directory_for(sources)\n\n      bc_flags_changed = bc_flags_changed? output_dir\n      target_triple = target_machine.triple\n\n      units = llvm_modules.map do |type_name, info|\n        llvm_mod = info.mod\n        llvm_mod.target = target_triple\n        CompilationUnit.new(self, program, type_name, llvm_mod, output_dir, bc_flags_changed)\n      end\n\n      {% if LibLLVM::IS_LT_170 %}\n        # initialize the legacy pass manager once in the main thread/process\n        # before we start codegen in threads (MT) or processes (fork)\n        init_llvm_legacy_pass_manager unless optimization_mode.o0?\n      {% end %}\n\n      if @cross_compile\n        cross_compile program, units, output_filename\n      else\n        units = with_file_lock(output_dir) do\n          codegen program, units, output_filename, output_dir\n        end\n\n        {% if flag?(:darwin) %}\n          run_dsymutil(output_filename) unless debug.none?\n        {% end %}\n\n        {% if flag?(:msvc) %}\n          copy_dlls(program, output_filename) unless static?\n        {% end %}\n      end\n\n      CacheDir.instance.cleanup if @cleanup\n\n      units\n    end\n\n    private def with_file_lock(output_dir, &)\n      File.open(File.join(output_dir, \"compiler.lock\"), \"w\") do |file|\n        file.flock_exclusive do\n          yield\n        end\n      end\n    end\n\n    private def run_dsymutil(filename)\n      dsymutil = Process.find_executable(\"dsymutil\")\n      return unless dsymutil\n\n      @progress_tracker.stage(\"dsymutil\") do\n        Process.run(dsymutil, [\"--flat\", filename])\n      end\n    end\n\n    private def copy_dlls(program, output_filename)\n      not_found = nil\n      output_directory = File.dirname(output_filename)\n\n      program.each_dll_path do |path, found|\n        if found\n          dest = File.join(output_directory, File.basename(path))\n          File.copy(path, dest) unless File.exists?(dest)\n        else\n          not_found ||= [] of String\n          not_found << path\n        end\n      end\n\n      if not_found\n        stderr << \"Warning: The following DLLs are required at run time, but Crystal is unable to locate them in CRYSTAL_LIBRARY_PATH, the compiler's directory, or PATH: \"\n        not_found.sort!.join(stderr, \", \")\n      end\n    end\n\n    private def cross_compile(program, units, output_filename)\n      unit = units.first\n      llvm_mod = unit.llvm_mod\n\n      @progress_tracker.stage(\"Codegen (bc+obj)\") do\n        optimize llvm_mod, target_machine unless @optimization_mode.o0?\n\n        unit.emit(@emit_targets, emit_base_filename || output_filename)\n\n        target_machine.emit_obj_to_file llvm_mod, output_filename\n      end\n      object_names = [output_filename]\n      output_filename = output_filename.rchop(unit.object_extension)\n      _, command, args = linker_command(program, object_names, output_filename, nil)\n      print_command(command, args)\n    end\n\n    private def print_command(command, args)\n      stdout.puts command.sub(%(\"${@}\"), args && Process.quote(args))\n    end\n\n    private def linker_command(program : Program, object_names, output_filename, output_dir, expand = false)\n      if program.has_flag? \"msvc\"\n        lib_flags = program.lib_flags(@cross_compile)\n        lib_flags = expand_lib_flags(lib_flags) if expand\n\n        object_arg = Process.quote_windows(object_names)\n        output_arg = Process.quote_windows(\"/Fe#{output_filename}\")\n\n        linker, link_args = program.msvc_compiler_and_flags\n        linker = Process.quote_windows(linker)\n        link_args.map! { |arg| Process.quote_windows(arg) }\n\n        link_args << \"/DEBUG:FULL /PDBALTPATH:%_PDB%\" unless debug.none?\n        link_args << \"/INCREMENTAL:NO /STACK:0x800000\"\n        link_args << lib_flags\n        @link_flags.try { |flags| link_args << flags }\n\n        {% if flag?(:msvc) %}\n          unless @cross_compile\n            extra_suffix = static? ? \"-static\" : \"-dynamic\"\n            search_result = Loader.search_libraries(Process.parse_arguments_windows(link_args.join(' ').gsub('\\n', ' ')), extra_suffix: extra_suffix)\n            if not_found = search_result.not_found?\n              error \"Cannot locate the .lib files for the following libraries: #{not_found.join(\", \")}\"\n            end\n\n            link_args = search_result.remaining_args.concat(search_result.library_paths).map { |arg| Process.quote_windows(arg) }\n          end\n        {% end %}\n\n        args = %(/nologo #{object_arg} #{output_arg} /link #{link_args.join(' ')}).gsub(\"\\n\", \" \")\n        cmd = \"#{linker} #{args}\"\n\n        if cmd.to_utf16.size > 32000\n          # The command line would be too big, pass the args through a UTF-16-encoded file instead.\n          # TODO: Use a proper way to write encoded text to a file when that's supported.\n          # The first character is the BOM; it will be converted in the same endianness as the rest.\n          args_16 = \"\\ufeff#{args}\".to_utf16\n          args_bytes = args_16.to_unsafe_bytes\n\n          args_filename = \"#{output_dir}/linker_args.txt\"\n          File.write(args_filename, args_bytes)\n          cmd = \"#{linker} #{Process.quote_windows(\"@\" + args_filename)}\"\n        end\n\n        {linker, cmd, nil}\n      elsif program.has_flag? \"wasm32\"\n        link_flags = @link_flags || \"\"\n        {\"wasm-ld\", %(wasm-ld \"${@}\" -o #{Process.quote_posix(output_filename)} #{link_flags} -lc #{program.lib_flags(@cross_compile)}), object_names}\n      elsif program.has_flag? \"avr\"\n        link_flags = @link_flags || \"\"\n        link_flags += \" --target=avr-unknown-unknown -mmcu=#{@mcpu} -Wl,--gc-sections\"\n        {DEFAULT_LINKER, %(#{DEFAULT_LINKER} \"${@}\" -o #{Process.quote_posix(output_filename)} #{link_flags} #{program.lib_flags(@cross_compile)}), object_names}\n      elsif program.has_flag?(\"win32\") && program.has_flag?(\"gnu\")\n        link_flags = @link_flags || \"\"\n        link_flags += \" -Wl,--stack,0x800000\"\n        link_flags = use_modern_linker(link_flags)\n        lib_flags = program.lib_flags(@cross_compile)\n        lib_flags = expand_lib_flags(lib_flags) if expand\n        cmd = %(#{DEFAULT_LINKER} #{Process.quote_windows(object_names)} -o #{Process.quote_windows(output_filename)} #{link_flags} #{lib_flags}).gsub('\\n', ' ')\n\n        if cmd.size > 32000\n          # The command line would be too big, pass the args through a file instead.\n          # GCC response file does not interpret those args as shell-escaped\n          # arguments, we must rebuild the whole command line\n          args_filename = \"#{output_dir}/linker_args.txt\"\n          File.open(args_filename, \"w\") do |f|\n            object_names.each do |object_name|\n              f << object_name.gsub(GCC_RESPONSE_FILE_TR) << ' '\n            end\n            f << \"-o \" << output_filename.gsub(GCC_RESPONSE_FILE_TR) << ' '\n            f << link_flags << ' ' << lib_flags\n          end\n          cmd = \"#{DEFAULT_LINKER} #{Process.quote_windows(\"@\" + args_filename)}\"\n        end\n\n        {DEFAULT_LINKER, cmd, nil}\n      else\n        link_flags = @link_flags || \"\"\n        link_flags += \" -rdynamic\"\n\n        if program.has_flag?(\"freebsd\") || program.has_flag?(\"openbsd\")\n          # pkgs are installed to usr/local/lib but it's not in LIBRARY_PATH by\n          # default; we declare it to ease linking on these platforms:\n          link_flags += \" -L/usr/local/lib\"\n        end\n\n        link_flags = use_modern_linker(link_flags)\n\n        {DEFAULT_LINKER, %(#{DEFAULT_LINKER} \"${@}\" -o #{Process.quote_posix(output_filename)} #{link_flags} #{program.lib_flags(@cross_compile)}), object_names}\n      end\n    end\n\n    # Tests if `mold` or `lld` are available and prefers them as linkers over\n    # the default `ld`. Only works when `cc` is the linker driver and can be\n    # disabled with `--link-flags=-fuse-ld=bfd`.\n    private def use_modern_linker(link_flags)\n      return link_flags unless DEFAULT_LINKER == \"cc\"\n      return link_flags if link_flags.includes?(\"-fuse-ld=\")\n\n      if Process.find_executable(\"mold\")\n        link_flags + \" -fuse-ld=mold\"\n      elsif Process.find_executable(\"ld.lld\")\n        link_flags + \" -fuse-ld=lld\"\n      else\n        link_flags\n      end\n    end\n\n    private GCC_RESPONSE_FILE_TR = {\n      \" \":  %q(\\ ),\n      \"'\":  %q(\\'),\n      \"\\\"\": %q(\\\"),\n      \"\\\\\": \"\\\\\\\\\",\n    }\n\n    private def expand_lib_flags(lib_flags)\n      lib_flags.gsub(/`(.*?)`/) do\n        command = $1\n        begin\n          error_io = IO::Memory.new\n          output = Process.run(command, shell: true, output: :pipe, error: error_io) do |process|\n            process.output.gets_to_end\n          end\n          unless $?.success?\n            error_io.rewind\n            error \"Error executing subcommand for linker flags: #{command.inspect}: #{error_io}\"\n          end\n          output.chomp\n        rescue exc\n          error \"Error executing subcommand for linker flags: #{command.inspect}: #{exc}\"\n        end\n      end\n    end\n\n    private def codegen(program, units : Array(CompilationUnit), output_filename, output_dir)\n      object_names = units.map &.object_filename\n\n      @progress_tracker.stage(\"Codegen (bc+obj)\") do\n        @progress_tracker.stage_progress_total = units.size\n\n        n_threads = @n_threads.clamp(1..units.size)\n\n        if n_threads == 1\n          sequential_codegen(units)\n        else\n          parallel_codegen(units, n_threads)\n        end\n\n        if units.size == 1\n          units.first.emit(@emit_targets, emit_base_filename || output_filename)\n        end\n      end\n\n      # We check again because maybe this directory was created in between (maybe with a macro run)\n      if Dir.exists?(output_filename)\n        error \"can't use `#{output_filename}` as output filename because it's a directory\"\n      end\n\n      output_filename = File.expand_path(output_filename)\n\n      @progress_tracker.stage(\"Codegen (linking)\") do\n        Dir.cd(output_dir) do\n          run_linker *linker_command(program, object_names, output_filename, output_dir, expand: true)\n        end\n      end\n\n      units\n    end\n\n    private def sequential_codegen(units)\n      units.each do |unit|\n        unit.compile\n        @progress_tracker.stage_progress += 1\n      end\n    end\n\n    private def parallel_codegen(units, n_threads)\n      {% if flag?(:preview_mt) %}\n        raise \"LLVM isn't multithreaded and cannot fork compiler in multithread mode.\" unless LLVM.multithreaded?\n        mt_codegen(units, n_threads)\n      {% elsif LibC.has_method?(\"fork\") %}\n        fork_codegen(units, n_threads)\n      {% else %}\n        raise \"Cannot fork compiler. `Crystal::System::Process.fork` is not implemented on this system.\"\n      {% end %}\n    end\n\n    private def mt_codegen(units, n_threads)\n      channel = Channel(CompilationUnit).new(n_threads * 2)\n      wg = WaitGroup.new\n      mutex = Mutex.new\n\n      n_threads.times do\n        wg.spawn do\n          while unit = channel.receive?\n            unit.compile(isolate_context: true)\n            mutex.synchronize { @progress_tracker.stage_progress += 1 }\n          end\n        end\n      end\n\n      units.each do |unit|\n        # We generate the bitcode in the main thread because LLVM contexts\n        # must be unique per compilation unit, but we share different contexts\n        # across many modules (or rely on the global context); trying to\n        # codegen in parallel would segfault!\n        #\n        # Luckily generating the bitcode is quick and once the bitcode is\n        # generated we don't need the global LLVM contexts anymore but can\n        # parse the bitcode in an isolated context and we can parallelize the\n        # slowest part: the optimization pass & compiling the object file.\n        unit.generate_bitcode\n\n        channel.send(unit)\n      end\n      channel.close\n\n      wg.wait\n    end\n\n    private def fork_codegen(units, n_threads)\n      workers = fork_workers(n_threads) do |input, output|\n        while i = input.gets(chomp: true).presence\n          unit = units[i.to_i]\n          unit.compile\n          result = {name: unit.name, reused: unit.reused_previous_compilation?}\n          output.puts result.to_json\n        end\n      rescue ex\n        result = {exception: {name: ex.class.name, message: ex.message, backtrace: ex.backtrace}}\n        output.puts result.to_json\n      end\n\n      overqueue = 1\n      indexes = Atomic(Int32).new(0)\n      channel = Channel(String).new(n_threads)\n      completed = Channel(Nil).new(n_threads)\n\n      workers.each do |pid, input, output|\n        spawn do\n          overqueued = 0\n\n          overqueue.times do\n            if (index = indexes.add(1)) < units.size\n              input.puts index\n              overqueued += 1\n            end\n          end\n\n          while (index = indexes.add(1)) < units.size\n            input.puts index\n\n            if response = output.gets(chomp: true)\n              channel.send response\n            else\n              Crystal::System.print_error \"\\nBUG: a codegen process failed\\n\"\n              exit 1\n            end\n          end\n\n          overqueued.times do\n            if response = output.gets(chomp: true)\n              channel.send response\n            else\n              Crystal::System.print_error \"\\nBUG: a codegen process failed\\n\"\n              exit 1\n            end\n          end\n\n          input << '\\n'\n          input.close\n          output.close\n\n          Process.new(pid).wait\n          completed.send(nil)\n        end\n      end\n\n      spawn do\n        n_threads.times { completed.receive }\n        channel.close\n      end\n\n      while response = channel.receive?\n        result = JSON.parse(response)\n\n        if ex = result[\"exception\"]?\n          Crystal::System.print_error \"\\nBUG: a codegen process failed: %s (%s)\\n\", ex[\"message\"].as_s, ex[\"name\"].as_s\n          ex[\"backtrace\"].as_a?.try(&.each { |frame| Crystal::System.print_error \"  from %s\\n\", frame })\n          exit 1\n        end\n\n        if @progress_tracker.stats?\n          if result[\"reused\"].as_bool\n            name = result[\"name\"].as_s\n            unit = units.find! { |unit| unit.name == name }\n            unit.reused_previous_compilation = true\n          end\n        end\n        @progress_tracker.stage_progress += 1\n      end\n    end\n\n    private def fork_workers(n_threads, &)\n      workers = [] of {Int32, IO::FileDescriptor, IO::FileDescriptor}\n\n      n_threads.times do\n        iread, iwrite = IO.pipe\n        oread, owrite = IO.pipe\n\n        iwrite.flush_on_newline = true\n        owrite.flush_on_newline = true\n\n        pid = Crystal::System::Process.fork do\n          iwrite.close\n          oread.close\n\n          yield iread, owrite\n\n          iread.close\n          owrite.close\n          exit 0\n        end\n\n        iread.close\n        owrite.close\n\n        workers << {pid, iwrite, oread}\n      end\n\n      workers\n    end\n\n    private def print_macro_run_stats(program)\n      return unless @progress_tracker.stats?\n      return if program.compiled_macros_cache.empty?\n\n      puts\n      puts \"Macro runs:\"\n      program.compiled_macros_cache.each do |filename, compiled_macro_run|\n        print \" - \"\n        print filename\n        print \": \"\n        if compiled_macro_run.reused\n          print \"reused previous compilation (#{compiled_macro_run.elapsed})\"\n        else\n          print compiled_macro_run.elapsed\n        end\n        puts\n      end\n    end\n\n    private def print_codegen_stats(units)\n      return unless @progress_tracker.stats?\n      return unless units\n\n      reused = units.count(&.reused_previous_compilation?)\n\n      puts\n      puts \"Codegen (bc+obj):\"\n      case reused\n      when units.size\n        puts \" - all previous .o files were reused\"\n      when .zero?\n        puts \" - no previous .o files were reused\"\n      else\n        puts \" - #{reused}/#{units.size} .o files were reused\"\n        puts\n        puts \"These modules were not reused:\"\n        units.each do |unit|\n          next if unit.reused_previous_compilation?\n          puts \" - #{unit.original_name} (#{unit.name}.bc)\"\n        end\n      end\n    end\n\n    getter(target_machine : LLVM::TargetMachine) do\n      create_target_machine\n    end\n\n    def create_target_machine\n      @codegen_target.to_target_machine(@mcpu || \"\", @mattr || \"\", @optimization_mode, @mcmodel)\n    rescue ex : ArgumentError\n      stderr.print colorize(\"Error: \").red.bold\n      stderr.print \"llc: \"\n      stderr.puts ex.message\n      exit 1\n    end\n\n    {% if LibLLVM::IS_LT_170 %}\n      property! pass_manager_builder : LLVM::PassManagerBuilder\n\n      private def init_llvm_legacy_pass_manager\n        registry = LLVM::PassRegistry.instance\n        registry.initialize_all\n\n        builder = LLVM::PassManagerBuilder.new\n        builder.size_level = 0\n\n        case optimization_mode\n        in .o3?\n          builder.opt_level = 3\n          builder.use_inliner_with_threshold = 275\n        in .o2?\n          builder.opt_level = 2\n          builder.use_inliner_with_threshold = 275\n        in .o1?\n          builder.opt_level = 1\n          builder.use_inliner_with_threshold = 150\n        in .o0?\n          # default behaviour, no optimizations\n        in .os?\n          builder.opt_level = 2\n          builder.size_level = 1\n          builder.use_inliner_with_threshold = 50\n        in .oz?\n          builder.opt_level = 2\n          builder.size_level = 2\n          builder.use_inliner_with_threshold = 5\n        end\n\n        @pass_manager_builder = builder\n      end\n\n      private def optimize_with_pass_manager(llvm_mod)\n        fun_pass_manager = llvm_mod.new_function_pass_manager\n        pass_manager_builder.populate fun_pass_manager\n        fun_pass_manager.run llvm_mod\n\n        module_pass_manager = LLVM::ModulePassManager.new\n        pass_manager_builder.populate module_pass_manager\n        module_pass_manager.run llvm_mod\n      end\n    {% end %}\n\n    protected def optimize(llvm_mod, target_machine)\n      {% if LibLLVM::IS_LT_130 %}\n        optimize_with_pass_manager(llvm_mod)\n      {% else %}\n        {% if LibLLVM::IS_LT_170 %}\n          # PassBuilder doesn't support Os and Oz before LLVM 17\n          if @optimization_mode.os? || @optimization_mode.oz?\n            return optimize_with_pass_manager(llvm_mod)\n          end\n        {% end %}\n\n        LLVM::PassBuilderOptions.new do |options|\n          LLVM.run_passes(llvm_mod, \"default<#{@optimization_mode}>\", target_machine, options)\n        end\n      {% end %}\n    end\n\n    private def run_linker(linker_name, command, args)\n      print_command(command, args) if verbose?\n\n      begin\n        Process.run(command, args, shell: true,\n          input: Process::Redirect::Close, output: Process::Redirect::Inherit, error: Process::Redirect::Pipe) do |process|\n          process.error.each_line(chomp: false) do |line|\n            hint_string = colorize(\"(this usually means you need to install the development package for lib\\\\1)\").yellow.bold\n            line = line.gsub(/cannot find -l(\\S+)\\b/, \"cannot find -l\\\\1 #{hint_string}\")\n            line = line.gsub(/unable to find library -l(\\S+)\\b/, \"unable to find library -l\\\\1 #{hint_string}\")\n            line = line.gsub(/library not found for -l(\\S+)\\b/, \"library not found for -l\\\\1 #{hint_string}\")\n            STDERR << line\n          end\n        end\n      rescue exc : File::AccessDeniedError | File::NotFoundError\n        linker_not_found exc.class, linker_name\n      end\n\n      status = $?\n      unless status.success?\n        exit_code = status.exit_code?\n        case exit_code\n        when 126\n          linker_not_found File::AccessDeniedError, linker_name\n        when 127\n          linker_not_found File::NotFoundError, linker_name\n        when nil\n          # abnormal exit\n          exit_code = 1\n        end\n        error \"execution of command failed with exit status #{status}: #{command}\", exit_code: exit_code\n      end\n    end\n\n    private def linker_not_found(exc_class, linker_name)\n      verbose_info = \"\\nRun with `--verbose` to print the full linker command.\" unless verbose?\n      case exc_class\n      when File::AccessDeniedError\n        error \"Could not execute linker: `#{linker_name}`: Permission denied#{verbose_info}\"\n      else\n        error \"Could not execute linker: `#{linker_name}`: File not found#{verbose_info}\"\n      end\n    end\n\n    private def error(msg, exit_code = 1)\n      Crystal.error msg, @color, exit_code, stderr: stderr\n    end\n\n    private def colorize(obj)\n      obj.colorize.toggle(@color)\n    end\n\n    # An LLVM::Module with information to compile it.\n    class CompilationUnit\n      getter compiler\n      getter name\n      getter original_name\n      getter llvm_mod\n      property? reused_previous_compilation = false\n      getter object_extension : String\n      @memory_buffer : LLVM::MemoryBuffer?\n      @object_name : String?\n      @bc_name : String?\n\n      def initialize(@compiler : Compiler, program : Program, @name : String,\n                     @llvm_mod : LLVM::Module, @output_dir : String, @bc_flags_changed : Bool)\n        @name = \"_main\" if @name == \"\"\n        @original_name = @name\n        @name = String.build do |str|\n          @name.each_char do |char|\n            case char\n            when 'a'..'z', '0'..'9', '_'\n              str << char\n            when 'A'..'Z'\n              # Because OSX has case insensitive filenames, try to avoid\n              # clash of 'a' and 'A' by using 'A-' for 'A'.\n              str << char << '-'\n            else\n              str << char.ord\n            end\n          end\n        end\n\n        if @name.size > 50\n          # 17 chars from name + 1 (dash) + 32 (md5) = 50\n          @name = \"#{@name[0..16]}-#{::Crystal::Digest::MD5.hexdigest(@name)}\"\n        end\n\n        @name = \"#{@name}#{@compiler.optimization_mode.suffix}\"\n        @object_extension = compiler.codegen_target.object_extension\n      end\n\n      def generate_bitcode\n        @memory_buffer ||= llvm_mod.write_bitcode_to_memory_buffer\n      end\n\n      # To compile a file we first generate a `.bc` file and then create an\n      # object file from it. These `.bc` files are stored in the cache\n      # directory.\n      #\n      # On a next compilation of the same project, and if the compile flags\n      # didn't change (a combination of the target triple, mcpu and link flags,\n      # amongst others), we check if the new `.bc` file is exactly the same as\n      # the old one. In that case the `.o` file will also be the same, so we\n      # simply reuse the old one. Generating an `.o` file is what takes most\n      # time.\n      #\n      # However, instead of directly generating the final `.o` file from the\n      # `.bc` file, we generate it to a temporary name (`.o.tmp`) and then we\n      # rename that file to `.o`. We do this because the compiler could be\n      # interrupted while the `.o` file is being generated, leading to a\n      # corrupted file that later would cause compilation issues. Moving a file\n      # is an atomic operation so no corrupted `.o` file should be generated.\n      def compile(isolate_context = false)\n        if must_compile?\n          isolate_module_context if isolate_context\n          update_bitcode_cache\n          compile_to_object\n        else\n          @reused_previous_compilation = true\n        end\n        dump_llvm_ir\n      end\n\n      private def must_compile?\n        memory_buffer = generate_bitcode\n\n        return true unless compiler.emit_targets.none?\n        return true if @bc_flags_changed\n        return true unless File.exists?(bc_name)\n        return true unless File.exists?(object_name)\n\n        # If the user cancelled a previous compilation\n        # it might be that the .o file is empty\n        return true if File.size(object_name) == 0\n\n        memory_io = IO::Memory.new(memory_buffer.to_slice)\n\n        changed = File.open(bc_name) { |bc_file| !IO.same_content?(bc_file, memory_io) }\n\n        memory_buffer.dispose unless changed\n\n        changed\n      end\n\n      # Parse the previously generated bitcode into the LLVM module using a\n      # dedicated context, so we can safely optimize & compile the module in\n      # multiple threads (llvm contexts can't be shared across threads).\n      private def isolate_module_context\n        @llvm_mod = LLVM::Module.parse(@memory_buffer.not_nil!, LLVM::Context.new)\n      end\n\n      private def update_bitcode_cache\n        return unless memory_buffer = @memory_buffer\n\n        # Delete existing .o file. It cannot be used anymore.\n        File.delete?(object_name)\n        # Create the .bc file (for next compilations)\n        File.write(bc_name, memory_buffer.to_slice)\n        memory_buffer.dispose\n      end\n\n      private def compile_to_object\n        temporary_object_name = self.temporary_object_name\n        target_machine = compiler.create_target_machine\n        compiler.optimize llvm_mod, target_machine unless compiler.optimization_mode.o0?\n        target_machine.emit_obj_to_file llvm_mod, temporary_object_name\n        File.rename(temporary_object_name, object_name)\n      end\n\n      private def dump_llvm_ir\n        llvm_mod.print_to_file ll_name if compiler.dump_ll?\n      end\n\n      def emit(emit_targets : EmitTarget, output_filename)\n        if emit_targets.asm?\n          compiler.target_machine.emit_asm_to_file llvm_mod, \"#{output_filename}.s\"\n        end\n        if emit_targets.llvm_bc?\n          FileUtils.cp(bc_name, \"#{output_filename}.bc\")\n        end\n        if emit_targets.llvm_ir?\n          llvm_mod.print_to_file \"#{output_filename}.ll\"\n        end\n        if emit_targets.obj?\n          FileUtils.cp(object_name, output_filename + @object_extension)\n        end\n      end\n\n      def object_name\n        Crystal.relative_filename(\"#{@output_dir}/#{object_filename}\")\n      end\n\n      def object_filename\n        @name + @object_extension\n      end\n\n      def temporary_object_name\n        Crystal.relative_filename(\"#{@output_dir}/#{object_filename}.tmp\")\n      end\n\n      def bc_name\n        \"#{@output_dir}/#{@name}.bc\"\n      end\n\n      def bc_name_new\n        \"#{@output_dir}/#{@name}.new.bc\"\n      end\n\n      def ll_name\n        \"#{@output_dir}/#{@name}.ll\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/config.cr",
    "content": "require \"./codegen/target\"\n\nmodule Crystal\n  module Config\n    class_property path : String = {{env(\"CRYSTAL_CONFIG_PATH\") || \"\"}}\n\n    def self.version\n      {{ read_file(\"#{__DIR__}/../../VERSION\").chomp }}\n    end\n\n    def self.description\n      String.build do |io|\n        io << \"Crystal \" << version\n        io << \" [\" << build_commit << \"]\" if build_commit\n        io << \" (\" << date << \")\" unless date.empty?\n\n        io << \"\\n\\nThe compiler was not built in release mode.\" unless release_mode?\n\n        io << \"\\n\\nLLVM: \" << LLVM.version\n        io << \"\\nDefault target: \" << host_target\n        io << \"\\n\"\n      end\n    end\n\n    def self.build_commit\n      sha = {{ env(\"CRYSTAL_CONFIG_BUILD_COMMIT\") || \"\" }}\n      sha = nil if sha.empty?\n\n      sha\n    end\n\n    def self.date\n      source_date_epoch = {{ (t = env(\"SOURCE_DATE_EPOCH\")) && !t.empty? ? t.to_i : nil }}\n      if source_date_epoch\n        Time.unix(source_date_epoch).to_s(\"%Y-%m-%d\")\n      else\n        \"\"\n      end\n    end\n\n    def self.release_mode?\n      {{ flag?(:release) }}\n    end\n\n    def self.exec_path\n      ENV.fetch(\"CRYSTAL_EXEC_PATH\") do\n        executable_path = Process.executable_path || return\n        File.dirname(executable_path)\n      end\n    end\n\n    @@host_target : Crystal::Codegen::Target?\n\n    def self.host_target : Crystal::Codegen::Target\n      @@host_target ||= begin\n        target = Crystal::Codegen::Target.new({{env(\"CRYSTAL_CONFIG_TARGET\")}} || LLVM.default_target_triple)\n\n        if target.linux?\n          # The statically linked linux binary runs as well on linux-gnu as\n          # on linux-musl, but the default target needs to match the C\n          # library used on the current system.\n          # This can be automatically detected from the output of `ldd --version`\n          # in order to use the appropriate environment target.\n          default_libc = target.gnu? ? \"-gnu\" : \"-musl\"\n\n          target = Crystal::Codegen::Target.new(target.to_s.sub(default_libc, \"-#{linux_runtime_libc}\"))\n        end\n\n        if target.macos?\n          # By default, LLVM infers the target macOS SDK version from the host\n          # version detected in `LLVM.default_target_triple`, and independently\n          # Clang will infer a different one from several options: `-target`,\n          # `-mtargetos`, `-mmacosx-version-min`, `$MACOSX_DEPLOYMENT_TARGET`,\n          # `-isysroot`, then finally the LLVM default (see\n          # https://github.com/llvm/llvm-project/blob/5f58f3dda8b17f664a85d4e5e3c808edde41ff46/clang/lib/Driver/ToolChains/Darwin.cpp#L2293-L2378\n          # for details). If the one we use is higher than the linker's, each\n          # object file being linked will produce a warning:\n          #\n          # > ld: warning: object file (...o0.o) was built for newer macOS version (15.0) than being linked (11.0)\n          #\n          # We have to match our SDK version with that of the linker. As long as\n          # we are not passing any of those command-line options to Clang in\n          # `Crystal::Compiler#linker_command`, the only override we need to\n          # handle ourselves is the environment variable one.\n          #\n          # Note that other platforms (e.g. iOS, tvOS) use different environment\n          # variables!\n          if min_version = ENV[\"MACOSX_DEPLOYMENT_TARGET\"]?\n            triple = \"#{target.architecture}-#{target.vendor}-macosx#{min_version}\"\n            target = Crystal::Codegen::Target.new(triple)\n          end\n        end\n\n        target\n      end\n    end\n\n    def self.linux_runtime_libc\n      ldd_version = String.build do |io|\n        Process.run(\"ldd\", {\"--version\"}, output: io, error: io)\n      rescue\n        # In case of an error (eg. `ldd` not available), we assume it's gnu.\n        return \"gnu\"\n      end\n\n      # Generally, `ldd --version` should print `musl`.\n      # But there is a bug in alpine 3.10 which breaks `ldd --version`.\n      # But detection still works with `-musl`, and it doesn't do harm in other\n      # cases.\n      if ldd_version.starts_with?(\"musl\") || ldd_version.includes?(\"-musl\")\n        \"musl\"\n      else\n        \"gnu\"\n      end\n    end\n\n    def self.library_path\n      {{env(\"CRYSTAL_CONFIG_LIBRARY_PATH\") || \"\"}}\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/crystal_path.cr",
    "content": "require \"./config\"\nrequire \"./error\"\n\nmodule Crystal\n  struct CrystalPath\n    class NotFoundError < Crystal::Error\n      getter filename\n      getter relative_to\n\n      def initialize(@filename : String, @relative_to : String?)\n        if relative_to = @relative_to\n          super \"can't find #{@filename.inspect} relative to #{relative_to.inspect}\"\n        else\n          super \"can't find #{@filename.inspect}\"\n        end\n      end\n    end\n\n    private DEFAULT_LIB_PATH = \"lib\"\n\n    def self.default_paths : Array(String)\n      if path = ENV[\"CRYSTAL_PATH\"]?\n        path_array = path.split(Process::PATH_DELIMITER, remove_empty: true)\n      elsif path = Crystal::Config.path.presence\n        path_array = path.split(Process::PATH_DELIMITER, remove_empty: true)\n        unless path_array.includes?(DEFAULT_LIB_PATH)\n          path_array.unshift DEFAULT_LIB_PATH\n        end\n      else\n        path_array = [DEFAULT_LIB_PATH]\n      end\n\n      expand_paths(path_array)\n\n      path_array\n    end\n\n    def self.default_path : String\n      default_paths.join(Process::PATH_DELIMITER)\n    end\n\n    # Expand `$ORIGIN` in the paths to the directory where the compiler binary\n    # is located (at runtime).\n    # For install locations like\n    #    `/path/prefix/bin/crystal`         for the compiler\n    #    `/path/prefix/share/crystal/src`   for the standard library\n    # the path `$ORIGIN/../share/crystal/src` resolves to\n    # the standard library location.\n    # This generic path can be passed into the compiler via CRYSTAL_CONFIG_PATH\n    # to produce a portable binary that resolves the standard library path\n    # relative to the compiler location, independent of the absolute path.\n    def self.expand_paths(paths, origin)\n      paths.map! do |path|\n        if (chopped = path.lchop?(\"$ORIGIN\")) && chopped[0].in?(::Path::SEPARATORS)\n          if origin.nil?\n            raise \"Missing executable path to expand $ORIGIN path\"\n          end\n          File.join(origin, chopped)\n        else\n          path\n        end\n      end\n    end\n\n    def self.expand_paths(paths)\n      origin = nil\n      if executable_path = Process.executable_path\n        origin = File.dirname(executable_path)\n      end\n      expand_paths(paths, origin)\n    end\n\n    property entries : Array(String)\n\n    def initialize(@entries : Array(String) = CrystalPath.default_paths, codegen_target = Config.host_target)\n      add_target_path(codegen_target)\n      @current_dir = Dir.current\n    end\n\n    private def add_target_path(codegen_target)\n      target = \"#{codegen_target.architecture}-#{codegen_target.os_name}\"\n\n      @entries.each do |path|\n        path = File.join(path, \"lib_c\", target)\n        if Dir.exists?(path)\n          @entries << path unless @entries.includes?(path)\n          return\n        end\n      end\n    end\n\n    def find(filename, relative_to = nil) : Array(String)\n      relative_to = File.dirname(relative_to) if relative_to.is_a?(String)\n\n      if filename.starts_with? '.'\n        result = find_in_path_relative_to_dir(filename, relative_to)\n      else\n        result = find_in_crystal_path(filename)\n      end\n\n      unless result\n        raise NotFoundError.new(filename, relative_to)\n      end\n\n      result = [result] if result.is_a?(String)\n      result\n    end\n\n    private def find_in_path_relative_to_dir(filename, relative_to)\n      return unless relative_to.is_a?(String)\n\n      # Check if it's a wildcard.\n      if filename.ends_with?(\"/*\") || (recursive = filename.ends_with?(\"/**\"))\n        filename_dir_index = filename.rindex!('/')\n        filename_dir = filename[0..filename_dir_index]\n        relative_dir = \"#{relative_to}/#{filename_dir}\"\n        if File.exists?(relative_dir)\n          files = [] of String\n          gather_dir_files(relative_dir, files, recursive)\n          return files\n        end\n\n        return nil\n      end\n\n      each_file_expansion(filename, relative_to) do |path|\n        absolute_path = File.expand_path(path, dir: @current_dir)\n        return absolute_path if File.file?(absolute_path)\n      end\n\n      nil\n    end\n\n    def each_file_expansion(filename, relative_to, &)\n      relative_filename = \"#{relative_to}/#{filename}\"\n      # Check if .cr file exists.\n      yield relative_filename.ensure_suffix(\".cr\")\n\n      filename_is_relative = filename.starts_with?('.')\n\n      shard_name, _, shard_path = filename.partition(\"/\")\n      shard_path = shard_path.presence\n\n      if !filename_is_relative && shard_path\n        shard_src = \"#{relative_to}/#{shard_name}/src\"\n        shard_path_stem = shard_path.rchop(\".cr\")\n\n        # If it's \"foo/bar/baz\", check if \"foo/src/bar/baz.cr\" exists (for a shard, non-namespaced structure)\n        yield \"#{shard_src}/#{shard_path_stem}.cr\"\n\n        # Then check if \"foo/src/foo/bar/baz.cr\" exists (for a shard, namespaced structure)\n        yield \"#{shard_src}/#{shard_name}/#{shard_path_stem}.cr\"\n\n        # If it's \"foo/bar/baz\", check if \"foo/bar/baz/baz.cr\" exists (std, nested)\n        basename = File.basename(relative_filename, \".cr\")\n        yield \"#{relative_filename}/#{basename}.cr\"\n\n        # If it's \"foo/bar/baz\", check if \"foo/src/foo/bar/baz/baz.cr\" exists (shard, non-namespaced, nested)\n        yield \"#{shard_src}/#{shard_path}/#{shard_path_stem}.cr\"\n\n        # If it's \"foo/bar/baz\", check if \"foo/src/foo/bar/baz/baz.cr\" exists (shard, namespaced, nested)\n        yield \"#{shard_src}/#{shard_name}/#{shard_path}/#{shard_path_stem}.cr\"\n      else\n        basename = File.basename(relative_filename, \".cr\")\n\n        # If it's \"foo\", check if \"foo/foo.cr\" exists (for the std, nested)\n        yield \"#{relative_filename}/#{basename}.cr\"\n\n        unless filename_is_relative\n          # If it's \"foo\", check if \"foo/src/foo.cr\" exists (for a shard)\n          yield \"#{relative_filename}/src/#{basename}.cr\"\n        end\n      end\n    end\n\n    private def gather_dir_files(dir, files_accumulator, recursive)\n      files = [] of String\n      dirs = [] of String\n\n      Dir.each_child(dir) do |filename|\n        full_name = \"#{dir}/#{filename}\"\n\n        if File.directory?(full_name)\n          if recursive\n            dirs << filename\n          end\n        else\n          if filename.ends_with?(\".cr\")\n            files << full_name\n          end\n        end\n      end\n\n      files.sort!\n      dirs.sort!\n\n      files.each do |file|\n        files_accumulator << File.expand_path(file, dir: @current_dir)\n      end\n\n      dirs.each do |subdir|\n        gather_dir_files(\"#{dir}/#{subdir}\", files_accumulator, recursive)\n      end\n    end\n\n    private def find_in_crystal_path(filename)\n      @entries.each do |path|\n        required = find_in_path_relative_to_dir(filename, path)\n        return required if required\n      end\n\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/error.cr",
    "content": "module Crystal\n  # Base class for all errors in the compiler.\n  class Error < ::Exception\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/exception.cr",
    "content": "require \"./util\"\nrequire \"./error\"\nrequire \"colorize\"\n\nmodule Crystal\n  # Base class for all errors related to specific user code.\n  abstract class CodeError < Error\n    property? color = false\n    property? error_trace = false\n    property? warning = false\n\n    @filename : String | VirtualFile | Nil\n\n    def to_s(io) : Nil\n      to_s_with_source(io, nil)\n    end\n\n    abstract def to_s_with_source(io : IO, source)\n\n    def to_json(json : JSON::Builder)\n      json.array do\n        to_json_single(json)\n      end\n    end\n\n    def true_filename(filename = @filename) : String\n      if filename.is_a? VirtualFile\n        loc = filename.expanded_location\n        if loc\n          true_filename loc.filename\n        else\n          \"\"\n        end\n      else\n        if filename\n          filename\n        else\n          \"\"\n        end\n      end\n    end\n\n    def to_s_with_source(source)\n      String.build do |io|\n        to_s_with_source(io, source)\n      end\n    end\n\n    def relative_filename(filename)\n      Crystal.relative_filename(filename)\n    end\n\n    def colorize(obj)\n      obj.colorize.toggle(@color)\n    end\n\n    def with_color\n      Colorize.with.toggle(@color)\n    end\n\n    def replace_leading_tabs_with_spaces(line)\n      found_non_space = false\n      line.gsub do |char|\n        if found_non_space\n          char\n        elsif char == '\\t'\n          ' '\n        elsif char.ascii_whitespace?\n          char\n        else\n          found_non_space = true\n          char\n        end\n      end\n    end\n  end\n\n  module ErrorFormat\n    MACRO_LINES_TO_SHOW               = 3\n    OFFSET_FROM_LINE_NUMBER_DECORATOR = 6\n\n    def error_body(source, default_message) : String | Nil\n      case filename = @filename\n      in VirtualFile\n        return format_macro_error(filename)\n      in String\n        if File.file?(filename)\n          return format_error_from_file(filename)\n        end\n      in Nil\n        # go on\n      end\n\n      return format_error(source) if source\n      default_message\n    end\n\n    def line_number_decorator(line_number)\n      \" #{line_number} | \"\n    end\n\n    def append_error_indicator(io, offset, column_number, size = 0)\n      size ||= 0\n      io << '\\n'\n      io << (\" \" * (offset + column_number - 1))\n      with_color.green.bold.surround(io) do\n        io << '^'\n        if size > 0\n          io << (\"-\" * (size - 1))\n        end\n      end\n    end\n\n    def filename_row_col_message(filename, line_number, column_number)\n      String.build do |io|\n        io << colorize(\"#{relative_filename(filename)}:#{line_number}:#{column_number}\").underline\n      end\n    end\n\n    def format_error(filename, lines, line_number, column_number, size = 0)\n      return \"#{relative_filename(filename)}\" unless line_number\n\n      unless line = lines[line_number - 1]?\n        return filename_row_col_message(filename, line_number, column_number)\n      end\n\n      String.build do |io|\n        case filename\n        in String\n          io << filename_row_col_message(filename, line_number, column_number)\n        in VirtualFile\n          io << \"macro '\" << colorize(\"#{filename.macro.name}\").underline << '\\''\n        in Nil\n          io << \"unknown location\"\n        end\n\n        decorator = line_number_decorator(line_number)\n        lstripped_line = line.lstrip\n        space_delta = line.size - lstripped_line.size\n        # Column number should start at `1`. We're using `0` to track bogus passed\n        # `column_number`.\n        final_column_number = (column_number - space_delta).clamp(0..)\n\n        io << \"\\n\\n\"\n        io << colorize(decorator).dim << colorize(lstripped_line.chomp).bold\n        append_error_indicator(io, decorator.size, final_column_number, size || 0)\n      end\n    end\n\n    def format_error_from_file(filename : String)\n      lines = File.read_lines(filename)\n      formatted_error = format_error(\n        filename: @filename,\n        lines: lines,\n        line_number: @line_number,\n        column_number: @column_number,\n        size: @size\n      )\n      \"In #{formatted_error}\"\n    end\n\n    def format_macro_error(virtual_file : VirtualFile)\n      show_where_macro_expanded = !(@error_trace && self.responds_to?(:error_trace=))\n      String.build do |io|\n        io << \"There was a problem expanding macro '#{virtual_file.macro.name}'\"\n        io << \"\\n\\n\"\n        if show_where_macro_expanded\n          append_where_macro_expanded(io, virtual_file)\n          io << '\\n'\n        end\n        io << \"Called macro defined in \"\n        append_macro_definition_location(io, virtual_file)\n        io << \"\\n\\n\"\n        io << \"Which expanded to:\"\n        io << \"\\n\\n\"\n        append_expanded_macro(io, virtual_file.source)\n      end\n    end\n\n    def remaining(lines : Array(String))\n      String.build do |io|\n        return if lines.empty?\n        io << \"\\n\\n\"\n        io << lines.skip_while(&.blank?).join('\\n')\n      end\n    end\n\n    def source_lines(filename)\n      case filename\n      in Nil\n        nil\n      in String\n        if File.file? filename\n          File.read_lines(filename)\n        else\n          nil\n        end\n      in VirtualFile\n        filename.source.lines\n      end\n    end\n\n    def append_macro_definition_location(io, filename : VirtualFile)\n      macro_source = filename.macro.location\n      source_filename = macro_source.try &.filename\n      line_number = macro_source.try &.line_number\n      column_number = macro_source.try &.column_number\n\n      case source_filename\n      in String\n        io << colorize(\"#{relative_filename(source_filename)}:#{line_number}:#{column_number}\").underline\n      in VirtualFile\n        io << \"macro '\" << colorize(\"#{source_filename.macro.name}\").underline << '\\''\n      in Nil\n        \"unknown location\"\n      end\n\n      lines = source_lines(source_filename)\n\n      if lines && line_number\n        io << \"\\n\\n\"\n        io << colorize(line_number_decorator(line_number)).dim\n        io << lines[line_number - 1].lstrip\n      end\n    end\n\n    def minimize_indentation(source)\n      min_leading_white_space =\n        source.min_of? { |line| leading_white_space(line) } || 0\n\n      if min_leading_white_space > 0\n        source = source.map do |line|\n          replace_leading_tabs_with_spaces(line).lchop(\" \" * min_leading_white_space)\n        end\n      end\n\n      {source, min_leading_white_space}\n    end\n\n    private def leading_white_space(line)\n      match = line.match(/^(\\s+)\\S/)\n      return 0 unless match\n\n      spaces = match[1]?\n      return 0 unless spaces\n\n      spaces.size\n    end\n\n    def append_expanded_macro(io, source)\n      line_number = @line_number\n      if @error_trace || !line_number\n        source, _ = minimize_indentation(source.lines)\n        io << Crystal.with_line_numbers(source, line_number, @color)\n      else\n        to_index = line_number.clamp(0..source.lines.size)\n        from_index = {0, to_index - MACRO_LINES_TO_SHOW}.max\n        source_slice = source.lines[from_index...to_index]\n        source_slice, spaces_removed = minimize_indentation(source_slice)\n\n        io << Crystal.with_line_numbers(source_slice, line_number, @color, from_index + 1)\n        offset = OFFSET_FROM_LINE_NUMBER_DECORATOR + line_number.to_s.size - spaces_removed\n        append_error_indicator(io, offset, @column_number, @size)\n      end\n    end\n\n    def append_where_macro_expanded(io, filename : VirtualFile)\n      expanded_source = filename.expanded_location\n      return unless expanded_source\n      source_filename = expanded_source.filename\n      lines = source_lines(source_filename)\n      return unless lines\n\n      io << \"Code in \" << format_error(\n        filename: source_filename,\n        lines: lines,\n        line_number: expanded_source.line_number,\n        column_number: expanded_source.column_number,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/ffi/call_interface.cr",
    "content": "module Crystal::FFI\n  # This type represents the signature of a function call made through LibFFI.\n  #\n  # Different functions sharing the same signature can share the same `CallInterface`\n  # instance.\n  # Variadic functions need separate `CallInterface` instances for every variadic\n  # signature.\n  struct CallInterface\n    def self.new(return_type : FFI::Type, arg_types : Array(FFI::Type), *, abi : LibFFI::ABI = :default) : CallInterface\n      # TODO: is there a way to avoid this malloc?\n      cif = Pointer(LibFFI::Cif).malloc(1)\n\n      status = LibFFI.prep_cif(\n        cif,\n        abi,\n        arg_types.size,\n        return_type,\n        arg_types.map(&.to_unsafe),\n      )\n\n      unless status.ok?\n        raise \"Error on LibFFI.prep_cif: #{status}\"\n      end\n\n      new(cif)\n    end\n\n    def self.variadic(return_type : FFI::Type, arg_types : Array(FFI::Type), fixed_args : Int32, *, abi : LibFFI::ABI = :default) : CallInterface\n      unless 0 <= fixed_args <= arg_types.size\n        raise \"invalid value for fixed_args\"\n      end\n\n      # TODO: is there a way to avoid this malloc?\n      cif = Pointer(LibFFI::Cif).malloc(1)\n\n      status = LibFFI.prep_cif_var(\n        cif,\n        abi,\n        fixed_args,\n        arg_types.size,\n        return_type,\n        arg_types.map(&.to_unsafe),\n      )\n\n      unless status.ok?\n        raise \"Error on LibFFI.prep_cif_var: #{status}\"\n      end\n\n      new(cif)\n    end\n\n    def initialize(@cif : Pointer(LibFFI::Cif))\n    end\n\n    # Calls the function at *function_pointer* with this call interface and the\n    # given *arguments*.\n    # The return value is assigned to *return_value*.\n    def call(function_pointer : Void*, arguments : Pointer(Void*), return_value : Void*)\n      LibFFI.call(\n        @cif,\n        function_pointer,\n        return_value,\n        arguments,\n      )\n    end\n\n    def inspect(io : IO)\n      io << \"FFI::CallInterface(\"\n      io << @cif.value\n      io << \")\"\n    end\n\n    def to_unsafe\n      @cif\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/ffi/closure.cr",
    "content": "require \"./ffi\"\n\nmodule Crystal::FFI\n  struct Closure\n    def initialize(call_interface : CallInterface, closure_fun : LibFFI::ClosureFun, user_data : Void*)\n      @closure = LibFFI.closure_alloc(LibFFI::SIZEOF_CLOSURE, out @code)\n      unless @closure\n        raise \"Error on LibFFI.closure_alloc\"\n      end\n\n      status = LibFFI.prep_closure_loc(@closure, call_interface, closure_fun, user_data, @code)\n      unless status.ok?\n        raise \"Error on LibFFI.prep_closure_loc: #{status}\"\n      end\n    end\n\n    def cast(t : T.class) forall T\n      T.new(@code.as(Pointer(Void)), Pointer(Void).null)\n    end\n\n    def to_unsafe\n      @code\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/ffi/ffi.cr",
    "content": "{% skip_file if flag?(:without_ffi) %}\nrequire \"./lib_ffi\"\nrequire \"./type\"\nrequire \"./call_interface\"\nrequire \"./closure\"\n\n# :nodoc:\nmodule Crystal::FFI\nend\n"
  },
  {
    "path": "src/compiler/crystal/ffi/lib_ffi.cr",
    "content": "# Supported library versions:\n#\n# * libffi\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#compiler-dependencies\nmodule Crystal\n  @[Link(\"ffi\", pkg_config: \"libffi\")]\n  {% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n    @[Link(dll: \"libffi.dll\")]\n  {% end %}\n  lib LibFFI\n    {% begin %}\n    enum ABI\n      {% if flag?(:x86_64) && flag?(:win32) %}\n        WIN64 = 1\n        GNUW64\n\n        {% if flag?(:gnu) %}\n          DEFAULT = GNUW64\n        {% else %}\n          DEFAULT = WIN64\n        {% end %}\n      {% elsif flag?(:x86_64) && flag?(:unix) %}\n        UNIX64 = 2\n        WIN64\n        EFI64 = WIN64\n        GNUW64\n\n        DEFAULT = UNIX64\n      {% elsif flag?(:i386) && flag?(:win32) %}\n        SYSV = 1\n        STDCALL\n        THISCALL\n        FASTCALL\n        MS_CDECL\n        PASCAL\n        REGISTER\n        LAST\n\n        DEFAULT = MS_CDECL\n      {% elsif flag?(:i386) && flag?(:unix) %}\n        SYSV     = 1\n        THISCALL = 3\n        FASTCALL\n        STDCALL\n        PASCAL\n        REGISTER\n        MS_CDECL\n        LAST\n\n        DEFAULT = SYSV\n      {% elsif flag?(:aarch64) %}\n        SYSV = 1\n        WIN64\n        LAST\n\n        {% if flag?(:win32) %}\n          DEFAULT = WIN64\n        {% else %}\n          DEFAULT = SYSV\n        {% end %}\n      {% elsif flag?(:arm) %}\n        SYSV = 1\n        VFP\n\n        {% if flag?(:armhf) || flag?(:win32) %}\n          DEFAULT = VFP\n        {% else %}\n          DEFAULT = SYSV\n        {% end %}\n      {% else %}\n        {% raise \"Unsupported target for ABI\" %}\n      {% end %}\n    end\n    {% end %}\n\n    enum Status\n      OK          = 0\n      BAD_TYPEDEF\n      BAD_ABI\n      BAD_ARGTYPE\n    end\n\n    enum TypeEnum : UInt16\n      VOID       =  0\n      INT        =  1\n      FLOAT      =  2\n      DOUBLE     =  3\n      LONGDOUBLE =  4\n      UINT8      =  5\n      SINT8      =  6\n      UINT16     =  7\n      SINT16     =  8\n      UINT32     =  9\n      SINT32     = 10\n      UINT64     = 11\n      SINT64     = 12\n      STRUCT     = 13\n      POINTER    = 14\n      COMPLEX    = 15\n    end\n\n    struct Cif\n      abi : ABI\n      nargs : LibC::UInt\n      arg_types : Type**\n      rtype : Type*\n      bytes : LibC::UInt\n      flags : LibC::UInt\n    end\n\n    struct Type\n      size : LibC::SizeT\n      alignment : UInt16\n      type : TypeEnum\n      elements : Type**\n    end\n\n    $ffi_type_void : Type\n    $ffi_type_uint8 : Type\n    $ffi_type_sint8 : Type\n    $ffi_type_uint16 : Type\n    $ffi_type_sint16 : Type\n    $ffi_type_uint32 : Type\n    $ffi_type_sint32 : Type\n    $ffi_type_uint64 : Type\n    $ffi_type_sint64 : Type\n    $ffi_type_float : Type\n    $ffi_type_double : Type\n    $ffi_type_pointer : Type\n\n    # We're treating ffi_closure as an opaque type because we do not need to interact\n    # with it directly and only pass a pointer around. We only need to allocate\n    # the memory, but the memory size differs based on target and ABI version (https://github.com/libffi/libffi/pull/540)\n    # We define the maximum possible size for each target and allocate that amount\n    # of memory, even if less would suffice. Overallocating a couple of bytes should\n    # not cause any issues\n    # https://github.com/crystal-lang/crystal/pull/12192#issuecomment-1173993292\n    # https://github.com/libffi/libffi/blob/ddc6764386b29449d941b2b18d000f2987a9d848/doc/libffi.texi#L815\n    type Closure = Void\n\n    {% if flag?(:bits64) %}\n      SIZEOF_CLOSURE = 56\n    {% else %}\n      SIZEOF_CLOSURE = 40\n    {% end %}\n\n    alias ClosureFun = Cif*, Void*, Void**, Void* -> Void\n\n    fun prep_cif = ffi_prep_cif(\n      cif : Cif*,\n      abi : ABI,\n      nargs : LibC::UInt,\n      rtype : Type*,\n      atypes : Type**,\n    ) : Status\n\n    fun prep_cif_var = ffi_prep_cif_var(\n      cif : Cif*,\n      abi : ABI,\n      nfixedargs : LibC::UInt,\n      varntotalargs : LibC::UInt,\n      rtype : Type*,\n      atypes : Type**,\n    ) : Status\n\n    @[Raises]\n    fun call = ffi_call(\n      cif : Cif*,\n      fn : Void*,\n      rvalue : Void*,\n      avalue : Void**,\n    ) : Void\n\n    fun closure_alloc = ffi_closure_alloc(size : LibC::SizeT, code : Void**) : Closure*\n    fun closure_free = ffi_closure_free(Void*)\n    fun prep_closure_loc = ffi_prep_closure_loc(\n      closure : Closure*,\n      cif : Cif*,\n      fun : ClosureFun,\n      user_data : Void*,\n      code_loc : Void*,\n    ) : Status\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/ffi/type.cr",
    "content": "require \"./ffi\"\n\nmodule Crystal::FFI\n  struct Type\n    def initialize(@type : LibFFI::Type*, @elements : Array(Type)? = nil)\n      # TODO: we store @elements here to avoid the GC,\n      # maybe that should be stored somewhere else.\n      # But maybe libffi already dups these?\n    end\n\n    def to_unsafe\n      @type\n    end\n\n    def self.void\n      new(pointerof(LibFFI.ffi_type_void))\n    end\n\n    def self.uint8\n      new(pointerof(LibFFI.ffi_type_uint8))\n    end\n\n    def self.sint8\n      new(pointerof(LibFFI.ffi_type_sint8))\n    end\n\n    def self.uint16\n      new(pointerof(LibFFI.ffi_type_uint16))\n    end\n\n    def self.sint16\n      new(pointerof(LibFFI.ffi_type_sint16))\n    end\n\n    def self.uint32\n      new(pointerof(LibFFI.ffi_type_uint32))\n    end\n\n    def self.sint32\n      new(pointerof(LibFFI.ffi_type_sint32))\n    end\n\n    def self.uint64\n      new(pointerof(LibFFI.ffi_type_uint64))\n    end\n\n    def self.sint64\n      new(pointerof(LibFFI.ffi_type_sint64))\n    end\n\n    def self.float\n      new(pointerof(LibFFI.ffi_type_float))\n    end\n\n    def self.double\n      new(pointerof(LibFFI.ffi_type_double))\n    end\n\n    def self.pointer\n      new(pointerof(LibFFI.ffi_type_pointer))\n    end\n\n    def self.struct(elements : Array(Type))\n      elements_ptr = Pointer(LibFFI::Type*).malloc(elements.size + 1)\n      elements.each_with_index do |element, i|\n        elements_ptr[i] = element.to_unsafe\n      end\n      elements_ptr[elements.size] = Pointer(LibFFI::Type).null\n\n      pointer = Pointer(LibFFI::Type).malloc(1)\n      pointer.value = LibFFI::Type.new(\n        type: LibFFI::TypeEnum::STRUCT,\n        elements: elements_ptr,\n      )\n      new(pointer, elements)\n    end\n\n    def inspect(io : IO)\n      io << \"FFI::Type(\"\n      io << @type.value\n      io << \")\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/formatter.cr",
    "content": "# This file exists so you can do:\n#\n# ```\n# require \"compiler/crystal/formatter\"\n# ```\n#\n# and use Crystal's formatter programmatically, without\n# requiring all the semantic and codegen code.\n\nrequire \"./tools/formatter\"\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/c.cr",
    "content": "require \"../types\"\nrequire \"./value\"\nrequire \"./repl\"\n\n# In this file we define how each Crystal type that can be passed to C\n# is mapped to C for libffi.\n\nmodule Crystal\n  class Type\n    # Must return an FFI::Type for this type.\n    def ffi_type : FFI::Type\n      raise \"BUG: missing ffi_type for #{self} (#{self.class})\"\n    end\n\n    # Returns an FFI::Type to be used as a C function argument.\n    def ffi_arg_type : FFI::Type\n      ffi_type\n    end\n  end\n\n  class BoolType\n    def ffi_type : FFI::Type\n      FFI::Type.uint8\n    end\n  end\n\n  class IntegerType\n    def ffi_type : FFI::Type\n      case kind\n      when .i8?  then FFI::Type.sint8\n      when .u8?  then FFI::Type.uint8\n      when .i16? then FFI::Type.sint16\n      when .u16? then FFI::Type.uint16\n      when .i32? then FFI::Type.sint32\n      when .u32? then FFI::Type.uint32\n      when .i64? then FFI::Type.sint64\n      when .u64? then FFI::Type.uint64\n      else\n        raise \"BUG: missing ffi_type for #{self} (#{self.class})\"\n      end\n    end\n  end\n\n  class FloatType\n    def ffi_type : FFI::Type\n      case kind\n      when .f32? then FFI::Type.float\n      when .f64? then FFI::Type.double\n      else\n        raise \"BUG: missing ffi_type for #{self} (#{self.class})\"\n      end\n    end\n  end\n\n  class EnumType\n    def ffi_type : FFI::Type\n      base_type.ffi_type\n    end\n  end\n\n  class PointerInstanceType\n    def ffi_type : FFI::Type\n      FFI::Type.pointer\n    end\n  end\n\n  class ProcInstanceType\n    def ffi_type : FFI::Type\n      FFI::Type.pointer\n    end\n\n    # Returns a FFI::CallInterface for this proc, suitable for calling it.\n    getter(ffi_call_interface : FFI::CallInterface) do\n      FFI::CallInterface.new(\n        return_type.ffi_type,\n        arg_types.map(&.ffi_arg_type)\n      )\n    end\n  end\n\n  class NilType\n    def ffi_type : FFI::Type\n      # Nil is used to pass a null pointer\n      FFI::Type.pointer\n    end\n  end\n\n  class NoReturnType\n    def ffi_type : FFI::Type\n      FFI::Type.void\n    end\n  end\n\n  class TypeDefType\n    def ffi_type : FFI::Type\n      typedef.ffi_type\n    end\n  end\n\n  class NonGenericClassType\n    def ffi_type : FFI::Type\n      FFI::Type.struct(all_instance_vars.map do |name, var|\n        var.type.ffi_type\n      end)\n    end\n  end\n\n  class StaticArrayInstanceType\n    def ffi_type : FFI::Type\n      element_ffi_type = element_type.ffi_type\n      FFI::Type.struct(\n        Array.new(size.as(NumberLiteral).value.to_i, element_ffi_type)\n      )\n    end\n\n    def ffi_arg_type : FFI::Type\n      FFI::Type.pointer\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/cast.cr",
    "content": "require \"./compiler\"\n\n# In this file we define the operations that cast types to other types,\n# be it to expand them to fit \"bigger\" types (upcast) or to shrink them\n# when a type is restricted, like when using `is_a?` (downcast).\n# This is similar to codegen/cast.cr except that instead of producing\n# LLVM code it works on the value on the top of the stack.\nclass Crystal::Repl::Compiler\n  private def upcast(node : ASTNode, from : Type, to : Type)\n    from = from.remove_indirection\n    to = to.remove_indirection\n\n    return if from == to\n\n    upcast_distinct(node, from, to)\n  end\n\n  private def upcast(node : ASTNode, from : Nil, to : Type)\n    # Nothing to do when casting from nil (NoReturn), as it's NoReturn\n  end\n\n  private def upcast_distinct(node : ASTNode, from : TypeDefType, to : Type)\n    upcast_distinct node, from.typedef, to\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilableType, to : MixedUnionType)\n    put_nilable_type_in_union(aligned_sizeof_type(to), node: nil)\n  end\n\n  private def upcast_distinct(node : ASTNode, from : MixedUnionType, to : MixedUnionType)\n    # It might happen that some types inside the union `value_type` are not inside `target_type`,\n    # for example with named tuple of same keys with different order. In that case we need cast\n    # those value to the correct type before finally storing them in the target union.\n    needs_union_value_cast = from.union_types.any? do |from_element|\n      needs_value_cast_inside_union?(from_element, to)\n    end\n\n    if needs_union_value_cast\n      # Compute the values that need a cast\n      types_needing_cast = from.union_types.select do |union_type|\n        needs_value_cast_inside_union?(union_type, to)\n      end\n\n      end_jumps = [] of Int32\n\n      types_needing_cast.each do |type_needing_cast|\n        # Find compatible type\n        compatible_type = to.union_types.find! { |union_type| type_needing_cast.implements?(union_type) }\n\n        # Get the type id of the \"from\" union\n        get_union_type_id(aligned_sizeof_type(from), node: node)\n\n        # Check if `from_type_id` is the same as `type_needing_cast`\n        put_i32 type_id(type_needing_cast), node: node\n        cmp_i32 node: node\n        cmp_eq node: node\n\n        # If they are not the same, continue\n        branch_unless 0, node: nil\n        cond_jump_location = patch_location\n\n        # We need to upcast from type_needing_cast to compatible_type\n        upcast(node, type_needing_cast, compatible_type)\n\n        # Then we need to set the correct union type id\n        put_union_type_id(type_id(compatible_type), aligned_sizeof_type(to), node: node)\n\n        # Then jump to the end\n        jump 0, node: nil\n        end_jumps << patch_location\n\n        # The unless above should jump here, which is the start\n        # of the next if, or just the end\n        patch_jump(cond_jump_location)\n      end\n    end\n\n    # Putting a smaller union type inside a bigger one is just extending the value\n    difference = aligned_sizeof_type(to) - aligned_sizeof_type(from)\n\n    if difference > 0\n      push_zeros(difference, node: nil)\n    end\n\n    # If needs_union_value_cast was true, we have a bunch of\n    # if .. then that need to jump to the end of everything.\n    # Here we do that.\n    if end_jumps\n      end_jumps.each do |end_jump|\n        patch_jump(end_jump)\n      end\n    end\n  end\n\n  private def needs_value_cast_inside_union?(value_type, union_type)\n    # A type needs a special cast if:\n    # 1. It's a tuple or named tuple\n    # 2. It's not inside the target union\n    # 3. There's a compatible type inside the target union\n    return false unless value_type.is_a?(TupleInstanceType) || value_type.is_a?(NamedTupleInstanceType)\n    !union_type.union_types.any?(&.==(value_type)) &&\n      union_type.union_types.any? { |ut| value_type.implements?(ut) || ut.implements?(value_type) }\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilType, to : MixedUnionType)\n    # Nil inside a union is all zeros\n    push_zeros(aligned_sizeof_type(to), node: nil)\n  end\n\n  private def upcast_distinct(node : ASTNode, from : PrimitiveType | EnumType | NonGenericClassType | GenericClassInstanceType | GenericClassInstanceMetaclassType | MetaclassType, to : MixedUnionType)\n    # It might happen that `from` is not of the union but it's compatible with one of them.\n    # We need to first cast the value to the compatible type and to `to`.\n    # This same logic exists in codegen/cast.cr\n    case from\n    when TupleInstanceType, NamedTupleInstanceType\n      unless to.union_types.any? &.==(from)\n        compatible_type = to.union_types.find! { |ut| from.implements?(ut) }\n        upcast(node, from, compatible_type)\n        return upcast(node, compatible_type, to)\n      end\n    end\n\n    put_in_union(type_id(from), aligned_sizeof_type(from), aligned_sizeof_type(to), node: nil)\n  end\n\n  private def upcast_distinct(node : ASTNode, from : VirtualMetaclassType, to : MixedUnionType)\n    # We have a type ID (8 bytes) in the stack.\n    # We need to put that into a union whose:\n    # - tag will be the type ID (8 bytes)\n    # - value will be the type ID (8 bytes) followed by zeros to fill up the union size\n\n    # We already have 8 bytes in the stack. That's the tag.\n    # Now we put the type ID again for the value. So far we have 16 bytes in total.\n    dup 8, node: nil\n\n    # Then fill out the rest of the union\n    remaining = aligned_sizeof_type(to) - 16\n    push_zeros remaining, node: nil if remaining > 0\n  end\n\n  private def upcast_distinct(node : ASTNode, from : ReferenceUnionType | NilableReferenceUnionType | VirtualType, to : MixedUnionType)\n    put_reference_type_in_union(aligned_sizeof_type(to), node: nil)\n  end\n\n  private def upcast_distinct(node : ASTNode, from : VirtualType, to : VirtualType)\n    # TODO: not tested\n    # Nothing to do: both are represented as pointers which already carry the type ID\n  end\n\n  private def upcast_distinct(node : ASTNode, from : ReferenceUnionType, to : VirtualType)\n    # TODO: not tested\n    # Nothing to do: both are represented as pointers which already carry the type ID\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NonGenericClassType, to : VirtualType)\n    # Nothing: both are represented as pointers\n  end\n\n  private def upcast_distinct(node : ASTNode, from : GenericClassInstanceType, to : VirtualType)\n    # TODO: not tested\n    # Nothing to do: both are represented as pointers which already carry the type ID\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilableType | NilableReferenceUnionType, to : NilType)\n    # TODO: not tested\n    # TODO: this is actually a downcast so it's not right, but this is also present\n    # in the main compiler so something should be fixed there first\n    pop aligned_sizeof_type(from), node: nil\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilType, to : NilableType)\n    put_i64 0_i64, node: nil\n  end\n\n  private def upcast_distinct(node : ASTNode, from : Type, to : NilableType)\n    # Nothing: both are represented as pointers\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilType, to : NilableReferenceUnionType)\n    # Transform nil (nothing) into a null pointer\n    put_i64 0_i64, node: nil\n  end\n\n  private def upcast_distinct(node : ASTNode, from : Type, to : NilableReferenceUnionType)\n    # Nothing: both are represented as pointers\n  end\n\n  private def upcast_distinct(node : ASTNode, from : Type, to : ReferenceUnionType)\n    # Nothing: both are represented as pointers\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilType, to : NilableProcType)\n    # This is Proc.new(Pointer(Void).null, Pointer(Void).null)\n    put_i64 0, node: nil\n    put_i64 0, node: nil\n  end\n\n  private def upcast_distinct(node : ASTNode, from : ProcInstanceType, to : ProcInstanceType)\n    # TODO: not tested (happens for example with Proc(NoReturn) to Proc(Nil))\n    # Nothing\n  end\n\n  private def upcast_distinct(node : ASTNode, from : ProcInstanceType, to : NilableProcType)\n    # Nothing\n  end\n\n  # TODO: remove these two because they are probably not needed\n  private def upcast_distinct(node : ASTNode, from : NoReturnType, to : Type)\n    # Nothing\n  end\n\n  private def upcast_distinct(node : ASTNode, from : Type, to : NoReturnType)\n    # Nothing\n  end\n\n  private def upcast_distinct(node : ASTNode, from : EnumType, to : IntegerType)\n    # TODO: not tested\n    # Nothing\n  end\n\n  private def upcast_distinct(node : ASTNode, from : TupleInstanceType, to : TupleInstanceType)\n    # If we are here it means the tuples are different\n    cast_tuple(node, from, to)\n\n    # Finally, we must pop the original tuple that was casted\n    pop_from_offset aligned_sizeof_type(from), aligned_sizeof_type(to), node: nil\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NamedTupleInstanceType, to : NamedTupleInstanceType)\n    # If we are here it means the tuples are different\n    cast_named_tuple(node, from, to)\n\n    # Finally, we must pop the original tuple that was casted\n    pop_from_offset aligned_sizeof_type(from), aligned_sizeof_type(to), node: nil\n  end\n\n  # Unpacks a tuple into a series of types.\n  # Each of the tuple elements is upcasted to the corresponding type in `to_types`.\n  # Every individual element is stack-aligned. Use `#cast_tuple` instead if they\n  # should follow their natural alignments inside a target tuple type. Nils\n  # inside `to_types` are discarded.\n  # It's the caller's responsibility to pop the original, unpacked tuple, from the\n  # stack if needed.\n  private def unpack_tuple(node : ASTNode, from : TupleInstanceType, to_types : Array(Type?))\n    from_aligned_size = aligned_sizeof_type(from)\n    to_element_offset = 0\n\n    to_types.each_with_index do |to_element_type, i|\n      next unless to_element_type\n\n      from_element_type = from.tuple_types[i]\n\n      from_inner_size = inner_sizeof_type(from_element_type)\n\n      from_element_offset = @context.offset_of(from, i)\n\n      # Copy inner size bytes from the tuple.\n      # The interpreter will make sure to align this value.\n      # Go back `from_aligned_size` plus any elements already pushed onto the stack,\n      # but then move forward (subtracting) to reach the element in `from`.\n      copy_from(from_aligned_size - from_element_offset + to_element_offset, from_inner_size, node: nil)\n\n      # Then upcast it to the target tuple element type\n      upcast node, from_element_type, to_element_type\n\n      # Now we have element_type in front of the tuple so we must skip it\n      to_element_offset += aligned_sizeof_type(to_element_type)\n    end\n  end\n\n  private def cast_tuple(node : ASTNode, from : TupleInstanceType, to : TupleInstanceType)\n    from_aligned_size = aligned_sizeof_type(from)\n    to_element_offset = 0\n\n    to.tuple_types.each_with_index do |to_element_type, i|\n      from_element_type = from.tuple_types[i]\n\n      from_inner_size = inner_sizeof_type(from_element_type)\n\n      from_element_offset = @context.offset_of(from, i)\n\n      # Copy inner size bytes from the tuple.\n      # The interpreter will make sure to align this value.\n      # Go back `from_aligned_size` plus any elements already pushed onto the stack,\n      # but then move forward (subtracting) to reach the element in `from`.\n      copy_from(from_aligned_size - from_element_offset + to_element_offset, from_inner_size, node: nil)\n\n      # Then upcast it to the target tuple element type\n      upcast node, from_element_type, to_element_type\n\n      # the new value is stack-aligned; adjust as necessary to follow the\n      # element's natural alignment inside the target type\n      next_to_offset =\n        if i == to.tuple_types.size - 1\n          aligned_sizeof_type(to)\n        else\n          @context.offset_of(to, i + 1)\n        end\n\n      difference = next_to_offset - to_element_offset - aligned_sizeof_type(to_element_type)\n      if difference > 0\n        push_zeros(difference, node: nil)\n      elsif difference < 0\n        pop(-difference, node: nil)\n      end\n\n      # Now we have element_type in front of the tuple so we must skip it\n      to_element_offset = next_to_offset\n    end\n  end\n\n  private def cast_named_tuple(node : ASTNode, from : NamedTupleInstanceType, to : NamedTupleInstanceType)\n    from_aligned_size = aligned_sizeof_type(from)\n    to_element_offset = 0\n\n    from_entry_indices = to.entries.map_with_index do |to_entry, i|\n      from.entries.index! do |other_entry|\n        other_entry.name == to_entry.name\n      end\n    end\n\n    to.entries.each_with_index do |to_entry, i|\n      from_entry_index = from_entry_indices[i]\n      from_entry = from.entries[from_entry_index]\n\n      from_element_type = from_entry.type\n      to_element_type = to_entry.type\n\n      from_inner_size = inner_sizeof_type(from_element_type)\n\n      from_element_offset = @context.offset_of(from, from_entry_index)\n\n      # Copy inner size bytes from the tuple.\n      # The interpreter will make sure to align this value.\n      # Go back `from_aligned_size` plus any elements already pushed onto the stack,\n      # but then move forward (subtracting) to reach the element in `from`.\n      copy_from(from_aligned_size - from_element_offset + to_element_offset, from_inner_size, node: nil)\n\n      # Then upcast it to the target tuple element type\n      upcast node, from_element_type, to_element_type\n\n      # the new value is stack-aligned; adjust as necessary to follow the\n      # element's natural alignment inside the target type\n      next_to_offset =\n        if i == to.entries.size - 1\n          aligned_sizeof_type(to)\n        else\n          @context.offset_of(to, i + 1)\n        end\n\n      difference = next_to_offset - to_element_offset - aligned_sizeof_type(to_element_type)\n      if difference > 0\n        push_zeros(difference, node: nil)\n      elsif difference < 0\n        pop(-difference, node: nil)\n      end\n\n      # Now we have element_type in front of the tuple so we must skip it\n      to_element_offset = next_to_offset\n    end\n  end\n\n  private def upcast_distinct(node : ASTNode, from : NilType, to : VoidType)\n    # TODO: not tested\n    # Nothing to do\n  end\n\n  private def upcast_distinct(node : ASTNode, from : MetaclassType | VirtualMetaclassType | GenericClassInstanceMetaclassType, to : VirtualMetaclassType)\n  end\n\n  private def upcast_distinct(node : ASTNode, from : Type, to : Type)\n    node.raise \"BUG: missing upcast_distinct from #{from} to #{to} (#{from.class} to #{to.class})\"\n  end\n\n  private def downcast(node : ASTNode, from : Type, to : Type)\n    from = from.remove_indirection\n    to = to.remove_indirection\n\n    return if from == to\n\n    downcast_distinct(node, from, to)\n  end\n\n  private def downcast(node : ASTNode, from : Type, to : Nil)\n    # Nothing to do when casting to nil (NoReturn)\n  end\n\n  private def downcast_distinct(node : ASTNode, from : Type, to : TypeDefType)\n    downcast_distinct node, from, to.typedef\n  end\n\n  private def downcast_distinct(node : ASTNode, from : MixedUnionType, to : MixedUnionType)\n    # It might happen that some types inside the union `from_type` are not inside `to_type`,\n    # for example with named tuple of same keys with different order. In that case we need cast\n    # those value to the correct type before finally storing them in the target union.\n    needs_union_value_cast = from.union_types.any? do |from_element|\n      needs_value_cast_inside_union?(from_element, to)\n    end\n\n    if needs_union_value_cast # Compute the values that need a cast\n      node.raise \"BUG: missing mixed union downcast from #{from} to #{to}\"\n    end\n\n    difference = aligned_sizeof_type(from) - aligned_sizeof_type(to)\n\n    if difference > 0\n      pop(difference, node: nil)\n    end\n  end\n\n  private def downcast_distinct(node : ASTNode, from : MixedUnionType, to : PrimitiveType | EnumType | NonGenericClassType | GenericClassInstanceType | GenericClassInstanceMetaclassType | NilableType | NilableProcType | NilableReferenceUnionType | ReferenceUnionType | MetaclassType | VirtualType | VirtualMetaclassType)\n    remove_from_union(aligned_sizeof_type(from), aligned_sizeof_type(to), node: nil)\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableType, to : NonGenericClassType | GenericClassInstanceType)\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableType, to : NilType)\n    pop sizeof(Pointer(Void)), node: nil\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableReferenceUnionType, to : VirtualType | NonGenericClassType | GenericClassInstanceType | NilableType)\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableReferenceUnionType, to : ReferenceUnionType)\n    # TODO: not tested\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableReferenceUnionType, to : NilableReferenceUnionType)\n    # TODO: not tested\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableReferenceUnionType, to : NilType)\n    # TODO: not tested\n    pop aligned_sizeof_type(from), node: nil\n  end\n\n  private def downcast_distinct(node : ASTNode, from : ReferenceUnionType, to : VirtualType | NonGenericClassType | GenericClassInstanceType | ReferenceUnionType)\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableProcType, to : ProcInstanceType)\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : ProcInstanceType, to : ProcInstanceType)\n    # Nothing to do\n    # This is when Proc(T) is casted to Proc(Nil)\n  end\n\n  private def downcast_distinct(node : ASTNode, from : NilableProcType, to : NilType)\n    # TODO: not tested\n    pop 16, node: nil\n  end\n\n  private def downcast_distinct(node : ASTNode, from : VirtualType, to : NonGenericClassType | GenericClassInstanceType | ReferenceUnionType | NilableReferenceUnionType)\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : VirtualType, to : VirtualType)\n    # TODO: not tested\n    # Nothing to do\n  end\n\n  private def downcast_distinct(node : ASTNode, from : VirtualMetaclassType, to : MetaclassType | VirtualMetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType)\n    # Nothing to do\n  end\n\n  # TODO: remove these two because they are probably not needed\n  private def downcast_distinct(node : ASTNode, from : NoReturnType, to : Type)\n    # Nothing\n  end\n\n  private def downcast_distinct(node : ASTNode, from : Type, to : NoReturnType)\n    # Nothing\n  end\n\n  private def downcast_distinct(node : ASTNode, from : Type, to : Type)\n    node.raise \"BUG: missing downcast_distinct from #{from} to #{to} (#{from.class} to #{to.class})\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/class_vars.cr",
    "content": "require \"./repl\"\n\n# This class keeps track of all the class variables the interpreter will\n# need access to, and allows you to declare and access their position\n# in memory, and whether they have an initializer or not.\n#\n# The interpreter holds a memory region for class variables, for example like this:\n#\n# [_,_,_,_,_,_,_,_,_,......]\n#  ^-----^ ^-------^\n#   A.@@a     B.@@b\n#\n# In this memory, for each class variables there are 8 bytes that indicate\n# whether the class variable was initialized or not. If a class variable\n# has no initializer it's considered to be initialized when the program\n# starts (see `#each_initialized_index`). After these 8 bytes comes the\n# class variable data, aligned to 8 bytes boundaries so that the GC\n# can properly track pointers (that's why we also use 8 bytes for the `initialized` bit.)\n#\n# This class and `Constants` are very similar.\nclass Crystal::Repl::ClassVars\n  # The offset to use after the index of a class variable to get access to its data.\n  OFFSET_FROM_INITIALIZED = 8\n\n  # Each class variable is determined by its owner and name\n  record Key, owner : Type, name : String\n\n  # For each class variable we record its index and whether it\n  # has an initializer, in the form of a CompiledDef (has the initializer\n  # already compiled to bytecode)\n  record Value, index : Int32, compiled_def : CompiledDef?\n\n  def initialize(@context : Context)\n    @data = {} of Key => Value\n    @bytesize = 0\n  end\n\n  # Returns the total amount of bytes needed to store all known class variables so far.\n  def bytesize\n    @bytesize\n  end\n\n  # Declares a new class variable. Returns the index in memory where it will be stored.\n  # `compiled_def` is the class var's initializer compiled to bytecode, if there was\n  # an initializer.\n  # Note that at that index the `initializer` \"bit\" (8 bytes) should be stored,\n  # and only after `OFFSET_FROM_INITIALIZER` the data should be stored.\n  def declare(owner : Type, name : String, type : Type, compiled_def : CompiledDef?) : Int32\n    key = Key.new(owner, name)\n\n    index = @bytesize\n    @data[key] = Value.new(index, compiled_def)\n\n    @bytesize += OFFSET_FROM_INITIALIZED\n    @bytesize += @context.aligned_sizeof_type(type)\n\n    index\n  end\n\n  # Fetches a class variable, if it's there.\n  def fetch?(owner : Type, name : String) : Value?\n    @data[Key.new(owner, name)]?\n  end\n\n  # Yields each index of every class variable that is trivially \"initialized\"\n  # when the program starts: those that don't have initializers.\n  def each_initialized_index(&)\n    @data.each_value do |value|\n      yield value.index unless value.compiled_def\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/closure.cr",
    "content": "require \"./repl\"\n\nmodule Crystal::Repl::Closure\n  # The variable name for closures allocated in a context\n  VAR_NAME = \".closure_var\"\n\n  # If a closure needs to be allocated, but we are in the context\n  # of a proc literal that also receives closure data in its hidden\n  # pointer, we declare that argument as ARG_NAME, and we copy\n  # that pointre into VAR_NAME.\n  ARG_NAME = \".closure_arg\"\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/closure_context.cr",
    "content": "class Crystal::Repl\n  # Information about closured variables in a given context.\n  class ClosureContext\n    # The variables closures in the closest context\n    getter vars : Hash(String, {Int32, Type})\n\n    # The self type, if captured, otherwise nil.\n    # Comes after vars, at the end of the closure (this closure never has a parent closure).\n    getter self_type : Type?\n\n    # The parent context, if any, where more closured variables might be reached\n    getter parent : ClosureContext?\n\n    # The total bytesize to hold all the immediate closure data.\n    # If this context has a parent context, it will come at the end of this\n    # data and occupy 8 bytes.\n    getter bytesize : Int32\n\n    def initialize(\n      @vars : Hash(String, {Int32, Type}),\n      @self_type : Type?,\n      @parent : ClosureContext?,\n      @bytesize : Int32,\n    )\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/compiled_block.cr",
    "content": "require \"./repl\"\n\nclass Crystal::Repl\n  # A block that's been compiled to bytecode.\n  class CompiledBlock\n    # The block that was compiled.\n    getter block : Block\n\n    # The bytecode to execute the block.\n    getter instructions : CompiledInstructions\n\n    # How many bytes occupy the block args\n    getter args_bytesize : Int32\n\n    # What's the byte offset where the local vars of this block start\n    getter locals_bytesize_start : Int32\n\n    # What's the byte offset where the local vars of this block end\n    getter locals_bytesize_end : Int32\n\n    # Local variables for the block (they might reference variables outside of the block)\n    property! local_vars : LocalVars\n\n    property closure_context : ClosureContext?\n\n    def initialize(@block : Block,\n                   @args_bytesize : Int32,\n                   @locals_bytesize_start : Int32,\n                   @locals_bytesize_end : Int32)\n      @instructions = CompiledInstructions.new\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/compiled_def.cr",
    "content": "require \"./repl\"\n\nclass Crystal::Repl\n  # A block that's been compiled to bytecode.\n  class CompiledDef\n    # The def that was compiled.\n    getter def : Def\n\n    # The bytecode to execute the method.\n    getter instructions : CompiledInstructions\n\n    # Local variables for the method.\n    getter local_vars : LocalVars\n\n    # What's `self` for this method.\n    getter owner : Type\n\n    # How many bytes occupy the method arguments.\n    getter args_bytesize : Int32\n\n    property closure_context : ClosureContext?\n\n    def initialize(\n      context : Context,\n      @def : Def,\n      @owner : Type,\n      @args_bytesize : Int32,\n      @instructions : CompiledInstructions = CompiledInstructions.new,\n      @local_vars = LocalVars.new(context),\n    )\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/compiled_instructions.cr",
    "content": "require \"./repl\"\n\n# Instructions together with their mapping back to AST nodes.\nclass Crystal::Repl::CompiledInstructions\n  # An exception handler is a region (from `start_index` to `end_index` inclusive)\n  # that, if an exception is raised, will make the flow go to `jump_index`\n  # if `exception_types` is `nil` (this is for an `ensure` clause)\n  # or if the raised exception is one of `exception_types`.\n  record ExceptionHandler, start_index : Int32, end_index : Int32, exception_types : Array(Type)?, jump_index : Int32\n\n  # The actual bytecode.\n  getter instructions : Array(UInt8) = [] of UInt8\n\n  # The nodes to refer from the instructions (by index)\n  getter nodes : Hash(Int32, ASTNode) = {} of Int32 => ASTNode\n\n  getter exception_handlers : Array(ExceptionHandler)?\n\n  def add_rescue(start_index : Int32, end_index : Int32, exception_types : Array(Type), jump_index : Int32)\n    return if start_index == end_index\n\n    exception_handlers = @exception_handlers ||= [] of ExceptionHandler\n    exception_handlers << ExceptionHandler.new(start_index, end_index, exception_types, jump_index)\n  end\n\n  def add_ensure(start_index : Int32, end_index : Int32, jump_index : Int32)\n    return if start_index == end_index\n\n    exception_handlers = @exception_handlers ||= [] of ExceptionHandler\n    exception_handlers << ExceptionHandler.new(start_index, end_index, nil, jump_index)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/compiler.cr",
    "content": "require \"./repl\"\nrequire \"./instructions\"\n\n# The compiler is in charge of turning Crystal AST into bytecode,\n# which is just a stream of bytes that tells the interpreter what to do.\nclass Crystal::Repl::Compiler < Crystal::Visitor\n  # The name we use for the variable where we store the\n  # `with ... yield` scope of calls without an `obj`.\n  WITH_SCOPE = \".with_scope\"\n\n  # A block that's being compiled: what's the block,\n  # and which def will invoke it.\n  record CompilingBlock, block : Block, target_def : Def\n\n  # A local variable: the index in the stack where it's located, and its type\n  record LocalVar, index : Int32, type : Type\n\n  # A closured variable: the array of indexes to traverse the closure context,\n  # and possibly parent context, to reach the variable with the given type.\n  record ClosuredVar, indexes : Array(Int32), type : Type\n\n  # What's `self` when compiling a node.\n  private getter scope : Type\n\n  # The method that's being compiled, if any\n  # (if `nil`, the node happens at the top-level)\n  private getter def : Def?\n\n  # The block we are in, if any.\n  # This is different than `compiling_block`. Consider this code:\n  #\n  # ```\n  # def foo(&)\n  #   # When this is called from the top-level, `compiled_block`\n  #   # will be the block given to `foo`, but `compiling_block`\n  #   # will be `nil` because we are not compiling a block.\n  #\n  #   bar do\n  #     # Now we are compiling `bar`, so `compiling_block` will\n  #     # have this block and `bar` as a value.\n  #     # And also, `compiled_block` is still the block given to `foo`.\n  #     # This is the point where you can see their difference.\n  #     yield\n  #   end\n  # end\n  #\n  # def bar(&)\n  #   yield\n  # end\n  #\n  # # Here `compiling_block` and `compiled_block` are `nil`.\n  #\n  # foo do\n  #   # When this block is compiled, `compiling_block` will have\n  #   # the block and `foo`.\n  #   a = 1\n  # end\n  # ```\n  property compiled_block : CompiledBlock?\n\n  @compiling_block : CompilingBlock?\n\n  # The instructions that are being generated.\n  getter instructions : CompiledInstructions\n\n  # In which block level we are in.\n  # Right when we enter a def we are at block level 0.\n  # When entering a block, the block level is incremented.\n  #\n  # This is useful to distinguish a same variable in multiple\n  # scopes, for example:\n  #\n  # ```\n  # a = 0      # has block_level = 0\n  # foo do |a| # <- this is a different variable, has block_level = 1\n  # end\n  # ```\n  property block_level = 0\n\n  property closure_context : ClosureContext?\n\n  # An ASTNode to override the node associated with an instruction.\n  # This is useful when values are inlined. For example if we have a constant\n  # like:\n  #\n  #     TWO = 2\n  #\n  # When the constant is referenced in code:\n  #\n  #     x = TWO\n  #\n  # we simply produce a value of 2 (the constant isn't actually stored anywhere.)\n  # But we don't want the debugger to jump to that \"2\".\n  # Instead, we make it so that the location of that \"2\" is the location\n  # of the mention of TWO.\n  #\n  # We do the same thing when inlining a method that only returns an instance variable.\n  @node_override : ASTNode?\n\n  def initialize(\n    @context : Context,\n    @local_vars : LocalVars,\n    @instructions : CompiledInstructions = CompiledInstructions.new,\n    scope : Type? = nil,\n    @def = nil,\n    @top_level = true,\n  )\n    @scope = scope || @context.program\n\n    # Do we want to push a value to the stack?\n    # This value is false for nodes whose value is not needed.\n    # For example, consider this code:\n    #\n    # ```\n    # a = 1\n    # 2\n    # a\n    # ```\n    #\n    # The value of the second node, `2`, is not needed at all\n    # and so it's not even pushed to the stack.\n    #\n    # And, actually, the value of `a = 1` is not needed either\n    # (it's not assigned to anything else after `a`).\n    #\n    # An alternative way to have done this is to push every\n    # node to the stack, and pop afterwards if not needed\n    # (this is in intermediary nodes of `Expressions`) but\n    # this is less efficient.\n    @wants_value = true\n\n    # Stack of ensures that will be inlined when a `return`\n    # is done inside an ensure.\n    @ensure_stack = [] of ASTNode\n  end\n\n  def self.new(\n    context : Context,\n    compiled_def : CompiledDef,\n    top_level : Bool,\n    scope : Type = compiled_def.owner,\n  )\n    new(\n      context: context,\n      local_vars: compiled_def.local_vars,\n      instructions: compiled_def.instructions,\n      scope: scope,\n      def: compiled_def.def,\n      top_level: top_level,\n    )\n  end\n\n  # Compile bytecode instructions for the given node.\n  def compile(node : ASTNode) : Nil\n    # If at the top-level, check if there's closure data\n    prepare_closure_context(@context.program) unless @def\n\n    node.accept self\n\n    # Use a dummy node so that pry stops at `end`\n    leave aligned_sizeof_type(node), node: Nop.new.at(node.end_location)\n  end\n\n  # Compile bytecode instructions for the given block, where `target_def`\n  # is the method that will yield to the block.\n  def compile_block(compiled_block : CompiledBlock, target_def : Def, parent_closure_context : ClosureContext?) : Nil\n    node = compiled_block.block\n\n    prepare_closure_context(node, parent_closure_context: parent_closure_context)\n\n    @compiling_block = CompilingBlock.new(node, target_def)\n\n    # Right when we enter a block we have the block arguments in the stack:\n    # we need to copy the values to the respective block arguments, which\n    # are really local variables inside the enclosing method.\n    # And we have to do them starting from the end because it's a stack.\n    node.args.reverse_each do |arg|\n      block_var = node.vars.not_nil![arg.name]\n      next if block_var.name == \"_\"\n\n      # If any block argument is closured, we need to store it in the closure\n      if block_var.closure_in?(node)\n        closured_var = lookup_closured_var(arg.name)\n        assign_to_closured_var(closured_var, node: nil)\n      else\n        index = @local_vars.name_to_index(block_var.name, @block_level)\n        # Don't use location so we don't pry break on a block arg (useless)\n        set_local index, aligned_sizeof_type(block_var), node: nil\n      end\n    end\n\n    # If it's `with ... yield` we pass the \"with\" scope\n    # as the first block argument... which is the last thing we want to pop.\n    with_scope = node.scope\n    if with_scope\n      index = @local_vars.name_to_index(WITH_SCOPE, @block_level)\n      set_local index, aligned_sizeof_type(with_scope), node: nil\n    end\n\n    node.body.accept self\n\n    if node.type.no_return?\n      # Nothing to do, the body never returns so there's nothing to upcast\n    else\n      upcast node.body, node.body.type, node.type\n    end\n\n    # Use a dummy node so that pry stops at `end`\n    leave aligned_sizeof_type(node), node: Nop.new.at(node.end_location)\n\n    # Keep a copy of the local vars before exiting the block.\n    # Otherwise we'll lose reference to the block's vars (useful for pry)\n    compiled_block.local_vars = @local_vars.dup\n    compiled_block.closure_context = @closure_context\n  end\n\n  # Compile bytecode instructions for the given method.\n  def compile_def(compiled_def : CompiledDef, parent_closure_context : ClosureContext? = nil, closure_owner = compiled_def.def) : Nil\n    node = compiled_def.def\n\n    prepare_closure_context(\n      node,\n      closure_owner: closure_owner,\n      parent_closure_context: parent_closure_context,\n    )\n\n    # If any def argument is closured, we need to store it in the closure\n    node.args.each do |arg|\n      move_arg_to_closure_if_closured(node, arg.name)\n    end\n\n    # Same for the block arg\n    if node.uses_block_arg?\n      move_arg_to_closure_if_closured(node, node.block_arg.not_nil!.name)\n    end\n\n    # Compiled Crystal supports a def's body being nil:\n    # it treats it as NoReturn. Here we do the same thing.\n    # In reality we should fix the compiler to avoid having\n    # nil in types, but that's a larger change and we can do\n    # it later. For now we just handle this specific case in\n    # the interpreter.\n    node.body.accept self\n\n    final_type = node.type\n\n    compiled_block = @compiled_block\n    if compiled_block\n      final_type = merge_block_break_type(final_type, compiled_block.block)\n    end\n\n    if final_type.nil_type?\n      # Cast whatever was returned to Nil, which means just popping it from the stack\n      if node.body.type?\n        pop aligned_sizeof_type(node.body), node: nil\n      else\n        # Nothing to do, there's no body type which probably means\n        # the last expression in the body is unreachable, and given\n        # that this already returns nil, the value was already \"casted\" to nil\n      end\n    elsif final_type.no_return?\n      # Nothing to do, the body never returns so there's nothing to upcast\n    elsif node.body.type?\n      upcast node.body, node.body.type, final_type\n    else\n      # Nothing to do, the body has no type\n    end\n\n    # Use a dummy node so that pry stops at `end`\n    leave aligned_sizeof_type(final_type), node: Nop.new.at(node.end_location)\n\n    compiled_def.closure_context = @closure_context\n  end\n\n  private def move_arg_to_closure_if_closured(node : Def, arg_name : String)\n    var = node.vars.not_nil![arg_name]\n    return unless var.type?\n    return unless var.closure_in?(node)\n\n    local_var = lookup_local_var(closured_arg_name(var.name))\n    closured_var = lookup_closured_var(var.name)\n\n    get_local local_var.index, aligned_sizeof_type(local_var.type), node: nil\n    assign_to_closured_var(closured_var, node: nil)\n  end\n\n  private def inside_method?\n    return false if @top_level\n\n    !!@def\n  end\n\n  def visit(node : Nop)\n    return false unless @wants_value\n\n    put_nil node: node\n    false\n  end\n\n  def visit(node : NilLiteral)\n    return false unless @wants_value\n\n    put_nil node: node\n    false\n  end\n\n  def visit(node : BoolLiteral)\n    return false unless @wants_value\n\n    if node.value\n      put_true node: node\n    else\n      put_false node: node\n    end\n\n    false\n  end\n\n  def visit(node : NumberLiteral)\n    return false unless @wants_value\n\n    compile_number(node, node.kind, node.value)\n\n    false\n  end\n\n  private def compile_number(node, kind, value)\n    case kind\n    in .i8?\n      put_i8 value.to_i8, node: node\n    in .u8?\n      put_u8 value.to_u8, node: node\n    in .i16?\n      put_i16 value.to_i16, node: node\n    in .u16?\n      put_u16 value.to_u16, node: node\n    in .i32?\n      put_i32 value.to_i32, node: node\n    in .u32?\n      put_u32 value.to_u32, node: node\n    in .i64?\n      put_i64 value.to_i64, node: node\n    in .u64?\n      put_u64 value.to_u64, node: node\n    in .i128?\n      put_i128 value.to_i128, node: node\n    in .u128?\n      put_u128 value.to_u128, node: node\n    in .f32?\n      put_i32 value.to_f32.unsafe_as(Int32), node: node\n    in .f64?\n      put_i64 value.to_f64.unsafe_as(Int64), node: node\n    end\n  end\n\n  def visit(node : CharLiteral)\n    return false unless @wants_value\n\n    put_i32 node.value.ord, node: node\n    false\n  end\n\n  def visit(node : StringLiteral)\n    return false unless @wants_value\n\n    put_string node.value, node: node\n\n    false\n  end\n\n  def visit(node : SymbolLiteral)\n    return false unless @wants_value\n\n    index = @context.symbol_index(node.value)\n\n    put_i32 index, node: node\n    false\n  end\n\n  def visit(node : TupleLiteral)\n    unless @wants_value\n      node.elements.each do |element|\n        discard_value element\n      end\n\n      return false\n    end\n\n    type = node.type.as(TupleInstanceType)\n\n    # The elements inside a tuple do not follow the stack alignment.\n    # Each element expression is stack-aligned, so we must adjust that:\n    # if the value in the stack has more bytes than needed, we pop\n    # the extra ones; if it has less bytes that needed we pad the value\n    # with zeros.\n    current_offset = 0\n    node.elements.each_with_index do |element, i|\n      element.accept self\n      aligned_size = aligned_sizeof_type(element)\n      next_offset =\n        if i == node.elements.size - 1\n          aligned_sizeof_type(type)\n        else\n          @context.offset_of(type, i + 1)\n        end\n\n      difference = next_offset - (current_offset + aligned_size)\n      if difference > 0\n        push_zeros(difference, node: nil)\n      elsif difference < 0\n        pop(-difference, node: nil)\n      end\n\n      current_offset = next_offset\n    end\n\n    false\n  end\n\n  def visit(node : NamedTupleLiteral)\n    unless @wants_value\n      node.entries.each do |entry|\n        discard_value entry.value\n      end\n\n      return false\n    end\n\n    type = node.type.as(NamedTupleInstanceType)\n\n    # This logic is similar to TupleLiteral.\n    current_offset = 0\n    node.entries.each_with_index do |entry, i|\n      entry.value.accept self\n      aligned_size = aligned_sizeof_type(entry.value)\n      next_offset =\n        if i == node.entries.size - 1\n          aligned_sizeof_type(type)\n        else\n          @context.offset_of(type, i + 1)\n        end\n\n      difference = next_offset - (current_offset + aligned_size)\n      if difference > 0\n        push_zeros(difference, node: nil)\n      elsif difference < 0\n        pop(-difference, node: nil)\n      end\n\n      current_offset = next_offset\n    end\n\n    false\n  end\n\n  def visit(node : ExceptionHandler)\n    # TODO: rescues, else, etc.\n    rescues = node.rescues\n    node_ensure = node.ensure\n    node_else = node.else\n\n    # Accept the body, recording where it starts and ends\n    body_start_index = instructions_index\n\n    @ensure_stack.push node_ensure if node_ensure\n\n    if node_else\n      discard_value node.body\n    else\n      node.body.accept self\n      upcast node.body, node.body.type, node.type if @wants_value\n    end\n\n    @ensure_stack.pop if node_ensure\n\n    body_end_index = instructions_index\n\n    # Now we'll write the catch tables so we want to skip this\n    jump 0, node: nil\n    jump_location = patch_location\n\n    # Assume we have only rescue for now\n    rescue_jump_locations = [] of Int32\n    rescue_indexes = [] of {Int32, Int32}\n\n    rescues.try &.each do |a_rescue|\n      rescue_start_index = instructions_index\n\n      name = a_rescue.name\n      types = a_rescue.types\n      if types\n        exception_types = types.map(&.type.instance_type)\n      else\n        exception_types = [@context.program.exception] of Type\n      end\n\n      instructions.add_rescue(\n        body_start_index,\n        body_end_index,\n        exception_types,\n        jump_index: rescue_start_index)\n\n      if name\n        # The exception is in the stack, so we copy it to the corresponding local variable\n        name_type = @context.program.type_merge_union_of(exception_types).not_nil!\n\n        assign_to_var(name, name_type, node: a_rescue)\n      else\n        # The exception is in the stack but we don't use it\n        pop sizeof(Void*), node: nil\n      end\n\n      a_rescue.body.accept self\n      upcast a_rescue.body, a_rescue.body.type, node.type if @wants_value\n\n      rescue_end_index = instructions_index\n      rescue_indexes << {rescue_start_index, rescue_end_index}\n\n      jump 0, node: nil\n      rescue_jump_locations << patch_location\n    end\n\n    if node_ensure\n      # If there's an ensure block we also generate another ensure\n      # clause to be executed when an exception is raised inside the body\n      # or any of the rescue clauses, which does the ensure, then reraises\n      ensure_index = instructions_index\n\n      instructions.add_ensure(\n        body_start_index,\n        body_end_index,\n        jump_index: ensure_index,\n      )\n\n      rescue_indexes.each do |rescue_start_index, rescue_end_index|\n        instructions.add_ensure(\n          rescue_start_index,\n          rescue_end_index,\n          jump_index: ensure_index,\n        )\n      end\n\n      # temp_var_index = temp_var_index.not_nil!\n\n      # set_local temp_var_index, sizeof(Interpreter::ThrowValue), node: nil\n\n      discard_value node_ensure\n\n      # get_local temp_var_index, sizeof(Interpreter::ThrowValue), node: nil\n\n      throw node: nil\n    end\n\n    # Now we are at the exit\n    patch_jump(jump_location)\n\n    # If there's an else, do it now\n    if node_else\n      else_start_index = instructions_index\n\n      node_else.accept self\n      upcast node_else, node_else.type, node.type if @wants_value\n\n      if ensure_index\n        instructions.add_ensure(\n          else_start_index,\n          instructions_index,\n          jump_index: ensure_index,\n        )\n      end\n    end\n\n    # Now comes the ensure part.\n    # We jump here from all the rescue blocks.\n    rescue_jump_locations.each do |location|\n      patch_jump(location)\n    end\n\n    discard_value node_ensure if node_ensure\n\n    false\n  end\n\n  def visit(node : Expressions)\n    old_wants_value = @wants_value\n\n    node.expressions.each_with_index do |expression, i|\n      @wants_value = old_wants_value && i == node.expressions.size - 1\n      expression.accept self\n    end\n\n    @wants_value = old_wants_value\n\n    false\n  end\n\n  def visit(node : Assign)\n    target = node.target\n    case target\n    when Var\n      compile_assign_to_var(node, target, node.value)\n    when InstanceVar\n      if inside_method?\n        request_value(node.value)\n\n        # Why we dup: check the Var case (it's similar)\n        if @wants_value\n          dup(aligned_sizeof_type(node.value), node: nil)\n        end\n\n        closure_self = lookup_closured_var?(\"self\")\n        if closure_self\n          if closure_self.type.passed_by_value?\n            ivar_offset, ivar_size = get_closured_self_pointer(closure_self, target.name, node: node)\n            pointer_set ivar_size, node: node\n          else\n            ivar_offset = ivar_offset(closure_self.type, target.name)\n            ivar = closure_self.type.lookup_instance_var(target.name)\n            ivar_size = inner_sizeof_type(ivar.type)\n\n            upcast node.value, node.value.type, ivar.type\n\n            # Read self pointer\n            read_from_closured_var(closure_self, node: nil)\n\n            # Now offset it to reach the instance var\n            if ivar_offset > 0\n              pointer_add_constant ivar_offset, node: nil\n            end\n\n            # Finally set it\n            pointer_set ivar_size, node: nil\n          end\n        else\n          ivar_offset = ivar_offset(scope, target.name)\n          ivar = scope.lookup_instance_var(target.name)\n          ivar_size = inner_sizeof_type(ivar.type)\n\n          upcast node.value, node.value.type, ivar.type\n\n          set_self_ivar ivar_offset, ivar_size, node: node\n        end\n      else\n        node.type = @context.program.nil_type\n        put_nil node: nil if @wants_value\n      end\n    when ClassVar\n      if inside_method?\n        dispatch_class_var(target) do |class_var|\n          index, compiled_def = class_var_index_and_compiled_def(class_var, node: target)\n\n          if compiled_def\n            initialize_class_var_if_needed(class_var, index, compiled_def)\n          end\n\n          request_value(node.value)\n\n          # Why we dup: check the Var case (it's similar)\n          if @wants_value\n            dup(aligned_sizeof_type(node.value), node: nil)\n          end\n\n          upcast node.value, node.value.type, class_var.type\n\n          set_class_var index, aligned_sizeof_type(class_var), node: node\n        end\n      else\n        # TODO: eagerly initialize the class var?\n        node.type = @context.program.nil_type\n        put_nil node: nil if @wants_value\n      end\n    when Underscore\n      node.value.accept self\n    when Path\n      const = target.target_const.not_nil!\n\n      # We inline simple constants.\n      if const.value.simple_literal?\n        put_nil node: node\n\n        # Not all non-trivial constants have a corresponding def:\n        # for example ARGV_UNSAFE.\n      elsif const.fake_def\n        index, compiled_def = get_const_index_and_compiled_def const\n\n        # This will initialize the constant\n        const_initialized index, node: nil\n        pop(sizeof(Pointer(Void)), node: nil) # pop the bool value\n\n        call compiled_def, node: nil\n\n        # Why we dup: check the Var case (it's similar)\n        dup(aligned_sizeof_type(const.value.type), node: nil) if @wants_value\n        set_const index, aligned_sizeof_type(const.value), node: nil\n      elsif @wants_value\n        # This is probably the last constant defined in a file, and it's a throw-away value\n        put_nil node: node\n      end\n    else\n      node.raise \"BUG: missing interpret for #{node.class} with target #{node.target.class}\"\n    end\n    false\n  end\n\n  def compile_assign_to_var(node : ASTNode, target : ASTNode, value : ASTNode)\n    request_value(value)\n\n    # If it's the case of `x = a = 1` then we need to preserve the value\n    # of 1 in the stack because it will be assigned to `x` too\n    # (set_local removes the value from the stack)\n    if @wants_value\n      dup(aligned_sizeof_type(value), node: nil)\n    end\n\n    if target.special_var?\n      # We need to assign through the special var pointer\n      var = lookup_local_var(\"#{target.name}*\")\n      var_type = var.type.as(PointerInstanceType).element_type\n\n      upcast value, value.type, var_type\n\n      get_local var.index, sizeof(Void*), node: node\n      pointer_set inner_sizeof_type(var_type), node: node\n    else\n      assign_to_var(target.name, value.type, node: node)\n    end\n  end\n\n  private def assign_to_var(name : String, value_type : Type, *, node : ASTNode?)\n    var = lookup_local_var_or_closured_var(name)\n\n    # Before assigning to the var we must potentially box inside a union\n    upcast node, value_type, var.type\n\n    case var\n    in LocalVar\n      set_local var.index, aligned_sizeof_type(var.type), node: node\n    in ClosuredVar\n      assign_to_closured_var(var, node: node)\n    end\n  end\n\n  def visit(node : TypeDeclaration)\n    var = node.var\n    return false unless var.is_a?(Var)\n\n    value = node.value\n    return false unless value\n\n    compile_assign_to_var(node, var, value)\n\n    false\n  end\n\n  def visit(node : Var)\n    return false unless @wants_value\n\n    is_self = node.name == \"self\"\n\n    # This is the case of \"self\" that refers to a metaclass,\n    # particularly when outside of a method.\n    if is_self && !scope.is_a?(Program) && !scope.passed_as_self?\n      put_type scope, node: node\n      return false\n    end\n\n    local_var = lookup_local_var_or_closured_var(node.name)\n    case local_var\n    in LocalVar\n      index, type = local_var.index, local_var.type\n\n      if is_self && type.passed_by_value?\n        if in_multidispatch?\n          # Inside a multidispatch \"self\" is already a pointer\n          get_local index, sizeof(Void*), node: node\n          pointer_get aligned_sizeof_type(type), node: node\n        else\n          # Load the entire self from the pointer that's self\n          get_self_ivar 0, aligned_sizeof_type(type), node: node\n        end\n      else\n        get_local index, aligned_sizeof_type(type), node: node\n      end\n\n      downcast node, type, node.type?\n    in ClosuredVar\n      read_from_closured_var(local_var, node: node)\n    end\n\n    false\n  end\n\n  def lookup_local_var_or_closured_var(name : String) : LocalVar | ClosuredVar\n    lookup_local_var?(name, at: @block_level) ||\n      lookup_closured_var?(name) ||\n      lookup_local_var?(name, from: @block_level - 1) ||\n      raise(\"BUG: can't find closured var or local var #{name}\")\n  end\n\n  def lookup_local_var(name : String) : LocalVar\n    lookup_local_var?(name) || raise(\"BUG: can't find local var #{name}\")\n  end\n\n  def lookup_local_var?(name : String, *, from : Int32 = @block_level) : LocalVar?\n    from.downto(0) do |block_level|\n      if local_var = lookup_local_var?(name, at: block_level)\n        return local_var\n      end\n    end\n  end\n\n  def lookup_local_var?(name : String, *, at block_level : Int32) : LocalVar?\n    if index = @local_vars.name_to_index?(name, block_level)\n      LocalVar.new(index, @local_vars.type(name, block_level))\n    end\n  end\n\n  def lookup_closured_var(name : String) : ClosuredVar\n    lookup_closured_var?(name) || raise(\"BUG: can't find closured var #{name}\")\n  end\n\n  def lookup_closured_var?(name : String) : ClosuredVar?\n    closure_context = @closure_context\n    return unless closure_context\n\n    indexes = [] of Int32\n\n    type = lookup_closured_var?(name, closure_context, indexes)\n\n    return nil if indexes.empty? || !type\n\n    ClosuredVar.new(indexes, type)\n  end\n\n  def lookup_closured_var?(name : String, closure_context : ClosureContext, indexes : Array(Int32))\n    if name == \"self\" && (closure_self_type = closure_context.self_type)\n      indexes << closure_context.bytesize - aligned_sizeof_type(closure_self_type)\n      return closure_self_type\n    end\n\n    closured_var = closure_context.vars[name]?\n    if closured_var\n      indexes << closured_var[0]\n      return closured_var[1]\n    end\n\n    parent_context = closure_context.parent\n    return nil unless parent_context\n\n    indexes << closure_context.bytesize - sizeof(Void*)\n    lookup_closured_var?(name, parent_context, indexes)\n  end\n\n  private def prepare_closure_context(vars_owner, closure_owner = vars_owner, parent_closure_context = nil)\n    closure_self_type = nil\n    if closure_owner.is_a?(Def) && closure_owner.self_closured?\n      closure_self_type = closure_owner.owner\n    end\n\n    closured_vars, closured_vars_bytesize = compute_closured_vars(vars_owner.vars, closure_owner)\n\n    # If there's no closure in this context, we might still have a closure\n    # if the parent formed a closure\n    if closured_vars.empty? && !closure_self_type\n      @closure_context = parent_closure_context\n      return\n    end\n\n    if parent_closure_context\n      closured_vars_bytesize += sizeof(Void*)\n    end\n\n    if closure_self_type\n      closured_vars_bytesize += aligned_sizeof_type(closure_self_type)\n    end\n\n    closure_context = ClosureContext.new(\n      vars: closured_vars,\n      parent: parent_closure_context,\n      self_type: closure_self_type,\n      bytesize: closured_vars_bytesize,\n    )\n    @closure_context = closure_context\n\n    # Allocate closure heap memory\n    put_i32 closure_context.bytesize, node: nil\n    pointer_malloc 1, node: nil\n\n    # Store the pointer in the closure context local variable\n    index = @local_vars.name_to_index(Closure::VAR_NAME, @block_level)\n    set_local index, sizeof(Void*), node: nil\n\n    # If there's a closured self type, store it now\n    if closure_self_type\n      # Load self\n      # (pointer_set expects the value to come before the pointer)\n      local_self_index = @local_vars.name_to_index(\"self\", 0)\n      if closure_self_type.passed_by_value?\n        # First load the pointer to self\n        get_local local_self_index, sizeof(Pointer(Void)), node: nil\n\n        # Then load the entire self\n        pointer_get aligned_sizeof_type(closure_self_type), node: nil\n      else\n        get_local local_self_index, aligned_sizeof_type(closure_self_type), node: nil\n      end\n\n      # Get the closure pointer\n      get_local index, sizeof(Void*), node: nil\n\n      # Offset pointer to reach self pointer\n      closure_self_index = closure_context.bytesize - aligned_sizeof_type(closure_self_type)\n      if closure_self_index > 0\n        pointer_add_constant closure_self_index, node: nil\n      end\n\n      # Store self in closure\n      pointer_set aligned_sizeof_type(closure_self_type), node: nil\n    end\n\n    if parent_closure_context\n      # Find the closest parent closure\n      block_level = @block_level\n      while true\n        # Only at block level 0 we have a proc closure data\n        if block_level == 0\n          parent_index = @local_vars.name_to_index?(Closure::ARG_NAME, block_level)\n          break if parent_index\n        end\n\n        block_level -= 1\n        break if block_level < 0\n\n        parent_index = @local_vars.name_to_index?(Closure::VAR_NAME, block_level)\n        break if parent_index\n      end\n\n      unless parent_index\n        raise \"Can't find parent closure index\"\n      end\n\n      get_local parent_index, sizeof(Void*), node: nil\n      read_closured_var_pointer ClosuredVar.new(\n        indexes: [closured_vars_bytesize - sizeof(Void*)] of Int32,\n        type: @context.program.pointer_of(@context.program.void),\n      ), node: nil\n      pointer_set(sizeof(Void*), node: nil)\n    end\n  end\n\n  private def compute_closured_vars(vars, closure_owner)\n    closured_vars = {} of String => {Int32, Type}\n    closure_var_index = 0\n\n    vars.try &.each do |name, var|\n      if var.type? && var.closure_in?(closure_owner)\n        closured_vars[name] = {closure_var_index, var.type}\n        closure_var_index += aligned_sizeof_type(var)\n      end\n    end\n\n    {closured_vars, closure_var_index}\n  end\n\n  private def assign_to_closured_var(closured_var : ClosuredVar, *, node : ASTNode?)\n    read_closured_var_pointer(closured_var, node: nil)\n\n    # Now we have the value in the stack, and the pointer.\n    # This is the correct order for pointer_set\n    pointer_set(aligned_sizeof_type(closured_var.type), node: node)\n  end\n\n  private def read_from_closured_var(closured_var : ClosuredVar, *, node : ASTNode?)\n    read_closured_var_pointer(closured_var, node: nil)\n\n    # Now read from the pointer\n    pointer_get inner_sizeof_type(closured_var.type), node: node\n  end\n\n  private def read_closured_var_pointer(closured_var : ClosuredVar, *, node : ASTNode?)\n    indexes = closured_var.indexes\n\n    # First load the closure pointer\n    closure_var_index = get_closure_var_index\n    get_local closure_var_index, sizeof(Void*), node: nil\n\n    # Now find the var through the pointer\n    indexes.each_with_index do |index, i|\n      if i == indexes.size - 1\n        # We reached the context where the var is.\n        # No need to offset if index is 0\n        if index > 0\n          pointer_add_constant index, node: nil\n        end\n      else\n        # The var is in the parent context, so load that first\n        pointer_add_constant index, node: nil\n        pointer_get sizeof(Void*), node: nil\n      end\n    end\n  end\n\n  private def pointerof_local_var_or_closured_var(var : LocalVar | ClosuredVar, *, node : ASTNode?)\n    case var\n    in LocalVar\n      pointerof_var(var.index, node: node)\n    in ClosuredVar\n      read_closured_var_pointer(var, node: node)\n    end\n  end\n\n  private def get_closure_var_index\n    # It might be that there's no closure in the current block,\n    # so we must search in parent blocks or the enclosing method\n    block_level = @block_level\n    while block_level >= 0\n      closure_var_index = @local_vars.name_to_index?(Closure::VAR_NAME, block_level)\n      return closure_var_index if closure_var_index\n\n      block_level -= 1\n    end\n\n    raise \"BUG: can't find closure var index starting from block level #{@block_level}\"\n  end\n\n  def visit(node : InstanceVar)\n    compile_instance_var(node)\n  end\n\n  private def compile_instance_var(node : InstanceVar)\n    return false unless @wants_value\n\n    closured_self = lookup_closured_var?(\"self\")\n    if closured_self\n      ivar_offset, ivar_size = get_closured_self_pointer(closured_self, node.name, node: node)\n      pointer_get ivar_size, node: node\n    else\n      ivar_offset = ivar_offset(scope, node.name)\n      ivar_size = inner_sizeof_type(scope.lookup_instance_var(node.name))\n\n      get_self_ivar ivar_offset, ivar_size, node: node\n    end\n\n    false\n  end\n\n  private def get_closured_self_pointer(closured_self : ClosuredVar, name : String, *, node : ASTNode?)\n    ivar_offset = ivar_offset(closured_self.type, name)\n    ivar_size = inner_sizeof_type(closured_self.type.lookup_instance_var(name))\n\n    if closured_self.type.passed_by_value?\n      # Read self pointer from closured self\n      closured_var = lookup_closured_var(\"self\")\n      read_closured_var_pointer(closured_var, node: node)\n    else\n      # Read self pointer\n      read_from_closured_var(closured_self, node: node)\n    end\n\n    # Now offset it to reach the instance var\n    if ivar_offset > 0\n      pointer_add_constant ivar_offset, node: node\n    end\n\n    {ivar_offset, ivar_size}\n  end\n\n  def visit(node : ClassVar)\n    return false unless @wants_value\n\n    dispatch_class_var(node) do |class_var|\n      index, compiled_def = class_var_index_and_compiled_def(class_var, node: node)\n\n      if compiled_def\n        initialize_class_var_if_needed(class_var, index, compiled_def)\n      end\n\n      get_class_var index, aligned_sizeof_type(class_var), node: node\n    end\n\n    false\n  end\n\n  private def dispatch_class_var(node : ClassVar, &)\n    var = node.var\n    owner = var.owner\n\n    case owner\n    when VirtualType\n      dispatch_class_var(owner.base_type, metaclass: false, node: node) do |var|\n        yield var\n      end\n    when VirtualMetaclassType\n      dispatch_class_var(owner.base_type.instance_type, metaclass: true, node: node) do |var|\n        yield var\n      end\n    else\n      yield var\n    end\n  end\n\n  private def dispatch_class_var(owner : Type, metaclass : Bool, node : ASTNode, &)\n    types = [] of Crystal::Type\n    owner.all_subclasses.each { |t| types << t if t.is_a?(ClassVarContainer) }\n    types.push(owner)\n    types.sort_by! { |type| -type.depth }\n\n    last_patch_location = nil\n    jump_locations = [] of Int32\n\n    types.each do |type|\n      patch_jump(last_patch_location) if last_patch_location\n\n      put_self node: node\n      is_a(node, scope, metaclass ? type.metaclass : type)\n      branch_unless 0, node: node\n      last_patch_location = patch_location\n\n      yield type.lookup_class_var(node.name)\n\n      jump 0, node: node\n      jump_locations << patch_location\n    end\n\n    patch_jump(last_patch_location.not_nil!)\n    unreachable \"BUG: didn't find class var type match\", node: node\n\n    jump_locations.each do |jump_location|\n      patch_jump(jump_location)\n    end\n  end\n\n  private def class_var_index_and_compiled_def(var : MetaTypeVar, *, node : ASTNode) : {Int32, CompiledDef?}\n    case var.owner\n    when VirtualType\n      node.raise \"BUG: shouldn't be calling this method with a virtual type\"\n    when VirtualMetaclassType\n      node.raise \"BUG: shouldn't be calling this method with a virtual metaclass type\"\n    end\n\n    index_and_compiled_def = @context.class_var_index_and_compiled_def(var.owner, var.name)\n    return index_and_compiled_def if index_and_compiled_def\n\n    initializer = var.initializer\n    if initializer\n      value = initializer.node\n\n      # It seems class variables initializers aren't cleaned up...\n      value = @context.program.cleanup(value)\n\n      def_name = \"#{var.owner}::#{var.name}\"\n\n      fake_def = Def.new(def_name)\n      fake_def.owner = var.owner.metaclass\n      fake_def.vars = initializer.meta_vars\n\n      # Check if we need to upcast the value to the class var's type\n      fake_def.body =\n        if value.type? == var.type\n          value\n        else\n          cast = Cast.new(value, TypeNode.new(var.type))\n          cast.upcast = true\n          cast.type = var.type\n          cast\n        end\n\n      fake_def.bind_to(fake_def.body)\n\n      compiled_def = CompiledDef.new(@context, fake_def, fake_def.owner, 0)\n\n      # TODO: it's wrong that class variable initializer variables go to the\n      # program, but this needs to be fixed in the main compiler first\n      declare_local_vars(fake_def, compiled_def.local_vars, @context.program)\n\n      compiler = Compiler.new(@context, compiled_def, scope: fake_def.owner, top_level: true)\n      compiler.compile_def(compiled_def, closure_owner: @context.program)\n\n      {% if Debug::DECOMPILE %}\n        puts \"=== #{def_name} ===\"\n        puts Disassembler.disassemble(@context, compiled_def)\n        puts \"=== #{def_name} ===\"\n      {% end %}\n    end\n\n    index = @context.declare_class_var(var.owner, var.name, var.type, compiled_def)\n\n    {index, compiled_def}\n  end\n\n  def visit(node : ReadInstanceVar)\n    compile_read_instance_var(node, node.obj, node.name)\n  end\n\n  private def compile_read_instance_var(node, obj, name, owner = obj.type)\n    unless @wants_value\n      discard_value(obj)\n      return false\n    end\n\n    ivar = owner.lookup_instance_var(name)\n    ivar_offset = ivar_offset(owner, name)\n    ivar_size = inner_sizeof_type(ivar)\n\n    obj.accept self\n\n    if owner.passed_by_value?\n      # We have the struct in the stack, now we need to keep a part of it\n\n      # If it's an extern struct with a Proc field, we need to convert\n      # the FFI::Closure object into a Crystal Proc\n      if owner.extern? && ivar.type.proc?\n        get_struct_ivar ivar_offset, sizeof(Void*), aligned_sizeof_type(obj), node: node\n        c_fun_to_proc node: node\n      else\n        get_struct_ivar ivar_offset, ivar_size, aligned_sizeof_type(obj), node: node\n      end\n    else\n      get_class_ivar ivar_offset, ivar_size, node: node\n    end\n\n    false\n  end\n\n  private def compile_pointerof_read_instance_var(obj, obj_type, name)\n    # Special handling for slice literals which have been expanded into\n    # `::Slice.new(pointerof($Slice:n.@buffer), ...)`,\n    # where `$Slice:n` is a `StaticArray`; we will build the literal contents\n    # in the context directly instead of using interpreter bytecode\n    if obj.is_a?(Path) && obj_type.is_a?(StaticArrayInstanceType) && name == \"@buffer\"\n      if buffer_name = obj.single_name?\n        if info = @context.program.const_slices[buffer_name]?\n          compile_pointerof_slice_literal_buffer(obj, info)\n          return false\n        end\n      end\n    end\n\n    ivar_offset = ivar_offset(obj_type, name)\n\n    # Get a pointer to the object\n    if obj_type.passed_by_value?\n      compile_pointerof_node(obj, obj_type)\n    else\n      request_value(obj)\n    end\n\n    # Now offset it\n    pointer_add_constant ivar_offset, node: nil\n\n    false\n  end\n\n  def visit(node : UninitializedVar)\n    case var = node.var\n    when Var\n      var.accept self\n    when InstanceVar\n      # Nothing to do\n    when ClassVar\n      # TODO: declare the class var (though it will be declared later on)\n    else\n      node.raise \"BUG: missing interpret UninitializedVar for #{var.class}\"\n    end\n\n    false\n  end\n\n  def visit(node : If)\n    # Compiled Crystal supports an if's type being nil:\n    # it treats it as NoReturn. Here we do the same thing.\n    # In reality we should fix the compiler to avoid having\n    # nil in types, but that's a larger change and we can do\n    # it later. For now we just handle this specific case in\n    # the interpreter.\n    node.type = @context.program.no_return unless node.type?\n\n    if node.truthy?\n      discard_value(node.cond)\n      node.then.accept self\n      return false unless @wants_value\n\n      upcast node.then, node.then.type, node.type\n      return false\n    elsif node.falsey?\n      discard_value(node.cond)\n      node.else.accept self\n      return false unless @wants_value\n\n      upcast node.else, node.else.type, node.type\n      return false\n    end\n\n    request_value(node.cond)\n\n    value_to_bool(node.cond, node.cond.type)\n\n    branch_unless 0, node: nil\n    cond_jump_location = patch_location\n\n    node.then.accept self\n\n    # TODO: for some reason the semantic pass might leave this as nil\n    if @wants_value && (then_type = node.then.type?)\n      upcast node.then, then_type, node.type\n    end\n\n    jump 0, node: nil\n    then_jump_location = patch_location\n\n    patch_jump(cond_jump_location)\n\n    node.else.accept self\n\n    # TODO: for some reason the semantic pass might leave this as nil\n    if @wants_value && (else_type = node.else.type?)\n      upcast node.else, else_type, node.type\n    end\n\n    patch_jump(then_jump_location)\n\n    false\n  end\n\n  def visit(node : While)\n    # Jump directly to the condition\n    jump 0, node: nil\n    cond_jump_location = patch_location\n\n    body_index = instructions_index\n\n    old_while = @while\n    old_while_breaks = @while_breaks\n    old_while_nexts = @while_nexts\n\n    @while = node\n    while_breaks = @while_breaks = [] of Int32\n    while_nexts = @while_nexts = [] of Int32\n\n    # Now write the body\n    discard_value(node.body)\n\n    # Here starts the condition.\n    # Any `next` that happened leads us here.\n    while_nexts.each do |while_next|\n      patch_jump(while_next)\n    end\n\n    patch_jump(cond_jump_location)\n    request_value(node.cond)\n    value_to_bool(node.cond, node.cond.type)\n\n    # If the condition holds, jump back to the body\n    branch_if body_index, node: nil\n\n    # Here we are at the point where the condition didn't hold anymore.\n    # We must convert `nil` to whatever while's type is.\n    upcast node.body, @context.program.nil_type, node.type\n\n    # Otherwise we are at the end of the while.\n    # Any `break` that happened leads us here\n    while_breaks.each do |while_break|\n      patch_jump(while_break)\n    end\n\n    unless @wants_value\n      pop aligned_sizeof_type(node), node: nil\n    end\n\n    @while = old_while\n    @while_breaks = old_while_breaks\n    @while_nexts = old_while_nexts\n\n    false\n  end\n\n  def visit(node : Return)\n    compile_return(node, node.exp)\n  end\n\n  def compile_return(node, exp)\n    exp_type =\n      if exp\n        request_value(exp)\n        exp.type?\n      else\n        put_nil node: node\n        @context.program.nil_type\n      end\n\n    def_type = @def.not_nil!.type\n\n    compiled_block = @compiled_block\n    if compiled_block\n      def_type = merge_block_break_type(def_type, compiled_block.block)\n    end\n\n    # Check if it's an explicit Nil return\n    if def_type.nil_type?\n      # In that case we don't need the return value, so we just pop it\n      pop aligned_sizeof_type(exp_type), node: node\n    else\n      upcast node, exp_type, def_type\n    end\n\n    if @compiling_block\n      leave_def aligned_sizeof_type(def_type), node: node\n    else\n      # If this return happens inside a begin/ensure block,\n      # inline any ensure right now.\n      @ensure_stack.reverse_each do |an_ensure|\n        discard_value(an_ensure)\n      end\n\n      leave aligned_sizeof_type(def_type), node: node\n    end\n\n    false\n  end\n\n  def visit(node : TypeOf)\n    return false unless @wants_value\n\n    # The type of a typeof node can be a virtual metaclass, but typeof\n    # should return a concrete type, so we devirtualize it.\n    put_type node.type.devirtualize, node: node\n    false\n  end\n\n  def visit(node : SizeOf)\n    return false unless @wants_value\n\n    put_i32 inner_sizeof_type(node.exp), node: node\n\n    false\n  end\n\n  def visit(node : InstanceSizeOf)\n    return false unless @wants_value\n\n    put_i32 inner_instance_sizeof_type(node.exp), node: node\n\n    false\n  end\n\n  def visit(node : AlignOf)\n    return false unless @wants_value\n\n    put_i32 inner_alignof_type(node.exp), node: node\n\n    false\n  end\n\n  def visit(node : InstanceAlignOf)\n    return false unless @wants_value\n\n    put_i32 inner_instance_alignof_type(node.exp), node: node\n\n    false\n  end\n\n  def visit(node : TypeNode)\n    return false unless @wants_value\n\n    put_type node.type, node: node\n    false\n  end\n\n  def visit(node : Path)\n    return false unless @wants_value\n\n    if const = node.target_const\n      if const.value.simple_literal?\n        with_node_override(node) do\n          const.value.accept self\n        end\n      elsif const == @context.program.argc\n        argc_unsafe(node: node)\n      elsif const == @context.program.argv\n        argv_unsafe(node: node)\n      else\n        index = initialize_const_if_needed(const)\n        get_const index, aligned_sizeof_type(const.value), node: node\n      end\n    elsif replacement = node.syntax_replacement\n      replacement.accept self\n    else\n      put_type node.type, node: node\n    end\n    false\n  end\n\n  private def get_const_index_and_compiled_def(const : Const) : {Int32, CompiledDef}\n    index_and_compiled_def = @context.const_index_and_compiled_def?(const)\n    return index_and_compiled_def if index_and_compiled_def\n\n    value = const.value\n    value = @context.program.cleanup(value)\n\n    fake_def = const.fake_def.not_nil!\n    fake_def.owner = const.namespace.metaclass\n    fake_def.body = value\n    fake_def.bind_to(value)\n\n    compiled_def = CompiledDef.new(@context, fake_def, fake_def.owner, 0)\n\n    declare_local_vars(fake_def, compiled_def.local_vars)\n\n    compiler = Compiler.new(@context, compiled_def, top_level: true)\n    compiler.compile_def(compiled_def)\n\n    {% if Debug::DECOMPILE %}\n      puts \"=== #{const} ===\"\n      puts Disassembler.disassemble(@context, compiled_def)\n      puts \"=== #{const} ===\"\n    {% end %}\n\n    {@context.declare_const(const, compiled_def), compiled_def}\n  end\n\n  def visit(node : Generic)\n    return false unless @wants_value\n\n    put_type node.type, node: node\n    false\n  end\n\n  def visit(node : PointerOf)\n    return false unless @wants_value\n\n    exp = node.exp\n    case exp\n    when Var\n      compile_pointerof_var(node, exp.name)\n    when InstanceVar\n      compile_pointerof_ivar(node, exp.name)\n    when ClassVar\n      compile_pointerof_class_var(node, exp)\n    when ReadInstanceVar\n      compile_pointerof_read_instance_var(exp.obj, exp.obj.type, exp.name)\n    when Call\n      # lib external var\n      external = exp.dependencies.first.as(External)\n      fn = @context.c_function(external.real_name)\n\n      # Put the symbol address, which is a pointer\n      put_u64 fn.address, node: node\n    else\n      node.raise \"BUG: missing interpret for PointerOf with exp #{exp.class}\"\n    end\n    false\n  end\n\n  private def compile_pointerof_var(node : ASTNode, name : String)\n    var = lookup_local_var_or_closured_var(name)\n    case var\n    in LocalVar\n      pointerof_var(var.index, node: node)\n    in ClosuredVar\n      read_closured_var_pointer(var, node: node)\n    end\n  end\n\n  private def compile_pointerof_ivar(node : ASTNode, name : String)\n    closure_self = lookup_closured_var?(\"self\")\n    if closure_self\n      get_closured_self_pointer(closure_self, name, node: node)\n      return\n    end\n\n    index = scope.index_of_instance_var(name).not_nil!\n    offset = if scope.struct?\n               @context.offset_of(scope, index)\n             else\n               @context.instance_offset_of(scope, index)\n             end\n    pointerof_ivar(offset, node: node)\n  end\n\n  private def compile_pointerof_class_var(node : ASTNode, exp : ClassVar)\n    dispatch_class_var(exp) do |class_var|\n      index, compiled_def = class_var_index_and_compiled_def(class_var, node: node)\n      initialize_class_var_if_needed(class_var, index, compiled_def) if compiled_def\n      pointerof_class_var(index, node: node)\n    end\n  end\n\n  def visit(node : Not)\n    node.type = @context.program.no_return unless node.type?\n\n    exp = node.exp\n    exp.accept self\n    return false unless @wants_value\n\n    value_to_bool(exp, exp.type)\n    logical_not node: node\n\n    false\n  end\n\n  def visit(node : Cast)\n    request_value node.obj\n\n    # TODO: check @wants_value in these branches\n\n    obj_type = node.obj.type\n    to_type = node.to.type.virtual_type\n\n    # TODO: check the proper conditions in codegen\n    if obj_type == to_type\n      # TODO: not tested\n      nop\n    elsif obj_type.pointer? && to_type.pointer?\n      # Cast between pointers is nop\n      nop\n    elsif obj_type.nil_type? && to_type.pointer?\n      # Cast from nil to Void* produces a null pointer\n      if @wants_value\n        pop aligned_sizeof_type(obj_type), node: nil\n        put_i64 0, node: nil\n      end\n    elsif obj_type.pointer? && to_type.reference_like?\n      # Cast from pointer to reference is nop\n      nop\n    elsif obj_type.reference_like? && to_type.is_a?(PointerInstanceType)\n      # Cast from reference to pointer is nop\n      nop\n    elsif node.upcast?\n      upcast node, obj_type, to_type\n    else\n      # Check if obj is a `to_type`\n      dup aligned_sizeof_type(node.obj), node: nil\n      filtered_type = is_a(node, obj_type, to_type)\n\n      # If so, branch\n      branch_if 0, node: nil\n      cond_jump_location = patch_location\n\n      # Otherwise we need to raise\n      put_string to_type.devirtualize.to_s, node: nil\n      put_string node.location.to_s, node: nil\n\n      call = Call.new(\n        nil,\n        \"__crystal_raise_cast_failed\",\n        [\n          TypeNode.new(obj_type),\n          TypeNode.new(@context.program.string),\n          TypeNode.new(@context.program.string),\n        ] of ASTNode,\n        global: true,\n      )\n      @context.program.semantic(call)\n\n      target_def = call.target_def\n\n      compiled_def = @context.defs[target_def]? ||\n                     begin\n                       create_compiled_def(call, target_def)\n                     rescue ex : Crystal::TypeException\n                       node.raise ex.message, inner: ex\n                     end\n      call compiled_def, node: node\n\n      patch_jump(cond_jump_location)\n\n      if @wants_value\n        downcast node.obj, obj_type, filtered_type\n      else\n        pop aligned_sizeof_type(obj_type), node: nil\n      end\n    end\n\n    false\n  end\n\n  def visit(node : NilableCast)\n    obj_type = node.obj.type\n    to_type = node.to.type.virtual_type\n\n    # TODO: check the proper conditions in codegen\n    if obj_type == to_type\n      node.obj.accept self\n\n      return false\n    end\n\n    filtered_type = obj_type.filter_by(to_type)\n    unless filtered_type\n      # If .as?(...) has no resulting type we must cast\n      # whatever type we have to nil.\n      discard_value node.obj\n      upcast node.obj, @context.program.nil_type, node.type\n      return false\n    end\n\n    node.obj.accept self\n\n    if node.upcast?\n      upcast node.obj, obj_type, node.non_nilable_type\n      upcast node.obj, node.non_nilable_type, node.type\n      return false\n    end\n\n    # Check if obj is a `to_type`\n    dup aligned_sizeof_type(node.obj), node: nil\n    filter_type(node, obj_type, filtered_type)\n\n    # If so, branch\n    branch_if 0, node: nil\n    cond_jump_location = patch_location\n\n    # Otherwise it's nil\n    put_nil node: nil\n    pop aligned_sizeof_type(node.obj), node: nil\n    upcast node.obj, @context.program.nil_type, node.type\n    jump 0, node: nil\n    otherwise_jump_location = patch_location\n\n    patch_jump(cond_jump_location)\n    downcast node.obj, obj_type, node.non_nilable_type\n    upcast node.obj, node.non_nilable_type, node.type\n\n    patch_jump(otherwise_jump_location)\n\n    false\n  end\n\n  def visit(node : IsA)\n    node.obj.accept self\n    return false unless @wants_value\n\n    obj_type = node.obj.type\n    const_type = node.const.type\n\n    is_a(node, obj_type, const_type)\n\n    false\n  end\n\n  def visit(node : RespondsTo)\n    node.obj.accept self\n    return false unless @wants_value\n\n    obj_type = node.obj.type\n\n    responds_to(node, obj_type, node.name)\n\n    false\n  end\n\n  private def is_a(node : ASTNode, type : Type, target_type : Type)\n    type = type.remove_indirection\n    filtered_type = type.filter_by(target_type).not_nil!\n\n    filter_type(node, type, filtered_type)\n\n    filtered_type\n  end\n\n  private def responds_to(node : ASTNode, type : Type, name : String)\n    type = type.remove_indirection\n    filtered_type = type.filter_by_responds_to(name).not_nil!\n\n    filter_type(node, type, filtered_type)\n  end\n\n  private def filter_type(node : ASTNode, type : Type, filtered_type : Type)\n    if type == filtered_type\n      # TODO: not tested\n      pop aligned_sizeof_type(type), node: nil\n      put_true node: nil\n      return\n    end\n\n    case type\n    when VirtualType\n      reference_is_a(type_id(filtered_type), node: node)\n    when MixedUnionType\n      union_is_a(aligned_sizeof_type(type), type_id(filtered_type), node: node)\n    when NilableType\n      if filtered_type.nil_type?\n        pointer_is_null(node: node)\n      else\n        pointer_is_not_null(node: node)\n      end\n    when NilableReferenceUnionType\n      if filtered_type.nil_type?\n        # TODO: not tested\n        pointer_is_null(node: node)\n      else\n        # TODO: maybe missing checking against another reference union type?\n        reference_is_a(type_id(filtered_type), node: node)\n      end\n    when NilableProcType\n      # Remove the closure data\n      pop sizeof(Void*), node: nil\n\n      if filtered_type.nil_type?\n        pointer_is_null(node: node)\n      else\n        pointer_is_not_null(node: node)\n      end\n    when ReferenceUnionType\n      case filtered_type\n      when NonGenericClassType\n        reference_is_a(type_id(filtered_type), node: node)\n      when GenericClassInstanceType\n        # TODO: not tested\n        reference_is_a(type_id(filtered_type), node: node)\n      when VirtualType\n        # TODO: not tested\n        reference_is_a(type_id(filtered_type), node: node)\n      when ReferenceUnionType\n        # TODO: not tested\n        reference_is_a(type_id(filtered_type), node: node)\n      else\n        node.raise \"BUG: missing filter type from #{type} to #{filtered_type} (#{type.class} to #{filtered_type.class})\"\n      end\n    when VirtualMetaclassType\n      case filtered_type\n      when MetaclassType, VirtualMetaclassType, GenericClassInstanceMetaclassType, GenericModuleInstanceMetaclassType\n        metaclass_is_a(type_id(filtered_type), node: node)\n      else\n        node.raise \"BUG: missing filter type from #{type} to #{filtered_type} (#{type.class} to #{filtered_type.class})\"\n      end\n    else\n      node.raise \"BUG: missing filter type from #{type} to #{filtered_type} (#{type.class} to #{filtered_type.class})\"\n    end\n  end\n\n  def visit(node : Call)\n    obj = node.obj\n    with_scope = node.with_scope\n\n    if !obj && with_scope && node.uses_with_scope?\n      obj = Var.new(WITH_SCOPE, with_scope)\n    end\n\n    target_defs = node.target_defs\n    unless target_defs\n      node.raise \"BUG: no target defs\"\n    end\n\n    if target_defs.size == 1\n      target_def = target_defs.first\n    else\n      target_def = Multidispatch.create_def(@context, node, target_defs)\n    end\n\n    body = target_def.body\n    if body.is_a?(Primitive)\n      visit_primitive(node, body, target_def)\n      return false\n    end\n\n    if body.is_a?(InstanceVar)\n      # Inline the call, so that it also works fine when wanting to take a pointer through things\n      # (this is how compiled Crystal works too\n      with_node_override(node) do\n        if obj\n          compile_read_instance_var(node, obj, body.name, owner: target_def.owner)\n        else\n          compile_instance_var(body)\n        end\n      end\n\n      # We still have to accept the call arguments, but discard their values\n      node.args.each { |arg| discard_value(arg) }\n\n      return false\n    end\n\n    if body.is_a?(Var) && body.name == \"self\"\n      # We also inline calls that simply return \"self\"\n\n      # We still have to accept the call arguments, but discard their values\n      node.args.each { |arg| discard_value(arg) }\n\n      if @wants_value\n        if obj\n          request_value(obj)\n        else\n          if scope.struct? && scope.passed_by_value?\n            # Load the entire self from the pointer that's self\n            get_self_ivar 0, aligned_sizeof_type(scope), node: node\n          else\n            put_self(node: node)\n          end\n        end\n      end\n\n      return false\n    end\n\n    if obj.try(&.type).is_a?(LibType)\n      compile_lib_call(node)\n      return false\n    end\n\n    # First compile the call args, then compile the def.\n    # The reason is that compiling the call args might introduce\n    # new temporary local variables, and if want to have those\n    # in place before compiling any block (otherwise the block\n    # variables' space would conflict with the temporary space)\n    compile_call_args(node, target_def)\n\n    compiled_def = @context.defs[target_def]? ||\n                   begin\n                     create_compiled_def(node, target_def)\n                   rescue ex : Crystal::TypeException\n                     node.raise ex.message, inner: ex\n                   end\n\n    if (block = node.block) && !block.fun_literal\n      call_with_block compiled_def, node: node\n    else\n      call compiled_def, node: node\n    end\n\n    unless @wants_value\n      pop aligned_sizeof_type(node), node: nil\n    end\n\n    false\n  end\n\n  def compile_extern_proc_wrapper(node, proc_type, symbol)\n    proc_type.arg_types.each_with_index do |arg_type, i|\n      index = @local_vars.name_to_index(\"arg#{i}\", 0)\n\n      case arg_type\n      when NilType\n        # Nil is used to mean Pointer.null\n        put_i64 0, node: nil\n      when StaticArrayInstanceType\n        # Static arrays are passed as pointers to C\n        pointerof_var index, node: nil\n      else\n        get_local index, aligned_sizeof_type(arg_type), node: nil\n        if arg_type.is_a?(ProcInstanceType)\n          proc_to_c_fun arg_type.ffi_call_interface, node: nil\n        end\n      end\n    end\n\n    lib_function = @context.lib_functions.put_if_absent(symbol) do\n      args_bytesizes = [] of Int32\n      args_ffi_types = [] of FFI::Type\n      return_bytesize = inner_sizeof_type(proc_type.return_type)\n\n      proc_type.arg_types.each do |arg_type|\n        case arg_type\n        when NilType\n          args_bytesizes << sizeof(Pointer(Void))\n          args_ffi_types << FFI::Type.pointer\n        when ProcInstanceType\n          args_bytesizes << sizeof(Void*)\n          args_ffi_types << FFI::Type.pointer\n        when StaticArrayInstanceType\n          # Static arrays are passed as pointers to C\n          args_bytesizes << sizeof(Void*)\n          args_ffi_types << FFI::Type.pointer\n        else\n          args_bytesizes << aligned_sizeof_type(arg_type)\n          args_ffi_types << arg_type.ffi_arg_type\n        end\n      end\n\n      LibFunction.new(\n        symbol: symbol,\n        call_interface: FFI::CallInterface.new(\n          proc_type.return_type.ffi_type,\n          args_ffi_types,\n        ),\n        args_bytesizes: args_bytesizes,\n        return_bytesize: return_bytesize,\n      )\n    end\n\n    lib_call(lib_function, node: node)\n\n    # Use a dummy node so that pry stops at `end`\n    leave aligned_sizeof_type(proc_type.return_type), node: Nop.new.at(node.end_location)\n  end\n\n  private def compile_lib_call(node : Call)\n    target_def = node.target_def\n    external = target_def.as(External)\n    symbol = @context.c_function(external.real_name)\n\n    args_bytesizes = [] of Int32\n    args_ffi_types = [] of FFI::Type\n    return_bytesize = inner_sizeof_type(external.type)\n\n    node.args.each_with_index do |arg, i|\n      arg_type = arg.type\n\n      case arg_type\n      when NilType\n        # Nil is used to mean Pointer.null\n        discard_value(arg)\n        put_i64 0, node: arg\n      when StaticArrayInstanceType\n        # Static arrays are passed as pointers to C\n        compile_pointerof_node(arg, arg_type)\n      else\n        request_value(arg)\n      end\n      # TODO: upcast?\n\n      case arg_type\n      when NilType\n        args_bytesizes << sizeof(Pointer(Void))\n        args_ffi_types << FFI::Type.pointer\n      when ProcInstanceType\n        external_arg = external.args[i]\n        args_bytesizes << sizeof(Void*)\n        args_ffi_types << FFI::Type.pointer\n\n        proc_to_c_fun external_arg.type.as(ProcInstanceType).ffi_call_interface, node: nil\n      when StaticArrayInstanceType\n        # Static arrays are passed as pointers to C\n        args_bytesizes << sizeof(Void*)\n        args_ffi_types << FFI::Type.pointer\n      else\n        case arg\n        when Out\n          # TODO: this out handling is bad. Why is out's type not a pointer already?\n          args_bytesizes << sizeof(Pointer(Void))\n          args_ffi_types << FFI::Type.pointer\n        else\n          if external.varargs?\n            # Apply default promotions to certain types used as variadic arguments in C function calls.\n\n            # Resolve EnumType to its base type because that's the type that gets promoted\n            if arg_type.is_a?(EnumType)\n              arg_type = arg_type.base_type\n            end\n\n            if arg_type.is_a?(FloatType) && arg_type.bytes < 8\n              # Arguments of type float are promoted to double\n              promoted_type = @context.program.float64\n              primitive_convert node, arg_type, promoted_type, true\n\n              arg_type = promoted_type\n            elsif arg_type.is_a?(IntegerType) && arg_type.bytes < 4\n              # Integer argument types smaller than 4 bytes are promoted to 4 bytes\n              promoted_type = arg_type.signed? ? @context.program.int32 : @context.program.uint32\n              primitive_convert node, arg_type, promoted_type, true\n\n              arg_type = promoted_type\n            end\n          end\n\n          args_bytesizes << aligned_sizeof_type(arg_type)\n          args_ffi_types << arg_type.ffi_arg_type\n        end\n      end\n    end\n\n    if external.varargs?\n      lib_function = LibFunction.new(\n        symbol: symbol,\n        call_interface: FFI::CallInterface.variadic(\n          external.type.ffi_type,\n          args_ffi_types,\n          fixed_args: external.args.size\n        ),\n        args_bytesizes: args_bytesizes,\n        return_bytesize: return_bytesize,\n      )\n      @context.add_gc_reference(lib_function)\n    else\n      lib_function = @context.lib_functions.put_if_absent(symbol) do\n        LibFunction.new(\n          symbol: symbol,\n          call_interface: FFI::CallInterface.new(\n            external.type.ffi_type,\n            args_ffi_types\n          ),\n          args_bytesizes: args_bytesizes,\n          return_bytesize: return_bytesize,\n        )\n      end\n    end\n\n    lib_call(lib_function, node: node)\n\n    unless @wants_value\n      pop aligned_sizeof_type(node), node: nil\n    end\n\n    false\n  end\n\n  private def create_compiled_def(node : Call, target_def : Def)\n    block = node.block\n    block = nil if block && !block.visited? && !block.fun_literal\n\n    # Compile the block too if there's one\n    if block && !block.fun_literal\n      compiled_block = create_compiled_block(block, target_def)\n    end\n\n    args_bytesize = 0\n\n    obj = node.obj\n    args = node.args\n    obj_type = obj.try(&.type) || target_def.owner\n\n    # TODO: should this use `Type#passed_as_self?` instead?\n    if obj_type == @context.program || obj_type.is_a?(FileModule)\n      # Nothing\n    elsif obj_type.passed_by_value?\n      args_bytesize += sizeof(Pointer(UInt8))\n    else\n      args_bytesize += aligned_sizeof_type(obj_type)\n    end\n\n    multidispatch_self = target_def.args.first?.try &.name == \"self\"\n\n    i = 0\n\n    # This is the case of a multidispatch with an explicit \"self\" being passed\n    i += 1 if multidispatch_self\n\n    args.each do\n      target_def_arg = target_def.args[i]\n      target_def_var_type = target_def.vars.not_nil![target_def_arg.name].type\n      args_bytesize += aligned_sizeof_type(target_def_var_type)\n\n      i += 1\n    end\n\n    node_args_size = node.args.size\n\n    # Don't count \"self\" arg in multidispatch\n    node_args_size += 1 if multidispatch_self\n\n    # Also take magic constants into account.\n    # Every magic constant is either an int or a string, and that's\n    # always 8 bytes when aligned.\n    args_bytesize += 8 * (target_def.args.size - node_args_size)\n\n    # Also consider special vars\n    special_vars = target_def.special_vars\n    if special_vars\n      # Each special var argument is a hidden pointer\n      args_bytesize += special_vars.size * sizeof(Void*)\n    end\n\n    # If the block is captured there's an extra argument\n    if block && block.fun_literal\n      args_bytesize += sizeof(Proc(Void))\n    end\n\n    # See line 19 in codegen call\n    owner = node.super? ? node.scope : target_def.owner\n\n    compiled_def = CompiledDef.new(@context, target_def, owner, args_bytesize)\n\n    # We don't cache defs that yield because we inline the block's contents\n    if block && !block.fun_literal\n      @context.add_gc_reference(compiled_def)\n    else\n      @context.defs[target_def] = compiled_def\n    end\n\n    declare_local_vars(target_def, compiled_def.local_vars)\n\n    compiler = Compiler.new(@context, compiled_def, top_level: false)\n    compiler.compiled_block = compiled_block\n\n    begin\n      compiler.compile_def(compiled_def)\n    rescue ex : Crystal::CodeError\n      node.raise \"compiling #{node}\", inner: ex\n    end\n\n    {% if Debug::DECOMPILE %}\n      puts \"=== #{target_def.owner}##{target_def.name} ===\"\n      puts compiled_def.local_vars\n      puts Disassembler.disassemble(@context, compiled_def)\n      puts \"=== #{target_def.owner}##{target_def.name} ===\"\n    {% end %}\n\n    compiled_def\n  end\n\n  private def create_compiled_block(block : Block, target_def : Def)\n    rewrite_block_with_splat(block)\n\n    bytesize_before_block_local_vars = @local_vars.current_bytesize\n\n    @local_vars.push_block\n\n    begin\n      needs_closure_context = false\n\n      # If it's `with ... yield` we pass the \"with\" scope\n      # as the first block argument.\n      with_scope = block.scope\n      if with_scope\n        @local_vars.declare(WITH_SCOPE, with_scope)\n      end\n\n      block.vars.try &.each do |name, var|\n        next if var.name == \"_\"\n\n        # Special vars don't have scopes like regular block vars do\n        next if var.special_var?\n\n        var_type = var.type?\n        var_type ||= @context.program.nil_type\n\n        if var.closure_in?(block)\n          needs_closure_context = true\n          next\n        end\n\n        next if var.context != block\n\n        @local_vars.declare(name, var_type)\n      end\n\n      if needs_closure_context\n        @local_vars.declare(Closure::VAR_NAME, @context.program.pointer_of(@context.program.void))\n      end\n\n      bytesize_after_block_local_vars = @local_vars.current_bytesize\n\n      block_args_bytesize = block.args.sum do |arg|\n        arg.name == \"_\" ? 0 : aligned_sizeof_type(arg)\n      end\n\n      # If it's `with ... yield` we pass the \"with\" scope\n      # as the first block argument, so we must count it too\n      # for the total blocks_args_bytesize.\n      if with_scope\n        block_args_bytesize += aligned_sizeof_type(with_scope)\n      end\n\n      compiled_block = CompiledBlock.new(block,\n        args_bytesize: block_args_bytesize,\n        locals_bytesize_start: bytesize_before_block_local_vars,\n        locals_bytesize_end: bytesize_after_block_local_vars,\n      )\n\n      # Store it so the GC doesn't collect it (it's in the instructions but it might not be aligned)\n      @context.add_gc_reference(compiled_block)\n\n      compiler = Compiler.new(@context, @local_vars,\n        instructions: compiled_block.instructions,\n        scope: @scope, def: @def, top_level: false)\n      compiler.compiled_block = @compiled_block\n      compiler.block_level = block_level + 1\n\n      compiler.compile_block(compiled_block, target_def, @closure_context)\n\n      # Keep a copy of the local vars before exiting the block.\n      # Otherwise we'll lose reference to the block's vars (useful for pry)\n      compiled_block.local_vars = @local_vars.dup\n\n      {% if Debug::DECOMPILE %}\n        puts \"=== #{target_def.owner}##{target_def.name}#block ===\"\n        puts compiled_block.local_vars\n        puts Disassembler.disassemble(@context, compiled_block.instructions, @local_vars)\n        puts \"=== #{target_def.owner}##{target_def.name}#block ===\"\n      {% end %}\n    ensure\n      @local_vars.pop_block\n    end\n\n    compiled_block\n  end\n\n  private def rewrite_block_with_splat(node : Block)\n    splat_index = node.splat_index\n    return unless splat_index\n\n    # If the block has a splat index, we rewrite it to something simpler.\n    #\n    # For example, assuming `y` is a tuple of 3 elements, we rewrite:\n    #\n    # ```\n    # foo do |x, *y, z|\n    #   p! x, y, z\n    # end\n    # ```\n    #\n    # to:\n    #\n    # ```\n    # foo do |x, temp1, temp2, temp3, z|\n    #   y = {temp1, temp2, temp3}\n    #   p! x, y, z\n    # end\n    # ```\n    #\n    # TODO: consider doing this in CleanupTransformer to also simplify\n    # compiled Crystal and any other future backend.\n    splat_arg = node.args[splat_index]\n    tuple_type = splat_arg.type.as(TupleInstanceType)\n\n    # Remove the fact that the block has a splat\n    node.splat_index = nil\n\n    if splat_arg.name != \"_\"\n      temp_var_names = Array.new(tuple_type.size) do\n        @context.program.new_temp_var_name\n      end\n\n      # Go from |x, *y, z| to |x, temp1, temp2, temp3, z|\n      node.args[splat_index..splat_index] = temp_var_names.map_with_index do |temp_var_name, i|\n        Var.new(temp_var_name, type: tuple_type.tuple_types[i])\n      end\n\n      # Create y = {temp1, temp2, temp3}\n      assign_var = Var.new(splat_arg.name, type: tuple_type)\n      tuple_vars = temp_var_names.map_with_index do |temp_var_name, i|\n        Var.new(temp_var_name, type: tuple_type.tuple_types[i]).as(ASTNode)\n      end\n      tuple_literal = TupleLiteral.new(tuple_vars)\n      tuple_literal.type = tuple_type\n\n      assign = Assign.new(assign_var, tuple_literal)\n      assign.type = tuple_type\n\n      # Replace the block body\n      block_body = node.body\n      unless block_body\n        block_body = NilLiteral.new\n        block_body.type = @context.program.nil_type\n      end\n\n      exps = Expressions.new([assign, block_body] of ASTNode)\n      exps.type = block_body.type\n      node.body = exps\n\n      # We also need to declare the vars in the block\n      temp_var_names.each_with_index do |temp_var_name, i|\n        meta_var = MetaVar.new(temp_var_name, tuple_type.tuple_types[i])\n        meta_var.context = node\n        node.vars.not_nil![temp_var_name] = meta_var\n      end\n    else\n      # Go from |x, *_, z| to |x, _, _, _, z|\n      # the block body remains unchanged\n      node.args[splat_index..splat_index] = Array.new(tuple_type.size) do\n        Var.new(\"_\")\n      end\n    end\n  end\n\n  private def compile_call_args(node : Call, target_def : Def) : Nil\n    obj = node.obj\n    with_scope = node.with_scope\n\n    if !obj && with_scope && node.uses_with_scope?\n      obj = Var.new(WITH_SCOPE, with_scope)\n    end\n\n    if obj\n      if obj.type.passed_by_value?\n        compile_pointerof_node(obj, target_def.owner)\n      else\n        request_value(obj)\n      end\n    else\n      # Pass implicit self if needed\n      case target_def.owner\n      when Program, FileModule\n        # These types aren't passed as self\n      else\n        put_self(node: node)\n      end\n    end\n\n    target_def_args = target_def.args\n    multidispatch_self = target_def_args.first?.try &.name == \"self\"\n\n    i = 0\n\n    # This is the case of a multidispatch with an explicit \"self\" being passed\n    i += 1 if multidispatch_self\n\n    node.args.each do |arg|\n      arg_type = arg.type\n      target_def_arg = target_def_args[i]\n      target_def_var_type = target_def.vars.not_nil![target_def_arg.name].type\n\n      compile_call_arg(arg, arg_type, target_def_arg.type, target_def_var_type)\n\n      i += 1\n    end\n\n    # Then magic constants (__LINE__, __FILE__, __DIR__)\n    node_args_size = node.args.size\n\n    # Then special vars\n    special_vars = target_def.special_vars\n    if special_vars\n      special_vars.each do |special_var|\n        var = lookup_local_var(special_var)\n        pointerof_var(var.index, node: nil)\n      end\n    end\n\n    # Don't count \"self\" arg in multidispatch\n    node_args_size += 1 if multidispatch_self\n\n    node_args_size.upto(target_def.args.size - 1) do |index|\n      arg = target_def.args[index]\n      default_value = arg.default_value.as(MagicConstant)\n      location = node.location\n      end_location = node.end_location\n      case default_value.name\n      when .magic_line?\n        put_i32 MagicConstant.expand_line(location), node: node\n      when .magic_end_line?\n        # TODO: not tested\n        put_i32 MagicConstant.expand_line(end_location), node: node\n      when .magic_file?\n        # TODO: not tested\n        put_string MagicConstant.expand_file(location), node: node\n      when .magic_dir?\n        # TODO: not tested\n        put_string MagicConstant.expand_dir(location), node: node\n      else\n        default_value.raise \"BUG: unknown magic constant: #{default_value.name}\"\n      end\n    end\n\n    if fun_literal = node.block.try(&.fun_literal)\n      request_value fun_literal\n    end\n  end\n\n  private def compile_call_arg(arg, arg_type, target_def_arg_type, target_def_var_type)\n    # Check autocasting from symbol to enum\n    if arg.is_a?(SymbolLiteral) && target_def_var_type.is_a?(EnumType)\n      symbol_name = arg.value.underscore\n      target_def_var_type.types.each do |enum_name, enum_value|\n        if enum_name.underscore == symbol_name\n          request_value(enum_value.as(Const).value)\n          return\n        end\n      end\n    end\n\n    if arg_type != target_def_var_type && arg.is_a?(NumberLiteral)\n      case target_def_var_type\n      when IntegerType\n        # Autocast to integer\n        compile_number(arg, target_def_var_type.kind, arg.value)\n        return\n      when FloatType\n        # Autocast to float\n        compile_number(arg, target_def_var_type.kind, arg.value)\n        return\n      end\n    end\n\n    request_value(arg)\n\n    # Check number autocast but for non-literals\n    if arg_type != target_def_arg_type && arg_type.is_a?(IntegerType | FloatType) && target_def_arg_type.is_a?(IntegerType | FloatType)\n      primitive_convert(arg, arg_type, target_def_arg_type, checked: false)\n    else\n      # We first cast the argument to the def's arg type,\n      # which is the external methods' type.\n      downcast arg, arg_type, target_def_arg_type\n    end\n\n    # Then we need to cast the argument to the target_def variable\n    # corresponding to the argument. If for example we have this:\n    #\n    # ```\n    # def foo(x : Int32)\n    #   x = nil\n    # end\n    #\n    # foo(1)\n    # ```\n    #\n    # Then the actual type of `x` inside `foo` is (Int32 | Nil),\n    # and we must cast `1` to it.\n    upcast arg, target_def_arg_type, target_def_var_type\n  end\n\n  private def compile_pointerof_node(obj : Var, owner : Type) : Nil\n    if obj.name == \"self\"\n      self_type = @def.not_nil!.vars.not_nil![\"self\"].type\n      if self_type.passed_by_value? && in_multidispatch?\n        # Inside a multidispatch \"self\" is already a pointer.\n        get_local 0, sizeof(Void*), node: obj\n\n        # If the self that we need to pass is a union but the actual type of `obj`\n        # is not a union, we need to reach the union's value.\n        if self_type.remove_indirection.is_a?(MixedUnionType) && !obj.type.remove_indirection.is_a?(MixedUnionType)\n          pointer_add_constant 8, node: obj\n        end\n      elsif self_type == owner\n        put_self(node: obj)\n      else\n        assign_to_temporary_and_return_pointer(obj)\n      end\n      return\n    end\n\n    var = lookup_local_var_or_closured_var(obj.name)\n    var_type = var.type\n\n    if obj.type == var_type\n      pointerof_local_var_or_closured_var(var, node: obj)\n    elsif var_type.is_a?(MixedUnionType) && obj.type.struct?\n      # Get pointer of var\n      pointerof_local_var_or_closured_var(var, node: obj)\n\n      # Add 8 to it, to reach the union value\n      pointer_add_constant 8, node: obj\n    elsif var_type.is_a?(MixedUnionType) && obj.type.is_a?(MixedUnionType)\n      pointerof_local_var_or_closured_var(var, node: obj)\n    elsif var_type.is_a?(VirtualType) && var_type.struct? && var_type.abstract?\n      if obj.type.is_a?(MixedUnionType)\n        # If downcasting to a mix of the subtypes, it's a union type and it\n        # has the same representation as the virtual type\n        pointerof_local_var_or_closured_var(var, node: obj)\n      else\n        # A virtual struct is represented like {type_id, value}, and if we need\n        # to downcast to one of the struct types we need to skip the type_id header,\n        # which is 8 bytes.\n\n        # Get pointer of var\n        pointerof_local_var_or_closured_var(var, node: obj)\n\n        # Add 8 to it, to reach the value\n        pointer_add_constant 8, node: obj\n      end\n    else\n      obj.raise \"BUG: missing call receiver by value cast from #{var_type} to #{obj.type} (#{var_type.class} to #{obj.type.class})\"\n    end\n  end\n\n  private def compile_pointerof_node(obj : InstanceVar, owner : Type) : Nil\n    compile_pointerof_ivar(obj, obj.name)\n  end\n\n  private def compile_pointerof_node(obj : ClassVar, owner : Type) : Nil\n    compile_pointerof_class_var(obj, obj)\n  end\n\n  private def compile_pointerof_node(obj : Path, owner : Type) : Nil\n    const = obj.target_const.not_nil!\n    index = initialize_const_if_needed(const)\n    get_const_pointer index, node: obj\n  end\n\n  private def compile_pointerof_node(obj : ReadInstanceVar, owner : Type) : Nil\n    compile_pointerof_read_instance_var(obj.obj, obj.obj.type, obj.name)\n  end\n\n  private def compile_pointerof_node(call : Call, owner : Type) : Nil\n    call_obj = call.obj\n    with_scope = call.with_scope\n\n    if !call_obj && with_scope && call.uses_with_scope?\n      call_obj = Var.new(WITH_SCOPE, with_scope)\n    end\n\n    target_defs = call.target_defs\n    unless target_defs\n      call.raise \"BUG: no target defs\"\n    end\n\n    unless target_defs.size == 1\n      assign_to_temporary_and_return_pointer(call)\n      return\n    end\n\n    target_def = target_defs.first\n    body = target_def.body\n\n    if body.is_a?(Primitive) && body.name == \"pointer_get\"\n      # We don't want pointer.value to return a copy of something\n      # if we are calling through it\n      call_obj = call_obj.not_nil!\n      request_value(call_obj)\n      return\n    end\n\n    if body.is_a?(InstanceVar)\n      # Inline the call, so that it also works fine when wanting to\n      # take a pointer through things (this is how compiled Crystal works too\n      if call_obj\n        compile_pointerof_read_instance_var(call_obj, target_def.owner, body.name)\n      else\n        compile_pointerof_ivar(body, body.name)\n      end\n\n      # We still have to accept the call arguments, but discard their values\n      call.args.each { |arg| discard_value(arg) }\n      return\n    end\n\n    if body.is_a?(Var) && body.name == \"self\"\n      # We also inline calls that simply return \"self\"\n      if call_obj\n        compile_pointerof_node(call_obj, owner)\n      else\n        put_self(node: call)\n      end\n\n      # We still have to accept the call arguments, but discard their values\n      call.args.each { |arg| discard_value(arg) }\n      return\n    end\n\n    assign_to_temporary_and_return_pointer(call)\n  end\n\n  private def compile_pointerof_node(obj : ASTNode, owner : Type) : Nil\n    assign_to_temporary_and_return_pointer(obj)\n  end\n\n  private def compile_pointerof_slice_literal_buffer(obj : Path, info : Program::ConstSliceInfo) : Nil\n    put_ptr @context.const_slice_buffer(info), node: obj\n  end\n\n  # Assigns the object's value to a temporary\n  # local variable, and then produces a pointer to that local variable.\n  # In this way we make sure that the memory the pointer is pointing\n  # to remains available, at least in this call frame.\n  private def assign_to_temporary_and_return_pointer(obj : ASTNode)\n    temp_var_name = @context.program.new_temp_var_name\n    temp_var_index = @local_vars.declare(temp_var_name, obj.type).not_nil!\n\n    request_value(obj)\n\n    set_local temp_var_index, aligned_sizeof_type(obj), node: obj\n    pointerof_var(temp_var_index, node: obj)\n  end\n\n  private def declare_local_vars(vars_owner, local_vars : LocalVars, owner = vars_owner)\n    needs_closure_context = false\n    special_vars = owner.is_a?(Def) ? owner.special_vars : nil\n\n    # First declare self, if there is one\n    self_var = vars_owner.vars.try &.[\"self\"]?\n    if self_var\n      local_vars.declare(\"self\", self_var.type)\n    end\n\n    # Then define def arguments because those will come in order from calls\n    if owner.is_a?(Def)\n      owner.args.each do |arg|\n        var = owner.vars.not_nil![arg.name]\n        var_type = var.type?\n        next unless var_type\n\n        # The self arg can appear if it's a multidispatch, and we don't want\n        # to declare it twice.\n        next if arg.name == \"self\"\n\n        if var.closure_in?(owner)\n          needs_closure_context = true\n\n          # Declare a local variable with a different name because\n          # we don't want to find it when doing local var lookups,\n          # but we'll need to copy it from the def args to the closure\n          local_vars.declare(closured_arg_name(arg.name), var_type)\n          next\n        end\n\n        local_vars.declare(var.name, var_type)\n      end\n\n      # We also need to declare the block arg with a different name\n      # if it's closured.\n      if owner.uses_block_arg?\n        block_arg = owner.block_arg.not_nil!\n\n        var = owner.vars.not_nil![block_arg.name]\n        var_type = var.type?\n        if var_type && var.closure_in?(owner)\n          needs_closure_context = true\n\n          # Declare a local variable with a different name because\n          # we don't want to find it when doing local var lookups,\n          # but we'll need to copy it from the def args to the closure\n          local_vars.declare(closured_arg_name(block_arg.name), var_type)\n        end\n      end\n    end\n\n    # Now declare special vars, if any\n    if owner.is_a?(Def) && (special_vars = owner.special_vars)\n      special_vars.each do |special_var|\n        var = vars_owner.vars.not_nil![special_var]\n        local_vars.declare(\"#{var.name}*\", @context.program.pointer_of(var.type))\n      end\n    end\n\n    # Next declare all remaining variables\n    vars_owner.vars.try &.each do |name, var|\n      var_type = var.type?\n      next unless var_type\n\n      # Skip if the var was already declared because it's also an argument\n      next if name == \"self\"\n      next if owner.is_a?(Def) && owner.args.any? { |arg| arg.name == name }\n\n      # TODO (optimization): don't declare local var if it's closured,\n      # but we need to be careful to support def args being closured\n      if var.closure_in?(owner)\n        needs_closure_context = true\n        next\n      end\n\n      local_vars.declare(name, var_type)\n    end\n\n    needs_closure_context ||= owner.is_a?(Def) && owner.self_closured?\n\n    if needs_closure_context\n      local_vars.declare(Closure::VAR_NAME, @context.program.pointer_of(@context.program.void))\n    end\n  end\n\n  private def closured_arg_name(name : String)\n    \"^#{name}\"\n  end\n\n  private def initialize_const_if_needed(const)\n    index, compiled_def = get_const_index_and_compiled_def const\n\n    # Do this:\n    #\n    # ```\n    # unless const_initialized(index)\n    #   call const_initializer\n    #   set_const index\n    # end\n    # ```\n\n    # This is `unless const_initialized(index)`\n    const_initialized index, node: nil\n    branch_if 0, node: nil\n    cond_jump_location = patch_location\n\n    # Now we are on the `then` branch\n    call compiled_def, node: nil\n    set_const index, aligned_sizeof_type(const.value), node: nil\n\n    # Here we are outside of the unless\n    patch_jump(cond_jump_location)\n\n    index\n  end\n\n  private def initialize_class_var_if_needed(var, index, compiled_def)\n    # Do this:\n    #\n    # ```\n    # unless class_var_initialized(index)\n    #   call class_var_initializer\n    #   set_class_var index\n    # end\n    # ```\n\n    # This is `unless class_var_initialized(index)`\n    class_var_initialized index, node: nil\n    branch_if 0, node: nil\n    cond_jump_location = patch_location\n\n    # Now we are on the `then` branch\n    call compiled_def, node: nil\n    set_class_var index, aligned_sizeof_type(var), node: nil\n\n    # Here we are outside of the unless\n    patch_jump(cond_jump_location)\n\n    index\n  end\n\n  private def accept_call_members(node : Call)\n    if obj = node.obj\n      obj.accept(self)\n    else\n      put_self(node: node) unless scope.is_a?(Program)\n    end\n\n    node.args.each &.accept(self)\n  end\n\n  def visit(node : Out)\n    case exp = node.exp\n    when Var\n      local_var = lookup_local_var_or_closured_var(exp.name)\n      case local_var\n      in LocalVar\n        pointerof_var(local_var.index, node: node)\n      in ClosuredVar\n        node.raise \"BUG: missing interpreter out closured var\"\n      end\n    when InstanceVar\n      compile_pointerof_ivar(node, exp.name)\n    when Underscore\n      # Allocate a temporary variable just for the underscore, then get a pointer to it\n      temp_var_name = @context.program.new_temp_var_name\n      temp_var_index = @local_vars.declare(temp_var_name, node.type).not_nil!\n\n      pointerof_var temp_var_index, node: node\n    else\n      node.raise \"BUG: unexpected out exp: #{exp}\"\n    end\n\n    false\n  end\n\n  def visit(node : ProcLiteral)\n    is_closure = node.def.closure?\n\n    # TODO: This was copied from Codegen. Why is it not in CleanupTransformer?\n    # If we don't care about a proc literal's return type then we mark the associated\n    # def as returning void. This can't be done in the type inference phase because\n    # of bindings and type propagation.\n    if node.force_nil?\n      node.def.set_type @context.program.nil\n    else\n      # Use proc literal's type, which might have a broader type then the body\n      # (for example, return type: Int32 | String, body: String)\n      node.def.set_type node.return_type\n    end\n\n    target_def = node.def\n    target_def.owner = @context.program\n    args = target_def.args\n\n    # 1. Compile def\n    args_bytesize = args.sum { |arg| aligned_sizeof_type(arg) }\n    args_bytesize += sizeof(Void*) if is_closure\n\n    compiled_def = CompiledDef.new(@context, target_def, target_def.owner, args_bytesize)\n\n    # 2. Store it in context\n    @context.add_gc_reference(compiled_def)\n\n    # Declare local variables for the newly compiled function\n\n    # First declare the proc arguments, so that the order matches the call\n    target_def.args.each do |arg|\n      var = target_def.vars.not_nil![arg.name]\n      var_type = var.type?\n      next unless var_type\n\n      if var.closure_in?(target_def)\n        # Declare a local variable with a different name because\n        # we don't want to find it when doing local var lookups,\n        # but we'll need to copy it from the def args to the closure\n        compiled_def.local_vars.declare(closured_arg_name(arg.name), var_type)\n        next\n      end\n\n      compiled_def.local_vars.declare(arg.name, var_type)\n    end\n\n    needs_closure_context = (target_def.vars.try &.any? { |name, var| var.type? && var.closure_in?(target_def) })\n\n    # Declare the closure context arg and var, if any\n    if is_closure || needs_closure_context\n      if is_closure && needs_closure_context\n        compiled_def.local_vars.declare(Closure::ARG_NAME, @context.program.pointer_of(@context.program.void))\n      end\n\n      compiled_def.local_vars.declare(Closure::VAR_NAME, @context.program.pointer_of(@context.program.void))\n    end\n\n    # Then declare all variables\n    target_def.vars.try &.each do |name, var|\n      var_type = var.type?\n      next unless var_type\n\n      if var.closure_in?(target_def)\n        needs_closure_context = true\n        next\n      end\n\n      # Skip arg because it was already declared above\n      next if target_def.args.any? { |arg| arg.name == name }\n\n      # TODO: closures!\n      next if var.context != target_def\n\n      compiled_def.local_vars.declare(name, var_type)\n    end\n\n    compiler = Compiler.new(@context, compiled_def, scope: scope, top_level: false)\n    begin\n      compiler.compile_def(compiled_def, is_closure ? @closure_context : nil)\n      @context.compiled_procs << compiled_def.object_id\n    rescue ex : Crystal::CodeError\n      node.raise \"compiling #{node}\", inner: ex\n    end\n\n    {% if Debug::DECOMPILE %}\n      puts \"=== ProcLiteral ===\"\n      puts compiled_def.local_vars\n      puts Disassembler.disassemble(@context, compiled_def)\n      puts \"=== ProcLiteral ===\"\n    {% end %}\n\n    # 3. Push compiled_def id to stack\n    put_i64 compiled_def.object_id.to_i64!, node: node\n\n    # 4. Push closure context to stack\n    if is_closure\n      # If it's a closure, we push the pointer that holds the closure data\n      closure_var_index = get_closure_var_index\n      get_local closure_var_index, sizeof(Void*), node: node\n    else\n      # Otherwise, it's a null pointer\n      put_i64 0, node: node\n    end\n\n    false\n  end\n\n  def visit(node : ProcPointer)\n    target_def = node.call.target_def\n\n    unless target_def.owner.is_a?(LibType)\n      # LLVM codegen supports more cases like closure data and obj/self\n      # target, but I can't trigger them — does LiteralExpander expand\n      # these cases into ProcLiteral?\n      raise \"BUG: missing interpret for ProcPointer to non Lib fun\"\n    end\n\n    # find or build a compiled_def\n    proc_type = node.type.as(ProcInstanceType)\n    symbol = @context.c_function(target_def.as(External).real_name)\n    compiled_def = @context.extern_proc_wrapper(proc_type, symbol)\n\n    # push compiled_def to stack + no closure data (null pointer)\n    put_i64 compiled_def.object_id.to_i64!, node: node\n    put_i64 0, node: node\n\n    false\n  end\n\n  def visit(node : Break)\n    exp = node.exp\n\n    exp_type =\n      if exp\n        request_value(exp)\n        exp.type\n      else\n        put_nil node: node\n\n        @context.program.nil_type\n      end\n\n    if target_while = @while\n      target_while = @while.not_nil!\n\n      upcast node, exp_type, target_while.type\n\n      jump 0, node: nil\n      @while_breaks.not_nil! << patch_location\n    elsif compiling_block = @compiling_block\n      block = compiling_block.block\n      target_def = compiling_block.target_def\n\n      final_type = merge_block_break_type(target_def.type, block)\n\n      upcast node, exp_type, final_type\n\n      break_block aligned_sizeof_type(final_type), node: node\n    else\n      node.raise \"BUG: break without target while or block\"\n    end\n\n    false\n  end\n\n  def visit(node : Next)\n    exp = node.exp\n\n    if @while\n      if exp\n        discard_value(exp)\n      else\n        put_nil node: node\n      end\n\n      jump 0, node: nil\n      @while_nexts.not_nil! << patch_location\n    elsif compiling_block = @compiling_block\n      exp_type =\n        if exp\n          request_value(exp)\n          exp.type\n        else\n          put_nil node: node\n          @context.program.nil_type\n        end\n\n      upcast node, exp_type, compiling_block.block.type\n      leave aligned_sizeof_type(compiling_block.block.type), node: node\n    else\n      if @def.try(&.captured_block?)\n        # next inside a proc or captured block is like doing return\n        compile_return(node, exp)\n      else\n        node.raise \"BUG: next without target while, block, and not inside captured_block\"\n      end\n    end\n\n    false\n  end\n\n  def visit(node : Yield)\n    compiled_block = @compiled_block.not_nil!\n    block = compiled_block.block\n\n    splat_index = block.splat_index\n    if splat_index\n      node.raise \"BUG: block with splat should have been rewritten to one withone one\"\n    end\n\n    with_scope = node.scope\n    if with_scope\n      request_value(with_scope)\n    end\n\n    # Check if tuple unpacking is needed.\n    # This happens when a yield has only one expression that's a tuple\n    # type, and the block arguments are more than one.\n    #\n    # For example:\n    #\n    #     def foo\n    #       yield({1, 2})\n    #     end\n    #\n    #     foo do |x, y|\n    #     end\n    #\n    # If the first yield argument is a splat then no tuple unpacking is done:\n    #\n    #     def foo\n    #       yield(*{1, 2}) # no unpacking\n    #     end\n    #\n    #     foo do |x, y|\n    #     end\n    #\n    # Unless... the tuple has a single tuple inside it:\n    #\n    #     def foo\n    #       yield(*{ {1, 2} }) # unpacking 1 into x and 2 into y\n    #     end\n    #\n    #     foo do |x, y|\n    #     end\n    #\n    # That's all expressed in the logic below:\n    if node.exps.size == 1 &&\n       (exp = node.exps.first) &&\n       (tuple_type = exp.type).is_a?(TupleInstanceType) &&\n       (!exp.is_a?(Splat) || (\n         exp.is_a?(Splat) &&\n         tuple_type.tuple_types.size == 1 &&\n         tuple_type.tuple_types.first.is_a?(TupleInstanceType)\n       )) &&\n       block.args.size > 1\n      # This is the case of `yield(*{ {1, 2}})`\n      if exp.is_a?(Splat)\n        exp = exp.exp\n        tuple_type = tuple_type.tuple_types.first.as(TupleInstanceType)\n      end\n\n      # Accept the tuple\n      request_value exp\n\n      # Compute which block var types we need to unpack to,\n      # and what's their total size\n      block_var_types = [] of Type?\n      block_var_types_size = 0\n\n      # We need to cast to the block var, not arg\n      # (the var might have more types in it if it's assigned other values)\n      block.args.each do |block_arg|\n        if block_arg.name == \"_\"\n          block_var_types << nil\n        else\n          block_var = block.vars.not_nil![block_arg.name]\n          block_var_type = block_var.type\n          block_var_types << block_var_type\n          block_var_types_size += aligned_sizeof_type(block_var_type)\n        end\n      end\n\n      unpack_tuple exp, tuple_type, block_var_types\n\n      # We need to discard the tuple value that comes before the unpacked values\n      pop_from_offset aligned_sizeof_type(tuple_type), block_var_types_size, node: nil\n    else\n      block_arg_index = 0\n\n      node.exps.each do |exp|\n        if exp.is_a?(Splat)\n          tuple_type = exp.exp.type.as(TupleInstanceType)\n\n          # First accept the tuple\n          request_value(exp.exp)\n\n          # Compute which block var types we need to unpack to,\n          # and what's their total size\n          block_var_types = [] of Type?\n          block_var_types_size = 0\n\n          tuple_element_index = 0\n          while (block_arg = block.args[block_arg_index]?) && tuple_element_index < tuple_type.tuple_types.size\n            if block_arg.name == \"_\"\n              block_var_types << nil\n            else\n              block_var = block.vars.not_nil![block_arg.name]\n              block_var_type = block_var.type\n              block_var_types << block_var_type\n              block_var_types_size += aligned_sizeof_type(block_var_type)\n            end\n\n            block_arg_index += 1\n            tuple_element_index += 1\n          end\n\n          unpack_tuple exp, tuple_type, block_var_types\n\n          # Now we need to pop the tuple\n          pop_from_offset aligned_sizeof_type(tuple_type), block_var_types_size, node: nil\n        else\n          if (block_arg = block.args[block_arg_index]?) && block_arg.name != \"_\"\n            request_value(exp)\n\n            # We need to cast to the block var, not arg\n            # (the var might have more types in it if it's assigned other values)\n            block_var = block.vars.not_nil![block_arg.name]\n\n            upcast exp, exp.type, block_var.type\n          else\n            discard_value(exp)\n          end\n\n          block_arg_index += 1\n        end\n      end\n    end\n\n    call_block compiled_block, node: node\n\n    unless @wants_value\n      pop aligned_sizeof_type(node), node: nil\n    end\n\n    false\n  end\n\n  def visit(node : ClassDef)\n    with_scope(node.resolved_type.metaclass) do\n      discard_value node.body\n    end\n\n    return false unless @wants_value\n\n    put_nil(node: node)\n    false\n  end\n\n  def visit(node : ModuleDef)\n    with_scope(node.resolved_type.metaclass) do\n      discard_value node.body\n    end\n\n    return false unless @wants_value\n\n    put_nil(node: node)\n    false\n  end\n\n  private def with_scope(scope : Type, &)\n    old_scope = @scope\n    @scope = scope\n    begin\n      yield\n    ensure\n      @scope = old_scope\n    end\n  end\n\n  def visit(node : EnumDef)\n    # TODO: visit body?\n    false\n  end\n\n  def visit(node : Def)\n    false\n  end\n\n  def visit(node : FunDef)\n    false\n  end\n\n  def visit(node : LibDef)\n    false\n  end\n\n  def visit(node : Macro)\n    false\n  end\n\n  def visit(node : VisibilityModifier)\n    node.exp.accept self\n    false\n  end\n\n  def visit(node : Annotation)\n    false\n  end\n\n  def visit(node : AnnotationDef)\n    false\n  end\n\n  def visit(node : Alias)\n    false\n  end\n\n  def visit(node : Include)\n    false\n  end\n\n  def visit(node : Extend)\n    false\n  end\n\n  def visit(node : Unreachable)\n    unreachable(\"Reached the unreachable\", node: node)\n\n    false\n  end\n\n  def visit(node : FileNode)\n    file_module = @context.program.file_module(node.filename)\n\n    a_def = Def.new(node.filename)\n    a_def.body = node.node\n    a_def.owner = @context.program\n    a_def.type = @context.program.nil_type\n    a_def.vars = file_module.vars\n\n    compiled_def = CompiledDef.new(@context, a_def, a_def.owner, 0)\n\n    declare_local_vars(file_module, compiled_def.local_vars)\n\n    compiler = Compiler.new(@context, compiled_def, top_level: true)\n    compiler.compile_def(compiled_def, closure_owner: file_module)\n\n    @context.add_gc_reference(compiled_def)\n\n    {% if Debug::DECOMPILE %}\n      puts \"=== #{node.filename} ===\"\n      puts Disassembler.disassemble(@context, compiled_def)\n      puts \"=== #{node.filename} ===\"\n    {% end %}\n\n    call compiled_def, node: node\n    false\n  end\n\n  def visit(node : ASTNode)\n    node.raise \"BUG: missing interpret for #{node.class}\"\n  end\n\n  # This is where we define one method per instruction/opcode.\n  {% for name, instruction in Crystal::Repl::Instructions %}\n    {% operands = instruction[:operands] || [] of Nil %}\n\n    def {{name.id}}(\n      {{operands.splat(\", \")}}*, node : ASTNode?\n    ) : Nil\n      node = @node_override || node\n      @instructions.nodes[instructions_index] = node if node\n\n      append OpCode::{{ name.id.upcase }}\n      {% for operand in operands %}\n        append {{operand.var}}\n      {% end %}\n    end\n  {% end %}\n\n  private def request_value(node : ASTNode)\n    accept_with_wants_value node, true\n  end\n\n  private def discard_value(node : ASTNode)\n    accept_with_wants_value node, false\n  end\n\n  private def accept_with_wants_value(node : ASTNode, wants_value)\n    old_wants_value = @wants_value\n    @wants_value = wants_value\n    node.accept self\n    @wants_value = old_wants_value\n  end\n\n  # TODO: block.break shouldn't exist: the type should be merged in target_def\n  private def merge_block_break_type(def_type : Type, block : Block)\n    block_break_type = block.break.type?\n    if block_break_type\n      @context.program.type_merge([def_type, block_break_type] of Type) ||\n        @context.program.no_return\n    else\n      def_type\n    end\n  end\n\n  private def put_true(*, node : ASTNode?)\n    put_i64 1_i64, node: node\n  end\n\n  private def put_false(*, node : ASTNode?)\n    put_i64 0_i64, node: node\n  end\n\n  private def put_i8(value : Int8, *, node : ASTNode?)\n    put_i64 value.to_i64!, node: node\n  end\n\n  private def put_u8(value : UInt8, *, node : ASTNode?)\n    put_i64 value.to_u64!.to_i64!, node: node\n  end\n\n  private def put_i16(value : Int16, *, node : ASTNode?)\n    put_i64 value.to_i64!, node: node\n  end\n\n  private def put_u16(value : UInt16, *, node : ASTNode?)\n    put_i64 value.to_u64!.to_i64!, node: node\n  end\n\n  private def put_i32(value : Int32, *, node : ASTNode?)\n    put_i64 value.to_i64!, node: node\n  end\n\n  private def put_u32(value : UInt32, *, node : ASTNode?)\n    put_i64 value.to_u64!.to_i64!, node: node\n  end\n\n  private def put_u64(value : UInt64, *, node : ASTNode?)\n    put_i64 value.to_i64!, node: node\n  end\n\n  private def put_u128(value : UInt128, *, node : ASTNode?)\n    put_i128 value.to_i128!, node: node\n  end\n\n  private def put_ptr(value : Pointer, *, node : ASTNode?)\n    put_i64 value.address.to_i64!, node: node\n  end\n\n  private def put_string(value : String, *, node : ASTNode?)\n    cached_string = @context.program.string_pool.get(value)\n\n    # Compute size so that it's also available on the program.\n    # TODO: maybe we shouldn't use these strings for the interpreted\n    # program and instead put memory for them somehow that would\n    # match their actual memory representation (for example the TYPE_ID\n    # might not match)\n    cached_string.size\n\n    put_i64 cached_string.object_id.unsafe_as(Int64), node: node\n  end\n\n  private def put_type(type : Type, *, node : ASTNode?)\n    put_i32 type_id(type), node: node\n  end\n\n  private def put_def(a_def : Def)\n  end\n\n  private def put_self(*, node : ASTNode)\n    closured_self = lookup_closured_var?(\"self\")\n    if closured_self\n      read_from_closured_var(closured_self, node: node)\n      return\n    end\n\n    if scope.struct?\n      if scope.passed_by_value?\n        get_local 0, sizeof(Pointer(UInt8)), node: node\n      else\n        get_local 0, aligned_sizeof_type(scope), node: node\n      end\n    else\n      get_local 0, sizeof(Pointer(UInt8)), node: node\n    end\n  end\n\n  private def pointer_add_constant(bytes : Int32, *, node : ASTNode?)\n    put_i32 bytes, node: node\n    pointer_add 1, node: node\n  end\n\n  private def append(op_code : OpCode)\n    append op_code.value\n  end\n\n  private def append(a_def : CompiledDef)\n    append(a_def.object_id.unsafe_as(Int64))\n  end\n\n  private def append(a_block : CompiledBlock)\n    append(a_block.object_id.unsafe_as(Int64))\n  end\n\n  private def append(lib_function : LibFunction)\n    append(lib_function.object_id.unsafe_as(Int64))\n  end\n\n  private def append(ffi_call_interface : FFI::CallInterface)\n    append(ffi_call_interface.to_unsafe.unsafe_as(Int64))\n  end\n\n  private def append(call : Call)\n    append(call.object_id.unsafe_as(Int64))\n  end\n\n  private def append(string : String)\n    append(string.object_id.unsafe_as(Int64))\n  end\n\n  private def append(value : Int128)\n    value.unsafe_as(StaticArray(UInt8, 16)).each do |byte|\n      append byte\n    end\n  end\n\n  private def append(value : Int64)\n    value.unsafe_as(StaticArray(UInt8, 8)).each do |byte|\n      append byte\n    end\n  end\n\n  private def append(value : Int32)\n    value.unsafe_as(StaticArray(UInt8, 4)).each do |byte|\n      append byte\n    end\n  end\n\n  private def append(value : Int16)\n    value.unsafe_as(StaticArray(UInt8, 2)).each do |byte|\n      append byte\n    end\n  end\n\n  private def append(value : UInt16)\n    value.unsafe_as(StaticArray(UInt8, 2)).each do |byte|\n      append byte\n    end\n  end\n\n  private def append(value : Int8)\n    append value.to_u8!\n  end\n\n  private def append(value : Symbol)\n    value.unsafe_as(StaticArray(UInt8, 4)).each do |byte|\n      append byte\n    end\n  end\n\n  private def append(value : Bool)\n    append(value ? 1_u8 : 0_u8)\n  end\n\n  private def append(value : UInt8)\n    @instructions.instructions << value\n  end\n\n  private def append(value : Enum)\n    append(value.value)\n  end\n\n  # Many times we need to jump or branch to an instruction for which we don't\n  # know the offset/index yet.\n  # In those cases we generate a jump to zero, but remember where that \"zero\"\n  # is in the bytecode. Once we know where we have to jump, we modify the\n  # bytecode to patch it with the correct jump offset.\n  private def patch_jump(offset : Int32)\n    (@instructions.instructions.to_unsafe + offset).as(Int32*).value = instructions_index\n  end\n\n  # After we emit bytecode for a branch or jump, the last four bytes\n  # are always for the jump offset.\n  private def patch_location\n    instructions_index - 4\n  end\n\n  private def instructions_index\n    @instructions.instructions.size\n  end\n\n  private delegate inner_sizeof_type, inner_alignof_type, aligned_sizeof_type,\n    inner_instance_sizeof_type, inner_instance_alignof_type, aligned_instance_sizeof_type,\n    to: @context\n\n  private def ivar_offset(type : Type, name : String) : Int32\n    if type.extern_union?\n      0\n    else\n      @context.ivar_offset(type, name)\n    end\n  end\n\n  private def type_id(type : Type)\n    @context.type_id(type)\n  end\n\n  private def in_multidispatch?\n    a_def = @def\n    return false unless a_def\n\n    first_arg = a_def.args.first?\n    return false unless first_arg\n\n    first_arg.name == \"self\"\n  end\n\n  private macro nop\n  end\n\n  private def with_node_override(node_override : ASTNode, &)\n    old_node_override = @node_override\n    @node_override = node_override\n    value = yield\n    @node_override = old_node_override\n    value\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/constants.cr",
    "content": "require \"./repl\"\n\n# This class keeps track of all the constants the interpreter will\n# need access to, and allows you to declare and access their position\n# in memory, and whether they have an initializer or not.\n#\n# Trivial constants such as `A = 1` (int literals, bools, etc.) are not stored\n# in memory. Instead, their value is inlined in the bytecode when they are used.\n#\n# The interpreter holds a memory region for constants, for example like this:\n#\n# [_,_,_,_,_,_,_,_,_,......]\n#  ^-----^ ^-------^\n#   A          B\n#\n# In this memory, for each constant there are 8 bytes that indicate\n# whether the constant was initialized or not. After these 8 bytes comes the\n# constant data, aligned to 8 bytes boundaries so that the GC\n# can properly track pointers (that's why we also use 8 bytes for the `initialized` bit.)\n#\n# This class and `ClassVars` are very similar.\nclass Crystal::Repl::Constants\n  # The offset to use after the index of a constant to get access to its data.\n  OFFSET_FROM_INITIALIZED = 8\n\n  # Each value tracked per constant: its index in memory and\n  record Value, index : Int32, compiled_def : CompiledDef\n\n  def initialize(@context : Context)\n    @data = {} of Const => Value\n    @bytesize = 0\n  end\n\n  # Returns the total amount of bytes needed to store all known constants so far.\n  def bytesize\n    @bytesize\n  end\n\n  # Declares a new constant. Returns the index in memory where it will be stored.\n  # `compiled_def` is the constant initializer compiled to bytecode.\n  # Note that at that index the `initializer` \"bit\" (8 bytes) should be stored,\n  # and only after `OFFSET_FROM_INITIALIZER` the data should be stored.\n  def declare(const : Const, compiled_def : CompiledDef) : Int32\n    # if `Compiler#get_const_index_and_compiled_def` calls itself due to a\n    # recursive constant initializer, e.g. `STDOUT`, the same constant might be\n    # declared twice; in that case, use the new initializer but keep the same\n    # constant index as before\n    if value = fetch?(const)\n      @data[const] = value.copy_with(compiled_def: compiled_def)\n      return value.index\n    end\n\n    type = const.value.type\n\n    index = @bytesize\n    @data[const] = Value.new(index, compiled_def)\n\n    @bytesize += OFFSET_FROM_INITIALIZED\n    @bytesize += @context.aligned_sizeof_type(type)\n\n    index\n  end\n\n  # Fetches a constant, if it's there.\n  def fetch?(const : Const) : Value?\n    @data[const]?\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/context.cr",
    "content": "require \"./repl\"\n\n# This class contains all of the global data used to interpret a single\n# program. For example, it includes the memory region to store constants\n# and class variables, what are all the know symbols, and a few more things.\nclass Crystal::Repl::Context\n  record MultidispatchKey, obj_type : Type, call_signature : CallSignature\n\n  getter program : Program\n\n  # A hash of Def to their compiled representation, so we don't compile\n  # a single method multiple times.\n  # The exceptions are methods with non-captured blocks.\n  getter defs : Hash(Def, CompiledDef)\n\n  # Information about all known constants.\n  getter! constants : Constants\n\n  # Information about all known class variables.\n  getter! class_vars : ClassVars\n\n  # libffi information about external functions.\n  getter lib_functions : Hash(Void*, LibFunction)\n\n  # Cache of multidispatch expansions.\n  getter multidispatchs : Hash(MultidispatchKey, Def)\n\n  # The single closure function that we use for all function pointers (procs)\n  # passed to C. This closure function knows all the information about\n  # the callback being passed, and is able to run the code associated to it.\n  getter ffi_closure_fun : LibFFI::ClosureFun\n\n  # The memory where constants are stored. Refer to `Constants` for more on this.\n  property constants_memory : Pointer(UInt8)\n\n  # The memory where class vars are stored. Refer to `ClassVars` for more on this.\n  property class_vars_memory : Pointer(UInt8)\n\n  # Associated an FFI::Closure's code to a CompiledDef.\n  # When we set an extern struct's field that is a Proc, we create\n  # an FFI::Closure object and set that instead of a Proc.\n  # In case a user reads that field back we need to create a Proc,\n  # we can't use an FFI::Closure object. In that case we lookup\n  # the proc in this Hash.\n  getter ffi_closure_to_compiled_def : Hash(Void*, CompiledDef)\n\n  # Cached underlying buffers for constant slices constructed via the\n  # `Slice.literal` compiler built-in, indexed by the internal buffer name\n  # (e.g. `$Slice:0`).\n  @const_slice_buffers = {} of String => UInt8*\n\n  # A cache of object IDs of the `CompiledDef`s corresponding to `ProcLiteral`s.\n  # Used to determine whether such an object ID or a raw function pointer is\n  # passed to `Proc.new`.\n  getter compiled_procs = Set(UInt64).new\n\n  # A cache from C proc pointers to `CompiledDef`s, formed using `Proc.new`.\n  getter extern_proc_wrappers = {} of Void* => CompiledDef\n\n  def initialize(@program : Program)\n    @program.flags << \"interpreted\"\n\n    @gc_references = [] of Void*\n\n    @defs = {} of Def => CompiledDef\n    @defs.compare_by_identity\n\n    @lib_functions = {} of Void* => LibFunction\n\n    @symbol_to_index = {} of String => Int32\n    @symbols = [] of String\n\n    @multidispatchs = {} of MultidispatchKey => Def\n\n    @ffi_closure_contexts = {} of {UInt64, UInt64} => FFIClosureContext\n\n    @constants_memory = Pointer(Void).malloc(1).as(UInt8*)\n    @class_vars_memory = Pointer(Void).malloc(1).as(UInt8*)\n\n    @ffi_closure_to_compiled_def = {} of Void* => CompiledDef\n\n    @type_instance_var_initializers = {} of Type => Array(CompiledDef)\n\n    @ffi_closure_fun = LibFFI::ClosureFun.new do |cif, ret, args, user_data|\n      Context.ffi_closure_fun(cif, ret, args, user_data)\n      nil\n    end\n\n    # This is a stack pool, for checkout_stack.\n    @stack_pool = Fiber::StackPool.new(protect: false, reuse_dead_fiber_stack: false)\n\n    # Mapping of types to numeric ids\n    @type_to_id = {} of Type => Int32\n    @id_to_type = [] of Type\n\n    @constants = Constants.new(self)\n    @class_vars = ClassVars.new(self)\n\n    # Nil has type id 0, String has type id 1\n    type_id(@program.nil_type)\n    type_id(@program.string)\n  end\n\n  getter(throw_value_type : Type) do\n    @program.static_array_of(@program.uint8, sizeof(Interpreter::ThrowValue))\n  end\n\n  # Many reference values we create when compiling nodes to bytecode\n  # must not be collected by the GC. Ideally they should be referenced\n  # in the bytecode itself. The problem is that the bytecode isn't\n  # always aligned to 8 bytes boundaries. So until we figure out what's\n  # the proper way to do it, we just retain these references here.\n  def add_gc_reference(ref : Reference)\n    @gc_references << ref.as(Void*)\n  end\n\n  # Checks out a stack from the stack pool and yields it to the given block.\n  # Once the block returns, the stack is returned to the pool.\n  # The stack is not cleared after or before it's used.\n  def checkout_stack(& : UInt8* -> _)\n    stack = @stack_pool.checkout\n\n    begin\n      yield stack.pointer.as(UInt8*)\n    ensure\n      @stack_pool.release(stack)\n    end\n  end\n\n  # This returns the CompiledDef that corresponds to __crystal_raise_overflow\n  getter(crystal_raise_overflow_compiled_def : CompiledDef) do\n    call = Call.new(\"__crystal_raise_overflow\", global: true)\n    program.semantic(call)\n\n    local_vars = LocalVars.new(self)\n    compiler = Compiler.new(self, local_vars)\n    compiler.compile(call)\n\n    defs[call.target_def]\n  end\n\n  record InstanceVarInitializer,\n    initializer : InstanceVarInitializerContainer::InstanceVarInitializer,\n    owner : Type\n\n  def type_instance_var_initializers(type : Type)\n    @type_instance_var_initializers[type] ||= begin\n      initializers = [] of InstanceVarInitializer\n      collect_instance_vars_initializers(type, initializers)\n\n      initializers.map do |initializer|\n        a_def = create_instance_var_initializer_def(type, initializer)\n\n        compiled_def = CompiledDef.new(self, a_def, a_def.owner, sizeof(Pointer(Void)))\n        compiled_def.local_vars.declare(\"self\", type)\n\n        initializer.initializer.meta_vars.each do |name, var|\n          var_type = var.type?\n          next unless var_type\n\n          compiled_def.local_vars.declare(name, var_type)\n        end\n\n        compiler = Compiler.new(self, compiled_def, top_level: false)\n        compiler.compile_def(compiled_def)\n\n        {% if Debug::DECOMPILE %}\n          puts \"=== #{a_def.name} ===\"\n          puts Disassembler.disassemble(self, compiled_def)\n          puts \"=== #{a_def.name} ===\"\n        {% end %}\n\n        compiled_def\n      end\n    end\n  end\n\n  private def collect_instance_vars_initializers(type : ClassType | GenericClassInstanceType, collected) : Nil\n    if superclass = type.superclass\n      collect_instance_vars_initializers superclass, collected\n    end\n\n    collect_instance_vars_initializers_non_recursive type, collected\n  end\n\n  private def collect_instance_vars_initializers(type : Type, collected) : Nil\n    # Nothing to do\n  end\n\n  private def collect_instance_vars_initializers_non_recursive(type : Type, collected) : Nil\n    initializers = type.instance_vars_initializers\n    initializers.try &.each do |initializer|\n      collected << InstanceVarInitializer.new(initializer, type)\n    end\n  end\n\n  private def create_instance_var_initializer_def(type : Type, initializer : InstanceVarInitializer)\n    # Creates a def that will assign the initializer's value to the instance variable.\n    # The initializer's value is fully typed already, so we don't need to type it\n    # again. We can just create the assignment and type those nodes for the\n    # interpreter compiler to be able to compile it.\n    value = initializer.initializer.value\n\n    ivar = InstanceVar.new(initializer.initializer.name)\n    ivar.type = value.type\n\n    assign = Assign.new(ivar, value)\n    assign.type = value.type\n\n    a_def = Def.new(\"initialize_#{initializer.initializer.name}\", args: [Arg.new(\"self\", type: type)])\n    a_def.body = assign\n    a_def.type = program.nil_type\n    a_def.owner = type\n\n    vars = initializer.initializer.meta_vars.clone\n    vars[\"self\"] = MetaVar.new(\"self\", type)\n    a_def.vars = vars\n\n    a_def\n  end\n\n  def symbol_index(symbol : String) : Int32\n    # TODO: use a string pool?\n    index = @symbol_to_index[symbol]?\n    unless index\n      index = @symbol_to_index.size\n      @symbol_to_index[symbol] = index\n      @symbols << symbol\n    end\n    index\n  end\n\n  def index_to_symbol(index : Int32) : String\n    @symbols[index]\n  end\n\n  def declare_const(const : Const, compiled_def : CompiledDef) : Int32\n    constants.declare(const, compiled_def)\n  end\n\n  def const_index_and_compiled_def?(const : Const) : {Int32, CompiledDef}?\n    value = constants.fetch?(const)\n    if value\n      {value.index, value.compiled_def}\n    else\n      nil\n    end\n  end\n\n  def declare_class_var(owner : Type, name : String, type : Type, compiled_def : CompiledDef?) : Int32\n    class_vars.declare(owner, name, type, compiled_def)\n  end\n\n  def class_var_index_and_compiled_def(owner : Type, name : String) : {Int32, CompiledDef?}?\n    value = class_vars.fetch?(owner, name)\n    if value\n      {value.index, value.compiled_def}\n    else\n      nil\n    end\n  end\n\n  def extern_proc_wrapper(proc_type : ProcInstanceType, symbol : Void*) : CompiledDef\n    extern_proc_wrappers.put_if_absent(symbol) do\n      crystal_args_bytesize = proc_type.arg_types.sum { |arg| aligned_sizeof_type(arg) }\n      target_def = proc_type.lookup_first_def(\"call\", false).not_nil!\n      compiled_def = CompiledDef.new(self, target_def, proc_type, crystal_args_bytesize)\n\n      proc_type.arg_types.each_with_index do |arg_type, i|\n        compiled_def.local_vars.declare(\"arg#{i}\", arg_type)\n      end\n\n      compiler = Compiler.new(self, compiled_def, top_level: false)\n      compiler.compile_extern_proc_wrapper(target_def, proc_type, symbol)\n\n      compiled_def\n    end\n  end\n\n  def ffi_closure_context(interpreter : Interpreter, compiled_def : CompiledDef)\n    # Keep the closure contexts in a Hash by the compiled def so we don't\n    # lose a reference to it in the GC.\n    @ffi_closure_contexts[{interpreter.object_id, compiled_def.object_id}] ||= FFIClosureContext.new(interpreter, compiled_def)\n  end\n\n  protected def self.ffi_closure_fun(cif : LibFFI::Cif*, ret : Void*, args : Void**, user_data : Void*)\n    # This is the generic callback that gets called on any C callback.\n    closure_context = user_data.as(FFIClosureContext)\n    interpreter = closure_context.interpreter\n    compiled_def = closure_context.compiled_def\n\n    # What to do:\n    #   - create a new interpreter that uses the same stack\n    #     (call the second initialize overload)\n    #   - copy args into the stack, starting from stack_top\n    #   - call interpret on the compiled_def.def.body\n    #   - copy the value back to ret\n\n    interpreter.context.checkout_stack do |stack|\n      stack_top = stack\n\n      # Clear the proc's local vars area, the stack might have garbage there\n      stack_top.clear(compiled_def.local_vars.max_bytesize)\n\n      compiled_def.def.args.each_with_index do |arg, i|\n        args[i].as(UInt8*).copy_to(stack_top, interpreter.inner_sizeof_type(arg.type))\n        stack_top += interpreter.aligned_sizeof_type(arg.type)\n      end\n\n      # TODO: maybe we don't need a new interpreter for this?\n      sub_interpreter = Interpreter.new(interpreter, compiled_def, stack, 0)\n\n      value = sub_interpreter.interpret(compiled_def.def.body, compiled_def.def.vars.not_nil!)\n\n      value.copy_to(ret.as(UInt8*))\n    end\n  end\n\n  def const_slice_buffer(info : Program::ConstSliceInfo) : UInt8*\n    @const_slice_buffers.put_if_absent(info.name) { info.to_bytes.to_unsafe }\n  end\n\n  def aligned_sizeof_type(node : ASTNode) : Int32\n    aligned_sizeof_type(node.type?)\n  end\n\n  def aligned_sizeof_type(type : Type) : Int32\n    align(inner_sizeof_type(type))\n  end\n\n  def aligned_sizeof_type(type : Nil) : Int32\n    0\n  end\n\n  def inner_sizeof_type(node : ASTNode) : Int32\n    inner_sizeof_type(node.type?)\n  end\n\n  def inner_sizeof_type(type : Type) : Int32\n    @program.size_of(type.sizeof_type).to_i32\n  end\n\n  def inner_sizeof_type(type : Nil) : Int32\n    0\n  end\n\n  def inner_alignof_type(node : ASTNode) : Int32\n    inner_alignof_type(node.type?)\n  end\n\n  def inner_alignof_type(type : Type) : Int32\n    @program.align_of(type.sizeof_type).to_i32\n  end\n\n  def inner_alignof_type(type : Nil) : Int32\n    0\n  end\n\n  def aligned_instance_sizeof_type(type : Type) : Int32\n    align(inner_instance_sizeof_type(type))\n  end\n\n  def inner_instance_sizeof_type(node : ASTNode) : Int32\n    inner_instance_sizeof_type(node.type?)\n  end\n\n  def inner_instance_sizeof_type(type : Type) : Int32\n    @program.instance_size_of(type.sizeof_type).to_i32\n  end\n\n  def inner_instance_sizeof_type(type : Nil) : Int32\n    0\n  end\n\n  def inner_instance_alignof_type(node : ASTNode) : Int32\n    inner_instance_alignof_type(node.type?)\n  end\n\n  def inner_instance_alignof_type(type : Type) : Int32\n    @program.instance_align_of(type.sizeof_type).to_i32\n  end\n\n  def inner_instance_alignof_type(type : Nil) : Int32\n    0\n  end\n\n  def offset_of(type : Type, index : Int32) : Int32\n    @program.offset_of(type.sizeof_type, index).to_i32\n  end\n\n  def instance_offset_of(type : Type, index : Int32) : Int32\n    @program.instance_offset_of(type.sizeof_type, index).to_i32\n  end\n\n  def ivar_offset(type : Type, name : String) : Int32\n    ivar_index = type.index_of_instance_var(name).not_nil!\n\n    if type.is_a?(VirtualType) && type.struct? && type.abstract?\n      # If the type is a virtual abstract struct then the type\n      # is actually represented as {type_id, value} so the offset\n      # of the instance var is behind type_id, which is 8 bytes\n      @program.offset_of(type.base_type, ivar_index).to_i32 + 8\n    elsif type.passed_by_value?\n      @program.offset_of(type.sizeof_type, ivar_index).to_i32\n    else\n      @program.instance_offset_of(type.sizeof_type, ivar_index).to_i32\n    end\n  end\n\n  def type_id(type : Type) : Int32\n    id = @type_to_id[type]?\n    unless id\n      id = @id_to_type.size\n      @id_to_type << type\n      @type_to_id[type] = id\n    end\n    id\n  end\n\n  def type_from_id(id : Int32) : Type\n    @id_to_type[id]\n  end\n\n  getter? loader : Loader?\n\n  getter(loader : Loader) {\n    lib_flags = program.lib_flags\n    # Execute and expand `subcommands`.\n    lib_flags = lib_flags.gsub(/`(.*?)`/) { `#{$1}`.chomp }\n\n    args = Process.parse_arguments(lib_flags)\n    # FIXME: Part 1: This is a workaround for initial integration of the interpreter:\n    # The loader can't handle the static libgc.a usually shipped with crystal and loading as a shared library conflicts\n    # with the compiler's own GC.\n    # (Windows doesn't seem to have this issue)\n    unless program.has_flag?(\"win32\") && program.has_flag?(\"gnu\")\n      args.delete(\"-lgc\")\n    end\n\n    # recreate the MSVC developer prompt environment, similar to how compiled\n    # code does it in `Compiler#linker_command`\n    if program.has_flag?(\"msvc\")\n      _, link_args = program.msvc_compiler_and_flags\n      args.concat(link_args)\n    end\n\n    Crystal::Loader.parse(args, dll_search_paths: dll_search_paths).tap do |loader|\n      # FIXME: Part 2: This is a workaround for initial integration of the interpreter:\n      # We append a handle to the current executable (i.e. the compiler program)\n      # to the loader's handle list. This gives the loader access to all the symbols in the compiler program,\n      # including those from statically linked libraries like libgc.\n      # This probably won't work for a fully statically linked compiler.\n      # But `Crystal::Loader` currently doesn't support that anyways.\n      loader.load_current_program_handle\n\n      if ENV[\"CRYSTAL_INTERPRETER_LOADER_INFO\"]?.presence\n        STDERR.puts \"Crystal::Loader loaded libraries:\"\n        loader.loaded_libraries.each do |path|\n          STDERR.puts \"      #{path}\"\n        end\n      end\n    end\n  }\n\n  # Extra DLL search paths to mimic compiled code's DLL-copying behavior\n  # regarding `@[Link]` annotations. These directories should match the ones\n  # used in `Crystal::Program#each_dll_path`\n  private def dll_search_paths\n    {% if flag?(:msvc) %}\n      paths = CrystalLibraryPath.default_paths\n\n      if executable_path = Process.executable_path\n        paths << File.dirname(executable_path)\n      end\n\n      ENV[\"PATH\"]?.try &.split(Process::PATH_DELIMITER, remove_empty: true) do |path|\n        paths << path\n      end\n\n      paths\n    {% else %}\n      nil\n    {% end %}\n  end\n\n  def c_function(name : String)\n    loader.find_symbol(name)\n  end\n\n  def align(size : Int32) : Int32\n    rem = size.remainder(8)\n    if rem == 0\n      size\n    else\n      size + (8 - rem)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/debug.cr",
    "content": "require \"./repl\"\n\n# Some constants to be used at compile-time to debug\n# the interpreter itself.\n#\n# These are not used at runtime because they significantly\n# slow down the interpreter.\nmodule Crystal::Repl::Debug\n  TRACE     = false\n  DECOMPILE = false\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/disassembler.cr",
    "content": "require \"./repl\"\n\n# Allows showing bytecode in a human-readable way.\n# TODO: right now local variables are not very nicely.\nmodule Crystal::Repl::Disassembler\n  def self.disassemble(context : Context, compiled_def : CompiledDef) : String\n    disassemble(context, compiled_def.instructions, compiled_def.local_vars)\n  end\n\n  def self.disassemble(context : Context, compiled_block : CompiledBlock) : String\n    disassemble(context, compiled_block.instructions, compiled_block.local_vars)\n  end\n\n  def self.disassemble(context : Context, instructions : CompiledInstructions, local_vars : LocalVars) : String\n    String.build do |io|\n      exception_handlers = instructions.exception_handlers\n      if exception_handlers\n        io.puts \"Catch table\"\n        io.puts \"===========\"\n        exception_handlers.each do |handler|\n          io << \"st: \" << handler.start_index << \", \"\n          io << \"ed: \" << handler.end_index << \", \"\n          if exception_types = handler.exception_types\n            io << \"ex: \"\n            exception_types.join(io, \", \")\n            io << \", \"\n          end\n          io << \"cont: \" << handler.jump_index\n          io.puts\n        end\n      end\n\n      ip = 0\n      while ip < instructions.instructions.size\n        ip = disassemble_one(context, instructions, ip, local_vars, io)\n      end\n    end\n  end\n\n  def self.disassemble_one(context : Context, instructions : CompiledInstructions, ip : Int32, local_vars : LocalVars, io : IO) : Int32\n    io.print ip.to_s.rjust(4, '0')\n    io.print ' '\n\n    node = instructions.nodes[ip]?\n    op_code, ip = next_instruction instructions, ip, OpCode\n\n    {% begin %}\n      case op_code\n        {% for name, instruction in Crystal::Repl::Instructions %}\n          in .{{name.id}}?\n            io.print \"{{name}}\"\n            {% for operand in instruction[:operands] || [] of Nil %}\n              {{operand.var}}, ip = next_instruction instructions, ip, {{operand.type}}\n            {% end %}\n\n            {% if instruction[:disassemble] %}\n              {% for name, disassemble in instruction[:disassemble] %}\n                {{name.id}} = {{disassemble}}\n              {% end %}\n            {% end %}\n\n            {% for operand in instruction[:operands] || [] of Nil %}\n              io.print \" \"\n              io.print {{operand.var}}\n            {% end %}\n            io.puts\n        {% end %}\n      end\n    {% end %}\n\n    ip\n  end\n\n  private def self.next_instruction(instructions, ip, t : T.class) forall T\n    {\n      (instructions.instructions.to_unsafe + ip).as(T*).value,\n      ip + sizeof(T),\n    }\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/escaping_exception.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\nrequire \"./repl\"\n\nclass Crystal::Repl::EscapingException < Exception\n  getter exception_pointer : UInt8*\n\n  def initialize(@interpreter : Interpreter, @exception_pointer : UInt8*)\n  end\n\n  def to_s(io : IO)\n    type_id = @exception_pointer.as(Int32*).value\n    type = @interpreter.context.type_from_id(type_id)\n\n    decl = UninitializedVar.new(Var.new(\"ex\"), TypeNode.new(@interpreter.context.program.exception.virtual_type))\n    call = Call.new(Var.new(\"ex\"), \"inspect_with_backtrace\")\n    exps = Expressions.new([decl, call] of ASTNode)\n\n    begin\n      value = Interpreter.interpret(context, exps) do |stack|\n        stack.as(UInt8**).value = @exception_pointer\n      end\n      if value.type == context.program.string\n        value.pointer.as(UInt8**).value.unsafe_as(String).to_s(io)\n      else\n        io.puts \"Exception#inspect_with_backtrace didn't return a String :/\"\n      end\n    rescue ex\n      io.puts \"Error while calling inspect_with_backtrace on exception: #{ex.message}\"\n      io.puts type\n    end\n  end\n\n  private def context\n    @interpreter.context\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/ffi_closure_context.cr",
    "content": "require \"./repl\"\n\nclass Crystal::Repl::FFIClosureContext\n  getter interpreter : Interpreter\n  getter compiled_def : CompiledDef\n\n  def initialize(@interpreter : Interpreter, @compiled_def : CompiledDef)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/instruction.cr",
    "content": "alias Crystal::Repl::Instruction = UInt8\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/instructions.cr",
    "content": "require \"./repl\"\n\n# This is the list of every VM instruction.\n#\n# An instruction consists of:\n# - a name/opcode: the name is only for debugging purposes, in the bytecode\n#   (bytes) it's just a number (a byte)\n# - operands: (default: []) values in the bytecode following the opcode.\n#   For example a `pop` instruction has an operand that tells it how many\n#   bytes to pop from the stack.\n# - pop_values: (default: []) the (typed) values to pop from the stack.\n# - push: (default: false) if true, the return value of `code` will be pushed to the stack.\n#   Some instructions have `push` set to false and manually push or\n#   modify the stack.\n# - overflow: (default: false) if true, rescue any OverflowError exception\n#   in the interpreter and produce a call to raise an OverflowError in the\n#   interpreted program.\n# - disassemble: (optional) a named tupled where operands can be mapped to a nicer\n#   string representation, when disassembling code.\n#\n# The instructions here are just \"macro code\" that's injected into several places:\n# - the `Compiler` will define one method per instruction that receive the specified operands\n# - the `Interpreter` will define code that reads operands and pops values, and executes `code`,\n#   optionally pushing things to the stack if `push` is true.\n# - the `Disassembler` will show a human friendly representation of the bytecode\n{% begin %}\n  Crystal::Repl::Instructions =\n    {\n      # <<< Put (3)\n\n      # Puts a nil value at the top of the stack.\n      # In reality, this doesn't push anything to the stack because\n      # nil doesn't occupy any bytes, but it's still useful to have\n      # this instruction so that `pry` stops on a `nil` value.\n      # TODO: maybe not? We could reduce the bytecode then.\n      put_nil: {\n        code:       nil,\n      },\n\n      # Puts an Int64 at the top of the stack.\n      put_i64: {\n        operands:   [value : Int64],\n        push:       true,\n        code:       value,\n      },\n      # Puts an Int128 at the top of the stack.\n      put_i128: {\n        operands:   [value : Int128],\n        push:       true,\n        code:       value,\n      },\n      # >>> Put (3)\n\n      # <<< Conversions (21)\n      # These convert a value in the stack into another value.\n      i64_to_i8: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i8,\n      },\n      i64_to_i16: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i16,\n      },\n      i64_to_i32: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i32,\n      },\n      i64_to_u8: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u8,\n      },\n      i64_to_u16: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u16,\n      },\n      i64_to_u32: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u32,\n      },\n      i64_to_u64: {\n        pop_values: [value : Int64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u64,\n      },\n      i64_to_f32: {\n        pop_values: [value : Int64],\n        push:       true,\n        code:       value.to_f32,\n      },\n      i64_to_f64: {\n        pop_values: [value : Int64],\n        push:       true,\n        code:       value.to_f64,\n      },\n      u64_to_i8: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i8,\n      },\n      u64_to_i16: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i16,\n      },\n      u64_to_i32: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i32,\n      },\n      u64_to_i64: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i64,\n      },\n      u64_to_u8: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u8,\n      },\n      u64_to_u16: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u16,\n      },\n      u64_to_u32: {\n        pop_values: [value : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u32,\n      },\n      u64_to_f32: {\n        pop_values: [value : UInt64],\n        push:       true,\n        code:       value.to_f32,\n      },\n      u64_to_f64: {\n        pop_values: [value : UInt64],\n        push:       true,\n        code:       value.to_f64,\n      },\n      i128_to_i8: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i8,\n      },\n      i128_to_i16: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i16,\n      },\n      i128_to_i32: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i32,\n      },\n      i128_to_i64: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i64,\n      },\n      i128_to_u8: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u8,\n      },\n      i128_to_u16: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u16,\n      },\n      i128_to_u32: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u32,\n      },\n      i128_to_u64: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u64,\n      },\n      i128_to_u128: {\n        pop_values: [value : Int128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u128,\n      },\n      i128_to_f32: {\n        pop_values: [value : Int128],\n        push:       true,\n        code:       value.to_f32,\n      },\n      i128_to_f64: {\n        pop_values: [value : Int128],\n        push:       true,\n        code:       value.to_f64,\n      },\n      u128_to_i8: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i8,\n      },\n      u128_to_i16: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i16,\n      },\n      u128_to_i32: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i32,\n      },\n      u128_to_i64: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i64,\n      },\n      u128_to_i128: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i128,\n      },\n      u128_to_u8: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u8,\n      },\n      u128_to_u16: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u16,\n      },\n      u128_to_u32: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u32,\n      },\n      u128_to_u64: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u64,\n      },\n      u128_to_f32: {\n        pop_values: [value : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       value.to_f32,\n      },\n      u128_to_f64: {\n        pop_values: [value : UInt128],\n        push:       true,\n        code:       value.to_f64,\n      },\n      u128_to_f32_bang: {\n        pop_values: [value : UInt128],\n        push:       true,\n        code:       value.to_f32!,\n      },\n      f32_to_f64: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       value.to_f64,\n      },\n      f32_to_u32_bang: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       value.to_u32!,\n      },\n      f32_to_u64_bang: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       value.to_u64!,\n      },\n      f64_to_i8: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i8,\n      },\n      f64_to_i16: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i16,\n      },\n      f64_to_i32: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i32,\n      },\n      f64_to_i64: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i64,\n      },\n      f64_to_i128: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_i128,\n      },\n      f64_to_i128_bang: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       value.to_i128!,\n      },\n      f64_to_u128: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u128,\n      },\n      f64_to_u8: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u8,\n      },\n      f64_to_u16: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u16,\n      },\n      f64_to_u32: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u32,\n      },\n      f64_to_u32_bang: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   false,\n        code:       value.to_u32!,\n      },\n      f64_to_u64: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_u64,\n      },\n      f64_to_i64_bang: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       value.to_i64!,\n      },\n      f64_to_u64_bang: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       value.to_u64!,\n      },\n      f64_to_f32: {\n        pop_values: [value : Float64],\n        push:       true,\n        overflow:   true,\n        code:       value.to_f32,\n      },\n      f64_to_f32_bang: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       value.to_f32!,\n      },\n      # Extend the sign of a signed number.\n      # For example when converting an Int8 into an Int16, we actually\n      # extend it to Int64 by changing the 7 bytes the follow the initial byte.\n      sign_extend: {\n        operands:   [amount : Int32],\n        code:       begin\n          # This is for 128 bits\n          stack_grow_by(8) if amount >= 8\n\n          if (stack - amount - 1).as(Int8*).value < 0\n            Intrinsics.memset((stack - amount).as(Void*), 255_u8, amount, false)\n          else\n            (stack - amount).clear(amount)\n          end\n        end,\n      },\n\n      # Extend an unsigned number by filling it with zeros.\n      zero_extend: {\n        operands:   [amount : Int32],\n        code:       begin\n          # This is for 128 bits\n          stack_grow_by(8) if amount >= 8\n\n          (stack - amount).clear(amount)\n        end,\n      },\n      # >>> Conversions (21)\n\n      # <<< Math (36)\n      add_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      add_wrap_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a &+ b,\n      },\n      sub_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      sub_wrap_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a &- b,\n      },\n      mul_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      mul_wrap_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a &* b,\n      },\n      xor_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a ^ b,\n      },\n      or_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a | b,\n      },\n      and_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a & b,\n      },\n      unsafe_shr_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_shl_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a.unsafe_shl(b),\n      },\n      unsafe_div_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      add_wrap_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a &+ b,\n      },\n      sub_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      sub_wrap_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a &- b,\n      },\n      mul_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      mul_wrap_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a &* b,\n      },\n      xor_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a ^ b,\n      },\n      or_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a | b,\n      },\n      and_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a & b,\n      },\n      unsafe_shr_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_shl_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a.unsafe_shl(b),\n      },\n      unsafe_div_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      add_wrap_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a &+ b,\n      },\n      sub_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      sub_wrap_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a &- b,\n      },\n      mul_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      mul_wrap_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a &* b,\n      },\n      xor_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a ^ b,\n      },\n      or_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a | b,\n      },\n      and_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a & b,\n      },\n      unsafe_shr_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_shl_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a.unsafe_shl(b),\n      },\n      unsafe_div_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_f32: {\n        pop_values: [a : Float32, b : Float32],\n        push:       true,\n        code:       a + b,\n      },\n      sub_f32: {\n        pop_values: [a : Float32, b : Float32],\n        push:       true,\n        code:       a - b,\n      },\n      mul_f32: {\n        pop_values: [a : Float32, b : Float32],\n        push:       true,\n        code:       a * b,\n      },\n      div_f32: {\n        pop_values: [a : Float32, b : Float32],\n        push:       true,\n        code:       a / b,\n      },\n      add_f64: {\n        pop_values: [a : Float64, b : Float64],\n        push:       true,\n        code:       a + b,\n      },\n      sub_f64: {\n        pop_values: [a : Float64, b : Float64],\n        push:       true,\n        code:       a - b,\n      },\n      mul_f64: {\n        pop_values: [a : Float64, b : Float64],\n        push:       true,\n        code:       a * b,\n      },\n      div_f64: {\n        pop_values: [a : Float64, b : Float64],\n        push:       true,\n        code:       a / b,\n      },\n      add_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      add_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       a + b,\n      },\n      sub_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       a - b,\n      },\n      mul_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        overflow:   true,\n        code:       a * b,\n      },\n      unsafe_shr_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        code:       a.unsafe_shr(b),\n      },\n      unsafe_div_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        code:       a.unsafe_div(b),\n      },\n      unsafe_mod_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        code:       a.unsafe_mod(b),\n      },\n      # >>> Math (36)\n\n      # <<< Comparisons (14)\n      cmp_i32: {\n        pop_values: [a : Int32, b : Int32],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_u32: {\n        pop_values: [a : UInt32, b : UInt32],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_i64: {\n        pop_values: [a : Int64, b : Int64],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_u64: {\n        pop_values: [a : UInt64, b : UInt64],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_i128: {\n        pop_values: [a : Int128, b : Int128],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_u128: {\n        pop_values: [a : UInt128, b : UInt128],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_u64_i64: {\n        pop_values: [a : UInt64, b : Int64],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_i64_u64: {\n        pop_values: [a : Int64, b : UInt64],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_u128_i128: {\n        pop_values: [a : UInt128, b : Int128],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_i128_u128: {\n        pop_values: [a : Int128, b : UInt128],\n        push:       true,\n        code:       a == b ? 0 : (a < b ? -1 : 1),\n      },\n      cmp_f32: {\n        operands:   [predicate : Compiler::FloatPredicate],\n        pop_values: [a : Float32, b : Float32],\n        push:       true,\n        code:       predicate.compare(a, b),\n      },\n      cmp_f64: {\n        operands:   [predicate : Compiler::FloatPredicate],\n        pop_values: [a : Float64, b : Float64],\n        push:       true,\n        code:       predicate.compare(a, b),\n      },\n      cmp_eq: {\n        pop_values: [cmp : Int32],\n        push:       true,\n        code:       cmp == 0,\n      },\n      cmp_neq: {\n        pop_values: [cmp : Int32],\n        push:       true,\n        code:       cmp != 0,\n      },\n      cmp_lt: {\n        pop_values: [cmp : Int32],\n        push:       true,\n        code:       cmp < 0,\n      },\n      cmp_le: {\n        pop_values: [cmp : Int32],\n        push:       true,\n        code:       cmp <= 0,\n      },\n      cmp_gt: {\n        pop_values: [cmp : Int32],\n        push:       true,\n        code:       cmp > 0,\n      },\n      cmp_ge: {\n        pop_values: [cmp : Int32],\n        push:       true,\n        code:       cmp >= 0,\n      },\n      # <<< Comparisons (14)\n\n      # <<< Not (1)\n      logical_not: {\n        pop_values: [value : Bool],\n        push:       true,\n        code:       !value,\n      },\n      # >>> Not (1)\n\n      # <<< Pointers (9)\n      pointer_malloc: {\n        operands:   [element_size : Int32],\n        pop_values: [size : UInt64],\n        push:       true,\n        code:       Pointer(Void).malloc(size * element_size).as(UInt8*),\n      },\n      pointer_realloc: {\n        operands:   [element_size : Int32],\n        pop_values: [pointer : Pointer(UInt8), size : UInt64],\n        push:       true,\n        code:       pointer.realloc(size * element_size),\n      },\n      pointer_set: {\n        operands:   [element_size : Int32],\n        pop_values: [pointer : Pointer(UInt8)],\n        code:       stack_move_to(pointer, element_size),\n      },\n      pointer_get: {\n        operands:   [element_size : Int32],\n        pop_values: [pointer : Pointer(UInt8)],\n        code:       stack_move_from(pointer, element_size),\n      },\n      pointer_new: {\n        pop_values: [type_id : Int32, address : UInt64],\n        push:       true,\n        code:       Pointer(UInt8).new(address),\n      },\n      pointer_address: {\n        pop_values: [pointer : Pointer(UInt8)],\n        push:       true,\n        code:       pointer.address,\n      },\n      pointer_diff: {\n        operands:   [element_size : Int32],\n        pop_values: [pointer1 : Pointer(UInt8), pointer2 : Pointer(UInt8)],\n        push:       true,\n        code: if pointer1.address > pointer2.address\n          (pointer1 - pointer2) // element_size\n        else\n          -((pointer2.address - pointer1.address).to_i64! // element_size)\n        end,\n      },\n      pointer_add: {\n        operands:   [element_size : Int32],\n        pop_values: [pointer : Pointer(UInt8), offset : Int64],\n        push:       true,\n        code:       pointer + (offset * element_size),\n      },\n      pointer_is_null: {\n        pop_values: [pointer : Pointer(UInt8)],\n        push:       true,\n        code:       pointer.null?,\n      },\n      # TODO: maybe remove this and use logical_not\n      pointer_is_not_null: {\n        pop_values: [pointer : Pointer(UInt8)],\n        push:       true,\n        code:       !pointer.null?,\n      },\n      # >>> Pointers (9)\n\n      # <<< Local variables (2)\n      set_local: {\n        operands:   [index : Int32, size : Int32],\n        code:       set_local_var(index, size),\n        # TODO: how to know the local var name?\n        disassemble: {\n          index: case node\n                 when Var\n                   \"#{node}@#{index}\"\n                 when Assign\n                   \"#{node.target}@#{index}\"\n                 else\n                   \"@#{index}\"\n                 end,\n        },\n      },\n      get_local: {\n        operands:   [index : Int32, size : Int32],\n        code:       get_local_var(index, size),\n        # TODO: how to know the local var name?\n        disassemble: {\n          index: case node\n                 when Var\n                   \"#{node}@#{index}\"\n                 when Assign\n                   \"#{node.target}@#{index}\"\n                 else\n                   \"@#{index}\"\n                 end,\n        },\n      },\n      # >>> Local variables (2)\n\n      # <<< Instance vars (4)\n      get_self_ivar: {\n        operands:   [offset : Int32, size : Int32],\n        code:       stack_move_from(self_class_pointer + offset, size),\n      },\n      set_self_ivar: {\n        operands:   [offset : Int32, size : Int32],\n        code:       stack_move_to(self_class_pointer + offset, size),\n      },\n      get_class_ivar: {\n        operands:   [offset : Int32, size : Int32],\n        pop_values: [pointer : Pointer(UInt8)],\n        code:       stack_move_from(pointer + offset, size),\n      },\n      get_struct_ivar: {\n        operands:   [offset : Int32, size : Int32, total_size : Int32],\n        code:       begin\n          # a, b, c\n          # --|_|--\n          (stack - total_size).move_from(stack - total_size + offset, size)\n          stack_shrink_by(total_size - size)\n          stack_grow_by(align(size) - size)\n        end,\n      },\n      # >>> Instance vars (4)\n\n      # <<< Constants (4)\n      const_initialized: {\n        operands:   [index : Int32],\n        push:       true,\n        code:       const_initialized?(index),\n      },\n      get_const: {\n        operands:   [index : Int32, size : Int32],\n        code:       get_const(index, size),\n      },\n      set_const: {\n        operands:   [index : Int32, size : Int32],\n        code:       set_const(index, size),\n      },\n      get_const_pointer: {\n        operands:   [index : Int32],\n        push:       true,\n        code:       get_const_pointer(index),\n      },\n      # >>> Constants (4)\n\n      # <<< Class vars (3)\n      class_var_initialized: {\n        operands:   [index : Int32],\n        push:       true,\n        code:       class_var_initialized?(index),\n      },\n      get_class_var: {\n        operands:   [index : Int32, size : Int32],\n        code:       get_class_var(index, size),\n      },\n      set_class_var: {\n        operands:   [index : Int32, size : Int32],\n        code:       set_class_var(index, size),\n      },\n      # >>> Class vars (3)\n\n      # <<< Stack manipulation (5)\n      pop: {\n        operands:   [size : Int32],\n        code:       stack_shrink_by(size),\n      },\n      # pops size bytes past offset from the stack\n      pop_from_offset: {\n        operands:   [size : Int32, offset : Int32],\n        code:       begin\n          (stack - offset - size).move_from(stack - offset, offset)\n          stack_shrink_by(size)\n        end,\n      },\n      dup: {\n        operands:   [size : Int32],\n        code:       stack_move_from(stack - size, size),\n      },\n      push_zeros: {\n        operands:   [amount : Int32],\n        code:       stack_grow_by(amount),\n      },\n      put_stack_top_pointer: {\n        operands:   [size : Int32],\n        push:       true,\n        code:       stack - size,\n      },\n      # >>> Stack manipulation (5)\n\n      # <<< Jumps (3)\n      branch_if: {\n        operands:   [index : Int32],\n        pop_values: [cond : Bool],\n        code:       (set_ip(index) if cond),\n      },\n      branch_unless: {\n        operands:   [index : Int32],\n        pop_values: [cond : Bool],\n        code:       (set_ip(index) unless cond),\n      },\n      jump: {\n        operands:   [index : Int32],\n        code:       set_ip(index),\n      },\n      # >>> Jumps (3)\n\n      # <<< Pointerof (3)\n      pointerof_var: {\n        operands:   [index : Int32],\n        push:       true,\n        code:       get_local_var_pointer(index),\n      },\n      pointerof_ivar: {\n        operands:   [offset : Int32],\n        push:       true,\n        code:       get_ivar_pointer(offset),\n      },\n      pointerof_class_var: {\n        operands:   [index : Int32],\n        push:       true,\n        code:       get_class_var_pointer(index),\n      },\n      # >>> Pointerof (3)\n\n      # <<< Calls (5)\n      call: {\n        operands:    [compiled_def : CompiledDef],\n        code:        call(compiled_def),\n        disassemble: {\n          compiled_def: \"#{compiled_def.owner}##{compiled_def.def.name}\",\n        },\n      },\n      call_with_block: {\n        operands:    [compiled_def : CompiledDef],\n        code:        call_with_block(compiled_def),\n        disassemble: {\n          compiled_def: \"#{compiled_def.owner}##{compiled_def.def.name}\",\n        },\n      },\n      call_block: {\n        operands:   [compiled_block : CompiledBlock],\n        code:       call_block(compiled_block),\n      },\n      lib_call: {\n        operands:   [lib_function : LibFunction],\n        code:       lib_call(lib_function),\n      },\n      leave: {\n        operands:   [size : Int32],\n        code:       leave(size),\n      },\n      leave_def: {\n        operands:   [size : Int32],\n        code:       leave_def(size),\n      },\n      break_block: {\n        operands:   [size : Int32],\n        code:       break_block(size),\n      },\n      # >>> Calls (4)\n\n      # <<< Classes (2)\n      allocate_class: {\n        operands:   [size : Int32, type_id : Int32],\n        push:       true,\n        code:       begin\n          ptr = Pointer(Void).malloc(size).as(UInt8*)\n          ptr.as(Int32*).value = type_id\n          ptr\n        end,\n      },\n      reset_class: {\n        operands:   [size : Int32, type_id : Int32],\n        pop_values: [pointer : Pointer(UInt8)],\n        push:       true,\n        code:       begin\n          pointer.clear(size)\n          unless type_id == 0\n            # 0 stands for any non-reference type\n            pointer.as(Int32*).value = type_id\n          end\n          pointer\n        end,\n      },\n      put_metaclass: {\n        operands:   [size : Int32, union_type : Bool],\n        push:       true,\n        code:       begin\n          type_id =\n            if union_type\n              (stack - size).as(Int32*).value\n            else\n              (stack - size).as(Void**).value.as(Int32*).value\n            end\n          stack_shrink_by(size)\n          type = context.type_from_id(type_id)\n          context.type_id(type.metaclass)\n        end,\n      },\n      # >>> Allocate (2)\n\n      # <<< Unions (7)\n      put_in_union: {\n        operands:   [type_id : Int32, from_size : Int32, union_size : Int32],\n        code:       begin\n          tmp_stack = stack\n          stack_grow_by(union_size - from_size)\n          (tmp_stack - from_size).move_to(tmp_stack - from_size + type_id_bytesize, from_size)\n          (tmp_stack - from_size).as(Int64*).value = type_id.to_i64!\n        end,\n        disassemble: {\n          type_id: context.type_from_id(type_id),\n        },\n      },\n      put_reference_type_in_union: {\n        operands:   [union_size : Int32],\n        code:       begin\n          # `copy_to` here is valid only when `from_size <= type_id_bytesize`,\n          # which is always true\n          from_size = sizeof(Pointer(UInt8))\n          reference = (stack - from_size).as(UInt8**).value\n          type_id =\n            if reference.null?\n              0\n            else\n              reference.as(Int32*).value\n            end\n\n          tmp_stack = stack\n          stack_grow_by(union_size - from_size)\n          (tmp_stack - from_size).copy_to(tmp_stack - from_size + type_id_bytesize, from_size)\n          (tmp_stack - from_size).as(Int64*).value = type_id.to_i64!\n        end,\n      },\n      # TODO: maybe avoid introducing one instruction per cast\n      put_nilable_type_in_union: {\n        operands:   [union_size : Int32],\n        pop_values: [pointer : Pointer(UInt8)],\n        code:       begin\n          if pointer.null?\n            # All zeros since this is putting nil inside a union\n            stack_grow_by(union_size)\n          else\n            type_id = pointer.as(Int32*).value\n\n            # Put the type id\n            stack_push(type_id)\n\n            # Put the pointer\n            stack_push(pointer)\n\n            # Fill with zeros until we reach union_size\n            remaining = union_size - sizeof(Pointer(Void)) - type_id_bytesize\n            stack_grow_by(remaining) if remaining > 0\n          end\n        end,\n      },\n      remove_from_union: {\n        operands:   [union_size : Int32, from_size : Int32],\n        code:       begin\n          (stack - union_size).move_from(stack - union_size + type_id_bytesize, from_size)\n          stack_shrink_by(union_size - from_size)\n        end,\n      },\n      union_to_bool: {\n        operands:   [union_size : Int32],\n        push:       true,\n        code:       begin\n          type_id = (stack - union_size).as(Int32*).value\n          type = type_from_type_id(type_id)\n\n          value = case type\n                  when NilType\n                    false\n                  when BoolType\n                    # TODO: union type id size\n                    (stack - union_size + 8).as(Bool*).value\n                  when PointerInstanceType\n                    !(stack - union_size + 8).as(UInt8**).value.null?\n                  else\n                    true\n                  end\n          stack_shrink_by(union_size)\n\n          value\n        end,\n      },\n      get_union_type_id: {\n        operands:   [union_size : Int32],\n        push:       true,\n        code:       (stack - union_size).as(Int32*).value,\n      },\n      put_union_type_id: {\n        operands:   [type_id : Int32, union_size : Int32],\n        push:       false,\n        code:       (stack - union_size).as(Int32*).value = type_id,\n      },\n      # >>> Unions (7)\n\n      # <<< is_a? (3)\n      reference_is_a: {\n        operands:   [filter_type_id : Int32],\n        pop_values: [pointer : Pointer(UInt8)],\n        push:       true,\n        code:       begin\n          if pointer.null?\n            false\n          else\n            type_id = pointer.as(Int32*).value\n            type = type_from_type_id(type_id)\n\n            filter_type = type_from_type_id(filter_type_id)\n\n            !!type.filter_by(filter_type)\n          end\n        end,\n        disassemble: {\n          filter_type_id: context.type_from_id(filter_type_id),\n        },\n      },\n      metaclass_is_a: {\n        operands:   [filter_type_id : Int32],\n        pop_values: [metaclass_id : Int32],\n        push:       true,\n        code:       begin\n          type = type_from_type_id(metaclass_id)\n          filter_type = type_from_type_id(filter_type_id)\n          if type.is_a?(VirtualMetaclassType)\n            # This can happen when using `typeof`.\n            # In this case we only match a `Foo+.class` to `Foo.class`,\n            # never to subtypes of `Foo.class`.\n            type.devirtualize == filter_type\n          else\n            !!type.filter_by(filter_type)\n          end\n        end,\n        disassemble: {\n          filter_type_id: context.type_from_id(filter_type_id),\n        },\n      },\n      union_is_a: {\n        operands:   [union_size : Int32, filter_type_id : Int32],\n        push:       true,\n        code:       begin\n          type_id = (stack - union_size).as(Int32*).value\n          type = type_from_type_id(type_id)\n          stack_shrink_by(union_size)\n\n          filter_type = type_from_type_id(filter_type_id)\n\n          !!type.filter_by(filter_type)\n        end,\n        disassemble: {\n          filter_type_id: context.type_from_id(filter_type_id),\n        },\n      },\n      # >>> is_a? (3)\n\n      # <<< Tuples (3)\n      tuple_indexer_known_index: {\n        operands:   [tuple_size : Int32, offset : Int32, value_size : Int32],\n        code:       begin\n          (stack - tuple_size).move_from(stack - tuple_size + offset, value_size)\n          aligned_value_size = align(value_size)\n          stack_shrink_by(tuple_size - value_size)\n          stack_grow_by(aligned_value_size - value_size)\n        end,\n      },\n      copy_from: {\n        operands:   [offset : Int32, size : Int32],\n        code:       stack_move_from(stack - offset, size),\n      },\n      tuple_copy_element: {\n        operands:   [tuple_size : Int32, old_offset : Int32, new_offset : Int32, element_size : Int32],\n        code:       (stack - tuple_size + new_offset).move_from(stack - tuple_size + old_offset, element_size),\n      },\n      # >>> Tuples (3)\n\n      # <<< Symbol (1)\n      symbol_to_s: {\n        pop_values: [index : Int32],\n        push:       true,\n        code:       @context.index_to_symbol(index).object_id.to_u64!,\n      },\n      # >>> Symbol (1)\n\n      # <<< Proc (1)\n      proc_call: {\n        pop_values: [compiled_def : CompiledDef, closure_data : Pointer(Void)],\n        push:       true,\n        code:       begin\n          # Push closure data, if any, as the last call argument\n          stack_push(closure_data) unless closure_data.null?\n\n          call(compiled_def)\n        end\n      },\n      # Turns a Crystal proc into a C function pointer, using libffi's FFI::Closure\n      proc_to_c_fun: {\n        operands:   [ffi_call_interface : FFI::CallInterface],\n        pop_values: [compiled_def : CompiledDef, closure_data : Void*],\n        push:       true,\n        code:       begin\n          # TODO: check that the closure data is not null, otherwise raise\n          ffi_closure = FFI::Closure.new(\n            ffi_call_interface,\n            @context.ffi_closure_fun,\n            @context.ffi_closure_context(self, compiled_def).as(Void*),\n          )\n\n          # Associate the FFI::Closure's code pointer with a CompileDef\n          # in case we need to call it later.\n          # TODO: this probably leaks memory. Figure out what to do here...\n          @context.ffi_closure_to_compiled_def[ffi_closure.to_unsafe] = compiled_def\n\n          ffi_closure.to_unsafe.unsafe_as(Int64)\n        end,\n        disassemble: {\n          ffi_call_interface: \"<ffi_call_interface>\",\n        },\n      },\n      c_fun_to_proc: {\n        pop_values: [ffi_closure_code : Void*],\n        push:       true,\n        code:       begin\n          compiled_def = @context.ffi_closure_to_compiled_def[ffi_closure_code]\n          {Pointer(Void).new(compiled_def.object_id), Pointer(Void).null}\n        end\n      },\n      # >>> Proc (1)\n\n      # <<< Atomic (3)\n      load_atomic: {\n        operands:   [element_size : Int32],\n        pop_values: [ptr : Pointer(UInt8), ordering : Symbol, volatile : Bool],\n        code:       begin\n          # TODO: don't hardcode ordering and volatile\n          # TODO: not tested\n          case element_size\n          when 1\n            i8 = Atomic::Ops.load(ptr, :sequentially_consistent, true)\n            stack_push(i8)\n          when 2\n            i16 = Atomic::Ops.load(ptr.as(Int16*), :sequentially_consistent, true)\n            stack_push(i16)\n          when 4\n            i32 = Atomic::Ops.load(ptr.as(Int32*), :sequentially_consistent, true)\n            stack_push(i32)\n          when 8\n            i64 = Atomic::Ops.load(ptr.as(Int64*), :sequentially_consistent, true)\n            stack_push(i64)\n          else\n            raise \"BUG: unhandled element size for load_atomic instruction: #{element_size}\"\n          end\n        end,\n      },\n      store_atomic: {\n        operands:   [element_size : Int32],\n        pop_values: [ptr : Pointer(UInt8), value : UInt64, ordering : Symbol, volatile : Bool],\n        code:       begin\n          # TODO: don't hardcode ordering and volatile\n          # TODO: not tested\n          case element_size\n          when 1\n            i8 = Atomic::Ops.store(ptr, value.to_u8!, :sequentially_consistent, true)\n            stack_push(i8)\n          when 2\n            i16 = Atomic::Ops.store(ptr.as(Int16*), value.to_i16!, :sequentially_consistent, true)\n            stack_push(i16)\n          when 4\n            i32 = Atomic::Ops.store(ptr.as(Int32*), value.to_i32!, :sequentially_consistent, true)\n            stack_push(i32)\n          when 8\n            i64 = Atomic::Ops.store(ptr.as(Int64*), value.to_i64!, :sequentially_consistent, true)\n            stack_push(i64)\n          else\n            raise \"BUG: unhandled element size for store_atomic instruction: #{element_size}\"\n          end\n        end,\n      },\n      atomicrmw: {\n        operands:   [element_size : Int32],\n        pop_values: [op_i : Int32, ptr : Pointer(UInt8), value : UInt64, ordering : Symbol, singlethread : Bool],\n        code:       begin\n          # TODO: don't hardcode ordering\n          # TODO: not tested\n          # TODO: optimize, don't case over string\n          op = @context.index_to_symbol(op_i)\n          case op\n          when \"add\"  then atomicrmw_op(:add)\n          when \"sub\"  then atomicrmw_op(:sub)\n          when \"and\"  then atomicrmw_op(:and)\n          when \"nand\" then atomicrmw_op(:nand)\n          when \"or\"   then atomicrmw_op(:or)\n          when \"xor\"  then atomicrmw_op(:xor)\n          when \"max\"  then atomicrmw_op(:max)\n          when \"umax\" then atomicrmw_op(:umax)\n          when \"min\"  then atomicrmw_op(:min)\n          when \"umin\" then atomicrmw_op(:umin)\n          when \"xchg\" then atomicrmw_op(:xchg)\n          else\n            raise \"BUG: missing atomicrmw #{op}\"\n          end\n        end,\n      },\n      cmpxchg: {\n        operands:   [element_size : Int32],\n        pop_values: [ptr : Pointer(UInt8), cmp : UInt64, new : UInt64, success_ordering : Symbol, failure_ordering : Symbol],\n        code:       begin\n          # TODO: don't assume ordering is :sequentially_consistent\n          # TODO: not tested\n          case element_size\n          when 1\n            i8 = Atomic::Ops.cmpxchg(ptr, cmp.to_u8!, new.to_u8!, :sequentially_consistent, :sequentially_consistent)\n            stack_push(i8)\n          when 2\n            i16 = Atomic::Ops.cmpxchg(ptr.as(Int16*), cmp.to_i16!, new.to_i16!, :sequentially_consistent, :sequentially_consistent)\n            stack_push(i16)\n          when 4\n            i32 = Atomic::Ops.cmpxchg(ptr.as(Int32*), cmp.to_i32!, new.to_i32!, :sequentially_consistent, :sequentially_consistent)\n            stack_push(i32)\n          when 8\n            i64 = Atomic::Ops.cmpxchg(ptr.as(Int64*), cmp.to_i64!, new.to_i64!, :sequentially_consistent, :sequentially_consistent)\n            stack_push(i64)\n          else\n            raise \"BUG: unhandled element size for cmpxchg instruction: #{element_size}\"\n          end\n        end,\n      },\n      # >>> Proc (3)\n\n      # <<< ARGV (2)\n      argc_unsafe: {\n        push:       true,\n        code:       argc_unsafe,\n      },\n      argv_unsafe: {\n        push:       true,\n        code:       argv_unsafe,\n      },\n      # >>> ARGV (2)\n\n      # <<< Overrides (6)\n      interpreter_proc_new: {\n        operands:   [proc_type_id : Int32],\n        pop_values: [pointer : Void*, closure_data : Void*],\n        push:       true,\n        code:       begin\n          if @context.compiled_procs.includes?(pointer.address)\n            {pointer, closure_data}\n          else\n            raise \"BUG: closure_data not nil but pointer is not a CompiledDef\" if closure_data\n\n            proc_type = type_from_type_id(proc_type_id).as(ProcInstanceType)\n            compiled_def = @context.extern_proc_wrapper(proc_type, pointer)\n\n            {compiled_def.as(Void*), Pointer(Void).null}\n          end\n        end,\n      },\n      interpreter_call_stack_unwind: {\n        push:       true,\n        code:       backtrace,\n      },\n      interpreter_raise_without_backtrace: {\n        pop_values: [exception : UInt8*],\n        code:       raise_exception(exception),\n      },\n      throw: {\n        code:       throw,\n      },\n      interpreter_current_fiber: {\n        push:       true,\n        code:       Fiber.current.as(Void*),\n      },\n      interpreter_spawn: {\n        pop_values: [fiber : Void*, fiber_main : Void*],\n        push:       true,\n        code:       spawn_interpreter(fiber, fiber_main),\n      },\n      interpreter_fiber_swapcontext: {\n        pop_values: [current_context : Void*, new_context : Void*],\n        code:       swapcontext(current_context, new_context),\n      },\n      interpreter_fiber_resumable: {\n        pop_values: [context : Void*],\n        push:       true,\n        code:       fiber_resumable(context),\n      },\n\n      interpreter_signal_descriptor: {\n        pop_values: [fd : Int32],\n        code:       signal_descriptor(fd),\n      },\n      interpreter_signal: {\n        pop_values: [signum : Int32, handler : Int32],\n        code:       signal(signum, handler),\n      },\n\n      {% if flag?(:bits64) %}\n        interpreter_intrinsics_memcpy: {\n          pop_values: [dest : Pointer(Void), src : Pointer(Void), len : UInt64, is_volatile : Bool],\n          code:       begin\n            # This is a pretty weird `if`, but the `memcpy` intrinsic requires the last argument to be a constant\n            if is_volatile\n              LibIntrinsics.memcpy(dest, src, len, true)\n            else\n              LibIntrinsics.memcpy(dest, src, len, false)\n            end\n          end,\n        },\n        interpreter_intrinsics_memmove: {\n          pop_values: [dest : Pointer(Void), src : Pointer(Void), len : UInt64, is_volatile : Bool],\n          code:       begin\n            # This is a pretty weird `if`, but the `memmove` intrinsic requires the last argument to be a constant\n            if is_volatile\n              LibIntrinsics.memmove(dest, src, len, true)\n            else\n              LibIntrinsics.memmove(dest, src, len, false)\n            end\n          end,\n        },\n        interpreter_intrinsics_memset: {\n          pop_values: [dest : Pointer(Void), val : UInt8, len : UInt64, is_volatile : Bool],\n          code:       begin\n            # This is a pretty weird `if`, but the `memset` intrinsic requires the last argument to be a constant\n            if is_volatile\n              LibIntrinsics.memset(dest, val, len, true)\n            else\n              LibIntrinsics.memset(dest, val, len, false)\n            end\n          end,\n        },\n      {% else %}\n        interpreter_intrinsics_memcpy: {\n          pop_values: [dest : Pointer(Void), src : Pointer(Void), len : UInt32, is_volatile : Bool],\n          code:       begin\n            # This is a pretty weird `if`, but the `memcpy` intrinsic requires the last argument to be a constant\n            if is_volatile\n              LibIntrinsics.memcpy(dest, src, len, true)\n            else\n              LibIntrinsics.memcpy(dest, src, len, false)\n            end\n          end,\n        },\n        interpreter_intrinsics_memmove: {\n          pop_values: [dest : Pointer(Void), src : Pointer(Void), len : UInt32, is_volatile : Bool],\n          code:       begin\n            # This is a pretty weird `if`, but the `memmove` intrinsic requires the last argument to be a constant\n            if is_volatile\n              LibIntrinsics.memmove(dest, src, len, true)\n            else\n              LibIntrinsics.memmove(dest, src, len, false)\n            end\n          end,\n        },\n        interpreter_intrinsics_memset: {\n          pop_values: [dest : Pointer(Void), val : UInt8, len : UInt32, is_volatile : Bool],\n          code:       begin\n            # This is a pretty weird `if`, but the `memset` intrinsic requires the last argument to be a constant\n            if is_volatile\n              LibIntrinsics.memset(dest, val, len, true)\n            else\n              LibIntrinsics.memset(dest, val, len, false)\n            end\n          end,\n        },\n      {% end %}\n\n      interpreter_intrinsics_debugtrap: {\n        code:       pry,\n      },\n\n      {% if flag?(:i386) || flag?(:x86_64) %}\n        interpreter_intrinsics_pause: {\n          code:       LibIntrinsics.pause,\n        },\n      {% end %}\n\n      interpreter_intrinsics_read_cycle_counter: {\n        push:       true,\n        code:       LibIntrinsics.read_cycle_counter,\n      },\n\n      {% for n in [8, 16, 32, 64, 128] %}\n        interpreter_intrinsics_bitreverse{{n}}: {\n          pop_values: [value : UInt{{n}}],\n          push:       true,\n          code:       LibIntrinsics.bitreverse{{n}}(value),\n        },\n        {% unless n == 8 %}\n          interpreter_intrinsics_bswap{{n}}: {\n            pop_values: [value : UInt{{n}}],\n            push:       true,\n            code:       LibIntrinsics.bswap{{n}}(value),\n          },\n        {% end %}\n        interpreter_intrinsics_popcount{{n}}: {\n          pop_values: [value : Int{{n}}],\n          push:       true,\n          code:       LibIntrinsics.popcount{{n}}(value),\n        },\n        interpreter_intrinsics_countleading{{n}}: {\n          pop_values: [src : Int{{n}}, zero_is_undef : Bool],\n          push:       true,\n          code:       begin\n            if zero_is_undef\n              LibIntrinsics.countleading{{n}}(src, true)\n            else\n              LibIntrinsics.countleading{{n}}(src, false)\n            end\n          end,\n        },\n        interpreter_intrinsics_counttrailing{{n}}: {\n          pop_values: [src : Int{{n}}, zero_is_undef : Bool],\n          push:       true,\n          code:       begin\n            if zero_is_undef\n              LibIntrinsics.counttrailing{{n}}(src, true)\n            else\n              LibIntrinsics.counttrailing{{n}}(src, false)\n            end\n          end,\n        },\n        interpreter_intrinsics_fshl{{n}}: {\n          pop_values: [a : UInt{{n}}, b : UInt{{n}}, count : UInt{{n}}],\n          push:       true,\n          code:       LibIntrinsics.fshl{{n}}(a, b, count),\n        },\n        interpreter_intrinsics_fshr{{n}}: {\n          pop_values: [a : UInt{{n}}, b : UInt{{n}}, count : UInt{{n}}],\n          push:       true,\n          code:       LibIntrinsics.fshr{{n}}(a, b, count),\n        },\n      {% end %}\n\n      libm_ceil_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.ceil_f32(value),\n      },\n      libm_ceil_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.ceil_f64(value),\n      },\n      libm_cos_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.cos_f32(value),\n      },\n      libm_cos_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.cos_f64(value),\n      },\n      libm_exp_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.exp_f32(value),\n      },\n      libm_exp_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.exp_f64(value),\n      },\n      libm_exp2_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.exp2_f32(value),\n      },\n      libm_exp2_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.exp2_f64(value),\n      },\n      libm_floor_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.floor_f32(value),\n      },\n      libm_floor_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.floor_f64(value),\n      },\n      libm_fma_f32: {\n        pop_values: [value1 : Float32, value2 : Float32, value3 : Float32],\n        push:       true,\n        code:       LibM.fma_f32(value1, value2, value3),\n      },\n      libm_fma_f64: {\n        pop_values: [value1 : Float64, value2 : Float64, value3 : Float64],\n        push:       true,\n        code:       LibM.fma_f64(value1, value2, value3),\n      },\n      libm_log_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.log_f32(value),\n      },\n      libm_log_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.log_f64(value),\n      },\n      libm_log2_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.log2_f32(value),\n      },\n      libm_log2_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.log2_f64(value),\n      },\n      libm_log10_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.log10_f32(value),\n      },\n      libm_log10_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.log10_f64(value),\n      },\n      libm_round_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.round_f32(value),\n      },\n      libm_round_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.round_f64(value),\n      },\n      libm_rint_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.rint_f32(value),\n      },\n      libm_rint_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.rint_f64(value),\n      },\n      libm_sin_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.sin_f32(value),\n      },\n      libm_sin_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.sin_f64(value),\n      },\n      libm_sqrt_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.sqrt_f32(value),\n      },\n      libm_sqrt_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.sqrt_f64(value),\n      },\n      libm_trunc_f32: {\n        pop_values: [value : Float32],\n        push:       true,\n        code:       LibM.trunc_f32(value),\n      },\n      libm_trunc_f64: {\n        pop_values: [value : Float64],\n        push:       true,\n        code:       LibM.trunc_f64(value),\n      },\n      libm_powi_f32: {\n        pop_values: [value : Float32, power : Int32],\n        push:       true,\n        code:       {% if flag?(:win32) %} LibM.pow_f32(value, power.to_f32) {% else %} LibM.powi_f32(value, power) {% end %},\n      },\n      libm_powi_f64: {\n        pop_values: [value : Float64, power : Int32],\n        push:       true,\n        code:       {% if flag?(:win32) %} LibM.pow_f64(value, power.to_f64) {% else %} LibM.powi_f64(value, power) {% end %},\n      },\n      libm_min_f32: {\n        pop_values: [value1 : Float32, value2 : Float32],\n        push:       true,\n        code:       LibM.min_f32(value1, value2),\n      },\n      libm_min_f64: {\n        pop_values: [value1 : Float64, value2 : Float64],\n        push:       true,\n        code:       LibM.min_f64(value1, value2),\n      },\n      libm_max_f32: {\n        pop_values: [value1 : Float32, value2 : Float32],\n        push:       true,\n        code:       LibM.max_f32(value1, value2),\n      },\n      libm_max_f64: {\n        pop_values: [value1 : Float64, value2 : Float64],\n        push:       true,\n        code:       LibM.max_f64(value1, value2),\n      },\n      libm_pow_f32: {\n        pop_values: [value : Float32, power : Float32],\n        push:       true,\n        code:       LibM.pow_f32(value, power),\n      },\n      libm_pow_f64: {\n        pop_values: [value : Float64, power : Float64],\n        push:       true,\n        code:       LibM.pow_f64(value, power),\n      },\n      libm_copysign_f32: {\n        pop_values: [magnitude : Float32, sign : Float32],\n        push:       true,\n        code:       LibM.copysign_f32(magnitude, sign),\n      },\n      libm_copysign_f64: {\n        pop_values: [magnitude : Float64, sign : Float64],\n        push:       true,\n        code:       LibM.copysign_f64(magnitude, sign),\n      },\n      unreachable: {\n        operands:   [message : String],\n        code:       raise message,\n      },\n      # >>> Overrides (6)\n\n    }\n{% end %}\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/interpreter.cr",
    "content": "require \"./repl\"\nrequire \"../ffi\"\nrequire \"colorize\"\nrequire \"../../../crystal/syntax_highlighter/colorize\"\n\n# The ones that understands Crystal bytecode.\nclass Crystal::Repl::Interpreter\n  record CallFrame,\n    # The CompiledDef related to this call frame\n    compiled_def : CompiledDef,\n    # The CompiledBlock related to this call frame, if any\n    compiled_block : CompiledBlock?,\n    # Instructions for this frame\n    instructions : CompiledInstructions,\n    # The pointer to the current instruction for this call frame.\n    # This value changes as the program goes, and when a call is made\n    # this value is useful to know where we need to continue after\n    # the call returns.\n    ip : Pointer(UInt8),\n    # What's the frame's stack.\n    # This value changes as the program goes, and when a call is made\n    # this value is useful to know what values in the stack we need\n    # to have when the call returns.\n    stack : Pointer(UInt8),\n    # What's the frame's stack bottom. After this position come the\n    # def's local variables.\n    stack_bottom : Pointer(UInt8),\n    # The index of the frame that called a block.\n    # This is useful to know because when a `yield` happens,\n    # we more or less create a new stack frame that has the same\n    # local variables as this frame, because the block will need\n    # to access that frame's variables.\n    # It's -1 if the value is not present.\n    block_caller_frame_index : Int32,\n    # When a `yield` happens we copy the frame pointed by\n    # `block_caller_frame_index`. If a `return` happens inside\n    # that block we need to return from that frame (the `def`s one.)\n    # With `real_frame_index` we know where that frame is actually\n    # in the call stack (the original, not the copy) and we can\n    # go back to just before that frame when a `return` happens.\n    real_frame_index : Int32,\n    # This is a bit hacky, but...\n    # Right now, when we produce an OverflowError we do it directly\n    # in the instruction that potentially produces the overflow.\n    # When we do that, the backtrace that is produced in that case\n    # is incorrect because the backtrace computing logic assumes\n    # an all call frames are, well, calls. But in this case it could\n    # be an `add_i32` instruction which has a different layout.\n    # To solve this, we have an optional `overflow_offset` that\n    # we put on a frame to indicate that the caller frame must\n    # add to get the correct backtrace location.\n    overflow_offset : Int32\n\n  getter context : Context\n\n  # Are we in pry mode?\n  getter? pry : Bool = false\n\n  # What's the last node we went over pry? This is useful to know\n  # because when doing `next` or `step` we want to stop at a node\n  # that has a different file/line number than this node\n  @pry_node : ASTNode?\n\n  # What's the maximum call stack frame index we want to stop at\n  # when doing `next`, `step` or `finish`:\n  # - when doing `next`, we want to continue in the same frame, or\n  #   the one above us (we don't want to go deeper)\n  # - when doing `step`, there's no maximum frame\n  # - when doing `finish`, we'd like to exit the current frame\n  @pry_max_target_frame : Int32?\n\n  # The input reader for the pry interface, it's stored here notably to hold the history.\n  @pry_reader : PryReader\n\n  # The set of local variables for interpreting code.\n  getter local_vars : LocalVars\n\n  # Memory for the stack.\n  getter stack : Pointer(UInt8)\n\n  # Values for `argv`, set when using `crystal i file.cr arg1 arg2 ...`.\n  property argv : Array(String)\n\n  SMALL_RETURN_VALUE_MAX_SIZE = 48\n\n  # This is a value that's being returned from inside a block.\n  # The tricky part here is that we need to return from a method, but we also\n  # need to execute any ensures. So, we store the returned value in this struct\n  # and start going up the call stack.\n  # `target_frame_index` is the frame index where we must stop.\n  alias ReturnedValue = SmallReturnedValue | BigReturnedValue\n\n  # This is a returned value that fits in 48 bytes.\n  # Most return values will fit here, so we avoid allocating memory for this.\n  record SmallReturnedValue, value : StaticArray(UInt8, SMALL_RETURN_VALUE_MAX_SIZE), size : Int32, target_frame_index : Int32 do\n    def pointer\n      @value.to_unsafe\n    end\n  end\n\n  # When the return value is bigger than 48 bytes we use this struct.\n  record BigReturnedValue, pointer : Pointer(UInt8), size : Int32, target_frame_index : Int32\n\n  # This is an exception that's being raised.\n  # Here we also need to go up the call stack.\n  record RaisedException, exception : Pointer(UInt8)\n\n  # An alias for either a returned value or a raised exception.\n  # When an ensure handler is executed because an exception was raised\n  # or because a value was returned, we abstract any of those in this type.\n  alias ThrowValue = ReturnedValue | RaisedException\n\n  def initialize(\n    @context : Context,\n    # TODO: what if the stack is exhausted?\n    @stack : UInt8* = Pointer(Void).malloc(8 * 1024 * 1024).as(UInt8*),\n  )\n    @local_vars = LocalVars.new(@context)\n    @argv = [] of String\n\n    @instructions = CompiledInstructions.new\n\n    @call_stack = [] of CallFrame\n    @call_stack_leave_index = 0\n\n    @block_level = 0\n\n    @compiled_def = nil\n\n    @pry_reader = PryReader.new\n    @pry_reader.color = @context.program.color?\n  end\n\n  def self.new(interpreter : Interpreter, compiled_def : CompiledDef, stack : Pointer(UInt8), block_level : Int32)\n    new(interpreter, compiled_def, compiled_def.local_vars, compiled_def.closure_context, stack, block_level)\n  end\n\n  def initialize(interpreter : Interpreter, compiled_def : CompiledDef, local_vars : LocalVars, @closure_context : ClosureContext?, stack : Pointer(UInt8), @block_level : Int32)\n    @context = interpreter.context\n    @local_vars = local_vars.dup\n    @argv = interpreter.@argv\n\n    @instructions = CompiledInstructions.new\n\n    @stack = stack\n    @call_stack = interpreter.@call_stack.dup\n    @call_stack_leave_index = @call_stack.size\n\n    @compiled_def = compiled_def\n\n    @pry_reader = PryReader.new\n    @pry_reader.color = @context.program.color?\n  end\n\n  # Interprets the give node under the given context.\n  # Yields the interpreter stack to potentially fill out any values in\n  # it before execution.\n  def self.interpret(context : Context, node : ASTNode, & : UInt8* -> _) : Repl::Value\n    context.checkout_stack do |stack|\n      interpreter = Interpreter.new(context, stack)\n\n      yield stack\n\n      main_visitor = MainVisitor.new(context.program, meta_vars: MetaVars.new)\n\n      node = context.program.normalize(node)\n      node = context.program.semantic(node, main_visitor: main_visitor)\n\n      interpreter.interpret(node, main_visitor.meta_vars)\n    end\n  end\n\n  # compiles the given code to bytecode, then interprets it by assuming the local variables\n  # are defined in `meta_vars`.\n  def interpret(node : ASTNode, meta_vars : MetaVars, scope : Type? = nil, in_pry : Bool = false) : Value\n    compiled_def = @compiled_def\n\n    # Declare or migrate local variables\n    # TODO: we should also migrate variables if we are outside of a block\n    # in a pry session, but that's tricky so we'll leave it for later.\n    if !compiled_def || in_pry\n      migrate_local_vars(@local_vars, meta_vars) if @local_vars.block_level == 0\n\n      # TODO: is it okay to assume this is always the program? Probably not.\n      # Check if we need a local variable for the closure context\n      if !in_pry && @context.program.vars.try &.any? { |name, var| var.type? && var.closure_in?(@context.program) }\n        # The closure context is always a pointer to some memory\n        @local_vars.declare(Closure::VAR_NAME, @context.program.pointer_of(@context.program.void))\n      end\n\n      meta_vars.each do |name, meta_var|\n        meta_var_type = meta_var.type?\n\n        # A meta var might end up without a type if it's assigned a value\n        # in a branch that's never executed/typed, and never read afterwards\n        next unless meta_var_type\n\n        # Closured vars don't belong in the local variables table\n        next if meta_var.closured?\n\n        # Check if the var already exists from the current block upwards\n        existing_type = nil\n        @local_vars.block_level.downto(0) do |level|\n          existing_type = @local_vars.type?(name, level)\n          break if existing_type\n        end\n\n        if existing_type\n          if existing_type != meta_var.type\n            raise \"BUG: can't change type of local variable #{name} from #{existing_type} to #{meta_var.type} yet\"\n          end\n        else\n          @local_vars.declare(name, meta_var_type)\n        end\n      end\n    end\n\n    finished_hooks = @context.program.finished_hooks.dup\n    @context.program.finished_hooks.clear\n\n    # TODO: top_level or not\n    compiler =\n      if compiled_def\n        Compiler.new(@context, @local_vars, scope: scope || compiled_def.owner, def: compiled_def.def)\n      elsif scope\n        Compiler.new(@context, @local_vars, scope: scope)\n      else\n        Compiler.new(@context, @local_vars)\n      end\n    compiler.block_level = @block_level\n    compiler.closure_context = @closure_context\n    compiler.compile(node)\n\n    @instructions = compiler.instructions\n\n    {% if Debug::DECOMPILE %}\n      if compiled_def\n        puts \"=== #{compiled_def.owner}##{compiled_def.def.name} ===\"\n      else\n        puts \"=== top-level ===\"\n      end\n      puts @local_vars\n      puts Disassembler.disassemble(@context, @instructions, @local_vars)\n\n      if compiled_def\n        puts \"=== #{compiled_def.owner}##{compiled_def.def.name} ===\"\n      else\n        puts \"=== top-level ===\"\n      end\n    {% end %}\n\n    value = interpret(node, node.type)\n\n    finished_hooks.each do |finished_hook|\n      interpret(finished_hook.node, meta_vars, finished_hook.scope.metaclass)\n    end\n\n    value\n  end\n\n  private def interpret(node : ASTNode, node_type : Type) : Value\n    # The stack is used like this:\n    #\n    # [.........., ...........]\n    # ^----------^ ^----------^\n    #  local vars   other data\n    #\n    # That is, there's a space right at the beginning where local variables\n    # are stored (local variables live in the stack.)\n\n    # This is the true beginning of the stack, and a reference to where local\n    # variables for the current call frame begin.\n    stack_bottom = @stack\n\n    # Shift stack to leave room for local vars.\n    # Previous runs that wrote to local vars would have those values\n    # written to @stack already (or property migrated thanks to `migrate_local_vars`)\n    stack_bottom_after_local_vars = stack_bottom + @local_vars.max_bytesize\n    stack = stack_bottom_after_local_vars\n\n    # Reserve space for constants (there might be new constants now)\n    @context.constants_memory = @context.constants_memory.realloc(@context.constants.bytesize)\n\n    # Reserve space for class vars (there might be new class vars now)\n    @context.class_vars_memory = @context.class_vars_memory.realloc(@context.class_vars.bytesize)\n\n    # Class variables that don't have an initializer are trivially initialized (with `nil`)\n    @context.class_vars.each_initialized_index do |index|\n      @context.class_vars_memory[index] = 1_u8\n    end\n\n    instructions : CompiledInstructions = @instructions\n    ip = instructions.instructions.to_unsafe\n    return_value = Pointer(UInt8).null\n\n    compiled_def = @compiled_def\n    if compiled_def\n      a_def = compiled_def.def\n    else\n      a_def = Def.new(\"<top-level>\", body: node)\n      a_def.owner = program\n      a_def.vars = program.vars\n    end\n\n    # Push an initial call frame\n    @call_stack << CallFrame.new(\n      compiled_def: CompiledDef.new(\n        context: @context,\n        def: a_def,\n        owner: compiled_def.try(&.owner) || a_def.owner,\n        args_bytesize: 0,\n        instructions: instructions,\n        local_vars: @local_vars,\n      ),\n      compiled_block: nil,\n      instructions: instructions,\n      ip: ip,\n      stack: stack,\n      stack_bottom: stack_bottom,\n      block_caller_frame_index: -1,\n      real_frame_index: 0,\n      overflow_offset: 0,\n    )\n\n    while true\n      {% if Debug::TRACE %}\n        puts \"-\" * 80\n\n        call_frame = @call_stack.last\n        a_def = call_frame.compiled_def.def\n        offset = (ip - instructions.instructions.to_unsafe).to_i32\n        puts \"In: #{a_def.owner}##{a_def.name}\"\n        node = instructions.nodes[offset]?\n        puts \"Node: #{node}\" if node\n        puts Slice.new(@stack, stack - @stack).hexdump\n\n        Disassembler.disassemble_one(@context, instructions, offset, current_local_vars, STDOUT)\n        puts\n      {% end %}\n\n      if @pry\n        pry_max_target_frame = @pry_max_target_frame\n        if !pry_max_target_frame || @call_stack.last.real_frame_index <= pry_max_target_frame\n          pry(ip, instructions, stack_bottom, stack)\n        end\n      end\n\n      # This is the main interpreter logic:\n      # 1. Read the next opcode\n      op_code = next_instruction OpCode\n\n      # 2. Do something depending on the opcode.\n      #    The code for each opcode is defined in Crystal::Repl::Instructions\n      {% begin %}\n        case op_code\n          {% for name, instruction in Crystal::Repl::Instructions %}\n            {% operands = instruction[:operands] || [] of Nil %}\n            {% pop_values = instruction[:pop_values] || [] of Nil %}\n\n            in .{{name.id}}?\n              # Read operands for this instruction\n              {% for operand in operands %}\n                {{operand.var}} = next_instruction {{operand.type}}\n              {% end %}\n\n              # Pop any values\n              {% for pop_value, i in pop_values %}\n                {% pop = pop_values[pop_values.size - i - 1] %}\n                {{ pop.var }} = stack_pop({{pop.type}})\n              {% end %}\n\n              begin\n                {% if instruction[:overflow] %}\n                  {{ \"begin\".id }}\n                {% end %}\n\n                # Execute the instruction and push the value to the stack, if needed\n                {% if instruction[:push] %}\n                  stack_push({{instruction[:code]}})\n                {% else %}\n                  {{instruction[:code]}}\n                {% end %}\n\n                {% if instruction[:overflow] %}\n                  {{ \"rescue OverflowError\".id }}\n                    # Compute the overflow offset to make it look like a call\n                    overflow_offset = 0\n                    {% for operand in operands %}\n                      overflow_offset -= sizeof({{operand.type}})\n                    {% end %}\n                    overflow_offset += sizeof(Void*)\n\n                    # On overflow, directly call __crystal_raise_overflow\n                    call(@context.crystal_raise_overflow_compiled_def, overflow_offset: overflow_offset)\n                  {{ \"end\".id }}\n                {% end %}\n              rescue escaping_exception : EscapingException\n                raise escaping_exception\n              rescue exception : Exception\n                {% for operand in operands %}\n                  ip -= sizeof({{operand.type}})\n                {% end %}\n                ip -= sizeof(OpCode)\n\n                call_frame = @call_stack.last\n                a_def = call_frame.compiled_def.def\n                offset = (ip - instructions.instructions.to_unsafe).to_i32\n                puts \"In: #{a_def.owner}##{a_def.name}\"\n                node = instructions.nodes[offset]?\n                puts \"Node: #{node}\" if node\n\n                raise exception\n              end\n          {% end %}\n        end\n      {% end %}\n\n      {% if Debug::TRACE %}\n        puts Slice.new(@stack, stack - @stack).hexdump\n      {% end %}\n    end\n\n    if stack != stack_bottom_after_local_vars\n      raise \"BUG: data left on stack (#{stack - stack_bottom_after_local_vars} bytes): #{Slice.new(@stack, stack - @stack)}\"\n    end\n\n    Value.new(self, return_value, node_type)\n  end\n\n  private def migrate_local_vars(current_local_vars, next_meta_vars)\n    # Check if any existing local variable type changed.\n    # If so, it means we need to put them inside a union,\n    # or make the union bigger.\n    current_names = current_local_vars.names_at_block_level_zero\n\n    needs_migration = current_names.any? do |current_name|\n      next_meta_var = next_meta_vars[current_name]?\n\n      # This can happen because the interpreter might declare temporary variables\n      # exclusive to it, not visible to the semantic phase (MainVisitor), specifically\n      # in `Compiler#assign_to_temporary_and_return_pointer`. In that case there's\n      # nothing to migrate.\n      next unless next_meta_var\n\n      current_type = current_local_vars.type(current_name, 0)\n      next_type = next_meta_vars[current_name].type\n      current_type != next_type\n    end\n\n    return unless needs_migration\n\n    # Always start with fresh variables, because union types might have changed\n    @local_vars = LocalVars.new(@context)\n\n    current_memory = Pointer(UInt8).malloc(current_local_vars.current_bytesize)\n    @stack.copy_to(current_memory, current_local_vars.current_bytesize)\n\n    stack = @stack\n    current_names.each do |current_name|\n      current_type = current_local_vars.type(current_name, 0)\n      next_meta_var = next_meta_vars[current_name]?\n\n      # Same as before: the next meta var might not exist.\n      # In that case, to simplify things, we make it so the next type\n      # is the same as the current type, which means \"doesn't need a migration\"\n      next_type = next_meta_var.try(&.type) || current_type\n\n      current_type_size = aligned_sizeof_type(current_type)\n      next_type_size = aligned_sizeof_type(next_type)\n\n      if current_type_size == next_type_size\n        # Doesn't need a migration, so we copy it as-is\n        stack.copy_from(current_memory, current_type_size)\n      else\n        # Needs a migration\n        case next_type\n        when MixedUnionType\n          case current_type\n          when PrimitiveType, NonGenericClassType, GenericClassInstanceType\n            stack.as(Int32*).value = type_id(current_type)\n            (stack + type_id_bytesize).copy_from(current_memory, current_type_size)\n          when ReferenceUnionType, NilableReferenceUnionType, VirtualType\n            reference = stack.as(UInt8**).value\n            if reference.null?\n              stack.clear(next_type_size)\n            else\n              stack.as(Int32*).value = reference.as(Int32*).value\n              (stack + type_id_bytesize).copy_from(current_memory, current_type_size)\n            end\n          when MixedUnionType\n            # Copy the union type id\n            stack.as(Int32*).value = current_memory.as(Int32*).value\n\n            # Copy the value\n            (stack + type_id_bytesize).copy_from(current_memory + type_id_bytesize, current_type_size)\n          else\n            # There might not be other cases to handle, but just in case...\n            raise \"BUG: missing local var migration from #{current_type} to #{next_type} (#{current_type.class} to #{next_type.class})\"\n          end\n        else\n          # I don't this a migration is ever needed unless the target type is a MixedUnionType,\n          # but just in case...\n          raise \"BUG: missing local var migration from #{current_type} to #{next_type}\"\n        end\n      end\n\n      stack += next_type_size\n      current_memory += current_type_size\n    end\n  end\n\n  private def current_local_vars\n    if call_frame = @call_stack.last?\n      call_frame.compiled_def.local_vars\n    else\n      @local_vars\n    end\n  end\n\n  # All of these are helper functions called from the interpreter\n  # or from Crystal::Repl::Instructions.\n  #\n  # Most of these are macros because the stack, instructions, etc.\n  # are all local variables inside the interpreter loop.\n  #\n  # TODO: I'm not sure all of these need to be local variables.\n  # I think for example Ruby invokes a different function per opcode and\n  # passes some data to these functions, for example the stack pointer, etc.,\n  # not sure.\n\n  private macro call(compiled_def,\n                     block_caller_frame_index = -1,\n                     overflow_offset = 0)\n    # At the point of a call like:\n    #\n    #     foo(x, y)\n    #\n    # x and y will already be in the stack, ready to be used\n    # as the function arguments in the target def.\n    #\n    # After the call, we want the stack to be at the point\n    # where it doesn't have the call args, ready to push\n    # return call's return value.\n    %stack_before_call_args = stack - {{compiled_def}}.args_bytesize\n\n    # Clear the portion after the call args and up to the def local vars\n    # because it might contain garbage data from previous block calls or\n    # method calls.\n    %size_to_clear = {{compiled_def}}.local_vars.max_bytesize - {{compiled_def}}.args_bytesize\n    if %size_to_clear < 0\n      raise \"OH NO, size to clear DEF is: #{ %size_to_clear }\"\n    end\n\n    stack.clear(%size_to_clear)\n\n    @call_stack[-1] = @call_stack.last.copy_with(\n      ip: ip,\n      stack: %stack_before_call_args,\n    )\n\n    %call_frame = CallFrame.new(\n      compiled_def: {{compiled_def}},\n      compiled_block: nil,\n      instructions: {{compiled_def}}.instructions,\n      ip: {{compiled_def}}.instructions.instructions.to_unsafe,\n      # We need to adjust the call stack to start right\n      # after the target def's local variables.\n      stack: %stack_before_call_args + {{compiled_def}}.local_vars.max_bytesize,\n      stack_bottom: %stack_before_call_args,\n      block_caller_frame_index: {{block_caller_frame_index}},\n      real_frame_index: @call_stack.size,\n      overflow_offset: {{overflow_offset}},\n    )\n\n    @call_stack << %call_frame\n\n    instructions = %call_frame.compiled_def.instructions\n    ip = %call_frame.ip\n    stack = %call_frame.stack\n    stack_bottom = %call_frame.stack_bottom\n  end\n\n  private macro call_with_block(compiled_def)\n    call({{compiled_def}}, block_caller_frame_index: @call_stack.size - 1)\n  end\n\n  private macro call_block(compiled_block)\n    # At this point the stack has the yield expressions, so after the call\n    # we must go back to before the yield expressions\n    %stack_before_call_args = stack - {{compiled_block}}.args_bytesize\n    @call_stack[-1] = @call_stack.last.copy_with(\n      ip: ip,\n      stack: %stack_before_call_args,\n    )\n\n    %block_caller_frame_index = @call_stack.last.block_caller_frame_index\n\n    copied_call_frame = @call_stack[%block_caller_frame_index].copy_with(\n      compiled_block: {{compiled_block}},\n      instructions: {{compiled_block}}.instructions,\n      ip: {{compiled_block}}.instructions.instructions.to_unsafe,\n      stack: stack,\n    )\n    @call_stack << copied_call_frame\n\n    instructions = copied_call_frame.instructions\n    ip = copied_call_frame.ip\n    stack_bottom = copied_call_frame.stack_bottom\n\n    %size_to_clear = {{compiled_block}}.locals_bytesize_end - {{compiled_block}}.locals_bytesize_start - {{compiled_block}}.args_bytesize\n\n    # The size to clear might not always be greater than zero.\n    # For example, if all block variables are closured then the only\n    # block var is the closure itself.\n    if %size_to_clear > 0\n      %offset_to_clear = {{compiled_block}}.locals_bytesize_start + {{compiled_block}}.args_bytesize\n\n      # Clear the portion after the block args and up to the block local vars\n      # because it might contain garbage data from previous block calls or\n      # method calls.\n      #\n      # stack ... locals ... locals_bytesize_start ... args_bytesize ... locals_bytesize_end\n      #                                                            [ ..................... ]\n      #                                                                   delete this\n      (stack_bottom + %offset_to_clear).clear(%size_to_clear)\n    end\n  end\n\n  private macro lib_call(lib_function)\n    %cif = lib_function.call_interface\n    %fn = lib_function.symbol\n    %args_bytesizes = lib_function.args_bytesizes\n    %return_bytesize = lib_function.return_bytesize\n\n    # Assume C calls don't have more than 100 arguments\n    # TODO: use the stack for this?\n    %pointers = uninitialized StaticArray(Pointer(Void), 100)\n    %offset = 0\n\n    %i = %args_bytesizes.size - 1\n    %args_bytesizes.reverse_each do |arg_bytesize|\n      %pointers[%i] = (stack - %offset - arg_bytesize).as(Void*)\n      %offset += arg_bytesize\n      %i -= 1\n    end\n\n    begin\n      %cif.call(%fn, %pointers.to_unsafe, stack.as(Void*))\n    rescue ex : EscapingException\n      raise_exception(ex.exception_pointer)\n    else\n      %aligned_return_bytesize = align(%return_bytesize)\n\n      (stack - %offset).move_from(stack, %return_bytesize)\n      stack = stack - %offset + %return_bytesize\n\n      stack_grow_by(%aligned_return_bytesize - %return_bytesize)\n    end\n  end\n\n  private macro leave(size)\n    # Remember the point the stack reached\n    %old_stack = stack\n    %previous_call_frame = @call_stack.pop\n\n    leave_after_pop_call_frame(%old_stack, %previous_call_frame, {{size}})\n  end\n\n  private macro leave_def(size)\n    %throw_value = new_returned_value(stack, {{size}}, @call_stack.last.real_frame_index)\n    throw_value(%throw_value)\n  end\n\n  private macro break_block(size)\n    %throw_value = new_returned_value(\n      stack,\n      {{size}},\n      # Exiting the current frame... (-1)\n      # ...we'll find the method that was given a block (-2)\n      # We go to the call frame that called the block (+1)\n      @call_stack[-2].block_caller_frame_index + 1,\n    )\n    throw_value(%throw_value)\n  end\n\n  private def new_returned_value(stack, size, target_frame_index)\n    if size <= SMALL_RETURN_VALUE_MAX_SIZE\n      static_array = StaticArray(UInt8, SMALL_RETURN_VALUE_MAX_SIZE).new(0)\n      static_array.to_unsafe.copy_from(stack - size, size)\n      SmallReturnedValue.new(static_array, size, target_frame_index)\n    else\n      pointer = Pointer(Void).malloc(size).as(UInt8*)\n      pointer.copy_from(stack - size, size)\n      BigReturnedValue.new(pointer, size, target_frame_index)\n    end\n  end\n\n  private macro leave_after_pop_call_frame(old_stack, previous_call_frame, size)\n    if @call_stack.size == @call_stack_leave_index\n      return_value = Pointer(Void).malloc({{size}}).as(UInt8*)\n      return_value.copy_from(stack_bottom_after_local_vars, {{size}})\n      stack_shrink_by({{size}})\n      break\n    else\n      %old_stack = {{old_stack}}\n      %previous_call_frame = {{previous_call_frame}}\n      %call_frame = @call_stack.last\n\n      # Restore ip, instructions and stack bottom\n      instructions = %call_frame.instructions\n      ip = %call_frame.ip\n      stack_bottom = %call_frame.stack_bottom\n      stack = %call_frame.stack\n\n      # Ccopy the return value\n      stack_move_from(%old_stack - {{size}}, {{size}})\n\n      # TODO: clean up stack\n    end\n  end\n\n  private macro backtrace\n    # Note: this won't work if Array's internal representation is changed,\n    # but that's unlikely to happen.\n    %bt = [] of String\n\n    @call_stack.each_with_index do |call_frame, index|\n      call_frame_instructions = call_frame.instructions.instructions\n      call_frame_nodes = call_frame.instructions.nodes\n\n      # All calls have 1 byte for the opcode and sizeof(Void*) bytes\n      # for the target call, so we go back to that point to find the relevant node.\n      # However, we don't need to do that for the top-most call frame.\n      call_frame_ip =\n        if index == @call_stack.size - sizeof(OpCode)\n          call_frame.ip\n        else\n          call_frame.ip - sizeof(Void*) - sizeof(OpCode)\n        end\n\n      if index < @call_stack.size - 1\n        # Add any overflow offset if necessary\n        call_frame_ip += @call_stack[index + 1].overflow_offset\n      end\n\n      call_frame_index = call_frame_ip - call_frame_instructions.to_unsafe\n      node = call_frame_nodes[call_frame_index]?\n      if node && (location = node.location)\n        location = location.macro_location || location\n        def_name = call_frame.compiled_def.def.name\n        filename = location.filename\n        line_number = location.line_number\n        column_number = location.column_number\n\n        # We could devise a way to encode this information more efficiently,\n        # but for now this works.\n        %bt << \"#{def_name}|#{line_number}|#{column_number}|#{filename}\"\n      end\n    end\n\n    %bt.reverse!\n\n    # Make sure the type ID of the returned array is the same\n    # as the one in the interpreted program.\n    # This is totally unsafe, but this value won't be used anymore in this program,\n    # only in the interpreted program.\n    %bt.as(Int32*).value = @context.type_id(@context.program.array_of(@context.program.string))\n\n    %bt\n  end\n\n  private macro raise_exception(exception)\n    %exception = {{exception}}\n\n    while true\n      %handlers = instructions.exception_handlers\n      %found_handler = false\n\n      if %handlers\n        %index = ip - instructions.instructions.to_unsafe\n\n        # Go back one byte because otherwise we are right at the\n        # beginning of the next instructions, which isn't where the\n        # exception was raised.\n        %index -= 1\n\n        %exception_type_id = %exception.as(Int32*).value\n        %exception_type = @context.type_from_id(%exception_type_id)\n\n        # Check if any handler should handle the current exception\n        %handlers.each do |handler|\n          %exception_types = handler.exception_types\n\n          # That is, if the instruction index/offset is within the handler's range,\n          # and if there are no specific exception types to rescue (this is an ensure clause)\n          # or if the raised exception is any of the exceptions to handle.\n          if handler.start_index <= %index < handler.end_index &&\n            (!%exception_types || %exception_types.any? { |ex_type| %exception_type.implements?(ex_type) })\n            # Push the exception so that it can be assigned to the rescue variable,\n            # or thrown in an ensure handler.\n            if %exception_types\n              stack_push(%exception)\n            else\n              # In the case of an ensure we make the exception be a ThrowValue,\n              # because ensure work both for exceptions and returned values.\n              %throw_value = RaisedException.new(%exception).as(ThrowValue)\n              stack_push(%throw_value)\n            end\n\n            # Jump to the handler's logic\n            set_ip(handler.jump_index)\n\n            %found_handler = true\n            break\n          end\n        end\n      end\n\n      break if %found_handler\n\n      %old_stack = stack\n      %previous_call_frame = @call_stack.pop\n\n      if @call_stack.size == @call_stack_leave_index\n        raise EscapingException.new(self, %exception)\n      end\n\n      leave_after_pop_call_frame(%old_stack, %previous_call_frame, 0)\n    end\n  end\n\n  private macro throw_value(throw_value)\n    %throw_value = {{throw_value}}\n\n    while true\n      %handlers = instructions.exception_handlers\n      %found_handler = false\n\n      if %handlers\n        %index = ip - instructions.instructions.to_unsafe\n\n        # Go back one byte because otherwise we are right at the\n        # beginning of the next instructions, which isn't where the\n        # exception was raised.\n        %index -= 1\n\n        # Check if any handler should handle the current exception\n        %handlers.each do |handler|\n          next if handler.exception_types\n\n          # That is, if it's an ensure handler (no exceptions)\n          # and the instruction index/offset is within the handler's range\n          if !handler.exception_types && handler.start_index <= %index < handler.end_index\n            stack_push(%throw_value.as(ThrowValue))\n\n            # Jump to the handler's logic\n            set_ip(handler.jump_index)\n\n            %found_handler = true\n            break\n          end\n        end\n      end\n\n      break if %found_handler\n\n      %old_stack = stack\n      %previous_call_frame = @call_stack.pop\n\n      if @call_stack.size == %throw_value.target_frame_index\n        %old_stack.copy_from(%throw_value.pointer, %throw_value.size)\n        %old_stack += %throw_value.size\n        leave_after_pop_call_frame(%old_stack, %previous_call_frame, %throw_value.size)\n        break\n      else\n        leave_after_pop_call_frame(%old_stack, %previous_call_frame, 0)\n      end\n    end\n  end\n\n  private macro throw\n    %throw_value = stack_pop(ThrowValue)\n    case %throw_value\n    in ReturnedValue\n      throw_value(%throw_value)\n    in RaisedException\n      raise_exception(%throw_value.exception)\n    end\n  end\n\n  private macro set_ip(ip)\n    ip = instructions.instructions.to_unsafe + {{ip}}\n  end\n\n  private macro set_local_var(index, size)\n    stack_move_to(stack_bottom + {{index}}, {{size}})\n  end\n\n  private macro get_local_var(index, size)\n    stack_move_from(stack_bottom + {{index}}, {{size}})\n  end\n\n  private macro get_local_var_pointer(index)\n    stack_bottom + {{index}}\n  end\n\n  private macro get_ivar_pointer(offset)\n    self_class_pointer + offset\n  end\n\n  private macro const_initialized?(index)\n    # TODO: make this atomic\n    %initialized = @context.constants_memory[{{index}}]\n    if %initialized == 1_u8\n      true\n    else\n      @context.constants_memory[{{index}}] = 1_u8\n      false\n    end\n  end\n\n  private macro get_const(index, size)\n    stack_move_from(get_const_pointer(index), {{size}})\n  end\n\n  private macro get_const_pointer(index)\n    @context.constants_memory + {{index}} + Constants::OFFSET_FROM_INITIALIZED\n  end\n\n  private macro set_const(index, size)\n    stack_move_to(get_const_pointer(index), {{size}})\n  end\n\n  private macro class_var_initialized?(index)\n    # TODO: make this atomic\n    %initialized = @context.class_vars_memory[{{index}}]\n    if %initialized == 1_u8\n      true\n    else\n      @context.class_vars_memory[{{index}}] = 1_u8\n      false\n    end\n  end\n\n  private macro get_class_var(index, size)\n    stack_move_from(get_class_var_pointer(index), {{size}})\n  end\n\n  private macro set_class_var(index, size)\n    stack_move_to(get_class_var_pointer(index), {{size}})\n  end\n\n  private macro get_class_var_pointer(index)\n    @context.class_vars_memory + {{index}} + ClassVars::OFFSET_FROM_INITIALIZED\n  end\n\n  private macro atomicrmw_op(op)\n    case element_size\n    when 1\n      i8 = Atomic::Ops.atomicrmw({{op}}, ptr, value.to_u8!, :sequentially_consistent, false)\n      stack_push(i8)\n    when 2\n      i16 = Atomic::Ops.atomicrmw({{op}}, ptr.as(UInt16*), value.to_u16!, :sequentially_consistent, false)\n      stack_push(i16)\n    when 4\n      i32 = Atomic::Ops.atomicrmw({{op}}, ptr.as(UInt32*), value.to_u32!, :sequentially_consistent, false)\n      stack_push(i32)\n    when 8\n      i64 = Atomic::Ops.atomicrmw({{op}}, ptr.as(UInt64*), value.to_u64!, :sequentially_consistent, false)\n      stack_push(i64)\n    else\n      raise \"BUG: unhandled element size for store_atomic instruction: #{element_size}\"\n    end\n  end\n\n  private macro pry\n    self.pry = true\n  end\n\n  def pry=(@pry)\n    @pry = pry\n\n    unless pry\n      @pry_node = nil\n      @pry_max_target_frame = nil\n    end\n  end\n\n  private macro next_instruction(t)\n    value = ip.as({{t}}*).value\n    ip += sizeof({{t}})\n    value\n  end\n\n  private macro self_class_pointer\n    get_local_var_pointer(0).as(Pointer(Pointer(UInt8))).value\n  end\n\n  private macro stack_pop(t)\n    %aligned_size = align(sizeof({{t}}))\n    %value = uninitialized {{t}}\n    (stack - %aligned_size).copy_to(pointerof(%value).as(UInt8*), sizeof(typeof(%value)))\n    stack_shrink_by(%aligned_size)\n    %value\n  end\n\n  private macro stack_push(value)\n    %temp = {{value}}\n    %size = sizeof(typeof(%temp))\n\n    stack.copy_from(pointerof(%temp).as(UInt8*), %size)\n    %aligned_size = align(%size)\n    stack += %size\n    stack_grow_by(%aligned_size - %size)\n  end\n\n  private macro stack_copy_to(pointer, size)\n    (stack - {{size}}).copy_to({{pointer}}, {{size}})\n  end\n\n  private macro stack_move_to(pointer, size)\n    %size = {{size}}\n    %aligned_size = align(%size)\n    (stack - %aligned_size).copy_to({{pointer}}, %size)\n    stack_shrink_by(%aligned_size)\n  end\n\n  private macro stack_move_from(pointer, size)\n    %size = {{size}}\n    %aligned_size = align(%size)\n\n    stack.copy_from({{pointer}}, %size)\n    stack += %size\n    stack_grow_by(%aligned_size - %size)\n  end\n\n  private macro stack_grow_by(size)\n    stack_clear({{size}})\n    stack += {{size}}\n  end\n\n  private macro stack_shrink_by(size)\n    stack -= {{size}}\n    stack_clear({{size}})\n  end\n\n  private macro stack_clear(size)\n    # TODO: clearing the stack after every step is very slow!\n    stack.clear({{size}})\n  end\n\n  def aligned_sizeof_type(type : Type) : Int32\n    @context.aligned_sizeof_type(type)\n  end\n\n  def inner_sizeof_type(type : Type) : Int32\n    @context.inner_sizeof_type(type)\n  end\n\n  private def type_id(type : Type) : Int32\n    @context.type_id(type)\n  end\n\n  private def type_from_type_id(id : Int32) : Type\n    @context.type_from_id(id)\n  end\n\n  # How many bytes the `type_id` portion of a union type occupy.\n  private macro type_id_bytesize\n    8\n  end\n\n  private def align(value : Int32)\n    @context.align(value)\n  end\n\n  private def program\n    @context.program\n  end\n\n  private def argc_unsafe\n    argv.size + 1\n  end\n\n  @argv_unsafe = Pointer(Pointer(UInt8)).null\n\n  private def argv_unsafe\n    @argv_unsafe ||= begin\n      pointers = Pointer(Pointer(UInt8)).malloc(argc_unsafe)\n      # The program name\n      pointers[0] = \"icr\".to_unsafe\n\n      argv.each_with_index do |arg, i|\n        pointers[i + 1] = arg.to_unsafe\n      end\n\n      pointers\n    end\n  end\n\n  private def spawn_interpreter(fiber : Void*, fiber_main : Void*) : Void*\n    spawned_fiber = Fiber.new do\n      # `fiber_main` is the pointer type of a `Proc(Fiber, Nil)`.\n      # `fiber` is the fiber that we need to pass `fiber_main` to kick off the fiber.\n      #\n      # To make it work, we construct a call like this:\n      #\n      # ```\n      # fiber_main = uninitialized Proc(Fiber, Nil)\n      # fiber = uninitialized Fiber\n      # fiber_main.call(fiber)\n      # ```\n      #\n      # And we inject their values in the stack.\n\n      fiber_type = @context.program.types[\"Fiber\"]\n      nil_type = @context.program.nil_type\n      proc_type = @context.program.proc_of([fiber_type, nil_type] of Type)\n\n      fiber_main_decl = UninitializedVar.new(Var.new(\"fiber_main\"), TypeNode.new(proc_type))\n      fiber_decl = UninitializedVar.new(Var.new(\"fiber\"), TypeNode.new(fiber_type))\n      call = Call.new(Var.new(\"fiber_main\"), \"call\", Var.new(\"fiber\"))\n      exps = Expressions.new([fiber_main_decl, fiber_decl, call] of ASTNode)\n\n      meta_vars = MetaVars.new\n\n      main_visitor = MainVisitor.new(@context.program, vars: meta_vars, meta_vars: meta_vars)\n      exps.accept main_visitor\n\n      @context.checkout_stack do |stack|\n        interpreter = Interpreter.new(@context, stack)\n\n        # We need to put the data for `fiber_main` and `fiber` on the stack.\n\n        # Here comes `fiber_main`\n        # Put the proc pointer first\n        stack.as(Void**).value = fiber_main\n        stack += sizeof(Void*)\n\n        # Put the closure data, which is nil\n        stack.as(Void**).value = Pointer(Void).null\n        stack += sizeof(Void*)\n\n        # Now comes `fiber`\n        stack.as(Void**).value = fiber\n\n        begin\n          interpreter.interpret(exps, main_visitor.meta_vars)\n        rescue ex : EscapingException\n          print \"Unhandled exception in spawn: \"\n          print ex\n        end\n\n        nil\n      end\n    end\n    spawned_fiber.@context.resumable = 1\n    spawned_fiber.as(Void*)\n  end\n\n  private def swapcontext(current_context : Void*, new_context : Void*)\n    # current_fiber = current_context.as(Fiber*).value\n    new_fiber = new_context.as(Fiber*).value\n\n    # delegates the context switch to the interpreter's scheduler, so we update\n    # the current fiber reference, set the GC stack bottom, and so on (aka\n    # there's more to switching context than `Fiber.swapcontext`):\n    new_fiber.resume\n  end\n\n  private def fiber_resumable(context : Void*) : LibC::Long\n    fiber = context.as(Fiber*).value\n    fiber.@context.resumable\n  end\n\n  private def signal_descriptor(fd : Int32) : Nil\n    {% if flag?(:unix) %}\n      # replace the interpreter's signal writer so that the interpreted code\n      # will receive signals from now on\n      writer = IO::FileDescriptor.new(fd)\n      writer.sync = true\n      Crystal::System::Signal.writer = writer\n    {% else %}\n      raise \"BUG: interpreter doesn't support signals on this target\"\n    {% end %}\n  end\n\n  private def signal(signum : Int32, handler : Int32) : Nil\n    {% if flag?(:unix) %}\n      signal = ::Signal.new(signum)\n      case handler\n      when 0\n        signal.reset\n      when 1\n        signal.ignore\n      else\n        # register the signal for the OS so the process will receive them;\n        # registers a fake handler since the interpreter won't handle the signal:\n        # the interpreted code will receive it and will execute the interpreted\n        # handler\n        signal.trap { }\n      end\n    {% else %}\n      raise \"BUG: interpreter doesn't support signals on this target\"\n    {% end %}\n  end\n\n  private def pry(ip, instructions, stack_bottom, stack)\n    offset = (ip - instructions.instructions.to_unsafe).to_i32\n    node = instructions.nodes[offset]?\n    pry_node = @pry_node\n\n    return unless node\n\n    location = node.location\n    return unless location\n\n    return unless different_node_line?(node, pry_node)\n\n    call_frame = @call_stack.last\n    compiled_def = call_frame.compiled_def\n    compiled_block = call_frame.compiled_block\n    local_vars = compiled_block.try(&.local_vars) || compiled_def.local_vars\n\n    a_def = compiled_def.def\n\n    whereami(a_def, location)\n\n    # puts\n    # puts Slice.new(stack_bottom, stack - stack_bottom).hexdump\n    # puts\n\n    # Remember the portion from stack_bottom + local_vars.max_bytesize up to stack\n    # because it might happen that the child interpreter will overwrite some\n    # of that if we already have some values in the stack past the local vars\n    original_local_vars_max_bytesize = local_vars.max_bytesize\n    data_size = stack - (stack_bottom + original_local_vars_max_bytesize)\n    data = Pointer(Void).malloc(data_size).as(UInt8*)\n    data.copy_from(stack_bottom + original_local_vars_max_bytesize, data_size)\n\n    gatherer = LocalVarsGatherer.new(location, a_def)\n    gatherer.gather\n    meta_vars = gatherer.meta_vars\n\n    # Freeze the type of existing variables because they can't\n    # change during a pry session.\n    meta_vars.each do |name, var|\n      var_type = var.type?\n      var.freeze_type = var_type if var_type\n    end\n\n    block_level = local_vars.block_level\n    owner = compiled_def.owner\n\n    closure_context =\n      if compiled_block\n        compiled_block.closure_context\n      else\n        compiled_def.closure_context\n      end\n\n    closure_context.try &.vars.each do |name, (index, type)|\n      meta_vars[name] = MetaVar.new(name, type)\n    end\n\n    main_visitor = MainVisitor.new(\n      @context.program,\n      vars: meta_vars,\n      meta_vars: meta_vars,\n      typed_def: a_def)\n\n    # Scope is used for instance types, never for Program\n    unless owner.is_a?(Program)\n      main_visitor.scope = owner\n    end\n\n    main_visitor.path_lookup = owner\n\n    interpreter = Interpreter.new(self, compiled_def, local_vars, closure_context, stack_bottom, block_level)\n\n    while @pry\n      @pry_reader.prompt_info = String.build do |io|\n        unless owner.is_a?(Program)\n          if owner.metaclass?\n            io.print owner.instance_type\n            io.print '.'\n          else\n            io.print owner\n            io.print '#'\n          end\n        end\n        io.print compiled_def.def.name\n      end\n\n      input = @pry_reader.read_next\n      unless input\n        self.pry = false\n        break\n      end\n\n      case input\n      when \"continue\"\n        self.pry = false\n        break\n      when \"step\"\n        @pry_node = node\n        @pry_max_target_frame = nil\n        break\n      when \"next\"\n        @pry_node = node\n        @pry_max_target_frame = @call_stack.last.real_frame_index\n        break\n      when \"finish\"\n        @pry_node = node\n        @pry_max_target_frame = @call_stack.last.real_frame_index - 1\n        break\n      when \"whereami\"\n        whereami(a_def, location)\n        next\n      when \"*d\"\n        puts local_vars\n        puts Disassembler.disassemble(@context, compiled_block || compiled_def)\n        next\n      when \"*s\"\n        puts Slice.new(@stack, stack - @stack).hexdump\n        next\n      end\n\n      begin\n        parser = Parser.new(\n          input,\n          string_pool: @context.program.string_pool,\n          var_scopes: [meta_vars.keys.to_set],\n        )\n        line_node = parser.parse\n\n        next unless line_node\n\n        main_visitor = MainVisitor.new(from_main_visitor: main_visitor)\n\n        vars_size_before_semantic = main_visitor.vars.size\n\n        line_node = @context.program.normalize(line_node)\n        line_node = @context.program.semantic(line_node, main_visitor: main_visitor)\n\n        vars_size_after_semantic = main_visitor.vars.size\n\n        if vars_size_after_semantic > vars_size_before_semantic\n          # These are all temporary variables created by MainVisitor.\n          # Let's add them to local vars.\n          main_visitor.vars.each_with_index do |(name, var), index|\n            next unless index >= vars_size_before_semantic\n\n            interpreter.local_vars.declare(name, var.type)\n          end\n        end\n\n        value = interpreter.interpret(line_node, meta_vars, in_pry: true)\n\n        # New local variables might have been declared during a pry session.\n        # Remember them by asking them from the interpreter\n        # (the interpreter will keep adding those, or migrate new ones\n        # to their new type)\n        local_vars = interpreter.local_vars\n\n        print \" => \"\n        puts SyntaxHighlighter::Colorize.highlight!(value.to_s)\n      rescue ex : EscapingException\n        print \"Unhandled exception: \"\n        print ex\n      rescue ex : Crystal::CodeError\n        ex.color = true\n        ex.error_trace = true\n        puts ex\n        next\n      rescue ex : Exception\n        ex.inspect_with_backtrace(STDOUT)\n        next\n      end\n    end\n\n    # Restore the stack data in case it tas overwritten\n    (stack_bottom + original_local_vars_max_bytesize).copy_from(data, data_size)\n  end\n\n  private def whereami(a_def : Def, location : Location)\n    filename = location.filename\n    line_number = location.line_number\n    column_number = location.column_number\n\n    if filename.is_a?(String)\n      puts \"From: #{Crystal.relative_filename(filename)}:#{line_number}:#{column_number} #{a_def.owner}##{a_def.name}:\"\n    else\n      puts \"From: #{location} #{a_def.owner}##{a_def.name}:\"\n    end\n\n    puts\n\n    source =\n      case filename\n      in String\n        File.read(filename)\n      in VirtualFile\n        filename.source\n      in Nil\n        nil\n      end\n\n    return unless source\n\n    if @context.program.color?\n      begin\n        # We highlight the entire file. We could try highlighting each\n        # individual line but that won't work well for heredocs and other\n        # constructs. Also, highlighting is pretty fast so it won't be noticeable.\n        #\n        # TODO: in reality if the heredoc starts way before the lines we show,\n        # we lose the command that flips the color on. We should probably do\n        # something better here, but for now this is good enough.\n        source = Crystal::SyntaxHighlighter::Colorize.highlight(source)\n      rescue\n        # Ignore highlight errors\n      end\n    end\n\n    lines = source.lines\n\n    min_line_number = {location.line_number - 5, 1}.max\n    max_line_number = {location.line_number + 5, lines.size}.min\n\n    max_line_number_size = max_line_number.to_s.size\n\n    min_line_number.upto(max_line_number) do |line_number|\n      line = lines[line_number - 1]\n      if line_number == location.line_number\n        print \" => \"\n      else\n        print \"    \"\n      end\n\n      # Pad line number if needed\n      line_number_size = line_number.to_s.size\n      (max_line_number_size - line_number_size).times do\n        print ' '\n      end\n\n      print @context.program.colorize(line_number).blue\n      print \": \"\n      puts line\n    end\n    puts\n  end\n\n  private def different_node_line?(node : ASTNode, previous_node : ASTNode?)\n    return true unless previous_node\n    return true if node.location.not_nil!.filename != previous_node.location.not_nil!.filename\n\n    node.location.not_nil!.line_number != previous_node.location.not_nil!.line_number\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/lib_function.cr",
    "content": "require \"./repl\"\n\n# Information about a C function that needs to be called.\nclass Crystal::Repl::LibFunction\n  # Symbol returned by dlopen and dlsym that is a pointer\n  # to the actual function.\n  getter symbol : Void*\n\n  # The FFI call interface definition to call the C function\n  getter call_interface : FFI::CallInterface\n\n  # Bytesize for each argument in the call\n  getter args_bytesizes : Array(Int32)\n\n  # Bytesize for the call's return value\n  getter return_bytesize : Int32\n\n  def initialize(\n    @symbol : Void*,\n    @call_interface : FFI::CallInterface,\n    @args_bytesizes : Array(Int32),\n    @return_bytesize : Int32,\n  )\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/local_vars.cr",
    "content": "require \"./repl\"\n\n# Records the position in the stack for local variables.\n#\n# This is almost a Hash where the keys are names and the values\n# are indexes, but it's a bit more complex because blocks create\n# separate scopes where name clashes can happen.\n#\n# So if we have a code like this:\n#\n# ```\n# a = 0\n# b = 1\n# foo do |a|\n#   b = a\n# end\n# c = 2\n# ```\n#\n# We actually want to store:\n# - a in the first position (0-8 bytes)\n# - b in the second position (8-16 bytes)\n# - the a in the block is a different var, so we put it in (16-24)\n# - now that the block is over, that new a (or any variable that only\n#   exists inside the block) are no longer reachable so:\n# - c can also be put in (16-24)\nclass Crystal::Repl::LocalVars\n  record Key, name : String, block_level : Int32\n\n  getter block_level : Int32\n\n  def initialize(@context : Context)\n    @types = {} of Key => Type\n    @name_to_index = {} of Key => Int32\n    @bytesize = 0\n    @max_bytesize = 0\n    @block_level = 0\n    @bytesize_per_block_level = [] of Int32\n  end\n\n  def initialize(local_vars : LocalVars)\n    @context = local_vars.@context\n    @types = local_vars.@types.dup\n    @name_to_index = local_vars.@name_to_index.dup\n    @bytesize = local_vars.@bytesize\n    @max_bytesize = local_vars.@max_bytesize\n    @block_level = local_vars.@block_level\n    @bytesize_per_block_level = local_vars.@bytesize_per_block_level\n  end\n\n  def push_block : Nil\n    @bytesize_per_block_level << @bytesize\n    @block_level += 1\n  end\n\n  def pop_block : Nil\n    # Undefine variables that belong to the current block\n    @types.reject! { |key, type| key.block_level == @block_level }\n    @name_to_index.reject! { |key, index| key.block_level == @block_level }\n\n    @block_level -= 1\n    @bytesize = @bytesize_per_block_level.pop\n  end\n\n  def current_bytesize\n    @bytesize\n  end\n\n  def max_bytesize\n    @max_bytesize\n  end\n\n  def names_at_block_level_zero\n    @name_to_index.keys.select { |key| key.block_level == 0 }.map(&.name)\n  end\n\n  def names\n    @name_to_index.keys.map(&.name)\n  end\n\n  def declare(name : String, type : Type) : Int32?\n    is_self = name == \"self\"\n    # TODO: should this use `Type#passed_as_self?` instead?\n    return if is_self && (type.is_a?(Program) || type.is_a?(FileModule))\n\n    key = Key.new(name, @block_level)\n\n    index = @bytesize\n    @name_to_index[key] = index\n\n    @types[key] = type\n\n    if is_self && type.passed_by_value?\n      @bytesize += sizeof(Pointer(UInt8))\n    else\n      @bytesize += @context.aligned_sizeof_type(type)\n    end\n\n    @max_bytesize = @bytesize if @bytesize > @max_bytesize\n\n    index\n  end\n\n  def name_to_index(name : String, block_level : Int32) : Int32\n    @name_to_index[Key.new(name, block_level)]\n  end\n\n  def name_to_index?(name : String, block_level : Int32) : Int32?\n    @name_to_index[Key.new(name, block_level)]?\n  end\n\n  def type(name : String, block_level : Int32) : Type\n    @types[Key.new(name, block_level)]\n  end\n\n  def type?(name : String, block_level : Int32) : Type?\n    @types[Key.new(name, block_level)]?\n  end\n\n  def to_s(io : IO) : Nil\n    return if @max_bytesize == 0\n\n    io << \"local table (bytesize: \" << @max_bytesize << \")\\n\"\n    @name_to_index.each do |key, index|\n      io << \"\\t\" unless index == 0\n      io << key.name << '@' << index\n      io << 'B' << key.block_level if key.block_level > 0\n      io << ':' << @types[key]\n    end\n  end\n\n  def dup\n    LocalVars.new(self)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/local_vars_gatherer.cr",
    "content": "require \"./repl\"\n\n# Gathers known local variables during a pry session.\n# For example, if we have code like this:\n#\n# ```\n# a = 1\n#\n# foo do |x|\n#   b = 2\n#   debugger\n#\n#   c = 3\n# end\n#\n# d = 4\n# ```\n#\n# When semantic analysis runs for the entire code, there will\n# be local variables for a, b, c, d and x. However, at the\n# point of `debugger` only a, b and x have a real, usable value\n# (c and d will still be in the stack memory, but have garbage\n# values.)\n#\n# This class is responsible for figuring out which local variables\n# can be accessed when a `debugger` call is issued: essentially,\n# any variable that is assigned or created before the `debugger` call.\nclass Crystal::Repl::LocalVarsGatherer < Crystal::Visitor\n  @block : Block?\n\n  # The meta vars that can be accessed at the location given\n  # in the `initialize` method.\n  getter meta_vars : MetaVars\n\n  def initialize(@location : Location, @def : Def)\n    @meta_vars = MetaVars.new\n  end\n\n  def gather : Nil\n    self_var = @def.vars.try &.[\"self\"]?\n    @meta_vars[\"self\"] = self_var.clone if self_var\n\n    @def.args.each do |arg|\n      var = @def.vars.try &.[arg.name]?\n      @meta_vars[var.name] = var.clone if var\n    end\n\n    if @def.uses_block_arg? && (block_arg = @def.block_arg)\n      block_arg_var = @def.vars.try &.[block_arg.name]?\n      @meta_vars[block_arg.name] = block_arg_var.clone if block_arg_var\n    end\n\n    @def.body.accept self\n  end\n\n  def visit(node : Rescue)\n    location = node.location\n    if location && location.line_number >= @location.line_number\n      return false\n    end\n\n    if name = node.name\n      add_var(name)\n    end\n\n    true\n  end\n\n  def visit(node : Var)\n    location = node.location\n    if location && location.line_number >= @location.line_number\n      return false\n    end\n\n    add_var(node.name)\n\n    false\n  end\n\n  def visit(node : Block)\n    location = node.location\n    end_location = node.end_location\n\n    if location && end_location && !(location.line_number < @location.line_number < end_location.line_number)\n      return false\n    end\n\n    old_block = @block\n    @block = node\n\n    node.args.each &.accept self\n    node.body.accept self\n\n    @block = old_block\n\n    false\n  end\n\n  def visit(node : ASTNode)\n    true\n  end\n\n  private def add_var(name : String)\n    var =\n      if block = @block\n        block.vars.try &.[name]?\n      else\n        @def.vars.try &.[name]?\n      end\n\n    @meta_vars[var.name] = var.clone if var\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/multidispatch.cr",
    "content": "require \"./repl\"\nrequire \"../semantic/main_visitor\"\n\n# Non-interpreted Crystal does multidispatch by essentially\n# inlining `is_a?` calls and performing the appropriate casting\n# and calling.\n#\n# For the interpreter this is a bit hard to do because it's a stack-based VM,\n# so it's hard to get access to stack values that are not at the top\n# of the stack.\n#\n# So, for interpreted mode, when there's a dispatch, we actually create\n# a method that will do the dispatch, and we call that method.\n# For example, if we have this code:\n#\n# ```\n# def foo(x : Int32)\n# end\n#\n# def foo(x : Char)\n# end\n#\n# a = 1 || 'a'\n# foo(a)\n# ```\n#\n# The call to `foo` is actually delegated to another `foo` method\n# that will do the dispatch, created in this file:\n#\n# ```\n# def foo(x)\n#   if x.is_a?(Int32)\n#     foo(x)\n#   elsif x.is_a?(Char)\n#     foo(x)\n#   else\n#     unreachable\n#   end\n# end\n# ```\nmodule Crystal::Repl::Multidispatch\n  def self.create_def(context : Context, node : Call, target_defs : Array(Def))\n    if node.block\n      a_def = create_def_uncached(context, node, target_defs)\n\n      # Store it so the GC doesn't collect it (it's in the instructions but it might not be aligned)\n      context.add_gc_reference(a_def)\n\n      return a_def\n    end\n\n    obj = node.obj\n    obj_type = obj.try(&.type) || node.scope\n\n    signature = CallSignature.new(\n      name: node.name,\n      arg_types: node.args.map(&.type),\n      named_args: nil,\n      block: node.block,\n    )\n\n    cache_key = Context::MultidispatchKey.new(obj_type, signature)\n    cached_def = context.multidispatchs[cache_key]?\n    return cached_def if cached_def\n\n    a_def = create_def_uncached(context, node, target_defs)\n\n    context.multidispatchs[cache_key] = a_def\n\n    a_def\n  end\n\n  private def self.create_def_uncached(context : Context, node : Call, target_defs : Array(Def))\n    autocast_types = nil\n\n    # The generated multidispatch method should handle autocasted\n    # values. For example if an argument is a symbol but the target\n    # type is an enum, the multidispatch should handle enum values,\n    # not symbols. Autocasting will naturally happen right before\n    # the multidispatch is called.\n\n    # Here we track which args perform autocasting.\n    node.args.each_with_index do |arg, i|\n      # Autocasting only happens from SymbolLiteral or NumberLiteral\n      next unless arg.is_a?(SymbolLiteral) || arg.is_a?(NumberLiteral)\n\n      non_matching_type = nil\n\n      # Check if the call arg type is passed to a method arg type\n      # where the types don't match. That's when autocasting happens.\n      target_defs.each do |target_def|\n        arg_type = target_def.args[i].type\n        if arg_type != arg.type\n          non_matching_type = arg_type\n          break\n        end\n      end\n\n      if non_matching_type\n        autocast_types ||= {} of Int32 => Type\n        autocast_types[i] = non_matching_type\n      end\n    end\n\n    obj = node.obj\n    obj_type = obj.try(&.type) || node.scope\n\n    a_def = Def.new(node.name).at(node)\n\n    unless obj_type.is_a?(Program)\n      self_arg = Arg.new(\"self\").at(node)\n      self_arg.type = obj_type\n      a_def.args << self_arg\n    end\n\n    all_special_vars = nil\n\n    i = 0\n\n    node.args.each_with_index do |arg, arg_index|\n      def_arg = Arg.new(\"arg#{i}\").at(node)\n      def_arg.type = autocast_types.try &.[arg_index]? || arg.type\n      a_def.args << def_arg\n      i += 1\n    end\n\n    block = node.block\n    block_fun_literal = block.try(&.fun_literal)\n\n    if block\n      if block_fun_literal\n        a_def.block_arg = Arg.new(\"block_arg\")\n        a_def.uses_block_arg = true\n      else\n        a_def.block_arg = Arg.new(\"\")\n        a_def.block_arity = block.args.size\n      end\n    end\n\n    main_if = nil\n    current_if = nil\n\n    blocks = [] of Block\n\n    target_defs.each_with_index do |target_def, target_def_index|\n      i = 0\n\n      condition = nil\n\n      unless obj_type.is_a?(Program)\n        unless obj_type.implements?(target_def.owner)\n          condition = IsA.new(Var.new(\"self\"), TypeNode.new(target_def.owner))\n        end\n      end\n\n      node.args.each_with_index do |arg, arg_index|\n        # If the argument was autocasted it will always match in a multidispatch\n        next if autocast_types.try &.[arg_index]?\n\n        target_def_arg = target_def.args[i]\n        condition = add_arg_condition(arg, target_def_arg, i, condition)\n\n        i += 1\n      end\n\n      condition ||= BoolLiteral.new(true)\n\n      call_args = [] of ASTNode\n\n      i = 0\n      node.args.each_with_index do |arg, arg_index|\n        var = Var.new(\"arg#{i}\")\n\n        # If the argument was autocasted it will always match in a multidispatch\n        if autocast_types.try &.[arg_index]?\n          call_args << var\n          i += 1\n          next\n        end\n\n        # Make sure to cast the argument to the target def arg's type\n        # in the last case, where the argument's type is not restricted by an if is_a?\n        if target_def_index == target_defs.size - 1\n          target_def_arg = target_def.args[i]\n\n          cast = Cast.new(var, TypeNode.new(target_def_arg.type))\n          call_args << cast\n        else\n          call_args << var\n        end\n\n        i += 1\n      end\n\n      call_obj =\n        if obj_type.is_a?(Program)\n          nil\n        else\n          Var.new(\"self\")\n        end\n\n      call = Call.new(call_obj, node.name, call_args)\n      call.target_defs = [target_def]\n      call.type = target_def.type\n\n      if block\n        if block_fun_literal\n          # We aren't going to recaluclate calls, so we prepare the call\n          # in a way that the interpreter is going to call it by passing\n          # the block_arg as an extra argument.\n          inner_block = Block.new\n          inner_block_fun_literal = Var.new(\"block_arg\")\n          inner_block_fun_literal.type = block_fun_literal.type\n          inner_block.fun_literal = inner_block_fun_literal\n          call.block = inner_block\n        else\n          block_args = block.args.map_with_index { |arg, i| Var.new(\"barg#{i}\", arg.type) }\n          yield_args = Array(ASTNode).new(block_args.size)\n          block.args.each_index { |i| yield_args << Var.new(\"barg#{i}\", block.args[i].type) }\n\n          inner_block = Block.new(block_args, body: Yield.new(yield_args))\n          blocks << inner_block\n\n          call.block = inner_block\n        end\n      end\n\n      exps = call\n\n      special_vars = target_def.special_vars\n      if special_vars\n        # What we do is something like this:\n        #\n        # ```\n        # .value = call(...)\n        # $~ = $~\n        # .value\n        # ```\n        all_special_vars ||= Set(String).new\n        all_special_vars.concat(special_vars)\n\n        assign = Assign.new(Var.new(\".value\"), call)\n        expressions = [assign] of ASTNode\n        special_vars.each do |special_var|\n          expressions << Assign.new(Var.new(special_var), Var.new(special_var))\n        end\n        expressions << Var.new(\".value\")\n        exps = Expressions.new(expressions)\n      end\n\n      target_def_if = If.new(condition, exps)\n\n      if current_if\n        current_if.else = target_def_if\n        current_if = target_def_if\n      else\n        main_if = target_def_if\n        current_if = target_def_if\n      end\n    end\n\n    current_if = current_if.not_nil!\n    current_if.else = Unreachable.new\n\n    main_if = main_if.not_nil!\n    a_def.body = main_if\n    a_def.special_vars = all_special_vars\n\n    a_def = context.program.normalize(a_def)\n    a_def.owner = obj_type\n\n    def_args = MetaVars.new\n\n    unless obj_type.is_a?(Program)\n      def_args[\"self\"] = MetaVar.new(\"self\", obj_type)\n    end\n\n    i = 0\n\n    node.args.each_with_index do |arg, arg_index|\n      def_args[\"arg#{i}\"] = MetaVar.new(\n        \"arg#{i}\",\n        autocast_types.try &.[arg_index]? || arg.type\n      )\n      i += 1\n    end\n\n    if block_fun_literal\n      def_args[\"block_arg\"] = MetaVar.new(\"block_arg\", block_fun_literal.type)\n    end\n\n    visitor = MultidispatchMainVisitor.new(context.program, def_args, a_def)\n    visitor.untyped_def = a_def\n    visitor.call = node\n\n    # puts a_def\n\n    # visitor.scope = obj_type\n    # visitor.yield_vars = yield_vars\n    # visitor.match_context = match.context\n    # visitor.call = self\n    # visitor.path_lookup = match.context.defining_type\n    a_def.body.accept visitor\n\n    # Because we skipped recalculating calls, the blocks weren't visited.\n    # We do this now.\n    blocks.each &.accept visitor\n\n    a_def.bind_to(a_def.body)\n\n    a_def.body = context.program.cleanup(a_def.body, inside_def: true)\n\n    a_def\n  end\n\n  private def self.add_arg_condition(arg, target_def_arg, i, condition)\n    unless arg.type.implements?(target_def_arg.type)\n      is_a = IsA.new(Var.new(\"arg#{i}\"), TypeNode.new(target_def_arg.type))\n      if condition\n        condition = And.new(condition, is_a)\n      else\n        condition = is_a\n      end\n    end\n    condition\n  end\n\n  # We use a special version of MainVisitor that doesn't resolve calls\n  # nor do some checks. This is because in a multidispatch we already know\n  # what the calls resolve to.\n  class MultidispatchMainVisitor < MainVisitor\n    def recalculate_call(node : Call)\n      # Define special vars (this is copied from the last bits of Call#recalculate)\n      target_def = node.target_def\n      target_def.special_vars.try &.each do |special_var_name|\n        special_var = target_def.vars.not_nil![special_var_name]\n        define_special_var(special_var_name, special_var)\n      end\n    end\n\n    def check_super_or_previous_def_in_initialize(node)\n      # Nothing\n    end\n\n    def check_call_in_initialize(node)\n      # Nothing\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/op_code.cr",
    "content": "require \"./repl\"\nrequire \"./instructions\"\n\n{% begin %}\n  enum Crystal::Repl::OpCode : UInt16\n    {% for name, instruction, i in Crystal::Repl::Instructions %}\n      {{ name.id.upcase }} = {{ i }}\n    {% end %}\n  end\n{% end %}\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/primitives.cr",
    "content": "require \"./compiler\"\n\n# Code to produce bytecode to interpret all the language primitives.\n# It's usually the case that every primitive needs an opcode,\n# and some regular crystal functions are also done as primitives\n# (for example `caller`, or doing a fiber context switch.)\n\nclass Crystal::Repl::Compiler\n  private def visit_primitive(node, body, target_def)\n    owner = node.super? ? node.scope : node.target_def.owner\n    obj = node.obj\n\n    case body.name\n    when \"unchecked_convert\"\n      primitive_convert(node, body, owner, checked: false)\n    when \"convert\"\n      primitive_convert(node, body, owner, checked: true)\n    when \"binary\"\n      primitive_binary(node, body, owner)\n    when \"pointer_new\"\n      accept_call_members(node)\n      return false unless @wants_value\n\n      pointer_new(node: node)\n    when \"pointer_malloc\"\n      discard_value(obj) if obj\n      request_value(node.args.first)\n\n      pointer_instance_type = owner.instance_type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n      element_size = inner_sizeof_type(element_type)\n\n      pointer_malloc(element_size, node: node)\n      pop(aligned_sizeof_type(pointer_instance_type), node: nil) unless @wants_value\n    when \"pointer_realloc\"\n      obj ? request_value(obj) : put_self(node: node)\n      request_value(node.args.first)\n\n      pointer_instance_type = owner.instance_type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n      element_size = inner_sizeof_type(element_type)\n\n      pointer_realloc(element_size, node: node)\n      pop(aligned_sizeof_type(pointer_instance_type), node: nil) unless @wants_value\n    when \"pointer_set\"\n      # Accept in reverse order so that it's easier for the interpreter\n      obj = obj.not_nil!\n\n      pointer_instance_type = owner.instance_type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n\n      arg = node.args.first\n      request_value(arg)\n      dup(aligned_sizeof_type(arg), node: nil) if @wants_value\n      upcast arg, arg.type, element_type\n\n      request_value(obj)\n\n      pointer_set(inner_sizeof_type(element_type), node: node)\n    when \"pointer_get\"\n      pointer_instance_type = owner.instance_type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n\n      accept_call_members(node)\n      return unless @wants_value\n\n      pointer_get(inner_sizeof_type(element_type), node: node)\n    when \"pointer_address\"\n      accept_call_members(node)\n      return unless @wants_value\n\n      pointer_address(node: node)\n    when \"pointer_diff\"\n      accept_call_members(node)\n      return unless @wants_value\n\n      pointer_instance_type = owner.instance_type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n\n      pointer_diff(inner_sizeof_type(element_type), node: node)\n    when \"pointer_add\"\n      accept_call_members(node)\n      return unless @wants_value\n\n      pointer_instance_type = owner.instance_type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n\n      pointer_add(inner_sizeof_type(element_type), node: node)\n    when \"class\"\n      # Should match Crystal::Repl::Value#runtime_type\n      # in src/compiler/crystal/interpreter/value.cr\n      obj = obj.not_nil!\n      type = obj.type.remove_indirection\n\n      case type\n      when VirtualType\n        obj.accept self\n        return unless @wants_value\n\n        put_metaclass aligned_sizeof_type(type), false, node: node\n      when UnionType\n        obj.accept self\n        return unless @wants_value\n\n        put_metaclass aligned_sizeof_type(type), true, node: node\n      else\n        discard_value obj\n        return unless @wants_value\n\n        put_type type, node: node\n      end\n    when \"object_crystal_type_id\"\n      unless @wants_value\n        discard_value obj if obj\n        return\n      end\n\n      if owner.is_a?(VirtualMetaclassType)\n        # For a virtual metaclass type, the value is already an int\n        # that's exactly the crystal_type_id, so there's nothing else to do.\n        if obj\n          request_obj_and_cast_if_needed(obj, owner)\n        else\n          put_self node: node\n        end\n      else\n        put_i32 type_id(owner), node: node\n      end\n    when \"class_crystal_instance_type_id\"\n      type =\n        if obj\n          discard_value(obj)\n          obj.type\n        else\n          scope\n        end\n\n      return unless @wants_value\n\n      put_i32 type_id(type.instance_type), node: node\n    when \"allocate\"\n      type =\n        if obj\n          discard_value(obj)\n          obj.type.instance_type\n        else\n          scope.instance_type\n        end\n\n      return unless @wants_value\n\n      if type.struct?\n        push_zeros(aligned_instance_sizeof_type(type), node: node)\n      else\n        allocate_class(aligned_instance_sizeof_type(type), type_id(type), node: node)\n      end\n\n      initializer_compiled_defs = @context.type_instance_var_initializers(type)\n      unless initializer_compiled_defs.empty?\n        # If it's a struct we need a pointer to it\n        if type.struct?\n          put_stack_top_pointer(aligned_sizeof_type(type), node: nil)\n        end\n\n        # We create a method that will receive \"self\" to initialize each instance var,\n        # and call that by passing a pointer to the class/struct. So we need to dup\n        # the self pointer once per initializer, and then do one call per initializer too.\n        initializer_compiled_defs.size.times do\n          dup sizeof(Pointer(Void)), node: nil\n        end\n\n        initializer_compiled_defs.each do |compiled_def|\n          call compiled_def, node: nil\n        end\n\n        # Pop the struct pointer\n        if type.struct?\n          pop(sizeof(Pointer(Void)), node: nil)\n        end\n      end\n    when \"pre_initialize\"\n      type =\n        if obj\n          discard_value(obj)\n          obj.type.instance_type\n        else\n          scope.instance_type\n        end\n\n      accept_call_args(node)\n\n      # 0 stands for any non-reference type in `reset_class`\n      # (normally 0 stands for `Nil` so there is no conflict here)\n      type_id = type.struct? ? 0 : type_id(type)\n      reset_class(aligned_instance_sizeof_type(type), type_id, node: node)\n\n      initializer_compiled_defs = @context.type_instance_var_initializers(type)\n      unless initializer_compiled_defs.empty?\n        initializer_compiled_defs.size.times do\n          dup sizeof(Pointer(Void)), node: nil\n        end\n\n        initializer_compiled_defs.each do |compiled_def|\n          call compiled_def, node: nil\n        end\n      end\n\n      # `Struct.pre_initialize` does not return a pointer, so always discard it\n      if !@wants_value || type.struct?\n        pop(sizeof(Pointer(Void)), node: nil)\n      end\n    when \"tuple_indexer_known_index\"\n      unless @wants_value\n        accept_call_members(node)\n        return\n      end\n\n      type = owner\n      case type\n      when TupleInstanceType\n        request_obj_or_self_and_cast_if_needed(node, obj, type)\n        index = body.as(TupleIndexer).index\n        case index\n        in Int32\n          element_type = type.tuple_types[index]\n          offset = @context.offset_of(type, index)\n          tuple_indexer_known_index(aligned_sizeof_type(type), offset, inner_sizeof_type(element_type), node: node)\n        in Range\n          element_type = @context.program.tuple_of(type.tuple_types[index].map &.as(Type))\n          tuple_size = aligned_sizeof_type(type)\n          index.each do |i|\n            old_offset = @context.offset_of(type, i)\n            new_offset = @context.offset_of(element_type, i - index.begin)\n            element_size = inner_sizeof_type(type.tuple_types[i])\n            tuple_copy_element(tuple_size, old_offset, new_offset, element_size, node: node)\n          end\n          value_size = inner_sizeof_type(element_type)\n          pop(tuple_size - value_size, node: node)\n          push_zeros(aligned_sizeof_type(element_type) - value_size, node: node)\n        end\n      when NamedTupleInstanceType\n        request_obj_or_self_and_cast_if_needed(node, obj, type)\n        index = body.as(TupleIndexer).index\n        case index\n        when Int32\n          entry = type.entries[index]\n          offset = @context.offset_of(type, index)\n          tuple_indexer_known_index(aligned_sizeof_type(type), offset, inner_sizeof_type(entry.type), node: node)\n        else\n          node.raise \"BUG: missing handling of primitive #{body.name} with range\"\n        end\n      else\n        discard_value obj if obj\n        type = type.instance_type\n        case type\n        when TupleInstanceType\n          index = body.as(TupleIndexer).index\n          case index\n          in Int32\n            put_type(type.tuple_types[index].as(Type).metaclass, node: node)\n          in Range\n            put_type(@context.program.tuple_of(type.tuple_types[index].map &.as(Type)), node: node)\n          end\n        when NamedTupleInstanceType\n          index = body.as(TupleIndexer).index\n          case index\n          when Int32\n            put_type(type.entries[index].type.as(Type).metaclass, node: node)\n          else\n            node.raise \"BUG: missing handling of primitive #{body.name} with range\"\n          end\n        else\n          node.raise \"BUG: missing handling of primitive #{body.name} for #{type}\"\n        end\n      end\n    when \"enum_value\"\n      accept_call_members(node)\n    when \"enum_new\"\n      accept_call_args(node)\n    when \"symbol_to_s\"\n      accept_call_members(node)\n      return unless @wants_value\n\n      symbol_to_s(node: node)\n    when \"object_id\"\n      accept_call_members(node)\n      return unless @wants_value\n\n      pointer_address(node: node)\n    when \"proc_call\"\n      proc_type = owner.as(ProcInstanceType)\n\n      node.args.each_with_index do |arg, arg_index|\n        request_value(arg)\n\n        # Cast call argument to proc's type\n        # (this same logic is done in codegen/primitives.cr)\n        proc_arg_type = proc_type.arg_types[arg_index]\n        target_def_arg_type = target_def.args[arg_index].type\n        if proc_arg_type != target_def_arg_type\n          upcast(arg, target_def_arg_type, proc_arg_type)\n        end\n      end\n\n      if obj\n        request_obj_and_cast_if_needed(obj, owner)\n      else\n        put_self(node: node)\n      end\n\n      proc_call(node: node)\n\n      pop(aligned_sizeof_type(node.type), node: nil) unless @wants_value\n    when \"load_atomic\"\n      accept_call_args(node)\n\n      pointer_instance_type = node.args.first.type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n      element_size = inner_sizeof_type(element_type)\n\n      load_atomic(element_size, node: node)\n    when \"store_atomic\"\n      accept_call_args(node)\n\n      pointer_instance_type = node.args.first.type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n      element_size = inner_sizeof_type(element_type)\n\n      store_atomic(element_size, node: node)\n    when \"atomicrmw\"\n      accept_call_args(node)\n\n      pointer_instance_type = node.args[1].type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n      element_size = inner_sizeof_type(element_type)\n\n      atomicrmw(element_size, node: node)\n    when \"cmpxchg\"\n      accept_call_args(node)\n\n      pointer_instance_type = node.args[0].type.as(PointerInstanceType)\n      element_type = pointer_instance_type.element_type\n      element_size = inner_sizeof_type(element_type)\n\n      cmpxchg(element_size, node: node)\n    when \"external_var_get\"\n      return unless @wants_value\n\n      external = node.target_def.as(External)\n\n      fn = @context.c_function(external.real_name)\n\n      # Put the symbol address, which is a pointer\n      put_u64 fn.address, node: node\n\n      # Read from the pointer\n      pointer_get(inner_sizeof_type(node), node: node)\n    when \"external_var_set\"\n      external = node.target_def.as(External)\n\n      # pointer_set needs first arg, then obj\n      arg = node.args.first\n      request_value(arg)\n      dup(aligned_sizeof_type(arg), node: nil) if @wants_value\n\n      fn = @context.c_function(external.real_name)\n\n      # Put the symbol address, which is a pointer\n      put_u64 fn.address, node: node\n\n      # Set the pointer's value\n      pointer_set(inner_sizeof_type(node), node: node)\n    when \"struct_or_union_set\"\n      obj = obj.not_nil!\n      arg = node.args.first\n\n      # Check if we need an extra conversion like `to_i32!` or `to_unsafe`\n      extra = body.extra\n      if extra\n        # It seems extra is always a Call, so this is always safe\n        # TODO: consider changing Primitive#@extra to be `Call?`\n        call = extra.as(Call)\n        call.obj = arg\n        arg = call\n      end\n\n      type = obj.type.as(NonGenericClassType)\n\n      ivar_name = '@' + node.name.rchop # remove the '=' suffix\n      ivar = type.lookup_instance_var(ivar_name)\n      ivar_offset = ivar_offset(type, ivar_name)\n\n      # pointer_set needs first arg, then obj\n      request_value(arg)\n      dup(aligned_sizeof_type(arg), node: nil) if @wants_value\n\n      upcast arg, arg.type, ivar.type\n\n      ivar_type = ivar.type\n      arg_type = arg.type\n      is_proc_type = false\n\n      # When assigning a proc to an extern struct field we need\n      # to remove the closure data part.\n      if ivar_type.is_a?(ProcInstanceType) && arg_type.is_a?(ProcInstanceType)\n        proc_to_c_fun arg_type.ffi_call_interface, node: nil\n        is_proc_type = true\n      end\n\n      # With this we get a pointer to the struct\n      compile_pointerof_node(obj, obj.type)\n\n      # Shift the pointer to the offset, if needed\n      if ivar_offset > 0\n        put_i64 1, node: nil\n        pointer_add(ivar_offset, node: node)\n      end\n\n      if is_proc_type\n        pointer_set(sizeof(Void*), node: node)\n      else\n        pointer_set(inner_sizeof_type(ivar.type), node: node)\n      end\n    when \"interpreter_proc_new\"\n      accept_call_args(node)\n      interpreter_proc_new(type_id(owner.instance_type), node: node)\n    when \"interpreter_call_stack_unwind\"\n      interpreter_call_stack_unwind(node: node)\n    when \"interpreter_raise_without_backtrace\"\n      accept_call_args(node)\n      interpreter_raise_without_backtrace(node: node)\n    when \"interpreter_current_fiber\"\n      interpreter_current_fiber(node: node)\n    when \"interpreter_spawn\"\n      accept_call_args(node)\n      interpreter_spawn(node: node)\n    when \"interpreter_fiber_swapcontext\"\n      accept_call_args(node)\n      interpreter_fiber_swapcontext(node: node)\n    when \"interpreter_fiber_resumable\"\n      accept_call_args(node)\n      interpreter_fiber_resumable(node: node)\n    when \"interpreter_signal_descriptor\"\n      accept_call_args(node)\n      interpreter_signal_descriptor(node: node)\n    when \"interpreter_signal\"\n      accept_call_args(node)\n      interpreter_signal(node: node)\n    when \"interpreter_intrinsics_memcpy\"\n      accept_call_args(node)\n      interpreter_intrinsics_memcpy(node: node)\n    when \"interpreter_intrinsics_memmove\"\n      accept_call_args(node)\n      interpreter_intrinsics_memmove(node: node)\n    when \"interpreter_intrinsics_memset\"\n      accept_call_args(node)\n      interpreter_intrinsics_memset(node: node)\n    when \"interpreter_intrinsics_debugtrap\"\n      interpreter_intrinsics_debugtrap(node: node)\n    when \"interpreter_intrinsics_pause\"\n      # TODO: given that this is interpreted, maybe `pause` can be nop instead of a real pause?\n      {% if flag?(:i386) || flag?(:x86_64) %}\n        interpreter_intrinsics_pause(node: node)\n      {% end %}\n    when \"interpreter_intrinsics_bswap16\"\n      accept_call_args(node)\n      interpreter_intrinsics_bswap16(node: node)\n    when \"interpreter_intrinsics_bswap32\"\n      accept_call_args(node)\n      interpreter_intrinsics_bswap32(node: node)\n    when \"interpreter_intrinsics_bswap64\"\n      accept_call_args(node)\n      interpreter_intrinsics_bswap64(node: node)\n    when \"interpreter_intrinsics_bswap128\"\n      accept_call_args(node)\n      interpreter_intrinsics_bswap128(node: node)\n    when \"interpreter_intrinsics_read_cycle_counter\"\n      interpreter_intrinsics_read_cycle_counter(node: node)\n    when \"interpreter_intrinsics_popcount8\"\n      accept_call_args(node)\n      interpreter_intrinsics_popcount8(node: node)\n    when \"interpreter_intrinsics_popcount16\"\n      accept_call_args(node)\n      interpreter_intrinsics_popcount16(node: node)\n    when \"interpreter_intrinsics_popcount32\"\n      accept_call_args(node)\n      interpreter_intrinsics_popcount32(node: node)\n    when \"interpreter_intrinsics_popcount64\"\n      accept_call_args(node)\n      interpreter_intrinsics_popcount64(node: node)\n    when \"interpreter_intrinsics_popcount128\"\n      accept_call_args(node)\n      interpreter_intrinsics_popcount128(node: node)\n    when \"interpreter_intrinsics_countleading8\"\n      accept_call_args(node)\n      interpreter_intrinsics_countleading8(node: node)\n    when \"interpreter_intrinsics_countleading16\"\n      accept_call_args(node)\n      interpreter_intrinsics_countleading16(node: node)\n    when \"interpreter_intrinsics_countleading32\"\n      accept_call_args(node)\n      interpreter_intrinsics_countleading32(node: node)\n    when \"interpreter_intrinsics_countleading64\"\n      accept_call_args(node)\n      interpreter_intrinsics_countleading64(node: node)\n    when \"interpreter_intrinsics_countleading128\"\n      accept_call_args(node)\n      interpreter_intrinsics_countleading128(node: node)\n    when \"interpreter_intrinsics_counttrailing8\"\n      accept_call_args(node)\n      interpreter_intrinsics_counttrailing8(node: node)\n    when \"interpreter_intrinsics_counttrailing16\"\n      accept_call_args(node)\n      interpreter_intrinsics_counttrailing16(node: node)\n    when \"interpreter_intrinsics_counttrailing32\"\n      accept_call_args(node)\n      interpreter_intrinsics_counttrailing32(node: node)\n    when \"interpreter_intrinsics_counttrailing64\"\n      accept_call_args(node)\n      interpreter_intrinsics_counttrailing64(node: node)\n    when \"interpreter_intrinsics_counttrailing128\"\n      accept_call_args(node)\n      interpreter_intrinsics_counttrailing128(node: node)\n    when \"interpreter_intrinsics_bitreverse8\"\n      accept_call_args(node)\n      interpreter_intrinsics_bitreverse8(node: node)\n    when \"interpreter_intrinsics_bitreverse16\"\n      accept_call_args(node)\n      interpreter_intrinsics_bitreverse16(node: node)\n    when \"interpreter_intrinsics_bitreverse32\"\n      accept_call_args(node)\n      interpreter_intrinsics_bitreverse32(node: node)\n    when \"interpreter_intrinsics_bitreverse64\"\n      accept_call_args(node)\n      interpreter_intrinsics_bitreverse64(node: node)\n    when \"interpreter_intrinsics_bitreverse128\"\n      accept_call_args(node)\n      interpreter_intrinsics_bitreverse128(node: node)\n    when \"interpreter_intrinsics_fshl8\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshl8(node: node)\n    when \"interpreter_intrinsics_fshl16\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshl16(node: node)\n    when \"interpreter_intrinsics_fshl32\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshl32(node: node)\n    when \"interpreter_intrinsics_fshl64\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshl64(node: node)\n    when \"interpreter_intrinsics_fshl128\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshl128(node: node)\n    when \"interpreter_intrinsics_fshr8\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshr8(node: node)\n    when \"interpreter_intrinsics_fshr16\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshr16(node: node)\n    when \"interpreter_intrinsics_fshr32\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshr32(node: node)\n    when \"interpreter_intrinsics_fshr64\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshr64(node: node)\n    when \"interpreter_intrinsics_fshr128\"\n      accept_call_args(node)\n      interpreter_intrinsics_fshr128(node: node)\n    when \"interpreter_libm_ceil_f32\"\n      accept_call_args(node)\n      libm_ceil_f32 node: node\n    when \"interpreter_libm_ceil_f64\"\n      accept_call_args(node)\n      libm_ceil_f64 node: node\n    when \"interpreter_libm_cos_f32\"\n      accept_call_args(node)\n      libm_cos_f32 node: node\n    when \"interpreter_libm_cos_f64\"\n      accept_call_args(node)\n      libm_cos_f64 node: node\n    when \"interpreter_libm_exp_f32\"\n      accept_call_args(node)\n      libm_exp_f32 node: node\n    when \"interpreter_libm_exp_f64\"\n      accept_call_args(node)\n      libm_exp_f64 node: node\n    when \"interpreter_libm_exp2_f32\"\n      accept_call_args(node)\n      libm_exp2_f32 node: node\n    when \"interpreter_libm_exp2_f64\"\n      accept_call_args(node)\n      libm_exp2_f64 node: node\n    when \"interpreter_libm_floor_f32\"\n      accept_call_args(node)\n      libm_floor_f32 node: node\n    when \"interpreter_libm_floor_f64\"\n      accept_call_args(node)\n      libm_floor_f64 node: node\n    when \"interpreter_libm_fma_f32\"\n      accept_call_args(node)\n      libm_fma_f32 node: node\n    when \"interpreter_libm_fma_f64\"\n      accept_call_args(node)\n      libm_fma_f64 node: node\n    when \"interpreter_libm_log_f32\"\n      accept_call_args(node)\n      libm_log_f32 node: node\n    when \"interpreter_libm_log_f64\"\n      accept_call_args(node)\n      libm_log_f64 node: node\n    when \"interpreter_libm_log2_f32\"\n      accept_call_args(node)\n      libm_log2_f32 node: node\n    when \"interpreter_libm_log2_f64\"\n      accept_call_args(node)\n      libm_log2_f64 node: node\n    when \"interpreter_libm_log10_f32\"\n      accept_call_args(node)\n      libm_log10_f32 node: node\n    when \"interpreter_libm_log10_f64\"\n      accept_call_args(node)\n      libm_log10_f64 node: node\n    when \"interpreter_libm_round_f32\"\n      accept_call_args(node)\n      libm_round_f32 node: node\n    when \"interpreter_libm_round_f64\"\n      accept_call_args(node)\n      libm_round_f64 node: node\n    when \"interpreter_libm_rint_f32\"\n      accept_call_args(node)\n      libm_rint_f32 node: node\n    when \"interpreter_libm_rint_f64\"\n      accept_call_args(node)\n      libm_rint_f64 node: node\n    when \"interpreter_libm_sin_f32\"\n      accept_call_args(node)\n      libm_sin_f32 node: node\n    when \"interpreter_libm_sin_f64\"\n      accept_call_args(node)\n      libm_sin_f64 node: node\n    when \"interpreter_libm_sqrt_f32\"\n      accept_call_args(node)\n      libm_sqrt_f32 node: node\n    when \"interpreter_libm_sqrt_f64\"\n      accept_call_args(node)\n      libm_sqrt_f64 node: node\n    when \"interpreter_libm_trunc_f32\"\n      accept_call_args(node)\n      libm_trunc_f32 node: node\n    when \"interpreter_libm_trunc_f64\"\n      accept_call_args(node)\n      libm_trunc_f64 node: node\n    when \"interpreter_libm_powi_f32\"\n      accept_call_args(node)\n      libm_powi_f32 node: node\n    when \"interpreter_libm_powi_f64\"\n      accept_call_args(node)\n      libm_powi_f64 node: node\n    when \"interpreter_libm_min_f32\"\n      accept_call_args(node)\n      libm_min_f32 node: node\n    when \"interpreter_libm_min_f64\"\n      accept_call_args(node)\n      libm_min_f64 node: node\n    when \"interpreter_libm_max_f32\"\n      accept_call_args(node)\n      libm_max_f32 node: node\n    when \"interpreter_libm_max_f64\"\n      accept_call_args(node)\n      libm_max_f64 node: node\n    when \"interpreter_libm_pow_f32\"\n      accept_call_args(node)\n      libm_pow_f32 node: node\n    when \"interpreter_libm_pow_f64\"\n      accept_call_args(node)\n      libm_pow_f64 node: node\n    when \"interpreter_libm_copysign_f32\"\n      accept_call_args(node)\n      libm_copysign_f32 node: node\n    when \"interpreter_libm_copysign_f64\"\n      accept_call_args(node)\n      libm_copysign_f64 node: node\n    else\n      node.raise \"BUG: missing handling of primitive #{body.name}\"\n    end\n  end\n\n  private def accept_call_args(node : Call)\n    node.args.each { |arg| request_value(arg) }\n  end\n\n  private def primitive_convert(node : ASTNode, body : Primitive, owner : Type, checked : Bool)\n    obj = node.obj\n\n    unless @wants_value\n      discard_value(obj) if obj\n      return\n    end\n\n    obj_type = owner\n    request_obj_or_self_and_cast_if_needed(node, obj, obj_type)\n\n    target_type = body.type\n\n    primitive_convert(node, obj_type, target_type, checked: checked)\n  end\n\n  private def primitive_convert(node : ASTNode, from_type : IntegerType | FloatType, to_type : IntegerType | FloatType, checked : Bool)\n    from_kind = integer_or_float_kind(from_type)\n    to_kind = integer_or_float_kind(to_type)\n\n    unless from_kind && to_kind\n      node.raise \"BUG: missing handling of unchecked_convert for #{from_type} (#{node})\"\n    end\n\n    primitive_convert(node, from_kind, to_kind, checked: checked)\n  end\n\n  private def primitive_convert(node : ASTNode, from_type : CharType, to_type : IntegerType, checked : Bool)\n    # This is Char#ord\n    nop\n  end\n\n  private def primitive_convert(node : ASTNode, from_type : IntegerType, to_type : CharType, checked : Bool)\n    primitive_convert(node, from_type, @context.program.int32, checked: checked)\n  end\n\n  private def primitive_convert(node : ASTNode, from_type : SymbolType, to_type : IntegerType, checked : Bool)\n    # This is Symbol#to_i, but a symbol is already represented as an Int32\n    nop\n  end\n\n  private def primitive_convert(node : ASTNode, from_type : Type, to_type : Type, checked : Bool)\n    node.raise \"BUG: missing handling of convert from #{from_type} to #{to_type}\"\n  end\n\n  private def primitive_convert(node : ASTNode, from_kind : NumberKind, to_kind : NumberKind, checked : Bool)\n    # Most of these are nop because we align the stack to 64 bits,\n    # so numbers are already converted to 64 bits\n    case {from_kind, to_kind}\n    in {.i8?, .i8?}     then nop\n    in {.i8?, .i16?}    then sign_extend(7, node: node)\n    in {.i8?, .i32?}    then sign_extend(7, node: node)\n    in {.i8?, .i64?}    then sign_extend(7, node: node)\n    in {.i8?, .i128?}   then sign_extend(15, node: node)\n    in {.i8?, .u8?}     then checked ? (sign_extend(7, node: node); i64_to_u8(node: node)) : nop\n    in {.i8?, .u16?}    then sign_extend(7, node: node); checked ? i64_to_u16(node: node) : nop\n    in {.i8?, .u32?}    then sign_extend(7, node: node); checked ? i64_to_u32(node: node) : nop\n    in {.i8?, .u64?}    then sign_extend(7, node: node); checked ? i64_to_u64(node: node) : nop\n    in {.i8?, .u128?}   then sign_extend(15, node: node); checked ? i128_to_u128(node: node) : nop\n    in {.i8?, .f32?}    then sign_extend(7, node: node); i64_to_f32(node: node)\n    in {.i8?, .f64?}    then sign_extend(7, node: node); i64_to_f64(node: node)\n    in {.u8?, .i8?}     then zero_extend(7, node: node); checked ? u64_to_i8(node: node) : nop\n    in {.u8?, .i16?}    then zero_extend(7, node: node)\n    in {.u8?, .i32?}    then zero_extend(7, node: node)\n    in {.u8?, .i64?}    then zero_extend(7, node: node)\n    in {.u8?, .i128?}   then zero_extend(15, node: node)\n    in {.u8?, .u8?}     then nop\n    in {.u8?, .u16?}    then zero_extend(7, node: node)\n    in {.u8?, .u32?}    then zero_extend(7, node: node)\n    in {.u8?, .u64?}    then zero_extend(7, node: node)\n    in {.u8?, .u128?}   then zero_extend(15, node: node)\n    in {.u8?, .f32?}    then zero_extend(7, node: node); u64_to_f32(node: node)\n    in {.u8?, .f64?}    then zero_extend(7, node: node); u64_to_f64(node: node)\n    in {.i16?, .i8?}    then checked ? (sign_extend(6, node: node); i64_to_i8(node: node)) : nop\n    in {.i16?, .i16?}   then nop\n    in {.i16?, .i32?}   then sign_extend(6, node: node)\n    in {.i16?, .i64?}   then sign_extend(6, node: node)\n    in {.i16?, .i128?}  then sign_extend(14, node: node)\n    in {.i16?, .u8?}    then checked ? (sign_extend(6, node: node); i64_to_u8(node: node)) : nop\n    in {.i16?, .u16?}   then checked ? (sign_extend(6, node: node); i64_to_u16(node: node)) : nop\n    in {.i16?, .u32?}   then sign_extend(6, node: node); checked ? i64_to_u32(node: node) : nop\n    in {.i16?, .u64?}   then sign_extend(6, node: node); checked ? i64_to_u64(node: node) : nop\n    in {.i16?, .u128?}  then sign_extend(14, node: node); checked ? i128_to_u128(node: node) : nop\n    in {.i16?, .f32?}   then sign_extend(6, node: node); i64_to_f32(node: node)\n    in {.i16?, .f64?}   then sign_extend(6, node: node); i64_to_f64(node: node)\n    in {.u16?, .i8?}    then checked ? (zero_extend(6, node: node); u64_to_i8(node: node)) : nop\n    in {.u16?, .i16?}   then checked ? (zero_extend(6, node: node); u64_to_i16(node: node)) : nop\n    in {.u16?, .i32?}   then zero_extend(6, node: node)\n    in {.u16?, .i64?}   then zero_extend(6, node: node)\n    in {.u16?, .i128?}  then zero_extend(14, node: node)\n    in {.u16?, .u8?}    then checked ? (zero_extend(6, node: node); u64_to_u8(node: node)) : nop\n    in {.u16?, .u16?}   then nop\n    in {.u16?, .u32?}   then zero_extend(6, node: node)\n    in {.u16?, .u64?}   then zero_extend(6, node: node)\n    in {.u16?, .u128?}  then zero_extend(14, node: node)\n    in {.u16?, .f32?}   then zero_extend(6, node: node); u64_to_f32(node: node)\n    in {.u16?, .f64?}   then zero_extend(6, node: node); u64_to_f64(node: node)\n    in {.i32?, .i8?}    then checked ? (sign_extend(4, node: node); i64_to_i8(node: node)) : nop\n    in {.i32?, .i16?}   then checked ? (sign_extend(4, node: node); i64_to_i16(node: node)) : nop\n    in {.i32?, .i32?}   then nop\n    in {.i32?, .i64?}   then sign_extend(4, node: node)\n    in {.i32?, .i128?}  then sign_extend(12, node: node)\n    in {.i32?, .u8?}    then checked ? (sign_extend(4, node: node); i64_to_u8(node: node)) : nop\n    in {.i32?, .u16?}   then checked ? (sign_extend(4, node: node); i64_to_u16(node: node)) : nop\n    in {.i32?, .u32?}   then checked ? (sign_extend(4, node: node); i64_to_u32(node: node)) : nop\n    in {.i32?, .u64?}   then checked ? (sign_extend(4, node: node); i64_to_u64(node: node)) : sign_extend(4, node: node)\n    in {.i32?, .u128?}  then checked ? (sign_extend(12, node: node); i128_to_u128(node: node)) : sign_extend(12, node: node)\n    in {.i32?, .f32?}   then sign_extend(4, node: node); i64_to_f32(node: node)\n    in {.i32?, .f64?}   then sign_extend(4, node: node); i64_to_f64(node: node)\n    in {.u32?, .i8?}    then checked ? (zero_extend(4, node: node); u64_to_i8(node: node)) : nop\n    in {.u32?, .i16?}   then checked ? (zero_extend(4, node: node); u64_to_i16(node: node)) : nop\n    in {.u32?, .i32?}   then checked ? (zero_extend(4, node: node); u64_to_i32(node: node)) : nop\n    in {.u32?, .i64?}   then zero_extend(4, node: node)\n    in {.u32?, .i128?}  then zero_extend(12, node: node)\n    in {.u32?, .u8?}    then checked ? (zero_extend(4, node: node); u64_to_u8(node: node)) : nop\n    in {.u32?, .u16?}   then checked ? (zero_extend(4, node: node); u64_to_u16(node: node)) : nop\n    in {.u32?, .u32?}   then nop\n    in {.u32?, .u64?}   then zero_extend(4, node: node)\n    in {.u32?, .u128?}  then zero_extend(12, node: node)\n    in {.u32?, .f32?}   then zero_extend(4, node: node); u64_to_f32(node: node)\n    in {.u32?, .f64?}   then zero_extend(4, node: node); u64_to_f64(node: node)\n    in {.i64?, .i8?}    then checked ? i64_to_i8(node: node) : nop\n    in {.i64?, .i16?}   then checked ? i64_to_i16(node: node) : nop\n    in {.i64?, .i32?}   then checked ? i64_to_i32(node: node) : nop\n    in {.i64?, .i64?}   then nop\n    in {.i64?, .i128?}  then sign_extend(8, node: node)\n    in {.i64?, .u8?}    then checked ? i64_to_u8(node: node) : nop\n    in {.i64?, .u16?}   then checked ? i64_to_u16(node: node) : nop\n    in {.i64?, .u32?}   then checked ? i64_to_u32(node: node) : nop\n    in {.i64?, .u64?}   then checked ? i64_to_u64(node: node) : nop\n    in {.i64?, .u128?}  then checked ? (sign_extend(8, node: node); i128_to_u128(node: node)) : sign_extend(8, node: node)\n    in {.i64?, .f32?}   then i64_to_f32(node: node)\n    in {.i64?, .f64?}   then i64_to_f64(node: node)\n    in {.u64?, .i8?}    then checked ? u64_to_i8(node: node) : nop\n    in {.u64?, .i16?}   then checked ? u64_to_i16(node: node) : nop\n    in {.u64?, .i32?}   then checked ? u64_to_i32(node: node) : nop\n    in {.u64?, .i64?}   then checked ? u64_to_i64(node: node) : nop\n    in {.u64?, .i128?}  then zero_extend(8, node: node)\n    in {.u64?, .u8?}    then checked ? u64_to_u8(node: node) : nop\n    in {.u64?, .u16?}   then checked ? u64_to_u16(node: node) : nop\n    in {.u64?, .u32?}   then checked ? u64_to_u32(node: node) : nop\n    in {.u64?, .u64?}   then nop\n    in {.u64?, .u128?}  then zero_extend(8, node: node)\n    in {.u64?, .f32?}   then u64_to_f32(node: node)\n    in {.u64?, .f64?}   then u64_to_f64(node: node)\n    in {.i128?, .i8?}   then checked ? i128_to_i8(node: node) : pop(8, node: node)\n    in {.i128?, .i16?}  then checked ? i128_to_i16(node: node) : pop(8, node: node)\n    in {.i128?, .i32?}  then checked ? i128_to_i32(node: node) : pop(8, node: node)\n    in {.i128?, .i64?}  then checked ? i128_to_i64(node: node) : pop(8, node: node)\n    in {.i128?, .i128?} then nop\n    in {.i128?, .u8?}   then checked ? i128_to_u8(node: node) : pop(8, node: node)\n    in {.i128?, .u16?}  then checked ? i128_to_u16(node: node) : pop(8, node: node)\n    in {.i128?, .u32?}  then checked ? i128_to_u32(node: node) : pop(8, node: node)\n    in {.i128?, .u64?}  then checked ? i128_to_u64(node: node) : pop(8, node: node)\n    in {.i128?, .u128?} then checked ? i128_to_u128(node: node) : nop\n    in {.i128?, .f32?}  then i128_to_f32(node: node)\n    in {.i128?, .f64?}  then i128_to_f64(node: node)\n    in {.u128?, .i8?}   then checked ? u128_to_i8(node: node) : pop(8, node: node)\n    in {.u128?, .i16?}  then checked ? u128_to_i16(node: node) : pop(8, node: node)\n    in {.u128?, .i32?}  then checked ? u128_to_i32(node: node) : pop(8, node: node)\n    in {.u128?, .i64?}  then checked ? u128_to_i64(node: node) : pop(8, node: node)\n    in {.u128?, .i128?} then checked ? u128_to_i128(node: node) : nop\n    in {.u128?, .u8?}   then checked ? u128_to_u8(node: node) : pop(8, node: node)\n    in {.u128?, .u16?}  then checked ? u128_to_u16(node: node) : pop(8, node: node)\n    in {.u128?, .u32?}  then checked ? u128_to_u32(node: node) : pop(8, node: node)\n    in {.u128?, .u64?}  then checked ? u128_to_u64(node: node) : pop(8, node: node)\n    in {.u128?, .u128?} then nop\n    in {.u128?, .f32?}  then checked ? u128_to_f32(node: node) : u128_to_f32_bang(node: node)\n    in {.u128?, .f64?}  then u128_to_f64(node: node)\n    in {.f32?, .i8?}    then f32_to_f64(node: node); checked ? f64_to_i8(node: node) : f64_to_i64_bang(node: node)\n    in {.f32?, .i16?}   then f32_to_f64(node: node); checked ? f64_to_i16(node: node) : f64_to_i64_bang(node: node)\n    in {.f32?, .i32?}   then f32_to_f64(node: node); checked ? f64_to_i32(node: node) : f64_to_i64_bang(node: node)\n    in {.f32?, .i64?}   then f32_to_f64(node: node); checked ? f64_to_i64(node: node) : f64_to_i64_bang(node: node)\n    in {.f32?, .i128?}  then f32_to_f64(node: node); checked ? f64_to_i128(node: node) : f64_to_i128_bang(node: node)\n    in {.f32?, .u8?}    then f32_to_f64(node: node); checked ? f64_to_u8(node: node) : f64_to_i64_bang(node: node)\n    in {.f32?, .u16?}   then f32_to_f64(node: node); checked ? f64_to_u16(node: node) : f64_to_i64_bang(node: node)\n    in {.f32?, .u32?}   then checked ? (f32_to_f64(node: node); f64_to_u32(node: node)) : f32_to_u32_bang(node: node)\n    in {.f32?, .u64?}   then checked ? (f32_to_f64(node: node); f64_to_u64(node: node)) : f32_to_u64_bang(node: node)\n    in {.f32?, .u128?}  then f32_to_f64(node: node); checked ? f64_to_u128(node: node) : f64_to_i128_bang(node: node)\n    in {.f32?, .f32?}   then nop\n    in {.f32?, .f64?}   then f32_to_f64(node: node)\n    in {.f64?, .i8?}    then checked ? f64_to_i8(node: node) : f64_to_i64_bang(node: node)\n    in {.f64?, .i16?}   then checked ? f64_to_i16(node: node) : f64_to_i64_bang(node: node)\n    in {.f64?, .i32?}   then checked ? f64_to_i32(node: node) : f64_to_i64_bang(node: node)\n    in {.f64?, .i64?}   then checked ? f64_to_i64(node: node) : f64_to_i64_bang(node: node)\n    in {.f64?, .i128?}  then checked ? f64_to_i128(node: node) : f64_to_i128_bang(node: node)\n    in {.f64?, .u8?}    then checked ? f64_to_u8(node: node) : f64_to_i64_bang(node: node)\n    in {.f64?, .u16?}   then checked ? f64_to_u16(node: node) : f64_to_i64_bang(node: node)\n    in {.f64?, .u32?}   then checked ? f64_to_u32(node: node) : f64_to_u32_bang(node: node)\n    in {.f64?, .u64?}   then checked ? f64_to_u64(node: node) : f64_to_u64_bang(node: node)\n    in {.f64?, .u128?}  then checked ? f64_to_u128(node: node) : f64_to_i128_bang(node: node)\n    in {.f64?, .f32?}   then checked ? f64_to_f32(node: node) : f64_to_f32_bang(node: node)\n    in {.f64?, .f64?}   then nop\n    end\n  end\n\n  private def primitive_binary(node, body, owner)\n    unless @wants_value\n      accept_call_members(node)\n      return\n    end\n\n    case node.name\n    when \"+\", \"&+\", \"-\", \"&-\", \"*\", \"&*\", \"^\", \"|\", \"&\", \"unsafe_shl\", \"unsafe_shr\", \"unsafe_div\", \"unsafe_mod\"\n      primitive_binary_op_math(node, body, owner, node.name)\n    when \"<\", \"<=\", \">\", \">=\", \"==\", \"!=\"\n      primitive_binary_op_cmp(node, body, owner, node.name)\n    when \"/\", \"fdiv\"\n      primitive_binary_float_div(node, body, owner)\n    else\n      node.raise \"BUG: missing handling of binary op #{node.name}\"\n    end\n  end\n\n  private def primitive_binary_op_math(node : ASTNode, body : Primitive, owner : Type, op : String)\n    obj = node.obj\n    arg = node.args.first\n\n    obj_type = owner\n    arg_type = arg.type\n\n    primitive_binary_op_math(obj_type, arg_type, obj, arg, node, op)\n  end\n\n  private def primitive_binary_op_math(left_type : IntegerType, right_type : IntegerType, left_node : ASTNode?, right_node : ASTNode, node : ASTNode, op : String)\n    kind = extend_int(left_type, right_type, left_node, right_node, node)\n    if kind.is_a?(MixedNumberKind)\n      case kind\n      in .mixed64?\n        if left_type.rank > right_type.rank\n          # It's UInt64 op X where X is a signed integer\n          request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n          right_node.accept self\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert(node, right_type.kind, :i64, checked: false)\n\n          case node.name\n          when \"+\"          then add_u64_i64(node: node)\n          when \"&+\"         then add_wrap_i64(node: node)\n          when \"-\"          then sub_u64_i64(node: node)\n          when \"&-\"         then sub_wrap_i64(node: node)\n          when \"*\"          then mul_u64_i64(node: node)\n          when \"&*\"         then mul_wrap_i64(node: node)\n          when \"^\"          then xor_i64(node: node)\n          when \"|\"          then or_i64(node: node)\n          when \"&\"          then and_i64(node: node)\n          when \"unsafe_shl\" then unsafe_shl_i64(node: node)\n          when \"unsafe_shr\" then unsafe_shr_u64_i64(node: node)\n          when \"unsafe_div\" then unsafe_div_u64_i64(node: node)\n          when \"unsafe_mod\" then unsafe_mod_u64_i64(node: node)\n          else\n            node.raise \"BUG: missing handling of binary #{op} with types #{left_type} and #{right_type}\"\n          end\n\n          kind = NumberKind::U64\n        else\n          # It's X op UInt64 where X is a signed integer\n          request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert(node, left_type.kind, :i64, checked: false)\n          right_node.accept self\n\n          case node.name\n          when \"+\"          then add_i64_u64(node: node)\n          when \"&+\"         then add_wrap_i64(node: node)\n          when \"-\"          then sub_i64_u64(node: node)\n          when \"&-\"         then sub_wrap_i64(node: node)\n          when \"*\"          then mul_i64_u64(node: node)\n          when \"&*\"         then mul_wrap_i64(node: node)\n          when \"^\"          then xor_i64(node: node)\n          when \"|\"          then or_i64(node: node)\n          when \"&\"          then and_i64(node: node)\n          when \"unsafe_shl\" then unsafe_shl_i64(node: node)\n          when \"unsafe_shr\" then unsafe_shr_i64_u64(node: node)\n          when \"unsafe_div\" then unsafe_div_i64_u64(node: node)\n          when \"unsafe_mod\" then unsafe_mod_i64_u64(node: node)\n          else\n            node.raise \"BUG: missing handling of binary #{op} with types #{left_type} and #{right_type}\"\n          end\n\n          kind = NumberKind::I64\n        end\n      in .mixed128?\n        if left_type.rank > right_type.rank\n          # It's UInt128 op X where X is a signed integer\n          request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n          right_node.accept self\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert(node, right_type.kind, :i128, checked: false)\n\n          case node.name\n          when \"+\"          then add_u128_i128(node: node)\n          when \"&+\"         then add_wrap_i128(node: node)\n          when \"-\"          then sub_u128_i128(node: node)\n          when \"&-\"         then sub_wrap_i128(node: node)\n          when \"*\"          then mul_u128_i128(node: node)\n          when \"&*\"         then mul_wrap_i128(node: node)\n          when \"^\"          then xor_i128(node: node)\n          when \"|\"          then or_i128(node: node)\n          when \"&\"          then and_i128(node: node)\n          when \"unsafe_shl\" then unsafe_shl_i128(node: node)\n          when \"unsafe_shr\" then unsafe_shr_u128_i128(node: node)\n          when \"unsafe_div\" then unsafe_div_u128_i128(node: node)\n          when \"unsafe_mod\" then unsafe_mod_u128_i128(node: node)\n          else\n            node.raise \"BUG: missing handling of binary #{op} with types #{left_type} and #{right_type}\"\n          end\n\n          kind = NumberKind::U128\n        else\n          # It's X op UInt128 where X is a signed integer\n          request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert(node, left_type.kind, :i128, checked: false)\n          right_node.accept self\n\n          case node.name\n          when \"+\"          then add_i128_u128(node: node)\n          when \"&+\"         then add_wrap_i128(node: node)\n          when \"-\"          then sub_i128_u128(node: node)\n          when \"&-\"         then sub_wrap_i128(node: node)\n          when \"*\"          then mul_i128_u128(node: node)\n          when \"&*\"         then mul_wrap_i128(node: node)\n          when \"^\"          then xor_i128(node: node)\n          when \"|\"          then or_i128(node: node)\n          when \"&\"          then and_i128(node: node)\n          when \"unsafe_shl\" then unsafe_shl_i128(node: node)\n          when \"unsafe_shr\" then unsafe_shr_i128_u128(node: node)\n          when \"unsafe_div\" then unsafe_div_i128_u128(node: node)\n          when \"unsafe_mod\" then unsafe_mod_i128_u128(node: node)\n          else\n            node.raise \"BUG: missing handling of binary #{op} with types #{left_type} and #{right_type}\"\n          end\n\n          kind = NumberKind::I128\n        end\n      end\n    else\n      # Go on\n      return false unless @wants_value\n\n      primitive_binary_op_math(node, kind, op)\n    end\n\n    if kind != left_type.kind\n      checked = node.name.in?(\"+\", \"-\", \"*\")\n      primitive_convert(node, kind, left_type.kind, checked: checked)\n    end\n  end\n\n  private def primitive_binary_op_math(left_type : IntegerType, right_type : FloatType, left_node : ASTNode?, right_node : ASTNode, node : ASTNode, op : String)\n    request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n    primitive_convert node, left_type.kind, right_type.kind, checked: false\n    right_node.accept self\n\n    primitive_binary_op_math(node, right_type.kind, op)\n  end\n\n  private def primitive_binary_op_math(left_type : FloatType, right_type : IntegerType, left_node : ASTNode?, right_node : ASTNode, node : ASTNode, op : String)\n    request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n    right_node.accept self\n    primitive_convert right_node, right_type.kind, left_type.kind, checked: false\n\n    primitive_binary_op_math(node, left_type.kind, op)\n  end\n\n  private def primitive_binary_op_math(left_type : FloatType, right_type : FloatType, left_node : ASTNode?, right_node : ASTNode, node : ASTNode, op : String)\n    if left_type == right_type\n      request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n      right_node.accept self\n      kind = left_type.kind\n    elsif left_type.rank < right_type.rank\n      # TODO: not tested\n      request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n      primitive_convert node, left_type.kind, right_type.kind, checked: false\n      right_node.accept self\n      kind = right_type.kind\n    else\n      # TODO: not tested\n      request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n      right_node.accept self\n      primitive_convert right_node, right_type.kind, left_type.kind, checked: false\n      kind = left_type.kind\n    end\n\n    primitive_binary_op_math(node, kind, op)\n\n    if kind != left_type.kind\n      primitive_convert(node, kind, left_type.kind, checked: false)\n    end\n  end\n\n  private def primitive_binary_op_math(node : ASTNode, kind : NumberKind, op : String)\n    case kind\n    when .i32?\n      case op\n      when \"+\"          then add_i32(node: node)\n      when \"&+\"         then add_wrap_i32(node: node)\n      when \"-\"          then sub_i32(node: node)\n      when \"&-\"         then sub_wrap_i32(node: node)\n      when \"*\"          then mul_i32(node: node)\n      when \"&*\"         then mul_wrap_i32(node: node)\n      when \"^\"          then xor_i32(node: node)\n      when \"|\"          then or_i32(node: node)\n      when \"&\"          then and_i32(node: node)\n      when \"unsafe_shl\" then unsafe_shl_i32(node: node)\n      when \"unsafe_shr\" then unsafe_shr_i32(node: node)\n      when \"unsafe_div\" then unsafe_div_i32(node: node)\n      when \"unsafe_mod\" then unsafe_mod_i32(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .u32?\n      case op\n      when \"+\"          then add_u32(node: node)\n      when \"&+\"         then add_wrap_i32(node: node)\n      when \"-\"          then sub_u32(node: node)\n      when \"&-\"         then sub_wrap_i32(node: node)\n      when \"*\"          then mul_u32(node: node)\n      when \"&*\"         then mul_wrap_i32(node: node)\n      when \"^\"          then xor_i32(node: node)\n      when \"|\"          then or_i32(node: node)\n      when \"&\"          then and_i32(node: node)\n      when \"unsafe_shl\" then unsafe_shl_i32(node: node)\n      when \"unsafe_shr\" then unsafe_shr_u32(node: node)\n      when \"unsafe_div\" then unsafe_div_u32(node: node)\n      when \"unsafe_mod\" then unsafe_mod_u32(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .i64?\n      case op\n      when \"+\"          then add_i64(node: node)\n      when \"&+\"         then add_wrap_i64(node: node)\n      when \"-\"          then sub_i64(node: node)\n      when \"&-\"         then sub_wrap_i64(node: node)\n      when \"*\"          then mul_i64(node: node)\n      when \"&*\"         then mul_wrap_i64(node: node)\n      when \"^\"          then xor_i64(node: node)\n      when \"|\"          then or_i64(node: node)\n      when \"&\"          then and_i64(node: node)\n      when \"unsafe_shl\" then unsafe_shl_i64(node: node)\n      when \"unsafe_shr\" then unsafe_shr_i64(node: node)\n      when \"unsafe_div\" then unsafe_div_i64(node: node)\n      when \"unsafe_mod\" then unsafe_mod_i64(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .u64?\n      case op\n      when \"+\"          then add_u64(node: node)\n      when \"&+\"         then add_wrap_i64(node: node)\n      when \"-\"          then sub_u64(node: node)\n      when \"&-\"         then sub_wrap_i64(node: node)\n      when \"*\"          then mul_u64(node: node)\n      when \"&*\"         then mul_wrap_i64(node: node)\n      when \"^\"          then xor_i64(node: node)\n      when \"|\"          then or_i64(node: node)\n      when \"&\"          then and_i64(node: node)\n      when \"unsafe_shl\" then unsafe_shl_i64(node: node)\n      when \"unsafe_shr\" then unsafe_shr_u64(node: node)\n      when \"unsafe_div\" then unsafe_div_u64(node: node)\n      when \"unsafe_mod\" then unsafe_mod_u64(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .i128?\n      case op\n      when \"+\"          then add_i128(node: node)\n      when \"&+\"         then add_wrap_i128(node: node)\n      when \"-\"          then sub_i128(node: node)\n      when \"&-\"         then sub_wrap_i128(node: node)\n      when \"*\"          then mul_i128(node: node)\n      when \"&*\"         then mul_wrap_i128(node: node)\n      when \"^\"          then xor_i128(node: node)\n      when \"|\"          then or_i128(node: node)\n      when \"&\"          then and_i128(node: node)\n      when \"unsafe_shl\" then unsafe_shl_i128(node: node)\n      when \"unsafe_shr\" then unsafe_shr_i128(node: node)\n      when \"unsafe_div\" then unsafe_div_i128(node: node)\n      when \"unsafe_mod\" then unsafe_mod_i128(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .u128?\n      case op\n      when \"+\"          then add_u128(node: node)\n      when \"&+\"         then add_wrap_i128(node: node)\n      when \"-\"          then sub_u128(node: node)\n      when \"&-\"         then sub_wrap_i128(node: node)\n      when \"*\"          then mul_u128(node: node)\n      when \"&*\"         then mul_wrap_i128(node: node)\n      when \"^\"          then xor_i128(node: node)\n      when \"|\"          then or_i128(node: node)\n      when \"&\"          then and_i128(node: node)\n      when \"unsafe_shl\" then unsafe_shl_i128(node: node)\n      when \"unsafe_shr\" then unsafe_shr_u128(node: node)\n      when \"unsafe_div\" then unsafe_div_u128(node: node)\n      when \"unsafe_mod\" then unsafe_mod_u128(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .f32?\n      # TODO: not tested\n      case op\n      when \"+\" then add_f32(node: node)\n      when \"-\" then sub_f32(node: node)\n      when \"*\" then mul_f32(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    when .f64?\n      case op\n      when \"+\" then add_f64(node: node)\n      when \"-\" then sub_f64(node: node)\n      when \"*\" then mul_f64(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n      end\n    else\n      node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n    end\n  end\n\n  private def primitive_binary_op_math(left_type : Type, right_type : Type, left_node : ASTNode?, right_node : ASTNode, node : ASTNode, op : String)\n    node.raise \"BUG: primitive_binary_op_math called with #{left_type} #{op} #{right_type}\"\n  end\n\n  private def primitive_binary_op_cmp(node : ASTNode, body : Primitive, owner : Type, op : String)\n    obj = node.obj.not_nil!\n    arg = node.args.first\n\n    obj_type = owner\n    arg_type = arg.type\n\n    primitive_binary_op_cmp(obj_type, arg_type, obj, arg, node, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : BoolType, right_type : BoolType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    left_node.accept self\n    right_node.accept self\n\n    cmp_i32(node: node)\n    primitive_binary_op_cmp_op(node, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : CharType, right_type : CharType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    left_node.accept self\n    right_node.accept self\n\n    cmp_i32(node: node)\n    primitive_binary_op_cmp_op(node, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : SymbolType, right_type : SymbolType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    left_node.accept self\n    right_node.accept self\n\n    cmp_i32(node: node)\n    primitive_binary_op_cmp_op(node, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : IntegerType, right_type : IntegerType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    kind = extend_int(left_type, right_type, left_node, right_node, node)\n    if kind.is_a?(MixedNumberKind)\n      case kind\n      in .mixed64?\n        if left_type.rank > right_type.rank\n          # It's UInt64 == X where X is a signed integer.\n\n          # We first extend right to left\n          left_node.accept self\n          right_node.accept self\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert right_node, right_type.kind, :i64, checked: false\n\n          cmp_u64_i64(node: node)\n        else\n          # It's X < UInt64 where X is a signed integer\n          left_node.accept self\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert left_node, left_type.kind, :i64, checked: false\n\n          right_node.accept self\n\n          cmp_i64_u64(node: node)\n        end\n      in .mixed128?\n        if left_type.rank > right_type.rank\n          # It's UInt128 == X where X is a signed integer.\n\n          # We first extend right to left\n          left_node.accept self\n          right_node.accept self\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert right_node, right_type.kind, :i128, checked: false\n\n          cmp_u128_i128(node: node)\n        else\n          # It's X < UInt128 where X is a signed integer\n          left_node.accept self\n\n          # TODO: do we need to check for overflow here?\n          primitive_convert left_node, left_type.kind, :i128, checked: false\n\n          right_node.accept self\n\n          cmp_i128_u128(node: node)\n        end\n      end\n    else\n      case kind\n      when .i32?  then cmp_i32(node: node)\n      when .u32?  then cmp_u32(node: node)\n      when .i64?  then cmp_i64(node: node)\n      when .u64?  then cmp_u64(node: node)\n      when .i128? then cmp_i128(node: node)\n      when .u128? then cmp_u128(node: node)\n      else\n        node.raise \"BUG: missing handling of binary #{op} for #{kind}\"\n      end\n    end\n\n    primitive_binary_op_cmp_op(node, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : FloatType, right_type : IntegerType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    left_node.accept self\n    right_node.accept self\n    primitive_convert right_node, right_type.kind, left_type.kind, checked: false\n\n    primitive_binary_op_cmp_float(node, left_type.kind, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : IntegerType, right_type : FloatType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    left_node.accept self\n    primitive_convert(left_node, left_type.kind, right_type.kind, checked: false)\n    right_node.accept self\n\n    primitive_binary_op_cmp_float(node, right_type.kind, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : FloatType, right_type : FloatType, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    if left_type == right_type\n      left_node.accept self\n      right_node.accept self\n\n      kind = left_type.kind\n    elsif left_type.rank < right_type.rank\n      left_node.accept self\n      primitive_convert(left_node, left_type.kind, right_type.kind, checked: false)\n      right_node.accept self\n\n      kind = NumberKind::F64\n    else\n      left_node.accept self\n      right_node.accept self\n      primitive_convert(right_node, right_type.kind, left_type.kind, checked: false)\n\n      kind = NumberKind::F64\n    end\n\n    primitive_binary_op_cmp_float(node, kind, op)\n  end\n\n  private def primitive_binary_op_cmp(left_type : Type, right_type : Type, left_node : ASTNode, right_node : ASTNode, node : ASTNode, op : String)\n    left_node.raise \"BUG: primitive_binary_op_cmp called with #{left_type} #{op} #{right_type}\"\n  end\n\n  private def primitive_binary_op_cmp_float(node : ASTNode, kind : NumberKind, op : String)\n    if predicate = FloatPredicate.from_method?(op)\n      case kind\n      when .f32? then return cmp_f32(predicate, node: node)\n      when .f64? then return cmp_f64(predicate, node: node)\n      end\n    end\n\n    node.raise \"BUG: missing handling of binary #{op} with kind #{kind}\"\n  end\n\n  # TODO: should integer comparisons also use `FloatPredicate`?\n  private def primitive_binary_op_cmp_op(node : ASTNode, op : String)\n    case op\n    when \"==\" then cmp_eq(node: node)\n    when \"!=\" then cmp_neq(node: node)\n    when \"<\"  then cmp_lt(node: node)\n    when \"<=\" then cmp_le(node: node)\n    when \">\"  then cmp_gt(node: node)\n    when \">=\" then cmp_ge(node: node)\n    else\n      node.raise \"BUG: missing handling of binary #{op}\"\n    end\n  end\n\n  # interpreter-exclusive flags for `cmp_f32` and `cmp_f64`\n  # currently compatible with `LLVM::RealPredicate`\n  @[Flags]\n  enum FloatPredicate : UInt8\n    Equal\n    GreaterThan\n    LessThan\n    Unordered\n\n    def self.from_method?(op : String)\n      case op\n      when \"==\" then Equal\n      when \"!=\" then LessThan | GreaterThan | Unordered\n      when \"<\"  then LessThan\n      when \"<=\" then LessThan | Equal\n      when \">\"  then GreaterThan\n      when \">=\" then GreaterThan | Equal\n      end\n    end\n\n    def compare(x, y) : Bool\n      (equal? && x == y) ||\n        (greater_than? && x > y) ||\n        (less_than? && x < y) ||\n        (unordered? && (x.nan? || y.nan?))\n    end\n  end\n\n  # interpreter-exclusive integer unions\n  private enum MixedNumberKind\n    # Int64 | UInt64\n    Mixed64\n\n    # Int128 | UInt128\n    Mixed128\n  end\n\n  private def extend_int(left_type : IntegerType, right_type : IntegerType, left_node : ASTNode?, right_node : ASTNode, node : ASTNode)\n    # We don't do operations \"below\" Int32, we always cast the values\n    # to at least Int32. This might be slightly slower, but it allows\n    # us to need less opcodes in the bytecode.\n    if left_type.rank <= 5 && right_type.rank <= 5\n      # If both fit in an Int32\n      # Convert them to Int32 first, then do the comparison\n      request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n      primitive_convert(left_node || right_node, left_type.kind, :i32, checked: false) if left_type.rank < 5\n\n      right_node.accept self\n      primitive_convert(right_node, right_type.kind, :i32, checked: false) if right_type.rank < 5\n\n      NumberKind::I32\n    elsif left_type.signed? == right_type.signed?\n      if left_type.rank == right_type.rank\n        request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n        right_node.accept self\n        left_type.kind\n      elsif left_type.rank < right_type.rank\n        request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n        primitive_convert(left_node || right_node, left_type.kind, right_type.kind, checked: false)\n        right_node.accept self\n        right_type.kind\n      else\n        request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n        right_node.accept self\n        primitive_convert(right_node, right_type.kind, left_type.kind, checked: false)\n        left_type.kind\n      end\n    elsif left_type.rank <= 7 && right_type.rank <= 7\n      # If both fit in an Int64\n      # Convert them to Int64 first, then do the comparison\n      request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n      primitive_convert(left_node || right_node, left_type.kind, :i64, checked: false) if left_type.rank < 7\n\n      right_node.accept self\n      primitive_convert(right_node, right_type.kind, :i64, checked: false) if right_type.rank < 7\n\n      NumberKind::I64\n    elsif left_type.rank <= 8 && right_type.rank <= 8\n      MixedNumberKind::Mixed64\n    elsif left_type.rank <= 9 && right_type.rank <= 9\n      # If both fit in an Int128\n      # Convert them to Int128 first, then do the comparison\n      request_obj_or_self_and_cast_if_needed(node, left_node, left_type)\n      primitive_convert(left_node || right_node, left_type.kind, :i128, checked: false) if left_type.rank < 9\n\n      right_node.accept self\n      primitive_convert(right_node, right_type.kind, :i128, checked: false) if right_type.rank < 9\n\n      NumberKind::I128\n    else\n      MixedNumberKind::Mixed128\n    end\n  end\n\n  private def primitive_binary_float_div(node : ASTNode, body, owner : Type)\n    # TODO: don't assume Float64 op Float64\n    obj = node.obj.not_nil!\n    arg = node.args.first\n\n    obj_type = owner\n    arg_type = arg.type\n\n    obj_kind = integer_or_float_kind(obj_type).not_nil!\n    arg_kind = integer_or_float_kind(arg_type).not_nil!\n\n    obj.accept self\n    if (obj_type.is_a?(IntegerType) && arg_type.is_a?(FloatType)) ||\n       (obj_type.is_a?(FloatType) && arg_type.is_a?(FloatType) && obj_type.rank < arg_type.rank)\n      primitive_convert(obj, obj_kind, arg_kind, checked: false)\n      obj_kind = arg_kind\n    end\n\n    arg.accept self\n    if (obj_type.is_a?(FloatType) && arg_type.is_a?(IntegerType)) ||\n       (obj_type.is_a?(FloatType) && arg_type.is_a?(FloatType) && obj_type.rank > arg_type.rank)\n      primitive_convert(arg, arg_kind, obj_kind, checked: false)\n      arg_kind = obj_kind\n    end\n\n    case {obj_kind, arg_kind}\n    when {.f32?, .f32?}\n      div_f32(node: node)\n    when {.f64?, .f64?}\n      div_f64(node: node)\n    else\n      node.raise \"BUG: missing handling of binary float div with types #{obj_type} and #{arg_type}\"\n    end\n\n    if obj_type.is_a?(FloatType) && arg_type.is_a?(FloatType) && obj_type.rank < arg_type.rank\n      primitive_convert(node, :f64, :f32, checked: false)\n    end\n  end\n\n  private def integer_or_float_kind(type)\n    case type\n    when IntegerType\n      type.kind\n    when FloatType\n      type.kind\n    else\n      nil\n    end\n  end\n\n  private def request_obj_and_cast_if_needed(obj, owner)\n    request_value(obj)\n\n    obj_type = obj.try &.type?.try &.remove_indirection\n    if obj_type && obj_type != owner\n      downcast(obj, obj_type, owner)\n    end\n  end\n\n  private def request_obj_or_self_and_cast_if_needed(node, obj, owner)\n    if obj\n      request_obj_and_cast_if_needed(obj, owner)\n    else\n      put_self(node: node)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/pry_reader.cr",
    "content": "require \"./repl_reader\"\n\nclass Crystal::PryReader < Crystal::ReplReader\n  property prompt_info = \"\"\n\n  def prompt(io, line_number, color)\n    io << \"pry(\"\n    io << @prompt_info\n    io << ')'\n\n    io.print(@incomplete ? '*' : '>')\n    io << ' '\n  end\n\n  def continue?(expression : String) : Bool\n    if expression == \"*s\" || expression == \"*d\"\n      @incomplete = false\n    else\n      super\n    end\n  end\n\n  def on_ctrl_down(&)\n    yield \"next\"\n  end\n\n  def on_ctrl_left(&)\n    yield \"finish\"\n  end\n\n  def on_ctrl_right(&)\n    yield \"step\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/repl.cr",
    "content": "class Crystal::Repl\n  property prelude : String = \"prelude\"\n  getter program : Program\n  getter context : Context\n\n  def initialize\n    @program = Program.new\n    @context = Context.new(@program)\n    @main_visitor = MainVisitor.new(@program)\n\n    @interpreter = Interpreter.new(@context)\n  end\n\n  def run\n    load_prelude\n\n    reader = ReplReader.new(repl: self)\n    reader.color = @context.program.color?\n\n    reader.read_loop do |expression|\n      case expression\n      when \"exit\"\n        break\n      when \"exit!\"\n        Process.exit(0)\n      when .presence\n        result = parse_and_interpret(expression)\n        result.warnings.report(STDOUT)\n\n        next unless result.value\n\n        print \" => \"\n        puts SyntaxHighlighter::Colorize.highlight!(result.value.to_s)\n      end\n    rescue ex : EscapingException\n      print \"Unhandled exception: \"\n      print ex\n    rescue ex : Crystal::CodeError\n      ex.color = @context.program.color?\n      ex.error_trace = true\n      puts ex\n    rescue ex : Exception\n      ex.inspect_with_backtrace(STDOUT)\n    end\n  end\n\n  record EvalResult, value : Value?, warnings : WarningCollection\n\n  def parse_and_interpret(expression : String) : EvalResult\n    parser = new_parser(expression)\n\n    node = parser.parse\n    return EvalResult.new(value: nil, warnings: parser.warnings) unless node\n\n    value = interpret(node)\n    return EvalResult.new(value: value, warnings: parser.warnings)\n  end\n\n  def run_file(filename, argv)\n    @interpreter.argv = argv\n\n    prelude_node = parse_prelude\n    other_node = parse_file(filename)\n    file_node = FileNode.new(other_node, filename)\n    exps = Expressions.new([prelude_node, file_node] of ASTNode)\n\n    interpret_and_exit_on_error(exps)\n\n    # Explicitly call exit at the end so at_exit handlers run\n    interpret_exit\n  end\n\n  def run_code(code, argv = [] of String)\n    @interpreter.argv = argv\n\n    prelude_node = parse_prelude\n    other_node = parse_code(code)\n    exps = Expressions.new([prelude_node, other_node] of ASTNode)\n\n    interpret(exps)\n  end\n\n  def load_prelude\n    node = parse_prelude\n\n    interpret_and_exit_on_error(node)\n  end\n\n  private def interpret(node : ASTNode)\n    @main_visitor = MainVisitor.new(from_main_visitor: @main_visitor)\n\n    node = @program.normalize(node)\n    node = @program.semantic(node, main_visitor: @main_visitor)\n    @interpreter.interpret(node, @main_visitor.meta_vars)\n  end\n\n  private def interpret_and_exit_on_error(node : ASTNode)\n    interpret(node)\n  rescue ex : EscapingException\n    # First run at_exit handlers by calling Crystal.exit\n    interpret_crystal_exit(ex)\n    exit 1\n  rescue ex : Crystal::CodeError\n    ex.color = true\n    ex.error_trace = true\n    puts ex\n    exit 1\n  rescue ex : Exception\n    ex.inspect_with_backtrace(STDOUT)\n    exit 1\n  end\n\n  private def parse_prelude\n    filenames = @program.find_in_path(prelude)\n    parsed_nodes = filenames.map { |filename| parse_file(filename) }\n    Expressions.new(parsed_nodes)\n  end\n\n  private def parse_file(filename)\n    parse_code File.read(filename), filename\n  end\n\n  private def parse_code(code, filename = \"\")\n    warnings = @program.warnings.dup\n    warnings.infos = [] of String\n    parser = Parser.new code, @program.string_pool, warnings: warnings\n    parser.filename = filename\n    parsed_nodes = parser.parse\n    warnings.report(STDOUT)\n    @program.normalize(parsed_nodes, inside_exp: false)\n  end\n\n  private def interpret_exit\n    interpret(Call.new(\"exit\", global: true))\n  end\n\n  private def interpret_crystal_exit(exception : EscapingException)\n    decl = UninitializedVar.new(Var.new(\"ex\"), TypeNode.new(@context.program.exception.virtual_type))\n    call = Call.new(Path.global(\"Crystal\"), \"exit\", NumberLiteral.new(1), Var.new(\"ex\"))\n    exps = Expressions.new([decl, call] of ASTNode)\n\n    begin\n      Interpreter.interpret(@context, exps) do |stack|\n        stack.as(UInt8**).value = exception.exception_pointer\n      end\n    rescue ex\n      puts \"Error while calling Crystal.exit: #{ex.message}\"\n    end\n  end\n\n  protected def new_parser(source)\n    Parser.new(\n      source,\n      string_pool: @context.program.string_pool,\n      var_scopes: [@interpreter.local_vars.names_at_block_level_zero.to_set]\n    )\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/repl_reader.cr",
    "content": "require \"reply\"\n\nclass Crystal::ReplReader < Reply::Reader\n  KEYWORDS = %w(\n    abstract alias annotation asm begin break case class\n    def do else elsif end ensure enum extend for fun\n    if in include instance_sizeof lib macro module\n    next of offsetof out pointerof private protected require\n    rescue return select sizeof struct super\n    then type typeof union uninitialized unless until\n    verbatim when while with yield\n  )\n  METHOD_KEYWORDS = %w(as as? is_a? nil? responds_to?)\n  CONTINUE_ERROR  = [\n    \"expecting identifier 'end', not 'EOF'\",\n    \"expecting token 'CONST', not 'EOF'\",\n    \"expecting any of these tokens: IDENT, CONST, `, <<, <, <=, ==, ===, !=, =~, !~, >>, >, >=, +, -, *, /, //, !, ~, %, &, |, ^, **, [], []?, []=, <=>, &+, &-, &*, &** (not 'EOF')\",\n    \"expecting any of these tokens: ;, NEWLINE (not 'EOF')\",\n    \"expecting token ')', not 'EOF'\",\n    \"expecting token ']', not 'EOF'\",\n    \"expecting token '}', not 'EOF'\",\n    \"expecting token '%}', not 'EOF'\",\n    \"expecting token '}', not ','\",\n    \"expected '}' or named tuple name, not EOF\",\n    \"unexpected token: NEWLINE\",\n    \"unexpected token: EOF\",\n    \"unexpected token: EOF (expecting when, else or end)\",\n    \"unexpected token: EOF (expecting ',', ';' or '\\n')\",\n    \"Unexpected EOF on heredoc identifier\",\n    \"unterminated parenthesized expression\",\n    \"unterminated call\",\n    \"Unterminated string literal\",\n    \"unterminated hash literal\",\n    \"Unterminated command literal\",\n    \"unterminated array literal\",\n    \"unterminated tuple literal\",\n    \"unterminated macro\",\n    \"Unterminated string interpolation\",\n    \"invalid trailing comma in call\",\n    \"unknown token: '\\\\u{0}'\",\n  ]\n  @incomplete = false\n  @repl : Repl?\n\n  def initialize(@repl = nil)\n    super()\n\n    # `\"`, `:`, `'`, are not a delimiter because symbols and strings are treated as one word.\n    # '=', !', '?' are not a delimiter because they could make part of method name.\n    self.word_delimiters = {{\" \\n\\t+-*/,;@&%<>^\\\\[](){}|.~\".chars}}\n  end\n\n  def prompt(io : IO, line_number : Int32, color : Bool) : Nil\n    io << \"icr:\"\n    io << line_number\n\n    io.print(@incomplete ? '*' : '>')\n    io << ' '\n  end\n\n  def highlight(expression : String) : String\n    SyntaxHighlighter::Colorize.highlight!(expression)\n  end\n\n  def continue?(expression : String) : Bool\n    ast = new_parser(expression).parse\n    @incomplete = annotations_only?(ast)\n  rescue e : CodeError\n    @incomplete = e.message.in?(CONTINUE_ERROR)\n    if (message = e.message) && message.matches? /Unterminated heredoc: can't find \".*\" anywhere before the end of file/\n      @incomplete = true\n    end\n\n    @incomplete\n  end\n\n  def format(expression : String) : String?\n    Crystal.format(expression).chomp rescue nil\n  end\n\n  def indentation_level(expression_before_cursor : String) : Int32?\n    parser = new_parser(expression_before_cursor)\n    parser.parse rescue nil\n\n    parser.type_nest + parser.def_nest + parser.fun_nest\n  end\n\n  def reindent_line(line)\n    case line.strip\n    when \"end\", \")\", \"]\", \"}\"\n      0\n    when \"else\", \"elsif\", \"rescue\", \"ensure\", \"in\", \"when\"\n      -1\n    else\n      nil\n    end\n  end\n\n  def save_in_history?(expression : String) : Bool\n    !expression.blank?\n  end\n\n  def auto_complete(name_filter : String, expression : String) : {String, Array(String)}\n    if expression.ends_with? '.'\n      return \"Keywords:\", METHOD_KEYWORDS.dup\n    else\n      return \"Keywords:\", KEYWORDS.dup\n    end\n  end\n\n  def auto_completion_display_title(io : IO, title : String)\n    io << title\n  end\n\n  def auto_completion_display_selected_entry(io : IO, entry : String)\n    io << entry.colorize.red.bright\n  end\n\n  def auto_completion_display_entry(io : IO, entry_matched : String, entry_remaining : String)\n    io << entry_matched.colorize.red.bright << entry_remaining\n  end\n\n  private def new_parser(source)\n    if repl = @repl\n      repl.new_parser(source)\n    else\n      Parser.new(source)\n    end\n  end\n\n  private def annotations_only?(ast : ASTNode) : Bool\n    case ast\n    when Annotation\n      true\n    when Expressions\n      ast.expressions.all? { |e| annotations_only?(e) }\n    else\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/to_bool.cr",
    "content": "require \"./compiler\"\n\n# Logic to transform a value in the stack to a boolean value.\n\nclass Crystal::Repl::Compiler\n  private def value_to_bool(node : ASTNode, type : NilType)\n    put_false node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : BoolType)\n    # Nothing to do\n  end\n\n  private def value_to_bool(node : ASTNode, type : PointerInstanceType)\n    pointer_is_not_null node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : NilableProcType)\n    # We have {pointer, closure_data} and we need to check if pointer is not null\n    pop 8, node: nil\n    pointer_is_not_null node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : NilableType)\n    pointer_is_not_null node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : NilableReferenceUnionType)\n    pointer_is_not_null node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : MixedUnionType)\n    union_to_bool aligned_sizeof_type(type), node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : NonGenericClassType | GenericClassInstanceType | VirtualType | MetaclassType | VirtualMetaclassType | ReferenceUnionType | IntegerType | CharType | SymbolType | FloatType | EnumType | NonGenericModuleType | GenericModuleInstanceType | GenericModuleInstanceMetaclassType | GenericClassInstanceMetaclassType)\n    pop aligned_sizeof_type(type), node: nil\n    put_true node: nil\n  end\n\n  private def value_to_bool(node : ASTNode, type : TypeDefType)\n    # TODO: not tested\n    value_to_bool node, type.typedef\n  end\n\n  private def value_to_bool(node : ASTNode, type : NoReturnType)\n    # Nothing to do\n  end\n\n  private def value_to_bool(node : ASTNode, type : Type)\n    node.raise \"BUG: missing value_to_bool for #{type} (#{type.class})\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter/value.cr",
    "content": "require \"./repl\"\n\n# A value produced by the interpreter, essentially\n# a pointer to some data coupled with type information.\n# Based on the type we know how to interpreter the data in `pointer`.\nstruct Crystal::Repl::Value\n  getter pointer : Pointer(UInt8)\n  getter type : Type\n\n  def initialize(@interpreter : Interpreter, @pointer : Pointer(UInt8), @type : Type)\n  end\n\n  # This is a fail-safe way of getting a Crystal value for well-known\n  # types like ints, floats, chars, tuples, classes and pointers.\n  def value\n    type = @type\n    case type\n    when NilType\n      nil\n    when BoolType\n      @pointer.as(Bool*).value\n    when CharType\n      @pointer.as(Char*).value\n    when IntegerType\n      case type.kind\n      when .i8?\n        @pointer.as(Int8*).value\n      when .u8?\n        @pointer.as(UInt8*).value\n      when .i16?\n        @pointer.as(Int16*).value\n      when .u16?\n        @pointer.as(UInt16*).value\n      when .i32?\n        @pointer.as(Int32*).value\n      when .u32?\n        @pointer.as(UInt32*).value\n      when .i64?\n        @pointer.as(Int64*).value\n      when .u64?\n        @pointer.as(UInt64*).value\n      when .i128?\n        @pointer.as(Int128*).value\n      when .u128?\n        @pointer.as(UInt128*).value\n      else\n        raise \"BUG: missing handling of Repl value for #{type}\"\n      end\n    when FloatType\n      case type.kind\n      when .f32?\n        @pointer.as(Float32*).value\n      when .f64?\n        @pointer.as(Float64*).value\n      else\n        raise \"BUG: missing handling of Repl value for #{type}\"\n      end\n    when type.program.string\n      @pointer.as(UInt8**).value.unsafe_as(String)\n    when PointerInstanceType\n      @pointer.as(UInt8**).value\n    when MetaclassType, GenericClassInstanceMetaclassType, VirtualMetaclassType\n      type_id = @pointer.as(Int32*).value\n      context.type_from_id(type_id)\n    else\n      @pointer\n    end\n  end\n\n  def runtime_type : Crystal::Type\n    # Should match Crystal::Repl::Compiler#visit_primitive \"class\" case\n    # in src/compiler/crystal/interpreter/primitives.cr\n    case type\n    when Crystal::UnionType\n      type_id = @pointer.as(Int32*).value\n      context.type_from_id(type_id)\n    when Crystal::VirtualType\n      type_id = @pointer.as(Void**).value.as(Int32*).value\n      context.type_from_id(type_id)\n    else\n      type\n    end\n  end\n\n  # Copies the contents of this value to another pointer.\n  def copy_to(pointer : Pointer(UInt8))\n    @pointer.copy_to(pointer, context.inner_sizeof_type(@type))\n  end\n\n  # Appends the string representation of this value to the given *io*.\n  # This is done by interpreting a call to `inspect` (not `to_s`)\n  # on this value.\n  def to_s(io : IO)\n    decl = UninitializedVar.new(Var.new(\"x\"), TypeNode.new(@type))\n    call = Call.new(Var.new(\"x\"), \"inspect\")\n    exps = Expressions.new([decl, call] of ASTNode)\n\n    begin\n      value = Interpreter.interpret(context, exps) do |stack|\n        stack.copy_from(@pointer, context.inner_sizeof_type(@type))\n      end\n      if value.type == context.program.string\n        value.pointer.as(UInt8**).value.unsafe_as(String).to_s(io)\n      else\n        value.fallback_to_s(io)\n      end\n    rescue ex\n      io.puts \"Error while calling inspect on value: #{ex.message}\"\n      fallback_to_s(io)\n    end\n  end\n\n  # A way to compute a string representation of a value for _some_\n  # types, if computing `inspect` fails for some reason.\n  def fallback_to_s(io : IO)\n    type = @type\n    case type\n    when NilType\n      io << \"nil\"\n    when BoolType\n      io << @pointer.as(Bool*).value\n    when CharType\n      @pointer.as(Char*).value.inspect(io)\n    when IntegerType\n      case type.kind\n      when .i8?\n        io << @pointer.as(Int8*).value\n      when .u8?\n        io << @pointer.as(UInt8*).value\n      when .i16?\n        io << @pointer.as(Int16*).value\n      when .u16?\n        io << @pointer.as(UInt16*).value\n      when .i32?\n        io << @pointer.as(Int32*).value\n      when .u32?\n        io << @pointer.as(UInt32*).value\n      when .i64?\n        io << @pointer.as(Int64*).value\n      when .u64?\n        io << @pointer.as(UInt64*).value\n      when .i128?\n        io << @pointer.as(Int128*).value\n      when .u128?\n        io << @pointer.as(UInt128*).value\n      else\n        raise \"BUG: missing handling of Repl::Value#to_s(io) for #{type}\"\n      end\n    when FloatType\n      case type.kind\n      when .f32?\n        io << @pointer.as(Float32*).value\n      when .f64?\n        io << @pointer.as(Float64*).value\n      else\n        raise \"BUG: missing handling of Repl::Value#to_s(io) for #{type}\"\n      end\n    when type.program.string\n      @pointer.as(UInt8**).value.unsafe_as(String).inspect(io)\n    when PointerInstanceType\n      pointer = @pointer.as(UInt8**).value\n      io << type\n      if pointer.null?\n        io << \".null\"\n      else\n        io << \"@0x\"\n        pointer.address.to_s(io, base: 16)\n      end\n    when TupleInstanceType\n      io << \"{\"\n      type.tuple_types.each_with_index do |tuple_type, i|\n        io << Value.new(@interpreter, @pointer + context.offset_of(type, i), tuple_type)\n        io << \", \" unless i == type.tuple_types.size - 1\n      end\n      io << \"}\"\n    when MetaclassType, GenericClassInstanceMetaclassType\n      type_id = @pointer.as(Int32*).value\n      type = context.type_from_id(type_id)\n      io << type\n    when MixedUnionType\n      type_id = @pointer.as(Int32*).value\n      type = context.type_from_id(type_id)\n      io << Value.new(@interpreter, @pointer + sizeof(Pointer(UInt8)), type)\n    when InstanceVarContainer\n      if type.struct?\n        ptr = @pointer\n        io << type\n        io << \"(\"\n        all_instance_vars = type.all_instance_vars\n        all_instance_vars.each_with_index do |(name, ivar), index|\n          offset = context.offset_of(type, index)\n          io << name\n          io << '='\n          io << Value.new(@interpreter, ptr + offset, ivar.type)\n          io << ' ' unless index == all_instance_vars.size - 1\n        end\n        io << \")\"\n      else\n        ptr = @pointer.as(UInt8**).value\n        type_id = ptr.as(Int32*).value\n        type = context.type_from_id(type_id)\n        io << \"#<\"\n        io << type\n        io << \":0x\"\n        ptr.address.to_s(io, 16)\n        type.all_instance_vars.each_with_index do |(name, ivar), index|\n          offset = context.instance_offset_of(type, index)\n          io << ' '\n          io << name\n          io << '='\n          io << Value.new(@interpreter, ptr + offset, ivar.type)\n        end\n        io << \">\"\n      end\n    else\n      io << \"BUG: missing handling of Repl::Value#to_s(io) for #{type}\"\n    end\n  end\n\n  private def context\n    @interpreter.context\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/interpreter.cr",
    "content": "{% skip_file if flag?(:without_interpreter) %}\n\nrequire \"../requires\"\nrequire \"./interpreter/*\"\n"
  },
  {
    "path": "src/compiler/crystal/loader/mingw.cr",
    "content": "{% skip_file unless flag?(:win32) && flag?(:gnu) %}\n\nrequire \"crystal/system/win32/library_archive\"\n\n# MinGW-based loader used on Windows. Assumes an MSYS2 shell.\n#\n# The core implementation is derived from the MSVC loader. Main deviations are:\n#\n# - `.parse` follows GNU `ld`'s style, rather than MSVC `link`'s;\n# - `.parse` automatically inserts a C runtime library if `-mcrtdll` isn't\n#   supplied;\n# - `#library_filename` follows the usual naming of the MinGW linker: `.dll.a`\n#   for DLL import libraries, `.a` for other libraries;\n# - `.default_search_paths` relies solely on `.cc_each_library_path`.\n#\n# TODO: The actual MinGW linker supports linking to DLLs directly, figure out\n# how this is done.\n\nclass Crystal::Loader\n  alias Handle = Void*\n\n  def initialize(@search_paths : Array(String))\n  end\n\n  # Parses linker arguments in the style of `ld`.\n  #\n  # This is identical to the Unix loader. *dll_search_paths* has no effect.\n  def self.parse(args : Array(String), *, search_paths : Array(String) = default_search_paths, dll_search_paths : Array(String)? = nil) : self\n    libnames = [] of String\n    file_paths = [] of String\n    extra_search_paths = [] of String\n\n    # note that `msvcrt` is a default runtime chosen at MinGW-w64 build time,\n    # `ucrt` is always UCRT (even in a MINGW64 environment), and\n    # `msvcrt-os` is always MSVCRT (even in a UCRT64 environment)\n    crt_dll = \"msvcrt\"\n\n    OptionParser.parse(args.dup) do |parser|\n      parser.on(\"-L DIRECTORY\", \"--library-path DIRECTORY\", \"Add DIRECTORY to library search path\") do |directory|\n        extra_search_paths << directory\n      end\n      parser.on(\"-l LIBNAME\", \"--library LIBNAME\", \"Search for library LIBNAME\") do |libname|\n        libnames << libname\n      end\n      parser.on(\"-static\", \"Do not link against shared libraries\") do\n        raise LoadError.new \"static libraries are not supported by Crystal's runtime loader\"\n      end\n      parser.unknown_args do |args, after_dash|\n        file_paths.concat args.reject(&.starts_with?(\"-mcrtdll=\"))\n      end\n\n      parser.invalid_option do |arg|\n        if crt_dll_arg = arg.lchop?(\"-mcrtdll=\")\n          # the GCC spec is `%{!mcrtdll=*:-lmsvcrt} %{mcrtdll=*:-l%*}`\n          crt_dll = crt_dll_arg\n        elsif !arg.starts_with?(\"-Wl,\")\n          raise LoadError.new \"Not a recognized linker flag: #{arg}\"\n        end\n      end\n    end\n\n    search_paths = extra_search_paths + search_paths\n    libnames << crt_dll\n\n    begin\n      loader = new(search_paths)\n      loader.load_all(libnames, file_paths)\n      loader\n    rescue exc : LoadError\n      exc.args = args\n      exc.search_paths = search_paths\n      raise exc\n    end\n  end\n\n  def self.library_filename(libname : String) : String\n    \"lib#{libname}.a\"\n  end\n\n  def find_symbol?(name : String) : Handle?\n    @handles.each do |handle|\n      address = LibC.GetProcAddress(handle, name.check_no_null_byte)\n      return address if address\n    end\n  end\n\n  def load_file(path : String | ::Path) : Nil\n    load_file?(path) || raise LoadError.new \"cannot load #{path}\"\n  end\n\n  def load_file?(path : String | ::Path) : Bool\n    if api_set?(path)\n      return load_dll?(path.to_s)\n    end\n\n    return false unless File.file?(path)\n\n    System::LibraryArchive.imported_dlls(path).all? do |dll|\n      load_dll?(dll)\n    end\n  end\n\n  private def load_dll?(dll)\n    handle = open_library(dll)\n    return false unless handle\n\n    @handles << handle\n    @loaded_libraries << (module_filename(handle) || dll)\n    true\n  end\n\n  def load_library(libname : String) : Nil\n    load_library?(libname) || raise LoadError.new \"cannot find #{Loader.library_filename(libname)}\"\n  end\n\n  def load_library?(libname : String) : Bool\n    if ::Path::SEPARATORS.any? { |separator| libname.includes?(separator) }\n      return load_file?(::Path[libname].expand)\n    end\n\n    # attempt .dll.a before .a\n    # TODO: verify search order\n    @search_paths.each do |directory|\n      library_path = File.join(directory, Loader.library_filename(libname + \".dll\"))\n      return true if load_file?(library_path)\n\n      library_path = File.join(directory, Loader.library_filename(libname))\n      return true if load_file?(library_path)\n    end\n\n    false\n  end\n\n  private def open_library(path : String)\n    LibC.LoadLibraryExW(System.to_wstr(path), nil, 0)\n  end\n\n  def load_current_program_handle\n    if LibC.GetModuleHandleExW(0, nil, out hmodule) != 0\n      @handles << hmodule\n      @loaded_libraries << (Process.executable_path || \"current program handle\")\n    end\n  end\n\n  def close_all : Nil\n    @handles.each do |handle|\n      LibC.FreeLibrary(handle)\n    end\n    @handles.clear\n  end\n\n  private def api_set?(dll)\n    dll.to_s.matches?(/^(?:api-|ext-)[a-zA-Z0-9-]*l\\d+-\\d+-\\d+\\.dll$/)\n  end\n\n  private def module_filename(handle)\n    Crystal::System.retry_wstr_buffer do |buffer, small_buf|\n      len = LibC.GetModuleFileNameW(handle, buffer, buffer.size)\n      if 0 < len < buffer.size\n        break String.from_utf16(buffer[0, len])\n      elsif small_buf && len == buffer.size\n        next 32767 # big enough. 32767 is the maximum total path length of UNC path.\n      else\n        break nil\n      end\n    end\n  end\n\n  # Returns a list of directories used as the default search paths.\n  #\n  # Right now this depends on `cc` exclusively.\n  def self.default_search_paths : Array(String)\n    default_search_paths = [] of String\n\n    cc_each_library_path do |path|\n      default_search_paths << path\n    end\n\n    default_search_paths.uniq!\n  end\n\n  # identical to the Unix loader\n  def self.cc_each_library_path(& : String ->) : Nil\n    search_dirs = begin\n      cc =\n        {% if Crystal.has_constant?(\"Compiler\") %}\n          Crystal::Compiler::DEFAULT_LINKER\n        {% else %}\n          # this allows the loader to be required alone without the compiler\n          ENV[\"CC\"]? || \"cc\"\n        {% end %}\n\n      `#{cc} -print-search-dirs`\n    rescue IO::Error\n      return\n    end\n\n    search_dirs.each_line do |line|\n      if libraries = line.lchop?(\"libraries: =\")\n        libraries.split(Process::PATH_DELIMITER) do |path|\n          yield File.expand_path(path)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/loader/msvc.cr",
    "content": "{% skip_file unless flag?(:msvc) %}\nrequire \"crystal/system/win32/library_archive\"\n\n# On Windows with the MSVC toolset, the loader tries to imitate the behaviour of\n# `link.exe`, using the Win32 DLL API.\n#\n# * Only dynamic libraries can be loaded. Static libraries and object files\n#   are unsupported. For example, `LibC.printf` and `LibC.snprintf` are inline\n#   functions in `legacy_stdio_definitions.lib` since VS2015, so they are never\n#   found by the loader (this is why stdlib no longer uses those functions).\n# * Unlike the Unix counterpart, symbols in the current module do not clash with\n#   the ones in DLLs or their corresponding import libraries.\n\nclass Crystal::Loader\n  alias Handle = Void*\n\n  class LoadError\n    include SystemError\n  end\n\n  getter dll_search_paths : Array(String)?\n\n  def initialize(@search_paths : Array(String), @dll_search_paths : Array(String)? = nil)\n  end\n\n  # Parses linker arguments in the style of `link.exe`.\n  #\n  # The directories in *dll_search_paths* are tried before Windows' search order\n  # when looking for DLLs corresponding to an import library. The compiler uses\n  # this to mimic `@[Link]`'s DLL-copying behavior for compiled code.\n  def self.parse(args : Array(String), *, search_paths : Array(String) = default_search_paths, dll_search_paths : Array(String)? = nil) : self\n    search_paths, libnames = parse_args(args, search_paths)\n    file_paths = [] of String\n\n    begin\n      loader = new(search_paths, dll_search_paths)\n      loader.load_all(libnames, file_paths)\n      loader\n    rescue exc : LoadError\n      exc.args = args\n      exc.search_paths = search_paths\n      exc.dll_search_paths = dll_search_paths\n      raise exc\n    end\n  end\n\n  struct SearchLibResult\n    getter library_paths = [] of String\n    getter remaining_args = [] of String\n    getter(not_found) { [] of String }\n\n    def not_found?\n      @not_found\n    end\n  end\n\n  # Extracts the command-line arguments from *args* that add libraries and\n  # expands them to their absolute paths. Returns a `SearchLibResult` with those\n  # expanded paths, plus unused arguments and libraries that were not found.\n  def self.search_libraries(args : Array(String), *, search_paths : Array(String) = default_search_paths, extra_suffix : String? = nil) : SearchLibResult\n    result = SearchLibResult.new\n    search_paths, libnames = parse_args(args, search_paths, remaining: result.remaining_args)\n\n    libnames.each do |libname|\n      if found_path = search_library(libname, search_paths, extra_suffix)\n        result.library_paths << found_path\n      else\n        result.not_found << libname\n      end\n    end\n\n    result\n  end\n\n  protected def self.search_library(libname, search_paths, extra_suffix)\n    if ::Path::SEPARATORS.any? { |separator| libname.includes?(separator) }\n      libname = File.expand_path(libname)\n      library_path = library_filename(libname)\n      return library_path if File.file?(library_path)\n    else\n      search_paths.each do |directory|\n        if extra_suffix\n          library_path = File.join(directory, library_filename(libname + extra_suffix))\n          return library_path if File.file?(library_path)\n        end\n\n        library_path = File.join(directory, library_filename(libname))\n        return library_path if File.file?(library_path)\n      end\n    end\n  end\n\n  def self.parse_args(args, search_paths, *, remaining = nil)\n    libnames = [] of String\n\n    # NOTE: `/LIBPATH`s are prepended before the default paths:\n    # (https://docs.microsoft.com/en-us/cpp/build/reference/libpath-additional-libpath)\n    #\n    # > ... The linker will first search in the path specified by this option,\n    # > and then search in the path specified in the LIB environment variable.\n    extra_search_paths = [] of String\n\n    args.each do |arg|\n      if !arg.starts_with?('/') && (name = arg.rchop?(\".lib\"))\n        libnames << name\n      else\n        remaining << arg if remaining\n        if lib_path = arg.lchop?(\"/LIBPATH:\")\n          extra_search_paths << lib_path\n        end\n      end\n    end\n\n    search_paths = extra_search_paths + search_paths\n    search_paths.uniq! &.downcase\n    libnames.uniq! &.downcase\n    {search_paths, libnames}\n  end\n\n  def self.library_filename(libname : String) : String\n    \"#{libname.rchop(\".lib\")}.lib\"\n  end\n\n  def find_symbol?(name : String) : Handle?\n    @handles.each do |handle|\n      address = LibC.GetProcAddress(handle, name.check_no_null_byte)\n      return address if address\n    end\n  end\n\n  def load_file(path : String | ::Path) : Nil\n    load_file?(path) || raise LoadError.from_winerror \"cannot load #{path}\"\n  end\n\n  def load_file?(path : String | ::Path) : Bool\n    # API sets shouldn't be linked directly from linker flags, but just in case\n    if api_set?(path)\n      return load_dll?(path.to_s)\n    end\n\n    return false unless File.file?(path)\n\n    # On Windows, each `.lib` import library may reference any number of `.dll`\n    # files, whose base names may not match the library's. Thus it is necessary\n    # to extract this information from the library archive itself.\n    System::LibraryArchive.imported_dlls(path).all? do |dll|\n      # API set names do not refer to physical filenames despite ending with\n      # `.dll`, and therefore should not use a path search:\n      # https://learn.microsoft.com/en-us/cpp/windows/universal-crt-deployment?view=msvc-170#local-deployment\n      unless api_set?(dll)\n        dll_full_path = @dll_search_paths.try &.each do |search_path|\n          full_path = File.join(search_path, dll)\n          break full_path if File.file?(full_path)\n        end\n      end\n      dll = dll_full_path || dll\n\n      # TODO: `dll` is an unqualified name, e.g. `SHELL32.dll`, so the default\n      # DLL search order is used if *dll_full_path* is nil; consider getting rid\n      # of the current working directory altogether\n      # (https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order)\n      #\n      # Note that the compiler's directory and PATH are effectively searched\n      # twice when coming from the interpreter\n      load_dll?(dll)\n    end\n  end\n\n  private def load_dll?(dll)\n    handle = open_library(dll)\n    return false unless handle\n\n    @handles << handle\n    @loaded_libraries << (module_filename(handle) || dll)\n    true\n  end\n\n  def load_library(libname : String) : Nil\n    load_library?(libname) || raise LoadError.from_winerror \"cannot find #{Loader.library_filename(libname)}\"\n  end\n\n  def load_library?(libname : String) : Bool\n    library_path = Loader.search_library(libname, @search_paths, \"-dynamic\")\n    !library_path.nil? && load_file?(library_path)\n  end\n\n  private def open_library(path : String)\n    LibC.LoadLibraryExW(System.to_wstr(path), nil, 0)\n  end\n\n  def load_current_program_handle\n    if LibC.GetModuleHandleExW(0, nil, out hmodule) != 0\n      @handles << hmodule\n      @loaded_libraries << (Process.executable_path || \"current program handle\")\n    end\n  end\n\n  def close_all : Nil\n    @handles.each do |handle|\n      LibC.FreeLibrary(handle)\n    end\n    @handles.clear\n  end\n\n  # Returns whether *dll* names an API set according to:\n  # https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets#api-set-contract-names\n  private def api_set?(dll)\n    dll.to_s.matches?(/^(?:api-|ext-)[a-zA-Z0-9-]*l\\d+-\\d+-\\d+\\.dll$/)\n  end\n\n  private def module_filename(handle)\n    Crystal::System.retry_wstr_buffer do |buffer, small_buf|\n      len = LibC.GetModuleFileNameW(handle, buffer, buffer.size)\n      if 0 < len < buffer.size\n        break String.from_utf16(buffer[0, len])\n      elsif small_buf && len == buffer.size\n        next 32767 # big enough. 32767 is the maximum total path length of UNC path.\n      else\n        break nil\n      end\n    end\n  end\n\n  # Returns a list of directories used as the default search paths.\n  #\n  # For MSVC this is simply the contents of the `LIB` environment variable,\n  # usually pre-populated by the developer prompt. (If a normal prompt is used\n  # but an MSVC installation is available, Crystal injects a replica of `LIB`'s\n  # default contents through `/LIBPATH` linker arguments.)\n  #\n  # This is _not_ the same thing as the default search paths for `.dll` files.\n  def self.default_search_paths : Array(String)\n    if env_lib = ENV[\"LIB\"]?\n      env_lib.split(Process::PATH_DELIMITER, remove_empty: true)\n    else\n      [] of String\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/loader/unix.cr",
    "content": "{% skip_file unless flag?(:unix) %}\n\n# On UNIX-like systems, the loader implementation is based on `libdl` .\n# It tries to imitate the behaviour of `ld.so` (linux`, BSDs) and `ld` (darwin).\n#\n# There are a number of differences compared to linking object files in a normal\n# object file linking process. Most are caused by limitations of `libdl`.\n#\n# * Only dynamic libraries can be loaded. Static libraries and object files\n#   are unsupported.\n# * All libraries are loaded into the same namespace. That means libraries\n#   loaded in the compiler program itself may provide symbols to libraries\n#   loaded with `Crystal::Loader`. Symbols may be available without explicitly\n#   mentioning their libraries. It might be impossible to link against other\n#   version of the libraries that the compiler is linked against.\n# * A fully statically linked compiler may help dealing with the previous\n#   issue. But using `libdl` in a non-dynamically loaded executable might cause\n#   other issues.\n\nclass Crystal::Loader\n  alias Handle = Void*\n\n  class LoadError\n    def self.new_dl_error(message)\n      if char_pointer = LibC.dlerror\n        new(String.build do |io|\n          io << message\n          io << \" (\"\n          io.write_string(Slice.new(char_pointer, LibC.strlen(char_pointer)))\n          io << \")\"\n        end)\n      else\n        new message\n      end\n    end\n  end\n\n  def initialize(@search_paths : Array(String))\n  end\n\n  # Parses linker arguments in the style of `ld`.\n  #\n  # *dll_search_paths* has no effect. (Technically speaking, `LD_LIBRARY_PATH`\n  # goes here and `LIBRARY_PATH` goes into *search_paths*, but there is little\n  # point in doing so since the same library files are used at both compile and\n  # run time.)\n  def self.parse(args : Array(String), *, search_paths : Array(String) = default_search_paths, dll_search_paths : Array(String)? = nil) : self\n    libnames = [] of String\n    file_paths = [] of String\n\n    # `man ld(1)` on Linux:\n    #\n    # > -L searchdir\n    # > ... The directories are searched in the order in which they are\n    # specified on the command line. Directories specified on the command line\n    # are searched before the default directories.\n    #\n    # `man ld(1)` on macOS:\n    #\n    # > -Ldir\n    # > ... Directories specified with -L are searched in the order they appear\n    # > on the command line and before the default search path...\n    extra_search_paths = [] of String\n\n    # OptionParser removes items from the args array, so we dup it here in order to produce a meaningful error message.\n    OptionParser.parse(args.dup) do |parser|\n      parser.on(\"-L DIRECTORY\", \"--library-path DIRECTORY\", \"Add DIRECTORY to library search path\") do |directory|\n        extra_search_paths << directory\n      end\n      parser.on(\"-l LIBNAME\", \"--library LIBNAME\", \"Search for library LIBNAME\") do |libname|\n        libnames << libname\n      end\n      parser.on(\"-static\", \"Do not link against shared libraries\") do\n        raise LoadError.new \"static libraries are not supported by Crystal's runtime loader\"\n      end\n      parser.unknown_args do |args, after_dash|\n        file_paths.concat args\n      end\n\n      # although flags starting with `-Wl,` appear in `args` above, this is\n      # still called by `OptionParser`, so we assume it is fine to ignore these\n      # flags\n      parser.invalid_option do |arg|\n        unless arg.starts_with?(\"-Wl,\")\n          raise LoadError.new \"Not a recognized linker flag: #{arg}\"\n        end\n      end\n    end\n\n    search_paths = extra_search_paths + search_paths\n\n    begin\n      loader = new(search_paths)\n      loader.load_all(libnames, file_paths)\n      loader\n    rescue exc : LoadError\n      exc.args = args\n      exc.search_paths = search_paths\n      raise exc\n    end\n  end\n\n  def self.library_filename(libname : String) : String\n    {% if flag?(:darwin) %}\n      \"lib#{libname}.dylib\"\n    {% else %}\n      \"lib#{libname}.so\"\n    {% end %}\n  end\n\n  def find_symbol?(name : String) : Handle?\n    @handles.each do |handle|\n      address = LibC.dlsym(handle, name)\n      return address if address\n    end\n  end\n\n  def load_file(path : String | ::Path) : Nil\n    load_file?(path) || raise LoadError.new_dl_error \"cannot load #{path}\"\n  end\n\n  def load_file?(path : String | ::Path) : Bool\n    handle = open_library(path.to_s)\n    return false unless handle\n\n    @handles << handle\n    @loaded_libraries << path.to_s\n    true\n  end\n\n  def load_library(libname : String) : Nil\n    load_library?(libname) || raise LoadError.new_dl_error \"cannot find -l#{libname}\"\n  end\n\n  private def open_library(path : String)\n    LibC.dlopen(path, LibC::RTLD_LAZY | LibC::RTLD_GLOBAL)\n  end\n\n  def load_current_program_handle\n    if program_handle = LibC.dlopen(nil, LibC::RTLD_LAZY | LibC::RTLD_GLOBAL)\n      @handles << program_handle\n      @loaded_libraries << (Process.executable_path || \"current program handle\")\n    end\n  end\n\n  # Closes all libraries loaded with this loader instance.\n  #\n  # Libraries are only unloaded when there are no references left.\n  def close_all : Nil\n    @handles.each do |handle|\n      LibC.dlclose(handle)\n    end\n    @handles.clear\n  end\n\n  # Returns a list of directories used as the default search paths\n  def self.default_search_paths : Array(String)\n    default_search_paths = [] of String\n\n    # TODO: respect the compiler's DT_RPATH (#13490)\n\n    if env_library_path = ENV[{{ flag?(:darwin) ? \"DYLD_LIBRARY_PATH\" : \"LD_LIBRARY_PATH\" }}]?\n      # TODO: Expand tokens $ORIGIN, $LIB, $PLATFORM\n      default_search_paths.concat env_library_path.split(Process::PATH_DELIMITER, remove_empty: true)\n    end\n\n    # TODO: respect the compiler's DT_RUNPATH\n    # TODO: respect $DYLD_FALLBACK_LIBRARY_PATH and the compiler's LC_RPATH on darwin\n\n    {% if (flag?(:linux) && !flag?(:android)) || flag?(:bsd) %}\n      read_ld_conf(default_search_paths)\n    {% end %}\n\n    cc_each_library_path do |path|\n      default_search_paths << path\n    end\n\n    {% if flag?(:darwin) %}\n      default_search_paths << \"/usr/lib\"\n      default_search_paths << \"/usr/local/lib\"\n    {% elsif flag?(:android) %}\n      default_search_paths << \"/vendor/lib64\" if File.directory?(\"/vendor/lib64\")\n      default_search_paths << \"/system/lib64\" if File.directory?(\"/system/lib64\")\n      default_search_paths << \"/vendor/lib\"\n      default_search_paths << \"/system/lib\"\n    {% else %}\n      {% if flag?(:linux) %}\n        default_search_paths << \"/lib64\" if File.directory?(\"/lib64\")\n        default_search_paths << \"/usr/lib64\" if File.directory?(\"/usr/lib64\")\n      {% end %}\n      default_search_paths << \"/lib\"\n      default_search_paths << \"/usr/lib\"\n    {% end %}\n\n    default_search_paths.uniq!\n  end\n\n  def self.read_ld_conf(array = [] of String, path = \"/etc/ld.so.conf\") : Nil\n    return unless File::Info.readable?(path)\n\n    File.each_line(path) do |line|\n      next if line.empty? || line.starts_with?(\"#\")\n\n      if include_path = line.lchop?(\"include \")\n        glob = ::Path[include_path]\n\n        # expand glob path relative to current config file\n        glob = glob.expand(File.dirname(path))\n        Dir.glob(glob) do |dir|\n          read_ld_conf(array, dir)\n        end\n      else\n        array << line.strip\n      end\n    end\n  end\n\n  def self.cc_each_library_path(& : String ->) : Nil\n    search_dirs = begin\n      `#{Crystal::Compiler::DEFAULT_LINKER} -print-search-dirs`\n    rescue IO::Error\n      return\n    end\n\n    search_dirs.each_line do |line|\n      if libraries = line.lchop?(\"libraries: =\")\n        libraries.split(Process::PATH_DELIMITER) do |path|\n          yield File.expand_path(path)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/loader.cr",
    "content": "{% skip_file unless flag?(:unix) || flag?(:win32) %}\nrequire \"option_parser\"\n\n# This loader component imitates the behaviour of `ld.so` for linking and loading\n# dynamic libraries at runtime.\n#\n# It provides a tool for interpreted mode, where the compiler does not generate\n# an object file that could be passed to the linker. Instead, `Crystal::Loader`\n# takes over the job of discovering libraries, loading them into memory and\n# finding symbols inside them.\n#\n# See system-specific implementations in ./loader for details.\nclass Crystal::Loader\n  class LoadError < Exception\n    property args : Array(String)?\n    property search_paths : Array(String)?\n    property dll_search_paths : Array(String)?\n\n    def message\n      String.build do |io|\n        io << super\n        if args = @args\n          io << \"\\nLinker arguments: \"\n          args.join(io, \" \")\n        end\n        if search_paths = @search_paths\n          io << \"\\nSearch path: \"\n          search_paths.join(io, Process::PATH_DELIMITER)\n        end\n        if dll_search_paths = @dll_search_paths\n          io << \"\\nDLL search path: \"\n          dll_search_paths.join(io, Process::PATH_DELIMITER)\n        end\n      end\n    end\n  end\n\n  def load_all(libnames : Array(String), file_paths : Array(String))\n    file_paths.each do |path|\n      load_file(::Path[path].expand)\n    end\n    libnames.each do |libname|\n      load_library(libname)\n    end\n  end\n\n  getter search_paths : Array(String)\n  getter loaded_libraries = [] of String\n  @handles = [] of Handle\n\n  # def self.library_filename(libname : String) : String\n  #   raise NotImplementedError.new(\"library_filename\")\n  # end\n\n  # def find_symbol?(name : String) : Handle?\n  #   raise NotImplementedError.new(\"find_symbol?\")\n  # end\n\n  # def load_file(path : String | ::Path) : Nil\n  #   raise NotImplementedError.new(\"load_file\")\n  # end\n\n  # def load_file?(path : String | ::Path) : Bool\n  #   raise NotImplementedError.new(\"load_file?\")\n  # end\n\n  # private def open_library(path : String) : Nil\n  #   raise NotImplementedError.new(\"open_library\")\n  # end\n\n  # def self.default_search_paths : Array(String)\n  #   raise NotImplementedError.new(\"close_all\")\n  # end\n\n  def find_symbol(name : String) : Handle\n    find_symbol?(name) || raise LoadError.new \"undefined reference to `#{name}'\"\n  end\n\n  def load_library(libname : String) : Nil\n    load_library?(libname) || raise LoadError.new \"cannot find -l#{libname}\"\n  end\n\n  def load_library?(libname : String) : Bool\n    if ::Path::SEPARATORS.any? { |separator| libname.includes?(separator) }\n      return load_file?(::Path[libname].expand)\n    end\n\n    @search_paths.each do |directory|\n      library_path = File.join(directory, Loader.library_filename(libname))\n      return true if load_file?(library_path)\n    end\n\n    false\n  end\n\n  def close_all : Nil\n  end\n\n  def finalize\n    close_all\n  end\nend\n\n{% if flag?(:unix) %}\n  require \"./loader/unix\"\n{% elsif flag?(:msvc) %}\n  require \"./loader/msvc\"\n{% elsif flag?(:win32) && flag?(:gnu) %}\n  require \"./loader/mingw\"\n{% end %}\n"
  },
  {
    "path": "src/compiler/crystal/macros/interpreter.cr",
    "content": "module Crystal\n  class MacroInterpreter < Visitor\n    getter last : ASTNode\n    property free_vars : Hash(String, TypeVar)?\n    property macro_expansion_pragmas : Hash(Int32, Array(Lexer::LocPragma))? = nil\n\n    def self.new(program, scope : Type, path_lookup : Type, a_macro : Macro, call, a_def : Def? = nil, in_macro = false)\n      vars = {} of String => ASTNode\n      splat_index = a_macro.splat_index\n      double_splat = a_macro.double_splat\n\n      # Process regular args\n      # (skip the splat index because we need to create an array for it)\n      a_macro.match(call.args) do |macro_arg, macro_arg_index, call_arg, call_arg_index|\n        vars[macro_arg.name] = call_arg if macro_arg_index != splat_index\n      end\n\n      # Gather splat args into an array\n      if splat_index\n        splat_arg = a_macro.args[splat_index]\n        unless splat_arg.name.empty?\n          splat_elements = if splat_index < call.args.size\n                             splat_size = Splat.size(a_macro, call.args)\n                             call.args[splat_index, splat_size]\n                           else\n                             [] of ASTNode\n                           end\n          vars[splat_arg.name] = TupleLiteral.new(splat_elements)\n        end\n      end\n\n      # The double splat argument\n      if double_splat\n        named_tuple_elems = [] of NamedTupleLiteral::Entry\n        if named_args = call.named_args\n          named_args.each do |named_arg|\n            # Skip an argument that's already there as a positional argument\n            next if a_macro.args.any? &.external_name.==(named_arg.name)\n\n            named_tuple_elems << NamedTupleLiteral::Entry.new(named_arg.name, named_arg.value)\n          end\n        end\n\n        vars[double_splat.name] = NamedTupleLiteral.new(named_tuple_elems)\n      end\n\n      # Process default values\n      a_macro.args.each do |macro_arg|\n        default_value = macro_arg.default_value\n        next unless default_value\n\n        next if vars.has_key?(macro_arg.name)\n\n        default_value = default_value.expand_node(call.location, call.end_location) if default_value.is_a?(MagicConstant)\n        vars[macro_arg.name] = default_value.clone\n      end\n\n      # The named arguments\n      call.named_args.try &.each do |named_arg|\n        arg = a_macro.args.find { |arg| arg.external_name == named_arg.name }\n        arg_name = arg.try(&.name) || named_arg.name\n        vars[arg_name] = named_arg.value\n      end\n\n      # The block arg\n      call_block = call.block\n      macro_block_arg = a_macro.block_arg\n      if macro_block_arg\n        vars[macro_block_arg.name] = call_block || Nop.new\n      end\n\n      new(program, scope, path_lookup, a_macro.location, vars, call.block, a_def, in_macro, call)\n    end\n\n    record MacroVarKey, name : String, exps : Array(ASTNode)?\n\n    def initialize(@program : Program,\n                   @scope : Type, @path_lookup : Type, @location : Location?,\n                   @vars = {} of String => ASTNode, @block : Block? = nil, @def : Def? = nil,\n                   @in_macro = false, @call : Call? = nil)\n      @str = IO::Memory.new(512) # Can't be String::Builder because of `{{debug}}`\n      @last = Nop.new\n    end\n\n    def define_var(name : String, value : ASTNode) : Nil\n      @vars[name] = value\n    end\n\n    # Calls the program's `interpreted_node_hook` hook with the macro ASTNode that was interpreted.\n    def interpreted_hook(node : ASTNode, *, location custom_location : Location? = nil) : ASTNode\n      @program.interpreted_node_hook.try &.call(node, false, false, custom_location)\n\n      node\n    end\n\n    # Calls the program's `interpreted_node_hook` hook with the macro ASTNode that was _not_ interpreted.\n    def not_interpreted_hook(node : ASTNode, use_significant_node : Bool = false, *, location custom_location : Location? = nil) : ASTNode\n      return node unless interpreted_hook = @program.interpreted_node_hook\n\n      interpreted_hook.call node, true, use_significant_node, custom_location\n\n      # If a Yield was missed, also mark the code that would have ran as missed.\n      if node.is_a?(Yield) && (block = @block)\n        interpreted_hook.call block.body, true, false, nil\n      end\n\n      node\n    end\n\n    def accept(node)\n      node.accept self\n      @last\n    end\n\n    def visit(node : Expressions)\n      node.expressions.each &.accept self\n      false\n    end\n\n    def visit(node : MacroExpression)\n      node.exp.accept self\n\n      if node.output?\n        is_yield = node.exp.is_a?(Yield) && !@last.is_a?(Nop)\n        if (loc = @last.location) && loc.filename.is_a?(String) || is_yield\n          macro_expansion_pragmas = @macro_expansion_pragmas ||= {} of Int32 => Array(Lexer::LocPragma)\n          (macro_expansion_pragmas[@str.pos.to_i32] ||= [] of Lexer::LocPragma) << Lexer::LocPushPragma.new\n          @str << \"begin\\n\" if is_yield\n          @last.to_s(@str, macro_expansion_pragmas: macro_expansion_pragmas, emit_doc: true)\n          @str << \" end\" if is_yield\n          (macro_expansion_pragmas[@str.pos.to_i32] ||= [] of Lexer::LocPragma) << Lexer::LocPopPragma.new\n        else\n          @last.to_s(@str, emit_location_pragmas: !!@program.interpreted_node_hook)\n        end\n      end\n\n      false\n    end\n\n    def visit(node : MacroLiteral)\n      @str << node.value\n      false\n    end\n\n    def visit(node : MacroVerbatim)\n      exp = node.exp\n      if exp.is_a?(Expressions)\n        exp.expressions.each do |subexp|\n          subexp.to_s(@str, emit_location_pragmas: !!@program.interpreted_node_hook)\n        end\n      else\n        exp.to_s(@str, emit_location_pragmas: !!@program.interpreted_node_hook)\n      end\n      false\n    end\n\n    def visit(node : Var)\n      self.interpreted_hook node\n\n      var = @vars[node.name]?\n      if var\n        @last = var\n        return false\n      end\n\n      # Try to consider the var as a top-level macro call.\n      #\n      # Note: this should really be done at the parser level. However,\n      # currently macro calls with blocks are possible, for example:\n      #\n      # some_macro_call do |arg|\n      #   {{arg}}\n      # end\n      #\n      # and in this case the parser has no idea about this, so the only\n      # solution is to do it now.\n      if value = interpret_top_level_call?(Call.new(node.name))\n        @last = value\n        return false\n      end\n\n      node.raise \"undefined macro variable '#{node.name}'\"\n    end\n\n    def visit(node : StringInterpolation)\n      @last = StringLiteral.new(String.build do |str|\n        node.expressions.each do |exp|\n          if exp.is_a?(StringLiteral)\n            str << exp.value\n          else\n            exp.accept self\n            @last.to_s(str)\n          end\n        end\n      end)\n      false\n    end\n\n    def visit(node : MacroIf)\n      self.interpreted_hook node\n\n      node.cond.accept self\n\n      body = if @last.truthy?\n               self.not_interpreted_hook node.else, use_significant_node: true\n               node.then\n             else\n               self.not_interpreted_hook node.then, use_significant_node: true\n               node.else\n             end\n\n      body.accept self\n\n      false\n    end\n\n    def visit(node : MacroFor)\n      self.interpreted_hook node.exp\n\n      node.exp.accept self\n\n      exp = @last\n      case exp\n      when ArrayLiteral\n        visit_macro_for_array_like node, exp\n      when TupleLiteral\n        visit_macro_for_array_like node, exp\n      when HashLiteral\n        visit_macro_for_hash_like(node, exp, exp.entries) do |entry|\n          {entry.key, entry.value}\n        end\n      when NamedTupleLiteral\n        visit_macro_for_hash_like(node, exp, exp.entries) do |entry|\n          {MacroId.new(entry.key), entry.value}\n        end\n      when RangeLiteral\n        range = exp.interpret_to_range(self)\n\n        element_var = node.vars[0]\n        index_var = node.vars[1]?\n\n        if range.empty?\n          self.not_interpreted_hook node.body, use_significant_node: true\n        end\n\n        range.each_with_index do |element, index|\n          @vars[element_var.name] = NumberLiteral.new(element)\n          if index_var\n            @vars[index_var.name] = NumberLiteral.new(index)\n          end\n          node.body.accept self\n        end\n\n        @vars.delete element_var.name\n        @vars.delete index_var.name if index_var\n      when TypeNode\n        type = exp.type\n\n        case type\n        when TupleInstanceType\n          visit_macro_for_array_like(node, exp, type.tuple_types) do |type|\n            TypeNode.new(type)\n          end\n        when NamedTupleInstanceType\n          visit_macro_for_hash_like(node, exp, type.entries) do |entry|\n            {MacroId.new(entry.name), TypeNode.new(entry.type)}\n          end\n        else\n          exp.raise \"can't iterate TypeNode of type #{type}, only tuple or named tuple types\"\n        end\n      else\n        node.exp.raise \"`for` expression must be an array, hash, tuple, named tuple or a range literal, not #{exp.class_desc}:\\n\\n#{exp}\"\n      end\n\n      false\n    end\n\n    def visit_macro_for_array_like(node, exp)\n      visit_macro_for_array_like node, exp, exp.elements, &.itself\n    end\n\n    def visit_macro_for_array_like(node, exp, entries, &)\n      element_var = node.vars[0]\n      index_var = node.vars[1]?\n\n      if entries.empty?\n        self.not_interpreted_hook node.body, use_significant_node: true\n      end\n\n      entries.each_with_index do |element, index|\n        @vars[element_var.name] = yield element\n        if index_var\n          @vars[index_var.name] = NumberLiteral.new(index)\n        end\n        node.body.accept self\n      end\n\n      @vars.delete element_var.name\n      @vars.delete index_var.name if index_var\n    end\n\n    def visit_macro_for_hash_like(node, exp, entries, &)\n      key_var = node.vars[0]\n      value_var = node.vars[1]?\n      index_var = node.vars[2]?\n\n      if entries.empty?\n        self.not_interpreted_hook node.body, use_significant_node: true\n      end\n\n      entries.each_with_index do |entry, i|\n        key, value = yield entry, value_var\n\n        @vars[key_var.name] = key\n        @vars[value_var.name] = value if value_var\n        @vars[index_var.name] = NumberLiteral.new(i) if index_var\n\n        node.body.accept self\n      end\n\n      @vars.delete key_var.name\n      @vars.delete value_var.name if value_var\n      @vars.delete index_var.name if index_var\n    end\n\n    def visit(node : MacroVar)\n      self.interpreted_hook node\n\n      if exps = node.exps\n        exps = exps.map { |exp| accept exp }\n      else\n        exps = nil\n      end\n\n      key = MacroVarKey.new(node.name, exps)\n\n      macro_vars = @macro_vars ||= {} of MacroVarKey => String\n      macro_var = macro_vars[key] ||= @program.new_temp_var_name\n      @str << macro_var\n      false\n    end\n\n    def visit(node : Assign)\n      self.interpreted_hook node\n\n      case target = node.target\n      when Var\n        node.value.accept self\n        @vars[target.name] = @last\n      when Underscore\n        node.value.accept self\n      else\n        node.raise \"can only assign to variables, not #{target.class_desc}\"\n      end\n\n      false\n    end\n\n    def visit(node : OpAssign)\n      @program.normalize(node).accept(self)\n      false\n    end\n\n    def visit(node : MultiAssign)\n      @program.literal_expander.expand(node).accept(self)\n      false\n    end\n\n    def visit(node : And)\n      self.interpreted_hook node\n\n      node.left.accept self\n\n      if @last.truthy?\n        node.right.accept self\n      else\n        self.not_interpreted_hook node.right, use_significant_node: true\n      end\n\n      false\n    end\n\n    def visit(node : Or)\n      self.interpreted_hook node\n\n      node.left.accept self\n\n      if !@last.truthy?\n        node.right.accept self\n      else\n        self.not_interpreted_hook node.right, use_significant_node: true\n      end\n\n      false\n    end\n\n    def visit(node : Not)\n      node.exp.accept self\n      @last = BoolLiteral.new(!@last.truthy?)\n      false\n    end\n\n    def visit(node : If)\n      self.interpreted_hook node\n\n      node.cond.accept self\n\n      a_then, a_else = node.then, node.else\n      unless @last.truthy?\n        a_then, a_else = a_else, a_then\n      end\n\n      self.not_interpreted_hook a_else\n      a_then.accept self\n\n      false\n    end\n\n    def visit(node : Unless)\n      self.interpreted_hook node\n\n      node.cond.accept self\n\n      a_then, a_else = node.then, node.else\n      if @last.truthy?\n        a_then, a_else = a_else, a_then\n      end\n\n      self.not_interpreted_hook a_else\n      a_then.accept self\n\n      false\n    end\n\n    def visit(node : Call)\n      obj = node.obj\n      if obj\n        if obj.is_a?(Var) && (existing_var = @vars[obj.name]?)\n          receiver = existing_var\n        else\n          obj.accept self\n          receiver = @last\n        end\n\n        self.interpreted_hook obj, location: node.name_location\n\n        args = node.args.map { |arg| accept arg }\n        named_args = node.named_args.try &.to_h { |arg| {arg.name, accept arg.value} }\n\n        # normalize needed for param unpacking\n        block = node.block.try { |b| @program.normalize(b) }\n\n        begin\n          @last = receiver.interpret(node.name, args, named_args, block, self, node.name_location)\n        rescue ex : MacroRaiseException\n          # Re-raise to avoid the logic in the other rescue blocks and to retain the original location\n          raise ex\n        rescue ex : Crystal::CodeError\n          node.raise ex.message, inner: ex\n        rescue ex\n          node.raise ex.message\n        end\n      else\n        self.interpreted_hook node\n\n        # no receiver: special calls\n        # may raise `Crystal::TopLevelMacroRaiseException`\n        interpret_top_level_call node\n      end\n\n      false\n    end\n\n    def visit(node : Yield)\n      self.interpreted_hook node\n\n      unless @in_macro\n        node.raise \"can't use `{{yield}}` outside a macro\"\n      end\n\n      if block = @block\n        if node.exps.empty?\n          @last = block.body.clone\n        else\n          block_vars = {} of String => ASTNode\n          node.exps.each_with_index do |exp, i|\n            if block_arg = block.args[i]?\n              block_vars[block_arg.name] = accept exp.clone\n            end\n          end\n          @last = replace_block_vars block.body.clone, block_vars\n        end\n      else\n        @last = Nop.new\n      end\n      false\n    end\n\n    def visit(node : Path)\n      self.interpreted_hook node\n\n      @last = resolve(node)\n      false\n    end\n\n    def visit(node : Generic)\n      @last = resolve(node)\n      false\n    end\n\n    def resolve(node : Path)\n      resolve?(node) || node.raise_undefined_constant(@path_lookup)\n    end\n\n    def resolve?(node : Path)\n      if (single_name = node.single_name?) && (match = @free_vars.try &.[single_name]?)\n        matched_type = match\n      else\n        matched_type = @path_lookup.lookup_path(node)\n      end\n\n      return unless matched_type\n\n      case matched_type\n      when Const\n        @program.check_deprecated_constant(matched_type, node)\n        matched_type.value\n      when Type\n        matched_type = matched_type.remove_alias\n\n        # If it's the T of a variadic generic type, produce tuple literals\n        # or named tuple literals. The compiler has them as a type\n        # (a tuple type, or a named tuple type) but the user should see\n        # them as literals, and having them as a type doesn't add\n        # any useful information.\n        path_lookup = @path_lookup.instance_type\n        if node.names.size == 1\n          case path_lookup\n          when UnionType\n            produce_tuple = node.names.first == \"T\"\n          when GenericInstanceType\n            produce_tuple = ((splat_index = path_lookup.splat_index) &&\n                             path_lookup.type_vars.keys.index(node.names.first) == splat_index) ||\n                            (path_lookup.double_variadic? && path_lookup.type_vars.first_key == node.names.first)\n          else\n            produce_tuple = false\n          end\n\n          if produce_tuple\n            case matched_type\n            when TupleInstanceType\n              return TupleLiteral.map(matched_type.tuple_types) { |t| TypeNode.new(t) }\n            when NamedTupleInstanceType\n              entries = matched_type.entries.map do |entry|\n                NamedTupleLiteral::Entry.new(entry.name, TypeNode.new(entry.type))\n              end\n              return NamedTupleLiteral.new(entries)\n            when UnionType\n              return TupleLiteral.map(matched_type.union_types) { |t| TypeNode.new(t) }\n            end\n          end\n        end\n\n        TypeNode.new(matched_type)\n      when ASTNode\n        matched_type\n      else\n        node.raise \"can't interpret #{node}\"\n      end\n    end\n\n    def resolve(node : Generic | Metaclass | ProcNotation)\n      type = @path_lookup.lookup_type(node, self_type: @scope, free_vars: @free_vars)\n      TypeNode.new(type)\n    end\n\n    def resolve?(node : Generic | Metaclass | ProcNotation)\n      resolve(node)\n    rescue Crystal::CodeError\n      nil\n    end\n\n    def resolve(node : Union)\n      union_type = @program.union_of(node.types.map do |type|\n        resolve(type).type\n      end)\n      TypeNode.new(union_type.not_nil!)\n    end\n\n    def resolve?(node : Union)\n      union_type = @program.union_of(node.types.map do |type|\n        resolved = resolve?(type)\n        return nil unless resolved\n\n        resolved.type\n      end)\n      TypeNode.new(union_type.not_nil!)\n    end\n\n    def resolve(node : ASTNode?)\n      node.raise \"can't resolve #{node} (#{node.class_desc})\"\n    end\n\n    def resolve?(node : ASTNode)\n      node.raise \"can't resolve #{node} (#{node.class_desc})\"\n    end\n\n    def visit(node : SizeOf)\n      type_node = resolve(node.exp)\n      unless type_node.is_a?(TypeNode) && stable_abi?(type_node.type)\n        node.raise \"argument to `sizeof` inside macros must be a type with a stable size\"\n      end\n\n      @last = NumberLiteral.new(@program.size_of(type_node.type.sizeof_type).to_i32)\n      false\n    end\n\n    def visit(node : AlignOf)\n      type_node = resolve(node.exp)\n      unless type_node.is_a?(TypeNode) && stable_abi?(type_node.type)\n        node.raise \"argument to `alignof` inside macros must be a type with a stable alignment\"\n      end\n\n      @last = NumberLiteral.new(@program.align_of(type_node.type.sizeof_type).to_i32)\n      false\n    end\n\n    # Returns whether *type*'s size and alignment are stable with respect to\n    # source code augmentation, i.e. they remain unchanged at the top level even\n    # as new code is being processed by the compiler at various phases.\n    #\n    # `instance_sizeof` and `instance_alignof` are inherently unstable, as they\n    # only work on subclasses of `Reference`, and instance variables can be\n    # added to them at will.\n    #\n    # This method does not imply there is a publicly stable ABI yet!\n    private def stable_abi?(type : Type) : Bool\n      case type\n      when ReferenceStorageType\n        # instance variables may be added at will\n        false\n      when GenericType, AnnotationType\n        # no such values exist\n        false\n      when .module?\n        # ABI-equivalent to the union of all including types, which may be added\n        # at will\n        false\n      when ProcInstanceType, PointerInstanceType\n        true\n      when StaticArrayInstanceType\n        stable_abi?(type.element_type)\n      when TupleInstanceType\n        type.tuple_types.all? { |t| stable_abi?(t) }\n      when NamedTupleInstanceType\n        type.entries.all? { |entry| stable_abi?(entry.type) }\n      when InstanceVarContainer\n        # instance variables of structs may be added at will; references always\n        # have the size and alignment of a pointer\n        !type.struct?\n      when UnionType\n        type.union_types.all? { |t| stable_abi?(t) }\n      when TypeDefType\n        stable_abi?(type.typedef)\n      when AliasType\n        stable_abi?(type.aliased_type)\n      else\n        true\n      end\n    end\n\n    def visit(node : Splat)\n      warnings.add_warning(node, \"Deprecated use of splat operator. Use `#splat` instead\")\n      node.exp.accept self\n      @last = @last.interpret(\"splat\", [] of ASTNode, nil, nil, self, node.location)\n      false\n    end\n\n    def visit(node : DoubleSplat)\n      warnings.add_warning(node, \"Deprecated use of double splat operator. Use `#double_splat` instead\")\n      node.exp.accept self\n      @last = @last.interpret(\"double_splat\", [] of ASTNode, nil, nil, self, node.location)\n      false\n    end\n\n    def visit(node : IsA)\n      node.obj.accept self\n      macro_type = @program.lookup_macro_type(node.const)\n      @last = BoolLiteral.new(@last.macro_is_a?(macro_type))\n      false\n    end\n\n    def visit(node : InstanceVar)\n      case node.name\n      when \"@type\"\n        target = @scope == @program.class_type ? @scope : @scope.instance_type\n        @last = TypeNode.new(target.devirtualize)\n      when \"@top_level\"\n        @last = TypeNode.new(@program)\n      when \"@def\"\n        @last = @def || NilLiteral.new\n      when \"@caller\"\n        @last = if call = @call\n                  ArrayLiteral.map [call], &.itself\n                else\n                  NilLiteral.new\n                end\n      else\n        node.raise \"unknown macro instance var: '#{node.name}'\"\n      end\n      false\n    end\n\n    def visit(node : TupleLiteral)\n      self.interpreted_hook node\n\n      @last = TupleLiteral.map(node.elements) { |element| accept element }\n      false\n    end\n\n    def visit(node : ArrayLiteral)\n      self.interpreted_hook node\n\n      @last = ArrayLiteral.map(node.elements) { |element| accept element }\n      false\n    end\n\n    def visit(node : HashLiteral)\n      self.interpreted_hook node\n\n      @last =\n        HashLiteral.new(node.entries.map do |entry|\n          HashLiteral::Entry.new(accept(entry.key), accept(entry.value))\n        end)\n      false\n    end\n\n    def visit(node : NamedTupleLiteral)\n      @last =\n        NamedTupleLiteral.new(node.entries.map do |entry|\n          NamedTupleLiteral::Entry.new(entry.key, accept(entry.value))\n        end)\n      false\n    end\n\n    def visit(node : Nop | NilLiteral | BoolLiteral | NumberLiteral | CharLiteral | StringLiteral | SymbolLiteral | RangeLiteral | RegexLiteral | MacroId | TypeNode | Def)\n      self.interpreted_hook node\n\n      @last = node.clone_without_location\n      false\n    end\n\n    def visit(node : ASTNode)\n      node.raise \"can't execute #{node.class_desc} in a macro\"\n    end\n\n    def to_s : String\n      @str.to_s\n    end\n\n    def replace_block_vars(body, vars)\n      transformer = ReplaceBlockVarsTransformer.new(vars)\n      body.transform transformer\n    end\n\n    class ReplaceBlockVarsTransformer < Transformer\n      @vars : Hash(String, ASTNode)\n\n      def initialize(@vars)\n      end\n\n      def transform(node : MacroExpression)\n        if (exp = node.exp).is_a?(Var)\n          replacement = @vars[exp.name]?\n          return replacement if replacement\n        end\n        node\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/macros/macros.cr",
    "content": "class Crystal::Program\n  # Temporary files which are generated by macro runs that need to be\n  # deleted after the compilation is finished.\n  getter tempfiles = [] of String\n\n  # Returns a `MacroExpander` to expand macro code into crystal code.\n  getter(macro_expander) { MacroExpander.new self }\n\n  # A cache of compiled \"macro run\" files.\n  # The keys are filenames that were compiled, the values are executable\n  # filenames ready to be run (so they don't need to be compiled twice),\n  # together with the time it took to compile them and whether a previous\n  # compilation was reused.\n  # The elapsed time is only needed for stats.\n  record CompiledMacroRun, filename : String, elapsed : Time::Span, reused : Bool\n  property compiled_macros_cache = {} of String => CompiledMacroRun\n\n  property interpreted_node_hook : Proc(ASTNode, Bool, Bool, Location?, Nil)? = nil\n  property macro_expanded_hook : Proc(Nil)? = nil\n  property macro_expansion_error_hook : Proc(::Exception?, Nil)? = nil\n\n  def expand_macro(a_macro : Macro, call : Call, scope : Type, path_lookup : Type? = nil, a_def : Def? = nil)\n    check_call_to_deprecated_macro a_macro, call\n\n    interpreter = MacroInterpreter.new self, scope, path_lookup || scope, a_macro, call, a_def, in_macro: true\n    a_macro.body.accept interpreter\n    {interpreter.to_s, interpreter.macro_expansion_pragmas}\n  rescue ex\n    raise ex if @program.macro_expansion_error_hook.nil?\n\n    # See SkipMacroCodeCoverageException's definition for more information.\n    raise SkipMacroCodeCoverageException.new ex\n  ensure\n    @program.macro_expanded_hook.try &.call\n  end\n\n  def expand_macro(node : ASTNode, scope : Type, path_lookup : Type? = nil, free_vars = nil, a_def : Def? = nil)\n    interpreter = MacroInterpreter.new self, scope, path_lookup || scope, node.location, def: a_def, in_macro: false\n    interpreter.free_vars = free_vars\n    node.accept interpreter\n    {interpreter.to_s, interpreter.macro_expansion_pragmas}\n  rescue ex\n    raise ex if @program.macro_expansion_error_hook.nil?\n\n    # See SkipMacroCodeCoverageException's definition for more information.\n    raise SkipMacroCodeCoverageException.new ex\n  ensure\n    @program.macro_expanded_hook.try &.call\n  end\n\n  def parse_macro_source(generated_source, macro_expansion_pragmas, the_macro, node, vars, current_def = nil, inside_type = false, inside_exp = false, mode : Parser::ParseMode = :normal, visibility : Visibility = :public)\n    parse_macro_source generated_source, macro_expansion_pragmas, the_macro, node, vars, current_def, inside_type, inside_exp, visibility, &.parse(mode)\n  end\n\n  def parse_macro_source(generated_source, macro_expansion_pragmas, the_macro, node, vars, current_def = nil, inside_type = false, inside_exp = false, visibility : Visibility = :public, &)\n    parser = @program.new_parser(generated_source, var_scopes: [vars.dup])\n    parser.filename = VirtualFile.new(the_macro, generated_source, node.location)\n    parser.macro_expansion_pragmas = macro_expansion_pragmas\n    parser.visibility = visibility\n    parser.def_nest = 1 if current_def && !current_def.is_a?(External)\n    parser.fun_nest = 1 if current_def && current_def.is_a?(External)\n    parser.type_nest = 1 if inside_type\n    parser.wants_doc = @program.wants_doc?\n    generated_node = yield parser\n    normalize(generated_node, inside_exp: inside_exp, current_def: current_def)\n  end\n\n  record MacroRunResult, stdout : String, stderr : String, status : Process::Status\n\n  def macro_run(filename, args)\n    compiled_macro_run = @compiled_macros_cache[filename] ||= macro_compile(filename)\n    compiled_file = compiled_macro_run.filename\n\n    out_io = IO::Memory.new\n    err_io = IO::Memory.new\n    Process.run(compiled_file, args: args, output: out_io, error: err_io)\n    MacroRunResult.new(out_io.to_s, err_io.to_s, $?)\n  end\n\n  record RequireWithTimestamp, filename : String, epoch : Int64 do\n    include JSON::Serializable\n  end\n\n  def macro_compile(filename)\n    time = Time.instant\n\n    source = File.read(filename)\n\n    # We store the executable relative to the cache directory for 'filename',\n    # that way if it's already there from a previous compilation, and no file\n    # that this program uses changes, we can simply avoid recompiling it again\n    #\n    # NOTE: it could happen that a macro run program runs macros that could\n    # change the program behaviour even if files don't change, but this is\n    # discouraged (and we should strongly document it) because it prevents\n    # incremental compiles.\n    program_dir = CacheDir.instance.directory_for(filename)\n    executable_path = File.join(program_dir, \"macro_run\")\n    recorded_requires_path = File.join(program_dir, \"recorded_requires\")\n    requires_path = File.join(program_dir, \"requires\")\n\n    {% unless flag?(:win32) %}\n      # First, update times for the program dir, so it remains in the cache longer\n      # (this is specially useful if a macro run program is used by multiple programs)\n      now = Time.utc\n      File.utime(now, now, program_dir)\n    {% end %}\n\n    if can_reuse_previous_compilation?(filename, executable_path, recorded_requires_path, requires_path)\n      return CompiledMacroRun.new(executable_path, time.elapsed, true)\n    end\n\n    result = host_compiler.compile Compiler::Source.new(filename, source), executable_path\n\n    # Write the new files from which 'filename' depends into the cache dir\n    # (here we store how to obtain these files, because a require might use\n    # '/*' or '/**' and we need to recompile if a file is added or removed)\n    File.open(recorded_requires_path, \"w\") do |file|\n      result.program.recorded_requires.to_json(file)\n    end\n\n    # Together with their timestamp\n    # (this is the list of all effective files that were required)\n    requires_with_timestamps = result.program.requires.map do |required_file|\n      epoch = File.info(required_file).modification_time.to_unix\n      RequireWithTimestamp.new(required_file, epoch)\n    end\n\n    File.open(requires_path, \"w\") do |file|\n      requires_with_timestamps.to_json(file)\n    end\n\n    CompiledMacroRun.new(executable_path, time.elapsed, false)\n  end\n\n  @host_compiler : Compiler?\n\n  # Creates a compiler instance with the host as target used for macro_run\n  # compilation.\n  def host_compiler\n    @host_compiler ||= Compiler.new.tap do |host_compiler|\n      if compiler = self.compiler\n        # When cross-compiling, the host compiler shouldn't copy the config for\n        # the target compiler and use the system defaults instead.\n        # TODO: Add configuration overrides for host compiler to CLI.\n        unless compiler.cross_compile?\n          host_compiler.flags = compiler.flags\n          host_compiler.dump_ll = compiler.dump_ll?\n          host_compiler.link_flags = compiler.link_flags\n          host_compiler.mcpu = compiler.mcpu\n          host_compiler.mattr = compiler.mattr\n          host_compiler.mcmodel = compiler.mcmodel\n          host_compiler.single_module = compiler.single_module?\n          host_compiler.static = compiler.static?\n        end\n\n        # Copy default settings that are not specific to host and target context:\n        host_compiler.n_threads = compiler.n_threads\n        host_compiler.prelude = compiler.prelude\n      end\n\n      # Although release takes longer, once the bc is cached in .crystal\n      # the subsequent times will make program execution faster.\n      host_compiler.release!\n\n      # Don't cleanup old directories after compiling: it might happen\n      # that in doing so we remove the directory associated with the current\n      # compilation (for example if we have more than 10 macro runs, the current\n      # directory will be the oldest).\n      host_compiler.cleanup = false\n\n      # No need to generate debug info for macro run programs\n      host_compiler.debug = Crystal::Debug::None\n    end\n  end\n\n  private def can_reuse_previous_compilation?(filename, executable_path, recorded_requires_path, requires_path)\n    return false unless File.exists?(executable_path)\n    return false unless File.exists?(recorded_requires_path)\n    return false unless File.exists?(requires_path)\n\n    recorded_requires =\n      begin\n        Array(Program::RecordedRequire).from_json(File.read(recorded_requires_path))\n      rescue JSON::Error\n        return false\n      end\n\n    requires_with_timestamps =\n      begin\n        Array(RequireWithTimestamp).from_json(File.read(requires_path))\n      rescue JSON::Error\n        return false\n      end\n\n    # From the recorded requires we reconstruct the effective required files.\n    # We start with the target filename\n    required_files = Set{filename}\n    recorded_requires.map do |recorded_require|\n      files = @program.find_in_path(recorded_require.filename, recorded_require.relative_to)\n      required_files.concat(files) if files\n    rescue Crystal::CrystalPath::NotFoundError\n      # Maybe the file is gone\n      next\n    end\n\n    new_requires_with_timestamps = required_files.map do |required_file|\n      epoch = File.info(required_file).modification_time.to_unix\n      RequireWithTimestamp.new(required_file, epoch)\n    end\n\n    # Quick check: if there are a different number of files, something changed\n    if requires_with_timestamps.size != new_requires_with_timestamps.size\n      return false\n    end\n\n    # Sort both requires and check if they are the same\n    requires_with_timestamps.sort_by! &.filename\n    new_requires_with_timestamps.sort_by! &.filename\n    requires_with_timestamps == new_requires_with_timestamps\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/macros/methods.cr",
    "content": "require \"../semantic/ast\"\nrequire \"./macros\"\nrequire \"semantic_version\"\n\nmodule Crystal\n  class MacroInterpreter\n    private def find_source_file(filename, &)\n      # Support absolute paths\n      if filename.starts_with?('/')\n        filename = \"#{filename}.cr\" unless filename.ends_with?(\".cr\")\n\n        if File.exists?(filename)\n          unless File.file?(filename)\n            return yield \"#{filename.inspect} is not a file\"\n          end\n        else\n          return yield \"can't find file #{filename.inspect}\"\n        end\n      else\n        begin\n          relative_to = @location.try &.original_filename\n          found_filenames = @program.find_in_path(filename, relative_to)\n        rescue ex\n          return yield ex.message\n        end\n\n        unless found_filenames\n          return yield \"can't find file #{filename.inspect}\"\n        end\n\n        if found_filenames.size > 1\n          return yield \"#{filename.inspect} is a directory\"\n        end\n\n        filename = found_filenames.first\n      end\n      filename\n    end\n\n    delegate warnings, to: @program\n\n    def interpret_top_level_call(node)\n      interpret_top_level_call?(node) ||\n        node.raise(\"undefined macro method: '#{node.name}'\")\n    end\n\n    def interpret_top_level_call?(node)\n      # Please order method names in lexicographical order\n      case node.name\n      when \"compare_versions\"\n        interpret_compare_versions(node)\n      when \"debug\"\n        interpret_debug(node)\n      when \"env\"\n        interpret_env(node)\n      when \"flag?\", \"host_flag?\"\n        interpret_flag?(node)\n      when \"parse_type\"\n        interpret_parse_type(node)\n      when \"puts\"\n        interpret_puts(node)\n      when \"print\"\n        interpret_print(node)\n      when \"p\", \"pp\"\n        interpret_p(node)\n      when \"p!\", \"pp!\"\n        interpret_pp!(node)\n      when \"skip_file\"\n        interpret_skip_file(node)\n      when \"system\", \"`\"\n        interpret_system(node)\n      when \"raise\"\n        interpret_raise(node)\n      when \"warning\"\n        interpret_warning(node)\n      when \"file_exists?\"\n        interpret_file_exists?(node)\n      when \"read_file\"\n        interpret_read_file(node)\n      when \"read_file?\"\n        interpret_read_file(node, nilable: true)\n      when \"run\"\n        interpret_run(node)\n      else\n        nil\n      end\n    end\n\n    def interpret_compare_versions(node)\n      interpret_check_args_toplevel do |first_arg, second_arg|\n        first = accept first_arg\n        first_string = first.to_string(\"first argument to 'compare_versions'\")\n\n        second = accept second_arg\n        second_string = second.to_string(\"second argument to 'compare_versions'\")\n\n        first_version = begin\n          SemanticVersion.parse(first_string)\n        rescue ex\n          first_arg.raise ex.message\n        end\n\n        second_version = begin\n          SemanticVersion.parse(second_string)\n        rescue ex\n          second_arg.raise ex.message\n        end\n\n        @last = NumberLiteral.new(first_version <=> second_version)\n      end\n    end\n\n    def interpret_debug(node)\n      if node.args.size >= 1\n        node.args.first.accept self\n        format = @last.truthy?\n      elsif named_args = node.named_args\n        format_arg = named_args.find { |arg| arg.name == \"format\" }\n        if format_arg\n          format_arg.value.accept self\n          format = @last.truthy?\n        end\n      else\n        format = true\n      end\n\n      if format\n        begin\n          @program.stdout.puts Crystal::Formatter.format(@str.to_s)\n        rescue\n          @program.stdout.puts @str\n        end\n      else\n        @program.stdout.puts @str\n      end\n\n      @last = Nop.new\n    end\n\n    def interpret_env(node)\n      interpret_check_args_toplevel do |arg|\n        arg.accept self\n        cmd = @last.to_macro_id\n        env_value = ENV[cmd]?\n        @last = env_value ? StringLiteral.new(env_value) : NilLiteral.new\n      end\n    end\n\n    def interpret_flag?(node)\n      interpret_check_args_toplevel do |arg|\n        arg.accept self\n        flag_name = @last.to_macro_id\n        value = case node.name\n                when \"flag?\"\n                  @program.flag_value(flag_name)\n                when \"host_flag?\"\n                  @program.host_flag_value(flag_name)\n                else\n                  raise \"Bug: unexpected macro method #{node.name}\"\n                end\n\n        @last = case value\n                in String then StringLiteral.new(value)\n                in Bool   then BoolLiteral.new(value)\n                end\n      end\n    end\n\n    def interpret_parse_type(node)\n      interpret_check_args_toplevel do |arg|\n        arg.accept self\n        type_name = case last = @last\n                    when StringLiteral then last.value\n                    else\n                      arg.raise \"argument to parse_type must be a StringLiteral, not #{last.class_desc}\"\n                    end\n\n        arg.raise \"argument to parse_type cannot be an empty value\" if type_name.blank?\n\n        begin\n          parser = @program.new_parser type_name\n          parser.next_token\n          type = parser.parse_bare_proc_type\n          parser.check :EOF\n          @last = type\n        rescue ex : Crystal::SyntaxException\n          arg.raise \"Invalid type name: #{type_name.inspect}\"\n        end\n      end\n    end\n\n    def interpret_puts(node)\n      node.args.each do |arg|\n        arg.accept self\n        last = @last\n\n        # The only difference in macro land between `p` and `puts` is that\n        # `puts` with a string literal shouldn't show the string quotes\n        last = last.value if last.is_a?(StringLiteral)\n\n        @program.stdout.puts last\n      end\n\n      @last = Nop.new\n    end\n\n    def interpret_print(node)\n      node.args.each do |arg|\n        arg.accept self\n        last = @last\n        last = last.value if last.is_a?(StringLiteral)\n\n        @program.stdout.print last\n      end\n\n      @last = Nop.new\n    end\n\n    def interpret_p(node)\n      node.args.each do |arg|\n        arg.accept self\n        @program.stdout.puts @last\n      end\n\n      @last = Nop.new\n    end\n\n    def interpret_pp!(node)\n      strings = [] of {String, String}\n\n      node.args.each do |arg|\n        arg.accept self\n        strings.push({arg.to_s, @last.to_s})\n      end\n\n      max_size = strings.max_of &.[0].size\n      strings.each do |left, right|\n        @program.stdout.puts \"#{left.ljust(max_size)} # => #{right}\"\n      end\n\n      @last = Nop.new\n    end\n\n    def interpret_skip_file(node)\n      raise SkipMacroException.new(@str.to_s, macro_expansion_pragmas)\n    end\n\n    def interpret_system(node)\n      cmd = node.args.map do |arg|\n        arg.accept self\n        @last.to_macro_id\n      end\n      cmd = cmd.join \" \"\n\n      begin\n        result = `#{cmd}`\n      rescue exc : File::Error | IO::Error\n        # Taking the os_error message to avoid duplicating the \"error executing process: \"\n        # prefix of the error message and ensure uniqueness between all error messages.\n        node.raise \"error executing command: #{cmd}: #{exc.os_error.try(&.message) || exc.message}\"\n      rescue exc\n        node.raise \"error executing command: #{cmd}: #{exc.message}\"\n      end\n\n      if $?.success?\n        @last = MacroId.new(result)\n      elsif result.empty?\n        node.raise \"error executing command: #{cmd}, got exit status #{$?}\"\n      else\n        node.raise \"error executing command: #{cmd}, got exit status #{$?}:\\n\\n#{result}\\n\"\n      end\n    end\n\n    def interpret_raise(node)\n      macro_raise(node, node.args, self, Crystal::TopLevelMacroRaiseException)\n    end\n\n    def interpret_warning(node)\n      macro_warning(node, node.args, self)\n    end\n\n    def interpret_file_exists?(node)\n      interpret_check_args_toplevel do |arg|\n        arg.accept self\n        filename = @last.to_macro_id\n\n        @last = BoolLiteral.new(File.exists?(filename))\n      end\n    end\n\n    def interpret_read_file(node, nilable = false)\n      interpret_check_args_toplevel do |arg|\n        arg.accept self\n        filename = @last.to_macro_id\n\n        begin\n          @last = StringLiteral.new(File.read(filename))\n        rescue ex\n          node.raise ex.to_s unless nilable\n          @last = NilLiteral.new\n        end\n      end\n    end\n\n    def interpret_run(node)\n      if node.args.size == 0\n        node.wrong_number_of_arguments \"macro '::run'\", 0, \"1+\"\n      end\n\n      node.args.first.accept self\n      original_filename = @last.to_macro_id\n\n      filename = find_source_file(original_filename) do |error_message|\n        node.raise \"error executing macro 'run': #{error_message}\"\n      end\n\n      run_args = [] of String\n      node.args.each_with_index do |arg, i|\n        next if i == 0\n\n        arg.accept self\n        run_args << @last.to_macro_id\n      end\n\n      result = @program.macro_run(filename, run_args)\n      if result.status.success?\n        @last = MacroId.new(result.stdout)\n      else\n        command = \"#{Process.quote(original_filename)} #{Process.quote(run_args)}\"\n\n        message = IO::Memory.new\n        message << \"Error executing run (exit code: #{result.status}): #{command}\\n\"\n\n        if result.stdout.empty? && result.stderr.empty?\n          message << \"\\nGot no output.\"\n        else\n          Colorize.reset(message)\n\n          unless result.stdout.empty?\n            message.puts\n            message << \"stdout:\".colorize.mode(:bold)\n            message.puts\n            message.puts\n            result.stdout.each_line do |line|\n              message << \"    \"\n              message << line\n              message << '\\n'\n            end\n            message << '\\n'\n          end\n\n          unless result.stderr.empty?\n            message.puts\n            message << \"stderr:\".colorize.mode(:bold)\n            message.puts\n            message.puts\n            result.stderr.each_line do |line|\n              message << \"    \"\n              message << line\n              message << '\\n'\n            end\n            message << '\\n'\n          end\n        end\n\n        node.raise message.to_s\n      end\n    end\n  end\n\n  class ASTNode\n    def to_macro_id\n      to_s\n    end\n\n    def to_string(context)\n      case self\n      when StringLiteral then self.value\n      when SymbolLiteral then self.value\n      when MacroId       then self.value\n      else\n        raise \"expected #{context} to be a StringLiteral, SymbolLiteral or MacroId, not #{class_desc}\"\n      end\n    end\n\n    def truthy?\n      case self\n      when NilLiteral, Nop\n        false\n      when BoolLiteral\n        self.value\n      else\n        true\n      end\n    end\n\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"id\"\n        interpret_check_args { MacroId.new(to_macro_id) }\n      when \"stringify\"\n        interpret_check_args { stringify }\n      when \"symbolize\"\n        interpret_check_args { symbolize }\n      when \"class_name\"\n        interpret_check_args { class_name }\n      when \"doc\"\n        interpret_check_args do\n          StringLiteral.new self.doc || \"\"\n        end\n      when \"doc_comment\"\n        interpret_check_args do\n          MacroId.new (self.doc || \"\").gsub(\"\\n\", \"\\n# \")\n        end\n      when \"raise\"\n        macro_raise self, args, interpreter, Crystal::MacroRaiseException\n      when \"warning\"\n        macro_warning self, args, interpreter\n      when \"filename\"\n        interpret_check_args do\n          filename = location.try &.original_filename\n          filename ? StringLiteral.new(filename) : NilLiteral.new\n        end\n      when \"line_number\"\n        interpret_check_args do\n          line_number = location.try &.expanded_location.try &.line_number\n          line_number ? NumberLiteral.new(line_number) : NilLiteral.new\n        end\n      when \"column_number\"\n        interpret_check_args do\n          column_number = location.try &.expanded_location.try &.column_number\n          column_number ? NumberLiteral.new(column_number) : NilLiteral.new\n        end\n      when \"end_line_number\"\n        interpret_check_args do\n          line_number = end_location.try &.expanded_location.try &.line_number\n          line_number ? NumberLiteral.new(line_number) : NilLiteral.new\n        end\n      when \"end_column_number\"\n        interpret_check_args do\n          column_number = end_location.try &.expanded_location.try &.column_number\n          column_number ? NumberLiteral.new(column_number) : NilLiteral.new\n        end\n      when \"==\"\n        interpret_check_args do |arg|\n          BoolLiteral.new(self == arg)\n        end\n      when \"!=\"\n        interpret_check_args do |arg|\n          BoolLiteral.new(self != arg)\n        end\n      when \"!\"\n        interpret_check_args { BoolLiteral.new(!truthy?) }\n      when \"nil?\"\n        interpret_check_args { BoolLiteral.new(is_a?(NilLiteral) || is_a?(Nop)) }\n      else\n        raise \"undefined macro method '#{class_desc}##{method}'\", exception_type: Crystal::UndefinedMacroMethodError\n      end\n    end\n\n    def interpret_compare(other)\n      raise \"can't compare #{self} to #{other}\"\n    end\n\n    def stringify\n      StringLiteral.new(to_s)\n    end\n\n    def symbolize\n      SymbolLiteral.new(to_s)\n    end\n\n    def class_name\n      StringLiteral.new(class_desc)\n    end\n  end\n\n  class NilLiteral\n    def to_macro_id\n      \"nil\"\n    end\n  end\n\n  class BoolLiteral\n    def to_macro_id\n      @value ? \"true\" : \"false\"\n    end\n  end\n\n  class NumberLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \">\"\n        bool_bin_op(method, args, named_args, block) { |me, other| me > other }\n      when \">=\"\n        bool_bin_op(method, args, named_args, block) { |me, other| me >= other }\n      when \"<\"\n        bool_bin_op(method, args, named_args, block) { |me, other| me < other }\n      when \"<=\"\n        bool_bin_op(method, args, named_args, block) { |me, other| me <= other }\n      when \"<=>\"\n        num_bin_op(method, args, named_args, block) do |me, other|\n          (me <=> other) || (return NilLiteral.new)\n        end\n      when \"+\"\n        interpret_check_args(min_count: 0) do |other|\n          if other\n            raise \"can't #{method} with #{other}\" unless other.is_a?(NumberLiteral)\n            NumberLiteral.new(to_number + other.to_number)\n          else\n            self\n          end\n        end\n      when \"-\"\n        interpret_check_args(min_count: 0) do |other|\n          if other\n            raise \"can't #{method} with #{other}\" unless other.is_a?(NumberLiteral)\n            NumberLiteral.new(to_number - other.to_number)\n          else\n            num = to_number\n            raise \"undefined method '-' for unsigned integer literal: #{self}\" if num.is_a?(Int::Unsigned)\n            NumberLiteral.new(-num)\n          end\n        end\n      when \"*\"\n        num_bin_op(method, args, named_args, block) { |me, other| me * other }\n      when \"/\"\n        num_bin_op(method, args, named_args, block) { |me, other| me / other }\n      when \"//\"\n        num_bin_op(method, args, named_args, block) { |me, other| me // other }\n      when \"**\"\n        num_bin_op(method, args, named_args, block) { |me, other| me ** other }\n      when \"%\"\n        int_bin_op(method, args, named_args, block) { |me, other| me % other }\n      when \"&\"\n        int_bin_op(method, args, named_args, block) { |me, other| me & other }\n      when \"|\"\n        int_bin_op(method, args, named_args, block) { |me, other| me | other }\n      when \"^\"\n        int_bin_op(method, args, named_args, block) { |me, other| me ^ other }\n      when \"<<\"\n        int_bin_op(method, args, named_args, block) { |me, other| me << other }\n      when \">>\"\n        int_bin_op(method, args, named_args, block) { |me, other| me >> other }\n      when \"~\"\n        interpret_check_args do\n          num = to_number\n          raise \"undefined method '~' for float literal: #{self}\" unless num.is_a?(Int)\n          NumberLiteral.new(~num)\n        end\n      when \"kind\"\n        interpret_check_args { SymbolLiteral.new(kind.to_s) }\n      when \"to_number\"\n        interpret_check_args { MacroId.new(to_number.to_s) }\n      when \"zero?\"\n        interpret_check_args { BoolLiteral.new(to_number.zero?) }\n      else\n        super\n      end\n    end\n\n    def interpret_compare(other : NumberLiteral)\n      to_number <=> other.to_number\n    end\n\n    def bool_bin_op(method, args, named_args, block, &)\n      interpret_check_args do |other|\n        raise \"can't #{method} with #{other}\" unless other.is_a?(NumberLiteral)\n        BoolLiteral.new(yield to_number, other.to_number)\n      end\n    end\n\n    def num_bin_op(method, args, named_args, block, &)\n      interpret_check_args do |other|\n        raise \"can't #{method} with #{other}\" unless other.is_a?(NumberLiteral)\n        NumberLiteral.new(yield to_number, other.to_number)\n      end\n    end\n\n    def int_bin_op(method, args, named_args, block, &)\n      interpret_check_args do |other|\n        raise \"can't #{method} with #{other}\" unless other.is_a?(NumberLiteral)\n        me = to_number\n        other = other.to_number\n\n        case {me, other}\n        when {Int, Int}\n          NumberLiteral.new(yield me, other)\n        when {Float, _}\n          raise \"undefined method '#{method}' for float literal: #{self}\"\n        else\n          raise \"argument to NumberLiteral##{method} can't be float literal: #{self}\"\n        end\n      end\n    end\n\n    def to_number\n      case @kind\n      in .i8?   then @value.to_i8\n      in .i16?  then @value.to_i16\n      in .i32?  then @value.to_i32\n      in .i64?  then @value.to_i64\n      in .i128? then @value.to_i128\n      in .u8?   then @value.to_u8\n      in .u16?  then @value.to_u16\n      in .u32?  then @value.to_u32\n      in .u64?  then @value.to_u64\n      in .u128? then @value.to_u128\n      in .f32?  then @value.to_f32\n      in .f64?  then @value.to_f64\n      end\n    end\n  end\n\n  class CharLiteral\n    def to_macro_id\n      @value.to_s\n    end\n\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"ord\"\n        interpret_check_args { NumberLiteral.new(ord) }\n      else\n        super\n      end\n    end\n\n    def ord\n      @value.ord\n    end\n  end\n\n  class StringLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"==\", \"!=\"\n        interpret_check_args do |arg|\n          case arg\n          when MacroId\n            if method == \"==\"\n              BoolLiteral.new(@value == arg.value)\n            else\n              BoolLiteral.new(@value != arg.value)\n            end\n          else\n            super\n          end\n        end\n      when \"[]\"\n        interpret_check_args do |arg|\n          case arg\n          when RangeLiteral\n            range = arg.interpret_to_nilable_range(interpreter)\n            StringLiteral.new(@value[range])\n          else\n            raise \"wrong argument for StringLiteral#[] (#{arg.class_desc}): #{arg}\"\n          end\n        end\n      when \"=~\"\n        interpret_check_args do |arg|\n          case arg\n          when RegexLiteral\n            regex = regex_value(arg)\n            BoolLiteral.new(!!(@value =~ regex))\n          else\n            BoolLiteral.new(false)\n          end\n        end\n      when \">\"\n        interpret_check_args do |arg|\n          case arg\n          when StringLiteral, MacroId\n            return BoolLiteral.new(interpret_compare(arg) > 0)\n          else\n            raise \"Can't compare StringLiteral with #{arg.class_desc}\"\n          end\n        end\n      when \"<\"\n        interpret_check_args do |arg|\n          case arg\n          when StringLiteral, MacroId\n            return BoolLiteral.new(interpret_compare(arg) < 0)\n          else\n            raise \"Can't compare StringLiteral with #{arg.class_desc}\"\n          end\n        end\n      when \"+\"\n        interpret_check_args do |arg|\n          case arg\n          when CharLiteral\n            piece = arg.value\n          when StringLiteral\n            piece = arg.value\n          else\n            raise \"StringLiteral#+ expects char or string, not #{arg.class_desc}\"\n          end\n          StringLiteral.new(@value + piece)\n        end\n      when \"*\"\n        interpret_check_args do |arg|\n          unless arg.is_a?(Crystal::NumberLiteral)\n            arg.raise \"argument to StringLiteral#* must be a number, not #{arg.class_desc}\"\n          end\n\n          num = arg.to_number\n\n          unless num.is_a?(Int)\n            arg.raise \"argument to StringLiteral#* must be an integer\"\n          end\n\n          StringLiteral.new(@value * num)\n        end\n      when \"camelcase\"\n        interpret_check_args(named_params: [\"lower\"]) do\n          lower = if named_args && (lower_arg = named_args[\"lower\"]?)\n                    lower_arg\n                  else\n                    BoolLiteral.new false\n                  end\n\n          raise \"named argument 'lower' to StringLiteral#camelcase must be a bool, not #{lower.class_desc}\" unless lower.is_a?(BoolLiteral)\n\n          StringLiteral.new(@value.camelcase(lower: lower.value))\n        end\n      when \"capitalize\"\n        interpret_check_args { StringLiteral.new(@value.capitalize) }\n      when \"chars\"\n        interpret_check_args { ArrayLiteral.map(@value.chars, Path.global(\"Char\")) { |value| CharLiteral.new(value) } }\n      when \"chomp\"\n        interpret_check_args { StringLiteral.new(@value.chomp) }\n      when \"downcase\"\n        interpret_check_args { StringLiteral.new(@value.downcase) }\n      when \"empty?\"\n        interpret_check_args { BoolLiteral.new(@value.empty?) }\n      when \"ends_with?\"\n        interpret_check_args do |arg|\n          case arg\n          when CharLiteral\n            piece = arg.value\n          when StringLiteral\n            piece = arg.value\n          else\n            raise \"StringLiteral#ends_with? expects char or string, not #{arg.class_desc}\"\n          end\n          BoolLiteral.new(@value.ends_with?(piece))\n        end\n      when \"gsub\"\n        if block\n          interpret_check_args(uses_block: true) do |first|\n            raise \"first argument to StringLiteral#gsub(&) must be a regex, not #{first.class_desc}\" unless first.is_a?(RegexLiteral)\n\n            regex = regex_value first\n\n            new_value = value.gsub regex do |string, matches|\n              string_match_arg = block.args[0]?\n              matches_array_arg = block.args[1]?\n              matches_array_literal = ArrayLiteral.map matches.to_a do |item|\n                item.nil? ? NilLiteral.new : StringLiteral.new item\n              end\n\n              interpreter.define_var(string_match_arg.name, StringLiteral.new string) if string_match_arg\n              interpreter.define_var(matches_array_arg.name, matches_array_literal) if matches_array_arg\n\n              interpreter.accept(block.body).to_macro_id\n            end\n\n            StringLiteral.new new_value\n          end\n        else\n          interpret_check_args do |first, second|\n            raise \"first argument to StringLiteral#gsub must be a regex, not #{first.class_desc}\" unless first.is_a?(RegexLiteral)\n            raise \"second argument to StringLiteral#gsub must be a string, not #{second.class_desc}\" unless second.is_a?(StringLiteral)\n\n            regex = regex_value(first)\n\n            StringLiteral.new(value.gsub(regex, second.value))\n          end\n        end\n      when \"identify\"\n        interpret_check_args { StringLiteral.new(@value.tr(\":\", \"_\")) }\n      when \"includes?\"\n        interpret_check_args do |arg|\n          case arg\n          when CharLiteral\n            piece = arg.value\n          when StringLiteral\n            piece = arg.value\n          else\n            raise \"StringLiteral#includes? expects char or string, not #{arg.class_desc}\"\n          end\n          BoolLiteral.new(@value.includes?(piece))\n        end\n      when \"match\"\n        interpret_check_args do |arg|\n          unless arg.is_a?(RegexLiteral)\n            raise \"StringLiteral#match expects a regex, not #{arg.class_desc}\"\n          end\n\n          regex = regex_value(arg)\n\n          if match_data = @value.match(regex)\n            regex_captures_hash(match_data)\n          else\n            NilLiteral.new\n          end\n        end\n      when \"scan\"\n        interpret_check_args do |arg|\n          unless arg.is_a?(RegexLiteral)\n            raise \"StringLiteral#scan expects a regex, not #{arg.class_desc}\"\n          end\n\n          regex = regex_value(arg)\n\n          matches = ArrayLiteral.new(\n            of: Generic.new(\n              Path.global(\"Hash\"),\n              [\n                Union.new([Path.global(\"Int32\"), Path.global(\"String\")] of ASTNode),\n                Union.new([Path.global(\"String\"), Path.global(\"Nil\")] of ASTNode),\n              ] of ASTNode\n            )\n          )\n\n          @value.scan(regex) do |match_data|\n            matches.elements << regex_captures_hash(match_data)\n          end\n\n          matches\n        end\n      when \"size\"\n        interpret_check_args { NumberLiteral.new(@value.size) }\n      when \"lines\"\n        interpret_check_args { ArrayLiteral.map(@value.lines, Path.global(\"String\")) { |value| StringLiteral.new(value) } }\n      when \"split\"\n        interpret_check_args(min_count: 0) do |arg|\n          if arg\n            case arg\n            when CharLiteral\n              splitter = arg.value\n            when StringLiteral\n              splitter = arg.value\n            when RegexLiteral\n              splitter = regex_value(arg)\n            else\n              interpreter.warnings.add_warning_at(name_loc, \"Deprecated StringLiteral#split(ASTNode). Use `#split(StringLiteral)` instead\")\n              splitter = arg.to_s\n            end\n\n            ArrayLiteral.map(@value.split(splitter), Path.global(\"String\")) { |value| StringLiteral.new(value) }\n          else\n            ArrayLiteral.map(@value.split, Path.global(\"String\")) { |value| StringLiteral.new(value) }\n          end\n        end\n      when \"count\"\n        interpret_check_args do |arg|\n          case arg\n          when CharLiteral\n            chr = arg.value\n          else\n            raise \"StringLiteral#count expects char, not #{arg.class_desc}\"\n          end\n          NumberLiteral.new(@value.count(chr))\n        end\n      when \"starts_with?\"\n        interpret_check_args do |arg|\n          case arg\n          when CharLiteral\n            piece = arg.value\n          when StringLiteral\n            piece = arg.value\n          else\n            raise \"StringLiteral#starts_with? expects char or string, not #{arg.class_desc}\"\n          end\n          BoolLiteral.new(@value.starts_with?(piece))\n        end\n      when \"strip\"\n        interpret_check_args { StringLiteral.new(@value.strip) }\n      when \"titleize\"\n        interpret_check_args { StringLiteral.new(@value.titleize) }\n      when \"to_i\"\n        value = interpret_check_args(min_count: 0) do |base|\n          if base\n            raise \"argument to StringLiteral#to_i must be a number, not #{base.class_desc}\" unless base.is_a?(NumberLiteral)\n            @value.to_i64?(base.to_number.to_i)\n          else\n            @value.to_i64?\n          end\n        end\n\n        if value\n          NumberLiteral.new(value.to_s, :i32)\n        else\n          raise \"StringLiteral#to_i: #{@value} is not an integer\"\n        end\n      when \"to_utf16\"\n        interpret_check_args do\n          slice = @value.to_utf16\n\n          # include the trailing zero that isn't counted in the slice but was\n          # generated by String#to_utf16 so the literal can be passed to C\n          # functions that expect a null terminated UInt16*\n          args = Slice(UInt16).new(slice.to_unsafe, slice.size + 1).to_a do |codepoint|\n            NumberLiteral.new(codepoint).as(ASTNode)\n          end\n          literal_node = Call.new(Generic.new(Path.global(\"Slice\"), [Path.global(\"UInt16\")] of ASTNode), \"literal\", args)\n\n          # but keep the trailing zero hidden in the exposed slice\n          Call.new(literal_node, \"[]\", [NumberLiteral.new(\"0\", :i32), NumberLiteral.new(slice.size)] of ASTNode)\n        end\n      when \"tr\"\n        interpret_check_args do |first, second|\n          raise \"first argument to StringLiteral#tr must be a string, not #{first.class_desc}\" unless first.is_a?(StringLiteral)\n          raise \"second argument to StringLiteral#tr must be a string, not #{second.class_desc}\" unless second.is_a?(StringLiteral)\n\n          StringLiteral.new(@value.tr(first.value, second.value))\n        end\n      when \"underscore\"\n        interpret_check_args { StringLiteral.new(@value.underscore) }\n      when \"upcase\"\n        interpret_check_args { StringLiteral.new(@value.upcase) }\n      else\n        super\n      end\n    end\n\n    def interpret_compare(other : StringLiteral | MacroId)\n      value <=> other.value\n    end\n\n    def to_macro_id\n      @value\n    end\n\n    def regex_value(arg)\n      regex_value = arg.value\n      if regex_value.is_a?(StringLiteral)\n        Regex.new(regex_value.value, arg.options)\n      else\n        raise \"regex interpolations not yet allowed in macros\"\n      end\n    end\n  end\n\n  class StringInterpolation\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"expressions\"\n        interpret_check_args { ArrayLiteral.map(expressions, &.itself) }\n      else\n        super\n      end\n    end\n  end\n\n  class ArrayLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"of\"\n        interpret_check_args { @of || Nop.new }\n      when \"type\"\n        interpret_check_args { @name || Nop.new }\n      when \"clear\"\n        interpret_check_args do\n          elements.clear\n          self\n        end\n      else\n        value = interpret_array_or_tuple_method(self, ArrayLiteral, method, args, named_args, block, interpreter)\n        value || super\n      end\n    end\n  end\n\n  class HashLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"empty?\"\n        interpret_check_args { BoolLiteral.new(entries.empty?) }\n      when \"keys\"\n        interpret_check_args { ArrayLiteral.map entries, &.key }\n      when \"size\"\n        interpret_check_args { NumberLiteral.new(entries.size) }\n      when \"to_a\"\n        interpret_check_args do\n          ArrayLiteral.map(entries) { |entry| TupleLiteral.new([entry.key, entry.value] of ASTNode) }\n        end\n      when \"values\"\n        interpret_check_args { ArrayLiteral.map entries, &.value }\n      when \"each\"\n        interpret_check_args(uses_block: true) do\n          block_arg_key = block.args[0]?\n          block_arg_value = block.args[1]?\n\n          if entries.empty?\n            interpreter.not_interpreted_hook block.body, use_significant_node: true\n          end\n\n          entries.each do |entry|\n            interpreter.define_var(block_arg_key.name, entry.key) if block_arg_key\n            interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n            interpreter.accept block.body\n          end\n\n          NilLiteral.new\n        end\n      when \"map\"\n        interpret_check_args(uses_block: true) do\n          block_arg_key = block.args[0]?\n          block_arg_value = block.args[1]?\n\n          if entries.empty?\n            interpreter.not_interpreted_hook block.body, use_significant_node: true\n          end\n\n          ArrayLiteral.map(entries) do |entry|\n            interpreter.define_var(block_arg_key.name, entry.key) if block_arg_key\n            interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n            interpreter.accept block.body\n          end\n        end\n      when \"double_splat\"\n        interpret_check_args(min_count: 0) do |arg|\n          if arg\n            unless arg.is_a?(Crystal::StringLiteral)\n              arg.raise \"argument to double_splat must be a StringLiteral, not #{arg.class_desc}\"\n            end\n\n            if entries.empty?\n              to_double_splat\n            else\n              to_double_splat(arg.value)\n            end\n          else\n            to_double_splat\n          end\n        end\n      when \"[]\"\n        interpret_check_args do |key|\n          entry = entries.find &.key.==(key)\n          entry.try(&.value) || NilLiteral.new\n        end\n      when \"[]=\"\n        interpret_check_args do |key, value|\n          index = entries.index &.key.==(key)\n          if index\n            entries[index] = HashLiteral::Entry.new(key, value)\n          else\n            entries << HashLiteral::Entry.new(key, value)\n          end\n\n          value\n        end\n      when \"of_key\"\n        interpret_check_args { @of.try(&.key) || Nop.new }\n      when \"of_value\"\n        interpret_check_args { @of.try(&.value) || Nop.new }\n      when \"has_key?\"\n        interpret_check_args do |key|\n          BoolLiteral.new(entries.any? &.key.==(key))\n        end\n      when \"type\"\n        interpret_check_args { @name || Nop.new }\n      when \"clear\"\n        interpret_check_args do\n          entries.clear\n          self\n        end\n      when \"select\"\n        if block\n          interpret_check_args(uses_block: true) do\n            block_arg_key = block.args[0]?\n            block_arg_value = block.args[1]?\n\n            if entries.empty?\n              interpreter.not_interpreted_hook block.body, use_significant_node: true\n            end\n\n            filtered = entries.select do |entry|\n              interpreter.define_var(block_arg_key.name, entry.key) if block_arg_key\n              interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n              interpreter.accept(block.body).truthy?\n            end\n            HashLiteral.new(filtered)\n          end\n        else\n          if args.empty?\n            wrong_number_of_arguments \"macro 'HashLiteral#select'\", args.size, \"1+\"\n          end\n          filtered = entries.select { |entry| args.any? &.==(entry.key) }\n          HashLiteral.new(filtered)\n        end\n      when \"reject\"\n        if block\n          interpret_check_args(uses_block: true) do\n            block_arg_key = block.args[0]?\n            block_arg_value = block.args[1]?\n\n            if entries.empty?\n              interpreter.not_interpreted_hook block.body, use_significant_node: true\n            end\n\n            filtered = entries.reject do |entry|\n              interpreter.define_var(block_arg_key.name, entry.key) if block_arg_key\n              interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n              interpreter.accept(block.body).truthy?\n            end\n            HashLiteral.new(filtered)\n          end\n        else\n          if args.empty?\n            wrong_number_of_arguments \"macro 'HashLiteral#reject'\", args.size, \"1+\"\n          end\n          filtered = entries.reject { |entry| args.any? &.==(entry.key) }\n          HashLiteral.new(filtered)\n        end\n      else\n        super\n      end\n    end\n\n    private def to_double_splat(trailing_string = \"\")\n      MacroId.new(entries.join(\", \") do |entry|\n        \"#{entry.key} => #{entry.value}\"\n      end + trailing_string)\n    end\n  end\n\n  class NamedTupleLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"empty?\"\n        interpret_check_args { BoolLiteral.new(entries.empty?) }\n      when \"keys\"\n        interpret_check_args { ArrayLiteral.map(entries) { |entry| MacroId.new(entry.key) } }\n      when \"size\"\n        interpret_check_args { NumberLiteral.new(entries.size) }\n      when \"to_a\"\n        interpret_check_args do\n          ArrayLiteral.map(entries) { |entry| TupleLiteral.new([MacroId.new(entry.key), entry.value] of ASTNode) }\n        end\n      when \"values\"\n        interpret_check_args { ArrayLiteral.map entries, &.value }\n      when \"each\"\n        interpret_check_args(uses_block: true) do\n          block_arg_key = block.args[0]?\n          block_arg_value = block.args[1]?\n\n          if entries.empty?\n            interpreter.not_interpreted_hook block.body, use_significant_node: true\n          end\n\n          entries.each do |entry|\n            interpreter.define_var(block_arg_key.name, MacroId.new(entry.key)) if block_arg_key\n            interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n            interpreter.accept block.body\n          end\n\n          NilLiteral.new\n        end\n      when \"map\"\n        interpret_check_args(uses_block: true) do\n          block_arg_key = block.args[0]?\n          block_arg_value = block.args[1]?\n\n          if entries.empty?\n            interpreter.not_interpreted_hook block.body, use_significant_node: true\n          end\n\n          ArrayLiteral.map(entries) do |entry|\n            interpreter.define_var(block_arg_key.name, MacroId.new(entry.key)) if block_arg_key\n            interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n            interpreter.accept block.body\n          end\n        end\n      when \"double_splat\"\n        interpret_check_args(min_count: 0) do |arg|\n          if arg\n            unless arg.is_a?(Crystal::StringLiteral)\n              arg.raise \"argument to double_splat must be a StringLiteral, not #{arg.class_desc}\"\n            end\n\n            if entries.empty?\n              to_double_splat\n            else\n              to_double_splat(arg.value)\n            end\n          else\n            to_double_splat\n          end\n        end\n      when \"[]\"\n        interpret_check_args do |key|\n          case key\n          when SymbolLiteral, MacroId, StringLiteral\n            key = key.value\n          else\n            raise \"argument to [] must be a symbol or string, not #{key.class_desc}:\\n\\n#{key}\"\n          end\n\n          entry = entries.find &.key.==(key)\n          entry.try(&.value) || NilLiteral.new\n        end\n      when \"[]=\"\n        interpret_check_args do |key, value|\n          case key\n          when SymbolLiteral, MacroId, StringLiteral\n            key = key.value\n          else\n            raise \"expected 'NamedTupleLiteral#[]=' first argument to be a SymbolLiteral or MacroId, not #{key.class_desc}\"\n          end\n\n          index = entries.index &.key.==(key)\n          if index\n            entries[index] = NamedTupleLiteral::Entry.new(key, value)\n          else\n            entries << NamedTupleLiteral::Entry.new(key, value)\n          end\n\n          value\n        end\n      when \"has_key?\"\n        interpret_check_args do |key|\n          case key\n          when SymbolLiteral, MacroId, StringLiteral\n            key = key.value\n          else\n            raise \"expected 'NamedTupleLiteral#has_key?' first argument to be a SymbolLiteral, StringLiteral or MacroId, not #{key.class_desc}\"\n          end\n\n          BoolLiteral.new(entries.any? &.key.==(key))\n        end\n      when \"select\"\n        if block\n          interpret_check_args(uses_block: true) do\n            block_arg_key = block.args[0]?\n            block_arg_value = block.args[1]?\n\n            if entries.empty?\n              interpreter.not_interpreted_hook block.body, use_significant_node: true\n            end\n\n            filtered = entries.select do |entry|\n              interpreter.define_var(block_arg_key.name, MacroId.new(entry.key)) if block_arg_key\n              interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n              interpreter.accept(block.body).truthy?\n            end\n            NamedTupleLiteral.new(filtered)\n          end\n        else\n          if args.empty?\n            wrong_number_of_arguments \"macro 'NamedTupleLiteral#select'\", args.size, \"1+\"\n          end\n          key_strings = args.map do |arg|\n            case arg\n            when SymbolLiteral, MacroId, StringLiteral\n              arg.value\n            else\n              raise \"argument to 'NamedTupleLiteral#select' must be a symbol, string, or macro id, not #{arg.class_desc}\"\n            end\n          end\n          filtered = entries.select { |entry| key_strings.includes?(entry.key) }\n          NamedTupleLiteral.new(filtered)\n        end\n      when \"reject\"\n        if block\n          interpret_check_args(uses_block: true) do\n            block_arg_key = block.args[0]?\n            block_arg_value = block.args[1]?\n\n            if entries.empty?\n              interpreter.not_interpreted_hook block.body, use_significant_node: true\n            end\n\n            filtered = entries.reject do |entry|\n              interpreter.define_var(block_arg_key.name, MacroId.new(entry.key)) if block_arg_key\n              interpreter.define_var(block_arg_value.name, entry.value) if block_arg_value\n              interpreter.accept(block.body).truthy?\n            end\n            NamedTupleLiteral.new(filtered)\n          end\n        else\n          if args.empty?\n            wrong_number_of_arguments \"macro 'NamedTupleLiteral#reject'\", args.size, \"1+\"\n          end\n          key_strings = args.map do |arg|\n            case arg\n            when SymbolLiteral, MacroId, StringLiteral\n              arg.value\n            else\n              raise \"argument to 'NamedTupleLiteral#reject' must be a symbol, string, or macro id, not #{arg.class_desc}\"\n            end\n          end\n          filtered = entries.reject { |entry| key_strings.includes?(entry.key) }\n          NamedTupleLiteral.new(filtered)\n        end\n      else\n        super\n      end\n    end\n\n    private def to_double_splat(trailing_string = \"\")\n      MacroId.new(entries.join(\", \") do |entry|\n        \"#{Symbol.quote_for_named_argument(entry.key)}: #{entry.value}\"\n      end + trailing_string)\n    end\n  end\n\n  class TupleLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      value = interpret_array_or_tuple_method(self, TupleLiteral, method, args, named_args, block, interpreter)\n      value || super\n    end\n  end\n\n  class RangeLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"begin\"\n        interpret_check_args { self.from }\n      when \"end\"\n        interpret_check_args { self.to }\n      when \"excludes_end?\"\n        interpret_check_args { BoolLiteral.new(self.exclusive?) }\n      when \"each\"\n        interpret_check_args(uses_block: true) do\n          block_arg = block.args.first?\n\n          range = interpret_to_range(interpreter)\n\n          if range.empty?\n            interpreter.not_interpreted_hook block.body, use_significant_node: true\n          end\n\n          range.each do |num|\n            interpreter.define_var(block_arg.name, NumberLiteral.new(num)) if block_arg\n            interpreter.accept block.body\n          end\n\n          NilLiteral.new\n        end\n      when \"map\"\n        interpret_check_args(uses_block: true) do\n          block_arg = block.args.first?\n\n          interpret_map(block, interpreter) do |num|\n            interpreter.define_var(block_arg.name, NumberLiteral.new(num)) if block_arg\n            interpreter.accept block.body\n          end\n        end\n      when \"to_a\"\n        interpret_check_args do\n          interpret_map(nil, interpreter) do |num|\n            NumberLiteral.new(num)\n          end\n        end\n      else\n        super\n      end\n    end\n\n    def interpret_map(block, interpreter, &)\n      range = interpret_to_range(interpreter)\n\n      if block && range.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      ArrayLiteral.map(range) do |num|\n        yield num\n      end\n    end\n\n    def interpret_to_range(interpreter)\n      node = interpreter.accept(self.from)\n      from = case node\n             when NumberLiteral\n               node.to_number.to_i\n             else\n               raise \"range begin must be a NumberLiteral, not #{node.class_desc}\"\n             end\n\n      node = interpreter.accept(self.to)\n      to = case node\n           when NumberLiteral\n             node.to_number.to_i\n           else\n             raise \"range end must be a NumberLiteral, not #{node.class_desc}\"\n           end\n\n      Range.new(from, to, self.exclusive?)\n    end\n\n    def interpret_to_nilable_range(interpreter)\n      node = interpreter.accept(self.from)\n      from = case node\n             when NumberLiteral\n               node.to_number.to_i\n             when NilLiteral, Nop\n               nil\n             else\n               raise \"range begin must be a NumberLiteral | NilLiteral | Nop, not #{node.class_desc}\"\n             end\n\n      node = interpreter.accept(self.to)\n      to = case node\n           when NumberLiteral\n             node.to_number.to_i\n           when NilLiteral, Nop\n             nil\n           else\n             raise \"range end must be a NumberLiteral | NilLiteral | Nop, not #{node.class_desc}\"\n           end\n\n      Range.new(from, to, self.exclusive?)\n    end\n  end\n\n  class RegexLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"source\"\n        interpret_check_args { @value }\n      when \"options\"\n        interpret_check_args do\n          options = [] of Symbol\n          options << :i if @options.ignore_case?\n          options << :m if @options.multiline?\n          options << :x if @options.extended?\n          ArrayLiteral.map(options, Path.global(\"Symbol\")) { |opt| SymbolLiteral.new(opt.to_s) }\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class MetaMacroVar < ASTNode\n    def to_macro_id\n      @name\n    end\n\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"type\"\n        interpret_check_args do\n          if type = @type\n            TypeNode.new(type)\n          else\n            NilLiteral.new\n          end\n        end\n      when \"default_value\"\n        interpret_check_args do\n          default_value || NilLiteral.new\n        end\n      when \"has_default_value?\"\n        interpret_check_args do\n          BoolLiteral.new(!!default_value)\n        end\n      when \"annotation\"\n        fetch_annotation(self, method, args, named_args, block) do |type|\n          self.var.annotation(type)\n        end\n      when \"annotations\"\n        fetch_annotations(self, method, args, named_args, block) do |type|\n          annotations = type ? self.var.annotations(type) : self.var.all_annotations\n          return ArrayLiteral.new if annotations.nil?\n          ArrayLiteral.map(annotations, &.itself)\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class Block\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"body\"\n        interpret_check_args { @body }\n      when \"args\"\n        interpret_check_args do\n          ArrayLiteral.map(@args) { |arg| MacroId.new(arg.name) }\n        end\n      when \"splat_index\"\n        interpret_check_args do\n          @splat_index ? NumberLiteral.new(@splat_index.not_nil!) : NilLiteral.new\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class ProcNotation\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"inputs\"\n        interpret_check_args do\n          if inputs = @inputs\n            ArrayLiteral.map(inputs, &.itself)\n          else\n            ArrayLiteral.new\n          end\n        end\n      when \"output\"\n        interpret_check_args { @output || NilLiteral.new }\n      when \"resolve\"\n        interpret_check_args { interpreter.resolve(self) }\n      when \"resolve?\"\n        interpret_check_args { interpreter.resolve?(self) || NilLiteral.new }\n      else\n        super\n      end\n    end\n  end\n\n  class ProcLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"args\", \"body\", \"return_type\"\n        @def.interpret(method, args, named_args, block, interpreter, location)\n      else\n        super\n      end\n    end\n  end\n\n  class ProcPointer\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"obj\"\n        interpret_check_args { @obj || NilLiteral.new }\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"args\"\n        interpret_check_args { ArrayLiteral.map(@args, &.itself) }\n      when \"global?\"\n        interpret_check_args { BoolLiteral.new(@global) }\n      else\n        super\n      end\n    end\n  end\n\n  class Expressions\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"expressions\"\n        interpret_check_args do\n          ArrayLiteral.map(@expressions) { |expression| expression }\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class BinaryOp\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"left\"\n        interpret_check_args { @left }\n      when \"right\"\n        interpret_check_args { @right }\n      else\n        super\n      end\n    end\n  end\n\n  class TypeDeclaration\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"var\"\n        interpret_check_args do\n          var = @var\n          var = MacroId.new(var.name) if var.is_a?(Var)\n          var\n        end\n      when \"type\"\n        interpret_check_args { @declared_type }\n      when \"value\"\n        interpret_check_args { @value || Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class UninitializedVar\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"var\"\n        interpret_check_args do\n          var = @var\n          var = MacroId.new(var.name) if var.is_a?(Var)\n          var\n        end\n      when \"type\"\n        interpret_check_args { @declared_type }\n      else\n        super\n      end\n    end\n  end\n\n  class Union\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"resolve\"\n        interpret_check_args { interpreter.resolve(self) }\n      when \"resolve?\"\n        interpret_check_args { interpreter.resolve?(self) || NilLiteral.new }\n      when \"types\"\n        interpret_check_args { ArrayLiteral.map(@types, &.itself) }\n      else\n        super\n      end\n    end\n  end\n\n  class Arg\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(external_name) }\n      when \"internal_name\"\n        interpret_check_args { MacroId.new(name) }\n      when \"default_value\"\n        interpret_check_args { default_value || Nop.new }\n      when \"restriction\"\n        interpret_check_args { restriction || Nop.new }\n      when \"annotation\"\n        fetch_annotation(self, method, args, named_args, block) do |type|\n          self.annotation(type)\n        end\n      when \"annotations\"\n        fetch_annotations(self, method, args, named_args, block) do |type|\n          annotations = type ? self.annotations(type) : self.all_annotations\n          return ArrayLiteral.new if annotations.nil?\n          ArrayLiteral.map(annotations, &.itself)\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class Def\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"args\"\n        interpret_check_args { ArrayLiteral.map @args, &.itself }\n      when \"splat_index\"\n        interpret_check_args do\n          @splat_index ? NumberLiteral.new(@splat_index.not_nil!) : NilLiteral.new\n        end\n      when \"double_splat\"\n        interpret_check_args { @double_splat || Nop.new }\n      when \"block_arg\"\n        interpret_check_args { @block_arg || Nop.new }\n      when \"accepts_block?\"\n        interpret_check_args { BoolLiteral.new(@block_arity != nil) }\n      when \"return_type\"\n        interpret_check_args { @return_type || Nop.new }\n      when \"free_vars\"\n        interpret_check_args do\n          if (free_vars = @free_vars) && !free_vars.empty?\n            ArrayLiteral.map(free_vars) { |free_var| MacroId.new(free_var) }\n          else\n            empty_no_return_array\n          end\n        end\n      when \"body\"\n        interpret_check_args { @body }\n      when \"receiver\"\n        interpret_check_args { @receiver || Nop.new }\n      when \"visibility\"\n        interpret_check_args do\n          visibility_to_symbol(@visibility)\n        end\n      when \"abstract?\"\n        interpret_check_args { BoolLiteral.new(@abstract) }\n      when \"annotation\"\n        fetch_annotation(self, method, args, named_args, block) do |type|\n          self.annotation(type)\n        end\n      when \"annotations\"\n        fetch_annotations(self, method, args, named_args, block) do |type|\n          annotations = type ? self.annotations(type) : self.all_annotations\n          return ArrayLiteral.new if annotations.nil?\n          ArrayLiteral.map(annotations, &.itself)\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class Primitive\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { SymbolLiteral.new(@name) }\n      else\n        super\n      end\n    end\n  end\n\n  class Macro\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"args\"\n        interpret_check_args { ArrayLiteral.map @args, &.itself }\n      when \"splat_index\"\n        interpret_check_args do\n          @splat_index ? NumberLiteral.new(@splat_index.not_nil!) : NilLiteral.new\n        end\n      when \"double_splat\"\n        interpret_check_args { @double_splat || Nop.new }\n      when \"block_arg\"\n        interpret_check_args { @block_arg || Nop.new }\n      when \"body\"\n        interpret_check_args { @body }\n      when \"visibility\"\n        interpret_check_args do\n          visibility_to_symbol(@visibility)\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class MacroExpression\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"exp\"\n        interpret_check_args { @exp }\n      when \"output?\"\n        interpret_check_args { BoolLiteral.new(@output) }\n      else\n        super\n      end\n    end\n  end\n\n  class MacroIf\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"cond\"\n        interpret_check_args { @cond }\n      when \"then\"\n        interpret_check_args { @then }\n      when \"else\"\n        interpret_check_args { @else }\n      when \"is_unless?\"\n        interpret_check_args { BoolLiteral.new @is_unless }\n      else\n        super\n      end\n    end\n  end\n\n  class MacroFor\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"vars\"\n        interpret_check_args { ArrayLiteral.map(@vars, &.itself) }\n      when \"exp\"\n        interpret_check_args { @exp }\n      when \"body\"\n        interpret_check_args { @body }\n      else\n        super\n      end\n    end\n  end\n\n  class MacroLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"value\"\n        interpret_check_args { MacroId.new(@value) }\n      else\n        super\n      end\n    end\n  end\n\n  class MacroVar\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"expressions\"\n        interpret_check_args do\n          if exps = @exps\n            ArrayLiteral.map(exps, &.itself)\n          else\n            empty_no_return_array\n          end\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class UnaryExpression\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"exp\"\n        interpret_check_args { @exp }\n      else\n        super\n      end\n    end\n  end\n\n  class Include\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { @name }\n      else\n        super\n      end\n    end\n  end\n\n  class Extend\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { @name }\n      else\n        super\n      end\n    end\n  end\n\n  class Alias\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { @name }\n      when \"type\"\n        interpret_check_args { @value }\n      else\n        super\n      end\n    end\n  end\n\n  class OffsetOf\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"type\"\n        interpret_check_args { @offsetof_type }\n      when \"offset\"\n        interpret_check_args { @offset }\n      else\n        super\n      end\n    end\n  end\n\n  class Metaclass\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"instance\"\n        interpret_check_args { @name }\n      when \"resolve\"\n        interpret_check_args { interpreter.resolve(self) }\n      when \"resolve?\"\n        interpret_check_args { interpreter.resolve?(self) || NilLiteral.new }\n      else\n        super\n      end\n    end\n  end\n\n  class VisibilityModifier\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"exp\"\n        interpret_check_args { @exp }\n      when \"visibility\"\n        interpret_check_args do\n          visibility_to_symbol(@modifier)\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class IsA\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"receiver\"\n        interpret_check_args { @obj }\n      when \"arg\"\n        interpret_check_args { @const }\n      else\n        super\n      end\n    end\n  end\n\n  class RespondsTo\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"receiver\"\n        interpret_check_args { @obj }\n      when \"name\"\n        interpret_check_args { StringLiteral.new(@name) }\n      else\n        super\n      end\n    end\n  end\n\n  class Require\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"path\"\n        interpret_check_args { StringLiteral.new(@string) }\n      else\n        super\n      end\n    end\n  end\n\n  class Asm\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"text\"\n        interpret_check_args { StringLiteral.new(@text) }\n      when \"outputs\"\n        interpret_check_args do\n          if outputs = @outputs\n            ArrayLiteral.map(outputs, &.itself)\n          else\n            empty_no_return_array\n          end\n        end\n      when \"inputs\"\n        interpret_check_args do\n          if inputs = @inputs\n            ArrayLiteral.map(inputs, &.itself)\n          else\n            empty_no_return_array\n          end\n        end\n      when \"clobbers\"\n        interpret_check_args do\n          if clobbers = @clobbers\n            ArrayLiteral.map(clobbers) { |clobber| StringLiteral.new(clobber) }\n          else\n            empty_no_return_array\n          end\n        end\n      when \"volatile?\"\n        interpret_check_args { BoolLiteral.new(@volatile) }\n      when \"alignstack?\"\n        interpret_check_args { BoolLiteral.new(@alignstack) }\n      when \"intel?\"\n        interpret_check_args { BoolLiteral.new(@intel) }\n      when \"can_throw?\"\n        interpret_check_args { BoolLiteral.new(@can_throw) }\n      else\n        super\n      end\n    end\n  end\n\n  class AsmOperand\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"constraint\"\n        interpret_check_args { StringLiteral.new(@constraint) }\n      when \"exp\"\n        interpret_check_args { @exp }\n      else\n        super\n      end\n    end\n  end\n\n  class MacroId\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"==\", \"!=\"\n        interpret_check_args do |arg|\n          case arg\n          when StringLiteral, SymbolLiteral\n            if method == \"==\"\n              BoolLiteral.new(@value == arg.value)\n            else\n              BoolLiteral.new(@value != arg.value)\n            end\n          else\n            super\n          end\n        end\n      when \"stringify\", \"class_name\", \"symbolize\"\n        super\n      else\n        value = StringLiteral.new(@value).interpret(method, args, named_args, block, interpreter, location)\n        value = MacroId.new(value.value) if value.is_a?(StringLiteral)\n        value\n      end\n    rescue UndefinedMacroMethodError\n      raise \"undefined macro method '#{class_desc}##{method}'\", exception_type: Crystal::UndefinedMacroMethodError\n    end\n\n    def interpret_compare(other : MacroId | StringLiteral)\n      value <=> other.value\n    end\n  end\n\n  class SymbolLiteral\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"==\", \"!=\"\n        interpret_check_args do |arg|\n          case arg\n          when MacroId\n            if method == \"==\"\n              BoolLiteral.new(@value == arg.value)\n            else\n              BoolLiteral.new(@value != arg.value)\n            end\n          else\n            super\n          end\n        end\n      when \"stringify\", \"class_name\", \"symbolize\"\n        super\n      else\n        value = StringLiteral.new(@value).interpret(method, args, named_args, block, interpreter, location)\n        value = SymbolLiteral.new(value.value) if value.is_a?(StringLiteral)\n        value\n      end\n    rescue UndefinedMacroMethodError\n      raise \"undefined macro method '#{class_desc}##{method}'\", exception_type: Crystal::UndefinedMacroMethodError\n    end\n  end\n\n  class TypeNode\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"abstract?\"\n        interpret_check_args { BoolLiteral.new(type.abstract?) }\n      when \"union?\"\n        interpret_check_args { BoolLiteral.new(type.is_a?(UnionType)) }\n      when \"module?\"\n        interpret_check_args { BoolLiteral.new(type.module?) }\n      when \"class?\"\n        interpret_check_args { BoolLiteral.new(type.class? && !type.struct?) }\n      when \"struct?\"\n        interpret_check_args { BoolLiteral.new(type.class? && type.struct?) }\n      when \"nilable?\"\n        interpret_check_args { BoolLiteral.new(type.nilable?) }\n      when \"union_types\"\n        interpret_check_args { TypeNode.union_types(self) }\n      when \"name\"\n        interpret_check_args(named_params: [\"generic_args\"]) do\n          generic_args = parse_generic_args_argument(self, method, named_args, default: true)\n          MacroId.new(type.devirtualize.to_s(generic_args: generic_args))\n        end\n      when \"type_vars\"\n        interpret_check_args { TypeNode.type_vars(type) }\n      when \"instance_vars\"\n        interpret_check_args { TypeNode.instance_vars(type, name_loc) }\n      when \"class_vars\"\n        interpret_check_args { TypeNode.class_vars(type) }\n      when \"ancestors\"\n        interpret_check_args { TypeNode.ancestors(type) }\n      when \"superclass\"\n        interpret_check_args { TypeNode.superclass(type) }\n      when \"subclasses\"\n        interpret_check_args { TypeNode.subclasses(type) }\n      when \"all_subclasses\"\n        interpret_check_args { TypeNode.all_subclasses(type) }\n      when \"includers\"\n        interpret_check_args { TypeNode.includers(type) }\n      when \"constants\"\n        interpret_check_args { TypeNode.constants(type) }\n      when \"constant\"\n        interpret_check_args do |arg|\n          value = arg.to_string(\"argument to 'TypeNode#constant'\")\n          TypeNode.constant(type, value)\n        end\n      when \"has_constant?\"\n        interpret_check_args do |arg|\n          value = arg.to_string(\"argument to 'TypeNode#has_constant?'\")\n          TypeNode.has_constant?(type, value)\n        end\n      when \"methods\"\n        interpret_check_args { TypeNode.methods(type) }\n      when \"has_method?\"\n        interpret_check_args do |arg|\n          value = arg.to_string(\"argument to 'TypeNode#has_method?'\")\n          TypeNode.has_method?(type, value)\n        end\n      when \"annotation\"\n        fetch_annotation(self, method, args, named_args, block) do |type|\n          self.type.annotation(type)\n        end\n      when \"annotations\"\n        fetch_annotations(self, method, args, named_args, block) do |type|\n          annotations = type ? self.type.annotations(type) : self.type.all_annotations\n          return ArrayLiteral.new if annotations.nil?\n          ArrayLiteral.map(annotations, &.itself)\n        end\n      when \"size\"\n        interpret_check_args do\n          type = self.type.instance_type\n          case type\n          when TupleInstanceType\n            NumberLiteral.new(type.tuple_types.size)\n          when NamedTupleInstanceType\n            NumberLiteral.new(type.entries.size)\n          else\n            raise \"undefined method 'size' for TypeNode of type #{type} (must be a tuple or named tuple type)\"\n          end\n        end\n      when \"keys\"\n        interpret_check_args do\n          type = self.type.instance_type\n          if type.is_a?(NamedTupleInstanceType)\n            ArrayLiteral.map(type.entries) { |entry| MacroId.new(entry.name) }\n          else\n            raise \"undefined method 'keys' for TypeNode of type #{type} (must be a named tuple type)\"\n          end\n        end\n      when \"[]\"\n        interpret_check_args do |arg|\n          type = self.type.instance_type\n          case type\n          when NamedTupleInstanceType\n            case arg\n            when SymbolLiteral\n              key = arg.value\n            when MacroId\n              key = arg.value\n            else\n              return NilLiteral.new\n            end\n            index = type.name_index(key)\n            unless index\n              return NilLiteral.new\n            end\n            TypeNode.new(type.entries[index].type)\n          when TupleInstanceType\n            case arg\n            when NumberLiteral\n              index = arg.to_number.to_i\n              type = type.tuple_types[index]?\n              unless type\n                return NilLiteral.new\n              end\n              TypeNode.new(type)\n            else\n              return NilLiteral.new\n            end\n          else\n            raise \"undefined method '[]' for TypeNode of type #{type} (must be a tuple or named tuple type)\"\n          end\n        end\n      when \"class\"\n        interpret_check_args { TypeNode.new(type.metaclass) }\n      when \"instance\"\n        interpret_check_args { TypeNode.new(type.instance_type) }\n      when \"==\", \"!=\"\n        interpret_check_args do |arg|\n          return super unless arg.is_a?(TypeNode)\n\n          self_type = self.type.devirtualize\n          other_type = arg.type.devirtualize\n\n          case method\n          when \"==\"\n            BoolLiteral.new(self_type == other_type)\n          else # \"!=\"\n            BoolLiteral.new(self_type != other_type)\n          end\n        end\n      when \"<\", \"<=\", \">\", \">=\"\n        interpret_check_args do |arg|\n          unless arg.is_a?(TypeNode)\n            raise \"TypeNode##{method} expects TypeNode, not #{arg.class_desc}\"\n          end\n\n          self_type = self.type.devirtualize\n          other_type = arg.type.devirtualize\n\n          case method\n          when \"<\"\n            value = self_type != other_type && self_type.implements?(other_type)\n          when \"<=\"\n            value = self_type.implements?(other_type)\n          when \">\"\n            value = self_type != other_type && other_type.implements?(self_type)\n          else # \">=\"\n            value = other_type.implements?(self_type)\n          end\n          BoolLiteral.new(!!value)\n        end\n      when \"overrides?\"\n        interpret_check_args do |arg1, arg2|\n          unless arg1.is_a?(TypeNode)\n            raise \"TypeNode##{method} expects TypeNode as a first argument, not #{arg1.class_desc}\"\n          end\n\n          value = arg2.to_string(\"second argument to 'TypeNode#overrides?\")\n          TypeNode.overrides?(type, arg1.type, value)\n        end\n      when \"resolve\"\n        interpret_check_args { self }\n      when \"resolve?\"\n        interpret_check_args { self }\n      when \"private?\"\n        interpret_check_args { BoolLiteral.new(type.private?) }\n      when \"public?\"\n        interpret_check_args { BoolLiteral.new(!type.private?) }\n      when \"visibility\"\n        interpret_check_args do\n          if type.private?\n            SymbolLiteral.new(\"private\")\n          else\n            SymbolLiteral.new(\"public\")\n          end\n        end\n      when \"has_inner_pointers?\"\n        interpret_check_args { TypeNode.has_inner_pointers?(type, name_loc) }\n      else\n        super\n      end\n    end\n\n    def self.includers(type)\n      case type\n      when NonGenericModuleType, GenericModuleType, GenericModuleInstanceType\n        types = type.raw_including_types\n        return empty_no_return_array unless types\n        ArrayLiteral.map(types) do |including_type|\n          TypeNode.new including_type\n        end\n      else\n        empty_no_return_array\n      end\n    end\n\n    def self.type_vars(type)\n      if type.is_a?(GenericClassInstanceType) || type.is_a?(GenericModuleInstanceType)\n        if type.is_a?(TupleInstanceType)\n          if type.tuple_types.empty?\n            empty_no_return_array\n          else\n            ArrayLiteral.map(type.tuple_types) do |tuple_type|\n              TypeNode.new(tuple_type)\n            end\n          end\n        else\n          if type.type_vars.empty?\n            empty_no_return_array\n          else\n            ArrayLiteral.map(type.type_vars.values) do |type_var|\n              if type_var.is_a?(Var)\n                TypeNode.new(type_var.type)\n              else\n                type_var\n              end\n            end\n          end\n        end\n      elsif type.is_a?(GenericType)\n        t = type.as(GenericType)\n        if t.type_vars.empty?\n          empty_no_return_array\n        else\n          ArrayLiteral.map(t.type_vars) do |type_var|\n            MacroId.new(type_var)\n          end\n        end\n      else\n        empty_no_return_array\n      end\n    end\n\n    def self.instance_vars(type, name_loc)\n      if type.is_a?(InstanceVarContainer)\n        unless type.program.top_level_semantic_complete?\n          message = \"`TypeNode#instance_vars` cannot be called in the top-level scope: instance vars are not yet initialized\"\n          if name_loc\n            raise Crystal::TypeException.new(message, name_loc)\n          else\n            raise Crystal::TypeException.new(message)\n          end\n        end\n        ArrayLiteral.map(type.all_instance_vars) do |name, ivar|\n          meta_var = MetaMacroVar.new(name[1..-1], ivar.type)\n          meta_var.var = ivar\n          meta_var.default_value = type.get_instance_var_initializer(name).try(&.value)\n          meta_var\n        end\n      else\n        empty_no_return_array\n      end\n    end\n\n    def self.has_inner_pointers?(type, name_loc)\n      unless type.program.top_level_semantic_complete?\n        message = \"`TypeNode#has_inner_pointers?` cannot be called in the top-level scope: instance vars are not yet initialized\"\n        if name_loc\n          raise Crystal::TypeException.new(message, name_loc)\n        else\n          raise Crystal::TypeException.new(message)\n        end\n      end\n\n      BoolLiteral.new(type.has_inner_pointers?)\n    end\n\n    def self.class_vars(type)\n      if type.is_a?(ClassVarContainer)\n        ArrayLiteral.map(type.all_class_vars) do |name, ivar|\n          meta_var = MetaMacroVar.new(name[2..-1], ivar.type)\n          meta_var.var = ivar\n          meta_var.default_value = ivar.initializer.try(&.node)\n          meta_var\n        end\n      else\n        empty_no_return_array\n      end\n    end\n\n    def self.ancestors(type)\n      ancestors = type.ancestors\n      if ancestors.empty?\n        empty_no_return_array\n      else\n        ArrayLiteral.map(type.ancestors) { |ancestor| TypeNode.new(ancestor) }\n      end\n    end\n\n    def self.superclass(type)\n      superclass = type.superclass\n      superclass ? TypeNode.new(superclass) : NilLiteral.new\n    rescue\n      NilLiteral.new\n    end\n\n    def self.subclasses(type)\n      subclasses = type.devirtualize.subclasses\n      if subclasses.empty?\n        empty_no_return_array\n      else\n        ArrayLiteral.map(subclasses) { |subtype| TypeNode.new(subtype) }\n      end\n    end\n\n    def self.all_subclasses(type)\n      subclasses = type.devirtualize.all_subclasses\n      if subclasses.empty?\n        empty_no_return_array\n      else\n        ArrayLiteral.map(subclasses) { |subtype| TypeNode.new(subtype) }\n      end\n    end\n\n    def self.union_types(type_node)\n      type = type_node.type\n\n      if type.is_a?(UnionType)\n        ArrayLiteral.map(type.union_types) { |uniontype| TypeNode.new(uniontype) }\n      else\n        ArrayLiteral.new([type_node] of ASTNode)\n      end\n    end\n\n    def self.constants(type)\n      if type.types.empty?\n        empty_no_return_array\n      else\n        names = type.types.map { |name, member_type| MacroId.new(name).as(ASTNode) }\n        ArrayLiteral.new names\n      end\n    end\n\n    def self.has_constant?(type, name)\n      BoolLiteral.new(type.types.has_key?(name))\n    end\n\n    def self.constant(type, name)\n      type = type.types[name]?\n      case type\n      when Const\n        type.value\n      when Type\n        TypeNode.new(type)\n      else\n        NilLiteral.new\n      end\n    end\n\n    def self.methods(type)\n      defs = [] of ASTNode\n      type.defs.try &.each do |name, metadatas|\n        metadatas.each do |metadata|\n          defs << metadata.def\n        end\n      end\n      ArrayLiteral.new(defs)\n    end\n\n    def self.has_method?(type, name)\n      BoolLiteral.new(!!type.has_def?(name))\n    end\n\n    def self.overrides?(type, target, method)\n      overrides = type.lookup_defs(method).any? do |a_def|\n        a_def.owner != target && a_def.macro_owner != target && !target.implements?(a_def.owner)\n      end\n      BoolLiteral.new(!!overrides)\n    end\n  end\n\n  class SymbolLiteral\n    def to_macro_id\n      @value\n    end\n  end\n\n  class Var\n    def to_macro_id\n      @name\n    end\n  end\n\n  class Call\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(name) }\n      when \"receiver\"\n        interpret_check_args { obj || Nop.new }\n      when \"args\"\n        interpret_check_args { ArrayLiteral.map self.args, &.itself }\n      when \"named_args\"\n        interpret_check_args do\n          if named_args = self.named_args\n            ArrayLiteral.map(named_args) { |arg| arg }\n          else\n            Nop.new\n          end\n        end\n      when \"block\"\n        interpret_check_args { self.block || Nop.new }\n      when \"block_arg\"\n        interpret_check_args { self.block_arg || Nop.new }\n      when \"global?\"\n        interpret_check_args { BoolLiteral.new(@global) }\n      else\n        super\n      end\n    end\n\n    def to_macro_id\n      if !obj && !block && args.empty?\n        @name\n      else\n        to_s\n      end\n    end\n  end\n\n  class NamedArgument\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(name) }\n      when \"value\"\n        interpret_check_args { value }\n      else\n        super\n      end\n    end\n  end\n\n  class If\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"cond\"\n        interpret_check_args { @cond }\n      when \"then\"\n        interpret_check_args { @then }\n      when \"else\"\n        interpret_check_args { @else }\n      else\n        super\n      end\n    end\n  end\n\n  class Case\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"cond\"\n        interpret_check_args { cond || Nop.new }\n      when \"whens\"\n        interpret_check_args { ArrayLiteral.map whens, &.itself }\n      when \"else\"\n        interpret_check_args { self.else || Nop.new }\n      when \"exhaustive?\"\n        interpret_check_args { BoolLiteral.new(@exhaustive) }\n      else\n        super\n      end\n    end\n  end\n\n  class Select\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"whens\"\n        interpret_check_args { ArrayLiteral.map whens, &.itself }\n      when \"else\"\n        interpret_check_args { self.else || Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class When\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"conds\"\n        interpret_check_args { ArrayLiteral.map(conds, &.itself) }\n      when \"body\"\n        interpret_check_args { body }\n      when \"exhaustive?\"\n        interpret_check_args { BoolLiteral.new(@exhaustive) }\n      else\n        super\n      end\n    end\n  end\n\n  class ExceptionHandler\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"body\"\n        interpret_check_args { @body }\n      when \"rescues\"\n        interpret_check_args { (rescues = @rescues) ? ArrayLiteral.map(rescues, &.itself) : NilLiteral.new }\n      when \"else\"\n        interpret_check_args { @else || Nop.new }\n      when \"ensure\"\n        interpret_check_args { @ensure || Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class Rescue\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"body\"\n        interpret_check_args { body }\n      when \"types\"\n        interpret_check_args { (types = @types) ? ArrayLiteral.map(types, &.itself) : NilLiteral.new }\n      when \"name\"\n        interpret_check_args { (name = @name) ? MacroId.new(name) : Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class ControlExpression\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"exp\"\n        interpret_check_args { exp || Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class Yield\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"expressions\"\n        interpret_check_args { ArrayLiteral.map(@exps, &.itself) }\n      when \"scope\"\n        interpret_check_args { scope || Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class Assign\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"target\"\n        interpret_check_args { target }\n      when \"value\"\n        interpret_check_args { value }\n      else\n        super\n      end\n    end\n  end\n\n  class MultiAssign\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"targets\"\n        interpret_check_args { ArrayLiteral.map(targets, &.itself) }\n      when \"values\"\n        interpret_check_args { ArrayLiteral.map(values, &.itself) }\n      else\n        super\n      end\n    end\n  end\n\n  class InstanceVar\n    def to_macro_id\n      @name\n    end\n\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      else\n        super\n      end\n    end\n  end\n\n  class ReadInstanceVar\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"obj\"\n        interpret_check_args { @obj }\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      else\n        super\n      end\n    end\n  end\n\n  class ClassVar\n    def to_macro_id\n      @name\n    end\n\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      else\n        super\n      end\n    end\n  end\n\n  class Global\n    def to_macro_id\n      @name\n    end\n\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      else\n        super\n      end\n    end\n  end\n\n  class Path\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"names\"\n        interpret_check_args do\n          ArrayLiteral.map(@names) { |name| MacroId.new(name) }\n        end\n      when \"global\"\n        interpreter.warnings.add_warning_at(name_loc, \"Deprecated Path#global. Use `#global?` instead\")\n        interpret_check_args { BoolLiteral.new(@global) }\n      when \"global?\"\n        interpret_check_args { BoolLiteral.new(@global) }\n      when \"resolve\"\n        interpret_check_args { interpreter.resolve(self) }\n      when \"resolve?\"\n        interpret_check_args { interpreter.resolve?(self) || NilLiteral.new }\n      when \"types\"\n        interpret_check_args { ArrayLiteral.new([self] of ASTNode) }\n      else\n        super\n      end\n    end\n\n    def to_macro_id\n      String.build do |io|\n        io << \"::\" if global?\n\n        @names.join(io, \"::\")\n      end\n    end\n  end\n\n  class While\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"cond\"\n        interpret_check_args { @cond }\n      when \"body\"\n        interpret_check_args { @body }\n      else\n        super\n      end\n    end\n  end\n\n  class Cast\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"obj\"\n        interpret_check_args { obj }\n      when \"to\"\n        interpret_check_args { to }\n      else\n        super\n      end\n    end\n  end\n\n  class NilableCast\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"obj\"\n        interpret_check_args { obj }\n      when \"to\"\n        interpret_check_args { to }\n      else\n        super\n      end\n    end\n  end\n\n  class TypeOf\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"args\"\n        interpret_check_args { ArrayLiteral.map(@expressions, &.itself) }\n      else\n        super\n      end\n    end\n  end\n\n  class Generic\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { name }\n      when \"type_vars\"\n        interpret_check_args { ArrayLiteral.map(type_vars, &.itself) }\n      when \"named_args\"\n        interpret_check_args do\n          if named_args = @named_args\n            NamedTupleLiteral.new(named_args.map { |arg| NamedTupleLiteral::Entry.new(arg.name, arg.value) })\n          else\n            NilLiteral.new\n          end\n        end\n      when \"resolve\"\n        interpret_check_args { interpreter.resolve(self) }\n      when \"resolve?\"\n        interpret_check_args { interpreter.resolve?(self) || NilLiteral.new }\n      when \"types\"\n        interpret_check_args { ArrayLiteral.new([self] of ASTNode) }\n      else\n        super\n      end\n    end\n  end\n\n  class Annotation\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { @path }\n      when \"[]\"\n        interpret_check_args do |arg|\n          case arg\n          when NumberLiteral\n            index = arg.to_number.to_i\n            return self.args[index]? || NilLiteral.new\n          when SymbolLiteral then name = arg.value\n          when StringLiteral then name = arg.value\n          when MacroId       then name = arg.value\n          else\n            raise \"argument to [] must be a number, symbol or string, not #{arg.class_desc}:\\n\\n#{arg}\"\n          end\n\n          named_arg = self.named_args.try &.find do |named_arg|\n            named_arg.name == name\n          end\n          named_arg.try(&.value) || NilLiteral.new\n        end\n      when \"args\"\n        interpret_check_args do\n          TupleLiteral.map self.args, &.itself\n        end\n      when \"named_args\"\n        interpret_check_args do\n          get_named_annotation_args self\n        end\n      else\n        super\n      end\n    end\n  end\n\n  class ClassDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"kind\"\n        interpret_check_args { MacroId.new(@struct ? \"struct\" : \"class\") }\n      when \"name\"\n        type_definition_generic_name(self, method, args, named_args, block)\n      when \"superclass\"\n        interpret_check_args { @superclass || Nop.new }\n      when \"type_vars\"\n        interpret_check_args do\n          if (type_vars = @type_vars) && type_vars.present?\n            ArrayLiteral.map(type_vars) { |type_var| MacroId.new(type_var) }\n          else\n            empty_no_return_array\n          end\n        end\n      when \"splat_index\"\n        interpret_check_args do\n          if splat_index = @splat_index\n            NumberLiteral.new(splat_index)\n          else\n            NilLiteral.new\n          end\n        end\n      when \"body\"\n        interpret_check_args { @body }\n      when \"abstract?\"\n        interpret_check_args { BoolLiteral.new(@abstract) }\n      when \"struct?\"\n        interpret_check_args { BoolLiteral.new(@struct) }\n      else\n        super\n      end\n    end\n  end\n\n  class ModuleDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"kind\"\n        interpret_check_args { MacroId.new(\"module\") }\n      when \"name\"\n        type_definition_generic_name(self, method, args, named_args, block)\n      when \"type_vars\"\n        interpret_check_args do\n          if (type_vars = @type_vars) && type_vars.present?\n            ArrayLiteral.map(type_vars) { |type_var| MacroId.new(type_var) }\n          else\n            empty_no_return_array\n          end\n        end\n      when \"splat_index\"\n        interpret_check_args do\n          if splat_index = @splat_index\n            NumberLiteral.new(splat_index)\n          else\n            NilLiteral.new\n          end\n        end\n      when \"body\"\n        interpret_check_args { @body }\n      else\n        super\n      end\n    end\n  end\n\n  class EnumDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"kind\"\n        interpret_check_args { MacroId.new(\"enum\") }\n      when \"name\"\n        interpret_check_args(named_params: [\"generic_args\"]) do\n          # parse the argument, but ignore it otherwise\n          parse_generic_args_argument(self, method, named_args, default: true)\n          @name\n        end\n      when \"base_type\"\n        interpret_check_args { @base_type || Nop.new }\n      when \"body\"\n        interpret_check_args { Expressions.from(@members) }\n      else\n        super\n      end\n    end\n  end\n\n  class AnnotationDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"kind\"\n        interpret_check_args { MacroId.new(\"annotation\") }\n      when \"name\"\n        interpret_check_args(named_params: [\"generic_args\"]) do\n          # parse the argument, but ignore it otherwise\n          parse_generic_args_argument(self, method, named_args, default: true)\n          @name\n        end\n      when \"body\"\n        interpret_check_args { Nop.new }\n      else\n        super\n      end\n    end\n  end\n\n  class LibDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"kind\"\n        interpret_check_args { MacroId.new(\"lib\") }\n      when \"name\"\n        interpret_check_args(named_params: [\"generic_args\"]) do\n          # parse the argument, but ignore it otherwise\n          parse_generic_args_argument(self, method, named_args, default: true)\n          @name\n        end\n      when \"body\"\n        interpret_check_args { @body }\n      else\n        super\n      end\n    end\n  end\n\n  class CStructOrUnionDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"kind\"\n        interpret_check_args { MacroId.new(@union ? \"union\" : \"struct\") }\n      when \"name\"\n        interpret_check_args(named_params: [\"generic_args\"]) do\n          # parse the argument, but ignore it otherwise\n          parse_generic_args_argument(self, method, named_args, default: true)\n          Path.new(@name)\n        end\n      when \"body\"\n        interpret_check_args { @body }\n      when \"union?\"\n        interpret_check_args { BoolLiteral.new(@union) }\n      else\n        super\n      end\n    end\n  end\n\n  class FunDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"real_name\"\n        interpret_check_args { @real_name != @name ? StringLiteral.new(@real_name) : Nop.new }\n      when \"args\"\n        interpret_check_args { ArrayLiteral.map(@args, &.itself) }\n      when \"variadic?\"\n        interpret_check_args { BoolLiteral.new(@varargs) }\n      when \"return_type\"\n        interpret_check_args { @return_type || Nop.new }\n      when \"body\"\n        interpret_check_args { @body || Nop.new }\n      when \"has_body?\"\n        interpret_check_args { BoolLiteral.new(!@body.nil?) }\n      else\n        super\n      end\n    end\n  end\n\n  class TypeDef\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { Path.new(@name).at(@name_location) }\n      when \"type\"\n        interpret_check_args { @type_spec }\n      else\n        super\n      end\n    end\n  end\n\n  class ExternalVar\n    def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)\n      case method\n      when \"name\"\n        interpret_check_args { MacroId.new(@name) }\n      when \"real_name\"\n        interpret_check_args { (real_name = @real_name) ? StringLiteral.new(real_name) : Nop.new }\n      when \"type\"\n        interpret_check_args { @type_spec }\n      else\n        super\n      end\n    end\n  end\nend\n\nprivate def get_named_annotation_args(object)\n  if named_args = object.named_args\n    Crystal::NamedTupleLiteral.new(named_args.map { |arg| Crystal::NamedTupleLiteral::Entry.new(arg.name, arg.value) })\n  else\n    Crystal::NamedTupleLiteral.new\n  end\nend\n\nprivate def interpret_array_or_tuple_method(object, klass, method, args, named_args, block, interpreter)\n  case method\n  when \"any?\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args.first?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      Crystal::BoolLiteral.new(object.elements.any? do |elem|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.accept(block.body).truthy?\n      end)\n    end\n  when \"all?\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args.first?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      Crystal::BoolLiteral.new(object.elements.all? do |elem|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.accept(block.body).truthy?\n      end)\n    end\n  when \"splat\"\n    interpret_check_args(node: object, min_count: 0) do |arg|\n      if arg\n        unless arg.is_a?(Crystal::StringLiteral)\n          arg.raise \"argument to splat must be a StringLiteral, not #{arg.class_desc}\"\n        end\n\n        if object.elements.empty?\n          Crystal::MacroId.new(\"\")\n        else\n          Crystal::MacroId.new((object.elements.join \", \") + arg.value)\n        end\n      else\n        Crystal::MacroId.new(object.elements.join \", \")\n      end\n    end\n  when \"empty?\"\n    interpret_check_args(node: object) { Crystal::BoolLiteral.new(object.elements.empty?) }\n  when \"find\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args.first?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      found = object.elements.find do |elem|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.accept(block.body).truthy?\n      end\n      found ? found : Crystal::NilLiteral.new\n    end\n  when \"first\"\n    interpret_check_args(node: object) { object.elements.first? || Crystal::NilLiteral.new }\n  when \"includes?\"\n    interpret_check_args(node: object) do |arg|\n      Crystal::BoolLiteral.new(object.elements.includes?(arg))\n    end\n  when \"join\"\n    interpret_check_args(node: object) do |arg|\n      Crystal::StringLiteral.new(object.elements.map(&.to_macro_id).join arg.to_macro_id)\n    end\n  when \"last\"\n    interpret_check_args(node: object) { object.elements.last? || Crystal::NilLiteral.new }\n  when \"size\"\n    interpret_check_args(node: object) { Crystal::NumberLiteral.new(object.elements.size) }\n  when \"each\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args.first?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      object.elements.each do |elem|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.accept block.body\n      end\n\n      Crystal::NilLiteral.new\n    end\n  when \"each_with_index\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args[0]?\n      index_arg = block.args[1]?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      object.elements.each_with_index do |elem, idx|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.define_var(index_arg.name, Crystal::NumberLiteral.new idx) if index_arg\n        interpreter.accept block.body\n      end\n\n      Crystal::NilLiteral.new\n    end\n  when \"map\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args.first?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      klass.map(object.elements) do |elem|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.accept block.body\n      end\n    end\n  when \"map_with_index\"\n    interpret_check_args(node: object, uses_block: true) do\n      block_arg = block.args[0]?\n      index_arg = block.args[1]?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      klass.map_with_index(object.elements) do |elem, idx|\n        interpreter.define_var(block_arg.name, elem) if block_arg\n        interpreter.define_var(index_arg.name, Crystal::NumberLiteral.new idx) if index_arg\n        interpreter.accept block.body\n      end\n    end\n  when \"select\"\n    interpret_check_args(node: object, uses_block: true) do\n      filter(object, klass, block, interpreter)\n    end\n  when \"reject\"\n    interpret_check_args(node: object, uses_block: true) do\n      filter(object, klass, block, interpreter, keep: false)\n    end\n  when \"reduce\"\n    interpret_check_args(node: object, min_count: 0, uses_block: true) do |memo|\n      accumulate_arg = block.args.first?\n      value_arg = block.args[1]?\n\n      if object.elements.empty?\n        interpreter.not_interpreted_hook block.body, use_significant_node: true\n      end\n\n      if memo\n        object.elements.reduce(memo) do |accumulate, elem|\n          interpreter.define_var(accumulate_arg.name, accumulate) if accumulate_arg\n          interpreter.define_var(value_arg.name, elem) if value_arg\n          interpreter.accept block.body\n        end\n      else\n        object.elements.reduce do |accumulate, elem|\n          interpreter.define_var(accumulate_arg.name, accumulate) if accumulate_arg\n          interpreter.define_var(value_arg.name, elem) if value_arg\n          interpreter.accept block.body\n        end\n      end\n    end\n  when \"shuffle\"\n    interpret_check_args(node: object) { klass.new(object.elements.shuffle) }\n  when \"sort\"\n    interpret_check_args(node: object) { klass.new(object.elements.sort { |x, y| x.interpret_compare(y) }) }\n  when \"sort_by\"\n    interpret_check_args(node: object, uses_block: true) do\n      sort_by(object, klass, block, interpreter)\n    end\n  when \"uniq\"\n    interpret_check_args(node: object) { klass.new(object.elements.uniq) }\n  when \"[]\"\n    interpret_check_args(node: object, min_count: 1) do |from, to|\n      if to\n        from = interpreter.accept(from)\n        to = interpreter.accept(to)\n\n        unless from.is_a?(Crystal::NumberLiteral)\n          from.raise \"expected first argument to RangeLiteral#[] to be a number, not #{from.class_desc}\"\n        end\n\n        unless to.is_a?(Crystal::NumberLiteral)\n          to.raise \"expected second argument to RangeLiteral#[] to be a number, not #{from.class_desc}\"\n        end\n\n        from = from.to_number.to_i\n        to = to.to_number.to_i\n        values = object.elements[from, to]?\n        values ? klass.new(values) : Crystal::NilLiteral.new\n      else\n        case arg = from\n        when Crystal::NumberLiteral\n          index = arg.to_number.to_i\n          object.elements[index]? || Crystal::NilLiteral.new\n        when Crystal::RangeLiteral\n          range = arg.interpret_to_nilable_range(interpreter)\n          values = object.elements[range]?\n          values ? klass.new(values) : Crystal::NilLiteral.new\n        else\n          arg.raise \"argument to [] must be a number or range, not #{arg.class_desc}:\\n\\n#{arg}\"\n        end\n      end\n    end\n  when \"[]=\"\n    interpret_check_args(node: object) do |index_node, value|\n      unless index_node.is_a?(Crystal::NumberLiteral)\n        index_node.raise \"expected index argument to ArrayLiteral#[]= to be a number, not #{index_node.class_desc}\"\n      end\n\n      index = index_node.to_number.to_i\n      index += object.elements.size if index < 0\n\n      unless 0 <= index < object.elements.size\n        index_node.raise \"index out of bounds (index: #{index}, size: #{object.elements.size})\"\n      end\n\n      object.elements[index] = value\n      value\n    end\n  when \"unshift\"\n    interpret_check_args(node: object) do |arg|\n      object.elements.unshift(arg)\n      object\n    end\n  when \"push\", \"<<\"\n    interpret_check_args(node: object) do |arg|\n      object.elements << arg\n      object\n    end\n  when \"+\"\n    interpret_check_args(node: object) do |arg|\n      case arg\n      when Crystal::TupleLiteral then other_elements = arg.elements\n      when Crystal::ArrayLiteral then other_elements = arg.elements\n      else\n        arg.raise \"argument to `#{klass}#+` must be a tuple or array, not #{arg.class_desc}:\\n\\n#{arg}\"\n      end\n      klass.new(object.elements + other_elements)\n    end\n  when \"-\"\n    interpret_check_args(node: object) do |arg|\n      case arg\n      when Crystal::TupleLiteral then other_elements = arg.elements\n      when Crystal::ArrayLiteral then other_elements = arg.elements\n      else\n        arg.raise \"argument to `#{klass}#-` must be a tuple or array, not #{arg.class_desc}:\\n\\n#{arg}\"\n      end\n      klass.new(object.elements - other_elements)\n    end\n  when \"*\"\n    interpret_check_args(node: object) do |arg|\n      unless arg.is_a?(Crystal::NumberLiteral)\n        arg.raise \"argument to `#{klass}#*` must be a number, not #{arg.class_desc}\"\n      end\n\n      num = arg.to_number\n\n      unless num.is_a?(Int)\n        arg.raise \"argument to `#{klass}#*` must be an integer\"\n      end\n\n      klass.new(object.elements * num)\n    end\n  else\n    nil\n  end\nend\n\n# Checks the following in an invocation of a macro `foo`:\n#\n# * The number of macro arguments to `foo` matches the number of block\n#   parameters to this macro. If `min_count` is given then only that many macro\n#   parameters are required, others are optional and this macro's corresponding\n#   block parameter will receive `nil` instead.\n# * If `named_params` is true, any named arguments to `foo` are allowed. If it\n#   is falsey (the default), no named arguments are allowed. Otherwise, only\n#   named arguments included by `named_params` are allowed. The block parameters\n#   of this macro are unaffected by named arguments.\n# * There is a block supplied to `foo` if and only if `uses_block` is true.\n#\n# `top_level` affects how error messages are formatted.\n#\n# Accesses the `method`, `args`, `named_args`, and `block` variables in the\n# current scope.\nprivate macro interpret_check_args(*, node = self, min_count = nil, named_params = nil, uses_block = false, top_level = false, &block)\n  {% if uses_block %}\n    unless block\n      %full_name = full_macro_name({{ node }}, method, {{ top_level }})\n      {{ node }}.raise \"#{%full_name} is expected to be invoked with a block, but no block was given\"\n    end\n  {% else %}\n    if block\n      %full_name = full_macro_name({{ node }}, method, {{ top_level }})\n      {{ node }}.raise \"#{%full_name} is not expected to be invoked with a block, but a block was given\"\n    end\n  {% end %}\n\n  {% if named_params != true %}\n    %allowed_keys = {{ named_params }}\n    if named_args\n      {% if top_level %}\n      named_args.each do |arg|\n        {{ node }}.raise \"no parameter named '#{arg.name}'\" unless %allowed_keys.try(&.includes?(arg.name))\n      end\n      {% else %}\n      named_args.each_key do |name|\n        {{ node }}.raise \"no parameter named '#{name}'\" unless %allowed_keys.try(&.includes?(name))\n      end\n      {% end %}\n    end\n  {% end %}\n\n  {% if min_count %}\n    unless {{ min_count }} <= args.size <= {{ block.args.size }}\n      %full_name = full_macro_name({{ node }}, method, {{ top_level }})\n      {{ node }}.wrong_number_of_arguments %full_name, args.size, {{ min_count }}..{{ block.args.size }}\n    end\n\n    {% for var, i in block.args %}\n      {{ var }} = args[{{ i }}]{% if i >= min_count %}?{% end %}\n    {% end %}\n  {% else %}\n    unless args.size == {{ block.args.size }}\n      %full_name = full_macro_name({{ node }}, method, {{ top_level }})\n      {{ node }}.wrong_number_of_arguments %full_name, args.size, {{ block.args.size }}\n    end\n\n    {% for var, i in block.args %}\n      {{ var }} = args[{{ i }}]\n    {% end %}\n  {% end %}\n\n  {{ block.body }}\nend\n\nprivate macro interpret_check_args_toplevel(*, min_count = nil, uses_block = false, &block)\n  method = node.name\n  args = node.args\n  named_args = node.named_args\n  block = node.block\n  interpret_check_args(node: node, min_count: {{ min_count }}, uses_block: {{ uses_block }}, top_level: true) {{ block }}\nend\n\nprivate def full_macro_name(node, method, top_level)\n  if top_level\n    \"macro '::#{method}'\"\n  else\n    \"macro '#{node.class_desc}##{method}'\"\n  end\nend\n\nprivate def visibility_to_symbol(visibility)\n  visibility_name =\n    case visibility\n    when .private?\n      \"private\"\n    when .protected?\n      \"protected\"\n    else\n      \"public\"\n    end\n  Crystal::SymbolLiteral.new(visibility_name)\nend\n\nprivate def parse_generic_args_argument(node, method, named_args, *, default)\n  case named_arg = named_args.try &.[\"generic_args\"]?\n  when Nil\n    default\n  when Crystal::BoolLiteral\n    named_arg.value\n  else\n    named_arg.raise \"named argument 'generic_args' to #{node.class_desc}##{method} must be a BoolLiteral, not #{named_arg.class_desc}\"\n  end\nend\n\nprivate def type_definition_generic_name(node, method, args, named_args, block)\n  interpret_check_args(node: node, named_params: [\"generic_args\"]) do\n    if parse_generic_args_argument(node, method, named_args, default: true) && (type_vars = node.type_vars)\n      type_vars = type_vars.map_with_index do |type_var, i|\n        param = Crystal::MacroId.new(type_var)\n        param = Crystal::Splat.new(param) if i == node.splat_index\n        param\n      end\n      Crystal::Generic.new(node.name, type_vars)\n    else\n      node.name\n    end\n  end\nend\n\nprivate def macro_raise(node, args, interpreter, exception_type)\n  msg = args.map do |arg|\n    arg.accept interpreter\n    interpreter.last.to_macro_id\n  end\n  msg = msg.join \" \"\n\n  node.raise msg, exception_type: exception_type\nend\n\nprivate def macro_warning(node, args, interpreter)\n  msg = args.map do |arg|\n    arg.accept interpreter\n    interpreter.last.to_macro_id\n  end\n  msg = msg.join \" \"\n\n  interpreter.warnings.add_warning_at(node.location, msg)\n\n  Crystal::NilLiteral.new\nend\n\nprivate def empty_no_return_array\n  Crystal::ArrayLiteral.new(of: Crystal::Path.global(\"NoReturn\"))\nend\n\nprivate def regex_captures_hash(match_data : Regex::MatchData)\n  captures = Crystal::HashLiteral.new(\n    of: Crystal::HashLiteral::Entry.new(\n      Crystal::Union.new([Crystal::Path.global(\"Int32\"), Crystal::Path.global(\"String\")] of Crystal::ASTNode),\n      Crystal::Union.new([Crystal::Path.global(\"String\"), Crystal::Path.global(\"Nil\")] of Crystal::ASTNode),\n    )\n  )\n\n  match_data.to_h.each do |capture, substr|\n    case capture\n    in Int32\n      key = Crystal::NumberLiteral.new(capture)\n    in String\n      key = Crystal::StringLiteral.new(capture)\n    end\n\n    case substr\n    in String\n      value = Crystal::StringLiteral.new(substr)\n    in Nil\n      value = Crystal::NilLiteral.new\n    end\n\n    captures.entries << Crystal::HashLiteral::Entry.new(key, value)\n  end\n\n  captures\nend\n\nprivate def filter(object, klass, block, interpreter, keep = true)\n  block_arg = block.args.first?\n\n  if object.elements.empty?\n    interpreter.not_interpreted_hook block.body, use_significant_node: true\n  end\n\n  klass.new(object.elements.select do |elem|\n    interpreter.define_var(block_arg.name, elem) if block_arg\n    block_result = interpreter.accept(block.body).truthy?\n    keep ? block_result : !block_result\n  end)\nend\n\nprivate def fetch_annotation(node, method, args, named_args, block, &)\n  interpret_check_args(node: node) do |arg|\n    unless arg.is_a?(Crystal::TypeNode)\n      args[0].raise \"argument to '#{node.class_desc}#annotation' must be a TypeNode, not #{arg.class_desc}\"\n    end\n\n    type = arg.type\n    unless type.is_a?(Crystal::AnnotationType)\n      args[0].raise \"argument to '#{node.class_desc}#annotation' must be an annotation type, not #{type} (#{type.type_desc})\"\n    end\n\n    value = yield type\n    value || Crystal::NilLiteral.new\n  end\nend\n\nprivate def fetch_annotations(node, method, args, named_args, block, &)\n  interpret_check_args(node: node, min_count: 0) do |arg|\n    unless arg\n      return yield(nil) || Crystal::NilLiteral.new\n    end\n\n    unless arg.is_a?(Crystal::TypeNode)\n      args[0].raise \"argument to '#{node.class_desc}#annotation' must be a TypeNode, not #{arg.class_desc}\"\n    end\n\n    type = arg.type\n    unless type.is_a?(Crystal::AnnotationType)\n      args[0].raise \"argument to '#{node.class_desc}#annotation' must be an annotation type, not #{type} (#{type.type_desc})\"\n    end\n\n    value = yield type\n    value || Crystal::NilLiteral.new\n  end\nend\n\nprivate def sort_by(object, klass, block, interpreter)\n  block_arg = block.args.first?\n\n  if object.elements.empty?\n    interpreter.not_interpreted_hook block.body, use_significant_node: true\n  end\n\n  klass.new(object.elements.sort_by do |elem|\n    block_arg.try { |arg| interpreter.define_var(arg.name, elem) }\n    result = interpreter.accept(block.body)\n    InterpretCompareWrapper.new(result)\n  end)\nend\n\nprivate record InterpretCompareWrapper, node : Crystal::ASTNode do\n  include Comparable(self)\n\n  def <=>(other : self)\n    node.interpret_compare(other.node)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/macros/types.cr",
    "content": "module Crystal\n  class Program\n    @macro_types = {} of String => MacroType\n\n    # Defines the hierarchy of built-in AST node types. These types should\n    # mirror the structure of `../macros.cr`, plus all types that are available\n    # in the macro language, even if they have no macro methods defined yet.\n    private def define_macro_types\n      @macro_types[\"ASTNode\"] = @ast_node = ast_node = NonGenericMacroType.new self, \"ASTNode\", nil\n\n      @macro_types[\"Nop\"] = NonGenericMacroType.new self, \"Nop\", ast_node\n      @macro_types[\"NilLiteral\"] = NonGenericMacroType.new self, \"NilLiteral\", ast_node\n      @macro_types[\"BoolLiteral\"] = NonGenericMacroType.new self, \"BoolLiteral\", ast_node\n      @macro_types[\"NumberLiteral\"] = NonGenericMacroType.new self, \"NumberLiteral\", ast_node\n      @macro_types[\"CharLiteral\"] = NonGenericMacroType.new self, \"CharLiteral\", ast_node\n      @macro_types[\"StringLiteral\"] = NonGenericMacroType.new self, \"StringLiteral\", ast_node\n      @macro_types[\"StringInterpolation\"] = NonGenericMacroType.new self, \"StringInterpolation\", ast_node\n      @macro_types[\"SymbolLiteral\"] = NonGenericMacroType.new self, \"SymbolLiteral\", ast_node\n      @macro_types[\"ArrayLiteral\"] = NonGenericMacroType.new self, \"ArrayLiteral\", ast_node\n      @macro_types[\"HashLiteral\"] = NonGenericMacroType.new self, \"HashLiteral\", ast_node\n      @macro_types[\"NamedTupleLiteral\"] = NonGenericMacroType.new self, \"NamedTupleLiteral\", ast_node\n      @macro_types[\"RangeLiteral\"] = NonGenericMacroType.new self, \"RangeLiteral\", ast_node\n      @macro_types[\"RegexLiteral\"] = NonGenericMacroType.new self, \"RegexLiteral\", ast_node\n      @macro_types[\"TupleLiteral\"] = NonGenericMacroType.new self, \"TupleLiteral\", ast_node\n\n      # Crystal::MetaMacroVar\n      @macro_types[\"MetaVar\"] = NonGenericMacroType.new self, \"MetaVar\", ast_node\n\n      @macro_types[\"Annotation\"] = NonGenericMacroType.new self, \"Annotation\", ast_node\n      @macro_types[\"Var\"] = NonGenericMacroType.new self, \"Var\", ast_node\n      @macro_types[\"Block\"] = NonGenericMacroType.new self, \"Block\", ast_node\n      @macro_types[\"Expressions\"] = NonGenericMacroType.new self, \"Expressions\", ast_node\n      @macro_types[\"Call\"] = NonGenericMacroType.new self, \"Call\", ast_node\n      @macro_types[\"NamedArgument\"] = NonGenericMacroType.new self, \"NamedArgument\", ast_node\n      @macro_types[\"If\"] = NonGenericMacroType.new self, \"If\", ast_node\n      @macro_types[\"Assign\"] = NonGenericMacroType.new self, \"Assign\", ast_node\n      @macro_types[\"MultiAssign\"] = NonGenericMacroType.new self, \"MultiAssign\", ast_node\n      @macro_types[\"InstanceVar\"] = NonGenericMacroType.new self, \"InstanceVar\", ast_node\n      @macro_types[\"ReadInstanceVar\"] = NonGenericMacroType.new self, \"ReadInstanceVar\", ast_node\n      @macro_types[\"ClassVar\"] = NonGenericMacroType.new self, \"ClassVar\", ast_node\n      @macro_types[\"Global\"] = NonGenericMacroType.new self, \"Global\", ast_node\n\n      @macro_types[\"BinaryOp\"] = binary_op = NonGenericMacroType.new self, \"BinaryOp\", ast_node\n      @macro_types[\"And\"] = NonGenericMacroType.new self, \"And\", binary_op\n      @macro_types[\"Or\"] = NonGenericMacroType.new self, \"Or\", binary_op\n\n      @macro_types[\"Arg\"] = NonGenericMacroType.new self, \"Arg\", ast_node\n      @macro_types[\"ProcNotation\"] = NonGenericMacroType.new self, \"ProcNotation\", ast_node\n      @macro_types[\"Def\"] = def_type = NonGenericMacroType.new self, \"Def\", ast_node\n      @macro_types[\"External\"] = NonGenericMacroType.new self, \"External\", def_type\n      @macro_types[\"Macro\"] = NonGenericMacroType.new self, \"Macro\", ast_node\n\n      @macro_types[\"UnaryExpression\"] = unary_expression = NonGenericMacroType.new self, \"UnaryExpression\", ast_node\n      @macro_types[\"Not\"] = NonGenericMacroType.new self, \"Not\", unary_expression\n      @macro_types[\"PointerOf\"] = NonGenericMacroType.new self, \"PointerOf\", unary_expression\n      @macro_types[\"SizeOf\"] = NonGenericMacroType.new self, \"SizeOf\", unary_expression\n      @macro_types[\"InstanceSizeOf\"] = NonGenericMacroType.new self, \"InstanceSizeOf\", unary_expression\n      @macro_types[\"AlignOf\"] = NonGenericMacroType.new self, \"AlignOf\", unary_expression\n      @macro_types[\"InstanceAlignOf\"] = NonGenericMacroType.new self, \"InstanceAlignOf\", unary_expression\n      @macro_types[\"Out\"] = NonGenericMacroType.new self, \"Out\", unary_expression\n      @macro_types[\"Splat\"] = NonGenericMacroType.new self, \"Splat\", unary_expression\n      @macro_types[\"DoubleSplat\"] = NonGenericMacroType.new self, \"DoubleSplat\", unary_expression\n\n      @macro_types[\"OffsetOf\"] = NonGenericMacroType.new self, \"OffsetOf\", ast_node\n      @macro_types[\"VisibilityModifier\"] = NonGenericMacroType.new self, \"VisibilityModifier\", ast_node\n      @macro_types[\"IsA\"] = NonGenericMacroType.new self, \"IsA\", ast_node\n      @macro_types[\"RespondsTo\"] = NonGenericMacroType.new self, \"RespondsTo\", ast_node\n      @macro_types[\"Require\"] = NonGenericMacroType.new self, \"Require\", ast_node\n      @macro_types[\"When\"] = NonGenericMacroType.new self, \"When\", ast_node\n      @macro_types[\"Case\"] = NonGenericMacroType.new self, \"Case\", ast_node\n      @macro_types[\"ImplicitObj\"] = NonGenericMacroType.new self, \"ImplicitObj\", ast_node\n      @macro_types[\"Path\"] = NonGenericMacroType.new self, \"Path\", ast_node\n      @macro_types[\"ClassDef\"] = NonGenericMacroType.new self, \"ClassDef\", ast_node\n      @macro_types[\"While\"] = NonGenericMacroType.new self, \"While\", ast_node\n      @macro_types[\"Generic\"] = NonGenericMacroType.new self, \"Generic\", ast_node\n      @macro_types[\"TypeDeclaration\"] = NonGenericMacroType.new self, \"TypeDeclaration\", ast_node\n      @macro_types[\"UninitializedVar\"] = NonGenericMacroType.new self, \"UninitializedVar\", ast_node\n      @macro_types[\"Rescue\"] = NonGenericMacroType.new self, \"Rescue\", ast_node\n      @macro_types[\"ExceptionHandler\"] = NonGenericMacroType.new self, \"ExceptionHandler\", ast_node\n      @macro_types[\"ProcLiteral\"] = NonGenericMacroType.new self, \"ProcLiteral\", ast_node\n      @macro_types[\"ProcPointer\"] = NonGenericMacroType.new self, \"ProcPointer\", ast_node\n      @macro_types[\"Union\"] = NonGenericMacroType.new self, \"Union\", ast_node\n\n      @macro_types[\"ControlExpression\"] = control_expression = NonGenericMacroType.new self, \"ControlExpression\", ast_node\n      @macro_types[\"Return\"] = NonGenericMacroType.new self, \"Return\", control_expression\n      @macro_types[\"Break\"] = NonGenericMacroType.new self, \"Break\", control_expression\n      @macro_types[\"Next\"] = NonGenericMacroType.new self, \"Next\", control_expression\n\n      @macro_types[\"Yield\"] = NonGenericMacroType.new self, \"Yield\", ast_node\n      @macro_types[\"Metaclass\"] = NonGenericMacroType.new self, \"Metaclass\", ast_node\n      @macro_types[\"Cast\"] = NonGenericMacroType.new self, \"Cast\", ast_node\n      @macro_types[\"NilableCast\"] = NonGenericMacroType.new self, \"NilableCast\", ast_node\n      @macro_types[\"MacroId\"] = NonGenericMacroType.new self, \"MacroId\", ast_node\n      @macro_types[\"TypeNode\"] = NonGenericMacroType.new self, \"TypeNode\", ast_node\n\n      @macro_types[\"MacroExpression\"] = NonGenericMacroType.new self, \"MacroExpression\", ast_node\n      @macro_types[\"MacroFor\"] = NonGenericMacroType.new self, \"MacroFor\", ast_node\n      @macro_types[\"MacroIf\"] = NonGenericMacroType.new self, \"MacroIf\", ast_node\n      @macro_types[\"MacroLiteral\"] = NonGenericMacroType.new self, \"MacroLiteral\", ast_node\n      @macro_types[\"MacroVar\"] = NonGenericMacroType.new self, \"MacroVar\", ast_node\n      @macro_types[\"MacroVerbatim\"] = NonGenericMacroType.new self, \"MacroVerbatim\", unary_expression\n\n      # bottom type\n      @macro_types[\"NoReturn\"] = @macro_no_return = NoReturnMacroType.new self\n\n      @macro_types[\"Self\"] = NonGenericMacroType.new self, \"Self\", ast_node\n      @macro_types[\"Underscore\"] = NonGenericMacroType.new self, \"Underscore\", ast_node\n      @macro_types[\"Select\"] = NonGenericMacroType.new self, \"Select\", ast_node\n      @macro_types[\"Asm\"] = NonGenericMacroType.new self, \"Asm\", ast_node\n      @macro_types[\"AsmOperand\"] = NonGenericMacroType.new self, \"AsmOperand\", ast_node\n      @macro_types[\"MagicConstant\"] = NonGenericMacroType.new self, \"MagicConstant\", ast_node\n      @macro_types[\"Primitive\"] = NonGenericMacroType.new self, \"Primitive\", ast_node\n      @macro_types[\"TypeOf\"] = NonGenericMacroType.new self, \"TypeOf\", ast_node\n      @macro_types[\"AnnotationDef\"] = NonGenericMacroType.new self, \"AnnotationDef\", ast_node\n      @macro_types[\"CStructOrUnionDef\"] = NonGenericMacroType.new self, \"CStructOrUnionDef\", ast_node\n      @macro_types[\"EnumDef\"] = NonGenericMacroType.new self, \"EnumDef\", ast_node\n      @macro_types[\"FunDef\"] = NonGenericMacroType.new self, \"FunDef\", ast_node\n      @macro_types[\"LibDef\"] = NonGenericMacroType.new self, \"LibDef\", ast_node\n      @macro_types[\"ModuleDef\"] = NonGenericMacroType.new self, \"ModuleDef\", ast_node\n      @macro_types[\"Alias\"] = NonGenericMacroType.new self, \"Alias\", ast_node\n      @macro_types[\"Extend\"] = NonGenericMacroType.new self, \"Extend\", ast_node\n      @macro_types[\"ExternalVar\"] = NonGenericMacroType.new self, \"ExternalVar\", ast_node\n      @macro_types[\"Include\"] = NonGenericMacroType.new self, \"Include\", ast_node\n      @macro_types[\"TypeDef\"] = NonGenericMacroType.new self, \"TypeDef\", ast_node\n    end\n\n    # Returns the macro type for a given AST node. This association is done\n    # through `Crystal::ASTNode#class_desc`.\n    def node_macro_type(node : ASTNode) : MacroType\n      @macro_types[node.class_desc]\n    end\n\n    # Returns the macro type named by a given AST node in the macro language.\n    def lookup_macro_type(name : Path)\n      # NOTE: `name.global?` doesn't matter since there are no namespaces for\n      # the AST node types\n      if name.names.size == 1\n        macro_type = @macro_types[name.names.first]?\n      end\n      macro_type || macro_no_return\n    end\n\n    def lookup_macro_type(name : Union)\n      MacroType.union(self, name.types.map { |type| lookup_macro_type(type) })\n    end\n\n    def lookup_macro_type(name : ASTNode)\n      macro_no_return\n    end\n\n    def ast_node\n      @ast_node.not_nil!\n    end\n\n    def macro_no_return\n      @macro_no_return.not_nil!\n    end\n  end\n\n  # An AST node type in the macro language, used in places such as\n  # `ASTNode#is_a?` that expect an AST node type instead of a \"regular\" type.\n  abstract class MacroType\n    getter program : Program\n\n    def initialize(@program)\n    end\n  end\n\n  # An AST node type that cannot be generic, e.g. `NumberLiteral`.\n  class NonGenericMacroType < MacroType\n    getter name : String\n    getter parent : MacroType?\n\n    def initialize(program, @name, @parent)\n      super(program)\n    end\n\n    def to_s(io : IO) : Nil\n      io << @name\n    end\n  end\n\n  # The bottom type of AST nodes. No AST nodes are of this type. Meaningful as\n  # a return type, e.g. `::raise`.\n  class NoReturnMacroType < MacroType\n    def to_s(io : IO) : Nil\n      io << \"NoReturn\"\n    end\n  end\n\n  # An irreducible union of 2 or more AST node types.\n  class UnionMacroType < MacroType\n    getter union_macro_types : Array(MacroType)\n\n    def initialize(program, @union_macro_types)\n      super(program)\n    end\n\n    def to_s(io : IO) : Nil\n      io << '('\n      @union_macro_types.join(io, \" | \")\n      io << ')'\n    end\n  end\n\n  class MacroType\n    # Returns true if *macro_type* is a subtype of *other*; that is, every AST\n    # node instance of *macro_type* is also an instance of *other*.\n    def self.subtype?(macro_type : NonGenericMacroType, other : NonGenericMacroType)\n      return true if macro_type == other\n      parent = macro_type.parent\n      !parent.nil? && subtype?(parent, other)\n    end\n\n    def self.subtype?(macro_type : NoReturnMacroType, other : MacroType)\n      true\n    end\n\n    def self.subtype?(macro_type : UnionMacroType, other : MacroType)\n      macro_type.union_macro_types.all? { |union_type| subtype?(union_type, other) }\n    end\n\n    def self.subtype?(macro_type : MacroType, other : UnionMacroType)\n      other.union_macro_types.any? { |union_type| subtype?(macro_type, union_type) }\n    end\n\n    def self.subtype?(macro_type : MacroType, other : MacroType)\n      false\n    end\n\n    # Returns the union of the given macro *types*. Additionally reduces the\n    # variant types so that:\n    #\n    # * There are no duplicate variant types;\n    # * `NoReturn` is not among the variant types, unless the union is empty;\n    # * No variant type is a subtype of another variant type.\n    #\n    # This is analogous to `Program#type_merge_union_of` for regular types. The\n    # returned macro type is a `UnionMacroType` only if there are 2 or more\n    # variant types.\n    def self.union(program : Program, types : Array(MacroType)) : MacroType\n      return program.macro_no_return if types.empty?\n\n      flattened = [] of MacroType\n      types.each do |type|\n        case type\n        when UnionMacroType\n          flattened.concat(type.union_macro_types)\n        when NoReturnMacroType\n          # do nothing\n        else\n          flattened << type\n        end\n      end\n      flattened.uniq!\n\n      case flattened.size\n      when 0\n        return program.macro_no_return\n      when 1\n        return flattened.first\n      when 2\n        a, b = flattened\n        return b if subtype?(a, b)\n        return a if subtype?(b, a)\n        merged = flattened\n      else\n        merged = flattened.reject do |type|\n          flattened.any? do |other|\n            type != other && subtype?(type, other)\n          end\n        end\n      end\n\n      case merged.size\n      when 0\n        program.macro_no_return\n      when 1\n        merged.first\n      else\n        UnionMacroType.new(program, merged)\n      end\n    end\n  end\n\n  class ASTNode\n    def macro_is_a?(macro_type : MacroType) : Bool\n      MacroType.subtype?(macro_type.program.node_macro_type(self), macro_type)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/macros.cr",
    "content": "{% skip_file unless flag?(:docs) %}\n\n# Defines string related macro methods.\n#\n# Many `StringLiteral` methods can be called from `SymbolLiteral` and `MacroId`,\n# because they are delegated to `StringLiteral`.\n# So, their documentations should be shared between `StringLiteral` and others.\nprivate macro def_string_methods(klass)\n  # Returns a `MacroId` for this string's contents.\n  def id : MacroId\n  end\n\n  # Similar to `String#[]`.\n  def [](range : RangeLiteral) : {{klass}}\n  end\n\n  # Similar to `String#matches?`.\n  def =~(range : RegexLiteral) : BoolLiteral\n  end\n\n  # Similar to `String#+`.\n  def +(other : StringLiteral | CharLiteral) : {{klass}}\n  end\n\n  # Similar to `String#camelcase`.\n  def camelcase(*, lower : BoolLiteral = false) : {{klass}}\n  end\n\n  # Similar to `String#capitalize`.\n  def capitalize : {{klass}}\n  end\n\n  # Similar to `String#chars`.\n  def chars : ArrayLiteral(CharLiteral)\n  end\n\n  # Similar to `String#chomp`.\n  def chomp : {{klass}}\n  end\n\n  # Similar to `String#count`.\n  def count(other : CharLiteral) : NumberLiteral\n  end\n\n  # Similar to `String#downcase`.\n  def downcase : {{klass}}\n  end\n\n  # Similar to `String#empty?`.\n  def empty? : BoolLiteral\n  end\n\n  # Similar to `String#ends_with?`.\n  def ends_with?(other : StringLiteral | CharLiteral) : BoolLiteral\n  end\n\n  # Similar to `String#gsub(pattern, options, &)`.\n  #\n  # NOTE: The special variables `$~` and `$1`, `$2`, ... are not supported.\n  def gsub(regex : RegexLiteral, & : StringLiteral, ArrayLiteral(StringLiteral | NilLiteral) -> _) : {{klass}}\n  end\n\n  # Similar to `String#gsub`.\n  def gsub(regex : RegexLiteral, replacement : StringLiteral) : {{klass}}\n  end\n\n  # Similar to `String#includes?`.\n  def includes?(search : StringLiteral | CharLiteral) : BoolLiteral\n  end\n\n  # Matches the given *regex* against this string and returns a capture hash, or\n  # `nil` if a match cannot be found.\n  #\n  # The capture hash has the same form as `Regex::MatchData#to_h`.\n  def match(regex : RegexLiteral) : HashLiteral(NumberLiteral | StringLiteral, StringLiteral | NilLiteral)\n  end\n\n  # Returns an array of capture hashes for each match of *regex* in this string.\n  #\n  # Capture hashes have the same form as `Regex::MatchData#to_h`.\n  def scan(regex : RegexLiteral) : ArrayLiteral(HashLiteral(NumberLiteral | StringLiteral, StringLiteral | NilLiteral))\n  end\n\n  # Similar to `String#size`.\n  def size : NumberLiteral\n  end\n\n  # Similar to `String#lines`.\n  def lines : ArrayLiteral(StringLiteral)\n  end\n\n  # Similar to `String#split()`.\n  def split : ArrayLiteral(StringLiteral)\n  end\n\n  # Similar to `String#split(String)`.\n  def split(node : StringLiteral) : ArrayLiteral(StringLiteral)\n  end\n\n  # Similar to `String#split(Char)`.\n  def split(node : CharLiteral) : ArrayLiteral(StringLiteral)\n  end\n\n  # Similar to `String#split(Regex)`.\n  def split(node : RegexLiteral) : ArrayLiteral(StringLiteral)\n  end\n\n  # Similar to `String#split(String)`.\n  @[Deprecated(\"Use `#split(StringLiteral)` instead\")]\n  def split(node : ASTNode) : ArrayLiteral(StringLiteral)\n  end\n\n  # Similar to `String#starts_with?`.\n  def starts_with?(other : StringLiteral | CharLiteral) : BoolLiteral\n  end\n\n  # Similar to `String#strip`.\n  def strip : {{klass}}\n  end\n\n  # Similar to `String#titleize`.\n  def titleize : {{klass}}\n  end\n\n  # Similar to `String#to_i`.\n  def to_i(base = 10)\n  end\n\n  # Returns an expression that evaluates to a slice literal containing the\n  # UTF-16 code units of this string, plus an extra trailing null character.\n  # This null character is not part of the slice, but ensures that calling\n  # `#to_unsafe` always results in a properly null-terminated C string.\n  #\n  # ```\n  # {{ \"abc😂\".to_utf16 }} # => ::Slice(::UInt16).literal(97, 98, 99, 55357, 56834, 0)[0, 5]\n  # ```\n  #\n  # WARNING: The return value is not necessarily a literal node.\n  @[Experimental(\"Slice literals are still under development. Join the discussion at [#2886](https://github.com/crystal-lang/crystal/issues/2886).\")]\n  def to_utf16 : ASTNode\n  end\n\n  # Similar to `String#tr`.\n  def tr(from : StringLiteral, to : StringLiteral) : {{klass}}\n  end\n\n  # Similar to `String#underscore`.\n  def underscore : {{klass}}\n  end\n\n  # Similar to `String#upcase`.\n  def upcase : {{klass}}\n  end\nend\n\n# The `Macros` module is a fictitious module used to document macros\n# and macro methods.\n#\n# You can invoke a **fixed subset** of methods on AST nodes at compile-time. These methods\n# are documented on the classes in this module. Additionally, methods of the\n# `Macros` module are top-level methods that you can invoke, like `puts` and `run`.\nmodule Crystal::Macros\n  # Compares two [semantic versions](http://semver.org/).\n  #\n  # Returns `-1`, `0` or `1` depending on whether *v1* is lower than *v2*,\n  # equal to *v2* or greater than *v2*.\n  #\n  # ```\n  # {{ compare_versions(\"1.10.0\", \"1.2.0\") }} # => 1\n  # ```\n  def compare_versions(v1 : StringLiteral, v2 : StringLiteral) : NumberLiteral\n  end\n\n  # Outputs the current macro's buffer to the standard output. Useful for debugging\n  # a macro to see what's being generated.\n  #\n  # By default, the output is tried to be formatted using Crystal's\n  # formatter, but you can disable this by passing `false` to this method.\n  def debug(format = true) : Nop\n  end\n\n  # Gets the value of an environment variable at compile-time, or `nil` if it doesn't exist.\n  def env(name) : StringLiteral | NilLiteral\n  end\n\n  # Returns whether a [compile-time flag](https://crystal-lang.org/docs/syntax_and_semantics/compile_time_flags.html) is set.\n  #\n  # ```\n  # {{ flag?(:x86_64) }} # true or false\n  # ```\n  def flag?(name) : BoolLiteral\n  end\n\n  # Returns whether a [compile-time flag](https://crystal-lang.org/docs/syntax_and_semantics/compile_time_flags.html)\n  # is set for the *host* platform, which can differ from the target platform\n  # (`flag?`) during cross-compilation.\n  #\n  # ```\n  # {{ host_flag?(:win32) }} # true or false\n  # ```\n  def host_flag?(name) : BoolLiteral\n  end\n\n  # Parses *type_name* into a `Path`, `Generic` (also used for unions), `ProcNotation`, or `Metaclass`.\n  #\n  # The `#resolve` method could then be used to resolve the value into a `TypeNode`, if the *type_name* represents a type,\n  # otherwise the value of the constant.\n  #\n  # A compile time error is raised if the type/constant does not actually exist,\n  # or if a required generic argument was not provided.\n  #\n  # ```\n  # class Foo; end\n  #\n  # struct Some::Namespace::Foo; end\n  #\n  # module Bar(T); end\n  #\n  # MY_CONST = 1234\n  #\n  # {{ parse_type(\"Foo\").resolve.class? }}                                   # => true\n  # {{ parse_type(\"Some::Namespace::Foo\").resolve.struct? }}                 # => true\n  # {{ parse_type(\"Foo|Some::Namespace::Foo\").resolve.union_types.size }}    # => 2\n  # {{ parse_type(\"Bar(Int32)|Foo\").resolve.union_types[0].type_vars.size }} # => 1\n  # {{ parse_type(\"MY_CONST\").resolve }}                                     # => 1234\n  #\n  # {{ parse_type(\"MissingType\").resolve }}   # Error: undefined constant MissingType\n  # {{ parse_type(\"UNKNOWN_CONST\").resolve }} # Error: undefined constant UNKNOWN_CONST\n  # ```\n  def parse_type(type_name : StringLiteral) : Path | Generic | ProcNotation | Metaclass\n  end\n\n  # Prints AST nodes at compile-time. Useful for debugging macros.\n  def puts(*expressions) : Nop\n  end\n\n  # Prints AST nodes at compile-time. Useful for debugging macros.\n  def print(*expressions) : Nop\n  end\n\n  # Same as `puts`.\n  def p(*expressions) : Nop\n  end\n\n  # Same as `puts`.\n  def pp(*expressions) : Nop\n  end\n\n  # Prints macro expressions together with their values at compile-time. Useful for debugging macros.\n  def p!(*expressions) : Nop\n  end\n\n  # Same as `p!`\n  def pp!(*expressions) : Nop\n  end\n\n  # Executes a system command and returns the output as a `MacroId`.\n  # Gives a compile-time error if the command failed to execute.\n  #\n  # It is impossible to call this method with any regular call syntax. There is an associated literal type which calls the method with the literal content as command:\n  #\n  # ```\n  # {{ `echo hi` }} # => \"hi\\n\"\n  # ```\n  #\n  # See [`Command` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/command.html) in the language reference.\n  def `(command) : MacroId\n  end\n\n  # Executes a system command and returns the output as a `MacroId`.\n  # Gives a compile-time error if the command failed to execute.\n  #\n  # ```\n  # {{ system(\"echo hi\") }} # => \"hi\\n\"\n  # ```\n  def system(command) : MacroId\n  end\n\n  # Gives a compile-time error with the given *message*.\n  def raise(message) : NoReturn\n  end\n\n  # Emits a compile-time warning with the given *message*.\n  def warning(message : StringLiteral) : NilLiteral\n  end\n\n  # Returns `true` if the given *filename* exists, `false` otherwise.\n  def file_exists?(filename) : BoolLiteral\n  end\n\n  # Reads a file and returns a `StringLiteral` with its contents.\n  #\n  # Gives a compile-time error if the file doesn't exist or if\n  # reading the file fails.\n  #\n  # To read a file relative to where the macro is defined, use:\n  #\n  # ```\n  # read_file(\"#{__DIR__}/some_file.txt\")\n  # ```\n  #\n  # NOTE: Relative paths are resolved to the current working directory.\n  def read_file(filename) : StringLiteral\n  end\n\n  # Same as `read_file`, except that `nil` is returned on any I/O failure\n  # instead of issuing a compile-time failure.\n  def read_file?(filename) : StringLiteral | NilLiteral\n  end\n\n  # Compiles and execute a Crystal program and returns its output\n  # as a `MacroId`.\n  #\n  # The file denoted by *filename* must be a valid Crystal program.\n  # This macro invocation passes *args* to the program as regular\n  # program arguments. This output is the result of this macro invocation,\n  # as a `MacroId`.\n  #\n  # The `run` macro is useful when the subset of available macro methods\n  # are not enough for your purposes and you need something more powerful.\n  # With `run` you can read files at compile time, connect to the internet\n  # or to a database.\n  #\n  # A simple example:\n  #\n  # ```\n  # # read.cr\n  # puts File.read(ARGV[0])\n  # ```\n  #\n  # ```\n  # # main.cr\n  # macro read_file_at_compile_time(filename)\n  #   {{ run(\"./read\", filename).stringify }}\n  # end\n  #\n  # puts read_file_at_compile_time(\"some_file.txt\")\n  # ```\n  #\n  # The above generates a program that will have the contents of `some_file.txt`.\n  # The file, however, is read at compile time and will not be needed at runtime.\n  #\n  # NOTE: the compiler is allowed to cache the executable generated for\n  # *filename* and only recompile it if any of the files it depends on changes\n  # (their modified time). This is why it's **strongly discouraged** to use a program\n  # for `run` that changes in subsequent compilations (for example, if it executes\n  # shell commands at compile time, or other macro run programs). It's also strongly\n  # discouraged to have a macro run program take a lot of time, because this will\n  # slow down compilation times. Reading files is OK, opening an HTTP connection\n  # at compile-time will most likely result in very slow compilations.\n  def run(filename, *args) : MacroId\n  end\n\n  # Skips the rest of the file from which it is executed.\n  # Typical usage is to skip files that have platform specific code,\n  # without having to surround the most relevant code in `{% if flag?(...) %} ... {% end %}` macro blocks.\n  #\n  # Example:\n  #\n  # ```\n  # # sth_for_osx.cr\n  # {% skip_file unless flag?(:darwin) %}\n  #\n  # # Class FooForMac will only be defined if we're compiling on OS X\n  # class FooForMac\n  # end\n  # ```\n  def skip_file : Nop\n  end\n\n  # Returns the size of the given *type* as number of bytes.\n  #\n  # For definition purposes, a type is considered to be **stable** if its size\n  # and alignment do not change as new code is being processed. Currently, all\n  # Crystal types are stable, _except_ the following:\n  #\n  # * Structs, e.g. `Bytes`\n  # * `ReferenceStorage` instances\n  # * Modules, e.g. `Math` (however, `Math.class` is stable)\n  # * Uninstantiated generic types, e.g. `Array`\n  # * `StaticArray`, `Tuple`, `NamedTuple` instances with unstable element types\n  # * Unions containing any unstable types\n  #\n  # *type* must be a constant referring to a stable type. It cannot be evaluated\n  # at macro evaluation time, nor a `typeof` expression.\n  #\n  # ```\n  # {{ sizeof(Int32) }} # => 4\n  # {{ sizeof(Void*) }} # usually 4 or 8\n  # ```\n  def __crystal_pseudo_sizeof(type) : NumberLiteral\n  end\n\n  # Returns the alignment of the given *type* as number of bytes.\n  #\n  # *type* must be a constant referring to a stable type. It cannot be evaluated\n  # at macro evaluation time, nor a `typeof` expression.\n  #\n  # See `sizeof` for the definition of a stable type.\n  #\n  # ```\n  # {{ alignof(Int32) }} # => 4\n  # {{ alignof(Void*) }} # usually 4 or 8\n  # ```\n  def __crystal_pseudo_alignof(type) : NumberLiteral\n  end\n\n  # This is the base class of all AST nodes. This methods are\n  # available to all AST nodes.\n  abstract class ASTNode\n    # Returns this node as a `MacroId`. Useful when you need an identifier\n    # out of a `StringLiteral`, `SymbolLiteral`, `Var` or `Call`.\n    #\n    # ```\n    # macro define_method(name, content)\n    #   def {{name.id}}\n    #     {{content}}\n    #   end\n    # end\n    #\n    # define_method :foo, 1\n    # define_method \"bar\", 2\n    # define_method baz, 3\n    #\n    # puts foo # => prints 1\n    # puts bar # => prints 2\n    # puts baz # => prints 3\n    # ```\n    def id : MacroId\n    end\n\n    # Returns a `StringLiteral` that contains this node's textual representation.\n    # Note that invoking stringify on a string literal will return a `StringLiteral`\n    # that contains a string literal.\n    #\n    # ```\n    # macro test\n    #   {{ \"foo\".stringify }}\n    # end\n    #\n    # puts test # prints \"foo\" (including the double quotes)\n    # ```\n    def stringify : StringLiteral\n    end\n\n    # Returns a `SymbolLiteral` that contains this node's textual representation.\n    #\n    # ```\n    # {{ \"foo\".id.symbolize }} # => :foo\n    # ```\n    def symbolize : SymbolLiteral\n    end\n\n    # Returns a `StringLiteral` that contains this node's name.\n    #\n    # ```\n    # macro test\n    #   {{ \"foo\".class_name }}\n    # end\n    #\n    # puts test # => prints StringLiteral\n    # ```\n    def class_name : StringLiteral\n    end\n\n    # Returns the filename where this node is located.\n    # Might return `nil` if the location is not known.\n    def filename : StringLiteral | NilLiteral\n    end\n\n    # Returns the line number where this node begins.\n    # Might return `nil` if the location is not known.\n    #\n    # The first line number in a file is 1.\n    def line_number : StringLiteral | NilLiteral\n    end\n\n    # Returns the column number where this node begins.\n    # Might return `nil` if the location is not known.\n    #\n    # The first column number in a line is `1`.\n    def column_number : StringLiteral | NilLiteral\n    end\n\n    # Returns the line number where this node ends.\n    # Might return `nil` if the location is not known.\n    #\n    # The first line number in a file is `1`.\n    def end_line_number : StringLiteral | NilLiteral\n    end\n\n    # Returns the column number where this node ends.\n    # Might return `nil` if the location is not known.\n    #\n    # The first column number in a line is `1`.\n    def end_column_number : StringLiteral | NilLiteral\n    end\n\n    # Returns `true` if this node's textual representation is the same as\n    # the *other* node.\n    def ==(other : ASTNode) : BoolLiteral\n    end\n\n    # Returns `true` if this node's textual representation is not the same as\n    # the *other* node.\n    def !=(other : ASTNode) : BoolLiteral\n    end\n\n    # Gives a compile-time error with the given *message*.\n    # This will highlight this node in the error message.\n    def raise(message) : NoReturn\n    end\n\n    # Emits a compile-time warning with the given *message*.\n    # This will highlight this node in the warning message.\n    def warning(message : StringLiteral) : NilLiteral\n    end\n\n    # Returns a `StringLiteral` that contains the documentation comments attached to this node, or an empty string if there are none.\n    #\n    # WARNING: The return value will be an empty string when executed outside of the `crystal docs` command.\n    def doc : StringLiteral\n    end\n\n    # Returns a `MacroId` that contains the documentation comments attached to this node, or an empty `MacroId` if there are none.\n    # Each line is prefixed with a `#` character to allow the output to be used directly within another node's documentation comment.\n    #\n    # A common use case is combining this method with the `@caller` macro instance variable in order to allow [merging macro expansion and call comments](https://crystal-lang.org/reference/syntax_and_semantics/macros/index.html#merging-expansion-and-call-comments).\n    #\n    # WARNING: The return value will be empty when executed outside of the `crystal docs` command.\n    def doc_comment : MacroId\n    end\n\n    # Returns `true` if this node's type is the given *type* or any of its\n    # subclasses.\n    #\n    # *type* always refers to an AST node type, never a type in the program.\n    #\n    # ```\n    # {{ 1.is_a?(NumberLiteral) }} # => true\n    # {{ 1.is_a?(BoolLiteral) }}   # => false\n    # {{ 1.is_a?(ASTNode) }}       # => true\n    # {{ 1.is_a?(Int32) }}         # => false\n    # ```\n    def __crystal_pseudo_is_a?(type : TypeNode) : BoolLiteral\n    end\n\n    # Returns `true` if this node is a `NilLiteral` or `Nop`.\n    def __crystal_pseudo_nil? : BoolLiteral\n    end\n  end\n\n  # The empty node. Similar to a `NilLiteral` but its textual representation\n  # is the empty string. This corresponds, for example, to the missing `else` branch of\n  # an `if` without an `else`.\n  class Nop < ASTNode\n  end\n\n  # The nil literal.\n  class NilLiteral < ASTNode\n  end\n\n  # A bool literal.\n  class BoolLiteral < ASTNode\n  end\n\n  # Any number literal.\n  class NumberLiteral < ASTNode\n    # Returns `true` if value is 0, `false` otherwise.\n    def zero? : BoolLiteral\n    end\n\n    # Compares this node's value to another node's value.\n    def <(other : NumberLiteral) : BoolLiteral\n    end\n\n    # :ditto:\n    def <=(other : NumberLiteral) : BoolLiteral\n    end\n\n    # :ditto:\n    def >(other : NumberLiteral) : BoolLiteral\n    end\n\n    # :ditto:\n    def >=(other : NumberLiteral) : BoolLiteral\n    end\n\n    # :ditto:\n    def <=>(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#+`\n    def +(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#-`\n    def -(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#*`\n    def *(other : NumberLiteral) : NumberLiteral\n    end\n\n    # MathInterpreter only works with Integer and Number#/ : Float\n    #\n    # # Same as `Number#/`\n    # def /(other : NumberLiteral) : NumberLiteral\n    # end\n\n    # Same as `Number#//`\n    def //(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#%`\n    def %(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#&`\n    def &(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#|`\n    def |(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#^`\n    def ^(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#**`\n    def **(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#<<`\n    def <<(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#>>`\n    def >>(other : NumberLiteral) : NumberLiteral\n    end\n\n    # Same as `Number#+`\n    def + : NumberLiteral\n    end\n\n    # Same as `Number#-`\n    def - : NumberLiteral\n    end\n\n    # Same as `Number#~`\n    def ~ : NumberLiteral\n    end\n\n    # The type of the literal: `:i32`, `:u16`, `:f32`, `:f64`, etc.\n    def kind : SymbolLiteral\n    end\n\n    # Returns the value of this number without a type suffix.\n    def to_number : MacroId\n    end\n  end\n\n  # A character literal.\n  class CharLiteral < ASTNode\n    # Returns a `MacroId` for this character's contents.\n    def id : MacroId\n    end\n\n    # Similar to `Char#ord`.\n    def ord : NumberLiteral\n    end\n  end\n\n  # A string literal.\n  class StringLiteral < ASTNode\n    def_string_methods StringLiteral\n\n    # Similar to `String#>`\n    def >(other : StringLiteral | MacroId) : BoolLiteral\n    end\n\n    # Similar to `String#<`\n    def <(other : StringLiteral | MacroId) : BoolLiteral\n    end\n\n    # Similar to `String#*`.\n    def *(other : NumberLiteral) : StringLiteral\n    end\n  end\n\n  # An interpolated string like `\"Hello, #{name}!\"`.\n  class StringInterpolation < ASTNode\n    # Returns a list of expressions that comprise the interpolated string.\n    #\n    # These alternate between `StringLiteral` for the plaintext parts and\n    # `ASTNode`s of any type for the interpolated expressions.\n    def expressions : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # A symbol literal.\n  class SymbolLiteral < ASTNode\n    def_string_methods SymbolLiteral\n  end\n\n  # An array literal.\n  class ArrayLiteral < ASTNode\n    # Similar to `Enumerable#any?`\n    def any?(&) : BoolLiteral\n    end\n\n    # Similar to `Enumerable#all?`\n    def all?(&) : BoolLiteral\n    end\n\n    # Returns a `MacroId` with all of this array's elements joined\n    # by commas.\n    #\n    # If *trailing_string* is given, it will be appended to\n    # the result unless this array is empty. This lets you\n    # splat an array and optionally write a trailing comma\n    # if needed.\n    def splat(trailing_string : StringLiteral = nil) : MacroId\n    end\n\n    # Similar to `Array#clear`\n    def clear : ArrayLiteral\n    end\n\n    # Similar to `Array#empty?`\n    def empty? : BoolLiteral\n    end\n\n    # Similar to `Enumerable#find`\n    def find(&) : ASTNode | NilLiteral\n    end\n\n    # Similar to `Array#first`, but returns a `NilLiteral` if the array is empty.\n    def first : ASTNode | NilLiteral\n    end\n\n    # Similar to `Enumerable#includes?(obj)`.\n    def includes?(node : ASTNode) : BoolLiteral\n    end\n\n    # Similar to `Enumerable#join`\n    def join(separator) : StringLiteral\n    end\n\n    # Similar to `Array#last`, but returns a `NilLiteral` if the array is empty.\n    def last : ASTNode | NilLiteral\n    end\n\n    # Similar to `Array#size`\n    def size : NumberLiteral\n    end\n\n    # Similar to `Enumerable#map`\n    def map(&) : ArrayLiteral\n    end\n\n    # Similar to `Enumerable#map_with_index`\n    def map_with_index(&) : ArrayLiteral\n    end\n\n    # Similar to `Array#each`\n    def each(&) : NilLiteral\n    end\n\n    # Similar to `Enumerable#each_with_index`\n    def each_with_index(&) : NilLiteral\n    end\n\n    # Similar to `Enumerable#select`\n    def select(&) : ArrayLiteral\n    end\n\n    # Similar to `Enumerable#reject`\n    def reject(&) : ArrayLiteral\n    end\n\n    # Similar to `Enumerable#reduce`\n    def reduce(&) : ASTNode\n    end\n\n    # Similar to `Enumerable#reduce`\n    def reduce(memo : ASTNode, &) : ASTNode\n    end\n\n    # Similar to `Array#shuffle`\n    def shuffle : ArrayLiteral\n    end\n\n    # Similar to `Array#sort`\n    def sort : ArrayLiteral\n    end\n\n    # Similar to `Array#sort_by`\n    def sort_by(&) : ArrayLiteral\n    end\n\n    # Similar to `Array#uniq`\n    def uniq : ArrayLiteral\n    end\n\n    # Similar to `Array#[]?(Int)`.\n    def [](index : NumberLiteral) : ASTNode\n    end\n\n    # Similar to `Array#[]?(Range)`.\n    def [](index : RangeLiteral) : ArrayLiteral(ASTNode) | NilLiteral\n    end\n\n    # Similar to `Array#[]?(Int, Int)`.\n    def [](start : NumberLiteral, count : NumberLiteral) : ArrayLiteral(ASTNode) | NilLiteral\n    end\n\n    # Similar to `Array#[]=`.\n    def []=(index : NumberLiteral, value : ASTNode) : ASTNode\n    end\n\n    # Similar to `Array#unshift`.\n    def unshift(value : ASTNode) : ArrayLiteral\n    end\n\n    # Similar to `Array#push`.\n    def push(value : ASTNode) : ArrayLiteral\n    end\n\n    # Similar to `Array#<<`.\n    def <<(value : ASTNode) : ArrayLiteral\n    end\n\n    # Similar to `Array#+`.\n    def +(other : ArrayLiteral) : ArrayLiteral\n    end\n\n    # Similar to `Array#-`.\n    def -(other : ArrayLiteral) : ArrayLiteral\n    end\n\n    # Similar to `Array#*`\n    def *(other : NumberLiteral) : ArrayLiteral\n    end\n\n    # Returns the type specified at the end of the array literal, if any.\n    #\n    # This refers to the part after brackets in `[] of String`.\n    def of : ASTNode | Nop\n    end\n\n    # Returns the type that receives the items of the array.\n    #\n    # This refers to the part before brackets in `MyArray{1, 2, 3}`\n    def type : Path | Nop\n    end\n  end\n\n  # A hash literal.\n  class HashLiteral < ASTNode\n    # Similar to `Hash#clear`\n    def clear : HashLiteral\n    end\n\n    # Similar to `Hash#each`\n    def each(&) : NilLiteral\n    end\n\n    # Similar to `Hash#empty?`\n    def empty? : BoolLiteral\n    end\n\n    # Similar to `Hash#keys`\n    def keys : ArrayLiteral\n    end\n\n    # Similar to `Hash#size`\n    def size : NumberLiteral\n    end\n\n    # Similar to `Hash#to_a`\n    def to_a : ArrayLiteral(TupleLiteral)\n    end\n\n    # Similar to `Hash#values`\n    def values : ArrayLiteral\n    end\n\n    # Similar to `Hash#map`\n    def map(&) : ArrayLiteral\n    end\n\n    # Similar to `Hash#select`\n    def select(&) : HashLiteral\n    end\n\n    # Returns a new `HashLiteral` with only the provided *keys*.\n    def select(*keys : ASTNode) : HashLiteral\n    end\n\n    # Similar to `Hash#reject`\n    def reject(&) : HashLiteral\n    end\n\n    # Returns a new `HashLiteral` without the provided *keys*.\n    def reject(*keys : ASTNode) : HashLiteral\n    end\n\n    # Similar to `Hash#[]?`\n    def [](key : ASTNode) : ASTNode\n    end\n\n    # Similar to `Hash#[]=`\n    def []=(key : ASTNode, value : ASTNode) : ASTNode\n    end\n\n    # Similar to `Hash#has_hey?`\n    def has_key?(key : ASTNode) : BoolLiteral\n    end\n\n    # Returns the type specified at the end of the Hash literal, if any.\n    #\n    # This refers to the key type after brackets in `{} of String => Int32`.\n    def of_key : ASTNode | Nop\n    end\n\n    # Returns the type specified at the end of the Hash literal, if any.\n    #\n    # This refers to the value type after brackets in `{} of String => Int32`.\n    def of_value : ASTNode | Nop\n    end\n\n    # Returns the type that receives the items of the array.\n    #\n    # This refers to the part before brackets in `MyHash{'a' => 1, 'b' => 2}`\n    def type : Path | Nop\n    end\n\n    # Returns a `MacroId` with all of this hash elements joined\n    # by commas.\n    #\n    # If *trailing_string* is given, it will be appended to\n    # the result unless this hash is empty. This lets you\n    # splat a hash and optionally write a trailing comma\n    # if needed.\n    def double_splat(trailing_string : StringLiteral = nil) : MacroId\n    end\n  end\n\n  # A named tuple literal.\n  class NamedTupleLiteral < ASTNode\n    # Similar to `NamedTuple#each`\n    def each(&) : NilLiteral\n    end\n\n    # Similar to `NamedTuple#each_with_index`\n    def each_with_index(&) : NilLiteral\n    end\n\n    # Similar to `NamedTuple#empty?`\n    def empty? : BoolLiteral\n    end\n\n    # Similar to `NamedTuple#keys`\n    def keys : ArrayLiteral\n    end\n\n    # Similar to `NamedTuple#size`\n    def size : NumberLiteral\n    end\n\n    # Similar to `NamedTuple#to_a`\n    def to_a : ArrayLiteral(TupleLiteral)\n    end\n\n    # Similar to `NamedTuple#values`\n    def values : ArrayLiteral\n    end\n\n    # Similar to `NamedTuple#map`\n    def map(&) : ArrayLiteral\n    end\n\n    # Similar to `Hash#select`\n    def select(&) : NamedTupleLiteral\n    end\n\n    # Returns a new `NamedTupleLiteral` with only the provided *keys*.\n    def select(*keys : SymbolLiteral | StringLiteral | MacroId) : NamedTupleLiteral\n    end\n\n    # Similar to `Hash#reject`\n    def reject(&) : NamedTupleLiteral\n    end\n\n    # Returns a new `NamedTupleLiteral` without the provided *keys*.\n    def reject(*keys : SymbolLiteral | StringLiteral | MacroId) : NamedTupleLiteral\n    end\n\n    # Similar to `HashLiteral#double_splat`\n    def double_splat(trailing_string : StringLiteral = nil) : MacroId\n    end\n\n    # Similar to `NamedTuple#[]` but returns `NilLiteral` if *key* is undefined.\n    def [](key : SymbolLiteral | StringLiteral | MacroId) : ASTNode\n    end\n\n    # Adds or replaces a key.\n    def []=(key : SymbolLiteral | StringLiteral | MacroId, value : ASTNode) : ASTNode\n    end\n\n    # Similar to `NamedTuple#has_key?`\n    def has_key?(key : SymbolLiteral | StringLiteral | MacroId) : ASTNode\n    end\n  end\n\n  # A range literal.\n  class RangeLiteral < ASTNode\n    # Similar to `Range#begin`\n    def begin : ASTNode\n    end\n\n    # Similar to `Range#each`\n    def each(&) : NilLiteral\n    end\n\n    # Similar to `Range#end`\n    def end : ASTNode\n    end\n\n    # Similar to `Range#excludes_end?`\n    def excludes_end? : ASTNode\n    end\n\n    # Similar to `Enumerable#map` for a `Range`.\n    # Only works on ranges of `NumberLiteral`s considered as integers.\n    def map(&) : ArrayLiteral\n    end\n\n    # Similar to `Enumerable#to_a` for a `Range`.\n    # Only works on ranges of `NumberLiteral`s considered as integers.\n    def to_a : ArrayLiteral\n    end\n  end\n\n  # A regular expression literal.\n  class RegexLiteral < ASTNode\n    # Similar to `Regex#source`.\n    def source : StringLiteral | StringInterpolation\n    end\n\n    # Similar to `Regex#options`,\n    # but returns an array of symbols such as `[:i, :m, :x]`\n    def options : ArrayLiteral(SymbolLiteral)\n    end\n  end\n\n  # A tuple literal.\n  #\n  # Its macro methods are nearly the same as `ArrayLiteral`.\n  class TupleLiteral < ASTNode\n    # Similar to `Enumerable#any?`\n    def any?(&) : BoolLiteral\n    end\n\n    # Similar to `Enumerable#all?`\n    def all?(&) : BoolLiteral\n    end\n\n    # Returns a `MacroId` with all of this tuple's elements joined\n    # by commas.\n    #\n    # If *trailing_string* is given, it will be appended to\n    # the result unless this tuple is empty. This lets you\n    # splat a tuple and optionally write a trailing comma\n    # if needed.\n    def splat(trailing_string : StringLiteral = nil) : MacroId\n    end\n\n    # Similar to `Tuple#empty?`\n    def empty? : BoolLiteral\n    end\n\n    # Similar to `Enumerable#find`\n    def find(&) : ASTNode | NilLiteral\n    end\n\n    # Similar to `Tuple#first`, but returns a `NilLiteral` if the tuple is empty.\n    def first : ASTNode | NilLiteral\n    end\n\n    # Similar to `Enumerable#includes?(obj)`.\n    def includes?(node : ASTNode) : BoolLiteral\n    end\n\n    # Similar to `Enumerable#join`\n    def join(separator) : StringLiteral\n    end\n\n    # Similar to `Tuple#last`, but returns a `NilLiteral` if the tuple is empty.\n    def last : ASTNode | NilLiteral\n    end\n\n    # Similar to `Tuple#size`\n    def size : NumberLiteral\n    end\n\n    # Similar to `Enumerable#map`\n    def map(&) : TupleLiteral\n    end\n\n    # Similar to `Enumerable#map_with_index`\n    def map_with_index(&) : TupleLiteral\n    end\n\n    # Similar to `Tuple#each`\n    def each(&) : NilLiteral\n    end\n\n    # Similar to `Enumerable#each_with_index`\n    def each_with_index(&) : NilLiteral\n    end\n\n    # Similar to `Enumerable#select`\n    def select(&) : TupleLiteral\n    end\n\n    # Similar to `Enumerable#reject`\n    def reject(&) : TupleLiteral\n    end\n\n    # Similar to `Enumerable#reduce`\n    def reduce(&) : ASTNode\n    end\n\n    # Similar to `Enumerable#reduce`\n    def reduce(memo : ASTNode, &) : ASTNode\n    end\n\n    # Similar to `Array#shuffle`\n    def shuffle : TupleLiteral\n    end\n\n    # Similar to `Array#sort`\n    def sort : TupleLiteral\n    end\n\n    # Similar to `Array#sort_by`\n    def sort_by(&) : TupleLiteral\n    end\n\n    # Similar to `Array#uniq`\n    def uniq : TupleLiteral\n    end\n\n    # Similar to `Tuple#[]?(Int)`.\n    def [](index : NumberLiteral) : ASTNode\n    end\n\n    # Similar to `Tuple#[]?(Range)`.\n    def [](index : RangeLiteral) : TupleLiteral | NilLiteral\n    end\n\n    # Similar to `Array#[]?(Int, Int)`, but returns another `TupleLiteral`\n    # instead of an `ArrayLiteral`.\n    def [](start : NumberLiteral, count : NumberLiteral) : TupleLiteral | NilLiteral\n    end\n\n    # Similar to `Array#[]=`.\n    def []=(index : NumberLiteral, value : ASTNode) : ASTNode\n    end\n\n    # Similar to `Array#unshift`.\n    def unshift(value : ASTNode) : TupleLiteral\n    end\n\n    # Similar to `Array#push`.\n    def push(value : ASTNode) : TupleLiteral\n    end\n\n    # Similar to `Array#<<`.\n    def <<(value : ASTNode) : TupleLiteral\n    end\n\n    # Similar to `Tuple#+`.\n    def +(other : TupleLiteral) : TupleLiteral\n    end\n\n    # Similar to `Array#-`.\n    def -(other : TupleLiteral) : TupleLiteral\n    end\n\n    # Similar to `Tuple#*`\n    def *(other : NumberLiteral) : TupleLiteral\n    end\n  end\n\n  # A fictitious node representing a variable or instance\n  # variable, together with type information.\n  class MetaVar < ASTNode\n    # Returns the name of this variable.\n    def name : MacroId\n    end\n\n    # Returns the type of this variable, if known, or `nil`.\n    def type : TypeNode | NilLiteral\n    end\n\n    # Returns the default value of this variable.\n    # Note that if the variable doesn't have a default value,\n    # or the default value is `nil`, a `NilLiteral` will be\n    # returned. To distinguish between these cases, use\n    # `has_default_value?`.\n    def default_value : ASTNode\n    end\n\n    # Returns whether this variable has a default value\n    # (which can in turn be `nil`).\n    def has_default_value? : BoolLiteral\n    end\n\n    # Returns the last `Annotation` with the given `type`\n    # attached to this variable or `NilLiteral` if there are none.\n    def annotation(type : TypeNode) : Annotation | NilLiteral\n    end\n\n    # Returns an array of annotations with the given `type`\n    # attached to this variable, or an empty `ArrayLiteral` if there are none.\n    def annotations(type : TypeNode) : ArrayLiteral(Annotation)\n    end\n\n    # Returns an array of all annotations attached to this\n    # variable, or an empty `ArrayLiteral` if there are none.\n    def annotations : ArrayLiteral(Annotation)\n    end\n  end\n\n  # An annotation on top of a type or variable.\n  class Annotation < ASTNode\n    # Returns the name of this annotation.\n    def name : Path\n    end\n\n    # Returns the value of a positional argument,\n    # or NilLiteral if out of bounds.\n    def [](index : NumberLiteral) : ASTNode\n    end\n\n    # Returns the value of a named argument,\n    # or NilLiteral if the named argument isn't\n    # used on `self`.\n    def [](name : SymbolLiteral | StringLiteral | MacroId) : ASTNode\n    end\n\n    # Returns a `TupleLiteral` representing the positional arguments on `self`.\n    def args : TupleLiteral\n    end\n\n    # Returns a `NamedTupleLiteral` representing the named arguments on `self`.\n    def named_args : NamedTupleLiteral\n    end\n  end\n\n  # A local variable or block argument.\n  class Var < ASTNode\n    # Returns this var's name as a `MacroId`.\n    def id : MacroId\n    end\n  end\n\n  # A code block.\n  class Block < ASTNode\n    # Returns the block's body, if any.\n    def body : ASTNode\n    end\n\n    # Returns the blocks arguments.\n    def args : ArrayLiteral(MacroId)\n    end\n\n    # Returns the index of the argument with a *splat, if any.\n    def splat_index : NumberLiteral | NilLiteral\n    end\n  end\n\n  # A group of expressions.\n  class Expressions < ASTNode\n    # Returns the list of expressions in this node\n    def expressions : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # A method call.\n  class Call < ASTNode\n    # Returns this call's name as a `MacroId`.\n    def id : MacroId\n    end\n\n    # Returns the method name of this call.\n    def name : MacroId\n    end\n\n    # Returns this call's receiver, if any.\n    def receiver : ASTNode | Nop\n    end\n\n    # Returns `true` if this call refers to a global method (starts with `::`).\n    def global? : BoolLiteral\n    end\n\n    # Returns this call's arguments.\n    def args : ArrayLiteral\n    end\n\n    # Returns this call's named arguments.\n    def named_args : ArrayLiteral(NamedArgument)\n    end\n\n    # Returns this call's block, if any.\n    def block : Block | Nop\n    end\n\n    # Returns this call's block argument, if any\n    def block_arg : ASTNode | Nop\n    end\n  end\n\n  # A call's named argument.\n  class NamedArgument < ASTNode\n    # Returns this named argument name.\n    def name : MacroId\n    end\n\n    # Returns this named argument value.\n    def value : ASTNode\n    end\n  end\n\n  # An if expression.\n  class If < ASTNode\n    # Returns this if's condition.\n    def cond : ASTNode\n    end\n\n    # Returns this if's `then` clause's body.\n    def then : ASTNode\n    end\n\n    # Returns this if's `else` clause's body.\n    def else : ASTNode\n    end\n  end\n\n  # class Unless < ASTNode\n  # end\n\n  # Assign expression.\n  class Assign < ASTNode\n    # Returns the target assigned to.\n    def target : ASTNode\n    end\n\n    # Returns the value that is being assigned.\n    def value : ASTNode\n    end\n  end\n\n  # Multiple assign expression.\n  class MultiAssign < ASTNode\n    # Returns the targets assigned to.\n    def targets : ArrayLiteral(ASTNode)\n    end\n\n    # Returns the values that are being assigned.\n    def values : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # An instance variable.\n  class InstanceVar < ASTNode\n    # Returns the name of this variable.\n    def name : MacroId\n    end\n  end\n\n  # Access to an instance variable, e.g. `obj.@var`.\n  class ReadInstanceVar < ASTNode\n    # Returns the object whose variable is being accessed.\n    def obj : ASTNode\n    end\n\n    # Returns the name of the instance variable being accessed.\n    def name : MacroId\n    end\n  end\n\n  # A class variable.\n  class ClassVar < ASTNode\n    # Returns the name of this variable.\n    def name : MacroId\n    end\n  end\n\n  # A global variable.\n  class Global < ASTNode\n    # Returns the name of this variable.\n    def name : MacroId\n    end\n  end\n\n  # A binary expression like `And` and `Or`.\n  abstract class BinaryOp < ASTNode\n    # Returns the left hand side of this node.\n    def left : ASTNode\n    end\n\n    # Returns the left hand side of this node.\n    def right : ASTNode\n    end\n  end\n\n  # An `&&` (and) expression\n  class And < BinaryOp\n  end\n\n  # An `||` (or) expression\n  class Or < BinaryOp\n  end\n\n  # A def argument.\n  class Arg < ASTNode\n    # Returns the last `Annotation` with the given `type`\n    # attached to this arg or `NilLiteral` if there are none.\n    def annotation(type : TypeNode) : Annotation | NilLiteral\n    end\n\n    # Returns an array of annotations with the given `type`\n    # attached to this arg, or an empty `ArrayLiteral` if there are none.\n    def annotations(type : TypeNode) : ArrayLiteral(Annotation)\n    end\n\n    # Returns an array of all annotations attached to this\n    # arg, or an empty `ArrayLiteral` if there are none.\n    def annotations : ArrayLiteral(Annotation)\n    end\n\n    # Returns the external name of this argument.\n    #\n    # For example, for `def write(to file)` returns `to`.\n    def name : MacroId\n    end\n\n    # Returns the internal name of this argument.\n    #\n    # For example, for `def write(to file)` returns `file`.\n    def internal_name : MacroId\n    end\n\n    # Returns the default value of this argument, if any.\n    def default_value : ASTNode | Nop\n    end\n\n    # Returns the type restriction of this argument, if any.\n    def restriction : ASTNode | Nop\n    end\n  end\n\n  # The type of a proc or block argument, like `String -> Int32`.\n  class ProcNotation < ASTNode\n    # Returns the argument types, or an empty list if no arguments.\n    def inputs : ArrayLiteral(ASTNode)\n    end\n\n    # Returns the output type, or nil if there is no return type.\n    def output : ASTNode | NilLiteral\n    end\n\n    # Resolves this proc notation to a `TypeNode` if it denotes a type,\n    # or otherwise gives a compile-time error.\n    def resolve : ASTNode\n    end\n\n    # Resolves this proc notation to a `TypeNode` if it denotes a type,\n    # or otherwise returns a `NilLiteral`.\n    def resolve? : ASTNode | NilLiteral\n    end\n  end\n\n  # A method definition.\n  class Def < ASTNode\n    # Returns the name of this method.\n    def name : MacroId\n    end\n\n    # Returns the arguments of this method.\n    def args : ArrayLiteral(Arg)\n    end\n\n    # Returns the index of the argument with a *splat, if any.\n    def splat_index : NumberLiteral | NilLiteral\n    end\n\n    # Returns the double splat argument, if any.\n    def double_splat : Arg | Nop\n    end\n\n    # Returns the block argument, if any.\n    def block_arg : Arg | Nop\n    end\n\n    # Returns `true` if this method can be called with a block, `false` otherwise.\n    def accepts_block? : BoolLiteral\n    end\n\n    # Returns the return type of the method, if specified.\n    def return_type : ASTNode | Nop\n    end\n\n    # Returns the free variables of this method, or an empty `ArrayLiteral` if\n    # there are none.\n    def free_vars : ArrayLiteral(MacroId)\n    end\n\n    # Returns the body of this method.\n    def body : ASTNode\n    end\n\n    # Returns the receiver (for example `self`) of this method definition,\n    # or `Nop` if not specified.\n    def receiver : ASTNode | Nop\n    end\n\n    # Returns `true` is this method is declared as abstract, `false` otherwise.\n    def abstract? : BoolLiteral\n    end\n\n    # Returns the visibility of this def: `:public`, `:protected` or `:private`.\n    def visibility : SymbolLiteral\n    end\n\n    # Returns the last `Annotation` with the given `type`\n    # attached to this method or `NilLiteral` if there are none.\n    def annotation(type : TypeNode) : Annotation | NilLiteral\n    end\n\n    # Returns an array of annotations with the given `type`\n    # attached to this method, or an empty `ArrayLiteral` if there are none.\n    def annotations(type : TypeNode) : ArrayLiteral(Annotation)\n    end\n\n    # Returns an array of all annotations attached to this\n    # method, or an empty `ArrayLiteral` if there are none.\n    def annotations : ArrayLiteral(Annotation)\n    end\n  end\n\n  # A fictitious node representing the body of a `Def` marked with\n  # `@[Primitive]`.\n  class Primitive < ASTNode\n    # Returns the name of the primitive.\n    #\n    # This is identical to the argument to the associated `@[Primitive]`\n    # annotation.\n    #\n    # ```\n    # module Foo\n    #   @[Primitive(:abc)]\n    #   def foo\n    #   end\n    # end\n    #\n    # {{ Foo.methods.first.body.name }} # => :abc\n    # ```\n    def name : SymbolLiteral\n    end\n  end\n\n  # A macro definition.\n  class Macro < ASTNode\n    # Returns the name of this macro.\n    def name : MacroId\n    end\n\n    # Returns the arguments of this macro.\n    def args : ArrayLiteral(Arg)\n    end\n\n    # Returns the index of the argument with a *splat, if any.\n    def splat_index : NumberLiteral | NilLiteral\n    end\n\n    # Returns the double splat argument, if any.\n    def double_splat : Arg | Nop\n    end\n\n    # Returns the block argument, if any.\n    def block_arg : Arg | Nop\n    end\n\n    # Returns the body of this macro.\n    def body : ASTNode\n    end\n\n    # Returns the visibility of this macro: `:public`, `:protected` or `:private`.\n    def visibility : SymbolLiteral\n    end\n  end\n\n  # An unary expression\n  abstract class UnaryExpression < ASTNode\n    # Returns the expression that this unary operation is applied to.\n    def exp : ASTNode\n    end\n  end\n\n  # An unary `not` (`!`).\n  class Not < UnaryExpression\n  end\n\n  # A `pointerof` expression.\n  class PointerOf < UnaryExpression\n  end\n\n  # A `sizeof` expression.\n  #\n  # Every expression `node` is equivalent to:\n  #\n  # ```\n  # sizeof({{ node.exp }})\n  # ```\n  class SizeOf < UnaryExpression\n  end\n\n  # An `instance_sizeof` expression.\n  #\n  # Every expression `node` is equivalent to:\n  #\n  # ```\n  # instance_sizeof({{ node.exp }})\n  # ```\n  class InstanceSizeOf < UnaryExpression\n  end\n\n  # A `alignof` expression.\n  #\n  # Every expression `node` is equivalent to:\n  #\n  # ```\n  # alignof({{ node.exp }})\n  # ```\n  class AlignOf < UnaryExpression\n  end\n\n  # An `instance_alignof` expression.\n  #\n  # Every expression `node` is equivalent to:\n  #\n  # ```\n  # instance_alignof({{ node.exp }})\n  # ```\n  class InstanceAlignOf < UnaryExpression\n  end\n\n  # An `out` expression.\n  class Out < UnaryExpression\n  end\n\n  # A splat expression: `*exp`.\n  class Splat < UnaryExpression\n  end\n\n  # A double splat expression: `**exp`.\n  class DoubleSplat < UnaryExpression\n  end\n\n  # An `offsetof` expression.\n  class OffsetOf < ASTNode\n    # Returns the type that has been used in this `offsetof` expression.\n    def type : ASTNode\n    end\n\n    # Returns the offset argument used in this `offsetof` expression.\n    def offset : ASTNode\n    end\n  end\n\n  # A visibility modifier\n  class VisibilityModifier < ASTNode\n    # Returns the visibility of this modifier: `:public`, `:protected` or `:private`.\n    def visibility : SymbolLiteral\n    end\n\n    # Returns the expression that the modifier is applied to.\n    def exp : ASTNode\n    end\n  end\n\n  # An `.is_a?` or `.nil?` call.\n  class IsA < ASTNode\n    # Returns this call's receiver.\n    def receiver : ASTNode\n    end\n\n    # Returns this call's argument.\n    def arg : ASTNode\n    end\n  end\n\n  # A `.responds_to?` call.\n  class RespondsTo < ASTNode\n    # Returns this call's receiver.\n    def receiver : ASTNode\n    end\n\n    # Returns the method name that is being checked for.\n    def name : StringLiteral\n    end\n  end\n\n  # A `require` statement.\n  class Require < ASTNode\n    # Returns the argument of the `require`.\n    def path : StringLiteral\n    end\n  end\n\n  # A `when` or `in` inside a `case` or `select`.\n  class When < ASTNode\n    # Returns the conditions of this `when`.\n    def conds : ArrayLiteral\n    end\n\n    # Returns the body of this `when`.\n    def body : ASTNode\n    end\n\n    # Returns `true` if this is an `in`, or `false` if this is a `when`.\n    def exhaustive? : BoolLiteral\n    end\n  end\n\n  # A `case` expression.\n  class Case < ASTNode\n    # Returns the condition (target) of this `case`.\n    def cond : ASTNode\n    end\n\n    # Returns the `when`s of this `case`.\n    def whens : ArrayLiteral(When)\n    end\n\n    # Returns the `else` of this `case`.\n    def else : ASTNode\n    end\n\n    # Returns whether this `case` is exhaustive (`case ... in`).\n    def exhaustive? : BoolLiteral\n    end\n  end\n\n  # A `select` expression.\n  #\n  # Every expression `node` is equivalent to:\n  #\n  # ```\n  # select\n  # {% for when_clause in node.whens %}\n  #   {{ when_clause }}\n  # {% end %}\n  # {% else_clause = node.else %}\n  # {% unless else_clause.is_a?(Nop) %}\n  #   else\n  #     {{ else_clause }}\n  # {% end %}\n  # end\n  # ```\n  class Select < ASTNode\n    # Returns the `when`s of this `select`.\n    def whens : ArrayLiteral(When)\n    end\n\n    # Returns the `else` of this `select`.\n    def else : ASTNode\n    end\n  end\n\n  # Node that represents an implicit object in:\n  #\n  #     case foo\n  #     when .bar? # this is a call with an implicit object\n  #     end\n  class ImplicitObj < ASTNode\n  end\n\n  # A Path to a constant or type, like `Foo` or `Foo::Bar::Baz`.\n  class Path < ASTNode\n    # Returns an array with each separate part of this path.\n    def names : ArrayLiteral(MacroId)\n    end\n\n    # Returns `true` if this is a global path (starts with `::`)\n    def global? : BoolLiteral\n    end\n\n    # Returns `true` if this is a global path (starts with `::`)\n    @[Deprecated(\"Use `#global?` instead\")]\n    def global : BoolLiteral\n    end\n\n    # Resolves this path to a `TypeNode` if it denotes a type, to\n    # the value of a constant if it denotes a constant, or otherwise\n    # gives a compile-time error.\n    def resolve : ASTNode\n    end\n\n    # Resolves this path to a `TypeNode` if it denotes a type, to\n    # the value of a constant if it denotes a constant, or otherwise\n    # returns a `NilLiteral`.\n    def resolve? : ASTNode | NilLiteral\n    end\n\n    # Returns this path inside an array literal.\n    # This method exists so you can call `types` on the type of a type\n    # declaration and get all types, whether it's a Generic, Path or Union.\n    def types : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # A class definition.\n  #\n  # Every class definition `node` is equivalent to:\n  #\n  # ```\n  # {% begin %}\n  #   {% \"abstract\".id if node.abstract? %} {{ node.kind }} {{ node.name }} {% if superclass = node.superclass %}< {{ superclass }}{% end %}\n  #     {{ node.body }}\n  #   end\n  # {% end %}\n  # ```\n  class ClassDef < ASTNode\n    # Returns whether this node defines an abstract class or struct.\n    def abstract? : BoolLiteral\n    end\n\n    # Returns the keyword used to define this type.\n    #\n    # For `ClassDef` this is either `class` or `struct`.\n    def kind : MacroId\n    end\n\n    # Returns the name of this type definition.\n    #\n    # If this node defines a generic type, and *generic_args* is true, returns a\n    # `Generic` whose type arguments are `MacroId`s, possibly with a `Splat` at\n    # the splat index. Otherwise, this method returns a `Path`.\n    def name(*, generic_args : BoolLiteral = true) : Path | Generic\n    end\n\n    # Returns the superclass of this type definition, or a `Nop` if one isn't\n    # specified.\n    def superclass : ASTNode\n    end\n\n    # Returns the body of this type definition.\n    def body : ASTNode\n    end\n\n    # Returns an array of `MacroId`s of this type definition's generic type\n    # parameters.\n    #\n    # On a non-generic type definition, returns an empty array.\n    def type_vars : ArrayLiteral\n    end\n\n    # Returns the splat index of this type definition's generic type parameters.\n    #\n    # Returns `nil` if this type definition isn't generic or if there isn't a\n    # splat parameter.\n    def splat_index : NumberLiteral | NilLiteral\n    end\n\n    # Returns `true` if this node defines a struct, `false` if this node defines\n    # a class.\n    def struct? : BoolLiteral\n    end\n  end\n\n  # A module definition.\n  #\n  # Every module definition `node` is equivalent to:\n  #\n  # ```\n  # {% begin %}\n  #   {{ node.kind }} {{ node.name }}\n  #     {{ node.body }}\n  #   end\n  # {% end %}\n  # ```\n  class ModuleDef < ASTNode\n    # Returns the keyword used to define this type.\n    #\n    # For `ModuleDef` this is always `module`.\n    def kind : MacroId\n    end\n\n    # Returns the name of this type definition.\n    #\n    # If this node defines a generic type, and *generic_args* is true, returns a\n    # `Generic` whose type arguments are `MacroId`s, possibly with a `Splat` at\n    # the splat index. Otherwise, this method returns a `Path`.\n    def name(*, generic_args : BoolLiteral = true) : Path | Generic\n    end\n\n    # Returns the body of this type definition.\n    def body : ASTNode\n    end\n\n    # Returns an array of `MacroId`s of this type definition's generic type\n    # parameters.\n    #\n    # On a non-generic type definition, returns an empty array.\n    def type_vars : ArrayLiteral\n    end\n\n    # Returns the splat index of this type definition's generic type parameters.\n    #\n    # Returns `nil` if this type definition isn't generic or if there isn't a\n    # splat parameter.\n    def splat_index : NumberLiteral | NilLiteral\n    end\n  end\n\n  # An enum definition.\n  #\n  # ```\n  # {% begin %}\n  #   {{ node.kind }} {{ node.name }} {% if base_type = node.base_type %}: {{ base_type }}{% end %}\n  #     {{ node.body }}\n  #   end\n  # {% end %}\n  # ```\n  class EnumDef < ASTNode\n    # Returns the keyword used to define this type.\n    #\n    # For `EnumDef` this is always `enum`.\n    def kind : MacroId\n    end\n\n    # Returns the name of this type definition.\n    #\n    # *generic_args* has no effect. It exists solely to match the interface of\n    # other related AST nodes.\n    def name(*, generic_args : BoolLiteral = true) : Path\n    end\n\n    # Returns the base type of this enum definition, or a `Nop` if one isn't\n    # specified.\n    def base_type : ASTNode\n    end\n\n    # Returns the body of this type definition.\n    def body : ASTNode\n    end\n  end\n\n  # An annotation definition.\n  #\n  # Every annotation definition `node` is equivalent to:\n  #\n  # ```\n  # {% begin %}\n  #   {{ node.kind }} {{ node.name }}\n  #     {{ node.body }}\n  #   end\n  # {% end %}\n  # ```\n  class AnnotationDef < ASTNode\n    # Returns the keyword used to define this type.\n    #\n    # For `AnnotationDef` this is always `annotation`.\n    def kind : MacroId\n    end\n\n    # Returns the name of this type definition.\n    #\n    # *generic_args* has no effect. It exists solely to match the interface of\n    # other related AST nodes.\n    def name(*, generic_args : BoolLiteral = true) : Path\n    end\n\n    # Returns the body of this type definition.\n    #\n    # Currently this is always a `Nop`, because annotation definitions cannot\n    # contain anything at all.\n    def body : Nop\n    end\n  end\n\n  # A lib definition.\n  #\n  # Every lib definition `node` is equivalent to:\n  #\n  # ```\n  # {% begin %}\n  #   {{ node.kind }} {{ node.name }}\n  #     {{ node.body }}\n  #   end\n  # {% end %}\n  # ```\n  class LibDef < ASTNode\n    # Returns the keyword used to define this type.\n    #\n    # For `LibDef` this is always `lib`.\n    def kind : MacroId\n    end\n\n    # Returns the name of this type definition.\n    #\n    # *generic_args* has no effect. It exists solely to match the interface of\n    # other related AST nodes.\n    def name(*, generic_args : BoolLiteral = true) : Path\n    end\n\n    # Returns the body of this type definition.\n    def body : ASTNode\n    end\n  end\n\n  # A struct or union definition inside a lib.\n  #\n  # Every type definition `node` is equivalent to:\n  #\n  # ```\n  # {% begin %}\n  #   {{ node.kind }} {{ node.name }}\n  #     {{ node.body }}\n  #   end\n  # {% end %}\n  # ```\n  class CStructOrUnionDef < ASTNode\n    # Returns whether this node defines a C union.\n    def union? : BoolLiteral\n    end\n\n    # Returns the keyword used to define this type.\n    #\n    # For `CStructOrUnionDef` this is either `struct` or `union`.\n    def kind : MacroId\n    end\n\n    # Returns the name of this type definition.\n    #\n    # *generic_args* has no effect. It exists solely to match the interface of\n    # other related AST nodes.\n    def name(*, generic_args : BoolLiteral = true) : Path\n    end\n\n    # Returns the body of this type definition.\n    def body : ASTNode\n    end\n  end\n\n  # A function declaration inside a lib, or a top-level C function definition.\n  #\n  # Every function `node` is equivalent to:\n  #\n  # ```\n  # fun {{ node.name }} {% if real_name = node.real_name %}= {{ real_name }}{% end %}(\n  #   {% for arg in node.args %} {{ arg }}, {% end %}\n  #   {% if node.variadic? %} ... {% end %}\n  # ) {% if return_type = node.return_type %}: {{ return_type }}{% end %}\n  # {% if node.has_body? %}\n  #   {{ body }}\n  # end\n  # {% end %}\n  # ```\n  class FunDef < ASTNode\n    # Returns the name of the function in Crystal.\n    def name : MacroId\n    end\n\n    # Returns the real C name of the function, if any.\n    def real_name : StringLiteral | Nop\n    end\n\n    # Returns the parameters of the function.\n    #\n    # This does not include the variadic parameter.\n    def args : ArrayLiteral(Arg)\n    end\n\n    # Returns whether the function is variadic.\n    def variadic? : BoolLiteral\n    end\n\n    # Returns the return type of the function, if specified.\n    def return_type : ASTNode | Nop\n    end\n\n    # Returns the body of the function, if any.\n    #\n    # Both top-level funs and lib funs may return a `Nop`. Instead, `#has_body?`\n    # can be used to distinguish between the two.\n    #\n    # ```\n    # macro body_class(x)\n    #   {{ (x.is_a?(LibDef) ? x.body : x).body.class_name }}\n    # end\n    #\n    # body_class(lib MyLib\n    #   fun foo\n    # end) # => \"Nop\"\n    #\n    # body_class(fun foo\n    # end) # => \"Nop\"\n    # ```\n    def body : ASTNode | Nop\n    end\n\n    # Returns whether this function has a body.\n    #\n    # Top-level funs have a body, whereas lib funs do not.\n    #\n    # ```\n    # macro has_body(x)\n    #   {{ (x.is_a?(LibDef) ? x.body : x).has_body? }}\n    # end\n    #\n    # has_body(lib MyLib\n    #   fun foo\n    # end) # => false\n    #\n    # has_body(fun foo\n    # end) # => true\n    # ```\n    def has_body? : BoolLiteral\n    end\n  end\n\n  # A typedef inside a lib.\n  #\n  # Every typedef `node` is equivalent to:\n  #\n  # ```\n  # type {{ node.name }} = {{ node.type }}\n  # ```\n  class TypeDef < ASTNode\n    # Returns the name of the typedef.\n    def name : Path\n    end\n\n    # Returns the name of the type this typedef is equivalent to.\n    def type : ASTNode\n    end\n  end\n\n  # An external variable declaration inside a lib.\n  #\n  # Every variable `node` is equivalent to:\n  #\n  # ```\n  # ${{ node.name }} {% if real_name = node.real_name %}= {{ real_name }}{% end %} : {{ node.type }}\n  # ```\n  class ExternalVar < ASTNode\n    # Returns the name of the variable in Crystal, without the preceding `$`.\n    def name : MacroId\n    end\n\n    # Returns the real C name of the variable, if any.\n    def real_name : StringLiteral | Nop\n    end\n\n    # Returns the name of the variable's type.\n    def type : ASTNode\n    end\n  end\n\n  # A `while` expression\n  class While < ASTNode\n    # Returns this while's condition.\n    def cond : ASTNode\n    end\n\n    # Returns this while's body.\n    def body : ASTNode\n    end\n  end\n\n  # class Until < ASTNode\n  # end\n\n  # A generic instantiation, like `Foo(T)` or `Foo::Bar::Baz(T)`\n  class Generic < ASTNode\n    # Returns the path to the generic.\n    def name : Path\n    end\n\n    # Returns the arguments (the type variables) of this instantiation.\n    def type_vars : ArrayLiteral(ASTNode)\n    end\n\n    # Returns the named arguments of this instantiation, if any.\n    def named_args : NamedTupleLiteral | NilLiteral\n    end\n\n    # Resolves this generic to a `TypeNode` if it denotes a type,\n    # or otherwise gives a compile-time error.\n    def resolve : ASTNode\n    end\n\n    # Resolves this path to a `TypeNode` if it denotes a type,\n    # or otherwise returns a `NilLiteral`.\n    def resolve? : ASTNode | NilLiteral\n    end\n\n    # Returns this generic inside an array literal.\n    # This method exists so you can call `types` on the type of a type\n    # declaration and get all types, whether it's a Generic, Path or Union.\n    def types : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # A type declaration like `x : Int32`\n  class TypeDeclaration < ASTNode\n    # Returns the variable part of the declaration.\n    def var : MacroId\n    end\n\n    # Returns the type part of the declaration.\n    def type : ASTNode\n    end\n\n    # The value assigned to the variable, if any.\n    def value : ASTNode | Nop\n    end\n  end\n\n  # An uninitialized variable declaration: `a = uninitialized Int32`\n  class UninitializedVar < ASTNode\n    # Returns the variable part of the declaration.\n    def var : MacroId\n    end\n\n    # Returns the type part of the declaration.\n    def type : ASTNode\n    end\n  end\n\n  # A `rescue` clause inside an exception handler.\n  class Rescue < ASTNode\n    # Returns this `rescue` clause's body.\n    def body : ASTNode\n    end\n\n    # Returns this `rescue` clause's exception types, if any.\n    def types : ArrayLiteral | NilLiteral\n    end\n\n    # Returns the variable name of the rescued exception, if any.\n    def name : MacroId | Nop\n    end\n  end\n\n  # A `begin ... end` expression with `rescue`, `else` and `ensure` clauses.\n  class ExceptionHandler < ASTNode\n    # Returns this exception handler's main body.\n    def body : ASTNode\n    end\n\n    # Returns this exception handler's `rescue` clauses, if any.\n    def rescues : ArrayLiteral(Rescue) | NilLiteral\n    end\n\n    # Returns this exception handler's `else` clause body, if any.\n    def else : ASTNode | Nop\n    end\n\n    # Returns this exception handler's `ensure` clause body, if any.\n    def ensure : ASTNode | Nop\n    end\n  end\n\n  # A proc method, written like:\n  # ```\n  # ->(arg : String) {\n  #   puts arg\n  # }\n  # ```\n  class ProcLiteral < ASTNode\n    # Returns the arguments of this proc.\n    def args : ArrayLiteral(Arg)\n    end\n\n    # Returns the body of this proc.\n    def body : ASTNode\n    end\n\n    # Returns the return type of this proc, if specified.\n    def return_type : ASTNode | Nop\n    end\n  end\n\n  # A proc pointer, like `->my_var.some_method(String)`\n  class ProcPointer < ASTNode\n    # Returns the types of the arguments of the proc.\n    def args : ArrayLiteral(ASTNode)\n    end\n\n    # Returns the receiver of the proc, or nil if the proc is not attached to an object.\n    def obj : ASTNode | NilLiteral\n    end\n\n    # Returns the name of the method this proc points to.\n    def name : MacroId\n    end\n\n    # Returns true if this proc pointer refers to a global method (starts with\n    # `::` and does not have a receiver).\n    def global? : BoolLiteral\n    end\n  end\n\n  # A type union, like `(Int32 | String)`.\n  class Union < ASTNode\n    # Resolves this union to a `TypeNode`. Gives a compile-time error\n    # if any type inside the union can't be resolved.\n    def resolve : ASTNode\n    end\n\n    # Resolves this union to a `TypeNode`. Returns a `NilLiteral`\n    # if any type inside the union can't be resolved.\n    def resolve? : ASTNode | NilLiteral\n    end\n\n    # Returns the types of this union.\n    def types : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # The `self` expression. May appear in code, such as in an instance method,\n  # and in type names.\n  class Self < ASTNode\n  end\n\n  # The base class of control expressions.\n  abstract class ControlExpression < ASTNode\n    # Returns the argument to this control expression, if any.\n    #\n    # If multiple arguments are present, they are wrapped inside a single\n    # `TupleLiteral`.\n    def exp : ASTNode | Nop\n    end\n  end\n\n  # A `return` expression.\n  class Return < ControlExpression\n  end\n\n  # A `break` expression.\n  class Break < ControlExpression\n  end\n\n  # A `next` expression.\n  class Next < ControlExpression\n  end\n\n  # A `yield` expression.\n  class Yield < ASTNode\n    # Returns the arguments to this `yield`.\n    def expressions : ArrayLiteral\n    end\n\n    # Returns the scope of this `yield`, if any.\n    #\n    # This refers to the part after `with` in a `with ... yield` expression.\n    def scope : ASTNode | Nop\n    end\n  end\n\n  # An `include` statement.\n  #\n  # Every statement `node` is equivalent to:\n  #\n  # ```\n  # include {{ node.name }}\n  # ```\n  class Include < ASTNode\n    # Returns the name of the type being included.\n    def name : ASTNode\n    end\n  end\n\n  # An `extend` statement.\n  #\n  # Every statement `node` is equivalent to:\n  #\n  # ```\n  # extend {{ node.name }}\n  # ```\n  class Extend < ASTNode\n    # Returns the name of the type being extended.\n    def name : ASTNode\n    end\n  end\n\n  # An `alias` statement.\n  #\n  # Every statement `node` is equivalent to:\n  #\n  # ```\n  # alias {{ node.name }} = {{ node.type }}\n  # ```\n  class Alias < ASTNode\n    # Returns the name of the alias.\n    def name : Path\n    end\n\n    # Returns the name of the type this alias is equivalent to.\n    def type : ASTNode\n    end\n  end\n\n  # A metaclass in a type expression: `T.class`\n  class Metaclass < ASTNode\n    # Returns the node representing the instance type of this metaclass.\n    def instance : ASTNode\n    end\n\n    # Resolves this metaclass to a `TypeNode` if it denotes a type,\n    # or otherwise gives a compile-time error.\n    def resolve : ASTNode\n    end\n\n    # Resolves this metaclass to a `TypeNode` if it denotes a type,\n    # or otherwise returns a `NilLiteral`.\n    def resolve? : ASTNode | NilLiteral\n    end\n  end\n\n  # A cast call: `obj.as(to)`\n  class Cast < ASTNode\n    # Returns the object part of the cast.\n    def obj : ASTNode\n    end\n\n    # Returns the target type of the cast.\n    def to : ASTNode\n    end\n  end\n\n  # A cast call: `obj.as?(to)`\n  class NilableCast < ASTNode\n    # Returns the object part of the cast.\n    def obj : ASTNode\n    end\n\n    # Returns the target type of the cast.\n    def to : ASTNode\n    end\n  end\n\n  # A `typeof` expression.\n  #\n  # Every expression *node* is equivalent to:\n  #\n  # ```\n  # typeof({{ node.args.splat }})\n  # ```\n  class TypeOf < ASTNode\n    # Returns the arguments to this `typeof`.\n    def args : ArrayLiteral(ASTNode)\n    end\n  end\n\n  # A macro expression.\n  #\n  # Every expression *node* is equivalent to:\n  #\n  # ```\n  # {% if node.output? %}\n  #   \\{{ {{ node.exp }} }}\n  # {% else %}\n  #   \\{% {{ node.exp }} %}\n  # {% end %}\n  # ```\n  class MacroExpression < ASTNode\n    # Returns the expression inside this node.\n    def exp : ASTNode\n    end\n\n    # Returns whether this node interpolates the expression's result.\n    def output? : BoolLiteral\n    end\n  end\n\n  # Free text that is part of a macro.\n  class MacroLiteral < ASTNode\n    # Returns the text of the literal.\n    def value : MacroId\n    end\n  end\n\n  # An `if`/`unless` inside a macro, e.g.\n  #\n  # ```\n  # {% if cond %}\n  #   puts \"Then\"\n  # {% else %}\n  #   puts \"Else\"\n  # {% end %}\n  #\n  # {% unless cond %}\n  #   puts \"Then\"\n  # {% else %}\n  #   puts \"Else\"\n  # {% end %}\n  # ```\n  class MacroIf < ASTNode\n    # The condition of the `if` clause.\n    def cond : ASTNode\n    end\n\n    # The `then` branch of the `if`.\n    def then : ASTNode\n    end\n\n    # The `else` branch of the `if`.\n    def else : ASTNode\n    end\n\n    # Returns `true` if this node represents an `unless` conditional, otherwise returns `false`.\n    def is_unless? : BoolLiteral\n    end\n  end\n\n  # A `for` loop inside a macro, e.g.\n  #\n  # ```\n  # {% for x in exp %}\n  #   puts {{x}}\n  # {% end %}\n  # ```\n  class MacroFor < ASTNode\n    # The variables declared after `for`.\n    def vars : ArrayLiteral(Var)\n    end\n\n    # The expression after `in`.\n    def exp : ASTNode\n    end\n\n    # The body of the `for` loop.\n    def body : ASTNode\n    end\n  end\n\n  # A macro fresh variable.\n  #\n  # Every variable `node` is equivalent to:\n  #\n  # ```\n  # {{ \"%#{name}\".id }}{% if expressions = node.expressions %}{{ \"{#{expressions.splat}}\".id }}{% end %}\n  # ```\n  class MacroVar < ASTNode\n    # Returns the name of the fresh variable.\n    def name : MacroId\n    end\n\n    # Returns the associated indices of the fresh variable.\n    def expressions : ArrayLiteral\n    end\n  end\n\n  # A verbatim expression.\n  #\n  # Every expression `node` is equivalent to:\n  #\n  # ```\n  # \\{% verbatim do %}\n  #   {{ node.exp }}\n  # \\{% end %}\n  # ```\n  class MacroVerbatim < UnaryExpression\n  end\n\n  # The `_` expression. May appear in code, such as an assignment target, and in\n  # type names.\n  class Underscore < ASTNode\n  end\n\n  # A pseudo constant used to provide information about source code location.\n  #\n  # Usually this node is resolved by the compiler. It appears unresolved when\n  # used as a default parameter value:\n  #\n  # ```\n  # # the `__FILE__` here is a `MagicConstant`\n  # def foo(file = __FILE__)\n  #   # the `__LINE__` here becomes a `NumberLiteral`\n  #   __LINE__\n  # end\n  # ```\n  class MagicConstant < ASTNode\n  end\n\n  # An inline assembly expression.\n  #\n  # Every assembly `node` is equivalent to:\n  #\n  # ```\n  # asm(\n  #   {{ node.text }} :\n  #   {{ node.outputs.splat }} :\n  #   {{ node.inputs.splat }} :\n  #   {{ node.clobbers.splat }} :\n  #   {% if node.volatile? %} \"volatile\", {% end %}\n  #   {% if node.alignstack? %} \"alignstack\", {% end %}\n  #   {% if node.intel? %} \"intel\", {% end %}\n  #   {% if node.can_throw? %} \"unwind\", {% end %}\n  # )\n  # ```\n  class Asm < ASTNode\n    # Returns the template string for this assembly expression.\n    def text : StringLiteral\n    end\n\n    # Returns an array of output operands for this assembly expression.\n    def outputs : ArrayLiteral(AsmOperand)\n    end\n\n    # Returns an array of input operands for this assembly expression.\n    def inputs : ArrayLiteral(AsmOperand)\n    end\n\n    # Returns an array of clobbered register names for this assembly expression.\n    def clobbers : ArrayLiteral(StringLiteral)\n    end\n\n    # Returns whether the assembly expression contains side effects that are\n    # not listed in `#outputs`, `#inputs`, and `#clobbers`.\n    def volatile? : BoolLiteral\n    end\n\n    # Returns whether the assembly expression requires stack alignment code.\n    def alignstack? : BoolLiteral\n    end\n\n    # Returns `true` if the template string uses the Intel syntax, `false` if it\n    # uses the AT&T syntax.\n    def intel? : BoolLiteral\n    end\n\n    # Returns whether the assembly expression might unwind the stack.\n    def can_throw? : BoolLiteral\n    end\n  end\n\n  # An output or input operand for an `Asm` node.\n  #\n  # Every operand `node` is equivalent to:\n  #\n  # ```\n  # {{ node.constraint }}({{ node.exp }})\n  # ```\n  class AsmOperand < ASTNode\n    # Returns the constraint string of this operand.\n    def constraint : StringLiteral\n    end\n\n    # Returns the associated output or input argument of this operand.\n    def exp : ASTNode\n    end\n  end\n\n  # A fictitious node representing an identifier like, `foo`, `Bar` or `something_else`.\n  #\n  # The parser doesn't create these nodes. Instead, you create them by invoking `id`\n  # on some nodes. For example, invoking `id` on a `StringLiteral` returns a `MacroId`\n  # for the string's content. Similarly, invoking ID on a `SymbolLiteral`, `Call`, `Var` and `Path`\n  # returns a MacroId for the node's content.\n  #\n  # This allows you to treat strings, symbols, variables and calls uniformly. For example:\n  #\n  # ```text\n  # macro getter(name)\n  #   def {{name.id}}\n  #     @{{name.id}}\n  #   end\n  # end\n  #\n  # getter unicorns\n  # getter :unicorns\n  # getter \"unicorns\"\n  # ```\n  #\n  # All of the above macro calls work because we invoked `id`, and the generated code\n  # looks like this:\n  #\n  # ```\n  # def unicorns\n  #   @unicorns\n  # end\n  # ```\n  #\n  # If we hadn't used `id`, the generated code would have been this:\n  #\n  # ```text\n  # def unicorns\n  #   @unicorns\n  # end\n  #\n  # def :unicorns\n  #   @:unicorns\n  # end\n  #\n  # def \"unicorns\"\n  #   @\"unicorns\"\n  # end\n  # ```\n  #\n  # The last two definitions are invalid and so will give a compile-time error.\n  class MacroId < ASTNode\n    def_string_methods MacroId\n\n    # Similar to `String#>`\n    def >(other : StringLiteral | MacroId) : BoolLiteral\n    end\n\n    # Similar to `String#<`\n    def <(other : StringLiteral | MacroId) : BoolLiteral\n    end\n  end\n\n  # Represents a type in the program, like `Int32` or `String`.\n  class TypeNode < ASTNode\n    # Returns `true` if `self` is abstract, otherwise `false`.\n    #\n    # ```\n    # module One; end\n    #\n    # abstract struct Two; end\n    #\n    # class Three; end\n    #\n    # abstract class Four; end\n    #\n    # {{One.abstract?}}   # => false\n    # {{Two.abstract?}}   # => true\n    # {{Three.abstract?}} # => false\n    # {{Four.abstract?}}  # => true\n    # ```\n    def abstract? : BoolLiteral\n    end\n\n    # Returns `true` if `self` is a union type, otherwise `false`.\n    #\n    # See also: `#union_types`.\n    #\n    # ```\n    # {{String.union?}}              # => false\n    # {{String?.union?}}             # => true\n    # {{Union(String, Bool).union?}} # => true\n    # ```\n    def union? : BoolLiteral\n    end\n\n    # Returns `true` if `nil` is an instance of `self`, otherwise `false`.\n    #\n    # ```\n    # {{String.nilable?}}                   # => false\n    # {{String?.nilable?}}                  # => true\n    # {{Union(String, Bool, Nil).nilable?}} # => true\n    # {{NoReturn.nilable?}}                 # => false\n    # {{Value.nilable?}}                    # => true\n    # ```\n    def nilable? : BoolLiteral\n    end\n\n    # Returns `true` if `self` is a `module`, otherwise `false`.\n    #\n    # ```\n    # module One; end\n    #\n    # class Two; end\n    #\n    # struct Three; end\n    #\n    # {{One.module?}}   # => true\n    # {{Two.module?}}   # => false\n    # {{Three.module?}} # => false\n    # ```\n    def module? : BoolLiteral\n    end\n\n    # Returns `true` if `self` is a `class`, otherwise `false`.\n    #\n    # ```\n    # module One; end\n    #\n    # class Two; end\n    #\n    # struct Three; end\n    #\n    # {{One.class?}}   # => false\n    # {{Two.class?}}   # => true\n    # {{Three.class?}} # => false\n    # ```\n    def class? : BoolLiteral\n    end\n\n    # Returns `true` if `self` is a `struct`, otherwise `false`.\n    #\n    # ```\n    # module One; end\n    #\n    # class Two; end\n    #\n    # struct Three; end\n    #\n    # {{One.struct?}}   # => false\n    # {{Two.struct?}}   # => false\n    # {{Three.struct?}} # => true\n    # ```\n    def struct? : BoolLiteral\n    end\n\n    # Returns the types forming a union type, if this is a union type.\n    # Otherwise returns this single type inside an array literal (so you can safely call `union_types` on any type and treat all types uniformly).\n    #\n    # See also: `#union?`.\n    def union_types : ArrayLiteral(TypeNode)\n    end\n\n    # Returns the fully qualified name of this type.  Optionally without *generic_args* if `self` is a generic type; see `#type_vars`.\n    #\n    # ```\n    # class Foo(T); end\n    #\n    # module Bar::Baz; end\n    #\n    # {{Bar::Baz.name}}                 # => Bar::Baz\n    # {{Foo.name}}                      # => Foo(T)\n    # {{Foo.name(generic_args: false)}} # => Foo\n    # ```\n    def name(*, generic_args : BoolLiteral = true) : MacroId\n    end\n\n    # Returns the type variables of the generic type. If the type is not\n    # generic, an empty array is returned.\n    def type_vars : ArrayLiteral(TypeNode)\n    end\n\n    # Returns the instance variables of this type.\n    # Can only be called from within methods (not top-level code), otherwise will return an empty list.\n    def instance_vars : ArrayLiteral(MetaVar)\n    end\n\n    # Returns the class variables of this type.\n    def class_vars : ArrayLiteral(MetaVar)\n    end\n\n    # Returns all ancestors of this type.\n    def ancestors : ArrayLiteral(TypeNode)\n    end\n\n    # Returns the direct superclass of this type.\n    def superclass : TypeNode | NilLiteral\n    end\n\n    # Returns the direct subclasses of this type.\n    def subclasses : ArrayLiteral(TypeNode)\n    end\n\n    # Returns all the types `self` is directly included in.\n    def includers : ArrayLiteral(TypeNode)\n    end\n\n    # Returns all subclasses of this type.\n    def all_subclasses : ArrayLiteral(TypeNode)\n    end\n\n    # Returns the constants and types defined by this type.\n    def constants : ArrayLiteral(MacroId)\n    end\n\n    # Returns a constant defined in this type.\n    #\n    # If the constant is a constant (like `A = 1`), then its value\n    # as an `ASTNode` is returned. If the constant is a type, the\n    # type is returned as a `TypeNode`. Otherwise, `NilLiteral` is returned.\n    def constant(name : StringLiteral | SymbolLiteral | MacroId) : ASTNode\n    end\n\n    # Returns `true` if this type has a constant. For example `DEFAULT_OPTIONS`\n    # (the name you pass to this method is `\"DEFAULT_OPTIONS\"` or `:DEFAULT_OPTIONS`\n    # in this cases).\n    def has_constant?(name : StringLiteral | SymbolLiteral) : BoolLiteral\n    end\n\n    # Returns the instance methods defined by this type, without including\n    # inherited methods.\n    def methods : ArrayLiteral(Def)\n    end\n\n    # Returns `true` if this type has a method. For example `default_options`\n    # (the name you pass to this method is `\"default_options\"` or `:default_options`\n    # in this cases).\n    def has_method?(name : StringLiteral | SymbolLiteral) : BoolLiteral\n    end\n\n    # Returns the last `Annotation` with the given `type`\n    # attached to this type or `NilLiteral` if there are none.\n    def annotation(type : TypeNode) : Annotation | NilLiteral\n    end\n\n    # Returns an array of annotations with the given `type`\n    # attached to this type, or an empty `ArrayLiteral` if there are none.\n    def annotations(type : TypeNode) : ArrayLiteral(Annotation)\n    end\n\n    # Returns an array of all annotations attached to this\n    # type, or an empty `ArrayLiteral` if there are none.\n    def annotations : ArrayLiteral(Annotation)\n    end\n\n    # Returns the number of elements in this tuple type or tuple metaclass type.\n    # Gives a compile error if this is not one of those types.\n    def size : NumberLiteral\n    end\n\n    # Returns the keys in this named tuple type.\n    # Gives a compile error if this is not a named tuple type.\n    def keys : ArrayLiteral(MacroId)\n    end\n\n    # Returns the type for the given key in this named tuple type.\n    # Gives a compile error if this is not a named tuple type.\n    def [](key : SymbolLiteral | MacroId) : TypeNode | NilLiteral\n    end\n\n    # Returns the class of this type. With this you can, for example, obtain class\n    # methods by invoking `type.class.methods`.\n    def class : TypeNode\n    end\n\n    # Returns the instance type of this type, if it's a class type,\n    # or `self` otherwise. This is the opposite of `#class`.\n    def instance : TypeNode\n    end\n\n    # Determines if `self` overrides any method named *method* from type *type*.\n    #\n    # ```\n    # class Foo\n    #   def one\n    #     1\n    #   end\n    #\n    #   def two\n    #     2\n    #   end\n    # end\n    #\n    # class Bar < Foo\n    #   def one\n    #     11\n    #   end\n    # end\n    #\n    # {{ Bar.overrides?(Foo, \"one\") }} # => true\n    # {{ Bar.overrides?(Foo, \"two\") }} # => false\n    # ```\n    def overrides?(type : TypeNode, method : StringLiteral | SymbolLiteral | MacroId) : BoolLiteral\n    end\n\n    # Returns `self`. This method exists so you can safely call `resolve` on a node and resolve it to a type, even if it's a type already.\n    def resolve : TypeNode\n    end\n\n    # Returns `self`. This method exists so you can safely call `resolve` on a node and resolve it to a type, even if it's a type already.\n    def resolve? : TypeNode\n    end\n\n    # Return `true` if `self` is private and `false` otherwise.\n    def private? : BoolLiteral\n    end\n\n    # Return `true` if `self` is public and `false` otherwise.\n    def public? : BoolLiteral\n    end\n\n    # Returns visibility of `self` as `:public` or `:private?`\n    def visibility : SymbolLiteral\n    end\n\n    # Returns `true` if *other* is an ancestor of `self`.\n    def <(other : TypeNode) : BoolLiteral\n    end\n\n    # Returns `true` if `self` is the same as *other* or if\n    # *other* is an ancestor of `self`.\n    def <=(other : TypeNode) : BoolLiteral\n    end\n\n    # Returns `true` if `self` is an ancestor of *other*.\n    def >(other : TypeNode) : BoolLiteral\n    end\n\n    # Returns `true` if *other* is the same as `self` or if\n    # `self` is an ancestor of *other*.\n    def >=(other : TypeNode) : BoolLiteral\n    end\n\n    # Returns whether `self` contains any inner pointers.\n    #\n    # Primitive types, except `Void`, are expected to not contain inner pointers.\n    # `Proc` and `Pointer` contain inner pointers.\n    # Unions, structs and collection types (tuples, static arrays)\n    # have inner pointers if any of their contained types has inner pointers.\n    # All other types, including classes, are expected to contain inner pointers.\n    #\n    # Types that do not have inner pointers may opt to use atomic allocations,\n    # i.e. `GC.malloc_atomic` rather than `GC.malloc`. The compiler ensures\n    # that, for any type `T`:\n    #\n    # * `Pointer(T).malloc` is atomic if and only if `T` has no inner pointers;\n    # * `T.allocate` is atomic if and only if `T` is a reference type and\n    #   `ReferenceStorage(T)` has no inner pointers.\n    # NOTE: Like `#instance_vars` this method must be called from within a method. The result may be incorrect when used in top-level code.\n    def has_inner_pointers? : BoolLiteral\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/program.cr",
    "content": "require \"llvm\"\nrequire \"json\"\nrequire \"./types\"\nrequire \"crystal/digest/md5\"\n\nmodule Crystal\n  # A program contains all types and top-level methods related to one\n  # compilation of a program.\n  #\n  # It also carries around all information needed to compile a bunch\n  # of files: the unions, the symbols used, all global variables,\n  # all required files, etc. Because of this, a Program is usually passed\n  # around in every step of a compilation to record and query this information.\n  #\n  # In a way, a Program is an alternative implementation to having global variables\n  # for all of this data, but modeled this way one can easily test and exercise\n  # programs because each one has its own definition of the types created,\n  # methods instantiated, etc.\n  #\n  # Additionally, a Program acts as a regular type (a module) that can have\n  # types (the top-level types) and methods (the top-level methods), and which\n  # can also include other modules (this happens when you do `include Module`\n  # at the top-level).\n  class Program < NonGenericModuleType\n    include DefInstanceContainer\n\n    # All symbols (:foo, :bar) found in the program\n    getter symbols = Set(String).new\n\n    # Hash that prevents recursive splat expansions. For example:\n    #\n    # ```\n    # def foo(*x)\n    #   foo(x)\n    # end\n    #\n    # foo(1)\n    # ```\n    #\n    # Here x will be {Int32}, then {{Int32}}, etc.\n    #\n    # The way we detect this is by remembering the types of the splat,\n    # associated to a def's object id (the UInt64), and on an instantiation\n    # we compare the new type with the previous ones and check if they all\n    # contain each other once the method is invoked a number of times recursively\n    # (currently 5 times or more).\n    getter splat_expansions : Hash(Def, Array(Type)) = ({} of Def => Array(Type)).compare_by_identity\n\n    # All FileModules indexed by their filename.\n    # These store file-private defs, and top-level variables in files other\n    # than the main file.\n    getter file_modules = {} of String => FileModule\n\n    # Types that have instance vars initializers which need to be visited\n    # (transformed) by `CleanupTransformer` once the semantic analysis finishes.\n    #\n    # TODO: this probably isn't needed and we can just traverse all types at the\n    # end, and analyze all instance variables initializers that we found. This\n    # should simplify a bit of code.\n    getter after_inference_types = Set(Type).new\n\n    # Top-level variables found in a program (only in the main file).\n    getter vars = MetaVars.new\n\n    # If `true`, doc comments are attached to types and methods.\n    property? wants_doc = false\n\n    # If `true`, error messages can be colorized\n    property? color = true\n\n    # All required files. The set stores absolute files. This way\n    # files loaded by `require` nodes are only processed once.\n    getter requires = Set(String).new\n\n    # All created unions in a program, indexed by an array of opaque\n    # ids of each type in the union. The array (the key) is sorted\n    # by this opaque id.\n    #\n    # A program caches them this way so a union of `String | Int32`\n    # or `Int32 | String` is represented by a single, unique type\n    # in the program.\n    getter unions = {} of Array(UInt64) => UnionType\n\n    # A String pool to avoid creating the same strings over and over.\n    # This pool is passed to the parser, macro expander, etc.\n    getter string_pool = StringPool.new\n\n    record ConstSliceInfo, name : String, element_type : NumberKind, args : Array(ASTNode) do\n      def to_bytes : Bytes\n        element_size = element_type.bytesize // 8\n        bytesize = args.size * element_size\n        buffer = Pointer(UInt8).malloc(bytesize)\n        ptr = buffer\n\n        args.each do |arg|\n          num = arg.as(NumberLiteral)\n          case element_type\n          in .i8?   then ptr.as(Int8*).value = num.value.to_i8\n          in .i16?  then ptr.as(Int16*).value = num.value.to_i16\n          in .i32?  then ptr.as(Int32*).value = num.value.to_i32\n          in .i64?  then ptr.as(Int64*).value = num.value.to_i64\n          in .i128? then ptr.as(Int128*).value = num.value.to_i128\n          in .u8?   then ptr.as(UInt8*).value = num.value.to_u8\n          in .u16?  then ptr.as(UInt16*).value = num.value.to_u16\n          in .u32?  then ptr.as(UInt32*).value = num.value.to_u32\n          in .u64?  then ptr.as(UInt64*).value = num.value.to_u64\n          in .u128? then ptr.as(UInt128*).value = num.value.to_u128\n          in .f32?  then ptr.as(Float32*).value = num.value.to_f32\n          in .f64?  then ptr.as(Float64*).value = num.value.to_f64\n          end\n          ptr += element_size\n        end\n\n        buffer.to_slice(bytesize)\n      end\n    end\n\n    # All constant slices constructed via the `Slice.literal` compiler built-in,\n    # indexed by their buffers' internal names (e.g. `$Slice:0`).\n    getter const_slices = {} of String => ConstSliceInfo\n\n    # Here we store constants, in the\n    # order that they are used. They will be initialized as soon\n    # as the program starts, before the main code.\n    getter const_initializers = [] of Const\n\n    # The class var initializers stored to be used by the cleanup transformer\n    getter class_var_initializers = [] of ClassVarInitializer\n\n    # Counters for the `__temp_*` temporary variables. Each prefix has its own\n    # counter, see `#new_temp_var_name` for details.\n    @temp_vars = Hash(String, Int32).new { |hash, prefix| hash[prefix] = 1 }\n\n    # The constant for ARGC_UNSAFE\n    getter! argc : Const\n\n    # The constant for ARGV_UNSAFE\n    getter! argv : Const\n\n    # Default standard output to use in a program, while compiling.\n    property stdout : IO = STDOUT\n\n    # Whether to show error trace\n    property? show_error_trace = false\n\n    # The main filename of this program\n    property filename : String?\n\n    # A `ProgressTracker` object which tracks compilation progress.\n    property progress_tracker = ProgressTracker.new\n\n    getter codegen_target = Config.host_target\n\n    getter predefined_constants = Array(Const).new\n\n    property compiler : Compiler?\n\n    def initialize\n      super(self, self, \"main\")\n\n      # Every crystal program comes with some predefined types that we initialize here,\n      # like Object, Value, Reference, etc.\n      types = self.types\n\n      types[\"Object\"] = object = @object = NonGenericClassType.new self, self, \"Object\", nil\n      object.can_be_stored = false\n      object.abstract = true\n\n      types[\"Reference\"] = reference = @reference = NonGenericClassType.new self, self, \"Reference\", object\n      reference.can_be_stored = false\n\n      types[\"Value\"] = value = @value = NonGenericClassType.new self, self, \"Value\", object\n      abstract_value_type(value)\n\n      types[\"Number\"] = number = @number = NonGenericClassType.new self, self, \"Number\", value\n      abstract_value_type(number)\n\n      types[\"NoReturn\"] = @no_return = NoReturnType.new self, self, \"NoReturn\"\n      types[\"Void\"] = @void = VoidType.new self, self, \"Void\"\n      types[\"Nil\"] = @nil = NilType.new self, self, \"Nil\", value, 1\n      types[\"Bool\"] = @bool = BoolType.new self, self, \"Bool\", value, 1\n      types[\"Char\"] = @char = CharType.new self, self, \"Char\", value, 4\n\n      types[\"Int\"] = int = @int = NonGenericClassType.new self, self, \"Int\", number\n      abstract_value_type(int)\n\n      types[\"Int8\"] = @int8 = IntegerType.new self, self, \"Int8\", int, 1, 1, :i8\n      types[\"UInt8\"] = @uint8 = IntegerType.new self, self, \"UInt8\", int, 1, 2, :u8\n      types[\"Int16\"] = @int16 = IntegerType.new self, self, \"Int16\", int, 2, 3, :i16\n      types[\"UInt16\"] = @uint16 = IntegerType.new self, self, \"UInt16\", int, 2, 4, :u16\n      types[\"Int32\"] = @int32 = IntegerType.new self, self, \"Int32\", int, 4, 5, :i32\n      types[\"UInt32\"] = @uint32 = IntegerType.new self, self, \"UInt32\", int, 4, 6, :u32\n      types[\"Int64\"] = @int64 = IntegerType.new self, self, \"Int64\", int, 8, 7, :i64\n      types[\"UInt64\"] = @uint64 = IntegerType.new self, self, \"UInt64\", int, 8, 8, :u64\n      types[\"Int128\"] = @int128 = IntegerType.new self, self, \"Int128\", int, 16, 9, :i128\n      types[\"UInt128\"] = @uint128 = IntegerType.new self, self, \"UInt128\", int, 16, 10, :u128\n\n      types[\"Float\"] = float = @float = NonGenericClassType.new self, self, \"Float\", number\n      abstract_value_type(float)\n\n      types[\"Float32\"] = @float32 = FloatType.new self, self, \"Float32\", float, 4, 9\n      types[\"Float64\"] = @float64 = FloatType.new self, self, \"Float64\", float, 8, 10\n\n      types[\"Symbol\"] = @symbol = SymbolType.new self, self, \"Symbol\", value, 4\n      types[\"Pointer\"] = pointer = @pointer = PointerType.new self, self, \"Pointer\", value, [\"T\"]\n      pointer.struct = true\n      pointer.can_be_stored = false\n\n      types[\"Tuple\"] = tuple = @tuple = TupleType.new self, self, \"Tuple\", value, [\"T\"]\n      tuple.can_be_stored = false\n\n      types[\"NamedTuple\"] = named_tuple = @named_tuple = NamedTupleType.new self, self, \"NamedTuple\", value, [\"T\"]\n      named_tuple.can_be_stored = false\n\n      types[\"StaticArray\"] = static_array = @static_array = StaticArrayType.new self, self, \"StaticArray\", value, [\"T\", \"N\"]\n      static_array.struct = true\n      static_array.declare_instance_var(\"@buffer\", static_array.type_parameter(\"T\"))\n      static_array.can_be_stored = false\n\n      types[\"String\"] = string = @string = NonGenericClassType.new self, self, \"String\", reference\n      string.declare_instance_var(\"@bytesize\", int32)\n      string.declare_instance_var(\"@length\", int32)\n      string.declare_instance_var(\"@c\", uint8)\n\n      types[\"Class\"] = klass = @class = MetaclassType.new(self, object, value, \"Class\")\n      klass.can_be_stored = false\n\n      types[\"Struct\"] = struct_t = @struct_t = NonGenericClassType.new self, self, \"Struct\", value\n      abstract_value_type(struct_t)\n\n      types[\"Enumerable\"] = @enumerable = GenericModuleType.new self, self, \"Enumerable\", [\"T\"]\n      types[\"Indexable\"] = @indexable = GenericModuleType.new self, self, \"Indexable\", [\"T\"]\n\n      types[\"Array\"] = @array = GenericClassType.new self, self, \"Array\", reference, [\"T\"]\n      types[\"Hash\"] = @hash_type = GenericClassType.new self, self, \"Hash\", reference, [\"K\", \"V\"]\n      types[\"Regex\"] = @regex = NonGenericClassType.new self, self, \"Regex\", reference\n      types[\"Range\"] = range = @range = GenericClassType.new self, self, \"Range\", struct_t, [\"B\", \"E\"]\n      range.struct = true\n      types[\"Slice\"] = slice = @slice = GenericClassType.new self, self, \"Slice\", struct_t, [\"T\"]\n      slice.struct = true\n\n      types[\"Exception\"] = @exception = NonGenericClassType.new self, self, \"Exception\", reference\n\n      types[\"Enum\"] = enum_t = @enum = NonGenericClassType.new self, self, \"Enum\", value\n      abstract_value_type(enum_t)\n\n      types[\"Proc\"] = @proc = ProcType.new self, self, \"Proc\", value, [\"T\", \"R\"]\n      types[\"Union\"] = @union = GenericUnionType.new self, self, \"Union\", value, [\"T\"]\n      types[\"Crystal\"] = @crystal = NonGenericModuleType.new self, self, \"Crystal\"\n\n      types[\"ARGC_UNSAFE\"] = @argc = argc_unsafe = Const.new self, self, \"ARGC_UNSAFE\", Primitive.new(\"argc\", int32)\n      types[\"ARGV_UNSAFE\"] = @argv = argv_unsafe = Const.new self, self, \"ARGV_UNSAFE\", Primitive.new(\"argv\", pointer_of(pointer_of(uint8)))\n\n      argc_unsafe.no_init_flag = true\n      argv_unsafe.no_init_flag = true\n\n      predefined_constants << argc_unsafe\n      predefined_constants << argv_unsafe\n\n      # Make sure to initialize `ARGC_UNSAFE` and `ARGV_UNSAFE` as soon as the program starts\n      const_initializers << argc_unsafe\n      const_initializers << argv_unsafe\n\n      types[\"GC\"] = gc = NonGenericModuleType.new self, self, \"GC\"\n      gc.metaclass.as(ModuleType).add_def Def.new(\"add_finalizer\", [Arg.new(\"object\")], Nop.new)\n\n      # Built-in annotations\n      types[\"AlwaysInline\"] = @always_inline_annotation = AnnotationType.new self, self, \"AlwaysInline\"\n      types[\"CallConvention\"] = @call_convention_annotation = AnnotationType.new self, self, \"CallConvention\"\n      types[\"Extern\"] = @extern_annotation = AnnotationType.new self, self, \"Extern\"\n      types[\"Flags\"] = @flags_annotation = AnnotationType.new self, self, \"Flags\"\n      types[\"Link\"] = @link_annotation = AnnotationType.new self, self, \"Link\"\n      types[\"Naked\"] = @naked_annotation = AnnotationType.new self, self, \"Naked\"\n      types[\"NoInline\"] = @no_inline_annotation = AnnotationType.new self, self, \"NoInline\"\n      types[\"Packed\"] = @packed_annotation = AnnotationType.new self, self, \"Packed\"\n      types[\"Primitive\"] = @primitive_annotation = AnnotationType.new self, self, \"Primitive\"\n      types[\"Raises\"] = @raises_annotation = AnnotationType.new self, self, \"Raises\"\n      types[\"ReturnsTwice\"] = @returns_twice_annotation = AnnotationType.new self, self, \"ReturnsTwice\"\n      types[\"ThreadLocal\"] = @thread_local_annotation = AnnotationType.new self, self, \"ThreadLocal\"\n      types[\"Deprecated\"] = @deprecated_annotation = AnnotationType.new self, self, \"Deprecated\"\n      types[\"Experimental\"] = @experimental_annotation = AnnotationType.new self, self, \"Experimental\"\n\n      define_crystal_constants\n\n      # definition in `macros/types.cr`\n      define_macro_types\n    end\n\n    # Returns a new `Parser` for the given *source*, sharing the string pool and\n    # warnings with this program.\n    def new_parser(source : String, var_scopes = [Set(String).new])\n      Parser.new(source, string_pool, var_scopes, warnings)\n    end\n\n    # Returns a `LiteralExpander` useful to expand literal like arrays and hashes\n    # into simpler forms.\n    getter(literal_expander) { LiteralExpander.new self }\n\n    # Returns a `CrystalPath` for this program.\n    getter(crystal_path) { CrystalPath.new(codegen_target: codegen_target) }\n\n    # Returns a `Var` that has `Nil` as a type.\n    # This variable is bound to other nodes in the semantic phase for things\n    # that need to be nilable, for example to a variable that's only declared\n    # in one branch of an `if` expression.\n    getter(nil_var) { Var.new(\"<nil_var>\", nil_type) }\n\n    # Defines a predefined constant in the Crystal module, such as BUILD_DATE and VERSION.\n    private def define_crystal_constants\n      if build_commit = Crystal::Config.build_commit\n        build_commit_const = define_crystal_string_constant \"BUILD_COMMIT\", build_commit\n      else\n        build_commit_const = define_crystal_nil_constant \"BUILD_COMMIT\"\n      end\n      build_commit_const.doc = <<-MD\n        The build commit identifier of the Crystal compiler.\n        MD\n\n      define_crystal_string_constant \"BUILD_DATE\", Crystal::Config.date, <<-MD\n        The build date of the Crystal compiler.\n        MD\n      define_crystal_string_constant \"CACHE_DIR\", CacheDir.instance.dir, <<-MD\n        The cache directory configured for the Crystal compiler.\n\n        The value is defined by the environment variable `CRYSTAL_CACHE_DIR` and\n        defaults to the user's configured cache directory.\n        MD\n      define_crystal_string_constant \"DEFAULT_PATH\", Crystal::Config.path, <<-MD\n        The default Crystal path configured in the compiler. This value is baked\n        into the compiler and usually points to the accompanying version of the\n        standard library.\n        MD\n      define_crystal_string_constant \"DESCRIPTION\", Crystal::Config.description, <<-MD\n        Full version information of the Crystal compiler. Equivalent to `crystal --version`.\n        MD\n      define_crystal_string_constant \"PATH\", Crystal::CrystalPath.default_path, <<-MD\n        Colon-separated paths where the compiler searches for required source files.\n\n        The value is defined by the environment variable `CRYSTAL_PATH`\n        and defaults to `DEFAULT_PATH`.\n        MD\n      define_crystal_string_constant \"LIBRARY_PATH\", Crystal::CrystalLibraryPath.default_path, <<-MD\n        Colon-separated paths where the compiler searches for (binary) libraries.\n\n        The value is defined by the environment variables `CRYSTAL_LIBRARY_PATH`.\n        MD\n      define_crystal_string_constant \"VERSION\", Crystal::Config.version, <<-MD\n        The version of the Crystal compiler.\n        MD\n      define_crystal_string_constant \"LLVM_VERSION\", LLVM.version, <<-MD\n        The version of LLVM used by the Crystal compiler.\n        MD\n      define_crystal_string_constant \"HOST_TRIPLE\", Crystal::Config.host_target.to_s, <<-MD\n        The LLVM target triple of the host system (the machine that the compiler runs on).\n        MD\n      define_crystal_string_constant \"TARGET_TRIPLE\", Crystal::Config.host_target.to_s, <<-MD\n        The LLVM target triple of the target system (the machine that the compiler builds for).\n        MD\n    end\n\n    private def define_crystal_string_constant(name, value, doc = nil)\n      define_crystal_constant name, StringLiteral.new(value).tap(&.set_type(string)), doc\n    end\n\n    private def define_crystal_nil_constant(name, doc = nil)\n      define_crystal_constant name, NilLiteral.new.tap(&.set_type(self.nil)), doc\n    end\n\n    private def define_crystal_constant(name, value, doc = nil) : Const\n      crystal.types[name] = const = Const.new self, crystal, name, value\n      const.no_init_flag = true\n      const.doc = doc\n\n      predefined_constants << const\n      const\n    end\n\n    property(target_machine : LLVM::TargetMachine) { codegen_target.to_target_machine }\n\n    def codegen_target=(@codegen_target : Codegen::Target) : Codegen::Target\n      crystal.types[\"TARGET_TRIPLE\"].as(Const).value.as(StringLiteral).value = codegen_target.to_s\n      @codegen_target\n    end\n\n    # Returns the `Type` for `Array(type)`\n    def array_of(type)\n      array.instantiate [type] of TypeVar\n    end\n\n    # Returns the `Type` for `Hash(key_type, value_type)`\n    def hash_of(key_type, value_type)\n      hash_type.instantiate [key_type, value_type] of TypeVar\n    end\n\n    # Returns the `Type` for `Range(begin_type, end_type)`\n    def range_of(begin_type, end_type)\n      range.instantiate [begin_type, end_type] of TypeVar\n    end\n\n    # Returns the `Type` for `Tuple(*types)`\n    def tuple_of(types)\n      type_vars = types.map &.as(TypeVar)\n      tuple.instantiate(type_vars)\n    end\n\n    # Returns the `Type` for `NamedTuple(**entries)`\n    def named_tuple_of(entries : Hash(String, Type) | NamedTuple)\n      entries = entries.map { |k, v| NamedArgumentType.new(k.to_s, v.as(Type)) }\n      named_tuple_of(entries)\n    end\n\n    # :ditto:\n    def named_tuple_of(entries : Array(NamedArgumentType))\n      named_tuple.instantiate_named_args(entries)\n    end\n\n    # Returns the `Type` for `type | Nil`\n    def nilable(type)\n      case type\n      when self.nil, self.no_return\n        # Nil | Nil      # => Nil\n        # NoReturn | Nil # => Nil\n        self.nil\n      when UnionType\n        types = Array(Type).new(type.union_types.size + 1)\n        types.concat type.union_types\n        types << self.nil unless types.includes? self.nil\n        union_of types\n      else\n        union_of self.nil, type\n      end\n    end\n\n    # Returns the `Type` for `type1 | type2`\n    def union_of(type1, type2)\n      # T | T # => T\n      return type1 if type1 == type2\n\n      union_of([type1, type2] of Type).not_nil!\n    end\n\n    # Returns the `Type` for `Union(*types)`\n    def union_of(types : Array)\n      case types.size\n      when 0\n        nil\n      when 1\n        types.first\n      else\n        types.sort_by! &.opaque_id\n        opaque_ids = types.map(&.opaque_id)\n        unions[opaque_ids] ||= make_union_type(types, opaque_ids)\n      end\n    end\n\n    private def make_union_type(types, opaque_ids)\n      # NilType has opaque_id == 0\n      has_nil = opaque_ids.first == 0\n\n      if has_nil\n        # Check if it's a Nilable type\n        if types.size == 2\n          other_type = types[1]\n          if other_type.reference_like? && !other_type.virtual?\n            return NilableType.new(self, other_type)\n          else\n            untyped_type = other_type.remove_typedef\n            if untyped_type.proc?\n              return NilableProcType.new(self, other_type)\n            end\n          end\n        end\n\n        # Remove the Nil type now and later insert it at the end\n        nil_type = types.shift\n      end\n\n      # Sort by name so a same union type, say `Int32 | String`, always is named that\n      # way, regardless of the actual order of the types. However, we always put\n      # Nil at the end, inside the `nil_type` check.\n      types.sort_by! &.to_s\n\n      if nil_type\n        types.push nil_type\n\n        if types.all?(&.reference_like?)\n          return NilableReferenceUnionType.new(self, types)\n        else\n          return MixedUnionType.new(self, types)\n        end\n      end\n\n      if types.all? &.reference_like?\n        return ReferenceUnionType.new(self, types)\n      end\n\n      MixedUnionType.new(self, types)\n    end\n\n    # Returns the `Type` for `Proc(*types)`\n    def proc_of(types : Array)\n      type_vars = types.map &.as(TypeVar)\n      unless type_vars.empty?\n        type_vars[-1] = self.nil if type_vars[-1].is_a?(VoidType)\n      end\n      proc.instantiate(type_vars)\n    end\n\n    # Returns the `Type` for `Proc(*nodes.map(&.type), return_type)`\n    def proc_of(nodes : Array(ASTNode), return_type : Type)\n      type_vars = Array(TypeVar).new(nodes.size + 1)\n      nodes.each do |node|\n        type_vars << node.type\n      end\n      return_type = self.nil if return_type.void?\n      type_vars << return_type\n      proc.instantiate(type_vars)\n    end\n\n    # Returns the `Type` for `Pointer(type)`\n    def pointer_of(type)\n      pointer.instantiate([type] of TypeVar)\n    end\n\n    # Returns the `Type` for `StaticArray(type, size)`\n    def static_array_of(type, size)\n      static_array.instantiate([type, NumberLiteral.new(size)] of TypeVar)\n    end\n\n    record RecordedRequire, filename : String, relative_to : String? do\n      include JSON::Serializable\n    end\n    property recorded_requires = [] of RecordedRequire\n\n    # Remembers that the program depends on this require.\n    def record_require(filename, relative_to) : Nil\n      recorded_requires << RecordedRequire.new(filename, relative_to)\n    end\n\n    def run_requires(node : Require, filenames, &) : Nil\n      dependency_printer = compiler.try(&.dependency_printer)\n\n      filenames.each do |filename|\n        unseen_file = requires.add?(filename)\n\n        dependency_printer.try(&.enter_file(filename, unseen_file))\n\n        if unseen_file\n          yield filename\n        end\n\n        dependency_printer.try(&.leave_file)\n      end\n    end\n\n    # Finds *filename* in the configured CRYSTAL_PATH for this program,\n    # relative to *relative_to*.\n    def find_in_path(filename, relative_to = nil) : Array(String)?\n      crystal_path.find filename, relative_to\n    end\n\n    {% for name in %w(object no_return value number reference void nil bool char int int8 int16 int32 int64 int128\n                     uint8 uint16 uint32 uint64 uint128 float float32 float64 string symbol pointer enumerable indexable\n                     array static_array exception tuple named_tuple proc union enum range slice regex crystal\n                     packed_annotation thread_local_annotation no_inline_annotation\n                     always_inline_annotation naked_annotation returns_twice_annotation\n                     raises_annotation primitive_annotation call_convention_annotation\n                     flags_annotation link_annotation extern_annotation deprecated_annotation experimental_annotation) %}\n      def {{name.id}}\n        @{{name.id}}.not_nil!\n      end\n    {% end %}\n\n    # Returns the `Nil` type\n    def nil_type\n      @nil.not_nil!\n    end\n\n    # Returns the `Hash` type\n    def hash_type\n      @hash_type.not_nil!\n    end\n\n    def type_from_literal_kind(kind : NumberKind)\n      case kind\n      in .i8?   then int8\n      in .i16?  then int16\n      in .i32?  then int32\n      in .i64?  then int64\n      in .i128? then int128\n      in .u8?   then uint8\n      in .u16?  then uint16\n      in .u32?  then uint32\n      in .u64?  then uint64\n      in .u128? then uint128\n      in .f32?  then float32\n      in .f64?  then float64\n      end\n    end\n\n    def int_type(signed, size)\n      if signed\n        case size\n        when  1 then int8\n        when  2 then int16\n        when  4 then int32\n        when  8 then int64\n        when 16 then int128\n        else\n          raise \"BUG: Invalid int size: #{size}\"\n        end\n      else\n        case size\n        when  1 then uint8\n        when  2 then uint16\n        when  4 then uint32\n        when  8 then uint64\n        when 16 then uint128\n        else\n          raise \"BUG: Invalid int size: #{size}\"\n        end\n      end\n    end\n\n    # Returns the `IntegerType` that matches the given Int value\n    def int?(int)\n      case int\n      when Int8    then int8\n      when Int16   then int16\n      when Int32   then int32\n      when Int64   then int64\n      when Int128  then int128\n      when UInt8   then uint8\n      when UInt16  then uint16\n      when UInt32  then uint32\n      when UInt64  then uint64\n      when UInt128 then uint128\n      else\n        nil\n      end\n    end\n\n    # Returns the `Struct` type\n    def struct\n      @struct_t.not_nil!\n    end\n\n    # Returns the `Class` type\n    def class_type\n      @class.not_nil!\n    end\n\n    def new_temp_var(node : ASTNode) : Var\n      # TODO: is it safe to add `.at(node)` here?\n      Var.new(new_temp_var_name(node))\n    end\n\n    def new_temp_var(prefix : String? = nil) : Var\n      Var.new(new_temp_var_name(prefix))\n    end\n\n    # Returns a unique variable name associated with the given AST *node*.\n    #\n    # Nodes with a location include the first 8 digits of the filename's MD5\n    # digest as part of the prefix, e.g. all names originating from `foo.cr` use\n    # the prefix `__temp_cd6ae5dd_*`. This localizes the impact on incremental\n    # object file generation to all types defined or reopened in that same file.\n    def new_temp_var_name(node : ASTNode) : String\n      if filename = node.location.try(&.original_filename)\n        # the parser creates `Location`s with an empty filename by default,\n        # assume they are equivalent to `nil` to make compiler specs less noisy\n        unless filename == \"\"\n          prefix = \"__temp_#{Crystal::Digest::MD5.hexdigest(filename)[0, 8]}_\"\n        end\n      end\n\n      new_temp_var_name(prefix)\n    end\n\n    # Returns a unique variable name associated with the given *prefix*.\n    #\n    # By convention, the prefix must start with `__temp_`, and defaults to it as\n    # well.\n    def new_temp_var_name(prefix : String? = nil) : String\n      prefix ||= \"__temp_\"\n      id = @temp_vars.update(prefix, &.succ)\n      \"#{prefix}#{id}\"\n    end\n\n    # Colorizes the given object, depending on whether this program\n    # is configured to use colors.\n    def colorize(obj)\n      obj.colorize.toggle(@color)\n    end\n\n    private def abstract_value_type(type)\n      type.abstract = true\n      type.struct = true\n      type.can_be_stored = false\n    end\n\n    # Next come overrides for the type system\n\n    def metaclass\n      self\n    end\n\n    def type_desc\n      \"main\"\n    end\n\n    def add_def(node : Def)\n      if file_module = check_private(node)\n        file_module.add_def node\n      else\n        super\n      end\n    end\n\n    def add_macro(node : Macro)\n      if file_module = check_private(node)\n        file_module.add_macro node\n      else\n        super\n      end\n    end\n\n    def lookup_private_matches(filename, signature, analyze_all = false)\n      file_module?(filename).try &.lookup_matches(signature, analyze_all: analyze_all)\n    end\n\n    def file_module?(filename)\n      file_modules[filename]?\n    end\n\n    def file_module(filename)\n      file_modules[filename] ||= FileModule.new(self, self, filename)\n    end\n\n    def check_private(node)\n      return nil unless node.visibility.private?\n\n      filename = node.location.try &.original_filename\n      return nil unless filename\n\n      file_module(filename)\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"<Program>\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/progress_tracker.cr",
    "content": "module Crystal\n  class ProgressTracker\n    # FIXME: This assumption is not always true\n    STAGES        = 14\n    STAGE_PADDING = 34\n\n    property? stats = false\n    property? progress = false\n\n    getter current_stage = 1\n    getter current_stage_name : String?\n    getter stage_progress = 0\n    getter stage_progress_total : Int32?\n\n    def stage(name, &)\n      @current_stage_name = name\n\n      print_stats\n      print_progress\n\n      time_start = Time.instant\n      retval = yield\n      time_taken = time_start.elapsed\n\n      print_stats(time_taken)\n      print_progress\n\n      @current_stage += 1\n      @stage_progress = 0\n      @stage_progress_total = nil\n\n      retval\n    end\n\n    def clear\n      return unless @progress\n      print \" \" * (STAGE_PADDING + 5)\n      print \"\\r\"\n    end\n\n    def print_stats(time_taken = nil)\n      return unless @stats\n\n      justified_name = \"#{current_stage_name}:\".ljust(STAGE_PADDING)\n      if time_taken\n        memory_usage_mb = GC.stats.heap_size / 1024.0 / 1024.0\n        memory_usage_str = \" (%7.2fMB)\" % {memory_usage_mb}\n        puts \"#{justified_name} #{time_taken}#{memory_usage_str}\"\n      else\n        print \"#{justified_name}\\r\" unless @progress\n      end\n    end\n\n    def print_progress\n      return unless @progress\n\n      if stage_progress_total = @stage_progress_total\n        progress = \" [#{@stage_progress}/#{stage_progress_total}]\"\n      end\n\n      stage_name = @current_stage_name.try(&.ljust(STAGE_PADDING))\n      print \"[#{@current_stage}/#{STAGES}]#{progress} #{stage_name}\\r\"\n    end\n\n    def stage_progress=(@stage_progress)\n      print_progress\n    end\n\n    def stage_progress_total=(@stage_progress_total)\n      print_progress\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/abstract_def_checker.cr",
    "content": "# Checks that abstract methods are implemented.\n#\n# We traverse all abstract types in the program (abstract classes/structs\n# and modules) and for each abstract method we find, we traverse the implementors\n# (subtypes and including types) and see if they implement that method.\n#\n# An abstract method is either implemented if the type restriction (if any) matches,\n# of if there's no type restriction. For example:\n#\n# ```\n# abstract class Foo\n#   abstract def foo(x : Int32)\n# end\n#\n# class Bar < Foo\n#   # OK\n#   def foo(x : Int32); end\n# end\n#\n# class Baz < Foo\n#   # OK too, because it's more general\n#   def foo(x); end\n# end\n# ```\nclass Crystal::AbstractDefChecker\n  def initialize(@program : Program)\n  end\n\n  def run\n    check_types(@program)\n    @program.file_modules.each_value do |file_module|\n      check_types(file_module)\n    end\n  end\n\n  def check_types(type)\n    type.types?.try &.each_value do |type|\n      check_single(type)\n    end\n  end\n\n  def check_single(type)\n    if type.abstract? || type.module?\n      type.defs.try &.each_value do |defs_with_metadata|\n        defs_with_metadata.each do |def_with_metadata|\n          a_def = def_with_metadata.def\n          if a_def.abstract?\n            free_vars = free_var_nodes(a_def)\n            check_implemented_in_subtypes(type, a_def, free_vars)\n          end\n        end\n      end\n    end\n\n    check_types(type)\n  end\n\n  def check_implemented_in_subtypes(type, method, free_vars)\n    check_implemented_in_subtypes(type, type, method, free_vars)\n  end\n\n  def check_implemented_in_subtypes(base, type, method, free_vars)\n    subtypes = case type\n               when NonGenericModuleType\n                 type.raw_including_types\n               when GenericModuleType\n                 type.raw_including_types\n               else\n                 type.subclasses\n               end\n\n    subtypes.try &.each do |subtype|\n      next if implements_with_ancestors?(subtype, method, base, free_vars)\n\n      # Union doesn't need a hash, dup, to_s, etc., methods because it's special\n      next if subtype == @program.union\n\n      if subtype.abstract? || subtype.module?\n        check_implemented_in_subtypes(base, subtype, method, free_vars)\n      else\n        msg = \"abstract `def #{Call.def_full_name(base, method)}` must be implemented by #{subtype}\"\n        if location = subtype.locations.try &.first?\n          raise TypeException.new(msg, location)\n        else\n          raise TypeException.new(msg)\n        end\n      end\n    end\n  end\n\n  def implements_with_ancestors?(type : Type, method : Def, base : Type, free_vars)\n    return true if implements?(type, type, method, base, free_vars)\n\n    type.ancestors.any? do |ancestor|\n      if implements?(type, ancestor, method, base, free_vars)\n        # Check that the implementation does not come from a supertype of `base`\n        if ancestor.is_a?(GenericInstanceType)\n          ancestor = ancestor.generic_type.as(Type)\n        end\n        !base.implements?(ancestor)\n      else\n        false\n      end\n    end\n  end\n\n  # Returns `true` if `ancestor_type` implements `method` of `base` when computing\n  # that information to check whether `target_type` implements `method` of `base`.\n  def implements?(target_type : Type, ancestor_type : Type, method : Def, base : Type, method_free_vars)\n    implemented = false\n    found_param_match = false\n    ancestor_type.defs.try &.each_value do |defs_with_metadata|\n      defs_with_metadata.each do |def_with_metadata|\n        a_def = def_with_metadata.def\n        def_free_vars = free_var_nodes(a_def)\n\n        if implements?(target_type, ancestor_type, a_def, def_free_vars, base, method, method_free_vars)\n          unless implemented\n            check_return_type(target_type, ancestor_type, a_def, base, method)\n            implemented = true\n          end\n\n          unless found_param_match || target_type.abstract? || target_type.module?\n            check_positional_param_names(a_def, base, method)\n            found_param_match = true if same_parameters?(a_def, method)\n          end\n        end\n      end\n    end\n    implemented\n  end\n\n  # Returns `true` if the method `t1#m1` implements `t2#m2` when computing\n  # that to check whether `target_type` implements `t2#m2`\n  # (`t1` is an ancestor of `target_type`).\n  def implements?(target_type : Type, t1 : Type, m1 : Def, free_vars1, t2 : Type, m2 : Def, free_vars2)\n    return false if m1.abstract?\n    return false unless m1.name == m2.name\n    return false unless m1.block_arity == m2.block_arity\n\n    m1_args, m1_kargs = def_arg_ranges(m1)\n    m2_args, m2_kargs = def_arg_ranges(m2)\n\n    # If the base type is a generic type, we find the generic instantiation of\n    # t1 for it. This will have a mapping of type vars to types, for example\n    # T will be Int32 in something like `class Bar < Foo(Int32)` with `Foo(T)`.\n    # Then we replace all `T` in the base method with `Int32`, and just then\n    # we check if they match.\n    if t2.is_a?(GenericType)\n      # We must find the generic instantiation starting from the target type,\n      # not from t1, because maybe t1 doesn't reach the generic base type.\n      generic_base = find_base_generic_instantiation(target_type, t2)\n      m2 = replace_method_arg_paths_with_type_vars(t2, m2, generic_base)\n    end\n\n    # First check positional arguments\n    # The following algorithm walk through the arguments in the abstract\n    # method and the implementation at the same time, until a splat argument is found\n    # or the end of the positional argument list is reached in both lists.\n    # The table below resumes the allowed cases (OK) and rejected (x) for each combination\n    # of the argument in the implementation (a1) and the abstract def (a2).\n    # `an = Dn` represents an argument with a default value. `-` represents that\n    # no more arguments are available to compare.\n    # Allowed cases are then verified that they have compatible default value\n    # and type restrictions.\n    #\n    #         |  a2  | a2 = D2 | *a2 |  -  |\n    # a1      |  OK  |   x     | x   |  x  |\n    # a1 = D1 |  OK  |   OK    | OK  |  OK |\n    # *a1     |  OK  |   x     | OK  |  OK |\n    # -       |  x   |   x     | x   |  OK |\n    i1 = i2 = 0\n    loop do\n      a1 = i1 <= m1_args ? m1.args[i1] : nil\n      a2 = i2 <= m2_args ? m2.args[i2] : nil\n\n      case\n      when !a1\n        # No more arguments in the implementation\n        return false unless !a2\n      when i1 == m1.splat_index\n        # The argument in the implementation is a splat\n        return false if a2 && a2.default_value\n      when !a1.default_value\n        # The argument in the implementation doesn't have a default value\n        return false if !a2 || a2.default_value || i2 == m2.splat_index\n      end\n\n      if a1 && a2\n        return false unless check_arg(t1, a1, free_vars1, t2, a2, free_vars2)\n      end\n\n      # Move next, unless we're on the splat already or at the end of the arguments\n      done = true\n      unless i1 == m1.splat_index || a1 == nil\n        i1 += 1\n        done = false\n      end\n      unless i2 == m2.splat_index || a2 == nil\n        i2 += 1\n        done = false\n      end\n      break if done\n    end\n\n    # Index keyword arguments\n    kargs =\n      m1_kargs.to_h do |i|\n        a1 = m1.args[i]\n        {a1.name, a1}\n      end\n\n    # Check double splat\n    if m2_double_splat = m2.double_splat\n      if m1_double_splat = m1.double_splat\n        return false unless check_arg(t1, m1_double_splat, free_vars1, t2, m2_double_splat, free_vars2)\n      else\n        return false\n      end\n    end\n\n    # Check keyword arguments\n    # They must either exist in the implementation or match with the double splat\n    m2_kargs.each do |i|\n      a2 = m2.args[i]\n      if a1 = kargs.delete(a2.name) || m1.double_splat\n        return false unless check_arg(t1, a1, free_vars1, t2, a2, free_vars2)\n      else\n        return false\n      end\n    end\n\n    # Check remaining keyword arguments\n    # They must have a default value and match the double splat in the abstract (if it exists)\n    kargs.each_value do |a1|\n      return false unless a1.default_value\n      if m2_double_splat = m2.double_splat\n        return false unless check_arg(t1, a1, free_vars1, t2, m2_double_splat, free_vars2)\n      end\n    end\n\n    true\n  end\n\n  private def def_arg_ranges(method : Def)\n    if splat = method.splat_index\n      if method.args[splat].name.size == 0\n        {splat - 1, (splat + 1...method.args.size)}\n      else\n        {splat, (splat + 1...method.args.size)}\n      end\n    else\n      {method.args.size - 1, (0...0)}\n    end\n  end\n\n  def check_arg(t1 : Type, a1 : Arg, free_vars1, t2 : Type, a2 : Arg, free_vars2)\n    if a2.default_value\n      return false unless a1.default_value == a2.default_value\n    end\n\n    r1 = a1.restriction\n    r2 = a2.restriction\n    return false if r1 && !r2\n    if r2 && r1 && r1 != r2\n      # Check if a1.restriction is contravariant with a2.restriction\n      begin\n        rt1 = t1.lookup_type(r1, free_vars: free_vars1)\n        rt2 = t2.lookup_type(r2, free_vars: free_vars2)\n        return false unless rt2.implements?(rt1)\n      rescue Crystal::TypeException\n        # Ignore if we can't find a type (assume the method is implemented)\n        return true\n      end\n    end\n\n    true\n  end\n\n  def same_parameters?(m1 : Def, m2 : Def)\n    return false unless m1.args.size == m2.args.size\n\n    splat_index = m1.splat_index\n    return false unless splat_index == m2.splat_index\n\n    named_args1 = nil\n    named_args2 = nil\n\n    m1.args.each_with_index do |arg1, i|\n      arg2 = m2.args[i]\n\n      if splat_index\n        if i > splat_index\n          # named parameters may be in any order\n          (named_args1 ||= [] of String) << arg1.external_name\n          (named_args2 ||= [] of String) << arg2.external_name\n          next\n        elsif i == splat_index\n          # single splat name may be different; bare splats must agree\n          return false unless (arg1.external_name != \"\") == (arg2.external_name != \"\")\n          next\n        end\n      end\n\n      # positional parameter name must agree\n      return false unless arg1.external_name == arg2.external_name\n    end\n\n    if named_args1 && named_args2\n      named_args1.sort!\n      named_args2.sort!\n      return false unless named_args1 == named_args2\n    end\n\n    # double splat name may be different\n    m1.double_splat.nil? == m2.double_splat.nil?\n  end\n\n  # Checks that the return type of `type#method` matches that of `base_type#base_method`\n  # when computing that information for `target_type` (`type` is an ancestor of `target_type`).\n  def check_return_type(target_type : Type, type : Type, method : Def, base_type : Type, base_method : Def)\n    base_return_type_node = base_method.return_type\n    return unless base_return_type_node\n\n    original_base_return_type = base_type.lookup_type?(base_return_type_node)\n    unless original_base_return_type\n      report_error(base_return_type_node, \"can't resolve return type #{base_return_type_node}\")\n      return\n    end\n\n    # If the base type is a generic type, we find the generic instantiation of\n    # t1 for it. This will have a mapping of type vars to types, for example\n    # T will be Int32 in something like `class Bar < Foo(Int32)` with `Foo(T)`.\n    # Then we replace all `T` in the base method return type with `Int32`,\n    # and just then we check if they match.\n    if base_type.is_a?(GenericType)\n      generic_base = find_base_generic_instantiation(target_type, base_type)\n      replacer = ReplacePathWithTypeVar.new(base_type, generic_base)\n      base_return_type_node = base_return_type_node.clone\n      base_return_type_node.accept(replacer)\n    end\n\n    base_return_type = base_type.lookup_type?(base_return_type_node)\n    unless base_return_type\n      report_error(base_return_type_node, \"can't resolve return type #{base_return_type_node}\")\n      return\n    end\n\n    return_type_node = method.return_type\n    unless return_type_node\n      report_error(method, \"this method overrides #{Call.def_full_name(base_type, base_method)} which has an explicit return type of #{original_base_return_type}.\\n#{@program.colorize(\"Please add an explicit return type (#{base_return_type} or a subtype of it) to this method as well.\").yellow.bold}\\n\")\n      return\n    end\n\n    return_type = type.lookup_type?(return_type_node)\n    unless return_type\n      report_error(return_type_node, \"can't resolve return type #{return_type_node}\")\n      return\n    end\n\n    unless return_type.implements?(base_return_type)\n      report_error(return_type_node, \"this method must return #{base_return_type}, which is the return type of the overridden method #{Call.def_full_name(base_type, base_method)}, or a subtype of it, not #{return_type}\")\n      return\n    end\n  end\n\n  def check_positional_param_names(impl_method : Def, base_type : Type, base_method : Def)\n    impl_param_count = impl_method.splat_index || impl_method.args.size\n    base_param_count = base_method.splat_index || base_method.args.size\n    {impl_param_count, base_param_count}.min.times do |i|\n      impl_param = impl_method.args[i]\n      base_param = base_method.args[i]\n      unless impl_param.external_name == base_param.external_name\n        @program.warnings.add_warning(impl_param, \"positional parameter '#{impl_param.external_name}' corresponds to parameter '#{base_param.external_name}' of the overridden method #{Call.def_full_name(base_type, base_method)}, which has a different name and may affect named argument passing\")\n      end\n    end\n  end\n\n  def replace_method_arg_paths_with_type_vars(base_type : Type, method : Def, generic_type : GenericInstanceType)\n    replacer = ReplacePathWithTypeVar.new(base_type, generic_type)\n\n    method = method.clone\n    method.args.each do |arg|\n      arg.restriction.try &.accept(replacer)\n    end\n    method\n  end\n\n  def find_base_generic_instantiation(type : Type, base_type : GenericType)\n    type.ancestors.find do |t|\n      t.is_a?(GenericInstanceType) && t.generic_type == base_type\n    end.as(GenericInstanceType)\n  end\n\n  private def this_warning_will_become_an_error\n    @program.colorize(\"The above warning will become an error in a future Crystal version.\").yellow.bold\n  end\n\n  private def report_error(node, message)\n    node.raise(message, nil)\n  end\n\n  # Fictitious nodes for a given def's free vars, used to override type lookup\n  # so that they are never shadowed by existing types. Type lookup will find\n  # these Paths and raise because a Path isn't a type. (`#check_arg` relies on\n  # ignoring undefined types for free vars to work, this should be improved in\n  # the future.)\n  private def free_var_nodes(a_def : Def)\n    a_def.free_vars.try &.to_h do |var|\n      {var, Path.new(var).as(TypeVar)}\n    end\n  end\n\n  class ReplacePathWithTypeVar < Visitor\n    def initialize(@base_type : GenericType, @generic_type : GenericInstanceType)\n    end\n\n    def visit(node : Path)\n      if name = node.single_name?\n        # Check if it matches any of the generic type vars\n        type_var = @generic_type.type_vars[name]?\n        if type_var.is_a?(Var)\n          # Check that it's actually a type parameter on the base type\n          if @base_type.lookup_type?(node).is_a?(TypeParameter)\n            node.type = type_var.type\n          end\n        end\n      end\n\n      false\n    end\n\n    def visit(node : ASTNode)\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/ast.cr",
    "content": "require \"../syntax/ast\"\n\nmodule Crystal\n  class ASTNode\n    def raise(message, inner = nil, exception_type = Crystal::TypeException)\n      ::raise exception_type.for_node(self, message, inner)\n    end\n\n    def simple_literal?\n      case self\n      when Nop, NilLiteral, BoolLiteral, NumberLiteral, CharLiteral,\n           StringLiteral, SymbolLiteral\n        true\n      else\n        false\n      end\n    end\n\n    # `number_autocast` defines if casting numeric expressions to larger ones is enabled\n    def supports_autocast?(number_autocast : Bool = true)\n      case self\n      when NumberLiteral, SymbolLiteral\n        true\n      else\n        if number_autocast\n          case self_type = self.type?\n          when IntegerType\n            self_type.kind.bytesize <= 64\n          when FloatType\n            self_type.kind.f32?\n          else\n            false\n          end\n        else\n          false\n        end\n      end\n    end\n  end\n\n  class Var\n    def initialize(@name : String, @type : Type)\n    end\n  end\n\n  # Fictitious node to represent primitives\n  class Primitive < ASTNode\n    getter name : String\n    property extra : ASTNode?\n\n    def initialize(@name : String, @type : Type? = nil)\n    end\n\n    def clone_without_location\n      Primitive.new(@name, @type)\n    end\n\n    def_equals_and_hash name\n  end\n\n  # Fictitious node to represent a tuple indexer\n  class TupleIndexer < Primitive\n    getter index : TupleInstanceType::Index\n\n    def initialize(@index)\n      super(\"tuple_indexer_known_index\")\n    end\n\n    def clone_without_location\n      TupleIndexer.new(index)\n    end\n\n    def_equals_and_hash index\n  end\n\n  # Fictitious node to represent a type\n  class TypeNode < ASTNode\n    def initialize(@type : Type)\n    end\n\n    def to_macro_id\n      @type.not_nil!.devirtualize.to_s\n    end\n\n    def clone_without_location\n      self\n    end\n\n    def_equals_and_hash type\n  end\n\n  # Fictitious node to represent an assignment with a type restriction,\n  # created to match the assignment of a method argument's default value.\n  class AssignWithRestriction < ASTNode\n    property assign\n    property restriction\n\n    def initialize(@assign : Assign, @restriction : ASTNode)\n    end\n\n    def clone_without_location\n      AssignWithRestriction.new @assign.clone, @restriction.clone\n    end\n\n    def_equals_and_hash assign, restriction\n  end\n\n  class Arg\n    include Annotatable\n\n    # Name of the original arg if its def has been expanded (default arguments)\n    property? original_name : String?\n\n    def initialize(@name : String, @default_value : ASTNode? = nil, @restriction : ASTNode? = nil, external_name : String? = nil, @type : Type? = nil)\n      @external_name = external_name || @name\n    end\n\n    def original_name\n      @original_name || @name\n    end\n\n    def clone_without_location\n      arg = previous_def\n\n      # An arg's type can sometimes be used as a restriction,\n      # and must be preserved when cloned\n      arg.set_type @type\n\n      arg.annotations = @annotations.dup\n      arg.original_name = original_name\n\n      arg\n    end\n  end\n\n  class Def\n    include Annotatable\n\n    property! owner : Type\n    property! original_owner : Type\n    property vars : MetaVars?\n    property yield_vars : Array(Var)?\n    property previous : DefWithMetadata?\n    property next : Def?\n    property special_vars : Set(String)?\n    property freeze_type : Type?\n    property block_nest = 0\n    property? raises = false\n    property? closure = false\n    property? self_closured = false\n    property? captured_block = false\n\n    # `true` if this def has the `@[NoInline]` annotation\n    property? no_inline = false\n\n    # `true` if this def has the `@[AlwaysInline]` annotation\n    property? always_inline = false\n\n    # `true` if this def has the `@[ReturnsTwice]` annotation\n    property? returns_twice = false\n\n    # `true` if this def has the `@[Naked]` annotation\n    property? naked = false\n\n    # Is this a `new` method that was expanded from an initialize?\n    property? new = false\n\n    # Name of the original def if this def has been expanded (default arguments)\n    property? original_name : String?\n\n    @macro_owner : Type?\n\n    # Used to override the meaning of `self` in restrictions\n    property self_restriction_type : Type?\n\n    def macro_owner=(@macro_owner)\n    end\n\n    def macro_owner\n      @macro_owner || @owner\n    end\n\n    def macro_owner?\n      @macro_owner\n    end\n\n    def original_name\n      @original_name || @name\n    end\n\n    def add_special_var(name)\n      special_vars = @special_vars ||= Set(String).new\n      special_vars << name\n    end\n\n    # Returns the minimum and maximum number of positional arguments that must\n    # be passed to this method, assuming positional parameters are not matched\n    # by named arguments.\n    def min_max_args_sizes\n      max_size = args.size\n      default_value_index = args.index(&.default_value)\n      min_size = default_value_index || max_size\n      splat_index = self.splat_index\n      if splat_index\n        min_size = {default_value_index || splat_index, splat_index}.min\n        if args[splat_index].name.empty?\n          max_size = splat_index\n        else\n          if splat_restriction = args[splat_index].restriction\n            min_size += 1 unless splat_restriction.is_a?(Splat) || default_value_index.try(&.< splat_index)\n          end\n          max_size = Int32::MAX\n        end\n      end\n      {min_size, max_size}\n    end\n\n    def clone_without_location\n      a_def = previous_def\n      a_def.previous = previous\n      a_def.raises = raises?\n      a_def.no_inline = no_inline?\n      a_def.always_inline = always_inline?\n      a_def.returns_twice = returns_twice?\n      a_def.naked = naked?\n      a_def.annotations = annotations\n      a_def.new = new?\n      a_def.original_name = original_name?\n      a_def\n    end\n\n    # Yields `arg, arg_index, object, object_index` corresponding\n    # to arguments matching the given objects, taking into account this\n    # def's splat index.\n    def match(objects, &block)\n      Splat.match(self, objects) do |arg, arg_index, object, object_index|\n        yield arg, arg_index, object, object_index\n      end\n    end\n\n    def free_var?(node : Path)\n      free_vars = @free_vars\n      return false unless free_vars\n      name = node.single_name?\n      !name.nil? && free_vars.includes?(name)\n    end\n\n    def free_var?(any)\n      false\n    end\n  end\n\n  class Macro\n    include Annotatable\n\n    property! owner : Type\n\n    # Yields `arg, arg_index, object, object_index` corresponding\n    # to arguments matching the given objects, taking into account this\n    # macro's splat index.\n    def match(objects, &block)\n      Splat.match(self, objects) do |arg, arg_index, object, object_index|\n        yield arg, arg_index, object, object_index\n      end\n    end\n\n    def matches?(call_args, named_args)\n      call_args_size = call_args.size\n      my_args_size = args.size\n      min_args_size = args.index(&.default_value) || my_args_size\n      max_args_size = my_args_size\n      splat_index = self.splat_index\n\n      if splat_index\n        if args[splat_index].external_name.empty?\n          min_args_size = max_args_size = splat_index\n        else\n          min_args_size -= 1\n          max_args_size = Int32::MAX\n        end\n      end\n\n      # If there are arguments past the splat index and no named args, there's no match,\n      # unless all args past it have default values\n      if splat_index && my_args_size > splat_index + 1 && !named_args\n        unless (splat_index + 1...args.size).all? { |i| args[i].default_value }\n          return false\n        end\n      end\n\n      # If there are more positional arguments than those required, there's no match\n      # (if there's less they might be matched with named arguments)\n      if call_args_size > max_args_size\n        return false\n      end\n\n      # If there are named args we must check that all mandatory args\n      # are covered by positional arguments or named arguments.\n      if named_args\n        mandatory_args = BitArray.new(my_args_size)\n      elsif call_args_size < min_args_size\n        # Otherwise, they must be matched by positional arguments\n        return false\n      end\n\n      self.match(call_args) do |my_arg, my_arg_index, call_arg, call_arg_index|\n        mandatory_args[my_arg_index] = true if mandatory_args\n      end\n\n      # Check named args\n      named_args.try &.each do |named_arg|\n        found_index = args.index { |arg| arg.external_name == named_arg.name }\n        if found_index\n          # A named arg can't target the splat index\n          if found_index == splat_index\n            return false\n          end\n\n          # Check whether the named arg refers to an argument that was already specified\n          if mandatory_args\n            if mandatory_args[found_index]\n              return false\n            end\n\n            mandatory_args[found_index] = true\n          else\n            if found_index < call_args_size\n              return false\n            end\n          end\n        else\n          # A double splat matches all named args\n          next if double_splat\n\n          return false\n        end\n      end\n\n      # Check that all mandatory args were specified\n      # (either with positional arguments or with named arguments)\n      if mandatory_args\n        self.args.each_with_index do |arg, index|\n          if index != splat_index && !arg.default_value && !mandatory_args[index]\n            return false\n          end\n        end\n      end\n\n      true\n    end\n  end\n\n  class Splat\n    # Yields `arg, arg_index, object, object_index` corresponding\n    # to def arguments matching the given objects, taking into account the\n    # def's splat index.\n    def self.match(a_def, objects, &block)\n      Splat.before(a_def, objects) do |arg, arg_index, object, object_index|\n        yield arg, arg_index, object, object_index\n      end\n      Splat.at(a_def, objects) do |arg, arg_index, object, object_index|\n        yield arg, arg_index, object, object_index\n      end\n    end\n\n    # Yields `arg, arg_index, object, object_index` corresponding\n    # to arguments before a def's splat index, matching the given objects.\n    # If there are more objects than arguments in the method, they are not yielded.\n    # If splat index is `nil`, all args and objects (with their indices) are yielded.\n    def self.before(a_def, objects, &block)\n      splat = a_def.splat_index || a_def.args.size\n      splat.times do |i|\n        obj = objects[i]?\n        break unless obj\n\n        yield a_def.args[i], i, obj, i\n        i += 1\n      end\n      nil\n    end\n\n    # Yields `arg, arg_index, object, object_index` corresponding\n    # to arguments at a def's splat index, matching the given objects.\n    # If there are more objects than arguments in the method, they are not yielded.\n    # If splat index is `nil`, all args and objects (with their indices) are yielded.\n    def self.at(a_def, objects, &block)\n      splat_index = a_def.splat_index\n      return unless splat_index\n\n      splat_size = Splat.size(a_def, objects, splat_index)\n      splat_size.times do |i|\n        obj_index = splat_index + i\n        obj = objects[obj_index]?\n        break unless obj\n\n        yield a_def.args[splat_index], splat_index, obj, obj_index\n      end\n\n      nil\n    end\n\n    # Returns the splat size of this def matching the given objects.\n    def self.size(a_def, objects, splat_index = a_def.splat_index)\n      if splat_index\n        objects.size - splat_index\n      else\n        0\n      end\n    end\n  end\n\n  class FunDef\n    property! external : External\n  end\n\n  class If\n    # This is set to `true` for an `If` that was created from an `&&` expression.\n    property? and = false\n\n    # This is set to `true` for an `If` that was created from an `||` expression.\n    property? or = false\n\n    # This is set to `true` when the compiler is sure that the condition is truthy\n    property? truthy = false\n\n    # This is set to `true` when the compiler is sure that the condition is falsey\n    property? falsey = false\n\n    def clone_without_location\n      a_if = previous_def\n      a_if.and = and?\n      a_if.or = or?\n      a_if\n    end\n  end\n\n  class MetaVar < ASTNode\n    include SpecialVar\n\n    property name : String\n\n    # This is the context of the variable: who allocates it.\n    # It can either be the Program (for top level variables),\n    # a Def or a Block.\n    property context : ASTNode | NonGenericModuleType | Nil\n\n    property freeze_type : Type?\n\n    # True if we need to mark this variable as nilable\n    # if this variable is read.\n    property? nil_if_read = false\n\n    # A variable is closured if it's used in a ProcLiteral context\n    # where it wasn't created.\n    getter? closured = false\n\n    # Is this metavar assigned a value?\n    property? assigned_to = false\n\n    # Is this metavar closured in a mutable way?\n    # This means it's closured and it got a value assigned to it more than once.\n    # If that's the case, when it's closured then all local variable related to\n    # it will also be bound to it.\n    property? mutably_closured = false\n\n    # Local variables associated with this meta variable.\n    # Can be Var or MetaVar.\n    property(local_vars) { [] of ASTNode }\n\n    def initialize(@name : String, @type : Type? = nil)\n    end\n\n    # Marks this variable as closured.\n    def mark_as_closured\n      @closured = true\n\n      return unless mutably_closured?\n\n      local_vars = @local_vars\n      return unless local_vars\n\n      # If a meta var is not readonly and it became a closure we must\n      # bind all previously related local vars to it so that\n      # they get all types assigned to it.\n      local_vars.each &.bind_to self\n    end\n\n    # True if this variable belongs to the given context\n    # but must be allocated in a closure.\n    def closure_in?(context)\n      closured? && belongs_to?(context)\n    end\n\n    # True if this variable belongs to the given context.\n    def belongs_to?(context)\n      @context.same?(context)\n    end\n\n    # Is this metavar associated with any local vars?\n    def local_vars?\n      @local_vars\n    end\n\n    def ==(other : self)\n      name == other.name\n    end\n\n    def clone_without_location\n      self\n    end\n\n    def inspect(io : IO) : Nil\n      io << name\n      if type = type?\n        io << \" : \"\n        type.to_s(io)\n      end\n      io << \" (nil-if-read)\" if nil_if_read?\n      io << \" (closured)\" if closured?\n      io << \" (mutably-closured)\" if mutably_closured?\n      io << \" (assigned-to)\" if assigned_to?\n      io << \" (object id: #{object_id})\"\n    end\n\n    def pretty_print(pp)\n      pp.text inspect\n    end\n  end\n\n  alias MetaVars = Hash(String, MetaVar)\n\n  # A variable belonging to a type: a global,\n  # class or instance variable (globals belong to the program).\n  class MetaTypeVar < Var\n    include Annotatable\n\n    property nil_reason : NilReason?\n\n    # The owner of this variable, useful for showing good\n    # error messages.\n    property! owner : Type\n\n    # The (optional) initial value of a class variable\n    property initializer : ClassVarInitializer?\n\n    property freeze_type : Type?\n\n    # Flag used during codegen to indicate the initializer is simple\n    # and doesn't require a call to a function\n    property? simple_initializer = false\n\n    # Is this variable thread local? Only applicable\n    # to global and class variables.\n    property? thread_local = false\n\n    # Is this variable \"unsafe\" (no need to check if it was initialized)?\n    property? uninitialized = false\n\n    # Was this class_var already read during the codegen phase?\n    # If not, and we are at the place that declares the class var, we can\n    # directly initialize it now, without checking for an `init` flag.\n    property? read = false\n\n    # If true, there's no need to check whether the class var was initialized or\n    # not when reading it.\n    property? no_init_flag = false\n\n    enum Kind\n      Class\n      Instance\n      Global\n    end\n\n    def kind\n      case name[0]\n      when '@'\n        if name[1] == '@'\n          Kind::Class\n        else\n          Kind::Instance\n        end\n      else\n        Kind::Global\n      end\n    end\n\n    def global?\n      kind.global?\n    end\n  end\n\n  class ClassVar\n    # The \"real\" variable associated with this node,\n    # belonging to a type.\n    property! var : MetaTypeVar\n  end\n\n  class Global\n    property! var : MetaTypeVar\n  end\n\n  class Path\n    property target_const : Const?\n    property target_type : Type?\n    property syntax_replacement : ASTNode?\n  end\n\n  class Call\n    property before_vars : MetaVars?\n\n    def clone_without_location\n      cloned = previous_def\n\n      # This is needed because this call might have resolved\n      # to a macro and has an expansion.\n      cloned.expanded = expanded.clone\n\n      cloned\n    end\n  end\n\n  class Block\n    property scope : Type?\n    property vars : MetaVars?\n    property after_vars : MetaVars?\n    property context : Def | NonGenericModuleType | Nil\n    property fun_literal : ASTNode?\n    property freeze_type : Type?\n    property? visited = false\n\n    getter(:break) { Var.new(\"%break\") }\n  end\n\n  class While\n    property break_vars : Array(MetaVars)?\n\n    def has_breaks?\n      !!@break_vars\n    end\n  end\n\n  class Break\n    property! target : ASTNode\n  end\n\n  class Next\n    property! target : ASTNode\n  end\n\n  class Return\n    property! target : Def\n  end\n\n  class IsA\n    property syntax_replacement : Call?\n  end\n\n  module ExpandableNode\n    property expanded : ASTNode?\n  end\n\n  {% for name in %w(And Or\n                   ArrayLiteral HashLiteral RegexLiteral RangeLiteral\n                   Case StringInterpolation\n                   MacroExpression MacroIf MacroFor MacroVerbatim MultiAssign\n                   SizeOf InstanceSizeOf AlignOf InstanceAlignOf OffsetOf Global Require Select) %}\n    class {{name.id}}\n      include ExpandableNode\n    end\n  {% end %}\n\n  class ClassDef\n    property! resolved_type : ClassType\n  end\n\n  class ModuleDef\n    property! resolved_type : ModuleType\n  end\n\n  class LibDef\n    property! resolved_type : LibType\n  end\n\n  class CStructOrUnionDef\n    property! resolved_type : NonGenericClassType\n  end\n\n  class Alias\n    property! resolved_type : AliasType\n  end\n\n  class AnnotationDef\n    include Annotatable\n\n    property! resolved_type : AnnotationType\n  end\n\n  class External < Def\n    property? external_var : Bool = false\n    property real_name : String\n    property! fun_def : FunDef\n    property call_convention : LLVM::CallConvention?\n    property wasm_import_module : String?\n\n    property? dead = false\n    property? used = false\n    property? varargs = false\n\n    # An External is also used to represent external variables\n    # such as libc's `$errno`, which can be annotated with\n    # `@[ThreadLocal]`. This property is `true` in that case.\n    property? thread_local = false\n\n    def initialize(name : String, args : Array(Arg), body, @real_name : String)\n      super(name, args, body, nil, nil, nil)\n    end\n\n    def mangled_name(program, obj_type)\n      real_name\n    end\n\n    def compatible_with?(other)\n      return false if args.size != other.args.size\n      return false if varargs? != other.varargs?\n\n      args.each_with_index do |arg, i|\n        return false if arg.type != other.args[i].type\n      end\n\n      type == other.type\n    end\n\n    def_hash @real_name, @varargs, @fun_def\n  end\n\n  class EnumDef\n    property! resolved_type : EnumType\n  end\n\n  class Yield\n    property expanded : Call?\n  end\n\n  class NilReason\n    enum Reason\n      UsedBeforeInitialized\n      UsedSelfBeforeInitialized\n      InitializedInRescue\n    end\n\n    getter name : String\n    getter reason : Reason\n    getter nodes : Array(ASTNode)?\n    getter scope : Type?\n\n    def initialize(@name, @reason : Reason, @nodes = nil, @scope = nil)\n    end\n  end\n\n  class Asm\n    property output_ptrofs : Array(PointerOf)?\n  end\n\n  # Fictitious node that means \"all these nodes come from this file\"\n  class FileNode < ASTNode\n    property node : ASTNode\n    property filename : String\n\n    def initialize(@node : ASTNode, @filename : String)\n    end\n\n    def accept_children(visitor)\n      @node.accept visitor\n    end\n\n    def clone_without_location\n      self\n    end\n\n    def_equals_and_hash node, filename\n  end\n\n  class Assign\n    # Whether a class variable assignment needs to be skipped\n    # because it was replaced with another initializer\n    #\n    # ```\n    # class Foo\n    #   @@x = 1 # This will never execute\n    #   @@x = 2\n    # end\n    # ```\n    property? discarded = false\n  end\n\n  class TypeDeclaration\n    # Whether a class variable assignment needs to be skipped\n    # because it was replaced with another initializer\n    #\n    # ```\n    # class Foo\n    #   @@x : Int32 = 1 # This will never execute\n    #   @@x : Int32 = 2\n    # end\n    # ```\n    property? discarded = false\n  end\n\n  # Fictitious node to represent an id inside a macro\n  class MacroId < ASTNode\n    property value : String\n\n    def initialize(@value)\n    end\n\n    def to_macro_id\n      @value\n    end\n\n    def clone_without_location\n      self\n    end\n\n    def_equals_and_hash value\n  end\n\n  # Fictitious node representing a variable in macros\n  class MetaMacroVar < ASTNode\n    property name : String\n    property default_value : ASTNode?\n\n    # The instance variable associated with this meta macro var\n    property! var : MetaTypeVar\n\n    def initialize(@name, @type)\n    end\n\n    def self.class_desc\n      \"MetaVar\"\n    end\n\n    def clone_without_location\n      self\n    end\n  end\n\n  # Fictitious node to mean a location in code shouldn't be reached.\n  # This is used in the implicit `else` branch of a case.\n  class Unreachable < ASTNode\n    def clone_without_location\n      Unreachable.new\n    end\n  end\n\n  class ProcLiteral\n    # If this ProcLiteral was created from expanding a ProcPointer,\n    # this holds the reference to it.\n    property proc_pointer : ProcPointer?\n  end\n\n  class ProcPointer\n    property expanded : ASTNode?\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/bindings.cr",
    "content": "module Crystal\n  # Specialized container for ASTNodes to use for bindings tracking.\n  #\n  # The average number of elements in both dependencies and observers is below 2\n  # for ASTNodes. This struct inlines the first two elements saving up 4\n  # allocations per node (two arrays, with a header and buffer for each) but we\n  # need to pay a slight extra cost in memory upfront: a total of 6 pointers (48\n  # bytes) vs 2 pointers (16 bytes). The other downside is that since this is a\n  # struct, we need to be careful with mutation.\n  struct SmallNodeList\n    include Enumerable(ASTNode)\n\n    @first : ASTNode?\n    @second : ASTNode?\n    @tail : Array(ASTNode)?\n\n    def each(& : ASTNode ->)\n      yield @first || return\n      yield @second || return\n      @tail.try(&.each { |node| yield node })\n    end\n\n    def size\n      if @first.nil?\n        0\n      elsif @second.nil?\n        1\n      elsif (tail = @tail).nil?\n        2\n      else\n        2 + tail.size\n      end\n    end\n\n    def push(node : ASTNode) : self\n      if @first.nil?\n        @first = node\n      elsif @second.nil?\n        @second = node\n      elsif (tail = @tail).nil?\n        @tail = [node] of ASTNode\n      else\n        tail.push(node)\n      end\n      self\n    end\n\n    def reject!(& : ASTNode ->) : self\n      if first = @first\n        if second = @second\n          if tail = @tail\n            tail.reject! { |node| yield node }\n          end\n          if yield second\n            @second = tail.try &.shift?\n          end\n        end\n        if yield first\n          @first = @second\n          @second = tail.try &.shift?\n        end\n      end\n      self\n    end\n\n    def concat(nodes : Enumerable(ASTNode)) : self\n      nodes.each { |node| self.push(node) }\n      self\n    end\n  end\n\n  class ASTNode\n    getter dependencies : SmallNodeList = SmallNodeList.new\n    @observers : SmallNodeList = SmallNodeList.new\n    property enclosing_call : Call?\n\n    @dirty = false\n\n    @type : Type?\n\n    def type\n      type? || ::raise \"BUG: `#{self}` at #{self.location} has no type\"\n    end\n\n    def type?\n      @type || freeze_type\n    end\n\n    def type(*, with_autocast = false)\n      type = self.type\n\n      if with_autocast\n        case self\n        when NumberLiteral\n          NumberAutocastType.new(type.program, self)\n        when SymbolLiteral\n          SymbolAutocastType.new(type.program, self)\n        else\n          case type\n          when IntegerType, FloatType\n            NumberAutocastType.new(type.program, self)\n          else\n            type\n          end\n        end\n      else\n        type\n      end\n    end\n\n    def set_type(type : Type)\n      type = type.remove_alias_if_simple\n      if !type.no_return? && (freeze_type = self.freeze_type) && !type.implements?(freeze_type)\n        raise_frozen_type freeze_type, type, self\n      end\n      @type = type\n    end\n\n    def set_type(type : Nil)\n      @type = type\n    end\n\n    def set_type_from(type, from)\n      set_type type\n    rescue ex : FrozenTypeException\n      # See if we can find where the mismatched type came from\n      if from && !ex.inner && (freeze_type = self.freeze_type) && type.is_a?(UnionType) && type.includes_type?(freeze_type) && type.union_types.size == 2\n        other_type = type.union_types.find { |type| type != freeze_type }\n        trace = from.find_owner_trace(freeze_type.program, other_type)\n        ex.inner = trace\n      elsif from && !ex.inner && (freeze_type = self.freeze_type)\n        trace = from.find_owner_trace(freeze_type.program, type)\n        ex.inner = trace\n      end\n\n      if from && !location\n        from.raise ex.message, ex.inner\n      else\n        ::raise ex\n      end\n    end\n\n    def freeze_type\n      nil\n    end\n\n    def raise_frozen_type(freeze_type, invalid_type, from)\n      if !freeze_type.includes_type?(invalid_type.program.nil) && invalid_type.includes_type?(invalid_type.program.nil)\n        # This means that an instance variable become nil\n        if self.is_a?(MetaTypeVar) && (nil_reason = self.nil_reason)\n          inner = MethodTraceException.new(nil, [] of ASTNode, nil_reason, freeze_type.program.show_error_trace?)\n        end\n      end\n\n      case self\n      when MetaTypeVar\n        if self.global?\n          from.raise \"global variable '#{self.name}' must be #{freeze_type}, not #{invalid_type}\", inner, Crystal::FrozenTypeException\n        else\n          from.raise \"#{self.kind.to_s.underscore} variable '#{self.name}' of #{self.owner} must be #{freeze_type}, not #{invalid_type}\", inner, Crystal::FrozenTypeException\n        end\n      when Def\n        (self.return_type || self).raise \"method #{self.short_reference} must return #{freeze_type} but it is returning #{invalid_type}\", inner, Crystal::FrozenTypeException\n      when NamedType\n        from.raise \"type #{self.full_name} must be #{freeze_type}, not #{invalid_type}\", inner, Crystal::FrozenTypeException\n      else\n        from.raise \"type must be #{freeze_type}, not #{invalid_type}\", inner, Crystal::FrozenTypeException\n      end\n    end\n\n    def type=(type)\n      return if @type.same?(type) || (!type && !@type)\n\n      set_type(type)\n      notify_observers\n      @type\n    end\n\n    def bind_to(node : ASTNode) : Nil\n      bind(node) do\n        @dependencies.push node\n        node.add_observer self\n      end\n    end\n\n    def bind_to(nodes : Indexable) : Nil\n      return if nodes.empty?\n\n      bind do\n        @dependencies.concat nodes\n        nodes.each &.add_observer self\n      end\n    end\n\n    def bind(from = nil, &)\n      # Quick check to provide a better error message when assigning a type\n      # to a variable whose type is frozen\n      if self.is_a?(MetaTypeVar) && (freeze_type = self.freeze_type) && from &&\n         (from_type = from.type?) && !from_type.implements?(freeze_type)\n        raise_frozen_type freeze_type, from_type, from\n      end\n\n      yield\n\n      new_type = type_from_dependencies\n      new_type = map_type(new_type) if new_type\n\n      if new_type && (freeze_type = self.freeze_type)\n        new_type = restrict_type_to_freeze_type(freeze_type, new_type)\n      end\n\n      return if @type.same? new_type\n      return unless new_type\n\n      set_type_from(new_type, from)\n      @dirty = true\n      propagate\n    end\n\n    def type_from_dependencies : Type?\n      Type.merge @dependencies\n    end\n\n    def unbind_from(nodes : Nil)\n      # Nothing to do\n    end\n\n    def unbind_from(node : ASTNode)\n      @dependencies.reject! &.same?(node)\n      node.remove_observer self\n    end\n\n    def unbind_from(nodes : Enumerable(ASTNode))\n      @dependencies.reject! { |dep| nodes.any? &.same?(dep) }\n      nodes.each &.remove_observer self\n    end\n\n    def add_observer(observer)\n      @observers.push observer\n    end\n\n    def remove_observer(observer)\n      @observers.try &.reject! &.same?(observer)\n    end\n\n    def set_enclosing_call(enclosing_call)\n      current_enclosing_call = @enclosing_call\n      if current_enclosing_call\n        # This can happen when a block is typed, and meanwhile a new\n        # generic instance type is created that triggers the block to\n        # be typed again, potentially analyzing a call twice.\n        unless current_enclosing_call.same?(enclosing_call)\n          raise \"BUG: already had a different enclosing call\"\n        end\n      else\n        @enclosing_call = enclosing_call\n      end\n    end\n\n    def remove_enclosing_call(enclosing_call)\n      @enclosing_call = nil if @enclosing_call.same?(enclosing_call)\n    end\n\n    def notify_observers\n      @observers.try &.each &.update self\n      @enclosing_call.try &.recalculate\n      @observers.try &.each &.propagate\n      @enclosing_call.try &.propagate\n    end\n\n    def update(from = nil)\n      return if @type && @type.same? from.try &.type?\n\n      new_type = type_from_dependencies\n      new_type = map_type(new_type) if new_type\n\n      if new_type && (freeze_type = self.freeze_type)\n        new_type = restrict_type_to_freeze_type(freeze_type, new_type)\n      end\n\n      return if @type.same? new_type\n\n      if new_type\n        set_type_from(new_type, from)\n      else\n        return unless @type\n\n        set_type(nil)\n      end\n\n      @dirty = true\n    end\n\n    def propagate\n      if @dirty\n        @dirty = false\n        notify_observers\n      end\n    end\n\n    def map_type(type)\n      type\n    end\n\n    # Computes the type resulting from assigning type to freeze_type,\n    # in the case where freeze_type is not nil.\n    #\n    # Special cases are listed inside the method body.\n    def restrict_type_to_freeze_type(freeze_type, type)\n      if freeze_type.is_a?(ProcInstanceType)\n        # We allow assigning Proc(*T, R) to Proc(*T, Nil)\n        if freeze_type.return_type.nil_type? &&\n           type.all? { |a_type|\n             a_type.is_a?(ProcInstanceType) && a_type.arg_types == freeze_type.arg_types\n           }\n          return freeze_type\n        end\n\n        # We also allow assigning Proc(*T, NoReturn) to Proc(*T, U)\n        if type.all? { |a_type|\n             a_type.is_a?(ProcInstanceType) &&\n             (a_type.return_type.is_a?(NoReturnType) || a_type.return_type == freeze_type.return_type) &&\n             a_type.arg_types == freeze_type.arg_types\n           }\n          return freeze_type\n        end\n      end\n\n      type\n    end\n\n    def find_owner_trace(program, owner)\n      owner_trace = [] of ASTNode\n      node = self\n\n      visited = Set(ASTNode).new.compare_by_identity\n      owner_trace << node if node.type?.try &.includes_type?(owner)\n      visited.add node\n      while node = node.dependencies.find { |dep| dep.type? && dep.type.includes_type?(owner) && !visited.includes?(dep) }\n        nil_reason = node.nil_reason if node.is_a?(MetaTypeVar)\n        owner_trace << node if node\n        visited.add node\n      end\n\n      MethodTraceException.new(owner, owner_trace, nil_reason, program.show_error_trace?)\n    end\n  end\n\n  class Def\n    def map_type(type)\n      # When we have Nil forced as a return type, NoReturn still\n      # wins, so we must account for this case.\n      # Otherwise we simply keep having the Nil type.\n      if freeze_type.try &.nil_type? && !type.no_return?\n        freeze_type\n      else\n        type\n      end\n    end\n  end\n\n  class PointerOf\n    def map_type(type)\n      old_type = self.type?\n      new_type = type.program.pointer_of(type)\n      if old_type && grew?(old_type, new_type)\n        raise \"recursive pointerof expansion: #{old_type}, #{new_type}, ...\"\n      end\n\n      new_type\n    end\n\n    def grew?(old_type, new_type)\n      new_type = new_type.as(PointerInstanceType)\n      element_type = new_type.element_type\n      type_includes?(element_type, old_type)\n    end\n\n    def type_includes?(haystack, needle)\n      return true if haystack == needle\n\n      case haystack\n      when UnionType\n        haystack.union_types.any? { |sub| type_includes?(sub, needle) }\n      when GenericClassInstanceType\n        splat_index = haystack.generic_type.splat_index\n        haystack.type_vars.each_with_index do |(_, sub), index|\n          if sub.is_a?(Var)\n            if index == splat_index\n              return true if sub.type.as(TupleInstanceType).tuple_types.any? { |sub2| type_includes?(sub2, needle) }\n            else\n              return true if type_includes?(sub.type, needle)\n            end\n          end\n        end\n        false\n      else\n        false\n      end\n    end\n  end\n\n  class TypeOf\n    property? in_type_args = false\n\n    def map_type(type)\n      @in_type_args ? type : type.metaclass\n    end\n\n    def update(from = nil)\n      super\n      propagate\n    end\n  end\n\n  class ExceptionHandler\n    def map_type(type)\n      if (ensure_type = @ensure.try &.type?).try &.is_a?(NoReturnType)\n        ensure_type\n      else\n        type\n      end\n    end\n  end\n\n  class Union\n    property? inside_is_a = false\n\n    def update(from = nil)\n      computed_types = types.compact_map do |subtype|\n        instance_type = subtype.type?\n        next unless instance_type\n\n        unless instance_type.can_be_stored?\n          subtype.raise \"can't use #{instance_type} in unions yet, use a more specific type\"\n        end\n        instance_type.virtual_type\n      end\n\n      return if computed_types.empty?\n\n      program = computed_types.first.program\n\n      if inside_is_a?\n        self.type = program.type_merge_union_of(computed_types)\n      else\n        self.type = program.type_merge(computed_types)\n      end\n    end\n  end\n\n  class Cast\n    property? upcast = false\n\n    def update(from = nil)\n      to_type = to.type?\n      return unless to_type\n\n      program = to_type.program\n\n      case to_type\n      when program.object\n        raise \"can't cast to Object yet\"\n      when program.reference\n        raise \"can't cast to Reference yet\"\n      when program.class_type\n        raise \"can't cast to Class yet\"\n      end\n\n      obj_type = obj.type?\n\n      if obj_type.is_a?(PointerInstanceType)\n        to_type_instance_type = to_type.instance_type\n        if to_type_instance_type.is_a?(GenericType)\n          raise \"can't cast #{obj_type} to #{to_type_instance_type}\"\n        end\n      end\n\n      @upcast = false\n\n      if obj_type && !(obj_type.pointer? || to_type.pointer?)\n        filtered_type = obj_type.filter_by(to_type)\n\n        # If the filtered type didn't change it means that an\n        # upcast is being made, for example:\n        #\n        #   1 as Int32 | Float64\n        #   Bar.new as Foo # where Bar < Foo\n        if obj_type == filtered_type && !to_type.is_a?(GenericClassType) &&\n           to_type.can_be_stored?\n          filtered_type = to_type\n          @upcast = true\n        end\n      end\n\n      # If we couldn't filter the type and we are casting to something that\n      # isn't allowed in variables (like Int or uninstantiated Array(T))\n      # we can't guess a type.\n      return if !filtered_type && !to_type.can_be_stored?\n\n      # If we don't have a matching type, leave it as the to_type:\n      # later (in cleanup) we will check again.\n      filtered_type ||= to_type\n      self.type = filtered_type.virtual_type\n    end\n  end\n\n  class NilableCast\n    property? upcast = false\n    getter! non_nilable_type : Type\n\n    def update(from = nil)\n      to_type = to.type?\n      return unless to_type\n\n      program = to_type.program\n\n      case to_type\n      when program.object\n        raise \"can't cast to Object yet\"\n      when program.reference\n        raise \"can't cast to Reference yet\"\n      when program.class_type\n        raise \"can't cast to Class yet\"\n      end\n\n      obj_type = obj.type?\n\n      if obj_type.is_a?(PointerInstanceType)\n        to_type_instance_type = to_type.instance_type\n        if to_type_instance_type.is_a?(GenericType)\n          raise \"can't cast #{obj_type} to #{to_type_instance_type}\"\n        end\n      end\n\n      @upcast = false\n\n      if obj_type\n        filtered_type = obj_type.filter_by(to_type)\n\n        # If the filtered type didn't change it means that an\n        # upcast is being made, for example:\n        #\n        #   1 as Int32 | Float64\n        #   Bar.new as Foo # where Bar < Foo\n        if obj_type == filtered_type && !to_type.is_a?(GenericClassType) &&\n           to_type.can_be_stored?\n          filtered_type = to_type.virtual_type\n          @upcast = true\n        end\n      end\n\n      # If we couldn't filter the type and we are casting to something that\n      # isn't allowed in variables (like Int or uninstantiated Array(T))\n      # we can't guess a type.\n      if !filtered_type && !to_type.can_be_stored?\n        self.type = to_type.program.nil_type\n        return\n      end\n\n      # If we don't have a matching type, leave it as the to_type:\n      # later (in cleanup) we will check again.\n      filtered_type ||= to_type\n      filtered_type = filtered_type.virtual_type\n\n      @non_nilable_type = filtered_type\n\n      # The final type is nilable\n      self.type = filtered_type.program.nilable(filtered_type)\n    end\n  end\n\n  class ProcLiteral\n    property? force_nil = false\n    property expected_return_type : Type?\n    property? from_block = false\n\n    def update(from = nil)\n      return unless self.def.args.all? &.type?\n      return unless self.def.type?\n\n      types = self.def.args.map &.type.virtual_type\n      return_type = @force_nil ? self.def.type.program.nil : self.def.type.virtual_type\n\n      expected_return_type = @expected_return_type\n      if expected_return_type && !expected_return_type.nil_type? && !return_type.implements?(expected_return_type)\n        raise \"expected #{from_block? ? \"block\" : \"Proc\"} to return #{expected_return_type.devirtualize}, not #{return_type}\"\n      end\n\n      types << (expected_return_type || return_type)\n\n      self.type = self.def.type.program.proc_of(types)\n    end\n\n    def return_type\n      @type.as(ProcInstanceType).return_type\n    end\n  end\n\n  class ProcPointer\n    property! call : Call\n\n    def map_type(type)\n      if self.expanded\n        return type\n      end\n\n      return nil unless call.type?\n\n      arg_types = call.args.map &.type.virtual_type\n      arg_types.push call.type.virtual_type\n\n      call.type.program.proc_of(arg_types)\n    end\n  end\n\n  class Generic\n    property! instance_type : GenericType\n    property scope : Type?\n    property? in_type_args = false\n    property? inside_is_a = false\n\n    def update(from = nil)\n      instance_type = self.instance_type\n      if instance_type.is_a?(NamedTupleType)\n        entries = Array(NamedArgumentType).new(named_args.try(&.size) || 0)\n        named_args.try &.each do |named_arg|\n          node = named_arg.value\n\n          if node.is_a?(Path) && (syntax_replacement = node.syntax_replacement)\n            node = syntax_replacement\n          end\n\n          if node.is_a?(NumberLiteral)\n            node.raise \"can't use number as type for NamedTuple\"\n          end\n\n          node_type = node.type?\n          return unless node_type\n\n          if node.is_a?(Path) && node.target_const\n            node.raise \"can't use constant as type for NamedTuple\"\n          end\n\n          unless node_type.can_be_stored?\n            node.raise \"can't use #{node_type} as generic type argument yet, use a more specific type\"\n          end\n\n          node_type = node_type.virtual_type\n\n          entries << NamedArgumentType.new(named_arg.name, node_type)\n        end\n\n        generic_type = instance_type.instantiate_named_args(entries)\n      else\n        type_vars_types = Array(TypeVar).new(type_vars.size + 1)\n        type_vars.each do |node|\n          if node.is_a?(Path) && (syntax_replacement = node.syntax_replacement)\n            node = syntax_replacement\n          end\n          if node.is_a?(SizeOf) && (expanded = node.expanded)\n            node = expanded\n          end\n          if node.is_a?(InstanceSizeOf) && (expanded = node.expanded)\n            node = expanded\n          end\n          if node.is_a?(AlignOf) && (expanded = node.expanded)\n            node = expanded\n          end\n          if node.is_a?(InstanceAlignOf) && (expanded = node.expanded)\n            node = expanded\n          end\n          if node.is_a?(OffsetOf) && (expanded = node.expanded)\n            node = expanded\n          end\n\n          case node\n          when NumberLiteral\n            type_var = node\n          when Splat\n            type = node.type?\n            return unless type.is_a?(TupleInstanceType)\n\n            type_vars_types.concat(type.tuple_types)\n            next\n          else\n            node_type = node.type?\n            return unless node_type\n\n            # If the Path points to a constant, we solve it and use it if it's a number literal\n            if node.is_a?(Path) && (target_const = node.target_const)\n              value = target_const.value\n              if value.is_a?(NumberLiteral)\n                type_var = value\n              else\n                # Try to interpret the value\n                visitor = target_const.visitor\n                if visitor\n                  numeric_value = visitor.interpret_enum_value(value)\n                  numeric_type = node_type.program.int?(numeric_value) || raise \"BUG: expected integer type, not #{numeric_value.class}\"\n                  type_var = NumberLiteral.new(numeric_value.to_s, numeric_type.kind)\n                  type_var.set_type_from(numeric_type, from)\n                else\n                  node.raise \"can't use constant #{node} (value = #{value}) as generic type argument, it must be a numeric constant\"\n                end\n              end\n            else\n              unless node_type.can_be_stored?\n                node.raise \"can't use #{node_type} as generic type argument yet, use a more specific type\"\n              end\n              type_var = node_type.virtual_type\n            end\n          end\n\n          type_vars_types << type_var\n        end\n\n        begin\n          generic_instance_type = instance_type.as(GenericType)\n          generic_type =\n            if generic_instance_type.is_a?(GenericUnionType) && inside_is_a?\n              # In the case of `exp.is_a?(Union(X, Y))` we make it work exactly\n              # like `exp.is_a?(X | Y)`, which won't resolve `X | Y` to the virtual\n              # parent type.\n              generic_instance_type.instantiate(type_vars_types, type_merge_union_of: true)\n            else\n              generic_instance_type.instantiate(type_vars_types)\n            end\n        rescue ex : Crystal::CodeError\n          raise ex.message, ex\n        end\n      end\n\n      if generic_type_too_nested?(generic_type.generic_nest)\n        raise \"generic type too nested: #{generic_type}\"\n      end\n\n      generic_type = generic_type.metaclass unless @in_type_args\n      self.type = generic_type\n    end\n  end\n\n  class TupleLiteral\n    property! program : Program\n\n    def update(from = nil)\n      types = [] of TypeVar\n      elements.each do |node|\n        if node.is_a?(Splat)\n          type = node.type?\n          return unless type.is_a?(TupleInstanceType)\n          types.concat(type.tuple_types)\n        else\n          type = node.type?\n          return unless type\n          types << type\n        end\n      end\n\n      tuple_type = program.tuple_of types\n\n      if generic_type_too_nested?(tuple_type.generic_nest)\n        raise \"tuple type too nested: #{tuple_type}\"\n      end\n\n      if types.size > 300\n        raise \"tuple size cannot be greater than 300 (size is #{types.size})\"\n      end\n\n      self.type = tuple_type\n    end\n  end\n\n  class NamedTupleLiteral\n    property! program : Program\n\n    def update(from = nil)\n      return unless entries.all? &.value.type?\n\n      entries = self.entries.map do |element|\n        NamedArgumentType.new(element.key, element.value.type)\n      end\n\n      named_tuple_type = program.named_tuple_of(entries)\n\n      if generic_type_too_nested?(named_tuple_type.generic_nest)\n        raise \"named tuple type too nested: #{named_tuple_type}\"\n      end\n\n      if entries.size > 300\n        raise \"named tuple size cannot be greater than 300 (size is #{entries.size})\"\n      end\n\n      self.type = named_tuple_type\n    end\n  end\n\n  class ReadInstanceVar\n    def update(from = nil)\n      obj_type = obj.type?\n      return unless obj_type\n\n      self.type =\n        if obj_type.is_a?(UnionType)\n          obj_type.program.type_merge(\n            obj_type.union_types.map do |union_type|\n              lookup_instance_var(union_type).type\n            end\n          )\n        else\n          lookup_instance_var(obj_type).type\n        end\n    end\n\n    private def lookup_instance_var(type)\n      ivar = type.lookup_instance_var(self)\n      unless ivar\n        similar_name = type.lookup_similar_instance_var_name(name)\n        type.program.undefined_instance_variable(self, type, similar_name)\n      end\n      ivar\n    end\n  end\n\n  class Not\n    def update(from = nil)\n      type = exp.type?\n      return unless type\n\n      self.type = type.no_return? ? type : type.program.bool\n    end\n  end\n\n  class Block\n    property binder : YieldBlockBinder?\n  end\n\n  # Fictitious node to bind yield expressions to block arguments\n  class YieldBlockBinder < ASTNode\n    getter block\n\n    def initialize(@program : Program, @block : Block)\n      @yields = [] of {Yield, Array(Var)?}\n    end\n\n    def add_yield(node : Yield, yield_vars : Array(Var)?)\n      @yields << {node, yield_vars}\n      node.exps.each &.add_observer(self)\n    end\n\n    def update(from = nil)\n      # We compute all the types for each block arguments\n      block_arg_types = Array(Array(Type)?).new(block.args.size, nil)\n\n      @yields.each do |a_yield, yield_vars|\n        gather_yield_block_arg_types(a_yield, yield_vars, block, block_arg_types)\n      end\n\n      block.args.each_with_index do |arg, i|\n        block_arg_type = block_arg_types[i]\n        if block_arg_type\n          arg_type = Type.merge(block_arg_type) || @program.nil\n          if i == block.splat_index && !arg_type.is_a?(TupleInstanceType)\n            arg.raise \"yield argument to block splat parameter must be a Tuple, not #{arg_type}\"\n          end\n          arg.type = arg_type\n        else\n          # Skip, no type info found in this position\n        end\n      end\n    end\n\n    # Gather all exps types and then assign to block_arg_types.\n    # We need to do that in case of a block splat argument, we need\n    # to split and create tuple types for that case.\n    private def gather_yield_block_arg_types(a_yield, yield_vars, block, block_arg_types)\n      args_size = block.args.size\n      splat_index = block.splat_index\n      exps_types = Array(Type).new(a_yield.exps.size)\n\n      i = 0\n      a_yield.exps.each do |exp|\n        exp_type = exp.type?\n        return unless exp_type\n\n        if exp.is_a?(Splat)\n          unless exp_type.is_a?(TupleInstanceType)\n            exp.raise \"expected splat expression to be a tuple type, not #{exp_type}\"\n          end\n\n          exps_types.concat(exp_type.tuple_types)\n          i += exp_type.tuple_types.size\n        else\n          exps_types << exp_type\n          i += 1\n        end\n      end\n\n      if splat_index\n        # Error if there are less expressions than the number of block arguments\n        if exps_types.size < (args_size - 1)\n          block.raise \"too many block parameters (given #{args_size - 1}+, expected maximum #{exps_types.size})\"\n        end\n        splat_range = (splat_index..splat_index - args_size)\n        exps_types[splat_range] = @program.tuple_of(exps_types[splat_range])\n      end\n\n      # Check if there are missing yield expressions to match\n      # the (optional) block signature, and if they match the declared types\n      if yield_vars\n        if exps_types.size < yield_vars.size\n          a_yield.raise \"wrong number of yield arguments (given #{exps_types.size}, expected #{yield_vars.size})\"\n        end\n\n        # Check that the types match\n        i = 0\n        yield_vars.zip(exps_types) do |yield_var, exp_type|\n          unless exp_type.implements?(yield_var.type)\n            a_yield.raise \"argument ##{i + 1} of yield expected to be #{yield_var.type}, not #{exp_type}\"\n          end\n          i += 1\n        end\n      end\n\n      # Check if tuple unpacking is needed\n      if exps_types.size == 1 &&\n         (exp_type = exps_types.first).is_a?(TupleInstanceType) &&\n         args_size > 1 &&\n         !splat_index\n        exps_types = exp_type.tuple_types\n      end\n\n      # Now move exps_types to block_arg_types\n      if block.args.size > exps_types.size\n        block.raise \"too many block parameters (given #{block.args.size}, expected maximum #{exps_types.size})\"\n      end\n\n      exps_types.each_with_index do |exp_type, i|\n        break if i >= block_arg_types.size\n\n        types = block_arg_types[i] ||= [] of Type\n        types << exp_type\n      end\n    end\n\n    def clone_without_location\n      self\n    end\n  end\nend\n\n# TODO: 300 is a pretty big number for the number of nested generic instantiations,\n# (think Array(Array(Array(Array(Array(Array(Array(Array(Array(Array(Array(...))))))))))\n# but we might want to implement an algorithm that correctly identifies this\n# infinite recursion.\nprivate def generic_type_too_nested?(nest_level)\n  nest_level > 300\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/call.cr",
    "content": "require \"levenshtein\"\nrequire \"../syntax/ast\"\nrequire \"../types\"\nrequire \"./type_lookup\"\n\nclass Crystal::Call\n  property! scope : Type\n  property with_scope : Type?\n  property! parent_visitor : MainVisitor\n  property target_defs : Array(Def)?\n  property expanded : ASTNode?\n  property expanded_macro : Macro?\n  property? uses_with_scope = false\n\n  class RetryLookupWithLiterals < ::Exception\n    def initialize\n      self.callstack = Exception::CallStack.empty\n    end\n  end\n\n  def program\n    scope.program\n  end\n\n  def target_def\n    if defs = @target_defs\n      if defs.size == 1\n        return defs.first\n      else\n        ::raise \"#{defs.size} target defs for #{self}\"\n      end\n    end\n\n    ::raise \"Zero target defs for #{self}\"\n  end\n\n  def recalculate\n    obj = @obj\n    obj_type = obj.type? if obj\n\n    case obj_type\n    when NoReturnType\n      # A call on NoReturn will be NoReturn, so there's nothing to do\n      return\n    when LibType\n      # `LibFoo.call` has a separate logic\n      return recalculate_lib_call obj_type\n    end\n\n    # Check if its call is inside LibFoo\n    # (can happen when assigning the call to a constant)\n    if !obj && (lib_type = scope()).is_a?(LibType)\n      return recalculate_lib_call lib_type\n    end\n\n    check_not_lib_out_args\n\n    # We can't type a call if any argument has a NoReturn type\n    #\n    # Note: we could make `NoReturn` match any type and instantiate methods,\n    # but it's a bit pointless because the call will never be made if we got\n    # there with something that's NoReturn.\n    #\n    # The only problem is that we might be missing out some errors, for example:\n    #\n    # ```\n    # def foo(x, y : Int32)\n    # end\n    #\n    # x = exit\n    #\n    # # Here the second argument should produce an error, but it doesn't\n    # foo(x, \"y\")\n    # ```\n    #\n    # So this is definitely a tradeoff.\n    return if args.any? &.type?.try &.no_return?\n    return if named_args.try &.any? &.value.type?.try &.no_return?\n\n    return unless obj_and_args_types_set?\n\n    block = @block\n\n    unbind_from @target_defs if @target_defs\n    unbind_from block.break if block\n\n    @target_defs = nil\n\n    if block_arg = @block_arg\n      replace_block_arg_with_block(block_arg)\n    end\n\n    matches = lookup_matches\n\n    # If @target_defs is set here it means there was a recalculation\n    # fired as a result of a recalculation. We keep the last one.\n\n    return if @target_defs\n\n    @target_defs = matches\n\n    bind_to matches if matches\n    bind_to block.break if block\n\n    if (parent_visitor = @parent_visitor) && matches\n      matches.each do |match|\n        match.special_vars.try &.each do |special_var_name|\n          special_var = match.vars.not_nil![special_var_name]\n          parent_visitor.define_special_var(special_var_name, special_var)\n        end\n      end\n    end\n  end\n\n  def lookup_matches\n    lookup_matches(with_autocast: false)\n  rescue ex : RetryLookupWithLiterals\n    lookup_matches(with_autocast: true)\n  end\n\n  def lookup_matches(*, with_autocast = false)\n    if args.any? { |arg| arg.is_a?(Splat) || arg.is_a?(DoubleSplat) }\n      lookup_matches_with_splat(with_autocast)\n    else\n      arg_types = args.map(&.type(with_autocast: with_autocast))\n      named_args_types = NamedArgumentType.from_args(named_args, with_autocast)\n      matches = lookup_matches_without_splat arg_types, named_args_types, with_autocast\n\n      # If we checked for automatic casts, see if an ambiguous call was produced\n      if with_autocast\n        arg_types.each &.check_restriction_exception\n        named_args_types.try &.each &.type.check_restriction_exception\n      end\n\n      matches\n    end\n  end\n\n  def lookup_matches_with_splat(with_autocast)\n    # Check if all splat are of tuples\n    arg_types = Array(Type).new(args.size * 2)\n    named_args_types = nil\n    args.each_with_index do |arg, i|\n      case arg\n      when Splat\n        case arg_type = arg.type\n        when TupleInstanceType\n          arg_types.concat arg_type.tuple_types\n        when UnionType\n          arg.raise \"splatting a union #{arg_type} is not yet supported\"\n        else\n          arg.raise \"argument to splat must be a tuple, not #{arg_type}\"\n        end\n      when DoubleSplat\n        case arg_type = arg.type\n        when NamedTupleInstanceType\n          arg_type.entries.each do |entry|\n            name, type = entry.name, entry.type\n\n            named_args_types ||= [] of NamedArgumentType\n            raise \"duplicate key: #{name}\" if named_args_types.any? &.name.==(name)\n            named_args_types << NamedArgumentType.new(name, type)\n          end\n        when UnionType\n          arg.raise \"double splatting a union #{arg_type} is not yet supported\"\n        else\n          arg.raise \"argument to double splat must be a named tuple, not #{arg_type}\"\n        end\n      else\n        arg_types << arg.type(with_autocast: with_autocast)\n      end\n    end\n\n    # Leave named arguments at the end, so double splat args come before them\n    # (they will be passed in this order)\n    if named_args = self.named_args\n      named_args_types ||= [] of NamedArgumentType\n      named_args.each do |named_arg|\n        raise \"duplicate key: #{named_arg.name}\" if named_args_types.any? &.name.==(named_arg.name)\n        named_args_types << NamedArgumentType.new(\n          named_arg.name,\n          named_arg.value.type(with_autocast: with_autocast),\n        )\n      end\n    end\n\n    lookup_matches_without_splat arg_types, named_args_types, with_autocast: with_autocast\n  end\n\n  def lookup_matches_without_splat(arg_types, named_args_types, with_autocast)\n    if obj = @obj\n      lookup_matches_in(obj.type, arg_types, named_args_types, with_autocast: with_autocast)\n    elsif name == \"super\"\n      lookup_super_matches(arg_types, named_args_types, with_autocast: with_autocast)\n    elsif name == \"previous_def\"\n      lookup_previous_def_matches(arg_types, named_args_types, with_autocast: with_autocast)\n    elsif with_scope = @with_scope\n      lookup_matches_with_scope_in with_scope, arg_types, named_args_types, with_autocast: with_autocast\n    else\n      lookup_matches_in scope, arg_types, named_args_types, with_autocast: with_autocast\n    end\n  end\n\n  def lookup_matches_in(owner : AliasType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    lookup_matches_in(owner.remove_alias, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast)\n  end\n\n  def lookup_matches_in(owner : UnionType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    owner.union_types.flat_map { |type| lookup_matches_in(type, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) }\n  end\n\n  def lookup_matches_in(owner : Program, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents: search_in_parents, with_autocast: with_autocast)\n  end\n\n  def lookup_matches_in(owner : FileModule, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    lookup_matches_in program, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast\n  end\n\n  def lookup_matches_in(owner : NonGenericModuleType | GenericModuleInstanceType | GenericType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    attach_subclass_observer owner\n\n    including_types = owner.including_types\n    if including_types\n      lookup_matches_in(including_types, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast)\n    else\n      [] of Def\n    end\n  end\n\n  def lookup_matches_in(owner : LibType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    raise \"lib fun call is not supported in dispatch\"\n  end\n\n  def lookup_matches_in(owner : Type, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false)\n    lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents: search_in_parents, with_autocast: with_autocast)\n  end\n\n  def lookup_matches_with_scope_in(owner, arg_types, named_args_types, with_autocast = false)\n    signature = CallSignature.new(name, arg_types, block, named_args_types)\n\n    matches = lookup_matches_checking_expansion(owner, signature, with_autocast: with_autocast)\n\n    if matches.empty? && owner.class? && owner.abstract?\n      matches = owner.virtual_type.lookup_matches(signature, analyze_all: with_autocast)\n    end\n\n    if matches.empty?\n      @uses_with_scope = false\n      return lookup_matches_in scope, arg_types, named_args_types, with_autocast: with_autocast\n    end\n\n    @uses_with_scope = true\n    instantiate signature, matches, owner, self_type: nil, with_autocast: with_autocast\n  end\n\n  def lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents, search_in_toplevel = true, with_autocast = false)\n    signature = CallSignature.new(def_name, arg_types, block, named_args_types)\n\n    matches = check_tuple_indexer(owner, def_name, args, arg_types)\n    matches ||= lookup_matches_checking_expansion(owner, signature, search_in_parents, with_autocast: with_autocast)\n\n    # If we didn't find a match and this call doesn't have a receiver,\n    # and we are not at the top level, let's try searching the top-level\n    if matches.empty? && !obj && owner != program && search_in_toplevel\n      program_matches = lookup_matches_with_signature(program, signature, search_in_parents, with_autocast)\n      matches = program_matches unless program_matches.empty?\n    end\n\n    if matches.empty? && owner.class? && owner.abstract? && !super?\n      matches = owner.virtual_type.lookup_matches(signature, analyze_all: with_autocast)\n    end\n\n    if matches.empty?\n      defined_method_missing = owner.check_method_missing(signature, self)\n      if defined_method_missing\n        matches = owner.lookup_matches(signature, analyze_all: with_autocast)\n      elsif with_scope = @with_scope\n        defined_method_missing = with_scope.check_method_missing(signature, self)\n        if defined_method_missing\n          matches = with_scope.lookup_matches(signature, analyze_all: with_autocast)\n          @uses_with_scope = true\n        end\n      end\n    end\n\n    if matches.empty?\n      # If the owner is abstract type without subclasses,\n      # or if the owner is an abstract generic instance type,\n      # don't give error. This is to allow small code comments without giving\n      # compile errors, which will anyway appear once you add concrete\n      # subclasses and instances.\n      if def_name == \"new\" || !(!owner.metaclass? && owner.abstract_leaf?)\n        raise_matches_not_found(matches.owner || owner, def_name, arg_types, named_args_types, matches, with_autocast: with_autocast, number_autocast: !program.has_flag?(\"no_number_autocast\"))\n      end\n    end\n\n    # If this call is an implicit call to self\n    if !obj && !program_matches && !owner.is_a?(Program)\n      parent_visitor.check_self_closured\n    end\n\n    instance_type = owner.instance_type\n    if instance_type.is_a?(VirtualType)\n      attach_subclass_observer instance_type.base_type\n    end\n\n    instantiate signature, matches, owner, self_type, with_autocast\n  end\n\n  def lookup_matches_checking_expansion(owner, signature, search_in_parents = true, with_autocast = false)\n    # If this call is an expansion (because of default or named args) we must\n    # resolve the call in the type that defined the original method, without\n    # triggering a virtual lookup. But the context of lookup must be preserved.\n    if expansion?\n      matches = bubbling_exception do\n        target = parent_visitor.typed_def.original_owner\n        if search_in_parents\n          target.lookup_matches(signature, analyze_all: with_autocast)\n        else\n          target.lookup_matches_without_parents(signature, analyze_all: with_autocast)\n        end\n      end\n      matches.each do |match|\n        match.context.instantiated_type = owner\n        match.context.defining_type = parent_visitor.path_lookup.not_nil!\n      end\n      matches\n    else\n      bubbling_exception { lookup_matches_with_signature(owner, signature, search_in_parents, with_autocast) }\n    end\n  end\n\n  def lookup_matches_with_signature(owner : Program, signature, search_in_parents, with_autocast)\n    location = self.location\n    if location && (filename = location.original_filename)\n      matches = owner.lookup_private_matches(filename, signature, analyze_all: with_autocast)\n    end\n\n    if matches\n      if matches.empty?\n        matches = owner.lookup_matches(signature, analyze_all: with_autocast)\n      end\n    else\n      matches = owner.lookup_matches(signature, analyze_all: with_autocast)\n    end\n\n    matches\n  end\n\n  def lookup_matches_with_signature(owner, signature, search_in_parents, with_autocast)\n    if search_in_parents\n      owner.lookup_matches(signature, analyze_all: with_autocast)\n    else\n      owner.lookup_matches_without_parents(signature, analyze_all: with_autocast)\n    end\n  end\n\n  def instantiate(signature, matches, owner, self_type, with_autocast)\n    matches.each &.remove_literals if with_autocast\n\n    block = @block\n\n    typed_defs = Array(Def).new(matches.size)\n\n    matches.each do |match|\n      check_visibility match\n\n      yield_vars, block_arg_type = match_block_arg(match)\n      use_cache = !block || match.def.block_arg\n\n      if block && match.def.block_arg\n        if block_arg_type.is_a?(ProcInstanceType)\n          block_type = block_arg_type.return_type\n        end\n        use_cache = false unless block_type\n      end\n\n      lookup_self_type = self_type || match.context.instantiated_type\n      if self_type\n        lookup_arg_types = Array(Type).new(match.arg_types.size + 1)\n        lookup_arg_types.push self_type\n        lookup_arg_types.concat match.arg_types\n      else\n        lookup_arg_types = match.arg_types\n      end\n      match_owner = match.context.instantiated_type\n      def_instance_owner = (self_type || match_owner).as(DefInstanceContainer)\n      named_args_types = match.named_arg_types\n\n      def_instance_key = DefInstanceKey.new(match.def.object_id, lookup_arg_types, block_type, named_args_types)\n      typed_def = def_instance_owner.lookup_def_instance def_instance_key if use_cache\n\n      unless typed_def\n        typed_def, typed_def_args = prepare_typed_def_with_args(match.def, match_owner, lookup_self_type, match.arg_types, block_arg_type, named_args_types)\n        def_instance_owner.add_def_instance(def_instance_key, typed_def) if use_cache\n\n        if typed_def_return_type = typed_def.return_type\n          check_return_type(typed_def, typed_def_return_type, match, match_owner)\n        end\n\n        bubbling_exception do\n          check_recursive_splat_call match.def, typed_def_args do\n            visitor = MainVisitor.new(program, typed_def_args, typed_def)\n            visitor.yield_vars = yield_vars\n            visitor.match_context = match.context\n            visitor.untyped_def = match.def\n            visitor.call = self\n            visitor.scope = lookup_self_type\n            visitor.path_lookup = match.context.defining_type\n\n            yields_to_block = block && !match.def.uses_block_arg?\n\n            if yields_to_block\n              raise_if_block_too_nested(match.def.block_nest)\n              match.def.block_nest += 1\n            end\n\n            typed_def.body.accept visitor\n\n            if yields_to_block\n              match.def.block_nest -= 1\n            end\n\n            if visitor.is_initialize\n              if match.def.macro_def?\n                visitor.check_initialize_instance_vars_types(owner)\n              end\n              visitor.bind_initialize_instance_vars(owner)\n            end\n          end\n        end\n      end\n\n      typed_defs << typed_def\n    end\n\n    typed_defs\n  end\n\n  def raise_if_block_too_nested(block_nest)\n    # When we visit this def's body, we nest. If we are nesting\n    # over and over again, and there's a block, it means this will go on forever\n    #\n    # TODO Ideally this should check `> 1`, but the algorithm isn't precise. However,\n    # manually nested blocks don't nest this deep.\n    if block_nest > 15\n      raise \"recursive block expansion: blocks that yield are always inlined, and this call leads to an infinite inlining\"\n    end\n  end\n\n  def check_return_type(typed_def, typed_def_return_type, match, match_owner)\n    return_type = lookup_node_type(match.context, typed_def_return_type)\n    return_type = program.nil if return_type.void?\n    typed_def.freeze_type = return_type\n    typed_def.type = return_type if return_type.no_return? || return_type.nil_type?\n  end\n\n  def check_tuple_indexer(owner, def_name, args, arg_types)\n    return unless args.size == 1\n\n    case def_name\n    when \"[]\"\n      nilable = false\n    when \"[]?\"\n      nilable = true\n    else\n      return\n    end\n\n    if owner.is_a?(TupleInstanceType)\n      # Check tuple indexer\n      tuple_indexer_helper(args, arg_types, owner, owner, nilable) do |instance_type, index|\n        instance_type.tuple_indexer(index)\n      end\n    elsif owner.metaclass? && (instance_type = owner.instance_type).is_a?(TupleInstanceType)\n      # Check tuple metaclass indexer\n      tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) do |instance_type, index|\n        instance_type.tuple_metaclass_indexer(index)\n      end\n    elsif owner.is_a?(NamedTupleInstanceType)\n      # Check named tuple indexer\n      named_tuple_indexer_helper(args, arg_types, owner, owner, nilable) do |instance_type, index|\n        instance_type.tuple_indexer(index)\n      end\n    elsif owner.metaclass? && (instance_type = owner.instance_type).is_a?(NamedTupleInstanceType)\n      # Check named tuple metaclass indexer\n      named_tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) do |instance_type, index|\n        instance_type.tuple_metaclass_indexer(index)\n      end\n    end\n  end\n\n  def tuple_indexer_helper(args, arg_types, owner, instance_type, nilable, &)\n    index = tuple_indexer_helper_index(owner, instance_type, nilable)\n    return unless index\n\n    indexer_def = yield instance_type, index\n    indexer_match = Match.new(indexer_def, arg_types, MatchContext.new(owner, owner))\n    Matches.new([indexer_match] of Match, true)\n  end\n\n  private def tuple_indexer_helper_index(owner, instance_type, nilable)\n    arg = args.first\n\n    # Make it work with constants too\n    while arg.is_a?(Path) && (target_const = arg.target_const)\n      arg = target_const.value\n    end\n\n    if arg.is_a?(NumberLiteral) && arg.kind.i32?\n      index = arg.value.to_i\n      index += instance_type.size if index < 0\n      in_bounds = (0 <= index < instance_type.size)\n      unless in_bounds\n        unless nilable\n          raise \"index '#{arg}' out of bounds for empty tuple\" if instance_type.size == 0\n          raise \"index out of bounds for #{owner} (#{arg} not in #{-instance_type.size}..#{instance_type.size - 1})\"\n        end\n        index = -1\n      end\n    elsif arg.is_a?(RangeLiteral)\n      from = arg.from\n      if from.is_a?(NumberLiteral) && from.kind.i32?\n        from_index = from.value.to_i\n        from_index += instance_type.size if from_index < 0\n        in_bounds = (0 <= from_index <= instance_type.size)\n        if !in_bounds && !nilable\n          raise \"begin index out of bounds for #{owner} (#{from} not in #{-instance_type.size}..#{instance_type.size})\"\n        end\n      elsif from.is_a?(Nop)\n        from_index = 0\n        in_bounds = true\n      else\n        return nil\n      end\n\n      to = arg.to\n      if to.is_a?(NumberLiteral) && to.kind.i32?\n        to_index = to.value.to_i\n        to_index += instance_type.size if to_index < 0\n        to_index = (to_index - (arg.exclusive? ? 1 : 0)).clamp(-1, instance_type.size - 1)\n      elsif to.is_a?(Nop)\n        to_index = instance_type.size - 1\n      else\n        return nil\n      end\n\n      if in_bounds\n        if from_index <= to_index\n          index = (from_index..to_index)\n        else\n          index = (0...0)\n        end\n      else\n        index = -1\n      end\n    else\n      return nil\n    end\n\n    index\n  end\n\n  def named_tuple_indexer_helper(args, arg_types, owner, instance_type, nilable, &)\n    arg = args.first\n\n    # Make it work with constants too\n    while arg.is_a?(Path) && (target_const = arg.target_const)\n      arg = target_const.value\n    end\n\n    case arg\n    when SymbolLiteral, StringLiteral\n      name = arg.value\n      index = instance_type.name_index(name)\n      if index || nilable\n        indexer_def = yield instance_type, (index || -1)\n        indexer_match = Match.new(indexer_def, arg_types, MatchContext.new(owner, owner))\n        Matches.new([indexer_match] of Match, true)\n      else\n        raise \"missing key '#{name}' for named tuple #{owner}\"\n      end\n    else\n      nil\n    end\n  end\n\n  def replace_splats\n    return unless args.any? { |arg| arg.is_a?(Splat) || arg.is_a?(DoubleSplat) }\n\n    new_args = [] of ASTNode\n    args.each_with_index do |arg, i|\n      case arg\n      when Splat\n        arg_type = arg.type\n        unless arg_type.is_a?(TupleInstanceType)\n          arg.raise \"BUG: splat expects a tuple, not #{arg_type}\"\n        end\n\n        arg_type.tuple_types.each_with_index do |tuple_type, index|\n          num = NumberLiteral.new(index)\n          num.type = program.int32\n          tuple_indexer = Call.new(arg.exp, \"[]\", num).at(arg)\n          parent_visitor.prepare_call(tuple_indexer)\n          tuple_indexer.recalculate\n          new_args << tuple_indexer\n          arg.remove_enclosing_call(self)\n        end\n      when DoubleSplat\n        arg_type = arg.type\n        unless arg_type.is_a?(NamedTupleInstanceType)\n          arg.raise \"BUG: double splat expects a named tuple, not #{arg_type}\"\n        end\n\n        arg_type.entries.each do |entry|\n          sym = SymbolLiteral.new(entry.name)\n          sym.type = program.symbol\n          program.symbols.add sym.value\n          tuple_indexer = Call.new(arg.exp, \"[]\", sym).at(arg)\n          parent_visitor.prepare_call(tuple_indexer)\n          tuple_indexer.recalculate\n          new_args << tuple_indexer\n          arg.remove_enclosing_call(self)\n        end\n      else\n        new_args << arg\n      end\n    end\n    self.args = new_args\n  end\n\n  def replace_block_arg_with_block(block_arg)\n    block_arg_type = block_arg.type\n    if block_arg_type.is_a?(ProcInstanceType)\n      vars = [] of Var\n      args = [] of ASTNode\n      block_arg_type.arg_types.map_with_index do |type, i|\n        arg = Var.new(\"__arg#{i}\").at(block_arg)\n        vars << arg\n        args << arg\n      end\n      block = Block.new(vars, Call.new(block_arg.clone, \"call\", args).at(block_arg)).at(block_arg)\n      block.vars = self.before_vars\n      self.block = block\n    else\n      block_arg.raise \"expected a function type, not #{block_arg.type}\"\n    end\n  end\n\n  def lookup_super_matches(arg_types, named_args_types, with_autocast)\n    if scope.is_a?(Program)\n      raise \"there's no superclass in this scope\"\n    end\n\n    enclosing_def = enclosing_def(\"super\")\n\n    # TODO: do this better\n    lookup = enclosing_def.owner\n\n    case lookup\n    when VirtualType\n      parents = lookup.base_type.ancestors\n    when NonGenericModuleType\n      ancestors = parent_visitor.scope.ancestors\n      index_of_ancestor = ancestors.index!(lookup)\n      parents = ancestors[index_of_ancestor + 1..-1]\n    when GenericModuleType\n      ancestors = parent_visitor.scope.ancestors\n      index_of_ancestor = ancestors.index! { |ancestor| ancestor.is_a?(GenericModuleInstanceType) && ancestor.generic_type == lookup }\n      parents = ancestors[index_of_ancestor + 1..-1]\n    when GenericType\n      ancestors = parent_visitor.scope.ancestors\n      index_of_ancestor = ancestors.index { |ancestor| ancestor.is_a?(GenericClassInstanceType) && ancestor.generic_type == lookup }\n      if index_of_ancestor\n        parents = ancestors[index_of_ancestor + 1..-1]\n      else\n        parents = ancestors\n      end\n    else\n      parents = lookup.ancestors\n    end\n\n    in_initialize = enclosing_def.name == \"initialize\"\n\n    if parents && parents.size > 0\n      parents.each_with_index do |parent, i|\n        if parent.lookup_first_def(enclosing_def.name, block)\n          return lookup_matches_in_type(parent, arg_types, named_args_types, scope, enclosing_def.name, !in_initialize, search_in_toplevel: false, with_autocast: with_autocast)\n        end\n      end\n      lookup_matches_in_type(parents.last, arg_types, named_args_types, scope, enclosing_def.name, !in_initialize, search_in_toplevel: false, with_autocast: with_autocast)\n    else\n      raise \"there's no superclass in this scope\"\n    end\n  end\n\n  def lookup_previous_def_matches(arg_types, named_args_types, with_autocast)\n    enclosing_def = enclosing_def(\"previous_def\")\n\n    previous_item = enclosing_def.previous\n    unless previous_item\n      return raise \"there is no previous definition of '#{enclosing_def.name}'\"\n    end\n\n    previous = previous_item.def\n\n    signature = CallSignature.new(previous.name, arg_types, block, named_args_types)\n    context = MatchContext.new(scope, scope, def_free_vars: previous.free_vars)\n    match = Match.new(previous, arg_types, context, named_args_types)\n    matches = Matches.new([match] of Match, true)\n\n    unless signature.match(previous_item, context)\n      raise_matches_not_found scope, previous.name, arg_types, named_args_types, matches, with_autocast: with_autocast, number_autocast: !program.has_flag?(\"no_number_autocast\")\n    end\n\n    unless scope.is_a?(Program)\n      parent_visitor.check_self_closured\n    end\n\n    typed_defs = instantiate signature, matches, scope, self_type: nil, with_autocast: with_autocast\n    typed_defs.each do |typed_def|\n      typed_def.next = parent_visitor.typed_def\n    end\n    typed_defs\n  end\n\n  def enclosing_def(context)\n    fun_literal_context = parent_visitor.fun_literal_context\n    if fun_literal_context.is_a?(Def)\n      return fun_literal_context\n    end\n\n    untyped_def = parent_visitor.untyped_def?\n    if untyped_def\n      return untyped_def\n    end\n\n    raise \"can't use '#{context}' outside method\"\n  end\n\n  def on_new_subclass\n    recalculate\n  end\n\n  def lookup_macro\n    in_macro_target do |target|\n      result = target.lookup_macro(name, args, named_args)\n      case result\n      when Macro\n        return result\n      when Type::DefInMacroLookup\n        return nil\n      else\n        # Check next target\n      end\n    end\n  end\n\n  def in_macro_target(&)\n    if with_scope = @with_scope\n      macros = yield with_scope\n      return macros if macros\n    end\n\n    node_scope = scope\n    node_scope = node_scope.base_type if node_scope.is_a?(VirtualType)\n\n    macros = yield node_scope\n\n    # If the scope is a module (through its instance type), lookup in Object too\n    # (so macros like `property` and others, defined in Object, work at the module level)\n    if !macros && node_scope.instance_type.module?\n      macros = yield program.object\n    end\n\n    macros ||= yield program\n\n    if !macros && (location = self.location) && (filename = location.original_filename).is_a?(String) && (file_module = program.file_module?(filename))\n      macros ||= yield file_module\n    end\n\n    macros\n  end\n\n  # Match the given block with the given block argument specification (&block : A, B, C -> D)\n  def match_block_arg(match)\n    block_arg = match.def.block_arg\n    return nil, nil unless block_arg\n    return nil, nil unless match.def.block_arity || match.def.uses_block_arg?\n\n    yield_vars = nil\n    block_arg_type = nil\n\n    block = @block.not_nil!\n\n    block_arg_restriction = block_arg.restriction\n\n    # If the block spec is &block : A, B, C -> D, we solve the argument types\n    if block_arg_restriction.is_a?(ProcNotation)\n      # If there are input types, solve them and creating the yield vars\n      if inputs = block_arg_restriction.inputs\n        yield_types = Array(Type).new(inputs.size + 1)\n        inputs.each do |input|\n          if input.is_a?(Splat)\n            tuple_type = lookup_node_type(match.context, input.exp)\n            unless tuple_type.is_a?(TupleInstanceType)\n              input.raise \"expected type to be a tuple type, not #{tuple_type}\"\n            end\n            tuple_type.tuple_types.each do |arg_type|\n              MainVisitor.check_type_allowed_as_proc_argument(input, arg_type)\n              yield_types << arg_type.virtual_type\n            end\n          else\n            arg_type = lookup_node_type(match.context, input)\n            MainVisitor.check_type_allowed_as_proc_argument(input, arg_type)\n            yield_types << arg_type.virtual_type\n          end\n        end\n\n        if splat_index = block.splat_index\n          if yield_types.size < block.args.size - 1\n            block.raise \"too many block parameters (given #{block.args.size - 1}+, expected maximum #{yield_types.size})\"\n          end\n          splat_range = (splat_index..splat_index - block.args.size)\n          yield_types[splat_range] = program.tuple_of(yield_types[splat_range])\n        end\n\n        yield_vars = yield_types.map_with_index { |type, i| Var.new(\"var#{i}\", type) }\n      end\n      output = block_arg_restriction.output\n    elsif block_arg_restriction\n      # Otherwise, the block spec could be something like &block : Foo, and that\n      # is valid too only if Foo is an alias/typedef that refers to a FunctionType\n      block_arg_restriction_type = lookup_node_type(match.context, block_arg_restriction).remove_typedef\n      unless block_arg_restriction_type.is_a?(ProcInstanceType)\n        if block_arg_restriction_type.is_a?(ProcType)\n          block_arg_restriction.raise \"can't create an instance of generic class #{block_arg_restriction_type} without specifying its type vars\"\n        else\n          block_arg_restriction.raise \"expected block type to be a function type, not #{block_arg_restriction_type}\"\n        end\n        return nil, nil\n      end\n\n      yield_vars = block_arg_restriction_type.arg_types.map_with_index do |input, i|\n        Var.new(\"var#{i}\", input)\n      end\n      output = block_arg_restriction_type.return_type\n      output_type = output\n      output_type = program.nil if output_type.void?\n    end\n\n    if yield_vars\n      # Check if tuple unpacking is needed\n      yield_var_type = yield_vars.first?.try &.type.as?(TupleInstanceType)\n      auto_unpack_needed = yield_vars.size == 1 &&\n                           yield_var_type &&\n                           block.args.size > 1 &&\n                           !block.splat_index\n\n      if auto_unpack_needed\n        yield_var_type.not_nil!.tuple_types.each_with_index do |tuple_type, i|\n          arg = block.args[i]?\n          arg.type = tuple_type if arg\n        end\n      else\n        yield_vars.each_with_index do |yield_var, i|\n          arg = block.args[i]?\n          arg.bind_to(yield_var || program.nil_var) if arg\n        end\n      end\n    end\n\n    # If the block is used, we convert it to a function pointer\n    if match.def.uses_block_arg?\n      # Create the arguments of the function literal\n      if yield_vars\n        if auto_unpack_needed\n          fun_args = [Arg.new(program.new_temp_var_name, type: yield_vars.first.type)]\n        else\n          fun_args = yield_vars.map_with_index do |var, i|\n            arg_name = block.args[i]?.try(&.name) || program.new_temp_var_name\n            Arg.new(arg_name, type: var.type)\n          end\n        end\n      else\n        fun_args = [] of Arg\n      end\n\n      if match.def.free_var?(output)\n        # Nothing, output is a free variable\n      elsif output.is_a?(ASTNode) && !output.is_a?(Underscore)\n        output_type = lookup_node_type?(match.context, output)\n        if output_type\n          output_type = program.nil if output_type.void?\n          unless output_type.can_be_stored?\n            output.raise \"can't use #{output_type} as a block return type yet, use a more specific type\"\n          end\n          output_type = output_type.virtual_type\n        end\n      end\n\n      # Check if the call has a block arg (foo &bar). If so, we need to see if the\n      # passed block has the same signature as the def's block arg. We use that\n      # same ProcLiteral (bar) for this call.\n      fun_literal = block.fun_literal\n      unless fun_literal\n        if call_block_arg = self.block_arg\n          check_call_block_arg_matches_def_block_arg(call_block_arg, yield_vars)\n          fun_literal = call_block_arg\n        else\n          # Otherwise, we create a ProcLiteral and type it\n          if auto_unpack_needed\n            yield_var_type = yield_var_type.not_nil!\n            if block.args.size > yield_var_type.tuple_types.size\n              block.raise \"too many block parameters (given #{block.args.size}, expected maximum #{yield_var_type.tuple_types.size})\"\n            end\n\n            unpack_exps = [] of ASTNode\n            tuple_name = fun_args.first.name\n            yield_var_type.tuple_types.each_with_index do |tuple_type, i|\n              if arg = block.args[i]?\n                call = Call.new(Var.new(tuple_name), \"[]\", NumberLiteral.new(i))\n                unpack_exps << Assign.new(Var.new(arg.name), call)\n              end\n            end\n\n            case old_body = block.body\n            when Nop\n              # do nothing\n              new_body = old_body\n            when Expressions\n              # multiple statements\n              new_body = old_body\n              new_body.expressions[0...0] = unpack_exps\n            else\n              # single statement\n              unpack_exps << old_body\n              new_body = Expressions.new(unpack_exps)\n            end\n\n            a_def = Def.new(\"->\", fun_args, new_body).at(block)\n            a_def.captured_block = true\n          else\n            if block.args.size > fun_args.size\n              wrong_number_of \"block parameters\", block.args.size, fun_args.size\n            end\n\n            a_def = Def.new(\"->\", fun_args, block.body).at(block)\n            a_def.captured_block = true\n          end\n\n          fun_literal = ProcLiteral.new(a_def).at(self)\n          fun_literal.expected_return_type = output_type if output_type\n          fun_literal.from_block = true\n          fun_literal.force_nil = true unless output\n          fun_literal.accept parent_visitor\n        end\n        block.fun_literal = fun_literal\n      end\n\n      # Now check if the ProcLiteral's type (the block's type) matches the block arg specification.\n      # If not, we delay it for later and compute the type based on the block arg return type, if any.\n      fun_literal_type = fun_literal.type?\n      if fun_literal_type\n        block_arg_type = fun_literal_type\n        block_type = fun_literal_type.as(ProcInstanceType).return_type\n        if output\n          match.context.def_free_vars = match.def.free_vars\n          matched = block_type.restrict(output, match.context)\n          if !matched && !void_return_type?(match.context, output)\n            if output.is_a?(ASTNode) && !output.is_a?(Underscore) && block_type.no_return?\n              block_type = lookup_node_type(match.context, output).virtual_type\n              block.type = output_type || block_type\n              block.freeze_type = output_type || block_type\n              block_arg_type = program.proc_of(fun_args, block_type)\n            else\n              raise \"expected block to return #{output}, not #{block_type}\"\n            end\n          elsif output_type\n            block.bind_to(block)\n            block.type = output_type\n            block.freeze_type = output_type\n          end\n        end\n      else\n        if output\n          if !match.def.free_var?(output) && output.is_a?(ASTNode) && !output.is_a?(Underscore)\n            output_type = lookup_node_type(match.context, output).virtual_type\n            output_type = program.nil if output_type.void?\n            block.type = output_type\n            block.freeze_type = output_type\n            block_arg_type = program.proc_of(fun_args, output_type)\n          else\n            cant_infer_block_return_type\n          end\n        else\n          block.body.type = program.void\n          block.type = program.void\n          block_arg_type = program.proc_of(fun_args, program.void)\n        end\n      end\n\n      # Because the block's type might be used as a free variable, we bind\n      # ourself to the block so when its type changes we recalculate ourself.\n      if output\n        block.try &.remove_enclosing_call(self)\n        block.try &.set_enclosing_call(self)\n      end\n    else\n      block.accept parent_visitor\n\n      # Similar to above: we check that the block's type matches the block arg specification,\n      # and we delay it if possible.\n      # If the return type is an underscore, we just ignore any return type checking.\n      if output && !output.is_a?(Underscore)\n        if !block.type?\n          if !match.def.free_var?(output) && output.is_a?(ASTNode) && !output.is_a?(Underscore)\n            begin\n              block_type = lookup_node_type(match.context, output).virtual_type\n              block_type = program.nil if block_type.void?\n            rescue ex : Crystal::CodeError\n              cant_infer_block_return_type\n            end\n          else\n            cant_infer_block_return_type\n          end\n        else\n          block_type = block.type\n          match.context.def_free_vars = match.def.free_vars\n          matched = block_type.restrict(output, match.context)\n          if (!matched || (matched && !block_type.implements?(matched))) && !void_return_type?(match.context, output)\n            if output.is_a?(ASTNode) && !output.is_a?(Underscore) && block_type.no_return?\n              begin\n                block_type = lookup_node_type(match.context, output).virtual_type\n              rescue ex : Crystal::CodeError\n                if block_type\n                  raise \"couldn't match #{block_type} to #{output}\", ex\n                else\n                  cant_infer_block_return_type\n                end\n              end\n            else\n              output_name = case output\n                            when Self\n                              match.context.instantiated_type\n                            when Crystal::Path\n                              match.context.defining_type.lookup_type_var(output, match.context.bound_free_vars)\n                            else\n                              output\n                            end\n              raise \"expected block to return #{output_name}, not #{block_type}\"\n            end\n          end\n\n          block.freeze_type = block_type\n        end\n      end\n    end\n\n    {yield_vars, block_arg_type}\n  end\n\n  private def check_call_block_arg_matches_def_block_arg(call_block_arg, yield_vars)\n    call_block_arg_types = call_block_arg.type.as(ProcInstanceType).arg_types\n    if yield_vars\n      if yield_vars.size != call_block_arg_types.size\n        wrong_number_of \"block argument's parameters\", call_block_arg_types.size, yield_vars.size\n      end\n\n      i = 1\n      yield_vars.zip(call_block_arg_types) do |yield_var, call_block_arg_type|\n        if yield_var.type != call_block_arg_type\n          raise \"expected block argument's parameter ##{i} to be #{yield_var.type}, not #{call_block_arg_type}\"\n        end\n        i += 1\n      end\n    elsif call_block_arg_types.size != 0\n      wrong_number_of \"block argument's parameters\", call_block_arg_types.size, 0\n    end\n  end\n\n  private def void_return_type?(match_context, output)\n    if output.is_a?(Path)\n      type = lookup_node_type(match_context, output)\n    else\n      type = output\n    end\n\n    type.is_a?(Type) && (type.void? || type.nil_type?)\n  end\n\n  private def cant_infer_block_return_type\n    raise \"can't infer block return type, try to cast the block body with `as`. See: https://crystal-lang.org/reference/syntax_and_semantics/as.html#usage-for-when-the-compiler-cant-infer-the-type-of-a-block\"\n  end\n\n  private def lookup_node_type(context, node)\n    bubbling_exception do\n      context.defining_type.lookup_type(node, self_type: context.instantiated_type.instance_type, free_vars: context.bound_free_vars, allow_typeof: false)\n    end\n  end\n\n  private def lookup_node_type?(context, node)\n    context.defining_type.lookup_type?(node, self_type: context.instantiated_type.instance_type, free_vars: context.bound_free_vars, allow_typeof: false)\n  end\n\n  def bubbling_exception(&)\n    yield\n  rescue ex : Crystal::TopLevelMacroRaiseException\n    # Sets the last frame to the method call that includes the top level macro raise re-raised within `SemanticVisitor#eval_macro`.\n    # The first frame will be the actual actual `#raise` method call.\n    ex.inner = Crystal::MacroRaiseException.for_node self, ex.message\n\n    ::raise ex\n  rescue ex : Crystal::MacroRaiseException\n    # Raise another exception on this node, keeping the original as the inner exception.\n    # This will insert this node into the trace as the new first frame.\n    self.raise ex.message, ex, exception_type: Crystal::MacroRaiseException\n  rescue ex : Crystal::CodeError\n    if @obj && name == \"initialize\"\n      # Avoid putting 'initialize' in the error trace\n      # because it's most likely that this is happening\n      # inside a generated 'new' method\n      ::raise ex\n    else\n      msg = String.build do |io|\n        io << \"instantiating '\"\n        signature(io)\n        io << \"'\"\n      end\n      raise msg, ex\n    end\n  end\n\n  def obj_and_args_types_set?\n    obj = @obj\n    block_arg = @block_arg\n    named_args = @named_args\n\n    return false unless args.all? &.type?\n    return false if obj && !obj.type?\n    return false if block_arg && !block_arg.type?\n    return false if named_args && named_args.any? { |arg| !arg.value.type? }\n\n    true\n  end\n\n  def prepare_typed_def_with_args(untyped_def, owner, self_type, arg_types, block_arg_type, named_args_types)\n    original_untyped_def = untyped_def\n\n    # If there's an argument count mismatch, or we have a splat, or a double splat, or there are\n    # named arguments, we create another def that sets ups everything for the real call.\n    if arg_types.size != untyped_def.args.size || untyped_def.splat_index || named_args_types || untyped_def.double_splat\n      named_args_names = named_args_types.try &.map &.name\n\n      # We expand new in a different way, because default arguments need to be solved at the instance level,\n      # not at the class level. So we simply create a `new` that simply forwards all arguments to the `initialize`\n      # call (first allocating an object, and later hooking it to the GC finalizer if needed): the `initialize`\n      # method will set up default values and repack splats if needed.\n      if untyped_def.new?\n        untyped_def = untyped_def.expand_new_default_arguments(self_type.instance_type, arg_types.size, named_args_names)\n      else\n        untyped_def = untyped_def.expand_default_arguments(program, arg_types.size, named_args_names)\n      end\n\n      # This is the case of Proc#call(*args), but could be applied to any primitive really\n      body = original_untyped_def.body\n      untyped_def.body = body.clone if body.is_a?(Primitive)\n    end\n\n    typed_def = untyped_def.clone\n    typed_def.owner = owner\n    typed_def.original_owner = untyped_def.owner\n\n    if body = typed_def.body\n      typed_def.bind_to body\n    end\n\n    args = MetaVars.new\n\n    if self_type\n      args[\"self\"] = MetaVar.new(\"self\", self_type)\n    end\n\n    strict_check = body.is_a?(Primitive) && body.name.in?(\"proc_call\", \"pointer_set\")\n\n    arg_types.each_index do |index|\n      arg = typed_def.args[index]\n      type = arg_types[index]\n      var = MetaVar.new(arg.name, type).at(arg)\n      var.bind_to(var)\n      args[arg.name] = var\n\n      if strict_check\n        case body.as(Primitive).name\n        when \"proc_call\"\n          owner = owner.as(ProcInstanceType)\n          proc_arg_type = owner.arg_types[index]\n          unless type.implements?(proc_arg_type)\n            self.args[index].raise \"type must be #{proc_arg_type}, not #{type}\"\n          end\n        when \"pointer_set\"\n          owner = owner.remove_typedef.as(PointerInstanceType)\n          pointer_type = owner.var.type\n          unless (type.nil_type? && pointer_type.void?) || type.implements?(pointer_type)\n            self.args[index].raise \"type must be #{pointer_type}, not #{type}\"\n          end\n        end\n      end\n\n      arg.type = type\n    end\n\n    # Fill magic constants (__LINE__, __FILE__, __DIR__)\n    named_args_size = named_args_types.try(&.size) || 0\n    (arg_types.size + named_args_size).upto(typed_def.args.size - 1) do |index|\n      arg = typed_def.args[index]\n      default_value = arg.default_value.as(MagicConstant)\n      case default_value.name\n      when .magic_line?, .magic_end_line?\n        type = program.int32\n      when .magic_file?, .magic_dir?\n        type = program.string\n      else\n        default_value.raise \"BUG: unknown magic constant: #{default_value.name}\"\n      end\n      var = MetaVar.new(arg.name, type).at(arg)\n      var.bind_to(var)\n      args[arg.name] = var\n      arg.type = type\n    end\n\n    named_args_types.try &.each do |named_arg|\n      arg = typed_def.args.find! { |arg| arg.external_name == named_arg.name }\n\n      type = named_arg.type\n      var = MetaVar.new(arg.name, type)\n      var.bind_to(var)\n\n      args[arg.name] = var\n      arg.type = type\n    end\n\n    fun_literal = @block.try &.fun_literal\n    if fun_literal && block_arg_type\n      block_arg = untyped_def.block_arg.not_nil!\n      var = MetaVar.new(block_arg.name, block_arg_type)\n      args[block_arg.name] = var\n\n      typed_def.block_arg.not_nil!.type = block_arg_type\n    end\n\n    {typed_def, args}\n  end\n\n  def attach_subclass_observer(type : Type)\n    if subclass_notifier = @subclass_notifier\n      subclass_notifier.as(SubclassObservable).remove_subclass_observer(self)\n    end\n\n    type.as(SubclassObservable).add_subclass_observer(self)\n    @subclass_notifier = type\n  end\n\n  def super?\n    !obj && name == \"super\"\n  end\n\n  def previous_def?\n    !obj && name == \"previous_def\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/call_error.cr",
    "content": "class Crystal::ASTNode\n  def wrong_number_of_arguments(subject, given, expected)\n    wrong_number_of \"arguments\", subject, given, expected\n  end\n\n  def wrong_number_of(elements, given, expected)\n    raise wrong_number_of_message(elements, given, expected)\n  end\n\n  def wrong_number_of(elements, subject, given, expected)\n    raise wrong_number_of_message(elements, subject, given, expected)\n  end\n\n  def wrong_number_of_message(elements, given, expected)\n    \"wrong number of #{elements} (given #{given}, expected #{expected})\"\n  end\n\n  def wrong_number_of_message(elements, subject, given, expected)\n    \"wrong number of #{elements} for #{subject} (given #{given}, expected #{expected})\"\n  end\nend\n\nclass Crystal::Path\n  def raise_undefined_constant(type)\n    private_const = type.lookup_path(self, include_private: true)\n    if private_const\n      self.raise(\"private constant #{private_const} referenced\")\n    end\n\n    similar_name = type.lookup_similar_path(self)\n    if similar_name\n      self.raise(\"undefined constant #{self}\\nDid you mean '#{similar_name}'?\")\n    else\n      self.raise(\"undefined constant #{self}\")\n    end\n  end\nend\n\nclass Crystal::Call\n  # :nodoc:\n  MAX_RENDERED_OVERLOADS = 20\n\n  def raise_matches_not_found(owner, def_name, arg_types, named_args_types, matches = nil, with_autocast = false, number_autocast = true)\n    obj = @obj\n    with_scope = @with_scope\n\n    # Special case: Foo+.class#new\n    if owner.is_a?(VirtualMetaclassType) && def_name == \"new\"\n      raise_matches_not_found_for_virtual_metaclass_new owner\n    end\n\n    if super?\n      defs = owner.lookup_defs_without_parents(def_name)\n    else\n      defs = owner.lookup_defs(def_name)\n    end\n\n    # Also consider private top-level defs\n    if owner.is_a?(Program)\n      location = self.location\n      if location && (filename = location.original_filename)\n        private_defs = owner.file_module?(filename).try &.lookup_defs(def_name)\n        defs.concat(private_defs) if private_defs\n      end\n    end\n\n    # Another special case: `new` and `initialize` are only looked up one level,\n    # so we must find the first one defined.\n    new_owner = owner\n    while defs.empty? && def_name.in?(\"initialize\", \"new\")\n      new_owner = new_owner.superclass\n      if new_owner\n        defs = new_owner.lookup_defs(def_name)\n      else\n        defs = [] of Def\n        break\n      end\n    end\n\n    # Also check with scope\n    if with_scope\n      defs.concat with_scope.lookup_defs(def_name)\n    end\n\n    # Check if it's the case of an abstract def\n    check_abstract_def_error(owner, matches, defs, def_name)\n\n    # Check if this is a `foo` call and we actually find it in the Program\n    if !obj && defs.empty?\n      program_defs = program.lookup_defs(def_name)\n      unless program_defs.empty?\n        defs = program_defs\n        owner = program\n      end\n    end\n\n    if defs.empty?\n      raise_undefined_method(owner, def_name, obj)\n    end\n\n    # If we made a lookup without the special rule for literals,\n    # and we have literals in the call, try again with that special rule.\n    if !with_autocast && (args.any?(&.supports_autocast? number_autocast) ||\n       named_args.try &.any? &.value.supports_autocast? number_autocast)\n      ::raise RetryLookupWithLiterals.new\n    end\n\n    # If it's on an initialize method and there's a similar method name, it's probably a typo\n    if def_name.in?(\"initialize\", \"new\") && (similar_def = owner.instance_type.lookup_similar_def(\"initialize\", self.args.size, block))\n      inner_msg = colorize(\"do you maybe have a typo in this '#{similar_def.name}' method?\").yellow.bold.to_s\n      inner_exception = TypeException.for_node(similar_def, inner_msg)\n    end\n\n    # Check why each def can't be called with this Call (what's the error?)\n    call_errors = defs.map do |a_def|\n      compute_call_error_reason(owner, a_def, arg_types, named_args_types)\n    end\n\n    check_block_mismatch(call_errors, owner, def_name)\n    call_errors.reject!(BlockMismatch)\n\n    check_missing_named_arguments(call_errors, owner, defs, arg_types, inner_exception)\n    check_extra_named_arguments(call_errors, owner, defs, arg_types, inner_exception)\n    check_arguments_already_specified(call_errors, owner, defs, arg_types, inner_exception)\n    check_wrong_number_of_arguments(call_errors, owner, defs, def_name, arg_types, named_args_types, inner_exception)\n    check_extra_types_arguments_mismatch(call_errors, owner, defs, def_name, arg_types, named_args_types, inner_exception)\n    check_arguments_type_mismatch(call_errors, owner, defs, def_name, arg_types, named_args_types, inner_exception)\n\n    if args.size == 1 && args.first.type.includes_type?(program.nil)\n      owner_trace = args.first.find_owner_trace(program, program.nil)\n    else\n      owner_trace = inner_exception\n    end\n\n    message = String.build do |msg|\n      no_overload_matches_message(msg, full_name(owner, def_name), defs, args, arg_types, named_args_types)\n\n      msg << \"Overloads are:\"\n      append_matches(defs, arg_types, msg)\n\n      if matches\n        cover = matches.cover\n        if cover.is_a?(Cover)\n          missing = cover.missing\n\n          uniq_arg_names = defs.map(&.args.map(&.name)).uniq!\n          uniq_arg_names = uniq_arg_names.size == 1 ? uniq_arg_names.first : nil\n          unless missing.empty?\n            msg << \"\\nCouldn't find overloads for these types:\"\n\n            missing.first(MAX_RENDERED_OVERLOADS).each do |missing_types|\n              if uniq_arg_names\n                signature_names = missing_types.map_with_index do |missing_type, i|\n                  if i >= arg_types.size && (named_arg = named_args_types.try &.[i - arg_types.size]?)\n                    \"#{named_arg.name} : #{missing_type}\"\n                  else\n                    \"#{uniq_arg_names[i]? || \"_\"} : #{missing_type}\"\n                  end\n                end\n                signature_args = signature_names.join \", \"\n              else\n                signature_args = missing_types.join \", \"\n              end\n              msg << \"\\n - #{full_name(owner, def_name)}(#{signature_args}\"\n              msg << \", &block\" if block\n              msg << ')'\n            end\n\n            if missing.size > MAX_RENDERED_OVERLOADS\n              msg << \"\\nAnd #{missing.size - MAX_RENDERED_OVERLOADS} more...\"\n            end\n          end\n        end\n      end\n    end\n\n    raise message, owner_trace\n  end\n\n  private def check_block_mismatch(call_errors, owner, def_name)\n    return unless call_errors.all?(BlockMismatch)\n\n    error_message =\n      if block\n        \"'#{full_name(owner, def_name)}' is not expected to be invoked with a block, but a block was given\"\n      else\n        \"'#{full_name(owner, def_name)}' is expected to be invoked with a block, but no block was given\"\n      end\n\n    raise error_message\n  end\n\n  private def check_missing_named_arguments(call_errors, owner, defs, arg_types, inner_exception)\n    gather_names_in_all_overloads(call_errors, MissingNamedArguments) do |call_errors, names|\n      raise_no_overload_matches(self, defs, arg_types, inner_exception) do |str|\n        if names.size == 1\n          str << \"missing argument: #{names.first}\"\n        else\n          str << \"missing arguments: #{names.join \", \"}\"\n        end\n      end\n    end\n  end\n\n  private def check_extra_named_arguments(call_errors, owner, defs, arg_types, inner_exception)\n    gather_names_in_all_overloads(call_errors, ExtraNamedArguments) do |call_errors, names|\n      raise_no_overload_matches(self, defs, arg_types, inner_exception) do |str|\n        quoted_names = names.map { |name| \"'#{name}'\" }\n\n        if quoted_names.size == 1\n          str << \"no parameter named #{quoted_names.first}\"\n        else\n          str << \"no parameters named #{quoted_names.join \", \"}\"\n        end\n\n        # Show did you mean for the simplest case for now\n        if names.size == 1 && call_errors.size == 1\n          extra_name = names.first\n          name_index = call_errors.first.names.index!(extra_name)\n          similar_name = call_errors.first.similar_names[name_index]\n          if similar_name\n            str.puts\n            str << \"Did you mean '#{similar_name}'?\"\n          end\n        end\n      end\n    end\n  end\n\n  private def check_arguments_already_specified(call_errors, owner, defs, arg_types, inner_exception)\n    gather_names_in_all_overloads(call_errors, ArgumentsAlreadySpecified) do |call_errors, names|\n      raise_no_overload_matches(self, defs, arg_types, inner_exception) do |str|\n        quoted_names = names.map { |name| \"'#{name}'\" }\n\n        if quoted_names.size == 1\n          str << \"argument for parameter #{quoted_names.first} already specified\"\n        else\n          str << \"arguments for parameters #{quoted_names.join \", \"} already specified\"\n        end\n      end\n    end\n  end\n\n  private def gather_names_in_all_overloads(call_errors, error_type : T.class, &) forall T\n    return unless call_errors.all?(T)\n\n    call_errors = call_errors.map &.as(T)\n    all_names = call_errors.flat_map(&.names).uniq!\n    names_in_all_overloads = all_names.select do |missing_name|\n      call_errors.all? &.names.includes?(missing_name)\n    end\n    unless names_in_all_overloads.empty?\n      yield call_errors, names_in_all_overloads\n    end\n  end\n\n  private def check_wrong_number_of_arguments(call_errors, owner, defs, def_name, arg_types, named_args_types, inner_exception)\n    return unless call_errors.all?(WrongNumberOfArguments)\n\n    raise_matches_not_found_named_args(owner, def_name, defs, arg_types, named_args_types, inner_exception)\n  end\n\n  private def check_extra_types_arguments_mismatch(call_errors, owner, defs, def_name, arg_types, named_args_types, inner_exception)\n    call_errors = call_errors.select(ArgumentsTypeMismatch)\n    return if call_errors.empty?\n\n    call_errors = call_errors.map &.as(ArgumentsTypeMismatch)\n    argument_type_mismatches = call_errors.flat_map(&.errors)\n\n    argument_type_mismatches.select!(&.extra_types)\n    return if argument_type_mismatches.empty?\n\n    argument_type_mismatches.each do |target_error|\n      index_or_name = target_error.index_or_name\n\n      mismatches = argument_type_mismatches.select(&.index_or_name.==(index_or_name))\n      expected_types = mismatches.map(&.expected_type).uniq!\n      actual_type = mismatches.first.actual_type\n\n      actual_types =\n        if actual_type.is_a?(UnionType)\n          actual_type.union_types\n        else\n          [actual_type] of Type\n        end\n\n      # It could happen that a type that's missing in one overload is actually\n      # covered in another overload, and eventually all overloads are covered\n      if expected_types.to_set == actual_types.to_set\n        expected_types = [target_error.expected_type]\n      end\n\n      raise_argument_type_mismatch(index_or_name, actual_type, expected_types.sort_by!(&.to_s), owner, defs, def_name, arg_types, inner_exception)\n    end\n  end\n\n  private def check_arguments_type_mismatch(call_errors, owner, defs, def_name, arg_types, named_args_types, inner_exception)\n    call_errors = call_errors.select(ArgumentsTypeMismatch)\n    return if call_errors.empty?\n\n    call_errors = call_errors.map &.as(ArgumentsTypeMismatch)\n    argument_type_mismatches = call_errors.flat_map(&.errors)\n\n    all_indexes_or_names = argument_type_mismatches.map(&.index_or_name).uniq!\n    indexes_or_names_in_all_overloads = all_indexes_or_names.select do |index_or_name|\n      call_errors.all? &.errors.any? &.index_or_name.==(index_or_name)\n    end\n\n    return if indexes_or_names_in_all_overloads.empty?\n\n    # Only show the first error. We'll still list all overloads.\n    index_or_name = indexes_or_names_in_all_overloads.first\n\n    mismatches = argument_type_mismatches.select(&.index_or_name.==(index_or_name))\n    expected_types = mismatches.map(&.expected_type).uniq!.sort_by!(&.to_s)\n    actual_type = mismatches.first.actual_type\n\n    raise_argument_type_mismatch(index_or_name, actual_type, expected_types, owner, defs, def_name, arg_types, inner_exception)\n  end\n\n  private def raise_argument_type_mismatch(index_or_name, actual_type, expected_types, owner, defs, def_name, arg_types, inner_exception)\n    arg =\n      case index_or_name\n      in Int32\n        args[index_or_name]?\n      in String\n        named_args.try &.find(&.name.==(index_or_name))\n      end\n\n    enum_types = expected_types.select(EnumType)\n    if actual_type.is_a?(SymbolType) && enum_types.size == 1\n      enum_type = enum_types.first\n\n      if arg.is_a?(SymbolLiteral)\n        symbol = arg.value\n      elsif arg.is_a?(NamedArgument) && (named_arg_value = arg.value).is_a?(SymbolLiteral)\n        symbol = named_arg_value.value\n      end\n    end\n\n    raise_no_overload_matches(arg || self, defs, arg_types, inner_exception) do |str|\n      argument_description =\n        case index_or_name\n        in Int32\n          \"##{index_or_name + 1}\"\n        in String\n          \"'#{index_or_name}'\"\n        end\n\n      if symbol && enum_type\n        str << \"expected argument #{argument_description} to '#{full_name(owner, def_name)}' to match a member of enum #{enum_type}.\"\n        str.puts\n        str.puts\n\n        options = enum_type.types.map(&.[1].name.underscore)\n        similar_name = Levenshtein.find(symbol.underscore, options)\n        if similar_name\n          str << \"Did you mean :#{similar_name}?\"\n        elsif options.size <= 10\n          str << \"Options are: \"\n          to_sentence(str, options.map { |o| \":#{o}\" }, \" and \")\n        end\n      else\n        str << \"expected argument #{argument_description} to '#{full_name(owner, def_name)}' to be \"\n        to_sentence(str, expected_types, \" or \")\n        str << \", not #{actual_type.devirtualize}\"\n      end\n    end\n  end\n\n  private def to_sentence(str : IO, elements : Array, last_connector : String)\n    elements.each_with_index do |element, i|\n      if i == elements.size - 1 && elements.size > 1\n        str << last_connector\n      elsif i > 0\n        str << \", \"\n      end\n      str << element\n    end\n  end\n\n  private def raise_no_overload_matches(node, defs, arg_types, inner_exception, &)\n    error_message = String.build do |str|\n      yield str\n\n      str.puts\n      str.puts\n      str << \"Overloads are:\"\n      append_matches(defs, arg_types, str)\n    end\n\n    node.raise(error_message, inner_exception)\n  end\n\n  record WrongNumberOfArguments\n  record MissingNamedArguments, names : Array(String)\n  record BlockMismatch\n  record ExtraNamedArguments, names : Array(String), similar_names : Array(String?)\n  record ArgumentsAlreadySpecified, names : Array(String)\n  record ArgumentsTypeMismatch, errors : Array(ArgumentTypeMismatch)\n  record ArgumentTypeMismatch,\n    index_or_name : (Int32 | String),\n    expected_type : Type | ASTNode,\n    actual_type : Type,\n    extra_types : Array(Type)?\n\n  private def compute_call_error_reason(owner, a_def, arg_types, named_args_types)\n    if (block && !a_def.block_arity) || (!block && a_def.block_arity)\n      return BlockMismatch.new\n    end\n\n    if !named_args_types\n      min_size, max_size = a_def.min_max_args_sizes\n      unless min_size <= arg_types.size <= max_size\n        return WrongNumberOfArguments.new\n      end\n    end\n\n    missing_named_args = extract_missing_named_args(a_def, named_args)\n    if missing_named_args\n      return MissingNamedArguments.new(missing_named_args)\n    end\n\n    arguments_already_specified = [] of String\n    extra_named_argument_names = [] of String\n    extra_named_argument_similar_names = [] of String?\n\n    named_args_types.try &.each do |named_arg|\n      found_index = a_def.args.index { |arg| arg.external_name == named_arg.name }\n      if found_index\n        min_size = arg_types.size\n        if found_index < min_size\n          arguments_already_specified << named_arg.name\n        end\n      elsif !a_def.double_splat\n        similar_name = Levenshtein.find(named_arg.name, a_def.args.select(&.default_value).map(&.external_name))\n\n        extra_named_argument_names << named_arg.name\n        extra_named_argument_similar_names << similar_name\n      end\n    end\n\n    unless arguments_already_specified.empty?\n      return ArgumentsAlreadySpecified.new(arguments_already_specified)\n    end\n\n    unless extra_named_argument_names.empty?\n      return ExtraNamedArguments.new(extra_named_argument_names, extra_named_argument_similar_names)\n    end\n\n    # For now let's not deal with splats\n    return if a_def.splat_index\n\n    a_def_owner = a_def.owner\n\n    # This is the actual instantiated type where the method was instantiated\n    instantiated_owner = owner\n\n    owner.ancestors.each do |ancestor|\n      if a_def_owner == ancestor\n        instantiated_owner = ancestor\n        break\n      end\n\n      # If the method is defined in a generic uninstantiated type\n      # then the method instantiation happens on the instantiated generic\n      # type whose generic type is that uninstantiated one.\n      if a_def_owner.is_a?(GenericType) &&\n         ancestor.is_a?(GenericInstanceType) &&\n         ancestor.generic_type == a_def_owner\n        instantiated_owner = ancestor\n        break\n      end\n    end\n\n    match_context = MatchContext.new(\n      instantiated_type: instantiated_owner,\n      defining_type: instantiated_owner,\n      def_free_vars: a_def.free_vars,\n    )\n\n    arguments_type_mismatch = [] of ArgumentTypeMismatch\n\n    arg_types.each_with_index do |arg_type, i|\n      def_arg = a_def.args[i]?\n      next unless def_arg\n\n      check_argument_type_mismatch(def_arg, i, arg_type, match_context, arguments_type_mismatch)\n    end\n\n    named_args_types.try &.each do |named_arg|\n      def_arg = a_def.args.find &.external_name.==(named_arg.name)\n      next unless def_arg\n\n      check_argument_type_mismatch(def_arg, named_arg.name, named_arg.type, match_context, arguments_type_mismatch)\n    end\n\n    unless arguments_type_mismatch.empty?\n      return ArgumentsTypeMismatch.new(arguments_type_mismatch)\n    end\n\n    nil\n  end\n\n  private def check_argument_type_mismatch(def_arg, index_or_name, arg_type, match_context, arguments_type_mismatch)\n    restricted = arg_type.restrict(def_arg, match_context)\n\n    arg_type = arg_type.remove_literal\n    return if restricted == arg_type\n\n    expected_type = compute_expected_type(def_arg, match_context)\n\n    extra_types =\n      if restricted\n        # This was a partial match\n        compute_extra_types(arg_type, expected_type)\n      else\n        # This wasn't a match\n        nil\n      end\n\n    arguments_type_mismatch << ArgumentTypeMismatch.new(\n      index_or_name: index_or_name,\n      expected_type: expected_type,\n      actual_type: arg_type.remove_literal,\n      extra_types: extra_types,\n    )\n  end\n\n  private def compute_expected_type(def_arg, match_context)\n    expected_type = def_arg.type?\n    unless expected_type\n      restriction = def_arg.restriction\n      if restriction\n        expected_type = match_context.instantiated_type.lookup_type?(restriction, free_vars: match_context.bound_free_vars)\n      end\n    end\n    expected_type ||= def_arg.restriction.not_nil!\n    expected_type = expected_type.devirtualize if expected_type.is_a?(Type)\n    expected_type\n  end\n\n  private def compute_extra_types(actual_type, expected_type)\n    expected_types =\n      if expected_type.is_a?(UnionType)\n        expected_type.union_types\n      elsif expected_type.is_a?(Type)\n        [expected_type] of Type\n      else\n        return\n      end\n\n    actual_types =\n      if actual_type.is_a?(UnionType)\n        actual_type.union_types\n      else\n        [actual_type] of Type\n      end\n\n    actual_types - expected_types\n  end\n\n  private def no_overload_matches_message(io, full_name, defs, args, arg_types, named_args_types)\n    if message = single_def_error_message(defs, named_args_types)\n      io << message\n      io << '\\n'\n      return\n    end\n\n    io << \"no overload matches '#{full_name}'\"\n    unless args.empty?\n      io << \" with type\"\n      io << 's' if arg_types.size > 1 || named_args_types\n      io << ' '\n      arg_types.join(io, \", \")\n    end\n\n    if named_args_types\n      named_args_types.each do |named_arg|\n        io << \", \"\n        io << named_arg.name\n        io << \": \"\n        io << named_arg.type\n      end\n    end\n\n    io << '\\n'\n  end\n\n  private def raise_undefined_method(owner, def_name, obj)\n    check_macro_wrong_number_of_arguments(def_name)\n\n    owner_trace = obj.try &.find_owner_trace(owner.program, owner)\n    similar_name = owner.lookup_similar_def_name(def_name, self.args.size, block)\n\n    error_msg = String.build do |msg|\n      if obj\n        could_be_local_variable = false\n      elsif logical_op = convert_to_logical_operator(def_name)\n        similar_name = logical_op\n        could_be_local_variable = false\n      elsif args.size > 0 || has_parentheses?\n        could_be_local_variable = false\n      else\n        # This check is for the case `a if a = 1`\n        similar_name = parent_visitor.lookup_similar_var_name(def_name) unless similar_name\n        could_be_local_variable = (similar_name != def_name)\n      end\n\n      if could_be_local_variable\n        msg << \"undefined local variable or method '#{def_name}'\"\n      else\n        msg << \"undefined method '#{def_name}'\"\n      end\n\n      owner_name = owner.is_a?(Program) ? \"top-level\" : owner.to_s\n\n      if with_scope && !obj && with_scope != owner\n        msg << \" for #{with_scope} (with ... yield) and #{owner_name} (current scope)\"\n      else\n        msg << \" for #{owner_name}\"\n      end\n\n      if def_name == \"allocate\" && owner.is_a?(MetaclassType) && owner.instance_type.module?\n        msg << colorize(\" (modules cannot be instantiated)\").yellow.bold\n      end\n\n      if obj && obj.type != owner\n        msg << colorize(\" (compile-time type is #{obj.type})\").yellow.bold\n      end\n\n      if similar_name\n        msg << '\\n'\n        if similar_name == def_name\n          # This check is for the case `a if a = 1`\n          msg << \"If you declared '#{def_name}' in a suffix if, declare it in a regular if for this to work. If the variable was declared in a macro it's not visible outside it.\"\n        else\n          msg << \"Did you mean '#{similar_name}'?\"\n        end\n      end\n\n      # Check if it's an instance variable that was never assigned a value\n      if obj.is_a?(InstanceVar)\n        scope = self.scope\n        ivar = scope.lookup_instance_var(obj.name)\n        if ivar.dependencies.size == 1 && ivar.dependencies.first.same?(program.nil_var)\n          similar_name = scope.lookup_similar_instance_var_name(ivar.name)\n          if similar_name\n            msg << colorize(\" (#{ivar.name} was never assigned a value, did you mean #{similar_name}?)\").yellow.bold\n          else\n            msg << colorize(\" (#{ivar.name} was never assigned a value)\").yellow.bold\n          end\n        end\n      end\n    end\n    raise error_msg, owner_trace\n  end\n\n  private def raise_matches_not_found_named_args(owner, def_name, defs, arg_types, named_args_types, inner_exception)\n    all_arguments_sizes = [] of Int32\n    min_splat = Int32::MAX\n    defs.each do |a_def|\n      next if (block && !a_def.block_arity) || (!block && a_def.block_arity)\n\n      min_size, max_size = a_def.min_max_args_sizes\n      if max_size == Int32::MAX\n        min_splat = Math.min(min_size, min_splat)\n        all_arguments_sizes.push min_splat\n      else\n        min_size.upto(max_size) do |size|\n          all_arguments_sizes.push size\n        end\n      end\n    end\n    all_arguments_sizes.uniq!.sort!\n\n    raise(String.build do |str|\n      if single_message = single_def_error_message(defs, named_args_types)\n        str << single_message\n        str << '\\n'\n      else\n        str << \"wrong number of arguments for '\"\n        str << full_name(owner, def_name)\n        str << \"' (given \"\n        str << arg_types.size\n        str << \", expected \"\n\n        # If we have 2, 3, 4, show it as 2..4\n        if all_arguments_sizes.size > 1 && all_arguments_sizes.last - all_arguments_sizes.first == all_arguments_sizes.size - 1\n          str << all_arguments_sizes.first\n          str << \"..\"\n          str << all_arguments_sizes.last\n        else\n          all_arguments_sizes.join str, \", \"\n        end\n\n        str << '+' if min_splat != Int32::MAX\n        str << \")\\n\"\n      end\n      str << \"Overloads are:\"\n      append_matches(defs, arg_types, str)\n    end, inner: inner_exception)\n  end\n\n  def convert_to_logical_operator(def_name)\n    case def_name\n    when \"and\"; \"&&\"\n    when \"or\" ; \"||\"\n    when \"not\"; \"!\"\n    else        nil\n    end\n  end\n\n  def single_def_error_message(defs, named_args)\n    if defs.size == 1\n      missing_argument_message(defs.first, named_args)\n    end\n  end\n\n  def missing_argument_message(a_def, named_args)\n    missing_args = extract_missing_named_args(a_def, named_args)\n    return unless missing_args\n\n    if missing_args.size == 1\n      \"missing argument: #{missing_args.first}\"\n    else\n      \"missing arguments: #{missing_args.join \", \"}\"\n    end\n  end\n\n  def extract_missing_named_args(a_def, named_args)\n    splat_index = a_def.splat_index\n    return if !splat_index && !named_args\n    return if splat_index == a_def.args.size - 1\n\n    # Check if some mandatory arguments are missing\n    mandatory_args = BitArray.new(a_def.args.size)\n    a_def.match(args) do |arg, arg_index, call_arg, call_arg_index|\n      mandatory_args[arg_index] = true\n    end\n\n    named_args.try &.each do |named_arg|\n      found_index = a_def.args.index { |arg| arg.external_name == named_arg.name }\n      if found_index\n        mandatory_args[found_index] = true\n      end\n    end\n\n    missing_args = [] of String\n    mandatory_args.each_with_index do |value, index|\n      next if value\n\n      arg = a_def.args[index]\n      next if arg.default_value\n      next if arg.external_name.empty?\n\n      missing_args << arg.external_name\n    end\n\n    return if missing_args.size.zero?\n\n    missing_args\n  end\n\n  def check_abstract_def_error(owner, matches, defs, def_name)\n    return unless !matches || (matches.try &.empty?)\n    return unless defs.all? &.abstract?\n\n    named_args_types = NamedArgumentType.from_args(named_args)\n    signature = CallSignature.new(def_name, args.map(&.type), block, named_args_types)\n    defs.each do |a_def|\n      context = MatchContext.new(owner, a_def.owner, def_free_vars: a_def.free_vars)\n      match = signature.match(DefWithMetadata.new(a_def), context)\n      next unless match\n\n      if a_def.owner == owner\n        owner.all_subclasses.each do |subclass|\n          submatches = subclass.lookup_matches(signature)\n          if submatches.empty?\n            raise_abstract_method_must_be_implemented a_def, subclass\n          end\n        end\n        raise_abstract_method_must_be_implemented a_def, owner\n      else\n        raise_abstract_method_must_be_implemented a_def, owner\n      end\n    end\n  end\n\n  def raise_abstract_method_must_be_implemented(a_def, owner)\n    if owner.abstract?\n      raise \"undefined method '#{def_full_name(a_def.owner, a_def)}'\"\n    else\n      raise \"abstract `def #{def_full_name(a_def.owner, a_def)}` must be implemented by #{owner}\"\n    end\n  end\n\n  def append_matches(defs, arg_types, str, *, matched_def = nil, argument_name = nil)\n    defs.each do |a_def|\n      next if a_def.abstract?\n      str << \"\\n - \"\n      append_def_full_name a_def.owner, a_def, arg_types, str\n      if defs.size > 1 && a_def.same?(matched_def)\n        str << colorize(\" (trying this one)\").blue\n      end\n      if a_def.args.any? { |arg| arg.default_value && arg.external_name == argument_name }\n        str << colorize(\" (did you mean this one?)\").yellow.bold\n      end\n    end\n  end\n\n  def def_full_name(owner, a_def, arg_types = nil)\n    Call.def_full_name(owner, a_def, arg_types)\n  end\n\n  def self.def_full_name(owner, a_def, arg_types = nil)\n    String.build { |io| append_def_full_name(owner, a_def, arg_types, io) }\n  end\n\n  def append_def_full_name(owner, a_def, arg_types, str)\n    Call.append_def_full_name(owner, a_def, arg_types, str)\n  end\n\n  def self.append_def_full_name(owner, a_def, arg_types, str)\n    str << full_name(owner, a_def.name)\n    str << '('\n    printed = false\n    a_def.args.each_with_index do |arg, i|\n      str << \", \" if printed\n      str << '*' if a_def.splat_index == i\n\n      if arg.external_name != arg.name\n        str << (arg.external_name.presence || '_')\n        str << ' '\n      end\n\n      str << arg.name\n\n      if arg_type = arg.type?\n        str << \" : \"\n        str << arg_type\n      elsif res = arg.restriction\n        str << \" : \"\n        if owner.is_a?(GenericClassInstanceType) && res.is_a?(Path) && res.names.size == 1 &&\n           (type_var = owner.type_vars[res.names[0]]?)\n          str << type_var.type\n        else\n          # Try to use the full name if the argument type and the call\n          # argument type have the same string representation\n          res_to_s = res.to_s\n          if (arg_type = arg_types.try &.[i]?) && arg_type.to_s == res_to_s &&\n             (matching_type = a_def.owner.lookup_type?(res))\n            str << matching_type\n          else\n            str << res_to_s\n          end\n        end\n      end\n      if arg_default = arg.default_value\n        str << \" = \"\n        str << arg_default\n      end\n      printed = true\n    end\n\n    if a_def.double_splat\n      str << \", \" if printed\n      str << \"**\" << a_def.double_splat\n      printed = true\n    end\n\n    if a_def.block_arity\n      str << \", \" if printed\n      str << '&'\n      if block_arg = a_def.block_arg\n        str << block_arg\n      end\n    end\n    str << ')'\n\n    if free_vars = a_def.free_vars\n      str << \" forall \"\n      free_vars.join(str, \", \")\n    end\n  end\n\n  def raise_matches_not_found_for_virtual_metaclass_new(owner)\n    owner.each_concrete_type do |concrete_type|\n      defs = concrete_type.instance_type.lookup_defs_with_modules(\"initialize\")\n      defs = defs.select { |a_def| a_def.args.size != args.size }\n      unless defs.empty?\n        all_arguments_sizes = Set(Int32).new\n        defs.each { |a_def| all_arguments_sizes << a_def.args.size }\n        wrong_number_of_arguments \"'#{concrete_type.instance_type}#initialize'\", args.size, all_arguments_sizes.join(\", \")\n      end\n    end\n  end\n\n  def check_macro_wrong_number_of_arguments(def_name)\n    return if (obj = self.obj) && !obj.is_a?(Path)\n\n    macros = in_macro_target &.lookup_macros(def_name)\n    return unless macros.is_a?(Array(Macro))\n\n    if msg = single_def_error_message(macros, named_args)\n      raise msg\n    end\n\n    all_arguments_sizes = Set(String).new\n    macros.each do |a_macro|\n      named_args.try &.each do |named_arg|\n        index = a_macro.args.index { |arg| arg.external_name == named_arg.name }\n        if index\n          if index < args.size\n            raise \"argument for parameter '#{named_arg.name}' already specified\"\n          end\n        else\n          raise \"no parameter named '#{named_arg.name}'\"\n        end\n      end\n\n      plus = false\n\n      splat_index = a_macro.splat_index\n      if splat_index\n        if a_macro.args[splat_index].name.empty?\n          min_size = max_size = splat_index\n        else\n          min_size = splat_index\n          max_size = a_macro.args.size\n          plus = true\n        end\n      else\n        min_size = a_macro.args.index(&.default_value) || a_macro.args.size\n        max_size = a_macro.args.size\n      end\n\n      if plus\n        all_arguments_sizes << \"#{min_size}+\"\n      else\n        min_size.upto(max_size) do |args_size|\n          all_arguments_sizes << args_size.to_s\n        end\n      end\n    end\n\n    wrong_number_of_arguments \"macro '#{def_name}'\", args.size, all_arguments_sizes.join(\", \")\n  end\n\n  def check_visibility(match)\n    case match.def.visibility\n    when .private?\n      if obj = @obj\n        if obj.is_a?(Var) && obj.name == \"self\"\n          # Special case: private method can be called with self\n          return\n        end\n\n        if name == \"initialize\" && parent_visitor.call.try(&.name) == \"new\"\n          # Special case: initialize call inside automatically defined new\n          return\n        end\n\n        raise \"private method '#{match.def.name}' called for #{match.def.owner}\"\n      end\n    when .protected?\n      scope_type = scope.instance_type\n      owner_type = match.def.owner.instance_type\n\n      unless scope_type.has_protected_access_to?(owner_type)\n        raise \"protected method '#{match.def.name}' called for #{match.def.owner}\"\n      end\n    when .public?\n      # okay\n    end\n  end\n\n  def check_recursive_splat_call(a_def, args, &)\n    if a_def.splat_index\n      previous_splat_types = program.splat_expansions[a_def] ||= [] of Type\n      previous_splat_types.push(args.values.last.type)\n\n      # This is just an heuristic, but if a same method is called recursively\n      # 5 times or more, and the splat type keeps expanding and containing\n      # previous splat types, there's a high chance it will recurse forever.\n      if previous_splat_types.size >= 5 &&\n         previous_splat_types.each.cons_pair.all? { |t1, t2| t2.has_in_type_vars?(t1) }\n        a_def.raise \"recursive splat expansion: #{previous_splat_types.join(\", \")}, ...\"\n      end\n\n      yield\n\n      previous_splat_types.pop\n      program.splat_expansions.delete a_def if previous_splat_types.empty?\n    else\n      yield\n    end\n  end\n\n  def full_name(owner, def_name = name)\n    Call.full_name(owner, def_name)\n  end\n\n  def self.full_name(owner, method_name = name)\n    case owner\n    when Program, Nil\n      method_name\n    when owner.program.class_type\n      # Class's instance_type is Object, not Class, so we cannot treat it like\n      # other metaclasses\n      \"#{owner}##{method_name}\"\n    when .metaclass?\n      \"#{owner.instance_type}.#{method_name}\"\n    else\n      \"#{owner}##{method_name}\"\n    end\n  end\n\n  def signature(io : IO) : Nil\n    io << full_name(obj.try(&.type)) << '('\n\n    first = true\n    args.each do |arg|\n      case {arg_type = arg.type, arg}\n      when {TupleInstanceType, Splat}\n        next if arg_type.tuple_types.empty?\n        io << \", \" unless first\n        arg_type.tuple_types.join(io, \", \")\n      when {NamedTupleInstanceType, DoubleSplat}\n        next if arg_type.entries.empty?\n        io << \", \" unless first\n        arg_type.entries.join(io, \", \") do |entry|\n          Symbol.quote_for_named_argument(io, entry.name)\n          io << \": \" << entry.type\n        end\n      else\n        io << \", \" unless first\n        io << arg.type\n      end\n      first = false\n    end\n\n    if named_args = @named_args\n      io << \", \" unless first\n      named_args.join(io, \", \") do |named_arg|\n        Symbol.quote_for_named_argument(io, named_arg.name)\n        io << \": \" << named_arg.value.type\n      end\n    end\n\n    io << ')'\n  end\n\n  private def colorize(obj)\n    program.colorize(obj)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/class_vars_initializer_visitor.cr",
    "content": "require \"./semantic_visitor\"\n\nmodule Crystal\n  class ClassVarInitializer\n    getter owner : ClassVarContainer,\n      name : String,\n      meta_vars : MetaVars\n    property node : ASTNode\n\n    def initialize(@owner, @name, @node, @meta_vars)\n    end\n  end\n\n  class Program\n    def visit_class_vars_initializers(node)\n      visitor = ClassVarsInitializerVisitor.new(self)\n      visit_with_finished_hooks(node, visitor)\n\n      # First gather them all\n      class_var_initializers = [] of ClassVarInitializer\n      visitor.class_vars.each do |owner, vars|\n        vars.each do |name, assign_and_node|\n          node = assign_and_node[1]\n\n          # If the initializer is nil there's no need to initialize anything\n          next if node.is_a?(NilLiteral)\n\n          meta_vars = MetaVars.new\n          class_var_initializers << ClassVarInitializer.new(owner, name, node, meta_vars)\n        end\n      end\n\n      # Put the simple ones (literal like nil, 1, true, \"foo\", etc.) at the beginning,\n      # so they are initialized before everything else and they are typed and can\n      # be used by code that could come before them, so circular dependencies are less\n      # of a problem.\n      simple_vars, complex_vars = class_var_initializers.partition &.node.simple_literal?\n      class_var_initializers = simple_vars + complex_vars\n\n      # Next assign their initializer, so we know which are initialized\n      # and shouldn't raise an error when trying to accessing them\n      # before they are defined\n      class_var_initializers.each do |initializer|\n        class_var = initializer.owner.class_vars[initializer.name]?\n        class_var.initializer = initializer if class_var\n      end\n\n      # Now type them\n      class_var_initializers.each do |initializer|\n        owner = initializer.owner\n        node = initializer.node\n        name = initializer.name\n        meta_vars = initializer.meta_vars\n\n        main_visitor = MainVisitor.new(self, meta_vars: meta_vars)\n        main_visitor.scope = owner.metaclass\n\n        # We want to first type the value, because it might\n        # happened that we couldn't guess a type from an expression\n        # but that expression has an error: we want to signal\n        # that error first.\n        had_class_var = true\n        class_var = owner.class_vars[name]?\n        unless class_var\n          class_var = MetaTypeVar.new(name)\n          class_var.owner = owner\n          had_class_var = false\n        end\n\n        main_visitor.pushing_type(owner.as(ModuleType)) do\n          # Check if we can autocast\n          if node.supports_autocast?(!@program.has_flag?(\"no_number_autocast\")) && (class_var_type = class_var.type?)\n            cloned_node = node.clone\n            cloned_node.accept MainVisitor.new(self)\n            if casted_value = MainVisitor.check_automatic_cast(@program, cloned_node, class_var_type)\n              node = initializer.node = casted_value\n            end\n          end\n\n          node.accept main_visitor unless node.type?\n        end\n\n        unless had_class_var\n          main_visitor.undefined_class_variable(class_var, owner)\n        end\n\n        class_var.bind_to(node)\n        class_var.initializer = initializer\n        self.class_var_initializers << initializer\n      end\n\n      node\n    end\n  end\n\n  # In this pass we gather class var initializers like:\n  #\n  # ```\n  # class Foo\n  #   @@x = 1\n  # end\n  # ```\n  #\n  # The last initializer set for a type is the one that\n  # will be used.\n  #\n  # Class variables don't have access to\n  # outside local variables. This won't compile:\n  #\n  # ```\n  # class Foo\n  #   a = 1\n  #   @@x = a # ERROR\n  # end\n  # ```\n  class ClassVarsInitializerVisitor < SemanticVisitor\n    getter class_vars\n\n    def initialize(mod)\n      super(mod)\n\n      @class_vars = {} of ClassVarContainer => Hash(String, {ASTNode, ASTNode})\n    end\n\n    def visit_any(node)\n      case node\n      when Assign\n        node.target.is_a?(ClassVar)\n      when TypeDeclaration\n        node.var.is_a?(ClassVar)\n      when FileNode, Expressions, ClassDef, ModuleDef, EnumDef, Alias, Include, Extend, LibDef, Def, Macro, Call, Require,\n           MacroExpression, MacroIf, MacroFor, VisibilityModifier\n        true\n      else\n        false\n      end\n    end\n\n    def visit(node : Assign)\n      target = node.target.as(ClassVar)\n      value = node.value\n      type_class_var(target, node, value)\n      false\n    end\n\n    def visit(node : TypeDeclaration)\n      target = node.var.as(ClassVar)\n      value = node.value\n      type_class_var(target, node, value) if value\n      false\n    end\n\n    def type_class_var(target, node, value)\n      owner = class_var_owner(target)\n      cvars = @class_vars[owner] ||= {} of String => {ASTNode, ASTNode}\n      existing = cvars[target.name]?\n      cvars[target.name] = {node.as(ASTNode), value}\n      if existing\n        node_to_discard = existing[0]\n        case node_to_discard\n        when Assign\n          node_to_discard.discarded = true\n        when TypeDeclaration\n          node_to_discard.discarded = true\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/cleanup_transformer.cr",
    "content": "require \"../syntax/ast\"\nrequire \"../syntax/transformer\"\nrequire \"../types\"\n\nmodule Crystal\n  class Program\n    getter(cleanup_transformer : CleanupTransformer) do\n      CleanupTransformer.new(self)\n    end\n\n    def cleanup(node, inside_def = false)\n      transformer = self.cleanup_transformer\n      transformer.inside_def! if inside_def\n      node = node.transform(transformer)\n      puts node if ENV[\"AFTER\"]? == \"1\"\n      node\n    end\n\n    def cleanup_types\n      transformer = self.cleanup_transformer\n\n      after_inference_types.each do |type|\n        cleanup_type type, transformer\n      end\n\n      self.class_var_initializers.each do |initializer|\n        initializer.node = initializer.node.transform(transformer)\n      end\n    end\n\n    def cleanup_type(type, transformer)\n      case type\n      when GenericClassInstanceType\n        cleanup_single_type(type, transformer)\n      when GenericClassType\n        type.each_instantiated_type do |instance|\n          cleanup_type instance, transformer\n        end\n      when ClassType\n        cleanup_single_type(type, transformer)\n      else\n        # no need to clean up\n      end\n    end\n\n    def cleanup_single_type(type, transformer)\n      type.instance_vars_initializers.try &.each do |initializer|\n        initializer.value = initializer.value.transform(transformer)\n      end\n    end\n\n    def cleanup_files\n      tempfiles.each do |tempfile|\n        File.delete?(tempfile)\n      end\n    end\n  end\n\n  # This visitor runs at the end and does some simplifications to the resulting AST node.\n  #\n  # For example, it rewrites and `if true; 1; else; 2; end` to a single `1`. It does\n  # so for other \"always true conditions\", such as `x.is_a?(Foo)` where `x` can only\n  # be of type `Foo`. These simplifications are needed because the codegen would have no\n  # idea on how to generate code for unreachable branches, because they have no type,\n  # and for now the codegen only deals with typed nodes.\n  class CleanupTransformer < Transformer\n    @transformed : Set(Def)\n\n    # The current method we are processing\n    @current_def : Def?\n\n    def initialize(@program : Program)\n      @transformed = Set(Def).new.compare_by_identity\n      @exhaustiveness_checker = ExhaustivenessChecker.new(@program)\n      @def_nest_count = 0\n      @last_is_truthy = false\n      @last_is_falsey = false\n    end\n\n    def inside_def!\n      @def_nest_count += 1\n    end\n\n    def after_transform(node)\n      case node\n      when And, Or, If, RespondsTo, IsA, Assign\n        # Nothing\n      when BoolLiteral\n        if node.value\n          @last_is_truthy = true\n        else\n          @last_is_falsey = true\n        end\n      when Not\n        if @last_is_truthy\n          @last_is_falsey = true\n          @last_is_truthy = false\n        elsif @last_is_falsey\n          @last_is_truthy = true\n          @last_is_falsey = false\n        else\n          reset_last_status\n        end\n      when NilLiteral\n        @last_is_falsey = true\n      when Nop\n        @last_is_falsey = true\n      else\n        if node.type?.try &.nil_type?\n          @last_is_falsey = true\n          @last_is_truthy = false\n        else\n          reset_last_status\n        end\n      end\n    end\n\n    def reset_last_status\n      @last_is_truthy = false\n      @last_is_falsey = false\n    end\n\n    def compute_last_truthiness(&)\n      reset_last_status\n      yield\n      {@last_is_truthy, @last_is_falsey}\n    end\n\n    def transform(node : AnnotationDef)\n      @program.check_call_to_deprecated_annotation node\n\n      node\n    end\n\n    def transform(node : Def)\n      node.hook_expansions.try &.map! &.transform self\n      node\n    end\n\n    def transform(node : ClassDef)\n      super\n\n      # check superclass for deprecation if the node isn't deprecated\n      if (type = node.type.lookup_type?(node.name)) && !type.annotation(@program.deprecated_annotation)\n        if ((superclass = node.superclass).is_a?(Path) && (stype = superclass.type? || node.type.lookup_type?(superclass)))\n          @program.check_deprecated_type(stype, superclass)\n        end\n      end\n\n      node.hook_expansions.try &.map! &.transform self\n      node\n    end\n\n    def transform(node : Include)\n      node.hook_expansions.try &.map! &.transform self\n      node\n    end\n\n    def transform(node : Extend)\n      node.hook_expansions.try &.map! &.transform self\n      node\n    end\n\n    def transform(node : Expressions)\n      if exp = node.single_expression?\n        return exp.transform(self)\n      end\n\n      exps = [] of ASTNode\n\n      node.expressions.each_with_index do |exp, i|\n        new_exp = exp.transform(self)\n\n        # We collect the transformed expressions, recursively,\n        # by flattening them. We stop collecting when there's\n        # a NoReturn expression, next, break or return.\n        break if flatten_collect(new_exp, exps)\n      end\n\n      node.expressions = exps\n      node\n    end\n\n    def flatten_collect(exp, exps)\n      exp = exp.single_expression\n      if exp.is_a?(Expressions)\n        exp.expressions.each do |subexp|\n          return true if flatten_collect(subexp, exps)\n        end\n      else\n        exps << exp\n        if exp.is_a?(Break) || exp.is_a?(Next) || exp.is_a?(Return) || exp.no_returns?\n          return true\n        end\n      end\n      false\n    end\n\n    def transform(node : Case)\n      @exhaustiveness_checker.check(node) if node.exhaustive?\n\n      if expanded = node.expanded\n        if node.exhaustive?\n          replace_unreachable_if_needed(node, expanded)\n        end\n\n        return expanded.transform(self)\n      end\n\n      node\n    end\n\n    # If any of the types checked in `case` is an enum, it can happen that\n    # the unreachable can be reached by doing `SomeEnum.new(some_value)`.\n    # In that case we replace the Unreachable node with `raise \"...\"`.\n    # In the future we should disallow creating such values.\n    def replace_unreachable_if_needed(node, expanded)\n      cond = node.cond\n      return unless cond\n\n      if cond.is_a?(TupleLiteral)\n        return unless cond.elements.all?(&.type?) &&\n                      cond.elements.any? { |element| has_enum_type?(element.type) }\n      else\n        cond_type = cond.type?\n        return unless cond_type && has_enum_type?(cond_type)\n      end\n\n      an_if = find_unreachable_parent(expanded)\n      unless an_if\n        node.raise \"BUG: expected to find Unreachable node\"\n      end\n\n      an_if.else = build_raise(\"Unhandled case: enum value outside of defined enum members\", node)\n    end\n\n    def has_enum_type?(type)\n      if type.is_a?(UnionType)\n        type.union_types.any?(EnumType)\n      else\n        type.is_a?(EnumType)\n      end\n    end\n\n    def find_unreachable_parent(expanded)\n      # An expanded case is either a series of if/else, or\n      # a bunch of assignments and then a series of if/else.\n      # The if/else chain always comes last.\n      if expanded.is_a?(Expressions)\n        expanded = expanded.expressions.last\n      end\n\n      while expanded.is_a?(If)\n        if an_else = expanded.else\n          if an_else.is_a?(Unreachable)\n            return expanded\n          else\n            expanded = an_else\n          end\n        end\n      end\n      nil\n    end\n\n    def transform(node : StringInterpolation)\n      # See if we can solve all the pieces to string literals.\n      # If that's the case, we can replace the entire interpolation\n      # with a single string literal.\n      pieces = node.expressions.dup\n      solve_string_interpolation_expressions(pieces)\n\n      if pieces.all?(StringLiteral)\n        string = pieces.join(&.as(StringLiteral).value)\n        string_literal = StringLiteral.new(string).at(node)\n        string_literal.type = @program.string\n        return string_literal\n      end\n\n      if expanded = node.expanded\n        return expanded.transform(self)\n      end\n      node\n    end\n\n    private def solve_string_interpolation_expressions(pieces : Array(ASTNode))\n      pieces.each_with_index do |piece, i|\n        replacement = solve_string_interpolation_expression(piece)\n        next unless replacement\n\n        pieces[i] = replacement\n      end\n    end\n\n    private def solve_string_interpolation_expression(piece : ASTNode) : StringLiteral?\n      if piece.is_a?(ExpandableNode)\n        if expanded = piece.expanded\n          return solve_string_interpolation_expression(expanded)\n        end\n      end\n\n      case piece\n      when Path\n        if target_const = piece.target_const\n          return solve_string_interpolation_expression(target_const.value)\n        end\n      when StringLiteral\n        return piece\n      end\n\n      nil\n    end\n\n    def transform(node : ExpandableNode)\n      if expanded = node.expanded\n        return expanded.transform(self)\n      end\n      node\n    end\n\n    def transform(node : Assign)\n      reset_last_status\n\n      if void_lib_call?(node.value)\n        node.value.raise \"assigning Void return value of lib fun call has no effect\"\n      end\n\n      target = node.target\n\n      # Ignore class var initializers\n      if target.is_a?(ClassVar) && !target.type?\n        return node\n      end\n\n      # This is the case of an instance variable initializer\n      if @def_nest_count == 0 && target.is_a?(InstanceVar)\n        return Nop.new\n      end\n\n      if target.is_a?(Path)\n        const = target.target_const.not_nil!\n        return node if !const.used? || const.cleaned_up?\n\n        unless const.value.type?\n          node.raise \"can't infer type of constant #{const} (maybe the constant refers to itself?)\"\n        end\n\n        if const.value.type.no_return?\n          node.raise \"constant #{const} has illegal type NoReturn\"\n        end\n      end\n\n      node.value = node.value.transform self\n\n      unless node.value.type?\n        return untyped_expression node\n      end\n\n      if target.is_a?(Path)\n        const = const.not_nil!\n        const.value = const.value.transform self\n        const.cleaned_up = true\n      end\n\n      if target == node.value &&\n         # This condition is because the interpreter will generate expressions\n         # like `$~ = $~` in multidispatches, and that's fine.\n         !(target.is_a?(Var) && target.special_var?)\n        node.raise \"expression has no effect\"\n      end\n\n      # We don't want to transform constant assignments into no return\n      unless node.target.is_a?(Path)\n        if node.value.type?.try &.no_return?\n          return node.value\n        end\n      end\n\n      node\n    end\n\n    def transform(node : MultiAssign)\n      expanded = node.expanded\n\n      if node.values.size == 1 && expanded\n        # the expanded node always starts with `temp = {{ node.values[0] }}`;\n        # `temp_assign` is this whole Assign node and its deduced type is same\n        # as the original RHS's type\n        temp_assign = expanded.as(Expressions).expressions.first\n        type = temp_assign.type?\n\n        # if the Assign node's RHS is untyped, this and all following\n        # assignments are unreachable\n        unless type\n          return untyped_expression node\n        end\n\n        target_count = node.targets.size\n        has_strict_multi_assign = @program.has_flag?(\"strict_multi_assign\")\n\n        # disallows `a, *b, c = {0 => \"x\", 1 => \"y\", 2 => \"z\"}`, as `Hash` is\n        # not `Indexable`\n        # also disallows `a, b, c = ...` if the strict flag is set\n        if node.targets.any?(Splat) || has_strict_multi_assign\n          unless type.implements?(@program.indexable)\n            node.values.first.raise \"right-hand side of one-to-many assignment must be an Indexable, not #{type}\"\n          end\n        end\n\n        if has_strict_multi_assign\n          case type\n          when UnionType\n            sizes = type.union_types.map { |union_type| constant_size(union_type) }\n            if sizes.none? &.in?(target_count, nil)\n              node.values.first.raise \"cannot assign #{type} to #{target_count} targets\"\n            end\n          else\n            if size = constant_size(type)\n              unless size == target_count\n                node.values.first.raise \"cannot assign #{type} to #{target_count} targets\"\n              end\n            end\n          end\n        end\n      end\n\n      if expanded\n        return expanded.transform self\n      end\n\n      node\n    end\n\n    private def constant_size(type)\n      case type\n      when TupleInstanceType\n        type.size\n      end\n    end\n\n    def transform(node : Path)\n      if const = node.target_const\n        @program.check_deprecated_constant(const, node)\n\n        # Some constants might not have been cleaned up at this point because\n        # they don't have an explicit `Assign` node. One example is regex\n        # literals: a constant is created for them, but there's no `Assign` node.\n        if const.used? && !const.cleaned_up?\n          const.value = const.value.transform self\n          const.cleaned_up = true\n        end\n      elsif type = node.target_type\n        @program.check_deprecated_type(type, node)\n      end\n\n      node\n    end\n\n    def transform(node : Generic)\n      transform_many node.type_vars\n\n      node\n    end\n\n    private def void_lib_call?(node)\n      return false unless node.is_a?(Call)\n\n      obj = node.obj\n      return false unless obj.is_a?(Path)\n\n      type = obj.type?\n      return false unless type.is_a?(LibType)\n\n      !!node.type?.try &.nil_type?\n    end\n\n    def transform(node : Global)\n      if expanded = node.expanded\n        return expanded.transform self\n      end\n\n      node\n    end\n\n    def transform(node : Call)\n      if expanded = node.expanded\n        return expanded.transform self\n      end\n\n      # Need to transform these manually because node.block doesn't\n      # need to be transformed if it has a fun_literal\n      # ~~~\n      if node_obj = node.obj\n        node.obj = node_obj.transform(self)\n      end\n      transform_many node.args\n\n      if (node_block = node.block) && !node_block.fun_literal\n        # TODO: if the block isn't used in any of the target defs we could\n        # avoid transforming it, because it probably isn't typed **at all**\n        node.block = node_block.transform(self)\n      end\n\n      if node_block_arg = node.block_arg\n        node.block_arg = node_block_arg.transform(self)\n      end\n\n      if named_args = node.named_args\n        named_args.map! { |named_arg| named_arg.transform(self).as(NamedArgument) }\n      end\n      # ~~~\n\n      node.args.each do |arg|\n        if void_lib_call?(arg)\n          arg.raise \"passing Void return value of lib fun call has no effect\"\n        end\n      end\n\n      named_args.try &.each do |arg|\n        if void_lib_call?(arg)\n          arg.raise \"passing Void return value of lib fun call has no effect\"\n        end\n      end\n\n      obj = node.obj\n      obj_type = obj.try &.type?\n      block = node.block\n\n      # It might happen that a call was made on a module or an abstract class\n      # and we don't know the type because there are no including classes or subclasses.\n      # In that case, turn this into an untyped expression.\n      if !node.type? && obj && obj_type && (obj_type.module? || obj_type.abstract? || obj_type.is_a?(GenericType))\n        return untyped_expression(node, \"`#{node}` has no type\")\n      end\n\n      if block && (fun_literal = block.fun_literal)\n        block.fun_literal = fun_literal.transform(self)\n      end\n\n      # Check if we have an untyped expression in this call. Replace it with raise.\n      if (obj && !obj_type)\n        return untyped_expression(node, \"`#{obj}` has no type\")\n      end\n\n      node.args.each do |arg|\n        unless arg.type?\n          return untyped_expression(node, \"`#{arg}` has no type\")\n        end\n      end\n\n      node.named_args.try &.each do |named_arg|\n        unless named_arg.value.type?\n          return untyped_expression(node, \"`#{named_arg}` has no type\")\n        end\n      end\n\n      # Check if the block has its type frozen and it doesn't match the current type\n      if block && (freeze_type = block.freeze_type) && (block_type = block.type?)\n        unless block_type.implements?(freeze_type)\n          freeze_type = freeze_type.base_type if freeze_type.is_a?(VirtualType)\n          node.raise \"expected block to return #{freeze_type}, not #{block_type}\"\n        end\n      end\n\n      # If any expression is no-return, replace the call with its expressions up to\n      # the one that no returns.\n      if (obj.try &.type?.try &.no_return?) || (node.args.any? &.type?.try &.no_return?) ||\n         (node.named_args.try &.any? &.value.type?.try &.no_return?)\n        call_exps = [] of ASTNode\n        call_exps << obj if obj\n        unless obj.try &.type?.try &.no_return?\n          node.args.each do |arg|\n            call_exps << arg\n            break if arg.type?.try &.no_return?\n          end\n          node.named_args.try &.each do |named_arg|\n            call_exps << named_arg.value\n            break if named_arg.value.type?.try &.no_return?\n          end\n        end\n        exps = Expressions.new(call_exps)\n        exps.set_type(call_exps.last.type?) unless call_exps.empty?\n        return exps\n      end\n\n      if target_defs = node.target_defs\n        if target_defs.size == 1\n          if target_defs[0].is_a?(External)\n            check_args_are_not_closure node, \"can't send closure to C function\"\n          elsif obj_type && obj_type.extern? && node.name.ends_with?('=')\n            check_args_are_not_closure node, \"can't set closure as C #{obj_type.type_desc} member\"\n          end\n        end\n\n        current_def = @current_def\n\n        target_defs.each do |target_def|\n          if @transformed.add?(target_def)\n            node.bubbling_exception do\n              @current_def = target_def\n              @def_nest_count += 1\n              target_def.body = target_def.body.transform(self)\n              @def_nest_count -= 1\n              @current_def = current_def\n            end\n          end\n\n          # If the current call targets a method that raises, the method\n          # where the call happens also raises.\n          current_def.raises = true if current_def && target_def.raises?\n        end\n\n        if node.target_defs.not_nil!.empty?\n          exps = [] of ASTNode\n          if obj = node.obj\n            exps.push obj\n          end\n          node.args.each { |arg| exps.push arg }\n          call_exps = Expressions.from exps\n          call_exps.set_type(exps.last.type?) unless exps.empty?\n          return call_exps\n        end\n      end\n\n      node.replace_splats\n\n      # Convert named arguments to regular arguments, because intermediate\n      # defs with the needed number of arguments are already defined.\n      if named_args = node.named_args\n        named_args.each do |named_arg|\n          node.args << named_arg.value\n        end\n        node.named_args = nil\n      end\n\n      # Check deprecations last, after the arguments have been flattened.\n      unless @current_def.try(&.annotation(@program.deprecated_annotation))\n        @program.check_call_to_deprecated_method(node)\n      end\n\n      node\n    end\n\n    class ClosuredVarsCollector < Visitor\n      getter vars : Array(ASTNode)\n      @a_def : Def\n\n      def self.collect(a_def)\n        visitor = new a_def\n        a_def.accept visitor\n        visitor.vars\n      end\n\n      def initialize(@a_def)\n        @vars = [] of ASTNode\n      end\n\n      def visit(node : Var)\n        if @a_def.vars.try &.[node.name]?.try &.closured?\n          @vars << node\n        end\n        false\n      end\n\n      def visit(node : InstanceVar)\n        @vars << node\n        false\n      end\n\n      def visit(node : ASTNode)\n        true\n      end\n    end\n\n    def check_args_are_not_closure(node, message)\n      node.args.each do |arg|\n        check_arg_is_not_closure(node, message, arg)\n      end\n    end\n\n    def check_arg_is_not_closure(node, message, arg)\n      case arg\n      when Expressions\n        arg.expressions.each do |exp|\n          check_arg_is_not_closure(node, message, exp)\n        end\n      when Call\n        # If the call simply returns its captured block unchanged, we can detect\n        # closured vars inside the block during compile-time\n        if target_def_is_captured_block?(arg)\n          check_arg_is_not_closure(node, message, arg.block.not_nil!.fun_literal)\n        end\n      when ProcLiteral\n        if proc_pointer = arg.proc_pointer\n          case proc_pointer.obj\n          when Var\n            arg.raise \"#{message} (closured vars: #{proc_pointer.obj})\"\n          when InstanceVar\n            arg.raise \"#{message} (closured vars: self)\"\n          end\n        end\n\n        if arg.def.closure?\n          vars = ClosuredVarsCollector.collect arg.def\n          if vars.empty?\n            message += \" (closured vars: self)\"\n          else\n            message += \" (closured vars: #{vars.join \", \"})\"\n          end\n\n          arg.raise message\n        end\n      when ProcPointer\n        if expanded = arg.expanded\n          return check_arg_is_not_closure(node, message, expanded)\n        end\n\n        if arg.obj.try &.type?.try &.passed_as_self?\n          arg.raise \"#{message} (closured vars: self)\"\n        end\n\n        owner = arg.call.target_def.owner\n        if owner.passed_as_self?\n          arg.raise \"#{message} (closured vars: self)\"\n        end\n      end\n    end\n\n    # Checks whether *arg*'s target def has the following definition:\n    #\n    # ```\n    # def f(&block)\n    #   block\n    # end\n    # ```\n    #\n    # That is, the def returns its captured block and does nothing else. An\n    # example is Proc.new(&block).\n    def target_def_is_captured_block?(arg : Call)\n      a_def = arg.target_defs.try &.first\n      return false unless a_def\n\n      block_arg = a_def.block_arg\n      return false unless block_arg\n\n      body_var = a_def.body.as?(Var)\n      return false unless body_var && body_var.name == block_arg.name\n\n      true\n    end\n\n    def transform(node : ProcPointer)\n      super\n\n      if expanded = node.expanded\n        return transform(expanded)\n      end\n\n      if call = node.call?\n        result = call.transform(self)\n\n        # If the transform didn't end up in a Call, it means the\n        # call will never be executed.\n        if result.is_a?(Call)\n          node.call = result\n        end\n      end\n\n      node\n    end\n\n    def transform(node : ProcLiteral)\n      body = node.def.body\n      if node.def.no_returns? && !body.type?\n        node.def.body = untyped_expression(body)\n      else\n        node.def.body = node.def.body.transform(self)\n      end\n      node\n    end\n\n    def untyped_expression(node, msg = nil)\n      ex_msg = String.build do |str|\n        str << \"can't execute `\" << node << \"` at \" << node.location\n        if msg\n          str << \": \"\n          str << msg\n        end\n      end\n\n      build_raise ex_msg, node\n    end\n\n    def build_raise(msg : String, node : ASTNode)\n      call = Call.global(\"raise\", StringLiteral.new(msg).at(node)).at(node)\n      call.accept MainVisitor.new(@program)\n      call\n    end\n\n    def transform(node : Yield)\n      if expanded = node.expanded\n        return expanded.transform(self)\n      end\n\n      super\n\n      # If the yield has a no-return expression, the yield never happens:\n      # replace it with a series of expressions up to the one that no-returns.\n      no_return_index = node.exps.index { |exp| !exp.type? || exp.no_returns? }\n      if no_return_index\n        exps = Expressions.new(node.exps[0, no_return_index + 1])\n        exps.bind_to(exps.expressions.last)\n        return exps\n      end\n\n      node\n    end\n\n    def transform(node : While)\n      super\n\n      # If the condition is a NoReturn, just replace the whole\n      # while with it, since the body will never be executed\n      if node.cond.no_returns?\n        return node.cond\n      end\n\n      node\n    end\n\n    def transform(node : If)\n      cond_is_truthy, cond_is_falsey = compute_last_truthiness do\n        node.cond = node.cond.transform(self)\n      end\n\n      node_cond = node.cond\n\n      if node_cond.no_returns?\n        return node_cond\n      end\n\n      case\n      when node_cond.true_literal?\n        node.truthy = true\n      when node_cond.false_literal?\n        node.falsey = true\n      when (cond_type = node_cond.type?) && cond_type.nil_type?\n        node.falsey = true\n      when cond_is_truthy\n        node.truthy = true\n      when cond_is_falsey\n        node.falsey = true\n      else\n        # Not a special condition\n      end\n\n      if node.falsey?\n        then_is_truthy = false\n        then_is_falsey = false\n      else\n        then_is_truthy, then_is_falsey = compute_last_truthiness do\n          node.then = node.then.transform(self)\n        end\n      end\n\n      if node.truthy?\n        else_is_truthy = false\n        else_is_falsey = false\n      else\n        else_is_truthy, else_is_falsey = compute_last_truthiness do\n          node.else = node.else.transform(self)\n        end\n      end\n\n      case node\n      when .and?\n        @last_is_truthy = cond_is_truthy && then_is_truthy\n        @last_is_falsey = cond_is_falsey || then_is_falsey\n      when .or?\n        @last_is_truthy = cond_is_truthy || else_is_truthy\n        @last_is_falsey = cond_is_falsey && else_is_falsey\n      else\n        reset_last_status\n      end\n\n      node\n    end\n\n    def transform(node : IsA)\n      super\n      reset_last_status\n      if replacement = node.syntax_replacement\n        return replacement.transform(self)\n      end\n\n      const_type = node.const.type?\n      return untyped_expression(node) unless const_type\n\n      transform_is_a_or_responds_to node, &.filter_by(const_type)\n    end\n\n    def transform(node : RespondsTo)\n      super\n      reset_last_status\n      transform_is_a_or_responds_to node, &.filter_by_responds_to(node.name)\n    end\n\n    def transform_is_a_or_responds_to(node, &)\n      obj = node.obj\n\n      if obj_type = obj.type?\n        filtered_type = yield obj_type\n\n        if obj_type == filtered_type\n          @last_is_truthy = true\n          if var?(obj)\n            return true_literal\n          else\n            exps = Expressions.new([obj, true_literal] of ASTNode)\n            exps.type = @program.bool\n            return exps\n          end\n        end\n\n        unless filtered_type\n          @last_is_falsey = true\n          if var?(obj)\n            return false_literal\n          else\n            exps = Expressions.new([obj, false_literal] of ASTNode)\n            exps.type = @program.bool\n            return exps\n          end\n        end\n      end\n\n      node\n    end\n\n    def var?(node)\n      case node\n      when Var, InstanceVar, ClassVar, Global\n        true\n      else\n        false\n      end\n    end\n\n    def transform(node : Cast)\n      node = super\n\n      obj_type = node.obj.type?\n      return node unless obj_type\n\n      if node.obj.no_returns?\n        return node.obj\n      end\n\n      to_type = node.to.type\n\n      if to_type.pointer?\n        if obj_type.pointer? || obj_type.reference_like?\n          return node\n        else\n          node.raise \"can't cast #{obj_type} to #{to_type}\"\n        end\n      end\n\n      if obj_type.pointer?\n        unless to_type.pointer? || to_type.reference_like?\n          node.raise \"can't cast #{obj_type} to #{to_type}\"\n        end\n      else\n        resulting_type = obj_type.filter_by(to_type)\n        unless resulting_type\n          node.raise \"can't cast #{obj_type} to #{to_type}\"\n        end\n      end\n\n      node\n    end\n\n    def transform(node : NilableCast)\n      node = super\n\n      if node.obj.no_returns?\n        return node.obj\n      end\n\n      node\n    end\n\n    def transform(node : FunDef)\n      node_body = node.body\n      return node unless node_body\n\n      node.body = node_body.transform(self)\n\n      if node_external = node.external\n        node_external.body = node_external.body.transform(self)\n      end\n      node\n    end\n\n    def transform(node : ExceptionHandler)\n      node = super\n\n      if node.body.no_returns?\n        node.else = nil\n      end\n\n      node\n    end\n\n    def transform(node : InstanceSizeOf)\n      exp_type = node.exp.type?\n\n      if exp_type\n        instance_type = exp_type.devirtualize\n        if instance_type.struct? || instance_type.module? || instance_type.metaclass? || instance_type.is_a?(UnionType)\n          node.exp.raise \"instance_sizeof can only be used with a class, but #{instance_type} is a #{instance_type.type_desc}\"\n        end\n      end\n\n      if expanded = node.expanded\n        return expanded.transform self\n      end\n\n      node\n    end\n\n    def transform(node : InstanceAlignOf)\n      exp_type = node.exp.type?\n\n      if exp_type\n        instance_type = exp_type.devirtualize\n        if instance_type.struct? || instance_type.module? || instance_type.metaclass? || instance_type.is_a?(UnionType)\n          node.exp.raise \"instance_alignof can only be used with a class, but #{instance_type} is a #{instance_type.type_desc}\"\n        end\n      end\n\n      if expanded = node.expanded\n        return expanded.transform self\n      end\n\n      node\n    end\n\n    def transform(node : TupleLiteral)\n      super\n\n      unless node.elements.all? &.type?\n        return untyped_expression node\n      end\n\n      no_return_index = node.elements.index &.no_returns?\n      if no_return_index\n        exps = Expressions.new(node.elements[0, no_return_index + 1])\n        exps.bind_to(exps.expressions.last)\n        return exps\n      end\n\n      # `node.program` is assigned by `MainVisitor` usually, however\n      # it may not be assigned in some edge-case (e.g. this `node` is placed\n      # at not invoked block.). This assignment is for it.\n      node.program = @program\n      node.update\n\n      node\n    end\n\n    def transform(node : NamedTupleLiteral)\n      super\n\n      unless node.entries.all? &.value.type?\n        return untyped_expression node\n      end\n\n      no_return_index = node.entries.index &.value.no_returns?\n      if no_return_index\n        exps = Expressions.new(node.entries[0, no_return_index + 1].map &.value)\n        exps.bind_to(exps.expressions.last)\n        return exps\n      end\n\n      node.program = @program\n      node.update\n\n      node\n    end\n\n    def transform(node : CStructOrUnionDef)\n      type = node.resolved_type.as(NonGenericClassType)\n      node.raise \"empty #{type.type_desc}s are disallowed\" if type.instance_vars.empty?\n      node\n    end\n\n    def transform(node : Primitive)\n      extra = node.extra\n      extra = extra.transform(self) if extra\n\n      # For `allocate` on a virtual abstract type we make `extra`\n      # be a call to `raise` at runtime. Here we just replace the\n      # \"allocate\" primitive with that raise call.\n      if node.name.in?(\"allocate\", \"pre_initialize\") && extra\n        return extra\n      end\n\n      node.extra = extra\n      node\n    end\n\n    def transform(node : TypeOf)\n      node = super\n\n      unless node.type?\n        node.unbind_from node.dependencies\n        node.bind_to node.expressions\n      end\n\n      node\n    end\n\n    def transform(node : AssignWithRestriction)\n      transform(node.assign)\n    end\n\n    @false_literal : BoolLiteral?\n\n    def false_literal\n      @false_literal ||= begin\n        false_literal = BoolLiteral.new(false)\n        false_literal.set_type(@program.bool)\n        false_literal\n      end\n    end\n\n    @true_literal : BoolLiteral?\n\n    def true_literal\n      @true_literal ||= begin\n        true_literal = BoolLiteral.new(true)\n        true_literal.set_type(@program.bool)\n        true_literal\n      end\n    end\n\n    def simple_constant?(node)\n      simple_constant?(node, [] of Const)\n    end\n\n    def simple_constant?(node, consts)\n      case node\n      when NilLiteral, BoolLiteral, CharLiteral, NumberLiteral, StringLiteral\n        return true\n      when Call\n        obj = node.obj\n        return false unless obj\n\n        case node.args.size\n        when 0\n          case node.name\n          when \"+\", \"-\", \"~\"\n            return simple_constant?(obj, consts)\n          end\n        when 1\n          case node.name\n          when \"+\", \"-\", \"*\", \"&+\", \"&-\", \"&*\", \"/\", \"//\", \"&\", \"|\"\n            return simple_constant?(obj, consts) && simple_constant?(node.args.first, consts)\n          end\n        end\n      when Path\n        if target_const = node.target_const\n          return false if consts.includes?(target_const)\n\n          consts << target_const\n          return simple_constant?(target_const.value, consts)\n        end\n      end\n\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/conversions.cr",
    "content": "module Crystal::Conversions\n  def self.numeric_argument(node, var, visitor, unaliased_type, expected_type, actual_type)\n    convert_call_name = \"to_#{unaliased_type.kind}!\"\n    convert_call = Call.new(var, convert_call_name).at(node)\n\n    begin\n      convert_call.accept visitor\n    rescue ex : Crystal::CodeError\n      if ex.message.try(&.includes?(\"undefined method '#{convert_call_name}'\"))\n        return nil\n      end\n\n      node.raise \"converting from #{actual_type} to #{expected_type} by invoking '#{convert_call_name}'\", ex\n    end\n\n    if convert_call.type? != unaliased_type\n      node.raise \"invoked '#{convert_call_name}' to convert from #{actual_type} to #{expected_type}, but got #{convert_call.type?}\"\n    end\n\n    convert_call\n  end\n\n  def self.to_unsafe(node, target, visitor, actual_type, expected_type)\n    unsafe_call = try_to_unsafe(target, visitor) do |ex|\n      unless to_unsafe_lookup_failed?(ex)\n        node.raise ex.message, ex\n      end\n      return nil\n    end\n    if unsafe_call.type? != expected_type\n      node.raise \"invoked 'to_unsafe' to convert from #{actual_type} to #{expected_type}, but got #{unsafe_call.type?}\"\n    end\n    unsafe_call\n  end\n\n  def self.try_to_unsafe(target, visitor, &)\n    unsafe_call = Call.new(target, \"to_unsafe\").at(target)\n    begin\n      unsafe_call.accept visitor\n    rescue ex : TypeException\n      yield ex\n    end\n    unsafe_call\n  end\n\n  def self.to_unsafe_lookup_failed?(ex)\n    !!ex.message.try(&.includes?(\"undefined method 'to_unsafe'\"))\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/cover.cr",
    "content": "require \"bit_array\"\nrequire \"../types\"\n\nmodule Crystal\n  struct Cover\n    getter signature : CallSignature\n    getter matches : Array(Match)\n\n    def self.create(signature, matches)\n      if matches\n        matches.empty? ? false : Cover.new(signature, matches)\n      else\n        false\n      end\n    end\n\n    def initialize(@signature, @matches)\n    end\n\n    def all?\n      compute_fast_cover.all?\n    end\n\n    def missing\n      cover, cover_arg_types = compute_cover\n\n      missing = [] of Array(Type)\n      add_missing missing, cover, cover_arg_types\n      missing\n    end\n\n    private def compute_cover\n      args_size = signature.arg_types.size\n      named_args_size = signature.named_args.try(&.size) || 0\n      all_args_size = args_size + named_args_size\n\n      cover = BitArray.new(cover_size)\n      cover_arg_types = Array(Type | Array(Type) | Nil).new(all_args_size)\n\n      signature.arg_types.each do |arg_type|\n        cover_arg_types.push(arg_type.cover)\n      end\n      signature.named_args.try &.each do |named_arg|\n        cover_arg_types.push(named_arg.type.cover)\n      end\n\n      matches.each { |match| mark_cover(match, cover, cover_arg_types) }\n\n      {cover, cover_arg_types}\n    end\n\n    private def compute_fast_cover\n      args_size = signature.arg_types.size\n      named_args_size = signature.named_args.try(&.size) || 0\n      all_args_size = args_size + named_args_size\n\n      # Check which arg indices of the matches have types or type restrictions\n      indices = BitArray.new(all_args_size)\n\n      matches.each do |match|\n        match.def.match(signature.arg_types) do |arg, arg_index, arg_type, arg_type_index|\n          indices[arg_type_index] = true if arg.type? || arg.restriction\n        end\n\n        signature.named_args.try &.each_with_index do |named_arg, i|\n          arg = match.def.args.find(&.external_name.==(named_arg.name))\n          if arg && (arg.type? || arg.restriction)\n            indices[args_size + i] = true\n          end\n        end\n      end\n\n      cover = BitArray.new(cover_size(indices))\n      cover_arg_types = Array(Type | Array(Type) | Nil).new(all_args_size)\n\n      i = 0\n      signature.arg_types.each do |arg_type|\n        cover_arg_types.push(indices[i] ? arg_type.cover : nil)\n        i += 1\n      end\n      signature.named_args.try &.each do |named_arg|\n        cover_arg_types.push(indices[i] ? named_arg.type.cover : nil)\n        i += 1\n      end\n\n      matches.each { |match| mark_cover(match, cover, cover_arg_types, indices) }\n\n      cover\n    end\n\n    private def cover_size\n      size = 1\n      signature.arg_types.each do |type|\n        size *= type.cover_size\n      end\n      signature.named_args.try &.each do |named_arg|\n        size *= named_arg.type.cover_size\n      end\n      size\n    end\n\n    private def cover_size(indices)\n      size = 1\n      i = 0\n      signature.arg_types.each do |type|\n        size *= type.cover_size if indices[i]\n        i += 1\n      end\n      signature.named_args.try &.each do |named_arg|\n        size *= named_arg.type.cover_size if indices[i]\n        i += 1\n      end\n      size\n    end\n\n    private def mark_cover(match, cover, cover_arg_types, indices = nil, index = 0, position = 0, multiplier = 1)\n      if index == cover_arg_types.size\n        cover[position] = true\n        return\n      end\n\n      if indices && !indices[index]\n        mark_cover match, cover, cover_arg_types, indices, index + 1, position, multiplier\n        return\n      end\n\n      arg_type = cover_arg_types[index]\n\n      if index < signature.arg_types.size\n        match_arg_type = match.arg_types[index]\n      else\n        match_arg_type = match.named_arg_types.not_nil![index - signature.arg_types.size].type\n      end\n\n      match_arg_type.each_cover do |match_arg_type2|\n        match_arg_type2_cover = match_arg_type2.cover\n        if match_arg_type2_cover.is_a?(Array)\n          match_arg_type2_cover.each do |sub_match_arg_type|\n            mark_cover_item arg_type, match, cover, cover_arg_types, indices, index, position, multiplier, sub_match_arg_type\n          end\n        else\n          mark_cover_item arg_type, match, cover, cover_arg_types, indices, index, position, multiplier, match_arg_type2_cover\n        end\n      end\n    end\n\n    private def mark_cover_item(arg_type, match, cover, cover_arg_types, indices, index, position, multiplier, sub_match_arg_type)\n      if arg_type.is_a?(Array)\n        offset = arg_type.index(sub_match_arg_type)\n        if offset\n          new_multiplier = multiplier * arg_type.size\n          mark_cover match, cover, cover_arg_types, indices, index + 1, position + offset * multiplier, new_multiplier\n        end\n      elsif arg_type == sub_match_arg_type\n        offset = 0\n        new_multiplier = multiplier\n        mark_cover match, cover, cover_arg_types, indices, index + 1, position + offset * multiplier, new_multiplier\n      end\n    end\n\n    private def add_missing(missing, cover, cover_arg_types, types = [] of Type, index = 0, position = 0, multiplier = 1)\n      if index == cover_arg_types.size\n        unless cover[position]\n          missing.push types.dup\n        end\n        return\n      end\n\n      arg_types = cover_arg_types[index]\n      arg_types = [arg_types] unless arg_types.is_a?(Array)\n      arg_types.each_with_index do |arg_type, offset|\n        types.push arg_type.not_nil!\n        new_multiplier = multiplier * arg_types.size\n        add_missing missing, cover, cover_arg_types, types, index + 1, position + offset * multiplier, new_multiplier\n        types.pop\n      end\n    end\n  end\n\n  class Type\n    def each_cover(&)\n      yield self\n    end\n\n    def cover\n      self\n    end\n\n    def append_cover(array)\n      array << self\n    end\n\n    def cover_size\n      1\n    end\n  end\n\n  class NonGenericModuleType\n    def cover\n      including_types.try(&.cover) || self\n    end\n\n    def append_cover(array)\n      if including_types = including_types()\n        including_types.append_cover(array)\n      else\n        array << self\n      end\n    end\n\n    def cover_size\n      including_types.try(&.cover_size) || 1\n    end\n  end\n\n  class UnionType\n    def each_cover(&)\n      @union_types.each do |union_type|\n        yield union_type\n      end\n    end\n\n    def cover\n      cover = [] of Type\n      append_cover(cover)\n      cover\n    end\n\n    def append_cover(array)\n      union_types.each &.append_cover(array)\n    end\n\n    def cover_size\n      union_types.sum &.cover_size\n    end\n  end\n\n  class VirtualType\n    def each_cover(&)\n      subtypes.each do |subtype|\n        yield subtype\n      end\n    end\n\n    def cover\n      if base_type.abstract?\n        cover = [] of Type\n        append_cover(cover)\n        cover\n      else\n        base_type\n      end\n    end\n\n    def append_cover(array)\n      if base_type.abstract?\n        base_type.subclasses.each &.virtual_type.append_cover(array)\n      else\n        array << base_type\n      end\n    end\n\n    def cover_size\n      if base_type.abstract?\n        base_type.subclasses.sum &.virtual_type.cover_size\n      else\n        1\n      end\n    end\n  end\n\n  class VirtualMetaclassType\n    delegate cover, cover_size, to: instance_type\n  end\n\n  class AliasType\n    delegate cover, cover_size, to: aliased_type\n  end\n\n  class AutocastType\n    delegate cover, cover_size, to: (@match || literal.type)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/default_arguments.cr",
    "content": "require \"../syntax/ast\"\n\nclass Crystal::Def\n  def expand_default_arguments(program, args_size, named_args = nil)\n    # If the named arguments cover all arguments with a default value and\n    # they come in the same order, we can safely return this def without\n    # needing a useless indirection.\n    if !splat_index && !double_splat && named_args && args_size + named_args.size == args.size\n      all_match = true\n      named_args.each_with_index do |named_arg, i|\n        arg = args[args_size + i]\n        unless arg.external_name == named_arg\n          all_match = false\n          break\n        end\n      end\n      if all_match\n        return self\n      end\n    end\n\n    # If there are no named args and all unspecified default arguments are magic\n    # constants we can return ourself (magic constants will be filled later)\n    if !named_args && !splat_index && !double_splat\n      all_magic = true\n      args_size.upto(args.size - 1) do |index|\n        unless args[index].default_value.is_a?(MagicConstant)\n          all_magic = false\n          break\n        end\n      end\n      if all_magic\n        return self\n      end\n    end\n\n    retain_body = block_arity || splat_index || double_splat || assigns_special_var? || macro_def? || args.any? { |arg| arg.default_value && arg.restriction }\n\n    splat_index = self.splat_index\n    double_splat = self.double_splat\n\n    new_args = [] of Arg\n\n    # Args before splat index\n    if splat_index\n      before_splat_size = Math.min(args_size, splat_index)\n    else\n      before_splat_size = args_size\n    end\n\n    before_splat_size.times do |index|\n      new_args << args[index].clone\n    end\n\n    # Splat arg\n    splat_size = 0\n\n    if splat_index\n      arg = args[splat_index]\n\n      unless arg.external_name.empty?\n        splat_names = [] of String\n\n        splat_size = args_size - splat_index\n        splat_size = 0 if splat_size < 0\n\n        splat_size.times do |index|\n          splat_name = program.new_temp_var_name(self)\n          splat_names << splat_name\n\n          splat_arg = Arg.new(splat_name)\n          splat_arg.annotations = arg.annotations.dup\n          splat_arg.original_name = arg.original_name\n\n          new_args << splat_arg\n        end\n      end\n    end\n\n    if named_args\n      # When **opts is expanded for named arguments, we must use internal\n      # names that won't clash with local variables defined in the method.\n      named_args_temp_names = Array(String).new(named_args.size)\n\n      new_name = String.build do |str|\n        str << name\n        named_args.each do |named_arg|\n          str << ':'\n          str << named_arg\n\n          temp_name = program.new_temp_var_name(self)\n          named_args_temp_names << temp_name\n\n          # If a named argument matches an argument's external name, use the internal name\n          matching_arg = args.find { |arg| arg.external_name == named_arg }\n          if matching_arg\n            new_arg = Arg.new(matching_arg.name, external_name: named_arg)\n            new_arg.annotations = matching_arg.annotations.dup\n            new_arg.original_name = matching_arg.original_name\n            new_args << new_arg\n          else\n            new_arg = Arg.new(temp_name, external_name: named_arg)\n            if double_splat = self.double_splat\n              new_arg.annotations = double_splat.annotations.dup\n              new_arg.original_name = double_splat.original_name\n            end\n            new_args << new_arg\n          end\n        end\n      end\n    else\n      new_name = name\n    end\n\n    expansion = Def.new(new_name, new_args, nil, receiver.clone, block_arg.clone, return_type.clone, macro_def?, block_arity).at(self)\n    expansion.args.each { |arg| arg.default_value = nil }\n    expansion.calls_super = calls_super?\n    expansion.calls_initialize = calls_initialize?\n    expansion.calls_previous_def = calls_previous_def?\n    expansion.uses_block_arg = uses_block_arg?\n    expansion.block_arity = block_arity\n    expansion.raises = raises?\n    expansion.free_vars = free_vars\n    expansion.annotations = annotations\n    expansion.special_vars = special_vars\n    if owner = self.owner?\n      expansion.owner = owner\n    end\n    expansion.original_name = original_name\n\n    if retain_body\n      new_body = [] of ASTNode\n      body = self.body.clone\n\n      # Declare variables that are not covered\n      args.each_with_index do |arg, index|\n        # Skip if the argument is covered by a positional argument\n        next if index < args_size && !(splat_index && index > splat_index)\n\n        # Skip if this is the splat index argument\n        next if index == splat_index\n\n        # But first check if we already have it in the named arguments\n        unless named_args.try &.index(arg.external_name)\n          default_value = arg.default_value.not_nil!\n\n          # If the default value is a magic constant we add it to the expanded\n          # def and don't declare it (since it's already an argument)\n          if default_value.is_a?(MagicConstant)\n            expansion.args.push arg.clone\n          else\n            assign = Assign.new(Var.new(arg.name).at(arg), default_value).at(arg)\n\n            if restriction = arg.restriction\n              assign = AssignWithRestriction.new(assign, restriction)\n            end\n\n            new_body << assign\n          end\n        end\n      end\n\n      # Splat argument\n      if splat_names && splat_index\n        tuple_args = [] of ASTNode\n        splat_size.times do |i|\n          tuple_args << Var.new(splat_names[i]).at(self)\n        end\n        splat_arg = args[splat_index]\n        tuple = TupleLiteral.new(tuple_args).at(splat_arg)\n        new_body << Assign.new(Var.new(splat_arg.name).at(splat_arg), tuple).at(splat_arg)\n      end\n\n      # Double splat argument\n      if double_splat\n        named_tuple_entries = [] of NamedTupleLiteral::Entry\n        named_args.try &.each_with_index do |named_arg, i|\n          # Don't put here regular arguments\n          next if args.any? &.external_name.==(named_arg)\n\n          temp_name = named_args_temp_names.not_nil![i]\n          named_tuple_entries << NamedTupleLiteral::Entry.new(named_arg, Var.new(temp_name))\n        end\n        named_tuple = NamedTupleLiteral.new(named_tuple_entries).at(double_splat)\n        new_body << Assign.new(Var.new(double_splat.name).at(double_splat), named_tuple).at(double_splat)\n      end\n\n      new_body.push body\n      expansion.body = Expressions.new(new_body).at(body)\n    else\n      new_args = [] of ASTNode\n      body = [] of ASTNode\n\n      # Append variables that are already covered\n      0.upto(args_size - 1) do |index|\n        arg = args[index]\n        new_args.push Var.new(arg.name)\n      end\n\n      # Append default values for those not covered\n      args_size.upto(args.size - 1) do |index|\n        arg = args[index]\n\n        # But first check if we already have it in the named arguments\n        if named_args.try &.index(arg.external_name)\n          new_args.push Var.new(arg.name)\n        else\n          default_value = arg.default_value.not_nil!\n\n          # If the default value is a magic constant we add it to the expanded\n          # def, and use that on the forwarded call\n          if default_value.is_a?(MagicConstant)\n            new_args.push Var.new(arg.name)\n            expansion.args.push arg.clone\n          else\n            body << Assign.new(Var.new(arg.name).at(arg), default_value.clone).at(arg)\n            new_args.push Var.new(arg.name).at(arg)\n          end\n        end\n      end\n\n      call = Call.new(name, new_args).at(self)\n      call.expansion = true\n      body << call\n\n      expansion.body = Expressions.new(body).at(self.body)\n    end\n\n    expansion\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/exception.cr",
    "content": "require \"../exception\"\nrequire \"../types\"\n\nmodule Crystal\n  class TypeException < CodeError\n    include ErrorFormat\n\n    getter node\n    property inner : CodeError?\n    getter line_number : Int32?\n    getter column_number : Int32\n    getter size : Int32\n\n    def color=(@color : Bool)\n      inner.try &.color=(color)\n      color\n    end\n\n    def error_trace=(@error_trace : Bool)\n      inner.try &.error_trace=(error_trace)\n      error_trace\n    end\n\n    def warning=(@warning : Bool)\n      inner.try &.warning=(warning)\n      warning\n    end\n\n    def self.for_node(node, message, inner = nil)\n      location = node.name_location || node.location\n      if location\n        ex = new message, location.line_number, location.column_number, location.filename, node.name_size, inner\n        wrap_macro_expression(ex, location)\n      else\n        new message, nil, 0, nil, 0, inner\n      end\n    end\n\n    def initialize(message, @line_number, @column_number : Int32, @filename, @size, @inner = nil)\n      @error_trace = true\n\n      super(message)\n    end\n\n    def self.new(message : String)\n      new message, nil, 0, nil, 0\n    end\n\n    def self.new(message : String, location : Location)\n      ex = new message, location.line_number, location.column_number, location.filename, 0\n      wrap_macro_expression(ex, location)\n    end\n\n    protected def self.wrap_macro_expression(ex, location)\n      filename = location.filename\n      if filename.is_a?(VirtualFile) && (expanded_location = filename.expanded_location)\n        ex = TypeException.new \"expanding macro\", expanded_location.line_number, expanded_location.column_number, expanded_location.filename, 0, ex\n      end\n      ex\n    end\n\n    def to_json_single(json)\n      json.object do\n        json.field \"file\", true_filename\n        json.field \"line\", @line_number\n        json.field \"column\", @column_number\n        json.field \"size\", @size\n        json.field \"message\", @message\n      end\n      if inner = @inner\n        inner.to_json_single(json)\n      end\n    end\n\n    def inspect_with_backtrace(io : IO) : Nil\n      to_s(io)\n\n      backtrace?.try &.each do |frame|\n        io.print \"  from \"\n        io.puts frame\n      end\n\n      io.flush\n    end\n\n    def to_s_with_source(io : IO, source)\n      append_to_s io, source\n    end\n\n    def append_to_s(io : IO, source)\n      inner = @inner\n\n      unless @error_trace || inner.is_a? MethodTraceException\n        if inner && inner.has_location?\n          return inner.append_to_s(io, source)\n        end\n      end\n\n      # If the inner exception has no location it means that they came from virtual nodes.\n      # In that case, get the deepest error message and only show that.\n      if inner && !inner.has_location?\n        msg = deepest_error_message.to_s\n      else\n        msg = @message.to_s\n      end\n\n      error_message_lines = msg.lines\n\n      unless @error_trace || @warning\n        io << colorize(\"Showing last frame. Use --error-trace for full trace.\").dim\n        io << \"\\n\\n\"\n      end\n\n      if body = error_body(source, default_message)\n        io << body\n        io << '\\n'\n      end\n\n      unless error_message_lines.empty?\n        io << error_headline(error_message_lines.shift)\n        io << remaining error_message_lines\n      end\n\n      if inner\n        return if inner.is_a? MethodTraceException && !inner.has_message?\n        return unless inner.has_location?\n        io << \"\\n\\n\"\n        io << '\\n' unless inner.is_a? MethodTraceException\n        inner.append_to_s io, source\n      end\n    end\n\n    def default_message\n      if line_number = @line_number\n        \"#{@warning ? \"warning\" : \"error\"} in line #{line_number}\"\n      end\n    end\n\n    def error_headline(msg)\n      return \"Warning: #{msg}\" if @warning\n\n      if (inner = @inner) && !inner.is_a? MethodTraceException? && inner.has_location?\n        colorize(\"Error: #{msg}\").yellow\n      else\n        colorize(\"Error: #{msg}\").yellow.bold\n      end\n    end\n\n    def has_location?\n      if @inner.try &.has_location?\n        true\n      else\n        @filename || @line_number\n      end\n    end\n\n    def deepest_error_message\n      if inner = @inner\n        inner.deepest_error_message\n      else\n        @message\n      end\n    end\n  end\n\n  class MethodTraceException < CodeError\n    def initialize(@owner : Type?, @trace : Array(ASTNode), @nil_reason : NilReason?, @show : Bool)\n      super(nil)\n    end\n\n    def has_location?\n      true\n    end\n\n    def to_json_single(json)\n    end\n\n    def to_s_with_source(io : IO, source)\n      append_to_s(io, source)\n    end\n\n    def has_trace?\n      @trace.any?(&.location)\n    end\n\n    def has_message?\n      @nil_reason || has_trace? && @show\n    end\n\n    def append_to_s(io : IO, source)\n      nil_reason = @nil_reason\n\n      if !@show\n        if nil_reason\n          print_nil_reason(nil_reason, io)\n          if has_trace? || nil_reason.try(&.nodes)\n            io.puts\n            io.puts\n          end\n        end\n        return\n      end\n\n      if has_trace?\n        io << \"#{@owner} trace:\"\n        @trace.each do |node|\n          print_with_location node, io\n        end\n      end\n\n      return unless nil_reason\n\n      if has_trace?\n        io.puts\n        io.puts\n      end\n\n      print_nil_reason(nil_reason, io)\n\n      if nil_reason_nodes = nil_reason.nodes\n        nil_reason_nodes.each do |node|\n          print_with_location node, io\n        end\n      end\n    end\n\n    def print_nil_reason(nil_reason, io)\n      case nil_reason.reason\n      in .used_before_initialized?\n        io << \"Instance variable '#{nil_reason.name}' was used before it was initialized in one of the 'initialize' methods, rendering it nilable\"\n      in .used_self_before_initialized?\n        io << \"'self' was used before initializing instance variable '#{nil_reason.name}', rendering it nilable\"\n      in .initialized_in_rescue?\n        io << \"Instance variable '#{nil_reason.name}' is initialized inside a begin-rescue, so it can potentially be left uninitialized if an exception is raised and rescued\"\n      end\n    end\n\n    def print_with_location(node, io)\n      location = node.location\n      return unless location\n\n      filename = location.filename\n      line_number = location.line_number\n\n      case filename\n      when VirtualFile\n        lines = filename.source.lines.to_a\n        filename = \"macro #{filename.macro.name} (in #{filename.macro.location.try &.filename}:#{filename.macro.location.try &.line_number})\"\n      when String\n        lines = File.read_lines(filename) if File.file?(filename)\n      else\n        return\n      end\n\n      io << \"\\n\\n\"\n      io << \"  \"\n      io << relative_filename(filename) << ':' << line_number\n      io << \"\\n\\n\"\n\n      return unless lines\n\n      line = lines[line_number - 1]\n\n      name_location = node.name_location\n      name_size = node.name_size\n\n      io << \"    \"\n      io << replace_leading_tabs_with_spaces(line.chomp)\n      io.puts\n\n      return unless name_location\n\n      io << \"    \"\n      io << (\" \" * (name_location.column_number - 1))\n      with_color.green.bold.surround(io) do\n        io << '^'\n        if name_size > 0\n          io << (\"~\" * (name_size - 1)) if name_size\n        end\n      end\n    end\n\n    def deepest_error_message\n      nil\n    end\n  end\n\n  class FrozenTypeException < TypeException\n  end\n\n  class UndefinedMacroMethodError < TypeException\n  end\n\n  class MacroRaiseException < TypeException\n  end\n\n  class TopLevelMacroRaiseException < MacroRaiseException\n  end\n\n  class SkipMacroException < ::Exception\n    getter expanded_before_skip : String\n    getter macro_expansion_pragmas : Hash(Int32, Array(Lexer::LocPragma))?\n\n    def initialize(@expanded_before_skip, @macro_expansion_pragmas)\n      super()\n    end\n  end\n\n  # Raised when another exception is raised when calculating macro code coverage.\n  # Extends `SkipMacroException` to ensure it's also treated as a somewhat non-failure exception.\n  #\n  # Rescuing this exception allows the macro code coverage tool to run on the code that was collected up to before the exception was raised, while not resulting in an error/running all the other code after it.\n  # The exception's message/trace is printed to STDOUT while the report is written to STDERR.\n  # This is especially useful for testing error flows of custom macro logic as it allows the code to assert the proper error was raised, while still allowing to save the coverage report.\n  # I.e. the report JSON/exception outputs are not co-mingled.\n  # The main benefit of this is preventing the need to run these kind of tests twice, once for the coverage report and once for the actual test assertions.\n  class SkipMacroCodeCoverageException < SkipMacroException\n    def initialize(exception : ::Exception)\n      super \"\", nil\n      @message = exception.message\n      @cause = exception\n    end\n  end\n\n  class Program\n    def undefined_class_variable(node, owner, similar_name)\n      common = String.build do |str|\n        str << \"can't infer the type of class variable '#{node.name}' of #{owner.devirtualize}\"\n        if similar_name\n          str << '\\n'\n          str << \"Did you mean '#{similar_name}'?\"\n        end\n      end\n\n      msg = String.build do |str|\n        str << common\n        str << \"\\n\\n\"\n        str << undefined_variable_message(\"class variable\", node.name, owner.devirtualize)\n      end\n      node.raise msg\n    end\n\n    def undefined_instance_variable(node, owner, similar_name)\n      common = String.build do |str|\n        str << \"can't infer the type of instance variable '#{node.name}' of #{owner.devirtualize}\"\n        if similar_name\n          str << '\\n'\n          str << \"Did you mean '#{similar_name}'?\"\n        end\n      end\n\n      msg = String.build do |str|\n        str << common\n        str << \"\\n\\n\"\n        str << undefined_variable_message(\"instance variable\", node.name, owner.devirtualize)\n      end\n      node.raise msg\n    end\n\n    def undefined_variable_message(kind, example_name, owner)\n      owner_keyword =\n        if owner.module?\n          \"module\"\n        elsif owner.struct?\n          \"struct\"\n        else\n          \"class\"\n        end\n\n      <<-MSG\n      Could you add a type annotation like this\n\n          #{owner_keyword} #{owner}\n            #{example_name} : Type\n          end\n\n      replacing `Type` with the expected type of `#{example_name}`?\n      MSG\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/exhaustiveness_checker.cr",
    "content": "struct Crystal::ExhaustivenessChecker\n  def initialize(@program : Program)\n  end\n\n  def check(node : Case)\n    cond = node.cond.not_nil!\n\n    if cond.is_a?(TupleLiteral)\n      check_tuple_exp(node, cond)\n    else\n      check_single_exp(node, cond)\n    end\n  end\n\n  private def check_single_exp(node, cond)\n    cond_type = cond.type?\n\n    # No type on condition means we couldn't type it so we can't\n    # check exhaustiveness.\n    return unless cond_type\n\n    # Compute all types that we must cover.\n    # We only take into account union types and single types,\n    # never virtual types because these can be extended.\n    cond_types = expand_types(cond_type)\n\n    # Compute all the targets that we must cover\n    targets = cond_types.map { |cond_type| compute_target(cond_type) }\n\n    # Is any type a @[Flags] enum?\n    flags_enum = cond_types.find { |type| type.is_a?(EnumType) && type.flags? }\n\n    # Are all patterns Path types?\n    all_patterns_are_types = true\n\n    # Start checking each `when`...\n    node.whens.each do |a_when|\n      a_when.conds.each do |when_cond|\n        pattern = when_pattern(when_cond)\n\n        unless pattern.is_a?(TypePattern)\n          all_patterns_are_types = false\n        end\n\n        targets.each &.cover(pattern)\n      end\n    end\n\n    targets.reject! &.covered?\n\n    # If we covered all types, we are done.\n    return if targets.empty?\n\n    if targets.all?(TypeTarget) && all_patterns_are_types\n      node.raise <<-MSG\n        case is not exhaustive.\n\n        Missing types:\n         - #{targets.map(&.type).join(\"\\n - \")}\n        MSG\n    end\n\n    single_target = targets.size == 1 ? targets.first : nil\n\n    case single_target\n    when BoolTarget\n      node.raise <<-MSG\n        case is not exhaustive.\n\n        Missing cases:\n         - #{single_target.missing_cases.join(\"\\n - \")}\n        MSG\n    when EnumTarget\n      node.raise <<-MSG\n      case is not exhaustive for enum #{single_target.type}.\n\n      Missing members:\n       - #{single_target.members.map(&.name).join(\"\\n - \")}\n      MSG\n    else\n      # No specific error messages for non-single types\n    end\n\n    msg = <<-MSG\n      case is not exhaustive.\n\n      Missing cases:\n       - #{targets.flat_map(&.missing_cases).join(\"\\n - \")}\n      MSG\n\n    if flags_enum\n      msg += \"\\n\\n\" + flags_enum_message(flags_enum)\n    end\n\n    node.raise msg\n  end\n\n  private def check_tuple_exp(node, cond)\n    elements = cond.elements\n\n    # No type on condition means we couldn't type it so we can't\n    # check exhaustiveness.\n    return unless elements.all? &.type?\n\n    element_types = elements.map &.type\n\n    all_expanded_types = element_types.map do |element_type|\n      expand_types(element_type)\n    end\n\n    # Is any type a @[Flags] enum?\n    flags_enum = nil\n    all_expanded_types.each do |types|\n      types.each do |type|\n        if type.is_a?(EnumType) && type.flags?\n          flags_enum = type\n          break\n        end\n      end\n      break if flags_enum\n    end\n\n    targets = compute_targets(all_expanded_types)\n\n    # Start checking each `when`...\n    node.whens.each do |a_when|\n      a_when.conds.each do |when_cond|\n        if when_cond.is_a?(TupleLiteral)\n          patterns = when_cond.elements.map do |when_cond_exp|\n            when_pattern(when_cond_exp)\n          end\n\n          targets.each &.cover(patterns, 0)\n        else\n          # Not a tuple literal so we don't care\n          # TODO: one could put `Tuple` or `Object` here and that would make\n          # the entire tuple match, but who would do that?\n          # We can do it, but it has very low priority.\n        end\n      end\n    end\n\n    targets.reject! &.reject_covered!\n\n    # If we covered all types, we are done.\n    return if targets.empty?\n\n    missing_cases = targets\n      .flat_map(&.missing_cases)\n      .map { |cases| \"{#{cases}}\" }\n      .join(\"\\n - \")\n\n    msg = <<-MSG\n      case is not exhaustive.\n\n      Missing cases:\n       - #{missing_cases}\n      MSG\n\n    if flags_enum\n      msg += \"\\n\\n\" + flags_enum_message(flags_enum)\n    end\n\n    node.raise msg\n  end\n\n  private def flags_enum_message(flags_enum)\n    <<-MSG\n      Note that @[Flags] enum can't be proved to be exhaustive by matching against enum members.\n      In particular, the enum #{flags_enum} can't be proved to be exhaustive like that.\n      MSG\n  end\n\n  private def compute_targets(type_groups)\n    type_groups.first.map do |type|\n      compute_target(type).tap do |target|\n        target.add_subtargets(type_groups, 1)\n      end\n    end\n  end\n\n  # Returns an array of all the types inside `type`:\n  # for unions it's all the union types, otherwise it's just that type.\n  private def expand_types(type)\n    if type.is_a?(UnionType)\n      type.union_types.map(&.devirtualize.as(Type))\n    else\n      [type.devirtualize]\n    end\n  end\n\n  private def compute_target(type : Type)\n    ExhaustivenessChecker.compute_target(type)\n  end\n\n  def self.compute_target(type : Type)\n    case type\n    when BoolType\n      BoolTarget.new(type)\n    when EnumType\n      if type.flags?\n        TypeTarget.new(type)\n      else\n        EnumTarget.new(type)\n      end\n    else\n      TypeTarget.new(type)\n    end\n  end\n\n  private def when_pattern(when_cond) : Pattern?\n    case when_cond\n    when Path\n      if !when_cond.syntax_replacement && !when_cond.target_const &&\n         when_cond.type?\n        # In case of a Path that points to a type,\n        # remove that type from the types we must cover\n        TypePattern.new(when_cond.type.devirtualize)\n      elsif (target_const = when_cond.target_const) &&\n            target_const.namespace.is_a?(EnumType)\n        # If we find a constant that doesn't point to a type (so a value),\n        # if it's an enum member, try to remove it from the targets.\n        EnumMemberPattern.new(target_const)\n      else\n        when_cond.raise \"can't use constant values in exhaustive case, only constant types\"\n      end\n    when Generic\n      TypePattern.new(when_cond.type.devirtualize)\n    when Call\n      obj = when_cond.obj\n\n      # Check if it's something like `.foo?` to remove that member from the ones\n      # we must cover.\n      # Note: a user could override the meaning of such methods.\n      # In the future it would be wise to mark these as non-redefinable\n      # so this checks are sounds.\n      if obj.is_a?(ImplicitObj) && when_cond.name.ends_with?('?')\n        EnumMemberNamePattern.new(when_cond.name.rchop)\n      elsif obj.is_a?(Path) && when_cond.name == \"class\"\n        TypePattern.new(obj.type.metaclass.devirtualize)\n      elsif obj.is_a?(Generic) && when_cond.name == \"class\"\n        TypePattern.new(obj.type.metaclass.devirtualize)\n      else\n        raise \"Bug: unknown pattern in exhaustive case\"\n      end\n    when BoolLiteral\n      BoolPattern.new(when_cond.value)\n    when NilLiteral\n      TypePattern.new(@program.nil_type)\n    when Underscore\n      UnderscorePattern.new\n    else\n      raise \"Bug: unknown pattern in exhaustive case\"\n    end\n  end\n\n  # A type pattern is when you do `when Type`\n  record TypePattern, type : Type\n\n  # An enum member pattern is when you do `when Foo::Bar`\n  # and `Bar` is an enum member of `Foo`\n  record EnumMemberPattern, member : Const\n\n  # An enum member pattern is when you do `when .foo?`\n  record EnumMemberNamePattern, name : String\n\n  # A bool pattern is when you do `when true` or `when false`\n  record BoolPattern, value : Bool\n\n  # An underscore pattern is when you do `when {.., _}` (only in tuple literals)\n  record UnderscorePattern\n\n  alias Pattern = TypePattern | EnumMemberPattern | EnumMemberNamePattern | BoolPattern | UnderscorePattern\n\n  # A target to check for exhaustiveness\n  #\n  # Every target can also have subtargets, used when matching against a tuple literal.\n  # For example, the BoolTarget will have one subtarget for the value `false` and\n  # one for the value `true`. When a pattern like `{false, ...}` is passed to it,\n  # only patterns for the `false` subtarget will be covered.\n  abstract class Target\n    # The type this target is based on.\n    getter type\n\n    def initialize(@type : Type)\n      @type_covered = false\n    end\n\n    # Tries to cover this target with the given pattern.\n    # By default, a TypePattern will cover a target.\n    # Other, more specific, patterns will partially cover a target.\n    def cover(pattern : Pattern) : Nil\n      case pattern\n      when TypePattern\n        if @type.implements?(pattern.type)\n          @type_covered = true\n        end\n      when UnderscorePattern\n        @type_covered = true\n      when EnumMemberPattern, EnumMemberNamePattern, BoolPattern\n        # No cover\n      end\n    end\n\n    # Covers this target and subsequent subtargets with the patterns starting\n    # at index.\n    abstract def cover(patterns : Array(Pattern), index : Int32) : Nil\n\n    # Removes covered subtargets from this target, and returns whether\n    # this target ended up being entirely covered.\n    abstract def reject_covered! : Bool\n\n    # Was this target covered?\n    abstract def covered? : Bool\n\n    # What are the cases that we didn't cover?\n    abstract def missing_cases : Array(String)\n\n    # Add subtargets for the given type groups, starting at index.\n    abstract def add_subtargets(type_groups : Array(Array(Type)), index : Int32) : Nil\n  end\n\n  # A bool target. Subtargets are the `false` and `true` literals.\n  class BoolTarget < Target\n    property? found_true = false\n    property? found_false = false\n    property! subtargets : Hash(Bool, Array(Target))\n\n    def cover(pattern : Pattern) : Nil\n      super\n\n      case pattern\n      when BoolPattern\n        if pattern.value\n          @found_true = true\n        else\n          @found_false = true\n        end\n      when UnderscorePattern\n        @found_true = true\n        @found_false = true\n      when TypePattern, EnumMemberPattern, EnumMemberNamePattern\n        # No cover\n      end\n    end\n\n    def cover(patterns : Array(Pattern), index : Int32) : Nil\n      if index == patterns.size - 1\n        cover(patterns.last)\n        return\n      end\n\n      pattern = patterns[index]\n      case pattern\n      when TypePattern\n        if @type.implements?(pattern.type)\n          subtargets.each_value &.each &.cover(patterns, index + 1)\n        end\n      when BoolPattern\n        subtargets[pattern.value].each &.cover(patterns, index + 1)\n      when UnderscorePattern\n        subtargets.each_value &.each &.cover(patterns, index + 1)\n      when EnumMemberPattern, EnumMemberNamePattern\n        # No cover\n      end\n    end\n\n    def reject_covered! : Bool\n      if subtargets = @subtargets\n        subtargets.reject! do |b, targets|\n          targets.reject! &.reject_covered!\n          targets.all? &.covered?\n        end\n        subtargets.all? { |b, targets| targets.all? &.covered? }\n      else\n        covered?\n      end\n    end\n\n    def covered? : Bool\n      if subtargets = @subtargets\n        subtargets.all? { |b, targets| targets.all? &.covered? }\n      else\n        @type_covered || (found_true? && found_false?)\n      end\n    end\n\n    def missing_cases : Array(String)\n      if subtargets = @subtargets\n        # First get all missing cases for each bool value\n        missing_cases_per_bool = subtargets.to_h do |bool, targets|\n          {bool, targets.flat_map &.missing_cases}\n        end\n\n        gathered_missing_cases = [] of String\n\n        # See if a case is missing for both false and true: show it as Bool in that case\n        missing_cases_per_bool.values.flatten.uniq!.each do |missing_case|\n          if {true, false}.all? { |bool| missing_cases_per_bool[bool]?.try &.includes?(missing_case) }\n            gathered_missing_cases << \"Bool, #{missing_case}\"\n            {true, false}.each { |bool| missing_cases_per_bool[bool]?.try &.delete(missing_case) }\n          end\n        end\n\n        missing_cases_per_bool.each do |bool, missing_cases|\n          missing_cases.each do |missing_case|\n            gathered_missing_cases << \"#{bool}, #{missing_case}\"\n          end\n        end\n\n        gathered_missing_cases\n      else\n        missing_cases = [] of String\n        missing_cases << \"false\" unless found_false?\n        missing_cases << \"true\" unless found_true?\n        if missing_cases.size == 2\n          missing_cases.clear\n          missing_cases << \"Bool\"\n        end\n        missing_cases\n      end\n    end\n\n    def add_subtargets(type_groups : Array(Array(Type)), index : Int32) : Nil\n      return if index >= type_groups.size\n\n      subtargets = @subtargets = {} of Bool => Array(Target)\n\n      {true, false}.each do |bool_value|\n        subtargets[bool_value] = type_groups[index].map do |expanded_type|\n          target = ExhaustivenessChecker.compute_target(expanded_type)\n          target.add_subtargets(type_groups, index + 1)\n          target\n        end\n      end\n    end\n  end\n\n  # An enum target. Subtargets are the enum members.\n  class EnumTarget < Target\n    getter members : Array(Const)\n\n    @original_members_size : Int32\n\n    property! subtargets : Hash(Const, Array(Target))\n\n    def initialize(type)\n      super\n      @members = type.types.values.select(Const)\n      @original_members_size = @members.size\n    end\n\n    def cover(pattern : Pattern) : Nil\n      super\n\n      case pattern\n      when EnumMemberPattern\n        @members.delete(pattern.member)\n      when EnumMemberNamePattern\n        @members.reject! { |member| member.name.underscore == pattern.name }\n      when UnderscorePattern\n        @members.clear\n      when TypePattern, BoolPattern\n        # No cover\n      end\n    end\n\n    def cover(patterns : Array(Pattern), index : Int32) : Nil\n      if index == patterns.size - 1\n        cover(patterns.last)\n        return\n      end\n\n      pattern = patterns[index]\n      case pattern\n      when TypePattern\n        if @type.implements?(pattern.type)\n          subtargets.each_value &.each &.cover(patterns, index + 1)\n        end\n      when EnumMemberPattern\n        subtargets.each do |member, targets|\n          if member == pattern.member\n            targets.each &.cover(patterns, index + 1)\n          end\n        end\n      when EnumMemberNamePattern\n        subtargets.each do |member, targets|\n          if member.name.underscore == pattern.name\n            targets.each &.cover(patterns, index + 1)\n          end\n        end\n      when UnderscorePattern\n        subtargets.each_value &.each &.cover(patterns, index + 1)\n      when BoolPattern\n        # No cover\n      end\n    end\n\n    def reject_covered! : Bool\n      if subtargets = @subtargets\n        subtargets.reject! do |c, targets|\n          targets.reject! &.reject_covered!\n          targets.all? &.covered?\n        end\n        subtargets.all? { |c, targets| targets.all? &.covered? }\n      else\n        covered?\n      end\n    end\n\n    def covered? : Bool\n      if subtargets = @subtargets\n        subtargets.all? { |c, targets| targets.all? &.covered? }\n      else\n        @type_covered || @members.empty?\n      end\n    end\n\n    def missing_cases : Array(String)\n      if subtargets = @subtargets\n        # First get all missing cases for each member\n        missing_cases_per_member = subtargets.to_h do |member, targets|\n          {member, targets.flat_map &.missing_cases}\n        end\n\n        gathered_missing_cases = [] of String\n\n        # See if a case is missing for all members: show it as the enum name in that case\n        missing_cases_per_member.values.flatten.uniq!.each do |missing_case|\n          if @members.all? { |member| missing_cases_per_member[member]?.try &.includes?(missing_case) }\n            gathered_missing_cases << \"#{@type}, #{missing_case}\"\n            @members.each { |member| missing_cases_per_member[member]?.try &.delete(missing_case) }\n          end\n        end\n\n        missing_cases_per_member.each do |member, missing_cases|\n          missing_cases.each do |missing_case|\n            gathered_missing_cases << \"#{member}, #{missing_case}\"\n          end\n        end\n\n        gathered_missing_cases\n      else\n        if @original_members_size == @members.size\n          [type.to_s]\n        else\n          @members.map(&.to_s)\n        end\n      end\n    end\n\n    def add_subtargets(type_groups : Array(Array(Type)), index : Int32) : Nil\n      return if index >= type_groups.size\n\n      subtargets = @subtargets = {} of Const => Array(Target)\n\n      @members.each do |member|\n        subtargets[member] = type_groups[index].map do |expanded_type|\n          target = ExhaustivenessChecker.compute_target(expanded_type)\n          target.add_subtargets(type_groups, index + 1)\n          target\n        end\n      end\n    end\n  end\n\n  # Any other target is a type target. The target to cover is the type itself.\n  class TypeTarget < Target\n    property! subtargets : Array(Target)\n\n    def cover(patterns : Array(Pattern), index : Int32) : Nil\n      if index == patterns.size - 1\n        cover(patterns.last)\n        return\n      end\n\n      pattern = patterns[index]\n      case pattern\n      when TypePattern\n        if @type.implements?(pattern.type)\n          subtargets.each &.cover(patterns, index + 1)\n        end\n      when UnderscorePattern\n        subtargets.each &.cover(patterns, index + 1)\n      when BoolPattern, EnumMemberPattern, EnumMemberNamePattern\n        # No cover\n      end\n    end\n\n    def reject_covered! : Bool\n      if subtargets = @subtargets\n        subtargets.reject! &.reject_covered!\n        subtargets.all? &.covered?\n      else\n        covered?\n      end\n    end\n\n    def covered? : Bool\n      if subtargets = @subtargets\n        subtargets.all? &.covered?\n      else\n        @type_covered\n      end\n    end\n\n    def missing_cases : Array(String)\n      if subtargets = @subtargets\n        subtargets.flat_map do |target|\n          target.missing_cases.map do |missing_case|\n            \"#{type}, #{missing_case}\"\n          end\n        end\n      else\n        [type.to_s]\n      end\n    end\n\n    def add_subtargets(type_groups : Array(Array(Type)), index : Int32) : Nil\n      return if index >= type_groups.size\n\n      subtargets = @subtargets = [] of Target\n\n      type_groups[index].each do |expanded_type|\n        target = ExhaustivenessChecker.compute_target(expanded_type)\n        target.add_subtargets(type_groups, index + 1)\n        subtargets << target\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/filters.cr",
    "content": "module Crystal\n  class TypeFilteredNode < ASTNode\n    def initialize(@filter : TypeFilter, @node : ASTNode)\n      @dependencies.push @node\n      node.add_observer self\n      update(@node)\n    end\n\n    def update(from = nil)\n      from_type = from.try &.type?\n\n      if from_type\n        self.type = @filter.apply(from_type)\n      end\n    end\n\n    def clone_without_location\n      TypeFilteredNode.new(@filter, @node)\n    end\n\n    def to_s(io : IO) : Nil\n      @filter.to_s(io)\n    end\n  end\n\n  class ASTNode\n    def filtered_by(filter)\n      TypeFilteredNode.new(filter, self)\n    end\n  end\n\n  abstract class TypeFilter\n    def self.and(type_filter1, type_filter2)\n      if type_filter1 == type_filter2\n        type_filter1\n      elsif type_filter1 && type_filter2\n        AndTypeFilter.new(type_filter1, type_filter2)\n      elsif type_filter1\n        type_filter1\n      elsif type_filter2\n        type_filter2\n      end\n    end\n\n    def self.or(type_filter1, type_filter2)\n      if type_filter1 == type_filter2\n        type_filter1\n      elsif type_filter1 && type_filter2\n        OrTypeFilter.new(type_filter1, type_filter2)\n      end\n    end\n\n    def not\n      NotFilter.new(self)\n    end\n  end\n\n  class SimpleTypeFilter < TypeFilter\n    getter type : Type\n\n    def initialize(@type)\n    end\n\n    def apply(other)\n      other.try &.filter_by(@type)\n    end\n\n    def ==(other : self)\n      @type == other.type\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"F(\"\n      @type.to_s(io)\n      io << ')'\n    end\n  end\n\n  class AndTypeFilter < TypeFilter\n    def initialize(@filter1 : TypeFilter, @filter2 : TypeFilter)\n    end\n\n    def apply(other)\n      type = other\n      type = @filter1.apply(type)\n      type = @filter2.apply(type)\n      type\n    end\n\n    def not\n      # !(a && b) -> !a || !b\n      TypeFilter.or(@filter1.not, @filter2.not)\n    end\n\n    def ==(other : self)\n      @filter1 == other.@filter1 && @filter2 == other.@filter2\n    end\n\n    def to_s(io : IO) : Nil\n      io << '(' << @filter1 << \" && \" << @filter2 << ')'\n    end\n  end\n\n  class OrTypeFilter < TypeFilter\n    def initialize(@filter1 : TypeFilter, @filter2 : TypeFilter)\n    end\n\n    def apply(other)\n      type1 = @filter1.apply(other)\n      type2 = @filter2.apply(other)\n      res = if type1 && type2\n              type1.program.type_merge_union_of([type1, type2])\n            else\n              type1 || type2\n            end\n      res\n    end\n\n    def not\n      # !(a || b) -> !a && !b\n      TypeFilter.and(@filter1.not, @filter2.not)\n    end\n\n    def ==(other : self)\n      @filter1 == other.@filter1 && @filter2 == other.@filter2\n    end\n\n    def to_s(io : IO) : Nil\n      io << '(' << @filter1 << \" || \" << @filter2 << ')'\n    end\n  end\n\n  class TruthyFilter < TypeFilter\n    INSTANCE = TruthyFilter.new\n\n    def self.instance\n      INSTANCE\n    end\n\n    def apply(other)\n      return nil unless other\n\n      case other\n      when NilType\n        nil\n      when UnionType\n        other.program.union_of(other.union_types.reject &.nil_type?)\n      else\n        other\n      end\n    end\n\n    def ==(other : self)\n      true\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"truthy\"\n    end\n  end\n\n  class NotFilter < TypeFilter\n    getter filter : TypeFilter\n\n    def initialize(@filter : TypeFilter)\n    end\n\n    def apply(other)\n      types = @filter.apply(other)\n\n      if types\n        case types\n        when UnionType\n          types = types.union_types\n        when Array\n          types\n        else\n          types = [types] of Type\n        end\n      else\n        types = [] of Type\n      end\n\n      if other.is_a?(UnionType)\n        other_types = other.union_types\n      else\n        other_types = other ? [other] of Type : [] of Type\n      end\n\n      resulting_types = other_types - types\n\n      # Special case: not truthy (falsey) can also be bool or pointer\n      if @filter.is_a?(TruthyFilter)\n        types.each do |type|\n          if type.bool_type? || type.pointer?\n            resulting_types << type\n          end\n        end\n      end\n\n      case resulting_types.size\n      when 0\n        if @filter.is_a?(TruthyFilter)\n          other\n        else\n          nil\n        end\n      when 1\n        resulting_types.first\n      else\n        Type.merge(resulting_types)\n      end\n    end\n\n    def not\n      @filter\n    end\n\n    def ==(other : self)\n      @filter == other.filter\n    end\n\n    def to_s(io : IO) : Nil\n      io << '!'\n      @filter.to_s(io)\n    end\n  end\n\n  class RespondsToTypeFilter < TypeFilter\n    def initialize(@name : String)\n    end\n\n    def apply(other)\n      other.try &.filter_by_responds_to(@name)\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"responds_to?(\" << @name << ')'\n    end\n  end\n\n  # In code like:\n  #\n  #   x = 1\n  #   if ...\n  #     x = \"hi\"\n  #     unreachable!\n  #   end\n  #\n  # `x` type must be Int32 after the `if`, because the String type\n  # doesn't matter afterwards as unreachable code comes. However, if\n  # `unreachable!` is only temporarily unreachable (because of type propagation\n  # that condition can change) we also want to bind `x` to String. But we\n  # want to do that conditionally on the `if`'s `then` block being NoReturn or\n  # not.\n  #\n  # This filter does that. It receives the var's type (String in this case)\n  # and applies a filter to the `then` (or `else`) part of the `if`: if it's\n  # no return, we return just that. If not, we return the var's type.\n  class NoReturnFilter < TypeFilter\n    def initialize(@var : ASTNode)\n    end\n\n    def apply(other)\n      other.try(&.no_return?) ? other : @var.type?\n    end\n  end\n\n  struct TypeFilters\n    protected getter pos = {} of String => TypeFilter\n    protected getter neg = {} of String => TypeFilter\n\n    protected def initialize\n    end\n\n    protected def initialize(*, @pos, @neg)\n    end\n\n    def self.new(node, filter)\n      new_filters = new\n      new_filters.pos[node.name] = filter\n      new_filters.neg[node.name] = filter.not\n      new_filters\n    end\n\n    def self.truthy(node)\n      new node, TruthyFilter.instance\n    end\n\n    def self.truthy_var(name : String)\n      new_filters = new\n      filter = TruthyFilter.instance\n      new_filters.pos[name] = filter\n      new_filters.neg[name] = filter.not\n      new_filters\n    end\n\n    def self.and(filters1, filters2)\n      return nil if filters1.nil? && filters2.nil?\n\n      new_filters = new\n      common_keys(filters1, filters2).each do |name|\n        if filter = TypeFilter.and(filters1.try(&.pos[name]?), filters2.try(&.pos[name]?))\n          new_filters.pos[name] = filter\n        end\n        if filter = TypeFilter.or(filters1.try(&.neg[name]?), filters2.try(&.neg[name]?))\n          new_filters.neg[name] = filter\n        end\n      end\n      new_filters\n    end\n\n    def self.or(filters1, filters2)\n      return nil if filters1.nil? && filters2.nil?\n\n      new_filters = new\n      common_keys(filters1, filters2).each do |name|\n        if filter = TypeFilter.or(filters1.try(&.pos[name]?), filters2.try(&.pos[name]?))\n          new_filters.pos[name] = filter\n        end\n        if filter = TypeFilter.and(filters1.try(&.neg[name]?), filters2.try(&.neg[name]?))\n          new_filters.neg[name] = filter\n        end\n      end\n      new_filters\n    end\n\n    def self.not(filters)\n      return nil if filters.nil?\n\n      TypeFilters.new pos: filters.neg.dup, neg: filters.pos.dup\n    end\n\n    # If we have\n    #\n    #   if a = b\n    #     ...\n    #   end\n    #\n    # then `a` and `b` must have the same truthiness. Thus we can strengthen the\n    # negation of the condition from `!a || !b` to `!a && !b`, which usually\n    # provides a stricter filter.\n    def self.assign_var(filters, target)\n      if filters.nil?\n        return truthy(target)\n      end\n\n      name = target.name\n      filter = TruthyFilter.instance\n\n      new_filters = filters.dup\n      new_filters.pos[name] = TypeFilter.and(new_filters.pos[name]?, filter).not_nil!\n      new_filters.neg[name] = TypeFilter.and(new_filters.neg[name]?, filter.not).not_nil!\n      new_filters\n    end\n\n    def each(&)\n      pos.each do |key, value|\n        yield key, value\n      end\n    end\n\n    def dup\n      TypeFilters.new pos: pos.dup, neg: neg.dup\n    end\n\n    private def self.common_keys(filters1, filters2)\n      keys = [] of String\n      if filters1\n        keys.concat(filters1.pos.keys)\n        keys.concat(filters1.neg.keys)\n      end\n      if filters2\n        keys.concat(filters2.pos.keys)\n        keys.concat(filters2.neg.keys)\n      end\n      keys.uniq!\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/fix_missing_types.cr",
    "content": "require \"../semantic\"\n\nclass Crystal::FixMissingTypes < Crystal::Visitor\n  @program : Program\n  @fixed : Set(Def)\n\n  def initialize(mod)\n    @program = mod\n    @fixed = Set(Def).new.compare_by_identity\n  end\n\n  def visit(node : Def)\n    node.hook_expansions.try &.each &.accept self\n    false\n  end\n\n  def visit(node : ClassDef)\n    node.hook_expansions.try &.each &.accept self\n    true\n  end\n\n  def visit(node : Include)\n    node.hook_expansions.try &.each &.accept self\n    false\n  end\n\n  def visit(node : Extend)\n    node.hook_expansions.try &.each &.accept self\n    false\n  end\n\n  def visit(node : Macro)\n    false\n  end\n\n  def visit(node : ProcLiteral)\n    node.def.body.accept self\n    unless node.def.type?\n      node.def.type = @program.no_return\n    end\n    false\n  end\n\n  def visit(node : ProcPointer)\n    if expanded = node.expanded\n      expanded.accept(self)\n    else\n      node.call?.try &.accept self\n    end\n    false\n  end\n\n  def end_visit(node : ProcPointer)\n    if !node.type? && node.call?\n      arg_types = node.call.args.map &.type\n      arg_types.push @program.no_return\n      node.type = node.call.type = @program.proc_of(arg_types)\n    end\n  end\n\n  def visit(node : ExpandableNode)\n    node.expanded.try &.accept self\n    false\n  end\n\n  def end_visit(node : Call)\n    if expanded = node.expanded\n      expanded.accept self\n    end\n\n    # If the block doesn't have a type, it's a no-return.\n    block = node.block\n    if block && !block.type?\n      block.type = @program.no_return\n    end\n\n    node.target_defs.try &.each do |target_def|\n      if @fixed.add?(target_def)\n        target_def.type = @program.no_return unless target_def.type?\n        target_def.accept_children self\n      end\n    end\n  end\n\n  def visit(node : ASTNode)\n    true\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/flags.cr",
    "content": "class Crystal::Program\n  @flags : Set(String)?\n  @host_flags : Set(String)?\n\n  # Returns the flags for this program. By default these\n  # are computed from the target triple (for example x86_64,\n  # darwin, linux, etc.), but can be overwritten with `flags=`\n  # and also added with the `-D` command line argument.\n  #\n  # See `Compiler#flags`.\n  def flags\n    @flags ||= flags_for_target(codegen_target)\n  end\n\n  def host_flags\n    @host_flags ||= flags_for_target(Config.host_target)\n  end\n\n  # Returns `true` if *name* is in the program's flags.\n  def has_flag?(name : String)\n    flags.includes?(name)\n  end\n\n  # Returns the value of a flag in the format `\"#{key}=#{value}\"`.\n  # Or `true` if the flags contain `key`.\n  def flag_value(name : String) : String | Bool\n    self.class.flag_value(flags_ary, name)\n  end\n\n  # Returns the value of a host flag in the format `\"#{key}=#{value}\"`.\n  # Or `true` if the host flags contain `key`.\n  def host_flag_value(name : String) : String | Bool\n    self.class.flag_value(host_flags_ary, name)\n  end\n\n  private getter flags_ary : Array(String) do\n    flags.to_a\n  end\n  private getter host_flags_ary : Array(String) do\n    host_flags.to_a\n  end\n\n  def self.flag_value(flags, name)\n    flags.reverse_each do |flag|\n      return true if flag == name\n\n      # Easy test to skip items that wouldn't match anyway\n      next unless flag.starts_with?(name)\n\n      key, assign, value = flag.partition(\"=\")\n      if key == name\n        return assign == \"=\" ? value : true\n      end\n    end\n\n    false\n  end\n\n  def bits64?\n    codegen_target.pointer_bit_width == 64\n  end\n\n  def size_bit_width\n    codegen_target.size_bit_width\n  end\n\n  private def flags_for_target(target)\n    flags = Set(String).new\n\n    flags.add target.architecture\n    flags.add target.vendor\n    flags.concat target.environment_parts\n\n    flags.add \"bits#{target.pointer_bit_width}\"\n\n    flags.add \"armhf\" if target.armhf?\n\n    flags.add \"unix\" if target.unix?\n    flags.add \"win32\" if target.win32?\n\n    flags.add \"darwin\" if target.macos?\n    if target.freebsd?\n      flags.add \"freebsd\"\n      flags.add \"freebsd#{target.freebsd_version}\"\n    end\n    flags.add \"netbsd\" if target.netbsd?\n\n    if target.openbsd?\n      flags.add \"openbsd\"\n\n      case target.architecture\n      when \"aarch64\"\n        flags.add \"branch-protection=bti\" unless flags.any?(&.starts_with?(\"branch-protection=\"))\n      when \"x86_64\", \"i386\"\n        flags.add \"cf-protection=branch\" unless flags.any?(&.starts_with?(\"cf-protection=\"))\n      end\n    end\n\n    flags.add \"dragonfly\" if target.dragonfly?\n    flags.add \"solaris\" if target.solaris?\n    flags.add \"android\" if target.android?\n\n    flags.add \"bsd\" if target.bsd?\n\n    if target.avr? && (cpu = target_machine.cpu.presence)\n      flags.add cpu\n    end\n\n    flags\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/hooks.cr",
    "content": "module Crystal\n  class Program\n    record FinishedHook, scope : ModuleType, macro : Macro, node : ASTNode\n    getter finished_hooks = [] of FinishedHook\n\n    def add_finished_hook(scope, a_macro, node)\n      @finished_hooks << FinishedHook.new(scope, a_macro, node)\n    end\n\n    # Visits all finished hooks with the given visitor\n    def process_finished_hooks(visitor)\n      @finished_hooks.each do |hook|\n        if visitor.is_a?(SemanticVisitor)\n          old_type = visitor.current_type.as(ModuleType)\n          visitor.current_type = hook.scope\n          hook.node.accept(visitor)\n          visitor.current_type = old_type\n        else\n          hook.node.accept(visitor)\n        end\n      end\n    end\n\n    def visit_with_finished_hooks(node, visitor)\n      node.accept visitor\n      process_finished_hooks visitor\n    end\n  end\n\n  # Holds hook expansions.\n  #\n  # For example, a `ClassDef` node will include macro expansions\n  # that result from the `inherited` hook. So in this code:\n  #\n  # ```\n  # class Foo\n  #   macro inherited\n  #     puts 1\n  #   end\n  # end\n  #\n  # class Bar < Foo\n  # end\n  # ```\n  #\n  # The AST node for the Bar class declaration (a `ClassDef`)\n  # will include a hook expansion consisting of `puts 1`, that\n  # is typed and executed before Bar's body.\n  module HookExpansionsContainer\n    getter hook_expansions : Array(ASTNode)?\n\n    def add_hook_expansion(node)\n      expansions = @hook_expansions ||= [] of ASTNode\n      expansions << node\n    end\n  end\n\n  class ClassDef\n    # Hook expansions correspond to the `inherited` hook\n    #\n    # ```\n    # class Foo\n    #   macro inherited\n    #     puts 1\n    #   end\n    # end\n    #\n    # # At this point the `inherited` hook is triggered\n    # class Bar < Foo\n    # end\n    # ```\n    include HookExpansionsContainer\n  end\n\n  class Include\n    # Hook expansions correspond to the `included` hook\n    #\n    # ```\n    # module Moo\n    #   macro included\n    #     puts 1\n    #   end\n    # end\n    #\n    # class Foo\n    #   # At this point the `included` hook is triggered\n    #   include Moo\n    # end\n    # ```\n    include HookExpansionsContainer\n  end\n\n  class Extend\n    # Hook expansions correspond to the `extended` hook\n    #\n    # ```\n    # module Moo\n    #   macro extended\n    #     puts 1\n    #   end\n    # end\n    #\n    # class Foo\n    #   # At this point the `extended` hook is triggered\n    #   extend Moo\n    # end\n    # ```\n    include HookExpansionsContainer\n  end\n\n  class Def\n    # Hook expansions correspond to the `method_added` hook\n    #\n    # ```\n    # class Foo\n    #   macro method_added(method)\n    #     # ...\n    #   end\n    #\n    #   # At this point the `method_added` hook is triggered\n    #   def foo\n    #   end\n    # end\n    # ```\n    include HookExpansionsContainer\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/instance_vars_initializer_visitor.cr",
    "content": "require \"./semantic_visitor\"\n\n# In this pass we check instance var initializers like:\n#\n#     @x = 1\n#     @x : Int32 = 1\n#\n# These initializers run when an instance is created,\n# so there's no way that the main code can use them before\n# creating an instance to use them. Conclusion: we must\n# analyze them before the main code.\n#\n# This solves the following problem:\n#\n# ```\n# # Here the compiler would complain because @bar\n# # wasn't initialized/analyzed yet\n# Foo.new.bar + 1\n#\n# class Foo\n#   @bar = 1\n#\n#   def bar\n#     @bar\n#   end\n# end\n# ```\nclass Crystal::InstanceVarsInitializerVisitor < Crystal::SemanticVisitor\n  record Initializer, scope : Type, target : InstanceVar, value : ASTNode, meta_vars : MetaVars\n  getter initializers = [] of Initializer\n\n  def visit_any(node)\n    case node\n    when Assign\n      node.target.is_a?(InstanceVar)\n    when TypeDeclaration\n      node.var.is_a?(InstanceVar)\n    when FileNode, Expressions, ClassDef, ModuleDef, Alias, Include, Extend, LibDef, Def, Macro, Call, Require,\n         MacroExpression, MacroIf, MacroFor, VisibilityModifier\n      true\n    else\n      false\n    end\n  end\n\n  def visit(node : Assign)\n    target = node.target.as(InstanceVar)\n    value = node.value\n    type_instance_var(node, target, value)\n    false\n  end\n\n  def visit(node : TypeDeclaration)\n    target = node.var.as(InstanceVar)\n    value = node.value\n    type_instance_var(node, target, value) if value\n    false\n  end\n\n  def type_instance_var(node, target, value)\n    current_type = current_type()\n    case current_type\n    when Program, FileModule\n      node.raise \"can't use instance variables at the top level\"\n    when ClassType, NonGenericModuleType, GenericModuleType\n      initializers << Initializer.new(current_type, target, value, MetaVars.new)\n      node.type = @program.nil\n      return\n    else\n      # TODO: can this happen?\n    end\n  end\n\n  def finish\n    scope_initializers = [] of InstanceVarInitializerContainer::InstanceVarInitializer?\n\n    # First declare them, so when we type all of them we will have\n    # the info of which instance vars have initializers (so they are not nil)\n    initializers.each do |i|\n      scope = i.scope\n      unless scope.lookup_instance_var?(i.target.name)\n        program.undefined_instance_variable(i.target, scope, nil)\n      end\n\n      scope_initializers <<\n        scope.add_instance_var_initializer(i.target.name, i.value, scope.is_a?(GenericType) ? nil : i.meta_vars)\n    end\n\n    # Now type them\n    initializers.each_with_index do |i, index|\n      scope = i.scope\n      value = i.value\n\n      next if scope.is_a?(GenericType)\n\n      # Check if we can autocast\n      if value.supports_autocast?(!@program.has_flag?(\"no_number_autocast\")) && (scope_initializer = scope_initializers[index])\n        cloned_value = value.clone\n        cloned_value.accept MainVisitor.new(program)\n        if casted_value = MainVisitor.check_automatic_cast(@program, cloned_value, scope.lookup_instance_var(i.target.name).type)\n          scope_initializer.value = casted_value\n          next\n        end\n      end\n\n      ivar_visitor = MainVisitor.new(program, meta_vars: i.meta_vars)\n      ivar_visitor.scope = scope.metaclass\n      ivar_visitor.pushing_type(scope.as(ModuleType)) do\n        value.accept ivar_visitor\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/lib.cr",
    "content": "class Crystal::Call\n  def recalculate_lib_call(obj_type)\n    replace_splats\n\n    old_target_defs = @target_defs\n\n    external = obj_type.lookup_first_def(name, false).as(External?)\n    raise \"undefined fun '#{name}' for #{obj_type}\" unless external\n\n    check_lib_call_named_args(external)\n\n    check_fun_args_size_match obj_type, external\n    check_fun_out_args external\n    unless obj_and_args_types_set?\n      # we can actually compute a type for this call even\n      # if we don't know the arguments types yet\n      self.type = external.type\n      return\n    end\n\n    check_fun_args_types_match obj_type, external\n\n    obj_type.used = true\n    external.used = true\n\n    untyped_defs = [external] of Def\n    @target_defs = untyped_defs\n\n    self.unbind_from old_target_defs if old_target_defs\n    self.bind_to untyped_defs\n  end\n\n  def check_lib_call_named_args(external)\n    named_args = self.named_args\n    return unless named_args\n\n    if external.varargs?\n      raise \"can't use named args with variadic function\"\n    end\n\n    # We check that all arguments are covered, and then we\n    # rewrite this call to not have named arguments\n    # (a dispatch can't include a lib type, so this call\n    # will only resolve to a function)\n\n    covered = BitArray.new(external.args.size)\n    args.size.times do |i|\n      covered[i] = true\n    end\n\n    # We gather named args according to their index.\n    sorted_named_args = [] of {Int32, NamedArgument}\n    named_args.each do |named_arg|\n      found_index = external.args.index { |arg| arg.name == named_arg.name }\n      unless found_index\n        named_arg.raise \"no parameter named '#{named_arg.name}'\"\n      end\n\n      if covered[found_index]\n        named_arg.raise \"argument for parameter '#{named_arg.name}' already specified\"\n      end\n\n      covered[found_index] = true\n      sorted_named_args << {found_index, named_arg}\n    end\n\n    missing_args = [] of String\n    covered.each_with_index do |value, index|\n      unless value\n        missing_args << external.args[index].name\n      end\n    end\n\n    if missing_args.size == 1\n      raise \"missing argument: #{missing_args.first}\"\n    elsif missing_args.size > 1\n      raise \"missing arguments: #{missing_args.join \", \"}\"\n    end\n\n    # Now we sort the named args according to their index.\n    # We can now append them to this call, as they necessarily\n    # must come after positional args that are already covered\n    # (and we checked that all args are covered), and we\n    # remove our named args.\n    sorted_named_args.sort_by! &.[0]\n    sorted_named_args.each do |(index, named_arg)|\n      self.args << named_arg.value\n    end\n    self.named_args = nil\n  end\n\n  def check_fun_args_size_match(obj_type, external)\n    call_args_count = args.size\n    all_args_count = external.args.size\n\n    if external.varargs? && call_args_count >= all_args_count\n      return\n    end\n\n    required_args_count = external.args.count { |arg| !arg.default_value }\n\n    return if required_args_count <= call_args_count <= all_args_count\n\n    wrong_number_of_arguments \"'#{full_name(obj_type)}'\", args.size, external.args.size\n  end\n\n  def check_fun_out_args(untyped_def)\n    untyped_def.args.each_with_index do |arg, i|\n      call_arg = self.args[i]\n      if call_arg.is_a?(Out)\n        arg_type = arg.type\n        if arg_type.is_a?(PointerInstanceType)\n          if arg_type.element_type.remove_typedef.void?\n            call_arg.raise \"can't use out with Void* (parameter #{lib_arg_name(arg, i)} of #{untyped_def.owner}.#{untyped_def.name} is Void*)\"\n          end\n\n          if call_arg.exp.is_a?(Underscore)\n            call_arg.exp.type = arg_type.element_type\n          else\n            var = parent_visitor.lookup_var_or_instance_var(call_arg.exp)\n            var.bind_to Var.new(\"out\", arg_type.element_type)\n            call_arg.exp.bind_to var\n            parent_visitor.bind_meta_var(call_arg.exp)\n          end\n        else\n          call_arg.raise \"parameter #{lib_arg_name(arg, i)} of #{untyped_def.owner}.#{untyped_def.name} cannot be passed as 'out' because it is not a pointer\"\n        end\n      end\n    end\n\n    # Check that there are no out args more then the number of arguments in the fun\n    if untyped_def.varargs?\n      untyped_def.args.size.upto(self.args.size - 1) do |i|\n        self_arg = self.args[i]\n        if self_arg.is_a?(Out)\n          self_arg.raise \"can't use out at varargs position: declare the variable with `#{self_arg.exp} = uninitialized ...` and pass it with `pointerof(#{self_arg.exp})`\"\n        end\n      end\n    end\n  end\n\n  def check_fun_args_types_match(obj_type, typed_def)\n    typed_def.args.each_with_index do |typed_def_arg, i|\n      self_arg = self.args[i]\n      check_fun_arg_type_matches(obj_type, self_arg, typed_def_arg, i)\n    end\n\n    # Need to call to_unsafe on variadic args too\n    if typed_def.varargs?\n      typed_def.args.size.upto(self.args.size - 1) do |i|\n        self_arg = self.args[i]\n        self_arg_type = self_arg.type?\n        if self_arg_type\n          unless self_arg_type.nil_type? || self_arg_type.allowed_in_lib?\n            implicit_call = Conversions.try_to_unsafe(self_arg.clone, parent_visitor) do |ex|\n              if Conversions.to_unsafe_lookup_failed?(ex)\n                self_arg.raise \"argument ##{i + 1} of '#{full_name(obj_type)}' is not a primitive type and no #{self_arg_type}#to_unsafe method found\"\n              else\n                self_arg.raise ex.message, ex\n              end\n            end\n            implicit_call_type = implicit_call.type?\n            if implicit_call_type\n              if implicit_call_type.allowed_in_lib?\n                self.args[i] = implicit_call\n              else\n                self_arg.raise \"converted #{self_arg_type} invoking to_unsafe, but #{implicit_call_type} is not a primitive type\"\n              end\n            else\n              self_arg.raise \"tried to convert #{self_arg_type} invoking to_unsafe, but can't deduce its type\"\n            end\n          end\n        else\n          self_arg.raise \"can't deduce argument type\"\n        end\n      end\n    end\n  end\n\n  def check_fun_arg_type_matches(obj_type, self_arg, typed_def_arg, index)\n    expected_type = typed_def_arg.type\n    actual_type = self_arg.type\n    actual_type = program.pointer_of(actual_type) if self_arg.is_a?(Out)\n    return if actual_type.compatible_with?(expected_type)\n    return if actual_type.implicitly_converted_in_c_to?(expected_type)\n\n    unaliased_type = expected_type.remove_alias\n    case unaliased_type\n    when IntegerType\n      return if convert_numeric_argument self_arg, unaliased_type, expected_type, actual_type, index\n    when FloatType\n      return if convert_numeric_argument self_arg, unaliased_type, expected_type, actual_type, index\n    end\n\n    implicit_call = Conversions.try_to_unsafe(self_arg.clone, parent_visitor) do |ex|\n      if Conversions.to_unsafe_lookup_failed?(ex)\n        arg_name = lib_arg_name(typed_def_arg, index)\n\n        if expected_type.is_a?(ProcInstanceType) &&\n           actual_type.is_a?(ProcInstanceType) &&\n           expected_type.arg_types == actual_type.arg_types\n          self_arg.raise \"argument #{arg_name} of '#{full_name(obj_type)}' must be a Proc returning #{expected_type.return_type}, not #{actual_type.return_type}\"\n        else\n          self_arg.raise \"argument #{arg_name} of '#{full_name(obj_type)}' must be #{expected_type}, not #{actual_type}\"\n        end\n      else\n        self_arg.raise ex.message, ex\n      end\n    end\n\n    implicit_call_type = implicit_call.type?\n    if implicit_call_type\n      if implicit_call_type.compatible_with?(expected_type) || implicit_call_type.implicitly_converted_in_c_to?(expected_type)\n        self.args[index] = implicit_call\n      else\n        arg_name = lib_arg_name(typed_def_arg, index)\n        self_arg.raise \"argument #{arg_name} of '#{full_name(obj_type)}' must be #{expected_type}, not #{actual_type} (nor #{implicit_call_type} returned by '#{actual_type}#to_unsafe')\"\n      end\n    else\n      self_arg.raise \"tried to convert #{actual_type} to #{expected_type} invoking to_unsafe, but can't deduce its type\"\n    end\n  end\n\n  def check_not_lib_out_args\n    args.find(&.is_a?(Out)).try &.raise \"out can only be used with lib funs\"\n    named_args.try &.find(&.value.is_a?(Out)).try &.raise \"out can only be used with lib funs\"\n  end\n\n  def convert_numeric_argument(self_arg, unaliased_type, expected_type, actual_type, index)\n    if self_arg.is_a?(NumberLiteral)\n      # TODO: check that the number literal fits, error otherwise\n\n      # If converting from a float to integer, we need to remove the dot\n      # so that later the codegen finds a correct value\n      if unaliased_type.is_a?(IntegerType) && (dot_index = self_arg.value.index('.'))\n        self_arg.value = self_arg.value[0...dot_index]\n      end\n\n      num = self_arg.to_number\n\n      self_arg.value =\n        case unaliased_type\n        when program.uint8  ; num.to_u8!.to_s\n        when program.uint16 ; num.to_u16!.to_s\n        when program.uint32 ; num.to_u32!.to_s\n        when program.uint64 ; num.to_u64!.to_s\n        when program.int8   ; num.to_i8!.to_s\n        when program.int16  ; num.to_i16!.to_s\n        when program.int32  ; num.to_i32!.to_s\n        when program.int64  ; num.to_i64!.to_s\n        when program.float32; num.to_f32!.to_s\n        else                  num.to_f64!.to_s\n        end\n      self_arg.kind = unaliased_type.kind\n      self_arg.type = unaliased_type\n      return true\n    end\n\n    convert_call = Conversions.numeric_argument(self_arg, self_arg.clone, parent_visitor, unaliased_type, expected_type, actual_type)\n    return false unless convert_call\n\n    self.args[index] = convert_call\n    true\n  end\nend\n\nclass Crystal::Type\n  # Returns `true` if this type can be used in a lib declaration:\n  # in a fun, struct/union member, or typedef.\n  def allowed_in_lib?\n    case self\n    when NilType\n      false\n    when PrimitiveType, NoReturnType, VoidType, TypeDefType, EnumType\n      true\n    when PointerInstanceType\n      self.element_type.allowed_in_lib?\n    when TupleInstanceType\n      self.tuple_types.all? &.allowed_in_lib?\n    when NamedTupleInstanceType\n      self.entries.all? &.type.allowed_in_lib?\n    when NilableType\n      self.not_nil_type.allowed_in_lib?\n    when NilableProcType\n      self.proc_type.allowed_in_lib?\n    when ProcInstanceType\n      self.arg_types.all?(&.allowed_in_lib?) && (self.return_type.allowed_in_lib? || self.return_type.nil_type?)\n    when StaticArrayInstanceType\n      self.element_type.allowed_in_lib?\n    else\n      self.extern?\n    end\n  end\n\n  # Returns `true` if this type can be implicitly converted to\n  # `expected_type` when this is a lib fun argument, without\n  # involving `to_unsafe` or numeric conversions.\n  # For example `nil` can be passed to an argument of type pointer.\n  def implicitly_converted_in_c_to?(expected_type)\n    case self\n    when NilType\n      # nil will be sent as pointer\n      expected_type.pointer? || expected_type.proc?\n    when ProcInstanceType\n      # fun will be cast to return nil\n      expected_type.is_a?(ProcInstanceType) && expected_type.return_type == program.nil && expected_type.arg_types == self.arg_types\n    when PointerInstanceType\n      # any pointer matches a void*\n      expected_type.is_a?(PointerInstanceType) && expected_type.element_type.void?\n    else\n      false\n    end\n  end\nend\n\nprivate def lib_arg_name(arg, index)\n  arg.name.empty? ? \"##{index + 1}\" : \"'#{arg.name}'\"\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/literal_expander.cr",
    "content": "module Crystal\n  class LiteralExpander\n    def initialize(@program : Program)\n      @regexes = [] of {String, Regex::CompileOptions}\n    end\n\n    # Converts an array literal to creating an Array and storing the values:\n    #\n    # From:\n    #\n    #     [] of T\n    #\n    # To:\n    #\n    #     Array(T).new\n    #\n    # From:\n    #\n    #     [1, 2, 3]\n    #\n    # To:\n    #\n    #     ary = ::Array(typeof(1, 2, 3)).unsafe_build(3)\n    #     buf = ary.to_unsafe\n    #     buf[0] = 1\n    #     buf[1] = 2\n    #     buf[2] = 3\n    #     ary\n    #\n    # From:\n    #\n    #     [1, *exp2, *exp3, 4]\n    #\n    # To:\n    #\n    #     temp1 = exp2\n    #     temp2 = exp3\n    #     ary = ::Array(typeof(1, ::Enumerable.element_type(temp1), ::Enumerable.element_type(temp2), 4)).new(2)\n    #     ary << 1\n    #     ary.concat(temp1)\n    #     ary.concat(temp2)\n    #     ary << 4\n    #     ary\n    def expand(node : ArrayLiteral)\n      elem_temp_vars, elem_temp_var_count = complex_elem_temp_vars(node.elements)\n      if node_of = node.of\n        type_var = node_of\n      else\n        type_var = typeof_exp(node, elem_temp_vars)\n      end\n\n      capacity = node.elements.count { |elem| !elem.is_a?(Splat) }\n\n      generic = Generic.new(Path.global(\"Array\"), type_var).at(node)\n\n      if node.elements.any?(Splat)\n        ary_var = new_temp_var.at(node)\n\n        ary_instance = Call.new(generic, \"new\", NumberLiteral.new(capacity).at(node)).at(node)\n\n        exps = Array(ASTNode).new(node.elements.size + elem_temp_var_count + 2)\n        elem_temp_vars.try &.each_with_index do |elem_temp_var, i|\n          next unless elem_temp_var\n          elem_exp = node.elements[i]\n          elem_exp = elem_exp.exp if elem_exp.is_a?(Splat)\n          exps << Assign.new(elem_temp_var, elem_exp.clone).at(elem_temp_var)\n        end\n        exps << Assign.new(ary_var.clone, ary_instance).at(node)\n\n        node.elements.each_with_index do |elem, i|\n          temp_var = elem_temp_vars.try &.[i]\n          if elem.is_a?(Splat)\n            exps << Call.new(ary_var.clone, \"concat\", (temp_var || elem.exp).clone).at(node)\n          else\n            exps << Call.new(ary_var.clone, \"<<\", (temp_var || elem).clone).at(node)\n          end\n        end\n\n        exps << ary_var\n\n        Expressions.new(exps).at(node)\n      elsif capacity.zero?\n        Call.new(generic, \"new\").at(node)\n      else\n        ary_var = new_temp_var.at(node)\n\n        ary_instance = Call.new(generic, \"unsafe_build\", NumberLiteral.new(capacity).at(node)).at(node)\n\n        buffer = Call.new(ary_var, \"to_unsafe\").at(node)\n        buffer_var = new_temp_var.at(node)\n\n        exps = Array(ASTNode).new(node.elements.size + elem_temp_var_count + 3)\n        elem_temp_vars.try &.each_with_index do |elem_temp_var, i|\n          next unless elem_temp_var\n          elem_exp = node.elements[i]\n          exps << Assign.new(elem_temp_var, elem_exp.clone).at(elem_temp_var)\n        end\n        exps << Assign.new(ary_var.clone, ary_instance).at(node)\n        exps << Assign.new(buffer_var, buffer).at(node)\n\n        node.elements.each_with_index do |elem, i|\n          temp_var = elem_temp_vars.try &.[i]\n          exps << Call.new(buffer_var.clone, \"[]=\", NumberLiteral.new(i).at(node), (temp_var || elem).clone).at(node)\n        end\n\n        exps << ary_var.clone\n\n        Expressions.new(exps).at(node)\n      end\n    end\n\n    def complex_elem_temp_vars(elems : Array, &)\n      temp_vars = nil\n      count = 0\n\n      elems.each_with_index do |elem, i|\n        elem = yield elem\n        elem = elem.exp if elem.is_a?(Splat)\n        next if elem.is_a?(Var) || elem.is_a?(InstanceVar) || elem.is_a?(ClassVar) || elem.simple_literal?\n\n        temp_vars ||= Array(Var?).new(elems.size, nil)\n        temp_vars[i] = new_temp_var.at(elem)\n        count += 1\n      end\n\n      {temp_vars, count}\n    end\n\n    def complex_elem_temp_vars(elems : Array(ASTNode))\n      complex_elem_temp_vars(elems, &.itself)\n    end\n\n    def typeof_exp(node : ArrayLiteral, temp_vars : Array(Var?)? = nil)\n      type_exps = node.elements.map_with_index do |elem, i|\n        temp_var = temp_vars.try &.[i]\n        if elem.is_a?(Splat)\n          Call.new(Path.global(\"Enumerable\").at(node), \"element_type\", (temp_var || elem.exp).clone).at(node)\n        else\n          (temp_var || elem).clone\n        end\n      end\n\n      TypeOf.new(type_exps).at(node)\n    end\n\n    # Converts an array-like literal to creating a container and storing the values:\n    #\n    # From:\n    #\n    #     T{1, 2, 3}\n    #\n    # To:\n    #\n    #     ary = T.new\n    #     ary << 1\n    #     ary << 2\n    #     ary << 3\n    #     ary\n    #\n    # From:\n    #\n    #     T{1, *exp2, *exp3, 4}\n    #\n    # To:\n    #\n    #     ary = T.new\n    #     ary << 1\n    #     exp2.each { |v| ary << v }\n    #     exp3.each { |v| ary << v }\n    #     ary << 4\n    #     ary\n    #\n    # If `T` is an uninstantiated generic type, injects a `typeof` with the\n    # element types.\n    def expand_named(node : ArrayLiteral, generic_type : ASTNode?)\n      elem_temp_vars, elem_temp_var_count = complex_elem_temp_vars(node.elements)\n      if generic_type\n        type_of = typeof_exp(node, elem_temp_vars)\n        node_name = Generic.new(generic_type, type_of).at(node)\n      else\n        node_name = node.name\n      end\n\n      constructor = Call.new(node_name, \"new\").at(node)\n      if node.elements.empty?\n        return constructor\n      end\n\n      ary_var = new_temp_var.at(node)\n\n      exps = Array(ASTNode).new(node.elements.size + elem_temp_var_count + 2)\n      elem_temp_vars.try &.each_with_index do |elem_temp_var, i|\n        next unless elem_temp_var\n        elem_exp = node.elements[i]\n        elem_exp = elem_exp.exp if elem_exp.is_a?(Splat)\n        exps << Assign.new(elem_temp_var, elem_exp.clone).at(elem_temp_var)\n      end\n      exps << Assign.new(ary_var.clone, constructor).at(node)\n\n      node.elements.each_with_index do |elem, i|\n        temp_var = elem_temp_vars.try &.[i]\n        if elem.is_a?(Splat)\n          yield_var = new_temp_var(elem)\n          each_body = Call.new(ary_var.clone, \"<<\", yield_var.clone).at(node)\n          each_block = Block.new(args: [yield_var], body: each_body).at(node)\n          exps << Call.new((temp_var || elem.exp).clone, \"each\", block: each_block).at(node)\n        else\n          exps << Call.new(ary_var.clone, \"<<\", (temp_var || elem).clone).at(node)\n        end\n      end\n\n      exps << ary_var\n\n      Expressions.new(exps).at(node)\n    end\n\n    # Converts a hash literal into creating a Hash and assigning keys and values.\n    #\n    # Equivalent to a hash-like literal using `::Hash`.\n    def expand(node : HashLiteral)\n      expand_named(node, Path.global(\"Hash\"))\n    end\n\n    # Converts a hash-like literal into creating a Hash and assigning keys and values:\n    #\n    # From:\n    #\n    #     T{}\n    #\n    # To:\n    #\n    #     T.new\n    #\n    # From:\n    #\n    #     {} of K => V\n    #\n    # To:\n    #\n    #     ::Hash(K, V).new\n    #\n    # From:\n    #\n    #     T{a => b, c => d}\n    #\n    # To:\n    #\n    #     hash = T.new\n    #     hash[a] = b\n    #     hash[c] = d\n    #     hash\n    #\n    # Or if `T` is an uninstantiated generic type:\n    #\n    #     hash = T(typeof(a, c), typeof(b, d)).new\n    #     hash[a] = b\n    #     hash[c] = d\n    #     hash\n    def expand_named(node : HashLiteral, generic_type : ASTNode?)\n      key_temp_vars, key_temp_var_count = complex_elem_temp_vars(node.entries, &.key)\n      value_temp_vars, value_temp_var_count = complex_elem_temp_vars(node.entries, &.value)\n\n      if of = node.of\n        # `generic_type` is nil here\n        type_vars = [of.key, of.value] of ASTNode\n        generic = Generic.new(Path.global(\"Hash\"), type_vars).at(node)\n      elsif generic_type\n        # `node.entries` is non-empty here\n        typeof_key = TypeOf.new(node.entries.map_with_index { |x, i| (key_temp_vars.try(&.[i]) || x.key).clone.as(ASTNode) }).at(node)\n        typeof_value = TypeOf.new(node.entries.map_with_index { |x, i| (value_temp_vars.try(&.[i]) || x.value).clone.as(ASTNode) }).at(node)\n        generic = Generic.new(generic_type, [typeof_key, typeof_value] of ASTNode).at(node)\n      else\n        generic = node.name\n      end\n\n      constructor = Call.new(generic, \"new\").at(node)\n      return constructor if node.entries.empty?\n\n      hash_var = new_temp_var\n\n      exps = Array(ASTNode).new(node.entries.size + key_temp_var_count + value_temp_var_count + 2)\n      node.entries.each_with_index do |entry, i|\n        if key_temp_var = key_temp_vars.try &.[i]?\n          exps << Assign.new(key_temp_var, entry.key.clone).at(key_temp_var)\n        end\n        if value_temp_var = value_temp_vars.try &.[i]?\n          exps << Assign.new(value_temp_var, entry.value.clone).at(value_temp_var)\n        end\n      end\n      exps << Assign.new(hash_var.clone, constructor).at(node)\n\n      node.entries.each_with_index do |entry, i|\n        key_exp = key_temp_vars.try(&.[i]) || entry.key\n        value_exp = value_temp_vars.try(&.[i]) || entry.value\n        exps << Call.new(hash_var.clone, \"[]=\", key_exp.clone, value_exp.clone).at(node)\n      end\n\n      exps << hash_var\n      Expressions.new(exps).at(node)\n    end\n\n    # From:\n    #\n    #     /regex/flags\n    #\n    # To declaring a constant with this value (if not already declared):\n    #\n    # ```\n    # Regex.new(\"regex\", Regex::Options.new(flags))\n    # ```\n    #\n    # and then reading from that constant.\n    # That is, we cache regex literals to avoid recompiling them all of the time.\n    #\n    # Only do this for regex literals that don't contain interpolation.\n    # If there's an interpolation, expand to: Regex.new(interpolation, flags)\n    def expand(node : RegexLiteral)\n      node_value = node.value\n      case node_value\n      when StringLiteral\n        string = node_value.value\n\n        key = {string, node.options}\n        index = @regexes.index(key) || @regexes.size\n        const_name = \"$Regex:#{index}\"\n\n        if index == @regexes.size\n          @regexes << key\n\n          const_value = regex_new_call(node, StringLiteral.new(string).at(node))\n          const = Const.new(@program, @program, const_name, const_value)\n\n          @program.types[const_name] = const\n        else\n          const = @program.types[const_name].as(Const)\n        end\n\n        Path.new(const_name).at(const.value)\n      else\n        regex_new_call(node, node_value)\n      end\n    end\n\n    private def regex_new_call(node, value)\n      Call.new(Path.global(\"Regex\").at(node), \"new\", value, regex_options(node)).at(node)\n    end\n\n    private def regex_options(node)\n      Call.new(Path.global([\"Regex\", \"Options\"]).at(node), \"new\", NumberLiteral.new(node.options.value.to_s).at(node)).at(node)\n    end\n\n    # Convert and to if:\n    #\n    # From:\n    #\n    #     a && b\n    #\n    # To:\n    #\n    #     if temp = a\n    #       b\n    #     else\n    #       temp\n    #     end\n    def expand(node : And)\n      left = node.left.single_expression\n\n      new_node = if left.is_a?(Var) || (left.is_a?(IsA) && left.obj.is_a?(Var))\n                   If.new(left, node.right, left.clone).at(node)\n                 elsif left.is_a?(Assign) && left.target.is_a?(Var)\n                   If.new(left, node.right, left.target.clone).at(node)\n                 elsif left.is_a?(Not) && left.exp.is_a?(Var)\n                   If.new(left, node.right, left.clone).at(node)\n                 elsif left.is_a?(Not) && ((left_exp = left.exp).is_a?(IsA) && left_exp.obj.is_a?(Var))\n                   If.new(left, node.right, left.clone).at(node)\n                 else\n                   temp_var = new_temp_var\n                   If.new(Assign.new(temp_var.clone, left).at(node), node.right, temp_var.clone).at(node)\n                 end\n      new_node.and = true\n      new_node.location = node.location\n      new_node\n    end\n\n    # Convert or to if\n    #\n    # From:\n    #\n    #     a || b\n    #\n    # To:\n    #\n    #     if temp = a\n    #       temp\n    #     else\n    #       b\n    #     end\n    def expand(node : Or)\n      left = node.left.single_expression\n\n      new_node = if left.is_a?(Var) || (left.is_a?(IsA) && left.obj.is_a?(Var))\n                   If.new(left, left.clone, node.right).at(node)\n                 elsif left.is_a?(Assign) && left.target.is_a?(Var)\n                   If.new(left, left.target.clone, node.right).at(node)\n                 elsif left.is_a?(Not) && left.exp.is_a?(Var)\n                   If.new(left, left.clone, node.right).at(node)\n                 elsif left.is_a?(Not) && ((left_exp = left.exp).is_a?(IsA) && left_exp.obj.is_a?(Var))\n                   If.new(left, left.clone, node.right).at(node)\n                 else\n                   temp_var = new_temp_var\n                   If.new(Assign.new(temp_var.clone, left).at(node), temp_var.clone, node.right).at(node)\n                 end\n      new_node.or = true\n      new_node.location = node.location\n      new_node\n    end\n\n    # Transform a range literal into creating a Range object.\n    #\n    # From:\n    #\n    #    1 .. 3\n    #\n    # To:\n    #\n    #    Range.new(1, 3, true)\n    #\n    # From:\n    #\n    #    1 ... 3\n    #\n    # To:\n    #\n    #    Range.new(1, 3, false)\n    def expand(node : RangeLiteral)\n      path = Path.global(\"Range\").at(node)\n      bool = BoolLiteral.new(node.exclusive?).at(node)\n      Call.new(path, \"new\", node.from, node.to, bool).at(node)\n    end\n\n    # Convert an interpolation to a call to `String.interpolation`\n    #\n    # From:\n    #\n    #     \"foo#{bar}baz#{qux}\"\n    #\n    # To:\n    #\n    #     String.interpolation(\"foo\", bar, \"baz\", qux)\n    def expand(node : StringInterpolation)\n      # We could do `node.expressions.dup` for more purity,\n      # but the string interpolation isn't used later on so this is fine,\n      # and having pieces in a different representation but same end\n      # result is just fine.\n      pieces = node.expressions\n      combine_contiguous_string_literals(pieces)\n      Call.new(Path.global(\"String\").at(node), \"interpolation\", pieces).at(node)\n    end\n\n    private def combine_contiguous_string_literals(pieces)\n      i = 0\n      pieces.reject! do |piece|\n        delete =\n          if i < pieces.size - 1\n            next_piece = pieces[i + 1]\n            if piece.is_a?(StringLiteral) && next_piece.is_a?(StringLiteral)\n              pieces[i + 1] = StringLiteral.new(piece.value + next_piece.value)\n              true\n            else\n              false\n            end\n          else\n            false\n          end\n        i += 1\n        delete\n      end\n    end\n\n    # Convert a Case into a series of if ... elseif ... end:\n    #\n    # From:\n    #\n    #     case foo\n    #     when bar, baz\n    #       1\n    #     when bun\n    #       2\n    #     else\n    #       3\n    #     end\n    #\n    # To:\n    #\n    #     temp = foo\n    #     if bar === temp || baz === temp\n    #       1\n    #     elsif bun === temp\n    #       2\n    #     else\n    #       3\n    #     end\n    #\n    # But, when the \"when\" has a constant name, it's transformed to is_a?:\n    #\n    # From:\n    #\n    #     case foo\n    #     when Bar\n    #       1\n    #     end\n    #\n    # To:\n    #\n    #     temp = foo\n    #     if temp.is_a?(Bar)\n    #       1\n    #     end\n    #\n    # We also take care to expand multiple conds\n    #\n    # From:\n    #\n    #     case {x, y}\n    #     when {1, 2}, {3, 4}\n    #       3\n    #     end\n    #\n    # To:\n    #\n    #     if (1 === x && y === 2) || (3 === x && 4 === y)\n    #       3\n    #     end\n    def expand(node : Case)\n      node_cond = node.cond\n\n      if node.whens.empty?\n        expressions = [] of ASTNode\n\n        node_else = node.else\n        if node_cond\n          expressions << node_cond\n          expressions << NilLiteral.new unless node_else\n        end\n        if node_else\n          expressions << node_else\n        end\n\n        return Expressions.new(expressions).at(node)\n      end\n\n      if node_cond\n        if node_cond.is_a?(TupleLiteral)\n          conds = node_cond.elements\n        else\n          conds = [node_cond]\n        end\n\n        assigns = [] of ASTNode\n        temp_vars = conds.map do |cond|\n          case cond = cond.single_expression\n          when Var, InstanceVar\n            temp_var = cond\n          when Assign\n            temp_var = cond.target\n            assigns << cond\n          else\n            temp_var = new_temp_var\n            assigns << Assign.new(temp_var.clone, cond).at(node_cond)\n          end\n          temp_var\n        end\n      end\n\n      a_if = nil\n      final_if = nil\n      node.whens.each do |wh|\n        final_comp = nil\n        wh.conds.each do |cond|\n          next if cond.is_a?(Underscore)\n\n          if node_cond.is_a?(TupleLiteral)\n            if cond.is_a?(TupleLiteral)\n              comp = nil\n              cond.elements.zip(temp_vars.not_nil!) do |lh, rh|\n                next if lh.is_a?(Underscore)\n\n                sub_comp = case_when_comparison(rh, lh).at(cond)\n                if comp\n                  comp = And.new(comp, sub_comp).at(comp)\n                else\n                  comp = sub_comp\n                end\n              end\n            else\n              comp = case_when_comparison(TupleLiteral.new(temp_vars.not_nil!.clone), cond).at(cond)\n            end\n          else\n            temp_var = temp_vars.try &.first\n            comp = case_when_comparison(temp_var, cond).at(cond)\n          end\n\n          next unless comp\n\n          if final_comp\n            final_comp = Or.new(final_comp, comp).at(final_comp)\n          else\n            final_comp = comp\n          end\n        end\n\n        final_comp ||= BoolLiteral.new(true)\n\n        wh_if = If.new(final_comp, wh.body).at(final_comp)\n        if a_if\n          a_if.else = wh_if\n        else\n          final_if = wh_if\n        end\n        a_if = wh_if\n      end\n\n      if node.exhaustive?\n        a_if.not_nil!.else = node.else || Unreachable.new\n      elsif node_else = node.else\n        a_if.not_nil!.else = node_else\n      end\n\n      final_if = final_if.not_nil!\n      final_exp = if assigns && !assigns.empty?\n                    assigns << final_if\n                    Expressions.new(assigns).at(node)\n                  else\n                    final_if\n                  end\n      final_exp.location = node.location\n      final_exp\n    end\n\n    # Convert a `select` statement into a `case` statement based on `Channel.select`\n    #\n    # From:\n    #\n    #     select\n    #     when foo then body\n    #     when x = bar then x.baz\n    #     end\n    #\n    # To:\n    #\n    #     %index, %value = ::Channel.select({foo_select_action, bar_select_action})\n    #     case %index\n    #     when 0\n    #       body\n    #     when 1\n    #       x = value.as(typeof(foo))\n    #       x.baz\n    #     else\n    #       ::raise(\"BUG: invalid select index\")\n    #     end\n    #\n    #\n    # If there's an `else` branch, use `Channel.non_blocking_select`.\n    #\n    # From:\n    #\n    #     select\n    #     when foo then body\n    #     else qux\n    #     end\n    #\n    # To:\n    #\n    #     %index, %value = ::Channel.non_blocking_select({foo_select_action})\n    #     case %index\n    #     when 0\n    #       body\n    #     else\n    #       qux\n    #     end\n    #\n    def expand(node : Select)\n      index_name = @program.new_temp_var_name\n      value_name = @program.new_temp_var_name\n\n      targets = [Var.new(index_name).at(node), Var.new(value_name).at(node)] of ASTNode\n      channel = Path.global(\"Channel\").at(node)\n\n      tuple_values = [] of ASTNode\n      case_whens = [] of When\n\n      node.whens.each_with_index do |a_when, index|\n        condition = a_when.conds.first\n        case condition\n        when Call\n          cloned_call = condition.clone\n          cloned_call.name = select_action_name(cloned_call.name)\n          tuple_values << cloned_call\n\n          case_whens << When.new([NumberLiteral.new(index).at(node)] of ASTNode, a_when.body.clone)\n        when Assign\n          cloned_call = condition.value.as(Call).clone\n          cloned_call.name = select_action_name(cloned_call.name)\n          tuple_values << cloned_call\n\n          typeof_node = TypeOf.new([condition.value.clone] of ASTNode).at(node)\n          cast = Cast.new(Var.new(value_name).at(node), typeof_node).at(node)\n          new_assign = Assign.new(condition.target.clone, cast).at(node)\n          new_body = Expressions.new([new_assign, a_when.body.clone] of ASTNode)\n          case_whens << When.new([NumberLiteral.new(index).at(node)] of ASTNode, new_body)\n        else\n          node.raise \"BUG: expected select when expression to be Assign or Call, not #{condition}\"\n        end\n      end\n\n      if node_else = node.else\n        case_else = node_else.clone\n      else\n        case_else = Call.new(\"raise\", StringLiteral.new(\"BUG: invalid select index\"), global: true).at(node)\n      end\n\n      call = Call.new(\n        channel,\n        node.else ? \"non_blocking_select\" : \"select\",\n        TupleLiteral.new(tuple_values).at(node),\n      ).at(node)\n      multi = MultiAssign.new(targets, [call] of ASTNode)\n      case_cond = Var.new(index_name).at(node)\n      a_case = Case.new(case_cond, case_whens, case_else, exhaustive: false).at(node)\n      Expressions.from([multi, a_case] of ASTNode).at(node)\n    end\n\n    def select_action_name(name)\n      case name\n      when .ends_with? \"!\"\n        name[0...-1] + \"_select_action!\"\n      when .ends_with? \"?\"\n        name[0...-1] + \"_select_action?\"\n      else\n        name + \"_select_action\"\n      end\n    end\n\n    # Transform a multi assign into many assigns.\n    def expand(node : MultiAssign)\n      splat_index = nil\n      splat_underscore = false\n      node.targets.each_with_index do |target, i|\n        if target.is_a?(Splat)\n          raise \"BUG: splat assignment already specified\" if splat_index\n          splat_index = i\n          splat_underscore = true if target.exp.is_a?(Underscore)\n        end\n      end\n\n      # From:\n      #\n      #     a, b = [1, 2]\n      #\n      #\n      # To:\n      #\n      #     temp = [1, 2]\n      #     a = temp[0]\n      #     b = temp[1]\n      #\n      # If the flag \"strict_multi_assign\" is present, requires `temp`'s size to\n      # match the number of assign targets exactly: (it must respond to `#size`)\n      #\n      #     temp = [1, 2]\n      #     raise ... if temp.size != 2\n      #     a = temp[0]\n      #     b = temp[1]\n      #\n      # From:\n      #\n      #     a, *b, c, d = [1, 2]\n      #\n      # To:\n      #\n      #     temp = [1, 2]\n      #     raise ... if temp.size < 3\n      #     a = temp[0]\n      #     b = temp[1..-3]\n      #     c = temp[-2]\n      #     d = temp[-1]\n      #\n      # Except any assignments to *_, including the indexing call, are omitted\n      # altogether.\n      if node.values.size == 1\n        value = node.values[0]\n        middle_splat = splat_index && (0 < splat_index < node.targets.size - 1)\n        raise_on_count_mismatch = @program.has_flag?(\"strict_multi_assign\") || middle_splat\n\n        temp_var = new_temp_var\n\n        # temp = ...\n        assigns = Array(ASTNode).new(node.targets.size + (splat_underscore ? 0 : 1) + (raise_on_count_mismatch ? 1 : 0))\n        assigns << Assign.new(temp_var.clone, value).at(value)\n\n        # raise ... if temp.size < ...\n        if raise_on_count_mismatch\n          size_call = Call.new(temp_var.clone, \"size\").at(value)\n          if middle_splat\n            size_comp = Call.new(size_call, \"<\", NumberLiteral.new(node.targets.size - 1)).at(value)\n          else\n            size_comp = Call.new(size_call, \"!=\", NumberLiteral.new(node.targets.size)).at(value)\n          end\n          index_error = Call.new(Path.global(\"IndexError\"), \"new\", StringLiteral.new(\"Multiple assignment count mismatch\")).at(value)\n          raise_call = Call.global(\"raise\", index_error).at(value)\n          assigns << If.new(size_comp, raise_call).at(value)\n        end\n\n        # ... = temp[...]\n        node.targets.each_with_index do |target, i|\n          if i == splat_index\n            next if splat_underscore\n            indexer = RangeLiteral.new(\n              NumberLiteral.new(i),\n              NumberLiteral.new(i - node.targets.size),\n              false,\n            ).at(value)\n          else\n            indexer = NumberLiteral.new(splat_index && i > splat_index ? i - node.targets.size : i)\n          end\n          call = Call.new(temp_var.clone, \"[]\", indexer).at(value)\n          assigns << transform_multi_assign_target(target, call)\n        end\n\n        exps = Expressions.new(assigns)\n\n        # From:\n        #\n        #     a, b = c, d\n        #\n        # To:\n        #\n        #     temp1 = c\n        #     temp2 = d\n        #     a = temp1\n        #     b = temp2\n        #\n        # From:\n        #\n        #     a, *b, c = d, e, f, g\n        #\n        # To:\n        #\n        #     temp1 = d\n        #     temp2 = ::Tuple.new(e, f)\n        #     temp3 = g\n        #     a = temp1\n        #     b = temp2\n        #     c = temp3\n        #\n        # Except values assigned to `*_` are evaluated directly where the\n        # `Tuple` would normally be constructed, and no assignments to `_` would\n        # actually take place.\n      else\n        if splat_index\n          raise \"BUG: multiple assignment count mismatch\" if node.targets.size - 1 > node.values.size\n        else\n          raise \"BUG: multiple assignment count mismatch\" if node.targets.size != node.values.size\n        end\n\n        assign_to_count = splat_underscore ? node.values.size : node.targets.size\n        assign_from_count = node.targets.size - (splat_underscore ? 1 : 0)\n        assign_to_temps = Array(ASTNode).new(assign_to_count)\n        assign_from_temps = Array(ASTNode).new(assign_from_count)\n\n        node.targets.each_with_index do |target, i|\n          if i == splat_index\n            if splat_underscore\n              node.values.each(within: i..i - node.targets.size) do |value|\n                assign_to_temps << value\n              end\n              next\n            end\n            value = Call.new(Path.global(\"Tuple\").at(node), \"new\", node.values[i..i - node.targets.size])\n          else\n            value = node.values[splat_index && i > splat_index ? i - node.targets.size : i]\n          end\n\n          temp_var = new_temp_var\n          assign_to_temps << Assign.new(temp_var.clone, value).at(node)\n          assign_from_temps << transform_multi_assign_target(target, temp_var.clone)\n        end\n\n        exps = Expressions.new(assign_to_temps.concat(assign_from_temps))\n      end\n      exps.location = node.location\n      exps\n    end\n\n    def transform_multi_assign_target(target, value)\n      if target.is_a?(Splat)\n        target = target.exp\n      end\n\n      if target.is_a?(Call)\n        target.name = \"#{target.name}=\"\n        target.args << value\n        target\n      else\n        Assign.new(target, value).at(target)\n      end\n    end\n\n    private def case_when_comparison(temp_var, cond)\n      return cond unless temp_var\n\n      right_side = temp_var.clone\n\n      check_implicit_obj Call\n      check_implicit_obj RespondsTo\n      check_implicit_obj IsA\n      check_implicit_obj Cast\n      check_implicit_obj NilableCast\n      check_implicit_obj Not\n\n      case cond\n      when NilLiteral\n        return IsA.new(right_side, Path.global(\"Nil\"))\n      when Path, Generic\n        return IsA.new(right_side, cond)\n      when Call\n        obj = cond.obj\n        case obj\n        when Path\n          if cond.name == \"class\"\n            return IsA.new(right_side, Metaclass.new(obj).at(obj))\n          end\n        when Generic\n          if cond.name == \"class\"\n            return IsA.new(right_side, Metaclass.new(obj).at(obj))\n          end\n        else\n          # no special treatment\n        end\n      else\n        # no special treatment\n      end\n\n      Call.new(cond, \"===\", right_side)\n    end\n\n    macro check_implicit_obj(type)\n      if cond.is_a?({{type}})\n        cond_obj = cond.is_a?(Not) ? cond.exp : cond.obj\n        if cond_obj.is_a?(ImplicitObj)\n          implicit_call = cond.clone.as({{type}})\n          if implicit_call.is_a?(Not)\n            implicit_call.exp = temp_var.clone\n          else\n            implicit_call.obj = temp_var.clone\n          end\n          return implicit_call\n        end\n      end\n    end\n\n    # Expand this:\n    #\n    # ```\n    # ->foo.bar(X, Y)\n    # ```\n    #\n    # To this:\n    #\n    # ```\n    # tmp = foo\n    # ->(x : X, y : Y) { tmp.bar(x, y) }\n    # ```\n    #\n    # Expand this:\n    #\n    # ```\n    # ->Foo.bar(X, Y)\n    # ```\n    #\n    # To this:\n    #\n    # ```\n    # ->(x : X, y : Y) { Foo.bar(x, y) }\n    # ```\n    #\n    # Expand this:\n    #\n    # ```\n    # ->bar(X, Y)\n    # ```\n    #\n    # To this:\n    #\n    # ```\n    # ->(x : X, y : Y) { bar(x, y) }\n    # ```\n    #\n    # in case the implicit `self` is a class or a virtual class.\n    def expand(node : ProcPointer)\n      obj = node.obj\n\n      if obj && !obj.is_a?(Path)\n        temp_var = new_temp_var.at(obj)\n        assign = Assign.new(temp_var, obj)\n        obj = temp_var\n      end\n\n      def_args = node.args.map do |arg|\n        Arg.new(@program.new_temp_var_name, restriction: arg).at(arg)\n      end\n\n      call_args = def_args.map do |def_arg|\n        Var.new(def_arg.name).at(def_arg).as(ASTNode)\n      end\n\n      body = Call.new(obj, node.name, call_args, global: node.global?).at(node)\n      proc_literal = ProcLiteral.new(Def.new(\"->\", def_args, body).at(node)).at(node)\n      proc_literal.proc_pointer = node\n\n      if assign\n        Expressions.new([assign, proc_literal])\n      else\n        proc_literal\n      end\n    end\n\n    def expand(node)\n      raise \"#{node} (#{node.class}) can't be expanded\"\n    end\n\n    def new_temp_var(key = nil)\n      @program.new_temp_var(key)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/main_visitor.cr",
    "content": "require \"./semantic_visitor\"\n\nmodule Crystal\n  class Program\n    def visit_main(node, visitor : MainVisitor = MainVisitor.new(self), process_finished_hooks = false, cleanup = true)\n      node.accept visitor\n      program.process_finished_hooks(visitor) if process_finished_hooks\n\n      missing_types = FixMissingTypes.new(self)\n      node.accept missing_types\n      program.process_finished_hooks(missing_types) if process_finished_hooks\n\n      node = cleanup node if cleanup\n\n      if process_finished_hooks\n        finished_hooks.map! do |hook|\n          hook_node = cleanup(hook.node)\n          FinishedHook.new(hook.scope, hook.macro, hook_node)\n        end\n      end\n\n      node\n    end\n  end\n\n  # This is the main visitor of the program, ran after types have been declared\n  # and their type declarations (like `@x : Int32`) have been processed.\n  #\n  # This visits the \"main\" code of the program and resolves calls, instantiates\n  # methods and visits them, recursively, with other MainVisitors.\n  #\n  # The visitor keeps track of a method's variables (or the main program, split into\n  # several files, in case of top-level code). It keeps track both of the type of a\n  # variable at a single point (stored in @vars) and the combined type of all assignments\n  # to it (in @meta_vars).\n  #\n  # Call resolution logic is in `Call#recalculate`, where method lookup is done.\n  class MainVisitor < SemanticVisitor\n    ValidGlobalAnnotations   = %w(ThreadLocal)\n    ValidClassVarAnnotations = %w(ThreadLocal)\n\n    getter! typed_def\n    property! untyped_def : Def\n    setter untyped_def\n    getter block : Block?\n    property call : Call?\n    property path_lookup\n    property fun_literal_context : Def | Program | Nil\n    property parent : MainVisitor?\n    property block_nest = 0\n    property with_scope : Type?\n\n    property match_context : MatchContext?\n\n    # These are the variables and types that come from a block specification\n    # like `&block : Int32 -> Int32`. When doing `yield 1` we need to verify\n    # that the yielded expression has the type that the block specification said.\n    property yield_vars : Array(Var)?\n\n    # In vars we store the types of variables as we traverse the nodes.\n    # These type are not cumulative: if you do `x = 1`, 'x' will have\n    # type Int32. Then if you do `x = false`, 'x' will have type Bool.\n    getter vars\n\n    # Here we store the cumulative types of variables as we traverse the nodes.\n    getter meta_vars : MetaVars\n    property is_initialize : Bool\n    property all_exception_handler_vars : Array(MetaVars)? = nil\n\n    private enum BlockKind\n      None\n      While\n      Block\n      Ensure\n    end\n\n    # It means the last block kind. It is used to detect `break` or `next`\n    # from `ensure`.\n    #\n    # ```\n    # begin\n    #   # `last_block_kind.none?`\n    # ensure\n    #   # `last_block_kind.ensure?`\n    #   while true\n    #     # `last_block_kind.while?`\n    #   end\n    #   loop do\n    #     # `last_block_kind.block?`\n    #   end\n    #   # `last_block_kind.ensure?`\n    # end\n    # ```\n    property last_block_kind : BlockKind = :none\n    property? inside_ensure : Bool = false\n    property? inside_constant = false\n    property file_module : FileModule?\n\n    @unreachable = false\n    @is_initialize = false\n    @inside_is_a = false\n    @in_type_args = 0\n\n    @while_stack = [] of While\n    @type_filters : TypeFilters?\n    @needs_type_filters = 0\n    @typeof_nest = 0\n    @found_self_in_initialize_call : Array(ASTNode)?\n    @used_ivars_in_calls_in_initialize : Hash(String, Array(ASTNode))?\n    @block_context : Block?\n    @while_vars : MetaVars?\n\n    # Type filters for `exp` in `!exp`, used after a `while`\n    @before_not_type_filters : TypeFilters?\n\n    def initialize(program, vars = MetaVars.new, @typed_def = nil, meta_vars = nil)\n      super(program, vars)\n      @is_initialize = !!(typed_def && (\n        typed_def.name == \"initialize\" ||\n        typed_def.name.starts_with?(\"initialize:\") # Because of expanded methods from named args\n      ))\n\n      # We initialize meta_vars from vars given in the constructor.\n      # We store those meta vars either in the typed def or in the program\n      # so the codegen phase knows the cumulative types to do allocas.\n      unless meta_vars\n        if typed_def = @typed_def\n          meta_vars = typed_def.vars = MetaVars.new\n        else\n          meta_vars = @program.vars\n        end\n        vars.each do |name, var|\n          meta_var = new_meta_var(name)\n          meta_var.bind_to(var)\n          meta_vars[name] = meta_var\n        end\n      end\n\n      @meta_vars = meta_vars\n    end\n\n    def initialize(*, from_main_visitor : MainVisitor)\n      super(from_main_visitor.@program, from_main_visitor.@vars)\n      @meta_vars = from_main_visitor.@meta_vars\n      @typed_def = from_main_visitor.@typed_def\n      @scope = from_main_visitor.@scope\n      @path_lookup = from_main_visitor.@path_lookup\n    end\n\n    def visit_any(node)\n      @unreachable = false\n      super\n    end\n\n    def visit(node : FileNode)\n      old_vars = @vars\n      old_meta_vars = @meta_vars\n      old_file_module = @file_module\n\n      @vars = MetaVars.new\n      @file_module = file_module = @program.file_module(node.filename)\n      @meta_vars = file_module.vars\n\n      node.node.accept self\n      node.type = @program.nil_type\n\n      @vars = old_vars\n      @meta_vars = old_meta_vars\n      @file_module = old_file_module\n\n      false\n    end\n\n    def visit(node : Path)\n      lookup_scope = @path_lookup || @scope || @current_type\n\n      # If the lookup scope is a generic type, like Foo(T), we don't\n      # want to find T in main code. For example:\n      #\n      # class Foo(T)\n      #   Bar(T) # This T is unbound and it shouldn't be found in the lookup\n      # end\n      find_root_generic_type_parameters = !lookup_scope.is_a?(GenericType)\n\n      type = lookup_scope.lookup_type_var(node,\n        free_vars: free_vars,\n        find_root_generic_type_parameters: find_root_generic_type_parameters,\n        remove_alias: false)\n\n      case type\n      when Const\n        if !type.value.type? && !type.visited?\n          type.visited = true\n\n          meta_vars = MetaVars.new\n          const_def = Def.new(\"const\", [] of Arg)\n          type_visitor = MainVisitor.new(@program, meta_vars, const_def)\n          type_visitor.current_type = type.namespace\n          type_visitor.inside_constant = true\n          type.value.accept type_visitor\n\n          type.fake_def = const_def\n          type.visitor = self\n          type.used = true\n\n          program.const_initializers << type\n        end\n\n        node.target_const = type\n        node.bind_to type.value\n      when Type\n        # We devirtualize the type because in an expression like\n        #\n        #     T.new\n        #\n        # even if T is a virtual type that resulted from a generic\n        # type argument, creating an instance or invoking methods\n        # on the type itself don't need to resolve virtually.\n        #\n        # It's different if from a virtual type we do `v.class.new`\n        # because the class could be any in the hierarchy.\n        node.type = check_type_in_type_args(type.remove_alias_if_simple).devirtualize\n        node.target_type = type\n      when ASTNode\n        type.accept self unless type.type?\n        node.syntax_replacement = type\n        node.bind_to type\n      end\n\n      false\n    end\n\n    def visit(node : Generic)\n      node.in_type_args = @in_type_args > 0\n      node.inside_is_a = @inside_is_a\n      node.scope = @scope\n\n      node.name.accept self\n\n      @in_type_args += 1\n      node.type_vars.each &.accept self\n      node.named_args.try &.each &.value.accept self\n      @in_type_args -= 1\n\n      return false if node.type?\n\n      name = node.name\n      if name.is_a?(Path) && name.target_const\n        node.raise \"#{name} is not a type, it's a constant\"\n      end\n\n      instance_type = node.name.type.instance_type\n      unless instance_type.is_a?(GenericType)\n        node.raise \"#{instance_type} is not a generic type, it's a #{instance_type.type_desc}\"\n      end\n\n      if instance_type.double_variadic?\n        unless node.type_vars.empty?\n          node.raise \"can only instantiate NamedTuple with named arguments\"\n        end\n      else\n        if node.named_args\n          node.raise \"can only use named arguments with NamedTuple\"\n        end\n\n        # Need to count type vars because there might be splats\n        type_vars_count = 0\n        knows_count = true\n        node.type_vars.each do |type_var|\n          if type_var.is_a?(Splat)\n            if (type_var_type = type_var.type?)\n              unless type_var_type.is_a?(TupleInstanceType)\n                type_var.raise \"argument to splat must be a tuple type, not #{type_var_type}\"\n              end\n\n              type_vars_count += type_var_type.size\n            else\n              knows_count = false\n              break\n            end\n          else\n            type_vars_count += 1\n          end\n        end\n\n        if knows_count\n          if instance_type.splat_index\n            min_needed = instance_type.type_vars.size\n            min_needed -= 1 if instance_type.splat_index\n\n            if type_vars_count < min_needed\n              node.wrong_number_of \"type vars\", instance_type, type_vars_count, \"#{min_needed}+\"\n            end\n          else\n            needed_count = instance_type.type_vars.size\n            if type_vars_count != needed_count\n              node.wrong_number_of \"type vars\", instance_type, type_vars_count, needed_count\n            end\n          end\n        end\n      end\n\n      node.instance_type = instance_type.as(GenericType)\n      node.type_vars.each &.add_observer(node)\n      node.named_args.try &.each &.value.add_observer(node)\n      node.update\n\n      false\n    end\n\n    def visit(node : ProcNotation)\n      types = [] of Type\n      @in_type_args += 1\n\n      node.inputs.try &.each do |input|\n        input.accept self\n        input_type = input.type\n        check_not_a_constant(input)\n        MainVisitor.check_type_allowed_as_proc_argument(input, input_type)\n        types << input_type.virtual_type\n      end\n\n      if output = node.output\n        output.accept self\n        output_type = output.type\n        check_not_a_constant(output)\n        MainVisitor.check_type_allowed_as_proc_argument(output, output_type)\n        types << output_type.virtual_type\n      else\n        types << program.void\n      end\n\n      @in_type_args -= 1\n      node.type = program.proc_of(types)\n\n      false\n    end\n\n    def visit(node : Union)\n      @in_type_args += 1\n      node.types.each &.accept self\n      @in_type_args -= 1\n\n      node.inside_is_a = @inside_is_a\n      node.update\n\n      false\n    end\n\n    def visit(node : Metaclass)\n      node.name.accept self\n      node.type = node.name.type.virtual_type.metaclass\n      false\n    end\n\n    def visit(node : Self)\n      node.type = the_self(node).instance_type\n      false\n    end\n\n    def visit(node : Var)\n      var = @vars[node.name]?\n      if var\n        if var.type?.is_a?(Program) && node.name == \"self\"\n          node.raise \"there's no self in this scope\"\n        end\n\n        meta_var = @meta_vars[node.name]\n        check_closured meta_var\n\n        if var.nil_if_read?\n          # Once we know a variable is nil if read we mark it as nilable\n          var.bind_to(@program.nil_var)\n          var.nil_if_read = false\n\n          meta_var.bind_to(@program.nil_var) unless meta_var.dependencies.any? &.same?(@program.nil_var)\n          node.bind_to(@program.nil_var)\n        end\n\n        check_mutably_closured meta_var, var\n\n        node.bind_to(var)\n\n        if needs_type_filters?\n          @type_filters = TypeFilters.truthy(node)\n        end\n      elsif node.name == \"self\"\n        current_type = current_type()\n        if current_type.is_a?(Program)\n          node.raise \"there's no self in this scope\"\n        else\n          node.type = current_type.metaclass\n        end\n      elsif node.special_var?\n        special_var = define_special_var(node.name, program.nil_var)\n        node.bind_to special_var\n      else\n        node.raise \"read before assignment to local variable '#{node.name}'\"\n      end\n      false\n    end\n\n    def visit(node : TypeDeclaration)\n      case var = node.var\n      when Var\n        if @meta_vars[var.name]?\n          node.raise \"variable '#{var.name}' already declared\"\n        end\n\n        meta_var = new_meta_var(var.name)\n        meta_var.type = @program.no_return\n\n        var.bind_to(meta_var)\n        @meta_vars[var.name] = meta_var\n\n        @in_type_args += 1\n        node.declared_type.accept self\n        @in_type_args -= 1\n\n        check_not_a_constant(node.declared_type)\n\n        if declared_type = node.declared_type.type?\n          var_type = check_declare_var_type node, declared_type, \"a variable\"\n          meta_var.freeze_type = var_type\n        else\n          node.raise \"can't infer type of type declaration\"\n        end\n\n        if value = node.value\n          type_assign(var, value, node)\n\n          node.bind_to value\n        else\n          node.type = @program.nil\n        end\n      when InstanceVar\n        if @untyped_def\n          node.raise \"declaring the type of an instance variable must be done at the class level\"\n        end\n\n        node.type = @program.nil\n      when ClassVar\n        if @untyped_def\n          node.raise \"declaring the type of a class variable must be done at the class level\"\n        end\n\n        thread_local = check_class_var_annotations\n\n        class_var = lookup_class_var(var)\n        var.var = class_var\n        class_var.thread_local = true if thread_local\n\n        node.type = @program.nil\n      else\n        raise \"Bug: unexpected var type: #{var.class}\"\n      end\n\n      false\n    end\n\n    def visit(node : UninitializedVar)\n      case var = node.var\n      when Var\n        if @vars[var.name]?\n          var.raise \"variable '#{var.name}' already declared\"\n        end\n\n        @in_type_args += 1\n        node.declared_type.accept self\n        @in_type_args -= 1\n\n        check_not_a_constant(node.declared_type)\n\n        # TODO: should we be using a binding here to recompute the type?\n        if declared_type = node.declared_type.type?\n          var_type = check_declare_var_type node, declared_type, \"a variable\"\n          var_type = var_type.virtual_type\n          var.type = var_type\n        else\n          node.raise \"can't infer type of type declaration\"\n        end\n\n        meta_var, _ = assign_to_meta_var(var.name)\n        if (existing_type = meta_var.type?) && existing_type != var_type\n          node.raise \"variable '#{var.name}' already declared with type #{existing_type}\"\n        end\n\n        meta_var.bind_to(var)\n        meta_var.freeze_type = var_type\n\n        @vars[var.name] = meta_var\n\n        check_exception_handler_vars(var.name, node)\n\n        node.type = meta_var.type unless meta_var.type.no_return?\n      when InstanceVar\n        type = scope? || current_type\n        if @untyped_def\n          @in_type_args += 1\n          node.declared_type.accept self\n          @in_type_args -= 1\n\n          check_declare_var_type node, node.declared_type.type, \"an instance variable\"\n          ivar = lookup_instance_var(var, type)\n\n          if @is_initialize\n            @vars[var.name] = MetaVar.new(var.name, ivar.type)\n          end\n        else\n          # Already handled in a previous visitor\n          node.type = @program.nil\n          return false\n        end\n\n        case type\n        when NonGenericClassType\n          @in_type_args += 1\n          node.declared_type.accept self\n          @in_type_args -= 1\n          check_declare_var_type node, node.declared_type.type, \"an instance variable\"\n        when GenericClassType\n          # OK\n        when GenericClassInstanceType\n          # OK\n        else\n          node.raise \"can only declare instance variables of a non-generic class, not a #{type.type_desc} (#{type})\"\n        end\n      when ClassVar\n        thread_local = check_class_var_annotations\n\n        class_var = visit_class_var var\n        class_var.thread_local = true if thread_local\n      else\n        raise \"Bug: unexpected var type: #{var.class}\"\n      end\n\n      node.type = @program.nil unless node.type?\n\n      false\n    end\n\n    def check_not_a_constant(node)\n      if node.is_a?(Path) && node.target_const\n        node.raise \"#{node.target_const} is not a type, it's a constant\"\n      end\n    end\n\n    def check_exception_handler_vars(var_name, node)\n      # If inside a begin part of an exception handler, bind this type to\n      # the variable that will be used in the rescue/else blocks.\n      @all_exception_handler_vars.try &.each do |exception_handler_vars|\n        var = (exception_handler_vars[var_name] ||= MetaVar.new(var_name))\n        var.bind_to(node)\n      end\n    end\n\n    def visit(node : Out)\n      case exp = node.exp\n      when Var\n        if @meta_vars.has_key?(exp.name)\n          exp.raise \"variable '#{exp.name}' is already defined, `out` must be used to define a variable, use another name\"\n        end\n\n        # We declare out variables\n        @meta_vars[exp.name] = new_meta_var(exp.name)\n        @vars[exp.name] = new_meta_var(exp.name)\n      when InstanceVar\n        var = lookup_instance_var exp\n        exp.bind_to(var)\n\n        if @is_initialize\n          @vars[exp.name] = MetaVar.new(exp.name)\n        end\n      when Underscore\n        # Nothing to do\n      else\n        node.raise \"BUG: unexpected out exp: #{exp}\"\n      end\n\n      node.bind_to node.exp\n\n      false\n    end\n\n    def visit(node : Global)\n      # Reading from a special global variable is actually\n      # reading from a local variable with that same not,\n      # invoking `not_nil!` on it (because these are usually\n      # accessed after invoking a method that brought them\n      # into the current scope, and it would be annoying\n      # to ask the user to always invoke `not_nil!` on it)\n      case node.name\n      when \"$~\", \"$?\"\n        expanded = Call.new(Var.new(node.name).at(node), \"not_nil!\").at(node)\n        expanded.accept self\n        node.bind_to expanded\n        node.expanded = expanded\n      else\n        node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n      end\n\n      false\n    end\n\n    def undefined_instance_variable(owner, node)\n      similar_name = owner.lookup_similar_instance_var_name(node.name)\n      program.undefined_instance_variable(node, owner, similar_name)\n    end\n\n    def first_time_accessing_meta_type_var?(var)\n      return false if var.uninitialized?\n\n      if var.freeze_type\n        deps = var.dependencies?\n        # If no dependencies, it's the case of a global for a regex literal.\n        # If there are dependencies and it's just one, it's the same var\n        deps ? deps.size == 1 : false\n      else\n        !var.dependencies?\n      end\n    end\n\n    def visit(node : InstanceVar)\n      var = lookup_instance_var node\n      node.bind_to(var)\n\n      if @is_initialize &&\n         @typeof_nest == 0 &&\n         !@vars.has_key?(node.name) &&\n         !scope.has_instance_var_initializer?(node.name)\n        ivar = scope.lookup_instance_var(node.name)\n        ivar.nil_reason ||= NilReason.new(node.name, :used_before_initialized, [node] of ASTNode)\n        ivar.bind_to program.nil_var\n      end\n\n      false\n    end\n\n    def visit(node : ReadInstanceVar)\n      visit_read_instance_var node\n      false\n    end\n\n    def visit_read_instance_var(node)\n      node.obj.accept self\n      node.obj.add_observer node\n      node.update\n    end\n\n    def visit(node : ClassVar)\n      thread_local = check_class_var_annotations\n\n      var = visit_class_var node\n      var.thread_local = true if thread_local\n\n      false\n    end\n\n    def visit_class_var(node)\n      var = lookup_class_var(node)\n      node.bind_to var\n      node.var = var\n      var\n    end\n\n    private def lookup_instance_var(node)\n      lookup_instance_var(node, @scope)\n    end\n\n    private def lookup_instance_var(node, scope)\n      unless scope\n        node.raise \"can't use instance variables at the top level\"\n      end\n\n      ivar = scope.remove_typedef.lookup_instance_var(node)\n      unless ivar\n        undefined_instance_variable(scope, node)\n      end\n\n      check_self_closured\n      ivar\n    end\n\n    def visit(node : Expressions)\n      exp_count = node.expressions.size\n      node.expressions.each_with_index do |exp, i|\n        if i == exp_count - 1\n          exp.accept self\n          node.bind_to exp\n        else\n          ignoring_type_filters { exp.accept self }\n        end\n      end\n\n      if node.empty?\n        node.set_type(@program.nil)\n      end\n\n      false\n    end\n\n    def visit(node : Assign)\n      type_assign node.target, node.value, node\n\n      if @is_initialize && !@found_self_in_initialize_call\n        value = node.value\n        if value.is_a?(Var) && value.name == \"self\"\n          @found_self_in_initialize_call = [value] of ASTNode\n        end\n      end\n      false\n    end\n\n    def type_assign(target : Var, value, node, restriction = nil)\n      value.accept self\n\n      var_name = target.name\n      meta_var, meta_var_existed = assign_to_meta_var(var_name)\n\n      freeze_type = meta_var.freeze_type\n\n      if freeze_type\n        if casted_value = check_automatic_cast(value, freeze_type, node)\n          value = casted_value\n        end\n      end\n\n      # If this assign comes from a AssignWithRestriction node, check the restriction\n\n      if restriction && (value_type = value.type?)\n        if value_type.restrict(restriction, match_context.not_nil!)\n          # OK\n        else\n          # Check autocast too\n          restriction_type = (path_lookup || scope).lookup_type?(restriction, free_vars: free_vars)\n          if casted_value = check_automatic_cast(value, restriction_type, node)\n            value = casted_value\n          else\n            if value.is_a?(SymbolLiteral) && restriction_type.is_a?(EnumType)\n              node.raise \"can't autocast #{value} to #{restriction_type}: no matching enum member\"\n            end\n\n            node.raise \"can't restrict #{value.type} to #{restriction}\"\n          end\n        end\n      end\n\n      target.bind_to value\n      node.bind_to value\n\n      value_type_filters = @type_filters\n      @type_filters = nil\n\n      # Save variable assignment location for debugging output\n      meta_var.location ||= target.location\n\n      begin\n        meta_var.bind_to value\n      rescue ex : FrozenTypeException\n        target.raise ex.message\n      end\n\n      meta_var.assigned_to = true\n      check_closured meta_var, mark_as_mutably_closured: meta_var_existed\n\n      simple_var = MetaVar.new(var_name)\n\n      # When we assign to a local variable with a fixed type, and it's\n      # a Proc, we always want to keep that proc's type.\n      if freeze_type && freeze_type.is_a?(ProcInstanceType)\n        simple_var.bind_to(meta_var)\n      else\n        simple_var.bind_to(target)\n\n        check_mutably_closured(meta_var, simple_var)\n      end\n\n      @vars[var_name] = simple_var\n\n      check_exception_handler_vars var_name, value\n\n      if needs_type_filters?\n        @type_filters = TypeFilters.assign_var(value_type_filters, target)\n      end\n\n      if target.special_var?\n        if typed_def = @typed_def\n          typed_def.add_special_var(target.name)\n\n          # Always bind with a special var with nil, so it's easier to assign it later\n          # in the codegen (just store the whole value through a pointer)\n          simple_var.bind_to(@program.nil_var)\n          meta_var.bind_to(@program.nil_var)\n\n          # If we are in a call's block, define the special var in the block\n          if (call = @call) && call.block\n            call.parent_visitor.define_special_var(target.name, value)\n          end\n        else\n          node.raise \"'#{var_name}' can't be assigned at the top level\"\n        end\n      end\n    end\n\n    def type_assign(target : InstanceVar, value, node)\n      # Check if this is an instance variable initializer\n      unless @scope\n        # `InstanceVar` assignment appeared in block is not checked\n        # by `Crystal::InstanceVarsInitializerVisitor` because this block\n        # may be passed to a macro. So, it checks here.\n        if current_type.is_a?(Program) || current_type.is_a?(FileModule)\n          node.raise \"can't use instance variables at the top level\"\n        end\n\n        # Already handled by InstanceVarsInitializerVisitor\n        return\n      end\n\n      value.accept self\n\n      var = lookup_instance_var target\n      if casted_value = check_automatic_cast(value, var.type, node)\n        value = casted_value\n      end\n\n      target.bind_to var\n      node.bind_to value\n\n      begin\n        var.bind_to value\n      rescue ex : FrozenTypeException\n        target.raise ex.message\n      end\n\n      if @is_initialize\n        var_name = target.name\n\n        # Don't track instance variables nilability (for example, if they were\n        # just assigned inside a branch) if they have an initializer\n        unless scope.has_instance_var_initializer?(var_name)\n          meta_var, _ = assign_to_meta_var(var_name)\n          meta_var.bind_to value\n          meta_var.assigned_to = true\n\n          simple_var = MetaVar.new(var_name)\n          simple_var.bind_to(target)\n        end\n\n        # Check if an instance variable is being assigned (for the first time)\n        # and self, or that same instance variable, was used (read) before that.\n        unless @vars.has_key?(var_name) || scope.has_instance_var_initializer?(var_name)\n          if (found_self = @found_self_in_initialize_call) ||\n             (used_ivars_node = @used_ivars_in_calls_in_initialize.try(&.[var_name]?)) ||\n             (@block_nest > 0)\n            ivar = scope.lookup_instance_var(var_name)\n            if found_self\n              ivar.nil_reason = NilReason.new(var_name, :used_self_before_initialized, found_self)\n            else\n              ivar.nil_reason = NilReason.new(var_name, :used_before_initialized, used_ivars_node)\n            end\n            ivar.bind_to program.nil_var\n          end\n        end\n\n        if simple_var\n          @vars[var_name] = simple_var\n\n          check_exception_handler_vars var_name, value\n        end\n      end\n    end\n\n    def type_assign(target : Path, value, node)\n      target.bind_to value\n      node.type = @program.nil\n      false\n    end\n\n    def type_assign(target : Global, value, node)\n      node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n    end\n\n    def type_assign(target : ClassVar, value, node)\n      thread_local = check_class_var_annotations\n\n      # Outside a def is already handled by ClassVarsInitializerVisitor\n      # (@exp_nest is 1 if we are at the top level because it was incremented\n      # by one since we are inside an Assign)\n      if !@typed_def && (@exp_nest <= 1) && !inside_block?\n        var = lookup_class_var(target)\n        target.var = var\n        var.thread_local = true if thread_local\n        return\n      end\n\n      value.accept self\n\n      var = lookup_class_var(target)\n      target.var = var\n      var.thread_local = true if thread_local\n\n      if casted_value = check_automatic_cast(value, var.type, node)\n        value = casted_value\n      end\n\n      target.bind_to var\n\n      node.bind_to value\n      var.bind_to value\n    end\n\n    def type_assign(target : Underscore, value, node)\n      value.accept self\n      node.bind_to value\n    end\n\n    def type_assign(target, value, node)\n      raise \"BUG: unknown assign target in MainVisitor: #{target}\"\n    end\n\n    # See if we can automatically cast the value if the types don't exactly match\n    def check_automatic_cast(value, var_type, assign = nil)\n      MainVisitor.check_automatic_cast(@program, value, var_type, assign)\n    end\n\n    def self.check_automatic_cast(program, value, var_type, assign = nil)\n      if value.is_a?(NumberLiteral) && value.type != var_type\n        literal_type = NumberAutocastType.new(program, value)\n        restricted = literal_type.restrict(var_type, MatchContext.new(value.type, value.type))\n        if restricted.is_a?(IntegerType) || restricted.is_a?(FloatType)\n          value.type = restricted\n          value.kind = restricted.kind\n          assign.value = value if assign\n          return value\n        end\n      elsif value.is_a?(SymbolLiteral) && value.type != var_type\n        literal_type = SymbolAutocastType.new(program, value)\n        restricted = literal_type.restrict(var_type, MatchContext.new(value.type, value.type))\n        if restricted.is_a?(EnumType)\n          member = restricted.find_member(value.value).not_nil!\n          path = Path.new(member.name)\n          path.target_const = member\n          path.type = restricted\n          value = path\n          assign.value = value if assign\n          return value\n        end\n      end\n\n      nil\n    end\n\n    def visit(node : Yield)\n      call = @call\n      unless call\n        node.raise \"can't use `yield` outside a method\"\n      end\n\n      if @fun_literal_context\n        node.raise <<-MSG\n          can't use `yield` inside a proc literal or captured block\n\n          Make sure to read the whole docs section about blocks and procs,\n          including \"Capturing blocks\" and \"Block forwarding\":\n\n          https://crystal-lang.org/reference/syntax_and_semantics/blocks_and_procs.html\n          MSG\n      end\n\n      block = call.block || node.raise(\"no block given\")\n\n      # This is the case of a yield when there's a captured block\n      if block.fun_literal\n        block_arg_name = typed_def.block_arg.not_nil!.name\n        block_var = Var.new(block_arg_name).at(node)\n        call = Call.new(block_var, \"call\", node.exps).at(node)\n        call.accept self\n        node.bind_to call\n        node.expanded = call\n        return false\n      end\n\n      node.scope.try &.accept self\n      node.exps.each &.accept self\n\n      # We use a binder to support splats and other complex forms\n      binder = block.binder ||= YieldBlockBinder.new(@program, block)\n      binder.add_yield(node, @yield_vars)\n      binder.update\n\n      unless block.visited?\n        # When we yield, we are no longer inside `untyped_def`, so we un-nest\n        untyped_def = @untyped_def\n        untyped_def.block_nest -= 1 if untyped_def\n\n        call.bubbling_exception do\n          if node_scope = node.scope\n            block.scope = node_scope.type\n          end\n          ignoring_type_filters do\n            block.accept call.parent_visitor.not_nil!\n          end\n        end\n\n        # And now we are back inside `untyped_def`\n        untyped_def.block_nest += 1 if untyped_def\n      end\n\n      node.bind_to block\n\n      @type_filters = nil\n      false\n    end\n\n    def visit(node : Block)\n      return false if node.visited?\n\n      node.visited = true\n      node.context = current_non_block_context\n\n      before_block_vars = node.vars.try(&.dup) || MetaVars.new\n\n      # Variables that we don't want to get their type merged\n      # with local variables before the block occurrence:\n      # mainly block arguments (locally override vars), but\n      # also block arguments that result from tuple unpacking\n      # that the parser currently generated as local assignments.\n      ignored_vars_after_block = nil\n\n      meta_vars = @meta_vars.dup\n\n      node.args.each do |arg|\n        bind_block_var(node, arg, meta_vars, before_block_vars)\n      end\n\n      # If the block has unpacking, like:\n      #\n      #     do |(x, y)|\n      #       ...\n      #     end\n      #\n      # it was transformed to unpack the block vars inside the body:\n      #\n      #     do |__temp_1|\n      #       x, y = __temp_1\n      #       ...\n      #     end\n      #\n      # We need to treat these variables as block arguments (so they don't override existing local variables).\n      if unpacks = node.unpacks\n        ignored_vars_after_block = node.args.dup\n        unpacks.each_value do |unpack|\n          handle_unpacked_block_argument(node, unpack, meta_vars, before_block_vars, ignored_vars_after_block)\n        end\n      end\n\n      @block_nest += 1\n\n      block_visitor = MainVisitor.new(program, before_block_vars, @typed_def, meta_vars)\n      block_visitor.yield_vars = @yield_vars\n      block_visitor.match_context = @match_context\n      block_visitor.untyped_def = @untyped_def\n      block_visitor.call = @call\n      block_visitor.fun_literal_context = @fun_literal_context\n      block_visitor.parent = self\n      block_visitor.with_scope = node.scope || with_scope\n      block_visitor.all_exception_handler_vars = @all_exception_handler_vars\n      block_visitor.file_module = @file_module\n\n      block_scope = @scope\n      block_scope ||= current_type.metaclass unless current_type.is_a?(Program)\n\n      block_visitor.scope = block_scope\n\n      block_visitor.block = node\n      block_visitor.path_lookup = path_lookup || current_type\n      block_visitor.block_nest = @block_nest\n\n      block_visitor.last_block_kind = :block\n      block_visitor.inside_ensure = inside_ensure?\n\n      node.body.accept block_visitor\n\n      @block_nest -= 1\n\n      # Check re-assigned variables and bind them.\n      ignored_vars_after_block ||= node.args\n      bind_vars block_visitor.vars, node.vars, ignored_vars_after_block\n      bind_vars block_visitor.vars, node.after_vars, ignored_vars_after_block\n\n      # Special vars, even if only assigned inside a block,\n      # must be inside the def's metavars.\n      meta_vars.each do |name, var|\n        if var.special_var?\n          define_special_var(name, var)\n        end\n      end\n\n      node.vars = meta_vars\n\n      node.bind_to node.body\n\n      false\n    end\n\n    def handle_unpacked_block_argument(node, arg, meta_vars, before_block_vars, ignored_vars_after_block)\n      case arg\n      when Var\n        bind_block_var(node, arg, meta_vars, before_block_vars)\n        ignored_vars_after_block << Var.new(arg.name)\n      when Underscore\n        # Nothing\n      when Splat\n        handle_unpacked_block_argument(node, arg.exp, meta_vars, before_block_vars, ignored_vars_after_block)\n      when Expressions\n        arg.expressions.each do |exp|\n          handle_unpacked_block_argument(node, exp, meta_vars, before_block_vars, ignored_vars_after_block)\n        end\n      end\n    end\n\n    def bind_block_var(node, target, meta_vars, before_block_vars)\n      meta_var = new_meta_var(target.name, context: node)\n      meta_var.bind_to(target)\n      meta_vars[target.name] = meta_var\n\n      before_block_var = new_meta_var(target.name, context: node)\n      before_block_var.bind_to(target)\n      before_block_vars[target.name] = before_block_var\n    end\n\n    def bind_vars(from_vars, to_vars, ignored = nil)\n      if to_vars\n        from_vars.each do |name, block_var|\n          unless ignored.try &.find { |arg| arg.name == name }\n            to_var = to_vars[name]?\n            if to_var && !to_var.same?(block_var)\n              to_var.try &.bind_to(block_var)\n            end\n          end\n        end\n      end\n    end\n\n    def visit(node : ProcLiteral)\n      return false if node.type?\n\n      fun_vars = @vars.dup\n      meta_vars = @meta_vars.dup\n\n      node.def.args.each do |arg|\n        # It can happen that the argument has a type already,\n        # when converting a block to a proc literal\n        if restriction = arg.restriction\n          @in_type_args += 1\n          restriction.accept self\n          @in_type_args -= 1\n          arg_type = restriction.type\n          MainVisitor.check_type_allowed_as_proc_argument(node, arg_type)\n          arg.type = arg_type.virtual_type\n        elsif !arg.type?\n          arg.raise \"parameter '#{arg.name}' of Proc literal must have a type\"\n        end\n\n        fun_var = MetaVar.new(arg.name, arg.type)\n        fun_vars[arg.name] = fun_var\n\n        meta_var = new_meta_var(arg.name, context: node.def)\n        meta_var.bind_to fun_var\n        meta_vars[arg.name] = meta_var\n      end\n\n      if return_type = node.def.return_type\n        @in_type_args += 1\n        return_type.accept self\n        @in_type_args -= 1\n        check_not_a_constant(return_type)\n\n        def_type = return_type.type\n        MainVisitor.check_type_allowed_as_proc_argument(node, def_type)\n        node.expected_return_type = def_type.virtual_type\n      end\n\n      node.bind_to node.def\n      node.def.bind_to node.def.body\n      node.def.vars = meta_vars\n\n      block_visitor = MainVisitor.new(program, fun_vars, node.def, meta_vars)\n      block_visitor.current_type = current_type\n      block_visitor.yield_vars = @yield_vars\n      block_visitor.match_context = @match_context\n      block_visitor.untyped_def = node.def\n      block_visitor.call = @call\n      block_visitor.scope = @scope\n      block_visitor.path_lookup = path_lookup\n      block_visitor.fun_literal_context = @fun_literal_context || @typed_def || @program\n      block_visitor.block_nest = @block_nest + 1\n      block_visitor.parent = self\n      block_visitor.is_initialize = @is_initialize\n\n      node.def.body.accept block_visitor\n\n      false\n    end\n\n    def self.check_type_allowed_as_proc_argument(node, type)\n      unless type.can_be_stored?\n        node.raise \"can't use #{type.to_s(generic_args: false)} as a Proc argument type yet, use a more specific type\"\n      end\n    end\n\n    def visit(node : ProcPointer)\n      obj = node.obj\n\n      if obj\n        obj.accept self\n      end\n\n      # The call might have been created if this is a proc pointer at the top-level\n      call = node.call? || Call.new(obj, node.name).at(obj)\n      prepare_call(call)\n\n      # A proc pointer like `->foo` where `foo` is a macro is invalid\n      if expand_macro(call)\n        node.raise(String.build do |io|\n          io << \"undefined method '#{node.name}'\"\n          (io << \" for \" << obj.type) if obj\n          io << \"\\n\\n'\" << node.name << \"' exists as a macro, but macros can't be used in proc pointers\"\n        end)\n      end\n\n      # If it's something like `->foo.bar` we turn it into a closure\n      # where `foo` is assigned to a temporary variable.\n      # If it's something like `->foo` then we also turn it into a closure\n      # because it could be doing a mutlidispatch and that's not supported in ProcPointer.\n      if !obj || obj.is_a?(Var) || obj.is_a?(InstanceVar) || obj.is_a?(ClassVar)\n        expand(node)\n        return false\n      end\n\n      # If it's something like `->Foo.bar` and `Foo` is not a lib type,\n      # it could also be producing a multidispatch so we rewrite that too\n      # (lib types can never produce a mutlidispatch and in that case we can\n      # actually generate a function pointer that points right into the C fun).\n      if obj.is_a?(Path) && !obj.type.is_a?(LibType)\n        expand(node)\n        return false\n      end\n\n      # Check if it's ->LibFoo.foo, so we deduce the type from that method\n      if obj.type.is_a?(LibType)\n        matching_fun = obj.type.lookup_first_def(node.name, false).as(External?)\n        node.raise \"undefined fun '#{node.name}' for #{obj.type}\" unless matching_fun\n\n        if !node.has_any_args?\n          call.args = matching_fun.args.map_with_index do |arg, i|\n            Var.new(\"arg#{i}\", arg.type).as(ASTNode)\n          end\n        else\n          # Variadic funs are always expanded according to input types, due to\n          # different ABIs.\n          if matching_fun.varargs?\n            expand(node)\n            return false\n          end\n\n          # If it's something like `->LibFoo.foo(Bar)`, check that the supplied\n          # parameter types are compatible with the C fun.\n          # This is partially based on `Call#check_fun_arg_type_matches`\n          unless node.args.size == matching_fun.args.size\n            call.wrong_number_of_arguments \"'#{call.full_name(obj.type)}'\", node.args.size, matching_fun.args.size\n          end\n\n          node.args.each_with_index do |node_arg, i|\n            node_arg.accept self\n            node_arg.type = node_arg_type = node_arg.type.instance_type\n            fun_arg_type = matching_fun.args[i].type\n            unless node_arg_type.compatible_with?(fun_arg_type) || node_arg_type.implicitly_converted_in_c_to?(fun_arg_type)\n              # Incompatible parameter type found; expand the pointer just\n              # like for non-lib types. This works for non-extern types as\n              # well; `->LibC.free(Bytes)` will compile, and\n              # `->LibC.getenv(Array(Int32))` will emit the same compiler\n              # error as a direct fun call.\n              expand(node)\n              return false\n            end\n          end\n\n          # If all parameter types are compatible, no proc literal is formed.\n          # This implies `->LibC.free` and `->LibC.free(Void*)` have exactly the\n          # same function pointer. So does `->LibC.free(UInt8*)`, although the\n          # proc type will be different here.\n          call.args = node.args.map_with_index do |arg, i|\n            Var.new(\"arg#{i}\", arg.type).as(ASTNode)\n          end\n        end\n      else\n        call.args = node.args.map_with_index do |arg, i|\n          arg.accept self\n          arg_type = arg.type\n          MainVisitor.check_type_allowed_as_proc_argument(node, arg_type)\n          Var.new(\"arg#{i}\", arg_type.virtual_type).as(ASTNode)\n        end\n      end\n\n      begin\n        call.recalculate\n      rescue ex : Crystal::CodeError\n        node.raise \"error instantiating #{node}\", ex\n      end\n\n      node.call = call\n      node.bind_to call\n\n      false\n    end\n\n    def visit(node : Call)\n      prepare_call(node)\n\n      if expand_macro(node)\n        # It can happen that this call is inside an ArrayLiteral or HashLiteral,\n        # was expanded but isn't bound to the expansion because the call (together\n        # with its expansion) was cloned.\n        if (expanded = node.expanded) && (node.dependencies.empty? || !node.type?)\n          node.bind_to(expanded)\n        end\n\n        return false\n      end\n\n      # If the call has splats or double splats, and any of them are\n      # not variables, instance variables or global variables,\n      # we replace this call with a separate one that declares temporary\n      # variables with this splat expressions, so we don't evaluate them\n      # twice (#2677)\n      if call_needs_splat_expansion?(node)\n        return replace_call_splats(node)\n      end\n\n      obj = node.obj\n      args = node.args\n      block_arg = node.block_arg\n      named_args = node.named_args\n\n      ignoring_type_filters do\n        if obj\n          obj.accept(self)\n\n          check_lib_call node, obj.type?\n\n          if check_special_new_call(node, obj.type?)\n            return false\n          end\n\n          if check_slice_literal_call(node, obj.type?)\n            return false\n          end\n        end\n\n        args.each &.accept(self)\n        block_arg.try &.accept self\n        named_args.try &.each &.value.accept self\n      end\n\n      obj.try &.set_enclosing_call(node)\n      args.each &.set_enclosing_call(node)\n      block_arg.try &.set_enclosing_call node\n      named_args.try &.each &.value.set_enclosing_call(node)\n\n      check_super_or_previous_def_in_initialize node\n\n      # If the call has a block we need to create a copy of the variables\n      # and bind them to the current variables. Then, when visiting\n      # the block we will bind more variables to these ones if variables\n      # are reassigned.\n      if node.block || block_arg\n        before_vars = MetaVars.new\n        after_vars = MetaVars.new\n\n        @vars.each do |name, var|\n          before_var = MetaVar.new(name)\n          before_var.bind_to(var)\n          before_var.nil_if_read = var.nil_if_read?\n          before_vars[name] = before_var\n\n          after_var = MetaVar.new(name)\n          after_var.bind_to(var)\n          after_var.nil_if_read = var.nil_if_read?\n          after_vars[name] = after_var\n          @vars[name] = after_var\n        end\n\n        if block = node.block\n          block.vars = before_vars\n          block.after_vars = after_vars\n        else\n          node.before_vars = before_vars\n        end\n      end\n\n      recalculate_call(node)\n\n      check_call_in_initialize node\n\n      @type_filters = nil\n      @unreachable = true if node.no_returns?\n\n      false\n    end\n\n    def prepare_call(node)\n      if node.global?\n        node.scope = @program\n      else\n        node.scope = @scope || current_type.metaclass\n      end\n      node.with_scope = with_scope unless node.obj\n      node.parent_visitor = self\n    end\n\n    def recalculate_call(node : Call)\n      node.recalculate\n    end\n\n    def call_needs_splat_expansion?(node)\n      node.args.each do |arg|\n        case arg\n        when Splat\n          exp = arg.exp\n        when DoubleSplat\n          exp = arg.exp\n        else\n          next\n        end\n\n        case exp\n        when Var, InstanceVar, ClassVar, Global\n          next\n        else\n          return true\n        end\n      end\n\n      false\n    end\n\n    def replace_call_splats(node)\n      expanded = node.clone\n\n      exps = [] of ASTNode\n      expanded.args.each do |arg|\n        case arg\n        when Splat\n          exp = arg.exp\n        when DoubleSplat\n          exp = arg.exp\n        else\n          next\n        end\n\n        case exp\n        when Var, InstanceVar, ClassVar, Global\n          next\n        end\n\n        temp_var = @program.new_temp_var(arg).at(arg)\n        assign = Assign.new(temp_var, exp).at(arg)\n        exps << assign\n        case arg\n        when Splat\n          arg.exp = temp_var.clone.at(arg)\n        when DoubleSplat\n          arg.exp = temp_var.clone.at(arg)\n        else\n          next\n        end\n      end\n\n      exps << expanded\n      expansion = Expressions.from(exps).at(expanded)\n      expansion.accept self\n      node.expanded = expansion\n      node.bind_to(expanded)\n\n      false\n    end\n\n    # If it's a super or previous_def call inside an initialize we treat\n    # set instance vars from superclasses to not-nil.\n    def check_super_or_previous_def_in_initialize(node)\n      if @is_initialize && (node.super? || node.previous_def?)\n        all_vars = scope.all_instance_vars.keys\n        all_vars -= scope.instance_vars.keys if node.super?\n        all_vars.each do |name|\n          instance_var = scope.lookup_instance_var(name)\n\n          # If a variable was used before this supercall, it becomes nilable\n          if @used_ivars_in_calls_in_initialize.try &.has_key?(name)\n            instance_var.nil_reason ||= NilReason.new(name, :used_before_initialized, [node] of ASTNode)\n            instance_var.bind_to @program.nil_var\n          else\n            # Otherwise, declare it as a \"local\" variable\n            meta_var = MetaVar.new(name)\n            meta_var.bind_to instance_var\n            @vars[name] = meta_var\n          end\n        end\n      end\n    end\n\n    # Checks if it's a call to self. In that case, all instance variables\n    # not mentioned so far will be considered nil.\n    def check_call_in_initialize(node)\n      return unless @is_initialize\n      return if @typeof_nest > 0\n\n      node_obj = node.obj\n      if !node_obj || (node_obj.is_a?(Var) && node_obj.name == \"self\")\n        # Special case: when calling self.class a class method will be invoked\n        # and there's no possibility of accessing instance vars, so we ignore this case.\n        if node.name == \"class\" && node.args.empty?\n          return\n        end\n\n        ivars, found_self = gather_instance_vars_read node\n        if found_self\n          @found_self_in_initialize_call ||= found_self\n        elsif ivars\n          used_ivars_in_calls_in_initialize = @used_ivars_in_calls_in_initialize\n          if used_ivars_in_calls_in_initialize\n            @used_ivars_in_calls_in_initialize = used_ivars_in_calls_in_initialize.merge(ivars)\n          else\n            @used_ivars_in_calls_in_initialize = ivars\n          end\n        end\n      else\n        # Check if any argument is \"self\"\n        unless @found_self_in_initialize_call\n          node.args.each do |arg|\n            if arg.is_a?(Var) && arg.name == \"self\"\n              @found_self_in_initialize_call = [node] of ASTNode\n              return\n            end\n          end\n        end\n      end\n    end\n\n    # Fill function literal argument types for C functions\n    def check_lib_call(node, obj_type)\n      return unless obj_type.is_a?(LibType)\n\n      # Error quickly if we can't find a fun\n      method = obj_type.lookup_first_def(node.name, false)\n      node.raise \"undefined fun '#{node.name}' for #{obj_type}\" unless method\n\n      node.args.each_with_index do |arg, index|\n        case arg\n        when ProcLiteral\n          next unless arg.def.args.any? { |def_arg| !def_arg.restriction && !def_arg.type? }\n\n          check_lib_call_arg(method, index) do |method_arg_type|\n            arg.def.args.each_with_index do |def_arg, def_arg_index|\n              if !def_arg.restriction && !def_arg.type?\n                def_arg.type = method_arg_type.arg_types[def_arg_index]?\n              end\n            end\n          end\n        when ProcPointer\n          next if arg.has_any_args?\n\n          check_lib_call_arg(method, index) do |method_arg_type|\n            method_arg_type.arg_types.each do |arg_type|\n              arg.args.push TypeNode.new(arg_type)\n            end\n          end\n        else\n          # keep checking\n        end\n      end\n    end\n\n    def check_lib_call_arg(method, arg_index, &)\n      method_arg = method.args[arg_index]?\n      return unless method_arg\n\n      method_arg_type = method_arg.type\n      return unless method_arg_type.is_a?(ProcInstanceType)\n\n      yield method_arg_type\n    end\n\n    # Checks if it's ProcType#new\n    def check_special_new_call(node, obj_type)\n      return false unless obj_type\n      return false unless obj_type.metaclass?\n\n      instance_type = obj_type.instance_type.remove_typedef\n\n      if node.name == \"new\"\n        case instance_type\n        when .extern?\n          if instance_type.namespace.is_a?(LibType) && (named_args = node.named_args)\n            return special_c_struct_or_union_new_with_named_args(node, instance_type, named_args)\n          end\n        end\n      end\n\n      false\n    end\n\n    def check_slice_literal_call(node, obj_type)\n      return false unless obj_type\n      return false unless obj_type.metaclass?\n      return false unless node.name == \"literal\"\n\n      instance_type = obj_type.instance_type.remove_typedef\n\n      case instance_type\n      when GenericClassType # Slice\n        return false unless instance_type == @program.slice\n\n        element_type = nil\n\n        node.args.each do |arg|\n          arg.raise \"Expected NumberLiteral, got #{arg.class_desc}\" unless arg.is_a?(NumberLiteral)\n          arg.accept self\n          element_type ||= arg.type\n          unless element_type == arg.type\n            arg.raise \"Too many element types for slice literal without generic argument: #{element_type}, #{arg.type}\"\n          end\n        end\n\n        unless element_type\n          node.raise \"Cannot create empty slice literal without element type\"\n        end\n      when GenericClassInstanceType # Slice(T)\n        return false unless instance_type.generic_type == @program.slice\n\n        element_type = instance_type.type_vars[\"T\"].type\n\n        node.args.each do |arg|\n          arg.raise \"Expected NumberLiteral, got #{arg.class_desc}\" unless arg.is_a?(NumberLiteral)\n          arg.accept self\n          arg.raise \"Argument out of range for a Slice(#{element_type})\" unless arg.representable_in?(element_type)\n        end\n      else\n        return false\n      end\n\n      kind =\n        case element_type\n        when IntegerType\n          element_type.kind\n        when FloatType\n          element_type.kind\n        else\n          node.raise \"Only slice literals of primitive integer or float types can be created\"\n        end\n\n      # create the internal constant `$Slice:n` to hold the slice contents\n      const_name = \"$Slice:#{@program.const_slices.size}\"\n      const_value = Nop.new\n      const_value.type = @program.static_array_of(element_type, node.args.size)\n      const = Const.new(@program, @program, const_name, const_value)\n      @program.types[const_name] = const\n      @program.const_slices[const_name] = Program::ConstSliceInfo.new(const_name, kind, node.args)\n\n      # ::Slice.new(pointerof($Slice:n.@buffer), {{ args.size }}, read_only: true)\n      pointer_node = PointerOf.new(ReadInstanceVar.new(Path.new(const_name).at(node), \"@buffer\").at(node)).at(node)\n      size_node = NumberLiteral.new(node.args.size.to_s, :i32).at(node)\n      read_only_node = NamedArgument.new(\"read_only\", BoolLiteral.new(true).at(node)).at(node)\n      expanded = Call.new(Path.global(\"Slice\").at(node), \"new\", [pointer_node, size_node], named_args: [read_only_node]).at(node)\n\n      expanded.accept self\n      node.bind_to expanded\n      node.expanded = expanded\n\n      true\n    end\n\n    # Rewrite:\n    #\n    #     LibFoo::Struct.new arg0: value0, argN: value0\n    #\n    # To:\n    #\n    #   temp = LibFoo::Struct.new\n    #   temp.arg0 = value0\n    #   temp.argN = valueN\n    #   temp\n    def special_c_struct_or_union_new_with_named_args(node, type, named_args)\n      exps = [] of ASTNode\n\n      temp_name = @program.new_temp_var_name\n\n      new_call = Call.new(node.obj, \"new\").at(node)\n\n      new_assign = Assign.new(Var.new(temp_name).at(node), new_call).at(node)\n      exps << new_assign\n\n      named_args.each do |named_arg|\n        assign_call = Call.new(Var.new(temp_name).at(named_arg), \"#{named_arg.name}=\", named_arg.value).at(named_arg)\n        if loc = named_arg.location\n          assign_call.location = loc\n          assign_call.name_location = loc\n        end\n        exps << assign_call\n      end\n\n      exps << Var.new(temp_name).at(node)\n\n      expanded = Expressions.new(exps).at(node)\n      expanded.accept self\n\n      node.bind_to expanded\n      node.expanded = expanded\n\n      true\n    end\n\n    class InstanceVarsCollector < Visitor\n      getter ivars : Hash(String, Array(ASTNode))?\n      getter found_self : Array(ASTNode)?\n      @scope : Type\n      @in_super : Int32\n      @callstack : Array(ASTNode)\n      @visited : Set(Def)?\n      @vars : MetaVars\n\n      def initialize(a_def, @scope, @vars)\n        @found_self = nil\n        @in_super = 0\n        @callstack = [a_def] of ASTNode\n      end\n\n      def node_in_callstack(node)\n        nodes = [] of ASTNode\n        nodes.concat @callstack\n        nodes.push node\n        nodes\n      end\n\n      def visit(node : InstanceVar)\n        unless @vars.has_key?(node.name)\n          ivars = @ivars ||= Hash(String, Array(ASTNode)).new\n          unless ivars.has_key?(node.name)\n            ivars[node.name] = node_in_callstack(node)\n          end\n        end\n        false\n      end\n\n      def visit(node : Var)\n        if @in_super == 0 && node.name == \"self\"\n          @found_self = node_in_callstack(node)\n        end\n        false\n      end\n\n      def visit(node : Assign)\n        node.value.accept self\n        false\n      end\n\n      def visit(node : Call)\n        obj = node.obj\n\n        # Skip class method\n        if obj.is_a?(Var) && obj.name == \"self\" && node.name == \"class\" && node.args.empty?\n          return false\n        end\n\n        if obj && !(obj.is_a?(Var) && obj.name == \"self\")\n          # not a self-instance method: only verify arguments\n          return true\n        end\n\n        visited = @visited\n\n        node.target_defs.try &.each do |target_def|\n          if target_def.owner == @scope\n            next if visited.try &.includes?(target_def)\n\n            visited = @visited ||= Set(Def).new.compare_by_identity\n            visited << target_def\n\n            @callstack.push(node)\n            target_def.body.accept self\n            @callstack.pop\n          end\n        end\n\n        if node.super?\n          @in_super += 1\n        end\n\n        true\n      end\n\n      def end_visit(node : Call)\n        if node.super?\n          @in_super -= 1\n        end\n      end\n\n      def visit(node : ASTNode)\n        true\n      end\n    end\n\n    def gather_instance_vars_read(node)\n      collector = InstanceVarsCollector.new(typed_def, scope, @vars)\n      node.accept collector\n      {collector.ivars, collector.found_self}\n    end\n\n    def visit(node : Return)\n      if inside_ensure?\n        node.raise \"can't return from ensure\"\n      end\n\n      if inside_constant?\n        node.raise \"can't return from constant\"\n      end\n\n      typed_def = @typed_def || node.raise(\"can't return from top level\")\n\n      if typed_def.captured_block?\n        node.raise \"can't return from captured block, use next\"\n      end\n\n      node.exp.try &.accept self\n\n      node.target = typed_def\n\n      typed_def.bind_to(node_exp_or_nil_literal(node))\n\n      @unreachable = true\n\n      node.type = @program.no_return\n\n      false\n    end\n\n    def end_visit(node : Splat)\n      node.bind_to node.exp\n    end\n\n    def end_visit(node : DoubleSplat)\n      node.bind_to node.exp\n    end\n\n    def visit(node : Underscore)\n      if @in_type_args == 0\n        node.raise \"can't read from _\"\n      else\n        node.raise \"can't use underscore as generic type argument\"\n      end\n    end\n\n    def visit(node : IsA)\n      node.obj.accept self\n\n      @in_type_args += 1\n      @inside_is_a = true\n      node.const.accept self\n      @inside_is_a = false\n      @in_type_args -= 1\n\n      node.type = program.bool\n      const = node.const\n\n      # When doing x.is_a?(A) and A turns out to be a constant (not a type),\n      # replace it with a === comparison. Most usually this happens in a case expression.\n      if const.is_a?(Path) && const.target_const\n        obj = node.obj.clone.at(node.obj)\n        const = node.const.clone.at(node.const)\n        comp = Call.new(const, \"===\", obj).at(node)\n        comp.accept self\n        node.syntax_replacement = comp\n        node.bind_to comp\n        return false\n      end\n\n      if needs_type_filters? && (var = get_expression_var(node.obj))\n        @type_filters = TypeFilters.new var, SimpleTypeFilter.new(node.const.type)\n      end\n\n      false\n    end\n\n    def end_visit(node : RespondsTo)\n      node.type = program.bool\n      if needs_type_filters? && (var = get_expression_var(node.obj))\n        @type_filters = TypeFilters.new var, RespondsToTypeFilter.new(node.name)\n      end\n    end\n\n    # Get the variable of an expression.\n    # If it's a variable, it's that variable.\n    # If it's an assignment to a variable, it's that variable.\n    def get_expression_var(exp)\n      case exp\n      when Var\n        return exp\n      when Assign\n        target = exp.target\n        return target if target.is_a?(Var)\n      when Expressions\n        return unless exp = exp.single_expression?\n        return get_expression_var(exp)\n      end\n      nil\n    end\n\n    def visit(node : Cast | NilableCast)\n      # If there's an `x.as(T)` inside a method, that method\n      # has a chance to raise, so we must mark it as such\n      if typed_def = @typed_def\n        typed_def.raises = true\n      end\n\n      ignoring_type_filters do\n        node.obj.accept self\n      end\n\n      @in_type_args += 1\n      ignoring_type_filters do\n        node.to.accept self\n      end\n      @in_type_args -= 1\n\n      node.obj.add_observer node\n      node.update\n\n      false\n    end\n\n    def visit(node : FunDef)\n      body = node.body.not_nil!\n\n      external = node.external\n      return_type = external.type\n\n      vars = MetaVars.new\n      external.args.each do |arg|\n        var = MetaVar.new(arg.name, arg.type)\n        var.bind_to var\n        vars[arg.name] = var\n      end\n\n      visitor = MainVisitor.new(@program, vars, external)\n      visitor.untyped_def = external\n      visitor.scope = @program\n\n      begin\n        body.accept visitor\n      rescue ex : Crystal::CodeError\n        node.raise ex.message, ex\n      end\n\n      inferred_return_type = @program.type_merge([body.type?, external.type?])\n\n      if return_type && return_type != @program.nil && inferred_return_type != return_type\n        node.raise \"expected fun to return #{return_type} but it returned #{inferred_return_type}\"\n      end\n\n      external.set_type(return_type)\n\n      false\n    end\n\n    def visit(node : If)\n      request_type_filters do\n        node.cond.accept self\n      end\n\n      cond_vars = @vars\n      cond_type_filters = @type_filters\n\n      # then branch\n      @vars = cond_vars.dup\n      @type_filters = nil\n      @unreachable = false\n\n      filter_vars cond_type_filters\n      before_then_vars = @vars.dup\n\n      node.then.accept self\n\n      then_vars = @vars\n      then_type_filters = @type_filters\n      then_unreachable = @unreachable\n\n      # else branch\n      @vars = cond_vars.dup\n      @type_filters = nil\n      @unreachable = false\n\n      filter_vars TypeFilters.not(cond_type_filters)\n      before_else_vars = @vars.dup\n\n      node.else.accept self\n\n      else_vars = @vars\n      else_type_filters = @type_filters\n      else_unreachable = @unreachable\n\n      merge_if_vars node, cond_vars, then_vars, else_vars, before_then_vars, before_else_vars, then_unreachable, else_unreachable\n\n      @type_filters = nil\n      if needs_type_filters?\n        case node\n        when .and?\n          # `a && b` is expanded to `a ? b : a`\n          # We don't use `else_type_filters` because if `a` is a temp var\n          # assignment then `cond_type_filters` would contain more information\n          and_filters = TypeFilters.and(cond_type_filters, then_type_filters)\n          # For variables assigned in the then branch but not in the condition,\n          # add truthy filters so they're properly narrowed when the && result\n          # is used as a condition (#15739)\n          then_vars.each do |name, _|\n            next if cond_vars.has_key?(name)\n            and_filters = TypeFilters.and(and_filters, TypeFilters.truthy_var(name))\n          end\n          @type_filters = and_filters\n        when .or?\n          # `a || b` is expanded to `a ? a : b`\n          @type_filters = TypeFilters.or(cond_type_filters, else_type_filters)\n        end\n      end\n\n      @unreachable = then_unreachable && else_unreachable\n\n      node.bind_to({node.then, node.else})\n\n      false\n    end\n\n    # Here we merge the variables from both branches of an if.\n    # We basically:\n    #   - Create a variable whose type is the merged types of the last\n    #     type of each branch.\n    #   - Make the variable nilable if the variable wasn't declared\n    #     before the 'if' and it doesn't appear in one of the branches.\n    #   - Don't use the type of a branch that is unreachable (ends with return,\n    #     break or with a call that is NoReturn)\n    def merge_if_vars(node, cond_vars, then_vars, else_vars, before_then_vars, before_else_vars, then_unreachable, else_unreachable)\n      then_vars.each do |name, then_var|\n        else_var = else_vars[name]?\n\n        merge_if_var(name, node, cond_vars, then_var, else_var, before_then_vars, before_else_vars, then_unreachable, else_unreachable)\n      end\n\n      else_vars.each do |name, else_var|\n        next if then_vars.has_key?(name)\n\n        merge_if_var(name, node, cond_vars, nil, else_var, before_then_vars, before_else_vars, then_unreachable, else_unreachable)\n      end\n    end\n\n    def merge_if_var(name, node, cond_vars, then_var, else_var, before_then_vars, before_else_vars, then_unreachable, else_unreachable)\n      # Check whether the var didn't change at all\n      return if then_var.same?(else_var)\n\n      cond_var = cond_vars[name]?\n\n      # Only copy `nil_if_read` from each branch if it's not unreachable\n      then_var_nil_if_read = !then_unreachable && then_var.try(&.nil_if_read?)\n      else_var_nil_if_read = !else_unreachable && else_var.try(&.nil_if_read?)\n      if_var_nil_if_read = !!(then_var_nil_if_read || else_var_nil_if_read)\n\n      # Check if no types were changes in either then 'then' and 'else' branches\n      if cond_var && !then_unreachable && !else_unreachable\n        if then_var.same?(before_then_vars[name]?) &&\n           else_var.same?(before_else_vars[name]?)\n          cond_var.nil_if_read = if_var_nil_if_read\n          @vars[name] = cond_var\n          return\n        end\n      end\n\n      if_var = MetaVar.new(name)\n      if_var.nil_if_read = if_var_nil_if_read\n\n      if then_var && else_var\n        if then_unreachable\n          if_var.bind_to conditional_no_return(node.then, then_var)\n        else\n          if_var.bind_to then_var\n        end\n\n        if else_unreachable\n          if_var.bind_to conditional_no_return(node.else, else_var)\n        else\n          if_var.bind_to else_var\n        end\n      elsif then_var\n        if then_unreachable\n          if_var.bind_to conditional_no_return(node.then, then_var)\n        else\n          if_var.bind_to then_var\n        end\n\n        if cond_var\n          if_var.bind_to cond_var\n        elsif !else_unreachable\n          if_var.bind_to program.nil_var\n          if_var.nil_if_read = true\n        else\n          if_var.bind_to conditional_no_return(node.else, @program.nil_var)\n        end\n      elsif else_var\n        if else_unreachable\n          if_var.bind_to conditional_no_return(node.else, else_var)\n        else\n          if_var.bind_to else_var\n        end\n\n        if cond_var\n          if_var.bind_to cond_var\n        elsif !then_unreachable\n          if_var.bind_to program.nil_var\n          if_var.nil_if_read = true\n        else\n          if_var.bind_to conditional_no_return(node.then, @program.nil_var)\n        end\n      end\n\n      @vars[name] = if_var\n    end\n\n    def conditional_no_return(node, var)\n      node.filtered_by NoReturnFilter.new(var)\n    end\n\n    def visit(node : While)\n      old_while_vars = @while_vars\n\n      before_cond_vars_copy = @vars.dup\n\n      @vars.each do |name, var|\n        before_var = MetaVar.new(name)\n        before_var.bind_to(var)\n        before_var.nil_if_read = var.nil_if_read?\n        @vars[name] = before_var\n      end\n\n      before_cond_vars = @vars.dup\n\n      request_type_filters do\n        node.cond.accept self\n      end\n\n      cond_type_filters = @type_filters\n\n      after_cond_vars = @vars.dup\n      @while_vars = after_cond_vars\n\n      filter_vars cond_type_filters\n\n      # `node.body` may reset this status, so we capture them in a set\n      # (we don't need the full MetaVars at the moment)\n      after_cond_vars_nil_if_read = Set(String).new\n      @vars.each do |name, var|\n        after_cond_vars_nil_if_read << name if var.nil_if_read?\n      end\n\n      @type_filters = nil\n      @block, old_block = nil, @block\n\n      @while_stack.push node\n\n      with_block_kind :while do\n        node.body.accept self\n      end\n\n      cond = node.cond.single_expression\n      endless_while = cond.true_literal?\n      merge_while_vars endless_while, before_cond_vars_copy, before_cond_vars, after_cond_vars, after_cond_vars_nil_if_read, node.break_vars\n\n      @while_stack.pop\n      @block = old_block\n      @while_vars = old_while_vars\n\n      unless node.has_breaks?\n        if endless_while\n          node.type = program.no_return\n          return false\n        end\n\n        filter_vars TypeFilters.not(cond_type_filters)\n      end\n\n      node.bind_to(@program.nil_var) unless endless_while\n\n      false\n    end\n\n    # Here we assign the types of variables after a while.\n    def merge_while_vars(endless, before_cond_vars_copy, before_cond_vars, after_cond_vars, after_cond_vars_nil_if_read, all_break_vars)\n      after_while_vars = MetaVars.new\n\n      @vars.each do |name, while_var|\n        before_cond_var = before_cond_vars[name]?\n        after_cond_var = after_cond_vars[name]?\n\n        # Check if no types were changed in the condition or the body\n        if while_var.same?(before_cond_var) && while_var.same?(after_cond_var)\n          after_while_vars[name] = while_var\n          next\n        end\n\n        after_while_vars[name] = after_while_var = MetaVar.new(name)\n\n        # After while's body, bind variables *before* the condition to the\n        # ones after the body, because the loop will repeat.\n        #\n        # For example:\n        #\n        #    x = exp\n        #    # x starts with the type of exp\n        #    while x = x.method\n        #      # but after the loop, the x above (in x.method)\n        #      # should now also get the type of x.method, recursively\n        #    end\n        if before_cond_var && !before_cond_var.same?(while_var)\n          before_cond_var.bind_to(while_var)\n        end\n\n        # If the loop is endless\n        if endless\n          # Suppose we have\n          #\n          #     x = exp1\n          #     while true\n          #       x = exp2\n          #       break if ...\n          #       x = exp3\n          #       break if ...\n          #       x = exp4\n          #     end\n          #\n          # Here the type of x after the loop will never be affected by\n          # `x = exp4`, because `x = exp2` must have been executed before the\n          # loop may exit at the first break. Therefore, if the x right before\n          # the first break is different from the last x, we don't use the\n          # latter's type upon exit (but exp2 itself may depend on exp4 if it\n          # refers to x).\n          break_var = all_break_vars.try &.dig?(0, name)\n          unless break_var && !break_var.same?(while_var)\n            after_while_var.bind_to(while_var)\n            if before_cond_var\n              after_while_var.nil_if_read = while_var.nil_if_read?\n            end\n          end\n\n          # If there wasn't a previous variable with the same name, the variable\n          # is newly defined inside the while.\n          unless before_cond_var\n            # If not all variables with the given name end up in a break it\n            # means that they can be nilable.\n            # Alternatively, if any var that ends in a break is nil-if-read then\n            # the resulting variable will be nil-if-read too.\n            if !all_break_vars.try(&.all? &.has_key?(name)) ||\n               all_break_vars.try(&.any? &.[name]?.try &.nil_if_read?)\n              after_while_var.nil_if_read = true\n            end\n          end\n        else\n          # If a variable was assigned in the condition, it has that type.\n          if after_cond_var && !after_cond_var.same?(before_cond_var)\n            after_while_var.bind_to(after_cond_var)\n\n            # If the variable after the condition is nil-if-read, that means the\n            # assignment inside the condition might not run upon loop exit, so\n            # the variable may receive the type inside the loop.\n            if after_cond_vars_nil_if_read.includes?(name)\n              after_while_var.nil_if_read = true\n              after_while_var.bind_to(while_var) if !after_cond_var.same?(while_var)\n            end\n\n            # If there was a previous variable, we use that type merged\n            # with the last type inside the while.\n          elsif before_cond_var\n            # We need to bind to the variable *before* the condition, even\n            # before the variables that are used in the condition\n            # `before_cond_vars` are modified in the while body\n            after_while_var.bind_to(before_cond_vars_copy[name])\n            after_while_var.bind_to(while_var)\n            after_while_var.nil_if_read = before_cond_var.nil_if_read? || while_var.nil_if_read?\n\n            # Otherwise, it's a new variable inside the while: used\n            # outside it must be nilable.\n          else\n            after_while_var.bind_to(while_var)\n            after_while_var.nil_if_read = true\n          end\n        end\n      end\n\n      # We also need to merge types from breaks inside while.\n      all_break_vars.try &.each do |break_vars|\n        break_vars.each do |name, break_var|\n          after_while_var = after_while_vars[name]?\n          unless after_while_var\n            # Fix for issue #2441:\n            # it might be that a break variable is not present\n            # in the current vars after a while\n            after_while_var = new_meta_var(name)\n            after_while_var.bind_to(program.nil_var)\n            @meta_vars[name].bind_to(program.nil_var)\n            after_while_vars[name] = after_while_var\n          end\n          after_while_var.bind_to(break_var)\n        end\n      end\n\n      @vars = after_while_vars\n    end\n\n    # If we have:\n    #\n    #   if a\n    #     ...\n    #   end\n    #\n    # then inside the if 'a' must not be nil.\n    #\n    # This is what we do here: we create a meta-variable for\n    # it and filter it accordingly. This also applied to\n    # .is_a? and .responds_to?.\n    #\n    # This also applies to 'while' conditions and also\n    # to the else part of an if, but with filters inverted.\n    def filter_vars(filters)\n      filter_vars(filters) { |filter| filter }\n    end\n\n    def filter_vars(filters, &)\n      filters.try &.each do |name, filter|\n        existing_var = @vars[name]\n        filtered_var = MetaVar.new(name)\n        filtered_var.bind_to(existing_var.filtered_by(yield filter))\n        @vars[name] = filtered_var\n      end\n    end\n\n    def end_visit(node : Break)\n      if last_block_kind.ensure?\n        node.raise \"can't use break inside ensure\"\n      end\n\n      if block = @block\n        node.target = block.call.not_nil!\n\n        block.break.bind_to(node_exp_or_nil_literal(node))\n\n        bind_vars @vars, block.after_vars, block.args\n      elsif target_while = @while_stack.last?\n        node.target = target_while\n\n        break_vars = (target_while.break_vars ||= [] of MetaVars)\n        break_vars.push @vars.dup\n        target_while.bind_to(node_exp_or_nil_literal(node))\n      else\n        if @typed_def.try &.captured_block?\n          node.raise \"can't break from captured block, try using `next`.\"\n        end\n\n        node.raise \"invalid break\"\n      end\n\n      node.type = @program.no_return\n\n      @unreachable = true\n    end\n\n    def end_visit(node : Next)\n      if last_block_kind.ensure?\n        node.raise \"can't use next inside ensure\"\n      end\n\n      if block = @block\n        node.target = block\n\n        block.bind_to(node_exp_or_nil_literal(node))\n\n        bind_vars @vars, block.vars\n        bind_vars @vars, block.after_vars, block.args\n      elsif target_while = @while_stack.last?\n        node.target = target_while\n\n        bind_vars @vars, @while_vars\n      else\n        typed_def = @typed_def\n        if typed_def && typed_def.captured_block?\n          node.target = typed_def\n          typed_def.bind_to(node_exp_or_nil_literal(node))\n        else\n          node.raise \"invalid next\"\n        end\n      end\n\n      node.type = @program.no_return\n\n      @unreachable = true\n    end\n\n    def with_block_kind(kind : BlockKind, &)\n      old_block_kind, @last_block_kind = last_block_kind, kind\n      old_inside_ensure, @inside_ensure = @inside_ensure, @inside_ensure || kind.ensure?\n      yield\n      @last_block_kind = old_block_kind\n      @inside_ensure = old_inside_ensure\n    end\n\n    def visit(node : Primitive)\n      case node.name\n      when \"pre_initialize\"\n        return visit_pre_initialize node\n      end\n\n      # If the method where this primitive is defined has a return type, use it\n      if return_type = typed_def.return_type\n        node.type = (path_lookup || scope).lookup_type(return_type, free_vars: free_vars)\n        return false\n      end\n\n      # TODO: move these into the case expression above and add return types to\n      # their corresponding methods\n      case node.name\n      when \"allocate\"\n        visit_allocate node\n      when \"pointer_malloc\"\n        visit_pointer_malloc node\n      when \"pointer_set\"\n        visit_pointer_set node\n      when \"pointer_new\"\n        visit_pointer_new node\n      when \"slice_literal\"\n        node.raise \"BUG: Slice literal should have been expanded\"\n      when \"argc\"\n        # Already typed\n      when \"argv\"\n        # Already typed\n      when \"struct_or_union_set\"\n        visit_struct_or_union_set node\n      when \"external_var_set\"\n        # Nothing to do\n      when \"external_var_get\"\n        # Nothing to do\n      when \"class\"\n        node.type = scope.metaclass\n      when \"enum_value\"\n        # Nothing to do\n      when \"enum_new\"\n        # Nothing to do\n      when \"throw_info\"\n        node.type = program.pointer_of(program.void)\n      when \"va_arg\"\n        visit_va_arg node\n      else\n        node.raise \"BUG: unhandled primitive in MainVisitor: #{node.name}\"\n      end\n\n      false\n    end\n\n    def visit_va_arg(node)\n      arg = call.not_nil!.args[0]? || node.raise(\"requires type argument\")\n      node.type = arg.type.instance_type\n    end\n\n    def visit_allocate(node)\n      instance_type = scope.instance_type\n\n      case instance_type\n      when GenericClassType\n        node.raise \"can't create instance of generic class #{instance_type} without specifying its type vars\"\n      when UnionType\n        node.raise \"can't create instance of a union type\"\n      when PointerInstanceType\n        node.raise \"can't create instance of a pointer type\"\n      else\n        if instance_type.abstract?\n          if instance_type.virtual?\n            # A call to \"allocate\" on a virtual type can happen if we have something like:\n            #\n            # ```\n            # abstract class Foo\n            # end\n            #\n            # class Bar < Foo\n            # end\n            #\n            # a = [] of Foo.class\n            # a << Bar\n            # a << Foo\n            #\n            # a.map(&.new)\n            # ```\n            #\n            # It's perfectly fine to have an array of `Foo.class`, and we can\n            # put `Foo` and `Bar` in it. We should also be able to call `new`\n            # on every element in the array. We'll have to trust the user doesn't\n            # put abstract types there. But if they are abstract, we make that\n            # call a runtime error.\n            base_type = instance_type.devirtualize\n\n            extra = Call.new(\n              nil,\n              \"raise\",\n              StringLiteral.new(\"Can't instantiate abstract #{base_type.type_desc} #{base_type}\"),\n              global: true)\n            extra.accept self\n\n            # This `extra` will replace the Primitive node in CleanupTransformer later on.\n            node.extra = extra\n            node.type = @program.no_return\n            return\n          else\n            # If the type is not virtual then we know for sure that the type\n            # can't be instantiated, and we can produce a compile-time error.\n            node.raise \"can't instantiate abstract #{instance_type.type_desc} #{instance_type}\"\n          end\n        end\n\n        node.type = instance_type\n      end\n    end\n\n    def visit_pre_initialize(node)\n      instance_type = scope.instance_type\n\n      case instance_type\n      when GenericClassType\n        node.raise \"Can't pre-initialize instance of #{instance_type.type_desc} #{instance_type} without specifying its type vars\"\n      when UnionType\n        node.raise \"Can't pre-initialize instance of a union type\"\n      end\n\n      if instance_type.abstract?\n        if instance_type.virtual? && !instance_type.struct?\n          # This is the same as `.initialize`\n          base_type = instance_type.devirtualize\n\n          extra = Call.new(\n            nil,\n            \"raise\",\n            StringLiteral.new(\"Can't pre-initialize abstract #{base_type.type_desc} #{base_type}\"),\n            global: true).at(node)\n          extra.accept self\n\n          # This `extra` will replace the Primitive node in CleanupTransformer later on.\n          node.extra = extra\n          node.type = @program.no_return\n          return false\n        else\n          # If the type is not virtual then we know for sure that the type\n          # can't be instantiated, and we can produce a compile-time error.\n          instance_type = instance_type.devirtualize\n          node.raise \"Can't pre-initialize abstract #{instance_type.type_desc} #{instance_type}\"\n        end\n      end\n\n      if instance_type.struct?\n        element_type = @vars[\"address\"].type.as(PointerInstanceType).element_type\n        if element_type.abstract? && element_type.struct?\n          node.raise \"Can't pre-initialize struct using pointer to abstract struct\"\n        end\n        node.type = @program.nil_type\n      else\n        node.type = instance_type\n      end\n\n      false\n    end\n\n    def visit_pointer_malloc(node)\n      if scope.instance_type.is_a?(GenericClassType)\n        node.raise \"can't malloc pointer without type, use Pointer(Type).malloc(size)\"\n      end\n\n      node.type = scope.instance_type\n    end\n\n    def visit_pointer_set(node)\n      scope = scope().remove_typedef.as(PointerInstanceType)\n\n      # We don't want to change the value of Pointer(Void) to include Nil (Nil passes as Void)\n      if scope.element_type.void?\n        node.type = @program.nil\n        return\n      end\n\n      value = @vars[\"value\"]\n\n      node.bind_to value\n    end\n\n    def visit_pointer_new(node)\n      if scope.instance_type.is_a?(GenericClassType)\n        node.raise \"can't create pointer without type, use Pointer(Type).new(address)\"\n      end\n\n      node.type = scope.instance_type\n    end\n\n    def visit_struct_or_union_set(node)\n      scope = self.scope.remove_typedef.as(NonGenericClassType)\n\n      field_name = call.not_nil!.name.rchop\n      expected_type = scope.instance_vars['@' + field_name].type\n      value = @vars[\"value\"]\n      actual_type = value.type\n\n      node.type = actual_type\n\n      actual_type = actual_type.remove_alias\n      unaliased_type = expected_type.remove_alias\n\n      return if actual_type.compatible_with?(unaliased_type)\n      return if actual_type.implicitly_converted_in_c_to?(unaliased_type)\n\n      case unaliased_type\n      when IntegerType\n        if convert_call = convert_struct_or_union_numeric_argument(node, unaliased_type, expected_type, actual_type)\n          node.extra = convert_call\n          return\n        end\n      when FloatType\n        if convert_call = convert_struct_or_union_numeric_argument(node, unaliased_type, expected_type, actual_type)\n          node.extra = convert_call\n          return\n        end\n      end\n\n      unsafe_call = Conversions.to_unsafe(node, Var.new(\"value\").at(node), self, actual_type, expected_type)\n      if unsafe_call\n        node.extra = unsafe_call\n        return\n      end\n\n      node.raise \"field '#{field_name}' of #{scope.type_desc} #{scope} has type #{expected_type}, not #{actual_type}\"\n    end\n\n    def convert_struct_or_union_numeric_argument(node, unaliased_type, expected_type, actual_type)\n      Conversions.numeric_argument(node, Var.new(\"value\"), self, unaliased_type, expected_type, actual_type)\n    end\n\n    def visit(node : PointerOf)\n      var = pointerof_var(node)\n\n      unless var\n        # Accept the exp to trigger potential errors there, like\n        # \"undefined local variable or method\"\n        node.exp.accept self\n\n        node.exp.raise \"can't take address of #{node.exp} because it's a #{node.exp.class_desc}. `pointerof` expects a variable or constant.\"\n      end\n\n      node.bind_to var\n      true\n    end\n\n    def pointerof_var(node)\n      case exp = node.exp\n      when Var\n        meta_var = @meta_vars[exp.name]\n        meta_var.assigned_to = true\n        meta_var\n      when InstanceVar\n        lookup_instance_var exp\n      when ClassVar\n        visit_class_var exp\n      when Global\n        node.raise \"BUG: there should be no use of global variables other than $~ and $?\"\n      when Path\n        exp.accept self\n        if const = exp.target_const\n          const.pointer_read = true\n          const.value\n        end\n      when ReadInstanceVar\n        visit_read_instance_var(exp)\n        exp\n      when Call\n        # Check lib external var\n        return unless exp.args.empty? && !exp.named_args && !exp.block && !exp.block_arg\n\n        obj = exp.obj\n        return unless obj\n\n        obj.accept(self)\n\n        obj_type = obj.type?\n        return unless obj_type.is_a?(LibType)\n\n        obj_type.lookup_var(exp.name)\n      else\n        nil\n      end\n    end\n\n    def visit(node : TypeOf)\n      # A typeof shouldn't change the type of variables:\n      # so we keep the ones before it and restore them at the end\n      old_vars = @vars.dup\n      old_meta_vars = @meta_vars\n      @meta_vars = old_meta_vars.dup\n\n      node.in_type_args = @in_type_args > 0\n\n      old_in_type_args = @in_type_args\n      @in_type_args = 0\n\n      @typeof_nest += 1\n      ignoring_type_filters do\n        node.expressions.each &.accept self\n      end\n      @typeof_nest -= 1\n\n      @in_type_args = old_in_type_args\n\n      node.bind_to node.expressions\n\n      @vars = old_vars\n      @meta_vars = old_meta_vars\n\n      false\n    end\n\n    def visit(node : SizeOf)\n      visit_size_or_align_of(node) do |type|\n        @program.size_of(type.sizeof_type)\n      end\n    end\n\n    def visit(node : InstanceSizeOf)\n      visit_instance_size_or_align_of(node) do |type|\n        @program.instance_size_of(type.sizeof_type)\n      end\n    end\n\n    def visit(node : AlignOf)\n      visit_size_or_align_of(node) do |type|\n        @program.align_of(type.sizeof_type)\n      end\n    end\n\n    def visit(node : InstanceAlignOf)\n      visit_instance_size_or_align_of(node) do |type|\n        @program.instance_align_of(type.sizeof_type)\n      end\n    end\n\n    private def visit_size_or_align_of(node, &)\n      @in_type_args += 1\n      node.exp.accept self\n      @in_type_args -= 1\n\n      type = node.exp.type?\n\n      if type.is_a?(GenericType)\n        node.exp.raise \"can't take #{sizeof_description(node)} of uninstantiated generic type #{type}\"\n      end\n\n      # Try to resolve the node right now to a number literal\n      # (useful for sizeof/alignof inside as a generic type argument, but also\n      # to make it easier for LLVM to optimize things)\n      if type && !node.exp.is_a?(TypeOf) &&\n         !(type.module? || (type.abstract? && type.struct?))\n        expanded = NumberLiteral.new(yield(type).to_s, :i32)\n        expanded.type = @program.int32\n        node.expanded = expanded\n      end\n\n      node.type = @program.int32\n\n      false\n    end\n\n    private def visit_instance_size_or_align_of(node, &)\n      @in_type_args += 1\n      node.exp.accept self\n      @in_type_args -= 1\n\n      type = node.exp.type?\n\n      if type.is_a?(GenericType)\n        node.exp.raise \"can't take #{sizeof_description(node)} of uninstantiated generic type #{type}\"\n      end\n\n      # Try to resolve the instance_sizeof right now to a number literal\n      # (useful for instance_sizeof inside as a generic type argument, but also\n      # to make it easier for LLVM to optimize things)\n      if type && type.devirtualize.class? && !type.metaclass? && !type.struct? && !node.exp.is_a?(TypeOf)\n        expanded = NumberLiteral.new(yield(type).to_s, :i32)\n        expanded.type = @program.int32\n        node.expanded = expanded\n      end\n\n      node.type = @program.int32\n\n      false\n    end\n\n    private def sizeof_description(node)\n      case node\n      in SizeOf\n        \"size\"\n      in AlignOf\n        \"alignment\"\n      in InstanceSizeOf\n        \"instance size\"\n      in InstanceAlignOf\n        \"instance alignment\"\n      end\n    end\n\n    def visit(node : OffsetOf)\n      @in_type_args += 1\n      node.offsetof_type.accept self\n      @in_type_args -= 1\n\n      type = node.offsetof_type.type?\n      node.offsetof_type.raise \"can't use typeof inside offsetof expression\" if node.offsetof_type.is_a?(TypeOf)\n\n      case type\n      when TupleInstanceType\n        number = node.offset.as?(NumberLiteral)\n        node.offset.raise \"can't take offset of a tuple element using an instance variable, use an index\" if number.nil?\n\n        ivar_index = number.integer_value\n        node.offset.raise \"can't take a negative offset of a tuple\" if ivar_index < 0\n        if ivar_index >= type.size\n          node.offset.raise \"can't take offset element at index #{ivar_index} from a tuple with #{type.size} elements\"\n        end\n      when InstanceVarContainer\n        ivar = node.offset.as?(InstanceVar)\n        node.offset.raise \"can't take offset element of #{type} using an index, use an instance variable\" if ivar.nil?\n\n        ivar_name = ivar.name\n        ivar_index = type.index_of_instance_var(ivar_name)\n\n        node.offset.raise \"type #{type} doesn't have an instance variable called #{ivar_name}\" unless ivar_index\n        node.offsetof_type.raise \"can't take offsetof element #{ivar_name} of uninstantiated generic type #{type}\" if type.is_a?(GenericType)\n      else\n        node.offsetof_type.raise \"type #{type} can't have instance variables neither is a Tuple\"\n      end\n\n      if type && (type.struct? || type.is_a?(TupleInstanceType))\n        offset = @program.offset_of(type.sizeof_type, ivar_index)\n      elsif type && type.instance_type.devirtualize.class?\n        offset = @program.instance_offset_of(type.sizeof_type, ivar_index)\n      else\n        node.offsetof_type.raise \"#{type} is neither a class, a struct nor a Tuple, it's a #{type.type_desc}\"\n      end\n\n      expanded = NumberLiteral.new(offset.to_s, :i32)\n      expanded.type = @program.int32\n      node.expanded = expanded\n      node.type = @program.int32\n\n      false\n    end\n\n    private def allowed_type_in_rescue?(type : UnionType) : Bool\n      type.union_types.all? do |subtype|\n        allowed_type_in_rescue? subtype\n      end\n    end\n\n    private def allowed_type_in_rescue?(type : Crystal::Type) : Bool\n      type.implements?(@program.exception) || type.module?\n    end\n\n    def visit(node : Rescue)\n      if node_types = node.types\n        types = node_types.map do |type|\n          type.accept self\n          instance_type = type.type.instance_type\n\n          unless self.allowed_type_in_rescue? instance_type\n            type.raise \"#{instance_type} cannot be used for `rescue`. Only subclasses of `Exception` and modules, or unions thereof, are allowed.\"\n          end\n\n          instance_type\n        end\n      end\n\n      if node_name = node.name\n        var = @vars[node_name] = new_meta_var(node_name)\n        meta_var, _ = assign_to_meta_var(node_name)\n        meta_var.bind_to(var)\n        meta_var.assigned_to = true\n        check_closured(meta_var)\n        check_mutably_closured(meta_var, var)\n\n        if types\n          unified_type = @program.type_merge(types).not_nil!\n          unified_type = unified_type.virtual_type unless unified_type.is_a?(VirtualType)\n        else\n          unified_type = @program.exception.virtual_type\n        end\n        var.type = unified_type\n        var.freeze_type = unified_type\n\n        node.set_type(var.type)\n      end\n\n      node.body.accept self\n\n      false\n    end\n\n    def visit(node : ExceptionHandler)\n      # Save old vars to know if new variables are declared inside begin/rescue/else\n      before_body_vars = @vars.dup\n\n      # Any variable assigned in the body (begin) will have, inside rescue\n      # blocks, all types that were assigned to them, because we can't know at which\n      # point an exception is raised.\n      # We have a stack of these, to take into account nested exception handlers.\n      all_exception_handler_vars = @all_exception_handler_vars ||= [] of MetaVars\n\n      # We create different vars, though, to avoid changing the type of vars\n      # before the handler.\n      exception_handler_vars = @vars.dup\n\n      all_exception_handler_vars.push exception_handler_vars\n\n      exception_handler_vars.each do |name, var|\n        new_var = new_meta_var(name)\n        new_var.nil_if_read = var.nil_if_read?\n        new_var.bind_to(var)\n        exception_handler_vars[name] = new_var\n      end\n\n      node.body.accept self\n\n      after_exception_handler_vars = @vars.dup\n\n      all_exception_handler_vars.pop\n\n      if node.rescues || node.else\n        # Any variable introduced in the begin block is possibly nil\n        # in the rescue blocks because we can't know if an exception\n        # was raised before assigning any of the vars.\n        exception_handler_vars.each do |name, var|\n          unless before_body_vars[name]?\n            # Instance variables inside the body must be marked as nil\n            if name.starts_with?('@')\n              ivar = scope.lookup_instance_var(name)\n              unless ivar.type.includes_type?(@program.nil_var)\n                ivar.nil_reason = NilReason.new(name, :initialized_in_rescue)\n                ivar.bind_to @program.nil_var\n              end\n            end\n\n            var.nil_if_read = true\n          end\n        end\n\n        # Now, using these vars, visit all rescue blocks and keep\n        # the results in this variable.\n        all_rescue_vars = [] of MetaVars\n\n        node.rescues.try &.each do |a_rescue|\n          @vars = exception_handler_vars.dup\n          @unreachable = false\n          a_rescue.accept self\n          all_rescue_vars << @vars unless @unreachable\n        end\n\n        # In the else block the types are the same as in the begin block,\n        # because we assume no exception was raised.\n        node.else.try do |a_else|\n          @vars = after_exception_handler_vars.dup\n          @unreachable = false\n          a_else.accept self\n          all_rescue_vars << @vars unless @unreachable\n        end\n\n        # If all rescue/else blocks are unreachable, then afterwards\n        # the flow continues as if there were no rescue/else blocks.\n        if all_rescue_vars.empty?\n          all_rescue_vars = nil\n        else\n          # Otherwise, merge all types that resulted from all rescue/else blocks\n          merge_rescue_vars exception_handler_vars, all_rescue_vars\n\n          # And then accept the ensure part\n          with_block_kind :ensure do\n            node.ensure.try &.accept self\n          end\n        end\n      end\n\n      # If there were no rescue/else blocks or all of them were unreachable\n      unless all_rescue_vars\n        if node_ensure = node.ensure\n          after_handler_vars = @vars\n          @vars = exception_handler_vars\n\n          # Variables in the ensure block might be nil because we don't know\n          # if an exception was thrown before any assignment.\n          @vars.each do |name, var|\n            unless before_body_vars[name]?\n              var.nil_if_read = true\n            end\n          end\n\n          before_ensure_vars = @vars.dup\n\n          with_block_kind :ensure do\n            node_ensure.accept self\n          end\n\n          @vars = after_handler_vars\n\n          # Variables declared or overwritten inside the ensure block\n          # must remain after the exception handler\n          exception_handler_vars.each do |name, var|\n            before_var = before_ensure_vars[name]?\n            @vars[name] = var unless var.same?(before_var)\n          end\n\n          # However, those previous variables can't be nil afterwards:\n          # if an exception was raised then we won't be running the code\n          # after the ensure clause, so variables don't matter. But if\n          # an exception was not raised then all variables were declared\n          # successfully.\n          @vars.each do |name, var|\n            unless before_body_vars[name]?\n              # But if the variable is already nilable after the begin\n              # block it must remain nilable\n              unless after_exception_handler_vars[name]?.try &.nil_if_read?\n                var.nil_if_read = false\n              end\n            end\n          end\n        else\n          # If there's no ensure, because all rescue/else end with unreachable\n          # we know all the vars after the exception handler will have the types\n          # after the handle (begin) block.\n          @vars = after_exception_handler_vars\n        end\n      end\n\n      if node_ensure = node.ensure\n        node_ensure.add_observer(node)\n      end\n\n      if node_else = node.else\n        node.bind_to node_else\n      else\n        node.bind_to node.body\n      end\n\n      if node_rescues = node.rescues\n        node_rescues.each do |a_rescue|\n          node.bind_to a_rescue.body\n        end\n      end\n\n      false\n    end\n\n    def merge_rescue_vars(body_vars, all_rescue_vars)\n      after_vars = MetaVars.new\n\n      all_rescue_vars.each do |rescue_vars|\n        rescue_vars.each do |name, var|\n          after_var = (after_vars[name] ||= new_meta_var(name))\n          if var.nil_if_read? || !body_vars[name]?\n            after_var.nil_if_read = true\n          end\n          after_var.bind_to(var)\n        end\n      end\n\n      body_vars.each do |name, var|\n        after_var = (after_vars[name] ||= new_meta_var(name))\n        after_var.bind_to(var)\n      end\n\n      @vars = after_vars\n    end\n\n    def end_visit(node : TupleLiteral)\n      node.elements.each &.add_observer(node)\n      node.program = @program\n      node.update\n\n      node.elements.each do |element|\n        if element.is_a?(Splat) && (type = element.type?)\n          unless type.is_a?(TupleInstanceType)\n            node.raise \"argument to splat must be a tuple, not #{type}\"\n          end\n        end\n      end\n\n      false\n    end\n\n    def end_visit(node : NamedTupleLiteral)\n      node.entries.each &.value.add_observer(node)\n      node.program = @program\n      node.update\n      false\n    end\n\n    def visit(node : TupleIndexer)\n      scope = @scope\n      if scope.is_a?(TupleInstanceType)\n        case index = node.index\n        in Range\n          node.type = @program.tuple_of(scope.tuple_types[index].map &.as(Type))\n        in Int32\n          node.type = scope.tuple_types[index].as(Type)\n        end\n      elsif scope.is_a?(NamedTupleInstanceType)\n        node.type = scope.entries[node.index.as(Int32)].type\n      elsif scope && (instance_type = scope.instance_type).is_a?(TupleInstanceType)\n        case index = node.index\n        in Range\n          node.type = @program.tuple_of(instance_type.tuple_types[index].map &.as(Type)).metaclass\n        in Int32\n          node.type = instance_type.tuple_types[index].as(Type).metaclass\n        end\n      elsif scope && (instance_type = scope.instance_type).is_a?(NamedTupleInstanceType)\n        node.type = instance_type.entries[node.index.as(Int32)].type.metaclass\n      else\n        node.raise \"unsupported TupleIndexer scope\"\n      end\n      false\n    end\n\n    def visit(node : Asm)\n      if outputs = node.outputs\n        node.output_ptrofs = outputs.map do |output|\n          ptrof = PointerOf.new(output.exp).at(output.exp)\n          ptrof.accept self\n          ptrof\n        end\n      end\n\n      if inputs = node.inputs\n        inputs.each &.exp.accept self\n      end\n\n      node.type = @program.void\n      false\n    end\n\n    # # Literals\n\n    def visit(node : Nop)\n      node.type = @program.nil\n      false\n    end\n\n    def visit(node : NilLiteral)\n      node.type = @program.nil\n      false\n    end\n\n    def visit(node : BoolLiteral)\n      node.type = program.bool\n      false\n    end\n\n    def visit(node : NumberLiteral)\n      node.type = program.type_from_literal_kind node.kind\n      false\n    end\n\n    def visit(node : CharLiteral)\n      node.type = program.char\n      false\n    end\n\n    def visit(node : SymbolLiteral)\n      node.type = program.symbol\n      program.symbols.add node.value\n      false\n    end\n\n    def visit(node : StringLiteral)\n      node.type = program.string\n      false\n    end\n\n    def visit(node : RegexLiteral)\n      expand(node)\n    end\n\n    def visit(node : ArrayLiteral)\n      if name = node.name\n        name.accept self\n        type = name.type.instance_type\n        generic_type = TypeNode.new(type).at(node) if type.is_a?(GenericClassType)\n        expand_named(node, generic_type)\n      else\n        expand(node)\n      end\n    end\n\n    def visit(node : HashLiteral)\n      if name = node.name\n        name.accept self\n        type = name.type.instance_type\n        generic_type = TypeNode.new(type).at(node) if type.is_a?(GenericClassType)\n        expand_named(node, generic_type)\n      else\n        expand(node)\n      end\n    end\n\n    def visit(node : And)\n      expand(node)\n    end\n\n    def visit(node : Or)\n      expand(node)\n    end\n\n    def visit(node : RangeLiteral)\n      expand(node)\n    end\n\n    def visit(node : StringInterpolation)\n      expand(node)\n\n      # This allows some methods to be resolved even if the interpolated expressions doesn't\n      # end up with a type because of recursive methods. We should really do something more\n      # clever and robust here for the general case.\n      node.type = program.string\n\n      false\n    end\n\n    def visit(node : Case)\n      # For exhaustiveness check, which is done in CleanupTransformer,\n      # we need to know the type of `cond`. However, LiteralExpander will\n      # work with copies of `cond` in case they are Var or InstanceVar so\n      # here we type them so their type is available later on.\n      cond = node.cond\n      case cond\n      when Var         then cond.accept(self)\n      when InstanceVar then cond.accept(self)\n      when TupleLiteral\n        cond.elements.each do |element|\n          case element\n          when Var         then element.accept(self)\n          when InstanceVar then element.accept(self)\n          end\n        end\n      end\n\n      expand(node)\n      false\n    end\n\n    def visit(node : Select)\n      expand(node)\n      false\n    end\n\n    def visit(node : MultiAssign)\n      expand(node)\n      false\n    end\n\n    def expand(node)\n      expand(node) { @program.literal_expander.expand node }\n    end\n\n    def expand_named(node, generic_type)\n      expand(node) { @program.literal_expander.expand_named node, generic_type }\n    end\n\n    def expand(node, &)\n      expanded = yield\n      expanded.accept self\n      node.expanded = expanded\n      node.bind_to expanded\n      false\n    end\n\n    def visit(node : Not)\n      node.exp.accept self\n      node.exp.add_observer node\n      node.update\n\n      if needs_type_filters?\n        @type_filters = TypeFilters.not(@type_filters)\n      end\n\n      false\n    end\n\n    def visit(node : VisibilityModifier)\n      exp = node.exp\n      exp.accept self\n\n      # Only check for calls that didn't resolve to a macro:\n      # all other cases are already covered in TopLevelVisitor\n      if exp.is_a?(Call) && !exp.expanded\n        node.raise \"can't apply visibility modifier\"\n      end\n\n      node.type = @program.nil\n\n      false\n    end\n\n    def visit(node : Arg)\n      # Arg nodes are also used for Enum constants, and they\n      # must be skipped here\n      false\n    end\n\n    def visit(node : AssignWithRestriction)\n      type_assign(\n        node.assign.target.as(Var),\n        node.assign.value,\n        node.assign,\n        restriction: node.restriction)\n      node.bind_to(node.assign)\n      false\n    end\n\n    def visit(node : Unreachable)\n      node.type = @program.no_return\n      @unreachable = true\n    end\n\n    # # Helpers\n\n    def free_vars\n      match_context.try &.bound_free_vars\n    end\n\n    def check_closured(var, mark_as_mutably_closured : Bool = false)\n      return if @typeof_nest > 0\n\n      if var.name == \"self\"\n        check_self_closured\n        return\n      end\n\n      context = current_context\n      var_context = var.context\n      if var_context.same?(context)\n        var_context = var_context.context if var_context.is_a?(Block)\n        if var.closured?\n          mark_as_closured(var, var_context, mark_as_mutably_closured)\n        end\n      else\n        # If the contexts are not the same, it might be that we are in a block\n        # inside a method, or a block inside another block. We don't want\n        # those cases to closure a variable. So if any context is a block\n        # we go to the block's context (a def or a proc literal) and compare\n        # if those are the same to determine whether the variable is closured.\n        context = context.context if context.is_a?(Block)\n        var_context = var_context.context if var_context.is_a?(Block)\n\n        closured = !context.same?(var_context)\n        if closured\n          mark_as_closured(var, var_context, mark_as_mutably_closured)\n        end\n      end\n    end\n\n    def mark_as_closured(var, var_context, mark_as_mutably_closured : Bool)\n      # This is a bit tricky: when we assign to a variable we create a new metavar\n      # for it if it didn't exist. If it did exist, and right now we are forming\n      # a closure, then we also want to mark it as readonly.\n      # We already do this in `assign_to_meta_var` but that's done **before**\n      # we detect a closure in an assignment. So that logic needs to be replicated here,\n      # and it must happen before we actually mark is as closured.\n      var.mutably_closured = true if mark_as_mutably_closured\n      var.mark_as_closured\n\n      # Go up and mark proc literal defs as closured until we get\n      # to the context where the variable is defined\n      visitor = self\n      while visitor\n        visitor_context = visitor.closure_context\n        break if visitor_context == var_context\n\n        visitor_context.closure = true if visitor_context.is_a?(Def)\n        visitor = visitor.parent\n      end\n    end\n\n    def check_self_closured\n      return if @typeof_nest > 0\n\n      scope = @scope\n      return unless scope\n\n      return if scope.metaclass? && !scope.virtual_metaclass?\n\n      context = @fun_literal_context\n      return unless context.is_a?(Def)\n\n      context.self_closured = true\n\n      # Go up and mark proc literal defs as closured until the top\n      # (which should be when we leave the top Def)\n      visitor = self\n      while visitor\n        visitor_context = visitor.closure_context\n        visitor_context.closure = true if visitor_context.is_a?(Def)\n        visitor = visitor.parent\n      end\n    end\n\n    def current_context\n      @block_context || current_non_block_context\n    end\n\n    def current_non_block_context\n      @typed_def || @file_module || @program\n    end\n\n    def closure_context\n      context = current_context\n      context = context.context if context.is_a?(Block)\n      context\n    end\n\n    def lookup_var_or_instance_var(var : Var)\n      @vars[var.name]\n    end\n\n    def lookup_var_or_instance_var(var : InstanceVar)\n      scope.lookup_instance_var(var.name)\n    end\n\n    def lookup_var_or_instance_var(var)\n      raise \"BUG: trying to lookup var or instance var but got #{var}\"\n    end\n\n    def bind_meta_var(var : Var)\n      @meta_vars[var.name].bind_to(var)\n    end\n\n    def bind_meta_var(var : InstanceVar)\n      # Nothing to do\n    end\n\n    def bind_meta_var(var)\n      raise \"BUG: trying to bind var or instance var but got #{var}\"\n    end\n\n    def bind_initialize_instance_vars(owner)\n      names_to_remove = [] of String\n\n      @vars.each do |name, var|\n        if name.starts_with? '@'\n          if var.nil_if_read?\n            ivar = lookup_instance_var(var, owner)\n            ivar.bind_to program.nil_var\n          end\n\n          names_to_remove << name\n        end\n      end\n\n      names_to_remove.each do |name|\n        @meta_vars.delete name\n        @vars.delete name\n      end\n    end\n\n    def check_initialize_instance_vars_types(owner)\n      return if untyped_def.calls_super? ||\n                untyped_def.calls_initialize?\n\n      owner.all_instance_vars.each do |name, var|\n        next if owner.has_instance_var_initializer?(name)\n        next if var.type.includes_type?(@program.nil)\n\n        meta_var = @meta_vars[name]?\n        unless meta_var\n          untyped_def.raise \"instance variable '#{name}' of #{owner} was not initialized in this 'initialize', rendering it nilable\"\n        end\n      end\n    end\n\n    def needs_type_filters?\n      @needs_type_filters > 0\n    end\n\n    def request_type_filters(&)\n      @type_filters = nil\n      @needs_type_filters += 1\n      begin\n        yield\n      ensure\n        @needs_type_filters -= 1\n      end\n    end\n\n    def ignoring_type_filters(&)\n      needs_type_filters, @needs_type_filters = @needs_type_filters, 0\n      begin\n        yield\n      ensure\n        @needs_type_filters = needs_type_filters\n      end\n    end\n\n    def lookup_similar_var_name(name)\n      Levenshtein.find(name) do |finder|\n        @vars.each_key do |var_name|\n          finder.test(var_name)\n        end\n      end\n    end\n\n    def define_special_var(name, value)\n      meta_var, _ = assign_to_meta_var(name)\n      meta_var.bind_to value\n      meta_var.bind_to program.nil_var unless meta_var.dependencies.any? &.same?(program.nil_var)\n      meta_var.assigned_to = true\n      check_closured meta_var\n\n      @vars[name] = meta_var\n      meta_var\n    end\n\n    def new_meta_var(name, context = current_context)\n      meta_var = MetaVar.new(name)\n      meta_var.context = context\n      meta_var\n    end\n\n    def assign_to_meta_var(name, context = current_context)\n      meta_var = @meta_vars[name]?\n      meta_var_existed = !!meta_var\n      if meta_var\n        # This var gets assigned a new value and it already existed before this line.\n        # If it's also a closured var it means it has become mutably closured.\n        meta_var.mutably_closured = true if meta_var.closured?\n      else\n        @meta_vars[name] = meta_var = new_meta_var(name)\n      end\n\n      # If a variable is being assigned inside a while then it's considered\n      # as mutably closured: it will get a value assigned to it multiple times\n      # exactly because it's in a loop.\n      meta_var.mutably_closured = true if inside_while?\n\n      # If a variable is being assigned to inside a block:\n      # - if the variable is a new variable then there's no need to mark is a mutably\n      #   closured because unless it gets assigned again it will be a different\n      #   variable allocation each time\n      # - if the variable already existed but it's assigned in the same context\n      #   as before, if it's not closured already then it still shouldn't\n      #   be marked as mutably closured\n      # - otherwise, we mark it as mutably closured. The block might happen\n      #   in a while loop, or invoked multiple times: we don't know, so we must\n      #   mark is as such until the compiler gets smarter (if really necessary)\n      if @block && meta_var_existed && !current_context.same?(meta_var.context)\n        meta_var.mutably_closured = true\n      end\n\n      {meta_var, meta_var_existed}\n    end\n\n    def block=(@block)\n      @block_context = @block\n    end\n\n    def inside_block?\n      @untyped_def || @block_context\n    end\n\n    def inside_while?\n      !@while_stack.empty?\n    end\n\n    def lookup_class_var(node)\n      class_var_owner = class_var_owner(node)\n      var = class_var_owner.lookup_class_var?(node.name)\n      unless var\n        undefined_class_variable(node, class_var_owner)\n      end\n      var\n    end\n\n    def undefined_class_variable(node, owner)\n      similar_name = lookup_similar_class_variable_name(node, owner)\n      @program.undefined_class_variable(node, owner, similar_name)\n    end\n\n    def lookup_similar_class_variable_name(node, owner)\n      Levenshtein.find(node.name) do |finder|\n        owner.class_vars.each_key do |name|\n          finder.test(name)\n        end\n      end\n    end\n\n    def check_type_in_type_args(type)\n      if @in_type_args > 0\n        type\n      else\n        type.metaclass\n      end\n    end\n\n    def the_self(node)\n      the_self = (@scope || current_type)\n      if the_self.is_a?(Program)\n        node.raise \"there's no self in this scope\"\n      end\n      the_self\n    end\n\n    # Returns node.exp if it's not nil. Otherwise,\n    # creates a NilLiteral node that has the same location\n    # as `node`, and returns that.\n    # We use this NilLiteral when the user writes\n    # `return`, `next` or `break` without arguments,\n    # so that in the error trace we can show it right\n    # (those expressions have a NoReturn type so we can't\n    # directly bind to them).\n    def node_exp_or_nil_literal(node)\n      exp = node.exp\n      return exp if exp\n\n      nil_exp = NilLiteral.new\n      nil_exp.location = node.location\n      nil_exp.type = @program.nil\n      nil_exp\n    end\n\n    # If the meta_var is closured but not readonly, then bind var\n    # to it (it gets all types assigned to meta_var).\n    # Otherwise, add it to the local vars so that they could be\n    # bond later on, if the meta_var stops being readonly.\n    def check_mutably_closured(meta_var, var)\n      if meta_var.closured? && meta_var.mutably_closured?\n        var.bind_to(meta_var)\n      else\n        meta_var.local_vars << var\n      end\n    end\n\n    def visit(node : When | Unless | Until | MacroLiteral | OpAssign)\n      raise \"BUG: #{node.class_desc} node '#{node}' (#{node.location}) should have been eliminated in normalize\"\n    end\n\n    def visit(node : ImplicitObj)\n      raise \"BUG: #{node.class_desc} node '#{node}' (#{node.location}) should have been eliminated in expand\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/match.cr",
    "content": "module Crystal\n  # Context information about a method lookup match.\n  #\n  # For example, given:\n  #\n  # ```\n  # class Foo\n  #   class Baz\n  #   end\n  #\n  #   def method\n  #     Baz.new\n  #   end\n  # end\n  #\n  # class Bar < Foo\n  #   class Baz\n  #   end\n  # end\n  #\n  # Bar.new.method # => #<Foo::Baz:0x10c1a2fc0>\n  # ```\n  #\n  # we have that:\n  #\n  # * `defining_type` is `Foo`, because it's the type that define the method\n  # * `instantiated_type` is `Bar`, because the method was invoked, and thus\n  #    instantiated there\n  #\n  # `defining_type` is needed because when we search types in `method`,\n  # we must search them starting from `Foo`, not from `Bar`.\n  # `instantiated_type` is needed because method resolution will start\n  # from `Bar`.\n  #\n  # TODO: this might slightly change in the future, we should probably instantiate\n  # the method on `Foo+` to avoid having duplicated methods, at least for reference\n  # types, even though that might lead to broader method resolutions (because\n  # a method call in `method` will now be searched in `Foo+`, not in `Bar`)\n  class MatchContext\n    # The type where the method was instantiated\n    property instantiated_type : Type\n\n    # The type that defines the method\n    property defining_type : Type\n\n    # Any instance variables associated with the method instantiation\n    getter bound_free_vars : Hash(String, TypeVar)?\n\n    # Def free variables (`def ... forall X, Y`)\n    property def_free_vars : Array(String)?\n\n    # The type that represents `self` (overriding `instantiated_type`), used to\n    # resolve restrictions properly when a macro def is about to be copied to a\n    # subtype\n    property self_restriction_type : Type?\n\n    def initialize(@instantiated_type, @defining_type, @bound_free_vars = nil, @def_free_vars = nil, @self_restriction_type = nil)\n    end\n\n    def bound_free_var?(name)\n      @bound_free_vars.try &.[name]?\n    end\n\n    def bind_free_var(name, type)\n      bound_free_vars = @bound_free_vars ||= {} of String => TypeVar\n      type = type.remove_literal if type.is_a?(Type)\n      bound_free_vars[name] = type\n    end\n\n    def has_unbound_free_var?(name)\n      return false if bound_free_var?(name)\n      return true if @def_free_vars.try &.includes?(name)\n\n      defining_type.metaclass? && defining_type.type_var?(name)\n    end\n\n    # Returns the type that corresponds to using `self` when looking\n    # a type relative to this context.\n    #\n    # For example, given:\n    #\n    # ```\n    # class Foo\n    #   def foo(&block : self ->)\n    #     ...\n    #   end\n    # end\n    #\n    # class Bar < Foo\n    # end\n    #\n    # Bar.new.foo { |x| }\n    # ```\n    #\n    # it's expected that the block argument `x` will be of type `Bar`, not `Foo`.\n    def self_type : Type\n      instantiated_type.instance_type\n    end\n\n    def clone\n      MatchContext.new(@instantiated_type, @defining_type, @bound_free_vars.dup, @def_free_vars.dup, @self_restriction_type)\n    end\n  end\n\n  # A method lookup match.\n  class Match\n    # The method that was matched\n    getter def : Def\n\n    # The type of the arguments of the matched method.\n    # These might be a subset of the types of the method call because\n    # of restrictions and overloads.\n    getter arg_types : Array(Type)\n\n    # The type of the named arguments of the matched method.\n    # These might be a subset of the types of the method call because\n    # of restrictions and overloads.\n    getter named_arg_types : Array(NamedArgumentType)?\n\n    # Context information associated with this match\n    getter context : MatchContext\n\n    def initialize(@def, @arg_types, @context, @named_arg_types = nil)\n    end\n\n    def remove_literals\n      @arg_types.map!(&.remove_literal)\n      @named_arg_types.try &.map! { |arg| NamedArgumentType.new(arg.name, arg.type.remove_literal) }\n    end\n  end\n\n  struct Matches\n    include Enumerable(Match)\n\n    property matches : Array(Match)?\n    property cover : Bool | Cover | Nil\n    property owner : Type?\n\n    def initialize(@matches, @cover, @owner = nil, @success = true)\n    end\n\n    def cover_all?\n      cover = @cover\n      matches = @matches\n      @success && matches && matches.size > 0 && (cover == true || (cover.is_a?(Cover) && cover.all?))\n    end\n\n    def empty?\n      return true unless @success\n\n      if matches = @matches\n        matches.empty?\n      else\n        true\n      end\n    end\n\n    def each(&)\n      @success && @matches.try &.each do |match|\n        yield match\n      end\n    end\n\n    def size\n      @matches.try(&.size) || 0\n    end\n\n    def [](*args)\n      Matches.new(@matches.try &.[](*args), @cover, @owner, @success)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/math_interpreter.cr",
    "content": "require \"./semantic_visitor\"\n\n# Interprets math expressions like 1 + 2 for enum values and\n# constant values that are being used for the N of a StaticArray.\nstruct Crystal::MathInterpreter\n  def initialize(@path_lookup : Type, @visitor : SemanticVisitor? = nil, @target_type : IntegerType? = nil)\n  end\n\n  def interpret(node : NumberLiteral)\n    case node.kind\n    when .signed_int?, .unsigned_int?\n      target_kind = @target_type.try(&.kind) || node.kind\n      case target_kind\n      when .i8?   then node.value.to_i8? || node.raise \"invalid Int8: #{node.value}\"\n      when .u8?   then node.value.to_u8? || node.raise \"invalid UInt8: #{node.value}\"\n      when .i16?  then node.value.to_i16? || node.raise \"invalid Int16: #{node.value}\"\n      when .u16?  then node.value.to_u16? || node.raise \"invalid UInt16: #{node.value}\"\n      when .i32?  then node.value.to_i32? || node.raise \"invalid Int32: #{node.value}\"\n      when .u32?  then node.value.to_u32? || node.raise \"invalid UInt32: #{node.value}\"\n      when .i64?  then node.value.to_i64? || node.raise \"invalid Int64: #{node.value}\"\n      when .u64?  then node.value.to_u64? || node.raise \"invalid UInt64: #{node.value}\"\n      when .i128? then node.value.to_i128? || node.raise \"invalid Int128: #{node.value}\"\n      when .u128? then node.value.to_u128? || node.raise \"invalid UInt128: #{node.value}\"\n      else\n        node.raise \"enum type must be an integer, not #{target_kind}\"\n      end\n    else\n      node.raise \"constant value must be an integer, not #{node.kind}\"\n    end\n  end\n\n  def interpret(node : Call)\n    obj = node.obj\n    if obj\n      if obj.is_a?(Path)\n        value = interpret_call_macro?(node)\n        return value if value\n      end\n\n      case node.args.size\n      when 0\n        left = interpret(obj)\n\n        case node.name\n        when \"+\" then +left\n        when \"-\"\n          case left\n          when Int8   then -left\n          when Int16  then -left\n          when Int32  then -left\n          when Int64  then -left\n          when Int128 then -left\n          else\n            interpret_call_macro(node)\n          end\n        when \"~\" then ~left\n        else\n          interpret_call_macro(node)\n        end\n      when 1\n        left = interpret(obj)\n        right = interpret(node.args.first)\n\n        case node.name\n        when \"+\"  then left + right\n        when \"-\"  then left - right\n        when \"*\"  then left * right\n        when \"&+\" then left &+ right\n        when \"&-\" then left &- right\n        when \"&*\" then left &* right\n          # MathInterpreter only works with Integer and left / right : Float\n          # when \"/\"  then left / right\n        when \"//\" then left // right\n        when \"&\"  then left & right\n        when \"|\"  then left | right\n        when \"^\"  then left ^ right\n        when \"<<\" then left << right\n        when \">>\" then left >> right\n        when \"%\"  then left % right\n        else\n          interpret_call_macro(node)\n        end\n      else\n        node.raise \"invalid constant value\"\n      end\n    else\n      interpret_call_macro(node)\n    end\n  end\n\n  def interpret_call_macro(node : Call)\n    interpret_call_macro?(node) ||\n      node.raise(\"invalid constant value\")\n  end\n\n  def interpret_call_macro?(node : Call)\n    visitor = @visitor\n    return unless visitor\n\n    if node.global?\n      node.scope = visitor.program\n    else\n      node.scope = visitor.scope? || visitor.current_type.metaclass\n    end\n\n    if visitor.expand_macro(node, raise_on_missing_const: false, first_pass: true)\n      return interpret(node.expanded.not_nil!)\n    end\n\n    nil\n  end\n\n  def interpret(node : Path)\n    type = @path_lookup.lookup_type_var(node)\n    case type\n    when Const\n      interpret(type.value)\n    else\n      node.raise \"invalid constant value\"\n    end\n  end\n\n  def interpret(node : Expressions)\n    if node.expressions.size == 1\n      interpret(node.expressions.first)\n    else\n      node.raise \"invalid constant value\"\n    end\n  end\n\n  def interpret(node : ASTNode)\n    node.raise \"invalid constant value\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/method_lookup.cr",
    "content": "require \"../types\"\n\n# Looking up matches involves two steps:\n#\n# 1. Lookup is done with autocasting disabled.\n#\n# In this scenario as soon as we find an exact match we don't look at other\n# overloads because the exact match will prevent them from being considered.\n#\n# If no matches are found we try again but this time with autocasting enabled.\n# In `semantic/call.cr` this is when `with_autocast` is `true`, and this is when\n# `analyze_all` will be `true` here.\n#\n# 2. Lookup is done with autocasting enabled.\n#\n# In this mode the types for NumberLiteral and SymbolLiteral are not the usual\n# types but instead the special NumberAutocastType and SymbolAutocastType.\n#\n# In this mode we also need to stop as soon as we find an exact match\n# (which just means when the first overload matches with autocasting, which\n# is for example when passing 1 to an Int64 restriction) but we still need\n# to analyze all possible methods in case there's an ambiguity. For example:\n#\n# ```\n# def foo(x : Int64)\n# end\n#\n# def foo(x : Int8)\n# end\n#\n# foo(1)\n# ```\n#\n# In the example above we can't just stop at the first overload because\n# we need to analyze the second overload to find out that the call is ambiguous.\n#\n# However, consider this:\n#\n# ```\n# def foo(x : Int64)\n# end\n#\n# def foo(x : *Int64)\n# end\n#\n# foo(1)\n# ```\n#\n# In this case there's no ambiguity: 1 means `Int64`. However, the first overload\n# is an exact match and there's no need to consider the second overload in the\n# multidispatch. However, we do need to analyze it to check if there's an ambiguity.\n\nmodule Crystal\n  record NamedArgumentType, name : String, type : Type do\n    def self.from_args(named_args : Array(NamedArgument)?, with_autocast = false)\n      named_args.try &.map do |named_arg|\n        new(named_arg.name, named_arg.value.type(with_autocast: with_autocast))\n      end\n    end\n  end\n\n  record CallSignature,\n    name : String,\n    arg_types : Array(Type),\n    block : Block?,\n    named_args : Array(NamedArgumentType)?\n\n  class Type\n    def lookup_matches(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false)\n      matches = lookup_matches_without_parents(signature, owner, path_lookup, matches_array, analyze_all: analyze_all)\n      return matches if matches.cover_all?\n\n      matches_array = matches.matches\n\n      cover = matches.cover\n\n      is_new = owner.metaclass? && signature.name == \"new\"\n      if is_new\n        # For a `new` method we need to do this in case a `new` is defined\n        # in a module type\n        my_parents = instance_type.parents.try &.map(&.metaclass)\n      else\n        my_parents = parents\n      end\n\n      # `new` must only be searched in ancestors if this type itself doesn't define\n      # an `initialize` or `self.new` method. This was already computed in `new.cr`\n      # and can be known by invoking `lookup_new_in_ancestors?`\n      if my_parents && !(is_new && !lookup_new_in_ancestors?)\n        my_parents.each do |parent|\n          matches = parent.lookup_matches(signature, owner, parent, matches_array, analyze_all: analyze_all)\n          if matches.cover_all?\n            return matches\n          else\n            matches_array = matches.matches\n          end\n\n          # If this is a `new` method, once a parent defines an `initialize`\n          # method and we couldn't find any matches we must not go up in the\n          # hierarchy.\n          break if is_new && parent.has_def_without_parents?(signature.name)\n        end\n      end\n\n      Matches.new(matches_array, cover, owner, false)\n    end\n\n    def lookup_matches_without_parents(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false)\n      if defs = self.defs.try &.[signature.name]?\n        context = MatchContext.new(owner, path_lookup)\n\n        exact_match = nil\n\n        defs.each do |item|\n          next if item.def.abstract?\n\n          # If the def has a macro owner, which means that the original\n          # def was defined via a `macro def` and copied to a subtype,\n          # we need to use the type that defined the `macro def` as a\n          # type lookup for arguments.\n          macro_owner = item.def.macro_owner?\n          context.defining_type = macro_owner if macro_owner\n          context.self_restriction_type = item.def.self_restriction_type\n          context.def_free_vars = item.def.free_vars\n          context.bound_free_vars.try &.clear\n\n          match = signature.match(item, context)\n\n          next if exact_match\n\n          if match\n            matches_array ||= [] of Match\n            matches_array << match\n\n            # If the argument types are compatible with the match's argument types,\n            # we are done. We don't just compare types with ==, there is a special case:\n            # a function type with return T can be transpass a restriction of a function\n            # with the same arguments but which returns Void.\n            if signature.matches_exactly?(match)\n              exact_match = Matches.new(matches_array, true, owner)\n              break unless analyze_all\n            end\n\n            context = MatchContext.new(owner, path_lookup)\n          else\n            context.defining_type = path_lookup if macro_owner\n            context.self_restriction_type = nil\n            context.def_free_vars = nil\n            context.bound_free_vars.try &.clear\n          end\n        end\n\n        if exact_match\n          return exact_match\n        end\n      end\n\n      Matches.new(matches_array, Cover.create(signature, matches_array), owner)\n    end\n\n    def lookup_matches_with_modules(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false)\n      matches = lookup_matches_without_parents(signature, owner, path_lookup, matches_array, analyze_all: analyze_all)\n      return matches unless matches.empty?\n\n      is_new = owner.metaclass? && signature.name == \"new\"\n      if is_new\n        # For a `new` method we need to do this in case a `new` is defined\n        # in a module type\n        my_parents = instance_type.parents.try &.map(&.metaclass)\n      else\n        my_parents = parents\n      end\n\n      # `new` must only be searched in ancestors if this type itself doesn't define\n      # an `initialize` or `self.new` method. This was already computed in `new.cr`\n      # and can be known by invoking `lookup_new_in_ancestors?`\n      if my_parents && !(!lookup_new_in_ancestors? && is_new)\n        my_parents.each do |parent|\n          break unless parent.module?\n\n          matches = parent.lookup_matches_with_modules(signature, owner, parent, matches_array, analyze_all: analyze_all)\n          return matches unless matches.empty?\n        end\n      end\n\n      Matches.new(matches_array, Cover.create(signature, matches_array), owner, false)\n    end\n  end\n\n  struct CallSignature\n    def match(def_metadata, context)\n      signature = self\n\n      # If yieldness isn't the same there's no match\n      if def_metadata.yields != !!signature.block\n        return nil\n      end\n\n      # If there are more positional arguments than those required, there's no match\n      # (if there's less they might be matched with named arguments)\n      if signature.arg_types.size > def_metadata.max_size\n        return nil\n      end\n\n      a_def = def_metadata.def\n      arg_types = signature.arg_types\n      named_args = signature.named_args\n      splat_index = a_def.splat_index\n      splat_restriction = a_def.args[splat_index].restriction if splat_index\n      double_splat = a_def.double_splat\n      double_splat_restriction = double_splat.try &.restriction\n\n      # If there are arguments past the splat index and no named args, there's no match,\n      # unless all args past it have default values\n      if splat_index && a_def.args.size > splat_index + 1 && !named_args\n        unless (splat_index + 1...a_def.args.size).all? { |i| a_def.args[i].default_value }\n          return nil\n        end\n      end\n\n      # If there are named args we must check that all mandatory args\n      # are covered by positional arguments or named arguments.\n      if named_args\n        mandatory_args = BitArray.new(a_def.args.size)\n      elsif signature.arg_types.size < def_metadata.min_size\n        # Otherwise, they must be matched by positional arguments\n        return nil\n      end\n\n      matched_arg_types = nil\n      matched_named_arg_types = nil\n\n      # If there's a restriction on a splat (that's not a splat restriction),\n      # zero splatted args don't match\n      if splat_index && splat_restriction &&\n         !splat_restriction.is_a?(Splat) &&\n         Splat.size(a_def, arg_types) == 0\n        return nil\n      end\n\n      if splat_restriction.is_a?(Splat)\n        splat_arg_types = [] of Type\n      end\n\n      a_def.match(arg_types) do |arg, arg_index, arg_type, arg_type_index|\n        # Don't match argument against splat restriction\n        if arg_index == splat_index && splat_arg_types\n          splat_arg_types << arg_type\n          next\n        end\n\n        match_arg_type = arg_type.restrict(arg, context)\n        if match_arg_type\n          if !named_args && !splat_arg_types && match_arg_type.same?(arg_type) && arg_types.size == 1\n            # Optimization: no need to create matched_arg_types if\n            # the call has a single argument and it exactly matches the restriction\n            break\n          end\n\n          matched_arg_types ||= [] of Type\n          matched_arg_types.push match_arg_type\n          mandatory_args[arg_index] = true if mandatory_args\n        else\n          return nil\n        end\n      end\n\n      # Match splat arguments against splat restriction\n      if splat_arg_types && splat_restriction.is_a?(Splat)\n        tuple_type = context.instantiated_type.program.tuple_of(splat_arg_types)\n        match_arg_type = tuple_type.restrict(splat_restriction.exp, context)\n        unless match_arg_type\n          return nil\n        end\n\n        matched_arg_types ||= [] of Type\n        matched_arg_types.concat(splat_arg_types)\n      end\n\n      found_unmatched_named_arg = false\n\n      if double_splat_restriction.is_a?(DoubleSplat)\n        double_splat_entries = [] of NamedArgumentType\n      end\n\n      # Check named args\n      if named_args\n        min_index = signature.arg_types.size\n        named_args.each do |named_arg|\n          found_index = a_def.args.index { |arg| arg.external_name == named_arg.name }\n          if found_index\n            # A named arg can't target the splat index\n            if found_index == splat_index\n              return nil\n            end\n\n            # Check whether the named arg refers to an argument that was already specified\n            if mandatory_args\n              if mandatory_args[found_index]\n                return nil\n              end\n              mandatory_args[found_index] = true\n            else\n              if found_index < min_index\n                return nil\n              end\n            end\n\n            match_arg_type = named_arg.type.restrict(a_def.args[found_index], context)\n            unless match_arg_type\n              return nil\n            end\n\n            matched_named_arg_types ||= [] of NamedArgumentType\n            matched_named_arg_types << NamedArgumentType.new(named_arg.name, match_arg_type)\n          else\n            # If there's a double splat it's OK, the named arg will be put there\n            if a_def.double_splat\n              match_arg_type = named_arg.type\n\n              # If there's a restriction on the double splat, check that it matches\n              if double_splat_restriction\n                if double_splat_entries\n                  double_splat_entries << named_arg\n                else\n                  match_arg_type = named_arg.type.restrict(double_splat_restriction, context)\n                  unless match_arg_type\n                    return nil\n                  end\n                end\n              end\n\n              matched_named_arg_types ||= [] of NamedArgumentType\n              matched_named_arg_types << NamedArgumentType.new(named_arg.name, match_arg_type)\n\n              found_unmatched_named_arg = true\n              next\n            end\n\n            return nil\n          end\n        end\n      end\n\n      # Match double splat arguments against double splat restriction\n      if double_splat_entries && double_splat_restriction.is_a?(DoubleSplat)\n        named_tuple_type = context.instantiated_type.program.named_tuple_of(double_splat_entries)\n        value = named_tuple_type.restrict(double_splat_restriction.exp, context)\n        unless value\n          return nil\n        end\n      end\n\n      # Check that all mandatory args were specified\n      # (either with positional arguments or with named arguments)\n      if mandatory_args\n        a_def.args.each_with_index do |arg, index|\n          if index != splat_index && !arg.default_value && !mandatory_args[index]\n            return nil\n          end\n        end\n      end\n\n      # If there's a restriction on a double splat, zero matching named arguments don't match\n      if double_splat && double_splat_restriction &&\n         !double_splat_restriction.is_a?(DoubleSplat) && !found_unmatched_named_arg\n        return nil\n      end\n\n      # We reuse a match context without free vars, but we create\n      # new ones when there are free vars.\n      context = context.clone if context.bound_free_vars\n\n      Match.new(a_def, (matched_arg_types || arg_types), context, matched_named_arg_types)\n    end\n\n    def matches_exactly?(match : Match, *, with_autocast : Bool = false)\n      arg_types_equal = self.arg_types.equals?(match.arg_types) do |x, y|\n        x.compatible_with?(y)\n      end\n      if (match_named_args = match.named_arg_types) && (signature_named_args = self.named_args) &&\n         match_named_args.size == signature_named_args.size\n        match_named_args = match_named_args.sort_by &.name\n        signature_named_args = signature_named_args.sort_by &.name\n        named_arg_types_equal = signature_named_args.equals?(match_named_args) do |x, y|\n          x.name == y.name && x.type.compatible_with?(y.type)\n        end\n      else\n        named_arg_types_equal = !match.named_arg_types && !self.named_args\n      end\n\n      arg_types_equal && named_arg_types_equal\n    end\n  end\n\n  class AliasType\n    delegate lookup_matches, lookup_matches_without_parents, to: aliased_type\n  end\n\n  module VirtualTypeLookup\n    record Change, type : ModuleType, def : Def\n\n    def virtual_lookup(type)\n      type\n    end\n\n    def lookup_matches(signature, owner = self, path_lookup = self, analyze_all = false)\n      is_new = virtual_metaclass? && signature.name == \"new\"\n\n      base_type_lookup = virtual_lookup(base_type)\n      base_type_matches = base_type_lookup.lookup_matches(signature, self, analyze_all: analyze_all)\n\n      # If there are no subclasses no need to look further\n      if leaf?\n        return base_type_matches\n      end\n\n      base_type_covers_all = base_type_matches.cover_all?\n\n      # If the base type doesn't cover every possible type combination, it's a failure\n      if !base_type.abstract? && !base_type_covers_all\n        return Matches.new(base_type_matches.matches, base_type_matches.cover, base_type_lookup, false)\n      end\n\n      type_to_matches = nil\n      matches = base_type_matches.matches\n      changes = nil\n\n      # Traverse all subtypes\n      instance_type.subtypes(base_type).each do |subtype|\n        next if is_new && subtype.is_a?(GenericClassInstanceType) && subtype.abstract?\n\n        subtype_lookup = virtual_lookup(subtype)\n        subtype_virtual_lookup = virtual_lookup(subtype.virtual_type)\n\n        # Check matches but without parents: only included modules\n        subtype_matches = subtype_lookup.lookup_matches_with_modules(signature, subtype_virtual_lookup, subtype_virtual_lookup, analyze_all: analyze_all)\n\n        # For Foo+.class#new we need to check that this subtype doesn't define\n        # an incompatible initialize: if so, we return empty matches, because\n        # all subtypes must have an initialize with the same number of arguments.\n        if is_new && subtype_matches.empty?\n          other_initializers = subtype_lookup.instance_type.lookup_defs_with_modules(\"initialize\")\n          unless other_initializers.empty?\n            return Matches.new(nil, false)\n          end\n        end\n\n        # If we didn't find a match in a subclass, and the base type match is a macro\n        # def, we need to copy it to the subclass so that @name, @instance_vars and other\n        # macro vars resolve correctly.\n        if subtype_matches.empty?\n          new_subtype_matches = nil\n\n          base_type_matches.each do |base_type_match|\n            if base_type_match.def.macro_def?\n              # We need to force any `self` restrictions in the base type match\n              # to refer to that base type, instead of whichever subtype is\n              # currently used to evaluate the copied def. We must do this even\n              # before any def is actually copied.\n              old_self = base_type_match.def.self_restriction_type\n              base_type_match.def.self_restriction_type = base_type\n\n              # We need to copy each submatch if it's a macro def\n              full_subtype_matches = subtype_lookup.lookup_matches(signature, subtype_virtual_lookup, subtype_virtual_lookup, analyze_all: analyze_all)\n              full_subtype_matches.each do |full_subtype_match|\n                cloned_def = full_subtype_match.def.clone\n                cloned_def.macro_owner = full_subtype_match.def.macro_owner\n                cloned_def.owner = subtype_lookup\n\n                # We want to add this cloned def at the end, because if we search subtype matches\n                # in the next iteration we will find it, and we don't want that.\n                changes ||= [] of Change\n\n                # On a generic instance type, we must add the macro method to the\n                # generic type, not the instance (this will make it so that the method\n                # is found on any generic instance type)\n                change_owner = subtype_lookup\n                change_owner = change_owner.generic_type if change_owner.is_a?(GenericInstanceType)\n\n                if change_owner.is_a?(ModuleType)\n                  changes << Change.new(change_owner, cloned_def)\n                end\n\n                new_subtype_matches ||= [] of Match\n                new_subtype_matches.push Match.new(cloned_def, full_subtype_match.arg_types, MatchContext.new(subtype_lookup, full_subtype_match.context.defining_type, full_subtype_match.context.bound_free_vars), full_subtype_match.named_arg_types)\n              end\n\n              # Reset the `self` restriction override\n              base_type_match.def.self_restriction_type = old_self\n            end\n          end\n\n          if new_subtype_matches\n            subtype_matches = Matches.new(new_subtype_matches, Cover.create(signature, new_subtype_matches))\n          end\n        end\n\n        if !subtype.leaf? && subtype_matches.size > 0\n          type_to_matches ||= {} of Type => Matches\n          type_to_matches[subtype] = subtype_matches\n        end\n\n        # If the subtype is non-abstract but doesn't cover all,\n        # we need to check if a parent covers it\n        if !subtype.abstract? && !base_type_covers_all && !subtype_matches.cover_all?\n          unless covered_by_superclass?(subtype, type_to_matches)\n            return Matches.new(subtype_matches.matches, subtype_matches.cover, subtype_lookup, false)\n          end\n        end\n\n        if !subtype_matches.empty? && (subtype_matches_matches = subtype_matches.matches)\n          if subtype.abstract? && !self.is_a?(VirtualMetaclassType) && subtype.subclasses.empty?\n            # No need to add matches if for an abstract class without subclasses\n          else\n            # We need to insert the matches before the previous ones\n            # because subtypes are more specific matches\n            if matches\n              subtype_matches_matches.concat matches\n            end\n            matches = subtype_matches_matches\n          end\n        end\n      end\n\n      changes.try &.each do |change|\n        change.type.add_def change.def\n      end\n\n      Matches.new(matches, !!(matches && matches.size > 0), self)\n    end\n\n    def covered_by_superclass?(subtype, type_to_matches)\n      superclass = subtype.superclass\n      while superclass && superclass != base_type\n        superclass_matches = type_to_matches.try &.[superclass]?\n        if superclass_matches && superclass_matches.cover_all?\n          return true\n        end\n        superclass = superclass.superclass\n      end\n      false\n    end\n  end\n\n  class VirtualMetaclassType\n    def virtual_lookup(type)\n      type.metaclass\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/method_missing.cr",
    "content": "require \"../types\"\n\nmodule Crystal\n  class Type\n    ONE_ARG = [Arg.new(\"a1\")]\n\n    def check_method_missing(signature, call)\n      if !metaclass? && signature.name != \"initialize\"\n        # Make sure to define method missing in the whole hierarchy\n        virtual_type = virtual_type()\n        if virtual_type == self\n          method_missing = lookup_method_missing\n          if method_missing\n            define_method_from_method_missing(method_missing, signature, call)\n            return true\n          end\n        else\n          return virtual_type.check_method_missing(signature, call)\n        end\n      end\n\n      false\n    end\n\n    def lookup_method_missing\n      a_macro = lookup_macro(\"method_missing\", ONE_ARG, nil)\n      a_macro.is_a?(Macro) ? a_macro : nil\n    end\n\n    def define_method_from_method_missing(method_missing, signature, original_call)\n      args_nodes = [] of ASTNode\n      named_args_nodes = nil\n      args_nodes_names = [] of {String?, String} # external <-> internal name\n      signature.arg_types.each_index do |index|\n        arg_node_name = \"_arg#{index}\"\n        args_nodes << MacroId.new(arg_node_name)\n        args_nodes_names << {nil, arg_node_name}\n      end\n      if named_args = signature.named_args\n        args_nodes_names << {nil, \"\"}\n        named_args.each_with_index do |named_arg, index|\n          named_arg_node_name = \"_named_arg#{index}\"\n          named_args_nodes ||= [] of NamedArgument\n          named_args_nodes << NamedArgument.new(named_arg.name, MacroId.new(named_arg_node_name))\n          args_nodes_names << {named_arg.name, named_arg_node_name}\n        end\n      end\n      if block = signature.block\n        block_vars = block.args.map_with_index do |_, index|\n          Var.new(\"_block_arg#{index}\")\n        end\n        yield_exps = block_vars.map { |var| var.clone.as(ASTNode) }\n        block_body = Yield.new(yield_exps)\n        block_node = Block.new(block_vars, block_body)\n      else\n        block_node = Nop.new\n      end\n\n      a_def = Def.new(signature.name, args_nodes_names.map { |ext_name, name| Arg.new(name, external_name: ext_name) })\n      a_def.splat_index = signature.arg_types.size if signature.named_args\n\n      call = Call.new(signature.name,\n        args: args_nodes,\n        named_args: named_args_nodes,\n        block: block_node.is_a?(Block) ? block_node : nil)\n      fake_call = Call.new(\"method_missing\", call)\n\n      expanded_macro, macro_expansion_pragmas = program.expand_macro method_missing, fake_call, self, self\n\n      # Check if the expanded macro is a def. We do this\n      # by just lexing the result and seeing if the first\n      # token is `def`\n      macro_vars = Set(String).new\n      args_nodes_names.each { |_, name| macro_vars << name }\n      expands_to_def = starts_with_def?(expanded_macro)\n      generated_nodes =\n        program.parse_macro_source(expanded_macro, macro_expansion_pragmas, method_missing, method_missing, macro_vars) do |parser|\n          if expands_to_def\n            parser.parse\n          else\n            parser.parse_to_def(a_def)\n          end\n        end\n\n      if generated_nodes.is_a?(Def)\n        a_def = generated_nodes\n      else\n        if expands_to_def\n          raise_wrong_method_missing_expansion(\n            \"it should only expand to a single def\",\n            expanded_macro,\n            original_call)\n        end\n\n        a_def.body = generated_nodes\n        a_def.block_arity = block.try &.args.size\n      end\n\n      owner = self\n      owner = owner.base_type if owner.is_a?(VirtualType)\n\n      return false unless owner.is_a?(ModuleType)\n\n      owner.add_def(a_def)\n\n      # If it expanded to a def, we check if the def\n      # is now found by regular lookup. It should!\n      # Otherwise there's a mistake in the macro.\n      if expands_to_def && owner.lookup_matches(signature).empty?\n        raise_wrong_method_missing_expansion(\n          \"the generated method won't be found by the original call invocation\",\n          expanded_macro,\n          original_call)\n      end\n\n      true\n    end\n\n    private def raise_wrong_method_missing_expansion(msg, expanded_macro, original_call)\n      str = String.build do |io|\n        io << \"wrong method_missing expansion\\n\\n\"\n        io << \"The method_missing macro expanded to:\\n\\n\"\n        io << Crystal.with_line_numbers(expanded_macro)\n        io << \"\\n\\n\"\n        io << \"However, \" << msg\n      end\n      original_call.raise str\n    end\n  end\n\n  class GenericInstanceType\n    delegate check_method_missing, to: @generic_type\n  end\n\n  class VirtualType\n    def check_method_missing(signature, call)\n      method_missing = base_type.lookup_method_missing\n      defined = false\n      if method_missing\n        defined = base_type.define_method_from_method_missing(method_missing, signature, call) || defined\n      end\n\n      defined = add_subclasses_method_missing_matches(base_type, method_missing, signature, call) || defined\n      defined\n    end\n\n    def add_subclasses_method_missing_matches(base_type, method_missing, signature, call)\n      defined = false\n\n      base_type.subclasses.each do |subclass|\n        next unless subclass.is_a?(ModuleType)\n\n        # First check if we can find the method\n        existing_def = subclass.lookup_first_def(signature.name, signature.block)\n        next if existing_def\n\n        subclass_method_missing = subclass.lookup_method_missing\n\n        # Check if the subclass redefined the method_missing\n        if subclass_method_missing && subclass_method_missing.object_id != method_missing.object_id\n          subclass.define_method_from_method_missing(subclass_method_missing, signature, call)\n          defined = true\n        elsif method_missing\n          # Otherwise, we need to define this method missing because of macro vars like @name\n          subclass.define_method_from_method_missing(method_missing, signature, call)\n          subclass_method_missing = method_missing\n          defined = true\n        end\n\n        defined = add_subclasses_method_missing_matches(subclass, subclass_method_missing, signature, call) || defined\n      end\n\n      defined\n    end\n  end\nend\n\nprivate def starts_with_def?(source)\n  lexer = Crystal::Lexer.new(source)\n  while true\n    token = lexer.next_token\n    return true if token.keyword?(:def)\n    break if token.type.eof?\n  end\n  false\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/new.cr",
    "content": "module Crystal\n  class Program\n    def define_new_methods(new_expansions)\n      # Here we complete the body of `self.new` methods\n      # created from `initialize` methods.\n      new_expansions.each do |original, expanded|\n        expanded.fill_body_from_initialize(original.owner)\n      end\n\n      # We also need to define empty `new` methods for types\n      # that don't have any `initialize` methods.\n      define_default_new(self)\n      file_modules.each_value do |file_module|\n        define_default_new(file_module)\n      end\n\n      # Once we are done with the expansions we mark `initialize` methods\n      # without an explicit visibility as `protected`.\n      new_expansions.each do |original, expanded|\n        original.visibility = Visibility::Protected if original.visibility.public?\n      end\n    end\n\n    def define_default_new(type)\n      type.types?.try &.each_value do |type|\n        define_default_new_single(type)\n      end\n    end\n\n    def define_default_new_single(type)\n      check = case type\n              when self.object, self.value, self.number, self.int, self.float,\n                   self.struct, self.enum, self.tuple, self.proc\n                false\n              when NonGenericClassType, GenericClassType\n                true\n              else\n                false\n              end\n\n      if check\n        type = type.as(ModuleType)\n\n        self_initialize_methods = type.lookup_defs_without_parents(\"initialize\")\n        self_new_methods = type.metaclass.lookup_defs_without_parents(\"new\")\n\n        # Check to see if a default `new` needs to be defined\n        initialize_methods = type.lookup_defs(\"initialize\", lookup_ancestors_for_new: true)\n        new_methods = type.metaclass.lookup_defs(\"new\", lookup_ancestors_for_new: true)\n        has_new_or_initialize = !initialize_methods.empty? || !new_methods.empty?\n\n        if !has_new_or_initialize\n          # Add self.new\n          new_method = Def.argless_new(type)\n          type.metaclass.as(ModuleType).add_def(new_method)\n\n          # Also add `initialize`, so `super` in a subclass\n          # inside an `initialize` will find this one\n          type.add_def Def.argless_initialize(type)\n        end\n\n        # Check to see if a type doesn't define `initialize`\n        # nor `self.new()` on its own. In this case, when we\n        # search a `new` method and we can't find it in this\n        # type we must search in the superclass. We record\n        # this information here instead of having to do this\n        # check every time.\n        has_self_initialize_methods = !self_initialize_methods.empty?\n        if !has_self_initialize_methods\n          is_generic = type.is_a?(GenericClassType)\n          inherits_from_generic = type.ancestors.any?(GenericClassInstanceType)\n          if is_generic || inherits_from_generic\n            has_default_self_new = self_new_methods.any?(&.has_any_args?.!)\n\n            # For a generic class type we need to define `new` even\n            # if a superclass defines it, because the generated new\n            # uses, for example, Foo(T) to match free vars, and here\n            # we might need Bar(T) with Bar(T) < Foo(T).\n            # (we can probably improve this in the future)\n            if initialize_methods.empty?\n              # If the type has `self.new()`, don't override it\n              unless has_default_self_new\n                type.metaclass.as(ModuleType).add_def(Def.argless_new(type))\n                type.add_def(Def.argless_initialize(type))\n              end\n            else\n              initialize_owner = nil\n\n              initialize_methods.each do |initialize|\n                # If the type has `self.new()`, don't override it\n                if !initialize.has_any_args? && has_default_self_new\n                  next\n                end\n\n                # Only copy initialize methods from the first ancestor that has them\n                if initialize_owner && initialize.owner != initialize_owner\n                  break\n                end\n\n                initialize_owner = initialize.owner\n\n                new_method = initialize.expand_new_from_initialize(type)\n                type.metaclass.as(ModuleType).add_def(new_method)\n              end\n\n              # Copy non-generated `new` methods from parent to child\n              new_methods.each do |new_method|\n                next if new_method.new?\n\n                type.metaclass.as(ModuleType).add_def(new_method.clone)\n              end\n            end\n          else\n            type.as(ClassType).lookup_new_in_ancestors = true\n          end\n        end\n      end\n\n      define_default_new(type)\n    end\n  end\n\n  class Def\n    def expand_new_from_initialize(instance_type)\n      new_def = expand_new_signature_from_initialize(instance_type)\n      new_def.fill_body_from_initialize(instance_type)\n      new_def\n    end\n\n    def expand_new_signature_from_initialize(instance_type)\n      def_args = args.clone\n\n      new_def = Def.new(\"new\", def_args, Nop.new).at(self)\n      new_def.splat_index = splat_index\n      new_def.double_splat = double_splat.clone\n      new_def.block_arity = block_arity\n      new_def.visibility = visibility\n      new_def.new = true\n      new_def.doc = doc\n      new_def.free_vars = free_vars\n      new_def.annotations = annotations\n\n      # Forward block argument if any\n      if uses_block_arg?\n        block_arg = self.block_arg.not_nil!\n        new_def.block_arg = block_arg.clone\n        new_def.uses_block_arg = true\n      end\n\n      new_def\n    end\n\n    def fill_body_from_initialize(instance_type)\n      if instance_type.is_a?(GenericClassType)\n        generic_type_args = instance_type.type_vars.map_with_index do |type_var, i|\n          arg = Path.new(type_var).as(ASTNode).at(self)\n          arg = Splat.new(arg).at(self) if instance_type.splat_index == i\n          arg\n        end\n        new_generic = Generic.new(Path.new(instance_type.name), generic_type_args).at(self)\n        alloc = Call.new(new_generic, \"allocate\").at(self)\n      else\n        alloc = Call.new(\"allocate\").at(self)\n      end\n\n      # This creates:\n      #\n      #    x = allocate\n      #    x.initialize ..., &block\n      #    GC.add_finalizer x if x.responds_to? :finalize\n      #    x\n      obj = Var.new(\"_\")\n\n      new_vars = [] of ASTNode\n      named_args = nil\n      splat_index = self.splat_index\n\n      args.each_with_index do |arg, i|\n        # This is the case of a bare splat argument\n        next if arg.name.empty?\n\n        # Check if the argument has to be passed as a named argument\n        if splat_index && i > splat_index\n          named_args ||= [] of NamedArgument\n          named_args << NamedArgument.new(arg.external_name, Var.new(arg.name).at(self)).at(self)\n        else\n          new_var = Var.new(arg.name).at(self)\n          new_var = Splat.new(new_var).at(self) if i == splat_index\n          new_vars << new_var\n        end\n      end\n\n      # Make sure to forward the double splat argument\n      if double_splat = self.double_splat\n        new_vars << DoubleSplat.new(Var.new(double_splat.name).at(self)).at(self)\n      end\n\n      assign = Assign.new(obj.clone, alloc).at(self)\n      init = Call.new(obj.clone, \"initialize\", new_vars, named_args: named_args).at(self)\n\n      # If the initialize yields, call it with a block\n      # that yields those arguments.\n      if block_args_count = self.block_arity\n        block_args = Array.new(block_args_count) { |i| Var.new(\"_arg#{i}\") }\n        vars = Array.new(block_args_count) { |i| Var.new(\"_arg#{i}\").at(self).as(ASTNode) }\n        init.block = Block.new(block_args, Yield.new(vars).at(self)).at(self)\n      end\n\n      exps = Array(ASTNode).new(4)\n      exps << assign\n      exps << init\n      exps << If.new(RespondsTo.new(obj.clone, \"finalize\").at(self),\n        Call.new(Path.global(\"GC\").at(self), \"add_finalizer\", obj.clone).at(self))\n      exps << obj\n\n      # Forward block argument if any\n      if uses_block_arg?\n        block_arg = self.block_arg.not_nil!\n        init.block_arg = Var.new(block_arg.name).at(self)\n      end\n\n      self.body = Expressions.from(exps).at(self.body)\n    end\n\n    def self.argless_new(instance_type)\n      loc = instance_type.locations.try &.first?\n\n      # This creates:\n      #\n      #    def new\n      #      x = allocate\n      #      GC.add_finalizer x if x.responds_to? :finalize\n      #      x\n      #    end\n      var = Var.new(\"x\").at(loc)\n      alloc = Call.new(\"allocate\").at(loc)\n      assign = Assign.new(var, alloc).at(loc)\n\n      call = Call.new(Path.global(\"GC\").at(loc), \"add_finalizer\", var.clone).at(loc)\n      exps = Array(ASTNode).new(3)\n      exps << assign\n      exps << If.new(RespondsTo.new(var.clone, \"finalize\").at(loc), call).at(loc)\n      exps << var.clone\n\n      a_def = Def.new(\"new\", body: exps).at(loc)\n      a_def.new = true\n      a_def\n    end\n\n    def self.argless_initialize(instance_type)\n      loc = instance_type.locations.try &.first?\n      Def.new(\"initialize\", body: Nop.new).at(loc)\n    end\n\n    def expand_new_default_arguments(instance_type, args_size, named_args)\n      def_args = [] of Arg\n\n      # Positional args\n      if splat_index = self.splat_index\n        before_splat_size = Math.min(args_size, splat_index)\n      else\n        before_splat_size = args_size\n      end\n\n      before_splat_size.times do |i|\n        new_arg = Arg.new(\"__arg#{i}\")\n        new_arg.annotations = args[i].annotations\n        new_arg.original_name = args[i].original_name\n        def_args << new_arg\n      end\n\n      # Splat args\n      splat_size = 0\n\n      if splat_index\n        splat_arg = args[splat_index]\n\n        splat_size = args_size - splat_index\n        splat_size = 0 if splat_size < 0\n\n        splat_size.times do |i|\n          new_arg = Arg.new(\"__arg#{before_splat_size + i}\")\n          new_arg.annotations = splat_arg.annotations\n          new_arg.original_name = splat_arg.original_name\n          def_args << new_arg\n        end\n      end\n\n      # Named args & double splat arg\n      new_splat_index = nil\n\n      if named_args\n        def_args << Arg.new(\"\")\n        new_splat_index = args_size\n\n        name = String.build do |str|\n          str << \"new\"\n          named_args.each do |named_arg|\n            str << ':'\n            str << named_arg\n\n            # When **opts is expanded for named arguments, we must use internal\n            # names that won't clash with local variables defined in the method.\n            temp_name = instance_type.program.new_temp_var_name(self)\n            def_arg = Arg.new(temp_name, external_name: named_arg)\n\n            if matching_arg = args.find { |arg| arg.external_name == named_arg }\n              def_arg.annotations = matching_arg.annotations.dup\n              def_arg.original_name = matching_arg.original_name\n            elsif double_splat = self.double_splat\n              def_arg.annotations = double_splat.annotations.dup\n              def_arg.original_name = double_splat.original_name\n            end\n\n            def_args << def_arg\n          end\n        end\n      else\n        name = \"new\"\n      end\n\n      expansion = Def.new(name, def_args, Nop.new, splat_index: new_splat_index).at(self)\n      expansion.block_arity = block_arity\n      expansion.visibility = visibility\n      expansion.annotations = annotations\n\n      if uses_block_arg?\n        block_arg = self.block_arg.not_nil!\n        expansion.block_arg = block_arg.clone\n        expansion.uses_block_arg = true\n      end\n      expansion.fill_body_from_initialize(instance_type)\n\n      if owner = self.owner?\n        expansion.owner = owner\n      end\n\n      # Remove the splat index: we just needed it so that named arguments\n      # are passed as named arguments to the initialize call\n      if new_splat_index\n        expansion.splat_index = nil\n        expansion.args.delete_at(new_splat_index)\n      end\n\n      expansion\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/normalizer.cr",
    "content": "require \"set\"\nrequire \"../program\"\nrequire \"../syntax/transformer\"\n\nmodule Crystal\n  class Program\n    def normalize(node, inside_exp = false, current_def = nil)\n      normalizer = Normalizer.new(self)\n      normalizer.current_def = current_def\n      node.transform(normalizer)\n    end\n  end\n\n  class Normalizer < Transformer\n    getter program : Program\n\n    # The current method where we are normalizing.\n    # This is used to expand argless `super` and `previous_def`\n    # to their version with arguments copied from the current method.\n    property current_def : Def?\n\n    @dead_code = false\n\n    def initialize(@program)\n    end\n\n    def before_transform(node)\n      @dead_code = false\n    end\n\n    def after_transform(node)\n      case node\n      when Return, Break, Next\n        @dead_code = true\n      when If, Unless, Expressions, Block, Assign\n        # Skip\n      else\n        @dead_code = false\n      end\n    end\n\n    def transform(node : Expressions)\n      exps = [] of ASTNode\n      node.expressions.each do |exp|\n        new_exp = exp.transform(self)\n        if new_exp\n          if new_exp.is_a?(Expressions)\n            exps.concat new_exp.expressions\n          else\n            exps << new_exp\n          end\n        end\n        break if @dead_code\n      end\n      case exps.size\n      when 0\n        Nop.new\n      else\n        node.expressions = exps\n        node\n      end\n    end\n\n    def transform(node : Call)\n      # Copy enclosing def's parameters to super/previous_def without parenthesis\n      case node\n      when .super?, .previous_def?\n        named_args = node.named_args\n        if node.args.empty? && (!named_args || named_args.empty?) && !node.has_parentheses?\n          if current_def = @current_def\n            splat_index = current_def.splat_index\n            current_def.args.each_with_index do |arg, i|\n              if splat_index && i > splat_index\n                # Past the splat index we must pass arguments as named arguments\n                named_args = node.named_args ||= Array(NamedArgument).new\n                named_args.push NamedArgument.new(arg.external_name, Var.new(arg.name))\n              elsif i == splat_index\n                # At the splat index we must use a splat, except the bare splat\n                # parameter will be skipped\n                unless arg.external_name.empty?\n                  node.args.push Splat.new(Var.new(arg.name))\n                end\n              else\n                # Otherwise it's just a regular argument\n                node.args.push Var.new(arg.name)\n              end\n            end\n\n            # Copy also the double splat\n            if arg = current_def.double_splat\n              node.args.push DoubleSplat.new(Var.new(arg.name))\n            end\n          end\n          node.has_parentheses = true\n        end\n      else\n        # not a special call\n      end\n\n      # Convert 'a <= b <= c' to 'a <= b && b <= c'\n      if comparison?(node.name) && (obj = node.obj) && obj.is_a?(Call) && comparison?(obj.name)\n        case middle = obj.args.first\n        when NumberLiteral, Var, InstanceVar\n          transform_many node.args\n          left = obj\n          right = Call.new(middle.clone, node.name, node.args).at(middle)\n        else\n          temp_var = program.new_temp_var\n          temp_assign = Assign.new(temp_var.clone, middle).at(middle)\n          left = Call.new(obj.obj, obj.name, temp_assign).at(obj.obj)\n          right = Call.new(temp_var.clone, node.name, node.args).at(node)\n        end\n        node = And.new(left, right).at(left)\n        node = node.transform self\n      else\n        node = super\n      end\n\n      node\n    end\n\n    def comparison?(name)\n      case name\n      when \"<=\", \"<\", \"!=\", \"==\", \"===\", \">\", \">=\"\n        true\n      else\n        false\n      end\n    end\n\n    def transform(node : Def)\n      @current_def = node\n      node = super\n      @current_def = nil\n\n      # If the def has a block argument without a specification\n      # and it doesn't use it, we remove it because it's useless\n      # and the semantic code won't have to bother checking it\n      block_arg = node.block_arg\n      if !node.uses_block_arg? && block_arg\n        block_arg_restriction = block_arg.restriction\n        if block_arg_restriction.is_a?(ProcNotation) && !block_arg_restriction.inputs && !block_arg_restriction.output\n          node.block_arg = nil\n        elsif !block_arg_restriction\n          node.block_arg = nil\n        end\n      end\n\n      node\n    end\n\n    def transform(node : Macro)\n      node\n    end\n\n    def transform(node : If)\n      node.cond = node.cond.transform(self)\n\n      node.then = node.then.transform(self)\n      then_dead_code = @dead_code\n\n      node.else = node.else.transform(self)\n      else_dead_code = @dead_code\n\n      @dead_code = then_dead_code && else_dead_code\n      node\n    end\n\n    # Convert unless to if:\n    #\n    # From:\n    #\n    #     unless foo\n    #       bar\n    #     else\n    #       baz\n    #     end\n    #\n    # To:\n    #\n    #     if foo\n    #       baz\n    #     else\n    #       bar\n    #     end\n    def transform(node : Unless)\n      If.new(node.cond, node.else, node.then).transform(self).at(node)\n    end\n\n    # Convert until to while:\n    #\n    # From:\n    #\n    #    until foo\n    #      bar\n    #    end\n    #\n    # To:\n    #\n    #    while !foo\n    #      bar\n    #    end\n    def transform(node : Until)\n      node = super\n      not_exp = Not.new(node.cond).at(node.cond)\n      While.new(not_exp, node.body).at(node)\n    end\n\n    # Checks if the right hand side is dead code\n    def transform(node : Assign)\n      super\n\n      if @dead_code\n        node.value\n      else\n        node\n      end\n    end\n\n    # Convert `a += b` to `a = a + b`\n    def transform(node : OpAssign)\n      super\n\n      target = node.target\n      if target.is_a?(Call)\n        if target.name == \"[]\"\n          transform_op_assign_index(node, target)\n        else\n          transform_op_assign_call(node, target)\n        end\n      else\n        transform_op_assign_simple(node, target)\n      end\n    end\n\n    def transform_op_assign_call(node, target)\n      obj = target.obj.not_nil!\n\n      # Convert\n      #\n      #     a.exp += b\n      #\n      # To\n      #\n      #     tmp = a\n      #     tmp.exp=(tmp.exp + b)\n      case obj\n      when Var, InstanceVar, ClassVar, .simple_literal?\n        tmp = obj\n      else\n        tmp = program.new_temp_var\n\n        # (1) = tmp = a\n        assign = Assign.new(tmp, obj).at(node)\n      end\n\n      # (2) = tmp.exp\n      call = Call.new(tmp.clone, target.name).at(node)\n      call.name_location = node.name_location\n\n      case node.op\n      when \"||\"\n        # Special: tmp.exp || tmp.exp=(b)\n        #\n        # (3) = tmp.exp=(b)\n        right = Call.new(tmp.clone, \"#{target.name}=\", node.value).at(node)\n        right.name_location = node.name_location\n\n        # (4) = (2) || (3)\n        call = Or.new(call, right).at(node)\n      when \"&&\"\n        # Special: tmp.exp && tmp.exp=(b)\n        #\n        # (3) = tmp.exp=(b)\n        right = Call.new(tmp.clone, \"#{target.name}=\", node.value).at(node)\n        right.name_location = node.name_location\n\n        # (4) = (2) && (3)\n        call = And.new(call, right).at(node)\n      else\n        # (3) = (2) + b\n        call = Call.new(call, node.op, node.value).at(node)\n        call.name_location = node.name_location\n\n        # (4) = tmp.exp=((3))\n        call = Call.new(tmp.clone, \"#{target.name}=\", call).at(node)\n        call.name_location = node.name_location\n      end\n\n      # (1); (4)\n      if assign\n        Expressions.new([assign, call] of ASTNode).at(node)\n      else\n        call\n      end\n    end\n\n    def transform_op_assign_index(node, target)\n      obj = target.obj.not_nil!\n\n      # Convert\n      #\n      #     a[exp1, exp2, ...] += b\n      #\n      # To\n      #\n      #     tmp = a\n      #     tmp1 = exp1\n      #     tmp2 = exp2\n      #     ...\n      #     tmp.[]=(tmp1, tmp2, ..., tmp[tmp1, tmp2, ...] + b)\n      tmp_args = target.args.map { program.new_temp_var(node).as(ASTNode) }\n      tmp = program.new_temp_var(node)\n\n      # (1) = tmp1 = exp1; tmp2 = exp2; ...; tmp = a\n      tmp_assigns = Array(ASTNode).new(tmp_args.size + 1)\n      tmp_args.each_with_index do |var, i|\n        # For simple literals we don't need a temp variable\n        arg = target.args[i]\n        if arg.simple_literal?\n          tmp_args[i] = arg\n        else\n          tmp_assigns << Assign.new(var.clone, arg).at(node)\n        end\n      end\n\n      case obj\n      when Var, InstanceVar, ClassVar, .simple_literal?\n        # Nothing\n        tmp = obj\n      else\n        tmp_assigns << Assign.new(tmp, obj).at(node)\n      end\n\n      case node.op\n      when \"||\"\n        # Special: tmp[tmp1, tmp2, ...]? || (tmp[tmp1, tmp2, ...] = b)\n        #\n        # (2) = tmp[tmp1, tmp2, ...]?\n        call = Call.new(tmp.clone, \"[]?\", tmp_args).at(node)\n        call.name_location = node.name_location\n\n        # (3) = tmp[tmp1, tmp2, ...] = b\n        args = Array(ASTNode).new(tmp_args.size + 1)\n        tmp_args.each { |arg| args << arg.clone }\n        args << node.value\n        right = Call.new(tmp.clone, \"[]=\", args).at(node)\n        right.name_location = node.name_location\n\n        # (3) = (2) || (4)\n        call = Or.new(call, right).at(node)\n      when \"&&\"\n        # Special: tmp[tmp1, tmp2, ...]? && (tmp[tmp1, tmp2, ...] = b)\n        #\n        # (2) = tmp[tmp1, tmp2, ...]?\n        call = Call.new(tmp.clone, \"[]?\", tmp_args).at(node)\n        call.name_location = node.name_location\n\n        # (3) = tmp[tmp1, tmp2, ...] = b\n        args = Array(ASTNode).new(tmp_args.size + 1)\n        tmp_args.each { |arg| args << arg.clone }\n        args << node.value\n        right = Call.new(tmp.clone, \"[]=\", args).at(node)\n        right.name_location = node.name_location\n\n        # (3) = (2) && (4)\n        call = And.new(call, right).at(node)\n      else\n        # (2) = tmp[tmp1, tmp2, ...]\n        call = Call.new(tmp.clone, \"[]\", tmp_args).at(node)\n        call.name_location = node.name_location\n\n        # (3) = (2) + b\n        call = Call.new(call, node.op, node.value).at(node)\n        call.name_location = node.name_location\n\n        # (4) tmp.[]=(tmp1, tmp2, ..., (3))\n        args = Array(ASTNode).new(tmp_args.size + 1)\n        tmp_args.each { |arg| args << arg.clone }\n        args << call\n        call = Call.new(tmp.clone, \"[]=\", args).at(node)\n        call.name_location = node.name_location\n      end\n\n      # (1); (4)\n      if tmp_assigns.empty?\n        call\n      else\n        exps = Array(ASTNode).new(tmp_assigns.size + 2)\n        exps.concat(tmp_assigns)\n        exps << call\n\n        Expressions.new(exps).at(node)\n      end\n    end\n\n    def transform_op_assign_simple(node, target)\n      case node.op\n      when \"&&\"\n        # (1) a = b\n        assign = Assign.new(target, node.value).at(node)\n\n        # a && (1)\n        And.new(target.clone, assign).at(node)\n      when \"||\"\n        # (1) a = b\n        assign = Assign.new(target, node.value).at(node)\n\n        # a || (1)\n        Or.new(target.clone, assign).at(node)\n      else\n        # (1) = a + b\n        call = Call.new(target, node.op, node.value).at(node)\n        call.name_location = node.name_location\n\n        # a = (1)\n        Assign.new(target.clone, call).at(node)\n      end\n    end\n\n    def transform(node : StringInterpolation)\n      # If the interpolation has just one string literal inside it,\n      # return that instead of an interpolation\n      if node.expressions.size == 1\n        first = node.expressions.first\n        return first if first.is_a?(StringLiteral)\n      end\n\n      super\n    end\n\n    # Turn block argument unpacking to multi assigns at the beginning\n    # of a block.\n    #\n    # So this:\n    #\n    #    foo do |(x, y), z|\n    #      x + y + z\n    #    end\n    #\n    # is transformed to:\n    #\n    #    foo do |__temp_1, z|\n    #      x, y = __temp_1\n    #      x + y + z\n    #    end\n    def transform(node : Block)\n      node = super\n\n      unpacks = node.unpacks\n      return node unless unpacks\n\n      # as `node` is mutated in-place, ensure it can only be mutated once\n      # we consider a block to be mutated if any unpack already has a\n      # corresponding block parameter with a name (as the fictitious packed\n      # parameters have empty names)\n      return node if unpacks.any? { |index, _| !node.args[index].name.empty? }\n\n      extra_expressions = [] of ASTNode\n      next_unpacks = [] of {String, Expressions}\n\n      unpacks.each do |index, expressions|\n        temp_name = program.new_temp_var_name\n        node.args[index] = Var.new(temp_name).at(node.args[index])\n\n        extra_expressions << block_unpack_multiassign(temp_name, expressions, next_unpacks)\n      end\n\n      if next_unpacks\n        while next_unpack = next_unpacks.shift?\n          var_name, expressions = next_unpack\n\n          extra_expressions << block_unpack_multiassign(var_name, expressions, next_unpacks)\n        end\n      end\n\n      body = node.body\n      case body\n      when Nop\n        node.body = Expressions.new(extra_expressions).at(node.body)\n      when Expressions\n        body.expressions = extra_expressions + body.expressions\n      else\n        extra_expressions << node.body\n        node.body = Expressions.new(extra_expressions).at(node.body)\n      end\n\n      node\n    end\n\n    private def block_unpack_multiassign(var_name, expressions, next_unpacks)\n      targets = expressions.expressions.map do |exp|\n        case exp\n        when Var\n          exp\n        when Underscore\n          exp\n        when Splat\n          exp\n        when Expressions\n          next_temp_name = program.new_temp_var_name\n\n          next_unpacks << {next_temp_name, exp}\n\n          Var.new(next_temp_name).at(exp)\n        else\n          raise \"BUG: unexpected block var #{exp} (#{exp.class})\"\n        end\n      end\n      values = [Var.new(var_name).at(expressions)] of ASTNode\n      MultiAssign.new(targets, values).at(expressions)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/path_lookup.cr",
    "content": "module Crystal\n  class Type\n    # Looks up a *path* (for example: `Foo::Bar::Baz`) relative to `self`.\n    #\n    # For example, given:\n    #\n    # ```\n    # class Foo\n    #   class Bar\n    #     class Baz\n    #     end\n    #   end\n    # end\n    # ```\n    #\n    # If `self` is `Foo` and we invoke `lookup_path([\"Bar\", \"Baz\"])` on it,\n    # we'll get `Foo::Bar::Baz` as the return value.\n    #\n    # The path is searched in the current type's ancestors, and optionally\n    # in its namespace, according to *lookup_in_namespace*.\n    #\n    # Returns `nil` if the path can't be found.\n    #\n    # *include_private* controls whether private types are found inside\n    # other types (when doing Foo::Bar, Bar won't be found if it's private).\n    #\n    # *location* can be passed and is the location where the lookup happens,\n    # and is useful to find file-private types.\n    #\n    # The result can be an `ASTNode` in the case the path denotes a type variable\n    # whose variable is an `ASTNode`. One such example is the `N` of `StaticArray(T, N)`\n    # for some instantiated `StaticArray`.\n    #\n    # If the path is global (for example ::Foo::Bar), the search starts at\n    # the top level.\n    def lookup_path(path : Path, lookup_in_namespace = true, include_private = false, location = path.location) : Type | ASTNode | Nil\n      location = nil if path.global?\n      (path.global? ? program : self).lookup_path(path.names, lookup_in_namespace, include_private, location)\n    end\n\n    # :ditto:\n    def lookup_path(names : Array(String), lookup_in_namespace = true, include_private = false, location = nil) : Type | ASTNode | Nil\n      type = self\n      names.each_with_index do |name, i|\n        # The search must continue in the namespace only for the first path\n        # item: for subsequent path items only the parents must be looked up\n        type = type.lookup_path_item(name, i == 0, lookup_in_namespace && i == 0, i == 0 || include_private, location)\n        return unless type\n\n        # Stop if this is the last name\n        break if i == names.size - 1\n\n        # An intermediate match could be an ASTNode, for example\n        # when searching T::N::X, and T denotes a static array:\n        # in this case we can't continue searching past `N`\n        return unless type.is_a?(Type)\n      end\n\n      # If this is a TypeParameter resulting from the formal argument of e.g. an\n      # inherited generic instance, we must solve this immediately whenever\n      # possible. For example:\n      #\n      # ```\n      # class Foo(T1); end\n      #\n      # class Bar(T2) < Foo(T2); end\n      # ```\n      #\n      # Looking up `T1` under `Bar(Int32)`'s context will return `T2`, which is\n      # the formal argument in `Foo(T2)`, and we solve it to return `Int32`. On\n      # the other hand, looking up `T1` under `Bar`'s context will return `nil`.\n      if type.is_a?(TypeParameter)\n        solved = type.solve?(self)\n        if solved.is_a?(Var)\n          return solved.type\n        elsif solved\n          # If the context were `Bar(1)` instead, then `solved` is that AST node\n          # itself (i.e. `1`), rather than a `Var` with the type of the solved\n          # type argument.\n          return solved\n        end\n      end\n\n      type\n    end\n\n    # Looks up a single path item relative to *self`.\n    #\n    # If *lookup_in_namespace* is `true`, if the type is not found\n    # in `self` or `self`'s parents, the path item is searched in this\n    # type's namespace. This parameter is useful because when writing\n    # `Foo::Bar::Baz`, `Foo` should be searched in enclosing namespaces,\n    # but `Bar` and `Baz` not.\n    #\n    # If *lookup_self* is `true`, if the type is not found under `self` but has\n    # the same name as `self`, then `self` is returned. This has higher\n    # precedence than ancestors and the enclosing namespace.\n    def lookup_path_item(name : String, lookup_self, lookup_in_namespace, include_private, location) : Type | ASTNode | Nil\n      # First search in our types\n      type = lookup_name(name)\n      if type\n        if type.private? && !include_private\n          return nil\n        end\n\n        return type\n      end\n\n      # Try ourself for the first path item, unless we are the top-level\n      if lookup_self && self != program\n        if self.is_a?(NamedType) && name == self.name\n          return self\n        end\n      end\n\n      # Then try out parents, but don't search in our parents namespace\n      parents.try &.each do |parent|\n        match = parent.lookup_path_item(name, false, false, include_private, location)\n        return match if match\n      end\n\n      # Try our namespace, unless we are the top-level\n      if lookup_in_namespace && self != program\n        return namespace.lookup_path_item(name, false, lookup_in_namespace, include_private, location)\n      end\n\n      nil\n    end\n  end\n\n  class Program\n    def lookup_path_item(name : String, lookup_self, lookup_in_namespace, include_private, location)\n      # Check if there's a private type in location\n      if location && (original_filename = location.original_filename) &&\n         (file_module = file_module?(original_filename)) &&\n         (item = file_module.types[name]?)\n        return item\n      end\n\n      super\n    end\n  end\n\n  module GenericType\n    def lookup_path_item(name : String, lookup_self, lookup_in_namespace, include_private, location)\n      # If we are Foo(T) and somebody looks up the type T, we return `nil` because we don't\n      # know what type T is, and we don't want to continue search in the namespace\n      if type_vars.includes?(name)\n        return nil\n      end\n      super\n    end\n  end\n\n  class GenericInstanceType\n    def lookup_path_item(name : String, lookup_self, lookup_in_namespace, include_private, location)\n      # Check if *name* is a type variable\n      if type_var = type_vars[name]?\n        if type_var.is_a?(Var)\n          type_var.type\n        else\n          type_var\n        end\n      else\n        generic_type.lookup_path_item(name, lookup_self, lookup_in_namespace, include_private, location)\n      end\n    end\n  end\n\n  class UnionType\n    def lookup_path_item(name : String, lookup_self, lookup_in_namespace, include_private, location)\n      # Union type does not currently inherit GenericClassInstanceType,\n      # so we check if *name* is the only type variable of Union(*T)\n      if name == \"T\"\n        return program.tuple_of(union_types)\n      end\n      program.lookup_path_item(name, lookup_self, lookup_in_namespace, include_private, location)\n    end\n  end\n\n  class AliasType\n    def lookup_path_item(name : String, lookup_self, lookup_in_namespace, include_private, location)\n      if aliased_type = aliased_type?\n        aliased_type.lookup_path_item(name, lookup_self, lookup_in_namespace, include_private, location)\n      else\n        super\n      end\n    end\n  end\n\n  class TypeDefType\n    delegate lookup_path, to: typedef\n  end\n\n  class MetaclassType\n    delegate lookup_path, to: instance_type\n  end\n\n  class GenericClassInstanceMetaclassType\n    delegate lookup_path, to: instance_type\n  end\n\n  class GenericModuleInstanceMetaclassType\n    delegate lookup_path, to: instance_type\n  end\n\n  class VirtualType\n    delegate lookup_path, to: base_type\n  end\n\n  class VirtualMetaclassType\n    delegate lookup_path, to: instance_type\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/recursive_struct_checker.cr",
    "content": "# Checks that there are no recursive structs in the program.\n#\n# An example of a recursive struct is:\n#\n# ```\n# struct Test\n#   def initialize(@test : Test | Nil)\n#   end\n# end\n#\n# Test.new(Test.new(nil))\n# ```\n#\n# Because the type of `Test.@test` would be: `Test | Nil`.\nclass Crystal::RecursiveStructChecker\n  @program : Program\n\n  def initialize(@program)\n  end\n\n  def run\n    check_types(@program)\n    @program.file_modules.each_value do |file_module|\n      check_types(file_module)\n    end\n  end\n\n  def check_types(type)\n    type.types?.try &.each_value do |type|\n      check_single(type)\n    end\n  end\n\n  def check_single(type)\n    if struct?(type)\n      target = type\n      checked = Set(Type).new\n      path = [] of Var | Type\n      check_recursive_instance_var_container(target, type, checked, path)\n    end\n\n    if type.is_a?(AliasType) && !type.simple?\n      target = type\n      checked = Set(Type).new\n      path = [] of Var | Type\n      check_recursive(target, type.aliased_type, checked, path)\n    end\n\n    check_types(type)\n    check_generic_instances(type)\n  end\n\n  def check_generic_instances(type)\n    if type.struct? && type.is_a?(GenericType)\n      type.each_instantiated_type do |instance|\n        check_single(instance)\n      end\n    end\n  end\n\n  def check_recursive(target, type, checked, path)\n    if target == type\n      if target.is_a?(AliasType)\n        alias_message = \" (recursive aliases are structs)\"\n      end\n\n      msg = <<-MSG\n        recursive struct #{target} detected#{alias_message}\n\n          #{path_to_s(path)}\n\n        The struct #{target} has, either directly or indirectly,\n        an instance variable whose type is, eventually, this same\n        struct. This makes it impossible to represent the struct\n        in memory, because the size of this instance variable depends\n        on the size of this struct, which depends on the size of\n        this instance variable, causing an infinite cycle.\n\n        You should probably be using classes here, as classes\n        instance variables are always behind a pointer, which makes\n        it possible to always compute a size for them.\n        MSG\n      location = target.locations.try &.first?\n      if location\n        raise TypeException.new(msg, location)\n      else\n        raise TypeException.new(msg)\n      end\n    end\n\n    return if checked.includes?(type)\n\n    if type.is_a?(VirtualType)\n      if type.struct?\n        push(path, type) do\n          type.subtypes.each do |subtype|\n            push(path, subtype) do\n              check_recursive(target, subtype, checked, path)\n            end\n          end\n        end\n      end\n    end\n\n    if type.is_a?(NonGenericModuleType) || type.is_a?(GenericModuleInstanceType)\n      push(path, type) do\n        # Check if the module is composed, recursively, of the target struct\n        type.raw_including_types.try &.each do |module_type|\n          push(path, module_type) do\n            check_recursive(target, module_type, checked, path)\n          end\n        end\n      end\n    end\n\n    if type.is_a?(InstanceVarContainer)\n      if struct?(type)\n        check_recursive_instance_var_container(target, type, checked, path)\n      end\n    end\n\n    if type.is_a?(UnionType)\n      push(path, type) do\n        type.union_types.each do |union_type|\n          push(path, union_type) do\n            check_recursive(target, union_type, checked, path)\n          end\n        end\n      end\n    end\n\n    if type.is_a?(TupleInstanceType)\n      push(path, type) do\n        type.tuple_types.each do |tuple_type|\n          push(path, tuple_type) do\n            check_recursive(target, tuple_type, checked, path)\n          end\n        end\n      end\n    end\n\n    if type.is_a?(NamedTupleInstanceType)\n      push(path, type) do\n        type.entries.each do |entry|\n          push(path, entry.type) do\n            check_recursive(target, entry.type, checked, path)\n          end\n        end\n      end\n    end\n  end\n\n  def check_recursive_instance_var_container(target, type, checked, path)\n    checked.add type\n    type.all_instance_vars.each_value do |var|\n      var_type = var.type?\n      next unless var_type\n\n      push(path, var) do\n        check_recursive(target, var_type, checked, path)\n      end\n    end\n    checked.delete type\n  end\n\n  def path_to_s(path)\n    path.join(\" -> \") do |var_or_type|\n      case var_or_type\n      when Var\n        \"`#{var_or_type.name} : #{var_or_type.type.devirtualize}`\"\n      else\n        \"`#{var_or_type.devirtualize}`\"\n      end\n    end\n  end\n\n  def struct?(type)\n    type.struct? && type.is_a?(InstanceVarContainer) && !type.is_a?(PrimitiveType) && !type.is_a?(ProcInstanceType) && !type.abstract?\n  end\n\n  def push(path, type, &)\n    if path.last? == type\n      yield\n    else\n      path.push type\n      yield\n      path.pop\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/restrictions.cr",
    "content": "require \"../syntax/ast\"\nrequire \"../types\"\n\n# Here is the logic for deciding two things:\n#\n# 1. Whether a method should come before another one when considering overloads.\n#    This is what `compare_strictness` and `restriction_of?` are for.\n# 2. What's the resulting type of filtering a type by a restriction.\n#    This is what `restrict` is for.\n#\n# If `a.restriction_of?(b)` is true, it means that `a` should come before `b`\n# when considering restrictions. This applies almost always to AST nodes, which\n# are sometimes resolved to see if a type inherits another one (and so it should\n# be considered before that type), but can apply to types when arguments have a\n# fixed type (mostly for primitive methods and abstract defs, though we should\n# get rid of this to simplify things).\n#\n# A similar logic applies to a `Def`, where this logic is applied for each of\n# the arguments, though here the number of arguments, splat index and other\n# factors are considered. If `a.compare_strictness(b) == 0`, for `a` and `b`\n# being `Def`s, then it means `a` and `b` are equivalent, and so when adding `b`\n# to a type's methods it will replace `a`.\n#\n# The method `restrict` is different in that the return value is not a boolean,\n# but a type, and computing it might be a bit more expensive. For example when\n# restricting `Int32 | String` against `Int32`, the result is `Int32`.\n\nmodule Crystal\n  class ASTNode\n    def restriction_of?(other : Underscore, owner, self_free_vars = nil, other_free_vars = nil)\n      true\n    end\n\n    def restriction_of?(other : ASTNode, owner, self_free_vars = nil, other_free_vars = nil)\n      self == other\n    end\n\n    def restriction_of?(other : Type, owner, self_free_vars = nil, other_free_vars = nil)\n      false\n    end\n\n    def restriction_of?(other, owner, self_free_vars = nil, other_free_vars = nil)\n      raise \"BUG: called #{self}.restriction_of?(#{other})\"\n    end\n  end\n\n  class Self\n    def restriction_of?(type : Type, owner, self_free_vars = nil, other_free_vars = nil)\n      owner.restriction_of?(type, owner, self_free_vars, other_free_vars)\n    end\n\n    def restriction_of?(type : Self, owner, self_free_vars = nil, other_free_vars = nil)\n      true\n    end\n\n    def restriction_of?(type : ASTNode, owner, self_free_vars = nil, other_free_vars = nil)\n      false\n    end\n  end\n\n  struct DefWithMetadata\n    # Compares two defs based on overload order. Has a return value similar to\n    # `Comparable`; that is, `self.compare_strictness(other)` returns:\n    #\n    # * `-1` if `self` is a stricter def than *other*;\n    # * `1` if *other* is a stricter def than `self`;\n    # * `0` if `self` and *other* are equivalent defs;\n    # * `nil` if neither def is stricter than the other.\n    def compare_strictness(other : DefWithMetadata, self_owner, *, other_owner = self_owner)\n      unless self_owner.program.has_flag?(\"preview_overload_order\")\n        return compare_strictness_old(other, self_owner, other_owner: other_owner)\n      end\n\n      # If one yields and the other doesn't, neither is stricter than the other\n      return nil unless self.yields == other.yields\n\n      # We don't check for incompatible defs from positional parameters here,\n      # because doing so would break transitivity, e.g.\n      #\n      #     def f(x = 0); end\n      #     def f(x, y, z = 0); end\n      #\n      # The two defs are indeed incompatible, but their order can be defined\n      # through the intermediate overload `def f(x, y = 0); end`.\n\n      self_named_args = self.named_arguments\n      other_named_args = other.named_arguments\n\n      # Required named parameter in self, no corresponding named parameter in\n      # other; neither is stricter than the other\n      unless other.def.double_splat\n        self_named_args.try &.each do |self_arg|\n          unless self_arg.default_value\n            unless other_named_args.try &.any?(&.external_name.== self_arg.external_name)\n              return nil\n            end\n          end\n        end\n      end\n\n      unless self.def.double_splat\n        other_named_args.try &.each do |other_arg|\n          unless other_arg.default_value\n            unless self_named_args.try &.any?(&.external_name.== other_arg.external_name)\n              return nil\n            end\n          end\n        end\n      end\n\n      self_stricter = true\n      other_stricter = true\n\n      # Compare all corresponding parameters based on subsumption order\n      each_corresponding_param(other, self_named_args, other_named_args) do |self_arg, other_arg|\n        self_restriction = self_arg.type? || self_arg.restriction\n        other_restriction = other_arg.type? || other_arg.restriction\n\n        case {self_restriction, other_restriction}\n        when {nil, nil}\n          # Check other corresponding parameters\n        when {nil, _}\n          self_is_not_stricter\n        when {_, nil}\n          other_is_not_stricter\n        else\n          self_is_not_stricter unless self_restriction.restriction_of?(other_restriction, self_owner)\n          other_is_not_stricter unless other_restriction.restriction_of?(self_restriction, other_owner)\n        end\n      end\n\n      # The overload order is fully defined at this point if either def already\n      # isn't stricter than the other\n      return stricter_pair_to_num(self_stricter, other_stricter) if !self_stricter || !other_stricter\n\n      # Combine the specificities from positional and all named signatures\n      self_stricter, other_stricter = compare_specific_positional(other)\n\n      if self_named_args || other_named_args\n        self_named_args.try &.each do |self_arg|\n          other_arg = other_named_args.try &.find(&.external_name.== self_arg.external_name)\n          self_n, other_n = compare_specific_named(other, self_arg, other_arg)\n          self_is_not_stricter if !self_n\n          other_is_not_stricter if !other_n\n        end\n\n        other_named_args.try &.each do |other_arg|\n          next if self_named_args.try &.any?(&.external_name.== other_arg.external_name)\n          self_n, other_n = compare_specific_named(other, nil, other_arg)\n          self_is_not_stricter if !self_n\n          other_is_not_stricter if !other_n\n        end\n      else\n        # If there are no named parameters at all, `(**ns)` is less specific than `()`\n        if self.def.double_splat && !other.def.double_splat\n          self_is_not_stricter\n        elsif other.def.double_splat && !self.def.double_splat\n          other_is_not_stricter\n        end\n      end\n\n      stricter_pair_to_num(self_stricter, other_stricter)\n    end\n\n    private macro self_is_not_stricter\n      self_stricter = false\n      return nil if !other_stricter\n    end\n\n    private macro other_is_not_stricter\n      other_stricter = false\n      return nil if !self_stricter\n    end\n\n    # Compares two defs based on whether one def's positional parameters are\n    # more specific than the other's.\n    #\n    # Required parameters are more specific than optional parameters, and single\n    # splat parameters are the least specific.\n    def compare_specific_positional(other : DefWithMetadata)\n      # If self has more required positional parameters than other, the last\n      # one in self must correspond to an optional or splat parameter in other,\n      # otherwise other has no corresponding parameter and `compare_strictness`\n      # would have already returned; hence, self is stricter than other in this\n      # case.\n      if self.min_size > other.min_size\n        self_is_stricter\n      elsif other.min_size > self.min_size\n        other_is_stricter\n      end\n\n      # Bare splats aren't single splat parameters\n      if self_splat_index = self.def.splat_index\n        self_splat_index = nil if self.def.args[self_splat_index].name.empty?\n      end\n      if other_splat_index = other.def.splat_index\n        other_splat_index = nil if other.def.args[other_splat_index].name.empty?\n      end\n\n      case {self_splat_index, other_splat_index}\n      in {nil, nil}\n        # Consider `(x0, x1 = 0, x2 = 0)` and `(y0, y1 = 0)`; both overloads can\n        # take 1 or 2 arguments, but only self could take 3, so other is stricter\n        # than self.\n        if self.max_size > other.max_size\n          other_is_stricter\n        elsif other.max_size > self.max_size\n          self_is_stricter\n        end\n      in {nil, Int32}\n        # other has a splat parameter, self doesn't; self is stricter than the other\n        self_is_stricter\n      in {Int32, nil}\n        # self has a splat parameter, other doesn't; other is stricter than self\n        other_is_stricter\n      in {Int32, Int32}\n        # Consider `(x0, *xs)` and `(y0, y1 = 0, *ys)`; here `y1` corresponds to\n        # `xs`, and splat parameter is less specific than optional parameter, so\n        # other is stricter than self.\n        if self_splat_index < other_splat_index\n          other_is_stricter\n        elsif other_splat_index < self_splat_index\n          self_is_stricter\n        end\n      end\n\n      no_differences\n    end\n\n    # Compares two defs based on whether one def's given named parameter is more\n    # specific than the other's.\n    def compare_specific_named(other : DefWithMetadata, self_arg : Arg?, other_arg : Arg?)\n      self_arg_required = self_arg && !self_arg.default_value\n      other_arg_required = other_arg && !other_arg.default_value\n\n      # `n` is required in self, but not required in other; `n`'s corresponding\n      # parameter in other must be optional or splat, so self is stricter than\n      # the other\n      if self_arg_required && !other_arg_required\n        self_is_stricter\n      elsif other_arg_required && !self_arg_required\n        other_is_stricter\n      end\n\n      self_arg_optional = self_arg && self_arg.default_value\n      other_arg_optional = other_arg && other_arg.default_value\n\n      case {self.def.double_splat, other.def.double_splat}\n      in {nil, nil}\n        # Consider `(*, n = 0)` and `()`; both overloads can take no named\n        # arguments, but only self could take `n`, so other is stricter than\n        # self.\n        if self_arg_optional && !other_arg_optional\n          other_is_stricter\n        elsif other_arg_optional && !self_arg_optional\n          self_is_stricter\n        end\n      in {nil, Arg}\n        # other has a splat parameter, self doesn't; self is stricter than the other\n        self_is_stricter\n      in {Arg, nil}\n        # self has a splat parameter, other doesn't; other is stricter than self\n        other_is_stricter\n      in {Arg, Arg}\n        # Consider `(*, **ms)` and `(*, n = 0, **ns)`; here `n` corresponds to\n        # `ms`, and splat parameter is less specific than optional parameter, so\n        # other is stricter than self.\n        if self_arg_optional && !other_arg_optional\n          self_is_stricter\n        elsif other_arg_optional && !self_arg_optional\n          other_is_stricter\n        end\n      end\n\n      no_differences\n    end\n\n    private macro self_is_stricter\n      return {true, false}\n    end\n\n    private macro other_is_stricter\n      return {false, true}\n    end\n\n    private macro no_differences\n      return {true, true}\n    end\n\n    # Yields each pair of corresponding parameters between `self` and *other*.\n    def each_corresponding_param(other : DefWithMetadata, self_named_args, other_named_args, &)\n      self_arg_index = 0\n      other_arg_index = 0\n\n      # Traverse through positional parameters, including single splats\n      while self_arg_index < self.def.args.size && other_arg_index < other.def.args.size\n        self_arg = self.def.args[self_arg_index]\n        self_splatting = (self_arg_index == self.def.splat_index)\n        break if self_splatting && self_arg.name.empty? # Start of named parameters\n\n        other_arg = other.def.args[other_arg_index]\n        other_splatting = (other_arg_index == other.def.splat_index)\n        break if other_splatting && other_arg.name.empty? # Start of named parameters\n\n        yield self_arg, other_arg\n\n        break if self_splatting && other_splatting # Both are splat parameters\n\n        self_arg_index += 1 unless self_splatting\n        other_arg_index += 1 unless other_splatting\n      end\n\n      # Traverse through named parameters\n      self_double_splat = self.def.double_splat\n      other_double_splat = other.def.double_splat\n\n      self_named_args.try &.each do |self_arg|\n        other_arg = other_named_args.try &.find(&.external_name.== self_arg.external_name)\n        other_arg ||= other_double_splat\n        next unless other_arg\n\n        yield self_arg, other_arg\n      end\n\n      if self_double_splat\n        # Pair self's double splat with any remaining named parameters in other\n        other_named_args.try &.each do |other_arg|\n          next if self_named_args.try &.any?(&.external_name.== other_arg.external_name)\n\n          yield self_double_splat, other_arg\n        end\n\n        # Double splats themselves are also corresponding named parameters\n        if other_double_splat\n          yield self_double_splat, other_double_splat\n        end\n      end\n    end\n\n    def stricter_pair_to_num(self_stricter, other_stricter)\n      case {self_stricter, other_stricter}\n      in {true, true}   then 0\n      in {true, false}  then -1\n      in {false, true}  then 1\n      in {false, false} then nil\n      end\n    end\n\n    def named_arguments\n      if (splat_index = self.def.splat_index) && splat_index != self.def.args.size - 1\n        self.def.args[splat_index + 1..]\n      end\n    end\n\n    def compare_strictness_old(other : DefWithMetadata, self_owner, *, other_owner = self_owner)\n      self_stricter = old_restriction_of?(other, self_owner)\n      other_stricter = other.old_restriction_of?(self, other_owner)\n      stricter_pair_to_num(self_stricter, other_stricter)\n    end\n\n    # this is part of `Crystal::Def#min_max_args_sizes` before #10711, provided\n    # that `-Dpreview_overload_order` is not in effect\n    # TODO: figure out if this can be derived from `self.min_size`\n    def old_min_args_size\n      if splat_index = self.def.splat_index\n        args = self.def.args\n        unless args[splat_index].name.empty?\n          default_value_index = args.index(&.default_value)\n          min_size = default_value_index || args.size\n          min_size -= 1 unless default_value_index.try(&.< splat_index)\n          return min_size\n        end\n      end\n      self.min_size\n    end\n\n    def old_restriction_of?(other : DefWithMetadata, owner)\n      # This is how multiple defs are sorted by 'restrictions' (?)\n\n      # If one yields and the other doesn't, none is stricter than the other\n      return false unless yields == other.yields\n\n      self_min_size = old_min_args_size\n      other_min_size = other.old_min_args_size\n\n      # A def with more required arguments than the other comes first\n      if self_min_size > other.max_size\n        return true\n      elsif other_min_size > max_size\n        return false\n      end\n\n      self_splat_index = self.def.splat_index\n      other_splat_index = other.def.splat_index\n\n      # If I double-splat but the other doesn't, I come later\n      if self.def.double_splat && !other.def.double_splat\n        return false\n      end\n\n      # If the other double-splats but I don't, I come first\n      if other.def.double_splat && !self.def.double_splat\n        return true\n      end\n\n      # If I splat but the other doesn't, I come later\n      if self_splat_index && !other_splat_index\n        return false\n      end\n\n      # If the other splats but I don't, I come first\n      if other_splat_index && !self_splat_index\n        return true\n      end\n\n      if self_splat_index && other_splat_index\n        min = Math.min(self_min_size, other_min_size)\n      else\n        min = Math.min(max_size, other.max_size)\n      end\n\n      self_free_vars = self.def.free_vars\n      other_free_vars = other.def.free_vars\n\n      (0...min).each do |index|\n        self_arg = self.def.args[index]\n        other_arg = other.def.args[index]\n\n        self_type = self_arg.type? || self_arg.restriction\n        other_type = other_arg.type? || other_arg.restriction\n        return false if self_type == nil && other_type != nil\n        if self_type && other_type\n          # If this is a splat arg and the other not, this is not stricter than the other\n          return false if index == self.def.splat_index\n\n          return false unless self_type.restriction_of?(other_type, owner, self_free_vars, other_free_vars)\n        end\n      end\n\n      if self_splat_index && other_splat_index\n        if self_splat_index == other_splat_index\n          self_arg = self.def.args[self_splat_index]\n          other_arg = other.def.args[other_splat_index]\n          self_restriction = self_arg.restriction\n          other_restriction = other_arg.restriction\n\n          if self_restriction && other_restriction\n            # If both splat have restrictions, check which one is stricter\n            return false unless self_restriction.restriction_of?(other_restriction, owner, self_free_vars, other_free_vars)\n          elsif self_restriction\n            # If only self has a restriction, it's stricter than the other\n            return true\n          elsif other_restriction\n            # If only the other has a restriction, it's stricter than self\n            return false\n          end\n        elsif self_splat_index < other_splat_index\n          return false\n        else\n          return true\n        end\n      end\n\n      # Check required named arguments\n      self_named_args = self.required_named_arguments\n      other_named_args = other.required_named_arguments\n\n      # If both have named args we must restrict name by name\n      if self_named_args && other_named_args\n        self_names = self_named_args.map(&.external_name)\n        other_names = other_named_args.map(&.external_name)\n\n        # If the names of the required named args are different, these are different overloads\n        return false if self_names != other_names\n\n        # They are the same, so we apply usual restriction checking on the args\n        self_named_args.zip(other_named_args) do |self_arg, other_arg|\n          self_restriction = self_arg.restriction\n          other_restriction = other_arg.restriction\n          return false if self_restriction == nil && other_restriction != nil\n\n          if self_restriction && other_restriction\n            return false unless self_restriction.restriction_of?(other_restriction, owner, self_free_vars, other_free_vars)\n          end\n        end\n\n        return true\n      end\n\n      # If one has required named args and the other doesn't, none is stricter than the other\n      if (self_named_args || other_named_args)\n        return false\n      end\n\n      self_double_splat_restriction = self.def.double_splat.try &.restriction\n      other_double_splat_restriction = other.def.double_splat.try &.restriction\n\n      # If both double splat have restrictions, check which one is stricter\n      if self_double_splat_restriction && other_double_splat_restriction\n        return false unless self_double_splat_restriction.restriction_of?(other_double_splat_restriction, owner, self_free_vars, other_free_vars)\n      elsif self_double_splat_restriction\n        # If only self has a restriction, it's stricter than the other\n        return true\n      elsif other_double_splat_restriction\n        # If only the other has a restriction, it's stricter than self\n        return false\n      end\n\n      true\n    end\n\n    def required_named_arguments\n      if (splat_index = self.def.splat_index) && splat_index != self.def.args.size - 1\n        self.def.args[splat_index + 1..-1].select { |arg| !arg.default_value }.sort_by! &.external_name\n      else\n        nil\n      end\n    end\n  end\n\n  class Macro\n    def overrides?(other : Macro)\n      # If they have different number of arguments, splat index or presence of\n      # double splat, no override.\n      if args.size != other.args.size ||\n         splat_index != other.splat_index ||\n         !!double_splat != !!other.double_splat\n        return false\n      end\n\n      self_named_args = self.required_named_arguments\n      other_named_args = other.required_named_arguments\n\n      # If both don't have named arguments, override.\n      return true if !self_named_args && !other_named_args\n\n      # If one has required named args and the other doesn't, no override.\n      return false unless self_named_args && other_named_args\n\n      self_names = self_named_args.map(&.external_name)\n      other_names = other_named_args.map(&.external_name)\n\n      # If different named arguments names, no override.\n      return false unless self_names == other_names\n\n      true\n    end\n\n    def required_named_arguments\n      if (splat_index = self.splat_index) && splat_index != args.size - 1\n        args[splat_index + 1..-1].select { |arg| !arg.default_value }.sort_by! &.external_name\n      else\n        nil\n      end\n    end\n  end\n\n  class Path\n    def restriction_of?(other : Path, owner, self_free_vars = nil, other_free_vars = nil)\n      self_is_free_var = self_free_vars && self.single_name?.try { |name| self_free_vars.includes?(name) }\n      other_is_free_var = other_free_vars && other.single_name?.try { |name| other_free_vars.includes?(name) }\n\n      if self_is_free_var == other_is_free_var\n        # TODO: if both paths are free variables, we need to detect renamed\n        # variables properly instead of doing a plain name check\n        return true if self == other\n      end\n\n      if !self_is_free_var && (self_type = owner.lookup_path(self))\n        if !other_is_free_var && (other_type = owner.lookup_path(other))\n          return self_type.restriction_of?(other_type, owner, self_free_vars, other_free_vars)\n        else\n          return true\n        end\n      end\n\n      false\n    end\n\n    def restriction_of?(other : Union, owner, self_free_vars = nil, other_free_vars = nil)\n      return false if self_free_vars && self.single_name?.try { |name| self_free_vars.includes?(name) }\n\n      # `true` if this type is a restriction of any type in the union\n      other.types.any? { |o| self.restriction_of?(o, owner, self_free_vars, other_free_vars) }\n    end\n\n    def restriction_of?(other : Generic, owner, self_free_vars = nil, other_free_vars = nil)\n      # ```\n      # def foo(param : T) forall T\n      # end\n      #\n      # def foo(param : Array(Foo))\n      # end\n      # ```\n      return false if self_free_vars && self.single_name?.try { |name| self_free_vars.includes?(name) }\n\n      self_type = owner.lookup_path(self)\n      if self_type\n        other_type = owner.lookup_type?(other)\n        if other_type\n          return self_type.restriction_of?(other_type, owner, self_free_vars, other_free_vars)\n        end\n      end\n\n      false\n    end\n\n    def restriction_of?(other : NumberLiteral, owner, self_free_vars = nil, other_free_vars = nil)\n      return false if self_free_vars && self.single_name?.try { |name| self_free_vars.includes?(name) }\n\n      # this happens when `self` and `other` are generic arguments:\n      #\n      # ```\n      # X = 1\n      #\n      # def foo(param : StaticArray(Int32, X))\n      # end\n      #\n      # def foo(param : StaticArray(Int32, 1))\n      # end\n      # ```\n      case self_type = owner.lookup_path(self)\n      when Const\n        self_type.value == other\n      when NumberLiteral\n        self_type == other\n      else\n        false\n      end\n    end\n\n    def restriction_of?(other : Underscore, owner, self_free_vars = nil, other_free_vars = nil)\n      true\n    end\n\n    def restriction_of?(other, owner, self_free_vars = nil, other_free_vars = nil)\n      false\n    end\n  end\n\n  class NumberLiteral\n    def restriction_of?(other : Path, owner, self_free_vars = nil, other_free_vars = nil)\n      # this happens when `self` and `other` are generic arguments:\n      #\n      # ```\n      # X = 1\n      #\n      # def foo(param : StaticArray(Int32, 1))\n      # end\n      #\n      # def foo(param : StaticArray(Int32, X))\n      # end\n      # ```\n      case other_type = owner.lookup_path(other)\n      when Const\n        other_type.value == self\n      when NumberLiteral\n        other_type == self\n      else\n        false\n      end\n    end\n  end\n\n  class Union\n    def restriction_of?(other : Underscore, owner, self_free_vars = nil, other_free_vars = nil)\n      true\n    end\n\n    def restriction_of?(other, owner, self_free_vars = nil, other_free_vars = nil)\n      # For a union to be considered before another restriction,\n      # all types in the union must be considered before\n      # that restriction.\n      # For example when using all subtypes of a parent type.\n      types.all? &.restriction_of?(other, owner, self_free_vars, other_free_vars)\n    end\n  end\n\n  class Generic\n    def restriction_of?(other : Path, owner, self_free_vars = nil, other_free_vars = nil)\n      # ```\n      # def foo(param : Array(T)) forall T\n      # end\n      #\n      # def foo(param : Int32)\n      # end\n      # ```\n      #\n      # Here, self is `Array`, other is `Int32`\n\n      self_type = owner.lookup_type?(self)\n      if self_type\n        other_type = owner.lookup_path(other)\n        if other_type\n          return self_type.restriction_of?(other_type, owner, self_free_vars, other_free_vars)\n        end\n      end\n\n      # `Array(T)` is always more strict than `Foo`\n      #\n      # Useful in cases where `Array(T)` overload must be checked before\n      # `T` overload:\n      # ```\n      # def foo(param : T) forall T\n      # end\n      #\n      # def foo(param : Array(T)) forall T\n      # end\n      #\n      # foo([1])\n      # ```\n      true\n    end\n\n    def restriction_of?(other : Generic, owner, self_free_vars = nil, other_free_vars = nil)\n      # The two `Foo(X)`s below are not equal because only one of them is bound\n      # and the other one is unbound, so we compare the free variables too:\n      # (`X` is an alias or a numeric constant)\n      #\n      # ```\n      # def foo(x : Foo(X)) forall X\n      # end\n      #\n      # def foo(x : Foo(X))\n      # end\n      # ```\n      #\n      # See also the todo in `Path#restriction_of?(Path)`\n      return true if self == other && self_free_vars == other_free_vars\n      return false unless name == other.name && type_vars.size == other.type_vars.size\n\n      # Special case: NamedTuple against NamedTuple\n      if (self_type = owner.lookup_type?(self)).is_a?(NamedTupleInstanceType)\n        if (other_type = owner.lookup_type?(other)).is_a?(NamedTupleInstanceType)\n          return self_type.restriction_of?(other_type, owner, self_free_vars, other_free_vars)\n        end\n      end\n\n      type_vars.zip(other.type_vars) do |type_var, other_type_var|\n        return false unless type_var.restriction_of?(other_type_var, owner, self_free_vars, other_free_vars)\n      end\n\n      true\n    end\n  end\n\n  class GenericClassType\n    def restriction_of?(other : GenericClassInstanceType, owner, self_free_vars = nil, other_free_vars = nil)\n      # ```\n      # def foo(param : Array)\n      # end\n      #\n      # def foo(param : Array(Int32))\n      # end\n      # ```\n      #\n      # Here, self is `Array`, other is `Array(Int32)`\n\n      # Even when the underlying generic type is the same,\n      # `SomeGeneric` is never a restriction of `SomeGeneric(X)`\n      false\n    end\n\n    def restrict(other : GenericClassType, context)\n      self == other ? self : super\n    end\n  end\n\n  class GenericClassInstanceType\n    def restriction_of?(other : GenericClassType, owner, self_free_vars = nil, other_free_vars = nil)\n      # ```\n      # def foo(param : Array(Int32))\n      # end\n      #\n      # def foo(param : Array)\n      # end\n      # ```\n      #\n      # Here, self is `Array(Int32)`, other is `Array`\n\n      # When the underlying generic type is the same:\n      # `SomeGeneric(X)` is always a restriction of `SomeGeneric`\n      self.generic_type == other\n    end\n  end\n\n  class Metaclass\n    def restriction_of?(other : Metaclass, owner, self_free_vars = nil, other_free_vars = nil)\n      name.restriction_of?(other.name, owner, self_free_vars, other_free_vars)\n    end\n\n    def restriction_of?(other : Path, owner, self_free_vars = nil, other_free_vars = nil)\n      if other_type = owner.lookup_type?(other)\n        # Special case: all metaclasses are subtypes of Class\n        if other_type.program.class_type.implements?(other_type)\n          return true\n        end\n      end\n\n      super\n    end\n  end\n\n  class Type\n    def restrict(other : Nil, context)\n      self\n    end\n\n    def restrict(other : Type, context)\n      if self == other\n        return self\n      end\n\n      # Allow Nil to match Void (useful for `Pointer(Void)#value=`)\n      if nil_type? && other.void?\n        return self\n      end\n\n      if parents.try &.any? &.restriction_of?(other, context.instantiated_type)\n        return self\n      end\n\n      nil\n    end\n\n    def restrict(other : AliasType, context)\n      if self == other\n        self\n      else\n        restrict(other.remove_alias, context)\n      end\n    end\n\n    def restrict(other : Self, context)\n      self_type = context.self_restriction_type || context.instantiated_type\n      restrict(self_type.instance_type, context)\n    end\n\n    def restrict(other : TypeOf, context)\n      other.raise \"can't use typeof in type restrictions\"\n    end\n\n    def restrict(other : UnionType, context)\n      restricted = nil\n\n      other.union_types.each do |union_type|\n        # Apply the restriction logic on each union type, even if we already\n        # have a match, so that we can detect ambiguous calls between of\n        # literal types against aliases that resolve to union types.\n        restriction = restrict(union_type, context)\n        restricted ||= restriction\n      end\n\n      restricted ? self : nil\n    end\n\n    def restrict(other : VirtualType, context)\n      implements?(other.base_type) ? self : nil\n    end\n\n    def restrict(other : GenericClassType, context)\n      parents.try &.each do |parent|\n        if parent.module?\n          return self if parent.restriction_of?(other, context.instantiated_type)\n        else\n          restricted = parent.restrict other, context\n          return self if restricted\n        end\n      end\n\n      nil\n    end\n\n    def restrict(other : Union, context)\n      # Match all concrete types first\n      free_var_count = other.types.count do |other_type|\n        other_type.is_a?(Path) &&\n          (first_name = other_type.single_name?) &&\n          context.has_unbound_free_var?(first_name)\n      end\n      if free_var_count > 1\n        other.raise \"can't specify more than one free var in union restriction\"\n      end\n\n      types = other.types.compact_map do |ident|\n        restrict(ident, context).as(Type?)\n      end\n      types.size > 0 ? program.type_merge_union_of(types) : nil\n    end\n\n    def restrict(other : Path, context)\n      if first_name = other.single_name?\n        if context.has_unbound_free_var?(first_name)\n          return context.bind_free_var(first_name, self)\n        end\n      end\n\n      if first_name\n        owner = context.instantiated_type\n\n        # Special case: if we have an *uninstantiated* generic type like Foo(X)\n        # and a restriction X, it matches, and we add X to the free vars.\n        if owner.is_a?(GenericType)\n          if owner.type_vars.includes?(first_name)\n            context.bind_free_var(first_name, self)\n            return self\n          end\n        end\n\n        ident_type = context.bound_free_var?(other.names.first)\n      end\n\n      had_ident_type = !!ident_type\n      ident_type ||= context.defining_type.lookup_path other\n\n      if ident_type\n        if ident_type.is_a?(Const)\n          other.raise \"#{ident_type} is not a type, it's a constant\"\n        end\n\n        return restrict ident_type, context\n      end\n\n      if first_name\n        if context.defining_type.type_var?(first_name)\n          return context.bind_free_var(first_name, self)\n        end\n      end\n\n      if had_ident_type\n        other.raise \"undefined constant #{other}\"\n      else\n        other.raise_undefined_constant(context.defining_type)\n      end\n    end\n\n    def restrict(other : Generic, context)\n      # Special case: consider `Union(X, Y, ...)` the same as `X | Y | ...`\n      generic_type = get_generic_type(other, context)\n      if generic_type.is_a?(GenericUnionType)\n        types = [] of Type\n\n        other.type_vars.each do |type_var|\n          if type_var.is_a?(Splat)\n            splat_type = context.defining_type.lookup_type?(type_var.exp)\n            return nil unless splat_type\n            unless splat_type.is_a?(TupleInstanceType)\n              type_var.raise \"argument to splat must be a tuple type, not #{splat_type}\"\n            end\n\n            splat_type.tuple_types.each do |tuple_type|\n              if type = restrict(tuple_type, context)\n                types << type\n              end\n            end\n          else\n            if type = restrict(type_var, context)\n              types << type\n            end\n          end\n        end\n\n        return types.size > 0 ? program.type_merge_union_of(types) : nil\n      end\n\n      parents.try &.each do |parent|\n        next if parent.is_a?(NonGenericModuleType)\n\n        restricted = parent.restrict other, context\n        return self if restricted\n      end\n\n      nil\n    end\n\n    def restrict(other : Metaclass, context)\n      nil\n    end\n\n    def restrict(other : ProcNotation, context)\n      nil\n    end\n\n    def restrict(other : Underscore, context)\n      self\n    end\n\n    def restrict(other : Arg, context)\n      restrict (other.type? || other.restriction), context\n    end\n\n    def restrict(other : NumberLiteral, context)\n      nil\n    end\n\n    def restrict(other : Splat, context)\n      nil\n    end\n\n    def restrict(other : ASTNode, context)\n      raise \"BUG: unsupported restriction: #{self} vs. #{other}\"\n    end\n\n    def restriction_of?(other : UnionType, owner, self_free_vars = nil, other_free_vars = nil)\n      other.union_types.any? { |subtype| restriction_of?(subtype, owner, self_free_vars, other_free_vars) }\n    end\n\n    def restriction_of?(other : VirtualType, owner, self_free_vars = nil, other_free_vars = nil)\n      implements? other.base_type\n    end\n\n    def restriction_of?(other : Type, owner, self_free_vars = nil, other_free_vars = nil)\n      if self == other\n        return true\n      end\n\n      !!parents.try &.any? &.restriction_of?(other, owner, self_free_vars, other_free_vars)\n    end\n\n    def restriction_of?(other : AliasType, owner, self_free_vars = nil, other_free_vars = nil)\n      if self == other\n        true\n      else\n        restriction_of?(other.remove_alias, owner, self_free_vars, other_free_vars)\n      end\n    end\n\n    def restriction_of?(other : ASTNode, owner, self_free_vars = nil, other_free_vars = nil)\n      raise \"BUG: called #{self}.restriction_of?(#{other})\"\n    end\n\n    def compatible_with?(type)\n      self == type\n    end\n  end\n\n  class UnionType\n    def restriction_of?(type, owner, self_free_vars = nil, other_free_vars = nil)\n      self == type || union_types.all? &.restriction_of?(type, owner, self_free_vars, other_free_vars)\n    end\n\n    def restrict(other : Union, context)\n      # Match all concrete types first\n      free_vars, other_types = other.types.partition do |other_type|\n        other_type.is_a?(Path) &&\n          (first_name = other_type.single_name?) &&\n          context.has_unbound_free_var?(first_name)\n      end\n      if free_vars.size > 1\n        other.raise \"can't specify more than one free var in union restriction\"\n      end\n\n      types = [] of Type\n      discarded = [] of Type\n      other_types.each do |other_type|\n        self.union_types.each do |type|\n          next if discarded.includes?(type)\n\n          restricted = type.restrict(other_type, context)\n          if restricted\n            types << restricted\n            discarded << type\n          end\n        end\n      end\n\n      # If there is a free var, we match it last and it'll be the union of the\n      # remaining types in self\n      if free_var = free_vars.first?\n        # If we restrict `T` against `T | U forall U`, then `U` can be any type;\n        # the smallest type satisfying the restriction is Union() or NoReturn,\n        # but we don't want that, so we make this a substitution failure.\n        if discarded.size == self.union_types\n          return nil\n        end\n\n        if remaining_type = program.type_merge_union_of(self.union_types - discarded)\n          if restricted = remaining_type.restrict(free_var, context)\n            types << restricted\n          end\n        end\n      end\n\n      program.type_merge_union_of(types)\n    end\n\n    def restrict(other : Type, context)\n      restrict_type_or_fun_or_generic other, context\n    end\n\n    def restrict(other : ProcNotation, context)\n      restrict_type_or_fun_or_generic other, context\n    end\n\n    def restrict(other : Generic, context)\n      restrict_type_or_fun_or_generic other, context\n    end\n\n    def restrict(other : Metaclass, context)\n      restrict_type_or_fun_or_generic other, context\n    end\n\n    def restrict_type_or_fun_or_generic(other, context)\n      types = union_types.compact_map do |type|\n        type.restrict(other, context).as(Type?)\n      end\n      program.type_merge_union_of(types)\n    end\n  end\n\n  class GenericInstanceType\n    def restriction_of?(other : GenericType, owner, self_free_vars = nil, other_free_vars = nil)\n      return true if generic_type == other\n      super\n    end\n\n    def restriction_of?(other : GenericInstanceType, owner, self_free_vars = nil, other_free_vars = nil)\n      return super unless generic_type == other.generic_type\n\n      type_vars.each do |name, type_var|\n        other_type_var = other.type_vars[name]\n        if type_var.is_a?(Var) && other_type_var.is_a?(Var)\n          # This overload can be called when the restriction node has a type due\n          # to e.g. AbstractDefChecker; generic instances shall behave like AST\n          # nodes when def restrictions are considered, i.e. all generic type\n          # variables are covariant.\n          return false unless type_var.type.implements?(other_type_var.type)\n        else\n          return false unless type_var == other_type_var\n        end\n      end\n\n      true\n    end\n\n    def restrict(other : GenericType, context)\n      return self if generic_type == other\n\n      parents.try &.each do |parent|\n        if parent.module?\n          return self if parent.restriction_of?(other, context.instantiated_type)\n        else\n          restricted = parent.restrict other, context\n          return self if restricted\n        end\n      end\n\n      nil\n    end\n\n    def restrict(other : Generic, context)\n      generic_type = get_generic_type(other, context)\n      generic_type = generic_type.remove_alias if generic_type.is_a? AliasType\n      return super unless generic_type == self.generic_type\n\n      generic_type = generic_type.as(GenericType)\n\n      # We match named tuples in NamedTupleInstanceType\n      if generic_type.is_a?(NamedTupleType)\n        return nil\n      end\n\n      if other.named_args\n        other.raise \"can only instantiate NamedTuple with named arguments\"\n      end\n\n      # Consider the case of a splat in the type vars\n      splat_index = self.splat_index\n      splat_given = other.type_vars.any?(Splat)\n      if splat_index || splat_given\n        types = Array(Type).new(type_vars.size)\n        i = 0\n        type_vars.each_value do |var|\n          return nil unless var.is_a?(Var)\n\n          var_type = var.type\n          if i == splat_index\n            types.concat(var_type.as(TupleInstanceType).tuple_types)\n          else\n            types << var_type\n          end\n          i += 1\n        end\n\n        # We are `(A, B, *C)`, they are `(T)`; matching would always fail\n        if splat_index && !splat_given\n          min_needed = generic_type.type_vars.size - 1\n          if other.type_vars.size < min_needed\n            other.wrong_number_of \"type vars\", generic_type, other.type_vars.size, \"#{min_needed}+\"\n          end\n        end\n\n        # We are `(A)`, they are `(T, U, *V)`; matching would always fail\n        if !splat_index && splat_given\n          non_splat_count = other.type_vars.count { |type_var| !type_var.is_a?(Splat) }\n          if non_splat_count > generic_type.type_vars.size\n            other.wrong_number_of \"type vars\", generic_type, \"#{non_splat_count}+\", generic_type.type_vars.size\n          end\n        end\n\n        i = 0\n        found_splat = false\n        other.type_vars.each do |type_var|\n          if type_var.is_a?(Splat)\n            type_var.raise \"can't specify more than one splat in restriction\" if found_splat\n            found_splat = true\n\n            count = types.size - (other.type_vars.size - 1)\n            return nil unless count >= 0\n\n            arg_types = types[i, count]\n            arg_types_tuple = context.instantiated_type.program.tuple_of(arg_types)\n\n            restricted = arg_types_tuple.restrict(type_var.exp, context)\n            return nil unless restricted == arg_types_tuple\n\n            i += count\n          else\n            arg_type = types[i]\n            restricted = arg_type.restrict(type_var, context)\n            return unless restricted == arg_type\n\n            i += 1\n          end\n        end\n\n        return self\n      end\n\n      if other.type_vars.size != generic_type.type_vars.size\n        other.wrong_number_of \"type vars\", generic_type, other.type_vars.size, generic_type.type_vars.size\n      end\n\n      i = 0\n      type_vars.each do |name, type_var|\n        other_type_var = other.type_vars[i]\n        restricted = restrict_type_var(type_var, other_type_var, context)\n        return nil unless restricted\n        i += 1\n      end\n\n      self\n    end\n\n    def restrict(other : GenericInstanceType, context)\n      return super unless generic_type == other.generic_type\n\n      type_vars.each do |name, type_var|\n        other_type_var = other.type_vars[name]\n        restricted = restrict_type_var(type_var, other_type_var, context)\n        return super unless restricted\n      end\n\n      self\n    end\n\n    def restrict_type_var(type_var, other_type_var, context)\n      if type_var.is_a?(NumberLiteral)\n        case other_type_var\n        when NumberLiteral\n          if type_var == other_type_var\n            return type_var\n          end\n        when Path\n          if first_name = other_type_var.single_name?\n            # If the free variable is already set to another\n            # number, there's no match\n            if existing = context.bound_free_var?(first_name)\n              return existing == type_var ? existing : nil\n            end\n\n            # If the free variable is not yet bound, there is a match\n            if context.has_unbound_free_var?(first_name)\n              context.bind_free_var(first_name, type_var)\n              return type_var\n            end\n          end\n        else\n          # Restriction is not possible (maybe return nil here?)\n        end\n      else\n        type_var = type_var.type? || type_var\n      end\n\n      unless other_type_var.is_a?(NumberLiteral)\n        other_type_var = other_type_var.type? || other_type_var\n      end\n\n      if type_var.is_a?(ASTNode)\n        type_var.restriction_of?(other_type_var, context.instantiated_type)\n      else\n        # To prevent infinite recursion, it checks equality between\n        # `type_var` and `other_type_var` directly before try to restrict\n        # `type_var` by `other_type_var`.\n        type_var == other_type_var || type_var.restrict(other_type_var, context) == type_var\n      end\n    end\n  end\n\n  class TupleInstanceType\n    def restriction_of?(other : TupleInstanceType, owner, self_free_vars = nil, other_free_vars = nil)\n      return true if self == other || self.implements?(other)\n\n      false\n    end\n\n    def restrict(other : Generic, context)\n      generic_type = get_generic_type(other, context)\n      return super unless generic_type == self.generic_type\n\n      if other.named_args\n        other.raise \"can only instantiate NamedTuple with named arguments\"\n      end\n\n      # Consider the case of a splat in the type vars\n      splat_given = other.type_vars.any?(Splat)\n      if splat_given\n        found_splat = false\n        i = 0\n        other.type_vars.each do |type_var|\n          if type_var.is_a?(Splat)\n            type_var.raise \"can't specify more than one splat in restriction\" if found_splat\n            found_splat = true\n\n            count = tuple_types.size - (other.type_vars.size - 1)\n            return nil unless count >= 0\n\n            arg_types = tuple_types[i, count]\n            arg_types_tuple = context.instantiated_type.program.tuple_of(arg_types)\n\n            restricted = arg_types_tuple.restrict(type_var.exp, context)\n            return nil unless restricted == arg_types_tuple\n\n            i += count\n          else\n            arg_type = tuple_types[i]\n            restricted = arg_type.restrict(type_var, context)\n            return unless restricted == arg_type\n\n            i += 1\n          end\n        end\n\n        return self\n      else\n        return nil unless other.type_vars.size == tuple_types.size\n\n        tuple_types.zip(other.type_vars) do |tuple_type, type_var|\n          restricted = tuple_type.restrict(type_var, context)\n          return nil unless restricted == tuple_type\n        end\n      end\n\n      self\n    end\n\n    def restrict(other : TupleInstanceType, context)\n      self.implements?(other) ? self : nil\n    end\n  end\n\n  class NamedTupleInstanceType\n    def restriction_of?(other : NamedTupleInstanceType, owner, self_free_vars = nil, other_free_vars = nil)\n      return true if self == other || self.implements?(other)\n\n      false\n    end\n\n    def restrict(other : Generic, context)\n      generic_type = get_generic_type(other, context)\n      return super unless generic_type == self.generic_type\n\n      unless other.type_vars.empty?\n        other.raise \"can only instantiate NamedTuple with named arguments\"\n      end\n\n      # Check for empty named tuples\n      unless other_named_args = other.named_args\n        return self.entries.empty? ? self : nil\n      end\n\n      # Check that the names are the same\n      other_names = other_named_args.map(&.name).sort!\n      self_names = self.entries.map(&.name).sort!\n\n      return nil unless self_names == other_names\n\n      # Now match name by name\n      other_named_args.each do |named_arg|\n        self_type = self.name_type(named_arg.name)\n        other_type = named_arg.value\n\n        restricted = self_type.restrict(other_type, context)\n        return nil unless restricted\n      end\n\n      self\n    end\n\n    def restrict(other : NamedTupleInstanceType, context)\n      self.implements?(other) ? self : nil\n    end\n  end\n\n  class VirtualType\n    def restriction_of?(other : Type, owner, self_free_vars = nil, other_free_vars = nil)\n      other = other.base_type if other.is_a?(VirtualType)\n      base_type.implements?(other) || other.implements?(base_type)\n    end\n\n    def restrict(other : Type, context)\n      other = other.remove_alias\n      base_type = self.base_type\n\n      if self == other\n        self\n      elsif other.is_a?(UnionType)\n        types = other.union_types.compact_map do |t|\n          restrict(t, context).as(Type?)\n        end\n        program.type_merge types\n      elsif other.is_a?(VirtualType)\n        result = base_type.restrict(other.base_type, context) || other.base_type.restrict(base_type, context)\n        result ? result.virtual_type : nil\n      elsif other.implements?(base_type)\n        other.virtual_type\n      elsif base_type.implements?(other)\n        self\n      elsif other.module?\n        if base_type.implements?(other)\n          self\n        else\n          types = base_type.subclasses.compact_map do |subclass|\n            subclass.virtual_type.restrict(other, context).as(Type?)\n          end\n          program.type_merge_union_of types\n        end\n      elsif base_type.is_a?(GenericInstanceType) && other.is_a?(GenericType)\n        # Consider the case of Foo(Int32) vs. Bar(T), with Bar(T) < Foo(T):\n        # we want to return Bar(Int32), so we search in Bar's generic instantiations\n        types = other.instantiated_types.compact_map do |instance|\n          next if instance.unbound? || instance.abstract?\n          instance.virtual_type if instance.implements?(base_type)\n        end\n        program.type_merge_union_of types\n      else\n        nil\n      end\n    end\n\n    def restrict(other : Generic, context)\n      # Restrict first against the base type\n      restricted = base_type.restrict(other, context)\n      if restricted\n        return restricted.virtual_type\n      end\n\n      types = base_type.subclasses.compact_map do |subclass|\n        subclass.virtual_type.restrict(other, context).as(Type?)\n      end\n      program.type_merge_union_of types\n    end\n  end\n\n  class VirtualMetaclassType\n    def restrict(other : Metaclass, context)\n      instance_type.restrict(other.name, context).try &.metaclass\n    end\n  end\n\n  class NonGenericModuleType\n    def restrict(other, context)\n      super || including_types.try(&.restrict(other, context))\n    end\n  end\n\n  class GenericModuleInstanceType\n    def restrict(other : Type, context)\n      super || including_types.try(&.restrict(other, context))\n    end\n  end\n\n  class AliasType\n    def restriction_of?(other : Underscore, owner, self_free_vars = nil, other_free_vars = nil)\n      true\n    end\n\n    def restriction_of?(other, owner, self_free_vars = nil, other_free_vars = nil)\n      return true if self == other\n\n      remove_alias.restriction_of?(other, owner, self_free_vars, other_free_vars)\n    end\n\n    def restrict(other : Path, context)\n      if first_name = other.single_name?\n        if context.has_unbound_free_var?(first_name)\n          return context.bind_free_var(first_name, self)\n        end\n      end\n\n      other_type = context.defining_type.lookup_path other\n      if other_type\n        if other_type == self\n          return self\n        end\n      else\n        if first_name = other.single_name?\n          if context.defining_type.type_var?(first_name)\n            return context.bind_free_var(first_name, self)\n          else\n            other.raise_undefined_constant(context.defining_type)\n          end\n        end\n      end\n\n      remove_alias.restrict(other, context)\n    end\n\n    def restrict(other : AliasType, context)\n      return self if self == other\n\n      if !self.simple? && !other.simple?\n        return nil\n      end\n\n      remove_alias.restrict(other, context)\n    end\n\n    def restrict(other, context)\n      return self if self == other\n\n      remove_alias.restrict(other, context)\n    end\n  end\n\n  class TypeDefType\n    def restrict(other : UnionType, context)\n      super\n    end\n\n    def restrict(other : AliasType, context)\n      other = other.remove_alias\n      return self if self == other\n      restrict(other, context)\n    end\n\n    def restrict(other : Type, context)\n      return self if self == other\n\n      restricted = typedef.restrict(other, context)\n      if restricted == typedef\n        self\n      elsif restricted.is_a?(UnionType)\n        program.type_merge(restricted.union_types.map { |t| t == typedef ? self : t })\n      else\n        restricted\n      end\n    end\n  end\n\n  class MetaclassType\n    def restrict(other : Metaclass, context)\n      restricted = instance_type.restrict(other.name, context)\n      instance_type == restricted ? self : nil\n    end\n\n    def restrict(other : VirtualMetaclassType, context)\n      # A module class can't be restricted into a class\n      return nil if instance_type.module?\n\n      restricted = instance_type.restrict(other.instance_type.base_type, context)\n      restricted ? self : nil\n    end\n\n    def restriction_of?(other : VirtualMetaclassType, owner, self_free_vars = nil, other_free_vars = nil)\n      restriction_of?(other.base_type.metaclass, owner, self_free_vars, other_free_vars)\n    end\n  end\n\n  class GenericClassInstanceMetaclassType\n    def restrict(other : Metaclass, context)\n      restricted = instance_type.restrict(other.name, context)\n      instance_type == restricted ? self : nil\n    end\n\n    def restrict(other : MetaclassType, context)\n      return self if instance_type.generic_type.metaclass == other\n\n      restricted = instance_type.restrict(other.instance_type, context)\n      restricted ? self : nil\n    end\n  end\n\n  class GenericModuleInstanceMetaclassType\n    def restrict(other : Metaclass, context)\n      restricted = instance_type.restrict(other.name, context)\n      instance_type == restricted ? self : nil\n    end\n\n    def restrict(other : MetaclassType, context)\n      return self if instance_type.generic_type.metaclass == other\n\n      restricted = instance_type.restrict(other.instance_type, context)\n      restricted ? self : nil\n    end\n  end\n\n  class ProcInstanceType\n    def restrict(other : ProcNotation, context)\n      inputs = other.inputs\n      inputs_size = inputs ? inputs.size : 0\n      output = other.output\n\n      # Consider the case of a splat in the type vars\n      if inputs && inputs.any?(Splat)\n        i = 0\n        inputs.each do |input|\n          if input.is_a?(Splat)\n            count = arg_types.size - (inputs.size - 1)\n            return nil unless count >= 0\n\n            input_arg_types = arg_types[i, count]\n            input_arg_types_tuple = context.instantiated_type.program.tuple_of(input_arg_types)\n\n            restricted = input_arg_types_tuple.restrict(input.exp, context)\n            return nil unless restricted == input_arg_types_tuple\n\n            i += count\n          else\n            arg_type = arg_types[i]\n            restricted = arg_type.restrict(input, context)\n            return unless restricted == arg_type\n\n            i += 1\n          end\n        end\n      else\n        return nil if arg_types.size != inputs_size\n\n        if inputs\n          inputs.zip(arg_types) do |input, my_input|\n            restricted = my_input.restrict(input, context)\n            return nil unless restricted == my_input\n          end\n        end\n      end\n\n      if output\n        my_output = self.return_type\n        if my_output.no_return?\n          # Ok, NoReturn can be \"cast\" to anything\n        else\n          restricted = my_output.restrict(output, context)\n          return nil unless restricted == my_output\n        end\n\n        self\n      else\n        program.proc_of(arg_types + [program.void])\n      end\n    end\n\n    def restrict(other : ProcInstanceType, context)\n      compatible_with?(other) ? other : nil\n    end\n\n    def restrict(other : Generic, context)\n      generic_type = get_generic_type(other, context)\n      return super unless generic_type.is_a?(ProcType)\n\n      # Consider the case of a splat in the type vars\n      splat_given = other.type_vars.any?(Splat)\n      if splat_given\n        proc_types = arg_types + [return_type]\n\n        i = 0\n        other.type_vars.each do |type_var|\n          if type_var.is_a?(Splat)\n            count = proc_types.size - (other.type_vars.size - 1)\n            return nil unless count >= 0\n\n            arg_types = proc_types[i, count]\n            arg_types_tuple = context.instantiated_type.program.tuple_of(arg_types)\n\n            restricted = arg_types_tuple.restrict(type_var.exp, context)\n            return nil unless restricted == arg_types_tuple\n\n            i += count\n          else\n            arg_type = proc_types[i]\n            restricted = arg_type.restrict(type_var, context)\n            return unless restricted == arg_type\n\n            i += 1\n          end\n        end\n\n        return self\n      end\n\n      unless other.type_vars.size == arg_types.size + 1\n        return nil\n      end\n\n      other.type_vars.each_with_index do |other_type_var, i|\n        # If checking the return type\n        if i == other.type_vars.size - 1\n          # any type matches Nil\n          if nil_type?(other_type_var, context)\n            # Also, all other types matched, so the matching type is this proc type\n            # except that it has a Nil return type\n            new_proc_arg_types = arg_types.dup\n            new_proc_arg_types << program.nil_type\n            return program.proc_of(new_proc_arg_types)\n          end\n\n          if return_type.no_return?\n            # Ok, NoReturn can be \"cast\" to anything\n            next\n          end\n        end\n\n        proc_type = arg_types[i]? || return_type\n        restricted = proc_type.restrict other_type_var, context\n        return nil unless restricted == proc_type\n      end\n\n      self\n    end\n\n    def nil_type?(node, context)\n      node.is_a?(Path) && context.defining_type.lookup_path(node).is_a?(NilType)\n    end\n\n    def compatible_with?(other : ProcInstanceType)\n      if return_type == other.return_type\n        # Ok\n      elsif other.return_type.nil_type?\n        # Ok, can cast fun to void\n      elsif return_type.no_return?\n        # Ok, NoReturn can be \"cast\" to anything\n      else\n        return false\n      end\n\n      # Disallow casting a function to another one accepting different argument count\n      return false if arg_types.size != other.arg_types.size\n\n      arg_types.zip(other.arg_types) do |arg_type, other_arg_type|\n        return false unless arg_type == other_arg_type\n      end\n\n      true\n    end\n  end\n\n  class AutocastType\n    # Returns true if the AST node associated with `self` denotes a value of the\n    # given *type*.\n    def matches_exactly?(type : Type) : Bool\n      false\n    end\n\n    # Returns true if the AST node associated with `self` denotes a value that\n    # may be interpreted in the given *type*, but is itself not of that type.\n    def matches_partially?(type : Type) : Bool\n      false\n    end\n\n    def restrict(other, context)\n      if other.is_a?(Type)\n        if matches_exactly?(other)\n          set_exact_match(other)\n          return other\n        elsif !exact_match? && matches_partially?(other)\n          add_match(other)\n          return other\n        end\n      end\n\n      literal_type = literal.type?\n      type = literal_type.try(&.restrict(other, context)) || super(other, context)\n      if type == self\n        # if *other* is an AST node (e.g. `Path`) or a complex type (e.g.\n        # `UnionType`), `@match` may be set from recursive calls to `#restrict`,\n        # so we propagate any exact matches found during those calls\n        type = @match || literal_type\n      end\n      type\n    end\n\n    def compatible_with?(type)\n      matches_exactly?(type) || matches_partially?(type)\n    end\n  end\n\n  class NumberAutocastType\n    def matches_exactly?(type : IntegerType | FloatType) : Bool\n      literal.type == type\n    end\n\n    def matches_partially?(type : IntegerType | FloatType) : Bool\n      literal = self.literal\n\n      if literal.is_a?(NumberLiteral)\n        literal.representable_in?(type)\n      else\n        literal_type = literal.type\n        (literal_type.is_a?(IntegerType) || literal_type.is_a?(FloatType)) && literal_type.subset_of?(type)\n      end\n    end\n  end\n\n  class SymbolAutocastType\n    def matches_exactly?(type : SymbolType) : Bool\n      true\n    end\n\n    def matches_partially?(type : EnumType) : Bool\n      !type.find_member(literal.value).nil?\n    end\n  end\nend\n\nprivate def get_generic_type(node, context)\n  name = node.name\n  if name.is_a?(Crystal::Path)\n    context.defining_type.lookup_path name\n  else\n    name.type\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/restrictions_augmenter.cr",
    "content": "module Crystal\n  class RestrictionsAugmenter < Visitor\n    @args_hash : Hash(String, Arg)?\n    @current_type : Type\n    @def : Def?\n\n    # If an assignment happens conditionally we can't add a type\n    # restriction because... we can't be sure the assignment will happen!\n    @conditional_nest = 0\n\n    def initialize(@program : Program, @new_expansions : Hash(Def, Def))\n      @current_type = @program\n    end\n\n    def visit(node : ExpandableNode)\n      if expanded = node.expanded\n        expanded.accept self\n      end\n      false\n    end\n\n    def visit(node : ClassDef)\n      old_type = @current_type\n      @current_type = node.resolved_type\n      node.body.accept self\n      @current_type = old_type\n      false\n    end\n\n    def visit(node : Def)\n      @def = node\n      @args_hash = args_hash = {} of String => Arg\n      node.args.each do |arg|\n        next if arg.restriction\n        args_hash[arg.name] = arg\n      end\n      node.body.accept self\n      @args_hash = nil\n      @def = nil\n      false\n    end\n\n    def visit(node : If)\n      node.cond.accept self\n      @conditional_nest += 1\n      node.then.accept self\n      node.else.try &.accept self\n      @conditional_nest -= 1\n      false\n    end\n\n    def visit(node : While)\n      node.cond.accept self\n      @conditional_nest += 1\n      node.body.accept self\n      @conditional_nest -= 1\n      false\n    end\n\n    def visit(node : Call)\n      if expanded = node.expanded\n        expanded.accept self\n        return false\n      end\n\n      node.obj.try &.accept self\n      node.args.each &.accept self\n      node.named_args.try &.each &.value.accept self\n      node.block.try do |block|\n        @conditional_nest += 1\n        block.accept self\n        @conditional_nest -= 1\n      end\n      node.block_arg.try &.accept self\n      false\n    end\n\n    def visit(node : Assign)\n      args_hash = @args_hash\n      return false unless args_hash\n\n      current_def = @def\n      return false unless current_def\n\n      target = node.target\n      value = node.value\n      current_type = @current_type\n\n      if target.is_a?(Var)\n        args_hash.delete(target.name)\n        return false\n      end\n\n      return false unless @conditional_nest == 0\n      return false unless value.is_a?(Var)\n\n      arg = args_hash[value.name]?\n      return false unless arg\n\n      case target\n      when InstanceVar\n        return false unless current_type.is_a?(InstanceVarContainer)\n\n        ivar = current_type.instance_vars[target.name]?\n        return false unless ivar\n\n        augment(target, value, current_type, current_def, arg, ivar.type)\n      when ClassVar\n        return false unless current_type.is_a?(ClassVarContainer)\n\n        cvar = current_type.class_vars[target.name]?\n        return false unless cvar\n\n        augment(target, value, current_type, current_def, arg, cvar.type)\n      end\n\n      false\n    end\n\n    def visit(node : ASTNode)\n      true\n    end\n\n    private def augment(target, value, current_type, current_def, arg, type)\n      converter = TypeToRestriction.new(current_type)\n\n      restriction = converter.convert(type)\n      return unless restriction\n\n      arg.restriction = restriction\n\n      # If this is an initialize, we can also add a type restriction to the\n      # auto-generated \"new\" so that it shows up in docs.\n      return unless current_def.name == \"initialize\"\n\n      expansion = @new_expansions[current_def]?\n      return unless expansion\n\n      expansion_arg = expansion.args.find do |expansion_arg|\n        expansion_arg.name == arg.name\n      end\n      return unless expansion_arg\n\n      expansion_arg.restriction = restriction.dup\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/semantic_visitor.cr",
    "content": "# Base visitor for semantic analysis. It traverses the whole\n# ASTNode tree, keeping a `current_type` in context, which corresponds\n# to the type being visited according to class/module/lib definitions.\nabstract class Crystal::SemanticVisitor < Crystal::Visitor\n  getter program : Program\n\n  # At every point there's a current type.\n  # In the beginning this is the `Program` (top-level), but when\n  # a class definition is visited this changes to that type, and so on.\n  property current_type : ModuleType\n\n  property! scope : Type\n  setter scope\n\n  property vars : MetaVars\n\n  @path_lookup : Type?\n  @untyped_def : Def?\n  @typed_def : Def?\n  @block : Block?\n\n  def initialize(@program, @vars = MetaVars.new)\n    @current_type = @program\n    @exp_nest = 0\n    @in_lib = false\n    @in_c_struct_or_union = false\n    @in_is_a = false\n  end\n\n  # Transform require to its source code.\n  # The source code can be a Nop if the file was already required.\n  def visit(node : Require)\n    if expanded = node.expanded\n      expanded.accept self\n      return false\n    end\n\n    if inside_exp?\n      node.raise \"can't require dynamically\"\n    end\n\n    location = node.location\n    filename = node.string\n    relative_to = location.try &.original_filename\n\n    # Remember that the program depends on this require\n    @program.record_require(filename, relative_to)\n\n    filenames = begin\n      @program.find_in_path(filename, relative_to)\n    rescue ex : CrystalPath::NotFoundError\n      message = \"can't find file '#{ex.filename}'\"\n      notes = [] of String\n\n      if ex.filename.starts_with? '.'\n        if relative_to\n          message += \" relative to '#{relative_to}'\"\n        end\n      else\n        notes << <<-NOTE\n          If you're trying to require a shard:\n          - Did you remember to run `shards install`?\n          - Did you make sure you're running the compiler in the same directory as your shard.yml?\n          NOTE\n      end\n\n      node.raise \"#{message}\\n\\n#{notes.join(\"\\n\")}\"\n    end\n\n    if filenames\n      nodes = Array(ASTNode).new(filenames.size)\n\n      @program.run_requires(node, filenames) do |filename|\n        nodes << require_file(node, filename)\n      end\n\n      expanded = Expressions.from(nodes)\n    else\n      expanded = Nop.new\n    end\n\n    node.expanded = expanded\n    node.bind_to(expanded)\n    false\n  end\n\n  private def require_file(node : Require, filename : String)\n    parser = @program.new_parser(File.read(filename))\n    parser.filename = filename\n    parser.wants_doc = @program.wants_doc?\n    begin\n      parsed_nodes = parser.parse\n      parsed_nodes = @program.normalize(parsed_nodes, inside_exp: inside_exp?)\n      # We must type the node immediately, in case a file requires another\n      # *before* one of the files in `filenames`\n      parsed_nodes.accept self\n    rescue ex : CodeError\n      node.raise \"while requiring \\\"#{node.string}\\\"\", ex\n    rescue ex\n      raise Error.new \"while requiring \\\"#{node.string}\\\"\", ex\n    end\n\n    FileNode.new(parsed_nodes, filename)\n  end\n\n  def visit(node : ClassDef)\n    check_outside_exp node, \"declare class\"\n    pushing_type(node.resolved_type) do\n      node.hook_expansions.try &.each &.accept self\n      node.body.accept self\n    end\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : ModuleDef)\n    check_outside_exp node, \"declare module\"\n    pushing_type(node.resolved_type) do\n      node.body.accept self\n    end\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : AnnotationDef)\n    check_outside_exp node, \"declare annotation\"\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : EnumDef)\n    check_outside_exp node, \"declare enum\"\n    pushing_type(node.resolved_type) do\n      node.members.each &.accept self\n    end\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : LibDef)\n    check_outside_exp node, \"declare lib\"\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : Include)\n    check_outside_exp node, \"include\"\n    node.hook_expansions.try &.each &.accept self\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : Extend)\n    check_outside_exp node, \"extend\"\n    node.hook_expansions.try &.each &.accept self\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : Alias)\n    check_outside_exp node, \"declare alias\"\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : Def)\n    check_outside_exp node, \"declare def\"\n    node.hook_expansions.try &.each &.accept self\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : Macro)\n    check_outside_exp node, \"declare macro\"\n    node.set_type(@program.nil)\n    false\n  end\n\n  def visit(node : Annotation)\n    annotations = @annotations ||= [] of Annotation\n    annotations << node\n    false\n  end\n\n  def visit(node : Call)\n    !expand_macro(node, raise_on_missing_const: false)\n  end\n\n  def visit(node : MacroExpression)\n    expand_inline_macro node\n    false\n  end\n\n  def visit(node : MacroIf)\n    expand_inline_macro node\n    false\n  end\n\n  def visit(node : MacroFor)\n    expand_inline_macro node\n    false\n  end\n\n  def visit(node : MacroVerbatim)\n    expansion = MacroIf.new(BoolLiteral.new(true), node)\n    expand_inline_macro expansion\n\n    node.expanded = expansion\n    node.bind_to expansion\n\n    false\n  end\n\n  def visit(node : ExternalVar | Path | Generic | ProcNotation | Union | Metaclass | Self | TypeOf)\n    false\n  end\n\n  def visit(node : ASTNode)\n    true\n  end\n\n  def visit_any(node)\n    @exp_nest += 1 if nesting_exp?(node)\n\n    true\n  end\n\n  def end_visit_any(node)\n    @exp_nest -= 1 if nesting_exp?(node)\n\n    if @annotations\n      case node\n      when Expressions\n        # Nothing, will be taken care in individual expressions\n      when Annotation\n        # Nothing\n      when Nop\n        # Nothing (might happen as a result of an evaluated macro if)\n      when Call\n        # Don't clear annotations if these were generated by a macro\n        unless node.expanded\n          @annotations = nil\n        end\n      when MacroExpression, MacroIf, MacroFor\n        # Don't clear annotations if these were generated by a macro\n      else\n        @annotations = nil\n      end\n    end\n  end\n\n  # Returns free variables\n  def free_vars : Hash(String, TypeVar)?\n    nil\n  end\n\n  def nesting_exp?(node)\n    case node\n    when Expressions, LibDef, CStructOrUnionDef, ClassDef, ModuleDef, FunDef, Def, Macro,\n         Alias, Include, Extend, EnumDef, VisibilityModifier, MacroFor, MacroIf, MacroExpression,\n         FileNode, TypeDeclaration, Require, AnnotationDef\n      false\n    else\n      true\n    end\n  end\n\n  def lookup_type(node : ASTNode,\n                  free_vars = nil,\n                  find_root_generic_type_parameters = true)\n    current_type.lookup_type(\n      node,\n      free_vars: free_vars,\n      allow_typeof: false,\n      find_root_generic_type_parameters: find_root_generic_type_parameters\n    )\n  end\n\n  def check_outside_exp(node, op)\n    node.raise \"can't #{op} dynamically\" if inside_exp?\n  end\n\n  def expand_macro(node, raise_on_missing_const = true, first_pass = false, accept = true)\n    if expanded = node.expanded\n      @exp_nest -= 1\n      eval_macro(node) do\n        expanded.accept self if accept\n      end\n      @exp_nest += 1\n      return true\n    end\n\n    obj = node.obj\n    case obj\n    when Path\n      base_type = @path_lookup || @scope || @current_type\n      macro_scope = base_type.lookup_type_var?(obj, free_vars: free_vars, raise: raise_on_missing_const)\n      return false unless macro_scope.is_a?(Type)\n\n      macro_scope = macro_scope.remove_alias\n\n      the_macro = macro_scope.metaclass.lookup_macro(node.name, node.args, node.named_args)\n      node.raise \"private macro '#{node.name}' called for #{obj}\" if the_macro.is_a?(Macro) && the_macro.visibility.private?\n    when Nil\n      return false if node.super? || node.previous_def?\n      the_macro = node.lookup_macro\n    else\n      return false\n    end\n\n    return false unless the_macro.is_a?(Macro)\n\n    # If we find a macro outside a def/block and this is not the first pass it means that the\n    # macro was defined before we first found this call, so it's an error\n    # (we must analyze the macro expansion in all passes)\n    if !@typed_def && !@block && !first_pass\n      node.raise \"macro '#{node.name}' must be defined before this point but is defined later\"\n    end\n\n    expansion_scope = (macro_scope || @scope || current_type)\n\n    args, named_args = expand_macro_arguments(node, expansion_scope)\n\n    @exp_nest -= 1\n    generated_nodes = expand_macro(the_macro, node, visibility: node.visibility, accept: accept) do\n      old_args, old_named_args = node.args, node.named_args\n      node.args, node.named_args = args, named_args\n      expanded_macro, macro_expansion_pragmas = @program.expand_macro the_macro, node, expansion_scope, expansion_scope, @untyped_def\n      node.args, node.named_args = old_args, old_named_args\n      {expanded_macro, macro_expansion_pragmas}\n    end\n    @exp_nest += 1\n\n    node.expanded = generated_nodes\n    node.expanded_macro = the_macro\n    node.bind_to generated_nodes\n\n    true\n  end\n\n  def expand_macro(the_macro, node, mode = nil, *, visibility : Visibility, accept = true, &)\n    expanded_macro, macro_expansion_pragmas =\n      eval_macro(node) do\n        yield\n      end\n\n    mode ||= if @in_c_struct_or_union\n               Parser::ParseMode::LibStructOrUnion\n             elsif @in_lib\n               Parser::ParseMode::Lib\n             else\n               Parser::ParseMode::Normal\n             end\n\n    # We could do Set.new(@vars.keys) but that creates an intermediate array\n    local_vars = Set(String).new(initial_capacity: @vars.size)\n    @vars.each_key { |key| local_vars << key }\n\n    generated_nodes = @program.parse_macro_source(expanded_macro, macro_expansion_pragmas, the_macro, node, local_vars,\n      current_def: @typed_def,\n      inside_type: !current_type.is_a?(Program),\n      inside_exp: @exp_nest > 0,\n      mode: mode,\n      visibility: visibility,\n    )\n\n    node.doc ||= annotations_doc @annotations\n\n    if node_doc = node.doc\n      generated_nodes.accept PropagateDocVisitor.new(node_doc)\n    end\n\n    generated_nodes.accept self if accept\n    generated_nodes\n  end\n\n  class PropagateDocVisitor < Visitor\n    @doc : String\n\n    def initialize(@doc)\n    end\n\n    def visit(node : ClassDef | ModuleDef | EnumDef | Def | FunDef | Macro | AnnotationDef | Alias | Assign | Call)\n      node.doc ||= @doc\n      false\n    end\n\n    def visit(node : ASTNode)\n      true\n    end\n  end\n\n  def expand_macro_arguments(call, expansion_scope)\n    # If any argument is a MacroExpression, solve it first and\n    # replace Path with Const/TypeNode if it denotes such thing\n    args = call.args\n    named_args = call.named_args\n\n    if args.any?(MacroExpression) || named_args.try &.any? &.value.is_a?(MacroExpression)\n      @exp_nest -= 1\n      args = args.map do |arg|\n        expand_macro_argument(arg, expansion_scope)\n      end\n      named_args = named_args.try &.map do |named_arg|\n        value = expand_macro_argument(named_arg.value, expansion_scope)\n        NamedArgument.new(named_arg.name, value)\n      end\n      @exp_nest += 1\n    end\n\n    {args, named_args}\n  end\n\n  def expand_macro_argument(node, expansion_scope)\n    if node.is_a?(MacroExpression)\n      node.accept self\n      expanded = node.expanded.not_nil!\n      if expanded.is_a?(Path)\n        expanded_type = expansion_scope.lookup_path(expanded)\n        case expanded_type\n        when Const\n          expanded = expanded_type.value\n        when Type\n          expanded = TypeNode.new(expanded_type)\n        end\n      end\n      expanded\n    else\n      node\n    end\n  end\n\n  def expand_inline_macro(node, mode = nil, accept = true)\n    if expanded = node.expanded\n      eval_macro(node) do\n        expanded.accept self if accept\n      end\n      return expanded\n    end\n\n    the_macro = Macro.new(\"macro_#{node.object_id}\", [] of Arg, node).at(node)\n\n    skip_macro_exception = nil\n\n    generated_nodes = expand_macro(the_macro, node, mode: mode, visibility: :public, accept: accept) do\n      @program.expand_macro node, (@scope || current_type), @path_lookup, free_vars, @untyped_def\n    rescue ex : SkipMacroException\n      skip_macro_exception = ex\n      {ex.expanded_before_skip, ex.macro_expansion_pragmas}\n    end\n\n    node.expanded = generated_nodes\n    node.bind_to generated_nodes\n\n    raise skip_macro_exception if skip_macro_exception\n\n    generated_nodes\n  end\n\n  def eval_macro(node, &)\n    yield\n  rescue ex : TopLevelMacroRaiseException\n    # If the node that caused a top level macro raise is a `Call`, it denotes it happened within the context of a macro.\n    # In this case, we want the inner most exception to be the call of the macro itself so that it's the last frame in the trace.\n    # This will make the actual `#raise` method call be the first frame.\n    if node.is_a? Call\n      ex.inner = Crystal::MacroRaiseException.for_node node, ex.message\n    end\n\n    # Otherwise, if the current node is _NOT_ a `Call`, it denotes a top level raise within a method.\n    # In this case, we want the same behavior as if it were a `Call`, but do not want to set the inner exception here since that will be handled via `Call#bubbling_exception`.\n    # So just re-raise the exception to keep the original location intact.\n    raise ex\n  rescue ex : MacroRaiseException\n    # Raise another exception on this node, keeping the original as the inner exception.\n    # This will retain the location of the node specific raise as the last frame, while also adding in this node into the trace.\n    #\n    # If the original exception does not have a location, it'll essentially be dropped and this node will take its place as the last frame.\n    node.raise ex.message, ex, exception_type: Crystal::MacroRaiseException\n  rescue ex : Crystal::CodeError\n    node.raise \"expanding macro\", ex\n  end\n\n  def process_annotations(annotations, &)\n    annotations.try &.each do |ann|\n      annotation_type = lookup_annotation(ann)\n      validate_annotation(annotation_type, ann)\n      yield annotation_type, ann\n    end\n  end\n\n  def lookup_annotation(ann)\n    # TODO: Since there's `Int::Primitive`, and now we'll have\n    # `::Primitive`, but there's no way to specify ::Primitive\n    # just yet in annotations, we temporarily hardcode\n    # that `Primitive` inside annotations means the top\n    # level primitive.\n    # We also have the same problem with File::Flags, which\n    # is an enum marked with Flags annotation.\n    if ann.path.single?(\"Primitive\")\n      type = @program.primitive_annotation\n    elsif ann.path.single?(\"Flags\")\n      type = @program.flags_annotation\n    else\n      type = lookup_type(ann.path)\n    end\n\n    unless type.is_a?(AnnotationType)\n      ann.raise \"#{ann.path} is not an annotation, it's a #{type.type_desc}\"\n    end\n\n    type\n  end\n\n  def validate_annotation(annotation_type, ann)\n    case annotation_type\n    when @program.deprecated_annotation\n      # Check whether a DeprecatedAnnotation can be built.\n      # There is no need to store it, but enforcing\n      # arguments makes sense here.\n      DeprecatedAnnotation.from(ann)\n    when @program.experimental_annotation\n      # ditto DeprecatedAnnotation\n      ExperimentalAnnotation.from(ann)\n    end\n  end\n\n  private def annotations_doc(annotations)\n    annotations.try(&.first?).try &.doc\n  end\n\n  def check_class_var_annotations\n    thread_local = false\n    process_annotations(@annotations) do |annotation_type, ann|\n      if annotation_type == @program.thread_local_annotation\n        thread_local = true\n      else\n        ann.raise \"class variables can only be annotated with ThreadLocal\"\n      end\n    end\n    thread_local\n  end\n\n  def check_allowed_in_lib(node, type = node.type.instance_type)\n    unless type.allowed_in_lib?\n      msg = String.build do |msg|\n        msg << \"only primitive types, pointers, structs, unions, enums and tuples are allowed in lib declarations, not #{type}\"\n        msg << \" (did you mean LibC::Int?)\" if type == @program.int\n        msg << \" (did you mean LibC::Float?)\" if type == @program.float\n      end\n      node.raise msg\n    end\n\n    type\n  end\n\n  def check_declare_var_type(node, declared_type, variable_kind)\n    type = declared_type.instance_type\n\n    if type.is_a?(GenericClassType)\n      node.raise \"can't declare variable of generic non-instantiated type #{type}\"\n    end\n\n    unless type.can_be_stored?\n      node.raise \"can't use #{type} as the type of #{variable_kind} yet, use a more specific type\"\n    end\n\n    declared_type\n  end\n\n  def class_var_owner(node)\n    scope = (@scope || current_type).class_var_owner\n\n    if scope.is_a?(Program)\n      node.raise \"can't use class variables at the top level\"\n    end\n\n    scope.as(ClassVarContainer)\n  end\n\n  def interpret_enum_value(node : ASTNode, target_type : IntegerType? = nil)\n    MathInterpreter\n      .new(current_type, self, target_type)\n      .interpret(node)\n  end\n\n  def inside_exp?\n    @exp_nest > 0\n  end\n\n  def pushing_type(type : ModuleType, &)\n    old_type = @current_type\n    @current_type = type\n    read_annotations\n    yield\n    @current_type = old_type\n  end\n\n  # Returns the current annotations and clears them for subsequent readers.\n  def read_annotations\n    annotations = @annotations\n    @annotations = nil\n    annotations\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/suggestions.cr",
    "content": "require \"levenshtein\"\nrequire \"../types\"\n\nmodule Crystal\n  class Type\n    SuggestableDefName = /\\A[a-z_]/\n\n    def lookup_similar_path(node : Path)\n      (node.global? ? program : self).lookup_similar_path(node.names)\n    end\n\n    def lookup_similar_path(names : Array(String), lookup_in_namespace = true)\n      type = self\n      names.each_with_index do |name, idx|\n        previous_type = type\n        type = previous_type.lookup_name(name)\n        unless type\n          best_match = Levenshtein.find(name.downcase) do |finder|\n            previous_type.remove_alias.types?.try &.each_key do |type_name|\n              finder.test(type_name.downcase, type_name)\n            end\n          end\n\n          if best_match\n            return (names[0...idx] + [best_match]).join \"::\"\n          else\n            break\n          end\n        end\n      end\n\n      parents.try &.each do |parent|\n        match = parent.lookup_similar_path(names, false)\n        return match if match\n      end\n\n      lookup_in_namespace && self != program ? namespace.lookup_similar_path(names) : nil\n    end\n\n    def lookup_similar_def(name, args_size, block)\n      return nil unless name =~ SuggestableDefName\n\n      if (defs = self.defs)\n        best_def = nil\n        best_match = nil\n        Levenshtein.find(name) do |finder|\n          defs.each do |def_name, hash|\n            if def_name =~ SuggestableDefName\n              hash.each do |def_with_metadata|\n                if def_with_metadata.max_size == args_size && def_with_metadata.yields == !!block && def_with_metadata.def.name != name\n                  finder.test(def_name)\n                  if finder.best_match != best_match\n                    best_match = finder.best_match\n                    best_def = def_with_metadata.def\n                  end\n                end\n              end\n            end\n          end\n        end\n        return best_def if best_def\n      end\n\n      parents.try &.each do |parent|\n        similar_def = parent.lookup_similar_def(name, args_size, block)\n        return similar_def if similar_def\n      end\n\n      nil\n    end\n\n    def lookup_similar_def_name(name, args_size, block)\n      lookup_similar_def(name, args_size, block).try &.name\n    end\n\n    def lookup_similar_instance_var_name(name)\n      Levenshtein.find(name, all_instance_vars.keys.select { |key| key != name })\n    end\n  end\n\n  class AliasType\n    delegate lookup_similar_def, to: aliased_type\n  end\n\n  class MetaclassType\n    delegate lookup_similar_path, to: instance_type\n  end\n\n  class GenericClassInstanceMetaclassType\n    delegate lookup_similar_path, to: instance_type\n  end\n\n  class GenericModuleInstanceMetaclassType\n    delegate lookup_similar_path, to: instance_type\n  end\n\n  class VirtualType\n    delegate lookup_similar_def, lookup_similar_path, to: base_type\n  end\n\n  class VirtualMetaclassType\n    delegate lookup_similar_path, to: instance_type\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/to_s.cr",
    "content": "require \"../syntax/to_s\"\n\nmodule Crystal\n  class ToSVisitor\n    def visit(node : Arg)\n      if parsed_annotations = node.parsed_annotations\n        parsed_annotations.each do |ann|\n          ann.accept self\n          @str << ' '\n        end\n      end\n\n      case @current_arg_type\n      when .splat?        then @str << '*'\n      when .double_splat? then @str << \"**\"\n      when .block_arg?    then @str << '&'\n      end\n\n      if node.external_name != node.name\n        visit_named_arg_name(node.external_name)\n        @str << ' '\n      end\n      if node.name\n        @str << node.name\n      else\n        @str << '?'\n      end\n      if type = node.type?\n        @str << \" : \"\n        TypeNode.new(type).accept(self)\n      elsif restriction = node.restriction\n        @str << \" : \"\n        restriction.accept self\n      end\n      if default_value = node.default_value\n        @str << \" = \"\n        default_value.accept self\n      end\n      false\n    ensure\n      @current_arg_type = :none\n    end\n\n    def visit(node : Primitive)\n      @str << \"# primitive: \"\n      @str << node.name\n      false\n    end\n\n    def visit(node : MetaVar)\n      @str << node.name\n      false\n    end\n\n    def visit(node : MetaMacroVar)\n      @str << node.name\n      false\n    end\n\n    def visit(node : TypeFilteredNode)\n      false\n    end\n\n    def visit(node : TypeNode)\n      node.type.devirtualize.to_s(@str)\n      false\n    end\n\n    def visit(node : AssignWithRestriction)\n      @str << \"# type restriction: \"\n      node.assign.target.accept self\n      @str << \" : \"\n      node.restriction.accept self\n      @str << \" = \"\n      node.assign.value.accept self\n      false\n    end\n\n    def visit(node : YieldBlockBinder)\n      false\n    end\n\n    def visit(node : FileNode)\n      @str.puts\n      @str << \"# \" << node.filename\n      @str.puts\n      node.node.accept self\n      false\n    end\n\n    def visit(node : External)\n      node.fun_def?.try &.accept self\n      false\n    end\n\n    def visit(node : MacroId)\n      @str << node.value\n      false\n    end\n\n    def visit(node : Unreachable)\n      @str << %(raise \"unreachable\")\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/top_level_visitor.cr",
    "content": "require \"./semantic_visitor\"\n\n# In this pass we traverse the AST nodes to declare and process:\n# - class\n# - struct\n# - module\n# - include\n# - extend\n# - enum (checking their value, since these need to be numbers or simple math operations)\n# - macro\n# - def (without going inside them)\n# - alias (without resolution)\n# - constants (without checking their value)\n# - macro calls (only surface macros, because we don't go inside defs)\n# - lib and everything inside them\n# - fun with body (without going inside them)\n#\n# Macro calls are expanded, but only the first pass is done to them. This\n# allows macros to define new classes and methods.\n#\n# We also process @[Link] annotations.\n#\n# After this pass we have completely defined the whole class hierarchy,\n# including methods. After this point no new classes or methods can be introduced\n# since in next passes we only go inside methods and top-level code, but we already\n# analyzed top-level (surface) macros that could have expanded to class/method\n# definitions.\n#\n# Now that we know the whole hierarchy, when someone types Foo, we know whether Foo has\n# subclasses or not and we can tag it as \"virtual\" (having subclasses), but that concept\n# might disappear in the future and we'll make consider everything as \"maybe virtual\".\nclass Crystal::TopLevelVisitor < Crystal::SemanticVisitor\n  # These are `new` methods (values) that was created from `initialize` methods (keys)\n  getter new_expansions : Hash(Def, Def) = ({} of Def => Def).compare_by_identity\n\n  # All finished hooks and their scope\n  record FinishedHook, scope : ModuleType, macro : Macro\n  @finished_hooks = [] of FinishedHook\n\n  @method_added_running = false\n\n  @last_doc : String?\n\n  # special types recognized for `@[Primitive]`\n  private enum PrimitiveType\n    ReferenceStorageType\n  end\n\n  def visit(node : ClassDef)\n    check_outside_exp node, \"declare class\"\n\n    scope, name, type = lookup_type_def(node)\n\n    annotations = read_annotations\n\n    special_type = nil\n    process_annotations(annotations) do |annotation_type, ann|\n      case annotation_type\n      when @program.primitive_annotation\n        if ann.args.size != 1\n          ann.raise \"expected Primitive annotation to have one argument\"\n        end\n\n        arg = ann.args.first\n        unless arg.is_a?(SymbolLiteral)\n          arg.raise \"expected Primitive argument to be a symbol literal\"\n        end\n\n        value = arg.value\n        special_type = PrimitiveType.parse?(value)\n        unless special_type\n          arg.raise \"BUG: Unknown primitive type #{value.inspect}\"\n        end\n      end\n    end\n\n    created_new_type = false\n\n    if type\n      type = type.remove_alias\n\n      unless type.is_a?(ClassType)\n        node.raise \"#{name} is not a #{node.struct? ? \"struct\" : \"class\"}, it's a #{type.type_desc}\"\n      end\n\n      if node.struct? != type.struct?\n        node.raise \"#{name} is not a #{node.struct? ? \"struct\" : \"class\"}, it's a #{type.type_desc}\"\n      end\n\n      if type_vars = node.type_vars\n        if type.is_a?(GenericType)\n          check_reopened_generic(type, node, type_vars)\n        else\n          node.raise \"#{name} is not a generic #{type.type_desc}\"\n        end\n      end\n    else\n      created_new_type = true\n      case special_type\n      in Nil\n        if type_vars = node.type_vars\n          type = GenericClassType.new @program, scope, name, nil, type_vars, false\n          type.splat_index = node.splat_index\n        else\n          type = NonGenericClassType.new @program, scope, name, nil, false\n        end\n        type.abstract = node.abstract?\n        type.struct = node.struct?\n      in .reference_storage_type?\n        type_vars = node.type_vars\n        case\n        when !node.struct?\n          node.raise \"BUG: Expected ReferenceStorageType to be a struct type\"\n        when node.abstract?\n          node.raise \"BUG: Expected ReferenceStorageType to be a non-abstract type\"\n        when !type_vars\n          node.raise \"BUG: Expected ReferenceStorageType to be a generic type\"\n        when type_vars.size != 1\n          node.raise \"BUG: Expected ReferenceStorageType to have a single generic type parameter\"\n        when node.splat_index\n          node.raise \"BUG: Expected ReferenceStorageType to have no splat parameter\"\n        end\n        type = GenericReferenceStorageType.new @program, scope, name, @program.value, type_vars, false\n        type.declare_instance_var(\"@type_id\", @program.int32)\n        type.can_be_stored = false\n      end\n    end\n\n    type.private = true if node.visibility.private?\n\n    node_superclass = node.superclass\n    if node_superclass\n      if type_vars = node.type_vars\n        free_vars = {} of String => TypeVar\n        type_vars.each do |type_var|\n          free_vars[type_var] = type.as(GenericType).type_parameter(type_var)\n        end\n      else\n        free_vars = nil\n      end\n\n      # find_root_generic_type_parameters is false because\n      # we don't want to find T in this case:\n      #\n      # class A(T)\n      #   class B < T\n      #   end\n      # end\n      #\n      # We search for a superclass starting from the current\n      # type, A(T) in this case, but we don't want to find\n      # type parameters because they will always be unbound.\n      superclass = lookup_type(node_superclass,\n        free_vars: free_vars,\n        find_root_generic_type_parameters: false).devirtualize\n      case superclass\n      when GenericClassType\n        node_superclass.raise \"generic type arguments must be specified when inheriting #{superclass}\"\n      when NonGenericClassType, GenericClassInstanceType\n        if superclass == @program.enum\n          node_superclass.raise \"can't inherit Enum. Use the enum keyword to define enums\"\n        end\n      else\n        node_superclass.raise \"#{superclass} is not a class, it's a #{superclass.type_desc}\"\n      end\n    else\n      superclass = node.struct? ? program.struct : program.reference\n    end\n\n    if node.superclass && !created_new_type && type.superclass != superclass\n      node.raise \"superclass mismatch for class #{type} (#{superclass} for #{type.superclass})\"\n    end\n\n    if created_new_type && superclass\n      if node.struct? != superclass.struct?\n        node.raise \"can't make #{node.struct? ? \"struct\" : \"class\"} '#{node.name}' inherit #{superclass.type_desc} '#{superclass}'\"\n      end\n\n      if superclass.struct? && !superclass.abstract?\n        node.raise \"can't extend non-abstract struct #{superclass}\"\n      end\n\n      if type.is_a?(GenericReferenceStorageType) && superclass != @program.value\n        node.raise \"BUG: Expected reference_storage_type to inherit from Value\"\n      end\n    end\n\n    if created_new_type\n      type.superclass = superclass\n\n      # If it's SomeClass(T) < Foo(T), or SomeClass < Foo(Int32),\n      # we want to add SomeClass as a subclass of Foo(T)\n      if superclass.is_a?(GenericClassInstanceType)\n        superclass.generic_type.add_subclass(type)\n      end\n      scope.types[name] = type\n    end\n\n    node.resolved_type = type\n\n    process_annotations(annotations) do |annotation_type, ann|\n      if node.struct? && type.is_a?(NonGenericClassType)\n        case annotation_type\n        when @program.extern_annotation\n          unless type.is_a?(NonGenericClassType)\n            node.raise \"can only use Extern annotation with non-generic structs\"\n          end\n\n          unless ann.args.empty?\n            ann.raise \"Extern annotation can't have positional arguments, only named arguments: 'union'\"\n          end\n\n          ann.named_args.try &.each do |named_arg|\n            case named_arg.name\n            when \"union\"\n              value = named_arg.value\n              if value.is_a?(BoolLiteral)\n                type.extern_union = value.value\n              else\n                value.raise \"Extern 'union' annotation must be a boolean, not #{value.class_desc}\"\n              end\n            else\n              named_arg.raise \"unknown Extern named argument, valid arguments are: 'union'\"\n            end\n          end\n\n          type.extern = true\n        when @program.packed_annotation\n          type.packed = true\n        else\n          # not a built-in annotation\n        end\n      end\n\n      type.add_annotation(annotation_type, ann)\n    end\n\n    attach_doc type, node, annotations\n\n    pushing_type(type) do\n      run_hooks(hook_type(superclass), type, :inherited, node) if created_new_type\n      node.body.accept self\n    rescue ex : MacroRaiseException\n      # Make the inner most exception to be the inherited node so that it's the last frame in the trace.\n      # This will make the location show on that node instead of the `raise` call.\n      ex.inner = Crystal::MacroRaiseException.for_node node, ex.message\n\n      raise ex\n    end\n\n    if created_new_type\n      type.force_add_subclass\n    end\n\n    false\n  end\n\n  def visit(node : ModuleDef)\n    check_outside_exp node, \"declare module\"\n\n    annotations = read_annotations\n\n    scope, name, type = lookup_type_def(node)\n\n    if type\n      type = type.remove_alias\n\n      unless type.module?\n        node.raise \"#{name} is not a module, it's a #{type.type_desc}\"\n      end\n\n      if type_vars = node.type_vars\n        if type.is_a?(GenericType)\n          check_reopened_generic(type, node, type_vars)\n        else\n          node.raise \"#{name} is not a generic module\"\n        end\n      end\n\n      type = type.as(ModuleType)\n    else\n      if type_vars = node.type_vars\n        type = GenericModuleType.new @program, scope, name, type_vars\n        type.splat_index = node.splat_index\n      else\n        type = NonGenericModuleType.new @program, scope, name\n      end\n      scope.types[name] = type\n    end\n\n    type.private = true if node.visibility.private?\n\n    node.resolved_type = type\n\n    attach_doc type, node, annotations\n\n    process_annotations(annotations) do |annotation_type, ann|\n      type.add_annotation(annotation_type, ann)\n    end\n\n    pushing_type(type) do\n      node.body.accept self\n    end\n\n    false\n  end\n\n  private def check_reopened_generic(generic, node, new_type_vars)\n    generic_type_vars = generic.type_vars\n    if new_type_vars != generic_type_vars || node.splat_index != generic.splat_index\n      msg = String.build do |io|\n        io << \"type var\"\n        io << 's' if generic_type_vars.size > 1\n        io << \" must be \"\n        generic_type_vars.each_with_index do |var, i|\n          io << \", \" if i > 0\n          io << '*' if i == generic.splat_index\n          var.to_s(io)\n        end\n        io << \", not \"\n        new_type_vars.each_with_index do |var, i|\n          io << \", \" if i > 0\n          io << '*' if i == node.splat_index\n          var.to_s(io)\n        end\n      end\n      node.raise msg\n    end\n  end\n\n  def visit(node : AnnotationDef)\n    check_outside_exp node, \"declare annotation\"\n\n    annotations = read_annotations\n    process_annotations(annotations) do |annotation_type, ann|\n      node.add_annotation(annotation_type, ann)\n    end\n\n    scope, name, type = lookup_type_def(node)\n\n    if type\n      unless type.is_a?(AnnotationType)\n        node.raise \"#{type} is not an annotation, it's a #{type.type_desc}\"\n      end\n    else\n      type = AnnotationType.new(@program, scope, name)\n      scope.types[name] = type\n    end\n\n    node.resolved_type = type\n\n    attach_doc type, node, annotations\n\n    false\n  end\n\n  def visit(node : Alias)\n    check_outside_exp node, \"declare alias\"\n\n    annotations = read_annotations\n\n    scope, name, existing_type = lookup_type_def(node)\n\n    if existing_type\n      if existing_type.is_a?(AliasType)\n        node.raise \"alias #{node.name} is already defined\"\n      else\n        node.raise \"can't alias #{node.name} because it's already defined as a #{existing_type.type_desc}\"\n      end\n    end\n\n    alias_type = AliasType.new(@program, scope, name, node.value)\n    process_annotations(annotations) do |annotation_type, ann|\n      alias_type.add_annotation(annotation_type, ann)\n    end\n    attach_doc alias_type, node, annotations\n    scope.types[name] = alias_type\n\n    alias_type.private = true if node.visibility.private?\n\n    node.resolved_type = alias_type\n\n    false\n  end\n\n  def visit(node : Macro)\n    check_outside_exp node, \"declare macro\"\n\n    annotations = read_annotations\n    process_annotations(annotations) do |annotation_type, ann|\n      node.add_annotation(annotation_type, ann)\n    end\n    node.doc ||= annotations_doc(annotations)\n    check_ditto node, node.location\n\n    node.args.each &.accept self\n    node.double_splat.try &.accept self\n    node.block_arg.try &.accept self\n\n    node.set_type @program.nil\n\n    if node.name == \"finished\"\n      unless node.args.empty?\n        node.raise \"wrong number of parameters for macro '#{node.name}' (given #{node.args.size}, expected 0)\"\n      end\n      @finished_hooks << FinishedHook.new(current_type, node)\n      return false\n    end\n\n    target = current_type.metaclass.as(ModuleType)\n    begin\n      target.add_macro node\n    rescue ex : Crystal::CodeError\n      node.raise ex.message\n    end\n\n    false\n  end\n\n  def visit(node : Arg)\n    if anns = node.parsed_annotations\n      process_annotations anns do |annotation_type, ann|\n        node.add_annotation annotation_type, ann\n      end\n    end\n\n    false\n  end\n\n  def visit(node : Def)\n    check_outside_exp node, \"declare def\"\n\n    annotations = read_annotations\n\n    process_def_annotations(node, annotations) do |annotation_type, ann|\n      if annotation_type == @program.primitive_annotation\n        process_def_primitive_annotation(node, ann)\n      end\n\n      node.add_annotation(annotation_type, ann)\n    end\n\n    node.doc ||= annotations_doc(annotations)\n    check_ditto node, node.location\n\n    node.args.each &.accept self\n    node.double_splat.try &.accept self\n    node.block_arg.try &.accept self\n\n    is_instance_method = false\n\n    target_type = case receiver = node.receiver\n                  when Nil\n                    is_instance_method = true\n                    current_type\n                  when Var\n                    unless receiver.name == \"self\"\n                      receiver.raise \"def receiver can only be a Type or self\"\n                    end\n                    current_type.metaclass\n                  else\n                    type = lookup_type(receiver)\n                    metaclass = type.metaclass\n                    case metaclass\n                    when LibType\n                      receiver.raise \"can't define method in lib #{metaclass}\"\n                    when GenericClassInstanceMetaclassType\n                      receiver.raise \"can't define method in generic instance #{metaclass}\"\n                    when GenericModuleInstanceMetaclassType\n                      receiver.raise \"can't define method in generic instance #{metaclass}\"\n                    end\n                    metaclass\n                  end\n\n    target_type = target_type.as(ModuleType)\n\n    if node.abstract?\n      if (target_type.class? || target_type.struct?) && !target_type.abstract?\n        node.raise \"can't define abstract def on non-abstract #{target_type.type_desc}\"\n      end\n      if target_type.metaclass?\n        node.raise \"can't define abstract def on metaclass\"\n      end\n    end\n\n    if target_type.struct? && !target_type.metaclass? && node.name == \"finalize\"\n      node.raise \"structs can't have finalizers because they are not tracked by the GC\"\n    end\n\n    if target_type.is_a?(EnumType) && node.name == \"initialize\"\n      node.raise \"enums can't define an `initialize` method, try using `def self.new`\"\n    end\n\n    target_type.add_def node\n\n    node.set_type @program.nil\n\n    if is_instance_method\n      # If it's an initialize method, we define a `self.new` for\n      # the type, initially empty. We will fill it once we know if\n      # a type defines a `finalize` method, but defining it now\n      # allows `previous_def` for a next `def self.new` definition\n      # to find this method.\n      if node.name == \"initialize\"\n        new_method = node.expand_new_signature_from_initialize(target_type)\n        target_type.metaclass.as(ModuleType).add_def(new_method)\n\n        # And we register it to later complete it\n        new_expansions[node] = new_method\n      end\n\n      if !@method_added_running && has_hooks?(target_type.metaclass)\n        @method_added_running = true\n        run_hooks target_type.metaclass, target_type, :method_added, node, Call.new(\"method_added\", node).at(node)\n        @method_added_running = false\n      end\n    end\n\n    false\n  end\n\n  private def process_def_primitive_annotation(node, ann)\n    if ann.args.size != 1\n      ann.raise \"expected Primitive annotation to have one argument\"\n    end\n\n    arg = ann.args.first\n    unless arg.is_a?(SymbolLiteral)\n      arg.raise \"expected Primitive argument to be a symbol literal\"\n    end\n\n    value = arg.value\n\n    primitive = Primitive.new(value)\n    primitive.location = node.location\n\n    node.body = primitive\n  end\n\n  def visit(node : Include)\n    check_outside_exp node, \"include\"\n    include_in current_type, node, :included\n    false\n  end\n\n  def visit(node : Extend)\n    check_outside_exp node, \"extend\"\n    include_in current_type.metaclass, node, :extended\n    false\n  end\n\n  def visit(node : LibDef)\n    check_outside_exp node, \"declare lib\"\n\n    annotations = read_annotations\n\n    scope, name, type = lookup_type_def(node)\n\n    if type\n      unless type.is_a?(LibType)\n        node.raise \"#{type} is not a lib, it's a #{type.type_desc}\"\n      end\n    else\n      type = LibType.new @program, scope, name\n      scope.types[name] = type\n    end\n\n    attach_doc type, node, annotations\n\n    node.resolved_type = type\n\n    type.private = true if node.visibility.private?\n\n    wasm_import_module = nil\n\n    process_annotations(annotations) do |annotation_type, ann|\n      case annotation_type\n      when @program.link_annotation\n        link_annotation = LinkAnnotation.from(ann)\n\n        if link_annotation.static?\n          @program.warnings.add_warning(ann, \"specifying static linking for individual libraries is deprecated\")\n        end\n\n        if ann.args.size > 1\n          @program.warnings.add_warning(ann, \"using non-named arguments for Link annotations is deprecated\")\n        end\n\n        if wasm_import_module && link_annotation.wasm_import_module\n          ann.raise \"multiple wasm import modules specified for lib #{type}\"\n        end\n\n        wasm_import_module = link_annotation.wasm_import_module\n\n        type.add_link_annotation(link_annotation)\n      when @program.call_convention_annotation\n        type.call_convention = parse_call_convention(ann, type.call_convention)\n      else\n        # not a built-in annotation\n      end\n      type.add_annotation(annotation_type, ann)\n    end\n\n    pushing_type(type) do\n      @in_lib = true\n      node.body.accept self\n      @in_lib = false\n    end\n\n    false\n  end\n\n  def visit(node : CStructOrUnionDef)\n    annotations = read_annotations\n\n    packed = false\n    unless node.union?\n      process_annotations(annotations) do |ann|\n        packed = true if ann == @program.packed_annotation\n      end\n    end\n\n    type = current_type.types[node.name]?\n    if type\n      unless type.is_a?(NonGenericClassType)\n        node.raise \"#{node.name} is already defined as #{type.type_desc}\"\n      end\n\n      if !type.extern? || (type.extern_union? != node.union?)\n        node.raise \"#{node.name} is already defined as #{type.type_desc}\"\n      end\n\n      node.raise \"#{node.name} is already defined\"\n    else\n      type = NonGenericClassType.new(@program, current_type, node.name, @program.struct)\n      type.struct = true\n      type.extern = true\n      type.extern_union = node.union?\n\n      attach_doc type, node, annotations\n\n      current_type.types[node.name] = type\n    end\n\n    node.resolved_type = type\n\n    type.packed = packed\n\n    false\n  end\n\n  def visit(node : TypeDef)\n    annotations = read_annotations\n    type = current_type.types[node.name]?\n\n    if type\n      node.raise \"#{node.name} is already defined\"\n    else\n      typed_def_type = lookup_type(node.type_spec)\n      typed_def_type = check_allowed_in_lib node.type_spec, typed_def_type\n      type = TypeDefType.new @program, current_type, node.name, typed_def_type\n\n      attach_doc type, node, annotations\n\n      current_type.types[node.name] = type\n    end\n\n    false\n  end\n\n  def visit(node : EnumDef)\n    check_outside_exp node, \"declare enum\"\n\n    annotations = read_annotations\n\n    scope, name, enum_type = lookup_type_def(node)\n\n    if enum_type\n      unless enum_type.is_a?(EnumType)\n        node.raise \"#{name} is not a enum, it's a #{enum_type.type_desc}\"\n      end\n    end\n\n    if base_type = node.base_type\n      enum_base_type = lookup_type(base_type)\n      unless enum_base_type.is_a?(IntegerType)\n        base_type.raise \"enum base type must be an integer type\"\n      end\n\n      if enum_type && enum_base_type != enum_type.base_type\n        base_type.raise \"enum #{name}'s base type is #{enum_type.base_type}, not #{enum_base_type}\"\n      end\n    end\n\n    existed = !!enum_type\n    enum_type ||= EnumType.new(@program, scope, name, enum_base_type || @program.int32)\n\n    enum_type.private = true if node.visibility.private?\n\n    process_annotations(annotations) do |annotation_type, ann|\n      enum_type.flags = true if annotation_type == @program.flags_annotation\n      enum_type.add_annotation(annotation_type, ann)\n    end\n\n    node.resolved_type = enum_type\n    attach_doc enum_type, node, annotations\n\n    pushing_type(enum_type) do\n      visit_enum_members(node, node.members, existed, enum_type)\n    end\n\n    unless existed\n      num_members = enum_type.types.size\n      if num_members > 0 && enum_type.flags?\n        # skip None & All, they doesn't count as members for @[Flags] enums\n        num_members = enum_type.types.count { |(name, _)| !name.in?(\"None\", \"All\") }\n      end\n\n      if num_members == 0\n        node.raise \"enum #{node.name} must have at least one member\"\n      end\n\n      if enum_type.flags?\n        unless enum_type.types.has_key?(\"None\")\n          none_member = enum_type.add_constant(\"None\", 0)\n\n          if node_location = node.location\n            none_member.add_location node_location\n          end\n\n          define_enum_none_question_method(enum_type, node)\n        end\n\n        unless enum_type.types.has_key?(\"All\")\n          all_value = enum_type.base_type.kind.cast(0).as(Int::Primitive)\n\n          enum_type.types.each_value do |member|\n            all_value |= interpret_enum_value(member.as(Const).value, enum_type.base_type)\n          end\n\n          all_member = enum_type.add_constant(\"All\", all_value)\n\n          if node_location = node.location\n            all_member.add_location node_location\n          end\n        end\n      end\n\n      scope.types[name] = enum_type\n    end\n\n    false\n  end\n\n  def visit_enum_members(node, members, existed, enum_type, previous_counter = nil)\n    members.reduce(previous_counter) do |counter, member|\n      visit_enum_member(node, member, existed, enum_type, counter)\n    end\n  end\n\n  def visit_enum_member(node, member, existed, enum_type, previous_counter = nil)\n    case member\n    when MacroIf\n      expanded = expand_inline_macro(member, mode: Parser::ParseMode::Enum, accept: false)\n      visit_enum_member(node, expanded, existed, enum_type, previous_counter)\n    when MacroExpression\n      expanded = expand_inline_macro(member, mode: Parser::ParseMode::Enum, accept: false)\n      visit_enum_member(node, expanded, existed, enum_type, previous_counter)\n    when MacroFor\n      expanded = expand_inline_macro(member, mode: Parser::ParseMode::Enum, accept: false)\n      visit_enum_member(node, expanded, existed, enum_type, previous_counter)\n    when Expressions\n      visit_enum_members(node, member.expressions, existed, enum_type, previous_counter)\n    when Arg\n      if existed\n        node.raise \"can't reopen enum and add more constants to it\"\n      end\n\n      if enum_type.types.has_key?(member.name)\n        member.raise \"enum '#{enum_type}' already contains a member named '#{member.name}'\"\n      end\n\n      if default_value = member.default_value\n        counter = interpret_enum_value(default_value, enum_type.base_type)\n      elsif previous_counter\n        if enum_type.flags?\n          if previous_counter == 0 # In case the member is set to 0\n            counter = 1\n          else\n            counter = previous_counter &* 2\n            unless (counter <=> previous_counter).sign == previous_counter.sign\n              member.raise \"value of enum member #{member} would overflow the base type #{enum_type.base_type}\"\n            end\n          end\n        else\n          counter = previous_counter &+ 1\n          unless counter > previous_counter\n            member.raise \"value of enum member #{member} would overflow the base type #{enum_type.base_type}\"\n          end\n        end\n      else\n        counter = enum_type.base_type.kind.cast(enum_type.flags? ? 1 : 0).as(Int::Primitive)\n      end\n\n      if enum_type.flags? && !@in_lib\n        if member.name == \"None\" && counter != 0\n          member.raise \"flags enum can't redefine None member to non-0\"\n        elsif member.name == \"All\"\n          member.raise \"flags enum can't redefine All member. None and All are autogenerated\"\n        end\n      end\n\n      if default_value.is_a?(Crystal::NumberLiteral)\n        enum_base_kind = enum_type.base_type.kind\n        if (enum_base_kind.i32?) && (enum_base_kind != default_value.kind)\n          default_value.raise \"enum value must be an Int32\"\n        end\n      end\n\n      define_enum_question_method(enum_type, member, enum_type.flags?)\n\n      const_member = enum_type.add_constant(member.name, counter)\n      member.default_value = const_member.value\n\n      const_member.doc = member.doc\n      check_ditto const_member, member.location\n\n      if member_location = member.location\n        const_member.add_location(member_location)\n      end\n\n      counter\n    else\n      member.accept self\n      previous_counter\n    end\n  end\n\n  def define_enum_question_method(enum_type, member, is_flags)\n    method_name = is_flags ? \"includes?\" : \"==\"\n    body = Call.new(Var.new(\"self\").at(member), method_name, Path.new(member.name).at(member)).at(member)\n    a_def = Def.new(\"#{member.name.underscore}?\", body: body).at(member)\n\n    a_def.doc = if member.doc.try &.starts_with?(\":nodoc:\")\n                  \":nodoc:\"\n                else\n                  \"Returns `true` if this enum value #{is_flags ? \"contains\" : \"equals\"} `#{member.name}`\"\n                end\n\n    enum_type.add_def a_def\n  end\n\n  def define_enum_none_question_method(enum_type, node)\n    body = Call.new(Call.new(nil, \"value\").at(node), \"==\", NumberLiteral.new(0)).at(node)\n    a_def = Def.new(\"none?\", body: body).at(node)\n    enum_type.add_def a_def\n  end\n\n  def visit(node : Expressions)\n    node.expressions.each_with_index do |child, i|\n      child.accept self\n    rescue ex : SkipMacroException\n      @program.macro_expansion_error_hook.try &.call(ex.cause) if ex.is_a? SkipMacroCodeCoverageException\n      node.expressions.delete_at(i..-1)\n      break\n    end\n    false\n  end\n\n  def visit(node : Assign)\n    type_assign(node.target, node.value, node)\n    false\n  end\n\n  def type_assign(target : Var, value, node)\n    @vars[target.name] = MetaVar.new(target.name)\n    value.accept self\n    false\n  end\n\n  def type_assign(target : Path, value, node)\n    # We are inside the assign, so we go outside it to check if we are inside an outer expression\n    @exp_nest -= 1\n    check_outside_exp node, \"declare constant\"\n    @exp_nest += 1\n\n    annotations = read_annotations\n\n    name = target.names.last\n    scope = lookup_type_def_scope(target, target)\n    type = scope.types[name]?\n    if type\n      target.raise \"already initialized constant #{type}\"\n    end\n\n    const = Const.new(@program, scope, name, value)\n    const.private = true if target.visibility.private?\n\n    process_annotations(annotations) do |annotation_type, ann|\n      # annotations on constants are inaccessible in macros so we only add deprecations\n      const.add_annotation(annotation_type, ann) if annotation_type == @program.deprecated_annotation\n    end\n\n    check_ditto node, node.location\n    attach_doc const, node, annotations\n\n    scope.types[name] = const\n\n    target.target_const = const\n  end\n\n  def type_assign(target, value, node)\n    value.accept self\n\n    # Prevent to assign instance variables inside nested expressions.\n    # `@exp_nest > 1` is to check nested expressions. We cannot use `inside_exp?` simply\n    # because `@exp_nest` is increased when `node` is `Assign`.\n    if @exp_nest > 1 && target.is_a?(InstanceVar)\n      node.raise \"can't use instance variables at the top level\"\n    end\n\n    false\n  end\n\n  def visit(node : VisibilityModifier)\n    node.exp.visibility = node.modifier\n    node.exp.accept self\n\n    # Can only apply visibility modifier to def, type, macro or a macro call\n    case exp = node.exp\n    when ClassDef, ModuleDef, EnumDef, Alias, LibDef\n      if node.modifier.private?\n        return false\n      else\n        node.raise \"can only use 'private' for types\"\n      end\n    when Assign\n      if exp.target.is_a?(Path)\n        if node.modifier.private?\n          return false\n        else\n          node.raise \"can only use 'private' for constants\"\n        end\n      end\n    when Def\n      return false\n    when Macro\n      if node.modifier.private?\n        return false\n      else\n        node.raise \"can only use 'private' for macros\"\n      end\n    when Call\n      # Don't give an error yet: wait to see if the\n      # call doesn't resolve to a method/macro\n      return false\n    end\n\n    node.raise \"can't apply visibility modifier\"\n  end\n\n  def visit(node : ProcLiteral)\n    old_vars_keys = @vars.keys\n\n    node.def.args.each do |arg|\n      @vars[arg.name] = MetaVar.new(arg.name)\n    end\n\n    node.def.body.accept self\n\n    # Now remove these vars, but only if they weren't vars before\n    node.def.args.each do |arg|\n      @vars.delete(arg.name) unless old_vars_keys.includes?(arg.name)\n    end\n\n    false\n  end\n\n  def visit(node : FunDef)\n    check_outside_exp node, \"declare fun\"\n\n    if node.body && !current_type.is_a?(Program)\n      node.raise \"can only declare fun at lib or global scope\"\n    end\n\n    annotations = read_annotations\n\n    # We'll resolve the external args types later, in TypeDeclarationVisitor\n    external_args = node.args.map do |arg|\n      Arg.new(arg.name).at(arg.location)\n    end\n\n    external = External.new(node.name, external_args, node.body, node.real_name).at(node)\n    external.name_location = node.name_location\n\n    call_convention = nil\n    process_def_annotations(external, annotations) do |annotation_type, ann|\n      if annotation_type == @program.call_convention_annotation\n        call_convention = parse_call_convention(ann, call_convention)\n      elsif annotation_type == @program.primitive_annotation\n        process_def_primitive_annotation(external, ann)\n      else\n        ann.raise \"funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention\"\n      end\n    end\n\n    node.doc ||= annotations_doc(annotations)\n    check_ditto node, node.location\n\n    # Copy call convention from lib, if any\n    scope = current_type\n    if !call_convention && scope.is_a?(LibType)\n      call_convention = scope.call_convention\n    end\n\n    if scope.is_a?(LibType)\n      external.wasm_import_module = scope.wasm_import_module\n    end\n\n    # We fill the arguments and return type in TypeDeclarationVisitor\n    external.doc = node.doc\n    external.call_convention = call_convention\n    external.varargs = node.varargs?\n    external.fun_def = node\n    external.return_type = node.return_type\n    node.external = external\n\n    current_type.add_def(external)\n\n    false\n  end\n\n  def visit(node : TypeDeclaration)\n    if (var = node.var).is_a?(Var)\n      @vars[var.name] = MetaVar.new(var.name)\n    end\n\n    # Because the value could be using macro expansions\n    node.value.try &.accept(self)\n\n    false\n  end\n\n  def visit(node : UninitializedVar)\n    if (var = node.var).is_a?(Var)\n      @vars[var.name] = MetaVar.new(var.name)\n    end\n    false\n  end\n\n  def visit(node : MultiAssign)\n    node.targets.each do |target|\n      var = target.is_a?(Splat) ? target.exp : target\n      if var.is_a?(Var)\n        @vars[var.name] = MetaVar.new(var.name)\n      end\n      target.accept self\n    end\n\n    node.values.each &.accept self\n    false\n  end\n\n  def visit(node : Rescue)\n    if name = node.name\n      @vars[name] = MetaVar.new(name)\n    end\n\n    node.body.accept self\n\n    false\n  end\n\n  def visit(node : Call)\n    node.scope = node.global? ? @program : current_type.metaclass\n    !expand_macro(node, raise_on_missing_const: false, first_pass: true)\n  end\n\n  def visit(node : ProcPointer)\n    # A proc pointer at the top-level might refer to a macro, so we check\n    # that here but we don't yet give an error: we let the real semantic visitor\n    # (MainVisitor) do that job to avoid duplicating code.\n    obj = node.obj\n\n    call = Call.new(obj, node.name).at(obj)\n    call.scope = current_type.metaclass\n    node.call = call\n\n    expand_macro(call, raise_on_missing_const: false, first_pass: true)\n\n    false\n  end\n\n  def visit(node : Out)\n    exp = node.exp\n    if exp.is_a?(Var)\n      @vars[exp.name] = MetaVar.new(exp.name)\n    end\n    true\n  end\n\n  def visit(node : Block)\n    # Remember how many local vars we had before the block\n    old_vars_size = @vars.size\n\n    # When accepting a block, declare variables for block arguments.\n    # These are needed for macro expansions to parser identifiers\n    # as variables and not calls.\n    node.args.each do |arg|\n      @vars[arg.name] = MetaVar.new(arg.name)\n    end\n\n    node.body.accept self\n\n    # After the block we should have the same number of local vars\n    # (blocks can't declare inject local vars to the outer scope)\n    while @vars.size > old_vars_size\n      @vars.delete(@vars.last_key)\n    end\n\n    false\n  end\n\n  def include_in(current_type, node, kind : HookKind)\n    node_name = node.name\n\n    type = lookup_type(node_name)\n    case type\n    when GenericModuleType\n      node.raise \"generic type arguments must be specified when including #{type}\"\n    when .module?\n      # OK\n    else\n      node_name.raise \"#{type} is not a module, it's a #{type.type_desc}\"\n    end\n\n    if node_name.is_a?(Path)\n      @program.check_deprecated_type(type, node_name)\n    end\n\n    begin\n      current_type.as(ModuleType).include type\n      run_hooks hook_type(type), current_type, kind, node\n    rescue ex : MacroRaiseException\n      # Make the inner most exception to be the include/extend node so that it's the last frame in the trace.\n      # This will make the location show on that node instead of the `raise` call.\n      ex.inner = Crystal::MacroRaiseException.for_node node, ex.message\n\n      raise ex\n    rescue ex : TypeException\n      node.raise \"at '#{kind}' hook\", ex\n    end\n  end\n\n  def has_hooks?(type_with_hooks)\n    hooks = type_with_hooks.as?(ModuleType).try &.hooks\n    !hooks.nil? && !hooks.empty?\n  end\n\n  def run_hooks(type_with_hooks, current_type, kind : HookKind, node, call = nil)\n    type_with_hooks.as?(ModuleType).try &.hooks.try &.each do |hook|\n      next if hook.kind != kind\n\n      expansion = expand_macro(hook.macro, node, visibility: :public) do\n        if call\n          @program.expand_macro hook.macro, call, current_type.instance_type\n        else\n          @program.expand_macro hook.macro.body, current_type.instance_type\n        end\n      end\n\n      node.add_hook_expansion(expansion)\n    end\n\n    if kind.inherited?\n      # In the case of:\n      #\n      #    class A(X); end\n      #    class B < A(Int32);end\n      #\n      # we need to go from A(Int32) to A(X) to go up the hierarchy.\n      if type_with_hooks.is_a?(GenericClassInstanceMetaclassType)\n        run_hooks(type_with_hooks.instance_type.generic_type.metaclass, current_type, kind, node)\n      elsif (superclass = type_with_hooks.instance_type.superclass)\n        run_hooks(superclass.metaclass, current_type, kind, node)\n      end\n    end\n  end\n\n  private def hook_type(type)\n    type = type.generic_type if type.is_a?(GenericInstanceType)\n    type.metaclass\n  end\n\n  def parse_call_convention(ann, call_convention)\n    if call_convention\n      ann.raise \"call convention already specified\"\n    end\n\n    if ann.args.size != 1\n      ann.wrong_number_of_arguments \"annotation CallConvention\", ann.args.size, 1\n    end\n\n    call_convention_node = ann.args.first\n    unless call_convention_node.is_a?(StringLiteral)\n      call_convention_node.raise \"argument to CallConvention must be a string\"\n    end\n\n    value = call_convention_node.value\n    call_convention = LLVM::CallConvention.parse?(value)\n    unless call_convention\n      call_convention_node.raise \"invalid call convention. Valid values are #{LLVM::CallConvention.values.join \", \"}\"\n    end\n    call_convention\n  end\n\n  def attach_doc(type, node, annotations)\n    if @program.wants_doc?\n      type.doc ||= node.doc\n      type.doc ||= annotations_doc(annotations) if annotations\n    end\n\n    if node_location = node.location\n      type.add_location(node_location)\n    end\n  end\n\n  def check_ditto(node : Def | Assign | FunDef | Const | Macro, location : Location?) : Nil\n    return if !@program.wants_doc?\n\n    if stripped_doc = node.doc.try &.strip\n      if stripped_doc == \":ditto:\"\n        node.doc = @last_doc\n        return\n      elsif appendix = stripped_doc.lchop?(\":ditto:\\n\")\n        node.doc = \"#{@last_doc}\\n\\n#{appendix.lchop('\\n')}\"\n        return\n      end\n    end\n\n    @last_doc = node.doc\n  end\n\n  def annotations_doc(annotations)\n    annotations.try(&.first?).try &.doc\n  end\n\n  def process_def_annotations(node, annotations, &)\n    process_annotations(annotations) do |annotation_type, ann|\n      case annotation_type\n      when @program.no_inline_annotation\n        node.no_inline = true\n      when @program.always_inline_annotation\n        node.always_inline = true\n      when @program.naked_annotation\n        node.naked = true\n      when @program.returns_twice_annotation\n        node.returns_twice = true\n      when @program.raises_annotation\n        node.raises = true\n      else\n        yield annotation_type, ann\n      end\n    end\n  end\n\n  def lookup_type_def(node : ASTNode)\n    path = node.name\n    scope = lookup_type_def_scope(node, path)\n    name = path.names.last\n    type = scope.types[name]?\n    if type && node.doc\n      type.doc = node.doc\n    end\n    {scope, name, type}\n  end\n\n  def lookup_type_def_scope(node : ASTNode, path : Path)\n    scope =\n      if path.names.size == 1\n        if path.global?\n          if node.visibility.private?\n            path.raise \"can't declare private type in the global namespace; drop the `private` for the top-level namespace, or drop the leading `::` for the file-private namespace\"\n          end\n          program\n        else\n          if current_type.is_a?(Program)\n            file_module = program.check_private(node)\n          end\n          file_module || current_type\n        end\n      else\n        prefix = path.clone\n        prefix.names.pop\n        lookup_type_def_name_creating_modules prefix\n      end\n\n    check_type_is_type_container(scope, path)\n  end\n\n  def check_type_is_type_container(scope, path)\n    if scope.is_a?(EnumType) || !scope.is_a?(ModuleType)\n      path.raise \"can't declare type inside #{scope.type_desc} #{scope}\"\n    end\n\n    scope\n  end\n\n  def lookup_type_def_name_creating_modules(path : Path)\n    base_type = path.global? ? program : current_type\n    target_type = base_type.lookup_path(path, lookup_in_namespace: false).as?(Type).try &.remove_alias_if_simple\n\n    unless target_type\n      next_type = base_type\n      path.names.each do |name|\n        next_type = base_type.lookup_path_item(name, lookup_self: false, lookup_in_namespace: false, include_private: true, location: path.location)\n        if next_type\n          if next_type.is_a?(ASTNode)\n            path.raise \"expected #{name} to be a type\"\n          end\n        else\n          base_type = check_type_is_type_container(base_type, path)\n          next_type = NonGenericModuleType.new(@program, base_type.as(ModuleType), name)\n          if (location = path.location)\n            next_type.add_location(location)\n          end\n          base_type.types[name] = next_type\n        end\n        base_type = next_type\n      end\n      target_type = next_type\n    end\n\n    unless target_type.is_a?(NamedType)\n      path.raise \"#{target_type} can't be used as a namespace\"\n    end\n\n    target_type\n  end\n\n  # Turns all finished macros into expanded nodes, and\n  # adds them to the program\n  def process_finished_hooks\n    @finished_hooks.each do |hook|\n      self.current_type = hook.scope\n      expansion = expand_macro(hook.macro, hook.macro, visibility: :public) do\n        @program.expand_macro hook.macro.body, hook.scope\n      end\n      program.add_finished_hook(hook.scope, hook.macro, expansion)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/transformer.cr",
    "content": "require \"../syntax/transformer\"\n\nmodule Crystal\n  class Transformer\n    def transform(node : MetaVar | MetaMacroVar | Primitive | TypeFilteredNode | TupleIndexer | TypeNode | AssignWithRestriction | YieldBlockBinder | MacroId | Unreachable)\n      node\n    end\n\n    def transform(node : FileNode)\n      node.node = node.node.transform self\n      node\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_declaration_processor.cr",
    "content": "# Here we mix the result of explicit type declarations for\n# global, class and instance variables with the implicit types\n# guessed from assignments to them.\n#\n# If a type is explicit, the guess is not used (it might later\n# give a compile error on incompatible types, on MainVisitor).\n#\n# If the type of an instance variable is implicit a bit more\n# logic is involved since we need to check if a variable is\n# assigned in all of the initialize methods, or if the initializers\n# are defined in a superclass, if super is called, etc.\nstruct Crystal::TypeDeclarationProcessor\n  record TypeDeclarationWithLocation,\n    type : Type,\n    location : Location,\n    uninitialized : Bool,\n    annotations : Array({AnnotationType, Annotation})?\n\n  # This captures an initialize info: it's related Def,\n  # and which instance variables are assigned. Useful\n  # to determine nilable instance variables.\n  class InitializeInfo\n    getter :def\n    property instance_vars : Array(String)?\n\n    def initialize(@def : Def)\n    end\n  end\n\n  # Captures an error related to a type that can't be used\n  # for an instance/class/global variable. The node is used\n  # to give the error at its location.\n  record Error,\n    node : ASTNode,\n    type : Type\n\n  # The types we guess for instance types can be nodes or types.\n  # In the case of a generic type we might have:\n  #\n  # ```\n  # class Foo(T)\n  #   def foo\n  #     @x = T.new\n  #   end\n  #\n  #   def foo\n  #     @x = 1\n  #   end\n  # end\n  # ```\n  #\n  # In the above case we know @x is of type T (but we don't know what\n  # T is when instantiated) and Int32 (we know that type). So we\n  # keep a list of all types and nodes, and we eventually resolve them\n  # all when a generic type is instantiated.\n  class InstanceVarTypeInfo\n    property type : Type\n    property outside_def\n    property annotations : Array({AnnotationType, Annotation})?\n    getter location\n\n    def initialize(@location : Location, @type : Type)\n      @outside_def = false\n    end\n\n    def add_annotations(anns : Array({AnnotationType, Annotation})?)\n      annotations = @annotations ||= [] of {AnnotationType, Annotation}\n      annotations.concat(anns)\n    end\n  end\n\n  record NilableInstanceVar,\n    info : InitializeInfo,\n    name : String\n\n  private getter type_decl_visitor\n  private getter type_guess_visitor\n\n  def initialize(@program : Program)\n    # The type of instance variables. The last one wins.\n    #\n    # In the case of instance variables we can't always match a type\n    # because they might be declared in generic types, for example:\n    #\n    # ```\n    # class Foo(T)\n    #   @x : T\n    # end\n    # ```\n    #\n    # In that case we remember that @x has a T node associated with it,\n    # and only resolve it when instantiating the generic type.\n    @explicit_instance_vars = {} of Type => Hash(String, TypeDeclarationWithLocation)\n\n    # The types we guess for instance vars, when not explicit\n    @guessed_instance_vars = {} of Type => Hash(String, InstanceVarTypeInfo)\n\n    # Info related to a type's initialize methods\n    @initialize_infos = {} of Type => Array(InitializeInfo)\n\n    # Instance variables that are initialized outside a method (at the class level)\n    @instance_vars_outside = {} of Type => Array(String)\n\n    # Instance vars that are determined to be non-nilable because\n    # they are initialized in at least one of the initialize methods.\n    @non_nilable_instance_vars = {} of Type => Array(String)\n\n    # Nilable variables there were detected to not be initialized in an initialize,\n    # but a superclass does initialize it. It's only an error if the explicit/guessed\n    # type is not nilable itself.\n    @nilable_instance_vars = {} of Type => Hash(String, InitializeInfo)\n\n    # Errors related to types like Class, Int and Reference used for\n    # instance variables. These are gathered by the guesser, and later\n    # removed if an explicit type is found (in remove_error).\n    @errors = {} of Type => Hash(String, Error)\n\n    # Types whose initialize methods are all macro defs\n    @has_macro_def = Set(Type).new\n\n    # Types that are not extended by any other types, used to speed up detection\n    # of instance vars in extended modules\n    @has_no_extenders = Set(Type).new\n\n    @type_decl_visitor = TypeDeclarationVisitor.new(@program, @explicit_instance_vars)\n\n    @type_guess_visitor = TypeGuessVisitor.new(@program, @explicit_instance_vars,\n      @guessed_instance_vars, @initialize_infos, @instance_vars_outside, @errors)\n  end\n\n  def process(node)\n    # First check type declarations\n    @program.visit_with_finished_hooks(node, type_decl_visitor)\n\n    # Use the last type found for class variables to declare them\n    type_decl_visitor.class_vars.each do |owner, vars|\n      vars.each do |name, type|\n        declare_meta_type_var(owner.class_vars, owner, name, type)\n      end\n    end\n\n    # Then use several syntactic rules to infer the types of\n    # variables that don't have an explicit type set\n    @program.visit_with_finished_hooks(node, type_guess_visitor)\n\n    # Process class variables\n    type_guess_visitor.class_vars.each do |owner, vars|\n      vars.each do |name, info|\n        # No need to freeze its type because it is frozen by check_class_var_errors\n        declare_meta_type_var(owner.class_vars, owner, name, info, freeze_type: false)\n      end\n    end\n\n    compute_non_nilable_instance_vars\n\n    process_instance_vars_declarations\n\n    remove_duplicate_instance_vars_declarations\n\n    # Check that instance vars that weren't initialized in an initialize,\n    # but a superclass does initialize then, are nilable, and if not\n    # give an error\n    check_nilable_instance_vars\n\n    check_cant_use_type_errors\n\n    check_class_var_errors(type_decl_visitor.class_vars, type_guess_visitor.class_vars)\n\n    {node, self}\n  end\n\n  private def declare_meta_type_var(vars, owner, name, type : Type, location : Location? = nil, instance_var = false, freeze_type = true, annotations = nil)\n    if instance_var && location && !owner.allows_instance_vars?\n      raise_cant_declare_instance_var(owner, location)\n    end\n\n    remove_error owner, name\n\n    if owner.extern? && !type.allowed_in_lib?\n      raise TypeException.new(\"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations, not #{type}\", location.not_nil!)\n    end\n\n    if owner.is_a?(NonGenericModuleType) || owner.is_a?(NonGenericClassType)\n      type = type.replace_type_parameters(owner)\n    end\n\n    var = MetaTypeVar.new(name)\n    var.owner = owner\n    var.type = type\n    var.bind_to(var)\n    var.freeze_type = type if freeze_type\n    var.location = location\n\n    annotations.try &.each do |annotation_type, ann|\n      var.add_annotation(annotation_type, ann)\n    end\n\n    vars[name] = var\n\n    var\n  end\n\n  private def declare_meta_type_var(vars, owner, name, info : TypeGuessVisitor::TypeInfo, freeze_type = true)\n    type = info.type\n    type = Type.merge!(type, @program.nil) unless info.outside_def\n    declare_meta_type_var(vars, owner, name, type, freeze_type: freeze_type)\n  end\n\n  private def declare_meta_type_var(vars, owner, name, info : TypeDeclarationWithLocation, instance_var = false, check_nilable = true, freeze_type = true)\n    if instance_var && !owner.allows_instance_vars?\n      raise_cant_declare_instance_var(owner, info.location)\n    end\n\n    var = declare_meta_type_var(vars, owner, name, info.type.as(Type), info.location, freeze_type: freeze_type, annotations: info.annotations)\n    var.location = info.location\n\n    # Check if var is uninitialized\n    var.uninitialized = true if info.uninitialized\n\n    # If the variable is guessed to be nilable because it is not initialized\n    # in all of the initialize methods, and the explicit type is not nilable,\n    # give an error right now\n    if check_nilable && instance_var && !var.type.includes_type?(@program.nil)\n      if nilable_instance_var?(owner, name)\n        raise_not_initialized_in_all_initialize(var, name, owner)\n      end\n    end\n\n    var\n  end\n\n  private def raise_cant_declare_instance_var(owner, location)\n    raise TypeException.new(\"can't declare instance variables in #{owner}\", location)\n  end\n\n  private def process_instance_vars_declarations\n    owners = @explicit_instance_vars.keys + @guessed_instance_vars.keys\n    owners.uniq!\n\n    # We traverse types from the top of the hierarchy, processing\n    # explicit vars first and then guessed ones. We must do this at the\n    # same type to avoid declaring an explicit type in a subclass and\n    # then find out the variable belongs to a superclass.\n    owners = sort_types_by_depth(owners)\n    owners.each do |owner|\n      vars = @explicit_instance_vars[owner]?\n      vars.try &.each do |name, type_decl|\n        process_owner_instance_var_declaration(owner, name, type_decl)\n      end\n\n      vars = @guessed_instance_vars[owner]?\n      vars.try &.each do |name, type_decl|\n        process_owner_guessed_instance_var_declaration(owner, name, type_decl)\n      end\n    end\n  end\n\n  private def process_owner_instance_var_declaration(owner, name, type_decl)\n    # Generic instances already have their instance vars\n    # set from uninstantiated generic types\n    return if owner.is_a?(GenericInstanceType)\n\n    if owner.is_a?(NonGenericModuleType) || owner.is_a?(GenericModuleType)\n      if extender = find_extending_type(owner)\n        raise TypeException.new(\"can't declare instance variables in #{owner} because #{extender} extends it\", type_decl.location)\n      end\n    elsif owner.metaclass?\n      raise TypeException.new(\"can't declare instance variables in #{owner}\", type_decl.location)\n    end\n\n    # Check if a superclass already defined this variable\n    supervar = owner.lookup_instance_var?(name)\n\n    if supervar && supervar.owner != owner\n      # Redeclaring a variable with the same type is OK\n      unless supervar.type.same?(type_decl.type)\n        raise TypeException.new(\"instance variable '#{name}' of #{supervar.owner}, with #{owner} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare it in #{owner} as #{type_decl.type})\", type_decl.location)\n      end\n\n      # Reject annotations to existing instance var\n      type_decl.annotations.try &.each do |_, ann|\n        ann.raise \"can't annotate #{name} in #{owner} because it was first defined in #{supervar.owner}\"\n      end\n    else\n      declare_meta_type_var(owner.instance_vars, owner, name, type_decl, instance_var: true, check_nilable: !owner.module?)\n      remove_error owner, name\n\n      if owner.is_a?(GenericType)\n        owner.each_instantiated_type do |instance|\n          new_type = type_decl.type.replace_type_parameters(instance)\n          new_type_decl = TypeDeclarationWithLocation.new(new_type, type_decl.location, type_decl.uninitialized, type_decl.annotations)\n          declare_meta_type_var(instance.instance_vars, instance, name, new_type_decl, instance_var: true, check_nilable: false)\n        end\n      end\n\n      if owner.is_a?(NonGenericModuleType)\n        # Transfer this declaration to including types, recursively\n        owner.raw_including_types.try &.each do |including_type|\n          process_owner_instance_var_declaration(including_type, name, type_decl)\n        end\n      end\n\n      if owner.is_a?(GenericModuleType)\n        # Transfer this declaration to including types, recursively\n        owner.raw_including_types.try &.each do |including_type|\n          process_owner_instance_var_declaration(including_type, name, type_decl)\n        end\n      end\n    end\n  end\n\n  private def find_extending_type(mod)\n    return nil if @has_no_extenders.includes?(mod)\n\n    mod.raw_including_types.try &.each do |includer|\n      case includer\n      when .metaclass?\n        return includer.instance_type\n      when NonGenericModuleType\n        type = find_extending_type(includer)\n        return type if type\n      when GenericModuleInstanceType\n        type = find_extending_type(includer.generic_type.as(GenericModuleType))\n        return type if type\n      end\n    end\n\n    @has_no_extenders << mod\n    nil\n  end\n\n  private def check_non_nilable_for_generic_module(owner, name, type_decl)\n    case owner\n    when GenericModuleType\n      remove_error owner, name\n      owner.inherited.try &.each do |inherited|\n        check_non_nilable_for_generic_module(inherited, name, type_decl)\n      end\n    when NonGenericModuleType\n      remove_error owner, name\n      owner.raw_including_types.try &.each do |inherited|\n        check_non_nilable_for_generic_module(inherited, name, type_decl)\n      end\n    when NonGenericClassType\n      var = owner.lookup_instance_var(name).instance_var\n      if !var.type.includes_type?(@program.nil)\n        if nilable_instance_var?(owner, name)\n          raise_not_initialized_in_all_initialize(var, name, owner)\n        end\n      end\n    when GenericClassType\n      if nilable_instance_var?(owner, name)\n        if !has_syntax_nil?(type_decl.type)\n          raise_not_initialized_in_all_initialize(type_decl.location, name, owner)\n        end\n      end\n    end\n  end\n\n  private def has_syntax_nil?(node)\n    case node\n    when Union\n      node.types.any? { |type| has_syntax_nil?(type) }\n    when Path\n      # TODO: we should actually check that this resolves to the top-level Nil type\n      node.names.size == 1 && node.names.first == \"Nil\"\n    else\n      false\n    end\n  end\n\n  private def process_owner_guessed_instance_var_declaration(owner, name, type_info)\n    # Generic instances already have their instance vars\n    # set from uninstantiated generic types\n    return if owner.is_a?(GenericInstanceType)\n\n    if owner.is_a?(NonGenericModuleType) || owner.is_a?(GenericModuleType)\n      if extender = find_extending_type(owner)\n        raise TypeException.new(\"can't declare instance variables in #{owner} because #{extender} extends it\", type_info.location)\n      end\n    elsif owner.metaclass?\n      raise TypeException.new(\"can't declare instance variables in #{owner}\", type_info.location)\n    end\n\n    # If a superclass already defines this variable we ignore\n    # the guessed type information for subclasses\n    supervar = owner.lookup_instance_var?(name)\n    return if supervar\n\n    case owner\n    when NonGenericClassType\n      type = type_info.type\n      if nilable_instance_var?(owner, name)\n        type = Type.merge!(type, @program.nil)\n      end\n\n      # If the only type that we were able to infer was nil, it's the same\n      # as not being able to infer anything (having a variable of just\n      # Nil is not useful and is the same as not having it at all, at\n      # least for non-generic types)\n      if type.nil_type?\n        raise_nil_instance_var owner, name, type_info.location\n      end\n\n      declare_meta_type_var(owner.instance_vars, owner, name, type, type_info.location, instance_var: true, annotations: type_info.annotations)\n    when NonGenericModuleType\n      type = type_info.type\n      if nilable_instance_var?(owner, name)\n        type = Type.merge!(type, @program.nil)\n      end\n\n      # Same as above, only Nil makes no sense\n      if type.nil_type?\n        return\n      end\n\n      declare_meta_type_var(owner.instance_vars, owner, name, type, type_info.location, instance_var: true, annotations: type_info.annotations)\n      remove_error owner, name\n      owner.raw_including_types.try &.each do |including_type|\n        process_owner_guessed_instance_var_declaration(including_type, name, type_info)\n        remove_error including_type, name\n      end\n    when GenericClassType\n      type = type_info.type\n      if nilable_instance_var?(owner, name)\n        type = Type.merge!(type, @program.nil)\n      end\n\n      # Same as above, only Nil makes no sense\n      if type.nil_type?\n        raise_nil_instance_var owner, name, type_info.location\n      end\n\n      declare_meta_type_var(owner.instance_vars, owner, name, type, type_info.location, instance_var: true, annotations: type_info.annotations)\n\n      owner.each_instantiated_type do |instance|\n        new_type = type.replace_type_parameters(instance)\n        declare_meta_type_var(instance.instance_vars, instance, name, new_type, type_info.location, instance_var: true, annotations: type_info.annotations)\n      end\n\n      remove_error owner, name\n    when GenericModuleType\n      type = type_info.type\n      if nilable_instance_var?(owner, name)\n        type = Type.merge!(type, @program.nil)\n      end\n\n      declare_meta_type_var(owner.instance_vars, owner, name, type, type_info.location, instance_var: true, annotations: type_info.annotations)\n      remove_error owner, name\n      owner.raw_including_types.try &.each do |including_type|\n        process_owner_guessed_instance_var_declaration(including_type, name, type_info)\n        remove_error including_type, name\n      end\n    else\n      # TODO: can this be reached?\n    end\n  end\n\n  private def nilable_instance_var?(owner, name)\n    return false if @has_macro_def.includes?(owner)\n\n    non_nilable_vars = @non_nilable_instance_vars[owner]?\n    !non_nilable_vars || (non_nilable_vars && !non_nilable_vars.includes?(name))\n  end\n\n  private def raise_nil_instance_var(owner, name, location)\n    raise TypeException.new(\"instance variable #{name} of #{owner} was inferred to be Nil, but Nil alone provides no information\", location)\n  end\n\n  private def compute_non_nilable_instance_vars\n    owners = sort_types_by_depth(@initialize_infos.keys).uniq!\n\n    owners.each do |owner|\n      # Compute nilable variables because of initialize\n      infos = find_initialize_infos(owner)\n\n      if infos\n        @has_macro_def << owner if infos.all?(&.def.macro_def?)\n        non_nilable = compute_non_nilable_instance_vars_multi(owner, infos)\n      end\n\n      # We must also take into account those variables that are\n      # initialized outside of an initializer\n      non_nilable_outside = compute_non_nilable_outside(owner)\n\n      # And add them all into one array\n      non_nilable = merge_non_nilable_vars(non_nilable, non_nilable_outside)\n\n      if non_nilable\n        @non_nilable_instance_vars[owner] = non_nilable\n      end\n    end\n  end\n\n  private def merge_non_nilable_vars(v1, v2)\n    if v1\n      if v2\n        v1 = (v1 + v2).uniq!\n      end\n    elsif v2\n      v1 = v2\n    end\n    v1\n  end\n\n  private def compute_non_nilable_instance_vars_multi(owner, infos)\n    # Get ancestor's non-nilable variables\n    if ancestor = owner.ancestors.first?\n      ancestor = uninstantiate(ancestor)\n      ancestor_non_nilable = @non_nilable_instance_vars[ancestor]?\n    end\n\n    # If the ancestor has non-nilable instance vars, check that all initialize either call\n    # super or assign all of those variables\n    if ancestor_non_nilable\n      infos.each do |info|\n        unless info.def.calls_super? || info.def.calls_previous_def? || info.def.calls_initialize? || info.def.macro_def?\n          ancestor_non_nilable.each do |name|\n            # If the variable is initialized outside, it's OK\n            next if initialized_outside?(owner, name)\n\n            unless info.try &.instance_vars.try &.includes?(name)\n              # Remember that this variable wasn't initialized here, and later error\n              # if it turns out to be non-nilable\n              nilable_vars = @nilable_instance_vars[owner] ||= {} of String => InitializeInfo\n              nilable_vars[name] = info\n            end\n          end\n        end\n      end\n    end\n\n    # Get all instance vars assigned in all the initialize methods\n    all_instance_vars = [] of String\n    infos.each do |info|\n      info.instance_vars.try { |ivars| all_instance_vars.concat(ivars) }\n    end\n    all_instance_vars.uniq!\n\n    # Then check which ones are assigned in all of them\n    non_nilable = [] of String\n    all_instance_vars.each do |instance_var|\n      infos.each do |info|\n        # If an initialize calls another initialize, consider it like it initializes\n        # all instance vars, because the other initialize will have to do that\n        next if info.def.calls_initialize?\n\n        # Assume a macro def initializes all of them\n        # (will be checked later)\n        next if info.def.macro_def?\n\n        # Similarly, calling previous_def would have the vars initialized\n        # in the other def\n        next if info.def.calls_previous_def?\n\n        # It's non-nilable if it's initialized outside\n        next if initialized_outside?(owner, instance_var)\n\n        # If an initialize with an ivar calls super and an ancestor has already\n        # typed the instance var as non-nilable\n        next if info.def.calls_super? && ancestor_non_nilable.try(&.includes?(instance_var))\n\n        unless info.try(&.instance_vars.try(&.includes?(instance_var)))\n          # Remember that this variable wasn't initialized here, and later error\n          # if it turns out to be non-nilable\n          nilable_vars = @nilable_instance_vars[owner] ||= {} of String => InitializeInfo\n          nilable_vars[instance_var] = info\n          break\n        end\n      end\n      non_nilable << instance_var\n    end\n\n    merge_non_nilable_vars(non_nilable, ancestor_non_nilable)\n  end\n\n  private def initialized_outside?(owner, name)\n    if @instance_vars_outside[owner]?.try &.includes?(name)\n      return true\n    end\n\n    owner.ancestors.any? do |ancestor|\n      ancestor = uninstantiate(ancestor)\n      @instance_vars_outside[ancestor]?.try &.includes?(name)\n    end\n  end\n\n  private def compute_non_nilable_outside(owner)\n    non_nilable_outside = nil\n    non_nilable_outside = compute_non_nilable_outside_single(owner, non_nilable_outside)\n    owner.ancestors.each do |ancestor|\n      ancestor = uninstantiate(ancestor)\n      non_nilable_outside = compute_non_nilable_outside_single(ancestor, non_nilable_outside)\n    end\n    non_nilable_outside\n  end\n\n  private def compute_non_nilable_outside_single(owner, non_nilable_outside)\n    if vars = @instance_vars_outside[owner]?\n      non_nilable_outside ||= [] of String\n      vars.each do |name|\n        non_nilable_outside << name unless non_nilable_outside.includes?(name)\n      end\n    end\n    non_nilable_outside\n  end\n\n  private def find_initialize_infos(owner)\n    # Find the first type in the ancestor chain, including self,\n    # that defines an initialize method\n    infos = @initialize_infos[owner]?\n    return infos if infos && !infos.empty?\n\n    owner.ancestors.each do |ancestor|\n      ancestor = uninstantiate(ancestor)\n      infos = @initialize_infos[ancestor]?\n      return infos if infos && !infos.empty?\n    end\n\n    nil\n  end\n\n  private def remove_duplicate_instance_vars_declarations\n    remove_duplicate_instance_vars_declarations(@program)\n  end\n\n  private def remove_duplicate_instance_vars_declarations(type : Type)\n    # If a class has an instance variable that already exists in a superclass, remove it.\n    # Ideally we should process instance variables in a top-down fashion, but it's tricky\n    # with modules and multiple-inheritance. Removing duplicates at the end is maybe\n    # a bit more expensive, but it's simpler.\n    if type.is_a?(InstanceVarContainer) && type.class? && !type.instance_vars.empty?\n      type.instance_vars.reject! do |name, ivar|\n        supervar = type.superclass.try &.lookup_instance_var?(name)\n        if supervar && supervar.type != ivar.type\n          message = \"instance variable '#{name}' of #{supervar.owner}, with #{type} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare it in #{type} as #{ivar.type})\"\n          location = ivar.location || type.locations.try &.first\n          if location\n            raise TypeException.new(message)\n          else\n            raise TypeException.new(message)\n          end\n        end\n        supervar\n      end\n    end\n\n    type.types?.try &.each_value do |nested_type|\n      remove_duplicate_instance_vars_declarations(nested_type)\n    end\n  end\n\n  private def check_nilable_instance_vars\n    @nilable_instance_vars.each do |owner, vars|\n      vars.each do |name, info|\n        ivar = owner.lookup_instance_var?(name)\n        if ivar\n          if ivar.type.includes_type?(@program.nil)\n            # If the variable is nilable because it was not initialized\n            # in all of the initialize methods, and it's not explicitly nil,\n            # give an error and ask to be explicit.\n            if nilable_instance_var?(owner, name)\n              raise_doesnt_explicitly_initializes(info, name, ivar)\n            end\n          elsif owner == ivar.owner\n            raise_doesnt_explicitly_initializes(info, name, ivar)\n          else\n            info.def.raise \"this 'initialize' doesn't initialize instance variable '#{name}' of #{ivar.owner}, with #{owner} < #{ivar.owner}, rendering it nilable\"\n          end\n        else\n          info.def.raise \"this 'initialize' doesn't initialize instance variable '#{name}', rendering it nilable\"\n        end\n      end\n    end\n  end\n\n  def check_non_nilable_class_vars_without_initializers\n    type_decl_visitor.class_vars.each do |owner, vars|\n      vars.each_key do |name|\n        check_non_nilable_class_var_without_initializers(owner, name)\n      end\n    end\n\n    type_guess_visitor.class_vars.each do |owner, vars|\n      vars.each_key do |name|\n        check_non_nilable_class_var_without_initializers(owner, name)\n      end\n    end\n  end\n\n  private def check_non_nilable_class_var_without_initializers(owner, name)\n    class_var = owner.class_vars[name]?\n    return unless class_var\n\n    return if class_var.uninitialized?\n\n    var_type = class_var.type?\n    return unless var_type\n\n    if !class_var.initializer && !var_type.includes_type?(@program.nil_type)\n      class_var.raise \"class variable '#{name}' of #{owner} is not nilable (it's #{var_type}) so it must have an initializer\"\n    end\n  end\n\n  private def remove_error(type, name)\n    @errors[type]?.try &.delete(name)\n  end\n\n  private def check_cant_use_type_errors\n    @errors.each do |type, entries|\n      entries.each do |name, error|\n        case name\n        when .starts_with?(\"$\")\n          error.node.raise \"can't use #{error.type} as the type of global variable '#{name}', use a more specific type\"\n        when .starts_with?(\"@@\")\n          error.node.raise \"can't use #{error.type} as the type of class variable '#{name}' of #{type}, use a more specific type\"\n        when .starts_with?(\"@\")\n          error.node.raise \"can't use #{error.type} as the type of instance variable '#{name}' of #{type}, use a more specific type\"\n        else\n          # TODO: can this be reached?\n        end\n      end\n    end\n  end\n\n  private def check_class_var_errors(type_decl_class_vars, guesser_class_vars)\n    {type_decl_class_vars, guesser_class_vars}.each do |all_vars|\n      all_vars.each do |owner, vars|\n        vars.each do |name, info|\n          owner_class_var = owner.lookup_class_var?(name)\n          next unless owner_class_var\n\n          owner.ancestors.each do |ancestor|\n            next unless ancestor.is_a?(ClassVarContainer)\n\n            ancestor_class_var = ancestor.lookup_class_var?(name)\n            next unless ancestor_class_var\n\n            if owner_class_var.type.implements?(ancestor_class_var.type)\n              owner_class_var.type = ancestor_class_var.type\n            end\n\n            if owner_class_var.type != ancestor_class_var.type\n              raise TypeException.new(\"class variable '#{name}' of #{owner} is already defined as #{ancestor_class_var.type} in #{ancestor}\", info.location)\n            end\n          end\n\n          owner_class_var.freeze_type = owner_class_var.type\n        end\n      end\n    end\n  end\n\n  private def raise_not_initialized_in_all_initialize(node : ASTNode, name, owner)\n    node.raise \"instance variable '#{name}' of #{owner} was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\"\n  end\n\n  private def raise_not_initialized_in_all_initialize(location : Location, name, owner)\n    raise TypeException.new \"instance variable '#{name}' of #{owner} was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.\", location\n  end\n\n  private def raise_doesnt_explicitly_initializes(info, name, ivar)\n    info.def.raise <<-MSG\n      this 'initialize' doesn't explicitly initialize instance variable '#{name}' of #{ivar.owner}, rendering it nilable\n\n      The instance variable '#{name}' is initialized in other 'initialize' methods,\n      and by not initializing it here it's not clear if the variable is supposed\n      to be nilable or if this is a mistake.\n\n      To fix this error, either assign nil to it here:\n\n        #{name} = nil\n\n      Or declare it as nilable outside at the type level:\n\n        #{name} : (#{ivar.type})?\n      MSG\n  end\n\n  private def sort_types_by_depth(types)\n    # We sort types. We put modules first, because if these declare types\n    # of instance variables we want them declared in including types.\n    # Then we sort other types by depth, so we declare types first in\n    # superclass and then in subclasses. Finally, two modules or classes\n    # with the same depths are sorted by name.\n    types.sort_by! do |t|\n      {t.module? ? 0 : 1, t.depth, t.to_s}\n    end\n  end\n\n  private def uninstantiate(type) : Type\n    if type.is_a?(GenericInstanceType)\n      type.generic_type.as(Type)\n    else\n      type\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_declaration_visitor.cr",
    "content": "require \"./semantic_visitor\"\nrequire \"./type_guess_visitor\"\n\n# In this pass we check type declarations like:\n#\n#     @x : Int32\n#     @@x : Int32\n#     $x : Int32\n#\n# In this way we declare their type before the \"main\" code.\n#\n# This allows to put \"main\" code before these declarations,\n# so order matters less in the end.\n#\n# This visitor also processes the following:\n#\n# - C struct/union fields\n# - fun declarations\n# - alias declarations\n#\n# We do this now because in a previous pass, TopLevelVisitor\n# declared all types so now we can search them and always find\n# them, not needing any kind of forward referencing.\nclass Crystal::TypeDeclarationVisitor < Crystal::SemanticVisitor\n  alias TypeDeclarationWithLocation = TypeDeclarationProcessor::TypeDeclarationWithLocation\n\n  getter class_vars\n  getter instance_vars\n\n  def initialize(mod,\n                 @instance_vars : Hash(Type, Hash(String, TypeDeclarationWithLocation)))\n    super(mod)\n\n    # The type of class variables. The last one wins.\n    # This is type => variables.\n    @class_vars = {} of ClassVarContainer => Hash(String, TypeDeclarationWithLocation)\n\n    # A hash of all defined funs, so we can detect when\n    # a fun is redefined with a different signature\n    @externals = {} of String => External\n  end\n\n  def visit(node : Alias)\n    node.resolved_type.process_value\n    false\n  end\n\n  def visit(node : Include)\n    if @in_c_struct_or_union\n      include_c_struct(node)\n    else\n      super\n    end\n    false\n  end\n\n  def include_c_struct(node)\n    type = current_type.as(NonGenericClassType)\n\n    included_type = lookup_type(node.name)\n    unless included_type.is_a?(NonGenericClassType) && included_type.extern? && !included_type.extern_union?\n      node.name.raise \"can only include C struct, not #{included_type.type_desc}\"\n    end\n\n    included_type.instance_vars.each_value do |var|\n      field_name = var.name[1..-1]\n      if type.lookup_instance_var?(var.name)\n        node.raise \"struct #{included_type} has a field named '#{field_name}', which #{type} already defines\"\n      end\n      declare_c_struct_or_union_field(type, field_name, var, var.location || node.location)\n    end\n  end\n\n  def visit(node : LibDef)\n    pushing_type(node.resolved_type) do\n      @in_lib = true\n      @annotations = nil\n      node.body.accept self\n      @in_lib = false\n    end\n\n    false\n  end\n\n  def visit(node : CStructOrUnionDef)\n    pushing_type(node.resolved_type) do\n      @in_c_struct_or_union = true\n      node.body.accept self\n      @in_c_struct_or_union = false\n    end\n\n    false\n  end\n\n  def visit(node : ExternalVar)\n    thread_local = check_class_var_annotations\n\n    var_type = lookup_type(node.type_spec)\n    var_type = check_allowed_in_lib node.type_spec, var_type\n\n    type = current_type.as(LibType)\n\n    setter = External.new(\n      \"#{node.name}=\", [Arg.new(\"value\", type: var_type)],\n      Primitive.new(\"external_var_set\", var_type), node.real_name || node.name\n    ).at(node.location)\n    setter.set_type(var_type)\n    setter.external_var = true\n    setter.thread_local = thread_local\n    setter.doc = node.doc || @annotations.try(&.first?).try(&.doc)\n\n    getter = External.new(\n      \"#{node.name}\", [] of Arg,\n      Primitive.new(\"external_var_get\", var_type), node.real_name || node.name\n    ).at(node.location)\n    getter.set_type(var_type)\n    getter.external_var = true\n    getter.thread_local = thread_local\n    getter.doc = node.doc || @annotations.try(&.first?).try(&.doc)\n\n    type.add_def setter\n    type.add_def getter\n\n    false\n  end\n\n  def visit(node : FunDef)\n    external = node.external\n\n    node.args.each_with_index do |arg, index|\n      restriction = arg.restriction.not_nil!\n      arg_type = lookup_type(restriction)\n      arg_type = check_allowed_in_lib(restriction, arg_type)\n      if arg_type.remove_typedef.void?\n        restriction.raise \"can't use Void as parameter type\"\n      end\n\n      # The external args were added in TopLevelVisitor\n      external.args[index].type = arg_type\n    end\n\n    node_return_type = node.return_type\n    if node_return_type\n      return_type = lookup_type(node_return_type)\n      return_type = check_allowed_in_lib(node_return_type, return_type) unless return_type.nil_type?\n      return_type = @program.nil if return_type.void?\n    else\n      return_type = @program.nil\n    end\n\n    external.set_type(return_type)\n\n    add_external external\n\n    if current_type.is_a?(Program)\n      key = DefInstanceKey.new external.object_id, external.args.map(&.type), nil, nil\n      program.add_def_instance key, external\n    end\n\n    node.type = @program.nil\n\n    false\n  end\n\n  def visit(node : TypeDeclaration)\n    case var = node.var\n    when Var\n      declare_c_struct_or_union_field(node) if @in_c_struct_or_union\n    when InstanceVar\n      declare_instance_var(node, var)\n    when ClassVar\n      declare_class_var(node, var, false)\n    else\n      raise \"Unexpected TypeDeclaration var type: #{var.class}\"\n    end\n\n    false\n  end\n\n  def add_external(external : External)\n    existing = @externals[external.real_name]?\n    if existing\n      unless existing.compatible_with?(external)\n        external.raise \"fun redefinition with different signature (was `#{existing}` at #{existing.location})\"\n      end\n      existing.dead = true\n    end\n    @externals[external.real_name] = external\n  end\n\n  def declare_c_struct_or_union_field(node)\n    type = current_type.as(NonGenericClassType)\n\n    field_type = lookup_type(node.declared_type)\n    field_type = check_allowed_in_lib node.declared_type, field_type\n    if field_type.remove_typedef.void?\n      node.declared_type.raise \"can't use Void as a #{type.type_desc} field type\"\n    end\n\n    field_name = node.var.as(Var).name\n    var_name = '@' + field_name\n\n    if type.lookup_instance_var?(var_name)\n      node.raise \"#{type.type_desc} #{type} already defines a field named '#{field_name}'\"\n    end\n\n    ivar = MetaTypeVar.new(var_name, field_type)\n    ivar.doc = node.var.as(Var).doc\n    ivar.owner = type\n\n    declare_c_struct_or_union_field(type, field_name, ivar, node.location)\n  end\n\n  def declare_c_struct_or_union_field(type, field_name, var, location)\n    type.instance_vars[var.name] = var\n\n    setter = Def.new(\"#{field_name}=\", [Arg.new(\"value\")], Primitive.new(\"struct_or_union_set\").at(location)).at(location)\n    setter.doc = var.doc\n\n    getter = Def.new(field_name, body: InstanceVar.new(var.name)).at(location)\n    getter.doc = var.doc\n\n    type.add_def setter\n    type.add_def getter\n  end\n\n  def declare_instance_var(node, var)\n    unless current_type.allows_instance_vars?\n      node.raise \"can't declare instance variables in #{current_type}\"\n    end\n\n    case owner = current_type\n    when NonGenericClassType\n      declare_instance_var(owner, node, var)\n      return\n    when GenericClassType\n      declare_instance_var(owner, node, var)\n      return\n    when GenericModuleType\n      declare_instance_var(owner, node, var)\n      return\n    when GenericClassInstanceType\n      # OK\n      return\n    when Program, FileModule\n      # Error, continue\n    when NonGenericModuleType\n      declare_instance_var(owner, node, var)\n      return\n    else\n      # Error, continue\n    end\n\n    node.raise \"can only declare instance variables of a non-generic class, not a #{owner.type_desc} (#{owner})\"\n  end\n\n  def declare_instance_var(owner, node, var)\n    annotations = nil\n    process_annotations(@annotations) do |annotation_type, ann|\n      annotations ||= [] of {AnnotationType, Annotation}\n      annotations << {annotation_type, ann}\n    end\n\n    var_type = lookup_type(node.declared_type)\n    var_type = check_declare_var_type(node, var_type, \"an instance variable\")\n    owner_vars = @instance_vars[owner] ||= {} of String => TypeDeclarationWithLocation\n    type_decl = TypeDeclarationWithLocation.new(var_type.virtual_type, node.location.not_nil!,\n      false, annotations)\n    owner_vars[var.name] = type_decl\n  end\n\n  def declare_class_var(node, var, uninitialized)\n    owner = class_var_owner(node)\n    var_type = lookup_type(node.declared_type)\n    var_type = check_declare_var_type(node, var_type, \"a class variable\")\n    owner_vars = @class_vars[owner] ||= {} of String => TypeDeclarationWithLocation\n    owner_vars[var.name] = TypeDeclarationWithLocation.new(var_type.virtual_type, node.location.not_nil!, uninitialized, nil)\n  end\n\n  def visit(node : UninitializedVar)\n    var = node.var\n    case var\n    when InstanceVar\n      declare_instance_var(node, var)\n    when ClassVar\n      declare_class_var(node, var, true)\n    else\n      # nothing (it's a var)\n    end\n    false\n  end\n\n  def visit(node : Assign)\n    false\n  end\n\n  def visit(node : ProcLiteral)\n    node.def.body.accept self\n    false\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_guess_visitor.cr",
    "content": "require \"./semantic_visitor\"\n\nmodule Crystal\n  # Guess the type of class and instance variables\n  # from assignments to them.\n  class TypeGuessVisitor < SemanticVisitor\n    alias TypeDeclarationWithLocation = TypeDeclarationProcessor::TypeDeclarationWithLocation\n    alias InitializeInfo = TypeDeclarationProcessor::InitializeInfo\n    alias InstanceVarTypeInfo = TypeDeclarationProcessor::InstanceVarTypeInfo\n    alias Error = TypeDeclarationProcessor::Error\n\n    getter class_vars\n    getter initialize_infos\n    getter errors\n\n    class TypeInfo\n      property type\n      property outside_def\n      getter location\n\n      def initialize(@type : Type, @location : Location)\n        @outside_def = false\n      end\n    end\n\n    @args : Array(Arg)?\n    @block_arg : Arg?\n    @splat_index : Int32?\n    @double_splat : Arg?\n    @splat : Arg?\n\n    @args_hash_initialized = true\n    @args_hash = {} of String => Arg\n\n    # Before checking types, we set this to nil.\n    # Afterwards, this is non-nil if an error was found\n    # (a type like Class or Reference is used)\n    @error : Error?\n\n    @type_override : Type?\n\n    # We increment this when we start searching types inside another\n    # type that's not the current type we are guessing vars for.\n    # See more comments in `lookup_type?` below.\n    @dont_find_root_generic_type_parameters = 0\n\n    def initialize(mod,\n                   @explicit_instance_vars : Hash(Type, Hash(String, TypeDeclarationWithLocation)),\n                   @guessed_instance_vars : Hash(Type, Hash(String, InstanceVarTypeInfo)),\n                   @initialize_infos : Hash(Type, Array(InitializeInfo)),\n                   @instance_vars_outside : Hash(Type, Array(String)),\n                   @errors : Hash(Type, Hash(String, Error)))\n      super(mod)\n\n      @class_vars = {} of ClassVarContainer => Hash(String, TypeInfo)\n\n      # Was `self` access found? If so, instance variables assigned after it\n      # don't go to an InitializeInfo (they are considered as not being assigned\n      # in that initialize)\n      @found_self = false\n      @has_self_visitor = HasSelfVisitor.new\n\n      # This is to prevent infinite resolution of constants, like in\n      #\n      # ```\n      # A = B\n      # B = A\n      # $x = A\n      # ```\n      @consts = [] of Const\n\n      # Methods being checked for a type guess. We must remember\n      # them to avoid infinite recursive lookup\n      @methods_being_checked = [] of Def\n\n      @outside_def = true\n      @inside_class_method = false\n    end\n\n    def visit(node : Var)\n      # Check for an argument that matches this var, and see\n      # if it has a default value. If so, we do a `self` check\n      # to make sure `self` isn't used\n      if (arg = args_hash[node.name]?) && (default_value = arg.default_value)\n        check_has_self(default_value)\n      end\n\n      check_var_is_self(node)\n      false\n    end\n\n    def visit(node : UninitializedVar)\n      var = node.var\n      if var.is_a?(InstanceVar)\n        if @inside_class_method\n          node.raise \"@instance_vars are not yet allowed in metaclasses: use @@class_vars instead\"\n        end\n\n        @error = nil\n\n        add_to_initialize_info(var.name)\n\n        case owner = current_type\n        when NonGenericClassType\n          process_uninitialized_instance_var(owner, var, node.declared_type)\n        when Program, FileModule\n          # Nothing\n        when NonGenericModuleType\n          process_uninitialized_instance_var(owner, var, node.declared_type)\n        when GenericClassType\n          process_uninitialized_instance_var(owner, var, node.declared_type)\n        when GenericModuleType\n          process_uninitialized_instance_var(owner, var, node.declared_type)\n        else\n          # TODO: can this be reached?\n        end\n      end\n      false\n    end\n\n    def visit(node : Assign)\n      # Invalidate the argument after assigned\n      if (target = node.target).is_a?(Var)\n        args_hash.delete target.name\n      end\n\n      process_assign(node)\n      false\n    end\n\n    def visit(node : MultiAssign)\n      # Invalidate the argument after assigned\n      node.targets.each do |target|\n        args_hash.delete target.name if target.is_a?(Var)\n      end\n\n      process_multi_assign(node)\n      false\n    end\n\n    def visit(node : Call)\n      if @outside_def\n        node.scope = node.global? ? @program : current_type.metaclass\n        super\n      else\n        # If it's \"self.class\", don't consider this as self being passed to a method\n        return false if self_dot_class?(node)\n\n        guess_type_call_lib_out(node)\n        true\n      end\n    end\n\n    def guess_type_call_lib_out(node : Call)\n      # Check if this call is LibFoo.fun(out @x), and deduce\n      # the type from it\n      node.args.each_with_index do |arg, index|\n        next unless arg.is_a?(Out)\n\n        exp = arg.exp\n        next unless exp.is_a?(InstanceVar)\n\n        add_to_initialize_info(exp.name)\n\n        obj = node.obj\n        next unless obj.is_a?(Path)\n\n        obj_type = lookup_type?(obj)\n        next unless obj_type.is_a?(LibType)\n\n        defs = obj_type.defs.try &.[node.name]?\n        # There should be only one, if there is any\n        defs.try &.each do |metadata|\n          external = metadata.def.as(External)\n          fun_def = external.fun_def?\n          next unless fun_def\n\n          fun_arg = fun_def.args[index]?\n          next unless fun_arg\n\n          type = obj_type.lookup_type?(fun_arg.restriction.not_nil!)\n          next unless type.is_a?(PointerInstanceType)\n\n          type = type.element_type\n\n          case owner = current_type\n          when NonGenericClassType\n            process_lib_out(owner, exp, type)\n          when Program, FileModule\n            # Nothing\n          when NonGenericModuleType\n            process_lib_out(owner, exp, type)\n          when GenericClassType\n            process_lib_out(owner, exp, type)\n          when GenericModuleType\n            process_lib_out(owner, exp, type)\n          else\n            # TODO: can this be reached?\n          end\n        end\n      end\n    end\n\n    def process_assign(node : Assign)\n      process_assign(node.target, node.value)\n    end\n\n    def process_assign(target, value)\n      check_has_self(value)\n\n      @error = nil\n\n      result =\n        case target\n        when ClassVar\n          process_assign_class_var(target, value)\n        when InstanceVar\n          if @inside_class_method\n            target.raise \"@instance_vars are not yet allowed in metaclasses: use @@class_vars instead\"\n          end\n\n          process_assign_instance_var(target, value)\n        when Path\n          # Don't guess anything from constant values\n          false\n        else\n          # Process the right hand side in case there's an assignment there too\n          value.accept self\n          nil\n        end\n\n      if error = @error\n        errors = @errors[current_type] ||= {} of String => Error\n        errors[target.to_s] ||= error\n      end\n\n      result\n    end\n\n    def process_multi_assign(node : MultiAssign)\n      @error = nil\n\n      if node.targets.size == node.values.size\n        node.targets.zip(node.values) do |target, value|\n          process_assign(target, value)\n        end\n      else\n        node.values.each do |value|\n          check_has_self(value)\n        end\n\n        node.targets.each do |target|\n          if target.is_a?(InstanceVar)\n            if @inside_class_method\n              target.raise \"@instance_vars are not yet allowed in metaclasses: use @@class_vars instead\"\n            end\n\n            add_to_initialize_info(target.name)\n          end\n        end\n\n        # If it's something like\n        #\n        # ```\n        # @x, @y = exp\n        # ```\n        #\n        # and we can guess the type of `exp` and it's a tuple type,\n        # we can guess the type of @x and @y\n        if node.values.size == 1 &&\n           node.targets.any? { |t| t.is_a?(InstanceVar) || t.is_a?(ClassVar) || t.is_a?(Global) }\n          type = guess_type(node.values.first)\n          if type.is_a?(TupleInstanceType) && type.size >= node.targets.size\n            node.targets.zip(type.tuple_types) do |target, tuple_type|\n              case target\n              when InstanceVar\n                owner_vars = @guessed_instance_vars[current_type] ||= {} of String => InstanceVarTypeInfo\n                add_instance_var_type_info(owner_vars, target.name, tuple_type, target)\n              when ClassVar\n                owner = class_var_owner(target)\n\n                # If the class variable already exists no need to guess its type\n                next if owner.class_vars[target.name]?\n\n                owner_vars = @class_vars[owner] ||= {} of String => TypeInfo\n                add_type_info(owner_vars, target.name, tuple_type, target)\n              else\n                # TODO: can this be reached?\n              end\n            end\n          end\n        end\n      end\n    end\n\n    def process_assign_class_var(target, value)\n      owner = class_var_owner(target)\n\n      # If the class variable already exists no need to guess its type\n      if var = owner.class_vars[target.name]?\n        return var.type\n      end\n\n      type = guess_type(value)\n      if type\n        owner_vars = @class_vars[owner] ||= {} of String => TypeInfo\n        add_type_info(owner_vars, target.name, type, target)\n      end\n      type\n    end\n\n    def process_assign_instance_var(target, value)\n      case owner = current_type\n      when NonGenericClassType\n        value = process_assign_instance_var(owner, target, value)\n      when Program, FileModule\n        # Nothing\n      when NonGenericModuleType\n        value = process_assign_instance_var(owner, target, value)\n      when GenericClassType\n        value = process_assign_instance_var(owner, target, value)\n      when GenericModuleType\n        value = process_assign_instance_var(owner, target, value)\n      end\n\n      unless current_type.allows_instance_vars?\n        target.raise \"can't declare instance variables in #{current_type}\"\n      end\n\n      add_to_initialize_info(target.name)\n\n      value\n    end\n\n    def add_to_initialize_info(name)\n      if !@found_self && (initialize_info = @initialize_info)\n        vars = initialize_info.instance_vars ||= [] of String\n        vars << name unless vars.includes?(name)\n      end\n    end\n\n    def process_assign_instance_var(owner, target, value)\n      if @outside_def\n        outside_vars = @instance_vars_outside[owner] ||= [] of String\n        outside_vars << target.name unless outside_vars.includes?(target.name)\n      end\n\n      # If there is already a type restriction, skip\n      existing = @explicit_instance_vars[owner]?.try &.[target.name]?\n      if existing\n        # Accept the value in case there are assigns there\n        value.accept self\n        return existing.type.as(Type)\n      end\n\n      # For non-generic class we can solve the type now\n      type = guess_type(value)\n      if type\n        owner_vars = @guessed_instance_vars[owner] ||= {} of String => InstanceVarTypeInfo\n        add_instance_var_type_info(owner_vars, target.name, type, target)\n      end\n      type\n    end\n\n    def process_uninitialized_instance_var(owner, target, value)\n      if @outside_def\n        outside_vars = @instance_vars_outside[owner] ||= [] of String\n        outside_vars << target.name unless outside_vars.includes?(target.name)\n      end\n\n      # If there is already a type restriction, skip\n      existing = @explicit_instance_vars[owner]?.try &.[target.name]?\n      if existing\n        return existing.type.as(Type)\n      end\n\n      # For non-generic class we can solve the type now\n      type = lookup_type?(value)\n      if type\n        owner_vars = @guessed_instance_vars[owner] ||= {} of String => InstanceVarTypeInfo\n        add_instance_var_type_info(owner_vars, target.name, type, target)\n      end\n      type\n    end\n\n    def process_lib_out(owner, target, type)\n      # If there is already a type restriction, skip\n      existing = @explicit_instance_vars[owner]?.try &.[target.name]?\n      if existing\n        return existing.type.as(Type)\n      end\n\n      owner_vars = @guessed_instance_vars[owner] ||= {} of String => InstanceVarTypeInfo\n      add_instance_var_type_info(owner_vars, target.name, type, target)\n    end\n\n    def add_type_info(vars, name, type, node)\n      info = vars[name]?\n      if info\n        info.type = Type.merge!(type, info.type)\n      else\n        info = TypeInfo.new(type, node.location.not_nil!)\n      end\n      info.outside_def = true if @outside_def\n      vars[name] = info\n    end\n\n    def add_instance_var_type_info(vars, name, type : Type, node)\n      annotations = nil\n      process_annotations(@annotations) do |annotation_type, ann|\n        annotations ||= [] of {AnnotationType, Annotation}\n        annotations << {annotation_type, ann}\n      end\n\n      info = vars[name]?\n      if info\n        info.type = Type.merge!(info.type, type)\n      else\n        info = InstanceVarTypeInfo.new(node.location.not_nil!, type)\n      end\n      info.outside_def = true if @outside_def\n      info.add_annotations(annotations) if annotations\n      vars[name] = info\n    end\n\n    def guess_type(node : NumberLiteral)\n      program.type_from_literal_kind node.kind\n    end\n\n    def guess_type(node : CharLiteral)\n      program.char\n    end\n\n    def guess_type(node : BoolLiteral)\n      program.bool\n    end\n\n    def guess_type(node : NilLiteral)\n      program.nil\n    end\n\n    def guess_type(node : StringLiteral)\n      program.string\n    end\n\n    def guess_type(node : StringInterpolation)\n      program.string\n    end\n\n    def guess_type(node : SymbolLiteral)\n      program.symbol\n    end\n\n    def guess_type(node : ArrayLiteral)\n      if name = node.name\n        type = lookup_type_no_check?(name)\n        if type.is_a?(GenericClassType)\n          element_types = guess_array_literal_element_types(node)\n          if element_types\n            return type.instantiate([Type.merge!(element_types)] of TypeVar).virtual_type\n          end\n        else\n          return check_can_be_stored(node, type)\n        end\n      elsif node_of = node.of\n        type = lookup_type?(node_of)\n        if type\n          return program.array_of(type.virtual_type).virtual_type\n        end\n      else\n        element_types = guess_array_literal_element_types(node)\n        if element_types\n          return program.array_of(Type.merge!(element_types)).virtual_type\n        end\n      end\n\n      nil\n    end\n\n    def guess_array_literal_element_types(node)\n      element_types = nil\n      node.elements.each do |element|\n        # Splats here require the yield type of `#each`, which we cannot guess\n        return nil if element.is_a?(Splat)\n\n        element_type = guess_type(element)\n        next unless element_type\n\n        element_types ||= [] of Type\n        element_types << element_type\n      end\n      element_types\n    end\n\n    def guess_type(node : HashLiteral)\n      if name = node.name\n        type = lookup_type_no_check?(name)\n        if type.is_a?(GenericClassType)\n          key_types, value_types = guess_hash_literal_key_value_types(node)\n          if key_types && value_types\n            return type.instantiate([Type.merge!(key_types), Type.merge!(value_types)] of TypeVar).virtual_type\n          end\n        else\n          return check_can_be_stored(node, type)\n        end\n      elsif node_of = node.of\n        key_type = lookup_type?(node_of.key)\n        return nil unless key_type\n\n        value_type = lookup_type?(node_of.value)\n        return nil unless value_type\n\n        return program.hash_of(key_type.virtual_type, value_type.virtual_type).virtual_type\n      else\n        key_types, value_types = guess_hash_literal_key_value_types(node)\n        if key_types && value_types\n          return program.hash_of(Type.merge!(key_types), Type.merge!(value_types)).virtual_type\n        end\n      end\n\n      nil\n    end\n\n    def guess_hash_literal_key_value_types(node : HashLiteral)\n      key_types = nil\n      value_types = nil\n      node.entries.each do |entry|\n        key_type = guess_type(entry.key)\n        if key_type\n          key_types ||= [] of Type\n          key_types << key_type\n        end\n\n        value_type = guess_type(entry.value)\n        if value_type\n          value_types ||= [] of Type\n          value_types << value_type\n        end\n      end\n      {key_types, value_types}\n    end\n\n    def guess_type(node : RangeLiteral)\n      from_type = guess_type(node.from)\n      to_type = guess_type(node.to)\n\n      if from_type && to_type\n        program.range_of(from_type, to_type)\n      else\n        nil\n      end\n    end\n\n    def guess_type(node : RegexLiteral)\n      program.regex\n    end\n\n    def guess_type(node : TupleLiteral)\n      element_types = nil\n      node.elements.each do |element|\n        if element.is_a?(Splat)\n          element_type = guess_type(element.exp)\n          return nil unless element_type.is_a?(TupleInstanceType)\n\n          next if element_type.tuple_types.empty?\n          element_types ||= [] of Type\n          element_types.concat(element_type.tuple_types)\n        else\n          element_type = guess_type(element)\n          return nil unless element_type\n\n          element_types ||= [] of Type\n          element_types << element_type\n        end\n      end\n\n      if element_types\n        program.tuple_of(element_types)\n      else\n        nil\n      end\n    end\n\n    def guess_type(node : NamedTupleLiteral)\n      entries = nil\n      node.entries.each do |entry|\n        element_type = guess_type(entry.value)\n        return nil unless element_type\n\n        entries ||= [] of NamedArgumentType\n        entries << NamedArgumentType.new(entry.key, element_type)\n      end\n\n      if entries\n        program.named_tuple_of(entries)\n      else\n        nil\n      end\n    end\n\n    def guess_type(node : ProcLiteral)\n      output = node.def.return_type\n      return nil unless output\n\n      types = nil\n\n      node.def.args.each do |input|\n        restriction = input.restriction\n        return nil unless restriction\n\n        input_type = lookup_type?(restriction)\n        return nil unless input_type\n\n        types ||= [] of Type\n        types << input_type.virtual_type\n      end\n\n      output_type = lookup_type?(output)\n      return nil unless output_type\n\n      types ||= [] of Type\n      types << output_type.virtual_type\n\n      program.proc_of(types)\n    end\n\n    def guess_type(node : Call)\n      if expanded = node.expanded\n        return guess_type(expanded)\n      end\n\n      guess_type_call_lib_out(node)\n\n      obj = node.obj\n\n      # If it's something like T.new, guess T.\n      # If it's something like T(X).new, guess T(X).\n      if node.name == \"new\" && obj && (obj.is_a?(Path) || obj.is_a?(Generic))\n        type = lookup_type?(obj)\n        if type\n          # See if the \"new\" method has a return type annotation, and use it if so\n          return_type = guess_type_from_class_method(type, node)\n          return return_type if return_type\n\n          # Otherwise, infer it to be T\n          return type\n        end\n      end\n\n      # If it's `new(...)` and this is a non-generic class type, guess it to be that class\n      if node.name == \"new\" && !obj && (\n           current_type.is_a?(NonGenericClassType) ||\n           current_type.is_a?(PrimitiveType) ||\n           current_type.is_a?(GenericClassInstanceType)\n         )\n        # See if the \"new\" method has a return type annotation\n        return_type = guess_type_from_class_method(current_type, node)\n        return return_type if return_type\n\n        # Otherwise, infer it to the current type\n        return current_type\n      end\n\n      # If it's Pointer(T).malloc or Pointer(T).null, guess it to Pointer(T)\n      if obj.is_a?(Generic) &&\n         (name = obj.name).is_a?(Path) && name.single?(\"Pointer\") &&\n         node.name.in?(\"malloc\", \"null\")\n        type = lookup_type?(obj)\n        return type if type.is_a?(PointerInstanceType)\n      end\n\n      type = guess_type_call_pointer_malloc_two_args(node)\n      return type if type\n\n      type = guess_type_call_lib_fun(node)\n      return type if type\n\n      type = guess_type_call_with_type_annotation(node)\n\n      # If the type is unbound (uninstantiated generic) but the call\n      # wasn't something like `Gen(Int32).something` then we can never\n      # guess a type, the type is probably inferred from type restrictions\n      if !obj.is_a?(Generic) && type.try &.unbound?\n        return nil\n      end\n\n      return type if type\n\n      nil\n    end\n\n    # If it's Pointer.malloc(size, value), infer element type from value\n    # to T and then infer to Pointer(T)\n    def guess_type_call_pointer_malloc_two_args(node)\n      obj = node.obj\n\n      if node.args.size == 2 && obj.is_a?(Path) &&\n         obj.single?(\"Pointer\") && node.name == \"malloc\"\n        type = lookup_type_no_check?(obj)\n        if type.is_a?(PointerType)\n          element_type = guess_type(node.args[1])\n          if element_type\n            return @program.pointer_of(element_type)\n          end\n        end\n      end\n      nil\n    end\n\n    def guess_type_call_lib_fun(node)\n      # If it's LibFoo.function, where LibFoo is a lib type,\n      # get the type from there\n      obj = node.obj\n      return unless obj.is_a?(Path)\n\n      obj_type = lookup_type?(obj)\n      return unless obj_type.is_a?(LibType)\n\n      defs = obj_type.defs.try &.[node.name]?\n      # There should be only one, if there is any\n      defs.try &.each do |metadata|\n        external = metadata.def.as(External)\n        if def_return_type = external.fun_def?.try &.return_type\n          return_type = obj_type.lookup_type(def_return_type)\n          return return_type if return_type\n        elsif external_type = external.type?\n          # This is the case of an External being an external variable\n          return external_type\n        end\n      end\n      nil\n    end\n\n    # Guess type from T.method, where T is a Path and\n    # method solves to a method with a type annotation\n    # (use the type annotation)\n    def guess_type_call_with_type_annotation(node : Call)\n      if node.global?\n        return guess_type_from_class_method(@program, node)\n      end\n\n      obj = node.obj\n      return nil unless obj\n\n      if obj.is_a?(Path) || obj.is_a?(Generic)\n        obj_type = lookup_type_no_check?(obj)\n        return nil unless obj_type\n\n        return guess_type_from_class_method(obj_type, node)\n      end\n\n      obj_type = guess_type(obj)\n      return nil unless obj_type\n\n      guess_type_from_method(obj_type, node)\n    end\n\n    def guess_type_from_class_method(obj_type, node : Call)\n      @dont_find_root_generic_type_parameters += 1 if obj_type != current_type\n\n      type = guess_type_from_class_method_impl(obj_type, node)\n\n      @dont_find_root_generic_type_parameters -= 1 if obj_type != current_type\n\n      type\n    end\n\n    def guess_type_from_class_method_impl(obj_type, node : Call)\n      metaclass = obj_type.devirtualize.metaclass\n\n      defs = metaclass.lookup_defs(node.name)\n      defs = defs.select do |a_def|\n        a_def_has_block = !!a_def.block_arity\n        call_has_block = !!(node.block || node.block_arg)\n        next unless a_def_has_block == call_has_block\n\n        min_size, max_size = a_def.min_max_args_sizes\n        min_size <= node.args.size <= max_size\n      end\n\n      # If there are no matching defs we can't guess anything\n      return if defs.empty?\n\n      # If it's a \"new\" method without arguments, keep the first one\n      # (might happen that a parent new is found here)\n      if node.name == \"new\" && node.args.empty? && !node.named_args && !node.block\n        defs = [defs.first]\n      end\n\n      # Only use return type if all matching defs have a return type\n      if defs.all? &.return_type\n        # We can only infer the type if all overloads return\n        # the same type (because we can't know the call\n        # argument's type)\n        return_types = defs.map { |a_def| lookup_type?(a_def.return_type.not_nil!, obj_type) || return nil }.uniq!\n        return unless return_types.size == 1\n\n        return return_types[0]\n      end\n\n      # If we only have one def, check the body, we might be\n      # able to infer something from it if it's sufficiently simple\n      return nil unless defs.size == 1\n\n      a_def = defs.first\n      body = a_def.body\n\n      # Prevent infinite recursion\n      if @methods_being_checked.any? &.same?(a_def)\n        return nil\n      end\n\n      @methods_being_checked.push a_def\n\n      # Try to guess from the method's body, but now\n      # the current lookup type is obj_type\n      old_type_override = @type_override\n      @type_override = obj_type\n\n      # Wrap everything in Expressions to check for explicit `return`\n      exps = Expressions.new([body] of ASTNode)\n      type = guess_type_in_method_body(exps)\n\n      @type_override = old_type_override\n\n      @methods_being_checked.pop\n\n      type\n    end\n\n    def guess_type_from_method(obj_type, node : Call)\n      @dont_find_root_generic_type_parameters += 1 if obj_type != current_type\n\n      type = guess_type_from_method_impl(obj_type, node)\n\n      @dont_find_root_generic_type_parameters -= 1 if obj_type != current_type\n\n      type\n    end\n\n    def guess_type_from_method_impl(obj_type, node : Call)\n      return nil if node.block || node.block_arg\n\n      arg_types = node.args.map do |arg|\n        guessed_arg_type = guess_type(arg)\n        return unless guessed_arg_type\n\n        guessed_arg_type\n      end\n\n      named_args_types = node.named_args.try(&.map do |named_arg|\n        guessed_arg_type = guess_type(named_arg.value)\n        return unless guessed_arg_type\n\n        NamedArgumentType.new(named_arg.name, guessed_arg_type)\n      end)\n\n      signature = CallSignature.new(\n        name: node.name,\n        arg_types: arg_types,\n        named_args: named_args_types,\n        block: nil,\n      )\n      matches = obj_type.lookup_matches(signature).matches\n      return nil unless matches\n\n      return_types = matches.compact_map do |match|\n        return_type = match.def.return_type\n        next unless return_type\n\n        lookup_type?(return_type, match.context.defining_type, match.context.instantiated_type.instance_type)\n      end\n\n      return nil if return_types.empty?\n\n      Type.merge(return_types)\n    end\n\n    def guess_type(node : Cast)\n      to = node.to\n\n      # Check for exp.as(typeof(something))\n      #\n      # In this case we can use the same rules for `something`.\n      # This is specially useful for the playground and other tools\n      # that will rewrite code.\n      if to.is_a?(TypeOf) && to.expressions.size == 1\n        exp = to.expressions.first\n        return guess_type(exp)\n      end\n\n      lookup_type?(to)\n    end\n\n    def guess_type(node : NilableCast)\n      type = lookup_type?(node.to)\n      type ? @program.nilable(type) : nil\n    end\n\n    def guess_type(node : UninitializedVar)\n      lookup_type?(node.declared_type)\n    end\n\n    def guess_type(node : Var)\n      if node.name == \"self\"\n        if current_type.is_a?(NonGenericClassType)\n          return current_type.virtual_type\n        else\n          return nil\n        end\n      end\n\n      if arg = args_hash[node.name]?\n        # If the argument has a restriction, guess the type from it\n        if restriction = arg.restriction\n          # It is for something like `def foo(*@foo : *T)`.\n          if @splat.same?(arg)\n            # If restriction is not splat (like `*foo : T`),\n            # we cannot guess the type.\n            # (We can also guess `Indexable(T)`, but it is not perfect.)\n            # And this early return is no problem because splat argument\n            # cannot have a default value.\n            return unless restriction.is_a?(Splat)\n            restriction = restriction.exp\n            # It is for something like `def foo(**@foo : **T)`.\n          elsif @double_splat.same?(arg)\n            return unless restriction.is_a?(DoubleSplat)\n            restriction = restriction.exp\n          end\n          type = lookup_type?(restriction)\n          return type if type\n        end\n\n        # If the argument has a default value, guess the type from it\n        if default_value = arg.default_value\n          return guess_type(default_value)\n        end\n\n        # If the node points block args and there's no restriction,\n        # it means it's a `-> Void` proc\n        if (block_arg = @block_arg) && block_arg.name == node.name\n          return @program.proc_of([@program.void] of Type)\n        end\n      end\n\n      nil\n    end\n\n    def guess_type(node : InstanceVar)\n      # In an assignment like @x = @y, we use the info gathered so far for @y\n      type_decl = @explicit_instance_vars[current_type]?.try &.[node.name]?\n      if (type = type_decl.try &.type).is_a?(Type)\n        return type\n      end\n\n      info = @guessed_instance_vars[current_type]?.try &.[node.name]?\n      if info\n        info.type\n      else\n        nil\n      end\n    end\n\n    def guess_type(node : BinaryOp)\n      left_type = guess_type(node.left)\n      right_type = guess_type(node.right)\n      guess_from_two(left_type, right_type, is_or: node.is_a?(Or))\n    end\n\n    def guess_type(node : If)\n      then_type = guess_type(node.then)\n      else_type = guess_type(node.else)\n      guess_from_two(then_type, else_type)\n    end\n\n    def guess_type(node : Unless)\n      then_type = guess_type(node.then)\n      else_type = guess_type(node.else)\n      guess_from_two(then_type, else_type)\n    end\n\n    def guess_type(node : Case)\n      types = nil\n\n      node.whens.each do |when|\n        type = guess_type(when.body)\n        next unless type\n\n        types ||= [] of Type\n        types << type\n      end\n\n      if node_else = node.else\n        type = guess_type(node_else)\n        if type\n          types ||= [] of Type\n          types << type\n        end\n      end\n\n      types ? Type.merge!(types) : nil\n    end\n\n    def guess_type(node : Path)\n      type = lookup_type_var?(node)\n      return nil unless type\n\n      if type.is_a?(Const)\n        # Don't solve a constant we've already seen\n        return nil if @consts.includes?(type)\n\n        # Check if the const's value is actually an enum member\n        if type.value.type?.try &.is_a?(EnumType)\n          type.value.type\n        else\n          @consts.push(type)\n          type = guess_type(type.value)\n          @consts.pop\n          type\n        end\n      else\n        type.virtual_type.metaclass\n      end\n    end\n\n    def guess_type(node : Expressions)\n      last = node.expressions.last?\n      last ? guess_type(last) : nil\n    end\n\n    def guess_type_in_method_body(node : Expressions)\n      nodes = gather_returns(node)\n      last = node.expressions.last?\n      nodes << last if last\n\n      types = nil\n      nodes.each do |node|\n        type = guess_type(node)\n        return nil unless type\n\n        types ||= [] of Type\n        types << type\n      end\n\n      if types\n        Type.merge!(types)\n      else\n        nil\n      end\n    end\n\n    def guess_type(node : Assign)\n      if node.target.is_a?(Var)\n        return guess_type(node.value)\n      end\n\n      type_var = process_assign(node)\n      type_var.is_a?(Type) ? type_var : nil\n    end\n\n    def guess_type(node : Not)\n      @program.bool\n    end\n\n    def guess_type(node : IsA)\n      @program.bool\n    end\n\n    def guess_type(node : RespondsTo)\n      @program.bool\n    end\n\n    def guess_type(node : SizeOf)\n      @program.int32\n    end\n\n    def guess_type(node : InstanceSizeOf)\n      @program.int32\n    end\n\n    def guess_type(node : AlignOf)\n      @program.int32\n    end\n\n    def guess_type(node : InstanceAlignOf)\n      @program.int32\n    end\n\n    def guess_type(node : OffsetOf)\n      @program.int32\n    end\n\n    def guess_type(node : Nop)\n      @program.nil\n    end\n\n    def guess_from_two(type1, type2, is_or = false)\n      type1 = TruthyFilter.instance.apply(type1) if type1 && is_or\n\n      if type1\n        if type2\n          Type.merge!(type1, type2)\n        else\n          type1\n        end\n      else\n        type2\n      end\n    end\n\n    def guess_type(node : ASTNode)\n      nil\n    end\n\n    def check_has_self(node)\n      return false if node.is_a?(Var)\n\n      @has_self_visitor.reset\n      @has_self_visitor.accept(node)\n      @found_self = true if @has_self_visitor.has_self\n    end\n\n    def check_var_is_self(node : Var)\n      @found_self = true if node.name == \"self\"\n    end\n\n    def lookup_type?(node, root = nil, self_type = nil)\n      find_root_generic_type_parameters =\n        @dont_find_root_generic_type_parameters == 0\n\n      # When searching a type that's not relative to the current type,\n      # we don't want to find type parameters of those types, because they\n      # are not bound.\n      #\n      # For example:\n      #\n      #    class Gen(T)\n      #      def self.new\n      #        Gen(T).new\n      #      end\n      #    end\n      #\n      #    class Foo\n      #      @x = Gen.new\n      #    end\n      #\n      # In the above example we would find `Gen.new`'s body to be\n      # `Gen(T).new` so infer it to return `Gen(T)`, and `T` would be\n      # found because we are searching types relative to `Gen`. But\n      # since our current type is Foo, `T` is unbound, and we don't\n      # want to find it.\n      #\n      # For this code:\n      #\n      #    class Foo(T)\n      #      @x : T\n      #    end\n      #\n      # we *do* want to find T as a type parameter relative to Foo,\n      # even if it's unbound, because we are in the context of Foo.\n      if root\n        find_root_generic_type_parameters = root == current_type\n      else\n        root = current_type\n      end\n\n      type = root.lookup_type?(\n        node,\n        self_type: self_type || root.instance_type,\n        allow_typeof: false,\n        find_root_generic_type_parameters: find_root_generic_type_parameters\n      )\n      check_can_be_stored(node, type)\n    end\n\n    def lookup_type_var?(node, root = current_type)\n      type_var = root.lookup_type_var?(node)\n      return nil unless type_var.is_a?(Type)\n\n      check_can_be_stored(node, type_var)\n      type_var\n    end\n\n    def lookup_type_no_check?(node)\n      current_type.lookup_type?(node, allow_typeof: false)\n    end\n\n    def check_can_be_stored(node, type)\n      if type.is_a?(GenericClassType)\n        nil\n      elsif type.is_a?(GenericModuleType)\n        nil\n      elsif type && !type.can_be_stored?\n        # Types such as Object, Int, etc., are not allowed in generics\n        # and as variables types, so we disallow them.\n        @error = Error.new(node, type)\n        nil\n      elsif type.is_a?(NonGenericClassType)\n        type.virtual_type\n      else\n        type\n      end\n    end\n\n    def visit(node : ClassDef)\n      @initialize_infos[node.resolved_type] ||= [] of InitializeInfo\n      super\n    end\n\n    def visit(node : ModuleDef)\n      @initialize_infos[node.resolved_type] ||= [] of InitializeInfo\n      super\n    end\n\n    def visit(node : TypeDeclaration)\n      if value = node.value\n        process_assign(node.var, value)\n      end\n      false\n    end\n\n    def visit(node : Def)\n      # If this method was redefined and this new method doesn't\n      # call `previous_def`, this method will never be called,\n      # so we ignore it\n      if (next_def = node.next) && !next_def.calls_previous_def?\n        return false\n      end\n\n      super\n\n      @outside_def = false\n      @found_self = false\n      @args = node.args\n      @block_arg = node.block_arg\n      @double_splat = node.double_splat\n      @splat_index = node.splat_index\n      @args_hash_initialized = false\n\n      if !node.receiver && node.name == \"initialize\" && !current_type.is_a?(Program)\n        initialize_info = @initialize_info = InitializeInfo.new(node)\n      end\n\n      @inside_class_method = !!node.receiver\n\n      node.body.accept self\n\n      @inside_class_method = false\n\n      if initialize_info\n        @initialize_infos[current_type] << initialize_info\n      end\n\n      @initialize_info = nil\n      @block_arg = nil\n      @args = nil\n      @double_splat = nil\n      @splat_index = nil\n      @splat = nil\n      @args_hash.clear\n      @args_hash_initialized = true\n      @outside_def = true\n\n      false\n    end\n\n    def visit(node : FunDef)\n      if body = node.body\n        @outside_def = false\n        @args = node.args\n        @args_hash_initialized = false\n        body.accept self\n        @args = nil\n        @args_hash.clear\n        @args_hash_initialized = true\n        @outside_def = true\n      end\n\n      false\n    end\n\n    def visit(node : ProcLiteral)\n      node.def.body.accept self\n      false\n    end\n\n    def visit(node : InstanceSizeOf | SizeOf | InstanceAlignOf | AlignOf | OffsetOf | TypeOf | PointerOf)\n      false\n    end\n\n    def visit(node : MacroExpression)\n      @outside_def ? super : false\n    end\n\n    def visit(node : MacroIf)\n      @outside_def ? super : false\n    end\n\n    def visit(node : MacroFor)\n      @outside_def ? super : false\n    end\n\n    def gather_returns(node)\n      gatherer = ReturnGatherer.new\n      node.accept gatherer\n      gatherer.returns\n    end\n\n    def current_type\n      @type_override || @current_type\n    end\n\n    def args_hash\n      unless @args_hash_initialized\n        @args.try &.each_with_index do |arg, i|\n          @splat = arg if @splat_index == i\n          @args_hash[arg.name] = arg\n        end\n\n        @double_splat.try do |arg|\n          @args_hash[arg.name] = arg\n        end\n\n        @block_arg.try do |arg|\n          @args_hash[arg.name] = arg\n        end\n\n        @args_hash_initialized = true\n      end\n\n      @args_hash\n    end\n  end\n\n  class HasSelfVisitor < Visitor\n    getter has_self\n\n    def initialize\n      @has_self = false\n    end\n\n    def reset\n      @has_self = false\n    end\n\n    def visit(node : Call)\n      # If it's \"self.class\", don't consider this as self being passed to a method\n      return false if self_dot_class?(node)\n\n      true\n    end\n\n    def visit(node : Var)\n      if node.name == \"self\"\n        @has_self = true\n      end\n      false\n    end\n\n    def visit(node : ASTNode)\n      true\n    end\n  end\n\n  class ReturnGatherer < Visitor\n    getter returns\n\n    def initialize\n      @returns = [] of ASTNode\n    end\n\n    def visit(node : Return)\n      @returns << (node.exp || NilLiteral.new)\n      true\n    end\n\n    def visit(node : ASTNode)\n      true\n    end\n  end\nend\n\nprivate def self_dot_class?(node : Crystal::Call)\n  obj = node.obj\n  obj.is_a?(Crystal::Var) && obj.name == \"self\" && node.name == \"class\" && node.args.empty?\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_intersect.cr",
    "content": "require \"../program\"\n\nmodule Crystal\n  class Type\n    # Given two types T and U, returns a common descendent V such that V <= T\n    # and V <= U. This is the same as:\n    #\n    # ```\n    # typeof(begin\n    #   x = uninitialized T\n    #   x.is_a?(U) ? x : raise \"\"\n    # end)\n    # ```\n    #\n    # except that `nil` is returned if the above produces `NoReturn`.\n    def self.common_descendent(type1 : Type, type2 : Type)\n      common_descendent_base(type1, type2)\n    end\n\n    def self.common_descendent(type1 : TupleInstanceType, type2 : TupleInstanceType)\n      type1.implements?(type2) ? type1 : nil\n    end\n\n    def self.common_descendent(type1 : NamedTupleInstanceType, type2 : NamedTupleInstanceType)\n      type1.implements?(type2) ? type1 : nil\n    end\n\n    def self.common_descendent(type1 : ProcInstanceType, type2 : ProcInstanceType)\n      type1.compatible_with?(type2) ? type2 : nil\n    end\n\n    def self.common_descendent(type1 : NonGenericModuleType | GenericModuleInstanceType, type2 : AliasType)\n      common_descendent(type1, type2.remove_alias) ||\n        common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : NonGenericModuleType | GenericModuleInstanceType, type2 : UnionType)\n      common_descendent_union(type1, type2) ||\n        common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : NonGenericModuleType | GenericModuleInstanceType, type2 : VirtualType)\n      common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : NonGenericModuleType | GenericModuleInstanceType, type2 : GenericClassType)\n      common_descendent_instance_and_generic(type1, type2) ||\n        common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : GenericModuleInstanceType, type2 : GenericModuleInstanceType)\n      common_descendent_generic_instances(type1, type2) ||\n        common_descendent_base(type1, type2) ||\n        common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : GenericModuleInstanceType, type2 : GenericModuleType)\n      return type1 if type1.generic_type == type2\n\n      common_descendent_instance_and_generic(type1, type2) ||\n        common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : NonGenericModuleType | GenericModuleInstanceType, type2 : Type)\n      common_descendent_base(type1, type2) ||\n        common_descendent_including_types(type1, type2)\n    end\n\n    def self.common_descendent(type1 : GenericClassInstanceType, type2 : GenericClassType)\n      return type1 if type1.generic_type == type2\n\n      common_descendent_instance_and_generic(type1, type2)\n    end\n\n    def self.common_descendent(type1 : GenericClassType, type2 : GenericClassInstanceType)\n      common_descendent(type2, type1)\n    end\n\n    def self.common_descendent(type1 : GenericInstanceType, type2 : GenericInstanceType)\n      common_descendent_generic_instances(type1, type2) ||\n        common_descendent_base(type1, type2)\n    end\n\n    def self.common_descendent(type1 : MetaclassType, type2 : VirtualMetaclassType)\n      # A module class can't be restricted into a class\n      return nil if type1.instance_type.module?\n\n      # `T+.class` is always a subtype of `Class`\n      return type2 if type1 == type1.program.class_type\n\n      restricted = common_descendent(type1.instance_type, type2.instance_type.base_type)\n      restricted.try(&.metaclass)\n    end\n\n    def self.common_descendent(type1 : VirtualMetaclassType, type2 : MetaclassType)\n      common_descendent(type2, type1)\n    end\n\n    def self.common_descendent(type1 : VirtualMetaclassType, type2 : VirtualMetaclassType)\n      restricted = common_descendent(type1.instance_type, type2.instance_type)\n      restricted.try(&.metaclass)\n    end\n\n    def self.common_descendent(type1 : GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType, type2 : MetaclassType | VirtualMetaclassType)\n      return type1 if type1.instance_type.generic_type.metaclass == type2\n\n      # Special case: a GenericInstanceMetaclass with a union type is\n      # the metaclass of a union type, for example Union(String | Int32).class.\n      # In that case the intersection of that type with a non-union metaclass\n      # will never match: it must also be another union type.\n      return nil if type1.instance_type.is_a?(UnionType)\n\n      restricted = common_descendent(type1.instance_type, type2.instance_type)\n      restricted ? type1 : nil\n    end\n\n    def self.common_descendent(type1 : MetaclassType | VirtualMetaclassType, type2 : GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType)\n      common_descendent(type2, type1)\n    end\n\n    def self.common_descendent(type1 : UnionType, type2 : Type)\n      types = type1.union_types.compact_map do |union_type|\n        common_descendent(union_type, type2)\n      end\n      type1.program.type_merge_union_of(types)\n    end\n\n    def self.common_descendent(type1 : AliasType, type2 : AliasType)\n      return type1 if type1 == type2\n\n      if !type1.simple? && !type2.simple?\n        return nil\n      end\n\n      common_descendent(type1.remove_alias, type2)\n    end\n\n    def self.common_descendent(type1 : AliasType, type2 : Type)\n      common_descendent(type1.remove_alias, type2)\n    end\n\n    def self.common_descendent(type1 : TypeDefType, type2 : UnionType)\n      common_descendent_union(type1, type2)\n    end\n\n    def self.common_descendent(type1 : TypeDefType, type2 : AliasType)\n      type2 = type2.remove_alias\n      return type1 if type1 == type2\n      common_descendent(type1, type2)\n    end\n\n    def self.common_descendent(type1 : TypeDefType, type2 : Type)\n      return type1 if type1 == type2\n\n      restricted = common_descendent(type1.typedef, type2)\n      if restricted == type1.typedef\n        type1\n      elsif restricted.is_a?(UnionType)\n        type1.program.type_merge(restricted.union_types.map { |t| t == type1.typedef ? type1 : t })\n      else\n        restricted\n      end\n    end\n\n    def self.common_descendent(type1 : VirtualType, type2 : VirtualType)\n      return type1 if type1 == type2\n\n      base_type1 = type1.base_type\n      base_type2 = type2.base_type\n      (common_descendent(base_type1, base_type2) || common_descendent(base_type2, base_type1)).try &.virtual_type\n    end\n\n    def self.common_descendent(type1 : VirtualType, type2 : AliasType)\n      common_descendent(type1, type2.remove_alias)\n    end\n\n    def self.common_descendent(type1 : VirtualType, type2 : UnionType)\n      types = type2.union_types.compact_map do |t|\n        common_descendent(type1, t)\n      end\n      type1.program.type_merge_union_of types\n    end\n\n    def self.common_descendent(type1 : VirtualType, type2 : Type)\n      base_type = type1.base_type\n\n      if type2.implements?(base_type)\n        type2.virtual_type\n      elsif base_type.implements?(type2)\n        type1\n      elsif type2.module?\n        types = base_type.subclasses.compact_map do |subclass|\n          common_descendent(subclass.virtual_type, type2)\n        end\n        type1.program.type_merge_union_of types\n      elsif base_type.is_a?(GenericInstanceType) && type2.is_a?(GenericType)\n        # Consider the case of Foo(Int32) vs. Bar(T), with Bar(T) < Foo(T):\n        # we want to return Bar(Int32), so we search in Bar's generic instantiations\n        types = type2.instantiated_types.compact_map do |instance|\n          next if instance.unbound? || instance.abstract?\n          instance.virtual_type if instance.implements?(base_type)\n        end\n        type1.program.type_merge_union_of types\n      else\n        nil\n      end\n    end\n\n    def self.common_descendent(type1 : NilType, type2 : VoidType)\n      # Allow Nil to match Void (useful for `Pointer(Void)#value=`)\n      type1\n    end\n\n    def self.common_descendent(type1 : GenericClassType, type2 : GenericClassType)\n      return type1 if type1 == type2\n\n      common_descendent_instance_and_generic(type1, type2)\n    end\n\n    def self.common_descendent(type1 : Type, type2 : AliasType)\n      return type1 if type1 == type2\n\n      common_descendent(type1, type2.remove_alias)\n    end\n\n    def self.common_descendent(type1 : Type, type2 : UnionType)\n      common_descendent_union(type1, type2)\n    end\n\n    def self.common_descendent(type1 : Type, type2 : VirtualType)\n      type1.implements?(type2.base_type) ? type1 : nil\n    end\n\n    def self.common_descendent(type1 : Type, type2 : GenericClassType)\n      common_descendent_instance_and_generic(type1, type2)\n    end\n\n    private def self.common_descendent_base(type1, type2)\n      if type1 == type2\n        return type1\n      end\n\n      if type1.parents.try &.any? &.implements?(type2)\n        return type1\n      end\n    end\n\n    private def self.common_descendent_union(type, union)\n      restricted = nil\n\n      union.union_types.each do |union_type|\n        # Apply the restriction logic on each union type, even if we already\n        # have a match, so that we can detect ambiguous calls between of\n        # literal types against aliases that resolve to union types.\n        restriction = common_descendent(type, union_type)\n        restricted ||= restriction\n      end\n\n      restricted ? type : nil\n    end\n\n    private def self.common_descendent_including_types(mod, type)\n      mod.including_types.try { |t| common_descendent(t, type) }\n    end\n\n    private def self.common_descendent_instance_and_generic(instance, generic)\n      instance.parents.try &.each do |parent|\n        if parent.module?\n          return instance if parent.implements?(generic)\n        else\n          restricted = common_descendent(parent, generic)\n          return instance if restricted\n        end\n      end\n    end\n\n    private def self.common_descendent_generic_instances(type1, type2)\n      return nil unless type1.generic_type == type2.generic_type\n\n      type1.type_vars.each do |name, type_var1|\n        type_var2 = type2.type_vars[name]\n        if type_var1.is_a?(Var) && type_var2.is_a?(Var)\n          # type vars are invariant except for Tuple and NamedTuple and those have\n          # separate logic\n          return nil unless type_var1.type.devirtualize == type_var2.type.devirtualize\n        else\n          return nil unless type_var1 == type_var2\n        end\n      end\n\n      type1\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_lookup.cr",
    "content": "require \"../types\"\n\nclass Crystal::Type\n  # Searches the type that corresponds to the given *node*, relative\n  # to `self`.\n  #\n  # This method handles AST nodes in the type grammar:\n  #\n  # - Path: Foo::Bar::Baz\n  # - Union: T | U\n  # - Metaclass: T.class\n  # - Generic: Foo(T)\n  # - ProcNotation: T -> U\n  # - TypeOf: typeof(...)\n  # - Self: self\n  #\n  # Passing other AST nodes will raise an exception.\n  #\n  # *self_type* is the type that will be used when `self` is encountered\n  # in the node.\n  #\n  # If *allow_typeof* is `false`, this method raises if there's a typeof\n  # in the given node.\n  #\n  # If *free_vars* is given, when resolving a Path, types will be first searched\n  # in the given Hash.\n  #\n  # If *find_root_generic_type_parameters* is `true` (the default), type parameters\n  # relative to `self_type` will be found. If `false`, they won't be found.\n  #\n  # For example, given:\n  #\n  # ```\n  # class Foo\n  #   class Bar(T)\n  #   end\n  #\n  #   class Baz\n  #   end\n  # end\n  # ```\n  #\n  # If `self` is `Foo` and `Bar(Baz)` is given, the result will be `Foo::Bar(Baz)`.\n  def lookup_type(node : ASTNode, self_type = self.instance_type, allow_typeof = true, free_vars : Hash(String, TypeVar)? = nil, find_root_generic_type_parameters = true) : Type\n    TypeLookup.new(self, self_type, true, allow_typeof, free_vars, find_root_generic_type_parameters).lookup(node).not_nil!\n  end\n\n  # Similar to `lookup_type`, but returns `nil` if a type can't be found.\n  def lookup_type?(node : ASTNode, self_type = self.instance_type, allow_typeof = true, free_vars : Hash(String, TypeVar)? = nil, find_root_generic_type_parameters = true) : Type?\n    TypeLookup.new(self, self_type, false, allow_typeof, free_vars, find_root_generic_type_parameters).lookup(node)\n  end\n\n  # Similar to `lookup_type`, but the result might also be an ASTNode, for example when\n  # looking `N` relative to a StaticArray.\n  def lookup_type_var(node : Path, free_vars : Hash(String, TypeVar)? = nil, find_root_generic_type_parameters = true, remove_alias = true) : Type | ASTNode\n    TypeLookup.new(self, self.instance_type, true, false, free_vars, find_root_generic_type_parameters, remove_alias).lookup_type_var(node).not_nil!\n  end\n\n  # Similar to `lookup_type_var`, but might return `nil`.\n  def lookup_type_var?(node : Path, free_vars : Hash(String, TypeVar)? = nil, raise = false, find_root_generic_type_parameters = true) : Type | ASTNode | Nil\n    TypeLookup.new(self, self.instance_type, raise, false, free_vars, find_root_generic_type_parameters).lookup_type_var?(node)\n  end\n\n  private struct TypeLookup\n    def initialize(@root : Type, @self_type : Type, @raise : Bool, @allow_typeof : Bool, @free_vars : Hash(String, TypeVar)? = nil, @find_root_generic_type_parameters = true, @remove_alias = true)\n      @in_generic_args = 0\n\n      # If we are looking types inside a non-instantiated generic type,\n      # for example Hash(K, V), we want to find K and V as type parameters\n      # of that type.\n      if @find_root_generic_type_parameters && root.is_a?(GenericType)\n        free_vars ||= {} of String => TypeVar\n        root.type_vars.each do |type_var|\n          free_vars[type_var] ||= root.type_parameter(type_var)\n        end\n        @free_vars = free_vars\n      end\n    end\n\n    delegate program, to: @root\n\n    def lookup(node : Path)\n      # A Path might have a type set.\n      # This is at least done in AbstractDefChecker when we replace type\n      # parameters with concrete types.\n      if type = node.type?\n        return type\n      end\n\n      type_var = lookup_type_var?(node)\n\n      case type_var\n      when Const\n        if @raise\n          node.raise \"#{type_var} is not a type, it's a constant\"\n        else\n          nil\n        end\n      when Type\n        type_var\n      else\n        if @raise\n          raise_undefined_constant(node)\n        else\n          nil\n        end\n      end\n    end\n\n    def lookup_type_var(node : Path)\n      type_var = lookup_type_var?(node)\n      return type_var if type_var\n\n      if @raise\n        raise_undefined_constant(node)\n      else\n        nil\n      end\n    end\n\n    def lookup_type_var?(node : Path)\n      # Check if the Path begins with a free variable\n      if !node.global? && (free_var = @free_vars.try &.[node.names.first]?)\n        if node.names.size == 1\n          return free_var\n        elsif free_var.is_a?(Type)\n          type = free_var.lookup_path(node.names[1..-1], lookup_in_namespace: false, location: node.location)\n        end\n      else\n        type = @root.lookup_path(node)\n      end\n\n      if type.is_a?(Type)\n        if @in_generic_args == 0 && type.is_a?(AliasType) && !type.aliased_type?\n          if type.value_processed?\n            node.raise \"infinite recursive definition of alias #{type}\"\n          else\n            type.process_value\n          end\n        end\n        type = type.remove_alias_if_simple if @remove_alias\n      end\n\n      type\n    end\n\n    def lookup(node : Union)\n      types = node.types.map do |ident|\n        type = lookup(ident)\n        return if !@raise && !type\n        type = type.not_nil!\n\n        unless type.can_be_stored?\n          ident.raise \"can't use #{type} in unions yet, use a more specific type\"\n        end\n\n        type.virtual_type\n      end\n      program.type_merge(types)\n    end\n\n    def lookup(node : Metaclass)\n      type = lookup(node.name)\n      return if !@raise && !type\n      type = type.not_nil!\n\n      type.virtual_type.metaclass.virtual_type\n    end\n\n    def lookup(node : Generic)\n      type = lookup(node.name)\n      return if !@raise && !type\n      type = type.not_nil!\n\n      instance_type = type\n\n      case instance_type\n      when NamedTupleType\n        unless node.type_vars.empty?\n          node.raise \"can only instantiate NamedTuple with named arguments\"\n        end\n\n        named_args = node.named_args\n        entries = Array(NamedArgumentType).new(named_args.try(&.size) || 0)\n        named_args.try &.each do |named_arg|\n          subnode = named_arg.value\n\n          if subnode.is_a?(NumberLiteral)\n            subnode.raise \"can't use number as type for NamedTuple\"\n          end\n\n          type = in_generic_args { lookup(subnode) }\n          return if !@raise && !type\n          type = type.not_nil!\n\n          unless type.can_be_stored?\n            subnode.raise \"can't use #{type} as a generic type argument yet, use a more specific type\"\n          end\n\n          entries << NamedArgumentType.new(named_arg.name, type.virtual_type)\n        end\n\n        begin\n          return instance_type.instantiate_named_args(entries)\n        rescue ex : Crystal::CodeError\n          node.raise \"instantiating #{node}\", inner: ex if @raise\n        end\n      when GenericType\n        if instance_type.splat_index\n          if node.named_args\n            node.raise \"can only use named arguments with NamedTuple\"\n          end\n\n          min_needed = instance_type.type_vars.size - 1\n          if node.type_vars.size < min_needed\n            node.wrong_number_of \"type vars\", instance_type, node.type_vars.size, \"#{min_needed}+\"\n          end\n        else\n          if node.named_args\n            node.raise \"can only use named arguments with NamedTuple\"\n          end\n\n          if instance_type.type_vars.size != node.type_vars.size\n            node.wrong_number_of \"type vars\", instance_type, node.type_vars.size, instance_type.type_vars.size\n          end\n        end\n      else\n        node.raise \"#{instance_type} is not a generic type, it's a #{instance_type.type_desc}\"\n      end\n\n      type_vars = Array(TypeVar).new(node.type_vars.size + 1)\n      node.type_vars.each do |type_var|\n        case type_var\n        when NumberLiteral\n          type_vars << type_var\n          next\n        when Splat\n          type = in_generic_args { lookup(type_var.exp) }\n          return if !@raise && !type\n          type = type.not_nil!\n\n          splat_type = type\n          case splat_type\n          when TupleInstanceType\n            type_vars.concat splat_type.tuple_types\n          when TypeParameter\n            # Consider the case of *T, where T is a type parameter\n            type_vars << TypeSplat.new(@root.program, splat_type)\n          else\n            return if !@raise\n\n            type_var.raise \"can only splat tuple type, not #{splat_type}\"\n          end\n          next\n        when SizeOf, InstanceSizeOf, AlignOf, InstanceAlignOf, OffsetOf\n          next unless @raise\n\n          type_var.raise \"can't use #{type_var} as a generic type argument\"\n        end\n\n        # Check the case of T resolving to a number\n        if type_var.is_a?(Path)\n          type = in_generic_args { lookup_type_var(type_var) }\n          case type\n          when Const\n            program.check_deprecated_constant(type, type_var)\n            interpreter = MathInterpreter.new(@root)\n            begin\n              num = interpreter.interpret(type.value)\n              type_vars << NumberLiteral.new(num)\n            rescue ex : Crystal::CodeError\n              type_var.raise \"expanding constant value for a number value\", inner: ex\n            end\n            next\n          when NumberLiteral\n            type_vars << type\n            next\n          end\n        end\n\n        type = in_generic_args { lookup(type_var) }\n        return if !@raise && !type\n        type = type.not_nil!\n\n        case instance_type\n        when GenericUnionType, PointerType, StaticArrayType, TupleType, ProcType\n          unless type.can_be_stored?\n            type_var.raise \"can't use #{type} as a generic type argument yet, use a more specific type\"\n          end\n        end\n\n        type_vars << type.virtual_type\n      end\n\n      begin\n        if instance_type.is_a?(GenericUnionType) && type_vars.any?(TypeSplat)\n          # In the case of `Union(*T)`, we don't need to instantiate the union right\n          # now because it will just return `*T`, but what we want to expand the\n          # union types only when the type is instantiated.\n          # TODO: check that everything is a type\n          MixedUnionType.new(@root.program, type_vars.map(&.as(Type)))\n        else\n          instance_type.as(GenericType).instantiate(type_vars)\n        end\n      rescue ex : Crystal::CodeError\n        node.raise \"instantiating #{node}\", inner: ex if @raise\n      end\n    end\n\n    def lookup(node : ProcNotation)\n      types = [] of Type\n      if inputs = node.inputs\n        inputs.each do |input|\n          if input.is_a?(Splat)\n            type = in_generic_args { lookup(input.exp) }\n            return if !@raise && !type\n            type = type.not_nil!\n\n            a_type = type\n            if a_type.is_a?(TupleInstanceType)\n              types.concat(a_type.tuple_types)\n            else\n              if @raise\n                input.exp.raise \"can only splat tuple type, not #{a_type}\"\n              else\n                return\n              end\n            end\n          else\n            type = in_generic_args { lookup(input) }\n            return if !@raise && !type\n            type = type.not_nil!\n\n            unless type.can_be_stored?\n              input.raise \"can't use #{type} as proc argument yet, use a more specific type\"\n            end\n\n            types << type.virtual_type\n          end\n        end\n      end\n\n      if output = node.output\n        type = in_generic_args { lookup(output) }\n        return if !@raise && !type\n        type = type.not_nil!\n\n        unless type.can_be_stored?\n          output.raise \"can't use #{type} as proc return type yet, use a more specific type\"\n        end\n\n        types << type.virtual_type\n      else\n        types << program.void\n      end\n\n      program.proc_of(types)\n    end\n\n    def lookup(node : Self)\n      if @self_type.is_a?(Program)\n        node.raise \"there's no self in this scope\"\n      end\n\n      if (self_type = @self_type).is_a?(GenericType) && (free_vars = @free_vars)\n        # Only instantiate self type with available free variables\n        params = self_type.type_vars.map do |type_var|\n          free_var = free_vars[type_var]?\n          if free_var\n            self_type.type_parameter(type_var).as(TypeVar)\n          else\n            return @self_type.virtual_type\n          end\n        end\n        self_type.instantiate(params)\n      else\n        @self_type.virtual_type\n      end\n    end\n\n    def lookup(node : TypeOf)\n      unless @allow_typeof\n        if @raise\n          node.raise \"can't use 'typeof' here\"\n        else\n          return\n        end\n      end\n\n      meta_vars = MetaVars{\"self\" => MetaVar.new(\"self\", @self_type)}\n      visitor = MainVisitor.new(program, meta_vars)\n      expressions = node.expressions.clone\n      begin\n        expressions.each &.accept visitor\n      rescue ex : Crystal::CodeError\n        node.raise \"typing typeof\", inner: ex\n      end\n      program.type_merge expressions\n    end\n\n    def lookup(node : Splat)\n      splat_type = in_generic_args { lookup(node.exp) }\n      case splat_type\n      when TypeParameter\n        # Consider the case of *T, where T is a type parameter\n        TypeSplat.new(@root.program, splat_type)\n      else\n        return if !@raise\n\n        node.raise \"can only splat tuple type, not #{splat_type}\"\n      end\n    end\n\n    def lookup(node : Underscore)\n      node.raise \"can't use underscore as generic type argument\" if @raise\n    end\n\n    def lookup(node : ASTNode)\n      raise \"BUG: unknown node in TypeLookup: #{node} #{node.class_desc}\"\n    end\n\n    def raise_undefined_constant(node)\n      check_cant_infer_generic_type_parameter(@root, node)\n      node.raise_undefined_constant(@root)\n    end\n\n    def check_cant_infer_generic_type_parameter(scope, node)\n      if scope.is_a?(MetaclassType) && (instance_type = scope.instance_type).is_a?(GenericType)\n        first_name = node.names.first\n        if instance_type.type_vars.includes?(first_name)\n          node.raise \"can't infer the type parameter #{first_name} for the #{instance_type.type_desc} #{instance_type}. Please provide it explicitly\"\n        end\n      end\n    end\n\n    def in_generic_args(&)\n      @in_generic_args += 1\n      value = yield\n      @in_generic_args -= 1\n      value\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_merge.cr",
    "content": "require \"../program\"\n\nmodule Crystal\n  class Program\n    def type_merge(types : Array(Type?)) : Type?\n      case types.size\n      when 0\n        nil\n      when 1\n        types.first\n      when 2\n        # Merging two types is the most common case, so we optimize it\n        first, second = types\n        type_merge(first, second)\n      else\n        combined_union_of compact_types(types)\n      end\n    end\n\n    def type_merge(nodes : Enumerable(ASTNode)) : Type?\n      case nodes.size\n      when 0\n        nil\n      when 1\n        nodes.first.type?\n      when 2\n        # Merging two types is the most common case, so we optimize it\n        # We use `#each_cons_pair` to avoid any intermediate allocation\n        nodes.each_cons_pair do |first, second|\n          return type_merge(first.type?, second.type?)\n        end\n      else\n        combined_union_of compact_types(nodes, &.type?)\n      end\n    end\n\n    def type_merge(first : Type?, second : Type?) : Type?\n      # Same, so return any of them\n      return first if first == second\n\n      # First is nil, so return second\n      return second unless first\n\n      # Second is nil, so return first\n      return first unless second\n\n      # NoReturn is removed from unions\n      return second if first.no_return?\n      return first if second.no_return?\n\n      if first.nil_type? && second.is_a?(UnionType) && second.union_types.includes?(first)\n        return second\n      end\n\n      if second.nil_type? && first.is_a?(UnionType) && first.union_types.includes?(second)\n        return first\n      end\n\n      # General case\n      combined_union_of compact_types({first, second})\n    end\n\n    def type_merge_union_of(types : Array(Type)) : Type?\n      union_of compact_types(types)\n    end\n\n    def compact_types(types) : Array(Type)\n      compact_types(types) { |type| type }\n    end\n\n    def compact_types(objects, &) : Array(Type)\n      all_types = Array(Type).new(objects.size)\n      objects.each { |obj| add_type all_types, yield(obj) }\n      all_types.reject! &.no_return? if all_types.size > 1\n      all_types\n    end\n\n    def add_type(types, type : UnionType)\n      type.union_types.each do |subtype|\n        add_type types, subtype\n      end\n    end\n\n    def add_type(types, type : AliasType)\n      aliased = type.remove_alias\n      if aliased == type\n        types << type unless types.includes? type\n      else\n        add_type types, aliased\n      end\n    end\n\n    # When Void participates in a union, it becomes Nil\n    # (users shouldn't deal with real Void values)\n    def add_type(types, type : VoidType)\n      add_type(types, nil_type)\n    end\n\n    def add_type(types, type : Type)\n      types << type unless types.includes? type\n    end\n\n    def add_type(set, type : Nil)\n      # Nothing to do\n    end\n\n    def combined_union_of(types : Array)\n      case types.size\n      when 0\n        nil\n      when 1\n        types.first\n      else\n        combined_types = type_combine types\n        union_of combined_types\n      end\n    end\n\n    def type_combine(types)\n      all_types = [types.shift] of Type\n\n      types.each do |t2|\n        not_found = all_types.all? do |t1|\n          ancestor = Type.least_common_ancestor(t1.devirtualize, t2.devirtualize)\n          if ancestor && virtual_root?(ancestor)\n            all_types.delete t1\n            all_types << ancestor.virtual_type\n            false\n          else\n            true\n          end\n        end\n        if not_found\n          all_types << t2\n        end\n      end\n\n      all_types\n    end\n\n    # Returns true if *type* can be used as a virtual root; that is, it must not\n    # be one of Object, Reference, Value, Struct, Number, Int, Float, or their\n    # corresponding metaclasses.\n    def virtual_root?(type)\n      # This discards Object, Reference and Value\n      return false if type.is_a?(ClassType) && type.depth <= 1\n\n      case type\n      when self.struct, self.number, self.int, self.float\n        false\n      else\n        true\n      end\n    end\n\n    def virtual_root?(type : VirtualType | VirtualMetaclassType)\n      virtual_root?(type.base_type)\n    end\n\n    def virtual_root?(type : MetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType)\n      virtual_root?(type.instance_type)\n    end\n  end\n\n  class Type\n    def self.merge(nodes : Enumerable(ASTNode)) : Type?\n      nodes.find(&.type?).try &.type.program.type_merge(nodes)\n    end\n\n    def self.merge(types : Array(Type)) : Type?\n      if types.size == 0\n        nil\n      else\n        types.first.program.type_merge(types)\n      end\n    end\n\n    def self.merge!(types_or_nodes) : Type\n      merge(types_or_nodes).not_nil!\n    end\n\n    def self.merge!(type1 : Type, type2 : Type) : Type\n      type1.program.type_merge(type1, type2).not_nil!\n    end\n\n    # Given two non-union types T and U, returns their least common ancestor\n    # LCA(T, U) such that the following properties are satisfied:\n    #\n    # * LCA(T, U) is never a union;\n    # * LCA(T, U) is never virtual, since `#type_combine` takes care of this;\n    # * T <= LCA(T, U) and U <= LCA(T, U);\n    # * for any type V, if T <= V and U <= V, then LCA(T, U) <= V;\n    # * LCA is commutative up to equivalence; that is, if V = LCA(T, U) and\n    #   W = LCA(U, T), then V <= W and W <= V;\n    # * LCA is associative up to equivalence.\n    #\n    # If such a type exists and this type can be used as a virtual root (see\n    # `Program#virtual_root?`), then T | U is precisely the virtual type of\n    # LCA(T, U). Otherwise, T | U is an irreducible union and this method should\n    # return `nil`.\n    #\n    # The above applies only if T and U are unequal; this is guaranteed by\n    # `Program#add_type`, so T | T produces a non-virtual type. However, this\n    # method should not break in case it recursively calls itself with two\n    # identical types.\n    def self.least_common_ancestor(type1 : Type, type2 : Type)\n      nil\n    end\n\n    def self.least_common_ancestor(\n      type1 : MetaclassType | GenericClassInstanceMetaclassType,\n      type2 : MetaclassType | GenericClassInstanceMetaclassType,\n    )\n      return nil unless unifiable_metaclass?(type1) && unifiable_metaclass?(type2)\n\n      common = least_common_ancestor(type1.instance_type, type2.instance_type)\n      common.try &.metaclass\n    end\n\n    def self.least_common_ancestor(type1 : NonGenericModuleType | GenericModuleInstanceType | GenericClassType, type2 : Type)\n      type1 if type2.implements?(type1)\n    end\n\n    def self.least_common_ancestor(type1 : Type, type2 : NonGenericModuleType | GenericModuleInstanceType | GenericClassType)\n      type2 if type1.implements?(type2)\n    end\n\n    def self.least_common_ancestor(\n      type1 : NonGenericModuleType | GenericModuleInstanceType | GenericClassType,\n      type2 : NonGenericModuleType | GenericModuleInstanceType | GenericClassType,\n    )\n      return type2 if type1.implements?(type2)\n      return type1 if type2.implements?(type1)\n    end\n\n    def self.least_common_ancestor(type1 : GenericClassType, type2 : ClassType | GenericClassInstanceType)\n      return type2 if type1.implements?(type2)\n      return type1 if type2.implements?(type1)\n    end\n\n    def self.least_common_ancestor(type1 : ClassType | GenericClassInstanceType, type2 : GenericClassType)\n      return type1 if type2.implements?(type1)\n      return type2 if type1.implements?(type2)\n    end\n\n    def self.least_common_ancestor(type1 : ClassType | GenericClassInstanceType, type2 : ClassType | GenericClassInstanceType)\n      return type1 if type1 == type2\n\n      if type1.depth == type2.depth\n        t1_superclass = type1.superclass\n        t2_superclass = type2.superclass\n\n        if t1_superclass && t2_superclass\n          return least_common_ancestor(t1_superclass, t2_superclass)\n        end\n      elsif type1.depth > type2.depth\n        t1_superclass = type1.superclass\n        if t1_superclass\n          return least_common_ancestor(t1_superclass, type2)\n        end\n      elsif type1.depth < type2.depth\n        t2_superclass = type2.superclass\n        if t2_superclass\n          return least_common_ancestor(type1, t2_superclass)\n        end\n      end\n\n      nil\n    end\n\n    def self.least_common_ancestor(type1 : TupleInstanceType, type2 : TupleInstanceType)\n      return nil unless type1.size == type2.size\n\n      result_types = type1.tuple_types.map_with_index do |self_tuple_type, index|\n        merge!(self_tuple_type, type2.tuple_types[index]).as(Type)\n      end\n      type1.program.tuple_of(result_types)\n    end\n\n    def self.least_common_ancestor(type1 : NamedTupleInstanceType, type2 : NamedTupleInstanceType)\n      return nil if type1.size != type2.size\n\n      self_entries = type1.entries.sort_by &.name\n      other_entries = type2.entries.sort_by &.name\n\n      # First check if the names are the same\n      self_entries.zip(other_entries) do |self_entry, other_entry|\n        return nil unless self_entry.name == other_entry.name\n      end\n\n      # If the names are the same we now merge the types for each key\n      # NOTE: we use self's order to preserve the order of the tuple on the left hand side\n      merged_entries = type1.entries.map_with_index do |self_entry, i|\n        name = self_entry.name\n        other_type = type2.name_type(name)\n        merged_type = merge!(self_entry.type, other_type).as(Type)\n        NamedArgumentType.new(name, merged_type)\n      end\n\n      type1.program.named_tuple_of(merged_entries)\n    end\n\n    private def self.unifiable_metaclass?(type)\n      case type.instance_type\n      when .module?\n        false # Module metaclasses are never unified\n      when UnionType\n        false # Union metaclasses are never unified\n      when TupleInstanceType\n        false # Tuple instances might be unified, but never tuple metaclasses\n      when NamedTupleInstanceType\n        false # Named tuple instances might be unified, but never named tuple metaclasses\n      else\n        true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/type_to_restriction.cr",
    "content": "require \"../types\"\n\nmodule Crystal\n  # Allows converting a type to a restriction from the context a given type.\n  struct TypeToRestriction\n    # Initializes this converter to convert types relative to the given type.\n    def initialize(@from_type : Type)\n    end\n\n    def convert(type : NilType)\n      Path.global(\"Nil\")\n    end\n\n    def convert(type : VoidType)\n      Path.global(\"Void\")\n    end\n\n    def convert(type : BoolType)\n      Path.global(\"Bool\")\n    end\n\n    def convert(type : CharType)\n      Path.global(\"Char\")\n    end\n\n    def convert(type : SymbolType)\n      Path.global(\"Symbol\")\n    end\n\n    def convert(type : IntegerType | FloatType)\n      case type.kind\n      in NumberKind::I8   then Path.global(\"Int8\")\n      in NumberKind::I16  then Path.global(\"Int16\")\n      in NumberKind::I32  then Path.global(\"Int32\")\n      in NumberKind::I64  then Path.global(\"Int64\")\n      in NumberKind::I128 then Path.global(\"Int128\")\n      in NumberKind::U8   then Path.global(\"UInt8\")\n      in NumberKind::U16  then Path.global(\"UInt16\")\n      in NumberKind::U32  then Path.global(\"UInt32\")\n      in NumberKind::U64  then Path.global(\"UInt64\")\n      in NumberKind::U128 then Path.global(\"UInt128\")\n      in NumberKind::F32  then Path.global(\"Float32\")\n      in NumberKind::F64  then Path.global(\"Float64\")\n      end\n    end\n\n    def convert(type : NonGenericClassType |\n                       NonGenericModuleType |\n                       EnumType |\n                       AliasType |\n                       TypeDefType)\n      type_to_path(type)\n    end\n\n    def convert(type : TupleInstanceType)\n      Generic.new(\n        Path.global(\"Tuple\"),\n        type.tuple_types.map do |tuple_type|\n          convert(tuple_type) || Underscore.new\n        end\n      )\n    end\n\n    def convert(type : NamedTupleInstanceType)\n      Generic.new(\n        Path.global(\"NamedTuple\"),\n        type_vars: [] of ASTNode,\n        named_args: type.entries.map do |entry|\n          NamedArgument.new(\n            entry.name,\n            convert(entry.type) || Underscore.new,\n          )\n        end\n      )\n    end\n\n    def convert(type : ProcInstanceType)\n      inputs =\n        type.arg_types.map do |arg_type|\n          convert(arg_type) || Underscore.new\n        end\n\n      output =\n        if type.return_type.is_a?(NilType)\n          # Because there's some strange autocasting for Procs that return Nil,\n          # it's better if we don't do anything fancy here.\n          Underscore.new\n        else\n          convert(type.return_type) || Underscore.new\n        end\n\n      ProcNotation.new(inputs, output)\n    end\n\n    def convert(type : GenericInstanceType)\n      path = type_to_path(type.generic_type)\n      type_vars = type.type_vars.map do |name, type_var|\n        if type_var.is_a?(NumberLiteral)\n          type_var.clone\n        elsif type_var_type = type_var.type?\n          convert(type_var_type) || Underscore.new\n        else\n          Underscore.new\n        end\n      end\n      Generic.new(path, type_vars)\n    end\n\n    def convert(type : UnionType)\n      converted_union_types =\n        type.union_types.map do |union_type|\n          restriction = convert(union_type)\n          return unless restriction\n\n          restriction.as(ASTNode)\n        end\n\n      if type.union_types.any?(TypeSplat)\n        Generic.new(Path.global(\"Union\"), converted_union_types)\n      else\n        Union.new(converted_union_types)\n      end\n    end\n\n    def convert(type : MetaclassType)\n      restriction = convert(type.instance_type)\n      return unless restriction\n\n      Metaclass.new(restriction)\n    end\n\n    def convert(type : TypeParameter)\n      Path.new(type.name)\n    end\n\n    def convert(type : NoReturnType)\n      Path.global(\"NoReturn\")\n    end\n\n    def convert(type : VirtualType)\n      convert(type.base_type)\n    end\n\n    def convert(type : VirtualMetaclassType |\n                       GenericClassInstanceMetaclassType |\n                       GenericModuleInstanceMetaclassType)\n      converted = convert(type.instance_type)\n      converted ? Metaclass.new(converted) : nil\n    end\n\n    def convert(type : TypeSplat)\n      converted = convert(type.splatted_type)\n      converted ? Splat.new(converted) : nil\n    end\n\n    # These can't happen as instance var types\n    def convert(type : Crystal::GenericClassType |\n                       Crystal::GenericModuleType |\n                       Crystal::LibType |\n                       Crystal::AnnotationType |\n                       Crystal::Const |\n                       Crystal::NumberAutocastType |\n                       Crystal::SymbolAutocastType)\n      nil\n    end\n\n    private def type_to_path(type)\n      common_namespace =\n        if fully_public?(type)\n          # If the type if fully public we can fully qualify the restriction\n          nil\n        else\n          # Otherwise, we need to use a relative path starting from `from_type`\n          common_namespace(type, @from_type)\n        end\n\n      names = [] of String\n      append_namespace(type, names, upto: common_namespace)\n      names << type.name\n\n      Path.new(names, global: !common_namespace)\n    end\n\n    private def common_namespace(type, from_type)\n      while true\n        return nil if type.is_a?(Program)\n\n        # If from_type is Foo::Bar and type is Foo::Bar::Baz\n        # we want to say that the common namespace Foo::Bar\n        return type if type == from_type\n\n        # If from_type is Foo::Bar and type is Foo::Baz\n        # we want to say that the common namespace is Foo\n        return type if type == from_type.namespace\n\n        type = type.namespace\n      end\n    end\n\n    private def append_namespace(type, names, upto = nil)\n      namespace = type.namespace\n      return if namespace.is_a?(Program)\n      return if namespace == upto\n\n      append_namespace(namespace, names, upto)\n      names << namespace.name\n    end\n\n    private def fully_public?(type)\n      return true if type.is_a?(Program)\n\n      !type.private? && fully_public?(type.namespace)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic/warnings.cr",
    "content": "require \"../warnings\"\n\nmodule Crystal\n  class Program\n    # Warning settings and all detected warnings.\n    property warnings = WarningCollection.new\n\n    @deprecated_constants_detected = Set(String).new\n    @deprecated_types_detected = Set(String).new\n    @deprecated_methods_detected = Set(String).new\n    @deprecated_macros_detected = Set(String).new\n    @deprecated_annotations_detected = Set(String).new\n\n    def check_deprecated_constant(const : Const, node : Path)\n      return unless @warnings.level.all?\n\n      check_deprecation(const, node, @deprecated_constants_detected)\n    end\n\n    def check_deprecated_type(type : Type, node : Path)\n      return unless @warnings.level.all?\n\n      unless check_deprecation(type, node, @deprecated_types_detected)\n        if type.is_a?(AliasType)\n          check_deprecation(type.aliased_type, node, @deprecated_types_detected)\n        end\n      end\n    end\n\n    def check_call_to_deprecated_macro(a_macro : Macro, call : Call)\n      return unless @warnings.level.all?\n\n      check_deprecation(a_macro, call, @deprecated_macros_detected)\n    end\n\n    def check_call_to_deprecated_method(node : Call)\n      return unless @warnings.level.all?\n      return if compiler_expanded_call(node)\n\n      # check if method if deprecated, otherwise check if any arg is deprecated\n      node.target_defs.try &.each do |target_def|\n        next if check_deprecation(target_def, node, @deprecated_methods_detected)\n\n        # can't annotate fun arguments\n        next if target_def.is_a?(External)\n\n        # skip last call in expanded default arguments def (it's calling the def\n        # with all the default args, and it would always trigger a warning)\n        next if node.expansion?\n\n        node.args.zip(target_def.args) do |arg, def_arg|\n          break if check_deprecation(def_arg, arg, @deprecated_methods_detected)\n        end\n      end\n    end\n\n    def check_call_to_deprecated_annotation(node : AnnotationDef) : Nil\n      return unless @warnings.level.all?\n\n      check_deprecation(node, node.name, @deprecated_annotations_detected)\n    end\n\n    private def check_deprecation(object, use_site, detects)\n      if (ann = object.annotation(self.deprecated_annotation)) &&\n         (deprecated_annotation = DeprecatedAnnotation.from(ann))\n        use_location = use_site.location.try(&.macro_location) || use_site.location\n        return false if !use_location || @warnings.ignore_warning_due_to_location?(use_location)\n\n        # skip warning if the use site was already informed\n        name = if object.responds_to?(:short_reference)\n                 object.short_reference\n               else\n                 object.to_s\n               end\n        warning_key = \"#{name} #{use_location}\"\n        return true if detects.includes?(warning_key)\n        detects.add(warning_key)\n\n        full_message = String.build do |io|\n          io << \"Deprecated \" << name << '.'\n          if message = deprecated_annotation.message\n            io << ' ' << message\n          end\n        end\n\n        @warnings.infos << use_site.warning(full_message)\n        true\n      else\n        false\n      end\n    end\n\n    private def compiler_expanded_call(node : Call)\n      # Compiler generates a `_.initialize` call in `new`\n      node.obj.as?(Var).try { |v| v.name == \"_\" } && node.name == \"initialize\"\n    end\n  end\n\n  class AnnotationDef\n    def short_reference\n      \"annotation #{resolved_type}\"\n    end\n  end\n\n  class Macro\n    def short_reference\n      case owner\n      when Program\n        \"::#{name}\"\n      when MetaclassType\n        \"#{owner.instance_type.to_s(generic_args: false)}.#{name}\"\n      else\n        \"#{owner}.#{name}\"\n      end\n    end\n  end\n\n  struct DeprecatedAnnotation\n    getter message : String?\n\n    def initialize(@message = nil)\n    end\n\n    def self.from(ann : Annotation)\n      args = ann.args\n      named_args = ann.named_args\n\n      if named_args\n        ann.raise \"too many named arguments (given #{named_args.size}, expected maximum 0)\"\n      end\n\n      message = nil\n      count = 0\n\n      args.each do |arg|\n        case count\n        when 0\n          arg.raise \"first argument must be a String\" unless arg.is_a?(StringLiteral)\n          message = arg.value\n        else\n          ann.wrong_number_of \"deprecated annotation arguments\", args.size, \"1\"\n        end\n\n        count += 1\n      end\n\n      new(message)\n    end\n  end\n\n  class Def\n    def short_reference\n      case owner\n      when Program\n        \"::#{original_name}\"\n      when .metaclass?\n        \"#{owner.instance_type}.#{original_name}\"\n      else\n        \"#{owner}##{original_name}\"\n      end\n    end\n  end\n\n  class Arg\n    def short_reference\n      \"argument #{original_name}\"\n    end\n  end\n\n  class Const\n    def short_reference\n      to_s\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/semantic.cr",
    "content": "require \"./program\"\nrequire \"./syntax/ast\"\nrequire \"./syntax/visitor\"\nrequire \"./semantic/*\"\n\n# The overall algorithm for semantic analysis of a program is:\n# - top level: declare classes, modules, macros, defs and other top-level stuff\n# - new methods: create `new` methods for every `initialize` method\n# - type declarations: process type declarations like `@x : Int32`\n# - check abstract defs: check that abstract defs are implemented\n# - class_vars_initializers (ClassVarsInitializerVisitor): process initializers like `@@x = 1`\n# - instance_vars_initializers (InstanceVarsInitializerVisitor): process initializers like `@x = 1`\n# - main: process \"main\" code, calls and method bodies (the whole program).\n# - cleanup: remove dead code and other simplifications\n# - check recursive structs (RecursiveStructChecker): check that structs are not recursive (impossible to codegen)\n\nclass Crystal::Program\n  # Runs semantic analysis on the given node, returning a node\n  # that's typed. In the process types and methods are defined in\n  # this program.\n  def semantic(node : ASTNode, cleanup = true, main_visitor : MainVisitor = MainVisitor.new(self)) : ASTNode\n    node, processor = top_level_semantic(node, main_visitor)\n\n    @progress_tracker.stage(\"Semantic (ivars initializers)\") do\n      visitor = InstanceVarsInitializerVisitor.new(self)\n      visit_with_finished_hooks(node, visitor)\n      visitor.finish\n    end\n\n    @progress_tracker.stage(\"Semantic (cvars initializers)\") do\n      visit_class_vars_initializers(node)\n    end\n\n    # Check that class vars without an initializer are nilable,\n    # give an error otherwise\n    processor.check_non_nilable_class_vars_without_initializers\n\n    result = @progress_tracker.stage(\"Semantic (main)\") do\n      visit_main(node, process_finished_hooks: true, cleanup: cleanup, visitor: main_visitor)\n    end\n\n    @progress_tracker.stage(\"Semantic (cleanup)\") do\n      cleanup_types\n      cleanup_files\n    end\n\n    @progress_tracker.stage(\"Semantic (recursive struct check)\") do\n      RecursiveStructChecker.new(self).run\n    end\n\n    result\n  end\n\n  # Processes type declarations and instance/class/global vars\n  # types are guessed or followed according to type annotations.\n  #\n  # This alone is useful for some tools like doc or hierarchy\n  # where a full semantic of the program is not needed.\n  def top_level_semantic(node, main_visitor : MainVisitor = MainVisitor.new(self))\n    new_expansions = @progress_tracker.stage(\"Semantic (top level)\") do\n      visitor = TopLevelVisitor.new(self)\n\n      # This is mainly for the interpreter so that vars are populated\n      # for macro calls.\n      # For compiled Crystal this should have no effect because we always\n      # use a new MainVisitor which will have no vars.\n      visitor.vars = main_visitor.vars.dup unless main_visitor.vars.empty?\n\n      node.accept visitor\n      visitor.process_finished_hooks\n      visitor.new_expansions\n    end\n    @progress_tracker.stage(\"Semantic (new)\") do\n      define_new_methods(new_expansions)\n    end\n    node, processor = @progress_tracker.stage(\"Semantic (type declarations)\") do\n      TypeDeclarationProcessor.new(self).process(node)\n    end\n\n    @progress_tracker.stage(\"Semantic (abstract def check)\") do\n      AbstractDefChecker.new(self).run\n    end\n\n    unless @program.has_flag?(\"no_restrictions_augmenter\")\n      @progress_tracker.stage(\"Semantic (restrictions augmenter)\") do\n        node.accept RestrictionsAugmenter.new(self, new_expansions)\n      end\n    end\n\n    self.top_level_semantic_complete = true\n\n    {node, processor}\n  end\n\n  # This property indicates that the compiler has finished the top-level semantic\n  # stage.\n  # At this point, instance variables are declared and macros `#instance_vars`\n  # and `#has_internal_pointers?` provide meaningful information.\n  #\n  # FIXME: Introduce a more generic method to track progress of compiler stages\n  # (potential synergy with `ProcessTracker`?).\n  property? top_level_semantic_complete = false\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/ast.cr",
    "content": "module Crystal\n  # Base class for nodes in the grammar.\n  abstract class ASTNode\n    # The location where this node starts, or `nil`\n    # if the location is not known.\n    property location : Location?\n\n    # The location where this node ends, or `nil`\n    # if the location is not known.\n    property end_location : Location?\n\n    # Updates this node's location and returns `self`\n    def at(@location : Location?)\n      self\n    end\n\n    # Sets this node's location and end location to those\n    # of `node`, and returns `self`\n    def at(node : ASTNode)\n      @location = node.location\n      @end_location = node.end_location\n      self\n    end\n\n    # Updates this node's end location and returns `self`\n    def at_end(@end_location : Location?)\n      self\n    end\n\n    # Sets this node's end location to those of `node` and\n    # returns self\n    def at_end(node : ASTNode)\n      @end_location = node.end_location\n      self\n    end\n\n    # Returns the number of lines between start and end locations\n    def length : Int32?\n      Location.lines(location, end_location)\n    end\n\n    # Returns a deep copy of this node. Copied nodes retain\n    # the location and end location of the original nodes.\n    def clone\n      clone = clone_without_location\n      clone.location = location\n      clone.end_location = end_location\n      clone.doc = doc\n      clone\n    end\n\n    # Returns the doc comment attached to this node. Not every node\n    # supports having doc comments, so by default this returns `nil`.\n    def doc\n    end\n\n    # Attaches a doc comment to this node. Not every node supports\n    # having doc comments, so by default this does nothing and some\n    # subclasses implement this.\n    def doc=(doc)\n    end\n\n    def name_location\n      nil\n    end\n\n    def name_size\n      0\n    end\n\n    def visibility=(visibility : Visibility)\n    end\n\n    def visibility\n      Visibility::Public\n    end\n\n    def nop?\n      self.is_a?(Nop)\n    end\n\n    def true_literal?\n      self.is_a?(BoolLiteral) && self.value\n    end\n\n    def false_literal?\n      self.is_a?(BoolLiteral) && !self.value\n    end\n\n    def self.class_desc : String\n      {{@type.name.split(\"::\").last.id.stringify}}\n    end\n\n    def class_desc\n      self.class.class_desc\n    end\n\n    def pretty_print(pp)\n      pp.text to_s\n    end\n\n    # It yields itself for any node, but `Expressions` yields first node\n    # if it holds only a node.\n    def single_expression\n      single_expression? || self\n    end\n\n    # It yields `nil` always.\n    # (It is overridden by `Expressions` to implement `#single_expression`.)\n    def single_expression?\n      nil\n    end\n  end\n\n  class Nop < ASTNode\n    def clone_without_location\n      Nop.new\n    end\n\n    def_equals_and_hash\n  end\n\n  # A container for one or many expressions.\n  class Expressions < ASTNode\n    enum Keyword\n      None\n      Paren\n      Begin\n    end\n\n    property expressions : Array(ASTNode)\n    property keyword : Keyword = Keyword::None\n\n    def self.from(obj : Nil)\n      Nop.new\n    end\n\n    def self.from(obj : Array)\n      case obj.size\n      when 0\n        Nop.new\n      when 1\n        obj.first\n      else\n        new obj\n      end\n    end\n\n    def self.from(obj : ASTNode)\n      obj\n    end\n\n    # Concatenates two AST nodes into a single `Expressions` node, removing\n    # `Nop`s and merging keyword-less expressions into a single node.\n    #\n    # *x* and *y* may be modified in-place if they are already `Expressions`\n    # nodes.\n    def self.concat!(x : ASTNode, y : ASTNode) : ASTNode\n      return x if y.is_a?(Nop)\n      return y if x.is_a?(Nop)\n\n      if x.is_a?(Expressions) && x.keyword.none?\n        if y.is_a?(Expressions) && y.keyword.none?\n          x.expressions.concat(y.expressions)\n        else\n          x.expressions << y\n        end\n        x.at_end(y.end_location)\n      elsif y.is_a?(Expressions) && y.keyword.none?\n        y.expressions.unshift(x)\n        y.at(x.location)\n      else\n        Expressions.new([x, y] of ASTNode).at(x.location).at_end(y.end_location)\n      end\n    end\n\n    def initialize(@expressions = [] of ASTNode)\n    end\n\n    def empty?\n      @expressions.empty?\n    end\n\n    def [](i)\n      @expressions[i]\n    end\n\n    def last\n      @expressions.last\n    end\n\n    def location\n      @location || @expressions.first?.try &.location\n    end\n\n    def end_location\n      @end_location || @expressions.last?.try &.end_location\n    end\n\n    # It yields first node if this holds only one node, or yields `nil`.\n    def single_expression?\n      return @expressions.first.single_expression if @expressions.size == 1\n\n      nil\n    end\n\n    def accept_children(visitor)\n      @expressions.each &.accept visitor\n    end\n\n    def clone_without_location\n      Expressions.new(@expressions.clone).tap &.keyword = keyword\n    end\n\n    def_equals_and_hash expressions\n  end\n\n  # The nil literal.\n  #\n  #     'nil'\n  #\n  class NilLiteral < ASTNode\n    def clone_without_location\n      NilLiteral.new\n    end\n\n    def_equals_and_hash\n  end\n\n  # A bool literal.\n  #\n  #     'true' | 'false'\n  #\n  class BoolLiteral < ASTNode\n    property value : Bool\n\n    def initialize(@value)\n    end\n\n    def clone_without_location\n      BoolLiteral.new(@value)\n    end\n\n    def_equals_and_hash value\n  end\n\n  # The kind of primitive numbers.\n  enum NumberKind\n    I8\n    I16\n    I32\n    I64\n    I128\n    U8\n    U16\n    U32\n    U64\n    U128\n    F32\n    F64\n\n    def to_s : String\n      super.downcase\n    end\n\n    # TODO: rename to `bit_width`\n    def bytesize\n      case self\n      in .i8?   then 8\n      in .i16?  then 16\n      in .i32?  then 32\n      in .i64?  then 64\n      in .i128? then 128\n      in .u8?   then 8\n      in .u16?  then 16\n      in .u32?  then 32\n      in .u64?  then 64\n      in .u128? then 128\n      in .f32?  then 32\n      in .f64?  then 64\n      end\n    end\n\n    def signed_int?\n      i8? || i16? || i32? || i64? || i128?\n    end\n\n    def unsigned_int?\n      u8? || u16? || u32? || u64? || u128?\n    end\n\n    def float?\n      f32? || f64?\n    end\n\n    def self.from_number(number : Number::Primitive) : self\n      case number\n      in Int8    then I8\n      in Int16   then I16\n      in Int32   then I32\n      in Int64   then I64\n      in Int128  then I128\n      in UInt8   then U8\n      in UInt16  then U16\n      in UInt32  then U32\n      in UInt64  then U64\n      in UInt128 then U128\n      in Float32 then F32\n      in Float64 then F64\n      end\n    end\n\n    def cast(number) : Number::Primitive\n      case self\n      in .i8?   then number.to_i8\n      in .i16?  then number.to_i16\n      in .i32?  then number.to_i32\n      in .i64?  then number.to_i64\n      in .i128? then number.to_i128\n      in .u8?   then number.to_u8\n      in .u16?  then number.to_u16\n      in .u32?  then number.to_u32\n      in .u64?  then number.to_u64\n      in .u128? then number.to_u128\n      in .f32?  then number.to_f32\n      in .f64?  then number.to_f64\n      end\n    end\n  end\n\n  # Any number literal.\n  class NumberLiteral < ASTNode\n    property value : String\n    property kind : NumberKind\n\n    def initialize(@value : String, @kind : NumberKind = :i32)\n    end\n\n    def self.new(value : Number)\n      new(value.to_s, NumberKind.from_number(value))\n    end\n\n    def has_sign?\n      @value[0].in?('+', '-')\n    end\n\n    def integer_value\n      unless kind.signed_int? || kind.unsigned_int?\n        raise \"BUG: called 'integer_value' for non-integer literal\"\n      end\n\n      kind.cast(value)\n    end\n\n    # Returns true if this literal is representable in the *other_type*. Used to\n    # define number literal autocasting.\n    #\n    # TODO: if *other_type* is a `FloatType` then precision loss and overflow\n    # may occur (#11710)\n    def representable_in?(other_type)\n      case {self.type, other_type}\n      when {IntegerType, IntegerType}\n        min, max = other_type.range\n        min <= integer_value <= max\n      when {IntegerType, FloatType}\n        true\n      when {FloatType, FloatType}\n        true\n      else\n        false\n      end\n    end\n\n    def clone_without_location\n      NumberLiteral.new(@value, @kind)\n    end\n\n    def_equals value.to_f64, kind\n    def_hash value, kind\n  end\n\n  # A char literal.\n  #\n  #     \"'\" \\w \"'\"\n  #\n  class CharLiteral < ASTNode\n    property value : Char\n\n    def initialize(@value : Char)\n    end\n\n    def clone_without_location\n      CharLiteral.new(@value)\n    end\n\n    def_equals_and_hash value\n  end\n\n  class StringLiteral < ASTNode\n    property value : String\n\n    def initialize(@value : String)\n    end\n\n    def clone_without_location\n      StringLiteral.new(@value)\n    end\n\n    def_equals_and_hash value\n  end\n\n  class StringInterpolation < ASTNode\n    property expressions : Array(ASTNode)\n\n    # Removed indentation size.\n    # This property is only available when this is created from heredoc.\n    property heredoc_indent : Int32\n\n    def initialize(@expressions : Array(ASTNode), @heredoc_indent = 0)\n    end\n\n    def accept_children(visitor)\n      @expressions.each &.accept visitor\n    end\n\n    def clone_without_location\n      StringInterpolation.new(@expressions.clone)\n    end\n\n    def_equals_and_hash expressions\n  end\n\n  class SymbolLiteral < ASTNode\n    property value : String\n\n    def initialize(@value : String)\n    end\n\n    def clone_without_location\n      SymbolLiteral.new(@value)\n    end\n\n    def_equals_and_hash value\n  end\n\n  # An array literal.\n  #\n  #  '[' [ expression [ ',' expression ]* ] ']'\n  #\n  class ArrayLiteral < ASTNode\n    property elements : Array(ASTNode)\n    property of : ASTNode?\n    property name : ASTNode?\n\n    def initialize(@elements = [] of ASTNode, @of = nil, @name = nil)\n    end\n\n    def self.map(values, of = nil, &)\n      new(values.map { |value| (yield value).as(ASTNode) }, of: of)\n    end\n\n    def self.map_with_index(values, &)\n      new(values.map_with_index { |value, idx| (yield value, idx).as(ASTNode) }, of: nil)\n    end\n\n    def accept_children(visitor)\n      @name.try &.accept visitor\n      elements.each &.accept visitor\n      @of.try &.accept visitor\n    end\n\n    def clone_without_location\n      ArrayLiteral.new(@elements.clone, @of.clone, @name.clone)\n    end\n\n    def_equals_and_hash @elements, @of, @name\n  end\n\n  class HashLiteral < ASTNode\n    property entries : Array(Entry)\n    property of : Entry?\n    property name : ASTNode?\n\n    def initialize(@entries = [] of Entry, @of = nil, @name = nil)\n    end\n\n    def accept_children(visitor)\n      @name.try &.accept visitor\n      @entries.each do |entry|\n        entry.key.accept visitor\n        entry.value.accept visitor\n      end\n      if of = @of\n        of.key.accept visitor\n        of.value.accept visitor\n      end\n    end\n\n    def clone_without_location\n      HashLiteral.new(@entries.clone, @of.clone, @name.clone)\n    end\n\n    def_equals_and_hash @entries, @of, @name\n\n    record Entry, key : ASTNode, value : ASTNode\n  end\n\n  class NamedTupleLiteral < ASTNode\n    property entries : Array(Entry)\n\n    def initialize(@entries = [] of Entry)\n    end\n\n    def accept_children(visitor)\n      @entries.each do |entry|\n        entry.value.accept visitor\n      end\n    end\n\n    def clone_without_location\n      NamedTupleLiteral.new(@entries.clone)\n    end\n\n    def_equals_and_hash @entries\n\n    record Entry, key : String, value : ASTNode\n  end\n\n  class RangeLiteral < ASTNode\n    property from : ASTNode\n    property to : ASTNode\n    property? exclusive : Bool\n\n    def initialize(@from, @to, @exclusive)\n    end\n\n    def accept_children(visitor)\n      @from.accept visitor\n      @to.accept visitor\n    end\n\n    def clone_without_location\n      RangeLiteral.new(@from.clone, @to.clone, @exclusive.clone)\n    end\n\n    def_equals_and_hash @from, @to, @exclusive\n  end\n\n  class RegexLiteral < ASTNode\n    property value : ASTNode\n    property options : Regex::CompileOptions\n\n    def initialize(@value, @options = Regex::CompileOptions::None)\n    end\n\n    def accept_children(visitor)\n      @value.accept visitor\n    end\n\n    def clone_without_location\n      RegexLiteral.new(@value.clone, @options)\n    end\n\n    def_equals_and_hash @value, @options\n  end\n\n  class TupleLiteral < ASTNode\n    property elements : Array(ASTNode)\n\n    def initialize(@elements)\n    end\n\n    def self.map(values, &)\n      new(values.map { |value| (yield value).as(ASTNode) })\n    end\n\n    def self.map_with_index(values, &)\n      new(values.map_with_index { |value, idx| (yield value, idx).as(ASTNode) })\n    end\n\n    def accept_children(visitor)\n      elements.each &.accept visitor\n    end\n\n    def clone_without_location\n      TupleLiteral.new(elements.clone)\n    end\n\n    def_equals_and_hash elements\n  end\n\n  module SpecialVar\n    def special_var?\n      @name.starts_with? '$'\n    end\n  end\n\n  # A local variable or block argument.\n  class Var < ASTNode\n    include SpecialVar\n\n    property name : String\n    property doc : String?\n\n    def initialize(@name : String)\n    end\n\n    def name_size\n      name.size\n    end\n\n    def clone_without_location\n      Var.new(@name)\n    end\n\n    def_equals name\n    def_hash name\n  end\n\n  # A code block.\n  #\n  #     'do' [ '|' arg [ ',' arg ]* '|' ]\n  #       body\n  #     'end'\n  #   |\n  #     '{' [ '|' arg [ ',' arg ]* '|' ] body '}'\n  #\n  class Block < ASTNode\n    property args : Array(Var)\n    property body : ASTNode\n    property call : Call?\n    property splat_index : Int32?\n\n    # When a block argument unpacks, the corresponding Var will\n    # have an empty name, and `unpacks` will have the unpacked\n    # Expressions in that index.\n    property unpacks : Hash(Int32, Expressions)?\n\n    def initialize(@args = [] of Var, body = nil, @splat_index = nil, @unpacks = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @args.each &.accept visitor\n      @body.accept visitor\n      @unpacks.try &.each_value &.accept visitor\n    end\n\n    def clone_without_location\n      Block.new(@args.clone, @body.clone, @splat_index, @unpacks.clone)\n    end\n\n    def has_any_args?\n      args.present?\n    end\n\n    def_equals_and_hash args, body, splat_index, unpacks\n  end\n\n  # A method call.\n  #\n  #     [ obj '.' ] name '(' ')' [ block ]\n  #   |\n  #     [ obj '.' ] name '(' arg [ ',' arg ]* ')' [ block ]\n  #   |\n  #     [ obj '.' ] name arg [ ',' arg ]* [ block ]\n  #   |\n  #     arg name arg\n  #\n  # The last syntax is for infix operators, and name will be\n  # the symbol of that operator instead of a string.\n  #\n  class Call < ASTNode\n    property obj : ASTNode?\n    property name : String\n    property args : Array(ASTNode)\n    property block : Block?\n    property block_arg : ASTNode?\n    property named_args : Array(NamedArgument)?\n    property name_location : Location?\n    @name_size = -1\n    property doc : String?\n    property visibility = Visibility::Public\n    property? global : Bool\n    property? expansion = false\n    property? args_in_brackets = false\n    property? has_parentheses = false\n\n    def initialize(@obj : ASTNode?, @name : String, @args : Array(ASTNode) = [] of ASTNode, @block = nil, @block_arg = nil, @named_args = nil, @global : Bool = false)\n      if block = @block\n        block.call = self\n      end\n    end\n\n    def self.new(obj : ASTNode?, name : String, *args : ASTNode, block : Block? = nil, block_arg : ASTNode? = nil, named_args : Array(NamedArgument)? = nil, global : Bool = false)\n      {% if compare_versions(Crystal::VERSION, \"1.5.0\") > 0 %}\n        new obj, name, [*args] of ASTNode, block: block, block_arg: block_arg, named_args: named_args, global: global\n      {% else %}\n        new obj, name, args.to_a(&.as(ASTNode)), block: block, block_arg: block_arg, named_args: named_args, global: global\n      {% end %}\n    end\n\n    def self.new(name : String, args : Array(ASTNode) = [] of ASTNode, block : Block? = nil, block_arg : ASTNode? = nil, named_args : Array(NamedArgument)? = nil, global : Bool = false)\n      new(nil, name, args, block: block, block_arg: block_arg, named_args: named_args, global: global)\n    end\n\n    def self.new(name : String, *args : ASTNode, block : Block? = nil, block_arg : ASTNode? = nil, named_args : Array(NamedArgument)? = nil, global : Bool = false)\n      new(nil, name, *args, block: block, block_arg: block_arg, named_args: named_args, global: global)\n    end\n\n    def self.global(name, *args : ASTNode)\n      new nil, name, *args, global: true\n    end\n\n    def name_size\n      if @name_size == -1\n        @name_size = name.to_s.ends_with?('=') || name.to_s.ends_with?('@') ? name.size - 1 : name.size\n      end\n      @name_size\n    end\n\n    setter name_size\n\n    def accept_children(visitor)\n      @obj.try &.accept visitor\n      @args.each &.accept visitor\n      @named_args.try &.each &.accept visitor\n      @block_arg.try &.accept visitor\n      @block.try &.accept visitor\n    end\n\n    def clone_without_location\n      clone = Call.new(@obj.clone, @name, @args.clone, @block.clone, @block_arg.clone, @named_args.clone, @global)\n      clone.name_location = name_location\n      clone.has_parentheses = has_parentheses?\n      clone.name_size = name_size\n      clone.expansion = expansion?\n      clone\n    end\n\n    def name_end_location\n      loc = @name_location\n      return unless loc\n\n      Location.new(loc.filename, loc.line_number, loc.column_number + name_size - 1)\n    end\n\n    # Returns `true` if this call has any arguments.\n    #\n    # Does not consider a block, only block argument.\n    # `foo {}` would be `false`, but `foo(&x)` would be `true`.\n    def has_any_args?\n      args.present? || !named_args.nil? || !block_arg.nil?\n    end\n\n    def_equals_and_hash obj, name, args, block, block_arg, named_args, global?\n  end\n\n  class NamedArgument < ASTNode\n    property name : String\n    property value : ASTNode\n\n    def initialize(@name : String, @value : ASTNode)\n    end\n\n    def accept_children(visitor)\n      @value.accept visitor\n    end\n\n    def clone_without_location\n      NamedArgument.new(name, value.clone)\n    end\n\n    def end_location\n      @end_location || value.end_location\n    end\n\n    def_equals_and_hash name, value\n  end\n\n  # An if expression.\n  #\n  #     'if' cond\n  #       then\n  #     [\n  #     'else'\n  #       else\n  #     ]\n  #     'end'\n  #\n  # An if elsif end is parsed as an If whose\n  # else is another If.\n  class If < ASTNode\n    property cond : ASTNode\n    property then : ASTNode\n    property else : ASTNode\n    property? ternary : Bool\n\n    # The location of the `else` keyword if present.\n    property else_location : Location?\n\n    def initialize(@cond, a_then = nil, a_else = nil, @ternary = false)\n      @then = Expressions.from a_then\n      @else = Expressions.from a_else\n    end\n\n    def accept_children(visitor)\n      @cond.accept visitor\n      @then.accept visitor\n      @else.accept visitor\n    end\n\n    def clone_without_location\n      If.new(@cond.clone, @then.clone, @else.clone, @ternary)\n    end\n\n    def_equals_and_hash @cond, @then, @else\n  end\n\n  class Unless < ASTNode\n    property cond : ASTNode\n    property then : ASTNode\n    property else : ASTNode\n\n    # The location of the `else` keyword if present.\n    property else_location : Location?\n\n    def initialize(@cond, a_then = nil, a_else = nil)\n      @then = Expressions.from a_then\n      @else = Expressions.from a_else\n    end\n\n    def accept_children(visitor)\n      @cond.accept visitor\n      @then.accept visitor\n      @else.accept visitor\n    end\n\n    def clone_without_location\n      Unless.new(@cond.clone, @then.clone, @else.clone)\n    end\n\n    def_equals_and_hash @cond, @then, @else\n  end\n\n  # Assign expression.\n  #\n  #     target '=' value\n  #\n  class Assign < ASTNode\n    property target : ASTNode\n    property value : ASTNode\n    property doc : String?\n\n    def initialize(@target, @value)\n    end\n\n    def visibility=(visibility)\n      target.visibility = visibility\n    end\n\n    def accept_children(visitor)\n      @target.accept visitor\n      @value.accept visitor\n    end\n\n    def end_location\n      @end_location || value.end_location\n    end\n\n    def clone_without_location\n      Assign.new(@target.clone, @value.clone)\n    end\n\n    def_equals_and_hash @target, @value\n  end\n\n  # Operator assign expression.\n  #\n  #     target op'=' value\n  #\n  # For example if `op` is `+` then the above is:\n  #\n  #     target '+=' value\n  class OpAssign < ASTNode\n    property target : ASTNode\n    property op : String\n    property value : ASTNode\n    property name_location : Location?\n\n    def initialize(@target, @op, @value)\n    end\n\n    def accept_children(visitor)\n      @target.accept visitor\n      @value.accept visitor\n    end\n\n    def end_location\n      @end_location || value.end_location\n    end\n\n    def clone_without_location\n      OpAssign.new(@target.clone, @op, @value.clone)\n    end\n\n    def_equals_and_hash @target, @op, @value\n  end\n\n  # Assign expression.\n  #\n  #     target [',' target]+ '=' value [',' value]*\n  #\n  class MultiAssign < ASTNode\n    property targets : Array(ASTNode)\n    property values : Array(ASTNode)\n\n    def initialize(@targets, @values)\n    end\n\n    def accept_children(visitor)\n      @targets.each &.accept visitor\n      @values.each &.accept visitor\n    end\n\n    def end_location\n      @end_location || @values.last.end_location\n    end\n\n    def clone_without_location\n      MultiAssign.new(@targets.clone, @values.clone)\n    end\n\n    def_equals_and_hash @targets, @values\n  end\n\n  # An instance variable.\n  class InstanceVar < ASTNode\n    property name : String\n\n    def initialize(@name)\n    end\n\n    def name_size\n      name.size\n    end\n\n    def clone_without_location\n      InstanceVar.new(@name)\n    end\n\n    def_equals_and_hash name\n  end\n\n  class ReadInstanceVar < ASTNode\n    property obj : ASTNode\n    property name : String\n\n    def initialize(@obj, @name)\n    end\n\n    def accept_children(visitor)\n      @obj.accept visitor\n    end\n\n    def clone_without_location\n      ReadInstanceVar.new(@obj.clone, @name)\n    end\n\n    def_equals_and_hash @obj, @name\n  end\n\n  class ClassVar < ASTNode\n    property name : String\n\n    def initialize(@name)\n    end\n\n    def clone_without_location\n      ClassVar.new(@name)\n    end\n\n    def_equals_and_hash name\n  end\n\n  # A global variable.\n  class Global < ASTNode\n    property name : String\n\n    def initialize(@name)\n    end\n\n    def name_size\n      name.size\n    end\n\n    def clone_without_location\n      Global.new(@name)\n    end\n\n    def_equals_and_hash name\n  end\n\n  abstract class BinaryOp < ASTNode\n    property left : ASTNode\n    property right : ASTNode\n\n    def initialize(@left, @right)\n    end\n\n    def accept_children(visitor)\n      @left.accept visitor\n      @right.accept visitor\n    end\n\n    def end_location\n      @end_location || @right.end_location\n    end\n\n    def_equals_and_hash left, right\n  end\n\n  # Expressions and.\n  #\n  #     expression '&&' expression\n  #\n  class And < BinaryOp\n    def clone_without_location\n      And.new(@left.clone, @right.clone)\n    end\n  end\n\n  # Expressions or.\n  #\n  #     expression '||' expression\n  #\n  class Or < BinaryOp\n    def clone_without_location\n      Or.new(@left.clone, @right.clone)\n    end\n  end\n\n  # A def argument.\n  class Arg < ASTNode\n    include SpecialVar\n\n    # The internal name\n    property name : String\n    property external_name : String\n    property default_value : ASTNode?\n    property restriction : ASTNode?\n    property doc : String?\n    property parsed_annotations : Array(Annotation)?\n\n    def initialize(@name : String, @default_value : ASTNode? = nil, @restriction : ASTNode? = nil, external_name : String? = nil, @parsed_annotations : Array(Annotation)? = nil)\n      @external_name = external_name || @name\n    end\n\n    def accept_children(visitor)\n      @default_value.try &.accept visitor\n      @restriction.try &.accept visitor\n    end\n\n    def name_size\n      name.size\n    end\n\n    def clone_without_location\n      Arg.new @name, @default_value.clone, @restriction.clone, @external_name.clone, @parsed_annotations.clone\n    end\n\n    def_equals_and_hash name, default_value, restriction, external_name, parsed_annotations\n  end\n\n  # The Proc notation in the type grammar:\n  #\n  #    input1, input2, ..., inputN -> output\n  class ProcNotation < ASTNode\n    property inputs : Array(ASTNode)?\n    property output : ASTNode?\n\n    def initialize(@inputs = nil, @output = nil)\n    end\n\n    def accept_children(visitor)\n      @inputs.try &.each &.accept visitor\n      @output.try &.accept visitor\n    end\n\n    def clone_without_location\n      ProcNotation.new(@inputs.clone, @output.clone)\n    end\n\n    def_equals_and_hash inputs, output\n  end\n\n  # A method definition.\n  #\n  #     'def' [ receiver '.' ] name\n  #       body\n  #     'end'\n  #   |\n  #     'def' [ receiver '.' ] name '(' [ arg [ ',' arg ]* ] ')'\n  #       body\n  #     'end'\n  #\n  class Def < ASTNode\n    property free_vars : Array(String)?\n    property receiver : ASTNode?\n    property name : String\n    property args : Array(Arg)\n    property double_splat : Arg?\n    property body : ASTNode\n    property block_arg : Arg?\n    property return_type : ASTNode?\n    # Number of block arguments accepted by this method.\n    # `nil` if it does not receive a block.\n    property block_arity : Int32?\n    property name_location : Location?\n    property splat_index : Int32?\n    property doc : String?\n    property visibility = Visibility::Public\n\n    property? macro_def : Bool\n    property? calls_super = false\n    property? calls_initialize = false\n    property? calls_previous_def = false\n    property? uses_block_arg = false\n    property? assigns_special_var = false\n    property? abstract : Bool\n\n    def initialize(@name, @args = [] of Arg, body = nil, @receiver = nil, @block_arg = nil, @return_type = nil, @macro_def = false, @block_arity = nil, @abstract = false, @splat_index = nil, @double_splat = nil, @free_vars = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @receiver.try &.accept visitor\n      @args.each &.accept visitor\n      @double_splat.try &.accept visitor\n      @block_arg.try &.accept visitor\n      @return_type.try &.accept visitor\n      @body.accept visitor\n    end\n\n    def name_size\n      name.size\n    end\n\n    def clone_without_location\n      a_def = Def.new(@name, @args.clone, @body.clone, @receiver.clone, @block_arg.clone, @return_type.clone, @macro_def, @block_arity, @abstract, @splat_index, @double_splat.clone, @free_vars)\n      a_def.calls_super = calls_super?\n      a_def.calls_initialize = calls_initialize?\n      a_def.calls_previous_def = calls_previous_def?\n      a_def.uses_block_arg = uses_block_arg?\n      a_def.assigns_special_var = assigns_special_var?\n      a_def.name_location = name_location\n      a_def.visibility = visibility\n      a_def\n    end\n\n    def_equals_and_hash @name, @args, @body, @receiver, @block_arg, @return_type, @macro_def, @block_arity, @abstract, @splat_index, @double_splat\n\n    def autogenerated?\n      location == body.location\n    end\n\n    def has_any_args?\n      args.present? || !block_arity.nil?\n    end\n  end\n\n  class Macro < ASTNode\n    property name : String\n    property args : Array(Arg)\n    property body : ASTNode\n    property double_splat : Arg?\n    property block_arg : Arg?\n    property name_location : Location?\n    property splat_index : Int32?\n    property doc : String?\n    property visibility = Visibility::Public\n\n    def initialize(@name, @args = [] of Arg, @body = Nop.new, @block_arg = nil, @splat_index = nil, @double_splat = nil)\n    end\n\n    def accept_children(visitor)\n      @args.each &.accept visitor\n      @body.accept visitor\n      @double_splat.try &.accept visitor\n      @block_arg.try &.accept visitor\n    end\n\n    def name_size\n      name.size\n    end\n\n    def clone_without_location\n      m = Macro.new(@name, @args.clone, @body.clone, @block_arg.clone, @splat_index, @double_splat.clone)\n      m.name_location = name_location\n      m\n    end\n\n    def has_any_args?\n      args.present? || !block_arg.nil?\n    end\n\n    def_equals_and_hash @name, @args, @body, @block_arg, @splat_index, @double_splat\n  end\n\n  abstract class UnaryExpression < ASTNode\n    property exp : ASTNode\n\n    def initialize(@exp)\n    end\n\n    def accept_children(visitor)\n      @exp.accept visitor\n    end\n\n    def end_location\n      @end_location || @exp.end_location\n    end\n\n    def_equals_and_hash exp\n  end\n\n  # Used only for flags\n  class Not < UnaryExpression\n    def clone_without_location\n      Not.new(@exp.clone)\n    end\n  end\n\n  class PointerOf < UnaryExpression\n    def clone_without_location\n      PointerOf.new(@exp.clone)\n    end\n  end\n\n  class SizeOf < UnaryExpression\n    def clone_without_location\n      SizeOf.new(@exp.clone)\n    end\n  end\n\n  class InstanceSizeOf < UnaryExpression\n    def clone_without_location\n      InstanceSizeOf.new(@exp.clone)\n    end\n  end\n\n  class AlignOf < UnaryExpression\n    def clone_without_location\n      AlignOf.new(@exp.clone)\n    end\n  end\n\n  class InstanceAlignOf < UnaryExpression\n    def clone_without_location\n      InstanceAlignOf.new(@exp.clone)\n    end\n  end\n\n  class Out < UnaryExpression\n    def clone_without_location\n      Out.new(@exp.clone)\n    end\n  end\n\n  class OffsetOf < ASTNode\n    property offsetof_type : ASTNode\n    property offset : ASTNode\n\n    def initialize(@offsetof_type, @offset)\n    end\n\n    def accept_children(visitor)\n      @offsetof_type.accept visitor\n      @offset.accept visitor\n    end\n\n    def clone_without_location\n      OffsetOf.new(@offsetof_type.clone, @offset.clone)\n    end\n\n    def_equals_and_hash @offsetof_type, @offset\n  end\n\n  class VisibilityModifier < ASTNode\n    property modifier : Visibility\n    property exp : ASTNode\n    property doc : String?\n\n    def initialize(@modifier : Visibility, @exp)\n    end\n\n    def accept_children(visitor)\n      @exp.accept visitor\n    end\n\n    def clone_without_location\n      VisibilityModifier.new(@modifier, @exp.clone)\n    end\n\n    def end_location\n      @end_location || @exp.end_location\n    end\n\n    def_equals_and_hash modifier, exp\n  end\n\n  class IsA < ASTNode\n    property obj : ASTNode\n    property const : ASTNode\n    property? nil_check : Bool\n\n    def initialize(@obj, @const, @nil_check = false)\n    end\n\n    def accept_children(visitor)\n      @obj.accept visitor\n      @const.accept visitor\n    end\n\n    def clone_without_location\n      IsA.new(@obj.clone, @const.clone, @nil_check)\n    end\n\n    def_equals_and_hash @obj, @const, @nil_check\n  end\n\n  class RespondsTo < ASTNode\n    property obj : ASTNode\n    property name : String\n\n    def initialize(@obj, @name)\n    end\n\n    def accept_children(visitor)\n      obj.accept visitor\n    end\n\n    def clone_without_location\n      RespondsTo.new(@obj.clone, @name)\n    end\n\n    def_equals_and_hash @obj, @name\n  end\n\n  class Require < ASTNode\n    property string : String\n\n    def initialize(@string)\n    end\n\n    def clone_without_location\n      Require.new(@string)\n    end\n\n    def_equals_and_hash string\n  end\n\n  class When < ASTNode\n    property conds : Array(ASTNode)\n    property body : ASTNode\n    property? exhaustive : Bool\n\n    def initialize(@conds : Array(ASTNode), body : ASTNode? = nil, @exhaustive = false)\n      @body = Expressions.from body\n    end\n\n    def self.new(cond : ASTNode, body : ASTNode? = nil, exhaustive = false)\n      new([cond] of ASTNode, body, exhaustive)\n    end\n\n    def accept_children(visitor)\n      @conds.each &.accept visitor\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      When.new(@conds.clone, @body.clone, @exhaustive)\n    end\n\n    def_equals_and_hash @conds, @body, @exhaustive\n  end\n\n  class Case < ASTNode\n    property cond : ASTNode?\n    property whens : Array(When)\n    property else : ASTNode?\n    property? exhaustive : Bool\n\n    def initialize(@cond : ASTNode?, @whens : Array(When), @else : ASTNode?, @exhaustive : Bool)\n      @whens.each do |wh|\n        wh.exhaustive = self.exhaustive?\n      end\n    end\n\n    def accept_children(visitor)\n      @cond.try &.accept visitor\n      @whens.each &.accept visitor\n      @else.try &.accept visitor\n    end\n\n    def clone_without_location\n      Case.new(@cond.clone, @whens.clone, @else.clone, @exhaustive)\n    end\n\n    def_equals_and_hash @exhaustive, @cond, @whens, @else\n  end\n\n  class Select < ASTNode\n    property whens : Array(When)\n    property else : ASTNode?\n\n    def initialize(@whens, @else = nil)\n    end\n\n    def accept_children(visitor)\n      @whens.each &.accept visitor\n      @else.try &.accept visitor\n    end\n\n    def clone_without_location\n      Select.new(@whens.clone, @else.clone)\n    end\n\n    def_equals_and_hash @whens, @else\n  end\n\n  # Node that represents an implicit obj in:\n  #\n  #     case foo\n  #     when .bar? # this is a call with an implicit obj\n  #     end\n  class ImplicitObj < ASTNode\n    def ==(other : self)\n      true\n    end\n\n    def clone_without_location\n      self\n    end\n\n    def_hash\n  end\n\n  # A qualified identifier.\n  #\n  #     const [ '::' const ]*\n  #\n  class Path < ASTNode\n    property names : Array(String)\n    property? global : Bool\n    property visibility = Visibility::Public\n\n    def initialize(@names : Array, @global = false)\n    end\n\n    def self.new(name : String, global = false)\n      new [name], global\n    end\n\n    def self.new(name1 : String, name2 : String, global = false)\n      new [name1, name2], global\n    end\n\n    def self.global(names)\n      new names, true\n    end\n\n    def name_size\n      names.sum(&.size) + (names.size + (global? ? 0 : -1)) * 2\n    end\n\n    # Returns true if this path has a single component\n    # with the given name\n    def single?(name)\n      names.size == 1 && names.first == name\n    end\n\n    # Returns this path's name if it has only one part and is not global\n    def single_name?\n      names.first if names.size == 1 && !global?\n    end\n\n    def clone_without_location\n      ident = Path.new(@names.clone, @global)\n      ident\n    end\n\n    def_equals_and_hash @names, @global\n  end\n\n  # Class definition:\n  #\n  #     'class' name [ '<' superclass ]\n  #       body\n  #     'end'\n  #\n  class ClassDef < ASTNode\n    property name : Path\n    property body : ASTNode\n    property superclass : ASTNode?\n    property type_vars : Array(String)?\n    property name_location : Location?\n    property doc : String?\n    property splat_index : Int32?\n    property? abstract : Bool\n    property? struct : Bool\n    property visibility = Visibility::Public\n\n    def initialize(@name, body = nil, @superclass = nil, @type_vars = nil, @abstract = false, @struct = false, @splat_index = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @superclass.try &.accept visitor\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      clone = ClassDef.new(@name, @body.clone, @superclass.clone, @type_vars.clone, @abstract, @struct, @splat_index)\n      clone.name_location = name_location\n      clone\n    end\n\n    def_equals_and_hash @name, @body, @superclass, @type_vars, @abstract, @struct, @splat_index\n  end\n\n  # Module definition:\n  #\n  #     'module' name\n  #       body\n  #     'end'\n  #\n  class ModuleDef < ASTNode\n    property name : Path\n    property body : ASTNode\n    property type_vars : Array(String)?\n    property splat_index : Int32?\n    property name_location : Location?\n    property doc : String?\n    property visibility = Visibility::Public\n\n    def initialize(@name, body = nil, @type_vars = nil, @splat_index = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      clone = ModuleDef.new(@name, @body.clone, @type_vars.clone, @splat_index)\n      clone.name_location = name_location\n      clone\n    end\n\n    def_equals_and_hash @name, @body, @type_vars, @splat_index\n  end\n\n  # Annotation definition:\n  #\n  #     'annotation' name\n  #     'end'\n  #\n  class AnnotationDef < ASTNode\n    property name : Path\n    property doc : String?\n    property name_location : Location?\n\n    def initialize(@name)\n    end\n\n    def accept_children(visitor)\n    end\n\n    def clone_without_location\n      clone = AnnotationDef.new(@name)\n      clone.name_location = name_location\n      clone\n    end\n\n    def_equals_and_hash @name\n  end\n\n  # While expression.\n  #\n  #     'while' cond\n  #       body\n  #     'end'\n  #\n  class While < ASTNode\n    property cond : ASTNode\n    property body : ASTNode\n\n    def initialize(@cond, body = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @cond.accept visitor\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      While.new(@cond.clone, @body.clone)\n    end\n\n    def_equals_and_hash @cond, @body\n  end\n\n  # Until expression.\n  #\n  #     'until' cond\n  #       body\n  #     'end'\n  #\n  class Until < ASTNode\n    property cond : ASTNode\n    property body : ASTNode\n\n    def initialize(@cond, body = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @cond.accept visitor\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      Until.new(@cond.clone, @body.clone)\n    end\n\n    def_equals_and_hash @cond, @body\n  end\n\n  class Generic < ASTNode\n    # Usually a Path, but can also be a TypeNode in the case of a\n    # custom array/hash-like literal.\n    property name : ASTNode\n    property type_vars : Array(ASTNode)\n    property named_args : Array(NamedArgument)?\n\n    property suffix : Suffix\n\n    enum Suffix\n      None\n      Question # T?\n      Asterisk # T*\n      Bracket  # T[N]\n    end\n\n    def initialize(@name, @type_vars : Array, @named_args = nil, @suffix = Suffix::None)\n    end\n\n    def self.new(name, type_var : ASTNode)\n      new name, [type_var] of ASTNode\n    end\n\n    def accept_children(visitor)\n      @name.accept visitor\n      @type_vars.each &.accept visitor\n      @named_args.try &.each &.accept visitor\n    end\n\n    def clone_without_location\n      generic = Generic.new(@name.clone, @type_vars.clone, @named_args.clone, @suffix)\n      generic\n    end\n\n    def_equals_and_hash @name, @type_vars, @named_args\n  end\n\n  class TypeDeclaration < ASTNode\n    property doc : String?\n    property var : ASTNode\n    property declared_type : ASTNode\n    property value : ASTNode?\n\n    def initialize(@var, @declared_type, @value = nil)\n    end\n\n    def accept_children(visitor)\n      var.accept visitor\n      declared_type.accept visitor\n      value.try &.accept visitor\n    end\n\n    def name_size\n      var = @var\n      case var\n      when Var\n        var.name.size\n      when InstanceVar\n        var.name.size\n      when ClassVar\n        var.name.size\n      when Global\n        var.name.size\n      else\n        raise \"can't happen\"\n      end\n    end\n\n    def clone_without_location\n      TypeDeclaration.new(@var.clone, @declared_type.clone, @value.clone)\n    end\n\n    def_equals_and_hash @var, @declared_type, @value\n  end\n\n  class UninitializedVar < ASTNode\n    property var : ASTNode\n    property declared_type : ASTNode\n\n    def initialize(@var, @declared_type)\n    end\n\n    def accept_children(visitor)\n      var.accept visitor\n      declared_type.accept visitor\n    end\n\n    def name_size\n      var = @var\n      case var\n      when Var\n        var.name.size\n      when InstanceVar\n        var.name.size\n      when ClassVar\n        var.name.size\n      else\n        raise \"can't happen\"\n      end\n    end\n\n    def clone_without_location\n      UninitializedVar.new(@var.clone, @declared_type.clone)\n    end\n\n    def_equals_and_hash @var, @declared_type\n  end\n\n  class Rescue < ASTNode\n    property body : ASTNode\n    property types : Array(ASTNode)?\n    property name : String?\n\n    def initialize(body = nil, @types = nil, @name = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @body.accept visitor\n      @types.try &.each &.accept visitor\n    end\n\n    def clone_without_location\n      Rescue.new(@body.clone, @types.clone, @name)\n    end\n\n    def_equals_and_hash @body, @types, @name\n  end\n\n  class ExceptionHandler < ASTNode\n    property body : ASTNode\n    property rescues : Array(Rescue)?\n    property else : ASTNode?\n    property ensure : ASTNode?\n    property implicit = false\n    property suffix = false\n\n    # The location of the `else` keyword if present.\n    property else_location : Location?\n\n    # The location of the `ensure` keyword if present.\n    property ensure_location : Location?\n\n    def initialize(body = nil, @rescues = nil, @else = nil, @ensure = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @body.accept visitor\n      @rescues.try &.each &.accept visitor\n      @else.try &.accept visitor\n      @ensure.try &.accept visitor\n    end\n\n    def clone_without_location\n      ex = ExceptionHandler.new(@body.clone, @rescues.clone, @else.clone, @ensure.clone)\n      ex.implicit = implicit\n      ex.suffix = suffix\n      ex\n    end\n\n    def_equals_and_hash @body, @rescues, @else, @ensure\n  end\n\n  class ProcLiteral < ASTNode\n    property def : Def\n\n    def initialize(@def = Def.new(\"->\"))\n    end\n\n    def accept_children(visitor)\n      @def.accept visitor\n    end\n\n    def clone_without_location\n      ProcLiteral.new(@def.clone)\n    end\n\n    def_equals_and_hash @def\n  end\n\n  class ProcPointer < ASTNode\n    property obj : ASTNode?\n    property name : String\n    property args : Array(ASTNode)\n    property? global : Bool\n\n    def initialize(@obj, @name, @args = [] of ASTNode, @global = false)\n    end\n\n    def accept_children(visitor)\n      @obj.try &.accept visitor\n      @args.each &.accept visitor\n    end\n\n    def clone_without_location\n      ProcPointer.new(@obj.clone, @name, @args.clone, @global)\n    end\n\n    def_equals_and_hash @obj, @name, @args, @global\n\n    def has_any_args?\n      args.present?\n    end\n  end\n\n  class Union < ASTNode\n    property types : Array(ASTNode)\n\n    def initialize(@types)\n    end\n\n    def accept_children(visitor)\n      @types.each &.accept visitor\n    end\n\n    def clone_without_location\n      Union.new(@types.clone)\n    end\n\n    def_equals_and_hash types\n  end\n\n  class Self < ASTNode\n    def ==(other : self)\n      true\n    end\n\n    def clone_without_location\n      Self.new\n    end\n\n    def_hash\n  end\n\n  abstract class ControlExpression < ASTNode\n    property exp : ASTNode?\n\n    def initialize(@exp : ASTNode? = nil)\n    end\n\n    def accept_children(visitor)\n      @exp.try &.accept visitor\n    end\n\n    def end_location\n      @end_location || @exp.try(&.end_location)\n    end\n\n    def_equals_and_hash exp\n  end\n\n  class Return < ControlExpression\n    def clone_without_location\n      Return.new(@exp.clone)\n    end\n  end\n\n  class Break < ControlExpression\n    def clone_without_location\n      Break.new(@exp.clone)\n    end\n  end\n\n  class Next < ControlExpression\n    def clone_without_location\n      Next.new(@exp.clone)\n    end\n  end\n\n  class Yield < ASTNode\n    property exps : Array(ASTNode)\n    property scope : ASTNode?\n    property? has_parentheses = false\n\n    def initialize(@exps = [] of ASTNode, @scope = nil, @has_parentheses = false)\n    end\n\n    def accept_children(visitor)\n      @scope.try &.accept visitor\n      @exps.each &.accept visitor\n    end\n\n    def clone_without_location\n      Yield.new(@exps.clone, @scope.clone, @has_parentheses)\n    end\n\n    def end_location\n      @end_location || @exps.last?.try(&.end_location)\n    end\n\n    def_equals_and_hash @exps, @scope, @has_parentheses\n  end\n\n  class Include < ASTNode\n    property name : ASTNode\n\n    def initialize(@name)\n    end\n\n    def accept_children(visitor)\n      @name.accept visitor\n    end\n\n    def clone_without_location\n      Include.new(@name)\n    end\n\n    def end_location\n      @end_location || @name.end_location\n    end\n\n    def_equals_and_hash name\n  end\n\n  class Extend < ASTNode\n    property name : ASTNode\n\n    def initialize(@name)\n    end\n\n    def accept_children(visitor)\n      @name.accept visitor\n    end\n\n    def clone_without_location\n      Extend.new(@name)\n    end\n\n    def end_location\n      @end_location || @name.end_location\n    end\n\n    def_equals_and_hash name\n  end\n\n  class LibDef < ASTNode\n    property name : Path\n    property doc : String?\n    property body : ASTNode\n    property name_location : Location?\n    property visibility = Visibility::Public\n\n    def initialize(@name, body = nil)\n      @body = Expressions.from body\n    end\n\n    def accept_children(visitor)\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      clone = LibDef.new(@name, @body.clone)\n      clone.name_location = name_location\n      clone\n    end\n\n    def_equals_and_hash @name, @body\n  end\n\n  class FunDef < ASTNode\n    property name : String\n    property args : Array(Arg)\n    property return_type : ASTNode?\n    property body : ASTNode?\n    property real_name : String\n    property doc : String?\n    property? varargs : Bool\n    property name_location : Location?\n\n    def initialize(@name, @args = [] of Arg, @return_type = nil, @varargs = false, @body = nil, @real_name = name)\n    end\n\n    def accept_children(visitor)\n      @args.each &.accept visitor\n      @return_type.try &.accept visitor\n      @body.try &.accept visitor\n    end\n\n    def clone_without_location\n      clone = FunDef.new(@name, @args.clone, @return_type.clone, @varargs, @body.clone, @real_name)\n      clone.name_location = name_location\n      clone\n    end\n\n    def name_size\n      @name.size\n    end\n\n    def has_any_args?\n      args.present? || varargs\n    end\n\n    def_equals_and_hash @name, @args, @return_type, @varargs, @body, @real_name\n  end\n\n  class TypeDef < ASTNode\n    property name : String\n    property doc : String?\n    property type_spec : ASTNode\n    property name_location : Location?\n\n    def initialize(@name, @type_spec)\n    end\n\n    def accept_children(visitor)\n      @type_spec.accept visitor\n    end\n\n    def clone_without_location\n      clone = TypeDef.new(@name, @type_spec.clone)\n      clone.name_location = name_location\n      clone\n    end\n\n    def_equals_and_hash @name, @type_spec\n  end\n\n  # A c struct/union definition inside a lib declaration\n  class CStructOrUnionDef < ASTNode\n    property name : String\n    property doc : String?\n    property body : ASTNode\n    property? union : Bool\n\n    def initialize(@name, body = nil, @union = false)\n      @body = Expressions.from(body)\n    end\n\n    def accept_children(visitor)\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      CStructOrUnionDef.new(@name, @body.clone, @union)\n    end\n\n    def_equals_and_hash @name, @union, @body\n  end\n\n  class EnumDef < ASTNode\n    property name : Path\n    property members : Array(ASTNode)\n    property base_type : ASTNode?\n    property doc : String?\n    property visibility = Visibility::Public\n\n    def initialize(@name, @members = [] of ASTNode, @base_type = nil)\n    end\n\n    def accept_children(visitor)\n      @members.each &.accept visitor\n      @base_type.try &.accept visitor\n    end\n\n    def clone_without_location\n      EnumDef.new(@name, @members.clone, @base_type.clone)\n    end\n\n    def_equals_and_hash @name, @members, @base_type\n  end\n\n  class ExternalVar < ASTNode\n    property name : String\n    property doc : String?\n    property type_spec : ASTNode\n    property real_name : String?\n\n    def initialize(@name, @type_spec, @real_name = nil)\n    end\n\n    def accept_children(visitor)\n      @type_spec.accept visitor\n    end\n\n    def clone_without_location\n      ExternalVar.new(@name, @type_spec.clone, @real_name)\n    end\n\n    def_equals_and_hash @name, @type_spec, @real_name\n  end\n\n  class Alias < ASTNode\n    property name : Path\n    property value : ASTNode\n    property doc : String?\n    property visibility = Visibility::Public\n\n    def initialize(@name : Path, @value : ASTNode)\n    end\n\n    def accept_children(visitor)\n      @value.accept visitor\n    end\n\n    def clone_without_location\n      Alias.new(@name.clone, @value.clone)\n    end\n\n    def_equals_and_hash @name, @value\n  end\n\n  class Metaclass < ASTNode\n    property name : ASTNode\n\n    def initialize(@name)\n    end\n\n    def accept_children(visitor)\n      @name.accept visitor\n    end\n\n    def clone_without_location\n      Metaclass.new(@name.clone)\n    end\n\n    def_equals_and_hash name\n  end\n\n  # obj as to\n  class Cast < ASTNode\n    property obj : ASTNode\n    property to : ASTNode\n\n    def initialize(@obj : ASTNode, @to : ASTNode)\n    end\n\n    def accept_children(visitor)\n      @obj.accept visitor\n      @to.accept visitor\n    end\n\n    def clone_without_location\n      Cast.new(@obj.clone, @to.clone)\n    end\n\n    def end_location\n      @end_location || @to.end_location\n    end\n\n    def_equals_and_hash @obj, @to\n  end\n\n  # obj.as?(to)\n  class NilableCast < ASTNode\n    property obj\n    property to\n\n    def initialize(@obj : ASTNode, @to : ASTNode)\n    end\n\n    def accept_children(visitor)\n      @obj.accept visitor\n      @to.accept visitor\n    end\n\n    def clone_without_location\n      NilableCast.new(@obj.clone, @to.clone)\n    end\n\n    def end_location\n      @end_location || @to.end_location\n    end\n\n    def_equals_and_hash @obj, @to\n  end\n\n  # typeof(exp, exp, ...)\n  class TypeOf < ASTNode\n    property expressions : Array(ASTNode)\n\n    def initialize(@expressions)\n    end\n\n    def accept_children(visitor)\n      @expressions.each &.accept visitor\n    end\n\n    def clone_without_location\n      TypeOf.new(@expressions.clone)\n    end\n\n    def_equals_and_hash expressions\n  end\n\n  class Annotation < ASTNode\n    property path : Path\n    property args : Array(ASTNode)\n    property named_args : Array(NamedArgument)?\n    property doc : String?\n\n    def initialize(@path, @args = [] of ASTNode, @named_args = nil)\n    end\n\n    def accept_children(visitor)\n      @path.accept visitor\n      @args.each &.accept visitor\n      @named_args.try &.each &.accept visitor\n    end\n\n    def clone_without_location\n      Annotation.new(@path.clone, @args.clone, @named_args.clone)\n    end\n\n    def has_any_args?\n      args.present? || !named_args.nil?\n    end\n\n    def_equals_and_hash path, args, named_args\n  end\n\n  # A macro expression,\n  # surrounded by {{ ... }} (output = true)\n  # or by {% ... %} (output = false)\n  class MacroExpression < ASTNode\n    property exp : ASTNode\n    property? output : Bool\n\n    def initialize(@exp : ASTNode, @output = true)\n    end\n\n    def accept_children(visitor)\n      @exp.accept visitor\n    end\n\n    def clone_without_location\n      MacroExpression.new(@exp.clone, @output)\n    end\n\n    def_equals_and_hash exp, output?\n  end\n\n  # Free text that is part of a macro\n  class MacroLiteral < ASTNode\n    property value : String\n\n    def initialize(@value : String)\n    end\n\n    def clone_without_location\n      self\n    end\n\n    def_equals_and_hash value\n  end\n\n  class MacroVerbatim < UnaryExpression\n    def clone_without_location\n      MacroVerbatim.new(@exp.clone)\n    end\n  end\n\n  # if inside a macro\n  #\n  #     {% 'if' cond %}\n  #       then\n  #     {% 'else' %}\n  #       else\n  #     {% 'end' %}\n  class MacroIf < ASTNode\n    property cond : ASTNode\n    property then : ASTNode\n    property else : ASTNode\n    property? is_unless : Bool\n\n    def initialize(@cond, a_then = nil, a_else = nil, @is_unless : Bool = false)\n      @then = Expressions.from a_then\n      @else = Expressions.from a_else\n    end\n\n    def accept_children(visitor)\n      @cond.accept visitor\n      @then.accept visitor\n      @else.accept visitor\n    end\n\n    def clone_without_location\n      MacroIf.new(@cond.clone, @then.clone, @else.clone, @is_unless)\n    end\n\n    def_equals_and_hash @cond, @then, @else, @is_unless\n  end\n\n  # for inside a macro:\n  #\n  #    {% for x1, x2, ..., xn in exp %}\n  #      body\n  #    {% end %}\n  class MacroFor < ASTNode\n    property vars : Array(Var)\n    property exp : ASTNode\n    property body : ASTNode\n\n    def initialize(@vars, @exp, @body)\n    end\n\n    def accept_children(visitor)\n      @vars.each &.accept visitor\n      @exp.accept visitor\n      @body.accept visitor\n    end\n\n    def clone_without_location\n      MacroFor.new(@vars.clone, @exp.clone, @body.clone)\n    end\n\n    def_equals_and_hash @vars, @exp, @body\n  end\n\n  # A uniquely named variable inside a macro (like %var)\n  class MacroVar < ASTNode\n    property name : String\n    property exps : Array(ASTNode)?\n\n    def initialize(@name : String, @exps = nil)\n    end\n\n    def accept_children(visitor)\n      @exps.try &.each &.accept visitor\n    end\n\n    def clone_without_location\n      MacroVar.new(@name, @exps.clone)\n    end\n\n    def_equals_and_hash @name, @exps\n  end\n\n  # An underscore matches against any type\n  class Underscore < ASTNode\n    def ==(other : self)\n      true\n    end\n\n    def clone_without_location\n      Underscore.new\n    end\n\n    def_hash\n  end\n\n  class Splat < UnaryExpression\n    def clone_without_location\n      Splat.new(@exp.clone)\n    end\n  end\n\n  class DoubleSplat < UnaryExpression\n    def clone_without_location\n      DoubleSplat.new(@exp.clone)\n    end\n  end\n\n  class MagicConstant < ASTNode\n    property name : Token::Kind\n\n    def initialize(@name : Token::Kind)\n    end\n\n    def clone_without_location\n      MagicConstant.new(@name)\n    end\n\n    def expand_node(location, end_location)\n      case name\n      when .magic_line?\n        MagicConstant.expand_line_node(location)\n      when .magic_end_line?\n        MagicConstant.expand_line_node(end_location)\n      when .magic_file?\n        MagicConstant.expand_file_node(location)\n      when .magic_dir?\n        MagicConstant.expand_dir_node(location)\n      else\n        raise \"BUG: unknown magic constant: #{name}\"\n      end\n    end\n\n    def self.expand_line_node(location)\n      NumberLiteral.new(expand_line(location))\n    end\n\n    def self.expand_line(location)\n      (location.try(&.expanded_location) || location).try(&.line_number) || 0\n    end\n\n    def self.expand_file_node(location)\n      StringLiteral.new(expand_file(location))\n    end\n\n    def self.expand_file(location)\n      location.try(&.original_filename.to_s) || \"?\"\n    end\n\n    def self.expand_dir_node(location)\n      StringLiteral.new(expand_dir(location))\n    end\n\n    def self.expand_dir(location)\n      location.try(&.dirname) || \"?\"\n    end\n\n    def_equals_and_hash name\n  end\n\n  class Asm < ASTNode\n    property text : String\n    property outputs : Array(AsmOperand)?\n    property inputs : Array(AsmOperand)?\n    property clobbers : Array(String)?\n    property? volatile : Bool\n    property? alignstack : Bool\n    property? intel : Bool\n    property? can_throw : Bool\n\n    def initialize(@text, @outputs = nil, @inputs = nil, @clobbers = nil, @volatile = false, @alignstack = false, @intel = false, @can_throw = false)\n    end\n\n    def accept_children(visitor)\n      @outputs.try &.each &.accept visitor\n      @inputs.try &.each &.accept visitor\n    end\n\n    def clone_without_location\n      Asm.new(@text, @outputs.clone, @inputs.clone, @clobbers, @volatile, @alignstack, @intel, @can_throw)\n    end\n\n    def_equals_and_hash text, outputs, inputs, clobbers, volatile?, alignstack?, intel?, can_throw?\n  end\n\n  class AsmOperand < ASTNode\n    property constraint : String\n    property exp : ASTNode\n\n    def initialize(@constraint : String, @exp : ASTNode)\n    end\n\n    def accept_children(visitor)\n      @exp.accept visitor\n    end\n\n    def clone_without_location\n      AsmOperand.new(@constraint, @exp)\n    end\n\n    def_equals_and_hash constraint, exp\n  end\n\n  enum Visibility : Int8\n    Public\n    Protected\n    Private\n  end\nend\n\nrequire \"./to_s\"\n"
  },
  {
    "path": "src/compiler/crystal/syntax/exception.cr",
    "content": "require \"../exception\"\n\nmodule Crystal\n  class SyntaxException < CodeError\n    include ErrorFormat\n\n    getter line_number : Int32\n    getter column_number : Int32\n    getter filename\n    getter size : Int32?\n\n    def initialize(message, @line_number, @column_number, @filename, @size = nil)\n      super(message)\n    end\n\n    def has_location?\n      @filename || @line_number\n    end\n\n    def to_json_single(json)\n      json.object do\n        json.field \"file\", true_filename\n        json.field \"line\", @line_number\n        json.field \"column\", @column_number\n        json.field \"size\", @size\n        json.field \"message\", @message\n      end\n    end\n\n    def append_to_s(io : IO, source)\n      msg = @message.to_s\n      error_message_lines = msg.lines\n\n      io << error_body(source, default_message)\n      io << '\\n'\n      io << colorize(\"#{@warning ? \"Warning\" : \"Error\"}: #{error_message_lines.shift}\").yellow.bold\n      io << remaining error_message_lines\n    end\n\n    def default_message\n      if (filename = @filename) && (line_number = @line_number)\n        \"#{@warning ? \"warning\" : \"syntax error\"} in #{filename}:#{line_number}\"\n      end\n    end\n\n    def to_s_with_source(io : IO, source)\n      append_to_s io, source\n    end\n\n    def deepest_error_message\n      @message\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/lexer.cr",
    "content": "require \"./token\"\nrequire \"../exception\"\nrequire \"string_pool\"\nrequire \"../warnings\"\n\nmodule Crystal\n  class Lexer\n    property? doc_enabled : Bool\n    property? comments_enabled : Bool\n    property? count_whitespace : Bool\n    property? wants_raw : Bool\n    property? slash_is_regex : Bool\n    property? wants_def_or_macro_name : Bool\n    getter reader : Char::Reader\n    getter token : Token\n    property line_number : Int32\n    property column_number : Int32\n    property wants_symbol : Bool\n    @filename : String | VirtualFile | Nil\n    @stacked_filename : String | VirtualFile | Nil\n    @token_end_location : Location?\n    @string_pool : StringPool\n\n    # This is an interface for storing data associated to a heredoc\n    module HeredocItem\n    end\n\n    # Heredocs pushed when found. Should be processed when encountering a newline\n    getter heredocs = [] of {Token::DelimiterState, HeredocItem}\n\n    property macro_expansion_pragmas : Hash(Int32, Array(LocPragma))? = nil\n\n    alias LocPragma = LocSetPragma | LocPushPragma | LocPopPragma\n\n    record LocSetPragma,\n      filename : String,\n      line_number : Int32,\n      column_number : Int32 do\n      def run_pragma(lexer)\n        lexer.set_location filename, line_number, column_number\n      end\n    end\n\n    record LocPushPragma do\n      def run_pragma(lexer)\n        lexer.push_location\n      end\n    end\n\n    record LocPopPragma do\n      def run_pragma(lexer)\n        lexer.pop_location\n      end\n    end\n\n    # Warning settings and all detected warnings.\n    property warnings : WarningCollection\n\n    def initialize(string, string_pool : StringPool? = nil, warnings : WarningCollection? = nil)\n      @warnings = warnings || WarningCollection.new\n      @reader = Char::Reader.new(string)\n      check_reader_error\n      @token = Token.new\n      @temp_token = Token.new\n      @line_number = 1\n      @column_number = 1\n      @filename = \"\"\n      @wants_regex = true\n      @doc_enabled = false\n      @comment_is_doc = true\n      @comments_enabled = false\n      @count_whitespace = false\n      @slash_is_regex = true\n      @wants_raw = false\n      @wants_def_or_macro_name = false\n      @wants_symbol = true\n      @string_pool = string_pool || StringPool.new\n\n      # When lexing macro tokens, when we encounter `#{` inside\n      # a string we push the current delimiter here and reset\n      # the current one to nil. The reason is, inside strings\n      # we don't want to consider %foo a macro variable, but\n      # we do want to do this inside interpolations.\n      # We then count curly braces, with @macro_curly_count,\n      # until we find the last `}` and then we pop from the stack\n      # and get the original delimiter.\n      @delimiter_state_stack = [] of Token::DelimiterState\n      @macro_curly_count = 0\n\n      @stacked = false\n      @stacked_filename = \"\"\n      @stacked_line_number = 1\n      @stacked_column_number = 1\n    end\n\n    def filename=(filename)\n      @filename = filename\n    end\n\n    def next_token\n      # Check previous token:\n      if @token.type.newline? || @token.type.eof?\n        # 1) After a newline or at the start of the stream (:EOF), a following comment can be a doc comment\n        @comment_is_doc = true\n      elsif !@token.type.space?\n        # 2) Any non-space token prevents a following comment from being a doc\n        # comment.\n        @comment_is_doc = false\n      end\n\n      reset_token\n\n      # Skip comments\n      while current_char == '#'\n        start = current_pos\n\n        # Check #<loc:...> pragma comment\n        if char_sequence?('<', 'l', 'o', 'c', ':', column_increment: false)\n          next_char_no_column_increment\n          consume_loc_pragma\n        else\n          if @doc_enabled && @comment_is_doc\n            consume_doc\n          elsif @comments_enabled\n            return consume_comment(start)\n          else\n            skip_comment\n          end\n        end\n      end\n\n      start = current_pos\n\n      # Fix location by `macro_expansion_pragmas`.\n      if me_pragmas = macro_expansion_pragmas\n        # It might happen that the current \"start\" already passed some\n        # location pragmas, so we must consume all of those. For example\n        # if one does `@{{...}}` inside a macro, \"start\" will be one less\n        # number than the pragma, and after consuming \"@...\" if we don't\n        # consume the pragmas generated by `{{...}}` we'll be in an\n        # incorrect location.\n        while me_pragmas.first_key?.try &.<=(start)\n          _, pragmas = me_pragmas.shift\n          pragmas.each &.run_pragma self\n        end\n      end\n\n      reset_regex_flags = true\n\n      case current_char\n      when '\\0'\n        @token.type = :EOF\n      when ' ', '\\t'\n        consume_whitespace\n        reset_regex_flags = false\n      when '\\\\'\n        case next_char\n        when '\\r', '\\n'\n          handle_slash_r_slash_n_or_slash_n\n          incr_line_number 0\n          @token.passed_backslash_newline = true\n          consume_whitespace\n          reset_regex_flags = false\n        else\n          unknown_token\n        end\n      when '\\n'\n        next_char :NEWLINE\n        incr_line_number\n        reset_regex_flags = false\n        consume_newlines\n      when '\\r'\n        if next_char == '\\n'\n          next_char :NEWLINE\n          incr_line_number\n          reset_regex_flags = false\n          consume_newlines\n        else\n          raise \"expected '\\\\n' after '\\\\r'\"\n        end\n      when '='\n        case next_char\n        when '='\n          case next_char\n          when '='\n            next_char :OP_EQ_EQ_EQ\n          else\n            @token.type = :OP_EQ_EQ\n          end\n        when '>'\n          next_char :OP_EQ_GT\n        when '~'\n          next_char :OP_EQ_TILDE\n        else\n          @token.type = :OP_EQ\n        end\n      when '!'\n        case next_char\n        when '='\n          next_char :OP_BANG_EQ\n        when '~'\n          next_char :OP_BANG_TILDE\n        else\n          @token.type = :OP_BANG\n        end\n      when '<'\n        case next_char\n        when '='\n          case next_char\n          when '>'\n            next_char :OP_LT_EQ_GT\n          else\n            @token.type = :OP_LT_EQ\n          end\n        when '<'\n          case next_char\n          when '='\n            next_char :OP_LT_LT_EQ\n          when '-'\n            consume_heredoc_start(start)\n          else\n            @token.type = :OP_LT_LT\n          end\n        else\n          @token.type = :OP_LT\n        end\n      when '>'\n        case next_char\n        when '='\n          next_char :OP_GT_EQ\n        when '>'\n          case next_char\n          when '='\n            next_char :OP_GT_GT_EQ\n          else\n            @token.type = :OP_GT_GT\n          end\n        else\n          @token.type = :OP_GT\n        end\n      when '+'\n        @token.start = start\n        case next_char\n        when '='\n          next_char :OP_PLUS_EQ\n        when '0'..'9'\n          scan_number start\n        when '+'\n          raise \"postfix increment is not supported, use `exp += 1`\"\n        else\n          @token.type = :OP_PLUS\n        end\n      when '-'\n        @token.start = start\n        case next_char\n        when '='\n          next_char :OP_MINUS_EQ\n        when '>'\n          next_char :OP_MINUS_GT\n        when '0'..'9'\n          scan_number start, negative: true\n        when '-'\n          raise \"postfix decrement is not supported, use `exp -= 1`\"\n        else\n          @token.type = :OP_MINUS\n        end\n      when '*'\n        case next_char\n        when '='\n          next_char :OP_STAR_EQ\n        when '*'\n          case next_char\n          when '='\n            next_char :OP_STAR_STAR_EQ\n          else\n            @token.type = :OP_STAR_STAR\n          end\n        else\n          @token.type = :OP_STAR\n        end\n      when '/'\n        char = next_char\n        if (@wants_def_or_macro_name || !@slash_is_regex) && char == '/'\n          case next_char\n          when '='\n            next_char :OP_SLASH_SLASH_EQ\n          else\n            @token.type = :OP_SLASH_SLASH\n          end\n        elsif !@slash_is_regex && char == '='\n          next_char :OP_SLASH_EQ\n        elsif @wants_def_or_macro_name\n          @token.type = :OP_SLASH\n        elsif @slash_is_regex\n          delimited_pair :regex, '/', '/', start, advance: false\n        elsif char.ascii_whitespace? || char == '\\0'\n          @token.type = :OP_SLASH\n        elsif @wants_regex\n          delimited_pair :regex, '/', '/', start, advance: false\n        else\n          @token.type = :OP_SLASH\n        end\n      when '%'\n        if @wants_def_or_macro_name\n          next_char :OP_PERCENT\n        else\n          case next_char\n          when '='\n            next_char :OP_PERCENT_EQ\n          when '(', '[', '{', '<', '|'\n            delimited_pair :string, current_char, closing_char, start\n          when 'i'\n            case peek_next_char\n            when '(', '{', '[', '<', '|'\n              start_char = next_char\n              next_char :SYMBOL_ARRAY_START\n              @token.raw = \"%i#{start_char}\" if @wants_raw\n              @token.delimiter_state = Token::DelimiterState.new(:symbol_array, start_char, closing_char(start_char))\n            else\n              @token.type = :OP_PERCENT\n            end\n          when 'q'\n            case peek_next_char\n            when '(', '{', '[', '<', '|'\n              next_char\n              delimited_pair :string, current_char, closing_char, start, allow_escapes: false\n            else\n              @token.type = :OP_PERCENT\n            end\n          when 'Q'\n            case peek_next_char\n            when '(', '{', '[', '<', '|'\n              next_char\n              delimited_pair :string, current_char, closing_char, start\n            else\n              @token.type = :OP_PERCENT\n            end\n          when 'r'\n            case peek_next_char\n            when '(', '[', '{', '<', '|'\n              next_char\n              delimited_pair :regex, current_char, closing_char, start\n            else\n              @token.type = :OP_PERCENT\n            end\n          when 'x'\n            case peek_next_char\n            when '(', '[', '{', '<', '|'\n              next_char\n              delimited_pair :command, current_char, closing_char, start\n            else\n              @token.type = :OP_PERCENT\n            end\n          when 'w'\n            case peek_next_char\n            when '(', '{', '[', '<', '|'\n              start_char = next_char\n              next_char :STRING_ARRAY_START\n              @token.raw = \"%w#{start_char}\" if @wants_raw\n              @token.delimiter_state = Token::DelimiterState.new(:string_array, start_char, closing_char(start_char))\n            else\n              @token.type = :OP_PERCENT\n            end\n          when '}'\n            next_char :OP_PERCENT_RCURLY\n          else\n            @token.type = :OP_PERCENT\n          end\n        end\n      when '(' then next_char :OP_LPAREN\n      when ')' then next_char :OP_RPAREN\n      when '{'\n        case next_char\n        when '%'\n          next_char :OP_LCURLY_PERCENT\n        when '{'\n          next_char :OP_LCURLY_LCURLY\n        else\n          @token.type = :OP_LCURLY\n        end\n      when '}' then next_char :OP_RCURLY\n      when '['\n        case next_char\n        when ']'\n          case next_char\n          when '='\n            next_char :OP_LSQUARE_RSQUARE_EQ\n          when '?'\n            next_char :OP_LSQUARE_RSQUARE_QUESTION\n          else\n            @token.type = :OP_LSQUARE_RSQUARE\n          end\n        else\n          @token.type = :OP_LSQUARE\n        end\n      when ']' then next_char :OP_RSQUARE\n      when ',' then next_char :OP_COMMA\n      when '?' then next_char :OP_QUESTION\n      when ';'\n        reset_regex_flags = false\n        next_char :OP_SEMICOLON\n      when ':'\n        if next_char == ':'\n          next_char :OP_COLON_COLON\n        elsif @wants_symbol\n          consume_symbol\n        else\n          @token.type = :OP_COLON\n        end\n      when '~'\n        next_char :OP_TILDE\n      when '.'\n        line = @line_number\n        column = @column_number\n        case next_char\n        when '.'\n          case next_char\n          when '.'\n            next_char :OP_PERIOD_PERIOD_PERIOD\n          else\n            @token.type = :OP_PERIOD_PERIOD\n          end\n        when .ascii_number?\n          raise \".1 style number literal is not supported, put 0 before dot\", line, column\n        else\n          @token.type = :OP_PERIOD\n        end\n      when '&'\n        case next_char\n        when '&'\n          case next_char\n          when '='\n            next_char :OP_AMP_AMP_EQ\n          else\n            @token.type = :OP_AMP_AMP\n          end\n        when '='\n          next_char :OP_AMP_EQ\n        when '+'\n          case next_char\n          when '='\n            next_char :OP_AMP_PLUS_EQ\n          else\n            @token.type = :OP_AMP_PLUS\n          end\n        when '-'\n          # Check if '>' comes after '&-', making it '&->'.\n          # We want to parse that like '&(->...)',\n          # so we only return '&' for now.\n          if peek_next_char == '>'\n            @token.type = :OP_AMP\n          else\n            case next_char\n            when '='\n              next_char :OP_AMP_MINUS_EQ\n            else\n              @token.type = :OP_AMP_MINUS\n            end\n          end\n        when '*'\n          case next_char\n          when '*'\n            next_char :OP_AMP_STAR_STAR\n          when '='\n            next_char :OP_AMP_STAR_EQ\n          else\n            @token.type = :OP_AMP_STAR\n          end\n        else\n          @token.type = :OP_AMP\n        end\n      when '|'\n        case next_char\n        when '|'\n          case next_char\n          when '='\n            next_char :OP_BAR_BAR_EQ\n          else\n            @token.type = :OP_BAR_BAR\n          end\n        when '='\n          next_char :OP_BAR_EQ\n        else\n          @token.type = :OP_BAR\n        end\n      when '^'\n        case next_char\n        when '='\n          next_char :OP_CARET_EQ\n        else\n          @token.type = :OP_CARET\n        end\n      when '\\''\n        line = @line_number\n        column = @column_number\n        @token.type = :CHAR\n        case char1 = next_char\n        when '\\\\'\n          case char2 = next_char\n          when '\\\\'\n            @token.value = '\\\\'\n          when '\\''\n            @token.value = '\\''\n          when 'a'\n            @token.value = '\\a'\n          when 'b'\n            @token.value = '\\b'\n          when 'e'\n            @token.value = '\\e'\n          when 'f'\n            @token.value = '\\f'\n          when 'n'\n            @token.value = '\\n'\n          when 'r'\n            @token.value = '\\r'\n          when 't'\n            @token.value = '\\t'\n          when 'v'\n            @token.value = '\\v'\n          when 'u'\n            value = consume_char_unicode_escape\n            @token.value = value.chr\n          when '0'\n            @token.value = '\\0'\n          when '\\0'\n            raise \"unterminated char literal\", line, column\n          else\n            raise \"invalid char escape sequence '\\\\#{char2}'\", line, column\n          end\n        when '\\''\n          raise \"invalid empty char literal (did you mean '\\\\''?)\", line, column\n        when '\\0'\n          raise \"unterminated char literal\", line, column\n        else\n          @token.value = char1\n        end\n        if next_char != '\\''\n          raise \"unterminated char literal, use double quotes for strings\", line, column\n        end\n        next_char\n        set_token_raw_from_start(start)\n      when '`'\n        if @wants_def_or_macro_name\n          next_char :OP_GRAVE\n        else\n          delimited_pair :command, '`', '`', start\n        end\n      when '\"'\n        delimited_pair :string, '\"', '\"', start\n      when '0'..'9'\n        scan_number start\n      when '@'\n        case next_char\n        when '['\n          next_char :OP_AT_LSQUARE\n        when '@'\n          next_char\n          consume_variable :CLASS_VAR, start\n        else\n          consume_variable :INSTANCE_VAR, start\n        end\n      when '$'\n        case next_char\n        when '~'\n          next_char :OP_DOLLAR_TILDE\n        when '?'\n          next_char :OP_DOLLAR_QUESTION\n        when .ascii_number?\n          start = current_pos\n          if current_char == '0'\n            next_char\n          else\n            while next_char.ascii_number?\n              # Nothing to do\n            end\n            next_char if current_char == '?'\n          end\n          @token.type = :GLOBAL_MATCH_DATA_INDEX\n          @token.value = string_range_from_pool(start)\n        else\n          consume_variable :GLOBAL, start\n        end\n      when 'a'\n        case next_char\n        when 'b'\n          if char_sequence?('s', 't', 'r', 'a', 'c', 't')\n            return check_ident_or_keyword(:abstract, start)\n          end\n        when 'l'\n          if next_char == 'i'\n            case next_char\n            when 'a'\n              if next_char == 's'\n                return check_ident_or_keyword(:alias, start)\n              end\n            when 'g'\n              if char_sequence?('n', 'o', 'f')\n                return check_ident_or_keyword(:alignof, start)\n              end\n            else\n              # scan_ident\n            end\n          end\n        when 's'\n          case peek_next_char\n          when 'm'\n            next_char\n            return check_ident_or_keyword(:asm, start)\n          when '?'\n            next_char\n            next_char :IDENT\n            @token.value = :as_question\n            return @token\n          else\n            return check_ident_or_keyword(:as, start)\n          end\n        when 'n'\n          if char_sequence?('n', 'o', 't', 'a', 't', 'i', 'o', 'n')\n            return check_ident_or_keyword(:annotation, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'b'\n        case next_char\n        when 'e'\n          if char_sequence?('g', 'i', 'n')\n            return check_ident_or_keyword(:begin, start)\n          end\n        when 'r'\n          if char_sequence?('e', 'a', 'k')\n            return check_ident_or_keyword(:break, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'c'\n        case next_char\n        when 'a'\n          if char_sequence?('s', 'e')\n            return check_ident_or_keyword(:case, start)\n          end\n        when 'l'\n          if char_sequence?('a', 's', 's')\n            return check_ident_or_keyword(:class, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'd'\n        case next_char\n        when 'e'\n          if next_char == 'f'\n            return check_ident_or_keyword(:def, start)\n          end\n        when 'o' then return check_ident_or_keyword(:do, start)\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'e'\n        case next_char\n        when 'l'\n          case next_char\n          when 's'\n            case next_char\n            when 'e' then return check_ident_or_keyword(:else, start)\n            when 'i'\n              if next_char == 'f'\n                return check_ident_or_keyword(:elsif, start)\n              end\n            else\n              # scan_ident\n            end\n          else\n            # scan_ident\n          end\n        when 'n'\n          case next_char\n          when 'd'\n            return check_ident_or_keyword(:end, start)\n          when 's'\n            if char_sequence?('u', 'r', 'e')\n              return check_ident_or_keyword(:ensure, start)\n            end\n          when 'u'\n            if next_char == 'm'\n              return check_ident_or_keyword(:enum, start)\n            end\n          else\n            # scan_ident\n          end\n        when 'x'\n          if char_sequence?('t', 'e', 'n', 'd')\n            return check_ident_or_keyword(:extend, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'f'\n        case next_char\n        when 'a'\n          if char_sequence?('l', 's', 'e')\n            return check_ident_or_keyword(:false, start)\n          end\n        when 'o'\n          if next_char == 'r'\n            return check_ident_or_keyword(:for, start)\n          end\n        when 'u'\n          if next_char == 'n'\n            return check_ident_or_keyword(:fun, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'i'\n        case next_char\n        when 'f'\n          return check_ident_or_keyword(:if, start)\n        when 'n'\n          if ident_part_or_end?(peek_next_char)\n            case next_char\n            when 'c'\n              if char_sequence?('l', 'u', 'd', 'e')\n                return check_ident_or_keyword(:include, start)\n              end\n            when 's'\n              if char_sequence?('t', 'a', 'n', 'c', 'e', '_')\n                case next_char\n                when 's'\n                  if char_sequence?('i', 'z', 'e', 'o', 'f')\n                    return check_ident_or_keyword(:instance_sizeof, start)\n                  end\n                when 'a'\n                  if char_sequence?('l', 'i', 'g', 'n', 'o', 'f')\n                    return check_ident_or_keyword(:instance_alignof, start)\n                  end\n                else\n                  # scan_ident\n                end\n              end\n            else\n              # scan_ident\n            end\n          else\n            next_char :IDENT\n            @token.value = :in\n            return @token\n          end\n        when 's'\n          if char_sequence?('_', 'a', '?')\n            return check_ident_or_keyword(:is_a_question, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'l'\n        case next_char\n        when 'i'\n          if next_char == 'b'\n            return check_ident_or_keyword(:lib, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'm'\n        case next_char\n        when 'a'\n          if char_sequence?('c', 'r', 'o')\n            return check_ident_or_keyword(:macro, start)\n          end\n        when 'o'\n          case next_char\n          when 'd'\n            if char_sequence?('u', 'l', 'e')\n              return check_ident_or_keyword(:module, start)\n            end\n          else\n            # scan_ident\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'n'\n        case next_char\n        when 'e'\n          if char_sequence?('x', 't')\n            return check_ident_or_keyword(:next, start)\n          end\n        when 'i'\n          case next_char\n          when 'l'\n            if peek_next_char == '?'\n              next_char\n              return check_ident_or_keyword(:nil_question, start)\n            else\n              return check_ident_or_keyword(:nil, start)\n            end\n          else\n            # scan_ident\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'o'\n        case next_char\n        when 'f'\n          if peek_next_char == 'f'\n            next_char\n            if char_sequence?('s', 'e', 't', 'o', 'f')\n              return check_ident_or_keyword(:offsetof, start)\n            end\n          else\n            return check_ident_or_keyword(:of, start)\n          end\n        when 'u'\n          if next_char == 't'\n            return check_ident_or_keyword(:out, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'p'\n        case next_char\n        when 'o'\n          if char_sequence?('i', 'n', 't', 'e', 'r', 'o', 'f')\n            return check_ident_or_keyword(:pointerof, start)\n          end\n        when 'r'\n          case next_char\n          when 'i'\n            if char_sequence?('v', 'a', 't', 'e')\n              return check_ident_or_keyword(:private, start)\n            end\n          when 'o'\n            if char_sequence?('t', 'e', 'c', 't', 'e', 'd')\n              return check_ident_or_keyword(:protected, start)\n            end\n          else\n            # scan_ident\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'r'\n        case next_char\n        when 'e'\n          case next_char\n          when 's'\n            case next_char\n            when 'c'\n              if char_sequence?('u', 'e')\n                return check_ident_or_keyword(:rescue, start)\n              end\n            when 'p'\n              if char_sequence?('o', 'n', 'd', 's', '_', 't', 'o', '?')\n                return check_ident_or_keyword(:responds_to_question, start)\n              end\n            else\n              # scan_ident\n            end\n          when 't'\n            if char_sequence?('u', 'r', 'n')\n              return check_ident_or_keyword(:return, start)\n            end\n          when 'q'\n            if char_sequence?('u', 'i', 'r', 'e')\n              return check_ident_or_keyword(:require, start)\n            end\n          else\n            # scan_ident\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 's'\n        case next_char\n        when 'e'\n          if next_char == 'l'\n            case next_char\n            when 'e'\n              if char_sequence?('c', 't')\n                return check_ident_or_keyword(:select, start)\n              end\n            when 'f'\n              return check_ident_or_keyword(:self, start)\n            else\n              # scan_ident\n            end\n          end\n        when 'i'\n          if char_sequence?('z', 'e', 'o', 'f')\n            return check_ident_or_keyword(:sizeof, start)\n          end\n        when 't'\n          if char_sequence?('r', 'u', 'c', 't')\n            return check_ident_or_keyword(:struct, start)\n          end\n        when 'u'\n          if char_sequence?('p', 'e', 'r')\n            return check_ident_or_keyword(:super, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 't'\n        case next_char\n        when 'h'\n          if char_sequence?('e', 'n')\n            return check_ident_or_keyword(:then, start)\n          end\n        when 'r'\n          if char_sequence?('u', 'e')\n            return check_ident_or_keyword(:true, start)\n          end\n        when 'y'\n          if char_sequence?('p', 'e')\n            if peek_next_char == 'o'\n              next_char\n              if next_char == 'f'\n                return check_ident_or_keyword(:typeof, start)\n              end\n            else\n              return check_ident_or_keyword(:type, start)\n            end\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'u'\n        if next_char == 'n'\n          case next_char\n          when 'i'\n            case next_char\n            when 'o'\n              if next_char == 'n'\n                return check_ident_or_keyword(:union, start)\n              end\n            when 'n'\n              if char_sequence?('i', 't', 'i', 'a', 'l', 'i', 'z', 'e', 'd')\n                return check_ident_or_keyword(:uninitialized, start)\n              end\n            else\n              # scan_ident\n            end\n          when 'l'\n            if char_sequence?('e', 's', 's')\n              return check_ident_or_keyword(:unless, start)\n            end\n          when 't'\n            if char_sequence?('i', 'l')\n              return check_ident_or_keyword(:until, start)\n            end\n          else\n            # scan_ident\n          end\n        end\n        scan_ident(start)\n      when 'v'\n        if char_sequence?('e', 'r', 'b', 'a', 't', 'i', 'm')\n          return check_ident_or_keyword(:verbatim, start)\n        end\n        scan_ident(start)\n      when 'w'\n        case next_char\n        when 'h'\n          case next_char\n          when 'e'\n            if next_char == 'n'\n              return check_ident_or_keyword(:when, start)\n            end\n          when 'i'\n            if char_sequence?('l', 'e')\n              return check_ident_or_keyword(:while, start)\n            end\n          else\n            # scan_ident\n          end\n        when 'i'\n          if char_sequence?('t', 'h')\n            return check_ident_or_keyword(:with, start)\n          end\n        else\n          # scan_ident\n        end\n        scan_ident(start)\n      when 'y'\n        if char_sequence?('i', 'e', 'l', 'd')\n          return check_ident_or_keyword(:yield, start)\n        end\n        scan_ident(start)\n      when '_'\n        case next_char\n        when '_'\n          case next_char\n          when 'D'\n            if char_sequence?('I', 'R', '_', '_')\n              unless ident_part_or_end?(peek_next_char)\n                next_char :MAGIC_DIR\n                return @token\n              end\n            end\n          when 'E'\n            if char_sequence?('N', 'D', '_', 'L', 'I', 'N', 'E', '_', '_')\n              unless ident_part_or_end?(peek_next_char)\n                next_char :MAGIC_END_LINE\n                return @token\n              end\n            end\n          when 'F'\n            if char_sequence?('I', 'L', 'E', '_', '_')\n              unless ident_part_or_end?(peek_next_char)\n                next_char :MAGIC_FILE\n                return @token\n              end\n            end\n          when 'L'\n            if char_sequence?('I', 'N', 'E', '_', '_')\n              unless ident_part_or_end?(peek_next_char)\n                next_char :MAGIC_LINE\n                return @token\n              end\n            end\n          else\n            # scan_ident\n          end\n        else\n          unless ident_part?(current_char)\n            @token.type = :UNDERSCORE\n            return @token\n          end\n        end\n\n        scan_ident(start)\n      else\n        if current_char.uppercase? || current_char.titlecase?\n          while ident_part?(next_char)\n            # Nothing to do\n          end\n          @token.type = :CONST\n          @token.value = string_range_from_pool(start)\n        elsif ident_start?(current_char)\n          next_char\n          scan_ident(start)\n        else\n          unknown_token\n        end\n      end\n\n      if reset_regex_flags\n        @wants_regex = true\n        @slash_is_regex = false\n      end\n\n      @token\n    end\n\n    def token_end_location\n      @token_end_location ||= Location.new(@filename, @line_number, @column_number - 1)\n    end\n\n    def slash_is_regex!\n      @slash_is_regex = true\n    end\n\n    def slash_is_not_regex!\n      @slash_is_regex = false\n    end\n\n    def consume_comment(start_pos)\n      skip_comment\n      @token.type = :COMMENT\n      @token.value = string_range(start_pos)\n      @token\n    end\n\n    def consume_doc\n      # Ignore first whitespace after comment, like in `# some doc`\n      next_char if current_char == ' '\n\n      start_pos = current_pos\n\n      skip_comment\n\n      if doc_buffer = @token.doc_buffer\n        doc_buffer << '\\n'\n      else\n        @token.doc_buffer = doc_buffer = IO::Memory.new\n      end\n\n      doc_buffer.write slice_range(start_pos)\n    end\n\n    def skip_comment\n      char = current_char\n      while !char.in?('\\n', '\\0')\n        char = next_char_no_column_increment\n      end\n    end\n\n    def consume_whitespace\n      start_pos = current_pos\n      next_char :SPACE\n      while true\n        case current_char\n        when ' ', '\\t'\n          next_char\n        when '\\\\'\n          if next_char.in?('\\r', '\\n')\n            handle_slash_r_slash_n_or_slash_n\n            next_char\n            incr_line_number\n            @token.passed_backslash_newline = true\n          else\n            unknown_token\n          end\n        else\n          break\n        end\n      end\n      if @count_whitespace\n        @token.value = string_range(start_pos)\n      end\n    end\n\n    def consume_newlines\n      # If there are heredocs we don't freely consume newlines because\n      # these will be part of the heredoc string\n      return unless @heredocs.empty?\n\n      if @count_whitespace\n        return\n      end\n\n      while true\n        case current_char\n        when '\\n'\n          next_char_no_column_increment\n          incr_line_number nil\n          @token.doc_buffer = nil\n        when '\\r'\n          if next_char_no_column_increment != '\\n'\n            raise \"expected '\\\\n' after '\\\\r'\"\n          end\n          next_char_no_column_increment\n          incr_line_number nil\n          @token.doc_buffer = nil\n        else\n          break\n        end\n      end\n    end\n\n    def check_ident_or_keyword(keyword : Keyword, start)\n      if ident_part_or_end?(peek_next_char)\n        scan_ident(start)\n      else\n        next_char :IDENT\n        @token.value = keyword\n      end\n      @token\n    end\n\n    def scan_ident(start)\n      while ident_part?(current_char)\n        next_char\n      end\n      if current_char.in?('?', '!') && peek_next_char != '='\n        next_char\n      end\n      @token.type = :IDENT\n      @token.value = string_range_from_pool(start)\n      @token\n    end\n\n    def next_char_and_symbol(value)\n      next_char\n      symbol value\n    end\n\n    def symbol(value)\n      @token.type = :SYMBOL\n      @token.value = value\n      @token.raw = \":#{value}\" if @wants_raw\n    end\n\n    macro gen_check_int_fits_in_size(type, method, size, number_size, raw_number_string, start, pos_before_suffix, negative)\n      {% if type.stringify.starts_with? \"U\" %}\n        raise \"Invalid negative value #{string_range({{start}}, {{pos_before_suffix}})} for {{type}}\", @token, (current_pos - {{start}}) if {{negative}}\n      {% end %}\n\n      if !@token.value || {{number_size}} > {{size}} || ({{number_size}} == {{size}} && {{raw_number_string}}.to_{{method.id}}? == nil)\n        raise_value_doesnt_fit_in \"{{type}}\", {{start}}, {{pos_before_suffix}}\n      end\n    end\n\n    def raise_value_doesnt_fit_in(type, start, pos_before_suffix)\n      raise \"#{string_range(start, pos_before_suffix)} doesn't fit in an #{type}\", @token, (current_pos - start)\n    end\n\n    def raise_value_doesnt_fit_in(type, start, pos_before_suffix, alternative)\n      raise \"#{string_range(start, pos_before_suffix)} doesn't fit in an #{type}, try using the suffix #{alternative}\", @token, (current_pos - start)\n    end\n\n    def warn_large_uint64_literal(start, pos_before_suffix)\n      @warnings.add_warning_at(@token.location, \"#{string_range(start, pos_before_suffix)} doesn't fit in an Int64, try using the suffix u64 or i128\")\n    end\n\n    private def scan_number(start, negative = false)\n      @token.type = :NUMBER\n      base = 10\n      number_size = 0\n      suffix_size = 0\n      is_decimal = false\n      is_e_notation = false\n      has_underscores = false\n      last_is_underscore = false\n      pos_after_prefix = start\n      pos_before_exponent = nil\n\n      # Consume prefix\n      if current_char == '0'\n        case next_char\n        when 'b'      then base = 2\n        when 'o'      then base = 8\n        when 'x'      then base = 16\n        when '0'..'9' then raise(\"octal constants should be prefixed with 0o\", @token, (current_pos - start))\n        when '_'\n          raise(\"octal constants should be prefixed with 0o\", @token, (current_pos - start)) if next_char.in?('0'..'9')\n          has_underscores = last_is_underscore = true\n        end\n\n        # Skip prefix (b, o, x)\n        unless base == 10\n          next_char\n          pos_after_prefix = current_pos\n          # Enforce number after prefix (disallow ex. \"0x\", \"0x_1\")\n          raise(\"unexpected '_' in number\", @token, (current_pos - start)) if current_char == '_'\n\n          digit = String::CHAR_TO_DIGIT[current_char.ord]?\n          raise(\"numeric literal without digits\", @token, (current_pos - start)) unless digit && digit.to_u8! < base\n        end\n      end\n\n      # Consume number\n      loop do\n        loop do\n          digit = String::CHAR_TO_DIGIT[current_char.ord]?\n          break unless digit && digit.to_u8! < base\n\n          number_size += 1 unless number_size == 0 && current_char == '0'\n          next_char\n          last_is_underscore = false\n        end\n\n        if pos_before_exponent\n          raise(\"invalid decimal number exponent\", @token, (current_pos - start)) unless current_pos > pos_before_exponent\n        end\n\n        case current_char\n        when '_'\n          raise(\"consecutive underscores in numbers aren't allowed\", @token, (current_pos - start)) if last_is_underscore\n          has_underscores = last_is_underscore = true\n        when '.'\n          raise(\"unexpected '_' in number\", @token, (current_pos - start)) if last_is_underscore\n          break if is_decimal || base != 10 || !peek_next_char.in?('0'..'9')\n          is_decimal = true\n        when 'e', 'E'\n          last_is_underscore = false\n          break if is_e_notation || base != 10\n          is_e_notation = is_decimal = true\n          next_char if peek_next_char.in?('+', '-')\n          raise(\"unexpected '_' in number\", @token, (current_pos - start)) if peek_next_char == '_'\n          pos_before_exponent = current_pos + 1\n        when 'i', 'u', 'f'\n          if current_char == 'f' && base != 10\n            case base\n            when 2 then raise(\"binary float literal is not supported\", @token, (current_pos - start))\n            when 8 then raise(\"octal float literal is not supported\", @token, (current_pos - start))\n            end\n            break\n          end\n          before_suffix_pos = current_pos\n          @token.number_kind = consume_number_suffix\n          next_char\n          suffix_size = current_pos - before_suffix_pos\n          suffix_size += 1 if last_is_underscore\n          break\n        else\n          raise(\"trailing '_' in number\", @token, (current_pos - start)) if last_is_underscore\n          break\n        end\n\n        next_char\n      end\n\n      set_token_raw_from_start(start)\n\n      # Sanitize string (or convert to decimal unless number is in base 10)\n      pos_before_suffix = current_pos - suffix_size\n      raw_number_string = string_range(pos_after_prefix, pos_before_suffix)\n      if base == 10\n        raw_number_string = raw_number_string.delete('_') if has_underscores\n        @token.value = raw_number_string\n      else\n        # The conversion to base 10 is first tried using a UInt64 to circumvent compiler\n        # regressions caused by bugs in the platform's UInt128 implementation.\n        base10_number_string = raw_number_string.to_u64?(base: base, underscore: true).try &.to_s\n        base10_number_string ||= raw_number_string.to_u128?(base: base, underscore: true).try &.to_s\n        if base10_number_string\n          number_size = base10_number_string.size\n          first_byte = @reader.string.byte_at(start).chr\n          base10_number_string = first_byte + base10_number_string if first_byte.in?('+', '-')\n          @token.value = raw_number_string = base10_number_string\n        end\n      end\n\n      if is_decimal\n        @token.number_kind = NumberKind::F64 if suffix_size == 0\n        raise(\"Invalid suffix #{@token.number_kind} for decimal number\", @token, (current_pos - start)) unless @token.number_kind.float?\n        return\n      end\n\n      # Check or determine suffix\n      if suffix_size == 0\n        raise_value_doesnt_fit_in(negative ? Int64 : UInt64, start, pos_before_suffix) unless @token.value\n        @token.number_kind = case number_size\n                             when 0..9   then NumberKind::I32\n                             when 10     then raw_number_string.to_i32? ? NumberKind::I32 : NumberKind::I64\n                             when 11..18 then NumberKind::I64\n                             when 19\n                               if raw_number_string.to_i64?\n                                 NumberKind::I64\n                               elsif negative\n                                 raise_value_doesnt_fit_in(Int64, start, pos_before_suffix, \"i128\")\n                               else\n                                 warn_large_uint64_literal(start, pos_before_suffix)\n                                 NumberKind::U64\n                               end\n                             when 20\n                               raise_value_doesnt_fit_in(Int64, start, pos_before_suffix, \"i128\") if negative\n                               if raw_number_string.to_u64?\n                                 warn_large_uint64_literal(start, pos_before_suffix)\n                                 NumberKind::U64\n                               else\n                                 raise_value_doesnt_fit_in(UInt64, start, pos_before_suffix, \"i128\")\n                               end\n                             when 21..38\n                               raise_value_doesnt_fit_in(negative ? Int64 : UInt64, start, pos_before_suffix, \"i128\")\n                             when 39\n                               if raw_number_string.to_i128?\n                                 raise_value_doesnt_fit_in(negative ? Int64 : UInt64, start, pos_before_suffix, \"i128\")\n                               elsif raw_number_string.to_u128?\n                                 raise_value_doesnt_fit_in(UInt64, start, pos_before_suffix, \"u128\")\n                               else\n                                 raise_value_doesnt_fit_in(negative ? Int64 : UInt64, start, pos_before_suffix)\n                               end\n                             else\n                               raise_value_doesnt_fit_in(negative ? Int64 : UInt64, start, pos_before_suffix)\n                             end\n      else\n        case @token.number_kind\n        when .i8?   then gen_check_int_fits_in_size(Int8, :i8, 3, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .u8?   then gen_check_int_fits_in_size(UInt8, :u8, 3, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .i16?  then gen_check_int_fits_in_size(Int16, :i16, 5, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .u16?  then gen_check_int_fits_in_size(UInt16, :u16, 5, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .i32?  then gen_check_int_fits_in_size(Int32, :i32, 10, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .u32?  then gen_check_int_fits_in_size(UInt32, :u32, 10, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .i64?  then gen_check_int_fits_in_size(Int64, :i64, 19, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .u64?  then gen_check_int_fits_in_size(UInt64, :u64, 20, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .i128? then gen_check_int_fits_in_size(Int128, :i128, 39, number_size, raw_number_string, start, pos_before_suffix, negative)\n        when .u128? then gen_check_int_fits_in_size(UInt128, :u128, 39, number_size, raw_number_string, start, pos_before_suffix, negative)\n        end\n      end\n    end\n\n    private def consume_number_suffix : NumberKind\n      case current_char\n      when 'i'\n        case next_char\n        when '8' then return NumberKind::I8\n        when '1'\n          case next_char\n          when '2' then return NumberKind::I128 if next_char == '8'\n          when '6' then return NumberKind::I16\n          end\n        when '3' then return NumberKind::I32 if next_char == '2'\n        when '6' then return NumberKind::I64 if next_char == '4'\n        end\n        raise \"invalid int suffix\"\n      when 'u'\n        case next_char\n        when '8' then return NumberKind::U8\n        when '1'\n          case next_char\n          when '2' then return NumberKind::U128 if next_char == '8'\n          when '6' then return NumberKind::U16\n          end\n        when '3' then return NumberKind::U32 if next_char == '2'\n        when '6' then return NumberKind::U64 if next_char == '4'\n        end\n        raise \"invalid uint suffix\"\n      when 'f'\n        case next_char\n        when '3' then return NumberKind::F32 if next_char == '2'\n        when '6' then return NumberKind::F64 if next_char == '4'\n        end\n        raise \"invalid float suffix\"\n      end\n      raise \"BUG: invalid suffix\"\n    end\n\n    def next_string_token(delimiter_state)\n      reset_token\n\n      @token.line_number = @line_number\n      @token.delimiter_state = delimiter_state\n\n      start = current_pos\n      string_end = delimiter_state.end\n      string_nest = delimiter_state.nest\n      string_open_count = delimiter_state.open_count\n\n      # For empty heredocs:\n      if @token.type.newline? && delimiter_state.kind.heredoc?\n        if check_heredoc_end delimiter_state\n          set_token_raw_from_start start\n          return @token\n        end\n      end\n\n      case current_char\n      when '\\0'\n        raise_unterminated_quoted delimiter_state\n      when string_end\n        next_char\n        if string_open_count == 0\n          @token.type = :DELIMITER_END\n        else\n          @token.type = :STRING\n          @token.value = string_end.to_s\n          @token.delimiter_state = delimiter_state.with_open_count_delta(-1)\n        end\n      when string_nest\n        next_char :STRING\n        @token.value = string_nest.to_s\n        @token.delimiter_state = delimiter_state.with_open_count_delta(+1)\n      when '\\\\'\n        if delimiter_state.allow_escapes\n          if delimiter_state.kind.regex?\n            char = next_char\n            raise_unterminated_quoted delimiter_state if char == '\\0'\n            next_char :STRING\n            if char == '/' || char.ascii_whitespace?\n              @token.value = char.to_s\n            else\n              @token.value = \"\\\\#{char}\"\n            end\n          else\n            case char = next_char\n            when '\\\\'\n              string_token_escape_value \"\\\\\"\n            when string_end\n              string_token_escape_value string_end.to_s\n            when string_nest\n              string_token_escape_value string_nest.to_s\n            when '#'\n              string_token_escape_value \"#\"\n            when 'a'\n              string_token_escape_value \"\\a\"\n            when 'b'\n              string_token_escape_value \"\\b\"\n            when 'n'\n              string_token_escape_value \"\\n\"\n            when 'r'\n              string_token_escape_value \"\\r\"\n            when 't'\n              string_token_escape_value \"\\t\"\n            when 'v'\n              string_token_escape_value \"\\v\"\n            when 'f'\n              string_token_escape_value \"\\f\"\n            when 'e'\n              string_token_escape_value \"\\e\"\n            when 'x'\n              value = consume_string_hex_escape\n              next_char :STRING\n              @token.value = String.new(1) do |buffer|\n                buffer[0] = value\n                {1, 0}\n              end\n            when 'u'\n              value = consume_string_unicode_escape\n              next_char :STRING\n              @token.value = value\n            when '0', '1', '2', '3', '4', '5', '6', '7'\n              value = consume_octal_escape(char)\n              next_char :STRING\n              @token.value = String.new(1) do |buffer|\n                buffer[0] = value\n                {1, 0}\n              end\n            when '\\r', '\\n'\n              handle_slash_r_slash_n_or_slash_n\n              incr_line_number\n              @token.line_number = @line_number\n\n              # Skip until the next non-whitespace char\n              while true\n                char = next_char\n                case char\n                when '\\0'\n                  raise_unterminated_quoted delimiter_state\n                when '\\n'\n                  incr_line_number\n                  @token.line_number = @line_number\n                when .ascii_whitespace?\n                  # Continue\n                else\n                  break\n                end\n              end\n              next_string_token delimiter_state\n            when '\\0'\n              raise_unterminated_quoted delimiter_state\n            else\n              @token.type = :STRING\n              @token.value = current_char.to_s\n              @token.invalid_escape = true\n              next_char\n            end\n          end\n        else\n          @token.type = :STRING\n          @token.value = current_char.to_s\n          next_char\n        end\n      when '#'\n        if delimiter_state.allow_escapes\n          if peek_next_char == '{'\n            next_char\n            next_char :INTERPOLATION_START\n          else\n            next_char :STRING\n            @token.value = \"#\"\n          end\n        else\n          next_char :STRING\n          @token.value = \"#\"\n        end\n      when '\\r', '\\n'\n        is_slash_r = handle_slash_r_slash_n_or_slash_n\n        next_char\n        incr_line_number 1\n        @token.line_number = @line_number\n        @token.column_number = @column_number\n\n        if delimiter_state.kind.heredoc?\n          unless check_heredoc_end delimiter_state\n            next_string_token_noescape delimiter_state\n            @token.value = string_range(start)\n          end\n        else\n          @token.type = :STRING\n          @token.value = is_slash_r ? \"\\r\\n\" : \"\\n\"\n        end\n      else\n        next_string_token_noescape delimiter_state\n        @token.value = string_range(start)\n      end\n\n      set_token_raw_from_start(start)\n\n      @token\n    end\n\n    def next_string_token_noescape(delimiter_state)\n      string_end = delimiter_state.end\n      string_nest = delimiter_state.nest\n\n      while !current_char.in?(string_end, string_nest, '\\0', '\\\\', '#', '\\r', '\\n')\n        next_char\n      end\n\n      @token.type = :STRING\n    end\n\n    def check_heredoc_end(delimiter_state)\n      string_end = delimiter_state.end.to_s\n      old_pos = current_pos\n      old_column = @column_number\n\n      while current_char.in?(' ', '\\t')\n        next_char\n      end\n\n      indent = @column_number - 1\n\n      if string_end.starts_with?(current_char)\n        reached_end = false\n\n        string_end.each_char do |c|\n          unless c == current_char\n            reached_end = false\n            break\n          end\n          next_char\n          reached_end = true\n        end\n\n        if reached_end &&\n           (current_char.in?('\\n', '\\0') ||\n           (current_char == '\\r' && peek_next_char == '\\n' && next_char))\n          @token.type = :DELIMITER_END\n          @token.delimiter_state = delimiter_state.with_heredoc_indent(indent)\n          return true\n        end\n      end\n\n      @reader.pos = old_pos\n      @column_number = old_column\n      @token.column_number = @column_number\n\n      false\n    end\n\n    def raise_unterminated_quoted(delimiter_state)\n      msg = case delimiter_state.kind\n            when .command? then \"Unterminated command literal\"\n            when .regex?   then \"Unterminated regular expression\"\n            when .heredoc?\n              \"Unterminated heredoc: can't find \\\"#{delimiter_state.end}\\\" anywhere before the end of file\"\n            when .string? then \"Unterminated string literal\"\n            else\n              ::raise \"unreachable\"\n            end\n      raise msg, @line_number, @column_number\n    end\n\n    def next_macro_token(macro_state, skip_whitespace)\n      reset_token\n\n      nest = macro_state.nest\n      control_nest = macro_state.control_nest\n      whitespace = macro_state.whitespace\n      delimiter_state = macro_state.delimiter_state\n      beginning_of_line = macro_state.beginning_of_line\n      comment = macro_state.comment\n      heredocs = macro_state.heredocs\n      yields = false\n\n      if skip_whitespace\n        skip_macro_whitespace\n      end\n\n      @token.location = nil\n      @token.line_number = @line_number\n      @token.column_number = @column_number\n\n      start = current_pos\n\n      if current_char == '\\0'\n        @token.type = :EOF\n        return @token\n      end\n\n      if current_char == '\\\\' && peek_next_char == '{'\n        beginning_of_line = false\n        next_char\n        start = current_pos\n        if next_char == '%'\n          while (char = next_char_check_line).ascii_whitespace?\n          end\n\n          case char\n          when 'e'\n            if char_sequence?('n', 'd') && !ident_part_or_end?(peek_next_char)\n              next_char\n              nest -= 1\n            end\n          when 'f'\n            if char_sequence?('o', 'r') && !ident_part_or_end?(peek_next_char)\n              next_char\n              nest += 1\n            end\n          when 'i'\n            if next_char == 'f' && !ident_part_or_end?(peek_next_char)\n              next_char\n              nest += 1\n            end\n          when 'u'\n            if char_sequence?('n', 'l', 'e', 's', 's') && !ident_part_or_end?(peek_next_char)\n              next_char\n              nest += 1\n            end\n          else\n            # no nesting\n          end\n        end\n\n        @token.type = :MACRO_LITERAL\n        @token.value = string_range(start)\n        @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n        set_token_raw_from_start(start)\n        return @token\n      end\n\n      if current_char == '\\\\' && peek_next_char == '%'\n        beginning_of_line = false\n        next_char\n        next_char :MACRO_LITERAL\n        @token.value = \"%\"\n        @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n        @token.raw = \"%\"\n        return @token\n      end\n\n      if current_char == '{'\n        case next_char\n        when '{'\n          beginning_of_line = false\n          next_char :MACRO_EXPRESSION_START\n          @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n          return @token\n        when '%'\n          beginning_of_line = false\n          next_char :MACRO_CONTROL_START\n          @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n          return @token\n        else\n          # Make sure to decrease the '}' count if inside an interpolation\n          @macro_curly_count += 1 if @macro_curly_count > 0\n        end\n      end\n\n      if comment || (!delimiter_state && current_char == '#')\n        comment = true\n        char = current_char\n        char = next_char if current_char == '#'\n        while true\n          case char\n          when '\\n'\n            comment = false\n            beginning_of_line = true\n            whitespace = true\n            next_char\n            incr_line_number\n            @token.line_number = @line_number\n            @token.column_number = @column_number\n            break\n          when '{'\n            break\n          when '\\\\'\n            if peek_next_char == '{'\n              break\n            else\n              char = next_char\n            end\n          when '\\0'\n            raise \"unterminated macro\"\n          else\n            char = next_char\n          end\n        end\n        @token.type = :MACRO_LITERAL\n        @token.value = string_range(start)\n        @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n        set_token_raw_from_start(start)\n        return @token\n      end\n\n      if !delimiter_state && current_char == '%' && ident_start?(peek_next_char)\n        char = next_char\n        if char == 'q' && peek_next_char.in?('(', '<', '[', '{', '|')\n          next_char\n          delimiter_state = Token::DelimiterState.percent_literal(:string, current_char, closing_char)\n          next_char\n        elsif char == 'Q' && peek_next_char.in?('(', '<', '[', '{', '|')\n          next_char\n          delimiter_state = Token::DelimiterState.percent_literal(:string, current_char, closing_char)\n          next_char\n        elsif char == 'i' && peek_next_char.in?('(', '<', '[', '{', '|')\n          next_char\n          delimiter_state = Token::DelimiterState.percent_literal(:symbol_array, current_char, closing_char)\n          next_char\n        elsif char == 'w' && peek_next_char.in?('(', '<', '[', '{', '|')\n          next_char\n          delimiter_state = Token::DelimiterState.percent_literal(:string_array, current_char, closing_char)\n          next_char\n        elsif char == 'x' && peek_next_char.in?('(', '<', '[', '{', '|')\n          next_char\n          delimiter_state = Token::DelimiterState.percent_literal(:command, current_char, closing_char)\n          next_char\n        elsif char == 'r' && peek_next_char.in?('(', '<', '[', '{', '|')\n          next_char\n          delimiter_state = Token::DelimiterState.percent_literal(:regex, current_char, closing_char)\n          next_char\n        else\n          start = current_pos\n          while ident_part?(char)\n            char = next_char\n          end\n          beginning_of_line = false\n          @token.type = :MACRO_VAR\n          @token.value = string_range_from_pool(start)\n          @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n          return @token\n        end\n      end\n\n      if !delimiter_state && current_char == 'e'\n        if next_char == 'n'\n          beginning_of_line = false\n          case next_char\n          when 'd'\n            if whitespace && !ident_part_or_end?(peek_next_char) && peek_next_char != ':'\n              if nest == 0 && control_nest == 0\n                next_char :MACRO_END\n                @token.macro_state = Token::MacroState.default\n                return @token\n              else\n                nest -= 1\n                whitespace = current_char.ascii_whitespace?\n                next_char\n              end\n            end\n          when 'u'\n            if !delimiter_state && whitespace && next_char == 'm' && !ident_part_or_end?(next_char)\n              char = current_char\n              nest += 1\n              whitespace = true\n            end\n          else\n            # not a keyword\n          end\n        else\n          whitespace = false\n          beginning_of_line = false\n        end\n      end\n\n      char = current_char\n\n      until char.in?('{', '\\0') ||\n            (char == '\\\\' && peek_next_char.in?('{', '%')) ||\n            (whitespace && !delimiter_state && char == 'e')\n        case char\n        when '\\n'\n          incr_line_number 0\n          whitespace = true\n          beginning_of_line = true\n          char = next_char\n\n          if !delimiter_state && heredocs && !heredocs.empty?\n            delimiter_state = heredocs.shift\n          end\n\n          if delimiter_state && delimiter_state.kind.heredoc? && check_heredoc_end(delimiter_state)\n            char = current_char\n            delimiter_state = heredocs.try &.shift?\n          end\n\n          next\n        when '\\\\'\n          char = next_char\n          if delimiter_state\n            case char\n            when delimiter_state.end\n              char = next_char\n            when '\\\\'\n              char = next_char\n            end\n            whitespace = false\n          else\n            whitespace = false\n          end\n          next\n        when '\\'', '\"'\n          if delimiter_state\n            delimiter_state = nil if delimiter_state.end == char\n          else\n            delimiter_state = Token::DelimiterState.new(:string, char, char)\n          end\n          whitespace = false\n        when '%'\n          case char = peek_next_char\n          when '(', '[', '<', '{', '|'\n            next_char\n            delimiter_state = Token::DelimiterState.percent_literal(:string, char, closing_char)\n          else\n            whitespace = false\n            # Don't break if this looks like a prefixed percent literal that will\n            # be handled before the main loop (e.g., %Q|...|, %w|...|, etc.)\n            if !delimiter_state && ident_start?(char)\n              is_percent_literal =\n                char.in?('q', 'Q', 'w', 'i', 'r', 'x') &&\n                  lookahead { next_char; peek_next_char.in?('(', '<', '[', '{', '|') }\n              break unless is_percent_literal\n            end\n          end\n        when '#'\n          if delimiter_state\n            # If it's \"#{...\", we don't want \"#{{{\" to parse it as \"# {{ {\", but as \"#{ {{\"\n            # (macro expression inside a string interpolation)\n            if peek_next_char == '{'\n              char = next_char\n\n              # We should now consider things that follow as crystal expressions,\n              # so we reset the delimiter state but save it in a stack\n              @macro_curly_count += 1\n              @delimiter_state_stack.push delimiter_state\n              delimiter_state = nil\n            end\n            whitespace = false\n          else\n            break\n          end\n        when '}'\n          if delimiter_state && delimiter_state.end == '}'\n            delimiter_state = delimiter_state.with_open_count_delta(-1)\n            if delimiter_state.open_count == 0\n              delimiter_state = nil\n            end\n          elsif @macro_curly_count > 0\n            # Once we find the final '}' that closes the interpolation,\n            # we are back inside the delimiter\n            if @macro_curly_count == 1\n              delimiter_state = @delimiter_state_stack.pop\n            end\n            @macro_curly_count -= 1\n          end\n        when '<'\n          if !delimiter_state && @delimiter_state_stack.empty? && (heredoc_delimiter_state = lookahead { check_heredoc_start })\n            heredocs ||= [] of Token::DelimiterState\n            heredocs << heredoc_delimiter_state\n            char = current_char\n            next\n          end\n        else\n          if !delimiter_state && whitespace && lookahead { char == 'y' && char_sequence?('i', 'e', 'l', 'd') && !ident_part_or_end?(peek_next_char) }\n            yields = true\n            char = current_char\n            whitespace = true\n            beginning_of_line = false\n          elsif !delimiter_state && whitespace && (keyword = lookahead { macro_starts_with_keyword?(beginning_of_line) })\n            char = current_char\n\n            nest += 1 unless keyword.abstract_def?\n            whitespace = true\n            beginning_of_line = false\n            next\n          else\n            char = current_char\n\n            if delimiter_state\n              # For symmetric delimiters (like ||), don't use nesting logic\n              if delimiter_state.nest == delimiter_state.end\n                if char == delimiter_state.end\n                  delimiter_state = nil\n                end\n              else\n                # For paired delimiters (like (), [], {}, <>), use nesting logic\n                case char\n                when delimiter_state.nest\n                  delimiter_state = delimiter_state.with_open_count_delta(+1)\n                when delimiter_state.end\n                  delimiter_state = delimiter_state.with_open_count_delta(-1)\n                  if delimiter_state.open_count == 0\n                    delimiter_state = nil\n                  end\n                end\n              end\n            end\n\n            # If an assignment comes, we accept if/unless/while/until as nesting\n            if char == '=' && peek_next_char.ascii_whitespace?\n              whitespace = false\n              beginning_of_line = true\n            else\n              whitespace = char.ascii_whitespace? || char.in?(';', '(', '[', '{')\n              if beginning_of_line && !whitespace\n                beginning_of_line = false\n              end\n            end\n          end\n        end\n        char = next_char\n      end\n\n      @token.type = :MACRO_LITERAL\n      @token.value = string_range(start)\n      @token.macro_state = Token::MacroState.new(whitespace, nest, control_nest, delimiter_state, beginning_of_line, yields, comment, heredocs)\n      set_token_raw_from_start(start)\n\n      @token\n    end\n\n    def lookahead(preserve_token_on_fail = false, &)\n      old_pos, old_line, old_column = current_pos, @line_number, @column_number\n      @temp_token.copy_from(@token) if preserve_token_on_fail\n\n      result = yield\n      unless result\n        self.current_pos, @line_number, @column_number = old_pos, old_line, old_column\n        @token.copy_from(@temp_token) if preserve_token_on_fail\n      end\n      result\n    end\n\n    def peek_ahead(&)\n      result = uninitialized typeof(yield)\n      lookahead(preserve_token_on_fail: true) do\n        result = yield\n        nil\n      end\n      result\n    end\n\n    def skip_macro_whitespace\n      start = current_pos\n      while current_char.ascii_whitespace?\n        if current_char == '\\n'\n          incr_line_number 0\n        end\n        next_char\n      end\n      if @wants_raw\n        string_range(start)\n      else\n        \"\"\n      end\n    end\n\n    enum MacroKeywordState\n      AbstractDef\n      Other\n    end\n\n    def macro_starts_with_keyword?(beginning_of_line) : MacroKeywordState?\n      case current_char\n      when 'a'\n        case next_char\n        when 'b'\n          if char_sequence?('s', 't', 'r', 'a', 'c', 't') && next_char.whitespace?\n            case next_char\n            when 'd'\n              MacroKeywordState::AbstractDef if char_sequence?('e', 'f') && peek_not_ident_part_or_end_next_char\n            when 'c'\n              MacroKeywordState::Other if char_sequence?('l', 'a', 's', 's') && peek_not_ident_part_or_end_next_char\n            when 's'\n              MacroKeywordState::Other if char_sequence?('t', 'r', 'u', 'c', 't') && peek_not_ident_part_or_end_next_char\n            end\n          end\n        when 'n'\n          MacroKeywordState::Other if char_sequence?('n', 'o', 't', 'a', 't', 'i', 'o', 'n') && peek_not_ident_part_or_end_next_char\n        end\n      when 'b'\n        MacroKeywordState::Other if char_sequence?('e', 'g', 'i', 'n') && peek_not_ident_part_or_end_next_char\n      when 'c'\n        case next_char\n        when 'a'\n          MacroKeywordState::Other if char_sequence?('s', 'e') && peek_not_ident_part_or_end_next_char\n        when 'l'\n          MacroKeywordState::Other if char_sequence?('a', 's', 's') && peek_not_ident_part_or_end_next_char\n        end\n      when 'd'\n        case next_char\n        when 'o'\n          MacroKeywordState::Other if peek_not_ident_part_or_end_next_char\n        when 'e'\n          MacroKeywordState::Other if next_char == 'f' && peek_not_ident_part_or_end_next_char\n        end\n      when 'f'\n        MacroKeywordState::Other if char_sequence?('u', 'n') && peek_not_ident_part_or_end_next_char\n      when 'i'\n        MacroKeywordState::Other if beginning_of_line && next_char == 'f' && peek_not_ident_part_or_end_next_char\n      when 'l'\n        MacroKeywordState::Other if char_sequence?('i', 'b') && peek_not_ident_part_or_end_next_char\n      when 'm'\n        case next_char\n        when 'a'\n          MacroKeywordState::Other if char_sequence?('c', 'r', 'o') && peek_not_ident_part_or_end_next_char\n        when 'o'\n          MacroKeywordState::Other if char_sequence?('d', 'u', 'l', 'e') && peek_not_ident_part_or_end_next_char\n        end\n      when 's'\n        case next_char\n        when 'e'\n          MacroKeywordState::Other if char_sequence?('l', 'e', 'c', 't') && !ident_part_or_end?(peek_next_char) && next_char\n        when 't'\n          MacroKeywordState::Other if char_sequence?('r', 'u', 'c', 't') && !ident_part_or_end?(peek_next_char) && next_char\n        end\n      when 'u'\n        if next_char == 'n'\n          case next_char\n          when 'i'\n            MacroKeywordState::Other if char_sequence?('o', 'n') && peek_not_ident_part_or_end_next_char\n          when 'l'\n            MacroKeywordState::Other if beginning_of_line && char_sequence?('e', 's', 's') && peek_not_ident_part_or_end_next_char\n          when 't'\n            MacroKeywordState::Other if beginning_of_line && char_sequence?('i', 'l') && peek_not_ident_part_or_end_next_char\n          end\n        end\n      when 'w'\n        MacroKeywordState::Other if beginning_of_line && char_sequence?('h', 'i', 'l', 'e') && peek_not_ident_part_or_end_next_char\n      end\n    end\n\n    def check_heredoc_start\n      return nil unless current_char == '<' && char_sequence?('<', '-')\n\n      has_single_quote = false\n      found_closing_single_quote = false\n\n      if next_char == '\\''\n        has_single_quote = true\n        next_char\n      end\n\n      return nil unless ident_part?(current_char)\n\n      start_here = current_pos\n      end_here = 0\n\n      while true\n        char = next_char\n        case\n        when char == '\\r'\n          if peek_next_char == '\\n'\n            end_here = current_pos\n            next_char\n            break\n          else\n            return nil\n          end\n        when char == '\\n'\n          end_here = current_pos\n          break\n        when ident_part?(char)\n          # ok\n        when char == '\\0'\n          return nil\n        when has_single_quote\n          if char == '\\''\n            found_closing_single_quote = true\n            end_here = current_pos\n            next_char\n            break\n          else\n            # wait until another quote\n          end\n        else\n          end_here = current_pos\n          break\n        end\n      end\n\n      return nil if has_single_quote && !found_closing_single_quote\n\n      here = string_range(start_here, end_here)\n      Token::DelimiterState.new(:heredoc, here, here, allow_escapes: !has_single_quote)\n    end\n\n    def consume_octal_escape(char)\n      value = char - '0'\n      count = 1\n      while count <= 3 && '0' <= peek_next_char < '8'\n        next_char\n        value = value * 8 + (current_char - '0')\n        count += 1\n      end\n      if value >= 256\n        raise \"octal value too big\"\n      end\n      value.to_u8\n    end\n\n    def consume_char_unicode_escape\n      char = peek_next_char\n      if char == '{'\n        next_char\n        consume_braced_unicode_escape\n      else\n        consume_non_braced_unicode_escape\n      end\n    end\n\n    def consume_string_hex_escape\n      char = next_char\n      high = char.to_i?(16)\n      raise \"invalid hex escape\" unless high\n\n      char = next_char\n      low = char.to_i?(16)\n      raise \"invalid hex escape\" unless low\n\n      ((high << 4) | low).to_u8\n    end\n\n    def consume_string_unicode_escape\n      char = peek_next_char\n      if char == '{'\n        next_char\n        consume_string_unicode_brace_escape\n      else\n        consume_non_braced_unicode_escape.chr.to_s\n      end\n    end\n\n    def consume_string_unicode_brace_escape\n      String.build do |str|\n        while true\n          str << consume_braced_unicode_escape(allow_spaces: true).chr\n          break unless current_char == ' '\n        end\n      end\n    end\n\n    def consume_non_braced_unicode_escape\n      codepoint = 0\n      4.times do\n        hex_value = next_char.to_i?(16) || expected_hexadecimal_character_in_unicode_escape\n        codepoint = 16 &* codepoint &+ hex_value\n      end\n      if 0xD800 <= codepoint <= 0xDFFF\n        raise \"invalid unicode codepoint (surrogate half)\"\n      end\n      codepoint\n    end\n\n    def consume_braced_unicode_escape(allow_spaces = false)\n      codepoint = 0\n      found_curly = false\n      found_space = false\n      found_digit = false\n      char = '\\0'\n\n      6.times do\n        char = next_char\n        case char\n        when '}'\n          found_curly = true\n          break\n        when ' '\n          if allow_spaces\n            found_space = true\n            break\n          else\n            expected_hexadecimal_character_in_unicode_escape\n          end\n        else\n          hex_value = char.to_i?(16) || expected_hexadecimal_character_in_unicode_escape\n          codepoint = 16 &* codepoint &+ hex_value\n          found_digit = true\n        end\n      end\n\n      if !found_digit\n        expected_hexadecimal_character_in_unicode_escape\n      elsif codepoint > 0x10FFFF\n        raise \"invalid unicode codepoint (too large)\"\n      elsif 0xD800 <= codepoint <= 0xDFFF\n        raise \"invalid unicode codepoint (surrogate half)\"\n      end\n\n      unless found_space\n        unless found_curly\n          char = next_char\n        end\n\n        unless char == '}'\n          raise \"expected '}' to close unicode escape\"\n        end\n      end\n\n      codepoint\n    end\n\n    def expected_hexadecimal_character_in_unicode_escape\n      raise \"expected hexadecimal character in unicode escape\"\n    end\n\n    def string_token_escape_value(value)\n      next_char :STRING\n      @token.value = value\n    end\n\n    def delimited_pair(kind : Token::DelimiterKind, string_nest, string_end, start, allow_escapes = true, advance = true)\n      next_char if advance\n      @token.type = :DELIMITER_START\n      @token.delimiter_state = Token::DelimiterState.new(kind, string_nest, string_end, allow_escapes)\n      set_token_raw_from_start(start)\n    end\n\n    def next_string_array_token\n      while true\n        if current_char == '\\n'\n          next_char\n          incr_line_number 1\n        elsif current_char.ascii_whitespace?\n          next_char\n        else\n          break\n        end\n      end\n\n      reset_token\n\n      if current_char == @token.delimiter_state.end\n        @token.raw = current_char.to_s if @wants_raw\n        next_char :STRING_ARRAY_END\n        return @token\n      end\n\n      start = current_pos\n      sub_start = start\n      value = String::Builder.new\n\n      escaped = false\n      while true\n        case current_char\n        when Char::ZERO\n          break # raise is handled by parser\n        when @token.delimiter_state.end\n          unless escaped\n            # For symmetric delimiters (like ||), don't use nesting logic\n            if @token.delimiter_state.nest == @token.delimiter_state.end || @token.delimiter_state.open_count == 0\n              break\n            else\n              @token.delimiter_state = @token.delimiter_state.with_open_count_delta(-1)\n            end\n          end\n        when @token.delimiter_state.nest\n          unless @token.delimiter_state.nest == @token.delimiter_state.end || escaped\n            @token.delimiter_state = @token.delimiter_state.with_open_count_delta(+1)\n          end\n        when .ascii_whitespace?\n          break unless escaped\n        else\n          if escaped\n            value << '\\\\'\n          end\n        end\n\n        escaped = current_char == '\\\\'\n        if escaped\n          value.write @reader.string.to_slice[sub_start, current_pos - sub_start]\n          sub_start = current_pos + 1\n        end\n\n        next_char\n      end\n\n      if start == current_pos\n        @token.type = :EOF\n        return @token\n      end\n\n      value.write @reader.string.to_slice[sub_start, current_pos - sub_start]\n\n      @token.type = :STRING\n      @token.value = value.to_s\n      set_token_raw_from_start(start)\n\n      @token\n    end\n\n    def consume_loc_pragma\n      case current_char\n      when '\"'\n        delimited_pair :string, '\"', '\"',\n          start: current_pos\n\n        state = @token.delimiter_state\n\n        filename = String.build do |str|\n          while (token = next_string_token(state)).type.string?\n            str << token.value\n          end\n        end\n\n        incr_column_number %(#<loc:\").size - 1\n\n        unless current_char == ','\n          raise \"expected ',' in loc pragma after filename\"\n        end\n        next_char\n\n        line_number = 0\n        while true\n          case current_char\n          when '0'..'9'\n            line_number = 10 * line_number + (current_char - '0').to_i\n          when ','\n            next_char\n            break\n          else\n            raise \"expected digit or ',' in loc pragma for line number\"\n          end\n          next_char\n        end\n\n        column_number = 0\n        while true\n          case current_char\n          when '0'..'9'\n            column_number = 10 * column_number + (current_char - '0').to_i\n          when '>'\n            next_char\n            break\n          else\n            raise \"expected digit or '>' in loc pragma for column_number number\"\n          end\n          next_char\n        end\n\n        set_location filename, line_number, column_number\n      when 'p'\n        # skip 'p'\n        next_char_no_column_increment\n\n        case current_char\n        when 'o'\n          unless char_sequence?('p', '>', column_increment: false)\n            raise %(expected #<loc:push>, #<loc:pop> or #<loc:\"...\">)\n          end\n\n          # skip '>'\n          next_char_no_column_increment\n\n          incr_column_number 10 # == \"#<loc:pop>\".size\n\n          pop_location\n        when 'u'\n          unless char_sequence?('s', 'h', '>', column_increment: false)\n            raise %(expected #<loc:push>, #<loc:pop> or #<loc:\"...\">)\n          end\n\n          # skip '>'\n          next_char_no_column_increment\n\n          incr_column_number 11 # == \"#<loc:push>\".size\n\n          @token.line_number = @line_number\n          @token.column_number = @column_number\n          push_location\n        else\n          raise %(expected #<loc:push>, #<loc:pop> or #<loc:\"...\">)\n        end\n      else\n        raise %(expected #<loc:push>, #<loc:pop> or #<loc:\"...\">)\n      end\n    end\n\n    private def consume_heredoc_start(start)\n      has_single_quote = false\n      found_closing_single_quote = false\n\n      if next_char == '\\''\n        has_single_quote = true\n        next_char\n      end\n\n      unless ident_start?(current_char)\n        raise \"heredoc identifier starts with invalid character\"\n      end\n\n      start_here = current_pos\n      end_here = 0\n\n      while true\n        char = next_char\n        case\n        when char == '\\r'\n          if peek_next_char == '\\n'\n            end_here = current_pos\n            next_char\n            break\n          else\n            raise \"expecting '\\\\n' after '\\\\r'\"\n          end\n        when char == '\\n'\n          end_here = current_pos\n          break\n        when ident_part?(char)\n          # ok\n        when char == '\\0'\n          raise \"Unexpected EOF on heredoc identifier\"\n        when has_single_quote\n          if char == '\\''\n            found_closing_single_quote = true\n            end_here = current_pos\n            char = next_char\n            raise \"Unexpected EOF on heredoc identifier\" if char == '\\0'\n            break\n          else\n            # wait until another quote\n          end\n        else\n          end_here = current_pos\n          break\n        end\n      end\n\n      if has_single_quote && !found_closing_single_quote\n        raise \"expecting closing single quote\"\n      end\n\n      here = string_range(start_here, end_here)\n\n      delimited_pair :heredoc, here, here, start, allow_escapes: !has_single_quote, advance: false\n    end\n\n    private def consume_symbol\n      case char = current_char\n      when ':'\n        next_char :OP_COLON_COLON\n      when '+'\n        next_char_and_symbol \"+\"\n      when '-'\n        next_char_and_symbol \"-\"\n      when '*'\n        if next_char == '*'\n          next_char_and_symbol \"**\"\n        else\n          symbol \"*\"\n        end\n      when '/'\n        case next_char\n        when '/'\n          next_char_and_symbol \"//\"\n        else\n          symbol \"/\"\n        end\n      when '='\n        case next_char\n        when '='\n          if next_char == '='\n            next_char_and_symbol \"===\"\n          else\n            symbol \"==\"\n          end\n        when '~'\n          next_char_and_symbol \"=~\"\n        else\n          unknown_token\n        end\n      when '!'\n        case next_char\n        when '='\n          next_char_and_symbol \"!=\"\n        when '~'\n          next_char_and_symbol \"!~\"\n        else\n          symbol \"!\"\n        end\n      when '<'\n        case next_char\n        when '='\n          if next_char == '>'\n            next_char_and_symbol \"<=>\"\n          else\n            symbol \"<=\"\n          end\n        when '<'\n          next_char_and_symbol \"<<\"\n        else\n          symbol \"<\"\n        end\n      when '>'\n        case next_char\n        when '='\n          next_char_and_symbol \">=\"\n        when '>'\n          next_char_and_symbol \">>\"\n        else\n          symbol \">\"\n        end\n      when '&'\n        case next_char\n        when '+'\n          next_char_and_symbol \"&+\"\n        when '-'\n          next_char_and_symbol \"&-\"\n        when '*'\n          case next_char\n          when '*'\n            next_char_and_symbol \"&**\"\n          else\n            symbol \"&*\"\n          end\n        else\n          symbol \"&\"\n        end\n      when '|'\n        next_char_and_symbol \"|\"\n      when '^'\n        next_char_and_symbol \"^\"\n      when '~'\n        next_char_and_symbol \"~\"\n      when '%'\n        next_char_and_symbol \"%\"\n      when '['\n        if next_char == ']'\n          case next_char\n          when '='\n            next_char_and_symbol \"[]=\"\n          when '?'\n            next_char_and_symbol \"[]?\"\n          else\n            symbol \"[]\"\n          end\n        else\n          unknown_token\n        end\n      when '\"'\n        line = @line_number\n        column = @column_number\n        start = current_pos + 1\n        io = IO::Memory.new\n        while true\n          char = next_char\n          case char\n          when '\\\\'\n            case char = next_char\n            when 'a'\n              io << '\\a'\n            when 'b'\n              io << '\\b'\n            when 'n'\n              io << '\\n'\n            when 'r'\n              io << '\\r'\n            when 't'\n              io << '\\t'\n            when 'v'\n              io << '\\v'\n            when 'f'\n              io << '\\f'\n            when 'e'\n              io << '\\e'\n            when 'x'\n              io.write_byte consume_string_hex_escape\n            when 'u'\n              io << consume_string_unicode_escape\n            when '0', '1', '2', '3', '4', '5', '6', '7'\n              io.write_byte consume_octal_escape(char)\n            when '\\n'\n              incr_line_number nil\n              io << '\\n'\n            when '\\0'\n              raise \"unterminated quoted symbol\", line, column\n            else\n              io << char\n            end\n          when '\"'\n            break\n          when '\\0'\n            raise \"unterminated quoted symbol\", line, column\n          else\n            io << char\n          end\n        end\n\n        @token.type = :SYMBOL\n        @token.value = io.to_s\n        next_char\n        set_token_raw_from_start(start - 2)\n      else\n        if ident_start?(char)\n          start = current_pos\n          while ident_part?(next_char)\n            # Nothing to do\n          end\n          if current_char == '?' || (current_char.in?('!', '=') && peek_next_char != '=')\n            next_char\n          end\n          @token.type = :SYMBOL\n          @token.value = string_range_from_pool(start)\n          set_token_raw_from_start(start - 1)\n        else\n          @token.type = :OP_COLON\n        end\n      end\n    end\n\n    private def consume_variable(token_type : Token::Kind, start)\n      if ident_start?(current_char)\n        while ident_part?(next_char)\n          # Nothing to do\n        end\n        @token.type = token_type\n        @token.value = string_range_from_pool(start)\n      else\n        unknown_token\n      end\n    end\n\n    def set_location(filename, line_number, column_number)\n      @token.filename = @filename = filename\n      @token.line_number = @line_number = line_number\n      @token.column_number = @column_number = column_number\n    end\n\n    def pop_location\n      if @stacked\n        @stacked = false\n        @token.filename = @filename = @stacked_filename\n        @token.line_number = @line_number = @stacked_line_number\n        @token.column_number = @column_number = @stacked_column_number\n      end\n    end\n\n    def push_location\n      unless @stacked\n        @stacked = true\n        @stacked_filename, @stacked_line_number, @stacked_column_number = @filename, @line_number, @column_number\n      end\n    end\n\n    def incr_column_number(d = 1)\n      @column_number += d\n      @stacked_column_number += d if @stacked\n    end\n\n    def incr_line_number(column_number = 1)\n      @line_number += 1\n      @column_number = column_number if column_number\n      if @stacked\n        @stacked_line_number += 1\n        @stacked_column_number = column_number if column_number\n      end\n    end\n\n    private UNICODE_BIDI_CONTROL_CHARACTERS = {'\\u202A', '\\u202B', '\\u202C', '\\u202D', '\\u202E', '\\u2066', '\\u2067', '\\u2068', '\\u2069'}\n\n    private FORBIDDEN_ESCAPES = UNICODE_BIDI_CONTROL_CHARACTERS.to_h { |ch| {ch, ch.unicode_escape} }\n\n    # used by the formatter\n    def self.escape_forbidden_characters(string)\n      string.each_char do |ch|\n        if ch.in?(UNICODE_BIDI_CONTROL_CHARACTERS)\n          return string.gsub(FORBIDDEN_ESCAPES)\n        end\n      end\n\n      string\n    end\n\n    def next_char_no_column_increment\n      @reader.next_char.tap { check_reader_error }\n    end\n\n    private def check_reader_error\n      if error = @reader.error\n        ::raise InvalidByteSequenceError.new(\"Unexpected byte 0x#{error.to_s(16)} at position #{@reader.pos}, malformed UTF-8\")\n      end\n    end\n\n    def next_char\n      incr_column_number\n      next_char_no_column_increment\n    end\n\n    def next_char_check_line\n      char = next_char_no_column_increment\n      if char == '\\n'\n        incr_line_number\n      else\n        incr_column_number\n      end\n      char\n    end\n\n    def next_char(token_type : Token::Kind)\n      next_char\n      @token.type = token_type\n    end\n\n    def reset_token\n      @token.value = nil\n      @token.line_number = @line_number\n      @token.column_number = @column_number\n      @token.filename = @filename\n      @token.location = nil\n      @token.passed_backslash_newline = false\n      @token.doc_buffer = nil unless @token.type.space? || @token.type.newline?\n      @token.invalid_escape = false\n      @token_end_location = nil\n    end\n\n    def next_token_skip_space\n      next_token\n      skip_space\n    end\n\n    def next_token_skip_space_or_newline\n      next_token\n      skip_space_or_newline\n    end\n\n    def next_token_skip_statement_end\n      next_token\n      skip_statement_end\n    end\n\n    def next_token_never_a_symbol\n      @wants_symbol = false\n      next_token.tap { @wants_symbol = true }\n    end\n\n    def wants_def_or_macro_name(& : ->)\n      @wants_def_or_macro_name = true\n      yield\n    ensure\n      @wants_def_or_macro_name = false\n    end\n\n    def current_char\n      @reader.current_char\n    end\n\n    def peek_next_char\n      @reader.peek_next_char\n    end\n\n    def current_pos\n      @reader.pos\n    end\n\n    def current_pos=(pos)\n      @reader.pos = pos\n    end\n\n    def string\n      @reader.string\n    end\n\n    def string_range(start_pos)\n      string_range(start_pos, current_pos)\n    end\n\n    def string_range(start_pos, end_pos)\n      @reader.string.byte_slice(start_pos, end_pos - start_pos)\n    end\n\n    def string_range_from_pool(start_pos)\n      string_range_from_pool(start_pos, current_pos)\n    end\n\n    def string_range_from_pool(start_pos, end_pos)\n      @string_pool.get slice_range(start_pos, end_pos)\n    end\n\n    def slice_range(start_pos)\n      slice_range(start_pos, current_pos)\n    end\n\n    def slice_range(start_pos, end_pos)\n      Slice.new(@reader.string.to_unsafe + start_pos, end_pos - start_pos)\n    end\n\n    def self.ident_start?(char)\n      char.ascii_letter? || char == '_' || char.ord > 0x9F\n    end\n\n    def self.ident_part?(char)\n      ident_start?(char) || char.ascii_number?\n    end\n\n    def self.ident?(name)\n      !!name[0]?.try { |char| ident_start?(char) }\n    end\n\n    def self.setter?(name)\n      ident?(name) && name.ends_with?('=')\n    end\n\n    private delegate ident_start?, ident_part?, to: Lexer\n\n    def ident_part_or_end?(char)\n      ident_part?(char) || char.in?('?', '!')\n    end\n\n    def peek_not_ident_part_or_end_next_char\n      !ident_part_or_end?(peek_next_char) && peek_next_char != ':' && next_char\n    end\n\n    def closing_char(char = current_char)\n      case char\n      when '<' then '>'\n      when '(' then ')'\n      when '[' then ']'\n      when '{' then '}'\n      else          char\n      end\n    end\n\n    def skip_space\n      while @token.type.space?\n        next_token\n      end\n    end\n\n    def skip_space_or_newline\n      while (@token.type.space? || @token.type.newline?)\n        next_token\n      end\n    end\n\n    def skip_statement_end\n      while (@token.type.space? || @token.type.newline? || @token.type.op_semicolon?)\n        next_token\n      end\n    end\n\n    def handle_slash_r_slash_n_or_slash_n\n      is_slash_r = current_char == '\\r'\n      if is_slash_r\n        if next_char != '\\n'\n          raise \"expecting '\\\\n' after '\\\\r'\"\n        end\n      end\n      is_slash_r\n    end\n\n    private def char_sequence?(*tokens, column_increment : Bool = true)\n      tokens.all? do |token|\n        token == (column_increment ? next_char : next_char_no_column_increment)\n      end\n    end\n\n    def unknown_token\n      raise \"unknown token: #{current_char.inspect}\", @line_number, @column_number\n    end\n\n    def set_token_raw_from_start(start)\n      @token.raw = string_range(start) if @wants_raw\n    end\n\n    def raise(message, line_number = @line_number, column_number = @column_number, filename = @filename)\n      ::raise Crystal::SyntaxException.new(message, line_number, column_number, filename)\n    end\n\n    def raise(message, token : Token, size = nil)\n      ::raise Crystal::SyntaxException.new(message, token.line_number, token.column_number, token.filename, size)\n    end\n\n    def raise(message, location : Location)\n      raise message, location.line_number, location.column_number, location.filename\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/location.cr",
    "content": "# A location of an `ASTnode`, including its filename, line number and column number.\nclass Crystal::Location\n  include Comparable(self)\n\n  getter line_number\n  getter column_number\n  getter filename\n\n  def initialize(@filename : String | VirtualFile | Nil, @line_number : Int32, @column_number : Int32)\n  end\n\n  # Returns a location from a string representation. Used by compiler tools like\n  # `context` and `implementations`.\n  def self.parse(cursor : String, *, expand : Bool = false) : self\n    file_and_line, _, col = cursor.rpartition(':')\n    file, _, line = file_and_line.rpartition(':')\n\n    raise ArgumentError.new \"cursor location must be file:line:column\" if file.empty? || line.empty? || col.empty?\n\n    file = File.expand_path(file) if expand\n\n    line_number = line.to_i? || 0\n    if line_number <= 0\n      raise ArgumentError.new \"line must be a positive integer, not #{line}\"\n    end\n\n    column_number = col.to_i? || 0\n    if column_number <= 0\n      raise ArgumentError.new \"column must be a positive integer, not #{col}\"\n    end\n\n    new(file, line_number, column_number)\n  end\n\n  # Returns the directory name of this location's filename. If\n  # the filename is a VirtualFile, this is invoked on its expanded\n  # location.\n  def dirname : String?\n    original_filename.try { |filename| File.dirname(filename) }\n  end\n\n  # Returns the Location whose filename is a String, not a VirtualFile,\n  # traversing virtual file expanded locations.\n  def expanded_location\n    case filename = @filename\n    when String\n      self\n    when VirtualFile\n      filename.expanded_location.try &.expanded_location\n    else\n      nil\n    end\n  end\n\n  # Returns the Location whose filename is a String, not a VirtualFile,\n  # traversing virtual file expanded locations leading to the original user source code\n  def macro_location\n    case filename = @filename\n    when String\n      self\n    when VirtualFile\n      filename.macro.location.try(&.macro_location)\n    else\n      nil\n    end\n  end\n\n  # Returns the filename of the `expanded_location`\n  def original_filename\n    expanded_location.try &.filename.as?(String)\n  end\n\n  def between?(min, max)\n    return false unless min && max\n\n    min <= self && self <= max\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def to_s(io : IO) : Nil\n    io << filename << ':' << line_number << ':' << column_number\n  end\n\n  def pretty_print(pp)\n    pp.text to_s\n  end\n\n  def <=>(other)\n    self_file = @filename\n    other_file = other.filename\n    if self_file.is_a?(String) && other_file.is_a?(String) && self_file == other_file\n      {@line_number, @column_number} <=> {other.line_number, other.column_number}\n    else\n      nil\n    end\n  end\n\n  # Returns the number of lines between start and finish locations.\n  def self.lines(start, finish)\n    return unless start && finish && start.filename == finish.filename\n    start, finish = finish, start if finish < start\n\n    finish.line_number - start.line_number + 1\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/parser.cr",
    "content": "require \"set\"\nrequire \"./ast\"\nrequire \"./lexer\"\n\nmodule Crystal\n  class Parser < Lexer\n    enum ParseMode\n      Normal\n      Lib\n      LibStructOrUnion\n      Enum\n    end\n\n    record Unclosed, name : String, location : Location\n\n    property visibility : Visibility?\n    property def_nest : Int32\n    property fun_nest : Int32\n    property type_nest : Int32\n    getter? wants_doc : Bool\n    @block_arg_name : String?\n\n    def self.parse(str, string_pool : StringPool? = nil, var_scopes = [Set(String).new]) : ASTNode\n      new(str, string_pool, var_scopes).parse\n    end\n\n    def initialize(str, string_pool : StringPool? = nil, @var_scopes = [Set(String).new], warnings : WarningCollection? = nil)\n      super(str, string_pool, warnings)\n      @unclosed_stack = [] of Unclosed\n      @calls_super = false\n      @calls_initialize = false\n      @calls_previous_def = false\n      @uses_block_arg = false\n      @is_macro_def = false\n      @assigns_special_var = false\n      @def_nest = 0\n      @fun_nest = 0\n      @type_nest = 0\n      @is_constant_assignment = false\n\n      # Keeps track of current call args starting locations,\n      # so if we parse a type declaration exactly at those points we\n      # know we don't need to declare those as local variables in those scopes.\n      @call_args_start_locations = [] of Location\n      @temp_arg_count = 0\n      @in_macro_expression = false\n      @stop_on_yield = 0\n      @inside_c_struct = false\n      @wants_doc = false\n      @doc_enabled = false\n      @no_type_declaration = 0\n      @consuming_heredocs = false\n      @inside_interpolation = false\n\n      # This flags tells the parser where it has to consider a \"do\"\n      # as belonging to the current parsed call. For example when writing\n      #\n      # ```\n      # foo bar do\n      # end\n      # ```\n      #\n      # this flag will be set to false for `foo`, and when parsing\n      # `foo`'s arguments it will be set to true. When `bar` is parsed\n      # the `do` won't be considered as part of `bar`, and eventually\n      # be considered as part of `foo`.\n      #\n      # If `foo` is written with parentheses, for example:\n      #\n      # ```\n      # foo(bar do\n      # end)\n      # ```\n      #\n      # then this flag is set to `true` when parsing `foo`'s arguments.\n      @stop_on_do = false\n      @assigned_vars = [] of String\n    end\n\n    def wants_doc=(@wants_doc : Bool)\n      @doc_enabled = wants_doc\n    end\n\n    def parse\n      next_token_skip_statement_end\n\n      parse_expressions.tap { check :EOF }\n    end\n\n    def parse(mode : ParseMode)\n      case mode\n      when .normal?\n        parse\n      when .lib?\n        parse_lib_body\n      when .lib_struct_or_union?\n        parse_c_struct_or_union_body\n      else\n        parse_enum_body\n      end\n    end\n\n    def parse_expressions\n      preserve_stop_on_do { parse_expressions_internal }\n    end\n\n    def parse_expressions_internal\n      if end_token?\n        return Nop.new\n      end\n\n      exp = parse_multi_assign\n\n      slash_is_regex!\n      skip_statement_end\n\n      if end_token?\n        return exp\n      end\n\n      exps = [] of ASTNode\n      exps.push exp\n\n      loop do\n        exps << parse_multi_assign\n        skip_statement_end\n        break if end_token?\n      end\n\n      Expressions.from(exps)\n    end\n\n    def parse_multi_assign\n      location = @token.location\n\n      if @token.type.op_star?\n        lhs_splat = {index: 0, location: @token.location}\n        next_token_skip_space\n      end\n\n      last = parse_expression\n      skip_space\n\n      last_is_target = multi_assign_target?(last) || multi_assign_middle?(last)\n\n      case @token.type\n      when .op_comma?\n        unless last_is_target\n          unexpected_token if lhs_splat\n          raise \"Multiple assignment is not allowed for constants\" if last.is_a?(Path)\n          unexpected_token\n        end\n      when .newline?, .op_semicolon?\n        unexpected_token if lhs_splat && !multi_assign_middle?(last)\n        return last unless lhs_splat\n      else\n        if end_token?\n          unexpected_token if lhs_splat && !multi_assign_middle?(last)\n          return last unless lhs_splat\n        else\n          unexpected_token\n        end\n      end\n\n      exps = [] of ASTNode\n      exps << last\n\n      i = 0\n      assign_index = -1\n\n      while @token.type.op_comma?\n        if assign_index == -1 && multi_assign_middle?(last)\n          assign_index = i\n        end\n\n        i += 1\n\n        next_token_skip_space_or_newline\n        if @token.type.op_star?\n          if assign_index == -1\n            raise \"splat assignment already specified\" if lhs_splat\n            lhs_splat = {index: i, location: @token.location}\n          else\n            unexpected_token\n          end\n          next_token_skip_space\n        end\n\n        last = parse_op_assign(allow_ops: false)\n        if assign_index == -1 && !multi_assign_target?(last) && !multi_assign_middle?(last)\n          unexpected_token\n        end\n\n        exps << last\n        skip_space\n      end\n\n      if assign_index == -1 && multi_assign_middle?(last)\n        assign_index = i\n      end\n\n      if assign_index == -1\n        unexpected_token\n      end\n\n      targets = exps[0...assign_index].map { |exp| multi_assign_left_hand(exp) }\n\n      assign = exps[assign_index]\n      values = [] of ASTNode\n\n      case assign\n      when Assign\n        targets << multi_assign_left_hand(assign.target)\n        values << assign.value\n      when Call\n        assign.name = assign.name.byte_slice(0, assign.name.bytesize - 1)\n        targets << assign\n        values << assign.args.pop\n      else\n        raise \"BUG: multi_assign index expression can only be Assign or Call\"\n      end\n\n      if lhs_splat\n        lhs_splat_location = lhs_splat[:location]\n        lhs_splat_index = lhs_splat[:index]\n        targets[lhs_splat_index] = Splat.new(targets[lhs_splat_index]).at(lhs_splat_location)\n      end\n\n      values.concat exps[assign_index + 1..-1]\n      if values.size != 1\n        if lhs_splat\n          raise \"Multiple assignment count mismatch\", location if targets.size - 1 > values.size\n        else\n          raise \"Multiple assignment count mismatch\", location if targets.size != values.size\n        end\n      end\n\n      multi = MultiAssign.new(targets, values).at(location)\n      parse_expression_suffix multi, location\n    end\n\n    def multi_assign_target?(exp)\n      case exp\n      when Underscore, Var, InstanceVar, ClassVar, Global\n        true\n      when Call\n        !exp.has_parentheses? && !exp.block && (!exp.has_any_args? || exp.name == \"[]\")\n      else\n        false\n      end\n    end\n\n    def multi_assign_middle?(exp)\n      case exp\n      when Assign\n        true\n      when Call\n        Lexer.setter?(exp.name) || exp.name == \"[]=\"\n      else\n        false\n      end\n    end\n\n    def multi_assign_left_hand(exp)\n      if exp.is_a?(Path)\n        raise \"can't assign to constant in multiple assignment\", exp.location.not_nil!\n      end\n\n      if exp.is_a?(Call)\n        case obj = exp.obj\n        when Nil\n          if exp.args.empty?\n            exp = Var.new(exp.name).at(exp)\n          end\n        when Global\n          if obj.name == \"$~\" && exp.name == \"[]\"\n            raise \"global match data cannot be assigned to\", obj.location.not_nil!\n          end\n        end\n      end\n\n      if exp.is_a?(Var)\n        if exp.name == \"self\"\n          raise \"can't change the value of self\", exp.location.not_nil!\n        end\n        push_var exp\n      end\n      exp\n    end\n\n    def parse_expression\n      location = @token.location\n      atomic = parse_op_assign\n      parse_expression_suffix atomic, location\n    end\n\n    def parse_expression_suffix(atomic, location)\n      while true\n        case @token.type\n        when .space?\n          next_token\n        when .ident?\n          case @token.value\n          when Keyword::IF\n            atomic = parse_expression_suffix(location) { |exp| If.new(exp, atomic) }\n          when Keyword::UNLESS\n            atomic = parse_expression_suffix(location) { |exp| Unless.new(exp, atomic) }\n          when Keyword::WHILE\n            raise \"trailing `while` is not supported\", @token\n          when Keyword::UNTIL\n            raise \"trailing `until` is not supported\", @token\n          when Keyword::RESCUE\n            rescue_location = @token.location\n            next_token_skip_space\n            rescue_body = parse_op_assign\n            rescues = [Rescue.new(rescue_body).at(rescue_location).at_end(rescue_body)] of Rescue\n            if atomic.is_a?(Assign)\n              atomic.value = ex = ExceptionHandler.new(atomic.value, rescues)\n            else\n              atomic = ex = ExceptionHandler.new(atomic, rescues)\n            end\n            ex.at(location).at_end(rescue_body)\n            ex.suffix = true\n          when Keyword::ENSURE\n            ensure_location = @token.location\n            next_token_skip_space\n            ensure_body = parse_op_assign\n            if atomic.is_a?(Assign)\n              atomic.value = ex = ExceptionHandler.new(atomic.value, ensure: ensure_body)\n            else\n              atomic = ex = ExceptionHandler.new(atomic, ensure: ensure_body)\n            end\n            ex.at(location).at_end(ensure_body)\n            ex.ensure_location = ensure_location\n            ex.suffix = true\n          else\n            break\n          end\n        when .op_rparen?, .op_comma?, .op_semicolon?, .op_percent_rcurly?, .newline?, .eof?\n          break\n        else\n          if end_token?\n            break\n          else\n            unexpected_token\n          end\n        end\n      end\n      atomic\n    end\n\n    def parse_expression_suffix(location, &)\n      slash_is_regex!\n      next_token_skip_statement_end\n      exp = parse_op_assign_no_control\n      (yield exp).at(location).at_end(exp)\n    end\n\n    def parse_op_assign_no_control(allow_ops = true, allow_suffix = true)\n      check_void_expression_keyword\n      parse_op_assign(allow_ops, allow_suffix)\n    end\n\n    def parse_op_assign(allow_ops = true, allow_suffix = true)\n      doc = @token.doc\n      location = @token.location\n      start_token = @token\n\n      atomic = parse_question_colon\n\n      while true\n        name_location = @token.location\n\n        case @token.type\n        when .space?\n          next_token\n          next\n        when .ident?\n          unexpected_token unless allow_suffix\n          break\n        when .op_eq?\n          slash_is_regex!\n          break unless can_be_assigned?(atomic)\n\n          if atomic.is_a?(Call) && atomic.name == \"[]\"\n            next_token_skip_space_or_newline\n\n            atomic.name = \"[]=\"\n            atomic.name_size = 0\n            arg = parse_op_assign_no_control\n            atomic.args << arg\n            atomic.end_location = arg.end_location\n          else\n            if atomic.is_a?(Path) && (inside_def? || inside_fun? || @is_constant_assignment)\n              raise \"dynamic constant assignment. Constants can only be declared at the top level or inside other types.\"\n            end\n\n            @is_constant_assignment = true if atomic.is_a?(Path)\n\n            if atomic.is_a?(Var) && atomic.name == \"self\"\n              raise \"can't change the value of self\", location\n            end\n\n            if atomic.is_a?(Call) && (atomic.name.ends_with?('?') || atomic.name.ends_with?('!'))\n              unexpected_token token: start_token\n            end\n\n            atomic = Var.new(atomic.name).at(atomic) if atomic.is_a?(Call)\n\n            next_token_skip_space_or_newline\n\n            # Constants need a new scope for their value\n            case atomic\n            when Path\n              needs_new_scope = true\n            when InstanceVar\n              needs_new_scope = @def_nest == 0\n            when ClassVar\n              needs_new_scope = @def_nest == 0\n            when Var\n              @assigns_special_var = true if atomic.special_var?\n            else\n              needs_new_scope = false\n            end\n\n            atomic_value = with_isolated_var_scope(needs_new_scope) do\n              if @token.keyword?(:uninitialized) && (\n                   atomic.is_a?(Var) || atomic.is_a?(InstanceVar) ||\n                   atomic.is_a?(ClassVar) || atomic.is_a?(Global)\n                 )\n                push_var atomic\n                next_token_skip_space\n                type = parse_bare_proc_type\n                atomic = UninitializedVar.new(atomic, type).at(location).at_end(type)\n                return atomic\n              else\n                if atomic.is_a?(Var) && !var?(atomic.name)\n                  @assigned_vars.push atomic.name\n                  value = parse_op_assign_no_control\n                  @assigned_vars.pop\n                  value\n                else\n                  parse_op_assign_no_control\n                end\n              end\n            end\n\n            @is_constant_assignment = false if atomic.is_a?(Path)\n\n            push_var atomic\n\n            atomic = Assign.new(atomic, atomic_value).at(location)\n            atomic.doc = doc\n            atomic\n          end\n        when .assignment_operator?\n          unexpected_token unless allow_ops\n\n          break unless can_be_assigned?(atomic)\n\n          if atomic.is_a?(Path)\n            raise \"can't reassign to constant\"\n          end\n\n          if atomic.is_a?(Var) && atomic.name == \"self\"\n            raise \"can't change the value of self\", location\n          end\n\n          if atomic.is_a?(Call) && atomic.name != \"[]\" && !var_in_scope?(atomic.name)\n            raise \"'#{@token.type}' before definition of '#{atomic.name}'\"\n          end\n\n          push_var atomic\n          method = @token.type.to_s.byte_slice(0, @token.to_s.bytesize - 1)\n          next_token_skip_space_or_newline\n          value = parse_op_assign_no_control\n          atomic = OpAssign.new(atomic, method, value).at(location)\n          atomic.name_location = name_location\n        else\n          break\n        end\n        allow_ops = true\n      end\n\n      atomic\n    end\n\n    def parse_question_colon\n      cond = parse_range\n\n      while @token.type.op_question?\n        location = @token.location\n\n        check_void_value cond, location\n\n        next_token_skip_space_or_newline\n\n        @no_type_declaration += 1\n        true_val = parse_question_colon\n\n        skip_space_or_newline\n        check :OP_COLON\n        next_token_skip_space_or_newline\n\n        false_val = parse_question_colon\n        @no_type_declaration -= 1\n\n        cond = If.new(cond, true_val, false_val, ternary: true).at(cond).at_end(false_val)\n      end\n\n      cond\n    end\n\n    def parse_range\n      location = @token.location\n\n      if @token.type.op_period_period? || @token.type.op_period_period_period?\n        exp = Nop.new\n      else\n        exp = parse_or\n      end\n\n      while true\n        case @token.type\n        when .op_period_period?\n          exp = new_range(exp, location, false)\n        when .op_period_period_period?\n          exp = new_range(exp, location, true)\n        else\n          return exp\n        end\n      end\n    end\n\n    def new_range(exp, location, exclusive)\n      end_location = token_end_location\n      check_void_value exp, location\n      next_token_skip_space\n      check_void_expression_keyword\n      if end_token? ||\n         @token.type.op_rparen? ||\n         @token.type.op_comma? ||\n         @token.type.op_semicolon? ||\n         @token.type.op_eq_gt? ||\n         @token.type.newline?\n        right = Nop.new\n      else\n        right = parse_or\n        end_location = right.end_location\n      end\n      RangeLiteral.new(exp, right, exclusive).at(location).at_end(end_location)\n    end\n\n    macro parse_operator(name, next_operator, node, *operators, right_associative = false)\n      def parse_{{name.id}}\n        location = @token.location\n\n        left = parse_{{next_operator.id}}\n        while true\n          case @token.type\n          when .space?\n            next_token\n          when {{operators.map { |op| \".#{op.id}\".id }.splat}}\n            check_void_value left, location\n\n            method = @token.type.to_s\n            name_location = @token.location\n\n            slash_is_regex!\n            next_token_skip_space_or_newline\n            right = parse_{{(right_associative ? name : next_operator).id}}\n            left = ({{node.id}}).at(location).at_end(right)\n            left.name_location = name_location if left.is_a?(Call)\n          else\n            return left\n          end\n        end\n      end\n    end\n\n    parse_operator :or, :and, \"Or.new left, right\", :op_bar_bar?\n    parse_operator :and, :equality, \"And.new left, right\", :op_amp_amp?\n    parse_operator :equality, :cmp, \"Call.new left, method, right\", :op_lt?, :op_lt_eq?, :op_gt?, :op_gt_eq?, :op_lt_eq_gt?\n    parse_operator :cmp, :logical_or, \"Call.new left, method, right\", :op_eq_eq?, :op_bang_eq?, :op_eq_tilde?, :op_bang_tilde?, :op_eq_eq_eq?\n    parse_operator :logical_or, :logical_and, \"Call.new left, method, right\", :op_bar?, :op_caret?\n    parse_operator :logical_and, :shift, \"Call.new left, method, right\", :op_amp?\n    parse_operator :shift, :add_or_sub, \"Call.new left, method, right\", :op_lt_lt?, :op_gt_gt?\n\n    def parse_add_or_sub\n      location = @token.location\n\n      left = parse_mul_or_div\n      while true\n        case @token.type\n        when .space?\n          next_token\n        when .op_plus?, .op_minus?, .op_amp_plus?, .op_amp_minus?\n          check_void_value left, location\n\n          method = @token.type.to_s\n          name_location = @token.location\n          next_token_skip_space_or_newline\n          right = parse_mul_or_div\n          left = Call.new(left, method, right).at(location).at_end(right)\n          left.name_location = name_location\n        when .number?\n          case char = @token.value.to_s[0]\n          when '+', '-'\n            method = char.to_s\n            name_location = @token.location\n\n            # Go back to the +/-, advance one char and continue from there\n            self.current_pos = @token.start + 1\n            next_token\n\n            right = parse_mul_or_div\n            left = Call.new(left, method, right).at(location).at_end(right)\n            left.name_location = name_location\n          else\n            return left\n          end\n        else\n          return left\n        end\n      end\n    end\n\n    parse_operator :mul_or_div, :pow, \"Call.new left, method, right\", :op_star?, :op_slash?, :op_slash_slash?, :op_percent?, :op_amp_star?\n    parse_operator :pow, :prefix, \"Call.new left, method, right\", :op_star_star?, :op_amp_star_star?, right_associative: true\n\n    def parse_prefix\n      name_location = @token.location\n      case token_type = @token.type\n      when .unary_operator?\n        location = @token.location\n        next_token_skip_space_or_newline\n        check_void_expression_keyword\n        arg = parse_prefix\n        if token_type.op_bang?\n          Not.new(arg).at(location).at_end(arg)\n        else\n          call = Call.new(arg, token_type.to_s).at(location).at_end(arg)\n          call.name_location = name_location\n          call\n        end\n      else\n        parse_atomic_with_method\n      end\n    end\n\n    # IDENT CONST + - * / // % | & ^ ~ ! ** << < <= == != =~ !~ >> > >= <=> === [] []= []? [ &+ &- &* &**\n    AtomicWithMethodCheck = [\n      :IDENT, :CONST, :OP_PLUS, :OP_MINUS, :OP_STAR, :OP_SLASH, :OP_SLASH_SLASH, :OP_PERCENT,\n      :OP_BAR, :OP_AMP, :OP_CARET, :OP_TILDE, :OP_BANG, :OP_STAR_STAR, :OP_LT_LT, :OP_LT, :OP_LT_EQ,\n      :OP_EQ_EQ, :OP_BANG_EQ, :OP_EQ_TILDE, :OP_BANG_TILDE, :OP_GT_GT, :OP_GT, :OP_GT_EQ,\n      :OP_LT_EQ_GT, :OP_EQ_EQ_EQ, :OP_LSQUARE_RSQUARE, :OP_LSQUARE_RSQUARE_EQ, :OP_LSQUARE_RSQUARE_QUESTION,\n      :OP_LSQUARE, :OP_AMP_PLUS, :OP_AMP_MINUS, :OP_AMP_STAR, :OP_AMP_STAR_STAR,\n    ] of Token::Kind\n\n    def parse_atomic_with_method\n      location = @token.location\n      atomic = parse_atomic\n      parse_atomic_method_suffix atomic, location\n    end\n\n    def parse_atomic_method_suffix(atomic, location)\n      while true\n        case @token.type\n        when .space?\n          next_token\n        when .newline?\n          # In these cases we don't want to chain a call\n          case atomic\n          when ClassDef, ModuleDef, EnumDef, FunDef, Def\n            break\n          else\n            # continue chaining\n          end\n\n          # Allow '.' after newline for chaining calls\n          unless lookahead(preserve_token_on_fail: true) { next_token_skip_space_or_newline; @token.type.op_period? }\n            break\n          end\n        when .op_period?\n          check_void_value atomic, location\n\n          @wants_regex = false\n\n          wants_def_or_macro_name do\n            next_token_skip_space_or_newline\n          end\n\n          if @token.type.instance_var?\n            atomic = parse_read_instance_var(atomic).at(location)\n            next\n          end\n\n          check AtomicWithMethodCheck\n\n          if @token.value == Keyword::IS_A_QUESTION\n            atomic = parse_is_a(atomic).at(location)\n          elsif @token.value == Keyword::AS\n            atomic = parse_as(atomic).at(location)\n          elsif @token.value == Keyword::AS_QUESTION\n            atomic = parse_as?(atomic).at(location)\n          elsif @token.value == Keyword::RESPONDS_TO_QUESTION\n            atomic = parse_responds_to(atomic).at(location)\n          elsif !@in_macro_expression && @token.value == Keyword::NIL_QUESTION\n            atomic = parse_nil?(atomic).at(location)\n          elsif @token.type.op_bang?\n            atomic = parse_negation_suffix(atomic).at(location)\n            atomic = parse_atomic_method_suffix_special(atomic, location)\n          elsif @token.type.op_lsquare?\n            return parse_atomic_method_suffix(atomic, location)\n          else\n            name = case @token.type\n                   when .ident?, .const?\n                     @token.value.to_s\n                   else\n                     @token.type.to_s\n                   end\n            name_location = @token.location\n            end_location = token_end_location\n\n            @wants_regex = false\n            next_token\n\n            space_consumed = false\n            if @token.type.space?\n              @wants_regex = true\n              next_token\n              space_consumed = true\n            end\n\n            case @token.type\n            when .op_eq?\n              atomic = Call.new(atomic, name)\n              unexpected_token unless can_be_assigned?(atomic)\n\n              # Rewrite 'f.x = arg' as f.x=(arg)\n              next_token\n\n              if @token.type.op_lparen?\n                # If we have `f.x=(exp1).a.b.c`, consider it the same as `f.x = (exp1).a.b.c`\n                # and not as `(f.x = exp1).a.b.c` because a difference in space\n                # should not make a difference in semantic (#4399)\n                # The only exception is doing a splat, in which case this can only\n                # be expanded arguments for the call.\n                if current_char == '*'\n                  next_token_skip_space\n                  arg = parse_single_arg\n                  check :OP_RPAREN\n                  end_location = token_end_location\n                  next_token\n                else\n                  arg = parse_op_assign_no_control\n                  end_location = arg.end_location\n                end\n              else\n                skip_space_or_newline\n                arg = parse_single_arg\n                end_location = arg.end_location\n              end\n\n              atomic.at(location).at_end(end_location)\n              atomic.name = \"#{name}=\"\n              atomic.args = [arg] of ASTNode\n              atomic.name_location = name_location\n              next\n            when .assignment_operator?\n              call = Call.new(atomic, name)\n              unexpected_token unless can_be_assigned?(call)\n\n              op_name_location = @token.location\n              method = @token.type.to_s.byte_slice(0, @token.type.to_s.size - 1)\n              next_token_skip_space_or_newline\n              value = parse_op_assign\n              call.at(location)\n              call.name_location = name_location\n              atomic = OpAssign.new(call, method, value).at(location)\n              atomic.name_location = op_name_location\n              next\n            else\n              call_args = preserve_stop_on_do { space_consumed ? parse_call_args_space_consumed : parse_call_args }\n              if call_args\n                args = call_args.args\n                block = call_args.block\n                block_arg = call_args.block_arg\n                named_args = call_args.named_args\n                has_parentheses = call_args.has_parentheses\n              else\n                args = block = block_arg = named_args = nil\n                has_parentheses = false\n              end\n            end\n\n            block = parse_block(block, stop_on_do: @stop_on_do)\n            atomic = Call.new atomic, name, (args || [] of ASTNode), block, block_arg, named_args\n            atomic.has_parentheses = has_parentheses\n            atomic.name_location = name_location\n            atomic.end_location = block.try(&.end_location) || call_args.try(&.end_location) || end_location\n            atomic.at(location)\n            atomic\n          end\n        when .op_lsquare_rsquare?\n          check_void_value atomic, location\n\n          name_location = @token.location\n          end_location = token_end_location\n          @wants_regex = false\n          next_token_skip_space\n          atomic = Call.new(atomic, \"[]\").at(location)\n          atomic.name_location = name_location\n          atomic.end_location = end_location\n          atomic.name_size = 0 if atomic.is_a?(Call)\n          atomic\n        when .op_lsquare?\n          check_void_value atomic, location\n\n          name_location = @token.location\n          next_token_skip_space_or_newline\n\n          call_args = preserve_stop_on_do do\n            parse_call_args_space_consumed(\n              check_plus_and_minus: false,\n              allow_curly: true,\n              end_token: :OP_RSQUARE,\n              allow_beginless_range: true,\n              control: true,\n            )\n          end\n\n          skip_space_or_newline\n          check :OP_RSQUARE\n          end_location = token_end_location\n          @wants_regex = false\n          next_token\n\n          if call_args\n            args = call_args.args\n            block = call_args.block\n            block_arg = call_args.block_arg\n            named_args = call_args.named_args\n          end\n\n          if @token.type.op_question?\n            method_name = \"[]?\"\n            end_location = token_end_location\n            next_token_skip_space\n          else\n            method_name = \"[]\"\n            skip_space\n          end\n\n          atomic = Call.new(atomic, method_name, (args || [] of ASTNode), block, block_arg, named_args).at(location)\n          atomic.name_location = name_location\n          atomic.end_location = end_location\n          atomic.name_size = 0\n          atomic.args_in_brackets = true\n          atomic\n        else\n          break\n        end\n      end\n\n      atomic\n    end\n\n    def parse_atomic_method_suffix_special(call, location)\n      case @token.type\n      when .op_period?, .op_lsquare?, .op_lsquare_rsquare?\n        parse_atomic_method_suffix(call, location)\n      else\n        call\n      end\n    end\n\n    def parse_single_arg\n      if @token.type.op_star?\n        location = @token.location\n        next_token_skip_space\n        arg = parse_op_assign_no_control\n        Splat.new(arg).at(location).at_end(arg)\n      else\n        parse_op_assign_no_control\n      end\n    end\n\n    def parse_is_a(atomic)\n      next_token_skip_space\n\n      if @token.type.op_lparen?\n        next_token_skip_space_or_newline\n        type = parse_bare_proc_type\n        skip_space_or_newline\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      else\n        type = parse_union_type\n        end_location = type.end_location\n      end\n\n      IsA.new(atomic, type).at_end(end_location)\n    end\n\n    def parse_as(atomic, klass = Cast)\n      next_token_skip_space\n\n      if @token.type.op_lparen?\n        next_token_skip_space_or_newline\n        type = parse_bare_proc_type\n        skip_space_or_newline\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      else\n        type = parse_union_type\n        end_location = token_end_location\n      end\n\n      klass.new(atomic, type).at_end(end_location)\n    end\n\n    def parse_as?(atomic)\n      parse_as atomic, klass: NilableCast\n    end\n\n    def parse_responds_to(atomic)\n      next_token\n\n      if @token.type.op_lparen?\n        next_token_skip_space_or_newline\n        name = parse_responds_to_name\n        next_token_skip_space_or_newline\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      elsif @token.type.space?\n        next_token\n        name = parse_responds_to_name\n        end_location = token_end_location\n        next_token_skip_space\n      else\n        unexpected_token \"expected space or '('\"\n      end\n\n      RespondsTo.new(atomic, name).at_end(end_location)\n    end\n\n    def parse_responds_to_name\n      unless @token.type.symbol?\n        unexpected_token \"expected symbol\"\n      end\n\n      @token.value.to_s\n    end\n\n    def parse_nil?(atomic)\n      end_location = token_end_location\n      next_token\n\n      if @token.type.op_lparen?\n        next_token_skip_space_or_newline\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      end\n\n      IsA.new(atomic, Path.global(\"Nil\"), nil_check: true).at_end(end_location)\n    end\n\n    def parse_negation_suffix(atomic)\n      end_location = token_end_location\n      next_token\n\n      if @token.type.op_lparen?\n        next_token_skip_space_or_newline\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      end\n\n      Not.new(atomic).at_end(end_location)\n    end\n\n    def parse_read_instance_var(atomic)\n      ivar_name = @token.value.to_s\n      end_location = token_end_location\n      next_token_skip_space\n\n      call = ReadInstanceVar.new(atomic, ivar_name)\n      call.end_location = end_location\n\n      call\n    end\n\n    def parse_atomic\n      location = @token.location\n      atomic = parse_atomic_without_location\n      atomic.location ||= location\n      atomic\n    end\n\n    def parse_atomic_without_location\n      case @token.type\n      when .op_lparen?\n        parse_parenthesized_expression\n      when .op_lsquare_rsquare?\n        parse_empty_array_literal\n      when .op_lsquare?\n        parse_array_literal\n      when .op_lcurly?\n        parse_hash_or_tuple_literal\n      when .op_lcurly_lcurly?\n        parse_percent_macro_expression\n      when .op_lcurly_percent?\n        parse_percent_macro_control\n      when .op_colon_colon?\n        parse_generic_or_global_call\n      when .op_minus_gt?\n        parse_fun_literal\n      when .op_at_lsquare?\n        parse_annotation\n      when .number?\n        @wants_regex = false\n        node_and_next_token NumberLiteral.new(@token.value.to_s, @token.number_kind)\n      when .char?\n        node_and_next_token CharLiteral.new(@token.value.as(Char))\n      when .string?, .delimiter_start?\n        parse_delimiter\n      when .string_array_start?\n        parse_string_array\n      when .symbol_array_start?\n        parse_symbol_array\n      when .symbol?\n        node_and_next_token SymbolLiteral.new(@token.value.to_s)\n      when .global?\n        raise \"$global_variables are not supported, use @@class_variables instead\"\n      when .op_dollar_tilde?, .op_dollar_question?\n        location = @token.location\n        var = Var.new(@token.to_s).at(location)\n\n        if peek_ahead { next_token_skip_space; @token.type.op_eq? }\n          push_var var\n          node_and_next_token var\n        else\n          node_and_next_token Global.new(var.name).at(location)\n        end\n      when .global_match_data_index?\n        if peek_ahead { next_token_skip_space; @token.type.op_eq? }\n          raise \"global match data cannot be assigned to\"\n        end\n\n        value = @token.value.to_s\n        if value_prefix = value.rchop? '?'\n          method = \"[]?\"\n          value = value_prefix\n        else\n          method = \"[]\"\n        end\n        location = @token.location\n        index = value.to_i?\n        raise \"Index $#{value} doesn't fit in an Int32\" unless index\n        node_and_next_token Call.new(Global.new(\"$~\").at(location), method, NumberLiteral.new(index))\n      when .magic_line?\n        node_and_next_token MagicConstant.expand_line_node(@token.location)\n      when .magic_end_line?\n        raise \"__END_LINE__ can only be used in default parameter value\", @token\n      when .magic_file?\n        node_and_next_token MagicConstant.expand_file_node(@token.location)\n      when .magic_dir?\n        node_and_next_token MagicConstant.expand_dir_node(@token.location)\n      when .ident?\n        # NOTE: Update `Parser#invalid_internal_name?` keyword list\n        # when adding or removing keyword to handle here.\n        if keyword = @token.value.as?(Keyword)\n          case keyword\n          when .begin?\n            check_type_declaration { parse_begin }\n          when Keyword::NIL\n            check_type_declaration { node_and_next_token NilLiteral.new }\n          when .true?\n            check_type_declaration { node_and_next_token BoolLiteral.new(true) }\n          when .false?\n            check_type_declaration { node_and_next_token BoolLiteral.new(false) }\n          when .yield?\n            check_type_declaration { parse_yield }\n          when .with?\n            check_type_declaration { parse_yield_with_scope }\n          when .abstract?\n            check_type_declaration do\n              check_not_inside_def(\"can't use abstract\") do\n                doc = @token.doc\n\n                next_token_skip_space_or_newline\n                case @token.value\n                when Keyword::DEF\n                  parse_def is_abstract: true, doc: doc\n                when Keyword::CLASS\n                  parse_class_def is_abstract: true, doc: doc\n                when Keyword::STRUCT\n                  parse_class_def is_abstract: true, is_struct: true, doc: doc\n                else\n                  unexpected_token\n                end\n              end\n            end\n          when .def?\n            check_type_declaration do\n              check_not_inside_def(\"can't define def\") do\n                parse_def\n              end\n            end\n          when .macro?\n            check_type_declaration do\n              check_not_inside_def(\"can't define macro\") do\n                parse_macro\n              end\n            end\n          when .require?\n            check_type_declaration do\n              check_not_inside_def(\"can't require\") do\n                parse_require\n              end\n            end\n          when .case?\n            check_type_declaration { parse_case }\n          when .select?\n            check_type_declaration { parse_select }\n          when .if?\n            check_type_declaration { parse_if }\n          when .unless?\n            check_type_declaration { parse_unless }\n          when .include?\n            check_type_declaration do\n              check_not_inside_def(\"can't include\") do\n                parse_include\n              end\n            end\n          when .extend?\n            check_type_declaration do\n              check_not_inside_def(\"can't extend\") do\n                parse_extend\n              end\n            end\n          when .class?\n            check_type_declaration do\n              check_not_inside_def(\"can't define class\") do\n                parse_class_def\n              end\n            end\n          when .struct?\n            check_type_declaration do\n              check_not_inside_def(\"can't define struct\") do\n                parse_class_def is_struct: true\n              end\n            end\n          when .module?\n            check_type_declaration do\n              check_not_inside_def(\"can't define module\") do\n                parse_module_def\n              end\n            end\n          when .enum?\n            check_type_declaration do\n              check_not_inside_def(\"can't define enum\") do\n                parse_enum_def\n              end\n            end\n          when .while?\n            check_type_declaration { parse_while }\n          when .until?\n            check_type_declaration { parse_until }\n          when .return?\n            check_type_declaration { parse_return }\n          when .next?\n            check_type_declaration { parse_next }\n          when .break?\n            check_type_declaration { parse_break }\n          when .lib?\n            check_type_declaration do\n              check_not_inside_def(\"can't define lib\") do\n                parse_lib\n              end\n            end\n          when .fun?\n            check_type_declaration do\n              check_not_inside_def(\"can't define fun\") do\n                parse_fun_def top_level: true, require_body: true\n              end\n            end\n          when .alias?\n            check_type_declaration do\n              check_not_inside_def(\"can't define alias\") do\n                parse_alias\n              end\n            end\n          when .pointerof?\n            check_type_declaration { parse_pointerof }\n          when .sizeof?\n            check_type_declaration { parse_sizeof }\n          when .instance_sizeof?\n            check_type_declaration { parse_instance_sizeof }\n          when .alignof?\n            check_type_declaration { parse_alignof }\n          when .instance_alignof?\n            check_type_declaration { parse_instance_alignof }\n          when .offsetof?\n            check_type_declaration { parse_offsetof }\n          when .typeof?\n            check_type_declaration { parse_typeof }\n          when .private?\n            check_type_declaration { parse_visibility_modifier Visibility::Private }\n          when .protected?\n            check_type_declaration { parse_visibility_modifier Visibility::Protected }\n          when .asm?\n            check_type_declaration { parse_asm }\n          when .annotation?\n            check_type_declaration do\n              check_not_inside_def(\"can't define annotation\") do\n                parse_annotation_def\n              end\n            end\n          else\n            set_visibility parse_var_or_call\n          end\n        else\n          set_visibility parse_var_or_call\n        end\n      when .const?\n        parse_generic_or_custom_literal\n      when .instance_var?\n        if @in_macro_expression && @token.value == \"@type\"\n          @is_macro_def = true\n        end\n        new_node_check_type_declaration InstanceVar\n      when .class_var?\n        new_node_check_type_declaration ClassVar\n      when .underscore?\n        node_and_next_token Underscore.new\n      else\n        unexpected_token_in_atomic\n      end\n    end\n\n    def check_type_declaration(&)\n      if next_comes_colon_space?\n        name = @token.value.to_s\n        var = Var.new(name).at(@token.location).at_end(token_end_location)\n        next_token\n        unless @token.type.space?\n          warnings.add_warning_at(@token.location, \"space required before colon in type declaration (run `crystal tool format` to fix this)\")\n        end\n        skip_space\n        check :OP_COLON\n        type_declaration = parse_type_declaration(var)\n        set_visibility type_declaration\n      else\n        yield\n      end\n    end\n\n    def parse_type_declaration(var)\n      next_token_skip_space_or_newline\n      var_type = parse_bare_proc_type\n      skip_space\n      if @token.type.op_eq?\n        next_token_skip_space_or_newline\n        value = parse_op_assign_no_control\n      end\n      TypeDeclaration.new(var, var_type, value).at(var).at_end(value || var_type)\n    end\n\n    def next_comes_colon_space?\n      return false unless @no_type_declaration == 0\n\n      pos = current_pos\n      while current_char.ascii_whitespace?\n        next_char_no_column_increment\n      end\n      comes_colon_space = current_char == ':'\n      if comes_colon_space\n        next_char_no_column_increment\n        comes_colon_space = current_char.ascii_whitespace?\n      end\n      self.current_pos = pos\n      comes_colon_space\n    end\n\n    def new_node_check_type_declaration(klass)\n      name = @token.value.to_s\n      var = klass.new(name).at(@token.location)\n      var.end_location = token_end_location\n      @wants_regex = false\n      next_token\n      space_after_name = @token.type.space?\n      skip_space\n\n      if @no_type_declaration == 0 && @token.type.op_colon?\n        unless space_after_name\n          warnings.add_warning_at(@token.location, \"space required before colon in type declaration (run `crystal tool format` to fix this)\")\n        end\n        parse_type_declaration(var)\n      else\n        var\n      end\n    end\n\n    def parse_generic_or_custom_literal\n      type = parse_generic(expression: true)\n      skip_space\n      parse_custom_literal type\n    end\n\n    def parse_custom_literal(type)\n      if @token.type.op_lcurly?\n        tuple_or_hash = parse_hash_or_tuple_literal allow_of: false\n\n        skip_space\n\n        if @token.keyword?(:of)\n          unexpected_token\n        end\n\n        case tuple_or_hash\n        when TupleLiteral\n          ary = ArrayLiteral.new(tuple_or_hash.elements, name: type).at(tuple_or_hash)\n          return ary\n        when HashLiteral\n          tuple_or_hash.name = type\n          return tuple_or_hash\n        else\n          raise \"BUG: tuple_or_hash should be tuple or hash, not #{tuple_or_hash}\"\n        end\n      end\n\n      type\n    end\n\n    def check_not_inside_def(message, &)\n      if @def_nest == 0 && @fun_nest == 0\n        yield\n      else\n        suffix = @def_nest > 0 ? \" inside def\" : \" inside fun\"\n        raise message + suffix, @token.line_number, @token.column_number\n      end\n    end\n\n    def inside_def?\n      @def_nest > 0\n    end\n\n    def inside_fun?\n      @fun_nest > 0\n    end\n\n    def parse_annotation\n      doc = @token.doc\n      location = @token.location\n\n      next_token_skip_space\n      name = parse_path\n      skip_space\n\n      args = [] of ASTNode\n      named_args = nil\n\n      if @token.type.op_lparen?\n        open(\"annotation\") do\n          next_token_skip_space_or_newline\n          while !@token.type.op_rparen?\n            if @token.type.ident? && current_char == ':'\n              named_args = parse_named_args(@token.location, first_name: nil, allow_newline: true)\n              check :OP_RPAREN\n              break\n            else\n              args << parse_call_arg\n            end\n\n            skip_space_or_newline\n            if @token.type.op_comma?\n              next_token_skip_space_or_newline\n            end\n          end\n          next_token_skip_space\n        end\n      end\n      check :OP_RSQUARE\n      end_location = token_end_location\n      @wants_regex = false\n      next_token_skip_space\n\n      ann = Annotation.new(name, args, named_args).at(location).at_end(end_location)\n      ann.doc = doc\n      ann\n    end\n\n    def parse_begin\n      begin_location = @token.location\n      slash_is_regex!\n      next_token_skip_statement_end\n      exps = parse_expressions\n      node, end_location = parse_exception_handler exps, begin_location: begin_location\n      if !node.is_a?(ExceptionHandler) && (!node.is_a?(Expressions) || !node.keyword.none?)\n        node = Expressions.new([node])\n      end\n      node.at(begin_location).at_end(end_location)\n      node.keyword = :begin if node.is_a?(Expressions)\n      node\n    end\n\n    def parse_exception_handler(exp, implicit = false, begin_location = nil)\n      rescues = nil\n      a_else = nil\n      a_ensure = nil\n      begin_location ||= exp.location\n\n      if @token.keyword?(:rescue)\n        rescues = [] of Rescue\n        found_catch_all = false\n        while true\n          begin_location ||= @token.location\n          location = @token.location\n          a_rescue = parse_rescue\n          if a_rescue.types\n            if found_catch_all\n              raise \"specific rescue must come before catch-all rescue\", location\n            end\n          else\n            if found_catch_all\n              raise \"catch-all rescue can only be specified once\", location\n            end\n            found_catch_all = true\n          end\n          rescues << a_rescue\n          break unless @token.keyword?(:rescue)\n        end\n      end\n\n      if @token.keyword?(:else)\n        unless rescues\n          raise \"'else' is useless without 'rescue'\", @token, 4\n        end\n\n        else_location = @token.location\n        begin_location ||= @token.location\n        next_token_skip_statement_end\n        a_else = parse_expressions\n        skip_statement_end\n      end\n\n      if @token.keyword?(:ensure)\n        ensure_location = @token.location\n        begin_location ||= @token.location\n        next_token_skip_statement_end\n        a_ensure = parse_expressions\n        skip_statement_end\n      end\n\n      end_location = token_end_location\n\n      check_ident :end\n      slash_is_not_regex!\n      next_token_skip_space\n\n      if rescues || a_ensure\n        ex = ExceptionHandler.new(exp, rescues, a_else, a_ensure)\n        ex.at(begin_location).at_end(end_location)\n        ex.implicit = true if implicit\n        ex.else_location = else_location\n        ex.ensure_location = ensure_location\n        {ex, end_location}\n      else\n        {exp, end_location}\n      end\n    end\n\n    SemicolonOrNewLine = [:OP_SEMICOLON, :NEWLINE] of Token::Kind\n    ConstOrDoubleColon = [:CONST, :OP_COLON_COLON] of Token::Kind\n\n    def parse_rescue\n      location = @token.location\n      end_location = token_end_location\n      next_token_skip_space\n\n      case @token.type\n      when .ident?\n        name = @token.value.to_s\n        push_var_name name\n        end_location = token_end_location\n        next_token_skip_space\n\n        if @token.type.op_colon?\n          next_token_skip_space_or_newline\n          check ConstOrDoubleColon\n          types = parse_rescue_types\n          end_location = types.last.end_location\n        end\n      when .const?, .op_colon_colon?\n        types = parse_rescue_types\n        end_location = types.last.end_location\n      else\n        # keep going\n      end\n\n      check SemicolonOrNewLine\n\n      next_token_skip_space_or_newline\n\n      if @token.keyword?(:end)\n        body = nil\n      else\n        body = parse_expressions\n        end_location = body.end_location\n        skip_statement_end\n      end\n\n      Rescue.new(body, types, name).at(location).at_end(end_location)\n    end\n\n    def parse_rescue_types\n      types = [] of ASTNode\n      while true\n        types << parse_generic\n        skip_space\n        if @token.type.op_bar?\n          next_token_skip_space\n        else\n          skip_space\n          break\n        end\n      end\n      types\n    end\n\n    def parse_while\n      parse_while_or_until While\n    end\n\n    def parse_until\n      parse_while_or_until Until\n    end\n\n    def parse_while_or_until(klass)\n      slash_is_regex!\n      next_token_skip_space_or_newline\n\n      cond = parse_op_assign_no_control allow_suffix: false\n\n      slash_is_regex!\n      skip_statement_end\n\n      body = parse_expressions\n      skip_statement_end\n\n      end_location = token_end_location\n      check_ident :end\n      next_token_skip_space\n\n      klass.new(cond, body).at_end(end_location)\n    end\n\n    def call_block_arg_follows?\n      @token.type.op_amp? && !current_char.ascii_whitespace?\n    end\n\n    def parse_call_block_arg(args, check_paren, named_args = nil)\n      location = @token.location\n\n      next_token_skip_space\n\n      if @token.type.op_period?\n        block_arg_name = temp_arg_name\n        obj = Var.new(block_arg_name)\n\n        @wants_regex = false\n        if current_char == '%'\n          next_char\n          @token.type = :OP_PERCENT\n          @token.column_number += 1\n          skip_space\n        else\n          next_token_skip_space\n        end\n\n        call = parse_call_block_arg_after_dot(obj)\n\n        block = Block.new([Var.new(block_arg_name)], call).at(location)\n        end_location = call.end_location\n      else\n        block_arg = parse_op_assign\n        end_location = block_arg.end_location\n      end\n\n      if check_paren\n        skip_space_or_newline\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      else\n        skip_space\n      end\n\n      CallArgs.new args, block, block_arg, named_args, false, end_location, has_parentheses: check_paren\n    end\n\n    def parse_call_block_arg_after_dot(obj)\n      location = @token.location\n\n      if @token.type.instance_var?\n        call = parse_read_instance_var(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n        return call\n      end\n\n      check AtomicWithMethodCheck\n\n      if @token.value == Keyword::IS_A_QUESTION\n        call = parse_is_a(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n      elsif @token.value == Keyword::AS\n        call = parse_as(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n      elsif @token.value == Keyword::AS_QUESTION\n        call = parse_as?(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n      elsif @token.value == Keyword::RESPONDS_TO_QUESTION\n        call = parse_responds_to(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n      elsif !@in_macro_expression && @token.value == Keyword::NIL_QUESTION\n        call = parse_nil?(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n      elsif @token.type.op_bang?\n        call = parse_negation_suffix(obj).at(location)\n        call = parse_atomic_method_suffix_special(call, location)\n      elsif @token.type.op_lsquare?\n        call = parse_atomic_method_suffix obj, location\n\n        if @token.type.op_eq? && call.is_a?(Call) && can_be_assigned?(call)\n          next_token_skip_space\n          exp = parse_op_assign\n          call.name = \"#{call.name}=\"\n          call.args << exp\n        end\n      else\n        # At this point we want to attach the \"do\" to the next call\n        old_stop_on_do = @stop_on_do\n        @stop_on_do = false\n        call = parse_var_or_call(force_call: true).at(location)\n\n        if call.is_a?(Call)\n          call.obj = obj\n        else\n          raise \"BUG: #{call} should be a call\"\n        end\n\n        call = call.as(Call)\n\n        if @token.type.op_eq?\n          unexpected_token unless can_be_assigned?(call)\n\n          next_token_skip_space\n          if @token.type.op_lparen?\n            next_token_skip_space\n            exp = parse_op_assign\n            check :OP_RPAREN\n            next_token_skip_space\n            call.name = \"#{call.name}=\"\n            call.args = [exp] of ASTNode\n            call = parse_atomic_method_suffix call, location\n          else\n            exp = parse_op_assign\n            call.name = \"#{call.name}=\"\n            call.args = [exp] of ASTNode\n          end\n        else\n          call = parse_atomic_method_suffix call, location\n\n          if @token.type.op_eq? && call.is_a?(Call) && can_be_assigned?(call)\n            next_token_skip_space\n            exp = parse_op_assign\n            call.name = \"#{call.name}=\"\n            call.args << exp\n          end\n        end\n\n        @stop_on_do = old_stop_on_do\n      end\n\n      call\n    end\n\n    StatementEnd = [:OP_SEMICOLON, :NEWLINE, :SPACE] of Token::Kind\n\n    def parse_class_def(is_abstract = false, is_struct = false, doc = nil)\n      @type_nest += 1\n\n      doc ||= @token.doc\n\n      next_token_skip_space_or_newline\n      name_location = @token.location\n\n      name = parse_path\n      found_space = @token.type.space?\n      skip_space\n\n      type_vars, splat_index = parse_type_vars\n\n      if type_vars\n        found_space = @token.type.space?\n        skip_space\n      end\n\n      superclass = nil\n\n      if @token.type.op_lt?\n        next_token_skip_space_or_newline\n        if @token.keyword?(:self)\n          superclass = Self.new.at(@token.location)\n          next_token\n        else\n          superclass = parse_generic\n        end\n      end\n\n      check(StatementEnd) if superclass || !found_space\n      skip_statement_end\n\n      body = push_visibility { parse_expressions }\n\n      end_location = token_end_location\n      check_ident :end\n      next_token_skip_space\n\n      @type_nest -= 1\n\n      class_def = ClassDef.new name, body, superclass, type_vars, is_abstract, is_struct, splat_index\n      class_def.doc = doc\n      class_def.name_location = name_location\n      class_def.end_location = end_location\n      set_visibility class_def\n      class_def\n    end\n\n    def parse_type_vars\n      type_vars = nil\n      splat_index = nil\n      if @token.type.op_lparen?\n        type_vars = [] of String\n\n        next_token_skip_space_or_newline\n\n        index = 0\n        while !@token.type.op_rparen?\n          if @token.type.op_star?\n            raise \"splat type parameter already specified\", @token if splat_index\n            splat_index = index\n            next_token\n          end\n          type_var_name = check_const\n\n          if type_vars.includes? type_var_name\n            raise \"duplicated type parameter name: #{type_var_name}\", @token\n          end\n\n          type_vars.push type_var_name\n\n          next_token_skip_space\n          if @token.type.op_comma?\n            next_token_skip_space_or_newline\n          else\n            skip_space_or_newline\n            check :OP_RPAREN\n          end\n\n          index += 1\n        end\n\n        if type_vars.empty?\n          raise \"must specify at least one type var\"\n        end\n\n        next_token\n      end\n      {type_vars, splat_index}\n    end\n\n    def parse_module_def\n      @type_nest += 1\n\n      location = @token.location\n      doc = @token.doc\n\n      next_token_skip_space_or_newline\n\n      name_location = @token.location\n      name = parse_path\n      found_space = @token.type.space?\n      skip_space\n\n      type_vars, splat_index = parse_type_vars\n\n      check(StatementEnd) if type_vars || !found_space\n      skip_statement_end\n\n      body = push_visibility { parse_expressions }\n\n      end_location = token_end_location\n      check_ident :end\n      next_token_skip_space\n\n      @type_nest -= 1\n\n      module_def = ModuleDef.new name, body, type_vars, splat_index\n      module_def.doc = doc\n      module_def.name_location = name_location\n      module_def.end_location = end_location\n      set_visibility module_def\n      module_def\n    end\n\n    def parse_annotation_def\n      location = @token.location\n      doc = @token.doc\n\n      next_token_skip_space_or_newline\n\n      name_location = @token.location\n      name = parse_path\n      check StatementEnd\n      skip_statement_end\n\n      end_location = token_end_location\n      check_ident :end\n      next_token_skip_space\n\n      annotation_def = AnnotationDef.new name\n      annotation_def.doc = doc\n      annotation_def.name_location = name_location\n      annotation_def.end_location = end_location\n      annotation_def\n    end\n\n    def parse_parenthesized_expression\n      location = @token.location\n      slash_is_regex!\n      next_token_skip_space_or_newline\n\n      if @token.type.op_rparen?\n        end_location = token_end_location\n        node = Expressions.new([Nop.new] of ASTNode).at(location).at_end(end_location)\n        node.keyword = :paren\n        return node_and_next_token node\n      end\n\n      exps = [] of ASTNode\n\n      # do...end in parenthesis should not stop because there's no call further\n      # left to bind to:\n      #\n      # ```\n      # (foo do\n      # end)\n      # ```\n      @stop_on_do = false\n\n      while true\n        exps << parse_expression\n        case @token.type\n        when .op_rparen?\n          @wants_regex = false\n          end_location = token_end_location\n          next_token_skip_space\n          break\n        when .newline?, .op_semicolon?\n          next_token_skip_statement_end\n          if @token.type.op_rparen?\n            @wants_regex = false\n            end_location = token_end_location\n            next_token_skip_space\n            break\n          end\n        else\n          raise \"unterminated parenthesized expression\", location\n        end\n      end\n\n      unexpected_token if @token.type.op_lparen?\n\n      node = Expressions.new(exps).at(location).at_end(end_location)\n      node.keyword = :paren\n      node\n    end\n\n    def parse_fun_literal\n      location = @token.location\n\n      next_token_skip_space_or_newline\n\n      case @token.type\n      when .symbol?\n        # -> :T { }\n        raise \"a space is mandatory between ':' and return type\", @token\n      when .op_lparen?, .op_lcurly?, .op_colon?\n        # do nothing\n      else\n        return parse_fun_pointer unless @token.keyword?(:do)\n      end\n\n      params = [] of Arg\n      if @token.type.op_lparen?\n        next_token_skip_space_or_newline\n        while !@token.type.op_rparen?\n          param = parse_fun_literal_param\n          if params.any? &.name.==(param.name)\n            raise \"duplicated proc literal parameter name: #{param.name}\", param.location.not_nil!\n          end\n\n          params << param\n        end\n        next_token_skip_space_or_newline\n      end\n\n      case @token.type\n      when .symbol?\n        # ->() :T { }\n        raise \"a space is mandatory between ':' and return type\", @token\n      when .op_colon?\n        next_token_skip_space_or_newline\n        return_type = parse_bare_proc_type\n        skip_space_or_newline\n      end\n\n      with_lexical_var_scope do\n        push_vars params\n\n        end_location = nil\n\n        if @token.keyword?(:do)\n          next_token_skip_statement_end\n          check_not_pipe_before_proc_literal_body\n          body = parse_expressions\n          body, end_location = parse_exception_handler body, implicit: true\n        elsif @token.type.op_lcurly?\n          next_token_skip_statement_end\n          check_not_pipe_before_proc_literal_body\n          body = preserve_stop_on_do { parse_expressions }\n          end_location = token_end_location\n          check :OP_RCURLY\n          next_token_skip_space\n        else\n          unexpected_token\n        end\n\n        a_def = Def.new(\"->\", params, body, return_type: return_type).at(location).at_end(end_location)\n        ProcLiteral.new(a_def).at(location).at_end(end_location)\n      end\n    end\n\n    def check_not_pipe_before_proc_literal_body\n      if @token.type.op_bar?\n        location = @token.location\n        next_token_skip_space\n\n        msg = String.build do |msg|\n          msg << %[unexpected token: \"|\", proc literals specify their parameters like this: ->(]\n          if @token.type.ident?\n            msg << @token.value.to_s << \" : Type\"\n            next_token_skip_space_or_newline\n            msg << \", ...\" if @token.type.op_comma?\n          else\n            msg << \"param : Type\"\n          end\n          msg << \") { ... }\"\n        end\n\n        raise msg, location\n      end\n    end\n\n    def parse_fun_literal_param\n      name = check_ident\n      location = @token.location\n      end_location = token_end_location\n      next_token_skip_space_or_newline\n\n      if @token.type.op_colon?\n        next_token_skip_space_or_newline\n\n        type = parse_bare_proc_type\n        end_location = type.end_location\n      end\n\n      if @token.type.op_comma?\n        next_token_skip_space_or_newline\n      else\n        skip_space_or_newline\n        check :OP_RPAREN\n      end\n\n      Arg.new(name, restriction: type).at(location).at_end(end_location)\n    end\n\n    def parse_fun_pointer\n      location = @token.location\n\n      global = false\n      if @token.type.op_colon_colon?\n        next_token_skip_space_or_newline\n        global = true\n      end\n\n      case @token.type\n      when .ident?\n        name = @token.value.to_s\n        var_location = @token.location\n        var_end_location = token_end_location\n        global_call = global\n        equals_sign, end_location = consume_def_equals_sign_skip_space\n        if equals_sign\n          name = \"#{name}=\"\n        elsif @token.type.op_period?\n          raise \"ProcPointer of local variable cannot be global\", location if global\n          if name != \"self\" && !var_in_scope?(name)\n            raise \"undefined variable '#{name}'\", location\n          end\n          obj = Var.new(name).at(var_location).at_end(var_end_location)\n\n          name = consume_def_or_macro_name\n          equals_sign, end_location = consume_def_equals_sign_skip_space\n          name = \"#{name}=\" if equals_sign\n        end\n      when .const?\n        obj = parse_generic global: global, location: location, expression: false\n        skip_space\n        check :OP_PERIOD\n        name = consume_def_or_macro_name\n        equals_sign, end_location = consume_def_equals_sign_skip_space\n        name = \"#{name}=\" if equals_sign\n      when .instance_var?\n        raise \"ProcPointer of instance variable cannot be global\", location if global\n        obj = InstanceVar.new(@token.value.to_s).at(location).at_end(token_end_location)\n        next_token_skip_space\n        check :OP_PERIOD\n        name = consume_def_or_macro_name\n        equals_sign, end_location = consume_def_equals_sign_skip_space\n        name = \"#{name}=\" if equals_sign\n      when .class_var?\n        raise \"ProcPointer of class variable cannot be global\", location if global\n        obj = ClassVar.new(@token.value.to_s).at(location).at_end(token_end_location)\n        next_token_skip_space\n        check :OP_PERIOD\n        name = consume_def_or_macro_name\n        equals_sign, end_location = consume_def_equals_sign_skip_space\n        name = \"#{name}=\" if equals_sign\n      else\n        unexpected_token\n      end\n\n      if @token.type.op_period?\n        unexpected_token\n      end\n\n      if @token.type.op_lparen?\n        next_token_skip_space\n        types = parse_union_types(:OP_RPAREN)\n        check :OP_RPAREN\n        end_location = token_end_location\n        next_token_skip_space\n      else\n        types = [] of ASTNode\n      end\n\n      ProcPointer.new(obj, name, types, !!global_call).at_end(end_location)\n    end\n\n    record Piece,\n      value : String | ASTNode,\n      line_number : Int32\n\n    def parse_delimiter(want_skip_space = true)\n      if @token.type.string?\n        return node_and_next_token StringLiteral.new(@token.value.to_s).at(@token.location)\n      end\n\n      location = @token.location\n      delimiter_state = @token.delimiter_state\n\n      check :DELIMITER_START\n\n      if delimiter_state.kind.heredoc?\n        if @inside_interpolation\n          raise \"heredoc cannot be used inside interpolation\", location\n        end\n        node = StringInterpolation.new([] of ASTNode).at(location)\n        @heredocs << {delimiter_state, node}\n        next_token\n        return node\n      end\n\n      next_string_token(delimiter_state)\n      delimiter_state = @token.delimiter_state\n\n      pieces = [] of Piece\n\n      delimiter_state, options, end_location = consume_delimiter pieces, delimiter_state\n\n      if want_skip_space && delimiter_state.kind.string?\n        while true\n          passed_backslash_newline = @token.passed_backslash_newline\n          skip_space\n\n          if passed_backslash_newline && @token.type.delimiter_start? && @token.delimiter_state.kind.string?\n            next_string_token(delimiter_state)\n            delimiter_state = @token.delimiter_state\n            delimiter_state, options, end_location = consume_delimiter pieces, delimiter_state\n          else\n            break\n          end\n        end\n      end\n\n      result = combine_pieces(pieces, delimiter_state).at(location).at_end(end_location)\n\n      case delimiter_state.kind\n      when .command?\n        result = Call.new(\"`\", result).at(result)\n      when .regex?\n        if result.is_a?(StringLiteral) && (regex_error = Regex.error?(result.value))\n          raise \"invalid regex: #{regex_error}\", location\n        end\n\n        result = RegexLiteral.new(result, options).at(result)\n      else\n        # no special treatment\n      end\n\n      result\n    end\n\n    private def combine_pieces(pieces, delimiter_state)\n      if pieces.any?(&.value.is_a?(ASTNode))\n        pieces = combine_interpolation_pieces(pieces, delimiter_state)\n        StringInterpolation.new(pieces)\n      else\n        string = combine_stringliteral_pieces(pieces, delimiter_state)\n        StringLiteral.new(string)\n      end\n    end\n\n    private def combine_interpolation_pieces(pieces, delimiter_state)\n      if needs_heredoc_indent_removed?(delimiter_state)\n        remove_heredoc_indent(pieces, delimiter_state.heredoc_indent)\n      else\n        pieces.map do |piece|\n          value = piece.value\n          value.is_a?(String) ? StringLiteral.new(value) : value\n        end\n      end\n    end\n\n    private def combine_stringliteral_pieces(pieces, delimiter_state)\n      if needs_heredoc_indent_removed?(delimiter_state)\n        pieces = remove_heredoc_indent(pieces, delimiter_state.heredoc_indent)\n        pieces.join { |piece| piece.as(StringLiteral).value }\n      else\n        pieces.map(&.value).join\n      end\n    end\n\n    def consume_delimiter(pieces, delimiter_state)\n      options = Regex::CompileOptions::None\n      end_location = nil\n      while true\n        case @token.type\n        when .string?\n          pieces << Piece.new(@token.value.to_s, @token.line_number)\n\n          next_string_token(delimiter_state)\n          delimiter_state = @token.delimiter_state\n        when .delimiter_end?\n          if delimiter_state.kind.regex?\n            options = consume_regex_options\n          end\n          end_location = token_end_location\n          next_token\n          break\n        when .eof?\n          case delimiter_state.kind\n          when .command?\n            raise \"Unterminated command\"\n          when .regex?\n            raise \"Unterminated regular expression\"\n          when .heredoc?\n            raise \"Unterminated heredoc\"\n          else\n            raise \"Unterminated string literal\"\n          end\n        else\n          next_token_skip_space_or_newline\n\n          line_number = @token.line_number\n          exp = consume_interpolation(delimiter_state)\n          next_string_token delimiter_state\n          pieces << Piece.new(exp, line_number)\n          delimiter_state = @token.delimiter_state\n        end\n      end\n\n      {delimiter_state, options, end_location}\n    end\n\n    def consume_interpolation(delimiter_state)\n      old_inside_interpolation = @inside_interpolation\n      @inside_interpolation = true\n\n      exp = preserve_stop_on_do { parse_expression }\n\n      skip_space_or_newline\n      if !@token.type.op_rcurly?\n        raise \"Unterminated string interpolation\"\n      end\n\n      @token.delimiter_state = delimiter_state\n      @inside_interpolation = old_inside_interpolation\n\n      # We cannot reduce `StringLiteral` of interpolation inside heredoc into `String`\n      # because heredoc try to remove its indentation.\n      if exp.is_a?(StringLiteral) && !delimiter_state.kind.heredoc?\n        exp = exp.value\n      end\n\n      exp\n    end\n\n    def consume_regex_options\n      options = Regex::CompileOptions::None\n      while true\n        case current_char\n        when 'i'\n          options |= Regex::CompileOptions::IGNORE_CASE\n          next_char\n        when 'm'\n          options |= Regex::CompileOptions::MULTILINE\n          next_char\n        when 'x'\n          options |= Regex::CompileOptions::EXTENDED\n          next_char\n        else\n          if 'a' <= current_char.downcase <= 'z'\n            raise \"unknown regex option: #{current_char}\"\n          end\n          break\n        end\n      end\n      options\n    end\n\n    def consume_heredocs\n      @consuming_heredocs = true\n      @heredocs.reverse!\n      while heredoc = @heredocs.pop?\n        consume_heredoc(heredoc[0], heredoc[1].as(StringInterpolation))\n      end\n      @consuming_heredocs = false\n    end\n\n    def consume_heredoc(delimiter_state, node)\n      next_string_token(delimiter_state)\n      delimiter_state = @token.delimiter_state\n\n      pieces = [] of Piece\n\n      delimiter_state, _options, end_location = consume_delimiter pieces, delimiter_state\n\n      result = combine_pieces(pieces, delimiter_state)\n      if result.is_a?(StringInterpolation)\n        node.expressions.concat(result.expressions)\n      else\n        node.expressions.push(result.at(node).at_end(end_location))\n      end\n\n      node.heredoc_indent = delimiter_state.heredoc_indent\n\n      node.end_location = end_location\n    end\n\n    def needs_heredoc_indent_removed?(delimiter_state)\n      delimiter_state.kind.heredoc? && delimiter_state.heredoc_indent >= 0\n    end\n\n    def remove_heredoc_indent(pieces : Array, indent)\n      current_line = IO::Memory.new\n      remove_indent = true\n      new_pieces = [] of ASTNode | String\n      previous_line_number = 0\n      pieces.each_with_index do |piece, i|\n        value = piece.value\n        line_number = piece.line_number\n\n        this_piece_is_in_new_line = line_number != previous_line_number\n        next_piece_is_in_new_line = i == pieces.size - 1 || pieces[i + 1].line_number != line_number\n        if value.is_a?(String)\n          if value.in?(\"\\n\", \"\\r\\n\")\n            current_line << value\n            if this_piece_is_in_new_line || next_piece_is_in_new_line\n              line = current_line.to_s\n              line = remove_heredoc_from_line(line, indent, line_number - 1) if remove_indent\n              add_heredoc_piece new_pieces, line\n              current_line.clear\n              remove_indent = true\n            end\n          elsif (slash_n = value.starts_with?('\\n')) || value.starts_with?(\"\\r\\n\")\n            current_line << (slash_n ? '\\n' : \"\\r\\n\")\n            line = current_line.to_s\n            line = remove_heredoc_from_line(line, indent, line_number - 1) if remove_indent\n            add_heredoc_piece new_pieces, line\n            current_line.clear\n            remove_indent = true\n            current_line << value.byte_slice(slash_n ? 1 : 2)\n          else\n            current_line << value\n          end\n        else\n          if remove_indent\n            line = current_line.to_s\n            if (line.size < indent) || !line.each_char.first(indent).all?(&.ascii_whitespace?)\n              raise \"heredoc line must have an indent greater than or equal to #{indent}\", line_number, 1\n            else\n              line = line[indent..-1]\n            end\n            add_heredoc_piece new_pieces, line unless line.empty?\n            add_heredoc_piece new_pieces, value\n            remove_indent = false\n            current_line.clear\n          else\n            unless current_line.empty?\n              line = current_line.to_s\n              add_heredoc_piece new_pieces, line\n              current_line.clear\n            end\n\n            add_heredoc_piece new_pieces, value\n          end\n        end\n        previous_line_number = line_number\n      end\n      unless current_line.empty?\n        line = current_line.to_s\n        line = remove_heredoc_from_line(line, indent, pieces.last.line_number) if remove_indent\n        add_heredoc_piece new_pieces, line\n      end\n      new_pieces.map do |piece|\n        if piece.is_a?(String)\n          StringLiteral.new(piece)\n        else\n          piece\n        end\n      end\n    end\n\n    private def add_heredoc_piece(pieces, piece : String)\n      last = pieces.last?\n      if last.is_a?(String)\n        last += piece\n        pieces[-1] = last\n      else\n        pieces << piece\n      end\n    end\n\n    private def add_heredoc_piece(pieces, piece : ASTNode)\n      pieces << piece\n    end\n\n    def remove_heredoc_from_line(line, indent, line_number)\n      if line.each_char.first(indent).all? &.ascii_whitespace?\n        if line.size - 1 < indent\n          \"\\n\"\n        else\n          line[indent..-1]\n        end\n      else\n        raise \"heredoc line must have an indent greater than or equal to #{indent}\", line_number, 1\n      end\n    end\n\n    def parse_string_without_interpolation(context, want_skip_space = true)\n      parse_string_literal_without_interpolation(context, want_skip_space).value\n    end\n\n    def parse_string_literal_without_interpolation(context, want_skip_space = true)\n      location = @token.location\n\n      unless string_literal_start?\n        raise \"expected string literal for #{context}, not #{@token}\"\n      end\n\n      string = parse_delimiter(want_skip_space)\n      if string.is_a?(StringLiteral)\n        string\n      else\n        raise \"interpolation not allowed in #{context}\", location\n      end\n    end\n\n    def parse_string_array\n      parse_string_or_symbol_array StringLiteral, \"String\"\n    end\n\n    def parse_symbol_array\n      parse_string_or_symbol_array SymbolLiteral, \"Symbol\"\n    end\n\n    def parse_string_or_symbol_array(klass, elements_type)\n      strings = [] of ASTNode\n      end_location = nil\n\n      while true\n        next_string_array_token\n        case @token.type\n        when .string?\n          strings << klass.new(@token.value.to_s).at(@token.location).at_end(token_end_location)\n        when .string_array_end?\n          end_location = token_end_location\n          next_token\n          break\n        else\n          raise \"Unterminated #{elements_type.downcase} array literal\"\n        end\n      end\n\n      ArrayLiteral.new(strings, Path.global(elements_type)).at_end(end_location)\n    end\n\n    def parse_empty_array_literal\n      line = @line_number\n      column = @token.column_number\n\n      next_token_skip_space\n      if @token.keyword?(:of)\n        next_token_skip_space_or_newline\n        of = parse_bare_proc_type\n        ArrayLiteral.new(of: of).at_end(of)\n      else\n        raise \"for empty arrays use '[] of ElementType'\", line, column\n      end\n    end\n\n    def parse_array_literal\n      line = @line_number\n      column = @token.column_number\n\n      slash_is_regex!\n\n      exps = [] of ASTNode\n      end_location = nil\n\n      open(\"array literal\") do\n        next_token_skip_space_or_newline\n        while !@token.type.op_rsquare?\n          exp_location = @token.location\n\n          if @token.type.op_star?\n            next_token_skip_space_or_newline\n            exp = Splat.new(parse_op_assign_no_control).at(exp_location)\n          else\n            exp = parse_op_assign_no_control\n          end\n\n          exps << exp\n          skip_space\n\n          if @token.type.op_comma?\n            slash_is_regex!\n            next_token_skip_space_or_newline\n          else\n            skip_space_or_newline\n            check :OP_RSQUARE\n            break\n          end\n        end\n        @wants_regex = false\n        end_location = token_end_location\n        next_token_skip_space\n      end\n\n      of = nil\n      if @token.keyword?(:of)\n        next_token_skip_space_or_newline\n        of = parse_bare_proc_type\n        end_location = of.end_location\n      elsif exps.size == 0\n        raise \"for empty arrays use '[] of ElementType'\", line, column\n      end\n\n      ArrayLiteral.new(exps, of).at_end(end_location)\n    end\n\n    def parse_hash_or_tuple_literal(allow_of = true)\n      location = @token.location\n      line = @line_number\n      column = @token.column_number\n\n      slash_is_regex!\n      next_token_skip_space_or_newline\n\n      if @token.type.op_rcurly?\n        end_location = token_end_location\n        next_token_skip_space\n        new_hash_literal([] of HashLiteral::Entry, line, column, end_location)\n      else\n        if named_tuple_start?\n          unless allow_of\n            raise \"can't use named tuple syntax for Hash-like literal, use '=>'\", @token\n          end\n          return parse_named_tuple(location)\n        else\n          if @token.type.op_star?\n            first_is_splat = true\n            next_token_skip_space_or_newline\n          end\n\n          key_location = @token.location\n          first_key = parse_op_assign_no_control\n          first_key = Splat.new(first_key).at(location) if first_is_splat\n          case @token.type\n          when .op_colon?\n            unexpected_token if first_is_splat\n\n            # Check that there's no space before the ':'\n            if @token.column_number != first_key.end_location.not_nil!.column_number + 1\n              raise \"space not allowed between named argument name and ':'\"\n            end\n\n            if first_key.is_a?(StringLiteral)\n              # It's a named tuple\n              unless allow_of\n                raise \"can't use named tuple syntax for Hash-like literal, use '=>'\", @token\n              end\n              if first_key.value.empty?\n                raise \"named tuple name cannot be empty\", key_location\n              end\n              return parse_named_tuple(location, first_key.value)\n            else\n              check :OP_EQ_GT\n            end\n          when .op_comma?\n            slash_is_regex!\n            next_token_skip_space_or_newline\n            return parse_tuple first_key, location\n          when .op_rcurly?\n            return parse_tuple first_key, location\n          when .newline?\n            next_token_skip_space\n            check :OP_RCURLY\n            return parse_tuple first_key, location\n          else\n            unexpected_token if first_is_splat\n            check :OP_EQ_GT\n          end\n        end\n        slash_is_regex!\n        next_token_skip_space_or_newline\n        parse_hash_literal first_key, location, allow_of\n      end\n    end\n\n    def parse_hash_literal(first_key, location, allow_of)\n      line = @line_number\n      column = @token.column_number\n      end_location = nil\n\n      entries = [] of HashLiteral::Entry\n      entries << HashLiteral::Entry.new(first_key, parse_op_assign)\n\n      if @token.type.newline?\n        next_token_skip_space_or_newline\n        check :OP_RCURLY\n        next_token_skip_space\n      else\n        open(\"hash literal\", location) do\n          skip_space_or_newline\n          if @token.type.op_comma?\n            slash_is_regex!\n            next_token_skip_space_or_newline\n          else\n            skip_space_or_newline\n            check :OP_RCURLY\n          end\n\n          while !@token.type.op_rcurly?\n            key_loc = @token.location\n            key = parse_op_assign_no_control\n            skip_space_or_newline\n            if @token.type.op_colon? && key.is_a?(StringLiteral)\n              raise \"can't use 'key: value' syntax in a hash literal\", key_loc\n            else\n              check :OP_EQ_GT\n            end\n            slash_is_regex!\n            next_token_skip_space_or_newline\n            entries << HashLiteral::Entry.new(key, parse_op_assign)\n            skip_space\n            if @token.type.op_comma?\n              slash_is_regex!\n              next_token_skip_space_or_newline\n            else\n              skip_space_or_newline\n              check :OP_RCURLY\n              break\n            end\n          end\n          end_location = token_end_location\n          next_token_skip_space\n        end\n      end\n\n      new_hash_literal entries, line, column, end_location, allow_of: allow_of\n    end\n\n    def named_tuple_start?\n      (@token.type.ident? || @token.type.const?) && current_char == ':' && peek_next_char != ':'\n    end\n\n    def string_literal_start?\n      @token.type.delimiter_start? && @token.delimiter_state.kind.string?\n    end\n\n    def parse_tuple(first_exp, location)\n      exps = [] of ASTNode\n      end_location = nil\n\n      open(\"tuple literal\", location) do\n        exps << first_exp\n        while !@token.type.op_rcurly?\n          exp_location = @token.location\n\n          if @token.type.op_star?\n            next_token_skip_space_or_newline\n            exp = Splat.new(parse_op_assign_no_control).at(exp_location)\n          else\n            exp = parse_op_assign_no_control\n          end\n\n          exps << exp\n          skip_space\n\n          if @token.type.op_comma?\n            next_token_skip_space_or_newline\n          else\n            skip_space_or_newline\n            check :OP_RCURLY\n            break\n          end\n        end\n        end_location = token_end_location\n        next_token_skip_space\n      end\n\n      TupleLiteral.new(exps).at_end(end_location)\n    end\n\n    def new_hash_literal(entries, line, column, end_location, allow_of = true)\n      of = nil\n\n      if allow_of\n        if @token.keyword?(:of)\n          next_token_skip_space_or_newline\n          of_key = parse_bare_proc_type\n          check :OP_EQ_GT\n          next_token_skip_space_or_newline\n          of_value = parse_bare_proc_type\n          of = HashLiteral::Entry.new(of_key, of_value)\n          end_location = of_value.end_location\n        end\n\n        if entries.empty? && !of\n          raise \"for empty hashes use '{} of KeyType => ValueType'\", line, column\n        end\n      end\n\n      HashLiteral.new(entries, of).at_end(end_location)\n    end\n\n    def parse_named_tuple(location)\n      parse_named_tuple(location, @token.value.to_s)\n    end\n\n    def parse_named_tuple(location, first_key)\n      next_token_never_a_symbol\n\n      slash_is_regex!\n      next_token_skip_space\n\n      first_value = parse_op_assign\n      skip_space_or_newline\n\n      entries = [] of NamedTupleLiteral::Entry\n      entries << NamedTupleLiteral::Entry.new(first_key, first_value)\n\n      if @token.type.op_comma?\n        next_token_skip_space_or_newline\n\n        while !@token.type.op_rcurly?\n          key_location = @token.location\n          key = @token.value.to_s\n          if named_tuple_start?\n            next_token_never_a_symbol\n          elsif string_literal_start?\n            key = parse_string_without_interpolation(\"named tuple name\", want_skip_space: false)\n          else\n            raise \"expected '}' or named tuple name, not #{@token}\", @token\n          end\n\n          if key.empty?\n            raise \"named tuple name cannot be empty\", key_location\n          end\n\n          if @token.type.space?\n            raise \"space not allowed between named argument name and ':'\"\n          end\n\n          check :OP_COLON\n\n          if entries.any? { |entry| entry.key == key }\n            raise \"duplicated key: #{key}\", @token\n          end\n\n          slash_is_regex!\n          next_token_skip_space\n\n          value = parse_op_assign_no_control\n          skip_space\n\n          entries << NamedTupleLiteral::Entry.new(key, value)\n          if @token.type.op_comma?\n            next_token_skip_space_or_newline\n          else\n            break\n          end\n        end\n      end\n\n      skip_space_or_newline\n      check :OP_RCURLY\n\n      end_location = token_end_location\n      next_token_skip_space\n\n      NamedTupleLiteral.new(entries).at(location).at_end(end_location)\n    end\n\n    def parse_require\n      raise \"can't require inside type declarations\", @token if @type_nest > 0\n\n      next_token_skip_space\n      string_literal = parse_string_literal_without_interpolation(\"require\")\n\n      skip_space\n\n      Require.new(string_literal.value).at_end(string_literal)\n    end\n\n    def parse_case\n      slash_is_regex!\n      next_token_skip_space_or_newline\n      while @token.type.op_semicolon?\n        next_token_skip_space\n      end\n\n      unless @token.value.in?(Keyword::WHEN, Keyword::ELSE, Keyword::END)\n        cond = parse_op_assign_no_control\n        skip_statement_end\n      end\n\n      whens = [] of When\n      a_else = nil\n      exhaustive = nil\n\n      # All when expressions, so we can detect duplicates\n      when_exps = Set(ASTNode).new\n\n      while true\n        case @token.value\n        when Keyword::WHEN, Keyword::IN\n          if exhaustive.nil?\n            exhaustive = @token.value == Keyword::IN\n            if exhaustive && !cond\n              raise \"exhaustive case (case ... in) requires a case expression (case exp; in ..)\"\n            end\n          elsif exhaustive && @token.value == Keyword::WHEN\n            raise \"expected 'in', not 'when'\"\n          elsif !exhaustive && @token.value == Keyword::IN\n            raise \"expected 'when', not 'in'\"\n          end\n\n          location = @token.location\n          {% if compare_versions(Crystal::VERSION, \"1.1.0\") < 0 %}\n            # FIXME: Workaround for compiler bug in Crystal 1.0 and earlier (https://github.com/crystal-lang/crystal/pull/15452#issuecomment-2653266710)\n            end_location = token_end_location\n          {% end %}\n          slash_is_regex!\n          next_token_skip_space_or_newline\n          when_conds = [] of ASTNode\n\n          if cond.is_a?(TupleLiteral)\n            raise \"splat is not allowed inside case expression\" if cond.elements.any?(Splat)\n\n            while true\n              if @token.type.op_lcurly?\n                curly_location = @token.location\n\n                next_token_skip_space_or_newline\n\n                tuple_elements = [] of ASTNode\n\n                while true\n                  exp = parse_when_expression(cond, single: false, exhaustive: exhaustive)\n                  check_valid_exhaustive_expression(exp) if exhaustive\n\n                  tuple_elements << exp\n\n                  skip_space\n                  if @token.type.op_comma?\n                    next_token_skip_space_or_newline\n                  else\n                    break\n                  end\n                end\n\n                if tuple_elements.size != cond.elements.size\n                  raise \"wrong number of tuple elements (given #{tuple_elements.size}, expected #{cond.elements.size})\", curly_location\n                end\n\n                tuple = TupleLiteral.new(tuple_elements).at(curly_location)\n                when_conds << tuple\n                add_when_exp(when_exps, tuple)\n\n                check :OP_RCURLY\n                next_token_skip_space\n              else\n                exp = parse_when_expression(cond, single: true, exhaustive: exhaustive)\n                when_conds << exp\n                add_when_exp(when_exps, exp)\n                skip_space\n              end\n\n              break if when_expression_end\n            end\n          else\n            while true\n              exp = parse_when_expression(cond, single: true, exhaustive: exhaustive)\n              check_valid_exhaustive_expression(exp) if exhaustive\n\n              when_conds << exp\n              add_when_exp(when_exps, exp)\n              skip_space\n              break if when_expression_end\n            end\n          end\n\n          when_body = parse_expressions\n          skip_space_or_newline\n          whens << When.new(when_conds, when_body)\n            .at(location)\n            .at_end(when_conds.last.end_location)\n        when Keyword::ELSE\n          if exhaustive\n            raise \"exhaustive case (case ... in) doesn't allow an 'else'\"\n          end\n\n          next_token_skip_statement_end\n          a_else = parse_expressions\n          skip_statement_end\n          check_ident :end\n          end_location = token_end_location\n          next_token\n          break\n        when Keyword::END\n          end_location = token_end_location\n          next_token\n          break\n        else\n          unexpected_token \"expecting when, else or end\"\n        end\n      end\n\n      Case.new(cond, whens, a_else, exhaustive.nil? ? false : exhaustive).at_end(end_location)\n    end\n\n    def check_valid_exhaustive_expression(exp)\n      case exp\n      when NilLiteral, BoolLiteral, Path, Generic, Underscore\n        return\n      when Call\n        if exp.obj.is_a?(ImplicitObj) && exp.name.ends_with?('?') &&\n           exp.args.empty? && !exp.named_args &&\n           !exp.block\n          return\n        end\n\n        if (exp.obj.is_a?(Path) || exp.obj.is_a?(Generic)) && exp.name == \"class\" &&\n           exp.args.empty? && !exp.named_args &&\n           !exp.block\n          return\n        end\n      end\n\n      raise \"expression of exhaustive case (case ... in) must be a constant (like `IO::Memory`), a generic (like `Array(Int32)`), a bool literal (true or false), a nil literal (nil) or a question method (like `.red?`)\", exp.location.not_nil!\n    end\n\n    # Adds an expression to all when expressions and error on duplicates\n    def add_when_exp(when_exps, exp)\n      return unless when_exp_constant?(exp)\n\n      if when_exps.includes?(exp)\n        raise \"duplicate when #{exp} in case\", exp.location.not_nil!\n      end\n\n      when_exps << exp\n    end\n\n    # Only error on constant values, because calls might have side-effects:\n    # a first call might return one value and not match the case\n    # value, but the second same call returns something different\n    # and matches it.\n    def when_exp_constant?(exp)\n      case exp\n      when NilLiteral, BoolLiteral, CharLiteral, NumberLiteral,\n           StringLiteral, SymbolLiteral, Path\n        true\n      when ArrayLiteral\n        exp.elements.all? { |e| when_exp_constant?(e) }\n      when TupleLiteral\n        exp.elements.all? { |e| when_exp_constant?(e) }\n      when RegexLiteral\n        when_exp_constant?(exp.value)\n      when RangeLiteral\n        when_exp_constant?(exp.from) &&\n          when_exp_constant?(exp.to)\n      else\n        false\n      end\n    end\n\n    def when_expression_end\n      slash_is_regex!\n      if @token.keyword?(:then)\n        next_token_skip_space_or_newline\n        return true\n      else\n        case @token.type\n        when .op_comma?\n          next_token_skip_space_or_newline\n        when .newline?\n          skip_space_or_newline\n          return true\n        when .op_semicolon?\n          skip_statement_end\n          return true\n        else\n          unexpected_token \"expecting ',', ';' or '\\\\n'\"\n        end\n      end\n      false\n    end\n\n    def parse_when_expression(cond, single, exhaustive)\n      if cond && @token.type.op_period?\n        location = @token.location\n        next_token\n        call = parse_var_or_call(force_call: true).at(location)\n        case call\n        when Call        then call.obj = ImplicitObj.new\n        when RespondsTo  then call.obj = ImplicitObj.new\n        when IsA         then call.obj = ImplicitObj.new\n        when Cast        then call.obj = ImplicitObj.new\n        when NilableCast then call.obj = ImplicitObj.new\n        when Not         then call.exp = ImplicitObj.new\n        else\n          raise \"BUG: expected Call, RespondsTo, IsA, Cast or NilableCast\"\n        end\n        call\n      elsif single && @token.type.underscore?\n        if exhaustive\n          raise \"'when _' is not supported\"\n        else\n          raise \"'when _' is not supported, use 'else' block instead\"\n        end\n      else\n        parse_op_assign_no_control\n      end\n    end\n\n    def parse_select\n      slash_is_regex!\n      next_token_skip_space\n      skip_statement_end\n\n      {% if compare_versions(Crystal::VERSION, \"1.1.0\") < 0 %}\n        # FIXME: Workaround for compiler bug in Crystal 1.0 and earlier (https://github.com/crystal-lang/crystal/pull/15452#issuecomment-2653266710)\n        end_location = token_end_location\n      {% end %}\n      whens = [] of When\n\n      while true\n        case @token.value\n        when Keyword::WHEN\n          slash_is_regex!\n          next_token_skip_space_or_newline\n\n          location = @token.location\n          condition = parse_op_assign_no_control\n          unless valid_select_when?(condition)\n            raise \"invalid select when expression: must be an assignment or call\", location\n          end\n\n          skip_space\n          unless when_expression_end\n            unexpected_token \"expecting then, ';' or newline\"\n          end\n          skip_statement_end\n\n          body = parse_expressions\n          skip_space_or_newline\n\n          whens << When.new(condition, body)\n            .at(location)\n            .at_end(condition.end_location)\n        when Keyword::ELSE\n          if whens.size == 0\n            unexpected_token \"expecting when\"\n          end\n          slash_is_regex!\n          next_token_skip_statement_end\n          a_else = parse_expressions\n          skip_statement_end\n          check_ident :end\n          end_location = token_end_location\n          next_token\n          break\n        when Keyword::END\n          if whens.empty?\n            unexpected_token \"expecting when, else or end\"\n          end\n          end_location = token_end_location\n          next_token\n          break\n        else\n          unexpected_token \"expecting when, else or end\"\n        end\n      end\n\n      Select.new(whens, a_else).at_end(end_location)\n    end\n\n    def valid_select_when?(node)\n      case node\n      when Assign\n        node.value.is_a?(Call)\n      when Call\n        true\n      else\n        false\n      end\n    end\n\n    def parse_include\n      parse_include_or_extend Include\n    end\n\n    def parse_extend\n      parse_include_or_extend Extend\n    end\n\n    def parse_include_or_extend(klass)\n      location = @token.location\n\n      next_token_skip_space_or_newline\n\n      if @token.keyword?(:self)\n        name = Self.new.at(@token.location)\n        name.end_location = token_end_location\n        next_token_skip_space\n      else\n        name = parse_generic\n        skip_space\n      end\n\n      klass.new name\n    end\n\n    def parse_to_def(a_def)\n      prepare_parse_def\n      @def_nest += 1\n\n      result = parse\n\n      a_def.calls_super = @calls_super\n      a_def.calls_initialize = @calls_initialize\n      a_def.calls_previous_def = @calls_previous_def\n      a_def.uses_block_arg = @uses_block_arg\n      a_def.assigns_special_var = @assigns_special_var\n\n      result\n    end\n\n    def parse_def(is_abstract = false, is_macro_def = false, doc = nil)\n      doc ||= @token.doc\n\n      prepare_parse_def\n      a_def = with_isolated_var_scope do\n        parse_def_helper is_abstract: is_abstract\n      end\n\n      a_def.calls_super = @calls_super\n      a_def.calls_initialize = @calls_initialize\n      a_def.calls_previous_def = @calls_previous_def\n      a_def.uses_block_arg = @uses_block_arg\n      a_def.assigns_special_var = @assigns_special_var\n      a_def.doc = doc\n      @calls_super = false\n      @calls_initialize = false\n      @calls_previous_def = false\n      @uses_block_arg = false\n      @assigns_special_var = false\n      @block_arg_name = nil\n      @is_macro_def = false\n      a_def\n    end\n\n    def prepare_parse_def\n      @calls_super = false\n      @calls_initialize = false\n      @calls_previous_def = false\n      @uses_block_arg = false\n      @block_arg_name = nil\n      @assigns_special_var = false\n      @is_macro_def = false\n    end\n\n    def parse_macro\n      doc = @token.doc\n\n      # Force lexer return if possible a def or macro name\n      # cases like: def `, def /, def //\n      # that in regular statements states for delimiters\n      # here must be treated as method names.\n      name = consume_def_or_macro_name\n\n      with_isolated_var_scope do\n        name_location = @token.location\n\n        case @token.type\n        when .const?\n          raise \"macro can't have a receiver\"\n        when .ident?\n          check_valid_def_name\n          equals_sign, _ = consume_def_equals_sign_skip_space\n          name = \"#{name}=\" if equals_sign\n        else\n          check_valid_def_op_name\n          next_token_skip_space\n        end\n\n        params = [] of Arg\n\n        found_default_value = false\n        found_splat = false\n        found_double_splat = nil\n\n        splat_index = nil\n        double_splat = nil\n        index = 0\n\n        case @token.type\n        when .op_lparen?\n          next_token_skip_space_or_newline\n          while !@token.type.op_rparen?\n            extras = parse_param(params,\n              extra_assigns: nil,\n              parentheses: true,\n              found_default_value: found_default_value,\n              found_splat: found_splat,\n              found_double_splat: found_double_splat,\n              allow_restrictions: false)\n            if !found_default_value && extras.default_value\n              found_default_value = true\n            end\n            if !splat_index && extras.splat\n              splat_index = index\n              found_splat = true\n            end\n            if extras.double_splat\n              double_splat = params.pop\n              found_double_splat = double_splat\n            end\n            if block_param = extras.block_arg\n              check :OP_RPAREN\n              break\n            elsif @token.type.op_comma?\n              next_token_skip_space_or_newline\n            else\n              skip_space_or_newline\n              check :OP_RPAREN\n            end\n            index += 1\n          end\n\n          if splat_index == params.size - 1 && params.last.name.empty?\n            raise \"named parameters must follow bare *\", params.last.location.not_nil!\n          end\n\n          next_token\n        when .ident?, .op_star?\n          if @token.keyword?(:end)\n            unexpected_token \"expected ';' or newline\"\n          else\n            unexpected_token \"parentheses are mandatory for macro parameters\"\n          end\n        when .op_semicolon?, .newline?\n          # Skip\n        when .op_period?\n          raise \"macro can't have a receiver\"\n        else\n          unexpected_token\n        end\n\n        end_location = nil\n\n        if @token.keyword?(:end)\n          end_location = token_end_location\n          body = Expressions.new\n          next_token_skip_space\n        else\n          body, end_location = parse_macro_body(name_location)\n        end\n\n        node = Macro.new name, params, body, block_param, splat_index, double_splat: double_splat\n        node.name_location = name_location\n        node.doc = doc\n        node.end_location = end_location\n        set_visibility node\n        node\n      end\n    end\n\n    def parse_macro_body(start_location, macro_state = Token::MacroState.default)\n      skip_whitespace = check_macro_skip_whitespace\n      slash_is_regex!\n\n      pieces = [] of ASTNode\n\n      while true\n        next_macro_token macro_state, skip_whitespace\n        macro_state = @token.macro_state\n        if macro_state.yields\n          @block_arity ||= 0\n        end\n\n        skip_whitespace = false\n\n        case @token.type\n        when .macro_literal?\n          pieces << MacroLiteral.new(@token.value.to_s).at(@token.location).at_end(token_end_location)\n        when .macro_expression_start?\n          location = @token.location\n          exp = MacroExpression.new(parse_macro_expression).at(location)\n          check_macro_expression_end\n          skip_whitespace = check_macro_skip_whitespace\n          pieces << exp.at_end(token_end_location)\n        when .macro_control_start?\n          macro_control = parse_macro_control(start_location, macro_state)\n          if macro_control\n            skip_space_or_newline\n            check :OP_PERCENT_RCURLY\n            pieces << macro_control\n            skip_whitespace = check_macro_skip_whitespace\n          else\n            return new_macro_expressions(pieces), nil\n          end\n        when .macro_var?\n          macro_var_name = @token.value.to_s\n          location = @token.location\n          if macro_var_name[0].uppercase? || macro_var_name[0].titlecase?\n            warnings.add_warning_at @token.location, \"macro fresh variables with constant names are deprecated\"\n          end\n          if current_char == '{'\n            if macro_var_name.size == 1\n              warnings.add_warning_at @token.location, \"single-letter macro fresh variables with indices are deprecated\"\n            end\n            macro_var_exps = parse_macro_var_exps\n          else\n            macro_var_exps = nil\n          end\n          pieces << MacroVar.new(macro_var_name, macro_var_exps).at(location).at_end(token_end_location)\n        when .macro_end?\n          break\n        when .eof?\n          raise \"unterminated macro\", start_location\n        else\n          unexpected_token\n        end\n      end\n\n      end_location = token_end_location\n\n      next_token\n\n      {new_macro_expressions(pieces), end_location}\n    end\n\n    private def new_macro_expressions(pieces)\n      if pieces.empty?\n        Expressions.new\n      else\n        Expressions.from(pieces)\n      end\n    end\n\n    def parse_macro_var_exps\n      next_token # '{'\n      next_token\n\n      exps = [] of ASTNode\n      while true\n        exps << parse_expression_inside_macro\n        skip_space\n        case @token.type\n        when .op_comma?\n          next_token_skip_space\n          if @token.type.op_rcurly?\n            break\n          end\n        when .op_rcurly?\n          break\n        else\n          unexpected_token %(expecting \",\" or \"}\")\n        end\n      end\n      exps\n    end\n\n    def check_macro_skip_whitespace\n      if current_char == '\\\\' && peek_next_char.ascii_whitespace?\n        next_char\n        true\n      else\n        false\n      end\n    end\n\n    def parse_percent_macro_expression\n      raise \"can't nest macro expressions\", @token if @in_macro_expression\n\n      slash_is_regex!\n      location = @token.location\n      macro_exp = parse_macro_expression\n      check_macro_expression_end\n      end_location = token_end_location\n      next_token\n      MacroExpression.new(macro_exp).at(location).at_end(end_location)\n    end\n\n    def parse_macro_expression\n      next_token_skip_space_or_newline\n      parse_expression_inside_macro\n    end\n\n    def check_macro_expression_end\n      if @token.type.op_comma?\n        raise <<-MSG\n          expecting token ',', not '}'\n\n          If you are nesting tuples or hashes you must write them like this:\n\n              { {x, y}, {z, w} } # Note the space after the first curly brace\n\n          because {{...}} is parsed as a macro expression.\n          MSG\n      end\n\n      check :OP_RCURLY\n\n      next_token\n      check :OP_RCURLY\n    end\n\n    def parse_percent_macro_control\n      raise \"can't nest macro expressions\", @token if @in_macro_expression\n\n      macro_control = parse_macro_control(@token.location)\n      if macro_control\n        skip_space_or_newline\n        check :OP_PERCENT_RCURLY\n        next_token_skip_space\n        macro_control\n      else\n        unexpected_token_in_atomic\n      end\n    end\n\n    def parse_macro_control(start_location, macro_state = Token::MacroState.default)\n      location = @token.location\n      next_token_skip_space_or_newline\n\n      case @token.value\n      when Keyword::FOR\n        next_token_skip_space\n\n        vars = [] of Var\n\n        while true\n          var = case @token.type\n                when .underscore?\n                  \"_\"\n                when .ident?\n                  @token.value.to_s\n                else\n                  unexpected_token \"expecting ident or underscore\"\n                end\n          vars << Var.new(var).at(@token.location).at_end(token_end_location)\n\n          next_token_skip_space\n          if @token.type.op_comma?\n            next_token_skip_space\n          else\n            break\n          end\n        end\n\n        check_ident :in\n        next_token_skip_space\n\n        exp = parse_expression_inside_macro\n\n        check :OP_PERCENT_RCURLY\n\n        macro_state.control_nest += 1\n        body, end_location = parse_macro_body(start_location, macro_state)\n        macro_state.control_nest -= 1\n\n        check_ident :end\n        next_token_skip_space\n        check :OP_PERCENT_RCURLY\n\n        return MacroFor.new(vars, exp, body).at_end(token_end_location)\n      when Keyword::IF\n        return parse_macro_if(start_location, macro_state).at(location)\n      when Keyword::UNLESS\n        return parse_macro_if(start_location, macro_state, is_unless: true).at(location)\n      when Keyword::BEGIN\n        next_token_skip_space\n        check :OP_PERCENT_RCURLY\n\n        macro_state.control_nest += 1\n        body, end_location = parse_macro_body(start_location, macro_state)\n        macro_state.control_nest -= 1\n\n        check_ident :end\n        next_token_skip_space\n        check :OP_PERCENT_RCURLY\n\n        return MacroIf.new(BoolLiteral.new(true), body).at(location).at_end(token_end_location)\n      when Keyword::ELSE, Keyword::ELSIF, Keyword::END\n        return nil\n      when Keyword::VERBATIM\n        next_token_skip_space\n        unless @token.keyword?(:do)\n          unexpected_token(msg: \"expecting 'do'\")\n        end\n        next_token_skip_space\n        check :OP_PERCENT_RCURLY\n\n        macro_state.control_nest += 1\n        body, end_location = parse_macro_body(start_location, macro_state)\n        macro_state.control_nest -= 1\n\n        check_ident :end\n        next_token_skip_space\n        check :OP_PERCENT_RCURLY\n\n        return MacroVerbatim.new(body).at_end(token_end_location)\n      else\n        # will be parsed as a normal expression\n      end\n\n      @in_macro_expression = true\n      exps = parse_expressions\n      @in_macro_expression = false\n\n      MacroExpression.new(exps, output: false).at(location).at_end(token_end_location)\n    end\n\n    def parse_macro_if(start_location, macro_state, check_end = true, is_unless = false)\n      location = @token.location\n\n      next_token_skip_space\n\n      @in_macro_expression = true\n      cond = parse_op_assign\n      @in_macro_expression = false\n\n      if !@token.type.op_percent_rcurly? && check_end\n        @in_macro_expression = true\n        if is_unless\n          node = parse_unless_after_condition cond, location\n        else\n          node = parse_if_after_condition cond, location, true\n        end\n        skip_statement_end\n        exps = parse_expressions\n        @in_macro_expression = false\n        check :OP_PERCENT_RCURLY\n\n        exps = Expressions.concat!(node, exps)\n        return MacroExpression.new(exps, output: false).at_end(token_end_location)\n      end\n\n      check :OP_PERCENT_RCURLY\n\n      macro_state.control_nest += 1\n      a_then, end_location = parse_macro_body(start_location, macro_state)\n      macro_state.control_nest -= 1\n\n      case @token.value\n      when Keyword::ELSE\n        next_token_skip_space\n        check :OP_PERCENT_RCURLY\n\n        macro_state.control_nest += 1\n        a_else, end_location = parse_macro_body(start_location, macro_state)\n        macro_state.control_nest -= 1\n\n        if check_end\n          check_ident :end\n          next_token_skip_space\n          check :OP_PERCENT_RCURLY\n        end\n      when Keyword::ELSIF\n        unexpected_token if is_unless\n        start_loc = @token.location\n        a_else = parse_macro_if(start_location, macro_state, false).at(start_loc)\n\n        if check_end\n          check_ident :end\n          next_token_skip_space\n          check :OP_PERCENT_RCURLY\n        end\n      when Keyword::END\n        if check_end\n          next_token_skip_space\n          check :OP_PERCENT_RCURLY\n        end\n      else\n        unexpected_token\n      end\n\n      a_then, a_else = a_else, a_then if is_unless\n      MacroIf.new(cond, a_then, a_else, is_unless: is_unless).at_end(token_end_location)\n    end\n\n    def parse_expression_inside_macro\n      @in_macro_expression = true\n\n      case @token.type\n      when .op_star?\n        next_token_skip_space\n        exp = parse_expression\n        exp = Splat.new(exp).at(exp)\n      when .op_star_star?\n        next_token_skip_space\n        exp = parse_expression\n        exp = DoubleSplat.new(exp).at(exp)\n      else\n        exp = parse_expression\n      end\n\n      skip_space_or_newline\n\n      @in_macro_expression = false\n      exp\n    end\n\n    # << < <= == === != =~ !~ >> > >= + - * / // ! ~ % & | ^ ** [] []? []= <=> &+ &- &* &**\n    DefOrMacroCheck2 = [\n      :OP_LT_LT, :OP_LT, :OP_LT_EQ, :OP_EQ_EQ, :OP_EQ_EQ_EQ, :OP_BANG_EQ, :OP_EQ_TILDE,\n      :OP_BANG_TILDE, :OP_GT_GT, :OP_GT, :OP_GT_EQ, :OP_PLUS, :OP_MINUS, :OP_STAR, :OP_SLASH,\n      :OP_SLASH_SLASH, :OP_BANG, :OP_TILDE, :OP_PERCENT, :OP_AMP, :OP_BAR, :OP_CARET, :OP_STAR_STAR,\n      :OP_LSQUARE_RSQUARE, :OP_LSQUARE_RSQUARE_EQ, :OP_LSQUARE_RSQUARE_QUESTION, :OP_LT_EQ_GT,\n      :OP_AMP_PLUS, :OP_AMP_MINUS, :OP_AMP_STAR, :OP_AMP_STAR_STAR,\n    ] of Token::Kind\n\n    def parse_def_helper(is_abstract = false)\n      @doc_enabled = false\n      @def_nest += 1\n\n      # At this point we want to attach the \"do\" to calls inside the def,\n      # not to calls that might have this def as a macro argument.\n      @stop_on_do = false\n\n      next_token\n\n      consume_def_or_macro_name\n\n      receiver = nil\n      @block_arity = nil\n      name_location = @token.location\n      receiver_location = @token.location\n      end_location = token_end_location\n\n      if @token.type.const?\n        receiver = parse_path\n        skip_space\n        last_was_space = false\n      elsif @token.type.ident?\n        check_valid_def_name\n        name = @token.value.to_s\n\n        equals_sign, _ = consume_def_equals_sign\n        name = \"#{name}=\" if equals_sign\n        last_was_space = @token.type.space?\n        skip_space\n      else\n        check_valid_def_op_name\n        name = @token.type.to_s\n\n        next_token\n        last_was_space = @token.type.space?\n        skip_space\n      end\n\n      params = [] of Arg\n      extra_assigns = [] of ASTNode\n\n      if @token.type.op_period?\n        unless receiver\n          if name\n            receiver = Var.new(name).at(receiver_location).at_end(end_location)\n          else\n            raise \"shouldn't reach this line\"\n          end\n        end\n\n        consume_def_or_macro_name\n\n        if @token.type.ident?\n          check_valid_def_name\n          name = @token.value.to_s\n\n          name_location = @token.location\n          equals_sign, _ = consume_def_equals_sign\n          name = \"#{name}=\" if equals_sign\n          last_was_space = @token.type.space?\n          skip_space\n        else\n          check DefOrMacroCheck2\n          check_valid_def_op_name\n          name = @token.type.to_s\n\n          name_location = @token.location\n          next_token\n          last_was_space = @token.type.space?\n          skip_space\n        end\n      else\n        if receiver\n          unexpected_token\n        else\n          raise \"shouldn't reach this line\" unless name\n        end\n        name = name.not_nil!\n      end\n\n      found_default_value = false\n      found_splat = false\n      found_double_splat = nil\n      found_block = false\n\n      index = 0\n      splat_index = nil\n      double_splat = nil\n\n      case @token.type\n      when .op_lparen?\n        next_token_skip_space_or_newline\n        while !@token.type.op_rparen?\n          extras = parse_param(params,\n            extra_assigns: extra_assigns,\n            parentheses: true,\n            found_default_value: found_default_value,\n            found_splat: found_splat,\n            found_double_splat: found_double_splat,\n            allow_restrictions: true,\n          )\n          if !found_default_value && extras.default_value\n            found_default_value = true\n          end\n          if !splat_index && extras.splat\n            splat_index = index\n            found_splat = true\n          end\n          if extras.double_splat\n            double_splat = params.pop\n            found_double_splat = double_splat\n          end\n          if block_param = extras.block_arg\n            compute_block_arg_yields block_param\n            check :OP_RPAREN\n            found_block = true\n            break\n          elsif @token.type.op_comma?\n            next_token_skip_space_or_newline\n          else\n            skip_space_or_newline\n            check :OP_RPAREN\n          end\n          index += 1\n        end\n\n        end_location = token_end_location\n\n        if Lexer.setter?(name)\n          if params.size > 1 || found_splat || found_double_splat\n            raise \"setter method '#{name}' cannot have more than one parameter\"\n          elsif found_block\n            raise \"setter method '#{name}' cannot have a block\"\n          end\n        end\n\n        if splat_index == params.size - 1 && params.last.name.empty?\n          raise \"named parameters must follow bare *\", params.last.location.not_nil!\n        end\n\n        next_token\n        last_was_space = @token.type.space?\n        skip_space\n        if @token.type.symbol?\n          raise \"a space is mandatory between ':' and return type\", @token\n        end\n      when .ident?, .instance_var?, .class_var?, .op_star?, .op_star_star?\n        if @token.keyword?(:end)\n          unexpected_token %(expected \";\" or newline)\n        else\n          unexpected_token \"parentheses are mandatory for def parameters\"\n        end\n      when .op_semicolon?, .newline?\n        # Skip\n      when .op_colon?\n        # Skip\n      when .op_amp?\n        unexpected_token \"parentheses are mandatory for def parameters\"\n      when .symbol?\n        raise \"a space is mandatory between ':' and return type\", @token\n      else\n        if is_abstract && @token.type.eof?\n          # OK\n        else\n          unexpected_token\n        end\n      end\n\n      if @token.type.op_colon?\n        unless last_was_space\n          warnings.add_warning_at @token.location, \"space required before colon in return type restriction (run `crystal tool format` to fix this)\"\n        end\n        next_token_skip_space\n        return_type = parse_bare_proc_type\n        end_location = return_type.end_location\n      end\n\n      skip_space\n      if @token.type.ident? && @token.value == \"forall\"\n        next_token_skip_space\n        free_vars = parse_def_free_vars\n      end\n\n      if is_abstract\n        body = Nop.new\n      else\n        slash_is_regex!\n        skip_statement_end\n\n        end_location = token_end_location\n\n        if @token.keyword?(:end)\n          body = Expressions.from(extra_assigns).at(@token.location)\n          next_token_skip_space\n        else\n          body = parse_expressions\n          if extra_assigns.size > 0\n            exps = [] of ASTNode\n            exps.concat extra_assigns\n            if body.is_a?(Expressions)\n              exps.concat body.expressions\n            else\n              exps.push body\n            end\n            body = Expressions.from(exps).at(body)\n          end\n          body, end_location = parse_exception_handler body, implicit: true\n        end\n      end\n\n      @def_nest -= 1\n      @doc_enabled = @wants_doc\n\n      node = Def.new name, params, body, receiver, block_param, return_type, @is_macro_def, @block_arity, is_abstract, splat_index, double_splat: double_splat, free_vars: free_vars\n      node.name_location = name_location\n      set_visibility node\n      node.end_location = end_location\n      node\n    end\n\n    def check_valid_def_name\n      if @token.value.in?(Keyword::IS_A_QUESTION, Keyword::AS, Keyword::AS_QUESTION, Keyword::RESPONDS_TO_QUESTION, Keyword::NIL_QUESTION)\n        raise \"'#{@token.value}' is a pseudo-method and can't be redefined\", @token\n      end\n    end\n\n    def check_valid_def_op_name\n      if @token.type.op_bang?\n        raise \"'!' is a pseudo-method and can't be redefined\", @token\n      end\n    end\n\n    def parse_def_free_vars\n      free_vars = [] of String\n      while true\n        check :CONST\n        free_var = @token.value.to_s\n        raise \"duplicated free variable name: #{free_var}\", @token if free_vars.includes?(free_var)\n        free_vars << free_var\n\n        next_token_skip_space\n        if @token.type.op_comma?\n          next_token_skip_space\n          check :CONST\n        else\n          break\n        end\n      end\n      free_vars\n    end\n\n    def compute_block_arg_yields(block_arg)\n      block_arg_restriction = block_arg.restriction\n      if block_arg_restriction.is_a?(ProcNotation)\n        @block_arity = block_arg_restriction.inputs.try(&.size) || 0\n      else\n        @block_arity = 0\n      end\n    end\n\n    record ArgExtras,\n      block_arg : Arg?,\n      default_value : Bool,\n      splat : Bool,\n      double_splat : Bool\n\n    def parse_param(params, extra_assigns, parentheses, found_default_value, found_splat, found_double_splat, allow_restrictions)\n      annotations = nil\n\n      # Parse annotations first since they would be before any actual param tokens.\n      # Do this in a loop to account for multiple annotations.\n      while @token.type.op_at_lsquare?\n        (annotations ||= Array(Annotation).new) << parse_annotation\n        skip_space_or_newline\n      end\n\n      if @token.type.op_amp?\n        next_token\n        space_after_amp = @token.type.space?\n        skip_space_or_newline\n\n        if @token.type.op_colon? && !space_after_amp # anonymous block arg without space\n          warnings.add_warning_at @token.location, \"space required before colon in type restriction (run `crystal tool format` to fix this)\"\n        end\n\n        block_param = parse_def_block_param(extra_assigns, annotations)\n        skip_space_or_newline\n        # When block_param.name is empty, this is an anonymous parameter.\n        # An anonymous parameter should not conflict other parameters names.\n        # (In fact `params` may contain anonymous splat parameter. See #9108).\n        # So check is skipped.\n        unless block_param.name.empty?\n          conflict_param = params.any?(&.name.==(block_param.name))\n          conflict_double_splat = found_double_splat && found_double_splat.name == block_param.name\n          if conflict_param || conflict_double_splat\n            raise \"duplicated def parameter name: #{block_param.name}\", block_param.location.not_nil!\n          end\n        end\n        return ArgExtras.new(block_param, false, false, false)\n      end\n\n      if found_double_splat\n        raise \"only block parameter is allowed after double splat\"\n      end\n\n      splat = false\n      double_splat = false\n      param_location = @token.location\n      allow_external_name = true\n\n      case @token.type\n      when .op_star?\n        if found_splat\n          unexpected_token\n        end\n\n        splat = true\n        allow_external_name = false\n        next_token_skip_space\n      when .op_star_star?\n        double_splat = true\n        allow_external_name = false\n        next_token_skip_space\n      else\n        # not a splat\n      end\n\n      found_space = false\n\n      if splat && (@token.type.op_comma? || @token.type.op_rparen?)\n        param_name = \"\"\n        allow_restrictions = false\n      else\n        param_location = @token.location\n        param_name, external_name, found_space, _uses_param = parse_param_name(param_location, extra_assigns, allow_external_name: allow_external_name)\n\n        params.each do |param|\n          if param.name == param_name\n            raise \"duplicated def parameter name: #{param_name}\", param_location\n          end\n\n          if param.external_name == external_name\n            raise \"duplicated def parameter external name: #{external_name}\", param_location\n          end\n        end\n\n        if @token.type.symbol?\n          raise \"space required after colon in type restriction\", @token\n        end\n      end\n\n      default_value = nil\n      restriction = nil\n\n      found_colon = false\n\n      if allow_restrictions && @token.type.op_colon?\n        if !default_value && !found_space\n          raise \"space required before colon in type restriction\", @token\n        end\n\n        next_token_skip_space_or_newline\n\n        location = @token.location\n        splat_restriction = false\n        if (splat && @token.type.op_star?) || (double_splat && @token.type.op_star_star?)\n          splat_restriction = true\n          next_token\n        end\n\n        restriction = parse_bare_proc_type\n\n        if splat_restriction\n          restriction = splat ? Splat.new(restriction) : DoubleSplat.new(restriction)\n          restriction.at(location)\n        end\n        found_colon = true\n      end\n\n      if @token.type.op_eq?\n        raise \"splat parameter can't have default value\", @token if splat\n        raise \"double splat parameter can't have default value\", @token if double_splat\n\n        slash_is_regex!\n        next_token_skip_space_or_newline\n\n        case @token.type\n        when .magic?\n          default_value = MagicConstant.new(@token.type).at(@token.location)\n          next_token\n        else\n          @no_type_declaration += 1\n          default_value = parse_op_assign\n          @no_type_declaration -= 1\n        end\n\n        skip_space\n      else\n        if found_default_value && !found_splat && !splat && !double_splat\n          raise \"parameter must have a default value\", param_location\n        end\n      end\n\n      unless found_colon\n        if @token.type.symbol?\n          raise \"the syntax for a parameter with a default value V and type T is `param : T = V`\", @token\n        end\n\n        if allow_restrictions && @token.type.op_colon?\n          raise \"the syntax for a parameter with a default value V and type T is `param : T = V`\", @token\n        end\n      end\n\n      raise \"BUG: param_name is nil\" unless param_name\n\n      param = Arg.new(param_name, default_value, restriction, external_name: external_name, parsed_annotations: annotations).at(param_location)\n      params << param\n      push_var param\n\n      ArgExtras.new(nil, !!default_value, splat, !!double_splat)\n    end\n\n    def parse_def_block_param(extra_assigns, annotations : Array(Annotation)?)\n      name_location = @token.location\n\n      if @token.type.op_rparen? || @token.type.newline? || @token.type.op_colon?\n        param_name = \"\"\n      else\n        param_name, _external_name, found_space, uses_param = parse_param_name(name_location, extra_assigns, allow_external_name: false)\n        @uses_block_arg = true if uses_param\n      end\n\n      if @token.type.op_colon?\n        unless param_name.empty? || found_space\n          warnings.add_warning_at @token.location, \"space required before colon in type restriction (run `crystal tool format` to fix this)\"\n        end\n        next_token_skip_space_or_newline\n\n        location = @token.location\n\n        type_spec = parse_bare_proc_type\n      end\n\n      block_param = Arg.new(param_name, restriction: type_spec, parsed_annotations: annotations).at(name_location)\n\n      push_var block_param\n\n      @block_arg_name = block_param.name\n\n      block_param\n    end\n\n    def parse_param_name(location, extra_assigns, allow_external_name)\n      do_next_token = true\n      found_string_literal = false\n      invalid_internal_name = nil\n      external_name_token = nil\n\n      if allow_external_name && (@token.type.ident? || string_literal_start?)\n        name_location = @token.location\n        external_name_token = @token.dup\n        if @token.type.ident?\n          if @token.keyword? && invalid_internal_name?(@token.value)\n            invalid_internal_name = @token.dup\n          end\n          external_name = @token.value.to_s\n          next_token\n        else\n          external_name = parse_string_without_interpolation(\"external name\")\n          found_string_literal = true\n        end\n\n        if external_name.empty?\n          raise \"external parameter name cannot be empty\", name_location\n        end\n\n        found_space = @token.type.space? || @token.type.newline?\n        skip_space\n        do_next_token = false\n      end\n\n      case @token.type\n      when .ident?\n        if @token.keyword? && invalid_internal_name?(@token.value)\n          raise \"cannot use '#{@token}' as a parameter name\", @token\n        end\n\n        param_name = @token.value.to_s\n        if param_name == external_name\n          raise \"when specified, external name must be different than internal name\", @token\n        end\n\n        check_valid_param_name\n\n        uses_param = false\n        do_next_token = true\n      when .instance_var?\n        # Transform `def foo(@x); end` to `def foo(x); @x = x; end`\n        param_name = @token.value.to_s[1..-1]\n        if param_name == external_name\n          raise \"when specified, external name must be different than internal name\", @token\n        end\n\n        # If it's something like @select, we can't transform it to:\n        #\n        #     @select = select\n        #\n        # because if someone uses `to_s` later it will produce invalid code.\n        # So we do something like:\n        #\n        # def method(select __arg0)\n        #   @select = __arg0\n        # end\n        #\n        # The external name defaults to the internal one unless otherwise\n        # specified (i.e. `def method(foo @select)`).\n        if invalid_internal_name?(param_name)\n          external_name ||= param_name\n          param_name = temp_arg_name\n        end\n\n        ivar = InstanceVar.new(@token.value.to_s).at(location)\n        var = Var.new(param_name).at(location)\n        assign = Assign.new(ivar, var).at(location)\n        if extra_assigns\n          extra_assigns.push assign\n        else\n          raise \"can't use @instance_variable here\"\n        end\n        uses_param = true\n        do_next_token = true\n      when .class_var?\n        param_name = @token.value.to_s[2..-1]\n        if param_name == external_name\n          raise \"when specified, external name must be different than internal name\", @token\n        end\n\n        # Same case as :INSTANCE_VAR for things like @select\n        if invalid_internal_name?(param_name)\n          external_name ||= param_name\n          param_name = temp_arg_name\n        end\n\n        cvar = ClassVar.new(@token.value.to_s).at(location)\n        var = Var.new(param_name).at(location)\n        assign = Assign.new(cvar, var).at(location)\n        if extra_assigns\n          extra_assigns.push assign\n        else\n          raise \"can't use @@class_var here\"\n        end\n        uses_param = true\n        do_next_token = true\n      else\n        if external_name\n          if found_string_literal\n            unexpected_token \"expected parameter internal name\"\n          end\n          if invalid_internal_name\n            raise \"cannot use '#{invalid_internal_name}' as a parameter name\", invalid_internal_name\n          end\n          param_name = external_name\n          if external_name_token.nil?\n            raise \"missing external name token\"\n          end\n          check_valid_param_name(external_name_token)\n        else\n          unexpected_token\n        end\n      end\n\n      if do_next_token\n        next_token\n        found_space = @token.type.space? || @token.type.newline?\n      end\n\n      skip_space\n\n      {param_name, external_name, found_space, uses_param}\n    end\n\n    def invalid_internal_name?(keyword)\n      case keyword\n      when Keyword\n        case keyword\n        # These names are handled as keyword by `Parser#parse_atomic_without_location`.\n        # We cannot assign value into them and never reference them,\n        # so they are invalid internal name.\n        when .begin?, Keyword::NIL, .true?, .false?, .yield?, .with?, .abstract?,\n             .def?, .macro?, .require?, .case?, .select?, .if?, .unless?, .include?,\n             .extend?, .class?, .struct?, .module?, .enum?, .while?, .until?, .return?,\n             .next?, .break?, .lib?, .fun?, .alias?, .pointerof?, .sizeof?, .offsetof?,\n             .instance_sizeof?, .typeof?, .private?, .protected?, .asm?, .out?,\n             .self?, Keyword::IN, .end?, .alignof?, .instance_alignof?\n          true\n        else\n          false\n        end\n      when String\n        case keyword\n        when \"begin\", \"nil\", \"true\", \"false\", \"yield\", \"with\", \"abstract\",\n             \"def\", \"macro\", \"require\", \"case\", \"select\", \"if\", \"unless\", \"include\",\n             \"extend\", \"class\", \"struct\", \"module\", \"enum\", \"while\", \"until\", \"return\",\n             \"next\", \"break\", \"lib\", \"fun\", \"alias\", \"pointerof\", \"sizeof\", \"offsetof\",\n             \"instance_sizeof\", \"typeof\", \"private\", \"protected\", \"asm\", \"out\",\n             \"self\", \"in\", \"end\", \"alignof\", \"instance_alignof\"\n          true\n        else\n          false\n        end\n      else\n        false\n      end\n    end\n\n    def check_valid_param_name(token : Token = @token)\n      param_name = token.value.to_s\n      if param_name[-1]?.in?('?', '!')\n        warnings.add_warning_at(token.location, \"invalid parameter name: #{param_name}\")\n      end\n    end\n\n    def parse_if(check_end = true)\n      location = @token.location\n\n      slash_is_regex!\n      next_token_skip_space_or_newline\n\n      cond = parse_op_assign_no_control allow_suffix: false\n      parse_if_after_condition cond, location, check_end\n    end\n\n    def parse_if_after_condition(cond, location, check_end)\n      slash_is_regex!\n      skip_statement_end\n\n      a_then = parse_expressions\n      skip_statement_end\n\n      else_location = nil\n      a_else = nil\n      if @token.type.ident?\n        case @token.value\n        when Keyword::ELSE\n          else_location = @token.location\n          next_token_skip_statement_end\n          a_else = parse_expressions\n        when Keyword::ELSIF\n          else_location = @token.location\n          a_else = parse_if check_end: false\n        end\n      end\n\n      end_location = token_end_location\n      if check_end\n        check_ident :end\n        next_token_skip_space\n      end\n\n      node = If.new(cond, a_then, a_else).at(location).at_end(end_location)\n      node.else_location = else_location\n      node\n    end\n\n    def parse_unless\n      location = @token.location\n\n      slash_is_regex!\n      next_token_skip_space_or_newline\n\n      cond = parse_op_assign_no_control allow_suffix: false\n      parse_unless_after_condition(cond, location)\n    end\n\n    def parse_unless_after_condition(cond, location)\n      slash_is_regex!\n      skip_statement_end\n\n      a_then = parse_expressions\n      skip_statement_end\n\n      a_else = nil\n      if @token.keyword?(:else)\n        else_location = @token.location\n        next_token_skip_statement_end\n        a_else = parse_expressions\n      end\n\n      check_ident :end\n      end_location = token_end_location\n      next_token_skip_space\n\n      node = Unless.new(cond, a_then, a_else).at(location).at_end(end_location)\n      node.else_location = else_location\n      node\n    end\n\n    def set_visibility(node)\n      if visibility = @visibility\n        node.visibility = visibility\n      end\n      node\n    end\n\n    def parse_var_or_call(global = false, force_call = false, location = @token.location)\n      end_location = token_end_location\n      doc = @token.doc\n\n      check AtomicWithMethodCheck\n\n      if @token.type.op_bang?\n        # only trigger from `parse_when_expression`\n        obj = Var.new(\"self\").at(location)\n        return parse_negation_suffix(obj)\n      end\n\n      case @token.value\n      when Keyword::IS_A_QUESTION\n        obj = Var.new(\"self\").at(location)\n        return parse_is_a(obj)\n      when Keyword::AS\n        obj = Var.new(\"self\").at(location)\n        return parse_as(obj)\n      when Keyword::AS_QUESTION\n        obj = Var.new(\"self\").at(location)\n        return parse_as?(obj)\n      when Keyword::RESPONDS_TO_QUESTION\n        obj = Var.new(\"self\").at(location)\n        return parse_responds_to(obj)\n      when Keyword::NIL_QUESTION\n        unless @in_macro_expression\n          obj = Var.new(\"self\").at(location)\n          return parse_nil?(obj)\n        end\n      else\n        # Not a special call, go on\n      end\n\n      name = @token.value.to_s\n      name_location = @token.location\n\n      if force_call && !@token.value\n        name = @token.type.to_s\n      end\n\n      is_var = var?(name)\n\n      # If the name is a var and '+' or '-' follow, never treat the name as a call\n      if is_var && next_comes_plus_or_minus?\n        var = Var.new(name)\n        var.doc = doc\n        var.location = name_location\n        var.end_location = end_location\n        next_token\n        return var\n      end\n\n      @wants_regex = false\n      next_token\n\n      name_followed_by_space = @token.type.space?\n      if name_followed_by_space\n        # We don't want the next token to be a regex literal if the call's name is\n        # a variable in the current scope (it's unlikely that there will be a method\n        # with that name that accepts a regex as a first argument).\n        # This allows us to write: a = 1; b = 2; a /b\n        @wants_regex = !is_var\n      end\n\n      case name\n      when \"super\"\n        @calls_super = true\n      when \"initialize\"\n        @calls_initialize = true\n      when \"previous_def\"\n        @calls_previous_def = true\n      else\n        # Not a special call\n      end\n\n      call_args = preserve_stop_on_do(@stop_on_do) { parse_call_args stop_on_do_after_space: @stop_on_do }\n\n      if call_args\n        args = call_args.args\n        block = call_args.block\n        block_arg = call_args.block_arg\n        named_args = call_args.named_args\n        has_parentheses = call_args.has_parentheses\n        force_call ||= has_parentheses || (args.try(&.empty?) == false) || (named_args.try(&.empty?) == false)\n      else\n        has_parentheses = false\n      end\n\n      if call_args && call_args.stopped_on_do_after_space\n        # This is the case when we have:\n        #\n        #     x = 1\n        #     foo x do\n        #         ^~~~\n        #     end\n        #\n        # In this case, since x is a variable and the previous call (foo)\n        # doesn't have parentheses, we don't parse \"x do end\" as an invocation\n        # to a method x with a block. Instead, we just stop on x and we don't\n        # consume the block, leaving the block for 'foo' to consume.\n        block = parse_curly_block(block)\n      elsif @stop_on_do && call_args && call_args.has_parentheses\n        # This is the case when we have:\n        #\n        #    foo x(y) do\n        #        ^~~~~~~\n        #    end\n        #\n        # We don't want to attach the block to `x`, but to `foo`.\n        block = parse_curly_block(block)\n      else\n        block = parse_block(block)\n      end\n\n      if block && block_arg\n        raise \"can't use captured and non-captured blocks together\", location\n      end\n\n      node =\n        if block || block_arg || global\n          call = Call.new(name, (args || [] of ASTNode), block, block_arg, named_args, global)\n          call.name_location = name_location\n          call.has_parentheses = has_parentheses\n          call\n        else\n          if args\n            maybe_var = !force_call && is_var\n            if maybe_var\n              Var.new(name)\n            else\n              call = Call.new(name, args, nil, nil, named_args, global)\n              call.name_location = name_location\n              call.has_parentheses = has_parentheses\n              call\n            end\n          else\n            if @no_type_declaration == 0 && @token.type.op_colon?\n              unless name_followed_by_space\n                warnings.add_warning_at(@token.location, \"space required before colon in type declaration (run `crystal tool format` to fix this)\")\n              end\n              declare_var = parse_type_declaration(Var.new(name).at(location).at_end(end_location))\n              end_location = declare_var.end_location\n\n              # Don't declare a local variable if it happens directly as an argument\n              # of a call, like `property foo : Int32` (we don't want `foo` to be a\n              # local variable afterwards.)\n              push_var declare_var unless @call_args_start_locations.includes?(location)\n              declare_var\n            elsif (!force_call && is_var)\n              if @block_arg_name && !@uses_block_arg && name == @block_arg_name\n                @uses_block_arg = true\n              end\n              Var.new(name)\n            else\n              if !force_call && !named_args && !global && @assigned_vars.includes?(name)\n                raise \"can't use variable name '#{name}' inside assignment to variable '#{name}'\", location\n              end\n\n              call = Call.new(name, [] of ASTNode, nil, nil, named_args, global)\n              call.name_location = name_location\n              call.has_parentheses = has_parentheses\n              call\n            end\n          end\n        end\n      node.doc = doc\n      node.location = location\n      node.end_location = block.try(&.end_location) || call_args.try(&.end_location) || end_location\n      node\n    end\n\n    def next_comes_plus_or_minus?\n      pos = current_pos\n      while current_char.ascii_whitespace?\n        next_char_no_column_increment\n      end\n      comes_plus_or_minus = current_char.in?('+', '-')\n      self.current_pos = pos\n      comes_plus_or_minus\n    end\n\n    def preserve_stop_on_do(new_value = false, &)\n      old_stop_on_do = @stop_on_do\n      @stop_on_do = new_value\n      value = yield\n      @stop_on_do = old_stop_on_do\n      value\n    end\n\n    def parse_block(block, stop_on_do = false)\n      if @token.keyword?(:do)\n        return block if stop_on_do\n\n        raise \"block already specified with &\" if block\n        parse_block2 do |body|\n          parse_exception_handler body, implicit: true\n        end\n      else\n        parse_curly_block(block)\n      end\n    end\n\n    def parse_curly_block(block)\n      if @token.type.op_lcurly?\n        raise \"block already specified with &\" if block\n        parse_block2 do |body|\n          check :OP_RCURLY\n          end_location = token_end_location\n          slash_is_not_regex!\n          next_token_skip_space\n          {body, end_location}\n        end\n      else\n        block\n      end\n    end\n\n    def parse_block2(&)\n      location = @token.location\n      block_params, splat_index, unpacks = parse_block_params(location)\n\n      with_lexical_var_scope do\n        push_vars block_params\n\n        unpacks.try &.each do |index, expressions|\n          push_block_vars(expressions)\n        end\n\n        block_body = parse_expressions\n        block_body, end_location = yield block_body\n\n        Block.new(block_params, block_body, splat_index, unpacks).at(location).at_end(end_location)\n      end\n    end\n\n    def parse_block_params(location)\n      block_params = [] of Var\n      all_names = [] of String\n      param_index = 0\n      splat_index = nil\n      unpacks = nil\n\n      slash_is_regex!\n      next_token_skip_space\n      if @token.type.op_bar?\n        next_token_skip_space_or_newline\n        while true\n          var, found_splat, unpack_expressions = parse_block_param(\n            found_splat: !!splat_index,\n            all_names: all_names,\n          )\n          splat_index ||= param_index if found_splat\n          block_params << var\n\n          if unpack_expressions\n            unpacks ||= {} of Int32 => Expressions\n            unpacks[param_index] = Expressions.new(unpack_expressions)\n          end\n\n          next_token_skip_space_or_newline\n\n          case @token.type\n          when .op_comma?\n            next_token_skip_space_or_newline\n            break if @token.type.op_bar?\n          when .op_bar?\n            break\n          else\n            raise \"expecting ',' or '|', not #{@token}\", @token\n          end\n\n          param_index += 1\n        end\n        next_token_skip_statement_end\n      else\n        skip_statement_end\n      end\n\n      {block_params, splat_index, unpacks}\n    end\n\n    def parse_block_param(found_splat, all_names : Array(String))\n      if @token.type.op_star?\n        if found_splat\n          raise \"splat block parameter already specified\", @token\n        end\n        found_splat = true\n        next_token\n      end\n\n      location = @token.location\n\n      case @token.type\n      when .ident?\n        if @token.keyword? && invalid_internal_name?(@token.value)\n          raise \"cannot use '#{@token}' as a block parameter name\", @token\n        end\n\n        param_name = @token.value.to_s\n        check_valid_param_name\n\n        if all_names.includes?(param_name)\n          raise \"duplicated block parameter name: #{param_name}\", @token\n        end\n        all_names << param_name\n      when .underscore?\n        param_name = \"_\"\n      when .op_lparen?\n        next_token_skip_space_or_newline\n\n        unpack_expressions = [] of ASTNode\n        found_splat_in_nested_expression = false\n\n        while true\n          sub_location = @token.location\n          sub_var, new_found_splat_in_nested_expression, sub_unpack_expressions = parse_block_param(\n            found_splat: found_splat_in_nested_expression,\n            all_names: all_names,\n          )\n\n          unpack_expression =\n            if sub_unpack_expressions\n              Expressions.new(sub_unpack_expressions).at(sub_location)\n            elsif sub_var.name == \"_\"\n              Underscore.new.at(sub_location)\n            else\n              sub_var\n            end\n\n          if new_found_splat_in_nested_expression && !found_splat_in_nested_expression\n            unpack_expression = Splat.new(unpack_expression).at(sub_location)\n          end\n          found_splat_in_nested_expression = new_found_splat_in_nested_expression\n\n          unpack_expressions << unpack_expression\n\n          next_token_skip_space_or_newline\n          case @token.type\n          when .op_comma?\n            next_token_skip_space_or_newline\n            break if @token.type.op_rparen?\n          when .op_rparen?\n            break\n          else\n            raise \"expecting ',' or ')', not #{@token}\", @token\n          end\n        end\n\n        param_name = \"\"\n      else\n        raise \"expecting block parameter name, not #{@token.type}\", @token\n      end\n\n      var = Var.new(param_name).at(location)\n      {var, found_splat, unpack_expressions}\n    end\n\n    def push_block_vars(node)\n      case node\n      when Expressions\n        node.expressions.each do |expression|\n          push_block_vars(expression)\n        end\n      when Var\n        push_var node\n      when Underscore\n        # Nothing to do\n      when Splat\n        push_block_vars(node.exp)\n      else\n        raise \"BUG: unexpected block var: #{node} (#{node.class})\"\n      end\n    end\n\n    record CallArgs,\n      args : Array(ASTNode)?,\n      block : Block?,\n      block_arg : ASTNode?,\n      named_args : Array(NamedArgument)?,\n      stopped_on_do_after_space : Bool,\n      end_location : Location?,\n      has_parentheses : Bool\n\n    def parse_call_args(stop_on_do_after_space = false, allow_curly = false, control = false)\n      case @token.type\n      when .op_lcurly?\n        nil\n      when .op_lparen?\n        slash_is_regex!\n\n        args = [] of ASTNode\n        end_location = nil\n\n        open(\"call\") do\n          # We found a parentheses, so calls inside it will get the `do`\n          # attached to them\n          @stop_on_do = false\n          found_double_splat = false\n\n          next_token_skip_space_or_newline\n          while !@token.type.op_rparen?\n            if call_block_arg_follows?\n              return parse_call_block_arg(args, true)\n            end\n\n            if named_tuple_start?\n              return parse_call_args_named_args(@token.location, args, first_name: nil, allow_newline: true)\n            else\n              arg = parse_call_arg(found_double_splat)\n              if @token.type.op_colon? && arg.is_a?(StringLiteral)\n                return parse_call_args_named_args(arg.location.not_nil!, args, first_name: arg.value, allow_newline: true)\n              else\n                args << arg\n                found_double_splat = arg.is_a?(DoubleSplat)\n              end\n            end\n\n            skip_space\n            if @token.type.op_comma?\n              slash_is_regex!\n              next_token_skip_space_or_newline\n            else\n              skip_space_or_newline\n              check :OP_RPAREN\n              break\n            end\n          end\n          end_location = token_end_location\n          @wants_regex = false\n          next_token_skip_space\n        end\n\n        CallArgs.new args, nil, nil, nil, false, end_location, has_parentheses: true\n      when .space?\n        slash_is_not_regex!\n        end_location = token_end_location\n        next_token\n\n        if stop_on_do_after_space && @token.keyword?(:do)\n          return CallArgs.new nil, nil, nil, nil, true, end_location, has_parentheses: false\n        end\n\n        if control && @token.keyword?(:do)\n          unexpected_token\n        end\n\n        parse_call_args_space_consumed check_plus_and_minus: true, allow_curly: allow_curly, control: control\n      else\n        nil\n      end\n    end\n\n    def parse_call_args_space_consumed(check_plus_and_minus = true, allow_curly = false, control = false, end_token : Token::Kind = :OP_RPAREN,\n                                       allow_beginless_range = false)\n      if @token.keyword?(:end) && !next_comes_colon_space?\n        return nil\n      end\n\n      case @token.type\n      when .op_amp?\n        return nil if current_char.ascii_whitespace?\n      when .op_plus?, .op_minus?\n        if check_plus_and_minus\n          return nil if current_char.ascii_whitespace?\n        end\n      when .op_lcurly?\n        return nil unless allow_curly\n      when .char?, .string?, .delimiter_start?, .string_array_start?, .symbol_array_start?,\n           .number?, .ident?, .symbol?, .instance_var?, .class_var?, .const?, .global?,\n           .op_dollar_tilde?, .op_dollar_question?, .global_match_data_index?, .op_lparen?,\n           .op_bang?, .op_lsquare?, .op_lsquare_rsquare?, .op_tilde?, .op_minus_gt?,\n           .op_lcurly_lcurly?, .magic?, .underscore?\n        # Nothing\n      when .op_star?, .op_star_star?\n        if current_char.ascii_whitespace?\n          return nil\n        end\n      when .op_colon_colon?\n        if current_char.ascii_whitespace?\n          return nil\n        end\n      when .op_period_period?, .op_period_period_period?\n        return nil unless allow_beginless_range\n      else\n        return nil\n      end\n\n      case @token.value\n      when Keyword::IF, Keyword::UNLESS, Keyword::WHILE, Keyword::UNTIL, Keyword::RESCUE, Keyword::ENSURE\n        return nil unless next_comes_colon_space?\n      when Keyword::YIELD\n        return nil if @stop_on_yield > 0 && !next_comes_colon_space?\n      else\n        # keep going\n      end\n\n      args = [] of ASTNode\n      end_location = nil\n\n      # On calls without parentheses we want to stop on `do`.\n      # The exception is when parsing `return`, `break`, `yield`\n      # and `next` arguments (marked with the `control` flag),\n      # because this:\n      #\n      # ```\n      # return foo do\n      # end\n      # ```\n      #\n      # must always be parsed as the block belonging to `foo`,\n      # never to `return`.\n      @stop_on_do = true unless control\n\n      found_double_splat = false\n\n      while !@token.type.newline? && !@token.type.op_semicolon? && !@token.type.eof? && @token.type != end_token && !@token.type.op_colon? && !end_token?\n        if call_block_arg_follows?\n          return parse_call_block_arg(args, false)\n        end\n\n        if @token.type.ident? && current_char == ':'\n          return parse_call_args_named_args(@token.location, args, first_name: nil, allow_newline: false)\n        else\n          arg = parse_call_arg(found_double_splat)\n          if @token.type.op_colon? && arg.is_a?(StringLiteral)\n            return parse_call_args_named_args(arg.location.not_nil!, args, first_name: arg.value, allow_newline: false)\n          else\n            args << arg\n            found_double_splat = arg.is_a?(DoubleSplat)\n          end\n          end_location = arg.end_location\n        end\n\n        skip_space\n\n        if @token.type.op_comma?\n          location = @token.location\n          slash_is_regex!\n          next_token_skip_space_or_newline\n          raise \"invalid trailing comma in call\", location if (@token.keyword?(:end) && !next_comes_colon_space?) || @token.type.eof?\n        else\n          break\n        end\n      end\n\n      CallArgs.new args, nil, nil, nil, false, end_location, has_parentheses: false\n    end\n\n    def parse_call_args_named_args(location, args, first_name, allow_newline)\n      named_args = parse_named_args(location, first_name: first_name, allow_newline: allow_newline)\n\n      if call_block_arg_follows?\n        return parse_call_block_arg(args, check_paren: allow_newline, named_args: named_args)\n      end\n\n      check :OP_RPAREN if allow_newline\n\n      # Prefer the end location of the last named argument when available.\n      # Using `token_end_location` would produce an off-by-one column\n      # in some cases (see issue #16092).\n      end_location = named_args.last?.try(&.end_location) unless allow_newline\n      end_location ||= token_end_location\n\n      if allow_newline\n        next_token_skip_space\n      else\n        skip_space\n      end\n      CallArgs.new args, nil, nil, named_args, false, end_location, has_parentheses: allow_newline\n    end\n\n    def parse_named_args(location, first_name = nil, allow_newline = false)\n      named_args = [] of NamedArgument\n      while true\n        if first_name\n          name = first_name\n          first_name = nil\n        else\n          if named_tuple_start?\n            name = @token.value.to_s\n            next_token_never_a_symbol\n          elsif string_literal_start?\n            name = parse_string_without_interpolation(\"named argument\")\n          else\n            raise \"expected named argument, not #{@token}\", location\n          end\n        end\n\n        if name.empty?\n          raise \"named argument cannot have an empty name\", location\n        end\n\n        if named_args.any? { |arg| arg.name == name }\n          raise \"duplicated named argument: #{name}\", location\n        end\n\n        check :OP_COLON\n        slash_is_regex!\n        next_token_skip_space_or_newline\n\n        if @token.keyword?(:out)\n          value = parse_out\n        else\n          @call_args_start_locations << @token.location\n          value =\n            begin\n              parse_op_assign\n            ensure\n              @call_args_start_locations.pop\n            end\n        end\n\n        named_args << NamedArgument.new(name, value).at(location)\n        skip_space\n        if @token.type.op_comma?\n          next_token_skip_space_or_newline\n          if @token.type.op_rparen? || @token.type.op_amp? || @token.type.op_rsquare?\n            break\n          end\n        elsif @token.type.newline? && allow_newline\n          skip_space_or_newline\n          break\n        else\n          break\n        end\n\n        location = @token.location\n      end\n      named_args\n    end\n\n    def parse_call_arg(found_double_splat = false)\n      @call_args_start_locations.push @token.location\n\n      if @token.keyword?(:out)\n        if found_double_splat\n          raise \"out argument not allowed after double splat\"\n        end\n\n        parse_out\n      else\n        location = @token.location\n        splat = nil\n        case @token.type\n        when .op_star?\n          unless current_char.ascii_whitespace?\n            if found_double_splat\n              raise \"splat not allowed after double splat\"\n            end\n\n            splat = :single\n            next_token\n          end\n        when .op_star_star?\n          unless current_char.ascii_whitespace?\n            splat = :double\n            next_token\n          end\n        else\n          # not a splat\n        end\n\n        arg = parse_op_assign_no_control\n\n        if found_double_splat && splat != :double\n          raise \"argument not allowed after double splat\", arg.location.not_nil!\n        end\n\n        case splat\n        when :single\n          arg = Splat.new(arg).at(location).at_end(arg)\n        when :double\n          arg = DoubleSplat.new(arg).at(location).at_end(arg)\n        else\n          # no splat\n        end\n\n        arg\n      end\n    ensure\n      @call_args_start_locations.pop\n    end\n\n    def parse_out\n      location = @token.location\n      next_token_skip_space_or_newline\n      name = @token.value.to_s\n\n      case @token.type\n      when .ident?\n        var = Var.new(name)\n        push_var var\n      when .instance_var?\n        var = InstanceVar.new(name)\n      when .underscore?\n        var = Underscore.new\n      else\n        raise \"expecting variable or instance variable after out\"\n      end\n      var.at(@token.location).at_end(token_end_location)\n      next_token\n      Out.new(var).at(location).at_end(var)\n    end\n\n    def parse_generic_or_global_call\n      location = @token.location\n      next_token_skip_space_or_newline\n\n      case @token.type\n      when .ident?\n        set_visibility parse_var_or_call global: true, location: location\n      when .const?\n        ident = parse_generic global: true, location: location, expression: true\n        skip_space\n        parse_custom_literal ident\n      else\n        unexpected_token\n      end\n    end\n\n    # Parse a **bare** proc type like `A, B, C -> D`.\n    # Generally it is entry point of type parsing and\n    # it is used on the context expected type (e.g. type restrictions, rhs of `alias` and more)\n    def parse_bare_proc_type\n      type = parse_type_splat { parse_union_type }\n\n      # To determine to consume comma, looking-ahead is needed.\n      # Consider `[ [] of Int32, Foo.new ]`, we want to parse it as `[ ([] of Int32), Foo.new ]` of course.\n      # If the parser consumes comma after Int32 quickly, it may cause parsing error.\n      unless @token.type.op_minus_gt? || (@token.type.op_comma? && type_start?(consume_newlines: true))\n        if type.is_a?(Splat)\n          raise \"invalid type splat\", type.location.not_nil!\n        end\n        return type\n      end\n\n      input_types = [type]\n      if !@token.type.op_minus_gt?\n        loop do\n          next_token_skip_space_or_newline\n          input_types << parse_type_splat { parse_union_type }\n          break unless @token.type.op_comma? && type_start?(consume_newlines: true)\n        end\n      end\n\n      parse_proc_type_output(input_types, input_types.first.location)\n    end\n\n    def parse_union_type\n      type = parse_atomic_type_with_suffix\n      return type unless @token.type.op_bar?\n\n      types = [type]\n      while @token.type.op_bar?\n        next_token_skip_space_or_newline\n        types << parse_atomic_type_with_suffix\n      end\n\n      Union.new(types).at(type).at_end(types.last)\n    end\n\n    def parse_atomic_type_with_suffix\n      type = parse_atomic_type\n      parse_type_suffix type\n    end\n\n    def parse_atomic_type\n      location = @token.location\n\n      case @token.type\n      when .ident?\n        case @token.value\n        when Keyword::SELF\n          next_token_skip_space\n          Self.new.at(location)\n        when \"self?\"\n          next_token_skip_space\n          make_nilable_type Self.new.at(location)\n        when Keyword::TYPEOF\n          parse_typeof\n        else\n          unexpected_token\n        end\n      when .underscore?\n        next_token_skip_space\n        Underscore.new.at(location)\n      when .const?, .op_colon_colon?\n        type = parse_generic\n        skip_space\n        type\n      when .op_lcurly?\n        next_token_skip_space_or_newline\n        if named_tuple_start? || @token.type.delimiter_start?\n          type = make_named_tuple_type parse_named_type_args(:OP_RCURLY)\n        else\n          type = make_tuple_type parse_union_types(:OP_RCURLY, allow_splats: true)\n        end\n        check :OP_RCURLY\n        end_location = token_end_location\n        next_token_skip_space\n        type.at(location).at_end(end_location)\n      when .op_minus_gt?\n        parse_proc_type_output(nil, location)\n      when .op_lparen?\n        next_token_skip_space_or_newline\n        if @token.type.op_rparen?\n          # ProcNotation without argument types: `() ->`\n          next_token_skip_space\n          check :OP_MINUS_GT\n          return parse_proc_type_output([] of ASTNode, location)\n        end\n\n        type = parse_type_splat { parse_union_type }\n        if type.is_a?(Union)\n          type.at(location).at_end(@token.location)\n        end\n        if @token.type.op_rparen?\n          next_token_skip_space\n          if @token.type.op_minus_gt? # `(A) -> B` case\n            type = parse_proc_type_output([type], location)\n          elsif type.is_a?(Splat)\n            raise \"invalid type splat\", type.location.not_nil!\n          end\n        else\n          input_types = [type]\n          while @token.type.op_comma?\n            next_token_skip_space_or_newline\n            break if @token.type.op_rparen? # allow trailing comma\n            input_types << parse_type_splat { parse_union_type }\n          end\n          if @token.type.op_minus_gt? # `(A, B, C -> D)` case\n            type = parse_proc_type_output(input_types, input_types.first.location)\n            check :OP_RPAREN\n            next_token_skip_space\n          else # `(A, B, C) -> D` case\n            check :OP_RPAREN\n            next_token_skip_space\n            type = parse_proc_type_output(input_types, location)\n          end\n        end\n        type\n      else\n        unexpected_token\n      end\n    end\n\n    def parse_union_types(end_token : Token::Kind, *, allow_splats = false)\n      type = allow_splats ? parse_type_splat { parse_union_type } : parse_union_type\n      types = [type]\n\n      while @token.type.op_comma?\n        next_token_skip_space_or_newline\n        break if @token.type == end_token # allow trailing comma\n        type = allow_splats ? parse_type_splat { parse_union_type } : parse_union_type\n        types << type\n      end\n\n      types\n    end\n\n    # Parse generic type path like `A::B(C, D)?`.\n    # This method is used to parse not only a type, but also an expression represents type.\n    # And it also consumes prefix `::` to specify global path.\n    def parse_generic(expression = false)\n      location = @token.location\n\n      global = false\n      if @token.type.op_colon_colon?\n        next_token_skip_space_or_newline\n        global = true\n      end\n\n      parse_generic global, location, expression\n    end\n\n    def parse_generic(global, location, expression)\n      path = parse_path(global, location)\n      type = parse_type_args(path)\n\n      # Nilable suffixes without any spaces are consumed here\n      # for expression represents nilable type. Typically such an expression\n      # is appeared in macro expression. (e.g. `{% if T <= Int32? %} ... {% end %}`)\n      # Note that the parser cannot consume any spaces because it conflicts ternary operator.\n      while expression && @token.type.op_question?\n        end_location = token_end_location\n        next_token\n        type = make_nilable_expression(type).at_end(end_location)\n      end\n\n      type\n    end\n\n    # Parse type path.\n    # It also consumes prefix `::` to specify global path.\n    def parse_path\n      location = @token.location\n\n      global = false\n      if @token.type.op_colon_colon?\n        next_token_skip_space_or_newline\n        global = true\n      end\n\n      parse_path(global, location)\n    end\n\n    def parse_path(global, location)\n      names = [check_const]\n      end_location = token_end_location\n\n      @wants_regex = false\n      next_token\n      while @token.type.op_colon_colon?\n        next_token_skip_space_or_newline\n        names << check_const\n        end_location = token_end_location\n\n        @wants_regex = false\n        next_token\n      end\n\n      Path.new(names, global).at(location).at_end(end_location)\n    end\n\n    def parse_type_args(name)\n      return name unless @token.type.op_lparen?\n\n      next_token_skip_space_or_newline\n      args = [] of ASTNode\n      if named_tuple_start? || string_literal_start?\n        named_args = parse_named_type_args(:OP_RPAREN)\n      elsif !@token.type.op_rparen?\n        args << parse_type_splat { parse_type_arg }\n        while @token.type.op_comma?\n          next_token_skip_space_or_newline\n          break if @token.type.op_rparen? # allow trailing comma\n          args << parse_type_splat { parse_type_arg }\n        end\n\n        has_int = args.any? do |arg|\n          arg.is_a?(NumberLiteral) || arg.is_a?(SizeOf) || arg.is_a?(InstanceSizeOf) ||\n            arg.is_a?(AlignOf) || arg.is_a?(InstanceAlignOf) || arg.is_a?(OffsetOf)\n        end\n\n        if @token.type.op_minus_gt? && !has_int\n          args = [parse_proc_type_output(args, args.first.location)] of ASTNode\n        end\n      end\n\n      skip_space_or_newline\n      check :OP_RPAREN\n      end_location = token_end_location\n      next_token\n\n      Generic.new(name, args, named_args).at(name).at_end(end_location)\n    end\n\n    def parse_named_type_args(end_token : Token::Kind)\n      named_args = [] of NamedArgument\n\n      while @token.type != end_token\n        name_location = @token.location\n        if named_tuple_start?\n          name = @token.value.to_s\n          next_token\n        elsif string_literal_start?\n          name = parse_string_without_interpolation(\"named argument\")\n        else\n          raise \"expected '#{end_token}' or named argument, not #{@token}\", @token\n        end\n\n        if name.empty?\n          raise \"named argument cannot have an empty name\", name_location\n        end\n\n        if named_args.any? { |arg| arg.name == name }\n          raise \"duplicated key: #{name}\", @token\n        end\n\n        check :OP_COLON\n        next_token_skip_space_or_newline\n\n        type = parse_bare_proc_type\n        skip_space\n\n        named_args << NamedArgument.new(name, type).at(name_location)\n\n        if @token.type.op_comma?\n          next_token_skip_space_or_newline\n        else\n          skip_space_or_newline\n          check end_token\n          break\n        end\n      end\n\n      named_args\n    end\n\n    def parse_type_splat(&)\n      location = @token.location\n\n      splat = false\n      if @token.type.op_star?\n        next_token_skip_space_or_newline\n        splat = true\n      end\n\n      type = yield\n      type = Splat.new(type).at(location) if splat\n      type\n    end\n\n    def parse_type_arg\n      if @token.type.number?\n        num = NumberLiteral.new(@token.value.to_s, @token.number_kind).at(@token.location)\n        next_token_skip_space\n        return num\n      end\n\n      case @token\n      when .keyword?(:sizeof)\n        parse_sizeof\n      when .keyword?(:instance_sizeof)\n        parse_instance_sizeof\n      when .keyword?(:alignof)\n        parse_alignof\n      when .keyword?(:instance_alignof)\n        parse_instance_alignof\n      when .keyword?(:offsetof)\n        parse_offsetof\n      else\n        parse_union_type\n      end\n    end\n\n    def parse_type_suffix(type)\n      loop do\n        end_location = token_end_location\n        case @token.type\n        when .op_period?\n          next_token_skip_space_or_newline\n          check_ident :class\n          end_location = token_end_location\n          next_token_skip_space\n          type = Metaclass.new(type).at(type).at_end(end_location)\n        when .op_question?\n          next_token_skip_space\n          type = make_nilable_type(type).at_end(end_location)\n        when .op_star?\n          next_token_skip_space\n          type = make_pointer_type(type).at_end(end_location)\n        when .op_star_star?\n          next_token_skip_space\n          type = make_pointer_type(make_pointer_type(type)).at_end(end_location)\n        when .op_lsquare?\n          next_token_skip_space_or_newline\n          size = parse_type_arg\n          skip_space_or_newline\n          check :OP_RSQUARE\n          end_location = token_end_location\n          next_token_skip_space\n          type = make_static_array_type(type, size).at_end(end_location)\n        else\n          return type\n        end\n      end\n    end\n\n    def parse_proc_type_output(input_types, location)\n      has_output_type = type_start?(consume_newlines: false)\n\n      check :OP_MINUS_GT\n      end_location = token_end_location\n      next_token_skip_space\n\n      if has_output_type\n        skip_space_or_newline\n        output_type = parse_union_type\n        end_location = output_type.end_location\n      end\n\n      ProcNotation.new(input_types, output_type).at(location).at_end(end_location)\n    end\n\n    def make_nilable_type(type)\n      Union.new([type, Path.global(\"Nil\").at(type)]).at(type)\n    end\n\n    def make_nilable_expression(type)\n      type = Generic.new(Path.global(\"Union\").at(type), [type, Path.global(\"Nil\").at(type)]).at(type)\n      type.suffix = :question\n      type\n    end\n\n    def make_pointer_type(type)\n      type = Generic.new(Path.global(\"Pointer\").at(type), [type] of ASTNode).at(type)\n      type.suffix = :asterisk\n      type\n    end\n\n    def make_static_array_type(type, size)\n      type = Generic.new(Path.global(\"StaticArray\").at(type), [type, size] of ASTNode).at(type)\n      type.suffix = :bracket\n      type\n    end\n\n    def make_tuple_type(types)\n      Generic.new(Path.global(\"Tuple\"), types)\n    end\n\n    def make_named_tuple_type(named_args)\n      Generic.new(Path.global(\"NamedTuple\"), [] of ASTNode, named_args: named_args)\n    end\n\n    # Looks ahead next tokens to check whether they indicate type.\n    def type_start?(*, consume_newlines)\n      peek_ahead do\n        if consume_newlines\n          next_token_skip_space_or_newline\n        else\n          next_token_skip_space\n        end\n\n        type_start?\n      rescue\n        false\n      end\n    end\n\n    def type_start?\n      while @token.type.op_lparen? || @token.type.op_lcurly?\n        next_token_skip_space_or_newline\n      end\n\n      # TODO: the below conditions are not complete, and there are many false-positive or true-negative examples.\n\n      case @token.type\n      when .ident?\n        return false if named_tuple_start?\n        case @token.value\n        when Keyword::TYPEOF\n          true\n        when Keyword::SELF, \"self?\"\n          next_token_skip_space\n          delimiter_or_type_suffix?\n        else\n          false\n        end\n      when .const?\n        return false if named_tuple_start?\n        type_path_start?\n      when .op_colon_colon?\n        next_token\n        type_path_start?\n      when .underscore?, .op_minus_gt?\n        true\n      when .op_star?\n        next_token_skip_space_or_newline\n        type_start?\n      else\n        false\n      end\n    end\n\n    def type_path_start?\n      while @token.type.const?\n        next_token\n        break unless @token.type.op_colon_colon?\n        next_token_skip_space_or_newline\n      end\n\n      skip_space\n      delimiter_or_type_suffix?\n    end\n\n    def delimiter_or_type_suffix?\n      case @token.type\n      when .op_period?\n        next_token_skip_space_or_newline\n        @token.keyword?(:class)\n      when .op_question?, .op_star?, .op_star_star?\n        # They are conflicted with operators, so more look-ahead is needed.\n        next_token_skip_space\n        delimiter_or_type_suffix?\n      when .op_minus_gt?, .op_bar?, .op_comma?, .op_eq_gt?, .newline?, .eof?,\n           .op_eq?, .op_semicolon?, .op_lparen?, .op_rparen?, .op_lsquare?, .op_rsquare?\n        # -> | , => \\n EOF = ; ( ) [ ]\n        true\n      else\n        false\n      end\n    end\n\n    def parse_typeof\n      location = @token.location\n\n      next_token_skip_space\n      check :OP_LPAREN\n      next_token_skip_space_or_newline\n      if @token.type.op_rparen?\n        raise \"missing typeof argument\"\n      end\n\n      with_lexical_var_scope do\n        exps = [] of ASTNode\n        while !@token.type.op_rparen?\n          exps << parse_op_assign\n          if @token.type.op_comma?\n            next_token_skip_space_or_newline\n          else\n            skip_space_or_newline\n            check :OP_RPAREN\n          end\n        end\n\n        end_location = token_end_location\n        next_token_skip_space\n\n        TypeOf.new(exps).at(location).at_end(end_location)\n      end\n    end\n\n    def parse_visibility_modifier(modifier)\n      doc = @token.doc\n      location = @token.location\n\n      next_token_skip_space\n      exp = parse_op_assign\n\n      modifier = VisibilityModifier.new(modifier, exp).at(location)\n      modifier.doc = doc\n      exp.doc = doc\n      modifier\n    end\n\n    def parse_asm\n      next_token_skip_space\n      check :OP_LPAREN\n      next_token_skip_space_or_newline\n      text = parse_string_without_interpolation(\"asm\")\n      skip_space_or_newline\n\n      volatile = false\n      alignstack = false\n      intel = false\n      can_throw = false\n\n      part_index = 0\n      until @token.type.op_rparen?\n        if @token.type.op_colon_colon?\n          next_token_skip_space_or_newline\n          part_index += 2\n        elsif @token.type.op_colon?\n          next_token_skip_space_or_newline\n          part_index += 1\n        else\n          unexpected_token\n        end\n\n        case part_index\n        when 1\n          if @token.type.delimiter_start?\n            outputs = parse_asm_operands\n          end\n        when 2\n          if @token.type.delimiter_start?\n            inputs = parse_asm_operands\n          end\n        when 3\n          if @token.type.delimiter_start?\n            clobbers = parse_asm_clobbers\n          end\n        when 4\n          if @token.type.delimiter_start?\n            volatile, alignstack, intel, can_throw = parse_asm_options\n          end\n        else break\n        end\n      end\n\n      check :OP_RPAREN\n\n      end_location = token_end_location\n\n      next_token_skip_space\n\n      Asm.new(text, outputs, inputs, clobbers, volatile, alignstack, intel, can_throw).at_end(end_location)\n    end\n\n    def parse_asm_operands\n      operands = [] of AsmOperand\n      while true\n        operands << parse_asm_operand\n        if @token.type.op_comma?\n          next_token_skip_space_or_newline\n        end\n        break unless @token.type.delimiter_start?\n      end\n      operands\n    end\n\n    def parse_asm_operand\n      text = parse_string_without_interpolation(\"constraint\")\n      check :OP_LPAREN\n      next_token_skip_space_or_newline\n      exp = parse_expression\n      check :OP_RPAREN\n      next_token_skip_space_or_newline\n      AsmOperand.new(text, exp)\n    end\n\n    def parse_asm_clobbers\n      clobbers = [] of String\n      while true\n        clobbers << parse_string_without_interpolation(\"asm clobber\")\n        skip_space_or_newline\n        if @token.type.op_comma?\n          next_token_skip_space_or_newline\n        end\n        break unless @token.type.delimiter_start?\n      end\n      clobbers\n    end\n\n    def parse_asm_options\n      volatile = false\n      alignstack = false\n      intel = false\n      can_throw = false\n      while true\n        location = @token.location\n        option = parse_string_without_interpolation(\"asm option\")\n        skip_space_or_newline\n        case option\n        when \"volatile\"\n          volatile = true\n        when \"alignstack\"\n          alignstack = true\n        when \"intel\"\n          intel = true\n        when \"unwind\"\n          can_throw = true\n        else\n          raise \"unknown asm option: #{option}\", location\n        end\n\n        if @token.type.op_comma?\n          next_token_skip_space_or_newline\n        end\n        break unless @token.type.delimiter_start?\n      end\n      {volatile, alignstack, intel, can_throw}\n    end\n\n    def parse_yield_with_scope\n      location = @token.location\n      next_token_skip_space\n      @stop_on_yield += 1\n      @block_arity ||= 1\n      scope = parse_op_assign\n      @stop_on_yield -= 1\n      skip_space\n      check_ident :yield\n      parse_yield scope, location\n    end\n\n    def parse_yield(scope = nil, location = @token.location)\n      end_location = token_end_location\n      next_token\n\n      call_args = preserve_stop_on_do { parse_call_args control: true }\n\n      if call_args\n        args = call_args.args\n        end_location = nil\n      end\n\n      block_arity = (@block_arity ||= 0)\n      if args && args.size > block_arity\n        @block_arity = args.size\n      end\n\n      Yield.new(args || [] of ASTNode, scope, !!call_args.try(&.has_parentheses)).at(location).at_end(end_location)\n    end\n\n    def parse_break\n      parse_control_expression Break\n    end\n\n    def parse_return\n      parse_control_expression Return\n    end\n\n    def parse_next\n      parse_control_expression Next\n    end\n\n    def parse_control_expression(klass)\n      end_location = token_end_location\n      next_token\n\n      call_args = preserve_stop_on_do { parse_call_args allow_curly: true, control: true }\n      args = call_args.args if call_args\n\n      if args && !args.empty?\n        if args.size == 1 && !args.first.is_a?(Splat)\n          node = klass.new(args.first)\n        else\n          tuple = TupleLiteral.new(args).at(args.first).at_end(args.last)\n          node = klass.new(tuple)\n        end\n      else\n        node = klass.new.at_end(end_location)\n      end\n\n      node\n    end\n\n    def parse_lib\n      doc = @token.doc\n      location = @token.location\n      next_token_skip_space_or_newline\n\n      name_location = @token.location\n      name = parse_path\n      check StatementEnd\n      skip_statement_end\n\n      body = push_visibility { parse_lib_body_expressions }\n\n      check_ident :end\n      end_location = token_end_location\n      next_token_skip_space\n\n      lib_def = LibDef.new(name, body).at(location).at_end(end_location)\n      lib_def.name_location = name_location\n      lib_def.doc = doc\n      lib_def\n    end\n\n    def parse_lib_body\n      next_token_skip_statement_end\n      Expressions.from(parse_lib_body_expressions)\n    end\n\n    private def parse_lib_body_expressions\n      expressions = [] of ASTNode\n      while true\n        skip_statement_end\n        break if end_token?\n        expressions << parse_lib_body_exp\n      end\n      expressions\n    end\n\n    def parse_lib_body_exp\n      location = @token.location\n      parse_lib_body_exp_without_location.at(location)\n    end\n\n    def parse_lib_body_exp_without_location\n      case @token.type\n      when .op_at_lsquare?\n        parse_annotation\n      when .ident?\n        case @token.value\n        when Keyword::ALIAS\n          parse_alias\n        when Keyword::FUN\n          parse_fun_def(top_level: false)\n        when Keyword::TYPE\n          parse_type_def\n        when Keyword::STRUCT\n          @inside_c_struct = true\n          node = parse_c_struct_or_union union: false\n          @inside_c_struct = false\n          node\n        when Keyword::UNION\n          parse_c_struct_or_union union: true\n        when Keyword::ENUM\n          parse_enum_def\n        else\n          unexpected_token\n        end\n      when .const?\n        ident = parse_path(global: false, location: @token.location)\n        skip_space\n        check :OP_EQ\n        next_token_skip_space_or_newline\n        value = parse_expression\n        skip_statement_end\n        Assign.new(ident, value)\n      when .global?\n        doc = @token.doc\n        location = @token.location\n        name = @token.value.to_s[1..-1]\n        next_token_skip_space_or_newline\n        if @token.type.op_eq?\n          next_token_skip_space\n          check IdentOrConst\n          real_name = @token.value.to_s\n          next_token_skip_space\n        end\n        check :OP_COLON\n        next_token_skip_space_or_newline\n        type = parse_bare_proc_type\n\n        if name[0].ascii_uppercase?\n          raise \"external variables must start with lowercase, use for example `$#{name.underscore} = #{name} : #{type}`\", location\n        end\n\n        skip_statement_end\n        ExternalVar.new(name, type, real_name)\n          .at(location).tap(&.doc=(doc))\n      when .op_lcurly_lcurly?\n        parse_percent_macro_expression\n      when .op_lcurly_percent?\n        parse_percent_macro_control\n      else\n        unexpected_token\n      end\n    end\n\n    IdentOrConst = [:IDENT, :CONST] of Token::Kind\n\n    def parse_fun_def(top_level, require_body = false)\n      location = @token.location\n      doc = @token.doc\n\n      with_isolated_var_scope(require_body) do\n        next_token_skip_space_or_newline\n\n        name_location = @token.location\n        name = if top_level\n                 check_ident\n               else\n                 check IdentOrConst\n                 @token.value.to_s\n               end\n\n        next_token_skip_space_or_newline\n\n        if @token.type.op_eq?\n          next_token_skip_space_or_newline\n          case @token.type\n          when .ident?, .const?\n            real_name = @token.value.to_s\n            next_token_skip_space_or_newline\n          when .delimiter_start?\n            real_name = parse_string_without_interpolation(\"fun name\")\n            skip_space\n          else\n            unexpected_token\n          end\n        else\n          real_name = name\n        end\n\n        params = [] of Arg\n        varargs = false\n\n        if @token.type.op_lparen?\n          next_token_skip_space_or_newline\n          while !@token.type.op_rparen?\n            if @token.type.op_period_period_period?\n              varargs = true\n              next_token_skip_space_or_newline\n              check :OP_RPAREN\n              break\n            end\n\n            if @token.type.ident?\n              param_name = @token.value.to_s\n              param_location = @token.location\n\n              next_token_skip_space_or_newline\n              check :OP_COLON\n              next_token_skip_space_or_newline\n              param_type = parse_bare_proc_type\n              skip_space_or_newline\n\n              params.each do |param|\n                if param.name == param_name\n                  raise \"duplicated fun parameter name: #{param_name}\", param_location\n                end\n              end\n\n              params << Arg.new(param_name, nil, param_type).at(param_location)\n\n              push_var_name param_name if require_body\n            else\n              if top_level\n                raise \"top-level fun parameter must have a name\", @token\n              end\n              param_type = parse_union_type\n              params << Arg.new(\"\", nil, param_type).at(param_type)\n            end\n\n            if @token.type.op_comma?\n              next_token_skip_space_or_newline\n            else\n              skip_space_or_newline\n              check :OP_RPAREN\n              break\n            end\n          end\n          end_location = token_end_location\n          next_token_skip_statement_end\n        end\n\n        if @token.type.op_colon?\n          next_token_skip_space_or_newline\n          end_location = token_end_location\n          return_type = parse_bare_proc_type\n        end\n\n        skip_statement_end\n\n        if require_body\n          @fun_nest += 1\n\n          if @token.keyword?(:end)\n            body = Nop.new\n            end_location = token_end_location\n            next_token\n          else\n            body = parse_expressions\n            body, end_location = parse_exception_handler body, implicit: true\n          end\n\n          @fun_nest -= 1\n        else\n          body = nil\n        end\n\n        fun_def = FunDef.new name, params, return_type, varargs, body, real_name\n        fun_def.name_location = name_location\n        fun_def.doc = doc\n        fun_def.at(location).at_end(end_location)\n      end\n    end\n\n    def parse_alias\n      doc = @token.doc\n\n      next_token_skip_space_or_newline\n\n      name = parse_path\n\n      skip_space\n      check :OP_EQ\n      next_token_skip_space_or_newline\n\n      value = parse_bare_proc_type\n      end_location = value.end_location\n      skip_space\n\n      alias_node = Alias.new(name, value).at_end(end_location)\n      alias_node.doc = doc\n      alias_node\n    end\n\n    def parse_pointerof\n      next_token_skip_space\n\n      check :OP_LPAREN\n      next_token_skip_space_or_newline\n\n      if @token.keyword?(:self)\n        raise \"can't take address of self\", @token.line_number, @token.column_number\n      end\n\n      exp = parse_op_assign\n      skip_space_or_newline\n\n      end_location = token_end_location\n      check :OP_RPAREN\n      next_token_skip_space\n\n      PointerOf.new(exp).at_end(end_location)\n    end\n\n    def parse_sizeof\n      parse_sizeof SizeOf\n    end\n\n    def parse_instance_sizeof\n      parse_sizeof InstanceSizeOf\n    end\n\n    def parse_alignof\n      parse_sizeof AlignOf\n    end\n\n    def parse_instance_alignof\n      parse_sizeof InstanceAlignOf\n    end\n\n    def parse_sizeof(klass)\n      sizeof_location = @token.location\n      next_token_skip_space\n\n      check :OP_LPAREN\n      next_token_skip_space_or_newline\n\n      location = @token.location\n      exp = parse_bare_proc_type.at(location)\n\n      skip_space_or_newline\n\n      end_location = token_end_location\n      check :OP_RPAREN\n      next_token_skip_space\n\n      klass.new(exp).at(sizeof_location).at_end(end_location)\n    end\n\n    def parse_offsetof\n      offsetof_location = @token.location\n      next_token_skip_space\n      check :OP_LPAREN\n\n      next_token_skip_space_or_newline\n      type_location = @token.location\n      type = parse_bare_proc_type.at(type_location)\n\n      skip_space\n      check :OP_COMMA\n\n      next_token_skip_space_or_newline\n      offset = case @token.type\n               when .instance_var?\n                 InstanceVar.new(@token.value.to_s)\n               when .number?\n                 raise \"expecting an integer offset, not '#{@token}'\", @token if !@token.number_kind.i32?\n                 NumberLiteral.new(@token.value.to_s, @token.number_kind)\n               else\n                 raise \"expecting an instance variable or a integer offset, not '#{@token}'\", @token\n               end\n      offset.at(@token.location)\n\n      next_token_skip_space_or_newline\n\n      end_location = token_end_location\n      check :OP_RPAREN\n      next_token_skip_space\n\n      OffsetOf.new(type, offset).at(offsetof_location).at_end(end_location)\n    end\n\n    def parse_type_def\n      doc = @token.doc\n      next_token_skip_space_or_newline\n      name = check_const\n      name_location = @token.location\n      next_token_skip_space_or_newline\n      check :OP_EQ\n      next_token_skip_space_or_newline\n\n      type = parse_bare_proc_type\n      skip_space\n\n      typedef = TypeDef.new name, type\n      typedef.name_location = name_location\n      typedef.doc = doc\n\n      typedef\n    end\n\n    def parse_c_struct_or_union(union : Bool)\n      doc = @token.doc\n      location = @token.location\n      next_token_skip_space_or_newline\n      name = check_const\n      next_token\n      check StatementEnd\n      skip_statement_end\n      body = parse_c_struct_or_union_body_expressions\n      check_ident :end\n      end_location = token_end_location\n      next_token_skip_space\n\n      CStructOrUnionDef.new(name, Expressions.from(body), union: union)\n        .at(location).at_end(end_location)\n        .tap(&.doc=(doc))\n    end\n\n    def parse_c_struct_or_union_body\n      next_token_skip_statement_end\n      Expressions.from(parse_c_struct_or_union_body_expressions)\n    end\n\n    private def parse_c_struct_or_union_body_expressions\n      exps = [] of ASTNode\n\n      while true\n        case @token.type\n        when .ident?\n          case @token.value\n          when Keyword::INCLUDE\n            if @inside_c_struct\n              location = @token.location\n              exps << parse_include.at(location)\n            else\n              parse_c_struct_or_union_fields exps\n            end\n          when Keyword::END\n            break\n          else\n            parse_c_struct_or_union_fields exps\n          end\n        when .op_lcurly_lcurly?\n          exps << parse_percent_macro_expression\n        when .op_lcurly_percent?\n          exps << parse_percent_macro_control\n        when .newline?, .op_semicolon?\n          skip_statement_end\n        else\n          break\n        end\n      end\n\n      exps\n    end\n\n    def parse_c_struct_or_union_fields(exps)\n      doc = @token.doc\n      vars = [Var.new(@token.value.to_s).at(@token.location).at_end(token_end_location)]\n\n      next_token_skip_space_or_newline\n\n      while @token.type.op_comma?\n        next_token_skip_space_or_newline\n        vars << Var.new(check_ident).at(@token.location).at_end(token_end_location)\n        next_token_skip_space_or_newline\n      end\n\n      check :OP_COLON\n      next_token_skip_space_or_newline\n\n      type = parse_bare_proc_type\n\n      skip_statement_end\n\n      vars.each do |var|\n        var.doc = doc\n        exps << TypeDeclaration.new(var, type).at(var).at_end(type)\n      end\n    end\n\n    def parse_enum_def\n      location = @token.location\n      doc = @token.doc\n\n      next_token_skip_space_or_newline\n\n      name = parse_path\n      skip_space\n\n      if @token.type.op_colon?\n        next_token_skip_space_or_newline\n        base_type = parse_path\n        skip_space\n      end\n\n      check SemicolonOrNewLine\n      skip_statement_end\n\n      members = parse_enum_body_expressions\n\n      check_ident :end\n      end_location = token_end_location\n      next_token_skip_space\n\n      enum_def = EnumDef.new name, members, base_type\n      enum_def.doc = doc\n      enum_def.at(location).at_end(end_location)\n    end\n\n    def parse_enum_body\n      next_token_skip_statement_end\n      Expressions.from(parse_enum_body_expressions)\n    end\n\n    private def parse_enum_body_expressions\n      members = [] of ASTNode\n      until end_token?\n        location = @token.location\n        case @token.type\n        when .const?\n          constant_name = @token.value.to_s\n          member_doc = @token.doc\n\n          next_token_skip_space\n          if @token.type.op_eq?\n            next_token_skip_space_or_newline\n            constant_value = parse_logical_or\n          else\n            constant_value = nil\n          end\n\n          skip_space\n\n          case @token.type\n          when .newline?, .op_semicolon?, .eof?\n            next_token_skip_statement_end\n          else\n            unless @token.keyword?(:end)\n              raise \"expecting ';', 'end' or newline after enum member\", location\n            end\n          end\n\n          arg = Arg.new(constant_name, constant_value).at(location).at_end(constant_value || location)\n          arg.doc = member_doc\n\n          members << arg\n        when .ident?\n          visibility = nil\n\n          case @token.value\n          when Keyword::PRIVATE\n            visibility = Visibility::Private\n            next_token_skip_space\n          when Keyword::PROTECTED\n            visibility = Visibility::Protected\n            next_token_skip_space\n          else\n            # not a visibility modifier\n          end\n\n          def_location = @token.location\n\n          case @token.value\n          when Keyword::DEF\n            member = parse_def.at(def_location)\n            member = VisibilityModifier.new(visibility, member).at(location) if visibility\n            members << member\n          when Keyword::MACRO\n            member = parse_macro.at(def_location)\n            member = VisibilityModifier.new(visibility, member).at(location) if visibility\n            members << member\n          else\n            unexpected_token\n          end\n        when .class_var?\n          class_var = ClassVar.new(@token.value.to_s).at(location)\n\n          next_token_skip_space\n          check :OP_EQ\n          next_token_skip_space_or_newline\n          value = parse_op_assign\n\n          members << Assign.new(class_var, value).at(class_var)\n        when .op_lcurly_lcurly?\n          members << parse_percent_macro_expression\n        when .op_lcurly_percent?\n          members << parse_percent_macro_control.at(location)\n        when .op_at_lsquare?\n          members << parse_annotation\n        when .newline?, .op_semicolon?\n          skip_statement_end\n        else\n          unexpected_token\n        end\n      end\n      members\n    end\n\n    def node_and_next_token(node)\n      node.end_location = token_end_location\n      next_token\n      node\n    end\n\n    def end_token?\n      case @token.type\n      when .op_rcurly?, .op_rsquare?, .op_percent_rcurly?, .eof?\n        return true\n      end\n\n      if keyword = @token.value.as?(Keyword)\n        case keyword\n        when .do?, .end?, .else?, .elsif?, .when?, Keyword::IN, .rescue?, .ensure?, .then?\n          !next_comes_colon_space?\n        else\n          false\n        end\n      else\n        false\n      end\n    end\n\n    def can_be_assigned?(node)\n      case node\n      when Var, InstanceVar, ClassVar, Path, Global, Underscore\n        true\n      when Call\n        return false if node.has_parentheses?\n        no_args = node.args.empty? && node.named_args.nil? && node.block.nil?\n        return true if Lexer.ident?(node.name) && no_args\n        node.name == \"[]\" && (node.args_in_brackets? || no_args)\n      else\n        false\n      end\n    end\n\n    # IDENT CONST ` << < <= == === != =~ !~ >> > >= + - * / // ! ~ % & | ^ ** [] []? []= <=> &+ &- &* &**\n    DefOrMacroCheck1 = [\n      :IDENT, :CONST, :OP_GRAVE,\n      :OP_LT_LT, :OP_LT, :OP_LT_EQ, :OP_EQ_EQ, :OP_EQ_EQ_EQ, :OP_BANG_EQ, :OP_EQ_TILDE,\n      :OP_BANG_TILDE, :OP_GT_GT, :OP_GT, :OP_GT_EQ, :OP_PLUS, :OP_MINUS, :OP_STAR, :OP_SLASH,\n      :OP_SLASH_SLASH, :OP_BANG, :OP_TILDE, :OP_PERCENT, :OP_AMP, :OP_BAR, :OP_CARET, :OP_STAR_STAR,\n      :OP_LSQUARE_RSQUARE, :OP_LSQUARE_RSQUARE_EQ, :OP_LSQUARE_RSQUARE_QUESTION, :OP_LT_EQ_GT,\n      :OP_AMP_PLUS, :OP_AMP_MINUS, :OP_AMP_STAR, :OP_AMP_STAR_STAR,\n    ] of Token::Kind\n\n    def consume_def_or_macro_name\n      # Force lexer return if possible a def or macro name\n      # cases like: def `, def /, def //\n      # that in regular statements states for delimiters\n      # here must be treated as method names.\n      wants_def_or_macro_name do\n        next_token_skip_space_or_newline\n        check DefOrMacroCheck1\n      end\n      @token.to_s\n    end\n\n    def consume_def_equals_sign_skip_space\n      result = consume_def_equals_sign\n      skip_space\n      result\n    end\n\n    def consume_def_equals_sign\n      end_location = token_end_location\n      next_token\n      if @token.type.op_eq?\n        end_location = token_end_location\n        next_token\n        {true, end_location}\n      else\n        {false, end_location}\n      end\n    end\n\n    # If *create_scope* is true, creates an isolated variable scope and returns\n    # the yield result, resetting the scope afterwards. Otherwise simply returns\n    # the yield result without touching the scopes.\n    def with_isolated_var_scope(create_scope = true, &)\n      return yield unless create_scope\n\n      begin\n        @var_scopes.push(Set(String).new)\n        yield\n      ensure\n        @var_scopes.pop\n      end\n    end\n\n    # Creates a new variable scope with the same variables as the current scope,\n    # and then returns the yield result, resetting the scope afterwards.\n    def with_lexical_var_scope(&)\n      current_scope = @var_scopes.last.dup\n      @var_scopes.push current_scope\n      yield\n    ensure\n      @var_scopes.pop\n    end\n\n    def push_vars(vars)\n      vars.each do |var|\n        push_var var\n      end\n    end\n\n    def push_var(var : Var | Arg)\n      push_var_name var.name.to_s\n    end\n\n    def push_var(var : TypeDeclaration)\n      var_var = var.var\n      case var_var\n      when Var\n        push_var_name var_var.name\n      when InstanceVar\n        push_var_name var_var.name\n      else\n        raise \"can't happen\"\n      end\n    end\n\n    def push_var_name(name)\n      @var_scopes.last.add name\n    end\n\n    def push_var(node)\n      # Nothing\n    end\n\n    def var_in_scope?(name)\n      @var_scopes.last.includes? name\n    end\n\n    def open(symbol, location = @token.location, &)\n      @unclosed_stack.push Unclosed.new(symbol, location)\n      begin\n        value = yield\n      ensure\n        @unclosed_stack.pop\n      end\n      value\n    end\n\n    def check_void_value(exp, location)\n      if exp.is_a?(ControlExpression)\n        raise \"void value expression\", location\n      end\n    end\n\n    def check_void_expression_keyword\n      case @token.value\n      when Keyword::BREAK, Keyword::NEXT, Keyword::RETURN\n        unless next_comes_colon_space?\n          raise \"void value expression\", @token, @token.value.to_s.size\n        end\n      end\n    end\n\n    def check(token_types : Array(Token::Kind))\n      raise \"expecting any of these tokens: #{token_types.join \", \"} (not '#{@token}')\", @token unless token_types.any? { |type| @token.type == type }\n    end\n\n    def check(token_type : Token::Kind)\n      raise \"expecting token '#{token_type}', not '#{@token}'\", @token unless token_type == @token.type\n    end\n\n    def check_ident(value : Keyword)\n      raise \"expecting identifier '#{value}', not '#{@token}'\", @token unless @token.keyword?(value)\n    end\n\n    def check_ident\n      check :IDENT\n      @token.value.to_s\n    end\n\n    def check_const\n      check :CONST\n      @token.value.to_s\n    end\n\n    def unexpected_token(msg : String? = nil, token : Token = @token)\n      token_str = token.type.eof? ? \"EOF\" : token.to_s.inspect\n      if msg\n        raise \"unexpected token: #{token_str} (#{msg})\", @token\n      else\n        raise \"unexpected token: #{token_str}\", @token\n      end\n    end\n\n    def unexpected_token_in_atomic\n      if unclosed = @unclosed_stack.last?\n        raise \"unterminated #{unclosed.name}\", unclosed.location\n      end\n\n      unexpected_token\n    end\n\n    def var?(name)\n      return true if @in_macro_expression\n\n      name = name.to_s\n      name == \"self\" || var_in_scope?(name)\n    end\n\n    def push_visibility(&)\n      old_visibility = @visibility\n      @visibility = nil\n      value = yield\n      @visibility = old_visibility\n      value\n    end\n\n    def next_token\n      token = super\n\n      if token.type.newline? && !@consuming_heredocs && !@heredocs.empty?\n        consume_heredocs\n      end\n\n      token\n    end\n\n    def temp_arg_name\n      arg_name = \"__arg#{@temp_arg_count}\"\n      @temp_arg_count += 1\n      arg_name\n    end\n  end\n\n  class StringInterpolation\n    include Lexer::HeredocItem\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/to_s.cr",
    "content": "require \"./ast\"\nrequire \"./visitor\"\n\nmodule Crystal\n  class ASTNode\n    def inspect(io : IO) : Nil\n      to_s(io)\n    end\n\n    def to_s(io : IO, macro_expansion_pragmas = nil, emit_doc = false, emit_location_pragmas : Bool = false) : Nil\n      visitor = ToSVisitor.new(io, macro_expansion_pragmas: macro_expansion_pragmas, emit_doc: emit_doc, emit_location_pragmas: emit_location_pragmas)\n      self.accept visitor\n    end\n  end\n\n  class ToSVisitor < Visitor\n    @str : IO\n    @macro_expansion_pragmas : Hash(Int32, Array(Lexer::LocPragma))?\n    @current_arg_type : DefArgType = :none\n\n    # Represents the root level `Expressions` instance within a `MacroExpression`.\n    @root_level_macro_expressions : Expressions? = nil\n\n    # Inside a comma-separated list of parameters or args, this becomes true and\n    # the outermost pair of parentheses are removed from type restrictions that\n    # are `ProcNotation` nodes, so `foo(x : (T, U -> V), W)` becomes\n    # `foo(x : T, U -> V, W)`. This is used by defs, lib funs, and calls to deal\n    # with the parsing rules for `->`. See #11966 and #14216 for more details.\n    getter? drop_parens_for_proc_notation = false\n\n    private enum DefArgType\n      NONE\n      SPLAT\n      DOUBLE_SPLAT\n      BLOCK_ARG\n    end\n\n    def initialize(@str = IO::Memory.new, @macro_expansion_pragmas = nil, @emit_doc = false, @emit_location_pragmas : Bool = false)\n      @indent = 0\n      @inside_macro = 0\n    end\n\n    def visit_any(node)\n      if @emit_doc && (doc = node.doc) && !doc.empty?\n        doc.each_line(chomp: true) do |line|\n          @str << \"# \"\n          @str << line\n          newline\n          append_indent\n        end\n      end\n\n      if (macro_expansion_pragmas = @macro_expansion_pragmas) && (loc = node.location) && (filename = loc.filename).is_a?(String)\n        pragmas = macro_expansion_pragmas[@str.pos.to_i32] ||= [] of Lexer::LocPragma\n        pragmas << Lexer::LocSetPragma.new(filename, loc.line_number, loc.column_number)\n      end\n\n      true\n    end\n\n    private def write_extra_newlines(first_node_location : Location?, second_node_location : Location?) : Nil\n      if first_node_location && second_node_location\n        # Only write the \"extra\" newlines. I.e. If there are more than one. The first newline is handled directly via the Expressions visitor.\n        ((second_node_location.line_number - 1) - first_node_location.line_number).times do\n          newline\n        end\n      end\n    end\n\n    def visit(node : Nop)\n      false\n    end\n\n    def visit(node : BoolLiteral)\n      @str << (node.value ? \"true\" : \"false\")\n      false\n    end\n\n    def visit(node : NumberLiteral)\n      @str << node.value\n\n      if needs_suffix?(node)\n        @str << '_'\n        @str << node.kind.to_s\n      end\n\n      false\n    end\n\n    def needs_suffix?(node : NumberLiteral)\n      case node.kind\n      when .i32?\n        false\n      when .f64?\n        # If there's no '.' nor 'e', for example in `1_f64`,\n        # we need to include it (#3315)\n        node.value.each_char do |char|\n          return false if char.in?('.', 'e')\n        end\n\n        true\n      else\n        true\n      end\n    end\n\n    def visit(node : CharLiteral)\n      node.value.inspect(@str)\n      false\n    end\n\n    def visit(node : SymbolLiteral)\n      visit_symbol_literal_value node.value\n      false\n    end\n\n    def visit_symbol_literal_value(value : String)\n      @str << ':'\n      if Symbol.needs_quotes?(value)\n        value.inspect(@str)\n      else\n        value.to_s(@str)\n      end\n    end\n\n    def visit(node : StringLiteral)\n      node.value.inspect(@str)\n      false\n    end\n\n    def visit(node : StringInterpolation)\n      @str << '\"'\n      visit_interpolation node, &.inspect_unquoted\n      @str << '\"'\n      false\n    end\n\n    def visit_interpolation(node, &)\n      node.expressions.chunks(&.is_a?(StringLiteral)).each do |(is_string, exps)|\n        if is_string\n          value = exps.join(&.as(StringLiteral).value)\n          @str << yield value\n        else\n          exps.each do |exp|\n            @str << \"\\#{\"\n            exp.accept(self)\n            @str << '}'\n          end\n        end\n      end\n    end\n\n    def visit(node : ArrayLiteral)\n      name = node.name\n      if name\n        name.accept self\n        @str << \" {\"\n      else\n        @str << '['\n      end\n\n      node.elements.join(@str, \", \", &.accept self)\n\n      if name\n        @str << '}'\n      else\n        @str << ']'\n      end\n\n      if of = node.of\n        @str << \" of \"\n        of.accept self\n      end\n      false\n    end\n\n    def visit(node : HashLiteral)\n      if name = node.name\n        name.accept self\n        @str << ' '\n      end\n\n      space = false\n      @str << '{'\n\n      node.entries.each_with_index do |entry, i|\n        @str << \", \" if i > 0\n\n        space = i == 0 && entry.key.is_a?(TupleLiteral) || entry.key.is_a?(NamedTupleLiteral) || entry.key.is_a?(HashLiteral)\n        @str << ' ' if space\n\n        entry.key.accept self\n        @str << \" => \"\n        entry.value.accept self\n      end\n\n      @str << ' ' if space\n      @str << '}'\n      if of = node.of\n        @str << \" of \"\n        of.key.accept self\n        @str << \" => \"\n        of.value.accept self\n      end\n      false\n    end\n\n    def visit(node : NamedTupleLiteral)\n      # short-circuit to handle empty named tuple context\n      if node.entries.empty?\n        @str << \"::NamedTuple.new\"\n        return false\n      end\n\n      @str << '{'\n\n      # A node starts multiline when its starting brace is on a different line than the staring line of it's first entry\n      start_multiline = (start_loc = node.location) && (first_entry_loc = node.entries.first?.try &.value.location) && first_entry_loc.line_number > start_loc.line_number\n\n      # and similarly ends multiline if its last entry's end location is on a different line than its ending brace\n      end_multiline = (last_entry_loc = node.entries.last?.try &.value.end_location) && (end_loc = node.end_location) && end_loc.line_number > last_entry_loc.line_number\n\n      last_entry = node.entries.first\n\n      if start_multiline\n        newline\n        @indent += 1\n        append_indent\n      end\n\n      node.entries.each_with_index do |entry, idx|\n        write_extra_newlines (last_entry.value || entry.value).end_location, entry.value.location\n\n        if (current_entry_loc = entry.value.location) && (last_entry_loc = last_entry.value.location) && current_entry_loc.line_number > last_entry_loc.line_number\n          newline\n\n          # If the node is not starting multiline, explicitly enable it once there is a line break to ensure additional values are indented properly\n          unless start_multiline\n            start_multiline = true\n            @indent += 1\n          end\n\n          append_indent\n        elsif !idx.zero?\n          @str << ' '\n        end\n\n        visit_named_arg_name(entry.key)\n        @str << \": \"\n        entry.value.accept self\n\n        last_entry = entry\n\n        @str << ',' unless idx == node.entries.size - 1\n      end\n\n      @indent -= 1 if start_multiline\n\n      if end_multiline\n        @str << ','\n        newline\n        append_indent\n      end\n\n      @str << '}'\n      false\n    end\n\n    def visit(node : NilLiteral)\n      @str << \"nil\"\n      false\n    end\n\n    def visit(node : Expressions)\n      is_multiline = false\n\n      case node.keyword\n      in .paren?\n        # Handled via dedicated #in_parenthesis call below\n        is_multiline = (loc = node.location) && (first_loc = node.expressions.first?.try &.location) && (first_loc.line_number > loc.line_number)\n        append_indent if is_multiline\n      in .begin?\n        @str << \"begin\"\n        @indent += 1\n        newline\n      in .none?\n        # Not a special condition\n      end\n\n      in_parenthesis node.keyword.paren?, is_multiline do\n        if @inside_macro > 0\n          node.expressions.each &.accept self\n        else\n          last_node = nil\n\n          node.expressions.each_with_index do |exp, i|\n            unless exp.nop?\n              write_extra_newlines (last_node || exp).end_location, exp.location\n\n              append_indent unless node.keyword.paren? && i == 0\n              exp.accept self\n\n              if (root = @root_level_macro_expressions) && root.same?(node) && i == node.expressions.size - 1\n                # Do not add a trailing newline after the last node in the root `Expressions` within a `MacroExpression`.\n                # This is handled by the `MacroExpression` logic.\n              elsif !(node.keyword.paren? && i == node.expressions.size - 1)\n                newline\n              end\n\n              last_node = exp\n            end\n          end\n        end\n      end\n\n      case node.keyword\n      in .paren?\n        # Handled via dedicated #in_parenthesis call above\n      in .begin?\n        @indent -= 1\n        append_indent\n        @str << \"end\"\n      in .none?\n        # Not a special condition\n      end\n\n      false\n    end\n\n    private def emit_loc_pragma(for location : Location?) : Nil\n      if @emit_location_pragmas && (loc = location) && (filename = loc.filename).is_a?(String)\n        @str << %(#<loc:\"#{filename}\",#{loc.line_number},#{loc.column_number}>)\n      end\n    end\n\n    def visit(node : If)\n      if node.ternary?\n        node.cond.accept self\n        @str << \" ? \"\n        node.then.accept self\n        @str << \" : \"\n        node.else.accept self\n        return false\n      end\n\n      self.emit_loc_pragma node.location\n\n      while true\n        @str << \"if \"\n        node.cond.accept self\n        newline\n\n        self.emit_loc_pragma node.then.location\n\n        accept_with_indent(node.then)\n        append_indent\n\n        # combine `else if` into `elsif` (does not apply to `unless` or `? :`)\n        if (else_node = node.else).is_a?(If) && !else_node.ternary?\n          @str << \"els\"\n          node = else_node\n        else\n          break\n        end\n      end\n\n      unless else_node.nop?\n        @str << \"else\"\n        newline\n\n        self.emit_loc_pragma node.else.location\n\n        accept_with_indent(node.else)\n        append_indent\n      end\n\n      self.emit_loc_pragma node.end_location\n\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : Unless)\n      self.emit_loc_pragma node.location\n\n      @str << \"unless \"\n      node.cond.accept self\n      newline\n\n      self.emit_loc_pragma node.then.location\n\n      accept_with_indent(node.then)\n      unless node.else.nop?\n        append_indent\n        @str << \"else\"\n        newline\n\n        self.emit_loc_pragma node.else.location\n\n        accept_with_indent(node.else)\n      end\n      append_indent\n\n      self.emit_loc_pragma node.end_location\n\n      @str << \"end\"\n\n      false\n    end\n\n    def visit(node : ClassDef)\n      if node.abstract?\n        @str << \"abstract \"\n      end\n      @str << (node.struct? ? \"struct\" : \"class\")\n      @str << ' '\n      node.name.accept self\n      if type_vars = node.type_vars\n        @str << '('\n        type_vars.each_with_index do |type_var, i|\n          @str << \", \" if i > 0\n          @str << '*' if node.splat_index == i\n          @str << type_var.to_s\n        end\n        @str << ')'\n      end\n      if superclass = node.superclass\n        @str << \" < \"\n        superclass.accept self\n      end\n      newline\n      accept_with_indent(node.body)\n\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : ModuleDef)\n      @str << \"module \"\n      node.name.accept self\n      if type_vars = node.type_vars\n        @str << '('\n        type_vars.each_with_index do |type_var, i|\n          @str << \", \" if i > 0\n          @str << '*' if node.splat_index == i\n          @str << type_var\n        end\n        @str << ')'\n      end\n      newline\n      accept_with_indent(node.body)\n\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : AnnotationDef)\n      @str << \"annotation \"\n      node.name.accept self\n      newline\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : Call)\n      visit_call node\n    end\n\n    # Related: `Token::Kind#unary_operator?`\n    UNARY_OPERATORS = {\"+\", \"-\", \"~\", \"&+\", \"&-\"}\n\n    def visit_call(node, ignore_obj = false)\n      if node.name == \"`\"\n        visit_backtick(node.args[0])\n        return false\n      end\n\n      node_obj = ignore_obj ? nil : node.obj\n      block = node.block\n\n      short_block_call = nil\n      if block\n        # Check if this is foo &.bar\n        first_block_arg = block.args.first?\n        if first_block_arg && block.args.size == 1 && block.args.first.name.starts_with?(\"__arg\")\n          block_body = block.body\n          if block_body.is_a?(Call)\n            block_obj = block_body.obj\n            if block_obj.is_a?(Var) && block_obj.name == first_block_arg.name\n              short_block_call = block_body\n              block = nil\n            end\n          end\n        end\n      end\n\n      need_parens = need_parens(node_obj)\n      is_multiline = false\n\n      @str << \"::\" if node.global?\n      if node_obj.is_a?(ImplicitObj)\n        @str << '.'\n        node_obj = nil\n      end\n\n      if node_obj && node.name.in?(\"[]\", \"[]?\") && !block\n        in_parenthesis(need_parens, node_obj)\n\n        @str << \"[\"\n        visit_args(node)\n\n        if short_block_call\n          @str << \", \" if node.args.present? || node.named_args\n          @str << \"&.\"\n          visit_call short_block_call, ignore_obj: true\n        end\n\n        if node.name == \"[]\"\n          @str << \"]\"\n        else\n          @str << \"]?\"\n        end\n      elsif node_obj && node.name == \"[]=\" && !node.args.empty? && !block\n        in_parenthesis(need_parens, node_obj)\n\n        @str << \"[\"\n        visit_args(node, exclude_last: true)\n\n        if short_block_call\n          @str << \", \" if node.args.size > 1 || node.named_args\n          @str << \"&.\"\n          visit_call short_block_call, ignore_obj: true\n        end\n\n        @str << \"] = \"\n        node.args.last.accept self\n      elsif node_obj && node.name.in?(UNARY_OPERATORS) && node.args.empty? && !node.named_args && !node.block_arg && !block && !short_block_call\n        @str << node.name\n        in_parenthesis(need_parens, node_obj)\n      elsif node_obj && !Lexer.ident?(node.name) && node.name != \"~\" && node.args.size == 1 && !node.named_args && !node.block_arg && !block && !short_block_call\n        in_parenthesis(need_parens, node_obj)\n\n        arg = node.args[0]\n        @str << ' '\n        @str << node.name\n        @str << ' '\n        in_parenthesis(need_parens(arg), arg)\n      else\n        if node_obj\n          # A call is multiline if the call's name is on a diff line than the obj it's being called on.\n          is_multiline = (node_obj_end_loc = node_obj.end_location) && (name_loc = node.name_end_location) && (name_loc.line_number > node_obj_end_loc.line_number)\n\n          in_parenthesis(need_parens, node_obj)\n\n          if is_multiline\n            newline\n            @indent += 1\n            append_indent\n          end\n\n          @str << '.'\n        end\n        if Lexer.setter?(node.name)\n          @str << node.name.rchop\n          @str << \" = \"\n          node.args.join(@str, \", \", &.accept self)\n        else\n          @str << node.name\n          in_parenthesis(node.has_parentheses? || !node.args.empty? || node.block_arg || node.named_args || short_block_call) do\n            visit_args(node)\n\n            if short_block_call\n              @str << \", \" if node.args.present? || node.named_args\n              @str << \"&.\"\n              visit_call short_block_call, ignore_obj: true\n            end\n          end\n        end\n      end\n\n      if block\n        @str << ' '\n        block.accept self\n      end\n\n      @indent -= 1 if is_multiline\n\n      false\n    end\n\n    private def visit_args(node, exclude_last = false)\n      printed_arg = false\n      node.args.each_with_index do |arg, i|\n        break if exclude_last && i == node.args.size - 1\n\n        @str << \", \" if printed_arg\n        drop_parens_for_proc_notation(arg, &.accept(self))\n        printed_arg = true\n      end\n      if named_args = node.named_args\n        named_args.each do |named_arg|\n          @str << \", \" if printed_arg\n          drop_parens_for_proc_notation(named_arg, &.accept(self))\n          printed_arg = true\n        end\n      end\n      if block_arg = node.block_arg\n        @str << \", \" if printed_arg\n        @str << '&'\n        drop_parens_for_proc_notation(block_arg, &.accept(self))\n      end\n    end\n\n    private def need_parens(obj)\n      case obj\n      when Call\n        case obj.args.size\n        when 0\n          !Lexer.ident?(obj.name)\n        else\n          case obj.name\n          when \"[]\", \"[]?\", \"<\", \"<=\", \">\", \">=\"\n            false\n          else\n            true\n          end\n        end\n      when Not\n        case exp = obj.exp\n        when Call\n          exp.obj.nil?\n        else\n          !obj.exp.is_a? Call\n        end\n      when Var, NilLiteral, BoolLiteral, CharLiteral, NumberLiteral, StringLiteral,\n           StringInterpolation, Path, Generic, InstanceVar, ClassVar, Global,\n           ImplicitObj, TupleLiteral, NamedTupleLiteral, IsA\n        false\n      when ArrayLiteral\n        !!obj.of\n      when HashLiteral\n        !!obj.of\n      else\n        true\n      end\n    end\n\n    def in_parenthesis(need_parens, is_multiline = false, &)\n      @str << '(' if need_parens\n\n      if is_multiline\n        newline\n        @indent += 1\n        append_indent\n      end\n\n      yield\n\n      if is_multiline\n        newline\n        @indent -= 1\n        append_indent\n      end\n\n      @str << ')' if need_parens\n    end\n\n    def in_parenthesis(need_parens, node, is_multiline = false)\n      in_parenthesis(need_parens, is_multiline) do\n        if node.is_a?(Expressions) && node.expressions.size == 1\n          node.expressions.first.accept self\n        else\n          node.accept self\n        end\n      end\n    end\n\n    def visit(node : NamedArgument)\n      visit_named_arg_name(node.name)\n      @str << \": \"\n      node.value.accept self\n      false\n    end\n\n    def visit_backtick(exp)\n      @str << '`'\n      case exp\n      when StringLiteral\n        @str << exp.value.inspect_unquoted.gsub('`', \"\\\\`\")\n      when StringInterpolation\n        visit_interpolation exp, &.inspect_unquoted.gsub('`', \"\\\\`\")\n      else\n        # This branch can be reached after the literal expander has expanded\n        # `StringLiteral` nodes to a call to `::String.interpolation` which means\n        # `exp` is a `Call`.\n\n        @str << \"\\#{\"\n        exp.accept(self)\n        @str << \"}\"\n      end\n      @str << '`'\n      false\n    end\n\n    def visit(node : Assign)\n      node.target.accept self\n      @str << \" = \"\n\n      need_parens = node.value.is_a?(Expressions)\n      in_parenthesis(need_parens, node.value)\n\n      false\n    end\n\n    def visit(node : OpAssign)\n      node.target.accept self\n      @str << ' ' << node.op << '=' << ' '\n      node.value.accept self\n      false\n    end\n\n    def visit(node : MultiAssign)\n      node.targets.join(@str, \", \", &.accept self)\n      @str << \" = \"\n      node.values.join(@str, \", \", &.accept self)\n      false\n    end\n\n    def visit(node : While)\n      visit_while_or_until node, \"while\"\n    end\n\n    def visit(node : Until)\n      visit_while_or_until node, \"until\"\n    end\n\n    def visit_while_or_until(node, name)\n      @str << name\n      @str << ' '\n      node.cond.accept self\n      newline\n      accept_with_indent(node.body)\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : Out)\n      @str << \"out \"\n      node.exp.accept self\n      false\n    end\n\n    def visit(node : Var)\n      @str << node.name\n      false\n    end\n\n    def visit(node : ProcLiteral)\n      @str << \"->\"\n      if node.def.args.size > 0\n        @str << '('\n        node.def.args.join(@str, \", \", &.accept self)\n        @str << ')'\n      end\n      if return_type = node.def.return_type\n        @str << \" : \"\n        return_type.accept self\n      end\n      @str << \" do\"\n      newline\n      accept_with_indent(node.def.body)\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : ProcPointer)\n      @str << \"->\"\n      @str << \"::\" if node.global?\n      if obj = node.obj\n        obj.accept self\n        @str << '.'\n      end\n      @str << node.name\n\n      if node.args.size > 0\n        @str << '('\n        node.args.join(@str, \", \", &.accept self)\n        @str << ')'\n      end\n      false\n    end\n\n    def visit(node : Def)\n      @str << \"abstract \" if node.abstract?\n      @str << \"def \"\n      if node_receiver = node.receiver\n        node_receiver.accept self\n        @str << '.'\n      end\n      @str << node.name\n      if node.args.size > 0 || node.block_arity || node.double_splat\n        @str << '('\n        printed_arg = false\n        node.args.each_with_index do |arg, i|\n          @str << \", \" if printed_arg\n          @current_arg_type = :splat if node.splat_index == i\n          drop_parens_for_proc_notation(arg, &.accept(self))\n          printed_arg = true\n        end\n        if double_splat = node.double_splat\n          @current_arg_type = :double_splat\n          @str << \", \" if printed_arg\n          drop_parens_for_proc_notation(double_splat, &.accept(self))\n          printed_arg = true\n        end\n        if block_arg = node.block_arg\n          @current_arg_type = :block_arg\n          @str << \", \" if printed_arg\n          drop_parens_for_proc_notation(block_arg, &.accept(self))\n        elsif node.block_arity\n          @str << \", \" if printed_arg\n          @str << '&'\n        end\n        @str << ')'\n      end\n      if return_type = node.return_type\n        @str << \" : \"\n        return_type.accept self\n      end\n\n      if free_vars = node.free_vars\n        @str << \" forall \"\n        free_vars.join(@str, \", \")\n      end\n\n      newline\n\n      unless node.abstract?\n        accept_with_indent(node.body)\n        append_indent\n        @str << \"end\"\n      end\n      false\n    end\n\n    def visit(node : Macro)\n      @str << \"macro \"\n      @str << node.name.to_s\n      if node.args.size > 0 || node.block_arg || node.double_splat\n        @str << '('\n        printed_arg = false\n        # NOTE: `drop_parens_for_proc_notation` needed here if macros support\n        # restrictions\n        node.args.each_with_index do |arg, i|\n          @str << \", \" if printed_arg\n          @current_arg_type = :splat if i == node.splat_index\n          arg.accept self\n          printed_arg = true\n        end\n        if double_splat = node.double_splat\n          @str << \", \" if printed_arg\n          @current_arg_type = :double_splat\n          double_splat.accept self\n          printed_arg = true\n        end\n        if block_arg = node.block_arg\n          @str << \", \" if printed_arg\n          @current_arg_type = :block_arg\n          block_arg.accept self\n        end\n        @str << ')'\n      end\n      newline\n\n      with_indent do\n        inside_macro do\n          accept node.body\n        end\n      end\n\n      # newline\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : MacroExpression)\n      # A node starts multiline when its starting location (`{{` or `{%`) is on a different line than the start of its expression\n      start_multiline = (start_loc = node.location) && (end_loc = node.exp.location) && end_loc.line_number > start_loc.line_number\n\n      # and similarly ends multiline if its expression end location is on a different line than its end location (`}}` or `%}`)\n      end_multiline = (body_end_loc = node.exp.end_location) && (end_loc = node.end_location) && end_loc.line_number > body_end_loc.line_number\n\n      @str << (node.output? ? \"{{ \" : start_multiline ? \"{%\" : \"{% \")\n\n      if start_multiline\n        newline\n        @indent += 1\n      end\n\n      if (exp = node.exp).is_a? Expressions\n        @root_level_macro_expressions = exp\n      end\n\n      outside_macro do\n        write_extra_newlines node.location, node.exp.location\n\n        # If the MacroExpression consists of a single node we need to manually handle appending indent and trailing newline if *start_multiline*\n        # Otherwise, the Expressions logic handles that for us\n        if start_multiline && !node.exp.is_a?(Expressions)\n          append_indent\n        end\n\n        node.exp.accept self\n      end\n\n      write_extra_newlines node.exp.end_location, node.end_location\n\n      # After writing the expression body, de-indent if things were originally multiline.\n      # This ensures the ending control has the proper indent relative to the start.\n      @indent -= 1 if start_multiline\n\n      if end_multiline\n        newline\n        append_indent\n      end\n\n      @str << (node.output? ? \" }}\" : end_multiline ? \"%}\" : \" %}\")\n      false\n    end\n\n    def visit(node : MacroIf)\n      else_node = nil\n\n      while true\n        if node.is_unless?\n          @str << \"{% unless \"\n          then_node = node.else\n          else_node = node.then\n        else\n          @str << (else_node ? \"{% elsif \" : \"{% if \")\n          then_node = node.then\n          else_node = node.else\n        end\n        node.cond.accept self\n        @str << \" %}\"\n\n        inside_macro do\n          then_node.accept self\n        end\n\n        # combine `{% else %}{% if %}` into `{% elsif %}` (does not apply to\n        # `{% unless %}`, nor when there is whitespace inbetween, as that would\n        # show up as a `MacroLiteral`)\n        if !node.is_unless? && else_node.is_a?(MacroIf) && !else_node.is_unless?\n          node = else_node\n        else\n          break\n        end\n      end\n\n      unless else_node.nop?\n        @str << \"{% else %}\"\n        inside_macro do\n          else_node.accept self\n        end\n      end\n\n      @str << \"{% end %}\"\n      false\n    end\n\n    def visit(node : MacroFor)\n      @str << \"{% for \"\n      node.vars.join(@str, \", \", &.accept self)\n      @str << \" in \"\n      node.exp.accept self\n      @str << \" %}\"\n      inside_macro do\n        node.body.accept self\n      end\n      @str << \"{% end %}\"\n      false\n    end\n\n    def visit(node : MacroVar)\n      @str << '%'\n      @str << node.name\n      if exps = node.exps\n        @str << '{'\n        exps.join(@str, \", \", &.accept self)\n        @str << '}'\n      end\n      false\n    end\n\n    def visit(node : MacroLiteral)\n      # These two can only come from an escaped sequence like \\{ or \\{%\n      if node.value == \"{\" || node.value.starts_with?(\"{%\")\n        @str << \"\\\\\"\n      end\n      @str << node.value\n      false\n    end\n\n    def visit(node : MacroVerbatim)\n      @str << \"{% verbatim do %}\"\n\n      with_indent do\n        inside_macro do\n          node.exp.accept self\n        end\n      end\n\n      @str << \"{% end %}\"\n      false\n    end\n\n    def visit(node : ExternalVar)\n      @str << '$'\n      @str << node.name\n      if real_name = node.real_name\n        @str << \" = \"\n        @str << real_name\n      end\n      @str << \" : \"\n      node.type_spec.accept self\n      false\n    end\n\n    def visit(node : Arg)\n      if parsed_annotations = node.parsed_annotations\n        parsed_annotations.each do |ann|\n          ann.accept self\n          @str << ' '\n        end\n      end\n\n      case @current_arg_type\n      when .splat?        then @str << '*'\n      when .double_splat? then @str << \"**\"\n      when .block_arg?    then @str << '&'\n      end\n\n      if node.external_name != node.name\n        visit_named_arg_name(node.external_name)\n        @str << ' '\n      end\n      if node.name\n        @str << node.name\n      else\n        @str << '?'\n      end\n      if restriction = node.restriction\n        @str << \" : \"\n        restriction.accept self\n      end\n      if default_value = node.default_value\n        @str << \" = \"\n        default_value.accept self\n      end\n      false\n    ensure\n      @current_arg_type = :none\n    end\n\n    def visit(node : ProcNotation)\n      @str << '(' unless drop_parens_for_proc_notation?\n\n      # only drop the outermost pair of parentheses; this produces\n      # `foo(x : (T -> U) -> V, W)`, not\n      # `foo(x : ((T -> U) -> V), W)` nor `foo(x : T -> U -> V, W)`\n      drop_parens_for_proc_notation(false) do\n        if inputs = node.inputs\n          inputs.join(@str, \", \", &.accept self)\n          @str << ' '\n        end\n        @str << \"->\"\n        if output = node.output\n          @str << ' '\n          output.accept self\n        end\n      end\n\n      @str << ')' unless drop_parens_for_proc_notation?\n      false\n    end\n\n    def visit(node : Self)\n      @str << \"self\"\n      false\n    end\n\n    def visit(node : Path)\n      @str << \"::\" if node.global?\n      node.names.join(@str, \"::\")\n      false\n    end\n\n    def visit(node : Generic)\n      node.name.accept self\n\n      printed_arg = false\n\n      @str << '('\n      node.type_vars.join(@str, \", \") do |var|\n        var.accept self\n        printed_arg = true\n      end\n\n      if named_args = node.named_args\n        named_args.each do |named_arg|\n          @str << \", \" if printed_arg\n          visit_named_arg_name(named_arg.name)\n          @str << \": \"\n          named_arg.value.accept self\n          printed_arg = true\n        end\n      end\n\n      @str << ')'\n      false\n    end\n\n    def visit_named_arg_name(name)\n      Symbol.quote_for_named_argument(@str, name)\n    end\n\n    def visit(node : Underscore)\n      @str << '_'\n      false\n    end\n\n    def visit(node : Splat)\n      @str << '*'\n      node.exp.accept self\n      false\n    end\n\n    def visit(node : DoubleSplat)\n      @str << \"**\"\n      node.exp.accept self\n      false\n    end\n\n    def visit(node : Union)\n      node.types.join(@str, \" | \", &.accept self)\n      false\n    end\n\n    def visit(node : Metaclass)\n      needs_parens = node.name.is_a?(Union)\n      @str << '(' if needs_parens\n      node.name.accept self\n      @str << ')' if needs_parens\n      @str << \".class\"\n      false\n    end\n\n    def visit(node : InstanceVar)\n      @str << node.name\n      false\n    end\n\n    def visit(node : ReadInstanceVar)\n      node.obj.accept self\n      @str << '.'\n      @str << node.name\n      false\n    end\n\n    def visit(node : ClassVar)\n      @str << node.name\n      false\n    end\n\n    def visit(node : Yield)\n      if scope = node.scope\n        @str << \"with \"\n        scope.accept self\n        @str << ' '\n      end\n      @str << \"yield\"\n      in_parenthesis(node.has_parentheses?) do\n        if node.exps.size > 0\n          @str << ' ' unless node.has_parentheses?\n          node.exps.join(@str, \", \", &.accept self)\n        end\n      end\n      false\n    end\n\n    def visit(node : Return)\n      visit_control node, \"return\"\n    end\n\n    def visit(node : Break)\n      visit_control node, \"break\"\n    end\n\n    def visit(node : Next)\n      visit_control node, \"next\"\n    end\n\n    def visit_control(node, keyword)\n      @str << keyword\n      if exp = node.exp\n        @str << ' '\n        exp.accept self\n      end\n      false\n    end\n\n    def visit(node : RegexLiteral)\n      if (exp = node.value).is_a?(StringLiteral) && exp.value.empty?\n        # // is not always an empty regex, sometimes is an operator\n        # so it's safer to emit empty regex as %r()\n        @str << \"%r()\"\n      else\n        @str << '/'\n        case exp = node.value\n        when StringLiteral\n          @str << '\\\\' if exp.value[0]?.try &.ascii_whitespace?\n          Regex.append_source exp.value, @str\n        when StringInterpolation\n          @str << '\\\\' if exp.expressions.first?.as?(StringLiteral).try &.value[0]?.try &.ascii_whitespace?\n          visit_interpolation(exp) { |s| Regex.append_source s, @str }\n        else\n          raise \"Bug: shouldn't happen\"\n        end\n        @str << '/'\n      end\n      @str << 'i' if node.options.ignore_case?\n      @str << 'm' if node.options.multiline?\n      @str << 'x' if node.options.extended?\n      false\n    end\n\n    def visit(node : TupleLiteral)\n      first = node.elements.first?\n      unless first\n        @str << \"::Tuple.new\"\n        return false\n      end\n\n      @str << '{'\n\n      space = first.is_a?(TupleLiteral) || first.is_a?(NamedTupleLiteral) || first.is_a?(HashLiteral)\n      @str << ' ' if space\n      node.elements.join(@str, \", \", &.accept self)\n      @str << ' ' if space\n      @str << '}'\n      false\n    end\n\n    def visit(node : TypeDeclaration)\n      node.var.accept self\n      @str << \" : \"\n      node.declared_type.accept self\n      if value = node.value\n        @str << \" = \"\n        value.accept self\n      end\n      false\n    end\n\n    def visit(node : UninitializedVar)\n      node.var.accept self\n      @str << \" = uninitialized \"\n      node.declared_type.accept self\n      false\n    end\n\n    def visit(node : Block)\n      # If the node's body end location is on the same line as the start of the block itself, it's on a single line.\n      single_line_block = (node_loc = node.location) && (end_loc = node.body.end_location) && end_loc.line_number == node_loc.line_number\n\n      @str << \"do\"\n\n      if node.has_any_args?\n        @str << \" |\"\n        node.args.each_with_index do |arg, i|\n          @str << \", \" if i > 0\n          @str << '*' if i == node.splat_index\n          if arg.name == \"\"\n            # This is an unpack\n            unpack = node.unpacks.not_nil![i]\n            visit_unpack(unpack)\n          else\n            arg.accept self\n          end\n        end\n        @str << '|'\n      end\n\n      write_extra_newlines node.location, node.body.location\n\n      if single_line_block\n        @str << ' '\n        node.body.accept self\n      else\n        newline\n        accept_with_indent node.body\n      end\n\n      if single_line_block\n        @str << ' '\n      else\n        append_indent\n      end\n\n      @str << \"end\"\n\n      false\n    end\n\n    def visit_unpack(node)\n      case node\n      when Expressions\n        @str << \"(\"\n        node.expressions.join(@str, \", \") do |exp|\n          visit_unpack exp\n        end\n        @str << \")\"\n      else\n        node.accept self\n      end\n    end\n\n    def visit(node : Include)\n      @str << \"include \"\n      node.name.accept self\n      false\n    end\n\n    def visit(node : Extend)\n      @str << \"extend \"\n      node.name.accept self\n      false\n    end\n\n    def visit(node : And)\n      to_s_binary node, \"&&\"\n    end\n\n    def visit(node : Or)\n      to_s_binary node, \"||\"\n    end\n\n    def visit(node : Not)\n      @str << '.' if node.exp.is_a?(ImplicitObj)\n      @str << '!'\n      need_parens = need_parens(node.exp)\n      in_parenthesis(need_parens, node.exp)\n      false\n    end\n\n    def visit(node : VisibilityModifier)\n      @str << node.modifier.to_s.downcase\n      @str << ' '\n      node.exp.accept self\n      false\n    end\n\n    def to_s_binary(node, op)\n      left_needs_parens = need_parens(node.left)\n      left_parens_multiline = left_needs_parens && (begin_loc = node.left.location) && (end_loc = node.left.end_location) && (end_loc.line_number > begin_loc.line_number)\n      in_parenthesis(left_needs_parens, node.left, left_parens_multiline)\n\n      @str << ' '\n      @str << op\n\n      if (right_loc = node.right.location) && (left_end_loc = node.left.end_location) && (right_loc.line_number > left_end_loc.line_number)\n        newline\n        append_indent\n      else\n        @str << ' '\n      end\n\n      right_needs_parens = need_parens(node.right)\n      right_parens_multiline = right_needs_parens && (begin_loc = node.right.location) && (end_loc = node.right.end_location) && (end_loc.line_number > begin_loc.line_number)\n      in_parenthesis(right_needs_parens, node.right, right_parens_multiline)\n\n      false\n    end\n\n    def visit(node : Global)\n      @str << node.name\n      false\n    end\n\n    def visit(node : LibDef)\n      @str << \"lib \"\n      node.name.accept self\n      newline\n      accept_with_indent(node.body)\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : FunDef)\n      @str << \"fun \"\n      if node.name == node.real_name\n        @str << node.name\n      else\n        @str << node.name\n        @str << \" = \"\n        Symbol.quote_for_named_argument(@str, node.real_name)\n      end\n      if node.args.size > 0\n        @str << '('\n        node.args.join(@str, \", \") do |arg|\n          if arg_name = arg.name.presence\n            @str << arg_name << \" : \"\n          end\n          drop_parens_for_proc_notation(arg) do\n            arg.restriction.not_nil!.accept self\n          end\n        end\n        if node.varargs?\n          @str << \", ...\"\n        end\n        @str << ')'\n      elsif node.varargs?\n        @str << \"(...)\"\n      end\n      if node_return_type = node.return_type\n        @str << \" : \"\n        node_return_type.accept self\n      end\n      if body = node.body\n        newline\n        accept_with_indent body\n        append_indent\n        @str << \"end\"\n      end\n      false\n    end\n\n    def visit(node : TypeDef)\n      @str << \"type \"\n      @str << node.name.to_s\n      @str << \" = \"\n      node.type_spec.accept self\n      false\n    end\n\n    def visit(node : CStructOrUnionDef)\n      @str << (node.union? ? \"union\" : \"struct\")\n      @str << ' '\n      @str << node.name.to_s\n      newline\n      accept_with_indent node.body\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : EnumDef)\n      @str << \"enum \"\n      @str << node.name.to_s\n      if base_type = node.base_type\n        @str << \" : \"\n        base_type.accept self\n      end\n      newline\n      with_indent do\n        node.members.each do |member|\n          append_indent\n          member.accept self\n          newline\n        end\n      end\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : RangeLiteral)\n      unless node.from.nop?\n        need_parens = need_parens(node.from)\n        in_parenthesis(need_parens, node.from)\n      end\n\n      if node.exclusive?\n        @str << \"...\"\n      else\n        @str << \"..\"\n      end\n\n      unless node.to.nop?\n        need_parens = need_parens(node.to)\n        in_parenthesis(need_parens, node.to)\n      end\n\n      false\n    end\n\n    def visit(node : PointerOf)\n      @str << \"pointerof(\"\n      node.exp.accept(self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : SizeOf)\n      @str << \"sizeof(\"\n      node.exp.accept(self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : InstanceSizeOf)\n      @str << \"instance_sizeof(\"\n      node.exp.accept(self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : AlignOf)\n      @str << \"alignof(\"\n      node.exp.accept(self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : InstanceAlignOf)\n      @str << \"instance_alignof(\"\n      node.exp.accept(self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : OffsetOf)\n      @str << \"offsetof(\"\n      node.offsetof_type.accept(self)\n      @str << \", \"\n      node.offset.accept(self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : IsA)\n      node.obj.accept self\n      if node.nil_check?\n        @str << \".nil?\"\n      else\n        @str << \".is_a?(\"\n        node.const.accept self\n        @str << ')'\n      end\n      false\n    end\n\n    def visit(node : Cast)\n      visit_cast node, \"as\"\n    end\n\n    def visit(node : NilableCast)\n      visit_cast node, \"as?\"\n    end\n\n    def visit_cast(node, keyword)\n      need_parens = need_parens(node.obj)\n      in_parenthesis(need_parens, node.obj)\n      @str << '.'\n      @str << keyword\n      @str << '('\n      node.to.accept self\n      @str << ')'\n      false\n    end\n\n    def visit(node : RespondsTo)\n      node.obj.accept self\n      @str << \".responds_to?(\"\n      visit_symbol_literal_value node.name\n      @str << ')'\n      false\n    end\n\n    def visit(node : Require)\n      @str << \"require \\\"\"\n      @str << node.string\n      @str << '\"'\n      false\n    end\n\n    def visit(node : Case)\n      @str << \"case\"\n      if cond = node.cond\n        @str << ' '\n        cond.accept self\n      end\n      newline\n\n      node.whens.each do |wh|\n        wh.accept self\n      end\n\n      if node_else = node.else\n        append_indent\n        @str << \"else\"\n        newline\n        accept_with_indent node_else\n      end\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : When)\n      append_indent\n      @str << (node.exhaustive? ? \"in\" : \"when\")\n      @str << ' '\n      node.conds.join(@str, \", \", &.accept self)\n      newline\n      accept_with_indent node.body\n      false\n    end\n\n    def visit(node : Select)\n      @str << \"select\"\n      newline\n      node.whens.each do |a_when|\n        append_indent\n        @str << \"when \"\n        a_when.conds.first.accept self\n        newline\n        accept_with_indent a_when.body\n      end\n      if a_else = node.else\n        append_indent\n        @str << \"else\"\n        newline\n        accept_with_indent a_else\n      end\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : ImplicitObj)\n      false\n    end\n\n    def visit(node : ExceptionHandler)\n      @str << \"begin\"\n      newline\n\n      accept_with_indent node.body\n\n      node.rescues.try &.each do |a_rescue|\n        append_indent\n        a_rescue.accept self\n      end\n\n      if node_else = node.else\n        append_indent\n        @str << \"else\"\n        newline\n        accept_with_indent node_else\n      end\n\n      if node_ensure = node.ensure\n        append_indent\n        @str << \"ensure\"\n        newline\n        accept_with_indent node_ensure\n      end\n\n      append_indent\n      @str << \"end\"\n      false\n    end\n\n    def visit(node : Rescue)\n      @str << \"rescue\"\n      if name = node.name\n        @str << ' '\n        @str << name\n      end\n      if (types = node.types) && types.size > 0\n        if node.name\n          @str << \" :\"\n        end\n        @str << ' '\n        types.join(@str, \" | \", &.accept self)\n      end\n      newline\n      accept_with_indent node.body\n      false\n    end\n\n    def visit(node : Alias)\n      @str << \"alias \"\n      node.name.accept self\n      @str << \" = \"\n      node.value.accept self\n      false\n    end\n\n    def visit(node : TypeOf)\n      @str << \"typeof(\"\n      node.expressions.join(@str, \", \", &.accept self)\n      @str << ')'\n      false\n    end\n\n    def visit(node : Annotation)\n      @str << \"@[\"\n      @str << node.path\n      if !node.args.empty? || node.named_args\n        @str << '('\n        printed_arg = false\n        node.args.join(@str, \", \") do |arg|\n          arg.accept self\n          printed_arg = true\n        end\n        if named_args = node.named_args\n          named_args.each do |named_arg|\n            @str << \", \" if printed_arg\n            visit_named_arg_name(named_arg.name)\n            @str << \": \"\n            named_arg.value.accept self\n            printed_arg = true\n          end\n        end\n        @str << ')'\n      end\n      @str << ']'\n      false\n    end\n\n    def visit(node : MagicConstant)\n      @str << node.name\n      false\n    end\n\n    def visit(node : Asm)\n      @str << \"asm(\"\n      node.text.inspect(@str)\n      @str << \" :\"\n      if outputs = node.outputs\n        @str << ' '\n        outputs.join(@str, \", \", &.accept self)\n        @str << ' '\n      end\n      @str << ':'\n      if inputs = node.inputs\n        @str << ' '\n        inputs.join(@str, \", \", &.accept self)\n        @str << ' '\n      end\n      @str << \":\"\n      if clobbers = node.clobbers\n        @str << ' '\n        clobbers.join(@str, \", \", &.inspect @str)\n        @str << ' '\n      end\n      @str << \":\"\n      if node.volatile? || node.alignstack? || node.intel? || node.can_throw?\n        @str << ' '\n        comma = false\n        if node.volatile?\n          @str << %(\"volatile\")\n          comma = true\n        end\n        if node.alignstack?\n          @str << \", \" if comma\n          @str << %(\"alignstack\")\n          comma = true\n        end\n        if node.intel?\n          @str << \", \" if comma\n          @str << %(\"intel\")\n          comma = true\n        end\n        if node.can_throw?\n          @str << \", \" if comma\n          @str << %(\"unwind\")\n        end\n      end\n      @str << ')'\n      false\n    end\n\n    def visit(node : AsmOperand)\n      node.constraint.inspect(@str)\n      @str << '('\n      node.exp.accept self\n      @str << ')'\n      false\n    end\n\n    def newline\n      @str << '\\n'\n    end\n\n    def indent_string\n      \"  \"\n    end\n\n    def append_indent\n      @indent.times do\n        @str << indent_string\n      end\n    end\n\n    def with_indent(&)\n      @indent += 1\n      yield\n      @indent -= 1\n    end\n\n    def accept_with_indent(node : Expressions)\n      with_indent do\n        append_indent unless node.keyword.none?\n        node.accept self\n      end\n      newline unless node.keyword.none?\n    end\n\n    def accept_with_indent(node : Nop)\n    end\n\n    def accept_with_indent(node : ASTNode)\n      with_indent do\n        append_indent\n        node.accept self\n      end\n      newline\n    end\n\n    def inside_macro(&)\n      @inside_macro += 1\n      yield\n      @inside_macro -= 1\n    end\n\n    def outside_macro(&)\n      old_inside_macro = @inside_macro\n      @inside_macro = 0\n      yield\n      @inside_macro = old_inside_macro\n    end\n\n    def drop_parens_for_proc_notation(drop : Bool = true, &)\n      old_drop_parens_for_proc_notation = @drop_parens_for_proc_notation\n      @drop_parens_for_proc_notation = drop\n      begin\n        yield\n      ensure\n        @drop_parens_for_proc_notation = old_drop_parens_for_proc_notation\n      end\n    end\n\n    def drop_parens_for_proc_notation(node : ASTNode, &)\n      outermost_type_is_proc_notation =\n        case node\n        when Arg\n          # def / fun parameters\n          node.restriction.is_a?(ProcNotation)\n        when TypeDeclaration\n          # call arguments\n          node.declared_type.is_a?(ProcNotation)\n        else\n          false\n        end\n\n      drop_parens_for_proc_notation(outermost_type_is_proc_notation) { yield node }\n    end\n\n    def to_s : String\n      @str.to_s\n    end\n\n    def to_s(io : IO) : Nil\n      @str.to_s(io)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/token.cr",
    "content": "require \"./location\"\n\nmodule Crystal\n  # All possible identifiers that may be considered keywords.\n  enum Keyword\n    ABSTRACT\n    ALIAS\n    ALIGNOF\n    ANNOTATION\n    AS\n    AS_QUESTION\n    ASM\n    BEGIN\n    BREAK\n    CASE\n    CLASS\n    DEF\n    DO\n    ELSE\n    ELSIF\n    END\n    ENSURE\n    ENUM\n    EXTEND\n    FALSE\n    FOR\n    FUN\n    IF\n    IN\n    INCLUDE\n    INSTANCE_ALIGNOF\n    INSTANCE_SIZEOF\n    IS_A_QUESTION\n    LIB\n    MACRO\n    MODULE\n    NEXT\n    NIL\n    NIL_QUESTION\n    OF\n    OFFSETOF\n    OUT\n    POINTEROF\n    PRIVATE\n    PROTECTED\n    REQUIRE\n    RESCUE\n    RESPONDS_TO_QUESTION\n    RETURN\n    SELECT\n    SELF\n    SIZEOF\n    STRUCT\n    SUPER\n    THEN\n    TRUE\n    TYPE\n    TYPEOF\n    UNINITIALIZED\n    UNION\n    UNLESS\n    UNTIL\n    VERBATIM\n    WHEN\n    WHILE\n    WITH\n    YIELD\n\n    def to_s\n      case self\n      when AS_QUESTION\n        \"as?\"\n      when IS_A_QUESTION\n        \"is_a?\"\n      when NIL_QUESTION\n        \"nil?\"\n      when RESPONDS_TO_QUESTION\n        \"responds_to?\"\n      else\n        super.downcase\n      end\n    end\n  end\n\n  class Token\n    enum Kind\n      EOF\n      SPACE\n      NEWLINE\n\n      IDENT\n      CONST\n      INSTANCE_VAR\n      CLASS_VAR\n\n      CHAR\n      STRING\n      SYMBOL\n      NUMBER\n\n      UNDERSCORE\n      COMMENT\n\n      DELIMITER_START\n      DELIMITER_END\n\n      STRING_ARRAY_START\n      INTERPOLATION_START\n      SYMBOL_ARRAY_START\n      STRING_ARRAY_END\n\n      GLOBAL\n      GLOBAL_MATCH_DATA_INDEX\n\n      MAGIC_DIR\n      MAGIC_END_LINE\n      MAGIC_FILE\n      MAGIC_LINE\n\n      MACRO_LITERAL\n      MACRO_EXPRESSION_START\n      MACRO_CONTROL_START\n      MACRO_VAR\n      MACRO_END\n\n      # the following operator kinds should be sorted by their codepoints\n      # refer to `#to_s` for the constant names of each individual character\n\n      OP_BANG                     # !\n      OP_BANG_EQ                  # !=\n      OP_BANG_TILDE               # !~\n      OP_DOLLAR_QUESTION          # $?\n      OP_DOLLAR_TILDE             # $~\n      OP_PERCENT                  # %\n      OP_PERCENT_EQ               # %=\n      OP_PERCENT_RCURLY           # %}\n      OP_AMP                      # &\n      OP_AMP_AMP                  # &&\n      OP_AMP_AMP_EQ               # &&=\n      OP_AMP_STAR                 # &*\n      OP_AMP_STAR_STAR            # &**\n      OP_AMP_STAR_EQ              # &*=\n      OP_AMP_PLUS                 # &+\n      OP_AMP_PLUS_EQ              # &+=\n      OP_AMP_MINUS                # &-\n      OP_AMP_MINUS_EQ             # &-=\n      OP_AMP_EQ                   # &=\n      OP_LPAREN                   # (\n      OP_RPAREN                   # )\n      OP_STAR                     # *\n      OP_STAR_STAR                # **\n      OP_STAR_STAR_EQ             # **=\n      OP_STAR_EQ                  # *=\n      OP_PLUS                     # +\n      OP_PLUS_EQ                  # +=\n      OP_COMMA                    # ,\n      OP_MINUS                    # -\n      OP_MINUS_EQ                 # -=\n      OP_MINUS_GT                 # ->\n      OP_PERIOD                   # .\n      OP_PERIOD_PERIOD            # ..\n      OP_PERIOD_PERIOD_PERIOD     # ...\n      OP_SLASH                    # /\n      OP_SLASH_SLASH              # //\n      OP_SLASH_SLASH_EQ           # //=\n      OP_SLASH_EQ                 # /=\n      OP_COLON                    # :\n      OP_COLON_COLON              # ::\n      OP_SEMICOLON                # ;\n      OP_LT                       # <\n      OP_LT_LT                    # <<\n      OP_LT_LT_EQ                 # <<=\n      OP_LT_EQ                    # <=\n      OP_LT_EQ_GT                 # <=>\n      OP_EQ                       # =\n      OP_EQ_EQ                    # ==\n      OP_EQ_EQ_EQ                 # ===\n      OP_EQ_GT                    # =>\n      OP_EQ_TILDE                 # =~\n      OP_GT                       # >\n      OP_GT_EQ                    # >=\n      OP_GT_GT                    # >>\n      OP_GT_GT_EQ                 # >>=\n      OP_QUESTION                 # ?\n      OP_AT_LSQUARE               # @[\n      OP_LSQUARE                  # [\n      OP_LSQUARE_RSQUARE          # []\n      OP_LSQUARE_RSQUARE_EQ       # []=\n      OP_LSQUARE_RSQUARE_QUESTION # []?\n      OP_RSQUARE                  # ]\n      OP_CARET                    # ^\n      OP_CARET_EQ                 # ^=\n      OP_GRAVE                    # `\n      OP_LCURLY                   # {\n      OP_LCURLY_PERCENT           # {%\n      OP_LCURLY_LCURLY            # {{\n      OP_BAR                      # |\n      OP_BAR_EQ                   # |=\n      OP_BAR_BAR                  # ||\n      OP_BAR_BAR_EQ               # ||=\n      OP_RCURLY                   # }\n      OP_TILDE                    # ~\n\n      # non-flag enums are special since the `IO` overload relies on the\n      # `String`-returning overload instead of the other way round\n      def to_s : String\n        {% begin %}\n          {%\n            operator1 = {\n              \"BANG\" => \"!\", \"DOLLAR\" => \"$\", \"PERCENT\" => \"%\", \"AMP\" => \"&\", \"LPAREN\" => \"(\",\n              \"RPAREN\" => \")\", \"STAR\" => \"*\", \"PLUS\" => \"+\", \"COMMA\" => \",\", \"MINUS\" => \"-\",\n              \"PERIOD\" => \".\", \"SLASH\" => \"/\", \"COLON\" => \":\", \"SEMICOLON\" => \";\", \"LT\" => \"<\",\n              \"EQ\" => \"=\", \"GT\" => \">\", \"QUESTION\" => \"?\", \"AT\" => \"@\", \"LSQUARE\" => \"[\",\n              \"RSQUARE\" => \"]\", \"CARET\" => \"^\", \"GRAVE\" => \"`\", \"LCURLY\" => \"{\", \"BAR\" => \"|\",\n              \"RCURLY\" => \"}\", \"TILDE\" => \"~\",\n            }\n          %}\n\n          case self\n          {% for member in @type.constants %}\n          in {{ member.id }}\n            {% if member.starts_with?(\"OP_\") %}\n              {% parts = member.split(\"_\") %}\n              {{ parts.map { |ch| operator1[ch] || \"\" }.join(\"\") }}\n            {% elsif member.starts_with?(\"MAGIC_\") %}\n              {{ \"__#{member[6..-1].id}__\" }}\n            {% elsif member == \"UNDERSCORE\" %}\n              \"_\"\n            {% else %}\n              {{ member.stringify }}\n            {% end %}\n          {% end %}\n          end\n        {% end %}\n      end\n\n      def operator?\n        self.in?(OP_BANG..OP_TILDE)\n      end\n\n      def assignment_operator?\n        # += -= *= /= //= %= |= &= ^= **= <<= >>= ||= &&= &+= &-= &*=\n        case self\n        when .op_plus_eq?, .op_minus_eq?, .op_star_eq?, .op_slash_eq?, .op_slash_slash_eq?,\n             .op_percent_eq?, .op_bar_eq?, .op_amp_eq?, .op_caret_eq?, .op_star_star_eq?,\n             .op_lt_lt_eq?, .op_gt_gt_eq?, .op_bar_bar_eq?, .op_amp_amp_eq?, .op_amp_plus_eq?,\n             .op_amp_minus_eq?, .op_amp_star_eq?\n          true\n        else\n          false\n        end\n      end\n\n      # Returns true if the operator can be used in prefix notation.\n      #\n      # Related: `ToSVisitor::UNARY_OPERATORS`\n      def unary_operator?\n        self.in?(OP_BANG, OP_PLUS, OP_MINUS, OP_TILDE, OP_AMP_PLUS, OP_AMP_MINUS)\n      end\n\n      def magic?\n        magic_dir? || magic_end_line? || magic_file? || magic_line?\n      end\n    end\n\n    property type : Kind\n    property value : Char | String | Keyword | Nil\n    property number_kind : NumberKind\n    property line_number : Int32\n    property column_number : Int32\n    property filename : String | VirtualFile | Nil\n    property delimiter_state : DelimiterState\n    property macro_state : MacroState\n    property passed_backslash_newline : Bool\n    property doc_buffer : IO::Memory?\n    property raw : String\n    property start : Int32\n    property invalid_escape : Bool\n\n    record MacroState,\n      whitespace : Bool,\n      nest : Int32,\n      control_nest : Int32,\n      delimiter_state : DelimiterState?,\n      beginning_of_line : Bool,\n      yields : Bool,\n      comment : Bool,\n      heredocs : Array(DelimiterState)? do\n      def self.default\n        MacroState.new(true, 0, 0, nil, true, false, false, nil)\n      end\n\n      setter whitespace\n      setter control_nest\n    end\n\n    enum DelimiterKind\n      STRING\n      REGEX\n      STRING_ARRAY\n      SYMBOL_ARRAY\n      COMMAND\n      HEREDOC\n    end\n\n    record DelimiterState,\n      kind : DelimiterKind,\n      nest : Char | String,\n      end : Char | String,\n      open_count : Int32,\n      heredoc_indent : Int32,\n      allow_escapes : Bool do\n    end\n\n    struct DelimiterState\n      def self.default\n        DelimiterState.new(:string, '\\0', '\\0', 0, 0, true)\n      end\n\n      def self.new(kind : DelimiterKind, nest, the_end)\n        new kind, nest, the_end, 0, 0, true\n      end\n\n      def self.new(kind : DelimiterKind, nest, the_end, allow_escapes : Bool)\n        new kind, nest, the_end, 0, 0, allow_escapes\n      end\n\n      def self.new(kind : DelimiterKind, nest, the_end, open_count : Int32)\n        new kind, nest, the_end, open_count, 0, true\n      end\n\n      # Creates a DelimiterState for percent literals in macros.\n      # For symmetric delimiters (||), uses open_count = 0 (no nesting).\n      # For paired delimiters (()), uses open_count = 1 (enables nesting).\n      def self.percent_literal(kind : DelimiterKind, nest, the_end)\n        open_count = nest == the_end ? 0 : 1\n        new kind, nest, the_end, open_count, 0, true\n      end\n\n      def with_open_count_delta(delta)\n        DelimiterState.new(@kind, @nest, @end, @open_count + delta, @heredoc_indent, @allow_escapes)\n      end\n\n      def with_heredoc_indent(indent)\n        DelimiterState.new(@kind, @nest, @end, @open_count, indent, @allow_escapes)\n      end\n    end\n\n    def initialize\n      @type = Kind::EOF\n      @number_kind = NumberKind::I32\n      @line_number = 0\n      @column_number = 0\n      @delimiter_state = DelimiterState.default\n      @macro_state = MacroState.default\n      @passed_backslash_newline = false\n      @raw = \"\"\n      @start = 0\n      @invalid_escape = false\n    end\n\n    def doc\n      @doc_buffer.try &.to_s\n    end\n\n    @location : Location?\n\n    def location\n      @location ||= Location.new(filename, line_number, column_number)\n    end\n\n    def location=(@location)\n    end\n\n    def keyword?\n      @type.ident? && @value.is_a?(Keyword)\n    end\n\n    def keyword?(keyword : Keyword)\n      @type.ident? && @value == keyword\n    end\n\n    def copy_from(other)\n      @type = other.type\n      @value = other.value\n      @number_kind = other.number_kind\n      @line_number = other.line_number\n      @column_number = other.column_number\n      @filename = other.filename\n      @delimiter_state = other.delimiter_state\n      @macro_state = other.macro_state\n      @doc_buffer = other.doc_buffer\n    end\n\n    def to_s(io : IO) : Nil\n      @value ? @value.to_s(io) : @type.to_s(io)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/transformer.cr",
    "content": "require \"./ast\"\n\nmodule Crystal\n  class ASTNode\n    def transform(transformer)\n      transformer.before_transform self\n      node = transformer.transform self\n      transformer.after_transform self\n      node\n    end\n  end\n\n  class Transformer\n    def before_transform(node)\n    end\n\n    def after_transform(node)\n    end\n\n    def transform(node : Expressions)\n      exps = [] of ASTNode\n      node.expressions.each do |exp|\n        new_exp = exp.transform(self)\n        if new_exp\n          if new_exp.is_a?(Expressions)\n            exps.concat new_exp.expressions\n          else\n            exps << new_exp\n          end\n        end\n      end\n\n      if exps.size == 1\n        exps[0]\n      else\n        node.expressions = exps\n        node\n      end\n    end\n\n    def transform(node : Call)\n      if node_obj = node.obj\n        node.obj = node_obj.transform(self)\n      end\n      transform_many node.args\n\n      if node_block = node.block\n        node.block = node_block.transform(self)\n      end\n\n      if node_block_arg = node.block_arg\n        node.block_arg = node_block_arg.transform(self)\n      end\n\n      if named_args = node.named_args\n        named_args.map! { |named_arg| named_arg.transform(self).as(NamedArgument) }\n      end\n\n      node\n    end\n\n    def transform(node : NamedArgument)\n      node.value = node.value.transform(self)\n      node\n    end\n\n    def transform(node : And)\n      node.left = node.left.transform(self)\n      node.right = node.right.transform(self)\n      node\n    end\n\n    def transform(node : Or)\n      node.left = node.left.transform(self)\n      node.right = node.right.transform(self)\n      node\n    end\n\n    def transform(node : StringInterpolation)\n      transform_many node.expressions\n      node\n    end\n\n    def transform(node : ArrayLiteral)\n      transform_many node.elements\n\n      if node_of = node.of\n        node.of = node_of.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : HashLiteral)\n      node.entries.map! do |entry|\n        HashLiteral::Entry.new(entry.key.transform(self), entry.value.transform(self))\n      end\n\n      if of = node.of\n        node.of = HashLiteral::Entry.new(of.key.transform(self), of.value.transform(self))\n      end\n\n      node\n    end\n\n    def transform(node : NamedTupleLiteral)\n      node.entries.map! do |entry|\n        NamedTupleLiteral::Entry.new(entry.key, entry.value.transform(self))\n      end\n      node\n    end\n\n    def transform(node : If)\n      node.cond = node.cond.transform(self)\n      node.then = node.then.transform(self)\n      node.else = node.else.transform(self)\n      node\n    end\n\n    def transform(node : Unless)\n      node.cond = node.cond.transform(self)\n      node.then = node.then.transform(self)\n      node.else = node.else.transform(self)\n      node\n    end\n\n    def transform(node : MultiAssign)\n      transform_many node.targets\n      transform_many node.values\n      node\n    end\n\n    def transform(node : Def)\n      transform_many node.args\n      node.body = node.body.transform(self)\n\n      if receiver = node.receiver\n        node.receiver = receiver.transform(self)\n      end\n\n      if double_splat = node.double_splat\n        node.double_splat = double_splat.transform(self)\n      end\n\n      if block_arg = node.block_arg\n        node.block_arg = block_arg.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : Macro)\n      transform_many node.args\n\n      if double_splat = node.double_splat\n        node.double_splat = double_splat.transform(self)\n      end\n\n      if block_arg = node.block_arg\n        node.block_arg = block_arg.transform(self)\n      end\n\n      node.body = node.body.transform(self)\n\n      node\n    end\n\n    def transform(node : PointerOf)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : SizeOf)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : InstanceSizeOf)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : AlignOf)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : InstanceAlignOf)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : OffsetOf)\n      node.offsetof_type = node.offsetof_type.transform(self)\n      node.offset = node.offset.transform(self)\n      node\n    end\n\n    def transform(node : ReadInstanceVar)\n      node.obj = node.obj.transform(self)\n      node\n    end\n\n    def transform(node : IsA)\n      node.obj = node.obj.transform(self)\n      node.const = node.const.transform(self)\n      node\n    end\n\n    def transform(node : RespondsTo)\n      node.obj = node.obj.transform(self)\n      node\n    end\n\n    def transform(node : Case)\n      node.cond = node.cond.try &.transform(self)\n      transform_many node.whens\n\n      if node_else = node.else\n        node.else = node_else.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : When)\n      transform_many node.conds\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : Select)\n      transform_many node.whens\n\n      if node_else = node.else\n        node.else = node_else.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : ImplicitObj)\n      node\n    end\n\n    def transform(node : ClassDef)\n      node.body = node.body.transform(self)\n\n      if superclass = node.superclass\n        node.superclass = superclass.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : ModuleDef)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : AnnotationDef)\n      node\n    end\n\n    def transform(node : While)\n      node.cond = node.cond.transform(self)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : Until)\n      node.cond = node.cond.transform(self)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : Generic)\n      node.name = node.name.transform(self)\n      transform_many node.type_vars\n      node\n    end\n\n    def transform(node : ExceptionHandler)\n      node.body = node.body.transform(self)\n      transform_many node.rescues\n\n      if node_ensure = node.ensure\n        node.ensure = node_ensure.transform(self)\n      end\n\n      if node_else = node.else\n        node.else = node_else.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : Rescue)\n      node.body = node.body.transform(self)\n      transform_many node.types\n      node\n    end\n\n    def transform(node : Union)\n      transform_many node.types\n      node\n    end\n\n    def transform(node : Metaclass)\n      node.name = node.name.transform(self)\n      node\n    end\n\n    def transform(node : Arg)\n      if default_value = node.default_value\n        node.default_value = default_value.transform(self)\n      end\n\n      if restriction = node.restriction\n        node.restriction = restriction.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : ProcNotation)\n      transform_many node.inputs\n\n      if output = node.output\n        node.output = output.transform(self)\n      end\n\n      node\n    end\n\n    def transform(node : Block)\n      node.args.map! { |exp| exp.transform(self).as(Var) }\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : ProcLiteral)\n      node.def.body = node.def.body.transform(self)\n      node\n    end\n\n    def transform(node : ProcPointer)\n      if obj = node.obj\n        node.obj = obj.transform(self)\n      end\n      node\n    end\n\n    def transform(node : Return)\n      node.exp = node.exp.try &.transform(self)\n      node\n    end\n\n    def transform(node : Break)\n      node.exp = node.exp.try &.transform(self)\n      node\n    end\n\n    def transform(node : Next)\n      node.exp = node.exp.try &.transform(self)\n      node\n    end\n\n    def transform(node : Yield)\n      if scope = node.scope\n        node.scope = scope.transform(self)\n      end\n      transform_many node.exps\n      node\n    end\n\n    def transform(node : Include)\n      node.name = node.name.transform(self)\n      node\n    end\n\n    def transform(node : Extend)\n      node.name = node.name.transform(self)\n      node\n    end\n\n    def transform(node : RangeLiteral)\n      node.from = node.from.transform(self)\n      node.to = node.to.transform(self)\n      node\n    end\n\n    def transform(node : Assign)\n      node.target = node.target.transform(self)\n      node.value = node.value.transform(self)\n      node\n    end\n\n    def transform(node : OpAssign)\n      node.target = node.target.transform(self)\n      node.value = node.value.transform(self)\n      node\n    end\n\n    def transform(node : Out)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : Nop)\n      node\n    end\n\n    def transform(node : NilLiteral)\n      node\n    end\n\n    def transform(node : BoolLiteral)\n      node\n    end\n\n    def transform(node : NumberLiteral)\n      node\n    end\n\n    def transform(node : CharLiteral)\n      node\n    end\n\n    def transform(node : StringLiteral)\n      node\n    end\n\n    def transform(node : SymbolLiteral)\n      node\n    end\n\n    def transform(node : RegexLiteral)\n      node.value = node.value.transform(self)\n      node\n    end\n\n    def transform(node : Var)\n      node\n    end\n\n    def transform(node : InstanceVar)\n      node\n    end\n\n    def transform(node : ClassVar)\n      node\n    end\n\n    def transform(node : Global)\n      node\n    end\n\n    def transform(node : Require)\n      node\n    end\n\n    def transform(node : Path)\n      node\n    end\n\n    def transform(node : Self)\n      node\n    end\n\n    def transform(node : LibDef)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : FunDef)\n      if body = node.body\n        node.body = body.transform(self)\n      end\n      node\n    end\n\n    def transform(node : TypeDef)\n      node\n    end\n\n    def transform(node : CStructOrUnionDef)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : EnumDef)\n      transform_many node.members\n      node\n    end\n\n    def transform(node : ExternalVar)\n      node\n    end\n\n    def transform(node : TypeOf)\n      transform_many node.expressions\n      node\n    end\n\n    def transform(node : MagicConstant)\n      node\n    end\n\n    def transform(node : Not)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : TupleLiteral)\n      transform_many node.elements\n      node\n    end\n\n    def transform(node : Cast)\n      node.obj = node.obj.transform(self)\n      node.to = node.to.transform(self)\n      node\n    end\n\n    def transform(node : NilableCast)\n      node.obj = node.obj.transform(self)\n      node.to = node.to.transform(self)\n      node\n    end\n\n    def transform(node : TypeDeclaration)\n      node.var = node.var.transform(self)\n      node.declared_type = node.declared_type.transform(self)\n      node.value = node.value.try &.transform(self)\n      node\n    end\n\n    def transform(node : UninitializedVar)\n      node.var = node.var.transform(self)\n      node.declared_type = node.declared_type.transform(self)\n      node\n    end\n\n    def transform(node : Alias)\n      node.value = node.value.transform(self)\n      node\n    end\n\n    def transform(node : Splat)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : DoubleSplat)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : VisibilityModifier)\n      node.exp = node.exp.transform(self)\n      node\n    end\n\n    def transform(node : Annotation)\n      node\n    end\n\n    def transform(node : MacroExpression)\n      node\n    end\n\n    def transform(node : MacroLiteral)\n      node\n    end\n\n    def transform(node : MacroVerbatim)\n      node\n    end\n\n    def transform(node : MacroIf)\n      node\n    end\n\n    def transform(node : MacroFor)\n      node\n    end\n\n    def transform(node : MacroVar)\n      node\n    end\n\n    def transform(node : Underscore)\n      node\n    end\n\n    def transform(node : Asm)\n      node.outputs.try &.each &.transform(self)\n      node.inputs.try &.each &.transform(self)\n      node\n    end\n\n    def transform(node : AsmOperand)\n      node.exp = node.exp.transform self\n      node\n    end\n\n    def transform_many(exps)\n      exps.map!(&.transform(self)) if exps\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/virtual_file.cr",
    "content": "# A VirtualFile is used as a Location's filename when\n# expanding a macro. It contains the macro expanded source\n# code so the user can debug it as if there was a file in the\n# filesystem with those contents.\nclass Crystal::VirtualFile\n  # The macro that produced this VirtualFile\n  getter macro : Macro\n\n  # The expanded source code of the macro\n  getter source : String\n\n  # The location where the macro was expanded (where the macro was invoked).\n  getter expanded_location : Location?\n\n  def initialize(@macro : Macro, @source : String, @expanded_location : Location?)\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"expanded macro: \" << @macro.name\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def pretty_print(pp)\n    pp.text inspect\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax/visitor.cr",
    "content": "require \"./ast\"\n\nmodule Crystal\n  class Visitor\n    def visit_any(node)\n      true\n    end\n\n    # def visit(node)\n    #   true\n    # end\n\n    def end_visit(node)\n    end\n\n    def end_visit_any(node)\n    end\n\n    def accept(node)\n      node.accept self\n    end\n  end\n\n  class ASTNode\n    def accept(visitor)\n      if visitor.visit_any self\n        if visitor.visit self\n          accept_children visitor\n        end\n        visitor.end_visit self\n        visitor.end_visit_any self\n      end\n    end\n\n    def accept_children(visitor)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/syntax.cr",
    "content": "# This file exists so you can do:\n#\n# ```\n# require \"compiler/crystal/syntax\"\n# ```\n#\n# and use the syntax bits of Crystal (lexer, parser, visitor)\n# without requiring all the semantic and codegen code.\n\nrequire \"./syntax/*\"\n"
  },
  {
    "path": "src/compiler/crystal/tools/context.cr",
    "content": "require \"../syntax/ast\"\nrequire \"../compiler\"\nrequire \"../semantic/*\"\nrequire \"./table_print\"\nrequire \"./typed_def_processor\"\nrequire \"json\"\n\nmodule Crystal\n  class PrettyTypeNameJsonConverter\n    def self.to_json(hash, json : JSON::Builder)\n      json.object do\n        hash.each do |key, value|\n          json.field key, pretty_type_name(value)\n        end\n      end\n    end\n\n    def self.pretty_type_name(type)\n      String.build do |io|\n        type.to_s_with_options(io, true)\n      end\n    end\n\n    def self.pretty_type_name(type, io)\n      type.to_s_with_options(io, true)\n    end\n  end\n\n  class ContextResult\n    include JSON::Serializable\n\n    property status : String\n    property message : String\n\n    @[JSON::Field(converter: JSON::ArrayConverter(Crystal::PrettyTypeNameJsonConverter))]\n    property contexts : Array(Hash(String, Type))?\n\n    def initialize(@status, @message)\n    end\n\n    def to_text(io)\n      io.puts message\n\n      if (ctxs = contexts) && ctxs.size > 0\n        exprs = ctxs.first.keys\n\n        io.puts\n        TablePrint.new(io).build do\n          row do\n            cell \"Expr\"\n            cell \"Type\", colspan: ctxs.size\n          end\n          separator\n\n          exprs.each do |expr|\n            row do\n              cell expr\n              ctxs.each do |ctx|\n                cell align: :center do |io|\n                  PrettyTypeNameJsonConverter.pretty_type_name(ctx[expr], io)\n                end\n              end\n            end\n          end\n        end\n        io.puts\n      end\n    end\n  end\n\n  class ReachableVisitor < Visitor\n    @visited_typed_defs : Set(Def)\n\n    def initialize(@context_visitor : Crystal::ContextVisitor)\n      @visited_typed_defs = Set(Def).new.compare_by_identity\n    end\n\n    def visit(node : Call)\n      return false if node.obj.nil? && node.name == \"raise\"\n      node.target_defs.try do |defs|\n        defs.each do |typed_def|\n          typed_def.accept(self)\n          next unless @context_visitor.def_with_yield.not_nil!.location == typed_def.location\n          @context_visitor.inside_typed_def do\n            typed_def.accept(@context_visitor)\n          end\n        end\n      end\n      true\n    end\n\n    def visit(node : Def)\n      @visited_typed_defs.add?(node)\n    end\n\n    def visit(node)\n      true\n    end\n  end\n\n  class ContextVisitor < Visitor\n    include TypedDefProcessor\n\n    getter contexts : Array(Hash(String, Type))\n    getter def_with_yield : Def?\n\n    def initialize(@target_location : Location)\n      @contexts = Array(Hash(String, Type)).new\n      @context = Hash(String, Type).new\n      @def_with_yield = nil\n      @inside_typed_def = false\n      @found_untyped_def = false\n    end\n\n    def inside_typed_def(&)\n      @inside_typed_def = true\n      yield.tap { @inside_typed_def = false }\n    end\n\n    def process_typed_def(typed_def)\n      return unless loc = typed_def.location\n      return unless loc.filename == typed_def.end_location.try &.filename\n      return unless contains_target typed_def\n      return if typed_def.new? # Skip autogenerated 'new' methods\n\n      @context = Hash(String, Type).new\n\n      type = typed_def.owner\n      if type.is_a?(GenericInstanceType)\n        type.type_vars.each_value do |type_var|\n          add_context type_var.name, type_var.type if type_var.is_a?(Var)\n        end\n      end\n      add_context \"self\", type\n      if type.is_a?(InstanceVarContainer)\n        type.instance_vars.each_value do |ivar|\n          add_context ivar.name, ivar.type\n        end\n      end\n      inside_typed_def { typed_def.accept(self) }\n\n      @contexts << @context unless @context.empty?\n    end\n\n    def process(result : Compiler::Result)\n      process_result result\n\n      if @contexts.empty?\n        @context = Hash(String, Type).new\n        result.program.vars.each do |name, var|\n          add_context name, var.type\n        end\n        result.node.accept(self)\n\n        if @def_with_yield\n          @context = Hash(String, Type).new\n          result.node.accept(ReachableVisitor.new(self))\n        end\n\n        # TODO should apply only if user is really in some of the nodes of the main expressions\n        @contexts << @context unless @context.empty?\n      end\n\n      if @contexts.empty?\n        if @found_untyped_def\n          ContextResult.new(\"failed\", \"no context information found (methods which are never called don't have a context)\")\n        else\n          ContextResult.new(\"failed\", \"no context information found\")\n        end\n      else\n        res = ContextResult.new(\"ok\", \"#{@contexts.size} possible context#{@contexts.size > 1 ? \"s\" : \"\"} found\")\n        res.contexts = @contexts\n        res\n      end\n    end\n\n    def visit(node : Def)\n      return false unless contains_target(node)\n\n      if @def_with_yield.nil? && !node.block_arity.nil?\n        @def_with_yield = node\n        return false\n      end\n\n      unless @inside_typed_def\n        @found_untyped_def = true\n        return false\n      end\n\n      node.args.each do |arg|\n        add_context arg.name, arg.type\n      end\n      node.vars.try do |vars|\n        vars.each do |name, meta_var|\n          add_context name, meta_var.type\n        end\n      end\n\n      true\n    end\n\n    def visit(node : Block)\n      return false unless contains_target(node)\n\n      node.args.each do |arg|\n        add_context arg.name, arg.type\n      end\n\n      node.vars.try do |vars|\n        vars.each do |_, var|\n          add_context var.name, var.type\n        end\n      end\n\n      true\n    end\n\n    def visit(node : Call)\n      if node.location && @target_location.between?(node.name_location, node.name_end_location)\n        add_context node.to_s, node.type\n      end\n\n      contains_target(node)\n    end\n\n    # TODO handle type filters of case statements\n\n    def visit(node : If)\n      return false unless contains_target(node)\n\n      # TODO handle conditions in expressions\n      case cond = node.cond\n      when Var\n        filters = TypeFilters.truthy(cond)\n      when IsA\n        if (obj = cond.obj).is_a?(Var)\n          filters = TypeFilters.new(obj, SimpleTypeFilter.new(cond.const.type))\n        end\n      end\n\n      if filters\n        # make a copy of the current context\n        current_context = {} of String => MetaVar\n        @context.each do |name, type|\n          current_context[name] = MetaVar.new(name, type)\n        end\n\n        # restrict the whole context\n        filters.each do |name, filter|\n          filtered_var = current_context[name]\n          filtered_var.bind_to(current_context[name].filtered_by(filter))\n          add_context name, filtered_var.type\n        end\n      end\n\n      true\n    end\n\n    def visit(node)\n      contains_target(node)\n    end\n\n    private def add_context(name, type)\n      return if name.starts_with?(\"__temp_\") # ignore temp vars\n      return if type.is_a?(Program) || type.is_a?(FileModule)\n\n      @context[name] = type\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/dependencies.cr",
    "content": "require \"set\"\nrequire \"colorize\"\nrequire \"../syntax/ast\"\n\nclass Crystal::Command\n  private def dependencies\n    config = create_compiler \"tool dependencies\", no_codegen: true, dependencies: true, allowed_formats: DependencyPrinter::Format.names.map!(&.downcase)\n\n    dependency_printer = DependencyPrinter.create(STDOUT, format: DependencyPrinter::Format.parse(config.output_format), verbose: config.verbose)\n\n    dependency_printer.includes.concat config.includes.map { |path| ::Path[path].expand.to_posix.to_s }\n    dependency_printer.excludes.concat config.excludes.map { |path| ::Path[path].expand.to_posix.to_s }\n    config.compiler.dependency_printer = dependency_printer\n\n    dependency_printer.start_format\n    config.compiler.top_level_semantic config.sources\n    dependency_printer.end_format\n  end\nend\n\nmodule Crystal\n  abstract class DependencyPrinter\n    enum Format\n      Tree\n      Flat\n      Dot\n      Mermaid\n    end\n\n    @stack = [] of String\n    @filter_depth = Int32::MAX\n\n    @format : Format\n\n    property includes = [] of String\n    property excludes = [] of String\n\n    getter default_paths : Array(::Path) = CrystalPath.default_paths.map { |path| ::Path[path].expand }\n\n    def self.create(io : IO, format : Format = Format::Tree, verbose : Bool = false)\n      case format\n      in .flat?, .tree?\n        List.new(io, format, verbose)\n      in .dot?\n        Dot.new(io, format, verbose)\n      in .mermaid?\n        Mermaid.new(io, format, verbose)\n      end\n    end\n\n    def initialize(@io : IO, @format : Format = Format::Tree, @verbose : Bool = false)\n    end\n\n    def enter_file(filename : String, unseen : Bool)\n      if @stack.size <= @filter_depth\n        filter = filter?(filename)\n\n        if filter\n          @filter_depth = @stack.size\n        else\n          @filter_depth = Int32::MAX\n        end\n\n        if (unseen && !filter) || @verbose\n          print_indent\n\n          print_file(filename, @stack.last?, filter, unseen)\n        end\n      end\n\n      @stack << filename\n    end\n\n    def leave_file\n      @stack.pop\n    end\n\n    def start_format\n    end\n\n    private def print_indent\n    end\n\n    private abstract def print_file(filename, parent, filter, unseen)\n\n    def end_format\n    end\n\n    private def edge_comment(filter = false, unseen = false)\n      if unseen\n        \"filtered\" if filter\n      else\n        \"duplicate skipped\"\n      end\n    end\n\n    private def path(filename)\n      ::Path[filename].relative_to?(Dir.current) || filename\n    end\n\n    private def filter?(filename)\n      paths = ::Path[filename].parents\n      paths << ::Path[filename]\n\n      return false if match_patterns?(includes, paths)\n\n      return true if default_paths.any? { |path| paths.includes?(path) }\n\n      match_patterns?(excludes, paths)\n    end\n\n    private def match_patterns?(patterns, paths)\n      patterns.any? { |pattern| paths.any? { |path| File.match?(pattern, path) } }\n    end\n\n    class List < DependencyPrinter\n      private def print_file(filename, parent, filter, unseen)\n        @io.print path(filename)\n        if comment = edge_comment(filter, unseen)\n          @io.print \" \"\n          @io.print comment\n        end\n        @io.puts\n      end\n\n      private def print_indent\n        @io.print \"  \" * @stack.size unless @stack.empty? || @format.flat?\n      end\n    end\n\n    class Dot < DependencyPrinter\n      def start_format\n        @io.puts \"digraph G {\"\n      end\n\n      def end_format\n        @io.puts \"}\"\n      end\n\n      private def print_file(filename, parent, filter, unseen)\n        return unless parent\n\n        @io.print \"  \"\n        @io.print path(parent)\n        @io.print \" -> \"\n        @io.print path(filename)\n        if comment = edge_comment(filter, unseen)\n          @io.print %( [label=\"#{comment}\"])\n        end\n        @io.puts\n      end\n\n      private def path(filename)\n        super.to_s.inspect\n      end\n    end\n\n    class Mermaid < DependencyPrinter\n      def start_format\n        @io.puts \"graph LR\"\n      end\n\n      private def print_file(filename, parent, filter, unseen)\n        return unless parent\n\n        @io.print \"  \"\n        @io.print path(parent)\n        @io.print \" -->\"\n        if comment = edge_comment(filter, unseen)\n          @io.print \"|#{comment}|\"\n        end\n        @io.print \" \"\n        @io.print path(filename)\n        @io.puts\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/constant.cr",
    "content": "require \"./item\"\n\nclass Crystal::Doc::Constant\n  include Item\n\n  getter type : Type\n  getter const : Const\n\n  def initialize(@generator : Generator, @type : Type, @const : Const)\n  end\n\n  def doc\n    @const.doc\n  end\n\n  def name\n    @const.name\n  end\n\n  def id\n    name\n  end\n\n  def value\n    @const.value\n  end\n\n  def formatted_value\n    SyntaxHighlighter::HTML.highlight! value.to_s\n  end\n\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"id\", id\n      builder.field \"name\", name\n      builder.field \"value\", value.try(&.to_s)\n      builder.field \"doc\", doc unless doc.nil?\n      builder.field \"summary\", formatted_summary unless formatted_summary.nil?\n    end\n  end\n\n  def annotations(annotation_type)\n    @const.annotations(annotation_type)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/generator.cr",
    "content": "require \"markd\"\nrequire \"crystal/syntax_highlighter/html\"\n\nclass Crystal::Doc::Generator\n  getter program : Program\n\n  @base_dir : String\n  getter project_info\n\n  # Adding a flag and associated css class will add support in parser\n  FLAG_COLORS = {\n    \"BUG\"          => \"red\",\n    \"DEPRECATED\"   => \"red\",\n    \"WARNING\"      => \"yellow\",\n    \"EXPERIMENTAL\" => \"lime\",\n    \"FIXME\"        => \"yellow\",\n    \"NOTE\"         => \"purple\",\n    \"OPTIMIZE\"     => \"green\",\n    \"TODO\"         => \"orange\",\n  }\n  FLAGS = FLAG_COLORS.keys\n\n  def self.new(program : Program, included_dirs : Array(String))\n    new(program, included_dirs, \".\", \"html\", nil, \"1.0\", \"never\", ProjectInfo.new(\"test\", \"0.0.0-test\"))\n  end\n\n  def initialize(@program : Program, @included_dirs : Array(String),\n                 @output_dir : String, @output_format : String,\n                 @sitemap_base_url : String?,\n                 @sitemap_priority : String, @sitemap_changefreq : String,\n                 @project_info : ProjectInfo)\n    @base_dir = Dir.current.chomp\n    @types = {} of Crystal::Type => Doc::Type\n  end\n\n  def run\n    types = collect_subtypes(@program)\n\n    program_type = type(@program)\n    if must_include_toplevel? program_type\n      types.insert 0, program_type\n    end\n\n    if @output_format == \"json\"\n      generate_docs_json program_type, types\n    else\n      generate_docs_html program_type, types\n    end\n  end\n\n  def program_type\n    type(@program)\n  end\n\n  def read_readme\n    if File.file?(\"README.md\")\n      filename = \"README.md\"\n    elsif File.file?(\"Readme.md\")\n      filename = \"Readme.md\"\n    end\n\n    if filename\n      content = File.read(filename)\n    else\n      content = \"\"\n    end\n\n    content\n  end\n\n  def generate_docs_json(program_type, types)\n    readme = read_readme\n    json = Main.new(readme, Type.new(self, @program), project_info)\n    puts json\n  end\n\n  def generate_docs_html(program_type, types)\n    copy_files\n    generate_types_docs types, @output_dir, types\n    generate_readme program_type, types\n    generate_sitemap types\n  end\n\n  def generate_readme(program_type, types)\n    raw_body = read_readme\n    body = doc(program_type, raw_body)\n\n    File.write File.join(@output_dir, \"index.html\"), MainTemplate.new(body, types, project_info)\n\n    main_index = Main.new(raw_body, Type.new(self, @program), project_info)\n    File.write File.join(@output_dir, \"index.json\"), main_index\n    File.write File.join(@output_dir, \"search-index.js\"), main_index.to_jsonp\n\n    File.write File.join(@output_dir, \"404.html\"), MainTemplate.new(Error404Template.new.to_s, types, project_info, base_path: project_info.base_path)\n  end\n\n  def generate_sitemap(types)\n    if sitemap_base_url = @sitemap_base_url\n      File.write File.join(@output_dir, \"sitemap.xml\"), SitemapTemplate.new(types, sitemap_base_url, @sitemap_priority, @sitemap_changefreq)\n    end\n  end\n\n  def copy_files\n    Dir.mkdir_p File.join(@output_dir, \"css\")\n    Dir.mkdir_p File.join(@output_dir, \"js\")\n\n    File.write File.join(@output_dir, \"css\", \"style.css\"), StyleTemplate.new\n    File.write File.join(@output_dir, \"js\", \"doc.js\"), JsTypeTemplate.new\n  end\n\n  def generate_types_docs(types, dir, all_types)\n    types.each do |type|\n      if type.program?\n        filename = File.join(dir, \"toplevel.html\")\n      else\n        filename = File.join(dir, \"#{type.name}.html\")\n      end\n\n      File.write filename, TypeTemplate.new(type, all_types, project_info)\n\n      next if type.program?\n\n      subtypes = type.types\n      if subtypes && !subtypes.empty?\n        dirname = File.join(dir, type.name)\n        Dir.mkdir_p dirname\n        generate_types_docs subtypes, dirname, all_types\n      end\n    end\n  end\n\n  def must_include?(type : Doc::Type)\n    must_include? type.type\n  end\n\n  def must_include?(type : Crystal::Type)\n    return false if type.private? && !showdoc?(type)\n    return false if nodoc? type\n    return true if crystal_builtin?(type)\n\n    # Don't include types whose namespace is :nodoc:\n    type.each_namespace do |ns|\n      return false if nodoc? ns\n    end\n\n    # Don't include lib types or types inside a lib type unless specified with `:showdoc:`\n    if (type.is_a?(LibType) || type.namespace.is_a?(LibType)) && !showdoc?(type)\n      return false\n    end\n\n    !!type.locations.try &.any? do |type_location|\n      must_include? type_location\n    end\n  end\n\n  def must_include?(method : Method)\n    must_include? method.def\n  end\n\n  def must_include?(a_def : Crystal::Def)\n    return false if nodoc? a_def\n\n    must_include? a_def.location\n  end\n\n  def must_include?(a_macro : Doc::Macro)\n    must_include? a_macro.macro\n  end\n\n  def must_include?(a_macro : Crystal::Macro)\n    return false if nodoc? a_macro\n\n    must_include? a_macro.location\n  end\n\n  def must_include?(constant : Constant)\n    must_include? constant.const\n  end\n\n  def must_include?(const : Crystal::Const)\n    return false if nodoc? const\n    return true if crystal_builtin?(const)\n\n    !!const.locations.try &.any? { |location| must_include? location }\n  end\n\n  def must_include?(location : Crystal::Location)\n    case filename = location.filename\n    when String\n      @included_dirs.any? { |included_dir| filename.starts_with? included_dir }\n    when VirtualFile\n      must_include? filename.expanded_location\n    else\n      false\n    end\n  end\n\n  def must_include?(a_nil : Nil)\n    false\n  end\n\n  def must_include_toplevel?(program_type : Type)\n    toplevel_items = [] of Method | Macro | Constant\n    toplevel_items.concat program_type.class_methods\n    toplevel_items.concat program_type.macros\n    toplevel_items.concat program_type.constants\n\n    toplevel_items.any? { |item| must_include? item }\n  end\n\n  def nodoc?(str : String?) : Bool\n    return false if !str || !@program.wants_doc?\n    str.starts_with?(\":nodoc:\")\n  end\n\n  def nodoc?(obj)\n    nodoc? obj.doc.try &.strip\n  end\n\n  def showdoc?(str : String?) : Bool\n    return false if !str || !@program.wants_doc?\n    str.starts_with?(\":showdoc:\")\n  end\n\n  def showdoc?(obj : Crystal::Type)\n    showdoc?(obj.doc.try &.strip)\n  end\n\n  def crystal_builtin?(type)\n    return false unless project_info.crystal_stdlib?\n    # TODO: Enabling this allows links to `NoReturn` to work, but has two `NoReturn`s show up in the sidebar\n    # return true if type.is_a?(NamedType) && {\"NoReturn\", \"Void\"}.includes?(type.name)\n    return false unless type.is_a?(Const) || type.is_a?(NonGenericModuleType)\n\n    crystal_type = @program.types[\"Crystal\"]\n    return true if type == crystal_type\n\n    return false unless type.is_a?(Const)\n    return false unless type.namespace == crystal_type\n\n    {\"BUILD_COMMIT\", \"BUILD_DATE\", \"CACHE_DIR\", \"DEFAULT_PATH\",\n     \"DESCRIPTION\", \"PATH\", \"VERSION\", \"LLVM_VERSION\",\n     \"LIBRARY_PATH\", \"HOST_TRIPLE\", \"TARGET_TRIPLE\"}.each do |name|\n      return true if type == crystal_type.types[name]?\n    end\n\n    false\n  end\n\n  def type(type)\n    @types[type] ||= Type.new(self, type)\n  end\n\n  def method(type, method, class_method)\n    Method.new(self, type, method, class_method)\n  end\n\n  def macro(type, a_macro)\n    Macro.new(self, type, a_macro)\n  end\n\n  def collect_subtypes(parent)\n    types = [] of Type\n\n    parent.types?.try &.each_value do |type|\n      case type\n      when Const\n        next\n      else\n        types << type(type) if must_include? type\n      end\n    end\n\n    types.sort_by! &.name.downcase\n  end\n\n  def collect_constants(parent)\n    types = [] of Constant\n\n    parent.type.types?.try &.each_value do |type|\n      if type.is_a?(Const) && must_include?(type) && (!type.private? || showdoc?(type))\n        types << Constant.new(self, parent, type)\n      end\n    end\n\n    types.sort_by! &.name.downcase unless parent.type.is_a?(EnumType)\n    types\n  end\n\n  def summary(obj : Type | Method | Macro | Constant)\n    doc = obj.doc\n\n    return if !doc && !has_doc_annotations?(obj)\n\n    summary obj, doc || \"\"\n  end\n\n  def summary(context, string)\n    line = fetch_doc_lines(string.strip).lines.first? || \"\"\n\n    dot_index = line =~ /\\.($|\\s)/\n    if dot_index\n      line = line[0..dot_index]\n    end\n\n    doc context, line\n  end\n\n  def doc(obj : Type | Method | Macro | Constant)\n    doc = obj.doc.try &.strip.lchop(\":showdoc:\").strip\n\n    return if !doc && !has_doc_annotations?(obj)\n\n    doc obj, doc || \"\"\n  end\n\n  def has_doc_annotations?(obj)\n    obj.annotations(@program.deprecated_annotation) ||\n      obj.annotations(@program.experimental_annotation) ||\n      obj.as?(Method).try &.def.args.any? { |arg| has_doc_annotations?(arg) }\n  end\n\n  def doc(context, string)\n    string = isolate_flag_lines string\n    if annotations = build_flag_lines_from_annotations context\n      string += \"\\n\\n\" + annotations\n    end\n    markdown = render_markdown(context, string)\n    generate_flags markdown\n  end\n\n  private def render_markdown(context, source)\n    options = ::Markd::Options.new\n    document = ::Markd::Parser.parse(source, options)\n    renderer = MarkdDocRenderer.new(context, options)\n    renderer.render(document).chomp\n  end\n\n  def fetch_doc_lines(doc : String) : String\n    doc.gsub /\\n+/ { |match| match.size == 1 ? \" \" : \"\\n\" }\n  end\n\n  # Replaces flag keywords with html equivalent\n  #\n  # Assumes that flag keywords are at the beginning of respective `p` element\n  def generate_flags(string)\n    FLAGS.reduce(string) do |str, flag|\n      # \"DEPRECATED(parameter foo):\"\n      str.gsub(/<p>\\s*#{flag}\\((.*)\\):/, %(<p><span class=\"flag #{FLAG_COLORS[flag]}\">#{flag} \\\\1</span> ))\n        # \"DEPRECATED:\"\n        .gsub(/<p>\\s*#{flag}:?/, %(<p><span class=\"flag #{FLAG_COLORS[flag]}\">#{flag}</span> ))\n    end\n  end\n\n  # Adds extra line break to flag keyword lines\n  #\n  # Guarantees that line is within its own paragraph element when parsed\n  def isolate_flag_lines(string)\n    flag_regexp = /^ ?(#{FLAGS.join('|')}):?/\n    String.build do |io|\n      string.each_line(chomp: false).join(io) do |line, io|\n        if line =~ flag_regexp\n          io << '\\n' << line\n        else\n          io << line\n        end\n      end\n    end\n  end\n\n  def build_flag_lines_from_annotations(context)\n    String.build do |io|\n      if anns = context.annotations(@program.deprecated_annotation)\n        anns.each do |ann|\n          io << \"DEPRECATED: #{DeprecatedAnnotation.from(ann).message}\\n\\n\"\n        end\n      end\n\n      if anns = context.annotations(@program.experimental_annotation)\n        anns.each do |ann|\n          io << \"EXPERIMENTAL: #{ExperimentalAnnotation.from(ann).message}\\n\\n\"\n        end\n      end\n\n      if context.is_a?(Method)\n        context.def.args.each do |arg|\n          if anns = arg.annotations(@program.deprecated_annotation)\n            anns.each do |ann|\n              io << \"DEPRECATED(parameter `#{arg.name}`): #{DeprecatedAnnotation.from(ann).message}\\n\\n\"\n            end\n          end\n\n          if anns = arg.annotations(@program.experimental_annotation)\n            anns.each do |ann|\n              io << \"EXPERIMENTAL(parameter `#{arg.name}`): #{ExperimentalAnnotation.from(ann).message}\\n\\n\"\n            end\n          end\n        end\n      end\n    end\n  end\n\n  def relative_location(node)\n    location = RelativeLocation.from(node, @base_dir)\n    return unless location\n    location.url = project_info.source_url(location)\n    location\n  end\n\n  def relative_locations(type)\n    locations = [] of RelativeLocation\n    type.locations.try &.each do |location|\n      location = RelativeLocation.from(location, @base_dir)\n      next unless location\n      filename = location.filename\n      next unless filename\n\n      location.url = project_info.source_url(location)\n\n      # Prevent identical link generation in the \"Defined in:\" section in the docs because of macros\n      next if locations.includes?(location)\n\n      same_file_location = locations.find { |loc| loc.filename == filename }\n      if same_file_location\n        location.show_line_number = true\n        same_file_location.show_line_number = true\n      end\n\n      locations << location\n    end\n    locations.sort\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/404.html",
    "content": "<h1 class=\"type-name\">\n  404 Not Found\n</h1>\n\n<p>\n  This page is unavailable in this version of the API docs.\n</p>\n\n<p>\n  You can use the sidebar to search for your page, or try a different\n  Crystal version.\n</p>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_head.html",
    "content": "<meta charset=\"utf-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<meta name=\"generator\" content=\"Crystal Docs <%= Crystal::VERSION %>\">\n<meta name=\"crystal_docs.project_version\" content=\"<%= project_info.version %>\">\n<meta name=\"crystal_docs.project_name\" content=\"<%= project_info.name %>\">\n<% if json_config_url = project_info.json_config_url %>\n  <meta name=\"crystal_docs.json_config_url\" content=\"<%= json_config_url %>\">\n<% end %>\n\n<%- if canonical_base_url = project_info.canonical_base_url -%>\n  <%- if current_type = self.current_type -%>\n    <link rel=\"canonical\" href=\"<%= ::Path.posix(canonical_base_url, current_type.path) %>\">\n  <%- else -%>\n    <link rel=\"canonical\" href=\"<%= canonical_base_url %>\">\n  <% end -%>\n<% end -%>\n\n<link href=\"<%= base_path %>css/style.css\" rel=\"stylesheet\" type=\"text/css\" />\n<script type=\"text/javascript\" src=\"<%= base_path %>js/doc.js\"></script>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_list_items.html",
    "content": "<ul>\n  <% types.each do |type| %>\n  <li class=\"<%= (type.program? || type.types.empty?) ? \"\" : (\"parent\" + (type.current?(current_type) || type.parents_of?(current_type) ? \" open\" : \"\")) %> <%= type.current?(current_type) ? \"current\" : \"\" %>\" data-id=\"<%= type.html_id %>\" data-name=\"<%= type.full_name.downcase %>\">\n      <a href=\"<%= type.path_from(current_type) %>\"><%= type.name %></a>\n      <% unless type.program? || type.types.empty? %>\n        <%= ListItemsTemplate.new(type.types, current_type) %>\n      <% end %>\n    </li>\n  <% end %>\n</ul>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_macros_inherited.html",
    "content": "<% macro_groups = macros.group_by { |m| m.name } %>\n<% unless macro_groups.empty? %>\n  <h3>Macros inherited from <%= ancestor.kind %> <code><%= ancestor.link_from(type) %></code></h3>\n  <% i = 0 %>\n  <% macro_groups.each do |k, v| %>\n    <a href=\"<%= type.path_to(ancestor) %><%= v[0].anchor %>\" class=\"tooltip\">\n      <span><%= v.map { |m| m.name + m.args_to_html(:highlight) } .join(\"<br/>\") %></span>\n    <%= k %></a><%= \", \" if i != macro_groups.size - 1 %>\n    <% i += 1 %>\n  <% end %>\n<% end %>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_method_detail.html",
    "content": "<% unless methods.empty? %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(title) %>\n    <%= title %>\n  </h2>\n  <% methods.each do |method| %>\n    <div class=\"entry-detail\" id=\"<%= method.html_id %>\">\n      <div class=\"signature\">\n        <%= method.abstract? ? \"abstract \" : \"\" %><%= method.visibility.try(&.+(\" \")) %>\n        <%= method.kind %><strong><%= method.name %></strong><%= method.real_name %><%= method.args_to_html %>\n\n        <a class=\"method-permalink\" href=\"<%= method.anchor %>\">#</a>\n      </div>\n      <% if doc = method.formatted_doc %>\n        <div class=\"doc\">\n          <% if doc_copied_from = method.doc_copied_from %>\n            <div class=\"doc-inherited\">\n              Description copied from <%= doc_copied_from.kind %> <%= doc_copied_from.link_from(method.type) %>\n            </div>\n          <% end %>\n          <%= doc %>\n        </div>\n      <% end %>\n      <br/>\n      <div>\n        <% if source_link = method.location.try(&.url) %>\n          [<a href=\"<%= source_link %>\" target=\"_blank\">View source</a>]\n        <% end %>\n      </div>\n    </div>\n  <% end %>\n<% end %>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_method_summary.html",
    "content": "<% unless methods.empty? %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(title) %>\n    <%= title %>\n  </h2>\n  <ul class=\"list-summary\">\n    <% methods.each do |method| %>\n      <li class=\"entry-summary\">\n        <a href=\"<%= method.anchor %>\" class=\"signature\"><strong><%= method.prefix %><%= method.name %></strong><%= method.real_name %><%= method.args_to_html(:highlight) %></a>\n        <% if summary = method.formatted_summary %>\n          <div class=\"summary\"><%= summary %></div>\n        <% end %>\n      </li>\n    <% end %>\n  </ul>\n<% end %>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_methods_inherited.html",
    "content": "<% method_groups = methods.group_by { |method| method.name } %>\n<% unless method_groups.empty? %>\n  <h3><%= label %> methods inherited from <%= ancestor.kind %> <code><%= ancestor.link_from(type) %></code></h3>\n  <% i = 0 %>\n  <% method_groups.each do |method_name, methods| %>\n    <a href=\"<%= type.path_to(ancestor) %><%= methods[0].anchor %>\" class=\"tooltip\">\n      <span><%= methods.map { |method| method.name + method.args_to_html(:highlight) } .join(\"<br/>\") %></span>\n    <%= method_name %></a><%= \", \" if i != method_groups.size - 1 %>\n    <% i += 1 %>\n  <% end %>\n<% end %>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_other_types.html",
    "content": "<% unless other_types.empty? %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(title) %>\n    <%= title %>\n  </h2>\n  <ul class=\"other-types-list\">\n    <% other_types.each_with_index do |other_type, i| %>\n      <li class=\"other-type\"><%= other_type.link_from(type) %></li>\n    <% end %>\n  </ul>\n<% end %>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/_sidebar.html",
    "content": "<div class=\"sidebar\">\n  <div class=\"sidebar-header\">\n    <div class=\"search-box\">\n      <input type=\"search\" class=\"search-input\" placeholder=\"Search...\" spellcheck=\"false\" aria-label=\"Search\">\n    </div>\n\n    <div class=\"project-summary\">\n      <h1 class=\"project-name\">\n        <a href=\"<%= current_type.try(&.path_to(\"\")) %>index.html\">\n          <%= project_info.name %>\n        </a>\n      </h1>\n\n      <span class=\"project-version\">\n        <%= project_info.version %>\n      </span>\n    </div>\n  </div>\n\n  <div class=\"search-results hidden\">\n    <ul class=\"search-list\"></ul>\n  </div>\n\n  <div class=\"types-list\">\n    <%= ListItemsTemplate.new(types, current_type) %>\n  </div>\n</div>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/css/style.css",
    "content": ":root {\n  color-scheme: light dark;\n}\n\nhtml, body {\n  background: #FFFFFF;\n  position: relative;\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n\nbody {\n  font-family: \"Avenir\", \"Tahoma\", \"Lucida Sans\", \"Lucida Grande\", Verdana, Arial, sans-serif;\n  color: #333;\n  line-height: 1.5;\n}\n\na {\n  color: #263F6C;\n}\n\na:visited {\n  color: #112750;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  margin: 35px 0 25px;\n  color: #444444;\n}\n\nh1.type-name {\n  color: #47266E;\n  margin: 20px 0 30px;\n  background-color: #F8F8F8;\n  padding: 10px 12px;\n  border: 1px solid #EBEBEB;\n  border-radius: 2px;\n}\n\nh2 {\n  border-bottom: 1px solid #E6E6E6;\n  padding-bottom: 5px;\n}\n\nbody {\n  display: flex;\n}\n\n.sidebar, .main-content {\n  overflow: auto;\n}\n\np, li {\n  max-width: 42em;\n}\n\n.methods-inherited {\n  max-width: 60em;\n}\n\n.sidebar {\n  width: 30em;\n  color: #F8F4FD;\n  background-color: #2E1052;\n  padding: 0 0 30px;\n  box-shadow: inset -3px 0 4px rgba(0,0,0,.35);\n  line-height: 1.2;\n  z-index: 0;\n}\n\n.sidebar .search-box {\n  padding: 13px 9px;\n}\n\n.sidebar input {\n  display: block;\n  box-sizing: border-box;\n  margin: 0;\n  padding: 5px;\n  font: inherit;\n  font-family: inherit;\n  line-height: 1.2;\n  width: 100%;\n  border: 0;\n  outline: 0;\n  border-radius: 2px;\n  box-shadow: 0px 3px 5px rgba(0,0,0,.25);\n  transition: box-shadow .12s;\n}\n\n.sidebar input:focus {\n  box-shadow: 0px 5px 6px rgba(0,0,0,.5);\n}\n\n.sidebar input::-webkit-input-placeholder { /* Chrome/Opera/Safari */\n  color: #757575;\n  font-size: 14px;\n  text-indent: 2px;\n}\n\n.sidebar input::-moz-placeholder { /* Firefox 19+ */\n  color: #757575;\n  font-size: 14px;\n  text-indent: 2px;\n}\n\n.sidebar input:-ms-input-placeholder { /* IE 10+ */\n  color: #757575;\n  font-size: 14px;\n  text-indent: 2px;\n}\n\n.sidebar input:-moz-placeholder { /* Firefox 18- */\n  color: #757575;\n  font-size: 14px;\n  text-indent: 2px;\n}\n\n.project-summary {\n  padding: 9px 15px 30px 30px;\n}\n\n.project-name {\n  font-size: 1.4rem;\n  margin: 0;\n  color: #f4f4f4;\n  font-weight: 600;\n}\n\n.project-version {\n  margin-top: 5px;\n  display: inline-block;\n  position: relative;\n}\n\n.project-version > form::after {\n  position: absolute;\n  right: 0;\n  top: 0;\n  content: \"\\25BC\";\n  font-size: .6em;\n  line-height: 1.2rem;\n  z-index: -1;\n}\n\n.project-versions-nav {\n  cursor: pointer;\n  margin: 0;\n  padding: 0 .9em 0 0;\n  border: none;\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  appearance: none;\n  background-color: transparent;\n  color: inherit;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n.project-versions-nav:focus {\n  outline: none;\n}\n\n.project-versions-nav > option {\n  color: initial;\n}\n\n.sidebar ul {\n  margin: 0;\n  padding: 0;\n  list-style: none outside;\n}\n\n.sidebar li {\n  display: block;\n  position: relative;\n}\n\n.types-list li.hide {\n  display: none;\n}\n\n.sidebar a {\n  text-decoration: none;\n  color: inherit;\n  transition: color .14s;\n}\n.types-list a {\n  display: block;\n  padding: 5px 15px 5px 30px;\n}\n\n.types-list {\n  display: block;\n}\n\n.sidebar a:focus {\n  outline: 1px solid #D1B7F1;\n}\n\n.types-list a {\n  padding: 5px 15px 5px 30px;\n}\n\n.sidebar .current > a,\n.sidebar a:hover {\n  color: #866BA6;\n}\n\n.types-list li ul {\n  overflow: hidden;\n  height: 0;\n  max-height: 0;\n  transition: 1s ease-in-out;\n}\n\n.types-list li.parent {\n  padding-left: 30px;\n}\n\n.types-list li.parent::before {\n  box-sizing: border-box;\n  content: \"▼\";\n  display: block;\n  width: 30px;\n  height: 30px;\n  position: absolute;\n  top: 0;\n  left: 0;\n  text-align: center;\n  color: white;\n  font-size: 8px;\n  line-height: 30px;\n  transform: rotateZ(-90deg);\n  cursor: pointer;\n  transition: .2s linear;\n}\n\n\n.types-list li.parent > a {\n  padding-left: 0;\n}\n\n.types-list li.parent.open::before {\n  transform: rotateZ(0);\n}\n\n.types-list li.open > ul {\n  height: auto;\n  max-height: 1000em;\n}\n\n.main-content {\n  padding: 0 30px 30px 30px;\n  width: 100%;\n}\n\n.kind {\n  font-size: 60%;\n  color: #866BA6;\n}\n\n.superclass-hierarchy {\n  margin: -15px 0 30px 0;\n  padding: 0;\n  list-style: none outside;\n  font-size: 80%;\n}\n\n.superclass-hierarchy .superclass {\n  display: inline-block;\n  margin: 0 7px 0 0;\n  padding: 0;\n}\n\n.superclass-hierarchy .superclass + .superclass::before {\n  content: \"<\";\n  margin-right: 7px;\n}\n\n.other-types-list li {\n  display: inline-block;\n}\n\n.other-types-list,\n.list-summary {\n  margin: 0 0 30px 0;\n  padding: 0;\n  list-style: none outside;\n}\n\n.entry-const {\n  font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;\n}\n\n.entry-const code {\n  white-space: pre-wrap;\n}\n\n.entry-summary {\n  padding-bottom: 4px;\n}\n\n.superclass-hierarchy .superclass a,\n.other-type a,\n.entry-summary .signature {\n  padding: 4px 8px;\n  margin-bottom: 4px;\n  display: inline-block;\n  background-color: #f8f8f8;\n  color: #47266E;\n  border: 1px solid #f0f0f0;\n  text-decoration: none;\n  border-radius: 3px;\n  font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;\n  transition: background .15s, border-color .15s;\n}\n\n.superclass-hierarchy .superclass a:hover,\n.other-type a:hover,\n.entry-summary .signature:hover {\n  background: #D5CAE3;\n  border-color: #624288;\n}\n\n.entry-summary .summary {\n  padding-left: 32px;\n}\n\n.entry-summary .summary p {\n  margin: 12px 0 16px;\n}\n\n.entry-summary a {\n  text-decoration: none;\n}\n\n.entry-detail {\n  padding: 30px 0;\n}\n\n.entry-detail .signature {\n  position: relative;\n  padding: 5px 15px;\n  margin-bottom: 10px;\n  display: block;\n  border-radius: 5px;\n  background-color: #f8f8f8;\n  color: #47266E;\n  border: 1px solid #f0f0f0;\n  font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;\n  transition: .2s ease-in-out;\n}\n\n.entry-detail:target .signature {\n  background-color: #D5CAE3;\n  border: 1px solid #624288;\n}\n\n.entry-detail .signature .method-permalink {\n  position: absolute;\n  top: 0;\n  left: -35px;\n  padding: 5px 15px;\n  text-decoration: none;\n  font-weight: bold;\n  color: #624288;\n  opacity: .4;\n  transition: opacity .2s;\n}\n\n.entry-detail .signature .method-permalink:hover {\n  opacity: 1;\n}\n\n.entry-detail:target .signature .method-permalink {\n  opacity: 1;\n}\n\n.methods-inherited {\n  padding-right: 10%;\n  line-height: 1.5em;\n}\n\n.methods-inherited h3 {\n  margin-bottom: 4px;\n}\n\n.methods-inherited a {\n  display: inline-block;\n  text-decoration: none;\n  color: #47266E;\n}\n\n.methods-inherited a:hover {\n  text-decoration: underline;\n  color: #6C518B;\n}\n\n.methods-inherited .tooltip>span {\n  background: #D5CAE3;\n  padding: 4px 8px;\n  border-radius: 3px;\n  margin: -4px -8px;\n}\n\n.methods-inherited .tooltip * {\n  color: #47266E;\n}\n\npre {\n  padding: 10px 20px;\n  margin-top: 4px;\n  border-radius: 3px;\n  line-height: 1.45;\n  overflow: auto;\n  color: #333;\n  background: #fdfdfd;\n  font-size: 14px;\n  border: 1px solid #eee;\n}\n\ncode {\n  font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;\n}\n\n:not(pre) > code {\n  background-color: rgba(40,35,30,0.05);\n  padding: 0.2em 0.4em;\n  font-size: 85%;\n  border-radius: 3px;\n}\n\nspan.flag {\n  padding: 2px 4px 1px;\n  border-radius: 3px;\n  margin-right: 3px;\n  font-size: 11px;\n  border: 1px solid transparent;\n}\n\nspan.flag.orange {\n  background-color: #EE8737;\n  color: #FCEBDD;\n  border-color: #EB7317;\n}\n\nspan.flag.yellow {\n  background-color: #E4B91C;\n  color: #FCF8E8;\n  border-color: #B69115;\n}\n\nspan.flag.green {\n  background-color: #469C14;\n  color: #E2F9D3;\n  border-color: #34700E;\n}\n\nspan.flag.red {\n  background-color: #BF1919;\n  color: #F9ECEC;\n  border-color: #822C2C;\n}\n\nspan.flag.purple {\n  background-color: #2E1052;\n  color: #ECE1F9;\n  border-color: #1F0B37;\n}\n\nspan.flag.lime {\n  background-color: #a3ff00;\n  color: #222222;\n  border-color: #00ff1e;\n}\n\n.tooltip>span {\n  position: absolute;\n  opacity: 0;\n  display: none;\n  pointer-events: none;\n}\n\n.tooltip:hover>span {\n  display: inline-block;\n  opacity: 1;\n}\n\n.c {\n  color: #969896;\n}\n\n.n {\n  color: #0086b3;\n}\n\n.t {\n  color: #0086b3;\n}\n\n.s {\n  color: #183691;\n}\n\n.i {\n  color: #7f5030;\n}\n\n.k {\n  color: #a71d5d;\n}\n\n.o {\n  color: #a71d5d;\n}\n\n.m {\n  color: #795da3;\n}\n\n.hidden {\n  display: none;\n}\n.search-results {\n  font-size: 90%;\n  line-height: 1.3;\n}\n\n.search-results mark {\n  color: inherit;\n  background: transparent;\n  font-weight: bold;\n}\n.search-result {\n  padding: 5px 8px 5px 5px;\n  cursor: pointer;\n  border-left: 5px solid transparent;\n  transform: translateX(-3px);\n  transition: all .2s, background-color 0s, border .02s;\n  min-height: 3.2em;\n}\n.search-result.current {\n  border-left-color: #ddd;\n  background-color: rgba(200,200,200,0.4);\n  transform: translateX(0);\n  transition: all .2s, background-color .5s, border 0s;\n}\n.search-result.current:hover,\n.search-result.current:focus {\n  border-left-color: #866BA6;\n}\n.search-result:not(.current):nth-child(2n) {\n  background-color: rgba(255,255,255,.06);\n}\n.search-result__title {\n  font-size: 105%;\n  word-break: break-all;\n  line-height: 1.1;\n  padding: 3px 0;\n}\n.search-result__title strong {\n  font-weight: normal;\n}\n.search-results .search-result__title > a {\n  padding: 0;\n  display: block;\n}\n.search-result__title > a > .args {\n  color: #dddddd;\n  font-weight: 300;\n  transition: inherit;\n  font-size: 88%;\n  line-height: 1.2;\n  letter-spacing: -.02em;\n}\n.search-result__title > a > .args * {\n  color: inherit;\n}\n\n.search-result a,\n.search-result a:hover {\n  color: inherit;\n}\n.search-result:not(.current):hover .search-result__title > a,\n.search-result:not(.current):focus .search-result__title > a,\n.search-result__title > a:focus {\n  color: #866BA6;\n}\n.search-result:not(.current):hover .args,\n.search-result:not(.current):focus .args {\n  color: #6a5a7d;\n}\n\n.search-result__type {\n  color: #e8e8e8;\n  font-weight: 300;\n}\n.search-result__doc {\n  color: #bbbbbb;\n  font-size: 90%;\n}\n.search-result__doc p {\n  margin: 0;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  -webkit-box-orient: vertical;\n  -webkit-line-clamp: 2;\n  overflow: hidden;\n  line-height: 1.2em;\n  max-height: 2.4em;\n}\n\n.js-modal-visible .modal-background {\n  display: flex;\n}\n.main-content {\n  position: relative;\n}\n.modal-background {\n  position: absolute;\n  display: none;\n  height: 100%;\n  width: 100%;\n  background: rgba(120,120,120,.4);\n  z-index: 100;\n  align-items: center;\n  justify-content: center;\n}\n.usage-modal {\n  max-width: 90%;\n  background: #fff;\n  border: 2px solid #ccc;\n  border-radius: 9px;\n  padding: 5px 15px 20px;\n  min-width: 50%;\n  color: #555;\n  position: relative;\n  transform: scale(.5);\n  transition: transform 200ms;\n}\n.js-modal-visible .usage-modal {\n  transform: scale(1);\n}\n.usage-modal > .close-button {\n  position: absolute;\n  right: 15px;\n  top: 8px;\n  color: #aaa;\n  font-size: 27px;\n  cursor: pointer;\n}\n.usage-modal > .close-button:hover {\n  text-shadow: 2px 2px 2px #ccc;\n  color: #999;\n}\n.modal-title {\n  margin: 0;\n  text-align: center;\n  font-weight: normal;\n  color: #666;\n  border-bottom: 2px solid #ddd;\n  padding: 10px;\n}\n.usage-list {\n  padding: 0;\n  margin: 13px;\n}\n.usage-list > li {\n  padding: 5px 2px;\n  overflow: auto;\n  padding-left: 100px;\n  min-width: 12em;\n}\n.usage-modal kbd {\n  background: #eee;\n  border: 1px solid #ccc;\n  border-bottom-width: 2px;\n  border-radius: 3px;\n  padding: 3px 8px;\n  font-family: monospace;\n  margin-right: 2px;\n  display: inline-block;\n}\n.usage-key {\n  float: left;\n  clear: left;\n  margin-left: -100px;\n  margin-right: 12px;\n}\n.doc-inherited {\n  font-weight: bold;\n}\n\n.anchor {\n  float: left;\n  padding-right: 4px;\n  margin-left: -20px;\n}\n\n.main-content .anchor .octicon-link {\n  width: 16px;\n  height: 16px;\n}\n\n.main-content .anchor:focus {\n  outline: none\n}\n\n.main-content h1:hover .anchor,\n.main-content h2:hover .anchor,\n.main-content h3:hover .anchor,\n.main-content h4:hover .anchor,\n.main-content h5:hover .anchor,\n.main-content h6:hover .anchor {\n  text-decoration: none\n}\n\n.main-content h1 .octicon-link,\n.main-content h2 .octicon-link,\n.main-content h3 .octicon-link,\n.main-content h4 .octicon-link,\n.main-content h5 .octicon-link,\n.main-content h6 .octicon-link {\n  visibility: hidden\n}\n\n.main-content h1:hover .anchor .octicon-link,\n.main-content h2:hover .anchor .octicon-link,\n.main-content h3:hover .anchor .octicon-link,\n.main-content h4:hover .anchor .octicon-link,\n.main-content h5:hover .anchor .octicon-link,\n.main-content h6:hover .anchor .octicon-link {\n  visibility: visible\n}\n\nimg {\n  max-width: 100%;\n}\n\ntable {\n  font-size: 14px;\n  display: block;\n  max-width: -moz-fit-content;\n  max-width: fit-content;\n  overflow-x: auto;\n  white-space: nowrap;\n  background: #fdfdfd;\n  text-align: center;\n  border: 1px solid #eee;\n  border-collapse: collapse;\n  padding: 0px 5px 0px 5px;\n}\n\ntable th {\n  padding: 10px;\n  letter-spacing: 1px;\n  border-bottom: 1px solid #eee;\n}\n\ntable td {\n  padding: 10px;\n}\n\n#sidebar-btn {\n  height: 32px;\n  width: 32px;\n}\n\n#sidebar-btn-label {\n  height: 2em;\n  width: 2em;\n}\n\n#sidebar-btn, #sidebar-btn-label {\n  display: none;\n  margin: .7rem;\n  appearance: none;\n  color: black;\n  cursor: pointer;\n}\n\n@media only screen and (max-width: 635px) {\n  .sidebar, .main-content {\n    /* svg size + vertical margin - .search-box padding-top */\n    padding-top: calc(2em + 2 * 0.7rem - 13px);\n  }\n\n  #sidebar-btn, #sidebar-btn-label {\n    display: block;\n    position: absolute;\n    z-index: 50;\n    transition-duration: 200ms;\n    left: 0;\n  }\n  \n  #sidebar-btn:not(:checked) ~ #sidebar-btn-label > .close,\n  #sidebar-btn:checked ~ #sidebar-btn-label > .open,\n  #sidebar-btn:checked ~ .main-content {\n    display: none;\n  }\n\n  #sidebar-btn:checked {\n    left: calc(100% - 32px - (2 * 0.7rem));\n  }\n\n  #sidebar-btn:checked ~ #sidebar-btn-label {\n    color: white;\n    /* 100% - svg size - horizontal margin */\n    left: calc(100% - 2em - (2 * 0.7rem));\n  }\n\n  #sidebar-btn~.sidebar {\n    width: 0%;\n  }\n\n  #sidebar-btn:checked~.sidebar {\n    visibility: visible;\n    width: 100%;\n  }\n\n  .sidebar {\n    transition-duration: 200ms;\n    max-width: 100vw;\n    visibility: hidden;\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  html, body {\n    background: #1b1b1b;\n  }\n\n  body {\n    color: white;\n  }\n\n  a {\n    color: #8cb4ff;\n  }\n\n  .main-content a:visited {\n    color: #5f8de3;\n  }\n\n  h1, h2, h3, h4, h5, h6 {\n    color: white;\n  }\n\n  h1.type-name {\n    color: white;\n    background-color: #202020;\n    border: 1px solid #353535;\n  }\n\n  .project-versions-nav > option {\n    background-color: #222;\n  }\n\n  .superclass-hierarchy .superclass a,\n  .superclass-hierarchy .superclass a:visited,\n  .other-type a,\n  .other-type a:visited,\n  .entry-summary .signature,\n  .entry-summary a:visited {\n    background-color: #202020;\n    color: white;\n    border: 1px solid #353535;\n  }\n\n  .superclass-hierarchy .superclass a:hover,\n  .other-type a:hover,\n  .entry-summary .signature:hover {\n    background: #443d4d;\n    border-color: #b092d4;\n  }\n\n  .kind {\n    color: #b092d4;\n  }\n\n  .n {\n    color: #00ade6;\n  }\n\n  .t {\n    color: #00ade6;\n  }\n\n  .k {\n    color: #ff66ae;\n  }\n\n  .o {\n    color: #ff66ae;\n  }\n\n  .s {\n    color: #7799ff;\n  }\n\n  .i {\n    color: #b38668;\n  }\n\n  .m {\n    color: #b9a5d6;\n  }\n\n  .c {\n    color: #a1a1a1;\n  }\n\n  .methods-inherited a, .methods-inherited a:visited {\n    color: #B290D9;\n  }\n\n  .methods-inherited a:hover {\n    color: #D4B7F4;\n  }\n\n  .methods-inherited .tooltip>span {\n    background: #443d4d;\n  }\n\n  .methods-inherited .tooltip * {\n    color: white;\n  }\n\n  .entry-detail:target .signature {\n    background-color: #443d4d;\n    border: 1px solid #b092d4;\n  }\n\n  .entry-detail .signature {\n    background-color: #202020;\n    color: white;\n    border: 1px solid #353535;\n  }\n\n  .entry-detail .signature .method-permalink {\n    color: #b092d4;\n  }\n\n  :not(pre)>code {\n    background-color: #202020;\n  }\n\n  span.flag.purple {\n    background-color: #443d4d;\n    color: #ECE1F9;\n    border-color: #b092d4;\n  }\n\n  .sidebar input::-webkit-input-placeholder { /* Chrome/Opera/Safari */\n    color: white;\n  }\n  \n  .sidebar input::-moz-placeholder { /* Firefox 19+ */\n    color: white;\n  }\n  \n  .sidebar input:-ms-input-placeholder { /* IE 10+ */\n    color: white;\n  }\n  \n  .sidebar input:-moz-placeholder { /* Firefox 18- */\n    color: white;\n  }\n\n  pre,\n  table {\n    color: white;\n    background: #202020;\n    border: 1px solid #353535;\n  }\n\n  table th {\n    border-bottom: 1px solid #353535;\n  }\n\n  #sidebar-btn, #sidebar-btn-label {\n    color: white;\n  }\n}\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/js/_navigator.js",
    "content": "Navigator = function(sidebar, searchInput, list, leaveSearchScope){\n  this.list = list;\n  var self = this;\n\n  var performingSearch = false;\n\n  document.addEventListener('CrystalDocs:searchStarted', function(){\n    performingSearch = true;\n  });\n  document.addEventListener('CrystalDocs:searchDebounceStarted', function(){\n    performingSearch = true;\n  });\n  document.addEventListener('CrystalDocs:searchPerformed', function(){\n    performingSearch = false;\n  });\n  document.addEventListener('CrystalDocs:searchDebounceStopped', function(event){\n    performingSearch = false;\n  });\n\n  function delayWhileSearching(callback) {\n    if(performingSearch){\n      document.addEventListener('CrystalDocs:searchPerformed', function listener(){\n        document.removeEventListener('CrystalDocs:searchPerformed', listener);\n\n        // add some delay to let search results display kick in\n        setTimeout(callback, 100);\n      });\n    }else{\n      callback();\n    }\n  }\n\n  function clearMoveTimeout() {\n    clearTimeout(self.moveTimeout);\n    self.moveTimeout = null;\n  }\n\n  function startMoveTimeout(upwards){\n    /*if(self.moveTimeout) {\n      clearMoveTimeout();\n    }\n\n    var go = function() {\n      if (!self.moveTimeout) return;\n      self.move(upwards);\n      self.moveTimeout = setTimeout(go, 600);\n    };\n    self.moveTimeout = setTimeout(go, 800);*/\n  }\n\n  function scrollCenter(element) {\n    var rect = element.getBoundingClientRect();\n    var middle = sidebar.clientHeight / 2;\n    sidebar.scrollTop += rect.top + rect.height / 2 - middle;\n  }\n\n  var move = this.move = function(upwards){\n    if(!this.current){\n      this.highlightFirst();\n      return true;\n    }\n    var next = upwards ? this.current.previousElementSibling : this.current.nextElementSibling;\n    if(next && next.classList) {\n      this.highlight(next);\n      scrollCenter(next);\n      return true;\n    }\n    return false;\n  };\n\n  this.moveRight = function(){\n  };\n  this.moveLeft = function(){\n  };\n\n  this.highlight = function(elem) {\n    if(!elem){\n      return;\n    }\n    this.removeHighlight();\n\n    this.current = elem;\n    this.current.classList.add(\"current\");\n  };\n\n  this.highlightFirst = function(){\n    this.highlight(this.list.querySelector('li:first-child'));\n  };\n\n  this.removeHighlight = function() {\n    if(this.current){\n      this.current.classList.remove(\"current\");\n    }\n    this.current = null;\n  }\n\n  this.openSelectedResult = function() {\n    if(this.current) {\n      this.current.click();\n    }\n  }\n\n  this.focus = function() {\n    searchInput.focus();\n    searchInput.select();\n    this.highlightFirst();\n  }\n\n  function handleKeyUp(event) {\n    switch(event.key) {\n      case \"ArrowUp\":\n      case \"ArrowDown\":\n      case \"i\":\n      case \"j\":\n      case \"k\":\n      case \"l\":\n      case \"c\":\n      case \"h\":\n      case \"t\":\n      case \"n\":\n      event.stopPropagation();\n      clearMoveTimeout();\n    }\n  }\n\n  function handleKeyDown(event) {\n    switch(event.key) {\n      case \"Enter\":\n        event.stopPropagation();\n        event.preventDefault();\n        leaveSearchScope();\n        self.openSelectedResult();\n        break;\n      case \"Escape\":\n        event.stopPropagation();\n        event.preventDefault();\n        leaveSearchScope();\n        break;\n      case \"j\":\n      case \"c\":\n      case \"ArrowUp\":\n        if(event.ctrlKey || event.key == \"ArrowUp\") {\n          event.stopPropagation();\n          self.move(true);\n          startMoveTimeout(true);\n        }\n        break;\n      case \"k\":\n      case \"h\":\n      case \"ArrowDown\":\n        if(event.ctrlKey || event.key == \"ArrowDown\") {\n          event.stopPropagation();\n          self.move(false);\n          startMoveTimeout(false);\n        }\n        break;\n      case \"k\":\n      case \"t\":\n      case \"ArrowLeft\":\n        if(event.ctrlKey || event.key == \"ArrowLeft\") {\n          event.stopPropagation();\n          self.moveLeft();\n        }\n        break;\n      case \"l\":\n      case \"n\":\n      case \"ArrowRight\":\n        if(event.ctrlKey || event.key == \"ArrowRight\") {\n          event.stopPropagation();\n          self.moveRight();\n        }\n        break;\n    }\n  }\n\n  function handleInputKeyUp(event) {\n    switch(event.key) {\n      case \"ArrowUp\":\n      case \"ArrowDown\":\n      event.stopPropagation();\n      event.preventDefault();\n      clearMoveTimeout();\n    }\n  }\n\n  function handleInputKeyDown(event) {\n    switch(event.key) {\n      case \"Enter\":\n        event.stopPropagation();\n        event.preventDefault();\n        delayWhileSearching(function(){\n          self.openSelectedResult();\n          leaveSearchScope();\n        });\n        break;\n      case \"Escape\":\n        event.stopPropagation();\n        event.preventDefault();\n        // remove focus from search input\n        leaveSearchScope();\n        sidebar.focus();\n        break;\n      case \"ArrowUp\":\n        event.stopPropagation();\n        event.preventDefault();\n        self.move(true);\n        startMoveTimeout(true);\n        break;\n\n      case \"ArrowDown\":\n        event.stopPropagation();\n        event.preventDefault();\n        self.move(false);\n        startMoveTimeout(false);\n        break;\n    }\n  }\n\n  sidebar.tabIndex = 100; // set tabIndex to enable keylistener\n  sidebar.addEventListener('keyup', function(event) {\n    handleKeyUp(event);\n  });\n  sidebar.addEventListener('keydown', function(event) {\n    handleKeyDown(event);\n  });\n  searchInput.addEventListener('keydown', function(event) {\n    handleInputKeyDown(event);\n  });\n  searchInput.addEventListener('keyup', function(event) {\n    handleInputKeyUp(event);\n  });\n  this.move();\n};\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/js/_search.js",
    "content": "CrystalDocs.searchIndex = (CrystalDocs.searchIndex || false);\nCrystalDocs.MAX_RESULTS_DISPLAY = 140;\n\nCrystalDocs.runQuery = function(query) {\n  function searchType(type, query, results) {\n    var matches = [];\n    var matchedFields = [];\n    var name = type.full_name;\n    var i = name.lastIndexOf(\"::\");\n    if (i > 0) {\n      name = name.substring(i + 2);\n    }\n    var nameMatches = query.matches(name);\n    if (nameMatches){\n      matches = matches.concat(nameMatches);\n      matchedFields.push(\"name\");\n    }\n\n    var namespaceMatches = query.matchesNamespace(type.full_name);\n    if(namespaceMatches){\n      matches = matches.concat(namespaceMatches);\n      matchedFields.push(\"name\");\n    }\n\n    var docMatches = query.matches(type.doc);\n    if(docMatches){\n      matches = matches.concat(docMatches);\n      matchedFields.push(\"doc\");\n    }\n    if (matches.length > 0) {\n      results.push({\n        id: type.html_id,\n        result_type: \"type\",\n        kind: type.kind,\n        name: name,\n        full_name: type.full_name,\n        href: type.path,\n        summary: type.summary,\n        matched_fields: matchedFields,\n        matched_terms: matches\n      });\n    }\n\n    if (type.instance_methods) {\n      type.instance_methods.forEach(function(method) {\n        searchMethod(method, type, \"instance_method\", query, results);\n      })\n    }\n    if (type.class_methods) {\n      type.class_methods.forEach(function(method) {\n        searchMethod(method, type, \"class_method\", query, results);\n      })\n    }\n    if (type.constructors) {\n      type.constructors.forEach(function(constructor) {\n        searchMethod(constructor, type, \"constructor\", query, results);\n      })\n    }\n    if (type.macros) {\n      type.macros.forEach(function(macro) {\n        searchMethod(macro, type, \"macro\", query, results);\n      })\n    }\n    if (type.constants) {\n      type.constants.forEach(function(constant){\n        searchConstant(constant, type, query, results);\n      });\n    }\n    if (type.types) {\n      type.types.forEach(function(subtype){\n        searchType(subtype, query, results);\n      });\n    }\n  };\n\n  function searchMethod(method, type, kind, query, results) {\n    var matches = [];\n    var matchedFields = [];\n    var nameMatches = query.matchesMethod(method.name, kind, type);\n    if (nameMatches){\n      matches = matches.concat(nameMatches);\n      matchedFields.push(\"name\");\n    }\n\n    if (method.args) {\n      method.args.forEach(function(arg){\n        var argMatches = query.matches(arg.external_name);\n        if (argMatches) {\n          matches = matches.concat(argMatches);\n          matchedFields.push(\"args\");\n        }\n      });\n    }\n\n    var docMatches = query.matches(type.doc);\n    if(docMatches){\n      matches = matches.concat(docMatches);\n      matchedFields.push(\"doc\");\n    }\n\n    if (matches.length > 0) {\n      var typeMatches = query.matches(type.full_name);\n      if (typeMatches) {\n        matchedFields.push(\"type\");\n        matches = matches.concat(typeMatches);\n      }\n      results.push({\n        id: method.html_id,\n        type: type.full_name,\n        result_type: kind,\n        name: method.name,\n        full_name: type.full_name + \"#\" + method.name,\n        args_string: method.args_string,\n        summary: method.summary,\n        href: type.path + \"#\" + method.html_id,\n        matched_fields: matchedFields,\n        matched_terms: matches\n      });\n    }\n  }\n\n  function searchConstant(constant, type, query, results) {\n    var matches = [];\n    var matchedFields = [];\n    var nameMatches = query.matches(constant.name);\n    if (nameMatches){\n      matches = matches.concat(nameMatches);\n      matchedFields.push(\"name\");\n    }\n    var docMatches = query.matches(constant.doc);\n    if(docMatches){\n      matches = matches.concat(docMatches);\n      matchedFields.push(\"doc\");\n    }\n    if (matches.length > 0) {\n      var typeMatches = query.matches(type.full_name);\n      if (typeMatches) {\n        matchedFields.push(\"type\");\n        matches = matches.concat(typeMatches);\n      }\n      results.push({\n        id: constant.id,\n        type: type.full_name,\n        result_type: \"constant\",\n        name: constant.name,\n        full_name: type.full_name + \"#\" + constant.name,\n        value: constant.value,\n        summary: constant.summary,\n        href: type.path + \"#\" + constant.id,\n        matched_fields: matchedFields,\n        matched_terms: matches\n      });\n    }\n  }\n\n  var results = [];\n  searchType(CrystalDocs.searchIndex.program, query, results);\n  return results;\n};\n\nCrystalDocs.rankResults = function(results, query) {\n  function uniqueArray(ar) {\n    var j = {};\n\n    ar.forEach(function(v) {\n      j[v + \"::\" + typeof v] = v;\n    });\n\n    return Object.keys(j).map(function(v) {\n      return j[v];\n    });\n  }\n\n  results = results.sort(function(a, b) {\n    var matchedTermsDiff = uniqueArray(b.matched_terms).length - uniqueArray(a.matched_terms).length;\n    var aHasDocs = b.matched_fields.includes(\"doc\");\n    var bHasDocs = b.matched_fields.includes(\"doc\");\n\n    var aOnlyDocs = aHasDocs && a.matched_fields.length == 1;\n    var bOnlyDocs = bHasDocs && b.matched_fields.length == 1;\n\n    if (a.result_type == \"type\" && b.result_type != \"type\" && !aOnlyDocs) {\n      if(CrystalDocs.DEBUG) { console.log(\"a is type b not\"); }\n      return -1;\n    } else if (b.result_type == \"type\" && a.result_type != \"type\" && !bOnlyDocs) {\n      if(CrystalDocs.DEBUG) { console.log(\"b is type, a not\"); }\n      return 1;\n    }\n    if (a.matched_fields.includes(\"name\")) {\n      if (b.matched_fields.includes(\"name\")) {\n        var a_name = (CrystalDocs.prefixForType(a.result_type) || \"\") + ((a.result_type == \"type\") ? a.full_name : a.name);\n        var b_name = (CrystalDocs.prefixForType(b.result_type) || \"\") + ((b.result_type == \"type\") ? b.full_name : b.name);\n        a_name = a_name.toLowerCase();\n        b_name = b_name.toLowerCase();\n        for(var i = 0; i < query.normalizedTerms.length; i++) {\n          var term = query.terms[i].replace(/^::?|::?$/, \"\");\n          var a_orig_index = a_name.indexOf(term);\n          var b_orig_index = b_name.indexOf(term);\n          if(CrystalDocs.DEBUG) { console.log(\"term: \" + term + \" a: \" + a_name + \" b: \" + b_name); }\n          if(CrystalDocs.DEBUG) { console.log(a_orig_index, b_orig_index, a_orig_index - b_orig_index); }\n          if (a_orig_index >= 0) {\n            if (b_orig_index >= 0) {\n              if(CrystalDocs.DEBUG) { console.log(\"both have exact match\", a_orig_index > b_orig_index ? -1 : 1); }\n              if(a_orig_index != b_orig_index) {\n                if(CrystalDocs.DEBUG) { console.log(\"both have exact match at different positions\", a_orig_index > b_orig_index ? 1 : -1); }\n                return a_orig_index > b_orig_index ? 1 : -1;\n              }\n            } else {\n              if(CrystalDocs.DEBUG) { console.log(\"a has exact match, b not\"); }\n              return -1;\n            }\n          } else if (b_orig_index >= 0) {\n            if(CrystalDocs.DEBUG) { console.log(\"b has exact match, a not\"); }\n            return 1;\n          }\n        }\n      } else {\n        if(CrystalDocs.DEBUG) { console.log(\"a has match in name, b not\"); }\n        return -1;\n      }\n    } else if (\n      !a.matched_fields.includes(\"name\") &&\n      b.matched_fields.includes(\"name\")\n    ) {\n      return 1;\n    }\n\n    if (matchedTermsDiff != 0 || (aHasDocs != bHasDocs)) {\n      if(CrystalDocs.DEBUG) { console.log(\"matchedTermsDiff: \" + matchedTermsDiff, aHasDocs, bHasDocs); }\n      return matchedTermsDiff;\n    }\n\n    var matchedFieldsDiff = b.matched_fields.length - a.matched_fields.length;\n    if (matchedFieldsDiff != 0) {\n      if(CrystalDocs.DEBUG) { console.log(\"matched to different number of fields: \" + matchedFieldsDiff); }\n      return matchedFieldsDiff > 0 ? 1 : -1;\n    }\n\n    var nameCompare = a.name.localeCompare(b.name);\n    if(nameCompare != 0){\n      if(CrystalDocs.DEBUG) { console.log(\"nameCompare resulted in: \" + a.name + \"<=>\" + b.name + \": \" + nameCompare); }\n      return nameCompare > 0 ? 1 : -1;\n    }\n\n    if(a.matched_fields.includes(\"args\") && b.matched_fields.includes(\"args\")) {\n      for(var i = 0; i < query.terms.length; i++) {\n        var term = query.terms[i];\n        var aIndex = a.args_string.indexOf(term);\n        var bIndex = b.args_string.indexOf(term);\n        if(CrystalDocs.DEBUG) { console.log(\"index of \" + term + \" in args_string: \" + aIndex + \" - \" + bIndex); }\n        if(aIndex >= 0){\n          if(bIndex >= 0){\n            if(aIndex != bIndex){\n              return aIndex > bIndex ? 1 : -1;\n            }\n          }else{\n            return -1;\n          }\n        }else if(bIndex >= 0) {\n          return 1;\n        }\n      }\n    }\n\n    return 0;\n  });\n\n  if (results.length > 1) {\n    // if we have more than two search terms, only include results with the most matches\n    var bestMatchedTerms = uniqueArray(results[0].matched_terms).length;\n\n    results = results.filter(function(result) {\n      return uniqueArray(result.matched_terms).length + 1 >= bestMatchedTerms;\n    });\n  }\n  return results;\n};\n\nCrystalDocs.prefixForType = function(type) {\n  switch (type) {\n    case \"instance_method\":\n      return \"#\";\n\n    case \"class_method\":\n    case \"macro\":\n    case \"constructor\":\n      return \".\";\n\n    default:\n      return false;\n  }\n};\n\nCrystalDocs.displaySearchResults = function(results, query) {\n  function sanitize(html){\n    return html.replace(/<(?!\\/?code)[^>]+>/g, \"\");\n  }\n\n  // limit results\n  if (results.length > CrystalDocs.MAX_RESULTS_DISPLAY) {\n    results = results.slice(0, CrystalDocs.MAX_RESULTS_DISPLAY);\n  }\n\n  var $frag = document.createDocumentFragment();\n  var $resultsElem = document.querySelector(\".search-list\");\n  $resultsElem.innerHTML = \"<!--\" + JSON.stringify(query) + \"-->\";\n\n  results.forEach(function(result, i) {\n    var url = CrystalDocs.base_path + result.href;\n    var type = false;\n\n    var title = query.highlight(result.result_type == \"type\" ? result.full_name : result.name);\n\n    var prefix = CrystalDocs.prefixForType(result.result_type);\n    if (prefix) {\n      title = \"<b>\" + prefix + \"</b>\" + title;\n    }\n\n    title = \"<strong>\" + title + \"</strong>\";\n\n    if (result.args_string) {\n      title +=\n        \"<span class=\\\"args\\\">\" + query.highlight(result.args_string) + \"</span>\";\n    }\n\n    $elem = document.createElement(\"li\");\n    $elem.className = \"search-result search-result--\" + result.result_type;\n    $elem.dataset.href = url;\n    $elem.setAttribute(\"title\", result.full_name + \" docs page\");\n\n    var $title = document.createElement(\"div\");\n    $title.setAttribute(\"class\", \"search-result__title\");\n    var $titleLink = document.createElement(\"a\");\n    $titleLink.setAttribute(\"href\", url);\n\n    $titleLink.innerHTML = title;\n    $title.appendChild($titleLink);\n    $elem.appendChild($title);\n    $elem.addEventListener(\"click\", function() {\n      $titleLink.click();\n    });\n\n    if (result.result_type !== \"type\") {\n      var $type = document.createElement(\"div\");\n      $type.setAttribute(\"class\", \"search-result__type\");\n      $type.innerHTML = query.highlight(result.type);\n      $elem.appendChild($type);\n    }\n\n    if(result.summary){\n      var $doc = document.createElement(\"div\");\n      $doc.setAttribute(\"class\", \"search-result__doc\");\n      $doc.innerHTML = query.highlight(sanitize(result.summary));\n      $elem.appendChild($doc);\n    }\n\n    $elem.appendChild(document.createComment(JSON.stringify(result)));\n    $frag.appendChild($elem);\n  });\n\n  $resultsElem.appendChild($frag);\n\n  CrystalDocs.toggleResultsList(true);\n};\n\nCrystalDocs.toggleResultsList = function(visible) {\n  if (visible) {\n    document.querySelector(\".types-list\").classList.add(\"hidden\");\n    document.querySelector(\".search-results\").classList.remove(\"hidden\");\n  } else {\n    document.querySelector(\".types-list\").classList.remove(\"hidden\");\n    document.querySelector(\".search-results\").classList.add(\"hidden\");\n  }\n};\n\nCrystalDocs.Query = function(string) {\n  this.original = string;\n  this.terms = string.split(/\\s+/).filter(function(word) {\n    return CrystalDocs.Query.stripModifiers(word).length > 0;\n  });\n\n  var normalized = this.terms.map(CrystalDocs.Query.normalizeTerm);\n  this.normalizedTerms = normalized;\n\n  function runMatcher(field, matcher) {\n    if (!field) {\n      return false;\n    }\n    var normalizedValue = CrystalDocs.Query.normalizeTerm(field);\n\n    var matches = [];\n    normalized.forEach(function(term) {\n      if (matcher(normalizedValue, term)) {\n        matches.push(term);\n      }\n    });\n    return matches.length > 0 ? matches : false;\n  }\n\n  this.matches = function(field) {\n    return runMatcher(field, function(normalized, term) {\n      if (term[0] == \"#\" || term[0] == \".\") {\n        return false;\n      }\n      return normalized.indexOf(term) >= 0;\n    });\n  };\n\n  function namespaceMatcher(normalized, term){\n    var i = term.indexOf(\":\");\n    if(i >= 0){\n      term = term.replace(/^::?|::?$/, \"\");\n      var index = normalized.indexOf(term);\n      if((index == 0) || (index > 0 && normalized[index-1] == \":\")){\n        return true;\n      }\n    }\n    return false;\n  }\n  this.matchesMethod = function(name, kind, type) {\n    return runMatcher(name, function(normalized, term) {\n      var i = term.indexOf(\"#\");\n      if(i >= 0){\n        if (kind != \"instance_method\") {\n          return false;\n        }\n      }else{\n        i = term.indexOf(\".\");\n        if(i >= 0){\n          if (kind != \"class_method\" && kind != \"macro\" && kind != \"constructor\") {\n            return false;\n          }\n        }else{\n          //neither # nor .\n          if(term.indexOf(\":\") && namespaceMatcher(normalized, term)){\n            return true;\n          }\n        }\n      }\n\n      var methodName = term;\n      if(i >= 0){\n        var termType = term.substring(0, i);\n        methodName = term.substring(i+1);\n\n        if(termType != \"\") {\n          if(CrystalDocs.Query.normalizeTerm(type.full_name).indexOf(termType) < 0){\n            return false;\n          }\n        }\n      }\n      return normalized.indexOf(methodName) >= 0;\n    });\n  };\n\n  this.matchesNamespace = function(namespace){\n    return runMatcher(namespace, namespaceMatcher);\n  };\n\n  this.highlight = function(string) {\n    if (typeof string == \"undefined\") {\n      return \"\";\n    }\n    function escapeRegExp(s) {\n      return s.replace(/[.*+?\\^${}()|\\[\\]\\\\]/g, \"\\\\$&\").replace(/^[#\\.:]+/, \"\");\n    }\n    return string.replace(\n      new RegExp(\"(\" + this.normalizedTerms.map(escapeRegExp).join(\"|\") + \")\", \"gi\"),\n      \"<mark>$1</mark>\"\n    );\n  };\n};\nCrystalDocs.Query.normalizeTerm = function(term) {\n  return term.toLowerCase();\n};\nCrystalDocs.Query.stripModifiers = function(term) {\n  switch (term[0]) {\n    case \"#\":\n    case \".\":\n    case \":\":\n      return term.substr(1);\n\n    default:\n      return term;\n  }\n}\n\nCrystalDocs.search = function(string) {\n  if(!CrystalDocs.searchIndex) {\n    console.log(\"CrystalDocs search index not initialized, delaying search\");\n\n    document.addEventListener(\"CrystalDocs:loaded\", function listener(){\n      document.removeEventListener(\"CrystalDocs:loaded\", listener);\n      CrystalDocs.search(string);\n    });\n    return;\n  }\n\n  document.dispatchEvent(new Event(\"CrystalDocs:searchStarted\"));\n\n  var query = new CrystalDocs.Query(string);\n  var results = CrystalDocs.runQuery(query);\n  results = CrystalDocs.rankResults(results, query);\n  CrystalDocs.displaySearchResults(results, query);\n\n  document.dispatchEvent(new Event(\"CrystalDocs:searchPerformed\"));\n};\n\nCrystalDocs.initializeIndex = function(data) {\n  CrystalDocs.searchIndex = data;\n\n  document.dispatchEvent(new Event(\"CrystalDocs:loaded\"));\n};\n\nCrystalDocs.loadIndex = function() {\n  function loadJSON(file, callback) {\n    var xobj = new XMLHttpRequest();\n    xobj.overrideMimeType(\"application/json\");\n    xobj.open(\"GET\", file, true);\n    xobj.onreadystatechange = function() {\n      if (xobj.readyState == 4 && xobj.status == \"200\") {\n        callback(xobj.responseText);\n      }\n    };\n    xobj.send(null);\n  }\n\n  function loadScript(file) {\n    script = document.createElement(\"script\");\n    script.src = file;\n    document.body.appendChild(script);\n  }\n\n  function parseJSON(json) {\n    CrystalDocs.initializeIndex(JSON.parse(json));\n  }\n\n  for(var i = 0; i < document.scripts.length; i++){\n    var script = document.scripts[i];\n    if (script.src && script.src.indexOf(\"js/doc.js\") >= 0) {\n      if (script.src.indexOf(\"file://\") == 0) {\n        // We need to support JSONP files for the search to work on local file system.\n        var jsonPath = script.src.replace(\"js/doc.js\", \"search-index.js\");\n        loadScript(jsonPath);\n        return;\n      } else {\n        var jsonPath = script.src.replace(\"js/doc.js\", \"index.json\");\n        loadJSON(jsonPath, parseJSON);\n        return;\n      }\n    }\n  }\n  console.error(\"Could not find location of js/doc.js\");\n};\n\n// Callback for jsonp\nfunction crystal_doc_search_index_callback(data) {\n  CrystalDocs.initializeIndex(data);\n}\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/js/_usage-modal.js",
    "content": "var UsageModal = function(title, content) {\n  var $body = document.body;\n  var self = this;\n  var $modalBackground = document.createElement(\"div\");\n  $modalBackground.classList.add(\"modal-background\");\n  var $usageModal = document.createElement(\"div\");\n  $usageModal.classList.add(\"usage-modal\");\n  $modalBackground.appendChild($usageModal);\n  var $title = document.createElement(\"h3\");\n  $title.classList.add(\"modal-title\");\n  $title.innerHTML = title\n  $usageModal.appendChild($title);\n  var $closeButton = document.createElement(\"span\");\n  $closeButton.classList.add(\"close-button\");\n  $closeButton.setAttribute(\"title\", \"Close modal\");\n  $closeButton.innerText = '×';\n  $usageModal.appendChild($closeButton);\n  $usageModal.insertAdjacentHTML(\"beforeend\", content);\n\n  $modalBackground.addEventListener('click', function(event) {\n    var element = event.target || event.srcElement;\n\n    if(element == $modalBackground) {\n      self.hide();\n    }\n  });\n  $closeButton.addEventListener('click', function(event) {\n    self.hide();\n  });\n\n  $body.insertAdjacentElement('beforeend', $modalBackground);\n\n  this.show = function(){\n    $body.classList.add(\"js-modal-visible\");\n  };\n  this.hide = function(){\n    $body.classList.remove(\"js-modal-visible\");\n  };\n  this.isVisible = function(){\n    return $body.classList.contains(\"js-modal-visible\");\n  }\n}\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/js/_versions.js",
    "content": "CrystalDocs.initializeVersions = function () {\n  function loadJSON(file, callback) {\n    var xobj = new XMLHttpRequest();\n    xobj.overrideMimeType(\"application/json\");\n    xobj.open(\"GET\", file, true);\n    xobj.onreadystatechange = function() {\n      if (xobj.readyState == 4 && xobj.status == \"200\") {\n        callback(xobj.responseText);\n      }\n    };\n    xobj.send(null);\n  }\n\n  function parseJSON(json) {\n    CrystalDocs.loadConfig(JSON.parse(json));\n  }\n\n  $elem = document.querySelector(\"html > head > meta[name=\\\"crystal_docs.json_config_url\\\"]\")\n  if ($elem == undefined) {\n    return\n  }\n  jsonURL = $elem.getAttribute(\"content\")\n  if (jsonURL && jsonURL != \"\") {\n    loadJSON(jsonURL, parseJSON);\n  }\n}\n\nCrystalDocs.loadConfig = function (config) {\n  var projectVersions = config[\"versions\"]\n  var currentVersion = document.querySelector(\"html > head > meta[name=\\\"crystal_docs.project_version\\\"]\").getAttribute(\"content\")\n\n  var currentVersionInList = projectVersions.find(function (element) {\n    return element.name == currentVersion\n  })\n\n  if (!currentVersionInList) {\n    projectVersions.unshift({ name: currentVersion, url: '#' })\n  }\n\n  $version = document.querySelector(\".project-summary > .project-version\")\n  $version.innerHTML = \"\"\n\n  $select = document.createElement(\"select\")\n  $select.classList.add(\"project-versions-nav\")\n  $select.addEventListener(\"change\", function () {\n    window.location.href = this.value\n  })\n  projectVersions.forEach(function (version) {\n    $item = document.createElement(\"option\")\n    $item.setAttribute(\"value\", version.url)\n    $item.append(document.createTextNode(version.name))\n\n    if (version.name == currentVersion) {\n      $item.setAttribute(\"selected\", true)\n      $item.setAttribute(\"disabled\", true)\n    }\n    $select.append($item)\n  });\n  $form = document.createElement(\"form\")\n  $form.setAttribute(\"autocomplete\", \"off\")\n  $form.append($select)\n  $version.append($form)\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  CrystalDocs.initializeVersions()\n})\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/js/doc.js",
    "content": "window.CrystalDocs = (window.CrystalDocs || {});\n\nCrystalDocs.base_path = (CrystalDocs.base_path || \"\");\n\n<%= JsSearchTemplate.new %>\n<%= JsNavigatorTemplate.new %>\n<%= JsVersionsTemplate.new %>\n<%= JsUsageModal.new %>\n\ndocument.addEventListener('DOMContentLoaded', function() {\n  var sessionStorage;\n  try {\n    sessionStorage = window.sessionStorage;\n  } catch (e) { }\n  if(!sessionStorage) {\n    sessionStorage = {\n      setItem: function() {},\n      getItem: function() {},\n      removeItem: function() {}\n    };\n  }\n\n  var repositoryName = document.querySelector('[name=repository-name]').getAttribute('content');\n  var typesList = document.querySelector('.types-list');\n  var searchInput = document.querySelector('.search-input');\n  var parents = document.querySelectorAll('.types-list li.parent');\n\n  var scrollSidebarToOpenType = function(){\n    var openTypes = typesList.querySelectorAll('.current');\n    if (openTypes.length > 0) {\n      var lastOpenType = openTypes[openTypes.length - 1];\n      lastOpenType.scrollIntoView(!(window.matchMedia('only screen and (max-width: 635px)')).matches);\n    }\n  }\n\n  scrollSidebarToOpenType();\n\n  var setPersistentSearchQuery = function(value){\n    sessionStorage.setItem(repositoryName + '::search-input:value', value);\n  }\n\n  for(var i = 0; i < parents.length; i++) {\n    var _parent = parents[i];\n    _parent.addEventListener('click', function(e) {\n      e.stopPropagation();\n\n      if(e.target.tagName.toLowerCase() == 'li') {\n        if(e.target.className.match(/open/)) {\n          sessionStorage.removeItem(e.target.getAttribute('data-id'));\n          e.target.className = e.target.className.replace(/ +open/g, '');\n        } else {\n          sessionStorage.setItem(e.target.getAttribute('data-id'), '1');\n          if(e.target.className.indexOf('open') == -1) {\n            e.target.className += ' open';\n          }\n        }\n      }\n    });\n\n    if(sessionStorage.getItem(_parent.getAttribute('data-id')) == '1') {\n      _parent.className += ' open';\n    }\n  }\n\n  var leaveSearchScope = function(){\n    CrystalDocs.toggleResultsList(false);\n    window.focus();\n  }\n\n  var navigator = new Navigator(document.querySelector('.types-list'), searchInput, document.querySelector(\".search-results\"), leaveSearchScope);\n\n  CrystalDocs.loadIndex();\n  var searchTimeout;\n  var lastSearchText = false;\n  var performSearch = function() {\n    document.dispatchEvent(new Event(\"CrystalDocs:searchDebounceStarted\"));\n\n    clearTimeout(searchTimeout);\n    searchTimeout = setTimeout(function() {\n      var text = searchInput.value;\n\n      if(text == \"\") {\n        CrystalDocs.toggleResultsList(false);\n      }else if(text == lastSearchText){\n        document.dispatchEvent(new Event(\"CrystalDocs:searchDebounceStopped\"));\n      }else{\n        CrystalDocs.search(text);\n        navigator.highlightFirst();\n        searchInput.focus();\n      }\n      lastSearchText = text;\n      setPersistentSearchQuery(text);\n    }, 200);\n  };\n\n  if(location.hash.length > 3 && location.hash.substring(0,3) == \"#q=\"){\n    // allows directly linking a search query which is then executed on the client\n    // this comes handy for establishing a custom browser search engine with https://crystal-lang.org/api/#q=%s as a search URL\n    // TODO: Add OpenSearch description\n    var searchQuery = location.hash.substring(3);\n    history.pushState({searchQuery: searchQuery}, \"Search for \" + searchQuery, location.href.replace(/#q=.*/, \"\"));\n    searchInput.value = decodeURIComponent(searchQuery);\n    document.addEventListener('CrystalDocs:loaded', performSearch);\n  }\n\n  if (searchInput.value.length == 0) {\n    var searchText = sessionStorage.getItem(repositoryName + '::search-input:value');\n    if(searchText){\n      searchInput.value = searchText;\n    }\n  }\n  searchInput.addEventListener('keyup', performSearch);\n  searchInput.addEventListener('input', performSearch);\n\n  var usageModal = new UsageModal('Keyboard Shortcuts', '' +\n      '<ul class=\"usage-list\">' +\n      '  <li>' +\n      '    <span class=\"usage-key\">' +\n      '      <kbd>s</kbd>,' +\n      '      <kbd>/</kbd>' +\n      '    </span>' +\n      '    Search' +\n      '  </li>' +\n      '  <li>' +\n      '    <kbd class=\"usage-key\">Esc</kbd>' +\n      '    Abort search / Close modal' +\n      '  </li>' +\n      '  <li>' +\n      '    <span class=\"usage-key\">' +\n      '      <kbd>⇨</kbd>,' +\n      '      <kbd>Enter</kbd>' +\n      '    </span>' +\n      '    Open highlighted result' +\n      '  </li>' +\n      '  <li>' +\n      '    <span class=\"usage-key\">' +\n      '      <kbd>⇧</kbd>,' +\n      '      <kbd>Ctrl+j</kbd>' +\n      '    </span>' +\n      '    Select previous result' +\n      '  </li>' +\n      '  <li>' +\n      '    <span class=\"usage-key\">' +\n      '      <kbd>⇩</kbd>,' +\n      '      <kbd>Ctrl+k</kbd>' +\n      '    </span>' +\n      '    Select next result' +\n      '  </li>' +\n      '  <li>' +\n      '    <kbd class=\"usage-key\">?</kbd>' +\n      '    Show usage info' +\n      '  </li>' +\n      '</ul>'\n    );\n\n  function handleShortkeys(event) {\n    var element = event.target || event.srcElement;\n\n    if(element.tagName == \"INPUT\" || element.tagName == \"TEXTAREA\" || element.parentElement.tagName == \"TEXTAREA\"){\n      return;\n    }\n\n    switch(event.key) {\n      case \"?\":\n        usageModal.show();\n        break;\n\n      case \"Escape\":\n        usageModal.hide();\n        break;\n\n      case \"s\":\n      case \"/\":\n        if(usageModal.isVisible()) {\n          return;\n        }\n        event.stopPropagation();\n        navigator.focus();\n        performSearch();\n        break;\n    }\n  }\n\n  document.addEventListener('keyup', handleShortkeys);\n\n  var scrollToEntryFromLocationHash = function() {\n    var hash = window.location.hash;\n    if (hash) {\n      var targetAnchor = decodeURI(hash.substr(1));\n      var targetEl = document.getElementById(targetAnchor)\n      if (targetEl) {\n        targetEl.offsetParent.scrollTop = targetEl.offsetTop;\n      }\n    }\n  };\n  window.addEventListener(\"hashchange\", scrollToEntryFromLocationHash, false);\n  scrollToEntryFromLocationHash();\n});\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/main.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <%- if base_path = self.base_path -%>\n  <base href=\"<%= base_path %>\" />\n  <%- end -%>\n  <%= HeadTemplate.new(project_info, nil) %>\n  <meta name=\"repository-name\" content=\"<%= project_info.name %>\">\n  <title><%= project_info.name %> <%= project_info.version %></title>\n  <script type=\"text/javascript\">\n  CrystalDocs.base_path = \"\";\n  </script>\n</head>\n<body>\n\n<%= Crystal::Doc::SVG_DEFS %>\n<%= Crystal::Doc::SIDEBAR_BUTTON %>\n<%= SidebarTemplate.new(project_info, types, nil) %>\n\n<div class=\"main-content\">\n<%= body %>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/sitemap.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n  <% types.each do |type| %><url>\n    <loc><%= ::Path.posix(base_url, type.path) %></loc>\n    <priority><%= priority %></priority>\n    <changefreq><%= changefreq %></changefreq>\n  </url><% end %>\n</urlset>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/html/type.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <%= HeadTemplate.new(project_info, type) %>\n  <meta name=\"repository-name\" content=\"<%= project_info.name %>\">\n  <title><%= type.full_name %> - <%= project_info.name %> <%= project_info.version %></title>\n  <script type=\"text/javascript\">\n    CrystalDocs.base_path = \"<%= type.path_to \"\" %>\";\n  </script>\n</head>\n<body>\n\n<%= Crystal::Doc::SVG_DEFS %>\n<%= Crystal::Doc::SIDEBAR_BUTTON %>\n<%= SidebarTemplate.new(project_info, types, type) %>\n\n<div class=\"main-content\">\n<h1 class=\"type-name\">\n<% if type.program? %>\n  <%= type.full_name.gsub(\"::\", \"::<wbr>\") %>\n<% else %>\n  <span class=\"kind\">\n    <%= type.abstract? ? \"abstract \" : \"\"%><%= type.visibility.try(&.+(\" \")) %><%= type.kind %>\n  </span> <%= type.full_name.gsub(\"::\", \"::<wbr>\") %>\n<% end %>\n</h1>\n\n<% if type.superclass %>\n  <%= type.superclass_hierarchy %>\n<% end %>\n\n\n<% if doc = type.formatted_doc %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(\"overview\") %>\n    Overview\n  </h2>\n\n  <%= doc %>\n<% end %>\n\n<% if type.alias? %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(\"alias-definition\") %>\n    Alias Definition\n  </h2>\n  <code><%= type.formatted_alias_definition %></code>\n<% end %>\n\n<% if type.type_def? %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(\"type-definition\") %>\n    Type Definition\n  </h2>\n  <code><%= type.formatted_type_definition %></code>\n<% end %>\n\n<%= OtherTypesTemplate.new(\"Included Modules\", type, type.included_modules) %>\n<%= OtherTypesTemplate.new(\"Extended Modules\", type, type.extended_modules) %>\n<%= OtherTypesTemplate.new(\"Direct Known Subclasses\", type, type.subclasses) %>\n<%= OtherTypesTemplate.new(\"Direct including types\", type, type.including_types) %>\n\n<% if locations = type.locations %>\n  <h2>\n    <%= Crystal::Doc.anchor_link(\"defined-in\") %>\n    Defined in:\n  </h2>\n  <% locations.each do |location| %>\n    <% if url = project_info.source_url(location) %>\n      <a href=\"<%= url %>\" target=\"_blank\">\n        <%= location.filename_in_project %><% if location.show_line_number %>:<%= location.line_number %><% end %>\n      </a>\n    <% else %>\n      <%= location.filename_in_project %><% if location.show_line_number %>:<%= location.line_number %><% end %>\n    <% end %>\n    <br/>\n  <% end %>\n<% end %>\n\n<% unless type.constants.empty? %>\n  <% if type.enum? %>\n    <h2>\n      <%= Crystal::Doc.anchor_link(\"enum-members\") %>\n      Enum Members\n    </h2>\n  <% else %>\n    <h2>\n      <%= Crystal::Doc.anchor_link(\"constant-summary\") %>\n      Constant Summary\n    </h2>\n  <% end %>\n  <dl>\n    <% type.constants.each do |const| %>\n      <dt class=\"entry-const\" id=\"<%= const.id %>\">\n        <strong><%= const.name %></strong> = <code><%= const.formatted_value %></code>\n      </dt>\n      <% if doc = const.formatted_doc %>\n      <dd class=\"entry-const-doc\">\n        <%= doc %>\n      </dd>\n      <% end %>\n    <% end %>\n  </dl>\n<% end %>\n\n<% if type.lib? %>\n  <%= MethodSummaryTemplate.new(\"External Variable Summary\", type.external_vars) %>\n  <%= MethodSummaryTemplate.new(\"Function Summary\", type.functions) %>\n  <%= MethodDetailTemplate.new(\"External Variable Detail\", type.external_vars) %>\n  <%= MethodDetailTemplate.new(\"Function Detail\", type.functions) %>\n<% elsif !type.type_def? %>\n  <%= MethodSummaryTemplate.new(\"Constructors\", type.constructors) %>\n  <%= MethodSummaryTemplate.new(type.program? ? \"Method Summary\" : \"Class Method Summary\", type.class_methods) %>\n  <%= MethodSummaryTemplate.new(\"Macro Summary\", type.macros) %>\n  <%= MethodSummaryTemplate.new(\"Instance Method Summary\", type.instance_methods) %>\n\n  <div class=\"methods-inherited\">\n    <% type.ancestors.each do |ancestor| %>\n      <%= MethodsInheritedTemplate.new(type, ancestor, ancestor.instance_methods, \"Instance\") %>\n      <%= MethodsInheritedTemplate.new(type, ancestor, ancestor.constructors, \"Constructor\") %>\n      <%= MethodsInheritedTemplate.new(type, ancestor, ancestor.class_methods, \"Class\") %>\n      <%= MacrosInheritedTemplate.new(type, ancestor, ancestor.macros) %>\n    <% end %>\n  </div>\n\n  <%= MethodDetailTemplate.new(\"Constructor Detail\", type.constructors) %>\n  <%= MethodDetailTemplate.new(type.program? ? \"Method Detail\" : \"Class Method Detail\", type.class_methods) %>\n  <%= MethodDetailTemplate.new(\"Macro Detail\", type.macros) %>\n  <%= MethodDetailTemplate.new(\"Instance Method Detail\", type.instance_methods) %>\n<% end %>\n</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/item.cr",
    "content": "module Crystal::Doc::Item\n  def formatted_doc\n    @generator.doc(self)\n  end\n\n  def formatted_summary\n    @generator.summary(self)\n  end\nend\n\nenum Crystal::Doc::HTMLOption\n  None\n  Highlight\n  All\n\n  def highlight? : Bool\n    self >= Highlight\n  end\n\n  def links? : Bool\n    self >= All\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/macro.cr",
    "content": "require \"html\"\nrequire \"uri\"\nrequire \"./item\"\n\nclass Crystal::Doc::Macro\n  include Item\n\n  getter type : Type\n  getter macro : Crystal::Macro\n\n  def initialize(@generator : Generator, @type : Type, @macro : Crystal::Macro)\n  end\n\n  def name\n    @macro.name\n  end\n\n  def args\n    @macro.args\n  end\n\n  def doc\n    @macro.doc.try &.strip.lchop(\":showdoc:\").strip\n  end\n\n  def doc_copied_from\n    nil\n  end\n\n  def location\n    @generator.relative_location(@macro)\n  end\n\n  def id\n    String.build do |io|\n      io << to_s.delete(' ')\n      io << \"-macro\"\n    end\n  end\n\n  def html_id\n    HTML.escape(id)\n  end\n\n  def anchor\n    '#' + URI.encode_path(id)\n  end\n\n  def prefix\n    \"\"\n  end\n\n  def abstract?\n    false\n  end\n\n  def visibility\n    case @macro.visibility\n    in .public?\n    in .protected?\n      \"protected\"\n    in .private?\n      \"private\"\n    end\n  end\n\n  def real_name\n  end\n\n  def kind\n    \"macro \"\n  end\n\n  def to_s(io : IO) : Nil\n    io << name\n    args_to_s io\n  end\n\n  def args_to_s\n    String.build { |io| args_to_s io }\n  end\n\n  def args_to_s(io : IO) : Nil\n    args_to_html(io, html: :none)\n  end\n\n  def args_to_html(html : HTMLOption = :all)\n    String.build { |io| args_to_html io, html }\n  end\n\n  def args_to_html(io : IO, html : HTMLOption = :all) : Nil\n    return unless has_args?\n\n    printed = false\n    io << '('\n\n    @macro.args.each_with_index do |arg, i|\n      io << \", \" if printed\n      io << '*' if @macro.splat_index == i\n      arg_to_html arg, io, html: html\n      printed = true\n    end\n\n    if double_splat = @macro.double_splat\n      io << \", \" if printed\n      io << \"**\"\n      arg_to_html double_splat, io, html: html\n      printed = true\n    end\n\n    if block_arg = @macro.block_arg\n      io << \", \" if printed\n      io << '&'\n      arg_to_html block_arg, io, html: html\n    end\n\n    io << ')'\n  end\n\n  def arg_to_html(arg : Arg, io, html : HTMLOption = :all)\n    if arg.external_name != arg.name\n      if name = arg.external_name.presence\n        name = Symbol.quote_for_named_argument(name)\n        if html.none?\n          io << name\n        else\n          HTML.escape name, io\n        end\n      else\n        io << \"_\"\n      end\n      io << ' '\n    end\n\n    io << arg.name\n\n    # Macro arg cannot not have a restriction.\n\n    if default_value = arg.default_value\n      io << \" = \"\n      if html.highlight?\n        io << SyntaxHighlighter::HTML.highlight!(default_value.to_s)\n      else\n        io << default_value\n      end\n    end\n  end\n\n  def has_args?\n    !@macro.args.empty? || @macro.double_splat || @macro.block_arg\n  end\n\n  def must_be_included?\n    @generator.must_include? @macro\n  end\n\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"html_id\", id\n      builder.field \"name\", name\n      builder.field \"doc\", doc unless doc.nil?\n      builder.field \"summary\", formatted_summary unless formatted_summary.nil?\n      builder.field \"abstract\", abstract?\n      builder.field \"args\", args unless args.empty?\n      builder.field \"args_string\", args_to_s unless args.empty?\n      builder.field \"args_html\", args_to_html unless args.empty?\n      builder.field \"location\", location unless location.nil?\n      builder.field \"def\", self.macro\n    end\n  end\n\n  def annotations(annotation_type)\n    @macro.annotations(annotation_type)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/main.cr",
    "content": "module Crystal::Doc\n  record Main, body : String, program : Type, project_info : ProjectInfo do\n    def to_s(io : IO) : Nil\n      to_json(io)\n    end\n\n    def to_jsonp\n      String.build do |io|\n        to_jsonp(io)\n      end\n    end\n\n    def to_jsonp(io : IO)\n      io << \"crystal_doc_search_index_callback(\"\n      to_json(io)\n      io << ')'\n    end\n\n    def to_json(builder : JSON::Builder)\n      builder.object do\n        builder.field \"repository_name\", project_info.name\n        builder.field \"body\", body\n        builder.field \"program\", program\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/markd_doc_renderer.cr",
    "content": "{% if !flag?(:without_libxml2) %}\n  require \"sanitize\"\n{% end %}\n\nclass Crystal::Doc::MarkdDocRenderer < Markd::HTMLRenderer\n  {% if !flag?(:without_libxml2) %}\n    SANITIZER = Sanitize::Policy::HTMLSanitizer.common\n  {% else %}\n    SANITIZER = nil\n  {% end %}\n\n  @anchor_map = Hash(String, Int32).new(0)\n\n  def initialize(@type : Crystal::Doc::Type, options)\n    super(options)\n  end\n\n  def self.new(obj : Constant | Macro | Method, options)\n    new obj.type, options\n  end\n\n  def heading(node : Markd::Node, entering : Bool)\n    tag_name = HEADINGS[node.data[\"level\"].as(Int32) - 1]\n    if entering\n      anchor = collect_text(node)\n        .underscore                # Underscore the string\n        .gsub(/[^\\w\\d\\s\\-.~]/, \"\") # Delete unsafe URL characters\n        .strip                     # Strip leading/trailing whitespace\n        .gsub(/[\\s_-]+/, '-')      # Replace `_` and leftover whitespace with `-`\n\n      seen_count = @anchor_map[anchor] += 1\n\n      if seen_count > 1\n        anchor += \"-#{seen_count - 1}\"\n      end\n\n      tag(tag_name, attrs(node))\n      literal Crystal::Doc.anchor_link(anchor)\n    else\n      tag(tag_name, end_tag: true)\n      newline\n    end\n  end\n\n  def collect_text(main)\n    String.build do |io|\n      walker = main.walker\n      while item = walker.next\n        node, entering = item\n        if entering && (text = node.text)\n          io << text\n        end\n      end\n    end\n  end\n\n  def code_body(node : Markd::Node)\n    if in_link?(node)\n      output(node.text)\n    else\n      literal(expand_code_links(escape(node.text)))\n    end\n  end\n\n  def in_link?(node)\n    parent = node.parent?\n    return false unless parent\n    return true if parent.type.link?\n\n    in_link?(parent)\n  end\n\n  def expand_code_links(text : String) : String\n    # Check method reference (without #, but must be the whole text)\n    if text =~ /\\A([\\w<=>+\\-*\\/\\[\\]&|?!^~]+[?!]?)(?:\\((.*?)\\))?\\Z/\n      name = $1\n      args = $2? || \"\"\n\n      method = lookup_method @type, name, args\n      if method\n        return method_link method, \"#{method.prefix}#{text}\"\n      end\n    end\n\n    # Check Type#method(...) or Type or #method(...)\n    text.gsub %r(\n      ((?:\\B::)?\\b[A-Z]\\w*(?:\\:\\:[A-Z]\\w*)*|\\B|(?<=\\bself))(?<!\\.)([#.])([\\w<=>+\\-*\\/\\[\\]&|?!^~]+[?!]?)(?:\\((.*?)\\))?\n        |\n      ((?:\\B::)?\\b[A-Z]\\w*(?:\\:\\:[A-Z]\\w*)*)\n      )x do |match_text|\n      if $5?\n        # Type\n        another_type = @type.lookup_path(match_text)\n        if another_type && another_type.must_be_included?\n          next type_link another_type, match_text\n        end\n        next match_text\n      end\n\n      type_name = $1.presence\n      instance_methods_first = $2 == \"#\"\n      method_name = $3\n      method_args = $4? || \"\"\n\n      if type_name\n        # Type#method(...)\n        another_type = @type.lookup_path(type_name)\n        if another_type && @type.must_be_included?\n          method = lookup_method another_type, method_name, method_args, instance_methods_first\n          if method\n            next method_link method, match_text\n          end\n        end\n      else\n        # #method(...)\n        method = lookup_method @type, method_name, method_args, instance_methods_first\n        if method && method.must_be_included?\n          next method_link method, match_text\n        end\n      end\n\n      match_text\n    end\n  end\n\n  def code_block_language(languages)\n    language = languages.try(&.first?).try(&.strip.presence)\n    if language.nil? || language == \"cr\"\n      language = \"crystal\"\n    end\n    language\n  end\n\n  def code_block_body(node : Markd::Node, language : String?)\n    code = node.text.chomp\n    if language == \"crystal\"\n      literal(SyntaxHighlighter::HTML.highlight! code)\n    else\n      output(code)\n    end\n  end\n\n  private def type_link(type, text)\n    %(<a href=\"#{type.path_from(@type)}\">#{text}</a>)\n  end\n\n  private def method_link(method, text)\n    %(<a href=\"#{method.type.path_from(@type)}#{method.anchor}\">#{text}</a>)\n  end\n\n  private def lookup_method(type, name, args, instance_methods_first = true)\n    case args\n    when \"\"\n      args_count = nil\n    when \"()\"\n      args_count = 0\n    else\n      args_count = args.chars.count(',') + 1\n    end\n\n    base_match =\n      if instance_methods_first\n        type.lookup_method(name, args_count) || type.lookup_class_method(name, args_count)\n      else\n        type.lookup_class_method(name, args_count) || type.lookup_method(name, args_count)\n      end\n    base_match ||\n      type.lookup_macro(name, args_count) ||\n      type.program.lookup_macro(name, args_count)\n  end\n\n  def text(node : Markd::Node, entering : Bool)\n    output(sanitize(node))\n  end\n\n  def html_block(node : Markd::Node, entering : Bool)\n    newline\n    content = @options.safe? ? \"<!-- raw HTML omitted -->\" : sanitize(node)\n    literal(content)\n    newline\n  end\n\n  def html_inline(node : Markd::Node, entering : Bool)\n    content = @options.safe? ? \"<!-- raw HTML omitted -->\" : sanitize(node)\n    literal(content)\n  end\n\n  def sanitize(node : Markd::Node) : String\n    {% if !flag?(:without_libxml2) %}\n      SANITIZER.process(node.text)\n    {% else %}\n      node.text\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/method.cr",
    "content": "require \"html\"\nrequire \"uri\"\nrequire \"./item\"\n\nclass Crystal::Doc::Method\n  include Item\n\n  PSEUDO_METHOD_PREFIX = \"__crystal_pseudo_\"\n  PSEUDO_METHOD_NOTE   = <<-DOC\n\n    NOTE: This is a pseudo-method provided directly by the Crystal compiler.\n    It cannot be redefined nor overridden.\n    DOC\n\n  getter type : Type\n  getter def : Def\n\n  def initialize(@generator : Generator, @type : Type, @def : Def, @class_method : Bool)\n  end\n\n  def name\n    name = @def.name\n    if @generator.project_info.crystal_stdlib?\n      name.lchop(PSEUDO_METHOD_PREFIX)\n    else\n      name\n    end\n  end\n\n  def args\n    @def.args\n  end\n\n  private record DocInfo, doc : String?, copied_from : Type?\n\n  private getter(doc_info : DocInfo) do\n    compute_doc_info\n  end\n\n  # Returns this method's docs ready to be shown (before formatting)\n  # in the UI. This includes copying docs from previous def or\n  # ancestors and replacing `:inherit:` with the ancestor docs.\n  # This docs not include the \"Description copied from ...\" banner\n  # in case it's needed.\n  def doc\n    doc_info.doc.try &.strip.lchop(\":showdoc:\").strip\n  end\n\n  # Returns the type this method's docs are copied from, but\n  # only if this method has no docs at all. In this case\n  # the docs will be copied from this type and a\n  # \"Description copied from ...\" will be added before the docs.\n  def doc_copied_from : Type?\n    doc_info.copied_from\n  end\n\n  private def compute_doc_info : DocInfo?\n    def_doc = @def.doc\n    if def_doc\n      ancestor_doc_info = nil\n      # TODO: warn about `:inherit:` not finding an ancestor\n      inherit_def_doc = def_doc.gsub(/^[ \\t]*:inherit:[ \\t]*$/m) do\n        ancestor_doc_info ||= self.ancestor_doc_info\n        ancestor_doc_info.try(&.doc) || break\n      end\n\n      # inherit_def_doc is nil when breaking from the gsub block which means\n      # no ancestor doc info was found\n      if inherit_def_doc && !inherit_def_doc.same?(def_doc)\n        return DocInfo.new(inherit_def_doc, nil)\n      end\n\n      if @def.name.starts_with?(PSEUDO_METHOD_PREFIX)\n        def_doc += PSEUDO_METHOD_NOTE\n      end\n\n      return DocInfo.new(def_doc, nil)\n    end\n\n    previous_docs = previous_def_docs(@def)\n    if previous_docs\n      return DocInfo.new(previous_docs, nil)\n    end\n\n    ancestor_info = self.ancestor_doc_info\n    return ancestor_info if ancestor_info\n\n    DocInfo.new(nil, nil)\n  end\n\n  private def previous_def_docs(a_def)\n    while previous = a_def.previous\n      a_def = previous.def\n      doc = a_def.doc\n      return doc if doc\n    end\n\n    nil\n  end\n\n  private def ancestor_doc_info\n    def_with_metadata = DefWithMetadata.new(@def)\n\n    # Check ancestors\n    type.type.ancestors.each do |ancestor|\n      other_defs_with_metadata = ancestor.defs.try &.[@def.name]?\n      other_defs_with_metadata.try &.each do |other_def_with_metadata|\n        # If we find an ancestor method with the same signature\n        if def_with_metadata.compare_strictness(other_def_with_metadata, self_owner: type.type, other_owner: ancestor) == 0\n          other_def = other_def_with_metadata.def\n          ancestor_type = @generator.type(ancestor)\n          ancestor_method = Doc::Method.new(@generator, ancestor_type, other_def, @class_method)\n          doc = ancestor_method.doc\n          return DocInfo.new(doc, ancestor_type) if doc\n        end\n      end\n    end\n\n    nil\n  end\n\n  def location\n    @generator.relative_location(@def)\n  end\n\n  def external_var?\n    !!@def.as?(External).try(&.external_var?)\n  end\n\n  def prefix\n    case\n    when @type.program?\n      \"\"\n    when external_var?\n      \"$\"\n    when @class_method, @type.lib?\n      \".\"\n    else\n      \"#\"\n    end\n  end\n\n  def visibility\n    case @def.visibility\n    in .public?\n    in .protected?\n      \"protected\"\n    in .private?\n      \"private\"\n    end\n  end\n\n  def real_name\n    a_def = @def.as?(External)\n    if a_def && (real_name = (a_def.real_name)) && (real_name != a_def.name)\n      \" = #{real_name}\"\n    end\n  end\n\n  def constructor?\n    return false unless @class_method\n    return true if name == \"new\"\n\n    return_type = self.return_type\n    if return_type.is_a?(Union)\n      if return_type.types.size == 2\n        if @type.nil_type?(return_type.types[0])\n          return_type = return_type.types[1]\n        elsif @type.nil_type?(return_type.types[1])\n          return_type = return_type.types[0]\n        end\n      end\n    end\n    return_type.to_s.in?(type.name, \"self\")\n  end\n\n  def abstract?\n    @def.abstract?\n  end\n\n  def return_type\n    return_type = @def.return_type\n\n    # If the def's body is a single instance variable, we include\n    # a return type since instance vars must have a fixed/guessed type,\n    # so docs will be better and easier to navigate.\n    if !return_type && (body = @def.body).is_a?(InstanceVar)\n      owner = type.type\n      if owner.is_a?(NonGenericClassType)\n        ivar = owner.lookup_instance_var?(body.name)\n        return_type = ivar.try &.type?\n      end\n    end\n    return_type\n  end\n\n  def kind\n    case\n    when external_var?\n      \"$\"\n    when @type.lib?\n      \"fun \"\n    when @type.program?\n      \"def \"\n    when @class_method\n      \"def self.\"\n    else\n      \"def \"\n    end\n  end\n\n  def id\n    String.build do |io|\n      io << to_s.delete(' ')\n      if external_var?\n        io << \"-external-var\"\n      elsif @type.lib?\n        io << \"-function\"\n      elsif @class_method\n        io << \"-class-method\"\n      else\n        io << \"-instance-method\"\n      end\n    end\n  end\n\n  def html_id\n    HTML.escape(id)\n  end\n\n  def anchor\n    \"#\" + URI.encode_path(id)\n  end\n\n  def to_s(io : IO) : Nil\n    io << name\n    args_to_s io\n  end\n\n  def args_to_s\n    String.build { |io| args_to_s io }\n  end\n\n  def args_to_s(io : IO) : Nil\n    args_to_html(io, html: :none)\n  end\n\n  def args_to_html(html : HTMLOption = :all)\n    String.build { |io| args_to_html io, html }\n  end\n\n  def args_to_html(io : IO, html : HTMLOption = :all) : Nil\n    return_type = self.return_type\n\n    return unless has_args? || return_type\n\n    if has_args?\n      io << '('\n      printed = false\n      @def.args.each_with_index do |arg, i|\n        io << \", \" if printed\n        io << '*' if @def.splat_index == i\n        arg_to_html arg, io, html: html\n        printed = true\n      end\n      if double_splat = @def.double_splat\n        io << \", \" if printed\n        io << \"**\"\n        io << double_splat\n        printed = true\n      end\n      if block_arg = @def.block_arg\n        io << \", \" if printed\n        io << '&'\n        arg_to_html block_arg, io, html: html\n      elsif @def.block_arity\n        io << \", \" if printed\n        io << '&'\n      end\n      io << ')'\n    end\n\n    case return_type\n    when Nil\n      # Nothing to do\n    when ASTNode\n      io << \" : \"\n      node_to_html return_type, io, html: html\n    when Crystal::Type\n      io << \" : \"\n      @type.type_to_html return_type, io, html: html\n    end\n\n    if free_vars = @def.free_vars\n      io << \" forall \"\n      free_vars.join(io, \", \")\n    end\n\n    io\n  end\n\n  def arg_to_html(arg : Arg, io, html : HTMLOption = :all)\n    if arg.external_name != arg.name\n      if name = arg.external_name.presence\n        name = Symbol.quote_for_named_argument(name)\n        if html.none?\n          io << name\n        else\n          HTML.escape name, io\n        end\n      else\n        io << \"_\"\n      end\n      io << ' '\n    end\n\n    io << arg.name\n\n    if restriction = arg.restriction\n      io << \" : \"\n      node_to_html restriction, io, html: html\n    elsif type = arg.type?\n      io << \" : \"\n      @type.type_to_html type, io, html: html\n    end\n\n    if default_value = arg.default_value\n      io << \" = \"\n      if html.highlight?\n        io << SyntaxHighlighter::HTML.highlight!(default_value.to_s)\n      else\n        io << default_value\n      end\n    end\n  end\n\n  def node_to_html(node, io, html : HTMLOption = :all)\n    @type.node_to_html node, io, html: html\n  end\n\n  def must_be_included?\n    @generator.must_include? @def\n  end\n\n  def has_args?\n    !@def.args.empty? || @def.double_splat || @def.block_arg || @def.block_arity\n  end\n\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"html_id\", id\n      builder.field \"name\", name\n      builder.field \"real_name\", real_name if real_name\n      builder.field \"doc\", doc unless doc.nil?\n      builder.field \"summary\", formatted_summary unless formatted_summary.nil?\n      builder.field \"abstract\", abstract?\n      builder.field \"visibility\", visibility if visibility\n      builder.field \"args\", args unless args.empty?\n      builder.field \"args_string\", args_to_s unless args.empty?\n      builder.field \"args_html\", args_to_html unless args.empty?\n      builder.field \"location\", location unless location.nil?\n      builder.field @type.lib? ? \"fun\" : \"def\", self.def\n      builder.field \"external_var\", external_var?\n    end\n  end\n\n  def annotations(annotation_type)\n    @def.annotations(annotation_type)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/project_info.cr",
    "content": "module Crystal::Doc\n  class ProjectInfo\n    property! name : String\n    property! version : String\n    property json_config_url : String? = nil\n    property refname : String? = nil\n    property source_url_pattern : String? = nil\n    property canonical_base_url : String? = nil\n    property base_path : String? = nil\n\n    def initialize(@name : String? = nil, @version : String? = nil, @refname : String? = nil, @source_url_pattern : String? = nil)\n    end\n\n    def_equals_and_hash @name, @version, @json_config_url, @refname, @source_url_pattern\n\n    def crystal_stdlib?\n      name == \"Crystal\"\n    end\n\n    def fill_with_defaults\n      unless version?\n        if git_version = ProjectInfo.find_git_version\n          self.version = git_version\n        end\n      end\n\n      if ProjectInfo.git_clean?\n        self.refname ||= ProjectInfo.git_ref(branch: false)\n      end\n\n      unless source_url_pattern\n        if remote = ProjectInfo.git_remote\n          self.source_url_pattern = ProjectInfo.find_source_url_pattern(remote)\n        end\n      end\n\n      unless name? && version?\n        shard_name, shard_version = ProjectInfo.read_shard_properties\n        if shard_name && !name?\n          self.name = shard_name\n        end\n        if shard_version && !version?\n          self.version = shard_version\n        end\n      end\n    end\n\n    def source_url(location : RelativeLocation)\n      refname = self.refname\n      url_pattern = source_url_pattern\n\n      return unless refname && url_pattern\n\n      url = url_pattern % {refname: refname, path: location.filename, filename: File.basename(location.filename), line: location.line_number}\n      url.presence\n    end\n\n    VERSION_TAG = /^v(\\d+[-.][-.a-zA-Z\\d]+)$/\n\n    def self.find_git_version\n      if ref = git_ref(branch: true)\n        if ref.matches?(VERSION_TAG)\n          ref = ref.byte_slice(1)\n        end\n\n        unless git_clean?\n          ref = \"#{ref}-dev\"\n        end\n\n        ref\n      end\n    end\n\n    def self.find_source_url_pattern(remote)\n      if (at_index = remote.index('@')) && (colon_index = remote.index(':')) && at_index < colon_index\n        # SSH URI\n        host = remote[(at_index + 1)...colon_index]\n        path = remote[(colon_index + 1)..]\n      else\n        begin\n          uri = URI.parse(remote)\n        rescue URI::Error\n          return\n        end\n        host = uri.host\n        path = uri.path\n      end\n\n      path = path.strip(\"/\")\n\n      case host\n      when \"github.com\", \"www.github.com\"\n        # GitHub only resolves URLs with the canonical repo name without .git extension.\n        path = path.rchop(\".git\")\n        \"https://github.com/#{path}/blob/%{refname}/%{path}#L%{line}\"\n      when \"gitlab.com\", \"www.gitlab.com\"\n        # GitLab only resolves URLs with the canonical repo name without .git extension.\n        path = path.rchop(\".git\")\n        \"https://gitlab.com/#{path}/blob/%{refname}/%{path}#L%{line}\"\n      when \"bitbucket.com\", \"www.bitbucket.com\"\n        # Bitbucket does resolve URLs the URL with .git extension, but without it\n        # the canonical form and should be preferred.\n        path = path.rchop(\".git\")\n        \"https://bitbucket.com/#{path}/src/%{refname}/%{path}#%{filename}-%{line}\"\n      when \"git.sr.ht\"\n        # On git.sr.ht ~foo/bar and ~foo/bar.git seem to mean different repos.\n        \"https://git.sr.ht/#{path}/tree/%{refname}/%{path}#L%{line}\"\n      else\n        # Unknown remote host, can't determine source url pattern\n      end\n    end\n\n    def self.git_remote\n      # check whether inside git work-tree\n      Crystal::Git.git_command([\"rev-parse\", \"--is-inside-work-tree\"]) || return\n\n      capture = Crystal::Git.git_capture([\"remote\", \"-v\"]) || return\n      remotes = capture.lines.select(&.ends_with?(\" (fetch)\"))\n\n      git_remote = remotes.find(&.starts_with?(\"origin\\t\")) || remotes.first? || return\n\n      start_pos = git_remote.index(\"\\t\")\n      end_pos = git_remote.rindex(\" \")\n      return unless start_pos && end_pos\n      git_remote[(start_pos + 1)...end_pos].presence\n    end\n\n    def self.git_clean?\n      # Use git to determine if index and working directory are clean\n      # In case the command failed to execute or returned error status, return false\n      capture = Crystal::Git.git_capture([\"status\", \"--porcelain\", \"--untracked-files=no\"]) || return false\n\n      # Index is clean if output is empty (and program status is success, checked by git_capture)\n      capture.bytesize == 0\n    end\n\n    def self.git_ref(*, branch)\n      # Check if current HEAD is tagged\n      capture = Crystal::Git.git_capture([\"tag\", \"--points-at\", \"HEAD\"]) || return\n      tags = capture.lines\n      # Return tag if commit is tagged, select first one if multiple\n      if tag = tags.first?\n        return tag\n      end\n\n      if branch\n        # Read current branch name\n        capture = Crystal::Git.git_capture([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]) || return\n\n        if branch_name = capture.strip.presence\n          return branch_name\n        end\n      end\n\n      # Otherwise, return current commit sha\n      capture = Crystal::Git.git_capture([\"rev-parse\", \"HEAD\"]) || return\n\n      if sha = capture.strip.presence\n        return sha\n      end\n    end\n\n    def self.read_shard_properties\n      return {nil, nil} unless File.exists?(\"shard.yml\")\n\n      name = nil\n      version = nil\n\n      # Poor man's YAML reader\n      File.each_line(\"shard.yml\") do |line|\n        if name.nil? && line.starts_with?(\"name:\")\n          end_pos = line.byte_index(\"#\") || line.bytesize\n          name = line.byte_slice(5, end_pos - 5).strip.strip(%(\"'))\n        elsif version.nil? && line.starts_with?(\"version:\")\n          end_pos = line.byte_index(\"#\") || line.bytesize\n          version = line.byte_slice(8, end_pos - 8).strip.strip(%(\"'))\n        elsif version && name\n          break\n        end\n      end\n\n      return name.presence, version.presence\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/relative_location.cr",
    "content": "class Crystal::Doc::RelativeLocation\n  include Comparable(self)\n  property show_line_number : Bool = false\n\n  # This property is only used to keep backwards compatibility in JSON output.\n  property url : String?\n\n  getter filename, line_number\n\n  def initialize(@filename : String, @line_number : Int32)\n  end\n\n  def_equals_and_hash @filename, @line_number\n\n  def filename_in_project\n    filename.lchop(\"src/\")\n  end\n\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"filename\", filename\n      builder.field \"line_number\", line_number\n      builder.field \"url\", url\n    end\n  end\n\n  def <=>(other : self)\n    cmp = filename <=> other.filename\n    return cmp unless cmp == 0\n    line_number <=> other.line_number\n  end\n\n  def self.from(node : ASTNode, base_dir : String)\n    if location = node.location\n      from(location, base_dir)\n    end\n  end\n\n  def self.from(location : Location, base_dir : String)\n    filename = location.filename\n    if filename.is_a?(VirtualFile)\n      location = filename.expanded_location || return\n      filename = location.filename\n    end\n\n    return unless filename.is_a?(String)\n    return unless filename.starts_with? base_dir\n    filename = filename[(base_dir.size + 1)..]\n\n    new(filename, location.line_number)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/templates.cr",
    "content": "require \"ecr/macros\"\n\nmodule Crystal::Doc\n  SVG_DEFS = <<-SVG\n  <svg class=\"hidden\">\n    <symbol id=\"octicon-link\" viewBox=\"0 0 16 16\">\n      <path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path>\n    </symbol>\n  </svg>\n  SVG\n\n  SIDEBAR_BUTTON = <<-HTML\n  <input type=\"checkbox\" id=\"sidebar-btn\">\n  <label for=\"sidebar-btn\" id=\"sidebar-btn-label\">\n    <svg class=\"open\" xmlns=\"http://www.w3.org/2000/svg\" height=\"2em\" width=\"2em\" viewBox=\"0 0 512 512\"><title>Open Sidebar</title><path fill=\"currentColor\" d=\"M80 96v64h352V96H80zm0 112v64h352v-64H80zm0 112v64h352v-64H80z\"></path></svg>\n    <svg class=\"close\" xmlns=\"http://www.w3.org/2000/svg\" width=\"2em\" height=\"2em\" viewBox=\"0 0 512 512\"><title>Close Sidebar</title><path fill=\"currentColor\" d=\"m118.6 73.4-45.2 45.2L210.7 256 73.4 393.4l45.2 45.2L256 301.3l137.4 137.3 45.2-45.2L301.3 256l137.3-137.4-45.2-45.2L256 210.7Z\"></path></svg>\n  </label>\n  HTML\n\n  def self.anchor_link(anchor : String)\n    anchor = anchor.downcase.gsub(' ', '-')\n\n    <<-HTML\n    <a id=\"#{anchor}\" class=\"anchor\" href=\"##{anchor}\">\n      <svg class=\"octicon-link\" aria-hidden=\"true\">\n        <use href=\"#octicon-link\"/>\n      </svg>\n    </a>\n    HTML\n  end\n\n  record TypeTemplate, type : Type, types : Array(Type), project_info : ProjectInfo do\n    ECR.def_to_s \"#{__DIR__}/html/type.html\"\n  end\n\n  record ListItemsTemplate, types : Array(Type), current_type : Type? do\n    ECR.def_to_s \"#{__DIR__}/html/_list_items.html\"\n  end\n\n  record MethodSummaryTemplate, title : String, methods : Array(Method) | Array(Macro) do\n    ECR.def_to_s \"#{__DIR__}/html/_method_summary.html\"\n  end\n\n  record MethodDetailTemplate, title : String, methods : Array(Method) | Array(Macro) do\n    ECR.def_to_s \"#{__DIR__}/html/_method_detail.html\"\n  end\n\n  record MethodsInheritedTemplate, type : Type, ancestor : Type, methods : Array(Method), label : String do\n    ECR.def_to_s \"#{__DIR__}/html/_methods_inherited.html\"\n  end\n\n  record MacrosInheritedTemplate, type : Type, ancestor : Type, macros : Array(Macro) do\n    ECR.def_to_s \"#{__DIR__}/html/_macros_inherited.html\"\n  end\n\n  record OtherTypesTemplate, title : String, type : Type, other_types : Array(Type) do\n    ECR.def_to_s \"#{__DIR__}/html/_other_types.html\"\n  end\n\n  record MainTemplate, body : String, types : Array(Type), project_info : ProjectInfo, base_path : String? = nil do\n    ECR.def_to_s \"#{__DIR__}/html/main.html\"\n  end\n\n  record HeadTemplate, project_info : ProjectInfo, current_type : Type? do\n    def base_path\n      if current_type = self.current_type\n        current_type.path_to \"\"\n      else\n        \"\"\n      end\n    end\n\n    ECR.def_to_s \"#{__DIR__}/html/_head.html\"\n  end\n\n  record SidebarTemplate, project_info : ProjectInfo, types : Array(Type), current_type : Type? do\n    ECR.def_to_s \"#{__DIR__}/html/_sidebar.html\"\n  end\n\n  record Error404Template do\n    ECR.def_to_s \"#{__DIR__}/html/404.html\"\n  end\n\n  struct JsTypeTemplate\n    ECR.def_to_s \"#{__DIR__}/html/js/doc.js\"\n  end\n\n  struct JsSearchTemplate\n    ECR.def_to_s \"#{__DIR__}/html/js/_search.js\"\n  end\n\n  struct JsNavigatorTemplate\n    ECR.def_to_s \"#{__DIR__}/html/js/_navigator.js\"\n  end\n\n  struct JsVersionsTemplate\n    ECR.def_to_s \"#{__DIR__}/html/js/_versions.js\"\n  end\n\n  struct JsUsageModal\n    ECR.def_to_s \"#{__DIR__}/html/js/_usage-modal.js\"\n  end\n\n  struct StyleTemplate\n    ECR.def_to_s \"#{__DIR__}/html/css/style.css\"\n  end\n\n  record SitemapTemplate, types : Array(Type), base_url : String, priority : String, changefreq : String do\n    ECR.def_to_s \"#{__DIR__}/html/sitemap.xml\"\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/to_json.cr",
    "content": "class Crystal::Arg\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"name\", name\n      builder.field \"doc\", doc unless doc.nil?\n      builder.field \"default_value\", default_value.to_s unless default_value.nil?\n      builder.field \"external_name\", external_name.to_s\n      builder.field \"restriction\", restriction.to_s\n    end\n  end\nend\n\nclass Crystal::Def\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"name\", name\n      builder.field \"args\", args unless args.empty?\n      builder.field \"double_splat\", double_splat unless double_splat.nil?\n      builder.field \"splat_index\", splat_index unless splat_index.nil?\n      builder.field \"yields\", block_arity unless block_arity.nil?\n      builder.field \"block_arity\", block_arity unless block_arity.nil?\n      builder.field \"block_arg\", block_arg unless block_arg.nil?\n      builder.field \"return_type\", return_type.to_s unless return_type.nil?\n      builder.field \"visibility\", visibility.to_s\n      builder.field \"body\", body.to_s\n    end\n  end\nend\n\nclass Crystal::Macro\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"name\", name\n      builder.field \"args\", args unless args.empty?\n      builder.field \"double_splat\", double_splat unless double_splat.nil?\n      builder.field \"splat_index\", splat_index unless splat_index.nil?\n      builder.field \"block_arg\", block_arg unless block_arg.nil?\n      builder.field \"visibility\", visibility.to_s\n      builder.field \"body\", body.to_s\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc/type.cr",
    "content": "require \"./item\"\n\nclass Crystal::Doc::Type\n  include Item\n\n  PSEUDO_CLASS_PREFIX = \"CRYSTAL_PSEUDO__\"\n  PSEUDO_CLASS_NOTE   = <<-DOC\n\n    NOTE: This is a pseudo-class provided directly by the Crystal compiler.\n    It cannot be reopened nor overridden.\n    DOC\n\n  getter type : Crystal::Type\n\n  def initialize(@generator : Generator, type : Crystal::Type)\n    @type = type.devirtualize\n  end\n\n  def kind\n    case @type\n    when Const\n      \"const\"\n    when .extern_union?\n      \"union\"\n    when .struct?\n      \"struct\"\n    when .class?, .metaclass?\n      \"class\"\n    when .module?\n      \"module\"\n    when AliasType\n      \"alias\"\n    when EnumType\n      \"enum\"\n    when NoReturnType, VoidType\n      \"struct\"\n    when AnnotationType\n      \"annotation\"\n    when LibType\n      \"lib\"\n    when TypeDefType\n      \"type\"\n    else\n      raise \"Unhandled type in `kind`: #{@type}\"\n    end\n  end\n\n  def name\n    case type = @type\n    when Program\n      \"Top Level Namespace\"\n    when NamedType\n      if @generator.project_info.crystal_stdlib?\n        type.name.lchop(PSEUDO_CLASS_PREFIX)\n      else\n        type.name\n      end\n    when NoReturnType\n      \"NoReturn\"\n    when VoidType\n      \"Void\"\n    when Const\n      type.name\n    when GenericInstanceType\n      type.generic_type.name\n    when TypeParameter\n      type.name\n    when TypeSplat\n      \"*#{type.splatted_type}\"\n    else\n      raise \"Unhandled type in `name`: #{@type}\"\n    end\n  end\n\n  def type_vars\n    case type = @type\n    when GenericType\n      type.type_vars\n    else\n      nil\n    end\n  end\n\n  def abstract?\n    @type.abstract?\n  end\n\n  def visibility\n    @type.private? ? \"private\" : nil\n  end\n\n  def parents_of?(type)\n    return false unless type\n\n    while type = type.namespace\n      return true if type.full_name == full_name\n    end\n\n    false\n  end\n\n  def current?(type)\n    return false unless type\n\n    parents_of?(type) || type.full_name == full_name\n  end\n\n  def superclass\n    case type = @type\n    when ClassType\n      superclass = type.superclass unless ast_node?\n    when GenericClassInstanceType\n      superclass = type.superclass\n    end\n\n    if superclass\n      @generator.type(superclass)\n    else\n      nil\n    end\n  end\n\n  def ancestors\n    ancestors = [] of self\n\n    unless ast_node?\n      @type.ancestors.each do |ancestor|\n        doc_type = @generator.type(ancestor)\n        ancestors << doc_type\n        break if ancestor == @generator.program.object || doc_type.ast_node?\n      end\n    end\n\n    ancestors\n  end\n\n  def ast_node?\n    type = @type\n    type.is_a?(ClassType) && type.full_name == \"Crystal::Macros::ASTNode\"\n  end\n\n  def locations\n    @generator.relative_locations(@type)\n  end\n\n  def program?\n    @type.is_a?(Program)\n  end\n\n  def program\n    @generator.type(@type.program)\n  end\n\n  def enum?\n    @type.is_a?(EnumType)\n  end\n\n  def alias?\n    @type.is_a?(AliasType)\n  end\n\n  def const?\n    @type.is_a?(Const)\n  end\n\n  def type_def?\n    @type.is_a?(TypeDefType)\n  end\n\n  def lib?\n    @type.is_a?(LibType)\n  end\n\n  def fun_def?\n    @type.is_a?(FunDef)\n  end\n\n  def alias_definition\n    alias_def = @type.as?(AliasType).try(&.aliased_type)\n    alias_def\n  end\n\n  def formatted_alias_definition\n    type_to_html alias_definition.as(Crystal::Type)\n  end\n\n  def type_definition\n    @type.as?(TypeDefType).try(&.typedef)\n  end\n\n  def formatted_type_definition\n    type_to_html type_definition.as(Crystal::Type)\n  end\n\n  @types : Array(Type)?\n\n  def types\n    @types ||= @generator.collect_subtypes(@type)\n  end\n\n  @instance_methods : Array(Method)?\n\n  def instance_methods\n    @instance_methods ||= begin\n      case @type\n      when Program\n        [] of Method\n      else\n        defs = [] of Method\n        @type.defs.try &.each do |def_name, defs_with_metadata|\n          defs_with_metadata.each do |def_with_metadata|\n            next if !def_with_metadata.def.visibility.public? && !showdoc?(def_with_metadata.def)\n            next unless @generator.must_include? def_with_metadata.def\n\n            defs << method(def_with_metadata.def, false)\n          end\n        end\n        defs.sort_by! { |x| sort_order(x) }\n      end\n    end\n  end\n\n  @external_vars : Array(Method)?\n\n  def external_vars\n    @external_vars ||= begin\n      case @type\n      when LibType\n        defs = [] of Method\n        @type.defs.try &.each do |def_name, defs_with_metadata|\n          defs_with_metadata.each do |def_with_metadata|\n            next unless (ext = def_with_metadata.def).is_a?(External)\n            next if !ext.external_var? || ext.name.ends_with?(\"=\")\n            next unless @generator.must_include? ext\n\n            defs << method(ext, false)\n          end\n        end\n        defs.sort_by! { |x| sort_order(x) }\n      else\n        [] of Method\n      end\n    end\n  end\n\n  @functions : Array(Method)?\n\n  def functions\n    @functions ||= begin\n      case @type\n      when LibType\n        defs = [] of Method\n        @type.defs.try &.each do |def_name, defs_with_metadata|\n          defs_with_metadata.each do |def_with_metadata|\n            next unless (ext = def_with_metadata.def).is_a?(External)\n            next if ext.external_var?\n            next unless @generator.must_include? def_with_metadata.def\n\n            defs << method(def_with_metadata.def, false)\n          end\n        end\n        defs.sort_by! { |x| sort_order(x) }\n      else\n        [] of Method\n      end\n    end\n  end\n\n  private def showdoc?(adef)\n    @generator.showdoc?(adef.doc.try &.strip)\n  end\n\n  private def sort_order(item)\n    # Sort operators first, then alphanumeric (case-insensitive).\n    {item.name[0].alphanumeric? ? 1 : 0, item.name.downcase}\n  end\n\n  @class_methods : Array(Method)?\n\n  def all_class_methods\n    @class_methods ||= begin\n      class_methods = [] of Method\n      @type.metaclass.defs.try &.each_value do |defs_with_metadata|\n        defs_with_metadata.each do |def_with_metadata|\n          a_def = def_with_metadata.def\n          next if !def_with_metadata.def.visibility.public? && !showdoc?(def_with_metadata.def)\n\n          body = a_def.body\n\n          # Skip auto-generated allocate method\n          next if body.is_a?(Crystal::Primitive) && body.name == \"allocate\"\n\n          if @generator.must_include? a_def\n            class_methods << method(a_def, true)\n          end\n        end\n      end\n      class_methods.sort_by! { |x| sort_order(x) }\n    end\n  end\n\n  def class_methods\n    all_class_methods - constructors\n  end\n\n  def constructors\n    all_class_methods.select &.constructor?\n  end\n\n  @macros : Array(Macro)?\n\n  def macros\n    @macros ||= begin\n      macros = [] of Macro\n      @type.metaclass.macros.try &.each_value do |the_macros|\n        the_macros.each do |a_macro|\n          next if !a_macro.visibility.public? && !showdoc?(a_macro)\n\n          if @generator.must_include? a_macro\n            macros << self.macro(a_macro)\n          end\n        end\n      end\n      macros.sort_by! { |x| sort_order(x) }\n    end\n  end\n\n  @constants : Array(Constant)?\n\n  def constants\n    @constants ||= @generator.collect_constants(self)\n  end\n\n  @included_modules : Array(Type)?\n\n  def included_modules\n    @included_modules ||= begin\n      included_modules = [] of Type\n      @type.parents.try &.each do |parent|\n        if parent.module?\n          included_modules << @generator.type(parent)\n        end\n      end\n      included_modules.sort_by! &.full_name.downcase\n    end\n  end\n\n  @extended_modules : Array(Type)?\n\n  def extended_modules\n    @extended_modules ||= begin\n      extended_modules = [] of Type\n      @type.metaclass.parents.try &.each do |parent|\n        if parent.module?\n          extended_modules << @generator.type(parent)\n        end\n      end\n      extended_modules.sort_by! &.full_name.downcase\n    end\n  end\n\n  @subclasses : Array(Type)?\n\n  def subclasses\n    @subclasses ||= begin\n      case type = @type\n      when .metaclass?\n        [] of Type\n      when ClassType\n        subclasses = [] of Type\n        type.subclasses.each do |subclass|\n          case subclass\n          when GenericClassInstanceType\n            next\n          when NonGenericClassType\n            next if subclass.extern?\n          end\n\n          next unless @generator.must_include?(subclass)\n\n          subclasses << @generator.type(subclass)\n        end\n        subclasses.sort_by! &.full_name.downcase\n      else\n        [] of Type\n      end\n    end\n  end\n\n  @including_types : Array(Type)?\n\n  def including_types\n    @including_types ||= begin\n      case type = @type\n      when NonGenericModuleType\n        gather_including_types type\n      when GenericModuleType\n        gather_including_types type\n      else\n        [] of Type\n      end\n    end\n  end\n\n  private def gather_including_types(type)\n    including_types = [] of Type\n    type.raw_including_types.try &.each do |subtype|\n      if @generator.must_include? subtype\n        including_types << @generator.type(subtype)\n      end\n    end\n    including_types.uniq!.sort_by! &.full_name.downcase\n  end\n\n  def namespace\n    namespace = type.namespace\n    if namespace.is_a?(Program)\n      nil\n    else\n      @generator.type(namespace)\n    end\n  end\n\n  def full_name\n    String.build { |io| full_name(io) }\n  end\n\n  def full_name(io)\n    full_name_without_type_vars(io)\n    append_type_vars io\n  end\n\n  def full_name_without_type_vars\n    String.build { |io| full_name_without_type_vars(io) }\n  end\n\n  def full_name_without_type_vars(io)\n    if namespace = self.namespace\n      namespace.full_name_without_type_vars(io)\n      io << \"::\"\n    end\n    io << name\n  end\n\n  def path\n    if program?\n      \"toplevel.html\"\n    elsif namespace = self.namespace\n      \"#{namespace.dir}/#{name}.html\"\n    else\n      \"#{name}.html\"\n    end\n  end\n\n  def path_from(type)\n    if type\n      type.path_to(self)\n    else\n      path\n    end\n  end\n\n  def path_to(filename : String)\n    \"#{\"../\" * nesting}#{filename}\"\n  end\n\n  def path_to(type : Type)\n    if type.const?\n      namespace = type.namespace || @generator.program_type\n      \"#{path_to(namespace)}##{type.name}\"\n    else\n      path_to(type.path)\n    end\n  end\n\n  def link_from(type : Type)\n    type.type_to_html self\n  end\n\n  def dir\n    if namespace = self.namespace\n      \"#{namespace.dir}/#{name}\"\n    else\n      name.to_s\n    end\n  end\n\n  def nesting\n    if namespace = self.namespace\n      1 + namespace.nesting\n    else\n      0\n    end\n  end\n\n  def doc\n    if (t = type).is_a?(NamedType) && t.name.starts_with?(PSEUDO_CLASS_PREFIX)\n      \"#{@type.doc}#{PSEUDO_CLASS_NOTE}\"\n    else\n      @type.doc\n    end\n  end\n\n  def lookup_path(path_or_names : Path | Array(String))\n    match = @type.lookup_path(path_or_names)\n    return unless match.is_a?(Crystal::Type)\n\n    @generator.type(match)\n  end\n\n  def lookup_path(full_path : String)\n    global = full_path.starts_with?(\"::\")\n    full_path = full_path[2..-1] if global\n    path = Path.new(full_path.split(\"::\"), global: global)\n    lookup_path(path)\n  end\n\n  def lookup_method(name)\n    lookup_in_methods instance_methods, name\n  end\n\n  def lookup_method(name, args_size)\n    lookup_in_methods instance_methods, name, args_size\n  end\n\n  def lookup_class_method(name)\n    lookup_in_methods all_class_methods, name\n  end\n\n  def lookup_class_method(name, args_size)\n    lookup_in_methods all_class_methods, name, args_size\n  end\n\n  def lookup_macro(name)\n    lookup_in_methods macros, name\n  end\n\n  def lookup_macro(name, args_size)\n    lookup_in_methods macros, name, args_size\n  end\n\n  private def lookup_in_methods(methods, name)\n    methods.find { |method| method.name == name }\n  end\n\n  private def lookup_in_methods(methods, name, args_size)\n    if args_size\n      methods.find { |method| method.name == name && method.args.size == args_size }\n    else\n      methods = methods.select { |method| method.name == name }\n      methods.find(&.args.empty?) || methods.first?\n    end\n  end\n\n  def method(a_def, class_method)\n    @generator.method(self, a_def, class_method)\n  end\n\n  def macro(a_macro)\n    @generator.macro(self, a_macro)\n  end\n\n  def to_s(io : IO) : Nil\n    io << name\n    append_type_vars io\n  end\n\n  private def append_type_vars(io : IO) : Nil\n    type = @type\n    if type_vars = type_vars()\n      io << '('\n      io << \"**\" if type.is_a?(GenericType) && type.double_variadic?\n      type_vars.each_with_index do |type_var, i|\n        io << \", \" if i > 0\n        io << '*' if type.is_a?(GenericType) && type.splat_index == i\n        io << type_var\n      end\n      io << ')'\n    end\n  end\n\n  def node_to_html(node)\n    String.build { |io| node_to_html node, io }\n  end\n\n  def node_to_html(node : Path, io, html : HTMLOption = :all)\n    match = lookup_path(node)\n    if match\n      # If the path is global, search a local path and\n      # see if there's a different match. If not, we can safely\n      # remove the `::` as a prefix (harder to read)\n      remove_colons = false\n      if node.global?\n        node.global = false\n        remove_colons = lookup_path(node) == match\n        node.global = true unless remove_colons\n      end\n\n      type_to_html match, io, node.to_s, html: html\n      node.global = true if remove_colons\n    else\n      io << node\n    end\n  end\n\n  def node_to_html(node : Generic, io, html : HTMLOption = :all)\n    node_to_html node.name, io, html: html\n    io << '('\n    node.type_vars.join(io, \", \") do |type_var|\n      node_to_html type_var, io, html: html\n    end\n    if (named_args = node.named_args) && !named_args.empty?\n      io << \", \" unless node.type_vars.empty?\n      named_args.join(io, \", \") do |entry|\n        Symbol.quote_for_named_argument(io, entry.name)\n        io << \": \"\n        node_to_html entry.value, io, html: html\n      end\n    end\n    io << ')'\n  end\n\n  def node_to_html(node : ProcNotation, io, html : HTMLOption = :all)\n    if inputs = node.inputs\n      inputs.join(io, \", \") do |input|\n        node_to_html input, io, html: html\n      end\n    end\n    io << \" -> \"\n    if output = node.output\n      node_to_html output, io, html: html\n    end\n  end\n\n  def node_to_html(node : Union, io, html : HTMLOption = :all)\n    # See if it's a nilable type\n    if node.types.size == 2\n      # See if first type is Nil\n      if nil_type?(node.types[0])\n        return nilable_type_to_html node.types[1], io, html: html\n      elsif nil_type?(node.types[1])\n        return nilable_type_to_html node.types[0], io, html: html\n      end\n    end\n\n    node.types.join(io, \" | \") do |elem|\n      node_to_html elem, io, html: html\n    end\n  end\n\n  private def nilable_type_to_html(node : ASTNode, io, html)\n    node_to_html node, io, html: html\n    io << '?'\n  end\n\n  private def nilable_type_to_html(type : Crystal::Type, io, text, html)\n    type_to_html(type, io, text, html: html)\n    io << '?'\n  end\n\n  def nil_type?(node : ASTNode)\n    return false unless node.is_a?(Path)\n\n    match = lookup_path(node)\n    !!match.try &.type == @generator.program.nil_type\n  end\n\n  def node_to_html(node, io, html : HTMLOption = :all)\n    if html.highlight?\n      io << SyntaxHighlighter::HTML.highlight!(node.to_s)\n    else\n      io << node\n    end\n  end\n\n  def node_to_html(node : Underscore, io, html : HTMLOption = :all)\n    io << '_'\n  end\n\n  def type_to_html(type)\n    type = type.type if type.is_a?(Type)\n    String.build { |io| type_to_html(type, io) }\n  end\n\n  def type_to_html(type : Crystal::UnionType, io, text = nil, html : HTMLOption = :all)\n    has_type_splat = type.union_types.any?(TypeSplat)\n\n    if !has_type_splat && type.union_types.size == 2\n      if type.union_types[0].nil_type?\n        return nilable_type_to_html(type.union_types[1], io, text, html)\n      elsif type.union_types[1].nil_type?\n        return nilable_type_to_html(type.union_types[0], io, text, html)\n      end\n    end\n\n    if has_type_splat\n      io << \"Union(\"\n      separator = \", \"\n    else\n      separator = \" | \"\n    end\n\n    type.union_types.join(io, separator) do |union_type|\n      type_to_html union_type, io, text, html: html\n    end\n\n    io << ')' if has_type_splat\n  end\n\n  def type_to_html(type : Crystal::ProcInstanceType, io, text = nil, html : HTMLOption = :all)\n    type.arg_types.join(io, \", \") do |arg_type|\n      type_to_html arg_type, io, html: html\n    end\n    io << \" -> \"\n    return_type = type.return_type\n    type_to_html return_type, io, html: html unless return_type.void?\n  end\n\n  def type_to_html(type : Crystal::TupleInstanceType, io, text = nil, html : HTMLOption = :all)\n    io << '{'\n    type.tuple_types.join(io, \", \") do |tuple_type|\n      type_to_html tuple_type, io, html: html\n    end\n    io << '}'\n  end\n\n  def type_to_html(type : Crystal::NamedTupleInstanceType, io, text = nil, html : HTMLOption = :all)\n    io << '{'\n    type.entries.join(io, \", \") do |entry|\n      Symbol.quote_for_named_argument(io, entry.name)\n      io << \": \"\n      type_to_html entry.type, io, html: html\n    end\n    io << '}'\n  end\n\n  def type_to_html(type : Crystal::GenericInstanceType, io, text = nil, html : HTMLOption = :all)\n    has_link_in_type_vars = type.type_vars.any? { |(name, type_var)| type_has_link? type_var.as?(Var).try(&.type) || type_var }\n    generic_type = @generator.type(type.generic_type)\n    must_be_included = generic_type.must_be_included?\n\n    if must_be_included && html.links?\n      io << %(<a href=\")\n      io << generic_type.path_from(self)\n      io << %(\">)\n    end\n\n    if text\n      io << text\n    else\n      generic_type.full_name_without_type_vars(io)\n    end\n\n    io << \"</a>\" if must_be_included && html.links? && has_link_in_type_vars\n\n    io << '('\n    type.type_vars.values.join(io, \", \") do |type_var|\n      case type_var\n      when Var\n        type_to_html type_var.type, io, html: html\n      else\n        type_to_html type_var, io, html: html\n      end\n    end\n    io << ')'\n\n    io << \"</a>\" if must_be_included && html.links? && !has_link_in_type_vars\n  end\n\n  def type_to_html(type : Crystal::VirtualType, io, text = nil, html : HTMLOption = :all)\n    type_to_html type.base_type, io, text, html: html\n  end\n\n  def type_to_html(type : Crystal::Type, io, text = nil, html : HTMLOption = :all)\n    type = @generator.type(type)\n    if type.must_be_included?\n      if html.links?\n        io << %(<a href=\")\n        io << type.path_from(self)\n        io << %(\">)\n      end\n      if text\n        io << text\n      else\n        type.full_name(io)\n      end\n      if html.links?\n        io << \"</a>\"\n      end\n    else\n      if text\n        io << text\n      else\n        type.full_name(io)\n      end\n    end\n  end\n\n  def type_to_html(type : Type, io, text = nil, html : HTMLOption = :all)\n    type_to_html type.type, io, text, html: html\n  end\n\n  def type_to_html(type : ASTNode, io, text = nil, html : HTMLOption = :all)\n    type.to_s io\n  end\n\n  def type_has_link?(type : Crystal::UnionType)\n    type.union_types.any? { |type| type_has_link? type }\n  end\n\n  def type_has_link?(type : Crystal::ProcInstanceType)\n    type.arg_types.any? { |type| type_has_link? type } ||\n      type_has_link? type.return_type\n  end\n\n  def type_has_link?(type : Crystal::TupleInstanceType)\n    type.tuple_types.any? { |type| type_has_link? type }\n  end\n\n  def type_has_link?(type : Crystal::NamedTupleInstanceType)\n    type.entries.any? { |entry| type_has_link? entry.type }\n  end\n\n  def type_has_link?(type : Crystal::GenericInstanceType)\n    @generator.type(type.generic_type).must_be_included? ||\n      type.type_vars.any? { |(name, type_var)| type_has_link? type_var.as?(Var).try(&.type) || type_var }\n  end\n\n  def type_has_link?(type : Crystal::Type)\n    @generator.type(type.devirtualize).must_be_included?\n  end\n\n  def type_has_link?(type : Crystal::TypeParameter | ASTNode)\n    false\n  end\n\n  def must_be_included?\n    @generator.must_include? self\n  end\n\n  def superclass_hierarchy\n    hierarchy = [self]\n    superclass = self.superclass\n    while superclass\n      hierarchy << superclass\n      superclass = superclass.superclass\n    end\n    String.build do |io|\n      io << %(<ul class=\"superclass-hierarchy\">)\n      hierarchy.each do |type|\n        io << %(<li class=\"superclass\">)\n        type_to_html type, io\n        io << \"</li>\"\n      end\n      io << \"</ul>\"\n    end\n  end\n\n  def html_id\n    \"#{@generator.project_info.name}/\" + (\n      if program?\n        \"toplevel\"\n      elsif namespace = self.namespace\n        \"#{namespace.dir}/#{name}\"\n      else\n        \"#{name}\"\n      end\n    )\n  end\n\n  delegate to_s, inspect, to: @type\n\n  def to_json(builder : JSON::Builder)\n    builder.object do\n      builder.field \"html_id\", html_id\n      builder.field \"path\", path\n      builder.field \"kind\", kind\n      builder.field \"full_name\", full_name\n      builder.field \"name\", name\n      builder.field \"abstract\", abstract?\n      builder.field \"superclass\" { superclass.try &.to_json_simple(builder) } unless superclass.nil?\n      unless ancestors.empty?\n        builder.field \"ancestors\" do\n          builder.array do\n            ancestors.each &.to_json_simple(builder)\n          end\n        end\n      end\n      builder.field \"locations\", locations\n      builder.field \"repository_name\", @generator.project_info.name\n      builder.field \"program\", program?\n      builder.field \"enum\", enum?\n      builder.field \"alias\", alias?\n      builder.field \"aliased\", alias_definition.to_s if alias?\n      builder.field \"aliased_html\", formatted_alias_definition if alias?\n      builder.field \"const\", const?\n      builder.field \"constants\", constants unless constants.empty?\n      unless included_modules.empty?\n        builder.field \"included_modules\" do\n          builder.array do\n            included_modules.each &.to_json_simple(builder)\n          end\n        end\n      end\n      unless extended_modules.empty?\n        builder.field \"extended_modules\" do\n          builder.array do\n            extended_modules.each &.to_json_simple(builder)\n          end\n        end\n      end\n      unless subclasses.empty?\n        builder.field \"subclasses\" do\n          builder.array do\n            subclasses.each &.to_json_simple(builder)\n          end\n        end\n      end\n      unless including_types.empty?\n        builder.field \"including_types\" do\n          builder.array do\n            including_types.each &.to_json_simple(builder)\n          end\n        end\n      end\n      builder.field \"namespace\" { namespace.try &.to_json_simple(builder) } unless namespace.nil?\n      builder.field \"doc\", doc unless doc.nil?\n      builder.field \"summary\", formatted_summary unless formatted_summary.nil?\n      builder.field \"class_methods\", class_methods unless class_methods.empty?\n      builder.field \"constructors\", constructors unless constructors.empty?\n      builder.field \"instance_methods\", instance_methods unless instance_methods.empty?\n      builder.field \"macros\", macros unless macros.empty?\n      builder.field \"types\", types unless types.empty?\n    end\n  end\n\n  def to_json_simple(builder : JSON::Builder)\n    builder.object do\n      builder.field \"html_id\", html_id\n      builder.field \"kind\", kind\n      builder.field \"full_name\", full_name\n      builder.field \"name\", name\n    end\n  end\n\n  def annotations(annotation_type)\n    @type.annotations(annotation_type)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/doc.cr",
    "content": "require \"./doc/*\"\n"
  },
  {
    "path": "src/compiler/crystal/tools/expand.cr",
    "content": "require \"./typed_def_processor\"\nrequire \"./implementations\"\n\nmodule Crystal\n  struct ExpandResult\n    include JSON::Serializable\n\n    property status : String\n    property message : String\n    property expansions : Array(Expansion)?\n\n    def initialize(@status, @message)\n    end\n\n    def to_text(io)\n      io.puts message\n      expansions.try &.each_with_index do |expansion, i|\n        io.puts \"expansion #{i + 1}:\"\n        io << \"   \"\n        io.puts expansion.original_source.lines(chomp: false).join \"   \"\n        io.puts\n        expansion.expanded_sources.zip(expansion.expanded_macros)\n          .each_with_index do |(expanded_source, expanded_macro), j|\n            expanded_macro.each do |a_macro|\n              name = a_macro[:name]\n              impl = a_macro[:implementation]\n              io.puts \"# expand macro '#{name}' (#{impl.filename}:#{impl.line}:#{impl.column})\"\n              # TODO: When `impl.expands` is not `nil`, how shows this?\n            end\n            io << \"~> \"\n            io.puts expanded_source.lines(chomp: false).join \"   \"\n            io.puts\n          end\n      end\n    end\n\n    struct Expansion\n      include JSON::Serializable\n      alias MacroImplementation = {name: String, implementation: ImplementationTrace}\n\n      property original_source : String\n      property expanded_sources : Array(String)\n      property expanded_macros : Array(Array(MacroImplementation))\n\n      def initialize(@original_source, @expanded_sources, @expanded_macros)\n      end\n\n      def self.build(original_node)\n        original_source = ast_to_s original_node\n\n        transformer = ExpandTransformer.new\n        expanded_node = transformer.transform original_node\n\n        expanded_sources = [] of String\n        expanded_macros = [] of Array(MacroImplementation)\n\n        while transformer.expanded?\n          expanded_sources << ast_to_s expanded_node\n          expanded_macros << transformer.macro_calls\n            .compact_map do |call|\n              if (a_macro = call.expanded_macro) && (location = a_macro.location)\n                name = a_macro.name\n                # Fix name like `mapping` to `JSON.mapping`\n                name = \"#{call.obj}.#{name}\" if call.obj.is_a?(Path)\n\n                implementation = ImplementationTrace.build location\n                MacroImplementation.new(\n                  name: name,\n                  implementation: implementation)\n              end\n            end\n\n          transformer.expanded = false\n          transformer.macro_calls.clear\n          expanded_node = transformer.transform expanded_node\n        end\n\n        Expansion.new original_source, expanded_sources, expanded_macros\n      end\n\n      private def self.ast_to_s(node)\n        source = String.build { |io| node.to_s(io, emit_doc: true) }\n\n        # Re-indentation is needed for `MacroIf` and `MacroFor`, because they have\n        # `MacroBody`, which is sub string of source code, in other words they may\n        # contain source code's indent.\n        return source unless node.is_a?(MacroIf) || node.is_a?(MacroFor)\n\n        indent = node.location.not_nil!.column_number - 1\n        source.lines(chomp: false).map do |line|\n          i = 0\n          line.each_char do |c|\n            break unless c.ascii_whitespace? && i < indent\n            i += 1\n          end\n          line[{i, indent}.min..-1]\n        end.join\n      end\n    end\n  end\n\n  class ExpandVisitor < Visitor\n    include TypedDefProcessor\n\n    def initialize(@target_location : Location)\n      @found_nodes = [] of ASTNode\n      @in_defs = false\n      @message = \"no expansion found\"\n    end\n\n    def process(result : Compiler::Result)\n      @in_defs = true\n      process_result result\n      @in_defs = false\n\n      result.node.accept(self)\n\n      if @found_nodes.empty?\n        ExpandResult.new(\"failed\", @message)\n      else\n        res = ExpandResult.new(\"ok\", \"#{@found_nodes.size} expansion#{@found_nodes.size > 1 ? \"s\" : \"\"} found\")\n        res.expansions = @found_nodes.map { |node| ExpandResult::Expansion.build(node) }\n        res\n      end\n    end\n\n    def visit(node : Def | FunDef)\n      @in_defs && contains_target(node)\n    end\n\n    def visit(node : Call)\n      if loc_start = node.location\n        # If node.obj (a.k.a. an receiver) is a Path, it may be macro call and node.obj has no expansion surely.\n        # Otherwise, we cannot decide node.obj has no expansion.\n        loc_start = node.name_location unless node.obj.is_a?(Path)\n        loc_end = node.name_end_location\n        if @target_location.between?(loc_start, loc_end)\n          if node.expanded\n            @found_nodes << node\n          else\n            @message = \"no expansion found: #{node} may not be a macro\"\n          end\n          false\n        else\n          contains_target(node)\n        end\n      else\n        false\n      end\n    end\n\n    def visit(node : MacroFor | MacroIf | MacroExpression)\n      if loc_start = node.location\n        loc_end = node.end_location || loc_start\n        if @target_location.between?(loc_start, loc_end) && node.expanded\n          @found_nodes << node\n          false\n        else\n          contains_target(node)\n        end\n      else\n        false\n      end\n    end\n\n    def visit(node)\n      contains_target(node)\n    end\n  end\n\n  class ExpandTransformer < Transformer\n    property? expanded = false\n    getter macro_calls = [] of Call\n\n    def transform(node : Call)\n      if expanded = node.expanded\n        self.expanded = true\n        macro_calls << node\n        expanded\n      else\n        super\n      end\n    end\n\n    def transform(node : MacroFor | MacroIf | MacroExpression)\n      if expanded = node.expanded\n        self.expanded = true\n        expanded\n      else\n        super\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/flags.cr",
    "content": "require \"colorize\"\nrequire \"../syntax/ast\"\n\nclass Crystal::Command\n  private def flags\n    OptionParser.parse(@options) do |opts|\n      opts.banner = \"Usage: crystal tool flags [path...]\\n\\nOptions:\"\n\n      opts.on(\"-h\", \"--help\", \"Show this message\") do\n        puts opts\n        exit\n      end\n\n      opts.on(\"--no-color\", \"Disable colored output\") do\n        @color = false\n      end\n    end\n\n    visitor = FlagsVisitor.new\n    find_sources(options) do |source|\n      Parser.parse(source.code).accept(visitor)\n    end\n    visitor.flag_names.each do |flag|\n      puts flag\n    end\n  end\n\n  def find_sources(\n    paths : Array(String),\n    stdin : IO = STDIN,\n    & : Compiler::Source ->\n  ) : Nil\n    stdin_content = nil\n    paths.each do |path|\n      if path == \"-\"\n        stdin_content ||= stdin.gets_to_end\n        yield Compiler::Source.new(path, stdin_content)\n      elsif File.file?(path)\n        yield Compiler::Source.new(path, File.read(path))\n      elsif Dir.exists?(path)\n        Dir.glob(::Path[path].to_posix.join(\"**/*.cr\")) do |dir_path|\n          if File.file?(dir_path)\n            yield Compiler::Source.new(path, File.read(dir_path))\n          end\n        end\n      else\n        Crystal.error \"file or directory does not exist: #{path}\", @color, leading_error: false\n      end\n    end\n  end\n\n  class FlagsVisitor < Visitor\n    @in_macro_expression = false\n\n    getter all_flags = [] of ASTNode\n\n    def initialize(@flag_name : String = \"flag?\")\n    end\n\n    def flag_names\n      all_flags.map { |flag| string_flag(flag) }.uniq!.sort!\n    end\n\n    private def string_flag(node)\n      case node\n      when StringLiteral, SymbolLiteral\n        node.value\n      else\n        node.to_s\n      end\n    end\n\n    def visit(node)\n      true\n    end\n\n    def visit(node : Crystal::MacroExpression | Crystal::MacroIf | Crystal::MacroFor)\n      @in_macro_expression = true\n\n      true\n    end\n\n    def end_visit(node : Crystal::MacroExpression | Crystal::MacroIf | Crystal::MacroFor)\n      @in_macro_expression = false\n    end\n\n    def visit(node : Crystal::Call)\n      check_call(node)\n      true\n    end\n\n    private def check_call(node)\n      return unless @in_macro_expression\n      return unless node.name == @flag_name\n      return unless node.obj.nil? && node.block.nil? && node.named_args.nil?\n\n      args = node.args\n      return unless args.size == 1\n      arg = args[0]\n\n      all_flags << arg\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/formatter.cr",
    "content": "require \"../syntax\"\n\nmodule Crystal\n  def self.format(source, filename = nil, report_warnings : IO? = nil, flags : Array(String)? = nil)\n    Crystal::Formatter.format(source, filename: filename, report_warnings: report_warnings, flags: flags)\n  end\n\n  class Formatter < Visitor\n    def self.format(source, filename = nil, report_warnings : IO? = nil, flags : Array(String)? = nil)\n      parser = Parser.new(source)\n      parser.filename = filename\n      nodes = parser.parse\n\n      # the formatter merely parses the same source again, it shouldn't\n      # introduce any new syntax warnings the parser cannot find\n      if report_warnings\n        parser.warnings.report(report_warnings)\n      end\n\n      formatter = new(source, flags: flags)\n      formatter.skip_space_or_newline\n      nodes.accept formatter\n      formatter.finish\n    end\n\n    record AlignInfo,\n      id : UInt64,\n      line : Int32,\n      start_column : Int32,\n      middle_column : Int32,\n      end_column : Int32,\n      number : Bool do\n      def size\n        end_column - start_column\n      end\n    end\n\n    class CommentInfo\n      property start_line : Int32\n      property end_line : Int32\n      property needs_newline : Bool\n      property needs_format : Bool\n\n      def initialize(@start_line : Int32, @needs_format : Bool)\n        @end_line = @start_line\n        @needs_newline = true\n      end\n    end\n\n    record HeredocFix,\n      start_line : Int32,\n      end_line : Int32,\n      difference : Int32\n\n    record HeredocInfo,\n      node : StringInterpolation,\n      token : Token,\n      line : Int32,\n      column : Int32,\n      indent : Int32,\n      string_continuation : Int32 do\n      include Lexer::HeredocItem\n    end\n\n    @lexer : Lexer\n    @comment_columns : Array(Int32?)\n    @indent : Int32\n    @line : Int32\n    @column : Int32\n    @token : Token\n    @output : IO::Memory\n    @line_output : IO::Memory\n    @wrote_newline : Bool\n    @wrote_double_newlines : Bool\n    @wrote_comment : Bool\n    @macro_state : Token::MacroState\n    @inside_macro : Int32\n    @inside_cond : Int32\n    @inside_lib : Int32\n    @inside_struct_or_union : Int32\n    @inside_enum : Int32\n    @implicit_exception_handler_indent : Int32\n    @last_write : String\n    @exp_needs_indent : Bool\n    @inside_def : Int32\n    @when_infos : Array(AlignInfo)\n    @hash_infos : Array(AlignInfo)\n    @assign_infos : Array(AlignInfo)\n    @doc_comments : Array(CommentInfo)\n    @current_doc_comment : CommentInfo?\n    @hash_in_same_line : Set(ASTNode)\n    @shebang : Bool\n    @heredoc_fixes : Array(HeredocFix)\n    @assign_length : Int32?\n    @current_hash : ASTNode?\n\n    getter no_rstrip_lines\n    property vars\n    property inside_lib\n    property inside_enum\n    property inside_struct_or_union\n    property indent\n    property subformat_nesting = 0\n\n    def initialize(source, @flags : Array(String)? = nil)\n      @lexer = Lexer.new(source)\n      @lexer.comments_enabled = true\n      @lexer.count_whitespace = true\n      @lexer.wants_raw = true\n      @comment_columns = [nil] of Int32?\n      @indent = 0\n      @line = 0\n      @column = 0\n      @token = @lexer.next_token\n\n      @output = IO::Memory.new(source.bytesize)\n      @line_output = IO::Memory.new\n      @wrote_newline = false\n      @wrote_double_newlines = false\n      @wrote_comment = false\n      @macro_state = Token::MacroState.default\n      @inside_macro = 0\n      @inside_cond = 0\n      @inside_lib = 0\n      @inside_enum = 0\n      @inside_struct_or_union = 0\n      @implicit_exception_handler_indent = 0\n      @last_write = \"\"\n      @exp_needs_indent = true\n      @inside_def = 0\n\n      # When we parse a type, parentheses information is not stored in ASTs, unlike\n      # for an Expressions node. So when we are printing a type (Path, ProcNotation, Union, etc.)\n      # we increment this when we find a '(', and decrement it when we find ')', but\n      # only if `paren_count > 0`: it might be the case of `def foo(x : A)`, but we don't\n      # want to print that last ')' when printing the type A.\n      @paren_count = 0\n\n      # This stores the column number (if any) of each comment in every line\n      @when_infos = [] of AlignInfo\n      @hash_infos = [] of AlignInfo\n      @assign_infos = [] of AlignInfo\n      @doc_comments = [] of CommentInfo\n      @current_doc_comment = nil\n      @hash_in_same_line = Set(ASTNode).new.compare_by_identity\n      @shebang = @token.type.comment? && @token.value.to_s.starts_with?(\"#!\")\n      @heredoc_fixes = [] of HeredocFix\n      @last_is_heredoc = false\n      @last_arg_is_skip = false\n      @string_continuation = 0\n      @inside_call_or_assign = 0\n      @passed_backslash_newline = false\n\n      # Lines that must not be rstripped (HEREDOC lines)\n      @no_rstrip_lines = Set(Int32).new\n\n      # Variables for when we format macro code without interpolation\n      @vars = [Set(String).new]\n    end\n\n    def flag?(flag)\n      !!@flags.try(&.includes?(flag))\n    end\n\n    def end_visit_any(node)\n      case node\n      when StringInterpolation\n        # Nothing\n      else\n        @last_is_heredoc = false\n      end\n    end\n\n    def visit(node : FileNode)\n      true\n    end\n\n    def visit(node : Expressions)\n      if node.expressions.size == 1 && @token.type.op_lparen?\n        # If it's (...) with a single expression, we treat it\n        # like a single expression, indenting it if needed\n        write \"(\"\n        next_token_skip_space\n        if @token.type.newline?\n          next_token_skip_space_or_newline\n          write_line\n          write_indent(@indent + 2, node.expressions.first)\n          skip_space_write_line\n          skip_space_or_newline\n          write_indent\n          write_token :OP_RPAREN\n          return false\n        end\n        skip_space_or_newline\n        accept node.expressions.first\n        skip_space\n        if @token.type.newline?\n          skip_space_or_newline\n          write_line\n          write_indent(@indent + 2)\n        end\n        skip_space_or_newline\n        write \")\"\n        next_token\n        return false\n      end\n\n      old_indent = @indent\n      next_needs_indent = false\n\n      has_newline = false\n      has_paren = false\n      has_begin = false\n\n      empty_expressions = node.expressions.size == 1 && node.expressions[0].is_a?(Nop)\n\n      if node.keyword.paren? && @token.type.op_lparen?\n        write \"(\"\n        next_needs_indent = false\n        has_paren = true\n        wrote_newline = next_token_skip_space\n        if @token.type.newline? || wrote_newline\n          @indent += 2\n          write_line unless wrote_newline\n          next_token_skip_space_or_newline\n          next_needs_indent = true\n          has_newline = true\n        end\n      elsif node.keyword.begin? && @token.keyword?(:begin)\n        write \"begin\"\n        @indent += 2\n        write_line\n        next_token\n        # Corner case: an empty `begin ... end`.\n        # In this case, we should not skip space because it will do in the below loop.\n        unless empty_expressions\n          skip_space_or_newline\n          if @token.type.op_semicolon?\n            next_token_skip_space_or_newline\n          end\n        end\n        has_begin = true\n        next_needs_indent = true\n        has_newline = true\n      end\n\n      last_aligned_assign = nil\n      last_found_comment = false\n      max_length = nil\n      skip_space\n\n      node.expressions.each_with_index do |exp, i|\n        is_assign = assign?(exp)\n        if is_assign && !last_aligned_assign\n          last_aligned_assign, max_length = find_assign_chunk(node.expressions, exp, i + 1)\n        else\n          max_length = nil unless is_assign\n        end\n\n        if last?(i, node.expressions)\n          needs_two_lines = false\n        else\n          next_exp = node.expressions[i + 1]\n          needs_two_lines = needs_two_lines?(exp, next_exp)\n        end\n\n        @assign_length = max_length\n        if next_needs_indent\n          write_indent(@indent, exp)\n        else\n          indent(@indent, exp)\n        end\n\n        found_comment = skip_space\n\n        if @token.type.op_semicolon?\n          if needs_two_lines\n            skip_semicolon_or_space_or_newline\n          else\n            found_comment = skip_semicolon_or_space\n            if @token.type.newline?\n              write_line\n              next_token_skip_space\n              next_needs_indent = true\n            else\n              write \"; \" unless last?(i, node.expressions) || found_comment\n              skip_space_or_newline\n              next_needs_indent = found_comment\n            end\n          end\n        else\n          next_needs_indent = true\n        end\n\n        unless @exp_needs_indent\n          next_needs_indent = false\n          @exp_needs_indent = true\n        end\n\n        if last?(i, node.expressions)\n          last_found_comment = skip_space_or_newline last: true, next_comes_end: true\n        else\n          if needs_two_lines\n            unless found_comment\n              if @wrote_newline\n                write_line unless @wrote_double_newlines\n              elsif !@wrote_double_newlines\n                write_line\n                write_line\n              end\n              @wrote_double_newlines = true\n              found_comment = skip_space_or_newline\n              write_line unless found_comment || @wrote_double_newlines\n            end\n          else\n            consume_newlines\n          end\n        end\n\n        last_aligned_assign = nil if last_aligned_assign.same?(exp)\n      end\n\n      @indent = old_indent\n\n      if has_newline && !last_found_comment && (!@wrote_newline || empty_expressions)\n        # Right after the last expression but we didn't insert a newline yet\n        # so we are still missing a newline following by an indent and the \"end\" keyword\n        write_line\n        write_indent\n      elsif has_newline && last_found_comment && @wrote_newline\n        # Right after the last expression and we did insert a newline (because of a comment)\n        # so we are still missing an indent and the \"end\" keyword\n        write_indent\n      end\n\n      if has_paren\n        write_token :OP_RPAREN\n      end\n\n      if has_begin\n        check_end\n        next_token\n        write \"end\"\n      end\n\n      false\n    end\n\n    def assign?(exp)\n      case exp\n      when Assign\n        exp.target.is_a?(Path)\n      when VisibilityModifier\n        assign? exp.exp\n      when Arg\n        true\n      else\n        false\n      end\n    end\n\n    def assign_length(exp)\n      case exp\n      when Assign\n        assign_length exp.target\n      when Arg\n        exp.name.size\n      when VisibilityModifier\n        assign_length exp.exp\n      when Path\n        exp.names.first.size\n      else\n        0\n      end\n    end\n\n    def find_assign_chunk(expressions, last, i)\n      max_length = assign_length(last)\n\n      while i < expressions.size\n        exp = expressions[i]\n        exp_location = exp.location\n        break unless exp_location\n\n        last_location = last.location\n        break unless last_location\n        break unless last_location.line_number + 1 == exp_location.line_number\n\n        last = exp\n        exp_length = assign_length(exp)\n        max_length = exp_length if exp_length > max_length\n\n        i += 1\n      end\n\n      {last, max_length}\n    end\n\n    def needs_two_lines?(node, next_node)\n      return false if node.is_a?(Annotation) || node.is_a?(MacroIf)\n      return false if abstract_def?(node) && abstract_def?(next_node)\n\n      needs_two_lines?(node) || needs_two_lines?(next_node)\n    end\n\n    def abstract_def?(node)\n      case node\n      when Def\n        node.abstract?\n      when VisibilityModifier\n        abstract_def? node.exp\n      else\n        false\n      end\n    end\n\n    def needs_two_lines?(node)\n      case node\n      when Def, ClassDef, ModuleDef, LibDef, CStructOrUnionDef, Macro\n        true\n      when VisibilityModifier\n        needs_two_lines? node.exp\n      else\n        false\n      end\n    end\n\n    def visit(node : Nop)\n      false\n    end\n\n    def visit(node : NilLiteral)\n      write_keyword :nil\n\n      false\n    end\n\n    def visit(node : BoolLiteral)\n      check_keyword :false, :true\n      write node.value\n      next_token\n\n      false\n    end\n\n    def visit(node : CharLiteral)\n      check :CHAR\n      write @token.raw\n      next_token\n\n      false\n    end\n\n    def visit(node : SymbolLiteral)\n      check :SYMBOL\n      write @token.raw\n      next_token\n\n      false\n    end\n\n    def visit(node : NumberLiteral)\n      if @token.type.magic_line?\n        write @token.type\n        next_token\n        return false\n      end\n\n      check :NUMBER\n      write @token.raw\n      next_token\n\n      false\n    end\n\n    def visit(node : StringLiteral)\n      column = @column\n\n      if @token.type.magic_file? || @token.type.magic_dir?\n        write @token.type\n        next_token\n        return false\n      end\n\n      check :DELIMITER_START\n      is_regex = @token.delimiter_state.kind.regex?\n\n      write @token.raw\n      next_string_token\n\n      visit_string_body(node, is_regex)\n\n      check :DELIMITER_END\n\n      write @token.raw\n      format_regex_modifiers if is_regex\n\n      if space_slash_newline?\n        old_indent = @indent\n        @indent = column if @string_continuation == 0\n        @string_continuation += 1\n        write \" \\\\\"\n        write_line\n        write_indent\n        next_token_skip_space_or_newline\n        visit(node)\n        @indent = old_indent\n        @string_continuation -= 1\n      else\n        next_token\n      end\n\n      false\n    end\n\n    def visit_string_body(node, is_regex = false)\n      while true\n        case @token.type\n        when .string?\n          write_sanitized_string_body(@token.delimiter_state.allow_escapes && !is_regex)\n          next_string_token\n        when .interpolation_start?\n          # This is the case of #{__DIR__}\n          write \"\\#{\"\n          next_token_skip_space_or_newline\n          indent(@column, node)\n          skip_space_or_newline\n          check :OP_RCURLY\n          write \"}\"\n          next_string_token\n        else\n          break\n        end\n      end\n    end\n\n    private def write_sanitized_string_body(escape)\n      body = @token.invalid_escape ? @token.value.as(String) : @token.raw\n      body = Lexer.escape_forbidden_characters(body) if escape\n      write body\n    end\n\n    def visit(node : StringInterpolation)\n      if @token.delimiter_state.kind.heredoc?\n        # For heredoc, only write the start: on a newline will print it\n        @lexer.heredocs << {@token.delimiter_state, HeredocInfo.new(node, @token.dup, @line, @column, @indent, @string_continuation)}\n        write @token.raw\n        next_token\n        return false\n      end\n\n      check :DELIMITER_START\n\n      visit_string_interpolation(node, @token, @line, @column, @indent, @string_continuation)\n    end\n\n    def visit_string_interpolation(node, token, line, column, old_indent, old_string_continuation, wrote_token = false)\n      @token = token\n\n      is_regex = token.delimiter_state.kind.regex?\n      indent_difference = token.column_number - (column + 1)\n\n      write token.raw unless wrote_token\n      next_string_token\n\n      delimiter_state = token.delimiter_state\n      is_heredoc = token.delimiter_state.kind.heredoc?\n      @last_is_heredoc = is_heredoc\n\n      heredoc_line = @line\n\n      # To detect the first content of interpolation of string literal correctly,\n      # we should consume the first string token if this token contains only removed indentation of heredoc.\n      if is_heredoc && @token.type.string?\n        token_is_indent = @token.raw.bytesize == node.heredoc_indent && @token.raw.each_char.all? &.ascii_whitespace?\n        if token_is_indent\n          write @token.raw\n          next_string_token\n        end\n      end\n\n      visit_string_interpolation_body(node, delimiter_state, column, is_regex: is_regex, is_heredoc: is_heredoc)\n\n      heredoc_end = @line\n\n      check :DELIMITER_END\n      write @token.raw\n\n      if is_heredoc\n        if indent_difference > 0\n          @heredoc_fixes << HeredocFix.new(heredoc_line, @line, indent_difference)\n        end\n        (heredoc_line...heredoc_end).each do |line|\n          @no_rstrip_lines.add line\n        end\n        write_line\n      end\n\n      format_regex_modifiers if is_regex\n      next_token\n\n      @string_continuation = old_string_continuation\n      @indent = old_indent unless is_heredoc\n\n      false\n    end\n\n    def visit_string_interpolation_body(node, delimiter_state, column, *, is_regex = false, is_heredoc = false)\n      node.expressions.each do |exp|\n        if @token.type.delimiter_end?\n          # Heredoc cannot contain string continuation,\n          # so we are done.\n          break if is_heredoc\n\n          # This is for \" ... \" \\\n          #     \" ... \"\n          @indent = column if @string_continuation == 0\n          @string_continuation += 1\n\n          write @token.raw\n          write \" \\\\\"\n          write_line\n          next_token_skip_space_or_newline\n          check :DELIMITER_START\n          write_indent\n          write @token.raw\n          next_string_token\n        end\n\n        if exp.is_a?(StringLiteral)\n          # It might be #{__DIR__}, for example\n          if @token.type.interpolation_start?\n            write \"\\#{\"\n            delimiter_state = @token.delimiter_state\n            next_token_skip_space_or_newline\n            indent(@column, exp)\n            skip_space_or_newline\n            check :OP_RCURLY\n            write \"}\"\n            @token.delimiter_state = delimiter_state\n            next_string_token\n          else\n            loop do\n              check :STRING\n              write_sanitized_string_body(delimiter_state.allow_escapes && !is_regex)\n              next_string_token\n\n              # On heredoc, pieces of contents are combined due to removing indentation.\n              # Thus, we should consume continuous string tokens at once.\n              break if !is_heredoc || !@token.type.string?\n            end\n          end\n        else\n          visit_interpolation(exp)\n        end\n      end\n    end\n\n    private def visit_interpolation(exp)\n      check :INTERPOLATION_START\n      write \"\\#{\"\n      delimiter_state = @token.delimiter_state\n\n      wrote_comment = next_token_skip_space\n      has_newline = wrote_comment || @token.type.newline?\n      skip_space_or_newline\n\n      if has_newline\n        write_line unless wrote_comment\n        write_indent(@column + 2)\n        indent(@column + 2, exp)\n        wrote_comment = skip_space_or_newline\n        write_line unless wrote_comment\n      else\n        indent(@column, exp)\n      end\n\n      skip_space_or_newline\n      check :OP_RCURLY\n      write \"}\"\n      @token.delimiter_state = delimiter_state\n      next_string_token\n    end\n\n    private def consume_heredocs\n      @consuming_heredocs = true\n      @lexer.heredocs.reverse!\n      while heredoc = @lexer.heredocs.pop?\n        consume_heredoc(heredoc[0], heredoc[1].as(HeredocInfo))\n      end\n      @consuming_heredocs = false\n    end\n\n    private def consume_heredoc(delimiter_state, info)\n      visit_string_interpolation(\n        info.node,\n        info.token,\n        info.line,\n        info.column,\n        info.indent, info.string_continuation,\n        wrote_token: true)\n    end\n\n    def visit(node : RegexLiteral)\n      # Go back and tokenize again if slash is recognized as divide operator.\n      # In this case, slash is tokenized on comment skipping (#4626).\n      # However this slash must be a start delimiter of regex because\n      # this is parsed as regex literal once before.\n      if @token.type.op_slash?\n        @lexer.reader.previous_char\n        @lexer.column_number -= 1\n        slash_is_regex!\n        next_token\n      end\n\n      accept node.value\n\n      false\n    end\n\n    def format_regex_modifiers\n      while true\n        char = @lexer.current_char\n        case char\n        when 'i', 'm', 'x'\n          write char\n          @lexer.next_char\n        else\n          break\n        end\n      end\n    end\n\n    def space_slash_newline?\n      pos, line, col = @lexer.current_pos, @lexer.line_number, @lexer.column_number\n      while true\n        char = @lexer.current_char\n        case char\n        when ' ', '\\t'\n          @lexer.next_char\n        when '\\\\'\n          @lexer.current_pos = pos\n          return true\n        else\n          break\n        end\n      end\n      @lexer.current_pos = pos\n      @lexer.line_number = line\n      @lexer.column_number = col\n      false\n    end\n\n    def space_newline?\n      pos, line, col = @lexer.current_pos, @lexer.line_number, @lexer.column_number\n      while true\n        char = @lexer.current_char\n        case char\n        when ' ', '\\t'\n          @lexer.next_char\n        when '\\n'\n          @lexer.current_pos = pos\n          return true\n        else\n          break\n        end\n      end\n      @lexer.current_pos = pos\n      @lexer.line_number = line\n      @lexer.column_number = col\n      false\n    end\n\n    def visit(node : ArrayLiteral)\n      case @token.type\n      when .op_lsquare?\n        format_literal_elements node.elements, :OP_LSQUARE, :OP_RSQUARE\n      when .op_lsquare_rsquare?\n        write \"[]\"\n        next_token\n      when .string_array_start?, .symbol_array_start?\n        first = true\n        write @token.raw\n        count = 0\n        while true\n          has_space_newline = space_newline?\n          if has_space_newline\n            write_line\n            if count == node.elements.size\n              write_indent\n            else\n              write_indent(@indent + 2)\n            end\n          end\n          next_string_array_token\n          case @token.type\n          when .string?\n            write \" \" unless first || has_space_newline\n            write @token.raw\n            first = false\n          when .string_array_end?\n            write @token.raw\n            next_token\n            break\n          else\n            raise \"Bug: unexpected token #{@token.type}\"\n          end\n          count += 1\n        end\n        return false\n      else\n        name = node.name.not_nil!\n        accept name\n        skip_space\n        format_literal_elements node.elements, :OP_LCURLY, :OP_RCURLY\n      end\n\n      if node_of = node.of\n        write_keyword \" \", :of, \" \"\n        accept node_of\n      end\n\n      false\n    end\n\n    def visit(node : TupleLiteral)\n      format_literal_elements node.elements, :OP_LCURLY, :OP_RCURLY\n      false\n    end\n\n    def format_literal_elements(elements, prefix : Token::Kind, suffix : Token::Kind)\n      slash_is_regex!\n      write_token prefix\n      has_newlines = false\n      wrote_newline = false\n      write_space_at_end = false\n      next_needs_indent = false\n      found_comment = false\n      found_first_newline = false\n\n      found_comment = skip_space\n      if found_comment || @token.type.newline?\n        # add one level of indentation for contents if a newline is present\n        offset = @indent + 2\n        start_column = @indent + 2\n\n        if elements.empty?\n          skip_space_or_newline(offset, last: true, at_least_one: true)\n          write_token suffix\n          return false\n        end\n\n        indent(offset) { consume_newlines }\n        skip_space_or_newline\n        wrote_newline = true\n        next_needs_indent = true\n        has_newlines = true\n        found_first_newline = true\n      else\n        # indent contents at the same column as starting token if no newline\n        offset = @indent\n        start_column = @column\n      end\n\n      elements.each_with_index do |element, i|\n        # This is to prevent writing `{{` and `{%`\n        if prefix.op_lcurly? && i == 0 && !wrote_newline &&\n           (@token.type.op_lcurly? || @token.type.op_lcurly_lcurly? || @token.type.op_lcurly_percent? ||\n           @token.type.op_percent? || @token.raw.starts_with?(\"%\"))\n          write \" \"\n          write_space_at_end = true\n        end\n\n        if next_needs_indent\n          write_indent(offset, element)\n        else\n          indent(offset, element)\n        end\n        has_heredoc_in_line = !@lexer.heredocs.empty?\n\n        last = last?(i, elements)\n\n        found_comment = skip_space(offset, write_comma: (last || has_heredoc_in_line) && has_newlines)\n\n        if @token.type.op_comma?\n          if !found_comment && (!last || has_heredoc_in_line)\n            write \",\"\n            wrote_comma = true\n          end\n\n          slash_is_regex!\n          next_token\n          found_comment = skip_space(offset, write_comma: last && has_newlines)\n          if @token.type.newline?\n            if last && !found_comment && !wrote_comma\n              write \",\"\n              found_comment = true\n            end\n            indent(offset) { consume_newlines }\n            skip_space_or_newline\n            next_needs_indent = true\n            has_newlines = true\n            offset = start_column\n          else\n            if !last && !found_comment\n              write \" \"\n              next_needs_indent = false\n            elsif found_comment\n              next_needs_indent = true\n              offset = start_column\n            end\n          end\n        end\n      end\n\n      finish_list suffix, has_newlines, found_comment, found_first_newline, write_space_at_end\n    end\n\n    def visit(node : HashLiteral)\n      if name = node.name\n        accept name\n        skip_space\n      end\n\n      old_hash = @current_hash\n      @current_hash = node\n      format_literal_elements node.entries, :OP_LCURLY, :OP_RCURLY\n      @current_hash = old_hash\n\n      if node_of = node.of\n        write_keyword \" \", :of, \" \"\n        format_hash_entry nil, node_of\n      end\n\n      if @hash_in_same_line.includes? node\n        @hash_infos.reject! { |info| info.id == node.object_id }\n      end\n\n      false\n    end\n\n    def accept(node : HashLiteral::Entry)\n      format_hash_entry(@current_hash.not_nil!, node)\n    end\n\n    def format_hash_entry(hash, entry)\n      start_line = @line\n      start_column = @column\n      found_in_same_line = false\n\n      accept entry.key\n      skip_space_or_newline\n      middle_column = @column\n      if @token.type.op_colon? && entry.key.is_a?(StringLiteral)\n        write \": \"\n        slash_is_regex!\n        next_token\n        middle_column = @column\n        found_in_same_line ||= check_hash_info hash, entry.key, start_line, start_column, middle_column\n      else\n        slash_is_regex!\n        write_token \" \", :OP_EQ_GT, \" \"\n        found_in_same_line ||= check_hash_info hash, entry.key, start_line, start_column, middle_column\n      end\n\n      skip_space_or_newline\n      accept entry.value\n\n      if hash && found_in_same_line\n        @hash_in_same_line << hash\n      end\n    end\n\n    def visit(node : NamedTupleLiteral)\n      old_hash = @current_hash\n      @current_hash = node\n      format_literal_elements node.entries, :OP_LCURLY, :OP_RCURLY\n      @current_hash = old_hash\n\n      if @hash_in_same_line.includes? node\n        @hash_infos.reject! { |info| info.id == node.object_id }\n      end\n\n      false\n    end\n\n    def accept(node : NamedTupleLiteral::Entry)\n      format_named_tuple_entry(@current_hash.not_nil!, node)\n    end\n\n    def format_named_tuple_entry(hash, entry)\n      start_line = @line\n      start_column = @column\n      found_in_same_line = false\n      format_named_argument_name(entry.key)\n      slash_is_regex!\n      write_token :OP_COLON, \" \"\n      middle_column = @column\n      found_in_same_line ||= check_hash_info hash, entry.key, start_line, start_column, middle_column\n      skip_space_or_newline\n      accept entry.value\n\n      if found_in_same_line\n        @hash_in_same_line << hash\n      end\n    end\n\n    def format_named_argument_name(name)\n      if @token.type.delimiter_start?\n        StringLiteral.new(name).accept self\n      else\n        write @token\n        @lexer.wants_symbol = false\n        next_token\n        @lexer.wants_symbol = true\n      end\n    end\n\n    def finish_list(suffix : Token::Kind, has_newlines, found_comment, found_first_newline, write_space_at_end)\n      if @token.type == suffix && !found_first_newline\n        if @wrote_newline\n          write_indent\n        else\n          write \" \" if write_space_at_end\n        end\n      else\n        found_comment ||= skip_space_or_newline\n        check suffix\n\n        if @wrote_newline\n          write_indent\n        elsif has_newlines\n          unless found_comment\n            write \",\"\n            write_line\n          end\n          write_indent\n        elsif write_space_at_end\n          write \" \"\n        end\n        skip_space_or_newline\n      end\n\n      write_token suffix\n    end\n\n    def check_hash_info(hash, key, start_line, start_column, middle_column)\n      end_column = @column\n      found_in_same_line = false\n      if @line == start_line\n        last_info = @hash_infos.last?\n        if last_info && last_info.line == @line\n          found_in_same_line = true\n        elsif hash\n          number = key.is_a?(NumberLiteral)\n          @hash_infos << AlignInfo.new(hash.object_id, @line, start_column, middle_column, end_column, number)\n        end\n      end\n      found_in_same_line\n    end\n\n    def visit(node : RangeLiteral)\n      accept node.from\n      skip_space\n      write_token(node.exclusive? ? Token::Kind::OP_PERIOD_PERIOD_PERIOD : Token::Kind::OP_PERIOD_PERIOD)\n      skip_space\n      accept node.to\n      false\n    end\n\n    def check_open_paren\n      if @token.type.op_lparen?\n        while @token.type.op_lparen?\n          write \"(\"\n          next_token_skip_space\n          @paren_count += 1\n        end\n        true\n      else\n        false\n      end\n    end\n\n    def check_close_paren\n      while @token.type.op_rparen? && @paren_count > 0\n        @paren_count -= 1\n        write_token :OP_RPAREN\n      end\n    end\n\n    def visit(node : Path)\n      check_open_paren\n\n      # Sometimes the :: is not present because the parser generates ::Nil, for example\n      if node.global? && @token.type.op_colon_colon?\n        write \"::\"\n        next_token_skip_space_or_newline\n      end\n\n      node.names.each_with_index do |name, i|\n        skip_space_or_newline\n        check :CONST\n        write @token.value\n        next_token\n        skip_space unless last?(i, node.names)\n        if @token.type.op_colon_colon?\n          write \"::\"\n          next_token\n        end\n      end\n\n      check_close_paren\n\n      false\n    end\n\n    def visit(node : Generic)\n      check_open_paren\n\n      name = node.name.as(Path)\n      if name.global? && @token.type.op_colon_colon?\n        write \"::\"\n        next_token_skip_space_or_newline\n      end\n\n      if node.suffix.question?\n        node.type_vars[0].accept self\n        skip_space\n        write_token :OP_QUESTION\n        return false\n      end\n\n      # Check if it's T* instead of Pointer(T)\n      if node.suffix.asterisk?\n        # Count nesting asterisk\n        asterisks = 1\n        while (type_var = node.type_vars.first) && type_var.is_a?(Generic) && type_var.suffix.asterisk?\n          node = type_var\n          asterisks += 1\n        end\n\n        type_var = node.type_vars.first\n        accept type_var\n        skip_space_or_newline\n\n        while asterisks > 0\n          skip_space\n          if @token.type.op_star_star? && asterisks >= 2\n            write \"**\"\n            next_token\n            asterisks -= 2\n          else\n            write_token :OP_STAR\n            asterisks -= 1\n          end\n        end\n\n        return false\n      end\n\n      # Check if it's T[N] instead of StaticArray(T, N)\n      if node.suffix.bracket?\n        accept node.type_vars[0]\n        skip_space_or_newline\n        write_token :OP_LSQUARE\n        skip_space_or_newline\n        accept node.type_vars[1]\n        skip_space_or_newline\n        write_token :OP_RSQUARE\n        return false\n      end\n\n      # NOTE: not `name.single_name?` as this is only for the expanded tuple literals\n      first_name = name.names.first if name.global? && name.names.size == 1\n\n      # Check if it's {A, B} instead of Tuple(A, B)\n      if first_name == \"Tuple\" && @token.value != \"Tuple\"\n        write_token :OP_LCURLY\n        found_comment = skip_space_or_newline\n        write_space_at_end = false\n        node.type_vars.each_with_index do |type_var, i|\n          # This is to prevent writing `{{` and `{%`\n          if i == 0 && !found_comment && (@token.type.op_lcurly? || @token.type.op_lcurly_lcurly? || @token.type.op_lcurly_percent? || @token.type.op_percent? || @token.raw.starts_with?(\"%\"))\n            write \" \"\n            write_space_at_end = true\n          end\n          accept type_var\n          skip_space_or_newline\n          if @token.type.op_comma?\n            write \", \" unless last?(i, node.type_vars)\n            next_token_skip_space_or_newline\n          end\n          # Write space at end when write space for preventing writing `{{` and `{%` at first.\n          write \" \" if last?(i, node.type_vars) && write_space_at_end\n        end\n        write_token :OP_RCURLY\n        return false\n      end\n\n      # Check if it's {x: A, y: B} instead of NamedTuple(x: A, y: B)\n      if first_name == \"NamedTuple\" && @token.value != \"NamedTuple\"\n        write_token :OP_LCURLY\n        skip_space_or_newline\n        named_args = node.named_args.not_nil!\n        named_args.each_with_index do |named_arg, i|\n          accept named_arg\n          skip_space_or_newline\n          if @token.type.op_comma?\n            write \", \" unless last?(i, named_args)\n            next_token_skip_space_or_newline\n          end\n        end\n        write_token :OP_RCURLY\n        return false\n      end\n\n      accept name\n      skip_space_or_newline\n\n      # Given that generic type arguments are always inside parentheses\n      # we can start counting them from 0 inside them.\n      old_paren_count = @paren_count\n      @paren_count = 0\n\n      if named_args = node.named_args\n        write_token :OP_LPAREN\n        skip_space\n        has_newlines, _, _ = format_named_args([] of ASTNode, named_args, @indent + 2)\n        # `format_named_args` doesn't skip trailing comma\n        if @paren_count == 0 && @token.type.op_comma?\n          next_token_skip_space_or_newline\n          if has_newlines\n            write \",\"\n            write_line\n            write_indent\n          end\n        end\n        skip_space_or_newline if @paren_count == 0\n        write_token :OP_RPAREN\n      else\n        format_literal_elements(node.type_vars, :OP_LPAREN, :OP_RPAREN)\n      end\n\n      # Restore the old parentheses count\n      @paren_count = old_paren_count\n\n      false\n    ensure\n      check_close_paren\n    end\n\n    def visit(node : Union)\n      check_open_paren\n\n      if @token.type.ident? && @token.value == \"self?\" && node.types.size == 2 &&\n         node.types[0].is_a?(Self) && node.types[1].to_s == \"::Nil\"\n        write \"self?\"\n        next_token\n        check_close_paren\n        return false\n      end\n\n      column = @column\n      node.types.each_with_index do |type, i|\n        if @token.type.op_question?\n          # This can happen if it's a nilable type written like T?\n          write_token :op_question\n          break\n        end\n\n        unless i == 0\n          skip_space_or_newline\n          write_token \" \", :op_bar, \" \"\n          skip_space\n\n          if @token.type.newline?\n            write_line\n            write_indent(column)\n            next_token_skip_space_or_newline\n          end\n        end\n\n        accept type\n\n        skip_space\n      end\n\n      check_close_paren\n\n      false\n    end\n\n    def visit(node : If)\n      if node.ternary?\n        accept node.cond\n        skip_space_or_newline\n        write_token \" \", :OP_QUESTION, \" \"\n        skip_space_or_newline\n        accept node.then\n        skip_space_or_newline\n        write_token \" \", :OP_COLON, \" \"\n        skip_space_or_newline\n        accept node.else\n        return false\n      end\n\n      visit_if_or_unless node, :if\n    end\n\n    def visit(node : Unless)\n      visit_if_or_unless node, :unless\n    end\n\n    def visit_if_or_unless(node, keyword : Keyword)\n      if !@token.keyword?(keyword) && node.else.is_a?(Nop)\n        # Suffix if/unless\n        accept node.then\n        write_keyword \" \", keyword, \" \"\n        inside_cond do\n          indent(@column, node.cond)\n        end\n        return false\n      end\n\n      write_keyword keyword, \" \"\n      format_if_at_cond node, keyword\n\n      false\n    end\n\n    def format_if_at_cond(node, keyword : Keyword, check_end = true)\n      inside_cond do\n        indent(@column, node.cond)\n      end\n\n      skip_space(@indent + 2)\n      skip_semicolon\n      format_nested node.then\n      skip_space_or_newline(@indent + 2, last: true)\n      jump_semicolon\n\n      node_else = node.else\n\n      if @token.keyword?(:else)\n        write_indent\n        write \"else\"\n        next_token\n        skip_space(@indent + 2)\n        skip_semicolon\n        format_nested node.else\n      elsif node_else.is_a?(If) && @token.keyword?(:elsif)\n        format_elsif node_else, keyword\n      end\n\n      if check_end\n        format_end @indent\n      end\n    end\n\n    def format_elsif(node_else, keyword : Keyword)\n      write_indent\n      write \"elsif \"\n      next_token_skip_space_or_newline\n      format_if_at_cond node_else, keyword, check_end: false\n    end\n\n    def visit(node : While)\n      format_while_or_until node, :while\n    end\n\n    def visit(node : Until)\n      format_while_or_until node, :until\n    end\n\n    def format_while_or_until(node, keyword : Keyword)\n      write_keyword keyword, \" \"\n      inside_cond do\n        indent(@column, node.cond)\n      end\n\n      format_nested_with_end node.body\n\n      false\n    end\n\n    def format_nested(node, indent = @indent, write_end_line = true, write_indent = true)\n      slash_is_regex!\n      if node.is_a?(Nop)\n        skip_space_write_line\n      else\n        if write_indent\n          indent(indent + 2) do\n            skip_space_write_line\n            skip_space_or_newline\n            write_indent(indent + 2, node)\n            skip_space_write_line if write_end_line\n          end\n        else\n          skip_space_write_line\n          skip_space_or_newline\n          accept node\n          skip_space_write_line if write_end_line\n        end\n      end\n    end\n\n    def format_nested_with_end(node, column = @indent, write_end_line = true)\n      skip_space(column + 2)\n\n      if @token.type.op_semicolon?\n        if node.is_a?(Nop)\n          skip_semicolon_or_space_or_newline\n          check_end\n          write \"; end\"\n          next_token\n          return false\n        else\n          next_token_skip_space\n        end\n      end\n\n      format_nested node, column, write_end_line: write_end_line\n      format_end(column)\n    end\n\n    def format_end(column)\n      skip_space_or_newline(column + 2, last: true)\n      check_end\n      write_indent(column)\n      write \"end\"\n      next_token\n    end\n\n    def visit(node : Def)\n      @implicit_exception_handler_indent = @indent\n      @inside_def += 1\n      @vars.push Set(String).new\n\n      write_keyword :abstract, \" \" if node.abstract?\n\n      write_keyword :def, \" \", skip_space_or_newline: false\n\n      if receiver = node.receiver\n        skip_space_or_newline\n        accept receiver\n        skip_space_or_newline\n        write_token :OP_PERIOD\n      end\n\n      @lexer.wants_def_or_macro_name do\n        skip_space_or_newline\n      end\n\n      write node.name\n\n      indent do\n        next_token\n\n        # this formats `def foo # ...` to `def foo(&) # ...` for yielding\n        # methods before consuming the comment line\n        if node.block_arity && node.args.empty? && !node.block_arg && !node.double_splat\n          write \"(&)\"\n        end\n\n        skip_space consume_newline: false\n        next_token_skip_space if @token.type.op_eq?\n      end\n\n      to_skip = format_def_args node\n\n      if return_type = node.return_type\n        skip_space\n        write_token \" \", :OP_COLON, \" \"\n        skip_space_or_newline\n        accept return_type\n      end\n\n      if free_vars = node.free_vars\n        skip_space_or_newline\n        write \" forall \"\n        next_token\n        last_index = free_vars.size - 1\n        free_vars.each_with_index do |free_var, i|\n          skip_space_or_newline\n          check :CONST\n          write free_var\n          next_token\n          skip_space_or_newline if last_index != i\n          if @token.type.op_comma?\n            write \", \"\n            next_token_skip_space_or_newline\n          end\n        end\n      end\n\n      body = remove_to_skip node, to_skip\n\n      unless node.abstract?\n        format_nested_with_end body\n      end\n\n      @vars.pop\n      @inside_def -= 1\n\n      false\n    end\n\n    def format_def_args(node : Def | Macro)\n      yields = node.is_a?(Def) && !node.block_arity.nil?\n      format_def_args node.args, node.block_arg, node.splat_index, false, node.double_splat, yields\n    end\n\n    def format_def_args(args : Array, block_arg, splat_index, variadic, double_splat, yields)\n      # If there are no args, remove extra \"()\"\n      if args.empty? && !block_arg && !double_splat && !variadic\n        if @token.type.op_lparen?\n          next_token_skip_space_or_newline\n          check :OP_RPAREN\n          next_token\n        end\n        return 0\n      end\n\n      # Count instance variable arguments. See `at_skip?`.\n      to_skip = 0\n\n      wrote_newline = false\n      found_first_newline = false\n\n      old_indent = @indent\n      @indent = @column + 1\n\n      write_token :OP_LPAREN\n      skip_space\n\n      # When \"(\" follows newline, it turns on two spaces indentation mode.\n      if @token.type.newline?\n        @indent = old_indent + 2\n        found_first_newline = true\n        wrote_newline = true\n\n        write_line\n        next_token_skip_space_or_newline\n      end\n\n      args.each_with_index do |arg, i|\n        has_more = !last?(i, args) || double_splat || block_arg || yields || variadic\n        wrote_newline = format_def_arg(wrote_newline, has_more, found_first_newline && !has_more) do\n          if i == splat_index\n            write_token :OP_STAR\n            skip_space_or_newline\n            next if arg.external_name.empty? # skip empty splat argument.\n          end\n\n          format_parameter_annotations(arg)\n\n          arg.accept self\n          to_skip += 1 if @last_arg_is_skip\n        end\n      end\n\n      if double_splat\n        wrote_newline = format_def_arg(wrote_newline, block_arg || yields, found_first_newline) do\n          write_token :OP_STAR_STAR\n          skip_space_or_newline\n\n          to_skip += 1 if at_skip?\n          double_splat.accept self\n        end\n      end\n\n      if block_arg\n        wrote_newline = format_def_arg(wrote_newline, false) do\n          format_parameter_annotations(block_arg)\n          write_token :OP_AMP\n          skip_space_or_newline\n\n          to_skip += 1 if at_skip?\n          block_arg.accept self\n        end\n      elsif yields\n        wrote_newline = format_def_arg(wrote_newline, false) do\n          write \"&\"\n          skip_space_or_newline\n        end\n      end\n\n      if variadic\n        wrote_newline = format_def_arg(wrote_newline, false) do\n          write_token :OP_PERIOD_PERIOD_PERIOD\n        end\n      end\n\n      if found_first_newline && !wrote_newline\n        write_line\n        wrote_newline = true\n      end\n      write_indent(found_first_newline ? old_indent : @indent) if wrote_newline\n      write_token :OP_RPAREN\n\n      @indent = old_indent\n\n      to_skip\n    end\n\n    private def format_parameter_annotations(node)\n      return unless (anns = node.parsed_annotations)\n\n      anns.each do |ann|\n        ann.accept self\n\n        skip_space\n\n        if @token.type.newline?\n          write_line\n          write_indent\n        else\n          write \" \"\n        end\n\n        skip_space_or_newline\n      end\n\n      if @token.type.newline?\n        skip_space_or_newline\n        write_line\n        write_indent\n      end\n    end\n\n    def format_def_arg(wrote_newline, has_more, write_trailing_comma = false, &)\n      write_indent if wrote_newline\n\n      yield\n\n      # Write \",\" before skipping spaces to prevent inserting comment between argument and comma.\n      write \",\" if has_more || (wrote_newline && @token.type.op_comma?) || write_trailing_comma\n\n      just_wrote_newline = skip_space\n      if @token.type.newline?\n        if has_more\n          consume_newlines\n          just_wrote_newline = true\n        else\n          # `last: true` is needed to write newline and comment only if comment is found.\n          just_wrote_newline = skip_space_or_newline(last: true)\n        end\n      end\n\n      if @token.type.op_comma?\n        found_comment = next_token_skip_space\n        if found_comment\n          just_wrote_newline = true\n        elsif @token.type.newline?\n          if has_more && !just_wrote_newline\n            consume_newlines\n            just_wrote_newline = true\n          else\n            just_wrote_newline |= skip_space_or_newline(last: true)\n          end\n        else\n          write \" \" if has_more && !just_wrote_newline\n        end\n      elsif @token.type.op_rparen? && has_more && !just_wrote_newline\n        # if we found a `)` and there are still more parameters to write, it\n        # must have been a missing `&` for a def that yields\n        write \" \"\n      end\n\n      just_wrote_newline\n    end\n\n    # The parser transforms `def foo(@x); end` to `def foo(x); @x = x; end` so if we\n    # find an instance var we later need to skip the first expressions in the body\n    def at_skip?\n      @token.type.instance_var? || @token.type.class_var?\n    end\n\n    def visit(node : FunDef)\n      write_keyword :fun, \" \"\n\n      check :IDENT, :CONST\n      write node.name\n      next_token_skip_space\n\n      if @token.type.op_eq?\n        write \" = \"\n        next_token_skip_space\n\n        if @token.type.newline?\n          write_line\n          next_token_skip_space_or_newline\n          write_indent(@indent + 2)\n        end\n\n        if @token.type.delimiter_start?\n          indent(@column, StringLiteral.new(node.real_name))\n        else\n          write node.real_name\n          next_token_skip_space\n        end\n      end\n\n      format_def_args node.args, nil, nil, node.varargs?, nil, false\n\n      if return_type = node.return_type\n        skip_space\n        write_token \" \", :OP_COLON, \" \"\n        skip_space_or_newline\n        accept return_type\n      end\n\n      if body = node.body\n        format_nested_with_end body\n      end\n\n      false\n    end\n\n    def visit(node : Macro)\n      reset_macro_state\n\n      macro_node_line = @line\n\n      write_keyword :macro, \" \"\n\n      write node.name\n      next_token\n\n      if @token.type.op_eq? && node.name.ends_with?('=')\n        next_token\n      end\n\n      skip_space(consume_newline: false)\n\n      format_def_args node\n\n      if macro_literal_source = macro_literal_contents(node.body)\n        format_macro_literal_only(node, macro_literal_source, macro_node_line)\n      else\n        format_macro_body node\n      end\n\n      false\n    end\n\n    private def format_macro_literal_only(node, source, macro_node_line)\n      # Only format the macro contents if it's valid Crystal code\n      Parser.new(source).parse\n\n      formatter, value = subformat(source)\n\n      # The formatted contents might have heredocs for which we must preserve\n      # trailing spaces, so here we copy those from the formatter we used\n      # to format the contents to this formatter (we add one because we insert\n      # a newline before the contents).\n      formatter.no_rstrip_lines.each do |line|\n        @no_rstrip_lines.add(macro_node_line + line + 1)\n      end\n\n      write_line\n      write value\n\n      next_macro_token\n\n      until @token.type.macro_end?\n        next_macro_token\n      end\n\n      skip_space_or_newline\n      check :MACRO_END\n      write_indent\n      write \"end\"\n      next_token\n    rescue ex : Crystal::SyntaxException\n      format_macro_body node\n    end\n\n    def format_macro_body(node)\n      if @token.keyword?(:end)\n        return format_macro_end\n      end\n\n      next_macro_token\n\n      if @token.type.macro_end?\n        return format_macro_end\n      end\n\n      body = node.body\n      if body.is_a?(Expressions) && body.expressions.empty?\n        while !@token.type.macro_end?\n          next_macro_token\n        end\n        return format_macro_end\n      end\n\n      inside_macro do\n        no_indent do\n          format_nested body, write_end_line: false, write_indent: false\n        end\n      end\n\n      skip_space_or_newline\n      check :MACRO_END\n      write \"end\"\n      next_token\n    end\n\n    def format_macro_end\n      write_line\n      write_indent\n      write \"end\"\n      next_token\n      false\n    end\n\n    def visit(node : MacroLiteral)\n      line = @line\n      @token.raw.scan(\"\\n\") do\n        line -= 1\n        @no_rstrip_lines.add line\n      end\n\n      raw = @token.raw\n\n      # If the macro literal has a backlash, but we are subformatting\n      # a macro, we need to escape it (if it got here unescaped it was escaped to\n      # begin with.)\n      if @subformat_nesting > 0\n        raw = raw.gsub(\"\\\\\", \"\\\\\" * (@subformat_nesting + 1))\n      end\n\n      write raw\n      next_macro_token\n      false\n    end\n\n    def visit(node : MacroVerbatim)\n      reset_macro_state\n\n      # `{% verbatim %}`\n      if inside_macro?\n        check :MACRO_CONTROL_START\n      else\n        check :OP_LCURLY_PERCENT\n      end\n      write \"{%\"\n      next_token_skip_space_or_newline\n      check_keyword :verbatim\n      write \" verbatim\"\n      next_token_skip_space\n      check_keyword :do\n      write \" do\"\n      next_token_skip_space\n      check :OP_PERCENT_RCURLY\n      write \" %}\"\n\n      @macro_state.control_nest += 1\n      check_macro_whitespace\n      next_macro_token\n      inside_macro { no_indent node.exp }\n      @macro_state.control_nest -= 1\n\n      # `{% end %}`\n      check :MACRO_CONTROL_START\n      write \"{%\"\n      next_token_skip_space_or_newline\n      check_keyword :end\n      write \" end\"\n      next_token_skip_space\n      check :OP_PERCENT_RCURLY\n      write \" %}\"\n\n      if inside_macro?\n        check_macro_whitespace\n        next_macro_token\n      else\n        next_token\n      end\n\n      false\n    end\n\n    def visit(node : MacroExpression)\n      reset_macro_state\n\n      old_column = @column\n\n      if node.output?\n        if inside_macro?\n          check :MACRO_EXPRESSION_START\n        else\n          check :OP_LCURLY_LCURLY\n        end\n        write_macro_slashes\n        write \"{{\"\n      else\n        case @token.type\n        when .macro_control_start?, .op_lcurly_percent?\n          # OK\n        else\n          check :MACRO_CONTROL_START\n        end\n        write_macro_slashes\n        write \"{%\"\n      end\n      macro_state = @macro_state\n      next_token\n\n      has_space = @token.type.space?\n      skip_space\n      has_newline = @token.type.newline?\n\n      old_indent = @indent\n      @indent = @column\n\n      if has_newline\n        write_line\n        write_indent\n        skip_space_or_newline\n        if @line_output.empty?\n          write_indent(@indent, node.exp)\n        else\n          indent(@indent, node.exp)\n        end\n      else\n        skip_space_or_newline\n        if has_space || !node.output?\n          write \" \"\n        end\n        indent(@column, node.exp)\n      end\n\n      @indent = old_indent\n\n      skip_space_or_newline\n      @macro_state = macro_state\n\n      if node.output?\n        if @wrote_newline\n          write_indent(old_column)\n        elsif has_space && !has_newline\n          write \" \"\n        elsif has_newline\n          write_line\n          write_indent(old_column)\n        end\n        check :OP_RCURLY\n        next_token\n        check :OP_RCURLY\n        write \"}}\"\n      else\n        check :OP_PERCENT_RCURLY\n        if @wrote_newline\n          write_indent(old_column)\n        elsif has_newline\n          write_line\n          write_indent(old_column)\n        else\n          write \" \"\n        end\n        write \"%}\"\n      end\n\n      if inside_macro?\n        check_macro_whitespace\n        next_macro_token\n      else\n        next_token\n      end\n\n      false\n    end\n\n    def visit(node : MacroIf)\n      reset_macro_state\n\n      if inside_macro?\n        check :MACRO_CONTROL_START\n      else\n        check :OP_LCURLY_PERCENT\n      end\n\n      write_macro_slashes\n      write \"{% \"\n\n      next_token_skip_space_or_newline\n\n      if @token.keyword?(:begin)\n        # This is rewritten to `if true`\n        write \"begin\"\n        next_token\n      elsif @token.keyword?(:unless)\n        # This is rewritten to `if !...`\n        node.then, node.else = node.else, node.then\n        write \"unless \"\n        next_token_skip_space_or_newline\n\n        outside_macro { indent(@column, node.cond) }\n      else\n        write_keyword :if, \" \"\n        outside_macro { indent(@column, node.cond) }\n      end\n\n      format_macro_if_epilogue(node, @macro_state)\n    end\n\n    def format_macro_if_epilogue(node, macro_state, check_end = true)\n      skip_space_or_newline\n      check :OP_PERCENT_RCURLY\n      write \" %}\"\n\n      @macro_state = macro_state\n      @macro_state.control_nest += 1\n      check_macro_whitespace\n\n      macro_node_line = @line\n      next_macro_token\n      format_macro_contents(node.then, macro_node_line)\n\n      unless node.else.is_a?(Nop)\n        check :MACRO_CONTROL_START\n        next_token_skip_space_or_newline\n\n        if @token.keyword?(:elsif)\n          sub_if = node.else.as(MacroIf)\n          next_token_skip_space_or_newline\n          write_macro_slashes\n          write \"{% elsif \"\n          outside_macro { indent(@column, sub_if.cond) }\n          format_macro_if_epilogue sub_if, macro_state, check_end: false\n        else\n          check_keyword :else\n          next_token_skip_space_or_newline\n          check :OP_PERCENT_RCURLY\n\n          write_macro_slashes\n          write \"{% else %}\"\n\n          @macro_state = macro_state\n          @macro_state.control_nest += 1\n          check_macro_whitespace\n\n          macro_node_line = @line\n          next_macro_token\n          format_macro_contents(node.else, macro_node_line)\n        end\n      end\n\n      @macro_state = macro_state\n      if check_end\n        check :MACRO_CONTROL_START\n        next_token_skip_space_or_newline\n\n        check_end\n        next_token_skip_space_or_newline\n        check :OP_PERCENT_RCURLY\n\n        write_macro_slashes\n        write \"{% end %}\"\n\n        if inside_macro?\n          check_macro_whitespace\n          next_macro_token\n        else\n          next_token\n        end\n      end\n\n      false\n    end\n\n    def visit(node : MacroFor)\n      reset_macro_state\n      old_macro_state = @macro_state\n\n      if inside_macro?\n        check :MACRO_CONTROL_START\n      else\n        check :OP_LCURLY_PERCENT\n      end\n\n      write_macro_slashes\n      write \"{% \"\n\n      next_token_skip_space_or_newline\n\n      write_keyword :for, \" \"\n\n      outside_macro do\n        node.vars.each_with_index do |var, i|\n          no_indent var\n          unless last?(i, node.vars)\n            skip_space_or_newline\n            if @token.type.op_comma?\n              write \", \"\n              next_token_skip_space_or_newline\n            end\n          end\n        end\n      end\n\n      write_keyword \" \", :in, \" \"\n\n      outside_macro { indent(@column, node.exp) }\n      skip_space_or_newline\n\n      check :OP_PERCENT_RCURLY\n      write \" %}\"\n\n      @macro_state.control_nest += 1\n      check_macro_whitespace\n\n      macro_node_line = @line\n      next_macro_token\n      format_macro_contents(node.body, macro_node_line)\n\n      @macro_state = old_macro_state\n\n      check :MACRO_CONTROL_START\n      next_token_skip_space_or_newline\n\n      check_end\n      next_token_skip_space_or_newline\n      check :OP_PERCENT_RCURLY\n\n      write_macro_slashes\n      write \"{% end %}\"\n\n      if inside_macro?\n        check_macro_whitespace\n        next_macro_token\n      else\n        next_token\n      end\n\n      false\n    end\n\n    def visit(node : MacroVar)\n      check :MACRO_VAR\n      write \"%\"\n      write node.name\n\n      if exps = node.exps\n        next_token\n        write_token :OP_LCURLY\n        skip_space_or_newline\n        exps.each_with_index do |exp, i|\n          indent(@column, exp)\n          skip_space_or_newline\n          if @token.type.op_comma?\n            write \", \" unless last?(i, exps)\n            next_token_skip_space_or_newline\n          end\n        end\n        check :OP_RCURLY\n        write \"}\"\n      end\n\n      next_macro_token\n\n      false\n    end\n\n    # If we are formatting macro contents, if there are nested macro\n    # control structures they are definitely escaped with `\\`,\n    # because otherwise we wouldn't be able to format the contents.\n    # So here we append those slashes. In theory the nesting can be\n    # very deep but it's usually just one level.\n    private def write_macro_slashes\n      @subformat_nesting.times do\n        write \"\\\\\"\n      end\n    end\n\n    def format_macro_contents(node, macro_node_line)\n      # If macro contents don't have interpolations nor newlines, and we\n      # are at the top-level (not already inside a macro) then the content\n      # must be a valid Crystal expression and we can format it.\n      #\n      # For example:\n      #\n      # {% if flag?(:foo) %}\n      #   puts \"This is an expression that we can format just fine\"\n      # {% end %}\n      if !inside_macro? && (value = macro_literal_contents(node)) && value.includes?(\"\\n\")\n        # Format the value and append 2 more spaces of indentation\n        begin\n          formatter, value = subformat(value)\n        rescue ex : Crystal::SyntaxException\n          raise Crystal::SyntaxException.new(\n            ex.message,\n            ex.line_number + macro_node_line,\n            ex.column_number,\n            ex.filename,\n            ex.size)\n        end\n\n        # The formatted contents might have heredocs for which we must preserve\n        # trailing spaces, so here we copy those from the formatter we used\n        # to format the contents to this formatter (we add one because we insert\n        # a newline before the contents).\n        formatter.no_rstrip_lines.each do |line|\n          @no_rstrip_lines.add(macro_node_line + line + 1)\n        end\n\n        write_line\n        write value\n        # No need to append a newline because the formatter value\n        # will already have it.\n        write_indent\n\n        increment_lines(macro_node_line + value.lines.size + 1 - @line)\n\n        line = @line\n\n        # We have to potentially skip multiple macro literal tokens\n        while @token.type.macro_literal?\n          next_macro_token\n        end\n\n        # Skipping the macro literal tokens might have altered `@line`:\n        # restore it to what it was before the macro tokens (we are\n        # already accounting for the lines in a different way).\n        if @line != line\n          increment_lines(line - @line)\n        end\n      else\n        inside_macro { no_indent node }\n      end\n    end\n\n    # Returns the node's String contents if it's composed entirely\n    # by MacroLiteral: either a MacroLiteral or an Expression composed\n    # only by MacroLiteral.\n    private def macro_literal_contents(node) : String?\n      return unless only_macro_literal?(node)\n\n      extract_macro_literal_contents(node)\n    end\n\n    private def only_macro_literal?(node)\n      case node\n      when MacroLiteral\n        true\n      when Expressions\n        node.expressions.all? do |exp|\n          only_macro_literal?(exp)\n        end\n      else\n        false\n      end\n    end\n\n    private def extract_macro_literal_contents(node)\n      String.build do |io|\n        extract_macro_literal_contents(node, io)\n      end\n    end\n\n    private def extract_macro_literal_contents(node, io)\n      if node.is_a?(MacroLiteral)\n        io << node.value\n      else\n        node.as(Expressions).expressions.each do |exp|\n          extract_macro_literal_contents(exp, io)\n        end\n      end\n    end\n\n    def subformat(source)\n      if @inside_struct_or_union > 0\n        mode = Parser::ParseMode::LibStructOrUnion\n      elsif @inside_enum < 0\n        mode = Parser::ParseMode::Enum\n      elsif @inside_lib > 0\n        mode = Parser::ParseMode::Lib\n      else\n        mode = Parser::ParseMode::Normal\n      end\n\n      parser = Parser.new(source, var_scopes: @vars.clone)\n      # parser.filename = formatter.filename\n      nodes = parser.parse(mode)\n\n      formatter = Formatter.new(source)\n      formatter.inside_lib = @inside_lib\n      formatter.inside_enum = @inside_enum\n      formatter.inside_struct_or_union = @inside_struct_or_union\n      formatter.indent = @indent + 2\n      formatter.skip_space_or_newline\n      formatter.write_indent\n      formatter.subformat_nesting = @subformat_nesting + 1\n      nodes.accept formatter\n      {formatter, formatter.finish}\n    end\n\n    def visit(node : Arg)\n      @last_arg_is_skip = false\n\n      restriction = node.restriction\n      default_value = node.default_value\n\n      if @inside_lib > 0\n        # This is the case of `fun foo(Char)`\n        if !@token.type.ident? && restriction\n          accept restriction\n          return false\n        end\n      end\n\n      if node.name.empty?\n        skip_space_or_newline\n      else\n        @vars.last.add(node.name)\n\n        at_skip = at_skip?\n\n        if !at_skip && node.external_name != node.name\n          if node.external_name.empty?\n            write \"_\"\n          elsif @token.type.delimiter_start?\n            accept StringLiteral.new(node.external_name)\n          else\n            write @token.value\n          end\n          write \" \"\n          next_token_skip_space_or_newline\n        end\n\n        @last_arg_is_skip = at_skip?\n\n        write @token.value\n        next_token\n      end\n\n      if restriction\n        skip_space_or_newline\n        write_token \" \", :OP_COLON, \" \"\n        skip_space_or_newline\n        accept restriction\n      end\n\n      if default_value\n        # The default value might be a Proc with args, so\n        # we need to remember this and restore it later\n        old_last_arg_is_skip = @last_arg_is_skip\n\n        skip_space_or_newline\n\n        check_align = check_assign_length node\n        write_token \" \", :OP_EQ, \" \"\n        before_column = @column\n        skip_space_or_newline\n        accept default_value\n        check_assign_align before_column, default_value if check_align\n\n        @last_arg_is_skip = old_last_arg_is_skip\n      end\n\n      # This is the case of an enum member\n      if @token.type.op_semicolon?\n        next_token\n        @lexer.skip_space\n        if @token.type.comment?\n          write_comment\n          @exp_needs_indent = true\n        else\n          write \";\" if @token.type.const?\n          write \" \"\n          @exp_needs_indent = @token.type.newline?\n        end\n      end\n\n      false\n    end\n\n    def reset_macro_state\n      @macro_state = Token::MacroState.default unless inside_macro?\n    end\n\n    def visit(node : Splat)\n      visit_splat node, :OP_STAR\n    end\n\n    def visit(node : DoubleSplat)\n      visit_splat node, :OP_STAR_STAR\n    end\n\n    def visit_splat(node, token : Token::Kind)\n      write_token token\n      skip_space_or_newline\n      accept node.exp\n      false\n    end\n\n    def visit(node : ProcNotation)\n      check_open_paren\n\n      paren_count = @paren_count\n\n      if inputs = node.inputs\n        # Check if it's ((X, Y) -> Z)\n        #                ^    ^\n        sub_paren_count = @paren_count\n        if check_open_paren\n          sub_paren_count = @paren_count\n        end\n\n        inputs.each_with_index do |input, i|\n          accept input\n          if @paren_count == sub_paren_count\n            skip_space_or_newline\n            if @token.type.op_comma?\n              write \", \" unless last?(i, inputs)\n              next_token_skip_space_or_newline\n            end\n          end\n        end\n\n        if sub_paren_count != paren_count\n          check_close_paren\n        end\n      end\n\n      skip_space_or_newline if paren_count == @paren_count\n      check_close_paren\n      skip_space\n\n      write \" \" if inputs\n      write_token :OP_MINUS_GT\n\n      if output = node.output\n        write \" \"\n        skip_space_or_newline\n        accept output\n      else\n        skip_space\n      end\n\n      check_close_paren\n\n      false\n    end\n\n    def visit(node : Self)\n      check_open_paren\n      write_keyword :self\n      check_close_paren\n      false\n    end\n\n    def visit(node : Var)\n      write node.name\n      next_token\n      false\n    end\n\n    def visit(node : InstanceVar)\n      write node.name\n      next_token\n      false\n    end\n\n    def visit(node : ClassVar)\n      write node.name\n      next_token\n      false\n    end\n\n    def visit(node : Global)\n      write node.name\n      next_token\n      false\n    end\n\n    def visit(node : ReadInstanceVar)\n      accept node.obj\n\n      skip_space_or_newline\n      write_token :OP_PERIOD\n      skip_space_or_newline\n      write node.name\n      next_token\n\n      false\n    end\n\n    def visit(node : Call)\n      # This is the case of `...`\n      if node.name == \"`\"\n        accept node.args.first\n        return false\n      end\n\n      special_call =\n        case node.name\n        when \"as\", \"as?\", \"is_a?\", \"nil?\", \"responds_to?\"\n          true\n        else\n          false\n        end\n\n      obj = node.obj\n\n      # Consider the case of `&.as(...)` and similar\n      if obj.is_a?(Nop)\n        obj = nil\n      end\n\n      # Consider the case of `as T`, that is, casting `self` without an explicit `self`\n      if special_call && obj.is_a?(Var) && obj.name == \"self\" && !@token.keyword?(:self)\n        obj = nil\n      end\n\n      column = @column\n      # The indent for arguments and block belonging to this node.\n      base_indent = @indent\n\n      # Special case: $1, $2, ...\n      if @token.type.global_match_data_index? && node.name.in?(\"[]\", \"[]?\") && obj.is_a?(Global)\n        write \"$\"\n        write @token.value\n        next_token\n        return false\n      end\n\n      write_token :OP_COLON_COLON if node.global?\n\n      if obj\n        # This handles unary operators written in prefix notation.\n        # The relevant distinction is that the call has a receiver and the\n        # current token is not that object but a unary operator.\n        if @token.type.unary_operator? && node.name == @token.type.to_s && !node.has_any_args?\n          write @token.type\n          next_token_skip_space_or_newline\n          accept obj\n          return false\n        end\n\n        accept obj\n\n        passed_backslash_newline = @token.passed_backslash_newline\n        needs_space = @token.type.space? || !node.name.in?(\"*\", \"/\", \"**\", \"//\")\n\n        slash_is_not_regex!\n        skip_space\n\n        # It's something like `foo.bar\\n\n        #                        .baz`\n        if (@token.type.newline?) || @wrote_newline\n          base_indent = @indent + 2\n          indent(base_indent) { consume_newlines }\n          write_indent(base_indent)\n        end\n\n        if !@token.type.op_period?\n          # It's an operator\n          if @token.type.op_lsquare?\n            write \"[\"\n            next_token_skip_space\n\n            args = node.args\n\n            if node.name == \"[]=\"\n              last_arg = args.pop\n            end\n\n            has_newlines, found_comment, _ = format_args args, true, node.named_args\n            if @token.type.op_comma? || @token.type.newline?\n              if has_newlines\n                write \",\"\n                found_comment = next_token_skip_space\n                write_line unless found_comment\n                write_indent\n                skip_space_or_newline\n              else\n                next_token_skip_space_or_newline\n              end\n            else\n              found_comment = skip_space_or_newline\n              write_indent if found_comment\n            end\n\n            # foo[&.bar]\n            if (block = node.block) && @token.type.op_amp?\n              has_args = !node.args.empty? || node.named_args\n              write \",\" if has_args\n              format_block(block, has_args)\n            end\n\n            write_token :OP_RSQUARE\n\n            if node.name == \"[]?\"\n              skip_space\n\n              # This might not be present in the case of `x[y] ||= z`\n              if @token.type.op_question?\n                write \"?\"\n                next_token\n              end\n            end\n\n            if last_arg\n              skip_space_or_newline\n\n              write \" =\"\n              next_token_skip_space\n              accept_assign_value_after_equals last_arg\n            end\n\n            return false\n          elsif @token.type.op_lsquare_rsquare?\n            write \"[]\"\n            next_token\n\n            if node.name == \"[]=\"\n              skip_space_or_newline\n              write_token \" \", :OP_EQ, \" \"\n              skip_space_or_newline\n              inside_call_or_assign do\n                accept node.args.last\n              end\n            end\n\n            return false\n          end\n\n          write \" \" if needs_space && !passed_backslash_newline\n          write node.name\n\n          # This is the case of a-1 and a+1\n          if @token.type.number?\n            @lexer.current_pos = @token.start + 1\n          end\n\n          slash_is_regex!\n\n          next_token\n          passed_backslash_newline = @token.passed_backslash_newline\n          found_comment = skip_space\n\n          if found_comment || @token.type.newline?\n            if @inside_call_or_assign == 0\n              next_indent = @indent + 2\n            else\n              next_indent = column == 0 ? 2 : column\n            end\n            indent(next_indent) do\n              skip_space_write_line\n              skip_space_or_newline\n            end\n            write_indent(next_indent, node.args.last)\n          else\n            write \" \" if needs_space && !passed_backslash_newline\n            inside_call_or_assign do\n              accept node.args.last\n            end\n          end\n\n          return false\n        end\n\n        @lexer.wants_def_or_macro_name do\n          next_token\n        end\n        skip_space\n\n        if (@token.type.newline?) || @wrote_newline\n          base_indent = @indent + 2\n          indent(base_indent) { consume_newlines }\n          write_indent(base_indent)\n        end\n\n        write \".\"\n\n        skip_space_or_newline\n      end\n\n      # This is for foo &.[bar] and &.[bar]?, or foo.[bar] and foo.[bar]?\n      if node.name.in?(\"[]\", \"[]?\") && @token.type.op_lsquare?\n        write \"[\"\n        next_token_skip_space_or_newline\n        format_call_args(node, false, base_indent)\n        skip_space_or_newline\n        write_token :OP_RSQUARE\n        write_token :OP_QUESTION if node.name == \"[]?\"\n        return false\n      end\n\n      # This is for foo.[bar] = baz\n      if node.name == \"[]=\" && @token.type.op_lsquare?\n        write \"[\"\n        next_token_skip_space_or_newline\n        args = node.args\n        last_arg = args.pop\n        format_call_args(node, true, base_indent)\n        skip_space_or_newline\n        write_token :OP_RSQUARE\n        skip_space_or_newline\n        write \" =\"\n        next_token_skip_space\n        accept_assign_value_after_equals last_arg\n        return false\n      end\n\n      # This is for foo.[] = bar\n      if node.name == \"[]=\" && @token.type.op_lsquare_rsquare?\n        write_token :OP_LSQUARE_RSQUARE\n        next_token_skip_space_or_newline\n        write \" =\"\n        next_token_skip_space\n        accept_assign_value_after_equals node.args.last\n        return false\n      end\n\n      assignment = Lexer.setter?(node.name)\n\n      if assignment\n        write node.name.rchop\n      else\n        write node.name\n      end\n      next_token\n\n      passed_backslash_newline = @token.passed_backslash_newline\n\n      if assignment\n        skip_space\n\n        next_token\n        if @token.type.op_lparen?\n          write \"=(\"\n          slash_is_regex!\n          next_token\n          format_call_args(node, true, base_indent)\n          skip_space_or_newline\n          write_token :OP_RPAREN\n        else\n          write \" =\"\n          skip_space\n          accept_assign_value_after_equals node.args.last\n        end\n\n        return false\n      end\n\n      has_parentheses = false\n      ends_with_newline = false\n      has_args = node.has_any_args?\n\n      has_newlines = false\n      found_comment = false\n\n      # For special calls we want to format `.as (Int32)` into `.as(Int32)`\n      # so we remove the space between \"as\" and \"(\".\n      skip_space if special_call\n\n      # If the call has a single argument which is a parenthesized `Expressions`,\n      # we skip whitespace between the method name and the arg. The parenthesized\n      # arg is transformed into a call with parenthesis: `foo (a)` becomes `foo(a)`.\n      if node.args.size == 1 &&\n         @token.type.space? &&\n         !node.named_args && !node.block_arg && !node.block &&\n         (expressions = node.args[0].as?(Expressions)) &&\n         expressions.keyword.paren? && expressions.expressions.size == 1\n        # ...except do not transform `foo ()` into `foo()`, as the former is\n        # actually semantically equivalent to `foo(nil)`\n        arg = expressions.expressions[0]\n        unless arg.is_a?(Nop)\n          skip_space\n          node.args[0] = arg\n        end\n      end\n\n      if @token.type.op_lparen?\n        slash_is_regex!\n        next_token\n\n        # If it's something like `foo.bar()` we rewrite it as `foo.bar`\n        # (parentheses are not needed). Also applies for special calls\n        # like `nil?` when there might not be a receiver.\n        if (obj || special_call) && !has_args && !node.block_arg && !node.block\n          skip_space_or_newline\n          check :OP_RPAREN\n          next_token\n          return false\n        end\n\n        write \"(\"\n        has_parentheses = true\n        has_newlines, found_comment, _ = format_call_args(node, true, base_indent)\n        found_comment ||= skip_space\n        if @token.type.newline?\n          ends_with_newline = true\n        end\n        indent(base_indent + 2) { skip_space_or_newline(last: true, at_least_one: ends_with_newline) }\n      elsif has_args || node.block_arg\n        write \" \" unless passed_backslash_newline\n        skip_space\n        has_newlines, found_comment, _ = format_call_args(node, false, base_indent)\n      end\n\n      if block = node.block\n        needs_space = !has_parentheses || has_args\n        block_indent = base_indent\n        skip_space\n        if has_parentheses\n          if @token.type.op_comma?\n            next_token\n            wrote_newline = skip_space(block_indent, write_comma: true)\n            if wrote_newline || @token.type.newline?\n              unless wrote_newline\n                next_token_skip_space_or_newline\n                write \",\"\n                write_line\n              end\n              needs_space = false\n              block_indent += 2 if !@token.type.op_rparen? # foo(1, ↵  &.foo) case\n              write_indent(block_indent)\n            else\n              write \",\" if !@token.type.op_rparen? # foo(1, &.foo) case\n            end\n          end\n          if @token.type.op_rparen?\n            if ends_with_newline\n              write_line unless found_comment || @wrote_newline\n              write_indent\n            end\n            write \")\"\n            next_token_skip_space_or_newline\n\n            indent(block_indent) { format_block block, needs_space }\n            return false\n          else\n            indent(block_indent) { format_block block, needs_space }\n\n            skip_space\n            if @token.type.newline?\n              ends_with_newline = true\n            end\n            skip_space_or_newline\n          end\n        else\n          indent(block_indent) { format_block block, needs_space }\n        end\n      end\n\n      finish_args(has_parentheses, has_newlines, ends_with_newline, found_comment, base_indent)\n\n      false\n    end\n\n    def format_call_args(node : ASTNode, has_parentheses, base_indent)\n      indent(base_indent) { format_args node.args, has_parentheses, node.named_args, node.block_arg }\n    end\n\n    def format_args(args : Array, has_parentheses, named_args = nil, block_arg = nil, needed_indent = @indent + 2, do_consume_newlines = false)\n      has_newlines = false\n      found_comment = false\n      @inside_call_or_assign += 1\n\n      unless args.empty?\n        has_newlines, found_comment, needed_indent = format_args_simple(args, needed_indent, do_consume_newlines)\n      end\n\n      if named_args\n        has_newlines, named_args_found_comment, needed_indent = format_named_args(args, named_args, needed_indent)\n        found_comment = true if args.empty? && named_args_found_comment\n      end\n\n      if block_arg\n        has_newlines = format_block_arg(block_arg, needed_indent)\n      end\n\n      @inside_call_or_assign -= 1\n\n      {has_newlines, found_comment, needed_indent}\n    end\n\n    def format_args_simple(args, needed_indent, do_consume_newlines)\n      has_newlines = false\n      found_comment = false\n\n      if @token.type.newline?\n        if do_consume_newlines\n          indent(needed_indent) { consume_newlines }\n          skip_space_or_newline\n        else\n          write_line\n          indent(needed_indent) { next_token_skip_space_or_newline }\n        end\n        next_needs_indent = true\n        has_newlines = true\n      end\n\n      skip_space_or_newline\n      args.each_with_index do |arg, i|\n        if next_needs_indent\n          write_indent(needed_indent, arg)\n        else\n          indent(@indent, arg)\n        end\n        next_needs_indent = false\n        unless last?(i, args)\n          if @last_is_heredoc && @token.type.newline?\n            skip_space_or_newline\n            write_indent\n          else\n            skip_space\n          end\n          slash_is_regex!\n          write_token :OP_COMMA\n\n          if @token.passed_backslash_newline\n            write_line\n            next_needs_indent = true\n            has_newlines = true\n          else\n            found_comment = skip_space(needed_indent)\n            if found_comment\n              write_indent(needed_indent)\n            else\n              if @token.type.newline?\n                indent(needed_indent) { consume_newlines }\n                next_needs_indent = true\n                has_newlines = true\n              else\n                write \" \"\n              end\n            end\n          end\n          skip_space_or_newline\n        end\n      end\n\n      {has_newlines, found_comment, needed_indent}\n    end\n\n    def format_named_args(args, named_args, needed_indent)\n      skip_space(needed_indent)\n\n      named_args_column = needed_indent\n\n      if args.empty?\n      else\n        write_token :OP_COMMA\n        found_comment = skip_space(needed_indent)\n        if found_comment || @token.type.newline?\n          write_indent(needed_indent) unless @last_is_heredoc\n        else\n          write \" \"\n        end\n      end\n\n      format_args named_args, false, needed_indent: named_args_column, do_consume_newlines: true\n    end\n\n    def format_block_arg(block_arg, needed_indent)\n      skip_space_or_newline\n      if @token.type.op_comma?\n        write \",\"\n        next_token_skip_space\n        if @token.type.newline?\n          write_line\n          write_indent(needed_indent)\n          has_newlines = true\n        else\n          write \" \"\n        end\n      end\n      skip_space_or_newline\n      write_token :OP_AMP\n      skip_space_or_newline\n      accept block_arg\n      has_newlines\n    end\n\n    def finish_args(has_parentheses, has_newlines, ends_with_newline, found_comment, column)\n      if has_parentheses\n        if @token.type.op_comma?\n          next_token\n          found_comment |= skip_space(column + 2, write_comma: true)\n          if @token.type.newline? && has_newlines\n            write \",\"\n            write_line\n            skip_space_or_newline(column + 2)\n            write_indent(column)\n            skip_space_or_newline(column)\n          else\n            found_comment |= skip_space_or_newline(column + 2)\n            if has_newlines\n              unless found_comment\n                write \",\"\n                write_line\n              end\n              write_indent(column)\n            end\n          end\n        elsif found_comment\n          write_indent(column)\n        end\n        check :OP_RPAREN\n\n        if ends_with_newline\n          write_line unless @wrote_newline\n          write_indent(column)\n        end\n        write \")\"\n        next_token\n      end\n    end\n\n    def format_parenthesized_args(args, named_args = nil)\n      write \"(\"\n      next_token_skip_space\n      has_newlines, found_comment, _ = format_args args, true, named_args: named_args\n      skip_space\n      ends_with_newline = false\n      if @token.type.newline?\n        ends_with_newline = true\n        next_token\n      end\n      skip_space_or_newline\n      finish_args(true, has_newlines, ends_with_newline, found_comment, @indent)\n    end\n\n    def visit(node : NamedArgument)\n      format_named_argument_name(node.name)\n      skip_space_or_newline\n      write_token :OP_COLON, \" \"\n\n      slash_is_regex!\n\n      skip_space_or_newline\n      accept node.value\n\n      false\n    end\n\n    def format_block(node, needs_space)\n      needs_comma = false\n      old_inside_call_or_assign = @inside_call_or_assign\n      @inside_call_or_assign = 0\n\n      comma_before_comment = false\n\n      if @token.type.op_comma?\n        next_token\n        next_token if @token.type.space?\n        if @token.type.comment?\n          write \",\"\n          needs_comma = false\n          comma_before_comment = true\n          @indent += 2\n        else\n          needs_comma = true\n        end\n        skip_space_or_newline\n        @indent -= 2 if comma_before_comment\n      end\n\n      if @token.keyword?(:do)\n        if comma_before_comment\n          @indent += 2\n          write_indent\n        else\n          write \" \"\n        end\n        write \"do\"\n        next_token\n        skip_space(@indent + 2)\n        body = format_block_args node.args, node\n        old_implicit_exception_handler_indent, @implicit_exception_handler_indent = @implicit_exception_handler_indent, @indent\n        format_nested_with_end body\n        @implicit_exception_handler_indent = old_implicit_exception_handler_indent\n        @indent -= 2\n      elsif @token.type.op_lcurly?\n        write \",\" if needs_comma\n        write \" {\"\n        next_token_skip_space\n        body = format_block_args node.args, node\n        next_token_skip_space_or_newline if @token.type.op_semicolon?\n        if @token.type.newline?\n          format_nested body\n          skip_space_or_newline\n          write_indent\n        else\n          unless body.is_a?(Nop)\n            write \" \"\n            accept body\n          end\n          skip_space_or_newline\n          write \" \"\n        end\n        write_token :OP_RCURLY\n      else\n        # It's foo &.bar\n        write \",\" if needs_comma\n        write \" \" if needs_space\n        write_token :OP_AMP\n        skip_space_or_newline\n        write \".\"\n        @lexer.wants_def_or_macro_name do\n          next_token_skip_space_or_newline\n        end\n\n        body = node.body\n        case body\n        when Call\n          call = body\n          clear_object call\n          indent(@indent, call)\n        when IsA\n          if body.obj.is_a?(Var)\n            if body.nil_check?\n              call = Call.new(\"nil?\")\n            else\n              call = Call.new(\"is_a?\", body.const)\n            end\n            accept call\n          else\n            clear_object(body)\n            accept body\n          end\n        when RespondsTo\n          if body.obj.is_a?(Var)\n            call = Call.new(\"responds_to?\", SymbolLiteral.new(body.name.to_s))\n            accept call\n          else\n            clear_object(body)\n            accept body\n          end\n        when Cast\n          if body.obj.is_a?(Var)\n            call = Call.new(\"as\", body.to)\n            accept call\n          else\n            clear_object(body)\n            accept body\n          end\n        when NilableCast\n          if body.obj.is_a?(Var)\n            call = Call.new(\"as?\", body.to)\n            accept call\n          else\n            clear_object(body)\n            accept body\n          end\n        when ReadInstanceVar\n          if body.obj.is_a?(Var)\n            call = Call.new(body.name)\n            accept call\n          else\n            clear_object(body)\n            accept body\n          end\n        when Not\n          if body.exp.is_a?(Var)\n            call = Call.new(\"!\")\n            accept call\n          else\n            clear_object(body)\n            accept body\n          end\n        else\n          raise \"BUG: unexpected node for &. argument, at #{node.location}, not #{body.class}\"\n        end\n      end\n\n      @inside_call_or_assign = old_inside_call_or_assign\n    end\n\n    def clear_object(node)\n      case node\n      when Call\n        if node.obj.is_a?(Var)\n          node.obj = nil\n        else\n          clear_object(node.obj)\n        end\n      when IsA\n        if node.obj.is_a?(Var)\n          node.obj = Nop.new\n        else\n          clear_object(node.obj)\n        end\n      when RespondsTo\n        if node.obj.is_a?(Var)\n          node.obj = Nop.new\n        else\n          clear_object(node.obj)\n        end\n      when Cast\n        if node.obj.is_a?(Var)\n          node.obj = Nop.new\n        else\n          clear_object(node.obj)\n        end\n      when NilableCast\n        if node.obj.is_a?(Var)\n          node.obj = Nop.new\n        else\n          clear_object(node.obj)\n        end\n      when Not\n        if node.exp.is_a?(Var)\n          node.exp = Nop.new\n        else\n          clear_object(node.exp)\n        end\n      end\n    end\n\n    def format_block_args(args, node)\n      return node.body if args.empty?\n\n      write_token \" \", :OP_BAR\n      skip_space_or_newline\n      args.each_index do |i|\n        format_block_arg\n\n        if @token.type.op_comma?\n          next_token_skip_space_or_newline\n          write \", \" unless last?(i, args)\n        end\n      end\n      skip_space_or_newline\n      write_token :OP_BAR\n      skip_space\n\n      node.body\n    end\n\n    def format_block_arg\n      if @token.type.op_star?\n        write_token :OP_STAR\n      end\n\n      case @token.type\n      when .op_lparen?\n        write_token :OP_LPAREN\n        skip_space_or_newline\n\n        while true\n          format_block_arg\n          if @token.type.op_comma?\n            next_token_skip_space_or_newline\n          end\n\n          break if @token.type.op_rparen?\n\n          write \", \"\n        end\n\n        write_token :OP_RPAREN\n      when .ident?\n        write @token.value\n        next_token\n      when .underscore?\n        write_token :UNDERSCORE\n      else\n        raise \"BUG: unexpected token #{@token.type}\"\n      end\n\n      skip_space_or_newline\n    end\n\n    def remove_to_skip(node, to_skip)\n      if to_skip > 0\n        body = node.body\n        if body.is_a?(ExceptionHandler) && body.implicit\n          sub_body = remove_to_skip(body, to_skip)\n          body.body = sub_body\n          return body\n        end\n\n        if body.is_a?(Expressions)\n          body.expressions = body.expressions[to_skip..-1]\n          case body.expressions.size\n          when 0\n            Nop.new\n          when 1\n            body.expressions.first\n          else\n            body\n          end\n        else\n          Nop.new\n        end\n      else\n        node.body\n      end\n    end\n\n    def visit(node : IsA)\n      if node.nil_check?\n        visit Call.new(node.obj, \"nil?\")\n      else\n        visit Call.new(node.obj, \"is_a?\", node.const)\n      end\n    end\n\n    def visit(node : RespondsTo)\n      visit Call.new(node.obj, \"responds_to?\", SymbolLiteral.new(node.name))\n    end\n\n    def visit(node : Or)\n      format_binary node, :OP_BAR_BAR, :OP_BAR_BAR_EQ\n    end\n\n    def visit(node : And)\n      format_binary node, :OP_AMP_AMP, :OP_AMP_AMP_EQ\n    end\n\n    def format_binary(node, token : Token::Kind, alternative : Token::Kind)\n      column = @column\n\n      accept node.left\n      skip_space_or_newline\n\n      # This is the case of `left ||= right`\n      if @token.type == alternative\n        write \" \"\n        write alternative\n        write \" \"\n        next_token_skip_space\n        case right = node.right\n        when Assign\n          accept_assign_value(right.value)\n        when Call\n          accept_assign_value(right.args.last)\n        else\n          raise \"BUG: expected Assign or Call after op assign, at #{node.location}\"\n        end\n        return false\n      end\n\n      write_token \" \", token\n      found_comment = skip_space\n      if found_comment || @token.type.newline?\n        if @inside_call_or_assign == 0\n          next_indent = @inside_cond == 0 ? @indent + 2 : @indent\n        else\n          next_indent = column == 0 ? 2 : column\n        end\n        indent(next_indent) do\n          skip_space_write_line\n          skip_space_or_newline\n        end\n        write_indent(next_indent, node.right)\n        return false\n      end\n\n      skip_space_or_newline\n      write \" \"\n      accept node.right\n\n      false\n    end\n\n    def visit(node : Not)\n      if @token.type.op_bang?\n        write_token :OP_BANG\n        skip_space_or_newline\n        accept node.exp\n      else\n        # Can be `exp.!`\n        accept node.exp\n        skip_space_or_newline\n        write_token :OP_PERIOD\n        skip_space_or_newline\n        write_token :OP_BANG\n      end\n\n      false\n    end\n\n    def visit(node : Assign)\n      target = node.target\n\n      @vars.last.add target.name if target.is_a?(Var)\n\n      accept target\n      skip_space_or_newline\n\n      check_align = check_assign_length node.target\n      slash_is_regex!\n      write_token \" \", :OP_EQ\n      skip_space(consume_newline: false)\n      accept_assign_value_after_equals node.value, check_align: check_align\n\n      false\n    end\n\n    def visit(node : OpAssign)\n      accept node.target\n      skip_space_or_newline\n\n      slash_is_regex!\n      write \" \"\n      write node.op\n      write \"=\"\n      next_token_skip_space\n      accept_assign_value_after_equals node.value\n\n      false\n    end\n\n    def accept_assign_value_after_equals(value, check_align = false)\n      if @token.type.newline?\n        next_token_skip_space_or_newline\n        write_line\n        write_indent(@indent + 2, value)\n      else\n        write \" \"\n        accept_assign_value value, check_align: check_align\n      end\n    end\n\n    def accept_assign_value(value, check_align = false)\n      before_column = @column\n      if @token.keyword?(:if) || @token.keyword?(:case) || value.is_a?(MacroIf)\n        indent(@column, value)\n      else\n        inside_call_or_assign do\n          accept value\n        end\n      end\n      check_assign_align before_column, value if check_align\n    end\n\n    def check_assign_length(exp)\n      if assign_length = @assign_length\n        target_length = assign_length(exp)\n        gap = assign_length - target_length\n        gap.times { write \" \" }\n        @assign_length = nil\n        true\n      else\n        false\n      end\n    end\n\n    def check_assign_align(before_column, exp)\n      if exp.is_a?(NumberLiteral)\n        @assign_infos << AlignInfo.new(0_u64, @line, before_column, @column, @column, true)\n      end\n    end\n\n    def visit(node : Require)\n      write_keyword :require, \" \"\n      accept StringLiteral.new(node.string)\n\n      false\n    end\n\n    def visit(node : VisibilityModifier)\n      case node.modifier\n      when .private?\n        write_keyword :private, \" \"\n      when .protected?\n        write_keyword :protected, \" \"\n      when .public?\n        # no keyword needed\n      end\n      accept node.exp\n\n      false\n    end\n\n    def visit(node : MagicConstant)\n      check node.name\n      write node.name\n      next_token\n\n      false\n    end\n\n    def visit(node : ModuleDef)\n      write_keyword :module, \" \"\n\n      accept node.name\n      format_type_vars node.type_vars, node.splat_index\n\n      format_nested_with_end node.body\n\n      false\n    end\n\n    def visit(node : AnnotationDef)\n      write_keyword :annotation, \" \"\n\n      accept node.name\n\n      skip_space(@indent + 2)\n\n      if @token.type.op_semicolon?\n        skip_semicolon_or_space_or_newline\n        check_end\n        write \"; end\"\n      else\n        skip_space_or_newline\n        check_end\n        write_line\n        write_indent\n        write \"end\"\n      end\n\n      next_token\n      false\n    end\n\n    def visit(node : ClassDef)\n      write_keyword :abstract, \" \" if node.abstract?\n      write_keyword (node.struct? ? Keyword::STRUCT : Keyword::CLASS), \" \"\n\n      accept node.name\n      format_type_vars node.type_vars, node.splat_index\n\n      if superclass = node.superclass\n        skip_space_or_newline\n        write_token \" \", :OP_LT, \" \"\n        skip_space_or_newline\n        accept superclass\n      end\n\n      format_nested_with_end node.body\n\n      false\n    end\n\n    def format_type_vars(type_vars, splat_index)\n      if type_vars\n        skip_space\n        write_token :OP_LPAREN\n        skip_space_or_newline\n        type_vars.each_with_index do |type_var, i|\n          write_token :OP_STAR if i == splat_index\n          write type_var\n          next_token_skip_space_or_newline\n          if @token.type.op_comma?\n            write \", \" unless last?(i, type_vars)\n            next_token_skip_space_or_newline\n          end\n        end\n        write_token :OP_RPAREN\n        skip_space\n      end\n    end\n\n    def visit(node : CStructOrUnionDef)\n      keyword = node.union? ? Keyword::UNION : Keyword::STRUCT\n      write_keyword keyword, \" \"\n\n      write node.name\n      next_token\n\n      @inside_struct_or_union += 1\n      format_nested_with_end node.body\n      @inside_struct_or_union -= 1\n\n      false\n    end\n\n    def visit(node : Include)\n      write_keyword :include, \" \"\n      accept node.name\n\n      false\n    end\n\n    def visit(node : Extend)\n      write_keyword :extend, \" \"\n      accept node.name\n\n      false\n    end\n\n    def visit(node : LibDef)\n      @inside_lib += 1\n\n      write_keyword :lib, \" \"\n\n      accept node.name\n\n      format_nested_with_end node.body\n\n      @inside_lib -= 1\n\n      false\n    end\n\n    def visit(node : EnumDef)\n      write_keyword :enum, \" \"\n      accept node.name\n\n      if base_type = node.base_type\n        skip_space\n        write_token \" \", :OP_COLON, \" \"\n        skip_space_or_newline\n        accept base_type\n      end\n\n      @inside_enum += 1\n      format_nested_with_end Expressions.from(node.members)\n      @inside_enum -= 1\n\n      false\n    end\n\n    def visit(node : TypeDeclaration)\n      accept node.var\n      skip_space_or_newline\n\n      # This is for a case like `x, y : Int32`\n      if @inside_struct_or_union && @token.type.op_comma?\n        @exp_needs_indent = false\n        write \", \"\n        next_token\n        return false\n      end\n\n      check :OP_COLON\n      next_token_skip_space_or_newline\n      write \" : \"\n      accept node.declared_type\n      if value = node.value\n        skip_space\n        check :OP_EQ\n        next_token_skip_space_or_newline\n        write \" = \"\n        accept value\n      end\n      false\n    end\n\n    def visit(node : UninitializedVar)\n      var = node.var\n\n      @vars.last.add var.name if var.is_a?(Var)\n\n      accept var\n      skip_space_or_newline\n      write_token \" \", :OP_EQ, \" \"\n      skip_space_or_newline\n      write_keyword :\"uninitialized\", \" \"\n      skip_space_or_newline\n      accept node.declared_type\n      false\n    end\n\n    def visit(node : Return)\n      format_control_expression node, :return\n    end\n\n    def visit(node : Break)\n      format_control_expression node, :break\n    end\n\n    def visit(node : Next)\n      format_control_expression node, :next\n    end\n\n    def format_control_expression(node, keyword : Keyword)\n      write_keyword keyword\n\n      has_parentheses = false\n      if @token.type.op_lparen?\n        has_parentheses = true\n        write \"(\"\n        next_token_skip_space_or_newline\n      end\n\n      if exp = node.exp\n        write \" \" unless has_parentheses\n        skip_space\n\n        # If the number of consecutive `{`s starting a tuple literal is 1 less\n        # than the level of tuple nesting in the actual AST node, this means the\n        # parser synthesized a TupleLiteral from multiple expressions, e.g.\n        #\n        #     return {1, 2}, 3, 4\n        #     return { {1, 2}, 3, 4 }\n        #\n        # The tuple depth is 2 in both cases but only 1 leading curly brace is\n        # present on the first return.\n        if exp.is_a?(TupleLiteral) && opening_curly_brace_count < leading_tuple_depth(exp)\n          format_args(exp.elements, has_parentheses)\n          skip_space if has_parentheses\n        else\n          indent(@indent, exp)\n          skip_space\n        end\n      end\n\n      write_token :OP_RPAREN if has_parentheses\n\n      false\n    end\n\n    def opening_curly_brace_count\n      @lexer.peek_ahead do\n        count = 0\n        while @lexer.token.type.op_lcurly?\n          count += 1\n          @lexer.next_token_skip_space_or_newline\n        end\n        count\n      end\n    end\n\n    def leading_tuple_depth(exp)\n      count = 0\n      while exp.is_a?(TupleLiteral)\n        count += 1\n        exp = exp.elements.first?\n      end\n      count\n    end\n\n    def visit(node : Yield)\n      if scope = node.scope\n        write_keyword :with, \" \"\n        accept scope\n        skip_space_or_newline\n        write \" \"\n      end\n\n      write_keyword :yield\n\n      if @token.type.op_lparen?\n        format_parenthesized_args(node.exps)\n      else\n        write \" \" unless node.exps.empty?\n        skip_space\n        format_args node.exps, false\n      end\n\n      false\n    end\n\n    def visit(node : Case)\n      slash_is_regex!\n      write_keyword :case\n      skip_space\n\n      if cond = node.cond\n        write \" \"\n        accept cond\n      end\n\n      skip_space_write_line\n\n      align_number = node.whens.all? { |a_when| a_when.conds.size === 1 && a_when.conds.first.is_a?(NumberLiteral) }\n\n      node.whens.each_with_index do |a_when, i|\n        format_when(node, a_when, last?(i, node.whens), align_number)\n        skip_space_or_newline(@indent + 2)\n      end\n\n      skip_space_or_newline\n\n      if a_else = node.else\n        write_indent\n        write_keyword :else\n        found_comment = skip_space(@indent + 2)\n        if @token.type.newline? || found_comment\n          write_line unless found_comment\n          format_nested(a_else)\n          skip_space_or_newline(@indent + 2)\n        else\n          while @token.type.op_semicolon?\n            next_token_skip_space\n          end\n\n          @when_infos << AlignInfo.new(node.object_id, @line, @column, @column, @column, false)\n          write \" \"\n          accept a_else\n          wrote_newline = @wrote_newline\n          found_comment = skip_space_or_newline\n          write_line unless found_comment || wrote_newline\n        end\n      end\n\n      check_end\n      write_indent\n      write \"end\"\n      next_token\n\n      false\n    end\n\n    def format_when(case_node, node, is_last, align_number)\n      skip_space_or_newline\n\n      slash_is_regex!\n      write_indent\n      write_keyword(node.exhaustive? ? Keyword::IN : Keyword::WHEN, \" \")\n      base_indent = @column\n      when_start_line = @line\n      when_start_column = @column\n      next_needs_indent = false\n      node.conds.each_with_index do |cond, i|\n        write_indent(base_indent) if next_needs_indent\n        accept cond\n        next_needs_indent = false\n        unless last?(i, node.conds)\n          skip_space_or_newline\n          if @token.type.op_comma?\n            write \",\"\n            slash_is_regex!\n            next_token\n            found_comment = skip_space\n            if found_comment || @token.type.newline?\n              write_line unless found_comment\n              skip_space_or_newline\n              next_needs_indent = true\n            else\n              write \" \"\n            end\n          end\n        end\n      end\n      when_column_middle = @column\n      indent { skip_space }\n      if @token.type.op_semicolon? || @token.keyword?(:then)\n        separator = @token.to_s\n        slash_is_regex!\n        if @token.type.op_semicolon?\n          skip_semicolon_or_space\n        else\n          next_token_skip_space\n        end\n        if @token.type.newline?\n          format_nested(node.body, @indent)\n        else\n          write \" \" if separator == \"then\"\n          write separator\n          write \" \"\n          when_column_end = @column\n          accept node.body\n          wrote_newline = @wrote_newline\n          if @line == when_start_line\n            @when_infos << AlignInfo.new(case_node.object_id, @line, when_start_column, when_column_middle, when_column_end, align_number)\n          end\n          found_comment = skip_space\n          write_line unless found_comment || wrote_newline\n        end\n      else\n        format_nested(node.body, @indent)\n      end\n\n      false\n    end\n\n    def visit(node : ImplicitObj)\n      false\n    end\n\n    def visit(node : Select)\n      slash_is_regex!\n      write_keyword :select\n      skip_space_write_line\n      skip_space_or_newline\n\n      node.whens.each do |a_when|\n        needs_indent = false\n        write_indent\n        write_keyword :when\n        skip_space_or_newline(@indent + 2)\n        write \" \"\n        a_when.conds.first.accept self\n        found_comment = skip_space(@indent + 2)\n        if @token.type.op_semicolon? || @token.keyword?(:then)\n          sep = @token.type.op_semicolon? ? \"; \" : \" then \"\n          next_token\n          skip_space(@indent + 2)\n          if @token.type.newline?\n            write_line\n            skip_space_or_newline(@indent + 2)\n            needs_indent = true\n          else\n            write sep\n            skip_space_or_newline(@indent + 2)\n          end\n        else\n          write_line unless found_comment\n          skip_space_or_newline(@indent + 2)\n          needs_indent = true\n        end\n        if needs_indent\n          format_nested(a_when.body)\n        else\n          a_when.body.accept self\n          write_line\n        end\n        skip_space_or_newline(@indent + 2)\n      end\n\n      if node_else = node.else\n        write_indent\n        write_keyword :else\n        found_comment = skip_space(@indent + 2)\n        write_line unless found_comment\n        skip_space_or_newline(@indent + 2)\n        format_nested(node_else)\n        skip_space_or_newline(@indent + 2)\n      end\n\n      write_indent\n      write_keyword :end\n\n      false\n    end\n\n    def visit(node : Annotation)\n      write_token :OP_AT_LSQUARE\n      skip_space_or_newline\n\n      node.path.accept self\n      skip_space_or_newline\n\n      if @token.type.op_lparen?\n        has_args = node.has_any_args?\n        if has_args\n          format_parenthesized_args(node.args, named_args: node.named_args)\n        else\n          next_token_skip_space_or_newline\n          check :OP_RPAREN\n          next_token\n        end\n      end\n\n      skip_space_or_newline\n      write_token :OP_RSQUARE\n\n      false\n    end\n\n    def visit(node : Cast)\n      visit Call.new(node.obj, \"as\", node.to)\n    end\n\n    def visit(node : NilableCast)\n      visit Call.new(node.obj, \"as?\", node.to)\n    end\n\n    def visit(node : TypeOf)\n      visit Call.new(\"typeof\", node.expressions)\n    end\n\n    def visit(node : SizeOf)\n      visit Call.new(\"sizeof\", node.exp)\n    end\n\n    def visit(node : InstanceSizeOf)\n      visit Call.new(\"instance_sizeof\", node.exp)\n    end\n\n    def visit(node : AlignOf)\n      visit Call.new(\"alignof\", node.exp)\n    end\n\n    def visit(node : InstanceAlignOf)\n      visit Call.new(\"instance_alignof\", node.exp)\n    end\n\n    def visit(node : OffsetOf)\n      visit Call.new(\"offsetof\", [node.offsetof_type, node.offset])\n    end\n\n    def visit(node : PointerOf)\n      visit Call.new(\"pointerof\", node.exp)\n    end\n\n    def visit(node : Underscore)\n      write_token :UNDERSCORE\n\n      false\n    end\n\n    def visit(node : MultiAssign)\n      node.targets.each_with_index do |target, i|\n        @vars.last.add target.name if target.is_a?(Var)\n\n        accept target\n        skip_space_or_newline\n        if @token.type.op_comma?\n          write \", \" unless last?(i, node.targets)\n          next_token_skip_space_or_newline\n        end\n      end\n\n      write_token \" \", :OP_EQ\n      skip_space\n      if @token.type.newline?\n        next_token_skip_space_or_newline\n        write_line\n        if node.values.size == 1\n          write_indent(@indent + 2, node.values.first)\n          return false\n        else\n          write_indent(@indent + 2)\n        end\n      else\n        write \" \"\n      end\n      format_multi_assign_values node.values\n\n      false\n    end\n\n    def format_multi_assign_values(values)\n      if values.size == 1\n        accept_assign_value values.first\n      else\n        indent(@column) do\n          values.each_with_index do |value, i|\n            accept value\n            unless last?(i, values)\n              skip_space_or_newline\n              if @token.type.op_comma?\n                write \", \"\n                next_token_skip_space_or_newline\n              end\n            end\n          end\n        end\n      end\n    end\n\n    def visit(node : ExceptionHandler)\n      column = @indent\n\n      implicit_handler = false\n      if node.implicit\n        accept node.body\n        write_line unless skip_space_or_newline last: true\n        implicit_handler = true\n        column = @implicit_exception_handler_indent\n      else\n        if node.suffix\n          inline = false\n\n          # This is the case of:\n          #\n          #     begin exp rescue exp end\n          #\n          # It's parsed as:\n          #\n          #     begin (exp rescue exp) end\n          #\n          # So it's a suffix rescue inside a begin/end, but the Parser\n          # returns it as an ExceptionHandler node.\n          if @token.keyword?(:begin)\n            inline = true\n            write_keyword :begin\n            skip_space_or_newline\n            write \" \"\n          end\n\n          accept node.body\n          passed_backslash_newline = @token.passed_backslash_newline\n          skip_space\n          write \" \" unless passed_backslash_newline\n          if @token.keyword?(:rescue)\n            write_keyword :rescue\n            write \" \"\n            next_token_skip_space_or_newline\n            accept node.rescues.not_nil!.first.not_nil!.body\n          elsif @token.keyword?(:ensure)\n            write_keyword :ensure\n            write \" \"\n            next_token_skip_space_or_newline\n            accept node.ensure.not_nil!\n          else\n            raise \"expected 'rescue' or 'ensure'\"\n          end\n\n          if inline\n            skip_space_or_newline\n            write \" \"\n            write_keyword :end\n          end\n\n          return false\n        end\n      end\n\n      unless implicit_handler\n        write_keyword :begin\n        format_nested(node.body, column)\n      end\n\n      if node_rescues = node.rescues\n        node_rescues.each do |node_rescue|\n          skip_space_or_newline(column + 2, last: true)\n          write_indent(column)\n          write_keyword :rescue\n\n          name = node_rescue.name\n          if name\n            skip_space_or_newline\n            write \" \"\n            write name\n            next_token\n          end\n\n          if types = node_rescue.types\n            skip_space_or_newline\n            if name\n              write_token \" \", :OP_COLON, \" \"\n              skip_space_or_newline\n            else\n              write \" \"\n            end\n            types.each_with_index do |type, j|\n              accept type\n              unless last?(j, types)\n                skip_space_or_newline\n                if @token.type.op_bar?\n                  write \" | \"\n                  next_token_skip_space_or_newline\n                end\n              end\n            end\n          end\n          format_nested(node_rescue.body, column)\n        end\n      end\n\n      if node_else = node.else\n        skip_space_or_newline(column + 2, last: true)\n        write_indent(column)\n        write_keyword :else\n        format_nested(node_else, column)\n      end\n\n      if node_ensure = node.ensure\n        skip_space_or_newline(column + 2, last: true)\n        write_indent(column)\n        write_keyword :ensure\n        format_nested(node_ensure, column)\n      end\n\n      unless implicit_handler\n        skip_space_or_newline(column + 2, last: true)\n        check_end\n        write_indent(column)\n        write \"end\"\n        next_token\n      end\n\n      false\n    end\n\n    def visit(node : Alias)\n      format_alias_or_typedef node, :alias, node.value\n    end\n\n    def visit(node : TypeDef)\n      format_alias_or_typedef node, :type, node.type_spec\n    end\n\n    def format_alias_or_typedef(node, keyword : Keyword, value)\n      write_keyword keyword, \" \"\n\n      name = node.name\n      if name.is_a?(Path)\n        accept name\n      else\n        write name\n        next_token\n      end\n\n      skip_space\n      write_token \" \", :OP_EQ, \" \"\n      skip_space_or_newline\n\n      accept value\n\n      false\n    end\n\n    def visit(node : ProcPointer)\n      write_token :OP_MINUS_GT\n      skip_space_or_newline\n\n      if node.global?\n        write_token :OP_COLON_COLON\n        skip_space_or_newline\n      end\n\n      if obj = node.obj\n        accept obj\n        skip_space\n        write_token :OP_PERIOD\n        skip_space_or_newline\n      end\n\n      write node.name\n      next_token_skip_space\n      next_token_skip_space if @token.type.op_eq?\n\n      if @token.type.op_lparen?\n        write \"(\" unless node.args.empty?\n        next_token_skip_space\n        node.args.each_with_index do |arg, i|\n          accept arg\n          skip_space_or_newline\n          if @token.type.op_comma?\n            write \", \" unless last?(i, node.args)\n            next_token_skip_space_or_newline\n          end\n        end\n        write \")\" unless node.args.empty?\n        next_token_skip_space\n      end\n\n      false\n    end\n\n    def visit(node : ProcLiteral)\n      write_token :OP_MINUS_GT\n      skip_space_or_newline\n\n      a_def = node.def\n\n      if @token.type.op_lparen?\n        write \"(\" unless a_def.args.empty?\n        next_token_skip_space_or_newline\n\n        a_def.args.each_with_index do |arg, i|\n          accept arg\n          skip_space_or_newline\n          if @token.type.op_comma?\n            write \", \" unless last?(i, a_def.args)\n            next_token_skip_space_or_newline\n          end\n        end\n\n        check :OP_RPAREN\n        write \")\" unless a_def.args.empty?\n        next_token_skip_space_or_newline\n      end\n\n      if return_type = a_def.return_type\n        check :OP_COLON\n        write \" : \"\n        next_token_skip_space_or_newline\n        accept return_type\n        skip_space_or_newline\n      end\n\n      write \" \"\n\n      is_do = false\n      if @token.keyword?(:do)\n        write_keyword :do\n        is_do = true\n      else\n        write_token :OP_LCURLY\n        write \" \" if a_def.body.is_a?(Nop)\n      end\n      skip_space\n\n      if @token.type.newline?\n        format_nested a_def.body\n      else\n        skip_space_or_newline\n        unless a_def.body.is_a?(Nop)\n          write \" \"\n          accept a_def.body\n          write \" \"\n        end\n      end\n\n      indent do\n        skip_space_or_newline\n      end\n\n      if is_do\n        check_end\n        write_indent\n        write \"end\"\n        next_token\n      else\n        if @wrote_newline\n          write_indent\n        end\n        write_token :OP_RCURLY\n      end\n\n      false\n    end\n\n    def visit(node : ExternalVar)\n      check :GLOBAL\n      write @token.value\n      next_token_skip_space_or_newline\n\n      if @token.type.op_eq?\n        write \" = \"\n        next_token_skip_space_or_newline\n        write @token.value\n        next_token_skip_space_or_newline\n      end\n\n      write_token \" \", :OP_COLON, \" \"\n      skip_space_or_newline\n\n      accept node.type_spec\n\n      false\n    end\n\n    def visit(node : Out)\n      write_keyword :out, \" \"\n      accept node.exp\n\n      false\n    end\n\n    def visit(node : Metaclass)\n      accept node.name\n      skip_space\n\n      write_token :OP_PERIOD\n      skip_space_or_newline\n      write_keyword :class\n\n      false\n    end\n\n    def visit(node : Block)\n      # Handled in format_block\n      false\n    end\n\n    def visit(node : When)\n      # Handled in format_when\n      false\n    end\n\n    def visit(node : Rescue)\n      # Handled in visit(node : ExceptionHandler)\n      false\n    end\n\n    def visit(node : MacroId)\n      false\n    end\n\n    def visit(node : MetaVar)\n      false\n    end\n\n    def visit(node : Asm)\n      write_keyword :asm\n      skip_space_or_newline\n\n      column = @column\n      has_newlines = false\n\n      write_token :OP_LPAREN\n      skip_space\n\n      if @token.type.newline?\n        @indent += 2\n        consume_newlines\n        has_newlines = true\n      end\n      skip_space_or_newline\n\n      string = StringLiteral.new(node.text)\n\n      if has_newlines\n        write_indent(@indent, string)\n      else\n        indent(@column, string)\n      end\n\n      skip_space\n\n      if @token.type.newline?\n        consume_newlines\n        if node.outputs || node.inputs\n          column += 4\n          write_indent(column)\n        end\n      end\n\n      skip_space_or_newline\n\n      if node.volatile? || node.alignstack? || node.intel? || node.can_throw?\n        expected_parts = 4\n      elsif node.clobbers\n        expected_parts = 3\n      elsif node.inputs\n        expected_parts = 2\n      elsif node.outputs\n        expected_parts = 1\n      else\n        expected_parts = 0\n      end\n\n      write \" \" if expected_parts > 0\n      colon_column = @column\n\n      part_index = 0\n      while part_index < expected_parts\n        if @token.type.op_colon_colon?\n          write_token :OP_COLON_COLON\n          part_index += 2\n        elsif @token.type.op_colon?\n          write_token :OP_COLON\n          part_index += 1\n        end\n        skip_space\n        consume_newlines\n\n        case part_index\n        when 1\n          if outputs = node.outputs\n            visit_asm_parts outputs, colon_column do |output|\n              accept output\n            end\n          end\n        when 2\n          if inputs = node.inputs\n            visit_asm_parts inputs, colon_column do |input|\n              accept input\n            end\n          end\n        when 3\n          if clobbers = node.clobbers\n            visit_asm_parts clobbers, colon_column do |clobber|\n              accept StringLiteral.new(clobber)\n            end\n          end\n        when 4\n          parts = [node.volatile?, node.alignstack?, node.intel?, node.can_throw?].select(&.itself)\n          visit_asm_parts parts, colon_column do\n            accept StringLiteral.new(\"\")\n          end\n        else break\n        end\n      end\n\n      # Mop up any trailing unused : or ::, don't write them since they should be removed\n      while @token.type.in?(Token::Kind::OP_COLON, Token::Kind::OP_COLON_COLON)\n        next_token_skip_space_or_newline\n      end\n\n      skip_space_or_newline\n\n      if has_newlines\n        @indent -= 2\n\n        write_line unless @wrote_newline\n        write_indent\n      end\n\n      write_token :OP_RPAREN\n\n      false\n    end\n\n    def visit(node : AsmOperand)\n      accept StringLiteral.new(node.constraint)\n\n      skip_space_or_newline\n      write_token :OP_LPAREN\n      skip_space_or_newline\n      accept node.exp\n      skip_space_or_newline\n      write_token :OP_RPAREN\n\n      false\n    end\n\n    def visit_asm_parts(parts, colon_column, &) : Nil\n      if @wrote_newline\n        write_indent\n      else\n        write \" \"\n      end\n\n      column = @column\n\n      parts.each_with_index do |part, i|\n        yield part\n        skip_space\n\n        if @token.type.op_comma?\n          write \",\" unless last?(i, parts)\n          next_token_skip_space\n        end\n\n        if @token.type.newline?\n          if last?(i, parts)\n            next_token_skip_space_or_newline\n            if @token.type.op_colon? || @token.type.op_colon_colon?\n              write_line\n              write_indent(colon_column)\n            end\n          else\n            consume_newlines\n            write_indent(last?(i, parts) ? colon_column : column)\n            skip_space_or_newline\n          end\n        else\n          skip_space_or_newline\n          if last?(i, parts)\n            if @token.type.op_colon? || @token.type.op_colon_colon?\n              write \" \"\n            end\n          else\n            write \" \"\n          end\n        end\n      end\n    end\n\n    def visit(node : ASTNode)\n      raise \"BUG: unexpected node: #{node.class} at #{node.location}\"\n    end\n\n    def to_s(io : IO) : Nil\n      io << @output\n    end\n\n    def next_token\n      current_line_number = @lexer.line_number\n      @token = @lexer.next_token\n      if @token.type.delimiter_start?\n        increment_lines(@lexer.line_number - current_line_number)\n      elsif @token.type.newline?\n        if !@lexer.heredocs.empty? && !@consuming_heredocs\n          write_line\n          consume_heredocs\n        end\n      end\n      @token\n    end\n\n    def next_string_token\n      current_line_number = @lexer.line_number\n      @token = @lexer.next_string_token(@token.delimiter_state)\n      increment_lines(@lexer.line_number - current_line_number)\n      @token\n    end\n\n    def next_string_array_token\n      @token = @lexer.next_string_array_token\n    end\n\n    def next_macro_token\n      current_line_number = @lexer.line_number\n\n      char = @lexer.current_char\n      @token = @lexer.next_macro_token(@macro_state, false)\n      @macro_state = @token.macro_state\n\n      increment_lines(@lexer.line_number - current_line_number)\n\n      # Unescape\n      if char == '\\\\' && !@token.raw.starts_with?(char)\n        @token.raw = \"\\\\#{@token.raw}\"\n      end\n    end\n\n    def next_token_skip_space\n      next_token\n      skip_space\n    end\n\n    def next_token_skip_space_or_newline\n      next_token\n      skip_space_or_newline\n    end\n\n    def skip_space(write_comma : Bool = false, consume_newline : Bool = true)\n      base_column = @column\n      has_space = false\n\n      if @token.type.space?\n        if @token.passed_backslash_newline\n          if write_comma\n            write \", \"\n          else\n            write \" \"\n          end\n          write \"\\\\\"\n          write_line\n          @indent += 2 unless @passed_backslash_newline\n          write_indent\n          next_token\n          @passed_backslash_newline = true\n          if @token.type.space? || @token.type.comment?\n            return skip_space(write_comma, consume_newline)\n          else\n            return false\n          end\n        end\n\n        next_token\n        has_space = true\n      end\n\n      if @token.type.comment?\n        needs_space = has_space && base_column != 0\n        if write_comma\n          write \", \"\n        else\n          write \" \" if needs_space\n        end\n        write_comment(needs_indent: !needs_space, consume_newline: consume_newline)\n        true\n      else\n        false\n      end\n    end\n\n    def skip_space(indent : Int32, write_comma = false)\n      indent(indent) { skip_space(write_comma) }\n    end\n\n    def skip_space_or_newline(last : Bool = false, at_least_one : Bool = false, next_comes_end : Bool = false)\n      just_wrote_line = @wrote_newline\n      base_column = @column\n      has_space = false\n      newlines = 0\n      while true\n        case @token.type\n        when .space?\n          has_space = true\n          next_token\n        when .newline?\n          newlines += 1\n          next_token\n        when .op_semicolon?\n          next_token\n        else\n          break\n        end\n      end\n      if @token.type.comment?\n        needs_space = has_space && newlines == 0 && base_column != 0\n        if needs_space\n          write \" \"\n        elsif last && newlines > 0\n          if newlines > 1\n            write_line if !just_wrote_line && !@wrote_newline\n            write_line\n          elsif !@wrote_newline\n            write_line\n          elsif at_least_one\n            write_line\n          end\n        end\n        write_comment(needs_indent: !needs_space, next_comes_end: last)\n        true\n      else\n        false\n      end\n    end\n\n    def skip_space_or_newline(indent : Int32, last : Bool = false, at_least_one : Bool = false, next_comes_end : Bool = false)\n      indent(indent) { skip_space_or_newline(last, at_least_one, next_comes_end) }\n    end\n\n    def slash_is_regex!\n      @lexer.slash_is_regex!\n    end\n\n    def slash_is_not_regex!\n      @lexer.slash_is_not_regex!\n    end\n\n    def skip_space_write_line\n      found_comment = skip_space\n      write_line unless found_comment || @wrote_newline\n      found_comment\n    end\n\n    def skip_semicolon\n      while @token.type.op_semicolon?\n        next_token\n      end\n    end\n\n    def skip_semicolon_or_space\n      found_comment = false\n      while true\n        case @token.type\n        when .op_semicolon?\n          next_token\n        when .space?\n          found_comment ||= skip_space\n        else\n          break\n        end\n      end\n      found_comment\n    end\n\n    def skip_semicolon_or_space_or_newline\n      while true\n        case @token.type\n        when .op_semicolon?\n          next_token\n        when .space?, .newline?\n          skip_space_or_newline\n        else\n          break\n        end\n      end\n    end\n\n    def jump_semicolon\n      skip_space\n      skip_semicolon\n      skip_space_or_newline\n    end\n\n    def write_comment(needs_indent = true, consume_newline = true, next_comes_end = false)\n      while @token.type.comment?\n        empty_line = @line_output.empty?\n        if empty_line\n          write_indent if needs_indent\n        end\n\n        value = @token.value.to_s.strip\n        raw_after_comment_value = value[1..-1]\n        after_comment_value = raw_after_comment_value.strip\n        if after_comment_value.starts_with?(\"=>\")\n          value = \"\\# => #{after_comment_value[2..-1].strip}\"\n        elsif after_comment_value.each_char.all? { |c| c.ascii_whitespace? || c == '#' }\n          # Nothing, leave sequences of whitespaces and '#' as is\n        else\n          char_1 = value[1]?\n          if char_1 && !char_1.ascii_whitespace?\n            value = \"\\# #{value[1..-1].strip}\"\n          end\n        end\n\n        if !@last_write.empty? && !@last_write[-1].ascii_whitespace?\n          write \" \"\n        end\n\n        unless @line_output.to_s.strip.empty?\n          @comment_columns[-1] = @column\n        end\n\n        if empty_line\n          current_doc_comment = @current_doc_comment\n\n          if after_comment_value.starts_with?(\"```\")\n            # Determine code fence language (what comes after \"```\")\n            language = after_comment_value.lchop(\"```\").strip\n\n            if current_doc_comment && language.empty?\n              # A code fence ends when nothing comes after \"```\"\n              current_doc_comment.end_line = @line - 1\n              @doc_comments << current_doc_comment if current_doc_comment.needs_format\n              @current_doc_comment = nil\n            else\n              # Normalize crystal language tag\n              if language.in?(\"cr\", \"crystal\")\n                value = value.rchop(language)\n                language = \"\"\n              end\n\n              # We only format crystal code (empty by default means crystal)\n              needs_format = language.empty?\n              @current_doc_comment = CommentInfo.new(@line + 1, needs_format)\n            end\n          end\n        end\n\n        @wrote_comment = true\n        write value\n        next_token_skip_space\n        if consume_newline\n          consume_newlines(next_comes_end: next_comes_end)\n          skip_space_or_newline\n        end\n      end\n    end\n\n    def consume_newlines(next_comes_end = false)\n      if @token.type.newline?\n        write_line unless @wrote_newline\n        next_token_skip_space\n\n        if @token.type.newline? && !next_comes_end\n          write_line\n          @wrote_double_newlines = true\n        end\n\n        skip_space_or_newline(last: next_comes_end, at_least_one: true)\n      end\n    end\n\n    def indent(&)\n      @indent += 2\n      value = yield\n      @indent -= 2\n      value\n    end\n\n    def indent(node : ASTNode)\n      indent { accept node }\n    end\n\n    def indent(indent : Int, &)\n      old_indent = @indent\n      @indent = indent\n      value = yield\n      @passed_backslash_newline = false\n      @indent = old_indent\n      value\n    end\n\n    def indent(indent : Int, node : ASTNode | HashLiteral::Entry | NamedTupleLiteral::Entry)\n      indent(indent) { accept node }\n    end\n\n    def no_indent(node : ASTNode)\n      no_indent { accept node }\n    end\n\n    def no_indent(&)\n      old_indent = @indent\n      @indent = 0\n      yield\n      @indent = old_indent\n    end\n\n    def write_indent\n      write_indent @indent\n    end\n\n    def write_indent(indent)\n      indent.times { write \" \" }\n    end\n\n    def write_indent(indent, node)\n      write_indent(indent) unless node.is_a?(Nop)\n      indent(indent, node)\n    end\n\n    def write_indent(indent = @indent, &)\n      write_indent(indent)\n      indent(indent) { yield }\n    end\n\n    def write(string : String)\n      @output << string\n      @line_output << string\n      last_newline = string.rindex('\\n')\n      if last_newline\n        @column = string.size - last_newline - 1\n      else\n        @column += string.size\n      end\n\n      @wrote_newline = false\n      @wrote_double_newlines = false\n      @last_write = string\n    end\n\n    def write(obj)\n      write obj.to_s\n    end\n\n    def write_line\n      @wrote_double_newlines = false\n      @current_doc_comment = nil unless @wrote_comment\n      @wrote_comment = false\n\n      @output.puts\n      @line_output.clear\n      @column = 0\n      @wrote_newline = true\n      increment_line\n      @last_write = \"\"\n      if @passed_backslash_newline\n        @passed_backslash_newline = false\n        @indent -= 2\n      end\n    end\n\n    def increment_line\n      @line += 1\n      @comment_columns << nil\n    end\n\n    def decrement_line\n      @line -= 1\n      @comment_columns.pop\n    end\n\n    def increment_lines(count)\n      if count < 0\n        (-count).times { decrement_line }\n      else\n        count.times { increment_line }\n      end\n    end\n\n    def finish\n      raise \"BUG: unclosed parenthesis\" if @paren_count > 0\n\n      skip_space\n      write_line\n      skip_space_or_newline last: true\n\n      # rstrip instead of string in case this is a subformat\n      # (we want to preserve the leading indentation)\n      result = to_s.rstrip\n\n      lines = result.split('\\n')\n      fix_heredocs(lines)\n      align_infos(lines, @when_infos)\n      align_infos(lines, @hash_infos)\n      align_infos(lines, @assign_infos)\n      align_comments(lines)\n      format_doc_comments(lines)\n      line_number = -1\n      lines.map! do |line|\n        line_number += 1\n        if @no_rstrip_lines.includes?(line_number)\n          line\n        else\n          line.rstrip\n        end\n      end\n      result = lines.join('\\n') + '\\n'\n      result = \"\" if result == \"\\n\"\n      if @shebang\n        result = result[0] + result[2..-1]\n      end\n      result\n    end\n\n    def fix_heredocs(lines)\n      @heredoc_fixes.each do |fix|\n        min_difference = (fix.start_line..fix.end_line).min_of do |line_number|\n          leading_space_count(lines[line_number], fix.difference)\n        end\n        indent_before_start = leading_space_count(lines[fix.start_line - 1], fix.difference)\n        min_difference = Math.max(min_difference - indent_before_start, 0)\n\n        fix.start_line.upto(fix.end_line) do |line_number|\n          line = lines[line_number]\n          lines[line_number] = line[min_difference..]\n        end\n      end\n    end\n\n    def leading_space_count(line, max_count)\n      max_count.times do |index|\n        return index unless line.byte_at?(index) == 0x20 # ' '\n      end\n      max_count\n    end\n\n    # Align series of successive inline when/else (in a case),\n    # or hash literals (the left side of the =>)\n    def align_infos(lines, align_infos)\n      max_size = nil\n      last_info = nil\n\n      align_infos.each_with_index do |align_info, i|\n        if max_size\n          align_info lines, align_info, max_size\n        else\n          last_info, max_size = find_last_info(align_infos, align_info, i + 1)\n          align_info lines, align_info, max_size\n        end\n\n        if last_info && align_info.line == last_info.line\n          last_info = nil\n          max_size = nil\n        end\n      end\n    end\n\n    def find_last_info(align_infos, base, i)\n      max_size = base.size\n\n      while i < align_infos.size\n        current = align_infos[i]\n        break unless current.id == base.id\n        break unless base.line + 1 == current.line\n\n        base = current\n        max_size = base.size if base.size > 0 && base.size > max_size\n\n        i += 1\n      end\n\n      {base, max_size}\n    end\n\n    def align_info(lines, info, max_size)\n      gap = max_size - info.size\n      return if gap == 0\n\n      line = lines[info.line]\n\n      if info.number\n        middle = info.start_column\n      else\n        middle = info.middle_column\n      end\n\n      before = line[0...middle]\n      after = line[middle..-1]\n      result = String.build do |str|\n        str << before\n        gap.times { str << ' ' }\n        str << after\n      end\n\n      lines[info.line] = result\n\n      # Make sure to move the comment in this line too\n      comment_column = @comment_columns[info.line]?\n      if comment_column\n        comment_column += gap\n        @comment_columns[info.line] = comment_column\n      end\n    end\n\n    # Align series of successive comments\n    def align_comments(lines)\n      max_column = nil\n\n      lines.each_with_index do |line, i|\n        comment_column = @comment_columns[i]?\n        if comment_column\n          if max_column\n            lines[i] = align_comment line, i, comment_column, max_column\n          else\n            max_column = find_max_column(lines, i + 1, comment_column)\n            lines[i] = align_comment line, i, comment_column, max_column\n          end\n        else\n          max_column = nil\n        end\n      end\n    end\n\n    def find_max_column(lines, base, max)\n      while base < @comment_columns.size\n        comment_column = @comment_columns[base]?\n        break unless comment_column\n\n        max = comment_column if comment_column > max\n        base += 1\n      end\n\n      max\n    end\n\n    def align_comment(line, i, comment_column, max_column)\n      return line if comment_column == max_column\n\n      source_line = line[0...comment_column]\n      comment_line = line[comment_column..-1]\n      gap = max_column - comment_column\n\n      result = String.build do |str|\n        str << source_line\n        gap.times { str << ' ' }\n        str << comment_line\n      end\n      result\n    end\n\n    def format_doc_comments(lines)\n      @doc_comments.reverse_each do |doc_comment|\n        next if doc_comment.start_line > doc_comment.end_line\n\n        first_line = lines[doc_comment.start_line]\n        sharp_index = first_line.index!('#')\n\n        comment = String.build do |str|\n          (doc_comment.start_line..doc_comment.end_line).each do |i|\n            line = lines[i].strip[1..-1]\n            line = line[1..-1] if line[0]? == ' '\n            str << line\n            str << '\\n'\n          end\n        end\n\n        begin\n          formatted_comment = Formatter.format(comment)\n          formatted_lines = formatted_comment.lines\n          formatted_lines.map! do |line|\n            String.build do |str|\n              sharp_index.times { str << ' ' }\n              str << \"# \"\n              str << line\n            end\n          end\n          lines[doc_comment.start_line..doc_comment.end_line] = formatted_lines\n        rescue Crystal::SyntaxException\n          # For now we don't care if doc comments have syntax errors,\n          # they shouldn't prevent formatting the real code\n        end\n      end\n    end\n\n    def write_keyword(keyword : Keyword)\n      check_keyword keyword\n      write keyword\n      next_token\n    end\n\n    def write_keyword(before : String, keyword : Keyword)\n      write before\n      write_keyword keyword\n    end\n\n    def write_keyword(keyword : Keyword, after : String, skip_space_or_newline = true)\n      write_keyword keyword\n      write after\n      skip_space_or_newline() if skip_space_or_newline\n    end\n\n    def write_keyword(before : String, keyword : Keyword, after : String)\n      passed_backslash_newline = @token.passed_backslash_newline\n      skip_space\n      if passed_backslash_newline && before == \" \"\n        # Nothing\n      else\n        write before\n      end\n      write_keyword keyword\n      write after\n      skip_space_or_newline\n    end\n\n    def write_token(type : Token::Kind)\n      check type\n      write type\n      next_token\n    end\n\n    def write_token(before : String, type : Token::Kind)\n      write before\n      write_token type\n    end\n\n    def write_token(type : Token::Kind, after : String)\n      write_token type\n      write after\n    end\n\n    def write_token(before : String, type : Token::Kind, after : String)\n      write before\n      write_token type\n      write after\n    end\n\n    def check_keyword(*keywords : Keyword)\n      raise \"expecting keyword #{keywords.join \" or \"}, not `#{@token.type}, #{@token.value}`, at #{@token.location}\" unless keywords.any? { |k| @token.keyword?(k) }\n    end\n\n    def check(*token_types : Token::Kind)\n      unless token_types.includes? @token.type\n        raise \"expecting #{token_types.join \" or \"}, not `#{@token.type}, #{@token.value}`, at #{@token.location}\"\n      end\n    end\n\n    def check_end\n      if @token.type.op_semicolon?\n        next_token_skip_space_or_newline\n      end\n      check_keyword :end\n    end\n\n    def last?(index, collection)\n      index == collection.size - 1\n    end\n\n    def inside_macro(&)\n      @inside_macro += 1\n      yield\n      @inside_macro -= 1\n    end\n\n    def outside_macro(&)\n      old_inside_macro = @inside_macro\n      @inside_macro = 0\n      yield\n      @inside_macro = old_inside_macro\n    end\n\n    def inside_macro?\n      @inside_macro != 0\n    end\n\n    def check_macro_whitespace\n      if @lexer.current_char == '\\\\' && @lexer.peek_next_char.ascii_whitespace?\n        @lexer.next_char\n        write \"\\\\\"\n        write @lexer.skip_macro_whitespace\n        @macro_state.whitespace = true\n        increment_line\n        true\n      else\n        false\n      end\n    end\n\n    def inside_cond(&)\n      @inside_cond += 1\n      yield\n      @inside_cond -= 1\n    end\n\n    def inside_call_or_assign(&)\n      @inside_call_or_assign += 1\n      yield\n      @inside_call_or_assign -= 1\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/git.cr",
    "content": "module Crystal::Git\n  class_property executable = \"git\"\n\n  # Tries to run git command with args.\n  # Yields block if exec fails or process status is not success.\n  def self.git_command(args, output : Process::Stdio = Process::Redirect::Close)\n    Process.run(executable, args, output: output).success?\n  rescue IO::Error\n    false\n  end\n\n  def self.git_capture(args)\n    String.build do |io|\n      git_command(args, output: io) || return\n    end\n  end\n\n  def self.git_config(key)\n    git_capture([\"config\", \"--get\", key]).try(&.strip).presence\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/implementations.cr",
    "content": "require \"../syntax/ast\"\nrequire \"../compiler\"\nrequire \"./typed_def_processor\"\nrequire \"json\"\n\nmodule Crystal\n  class ImplementationResult\n    include JSON::Serializable\n    property status : String\n    property message : String\n    property implementations : Array(ImplementationTrace)?\n\n    def initialize(@status, @message)\n    end\n\n    def to_text(io)\n      io.puts message\n      implementations.try do |arr|\n        arr.each do |impl|\n          io.puts \"#{impl.filename}:#{impl.line}:#{impl.column}\"\n          expanded = impl.expands\n          while expanded\n            io.puts \" ~> macro #{expanded.macro}: #{expanded.filename}:#{expanded.line}:#{expanded.column}\"\n            expanded = expanded.expands\n          end\n        end\n      end\n    end\n  end\n\n  # Contains information regarding where an implementation is defined.\n  # It keeps track of macro expansion in a human friendly way and\n  # pointing to the exact line an expansion and method definition occurs.\n  class ImplementationTrace\n    include JSON::Serializable\n\n    property line : Int32\n    property column : Int32\n    property filename : String\n    property macro : String?\n    property expands : ImplementationTrace?\n\n    def initialize(loc : Location)\n      f = loc.filename\n      if f.is_a?(String)\n        @line = loc.line_number\n        @column = loc.column_number\n        @filename = f\n      elsif f.is_a?(VirtualFile)\n        macro_location = f.macro.location.not_nil!\n        @macro = f.macro.name\n        @filename = macro_location.filename.to_s\n        @line = macro_location.line_number + loc.line_number\n        @column = loc.column_number\n      else\n        @line = loc.line_number\n        @column = loc.column_number\n        @filename = \"<unknown>\"\n      end\n    end\n\n    def self.parent(loc : Location)\n      f = loc.filename\n\n      if f.is_a?(VirtualFile)\n        f.expanded_location\n      else\n        nil\n      end\n    end\n\n    def self.build(loc : Location)\n      res = self.new(loc)\n      parent = self.parent(loc)\n\n      while parent\n        outer = self.new(parent)\n        parent = self.parent(parent)\n\n        outer.expands = res\n        res = outer\n      end\n\n      res\n    end\n  end\n\n  class ImplementationsVisitor < Visitor\n    include TypedDefProcessor\n\n    getter locations : Array(Location)\n\n    def initialize(@target_location : Location)\n      @locations = [] of Location\n    end\n\n    def process(result : Compiler::Result)\n      process_result result\n\n      result.node.accept(self)\n\n      if @locations.empty?\n        ImplementationResult.new(\"failed\", \"no implementations or method call found\")\n      else\n        res = ImplementationResult.new(\"ok\", \"#{@locations.size} implementation#{@locations.size > 1 ? \"s\" : \"\"} found\")\n        res.implementations = @locations.map { |loc| ImplementationTrace.build(loc) }\n        res\n      end\n    end\n\n    def visit(node : Call)\n      return contains_target(node) unless node.location && @target_location.between?(node.name_location, node.name_end_location)\n\n      if target_defs = node.target_defs\n        target_defs.each do |target_def|\n          @locations << (target_def.location || Location.new(nil, 0, 0))\n        end\n      end\n      false\n    end\n\n    def visit(node : Path)\n      return contains_target(node) unless (loc = node.location) && (end_loc = node.end_location) && @target_location.between?(loc, end_loc)\n\n      target = node.target_const || node.target_type\n      target.try &.locations.try &.each do |loc|\n        @locations << loc\n      end\n      false\n    end\n\n    def visit(node)\n      contains_target(node)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/editorconfig.ecr",
    "content": "root = true\n\n[*.cr]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/example.cr.ecr",
    "content": "# TODO: Write documentation for `<%= module_name %>`\nmodule <%= module_name %>\n  VERSION = \"0.1.0\"\n\n  # TODO: Put your code here\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/example_spec.cr.ecr",
    "content": "require \"./spec_helper\"\n\ndescribe <%= module_name %> do\n  # TODO: Write tests\n\n  it \"works\" do\n    false.should eq(true)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/gitignore.ecr",
    "content": "/docs/\n/lib/\n/bin/\n/.shards/\n*.dwarf\n<%- if config.skeleton_type == \"lib\" -%>\n\n# Libraries don't need dependency lock\n# Dependencies will be locked in applications that use them\n/shard.lock\n<%- end -%>\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/license.ecr",
    "content": "MIT License\n\nCopyright (c) <%= Time.local.year %> <%= config.author %> <<%= config.email %>>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/readme.md.ecr",
    "content": "# <%= config.name %>\n\nTODO: Write a description here\n\n## Installation\n\n<%- if config.skeleton_type == \"lib\" -%>\n1. Add the dependency to your `shard.yml`:\n\n   ```yaml\n   dependencies:\n     <%= config.name %>:\n       github: <%= config.github_repo %>\n   ```\n\n2. Run `shards install`\n<%- else -%>\nTODO: Write installation instructions here\n<%- end -%>\n\n## Usage\n\n<%- if config.skeleton_type == \"lib\" -%>\n```crystal\nrequire \"<%= config.name %>\"\n```\n\n<%- end -%>\nTODO: Write usage instructions here\n\n## Development\n\nTODO: Write development instructions here\n\n## Contributing\n\n1. Fork it (<https://github.com/<%= config.github_repo %>/fork>)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [<%= config.author %>](https://github.com/<%= config.github_name %>) - creator and maintainer\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/shard.yml.ecr",
    "content": "name: <%= config.name %>\nversion: 0.1.0\n\nauthors:\n  - <%= config.author %> <<%= config.email %>>\n\n<%- if config.skeleton_type == \"app\" -%>\ntargets:\n  <%= config.name %>:\n    main: src/<%= config.name %>.cr\n\n<%- end -%>\ncrystal: '>= <%= Crystal::Config.version %>'\n\nlicense: MIT\n"
  },
  {
    "path": "src/compiler/crystal/tools/init/template/spec_helper.cr.ecr",
    "content": "require \"spec\"\nrequire \"../src/<%= @config.name %>\"\n"
  },
  {
    "path": "src/compiler/crystal/tools/init.cr",
    "content": "# Implementation of the `crystal init` command\n\nrequire \"ecr/macros\"\nrequire \"option_parser\"\nrequire \"./git\"\n\nmodule Crystal\n  module Init\n    class Error < Crystal::Error\n      def self.new(message, opts : OptionParser)\n        new(\"#{message}\\n#{opts}\\n\")\n      end\n    end\n\n    class FilesConflictError < Error\n      getter conflicting_files : Array(String)\n\n      def initialize(@conflicting_files)\n        super(\"Some files would be overwritten: #{conflicting_files.join(\", \")}\")\n      end\n    end\n\n    def self.run(args)\n      config = parse_args(args)\n      InitProject.new(config).run\n    rescue ex : Init::FilesConflictError\n      STDERR.puts \"Cannot initialize Crystal project, the following files would be overwritten:\"\n      ex.conflicting_files.each do |path|\n        STDERR.puts \"   #{\"file\".colorize(:red)} #{path} #{\"already exist\".colorize(:red)}\"\n      end\n      STDERR.puts \"You can use --force to overwrite those files,\"\n      STDERR.puts \"or --skip-existing to skip existing files and generate the others.\"\n      exit 1\n    rescue ex : Init::Error\n      STDERR.puts \"Cannot initialize Crystal project: #{ex}\"\n      exit 1\n    end\n\n    def self.parse_args(args)\n      config = Config.new\n\n      OptionParser.parse(args) do |opts|\n        opts.banner = <<-USAGE\n          Usage: crystal init TYPE (DIR | NAME DIR)\n\n          Initializes a project folder as a git repository and default folder\n          structure for Crystal projects.\n\n          TYPE is one of:\n              lib                      Creates a library skeleton\n              app                      Creates an application skeleton\n\n          DIR  - directory where project will be generated\n          NAME - name of project to be generated, default: basename of DIR\n\n          USAGE\n\n        opts.on(\"-h\", \"--help\", \"show this help\") do\n          puts opts\n          exit\n        end\n\n        opts.on(\"-f\", \"--force\", \"force overwrite existing files\") do\n          config.force = true\n        end\n\n        opts.on(\"-s\", \"--skip-existing\", \"skip existing files\") do\n          config.skip_existing = true\n        end\n\n        opts.unknown_args do |args, after_dash|\n          config.skeleton_type = fetch_skeleton_type(opts, args)\n          dir = fetch_required_parameter(opts, args, \"DIR\")\n          if args.empty?\n            # crystal init TYPE DIR\n            config.dir = dir\n            config.name = config.expanded_dir.basename\n          else\n            # crystal init TYPE NAME DIR\n            config.name = dir\n            config.dir = args.shift\n          end\n        end\n      end\n\n      if config.force && config.skip_existing\n        raise Error.new \"Cannot use --force and --skip-existing together\"\n      end\n\n      validate_name(config.name)\n\n      config.author = fetch_author\n      config.email = fetch_email\n      config.github_name = fetch_github_name\n      config\n    end\n\n    def self.fetch_author\n      Crystal::Git.git_config(\"user.name\") || \"your-name-here\"\n    end\n\n    def self.fetch_email\n      Crystal::Git.git_config(\"user.email\") || \"your-email-here\"\n    end\n\n    def self.fetch_github_name\n      Crystal::Git.git_config(\"github.user\") || \"your-github-user\"\n    end\n\n    def self.fetch_skeleton_type(opts, args)\n      skeleton_type = fetch_required_parameter(opts, args, \"TYPE\")\n      unless skeleton_type.in?(\"lib\", \"app\")\n        raise Error.new \"Invalid TYPE value: #{skeleton_type}\", opts\n      end\n      skeleton_type\n    end\n\n    def self.fetch_required_parameter(opts, args, name)\n      if args.empty?\n        raise Error.new \"Argument #{name} is missing\", opts\n      end\n      args.shift\n    end\n\n    def self.validate_name(name)\n      case\n      when name.blank?                       then raise Error.new(\"NAME must not be empty\")\n      when name.size > 50                    then raise Error.new(\"NAME must not be longer than 50 characters\")\n      when name.each_char.any?(&.uppercase?) then raise Error.new(\"NAME should be all lower cased\")\n      when !name[0].ascii_letter?            then raise Error.new(\"NAME must start with a letter\")\n      when name.index(\"--\")                  then raise Error.new(\"NAME must not have consecutive dashes\")\n      when name.index(\"__\")                  then raise Error.new(\"NAME must not have consecutive underscores\")\n      when !name.each_char.all? { |c| c.alphanumeric? || c.in?('-', '_') }\n        raise Error.new(\"NAME must only contain alphanumerical characters, underscores or dashes\")\n      else\n        # name is valid\n      end\n    end\n\n    class Config\n      property skeleton_type : String\n      property name : String\n      property dir : String\n      property author : String\n      property email : String\n      property github_name : String\n      property silent : Bool\n      property force : Bool\n      property skip_existing : Bool\n\n      def initialize(\n        @skeleton_type = \"none\",\n        @name = \"none\",\n        @dir = \"none\",\n        @author = \"none\",\n        @email = \"none\",\n        @github_name = \"none\",\n        @silent = false,\n        @force = false,\n        @skip_existing = false,\n      )\n      end\n\n      getter expanded_dir : ::Path { ::Path.new(dir).expand(home: true) }\n\n      getter github_repo : String { \"#{github_name}/#{expanded_dir.basename}\" }\n    end\n\n    abstract class View\n      getter config : Config\n      getter full_path : ::Path\n\n      @@views = [] of View.class\n\n      def self.views\n        @@views\n      end\n\n      def self.register(view)\n        views << view\n      end\n\n      def initialize(@config)\n        @full_path = config.expanded_dir.join(path)\n      end\n\n      def overwriting?\n        File.exists?(full_path)\n      end\n\n      def render\n        overwriting = overwriting?\n\n        Dir.mkdir_p(full_path.dirname)\n        File.write(full_path, to_s)\n        puts log_message(overwriting) unless config.silent\n      end\n\n      def log_message(overwriting = false)\n        if overwriting\n          \" #{\"overwrite\".colorize(:light_green)}  #{full_path}\"\n        else\n          \"    #{\"create\".colorize(:light_green)}  #{full_path}\"\n        end\n      end\n\n      def module_name\n        View.module_name(config.name)\n      end\n\n      def self.module_name(name)\n        name\n          .gsub(/[-_]([^a-z])/i, \"\\\\1\")\n          .split('-')\n          .compact_map do |name|\n            name.camelcase if name[0]?.try(&.ascii_letter?)\n          end\n          .join(\"::\")\n      end\n\n      abstract def path\n    end\n\n    class InitProject\n      getter config : Config\n\n      def initialize(@config : Config)\n      end\n\n      def overwrite_checks(views)\n        existing_views, new_views = views.partition(&.overwriting?)\n\n        if existing_views.present? && !config.skip_existing\n          raise FilesConflictError.new existing_views.map(&.path)\n        end\n\n        new_views\n      end\n\n      def run\n        if (info = File.info?(config.expanded_dir)) && !info.directory?\n          raise Error.new \"#{config.dir.inspect} is a #{info.type.to_s.downcase}\"\n        end\n\n        views = self.views\n\n        unless config.force\n          views = overwrite_checks(views)\n        end\n\n        views.each &.render\n      end\n\n      private def views\n        View.views.map(&.new(config))\n      end\n    end\n\n    class GitInitView < View\n      def render\n        Crystal::Git.git_command([\"init\", config.dir], output: config.silent ? Process::Redirect::Close : STDOUT)\n      end\n\n      def path\n        \".git\"\n      end\n    end\n\n    TEMPLATE_DIR = \"#{__DIR__}/init/template\"\n\n    macro template(name, template_path, destination_path)\n      class {{name.id}} < View\n        ECR.def_to_s {{\"#{TEMPLATE_DIR.id}/#{template_path.id}\"}}\n\n        def path\n          {{destination_path}}\n        end\n      end\n\n      View.register({{name.id}})\n    end\n\n    template GitignoreView, \"gitignore.ecr\", \".gitignore\"\n    template EditorconfigView, \"editorconfig.ecr\", \".editorconfig\"\n    template LicenseView, \"license.ecr\", \"LICENSE\"\n    template ReadmeView, \"readme.md.ecr\", \"README.md\"\n    template ShardView, \"shard.yml.ecr\", \"shard.yml\"\n\n    template SrcExampleView, \"example.cr.ecr\", \"src/#{config.name}.cr\"\n\n    template SpecHelperView, \"spec_helper.cr.ecr\", \"spec/spec_helper.cr\"\n    template SpecExampleView, \"example_spec.cr.ecr\", \"spec/#{config.name}_spec.cr\"\n\n    View.register(GitInitView)\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/macro_code_coverage.cr",
    "content": "require \"../syntax/ast\"\nrequire \"../compiler\"\nrequire \"json\"\n\nmodule Crystal\n  class Command\n    private def macro_code_coverage\n      coverage_processor = MacroCoverageProcessor.new\n\n      config = create_compiler \"tool macro_code_coverage\", path_filter: true, no_codegen: true, allowed_formats: [\"codecov\"]\n      config.compiler.no_codegen = true\n\n      config.compile_configure_program do |program|\n        coverage_processor.configure program\n      end\n\n      coverage_processor.includes.concat config.includes.map { |path| ::Path[path].expand.to_posix.to_s }\n\n      coverage_processor.excludes.concat CrystalPath.default_paths.map { |path| ::Path[path].expand.to_posix.to_s }\n      coverage_processor.excludes.concat config.excludes.map { |path| ::Path[path].expand.to_posix.to_s }\n\n      coverage_processor.process\n    end\n  end\n\n  class MacroCoverageProcessor\n    private CURRENT_DIR = Dir.current\n\n    @hits = Hash(String, Hash(Int32, Int32 | String)).new { |hash, key| hash[key] = Hash(Int32, Int32 | String).new(0) }\n    @conditional_hit_cache = Hash(String, Hash(Int32, Set({ASTNode, Bool}))).new { |hash, key| hash[key] = Hash(Int32, Set({ASTNode, Bool})).new { |h, k| h[k] = Set({ASTNode, Bool}).new } }\n\n    @covered_macro_nodes = Array({ASTNode, Location, Bool}).new\n    @collected_covered_macro_nodes = Array(Array({ASTNode, Location, Bool})).new\n    getter coverage_interrupt_exception : ::Exception? = nil\n\n    # :nodoc:\n    def configure(program : Program) : Nil\n      program.interpreted_node_hook = ->interpreted_node_hook(ASTNode, Bool, Bool, Location?)\n      program.macro_expanded_hook = ->macro_expanded_hook\n      program.macro_expansion_error_hook = ->macro_expansion_error_hook(::Exception?)\n    end\n\n    protected def interpreted_node_hook(node : ASTNode, missed : Bool = false, use_significant_node : Bool = false, location custom_location : Location? = nil) : Nil\n      return unless location = (custom_location || node.location)\n\n      # If desired, try to find a more significant node to use for a more accurate location.\n      if use_significant_node\n        node = self.find_first_significant_node node\n        location = node.try(&.location) || location\n      end\n\n      unless location.filename.is_a? String\n        return node unless macro_location = location.macro_location\n\n        location = Location.new(\n          macro_location.filename,\n          location.line_number + macro_location.line_number,\n          location.column_number\n        )\n      end\n\n      @covered_macro_nodes << {node, location, missed}\n    end\n\n    protected def macro_expanded_hook : Nil\n      @collected_covered_macro_nodes << @covered_macro_nodes.dup\n      @covered_macro_nodes.clear\n    end\n\n    protected def macro_expansion_error_hook(exception : ::Exception?) : Nil\n      @coverage_interrupt_exception = exception unless exception.is_a?(SkipMacroException)\n    end\n\n    property includes = [] of String\n    property excludes = [] of String\n\n    def process : Nil\n      @hits.clear\n\n      self.compute_coverage\n\n      if err = @coverage_interrupt_exception\n        err.inspect_with_backtrace STDERR\n        STDERR.puts\n        STDERR.puts\n      end\n\n      self.write_output STDOUT\n\n      exit 1 if err\n    end\n\n    # See https://docs.codecov.com/docs/codecov-custom-coverage-format\n    private def write_output(io : IO) : Nil\n      JSON.build io, indent: \"  \" do |builder|\n        builder.object do\n          builder.string \"coverage\"\n          builder.object do\n            @hits.each do |filename, line_coverage|\n              builder.field filename do\n                builder.object do\n                  line_coverage.to_a.sort_by! { |(line, count)| line }.each do |line, count|\n                    builder.field line, count\n                  end\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n\n    # First filters the nodes to only those with locations we care about.\n    # The nodes are then chunked by line number, essentially grouping them.\n    # Each group is then processed to determine if that line is a hit or miss, but may also yield more than once, such as to mark an `If` conditional as a hit, but it's `else` block as a miss.\n    #\n    # The coverage information is stored in a similar way as the resulting output report: https://docs.codecov.com/docs/codecov-custom-coverage-format.\n    def compute_coverage\n      @collected_covered_macro_nodes\n        .select { |nodes| nodes.any? { |(_, location, _)| match_path? location.filename.as(String) } }\n        .each do |nodes|\n          nodes\n            .chunk { |(_, location, _)| location.line_number }\n            .each do |(line_number, nodes_by_line)|\n              self.process_line(line_number, nodes_by_line) do |(count, location, branches)|\n                next unless location.filename.is_a? String\n\n                location = self.normalize_location(location)\n\n                @hits[location.filename][location.line_number] = case existing_hits = @hits[location.filename][location.line_number]?\n                                                                 in String\n                                                                   hits, _, total = existing_hits.partition '/'\n\n                                                                   \"#{(hits.to_i + count).clamp(1, total.to_i)}/#{total}\"\n                                                                 in Int32 then existing_hits + count\n                                                                 in Nil\n                                                                   branches && count >= 1 ? \"#{count.clamp(1, branches)}/#{branches}\" : count\n                                                                 end\n              end\n            end\n        end\n\n      @hits\n    end\n\n    # These overloads try to find a more significant node to mark as missed.\n    # This ensures the missed value in the report maps to an actual node\n    # instead of just `{%` in the context of a multi-line `MacroExpression`,\n    # or just some whitespace as part of a `MacroLiteral`.\n\n    private def find_first_significant_node(node : MacroExpression) : ASTNode\n      self.find_first_significant_node node.exp\n    end\n\n    private def find_first_significant_node(node : Expressions) : ASTNode\n      if n = node.expressions.reject(MacroLiteral).reject(MultiAssign).first?\n        return self.find_first_significant_node n\n      end\n\n      node\n    end\n\n    private def find_first_significant_node(node : _) : ASTNode\n      node\n    end\n\n    private alias NodeTuple = {ASTNode, Location, Bool}\n\n    private def process_line(line : Int32, nodes : Array(NodeTuple), & : {Int32, Location, Int32?} ->) : Nil\n      # It's safe to use the first location since they were chunked by line.\n      first_node, location, _ = nodes.first\n\n      # Check for conditional hits first so that suffix conditionals are still treated as `1/2`.\n      if match = has_conditional_node?(nodes)\n        conditional_node, branches = match\n\n        # Keep track of what specific conditional branches were hit and missed as to ensure a proper partial count.\n        # In certain cases there may be more than 1 missed node, in which case we'll use the last non-missed node.\n        # Otherwise if there are none that are missed or only one, it's safe to just use the last node.\n        node, _, missed = case nodes.count { |(_, _, missed)| missed }\n                          when 0, 1\n                            nodes.last\n                          else\n                            nodes.reverse.find(nodes.last) { |_, _, is_missed| !is_missed }\n                          end\n\n        newly_hit = @conditional_hit_cache[location.filename][location.line_number].add?({node, missed})\n\n        hit_count = if newly_hit\n                      if conditional_node.is_a?(If | Unless) && (loc = conditional_node.location) && (end_loc = conditional_node.end_location) && loc.line_number == end_loc.line_number\n                        # Special case: Handle suffix `If` and `Unless` given there is no missed node in this context.\n                        1\n                      elsif nodes.all? { |(_, _, missed)| missed }\n                        # If all nodes on this line were missed, it's a miss\n                        0\n                      else\n                        # Otherwise, if no nodes were missed on this line, then all branches of this conditional were hit at once.\n                        nodes.none? { |(_, _, missed)| missed } ? branches : 1\n                      end\n                    else\n                      0\n                    end\n\n        yield({hit_count, location, branches})\n        return\n      end\n\n      # If no nodes on this line were missed, we can be assured it was a hit\n      if nodes.none? { |(_, _, missed)| missed }\n        yield({1, location, nil})\n        return\n      end\n\n      # For `MacroIf` nodes whose both then and else bodies consist only of a single `MacroLiteral`, the cond and then's body will both have the same start location.\n      # We need to handle the edge case of if the then body was missed by marking the cond as hit, and the `else` control line as hit (which is the end location of then's body.\n      # This will at least indicate the else block executed as we can't actually mark any line in either body as hit since they're non-macro code.\n      if first_node.is_a?(MacroIf) && (last_node = nodes.last?) && last_node[0].is_a?(MacroLiteral) && last_node[2]\n        yield({1, location, nil})\n\n        if end_loc = last_node[0].end_location\n          yield({1, end_loc, nil})\n        end\n        return\n      end\n\n      yield({0, location, nil})\n    end\n\n    private def has_conditional_node?(nodes : Array(NodeTuple)) : {ASTNode, Int32}?\n      nodes.each do |(node, _, _)|\n        if (n = node).is_a?(If | Unless | MacroIf | Or | And) && (branches = self.conditional_statement_branches(n)) > 1\n          return node, branches.not_nil!\n        end\n      end\n    end\n\n    # Returns how many unique values a conditional statement could return on a single line.\n    private def conditional_statement_branches(node : If | Unless | MacroIf | Or | And) : Int32\n      return 1 unless start_location = node.location\n      return 1 unless end_location = node.end_location\n      return 1 if end_location.line_number > start_location.line_number\n\n      self.count_branches node\n    end\n\n    # Workaround for a Crystal 1.0.0 compiler error\n    private def conditional_statement_branches(node : ASTNode) : Int32\n      1\n    end\n\n    private def count_branches(node : Or | And) : Int32\n      self.count_branches node.left, node.right\n    end\n\n    private def count_branches(node : MacroIf | If | Unless) : Int32\n      self.count_branches node.then, node.else\n    end\n\n    private def count_branches(left : ASTNode, right : ASTNode) : Int32\n      then_depth = case n = left\n                   when MacroIf, If, Unless, Or, And then self.count_branches n\n                   else\n                     1\n                   end\n\n      else_depth = case n = right\n                   when MacroIf, If, Unless, Or, And then self.count_branches n\n                   else\n                     1\n                   end\n\n      then_depth + else_depth\n    end\n\n    private def normalize_location(location : Location) : Location\n      Location.new(\n        ::Path[location.filename.as(String)].relative_to(CURRENT_DIR).to_s,\n        location.line_number,\n        location.column_number\n      )\n    end\n\n    private def match_path?(path)\n      paths = ::Path[path].parents << ::Path[path]\n\n      match_any_pattern?(includes, paths) || !match_any_pattern?(excludes, paths)\n    end\n\n    private def match_any_pattern?(patterns, paths)\n      patterns.any? { |pattern| paths.any? { |path| path == pattern || File.match?(pattern, path.to_posix) } }\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/agent.cr",
    "content": "require \"http\"\nrequire \"json\"\n\nclass Crystal::Playground::Agent\n  @send_runtime = true\n\n  def initialize(url, @tag : Int32)\n    @ws = HTTP::WebSocket.new(URI.parse(url))\n  end\n\n  def i(line, names = nil, &)\n    value = begin\n      yield\n    rescue ex\n      if @send_runtime\n        @send_runtime = false # send only the inner runtime exception\n        send \"runtime-exception\" do |json|\n          json.field \"line\", line\n          json.field \"exception\", ex.to_s\n        end\n      end\n      raise ex\n    end\n\n    send \"value\" do |json|\n      json.field \"line\", line\n      json.field \"value\", safe_to_value(value)\n      json.field \"html_value\", safe_to_html_value(value)\n      json.field \"value_type\", typeof(value).to_s\n\n      if names && value.is_a?(Tuple)\n        json.field \"data\" do\n          json.object do\n            value.to_a.zip(names) do |v, name|\n              json.field name, safe_to_value(v)\n            end\n          end\n        end\n      end\n    end\n\n    value\n  end\n\n  def safe_to_value(value)\n    to_value(value) rescue \"(error)\"\n  end\n\n  def safe_to_html_value(value)\n    to_html_value(value) rescue \"(error)\"\n  end\n\n  def to_value(value)\n    value.inspect\n  end\n\n  def to_html_value(value)\n    HTML.escape(value.pretty_inspect)\n  end\n\n  private def send(message_type, &)\n    message = JSON.build do |json|\n      json.object do\n        json.field \"tag\", @tag\n        json.field \"type\", message_type\n\n        yield json\n      end\n    end\n\n    @ws.send(message) rescue nil\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/agent_instrumentor_transformer.cr",
    "content": "module Crystal\n  class Playground::AgentInstrumentorTransformer < Transformer\n    class MacroDefNameCollector < Visitor\n      getter names\n\n      def initialize\n        @names = Set(String).new\n      end\n\n      def visit(node : Macro)\n        @names << node.name\n        false\n      end\n\n      def visit(node)\n        true\n      end\n    end\n\n    class FirstBlockVisitor < Visitor\n      def initialize(@instrumentor : AgentInstrumentorTransformer)\n      end\n\n      def visit(node : Call)\n        if node_block = node.block\n          @instrumentor.ignoring_line_of_node node do\n            node.block = node_block.transform(@instrumentor)\n          end\n        end\n        false\n      end\n\n      def visit(node)\n        true\n      end\n    end\n\n    class TypeBodyTransformer < Transformer\n      def initialize(@instrumentor : AgentInstrumentorTransformer)\n      end\n\n      def transform(node : Def)\n        @instrumentor.transform(node)\n      end\n    end\n\n    property ignore_line : Int32?\n\n    def initialize(@macro_names : Set(String))\n      @macro_names << \"record\"\n\n      @ignore_line = nil\n      @nested_block_visitor = FirstBlockVisitor.new(self)\n      @type_body_transformer = TypeBodyTransformer.new(self)\n    end\n\n    def self.transform(ast)\n      # collect names of declared macros in ast\n      # so the instrumentor can ignore call's of methods with this name\n      # this will avoid instrumenting calls to methods with the same name than\n      # declared macros in the playground source. For a more accurate solution\n      # a compilation should be done to distinguish whether each call refers to a macro or\n      # a method. Between the macro names collection and only instrumenting def's inside\n      # modules/classes the generated instrumentation is pretty good enough. See #2355\n      collector = MacroDefNameCollector.new\n      ast.accept collector\n\n      ast.transform self.new(collector.names)\n    end\n\n    private def instrument(node, add_as_typeof = false)\n      if (location = node.location) && location.line_number != ignore_line\n        splat = node.is_a?(Splat)\n        node = node.exp if node.is_a?(Splat)\n        @nested_block_visitor.not_nil!.accept(node)\n        args = [NumberLiteral.new(location.line_number)] of ASTNode\n        if node.is_a?(TupleLiteral)\n          args << ArrayLiteral.new(node.elements.map { |e| StringLiteral.new(e.to_s).as(ASTNode) })\n        end\n        call = Call.new(Call.new(\"_p\"), \"i\", args, Block.new([] of Var, node.as(ASTNode)).at(node))\n        call = Cast.new(call, TypeOf.new([node.clone] of ASTNode)) if add_as_typeof\n        call = Splat.new(call) if splat\n        call\n      else\n        node\n      end\n    end\n\n    def transform(node : Assign)\n      # constants are Path, avoid instrumenting those assignments\n      unless node.target.is_a?(Path)\n        node.value = instrument(node.value, add_as_typeof: !node.target.is_a?(Var))\n      end\n      node\n    end\n\n    def transform(node : MultiAssign)\n      node.values = if node.values.size == 1\n                      [instrument(node.values[0])] of ASTNode\n                    else\n                      rhs = TupleLiteral.new(node.values).at(node)\n                      rhs.location = node.location\n                      [instrument(rhs)] of ASTNode\n                    end\n      node\n    end\n\n    def transform(node : NilLiteral | NumberLiteral | StringLiteral | BoolLiteral | CharLiteral | SymbolLiteral | TupleLiteral | ArrayLiteral | HashLiteral | StringInterpolation | RegexLiteral | Var | InstanceVar | ClassVar | Global | TypeOf | UnaryExpression | BinaryOp | IsA | ReadInstanceVar)\n      instrument(node)\n    end\n\n    def transform(node : Call)\n      case {node.obj, node.name, node.args.size}\n      when {nil, \"raise\", 1}\n        instrument_arg node\n      when {nil, \"puts\", _}\n        instrument_if_args node\n      when {nil, \"print\", _}\n        instrument_if_args node\n      when {nil, _, _}\n        if @macro_names.includes?(node.name)\n          node\n        else\n          instrument(node)\n        end\n      else\n        instrument(node)\n      end\n    end\n\n    private def instrument_if_args(node : Call)\n      case node.args.size\n      when 0\n        node\n      when 1\n        instrument_arg node\n      else\n        instrument_args_and_splat node\n      end\n    end\n\n    private def instrument_arg(node : Call)\n      node.args[0] = instrument(node.args[0])\n      node\n    end\n\n    private def instrument_args_and_splat(node : Call)\n      args = TupleLiteral.new(node.args).at(node)\n      args.location = node.location\n      node.args = [Splat.new(instrument(args))] of ASTNode\n      node\n    end\n\n    def transform(node : Yield)\n      node.exps[0] = instrument(node.exps[0]) if node.exps.size == 1\n      node\n    end\n\n    def transform(node : If | Unless)\n      node.then = node.then.transform(self)\n      node.else = node.else.transform(self)\n      node\n    end\n\n    def transform(node : Case)\n      node.whens.each do |w|\n        w.body = w.body.transform(self)\n      end\n      if e = node.else\n        node.else = e.transform(self)\n      end\n      node\n    end\n\n    def transform(node : While)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : Return)\n      if exp = node.exp\n        node.exp = instrument(exp)\n      end\n      node\n    end\n\n    def transform(node : Def)\n      ignoring_line_of_node node do\n        node.body = node.body.transform(self)\n        node\n      end\n    end\n\n    def transform(node : ClassDef | ModuleDef)\n      node.body = @type_body_transformer.not_nil!.transform(node.body)\n      node\n    end\n\n    def transform(node : Expressions)\n      node.expressions = node.expressions.map(&.transform(self).as(ASTNode)).to_a\n      node\n    end\n\n    def transform(node : Block)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node : ExceptionHandler)\n      node.body = node.body.transform(self)\n      node.rescues = transform_many(node.rescues)\n      if node_else = node.else\n        node.else = node_else.transform(self)\n      end\n      if node_ensure = node.ensure\n        node.ensure = node_ensure.transform(self)\n      end\n      node\n    end\n\n    def transform(node : Rescue)\n      node.body = node.body.transform(self)\n      node\n    end\n\n    def transform(node)\n      node\n    end\n\n    def ignoring_line_of_node(node, &)\n      old_ignore_line = @ignore_line\n      @ignore_line = node.location.try(&.line_number)\n      res = yield\n      @ignore_line = old_ignore_line\n      res\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/application.css",
    "content": "body {\n  display: flex;\n  min-height: 100vh;\n  flex-direction: column;\n}\n\nmain {\n  flex: 1 0 auto;\n}\n\nnav ul a {\n  color: #616161;\n}\n\nfooter > div > div {\n  margin-bottom: 4px;\n}\n\nfooter small {\n  display: block;\n  margin-bottom: 1.5em;\n}\n\nhtml {\n  font-family: Helvetica Neue, Helvetica, Arial, sans-serif;\n  font-weight: 400;\n}\n\nh1 {\n  margin-top: 3.14rem;\n}\n\nh1 {\n  font-size: 2.5rem;\n}\n\nh2 {\n  font-size: 2.0rem;\n}\n\nh3 {\n  font-size: 1.8rem;\n}\n\nh4 {\n  font-size: 1.5rem;\n}\n\n.brand-logo img {\n  position: absolute;\n  top: 7px;\n  left: 7px;\n  transform: scale(0.66);\n  height: 1.7em;\n}\n\n@media only screen and (max-width: 992px) {\n  nav .brand-logo {\n    left: 7px;\n    transform: none;\n  }\n}\n\n@media only screen and (min-width: 600px) {\n  #toast-container {\n    top: initial;\n    bottom: 45px;\n    right: 90px;\n  }\n}\n\n.row-narrow {\n  margin-bottom: 0;\n}\n\n.editor, .sidebar, .output, .inspect-values, .CodeMirror {\n  font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;\n  font-size: 14px;\n  line-height: 1.46em;\n}\n\n\n#mainEditorContainer .editor, #mainEditorContainer .sidebar {\n  min-height: 75vh;\n}\n\n@media (max-height: 780px) {\n  #mainEditorContainer .editor, #mainEditorContainer .sidebar {\n    min-height: 70vh;\n  }\n}\n\n@media (max-height: 540px) {\n  #mainEditorContainer .editor, #mainEditorContainer .sidebar {\n    min-height: 65vh;\n  }\n}\n\n.editor-wrapper, .sidebar-wrapper {\n  position: relative;\n}\n\n.CodeMirror {\n  height: auto;\n}\n\n.CodeMirror-lines {\n  padding: 0.5em 0;\n}\n\n.CodeMirror-gutters.phantom {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 29px;\n}\n\n.CodeMirror .inspector-hover {\n  background-color: #eee;\n}\n\n.card-plain {\n  background: transparent;\n  box-shadow: none;\n  border: 1px solid #ccc;\n  padding-left: 1ch;\n}\n\n.sidebar {\n  position: relative;\n}\n\n.sidebar div {\n  position: absolute;\n  white-space: pre;\n  left: 0;\n  right: 0;\n}\n\n.sidebar .type {\n  color: #64b5f6;\n  font-weight: 400;\n}\n\n.sidebar .type::before {\n  content: ' : ';\n}\n\n.editor-error {\n  cursor: pointer;\n}\n\n.editor-error-col {\n  position: absolute;\n  width: 1em;\n  height: 1em;\n  transform: rotate(45deg);\n  transform-origin: 65% -21%;\n  z-index: -1;\n}\n\n.editor-error-msg {\n  margin-top: 0.5em !important;\n  padding: 0 4px;\n  z-index: 2;\n}\n\n.inspect-table th, .inspect-table td {\n  padding: 8px 5px\n}\n\n.inspect-table th {\n  font-weight: 400;\n}\n\n.code-btn {\n  font-size: 11px;\n  padding:1px 12px 0;\n  height: 26px;\n  line-height: 26px;\n}\n.code-btn .icon { margin-right: 3px; }\n.code-btn .icon :before { font-size:14px;}\n\n.run-button-preloader {\n  position: absolute;\n  top: 11px;\n  left: -5px;\n}\n\n.demoButtonsContainer {\n  position: relative;\n  width: 55.5px;\n  margin: 0 auto;\n}\n\n.demoButtonsContainer .run-button-preloader {\n  top: -4px;\n}\n\n.f-16 {\n  width: 16px; /* avoid horizontal flicking in the toolbar */\n}\n\nmain ul {\n  padding: 0 2rem;\n}\n\nmain ul li {\n  list-style-type: disc;\n}\n\nul.unstyled {\n  padding: 0;\n}\n\nul.unstyled li {\n  list-style-type: none;\n}\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/application.js",
    "content": "$(function(){\n  $('.modal-trigger').leanModal();\n\n  $(\".crystal-version\").text(Environment.version);\n\n  if (Playground.hasStorage == false) {\n    Materialize.toast(\"LocalStorage not available\", 4000);\n  }\n});\n\n// main page initialization\n$(function(){\n  if ($('#mainEditorContainer').length == 0)\n    return;\n\n  // session is first so the user can navigates back and forth from pages.\n  // new tabs will load the environment code.\n  var code = (Playground.hasStorage ? sessionStorage.lastCode : null) ||\n              Environment.source ||\n             (Playground.hasStorage ? localStorage.lastCode : null) ||\n              Environment.defaultSource;\n\n  var session = new Playground.Session({\n    container: $('#mainEditorContainer'),\n    stdout: $('#mainOutput'),\n    outputIndicator: $('#mainOutputIndicator'),\n    source: code,\n    autofocus: true,\n  });\n  var buttons = new Playground.RunButtons({\n    container: $('#mainButtonsContainer')\n  });\n  // honor run debounce in initial autorun\n  var autorun = isFinite(Playground.settings.getRunDebounce());\n  session.bindRunButtons(buttons, {autorun: autorun});\n  session.connect();\n\n  function saveAsLastCode() {\n    if(typeof(Storage) !== \"undefined\") {\n      localStorage.lastCode = sessionStorage.lastCode = session.getSource();\n    }\n  }\n  session.onChange = function() {\n    saveAsLastCode();\n  };\n  $(window).on(\"unload\", function(){\n    saveAsLastCode();\n  });\n\n  $(\"#saveAsFile\").click(function(e) {\n    var uri = \"data:text/plain;charset=utf-8,\" + encodeURIComponent(session.getSource());\n\n    var link = $(\"<a>\");\n    $(\"body\").append(link);\n    link.attr('download', 'play.cr');\n    link.attr('href', uri);\n    link[0].click();\n    link.remove();\n\n    e.preventDefault();\n  });\n\n  $(\"#saveAsGist\").click(function(e) {\n    if (Playground.settings.getGithubToken() == '') {\n      window.open('/settings');\n      return;\n    }\n\n    $.ajax({\n      type:\"POST\",\n      beforeSend: function (request) {\n        request.setRequestHeader(\"Authorization\", \"token \" + Playground.settings.getGithubToken());\n      },\n      url: \"https://api.github.com/gists\",\n      data: JSON.stringify({\n        \"public\": true,\n        \"files\": {\"play.cr\": {\"content\": session.getSource() }}\n      }),\n      success: function(msg) {\n        new ModalDialog().append(\n          $(\"<p>\")\n            .append(\"There is a new gist at \")\n            .append($(\"<a>\")\n              .attr(\"href\", msg.html_url)\n              .attr(\"target\", \"_blank\")\n              .append($(\"<span>\").text(msg.html_url))\n              .append(` <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#link-external-16\"></use></svg>`)\n            )).openModal();\n      }\n    });\n    Materialize.toast('Uploading gist', 4000);\n\n    e.preventDefault();\n  });\n\n  // load file by drag and drop\n  var doc = document.documentElement;\n  doc.ondragover = function () { return false; };\n  doc.ondragend = function () { return false; };\n  doc.ondrop = function (event) {\n    event.preventDefault && event.preventDefault();\n    var files = event.dataTransfer.files;\n    if (files.length > 0) {\n      var reader = new FileReader();\n      reader.onload = function (event) {\n        session.setSource(reader.result);\n      };\n      reader.readAsText(files[0]);\n    }\n    return false;\n  };\n\n  $(window).resize(session._matchEditorSidebarHeight());\n});\n\n\n// about page initialization\nfunction initDemoPlayground(dom) {\n  var editorContainer, output, outputIndicator, buttonsContainer;\n  // markdown renders <pre><code>... and in html is <pre>...\n  // extract code and leave dom been <pre> dom.\n  var code = dom.text();\n  dom = dom.closest(\"pre\");\n  //\n  dom.after(editorContainer = $(\"<div>\").addClass(\"row row-narrow\"));\n  editorContainer.after(\n    $(\"<div>\").addClass(\"row\").append(\n      $(\"<div>\").addClass(\"col s7\").append(\n        $(\"<div>\").addClass(\"card card-plain\").append(\n          $(\"<pre>\").addClass(\"ansi-base16-railscasts-bright\").append(\n            output = $(\"<code>\").addClass(\"output\").css(\"min-height\", \"1.5em\")\n          )))\n      ).append(\n        `<div class=\"col s1\"><svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#terminal-16\"></use></svg></div>`\n      ).append(\n      outputIndicator = $(\"<div>\")\n      ).append(\n        $(\"<div>\").addClass(\"col s4\").append(buttonsContainer = $(\"<div>\").addClass(\"demoButtonsContainer\"))\n      ));\n\n  var session = new Playground.Session({\n    container: editorContainer,\n    stdout: output,\n    outputIndicator: outputIndicator,\n    source: dom.text()\n  });\n  var buttons = new Playground.RunButtons({\n    container: buttonsContainer\n  });\n  session.bindRunButtons(buttons, {autorun: dom.hasClass(\"autorun\")});\n  dom.remove();\n  session.connect();\n}\n\n$(function(){\n  // .language-playground is for markdown ```playground\n  $(\".playground, .language-playground\").each(function(){\n    initDemoPlayground($(this));\n  });\n});\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/session.js",
    "content": "CodeMirror.keyMap.macDefault[\"Cmd-/\"] = \"toggleComment\";\nCodeMirror.keyMap.pcDefault[\"Ctrl-/\"] = \"toggleComment\";\n\nCodeMirror.keyMap.macDefault[\"Cmd-Enter\"] = \"runCode\";\nCodeMirror.keyMap.macDefault[\"Cmd-S\"] = \"runCode\";\nCodeMirror.keyMap.pcDefault[\"Ctrl-Enter\"] = \"runCode\";\nCodeMirror.keyMap.pcDefault[\"Ctrl-S\"] = \"runCode\";\n\nCodeMirror.commands.runCode = function(editor) {\n  if (editor._playgroundSession) {\n    editor._playgroundSession.run();\n  }\n};\n\nfunction ModalDialog(options) {\n  options = $.extend({}, {destroyOnClose: true}, options);\n\n  $(\"body\").append(\n    this.modalDom = $(\"<div>\").addClass(\"modal modal-fixed-footer\")\n      .append(this.modalContentDom = $(\"<div>\").addClass(\"modal-content\"))\n      .append($(\"<div>\").addClass(\"modal-footer\")\n        .append($(\"<a>\").text(\"Close\")\n          .addClass(\"modal-action modal-close waves-effect waves-green btn-flat\")\n          .attr(\"href\", \"javascript:\"))));\n\n  this.onClose = function() { };\n\n  this.openModal = function() {\n    this.modalDom.openModal({\n      complete: function() {\n        this.onClose();\n        if (options.destroyOnClose) {\n          this.destroy();\n        }\n      }.bind(this)\n    });\n    return this;\n  }.bind(this);\n\n  this.append = function() {\n    for(var i = 0; i < arguments.length; i++) {\n      this.modalContentDom.append(arguments[i]);\n    }\n    return this;\n  }.bind(this);\n\n  this.destroy = function() {\n    this.modalDom.remove();\n  }.bind(this);\n\n  return this;\n}\n\nfunction cdiv(cssClass) {\n  return $(\"<div>\").addClass(cssClass);\n}\n\nPlayground.RunButtons = function(options) {\n  var buildAnchor = function(tooltip, octicon) {\n    return $(\"<a>\").addClass(\"run-button btn-floating btn-large waves-effect waves-light tooltipped\")\n      .attr(\"href\", \"#\")\n      .attr(\"data-position\", \"left\").attr(\"data-delay\", \"50\").attr(\"data-tooltip\", tooltip)\n      .append(`<svg viewBox=\"0 0 16 16\" class=\"mega-octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#${octicon}-16\"></use></svg>`);\n  }\n\n  var buildProgress = function() {\n    var buildSpinner = function(color) {\n      return cdiv(\"spinner-layer spinner-\" + color)\n        .append(cdiv(\"circle-clipper left\").append(cdiv(\"circle\")))\n        .append(cdiv(\"gap-patch\").append(cdiv(\"circle\")))\n        .append(cdiv(\"circle-clipper right\").append(cdiv(\"circle\")))\n    };\n\n    return cdiv(\"preloader-wrapper big active run-button-preloader\")\n      .append(buildSpinner(\"blue\"))\n      .append(buildSpinner(\"red\"))\n      .append(buildSpinner(\"yellow\"))\n      .append(buildSpinner(\"green\"));\n  }\n\n  var mac = /Mac/.test(navigator.platform);\n\n  options.container\n    .prepend(this.stopButton = buildAnchor(\"Stops code\", \"square-fill\"))\n    .prepend(this.playButton = buildAnchor(mac ? \"⌘ + Enter\" : \"Ctrl + Enter\", \"triangle-right\"))\n    .prepend(this.progress = buildProgress());\n\n  this.stopButton.hide().tooltip();\n  this.playButton.hide().tooltip();\n  this.progress.hide();\n\n  this.showPlay = function() {\n    this.playButton.removeClass(\"disabled\");\n    this.playButton.show();\n    this.stopButton.hide();\n    this.progress.hide();\n  }.bind(this);\n\n  this.showStop = function() {\n    this.playButton.removeClass(\"disabled\");\n    this.playButton.hide();\n    this.stopButton.show();\n    this.progress.show();\n  }.bind(this);\n\n  this.showPlayDisabled = function() {\n    this.playButton.addClass(\"disabled\");\n    this.playButton.show();\n    this.stopButton.hide();\n    this.progress.hide();\n  }.bind(this);\n\n  this.onPlay = function() { }\n  this.onStop = function() { }\n\n  this.playButton.click(function(e) {\n    e.preventDefault();\n    this.onPlay();\n  }.bind(this));\n\n  this.stopButton.click(function(e) {\n    e.preventDefault();\n    this.onStop();\n  }.bind(this));\n\n  return this;\n}\n\nPlayground.OutputIndicator = function(dom) {\n  this.dom = dom;\n  this.blinkTimeout = null;\n\n  this.turnOnWithBlink = function () {\n    this.dom.removeClass('teal-text red-text');\n    this.blinkTimeout = window.setTimeout(function(){\n      if (this.isError) return;\n      this.dom.addClass('teal-text');\n    }.bind(this), 200);\n  }.bind(this);\n\n  this.turnOff = function () {\n    this.isError = false;\n    this._cancelBlink();\n    this.dom.removeClass('teal-text red-text');\n  }.bind(this);\n\n  this.turnError = function () {\n    this.isError = true;\n    this._cancelBlink();\n    this.dom.addClass('red-text').removeClass('teal-text grey-text');\n  }.bind(this);\n\n  this._cancelBlink = function() {\n    if (this.blinkTimeout != null) {\n      clearTimeout(this.blinkTimeout);\n    }\n  }.bind(this);\n\n  this.turnOff();\n\n  return this;\n}\n\nPlayground.Inspector = function(session, line) {\n  this.lineDom = $(\"<div>\")\n      .addClass(\"truncate\")\n      .css(\"top\", session.editor.heightAtLine(line-1, \"local\") + \"px\")\n      .css(\"cursor\", \"pointer\");\n  session.sidebarDom.append(this.lineDom);\n\n  var INSPECTOR_HOVER_CLASS = \"inspector-hover\";\n  this.lineDom.hover(function(){\n    session.editor.addLineClass(line-1, \"wrap\", INSPECTOR_HOVER_CLASS);\n  }.bind(this), function(){\n    session.editor.removeLineClass(line-1, \"wrap\", INSPECTOR_HOVER_CLASS);\n  }.bind(this));\n\n  this.messages = [];\n  this.value_type = null; // null if mismatch. keep value if always the same.\n\n  this.modal = null;\n\n  var labels;\n  var tableBody = null;\n  var appendMessageToTableBody = function(i, message) {\n    var row = $(\"<tr>\");\n    row.append($(\"<td>\").text(i+1));\n\n    for(var j = 0; j < labels.length; j++) {\n      row.append($(\"<td>\").text(message.data[labels[j]]));\n    }\n\n    row.append($(\"<td>\").html(\"<pre><code>\" + message.html_value + \"</code></pre>\"));\n    row.append($(\"<td>\").text(message.value_type));\n    tableBody.append(row);\n  }\n\n  this.lineDom.click(function() {\n    var inspectModalTable = $(\"<table>\").addClass(\"inspect-table highlight\")\n\n    labels = this.dataLabels();\n    var tableHeaderRow = $(\"<tr>\")\n    inspectModalTable.append($(\"<thead>\").append(tableHeaderRow));\n    tableHeaderRow.append($(\"<th>\").text(\"#\"));\n    for(var j = 0; j < labels.length; j++) {\n      tableHeaderRow.append($(\"<th>\").text(labels[j]));\n    }\n    tableHeaderRow.append($(\"<th>\").text(\"Value\"));\n    tableHeaderRow.append($(\"<th>\").text(\"Type\"));\n\n    tableBody = $(\"<tbody>\");\n    inspectModalTable.append(tableBody);\n\n    for(var i = 0; i < this.messages.length; i++) {\n      appendMessageToTableBody(i, this.messages[i]);\n    }\n\n    this.modal = new ModalDialog().append(inspectModalTable).openModal();\n    this.modal.onClose = function() {\n      this.modal = null;\n    }.bind(this);\n  }.bind(this));\n\n  this.addMessage = function(message) {\n    this.messages.push(message);\n    if (this.messages.length == 1) {\n      this.lineDom.text(message.value);\n      this.value_type = message.value_type;\n    } else {\n      this.lineDom.text(\"(\" + this.messages.length + \" times)\");\n      if (this.value_type != message.value_type) {\n        this.value_type = null;\n      }\n    }\n    if (this.value_type != null && Playground.settings.getShowTypes()) {\n      this.lineDom.append($(\"<span>\").addClass(\"type\").text(this.value_type));\n    }\n\n    if (this.modal != null) {\n      appendMessageToTableBody(this.messages.length-1, message);\n    }\n  }.bind(this);\n\n  this.dataLabels = function() {\n    // collect all data labels, in order of appearance\n    var res = []\n    var resSet = {}\n    for(var i = 0; i < this.messages.length; i++) {\n      var message = this.messages[i];\n      if (message.data) {\n        for(var k in message.data) {\n          if (resSet[k] != true) {\n            resSet[k] = true;\n            res.push(k);\n          }\n        }\n      }\n    }\n\n    return res;\n  }.bind(this);\n\n  return this;\n}\n\nPlayground.Session = function(options) {\n  options = $.extend({}, {autofocus: false, source: ''}, options);\n\n  // render components\n  options.container.append(\n    cdiv(\"col s7\").append(\n      this.editorWrapper = cdiv(\"card editor-wrapper\")\n        .append(cdiv(\"CodeMirror-gutters phantom\"))\n        .append(this.editorDom = cdiv(\"editor\"))\n      )\n  ).append(\n    cdiv(\"col s5\").append(\n      this.sidebarWrapper = cdiv(\"card card-plain sidebar-wrapper\")\n        .append(this.sidebarDom = cdiv(\"sidebar\"))\n      )\n  );\n  this.isRunning = false;\n  this.stdout = options.stdout;\n  this.stdoutRawContent = \"\";\n  this.outputIndicator = new Playground.OutputIndicator(options.outputIndicator);\n\n  this.editor = CodeMirror(this.editorDom[0], {\n    mode: 'crystal',\n    theme: 'neat',\n    lineNumbers: true,\n    autofocus: options.autofocus,\n    tabSize: 2,\n    viewportMargin: Infinity,\n    dragDrop: false, // dragDrop functionality is implemented to capture drop anywhere and replace source\n    value: options.source,\n    // indent using spaces, see https://github.com/codemirror/codemirror5/issues/988\n    extraKeys: {\n      Tab: (cm) => {\n        if (cm.getMode().name === 'null') {\n          cm.execCommand('insertTab');\n        } else {\n          if (cm.somethingSelected()) {\n            cm.execCommand('indentMore');\n          } else {\n            cm.execCommand('insertSoftTab');\n          }\n        }\n      },\n      'Shift-Tab': (cm) => cm.execCommand('indentLess')\n    }\n  });\n  this.editor._playgroundSession = this;\n\n  this.connect = function() {\n    var socketProtocol = location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n    this.ws = new WebSocket(socketProtocol + \"//\" + location.host + \"/client\");\n\n    this.ws.onopen = function() {\n      this._triggerReady();\n    }.bind(this);\n\n    this.ws.onclose = function() {\n      if (Playground.connectLostShown !== true) {\n        Playground.connectLostShown = true;\n        Materialize.toast('Connection lost. Refresh.');\n      }\n      this._triggerDisconnect();\n    }.bind(this);\n\n    this.ws.onmessage = function(e) {\n      var message = JSON.parse(e.data);\n      if (message.tag != this.runTag) return; // discarding message form old execution\n\n      switch (message.type) {\n        case \"run\":\n          break;\n        case \"output\":\n          this._appendStdout(message.content);\n          this.outputIndicator.turnOnWithBlink();\n          break;\n        case \"value\":\n          this._getInspector(message.line).addMessage(message);\n          break;\n        case \"runtime-exception\":\n          this._fullError = message.exception;\n          var column =  this.editor.getLine(message.line-1).match(/\\s*\\S/)[0].length;\n          this._showEditorError(message.line, column, message.exception, 0);\n          break;\n        case \"exit\":\n          this._triggerFinish();\n\n          if (message.status != 0) {\n            this._appendStdout(\"\\nexit status: \" + message.status);\n            this.outputIndicator.turnError();\n          }\n          break;\n        case \"exception\":\n          this._triggerFinish();\n\n          var last_line = this.editor.lastLine() + 1;\n          this._fullError = message.exception.message;\n          if (message.exception.payload) {\n            for (var i = 0; i < message.exception.payload.length; i++) {\n              var ex = message.exception.payload[i];\n              if (ex.file == \"play\" || ex.file == \"\") {\n                // if there is an issue with the reported line,\n                // let's make sure the error is displayed\n                var ex_line = Math.min(ex.line, last_line);\n                this._showEditorError(ex_line, ex.column, ex.message, i);\n              }\n            }\n          } else {\n            this._showEditorError(last_line, 1, \"Compiler error\", 0);\n          }\n          break;\n        case \"bug\":\n          this._triggerFinish();\n\n          new ModalDialog().append(\n            $(\"<h1>\").append(\"Bug\"),\n            $(\"<p>\")\n              .append(\"You've reached a bug in the playground. Please \")\n              .append($(\"<a>\")\n                .text(\"let us know\")\n                .attr(\"href\", \"https://github.com/crystal-lang/crystal/issues/new\")\n                .attr(\"target\", \"_blank\"))\n              .append(\" about it.\"),\n            $(\"<h2>\").append(\"Code\"),\n            $(\"<pre>\").append(this.editor.getValue()),\n            $(\"<h2>\").append(\"Exception\"),\n            $(\"<pre>\").append(message.exception.message)).openModal();\n\n          break;\n        case \"format\":\n          codeFormatter.stop();\n          this.setSource(message.value);\n          break;\n        default:\n          console.error(\"ws message not handled\", message);\n      }\n    }.bind(this);\n  }.bind(this);\n\n  this.runTag = 0;\n\n  this.format = function() {\n    this._removeScheduledRun();\n\n    this._clearInspectors();\n    this._hideEditorErrors();\n    this._clearStdout();\n\n    this.ws.send(JSON.stringify({\n      type: \"format\",\n      source: this.editor.getValue(),\n      tag: this.runTag\n    }));\n  }.bind(this);\n\n  this.run = function() {\n    if (Playground.connectLostShown) return;\n    if (this.isRunning) return;\n\n    this.isRunning = true;\n    this._removeScheduledRun();\n    this.runTag++;\n\n    this._clearInspectors();\n    this._hideEditorErrors();\n    this._clearStdout();\n\n    this.ws.send(JSON.stringify({\n      type: \"run\",\n      source: this.editor.getValue(),\n      tag: this.runTag\n    }));\n\n    this._triggerRun();\n  }.bind(this);\n\n  this.stop = function() {\n    this.ws.send(JSON.stringify({\n      type: \"stop\",\n      tag: this.runTag\n    }));\n  }.bind(this);\n\n  this.getSource = function() {\n    return this.editor.getValue();\n  }.bind(this);\n\n  this.setSource = function(value) {\n    this.editor.setValue(value);\n  }.bind(this);\n\n  this.bindRunButtons = function(runButtons, options) {\n    options = $.extend({}, {autorun: false}, options);\n    runButtons.showPlayDisabled();\n\n    this.onReady = function() {\n      runButtons.showPlay();\n      if (options.autorun) {\n        this.run();\n      }\n    }.bind(this);\n\n    this.onRun = function() {\n      runButtons.showStop();\n    }.bind(this);\n\n    this.onFinish = function() {\n      this.isRunning = false;\n      runButtons.showPlay();\n      if (codeFormatter && codeFormatter.isRunning) {\n        codeFormatter.stop();\n      }\n    }.bind(this);\n\n    this.onDisconnect = function() {\n      runButtons.showPlayDisabled();\n    }.bind(this);\n\n    runButtons.onPlay = function() {\n      this.run();\n    }.bind(this);\n\n    runButtons.onStop = function() {\n      this.stop();\n    }.bind(this);\n  }.bind(this);\n\n  this.onReady = function() { };\n  this.onRun = function() { };\n  this.onFinish = function() { };\n  this.onDisconnect = function() { };\n  this.onChange = function() { };\n\n  this._triggerReady = function() { this.onReady(this); }.bind(this);\n  this._triggerRun = function() { this.onRun(this); }.bind(this);\n  this._triggerFinish = function() { this.onFinish(this); }.bind(this);\n  this._triggerDisconnect = function() { this.onDisconnect(this); }.bind(this);\n  this._triggerChange = function() { this.onChange(this); }.bind(this);\n\n  // schedule run\n  this._runTimeout = null;\n\n  this._removeScheduledRun = function() {\n    if (this._runTimeout == null) return;\n    clearTimeout(this._runTimeout);\n  }.bind(this);\n\n  this._scheduleRun = function() {\n    this._removeScheduledRun();\n    if (isFinite(Playground.settings.getRunDebounce())) {\n      this._runTimeout = window.setTimeout(function(){\n        this.run();\n      }.bind(this), Playground.settings.getRunDebounce());\n    }\n  }.bind(this);\n  //\n\n  // editor errors\n  var renderConsoleText = function(dom, text) {\n    var lines = text.match(/[^\\r\\n]+/g);\n\n    for(var i = 0; i < lines.length; i++) {\n      if (i > 0) {\n        dom.append(\"<br>\");\n      }\n      var str = lines[i];\n      var firstNonWhite = 0;\n      while (str[firstNonWhite] == ' ') {\n        firstNonWhite++;\n      }\n      var rendered = \"\\u00a0\".repeat(firstNonWhite) + str.substring(firstNonWhite);\n      dom.append(document.createTextNode(rendered));\n    }\n\n    return dom;\n  };\n\n  this.currentErrors = [];\n\n  this._hideEditorErrors = function() {\n    this._fullError = null;\n    for(var i = 0; i < this.currentErrors.length; i++) {\n      this.currentErrors[i].clear();\n    }\n    this.currentErrors.splice(0,this.currentErrors.length);\n    this._matchEditorSidebarHeight();\n  }.bind(this);\n\n  this._showEditorError = function(line, column, message, color) {\n    var msg;\n    var colorClass = \"red\" + (color > 0 ? \" darken-\" + Math.min(color, 4) : \"\");\n    var cursor = $(\"<div>\").addClass(colorClass + \" editor-error-col\");\n    var dom = $(\"<div>\").addClass(\"editor-error\")\n      .append(cursor)\n      .append(renderConsoleText($(\"<div>\"), message)\n        .addClass(colorClass + \" editor-error-msg white-text\"));\n\n    this.currentErrors.push(this.editor.addLineWidget(line-1, dom[0]));\n    cursor.css('left', column + 'ch');\n    this._matchEditorSidebarHeight();\n    dom.click(function(e) {\n      this._showFullError();\n      e.stopPropagation();\n    }.bind(this));\n  }.bind(this);\n\n  this._showFullError = function() {\n    new ModalDialog().append(\n      $(\"<h1>\").append(\"Error\"),\n      $(\"<pre>\").css(\"min-height\", \"70%\").text(this._fullError))\n      .openModal();\n  }.bind(this);\n  //\n\n  // inspectors\n  this.inspectors = {};\n  this._clearInspectors = function() {\n    this.sidebarDom.empty();\n    this.inspectors = {};\n  }.bind(this);\n\n  this._getInspector = function(line) {\n    var res = this.inspectors[line];\n    if (!res) {\n      res = new Playground.Inspector(this, line);\n      this.inspectors[line] = res;\n    }\n    return res;\n  }.bind(this);\n  //\n\n  //stdout\n  this._appendStdout = function(content) {\n    this.stdoutRawContent += content;\n    this.stdout[0].innerHTML = ansi_up.ansi_to_html(ansi_up.escape_for_html(this.stdoutRawContent), {\"use_classes\": true});\n  }.bind(this);\n\n  this._clearStdout = function() {\n    this.stdoutRawContent = \"\";\n    this.stdout[0].innerHTML = \"\";\n    this.outputIndicator.turnOff();\n  }.bind(this);\n  //\n\n  this._matchEditorSidebarHeight = function() {\n    window.setTimeout(function(){\n      this.sidebarWrapper.height(this.editorWrapper.height());\n    }.bind(this),0)\n  }.bind(this);\n\n  this.editor.on(\"change\", function(){\n    this._triggerChange(); // -> saveAsLastCode();\n    this._hideEditorErrors();\n    this._matchEditorSidebarHeight();\n    this._scheduleRun();\n  }.bind(this));\n\n  // code formatter\n  var sessionInstance = this;\n  var codeFormatter = (function() {\n    var isRunning = false;\n    var btn = document.getElementById('runFormatterBtn');\n    if (!btn) { return; }\n    btn.addEventListener('click', function(evt) {\n      evt.preventDefault();\n      run();\n    });\n\n    function run() {\n      if (isRunning) {\n        return console.info('code formatter is already running. Attempt aborted...');\n      }\n      if (sessionInstance.isRunning) {\n        return console.info('compiler running. Formatter attempt aborted...')\n      }\n      btn.classList.add('running');\n      isRunning = true;\n      sessionInstance.format();\n    };\n\n    function stop() {\n      isRunning = false;\n      btn.classList.remove('running');\n    }\n\n    var publicProps = { run, stop };\n\n    Object.defineProperty(publicProps, \"isRunning\", {\n      get: function() { return isRunning; }\n    });\n\n    return publicProps;\n  })();\n\n  // when clicking below the editor, set the cursor at the very end\n  this.editorDom.click(function(e){\n    this._hideEditorErrors();\n    if (e.target == this.editorDom[0]) {\n      var info = this.editor.lineInfo(this.editor.lastLine())\n      this.editor.setCursor(info.line, info.text.length);\n      this.editor.focus();\n    }\n  }.bind(this));\n\n  $(window).resize(this._matchEditorSidebarHeight);\n  this._matchEditorSidebarHeight();\n\n  $(window).on(\"unload\", function(){\n    this.stop();\n  }.bind(this));\n};\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/settings.js",
    "content": "if(typeof(Playground) === 'undefined') {\n  Playground = {};\n}\n\nfunction hasStorage() {\n  if (typeof(Storage) !== 'undefined') {\n    try {\n      localStorage.setItem('feature_test', 'yes');\n      if (localStorage.getItem('feature_test') === 'yes') {\n        localStorage.removeItem('feature_test');\n        return true;\n      }\n    } catch (e) {\n      return false;\n    }\n  }\n\n  return false;\n}\n\nPlayground.hasStorage = hasStorage();\n\nPlayground.settings = {\n  default: {\n    settingsGithubToken: '',\n    settingsShowTypes: 'true',\n    settingsRunDebounce: '800'\n  },\n  _readSetting: function(key) {\n    if(Playground.hasStorage && typeof(localStorage[key]) !== 'undefined') {\n      return localStorage[key];\n    }\n    return Playground.settings.default[key];\n  },\n  _saveSetting: function(key, value) {\n    if(Playground.hasStorage) {\n      localStorage[key] = value;\n    } else {\n      console.error(\"Unable to save settings since localStorage is not available\");\n    }\n  },\n  getGithubToken: function() {\n    return Playground.settings._readSetting('settingsGithubToken');\n  },\n  getShowTypes: function() {\n    return Playground.settings._readSetting('settingsShowTypes') == 'true';\n  },\n  getRunDebounce: function() {\n    return parseInt(Playground.settings._readSetting('settingsRunDebounce'));\n  }\n};\n\n$(document).ready(function(){\n  var githubTokenText = $(\"[name=settingsGithubToken]\")\n  var showTypesCheck = $(\"[name=settingsShowTypes]\")\n  var runDebounceInput = $(\"[name=settingsRunDebounce]\")\n\n  if (githubTokenText.length > 0) {\n    // settings is the current page\n    githubTokenText.val(Playground.settings.getGithubToken());\n    showTypesCheck.prop('checked', Playground.settings.getShowTypes());\n    runDebounceInput.val(Playground.settings.getRunDebounce());\n\n    var saveSettings = function() {\n      Playground.settings._saveSetting(\"settingsGithubToken\", githubTokenText.val());\n      Playground.settings._saveSetting(\"settingsShowTypes\", showTypesCheck.is(\":checked\") ? 'true' : 'false');\n      Playground.settings._saveSetting(\"settingsRunDebounce\", runDebounceInput.val());\n    };\n\n    $(\"input\").change(function(){\n      saveSettings();\n    });\n\n    window.onunload = function(){\n      saveSettings();\n    };\n  }\n});\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/REUSE.toml",
    "content": "version = 1\n\n[[annotations]]\npath = \"ansi_up-*/**\"\nprecedence = \"override\"\nSPDX-FileCopyrightText = \"Copyright (c) 2011 Dru Nelson\"\nSPDX-License-Identifier = \"MIT\"\n\n[[annotations]]\npath = \"codemirror-*/**\"\nprecedence = \"override\"\nSPDC-FileCopyrightText = \"Copyright (C) 2016 by Marijn Haverbeke <marijnh@gmail.com> and others\"\nSPDX-License-Identifier = \"MIT\"\n\n[[annotations]]\npath = \"jquery-*.js\"\nprecedence = \"override\"\nSPDC-FileCopyrightText = \"Copyright JS Foundation and other contributors, https://js.foundation/\"\nSPDX-License-Identifier = \"MIT\"\n\n[[annotations]]\npath = \"materialize-*/**\"\nprecedence = \"override\"\nSPDC-FileCopyrightText = \"Copyright (c) 2014-2015 Materialize\"\nSPDX-License-Identifier = \"MIT\"\n\n[[annotations]]\npath = \"octicons-*/**\"\nprecedence = \"override\"\nSPDC-FileCopyrightText = \"(c) 2023 GitHub Inc.\"\nSPDX-License-Identifier = \"MIT\"\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/ansi_up-1.3.0/ansi_up.js",
    "content": "// ansi_up.js\n// version : 1.3.0\n// author : Dru Nelson\n// license : MIT\n// http://github.com/drudru/ansi_up\n\n(function (Date, undefined) {\n\n    var ansi_up,\n        VERSION = \"1.3.0\",\n\n        // check for nodeJS\n        hasModule = (typeof module !== 'undefined'),\n\n        // Normal and then Bright\n        ANSI_COLORS = [\n          [\n            { color: \"0, 0, 0\",        'class': \"ansi-black\"   },\n            { color: \"187, 0, 0\",      'class': \"ansi-red\"     },\n            { color: \"0, 187, 0\",      'class': \"ansi-green\"   },\n            { color: \"187, 187, 0\",    'class': \"ansi-yellow\"  },\n            { color: \"0, 0, 187\",      'class': \"ansi-blue\"    },\n            { color: \"187, 0, 187\",    'class': \"ansi-magenta\" },\n            { color: \"0, 187, 187\",    'class': \"ansi-cyan\"    },\n            { color: \"255,255,255\",    'class': \"ansi-white\"   }\n          ],\n          [\n            { color: \"85, 85, 85\",     'class': \"ansi-bright-black\"   },\n            { color: \"255, 85, 85\",    'class': \"ansi-bright-red\"     },\n            { color: \"0, 255, 0\",      'class': \"ansi-bright-green\"   },\n            { color: \"255, 255, 85\",   'class': \"ansi-bright-yellow\"  },\n            { color: \"85, 85, 255\",    'class': \"ansi-bright-blue\"    },\n            { color: \"255, 85, 255\",   'class': \"ansi-bright-magenta\" },\n            { color: \"85, 255, 255\",   'class': \"ansi-bright-cyan\"    },\n            { color: \"255, 255, 255\",  'class': \"ansi-bright-white\"   }\n          ]\n        ],\n\n        // 256 Colors Palette\n        PALETTE_COLORS;\n\n    function Ansi_Up() {\n      this.fg = this.bg = this.fg_truecolor = this.bg_truecolor = null;\n      this.bright = 0;\n    }\n\n    Ansi_Up.prototype.setup_palette = function() {\n      PALETTE_COLORS = [];\n      // Index 0..15 : System color\n      (function() {\n        var i, j;\n        for (i = 0; i < 2; ++i) {\n          for (j = 0; j < 8; ++j) {\n            PALETTE_COLORS.push(ANSI_COLORS[i][j]['color']);\n          }\n        }\n      })();\n\n      // Index 16..231 : RGB 6x6x6\n      // https://gist.github.com/jasonm23/2868981#file-xterm-256color-yaml\n      (function() {\n        var levels = [0, 95, 135, 175, 215, 255];\n        var format = function (r, g, b) { return levels[r] + ', ' + levels[g] + ', ' + levels[b] };\n        var r, g, b;\n        for (r = 0; r < 6; ++r) {\n          for (g = 0; g < 6; ++g) {\n            for (b = 0; b < 6; ++b) {\n              PALETTE_COLORS.push(format.call(this, r, g, b));\n            }\n          }\n        }\n      })();\n\n      // Index 232..255 : Grayscale\n      (function() {\n        var level = 8;\n        var format = function(level) { return level + ', ' + level + ', ' + level };\n        var i;\n        for (i = 0; i < 24; ++i, level += 10) {\n          PALETTE_COLORS.push(format.call(this, level));\n        }\n      })();\n    };\n\n    Ansi_Up.prototype.escape_for_html = function (txt) {\n      return txt.replace(/[&<>]/gm, function(str) {\n        if (str == \"&\") return \"&amp;\";\n        if (str == \"<\") return \"&lt;\";\n        if (str == \">\") return \"&gt;\";\n      });\n    };\n\n    Ansi_Up.prototype.linkify = function (txt) {\n      return txt.replace(/(https?:\\/\\/[^\\s]+)/gm, function(str) {\n        return \"<a href=\\\"\" + str + \"\\\">\" + str + \"</a>\";\n      });\n    };\n\n    Ansi_Up.prototype.ansi_to_html = function (txt, options) {\n      return this.process(txt, options, true);\n    };\n\n    Ansi_Up.prototype.ansi_to_text = function (txt) {\n      var options = {};\n      return this.process(txt, options, false);\n    };\n\n    Ansi_Up.prototype.process = function (txt, options, markup) {\n      var self = this;\n      var raw_text_chunks = txt.split(/\\033\\[/);\n      var first_chunk = raw_text_chunks.shift(); // the first chunk is not the result of the split\n\n      var color_chunks = raw_text_chunks.map(function (chunk) {\n        return self.process_chunk(chunk, options, markup);\n      });\n\n      color_chunks.unshift(first_chunk);\n\n      return color_chunks.join('');\n    };\n\n    Ansi_Up.prototype.process_chunk = function (text, options, markup) {\n\n      // Are we using classes or styles?\n      options = typeof options == 'undefined' ? {} : options;\n      var use_classes = typeof options.use_classes != 'undefined' && options.use_classes;\n      var key = use_classes ? 'class' : 'color';\n\n      // Each 'chunk' is the text after the CSI (ESC + '[') and before the next CSI/EOF.\n      //\n      // This regex matches four groups within a chunk.\n      //\n      // The first and third groups match code type.\n      // We supported only SGR command. It has empty first group and 'm' in third.\n      //\n      // The second group matches all of the number+semicolon command sequences\n      // before the 'm' (or other trailing) character.\n      // These are the graphics or SGR commands.\n      //\n      // The last group is the text (including newlines) that is colored by\n      // the other group's commands.\n      var matches = text.match(/^([!\\x3c-\\x3f]*)([\\d;]*)([\\x20-\\x2c]*[\\x40-\\x7e])([\\s\\S]*)/m);\n\n      if (!matches) return text;\n\n      var orig_txt = matches[4];\n      var nums = matches[2].split(';');\n\n      // We currently support only \"SGR\" (Select Graphic Rendition)\n      // Simply ignore if not a SGR command.\n      if (matches[1] !== '' || matches[3] !== 'm') {\n        return orig_txt;\n      }\n\n      if (!markup) {\n        return orig_txt;\n      }\n\n      var self = this;\n\n      while (nums.length > 0) {\n        var num_str = nums.shift();\n        var num = parseInt(num_str);\n\n        if (isNaN(num) || num === 0) {\n          self.fg = self.bg = null;\n          self.bright = 0;\n        } else if (num === 1) {\n          self.bright = 1;\n        } else if (num == 39) {\n          self.fg = null;\n        } else if (num == 49) {\n          self.bg = null;\n        } else if ((num >= 30) && (num < 38)) {\n          self.fg = ANSI_COLORS[self.bright][(num % 10)][key];\n        } else if ((num >= 90) && (num < 98)) {\n          self.fg = ANSI_COLORS[1][(num % 10)][key];\n        } else if ((num >= 40) && (num < 48)) {\n          self.bg = ANSI_COLORS[0][(num % 10)][key];\n        } else if ((num >= 100) && (num < 108)) {\n          self.bg = ANSI_COLORS[1][(num % 10)][key];\n        } else if (num === 38 || num === 48) { // extend color (38=fg, 48=bg)\n          (function() {\n            var is_foreground = (num === 38);\n            if (nums.length >= 1) {\n              var mode = nums.shift();\n              if (mode === '5' && nums.length >= 1) { // palette color\n                var palette_index = parseInt(nums.shift());\n                if (palette_index >= 0 && palette_index <= 255) {\n                  if (!use_classes) {\n                    if (!PALETTE_COLORS) {\n                      self.setup_palette.call(self);\n                    }\n                    if (is_foreground) {\n                      self.fg = PALETTE_COLORS[palette_index];\n                    } else {\n                      self.bg = PALETTE_COLORS[palette_index];\n                    }\n                  } else {\n                    var klass = (palette_index >= 16)\n                          ? ('ansi-palette-' + palette_index)\n                          : ANSI_COLORS[palette_index > 7 ? 1 : 0][palette_index % 8]['class'];\n                    if (is_foreground) {\n                      self.fg = klass;\n                    } else {\n                      self.bg = klass;\n                    }\n                  }\n                }\n              } else if(mode === '2' && nums.length >= 3) { // true color\n                var r = parseInt(nums.shift());\n                var g = parseInt(nums.shift());\n                var b = parseInt(nums.shift());\n                if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {\n                  var color = r + ', ' + g + ', ' + b;\n                  if (!use_classes) {\n                    if (is_foreground) {\n                      self.fg = color;\n                    } else {\n                      self.bg = color;\n                    }\n                  } else {\n                    if (is_foreground) {\n                      self.fg = 'ansi-truecolor';\n                      self.fg_truecolor = color;\n                    } else {\n                      self.bg = 'ansi-truecolor';\n                      self.bg_truecolor = color;\n                    }\n                  }\n                }\n              }\n            }\n          })();\n        }\n      }\n\n      if ((self.fg === null) && (self.bg === null)) {\n        return orig_txt;\n      } else {\n        var styles = [];\n        var classes = [];\n        var data = {};\n        var render_data = function (data) {\n          var fragments = [];\n          var key;\n          for (key in data) {\n            if (data.hasOwnProperty(key)) {\n              fragments.push('data-' + key + '=\"' + this.escape_for_html(data[key]) + '\"');\n            }\n          }\n          return fragments.length > 0 ? ' ' + fragments.join(' ') : '';\n        };\n\n        if (self.fg) {\n          if (use_classes) {\n            classes.push(self.fg + \"-fg\");\n            if (self.fg_truecolor !== null) {\n              data['ansi-truecolor-fg'] = self.fg_truecolor;\n              self.fg_truecolor = null;\n            }\n          } else {\n            styles.push(\"color:rgb(\" + self.fg + \")\");\n          }\n        }\n        if (self.bg) {\n          if (use_classes) {\n            classes.push(self.bg + \"-bg\");\n            if (self.bg_truecolor !== null) {\n              data['ansi-truecolor-bg'] = self.bg_truecolor;\n              self.bg_truecolor = null;\n            }\n          } else {\n            styles.push(\"background-color:rgb(\" + self.bg + \")\");\n          }\n        }\n        if (use_classes) {\n          return '<span class=\"' + classes.join(' ') + '\"' + render_data.call(self, data) + '>' + orig_txt + '</span>';\n        } else {\n          return '<span style=\"' + styles.join(';') + '\"' + render_data.call(self, data) + '>' + orig_txt + '</span>';\n        }\n      }\n    };\n\n    // Module exports\n    ansi_up = {\n\n      escape_for_html: function (txt) {\n        var a2h = new Ansi_Up();\n        return a2h.escape_for_html(txt);\n      },\n\n      linkify: function (txt) {\n        var a2h = new Ansi_Up();\n        return a2h.linkify(txt);\n      },\n\n      ansi_to_html: function (txt, options) {\n        var a2h = new Ansi_Up();\n        return a2h.ansi_to_html(txt, options);\n      },\n\n      ansi_to_text: function (txt) {\n        var a2h = new Ansi_Up();\n        return a2h.ansi_to_text(txt);\n      },\n\n      ansi_to_html_obj: function () {\n        return new Ansi_Up();\n      }\n    };\n\n    // CommonJS module is defined\n    if (hasModule) {\n        module.exports = ansi_up;\n    }\n    /*global ender:false */\n    if (typeof window !== 'undefined' && typeof ender === 'undefined') {\n        window.ansi_up = ansi_up;\n    }\n    /*global define:false */\n    if (typeof define === \"function\" && define.amd) {\n        define(\"ansi_up\", [], function () {\n            return ansi_up;\n        });\n    }\n})(Date);\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/ansi_up-1.3.0/theme.css",
    "content": ".ansi-base16-railscasts-bright .ansi-bright-black-fg, .ansi-base16-railscasts-bright .ansi-black-fg {\n    color: #000000;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-red-fg, .ansi-base16-railscasts-bright .ansi-red-fg {\n    color: #fb0120;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-green-fg, .ansi-base16-railscasts-bright .ansi-green-fg {\n    color: #00770A;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-yellow-fg, .ansi-base16-railscasts-bright .ansi-yellow-fg {\n    color: #fda331;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-blue-fg, .ansi-base16-railscasts-bright .ansi-blue-fg {\n    color: #6fb3d2;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-magenta-fg, .ansi-base16-railscasts-bright .ansi-magenta-fg {\n    color: #d381c3;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-cyan-fg, .ansi-base16-railscasts-bright .ansi-cyan-fg {\n    color: #76c7b7;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-white-fg, .ansi-base16-railscasts-bright .ansi-white-fg {\n    color: #ffffff;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-black-bg, .ansi-base16-railscasts-bright .ansi-black-bg {\n    background-color: #000000;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-red-bg, .ansi-base16-railscasts-bright .ansi-red-bg {\n    background-color: #fb0120;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-green-bg, .ansi-base16-railscasts-bright .ansi-green-bg {\n    background-color: #a1c659;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-yellow-bg, .ansi-base16-railscasts-bright .ansi-yellow-bg {\n    background-color: #fda331;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-blue-bg, .ansi-base16-railscasts-bright .ansi-blue-bg {\n    background-color: #6fb3d2;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-magenta-bg, .ansi-base16-railscasts-bright .ansi-magenta-bg {\n    background-color: #d381c3;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-cyan-bg, .ansi-base16-railscasts-bright .ansi-cyan-bg {\n    background-color: #76c7b7;\n  }\n\n.ansi-base16-railscasts-bright .ansi-bright-white-bg, .ansi-base16-railscasts-bright .ansi-white-bg {\n    background-color: #ffffff;\n  }\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/codemirror-5.38.0/addon/comment/comment.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var noOptions = {};\n  var nonWS = /[^\\s\\u00a0]/;\n  var Pos = CodeMirror.Pos;\n\n  function firstNonWS(str) {\n    var found = str.search(nonWS);\n    return found == -1 ? 0 : found;\n  }\n\n  CodeMirror.commands.toggleComment = function(cm) {\n    cm.toggleComment();\n  };\n\n  CodeMirror.defineExtension(\"toggleComment\", function(options) {\n    if (!options) options = noOptions;\n    var cm = this;\n    var minLine = Infinity, ranges = this.listSelections(), mode = null;\n    for (var i = ranges.length - 1; i >= 0; i--) {\n      var from = ranges[i].from(), to = ranges[i].to();\n      if (from.line >= minLine) continue;\n      if (to.line >= minLine) to = Pos(minLine, 0);\n      minLine = from.line;\n      if (mode == null) {\n        if (cm.uncomment(from, to, options)) mode = \"un\";\n        else { cm.lineComment(from, to, options); mode = \"line\"; }\n      } else if (mode == \"un\") {\n        cm.uncomment(from, to, options);\n      } else {\n        cm.lineComment(from, to, options);\n      }\n    }\n  });\n\n  // Rough heuristic to try and detect lines that are part of multi-line string\n  function probablyInsideString(cm, pos, line) {\n    return /\\bstring\\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\\'\\\"\\`]/.test(line)\n  }\n\n  function getMode(cm, pos) {\n    var mode = cm.getMode()\n    return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)\n  }\n\n  CodeMirror.defineExtension(\"lineComment\", function(from, to, options) {\n    if (!options) options = noOptions;\n    var self = this, mode = getMode(self, from);\n    var firstLine = self.getLine(from.line);\n    if (firstLine == null || probablyInsideString(self, from, firstLine)) return;\n\n    var commentString = options.lineComment || mode.lineComment;\n    if (!commentString) {\n      if (options.blockCommentStart || mode.blockCommentStart) {\n        options.fullLines = true;\n        self.blockComment(from, to, options);\n      }\n      return;\n    }\n\n    var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);\n    var pad = options.padding == null ? \" \" : options.padding;\n    var blankLines = options.commentBlankLines || from.line == to.line;\n\n    self.operation(function() {\n      if (options.indent) {\n        var baseString = null;\n        for (var i = from.line; i < end; ++i) {\n          var line = self.getLine(i);\n          var whitespace = line.slice(0, firstNonWS(line));\n          if (baseString == null || baseString.length > whitespace.length) {\n            baseString = whitespace;\n          }\n        }\n        for (var i = from.line; i < end; ++i) {\n          var line = self.getLine(i), cut = baseString.length;\n          if (!blankLines && !nonWS.test(line)) continue;\n          if (line.slice(0, cut) != baseString) cut = firstNonWS(line);\n          self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));\n        }\n      } else {\n        for (var i = from.line; i < end; ++i) {\n          if (blankLines || nonWS.test(self.getLine(i)))\n            self.replaceRange(commentString + pad, Pos(i, 0));\n        }\n      }\n    });\n  });\n\n  CodeMirror.defineExtension(\"blockComment\", function(from, to, options) {\n    if (!options) options = noOptions;\n    var self = this, mode = getMode(self, from);\n    var startString = options.blockCommentStart || mode.blockCommentStart;\n    var endString = options.blockCommentEnd || mode.blockCommentEnd;\n    if (!startString || !endString) {\n      if ((options.lineComment || mode.lineComment) && options.fullLines != false)\n        self.lineComment(from, to, options);\n      return;\n    }\n    if (/\\bcomment\\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return\n\n    var end = Math.min(to.line, self.lastLine());\n    if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;\n\n    var pad = options.padding == null ? \" \" : options.padding;\n    if (from.line > end) return;\n\n    self.operation(function() {\n      if (options.fullLines != false) {\n        var lastLineHasText = nonWS.test(self.getLine(end));\n        self.replaceRange(pad + endString, Pos(end));\n        self.replaceRange(startString + pad, Pos(from.line, 0));\n        var lead = options.blockCommentLead || mode.blockCommentLead;\n        if (lead != null) for (var i = from.line + 1; i <= end; ++i)\n          if (i != end || lastLineHasText)\n            self.replaceRange(lead + pad, Pos(i, 0));\n      } else {\n        self.replaceRange(endString, to);\n        self.replaceRange(startString, from);\n      }\n    });\n  });\n\n  CodeMirror.defineExtension(\"uncomment\", function(from, to, options) {\n    if (!options) options = noOptions;\n    var self = this, mode = getMode(self, from);\n    var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);\n\n    // Try finding line comments\n    var lineString = options.lineComment || mode.lineComment, lines = [];\n    var pad = options.padding == null ? \" \" : options.padding, didSomething;\n    lineComment: {\n      if (!lineString) break lineComment;\n      for (var i = start; i <= end; ++i) {\n        var line = self.getLine(i);\n        var found = line.indexOf(lineString);\n        if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;\n        if (found == -1 && nonWS.test(line)) break lineComment;\n        if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;\n        lines.push(line);\n      }\n      self.operation(function() {\n        for (var i = start; i <= end; ++i) {\n          var line = lines[i - start];\n          var pos = line.indexOf(lineString), endPos = pos + lineString.length;\n          if (pos < 0) continue;\n          if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;\n          didSomething = true;\n          self.replaceRange(\"\", Pos(i, pos), Pos(i, endPos));\n        }\n      });\n      if (didSomething) return true;\n    }\n\n    // Try block comments\n    var startString = options.blockCommentStart || mode.blockCommentStart;\n    var endString = options.blockCommentEnd || mode.blockCommentEnd;\n    if (!startString || !endString) return false;\n    var lead = options.blockCommentLead || mode.blockCommentLead;\n    var startLine = self.getLine(start), open = startLine.indexOf(startString)\n    if (open == -1) return false\n    var endLine = end == start ? startLine : self.getLine(end)\n    var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);\n    var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)\n    if (close == -1 ||\n        !/comment/.test(self.getTokenTypeAt(insideStart)) ||\n        !/comment/.test(self.getTokenTypeAt(insideEnd)) ||\n        self.getRange(insideStart, insideEnd, \"\\n\").indexOf(endString) > -1)\n      return false;\n\n    // Avoid killing block comments completely outside the selection.\n    // Positions of the last startString before the start of the selection, and the first endString after it.\n    var lastStart = startLine.lastIndexOf(startString, from.ch);\n    var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);\n    if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;\n    // Positions of the first endString after the end of the selection, and the last startString before it.\n    firstEnd = endLine.indexOf(endString, to.ch);\n    var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);\n    lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;\n    if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;\n\n    self.operation(function() {\n      self.replaceRange(\"\", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),\n                        Pos(end, close + endString.length));\n      var openEnd = open + startString.length;\n      if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;\n      self.replaceRange(\"\", Pos(start, open), Pos(start, openEnd));\n      if (lead) for (var i = start + 1; i <= end; ++i) {\n        var line = self.getLine(i), found = line.indexOf(lead);\n        if (found == -1 || nonWS.test(line.slice(0, found))) continue;\n        var foundEnd = found + lead.length;\n        if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;\n        self.replaceRange(\"\", Pos(i, found), Pos(i, foundEnd));\n      }\n    });\n    return true;\n  });\n});\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/codemirror-5.38.0/lib/codemirror.css",
    "content": "/* BASICS */\n\n.CodeMirror {\n  /* Set height, width, borders, and global font properties here */\n  font-family: monospace;\n  height: 300px;\n  color: black;\n  direction: ltr;\n}\n\n/* PADDING */\n\n.CodeMirror-lines {\n  padding: 4px 0; /* Vertical padding around content */\n}\n.CodeMirror pre {\n  padding: 0 4px; /* Horizontal padding of content */\n}\n\n.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  background-color: white; /* The little square between H and V scrollbars */\n}\n\n/* GUTTER */\n\n.CodeMirror-gutters {\n  border-right: 1px solid #ddd;\n  background-color: #f7f7f7;\n  white-space: nowrap;\n}\n.CodeMirror-linenumbers {}\n.CodeMirror-linenumber {\n  padding: 0 3px 0 5px;\n  min-width: 20px;\n  text-align: right;\n  color: #999;\n  white-space: nowrap;\n}\n\n.CodeMirror-guttermarker { color: black; }\n.CodeMirror-guttermarker-subtle { color: #999; }\n\n/* CURSOR */\n\n.CodeMirror-cursor {\n  border-left: 1px solid black;\n  border-right: none;\n  width: 0;\n}\n/* Shown when moving in bi-directional text */\n.CodeMirror div.CodeMirror-secondarycursor {\n  border-left: 1px solid silver;\n}\n.cm-fat-cursor .CodeMirror-cursor {\n  width: auto;\n  border: 0 !important;\n  background: #7e7;\n}\n.cm-fat-cursor div.CodeMirror-cursors {\n  z-index: 1;\n}\n.cm-fat-cursor-mark {\n  background-color: rgba(20, 255, 20, 0.5);\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n}\n.cm-animate-fat-cursor {\n  width: auto;\n  border: 0;\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n  background-color: #7e7;\n}\n@-moz-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@-webkit-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n\n/* Can style cursor different in overwrite (non-insert) mode */\n.CodeMirror-overwrite .CodeMirror-cursor {}\n\n.cm-tab { display: inline-block; text-decoration: inherit; }\n\n.CodeMirror-rulers {\n  position: absolute;\n  left: 0; right: 0; top: -50px; bottom: -20px;\n  overflow: hidden;\n}\n.CodeMirror-ruler {\n  border-left: 1px solid #ccc;\n  top: 0; bottom: 0;\n  position: absolute;\n}\n\n/* DEFAULT THEME */\n\n.cm-s-default .cm-header {color: blue;}\n.cm-s-default .cm-quote {color: #090;}\n.cm-negative {color: #d44;}\n.cm-positive {color: #292;}\n.cm-header, .cm-strong {font-weight: bold;}\n.cm-em {font-style: italic;}\n.cm-link {text-decoration: underline;}\n.cm-strikethrough {text-decoration: line-through;}\n\n.cm-s-default .cm-keyword {color: #708;}\n.cm-s-default .cm-atom {color: #219;}\n.cm-s-default .cm-number {color: #164;}\n.cm-s-default .cm-def {color: #00f;}\n.cm-s-default .cm-variable,\n.cm-s-default .cm-punctuation,\n.cm-s-default .cm-property,\n.cm-s-default .cm-operator {}\n.cm-s-default .cm-variable-2 {color: #05a;}\n.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}\n.cm-s-default .cm-comment {color: #a50;}\n.cm-s-default .cm-string {color: #a11;}\n.cm-s-default .cm-string-2 {color: #f50;}\n.cm-s-default .cm-meta {color: #555;}\n.cm-s-default .cm-qualifier {color: #555;}\n.cm-s-default .cm-builtin {color: #30a;}\n.cm-s-default .cm-bracket {color: #997;}\n.cm-s-default .cm-tag {color: #170;}\n.cm-s-default .cm-attribute {color: #00c;}\n.cm-s-default .cm-hr {color: #999;}\n.cm-s-default .cm-link {color: #00c;}\n\n.cm-s-default .cm-error {color: #f00;}\n.cm-invalidchar {color: #f00;}\n\n.CodeMirror-composing { border-bottom: 2px solid; }\n\n/* Default styles for common addons */\n\ndiv.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}\ndiv.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}\n.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }\n.CodeMirror-activeline-background {background: #e8f2ff;}\n\n/* STOP */\n\n/* The rest of this file contains styles related to the mechanics of\n   the editor. You probably shouldn't touch them. */\n\n.CodeMirror {\n  position: relative;\n  overflow: hidden;\n  background: white;\n}\n\n.CodeMirror-scroll {\n  overflow: scroll !important; /* Things will break if this is overridden */\n  /* 30px is the magic margin used to hide the element's real scrollbars */\n  /* See overflow: hidden in .CodeMirror */\n  margin-bottom: -30px; margin-right: -30px;\n  padding-bottom: 30px;\n  height: 100%;\n  outline: none; /* Prevent dragging from highlighting the element */\n  position: relative;\n}\n.CodeMirror-sizer {\n  position: relative;\n  border-right: 30px solid transparent;\n}\n\n/* The fake, visible scrollbars. Used to force redraw during scrolling\n   before actual scrolling happens, thus preventing shaking and\n   flickering artifacts. */\n.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  position: absolute;\n  z-index: 6;\n  display: none;\n}\n.CodeMirror-vscrollbar {\n  right: 0; top: 0;\n  overflow-x: hidden;\n  overflow-y: scroll;\n}\n.CodeMirror-hscrollbar {\n  bottom: 0; left: 0;\n  overflow-y: hidden;\n  overflow-x: scroll;\n}\n.CodeMirror-scrollbar-filler {\n  right: 0; bottom: 0;\n}\n.CodeMirror-gutter-filler {\n  left: 0; bottom: 0;\n}\n\n.CodeMirror-gutters {\n  position: absolute; left: 0; top: 0;\n  min-height: 100%;\n  z-index: 3;\n}\n.CodeMirror-gutter {\n  white-space: normal;\n  height: 100%;\n  display: inline-block;\n  vertical-align: top;\n  margin-bottom: -30px;\n}\n.CodeMirror-gutter-wrapper {\n  position: absolute;\n  z-index: 4;\n  background: none !important;\n  border: none !important;\n}\n.CodeMirror-gutter-background {\n  position: absolute;\n  top: 0; bottom: 0;\n  z-index: 4;\n}\n.CodeMirror-gutter-elt {\n  position: absolute;\n  cursor: default;\n  z-index: 4;\n}\n.CodeMirror-gutter-wrapper ::selection { background-color: transparent }\n.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }\n\n.CodeMirror-lines {\n  cursor: text;\n  min-height: 1px; /* prevents collapsing before first draw */\n}\n.CodeMirror pre {\n  /* Reset some styles that the rest of the page might have set */\n  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;\n  border-width: 0;\n  background: transparent;\n  font-family: inherit;\n  font-size: inherit;\n  margin: 0;\n  white-space: pre;\n  word-wrap: normal;\n  line-height: inherit;\n  color: inherit;\n  z-index: 2;\n  position: relative;\n  overflow: visible;\n  -webkit-tap-highlight-color: transparent;\n  -webkit-font-variant-ligatures: contextual;\n  font-variant-ligatures: contextual;\n}\n.CodeMirror-wrap pre {\n  word-wrap: break-word;\n  white-space: pre-wrap;\n  word-break: normal;\n}\n\n.CodeMirror-linebackground {\n  position: absolute;\n  left: 0; right: 0; top: 0; bottom: 0;\n  z-index: 0;\n}\n\n.CodeMirror-linewidget {\n  position: relative;\n  z-index: 2;\n  padding: 0.1px; /* Force widget margins to stay inside of the container */\n}\n\n.CodeMirror-widget {}\n\n.CodeMirror-rtl pre { direction: rtl; }\n\n.CodeMirror-code {\n  outline: none;\n}\n\n/* Force content-box sizing for the elements where we expect it */\n.CodeMirror-scroll,\n.CodeMirror-sizer,\n.CodeMirror-gutter,\n.CodeMirror-gutters,\n.CodeMirror-linenumber {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n}\n\n.CodeMirror-measure {\n  position: absolute;\n  width: 100%;\n  height: 0;\n  overflow: hidden;\n  visibility: hidden;\n}\n\n.CodeMirror-cursor {\n  position: absolute;\n  pointer-events: none;\n}\n.CodeMirror-measure pre { position: static; }\n\ndiv.CodeMirror-cursors {\n  visibility: hidden;\n  position: relative;\n  z-index: 3;\n}\ndiv.CodeMirror-dragcursors {\n  visibility: visible;\n}\n\n.CodeMirror-focused div.CodeMirror-cursors {\n  visibility: visible;\n}\n\n.CodeMirror-selected { background: #d9d9d9; }\n.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }\n.CodeMirror-crosshair { cursor: crosshair; }\n.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }\n.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }\n\n.cm-searching {\n  background-color: #ffa;\n  background-color: rgba(255, 255, 0, .4);\n}\n\n/* Used to force a border model for a node */\n.cm-force-border { padding-right: .1px; }\n\n@media print {\n  /* Hide the cursor when printing */\n  .CodeMirror div.CodeMirror-cursors {\n    visibility: hidden;\n  }\n}\n\n/* See issue #2901 */\n.cm-tab-wrap-hack:after { content: ''; }\n\n/* Help users use markselection to safely style text background */\nspan.CodeMirror-selectedtext { background: none; }\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/codemirror-5.38.0/lib/codemirror.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/LICENSE\n\n// This is CodeMirror (http://codemirror.net), a code editor\n// implemented in JavaScript on top of the browser's DOM.\n//\n// You can find some technical background for some of the code below\n// at http://marijnhaverbeke.nl/blog/#cm-internals .\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global.CodeMirror = factory());\n}(this, (function () { 'use strict';\n\n// Kludges for bugs and behavior differences that can't be feature\n// detected are enabled based on userAgent etc sniffing.\nvar userAgent = navigator.userAgent\nvar platform = navigator.platform\n\nvar gecko = /gecko\\/\\d/i.test(userAgent)\nvar ie_upto10 = /MSIE \\d/.test(userAgent)\nvar ie_11up = /Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(userAgent)\nvar edge = /Edge\\/(\\d+)/.exec(userAgent)\nvar ie = ie_upto10 || ie_11up || edge\nvar ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])\nvar webkit = !edge && /WebKit\\//.test(userAgent)\nvar qtwebkit = webkit && /Qt\\/\\d+\\.\\d+/.test(userAgent)\nvar chrome = !edge && /Chrome\\//.test(userAgent)\nvar presto = /Opera\\//.test(userAgent)\nvar safari = /Apple Computer/.test(navigator.vendor)\nvar mac_geMountainLion = /Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(userAgent)\nvar phantom = /PhantomJS/.test(userAgent)\n\nvar ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\\/\\w+/.test(userAgent)\nvar android = /Android/.test(userAgent)\n// This is woefully incomplete. Suggestions for alternative methods welcome.\nvar mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)\nvar mac = ios || /Mac/.test(platform)\nvar chromeOS = /\\bCrOS\\b/.test(userAgent)\nvar windows = /win/i.test(platform)\n\nvar presto_version = presto && userAgent.match(/Version\\/(\\d*\\.\\d*)/)\nif (presto_version) { presto_version = Number(presto_version[1]) }\nif (presto_version && presto_version >= 15) { presto = false; webkit = true }\n// Some browsers use the wrong event properties to signal cmd/ctrl on OS X\nvar flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))\nvar captureRightClick = gecko || (ie && ie_version >= 9)\n\nfunction classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\nvar rmClass = function(node, cls) {\n  var current = node.className\n  var match = classTest(cls).exec(current)\n  if (match) {\n    var after = current.slice(match.index + match[0].length)\n    node.className = current.slice(0, match.index) + (after ? match[1] + after : \"\")\n  }\n}\n\nfunction removeChildren(e) {\n  for (var count = e.childNodes.length; count > 0; --count)\n    { e.removeChild(e.firstChild) }\n  return e\n}\n\nfunction removeChildrenAndAdd(parent, e) {\n  return removeChildren(parent).appendChild(e)\n}\n\nfunction elt(tag, content, className, style) {\n  var e = document.createElement(tag)\n  if (className) { e.className = className }\n  if (style) { e.style.cssText = style }\n  if (typeof content == \"string\") { e.appendChild(document.createTextNode(content)) }\n  else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }\n  return e\n}\n// wrapper for elt, which removes the elt from the accessibility tree\nfunction eltP(tag, content, className, style) {\n  var e = elt(tag, content, className, style)\n  e.setAttribute(\"role\", \"presentation\")\n  return e\n}\n\nvar range\nif (document.createRange) { range = function(node, start, end, endNode) {\n  var r = document.createRange()\n  r.setEnd(endNode || node, end)\n  r.setStart(node, start)\n  return r\n} }\nelse { range = function(node, start, end) {\n  var r = document.body.createTextRange()\n  try { r.moveToElementText(node.parentNode) }\n  catch(e) { return r }\n  r.collapse(true)\n  r.moveEnd(\"character\", end)\n  r.moveStart(\"character\", start)\n  return r\n} }\n\nfunction contains(parent, child) {\n  if (child.nodeType == 3) // Android browser always returns false when child is a textnode\n    { child = child.parentNode }\n  if (parent.contains)\n    { return parent.contains(child) }\n  do {\n    if (child.nodeType == 11) { child = child.host }\n    if (child == parent) { return true }\n  } while (child = child.parentNode)\n}\n\nfunction activeElt() {\n  // IE and Edge may throw an \"Unspecified Error\" when accessing document.activeElement.\n  // IE < 10 will throw when accessed while the page is loading or in an iframe.\n  // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.\n  var activeElement\n  try {\n    activeElement = document.activeElement\n  } catch(e) {\n    activeElement = document.body || null\n  }\n  while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n    { activeElement = activeElement.shadowRoot.activeElement }\n  return activeElement\n}\n\nfunction addClass(node, cls) {\n  var current = node.className\n  if (!classTest(cls).test(current)) { node.className += (current ? \" \" : \"\") + cls }\n}\nfunction joinClasses(a, b) {\n  var as = a.split(\" \")\n  for (var i = 0; i < as.length; i++)\n    { if (as[i] && !classTest(as[i]).test(b)) { b += \" \" + as[i] } }\n  return b\n}\n\nvar selectInput = function(node) { node.select() }\nif (ios) // Mobile Safari apparently has a bug where select() is broken.\n  { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }\nelse if (ie) // Suppress mysterious IE10 errors\n  { selectInput = function(node) { try { node.select() } catch(_e) {} } }\n\nfunction bind(f) {\n  var args = Array.prototype.slice.call(arguments, 1)\n  return function(){return f.apply(null, args)}\n}\n\nfunction copyObj(obj, target, overwrite) {\n  if (!target) { target = {} }\n  for (var prop in obj)\n    { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n      { target[prop] = obj[prop] } }\n  return target\n}\n\n// Counts the column offset in a string, taking tabs into account.\n// Used mostly to find indentation.\nfunction countColumn(string, end, tabSize, startIndex, startValue) {\n  if (end == null) {\n    end = string.search(/[^\\s\\u00a0]/)\n    if (end == -1) { end = string.length }\n  }\n  for (var i = startIndex || 0, n = startValue || 0;;) {\n    var nextTab = string.indexOf(\"\\t\", i)\n    if (nextTab < 0 || nextTab >= end)\n      { return n + (end - i) }\n    n += nextTab - i\n    n += tabSize - (n % tabSize)\n    i = nextTab + 1\n  }\n}\n\nvar Delayed = function() {this.id = null};\nDelayed.prototype.set = function (ms, f) {\n  clearTimeout(this.id)\n  this.id = setTimeout(f, ms)\n};\n\nfunction indexOf(array, elt) {\n  for (var i = 0; i < array.length; ++i)\n    { if (array[i] == elt) { return i } }\n  return -1\n}\n\n// Number of pixels added to scroller and sizer to hide scrollbar\nvar scrollerGap = 30\n\n// Returned or thrown by various protocols to signal 'I'm not\n// handling this'.\nvar Pass = {toString: function(){return \"CodeMirror.Pass\"}}\n\n// Reused option objects for setSelection & friends\nvar sel_dontScroll = {scroll: false};\nvar sel_mouse = {origin: \"*mouse\"};\nvar sel_move = {origin: \"+move\"};\n// The inverse of countColumn -- find the offset that corresponds to\n// a particular column.\nfunction findColumn(string, goal, tabSize) {\n  for (var pos = 0, col = 0;;) {\n    var nextTab = string.indexOf(\"\\t\", pos)\n    if (nextTab == -1) { nextTab = string.length }\n    var skipped = nextTab - pos\n    if (nextTab == string.length || col + skipped >= goal)\n      { return pos + Math.min(skipped, goal - col) }\n    col += nextTab - pos\n    col += tabSize - (col % tabSize)\n    pos = nextTab + 1\n    if (col >= goal) { return pos }\n  }\n}\n\nvar spaceStrs = [\"\"]\nfunction spaceStr(n) {\n  while (spaceStrs.length <= n)\n    { spaceStrs.push(lst(spaceStrs) + \" \") }\n  return spaceStrs[n]\n}\n\nfunction lst(arr) { return arr[arr.length-1] }\n\nfunction map(array, f) {\n  var out = []\n  for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }\n  return out\n}\n\nfunction insertSorted(array, value, score) {\n  var pos = 0, priority = score(value)\n  while (pos < array.length && score(array[pos]) <= priority) { pos++ }\n  array.splice(pos, 0, value)\n}\n\nfunction nothing() {}\n\nfunction createObj(base, props) {\n  var inst\n  if (Object.create) {\n    inst = Object.create(base)\n  } else {\n    nothing.prototype = base\n    inst = new nothing()\n  }\n  if (props) { copyObj(props, inst) }\n  return inst\n}\n\nvar nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/\nfunction isWordCharBasic(ch) {\n  return /\\w/.test(ch) || ch > \"\\x80\" &&\n    (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))\n}\nfunction isWordChar(ch, helper) {\n  if (!helper) { return isWordCharBasic(ch) }\n  if (helper.source.indexOf(\"\\\\w\") > -1 && isWordCharBasic(ch)) { return true }\n  return helper.test(ch)\n}\n\nfunction isEmpty(obj) {\n  for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }\n  return true\n}\n\n// Extending unicode characters. A series of a non-extending char +\n// any number of extending chars is treated as a single unit as far\n// as editing and measuring is concerned. This is not fully correct,\n// since some scripts/fonts/browsers also treat other configurations\n// of code points as a group.\nvar extendingChars = /[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/\nfunction isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }\n\n// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.\nfunction skipExtendingChars(str, pos, dir) {\n  while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir }\n  return pos\n}\n\n// Returns the value from the range [`from`; `to`] that satisfies\n// `pred` and is closest to `from`. Assumes that at least `to`\n// satisfies `pred`. Supports `from` being greater than `to`.\nfunction findFirst(pred, from, to) {\n  // At any point we are certain `to` satisfies `pred`, don't know\n  // whether `from` does.\n  var dir = from > to ? -1 : 1\n  for (;;) {\n    if (from == to) { return from }\n    var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)\n    if (mid == from) { return pred(mid) ? from : to }\n    if (pred(mid)) { to = mid }\n    else { from = mid + dir }\n  }\n}\n\n// The display handles the DOM integration, both for input reading\n// and content drawing. It holds references to DOM nodes and\n// display-related state.\n\nfunction Display(place, doc, input) {\n  var d = this\n  this.input = input\n\n  // Covers bottom-right square when both scrollbars are present.\n  d.scrollbarFiller = elt(\"div\", null, \"CodeMirror-scrollbar-filler\")\n  d.scrollbarFiller.setAttribute(\"cm-not-content\", \"true\")\n  // Covers bottom of gutter when coverGutterNextToScrollbar is on\n  // and h scrollbar is present.\n  d.gutterFiller = elt(\"div\", null, \"CodeMirror-gutter-filler\")\n  d.gutterFiller.setAttribute(\"cm-not-content\", \"true\")\n  // Will contain the actual code, positioned to cover the viewport.\n  d.lineDiv = eltP(\"div\", null, \"CodeMirror-code\")\n  // Elements are added to these to represent selection and cursors.\n  d.selectionDiv = elt(\"div\", null, null, \"position: relative; z-index: 1\")\n  d.cursorDiv = elt(\"div\", null, \"CodeMirror-cursors\")\n  // A visibility: hidden element used to find the size of things.\n  d.measure = elt(\"div\", null, \"CodeMirror-measure\")\n  // When lines outside of the viewport are measured, they are drawn in this.\n  d.lineMeasure = elt(\"div\", null, \"CodeMirror-measure\")\n  // Wraps everything that needs to exist inside the vertically-padded coordinate system\n  d.lineSpace = eltP(\"div\", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],\n                    null, \"position: relative; outline: none\")\n  var lines = eltP(\"div\", [d.lineSpace], \"CodeMirror-lines\")\n  // Moved around its parent to cover visible view.\n  d.mover = elt(\"div\", [lines], null, \"position: relative\")\n  // Set to the height of the document, allowing scrolling.\n  d.sizer = elt(\"div\", [d.mover], \"CodeMirror-sizer\")\n  d.sizerWidth = null\n  // Behavior of elts with overflow: auto and padding is\n  // inconsistent across browsers. This is used to ensure the\n  // scrollable area is big enough.\n  d.heightForcer = elt(\"div\", null, null, \"position: absolute; height: \" + scrollerGap + \"px; width: 1px;\")\n  // Will contain the gutters, if any.\n  d.gutters = elt(\"div\", null, \"CodeMirror-gutters\")\n  d.lineGutter = null\n  // Actual scrollable element.\n  d.scroller = elt(\"div\", [d.sizer, d.heightForcer, d.gutters], \"CodeMirror-scroll\")\n  d.scroller.setAttribute(\"tabIndex\", \"-1\")\n  // The element in which the editor lives.\n  d.wrapper = elt(\"div\", [d.scrollbarFiller, d.gutterFiller, d.scroller], \"CodeMirror\")\n\n  // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)\n  if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }\n  if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }\n\n  if (place) {\n    if (place.appendChild) { place.appendChild(d.wrapper) }\n    else { place(d.wrapper) }\n  }\n\n  // Current rendered range (may be bigger than the view window).\n  d.viewFrom = d.viewTo = doc.first\n  d.reportedViewFrom = d.reportedViewTo = doc.first\n  // Information about the rendered lines.\n  d.view = []\n  d.renderedView = null\n  // Holds info about a single rendered line when it was rendered\n  // for measurement, while not in view.\n  d.externalMeasured = null\n  // Empty space (in pixels) above the view\n  d.viewOffset = 0\n  d.lastWrapHeight = d.lastWrapWidth = 0\n  d.updateLineNumbers = null\n\n  d.nativeBarWidth = d.barHeight = d.barWidth = 0\n  d.scrollbarsClipped = false\n\n  // Used to only resize the line number gutter when necessary (when\n  // the amount of lines crosses a boundary that makes its width change)\n  d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null\n  // Set to true when a non-horizontal-scrolling line widget is\n  // added. As an optimization, line widget aligning is skipped when\n  // this is false.\n  d.alignWidgets = false\n\n  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null\n\n  // Tracks the maximum line length so that the horizontal scrollbar\n  // can be kept static when scrolling.\n  d.maxLine = null\n  d.maxLineLength = 0\n  d.maxLineChanged = false\n\n  // Used for measuring wheel scrolling granularity\n  d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null\n\n  // True when shift is held down.\n  d.shift = false\n\n  // Used to track whether anything happened since the context menu\n  // was opened.\n  d.selForContextMenu = null\n\n  d.activeTouch = null\n\n  input.init(d)\n}\n\n// Find the line object corresponding to the given line number.\nfunction getLine(doc, n) {\n  n -= doc.first\n  if (n < 0 || n >= doc.size) { throw new Error(\"There is no line \" + (n + doc.first) + \" in the document.\") }\n  var chunk = doc\n  while (!chunk.lines) {\n    for (var i = 0;; ++i) {\n      var child = chunk.children[i], sz = child.chunkSize()\n      if (n < sz) { chunk = child; break }\n      n -= sz\n    }\n  }\n  return chunk.lines[n]\n}\n\n// Get the part of a document between two positions, as an array of\n// strings.\nfunction getBetween(doc, start, end) {\n  var out = [], n = start.line\n  doc.iter(start.line, end.line + 1, function (line) {\n    var text = line.text\n    if (n == end.line) { text = text.slice(0, end.ch) }\n    if (n == start.line) { text = text.slice(start.ch) }\n    out.push(text)\n    ++n\n  })\n  return out\n}\n// Get the lines between from and to, as array of strings.\nfunction getLines(doc, from, to) {\n  var out = []\n  doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value\n  return out\n}\n\n// Update the height of a line, propagating the height change\n// upwards to parent nodes.\nfunction updateLineHeight(line, height) {\n  var diff = height - line.height\n  if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }\n}\n\n// Given a line object, find its line number by walking up through\n// its parent links.\nfunction lineNo(line) {\n  if (line.parent == null) { return null }\n  var cur = line.parent, no = indexOf(cur.lines, line)\n  for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {\n    for (var i = 0;; ++i) {\n      if (chunk.children[i] == cur) { break }\n      no += chunk.children[i].chunkSize()\n    }\n  }\n  return no + cur.first\n}\n\n// Find the line at the given vertical position, using the height\n// information in the document tree.\nfunction lineAtHeight(chunk, h) {\n  var n = chunk.first\n  outer: do {\n    for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {\n      var child = chunk.children[i$1], ch = child.height\n      if (h < ch) { chunk = child; continue outer }\n      h -= ch\n      n += child.chunkSize()\n    }\n    return n\n  } while (!chunk.lines)\n  var i = 0\n  for (; i < chunk.lines.length; ++i) {\n    var line = chunk.lines[i], lh = line.height\n    if (h < lh) { break }\n    h -= lh\n  }\n  return n + i\n}\n\nfunction isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}\n\nfunction lineNumberFor(options, i) {\n  return String(options.lineNumberFormatter(i + options.firstLineNumber))\n}\n\n// A Pos instance represents a position within the text.\nfunction Pos(line, ch, sticky) {\n  if ( sticky === void 0 ) sticky = null;\n\n  if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }\n  this.line = line\n  this.ch = ch\n  this.sticky = sticky\n}\n\n// Compare two positions, return 0 if they are the same, a negative\n// number when a is less, and a positive number otherwise.\nfunction cmp(a, b) { return a.line - b.line || a.ch - b.ch }\n\nfunction equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }\n\nfunction copyPos(x) {return Pos(x.line, x.ch)}\nfunction maxPos(a, b) { return cmp(a, b) < 0 ? b : a }\nfunction minPos(a, b) { return cmp(a, b) < 0 ? a : b }\n\n// Most of the external API clips given positions to make sure they\n// actually exist within the document.\nfunction clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}\nfunction clipPos(doc, pos) {\n  if (pos.line < doc.first) { return Pos(doc.first, 0) }\n  var last = doc.first + doc.size - 1\n  if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }\n  return clipToLen(pos, getLine(doc, pos.line).text.length)\n}\nfunction clipToLen(pos, linelen) {\n  var ch = pos.ch\n  if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }\n  else if (ch < 0) { return Pos(pos.line, 0) }\n  else { return pos }\n}\nfunction clipPosArray(doc, array) {\n  var out = []\n  for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }\n  return out\n}\n\n// Optimize some code when these features are not used.\nvar sawReadOnlySpans = false;\nvar sawCollapsedSpans = false;\nfunction seeReadOnlySpans() {\n  sawReadOnlySpans = true\n}\n\nfunction seeCollapsedSpans() {\n  sawCollapsedSpans = true\n}\n\n// TEXTMARKER SPANS\n\nfunction MarkedSpan(marker, from, to) {\n  this.marker = marker\n  this.from = from; this.to = to\n}\n\n// Search an array of spans for a span matching the given marker.\nfunction getMarkedSpanFor(spans, marker) {\n  if (spans) { for (var i = 0; i < spans.length; ++i) {\n    var span = spans[i]\n    if (span.marker == marker) { return span }\n  } }\n}\n// Remove a span from an array, returning undefined if no spans are\n// left (we don't store arrays for lines without spans).\nfunction removeMarkedSpan(spans, span) {\n  var r\n  for (var i = 0; i < spans.length; ++i)\n    { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }\n  return r\n}\n// Add a span to a line.\nfunction addMarkedSpan(line, span) {\n  line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]\n  span.marker.attachLine(line)\n}\n\n// Used for the algorithm that adjusts markers for a change in the\n// document. These functions cut an array of spans at a given\n// character position, returning an array of remaining chunks (or\n// undefined if nothing remains).\nfunction markedSpansBefore(old, startCh, isInsert) {\n  var nw\n  if (old) { for (var i = 0; i < old.length; ++i) {\n    var span = old[i], marker = span.marker\n    var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)\n    if (startsBefore || span.from == startCh && marker.type == \"bookmark\" && (!isInsert || !span.marker.insertLeft)) {\n      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)\n      ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))\n    }\n  } }\n  return nw\n}\nfunction markedSpansAfter(old, endCh, isInsert) {\n  var nw\n  if (old) { for (var i = 0; i < old.length; ++i) {\n    var span = old[i], marker = span.marker\n    var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)\n    if (endsAfter || span.from == endCh && marker.type == \"bookmark\" && (!isInsert || span.marker.insertLeft)) {\n      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)\n      ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,\n                                            span.to == null ? null : span.to - endCh))\n    }\n  } }\n  return nw\n}\n\n// Given a change object, compute the new set of marker spans that\n// cover the line in which the change took place. Removes spans\n// entirely within the change, reconnects spans belonging to the\n// same marker that appear on both sides of the change, and cuts off\n// spans partially within the change. Returns an array of span\n// arrays with one element for each line in (after) the change.\nfunction stretchSpansOverChange(doc, change) {\n  if (change.full) { return null }\n  var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans\n  var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans\n  if (!oldFirst && !oldLast) { return null }\n\n  var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0\n  // Get the spans that 'stick out' on both sides\n  var first = markedSpansBefore(oldFirst, startCh, isInsert)\n  var last = markedSpansAfter(oldLast, endCh, isInsert)\n\n  // Next, merge those two ends\n  var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)\n  if (first) {\n    // Fix up .to properties of first\n    for (var i = 0; i < first.length; ++i) {\n      var span = first[i]\n      if (span.to == null) {\n        var found = getMarkedSpanFor(last, span.marker)\n        if (!found) { span.to = startCh }\n        else if (sameLine) { span.to = found.to == null ? null : found.to + offset }\n      }\n    }\n  }\n  if (last) {\n    // Fix up .from in last (or move them into first in case of sameLine)\n    for (var i$1 = 0; i$1 < last.length; ++i$1) {\n      var span$1 = last[i$1]\n      if (span$1.to != null) { span$1.to += offset }\n      if (span$1.from == null) {\n        var found$1 = getMarkedSpanFor(first, span$1.marker)\n        if (!found$1) {\n          span$1.from = offset\n          if (sameLine) { (first || (first = [])).push(span$1) }\n        }\n      } else {\n        span$1.from += offset\n        if (sameLine) { (first || (first = [])).push(span$1) }\n      }\n    }\n  }\n  // Make sure we didn't create any zero-length spans\n  if (first) { first = clearEmptySpans(first) }\n  if (last && last != first) { last = clearEmptySpans(last) }\n\n  var newMarkers = [first]\n  if (!sameLine) {\n    // Fill gap with whole-line-spans\n    var gap = change.text.length - 2, gapMarkers\n    if (gap > 0 && first)\n      { for (var i$2 = 0; i$2 < first.length; ++i$2)\n        { if (first[i$2].to == null)\n          { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }\n    for (var i$3 = 0; i$3 < gap; ++i$3)\n      { newMarkers.push(gapMarkers) }\n    newMarkers.push(last)\n  }\n  return newMarkers\n}\n\n// Remove spans that are empty and don't have a clearWhenEmpty\n// option of false.\nfunction clearEmptySpans(spans) {\n  for (var i = 0; i < spans.length; ++i) {\n    var span = spans[i]\n    if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)\n      { spans.splice(i--, 1) }\n  }\n  if (!spans.length) { return null }\n  return spans\n}\n\n// Used to 'clip' out readOnly ranges when making a change.\nfunction removeReadOnlyRanges(doc, from, to) {\n  var markers = null\n  doc.iter(from.line, to.line + 1, function (line) {\n    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n      var mark = line.markedSpans[i].marker\n      if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))\n        { (markers || (markers = [])).push(mark) }\n    } }\n  })\n  if (!markers) { return null }\n  var parts = [{from: from, to: to}]\n  for (var i = 0; i < markers.length; ++i) {\n    var mk = markers[i], m = mk.find(0)\n    for (var j = 0; j < parts.length; ++j) {\n      var p = parts[j]\n      if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }\n      var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)\n      if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)\n        { newParts.push({from: p.from, to: m.from}) }\n      if (dto > 0 || !mk.inclusiveRight && !dto)\n        { newParts.push({from: m.to, to: p.to}) }\n      parts.splice.apply(parts, newParts)\n      j += newParts.length - 3\n    }\n  }\n  return parts\n}\n\n// Connect or disconnect spans from a line.\nfunction detachMarkedSpans(line) {\n  var spans = line.markedSpans\n  if (!spans) { return }\n  for (var i = 0; i < spans.length; ++i)\n    { spans[i].marker.detachLine(line) }\n  line.markedSpans = null\n}\nfunction attachMarkedSpans(line, spans) {\n  if (!spans) { return }\n  for (var i = 0; i < spans.length; ++i)\n    { spans[i].marker.attachLine(line) }\n  line.markedSpans = spans\n}\n\n// Helpers used when computing which overlapping collapsed span\n// counts as the larger one.\nfunction extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }\nfunction extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }\n\n// Returns a number indicating which of two overlapping collapsed\n// spans is larger (and thus includes the other). Falls back to\n// comparing ids when the spans cover exactly the same range.\nfunction compareCollapsedMarkers(a, b) {\n  var lenDiff = a.lines.length - b.lines.length\n  if (lenDiff != 0) { return lenDiff }\n  var aPos = a.find(), bPos = b.find()\n  var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)\n  if (fromCmp) { return -fromCmp }\n  var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)\n  if (toCmp) { return toCmp }\n  return b.id - a.id\n}\n\n// Find out whether a line ends or starts in a collapsed span. If\n// so, return the marker for that span.\nfunction collapsedSpanAtSide(line, start) {\n  var sps = sawCollapsedSpans && line.markedSpans, found\n  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n    sp = sps[i]\n    if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&\n        (!found || compareCollapsedMarkers(found, sp.marker) < 0))\n      { found = sp.marker }\n  } }\n  return found\n}\nfunction collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }\nfunction collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }\n\nfunction collapsedSpanAround(line, ch) {\n  var sps = sawCollapsedSpans && line.markedSpans, found\n  if (sps) { for (var i = 0; i < sps.length; ++i) {\n    var sp = sps[i]\n    if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&\n        (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker }\n  } }\n  return found\n}\n\n// Test whether there exists a collapsed span that partially\n// overlaps (covers the start or end, but not both) of a new span.\n// Such overlap is not allowed.\nfunction conflictingCollapsedRange(doc, lineNo, from, to, marker) {\n  var line = getLine(doc, lineNo)\n  var sps = sawCollapsedSpans && line.markedSpans\n  if (sps) { for (var i = 0; i < sps.length; ++i) {\n    var sp = sps[i]\n    if (!sp.marker.collapsed) { continue }\n    var found = sp.marker.find(0)\n    var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)\n    var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)\n    if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }\n    if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||\n        fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))\n      { return true }\n  } }\n}\n\n// A visual line is a line as drawn on the screen. Folding, for\n// example, can cause multiple logical lines to appear on the same\n// visual line. This finds the start of the visual line that the\n// given line is part of (usually that is the line itself).\nfunction visualLine(line) {\n  var merged\n  while (merged = collapsedSpanAtStart(line))\n    { line = merged.find(-1, true).line }\n  return line\n}\n\nfunction visualLineEnd(line) {\n  var merged\n  while (merged = collapsedSpanAtEnd(line))\n    { line = merged.find(1, true).line }\n  return line\n}\n\n// Returns an array of logical lines that continue the visual line\n// started by the argument, or undefined if there are no such lines.\nfunction visualLineContinued(line) {\n  var merged, lines\n  while (merged = collapsedSpanAtEnd(line)) {\n    line = merged.find(1, true).line\n    ;(lines || (lines = [])).push(line)\n  }\n  return lines\n}\n\n// Get the line number of the start of the visual line that the\n// given line number is part of.\nfunction visualLineNo(doc, lineN) {\n  var line = getLine(doc, lineN), vis = visualLine(line)\n  if (line == vis) { return lineN }\n  return lineNo(vis)\n}\n\n// Get the line number of the start of the next visual line after\n// the given line.\nfunction visualLineEndNo(doc, lineN) {\n  if (lineN > doc.lastLine()) { return lineN }\n  var line = getLine(doc, lineN), merged\n  if (!lineIsHidden(doc, line)) { return lineN }\n  while (merged = collapsedSpanAtEnd(line))\n    { line = merged.find(1, true).line }\n  return lineNo(line) + 1\n}\n\n// Compute whether a line is hidden. Lines count as hidden when they\n// are part of a visual line that starts with another line, or when\n// they are entirely covered by collapsed, non-widget span.\nfunction lineIsHidden(doc, line) {\n  var sps = sawCollapsedSpans && line.markedSpans\n  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n    sp = sps[i]\n    if (!sp.marker.collapsed) { continue }\n    if (sp.from == null) { return true }\n    if (sp.marker.widgetNode) { continue }\n    if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))\n      { return true }\n  } }\n}\nfunction lineIsHiddenInner(doc, line, span) {\n  if (span.to == null) {\n    var end = span.marker.find(1, true)\n    return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))\n  }\n  if (span.marker.inclusiveRight && span.to == line.text.length)\n    { return true }\n  for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {\n    sp = line.markedSpans[i]\n    if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&\n        (sp.to == null || sp.to != span.from) &&\n        (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&\n        lineIsHiddenInner(doc, line, sp)) { return true }\n  }\n}\n\n// Find the height above the given line.\nfunction heightAtLine(lineObj) {\n  lineObj = visualLine(lineObj)\n\n  var h = 0, chunk = lineObj.parent\n  for (var i = 0; i < chunk.lines.length; ++i) {\n    var line = chunk.lines[i]\n    if (line == lineObj) { break }\n    else { h += line.height }\n  }\n  for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {\n    for (var i$1 = 0; i$1 < p.children.length; ++i$1) {\n      var cur = p.children[i$1]\n      if (cur == chunk) { break }\n      else { h += cur.height }\n    }\n  }\n  return h\n}\n\n// Compute the character length of a line, taking into account\n// collapsed ranges (see markText) that might hide parts, and join\n// other lines onto it.\nfunction lineLength(line) {\n  if (line.height == 0) { return 0 }\n  var len = line.text.length, merged, cur = line\n  while (merged = collapsedSpanAtStart(cur)) {\n    var found = merged.find(0, true)\n    cur = found.from.line\n    len += found.from.ch - found.to.ch\n  }\n  cur = line\n  while (merged = collapsedSpanAtEnd(cur)) {\n    var found$1 = merged.find(0, true)\n    len -= cur.text.length - found$1.from.ch\n    cur = found$1.to.line\n    len += cur.text.length - found$1.to.ch\n  }\n  return len\n}\n\n// Find the longest line in the document.\nfunction findMaxLine(cm) {\n  var d = cm.display, doc = cm.doc\n  d.maxLine = getLine(doc, doc.first)\n  d.maxLineLength = lineLength(d.maxLine)\n  d.maxLineChanged = true\n  doc.iter(function (line) {\n    var len = lineLength(line)\n    if (len > d.maxLineLength) {\n      d.maxLineLength = len\n      d.maxLine = line\n    }\n  })\n}\n\n// BIDI HELPERS\n\nfunction iterateBidiSections(order, from, to, f) {\n  if (!order) { return f(from, to, \"ltr\", 0) }\n  var found = false\n  for (var i = 0; i < order.length; ++i) {\n    var part = order[i]\n    if (part.from < to && part.to > from || from == to && part.to == from) {\n      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? \"rtl\" : \"ltr\", i)\n      found = true\n    }\n  }\n  if (!found) { f(from, to, \"ltr\") }\n}\n\nvar bidiOther = null\nfunction getBidiPartAt(order, ch, sticky) {\n  var found\n  bidiOther = null\n  for (var i = 0; i < order.length; ++i) {\n    var cur = order[i]\n    if (cur.from < ch && cur.to > ch) { return i }\n    if (cur.to == ch) {\n      if (cur.from != cur.to && sticky == \"before\") { found = i }\n      else { bidiOther = i }\n    }\n    if (cur.from == ch) {\n      if (cur.from != cur.to && sticky != \"before\") { found = i }\n      else { bidiOther = i }\n    }\n  }\n  return found != null ? found : bidiOther\n}\n\n// Bidirectional ordering algorithm\n// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm\n// that this (partially) implements.\n\n// One-char codes used for character types:\n// L (L):   Left-to-Right\n// R (R):   Right-to-Left\n// r (AL):  Right-to-Left Arabic\n// 1 (EN):  European Number\n// + (ES):  European Number Separator\n// % (ET):  European Number Terminator\n// n (AN):  Arabic Number\n// , (CS):  Common Number Separator\n// m (NSM): Non-Spacing Mark\n// b (BN):  Boundary Neutral\n// s (B):   Paragraph Separator\n// t (S):   Segment Separator\n// w (WS):  Whitespace\n// N (ON):  Other Neutrals\n\n// Returns null if characters are ordered as they appear\n// (left-to-right), or an array of sections ({from, to, level}\n// objects) in the order in which they occur visually.\nvar bidiOrdering = (function() {\n  // Character types for codepoints 0 to 0xff\n  var lowTypes = \"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\"\n  // Character types for codepoints 0x600 to 0x6f9\n  var arabicTypes = \"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111\"\n  function charType(code) {\n    if (code <= 0xf7) { return lowTypes.charAt(code) }\n    else if (0x590 <= code && code <= 0x5f4) { return \"R\" }\n    else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }\n    else if (0x6ee <= code && code <= 0x8ac) { return \"r\" }\n    else if (0x2000 <= code && code <= 0x200b) { return \"w\" }\n    else if (code == 0x200c) { return \"b\" }\n    else { return \"L\" }\n  }\n\n  var bidiRE = /[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/\n  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/\n\n  function BidiSpan(level, from, to) {\n    this.level = level\n    this.from = from; this.to = to\n  }\n\n  return function(str, direction) {\n    var outerType = direction == \"ltr\" ? \"L\" : \"R\"\n\n    if (str.length == 0 || direction == \"ltr\" && !bidiRE.test(str)) { return false }\n    var len = str.length, types = []\n    for (var i = 0; i < len; ++i)\n      { types.push(charType(str.charCodeAt(i))) }\n\n    // W1. Examine each non-spacing mark (NSM) in the level run, and\n    // change the type of the NSM to the type of the previous\n    // character. If the NSM is at the start of the level run, it will\n    // get the type of sor.\n    for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {\n      var type = types[i$1]\n      if (type == \"m\") { types[i$1] = prev }\n      else { prev = type }\n    }\n\n    // W2. Search backwards from each instance of a European number\n    // until the first strong type (R, L, AL, or sor) is found. If an\n    // AL is found, change the type of the European number to Arabic\n    // number.\n    // W3. Change all ALs to R.\n    for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {\n      var type$1 = types[i$2]\n      if (type$1 == \"1\" && cur == \"r\") { types[i$2] = \"n\" }\n      else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == \"r\") { types[i$2] = \"R\" } }\n    }\n\n    // W4. A single European separator between two European numbers\n    // changes to a European number. A single common separator between\n    // two numbers of the same type changes to that type.\n    for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {\n      var type$2 = types[i$3]\n      if (type$2 == \"+\" && prev$1 == \"1\" && types[i$3+1] == \"1\") { types[i$3] = \"1\" }\n      else if (type$2 == \",\" && prev$1 == types[i$3+1] &&\n               (prev$1 == \"1\" || prev$1 == \"n\")) { types[i$3] = prev$1 }\n      prev$1 = type$2\n    }\n\n    // W5. A sequence of European terminators adjacent to European\n    // numbers changes to all European numbers.\n    // W6. Otherwise, separators and terminators change to Other\n    // Neutral.\n    for (var i$4 = 0; i$4 < len; ++i$4) {\n      var type$3 = types[i$4]\n      if (type$3 == \",\") { types[i$4] = \"N\" }\n      else if (type$3 == \"%\") {\n        var end = (void 0)\n        for (end = i$4 + 1; end < len && types[end] == \"%\"; ++end) {}\n        var replace = (i$4 && types[i$4-1] == \"!\") || (end < len && types[end] == \"1\") ? \"1\" : \"N\"\n        for (var j = i$4; j < end; ++j) { types[j] = replace }\n        i$4 = end - 1\n      }\n    }\n\n    // W7. Search backwards from each instance of a European number\n    // until the first strong type (R, L, or sor) is found. If an L is\n    // found, then change the type of the European number to L.\n    for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {\n      var type$4 = types[i$5]\n      if (cur$1 == \"L\" && type$4 == \"1\") { types[i$5] = \"L\" }\n      else if (isStrong.test(type$4)) { cur$1 = type$4 }\n    }\n\n    // N1. A sequence of neutrals takes the direction of the\n    // surrounding strong text if the text on both sides has the same\n    // direction. European and Arabic numbers act as if they were R in\n    // terms of their influence on neutrals. Start-of-level-run (sor)\n    // and end-of-level-run (eor) are used at level run boundaries.\n    // N2. Any remaining neutrals take the embedding direction.\n    for (var i$6 = 0; i$6 < len; ++i$6) {\n      if (isNeutral.test(types[i$6])) {\n        var end$1 = (void 0)\n        for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}\n        var before = (i$6 ? types[i$6-1] : outerType) == \"L\"\n        var after = (end$1 < len ? types[end$1] : outerType) == \"L\"\n        var replace$1 = before == after ? (before ? \"L\" : \"R\") : outerType\n        for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }\n        i$6 = end$1 - 1\n      }\n    }\n\n    // Here we depart from the documented algorithm, in order to avoid\n    // building up an actual levels array. Since there are only three\n    // levels (0, 1, 2) in an implementation that doesn't take\n    // explicit embedding into account, we can build up the order on\n    // the fly, without following the level-based algorithm.\n    var order = [], m\n    for (var i$7 = 0; i$7 < len;) {\n      if (countsAsLeft.test(types[i$7])) {\n        var start = i$7\n        for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}\n        order.push(new BidiSpan(0, start, i$7))\n      } else {\n        var pos = i$7, at = order.length\n        for (++i$7; i$7 < len && types[i$7] != \"L\"; ++i$7) {}\n        for (var j$2 = pos; j$2 < i$7;) {\n          if (countsAsNum.test(types[j$2])) {\n            if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }\n            var nstart = j$2\n            for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}\n            order.splice(at, 0, new BidiSpan(2, nstart, j$2))\n            pos = j$2\n          } else { ++j$2 }\n        }\n        if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }\n      }\n    }\n    if (direction == \"ltr\") {\n      if (order[0].level == 1 && (m = str.match(/^\\s+/))) {\n        order[0].from = m[0].length\n        order.unshift(new BidiSpan(0, 0, m[0].length))\n      }\n      if (lst(order).level == 1 && (m = str.match(/\\s+$/))) {\n        lst(order).to -= m[0].length\n        order.push(new BidiSpan(0, len - m[0].length, len))\n      }\n    }\n\n    return direction == \"rtl\" ? order.reverse() : order\n  }\n})()\n\n// Get the bidi ordering for the given line (and cache it). Returns\n// false for lines that are fully left-to-right, and an array of\n// BidiSpan objects otherwise.\nfunction getOrder(line, direction) {\n  var order = line.order\n  if (order == null) { order = line.order = bidiOrdering(line.text, direction) }\n  return order\n}\n\n// EVENT HANDLING\n\n// Lightweight event framework. on/off also work on DOM nodes,\n// registering native DOM handlers.\n\nvar noHandlers = []\n\nvar on = function(emitter, type, f) {\n  if (emitter.addEventListener) {\n    emitter.addEventListener(type, f, false)\n  } else if (emitter.attachEvent) {\n    emitter.attachEvent(\"on\" + type, f)\n  } else {\n    var map = emitter._handlers || (emitter._handlers = {})\n    map[type] = (map[type] || noHandlers).concat(f)\n  }\n}\n\nfunction getHandlers(emitter, type) {\n  return emitter._handlers && emitter._handlers[type] || noHandlers\n}\n\nfunction off(emitter, type, f) {\n  if (emitter.removeEventListener) {\n    emitter.removeEventListener(type, f, false)\n  } else if (emitter.detachEvent) {\n    emitter.detachEvent(\"on\" + type, f)\n  } else {\n    var map = emitter._handlers, arr = map && map[type]\n    if (arr) {\n      var index = indexOf(arr, f)\n      if (index > -1)\n        { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }\n    }\n  }\n}\n\nfunction signal(emitter, type /*, values...*/) {\n  var handlers = getHandlers(emitter, type)\n  if (!handlers.length) { return }\n  var args = Array.prototype.slice.call(arguments, 2)\n  for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }\n}\n\n// The DOM events that CodeMirror handles can be overridden by\n// registering a (non-DOM) handler on the editor for the event name,\n// and preventDefault-ing the event in that handler.\nfunction signalDOMEvent(cm, e, override) {\n  if (typeof e == \"string\")\n    { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }\n  signal(cm, override || e.type, cm, e)\n  return e_defaultPrevented(e) || e.codemirrorIgnore\n}\n\nfunction signalCursorActivity(cm) {\n  var arr = cm._handlers && cm._handlers.cursorActivity\n  if (!arr) { return }\n  var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])\n  for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)\n    { set.push(arr[i]) } }\n}\n\nfunction hasHandler(emitter, type) {\n  return getHandlers(emitter, type).length > 0\n}\n\n// Add on and off methods to a constructor's prototype, to make\n// registering events on such objects more convenient.\nfunction eventMixin(ctor) {\n  ctor.prototype.on = function(type, f) {on(this, type, f)}\n  ctor.prototype.off = function(type, f) {off(this, type, f)}\n}\n\n// Due to the fact that we still support jurassic IE versions, some\n// compatibility wrappers are needed.\n\nfunction e_preventDefault(e) {\n  if (e.preventDefault) { e.preventDefault() }\n  else { e.returnValue = false }\n}\nfunction e_stopPropagation(e) {\n  if (e.stopPropagation) { e.stopPropagation() }\n  else { e.cancelBubble = true }\n}\nfunction e_defaultPrevented(e) {\n  return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false\n}\nfunction e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}\n\nfunction e_target(e) {return e.target || e.srcElement}\nfunction e_button(e) {\n  var b = e.which\n  if (b == null) {\n    if (e.button & 1) { b = 1 }\n    else if (e.button & 2) { b = 3 }\n    else if (e.button & 4) { b = 2 }\n  }\n  if (mac && e.ctrlKey && b == 1) { b = 3 }\n  return b\n}\n\n// Detect drag-and-drop\nvar dragAndDrop = function() {\n  // There is *some* kind of drag-and-drop support in IE6-8, but I\n  // couldn't get it to work yet.\n  if (ie && ie_version < 9) { return false }\n  var div = elt('div')\n  return \"draggable\" in div || \"dragDrop\" in div\n}()\n\nvar zwspSupported\nfunction zeroWidthElement(measure) {\n  if (zwspSupported == null) {\n    var test = elt(\"span\", \"\\u200b\")\n    removeChildrenAndAdd(measure, elt(\"span\", [test, document.createTextNode(\"x\")]))\n    if (measure.firstChild.offsetHeight != 0)\n      { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }\n  }\n  var node = zwspSupported ? elt(\"span\", \"\\u200b\") :\n    elt(\"span\", \"\\u00a0\", null, \"display: inline-block; width: 1px; margin-right: -1px\")\n  node.setAttribute(\"cm-text\", \"\")\n  return node\n}\n\n// Feature-detect IE's crummy client rect reporting for bidi text\nvar badBidiRects\nfunction hasBadBidiRects(measure) {\n  if (badBidiRects != null) { return badBidiRects }\n  var txt = removeChildrenAndAdd(measure, document.createTextNode(\"A\\u062eA\"))\n  var r0 = range(txt, 0, 1).getBoundingClientRect()\n  var r1 = range(txt, 1, 2).getBoundingClientRect()\n  removeChildren(measure)\n  if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)\n  return badBidiRects = (r1.right - r0.right < 3)\n}\n\n// See if \"\".split is the broken IE version, if so, provide an\n// alternative way to split lines.\nvar splitLinesAuto = \"\\n\\nb\".split(/\\n/).length != 3 ? function (string) {\n  var pos = 0, result = [], l = string.length\n  while (pos <= l) {\n    var nl = string.indexOf(\"\\n\", pos)\n    if (nl == -1) { nl = string.length }\n    var line = string.slice(pos, string.charAt(nl - 1) == \"\\r\" ? nl - 1 : nl)\n    var rt = line.indexOf(\"\\r\")\n    if (rt != -1) {\n      result.push(line.slice(0, rt))\n      pos += rt + 1\n    } else {\n      result.push(line)\n      pos = nl + 1\n    }\n  }\n  return result\n} : function (string) { return string.split(/\\r\\n?|\\n/); }\n\nvar hasSelection = window.getSelection ? function (te) {\n  try { return te.selectionStart != te.selectionEnd }\n  catch(e) { return false }\n} : function (te) {\n  var range\n  try {range = te.ownerDocument.selection.createRange()}\n  catch(e) {}\n  if (!range || range.parentElement() != te) { return false }\n  return range.compareEndPoints(\"StartToEnd\", range) != 0\n}\n\nvar hasCopyEvent = (function () {\n  var e = elt(\"div\")\n  if (\"oncopy\" in e) { return true }\n  e.setAttribute(\"oncopy\", \"return;\")\n  return typeof e.oncopy == \"function\"\n})()\n\nvar badZoomedRects = null\nfunction hasBadZoomedRects(measure) {\n  if (badZoomedRects != null) { return badZoomedRects }\n  var node = removeChildrenAndAdd(measure, elt(\"span\", \"x\"))\n  var normal = node.getBoundingClientRect()\n  var fromRange = range(node, 0, 1).getBoundingClientRect()\n  return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1\n}\n\nvar modes = {};\nvar mimeModes = {};\n// Extra arguments are stored as the mode's dependencies, which is\n// used by (legacy) mechanisms like loadmode.js to automatically\n// load a mode. (Preferred mechanism is the require/define calls.)\nfunction defineMode(name, mode) {\n  if (arguments.length > 2)\n    { mode.dependencies = Array.prototype.slice.call(arguments, 2) }\n  modes[name] = mode\n}\n\nfunction defineMIME(mime, spec) {\n  mimeModes[mime] = spec\n}\n\n// Given a MIME type, a {name, ...options} config object, or a name\n// string, return a mode config object.\nfunction resolveMode(spec) {\n  if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n    spec = mimeModes[spec]\n  } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n    var found = mimeModes[spec.name]\n    if (typeof found == \"string\") { found = {name: found} }\n    spec = createObj(found, spec)\n    spec.name = found.name\n  } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n    return resolveMode(\"application/xml\")\n  } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n    return resolveMode(\"application/json\")\n  }\n  if (typeof spec == \"string\") { return {name: spec} }\n  else { return spec || {name: \"null\"} }\n}\n\n// Given a mode spec (anything that resolveMode accepts), find and\n// initialize an actual mode object.\nfunction getMode(options, spec) {\n  spec = resolveMode(spec)\n  var mfactory = modes[spec.name]\n  if (!mfactory) { return getMode(options, \"text/plain\") }\n  var modeObj = mfactory(options, spec)\n  if (modeExtensions.hasOwnProperty(spec.name)) {\n    var exts = modeExtensions[spec.name]\n    for (var prop in exts) {\n      if (!exts.hasOwnProperty(prop)) { continue }\n      if (modeObj.hasOwnProperty(prop)) { modeObj[\"_\" + prop] = modeObj[prop] }\n      modeObj[prop] = exts[prop]\n    }\n  }\n  modeObj.name = spec.name\n  if (spec.helperType) { modeObj.helperType = spec.helperType }\n  if (spec.modeProps) { for (var prop$1 in spec.modeProps)\n    { modeObj[prop$1] = spec.modeProps[prop$1] } }\n\n  return modeObj\n}\n\n// This can be used to attach properties to mode objects from\n// outside the actual mode definition.\nvar modeExtensions = {}\nfunction extendMode(mode, properties) {\n  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})\n  copyObj(properties, exts)\n}\n\nfunction copyState(mode, state) {\n  if (state === true) { return state }\n  if (mode.copyState) { return mode.copyState(state) }\n  var nstate = {}\n  for (var n in state) {\n    var val = state[n]\n    if (val instanceof Array) { val = val.concat([]) }\n    nstate[n] = val\n  }\n  return nstate\n}\n\n// Given a mode and a state (for that mode), find the inner mode and\n// state at the position that the state refers to.\nfunction innerMode(mode, state) {\n  var info\n  while (mode.innerMode) {\n    info = mode.innerMode(state)\n    if (!info || info.mode == mode) { break }\n    state = info.state\n    mode = info.mode\n  }\n  return info || {mode: mode, state: state}\n}\n\nfunction startState(mode, a1, a2) {\n  return mode.startState ? mode.startState(a1, a2) : true\n}\n\n// STRING STREAM\n\n// Fed to the mode parsers, provides helper functions to make\n// parsers more succinct.\n\nvar StringStream = function(string, tabSize, lineOracle) {\n  this.pos = this.start = 0\n  this.string = string\n  this.tabSize = tabSize || 8\n  this.lastColumnPos = this.lastColumnValue = 0\n  this.lineStart = 0\n  this.lineOracle = lineOracle\n};\n\nStringStream.prototype.eol = function () {return this.pos >= this.string.length};\nStringStream.prototype.sol = function () {return this.pos == this.lineStart};\nStringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};\nStringStream.prototype.next = function () {\n  if (this.pos < this.string.length)\n    { return this.string.charAt(this.pos++) }\n};\nStringStream.prototype.eat = function (match) {\n  var ch = this.string.charAt(this.pos)\n  var ok\n  if (typeof match == \"string\") { ok = ch == match }\n  else { ok = ch && (match.test ? match.test(ch) : match(ch)) }\n  if (ok) {++this.pos; return ch}\n};\nStringStream.prototype.eatWhile = function (match) {\n  var start = this.pos\n  while (this.eat(match)){}\n  return this.pos > start\n};\nStringStream.prototype.eatSpace = function () {\n    var this$1 = this;\n\n  var start = this.pos\n  while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }\n  return this.pos > start\n};\nStringStream.prototype.skipToEnd = function () {this.pos = this.string.length};\nStringStream.prototype.skipTo = function (ch) {\n  var found = this.string.indexOf(ch, this.pos)\n  if (found > -1) {this.pos = found; return true}\n};\nStringStream.prototype.backUp = function (n) {this.pos -= n};\nStringStream.prototype.column = function () {\n  if (this.lastColumnPos < this.start) {\n    this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)\n    this.lastColumnPos = this.start\n  }\n  return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n};\nStringStream.prototype.indentation = function () {\n  return countColumn(this.string, null, this.tabSize) -\n    (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n};\nStringStream.prototype.match = function (pattern, consume, caseInsensitive) {\n  if (typeof pattern == \"string\") {\n    var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }\n    var substr = this.string.substr(this.pos, pattern.length)\n    if (cased(substr) == cased(pattern)) {\n      if (consume !== false) { this.pos += pattern.length }\n      return true\n    }\n  } else {\n    var match = this.string.slice(this.pos).match(pattern)\n    if (match && match.index > 0) { return null }\n    if (match && consume !== false) { this.pos += match[0].length }\n    return match\n  }\n};\nStringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};\nStringStream.prototype.hideFirstChars = function (n, inner) {\n  this.lineStart += n\n  try { return inner() }\n  finally { this.lineStart -= n }\n};\nStringStream.prototype.lookAhead = function (n) {\n  var oracle = this.lineOracle\n  return oracle && oracle.lookAhead(n)\n};\nStringStream.prototype.baseToken = function () {\n  var oracle = this.lineOracle\n  return oracle && oracle.baseToken(this.pos)\n};\n\nvar SavedContext = function(state, lookAhead) {\n  this.state = state\n  this.lookAhead = lookAhead\n};\n\nvar Context = function(doc, state, line, lookAhead) {\n  this.state = state\n  this.doc = doc\n  this.line = line\n  this.maxLookAhead = lookAhead || 0\n  this.baseTokens = null\n  this.baseTokenPos = 1\n};\n\nContext.prototype.lookAhead = function (n) {\n  var line = this.doc.getLine(this.line + n)\n  if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n }\n  return line\n};\n\nContext.prototype.baseToken = function (n) {\n    var this$1 = this;\n\n  if (!this.baseTokens) { return null }\n  while (this.baseTokens[this.baseTokenPos] <= n)\n    { this$1.baseTokenPos += 2 }\n  var type = this.baseTokens[this.baseTokenPos + 1]\n  return {type: type && type.replace(/( |^)overlay .*/, \"\"),\n          size: this.baseTokens[this.baseTokenPos] - n}\n};\n\nContext.prototype.nextLine = function () {\n  this.line++\n  if (this.maxLookAhead > 0) { this.maxLookAhead-- }\n};\n\nContext.fromSaved = function (doc, saved, line) {\n  if (saved instanceof SavedContext)\n    { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }\n  else\n    { return new Context(doc, copyState(doc.mode, saved), line) }\n};\n\nContext.prototype.save = function (copy) {\n  var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state\n  return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state\n};\n\n\n// Compute a style array (an array starting with a mode generation\n// -- for invalidation -- followed by pairs of end positions and\n// style strings), which is used to highlight the tokens on the\n// line.\nfunction highlightLine(cm, line, context, forceToEnd) {\n  // A styles array always starts with a number identifying the\n  // mode/overlays that it is based on (for easy invalidation).\n  var st = [cm.state.modeGen], lineClasses = {}\n  // Compute the base array of styles\n  runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },\n          lineClasses, forceToEnd)\n  var state = context.state\n\n  // Run overlays, adjust style array.\n  var loop = function ( o ) {\n    context.baseTokens = st\n    var overlay = cm.state.overlays[o], i = 1, at = 0\n    context.state = true\n    runMode(cm, line.text, overlay.mode, context, function (end, style) {\n      var start = i\n      // Ensure there's a token end at the current position, and that i points at it\n      while (at < end) {\n        var i_end = st[i]\n        if (i_end > end)\n          { st.splice(i, 1, end, st[i+1], i_end) }\n        i += 2\n        at = Math.min(end, i_end)\n      }\n      if (!style) { return }\n      if (overlay.opaque) {\n        st.splice(start, i - start, end, \"overlay \" + style)\n        i = start + 2\n      } else {\n        for (; start < i; start += 2) {\n          var cur = st[start+1]\n          st[start+1] = (cur ? cur + \" \" : \"\") + \"overlay \" + style\n        }\n      }\n    }, lineClasses)\n    context.state = state\n    context.baseTokens = null\n    context.baseTokenPos = 1\n  };\n\n  for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );\n\n  return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}\n}\n\nfunction getLineStyles(cm, line, updateFrontier) {\n  if (!line.styles || line.styles[0] != cm.state.modeGen) {\n    var context = getContextBefore(cm, lineNo(line))\n    var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state)\n    var result = highlightLine(cm, line, context)\n    if (resetState) { context.state = resetState }\n    line.stateAfter = context.save(!resetState)\n    line.styles = result.styles\n    if (result.classes) { line.styleClasses = result.classes }\n    else if (line.styleClasses) { line.styleClasses = null }\n    if (updateFrontier === cm.doc.highlightFrontier)\n      { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) }\n  }\n  return line.styles\n}\n\nfunction getContextBefore(cm, n, precise) {\n  var doc = cm.doc, display = cm.display\n  if (!doc.mode.startState) { return new Context(doc, true, n) }\n  var start = findStartLine(cm, n, precise)\n  var saved = start > doc.first && getLine(doc, start - 1).stateAfter\n  var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start)\n\n  doc.iter(start, n, function (line) {\n    processLine(cm, line.text, context)\n    var pos = context.line\n    line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null\n    context.nextLine()\n  })\n  if (precise) { doc.modeFrontier = context.line }\n  return context\n}\n\n// Lightweight form of highlight -- proceed over this line and\n// update state, but don't save a style array. Used for lines that\n// aren't currently visible.\nfunction processLine(cm, text, context, startAt) {\n  var mode = cm.doc.mode\n  var stream = new StringStream(text, cm.options.tabSize, context)\n  stream.start = stream.pos = startAt || 0\n  if (text == \"\") { callBlankLine(mode, context.state) }\n  while (!stream.eol()) {\n    readToken(mode, stream, context.state)\n    stream.start = stream.pos\n  }\n}\n\nfunction callBlankLine(mode, state) {\n  if (mode.blankLine) { return mode.blankLine(state) }\n  if (!mode.innerMode) { return }\n  var inner = innerMode(mode, state)\n  if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }\n}\n\nfunction readToken(mode, stream, state, inner) {\n  for (var i = 0; i < 10; i++) {\n    if (inner) { inner[0] = innerMode(mode, state).mode }\n    var style = mode.token(stream, state)\n    if (stream.pos > stream.start) { return style }\n  }\n  throw new Error(\"Mode \" + mode.name + \" failed to advance stream.\")\n}\n\nvar Token = function(stream, type, state) {\n  this.start = stream.start; this.end = stream.pos\n  this.string = stream.current()\n  this.type = type || null\n  this.state = state\n};\n\n// Utility for getTokenAt and getLineTokens\nfunction takeToken(cm, pos, precise, asArray) {\n  var doc = cm.doc, mode = doc.mode, style\n  pos = clipPos(doc, pos)\n  var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise)\n  var stream = new StringStream(line.text, cm.options.tabSize, context), tokens\n  if (asArray) { tokens = [] }\n  while ((asArray || stream.pos < pos.ch) && !stream.eol()) {\n    stream.start = stream.pos\n    style = readToken(mode, stream, context.state)\n    if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) }\n  }\n  return asArray ? tokens : new Token(stream, style, context.state)\n}\n\nfunction extractLineClasses(type, output) {\n  if (type) { for (;;) {\n    var lineClass = type.match(/(?:^|\\s+)line-(background-)?(\\S+)/)\n    if (!lineClass) { break }\n    type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)\n    var prop = lineClass[1] ? \"bgClass\" : \"textClass\"\n    if (output[prop] == null)\n      { output[prop] = lineClass[2] }\n    else if (!(new RegExp(\"(?:^|\\s)\" + lineClass[2] + \"(?:$|\\s)\")).test(output[prop]))\n      { output[prop] += \" \" + lineClass[2] }\n  } }\n  return type\n}\n\n// Run the given mode's parser over a line, calling f for each token.\nfunction runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {\n  var flattenSpans = mode.flattenSpans\n  if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }\n  var curStart = 0, curStyle = null\n  var stream = new StringStream(text, cm.options.tabSize, context), style\n  var inner = cm.options.addModeClass && [null]\n  if (text == \"\") { extractLineClasses(callBlankLine(mode, context.state), lineClasses) }\n  while (!stream.eol()) {\n    if (stream.pos > cm.options.maxHighlightLength) {\n      flattenSpans = false\n      if (forceToEnd) { processLine(cm, text, context, stream.pos) }\n      stream.pos = text.length\n      style = null\n    } else {\n      style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses)\n    }\n    if (inner) {\n      var mName = inner[0].name\n      if (mName) { style = \"m-\" + (style ? mName + \" \" + style : mName) }\n    }\n    if (!flattenSpans || curStyle != style) {\n      while (curStart < stream.start) {\n        curStart = Math.min(stream.start, curStart + 5000)\n        f(curStart, curStyle)\n      }\n      curStyle = style\n    }\n    stream.start = stream.pos\n  }\n  while (curStart < stream.pos) {\n    // Webkit seems to refuse to render text nodes longer than 57444\n    // characters, and returns inaccurate measurements in nodes\n    // starting around 5000 chars.\n    var pos = Math.min(stream.pos, curStart + 5000)\n    f(pos, curStyle)\n    curStart = pos\n  }\n}\n\n// Finds the line to start with when starting a parse. Tries to\n// find a line with a stateAfter, so that it can start with a\n// valid state. If that fails, it returns the line with the\n// smallest indentation, which tends to need the least context to\n// parse correctly.\nfunction findStartLine(cm, n, precise) {\n  var minindent, minline, doc = cm.doc\n  var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)\n  for (var search = n; search > lim; --search) {\n    if (search <= doc.first) { return doc.first }\n    var line = getLine(doc, search - 1), after = line.stateAfter\n    if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))\n      { return search }\n    var indented = countColumn(line.text, null, cm.options.tabSize)\n    if (minline == null || minindent > indented) {\n      minline = search - 1\n      minindent = indented\n    }\n  }\n  return minline\n}\n\nfunction retreatFrontier(doc, n) {\n  doc.modeFrontier = Math.min(doc.modeFrontier, n)\n  if (doc.highlightFrontier < n - 10) { return }\n  var start = doc.first\n  for (var line = n - 1; line > start; line--) {\n    var saved = getLine(doc, line).stateAfter\n    // change is on 3\n    // state on line 1 looked ahead 2 -- so saw 3\n    // test 1 + 2 < 3 should cover this\n    if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {\n      start = line + 1\n      break\n    }\n  }\n  doc.highlightFrontier = Math.min(doc.highlightFrontier, start)\n}\n\n// LINE DATA STRUCTURE\n\n// Line objects. These hold state related to a line, including\n// highlighting info (the styles array).\nvar Line = function(text, markedSpans, estimateHeight) {\n  this.text = text\n  attachMarkedSpans(this, markedSpans)\n  this.height = estimateHeight ? estimateHeight(this) : 1\n};\n\nLine.prototype.lineNo = function () { return lineNo(this) };\neventMixin(Line)\n\n// Change the content (text, markers) of a line. Automatically\n// invalidates cached information and tries to re-estimate the\n// line's height.\nfunction updateLine(line, text, markedSpans, estimateHeight) {\n  line.text = text\n  if (line.stateAfter) { line.stateAfter = null }\n  if (line.styles) { line.styles = null }\n  if (line.order != null) { line.order = null }\n  detachMarkedSpans(line)\n  attachMarkedSpans(line, markedSpans)\n  var estHeight = estimateHeight ? estimateHeight(line) : 1\n  if (estHeight != line.height) { updateLineHeight(line, estHeight) }\n}\n\n// Detach a line from the document tree and its markers.\nfunction cleanUpLine(line) {\n  line.parent = null\n  detachMarkedSpans(line)\n}\n\n// Convert a style as returned by a mode (either null, or a string\n// containing one or more styles) to a CSS style. This is cached,\n// and also looks for line-wide styles.\nvar styleToClassCache = {};\nvar styleToClassCacheWithMode = {};\nfunction interpretTokenStyle(style, options) {\n  if (!style || /^\\s*$/.test(style)) { return null }\n  var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache\n  return cache[style] ||\n    (cache[style] = style.replace(/\\S+/g, \"cm-$&\"))\n}\n\n// Render the DOM representation of the text of a line. Also builds\n// up a 'line map', which points at the DOM nodes that represent\n// specific stretches of text, and is used by the measuring code.\n// The returned object contains the DOM node, this map, and\n// information about line-wide styles that were set by the mode.\nfunction buildLineContent(cm, lineView) {\n  // The padding-right forces the element to have a 'border', which\n  // is needed on Webkit to be able to get line-level bounding\n  // rectangles for it (in measureChar).\n  var content = eltP(\"span\", null, null, webkit ? \"padding-right: .1px\" : null)\n  var builder = {pre: eltP(\"pre\", [content], \"CodeMirror-line\"), content: content,\n                 col: 0, pos: 0, cm: cm,\n                 trailingSpace: false,\n                 splitSpaces: (ie || webkit) && cm.getOption(\"lineWrapping\")}\n  lineView.measure = {}\n\n  // Iterate over the logical lines that make up this visual line.\n  for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {\n    var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)\n    builder.pos = 0\n    builder.addToken = buildToken\n    // Optionally wire in some hacks into the token-rendering\n    // algorithm, to deal with browser quirks.\n    if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))\n      { builder.addToken = buildTokenBadBidi(builder.addToken, order) }\n    builder.map = []\n    var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)\n    insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))\n    if (line.styleClasses) {\n      if (line.styleClasses.bgClass)\n        { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || \"\") }\n      if (line.styleClasses.textClass)\n        { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || \"\") }\n    }\n\n    // Ensure at least a single node is present, for measuring.\n    if (builder.map.length == 0)\n      { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }\n\n    // Store the map and a cache object for the current logical line\n    if (i == 0) {\n      lineView.measure.map = builder.map\n      lineView.measure.cache = {}\n    } else {\n      ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)\n      ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})\n    }\n  }\n\n  // See issue #2901\n  if (webkit) {\n    var last = builder.content.lastChild\n    if (/\\bcm-tab\\b/.test(last.className) || (last.querySelector && last.querySelector(\".cm-tab\")))\n      { builder.content.className = \"cm-tab-wrap-hack\" }\n  }\n\n  signal(cm, \"renderLine\", cm, lineView.line, builder.pre)\n  if (builder.pre.className)\n    { builder.textClass = joinClasses(builder.pre.className, builder.textClass || \"\") }\n\n  return builder\n}\n\nfunction defaultSpecialCharPlaceholder(ch) {\n  var token = elt(\"span\", \"\\u2022\", \"cm-invalidchar\")\n  token.title = \"\\\\u\" + ch.charCodeAt(0).toString(16)\n  token.setAttribute(\"aria-label\", token.title)\n  return token\n}\n\n// Build up the DOM representation for a single token, and add it to\n// the line map. Takes care to render special characters separately.\nfunction buildToken(builder, text, style, startStyle, endStyle, title, css) {\n  if (!text) { return }\n  var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text\n  var special = builder.cm.state.specialChars, mustWrap = false\n  var content\n  if (!special.test(text)) {\n    builder.col += text.length\n    content = document.createTextNode(displayText)\n    builder.map.push(builder.pos, builder.pos + text.length, content)\n    if (ie && ie_version < 9) { mustWrap = true }\n    builder.pos += text.length\n  } else {\n    content = document.createDocumentFragment()\n    var pos = 0\n    while (true) {\n      special.lastIndex = pos\n      var m = special.exec(text)\n      var skipped = m ? m.index - pos : text.length - pos\n      if (skipped) {\n        var txt = document.createTextNode(displayText.slice(pos, pos + skipped))\n        if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt])) }\n        else { content.appendChild(txt) }\n        builder.map.push(builder.pos, builder.pos + skipped, txt)\n        builder.col += skipped\n        builder.pos += skipped\n      }\n      if (!m) { break }\n      pos += skipped + 1\n      var txt$1 = (void 0)\n      if (m[0] == \"\\t\") {\n        var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize\n        txt$1 = content.appendChild(elt(\"span\", spaceStr(tabWidth), \"cm-tab\"))\n        txt$1.setAttribute(\"role\", \"presentation\")\n        txt$1.setAttribute(\"cm-text\", \"\\t\")\n        builder.col += tabWidth\n      } else if (m[0] == \"\\r\" || m[0] == \"\\n\") {\n        txt$1 = content.appendChild(elt(\"span\", m[0] == \"\\r\" ? \"\\u240d\" : \"\\u2424\", \"cm-invalidchar\"))\n        txt$1.setAttribute(\"cm-text\", m[0])\n        builder.col += 1\n      } else {\n        txt$1 = builder.cm.options.specialCharPlaceholder(m[0])\n        txt$1.setAttribute(\"cm-text\", m[0])\n        if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt$1])) }\n        else { content.appendChild(txt$1) }\n        builder.col += 1\n      }\n      builder.map.push(builder.pos, builder.pos + 1, txt$1)\n      builder.pos++\n    }\n  }\n  builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32\n  if (style || startStyle || endStyle || mustWrap || css) {\n    var fullStyle = style || \"\"\n    if (startStyle) { fullStyle += startStyle }\n    if (endStyle) { fullStyle += endStyle }\n    var token = elt(\"span\", [content], fullStyle, css)\n    if (title) { token.title = title }\n    return builder.content.appendChild(token)\n  }\n  builder.content.appendChild(content)\n}\n\nfunction splitSpaces(text, trailingBefore) {\n  if (text.length > 1 && !/  /.test(text)) { return text }\n  var spaceBefore = trailingBefore, result = \"\"\n  for (var i = 0; i < text.length; i++) {\n    var ch = text.charAt(i)\n    if (ch == \" \" && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))\n      { ch = \"\\u00a0\" }\n    result += ch\n    spaceBefore = ch == \" \"\n  }\n  return result\n}\n\n// Work around nonsense dimensions being reported for stretches of\n// right-to-left text.\nfunction buildTokenBadBidi(inner, order) {\n  return function (builder, text, style, startStyle, endStyle, title, css) {\n    style = style ? style + \" cm-force-border\" : \"cm-force-border\"\n    var start = builder.pos, end = start + text.length\n    for (;;) {\n      // Find the part that overlaps with the start of this text\n      var part = (void 0)\n      for (var i = 0; i < order.length; i++) {\n        part = order[i]\n        if (part.to > start && part.from <= start) { break }\n      }\n      if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }\n      inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)\n      startStyle = null\n      text = text.slice(part.to - start)\n      start = part.to\n    }\n  }\n}\n\nfunction buildCollapsedSpan(builder, size, marker, ignoreWidget) {\n  var widget = !ignoreWidget && marker.widgetNode\n  if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }\n  if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {\n    if (!widget)\n      { widget = builder.content.appendChild(document.createElement(\"span\")) }\n    widget.setAttribute(\"cm-marker\", marker.id)\n  }\n  if (widget) {\n    builder.cm.display.input.setUneditable(widget)\n    builder.content.appendChild(widget)\n  }\n  builder.pos += size\n  builder.trailingSpace = false\n}\n\n// Outputs a number of spans to make up a line, taking highlighting\n// and marked text into account.\nfunction insertLineContent(line, builder, styles) {\n  var spans = line.markedSpans, allText = line.text, at = 0\n  if (!spans) {\n    for (var i$1 = 1; i$1 < styles.length; i$1+=2)\n      { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }\n    return\n  }\n\n  var len = allText.length, pos = 0, i = 1, text = \"\", style, css\n  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed\n  for (;;) {\n    if (nextChange == pos) { // Update current marker set\n      spanStyle = spanEndStyle = spanStartStyle = title = css = \"\"\n      collapsed = null; nextChange = Infinity\n      var foundBookmarks = [], endStyles = (void 0)\n      for (var j = 0; j < spans.length; ++j) {\n        var sp = spans[j], m = sp.marker\n        if (m.type == \"bookmark\" && sp.from == pos && m.widgetNode) {\n          foundBookmarks.push(m)\n        } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {\n          if (sp.to != null && sp.to != pos && nextChange > sp.to) {\n            nextChange = sp.to\n            spanEndStyle = \"\"\n          }\n          if (m.className) { spanStyle += \" \" + m.className }\n          if (m.css) { css = (css ? css + \";\" : \"\") + m.css }\n          if (m.startStyle && sp.from == pos) { spanStartStyle += \" \" + m.startStyle }\n          if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }\n          if (m.title && !title) { title = m.title }\n          if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))\n            { collapsed = sp }\n        } else if (sp.from > pos && nextChange > sp.from) {\n          nextChange = sp.from\n        }\n      }\n      if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)\n        { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += \" \" + endStyles[j$1] } } }\n\n      if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)\n        { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }\n      if (collapsed && (collapsed.from || 0) == pos) {\n        buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,\n                           collapsed.marker, collapsed.from == null)\n        if (collapsed.to == null) { return }\n        if (collapsed.to == pos) { collapsed = false }\n      }\n    }\n    if (pos >= len) { break }\n\n    var upto = Math.min(len, nextChange)\n    while (true) {\n      if (text) {\n        var end = pos + text.length\n        if (!collapsed) {\n          var tokenText = end > upto ? text.slice(0, upto - pos) : text\n          builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,\n                           spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : \"\", title, css)\n        }\n        if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}\n        pos = end\n        spanStartStyle = \"\"\n      }\n      text = allText.slice(at, at = styles[i++])\n      style = interpretTokenStyle(styles[i++], builder.cm.options)\n    }\n  }\n}\n\n\n// These objects are used to represent the visible (currently drawn)\n// part of the document. A LineView may correspond to multiple\n// logical lines, if those are connected by collapsed ranges.\nfunction LineView(doc, line, lineN) {\n  // The starting line\n  this.line = line\n  // Continuing lines, if any\n  this.rest = visualLineContinued(line)\n  // Number of logical lines in this visual line\n  this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1\n  this.node = this.text = null\n  this.hidden = lineIsHidden(doc, line)\n}\n\n// Create a range of LineView objects for the given lines.\nfunction buildViewArray(cm, from, to) {\n  var array = [], nextPos\n  for (var pos = from; pos < to; pos = nextPos) {\n    var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)\n    nextPos = pos + view.size\n    array.push(view)\n  }\n  return array\n}\n\nvar operationGroup = null\n\nfunction pushOperation(op) {\n  if (operationGroup) {\n    operationGroup.ops.push(op)\n  } else {\n    op.ownsGroup = operationGroup = {\n      ops: [op],\n      delayedCallbacks: []\n    }\n  }\n}\n\nfunction fireCallbacksForOps(group) {\n  // Calls delayed callbacks and cursorActivity handlers until no\n  // new ones appear\n  var callbacks = group.delayedCallbacks, i = 0\n  do {\n    for (; i < callbacks.length; i++)\n      { callbacks[i].call(null) }\n    for (var j = 0; j < group.ops.length; j++) {\n      var op = group.ops[j]\n      if (op.cursorActivityHandlers)\n        { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)\n          { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }\n    }\n  } while (i < callbacks.length)\n}\n\nfunction finishOperation(op, endCb) {\n  var group = op.ownsGroup\n  if (!group) { return }\n\n  try { fireCallbacksForOps(group) }\n  finally {\n    operationGroup = null\n    endCb(group)\n  }\n}\n\nvar orphanDelayedCallbacks = null\n\n// Often, we want to signal events at a point where we are in the\n// middle of some work, but don't want the handler to start calling\n// other methods on the editor, which might be in an inconsistent\n// state or simply not expect any other events to happen.\n// signalLater looks whether there are any handlers, and schedules\n// them to be executed when the last operation ends, or, if no\n// operation is active, when a timeout fires.\nfunction signalLater(emitter, type /*, values...*/) {\n  var arr = getHandlers(emitter, type)\n  if (!arr.length) { return }\n  var args = Array.prototype.slice.call(arguments, 2), list\n  if (operationGroup) {\n    list = operationGroup.delayedCallbacks\n  } else if (orphanDelayedCallbacks) {\n    list = orphanDelayedCallbacks\n  } else {\n    list = orphanDelayedCallbacks = []\n    setTimeout(fireOrphanDelayed, 0)\n  }\n  var loop = function ( i ) {\n    list.push(function () { return arr[i].apply(null, args); })\n  };\n\n  for (var i = 0; i < arr.length; ++i)\n    loop( i );\n}\n\nfunction fireOrphanDelayed() {\n  var delayed = orphanDelayedCallbacks\n  orphanDelayedCallbacks = null\n  for (var i = 0; i < delayed.length; ++i) { delayed[i]() }\n}\n\n// When an aspect of a line changes, a string is added to\n// lineView.changes. This updates the relevant part of the line's\n// DOM structure.\nfunction updateLineForChanges(cm, lineView, lineN, dims) {\n  for (var j = 0; j < lineView.changes.length; j++) {\n    var type = lineView.changes[j]\n    if (type == \"text\") { updateLineText(cm, lineView) }\n    else if (type == \"gutter\") { updateLineGutter(cm, lineView, lineN, dims) }\n    else if (type == \"class\") { updateLineClasses(cm, lineView) }\n    else if (type == \"widget\") { updateLineWidgets(cm, lineView, dims) }\n  }\n  lineView.changes = null\n}\n\n// Lines with gutter elements, widgets or a background class need to\n// be wrapped, and have the extra elements added to the wrapper div\nfunction ensureLineWrapped(lineView) {\n  if (lineView.node == lineView.text) {\n    lineView.node = elt(\"div\", null, null, \"position: relative\")\n    if (lineView.text.parentNode)\n      { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }\n    lineView.node.appendChild(lineView.text)\n    if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }\n  }\n  return lineView.node\n}\n\nfunction updateLineBackground(cm, lineView) {\n  var cls = lineView.bgClass ? lineView.bgClass + \" \" + (lineView.line.bgClass || \"\") : lineView.line.bgClass\n  if (cls) { cls += \" CodeMirror-linebackground\" }\n  if (lineView.background) {\n    if (cls) { lineView.background.className = cls }\n    else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }\n  } else if (cls) {\n    var wrap = ensureLineWrapped(lineView)\n    lineView.background = wrap.insertBefore(elt(\"div\", null, cls), wrap.firstChild)\n    cm.display.input.setUneditable(lineView.background)\n  }\n}\n\n// Wrapper around buildLineContent which will reuse the structure\n// in display.externalMeasured when possible.\nfunction getLineContent(cm, lineView) {\n  var ext = cm.display.externalMeasured\n  if (ext && ext.line == lineView.line) {\n    cm.display.externalMeasured = null\n    lineView.measure = ext.measure\n    return ext.built\n  }\n  return buildLineContent(cm, lineView)\n}\n\n// Redraw the line's text. Interacts with the background and text\n// classes because the mode may output tokens that influence these\n// classes.\nfunction updateLineText(cm, lineView) {\n  var cls = lineView.text.className\n  var built = getLineContent(cm, lineView)\n  if (lineView.text == lineView.node) { lineView.node = built.pre }\n  lineView.text.parentNode.replaceChild(built.pre, lineView.text)\n  lineView.text = built.pre\n  if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {\n    lineView.bgClass = built.bgClass\n    lineView.textClass = built.textClass\n    updateLineClasses(cm, lineView)\n  } else if (cls) {\n    lineView.text.className = cls\n  }\n}\n\nfunction updateLineClasses(cm, lineView) {\n  updateLineBackground(cm, lineView)\n  if (lineView.line.wrapClass)\n    { ensureLineWrapped(lineView).className = lineView.line.wrapClass }\n  else if (lineView.node != lineView.text)\n    { lineView.node.className = \"\" }\n  var textClass = lineView.textClass ? lineView.textClass + \" \" + (lineView.line.textClass || \"\") : lineView.line.textClass\n  lineView.text.className = textClass || \"\"\n}\n\nfunction updateLineGutter(cm, lineView, lineN, dims) {\n  if (lineView.gutter) {\n    lineView.node.removeChild(lineView.gutter)\n    lineView.gutter = null\n  }\n  if (lineView.gutterBackground) {\n    lineView.node.removeChild(lineView.gutterBackground)\n    lineView.gutterBackground = null\n  }\n  if (lineView.line.gutterClass) {\n    var wrap = ensureLineWrapped(lineView)\n    lineView.gutterBackground = elt(\"div\", null, \"CodeMirror-gutter-background \" + lineView.line.gutterClass,\n                                    (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px; width: \" + (dims.gutterTotalWidth) + \"px\"))\n    cm.display.input.setUneditable(lineView.gutterBackground)\n    wrap.insertBefore(lineView.gutterBackground, lineView.text)\n  }\n  var markers = lineView.line.gutterMarkers\n  if (cm.options.lineNumbers || markers) {\n    var wrap$1 = ensureLineWrapped(lineView)\n    var gutterWrap = lineView.gutter = elt(\"div\", null, \"CodeMirror-gutter-wrapper\", (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px\"))\n    cm.display.input.setUneditable(gutterWrap)\n    wrap$1.insertBefore(gutterWrap, lineView.text)\n    if (lineView.line.gutterClass)\n      { gutterWrap.className += \" \" + lineView.line.gutterClass }\n    if (cm.options.lineNumbers && (!markers || !markers[\"CodeMirror-linenumbers\"]))\n      { lineView.lineNumber = gutterWrap.appendChild(\n        elt(\"div\", lineNumberFor(cm.options, lineN),\n            \"CodeMirror-linenumber CodeMirror-gutter-elt\",\n            (\"left: \" + (dims.gutterLeft[\"CodeMirror-linenumbers\"]) + \"px; width: \" + (cm.display.lineNumInnerWidth) + \"px\"))) }\n    if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {\n      var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]\n      if (found)\n        { gutterWrap.appendChild(elt(\"div\", [found], \"CodeMirror-gutter-elt\",\n                                   (\"left: \" + (dims.gutterLeft[id]) + \"px; width: \" + (dims.gutterWidth[id]) + \"px\"))) }\n    } }\n  }\n}\n\nfunction updateLineWidgets(cm, lineView, dims) {\n  if (lineView.alignable) { lineView.alignable = null }\n  for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {\n    next = node.nextSibling\n    if (node.className == \"CodeMirror-linewidget\")\n      { lineView.node.removeChild(node) }\n  }\n  insertLineWidgets(cm, lineView, dims)\n}\n\n// Build a line's DOM representation from scratch\nfunction buildLineElement(cm, lineView, lineN, dims) {\n  var built = getLineContent(cm, lineView)\n  lineView.text = lineView.node = built.pre\n  if (built.bgClass) { lineView.bgClass = built.bgClass }\n  if (built.textClass) { lineView.textClass = built.textClass }\n\n  updateLineClasses(cm, lineView)\n  updateLineGutter(cm, lineView, lineN, dims)\n  insertLineWidgets(cm, lineView, dims)\n  return lineView.node\n}\n\n// A lineView may contain multiple logical lines (when merged by\n// collapsed spans). The widgets for all of them need to be drawn.\nfunction insertLineWidgets(cm, lineView, dims) {\n  insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)\n  if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n    { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }\n}\n\nfunction insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {\n  if (!line.widgets) { return }\n  var wrap = ensureLineWrapped(lineView)\n  for (var i = 0, ws = line.widgets; i < ws.length; ++i) {\n    var widget = ws[i], node = elt(\"div\", [widget.node], \"CodeMirror-linewidget\")\n    if (!widget.handleMouseEvents) { node.setAttribute(\"cm-ignore-events\", \"true\") }\n    positionLineWidget(widget, node, lineView, dims)\n    cm.display.input.setUneditable(node)\n    if (allowAbove && widget.above)\n      { wrap.insertBefore(node, lineView.gutter || lineView.text) }\n    else\n      { wrap.appendChild(node) }\n    signalLater(widget, \"redraw\")\n  }\n}\n\nfunction positionLineWidget(widget, node, lineView, dims) {\n  if (widget.noHScroll) {\n    ;(lineView.alignable || (lineView.alignable = [])).push(node)\n    var width = dims.wrapperWidth\n    node.style.left = dims.fixedPos + \"px\"\n    if (!widget.coverGutter) {\n      width -= dims.gutterTotalWidth\n      node.style.paddingLeft = dims.gutterTotalWidth + \"px\"\n    }\n    node.style.width = width + \"px\"\n  }\n  if (widget.coverGutter) {\n    node.style.zIndex = 5\n    node.style.position = \"relative\"\n    if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + \"px\" }\n  }\n}\n\nfunction widgetHeight(widget) {\n  if (widget.height != null) { return widget.height }\n  var cm = widget.doc.cm\n  if (!cm) { return 0 }\n  if (!contains(document.body, widget.node)) {\n    var parentStyle = \"position: relative;\"\n    if (widget.coverGutter)\n      { parentStyle += \"margin-left: -\" + cm.display.gutters.offsetWidth + \"px;\" }\n    if (widget.noHScroll)\n      { parentStyle += \"width: \" + cm.display.wrapper.clientWidth + \"px;\" }\n    removeChildrenAndAdd(cm.display.measure, elt(\"div\", [widget.node], null, parentStyle))\n  }\n  return widget.height = widget.node.parentNode.offsetHeight\n}\n\n// Return true when the given mouse event happened in a widget\nfunction eventInWidget(display, e) {\n  for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {\n    if (!n || (n.nodeType == 1 && n.getAttribute(\"cm-ignore-events\") == \"true\") ||\n        (n.parentNode == display.sizer && n != display.mover))\n      { return true }\n  }\n}\n\n// POSITION MEASUREMENT\n\nfunction paddingTop(display) {return display.lineSpace.offsetTop}\nfunction paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}\nfunction paddingH(display) {\n  if (display.cachedPaddingH) { return display.cachedPaddingH }\n  var e = removeChildrenAndAdd(display.measure, elt(\"pre\", \"x\"))\n  var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle\n  var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}\n  if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }\n  return data\n}\n\nfunction scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }\nfunction displayWidth(cm) {\n  return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth\n}\nfunction displayHeight(cm) {\n  return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight\n}\n\n// Ensure the lineView.wrapping.heights array is populated. This is\n// an array of bottom offsets for the lines that make up a drawn\n// line. When lineWrapping is on, there might be more than one\n// height.\nfunction ensureLineHeights(cm, lineView, rect) {\n  var wrapping = cm.options.lineWrapping\n  var curWidth = wrapping && displayWidth(cm)\n  if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {\n    var heights = lineView.measure.heights = []\n    if (wrapping) {\n      lineView.measure.width = curWidth\n      var rects = lineView.text.firstChild.getClientRects()\n      for (var i = 0; i < rects.length - 1; i++) {\n        var cur = rects[i], next = rects[i + 1]\n        if (Math.abs(cur.bottom - next.bottom) > 2)\n          { heights.push((cur.bottom + next.top) / 2 - rect.top) }\n      }\n    }\n    heights.push(rect.bottom - rect.top)\n  }\n}\n\n// Find a line map (mapping character offsets to text nodes) and a\n// measurement cache for the given line number. (A line view might\n// contain multiple lines when collapsed ranges are present.)\nfunction mapFromLineView(lineView, line, lineN) {\n  if (lineView.line == line)\n    { return {map: lineView.measure.map, cache: lineView.measure.cache} }\n  for (var i = 0; i < lineView.rest.length; i++)\n    { if (lineView.rest[i] == line)\n      { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }\n  for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)\n    { if (lineNo(lineView.rest[i$1]) > lineN)\n      { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }\n}\n\n// Render a line into the hidden node display.externalMeasured. Used\n// when measurement is needed for a line that's not in the viewport.\nfunction updateExternalMeasurement(cm, line) {\n  line = visualLine(line)\n  var lineN = lineNo(line)\n  var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)\n  view.lineN = lineN\n  var built = view.built = buildLineContent(cm, view)\n  view.text = built.pre\n  removeChildrenAndAdd(cm.display.lineMeasure, built.pre)\n  return view\n}\n\n// Get a {top, bottom, left, right} box (in line-local coordinates)\n// for a given character.\nfunction measureChar(cm, line, ch, bias) {\n  return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)\n}\n\n// Find a line view that corresponds to the given line number.\nfunction findViewForLine(cm, lineN) {\n  if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)\n    { return cm.display.view[findViewIndex(cm, lineN)] }\n  var ext = cm.display.externalMeasured\n  if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)\n    { return ext }\n}\n\n// Measurement can be split in two steps, the set-up work that\n// applies to the whole line, and the measurement of the actual\n// character. Functions like coordsChar, that need to do a lot of\n// measurements in a row, can thus ensure that the set-up work is\n// only done once.\nfunction prepareMeasureForLine(cm, line) {\n  var lineN = lineNo(line)\n  var view = findViewForLine(cm, lineN)\n  if (view && !view.text) {\n    view = null\n  } else if (view && view.changes) {\n    updateLineForChanges(cm, view, lineN, getDimensions(cm))\n    cm.curOp.forceUpdate = true\n  }\n  if (!view)\n    { view = updateExternalMeasurement(cm, line) }\n\n  var info = mapFromLineView(view, line, lineN)\n  return {\n    line: line, view: view, rect: null,\n    map: info.map, cache: info.cache, before: info.before,\n    hasHeights: false\n  }\n}\n\n// Given a prepared measurement object, measures the position of an\n// actual character (or fetches it from the cache).\nfunction measureCharPrepared(cm, prepared, ch, bias, varHeight) {\n  if (prepared.before) { ch = -1 }\n  var key = ch + (bias || \"\"), found\n  if (prepared.cache.hasOwnProperty(key)) {\n    found = prepared.cache[key]\n  } else {\n    if (!prepared.rect)\n      { prepared.rect = prepared.view.text.getBoundingClientRect() }\n    if (!prepared.hasHeights) {\n      ensureLineHeights(cm, prepared.view, prepared.rect)\n      prepared.hasHeights = true\n    }\n    found = measureCharInner(cm, prepared, ch, bias)\n    if (!found.bogus) { prepared.cache[key] = found }\n  }\n  return {left: found.left, right: found.right,\n          top: varHeight ? found.rtop : found.top,\n          bottom: varHeight ? found.rbottom : found.bottom}\n}\n\nvar nullRect = {left: 0, right: 0, top: 0, bottom: 0}\n\nfunction nodeAndOffsetInLineMap(map, ch, bias) {\n  var node, start, end, collapse, mStart, mEnd\n  // First, search the line map for the text node corresponding to,\n  // or closest to, the target character.\n  for (var i = 0; i < map.length; i += 3) {\n    mStart = map[i]\n    mEnd = map[i + 1]\n    if (ch < mStart) {\n      start = 0; end = 1\n      collapse = \"left\"\n    } else if (ch < mEnd) {\n      start = ch - mStart\n      end = start + 1\n    } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {\n      end = mEnd - mStart\n      start = end - 1\n      if (ch >= mEnd) { collapse = \"right\" }\n    }\n    if (start != null) {\n      node = map[i + 2]\n      if (mStart == mEnd && bias == (node.insertLeft ? \"left\" : \"right\"))\n        { collapse = bias }\n      if (bias == \"left\" && start == 0)\n        { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {\n          node = map[(i -= 3) + 2]\n          collapse = \"left\"\n        } }\n      if (bias == \"right\" && start == mEnd - mStart)\n        { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {\n          node = map[(i += 3) + 2]\n          collapse = \"right\"\n        } }\n      break\n    }\n  }\n  return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}\n}\n\nfunction getUsefulRect(rects, bias) {\n  var rect = nullRect\n  if (bias == \"left\") { for (var i = 0; i < rects.length; i++) {\n    if ((rect = rects[i]).left != rect.right) { break }\n  } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {\n    if ((rect = rects[i$1]).left != rect.right) { break }\n  } }\n  return rect\n}\n\nfunction measureCharInner(cm, prepared, ch, bias) {\n  var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)\n  var node = place.node, start = place.start, end = place.end, collapse = place.collapse\n\n  var rect\n  if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.\n    for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned\n      while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }\n      while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }\n      if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)\n        { rect = node.parentNode.getBoundingClientRect() }\n      else\n        { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }\n      if (rect.left || rect.right || start == 0) { break }\n      end = start\n      start = start - 1\n      collapse = \"right\"\n    }\n    if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }\n  } else { // If it is a widget, simply get the box for the whole widget.\n    if (start > 0) { collapse = bias = \"right\" }\n    var rects\n    if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)\n      { rect = rects[bias == \"right\" ? rects.length - 1 : 0] }\n    else\n      { rect = node.getBoundingClientRect() }\n  }\n  if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {\n    var rSpan = node.parentNode.getClientRects()[0]\n    if (rSpan)\n      { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }\n    else\n      { rect = nullRect }\n  }\n\n  var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top\n  var mid = (rtop + rbot) / 2\n  var heights = prepared.view.measure.heights\n  var i = 0\n  for (; i < heights.length - 1; i++)\n    { if (mid < heights[i]) { break } }\n  var top = i ? heights[i - 1] : 0, bot = heights[i]\n  var result = {left: (collapse == \"right\" ? rect.right : rect.left) - prepared.rect.left,\n                right: (collapse == \"left\" ? rect.left : rect.right) - prepared.rect.left,\n                top: top, bottom: bot}\n  if (!rect.left && !rect.right) { result.bogus = true }\n  if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }\n\n  return result\n}\n\n// Work around problem with bounding client rects on ranges being\n// returned incorrectly when zoomed on IE10 and below.\nfunction maybeUpdateRectForZooming(measure, rect) {\n  if (!window.screen || screen.logicalXDPI == null ||\n      screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))\n    { return rect }\n  var scaleX = screen.logicalXDPI / screen.deviceXDPI\n  var scaleY = screen.logicalYDPI / screen.deviceYDPI\n  return {left: rect.left * scaleX, right: rect.right * scaleX,\n          top: rect.top * scaleY, bottom: rect.bottom * scaleY}\n}\n\nfunction clearLineMeasurementCacheFor(lineView) {\n  if (lineView.measure) {\n    lineView.measure.cache = {}\n    lineView.measure.heights = null\n    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n      { lineView.measure.caches[i] = {} } }\n  }\n}\n\nfunction clearLineMeasurementCache(cm) {\n  cm.display.externalMeasure = null\n  removeChildren(cm.display.lineMeasure)\n  for (var i = 0; i < cm.display.view.length; i++)\n    { clearLineMeasurementCacheFor(cm.display.view[i]) }\n}\n\nfunction clearCaches(cm) {\n  clearLineMeasurementCache(cm)\n  cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null\n  if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }\n  cm.display.lineNumChars = null\n}\n\nfunction pageScrollX() {\n  // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206\n  // which causes page_Offset and bounding client rects to use\n  // different reference viewports and invalidate our calculations.\n  if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }\n  return window.pageXOffset || (document.documentElement || document.body).scrollLeft\n}\nfunction pageScrollY() {\n  if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }\n  return window.pageYOffset || (document.documentElement || document.body).scrollTop\n}\n\nfunction widgetTopHeight(lineObj) {\n  var height = 0\n  if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)\n    { height += widgetHeight(lineObj.widgets[i]) } } }\n  return height\n}\n\n// Converts a {top, bottom, left, right} box from line-local\n// coordinates into another coordinate system. Context may be one of\n// \"line\", \"div\" (display.lineDiv), \"local\"./null (editor), \"window\",\n// or \"page\".\nfunction intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {\n  if (!includeWidgets) {\n    var height = widgetTopHeight(lineObj)\n    rect.top += height; rect.bottom += height\n  }\n  if (context == \"line\") { return rect }\n  if (!context) { context = \"local\" }\n  var yOff = heightAtLine(lineObj)\n  if (context == \"local\") { yOff += paddingTop(cm.display) }\n  else { yOff -= cm.display.viewOffset }\n  if (context == \"page\" || context == \"window\") {\n    var lOff = cm.display.lineSpace.getBoundingClientRect()\n    yOff += lOff.top + (context == \"window\" ? 0 : pageScrollY())\n    var xOff = lOff.left + (context == \"window\" ? 0 : pageScrollX())\n    rect.left += xOff; rect.right += xOff\n  }\n  rect.top += yOff; rect.bottom += yOff\n  return rect\n}\n\n// Coverts a box from \"div\" coords to another coordinate system.\n// Context may be \"window\", \"page\", \"div\", or \"local\"./null.\nfunction fromCoordSystem(cm, coords, context) {\n  if (context == \"div\") { return coords }\n  var left = coords.left, top = coords.top\n  // First move into \"page\" coordinate system\n  if (context == \"page\") {\n    left -= pageScrollX()\n    top -= pageScrollY()\n  } else if (context == \"local\" || !context) {\n    var localBox = cm.display.sizer.getBoundingClientRect()\n    left += localBox.left\n    top += localBox.top\n  }\n\n  var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()\n  return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}\n}\n\nfunction charCoords(cm, pos, context, lineObj, bias) {\n  if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }\n  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)\n}\n\n// Returns a box for a given cursor position, which may have an\n// 'other' property containing the position of the secondary cursor\n// on a bidi boundary.\n// A cursor Pos(line, char, \"before\") is on the same visual line as `char - 1`\n// and after `char - 1` in writing order of `char - 1`\n// A cursor Pos(line, char, \"after\") is on the same visual line as `char`\n// and before `char` in writing order of `char`\n// Examples (upper-case letters are RTL, lower-case are LTR):\n//     Pos(0, 1, ...)\n//     before   after\n// ab     a|b     a|b\n// aB     a|B     aB|\n// Ab     |Ab     A|b\n// AB     B|A     B|A\n// Every position after the last character on a line is considered to stick\n// to the last character on the line.\nfunction cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {\n  lineObj = lineObj || getLine(cm.doc, pos.line)\n  if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }\n  function get(ch, right) {\n    var m = measureCharPrepared(cm, preparedMeasure, ch, right ? \"right\" : \"left\", varHeight)\n    if (right) { m.left = m.right; } else { m.right = m.left }\n    return intoCoordSystem(cm, lineObj, m, context)\n  }\n  var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky\n  if (ch >= lineObj.text.length) {\n    ch = lineObj.text.length\n    sticky = \"before\"\n  } else if (ch <= 0) {\n    ch = 0\n    sticky = \"after\"\n  }\n  if (!order) { return get(sticky == \"before\" ? ch - 1 : ch, sticky == \"before\") }\n\n  function getBidi(ch, partPos, invert) {\n    var part = order[partPos], right = part.level == 1\n    return get(invert ? ch - 1 : ch, right != invert)\n  }\n  var partPos = getBidiPartAt(order, ch, sticky)\n  var other = bidiOther\n  var val = getBidi(ch, partPos, sticky == \"before\")\n  if (other != null) { val.other = getBidi(ch, other, sticky != \"before\") }\n  return val\n}\n\n// Used to cheaply estimate the coordinates for a position. Used for\n// intermediate scroll updates.\nfunction estimateCoords(cm, pos) {\n  var left = 0\n  pos = clipPos(cm.doc, pos)\n  if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }\n  var lineObj = getLine(cm.doc, pos.line)\n  var top = heightAtLine(lineObj) + paddingTop(cm.display)\n  return {left: left, right: left, top: top, bottom: top + lineObj.height}\n}\n\n// Positions returned by coordsChar contain some extra information.\n// xRel is the relative x position of the input coordinates compared\n// to the found position (so xRel > 0 means the coordinates are to\n// the right of the character position, for example). When outside\n// is true, that means the coordinates lie outside the line's\n// vertical range.\nfunction PosWithInfo(line, ch, sticky, outside, xRel) {\n  var pos = Pos(line, ch, sticky)\n  pos.xRel = xRel\n  if (outside) { pos.outside = true }\n  return pos\n}\n\n// Compute the character position closest to the given coordinates.\n// Input must be lineSpace-local (\"div\" coordinate system).\nfunction coordsChar(cm, x, y) {\n  var doc = cm.doc\n  y += cm.display.viewOffset\n  if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }\n  var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1\n  if (lineN > last)\n    { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }\n  if (x < 0) { x = 0 }\n\n  var lineObj = getLine(doc, lineN)\n  for (;;) {\n    var found = coordsCharInner(cm, lineObj, lineN, x, y)\n    var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0))\n    if (!collapsed) { return found }\n    var rangeEnd = collapsed.find(1)\n    if (rangeEnd.line == lineN) { return rangeEnd }\n    lineObj = getLine(doc, lineN = rangeEnd.line)\n  }\n}\n\nfunction wrappedLineExtent(cm, lineObj, preparedMeasure, y) {\n  y -= widgetTopHeight(lineObj)\n  var end = lineObj.text.length\n  var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)\n  end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)\n  return {begin: begin, end: end}\n}\n\nfunction wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {\n  if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }\n  var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), \"line\").top\n  return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)\n}\n\n// Returns true if the given side of a box is after the given\n// coordinates, in top-to-bottom, left-to-right order.\nfunction boxIsAfter(box, x, y, left) {\n  return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x\n}\n\nfunction coordsCharInner(cm, lineObj, lineNo, x, y) {\n  // Move y into line-local coordinate space\n  y -= heightAtLine(lineObj)\n  var preparedMeasure = prepareMeasureForLine(cm, lineObj)\n  // When directly calling `measureCharPrepared`, we have to adjust\n  // for the widgets at this line.\n  var widgetHeight = widgetTopHeight(lineObj)\n  var begin = 0, end = lineObj.text.length, ltr = true\n\n  var order = getOrder(lineObj, cm.doc.direction)\n  // If the line isn't plain left-to-right text, first figure out\n  // which bidi section the coordinates fall into.\n  if (order) {\n    var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)\n                 (cm, lineObj, lineNo, preparedMeasure, order, x, y)\n    ltr = part.level != 1\n    // The awkward -1 offsets are needed because findFirst (called\n    // on these below) will treat its first bound as inclusive,\n    // second as exclusive, but we want to actually address the\n    // characters in the part's range\n    begin = ltr ? part.from : part.to - 1\n    end = ltr ? part.to : part.from - 1\n  }\n\n  // A binary search to find the first character whose bounding box\n  // starts after the coordinates. If we run across any whose box wrap\n  // the coordinates, store that.\n  var chAround = null, boxAround = null\n  var ch = findFirst(function (ch) {\n    var box = measureCharPrepared(cm, preparedMeasure, ch)\n    box.top += widgetHeight; box.bottom += widgetHeight\n    if (!boxIsAfter(box, x, y, false)) { return false }\n    if (box.top <= y && box.left <= x) {\n      chAround = ch\n      boxAround = box\n    }\n    return true\n  }, begin, end)\n\n  var baseX, sticky, outside = false\n  // If a box around the coordinates was found, use that\n  if (boxAround) {\n    // Distinguish coordinates nearer to the left or right side of the box\n    var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr\n    ch = chAround + (atStart ? 0 : 1)\n    sticky = atStart ? \"after\" : \"before\"\n    baseX = atLeft ? boxAround.left : boxAround.right\n  } else {\n    // (Adjust for extended bound, if necessary.)\n    if (!ltr && (ch == end || ch == begin)) { ch++ }\n    // To determine which side to associate with, get the box to the\n    // left of the character and compare it's vertical position to the\n    // coordinates\n    sticky = ch == 0 ? \"after\" : ch == lineObj.text.length ? \"before\" :\n      (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?\n      \"after\" : \"before\"\n    // Now get accurate coordinates for this place, in order to get a\n    // base X position\n    var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), \"line\", lineObj, preparedMeasure)\n    baseX = coords.left\n    outside = y < coords.top || y >= coords.bottom\n  }\n\n  ch = skipExtendingChars(lineObj.text, ch, 1)\n  return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)\n}\n\nfunction coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {\n  // Bidi parts are sorted left-to-right, and in a non-line-wrapping\n  // situation, we can take this ordering to correspond to the visual\n  // ordering. This finds the first part whose end is after the given\n  // coordinates.\n  var index = findFirst(function (i) {\n    var part = order[i], ltr = part.level != 1\n    return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? \"before\" : \"after\"),\n                                   \"line\", lineObj, preparedMeasure), x, y, true)\n  }, 0, order.length - 1)\n  var part = order[index]\n  // If this isn't the first part, the part's start is also after\n  // the coordinates, and the coordinates aren't on the same line as\n  // that start, move one part back.\n  if (index > 0) {\n    var ltr = part.level != 1\n    var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? \"after\" : \"before\"),\n                             \"line\", lineObj, preparedMeasure)\n    if (boxIsAfter(start, x, y, true) && start.top > y)\n      { part = order[index - 1] }\n  }\n  return part\n}\n\nfunction coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {\n  // In a wrapped line, rtl text on wrapping boundaries can do things\n  // that don't correspond to the ordering in our `order` array at\n  // all, so a binary search doesn't work, and we want to return a\n  // part that only spans one line so that the binary search in\n  // coordsCharInner is safe. As such, we first find the extent of the\n  // wrapped line, and then do a flat search in which we discard any\n  // spans that aren't on the line.\n  var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);\n  var begin = ref.begin;\n  var end = ref.end;\n  if (/\\s/.test(lineObj.text.charAt(end - 1))) { end-- }\n  var part = null, closestDist = null\n  for (var i = 0; i < order.length; i++) {\n    var p = order[i]\n    if (p.from >= end || p.to <= begin) { continue }\n    var ltr = p.level != 1\n    var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right\n    // Weigh against spans ending before this, so that they are only\n    // picked if nothing ends after\n    var dist = endX < x ? x - endX + 1e9 : endX - x\n    if (!part || closestDist > dist) {\n      part = p\n      closestDist = dist\n    }\n  }\n  if (!part) { part = order[order.length - 1] }\n  // Clip the part to the wrapped line.\n  if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }\n  if (part.to > end) { part = {from: part.from, to: end, level: part.level} }\n  return part\n}\n\nvar measureText\n// Compute the default text height.\nfunction textHeight(display) {\n  if (display.cachedTextHeight != null) { return display.cachedTextHeight }\n  if (measureText == null) {\n    measureText = elt(\"pre\")\n    // Measure a bunch of lines, for browsers that compute\n    // fractional heights.\n    for (var i = 0; i < 49; ++i) {\n      measureText.appendChild(document.createTextNode(\"x\"))\n      measureText.appendChild(elt(\"br\"))\n    }\n    measureText.appendChild(document.createTextNode(\"x\"))\n  }\n  removeChildrenAndAdd(display.measure, measureText)\n  var height = measureText.offsetHeight / 50\n  if (height > 3) { display.cachedTextHeight = height }\n  removeChildren(display.measure)\n  return height || 1\n}\n\n// Compute the default character width.\nfunction charWidth(display) {\n  if (display.cachedCharWidth != null) { return display.cachedCharWidth }\n  var anchor = elt(\"span\", \"xxxxxxxxxx\")\n  var pre = elt(\"pre\", [anchor])\n  removeChildrenAndAdd(display.measure, pre)\n  var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10\n  if (width > 2) { display.cachedCharWidth = width }\n  return width || 10\n}\n\n// Do a bulk-read of the DOM positions and sizes needed to draw the\n// view, so that we don't interleave reading and writing to the DOM.\nfunction getDimensions(cm) {\n  var d = cm.display, left = {}, width = {}\n  var gutterLeft = d.gutters.clientLeft\n  for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {\n    left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft\n    width[cm.options.gutters[i]] = n.clientWidth\n  }\n  return {fixedPos: compensateForHScroll(d),\n          gutterTotalWidth: d.gutters.offsetWidth,\n          gutterLeft: left,\n          gutterWidth: width,\n          wrapperWidth: d.wrapper.clientWidth}\n}\n\n// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,\n// but using getBoundingClientRect to get a sub-pixel-accurate\n// result.\nfunction compensateForHScroll(display) {\n  return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left\n}\n\n// Returns a function that estimates the height of a line, to use as\n// first approximation until the line becomes visible (and is thus\n// properly measurable).\nfunction estimateHeight(cm) {\n  var th = textHeight(cm.display), wrapping = cm.options.lineWrapping\n  var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)\n  return function (line) {\n    if (lineIsHidden(cm.doc, line)) { return 0 }\n\n    var widgetsHeight = 0\n    if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {\n      if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }\n    } }\n\n    if (wrapping)\n      { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }\n    else\n      { return widgetsHeight + th }\n  }\n}\n\nfunction estimateLineHeights(cm) {\n  var doc = cm.doc, est = estimateHeight(cm)\n  doc.iter(function (line) {\n    var estHeight = est(line)\n    if (estHeight != line.height) { updateLineHeight(line, estHeight) }\n  })\n}\n\n// Given a mouse event, find the corresponding position. If liberal\n// is false, it checks whether a gutter or scrollbar was clicked,\n// and returns null if it was. forRect is used by rectangular\n// selections, and tries to estimate a character position even for\n// coordinates beyond the right of the text.\nfunction posFromMouse(cm, e, liberal, forRect) {\n  var display = cm.display\n  if (!liberal && e_target(e).getAttribute(\"cm-not-content\") == \"true\") { return null }\n\n  var x, y, space = display.lineSpace.getBoundingClientRect()\n  // Fails unpredictably on IE[67] when mouse is dragged around quickly.\n  try { x = e.clientX - space.left; y = e.clientY - space.top }\n  catch (e) { return null }\n  var coords = coordsChar(cm, x, y), line\n  if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {\n    var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length\n    coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))\n  }\n  return coords\n}\n\n// Find the view element corresponding to a given line. Return null\n// when the line isn't visible.\nfunction findViewIndex(cm, n) {\n  if (n >= cm.display.viewTo) { return null }\n  n -= cm.display.viewFrom\n  if (n < 0) { return null }\n  var view = cm.display.view\n  for (var i = 0; i < view.length; i++) {\n    n -= view[i].size\n    if (n < 0) { return i }\n  }\n}\n\nfunction updateSelection(cm) {\n  cm.display.input.showSelection(cm.display.input.prepareSelection())\n}\n\nfunction prepareSelection(cm, primary) {\n  if ( primary === void 0 ) primary = true;\n\n  var doc = cm.doc, result = {}\n  var curFragment = result.cursors = document.createDocumentFragment()\n  var selFragment = result.selection = document.createDocumentFragment()\n\n  for (var i = 0; i < doc.sel.ranges.length; i++) {\n    if (!primary && i == doc.sel.primIndex) { continue }\n    var range = doc.sel.ranges[i]\n    if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }\n    var collapsed = range.empty()\n    if (collapsed || cm.options.showCursorWhenSelecting)\n      { drawSelectionCursor(cm, range.head, curFragment) }\n    if (!collapsed)\n      { drawSelectionRange(cm, range, selFragment) }\n  }\n  return result\n}\n\n// Draws a cursor for the given range\nfunction drawSelectionCursor(cm, head, output) {\n  var pos = cursorCoords(cm, head, \"div\", null, null, !cm.options.singleCursorHeightPerLine)\n\n  var cursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor\"))\n  cursor.style.left = pos.left + \"px\"\n  cursor.style.top = pos.top + \"px\"\n  cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + \"px\"\n\n  if (pos.other) {\n    // Secondary cursor, shown when on a 'jump' in bi-directional text\n    var otherCursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor CodeMirror-secondarycursor\"))\n    otherCursor.style.display = \"\"\n    otherCursor.style.left = pos.other.left + \"px\"\n    otherCursor.style.top = pos.other.top + \"px\"\n    otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + \"px\"\n  }\n}\n\nfunction cmpCoords(a, b) { return a.top - b.top || a.left - b.left }\n\n// Draws the given range as a highlighted selection\nfunction drawSelectionRange(cm, range, output) {\n  var display = cm.display, doc = cm.doc\n  var fragment = document.createDocumentFragment()\n  var padding = paddingH(cm.display), leftSide = padding.left\n  var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right\n  var docLTR = doc.direction == \"ltr\"\n\n  function add(left, top, width, bottom) {\n    if (top < 0) { top = 0 }\n    top = Math.round(top)\n    bottom = Math.round(bottom)\n    fragment.appendChild(elt(\"div\", null, \"CodeMirror-selected\", (\"position: absolute; left: \" + left + \"px;\\n                             top: \" + top + \"px; width: \" + (width == null ? rightSide - left : width) + \"px;\\n                             height: \" + (bottom - top) + \"px\")))\n  }\n\n  function drawForLine(line, fromArg, toArg) {\n    var lineObj = getLine(doc, line)\n    var lineLen = lineObj.text.length\n    var start, end\n    function coords(ch, bias) {\n      return charCoords(cm, Pos(line, ch), \"div\", lineObj, bias)\n    }\n\n    function wrapX(pos, dir, side) {\n      var extent = wrappedLineExtentChar(cm, lineObj, null, pos)\n      var prop = (dir == \"ltr\") == (side == \"after\") ? \"left\" : \"right\"\n      var ch = side == \"after\" ? extent.begin : extent.end - (/\\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)\n      return coords(ch, prop)[prop]\n    }\n\n    var order = getOrder(lineObj, doc.direction)\n    iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {\n      var ltr = dir == \"ltr\"\n      var fromPos = coords(from, ltr ? \"left\" : \"right\")\n      var toPos = coords(to - 1, ltr ? \"right\" : \"left\")\n\n      var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen\n      var first = i == 0, last = !order || i == order.length - 1\n      if (toPos.top - fromPos.top <= 3) { // Single line\n        var openLeft = (docLTR ? openStart : openEnd) && first\n        var openRight = (docLTR ? openEnd : openStart) && last\n        var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left\n        var right = openRight ? rightSide : (ltr ? toPos : fromPos).right\n        add(left, fromPos.top, right - left, fromPos.bottom)\n      } else { // Multiple lines\n        var topLeft, topRight, botLeft, botRight\n        if (ltr) {\n          topLeft = docLTR && openStart && first ? leftSide : fromPos.left\n          topRight = docLTR ? rightSide : wrapX(from, dir, \"before\")\n          botLeft = docLTR ? leftSide : wrapX(to, dir, \"after\")\n          botRight = docLTR && openEnd && last ? rightSide : toPos.right\n        } else {\n          topLeft = !docLTR ? leftSide : wrapX(from, dir, \"before\")\n          topRight = !docLTR && openStart && first ? rightSide : fromPos.right\n          botLeft = !docLTR && openEnd && last ? leftSide : toPos.left\n          botRight = !docLTR ? rightSide : wrapX(to, dir, \"after\")\n        }\n        add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)\n        if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }\n        add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)\n      }\n\n      if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }\n      if (cmpCoords(toPos, start) < 0) { start = toPos }\n      if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }\n      if (cmpCoords(toPos, end) < 0) { end = toPos }\n    })\n    return {start: start, end: end}\n  }\n\n  var sFrom = range.from(), sTo = range.to()\n  if (sFrom.line == sTo.line) {\n    drawForLine(sFrom.line, sFrom.ch, sTo.ch)\n  } else {\n    var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)\n    var singleVLine = visualLine(fromLine) == visualLine(toLine)\n    var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end\n    var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start\n    if (singleVLine) {\n      if (leftEnd.top < rightStart.top - 2) {\n        add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)\n        add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)\n      } else {\n        add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)\n      }\n    }\n    if (leftEnd.bottom < rightStart.top)\n      { add(leftSide, leftEnd.bottom, null, rightStart.top) }\n  }\n\n  output.appendChild(fragment)\n}\n\n// Cursor-blinking\nfunction restartBlink(cm) {\n  if (!cm.state.focused) { return }\n  var display = cm.display\n  clearInterval(display.blinker)\n  var on = true\n  display.cursorDiv.style.visibility = \"\"\n  if (cm.options.cursorBlinkRate > 0)\n    { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? \"\" : \"hidden\"; },\n      cm.options.cursorBlinkRate) }\n  else if (cm.options.cursorBlinkRate < 0)\n    { display.cursorDiv.style.visibility = \"hidden\" }\n}\n\nfunction ensureFocus(cm) {\n  if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }\n}\n\nfunction delayBlurEvent(cm) {\n  cm.state.delayingBlurEvent = true\n  setTimeout(function () { if (cm.state.delayingBlurEvent) {\n    cm.state.delayingBlurEvent = false\n    onBlur(cm)\n  } }, 100)\n}\n\nfunction onFocus(cm, e) {\n  if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }\n\n  if (cm.options.readOnly == \"nocursor\") { return }\n  if (!cm.state.focused) {\n    signal(cm, \"focus\", cm, e)\n    cm.state.focused = true\n    addClass(cm.display.wrapper, \"CodeMirror-focused\")\n    // This test prevents this from firing when a context\n    // menu is closed (since the input reset would kill the\n    // select-all detection hack)\n    if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {\n      cm.display.input.reset()\n      if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730\n    }\n    cm.display.input.receivedFocus()\n  }\n  restartBlink(cm)\n}\nfunction onBlur(cm, e) {\n  if (cm.state.delayingBlurEvent) { return }\n\n  if (cm.state.focused) {\n    signal(cm, \"blur\", cm, e)\n    cm.state.focused = false\n    rmClass(cm.display.wrapper, \"CodeMirror-focused\")\n  }\n  clearInterval(cm.display.blinker)\n  setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)\n}\n\n// Read the actual heights of the rendered lines, and update their\n// stored heights to match.\nfunction updateHeightsInViewport(cm) {\n  var display = cm.display\n  var prevBottom = display.lineDiv.offsetTop\n  for (var i = 0; i < display.view.length; i++) {\n    var cur = display.view[i], height = (void 0)\n    if (cur.hidden) { continue }\n    if (ie && ie_version < 8) {\n      var bot = cur.node.offsetTop + cur.node.offsetHeight\n      height = bot - prevBottom\n      prevBottom = bot\n    } else {\n      var box = cur.node.getBoundingClientRect()\n      height = box.bottom - box.top\n    }\n    var diff = cur.line.height - height\n    if (height < 2) { height = textHeight(display) }\n    if (diff > .005 || diff < -.005) {\n      updateLineHeight(cur.line, height)\n      updateWidgetHeight(cur.line)\n      if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)\n        { updateWidgetHeight(cur.rest[j]) } }\n    }\n  }\n}\n\n// Read and store the height of line widgets associated with the\n// given line.\nfunction updateWidgetHeight(line) {\n  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {\n    var w = line.widgets[i], parent = w.node.parentNode\n    if (parent) { w.height = parent.offsetHeight }\n  } }\n}\n\n// Compute the lines that are visible in a given viewport (defaults\n// the the current scroll position). viewport may contain top,\n// height, and ensure (see op.scrollToPos) properties.\nfunction visibleLines(display, doc, viewport) {\n  var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop\n  top = Math.floor(top - paddingTop(display))\n  var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight\n\n  var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)\n  // Ensure is a {from: {line, ch}, to: {line, ch}} object, and\n  // forces those lines into the viewport (if possible).\n  if (viewport && viewport.ensure) {\n    var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line\n    if (ensureFrom < from) {\n      from = ensureFrom\n      to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)\n    } else if (Math.min(ensureTo, doc.lastLine()) >= to) {\n      from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)\n      to = ensureTo\n    }\n  }\n  return {from: from, to: Math.max(to, from + 1)}\n}\n\n// Re-align line numbers and gutter marks to compensate for\n// horizontal scrolling.\nfunction alignHorizontally(cm) {\n  var display = cm.display, view = display.view\n  if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }\n  var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft\n  var gutterW = display.gutters.offsetWidth, left = comp + \"px\"\n  for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {\n    if (cm.options.fixedGutter) {\n      if (view[i].gutter)\n        { view[i].gutter.style.left = left }\n      if (view[i].gutterBackground)\n        { view[i].gutterBackground.style.left = left }\n    }\n    var align = view[i].alignable\n    if (align) { for (var j = 0; j < align.length; j++)\n      { align[j].style.left = left } }\n  } }\n  if (cm.options.fixedGutter)\n    { display.gutters.style.left = (comp + gutterW) + \"px\" }\n}\n\n// Used to ensure that the line number gutter is still the right\n// size for the current document size. Returns true when an update\n// is needed.\nfunction maybeUpdateLineNumberWidth(cm) {\n  if (!cm.options.lineNumbers) { return false }\n  var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display\n  if (last.length != display.lineNumChars) {\n    var test = display.measure.appendChild(elt(\"div\", [elt(\"div\", last)],\n                                               \"CodeMirror-linenumber CodeMirror-gutter-elt\"))\n    var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW\n    display.lineGutter.style.width = \"\"\n    display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1\n    display.lineNumWidth = display.lineNumInnerWidth + padding\n    display.lineNumChars = display.lineNumInnerWidth ? last.length : -1\n    display.lineGutter.style.width = display.lineNumWidth + \"px\"\n    updateGutterSpace(cm)\n    return true\n  }\n  return false\n}\n\n// SCROLLING THINGS INTO VIEW\n\n// If an editor sits on the top or bottom of the window, partially\n// scrolled out of view, this ensures that the cursor is visible.\nfunction maybeScrollWindow(cm, rect) {\n  if (signalDOMEvent(cm, \"scrollCursorIntoView\")) { return }\n\n  var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null\n  if (rect.top + box.top < 0) { doScroll = true }\n  else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }\n  if (doScroll != null && !phantom) {\n    var scrollNode = elt(\"div\", \"\\u200b\", null, (\"position: absolute;\\n                         top: \" + (rect.top - display.viewOffset - paddingTop(cm.display)) + \"px;\\n                         height: \" + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + \"px;\\n                         left: \" + (rect.left) + \"px; width: \" + (Math.max(2, rect.right - rect.left)) + \"px;\"))\n    cm.display.lineSpace.appendChild(scrollNode)\n    scrollNode.scrollIntoView(doScroll)\n    cm.display.lineSpace.removeChild(scrollNode)\n  }\n}\n\n// Scroll a given position into view (immediately), verifying that\n// it actually became visible (as line heights are accurately\n// measured, the position of something may 'drift' during drawing).\nfunction scrollPosIntoView(cm, pos, end, margin) {\n  if (margin == null) { margin = 0 }\n  var rect\n  if (!cm.options.lineWrapping && pos == end) {\n    // Set pos and end to the cursor positions around the character pos sticks to\n    // If pos.sticky == \"before\", that is around pos.ch - 1, otherwise around pos.ch\n    // If pos == Pos(_, 0, \"before\"), pos and end are unchanged\n    pos = pos.ch ? Pos(pos.line, pos.sticky == \"before\" ? pos.ch - 1 : pos.ch, \"after\") : pos\n    end = pos.sticky == \"before\" ? Pos(pos.line, pos.ch + 1, \"before\") : pos\n  }\n  for (var limit = 0; limit < 5; limit++) {\n    var changed = false\n    var coords = cursorCoords(cm, pos)\n    var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)\n    rect = {left: Math.min(coords.left, endCoords.left),\n            top: Math.min(coords.top, endCoords.top) - margin,\n            right: Math.max(coords.left, endCoords.left),\n            bottom: Math.max(coords.bottom, endCoords.bottom) + margin}\n    var scrollPos = calculateScrollPos(cm, rect)\n    var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft\n    if (scrollPos.scrollTop != null) {\n      updateScrollTop(cm, scrollPos.scrollTop)\n      if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }\n    }\n    if (scrollPos.scrollLeft != null) {\n      setScrollLeft(cm, scrollPos.scrollLeft)\n      if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }\n    }\n    if (!changed) { break }\n  }\n  return rect\n}\n\n// Scroll a given set of coordinates into view (immediately).\nfunction scrollIntoView(cm, rect) {\n  var scrollPos = calculateScrollPos(cm, rect)\n  if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop) }\n  if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }\n}\n\n// Calculate a new scroll position needed to scroll the given\n// rectangle into view. Returns an object with scrollTop and\n// scrollLeft properties. When these are undefined, the\n// vertical/horizontal position does not need to be adjusted.\nfunction calculateScrollPos(cm, rect) {\n  var display = cm.display, snapMargin = textHeight(cm.display)\n  if (rect.top < 0) { rect.top = 0 }\n  var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop\n  var screen = displayHeight(cm), result = {}\n  if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen }\n  var docBottom = cm.doc.height + paddingVert(display)\n  var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin\n  if (rect.top < screentop) {\n    result.scrollTop = atTop ? 0 : rect.top\n  } else if (rect.bottom > screentop + screen) {\n    var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)\n    if (newTop != screentop) { result.scrollTop = newTop }\n  }\n\n  var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft\n  var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)\n  var tooWide = rect.right - rect.left > screenw\n  if (tooWide) { rect.right = rect.left + screenw }\n  if (rect.left < 10)\n    { result.scrollLeft = 0 }\n  else if (rect.left < screenleft)\n    { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) }\n  else if (rect.right > screenw + screenleft - 3)\n    { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw }\n  return result\n}\n\n// Store a relative adjustment to the scroll position in the current\n// operation (to be applied when the operation finishes).\nfunction addToScrollTop(cm, top) {\n  if (top == null) { return }\n  resolveScrollToPos(cm)\n  cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top\n}\n\n// Make sure that at the end of the operation the current cursor is\n// shown.\nfunction ensureCursorVisible(cm) {\n  resolveScrollToPos(cm)\n  var cur = cm.getCursor()\n  cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}\n}\n\nfunction scrollToCoords(cm, x, y) {\n  if (x != null || y != null) { resolveScrollToPos(cm) }\n  if (x != null) { cm.curOp.scrollLeft = x }\n  if (y != null) { cm.curOp.scrollTop = y }\n}\n\nfunction scrollToRange(cm, range) {\n  resolveScrollToPos(cm)\n  cm.curOp.scrollToPos = range\n}\n\n// When an operation has its scrollToPos property set, and another\n// scroll action is applied before the end of the operation, this\n// 'simulates' scrolling that position into view in a cheap way, so\n// that the effect of intermediate scroll commands is not ignored.\nfunction resolveScrollToPos(cm) {\n  var range = cm.curOp.scrollToPos\n  if (range) {\n    cm.curOp.scrollToPos = null\n    var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)\n    scrollToCoordsRange(cm, from, to, range.margin)\n  }\n}\n\nfunction scrollToCoordsRange(cm, from, to, margin) {\n  var sPos = calculateScrollPos(cm, {\n    left: Math.min(from.left, to.left),\n    top: Math.min(from.top, to.top) - margin,\n    right: Math.max(from.right, to.right),\n    bottom: Math.max(from.bottom, to.bottom) + margin\n  })\n  scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop)\n}\n\n// Sync the scrollable area and scrollbars, ensure the viewport\n// covers the visible area.\nfunction updateScrollTop(cm, val) {\n  if (Math.abs(cm.doc.scrollTop - val) < 2) { return }\n  if (!gecko) { updateDisplaySimple(cm, {top: val}) }\n  setScrollTop(cm, val, true)\n  if (gecko) { updateDisplaySimple(cm) }\n  startWorker(cm, 100)\n}\n\nfunction setScrollTop(cm, val, forceScroll) {\n  val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)\n  if (cm.display.scroller.scrollTop == val && !forceScroll) { return }\n  cm.doc.scrollTop = val\n  cm.display.scrollbars.setScrollTop(val)\n  if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }\n}\n\n// Sync scroller and scrollbar, ensure the gutter elements are\n// aligned.\nfunction setScrollLeft(cm, val, isScroller, forceScroll) {\n  val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)\n  if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }\n  cm.doc.scrollLeft = val\n  alignHorizontally(cm)\n  if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }\n  cm.display.scrollbars.setScrollLeft(val)\n}\n\n// SCROLLBARS\n\n// Prepare DOM reads needed to update the scrollbars. Done in one\n// shot to minimize update/measure roundtrips.\nfunction measureForScrollbars(cm) {\n  var d = cm.display, gutterW = d.gutters.offsetWidth\n  var docH = Math.round(cm.doc.height + paddingVert(cm.display))\n  return {\n    clientHeight: d.scroller.clientHeight,\n    viewHeight: d.wrapper.clientHeight,\n    scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,\n    viewWidth: d.wrapper.clientWidth,\n    barLeft: cm.options.fixedGutter ? gutterW : 0,\n    docHeight: docH,\n    scrollHeight: docH + scrollGap(cm) + d.barHeight,\n    nativeBarWidth: d.nativeBarWidth,\n    gutterWidth: gutterW\n  }\n}\n\nvar NativeScrollbars = function(place, scroll, cm) {\n  this.cm = cm\n  var vert = this.vert = elt(\"div\", [elt(\"div\", null, null, \"min-width: 1px\")], \"CodeMirror-vscrollbar\")\n  var horiz = this.horiz = elt(\"div\", [elt(\"div\", null, null, \"height: 100%; min-height: 1px\")], \"CodeMirror-hscrollbar\")\n  vert.tabIndex = horiz.tabIndex = -1\n  place(vert); place(horiz)\n\n  on(vert, \"scroll\", function () {\n    if (vert.clientHeight) { scroll(vert.scrollTop, \"vertical\") }\n  })\n  on(horiz, \"scroll\", function () {\n    if (horiz.clientWidth) { scroll(horiz.scrollLeft, \"horizontal\") }\n  })\n\n  this.checkedZeroWidth = false\n  // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).\n  if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = \"18px\" }\n};\n\nNativeScrollbars.prototype.update = function (measure) {\n  var needsH = measure.scrollWidth > measure.clientWidth + 1\n  var needsV = measure.scrollHeight > measure.clientHeight + 1\n  var sWidth = measure.nativeBarWidth\n\n  if (needsV) {\n    this.vert.style.display = \"block\"\n    this.vert.style.bottom = needsH ? sWidth + \"px\" : \"0\"\n    var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)\n    // A bug in IE8 can cause this value to be negative, so guard it.\n    this.vert.firstChild.style.height =\n      Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + \"px\"\n  } else {\n    this.vert.style.display = \"\"\n    this.vert.firstChild.style.height = \"0\"\n  }\n\n  if (needsH) {\n    this.horiz.style.display = \"block\"\n    this.horiz.style.right = needsV ? sWidth + \"px\" : \"0\"\n    this.horiz.style.left = measure.barLeft + \"px\"\n    var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)\n    this.horiz.firstChild.style.width =\n      Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + \"px\"\n  } else {\n    this.horiz.style.display = \"\"\n    this.horiz.firstChild.style.width = \"0\"\n  }\n\n  if (!this.checkedZeroWidth && measure.clientHeight > 0) {\n    if (sWidth == 0) { this.zeroWidthHack() }\n    this.checkedZeroWidth = true\n  }\n\n  return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}\n};\n\nNativeScrollbars.prototype.setScrollLeft = function (pos) {\n  if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }\n  if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, \"horiz\") }\n};\n\nNativeScrollbars.prototype.setScrollTop = function (pos) {\n  if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }\n  if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, \"vert\") }\n};\n\nNativeScrollbars.prototype.zeroWidthHack = function () {\n  var w = mac && !mac_geMountainLion ? \"12px\" : \"18px\"\n  this.horiz.style.height = this.vert.style.width = w\n  this.horiz.style.pointerEvents = this.vert.style.pointerEvents = \"none\"\n  this.disableHoriz = new Delayed\n  this.disableVert = new Delayed\n};\n\nNativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {\n  bar.style.pointerEvents = \"auto\"\n  function maybeDisable() {\n    // To find out whether the scrollbar is still visible, we\n    // check whether the element under the pixel in the bottom\n    // right corner of the scrollbar box is the scrollbar box\n    // itself (when the bar is still visible) or its filler child\n    // (when the bar is hidden). If it is still visible, we keep\n    // it enabled, if it's hidden, we disable pointer events.\n    var box = bar.getBoundingClientRect()\n    var elt = type == \"vert\" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)\n        : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)\n    if (elt != bar) { bar.style.pointerEvents = \"none\" }\n    else { delay.set(1000, maybeDisable) }\n  }\n  delay.set(1000, maybeDisable)\n};\n\nNativeScrollbars.prototype.clear = function () {\n  var parent = this.horiz.parentNode\n  parent.removeChild(this.horiz)\n  parent.removeChild(this.vert)\n};\n\nvar NullScrollbars = function () {};\n\nNullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };\nNullScrollbars.prototype.setScrollLeft = function () {};\nNullScrollbars.prototype.setScrollTop = function () {};\nNullScrollbars.prototype.clear = function () {};\n\nfunction updateScrollbars(cm, measure) {\n  if (!measure) { measure = measureForScrollbars(cm) }\n  var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight\n  updateScrollbarsInner(cm, measure)\n  for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {\n    if (startWidth != cm.display.barWidth && cm.options.lineWrapping)\n      { updateHeightsInViewport(cm) }\n    updateScrollbarsInner(cm, measureForScrollbars(cm))\n    startWidth = cm.display.barWidth; startHeight = cm.display.barHeight\n  }\n}\n\n// Re-synchronize the fake scrollbars with the actual size of the\n// content.\nfunction updateScrollbarsInner(cm, measure) {\n  var d = cm.display\n  var sizes = d.scrollbars.update(measure)\n\n  d.sizer.style.paddingRight = (d.barWidth = sizes.right) + \"px\"\n  d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + \"px\"\n  d.heightForcer.style.borderBottom = sizes.bottom + \"px solid transparent\"\n\n  if (sizes.right && sizes.bottom) {\n    d.scrollbarFiller.style.display = \"block\"\n    d.scrollbarFiller.style.height = sizes.bottom + \"px\"\n    d.scrollbarFiller.style.width = sizes.right + \"px\"\n  } else { d.scrollbarFiller.style.display = \"\" }\n  if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {\n    d.gutterFiller.style.display = \"block\"\n    d.gutterFiller.style.height = sizes.bottom + \"px\"\n    d.gutterFiller.style.width = measure.gutterWidth + \"px\"\n  } else { d.gutterFiller.style.display = \"\" }\n}\n\nvar scrollbarModel = {\"native\": NativeScrollbars, \"null\": NullScrollbars}\n\nfunction initScrollbars(cm) {\n  if (cm.display.scrollbars) {\n    cm.display.scrollbars.clear()\n    if (cm.display.scrollbars.addClass)\n      { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }\n  }\n\n  cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {\n    cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)\n    // Prevent clicks in the scrollbars from killing focus\n    on(node, \"mousedown\", function () {\n      if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }\n    })\n    node.setAttribute(\"cm-not-content\", \"true\")\n  }, function (pos, axis) {\n    if (axis == \"horizontal\") { setScrollLeft(cm, pos) }\n    else { updateScrollTop(cm, pos) }\n  }, cm)\n  if (cm.display.scrollbars.addClass)\n    { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }\n}\n\n// Operations are used to wrap a series of changes to the editor\n// state in such a way that each change won't have to update the\n// cursor and display (which would be awkward, slow, and\n// error-prone). Instead, display updates are batched and then all\n// combined and executed at once.\n\nvar nextOpId = 0\n// Start a new operation.\nfunction startOperation(cm) {\n  cm.curOp = {\n    cm: cm,\n    viewChanged: false,      // Flag that indicates that lines might need to be redrawn\n    startHeight: cm.doc.height, // Used to detect need to update scrollbar\n    forceUpdate: false,      // Used to force a redraw\n    updateInput: null,       // Whether to reset the input textarea\n    typing: false,           // Whether this reset should be careful to leave existing text (for compositing)\n    changeObjs: null,        // Accumulated changes, for firing change events\n    cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on\n    cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already\n    selectionChanged: false, // Whether the selection needs to be redrawn\n    updateMaxLine: false,    // Set when the widest line needs to be determined anew\n    scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet\n    scrollToPos: null,       // Used to scroll to a specific position\n    focus: false,\n    id: ++nextOpId           // Unique ID\n  }\n  pushOperation(cm.curOp)\n}\n\n// Finish an operation, updating the display and signalling delayed events\nfunction endOperation(cm) {\n  var op = cm.curOp\n  finishOperation(op, function (group) {\n    for (var i = 0; i < group.ops.length; i++)\n      { group.ops[i].cm.curOp = null }\n    endOperations(group)\n  })\n}\n\n// The DOM updates done when an operation finishes are batched so\n// that the minimum number of relayouts are required.\nfunction endOperations(group) {\n  var ops = group.ops\n  for (var i = 0; i < ops.length; i++) // Read DOM\n    { endOperation_R1(ops[i]) }\n  for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)\n    { endOperation_W1(ops[i$1]) }\n  for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM\n    { endOperation_R2(ops[i$2]) }\n  for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)\n    { endOperation_W2(ops[i$3]) }\n  for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM\n    { endOperation_finish(ops[i$4]) }\n}\n\nfunction endOperation_R1(op) {\n  var cm = op.cm, display = cm.display\n  maybeClipScrollbars(cm)\n  if (op.updateMaxLine) { findMaxLine(cm) }\n\n  op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||\n    op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||\n                       op.scrollToPos.to.line >= display.viewTo) ||\n    display.maxLineChanged && cm.options.lineWrapping\n  op.update = op.mustUpdate &&\n    new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)\n}\n\nfunction endOperation_W1(op) {\n  op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)\n}\n\nfunction endOperation_R2(op) {\n  var cm = op.cm, display = cm.display\n  if (op.updatedDisplay) { updateHeightsInViewport(cm) }\n\n  op.barMeasure = measureForScrollbars(cm)\n\n  // If the max line changed since it was last measured, measure it,\n  // and ensure the document's width matches it.\n  // updateDisplay_W2 will use these properties to do the actual resizing\n  if (display.maxLineChanged && !cm.options.lineWrapping) {\n    op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3\n    cm.display.sizerWidth = op.adjustWidthTo\n    op.barMeasure.scrollWidth =\n      Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)\n    op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))\n  }\n\n  if (op.updatedDisplay || op.selectionChanged)\n    { op.preparedSelection = display.input.prepareSelection() }\n}\n\nfunction endOperation_W2(op) {\n  var cm = op.cm\n\n  if (op.adjustWidthTo != null) {\n    cm.display.sizer.style.minWidth = op.adjustWidthTo + \"px\"\n    if (op.maxScrollLeft < cm.doc.scrollLeft)\n      { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }\n    cm.display.maxLineChanged = false\n  }\n\n  var takeFocus = op.focus && op.focus == activeElt()\n  if (op.preparedSelection)\n    { cm.display.input.showSelection(op.preparedSelection, takeFocus) }\n  if (op.updatedDisplay || op.startHeight != cm.doc.height)\n    { updateScrollbars(cm, op.barMeasure) }\n  if (op.updatedDisplay)\n    { setDocumentHeight(cm, op.barMeasure) }\n\n  if (op.selectionChanged) { restartBlink(cm) }\n\n  if (cm.state.focused && op.updateInput)\n    { cm.display.input.reset(op.typing) }\n  if (takeFocus) { ensureFocus(op.cm) }\n}\n\nfunction endOperation_finish(op) {\n  var cm = op.cm, display = cm.display, doc = cm.doc\n\n  if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }\n\n  // Abort mouse wheel delta measurement, when scrolling explicitly\n  if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))\n    { display.wheelStartX = display.wheelStartY = null }\n\n  // Propagate the scroll position to the actual DOM scroller\n  if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll) }\n\n  if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true) }\n  // If we need to scroll a specific position into view, do so.\n  if (op.scrollToPos) {\n    var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),\n                                 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)\n    maybeScrollWindow(cm, rect)\n  }\n\n  // Fire events for markers that are hidden/unidden by editing or\n  // undoing\n  var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers\n  if (hidden) { for (var i = 0; i < hidden.length; ++i)\n    { if (!hidden[i].lines.length) { signal(hidden[i], \"hide\") } } }\n  if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)\n    { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], \"unhide\") } } }\n\n  if (display.wrapper.offsetHeight)\n    { doc.scrollTop = cm.display.scroller.scrollTop }\n\n  // Fire change events, and delayed event handlers\n  if (op.changeObjs)\n    { signal(cm, \"changes\", cm, op.changeObjs) }\n  if (op.update)\n    { op.update.finish() }\n}\n\n// Run the given function in an operation\nfunction runInOp(cm, f) {\n  if (cm.curOp) { return f() }\n  startOperation(cm)\n  try { return f() }\n  finally { endOperation(cm) }\n}\n// Wraps a function in an operation. Returns the wrapped function.\nfunction operation(cm, f) {\n  return function() {\n    if (cm.curOp) { return f.apply(cm, arguments) }\n    startOperation(cm)\n    try { return f.apply(cm, arguments) }\n    finally { endOperation(cm) }\n  }\n}\n// Used to add methods to editor and doc instances, wrapping them in\n// operations.\nfunction methodOp(f) {\n  return function() {\n    if (this.curOp) { return f.apply(this, arguments) }\n    startOperation(this)\n    try { return f.apply(this, arguments) }\n    finally { endOperation(this) }\n  }\n}\nfunction docMethodOp(f) {\n  return function() {\n    var cm = this.cm\n    if (!cm || cm.curOp) { return f.apply(this, arguments) }\n    startOperation(cm)\n    try { return f.apply(this, arguments) }\n    finally { endOperation(cm) }\n  }\n}\n\n// Updates the display.view data structure for a given change to the\n// document. From and to are in pre-change coordinates. Lendiff is\n// the amount of lines added or subtracted by the change. This is\n// used for changes that span multiple lines, or change the way\n// lines are divided into visual lines. regLineChange (below)\n// registers single-line changes.\nfunction regChange(cm, from, to, lendiff) {\n  if (from == null) { from = cm.doc.first }\n  if (to == null) { to = cm.doc.first + cm.doc.size }\n  if (!lendiff) { lendiff = 0 }\n\n  var display = cm.display\n  if (lendiff && to < display.viewTo &&\n      (display.updateLineNumbers == null || display.updateLineNumbers > from))\n    { display.updateLineNumbers = from }\n\n  cm.curOp.viewChanged = true\n\n  if (from >= display.viewTo) { // Change after\n    if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)\n      { resetView(cm) }\n  } else if (to <= display.viewFrom) { // Change before\n    if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {\n      resetView(cm)\n    } else {\n      display.viewFrom += lendiff\n      display.viewTo += lendiff\n    }\n  } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap\n    resetView(cm)\n  } else if (from <= display.viewFrom) { // Top overlap\n    var cut = viewCuttingPoint(cm, to, to + lendiff, 1)\n    if (cut) {\n      display.view = display.view.slice(cut.index)\n      display.viewFrom = cut.lineN\n      display.viewTo += lendiff\n    } else {\n      resetView(cm)\n    }\n  } else if (to >= display.viewTo) { // Bottom overlap\n    var cut$1 = viewCuttingPoint(cm, from, from, -1)\n    if (cut$1) {\n      display.view = display.view.slice(0, cut$1.index)\n      display.viewTo = cut$1.lineN\n    } else {\n      resetView(cm)\n    }\n  } else { // Gap in the middle\n    var cutTop = viewCuttingPoint(cm, from, from, -1)\n    var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)\n    if (cutTop && cutBot) {\n      display.view = display.view.slice(0, cutTop.index)\n        .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))\n        .concat(display.view.slice(cutBot.index))\n      display.viewTo += lendiff\n    } else {\n      resetView(cm)\n    }\n  }\n\n  var ext = display.externalMeasured\n  if (ext) {\n    if (to < ext.lineN)\n      { ext.lineN += lendiff }\n    else if (from < ext.lineN + ext.size)\n      { display.externalMeasured = null }\n  }\n}\n\n// Register a change to a single line. Type must be one of \"text\",\n// \"gutter\", \"class\", \"widget\"\nfunction regLineChange(cm, line, type) {\n  cm.curOp.viewChanged = true\n  var display = cm.display, ext = cm.display.externalMeasured\n  if (ext && line >= ext.lineN && line < ext.lineN + ext.size)\n    { display.externalMeasured = null }\n\n  if (line < display.viewFrom || line >= display.viewTo) { return }\n  var lineView = display.view[findViewIndex(cm, line)]\n  if (lineView.node == null) { return }\n  var arr = lineView.changes || (lineView.changes = [])\n  if (indexOf(arr, type) == -1) { arr.push(type) }\n}\n\n// Clear the view.\nfunction resetView(cm) {\n  cm.display.viewFrom = cm.display.viewTo = cm.doc.first\n  cm.display.view = []\n  cm.display.viewOffset = 0\n}\n\nfunction viewCuttingPoint(cm, oldN, newN, dir) {\n  var index = findViewIndex(cm, oldN), diff, view = cm.display.view\n  if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)\n    { return {index: index, lineN: newN} }\n  var n = cm.display.viewFrom\n  for (var i = 0; i < index; i++)\n    { n += view[i].size }\n  if (n != oldN) {\n    if (dir > 0) {\n      if (index == view.length - 1) { return null }\n      diff = (n + view[index].size) - oldN\n      index++\n    } else {\n      diff = n - oldN\n    }\n    oldN += diff; newN += diff\n  }\n  while (visualLineNo(cm.doc, newN) != newN) {\n    if (index == (dir < 0 ? 0 : view.length - 1)) { return null }\n    newN += dir * view[index - (dir < 0 ? 1 : 0)].size\n    index += dir\n  }\n  return {index: index, lineN: newN}\n}\n\n// Force the view to cover a given range, adding empty view element\n// or clipping off existing ones as needed.\nfunction adjustView(cm, from, to) {\n  var display = cm.display, view = display.view\n  if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {\n    display.view = buildViewArray(cm, from, to)\n    display.viewFrom = from\n  } else {\n    if (display.viewFrom > from)\n      { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }\n    else if (display.viewFrom < from)\n      { display.view = display.view.slice(findViewIndex(cm, from)) }\n    display.viewFrom = from\n    if (display.viewTo < to)\n      { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }\n    else if (display.viewTo > to)\n      { display.view = display.view.slice(0, findViewIndex(cm, to)) }\n  }\n  display.viewTo = to\n}\n\n// Count the number of lines in the view whose DOM representation is\n// out of date (or nonexistent).\nfunction countDirtyView(cm) {\n  var view = cm.display.view, dirty = 0\n  for (var i = 0; i < view.length; i++) {\n    var lineView = view[i]\n    if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }\n  }\n  return dirty\n}\n\n// HIGHLIGHT WORKER\n\nfunction startWorker(cm, time) {\n  if (cm.doc.highlightFrontier < cm.display.viewTo)\n    { cm.state.highlight.set(time, bind(highlightWorker, cm)) }\n}\n\nfunction highlightWorker(cm) {\n  var doc = cm.doc\n  if (doc.highlightFrontier >= cm.display.viewTo) { return }\n  var end = +new Date + cm.options.workTime\n  var context = getContextBefore(cm, doc.highlightFrontier)\n  var changedLines = []\n\n  doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {\n    if (context.line >= cm.display.viewFrom) { // Visible\n      var oldStyles = line.styles\n      var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null\n      var highlighted = highlightLine(cm, line, context, true)\n      if (resetState) { context.state = resetState }\n      line.styles = highlighted.styles\n      var oldCls = line.styleClasses, newCls = highlighted.classes\n      if (newCls) { line.styleClasses = newCls }\n      else if (oldCls) { line.styleClasses = null }\n      var ischange = !oldStyles || oldStyles.length != line.styles.length ||\n        oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)\n      for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }\n      if (ischange) { changedLines.push(context.line) }\n      line.stateAfter = context.save()\n      context.nextLine()\n    } else {\n      if (line.text.length <= cm.options.maxHighlightLength)\n        { processLine(cm, line.text, context) }\n      line.stateAfter = context.line % 5 == 0 ? context.save() : null\n      context.nextLine()\n    }\n    if (+new Date > end) {\n      startWorker(cm, cm.options.workDelay)\n      return true\n    }\n  })\n  doc.highlightFrontier = context.line\n  doc.modeFrontier = Math.max(doc.modeFrontier, context.line)\n  if (changedLines.length) { runInOp(cm, function () {\n    for (var i = 0; i < changedLines.length; i++)\n      { regLineChange(cm, changedLines[i], \"text\") }\n  }) }\n}\n\n// DISPLAY DRAWING\n\nvar DisplayUpdate = function(cm, viewport, force) {\n  var display = cm.display\n\n  this.viewport = viewport\n  // Store some values that we'll need later (but don't want to force a relayout for)\n  this.visible = visibleLines(display, cm.doc, viewport)\n  this.editorIsHidden = !display.wrapper.offsetWidth\n  this.wrapperHeight = display.wrapper.clientHeight\n  this.wrapperWidth = display.wrapper.clientWidth\n  this.oldDisplayWidth = displayWidth(cm)\n  this.force = force\n  this.dims = getDimensions(cm)\n  this.events = []\n};\n\nDisplayUpdate.prototype.signal = function (emitter, type) {\n  if (hasHandler(emitter, type))\n    { this.events.push(arguments) }\n};\nDisplayUpdate.prototype.finish = function () {\n    var this$1 = this;\n\n  for (var i = 0; i < this.events.length; i++)\n    { signal.apply(null, this$1.events[i]) }\n};\n\nfunction maybeClipScrollbars(cm) {\n  var display = cm.display\n  if (!display.scrollbarsClipped && display.scroller.offsetWidth) {\n    display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth\n    display.heightForcer.style.height = scrollGap(cm) + \"px\"\n    display.sizer.style.marginBottom = -display.nativeBarWidth + \"px\"\n    display.sizer.style.borderRightWidth = scrollGap(cm) + \"px\"\n    display.scrollbarsClipped = true\n  }\n}\n\nfunction selectionSnapshot(cm) {\n  if (cm.hasFocus()) { return null }\n  var active = activeElt()\n  if (!active || !contains(cm.display.lineDiv, active)) { return null }\n  var result = {activeElt: active}\n  if (window.getSelection) {\n    var sel = window.getSelection()\n    if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {\n      result.anchorNode = sel.anchorNode\n      result.anchorOffset = sel.anchorOffset\n      result.focusNode = sel.focusNode\n      result.focusOffset = sel.focusOffset\n    }\n  }\n  return result\n}\n\nfunction restoreSelection(snapshot) {\n  if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }\n  snapshot.activeElt.focus()\n  if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {\n    var sel = window.getSelection(), range = document.createRange()\n    range.setEnd(snapshot.anchorNode, snapshot.anchorOffset)\n    range.collapse(false)\n    sel.removeAllRanges()\n    sel.addRange(range)\n    sel.extend(snapshot.focusNode, snapshot.focusOffset)\n  }\n}\n\n// Does the actual updating of the line display. Bails out\n// (returning false) when there is nothing to be done and forced is\n// false.\nfunction updateDisplayIfNeeded(cm, update) {\n  var display = cm.display, doc = cm.doc\n\n  if (update.editorIsHidden) {\n    resetView(cm)\n    return false\n  }\n\n  // Bail out if the visible area is already rendered and nothing changed.\n  if (!update.force &&\n      update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&\n      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&\n      display.renderedView == display.view && countDirtyView(cm) == 0)\n    { return false }\n\n  if (maybeUpdateLineNumberWidth(cm)) {\n    resetView(cm)\n    update.dims = getDimensions(cm)\n  }\n\n  // Compute a suitable new viewport (from & to)\n  var end = doc.first + doc.size\n  var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)\n  var to = Math.min(end, update.visible.to + cm.options.viewportMargin)\n  if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }\n  if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }\n  if (sawCollapsedSpans) {\n    from = visualLineNo(cm.doc, from)\n    to = visualLineEndNo(cm.doc, to)\n  }\n\n  var different = from != display.viewFrom || to != display.viewTo ||\n    display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth\n  adjustView(cm, from, to)\n\n  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))\n  // Position the mover div to align with the current scroll position\n  cm.display.mover.style.top = display.viewOffset + \"px\"\n\n  var toUpdate = countDirtyView(cm)\n  if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&\n      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))\n    { return false }\n\n  // For big changes, we hide the enclosing element during the\n  // update, since that speeds up the operations on most browsers.\n  var selSnapshot = selectionSnapshot(cm)\n  if (toUpdate > 4) { display.lineDiv.style.display = \"none\" }\n  patchDisplay(cm, display.updateLineNumbers, update.dims)\n  if (toUpdate > 4) { display.lineDiv.style.display = \"\" }\n  display.renderedView = display.view\n  // There might have been a widget with a focused element that got\n  // hidden or updated, if so re-focus it.\n  restoreSelection(selSnapshot)\n\n  // Prevent selection and cursors from interfering with the scroll\n  // width and height.\n  removeChildren(display.cursorDiv)\n  removeChildren(display.selectionDiv)\n  display.gutters.style.height = display.sizer.style.minHeight = 0\n\n  if (different) {\n    display.lastWrapHeight = update.wrapperHeight\n    display.lastWrapWidth = update.wrapperWidth\n    startWorker(cm, 400)\n  }\n\n  display.updateLineNumbers = null\n\n  return true\n}\n\nfunction postUpdateDisplay(cm, update) {\n  var viewport = update.viewport\n\n  for (var first = true;; first = false) {\n    if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {\n      // Clip forced viewport to actual scrollable area.\n      if (viewport && viewport.top != null)\n        { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }\n      // Updated line heights might result in the drawn area not\n      // actually covering the viewport. Keep looping until it does.\n      update.visible = visibleLines(cm.display, cm.doc, viewport)\n      if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)\n        { break }\n    }\n    if (!updateDisplayIfNeeded(cm, update)) { break }\n    updateHeightsInViewport(cm)\n    var barMeasure = measureForScrollbars(cm)\n    updateSelection(cm)\n    updateScrollbars(cm, barMeasure)\n    setDocumentHeight(cm, barMeasure)\n    update.force = false\n  }\n\n  update.signal(cm, \"update\", cm)\n  if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {\n    update.signal(cm, \"viewportChange\", cm, cm.display.viewFrom, cm.display.viewTo)\n    cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo\n  }\n}\n\nfunction updateDisplaySimple(cm, viewport) {\n  var update = new DisplayUpdate(cm, viewport)\n  if (updateDisplayIfNeeded(cm, update)) {\n    updateHeightsInViewport(cm)\n    postUpdateDisplay(cm, update)\n    var barMeasure = measureForScrollbars(cm)\n    updateSelection(cm)\n    updateScrollbars(cm, barMeasure)\n    setDocumentHeight(cm, barMeasure)\n    update.finish()\n  }\n}\n\n// Sync the actual display DOM structure with display.view, removing\n// nodes for lines that are no longer in view, and creating the ones\n// that are not there yet, and updating the ones that are out of\n// date.\nfunction patchDisplay(cm, updateNumbersFrom, dims) {\n  var display = cm.display, lineNumbers = cm.options.lineNumbers\n  var container = display.lineDiv, cur = container.firstChild\n\n  function rm(node) {\n    var next = node.nextSibling\n    // Works around a throw-scroll bug in OS X Webkit\n    if (webkit && mac && cm.display.currentWheelTarget == node)\n      { node.style.display = \"none\" }\n    else\n      { node.parentNode.removeChild(node) }\n    return next\n  }\n\n  var view = display.view, lineN = display.viewFrom\n  // Loop over the elements in the view, syncing cur (the DOM nodes\n  // in display.lineDiv) with the view as we go.\n  for (var i = 0; i < view.length; i++) {\n    var lineView = view[i]\n    if (lineView.hidden) {\n    } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet\n      var node = buildLineElement(cm, lineView, lineN, dims)\n      container.insertBefore(node, cur)\n    } else { // Already drawn\n      while (cur != lineView.node) { cur = rm(cur) }\n      var updateNumber = lineNumbers && updateNumbersFrom != null &&\n        updateNumbersFrom <= lineN && lineView.lineNumber\n      if (lineView.changes) {\n        if (indexOf(lineView.changes, \"gutter\") > -1) { updateNumber = false }\n        updateLineForChanges(cm, lineView, lineN, dims)\n      }\n      if (updateNumber) {\n        removeChildren(lineView.lineNumber)\n        lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))\n      }\n      cur = lineView.node.nextSibling\n    }\n    lineN += lineView.size\n  }\n  while (cur) { cur = rm(cur) }\n}\n\nfunction updateGutterSpace(cm) {\n  var width = cm.display.gutters.offsetWidth\n  cm.display.sizer.style.marginLeft = width + \"px\"\n}\n\nfunction setDocumentHeight(cm, measure) {\n  cm.display.sizer.style.minHeight = measure.docHeight + \"px\"\n  cm.display.heightForcer.style.top = measure.docHeight + \"px\"\n  cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + \"px\"\n}\n\n// Rebuild the gutter elements, ensure the margin to the left of the\n// code matches their width.\nfunction updateGutters(cm) {\n  var gutters = cm.display.gutters, specs = cm.options.gutters\n  removeChildren(gutters)\n  var i = 0\n  for (; i < specs.length; ++i) {\n    var gutterClass = specs[i]\n    var gElt = gutters.appendChild(elt(\"div\", null, \"CodeMirror-gutter \" + gutterClass))\n    if (gutterClass == \"CodeMirror-linenumbers\") {\n      cm.display.lineGutter = gElt\n      gElt.style.width = (cm.display.lineNumWidth || 1) + \"px\"\n    }\n  }\n  gutters.style.display = i ? \"\" : \"none\"\n  updateGutterSpace(cm)\n}\n\n// Make sure the gutters options contains the element\n// \"CodeMirror-linenumbers\" when the lineNumbers option is true.\nfunction setGuttersForLineNumbers(options) {\n  var found = indexOf(options.gutters, \"CodeMirror-linenumbers\")\n  if (found == -1 && options.lineNumbers) {\n    options.gutters = options.gutters.concat([\"CodeMirror-linenumbers\"])\n  } else if (found > -1 && !options.lineNumbers) {\n    options.gutters = options.gutters.slice(0)\n    options.gutters.splice(found, 1)\n  }\n}\n\nvar wheelSamples = 0;\nvar wheelPixelsPerUnit = null;\n// Fill in a browser-detected starting value on browsers where we\n// know one. These don't have to be accurate -- the result of them\n// being wrong would just be a slight flicker on the first wheel\n// scroll (if it is large enough).\nif (ie) { wheelPixelsPerUnit = -.53 }\nelse if (gecko) { wheelPixelsPerUnit = 15 }\nelse if (chrome) { wheelPixelsPerUnit = -.7 }\nelse if (safari) { wheelPixelsPerUnit = -1/3 }\n\nfunction wheelEventDelta(e) {\n  var dx = e.wheelDeltaX, dy = e.wheelDeltaY\n  if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }\n  if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }\n  else if (dy == null) { dy = e.wheelDelta }\n  return {x: dx, y: dy}\n}\nfunction wheelEventPixels(e) {\n  var delta = wheelEventDelta(e)\n  delta.x *= wheelPixelsPerUnit\n  delta.y *= wheelPixelsPerUnit\n  return delta\n}\n\nfunction onScrollWheel(cm, e) {\n  var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y\n\n  var display = cm.display, scroll = display.scroller\n  // Quit if there's nothing to scroll here\n  var canScrollX = scroll.scrollWidth > scroll.clientWidth\n  var canScrollY = scroll.scrollHeight > scroll.clientHeight\n  if (!(dx && canScrollX || dy && canScrollY)) { return }\n\n  // Webkit browsers on OS X abort momentum scrolls when the target\n  // of the scroll event is removed from the scrollable element.\n  // This hack (see related code in patchDisplay) makes sure the\n  // element is kept around.\n  if (dy && mac && webkit) {\n    outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {\n      for (var i = 0; i < view.length; i++) {\n        if (view[i].node == cur) {\n          cm.display.currentWheelTarget = cur\n          break outer\n        }\n      }\n    }\n  }\n\n  // On some browsers, horizontal scrolling will cause redraws to\n  // happen before the gutter has been realigned, causing it to\n  // wriggle around in a most unseemly way. When we have an\n  // estimated pixels/delta value, we just handle horizontal\n  // scrolling entirely here. It'll be slightly off from native, but\n  // better than glitching out.\n  if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {\n    if (dy && canScrollY)\n      { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)) }\n    setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit))\n    // Only prevent default scrolling if vertical scrolling is\n    // actually possible. Otherwise, it causes vertical scroll\n    // jitter on OSX trackpads when deltaX is small and deltaY\n    // is large (issue #3579)\n    if (!dy || (dy && canScrollY))\n      { e_preventDefault(e) }\n    display.wheelStartX = null // Abort measurement, if in progress\n    return\n  }\n\n  // 'Project' the visible viewport to cover the area that is being\n  // scrolled into view (if we know enough to estimate it).\n  if (dy && wheelPixelsPerUnit != null) {\n    var pixels = dy * wheelPixelsPerUnit\n    var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight\n    if (pixels < 0) { top = Math.max(0, top + pixels - 50) }\n    else { bot = Math.min(cm.doc.height, bot + pixels + 50) }\n    updateDisplaySimple(cm, {top: top, bottom: bot})\n  }\n\n  if (wheelSamples < 20) {\n    if (display.wheelStartX == null) {\n      display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop\n      display.wheelDX = dx; display.wheelDY = dy\n      setTimeout(function () {\n        if (display.wheelStartX == null) { return }\n        var movedX = scroll.scrollLeft - display.wheelStartX\n        var movedY = scroll.scrollTop - display.wheelStartY\n        var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||\n          (movedX && display.wheelDX && movedX / display.wheelDX)\n        display.wheelStartX = display.wheelStartY = null\n        if (!sample) { return }\n        wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)\n        ++wheelSamples\n      }, 200)\n    } else {\n      display.wheelDX += dx; display.wheelDY += dy\n    }\n  }\n}\n\n// Selection objects are immutable. A new one is created every time\n// the selection changes. A selection is one or more non-overlapping\n// (and non-touching) ranges, sorted, and an integer that indicates\n// which one is the primary selection (the one that's scrolled into\n// view, that getCursor returns, etc).\nvar Selection = function(ranges, primIndex) {\n  this.ranges = ranges\n  this.primIndex = primIndex\n};\n\nSelection.prototype.primary = function () { return this.ranges[this.primIndex] };\n\nSelection.prototype.equals = function (other) {\n    var this$1 = this;\n\n  if (other == this) { return true }\n  if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }\n  for (var i = 0; i < this.ranges.length; i++) {\n    var here = this$1.ranges[i], there = other.ranges[i]\n    if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }\n  }\n  return true\n};\n\nSelection.prototype.deepCopy = function () {\n    var this$1 = this;\n\n  var out = []\n  for (var i = 0; i < this.ranges.length; i++)\n    { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }\n  return new Selection(out, this.primIndex)\n};\n\nSelection.prototype.somethingSelected = function () {\n    var this$1 = this;\n\n  for (var i = 0; i < this.ranges.length; i++)\n    { if (!this$1.ranges[i].empty()) { return true } }\n  return false\n};\n\nSelection.prototype.contains = function (pos, end) {\n    var this$1 = this;\n\n  if (!end) { end = pos }\n  for (var i = 0; i < this.ranges.length; i++) {\n    var range = this$1.ranges[i]\n    if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)\n      { return i }\n  }\n  return -1\n};\n\nvar Range = function(anchor, head) {\n  this.anchor = anchor; this.head = head\n};\n\nRange.prototype.from = function () { return minPos(this.anchor, this.head) };\nRange.prototype.to = function () { return maxPos(this.anchor, this.head) };\nRange.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };\n\n// Take an unsorted, potentially overlapping set of ranges, and\n// build a selection out of it. 'Consumes' ranges array (modifying\n// it).\nfunction normalizeSelection(ranges, primIndex) {\n  var prim = ranges[primIndex]\n  ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })\n  primIndex = indexOf(ranges, prim)\n  for (var i = 1; i < ranges.length; i++) {\n    var cur = ranges[i], prev = ranges[i - 1]\n    if (cmp(prev.to(), cur.from()) >= 0) {\n      var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())\n      var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head\n      if (i <= primIndex) { --primIndex }\n      ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))\n    }\n  }\n  return new Selection(ranges, primIndex)\n}\n\nfunction simpleSelection(anchor, head) {\n  return new Selection([new Range(anchor, head || anchor)], 0)\n}\n\n// Compute the position of the end of a change (its 'to' property\n// refers to the pre-change end).\nfunction changeEnd(change) {\n  if (!change.text) { return change.to }\n  return Pos(change.from.line + change.text.length - 1,\n             lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))\n}\n\n// Adjust a position to refer to the post-change position of the\n// same text, or the end of the change if the change covers it.\nfunction adjustForChange(pos, change) {\n  if (cmp(pos, change.from) < 0) { return pos }\n  if (cmp(pos, change.to) <= 0) { return changeEnd(change) }\n\n  var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch\n  if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }\n  return Pos(line, ch)\n}\n\nfunction computeSelAfterChange(doc, change) {\n  var out = []\n  for (var i = 0; i < doc.sel.ranges.length; i++) {\n    var range = doc.sel.ranges[i]\n    out.push(new Range(adjustForChange(range.anchor, change),\n                       adjustForChange(range.head, change)))\n  }\n  return normalizeSelection(out, doc.sel.primIndex)\n}\n\nfunction offsetPos(pos, old, nw) {\n  if (pos.line == old.line)\n    { return Pos(nw.line, pos.ch - old.ch + nw.ch) }\n  else\n    { return Pos(nw.line + (pos.line - old.line), pos.ch) }\n}\n\n// Used by replaceSelections to allow moving the selection to the\n// start or around the replaced test. Hint may be \"start\" or \"around\".\nfunction computeReplacedSel(doc, changes, hint) {\n  var out = []\n  var oldPrev = Pos(doc.first, 0), newPrev = oldPrev\n  for (var i = 0; i < changes.length; i++) {\n    var change = changes[i]\n    var from = offsetPos(change.from, oldPrev, newPrev)\n    var to = offsetPos(changeEnd(change), oldPrev, newPrev)\n    oldPrev = change.to\n    newPrev = to\n    if (hint == \"around\") {\n      var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0\n      out[i] = new Range(inv ? to : from, inv ? from : to)\n    } else {\n      out[i] = new Range(from, from)\n    }\n  }\n  return new Selection(out, doc.sel.primIndex)\n}\n\n// Used to get the editor into a consistent state again when options change.\n\nfunction loadMode(cm) {\n  cm.doc.mode = getMode(cm.options, cm.doc.modeOption)\n  resetModeState(cm)\n}\n\nfunction resetModeState(cm) {\n  cm.doc.iter(function (line) {\n    if (line.stateAfter) { line.stateAfter = null }\n    if (line.styles) { line.styles = null }\n  })\n  cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first\n  startWorker(cm, 100)\n  cm.state.modeGen++\n  if (cm.curOp) { regChange(cm) }\n}\n\n// DOCUMENT DATA STRUCTURE\n\n// By default, updates that start and end at the beginning of a line\n// are treated specially, in order to make the association of line\n// widgets and marker elements with the text behave more intuitive.\nfunction isWholeLineUpdate(doc, change) {\n  return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == \"\" &&\n    (!doc.cm || doc.cm.options.wholeLineUpdateBefore)\n}\n\n// Perform a change on the document data structure.\nfunction updateDoc(doc, change, markedSpans, estimateHeight) {\n  function spansFor(n) {return markedSpans ? markedSpans[n] : null}\n  function update(line, text, spans) {\n    updateLine(line, text, spans, estimateHeight)\n    signalLater(line, \"change\", line, change)\n  }\n  function linesFor(start, end) {\n    var result = []\n    for (var i = start; i < end; ++i)\n      { result.push(new Line(text[i], spansFor(i), estimateHeight)) }\n    return result\n  }\n\n  var from = change.from, to = change.to, text = change.text\n  var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)\n  var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line\n\n  // Adjust the line structure\n  if (change.full) {\n    doc.insert(0, linesFor(0, text.length))\n    doc.remove(text.length, doc.size - text.length)\n  } else if (isWholeLineUpdate(doc, change)) {\n    // This is a whole-line replace. Treated specially to make\n    // sure line objects move the way they are supposed to.\n    var added = linesFor(0, text.length - 1)\n    update(lastLine, lastLine.text, lastSpans)\n    if (nlines) { doc.remove(from.line, nlines) }\n    if (added.length) { doc.insert(from.line, added) }\n  } else if (firstLine == lastLine) {\n    if (text.length == 1) {\n      update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)\n    } else {\n      var added$1 = linesFor(1, text.length - 1)\n      added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))\n      doc.insert(from.line + 1, added$1)\n    }\n  } else if (text.length == 1) {\n    update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))\n    doc.remove(from.line + 1, nlines)\n  } else {\n    update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))\n    update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)\n    var added$2 = linesFor(1, text.length - 1)\n    if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }\n    doc.insert(from.line + 1, added$2)\n  }\n\n  signalLater(doc, \"change\", doc, change)\n}\n\n// Call f for all linked documents.\nfunction linkedDocs(doc, f, sharedHistOnly) {\n  function propagate(doc, skip, sharedHist) {\n    if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {\n      var rel = doc.linked[i]\n      if (rel.doc == skip) { continue }\n      var shared = sharedHist && rel.sharedHist\n      if (sharedHistOnly && !shared) { continue }\n      f(rel.doc, shared)\n      propagate(rel.doc, doc, shared)\n    } }\n  }\n  propagate(doc, null, true)\n}\n\n// Attach a document to an editor.\nfunction attachDoc(cm, doc) {\n  if (doc.cm) { throw new Error(\"This document is already in use.\") }\n  cm.doc = doc\n  doc.cm = cm\n  estimateLineHeights(cm)\n  loadMode(cm)\n  setDirectionClass(cm)\n  if (!cm.options.lineWrapping) { findMaxLine(cm) }\n  cm.options.mode = doc.modeOption\n  regChange(cm)\n}\n\nfunction setDirectionClass(cm) {\n  ;(cm.doc.direction == \"rtl\" ? addClass : rmClass)(cm.display.lineDiv, \"CodeMirror-rtl\")\n}\n\nfunction directionChanged(cm) {\n  runInOp(cm, function () {\n    setDirectionClass(cm)\n    regChange(cm)\n  })\n}\n\nfunction History(startGen) {\n  // Arrays of change events and selections. Doing something adds an\n  // event to done and clears undo. Undoing moves events from done\n  // to undone, redoing moves them in the other direction.\n  this.done = []; this.undone = []\n  this.undoDepth = Infinity\n  // Used to track when changes can be merged into a single undo\n  // event\n  this.lastModTime = this.lastSelTime = 0\n  this.lastOp = this.lastSelOp = null\n  this.lastOrigin = this.lastSelOrigin = null\n  // Used by the isClean() method\n  this.generation = this.maxGeneration = startGen || 1\n}\n\n// Create a history change event from an updateDoc-style change\n// object.\nfunction historyChangeFromChange(doc, change) {\n  var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}\n  attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)\n  linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)\n  return histChange\n}\n\n// Pop all selection events off the end of a history array. Stop at\n// a change event.\nfunction clearSelectionEvents(array) {\n  while (array.length) {\n    var last = lst(array)\n    if (last.ranges) { array.pop() }\n    else { break }\n  }\n}\n\n// Find the top change event in the history. Pop off selection\n// events that are in the way.\nfunction lastChangeEvent(hist, force) {\n  if (force) {\n    clearSelectionEvents(hist.done)\n    return lst(hist.done)\n  } else if (hist.done.length && !lst(hist.done).ranges) {\n    return lst(hist.done)\n  } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {\n    hist.done.pop()\n    return lst(hist.done)\n  }\n}\n\n// Register a change in the history. Merges changes that are within\n// a single operation, or are close together with an origin that\n// allows merging (starting with \"+\") into a single event.\nfunction addChangeToHistory(doc, change, selAfter, opId) {\n  var hist = doc.history\n  hist.undone.length = 0\n  var time = +new Date, cur\n  var last\n\n  if ((hist.lastOp == opId ||\n       hist.lastOrigin == change.origin && change.origin &&\n       ((change.origin.charAt(0) == \"+\" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||\n        change.origin.charAt(0) == \"*\")) &&\n      (cur = lastChangeEvent(hist, hist.lastOp == opId))) {\n    // Merge this change into the last event\n    last = lst(cur.changes)\n    if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {\n      // Optimized case for simple insertion -- don't want to add\n      // new changesets for every character typed\n      last.to = changeEnd(change)\n    } else {\n      // Add new sub-event\n      cur.changes.push(historyChangeFromChange(doc, change))\n    }\n  } else {\n    // Can not be merged, start a new event.\n    var before = lst(hist.done)\n    if (!before || !before.ranges)\n      { pushSelectionToHistory(doc.sel, hist.done) }\n    cur = {changes: [historyChangeFromChange(doc, change)],\n           generation: hist.generation}\n    hist.done.push(cur)\n    while (hist.done.length > hist.undoDepth) {\n      hist.done.shift()\n      if (!hist.done[0].ranges) { hist.done.shift() }\n    }\n  }\n  hist.done.push(selAfter)\n  hist.generation = ++hist.maxGeneration\n  hist.lastModTime = hist.lastSelTime = time\n  hist.lastOp = hist.lastSelOp = opId\n  hist.lastOrigin = hist.lastSelOrigin = change.origin\n\n  if (!last) { signal(doc, \"historyAdded\") }\n}\n\nfunction selectionEventCanBeMerged(doc, origin, prev, sel) {\n  var ch = origin.charAt(0)\n  return ch == \"*\" ||\n    ch == \"+\" &&\n    prev.ranges.length == sel.ranges.length &&\n    prev.somethingSelected() == sel.somethingSelected() &&\n    new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)\n}\n\n// Called whenever the selection changes, sets the new selection as\n// the pending selection in the history, and pushes the old pending\n// selection into the 'done' array when it was significantly\n// different (in number of selected ranges, emptiness, or time).\nfunction addSelectionToHistory(doc, sel, opId, options) {\n  var hist = doc.history, origin = options && options.origin\n\n  // A new event is started when the previous origin does not match\n  // the current, or the origins don't allow matching. Origins\n  // starting with * are always merged, those starting with + are\n  // merged when similar and close together in time.\n  if (opId == hist.lastSelOp ||\n      (origin && hist.lastSelOrigin == origin &&\n       (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||\n        selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))\n    { hist.done[hist.done.length - 1] = sel }\n  else\n    { pushSelectionToHistory(sel, hist.done) }\n\n  hist.lastSelTime = +new Date\n  hist.lastSelOrigin = origin\n  hist.lastSelOp = opId\n  if (options && options.clearRedo !== false)\n    { clearSelectionEvents(hist.undone) }\n}\n\nfunction pushSelectionToHistory(sel, dest) {\n  var top = lst(dest)\n  if (!(top && top.ranges && top.equals(sel)))\n    { dest.push(sel) }\n}\n\n// Used to store marked span information in the history.\nfunction attachLocalSpans(doc, change, from, to) {\n  var existing = change[\"spans_\" + doc.id], n = 0\n  doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {\n    if (line.markedSpans)\n      { (existing || (existing = change[\"spans_\" + doc.id] = {}))[n] = line.markedSpans }\n    ++n\n  })\n}\n\n// When un/re-doing restores text containing marked spans, those\n// that have been explicitly cleared should not be restored.\nfunction removeClearedSpans(spans) {\n  if (!spans) { return null }\n  var out\n  for (var i = 0; i < spans.length; ++i) {\n    if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }\n    else if (out) { out.push(spans[i]) }\n  }\n  return !out ? spans : out.length ? out : null\n}\n\n// Retrieve and filter the old marked spans stored in a change event.\nfunction getOldSpans(doc, change) {\n  var found = change[\"spans_\" + doc.id]\n  if (!found) { return null }\n  var nw = []\n  for (var i = 0; i < change.text.length; ++i)\n    { nw.push(removeClearedSpans(found[i])) }\n  return nw\n}\n\n// Used for un/re-doing changes from the history. Combines the\n// result of computing the existing spans with the set of spans that\n// existed in the history (so that deleting around a span and then\n// undoing brings back the span).\nfunction mergeOldSpans(doc, change) {\n  var old = getOldSpans(doc, change)\n  var stretched = stretchSpansOverChange(doc, change)\n  if (!old) { return stretched }\n  if (!stretched) { return old }\n\n  for (var i = 0; i < old.length; ++i) {\n    var oldCur = old[i], stretchCur = stretched[i]\n    if (oldCur && stretchCur) {\n      spans: for (var j = 0; j < stretchCur.length; ++j) {\n        var span = stretchCur[j]\n        for (var k = 0; k < oldCur.length; ++k)\n          { if (oldCur[k].marker == span.marker) { continue spans } }\n        oldCur.push(span)\n      }\n    } else if (stretchCur) {\n      old[i] = stretchCur\n    }\n  }\n  return old\n}\n\n// Used both to provide a JSON-safe object in .getHistory, and, when\n// detaching a document, to split the history in two\nfunction copyHistoryArray(events, newGroup, instantiateSel) {\n  var copy = []\n  for (var i = 0; i < events.length; ++i) {\n    var event = events[i]\n    if (event.ranges) {\n      copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)\n      continue\n    }\n    var changes = event.changes, newChanges = []\n    copy.push({changes: newChanges})\n    for (var j = 0; j < changes.length; ++j) {\n      var change = changes[j], m = (void 0)\n      newChanges.push({from: change.from, to: change.to, text: change.text})\n      if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\\d+)$/)) {\n        if (indexOf(newGroup, Number(m[1])) > -1) {\n          lst(newChanges)[prop] = change[prop]\n          delete change[prop]\n        }\n      } } }\n    }\n  }\n  return copy\n}\n\n// The 'scroll' parameter given to many of these indicated whether\n// the new cursor position should be scrolled into view after\n// modifying the selection.\n\n// If shift is held or the extend flag is set, extends a range to\n// include a given position (and optionally a second position).\n// Otherwise, simply returns the range between the given positions.\n// Used for cursor motion and such.\nfunction extendRange(range, head, other, extend) {\n  if (extend) {\n    var anchor = range.anchor\n    if (other) {\n      var posBefore = cmp(head, anchor) < 0\n      if (posBefore != (cmp(other, anchor) < 0)) {\n        anchor = head\n        head = other\n      } else if (posBefore != (cmp(head, other) < 0)) {\n        head = other\n      }\n    }\n    return new Range(anchor, head)\n  } else {\n    return new Range(other || head, head)\n  }\n}\n\n// Extend the primary selection range, discard the rest.\nfunction extendSelection(doc, head, other, options, extend) {\n  if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend) }\n  setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options)\n}\n\n// Extend all selections (pos is an array of selections with length\n// equal the number of selections)\nfunction extendSelections(doc, heads, options) {\n  var out = []\n  var extend = doc.cm && (doc.cm.display.shift || doc.extend)\n  for (var i = 0; i < doc.sel.ranges.length; i++)\n    { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend) }\n  var newSel = normalizeSelection(out, doc.sel.primIndex)\n  setSelection(doc, newSel, options)\n}\n\n// Updates a single range in the selection.\nfunction replaceOneSelection(doc, i, range, options) {\n  var ranges = doc.sel.ranges.slice(0)\n  ranges[i] = range\n  setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)\n}\n\n// Reset the selection to a single range.\nfunction setSimpleSelection(doc, anchor, head, options) {\n  setSelection(doc, simpleSelection(anchor, head), options)\n}\n\n// Give beforeSelectionChange handlers a change to influence a\n// selection update.\nfunction filterSelectionChange(doc, sel, options) {\n  var obj = {\n    ranges: sel.ranges,\n    update: function(ranges) {\n      var this$1 = this;\n\n      this.ranges = []\n      for (var i = 0; i < ranges.length; i++)\n        { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),\n                                   clipPos(doc, ranges[i].head)) }\n    },\n    origin: options && options.origin\n  }\n  signal(doc, \"beforeSelectionChange\", doc, obj)\n  if (doc.cm) { signal(doc.cm, \"beforeSelectionChange\", doc.cm, obj) }\n  if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }\n  else { return sel }\n}\n\nfunction setSelectionReplaceHistory(doc, sel, options) {\n  var done = doc.history.done, last = lst(done)\n  if (last && last.ranges) {\n    done[done.length - 1] = sel\n    setSelectionNoUndo(doc, sel, options)\n  } else {\n    setSelection(doc, sel, options)\n  }\n}\n\n// Set a new selection.\nfunction setSelection(doc, sel, options) {\n  setSelectionNoUndo(doc, sel, options)\n  addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)\n}\n\nfunction setSelectionNoUndo(doc, sel, options) {\n  if (hasHandler(doc, \"beforeSelectionChange\") || doc.cm && hasHandler(doc.cm, \"beforeSelectionChange\"))\n    { sel = filterSelectionChange(doc, sel, options) }\n\n  var bias = options && options.bias ||\n    (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)\n  setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))\n\n  if (!(options && options.scroll === false) && doc.cm)\n    { ensureCursorVisible(doc.cm) }\n}\n\nfunction setSelectionInner(doc, sel) {\n  if (sel.equals(doc.sel)) { return }\n\n  doc.sel = sel\n\n  if (doc.cm) {\n    doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true\n    signalCursorActivity(doc.cm)\n  }\n  signalLater(doc, \"cursorActivity\", doc)\n}\n\n// Verify that the selection does not partially select any atomic\n// marked ranges.\nfunction reCheckSelection(doc) {\n  setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))\n}\n\n// Return a selection that does not partially select any atomic\n// ranges.\nfunction skipAtomicInSelection(doc, sel, bias, mayClear) {\n  var out\n  for (var i = 0; i < sel.ranges.length; i++) {\n    var range = sel.ranges[i]\n    var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]\n    var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)\n    var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)\n    if (out || newAnchor != range.anchor || newHead != range.head) {\n      if (!out) { out = sel.ranges.slice(0, i) }\n      out[i] = new Range(newAnchor, newHead)\n    }\n  }\n  return out ? normalizeSelection(out, sel.primIndex) : sel\n}\n\nfunction skipAtomicInner(doc, pos, oldPos, dir, mayClear) {\n  var line = getLine(doc, pos.line)\n  if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n    var sp = line.markedSpans[i], m = sp.marker\n    if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&\n        (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {\n      if (mayClear) {\n        signal(m, \"beforeCursorEnter\")\n        if (m.explicitlyCleared) {\n          if (!line.markedSpans) { break }\n          else {--i; continue}\n        }\n      }\n      if (!m.atomic) { continue }\n\n      if (oldPos) {\n        var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)\n        if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)\n          { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }\n        if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))\n          { return skipAtomicInner(doc, near, pos, dir, mayClear) }\n      }\n\n      var far = m.find(dir < 0 ? -1 : 1)\n      if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)\n        { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }\n      return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null\n    }\n  } }\n  return pos\n}\n\n// Ensure a given position is not inside an atomic range.\nfunction skipAtomic(doc, pos, oldPos, bias, mayClear) {\n  var dir = bias || 1\n  var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||\n      (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||\n      skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||\n      (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))\n  if (!found) {\n    doc.cantEdit = true\n    return Pos(doc.first, 0)\n  }\n  return found\n}\n\nfunction movePos(doc, pos, dir, line) {\n  if (dir < 0 && pos.ch == 0) {\n    if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }\n    else { return null }\n  } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {\n    if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }\n    else { return null }\n  } else {\n    return new Pos(pos.line, pos.ch + dir)\n  }\n}\n\nfunction selectAll(cm) {\n  cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)\n}\n\n// UPDATING\n\n// Allow \"beforeChange\" event handlers to influence a change\nfunction filterChange(doc, change, update) {\n  var obj = {\n    canceled: false,\n    from: change.from,\n    to: change.to,\n    text: change.text,\n    origin: change.origin,\n    cancel: function () { return obj.canceled = true; }\n  }\n  if (update) { obj.update = function (from, to, text, origin) {\n    if (from) { obj.from = clipPos(doc, from) }\n    if (to) { obj.to = clipPos(doc, to) }\n    if (text) { obj.text = text }\n    if (origin !== undefined) { obj.origin = origin }\n  } }\n  signal(doc, \"beforeChange\", doc, obj)\n  if (doc.cm) { signal(doc.cm, \"beforeChange\", doc.cm, obj) }\n\n  if (obj.canceled) { return null }\n  return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}\n}\n\n// Apply a change to a document, and add it to the document's\n// history, and propagating it to all linked documents.\nfunction makeChange(doc, change, ignoreReadOnly) {\n  if (doc.cm) {\n    if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }\n    if (doc.cm.state.suppressEdits) { return }\n  }\n\n  if (hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")) {\n    change = filterChange(doc, change, true)\n    if (!change) { return }\n  }\n\n  // Possibly split or suppress the update based on the presence\n  // of read-only spans in its range.\n  var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)\n  if (split) {\n    for (var i = split.length - 1; i >= 0; --i)\n      { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [\"\"] : change.text, origin: change.origin}) }\n  } else {\n    makeChangeInner(doc, change)\n  }\n}\n\nfunction makeChangeInner(doc, change) {\n  if (change.text.length == 1 && change.text[0] == \"\" && cmp(change.from, change.to) == 0) { return }\n  var selAfter = computeSelAfterChange(doc, change)\n  addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)\n\n  makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))\n  var rebased = []\n\n  linkedDocs(doc, function (doc, sharedHist) {\n    if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n      rebaseHist(doc.history, change)\n      rebased.push(doc.history)\n    }\n    makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))\n  })\n}\n\n// Revert a change stored in a document's history.\nfunction makeChangeFromHistory(doc, type, allowSelectionOnly) {\n  var suppress = doc.cm && doc.cm.state.suppressEdits\n  if (suppress && !allowSelectionOnly) { return }\n\n  var hist = doc.history, event, selAfter = doc.sel\n  var source = type == \"undo\" ? hist.done : hist.undone, dest = type == \"undo\" ? hist.undone : hist.done\n\n  // Verify that there is a useable event (so that ctrl-z won't\n  // needlessly clear selection events)\n  var i = 0\n  for (; i < source.length; i++) {\n    event = source[i]\n    if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)\n      { break }\n  }\n  if (i == source.length) { return }\n  hist.lastOrigin = hist.lastSelOrigin = null\n\n  for (;;) {\n    event = source.pop()\n    if (event.ranges) {\n      pushSelectionToHistory(event, dest)\n      if (allowSelectionOnly && !event.equals(doc.sel)) {\n        setSelection(doc, event, {clearRedo: false})\n        return\n      }\n      selAfter = event\n    } else if (suppress) {\n      source.push(event)\n      return\n    } else { break }\n  }\n\n  // Build up a reverse change object to add to the opposite history\n  // stack (redo when undoing, and vice versa).\n  var antiChanges = []\n  pushSelectionToHistory(selAfter, dest)\n  dest.push({changes: antiChanges, generation: hist.generation})\n  hist.generation = event.generation || ++hist.maxGeneration\n\n  var filter = hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")\n\n  var loop = function ( i ) {\n    var change = event.changes[i]\n    change.origin = type\n    if (filter && !filterChange(doc, change, false)) {\n      source.length = 0\n      return {}\n    }\n\n    antiChanges.push(historyChangeFromChange(doc, change))\n\n    var after = i ? computeSelAfterChange(doc, change) : lst(source)\n    makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))\n    if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }\n    var rebased = []\n\n    // Propagate to the linked documents\n    linkedDocs(doc, function (doc, sharedHist) {\n      if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n        rebaseHist(doc.history, change)\n        rebased.push(doc.history)\n      }\n      makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))\n    })\n  };\n\n  for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {\n    var returned = loop( i$1 );\n\n    if ( returned ) return returned.v;\n  }\n}\n\n// Sub-views need their line numbers shifted when text is added\n// above or below them in the parent document.\nfunction shiftDoc(doc, distance) {\n  if (distance == 0) { return }\n  doc.first += distance\n  doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(\n    Pos(range.anchor.line + distance, range.anchor.ch),\n    Pos(range.head.line + distance, range.head.ch)\n  ); }), doc.sel.primIndex)\n  if (doc.cm) {\n    regChange(doc.cm, doc.first, doc.first - distance, distance)\n    for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)\n      { regLineChange(doc.cm, l, \"gutter\") }\n  }\n}\n\n// More lower-level change function, handling only a single document\n// (not linked ones).\nfunction makeChangeSingleDoc(doc, change, selAfter, spans) {\n  if (doc.cm && !doc.cm.curOp)\n    { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }\n\n  if (change.to.line < doc.first) {\n    shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))\n    return\n  }\n  if (change.from.line > doc.lastLine()) { return }\n\n  // Clip the change to the size of this doc\n  if (change.from.line < doc.first) {\n    var shift = change.text.length - 1 - (doc.first - change.from.line)\n    shiftDoc(doc, shift)\n    change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),\n              text: [lst(change.text)], origin: change.origin}\n  }\n  var last = doc.lastLine()\n  if (change.to.line > last) {\n    change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),\n              text: [change.text[0]], origin: change.origin}\n  }\n\n  change.removed = getBetween(doc, change.from, change.to)\n\n  if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }\n  if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }\n  else { updateDoc(doc, change, spans) }\n  setSelectionNoUndo(doc, selAfter, sel_dontScroll)\n}\n\n// Handle the interaction of a change to a document with the editor\n// that this document is part of.\nfunction makeChangeSingleDocInEditor(cm, change, spans) {\n  var doc = cm.doc, display = cm.display, from = change.from, to = change.to\n\n  var recomputeMaxLength = false, checkWidthStart = from.line\n  if (!cm.options.lineWrapping) {\n    checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))\n    doc.iter(checkWidthStart, to.line + 1, function (line) {\n      if (line == display.maxLine) {\n        recomputeMaxLength = true\n        return true\n      }\n    })\n  }\n\n  if (doc.sel.contains(change.from, change.to) > -1)\n    { signalCursorActivity(cm) }\n\n  updateDoc(doc, change, spans, estimateHeight(cm))\n\n  if (!cm.options.lineWrapping) {\n    doc.iter(checkWidthStart, from.line + change.text.length, function (line) {\n      var len = lineLength(line)\n      if (len > display.maxLineLength) {\n        display.maxLine = line\n        display.maxLineLength = len\n        display.maxLineChanged = true\n        recomputeMaxLength = false\n      }\n    })\n    if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }\n  }\n\n  retreatFrontier(doc, from.line)\n  startWorker(cm, 400)\n\n  var lendiff = change.text.length - (to.line - from.line) - 1\n  // Remember that these lines changed, for updating the display\n  if (change.full)\n    { regChange(cm) }\n  else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))\n    { regLineChange(cm, from.line, \"text\") }\n  else\n    { regChange(cm, from.line, to.line + 1, lendiff) }\n\n  var changesHandler = hasHandler(cm, \"changes\"), changeHandler = hasHandler(cm, \"change\")\n  if (changeHandler || changesHandler) {\n    var obj = {\n      from: from, to: to,\n      text: change.text,\n      removed: change.removed,\n      origin: change.origin\n    }\n    if (changeHandler) { signalLater(cm, \"change\", cm, obj) }\n    if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }\n  }\n  cm.display.selForContextMenu = null\n}\n\nfunction replaceRange(doc, code, from, to, origin) {\n  if (!to) { to = from }\n  if (cmp(to, from) < 0) { var assign;\n    (assign = [to, from], from = assign[0], to = assign[1], assign) }\n  if (typeof code == \"string\") { code = doc.splitLines(code) }\n  makeChange(doc, {from: from, to: to, text: code, origin: origin})\n}\n\n// Rebasing/resetting history to deal with externally-sourced changes\n\nfunction rebaseHistSelSingle(pos, from, to, diff) {\n  if (to < pos.line) {\n    pos.line += diff\n  } else if (from < pos.line) {\n    pos.line = from\n    pos.ch = 0\n  }\n}\n\n// Tries to rebase an array of history events given a change in the\n// document. If the change touches the same lines as the event, the\n// event, and everything 'behind' it, is discarded. If the change is\n// before the event, the event's positions are updated. Uses a\n// copy-on-write scheme for the positions, to avoid having to\n// reallocate them all on every rebase, but also avoid problems with\n// shared position objects being unsafely updated.\nfunction rebaseHistArray(array, from, to, diff) {\n  for (var i = 0; i < array.length; ++i) {\n    var sub = array[i], ok = true\n    if (sub.ranges) {\n      if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }\n      for (var j = 0; j < sub.ranges.length; j++) {\n        rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)\n        rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)\n      }\n      continue\n    }\n    for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {\n      var cur = sub.changes[j$1]\n      if (to < cur.from.line) {\n        cur.from = Pos(cur.from.line + diff, cur.from.ch)\n        cur.to = Pos(cur.to.line + diff, cur.to.ch)\n      } else if (from <= cur.to.line) {\n        ok = false\n        break\n      }\n    }\n    if (!ok) {\n      array.splice(0, i + 1)\n      i = 0\n    }\n  }\n}\n\nfunction rebaseHist(hist, change) {\n  var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1\n  rebaseHistArray(hist.done, from, to, diff)\n  rebaseHistArray(hist.undone, from, to, diff)\n}\n\n// Utility for applying a change to a line by handle or number,\n// returning the number and optionally registering the line as\n// changed.\nfunction changeLine(doc, handle, changeType, op) {\n  var no = handle, line = handle\n  if (typeof handle == \"number\") { line = getLine(doc, clipLine(doc, handle)) }\n  else { no = lineNo(handle) }\n  if (no == null) { return null }\n  if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }\n  return line\n}\n\n// The document is represented as a BTree consisting of leaves, with\n// chunk of lines in them, and branches, with up to ten leaves or\n// other branch nodes below them. The top node is always a branch\n// node, and is the document object itself (meaning it has\n// additional methods and properties).\n//\n// All nodes have parent links. The tree is used both to go from\n// line numbers to line objects, and to go from objects to numbers.\n// It also indexes by height, and is used to convert between height\n// and line object, and to find the total height of the document.\n//\n// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html\n\nfunction LeafChunk(lines) {\n  var this$1 = this;\n\n  this.lines = lines\n  this.parent = null\n  var height = 0\n  for (var i = 0; i < lines.length; ++i) {\n    lines[i].parent = this$1\n    height += lines[i].height\n  }\n  this.height = height\n}\n\nLeafChunk.prototype = {\n  chunkSize: function chunkSize() { return this.lines.length },\n\n  // Remove the n lines at offset 'at'.\n  removeInner: function removeInner(at, n) {\n    var this$1 = this;\n\n    for (var i = at, e = at + n; i < e; ++i) {\n      var line = this$1.lines[i]\n      this$1.height -= line.height\n      cleanUpLine(line)\n      signalLater(line, \"delete\")\n    }\n    this.lines.splice(at, n)\n  },\n\n  // Helper used to collapse a small branch into a single leaf.\n  collapse: function collapse(lines) {\n    lines.push.apply(lines, this.lines)\n  },\n\n  // Insert the given array of lines at offset 'at', count them as\n  // having the given height.\n  insertInner: function insertInner(at, lines, height) {\n    var this$1 = this;\n\n    this.height += height\n    this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))\n    for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }\n  },\n\n  // Used to iterate over a part of the tree.\n  iterN: function iterN(at, n, op) {\n    var this$1 = this;\n\n    for (var e = at + n; at < e; ++at)\n      { if (op(this$1.lines[at])) { return true } }\n  }\n}\n\nfunction BranchChunk(children) {\n  var this$1 = this;\n\n  this.children = children\n  var size = 0, height = 0\n  for (var i = 0; i < children.length; ++i) {\n    var ch = children[i]\n    size += ch.chunkSize(); height += ch.height\n    ch.parent = this$1\n  }\n  this.size = size\n  this.height = height\n  this.parent = null\n}\n\nBranchChunk.prototype = {\n  chunkSize: function chunkSize() { return this.size },\n\n  removeInner: function removeInner(at, n) {\n    var this$1 = this;\n\n    this.size -= n\n    for (var i = 0; i < this.children.length; ++i) {\n      var child = this$1.children[i], sz = child.chunkSize()\n      if (at < sz) {\n        var rm = Math.min(n, sz - at), oldHeight = child.height\n        child.removeInner(at, rm)\n        this$1.height -= oldHeight - child.height\n        if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }\n        if ((n -= rm) == 0) { break }\n        at = 0\n      } else { at -= sz }\n    }\n    // If the result is smaller than 25 lines, ensure that it is a\n    // single leaf node.\n    if (this.size - n < 25 &&\n        (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {\n      var lines = []\n      this.collapse(lines)\n      this.children = [new LeafChunk(lines)]\n      this.children[0].parent = this\n    }\n  },\n\n  collapse: function collapse(lines) {\n    var this$1 = this;\n\n    for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }\n  },\n\n  insertInner: function insertInner(at, lines, height) {\n    var this$1 = this;\n\n    this.size += lines.length\n    this.height += height\n    for (var i = 0; i < this.children.length; ++i) {\n      var child = this$1.children[i], sz = child.chunkSize()\n      if (at <= sz) {\n        child.insertInner(at, lines, height)\n        if (child.lines && child.lines.length > 50) {\n          // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.\n          // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.\n          var remaining = child.lines.length % 25 + 25\n          for (var pos = remaining; pos < child.lines.length;) {\n            var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))\n            child.height -= leaf.height\n            this$1.children.splice(++i, 0, leaf)\n            leaf.parent = this$1\n          }\n          child.lines = child.lines.slice(0, remaining)\n          this$1.maybeSpill()\n        }\n        break\n      }\n      at -= sz\n    }\n  },\n\n  // When a node has grown, check whether it should be split.\n  maybeSpill: function maybeSpill() {\n    if (this.children.length <= 10) { return }\n    var me = this\n    do {\n      var spilled = me.children.splice(me.children.length - 5, 5)\n      var sibling = new BranchChunk(spilled)\n      if (!me.parent) { // Become the parent node\n        var copy = new BranchChunk(me.children)\n        copy.parent = me\n        me.children = [copy, sibling]\n        me = copy\n     } else {\n        me.size -= sibling.size\n        me.height -= sibling.height\n        var myIndex = indexOf(me.parent.children, me)\n        me.parent.children.splice(myIndex + 1, 0, sibling)\n      }\n      sibling.parent = me.parent\n    } while (me.children.length > 10)\n    me.parent.maybeSpill()\n  },\n\n  iterN: function iterN(at, n, op) {\n    var this$1 = this;\n\n    for (var i = 0; i < this.children.length; ++i) {\n      var child = this$1.children[i], sz = child.chunkSize()\n      if (at < sz) {\n        var used = Math.min(n, sz - at)\n        if (child.iterN(at, used, op)) { return true }\n        if ((n -= used) == 0) { break }\n        at = 0\n      } else { at -= sz }\n    }\n  }\n}\n\n// Line widgets are block elements displayed above or below a line.\n\nvar LineWidget = function(doc, node, options) {\n  var this$1 = this;\n\n  if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))\n    { this$1[opt] = options[opt] } } }\n  this.doc = doc\n  this.node = node\n};\n\nLineWidget.prototype.clear = function () {\n    var this$1 = this;\n\n  var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)\n  if (no == null || !ws) { return }\n  for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }\n  if (!ws.length) { line.widgets = null }\n  var height = widgetHeight(this)\n  updateLineHeight(line, Math.max(0, line.height - height))\n  if (cm) {\n    runInOp(cm, function () {\n      adjustScrollWhenAboveVisible(cm, line, -height)\n      regLineChange(cm, no, \"widget\")\n    })\n    signalLater(cm, \"lineWidgetCleared\", cm, this, no)\n  }\n};\n\nLineWidget.prototype.changed = function () {\n    var this$1 = this;\n\n  var oldH = this.height, cm = this.doc.cm, line = this.line\n  this.height = null\n  var diff = widgetHeight(this) - oldH\n  if (!diff) { return }\n  updateLineHeight(line, line.height + diff)\n  if (cm) {\n    runInOp(cm, function () {\n      cm.curOp.forceUpdate = true\n      adjustScrollWhenAboveVisible(cm, line, diff)\n      signalLater(cm, \"lineWidgetChanged\", cm, this$1, lineNo(line))\n    })\n  }\n};\neventMixin(LineWidget)\n\nfunction adjustScrollWhenAboveVisible(cm, line, diff) {\n  if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))\n    { addToScrollTop(cm, diff) }\n}\n\nfunction addLineWidget(doc, handle, node, options) {\n  var widget = new LineWidget(doc, node, options)\n  var cm = doc.cm\n  if (cm && widget.noHScroll) { cm.display.alignWidgets = true }\n  changeLine(doc, handle, \"widget\", function (line) {\n    var widgets = line.widgets || (line.widgets = [])\n    if (widget.insertAt == null) { widgets.push(widget) }\n    else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }\n    widget.line = line\n    if (cm && !lineIsHidden(doc, line)) {\n      var aboveVisible = heightAtLine(line) < doc.scrollTop\n      updateLineHeight(line, line.height + widgetHeight(widget))\n      if (aboveVisible) { addToScrollTop(cm, widget.height) }\n      cm.curOp.forceUpdate = true\n    }\n    return true\n  })\n  if (cm) { signalLater(cm, \"lineWidgetAdded\", cm, widget, typeof handle == \"number\" ? handle : lineNo(handle)) }\n  return widget\n}\n\n// TEXTMARKERS\n\n// Created with markText and setBookmark methods. A TextMarker is a\n// handle that can be used to clear or find a marked position in the\n// document. Line objects hold arrays (markedSpans) containing\n// {from, to, marker} object pointing to such marker objects, and\n// indicating that such a marker is present on that line. Multiple\n// lines may point to the same marker when it spans across lines.\n// The spans will have null for their from/to properties when the\n// marker continues beyond the start/end of the line. Markers have\n// links back to the lines they currently touch.\n\n// Collapsed markers have unique ids, in order to be able to order\n// them, which is needed for uniquely determining an outer marker\n// when they overlap (they may nest, but not partially overlap).\nvar nextMarkerId = 0\n\nvar TextMarker = function(doc, type) {\n  this.lines = []\n  this.type = type\n  this.doc = doc\n  this.id = ++nextMarkerId\n};\n\n// Clear the marker.\nTextMarker.prototype.clear = function () {\n    var this$1 = this;\n\n  if (this.explicitlyCleared) { return }\n  var cm = this.doc.cm, withOp = cm && !cm.curOp\n  if (withOp) { startOperation(cm) }\n  if (hasHandler(this, \"clear\")) {\n    var found = this.find()\n    if (found) { signalLater(this, \"clear\", found.from, found.to) }\n  }\n  var min = null, max = null\n  for (var i = 0; i < this.lines.length; ++i) {\n    var line = this$1.lines[i]\n    var span = getMarkedSpanFor(line.markedSpans, this$1)\n    if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), \"text\") }\n    else if (cm) {\n      if (span.to != null) { max = lineNo(line) }\n      if (span.from != null) { min = lineNo(line) }\n    }\n    line.markedSpans = removeMarkedSpan(line.markedSpans, span)\n    if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)\n      { updateLineHeight(line, textHeight(cm.display)) }\n  }\n  if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {\n    var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)\n    if (len > cm.display.maxLineLength) {\n      cm.display.maxLine = visual\n      cm.display.maxLineLength = len\n      cm.display.maxLineChanged = true\n    }\n  } }\n\n  if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }\n  this.lines.length = 0\n  this.explicitlyCleared = true\n  if (this.atomic && this.doc.cantEdit) {\n    this.doc.cantEdit = false\n    if (cm) { reCheckSelection(cm.doc) }\n  }\n  if (cm) { signalLater(cm, \"markerCleared\", cm, this, min, max) }\n  if (withOp) { endOperation(cm) }\n  if (this.parent) { this.parent.clear() }\n};\n\n// Find the position of the marker in the document. Returns a {from,\n// to} object by default. Side can be passed to get a specific side\n// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the\n// Pos objects returned contain a line object, rather than a line\n// number (used to prevent looking up the same line twice).\nTextMarker.prototype.find = function (side, lineObj) {\n    var this$1 = this;\n\n  if (side == null && this.type == \"bookmark\") { side = 1 }\n  var from, to\n  for (var i = 0; i < this.lines.length; ++i) {\n    var line = this$1.lines[i]\n    var span = getMarkedSpanFor(line.markedSpans, this$1)\n    if (span.from != null) {\n      from = Pos(lineObj ? line : lineNo(line), span.from)\n      if (side == -1) { return from }\n    }\n    if (span.to != null) {\n      to = Pos(lineObj ? line : lineNo(line), span.to)\n      if (side == 1) { return to }\n    }\n  }\n  return from && {from: from, to: to}\n};\n\n// Signals that the marker's widget changed, and surrounding layout\n// should be recomputed.\nTextMarker.prototype.changed = function () {\n    var this$1 = this;\n\n  var pos = this.find(-1, true), widget = this, cm = this.doc.cm\n  if (!pos || !cm) { return }\n  runInOp(cm, function () {\n    var line = pos.line, lineN = lineNo(pos.line)\n    var view = findViewForLine(cm, lineN)\n    if (view) {\n      clearLineMeasurementCacheFor(view)\n      cm.curOp.selectionChanged = cm.curOp.forceUpdate = true\n    }\n    cm.curOp.updateMaxLine = true\n    if (!lineIsHidden(widget.doc, line) && widget.height != null) {\n      var oldHeight = widget.height\n      widget.height = null\n      var dHeight = widgetHeight(widget) - oldHeight\n      if (dHeight)\n        { updateLineHeight(line, line.height + dHeight) }\n    }\n    signalLater(cm, \"markerChanged\", cm, this$1)\n  })\n};\n\nTextMarker.prototype.attachLine = function (line) {\n  if (!this.lines.length && this.doc.cm) {\n    var op = this.doc.cm.curOp\n    if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)\n      { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }\n  }\n  this.lines.push(line)\n};\n\nTextMarker.prototype.detachLine = function (line) {\n  this.lines.splice(indexOf(this.lines, line), 1)\n  if (!this.lines.length && this.doc.cm) {\n    var op = this.doc.cm.curOp\n    ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)\n  }\n};\neventMixin(TextMarker)\n\n// Create a marker, wire it up to the right lines, and\nfunction markText(doc, from, to, options, type) {\n  // Shared markers (across linked documents) are handled separately\n  // (markTextShared will call out to this again, once per\n  // document).\n  if (options && options.shared) { return markTextShared(doc, from, to, options, type) }\n  // Ensure we are in an operation.\n  if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }\n\n  var marker = new TextMarker(doc, type), diff = cmp(from, to)\n  if (options) { copyObj(options, marker, false) }\n  // Don't connect empty markers unless clearWhenEmpty is false\n  if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)\n    { return marker }\n  if (marker.replacedWith) {\n    // Showing up as a widget implies collapsed (widget replaces text)\n    marker.collapsed = true\n    marker.widgetNode = eltP(\"span\", [marker.replacedWith], \"CodeMirror-widget\")\n    if (!options.handleMouseEvents) { marker.widgetNode.setAttribute(\"cm-ignore-events\", \"true\") }\n    if (options.insertLeft) { marker.widgetNode.insertLeft = true }\n  }\n  if (marker.collapsed) {\n    if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||\n        from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))\n      { throw new Error(\"Inserting collapsed marker partially overlapping an existing one\") }\n    seeCollapsedSpans()\n  }\n\n  if (marker.addToHistory)\n    { addChangeToHistory(doc, {from: from, to: to, origin: \"markText\"}, doc.sel, NaN) }\n\n  var curLine = from.line, cm = doc.cm, updateMaxLine\n  doc.iter(curLine, to.line + 1, function (line) {\n    if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)\n      { updateMaxLine = true }\n    if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }\n    addMarkedSpan(line, new MarkedSpan(marker,\n                                       curLine == from.line ? from.ch : null,\n                                       curLine == to.line ? to.ch : null))\n    ++curLine\n  })\n  // lineIsHidden depends on the presence of the spans, so needs a second pass\n  if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {\n    if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }\n  }) }\n\n  if (marker.clearOnEnter) { on(marker, \"beforeCursorEnter\", function () { return marker.clear(); }) }\n\n  if (marker.readOnly) {\n    seeReadOnlySpans()\n    if (doc.history.done.length || doc.history.undone.length)\n      { doc.clearHistory() }\n  }\n  if (marker.collapsed) {\n    marker.id = ++nextMarkerId\n    marker.atomic = true\n  }\n  if (cm) {\n    // Sync editor state\n    if (updateMaxLine) { cm.curOp.updateMaxLine = true }\n    if (marker.collapsed)\n      { regChange(cm, from.line, to.line + 1) }\n    else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)\n      { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, \"text\") } }\n    if (marker.atomic) { reCheckSelection(cm.doc) }\n    signalLater(cm, \"markerAdded\", cm, marker)\n  }\n  return marker\n}\n\n// SHARED TEXTMARKERS\n\n// A shared marker spans multiple linked documents. It is\n// implemented as a meta-marker-object controlling multiple normal\n// markers.\nvar SharedTextMarker = function(markers, primary) {\n  var this$1 = this;\n\n  this.markers = markers\n  this.primary = primary\n  for (var i = 0; i < markers.length; ++i)\n    { markers[i].parent = this$1 }\n};\n\nSharedTextMarker.prototype.clear = function () {\n    var this$1 = this;\n\n  if (this.explicitlyCleared) { return }\n  this.explicitlyCleared = true\n  for (var i = 0; i < this.markers.length; ++i)\n    { this$1.markers[i].clear() }\n  signalLater(this, \"clear\")\n};\n\nSharedTextMarker.prototype.find = function (side, lineObj) {\n  return this.primary.find(side, lineObj)\n};\neventMixin(SharedTextMarker)\n\nfunction markTextShared(doc, from, to, options, type) {\n  options = copyObj(options)\n  options.shared = false\n  var markers = [markText(doc, from, to, options, type)], primary = markers[0]\n  var widget = options.widgetNode\n  linkedDocs(doc, function (doc) {\n    if (widget) { options.widgetNode = widget.cloneNode(true) }\n    markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))\n    for (var i = 0; i < doc.linked.length; ++i)\n      { if (doc.linked[i].isParent) { return } }\n    primary = lst(markers)\n  })\n  return new SharedTextMarker(markers, primary)\n}\n\nfunction findSharedMarkers(doc) {\n  return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })\n}\n\nfunction copySharedMarkers(doc, markers) {\n  for (var i = 0; i < markers.length; i++) {\n    var marker = markers[i], pos = marker.find()\n    var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)\n    if (cmp(mFrom, mTo)) {\n      var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)\n      marker.markers.push(subMark)\n      subMark.parent = marker\n    }\n  }\n}\n\nfunction detachSharedMarkers(markers) {\n  var loop = function ( i ) {\n    var marker = markers[i], linked = [marker.primary.doc]\n    linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })\n    for (var j = 0; j < marker.markers.length; j++) {\n      var subMarker = marker.markers[j]\n      if (indexOf(linked, subMarker.doc) == -1) {\n        subMarker.parent = null\n        marker.markers.splice(j--, 1)\n      }\n    }\n  };\n\n  for (var i = 0; i < markers.length; i++) loop( i );\n}\n\nvar nextDocId = 0\nvar Doc = function(text, mode, firstLine, lineSep, direction) {\n  if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }\n  if (firstLine == null) { firstLine = 0 }\n\n  BranchChunk.call(this, [new LeafChunk([new Line(\"\", null)])])\n  this.first = firstLine\n  this.scrollTop = this.scrollLeft = 0\n  this.cantEdit = false\n  this.cleanGeneration = 1\n  this.modeFrontier = this.highlightFrontier = firstLine\n  var start = Pos(firstLine, 0)\n  this.sel = simpleSelection(start)\n  this.history = new History(null)\n  this.id = ++nextDocId\n  this.modeOption = mode\n  this.lineSep = lineSep\n  this.direction = (direction == \"rtl\") ? \"rtl\" : \"ltr\"\n  this.extend = false\n\n  if (typeof text == \"string\") { text = this.splitLines(text) }\n  updateDoc(this, {from: start, to: start, text: text})\n  setSelection(this, simpleSelection(start), sel_dontScroll)\n}\n\nDoc.prototype = createObj(BranchChunk.prototype, {\n  constructor: Doc,\n  // Iterate over the document. Supports two forms -- with only one\n  // argument, it calls that for each line in the document. With\n  // three, it iterates over the range given by the first two (with\n  // the second being non-inclusive).\n  iter: function(from, to, op) {\n    if (op) { this.iterN(from - this.first, to - from, op) }\n    else { this.iterN(this.first, this.first + this.size, from) }\n  },\n\n  // Non-public interface for adding and removing lines.\n  insert: function(at, lines) {\n    var height = 0\n    for (var i = 0; i < lines.length; ++i) { height += lines[i].height }\n    this.insertInner(at - this.first, lines, height)\n  },\n  remove: function(at, n) { this.removeInner(at - this.first, n) },\n\n  // From here, the methods are part of the public interface. Most\n  // are also available from CodeMirror (editor) instances.\n\n  getValue: function(lineSep) {\n    var lines = getLines(this, this.first, this.first + this.size)\n    if (lineSep === false) { return lines }\n    return lines.join(lineSep || this.lineSeparator())\n  },\n  setValue: docMethodOp(function(code) {\n    var top = Pos(this.first, 0), last = this.first + this.size - 1\n    makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),\n                      text: this.splitLines(code), origin: \"setValue\", full: true}, true)\n    if (this.cm) { scrollToCoords(this.cm, 0, 0) }\n    setSelection(this, simpleSelection(top), sel_dontScroll)\n  }),\n  replaceRange: function(code, from, to, origin) {\n    from = clipPos(this, from)\n    to = to ? clipPos(this, to) : from\n    replaceRange(this, code, from, to, origin)\n  },\n  getRange: function(from, to, lineSep) {\n    var lines = getBetween(this, clipPos(this, from), clipPos(this, to))\n    if (lineSep === false) { return lines }\n    return lines.join(lineSep || this.lineSeparator())\n  },\n\n  getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},\n\n  getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},\n  getLineNumber: function(line) {return lineNo(line)},\n\n  getLineHandleVisualStart: function(line) {\n    if (typeof line == \"number\") { line = getLine(this, line) }\n    return visualLine(line)\n  },\n\n  lineCount: function() {return this.size},\n  firstLine: function() {return this.first},\n  lastLine: function() {return this.first + this.size - 1},\n\n  clipPos: function(pos) {return clipPos(this, pos)},\n\n  getCursor: function(start) {\n    var range = this.sel.primary(), pos\n    if (start == null || start == \"head\") { pos = range.head }\n    else if (start == \"anchor\") { pos = range.anchor }\n    else if (start == \"end\" || start == \"to\" || start === false) { pos = range.to() }\n    else { pos = range.from() }\n    return pos\n  },\n  listSelections: function() { return this.sel.ranges },\n  somethingSelected: function() {return this.sel.somethingSelected()},\n\n  setCursor: docMethodOp(function(line, ch, options) {\n    setSimpleSelection(this, clipPos(this, typeof line == \"number\" ? Pos(line, ch || 0) : line), null, options)\n  }),\n  setSelection: docMethodOp(function(anchor, head, options) {\n    setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)\n  }),\n  extendSelection: docMethodOp(function(head, other, options) {\n    extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)\n  }),\n  extendSelections: docMethodOp(function(heads, options) {\n    extendSelections(this, clipPosArray(this, heads), options)\n  }),\n  extendSelectionsBy: docMethodOp(function(f, options) {\n    var heads = map(this.sel.ranges, f)\n    extendSelections(this, clipPosArray(this, heads), options)\n  }),\n  setSelections: docMethodOp(function(ranges, primary, options) {\n    var this$1 = this;\n\n    if (!ranges.length) { return }\n    var out = []\n    for (var i = 0; i < ranges.length; i++)\n      { out[i] = new Range(clipPos(this$1, ranges[i].anchor),\n                         clipPos(this$1, ranges[i].head)) }\n    if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }\n    setSelection(this, normalizeSelection(out, primary), options)\n  }),\n  addSelection: docMethodOp(function(anchor, head, options) {\n    var ranges = this.sel.ranges.slice(0)\n    ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))\n    setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)\n  }),\n\n  getSelection: function(lineSep) {\n    var this$1 = this;\n\n    var ranges = this.sel.ranges, lines\n    for (var i = 0; i < ranges.length; i++) {\n      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())\n      lines = lines ? lines.concat(sel) : sel\n    }\n    if (lineSep === false) { return lines }\n    else { return lines.join(lineSep || this.lineSeparator()) }\n  },\n  getSelections: function(lineSep) {\n    var this$1 = this;\n\n    var parts = [], ranges = this.sel.ranges\n    for (var i = 0; i < ranges.length; i++) {\n      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())\n      if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }\n      parts[i] = sel\n    }\n    return parts\n  },\n  replaceSelection: function(code, collapse, origin) {\n    var dup = []\n    for (var i = 0; i < this.sel.ranges.length; i++)\n      { dup[i] = code }\n    this.replaceSelections(dup, collapse, origin || \"+input\")\n  },\n  replaceSelections: docMethodOp(function(code, collapse, origin) {\n    var this$1 = this;\n\n    var changes = [], sel = this.sel\n    for (var i = 0; i < sel.ranges.length; i++) {\n      var range = sel.ranges[i]\n      changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin}\n    }\n    var newSel = collapse && collapse != \"end\" && computeReplacedSel(this, changes, collapse)\n    for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)\n      { makeChange(this$1, changes[i$1]) }\n    if (newSel) { setSelectionReplaceHistory(this, newSel) }\n    else if (this.cm) { ensureCursorVisible(this.cm) }\n  }),\n  undo: docMethodOp(function() {makeChangeFromHistory(this, \"undo\")}),\n  redo: docMethodOp(function() {makeChangeFromHistory(this, \"redo\")}),\n  undoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"undo\", true)}),\n  redoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"redo\", true)}),\n\n  setExtending: function(val) {this.extend = val},\n  getExtending: function() {return this.extend},\n\n  historySize: function() {\n    var hist = this.history, done = 0, undone = 0\n    for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }\n    for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }\n    return {undo: done, redo: undone}\n  },\n  clearHistory: function() {this.history = new History(this.history.maxGeneration)},\n\n  markClean: function() {\n    this.cleanGeneration = this.changeGeneration(true)\n  },\n  changeGeneration: function(forceSplit) {\n    if (forceSplit)\n      { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }\n    return this.history.generation\n  },\n  isClean: function (gen) {\n    return this.history.generation == (gen || this.cleanGeneration)\n  },\n\n  getHistory: function() {\n    return {done: copyHistoryArray(this.history.done),\n            undone: copyHistoryArray(this.history.undone)}\n  },\n  setHistory: function(histData) {\n    var hist = this.history = new History(this.history.maxGeneration)\n    hist.done = copyHistoryArray(histData.done.slice(0), null, true)\n    hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)\n  },\n\n  setGutterMarker: docMethodOp(function(line, gutterID, value) {\n    return changeLine(this, line, \"gutter\", function (line) {\n      var markers = line.gutterMarkers || (line.gutterMarkers = {})\n      markers[gutterID] = value\n      if (!value && isEmpty(markers)) { line.gutterMarkers = null }\n      return true\n    })\n  }),\n\n  clearGutter: docMethodOp(function(gutterID) {\n    var this$1 = this;\n\n    this.iter(function (line) {\n      if (line.gutterMarkers && line.gutterMarkers[gutterID]) {\n        changeLine(this$1, line, \"gutter\", function () {\n          line.gutterMarkers[gutterID] = null\n          if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }\n          return true\n        })\n      }\n    })\n  }),\n\n  lineInfo: function(line) {\n    var n\n    if (typeof line == \"number\") {\n      if (!isLine(this, line)) { return null }\n      n = line\n      line = getLine(this, line)\n      if (!line) { return null }\n    } else {\n      n = lineNo(line)\n      if (n == null) { return null }\n    }\n    return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,\n            textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,\n            widgets: line.widgets}\n  },\n\n  addLineClass: docMethodOp(function(handle, where, cls) {\n    return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n      var prop = where == \"text\" ? \"textClass\"\n               : where == \"background\" ? \"bgClass\"\n               : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\"\n      if (!line[prop]) { line[prop] = cls }\n      else if (classTest(cls).test(line[prop])) { return false }\n      else { line[prop] += \" \" + cls }\n      return true\n    })\n  }),\n  removeLineClass: docMethodOp(function(handle, where, cls) {\n    return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n      var prop = where == \"text\" ? \"textClass\"\n               : where == \"background\" ? \"bgClass\"\n               : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\"\n      var cur = line[prop]\n      if (!cur) { return false }\n      else if (cls == null) { line[prop] = null }\n      else {\n        var found = cur.match(classTest(cls))\n        if (!found) { return false }\n        var end = found.index + found[0].length\n        line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? \"\" : \" \") + cur.slice(end) || null\n      }\n      return true\n    })\n  }),\n\n  addLineWidget: docMethodOp(function(handle, node, options) {\n    return addLineWidget(this, handle, node, options)\n  }),\n  removeLineWidget: function(widget) { widget.clear() },\n\n  markText: function(from, to, options) {\n    return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || \"range\")\n  },\n  setBookmark: function(pos, options) {\n    var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),\n                    insertLeft: options && options.insertLeft,\n                    clearWhenEmpty: false, shared: options && options.shared,\n                    handleMouseEvents: options && options.handleMouseEvents}\n    pos = clipPos(this, pos)\n    return markText(this, pos, pos, realOpts, \"bookmark\")\n  },\n  findMarksAt: function(pos) {\n    pos = clipPos(this, pos)\n    var markers = [], spans = getLine(this, pos.line).markedSpans\n    if (spans) { for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i]\n      if ((span.from == null || span.from <= pos.ch) &&\n          (span.to == null || span.to >= pos.ch))\n        { markers.push(span.marker.parent || span.marker) }\n    } }\n    return markers\n  },\n  findMarks: function(from, to, filter) {\n    from = clipPos(this, from); to = clipPos(this, to)\n    var found = [], lineNo = from.line\n    this.iter(from.line, to.line + 1, function (line) {\n      var spans = line.markedSpans\n      if (spans) { for (var i = 0; i < spans.length; i++) {\n        var span = spans[i]\n        if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||\n              span.from == null && lineNo != from.line ||\n              span.from != null && lineNo == to.line && span.from >= to.ch) &&\n            (!filter || filter(span.marker)))\n          { found.push(span.marker.parent || span.marker) }\n      } }\n      ++lineNo\n    })\n    return found\n  },\n  getAllMarks: function() {\n    var markers = []\n    this.iter(function (line) {\n      var sps = line.markedSpans\n      if (sps) { for (var i = 0; i < sps.length; ++i)\n        { if (sps[i].from != null) { markers.push(sps[i].marker) } } }\n    })\n    return markers\n  },\n\n  posFromIndex: function(off) {\n    var ch, lineNo = this.first, sepSize = this.lineSeparator().length\n    this.iter(function (line) {\n      var sz = line.text.length + sepSize\n      if (sz > off) { ch = off; return true }\n      off -= sz\n      ++lineNo\n    })\n    return clipPos(this, Pos(lineNo, ch))\n  },\n  indexFromPos: function (coords) {\n    coords = clipPos(this, coords)\n    var index = coords.ch\n    if (coords.line < this.first || coords.ch < 0) { return 0 }\n    var sepSize = this.lineSeparator().length\n    this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value\n      index += line.text.length + sepSize\n    })\n    return index\n  },\n\n  copy: function(copyHistory) {\n    var doc = new Doc(getLines(this, this.first, this.first + this.size),\n                      this.modeOption, this.first, this.lineSep, this.direction)\n    doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft\n    doc.sel = this.sel\n    doc.extend = false\n    if (copyHistory) {\n      doc.history.undoDepth = this.history.undoDepth\n      doc.setHistory(this.getHistory())\n    }\n    return doc\n  },\n\n  linkedDoc: function(options) {\n    if (!options) { options = {} }\n    var from = this.first, to = this.first + this.size\n    if (options.from != null && options.from > from) { from = options.from }\n    if (options.to != null && options.to < to) { to = options.to }\n    var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)\n    if (options.sharedHist) { copy.history = this.history\n    ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})\n    copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]\n    copySharedMarkers(copy, findSharedMarkers(this))\n    return copy\n  },\n  unlinkDoc: function(other) {\n    var this$1 = this;\n\n    if (other instanceof CodeMirror) { other = other.doc }\n    if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {\n      var link = this$1.linked[i]\n      if (link.doc != other) { continue }\n      this$1.linked.splice(i, 1)\n      other.unlinkDoc(this$1)\n      detachSharedMarkers(findSharedMarkers(this$1))\n      break\n    } }\n    // If the histories were shared, split them again\n    if (other.history == this.history) {\n      var splitIds = [other.id]\n      linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)\n      other.history = new History(null)\n      other.history.done = copyHistoryArray(this.history.done, splitIds)\n      other.history.undone = copyHistoryArray(this.history.undone, splitIds)\n    }\n  },\n  iterLinkedDocs: function(f) {linkedDocs(this, f)},\n\n  getMode: function() {return this.mode},\n  getEditor: function() {return this.cm},\n\n  splitLines: function(str) {\n    if (this.lineSep) { return str.split(this.lineSep) }\n    return splitLinesAuto(str)\n  },\n  lineSeparator: function() { return this.lineSep || \"\\n\" },\n\n  setDirection: docMethodOp(function (dir) {\n    if (dir != \"rtl\") { dir = \"ltr\" }\n    if (dir == this.direction) { return }\n    this.direction = dir\n    this.iter(function (line) { return line.order = null; })\n    if (this.cm) { directionChanged(this.cm) }\n  })\n})\n\n// Public alias.\nDoc.prototype.eachLine = Doc.prototype.iter\n\n// Kludge to work around strange IE behavior where it'll sometimes\n// re-fire a series of drag-related events right after the drop (#1551)\nvar lastDrop = 0\n\nfunction onDrop(e) {\n  var cm = this\n  clearDragCursor(cm)\n  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))\n    { return }\n  e_preventDefault(e)\n  if (ie) { lastDrop = +new Date }\n  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files\n  if (!pos || cm.isReadOnly()) { return }\n  // Might be a file drop, in which case we simply extract the text\n  // and insert it.\n  if (files && files.length && window.FileReader && window.File) {\n    var n = files.length, text = Array(n), read = 0\n    var loadFile = function (file, i) {\n      if (cm.options.allowDropFileTypes &&\n          indexOf(cm.options.allowDropFileTypes, file.type) == -1)\n        { return }\n\n      var reader = new FileReader\n      reader.onload = operation(cm, function () {\n        var content = reader.result\n        if (/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(content)) { content = \"\" }\n        text[i] = content\n        if (++read == n) {\n          pos = clipPos(cm.doc, pos)\n          var change = {from: pos, to: pos,\n                        text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),\n                        origin: \"paste\"}\n          makeChange(cm.doc, change)\n          setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))\n        }\n      })\n      reader.readAsText(file)\n    }\n    for (var i = 0; i < n; ++i) { loadFile(files[i], i) }\n  } else { // Normal drop\n    // Don't do a replace if the drop happened inside of the selected text.\n    if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {\n      cm.state.draggingText(e)\n      // Ensure the editor is re-focused\n      setTimeout(function () { return cm.display.input.focus(); }, 20)\n      return\n    }\n    try {\n      var text$1 = e.dataTransfer.getData(\"Text\")\n      if (text$1) {\n        var selected\n        if (cm.state.draggingText && !cm.state.draggingText.copy)\n          { selected = cm.listSelections() }\n        setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))\n        if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)\n          { replaceRange(cm.doc, \"\", selected[i$1].anchor, selected[i$1].head, \"drag\") } }\n        cm.replaceSelection(text$1, \"around\", \"paste\")\n        cm.display.input.focus()\n      }\n    }\n    catch(e){}\n  }\n}\n\nfunction onDragStart(cm, e) {\n  if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }\n  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }\n\n  e.dataTransfer.setData(\"Text\", cm.getSelection())\n  e.dataTransfer.effectAllowed = \"copyMove\"\n\n  // Use dummy image instead of default browsers image.\n  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.\n  if (e.dataTransfer.setDragImage && !safari) {\n    var img = elt(\"img\", null, null, \"position: fixed; left: 0; top: 0;\")\n    img.src = \"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\"\n    if (presto) {\n      img.width = img.height = 1\n      cm.display.wrapper.appendChild(img)\n      // Force a relayout, or Opera won't use our image for some obscure reason\n      img._top = img.offsetTop\n    }\n    e.dataTransfer.setDragImage(img, 0, 0)\n    if (presto) { img.parentNode.removeChild(img) }\n  }\n}\n\nfunction onDragOver(cm, e) {\n  var pos = posFromMouse(cm, e)\n  if (!pos) { return }\n  var frag = document.createDocumentFragment()\n  drawSelectionCursor(cm, pos, frag)\n  if (!cm.display.dragCursor) {\n    cm.display.dragCursor = elt(\"div\", null, \"CodeMirror-cursors CodeMirror-dragcursors\")\n    cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)\n  }\n  removeChildrenAndAdd(cm.display.dragCursor, frag)\n}\n\nfunction clearDragCursor(cm) {\n  if (cm.display.dragCursor) {\n    cm.display.lineSpace.removeChild(cm.display.dragCursor)\n    cm.display.dragCursor = null\n  }\n}\n\n// These must be handled carefully, because naively registering a\n// handler for each editor will cause the editors to never be\n// garbage collected.\n\nfunction forEachCodeMirror(f) {\n  if (!document.getElementsByClassName) { return }\n  var byClass = document.getElementsByClassName(\"CodeMirror\")\n  for (var i = 0; i < byClass.length; i++) {\n    var cm = byClass[i].CodeMirror\n    if (cm) { f(cm) }\n  }\n}\n\nvar globalsRegistered = false\nfunction ensureGlobalHandlers() {\n  if (globalsRegistered) { return }\n  registerGlobalHandlers()\n  globalsRegistered = true\n}\nfunction registerGlobalHandlers() {\n  // When the window resizes, we need to refresh active editors.\n  var resizeTimer\n  on(window, \"resize\", function () {\n    if (resizeTimer == null) { resizeTimer = setTimeout(function () {\n      resizeTimer = null\n      forEachCodeMirror(onResize)\n    }, 100) }\n  })\n  // When the window loses focus, we want to show the editor as blurred\n  on(window, \"blur\", function () { return forEachCodeMirror(onBlur); })\n}\n// Called when the window resizes\nfunction onResize(cm) {\n  var d = cm.display\n  if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)\n    { return }\n  // Might be a text scaling operation, clear size caches.\n  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null\n  d.scrollbarsClipped = false\n  cm.setSize()\n}\n\nvar keyNames = {\n  3: \"Pause\", 8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 16: \"Shift\", 17: \"Ctrl\", 18: \"Alt\",\n  19: \"Pause\", 20: \"CapsLock\", 27: \"Esc\", 32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\",\n  36: \"Home\", 37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"PrintScrn\", 45: \"Insert\",\n  46: \"Delete\", 59: \";\", 61: \"=\", 91: \"Mod\", 92: \"Mod\", 93: \"Mod\",\n  106: \"*\", 107: \"=\", 109: \"-\", 110: \".\", 111: \"/\", 127: \"Delete\", 145: \"ScrollLock\",\n  173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\", 219: \"[\", 220: \"\\\\\",\n  221: \"]\", 222: \"'\", 63232: \"Up\", 63233: \"Down\", 63234: \"Left\", 63235: \"Right\", 63272: \"Delete\",\n  63273: \"Home\", 63275: \"End\", 63276: \"PageUp\", 63277: \"PageDown\", 63302: \"Insert\"\n}\n\n// Number keys\nfor (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }\n// Alphabetic keys\nfor (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }\n// Function keys\nfor (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = \"F\" + i$2 }\n\nvar keyMap = {}\n\nkeyMap.basic = {\n  \"Left\": \"goCharLeft\", \"Right\": \"goCharRight\", \"Up\": \"goLineUp\", \"Down\": \"goLineDown\",\n  \"End\": \"goLineEnd\", \"Home\": \"goLineStartSmart\", \"PageUp\": \"goPageUp\", \"PageDown\": \"goPageDown\",\n  \"Delete\": \"delCharAfter\", \"Backspace\": \"delCharBefore\", \"Shift-Backspace\": \"delCharBefore\",\n  \"Tab\": \"defaultTab\", \"Shift-Tab\": \"indentAuto\",\n  \"Enter\": \"newlineAndIndent\", \"Insert\": \"toggleOverwrite\",\n  \"Esc\": \"singleSelection\"\n}\n// Note that the save and find-related commands aren't defined by\n// default. User code or addons can define them. Unknown commands\n// are simply ignored.\nkeyMap.pcDefault = {\n  \"Ctrl-A\": \"selectAll\", \"Ctrl-D\": \"deleteLine\", \"Ctrl-Z\": \"undo\", \"Shift-Ctrl-Z\": \"redo\", \"Ctrl-Y\": \"redo\",\n  \"Ctrl-Home\": \"goDocStart\", \"Ctrl-End\": \"goDocEnd\", \"Ctrl-Up\": \"goLineUp\", \"Ctrl-Down\": \"goLineDown\",\n  \"Ctrl-Left\": \"goGroupLeft\", \"Ctrl-Right\": \"goGroupRight\", \"Alt-Left\": \"goLineStart\", \"Alt-Right\": \"goLineEnd\",\n  \"Ctrl-Backspace\": \"delGroupBefore\", \"Ctrl-Delete\": \"delGroupAfter\", \"Ctrl-S\": \"save\", \"Ctrl-F\": \"find\",\n  \"Ctrl-G\": \"findNext\", \"Shift-Ctrl-G\": \"findPrev\", \"Shift-Ctrl-F\": \"replace\", \"Shift-Ctrl-R\": \"replaceAll\",\n  \"Ctrl-[\": \"indentLess\", \"Ctrl-]\": \"indentMore\",\n  \"Ctrl-U\": \"undoSelection\", \"Shift-Ctrl-U\": \"redoSelection\", \"Alt-U\": \"redoSelection\",\n  fallthrough: \"basic\"\n}\n// Very basic readline/emacs-style bindings, which are standard on Mac.\nkeyMap.emacsy = {\n  \"Ctrl-F\": \"goCharRight\", \"Ctrl-B\": \"goCharLeft\", \"Ctrl-P\": \"goLineUp\", \"Ctrl-N\": \"goLineDown\",\n  \"Alt-F\": \"goWordRight\", \"Alt-B\": \"goWordLeft\", \"Ctrl-A\": \"goLineStart\", \"Ctrl-E\": \"goLineEnd\",\n  \"Ctrl-V\": \"goPageDown\", \"Shift-Ctrl-V\": \"goPageUp\", \"Ctrl-D\": \"delCharAfter\", \"Ctrl-H\": \"delCharBefore\",\n  \"Alt-D\": \"delWordAfter\", \"Alt-Backspace\": \"delWordBefore\", \"Ctrl-K\": \"killLine\", \"Ctrl-T\": \"transposeChars\",\n  \"Ctrl-O\": \"openLine\"\n}\nkeyMap.macDefault = {\n  \"Cmd-A\": \"selectAll\", \"Cmd-D\": \"deleteLine\", \"Cmd-Z\": \"undo\", \"Shift-Cmd-Z\": \"redo\", \"Cmd-Y\": \"redo\",\n  \"Cmd-Home\": \"goDocStart\", \"Cmd-Up\": \"goDocStart\", \"Cmd-End\": \"goDocEnd\", \"Cmd-Down\": \"goDocEnd\", \"Alt-Left\": \"goGroupLeft\",\n  \"Alt-Right\": \"goGroupRight\", \"Cmd-Left\": \"goLineLeft\", \"Cmd-Right\": \"goLineRight\", \"Alt-Backspace\": \"delGroupBefore\",\n  \"Ctrl-Alt-Backspace\": \"delGroupAfter\", \"Alt-Delete\": \"delGroupAfter\", \"Cmd-S\": \"save\", \"Cmd-F\": \"find\",\n  \"Cmd-G\": \"findNext\", \"Shift-Cmd-G\": \"findPrev\", \"Cmd-Alt-F\": \"replace\", \"Shift-Cmd-Alt-F\": \"replaceAll\",\n  \"Cmd-[\": \"indentLess\", \"Cmd-]\": \"indentMore\", \"Cmd-Backspace\": \"delWrappedLineLeft\", \"Cmd-Delete\": \"delWrappedLineRight\",\n  \"Cmd-U\": \"undoSelection\", \"Shift-Cmd-U\": \"redoSelection\", \"Ctrl-Up\": \"goDocStart\", \"Ctrl-Down\": \"goDocEnd\",\n  fallthrough: [\"basic\", \"emacsy\"]\n}\nkeyMap[\"default\"] = mac ? keyMap.macDefault : keyMap.pcDefault\n\n// KEYMAP DISPATCH\n\nfunction normalizeKeyName(name) {\n  var parts = name.split(/-(?!$)/)\n  name = parts[parts.length - 1]\n  var alt, ctrl, shift, cmd\n  for (var i = 0; i < parts.length - 1; i++) {\n    var mod = parts[i]\n    if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }\n    else if (/^a(lt)?$/i.test(mod)) { alt = true }\n    else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }\n    else if (/^s(hift)?$/i.test(mod)) { shift = true }\n    else { throw new Error(\"Unrecognized modifier name: \" + mod) }\n  }\n  if (alt) { name = \"Alt-\" + name }\n  if (ctrl) { name = \"Ctrl-\" + name }\n  if (cmd) { name = \"Cmd-\" + name }\n  if (shift) { name = \"Shift-\" + name }\n  return name\n}\n\n// This is a kludge to keep keymaps mostly working as raw objects\n// (backwards compatibility) while at the same time support features\n// like normalization and multi-stroke key bindings. It compiles a\n// new normalized keymap, and then updates the old object to reflect\n// this.\nfunction normalizeKeyMap(keymap) {\n  var copy = {}\n  for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {\n    var value = keymap[keyname]\n    if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }\n    if (value == \"...\") { delete keymap[keyname]; continue }\n\n    var keys = map(keyname.split(\" \"), normalizeKeyName)\n    for (var i = 0; i < keys.length; i++) {\n      var val = (void 0), name = (void 0)\n      if (i == keys.length - 1) {\n        name = keys.join(\" \")\n        val = value\n      } else {\n        name = keys.slice(0, i + 1).join(\" \")\n        val = \"...\"\n      }\n      var prev = copy[name]\n      if (!prev) { copy[name] = val }\n      else if (prev != val) { throw new Error(\"Inconsistent bindings for \" + name) }\n    }\n    delete keymap[keyname]\n  } }\n  for (var prop in copy) { keymap[prop] = copy[prop] }\n  return keymap\n}\n\nfunction lookupKey(key, map, handle, context) {\n  map = getKeyMap(map)\n  var found = map.call ? map.call(key, context) : map[key]\n  if (found === false) { return \"nothing\" }\n  if (found === \"...\") { return \"multi\" }\n  if (found != null && handle(found)) { return \"handled\" }\n\n  if (map.fallthrough) {\n    if (Object.prototype.toString.call(map.fallthrough) != \"[object Array]\")\n      { return lookupKey(key, map.fallthrough, handle, context) }\n    for (var i = 0; i < map.fallthrough.length; i++) {\n      var result = lookupKey(key, map.fallthrough[i], handle, context)\n      if (result) { return result }\n    }\n  }\n}\n\n// Modifier key presses don't count as 'real' key presses for the\n// purpose of keymap fallthrough.\nfunction isModifierKey(value) {\n  var name = typeof value == \"string\" ? value : keyNames[value.keyCode]\n  return name == \"Ctrl\" || name == \"Alt\" || name == \"Shift\" || name == \"Mod\"\n}\n\nfunction addModifierNames(name, event, noShift) {\n  var base = name\n  if (event.altKey && base != \"Alt\") { name = \"Alt-\" + name }\n  if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != \"Ctrl\") { name = \"Ctrl-\" + name }\n  if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != \"Cmd\") { name = \"Cmd-\" + name }\n  if (!noShift && event.shiftKey && base != \"Shift\") { name = \"Shift-\" + name }\n  return name\n}\n\n// Look up the name of a key as indicated by an event object.\nfunction keyName(event, noShift) {\n  if (presto && event.keyCode == 34 && event[\"char\"]) { return false }\n  var name = keyNames[event.keyCode]\n  if (name == null || event.altGraphKey) { return false }\n  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,\n  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)\n  if (event.keyCode == 3 && event.code) { name = event.code }\n  return addModifierNames(name, event, noShift)\n}\n\nfunction getKeyMap(val) {\n  return typeof val == \"string\" ? keyMap[val] : val\n}\n\n// Helper for deleting text near the selection(s), used to implement\n// backspace, delete, and similar functionality.\nfunction deleteNearSelection(cm, compute) {\n  var ranges = cm.doc.sel.ranges, kill = []\n  // Build up a set of ranges to kill first, merging overlapping\n  // ranges.\n  for (var i = 0; i < ranges.length; i++) {\n    var toKill = compute(ranges[i])\n    while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {\n      var replaced = kill.pop()\n      if (cmp(replaced.from, toKill.from) < 0) {\n        toKill.from = replaced.from\n        break\n      }\n    }\n    kill.push(toKill)\n  }\n  // Next, remove those actual ranges.\n  runInOp(cm, function () {\n    for (var i = kill.length - 1; i >= 0; i--)\n      { replaceRange(cm.doc, \"\", kill[i].from, kill[i].to, \"+delete\") }\n    ensureCursorVisible(cm)\n  })\n}\n\nfunction moveCharLogically(line, ch, dir) {\n  var target = skipExtendingChars(line.text, ch + dir, dir)\n  return target < 0 || target > line.text.length ? null : target\n}\n\nfunction moveLogically(line, start, dir) {\n  var ch = moveCharLogically(line, start.ch, dir)\n  return ch == null ? null : new Pos(start.line, ch, dir < 0 ? \"after\" : \"before\")\n}\n\nfunction endOfLine(visually, cm, lineObj, lineNo, dir) {\n  if (visually) {\n    var order = getOrder(lineObj, cm.doc.direction)\n    if (order) {\n      var part = dir < 0 ? lst(order) : order[0]\n      var moveInStorageOrder = (dir < 0) == (part.level == 1)\n      var sticky = moveInStorageOrder ? \"after\" : \"before\"\n      var ch\n      // With a wrapped rtl chunk (possibly spanning multiple bidi parts),\n      // it could be that the last bidi part is not on the last visual line,\n      // since visual lines contain content order-consecutive chunks.\n      // Thus, in rtl, we are looking for the first (content-order) character\n      // in the rtl chunk that is on the last line (that is, the same line\n      // as the last (content-order) character).\n      if (part.level > 0 || cm.doc.direction == \"rtl\") {\n        var prep = prepareMeasureForLine(cm, lineObj)\n        ch = dir < 0 ? lineObj.text.length - 1 : 0\n        var targetTop = measureCharPrepared(cm, prep, ch).top\n        ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)\n        if (sticky == \"before\") { ch = moveCharLogically(lineObj, ch, 1) }\n      } else { ch = dir < 0 ? part.to : part.from }\n      return new Pos(lineNo, ch, sticky)\n    }\n  }\n  return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? \"before\" : \"after\")\n}\n\nfunction moveVisually(cm, line, start, dir) {\n  var bidi = getOrder(line, cm.doc.direction)\n  if (!bidi) { return moveLogically(line, start, dir) }\n  if (start.ch >= line.text.length) {\n    start.ch = line.text.length\n    start.sticky = \"before\"\n  } else if (start.ch <= 0) {\n    start.ch = 0\n    start.sticky = \"after\"\n  }\n  var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]\n  if (cm.doc.direction == \"ltr\" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {\n    // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,\n    // nothing interesting happens.\n    return moveLogically(line, start, dir)\n  }\n\n  var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }\n  var prep\n  var getWrappedLineExtent = function (ch) {\n    if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }\n    prep = prep || prepareMeasureForLine(cm, line)\n    return wrappedLineExtentChar(cm, line, prep, ch)\n  }\n  var wrappedLineExtent = getWrappedLineExtent(start.sticky == \"before\" ? mv(start, -1) : start.ch)\n\n  if (cm.doc.direction == \"rtl\" || part.level == 1) {\n    var moveInStorageOrder = (part.level == 1) == (dir < 0)\n    var ch = mv(start, moveInStorageOrder ? 1 : -1)\n    if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {\n      // Case 2: We move within an rtl part or in an rtl editor on the same visual line\n      var sticky = moveInStorageOrder ? \"before\" : \"after\"\n      return new Pos(start.line, ch, sticky)\n    }\n  }\n\n  // Case 3: Could not move within this bidi part in this visual line, so leave\n  // the current bidi part\n\n  var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {\n    var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder\n      ? new Pos(start.line, mv(ch, 1), \"before\")\n      : new Pos(start.line, ch, \"after\"); }\n\n    for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {\n      var part = bidi[partPos]\n      var moveInStorageOrder = (dir > 0) == (part.level != 1)\n      var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)\n      if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }\n      ch = moveInStorageOrder ? part.from : mv(part.to, -1)\n      if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }\n    }\n  }\n\n  // Case 3a: Look for other bidi parts on the same visual line\n  var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)\n  if (res) { return res }\n\n  // Case 3b: Look for other bidi parts on the next visual line\n  var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)\n  if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {\n    res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))\n    if (res) { return res }\n  }\n\n  // Case 4: Nowhere to move\n  return null\n}\n\n// Commands are parameter-less actions that can be performed on an\n// editor, mostly used for keybindings.\nvar commands = {\n  selectAll: selectAll,\n  singleSelection: function (cm) { return cm.setSelection(cm.getCursor(\"anchor\"), cm.getCursor(\"head\"), sel_dontScroll); },\n  killLine: function (cm) { return deleteNearSelection(cm, function (range) {\n    if (range.empty()) {\n      var len = getLine(cm.doc, range.head.line).text.length\n      if (range.head.ch == len && range.head.line < cm.lastLine())\n        { return {from: range.head, to: Pos(range.head.line + 1, 0)} }\n      else\n        { return {from: range.head, to: Pos(range.head.line, len)} }\n    } else {\n      return {from: range.from(), to: range.to()}\n    }\n  }); },\n  deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n    from: Pos(range.from().line, 0),\n    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))\n  }); }); },\n  delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n    from: Pos(range.from().line, 0), to: range.from()\n  }); }); },\n  delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {\n    var top = cm.charCoords(range.head, \"div\").top + 5\n    var leftPos = cm.coordsChar({left: 0, top: top}, \"div\")\n    return {from: leftPos, to: range.from()}\n  }); },\n  delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {\n    var top = cm.charCoords(range.head, \"div\").top + 5\n    var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n    return {from: range.from(), to: rightPos }\n  }); },\n  undo: function (cm) { return cm.undo(); },\n  redo: function (cm) { return cm.redo(); },\n  undoSelection: function (cm) { return cm.undoSelection(); },\n  redoSelection: function (cm) { return cm.redoSelection(); },\n  goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },\n  goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },\n  goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },\n    {origin: \"+move\", bias: 1}\n  ); },\n  goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },\n    {origin: \"+move\", bias: 1}\n  ); },\n  goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },\n    {origin: \"+move\", bias: -1}\n  ); },\n  goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {\n    var top = cm.cursorCoords(range.head, \"div\").top + 5\n    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n  }, sel_move); },\n  goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {\n    var top = cm.cursorCoords(range.head, \"div\").top + 5\n    return cm.coordsChar({left: 0, top: top}, \"div\")\n  }, sel_move); },\n  goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {\n    var top = cm.cursorCoords(range.head, \"div\").top + 5\n    var pos = cm.coordsChar({left: 0, top: top}, \"div\")\n    if (pos.ch < cm.getLine(pos.line).search(/\\S/)) { return lineStartSmart(cm, range.head) }\n    return pos\n  }, sel_move); },\n  goLineUp: function (cm) { return cm.moveV(-1, \"line\"); },\n  goLineDown: function (cm) { return cm.moveV(1, \"line\"); },\n  goPageUp: function (cm) { return cm.moveV(-1, \"page\"); },\n  goPageDown: function (cm) { return cm.moveV(1, \"page\"); },\n  goCharLeft: function (cm) { return cm.moveH(-1, \"char\"); },\n  goCharRight: function (cm) { return cm.moveH(1, \"char\"); },\n  goColumnLeft: function (cm) { return cm.moveH(-1, \"column\"); },\n  goColumnRight: function (cm) { return cm.moveH(1, \"column\"); },\n  goWordLeft: function (cm) { return cm.moveH(-1, \"word\"); },\n  goGroupRight: function (cm) { return cm.moveH(1, \"group\"); },\n  goGroupLeft: function (cm) { return cm.moveH(-1, \"group\"); },\n  goWordRight: function (cm) { return cm.moveH(1, \"word\"); },\n  delCharBefore: function (cm) { return cm.deleteH(-1, \"char\"); },\n  delCharAfter: function (cm) { return cm.deleteH(1, \"char\"); },\n  delWordBefore: function (cm) { return cm.deleteH(-1, \"word\"); },\n  delWordAfter: function (cm) { return cm.deleteH(1, \"word\"); },\n  delGroupBefore: function (cm) { return cm.deleteH(-1, \"group\"); },\n  delGroupAfter: function (cm) { return cm.deleteH(1, \"group\"); },\n  indentAuto: function (cm) { return cm.indentSelection(\"smart\"); },\n  indentMore: function (cm) { return cm.indentSelection(\"add\"); },\n  indentLess: function (cm) { return cm.indentSelection(\"subtract\"); },\n  insertTab: function (cm) { return cm.replaceSelection(\"\\t\"); },\n  insertSoftTab: function (cm) {\n    var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize\n    for (var i = 0; i < ranges.length; i++) {\n      var pos = ranges[i].from()\n      var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)\n      spaces.push(spaceStr(tabSize - col % tabSize))\n    }\n    cm.replaceSelections(spaces)\n  },\n  defaultTab: function (cm) {\n    if (cm.somethingSelected()) { cm.indentSelection(\"add\") }\n    else { cm.execCommand(\"insertTab\") }\n  },\n  // Swap the two chars left and right of each selection's head.\n  // Move cursor behind the two swapped characters afterwards.\n  //\n  // Doesn't consider line feeds a character.\n  // Doesn't scan more than one line above to find a character.\n  // Doesn't do anything on an empty line.\n  // Doesn't do anything with non-empty selections.\n  transposeChars: function (cm) { return runInOp(cm, function () {\n    var ranges = cm.listSelections(), newSel = []\n    for (var i = 0; i < ranges.length; i++) {\n      if (!ranges[i].empty()) { continue }\n      var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text\n      if (line) {\n        if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }\n        if (cur.ch > 0) {\n          cur = new Pos(cur.line, cur.ch + 1)\n          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),\n                          Pos(cur.line, cur.ch - 2), cur, \"+transpose\")\n        } else if (cur.line > cm.doc.first) {\n          var prev = getLine(cm.doc, cur.line - 1).text\n          if (prev) {\n            cur = new Pos(cur.line, 1)\n            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +\n                            prev.charAt(prev.length - 1),\n                            Pos(cur.line - 1, prev.length - 1), cur, \"+transpose\")\n          }\n        }\n      }\n      newSel.push(new Range(cur, cur))\n    }\n    cm.setSelections(newSel)\n  }); },\n  newlineAndIndent: function (cm) { return runInOp(cm, function () {\n    var sels = cm.listSelections()\n    for (var i = sels.length - 1; i >= 0; i--)\n      { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, \"+input\") }\n    sels = cm.listSelections()\n    for (var i$1 = 0; i$1 < sels.length; i$1++)\n      { cm.indentLine(sels[i$1].from().line, null, true) }\n    ensureCursorVisible(cm)\n  }); },\n  openLine: function (cm) { return cm.replaceSelection(\"\\n\", \"start\"); },\n  toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }\n}\n\n\nfunction lineStart(cm, lineN) {\n  var line = getLine(cm.doc, lineN)\n  var visual = visualLine(line)\n  if (visual != line) { lineN = lineNo(visual) }\n  return endOfLine(true, cm, visual, lineN, 1)\n}\nfunction lineEnd(cm, lineN) {\n  var line = getLine(cm.doc, lineN)\n  var visual = visualLineEnd(line)\n  if (visual != line) { lineN = lineNo(visual) }\n  return endOfLine(true, cm, line, lineN, -1)\n}\nfunction lineStartSmart(cm, pos) {\n  var start = lineStart(cm, pos.line)\n  var line = getLine(cm.doc, start.line)\n  var order = getOrder(line, cm.doc.direction)\n  if (!order || order[0].level == 0) {\n    var firstNonWS = Math.max(0, line.text.search(/\\S/))\n    var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch\n    return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)\n  }\n  return start\n}\n\n// Run a handler that was bound to a key.\nfunction doHandleBinding(cm, bound, dropShift) {\n  if (typeof bound == \"string\") {\n    bound = commands[bound]\n    if (!bound) { return false }\n  }\n  // Ensure previous input has been read, so that the handler sees a\n  // consistent view of the document\n  cm.display.input.ensurePolled()\n  var prevShift = cm.display.shift, done = false\n  try {\n    if (cm.isReadOnly()) { cm.state.suppressEdits = true }\n    if (dropShift) { cm.display.shift = false }\n    done = bound(cm) != Pass\n  } finally {\n    cm.display.shift = prevShift\n    cm.state.suppressEdits = false\n  }\n  return done\n}\n\nfunction lookupKeyForEditor(cm, name, handle) {\n  for (var i = 0; i < cm.state.keyMaps.length; i++) {\n    var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)\n    if (result) { return result }\n  }\n  return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))\n    || lookupKey(name, cm.options.keyMap, handle, cm)\n}\n\n// Note that, despite the name, this function is also used to check\n// for bound mouse clicks.\n\nvar stopSeq = new Delayed\n\nfunction dispatchKey(cm, name, e, handle) {\n  var seq = cm.state.keySeq\n  if (seq) {\n    if (isModifierKey(name)) { return \"handled\" }\n    if (/\\'$/.test(name))\n      { cm.state.keySeq = null }\n    else\n      { stopSeq.set(50, function () {\n        if (cm.state.keySeq == seq) {\n          cm.state.keySeq = null\n          cm.display.input.reset()\n        }\n      }) }\n    if (dispatchKeyInner(cm, seq + \" \" + name, e, handle)) { return true }\n  }\n  return dispatchKeyInner(cm, name, e, handle)\n}\n\nfunction dispatchKeyInner(cm, name, e, handle) {\n  var result = lookupKeyForEditor(cm, name, handle)\n\n  if (result == \"multi\")\n    { cm.state.keySeq = name }\n  if (result == \"handled\")\n    { signalLater(cm, \"keyHandled\", cm, name, e) }\n\n  if (result == \"handled\" || result == \"multi\") {\n    e_preventDefault(e)\n    restartBlink(cm)\n  }\n\n  return !!result\n}\n\n// Handle a key from the keydown event.\nfunction handleKeyBinding(cm, e) {\n  var name = keyName(e, true)\n  if (!name) { return false }\n\n  if (e.shiftKey && !cm.state.keySeq) {\n    // First try to resolve full name (including 'Shift-'). Failing\n    // that, see if there is a cursor-motion command (starting with\n    // 'go') bound to the keyname without 'Shift-'.\n    return dispatchKey(cm, \"Shift-\" + name, e, function (b) { return doHandleBinding(cm, b, true); })\n        || dispatchKey(cm, name, e, function (b) {\n             if (typeof b == \"string\" ? /^go[A-Z]/.test(b) : b.motion)\n               { return doHandleBinding(cm, b) }\n           })\n  } else {\n    return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })\n  }\n}\n\n// Handle a key from the keypress event\nfunction handleCharBinding(cm, e, ch) {\n  return dispatchKey(cm, \"'\" + ch + \"'\", e, function (b) { return doHandleBinding(cm, b, true); })\n}\n\nvar lastStoppedKey = null\nfunction onKeyDown(e) {\n  var cm = this\n  cm.curOp.focus = activeElt()\n  if (signalDOMEvent(cm, e)) { return }\n  // IE does strange things with escape.\n  if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }\n  var code = e.keyCode\n  cm.display.shift = code == 16 || e.shiftKey\n  var handled = handleKeyBinding(cm, e)\n  if (presto) {\n    lastStoppedKey = handled ? code : null\n    // Opera has no cut event... we try to at least catch the key combo\n    if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))\n      { cm.replaceSelection(\"\", null, \"cut\") }\n  }\n\n  // Turn mouse into crosshair when Alt is held on Mac.\n  if (code == 18 && !/\\bCodeMirror-crosshair\\b/.test(cm.display.lineDiv.className))\n    { showCrossHair(cm) }\n}\n\nfunction showCrossHair(cm) {\n  var lineDiv = cm.display.lineDiv\n  addClass(lineDiv, \"CodeMirror-crosshair\")\n\n  function up(e) {\n    if (e.keyCode == 18 || !e.altKey) {\n      rmClass(lineDiv, \"CodeMirror-crosshair\")\n      off(document, \"keyup\", up)\n      off(document, \"mouseover\", up)\n    }\n  }\n  on(document, \"keyup\", up)\n  on(document, \"mouseover\", up)\n}\n\nfunction onKeyUp(e) {\n  if (e.keyCode == 16) { this.doc.sel.shift = false }\n  signalDOMEvent(this, e)\n}\n\nfunction onKeyPress(e) {\n  var cm = this\n  if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }\n  var keyCode = e.keyCode, charCode = e.charCode\n  if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}\n  if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }\n  var ch = String.fromCharCode(charCode == null ? keyCode : charCode)\n  // Some browsers fire keypress events for backspace\n  if (ch == \"\\x08\") { return }\n  if (handleCharBinding(cm, e, ch)) { return }\n  cm.display.input.onKeyPress(e)\n}\n\nvar DOUBLECLICK_DELAY = 400\n\nvar PastClick = function(time, pos, button) {\n  this.time = time\n  this.pos = pos\n  this.button = button\n};\n\nPastClick.prototype.compare = function (time, pos, button) {\n  return this.time + DOUBLECLICK_DELAY > time &&\n    cmp(pos, this.pos) == 0 && button == this.button\n};\n\nvar lastClick;\nvar lastDoubleClick;\nfunction clickRepeat(pos, button) {\n  var now = +new Date\n  if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {\n    lastClick = lastDoubleClick = null\n    return \"triple\"\n  } else if (lastClick && lastClick.compare(now, pos, button)) {\n    lastDoubleClick = new PastClick(now, pos, button)\n    lastClick = null\n    return \"double\"\n  } else {\n    lastClick = new PastClick(now, pos, button)\n    lastDoubleClick = null\n    return \"single\"\n  }\n}\n\n// A mouse down can be a single click, double click, triple click,\n// start of selection drag, start of text drag, new cursor\n// (ctrl-click), rectangle drag (alt-drag), or xwin\n// middle-click-paste. Or it might be a click on something we should\n// not interfere with, such as a scrollbar or widget.\nfunction onMouseDown(e) {\n  var cm = this, display = cm.display\n  if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }\n  display.input.ensurePolled()\n  display.shift = e.shiftKey\n\n  if (eventInWidget(display, e)) {\n    if (!webkit) {\n      // Briefly turn off draggability, to allow widgets to do\n      // normal dragging things.\n      display.scroller.draggable = false\n      setTimeout(function () { return display.scroller.draggable = true; }, 100)\n    }\n    return\n  }\n  if (clickInGutter(cm, e)) { return }\n  var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : \"single\"\n  window.focus()\n\n  // #3261: make sure, that we're not starting a second selection\n  if (button == 1 && cm.state.selectingText)\n    { cm.state.selectingText(e) }\n\n  if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }\n\n  if (button == 1) {\n    if (pos) { leftButtonDown(cm, pos, repeat, e) }\n    else if (e_target(e) == display.scroller) { e_preventDefault(e) }\n  } else if (button == 2) {\n    if (pos) { extendSelection(cm.doc, pos) }\n    setTimeout(function () { return display.input.focus(); }, 20)\n  } else if (button == 3) {\n    if (captureRightClick) { onContextMenu(cm, e) }\n    else { delayBlurEvent(cm) }\n  }\n}\n\nfunction handleMappedButton(cm, button, pos, repeat, event) {\n  var name = \"Click\"\n  if (repeat == \"double\") { name = \"Double\" + name }\n  else if (repeat == \"triple\") { name = \"Triple\" + name }\n  name = (button == 1 ? \"Left\" : button == 2 ? \"Middle\" : \"Right\") + name\n\n  return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {\n    if (typeof bound == \"string\") { bound = commands[bound] }\n    if (!bound) { return false }\n    var done = false\n    try {\n      if (cm.isReadOnly()) { cm.state.suppressEdits = true }\n      done = bound(cm, pos) != Pass\n    } finally {\n      cm.state.suppressEdits = false\n    }\n    return done\n  })\n}\n\nfunction configureMouse(cm, repeat, event) {\n  var option = cm.getOption(\"configureMouse\")\n  var value = option ? option(cm, repeat, event) : {}\n  if (value.unit == null) {\n    var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey\n    value.unit = rect ? \"rectangle\" : repeat == \"single\" ? \"char\" : repeat == \"double\" ? \"word\" : \"line\"\n  }\n  if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey }\n  if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey }\n  if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) }\n  return value\n}\n\nfunction leftButtonDown(cm, pos, repeat, event) {\n  if (ie) { setTimeout(bind(ensureFocus, cm), 0) }\n  else { cm.curOp.focus = activeElt() }\n\n  var behavior = configureMouse(cm, repeat, event)\n\n  var sel = cm.doc.sel, contained\n  if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&\n      repeat == \"single\" && (contained = sel.contains(pos)) > -1 &&\n      (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&\n      (cmp(contained.to(), pos) > 0 || pos.xRel < 0))\n    { leftButtonStartDrag(cm, event, pos, behavior) }\n  else\n    { leftButtonSelect(cm, event, pos, behavior) }\n}\n\n// Start a text drag. When it ends, see if any dragging actually\n// happen, and treat as a click if it didn't.\nfunction leftButtonStartDrag(cm, event, pos, behavior) {\n  var display = cm.display, moved = false\n  var dragEnd = operation(cm, function (e) {\n    if (webkit) { display.scroller.draggable = false }\n    cm.state.draggingText = false\n    off(display.wrapper.ownerDocument, \"mouseup\", dragEnd)\n    off(display.wrapper.ownerDocument, \"mousemove\", mouseMove)\n    off(display.scroller, \"dragstart\", dragStart)\n    off(display.scroller, \"drop\", dragEnd)\n    if (!moved) {\n      e_preventDefault(e)\n      if (!behavior.addNew)\n        { extendSelection(cm.doc, pos, null, null, behavior.extend) }\n      // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)\n      if (webkit || ie && ie_version == 9)\n        { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus()}, 20) }\n      else\n        { display.input.focus() }\n    }\n  })\n  var mouseMove = function(e2) {\n    moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10\n  }\n  var dragStart = function () { return moved = true; }\n  // Let the drag handler handle this.\n  if (webkit) { display.scroller.draggable = true }\n  cm.state.draggingText = dragEnd\n  dragEnd.copy = !behavior.moveOnDrag\n  // IE's approach to draggable\n  if (display.scroller.dragDrop) { display.scroller.dragDrop() }\n  on(display.wrapper.ownerDocument, \"mouseup\", dragEnd)\n  on(display.wrapper.ownerDocument, \"mousemove\", mouseMove)\n  on(display.scroller, \"dragstart\", dragStart)\n  on(display.scroller, \"drop\", dragEnd)\n\n  delayBlurEvent(cm)\n  setTimeout(function () { return display.input.focus(); }, 20)\n}\n\nfunction rangeForUnit(cm, pos, unit) {\n  if (unit == \"char\") { return new Range(pos, pos) }\n  if (unit == \"word\") { return cm.findWordAt(pos) }\n  if (unit == \"line\") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }\n  var result = unit(cm, pos)\n  return new Range(result.from, result.to)\n}\n\n// Normal selection, as opposed to text dragging.\nfunction leftButtonSelect(cm, event, start, behavior) {\n  var display = cm.display, doc = cm.doc\n  e_preventDefault(event)\n\n  var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges\n  if (behavior.addNew && !behavior.extend) {\n    ourIndex = doc.sel.contains(start)\n    if (ourIndex > -1)\n      { ourRange = ranges[ourIndex] }\n    else\n      { ourRange = new Range(start, start) }\n  } else {\n    ourRange = doc.sel.primary()\n    ourIndex = doc.sel.primIndex\n  }\n\n  if (behavior.unit == \"rectangle\") {\n    if (!behavior.addNew) { ourRange = new Range(start, start) }\n    start = posFromMouse(cm, event, true, true)\n    ourIndex = -1\n  } else {\n    var range = rangeForUnit(cm, start, behavior.unit)\n    if (behavior.extend)\n      { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) }\n    else\n      { ourRange = range }\n  }\n\n  if (!behavior.addNew) {\n    ourIndex = 0\n    setSelection(doc, new Selection([ourRange], 0), sel_mouse)\n    startSel = doc.sel\n  } else if (ourIndex == -1) {\n    ourIndex = ranges.length\n    setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),\n                 {scroll: false, origin: \"*mouse\"})\n  } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == \"char\" && !behavior.extend) {\n    setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),\n                 {scroll: false, origin: \"*mouse\"})\n    startSel = doc.sel\n  } else {\n    replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)\n  }\n\n  var lastPos = start\n  function extendTo(pos) {\n    if (cmp(lastPos, pos) == 0) { return }\n    lastPos = pos\n\n    if (behavior.unit == \"rectangle\") {\n      var ranges = [], tabSize = cm.options.tabSize\n      var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)\n      var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)\n      var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)\n      for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));\n           line <= end; line++) {\n        var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)\n        if (left == right)\n          { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }\n        else if (text.length > leftPos)\n          { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }\n      }\n      if (!ranges.length) { ranges.push(new Range(start, start)) }\n      setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),\n                   {origin: \"*mouse\", scroll: false})\n      cm.scrollIntoView(pos)\n    } else {\n      var oldRange = ourRange\n      var range = rangeForUnit(cm, pos, behavior.unit)\n      var anchor = oldRange.anchor, head\n      if (cmp(range.anchor, anchor) > 0) {\n        head = range.head\n        anchor = minPos(oldRange.from(), range.anchor)\n      } else {\n        head = range.anchor\n        anchor = maxPos(oldRange.to(), range.head)\n      }\n      var ranges$1 = startSel.ranges.slice(0)\n      ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))\n      setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)\n    }\n  }\n\n  var editorSize = display.wrapper.getBoundingClientRect()\n  // Used to ensure timeout re-tries don't fire when another extend\n  // happened in the meantime (clearTimeout isn't reliable -- at\n  // least on Chrome, the timeouts still happen even when cleared,\n  // if the clear happens after their scheduled firing time).\n  var counter = 0\n\n  function extend(e) {\n    var curCount = ++counter\n    var cur = posFromMouse(cm, e, true, behavior.unit == \"rectangle\")\n    if (!cur) { return }\n    if (cmp(cur, lastPos) != 0) {\n      cm.curOp.focus = activeElt()\n      extendTo(cur)\n      var visible = visibleLines(display, doc)\n      if (cur.line >= visible.to || cur.line < visible.from)\n        { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }\n    } else {\n      var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0\n      if (outside) { setTimeout(operation(cm, function () {\n        if (counter != curCount) { return }\n        display.scroller.scrollTop += outside\n        extend(e)\n      }), 50) }\n    }\n  }\n\n  function done(e) {\n    cm.state.selectingText = false\n    counter = Infinity\n    e_preventDefault(e)\n    display.input.focus()\n    off(display.wrapper.ownerDocument, \"mousemove\", move)\n    off(display.wrapper.ownerDocument, \"mouseup\", up)\n    doc.history.lastSelOrigin = null\n  }\n\n  var move = operation(cm, function (e) {\n    if (e.buttons === 0 || !e_button(e)) { done(e) }\n    else { extend(e) }\n  })\n  var up = operation(cm, done)\n  cm.state.selectingText = up\n  on(display.wrapper.ownerDocument, \"mousemove\", move)\n  on(display.wrapper.ownerDocument, \"mouseup\", up)\n}\n\n// Used when mouse-selecting to adjust the anchor to the proper side\n// of a bidi jump depending on the visual position of the head.\nfunction bidiSimplify(cm, range) {\n  var anchor = range.anchor;\n  var head = range.head;\n  var anchorLine = getLine(cm.doc, anchor.line)\n  if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }\n  var order = getOrder(anchorLine)\n  if (!order) { return range }\n  var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]\n  if (part.from != anchor.ch && part.to != anchor.ch) { return range }\n  var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)\n  if (boundary == 0 || boundary == order.length) { return range }\n\n  // Compute the relative visual position of the head compared to the\n  // anchor (<0 is to the left, >0 to the right)\n  var leftSide\n  if (head.line != anchor.line) {\n    leftSide = (head.line - anchor.line) * (cm.doc.direction == \"ltr\" ? 1 : -1) > 0\n  } else {\n    var headIndex = getBidiPartAt(order, head.ch, head.sticky)\n    var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)\n    if (headIndex == boundary - 1 || headIndex == boundary)\n      { leftSide = dir < 0 }\n    else\n      { leftSide = dir > 0 }\n  }\n\n  var usePart = order[boundary + (leftSide ? -1 : 0)]\n  var from = leftSide == (usePart.level == 1)\n  var ch = from ? usePart.from : usePart.to, sticky = from ? \"after\" : \"before\"\n  return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)\n}\n\n\n// Determines whether an event happened in the gutter, and fires the\n// handlers for the corresponding event.\nfunction gutterEvent(cm, e, type, prevent) {\n  var mX, mY\n  if (e.touches) {\n    mX = e.touches[0].clientX\n    mY = e.touches[0].clientY\n  } else {\n    try { mX = e.clientX; mY = e.clientY }\n    catch(e) { return false }\n  }\n  if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }\n  if (prevent) { e_preventDefault(e) }\n\n  var display = cm.display\n  var lineBox = display.lineDiv.getBoundingClientRect()\n\n  if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }\n  mY -= lineBox.top - display.viewOffset\n\n  for (var i = 0; i < cm.options.gutters.length; ++i) {\n    var g = display.gutters.childNodes[i]\n    if (g && g.getBoundingClientRect().right >= mX) {\n      var line = lineAtHeight(cm.doc, mY)\n      var gutter = cm.options.gutters[i]\n      signal(cm, type, cm, line, gutter, e)\n      return e_defaultPrevented(e)\n    }\n  }\n}\n\nfunction clickInGutter(cm, e) {\n  return gutterEvent(cm, e, \"gutterClick\", true)\n}\n\n// CONTEXT MENU HANDLING\n\n// To make the context menu work, we need to briefly unhide the\n// textarea (making it as unobtrusive as possible) to let the\n// right-click take effect on it.\nfunction onContextMenu(cm, e) {\n  if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }\n  if (signalDOMEvent(cm, e, \"contextmenu\")) { return }\n  cm.display.input.onContextMenu(e)\n}\n\nfunction contextMenuInGutter(cm, e) {\n  if (!hasHandler(cm, \"gutterContextMenu\")) { return false }\n  return gutterEvent(cm, e, \"gutterContextMenu\", false)\n}\n\nfunction themeChanged(cm) {\n  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\\s*cm-s-\\S+/g, \"\") +\n    cm.options.theme.replace(/(^|\\s)\\s*/g, \" cm-s-\")\n  clearCaches(cm)\n}\n\nvar Init = {toString: function(){return \"CodeMirror.Init\"}}\n\nvar defaults = {}\nvar optionHandlers = {}\n\nfunction defineOptions(CodeMirror) {\n  var optionHandlers = CodeMirror.optionHandlers\n\n  function option(name, deflt, handle, notOnInit) {\n    CodeMirror.defaults[name] = deflt\n    if (handle) { optionHandlers[name] =\n      notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }\n  }\n\n  CodeMirror.defineOption = option\n\n  // Passed to option handlers when there is no old value.\n  CodeMirror.Init = Init\n\n  // These two are, on init, called from the constructor because they\n  // have to be initialized before the editor can start at all.\n  option(\"value\", \"\", function (cm, val) { return cm.setValue(val); }, true)\n  option(\"mode\", null, function (cm, val) {\n    cm.doc.modeOption = val\n    loadMode(cm)\n  }, true)\n\n  option(\"indentUnit\", 2, loadMode, true)\n  option(\"indentWithTabs\", false)\n  option(\"smartIndent\", true)\n  option(\"tabSize\", 4, function (cm) {\n    resetModeState(cm)\n    clearCaches(cm)\n    regChange(cm)\n  }, true)\n\n  option(\"lineSeparator\", null, function (cm, val) {\n    cm.doc.lineSep = val\n    if (!val) { return }\n    var newBreaks = [], lineNo = cm.doc.first\n    cm.doc.iter(function (line) {\n      for (var pos = 0;;) {\n        var found = line.text.indexOf(val, pos)\n        if (found == -1) { break }\n        pos = found + val.length\n        newBreaks.push(Pos(lineNo, found))\n      }\n      lineNo++\n    })\n    for (var i = newBreaks.length - 1; i >= 0; i--)\n      { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }\n  })\n  option(\"specialChars\", /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028\\u2029\\ufeff]/g, function (cm, val, old) {\n    cm.state.specialChars = new RegExp(val.source + (val.test(\"\\t\") ? \"\" : \"|\\t\"), \"g\")\n    if (old != Init) { cm.refresh() }\n  })\n  option(\"specialCharPlaceholder\", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)\n  option(\"electricChars\", true)\n  option(\"inputStyle\", mobile ? \"contenteditable\" : \"textarea\", function () {\n    throw new Error(\"inputStyle can not (yet) be changed in a running editor\") // FIXME\n  }, true)\n  option(\"spellcheck\", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)\n  option(\"rtlMoveVisually\", !windows)\n  option(\"wholeLineUpdateBefore\", true)\n\n  option(\"theme\", \"default\", function (cm) {\n    themeChanged(cm)\n    guttersChanged(cm)\n  }, true)\n  option(\"keyMap\", \"default\", function (cm, val, old) {\n    var next = getKeyMap(val)\n    var prev = old != Init && getKeyMap(old)\n    if (prev && prev.detach) { prev.detach(cm, next) }\n    if (next.attach) { next.attach(cm, prev || null) }\n  })\n  option(\"extraKeys\", null)\n  option(\"configureMouse\", null)\n\n  option(\"lineWrapping\", false, wrappingChanged, true)\n  option(\"gutters\", [], function (cm) {\n    setGuttersForLineNumbers(cm.options)\n    guttersChanged(cm)\n  }, true)\n  option(\"fixedGutter\", true, function (cm, val) {\n    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + \"px\" : \"0\"\n    cm.refresh()\n  }, true)\n  option(\"coverGutterNextToScrollbar\", false, function (cm) { return updateScrollbars(cm); }, true)\n  option(\"scrollbarStyle\", \"native\", function (cm) {\n    initScrollbars(cm)\n    updateScrollbars(cm)\n    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)\n    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)\n  }, true)\n  option(\"lineNumbers\", false, function (cm) {\n    setGuttersForLineNumbers(cm.options)\n    guttersChanged(cm)\n  }, true)\n  option(\"firstLineNumber\", 1, guttersChanged, true)\n  option(\"lineNumberFormatter\", function (integer) { return integer; }, guttersChanged, true)\n  option(\"showCursorWhenSelecting\", false, updateSelection, true)\n\n  option(\"resetSelectionOnContextMenu\", true)\n  option(\"lineWiseCopyCut\", true)\n  option(\"pasteLinesPerSelection\", true)\n\n  option(\"readOnly\", false, function (cm, val) {\n    if (val == \"nocursor\") {\n      onBlur(cm)\n      cm.display.input.blur()\n    }\n    cm.display.input.readOnlyChanged(val)\n  })\n  option(\"disableInput\", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)\n  option(\"dragDrop\", true, dragDropChanged)\n  option(\"allowDropFileTypes\", null)\n\n  option(\"cursorBlinkRate\", 530)\n  option(\"cursorScrollMargin\", 0)\n  option(\"cursorHeight\", 1, updateSelection, true)\n  option(\"singleCursorHeightPerLine\", true, updateSelection, true)\n  option(\"workTime\", 100)\n  option(\"workDelay\", 100)\n  option(\"flattenSpans\", true, resetModeState, true)\n  option(\"addModeClass\", false, resetModeState, true)\n  option(\"pollInterval\", 100)\n  option(\"undoDepth\", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })\n  option(\"historyEventDelay\", 1250)\n  option(\"viewportMargin\", 10, function (cm) { return cm.refresh(); }, true)\n  option(\"maxHighlightLength\", 10000, resetModeState, true)\n  option(\"moveInputWithCursor\", true, function (cm, val) {\n    if (!val) { cm.display.input.resetPosition() }\n  })\n\n  option(\"tabindex\", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || \"\"; })\n  option(\"autofocus\", null)\n  option(\"direction\", \"ltr\", function (cm, val) { return cm.doc.setDirection(val); }, true)\n}\n\nfunction guttersChanged(cm) {\n  updateGutters(cm)\n  regChange(cm)\n  alignHorizontally(cm)\n}\n\nfunction dragDropChanged(cm, value, old) {\n  var wasOn = old && old != Init\n  if (!value != !wasOn) {\n    var funcs = cm.display.dragFunctions\n    var toggle = value ? on : off\n    toggle(cm.display.scroller, \"dragstart\", funcs.start)\n    toggle(cm.display.scroller, \"dragenter\", funcs.enter)\n    toggle(cm.display.scroller, \"dragover\", funcs.over)\n    toggle(cm.display.scroller, \"dragleave\", funcs.leave)\n    toggle(cm.display.scroller, \"drop\", funcs.drop)\n  }\n}\n\nfunction wrappingChanged(cm) {\n  if (cm.options.lineWrapping) {\n    addClass(cm.display.wrapper, \"CodeMirror-wrap\")\n    cm.display.sizer.style.minWidth = \"\"\n    cm.display.sizerWidth = null\n  } else {\n    rmClass(cm.display.wrapper, \"CodeMirror-wrap\")\n    findMaxLine(cm)\n  }\n  estimateLineHeights(cm)\n  regChange(cm)\n  clearCaches(cm)\n  setTimeout(function () { return updateScrollbars(cm); }, 100)\n}\n\n// A CodeMirror instance represents an editor. This is the object\n// that user code is usually dealing with.\n\nfunction CodeMirror(place, options) {\n  var this$1 = this;\n\n  if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }\n\n  this.options = options = options ? copyObj(options) : {}\n  // Determine effective options based on given values and defaults.\n  copyObj(defaults, options, false)\n  setGuttersForLineNumbers(options)\n\n  var doc = options.value\n  if (typeof doc == \"string\") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) }\n  this.doc = doc\n\n  var input = new CodeMirror.inputStyles[options.inputStyle](this)\n  var display = this.display = new Display(place, doc, input)\n  display.wrapper.CodeMirror = this\n  updateGutters(this)\n  themeChanged(this)\n  if (options.lineWrapping)\n    { this.display.wrapper.className += \" CodeMirror-wrap\" }\n  initScrollbars(this)\n\n  this.state = {\n    keyMaps: [],  // stores maps added by addKeyMap\n    overlays: [], // highlighting overlays, as added by addOverlay\n    modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info\n    overwrite: false,\n    delayingBlurEvent: false,\n    focused: false,\n    suppressEdits: false, // used to disable editing during key handlers when in readOnly mode\n    pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll\n    selectingText: false,\n    draggingText: false,\n    highlight: new Delayed(), // stores highlight worker timeout\n    keySeq: null,  // Unfinished key sequence\n    specialChars: null\n  }\n\n  if (options.autofocus && !mobile) { display.input.focus() }\n\n  // Override magic textarea content restore that IE sometimes does\n  // on our hidden textarea on reload\n  if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }\n\n  registerEventHandlers(this)\n  ensureGlobalHandlers()\n\n  startOperation(this)\n  this.curOp.forceUpdate = true\n  attachDoc(this, doc)\n\n  if ((options.autofocus && !mobile) || this.hasFocus())\n    { setTimeout(bind(onFocus, this), 20) }\n  else\n    { onBlur(this) }\n\n  for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))\n    { optionHandlers[opt](this$1, options[opt], Init) } }\n  maybeUpdateLineNumberWidth(this)\n  if (options.finishInit) { options.finishInit(this) }\n  for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }\n  endOperation(this)\n  // Suppress optimizelegibility in Webkit, since it breaks text\n  // measuring on line wrapping boundaries.\n  if (webkit && options.lineWrapping &&\n      getComputedStyle(display.lineDiv).textRendering == \"optimizelegibility\")\n    { display.lineDiv.style.textRendering = \"auto\" }\n}\n\n// The default configuration options.\nCodeMirror.defaults = defaults\n// Functions to run when options are changed.\nCodeMirror.optionHandlers = optionHandlers\n\n// Attach the necessary event handlers when initializing the editor\nfunction registerEventHandlers(cm) {\n  var d = cm.display\n  on(d.scroller, \"mousedown\", operation(cm, onMouseDown))\n  // Older IE's will not fire a second mousedown for a double click\n  if (ie && ie_version < 11)\n    { on(d.scroller, \"dblclick\", operation(cm, function (e) {\n      if (signalDOMEvent(cm, e)) { return }\n      var pos = posFromMouse(cm, e)\n      if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }\n      e_preventDefault(e)\n      var word = cm.findWordAt(pos)\n      extendSelection(cm.doc, word.anchor, word.head)\n    })) }\n  else\n    { on(d.scroller, \"dblclick\", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }\n  // Some browsers fire contextmenu *after* opening the menu, at\n  // which point we can't mess with it anymore. Context menu is\n  // handled in onMouseDown for these browsers.\n  if (!captureRightClick) { on(d.scroller, \"contextmenu\", function (e) { return onContextMenu(cm, e); }) }\n\n  // Used to suppress mouse event handling when a touch happens\n  var touchFinished, prevTouch = {end: 0}\n  function finishTouch() {\n    if (d.activeTouch) {\n      touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)\n      prevTouch = d.activeTouch\n      prevTouch.end = +new Date\n    }\n  }\n  function isMouseLikeTouchEvent(e) {\n    if (e.touches.length != 1) { return false }\n    var touch = e.touches[0]\n    return touch.radiusX <= 1 && touch.radiusY <= 1\n  }\n  function farAway(touch, other) {\n    if (other.left == null) { return true }\n    var dx = other.left - touch.left, dy = other.top - touch.top\n    return dx * dx + dy * dy > 20 * 20\n  }\n  on(d.scroller, \"touchstart\", function (e) {\n    if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {\n      d.input.ensurePolled()\n      clearTimeout(touchFinished)\n      var now = +new Date\n      d.activeTouch = {start: now, moved: false,\n                       prev: now - prevTouch.end <= 300 ? prevTouch : null}\n      if (e.touches.length == 1) {\n        d.activeTouch.left = e.touches[0].pageX\n        d.activeTouch.top = e.touches[0].pageY\n      }\n    }\n  })\n  on(d.scroller, \"touchmove\", function () {\n    if (d.activeTouch) { d.activeTouch.moved = true }\n  })\n  on(d.scroller, \"touchend\", function (e) {\n    var touch = d.activeTouch\n    if (touch && !eventInWidget(d, e) && touch.left != null &&\n        !touch.moved && new Date - touch.start < 300) {\n      var pos = cm.coordsChar(d.activeTouch, \"page\"), range\n      if (!touch.prev || farAway(touch, touch.prev)) // Single tap\n        { range = new Range(pos, pos) }\n      else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap\n        { range = cm.findWordAt(pos) }\n      else // Triple tap\n        { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }\n      cm.setSelection(range.anchor, range.head)\n      cm.focus()\n      e_preventDefault(e)\n    }\n    finishTouch()\n  })\n  on(d.scroller, \"touchcancel\", finishTouch)\n\n  // Sync scrolling between fake scrollbars and real scrollable\n  // area, ensure viewport is updated when scrolling.\n  on(d.scroller, \"scroll\", function () {\n    if (d.scroller.clientHeight) {\n      updateScrollTop(cm, d.scroller.scrollTop)\n      setScrollLeft(cm, d.scroller.scrollLeft, true)\n      signal(cm, \"scroll\", cm)\n    }\n  })\n\n  // Listen to wheel events in order to try and update the viewport on time.\n  on(d.scroller, \"mousewheel\", function (e) { return onScrollWheel(cm, e); })\n  on(d.scroller, \"DOMMouseScroll\", function (e) { return onScrollWheel(cm, e); })\n\n  // Prevent wrapper from ever scrolling\n  on(d.wrapper, \"scroll\", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })\n\n  d.dragFunctions = {\n    enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},\n    over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},\n    start: function (e) { return onDragStart(cm, e); },\n    drop: operation(cm, onDrop),\n    leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}\n  }\n\n  var inp = d.input.getField()\n  on(inp, \"keyup\", function (e) { return onKeyUp.call(cm, e); })\n  on(inp, \"keydown\", operation(cm, onKeyDown))\n  on(inp, \"keypress\", operation(cm, onKeyPress))\n  on(inp, \"focus\", function (e) { return onFocus(cm, e); })\n  on(inp, \"blur\", function (e) { return onBlur(cm, e); })\n}\n\nvar initHooks = []\nCodeMirror.defineInitHook = function (f) { return initHooks.push(f); }\n\n// Indent the given line. The how parameter can be \"smart\",\n// \"add\"/null, \"subtract\", or \"prev\". When aggressive is false\n// (typically set to true for forced single-line indents), empty\n// lines are not indented, and places where the mode returns Pass\n// are left alone.\nfunction indentLine(cm, n, how, aggressive) {\n  var doc = cm.doc, state\n  if (how == null) { how = \"add\" }\n  if (how == \"smart\") {\n    // Fall back to \"prev\" when the mode doesn't have an indentation\n    // method.\n    if (!doc.mode.indent) { how = \"prev\" }\n    else { state = getContextBefore(cm, n).state }\n  }\n\n  var tabSize = cm.options.tabSize\n  var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)\n  if (line.stateAfter) { line.stateAfter = null }\n  var curSpaceString = line.text.match(/^\\s*/)[0], indentation\n  if (!aggressive && !/\\S/.test(line.text)) {\n    indentation = 0\n    how = \"not\"\n  } else if (how == \"smart\") {\n    indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)\n    if (indentation == Pass || indentation > 150) {\n      if (!aggressive) { return }\n      how = \"prev\"\n    }\n  }\n  if (how == \"prev\") {\n    if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }\n    else { indentation = 0 }\n  } else if (how == \"add\") {\n    indentation = curSpace + cm.options.indentUnit\n  } else if (how == \"subtract\") {\n    indentation = curSpace - cm.options.indentUnit\n  } else if (typeof how == \"number\") {\n    indentation = curSpace + how\n  }\n  indentation = Math.max(0, indentation)\n\n  var indentString = \"\", pos = 0\n  if (cm.options.indentWithTabs)\n    { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += \"\\t\"} }\n  if (pos < indentation) { indentString += spaceStr(indentation - pos) }\n\n  if (indentString != curSpaceString) {\n    replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), \"+input\")\n    line.stateAfter = null\n    return true\n  } else {\n    // Ensure that, if the cursor was in the whitespace at the start\n    // of the line, it is moved to the end of that space.\n    for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {\n      var range = doc.sel.ranges[i$1]\n      if (range.head.line == n && range.head.ch < curSpaceString.length) {\n        var pos$1 = Pos(n, curSpaceString.length)\n        replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))\n        break\n      }\n    }\n  }\n}\n\n// This will be set to a {lineWise: bool, text: [string]} object, so\n// that, when pasting, we know what kind of selections the copied\n// text was made out of.\nvar lastCopied = null\n\nfunction setLastCopied(newLastCopied) {\n  lastCopied = newLastCopied\n}\n\nfunction applyTextInput(cm, inserted, deleted, sel, origin) {\n  var doc = cm.doc\n  cm.display.shift = false\n  if (!sel) { sel = doc.sel }\n\n  var paste = cm.state.pasteIncoming || origin == \"paste\"\n  var textLines = splitLinesAuto(inserted), multiPaste = null\n  // When pasting N lines into N selections, insert one line per selection\n  if (paste && sel.ranges.length > 1) {\n    if (lastCopied && lastCopied.text.join(\"\\n\") == inserted) {\n      if (sel.ranges.length % lastCopied.text.length == 0) {\n        multiPaste = []\n        for (var i = 0; i < lastCopied.text.length; i++)\n          { multiPaste.push(doc.splitLines(lastCopied.text[i])) }\n      }\n    } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {\n      multiPaste = map(textLines, function (l) { return [l]; })\n    }\n  }\n\n  var updateInput\n  // Normal behavior is to insert the new text into every selection\n  for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {\n    var range = sel.ranges[i$1]\n    var from = range.from(), to = range.to()\n    if (range.empty()) {\n      if (deleted && deleted > 0) // Handle deletion\n        { from = Pos(from.line, from.ch - deleted) }\n      else if (cm.state.overwrite && !paste) // Handle overwrite\n        { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }\n      else if (lastCopied && lastCopied.lineWise && lastCopied.text.join(\"\\n\") == inserted)\n        { from = to = Pos(from.line, 0) }\n    }\n    updateInput = cm.curOp.updateInput\n    var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,\n                       origin: origin || (paste ? \"paste\" : cm.state.cutIncoming ? \"cut\" : \"+input\")}\n    makeChange(cm.doc, changeEvent)\n    signalLater(cm, \"inputRead\", cm, changeEvent)\n  }\n  if (inserted && !paste)\n    { triggerElectric(cm, inserted) }\n\n  ensureCursorVisible(cm)\n  cm.curOp.updateInput = updateInput\n  cm.curOp.typing = true\n  cm.state.pasteIncoming = cm.state.cutIncoming = false\n}\n\nfunction handlePaste(e, cm) {\n  var pasted = e.clipboardData && e.clipboardData.getData(\"Text\")\n  if (pasted) {\n    e.preventDefault()\n    if (!cm.isReadOnly() && !cm.options.disableInput)\n      { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, \"paste\"); }) }\n    return true\n  }\n}\n\nfunction triggerElectric(cm, inserted) {\n  // When an 'electric' character is inserted, immediately trigger a reindent\n  if (!cm.options.electricChars || !cm.options.smartIndent) { return }\n  var sel = cm.doc.sel\n\n  for (var i = sel.ranges.length - 1; i >= 0; i--) {\n    var range = sel.ranges[i]\n    if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }\n    var mode = cm.getModeAt(range.head)\n    var indented = false\n    if (mode.electricChars) {\n      for (var j = 0; j < mode.electricChars.length; j++)\n        { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {\n          indented = indentLine(cm, range.head.line, \"smart\")\n          break\n        } }\n    } else if (mode.electricInput) {\n      if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))\n        { indented = indentLine(cm, range.head.line, \"smart\") }\n    }\n    if (indented) { signalLater(cm, \"electricInput\", cm, range.head.line) }\n  }\n}\n\nfunction copyableRanges(cm) {\n  var text = [], ranges = []\n  for (var i = 0; i < cm.doc.sel.ranges.length; i++) {\n    var line = cm.doc.sel.ranges[i].head.line\n    var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}\n    ranges.push(lineRange)\n    text.push(cm.getRange(lineRange.anchor, lineRange.head))\n  }\n  return {text: text, ranges: ranges}\n}\n\nfunction disableBrowserMagic(field, spellcheck) {\n  field.setAttribute(\"autocorrect\", \"off\")\n  field.setAttribute(\"autocapitalize\", \"off\")\n  field.setAttribute(\"spellcheck\", !!spellcheck)\n}\n\nfunction hiddenTextarea() {\n  var te = elt(\"textarea\", null, null, \"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none\")\n  var div = elt(\"div\", [te], null, \"overflow: hidden; position: relative; width: 3px; height: 0px;\")\n  // The textarea is kept positioned near the cursor to prevent the\n  // fact that it'll be scrolled into view on input from scrolling\n  // our fake cursor out of view. On webkit, when wrap=off, paste is\n  // very slow. So make the area wide instead.\n  if (webkit) { te.style.width = \"1000px\" }\n  else { te.setAttribute(\"wrap\", \"off\") }\n  // If border: 0; -- iOS fails to open keyboard (issue #1287)\n  if (ios) { te.style.border = \"1px solid black\" }\n  disableBrowserMagic(te)\n  return div\n}\n\n// The publicly visible API. Note that methodOp(f) means\n// 'wrap f in an operation, performed on its `this` parameter'.\n\n// This is not the complete set of editor methods. Most of the\n// methods defined on the Doc type are also injected into\n// CodeMirror.prototype, for backwards compatibility and\n// convenience.\n\nfunction addEditorMethods(CodeMirror) {\n  var optionHandlers = CodeMirror.optionHandlers\n\n  var helpers = CodeMirror.helpers = {}\n\n  CodeMirror.prototype = {\n    constructor: CodeMirror,\n    focus: function(){window.focus(); this.display.input.focus()},\n\n    setOption: function(option, value) {\n      var options = this.options, old = options[option]\n      if (options[option] == value && option != \"mode\") { return }\n      options[option] = value\n      if (optionHandlers.hasOwnProperty(option))\n        { operation(this, optionHandlers[option])(this, value, old) }\n      signal(this, \"optionChange\", this, option)\n    },\n\n    getOption: function(option) {return this.options[option]},\n    getDoc: function() {return this.doc},\n\n    addKeyMap: function(map, bottom) {\n      this.state.keyMaps[bottom ? \"push\" : \"unshift\"](getKeyMap(map))\n    },\n    removeKeyMap: function(map) {\n      var maps = this.state.keyMaps\n      for (var i = 0; i < maps.length; ++i)\n        { if (maps[i] == map || maps[i].name == map) {\n          maps.splice(i, 1)\n          return true\n        } }\n    },\n\n    addOverlay: methodOp(function(spec, options) {\n      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)\n      if (mode.startState) { throw new Error(\"Overlays may not be stateful.\") }\n      insertSorted(this.state.overlays,\n                   {mode: mode, modeSpec: spec, opaque: options && options.opaque,\n                    priority: (options && options.priority) || 0},\n                   function (overlay) { return overlay.priority; })\n      this.state.modeGen++\n      regChange(this)\n    }),\n    removeOverlay: methodOp(function(spec) {\n      var this$1 = this;\n\n      var overlays = this.state.overlays\n      for (var i = 0; i < overlays.length; ++i) {\n        var cur = overlays[i].modeSpec\n        if (cur == spec || typeof spec == \"string\" && cur.name == spec) {\n          overlays.splice(i, 1)\n          this$1.state.modeGen++\n          regChange(this$1)\n          return\n        }\n      }\n    }),\n\n    indentLine: methodOp(function(n, dir, aggressive) {\n      if (typeof dir != \"string\" && typeof dir != \"number\") {\n        if (dir == null) { dir = this.options.smartIndent ? \"smart\" : \"prev\" }\n        else { dir = dir ? \"add\" : \"subtract\" }\n      }\n      if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }\n    }),\n    indentSelection: methodOp(function(how) {\n      var this$1 = this;\n\n      var ranges = this.doc.sel.ranges, end = -1\n      for (var i = 0; i < ranges.length; i++) {\n        var range = ranges[i]\n        if (!range.empty()) {\n          var from = range.from(), to = range.to()\n          var start = Math.max(end, from.line)\n          end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1\n          for (var j = start; j < end; ++j)\n            { indentLine(this$1, j, how) }\n          var newRanges = this$1.doc.sel.ranges\n          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)\n            { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }\n        } else if (range.head.line > end) {\n          indentLine(this$1, range.head.line, how, true)\n          end = range.head.line\n          if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }\n        }\n      }\n    }),\n\n    // Fetch the parser token for a given character. Useful for hacks\n    // that want to inspect the mode state (say, for completion).\n    getTokenAt: function(pos, precise) {\n      return takeToken(this, pos, precise)\n    },\n\n    getLineTokens: function(line, precise) {\n      return takeToken(this, Pos(line), precise, true)\n    },\n\n    getTokenTypeAt: function(pos) {\n      pos = clipPos(this.doc, pos)\n      var styles = getLineStyles(this, getLine(this.doc, pos.line))\n      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch\n      var type\n      if (ch == 0) { type = styles[2] }\n      else { for (;;) {\n        var mid = (before + after) >> 1\n        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }\n        else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }\n        else { type = styles[mid * 2 + 2]; break }\n      } }\n      var cut = type ? type.indexOf(\"overlay \") : -1\n      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)\n    },\n\n    getModeAt: function(pos) {\n      var mode = this.doc.mode\n      if (!mode.innerMode) { return mode }\n      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode\n    },\n\n    getHelper: function(pos, type) {\n      return this.getHelpers(pos, type)[0]\n    },\n\n    getHelpers: function(pos, type) {\n      var this$1 = this;\n\n      var found = []\n      if (!helpers.hasOwnProperty(type)) { return found }\n      var help = helpers[type], mode = this.getModeAt(pos)\n      if (typeof mode[type] == \"string\") {\n        if (help[mode[type]]) { found.push(help[mode[type]]) }\n      } else if (mode[type]) {\n        for (var i = 0; i < mode[type].length; i++) {\n          var val = help[mode[type][i]]\n          if (val) { found.push(val) }\n        }\n      } else if (mode.helperType && help[mode.helperType]) {\n        found.push(help[mode.helperType])\n      } else if (help[mode.name]) {\n        found.push(help[mode.name])\n      }\n      for (var i$1 = 0; i$1 < help._global.length; i$1++) {\n        var cur = help._global[i$1]\n        if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)\n          { found.push(cur.val) }\n      }\n      return found\n    },\n\n    getStateAfter: function(line, precise) {\n      var doc = this.doc\n      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)\n      return getContextBefore(this, line + 1, precise).state\n    },\n\n    cursorCoords: function(start, mode) {\n      var pos, range = this.doc.sel.primary()\n      if (start == null) { pos = range.head }\n      else if (typeof start == \"object\") { pos = clipPos(this.doc, start) }\n      else { pos = start ? range.from() : range.to() }\n      return cursorCoords(this, pos, mode || \"page\")\n    },\n\n    charCoords: function(pos, mode) {\n      return charCoords(this, clipPos(this.doc, pos), mode || \"page\")\n    },\n\n    coordsChar: function(coords, mode) {\n      coords = fromCoordSystem(this, coords, mode || \"page\")\n      return coordsChar(this, coords.left, coords.top)\n    },\n\n    lineAtHeight: function(height, mode) {\n      height = fromCoordSystem(this, {top: height, left: 0}, mode || \"page\").top\n      return lineAtHeight(this.doc, height + this.display.viewOffset)\n    },\n    heightAtLine: function(line, mode, includeWidgets) {\n      var end = false, lineObj\n      if (typeof line == \"number\") {\n        var last = this.doc.first + this.doc.size - 1\n        if (line < this.doc.first) { line = this.doc.first }\n        else if (line > last) { line = last; end = true }\n        lineObj = getLine(this.doc, line)\n      } else {\n        lineObj = line\n      }\n      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || \"page\", includeWidgets || end).top +\n        (end ? this.doc.height - heightAtLine(lineObj) : 0)\n    },\n\n    defaultTextHeight: function() { return textHeight(this.display) },\n    defaultCharWidth: function() { return charWidth(this.display) },\n\n    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},\n\n    addWidget: function(pos, node, scroll, vert, horiz) {\n      var display = this.display\n      pos = cursorCoords(this, clipPos(this.doc, pos))\n      var top = pos.bottom, left = pos.left\n      node.style.position = \"absolute\"\n      node.setAttribute(\"cm-ignore-events\", \"true\")\n      this.display.input.setUneditable(node)\n      display.sizer.appendChild(node)\n      if (vert == \"over\") {\n        top = pos.top\n      } else if (vert == \"above\" || vert == \"near\") {\n        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),\n        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)\n        // Default to positioning above (if specified and possible); otherwise default to positioning below\n        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)\n          { top = pos.top - node.offsetHeight }\n        else if (pos.bottom + node.offsetHeight <= vspace)\n          { top = pos.bottom }\n        if (left + node.offsetWidth > hspace)\n          { left = hspace - node.offsetWidth }\n      }\n      node.style.top = top + \"px\"\n      node.style.left = node.style.right = \"\"\n      if (horiz == \"right\") {\n        left = display.sizer.clientWidth - node.offsetWidth\n        node.style.right = \"0px\"\n      } else {\n        if (horiz == \"left\") { left = 0 }\n        else if (horiz == \"middle\") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }\n        node.style.left = left + \"px\"\n      }\n      if (scroll)\n        { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) }\n    },\n\n    triggerOnKeyDown: methodOp(onKeyDown),\n    triggerOnKeyPress: methodOp(onKeyPress),\n    triggerOnKeyUp: onKeyUp,\n    triggerOnMouseDown: methodOp(onMouseDown),\n\n    execCommand: function(cmd) {\n      if (commands.hasOwnProperty(cmd))\n        { return commands[cmd].call(null, this) }\n    },\n\n    triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),\n\n    findPosH: function(from, amount, unit, visually) {\n      var this$1 = this;\n\n      var dir = 1\n      if (amount < 0) { dir = -1; amount = -amount }\n      var cur = clipPos(this.doc, from)\n      for (var i = 0; i < amount; ++i) {\n        cur = findPosH(this$1.doc, cur, dir, unit, visually)\n        if (cur.hitSide) { break }\n      }\n      return cur\n    },\n\n    moveH: methodOp(function(dir, unit) {\n      var this$1 = this;\n\n      this.extendSelectionsBy(function (range) {\n        if (this$1.display.shift || this$1.doc.extend || range.empty())\n          { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }\n        else\n          { return dir < 0 ? range.from() : range.to() }\n      }, sel_move)\n    }),\n\n    deleteH: methodOp(function(dir, unit) {\n      var sel = this.doc.sel, doc = this.doc\n      if (sel.somethingSelected())\n        { doc.replaceSelection(\"\", null, \"+delete\") }\n      else\n        { deleteNearSelection(this, function (range) {\n          var other = findPosH(doc, range.head, dir, unit, false)\n          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}\n        }) }\n    }),\n\n    findPosV: function(from, amount, unit, goalColumn) {\n      var this$1 = this;\n\n      var dir = 1, x = goalColumn\n      if (amount < 0) { dir = -1; amount = -amount }\n      var cur = clipPos(this.doc, from)\n      for (var i = 0; i < amount; ++i) {\n        var coords = cursorCoords(this$1, cur, \"div\")\n        if (x == null) { x = coords.left }\n        else { coords.left = x }\n        cur = findPosV(this$1, coords, dir, unit)\n        if (cur.hitSide) { break }\n      }\n      return cur\n    },\n\n    moveV: methodOp(function(dir, unit) {\n      var this$1 = this;\n\n      var doc = this.doc, goals = []\n      var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()\n      doc.extendSelectionsBy(function (range) {\n        if (collapse)\n          { return dir < 0 ? range.from() : range.to() }\n        var headPos = cursorCoords(this$1, range.head, \"div\")\n        if (range.goalColumn != null) { headPos.left = range.goalColumn }\n        goals.push(headPos.left)\n        var pos = findPosV(this$1, headPos, dir, unit)\n        if (unit == \"page\" && range == doc.sel.primary())\n          { addToScrollTop(this$1, charCoords(this$1, pos, \"div\").top - headPos.top) }\n        return pos\n      }, sel_move)\n      if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)\n        { doc.sel.ranges[i].goalColumn = goals[i] } }\n    }),\n\n    // Find the word at the given position (as returned by coordsChar).\n    findWordAt: function(pos) {\n      var doc = this.doc, line = getLine(doc, pos.line).text\n      var start = pos.ch, end = pos.ch\n      if (line) {\n        var helper = this.getHelper(pos, \"wordChars\")\n        if ((pos.sticky == \"before\" || end == line.length) && start) { --start; } else { ++end }\n        var startChar = line.charAt(start)\n        var check = isWordChar(startChar, helper)\n          ? function (ch) { return isWordChar(ch, helper); }\n          : /\\s/.test(startChar) ? function (ch) { return /\\s/.test(ch); }\n          : function (ch) { return (!/\\s/.test(ch) && !isWordChar(ch)); }\n        while (start > 0 && check(line.charAt(start - 1))) { --start }\n        while (end < line.length && check(line.charAt(end))) { ++end }\n      }\n      return new Range(Pos(pos.line, start), Pos(pos.line, end))\n    },\n\n    toggleOverwrite: function(value) {\n      if (value != null && value == this.state.overwrite) { return }\n      if (this.state.overwrite = !this.state.overwrite)\n        { addClass(this.display.cursorDiv, \"CodeMirror-overwrite\") }\n      else\n        { rmClass(this.display.cursorDiv, \"CodeMirror-overwrite\") }\n\n      signal(this, \"overwriteToggle\", this, this.state.overwrite)\n    },\n    hasFocus: function() { return this.display.input.getField() == activeElt() },\n    isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },\n\n    scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }),\n    getScrollInfo: function() {\n      var scroller = this.display.scroller\n      return {left: scroller.scrollLeft, top: scroller.scrollTop,\n              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,\n              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,\n              clientHeight: displayHeight(this), clientWidth: displayWidth(this)}\n    },\n\n    scrollIntoView: methodOp(function(range, margin) {\n      if (range == null) {\n        range = {from: this.doc.sel.primary().head, to: null}\n        if (margin == null) { margin = this.options.cursorScrollMargin }\n      } else if (typeof range == \"number\") {\n        range = {from: Pos(range, 0), to: null}\n      } else if (range.from == null) {\n        range = {from: range, to: null}\n      }\n      if (!range.to) { range.to = range.from }\n      range.margin = margin || 0\n\n      if (range.from.line != null) {\n        scrollToRange(this, range)\n      } else {\n        scrollToCoordsRange(this, range.from, range.to, range.margin)\n      }\n    }),\n\n    setSize: methodOp(function(width, height) {\n      var this$1 = this;\n\n      var interpret = function (val) { return typeof val == \"number\" || /^\\d+$/.test(String(val)) ? val + \"px\" : val; }\n      if (width != null) { this.display.wrapper.style.width = interpret(width) }\n      if (height != null) { this.display.wrapper.style.height = interpret(height) }\n      if (this.options.lineWrapping) { clearLineMeasurementCache(this) }\n      var lineNo = this.display.viewFrom\n      this.doc.iter(lineNo, this.display.viewTo, function (line) {\n        if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)\n          { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, \"widget\"); break } } }\n        ++lineNo\n      })\n      this.curOp.forceUpdate = true\n      signal(this, \"refresh\", this)\n    }),\n\n    operation: function(f){return runInOp(this, f)},\n    startOperation: function(){return startOperation(this)},\n    endOperation: function(){return endOperation(this)},\n\n    refresh: methodOp(function() {\n      var oldHeight = this.display.cachedTextHeight\n      regChange(this)\n      this.curOp.forceUpdate = true\n      clearCaches(this)\n      scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop)\n      updateGutterSpace(this)\n      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)\n        { estimateLineHeights(this) }\n      signal(this, \"refresh\", this)\n    }),\n\n    swapDoc: methodOp(function(doc) {\n      var old = this.doc\n      old.cm = null\n      attachDoc(this, doc)\n      clearCaches(this)\n      this.display.input.reset()\n      scrollToCoords(this, doc.scrollLeft, doc.scrollTop)\n      this.curOp.forceScroll = true\n      signalLater(this, \"swapDoc\", this, old)\n      return old\n    }),\n\n    getInputField: function(){return this.display.input.getField()},\n    getWrapperElement: function(){return this.display.wrapper},\n    getScrollerElement: function(){return this.display.scroller},\n    getGutterElement: function(){return this.display.gutters}\n  }\n  eventMixin(CodeMirror)\n\n  CodeMirror.registerHelper = function(type, name, value) {\n    if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }\n    helpers[type][name] = value\n  }\n  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {\n    CodeMirror.registerHelper(type, name, value)\n    helpers[type]._global.push({pred: predicate, val: value})\n  }\n}\n\n// Used for horizontal relative motion. Dir is -1 or 1 (left or\n// right), unit can be \"char\", \"column\" (like char, but doesn't\n// cross line boundaries), \"word\" (across next word), or \"group\" (to\n// the start of next group of word or non-word-non-whitespace\n// chars). The visually param controls whether, in right-to-left\n// text, direction 1 means to move towards the next index in the\n// string, or towards the character to the right of the current\n// position. The resulting position will have a hitSide=true\n// property if it reached the end of the document.\nfunction findPosH(doc, pos, dir, unit, visually) {\n  var oldPos = pos\n  var origDir = dir\n  var lineObj = getLine(doc, pos.line)\n  function findNextLine() {\n    var l = pos.line + dir\n    if (l < doc.first || l >= doc.first + doc.size) { return false }\n    pos = new Pos(l, pos.ch, pos.sticky)\n    return lineObj = getLine(doc, l)\n  }\n  function moveOnce(boundToLine) {\n    var next\n    if (visually) {\n      next = moveVisually(doc.cm, lineObj, pos, dir)\n    } else {\n      next = moveLogically(lineObj, pos, dir)\n    }\n    if (next == null) {\n      if (!boundToLine && findNextLine())\n        { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) }\n      else\n        { return false }\n    } else {\n      pos = next\n    }\n    return true\n  }\n\n  if (unit == \"char\") {\n    moveOnce()\n  } else if (unit == \"column\") {\n    moveOnce(true)\n  } else if (unit == \"word\" || unit == \"group\") {\n    var sawType = null, group = unit == \"group\"\n    var helper = doc.cm && doc.cm.getHelper(pos, \"wordChars\")\n    for (var first = true;; first = false) {\n      if (dir < 0 && !moveOnce(!first)) { break }\n      var cur = lineObj.text.charAt(pos.ch) || \"\\n\"\n      var type = isWordChar(cur, helper) ? \"w\"\n        : group && cur == \"\\n\" ? \"n\"\n        : !group || /\\s/.test(cur) ? null\n        : \"p\"\n      if (group && !first && !type) { type = \"s\" }\n      if (sawType && sawType != type) {\n        if (dir < 0) {dir = 1; moveOnce(); pos.sticky = \"after\"}\n        break\n      }\n\n      if (type) { sawType = type }\n      if (dir > 0 && !moveOnce(!first)) { break }\n    }\n  }\n  var result = skipAtomic(doc, pos, oldPos, origDir, true)\n  if (equalCursorPos(oldPos, result)) { result.hitSide = true }\n  return result\n}\n\n// For relative vertical movement. Dir may be -1 or 1. Unit can be\n// \"page\" or \"line\". The resulting position will have a hitSide=true\n// property if it reached the end of the document.\nfunction findPosV(cm, pos, dir, unit) {\n  var doc = cm.doc, x = pos.left, y\n  if (unit == \"page\") {\n    var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)\n    var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)\n    y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount\n\n  } else if (unit == \"line\") {\n    y = dir > 0 ? pos.bottom + 3 : pos.top - 3\n  }\n  var target\n  for (;;) {\n    target = coordsChar(cm, x, y)\n    if (!target.outside) { break }\n    if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }\n    y += dir * 5\n  }\n  return target\n}\n\n// CONTENTEDITABLE INPUT STYLE\n\nvar ContentEditableInput = function(cm) {\n  this.cm = cm\n  this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null\n  this.polling = new Delayed()\n  this.composing = null\n  this.gracePeriod = false\n  this.readDOMTimeout = null\n};\n\nContentEditableInput.prototype.init = function (display) {\n    var this$1 = this;\n\n  var input = this, cm = input.cm\n  var div = input.div = display.lineDiv\n  disableBrowserMagic(div, cm.options.spellcheck)\n\n  on(div, \"paste\", function (e) {\n    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n    // IE doesn't fire input events, so we schedule a read for the pasted content in this way\n    if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20) }\n  })\n\n  on(div, \"compositionstart\", function (e) {\n    this$1.composing = {data: e.data, done: false}\n  })\n  on(div, \"compositionupdate\", function (e) {\n    if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }\n  })\n  on(div, \"compositionend\", function (e) {\n    if (this$1.composing) {\n      if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }\n      this$1.composing.done = true\n    }\n  })\n\n  on(div, \"touchstart\", function () { return input.forceCompositionEnd(); })\n\n  on(div, \"input\", function () {\n    if (!this$1.composing) { this$1.readFromDOMSoon() }\n  })\n\n  function onCopyCut(e) {\n    if (signalDOMEvent(cm, e)) { return }\n    if (cm.somethingSelected()) {\n      setLastCopied({lineWise: false, text: cm.getSelections()})\n      if (e.type == \"cut\") { cm.replaceSelection(\"\", null, \"cut\") }\n    } else if (!cm.options.lineWiseCopyCut) {\n      return\n    } else {\n      var ranges = copyableRanges(cm)\n      setLastCopied({lineWise: true, text: ranges.text})\n      if (e.type == \"cut\") {\n        cm.operation(function () {\n          cm.setSelections(ranges.ranges, 0, sel_dontScroll)\n          cm.replaceSelection(\"\", null, \"cut\")\n        })\n      }\n    }\n    if (e.clipboardData) {\n      e.clipboardData.clearData()\n      var content = lastCopied.text.join(\"\\n\")\n      // iOS exposes the clipboard API, but seems to discard content inserted into it\n      e.clipboardData.setData(\"Text\", content)\n      if (e.clipboardData.getData(\"Text\") == content) {\n        e.preventDefault()\n        return\n      }\n    }\n    // Old-fashioned briefly-focus-a-textarea hack\n    var kludge = hiddenTextarea(), te = kludge.firstChild\n    cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)\n    te.value = lastCopied.text.join(\"\\n\")\n    var hadFocus = document.activeElement\n    selectInput(te)\n    setTimeout(function () {\n      cm.display.lineSpace.removeChild(kludge)\n      hadFocus.focus()\n      if (hadFocus == div) { input.showPrimarySelection() }\n    }, 50)\n  }\n  on(div, \"copy\", onCopyCut)\n  on(div, \"cut\", onCopyCut)\n};\n\nContentEditableInput.prototype.prepareSelection = function () {\n  var result = prepareSelection(this.cm, false)\n  result.focus = this.cm.state.focused\n  return result\n};\n\nContentEditableInput.prototype.showSelection = function (info, takeFocus) {\n  if (!info || !this.cm.display.view.length) { return }\n  if (info.focus || takeFocus) { this.showPrimarySelection() }\n  this.showMultipleSelections(info)\n};\n\nContentEditableInput.prototype.getSelection = function () {\n  return this.cm.display.wrapper.ownerDocument.getSelection()\n};\n\nContentEditableInput.prototype.showPrimarySelection = function () {\n  var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary()\n  var from = prim.from(), to = prim.to()\n\n  if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {\n    sel.removeAllRanges()\n    return\n  }\n\n  var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)\n  var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)\n  if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&\n      cmp(minPos(curAnchor, curFocus), from) == 0 &&\n      cmp(maxPos(curAnchor, curFocus), to) == 0)\n    { return }\n\n  var view = cm.display.view\n  var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||\n      {node: view[0].measure.map[2], offset: 0}\n  var end = to.line < cm.display.viewTo && posToDOM(cm, to)\n  if (!end) {\n    var measure = view[view.length - 1].measure\n    var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map\n    end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}\n  }\n\n  if (!start || !end) {\n    sel.removeAllRanges()\n    return\n  }\n\n  var old = sel.rangeCount && sel.getRangeAt(0), rng\n  try { rng = range(start.node, start.offset, end.offset, end.node) }\n  catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible\n  if (rng) {\n    if (!gecko && cm.state.focused) {\n      sel.collapse(start.node, start.offset)\n      if (!rng.collapsed) {\n        sel.removeAllRanges()\n        sel.addRange(rng)\n      }\n    } else {\n      sel.removeAllRanges()\n      sel.addRange(rng)\n    }\n    if (old && sel.anchorNode == null) { sel.addRange(old) }\n    else if (gecko) { this.startGracePeriod() }\n  }\n  this.rememberSelection()\n};\n\nContentEditableInput.prototype.startGracePeriod = function () {\n    var this$1 = this;\n\n  clearTimeout(this.gracePeriod)\n  this.gracePeriod = setTimeout(function () {\n    this$1.gracePeriod = false\n    if (this$1.selectionChanged())\n      { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }\n  }, 20)\n};\n\nContentEditableInput.prototype.showMultipleSelections = function (info) {\n  removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)\n  removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)\n};\n\nContentEditableInput.prototype.rememberSelection = function () {\n  var sel = this.getSelection()\n  this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset\n  this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset\n};\n\nContentEditableInput.prototype.selectionInEditor = function () {\n  var sel = this.getSelection()\n  if (!sel.rangeCount) { return false }\n  var node = sel.getRangeAt(0).commonAncestorContainer\n  return contains(this.div, node)\n};\n\nContentEditableInput.prototype.focus = function () {\n  if (this.cm.options.readOnly != \"nocursor\") {\n    if (!this.selectionInEditor())\n      { this.showSelection(this.prepareSelection(), true) }\n    this.div.focus()\n  }\n};\nContentEditableInput.prototype.blur = function () { this.div.blur() };\nContentEditableInput.prototype.getField = function () { return this.div };\n\nContentEditableInput.prototype.supportsTouch = function () { return true };\n\nContentEditableInput.prototype.receivedFocus = function () {\n  var input = this\n  if (this.selectionInEditor())\n    { this.pollSelection() }\n  else\n    { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }\n\n  function poll() {\n    if (input.cm.state.focused) {\n      input.pollSelection()\n      input.polling.set(input.cm.options.pollInterval, poll)\n    }\n  }\n  this.polling.set(this.cm.options.pollInterval, poll)\n};\n\nContentEditableInput.prototype.selectionChanged = function () {\n  var sel = this.getSelection()\n  return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||\n    sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset\n};\n\nContentEditableInput.prototype.pollSelection = function () {\n  if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }\n  var sel = this.getSelection(), cm = this.cm\n  // On Android Chrome (version 56, at least), backspacing into an\n  // uneditable block element will put the cursor in that element,\n  // and then, because it's not editable, hide the virtual keyboard.\n  // Because Android doesn't allow us to actually detect backspace\n  // presses in a sane way, this code checks for when that happens\n  // and simulates a backspace press in this case.\n  if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {\n    this.cm.triggerOnKeyDown({type: \"keydown\", keyCode: 8, preventDefault: Math.abs})\n    this.blur()\n    this.focus()\n    return\n  }\n  if (this.composing) { return }\n  this.rememberSelection()\n  var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)\n  var head = domToPos(cm, sel.focusNode, sel.focusOffset)\n  if (anchor && head) { runInOp(cm, function () {\n    setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)\n    if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }\n  }) }\n};\n\nContentEditableInput.prototype.pollContent = function () {\n  if (this.readDOMTimeout != null) {\n    clearTimeout(this.readDOMTimeout)\n    this.readDOMTimeout = null\n  }\n\n  var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()\n  var from = sel.from(), to = sel.to()\n  if (from.ch == 0 && from.line > cm.firstLine())\n    { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }\n  if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())\n    { to = Pos(to.line + 1, 0) }\n  if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }\n\n  var fromIndex, fromLine, fromNode\n  if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {\n    fromLine = lineNo(display.view[0].line)\n    fromNode = display.view[0].node\n  } else {\n    fromLine = lineNo(display.view[fromIndex].line)\n    fromNode = display.view[fromIndex - 1].node.nextSibling\n  }\n  var toIndex = findViewIndex(cm, to.line)\n  var toLine, toNode\n  if (toIndex == display.view.length - 1) {\n    toLine = display.viewTo - 1\n    toNode = display.lineDiv.lastChild\n  } else {\n    toLine = lineNo(display.view[toIndex + 1].line) - 1\n    toNode = display.view[toIndex + 1].node.previousSibling\n  }\n\n  if (!fromNode) { return false }\n  var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))\n  var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))\n  while (newText.length > 1 && oldText.length > 1) {\n    if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }\n    else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }\n    else { break }\n  }\n\n  var cutFront = 0, cutEnd = 0\n  var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)\n  while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))\n    { ++cutFront }\n  var newBot = lst(newText), oldBot = lst(oldText)\n  var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),\n                           oldBot.length - (oldText.length == 1 ? cutFront : 0))\n  while (cutEnd < maxCutEnd &&\n         newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))\n    { ++cutEnd }\n  // Try to move start of change to start of selection if ambiguous\n  if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {\n    while (cutFront && cutFront > from.ch &&\n           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {\n      cutFront--\n      cutEnd++\n    }\n  }\n\n  newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\\u200b+/, \"\")\n  newText[0] = newText[0].slice(cutFront).replace(/\\u200b+$/, \"\")\n\n  var chFrom = Pos(fromLine, cutFront)\n  var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)\n  if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {\n    replaceRange(cm.doc, newText, chFrom, chTo, \"+input\")\n    return true\n  }\n};\n\nContentEditableInput.prototype.ensurePolled = function () {\n  this.forceCompositionEnd()\n};\nContentEditableInput.prototype.reset = function () {\n  this.forceCompositionEnd()\n};\nContentEditableInput.prototype.forceCompositionEnd = function () {\n  if (!this.composing) { return }\n  clearTimeout(this.readDOMTimeout)\n  this.composing = null\n  this.updateFromDOM()\n  this.div.blur()\n  this.div.focus()\n};\nContentEditableInput.prototype.readFromDOMSoon = function () {\n    var this$1 = this;\n\n  if (this.readDOMTimeout != null) { return }\n  this.readDOMTimeout = setTimeout(function () {\n    this$1.readDOMTimeout = null\n    if (this$1.composing) {\n      if (this$1.composing.done) { this$1.composing = null }\n      else { return }\n    }\n    this$1.updateFromDOM()\n  }, 80)\n};\n\nContentEditableInput.prototype.updateFromDOM = function () {\n    var this$1 = this;\n\n  if (this.cm.isReadOnly() || !this.pollContent())\n    { runInOp(this.cm, function () { return regChange(this$1.cm); }) }\n};\n\nContentEditableInput.prototype.setUneditable = function (node) {\n  node.contentEditable = \"false\"\n};\n\nContentEditableInput.prototype.onKeyPress = function (e) {\n  if (e.charCode == 0 || this.composing) { return }\n  e.preventDefault()\n  if (!this.cm.isReadOnly())\n    { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }\n};\n\nContentEditableInput.prototype.readOnlyChanged = function (val) {\n  this.div.contentEditable = String(val != \"nocursor\")\n};\n\nContentEditableInput.prototype.onContextMenu = function () {};\nContentEditableInput.prototype.resetPosition = function () {};\n\nContentEditableInput.prototype.needsContentAttribute = true\n\nfunction posToDOM(cm, pos) {\n  var view = findViewForLine(cm, pos.line)\n  if (!view || view.hidden) { return null }\n  var line = getLine(cm.doc, pos.line)\n  var info = mapFromLineView(view, line, pos.line)\n\n  var order = getOrder(line, cm.doc.direction), side = \"left\"\n  if (order) {\n    var partPos = getBidiPartAt(order, pos.ch)\n    side = partPos % 2 ? \"right\" : \"left\"\n  }\n  var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)\n  result.offset = result.collapse == \"right\" ? result.end : result.start\n  return result\n}\n\nfunction isInGutter(node) {\n  for (var scan = node; scan; scan = scan.parentNode)\n    { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }\n  return false\n}\n\nfunction badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }\n\nfunction domTextBetween(cm, from, to, fromLine, toLine) {\n  var text = \"\", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false\n  function recognizeMarker(id) { return function (marker) { return marker.id == id; } }\n  function close() {\n    if (closing) {\n      text += lineSep\n      if (extraLinebreak) { text += lineSep }\n      closing = extraLinebreak = false\n    }\n  }\n  function addText(str) {\n    if (str) {\n      close()\n      text += str\n    }\n  }\n  function walk(node) {\n    if (node.nodeType == 1) {\n      var cmText = node.getAttribute(\"cm-text\")\n      if (cmText) {\n        addText(cmText)\n        return\n      }\n      var markerID = node.getAttribute(\"cm-marker\"), range\n      if (markerID) {\n        var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))\n        if (found.length && (range = found[0].find(0)))\n          { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) }\n        return\n      }\n      if (node.getAttribute(\"contenteditable\") == \"false\") { return }\n      var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName)\n      if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }\n\n      if (isBlock) { close() }\n      for (var i = 0; i < node.childNodes.length; i++)\n        { walk(node.childNodes[i]) }\n\n      if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true }\n      if (isBlock) { closing = true }\n    } else if (node.nodeType == 3) {\n      addText(node.nodeValue.replace(/\\u200b/g, \"\").replace(/\\u00a0/g, \" \"))\n    }\n  }\n  for (;;) {\n    walk(from)\n    if (from == to) { break }\n    from = from.nextSibling\n    extraLinebreak = false\n  }\n  return text\n}\n\nfunction domToPos(cm, node, offset) {\n  var lineNode\n  if (node == cm.display.lineDiv) {\n    lineNode = cm.display.lineDiv.childNodes[offset]\n    if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }\n    node = null; offset = 0\n  } else {\n    for (lineNode = node;; lineNode = lineNode.parentNode) {\n      if (!lineNode || lineNode == cm.display.lineDiv) { return null }\n      if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }\n    }\n  }\n  for (var i = 0; i < cm.display.view.length; i++) {\n    var lineView = cm.display.view[i]\n    if (lineView.node == lineNode)\n      { return locateNodeInLineView(lineView, node, offset) }\n  }\n}\n\nfunction locateNodeInLineView(lineView, node, offset) {\n  var wrapper = lineView.text.firstChild, bad = false\n  if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }\n  if (node == wrapper) {\n    bad = true\n    node = wrapper.childNodes[offset]\n    offset = 0\n    if (!node) {\n      var line = lineView.rest ? lst(lineView.rest) : lineView.line\n      return badPos(Pos(lineNo(line), line.text.length), bad)\n    }\n  }\n\n  var textNode = node.nodeType == 3 ? node : null, topNode = node\n  if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {\n    textNode = node.firstChild\n    if (offset) { offset = textNode.nodeValue.length }\n  }\n  while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }\n  var measure = lineView.measure, maps = measure.maps\n\n  function find(textNode, topNode, offset) {\n    for (var i = -1; i < (maps ? maps.length : 0); i++) {\n      var map = i < 0 ? measure.map : maps[i]\n      for (var j = 0; j < map.length; j += 3) {\n        var curNode = map[j + 2]\n        if (curNode == textNode || curNode == topNode) {\n          var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])\n          var ch = map[j] + offset\n          if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] }\n          return Pos(line, ch)\n        }\n      }\n    }\n  }\n  var found = find(textNode, topNode, offset)\n  if (found) { return badPos(found, bad) }\n\n  // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems\n  for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {\n    found = find(after, after.firstChild, 0)\n    if (found)\n      { return badPos(Pos(found.line, found.ch - dist), bad) }\n    else\n      { dist += after.textContent.length }\n  }\n  for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {\n    found = find(before, before.firstChild, -1)\n    if (found)\n      { return badPos(Pos(found.line, found.ch + dist$1), bad) }\n    else\n      { dist$1 += before.textContent.length }\n  }\n}\n\n// TEXTAREA INPUT STYLE\n\nvar TextareaInput = function(cm) {\n  this.cm = cm\n  // See input.poll and input.reset\n  this.prevInput = \"\"\n\n  // Flag that indicates whether we expect input to appear real soon\n  // now (after some event like 'keypress' or 'input') and are\n  // polling intensively.\n  this.pollingFast = false\n  // Self-resetting timeout for the poller\n  this.polling = new Delayed()\n  // Used to work around IE issue with selection being forgotten when focus moves away from textarea\n  this.hasSelection = false\n  this.composing = null\n};\n\nTextareaInput.prototype.init = function (display) {\n    var this$1 = this;\n\n  var input = this, cm = this.cm\n  this.createField(display)\n  var te = this.textarea\n\n  display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild)\n\n  // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)\n  if (ios) { te.style.width = \"0px\" }\n\n  on(te, \"input\", function () {\n    if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }\n    input.poll()\n  })\n\n  on(te, \"paste\", function (e) {\n    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n\n    cm.state.pasteIncoming = true\n    input.fastPoll()\n  })\n\n  function prepareCopyCut(e) {\n    if (signalDOMEvent(cm, e)) { return }\n    if (cm.somethingSelected()) {\n      setLastCopied({lineWise: false, text: cm.getSelections()})\n    } else if (!cm.options.lineWiseCopyCut) {\n      return\n    } else {\n      var ranges = copyableRanges(cm)\n      setLastCopied({lineWise: true, text: ranges.text})\n      if (e.type == \"cut\") {\n        cm.setSelections(ranges.ranges, null, sel_dontScroll)\n      } else {\n        input.prevInput = \"\"\n        te.value = ranges.text.join(\"\\n\")\n        selectInput(te)\n      }\n    }\n    if (e.type == \"cut\") { cm.state.cutIncoming = true }\n  }\n  on(te, \"cut\", prepareCopyCut)\n  on(te, \"copy\", prepareCopyCut)\n\n  on(display.scroller, \"paste\", function (e) {\n    if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }\n    cm.state.pasteIncoming = true\n    input.focus()\n  })\n\n  // Prevent normal selection in the editor (we handle our own)\n  on(display.lineSpace, \"selectstart\", function (e) {\n    if (!eventInWidget(display, e)) { e_preventDefault(e) }\n  })\n\n  on(te, \"compositionstart\", function () {\n    var start = cm.getCursor(\"from\")\n    if (input.composing) { input.composing.range.clear() }\n    input.composing = {\n      start: start,\n      range: cm.markText(start, cm.getCursor(\"to\"), {className: \"CodeMirror-composing\"})\n    }\n  })\n  on(te, \"compositionend\", function () {\n    if (input.composing) {\n      input.poll()\n      input.composing.range.clear()\n      input.composing = null\n    }\n  })\n};\n\nTextareaInput.prototype.createField = function (_display) {\n  // Wraps and hides input textarea\n  this.wrapper = hiddenTextarea()\n  // The semihidden textarea that is focused when the editor is\n  // focused, and receives input.\n  this.textarea = this.wrapper.firstChild\n};\n\nTextareaInput.prototype.prepareSelection = function () {\n  // Redraw the selection and/or cursor\n  var cm = this.cm, display = cm.display, doc = cm.doc\n  var result = prepareSelection(cm)\n\n  // Move the hidden textarea near the cursor to prevent scrolling artifacts\n  if (cm.options.moveInputWithCursor) {\n    var headPos = cursorCoords(cm, doc.sel.primary().head, \"div\")\n    var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()\n    result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,\n                                        headPos.top + lineOff.top - wrapOff.top))\n    result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,\n                                         headPos.left + lineOff.left - wrapOff.left))\n  }\n\n  return result\n};\n\nTextareaInput.prototype.showSelection = function (drawn) {\n  var cm = this.cm, display = cm.display\n  removeChildrenAndAdd(display.cursorDiv, drawn.cursors)\n  removeChildrenAndAdd(display.selectionDiv, drawn.selection)\n  if (drawn.teTop != null) {\n    this.wrapper.style.top = drawn.teTop + \"px\"\n    this.wrapper.style.left = drawn.teLeft + \"px\"\n  }\n};\n\n// Reset the input to correspond to the selection (or to be empty,\n// when not typing and nothing is selected)\nTextareaInput.prototype.reset = function (typing) {\n  if (this.contextMenuPending || this.composing) { return }\n  var cm = this.cm\n  if (cm.somethingSelected()) {\n    this.prevInput = \"\"\n    var content = cm.getSelection()\n    this.textarea.value = content\n    if (cm.state.focused) { selectInput(this.textarea) }\n    if (ie && ie_version >= 9) { this.hasSelection = content }\n  } else if (!typing) {\n    this.prevInput = this.textarea.value = \"\"\n    if (ie && ie_version >= 9) { this.hasSelection = null }\n  }\n};\n\nTextareaInput.prototype.getField = function () { return this.textarea };\n\nTextareaInput.prototype.supportsTouch = function () { return false };\n\nTextareaInput.prototype.focus = function () {\n  if (this.cm.options.readOnly != \"nocursor\" && (!mobile || activeElt() != this.textarea)) {\n    try { this.textarea.focus() }\n    catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM\n  }\n};\n\nTextareaInput.prototype.blur = function () { this.textarea.blur() };\n\nTextareaInput.prototype.resetPosition = function () {\n  this.wrapper.style.top = this.wrapper.style.left = 0\n};\n\nTextareaInput.prototype.receivedFocus = function () { this.slowPoll() };\n\n// Poll for input changes, using the normal rate of polling. This\n// runs as long as the editor is focused.\nTextareaInput.prototype.slowPoll = function () {\n    var this$1 = this;\n\n  if (this.pollingFast) { return }\n  this.polling.set(this.cm.options.pollInterval, function () {\n    this$1.poll()\n    if (this$1.cm.state.focused) { this$1.slowPoll() }\n  })\n};\n\n// When an event has just come in that is likely to add or change\n// something in the input textarea, we poll faster, to ensure that\n// the change appears on the screen quickly.\nTextareaInput.prototype.fastPoll = function () {\n  var missed = false, input = this\n  input.pollingFast = true\n  function p() {\n    var changed = input.poll()\n    if (!changed && !missed) {missed = true; input.polling.set(60, p)}\n    else {input.pollingFast = false; input.slowPoll()}\n  }\n  input.polling.set(20, p)\n};\n\n// Read input from the textarea, and update the document to match.\n// When something is selected, it is present in the textarea, and\n// selected (unless it is huge, in which case a placeholder is\n// used). When nothing is selected, the cursor sits after previously\n// seen text (can be empty), which is stored in prevInput (we must\n// not reset the textarea when typing, because that breaks IME).\nTextareaInput.prototype.poll = function () {\n    var this$1 = this;\n\n  var cm = this.cm, input = this.textarea, prevInput = this.prevInput\n  // Since this is called a *lot*, try to bail out as cheaply as\n  // possible when it is clear that nothing happened. hasSelection\n  // will be the case when there is a lot of text in the textarea,\n  // in which case reading its value would be expensive.\n  if (this.contextMenuPending || !cm.state.focused ||\n      (hasSelection(input) && !prevInput && !this.composing) ||\n      cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)\n    { return false }\n\n  var text = input.value\n  // If nothing changed, bail.\n  if (text == prevInput && !cm.somethingSelected()) { return false }\n  // Work around nonsensical selection resetting in IE9/10, and\n  // inexplicable appearance of private area unicode characters on\n  // some key combos in Mac (#2689).\n  if (ie && ie_version >= 9 && this.hasSelection === text ||\n      mac && /[\\uf700-\\uf7ff]/.test(text)) {\n    cm.display.input.reset()\n    return false\n  }\n\n  if (cm.doc.sel == cm.display.selForContextMenu) {\n    var first = text.charCodeAt(0)\n    if (first == 0x200b && !prevInput) { prevInput = \"\\u200b\" }\n    if (first == 0x21da) { this.reset(); return this.cm.execCommand(\"undo\") }\n  }\n  // Find the part of the input that is actually new\n  var same = 0, l = Math.min(prevInput.length, text.length)\n  while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }\n\n  runInOp(cm, function () {\n    applyTextInput(cm, text.slice(same), prevInput.length - same,\n                   null, this$1.composing ? \"*compose\" : null)\n\n    // Don't leave long text in the textarea, since it makes further polling slow\n    if (text.length > 1000 || text.indexOf(\"\\n\") > -1) { input.value = this$1.prevInput = \"\" }\n    else { this$1.prevInput = text }\n\n    if (this$1.composing) {\n      this$1.composing.range.clear()\n      this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor(\"to\"),\n                                         {className: \"CodeMirror-composing\"})\n    }\n  })\n  return true\n};\n\nTextareaInput.prototype.ensurePolled = function () {\n  if (this.pollingFast && this.poll()) { this.pollingFast = false }\n};\n\nTextareaInput.prototype.onKeyPress = function () {\n  if (ie && ie_version >= 9) { this.hasSelection = null }\n  this.fastPoll()\n};\n\nTextareaInput.prototype.onContextMenu = function (e) {\n  var input = this, cm = input.cm, display = cm.display, te = input.textarea\n  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop\n  if (!pos || presto) { return } // Opera is difficult.\n\n  // Reset the current text selection only if the click is done outside of the selection\n  // and 'resetSelectionOnContextMenu' option is true.\n  var reset = cm.options.resetSelectionOnContextMenu\n  if (reset && cm.doc.sel.contains(pos) == -1)\n    { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }\n\n  var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText\n  input.wrapper.style.cssText = \"position: absolute\"\n  var wrapperBox = input.wrapper.getBoundingClientRect()\n  te.style.cssText = \"position: absolute; width: 30px; height: 30px;\\n      top: \" + (e.clientY - wrapperBox.top - 5) + \"px; left: \" + (e.clientX - wrapperBox.left - 5) + \"px;\\n      z-index: 1000; background: \" + (ie ? \"rgba(255, 255, 255, .05)\" : \"transparent\") + \";\\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);\"\n  var oldScrollY\n  if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)\n  display.input.focus()\n  if (webkit) { window.scrollTo(null, oldScrollY) }\n  display.input.reset()\n  // Adds \"Select all\" to context menu in FF\n  if (!cm.somethingSelected()) { te.value = input.prevInput = \" \" }\n  input.contextMenuPending = true\n  display.selForContextMenu = cm.doc.sel\n  clearTimeout(display.detectingSelectAll)\n\n  // Select-all will be greyed out if there's nothing to select, so\n  // this adds a zero-width space so that we can later check whether\n  // it got selected.\n  function prepareSelectAllHack() {\n    if (te.selectionStart != null) {\n      var selected = cm.somethingSelected()\n      var extval = \"\\u200b\" + (selected ? te.value : \"\")\n      te.value = \"\\u21da\" // Used to catch context-menu undo\n      te.value = extval\n      input.prevInput = selected ? \"\" : \"\\u200b\"\n      te.selectionStart = 1; te.selectionEnd = extval.length\n      // Re-set this, in case some other handler touched the\n      // selection in the meantime.\n      display.selForContextMenu = cm.doc.sel\n    }\n  }\n  function rehide() {\n    input.contextMenuPending = false\n    input.wrapper.style.cssText = oldWrapperCSS\n    te.style.cssText = oldCSS\n    if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }\n\n    // Try to detect the user choosing select-all\n    if (te.selectionStart != null) {\n      if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }\n      var i = 0, poll = function () {\n        if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&\n            te.selectionEnd > 0 && input.prevInput == \"\\u200b\") {\n          operation(cm, selectAll)(cm)\n        } else if (i++ < 10) {\n          display.detectingSelectAll = setTimeout(poll, 500)\n        } else {\n          display.selForContextMenu = null\n          display.input.reset()\n        }\n      }\n      display.detectingSelectAll = setTimeout(poll, 200)\n    }\n  }\n\n  if (ie && ie_version >= 9) { prepareSelectAllHack() }\n  if (captureRightClick) {\n    e_stop(e)\n    var mouseup = function () {\n      off(window, \"mouseup\", mouseup)\n      setTimeout(rehide, 20)\n    }\n    on(window, \"mouseup\", mouseup)\n  } else {\n    setTimeout(rehide, 50)\n  }\n};\n\nTextareaInput.prototype.readOnlyChanged = function (val) {\n  if (!val) { this.reset() }\n  this.textarea.disabled = val == \"nocursor\"\n};\n\nTextareaInput.prototype.setUneditable = function () {};\n\nTextareaInput.prototype.needsContentAttribute = false\n\nfunction fromTextArea(textarea, options) {\n  options = options ? copyObj(options) : {}\n  options.value = textarea.value\n  if (!options.tabindex && textarea.tabIndex)\n    { options.tabindex = textarea.tabIndex }\n  if (!options.placeholder && textarea.placeholder)\n    { options.placeholder = textarea.placeholder }\n  // Set autofocus to true if this textarea is focused, or if it has\n  // autofocus and no other element is focused.\n  if (options.autofocus == null) {\n    var hasFocus = activeElt()\n    options.autofocus = hasFocus == textarea ||\n      textarea.getAttribute(\"autofocus\") != null && hasFocus == document.body\n  }\n\n  function save() {textarea.value = cm.getValue()}\n\n  var realSubmit\n  if (textarea.form) {\n    on(textarea.form, \"submit\", save)\n    // Deplorable hack to make the submit method do the right thing.\n    if (!options.leaveSubmitMethodAlone) {\n      var form = textarea.form\n      realSubmit = form.submit\n      try {\n        var wrappedSubmit = form.submit = function () {\n          save()\n          form.submit = realSubmit\n          form.submit()\n          form.submit = wrappedSubmit\n        }\n      } catch(e) {}\n    }\n  }\n\n  options.finishInit = function (cm) {\n    cm.save = save\n    cm.getTextArea = function () { return textarea; }\n    cm.toTextArea = function () {\n      cm.toTextArea = isNaN // Prevent this from being ran twice\n      save()\n      textarea.parentNode.removeChild(cm.getWrapperElement())\n      textarea.style.display = \"\"\n      if (textarea.form) {\n        off(textarea.form, \"submit\", save)\n        if (typeof textarea.form.submit == \"function\")\n          { textarea.form.submit = realSubmit }\n      }\n    }\n  }\n\n  textarea.style.display = \"none\"\n  var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },\n    options)\n  return cm\n}\n\nfunction addLegacyProps(CodeMirror) {\n  CodeMirror.off = off\n  CodeMirror.on = on\n  CodeMirror.wheelEventPixels = wheelEventPixels\n  CodeMirror.Doc = Doc\n  CodeMirror.splitLines = splitLinesAuto\n  CodeMirror.countColumn = countColumn\n  CodeMirror.findColumn = findColumn\n  CodeMirror.isWordChar = isWordCharBasic\n  CodeMirror.Pass = Pass\n  CodeMirror.signal = signal\n  CodeMirror.Line = Line\n  CodeMirror.changeEnd = changeEnd\n  CodeMirror.scrollbarModel = scrollbarModel\n  CodeMirror.Pos = Pos\n  CodeMirror.cmpPos = cmp\n  CodeMirror.modes = modes\n  CodeMirror.mimeModes = mimeModes\n  CodeMirror.resolveMode = resolveMode\n  CodeMirror.getMode = getMode\n  CodeMirror.modeExtensions = modeExtensions\n  CodeMirror.extendMode = extendMode\n  CodeMirror.copyState = copyState\n  CodeMirror.startState = startState\n  CodeMirror.innerMode = innerMode\n  CodeMirror.commands = commands\n  CodeMirror.keyMap = keyMap\n  CodeMirror.keyName = keyName\n  CodeMirror.isModifierKey = isModifierKey\n  CodeMirror.lookupKey = lookupKey\n  CodeMirror.normalizeKeyMap = normalizeKeyMap\n  CodeMirror.StringStream = StringStream\n  CodeMirror.SharedTextMarker = SharedTextMarker\n  CodeMirror.TextMarker = TextMarker\n  CodeMirror.LineWidget = LineWidget\n  CodeMirror.e_preventDefault = e_preventDefault\n  CodeMirror.e_stopPropagation = e_stopPropagation\n  CodeMirror.e_stop = e_stop\n  CodeMirror.addClass = addClass\n  CodeMirror.contains = contains\n  CodeMirror.rmClass = rmClass\n  CodeMirror.keyNames = keyNames\n}\n\n// EDITOR CONSTRUCTOR\n\ndefineOptions(CodeMirror)\n\naddEditorMethods(CodeMirror)\n\n// Set up methods on CodeMirror's prototype to redirect to the editor's document.\nvar dontDelegate = \"iter insert remove copy getEditor constructor\".split(\" \")\nfor (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)\n  { CodeMirror.prototype[prop] = (function(method) {\n    return function() {return method.apply(this.doc, arguments)}\n  })(Doc.prototype[prop]) } }\n\neventMixin(Doc)\n\n// INPUT HANDLING\n\nCodeMirror.inputStyles = {\"textarea\": TextareaInput, \"contenteditable\": ContentEditableInput}\n\n// MODE DEFINITION AND QUERYING\n\n// Extra arguments are stored as the mode's dependencies, which is\n// used by (legacy) mechanisms like loadmode.js to automatically\n// load a mode. (Preferred mechanism is the require/define calls.)\nCodeMirror.defineMode = function(name/*, mode, …*/) {\n  if (!CodeMirror.defaults.mode && name != \"null\") { CodeMirror.defaults.mode = name }\n  defineMode.apply(this, arguments)\n}\n\nCodeMirror.defineMIME = defineMIME\n\n// Minimal default mode.\nCodeMirror.defineMode(\"null\", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })\nCodeMirror.defineMIME(\"text/plain\", \"null\")\n\n// EXTENSIONS\n\nCodeMirror.defineExtension = function (name, func) {\n  CodeMirror.prototype[name] = func\n}\nCodeMirror.defineDocExtension = function (name, func) {\n  Doc.prototype[name] = func\n}\n\nCodeMirror.fromTextArea = fromTextArea\n\naddLegacyProps(CodeMirror)\n\nCodeMirror.version = \"5.38.0\"\n\nreturn CodeMirror;\n\n})));"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/codemirror-5.38.0/mode/crystal/crystal.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"crystal\", function(config) {\n    function wordRegExp(words, end) {\n      return new RegExp((end ? \"\" : \"^\") + \"(?:\" + words.join(\"|\") + \")\" + (end ? \"$\" : \"\\\\b\"));\n    }\n\n    function chain(tokenize, stream, state) {\n      state.tokenize.push(tokenize);\n      return tokenize(stream, state);\n    }\n\n    var operators = /^(?:[-+/%|&^]|\\*\\*?|[<>]{2})/;\n    var conditionalOperators = /^(?:[=!]~|===|<=>|[<>=!]=?|[|&]{2}|~)/;\n    var indexingOperators = /^(?:\\[\\][?=]?)/;\n    var anotherOperators = /^(?:\\.(?:\\.{2})?|->|[?:])/;\n    var idents = /^[a-z_\\u009F-\\uFFFF][a-zA-Z0-9_\\u009F-\\uFFFF]*/;\n    var types = /^[A-Z_\\u009F-\\uFFFF][a-zA-Z0-9_\\u009F-\\uFFFF]*/;\n    var keywords = wordRegExp([\n      \"abstract\", \"alias\", \"as\", \"asm\", \"begin\", \"break\", \"case\", \"class\", \"def\", \"do\",\n      \"else\", \"elsif\", \"end\", \"ensure\", \"enum\", \"extend\", \"for\", \"fun\", \"if\",\n      \"include\", \"instance_sizeof\", \"lib\", \"macro\", \"module\", \"next\", \"of\", \"out\", \"pointerof\",\n      \"private\", \"protected\", \"rescue\", \"return\", \"require\", \"select\", \"sizeof\", \"struct\",\n      \"super\", \"then\", \"type\", \"typeof\", \"uninitialized\", \"union\", \"unless\", \"until\", \"when\", \"while\", \"with\",\n      \"yield\", \"__DIR__\", \"__END_LINE__\", \"__FILE__\", \"__LINE__\"\n    ]);\n    var atomWords = wordRegExp([\"true\", \"false\", \"nil\", \"self\"]);\n    var indentKeywordsArray = [\n      \"def\", \"fun\", \"macro\",\n      \"class\", \"module\", \"struct\", \"lib\", \"enum\", \"union\",\n      \"do\", \"for\"\n    ];\n    var indentKeywords = wordRegExp(indentKeywordsArray);\n    var indentExpressionKeywordsArray = [\"if\", \"unless\", \"case\", \"while\", \"until\", \"begin\", \"then\"];\n    var indentExpressionKeywords = wordRegExp(indentExpressionKeywordsArray);\n    var dedentKeywordsArray = [\"end\", \"else\", \"elsif\", \"rescue\", \"ensure\"];\n    var dedentKeywords = wordRegExp(dedentKeywordsArray);\n    var dedentPunctualsArray = [\"\\\\)\", \"\\\\}\", \"\\\\]\"];\n    var dedentPunctuals = new RegExp(\"^(?:\" + dedentPunctualsArray.join(\"|\") + \")$\");\n    var nextTokenizer = {\n      \"def\": tokenFollowIdent, \"fun\": tokenFollowIdent, \"macro\": tokenMacroDef,\n      \"class\": tokenFollowType, \"module\": tokenFollowType, \"struct\": tokenFollowType,\n      \"lib\": tokenFollowType, \"enum\": tokenFollowType, \"union\": tokenFollowType\n    };\n    var matching = {\"[\": \"]\", \"{\": \"}\", \"(\": \")\", \"<\": \">\"};\n\n    function tokenBase(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      // Macros\n      if (state.lastToken != \"\\\\\" && stream.match(\"{%\", false)) {\n        return chain(tokenMacro(\"%\", \"%\"), stream, state);\n      }\n\n      if (state.lastToken != \"\\\\\" && stream.match(\"{{\", false)) {\n        return chain(tokenMacro(\"{\", \"}\"), stream, state);\n      }\n\n      // Comments\n      if (stream.peek() == \"#\") {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n\n      // Variables and keywords\n      var matched;\n      if (stream.match(idents)) {\n        stream.eat(/[?!]/);\n\n        matched = stream.current();\n        if (stream.eat(\":\")) {\n          return \"atom\";\n        } else if (state.lastToken == \".\") {\n          return \"property\";\n        } else if (keywords.test(matched)) {\n          if (indentKeywords.test(matched)) {\n            if (!(matched == \"fun\" && state.blocks.indexOf(\"lib\") >= 0) && !(matched == \"def\" && state.lastToken == \"abstract\")) {\n              state.blocks.push(matched);\n              state.currentIndent += 1;\n            }\n          } else if ((state.lastStyle == \"operator\" || !state.lastStyle) && indentExpressionKeywords.test(matched)) {\n            state.blocks.push(matched);\n            state.currentIndent += 1;\n          } else if (matched == \"end\") {\n            state.blocks.pop();\n            state.currentIndent -= 1;\n          }\n\n          if (nextTokenizer.hasOwnProperty(matched)) {\n            state.tokenize.push(nextTokenizer[matched]);\n          }\n\n          return \"keyword\";\n        } else if (atomWords.test(matched)) {\n          return \"atom\";\n        }\n\n        return \"variable\";\n      }\n\n      // Class variables and instance variables\n      // or attributes\n      if (stream.eat(\"@\")) {\n        if (stream.peek() == \"[\") {\n          return chain(tokenNest(\"[\", \"]\", \"meta\"), stream, state);\n        }\n\n        stream.eat(\"@\");\n        stream.match(idents) || stream.match(types);\n        return \"variable-2\";\n      }\n\n      // Constants and types\n      if (stream.match(types)) {\n        return \"tag\";\n      }\n\n      // Symbols or ':' operator\n      if (stream.eat(\":\")) {\n        if (stream.eat(\"\\\"\")) {\n          return chain(tokenQuote(\"\\\"\", \"atom\", false), stream, state);\n        } else if (stream.match(idents) || stream.match(types) ||\n                   stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators)) {\n          return \"atom\";\n        }\n        stream.eat(\":\");\n        return \"operator\";\n      }\n\n      // Strings\n      if (stream.eat(\"\\\"\")) {\n        return chain(tokenQuote(\"\\\"\", \"string\", true), stream, state);\n      }\n\n      // Strings or regexps or macro variables or '%' operator\n      if (stream.peek() == \"%\") {\n        var style = \"string\";\n        var embed = true;\n        var delim;\n\n        if (stream.match(\"%r\")) {\n          // Regexps\n          style = \"string-2\";\n          delim = stream.next();\n        } else if (stream.match(\"%w\")) {\n          embed = false;\n          delim = stream.next();\n        } else if (stream.match(\"%q\")) {\n          embed = false;\n          delim = stream.next();\n        } else {\n          if(delim = stream.match(/^%([^\\w\\s=])/)) {\n            delim = delim[1];\n          } else if (stream.match(/^%[a-zA-Z0-9_\\u009F-\\uFFFF]*/)) {\n            // Macro variables\n            return \"meta\";\n          } else {\n            // '%' operator\n            return \"operator\";\n          }\n        }\n\n        if (matching.hasOwnProperty(delim)) {\n          delim = matching[delim];\n        }\n        return chain(tokenQuote(delim, style, embed), stream, state);\n      }\n\n      // Here Docs\n      if (matched = stream.match(/^<<-('?)([A-Z]\\w*)\\1/)) {\n        return chain(tokenHereDoc(matched[2], !matched[1]), stream, state)\n      }\n\n      // Characters\n      if (stream.eat(\"'\")) {\n        stream.match(/^(?:[^']|\\\\(?:[befnrtv0'\"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\\{[0-9a-fA-F]{1,6}\\})))/);\n        stream.eat(\"'\");\n        return \"atom\";\n      }\n\n      // Numbers\n      if (stream.eat(\"0\")) {\n        if (stream.eat(\"x\")) {\n          stream.match(/^[0-9a-fA-F]+/);\n        } else if (stream.eat(\"o\")) {\n          stream.match(/^[0-7]+/);\n        } else if (stream.eat(\"b\")) {\n          stream.match(/^[01]+/);\n        }\n        return \"number\";\n      }\n\n      if (stream.eat(/^\\d/)) {\n        stream.match(/^\\d*(?:\\.\\d+)?(?:[eE][+-]?\\d+)?/);\n        return \"number\";\n      }\n\n      // Operators\n      if (stream.match(operators)) {\n        stream.eat(\"=\"); // Operators can follow assign symbol.\n        return \"operator\";\n      }\n\n      if (stream.match(conditionalOperators) || stream.match(anotherOperators)) {\n        return \"operator\";\n      }\n\n      // Parens and braces\n      if (matched = stream.match(/[({[]/, false)) {\n        matched = matched[0];\n        return chain(tokenNest(matched, matching[matched], null), stream, state);\n      }\n\n      // Escapes\n      if (stream.eat(\"\\\\\")) {\n        stream.next();\n        return \"meta\";\n      }\n\n      stream.next();\n      return null;\n    }\n\n    function tokenNest(begin, end, style, started) {\n      return function (stream, state) {\n        if (!started && stream.match(begin)) {\n          state.tokenize[state.tokenize.length - 1] = tokenNest(begin, end, style, true);\n          state.currentIndent += 1;\n          return style;\n        }\n\n        var nextStyle = tokenBase(stream, state);\n        if (stream.current() === end) {\n          state.tokenize.pop();\n          state.currentIndent -= 1;\n          nextStyle = style;\n        }\n\n        return nextStyle;\n      };\n    }\n\n    function tokenMacro(begin, end, started) {\n      return function (stream, state) {\n        if (!started && stream.match(\"{\" + begin)) {\n          state.currentIndent += 1;\n          state.tokenize[state.tokenize.length - 1] = tokenMacro(begin, end, true);\n          return \"meta\";\n        }\n\n        if (stream.match(end + \"}\")) {\n          state.currentIndent -= 1;\n          state.tokenize.pop();\n          return \"meta\";\n        }\n\n        return tokenBase(stream, state);\n      };\n    }\n\n    function tokenMacroDef(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      var matched;\n      if (matched = stream.match(idents)) {\n        if (matched == \"def\") {\n          return \"keyword\";\n        }\n        stream.eat(/[?!]/);\n      }\n\n      state.tokenize.pop();\n      return \"def\";\n    }\n\n    function tokenFollowIdent(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      if (stream.match(idents)) {\n        stream.eat(/[!?]/);\n      } else {\n        stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators);\n      }\n      state.tokenize.pop();\n      return \"def\";\n    }\n\n    function tokenFollowType(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      stream.match(types);\n      state.tokenize.pop();\n      return \"def\";\n    }\n\n    function tokenQuote(end, style, embed) {\n      return function (stream, state) {\n        var escaped = false;\n\n        while (stream.peek()) {\n          if (!escaped) {\n            if (stream.match(\"{%\", false)) {\n              state.tokenize.push(tokenMacro(\"%\", \"%\"));\n              return style;\n            }\n\n            if (stream.match(\"{{\", false)) {\n              state.tokenize.push(tokenMacro(\"{\", \"}\"));\n              return style;\n            }\n\n            if (embed && stream.match(\"#{\", false)) {\n              state.tokenize.push(tokenNest(\"#{\", \"}\", \"meta\"));\n              return style;\n            }\n\n            var ch = stream.next();\n\n            if (ch == end) {\n              state.tokenize.pop();\n              return style;\n            }\n\n            escaped = embed && ch == \"\\\\\";\n          } else {\n            stream.next();\n            escaped = false;\n          }\n        }\n\n        return style;\n      };\n    }\n\n    function tokenHereDoc(phrase, embed) {\n      return function (stream, state) {\n        if (stream.sol()) {\n          stream.eatSpace()\n          if (stream.match(phrase)) {\n            state.tokenize.pop();\n            return \"string\";\n          }\n        }\n\n        var escaped = false;\n        while (stream.peek()) {\n          if (!escaped) {\n            if (stream.match(\"{%\", false)) {\n              state.tokenize.push(tokenMacro(\"%\", \"%\"));\n              return \"string\";\n            }\n\n            if (stream.match(\"{{\", false)) {\n              state.tokenize.push(tokenMacro(\"{\", \"}\"));\n              return \"string\";\n            }\n\n            if (embed && stream.match(\"#{\", false)) {\n              state.tokenize.push(tokenNest(\"#{\", \"}\", \"meta\"));\n              return \"string\";\n            }\n\n            escaped = embed && stream.next() == \"\\\\\";\n          } else {\n            stream.next();\n            escaped = false;\n          }\n        }\n\n        return \"string\";\n      }\n    }\n\n    return {\n      startState: function () {\n        return {\n          tokenize: [tokenBase],\n          currentIndent: 0,\n          lastToken: null,\n          lastStyle: null,\n          blocks: []\n        };\n      },\n\n      token: function (stream, state) {\n        var style = state.tokenize[state.tokenize.length - 1](stream, state);\n        var token = stream.current();\n\n        if (style && style != \"comment\") {\n          state.lastToken = token;\n          state.lastStyle = style;\n        }\n\n        return style;\n      },\n\n      indent: function (state, textAfter) {\n        textAfter = textAfter.replace(/^\\s*(?:\\{%)?\\s*|\\s*(?:%\\})?\\s*$/g, \"\");\n\n        if (dedentKeywords.test(textAfter) || dedentPunctuals.test(textAfter)) {\n          return config.indentUnit * (state.currentIndent - 1);\n        }\n\n        return config.indentUnit * state.currentIndent;\n      },\n\n      fold: \"indent\",\n      electricInput: wordRegExp(dedentPunctualsArray.concat(dedentKeywordsArray), true),\n      lineComment: '#'\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-crystal\", \"crystal\");\n});\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/codemirror-5.38.0/theme/neat.css",
    "content": ".cm-s-neat span.cm-comment { color: #a86; }\n.cm-s-neat span.cm-keyword { line-height: 1em; font-weight: bold; color: blue; }\n.cm-s-neat span.cm-string { color: #a22; }\n.cm-s-neat span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; }\n.cm-s-neat span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; }\n.cm-s-neat span.cm-variable { color: black; }\n.cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; }\n.cm-s-neat span.cm-meta { color: #555; }\n.cm-s-neat span.cm-link { color: #3a3; }\n\n.cm-s-neat .CodeMirror-activeline-background { background: #e8f2ff; }\n.cm-s-neat .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; }\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/public/vendor/octicons-19.5.0/octicons.css",
    "content": "/*\n\n.octicon is optimized for 16px.\n.mega-octicon is optimized for larger sizes.\n\n*/\n.octicon, .mega-octicon {\n  display: inline-block;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  width: 16px;\n  height: 16px;\n  vertical-align: text-bottom;\n}\n\n.mega-octicon {\n  width: 50px;\n  height: 50px;\n  vertical-align: middle;\n}\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/server.cr",
    "content": "require \"http/server\"\nrequire \"log\"\nrequire \"ecr/macros\"\nrequire \"compiler/crystal/tools/formatter\"\nrequire \"markd\"\n\nmodule Crystal::Playground\n  Log = ::Log.for(\"crystal.playground\")\n\n  class Session\n    getter tag : Int32\n\n    def initialize(@ws : HTTP::WebSocket, @session_key : Int32, @port : Int32, @host : String? = \"localhost\")\n      @running_process_filename = \"\"\n      @tag = 0\n    end\n\n    def self.instrument_and_prelude(session_key, port, tag, source, host : String? = \"localhost\")\n      # TODO: figure out how syntax warnings should be reported\n      ast = Parser.new(source).parse\n\n      instrumented = Playground::AgentInstrumentorTransformer.transform(ast).to_s\n      Log.info { \"Code instrumentation (session=#{session_key}, tag=#{tag}).\\n#{instrumented}\" }\n\n      prelude = <<-CRYSTAL\n        require \"compiler/crystal/tools/playground/agent\"\n\n        class Crystal::Playground::Agent\n          @@instance = Crystal::Playground::Agent.new(\"ws://#{host}:#{port}/agent/#{session_key}/#{tag}\", #{tag})\n\n          def self.instance\n            @@instance\n          end\n        end\n\n        def _p\n          Crystal::Playground::Agent.instance\n        end\n        CRYSTAL\n\n      [\n        Compiler::Source.new(\"playground_prelude\", prelude),\n        Compiler::Source.new(\"play\", instrumented),\n      ]\n    end\n\n    def run(source, tag)\n      Log.info { \"Request to run code (session=#{@session_key}, tag=#{tag}).\\n#{source}\" }\n\n      @tag = tag\n      begin\n        sources = self.class.instrument_and_prelude(@session_key, @port, tag, source, host: @host)\n      rescue ex : Crystal::CodeError\n        send_exception ex, tag\n        return\n      end\n\n      output_filename = Crystal.temp_executable \"play-#{@session_key}-#{tag}\"\n      compiler = Compiler.new\n      compiler.color = false\n      begin\n        Log.info { \"Instrumented code compilation started (session=#{@session_key}, tag=#{tag}).\" }\n        compiler.compile sources, output_filename\n      rescue ex\n        Log.info { \"Instrumented code compilation failed (session=#{@session_key}, tag=#{tag}).\" }\n\n        # due to instrumentation, we compile the original program\n        begin\n          Log.info { \"Original code compilation started (session=#{@session_key}, tag=#{tag}).\" }\n          compiler.compile Compiler::Source.new(\"play\", source), output_filename\n        rescue ex\n          Log.info { \"Original code compilation failed (session=#{@session_key}, tag=#{tag}).\" }\n          send_exception ex, tag\n          return # if we don't exit here we've found a bug\n        end\n\n        Log.error { \"Instrumentation bug found (session=#{@session_key}, tag=#{tag}).\" }\n        send_with_json_builder do |json|\n          json.field \"type\", \"bug\"\n          json.field \"tag\", tag\n          json.field \"exception\" do\n            append_exception json, ex\n          end\n        end\n\n        return\n      end\n\n      execute tag, output_filename\n\n      send_with_json_builder do |json|\n        json.field \"type\", \"run\"\n        json.field \"tag\", tag\n        json.field \"filename\", output_filename\n      end\n    end\n\n    def format(source, tag)\n      Log.info { \"Request to format code (session=#{@session_key}, tag=#{tag}).\\n#{source}\" }\n\n      @tag = tag\n\n      begin\n        value = Crystal.format source\n      rescue ex : Crystal::CodeError\n        send_exception ex, tag\n        return\n      end\n\n      send_with_json_builder do |json|\n        json.field \"type\", \"format\"\n        json.field \"tag\", tag\n        json.field \"value\", value\n      end\n    end\n\n    def stop\n      stop_process\n    end\n\n    def send(message)\n      @ws.send(message)\n    rescue ex : IO::Error\n      Log.warn { \"Unable to send message (session=#{@session_key}).\" }\n    end\n\n    def send_with_json_builder(&)\n      send(JSON.build do |json|\n        json.object do\n          yield json\n        end\n      end)\n    end\n\n    def send_exception(ex, tag)\n      send_with_json_builder do |json|\n        json.field \"type\", \"exception\"\n        json.field \"tag\", tag\n        json.field \"exception\" do\n          append_exception json, ex\n        end\n      end\n    end\n\n    def append_exception(json, ex)\n      json.object do\n        json.field \"message\", ex.to_s\n        if ex.is_a?(Crystal::CodeError)\n          json.field \"payload\" do\n            ex.to_json(json)\n          end\n        end\n      end\n    end\n\n    private def stop_process\n      if process = @process\n        Log.info { \"Code execution killed (session=#{@session_key}, filename=#{@running_process_filename}).\" }\n        @process = nil\n        File.delete? @running_process_filename\n        process.terminate rescue nil\n      end\n    end\n\n    private def execute(tag, output_filename)\n      stop_process\n\n      Log.info { \"Code execution started (session=#{@session_key}, tag=#{tag}, filename=#{output_filename}).\" }\n      process = @process = Process.new(output_filename, args: [] of String, input: Process::Redirect::Pipe, output: Process::Redirect::Pipe, error: Process::Redirect::Pipe)\n      @running_process_filename = output_filename\n\n      spawn do\n        status = process.wait\n        Log.info { \"Code execution ended (session=#{@session_key}, tag=#{tag}, filename=#{output_filename}).\" }\n\n        send_with_json_builder do |json|\n          json.field \"type\", \"exit\"\n          json.field \"tag\", tag\n          json.field \"status\", status.to_s\n        end\n      end\n\n      bind_io_as_output tag, process.output\n      bind_io_as_output tag, process.error\n    end\n\n    private def bind_io_as_output(tag, io)\n      spawn do\n        loop do\n          output = String.new(4096) do |buffer|\n            length = io.read_utf8(Slice.new(buffer, 4096))\n            {length, 0}\n          end\n          break if output.empty?\n\n          send_with_json_builder do |json|\n            json.field \"type\", \"output\"\n            json.field \"tag\", tag\n            json.field \"content\", output\n          end\n        rescue\n          break\n        end\n      end\n    end\n  end\n\n  abstract class PlaygroundPage\n    getter styles = [] of String\n    getter scripts = [] of String\n\n    def render_with_layout(io, &block)\n      ECR.embed \"#{__DIR__}/views/layout.html.ecr\", io\n    end\n  end\n\n  class FileContentPage < PlaygroundPage\n    def initialize(@filename : String)\n    end\n\n    def content\n      extname = File.extname(@filename)\n      content = if extname == \".cr\"\n                  crystal_source_to_markdown(@filename)\n                else\n                  File.read(@filename)\n                end\n\n      if extname.in?(\".md\", \".cr\")\n        content = Markd.to_html(content)\n      end\n      content\n    rescue e\n      e.message || \"Error: generating content for #{@filename}\"\n    end\n\n    def to_s(io : IO) : Nil\n      body = content\n      # avoid the layout if the file is a full html\n      if File.extname(@filename).starts_with?(\".htm\") && content.starts_with?(\"<!\")\n        io << body\n      else\n        render_with_layout(io) do\n          body\n        end\n      end\n    end\n\n    private def crystal_source_to_markdown(filename)\n      String.build do |io|\n        header = true\n        File.each_line(filename, chomp: false) do |line|\n          if header && line[0] != '\\n' && line[0] != '#'\n            header = false\n            io << \"```playground\\n\"\n          end\n\n          if header\n            io << line.sub(/^\\#\\ /, \"\")\n          else\n            io << line\n          end\n        end\n\n        unless header\n          io << \"```\"\n        end\n      end\n    end\n  end\n\n  class WorkbookIndexPage < PlaygroundPage\n    record Item, title : String, path : String\n\n    def items\n      files.map do |f|\n        ext = File.extname(f)\n        title = File.basename(f)[0..-ext.size - 1].gsub(/[_-]/, \" \").camelcase\n        Item.new(title, \"/workbook/#{f[0..-ext.size - 1]}\")\n      end\n    end\n\n    def has_items\n      !files.empty?\n    end\n\n    private def files\n      Dir[\"playground/*.{md,html,cr}\"]\n    end\n\n    def to_s(io : IO) : Nil\n      render_with_layout(io) do\n        ECR.embed \"#{__DIR__}/views/_workbook.html.ecr\", io\n        nil\n      end\n    end\n  end\n\n  class PageHandler\n    include HTTP::Handler\n\n    @page : PlaygroundPage\n\n    def initialize(@path : String, filename : String)\n      @page = FileContentPage.new(filename)\n    end\n\n    def initialize(@path : String, @page : PlaygroundPage)\n    end\n\n    def call(context)\n      case {context.request.method, context.request.resource}\n      when {\"GET\", @path}\n        context.response.headers[\"Content-Type\"] = \"text/html\"\n        context.response << @page.to_s\n      else\n        call_next(context)\n      end\n    end\n  end\n\n  class WorkbookHandler\n    include HTTP::Handler\n\n    def call(context)\n      case {context.request.method, context.request.path}\n      when {\"GET\", /\\/workbook\\/playground\\/(.*)/}\n        files = Dir[\"playground/#{$1}.{md,html,cr}\"]\n        if files.size > 0\n          context.response.headers[\"Content-Type\"] = \"text/html\"\n          page = FileContentPage.new(files[0])\n          load_resources page\n          context.response << page\n          return\n        end\n      else\n        # Not a special path\n      end\n\n      call_next(context)\n    end\n\n    def load_resources(page : PlaygroundPage)\n      Dir[\"playground/resources/*.css\"].each do |file|\n        page.styles << \"/workbook/#{file}\"\n      end\n      Dir[\"playground/resources/*.js\"].each do |file|\n        page.scripts << \"/workbook/#{file}\"\n      end\n    end\n  end\n\n  class PathStaticFileHandler < HTTP::StaticFileHandler\n    def initialize(@path : String, public_dir : String, fallthrough = true)\n      super(public_dir, fallthrough)\n    end\n\n    def call(context)\n      if context.request.path.try &.starts_with?(@path)\n        super\n      else\n        call_next(context)\n      end\n    end\n\n    def request_path(path : String) : String\n      path[@path.size..-1]\n    end\n  end\n\n  class PathWebSocketHandler < HTTP::WebSocketHandler\n    def initialize(@path : String, &proc : HTTP::WebSocket, HTTP::Server::Context ->)\n      super(&proc)\n    end\n\n    def call(context)\n      if context.request.path.try &.starts_with?(@path)\n        super\n      else\n        call_next(context)\n      end\n    end\n  end\n\n  class EnvironmentHandler\n    include HTTP::Handler\n\n    DEFAULT_SOURCE = <<-CRYSTAL\n      def find_string(text, word)\n        (0..text.size-word.size).each do |i|\n          { i, text[i..i+word.size-1] }\n          if text[i..i+word.size-1] == word\n            return i\n          end\n        end\n\n        nil\n      end\n\n      find_string \"Crystal is awesome!\", \"awesome\"\n      find_string \"Crystal is awesome!\", \"not sure\"\n      CRYSTAL\n\n    def initialize(@server : Playground::Server)\n    end\n\n    def call(context)\n      case {context.request.method, context.request.resource}\n      when {\"GET\", \"/environment.js\"}\n        context.response.headers[\"Content-Type\"] = \"application/javascript\"\n\n        context.response.puts <<-JS\n          Environment = {}\n          Environment.version = #{Crystal::Config.description.inspect}\n          Environment.defaultSource = #{DEFAULT_SOURCE.inspect}\n          JS\n\n        if source = @server.source\n          context.response.puts \"Environment.source = #{source.code.inspect}\"\n        else\n          context.response.puts \"Environment.source = null\"\n        end\n      else\n        call_next(context)\n      end\n    end\n  end\n\n  class Error < Crystal::Error\n  end\n\n  class Server\n    @sessions = {} of Int32 => Session\n    @sessions_key = 0\n\n    property host : String?\n    property port\n    property source : Compiler::Source?\n\n    def initialize\n      @host = nil\n      @port = 8080\n      @verbose = false\n    end\n\n    def start\n      playground_dir = File.dirname(CrystalPath.new.find(\"compiler/crystal/tools/playground/server.cr\").not_nil![0])\n      views_dir = File.join(playground_dir, \"views\")\n      public_dir = File.join(playground_dir, \"public\")\n\n      agent_ws = PathWebSocketHandler.new \"/agent\" do |ws, context|\n        match_data = context.request.path.not_nil!.match!(/\\/(\\d+)\\/(\\d+)$/)\n        session_key = match_data[1].to_i\n        tag = match_data[2].to_i\n        Log.info { \"#{context.request.path} WebSocket connected (session=#{session_key}, tag=#{tag})\" }\n\n        session = @sessions[session_key]\n\n        ws.on_message do |message|\n          # ignore if the session is already about another execution.\n          if tag == session.tag\n            # forward every message to the client.\n            session.send(message)\n          end\n        end\n      end\n\n      client_ws = PathWebSocketHandler.new \"/client\" do |ws, context|\n        origin = context.request.headers[\"Origin\"]\n        if !accept_request?(origin)\n          Log.warn { \"Invalid Request Origin: #{origin}\" }\n          ws.close :policy_violation, \"Invalid Request Origin\"\n        else\n          @sessions_key += 1\n          @sessions[@sessions_key] = session = Session.new(ws, @sessions_key, @port, host: @host)\n          Log.info { \"/client WebSocket connected as session=#{@sessions_key}\" }\n\n          ws.on_message do |message|\n            json = JSON.parse(message)\n            case json[\"type\"].as_s\n            when \"run\"\n              source = json[\"source\"].as_s\n              tag = json[\"tag\"].as_i\n              session.run source, tag\n            when \"stop\"\n              session.stop\n            when \"format\"\n              source = json[\"source\"].as_s\n              tag = json[\"tag\"].as_i\n              session.format source, tag\n            else\n              # TODO: maybe raise because it's an unexpected message?\n            end\n          end\n        end\n      end\n\n      handlers = [\n        client_ws,\n        agent_ws,\n        PageHandler.new(\"/\", File.join(views_dir, \"_index.html\")),\n        PageHandler.new(\"/about\", File.join(views_dir, \"_about.html\")),\n        PageHandler.new(\"/settings\", File.join(views_dir, \"_settings.html\")),\n        PageHandler.new(\"/workbook\", WorkbookIndexPage.new),\n        PathStaticFileHandler.new(\"/workbook/playground/resources\", \"playground/resources\", false),\n        WorkbookHandler.new,\n        EnvironmentHandler.new(self),\n        HTTP::StaticFileHandler.new(public_dir),\n      ]\n\n      server = HTTP::Server.new handlers\n\n      address = server.bind_tcp @host || Socket::IPAddress::LOOPBACK, @port\n      @port = address.port\n      @host = address.address\n\n      puts \"Listening on http://#{address}\"\n      if address.unspecified?\n        puts \"WARNING running playground on #{address.address} is insecure.\"\n      end\n\n      begin\n        server.listen\n      rescue ex\n        raise Playground::Error.new(ex.message)\n      end\n    rescue e : Socket::BindError\n      raise Playground::Error.new(e.message)\n    end\n\n    private def accept_request?(origin)\n      case @host\n      when nil, \"localhost\", \"127.0.0.1\"\n        origin.in?(\"http://localhost:#{@port}\", \"http://127.0.0.1:#{@port}\")\n      when \"0.0.0.0\"\n        true\n      else\n        origin == \"http://#{@host}:#{@port}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/views/_about.html",
    "content": "<h1>Source</h1>\n\n<p>\nCrystal Playground is developed together with Crystal Compiler.\n</p>\n\n<p>\nYou can find the source code at <a href=\"https://github.com/crystal-lang/crystal/tree/master/src/compiler/crystal/tools/playground\">https://github.com/crystal-lang/crystal/tree/master/src/compiler/crystal/tools/playground</a>.\n</p>\n\n<p>\nCurrently you are using <code class=\"crystal-version\"></code>.\n</p>\n\n<h1>Usage</h1>\n\n<p>\nStart typing code in the editor. When you stop typing or click the green button the code will be compiled and executed. Go ahead, try it!\n</p>\n\n<pre class=\"playground\">\na = 1\nb = 3</pre>\n\n<p>\nAs you can see, some values appeared on the side. These are the values that each line generates.\n</p>\n\n<p>\nWhen a program prints, like when using <code>puts</code>, the output is visible in the bottom panel.\n</p>\n\n<pre class=\"playground\">\ns = \"Lorem ipsum\"\nputs s</pre>\n\n<p>\nIn the main playground this is hidden by default and will appear when <svg viewBox=\"0 0 16 16\" class=\"octicon teal-text\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#terminal-16\"></use></svg> is pressed. Since the sidebar will likely show all the needed information you won't need this output all the time. Also, <code>puts</code> returns <code>nil</code>, but the sidebar will show the argument instead of the returned value. The same applies for <code>print</code> and <code>raises</code>.\n</p>\n\n<p>\nLoops, functions, classes, sleeps and the whole language is available. When a line is executed multiple times, all produced values are collected and can be inspected in a table.\n</p>\n\n<pre class=\"playground\">\ndef sample(n)\n  res = [] of Float64\n  n.times do\n    res << rand\n  end\n  res\nend\n\nsample 5</pre>\n\n<p>\nAnd you can use the whole standard library. Seriously. Run the next sample and <a href=\"http://0.0.0.0:5678\" target=\"_blank\">click here <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#link-external-16\"></use></svg></a>. Once you finish you can stop the long running process with the stop button. That will kill the process, hence display an exit status and a <svg viewBox=\"0 0 16 16\" class=\"octicon red-text\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#terminal-16\"></use></svg></span>.\n</p>\n\n<pre class=\"playground\">\nrequire \"http/server\"\n\nserver = HTTP::Server.new do |context|\n  context.request.path\n  context.response.headers[\"Content-Type\"] = \"text/plain\"\n  context.response.print(\"Hello world!\")\nend\nserver.bind_tcp \"0.0.0.0\", 5678\n\nputs \"Listening on http://0.0.0.0:5678\"\nserver.listen</pre>\n\n<p>\nWhen <code>crystal play</code> is run from your awesome Crystal app or shard you are able to use <code>require</code> as you would normally do: require relative files and even dependencies declared in your <code>shards.yml</code>.\n</p>\n\n<p>\nIn loops it is sometimes useful to inspect the values of multiple variables. Tuple literals are handy for this since each element of the tuple will appear in a different column in the table.\n</p>\n\n<pre class=\"playground\">\n(1..100).each do |i|\n  r = case {i % 3 == 0, i % 5 == 0}\n  when {true, true}\n    \"FizzBuzz\"\n  when {true, _}\n    \"Fizz\"\n  when {_, true}\n    \"Buzz\"\n  else\n    i\n  end\n\n  {i, r, i % 3, i % 5}\nend\n</pre>\n\n<p>\nAnd regarding errors, try to fix the following code one step at a time. You can see the full trace when clicking the inline errors.\n</p>\n\n<pre class=\"playground autorun\">\ndef bar\n  \"Hurray \" + 42\nend\n\nbaz\n</pre>\n\n        <h1>Load, Store and Share</h1>\n\n<div class=\"row\">\n  <div class=\"col s8\">\n    <p>\n    In the playground your code is persisted in every stroke and before you close or refresh the window. It is persisted in both <code>SessionStore</code> and <code>LocalStore</code> if available. This means that next time you open the playground the code will still be there!\n    </p>\n\n    <p>\n    You are able to save the current code as a file and even share it as a gist from the action buttons in the bottom right corner.\n    </p>\n\n    <p>\n    For loading a Crystal <code>.cr</code> file, just drag and drop it anywhere in the playground. Or start the server with <code>$ crystal play ./path/to/file.cr</code>.\n    </p>\n  </div>\n  <div class=\"col s4 center-align\">\n    <div>\n      <div>\n        <ul class=\"unstyled\">\n          <li style=\"padding-bottom: 14px;\">\n            <a class=\"btn-floating btn grey darken-1\" href=\"javascript:\">\n              <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#github-16\"></use></svg>\n            </a>\n          </li>\n          <li>\n            <a class=\"btn-floating btn red darken-1\" href=\"javascript:\">\n              <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#desktop-download-16\"></use></svg>\n            </a>\n          </li>\n        </ul>\n        <a class=\"run-button btn-floating btn-large waves-effect waves-light\" href=\"javascript:\">\n          <svg viewBox=\"0 0 16 16\" class=\"mega-octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#triangle-right-16\"></use></svg>\n        </a>\n      </div>\n    </div>\n  </div>\n</div>\n\n<h1>Workbook</h1>\n\n<p>In the <a href=\"/workbook\">workbook</a> you will find a listing of all the files in the <code>./playground</code> folder of your current location.<p>\n\n<p>\nThese files can be <code>.md</code>, <code>.html</code> and even <code>.cr</code>. And will be displayed with playground editor.\n</p>\n\n<h1>Credits</h1>\n\n<p>\nThe following libraries are used in Crystal Playground.\n</p>\n\n<ul>\n  <li><a href=\"//materializecss.com\" target=\"_blank\">Materialize</a></li>\n  <li><a href=\"//codemirror.net\" target=\"_blank\">CodeMirror</a></li>\n  <li><a href=\"//octicons.github.com\" target=\"_blank\">GitHub Octicons</a></li>\n  <li><a href=\"//github.com/drudru/ansi_up\" target=\"_blank\">ansi_up.js</a></li>\n</ul>\n\n<p>\nThanks for all the hard work!\n</p>\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/views/_index.html",
    "content": "<div class=\"row\">\n</div>\n\n<div class=\"row row-narrow\" id=\"mainEditorContainer\">\n</div>\n\n<div class=\"row\">\n  <div class=\"col s7\">\n    <a id=\"runFormatterBtn\" class=\"code-btn waves-effect waves-light btn-flat\" href=\"#run-formatter\">\n      <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#checklist-16\"></use></svg>\n      <span>Run Formatter</span>\n    </a>\n  </div>\n  <div class=\"col s5\">\n    <a class=\"modal-trigger code-btn waves-effect waves-light btn-flat\" href=\"#output-modal\">\n      <svg id=\"mainOutputIndicator\" viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#terminal-16\"></use></svg>\n      <span>Show Output</span>\n    </a>\n  </div>\n</div>\n\n<div class=\"fixed-action-btn\" style=\"bottom: 45px; right: 24px;\" id=\"mainButtonsContainer\">\n  <ul class=\"unstyled\">\n    <li>\n      <a class=\"btn-floating btn grey darken-1 tooltipped\" href=\"#\" data-position=\"left\" data-delay=\"50\" data-tooltip=\"Share as gist\" id=\"saveAsGist\">\n        <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#github-16\"></use></svg>\n      </a>\n    </li>\n    <li>\n      <a class=\"btn-floating btn red darken-1 tooltipped\" href=\"#\" data-position=\"left\" data-delay=\"50\" data-tooltip=\"Download code as file\" id=\"saveAsFile\">\n        <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#desktop-download-16\"></use></svg>\n      </a>\n    </li>\n  </ul>\n</div>\n\n<div id=\"output-modal\" class=\"modal bottom-sheet\">\n  <div class=\"modal-content\">\n    <h6>Output</h6>\n    <pre class=\"ansi-base16-railscasts-bright\"><code class=\"output\" id=\"mainOutput\"></code></pre>\n  </div>\n</div>\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/views/_settings.html",
    "content": "<h1>Settings</h1>\n\n<div class=\"row\">\n  <div class=\"input-field col s12\">\n    <input id=\"github\" name=\"settingsGithubToken\" type=\"text\">\n    <label for=\"github\">GitHub Access Token</label>\n  </div>\n  <div class=\"col s12\">\n    Create a <a href=\"https://github.com/settings/tokens\" target=\"_blank\">GitHub Access Token <svg viewBox=\"0 0 16 16\" class=\"octicon\"><use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#link-external-16\"></use></svg></a> in order to use the <i>share as gist</i> button.\n  </div>\n</div>\n\n<div class=\"row\">\n  <div class=\"input-field col s12\">\n    <div class=\"switch\">\n      <label>\n        Hide types\n        <input name=\"settingsShowTypes\" type=\"checkbox\">\n        <span class=\"lever\"></span>\n        Show types\n      </label>\n    </div>\n  </div>\n</div>\n\n<br>\n<br>\n\n<div class=\"row\">\n  <div class=\"input-field col s12\">\n    <input id=\"runDebounce\" name=\"settingsRunDebounce\" type=\"number\">\n    <label for=\"runDebounce\">Time to wait before running the code in milliseconds (leave blank for explicit runs only)</label>\n  </div>\n</div>\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/views/_workbook.html.ecr",
    "content": "<h1>Workbook</h1>\n\n<% if has_items %>\n  <div class=\"collection\">\n    <% items.each do |item| %>\n      <a class=\"collection-item\" href=\"<%= item.path %>\"><%= item.title %></a>\n    <% end %>\n  </div>\n<% else %>\n  <h2>Usage</h2>\n\n  <p>\n    This page lists <code>.md</code>, <code>.html</code>, and <code>.cr</code> files in <code>./playground</code> folder.\n  </p>\n\n  <p>\n    Simply place crystal code blocks in\n    <code>&lt;pre class=\"playground\">&lt;/pre></code>\n    tags. Or use <code>```playground</code> in <code>.md</code> files.\n  </p>\n\n  <p>For example, either</p>\n  <p><pre><code>&lt;pre class=\"playground\">\n  [0x679bff14d, 0x2a4, 0x5cd88d86, 0xbe0ed]\n    .map(&.to_s(36)).join(\" \")\n&lt;/pre></pre></code></p>\n\n  <p>or</p>\n\n  <p><pre><code>```playground\n  [0x679bff14d, 0x2a4, 0x5cd88d86, 0xbe0ed]\n    .map(&.to_s(36)).join(\" \")\n```</pre></code></p>\n\n  <p>becomes</p>\n\n  <pre class=\"playground\">\n  [0x679bff14d, 0x2a4, 0x5cd88d86, 0xbe0ed]\n    .map(&.to_s(36)).join(\" \")\n  </pre>\n<% end %>\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground/views/layout.html.ecr",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\">\n    <link rel=\"stylesheet\" href=\"/vendor/materialize-v0.97.5/css/materialize.min.css\">\n    <link rel=\"stylesheet\" href=\"/vendor/codemirror-5.38.0/lib/codemirror.css\">\n    <link rel=\"stylesheet\" href=\"/vendor/codemirror-5.38.0/theme/neat.css\">\n    <link rel=\"stylesheet\" href=\"/vendor/octicons-19.5.0/octicons.css\">\n    <link rel=\"stylesheet\" href=\"/vendor/ansi_up-1.3.0/theme.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"/application.css\">\n    <% styles.each do |path| %>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"<%= path %>\">\n    <% end %>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n\n    <title>Crystal Playground</title>\n  </head>\n  <body class=\"grey lighten-3 grey-text text-darken-2\">\n    <header>\n      <div class=\"navbar\">\n        <nav>\n          <div class=\"nav-wrapper white\">\n            <div class=\"col s12\">\n              <a href=\"/\" class=\"brand-logo\">\n                <img src=\"/icon.svg\" />\n              </a>\n              <ul class=\"right\">\n                <li><a href=\"/\">Playground</a></li>\n                <li><a href=\"/workbook\">Workbook</a></li>\n                <li><a href=\"/about\">About</a></li>\n                <li>\n                  <a href=\"/settings\">\n                    <svg viewBox=\"0 0 16 16\" class=\"octicon\">\n                      <use xlink:href=\"/vendor/octicons-19.5.0/octicons.svg#gear-16\"></use>\n                    </svg>\n                  </a>\n                </li>\n              </ul>\n            </div>\n          </div>\n        </nav>\n      </div>\n    </header>\n\n    <main>\n      <div class=\"container\">\n        <%= yield %>\n      </div>\n    </main>\n\n    <footer>\n      <div class=\"footer-copyright\">\n        <div class=\"center-align\">Happy Crystalling ♥</div>\n        <small class=\"center-align crystal-version grey-text text-lighten-1\"></small>\n      </div>\n    </footer>\n\n    <script type=\"text/javascript\" src=\"/vendor/jquery-2.2.1.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/vendor/materialize-v0.97.5/js/materialize.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/vendor/codemirror-5.38.0/lib/codemirror.js\"></script>\n    <script type=\"text/javascript\" src=\"/vendor/codemirror-5.38.0/addon/comment/comment.js\"></script>\n    <script type=\"text/javascript\" src=\"/vendor/codemirror-5.38.0/mode/crystal/crystal.js\"></script>\n    <script type=\"text/javascript\" src=\"/vendor/ansi_up-1.3.0/ansi_up.js\"></script>\n    <script type=\"text/javascript\" src=\"/environment.js\"></script>\n    <script type=\"text/javascript\" src=\"/settings.js\"></script>\n    <script type=\"text/javascript\" src=\"/session.js\"></script>\n    <script type=\"text/javascript\" src=\"/application.js\"></script>\n    <% scripts.each do |path| %>\n      <script type=\"text/javascript\" src=\"<%= path %>\"></script>\n    <% end %>\n  </body>\n</html>\n"
  },
  {
    "path": "src/compiler/crystal/tools/playground.cr",
    "content": "{% skip_file if flag?(:without_playground) %}\n\nrequire \"./playground/*\"\n"
  },
  {
    "path": "src/compiler/crystal/tools/print_hierarchy.cr",
    "content": "require \"set\"\nrequire \"colorize\"\nrequire \"../syntax/ast\"\n\nmodule Crystal\n  def self.print_hierarchy(program, io, exp, format)\n    case format\n    when \"text\"\n      TextHierarchyPrinter.new(program, io, exp).execute\n    when \"json\"\n      JSONHierarchyPrinter.new(program, io, exp).execute\n    else\n      raise \"Unknown hierarchy format: #{format}\"\n    end\n  end\n\n  abstract class HierarchyPrinter\n    abstract def print_all\n\n    @llvm_typer : LLVMTyper\n\n    def initialize(@program : Program, exp : String?)\n      @exp = exp ? Regex.new(exp) : nil\n      @targets = Set(Type).new\n      @llvm_typer = @program.llvm_typer\n    end\n\n    def execute\n      if exp = @exp\n        compute_targets(@program.types, exp, false)\n      end\n\n      print_all\n    end\n\n    def compute_targets(types : Array, exp, must_include = false)\n      outer_must_include = must_include\n      types.each do |type|\n        must_include |= compute_target type, exp, outer_must_include\n      end\n      must_include\n    end\n\n    def compute_targets(types : Hash, exp, must_include = false)\n      outer_must_include = must_include\n      types.each_value do |type|\n        must_include |= compute_target type, exp, outer_must_include\n      end\n      must_include\n    end\n\n    def compute_target(type : NonGenericClassType, exp, must_include)\n      if must_include || (type.full_name =~ exp)\n        @targets << type\n        must_include = true\n      end\n\n      compute_targets type.types, exp, false\n\n      subtypes = type.subclasses.reject(GenericClassInstanceType)\n      must_include |= compute_targets subtypes, exp, must_include\n      if must_include\n        @targets << type\n      end\n      must_include\n    end\n\n    def compute_target(type : GenericClassType, exp, must_include)\n      if must_include || (type.full_name =~ exp)\n        @targets << type\n        must_include = true\n      end\n\n      compute_targets type.types, exp, false\n      compute_targets type.instantiated_types, exp, must_include\n\n      subtypes = type.subclasses.reject(GenericClassInstanceType)\n      must_include |= compute_targets subtypes, exp, must_include\n      if must_include\n        @targets << type\n      end\n      must_include\n    end\n\n    def compute_target(type : GenericClassInstanceType, exp, must_include)\n      if must_include\n        @targets << type\n        must_include = true\n      end\n      must_include\n    end\n\n    def compute_target(type, exp, must_include)\n      false\n    end\n\n    def must_print?(type : NonGenericClassType | GenericClassType)\n      !@exp || @targets.includes?(type)\n    end\n\n    def must_print?(type)\n      false\n    end\n\n    def type_kind(type)\n      type.extern_union? ? \"union\" : type.struct? ? \"struct\" : \"class\"\n    end\n\n    def type_size(type)\n      return nil unless constant_type_size?(type)\n\n      if type.is_a?(GenericClassType)\n        # obtain a \"generic instance\" where all arguments are simply the unbound\n        # parameters themselves; if we are here, `LLVMTyper` should never be\n        # requesting the size of a `TypeParameter`\n        type_vars = type.type_vars.map { |type_var| type.type_parameter(type_var).as(TypeVar) }\n        type = type.instantiate(type_vars)\n      end\n\n      llvm_type =\n        case type\n        when PointerInstanceType, ProcInstanceType\n          @llvm_typer.llvm_type(type, wants_size: true)\n        when InstanceVarContainer\n          @llvm_typer.llvm_struct_type(type, wants_size: true)\n        else\n          @llvm_typer.llvm_type(type, wants_size: true)\n        end\n\n      @llvm_typer.size_of(llvm_type)\n    end\n\n    def ivar_size(ivar, extern)\n      return nil unless constant_ivar_size?(ivar.type)\n\n      llvm_type = if extern\n                    @llvm_typer.llvm_embedded_c_type(ivar.type, wants_size: true)\n                  else\n                    @llvm_typer.llvm_embedded_type(ivar.type, wants_size: true)\n                  end\n\n      @llvm_typer.size_of(llvm_type)\n    end\n\n    # Returns `true` if `type`'s size (`sizeof` for values, `instance_sizeof`\n    # for references) is a constant, in particular if it doesn't depend on\n    # `type`'s generic type parameters. `type` is never a generic instance.\n    def constant_type_size?(type)\n      case type\n      when GenericUnionType, StaticArrayType, TupleType, NamedTupleType\n        false\n      when PointerType, ProcType\n        true\n      when GenericClassType\n        type.all_instance_vars.each do |_, ivar|\n          return false unless ivar_type = ivar.type?\n          return false unless constant_ivar_size?(ivar_type)\n        end\n        true\n      else\n        true\n      end\n    end\n\n    # Returns `true` if `sizeof(type)` is a constant, in particular if it\n    # doesn't depend on `type`'s generic type parameters. Unlike\n    # `#constant_type_size?`, here `type` can be a generic instance, but not an\n    # uninstantiated generic.\n    def constant_ivar_size?(type)\n      return true unless type.unbound? || type.is_a?(GenericType)\n\n      case type\n      when GenericType\n        type_vars = type.type_vars.map { |type_var| type.type_parameter(type_var).as(TypeVar) }\n        constant_ivar_size?(type.instantiate(type_vars))\n      when TypeParameter\n        false\n      when MixedUnionType\n        type.union_types.all? { |t| constant_ivar_size?(t) }\n      when StaticArrayInstanceType\n        return false unless constant_ivar_size?(type.element_type)\n        case size_var = type.size\n        when NumberLiteral\n          true\n        when Var\n          return false unless size_var_type = size_var.type?\n          return false unless constant_ivar_size?(size_var_type)\n        else\n          false\n        end\n      when TupleInstanceType\n        type.tuple_types.all? { |t| constant_ivar_size?(t) }\n      when NamedTupleInstanceType\n        type.entries.all? { |entry| constant_ivar_size?(entry.type) }\n      when GenericModuleInstanceType\n        # TODO: verify\n        type.generic_type.each_instantiated_type do |instance|\n          instance.as(GenericModuleInstanceType).raw_including_types.try &.each do |including_type|\n            return false unless constant_ivar_size?(including_type)\n          end\n        end\n        true\n      when InstanceVarContainer\n        if type.struct?\n          type.all_instance_vars.each do |_, ivar|\n            return false unless ivar_type = ivar.type?\n            return false unless constant_ivar_size?(ivar_type)\n          end\n        end\n        true\n      else\n        true\n      end\n    end\n  end\n\n  class TextHierarchyPrinter < HierarchyPrinter\n    def initialize(program : Program, @io : IO, exp : String?)\n      super(program, exp)\n      @indents = [] of Bool\n    end\n\n    def print_all\n      with_color.light_gray.bold.surround(@io) do\n        print_type @program.object\n      end\n    end\n\n    def print_subtypes(types)\n      types = types.sort_by &.to_s\n      types.each_with_index do |type, i|\n        if i == types.size - 1\n          @indents[-1] = false\n        end\n        print_subtype type\n      end\n    end\n\n    def print_subtype(type)\n      return unless must_print? type\n\n      unless @indents.empty?\n        print_indent\n        @io << \"|\\n\"\n      end\n\n      print_type type\n    end\n\n    def print_type_name(type)\n      print_indent\n      @io << \"+\" unless @indents.empty?\n      @io << \"- \" << type_kind(type) << \" \" << type\n\n      if type_size = type_size(type)\n        with_color.light_gray.surround(@io) do\n          @io << \" (\" << type_size << \" bytes)\"\n        end\n      end\n      @io << '\\n'\n    end\n\n    def print_type(type : GenericClassType | NonGenericClassType | GenericClassInstanceType)\n      print_type_name type\n\n      subtypes = type.subclasses.select { |sub| must_print?(sub) }\n      print_instance_vars type, !subtypes.empty?\n\n      with_indent do\n        print_subtypes subtypes\n      end\n    end\n\n    def print_type(type)\n      # Nothing to do\n    end\n\n    def print_instance_vars(type, has_subtypes)\n      instance_vars = type.instance_vars\n      return if instance_vars.empty?\n\n      instance_vars = instance_vars.values\n      instance_var_types = {} of MetaTypeVar => {Type, UInt64?}\n      instance_vars.each do |ivar|\n        if ivar_type = ivar.type?\n          instance_var_types[ivar] = {ivar_type, ivar_size(ivar, type.extern?)}\n        end\n      end\n\n      max_name_size = instance_vars.max_of &.name.size\n\n      max_type_size = instance_var_types.max_of? { |_, (type, _)| type.to_s.size } || 0\n      max_bytes_size = instance_var_types.max_of? { |_, (_, size)| size.try(&.to_s.size) || 0 } || 0\n\n      instance_vars.each do |ivar|\n        print_indent\n        @io << (@indents.last ? \"|\" : \" \") << (has_subtypes ? \"  .   \" : \"      \")\n\n        with_color.light_gray.surround(@io) do\n          ivar.name.ljust(@io, max_name_size)\n          @io << \" : \"\n          if entry = instance_var_types[ivar]?\n            ivar_type, size = entry\n            if size\n              ivar_type.to_s.ljust(@io, max_type_size)\n              with_color.light_gray.surround(@io) do\n                @io << \" (\"\n                size.to_s.rjust(@io, max_bytes_size)\n                @io << \" bytes)\"\n              end\n            else\n              @io << ivar_type\n            end\n          else\n            @io << \"MISSING\".colorize.red.bright\n          end\n        end\n        @io << '\\n'\n      end\n    end\n\n    def print_indent\n      unless @indents.empty?\n        @io << \"  \"\n        0.upto(@indents.size - 2) do |i|\n          indent = @indents[i]\n          if indent\n            @io << \"|  \"\n          else\n            @io << \"   \"\n          end\n        end\n      end\n    end\n\n    def with_indent(&)\n      @indents.push true\n      yield\n      @indents.pop\n    end\n\n    def with_color\n      Colorize.with.toggle(@program.color?)\n    end\n  end\n\n  class JSONHierarchyPrinter < HierarchyPrinter\n    def initialize(program : Program, io : IO, exp : String?)\n      super(program, exp)\n      @json = JSON::Builder.new(io)\n    end\n\n    def print_all\n      @json.document do\n        @json.object do\n          print_type(@program.object)\n        end\n      end\n    end\n\n    def print_subtypes(types)\n      types = types.sort_by &.to_s\n\n      @json.field \"sub_types\" do\n        @json.array do\n          types.each do |type|\n            if must_print? type\n              @json.object do\n                print_type(type)\n              end\n            end\n          end\n        end\n      end\n    end\n\n    def print_type_name(type)\n      @json.field \"name\", type.to_s\n      @json.field \"kind\", type_kind(type)\n\n      if type_size = type_size(type)\n        @json.field \"size_in_bytes\", type_size\n      end\n    end\n\n    def print_type(type : GenericClassType | NonGenericClassType | GenericClassInstanceType)\n      print_type_name(type)\n      subtypes = type.subclasses.select { |sub| must_print?(sub) }\n\n      print_instance_vars(type, !subtypes.empty?)\n      print_subtypes(subtypes)\n    end\n\n    def print_type(type)\n      # Nothing to do\n    end\n\n    def print_instance_vars(type, has_subtypes)\n      instance_vars = type.instance_vars\n      return if instance_vars.empty?\n\n      instance_vars = instance_vars.values\n      @json.field \"instance_vars\" do\n        @json.array do\n          instance_vars.each do |instance_var|\n            if ivar_type = instance_var.type?\n              @json.object do\n                @json.field \"name\", instance_var.name.to_s\n                @json.field \"type\", ivar_type.to_s\n                if ivar_size = ivar_size(instance_var, type.extern?)\n                  @json.field \"size_in_bytes\", ivar_size\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/print_types_visitor.cr",
    "content": "require \"set\"\nrequire \"../syntax/ast\"\n\nmodule Crystal\n  def self.print_types(node)\n    node.accept PrintTypesVisitor.new\n  end\n\n  class PrintTypesVisitor < Visitor\n    @vars : Set(String)\n\n    def initialize\n      @vars = Set(String).new\n    end\n\n    def visit(node)\n      true\n    end\n\n    def visit(node : ClassDef)\n      false\n    end\n\n    def visit(node : Def)\n      false\n    end\n\n    def visit(node : FunDef)\n      false\n    end\n\n    def visit(node : Macro)\n      false\n    end\n\n    def visit(node : Assign)\n      !node.target.is_a?(Path)\n    end\n\n    def visit(node : Var)\n      output_name node\n      false\n    end\n\n    def visit(node : Global)\n      output_name node\n      false\n    end\n\n    def visit(node : TypeDeclaration)\n      var = node.var\n      if var.is_a?(Var)\n        output_name var\n      end\n      false\n    end\n\n    def visit(node : UninitializedVar)\n      var = node.var\n      if var.is_a?(Var)\n        output_name var\n      end\n      false\n    end\n\n    def output_name(node)\n      if !node.name.starts_with?('#') && !@vars.includes?(node.name)\n        puts \"#{node.name} : #{node.type?}\"\n        @vars.add node.name\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/table_print.cr",
    "content": "module Crystal\n  class TablePrint\n    COL_SEP     = '|'\n    CELL_MARGIN = ' '\n\n    struct Separator\n    end\n\n    enum Alignment\n      Left\n      Right\n      Center\n    end\n\n    class Column\n      def initialize\n        @max_size = 0\n      end\n\n      def width\n        @max_size\n      end\n\n      def will_render(cell)\n        @max_size = Math.max(@max_size, cell.text.size) if cell.colspan == 1\n      end\n\n      def render_cell(table, cell)\n        if cell.colspan == 1\n          available_width = width\n        else\n          available_width = table.columns.skip(cell.column_index).first(cell.colspan).sum(&.width) + 3 * (cell.colspan - 1)\n        end\n\n        case cell.align\n        in .left?\n          cell.text.ljust(available_width)\n        in .right?\n          cell.text.rjust(available_width)\n        in .center?\n          cell.text.center(available_width)\n        end\n      end\n    end\n\n    class Cell\n      property text : String\n      property align : Alignment\n      property colspan : Int32\n      property! column_index : Int32\n\n      def initialize(@text, @align : Alignment, @colspan)\n      end\n    end\n\n    alias RowTypes = Array(Cell) | Separator\n\n    property! last_string_row : Array(Cell)\n    property columns : Array(Column)\n\n    def initialize(@io : IO)\n      @data = [] of RowTypes\n      @columns = [] of Column\n    end\n\n    def build(&)\n      with self yield self\n      render\n    end\n\n    def separator\n      @data << Separator.new\n    end\n\n    def row(&)\n      @last_string_row = [] of Cell\n      @data << last_string_row\n      with self yield\n    end\n\n    def cell(text, align : Alignment = :left, colspan = 1)\n      cell = Cell.new(text, align, colspan)\n      last_string_row << cell\n      column_for_last_cell.will_render(cell)\n    end\n\n    def cell(align : Alignment = :left, colspan = 1, &)\n      cell(String::Builder.build { |io| yield io }, align, colspan)\n    end\n\n    protected def render\n      @data.each_with_index do |data_row, i|\n        @io << '\\n' if i != 0\n        if data_row.is_a?(Separator)\n          @io << \"-\" * (@columns.sum(&.width) + 1 + 3 * @columns.size)\n        elsif data_row.is_a?(Array(Cell))\n          column_index = 0\n          data_row.each_with_index do |cell, i|\n            cell.column_index = column_index\n\n            @io << COL_SEP if i == 0\n            @io << CELL_MARGIN << @columns[column_index].render_cell(self, cell) << CELL_MARGIN << COL_SEP\n\n            column_index += cell.colspan\n          end\n        end\n      end\n    end\n\n    protected def column_for_last_cell\n      col = @columns[last_string_row.size - 1]?\n      unless col\n        col = Column.new\n        @columns << col\n      end\n      col\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/typed_def_processor.cr",
    "content": "# `TypeDefProcessor` is a mixin to provide a visitor for typed defs\n# and some utilities.\n#\n# It is used for `crystal tool context/expand/implementation`.\nmodule Crystal::TypedDefProcessor\n  private def process_typed_def(typed_def : Def)\n    typed_def.accept self\n  end\n\n  private getter target_location\n\n  private def process_result(result : Compiler::Result)\n    process_type result.program\n    if (filename = target_location.original_filename) && (file_module = result.program.file_module?(filename))\n      process_type file_module\n    end\n  end\n\n  private def process_type(type : Type) : Nil\n    if type.is_a?(NamedType) || type.is_a?(Program) || type.is_a?(FileModule)\n      type.types?.try &.each_value do |inner_type|\n        process_type inner_type\n      end\n    end\n\n    if type.is_a?(GenericType)\n      type.each_instantiated_type do |instance|\n        process_type instance\n      end\n    end\n\n    process_type type.metaclass if type.metaclass != type\n\n    if type.is_a?(DefInstanceContainer)\n      type.def_instances.each_value do |typed_def|\n        process_typed_def typed_def\n      end\n    end\n  end\n\n  private def contains_target(node)\n    if loc_start = node.location\n      loc_end = node.end_location || loc_start\n      # if it is not between, it could be the case that node is the top level Expressions\n      # in which the (start) location might be in one file and the end location in another.\n      @target_location.between?(loc_start, loc_end) || loc_start.filename != loc_end.filename\n    else\n      # if node has no location, assume they may contain the target.\n      # for example with the main expressions ast node this matters\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/tools/unreachable.cr",
    "content": "require \"../syntax/ast\"\nrequire \"../compiler\"\nrequire \"json\"\nrequire \"csv\"\n\nmodule Crystal\n  class Command\n    private def unreachable\n      config, result = compile_no_codegen \"tool unreachable\", path_filter: true, unreachable_command: true, allowed_formats: %w[text json csv codecov]\n\n      unreachable = UnreachableVisitor.new\n\n      unreachable.includes.concat config.includes.map { |path| ::Path[path].expand.to_posix.to_s }\n\n      unreachable.excludes.concat CrystalPath.default_paths.map { |path| ::Path[path].expand.to_posix.to_s }\n      unreachable.excludes.concat config.excludes.map { |path| ::Path[path].expand.to_posix.to_s }\n\n      tallies = unreachable.process(result)\n      tallies.sort_by! do |a_def, _|\n        location = a_def.location.not_nil!\n        {\n          location.filename.as(String),\n          location.line_number,\n          location.column_number,\n        }\n      end\n\n      UnreachablePresenter.new(tallies, format: config.output_format, print_tallies: config.tallies).to_s(STDOUT)\n\n      if config.check\n        exit 1 if tallies.any?(&.[1].zero?)\n      end\n    end\n  end\n\n  record UnreachablePresenter, tallies : Array({Def, Int32}), format : String?, print_tallies : Bool do\n    include JSON::Serializable\n\n    def to_s(io)\n      case format\n      when \"json\"\n        to_json(STDOUT)\n      when \"csv\"\n        to_csv(STDOUT)\n      when \"codecov\"\n        to_codecov(STDOUT)\n      else\n        to_text(STDOUT)\n      end\n    end\n\n    def each(&)\n      current_dir = Dir.current\n      tallies.each do |a_def, count|\n        next unless print_tallies || count.zero?\n\n        location = a_def.location.not_nil!\n        filename = ::Path[location.filename.as(String)].relative_to(current_dir).to_s\n        location = Location.new(filename, location.line_number, location.column_number)\n        yield a_def, location, count\n      end\n    end\n\n    def to_text(io)\n      each do |a_def, location, count|\n        io << count << \"\\t\" if print_tallies\n        io << location << \"\\t\"\n        io << a_def.short_reference << \"\\t\"\n        io << a_def.length << \" lines\"\n        if annotations = a_def.all_annotations\n          io << \"\\t\"\n          annotations.join(io, \" \")\n        end\n        io.puts\n      end\n    end\n\n    def to_json(builder : JSON::Builder)\n      builder.array do\n        each do |a_def, location, count|\n          builder.object do\n            builder.field \"name\", a_def.short_reference\n            builder.field \"location\", location.to_s\n            if lines = a_def.length\n              builder.field \"lines\", lines\n            end\n            builder.field \"count\", count if print_tallies\n            if annotations = a_def.all_annotations\n              builder.field \"annotations\", annotations.map(&.to_s)\n            end\n          end\n        end\n      end\n    end\n\n    def to_csv(io)\n      CSV.build(io) do |builder|\n        builder.row do |row|\n          row << \"count\" if print_tallies\n          row.concat %w[name file line column length annotations]\n        end\n\n        each do |a_def, location, count|\n          builder.row do |row|\n            row << count if print_tallies\n            row << a_def.short_reference\n            row << location.filename\n            row << location.line_number\n            row << location.column_number\n            row << a_def.length\n            row << a_def.all_annotations.try(&.join(\" \"))\n          end\n        end\n      end\n    end\n\n    # https://docs.codecov.com/docs/codecov-custom-coverage-format\n    def to_codecov(io)\n      hits = Hash(String, Hash(Int32, Int32)).new { |hash, key| hash[key] = Hash(Int32, Int32).new(0) }\n\n      each do |a_def, location, count|\n        hits[location.filename][location.line_number] = count\n      end\n\n      JSON.build io do |builder|\n        builder.object do\n          builder.string \"coverage\"\n          builder.object do\n            hits.each do |filename, line_coverage|\n              builder.string filename\n              builder.object do\n                line_coverage.each do |line, count|\n                  builder.field line, count\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n  end\n\n  # This visitor walks the entire reachable code tree and collect locations\n  # of all defs that are a target of a call into `@used_def_locations`.\n  # The locations are filtered to only those that we're interested in per\n  # `@includes` and `@excludes`.\n  # Then it traverses all types and their defs and reports those that are not\n  # in `@used_def_locations` (and match the filter).\n  class UnreachableVisitor < Visitor\n    @used_def_locations = Hash(Location, Int32).new(0)\n    @tallies : Hash(Def, Int32) = Hash(Def, Int32).new.compare_by_identity\n    @visited : Set(ASTNode) = Set(ASTNode).new.compare_by_identity\n\n    property includes = [] of String\n    property excludes = [] of String\n\n    def process_type(type)\n      if type.is_a?(ModuleType)\n        track_unused_defs type\n      end\n\n      type.types?.try &.each_value do |inner_type|\n        process_type(inner_type)\n      end\n\n      process_type(type.metaclass) if type.metaclass != type\n    end\n\n    def process(result : Compiler::Result) : Array({Def, Int32})\n      @tallies.clear\n\n      result.node.accept(self)\n\n      process_type(result.program)\n\n      @tallies.to_a\n    end\n\n    def visit(node)\n      true\n    end\n\n    def visit(node : ExpandableNode)\n      return false unless @visited.add?(node)\n\n      if expanded = node.expanded\n        expanded.accept self\n      end\n\n      true\n    end\n\n    def visit(node : Call)\n      if expanded = node.expanded\n        expanded.accept(self)\n\n        return true\n      end\n\n      node.target_defs.try &.each do |a_def|\n        if (location = a_def.location)\n          @used_def_locations.update(location, &.+(1)) if interested_in(location)\n        end\n\n        if @visited.add?(a_def)\n          a_def.body.accept(self)\n        end\n      end\n\n      true\n    end\n\n    def visit(node : ClassDef)\n      node.resolved_type.instance_vars_initializers.try &.each do |initializer|\n        initializer.value.accept(self)\n      end\n\n      true\n    end\n\n    private def track_unused_defs(module_type : ModuleType)\n      module_type.defs.try &.each_value.each do |defs_with_meta|\n        defs_with_meta.each do |def_with_meta|\n          check_def(def_with_meta.def)\n        end\n      end\n    end\n\n    private def check_def(a_def : Def)\n      return if a_def.abstract?\n      return if a_def.autogenerated?\n      return unless interested_in(a_def.location)\n\n      previous = a_def.previous.try(&.def)\n\n      check_def(previous) if previous && !a_def.calls_previous_def?\n\n      tally = @used_def_locations[a_def.location]\n      @tallies[a_def] = tally\n\n      check_def(previous) if previous && a_def.calls_previous_def? && tally == 0\n    end\n\n    private def interested_in(location)\n      if filename = location.try(&.filename).as?(String)\n        match_path?(filename)\n      end\n    end\n\n    def match_path?(path)\n      paths = ::Path[path].parents << ::Path[path]\n\n      match_any_pattern?(includes, paths) || !match_any_pattern?(excludes, paths)\n    end\n\n    private def match_any_pattern?(patterns, paths)\n      patterns.any? { |pattern| paths.any? { |path| path == pattern || File.match?(pattern, path.to_posix) } }\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/types.cr",
    "content": "require \"./syntax/ast\"\n\nmodule Crystal\n  # Abstract base class of all types\n  abstract class Type\n    include Annotatable\n\n    # Returns the program where this type belongs.\n    getter program\n\n    def initialize(@program : Program)\n    end\n\n    # Returns any doc comments associated to this type.\n    def doc : String?\n      nil\n    end\n\n    # Returns all locations where this type is declared\n    def locations : Array(Location)?\n      nil\n    end\n\n    # An opaque id of every type. 0 for Nil, non zero for others, so we can\n    # sort types by opaque_id and have Nil in the beginning.\n    def opaque_id\n      self.is_a?(NilType) ? 0_u64 : object_id\n    end\n\n    # The namespace this type belongs to. Every type belongs to\n    # a namespace, and, when not explicit, the namespace is the `Program` itself.\n    def namespace : ModuleType\n      program\n    end\n\n    # Yields each namespace this type belongs to, excluding the `Program` itself.\n    def each_namespace(& : ModuleType ->) : Nil\n      ns = self.namespace\n      until ns == program\n        yield ns\n        ns = ns.namespace\n      end\n    end\n\n    # Returns `true` if this type is abstract.\n    def abstract?\n      false\n    end\n\n    # Returns `true` if this type is abstract and it has no\n    # concrete subclasses. This can happen if this type has abstract\n    # subclasses, or non-abstract generic subclasses without instantiations.\n    def abstract_leaf?\n      type = self.devirtualize\n\n      case type\n      when GenericType\n        instances = type.instantiated_types\n        (type.abstract? && instances.empty?) || instances.all?(&.abstract_leaf?)\n      when GenericClassInstanceType\n        type.abstract? && type.subclasses.all?(&.abstract_leaf?)\n      when ClassType\n        type.abstract? && type.subclasses.all?(&.abstract_leaf?)\n      else\n        false\n      end\n    end\n\n    # Returns `true` if this type is a struct.\n    def struct?\n      false\n    end\n\n    # Returns `true` if this is an extern C struct or union (`extern_union?` tells which one)\n    def extern?\n      false\n    end\n\n    # Returns `true` if this is an extern C union (`extern?` will be `true` too)\n    def extern_union?\n      false\n    end\n\n    # Returns `true` if this type has the `@[Packed]` annotation on it\n    # (only applicable for C structs)\n    def packed?\n      false\n    end\n\n    # Returns `true` if this type inherits from `Reference` or if this\n    # is a union type where all types are reference types or nil.\n    # In this case this type can be represented with a single pointer.\n    def reference_like?\n      case self\n      when NilType, NilableType, NilableReferenceUnionType, ReferenceUnionType\n        true\n      when NonGenericClassType\n        !self.struct?\n      when GenericClassType\n        !self.struct?\n      when GenericClassInstanceType\n        !self.struct?\n      when VirtualType\n        !self.struct?\n      else\n        false\n      end\n    end\n\n    # Returns the methods defined in this type, indexed by their name.\n    # This does not include methods defined in ancestors.\n    def defs : Hash(String, Array(DefWithMetadata))?\n      nil\n    end\n\n    # Returns all macros defines in this type, indexed by their name.\n    # This does not include methods defined in ancestors.\n    def macros : Hash(String, Array(Macro))?\n      nil\n    end\n\n    # Returns this type's metaclass, which holds class methods for this type.\n    getter metaclass : Type do\n      metaclass = MetaclassType.new(program, self)\n      initialize_metaclass(metaclass)\n      metaclass\n    end\n\n    # Initializes a metaclass.\n    # Some subtypes (classes) add an `allocate` method so a class can be instantiated.\n    protected def initialize_metaclass(metaclass)\n      # Nothing\n    end\n\n    # Returns `true` if this type can be assigned to an instance or class\n    # variable, or used in a generic type argument.\n    #\n    # As of now, abstract base type such as Object, Reference, Value,\n    # Int, and unbound generic types such as `Array(T)`, can't be stored.\n    def can_be_stored?\n      true\n    end\n\n    # Returns direct subclasses of this type.\n    def subclasses : Array(Type)\n      [] of Type\n    end\n\n    # Returns all subclasses of this type, including subclasses of\n    # subclasses recursively.\n    def all_subclasses\n      subclasses = [] of Type\n      append_subclasses(self, subclasses)\n      subclasses\n    end\n\n    private def append_subclasses(type, subclasses)\n      type.subclasses.each do |subclass|\n        subclasses << subclass\n        append_subclasses subclass, subclasses\n      end\n    end\n\n    # Returns `true` if this type has no subclasses.\n    def leaf?\n      subclasses.size == 0\n    end\n\n    def class?\n      false\n    end\n\n    def module?\n      false\n    end\n\n    def metaclass?\n      case self\n      when MetaclassType,\n           GenericClassInstanceMetaclassType, GenericModuleInstanceMetaclassType,\n           VirtualMetaclassType\n        true\n      else\n        false\n      end\n    end\n\n    def pointer?\n      self.is_a?(PointerInstanceType)\n    end\n\n    def nil_type?\n      self.is_a?(NilType)\n    end\n\n    def nilable?\n      nil_type? || program.nil_type.implements?(self)\n    end\n\n    def bool_type?\n      self.is_a?(BoolType)\n    end\n\n    def no_return?\n      self.is_a?(NoReturnType)\n    end\n\n    def virtual?\n      self.is_a?(VirtualType)\n    end\n\n    def virtual_metaclass?\n      self.is_a?(VirtualMetaclassType)\n    end\n\n    def proc?\n      self.is_a?(ProcInstanceType)\n    end\n\n    def void?\n      self.is_a?(VoidType)\n    end\n\n    def virtual_type\n      self\n    end\n\n    def virtual_type!\n      self\n    end\n\n    def instance_type\n      self\n    end\n\n    def generic_type\n      raise \"BUG: #{self} doesn't implement generic_type\"\n    end\n\n    def includes_type?(type)\n      self == type\n    end\n\n    def remove_typedef\n      self\n    end\n\n    # Returns the type that owns class vars for a type.\n    #\n    # This method returns self, but subclasses might override.\n    # For example, a metaclass' class_var_owner is the instance type.\n    def class_var_owner\n      self\n    end\n\n    def has_in_type_vars?(type)\n      false\n    end\n\n    # Should `new` be looked up in ancestors?\n    #\n    # This is `true` if this type doesn't define any\n    # `initialize` methods.\n    def lookup_new_in_ancestors?\n      false\n    end\n\n    # Returns the non-virtual type of a given type\n    # (returns self if self is already non-virtual)\n    def devirtualize\n      case self\n      when VirtualType\n        self.base_type\n      when VirtualMetaclassType\n        self.base_type.metaclass\n      else\n        self\n      end\n    end\n\n    def implements?(other_type : Type)\n      return true if self == other_type\n\n      other_type = other_type.remove_alias\n      case other_type\n      when UnionType\n        other_type.union_types.any? do |union_type|\n          implements?(union_type)\n        end\n      when VirtualType\n        implements?(other_type.base_type)\n      when VirtualMetaclassType\n        implements?(other_type.base_type.metaclass)\n      else\n        !!parents.try &.any? &.implements?(other_type)\n      end\n    end\n\n    def filter_by(other_type : Type)\n      Type.common_descendent(self, other_type)\n    end\n\n    def filter_by_responds_to(name)\n      nil\n    end\n\n    def add_instance_var_initializer(name, value, meta_vars)\n      raise \"BUG: #{self} doesn't implement add_instance_var_initializer\"\n    end\n\n    def declare_instance_var(name, type : Type, annotations = nil)\n      var = MetaTypeVar.new(name)\n      var.owner = self\n      var.type = type\n      var.annotations = annotations\n      var.bind_to var\n      var.freeze_type = type\n      instance_vars[name] = var\n    end\n\n    # Determines if `self` can access *type* assuming it's a `protected` access.\n    # If `allow_same_namespace` is true (the default), `protected` also means\n    # the types are in the same namespace. Otherwise, it means they are just\n    # in the same type hierarchy.\n    def has_protected_access_to?(type, allow_same_namespace = true)\n      owner = self.devirtualize\n      type = type.devirtualize\n\n      # Allow two different generic instantiations\n      # of the same type to have protected access\n      type = type.generic_type.as(Type) if type.is_a?(GenericInstanceType)\n      owner = owner.generic_type.as(Type) if owner.is_a?(GenericInstanceType)\n\n      owner.implements?(type) ||\n        type.implements?(owner) ||\n        (allow_same_namespace && owner.same_namespace?(type))\n    end\n\n    # Returns true if `self` and *other* are in the same namespace.\n    def same_namespace?(other)\n      top_namespace(self) == top_namespace(other) ||\n        !!parents.try &.any?(&.same_namespace?(other))\n    end\n\n    private def top_namespace(type)\n      type = type.generic_type if type.is_a?(GenericInstanceType)\n\n      namespace = case type\n                  when NamedType\n                    type.namespace\n                  when GenericClassInstanceType\n                    type.namespace\n                  else\n                    nil\n                  end\n      case namespace\n      when Program\n        type\n      when GenericInstanceType\n        top_namespace(namespace.generic_type)\n      when NamedType\n        top_namespace(namespace)\n      else\n        type\n      end\n    end\n\n    def types\n      raise \"BUG: #{self} has no types\"\n    end\n\n    def types?\n      nil\n    end\n\n    def lookup_name(name)\n      types?.try(&.[name]?)\n    end\n\n    def parents\n      nil\n    end\n\n    def ancestors\n      ancestors = [] of Type\n      collect_ancestors(ancestors)\n      ancestors\n    end\n\n    protected def collect_ancestors(ancestors)\n      parents.try &.each do |parent|\n        ancestors << parent\n        parent.collect_ancestors(ancestors)\n      end\n    end\n\n    # Returns this type's superclass, or `nil` if it doesn't have one\n    def superclass : Type?\n      nil\n    end\n\n    def lookup_defs(name : String, lookup_ancestors_for_new : Bool = false)\n      all_defs = [] of Def\n      lookup_defs(name, all_defs, lookup_ancestors_for_new)\n      all_defs\n    end\n\n    def lookup_defs(name : String, all_defs : Array(Def), lookup_ancestors_for_new : Bool? = false)\n      self.defs.try &.[name]?.try &.each do |item|\n        all_defs << item.def unless all_defs.find(&.same?(item.def))\n      end\n\n      is_new = name == \"new\"\n      is_new_or_initialize = is_new || name == \"initialize\"\n      return if is_new_or_initialize && !all_defs.empty?\n\n      if !is_new_or_initialize || (lookup_ancestors_for_new || self.lookup_new_in_ancestors?)\n        if is_new\n          # For a `new` method we need to do this in case a `new` is defined\n          # in a module type\n          my_parents = instance_type.parents.try &.map(&.metaclass)\n        else\n          my_parents = parents\n        end\n\n        my_parents.try &.each do |parent|\n          old_size = all_defs.size\n          parent.lookup_defs(name, all_defs, lookup_ancestors_for_new)\n\n          # Don't lookup new or initialize in parents once we found some defs\n          break if is_new_or_initialize && all_defs.size > old_size\n        end\n      end\n    end\n\n    def lookup_defs_without_parents(name : String)\n      all_defs = [] of Def\n      lookup_defs_without_parents(name, all_defs)\n      all_defs\n    end\n\n    def lookup_defs_without_parents(name : String, all_defs : Array(Def))\n      self.defs.try &.[name]?.try &.each do |item|\n        all_defs << item.def unless all_defs.find(&.same?(item.def))\n      end\n    end\n\n    def lookup_defs_with_modules(name)\n      if (list = self.defs.try &.[name]?) && !list.empty?\n        return list.map(&.def)\n      end\n\n      parents.try &.each do |parent|\n        next unless parent.module?\n\n        parent_defs = parent.lookup_defs_with_modules(name)\n        return parent_defs unless parent_defs.empty?\n      end\n\n      [] of Def\n    end\n\n    def lookup_first_def(name, block)\n      block = !!block\n      defs.try &.[name]?.try &.find(&.yields.==(block)).try &.def\n    end\n\n    def has_def?(name)\n      has_def_without_parents?(name) || !!parents.try(&.any?(&.has_def?(name)))\n    end\n\n    def has_def_without_parents?(name)\n      !!defs.try(&.has_key?(name))\n    end\n\n    record DefInMacroLookup\n\n    # Looks up a macro with the given name and matching the given args\n    # and named_args. Returns:\n    # - a `Macro`, if found\n    # - `nil`, if not found\n    # - `DefInMacroLookup` if not found and a Def was found instead\n    #\n    # In the case of `DefInMacroLookup`, it means that macros shouldn't\n    # be looked up in implicit enclosing scopes such as Object\n    # or the Program.\n    def lookup_macro(name, args : Array, named_args)\n      # Macros are always stored in a type's metaclass\n      macros_scope = self.metaclass? ? self : self.metaclass\n\n      if macros = macros_scope.macros.try &.[name]?\n        match = macros.find &.matches?(args, named_args)\n        return match if match\n      end\n\n      # First check if there are defs at this scope with that name.\n      # If so, make that a priority in the lookup and don't consider\n      # macro matches.\n      if has_def_without_parents?(name)\n        return DefInMacroLookup.new\n      end\n\n      # We need to go through the instance type because of module\n      # inclusion and inheritance.\n      instance_type.parents.try &.each do |parent|\n        # Make sure to start the search in the metaclass if we are a metaclass\n        parent = parent.metaclass if self.metaclass?\n        parent_macro = parent.lookup_macro(name, args, named_args)\n        return parent_macro if parent_macro\n      end\n\n      nil\n    end\n\n    # Looks up macros with the given name. Returns:\n    # - an Array of Macro if found\n    # - `nil` if not found\n    # - `DefInMacroLookup` if not found and some Defs were found instead\n    def lookup_macros(name)\n      # Macros are always stored in a type's metaclass\n      macros_scope = self.metaclass? ? self : self.metaclass\n\n      if macros = macros_scope.macros.try &.[name]?\n        return macros\n      end\n\n      if has_def_without_parents?(name)\n        return DefInMacroLookup.new\n      end\n\n      # We need to go through the instance type because of module\n      # inclusion and inheritance.\n      instance_type.parents.try &.each do |parent|\n        # Make sure to start the search in the metaclass if we are a metaclass\n        parent = parent.metaclass if self.metaclass?\n        parent_macros = parent.lookup_macros(name)\n        return parent_macros if parent_macros\n      end\n\n      nil\n    end\n\n    def add_including_type(mod)\n      raise \"BUG: #{self} doesn't implement add_including_type\"\n    end\n\n    def including_types\n      raise \"BUG: #{self} doesn't implement including_types\"\n    end\n\n    # Returns `true` if this type can have instance vars.\n    # Primitive types, and types like Reference and Object,\n    # can't have instance vars.\n    def allows_instance_vars?\n      case self\n      when program.object, program.value, program.struct,\n           program.reference, program.class_type,\n           program.number, program.int, program.float,\n           program.tuple, program.named_tuple,\n           program.pointer, program.static_array,\n           program.union, program.enum, program.proc,\n           PrimitiveType, GenericReferenceStorageType\n        false\n      else\n        true\n      end\n    end\n\n    def instance_vars\n      raise \"BUG: #{self} doesn't implement instance_vars\"\n    end\n\n    def class_vars\n      raise \"BUG: #{self} doesn't implement class_vars\"\n    end\n\n    def all_instance_vars\n      if superclass = self.superclass\n        superclass.all_instance_vars.merge(instance_vars)\n      else\n        instance_vars\n      end\n    end\n\n    def all_class_vars\n      all_class_vars = {} of String => MetaTypeVar\n      all_class_vars.merge!(class_vars)\n      parents.try &.each do |parent|\n        all_class_vars.merge!(parent.all_class_vars)\n      end\n      all_class_vars\n    end\n\n    def index_of_instance_var(name)\n      if superclass = self.superclass\n        index = superclass.index_of_instance_var(name)\n        if index\n          index\n        else\n          index = instance_vars.index { |k, v| k == name }\n          if index\n            superclass.all_instance_vars_count + index\n          else\n            nil\n          end\n        end\n      else\n        instance_vars.index { |k, v| k == name }\n      end\n    end\n\n    def lookup_instance_var(name : String)\n      lookup_instance_var?(name).not_nil!\n    end\n\n    def lookup_instance_var?(name : String)\n      superclass.try(&.lookup_instance_var?(name)) ||\n        instance_vars[name]?\n    end\n\n    def lookup_class_var?(name)\n      nil\n    end\n\n    def lookup_class_var(name)\n      raise \"BUG: #{self} doesn't implement lookup_class_var\"\n    end\n\n    def has_instance_var_initializer?(name)\n      false\n    end\n\n    def all_instance_vars_count\n      (superclass.try(&.all_instance_vars_count) || 0) + instance_vars.size\n    end\n\n    def lookup_similar_instance_var_name(ivar_name : String)\n      case self\n      when NonGenericModuleType, GenericClassType, GenericModuleType\n        nil\n      else\n        Levenshtein.find(ivar_name) do |finder|\n          self.all_instance_vars.each_key do |name|\n            finder.test(name)\n          end\n        end\n      end\n    end\n\n    def lookup_instance_var(node : InstanceVar | ReadInstanceVar | MetaVar)\n      case self\n      when Program\n        node.raise \"can't use instance variables at the top level\"\n      when PrimitiveType\n        node.raise \"can't use instance variables inside primitive types (at #{self})\"\n      when EnumType\n        node.raise \"can't use instance variables inside enums (at enum #{self})\"\n      when .metaclass?\n        node.raise \"@instance_vars are not yet allowed in metaclasses: use @@class_vars instead\"\n      when InstanceVarContainer\n        lookup_instance_var?(node.name)\n      else\n        node.raise \"BUG: #{self} is not an InstanceVarContainer\"\n      end\n    end\n\n    def add_subclass(subclass)\n      raise \"BUG: #{self} doesn't implement add_subclass\"\n    end\n\n    # Replace type parameters in this type with the type parameters\n    # of the given *instance* type.\n    def replace_type_parameters(instance) : Type\n      self\n    end\n\n    def depth\n      0\n    end\n\n    def type_desc\n      to_s\n    end\n\n    def remove_alias\n      self\n    end\n\n    def remove_alias_if_simple\n      self\n    end\n\n    def remove_indirection\n      self\n    end\n\n    def remove_literal\n      self\n    end\n\n    def generic_nest\n      0\n    end\n\n    def double_variadic?\n      false\n    end\n\n    def splat_index\n      nil\n    end\n\n    def type_vars\n      raise \"BUG: #{self} doesn't implement type_vars\"\n    end\n\n    def unbound?\n      false\n    end\n\n    def private?\n      false\n    end\n\n    def private=(set_private)\n    end\n\n    # Returns true if *name* if an unbound type variable in this (generic) type.\n    def type_var?(name)\n      false\n    end\n\n    # Returns the type that has to be used in sizeof and instance_sizeof computations\n    def sizeof_type\n      if struct?\n        # In the case of an abstract struct we want to consider the union type\n        # of all subtypes (if it's not abstract it's concrete and this will return self)\n        virtual_type.remove_indirection\n      else\n        devirtualize\n      end\n    end\n\n    def get_instance_var_initializer(name)\n      nil\n    end\n\n    # Checks whether an exception needs to be raised because of a restriction\n    # failure. Only overwritten by literal types (NumberAutocastType and\n    # SymbolAutocastType) when they produce an ambiguous call.\n    def check_restriction_exception\n      nil\n    end\n\n    # Yields self and returns true if the block returns a truthy value.\n    # UnionType overrides it and yields all types in turn and returns\n    # true if for each of them the block returns true.\n    def all?(&)\n      (yield self) ? true : false\n    end\n\n    def to_s(*, generic_args : Bool = true)\n      String.build do |io|\n        to_s_with_options io, generic_args: generic_args\n      end\n    end\n\n    def inspect(io : IO) : Nil\n      to_s(io)\n    end\n\n    def to_s(io : IO) : Nil\n      to_s_with_options(io)\n    end\n\n    abstract def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n\n    def pretty_print(pp)\n      pp.text to_s\n    end\n  end\n\n  # A type that has a name and can be inside a namespace.\n  # For example, given `class Foo::Bar`, `Foo` is the namespace and `Bar` is the name.\n  #\n  # There are other types that have a name but it can be deduced from other(s) type(s),\n  # so they don't inherit NamedType: a union type, a metaclass, etc.\n  abstract class NamedType < Type\n    getter namespace : ModuleType\n    getter name : String\n    getter locations : Array(Location)?\n    property doc : String?\n    property? private : Bool = false\n\n    def initialize(program, @namespace, @name)\n      super(program)\n    end\n\n    # Adds a location to this type.\n    def add_location(location : Location)\n      locations = @locations ||= [] of Location\n      locations << location\n    end\n\n    getter(types) { {} of String => NamedType }\n\n    def types?\n      @types\n    end\n\n    def append_full_name(io : IO, codegen : Bool = false) : Nil\n      case namespace\n      when Program\n        # Skip\n      when FileModule\n        # For codegen we need the filename to distinguish it from other\n        # types, but in macros we can't use that because it won't parse\n        if codegen\n          namespace.to_s_with_options(io, generic_args: false, codegen: codegen)\n          io << \"::\"\n        end\n      else\n        namespace.to_s_with_options(io, generic_args: false, codegen: codegen)\n        io << \"::\"\n      end\n      io << @name\n    end\n\n    def full_name\n      String.build { |io| append_full_name(io) }\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      append_full_name(io, codegen: codegen)\n    end\n  end\n\n  # A Def with some metadata to speed up matching it against\n  # a call signature, or against other defs:\n  # - max_size: the maximum number of arguments that can be passed to the method\n  # - min_size: the minimum number of arguments that can be passed to the method\n  # - yields: whether the method has a block\n  record DefWithMetadata,\n    min_size : Int32,\n    max_size : Int32,\n    yields : Bool,\n    def : Def do\n    def self.new(a_def : Def)\n      min_size, max_size = a_def.min_max_args_sizes\n      new min_size, max_size, !!a_def.block_arity, a_def\n    end\n  end\n\n  # Kinds of macro hooks (`method_missing` and `finished` are handled separately)\n  enum HookKind\n    Inherited\n    Included\n    Extended\n    MethodAdded\n  end\n\n  # A macro hook\n  record Hook, kind : HookKind, macro : Macro\n\n  # The key by which instantiated methods are cached.\n  #\n  # For example, given:\n  #\n  # ```\n  # def foo(x, y) # def object id: 1234\n  #   x + y\n  # end\n  #\n  # foo(1, 2.5)\n  # ```\n  #\n  # When `foo(1)` is analyzed the argument types are [Int32, Float64],\n  # and so we instantiate the method with those types (in this case there's\n  # no block type nor named argument types). We remember this instantiation\n  # with a key that includes the def's object id, argument types, etc., so\n  # when a call with the same target Def, argument types, etc., is found\n  # we don't need to re-analyze it.\n  record DefInstanceKey,\n    def_object_id : UInt64,\n    arg_types : Array(Type),\n    block_type : Type?,\n    named_args : Array(NamedArgumentType)?\n\n  # A type that holds Def instantiations (defs where the argument types\n  # are fixed). Some types don't support having def instances, for example\n  # union types.\n  module DefInstanceContainer\n    getter(def_instances) { {} of DefInstanceKey => Def }\n\n    def add_def_instance(key, typed_def)\n      def_instances[key] = typed_def\n    end\n\n    def lookup_def_instance(key)\n      def_instances[key]?\n    end\n  end\n\n  # Base type for all module-like types (modules, classes, structs, enums).\n  abstract class ModuleType < NamedType\n    getter defs : Hash(String, Array(DefWithMetadata))?\n    getter macros : Hash(String, Array(Macro))?\n    getter hooks : Array(Hook)?\n    getter(parents) { [] of Type }\n\n    def add_def(a_def)\n      a_def.owner = self\n\n      item = DefWithMetadata.new(a_def)\n\n      defs = (@defs ||= {} of String => Array(DefWithMetadata))\n      list = defs[a_def.name] ||= [] of DefWithMetadata\n      list.each_with_index do |ex_item, i|\n        case item.compare_strictness(ex_item, self)\n        when nil\n          # Incompatible defs; do nothing\n        when 0\n          # The two defs have the same signature so item overrides ex_item.\n          list[i] = item\n          a_def.previous = ex_item\n          a_def.doc ||= ex_item.def.doc\n          ex_item.def.next = a_def\n          return ex_item.def\n        when .< 0\n          # item has a new signature, stricter than ex_item.\n          list.insert(i, item)\n          return nil\n        end\n      end\n\n      # item has a new signature, less strict than the existing defs with same name.\n      list << item\n\n      nil\n    end\n\n    def add_macro(a_macro)\n      a_macro.owner = self\n\n      case a_macro.name\n      when \"inherited\"\n        return add_hook :inherited, a_macro\n      when \"included\"\n        return add_hook :included, a_macro\n      when \"extended\"\n        return add_hook :extended, a_macro\n      when \"method_added\"\n        return add_hook :method_added, a_macro, args_size: 1\n      when \"method_missing\"\n        check_macro_param_count(a_macro, 1)\n      else\n        # normal macro\n      end\n\n      macros = (@macros ||= {} of String => Array(Macro))\n      array = (macros[a_macro.name] ||= [] of Macro)\n      index = array.index { |existing_macro| a_macro.overrides?(existing_macro) }\n      if index\n        # a_macro has the same signature of an existing macro, we override it.\n        a_macro.doc ||= array[index].doc\n        array[index] = a_macro\n      else\n        # a_macro has a new signature, add it with the others.\n        array << a_macro\n      end\n    end\n\n    def add_hook(kind : HookKind, a_macro, args_size = 0)\n      check_macro_param_count(a_macro, args_size)\n      hooks = @hooks ||= [] of Hook\n      hooks << Hook.new(kind, a_macro)\n    end\n\n    private def check_macro_param_count(a_macro, expected_count)\n      param_count = a_macro.args.size\n      if param_count != expected_count\n        raise TypeException.new \"wrong number of parameters for macro '#{a_macro.name}' (given #{param_count}, expected #{expected_count})\"\n      end\n    end\n\n    def filter_by_responds_to(name)\n      has_def?(name) ? self : nil\n    end\n\n    def include(mod)\n      generic_module = mod.is_a?(GenericModuleInstanceType) ? mod.generic_type : mod\n\n      if generic_module == self\n        raise TypeException.new \"cyclic include detected\"\n      end\n\n      generic_module.ancestors.each do |ancestor|\n        if ancestor == self || ancestor.is_a?(GenericModuleInstanceType) && ancestor.generic_type == self\n          raise TypeException.new \"cyclic include detected\"\n        end\n      end\n\n      unless parents.includes?(mod)\n        parents.insert 0, mod\n        mod.add_including_type(self)\n      end\n    end\n\n    def type_desc\n      \"module\"\n    end\n  end\n\n  # A type that can have class variables.\n  module ClassVarContainer\n    getter(class_vars) { {} of String => MetaTypeVar }\n\n    def class_vars?\n      @class_vars\n    end\n\n    def lookup_class_var(name)\n      lookup_class_var?(name).not_nil!\n    end\n\n    def lookup_class_var?(name)\n      class_var = @class_vars.try &.[name]?\n      return class_var if class_var\n\n      ancestors.each do |ancestor|\n        next unless ancestor.is_a?(ClassVarContainer)\n\n        class_var = ancestor.lookup_class_var?(name)\n        if class_var\n          var = MetaTypeVar.new(name, class_var.type)\n          var.owner = self\n          var.thread_local = class_var.thread_local?\n          var.initializer = class_var.initializer\n          var.bind_to(class_var)\n          self.class_vars[name] = var\n          return var\n        end\n      end\n\n      nil\n    end\n  end\n\n  # Temporary type to recompute calls when a subclass is added to a type\n  # hierarchy. This shouldn't be needed, because the type hierarchy is\n  # now computed in a first pass, but for generic types, instantiations\n  # are considered as kind of subclasses, and calls must be recomputed\n  # to take them into account (but this should change in the future).\n  module SubclassObservable\n    def add_subclass_observer(observer)\n      observers = (@subclass_observers ||= [] of Call)\n      observers << observer\n    end\n\n    def remove_subclass_observer(observer)\n      @subclass_observers.try &.reject! &.same?(observer)\n    end\n\n    def notify_subclass_added\n      @subclass_observers.try &.dup.each &.on_new_subclass\n    end\n  end\n\n  # A type that can have instance var initializers, like\n  #\n  # ```\n  # @x = 1\n  # ```\n  module InstanceVarInitializerContainer\n    class InstanceVarInitializer\n      getter name : String\n      property value : ASTNode\n      getter meta_vars : MetaVars\n\n      def initialize(@name, @value, @meta_vars)\n      end\n    end\n\n    getter instance_vars_initializers : Array(InstanceVarInitializer)?\n\n    def add_instance_var_initializer(name, value, meta_vars)\n      initializers = @instance_vars_initializers ||= [] of InstanceVarInitializer\n\n      # No meta vars means this initializer came from a generic type,\n      # so we must type it now that we are defining it in a concrete type\n      if !meta_vars && !self.is_a?(GenericType)\n        meta_vars = MetaVars.new\n        visitor = MainVisitor.new(program, vars: meta_vars, meta_vars: meta_vars)\n        visitor.scope = self.metaclass\n        value = value.clone\n        value.accept visitor\n      end\n\n      meta_vars ||= MetaVars.new\n\n      unless self.is_a?(GenericType)\n        instance_var = lookup_instance_var(name)\n        instance_var.bind_to(value)\n      end\n\n      initializer = InstanceVarInitializer.new(name, value, meta_vars)\n      initializers << initializer\n\n      program.after_inference_types << self\n\n      initializer\n    end\n\n    def has_instance_var_initializer?(name)\n      @instance_vars_initializers.try(&.any? { |init| init.name == name }) ||\n        ancestors.any?(&.has_instance_var_initializer?(name))\n    end\n\n    def get_instance_var_initializer(name)\n      match = @instance_vars_initializers.try &.find do |init|\n        init.name == name\n      end\n      return match if match\n\n      ancestors.each do |ancestor|\n        match = ancestor.get_instance_var_initializer(name)\n        return match if match\n      end\n\n      nil\n    end\n  end\n\n  # A type that can have instance variables.\n  module InstanceVarContainer\n    getter(instance_vars) { {} of String => MetaTypeVar }\n  end\n\n  # A non generic module type.\n  class NonGenericModuleType < ModuleType\n    include InstanceVarContainer\n    include ClassVarContainer\n    include SubclassObservable\n\n    def add_including_type(type)\n      return if type.unbound?\n\n      including_types = @including_types ||= [] of Type\n      including_types.push type\n\n      notify_subclass_added\n    end\n\n    def including_types\n      if including_types = @including_types\n        all_types = Array(Type).new(including_types.size)\n        add_to_including_types(all_types)\n        program.type_merge_union_of(all_types)\n      else\n        nil\n      end\n    end\n\n    def add_to_including_types(all_types)\n      if including_types = @including_types\n        including_types.each do |including_type|\n          add_to_including_types(including_type, all_types)\n        end\n      end\n    end\n\n    def raw_including_types\n      @including_types\n    end\n\n    def remove_indirection\n      if including_types = self.including_types\n        including_types.remove_indirection\n      else\n        self\n      end\n    end\n\n    def filter_by_responds_to(name)\n      including_types.try &.filter_by_responds_to(name)\n    end\n\n    def module?\n      true\n    end\n\n    def add_instance_var_initializer(name, value, meta_vars)\n      add_instance_var_initializer @including_types, name, value, meta_vars\n    end\n  end\n\n  # A module that is related to a file and contains its private defs.\n  class FileModule < NonGenericModuleType\n    include DefInstanceContainer\n\n    getter(vars) { MetaVars.new }\n\n    def vars?\n      @vars\n    end\n\n    def metaclass?\n      true\n    end\n\n    def metaclass\n      self\n    end\n  end\n\n  # Abstract base type for classes and structs\n  # (types that can be allocated via the `allocate` method).\n  abstract class ClassType < ModuleType\n    include DefInstanceContainer\n    include SubclassObservable\n    include InstanceVarInitializerContainer\n\n    setter metaclass : Type?\n    getter superclass : Type?\n    getter subclasses = [] of Type\n    getter depth : Int32\n    property? :abstract; @abstract = false\n    property? :struct; @struct = false\n    property? can_be_stored = true\n    property? lookup_new_in_ancestors = false\n\n    property? extern = false\n    property? extern_union = false\n    property? packed = false\n\n    def initialize(program, namespace, name, @superclass, add_subclass = true)\n      super(program, namespace, name)\n      @depth = superclass ? (superclass.depth + 1) : 0\n      parents.push superclass if superclass\n      force_add_subclass if add_subclass\n    end\n\n    def superclass=(@superclass)\n      @depth = superclass ? (superclass.depth + 1) : 0\n      parents.push superclass if superclass\n    end\n\n    def add_subclass(subclass)\n      return if subclass.unbound?\n\n      subclasses << subclass\n      notify_subclass_added\n\n      superclass = superclass()\n      while superclass.is_a?(SubclassObservable)\n        superclass.notify_subclass_added\n        superclass = superclass.superclass\n      end\n    end\n\n    def force_add_subclass\n      superclass.try &.add_subclass(self)\n    end\n\n    def type_desc\n      case\n      when extern? && extern_union?\n        \"union\"\n      when struct?\n        \"struct\"\n      else\n        \"class\"\n      end\n    end\n  end\n\n  # A non-generic class type, like String.\n  class NonGenericClassType < ClassType\n    include InstanceVarContainer\n    include ClassVarContainer\n\n    protected def initialize_metaclass(metaclass)\n      metaclass.add_def Def.new(\"allocate\", body: Primitive.new(\"allocate\"))\n    end\n\n    def virtual_type\n      if leaf? && !abstract?\n        self\n      elsif struct? && abstract? && !leaf?\n        virtual_type!\n      elsif struct?\n        self\n      else\n        virtual_type!\n      end\n    end\n\n    def virtual_type!\n      @virtual_type ||= VirtualType.new(program, self)\n    end\n\n    def class?\n      true\n    end\n  end\n\n  # Base type for primitive types like Bool and Char.\n  abstract class PrimitiveType < ClassType\n    include ClassVarContainer\n\n    # Returns the number of bytes this type occupies in memory.\n    getter bytes : Int32\n\n    def initialize(program, namespace, name, superclass, @bytes : Int32)\n      super(program, namespace, name, superclass)\n      self.struct = true\n    end\n\n    def abstract?\n      false\n    end\n  end\n\n  class BoolType < PrimitiveType\n  end\n\n  class CharType < PrimitiveType\n  end\n\n  class IntegerType < PrimitiveType\n    getter rank : Int32\n    getter kind : NumberKind\n\n    def initialize(program, namespace, name, superclass, bytes, @rank, @kind : NumberKind)\n      super(program, namespace, name, superclass, bytes)\n    end\n\n    def signed?\n      @rank % 2 == 1\n    end\n\n    def unsigned?\n      @rank % 2 == 0\n    end\n\n    def bits\n      8 * (2 ** normal_rank)\n    end\n\n    def normal_rank\n      (@rank - 1) // 2\n    end\n\n    def range\n      case kind\n      when .i8?\n        {Int8::MIN, Int8::MAX}\n      when .i16?\n        {Int16::MIN, Int16::MAX}\n      when .i32?\n        {Int32::MIN, Int32::MAX}\n      when .i64?\n        {Int64::MIN, Int64::MAX}\n      when .i128?\n        {Int128::MIN, Int128::MAX}\n      when .u8?\n        {UInt8::MIN, UInt8::MAX}\n      when .u16?\n        {UInt16::MIN, UInt16::MAX}\n      when .u32?\n        {UInt32::MIN, UInt32::MAX}\n      when .u64?\n        {UInt64::MIN, UInt64::MAX}\n      when .u128?\n        {UInt128::MIN, UInt128::MAX}\n      else\n        raise \"Bug: called 'range' for non-integer literal\"\n      end\n    end\n\n    # Returns true if every _finite_ member of this type is also exactly\n    # representable in the *other_type*. Used to define legal autocasts of\n    # number-typed variables\n    def subset_of?(other_type : IntegerType) : Bool\n      self_min, self_max = self.range\n      other_min, other_max = other_type.range\n      other_min <= self_min && self_max <= other_max\n    end\n\n    # :ditto:\n    def subset_of?(other_type : FloatType) : Bool\n      # Float32 mantissa has 23 bits,\n      # Float64 mantissa has 52 bits\n      case kind\n      when .i8?, .u8?, .i16?, .u16?\n        # Less than 23 bits, so convertible to Float32 and Float64 without precision loss\n        true\n      when .i32?, .u32?\n        # Less than 52 bits, so convertible to Float64 without precision loss\n        other_type.kind.f64?\n      else\n        false\n      end\n    end\n  end\n\n  class FloatType < PrimitiveType\n    getter rank : Int32\n\n    def initialize(program, namespace, name, superclass, bytes, @rank)\n      super(program, namespace, name, superclass, bytes)\n    end\n\n    def kind\n      @bytes == 4 ? NumberKind::F32 : NumberKind::F64\n    end\n\n    def range\n      case kind\n      when .f32?\n        {Float32::MIN, Float32::MAX}\n      when .f64?\n        {Float64::MIN, Float64::MAX}\n      else\n        raise \"Bug: called 'range' for non-float literal\"\n      end\n    end\n\n    # Returns true if every _finite_ member of this type is also exactly\n    # representable in the *other_type*. Used to define legal autocasts of\n    # number-typed variables.\n    def subset_of?(other_type : IntegerType) : Bool\n      false\n    end\n\n    # :ditto:\n    def subset_of?(other_type : FloatType) : Bool\n      kind.f32? && other_type.kind.f64?\n    end\n  end\n\n  class SymbolType < PrimitiveType\n  end\n\n  class NilType < PrimitiveType\n  end\n\n  class NoReturnType < NamedType\n    # NoReturn can be assigned to any other type (because it never will)\n    def implements?(other_type)\n      true\n    end\n  end\n\n  class VoidType < NamedType\n  end\n\n  abstract class AutocastType < Type\n    # The most exact match type, or the first match otherwise\n    getter match : Type?\n\n    # All matches. It's nil if `@match` is an exact match.\n    @all_matches : Set(Type)?\n\n    def set_exact_match(type)\n      @match = type\n      @all_matches = nil\n    end\n\n    def add_match(type)\n      if (match = @match) && match != type\n        all_matches = @all_matches\n        if all_matches.nil?\n          all_matches = @all_matches = Set(Type).new\n          all_matches << match\n        end\n        all_matches << type\n      else\n        @match = type\n      end\n    end\n\n    def exact_match?\n      literal.type == @match\n    end\n\n    def remove_literal\n      literal.type\n    end\n\n    def check_restriction_exception\n      if all_matches = @all_matches\n        literal_value =\n          case literal\n          when NumberLiteral, SymbolLiteral\n            literal.to_s\n          else\n            literal.type.to_s\n          end\n        literal.raise \"ambiguous call, implicit cast of #{literal_value} matches all of #{all_matches.join(\", \")}\"\n      end\n    end\n  end\n\n  # Type for a number literal: it has the specific type of the number literal\n  # but can also match other types (like ints and floats) if the literal\n  # fits in those types.\n  class NumberAutocastType < AutocastType\n    # The literal associated with this type\n    getter literal : ASTNode\n\n    def initialize(program, @literal)\n      super(program)\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << @literal.type\n    end\n  end\n\n  # Type for a symbol literal: it has the specific type of the symbol literal (SymbolType)\n  # but can also match enums if their members match the symbol's name.\n  class SymbolAutocastType < AutocastType\n    # The literal associated with this type\n    getter literal : SymbolLiteral\n\n    def initialize(program, @literal)\n      super(program)\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << @literal.type\n    end\n  end\n\n  # Any thing that can be passed as a generic type variable.\n  #\n  # For example, in:\n  #\n  # ```\n  # StaticArray(UInt8, 256)\n  # ```\n  #\n  # there are two type vars: UInt8 (the type) and 256 (a number literal).\n  #\n  # These are the only things that are currently accepted as type variables,\n  # but this is kept as `Type | ASTNode` to make it easier to add new nodes\n  # in the future.\n  alias TypeVar = Type | ASTNode\n\n  # A non-instantiated generic type, like Array(T), Hash(K, V)\n  # or Enumerable(T).\n  module GenericType\n    include InstanceVarContainer\n\n    # The type variable names (K and V in Hash).\n    getter type_vars : Array(String)\n\n    # The index of the `*` in the type variables.\n    property splat_index : Int32?\n\n    # Is it `**`? Currently only NamedTuple is.\n    property? double_variadic = false\n\n    # All generic type instantiations of this generic type, indexed\n    # by the type variables.\n    protected getter(generic_types) { {} of Array(TypeVar) => Type }\n\n    # Returns an array of all instantiations of this generic type.\n    # NamedTupleType overrides this because it doesn't use positional type\n    # arguments.\n    def instantiated_types\n      generic_types.values\n    end\n\n    def each_instantiated_type(&)\n      if types = @generic_types\n        types.each_value { |type| yield type }\n      end\n    end\n\n    # Returns a TypeParameter relative to this type\n    def type_parameter(name) : TypeParameter\n      type_parameters = @type_parameters ||= {} of String => TypeParameter\n      type_parameters[name] ||= TypeParameter.new(program, self, name)\n    end\n\n    def instantiate(type_vars)\n      if (instance = generic_types[type_vars]?)\n        return instance\n      end\n\n      instance_type_vars = {} of String => ASTNode\n      type_var_index = 0\n      self.type_vars.each_with_index do |name, index|\n        if splat_index == index\n          types = [] of TypeVar\n          (type_vars.size - (self.type_vars.size - 1)).times do\n            types << type_vars[type_var_index]\n            type_var_index += 1\n          end\n          var = Var.new(name, program.tuple_of(types))\n          var.bind_to(var)\n          instance_type_vars[name] = var\n        else\n          type_var = type_vars[type_var_index]\n          case type_var\n          when Type\n            var = Var.new(name, type_var)\n            var.bind_to var\n            instance_type_vars[name] = var\n          when ASTNode\n            instance_type_vars[name] = type_var\n          end\n          type_var_index += 1\n        end\n      end\n\n      instance = self.new_generic_instance(program, self, instance_type_vars)\n      generic_types[type_vars] = instance\n\n      if instance.is_a?(GenericClassInstanceType) && !instance.superclass\n        instance.superclass = instantiated_generic_superclass(instance)\n      end\n\n      self.instance_vars.each do |name, ivar|\n        ivar_type = ivar.type\n        if ivar_type.is_a?(TypeSplat)\n          # Consider the case of @x : *T\n          instance_var_type = ivar_type.splatted_type.replace_type_parameters(instance)\n          unless instance_var_type.is_a?(TupleInstanceType)\n            raise TypeException.new \"expected splatted type to be a tuple type, not #{instance_var_type}\"\n          end\n        else\n          instance_var_type = ivar_type.replace_type_parameters(instance)\n        end\n        instance.declare_instance_var(name, instance_var_type, ivar.annotations)\n      end\n\n      run_instance_vars_initializers self, self, instance\n\n      instance.after_initialize\n\n      # Notify that a subclass/instance of self was added\n      self.notify_subclass_added if self.is_a?(SubclassObservable)\n\n      # Notify parents that an instance was added\n      notify_parents_subclass_added(self)\n\n      instance\n    end\n\n    def notify_parents_subclass_added(type)\n      type.parents.try &.each do |parent|\n        parent.notify_subclass_added if parent.is_a?(SubclassObservable)\n        notify_parents_subclass_added parent\n      end\n    end\n\n    getter inherited : Array(Type)?\n\n    def add_inherited(type)\n      inherited = @inherited ||= [] of Type\n      inherited << type\n    end\n\n    def add_instance_var_initializer(name, value, meta_vars)\n      initializer = super\n\n      # Make sure to type the initializer for existing instantiations\n      each_instantiated_type do |instance|\n        run_instance_var_initializer(initializer, instance)\n      end\n\n      @inherited.try &.each do |inherited|\n        run_instance_var_initializer(initializer, inherited)\n        if inherited.is_a?(GenericClassType)\n          inherited.add_instance_var_initializer(name, value, meta_vars)\n        end\n      end\n\n      initializer\n    end\n\n    def run_instance_vars_initializers(real_type, type : GenericClassType | ClassType, instance)\n      if superclass = type.superclass\n        run_instance_vars_initializers(real_type, superclass, instance)\n      end\n\n      type.instance_vars_initializers.try &.each do |initializer|\n        run_instance_var_initializer initializer, instance\n      end\n    end\n\n    def run_instance_vars_initializers(real_type, type, instance)\n      # Nothing\n    end\n\n    def run_instance_var_initializer(initializer, instance : GenericClassInstanceType | NonGenericClassType)\n      return if instance.unbound?\n\n      meta_vars = MetaVars.new\n      visitor = MainVisitor.new(program, vars: meta_vars, meta_vars: meta_vars)\n      visitor.scope = instance.metaclass\n      value = initializer.value.clone\n      value.accept visitor\n      instance_var = instance.lookup_instance_var(initializer.name)\n\n      # Check if automatic cast can be done\n      if instance_var.type != value.type && value.supports_autocast?(!@program.has_flag?(\"no_number_autocast\"))\n        if casted_value = MainVisitor.check_automatic_cast(@program, value, instance_var.type)\n          value = casted_value\n        end\n      end\n\n      instance_var.bind_to(value)\n      instance.add_instance_var_initializer(initializer.name, value, meta_vars)\n    end\n\n    def run_instance_var_initializer(initializer, instance)\n      # Nothing\n    end\n\n    def instantiated_generic_superclass(instance)\n      superclass = self.superclass.not_nil!\n      if superclass.is_a?(GenericClassInstanceType)\n        superclass = superclass.replace_type_parameters(instance)\n      end\n      superclass\n    end\n\n    def type_var?(name)\n      type_vars.includes? name\n    end\n  end\n\n  # An un-bound type parameter of a generic type.\n\n  # For example, given:\n  #\n  # ```\n  # class Bar(T) < Foo(T, Int32)\n  # end\n  # ```\n  #\n  # when we solve `Foo(T, Int32)` we'll find Foo, and\n  # then instantiate it with `T` being the type parameter\n  # `T` of `Bar`, and `Int32` a regular type variable.\n  #\n  # Similarly, when including a generic module inside a generic\n  # type, type parameters will be used.\n  #\n  # ```\n  # class Baz(T)\n  #   include Enumerable(T) # <- this is TypeParameter T of Foo\n  # end\n  # ```\n  #\n  # When instantiating Bar(T) in the first example, for example\n  # doing `Bar(Char)`, superclasses and including modules will\n  # have type parameters replaced with types given in the instantiation,\n  # so `Foo(T, Int32)` will become `Foo(Char, Int32)`.\n  class TypeParameter < Type\n    # Returns the type that owns this type parameter\n    getter owner\n\n    # Returns the name of this type parameter\n    getter name\n\n    def initialize(program, @owner : GenericType, @name : String)\n      super(program)\n    end\n\n    def replace_type_parameters(instance)\n      node = solve(instance)\n      if node.is_a?(Var)\n        node.type\n      else\n        node.raise \"can't declare variable with #{node.class_desc}\"\n      end\n    end\n\n    def solve(instance)\n      solve?(instance).not_nil!\n    end\n\n    def solve?(instance)\n      if instance.is_a?(GenericInstanceType) && instance.generic_type == @owner\n        ancestor = instance\n      else\n        ancestor = instance.ancestors.find { |ancestor| ancestor.is_a?(GenericInstanceType) && ancestor.generic_type == owner }.as?(GenericInstanceType)\n      end\n\n      ancestor.try &.type_vars[name]?\n    end\n\n    def unbound?\n      true\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << @name\n    end\n  end\n\n  # A splatted type inside an inherited generic class or included generic module.\n  #\n  # For example, given:\n  #\n  # ```\n  # class Foo(T)\n  #   include Bar(Union(*T))\n  # end\n  # ```\n  #\n  # the `T` in the included type will be a TypeParameter, but we can't\n  # splat it yet (expands the types behind T) until we know T. We mark\n  # this as a TypeSplat of the type parameter T.\n  #\n  # When instantiating Foo, T will be replaced with the instantiated type\n  # and this TypeSplat will check that it's a tuple type and append its\n  # types to the final type variables.\n  class TypeSplat < Type\n    getter splatted_type\n\n    def initialize(program, @splatted_type : TypeParameter)\n      super(program)\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << '*' << @splatted_type\n    end\n  end\n\n  # A generic module type, like Enumerable(T).\n  class GenericModuleType < ModuleType\n    include GenericType\n    include ClassVarContainer\n\n    def initialize(program, namespace, name, @type_vars)\n      super(program, namespace, name)\n    end\n\n    def module?\n      true\n    end\n\n    def can_be_stored?\n      false\n    end\n\n    def type_desc\n      \"generic module\"\n    end\n\n    def add_including_type(type)\n      including_types = @including_types ||= [] of Type\n      including_types.push type\n    end\n\n    def raw_including_types\n      @including_types\n    end\n\n    def new_generic_instance(program, generic_type, type_vars)\n      GenericModuleInstanceType.new program, generic_type, type_vars\n    end\n\n    def add_instance_var_initializer(name, value, meta_vars)\n      add_instance_var_initializer @including_types, name, value, meta_vars\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      super\n      if generic_args\n        io << '('\n        type_vars.each_with_index do |type_var, i|\n          io << \", \" if i > 0\n          io << '*' if i == splat_index\n          type_var.to_s(io)\n        end\n        io << ')'\n      end\n    end\n  end\n\n  # A generic class type, like Array(T).\n  class GenericClassType < ClassType\n    include GenericType\n    include ClassVarContainer\n\n    def initialize(program, namespace, name, superclass, @type_vars : Array(String), add_subclass = true)\n      super(program, namespace, name, superclass, add_subclass)\n    end\n\n    def class?\n      true\n    end\n\n    def can_be_stored?\n      false\n    end\n\n    def new_generic_instance(program, generic_type, type_vars)\n      GenericClassInstanceType.new program, generic_type, nil, type_vars\n    end\n\n    protected def initialize_metaclass(metaclass)\n      metaclass.add_def Def.new(\"allocate\", body: Primitive.new(\"allocate\"))\n    end\n\n    def type_desc\n      struct? ? \"generic struct\" : \"generic class\"\n    end\n\n    def including_types\n      instances = instantiated_types\n      subclasses.each do |subclass|\n        if subclass.is_a?(GenericClassType)\n          subtypes = subclass.including_types\n          instances << subtypes if subtypes\n        else\n          instances << subclass\n        end\n      end\n      program.union_of instances\n    end\n\n    def remove_indirection\n      if including_types = self.including_types\n        including_types.remove_indirection\n      else\n        self\n      end\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      super\n      if generic_args\n        io << '('\n        type_vars.each_with_index do |type_var, i|\n          io << \", \" if i > 0\n          io << '*' if i == splat_index\n          type_var.to_s(io)\n        end\n        io << ')'\n      end\n    end\n  end\n\n  # An instantiated generic type (like Array(String) or Enumerable(Int32)).\n  #\n  # To represent generic superclasses and generic included modules,\n  # GenericInstanceType is also used. For example, in:\n  #\n  # ```\n  # class Foo(T); end\n  #\n  # class Bar(T) < Foo(T); end\n  # ```\n  #\n  # The GenericClassType `Bar(T)` will have a GenericClassInstanceType\n  # `Foo(T)` as a superclass, with `T` being a `TypeParameter`. We can't\n  # simply have the generic type `Foo(T)` be the superclass (maybe in this\n  # particular example yes) but we could also have:\n  #\n  # ```\n  # class Foo(X, Y); end\n  #\n  # class Bar(T) < Foo(Int32, T); end\n  # ```\n  #\n  # In that case `Foo(X, Y)` is not quite the superclass, because\n  # the superclass has a fixed type `Int32` as the first parameter.\n  abstract class GenericInstanceType < Type\n    getter generic_type : GenericType\n    getter type_vars : Hash(String, ASTNode)\n\n    delegate :annotation, :annotations, :all_annotations, to: generic_type\n\n    def initialize(program, @generic_type, @type_vars)\n      super(program)\n    end\n\n    def class_var_owner\n      generic_type.class_var_owner\n    end\n\n    def parents\n      generic_type.parents.try &.map do |parent|\n        parent.replace_type_parameters(self)\n      end\n    end\n\n    def replace_type_parameters(instance)\n      new_type_vars = [] of TypeVar\n\n      type_vars.each_with_index do |(name, node), index|\n        if node.is_a?(Var)\n          type = node.type\n\n          case type\n          when TypeParameter\n            replacement = type.solve(instance)\n            if replacement.is_a?(Var)\n              type_var = replacement.type\n            else\n              type_var = replacement\n            end\n          when TypeSplat\n            type_var = type.splatted_type.replace_type_parameters(instance)\n          else\n            type_var = type.replace_type_parameters(instance)\n          end\n\n          if splat_index == index\n            if type_var.is_a?(TupleInstanceType)\n              new_type_vars.concat(type_var.tuple_types)\n            else\n              node.raise \"expected type to be a tuple type, not #{type_var}\"\n            end\n          elsif type.is_a?(TypeSplat)\n            if type_var.is_a?(TupleInstanceType)\n              new_type_vars.concat(type_var.tuple_types)\n            else\n              node.raise \"expected type to be a tuple type, not #{type_var}\"\n            end\n          else\n            new_type_vars << type_var\n          end\n        else\n          new_type_vars << node\n        end\n      end\n\n      generic_type.instantiate(new_type_vars)\n    end\n\n    def implements?(other_type : GenericInstanceType)\n      if generic_type == other_type.generic_type\n        type_vars.each do |name, type_var|\n          other_type_var = other_type.type_vars[name]\n          if type_var.is_a?(Var) && other_type_var.is_a?(Var)\n            # type vars are invariant here; (Named)TupleInstanceType have their own logic\n            return super unless type_var.type.devirtualize == other_type_var.type.devirtualize\n          else\n            return super unless type_var == other_type_var\n          end\n        end\n        return true\n      end\n\n      super\n    end\n\n    def implements?(other_type)\n      other_type = other_type.remove_alias\n      super || generic_type.implements?(other_type)\n    end\n\n    def has_in_type_vars?(type)\n      type_vars.each_value do |type_var|\n        if type_var.is_a?(Var)\n          return true if type_var.type.includes_type?(type) || type_var.type.has_in_type_vars?(type)\n        end\n      end\n      false\n    end\n\n    def unbound?\n      type_vars.each_value do |type_var|\n        if type_var.is_a?(Var)\n          return true if type_var.type.unbound?\n        end\n      end\n      false\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      generic_type.append_full_name(io)\n      if generic_args\n        io << '('\n        first = true\n        type_vars.each_with_index do |(_, type_var), i|\n          if type_var.is_a?(Var)\n            if i == splat_index\n              tuple = type_var.type.as(TupleInstanceType)\n              tuple.tuple_types.each do |tuple_type|\n                io << \", \" unless first\n                first = false\n                tuple_type = tuple_type.devirtualize unless codegen\n                tuple_type.to_s_with_options(io, codegen: codegen)\n              end\n            else\n              io << \", \" unless first\n              first = false\n              type_var_type = type_var.type\n              type_var_type = type_var_type.devirtualize unless codegen\n              type_var_type.to_s_with_options(io, skip_union_parens: true, codegen: codegen)\n            end\n          else\n            # `type_var` could only be a non-type such as `NumberLiteral`, no\n            # need to use `#to_s_with_options` here\n            io << \", \" unless first\n            first = false\n            type_var.to_s(io)\n          end\n        end\n        io << ')'\n      end\n    end\n  end\n\n  # An instantiated generic type, like Array(String).\n  class GenericClassInstanceType < GenericInstanceType\n    include InstanceVarContainer\n    include InstanceVarInitializerContainer\n    include DefInstanceContainer\n    include SubclassObservable\n\n    property superclass : Type?\n    getter subclasses = [] of Type\n    getter generic_nest : Int32\n\n    def initialize(program, generic_type, @superclass, type_vars, generic_nest = nil)\n      super(program, generic_type, type_vars)\n      @generic_nest = generic_nest || (1 + @type_vars.values.max_of { |node| node.type?.try(&.generic_nest) || 0 })\n    end\n\n    def after_initialize\n      superclass.not_nil!.add_subclass(self)\n      ancestors.each do |ancestor|\n        ancestor.add_including_type(self) if ancestor.is_a?(GenericModuleInstanceType)\n      end\n    end\n\n    def add_subclass(subclass)\n      return if subclass.unbound?\n\n      subclasses << subclass\n      notify_subclass_added\n\n      superclass = superclass()\n      while superclass.is_a?(SubclassObservable)\n        superclass.notify_subclass_added\n        superclass = superclass.superclass\n      end\n    end\n\n    def virtual_type\n      if generic_type.leaf? && !abstract?\n        self\n      elsif struct? && abstract? && !generic_type.leaf?\n        virtual_type!\n      elsif struct?\n        self\n      else\n        virtual_type!\n      end\n    end\n\n    def virtual_type!\n      @virtual_type ||= VirtualType.new(program, self)\n    end\n\n    delegate depth, defs, superclass, macros, abstract?, struct?,\n      type_desc, namespace, lookup_new_in_ancestors?,\n      splat_index, double_variadic?, to: @generic_type\n\n    def filter_by_responds_to(name)\n      @generic_type.filter_by_responds_to(name) ? self : nil\n    end\n\n    def class?\n      true\n    end\n\n    getter(metaclass) { GenericClassInstanceMetaclassType.new(self.program, self) }\n  end\n\n  # An instantiated generic module, like Enumerable(Int32).\n  class GenericModuleInstanceType < GenericInstanceType\n    include InstanceVarContainer\n    include InstanceVarInitializerContainer\n    include DefInstanceContainer\n    include SubclassObservable\n\n    getter generic_nest : Int32\n\n    def initialize(program, generic_type, type_vars, generic_nest = nil)\n      super(program, generic_type, type_vars)\n      @generic_nest = generic_nest || (1 + @type_vars.values.max_of { |node| node.type?.try(&.generic_nest) || 0 })\n    end\n\n    def after_initialize\n      ancestors.each do |ancestor|\n        ancestor.add_including_type(self) if ancestor.is_a?(GenericModuleInstanceType)\n      end\n    end\n\n    def virtual_type\n      self\n    end\n\n    delegate leaf?, depth, defs, macros,\n      type_desc, namespace, lookup_new_in_ancestors?,\n      splat_index, double_variadic?, to: @generic_type\n\n    def add_including_type(type)\n      return if type.unbound?\n\n      @generic_type.add_including_type(type)\n\n      including_types = @including_types ||= [] of Type\n      including_types.push type\n\n      notify_subclass_added\n    end\n\n    def including_types\n      if including_types = @including_types\n        all_types = Array(Type).new(including_types.size)\n        add_to_including_types(all_types)\n        program.type_merge_union_of(all_types)\n      else\n        nil\n      end\n    end\n\n    def add_to_including_types(all_types)\n      if including_types = @including_types\n        including_types.each do |including_type|\n          add_to_including_types(including_type, all_types)\n        end\n      end\n    end\n\n    def raw_including_types\n      @including_types\n    end\n\n    def remove_indirection\n      if including_types = self.including_types\n        including_types.remove_indirection\n      else\n        self\n      end\n    end\n\n    def filter_by_responds_to(name)\n      including_types.try &.filter_by_responds_to(name)\n    end\n\n    def module?\n      true\n    end\n\n    getter(metaclass) { GenericModuleInstanceMetaclassType.new(self.program, self) }\n  end\n\n  # The non-instantiated Pointer(T) type.\n  class PointerType < GenericClassType\n    def new_generic_instance(program, generic_type, type_vars)\n      PointerInstanceType.new program, generic_type, program.struct, type_vars\n    end\n\n    def type_desc\n      \"generic struct\"\n    end\n  end\n\n  # An instantiated pointer type, like Pointer(Int32).\n  class PointerInstanceType < GenericClassInstanceType\n    def var\n      type_vars[\"T\"]\n    end\n\n    def element_type\n      var.type\n    end\n\n    def type_desc\n      \"struct\"\n    end\n  end\n\n  # The non-instantiated StaticArray(T, N) type.\n  class StaticArrayType < GenericClassType\n    def new_generic_instance(program, generic_type, type_vars)\n      n = type_vars[\"N\"]\n\n      unless n.is_a?(Var) && n.type.is_a?(TypeParameter)\n        unless n.is_a?(NumberLiteral)\n          raise TypeException.new \"can't instantiate StaticArray(T, N) with N = #{n.type} (N must be an integer)\"\n        end\n\n        value = n.value.to_i?\n        unless value\n          raise TypeException.new \"can't instantiate StaticArray(T, N) with N = #{n} (N must be an integer)\"\n        end\n\n        if value < 0\n          raise TypeException.new \"can't instantiate StaticArray(T, N) with N = #{value} (N must be positive)\"\n        end\n      end\n\n      StaticArrayInstanceType.new program, generic_type, program.struct, type_vars\n    end\n  end\n\n  # An instantiated static array type, like StaticArray(UInt8, 256)\n  class StaticArrayInstanceType < GenericClassInstanceType\n    def var\n      type_vars[\"T\"]\n    end\n\n    def size\n      type_vars[\"N\"]\n    end\n\n    def element_type\n      var.type\n    end\n  end\n\n  # The non-instantiated Proc(*T, R) type.\n  class ProcType < GenericClassType\n    @splat_index = 0\n    @struct = true\n\n    def instantiate(type_vars)\n      if (instance = generic_types[type_vars]?)\n        return instance\n      end\n\n      types = type_vars.map do |type_var|\n        unless type_var.is_a?(Type)\n          type_var.raise \"argument to Proc must be a type, not #{type_var}\"\n        end\n        type_var\n      end\n      return_type = types.pop\n\n      # Transform Proc(*T, Void) to Proc(*T, Nil) as Void has\n      # only a special meaning in Pointer(Void)\n      if return_type == @program.void\n        return_type = @program.nil_type\n      end\n\n      instance = ProcInstanceType.new(program, types, return_type)\n      generic_types[type_vars] = instance\n      instance.after_initialize\n      instance\n    end\n\n    def can_be_stored?\n      false\n    end\n\n    def new_generic_instance(program, generic_type, type_vars)\n      raise \"BUG: ProcType#new_generic_instance shouldn't be invoked\"\n    end\n\n    def type_desc\n      \"function\"\n    end\n  end\n\n  # An instantiated proc type like Proc(Int32, Char)\n  class ProcInstanceType < GenericClassInstanceType\n    getter arg_types : Array(Type)\n    getter return_type : Type\n\n    def initialize(program, @arg_types, @return_type)\n      t_var = Var.new(\"T\", program.tuple_of(@arg_types))\n      t_var.bind_to t_var\n\n      r_var = Var.new(\"R\", @return_type)\n      r_var.bind_to r_var\n\n      super(program, program.proc, program.struct, {\"T\" => t_var, \"R\" => r_var} of String => ASTNode)\n    end\n\n    def struct?\n      true\n    end\n\n    def implements?(other_type : ProcInstanceType)\n      return true if self == other_type\n      return false unless self.arg_types == other_type.arg_types\n\n      # - Proc(..., NoReturn) can be cast to Proc(..., T)\n      # - Anything can be cast to Proc(..., Void)\n      # - Anything can be cast to Proc(..., Nil)\n      self.return_type.no_return? || other_type.return_type.void? || other_type.return_type.nil_type?\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << \"Proc(\"\n      arg_types.each do |type|\n        type = type.devirtualize unless codegen\n        type.to_s_with_options(io, codegen: codegen)\n        io << \", \"\n      end\n      return_type = self.return_type\n      return_type = return_type.devirtualize unless codegen\n      return_type.to_s_with_options(io, codegen: codegen)\n      io << ')'\n    end\n  end\n\n  # The non-instantiated type Tuple(*T).\n  class TupleType < GenericClassType\n    @splat_index = 0\n    @struct = true\n\n    def instantiate(type_vars)\n      if (instance = generic_types[type_vars]?)\n        return instance\n      end\n\n      types = type_vars.map do |type_var|\n        unless type_var.is_a?(Type)\n          type_var.raise \"argument to Tuple must be a type, not #{type_var}\"\n        end\n        type_var\n      end\n      instance = TupleInstanceType.new(program, types)\n      generic_types[type_vars] = instance\n      instance.after_initialize\n      instance\n    end\n\n    def new_generic_instance(program, generic_type, type_vars)\n      raise \"BUG: TupleType#new_generic_instance shouldn't be invoked\"\n    end\n\n    def type_desc\n      \"tuple\"\n    end\n  end\n\n  # An instantiated tuple type, like Tuple(Char, Int32).\n  class TupleInstanceType < GenericClassInstanceType\n    alias Index = Int32 | Range(Int32, Int32)\n\n    getter tuple_types : Array(Type)\n\n    def initialize(program, @tuple_types)\n      generic_nest = 1 + (@tuple_types.empty? ? 0 : @tuple_types.max_of(&.generic_nest))\n      var = Var.new(\"T\", self)\n      var.bind_to var\n      super(program, program.tuple, program.struct,\n        {\"T\" => var} of String => ASTNode, generic_nest)\n    end\n\n    def tuple_indexer(index)\n      indexers = @tuple_indexers ||= {} of Index => Def\n      tuple_indexer(indexers, index)\n    end\n\n    def tuple_metaclass_indexer(index)\n      indexers = @tuple_metaclass_indexers ||= {} of Index => Def\n      tuple_indexer(indexers, index)\n    end\n\n    def size\n      tuple_types.size\n    end\n\n    private def tuple_indexer(indexers, index)\n      indexers[index] ||= begin\n        body = index == -1 ? NilLiteral.new : TupleIndexer.new(index)\n        indexer = Def.new(\"[]\", [Arg.new(\"index\")], body)\n        indexer.owner = self\n        indexer\n      end\n    end\n\n    def implements?(other_type : TupleInstanceType)\n      return true if self == other_type\n      return false unless self.size == other_type.size\n\n      tuple_types.zip(other_type.tuple_types) do |self_tuple_type, other_tuple_type|\n        return false unless self_tuple_type.implements?(other_tuple_type)\n      end\n\n      true\n    end\n\n    def var\n      type_vars[\"T\"]\n    end\n\n    def has_in_type_vars?(type)\n      tuple_types.any? { |tuple_type| tuple_type.includes_type?(type) || tuple_type.has_in_type_vars?(type) }\n    end\n\n    def replace_type_parameters(instance)\n      new_tuple_types = [] of Type\n      tuple_types.each do |tuple_type|\n        if tuple_type.is_a?(TypeSplat)\n          type = tuple_type.splatted_type.replace_type_parameters(instance)\n          if type.is_a?(TupleInstanceType)\n            new_tuple_types.concat(type.tuple_types)\n          else\n            raise \"expected type to be a tuple type, not #{type}\"\n          end\n        else\n          new_tuple_types << tuple_type.replace_type_parameters(instance)\n        end\n      end\n      program.tuple_of(new_tuple_types)\n    end\n\n    def unbound?\n      tuple_types.any? &.unbound?\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << \"Tuple(\"\n      @tuple_types.join(io, \", \") do |tuple_type|\n        tuple_type = tuple_type.devirtualize unless codegen\n        tuple_type.to_s_with_options(io, skip_union_parens: true, codegen: codegen)\n      end\n      io << ')'\n    end\n\n    def type_desc\n      \"tuple\"\n    end\n  end\n\n  # The non-instantiated NamedTuple(**T) type.\n  class NamedTupleType < GenericClassType\n    @struct = true\n    @double_variadic = true\n    @instantiations = {} of Array(NamedArgumentType) => Type\n\n    def instantiated_types\n      @instantiations.values\n    end\n\n    def each_instantiated_type(&)\n      @instantiations.each_value { |type| yield type }\n    end\n\n    def instantiate(type_vars)\n      raise \"can't instantiate NamedTuple type yet\"\n    end\n\n    def instantiate_named_args(entries : Array(NamedArgumentType))\n      @instantiations[entries] ||= NamedTupleInstanceType.new(program, entries)\n    end\n\n    def new_generic_instance(program, generic_type, type_vars)\n      raise \"BUG: NamedTupleType#new_generic_instance shouldn't be invoked\"\n    end\n\n    def type_desc\n      \"named tuple\"\n    end\n  end\n\n  # An instantiated named tuple type, like NamedTuple(x: Int32, y: Char).\n  class NamedTupleInstanceType < GenericClassInstanceType\n    getter entries\n\n    def initialize(program, @entries : Array(NamedArgumentType))\n      generic_nest = 1 + (@entries.empty? ? 0 : @entries.max_of(&.type.generic_nest))\n      var = Var.new(\"T\", self)\n      var.bind_to var\n      super(program, program.named_tuple, program.struct,\n        {\"T\" => var} of String => ASTNode, generic_nest)\n    end\n\n    def name_index(name)\n      @entries.index(&.name.==(name))\n    end\n\n    def name_type(name)\n      @entries.find!(&.name.==(name)).type\n    end\n\n    def tuple_indexer(index)\n      indexers = @tuple_indexers ||= {} of Int32 => Def\n      tuple_indexer(indexers, index)\n    end\n\n    def tuple_metaclass_indexer(index)\n      indexers = @tuple_metaclass_indexers ||= {} of Int32 => Def\n      tuple_indexer(indexers, index)\n    end\n\n    private def tuple_indexer(indexers, index)\n      indexers[index] ||= begin\n        body = index == -1 ? NilLiteral.new : TupleIndexer.new(index)\n        indexer = Def.new(\"[]\", [Arg.new(\"index\")], body)\n        indexer.owner = self\n        indexer\n      end\n    end\n\n    def implements?(other_type : NamedTupleInstanceType)\n      return true if self == other_type\n      return false unless self.size == other_type.size\n\n      self_entries = self.entries.sort_by &.name\n      other_entries = other_type.entries.sort_by &.name\n\n      self_entries.zip(other_entries) do |self_entry, other_entry|\n        return false unless self_entry.name == other_entry.name\n        return false unless self_entry.type.implements?(other_entry.type)\n      end\n\n      true\n    end\n\n    def size\n      entries.size\n    end\n\n    def var\n      type_vars[\"T\"]\n    end\n\n    def replace_type_parameters(instance)\n      new_entries = entries.map do |entry|\n        NamedArgumentType.new(entry.name, entry.type.replace_type_parameters(instance))\n      end\n      program.named_tuple_of(new_entries)\n    end\n\n    def unbound?\n      entries.any? &.type.unbound?\n    end\n\n    def has_in_type_vars?(type)\n      entries.any? { |entry| entry.type.includes_type?(type) || entry.type.has_in_type_vars?(type) }\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << \"NamedTuple(\"\n      @entries.join(io, \", \") do |entry|\n        Symbol.quote_for_named_argument(io, entry.name)\n        io << \": \"\n        entry_type = entry.type\n        entry_type = entry_type.devirtualize unless codegen\n        entry_type.to_s_with_options(io, skip_union_parens: true, codegen: codegen)\n      end\n      io << ')'\n    end\n\n    def type_desc\n      \"tuple\"\n    end\n  end\n\n  # The non-instantiated ReferenceStorage(T) type.\n  class GenericReferenceStorageType < GenericClassType\n    @struct = true\n\n    def new_generic_instance(program, generic_type, type_vars)\n      type_param = self.type_vars.first\n      t = type_vars[type_param].type\n\n      unless t.is_a?(TypeParameter) || (t.class? && !t.struct?)\n        raise TypeException.new \"Can't instantiate ReferenceStorage(#{type_param}) with #{type_param} = #{t} (#{type_param} must be a reference type)\"\n      end\n\n      ReferenceStorageType.new program, t, self, type_param\n    end\n  end\n\n  class ReferenceStorageType < GenericClassInstanceType\n    getter reference_type : Type\n\n    def initialize(program, @reference_type, generic_type, type_param)\n      t_var = Var.new(\"T\", @reference_type)\n      t_var.bind_to t_var\n\n      super(program, generic_type, program.value, {type_param => t_var} of String => ASTNode)\n    end\n  end\n\n  # A lib type, like `lib LibC`.\n  class LibType < ModuleType\n    getter link_annotations : Array(LinkAnnotation)?\n    property? used = false\n    property call_convention : LLVM::CallConvention?\n\n    def add_link_annotation(link_annotation : LinkAnnotation)\n      link_annotations = @link_annotations ||= [] of LinkAnnotation\n      link_annotations << link_annotation unless link_annotations.includes?(link_annotation)\n    end\n\n    def metaclass\n      self\n    end\n\n    def lookup_var(name)\n      a_def = lookup_first_def(name, false)\n      return nil unless a_def\n\n      body = a_def.body\n      return nil unless body.is_a?(Primitive) && body.name == \"external_var_get\"\n\n      a_def\n    end\n\n    def type_desc\n      \"lib\"\n    end\n\n    def wasm_import_module\n      (@link_annotations.try &.find &.wasm_import_module).try &.wasm_import_module\n    end\n  end\n\n  # A `type` (typedef) type inside a `lib` declaration.\n  class TypeDefType < NamedType\n    include DefInstanceContainer\n\n    getter typedef : Type\n\n    def initialize(program, namespace, name, @typedef)\n      super(program, namespace, name)\n    end\n\n    delegate remove_typedef, pointer?, defs,\n      macros, reference_like?, parents, lookup_instance_var, to: typedef\n\n    def remove_indirection\n      self\n    end\n\n    def type_def_type?\n      true\n    end\n\n    def type_desc\n      \"type def\"\n    end\n  end\n\n  # An alias type.\n  class AliasType < NamedType\n    getter? value_processed = false\n    property! aliased_type : Type\n    getter? simple\n\n    def initialize(program, namespace, name, @value : ASTNode)\n      super(program, namespace, name)\n      @simple = true\n    end\n\n    delegate lookup_defs, lookup_defs_with_modules, lookup_first_def,\n      lookup_macro, lookup_macros, to: aliased_type\n\n    def lookup_name(name)\n      process_value\n      @aliased_type.try(&.lookup_name(name))\n    end\n\n    def remove_alias\n      process_value\n      if aliased_type = @aliased_type\n        aliased_type.remove_alias\n      else\n        @simple = false\n        self\n      end\n    end\n\n    def remove_alias_if_simple\n      process_value\n      @simple ? remove_alias : self\n    end\n\n    def remove_indirection\n      process_value\n      if aliased_type = @aliased_type\n        aliased_type.remove_indirection\n      else\n        @simple = false\n        self\n      end\n    end\n\n    def can_be_stored?\n      process_value\n      if aliased_type = @aliased_type\n        aliased_type.remove_alias.can_be_stored?\n      else\n        true\n      end\n    end\n\n    def process_value\n      return if @value_processed\n      @value_processed = true\n      @aliased_type = namespace.lookup_type(@value,\n        allow_typeof: false,\n        find_root_generic_type_parameters: false)\n    end\n\n    def includes_type?(other)\n      remove_indirection.includes_type?(other)\n    end\n\n    def type_desc\n      \"alias\"\n    end\n  end\n\n  # An instantiated enum type.\n  #\n  # TODO: right now this is not properly modeled. Ideally there\n  # should be EnumType and EnumInstanceType, where EnumType would\n  # be `Enum(T)` and given:\n  #\n  # ```\n  # enum Foo : Int32\n  #   # ...\n  # end\n  # ```\n  #\n  # we'd have:\n  #\n  # ```\n  # enum Foo < Enum(Int32)\n  # end\n  # ```\n  #\n  # but right now that's not the case.\n  class EnumType < ModuleType\n    include DefInstanceContainer\n    include ClassVarContainer\n\n    getter base_type : IntegerType\n    property? flags = false\n\n    def initialize(program, namespace, name, @base_type)\n      super(program, namespace, name)\n\n      add_def Def.new(\"value\", [] of Arg, Primitive.new(\"enum_value\", @base_type))\n      metaclass.as(ModuleType).add_def Def.new(\"new\", [Arg.new(\"value\", restriction: Path.global(@base_type.to_s))], Primitive.new(\"enum_new\", self))\n    end\n\n    def parents\n      @parents ||= [program.enum] of Type\n    end\n\n    def add_constant(name, value)\n      const_exp = NumberLiteral.new(value.to_s, base_type.kind)\n      const_exp.type = self\n      types[name] = const = Const.new(program, self, name, const_exp)\n      program.const_initializers << const\n      const\n    end\n\n    def lookup_new_in_ancestors?\n      true\n    end\n\n    def find_member(name)\n      name = name.underscore\n      types.each do |member_name, member|\n        if name == member_name.underscore\n          return member.as(Const)\n        end\n      end\n      nil\n    end\n\n    def type_desc\n      \"enum\"\n    end\n  end\n\n  class AnnotationType < NamedType\n    def type_desc\n      \"annotation\"\n    end\n  end\n\n  # A metaclass type, that results from invoking `.class` on a type.\n  #\n  # For example `String.class` is the metaclass of `String`, and it's\n  # the type of `String` (the type of `\"foo\"` is `String`, the type of\n  # `String` is `String.class`).\n  #\n  # This metaclass represents only the metaclass of non-generic types.\n  class MetaclassType < ClassType\n    include ClassVarContainer\n\n    getter instance_type : Type\n\n    def initialize(program, @instance_type : Type, super_class = nil, name = nil)\n      super_class ||= if instance_type.is_a?(ClassType) && instance_type.superclass\n                        instance_type.superclass.not_nil!.metaclass\n                      elsif instance_type.is_a?(EnumType)\n                        program.enum.metaclass\n                      else\n                        program.class_type\n                      end\n      unless name\n        if instance_type.module?\n          name = \"#{@instance_type}:Module\"\n        else\n          name = \"#{@instance_type}.class\"\n        end\n      end\n      super(program, program, name, super_class)\n    end\n\n    def metaclass\n      program.class_type\n    end\n\n    delegate abstract?, generic_nest, lookup_new_in_ancestors?,\n      type_var?, to: instance_type\n\n    def class_var_owner\n      instance_type.class_var_owner\n    end\n\n    def virtual_type\n      instance_type.virtual_type.metaclass\n    end\n\n    def virtual_type!\n      instance_type.virtual_type!.metaclass\n    end\n\n    def remove_typedef\n      if instance_type.is_a?(TypeDefType)\n        return instance_type.remove_typedef.metaclass\n      end\n      self\n    end\n\n    def replace_type_parameters(instance)\n      instance_type.replace_type_parameters(instance).metaclass\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      if codegen\n        if (namespace = instance_type.namespace).is_a?(FileModule)\n          namespace.to_s_with_options(io, generic_args: false, codegen: codegen)\n          io << \"::\"\n        end\n      end\n      io << @name\n    end\n\n    def type_desc\n      \"metaclass\"\n    end\n  end\n\n  # The metaclass of a generic class instance type, like `Array(String).class`\n  class GenericClassInstanceMetaclassType < Type\n    include DefInstanceContainer\n\n    getter instance_type : Type\n\n    def initialize(program, @instance_type)\n      super(program)\n    end\n\n    def metaclass\n      program.class_type\n    end\n\n    def add_subclass(subclass)\n      # Nothing\n    end\n\n    def superclass\n      instance_type.superclass.try(&.metaclass) || program.class_type\n    end\n\n    def parents\n      generic_type.parents.try &.map do |parent|\n        parent.replace_type_parameters(instance_type)\n      end\n    end\n\n    def generic_type\n      instance_type.generic_type.metaclass\n    end\n\n    def implements?(other_type : Type)\n      other_type = other_type.remove_alias\n      super || generic_type.implements?(other_type)\n    end\n\n    def replace_type_parameters(instance_type)\n      self.instance_type.replace_type_parameters(instance_type).metaclass\n    end\n\n    def virtual_type\n      instance_type.virtual_type.metaclass\n    end\n\n    def virtual_type!\n      instance_type.virtual_type!.metaclass\n    end\n\n    delegate defs, macros, to: generic_type\n    delegate type_vars, abstract?, generic_nest, lookup_new_in_ancestors?, to: instance_type\n\n    def class_var_owner\n      instance_type.class_var_owner\n    end\n\n    def filter_by_responds_to(name)\n      if generic_type.filter_by_responds_to(name)\n        self\n      else\n        nil\n      end\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      instance_type.to_s_with_options(io, codegen: codegen)\n      io << \".class\"\n    end\n\n    def type_desc\n      \"metaclass\"\n    end\n  end\n\n  # The metaclass of a generic module instance type, like `Enumerable(Int32).class`\n  class GenericModuleInstanceMetaclassType < Type\n    include DefInstanceContainer\n\n    getter instance_type : Type\n\n    def initialize(program, @instance_type)\n      super(program)\n    end\n\n    def metaclass\n      program.class_type\n    end\n\n    def add_subclass(subclass)\n      # Nothing\n    end\n\n    def parents\n      generic_type.parents.try &.map do |parent|\n        parent.replace_type_parameters(instance_type)\n      end\n    end\n\n    def generic_type\n      instance_type.generic_type.metaclass\n    end\n\n    def implements?(other_type : Type)\n      other_type = other_type.remove_alias\n      super || generic_type.implements?(other_type)\n    end\n\n    def replace_type_parameters(instance_type)\n      self.instance_type.replace_type_parameters(instance_type).metaclass\n    end\n\n    delegate defs, macros, to: generic_type\n    delegate type_vars, generic_nest, lookup_new_in_ancestors?, to: instance_type\n\n    def class_var_owner\n      instance_type.class_var_owner\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      instance_type.to_s_with_options(io, codegen: codegen)\n      io << \".class\"\n    end\n\n    def type_desc\n      \"metaclass\"\n    end\n  end\n\n  # A type that consists of other types, like unions and virtual types.\n  module MultiType\n    def concrete_types\n      types = [] of Type\n      each_concrete_type { |type| types << type }\n      types\n    end\n\n    def union_types\n      union_types = program.type_merge_union_of(concrete_types)\n      union_types || base_type\n    end\n  end\n\n  # The non-instantiated Union(*T) type.\n  class GenericUnionType < GenericClassType\n    @splat_index = 0\n    @struct = true\n\n    def instantiate(type_vars, type_merge_union_of = false)\n      types = type_vars.map do |type_var|\n        unless type_var.is_a?(Type)\n          type_var.raise \"argument to Union must be a type, not #{type_var}\"\n        end\n        # There's no need for types to be virtual because at the end\n        # `type_merge` will take care of that.\n        # The benefit is that if one writes `Union(T)`, that becomes exactly T\n        # and not T+ (which might lead to some inconsistencies).\n        type_var.devirtualize.as(Type)\n      end\n\n      if type_merge_union_of\n        program.type_merge_union_of(types) || program.no_return\n      else\n        program.type_merge(types) || program.no_return\n      end\n    end\n\n    def new_generic_instance(program, generic_type, type_vars)\n      raise \"BUG: GenericUnionType#new_generic_instance shouldn't be invoked\"\n    end\n\n    def type_desc\n      \"union\"\n    end\n  end\n\n  # Base class for instantiated union types.\n  abstract class UnionType < Type\n    include MultiType\n\n    getter union_types : Array(Type)\n\n    def initialize(program, @union_types)\n      super(program)\n    end\n\n    def parents\n      @parents ||= [program.value] of Type\n    end\n\n    def superclass\n      program.value\n    end\n\n    def generic_type\n      program.union\n    end\n\n    def metaclass\n      @metaclass ||= GenericClassInstanceMetaclassType.new(program, self)\n    end\n\n    def generic_nest\n      union_types.max_of &.generic_nest\n    end\n\n    def includes_type?(other_type)\n      union_types.any? &.includes_type?(other_type)\n    end\n\n    def filter_by_responds_to(name)\n      filtered_types = union_types.compact_map &.filter_by_responds_to(name)\n      program.type_merge_union_of filtered_types\n    end\n\n    def each_concrete_type(&)\n      union_types.each do |type|\n        if type.is_a?(VirtualType) || type.is_a?(VirtualMetaclassType)\n          type.each_concrete_type do |concrete_type|\n            yield concrete_type\n          end\n        elsif type.is_a?(ModuleType) || type.is_a?(GenericModuleInstanceType)\n          _type = type.remove_indirection\n          if _type.responds_to?(:concrete_types)\n            # do to recursion uncaptured block method\n            # we need to use concrete_types.each\n            # instead of each_concrete_types\n            _type.concrete_types.each do |concrete_type|\n              yield concrete_type\n            end\n          else\n            yield _type\n          end\n        else\n          yield type\n        end\n      end\n    end\n\n    def virtual_type\n      if union_types.any? { |t| t.virtual_type != t }\n        program.type_merge(union_types.map(&.virtual_type)).not_nil!\n      else\n        self\n      end\n    end\n\n    def implements?(other_type : Type)\n      other_type = other_type.remove_alias\n      self == other_type || union_types.all?(&.implements?(other_type))\n    end\n\n    def replace_type_parameters(instance)\n      new_union_types = Array(Type).new(union_types.size)\n      union_types.each do |type|\n        case type\n        when TypeParameter\n          replacement = type.solve(instance)\n          if replacement.is_a?(Var)\n            new_union_types << replacement.type\n          else\n            raise TypeException.new \"expected type, not #{replacement.class_desc}\"\n          end\n        when TypeSplat\n          type_var = type.splatted_type.replace_type_parameters(instance)\n          if type_var.is_a?(TupleInstanceType)\n            new_union_types.concat(type_var.tuple_types)\n          else\n            raise TypeException.new \"expected tuple type, not #{type_var}\"\n          end\n        else\n          new_union_types << type.replace_type_parameters(instance)\n        end\n      end\n      program.type_merge(new_union_types) || program.no_return\n    end\n\n    def unbound?\n      union_types.any? &.unbound?\n    end\n\n    def all?(&)\n      union_types.all? { |union_type| yield union_type }\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      io << '(' unless skip_union_parens\n      union_types = @union_types\n      # Make sure to put Nil at the end\n      if nil_type_index = @union_types.index(&.nil_type?)\n        union_types = @union_types.dup\n        union_types << union_types.delete_at(nil_type_index)\n      end\n      union_types.join(io, \" | \") do |type|\n        type = type.devirtualize unless codegen\n        type.to_s_with_options(io, codegen: codegen)\n      end\n      io << ')' unless skip_union_parens\n    end\n\n    def type_desc\n      \"union\"\n    end\n  end\n\n  # A union type that has two types: Nil and another Reference type.\n  # Can be represented as a maybe-null pointer where the type id\n  # of the type that is not nil is known at compile time.\n  class NilableType < UnionType\n    def initialize(program, not_nil_type)\n      super(program, [program.nil, not_nil_type] of Type)\n    end\n\n    def not_nil_type\n      @union_types.last\n    end\n  end\n\n  # A union type that has Nil and other reference-like types.\n  # Can be represented as a maybe-null pointer but the type id is\n  # not known at compile time.\n  class NilableReferenceUnionType < UnionType\n  end\n\n  # A union type that doesn't have nil, and all types are reference-like.\n  # Can be represented as a never-null pointer.\n  class ReferenceUnionType < UnionType\n  end\n\n  # A union type of nil and a single function type.\n  class NilableProcType < UnionType\n    def initialize(program, proc_type)\n      super(program, [program.nil, proc_type] of Type)\n    end\n\n    def proc_type\n      @union_types.last.remove_typedef.as(ProcInstanceType)\n    end\n  end\n\n  # A union type that doesn't match any of the previous definitions,\n  # so it can contain Nil with primitive types, or Reference types with\n  # primitives types.\n  # Must be represented as a union.\n  class MixedUnionType < UnionType\n  end\n\n  # A constant inside a type. For example, given:\n  #\n  # ```\n  # class Foo\n  #   A = 1\n  # end\n  # ```\n  #\n  # `A` will be a Const type with a value of 1.\n  #\n  # A constant is a type because when we do `Foo::A` we do a regular\n  # type lookup, which might result in a constant, so constants are\n  # saved under a type types like any other type.\n  class Const < NamedType\n    property value : ASTNode\n    property fake_def : Def?\n    property? used = false\n    property? visited = false\n\n    # Was this const's value cleaned up by CleanupTransformer yet?\n    property? cleaned_up = false\n\n    # Is this constant accessed with pointerof(...)?\n    property? pointer_read = false\n\n    property visitor : MainVisitor?\n\n    def initialize(program, namespace, name, @value)\n      super(program, namespace, name)\n    end\n\n    def type_desc\n      \"constant\"\n    end\n  end\n\n  module VirtualTypeLookup\n    def filter_by_responds_to(name)\n      filtered = virtual_lookup(base_type).filter_by_responds_to(name)\n      return filtered.virtual_type if filtered\n\n      result = [] of Type\n      collect_filtered_by_responds_to(name, base_type, result)\n      program.type_merge_union_of(result)\n    end\n\n    def collect_filtered_by_responds_to(name, type, result)\n      type.subclasses.each do |subclass|\n        unless subclass.is_a?(GenericType) || subclass.unbound?\n          filtered = virtual_lookup(subclass).filter_by_responds_to(name)\n          if filtered\n            result << virtual_lookup(subclass).virtual_type\n            next\n          end\n        end\n\n        collect_filtered_by_responds_to(name, subclass, result)\n      end\n    end\n  end\n\n  # A virtual type represents a type or any of its subclasses. It's created\n  # automatically by the compiler when a type is used in a generic argument\n  # and it either has subtypes, or it's abstract. A virtual type never exists\n  # for a non-abstract type that doesn't have subtypes.\n  #\n  # A virtual type is denoted, internally, with a '+' sign following the type.\n  #\n  # ```\n  # class Foo\n  # end\n  #\n  # class Bar < Foo\n  # end\n  #\n  # # Here the compiler actually makes this be [] of Foo+, so the array\n  # # can actually hold a Foo or a Bar, transparently.\n  # ary = [] of Foo\n  #\n  # # Here the compiler leaves it as [] of Bar, because Bar has no subclasses.\n  # another = [] of Bar\n  # ```\n  class VirtualType < Type\n    include MultiType\n    include DefInstanceContainer\n    include VirtualTypeLookup\n    include InstanceVarContainer\n    include ClassVarContainer\n\n    # Given `Foo+`, this returns `Foo`.\n    getter base_type : Type\n\n    def initialize(program, @base_type)\n      super(program)\n    end\n\n    delegate leaf?, superclass, lookup_first_def, lookup_defs,\n      lookup_defs_with_modules, lookup_instance_var, lookup_instance_var?,\n      index_of_instance_var, lookup_macro, lookup_macros, all_instance_vars,\n      abstract?, implements?, ancestors, struct?,\n      type_var?, unbound?, to: base_type\n\n    def remove_indirection\n      if struct?\n        union_types\n      else\n        self\n      end\n    end\n\n    def metaclass\n      @metaclass ||= VirtualMetaclassType.new(program, self)\n    end\n\n    def each_concrete_type(&)\n      subtypes.each do |subtype|\n        yield subtype unless subtype.abstract?\n      end\n    end\n\n    def subtypes\n      subtypes = [] of Type\n      collect_subtypes(base_type, subtypes)\n      subtypes\n    end\n\n    def subtypes(type)\n      subtypes = [] of Type\n      type.subclasses.each do |subclass|\n        collect_subtypes subclass, subtypes\n      end\n      subtypes\n    end\n\n    def collect_subtypes(type, subtypes)\n      subtypes << type unless type.is_a?(GenericType) || type.unbound?\n      type.subclasses.each do |subclass|\n        collect_subtypes subclass, subtypes\n      end\n    end\n\n    def lookup_class_var?(name)\n      class_var = @class_vars.try &.[name]?\n      return class_var if class_var\n\n      class_var = base_type.lookup_class_var?(name)\n      if class_var\n        var = MetaTypeVar.new(name, class_var.type)\n        var.owner = self\n        var.thread_local = class_var.thread_local?\n        var.initializer = class_var.initializer\n        var.bind_to(class_var)\n        self.class_vars[name] = var\n        return var\n      end\n\n      nil\n    end\n\n    def all_class_vars\n      all_class_vars = {} of String => MetaTypeVar\n      @class_vars.try { |v| all_class_vars.merge!(v) }\n      all_class_vars.merge!(base_type.all_class_vars)\n      all_class_vars\n    end\n\n    def replace_type_parameters(instance)\n      base_type.replace_type_parameters(instance).virtual_type\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      base_type.to_s_with_options(io, codegen: codegen)\n      io << '+'\n    end\n\n    def name\n      to_s\n    end\n  end\n\n  class VirtualMetaclassType < Type\n    include MultiType\n    include DefInstanceContainer\n    include VirtualTypeLookup\n    include ClassVarContainer\n\n    getter instance_type : VirtualType\n\n    def initialize(program, @instance_type)\n      super(program)\n    end\n\n    def metaclass\n      program.class_type\n    end\n\n    def parents\n      @parents ||= [instance_type.superclass.try(&.metaclass) || program.class_type] of Type\n    end\n\n    def leaf?\n      instance_type.leaf?\n    end\n\n    # Given `Foo+.class` returns `Foo` (not `Foo.class`)\n    delegate base_type, to: instance_type\n\n    delegate lookup_first_def, to: instance_type.metaclass\n\n    def replace_type_parameters(instance)\n      base_type.replace_type_parameters(instance).virtual_type.metaclass\n    end\n\n    def each_concrete_type(&)\n      instance_type.subtypes.each do |type|\n        yield type.metaclass\n      end\n    end\n\n    def lookup_class_var?(name)\n      class_var = @class_vars.try &.[name]?\n      return class_var if class_var\n\n      class_var = base_type.instance_type.lookup_class_var?(name)\n      if class_var\n        var = MetaTypeVar.new(name, class_var.type)\n        var.owner = self\n        var.thread_local = class_var.thread_local?\n        var.initializer = class_var.initializer\n        var.bind_to(class_var)\n        self.class_vars[name] = var\n        return var\n      end\n\n      nil\n    end\n\n    def implements?(other_type)\n      base_type.metaclass.implements?(other_type)\n    end\n\n    def to_s_with_options(io : IO, skip_union_parens : Bool = false, generic_args : Bool = true, codegen : Bool = false) : Nil\n      instance_type.to_s_with_options(io, codegen: codegen)\n      io << \".class\"\n    end\n\n    def type_desc\n      \"metaclass\"\n    end\n  end\nend\n\nprivate def add_to_including_types(type : Crystal::GenericType, all_types)\n  type.each_instantiated_type do |instance|\n    # Unbound generic types are not concrete types\n    next if instance.unbound?\n\n    # Abstract types also shouldn't form the union of including types\n    next if instance.abstract?\n\n    all_types << instance unless all_types.includes?(instance)\n  end\n  type.subclasses.each do |subclass|\n    add_to_including_types subclass, all_types\n  end\nend\n\nprivate def add_to_including_types(type : Crystal::NonGenericModuleType | Crystal::GenericModuleInstanceType, all_types)\n  type.add_to_including_types(all_types)\nend\n\nprivate def add_to_including_types(type, all_types)\n  virtual_type = type.virtual_type\n  all_types << virtual_type unless all_types.includes?(virtual_type)\nend\n\nprivate def add_instance_var_initializer(including_types, name, value, meta_vars)\n  including_types.try &.each do |type|\n    case type\n    when Crystal::Program, Crystal::FileModule\n      # skip\n    when Crystal::NonGenericModuleType\n      type.add_instance_var_initializer(name, value, meta_vars)\n    when Crystal::NonGenericClassType\n      type.add_instance_var_initializer(name, value, meta_vars)\n    when Crystal::GenericClassType\n      type.add_instance_var_initializer(name, value, meta_vars)\n    when Crystal::GenericModuleType\n      type.add_instance_var_initializer(name, value, meta_vars)\n    else\n      # skip\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/util.cr",
    "content": "require \"path\"\n\nmodule Crystal\n  def self.relative_filename(filename)\n    return filename unless filename.is_a?(String)\n\n    if base_file = filename.lchop? Dir.current\n      ::Path::SEPARATORS.each do |sep|\n        if file_prefix = base_file.lchop? sep\n          return file_prefix\n        end\n      end\n      return base_file\n    end\n    filename\n  end\n\n  def self.error(msg, color, exit_code : Int = 1, stderr = STDERR, leading_error = true) : NoReturn\n    error(msg, color, nil, stderr, leading_error)\n    exit(exit_code)\n  end\n\n  def self.error(msg, color, exit_code : Nil, stderr = STDERR, leading_error = true)\n    stderr.print \"Error: \".colorize.toggle(color).red.bold if leading_error\n    stderr.puts msg.colorize.toggle(color).bright\n  end\n\n  def self.tempfile(basename)\n    CacheDir.instance.join(\"crystal-run-#{basename}.tmp\")\n  end\n\n  def self.temp_executable(basename)\n    name = tempfile(basename)\n    {% if flag?(:win32) %}\n      name += \".exe\"\n    {% end %}\n    name\n  end\n\n  def self.with_line_numbers(\n    source : String | Array(String),\n    highlight_line_number = nil,\n    color = false,\n    line_number_start = 1,\n  )\n    source = source.lines if source.is_a? String\n    line_number_padding = (source.size + line_number_start).to_s.size\n    source.map_with_index do |line, i|\n      line = line.to_s.chomp\n      line_number = (i + line_number_start).to_s.rjust(line_number_padding)\n      target = i + line_number_start == highlight_line_number\n      if target\n        if color\n          \" > #{line_number} | \".colorize.green.to_s + line.colorize.bold.to_s\n        else\n          \" > #{line_number} | \" + line\n        end\n      else\n        if color\n          \" > #{line_number} | \".colorize.dim.to_s + line\n        else\n          \"   #{line_number} | \" + line\n        end\n      end\n    end.join '\\n'\n  end\n\n  def self.normalize_path(path)\n    path = ::Path[path].normalize\n    path = ::Path[\".\"] / path unless path.anchor\n    path.to_s\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal/warnings.cr",
    "content": "module Crystal\n  # Which warnings to detect.\n  enum WarningLevel\n    None\n    All\n  end\n\n  # This collection handles warning detection, reporting, and related options.\n  # It is shared between a `Crystal::Compiler` and other components that need to\n  # produce warnings.\n  class WarningCollection\n    # Which kind of warnings we want to detect.\n    property level : WarningLevel = :all\n\n    @excluded_paths = [] of String\n    @lib_path : String?\n\n    # Whether to ignore the \"lib\" path for warning detection. Turned off by\n    # the `--exclude-warnings` command-line option.\n    def exclude_lib_path? : Bool\n      !@lib_path.nil?\n    end\n\n    def exclude_lib_path=(exclude : Bool)\n      @lib_path = exclude ? File.expand_path(Crystal.normalize_path(\"lib\")) : nil\n    end\n\n    # Detected warnings.\n    property infos = [] of String\n\n    # Whether the compiler will error if any warnings are detected.\n    property? error_on_warnings = false\n\n    def exclude_path(path : ::Path | String)\n      @excluded_paths << File.expand_path(Crystal.normalize_path(path))\n    end\n\n    def add_warning(node : ASTNode, message : String)\n      return unless @level.all?\n      return if ignore_warning_due_to_location?(node.location)\n\n      @infos << node.warning(message)\n    end\n\n    def add_warning_at(location : Location?, message : String)\n      return unless @level.all?\n      return if ignore_warning_due_to_location?(location)\n\n      if location\n        message = String.build do |io|\n          exception = SyntaxException.new message, location.line_number, location.column_number, location.filename\n          exception.warning = true\n          exception.append_to_s(io, nil)\n        end\n      end\n\n      @infos << message\n    end\n\n    def report(io : IO)\n      unless @infos.empty?\n        @infos.each do |message|\n          io.puts message\n          io.puts \"\\n\"\n        end\n        io.puts \"A total of #{@infos.size} warnings were found.\"\n      end\n    end\n\n    def ignore_warning_due_to_location?(location : Location?)\n      return false unless location\n\n      filename = location.original_filename\n      return false unless filename\n\n      if lib_path = @lib_path\n        return true if filename.starts_with?(lib_path)\n      end\n\n      @excluded_paths.any? do |path|\n        filename.starts_with?(path)\n      end\n    end\n  end\n\n  class ASTNode\n    def warning(message, inner = nil, exception_type = Crystal::TypeException)\n      # TODO extract message formatting from exceptions\n      String.build do |io|\n        exception = exception_type.for_node(self, message, inner)\n        exception.warning = true\n        exception.append_to_s(io, nil)\n      end\n    end\n  end\n\n  class Command\n    def report_warnings\n      @compiler.try &.warnings.report(STDERR)\n    end\n\n    def warnings_fail_on_exit?\n      compiler = @compiler\n      return false unless compiler\n\n      warnings = compiler.warnings\n      warnings.error_on_warnings? && !warnings.infos.empty?\n    end\n  end\nend\n"
  },
  {
    "path": "src/compiler/crystal.cr",
    "content": "# This is the file that is compiled to generate the\n# executable for the compiler.\n\n{% raise(\"Please use `make crystal` to build the compiler, or set the i_know_what_im_doing flag if you know what you're doing\") unless env(\"CRYSTAL_HAS_WRAPPER\") || flag?(\"i_know_what_im_doing\") %}\n\nrequire \"log\"\nrequire \"./requires\"\n\nLog.setup_from_env(default_level: :warn, default_sources: \"crystal.*\")\n\n{% if flag?(:execution_context) %}\n  Fiber::ExecutionContext.default.resize(Fiber::ExecutionContext.default_workers_count)\n{% end %}\n\nCrystal::Command.run\n"
  },
  {
    "path": "src/compiler/requires.cr",
    "content": "require \"./crystal/annotatable\"\nrequire \"./crystal/program\"\nrequire \"./crystal/*\"\nrequire \"./crystal/semantic/*\"\nrequire \"./crystal/macros/*\"\nrequire \"./crystal/codegen/*\"\n"
  },
  {
    "path": "src/complex.cr",
    "content": "# A complex number is a number represented in the form a + bi. In this form,\n# a and b are real numbers, and i is an imaginary number such as i² = -1.\n# The a is the real part of the number, and the b is the imaginary part of\n# the number.\n#\n# NOTE: To use `Complex`, you must explicitly import it with `require \"complex\"`\n#\n# ```\n# require \"complex\"\n#\n# Complex.new(1, 0)   # => 1.0 + 0.0.i\n# Complex.new(5, -12) # => 5.0 - 12.0.i\n#\n# 1.to_c # => 1.0 + 0.0.i\n# 1.i    # => 0.0 + 1.0.i\n# ```\nstruct Complex\n  # Returns the real part.\n  getter real : Float64\n\n  # Returns the imaginary part.\n  getter imag : Float64\n\n  def initialize(real : Number, imag : Number = 0)\n    @real = real.to_f\n    @imag = imag.to_f\n  end\n\n  def self.new(c : Complex)\n    c\n  end\n\n  # Determines whether `self` equals *other* or not.\n  def ==(other : Complex)\n    @real == other.real && @imag == other.imag\n  end\n\n  # :ditto:\n  def ==(other : Number)\n    @real == other && @imag.zero?\n  end\n\n  # :ditto:\n  def ==(other)\n    false\n  end\n\n  # Returns `self`.\n  def to_c\n    self\n  end\n\n  # Returns the value as a `Float64` if possible (the imaginary part should be exactly zero),\n  # raises otherwise.\n  def to_f64 : Float64\n    unless @imag.zero?\n      raise Exception.new \"Complex number with non-zero imaginary part can't be converted to real number\"\n    end\n    @real\n  end\n\n  # See `#to_f64`.\n  def to_f\n    to_f64\n  end\n\n  delegate to_i128, to_i64, to_i32, to_i16, to_i8, to: to_f64\n  delegate to_u128, to_u64, to_u32, to_u16, to_u8, to: to_f64\n  delegate to_f32, to: to_f64\n\n  # See `#to_i32`.\n  def to_i\n    to_i32\n  end\n\n  # Writes this complex object to an *io*.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(42, 2).to_s # => \"42.0 + 2.0i\"\n  # ```\n  def to_s(io : IO) : Nil\n    io << @real\n    io << (@imag.nan? || @imag.sign_bit > 0 ? \" + \" : \" - \")\n    io << Math.copysign(@imag, 1.0)\n    io << 'i'\n  end\n\n  # Writes this complex object to an *io*, surrounded by parentheses.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(42, 2).inspect # => \"(42.0 + 2.0i)\"\n  # ```\n  def inspect(io : IO) : Nil\n    io << '('\n    to_s(io)\n    io << ')'\n  end\n\n  # Returns the absolute value of this complex number in a\n  # number form, using the Pythagorean theorem.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(42, 2).abs  # => 42.04759208325728\n  # Complex.new(-42, 2).abs # => 42.04759208325728\n  # ```\n  def abs : Float64\n    Math.hypot(@real, @imag)\n  end\n\n  # Returns the square of absolute value in a number form.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(42, 2).abs2 # => 1768\n  # ```\n  def abs2 : Float64\n    @real * @real + @imag * @imag\n  end\n\n  # Returns the complex sign of `self`.\n  #\n  # If `self` is non-zero, the returned value has the same phase as `self` and\n  # absolute value `1.0`. If `self` is zero, returns `self`.\n  #\n  # The returned value's real and imaginary components always have the same\n  # signs as the respective components of `self`.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(7, -24).sign        # => (0.28 - 0.96.i)\n  # Complex.new(1.0 / 0.0, 24).sign # => (1.0 + 0.0.i)\n  # Complex.new(-0.0, +0.0).sign    # => (-0.0 + 0.0.i)\n  # ```\n  def sign : Complex\n    return self if zero?\n\n    if @real.nan? || @imag.nan?\n      return Complex.new(Float64::NAN, Float64::NAN)\n    end\n\n    return Complex.new(@real.sign, @imag) if @real != 0 && @imag == 0\n    return Complex.new(@real, @imag.sign) if @real == 0 && @imag != 0\n\n    case {real_inf_sign = @real.infinite?, imag_inf_sign = @imag.infinite?}\n    in {Nil, Nil}\n      phase.cis\n    in {Nil, Int32}\n      Complex.new(Math.copysign(0.0, @real), imag_inf_sign)\n    in {Int32, Nil}\n      Complex.new(real_inf_sign, Math.copysign(0.0, @imag))\n    in {Int32, Int32}\n      sqrt = Math.sqrt(0.5)\n      Complex.new(sqrt * real_inf_sign, sqrt * imag_inf_sign)\n    end\n  end\n\n  # Returns the phase of `self`.\n  def phase : Float64\n    Math.atan2(@imag, @real)\n  end\n\n  # Returns a `Tuple` with the `abs` value and the `phase`.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(42, 2).polar # => {42.047592083257278, 0.047583103276983396}\n  # ```\n  def polar : {Float64, Float64}\n    {abs, phase}\n  end\n\n  # Returns the conjugate of `self`.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(42, 2).conj  # => 42.0 - 2.0.i\n  # Complex.new(42, -2).conj # => 42.0 + 2.0.i\n  # ```\n  def conj : Complex\n    Complex.new(@real, -@imag)\n  end\n\n  # Returns the inverse of `self`.\n  def inv : Complex\n    conj / abs2\n  end\n\n  # Returns `self`.\n  def + : Complex\n    self\n  end\n\n  # Adds the value of `self` to *other*.\n  def +(other : Complex) : Complex\n    Complex.new(@real + other.real, @imag + other.imag)\n  end\n\n  # :ditto:\n  def +(other : Number) : Complex\n    Complex.new(@real + other, @imag)\n  end\n\n  # Returns the opposite of `self`.\n  def - : Complex\n    Complex.new(-@real, -@imag)\n  end\n\n  # Removes the value of *other* from `self`.\n  def -(other : Complex) : Complex\n    Complex.new(@real - other.real, @imag - other.imag)\n  end\n\n  # :ditto:\n  def -(other : Number) : Complex\n    Complex.new(@real - other, @imag)\n  end\n\n  # Multiplies `self` by *other*.\n  def *(other : Complex) : Complex\n    Complex.new(@real * other.real - @imag * other.imag, @real * other.imag + @imag * other.real)\n  end\n\n  # :ditto:\n  def *(other : Number) : Complex\n    Complex.new(@real * other, @imag * other)\n  end\n\n  # Divides `self` by *other*.\n  def /(other : Complex) : Complex\n    if other.real.nan? || other.imag.nan?\n      Complex.new(Float64::NAN, Float64::NAN)\n    elsif other.imag.abs < other.real.abs\n      r = other.imag / other.real\n      d = other.real + r * other.imag\n\n      if d.nan? || d == 0\n        Complex.new(Float64::NAN, Float64::NAN)\n      else\n        Complex.new((@real + @imag * r) / d, (@imag - @real * r) / d)\n      end\n    elsif other.imag == 0 # other.real == 0\n      Complex.new(@real / other.real, @imag / other.real)\n    else # 0 < other.real.abs <= other.imag.abs\n      r = other.real / other.imag\n      d = other.imag + r * other.real\n\n      if d.nan? || d == 0\n        Complex.new(Float64::NAN, Float64::NAN)\n      else\n        Complex.new((@real * r + @imag) / d, (@imag * r - @real) / d)\n      end\n    end\n  end\n\n  # :ditto:\n  def /(other : Number) : Complex\n    Complex.new(@real / other, @imag / other)\n  end\n\n  def clone\n    self\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher = real.hash(hasher)\n    hasher = imag.hash(hasher) unless imag.zero?\n    hasher\n  end\n\n  # Returns the number `0` in complex form.\n  def self.zero : Complex\n    new 0, 0\n  end\n\n  # Returns `true` if the complex number is zero.\n  # This means the real and imaginary are both zero.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Complex.new(0, 0).zero? # => true\n  # Complex.new(1, 0).zero? # => false\n  # Complex.new(0, 1).zero? # => false\n  # ````\n  def zero? : Bool\n    @real == 0 && @imag == 0\n  end\n\n  def self.additive_identity : self\n    zero\n  end\n\n  def self.multiplicative_identity : self\n    new 1, 0\n  end\n\n  # Rounds to the nearest *digits*.\n  def round(digits = 0) : Complex\n    Complex.new(@real.round(digits), @imag.round(digits))\n  end\nend\n\nstruct Number\n  # Returns a `Complex` object with the value of `self` as the real part.\n  def to_c : Complex\n    Complex.new(self, 0)\n  end\n\n  # Returns a `Complex` object with the value of `self` as the imaginary part.\n  def i : Complex\n    Complex.new(0, self)\n  end\n\n  def ==(other : Complex)\n    other == self\n  end\n\n  # [Cis](https://en.wikipedia.org/wiki/Cis_(mathematics)) is a mathematical notation representing `cos x + i sin x`.\n  #\n  # Returns a `Complex` object with real part `Math.cos(self)` and imaginary part `Math.sin(self)`, where `self` represents the angle in radians.\n  def cis : Complex\n    Complex.new(Math.cos(self), Math.sin(self))\n  end\n\n  def +(other : Complex) : Complex\n    Complex.new(self + other.real, other.imag)\n  end\n\n  def -(other : Complex) : Complex\n    Complex.new(self - other.real, -other.imag)\n  end\n\n  def *(other : Complex) : Complex\n    Complex.new(self * other.real, self * other.imag)\n  end\n\n  def /(other : Complex) : Complex\n    self * other.inv\n  end\nend\n\nmodule Math\n  # Calculates the exponential of *value*.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Math.exp(4 + 2.i) # => -22.720847417619233 + 49.645957334580565.i\n  # ```\n  def exp(value : Complex) : Complex\n    r = exp(value.real)\n    Complex.new(r * cos(value.imag), r * sin(value.imag))\n  end\n\n  # Calculates the natural logarithm of *value*.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Math.log(4 + 2.i) # => 1.4978661367769956 + 0.4636476090008061.i\n  # ```\n  def log(value : Complex) : Complex\n    Complex.new(Math.log(value.abs), value.phase)\n  end\n\n  # Calculates the logarithm of *value* to base 2.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Math.log2(4 + 2.i) # => 2.1609640474436813 + 0.6689021062254881.i\n  # ```\n  def log2(value : Complex) : Complex\n    log(value) / LOG2\n  end\n\n  # Calculates the logarithm of *value* to base 10.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Math.log10(4 + 2.i) # => 0.6505149978319906 + 0.20135959813668655.i\n  # ```\n  def log10(value : Complex) : Complex\n    log(value) / LOG10\n  end\n\n  # Calculates the square root of *value*.\n  # Inspired by the [following blog post](https://pavpanchekha.com/blog/casio-mathjs.html) of Pavel Panchekha on floating point precision.\n  #\n  # ```\n  # require \"complex\"\n  #\n  # Math.sqrt(4 + 2.i) # => 2.0581710272714924 + 0.48586827175664565.i\n  # ```\n  #\n  # Although the imaginary number is defined as i = sqrt(-1),\n  # calling `Math.sqrt` with a negative number will return `-NaN`.\n  # To obtain the result in the complex plane, `Math.sqrt` must\n  # be called with a complex number.\n  #\n  # ```\n  # Math.sqrt(-1.0)         # => -NaN\n  # Math.sqrt(-1.0 + 0.0.i) # => 0.0 + 1.0.i\n  # ```\n  def sqrt(value : Complex) : Complex\n    r = value.abs\n\n    re = if value.real >= 0\n           0.5 * sqrt(2.0 * (r + value.real))\n         else\n           value.imag.abs / sqrt(2.0 * (r - value.real))\n         end\n\n    im = if value.real <= 0\n           0.5 * sqrt(2.0 * (r - value.real))\n         else\n           value.imag.abs / sqrt(2.0 * (r + value.real))\n         end\n\n    Complex.new(re, value.imag >= 0 ? im : -im)\n  end\nend\n"
  },
  {
    "path": "src/compress/deflate/deflate.cr",
    "content": "require \"lib_z\"\nrequire \"./*\"\n\n# The Deflate module contains readers and writers of DEFLATE format compressed\n# data, as specified in [RFC 1951](https://www.ietf.org/rfc/rfc1951.txt).\n#\n# See `Gzip`, `Zip` and `Zlib` for modules that provide access\n# to DEFLATE-based file formats.\n#\n# NOTE: To use `Deflate` or its children, you must explicitly import it with `require \"compress/deflate\"`\nmodule Compress::Deflate\n  NO_COMPRESSION      =  0\n  BEST_SPEED          =  1\n  BEST_COMPRESSION    =  9\n  DEFAULT_COMPRESSION = -1\n\n  enum Strategy\n    FILTERED     = 1\n    HUFFMAN_ONLY = 2\n    RLE          = 3\n    FIXED        = 4\n    DEFAULT      = 0\n  end\n\n  class Error < Exception\n    def initialize(ret, stream)\n      msg = stream.msg\n      msg = LibZ.zError(ret) if msg.null?\n\n      if msg\n        error_msg = String.new(msg)\n        super(\"deflate: #{error_msg}\")\n      else\n        super(\"deflate: #{ret}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/deflate/reader.cr",
    "content": "# A read-only `IO` object to decompress data in the DEFLATE format.\n#\n# Instances of this class wrap another IO object. When you read from this instance\n# instance, it reads data from the underlying IO, decompresses it, and returns\n# it to the caller.\nclass Compress::Deflate::Reader < IO\n  include IO::Buffered\n\n  # If `#sync_close?` is `true`, closing this IO will close the underlying IO.\n  property? sync_close : Bool\n\n  # Returns `true` if this reader is closed.\n  getter? closed = false\n\n  # Dictionary passed in the constructor\n  getter dict : Bytes?\n\n  # Peeked bytes from the underlying IO\n  @peek : Bytes?\n\n  # Creates an instance of Flate::Reader.\n  def initialize(@io : IO, @sync_close : Bool = false, @dict : Bytes? = nil)\n    @buf = uninitialized UInt8[1] # input buffer used by zlib\n    @stream = LibZ::ZStream.new\n    @stream.zalloc = LibZ::AllocFunc.new { |opaque, items, size| GC.malloc(items * size) }\n    @stream.zfree = LibZ::FreeFunc.new { |opaque, address| GC.free(address) }\n    ret = LibZ.inflateInit2(pointerof(@stream), -LibZ::MAX_BITS, LibZ.zlibVersion, sizeof(LibZ::ZStream))\n    raise Compress::Deflate::Error.new(ret, @stream) unless ret.ok?\n\n    @peek = nil\n    @end = false\n  end\n\n  # Creates a new reader from the given *io*, yields it to the given block,\n  # and closes it at its end.\n  def self.open(io : IO, sync_close : Bool = false, dict : Bytes? = nil, &)\n    reader = new(io, sync_close: sync_close, dict: dict)\n    yield reader ensure reader.close\n  end\n\n  # Creates an instance of Flate::Reader for the gzip format.\n  # has written.\n  def self.gzip(input, sync_close : Bool = false) : self\n    new input, wbits: GZIP, sync_close: sync_close\n  end\n\n  # Creates an instance of Flate::Reader for the gzip format, yields it to the given block, and closes\n  # it at its end.\n  def self.gzip(input, sync_close : Bool = false, &)\n    reader = gzip input, sync_close: sync_close\n    yield reader ensure reader.close\n  end\n\n  # Always raises `IO::Error` because this is a read-only `IO`.\n  def unbuffered_write(slice : Bytes) : NoReturn\n    raise IO::Error.new \"Can't write to Compress::Deflate::Reader\"\n  end\n\n  # See `IO#read`.\n  def unbuffered_read(slice : Bytes) : Int32\n    check_open\n\n    return 0 if slice.empty?\n    return 0 if @end\n\n    while true\n      if @stream.avail_in == 0\n        # Try to peek into the underlying IO, so we can feed more\n        # data into zlib\n        @peek = @io.peek\n        if peek = @peek\n          @stream.next_in = peek\n          @stream.avail_in = peek.size\n        else\n          # If peeking is not possible, we are cautious and\n          # read byte per byte to avoid reading more data beyond\n          # the compressed data (for example, if the compressed stream\n          # is part of a zip/gzip file).\n          @stream.next_in = @buf.to_unsafe\n          @stream.avail_in = @io.read(@buf.to_slice).to_u32\n        end\n      end\n\n      old_avail_in = @stream.avail_in\n\n      @stream.avail_out = slice.size.to_u32\n      @stream.next_out = slice.to_unsafe\n\n      ret = LibZ.inflate(pointerof(@stream), LibZ::Flush::NO_FLUSH)\n      read_bytes = slice.size - @stream.avail_out\n\n      # If we were able to peek, skip the used bytes in the underlying IO\n      avail_in_diff = old_avail_in - @stream.avail_in\n      if @peek && avail_in_diff > 0\n        @io.skip(avail_in_diff)\n      end\n\n      case ret\n      when .need_dict?\n        if dict = @dict\n          ret = LibZ.inflateSetDictionary(pointerof(@stream), dict, dict.size)\n          next if ret.ok?\n        end\n\n        raise Compress::Deflate::Error.new(ret, @stream)\n      when .errno?,\n           .data_error?,\n           .mem_error?,\n           .buf_error?,\n           .version_error?\n        raise Compress::Deflate::Error.new(ret, @stream)\n      when .stream_end?\n        @end = true\n        return read_bytes\n      else\n        # LibZ.inflate might not write any data to the output slice because\n        # it might need more input. We can know this happened because *ret*\n        # is not STREAM_END.\n        if read_bytes == 0\n          next\n        else\n          return read_bytes\n        end\n      end\n    end\n  end\n\n  def unbuffered_flush : NoReturn\n    raise IO::Error.new \"Can't flush Compress::Deflate::Reader\"\n  end\n\n  # Closes this reader.\n  def unbuffered_close : Nil\n    return if @closed\n    @closed = true\n\n    ret = LibZ.inflateEnd(pointerof(@stream))\n    raise Compress::Deflate::Error.new(ret, @stream) unless ret.ok?\n\n    @io.close if @sync_close\n  end\n\n  def unbuffered_rewind : Nil\n    check_open\n\n    @io.rewind\n\n    initialize(@io, @sync_close, @dict)\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\nend\n"
  },
  {
    "path": "src/compress/deflate/writer.cr",
    "content": "# A write-only `IO` object to compress data in the DEFLATE format.\n#\n# Instances of this class wrap another IO object. When you write to this\n# instance, it compresses the data and writes it to the underlying IO.\n#\n# NOTE: unless created with a block, `close` must be invoked after all\n# data has been written to a Flate::Writer instance.\nclass Compress::Deflate::Writer < IO\n  # If `#sync_close?` is `true`, closing this IO will close the underlying IO.\n  property? sync_close : Bool\n\n  # Creates an instance of Flate::Writer. `close` must be invoked after all data\n  # has written.\n  def initialize(@output : IO, level : Int32 = Compress::Deflate::DEFAULT_COMPRESSION,\n                 strategy : Compress::Deflate::Strategy = Compress::Deflate::Strategy::DEFAULT,\n                 @sync_close : Bool = false, @dict : Bytes? = nil)\n    unless -1 <= level <= 9\n      raise ArgumentError.new(\"Invalid Flate level: #{level} (must be in -1..9)\")\n    end\n\n    @buf = uninitialized UInt8[8192] # output buffer used by zlib\n    @stream = LibZ::ZStream.new\n    @stream.zalloc = LibZ::AllocFunc.new { |opaque, items, size| GC.malloc(items * size) }\n    @stream.zfree = LibZ::FreeFunc.new { |opaque, address| GC.free(address) }\n    @closed = false\n    ret = LibZ.deflateInit2(pointerof(@stream), level, LibZ::Z_DEFLATED, -LibZ::MAX_BITS, LibZ::DEF_MEM_LEVEL,\n      strategy.value, LibZ.zlibVersion, sizeof(LibZ::ZStream))\n    raise Compress::Deflate::Error.new(ret, @stream) unless ret.ok?\n  end\n\n  # Creates a new writer for the given *io*, yields it to the given block,\n  # and closes it at its end.\n  def self.open(io : IO, level : Int32 = Compress::Deflate::DEFAULT_COMPRESSION,\n                strategy : Compress::Deflate::Strategy = Compress::Deflate::Strategy::DEFAULT,\n                sync_close : Bool = false, dict : Bytes? = nil, &)\n    writer = new(io, level: level, strategy: strategy, sync_close: sync_close, dict: dict)\n    yield writer ensure writer.close\n  end\n\n  # Always raises `IO::Error` because this is a write-only `IO`.\n  def read(slice : Bytes) : NoReturn\n    raise \"Can't read from Flate::Writer\"\n  end\n\n  # See `IO#write`.\n  def write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    @stream.avail_in = slice.size\n    @stream.next_in = slice\n    consume_output LibZ::Flush::NO_FLUSH\n  end\n\n  # See `IO#flush`.\n  def flush : Nil\n    return if @closed\n\n    consume_output LibZ::Flush::SYNC_FLUSH\n    @output.flush\n  end\n\n  # Closes this writer. Must be invoked after all data has been written.\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    @stream.avail_in = 0\n    @stream.next_in = Pointer(UInt8).null\n    consume_output LibZ::Flush::FINISH\n\n    ret = LibZ.deflateEnd(pointerof(@stream))\n    raise Compress::Deflate::Error.new(ret, @stream) unless ret.ok?\n\n    @output.close if @sync_close\n  end\n\n  # Returns `true` if this IO is closed.\n  def closed? : Bool\n    @closed\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  private def consume_output(flush)\n    loop do\n      @stream.next_out = @buf.to_unsafe\n      @stream.avail_out = @buf.size.to_u32\n      LibZ.deflate(pointerof(@stream), flush) # no bad return value\n      @output.write(@buf.to_slice[0, @buf.size - @stream.avail_out])\n      break if @stream.avail_out != 0\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/gzip/gzip.cr",
    "content": "require \"compress/deflate\"\nrequire \"digest/crc32\"\n\n# The Gzip module contains readers and writers of gzip format compressed\n# data, as specified in [RFC 1952](https://www.ietf.org/rfc/rfc1952.txt).\n#\n# NOTE: To use `Gzip` or its children, you must explicitly import it with `require \"compress/gzip\"`\nmodule Compress::Gzip\n  NO_COMPRESSION      = Compress::Deflate::NO_COMPRESSION\n  BEST_SPEED          = Compress::Deflate::BEST_SPEED\n  BEST_COMPRESSION    = Compress::Deflate::BEST_COMPRESSION\n  DEFAULT_COMPRESSION = Compress::Deflate::DEFAULT_COMPRESSION\n\n  private ID1     = 0x1f_u8\n  private ID2     = 0x8b_u8\n  private DEFLATE =    8_u8\n\n  class Error < Exception\n  end\nend\n\nrequire \"./*\"\n"
  },
  {
    "path": "src/compress/gzip/header.cr",
    "content": "# A header in a gzip stream.\nclass Compress::Gzip::Header\n  property modification_time : Time\n  property os : UInt8\n  property extra = Bytes.empty\n  property name : String?\n  property comment : String?\n\n  # :nodoc:\n  @[Flags]\n  enum Flg : UInt8\n    TEXT\n    HCRC\n    EXTRA\n    NAME\n    COMMENT\n  end\n\n  # :nodoc:\n  def initialize\n    @modification_time = Time.utc\n    @os = 255_u8 # Unknown\n  end\n\n  # :nodoc:\n  def initialize(first_byte : UInt8, io : IO)\n    header = uninitialized UInt8[10]\n    header[0] = first_byte\n    io.read_fully(header.to_slice + 1)\n\n    if header[0] != ID1 || header[1] != ID2 || header[2] != DEFLATE\n      raise Error.new(\"Invalid gzip header\")\n    end\n\n    flg = Flg.new(header[3])\n\n    seconds = IO::ByteFormat::LittleEndian.decode(UInt32, header.to_slice[4, 4])\n    @modification_time = Time.unix(seconds).to_local\n\n    _xfl = header[8]\n    @os = header[9]\n\n    if flg.extra?\n      xlen = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)\n      @extra = Bytes.new(xlen)\n      io.read_fully(@extra)\n    end\n\n    if flg.name?\n      @name = io.gets('\\0', chomp: true)\n    end\n\n    if flg.comment?\n      @comment = io.gets('\\0', chomp: true)\n    end\n\n    if flg.hcrc?\n      _crc16 = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)\n      # TODO check crc16\n    end\n  end\n\n  # :nodoc:\n  def to_io(io)\n    # header\n    io.write_byte ID1\n    io.write_byte ID2\n\n    # compression method\n    io.write_byte DEFLATE\n\n    # flg\n    flg = Flg::None\n    flg |= Flg::EXTRA unless @extra.empty?\n    flg |= Flg::NAME if @name\n    flg |= Flg::COMMENT if @comment\n    io.write_byte flg.value\n\n    # time\n    io.write_bytes(modification_time.to_unix.to_u32, IO::ByteFormat::LittleEndian)\n\n    # xfl\n    io.write_byte 0_u8\n\n    # os\n    io.write_byte os\n\n    unless @extra.empty?\n      io.write_bytes(@extra.size.to_u16, IO::ByteFormat::LittleEndian)\n      io.write(@extra)\n    end\n\n    if name = @name\n      io << name\n      io.write_byte 0_u8\n    end\n\n    if comment = @comment\n      io << comment\n      io.write_byte 0_u8\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/gzip/reader.cr",
    "content": "# A read-only `IO` object to decompress data in the gzip format.\n#\n# Instances of this class wrap another IO object. When you read from this instance\n# instance, it reads data from the underlying IO, decompresses it, and returns\n# it to the caller.\n#\n# NOTE: A gzip stream can contain zero or more members. If it contains\n# no members, `header` will be `nil`. If it contains one or more\n# members, only the first header will be recorded here. This is\n# because gzipping multiple members is not common as one usually\n# combines gzip with tar. If, however, multiple members are present\n# then reading from this reader will return the concatenation of\n# all the members.\n#\n# ### Example: decompress a gzip file\n#\n# ```\n# require \"compress/gzip\"\n#\n# File.write(\"file.gzip\", Bytes[31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 75, 76, 74, 6, 0, 194, 65, 36, 53, 3, 0, 0, 0])\n#\n# string = File.open(\"file.gzip\") do |file|\n#   Compress::Gzip::Reader.open(file) do |gzip|\n#     gzip.gets_to_end\n#   end\n# end\n# string # => \"abc\"\n# ```\nclass Compress::Gzip::Reader < IO\n  include IO::Buffered\n\n  # Whether to close the enclosed `IO` when closing this reader.\n  property? sync_close = false\n\n  # Returns `true` if this reader is closed.\n  getter? closed = false\n\n  # Returns the first header in the gzip stream, if any.\n  getter header : Header?\n\n  @flate_io : Compress::Deflate::Reader?\n\n  # Creates a new reader from the given *io*.\n  def initialize(@io : IO, @sync_close = false)\n    # CRC32 of written data\n    @crc32 = ::Digest::CRC32.initial\n\n    # Total size of the original (uncompressed) input data modulo 2^32.\n    @isize = 0_u32\n\n    first_byte = @io.read_byte\n\n    # A gzip file could be empty (have no members), so\n    # we account for that case\n    return unless first_byte\n\n    @header = Header.new(first_byte, @io)\n    @flate_io = Compress::Deflate::Reader.new(@io)\n  end\n\n  # Creates a new reader from the given *filename*.\n  def self.new(filename : String)\n    new(::File.new(filename), sync_close: true)\n  end\n\n  # Creates a new reader from the given *io*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(io : IO, sync_close = false, &)\n    reader = new(io, sync_close: sync_close)\n    yield reader ensure reader.close\n  end\n\n  # Creates a new reader from the given *filename*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(filename : String, &)\n    reader = new(filename)\n    yield reader ensure reader.close\n  end\n\n  # See `IO#read`.\n  def unbuffered_read(slice : Bytes) : Int32\n    check_open\n\n    return 0 if slice.empty?\n\n    while true\n      flate_io = @flate_io\n      return 0 unless flate_io\n\n      read_bytes = flate_io.read(slice)\n      if read_bytes == 0\n        crc32 = @io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)\n        isize = @io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)\n\n        if crc32 != @crc32\n          raise Compress::Gzip::Error.new(\"CRC32 checksum mismatch\")\n        end\n\n        if isize != @isize\n          raise Compress::Gzip::Error.new(\"isize mismatch\")\n        end\n\n        # Reset checksum and total size for next entry\n        @crc32 = ::Digest::CRC32.initial\n        @isize = 0_u32\n\n        # Check if another header with data comes\n        first_byte = @io.read_byte\n        if first_byte\n          Header.new(first_byte, @io)\n          @flate_io = Compress::Deflate::Reader.new(@io)\n        else\n          @flate_io = nil\n          break\n        end\n      else\n        # Update CRC32 and total data size\n        @crc32 = ::Digest::CRC32.update(slice[0, read_bytes], @crc32)\n\n        # Using wrapping addition here because isize is only 32 bits wide but\n        # uncompressed data size can be bigger.\n        @isize &+= read_bytes\n\n        break\n      end\n    end\n\n    read_bytes\n  end\n\n  # Always raises `IO::Error` because this is a read-only `IO`.\n  def unbuffered_write(slice : Bytes) : NoReturn\n    raise IO::Error.new(\"Can't write to Compress::Gzip::Reader\")\n  end\n\n  def unbuffered_flush : NoReturn\n    raise IO::Error.new \"Can't flush Compress::Gzip::Reader\"\n  end\n\n  # Closes this reader.\n  def unbuffered_close : Nil\n    return if @closed\n    @closed = true\n\n    @flate_io.try &.close\n    @io.close if @sync_close\n  end\n\n  def unbuffered_rewind : Nil\n    check_open\n\n    @io.rewind\n\n    @header = nil\n    @flate_io = nil\n\n    initialize(@io, @sync_close)\n  end\nend\n"
  },
  {
    "path": "src/compress/gzip/writer.cr",
    "content": "# A write-only `IO` object to compress data in the gzip format.\n#\n# Instances of this class wrap another `IO` object. When you write to this\n# instance, it compresses the data and writes it to the underlying `IO`.\n#\n# NOTE: unless created with a block, `close` must be invoked after all\n# data has been written to a `Gzip::Writer` instance.\n#\n# ### Example: compress a file\n#\n# ```\n# require \"compress/gzip\"\n#\n# File.write(\"file.txt\", \"abc\")\n#\n# File.open(\"./file.txt\", \"r\") do |input_file|\n#   File.open(\"./file.gzip\", \"w\") do |output_file|\n#     Compress::Gzip::Writer.open(output_file) do |gzip|\n#       IO.copy(input_file, gzip)\n#     end\n#   end\n# end\n# ```\nclass Compress::Gzip::Writer < IO\n  # Whether to close the enclosed `IO` when closing this writer.\n  property? sync_close = false\n\n  # Returns `true` if this writer is closed.\n  getter? closed = false\n\n  # The header to write to the gzip stream. It will be\n  # written just before the first write to this writer.\n  # Changes to the header after the first write are\n  # ignored.\n  getter header = Header.new\n\n  # Creates a new writer to the given *io*.\n  def initialize(@io : IO, @level = Compress::Gzip::DEFAULT_COMPRESSION, @sync_close = false)\n    # CRC32 of written data\n    @crc32 = ::Digest::CRC32.initial\n\n    # Total size of the original (uncompressed) input data modulo 2^32.\n    @isize = 0\n  end\n\n  # Creates a new writer to the given *filename*.\n  def self.new(filename : String, level = Compress::Gzip::DEFAULT_COMPRESSION)\n    new(::File.new(filename, \"w\"), level: level, sync_close: true)\n  end\n\n  # Creates a new writer to the given *io*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(io : IO, level = Compress::Gzip::DEFAULT_COMPRESSION, sync_close = false, &)\n    writer = new(io, level: level, sync_close: sync_close)\n    yield writer ensure writer.close\n  end\n\n  # Creates a new writer to the given *filename*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(filename : String, level = Compress::Gzip::DEFAULT_COMPRESSION, &)\n    writer = new(filename, level: level)\n    yield writer ensure writer.close\n  end\n\n  # Always raises `IO::Error` because this is a write-only `IO`.\n  def read(slice : Bytes) : NoReturn\n    raise IO::Error.new(\"Can't read from Gzip::Writer\")\n  end\n\n  # See `IO#write`.\n  def write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    flate_io = write_header\n    flate_io.write(slice)\n\n    # Update CRC32 and total data size\n    @crc32 = ::Digest::CRC32.update(slice, @crc32)\n\n    # Using wrapping addition here because isize is only 32 bits wide but\n    # uncompressed data size can be bigger.\n    @isize &+= slice.size\n  end\n\n  # Flushes data, forcing writing the gzip header if no\n  # data has been written yet.\n  #\n  # See `IO#flush`.\n  def flush : Nil\n    check_open\n\n    flate_io = write_header\n    flate_io.flush\n  end\n\n  # Closes this writer. Must be invoked after all data has been written.\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    flate_io = write_header\n    flate_io.close\n\n    @io.write_bytes @crc32, IO::ByteFormat::LittleEndian\n    @io.write_bytes @isize, IO::ByteFormat::LittleEndian\n\n    @io.close if @sync_close\n  end\n\n  private def write_header\n    flate_io = @flate_io\n    unless flate_io\n      flate_io = @flate_io = Compress::Deflate::Writer.new(@io, level: @level)\n      header.to_io(@io)\n    end\n    flate_io\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/checksum_reader.cr",
    "content": "module Compress::Zip\n  # Computes a CRC32 while reading from an underlying IO,\n  # optionally verifying the computed value against an\n  # expected one.\n  private class ChecksumReader < IO\n    getter crc32 = ::Digest::CRC32.initial\n\n    def initialize(@io : IO, @filename : String, verify @expected_crc32 : UInt32? = nil)\n    end\n\n    def read(slice : Bytes) : Int32\n      read_bytes = @io.read(slice)\n      if read_bytes == 0\n        if (expected_crc32 = @expected_crc32) && crc32 != expected_crc32\n          raise Compress::Zip::Error.new(\"Checksum failed for entry #{@filename} (expected #{expected_crc32}, got #{crc32}\")\n        end\n      else\n        @crc32 = ::Digest::CRC32.update(slice[0, read_bytes], @crc32)\n      end\n      read_bytes\n    end\n\n    def peek : Bytes?\n      @io.peek\n    end\n\n    def write(slice : Bytes) : NoReturn\n      raise IO::Error.new \"Can't write to Zip::Reader or Zip::File entry\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/checksum_writer.cr",
    "content": "module Compress::Zip\n  # Counts written bytes and optionally computes a CRC32\n  # checksum while writing to an underlying IO.\n  private class ChecksumWriter < IO\n    getter count = 0_u32\n    getter crc32 = ::Digest::CRC32.initial\n    getter! io : IO\n\n    def initialize(@compute_crc32 = false)\n    end\n\n    def read(slice : Bytes) : NoReturn\n      raise IO::Error.new \"Can't read from Zip::Writer entry\"\n    end\n\n    def write(slice : Bytes) : Nil\n      return if slice.empty?\n\n      @count += slice.size\n      @crc32 = ::Digest::CRC32.update(slice, @crc32) if @compute_crc32\n      io.write(slice)\n    end\n\n    def io=(@io)\n      @count = 0_u32\n      @crc32 = ::Digest::CRC32.initial\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/compression_method.cr",
    "content": "# Supported compression methods in the current implementation.\nenum Compress::Zip::CompressionMethod : UInt16\n  STORED   = 0\n  DEFLATED = 8\nend\n"
  },
  {
    "path": "src/compress/zip/file.cr",
    "content": "require \"./file_info\"\n\n# Provides random read access to zip file entries stores inside\n# a `File` or an `IO::Memory`.\n#\n# ### Example\n#\n# ```\n# require \"compress/zip\"\n#\n# Compress::Zip::File.open(\"./file.zip\") do |file|\n#   # Iterate through all entries printing their filename and contents\n#   file.entries.each do |entry|\n#     p entry.filename\n#     entry.open do |io|\n#       p io.gets_to_end\n#     end\n#   end\n#\n#   # Random access to entries by filename is also provided\n#   entry = file[\"some_file.txt\"]\n#   entry.open do |io|\n#     p io.gets_to_end\n#   end\n# end\n# ```\nclass Compress::Zip::File\n  # Returns all entries inside this zip file.\n  getter entries : Array(Entry)\n\n  # Returns `true` if this zip file is closed.\n  getter? closed = false\n\n  # Returns the zip file comment.\n  getter comment = \"\"\n\n  # Opens a `Zip::File` for reading from the given *io*.\n  def initialize(@io : IO, @sync_close = false)\n    directory_end_offset = find_directory_end_offset\n    entries_size, directory_offset = read_directory_end(directory_end_offset)\n    @entries = Array(Entry).new(entries_size)\n    @entries_by_filename = {} of String => Entry\n    read_entries(directory_offset, entries_size)\n  end\n\n  # Opens a `Zip::File` for reading from the given *filename*.\n  def self.new(filename : Path | String)\n    new(::File.new(filename), sync_close: true)\n  end\n\n  # Opens a `Zip::File` for reading from the given *io*, yields\n  # it to the given block, and closes it at the end.\n  def self.open(io : IO, sync_close = false, &)\n    zip = new io, sync_close\n    yield zip ensure zip.close\n  end\n\n  # Opens a `Zip::File` for reading from the given *filename*, yields\n  # it to the given block, and closes it at the end.\n  def self.open(filename : Path | String, &)\n    zip = new filename\n    yield zip ensure zip.close\n  end\n\n  # Returns the entry that has the given filename, or\n  # raises `KeyError` if no such entry exists.\n  def [](filename : Path | String) : Entry\n    self[filename]? || raise(KeyError.new(\"Missing zip entry: #{filename}\"))\n  end\n\n  # Returns the entry that has the given filename, or\n  # `nil` if no such entry exists.\n  def []?(filename : Path | String) : Entry?\n    @entries_by_filename[filename.to_s]?\n  end\n\n  # Closes this zip file.\n  def close : Nil\n    return if @closed\n    @closed = true\n    if @sync_close\n      @io.close\n    end\n  end\n\n  # Tries to find the directory end offset (by searching its signature)\n  # in the last 64, 1024 and 65K bytes (in that order)\n  private def find_directory_end_offset\n    find_directory_end_offset(64) ||\n      find_directory_end_offset(1024) ||\n      find_directory_end_offset(65 * 1024) ||\n      raise Compress::Zip::Error.new(\"Couldn't find directory end signature in the last 65KB\")\n  end\n\n  private def find_directory_end_offset(buf_size)\n    @io.seek(0, IO::Seek::End)\n    size = @io.pos\n\n    buf_size = Math.min(buf_size, size)\n    @io.pos = size - buf_size\n\n    buf = Bytes.new(buf_size)\n    @io.read_fully(buf)\n\n    i = buf_size - 1 - 4\n    while i >= 0\n      # These are the bytes the make up the directory end signature,\n      # according to the spec\n      break if buf[i] == 0x50 && buf[i + 1] == 0x4b && buf[i + 2] == 0x05 && buf[i + 3] == 0x06\n      i -= 1\n    end\n\n    i == -1 ? nil : size - buf_size + i\n  end\n\n  private def read_directory_end(directory_end_offset)\n    @io.pos = directory_end_offset\n\n    signature = read UInt32\n    if signature != Zip::END_OF_CENTRAL_DIRECTORY_HEADER_SIGNATURE\n      raise Error.new(\"Expected end of central directory header signature, not 0x#{signature.to_s(16)}\")\n    end\n\n    read UInt16                    # number of this disk\n    read UInt16                    # disk start\n    read UInt16                    # number of entries in disk\n    entries_size = read UInt16     # number of total entries\n    read UInt32                    # size of the central directory\n    directory_offset = read UInt32 # offset of central directory\n    comment_length = read UInt16   # comment length\n    if comment_length != 0\n      @comment = @io.read_string(comment_length)\n    end\n    {entries_size, directory_offset}\n  end\n\n  private def read_entries(directory_offset, entries_size)\n    @io.pos = directory_offset\n\n    entries_size.times do\n      signature = read UInt32\n      if signature != Zip::CENTRAL_DIRECTORY_HEADER_SIGNATURE\n        raise Error.new(\"Expected directory header signature, not 0x#{signature.to_s(16)}\")\n      end\n\n      entry = Entry.new(@io)\n      @entries << entry\n      @entries_by_filename[entry.filename] = entry\n    end\n  end\n\n  private def read(type)\n    @io.read_bytes(type, IO::ByteFormat::LittleEndian)\n  end\n\n  # An entry inside a `Zip::File`.\n  #\n  # Use the `open` method to read from it.\n  class Entry\n    include FileInfo\n\n    # :nodoc:\n    def initialize(@io : IO)\n      super(at_central_directory_header: io)\n    end\n\n    # Yields an `IO` to read this entry's contents.\n    # Multiple entries can be opened and read concurrently.\n    def open(&)\n      @io.read_at(data_offset.to_i32, compressed_size.to_i32) do |io|\n        io = decompressor_for(io, is_sized: true)\n        checksum_reader = ChecksumReader.new(io, filename, verify: crc32)\n        yield checksum_reader\n      end\n    end\n\n    private getter(data_offset : UInt32) do\n      # Apparently a zip entry might have different extra bytes\n      # in the local file header and central directory header,\n      # so to know the data offset we must read them again.\n      #\n      # The bytes inside a local file header, from the signature\n      # and up to the extra length field, sum up 30 bytes.\n      #\n      # This 30 and 22 constants are burned inside the zip spec and\n      # will never change.\n      @io.read_at(offset.to_i32, 30) do |io|\n        # at least check that the signature is OK (these are 4 bytes)\n        signature = read(io, UInt32)\n        if signature != FileInfo::SIGNATURE\n          raise Compress::Zip::Error.new(\"Wrong local file header signature (expected 0x#{FileInfo::SIGNATURE.to_s(16)}, got 0x#{signature.to_s(16)})\")\n        end\n\n        # Skip most of the headers except filename length and extra length\n        # (skip 22, so we already read 26 bytes)\n        io.skip(22)\n\n        # With these two we read 4 bytes more, so we are at 30 bytes\n        filename_length = read(io, UInt16)\n        extra_length = read(io, UInt16)\n\n        # The data of this entry comes at the local file header offset\n        # plus 30 bytes (the ones we just skipped) plus skipping the\n        # filename's bytes plus skipping the extra bytes.\n        @offset + 30 + filename_length + extra_length\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/file_info.cr",
    "content": "# Base type for file information related to zip entries.\nmodule Compress::Zip::FileInfo\n  SIGNATURE                 = 0x04034b50\n  DATA_DESCRIPTOR_SIGNATURE = 0x08074b50\n\n  DEFLATE_END_SIGNATURE = Bytes[80, 75, 7, 8, read_only: true]\n\n  property version : UInt16 = Zip::VERSION\n  property general_purpose_bit_flag = 0_u16\n  property compression_method = CompressionMethod::DEFLATED\n  property time : Time\n  property crc32 = 0_u32\n  property compressed_size = 0_u32\n  property uncompressed_size = 0_u32\n  property filename = \"\"\n  property extra = Bytes.empty\n  property comment = \"\"\n  property offset = 0_u32\n\n  # :nodoc:\n  def initialize(*, at_file_header io : IO)\n    @version = read(io, UInt16)\n    file_name_length, extra_field_length, time = initialize_meta(io)\n    @time = time\n    @filename = io.read_string(file_name_length)\n    if extra_field_length != 0\n      @extra = Bytes.new(extra_field_length)\n      io.read_fully(@extra)\n    end\n  end\n\n  # :nodoc:\n  def initialize(*, at_central_directory_header io : IO)\n    read io, UInt16            # version made by\n    @version = read io, UInt16 # version needed to extract\n    file_name_length, extra_field_length, time = initialize_meta(io)\n    @time = time\n    file_comment_length = read(io, UInt16) # file comment length\n    read(io, UInt16)                       # disk number start\n    read(io, UInt16)                       # internal file attribute\n    read(io, UInt32)                       # external file attribute\n    @offset = read(io, UInt32)             # relative offset of local header\n    @filename = io.read_string(file_name_length)\n    if extra_field_length != 0\n      @extra = Bytes.new(extra_field_length)\n      io.read_fully(@extra)\n    end\n    if file_comment_length != 0\n      @comment = io.read_string(file_comment_length)\n    end\n  end\n\n  # :nodoc:\n  def initialize_meta(io : IO)\n    @general_purpose_bit_flag = read(io, UInt16)\n    @compression_method = CompressionMethod.new(read(io, UInt16))\n    time = read(io, UInt16)\n    date = read(io, UInt16)\n    time = from_dos(date, time)\n    @crc32 = read(io, UInt32)\n    @compressed_size = read(io, UInt32)\n    @uncompressed_size = read(io, UInt32)\n    file_name_length = read(io, UInt16)\n    extra_field_length = read(io, UInt16)\n    {file_name_length, extra_field_length, time}\n  end\n\n  def initialize(@filename : String, @time = Time.utc, @comment = \"\", @extra = Bytes.empty)\n  end\n\n  # Returns `true` if this entry is a directory.\n  def dir? : Bool\n    filename.ends_with?('/')\n  end\n\n  # Returns `true` if this entry is a file.\n  def file? : Bool\n    !dir?\n  end\n\n  protected def to_io(io : IO)\n    write io, SIGNATURE # 4\n    write io, @version  # 2\n    meta_count = meta_to_io(io)\n    io << @filename\n    io.write(extra)\n    meta_count + 6 + @filename.bytesize + extra.size\n  end\n\n  protected def meta_to_io(io : IO)\n    write io, @general_purpose_bit_flag # 2\n    write io, @compression_method.value # 2\n    date, time = to_dos\n    write io, time                      # 2\n    write io, date                      # 2\n    write io, @crc32                    # 4\n    write io, @compressed_size          # 4\n    write io, @uncompressed_size        # 4\n    write io, @filename.bytesize.to_u16 # filename length (2)\n    write io, extra.size.to_u16         # extra field length (2)\n    24                                  # the 24 bytes we just wrote\n  end\n\n  protected def write_data_descriptor(io : IO)\n    io.write FileInfo::DEFLATE_END_SIGNATURE # 4\n    write io, @crc32                         # 4\n    write io, @compressed_size               # 4\n    write io, @uncompressed_size             # 4\n    16                                       # the 16 bytes we just wrote\n  end\n\n  protected def decompressor_for(io, is_sized = false)\n    case compression_method\n    when .stored?\n      io = IO::Sized.new(io, compressed_size) unless is_sized\n    when .deflated?\n      if compressed_size == 0 && bit_3_set?\n        # Read until we end decompressing the deflate data,\n        # which has an unknown size\n      else\n        io = IO::Sized.new(io, compressed_size) unless is_sized\n      end\n\n      io = Compress::Deflate::Reader.new(io)\n    else\n      raise \"Unsupported compression method: #{compression_method}\"\n    end\n\n    io\n  end\n\n  protected def bit_3_set?\n    (general_purpose_bit_flag & 0b1000) != 0\n  end\n\n  # date is:\n  # - 7 bits for year (0 is 1980)\n  # - 4 bits for month\n  # - 5 bits for day\n  # time is:\n  # - 5 bits for hour\n  # - 6 bits for minute\n  # - 5 bits for seconds (0..29), precision of two seconds\n\n  private def from_dos(date, time)\n    year = 1980 + (date >> 9)\n    month = (date >> 5) & 0b1111\n    day = date & 0b11111\n\n    hour = time >> 11\n    minute = (time >> 5) & 0b111111\n    second = (time & 0b11111) * 2\n\n    Time.utc(year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i)\n  end\n\n  private def to_dos\n    date = ((@time.year - 1980) << 9) |\n           (@time.month << 5) |\n           @time.day\n\n    time = (@time.hour << 11) |\n           (@time.minute << 5) |\n           (@time.second // 2)\n\n    {date.to_u16, time.to_u16}\n  end\n\n  private def read(io, type)\n    io.read_bytes(type, IO::ByteFormat::LittleEndian)\n  end\n\n  private def write(io, value)\n    io.write_bytes(value, IO::ByteFormat::LittleEndian)\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/reader.cr",
    "content": "require \"./file_info\"\n\n# Reads zip file entries sequentially from an `IO`.\n#\n# NOTE: Entries might not have correct values\n# for crc32, compressed_size, uncompressed_size and comment,\n# because when reading a zip file directly from a stream this\n# information might be stored later in the zip stream.\n# If you need this information, consider using `Zip::File`.\n#\n# ### Example\n#\n# ```\n# require \"compress/zip\"\n#\n# Compress::Zip::Reader.open(\"./file.zip\") do |zip|\n#   zip.each_entry do |entry|\n#     p entry.filename\n#     p entry.file?\n#     p entry.dir?\n#     p entry.io.gets_to_end\n#   end\n# end\n# ```\nclass Compress::Zip::Reader\n  # Whether to close the enclosed `IO` when closing this reader.\n  property? sync_close = false\n\n  # Returns `true` if this reader is closed.\n  getter? closed = false\n\n  # Creates a new reader from the given *io*.\n  def initialize(@io : IO, @sync_close = false)\n    @reached_end = false\n    @read_data_descriptor = true\n  end\n\n  # Creates a new reader from the given *filename*.\n  def self.new(filename : Path | String)\n    new(::File.new(filename), sync_close: true)\n  end\n\n  # Creates a new reader from the given *io*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(io : IO, sync_close = false, &)\n    reader = new(io, sync_close: sync_close)\n    yield reader ensure reader.close\n  end\n\n  # Creates a new reader from the given *filename*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(filename : Path | String, &)\n    reader = new(filename)\n    yield reader ensure reader.close\n  end\n\n  # Reads the next entry in the zip, or `nil` if there\n  # are no more entries.\n  #\n  # After reading a next entry, previous entries can no\n  # longer be read (their `IO` will be closed.)\n  def next_entry : Entry?\n    return nil if @reached_end\n\n    if last_entry = @last_entry\n      last_entry.close\n      skip_data_descriptor(last_entry)\n    end\n\n    while true\n      signature = read UInt32\n\n      case signature\n      when FileInfo::SIGNATURE\n        # Found file info signature\n        break\n      when FileInfo::DATA_DESCRIPTOR_SIGNATURE\n        if last_entry && !@read_data_descriptor\n          # Consider the case where a data descriptor comes after\n          # a STORED entry: skip data descriptor and expect file signature next\n          read_data_descriptor(last_entry)\n          next\n        else\n          raise Error.new(\"Unexpected data descriptor when reading zip\")\n        end\n      else\n        # Other signature: we are done with entries (next comes metadata)\n        @reached_end = true\n        return nil\n      end\n    end\n\n    @last_entry = Entry.new(@io)\n  end\n\n  # Yields each entry in the zip to the given block.\n  def each_entry(&)\n    while entry = next_entry\n      yield entry\n    end\n  end\n\n  # Closes this zip reader.\n  def close : Nil\n    return if @closed\n    @closed = true\n    @io.close if @sync_close\n  end\n\n  private def skip_data_descriptor(entry)\n    if entry.compression_method.deflated? && entry.bit_3_set?\n      # The data descriptor signature is optional: if we\n      # find it, we read the data descriptor info normally;\n      # otherwise, the first four bytes are the crc32 value.\n      signature = read UInt32\n      if signature == FileInfo::DATA_DESCRIPTOR_SIGNATURE\n        read_data_descriptor(entry)\n      else\n        read_data_descriptor(entry, crc32: signature)\n      end\n      @read_data_descriptor = true\n    else\n      @read_data_descriptor = false\n      verify_checksum(entry)\n    end\n  end\n\n  private def read_data_descriptor(entry, crc32 = nil)\n    entry.crc32 = crc32 || (read UInt32)\n    entry.compressed_size = read UInt32\n    entry.uncompressed_size = read UInt32\n    verify_checksum(entry)\n  end\n\n  private def verify_checksum(entry)\n    if entry.crc32 != entry.checksum_io.crc32\n      raise Compress::Zip::Error.new(\"Checksum failed for entry #{entry.filename} (expected #{entry.crc32}, got #{entry.checksum_io.crc32}\")\n    end\n  end\n\n  private def read(type)\n    @io.read_bytes(type, IO::ByteFormat::LittleEndian)\n  end\n\n  # A entry inside a `Zip::Reader`.\n  #\n  # Use the `io` method to read from it.\n  class Entry\n    include FileInfo\n\n    # :nodoc:\n    def initialize(io)\n      super(at_file_header: io)\n      @io = ChecksumReader.new(decompressor_for(io), @filename)\n      @closed = false\n    end\n\n    # Returns an `IO` to the entry's data.\n    def io : IO\n      @io\n    end\n\n    protected def checksum_io\n      @io\n    end\n\n    protected def close\n      return if @closed\n      @closed = true\n      @io.skip_to_end\n      @io.close\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/writer.cr",
    "content": "require \"./file_info\"\n\n# Writes (streams) zip entries to an `IO`.\n#\n# ### Example\n#\n# ```\n# require \"compress/zip\"\n#\n# File.open(\"./file.zip\", \"w\") do |file|\n#   Compress::Zip::Writer.open(file) do |zip|\n#     # Add a file with a String content\n#     zip.add \"foo.txt\", \"contents of foo\"\n#\n#     # Add a file and write data to it through an IO\n#     zip.add(\"bar.txt\") do |io|\n#       io << \"contents of bar\"\n#     end\n#\n#     # Add a file by referencing a file in the filesystem\n#     # (the file is automatically closed after this call)\n#     zip.add(\"baz.txt\", File.open(\"./some_file.txt\"))\n#   end\n# end\n# ```\nclass Compress::Zip::Writer\n  # Whether to close the enclosed `IO` when closing this writer.\n  property? sync_close = false\n\n  # Returns `true` if this writer is closed.\n  getter? closed = false\n\n  # Sets the zip file comment\n  setter comment = \"\"\n\n  # Creates a new writer to the given *io*.\n  def initialize(@io : IO, @sync_close = false)\n    @entries = [] of Entry\n    @compressed_size_counter = ChecksumWriter.new\n    @uncompressed_size_counter = ChecksumWriter.new(compute_crc32: true)\n\n    # Keep track of how many bytes we write, because we need\n    # that to write the offset of each local file header and some other info\n    @written = 0_u32\n  end\n\n  # Creates a new writer to the given *filename*.\n  def self.new(filename : Path | String)\n    new(::File.new(filename, \"w\"), sync_close: true)\n  end\n\n  # Creates a new writer to the given *io*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(io : IO, sync_close = false, &)\n    writer = new(io, sync_close: sync_close)\n    yield writer ensure writer.close\n  end\n\n  # Creates a new writer to the given *filename*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(filename : Path | String, &)\n    writer = new(filename)\n    yield writer ensure writer.close\n  end\n\n  # Adds an entry that will have the given *filename* and current\n  # time (`Time.utc`) and yields an `IO` to write that entry's\n  # contents.\n  def add(filename : Path | String, &)\n    add(Entry.new(filename.to_s)) do |io|\n      yield io\n    end\n  end\n\n  # Adds an entry and yields `IO` to write that entry's contents.\n  #\n  # You can choose the Entry's compression method before adding it.\n  #\n  # * If the STORED compression method is used, its crc32, compressed\n  # size and uncompressed size **must** be set and be correct with\n  # respect to the data that will be written to the yielded `IO`.\n  # * If the DEFLATED compression method is used, crc32, compressed\n  # size and uncompressed size will be computed from the data\n  # written to the yielded IO.\n  #\n  # You can also set the Entry's time (which is `Time.utc` by default)\n  #  and extra data before adding it to the zip stream.\n  def add(entry : Entry, &)\n    # bit 3: unknown compression size (not needed for STORED, by if left out it doesn't work...)\n    entry.general_purpose_bit_flag |= (1 << 3)\n    # bit 11: require UTF-8 set\n    entry.general_purpose_bit_flag |= (1 << 11)\n    entry.offset = @written\n\n    @written += entry.to_io(@io)\n\n    case entry.compression_method\n    when .stored?\n      if entry.compressed_size != entry.uncompressed_size\n        raise Error.new \"Entry compressed size (#{entry.compressed_size}) is not equal to its uncompressed size (#{entry.uncompressed_size})\"\n      end\n\n      @uncompressed_size_counter.io = @io\n      yield @uncompressed_size_counter\n    when .deflated?\n      @compressed_size_counter.io = @io\n      io = Compress::Deflate::Writer.new(@compressed_size_counter)\n      @uncompressed_size_counter.io = io\n      yield @uncompressed_size_counter\n      io.close\n    else\n      raise \"Unsupported compression method: #{entry.compression_method}\"\n    end\n\n    if entry.compression_method.stored?\n      @written += @uncompressed_size_counter.count\n    else\n      @written += @compressed_size_counter.count\n    end\n\n    crc32 = @uncompressed_size_counter.crc32.to_u32\n    uncompressed_size = @uncompressed_size_counter.count\n\n    if entry.compression_method.stored?\n      compressed_size = uncompressed_size\n    else\n      compressed_size = @compressed_size_counter.count\n    end\n\n    if entry.compression_method.stored?\n      if entry.crc32 != crc32\n        raise Error.new(\"Entry CRC32 mismatch (#{entry.crc32} given but was #{crc32})\")\n      end\n\n      if entry.uncompressed_size != uncompressed_size\n        raise Error.new(\"Entry uncompressed size mismatch (#{entry.uncompressed_size} given but was #{uncompressed_size})\")\n      end\n    else\n      entry.crc32 = crc32\n      entry.compressed_size = compressed_size\n      entry.uncompressed_size = uncompressed_size\n    end\n\n    # A data descriptor is not needed for the STORED method\n    # (because we know how many bytes we need to read)\n    unless entry.compression_method.stored?\n      @written += entry.write_data_descriptor(@io)\n    end\n\n    @entries << entry\n  end\n\n  # Adds an entry that will have *string* as its contents.\n  def add(filename_or_entry : Path | String | Entry, string : String) : Nil\n    add(filename_or_entry) do |io|\n      io << string\n    end\n  end\n\n  # Adds an entry that will have *bytes* as its contents.\n  def add(filename_or_entry : Path | String | Entry, bytes : Bytes) : Nil\n    add(filename_or_entry) do |io|\n      io.write(bytes)\n    end\n  end\n\n  # Adds an entry that will have its data copied from the given *data*.\n  # If the given *data* is a `::File`, it is automatically closed\n  # after data is copied from it.\n  def add(filename_or_entry : Path | String | Entry, data : IO) : Nil\n    add(filename_or_entry) do |io|\n      IO.copy(data, io)\n      data.close if data.is_a?(::File)\n    end\n  end\n\n  # Adds a directory entry that will have the given *name*.\n  def add_dir(name) : Nil\n    name = name + '/' unless name.ends_with?('/')\n    add(Entry.new(name)) { }\n  end\n\n  # Closes this zip writer.\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    start_offset = @written\n    write_central_directory\n\n    write_end_of_central_directory(start_offset, @written - start_offset)\n    @io.close if @sync_close\n  end\n\n  private def write_central_directory\n    @entries.each do |entry|\n      write Zip::CENTRAL_DIRECTORY_HEADER_SIGNATURE # 4\n      write Zip::VERSION                            # version made by (2)\n      write Zip::VERSION                            # version needed to extract (2)\n      @written += 8                                 # the 8 bytes we just wrote\n\n      @written += entry.meta_to_io(@io)\n\n      write entry.comment.bytesize.to_u16 # file comment length (2)\n      write 0_u16                         # disk number start (2)\n      write 0_u16                         # internal file attribute (2)\n      write 0_u32                         # external file attribute (4)\n      write entry.offset                  # relative offset of local header (4)\n      @written += 14                      # the 14 bytes we just wrote\n\n      @io << entry.filename\n      @written += entry.filename.bytesize\n\n      @io.write(entry.extra)\n      @written += entry.extra.size\n\n      @io << entry.comment\n      @written += entry.comment.bytesize\n    end\n  end\n\n  private def write_end_of_central_directory(offset, size)\n    write Zip::END_OF_CENTRAL_DIRECTORY_HEADER_SIGNATURE\n    write 0_u16                    # number of this disk\n    write 0_u16                    # disk start\n    write @entries.size.to_u16     # number of entries in disk\n    write @entries.size.to_u16     # number of total entries\n    write size.to_u32              # size of the central directory\n    write offset.to_u32            # offset of central directory\n    write @comment.bytesize.to_u16 # comment length\n    @io << @comment                # comment\n  end\n\n  private def write(value)\n    @io.write_bytes(value, IO::ByteFormat::LittleEndian)\n  end\n\n  # An entry to write into a `Zip::Writer`.\n  class Entry\n    include FileInfo\n  end\nend\n"
  },
  {
    "path": "src/compress/zip/zip.cr",
    "content": "require \"compress/deflate\"\nrequire \"digest/crc32\"\n\n# The Compress::Zip module contains readers and writers of the zip\n# file format, described at [PKWARE's site](https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT).\n#\n# NOTE: To use `Zip` or its children, you must explicitly import it with `require \"compress/zip\"`\n#\n# ### Reading zip files\n#\n# Two types are provided to read from zip files:\n# * `Compress::Zip::File`: can read zip entries from a `File` or from an `IO::Memory`\n# and provides random read access to its entries.\n# * `Compress::Zip::Reader`: can only read zip entries sequentially from any `IO`.\n#\n# `Compress::Zip::File` is the preferred method to read zip files if you\n# can provide a `File`, because it's a bit more flexible and provides\n# more complete information for zip entries (such as comments).\n#\n# When reading zip files, CRC32 checksum values are automatically\n# verified when finishing reading an entry, and `Compress::Zip::Error` will\n# be raised if the computed CRC32 checksum does not match.\n#\n# ### Writer zip files\n#\n# Use `Compress::Zip::Writer`, which writes zip entries sequentially to\n# any `IO`.\n#\n# NOTE: only compression methods 0 (STORED) and 8 (DEFLATED) are\n# supported. Additionally, ZIP64 is not yet supported.\nmodule Compress::Zip\n  VERSION                                   =     20_u16\n  CENTRAL_DIRECTORY_HEADER_SIGNATURE        = 0x02014b50\n  END_OF_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x06054b50\n\n  class Error < Exception\n  end\nend\n\nrequire \"./*\"\n"
  },
  {
    "path": "src/compress/zlib/reader.cr",
    "content": "# A read-only `IO` object to decompress data in the zlib format.\n#\n# Instances of this class wrap another IO object. When you read from this instance\n# instance, it reads data from the underlying IO, decompresses it, and returns\n# it to the caller.\nclass Compress::Zlib::Reader < IO\n  include IO::Buffered\n\n  # Whether to close the enclosed `IO` when closing this reader.\n  property? sync_close = false\n\n  # Returns `true` if this reader is closed.\n  getter? closed = false\n\n  # Creates a new reader from the given *io*.\n  def initialize(@io : IO, @sync_close = false, dict : Bytes? = nil)\n    Compress::Zlib::Reader.read_header(io, dict)\n    @flate_io = Compress::Deflate::Reader.new(@io, dict: dict)\n    @adler32 = ::Digest::Adler32.initial\n    @end = false\n  end\n\n  # Creates a new reader from the given *io*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(io : IO, sync_close = false, dict : Bytes? = nil, &)\n    reader = new(io, sync_close: sync_close, dict: dict)\n    yield reader ensure reader.close\n  end\n\n  protected def self.read_header(io, dict)\n    cmf = io.read_byte || invalid_header\n\n    cm = cmf & 0xF\n    if cm != 8 # the compression method must be 8\n      invalid_header\n    end\n\n    flg = io.read_byte || invalid_header\n\n    # CMF and FLG, when viewed as a 16-bit unsigned integer stored\n    # in MSB order (CMF*256 + FLG), must be a multiple of 31\n    unless (cmf.to_u16*256 + flg.to_u16).divisible_by?(31)\n      invalid_header\n    end\n\n    fdict = flg.bit(5) == 1\n    if fdict\n      unless dict\n        raise Compress::Zlib::Error.new(\"Missing dictionary\")\n      end\n\n      checksum = io.read_bytes(UInt32, IO::ByteFormat::BigEndian)\n      dict_checksum = ::Digest::Adler32.checksum(dict)\n      if checksum != dict_checksum\n        raise Compress::Zlib::Error.new(\"Dictionary ADLER-32 checksum mismatch\")\n      end\n    end\n  end\n\n  # See `IO#read`.\n  def unbuffered_read(slice : Bytes) : Int32\n    check_open\n\n    return 0 if slice.empty?\n    return 0 if @end\n\n    read_bytes = @flate_io.read(slice)\n    if read_bytes == 0\n      # Check ADLER-32\n      @end = true\n      @flate_io.close\n      adler32 = @io.read_bytes(UInt32, IO::ByteFormat::BigEndian)\n      if adler32 != @adler32\n        raise Compress::Zlib::Error.new(\"ADLER-32 checksum mismatch\")\n      end\n    else\n      # Update ADLER-32 checksum\n      @adler32 = ::Digest::Adler32.update(slice[0, read_bytes], @adler32)\n    end\n    read_bytes\n  end\n\n  # Always raises `IO::Error` because this is a read-only `IO`.\n  def unbuffered_write(slice : Bytes) : NoReturn\n    raise IO::Error.new \"Can't write to Compress::Zlib::Reader\"\n  end\n\n  def unbuffered_flush : NoReturn\n    raise IO::Error.new \"Can't flush Compress::Zlib::Reader\"\n  end\n\n  def unbuffered_close : Nil\n    return if @closed\n    @closed = true\n\n    @flate_io.close\n    @io.close if @sync_close\n  end\n\n  def unbuffered_rewind : Nil\n    check_open\n\n    @io.rewind\n\n    initialize(@io, @sync_close, @flate_io.dict)\n  end\n\n  protected def self.invalid_header\n    raise Compress::Zlib::Error.new(\"Invalid header\")\n  end\nend\n"
  },
  {
    "path": "src/compress/zlib/writer.cr",
    "content": "# A write-only `IO` object to compress data in the zlib format.\n#\n# Instances of this class wrap another IO object. When you write to this\n# instance, it compresses the data and writes it to the underlying IO.\n#\n# NOTE: unless created with a block, `close` must be invoked after all\n# data has been written to a Zlib::Writer instance.\nclass Compress::Zlib::Writer < IO\n  # Whether to close the enclosed `IO` when closing this writer.\n  property? sync_close = false\n\n  # Returns `true` if this writer is closed.\n  getter? closed = false\n\n  # Creates a new writer to the given *io*.\n  def initialize(@io : IO, @level = Zlib::DEFAULT_COMPRESSION, @sync_close = false, @dict : Bytes? = nil)\n    @wrote_header = false\n    @adler32 = ::Digest::Adler32.initial\n    @flate_io = Compress::Deflate::Writer.new(@io, level: level, dict: @dict)\n  end\n\n  # Creates a new writer to the given *filename*.\n  def self.new(filename : String, level = Zlib::DEFAULT_COMPRESSION, dict : Bytes? = nil)\n    new(::File.new(filename, \"w\"), level: level, sync_close: true, dict: dict)\n  end\n\n  # Creates a new writer to the given *io*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(io : IO, level = Zlib::DEFAULT_COMPRESSION, sync_close = false, dict : Bytes? = nil, &)\n    writer = new(io, level: level, sync_close: sync_close, dict: dict)\n    yield writer ensure writer.close\n  end\n\n  # Creates a new writer to the given *filename*, yields it to the given block,\n  # and closes it at the end.\n  def self.open(filename : String, level = Zlib::DEFAULT_COMPRESSION, dict : Bytes? = nil, &)\n    writer = new(filename, level: level, dict: dict)\n    yield writer ensure writer.close\n  end\n\n  # Always raises `IO::Error` because this is a write-only `IO`.\n  def read(slice : Bytes) : NoReturn\n    raise IO::Error.new(\"Can't read from Gzip::Writer\")\n  end\n\n  # See `IO#write`.\n  def write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    write_header unless @wrote_header\n\n    @flate_io.write(slice)\n    @adler32 = ::Digest::Adler32.update(slice, @adler32)\n  end\n\n  # Flushes data, forcing writing the zlib header if no\n  # data has been written yet.\n  #\n  # See `IO#flush`.\n  def flush : Nil\n    check_open\n\n    write_header unless @wrote_header\n    @flate_io.flush\n  end\n\n  # Closes this writer. Must be invoked after all data has been written.\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    write_header unless @wrote_header\n\n    @flate_io.close\n\n    @io.write_bytes(@adler32, IO::ByteFormat::BigEndian)\n\n    @io.close if @sync_close\n  end\n\n  private def write_header\n    @wrote_header = true\n\n    # CMF byte: 7 for window size, 8 for compression method (deflate)\n    cmf = 0x78_u8\n    @io.write_byte cmf\n\n    dict = @dict\n\n    flg = 0_u8\n\n    if dict\n      flg |= 1 << 5\n    end\n\n    case @level\n    when 0..1\n      flg |= 0 << 6\n    when 2..5\n      flg |= 1 << 6\n    when 6, -1\n      flg |= 2 << 6\n    else\n      flg |= 3 << 6\n    end\n\n    # CMF and FLG, when viewed as a 16-bit unsigned integer stored\n    # in MSB order (CMF*256 + FLG), must be a multiple of 31\n    flg += 31 - (cmf.to_u16*256 + flg.to_u16).remainder(31)\n\n    @io.write_byte flg\n\n    if dict\n      dict_checksum = ::Digest::Adler32.checksum(dict)\n      @io.write_bytes(dict_checksum, IO::ByteFormat::BigEndian)\n    end\n  end\nend\n"
  },
  {
    "path": "src/compress/zlib/zlib.cr",
    "content": "require \"compress/deflate\"\nrequire \"digest/adler32\"\n\n# The Compress::Zlib module contains readers and writers of zlib format compressed\n# data, as specified in [RFC 1950](https://www.ietf.org/rfc/rfc1950.txt).\n#\n# NOTE: To use `Zlib` or its children, you must explicitly import it with `require \"compress/zlib\"`\nmodule Compress::Zlib\n  NO_COMPRESSION      = Compress::Deflate::NO_COMPRESSION\n  BEST_SPEED          = Compress::Deflate::BEST_SPEED\n  BEST_COMPRESSION    = Compress::Deflate::BEST_COMPRESSION\n  DEFAULT_COMPRESSION = Compress::Deflate::DEFAULT_COMPRESSION\n\n  class Error < Exception\n  end\nend\n\nrequire \"./*\"\n"
  },
  {
    "path": "src/concurrent.cr",
    "content": "require \"fiber\"\nrequire \"channel\"\nrequire \"crystal/tracing\"\n\n{% if flag?(:execution_context) %}\n  require \"fiber/execution_context\"\n{% else %}\n  require \"crystal/scheduler\"\n{% end %}\n\n# Blocks the current fiber for the specified number of seconds.\n#\n# While this fiber is waiting this time, other ready-to-execute\n# fibers might start their execution.\n@[Deprecated(\"Use `::sleep(Time::Span)` instead\")]\ndef sleep(seconds : Number) : Nil\n  if seconds < 0\n    raise ArgumentError.new \"Sleep seconds must be positive\"\n  end\n  sleep(seconds.seconds)\nend\n\n# Blocks the current Fiber for the specified time span.\n#\n# While this fiber is waiting this time, other ready-to-execute\n# fibers might start their execution.\ndef sleep(time : Time::Span) : Nil\n  Crystal.trace :sched, \"sleep\", for: time\n  Crystal::EventLoop.current.sleep(time)\nend\n\n# Blocks the current fiber forever.\n#\n# Meanwhile, other ready-to-execute fibers might start their execution.\ndef sleep : Nil\n  Fiber.suspend\nend\n\n{% begin %}\n# Spawns a new fiber.\n#\n# When using execution contexts, the fiber spawns into the current execution\n# context (`Fiber::ExecutionContext.current`).\n#\n# NOTE: The newly created fiber doesn't run as soon as spawned.\n#\n# Example:\n# ```\n# # Write \"1\" every 1 second and \"2\" every 2 seconds for 6 seconds.\n#\n# require \"wait_group\"\n#\n# wg = WaitGroup.new 2\n#\n# spawn do\n#   6.times do\n#     sleep 1.second\n#     puts 1\n#   end\n# ensure\n#   wg.done\n# end\n#\n# spawn do\n#   3.times do\n#     sleep 2.seconds\n#     puts 2\n#   end\n# ensure\n#   wg.done\n# end\n#\n# wg.wait\n# ```\ndef spawn(*, name : String? = nil, same_thread = false, &block)\n  {% if flag?(:execution_context) %}\n    Fiber::ExecutionContext::Scheduler.current.spawn(name: name, same_thread: same_thread, &block)\n  {% else %}\n    fiber = Fiber.new(name, &block)\n    Crystal.trace :sched, \"spawn\", fiber: fiber\n    {% if flag?(:preview_mt) %} fiber.set_current_thread if same_thread {% end %}\n    fiber.enqueue\n    fiber\n  {% end %}\nend\n{% end %}\n\n# Spawns a fiber by first creating a `Proc`, passing the *call*'s\n# expressions to it, and letting the `Proc` finally invoke the *call*.\n#\n# When using execution contexts, the fiber spawns into the current execution\n# context (`Fiber::ExecutionContext.current`).\n#\n# Compare this:\n#\n# ```\n# i = 0\n# while i < 5\n#   spawn { print(i) }\n#   i += 1\n# end\n# Fiber.yield\n# # Output: 55555\n# ```\n#\n# To this:\n#\n# ```\n# i = 0\n# while i < 5\n#   spawn print(i)\n#   i += 1\n# end\n# Fiber.yield\n# # Output: 01234\n# ```\n#\n# This is because in the first case all spawned fibers refer to\n# the same local variable, while in the second example copies of\n# *i* are passed to a `Proc` that eventually invokes the call.\nmacro spawn(call, *, name = nil, same_thread = false, &block)\n  {% if block %}\n    {% raise \"`spawn(call)` can't be invoked with a block, did you mean `spawn(name: ...) { ... }`?\" %}\n  {% end %}\n\n  {% if call.is_a?(Call) %}\n    ->(\n      {% for arg, i in call.args %}\n        __arg{{i}} : typeof({{arg.is_a?(Splat) ? arg.exp : arg}}),\n      {% end %}\n      {% if call.named_args %}\n        {% for narg, i in call.named_args %}\n          __narg{{i}} : typeof({{narg.value}}),\n        {% end %}\n      {% end %}\n      ) {\n      spawn(name: {{name}}, same_thread: {{same_thread}}) do\n        {% if call.receiver %}{{ call.receiver }}.{% end %}{{call.name}}(\n          {% for arg, i in call.args %}\n            {% if arg.is_a?(Splat) %}*{% end %}__arg{{i}},\n          {% end %}\n          {% if call.named_args %}\n            {% for narg, i in call.named_args %}\n              {{narg.name}}: __narg{{i}},\n            {% end %}\n          {% end %}\n        )\n      end\n      }.call(\n        {% for arg in call.args %}\n          {{arg.is_a?(Splat) ? arg.exp : arg}},\n        {% end %}\n        {% if call.named_args %}\n          {{call.named_args.map(&.value).splat}}\n        {% end %}\n      )\n  {% else %}\n    spawn do\n      {{call}}\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/crypto/bcrypt/base64.cr",
    "content": "require \"../bcrypt\"\n\n# :nodoc:\nmodule Crypto::Bcrypt::Base64\n  ALPHABET = \"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\"\n\n  TABLE = Int8[\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, 0, 1, 54, 55,\n    56, 57, 58, 59, 60, 61, 62, 63, -1, -1,\n    -1, -1, -1, -1, -1, 2, 3, 4, 5, 6,\n    7, 8, 9, 10, 11, 12, 13, 14, 15, 16,\n    17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\n    -1, -1, -1, -1, -1, -1, 28, 29, 30,\n    31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n    41, 42, 43, 44, 45, 46, 47, 48, 49, 50,\n    51, 52, 53, -1, -1, -1, -1, -1,\n  ]\n\n  def self.encode(d, len : Int32) : String\n    off = 0\n\n    String.build do |str|\n      loop do\n        c1 = d[off] & 0xff\n        off += 1\n        str << ALPHABET[(c1 >> 2) & 0x3f]\n        c1 = (c1 & 0x03) << 4\n\n        if off >= len\n          str << ALPHABET[c1 & 0x3f]\n          break\n        end\n\n        c2 = d[off] & 0xff\n        off += 1\n        c1 |= (c2 >> 4) & 0x0f\n        str << ALPHABET[c1 & 0x3f]\n        c1 = (c2 & 0x0f) << 2\n\n        if off >= len\n          str << ALPHABET[c1 & 0x3f]\n          break\n        end\n\n        c2 = d[off] & 0xff\n        off += 1\n        c1 |= (c2 >> 6) & 0x03\n        str << ALPHABET[c1 & 0x3f]\n        str << ALPHABET[c2 & 0x3f]\n\n        break if off >= len\n      end\n    end\n  end\n\n  def self.decode(string : String, maxolen : Int32) : Bytes\n    off, slen, olen = 0, string.size, 0\n\n    i = -1\n    str = Bytes.new(maxolen)\n\n    while off < slen - 1 && olen < maxolen\n      c1, c2 = char64(string[off]), char64(string[off + 1])\n      break if c1 == -1 || c2 == -1\n      off += 2\n\n      str[i += 1] = ((c1 << 2) | (c2 & 0x30) >> 4).to_u8!\n      break if (olen += 1) >= maxolen || off >= slen\n\n      c3 = char64(string[off])\n      break if c3 == -1\n      off += 1\n\n      str[i += 1] = (((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2)).to_u8!\n      break if (olen += 1) >= maxolen || off >= slen\n\n      c4 = char64(string[off])\n      str[i += 1] = (((c3 & 0x03) << 6) | c4).to_u8!\n      off += 1\n      olen += 1\n    end\n\n    str[0, olen]\n  end\n\n  private def self.char64(x)\n    TABLE[x.ord]? || -1\n  end\nend\n"
  },
  {
    "path": "src/crypto/bcrypt/blowfish.cr",
    "content": "require \"../bcrypt\"\nrequire \"../blowfish\"\n\n# :nodoc:\nclass Crypto::Bcrypt::Blowfish < Crypto::Blowfish\n  def enhance_key_schedule(data : Bytes, key : Bytes, cost : Int32) : Nil\n    enhance_key_schedule(data, key)\n\n    (1_u32 << cost).times do\n      expand_key(key)\n      expand_key(data)\n    end\n  end\n\n  def enhance_key_schedule(data : Bytes, key : Bytes) : Nil\n    pos = 0\n\n    0.upto(17) do |i|\n      @p.to_unsafe[i] ^= next_word(key, pointerof(pos))\n    end\n\n    l, r, pos = 0_u32, 0_u32, 0\n\n    (0..17).step(2) do |i|\n      l ^= next_word(data, pointerof(pos))\n      r ^= next_word(data, pointerof(pos))\n      l, r = encrypt_pair(l, r)\n      @p.to_unsafe[i] = l\n      @p.to_unsafe[i + 1] = r\n    end\n\n    (0..1023).step(2) do |i|\n      l ^= next_word(data, pointerof(pos))\n      r ^= next_word(data, pointerof(pos))\n      l, r = encrypt_pair(l, r)\n      @s.to_unsafe[i] = l\n      @s.to_unsafe[i + 1] = r\n    end\n  end\nend\n"
  },
  {
    "path": "src/crypto/bcrypt/password.cr",
    "content": "require \"../bcrypt\"\nrequire \"../subtle\"\n\n# Generate, read and verify `Crypto::Bcrypt` hashes.\n#\n# NOTE: To use `Password`, you must explicitly import it with `require \"crypto/bcrypt/password\"`\n#\n# ```\n# require \"crypto/bcrypt/password\"\n#\n# password = Crypto::Bcrypt::Password.create(\"super secret\", cost: 10)\n# # => $2a$10$rI4xRiuAN2fyiKwynO6PPuorfuoM4L2PVv6hlnVJEmNLjqcibAfHq\n#\n# password.verify(\"wrong secret\") # => false\n# password.verify(\"super secret\") # => true\n# ```\n#\n# See `Crypto::Bcrypt` for hints to select the cost when generating hashes.\nclass Crypto::Bcrypt::Password\n  private SUPPORTED_VERSIONS = [\"2\", \"2a\", \"2b\", \"2y\"]\n\n  # Hashes a password.\n  #\n  # ```\n  # require \"crypto/bcrypt/password\"\n  #\n  # password = Crypto::Bcrypt::Password.create(\"super secret\", cost: 10)\n  # # => $2a$10$rI4xRiuAN2fyiKwynO6PPuorfuoM4L2PVv6hlnVJEmNLjqcibAfHq\n  # ```\n  def self.create(password : String, cost : Int32 = DEFAULT_COST) : self\n    new(Bcrypt.hash_secret(password, cost).to_s)\n  end\n\n  getter version : String\n  getter cost : Int32\n  getter salt : String\n  getter digest : String\n\n  # Loads a bcrypt hash.\n  #\n  # ```\n  # require \"crypto/bcrypt/password\"\n  #\n  # password = Crypto::Bcrypt::Password.new(\"$2a$10$X6rw/jDiLBuzHV./JjBNXe8/Po4wTL0fhdDNdAdjcKN/Fup8tGCya\")\n  # password.version # => \"2a\"\n  # password.salt    # => \"X6rw/jDiLBuzHV./JjBNXe\"\n  # password.digest  # => \"8/Po4wTL0fhdDNdAdjcKN/Fup8tGCya\"\n  # ```\n  def initialize(@raw_hash : String)\n    parts = @raw_hash.split('$')\n    raise Error.new(\"Invalid hash string\") unless parts.size == 4\n    raise Error.new(\"Invalid hash version\") unless SUPPORTED_VERSIONS.includes?(parts[1])\n\n    @version = parts[1]\n    @cost = parts[2].to_i\n    @salt = parts[3][0..21]\n    @digest = parts[3][22..-1]\n\n    raise Error.new(\"Invalid cost\") unless COST_RANGE.includes?(cost)\n    raise Error.new(\"Invalid salt size\") unless salt.size == 22\n    raise Error.new(\"Invalid digest size\") unless digest.size == 31\n  end\n\n  # Verifies a password against the hash.\n  #\n  # ```\n  # require \"crypto/bcrypt/password\"\n  #\n  # password = Crypto::Bcrypt::Password.create(\"super secret\")\n  # password.verify(\"wrong secret\") # => false\n  # password.verify(\"super secret\") # => true\n  # ```\n  def verify(password : String) : Bool\n    hashed_password = Bcrypt.new(password, salt, cost)\n    hashed_password_digest = Base64.encode(hashed_password.digest, hashed_password.digest.size - 1)\n    Crypto::Subtle.constant_time_compare(@digest, hashed_password_digest)\n  end\n\n  # Returns the bcrypt hash, suitable for storage and use in `Crypto::Bcrypt::Password.new`.\n  #\n  # ```\n  # require \"crypto/bcrypt/password\"\n  #\n  # password = Crypto::Bcrypt::Password.create(\"super secret\")\n  # password.to_s # => \"$2a$11$zs8yeubYXMGGJmWyIYdFtO9aOrx44g5rarvixyBfl1klr3dZPG8Ma\"\n  # ```\n  def to_s(io : IO) : Nil\n    io << @raw_hash\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\nend\n"
  },
  {
    "path": "src/crypto/bcrypt.cr",
    "content": "require \"random/secure\"\nrequire \"./subtle\"\n\n# Pure Crystal implementation of the Bcrypt algorithm by Niels Provos and David\n# Mazières, as [presented at USENIX in\n# 1999](https://www.usenix.org/legacy/events/usenix99/provos/provos_html/index.html).\n#\n# The algorithm has a maximum password length limit of 71 characters (see\n# [this comment](https://security.stackexchange.com/questions/39849/does-bcrypt-have-a-maximum-password-length#answer-39851)\n# on stackoverflow).\n#\n# Refer to `Crypto::Bcrypt::Password` for a higher level interface.\n#\n# About the Cost\n#\n# Bcrypt, like the PBKDF2 or scrypt ciphers, are designed to be slow, so\n# generating rainbow tables or cracking passwords is nearly impossible. Yet,\n# computers are always getting faster and faster, so the actual cost must be\n# incremented every once in a while.\n# Always use the maximum cost that is tolerable, performance wise, for your\n# application. Be sure to test and select this based on your server, not your\n# home computer.\n#\n# Last but not least: beware of denial of services! Always protect your\n# application using an external strategy (eg: rate limiting), otherwise\n# endpoints that verifies bcrypt hashes will be an easy target.\n#\n# NOTE: To use `Bcrypt`, you must explicitly import it with `require \"crypto/bcrypt\"`\nclass Crypto::Bcrypt\n  class Error < Exception\n  end\n\n  DEFAULT_COST   = 11\n  COST_RANGE     = 4..31\n  PASSWORD_RANGE = 1..72\n  SALT_SIZE      = 16\n\n  private BLOWFISH_ROUNDS = 16\n  private DIGEST_SIZE     = 31\n\n  # bcrypt IV: \"OrpheanBeholderScryDoubt\"\n  {% if compare_versions(Crystal::VERSION, \"1.16.0\") >= 0 %}\n    private CIPHER_TEXT = Slice(UInt32).literal(\n      0x4f727068, 0x65616e42, 0x65686f6c,\n      0x64657253, 0x63727944, 0x6f756274,\n    )\n  {% else %}\n    private CIPHER_TEXT = UInt32.static_array(\n      0x4f727068, 0x65616e42, 0x65686f6c,\n      0x64657253, 0x63727944, 0x6f756274,\n    )\n  {% end %}\n\n  # Hashes the *password* using bcrypt algorithm using salt obtained via `Random::Secure.random_bytes(SALT_SIZE)`.\n  #\n  # ```\n  # require \"crypto/bcrypt\"\n  #\n  # Crypto::Bcrypt.hash_secret \"secret\"\n  # ```\n  def self.hash_secret(password : String, cost : Int32 = DEFAULT_COST) : String\n    # We make a clone here to we don't keep a mutable reference to the original string\n    passwordb = password.to_unsafe.to_slice(password.bytesize + 1).clone # include leading 0\n    saltb = Random::Secure.random_bytes(SALT_SIZE)\n    new(passwordb, saltb, cost).to_s\n  end\n\n  # Creates a new `Crypto::Bcrypt` object from the given *password* with *salt* and *cost*.\n  #\n  # *salt* must be a base64 encoded string of 16 bytes (128 bits).\n  #\n  # ```\n  # require \"crypto/bcrypt\"\n  #\n  # password = Crypto::Bcrypt.new \"secret\", \"CJjskaIgXR32DJYjVyNPdA==\"\n  # password.to_s # => \"$2a$11$CJjskaIgXR32DJYjVyNPd./ajV3Yj6GiP0IAI6rR.fMnjRgozqqqG\"\n  # ```\n  def self.new(password : String, salt : String, cost : Int32 = DEFAULT_COST) : self\n    # We make a clone here to we don't keep a mutable reference to the original string\n    passwordb = password.to_unsafe.to_slice(password.bytesize + 1).clone # include leading 0\n    saltb = Base64.decode(salt, SALT_SIZE)\n    new(passwordb, saltb, cost)\n  end\n\n  getter password : Bytes\n  getter salt : Bytes\n  getter cost : Int32\n\n  # Creates a new `Crypto::Bcrypt` object from the given *password* with *salt* in bytes and *cost*.\n  #\n  # ```\n  # require \"crypto/bcrypt\"\n  #\n  # password = Crypto::Bcrypt.new \"secret\".to_slice, \"salt_of_16_chars\".to_slice\n  # password.digest\n  # ```\n  def initialize(@password : Bytes, @salt : Bytes, @cost = DEFAULT_COST)\n    raise Error.new(\"Invalid cost\") unless COST_RANGE.includes?(cost)\n    raise Error.new(\"Invalid salt size\") unless salt.size == SALT_SIZE\n    raise Error.new(\"Invalid password size\") unless PASSWORD_RANGE.includes?(password.size)\n  end\n\n  @digest : Bytes?\n\n  def digest : Bytes\n    @digest ||= hash_password\n  end\n\n  @hash : String?\n\n  def to_s : String\n    @hash ||= begin\n      salt64 = Base64.encode(salt, salt.size)\n      digest64 = Base64.encode(digest, digest.size - 1)\n      \"$2a$%02d$%s%s\" % {cost, salt64, digest64}\n    end\n  end\n\n  def to_s(io : IO) : Nil\n    io << to_s\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  delegate to_slice, to: to_s\n\n  private def hash_password\n    blowfish = Blowfish.new(BLOWFISH_ROUNDS)\n    blowfish.enhance_key_schedule(salt, password, cost)\n\n    cipher = uninitialized UInt32[6]\n    cipher.to_slice.copy_from(CIPHER_TEXT.to_slice)\n    cdata = cipher.to_unsafe\n\n    0.step(to: 4, by: 2) do |i|\n      64.times do\n        l, r = blowfish.encrypt_pair(cdata[i], cdata[i + 1])\n        cdata[i], cdata[i + 1] = l, r\n      end\n    end\n\n    ret = Bytes.new(cipher.size * 4)\n    j = -1\n\n    cipher.size.times do |i|\n      ret[j += 1] = (cdata[i] >> 24).to_u8!\n      ret[j += 1] = (cdata[i] >> 16).to_u8!\n      ret[j += 1] = (cdata[i] >> 8).to_u8!\n      ret[j += 1] = cdata[i].to_u8!\n    end\n\n    ret\n  end\nend\n\nrequire \"./bcrypt/*\"\n"
  },
  {
    "path": "src/crypto/blowfish.cr",
    "content": "class Crypto::Blowfish\n  DEFAULT_ROUNDS = 16\n\n  @p : StaticArray(UInt32, 18)\n  @s : StaticArray(UInt32, 1024)\n  @s1 : UInt32*\n  @s2 : UInt32*\n  @s3 : UInt32*\n\n  def initialize(@rounds = DEFAULT_ROUNDS)\n    @p = uninitialized UInt32[18]\n    @s = uninitialized UInt32[1024]\n\n    @p.to_slice.copy_from(P.to_slice)\n    @s.to_slice.copy_from(S.to_slice)\n    @s1 = @s.to_unsafe + 256\n    @s2 = @s.to_unsafe + 512\n    @s3 = @s.to_unsafe + 768\n  end\n\n  def expand_key(key) : Nil\n    pos = 0\n\n    0.upto(17) do |i|\n      @p.to_unsafe[i] ^= next_word(key, pointerof(pos))\n    end\n\n    l, r = 0_u32, 0_u32\n\n    (0..17).step(2) do |i|\n      l, r = encrypt_pair(l, r)\n      @p.to_unsafe[i] = l\n      @p.to_unsafe[i + 1] = r\n    end\n\n    (0..1023).step(2) do |i|\n      l, r = encrypt_pair(l, r)\n      @s.to_unsafe[i] = l\n      @s.to_unsafe[i + 1] = r\n    end\n  end\n\n  def encrypt_pair(l : UInt32, r : UInt32) : Tuple(UInt32, UInt32)\n    0.upto(@rounds - 1) do |i|\n      l ^= @p.to_unsafe[i]\n      r ^= f(l)\n      l, r = r, l\n    end\n\n    l, r = r, l\n\n    r ^= @p.to_unsafe[@rounds]\n    l ^= @p.to_unsafe[@rounds + 1]\n\n    {l, r}\n  end\n\n  def decrypt_pair(l : UInt32, r : UInt32) : {UInt32, UInt32}\n    (@rounds + 1).downto(2) do |i|\n      l ^= @p.to_unsafe[i]\n      r ^= f(l)\n      l, r = r, l\n    end\n\n    l, r = r, l\n\n    r ^= @p.to_unsafe[1]\n    l ^= @p.to_unsafe[0]\n\n    {l, r}\n  end\n\n  @[AlwaysInline]\n  private def f(x : UInt32)\n    d = x.to_u8!\n    c = (x >> 8).to_u8!\n    b = (x >> 16).to_u8!\n    a = (x >> 24).to_u8!\n    ((@s.to_unsafe[a] &+ @s1[b]) ^ @s2[c]) &+ @s3[d]\n  end\n\n  private def next_word(data, pos)\n    word = 0_u32\n\n    4.times do\n      word = (word << 8) | data[pos.value]\n      pos.value = (pos.value + 1) % data.size\n    end\n\n    word\n  end\n\n  {% if compare_versions(Crystal::VERSION, \"1.16.0\") >= 0 %}\n    private P = Slice(UInt32).literal(\n      0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,\n      0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,\n      0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,\n    )\n\n    private S = Slice(UInt32).literal(\n      0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,\n      0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,\n      0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,\n      0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,\n      0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,\n      0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,\n      0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,\n      0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,\n      0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,\n      0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,\n      0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,\n      0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,\n      0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,\n      0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,\n      0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,\n      0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,\n      0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,\n      0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,\n      0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,\n      0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,\n      0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,\n      0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,\n      0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,\n      0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,\n      0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,\n      0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,\n      0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,\n      0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,\n      0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,\n      0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,\n      0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,\n      0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,\n      0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,\n      0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,\n      0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,\n      0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,\n      0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,\n      0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,\n      0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,\n      0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,\n      0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,\n      0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,\n      0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,\n\n      0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,\n      0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,\n      0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,\n      0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,\n      0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,\n      0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,\n      0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,\n      0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,\n      0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,\n      0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,\n      0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,\n      0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,\n      0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,\n      0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,\n      0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,\n      0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,\n      0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,\n      0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,\n      0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,\n      0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,\n      0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,\n      0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,\n      0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,\n      0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,\n      0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,\n      0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,\n      0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,\n      0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,\n      0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,\n      0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,\n      0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,\n      0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,\n      0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,\n      0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,\n      0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,\n      0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,\n      0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,\n      0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,\n      0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,\n      0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,\n      0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,\n      0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,\n      0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,\n\n      0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,\n      0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,\n      0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,\n      0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,\n      0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,\n      0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,\n      0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,\n      0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,\n      0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,\n      0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,\n      0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,\n      0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,\n      0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,\n      0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,\n      0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,\n      0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,\n      0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,\n      0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,\n      0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,\n      0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,\n      0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,\n      0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,\n      0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,\n      0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,\n      0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,\n      0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,\n      0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,\n      0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,\n      0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,\n      0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,\n      0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,\n      0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,\n      0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,\n      0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,\n      0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,\n      0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,\n      0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,\n      0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,\n      0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,\n      0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,\n      0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,\n      0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,\n      0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,\n\n      0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,\n      0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,\n      0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,\n      0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,\n      0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,\n      0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,\n      0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,\n      0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,\n      0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,\n      0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,\n      0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,\n      0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,\n      0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,\n      0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,\n      0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,\n      0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,\n      0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,\n      0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,\n      0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,\n      0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,\n      0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,\n      0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,\n      0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,\n      0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,\n      0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,\n      0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,\n      0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,\n      0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,\n      0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,\n      0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,\n      0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,\n      0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,\n      0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,\n      0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,\n      0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,\n      0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,\n      0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,\n      0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,\n      0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,\n      0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,\n      0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,\n      0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,\n      0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,\n    )\n  {% else %}\n    private P = UInt32.static_array(\n      0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,\n      0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,\n      0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,\n    )\n\n    private S = UInt32.static_array(\n      0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,\n      0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,\n      0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,\n      0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,\n      0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,\n      0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,\n      0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,\n      0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,\n      0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,\n      0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,\n      0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,\n      0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,\n      0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,\n      0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,\n      0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,\n      0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,\n      0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,\n      0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,\n      0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,\n      0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,\n      0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,\n      0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,\n      0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,\n      0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,\n      0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,\n      0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,\n      0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,\n      0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,\n      0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,\n      0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,\n      0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,\n      0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,\n      0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,\n      0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,\n      0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,\n      0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,\n      0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,\n      0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,\n      0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,\n      0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,\n      0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,\n      0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,\n      0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,\n\n      0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,\n      0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,\n      0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,\n      0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,\n      0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,\n      0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,\n      0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,\n      0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,\n      0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,\n      0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,\n      0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,\n      0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,\n      0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,\n      0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,\n      0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,\n      0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,\n      0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,\n      0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,\n      0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,\n      0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,\n      0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,\n      0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,\n      0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,\n      0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,\n      0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,\n      0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,\n      0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,\n      0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,\n      0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,\n      0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,\n      0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,\n      0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,\n      0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,\n      0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,\n      0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,\n      0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,\n      0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,\n      0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,\n      0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,\n      0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,\n      0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,\n      0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,\n      0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,\n\n      0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,\n      0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,\n      0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,\n      0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,\n      0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,\n      0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,\n      0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,\n      0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,\n      0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,\n      0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,\n      0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,\n      0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,\n      0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,\n      0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,\n      0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,\n      0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,\n      0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,\n      0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,\n      0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,\n      0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,\n      0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,\n      0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,\n      0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,\n      0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,\n      0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,\n      0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,\n      0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,\n      0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,\n      0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,\n      0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,\n      0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,\n      0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,\n      0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,\n      0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,\n      0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,\n      0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,\n      0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,\n      0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,\n      0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,\n      0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,\n      0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,\n      0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,\n      0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,\n\n      0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,\n      0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,\n      0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,\n      0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,\n      0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,\n      0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,\n      0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,\n      0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,\n      0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,\n      0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,\n      0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,\n      0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,\n      0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,\n      0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,\n      0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,\n      0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,\n      0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,\n      0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,\n      0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,\n      0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,\n      0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,\n      0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,\n      0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,\n      0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,\n      0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,\n      0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,\n      0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,\n      0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,\n      0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,\n      0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,\n      0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,\n      0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,\n      0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,\n      0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,\n      0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,\n      0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,\n      0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,\n      0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,\n      0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,\n      0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,\n      0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,\n      0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,\n      0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,\n    )\n  {% end %}\nend\n"
  },
  {
    "path": "src/crypto/subtle.cr",
    "content": "module Crypto::Subtle\n  # Compares *x* and *y* in constant time and returns `true` if they are the same, and `false` if they are not.\n  #\n  # ```\n  # require \"crypto/subtle\"\n  #\n  # Crypto::Subtle.constant_time_compare(\"foo\", \"bar\") # => false\n  # Crypto::Subtle.constant_time_compare(\"foo\", \"foo\") # => true\n  # ```\n  #\n  # NOTE: *x* and *y* must be able to respond to `to_slice`.\n  def self.constant_time_compare(x, y) : Bool\n    x = x.to_slice\n    y = y.to_slice\n    return false if x.size != y.size\n\n    v = 0_u8\n\n    x.size.times do |i|\n      v |= x[i] ^ y[i]\n    end\n\n    constant_time_byte_eq(v, 0) == 1\n  end\n\n  def self.constant_time_byte_eq(x, y)\n    z = ~(x ^ y)\n    z &= z >> 4\n    z &= z >> 2\n    z &= z >> 1\n    z\n  end\nend\n"
  },
  {
    "path": "src/crystal/at_exit_handlers.cr",
    "content": "# :nodoc:\nmodule Crystal::AtExitHandlers\n  @@mutex = ::Thread::Mutex.new\n\n  def self.add(handler)\n    @@mutex.synchronize do\n      handlers = @@handlers ||= [] of Int32, ::Exception? ->\n      handlers << handler\n    end\n  end\n\n  def self.run(status : ::Process::Status, exception = nil)\n    if code = status.exit_code?\n      run(code, exception)\n    else\n      status\n    end\n  end\n\n  def self.run(status, exception = nil)\n    return status unless @@handlers\n\n    # Run the registered handlers in reverse order\n    while handler = @@mutex.synchronize { @@handlers.try(&.pop?) }\n      begin\n        handler.call status, exception\n      rescue handler_ex\n        Crystal::System.print_error \"Error running at_exit handler: %s\\n\", handler_ex.message || \"\"\n        status = 1 if status.zero?\n      end\n    end\n\n    status\n  end\nend\n"
  },
  {
    "path": "src/crystal/atomic_semaphore.cr",
    "content": "# :nodoc:\nclass Crystal::AtomicSemaphore\n  @m = Atomic(UInt32).new(0)\n\n  def wait(&) : Nil\n    m = @m.get\n    while m == 0 || !@m.compare_and_set(m, m &- 1).last\n      yield\n      m = @m.get\n    end\n  end\n\n  def signal : Nil\n    @m.add(1)\n  end\nend\n"
  },
  {
    "path": "src/crystal/compiler_rt/divmod128.cr",
    "content": "# This file includes an implementation of (U)Int128 modulo/division operations\n\n# :nodoc:\nfun __divti3(a : Int128, b : Int128) : Int128\n  # Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/int_div_impl.inc\n\n  s_a = a >> 127       # s_a = a < 0 ? -1 : 0\n  s_b = b >> 127       # s_b = b < 0 ? -1 : 0\n  a = (a ^ s_a) &- s_a # negate if s_a == -1\n  b = (b ^ s_b) &- s_b # negate if s_b == -1\n  s_a ^= s_b           # sign of quotient\n  quo, _ = _u128_div_rem(a.to_u128!, b.to_u128!)\n  ((quo ^ s_a) &- s_a).to_i128! # negate if s_a == -1\nend\n\n# :nodoc:\nfun __modti3(a : Int128, b : Int128) : Int128\n  # Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/int_div_impl.inc\n\n  s = b >> 127     # s = b < 0 ? -1 : 0\n  b = (b ^ s) &- s # negate if s == -1\n  s = a >> 127     # s = a < 0 ? -1 : 0\n  a = (a ^ s) &- s # negate if s == -1\n  _, rem = _u128_div_rem(a.to_u128!, b.to_u128!)\n  (rem.to_i128! ^ s) &- s # negate if s == -1\nend\n\n# :nodoc:\nfun __udivti3(a : UInt128, b : UInt128) : UInt128\n  # Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/int_div_impl.inc\n\n  quo, _ = _u128_div_rem(a, b)\n  quo\nend\n\n# :nodoc:\nfun __umodti3(a : UInt128, b : UInt128) : UInt128\n  # Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/int_div_impl.inc\n\n  _, rem = _u128_div_rem(a, b)\n  rem\nend\n\n# :nodoc:\ndef _carrying_mul(lhs : UInt64, rhs : UInt64) : Tuple(UInt64, UInt64)\n  # Ported from https://github.com/rust-lang/compiler-builtins/blob/2be2bc086bd9b3c0fc8eb8d2dc7df025e6ffd318/src/int/specialized_div_rem/trifecta.rs\n\n  tmp = lhs.to_u128! &* rhs.to_u128!\n  {tmp.to_u64!, (tmp >> 64).to_u64!}\nend\n\n# :nodoc:\ndef _carrying_mul_add(lhs : UInt64, mul : UInt64, add : UInt64) : Tuple(UInt64, UInt64)\n  # Ported from https://github.com/rust-lang/compiler-builtins/blob/2be2bc086bd9b3c0fc8eb8d2dc7df025e6ffd318/src/int/specialized_div_rem/trifecta.rs\n\n  tmp = lhs.to_u128!\n  tmp &*= mul.to_u128!\n  tmp &+= add.to_u128!\n  {tmp.to_u64!, (tmp >> 64).to_u64!}\nend\n\n# :nodoc:\ndef _u128_div_rem(duo : UInt128, div : UInt128) : Tuple(UInt128, UInt128)\n  # Ported from https://github.com/rust-lang/compiler-builtins/blob/2be2bc086bd9b3c0fc8eb8d2dc7df025e6ffd318/src/int/specialized_div_rem/trifecta.rs\n\n  # Rust also has another algorithm for 128-bit integer division\n  # for microarchitectures that have slow hardware integer division.\n\n  # This algorithm is called the trifecta algorithm because it uses three main algorithms:\n  # - short division for small divisors\n  # - the two possibility algorithm for large divisors\n  # - an undersubtracting long division algorithm for intermediate cases\n\n  div_lz = div.leading_zeros_count\n  duo_lz = duo.leading_zeros_count\n\n  if div_lz <= duo_lz\n    # Resulting quotient is 0 or 1 at this point\n    # The highest set bit of `duo` needs to be at least one place higher than `div` for the quotient to be more than one.\n    if duo >= div\n      return {1_u128, duo - div}\n    else\n      return {0_u128, duo}\n    end\n  end\n\n  # Use 64-bit integer division if possible\n  if duo_lz >= 64\n    # duo fits in a 64-bit integer\n    # Because of the previous branch (div_lz <= duo_lz), div will also fit in an 64-bit integer\n    quo_local1 = duo.to_u64! // div.to_u64!\n    rem_local1 = duo.to_u64! % div.to_u64!\n    return {quo_local1.to_u128!, rem_local1.to_u128!}\n  end\n\n  # Short division branch\n  if div_lz >= 96\n    duo_hi = (duo >> 64).to_u64!\n    div_0 = div.to_u32!.to_u64!\n    quo_hi = duo_hi // div_0\n    rem_3 = duo_hi % div_0\n\n    duo_mid = (duo >> 32).to_u32!.to_u64! | (rem_3 << 32)\n    quo_1 = duo_mid // div_0\n    rem_2 = duo_mid % div_0\n\n    duo_lo = duo.to_u32!.to_u64! | (rem_2 << 32)\n    quo_0 = duo_lo // div_0\n    rem_1 = duo_lo % div_0\n\n    return {quo_0.to_u128! | (quo_1.to_u128! << 32) | (quo_hi.to_u128! << 64), rem_1.to_u128!}\n  end\n\n  # Relative leading significant bits (cannot overflow because of above branches)\n  lz_diff = div_lz - duo_lz\n\n  if lz_diff < 32\n    # Two possibility division algorithm\n\n    # The most significant bits of duo and div are within 32 bits of each other.\n    # If we take the n most significant bits of duo and divide them by the corresponding bits in div, it produces the quotient value quo.\n    # It happens that quo or quo - 1 will always be the correct quotient for the whole number.\n\n    shift = 64 - duo_lz\n    duo_sig_n = (duo >> shift).to_u64!\n    div_sig_n = (div >> shift).to_u64!\n    quo_local2 = duo_sig_n // div_sig_n\n\n    # The larger quo can overflow, so a manual carrying mul is used with manual overflow checking.\n    div_lo = div.to_u64!\n    div_hi = (div >> 64).to_u64!\n    tmp_lo, carry = _carrying_mul(quo_local2, div_lo)\n    tmp_hi, overflow = _carrying_mul_add(quo_local2, div_hi, carry)\n    tmp = tmp_lo.to_u128! | (tmp_hi.to_u128! << 64)\n    if (overflow != 0) || (duo < tmp)\n      # In `duo &+ div &- tmp`, both the subtraction and addition can overflow, but the result is always a correct positive number.\n      return {(quo_local2 - 1).to_u128!, duo &+ div &- tmp}\n    else\n      return {quo_local2.to_u128!, duo - tmp}\n    end\n  end\n\n  # Undersubtracting long division algorithm.\n\n  quo : UInt128 = 0\n  div_extra = 96 - div_lz                  # Number of lesser significant bits that aren't part of div_sig_32\n  div_sig_32 = (div >> div_extra).to_u32!  # Most significant 32 bits of div\n  div_sig_32_add1 = div_sig_32.to_u64! + 1 # This must be a UInt64 because this can overflow\n\n  loop do\n    duo_extra = 64 - duo_lz                # Number of lesser significant bits that aren't part of duo_sig_n\n    duo_sig_n = (duo >> duo_extra).to_u64! # Most significant 64 bits of duo\n\n    # The two possibility algorithm requires that the difference between most significant bits is less than 32\n    if div_extra <= duo_extra\n      # Undersubtracting long division step\n      quo_part = (duo_sig_n // div_sig_32_add1).to_u128!\n      extra_shl = duo_extra - div_extra\n\n      # Addition to the quotient\n      quo += (quo_part << extra_shl)\n\n      # Subtraction from duo. At least 31 bits are cleared from duo here\n      duo -= ((div &* quo_part) << extra_shl)\n    else\n      # Two possibility algorithm\n\n      shift = 64 - duo_lz\n      duo_sig_n = (duo >> shift).to_u64!\n      div_sig_n = (div >> shift).to_u64!\n      quo_part = duo_sig_n // div_sig_n\n      div_lo = div.to_u64!\n      div_hi = (div >> 64).to_u64!\n\n      tmp_lo, carry = _carrying_mul(quo_part, div_lo)\n      # The undersubtracting long division algorithm has already run once, so overflow beyond 128 bits is impossible\n      tmp_hi, _ = _carrying_mul_add(quo_part, div_hi, carry)\n      tmp = tmp_lo.to_u128! | (tmp_hi.to_u128! << 64)\n\n      if duo < tmp\n        return {quo + (quo_part - 1), duo &+ div &- tmp}\n      else\n        return {quo + quo_part, duo - tmp}\n      end\n    end\n\n    duo_lz = duo.leading_zeros_count\n\n    if div_lz <= duo_lz\n      # Quotient can have 0 or 1 added to it\n      if div <= duo\n        return {quo + 1, duo - div}\n      else\n        return {quo, duo}\n      end\n    end\n\n    # This can only happen if div_sd < 64\n    if 64 <= duo_lz\n      quo_local3 = duo.to_u64! // div.to_u64!\n      rem_local2 = duo.to_u64! % div.to_u64!\n      return {quo + quo_local3, rem_local2.to_u128!}\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/compiler_rt/fixint.cr",
    "content": "private macro __fixint_impl(name, from, to)\n  {% n = from.stringify.gsub(/^.*?(\\d+)$/, \"\\\\1\").to_i %}\n  {% to_n = to.stringify.gsub(/^.*?(\\d+)$/, \"\\\\1\").to_i %}\n  {% signed = to.stringify.starts_with?(\"Int\") %}\n\n  # :nodoc:\n  # Ported from https://github.com/llvm/llvm-project/tree/82b74363a943b570c4ee7799d5f3ee4b3e7163a5/compiler-rt/lib/builtins\n  fun {{name}}(a : {{from}}) : {{to}}\n    # Break a into sign, exponent, significand parts.\n    a_rep = a.unsafe_as(UInt{{n}})\n    a_abs = a_rep & UInt{{n}}::MAX.unsafe_shr(1)\n    sign = a_rep & (UInt{{n}}.new!(1).unsafe_shl({{n - 1}})) != 0 ? -1 : 1\n    significand_bits = {{from}}::MANT_DIGITS &- 1\n    exponent = a_abs.unsafe_shr(significand_bits).to_i! &- ({{from}}::MAX_EXP &- 1)\n    implicit_bit = UInt{{n}}.new!(1).unsafe_shl(significand_bits)\n    significand = (a_abs & (implicit_bit &- 1)) | implicit_bit\n\n    {% if signed %}\n      # If exponent is negative, the result is zero.\n      if exponent < 0\n        return {{to}}.new!(0)\n      end\n\n      # If the value is too large for the integer type, saturate.\n      if exponent >= {{to_n}}\n        return sign == 1 ? {{to}}::MAX : {{to}}::MIN\n      end\n\n      # If 0 <= exponent < significandBits, right shift to get the result.\n      # Otherwise, shift left. (`#<<` handles this)\n      {{to}}.new!(sign) * ({{to}}.new!(significand) << (exponent &- significand_bits))\n    {% else %}\n      # If either the value or the exponent is negative, the result is zero.\n      if sign == -1 || exponent < 0\n        return {{to}}.new!(0)\n      end\n\n      # If the value is too large for the integer type, saturate.\n      if exponent >= {{to_n}}\n        return {{to}}::MAX\n      end\n\n      # If 0 <= exponent < significandBits, right shift to get the result.\n      # Otherwise, shift left. (`#<<` handles this)\n      {{to}}.new!(significand) << (exponent &- significand_bits)\n    {% end %}\n  end\nend\n\n__fixint_impl(__fixdfti, Float64, Int128)\n__fixint_impl(__fixsfti, Float32, Int128)\n__fixint_impl(__fixunsdfti, Float64, UInt128)\n__fixint_impl(__fixunssfti, Float32, UInt128)\n"
  },
  {
    "path": "src/crystal/compiler_rt/float.cr",
    "content": "private macro __float_impl(name, from, to)\n  {% n = from.stringify.gsub(/^.*?(\\d+)$/, \"\\\\1\").to_i %}\n  {% to_n = to.stringify.gsub(/^.*?(\\d+)$/, \"\\\\1\").to_i %}\n  {% signed = from.stringify.starts_with?(\"Int\") %}\n  {% raw = \"UInt#{to_n}\".id %}\n\n  # :nodoc:\n  # Ported from https://github.com/llvm/llvm-project/tree/82b74363a943b570c4ee7799d5f3ee4b3e7163a5/compiler-rt/lib/builtins\n  fun {{name}}(a : {{from}}) : {{to}}\n    if a == 0\n      return {{to}}.new!(0)\n    end\n    {% if signed %}\n      s = a.unsafe_shr({{n - 1}})\n      a = (a ^ s) &- s\n    {% end %}\n    sd = {{raw}}.new!({{n}}) &- a.leading_zeros_count # number of significant digits\n    e = sd &- 1                                       # exponent\n    if sd > {{to}}::MANT_DIGITS\n      #  start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx\n      # finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR\n      #                                               12345678901234567890123456\n      # 1 = msb 1 bit\n      # P = bit MANT_DIGITS-1 bits to the right of 1\n      # Q = bit MANT_DIGITS bits to the right of 1\n      # R = \"or\" of all bits to the right of Q\n      if sd == {{to}}::MANT_DIGITS &+ 1\n        a = a.unsafe_shl(1)\n      elsif sd == {{to}}::MANT_DIGITS &+ 2\n        # do nothing\n      else\n        a2 = (a & UInt{{n}}::MAX.unsafe_shr(({{n}} &+ {{to}}::MANT_DIGITS &+ 2) &- sd)) != 0\n        a = {{from}}.new!(UInt{{n}}.new!(a).unsafe_shr(sd &- ({{to}}::MANT_DIGITS &+ 2)))\n        a |= 1 if a2\n      end\n      # finish:\n      a |= 1 if (a & 4) != 0 # Or P into R\n      a &+= 1                # round - this step may add a significant bit\n      a = a.unsafe_shr(2)    # dump Q and R\n      # a is now rounded to MANT_DIGITS or MANT_DIGITS+1 bits\n      if a & {{from}}.new!(1).unsafe_shl({{to}}::MANT_DIGITS) != 0\n        a = a.unsafe_shr(1)\n        e &+= 1\n      end\n      # a is now rounded to MANT_DIGITS bits\n    else\n      a = a.unsafe_shl({{to}}::MANT_DIGITS &- sd)\n      # a is now rounded to MANT_DIGITS bits\n    end\n    fb = {% if signed %} ({{raw}}.new!(1).unsafe_shl({{to_n - 1}}) & s) | {% end %} # sign\n          (e &+ {{to}}::MAX_EXP &- 1).unsafe_shl({{to}}::MANT_DIGITS &- 1) |        # exponent\n          (a & ~({{raw}}::MAX.unsafe_shl({{to}}::MANT_DIGITS &- 1)))                # mantissa\n    fb.unsafe_as({{to}})\n  end\nend\n\n__float_impl(__floattidf, Int128, Float64)\n__float_impl(__floattisf, Int128, Float32)\n__float_impl(__floatuntidf, UInt128, Float64)\n__float_impl(__floatuntisf, UInt128, Float32)\n"
  },
  {
    "path": "src/crystal/compiler_rt/mul.cr",
    "content": "# :nodoc:\nprivate macro __mul_impl(name, type, n)\n  # :nodoc:\n  # Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/int_mulo_impl.inc\n  fun {{name}}(a : {{type}}, b : {{type}}, overflow : Int32*) : {{type}}\n    overflow.value = 0\n    result = a &* b\n    if a == {{type}}::MIN\n      if b != 0 && b != 1\n        overflow.value = 1\n      end\n      return result\n    end\n    if b == {{type}}::MIN\n      if a != 0 && a != 1\n        overflow.value = 1\n      end\n      return result\n    end\n    sa = a >> {{n - 1}}\n    abs_a = (a ^ sa) &- sa\n    sb = b >> {{n - 1}}\n    abs_b = (b ^ sb) &- sb\n    if abs_a < 2 || abs_b < 2\n      return result\n    end\n    if sa == sb\n      if abs_a > ({{type}}::MAX // abs_b)\n        overflow.value = 1\n      end\n    else\n      if abs_a > ({{type}}::MIN // ({{type}}.new(0) &- abs_b))\n        overflow.value = 1\n      end\n    end\n    return result\n  end\nend\n\n__mul_impl(__mulosi4, Int32, 32)\n__mul_impl(__mulodi4, Int64, 64)\n__mul_impl(__muloti4, Int128, 128)\n"
  },
  {
    "path": "src/crystal/compiler_rt/multi3.cr",
    "content": "private def __mulddi3(a : UInt64, b : UInt64) : Int128\n  bits_in_dword_2 = (sizeof(Int64) &* 8) // 2\n  lower_mask = ~0_u64 >> bits_in_dword_2\n\n  low = (a & lower_mask) &* (b & lower_mask)\n  t = low >> bits_in_dword_2\n  low &= lower_mask\n  t &+= (a >> bits_in_dword_2) &* (b & lower_mask)\n  low &+= (t & lower_mask) << bits_in_dword_2\n  high = t >> bits_in_dword_2\n  t = low >> bits_in_dword_2\n  low &= lower_mask\n  t &+= (b >> bits_in_dword_2) &* (a & lower_mask)\n  low &+= (t & lower_mask) << bits_in_dword_2\n  high &+= t >> bits_in_dword_2\n  high &+= (a >> bits_in_dword_2) &* (b >> bits_in_dword_2)\n\n  (high.to_i128! << 64) &+ low\nend\n\n# :nodoc:\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/multi3.c\nfun __multi3(a : Int128, b : Int128) : Int128\n  a_low = (a & ~0_u64).to_u64!\n  a_high = (a >> 64).to_i64!\n  b_low = (b & ~0_u64).to_u64!\n  b_high = (b >> 64).to_i64!\n\n  result = __mulddi3(a_low, b_low)\n  result &+= ((a_high &* b_low) &+ (a_low &* b_high)).to_i128! << 64\n  result\nend\n"
  },
  {
    "path": "src/crystal/compiler_rt/pow.cr",
    "content": "private macro __pow_impl(name, one, float_type)\n  # :nodoc:\n  # Ported from https://github.com/llvm/llvm-project/blob/2e9df860468425645dcd1b241c5dbf76c072e314/compiler-rt/lib/builtins\n  fun {{name}}(a : {{float_type}}, b : Int32) : {{float_type}}\n    recip = b < 0\n    r = {{one}}\n\n    loop do\n      r *= a if b & 1 != 0\n      b = b.unsafe_div 2\n      break if b == 0\n      a *= a\n    end\n\n    recip ? 1 / r : r\n  end\nend\n\n__pow_impl(__powisf2, 1f32, Float32)\n__pow_impl(__powidf2, 1f64, Float64)\n"
  },
  {
    "path": "src/crystal/compiler_rt/shift.cr",
    "content": "# :nodoc:\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/ashlti3.c\n#\n# Returns: a << b\n# Precondition:  0 <= b < bits_in_tword\nfun __ashlti3(a : Int128, b : Int32) : Int128\n  low, high = a.unsafe_as(Tuple(UInt64, Int64))\n  if b >= 64\n    low, high = 0u64, low.unsafe_shl(b &- 64).to_i64!\n  elsif b == 0\n    return a\n  else\n    low, high = low.unsafe_shl(b), high.unsafe_shl(b) | low.unsafe_shr(64 &- b)\n  end\n\n  {low, high}.unsafe_as(Int128)\nend\n\n# :nodoc:\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/ashrti3.c\n#\n# Returns: a >> b\n# Precondition:  0 <= b < bits_in_tword\nfun __ashrti3(a : Int128, b : Int32) : Int128\n  low, high = a.unsafe_as(Tuple(UInt64, Int64))\n  if b >= 64\n    low, high = high.unsafe_shr(b &- 64), high.unsafe_shr(63)\n  elsif b == 0\n    return a\n  else\n    low, high = high.unsafe_shl(64 &- b) | low.unsafe_shr(b), high.unsafe_shr(b)\n  end\n\n  {low, high}.unsafe_as(Int128)\nend\n\n# :nodoc:\n# Ported from https://github.com/llvm/llvm-project/blob/ce59ccd04023cab3a837da14079ca2dcbfebb70c/compiler-rt/lib/builtins/lshrti3.c\n#\n# Returns: logical a >> b\n# Precondition:  0 <= b < bits_in_tword\nfun __lshrti3(a : Int128, b : Int32) : Int128\n  low, high = a.unsafe_as(Tuple(UInt64, UInt64))\n  if b >= 64\n    low, high = high.unsafe_shr(b &- 64), 0u64\n  elsif b == 0\n    return a\n  else\n    low, high = high.unsafe_shl(64 &- b) | low.unsafe_shr(b), high.unsafe_shr(b)\n  end\n\n  {low, high}.unsafe_as(Int128)\nend\n"
  },
  {
    "path": "src/crystal/compiler_rt.cr",
    "content": "{% skip_file if flag?(:skip_crystal_compiler_rt) %}\n\nrequire \"./compiler_rt/fixint.cr\"\nrequire \"./compiler_rt/float.cr\"\nrequire \"./compiler_rt/mul.cr\"\nrequire \"./compiler_rt/divmod128.cr\"\n\n{% if flag?(:arm) || flag?(:wasm32) %}\n  # __multi3 was only missing on arm and wasm32\n  require \"./compiler_rt/multi3.cr\"\n{% end %}\n\n{% if flag?(:wasm32) %}\n  # __ashlti3, __ashrti3 and __lshrti3 are missing on wasm32\n  require \"./compiler_rt/shift.cr\"\n\n  # __powisf2 and __powidf2 are missing on wasm32\n  require \"./compiler_rt/pow.cr\"\n{% end %}\n\n{% if flag?(:win32) && flag?(:bits64) %}\n  # LLVM doesn't honor the Windows x64 ABI when calling certain compiler-rt\n  # functions from its own instructions, but calls from Crystal do, so we invoke\n  # those functions directly\n  # note that the following defs redefine the ones in `primitives.cr`\n\n  # https://github.com/llvm/llvm-project/commit/4a406d32e97b1748c4eed6674a2c1819b9cf98ea\n  struct Int128\n    {% for int2 in [Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128] %}\n      @[AlwaysInline]\n      def unsafe_div(other : {{ int2.id }}) : self\n        __divti3(self, other.to_i128!)\n      end\n\n      @[AlwaysInline]\n      def unsafe_mod(other : {{ int2.id }}) : self\n        __modti3(self, other.to_i128!)\n      end\n    {% end %}\n  end\n\n  struct UInt128\n    {% for int2 in [Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128] %}\n      @[AlwaysInline]\n      def unsafe_div(other : {{ int2.id }}) : self\n        __udivti3(self, other.to_u128!)\n      end\n\n      @[AlwaysInline]\n      def unsafe_mod(other : {{ int2.id }}) : self\n        __umodti3(self, other.to_u128!)\n      end\n    {% end %}\n  end\n\n  {% for int1 in [Int8, Int16, Int32, Int64] %}\n    {% for int2 in [Int128, UInt128] %}\n      struct {{ int1.id }}\n        @[AlwaysInline]\n        def unsafe_div(other : {{ int2.id }}) : self\n          {{ int1.id }}.new!(__divti3(self.to_i128!, other.to_i128!))\n        end\n\n        @[AlwaysInline]\n        def unsafe_mod(other : {{ int2.id }}) : self\n          {{ int1.id }}.new!(__modti3(self.to_i128!, other.to_i128!))\n        end\n      end\n    {% end %}\n  {% end %}\n\n  {% for int1 in [UInt8, UInt16, UInt32, UInt64] %}\n    {% for int2 in [Int128, UInt128] %}\n      struct {{ int1.id }}\n        @[AlwaysInline]\n        def unsafe_div(other : {{ int2.id }}) : self\n          {{ int1.id }}.new!(__udivti3(self.to_u128!, other.to_u128!))\n        end\n\n        @[AlwaysInline]\n        def unsafe_mod(other : {{ int2.id }}) : self\n          {{ int1.id }}.new!(__umodti3(self.to_u128!, other.to_u128!))\n        end\n      end\n    {% end %}\n  {% end %}\n\n  # https://github.com/llvm/llvm-project/commit/d6216e2cd1a5e07f8509215ee5422ff5ee358da8\n  {% if compare_versions(Crystal::LLVM_VERSION, \"14.0.0\") >= 0 %}\n    # the following overflow comparisons must be identical to the ones in\n    # `Crystal::CodeGenVisitor#codegen_out_of_range`\n\n    struct Int128\n      @[AlwaysInline]\n      def to_f32 : Float32\n        __floattisf(self)\n      end\n\n      @[AlwaysInline]\n      def to_f64 : Float64\n        __floattidf(self)\n      end\n    end\n\n    struct UInt128\n      @[AlwaysInline]\n      def to_f32 : Float32\n        # Float32::MAX.to_u128! (it is okay to use a literal here because\n        # support for LLVM 14 was added after 128-bit literals)\n        if self > 340282346638528859811704183484516925440_u128\n          raise OverflowError.new\n        end\n        __floatuntisf(self)\n      end\n\n      @[AlwaysInline]\n      def to_f64 : Float64\n        __floatuntidf(self)\n      end\n    end\n\n    struct Float32\n      @[AlwaysInline]\n      def to_i128 : Int128\n        # Int128::MIN.to_f32!..Int128::MAX.to_f32!.prev_float\n        if !(self >= -1.7014118e+38_f32) || self > 1.7014117e+38_f32\n          raise OverflowError.new\n        end\n        __fixsfti(self)\n      end\n\n      @[AlwaysInline]\n      def to_u128 : UInt128\n        # UInt128::MIN.to_f32!..Float32::MAX\n        if !(self >= 0_f32) || self > 3.4028235e+38_f32\n          raise OverflowError.new\n        end\n        __fixunssfti(self)\n      end\n    end\n\n    struct Float64\n      @[AlwaysInline]\n      def to_i128 : Int128\n        # Int128::MIN.to_f64!..Int128::MAX.to_f64!.prev_float\n        if !(self >= -1.7014118346046923e+38_f64) || self > 1.7014118346046921e+38_f64\n          raise OverflowError.new\n        end\n        __fixdfti(self)\n      end\n\n      @[AlwaysInline]\n      def to_u128 : UInt128\n        # UInt128::MIN.to_f64!..UInt128::MAX.to_f64!.prev_float\n        if !(self >= 0_f64) || self > 3.4028236692093843e+38_f64\n          raise OverflowError.new\n        end\n        __fixunsdfti(self)\n      end\n    end\n\n    {% for v in [\n                  {Int128, \"to_f32\", Float32, \"__floattisf\"},\n                  {Int128, \"to_f64\", Float64, \"__floattidf\"},\n                  {UInt128, \"to_f32\", Float32, \"__floatuntisf\"},\n                  {UInt128, \"to_f64\", Float64, \"__floatuntidf\"},\n                  {Float32, \"to_i128\", Int128, \"__fixsfti\"},\n                  {Float32, \"to_u128\", UInt128, \"__fixunssfti\"},\n                  {Float64, \"to_i128\", Int128, \"__fixdfti\"},\n                  {Float64, \"to_u128\", UInt128, \"__fixunsdfti\"},\n                ] %}\n      {% type, method, ret, rt_method = v %}\n      struct {{ type.id }}\n        @[AlwaysInline]\n        def {{ method.id }}! : {{ ret.id }}\n          {{ rt_method.id }}(self)\n        end\n      end\n    {% end %}\n\n    {% for op in {\"+\", \"-\", \"*\", \"fdiv\"} %}\n      struct Int128\n        @[AlwaysInline]\n        def {{ op.id }}(other : Float32) : Float32\n          to_f32 {{ op.id }} other\n        end\n\n        @[AlwaysInline]\n        def {{ op.id }}(other : Float64) : Float64\n          to_f64 {{ op.id }} other\n        end\n      end\n\n      struct UInt128\n        @[AlwaysInline]\n        def {{ op.id }}(other : Float32) : Float32\n          to_f32 {{ op.id }} other\n        end\n\n        @[AlwaysInline]\n        def {{ op.id }}(other : Float64) : Float64\n          to_f64 {{ op.id }} other\n        end\n      end\n\n      struct Float32\n        @[AlwaysInline]\n        def {{ op.id }}(other : Int128) : Float32\n          self.{{ op.id }}(other.to_f32)\n        end\n\n        @[AlwaysInline]\n        def {{ op.id }}(other : UInt128) : Float32\n          self.{{ op.id }}(other.to_f32)\n        end\n      end\n\n      struct Float64\n        @[AlwaysInline]\n        def {{ op.id }}(other : Int128) : Float64\n          self.{{ op.id }}(other.to_f64)\n        end\n\n        @[AlwaysInline]\n        def {{ op.id }}(other : UInt128) : Float64\n          self.{{ op.id }}(other.to_f64)\n        end\n      end\n    {% end %}\n  {% end %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/datum.cr",
    "content": "module Crystal\n  # :nodoc:\n  macro datum_accessors(short, type, immutable)\n    # Checks that the underlying value is `{{type}}`, and returns its value.\n    # Raises otherwise.\n    def as_{{short.id}} : {{type}}\n      {% if immutable == true %}\n        @raw.as({{type}}).clone\n      {% else %}\n        @raw.as({{type}})\n      {% end %}\n    end\n\n    {% if type.resolve != Nil %}\n      # Checks that the underlying value is `{{type}}`, and returns its value.\n      # Returns `nil` otherwise.\n      def as_{{short.id}}? : {{type}}?\n        {% if immutable == true %}\n          @raw.as?({{type}}).clone\n        {% else %}\n          @raw.as?({{type}})\n        {% end %}\n      end\n    {% end %}\n  end\n\n  # `Crystal.datum` macro is an internal helper to create data types that will hold\n  # values of multiple kinds similar to `JSON::Any` and `YAML::Any`.\n  #\n  # * **types**: contains a named tuple of prefixes and data types of each leaf\n  # * **hash_key_type** specifies the type used as the key of `Hash`\n  # * **immutable**: will generate honor immutability of the values via `.clone`\n  # * **target_type**: is the type where the macro is invoked (it's a workaround for #9099).\n\n  # :nodoc:\n  macro datum(*, types, hash_key_type, immutable, target_type)\n\n    # All possible `{{target_type}}` types.\n    alias Type = {% for short, type in types %}{{type}} | {% end %}Array(self) | Hash({{hash_key_type}}, self)\n\n    # Returns the raw underlying value, a `Type`.\n    getter raw : Type\n\n    # Creates a `{{target_type}}` that wraps the given `Type`.\n    def initialize(@raw : Type)\n    end\n\n    Crystal.datum_accessors a, Array(self), {{immutable}}\n    Crystal.datum_accessors h, Hash({{hash_key_type}}, self), {{immutable}}\n\n    {% for short, type in types %}\n      Crystal.datum_accessors {{short}}, {{type}}, {{immutable}}\n    {% end %}\n\n    # Assumes the underlying value is an `Array` or `Hash` and returns its size.\n    #\n    # Raises if the underlying value is not an `Array` or `Hash`.\n    def size : Int\n      case object = @raw\n      when Array\n        object.size\n      when Hash\n        object.size\n      else\n        raise \"Expected Array or Hash for #size, not #{object.class}\"\n      end\n    end\n\n    # Assumes the underlying value is an `Array` or `Hash`\n    # and returns the element at the given *index_or_key*.\n    #\n    # Raises if the underlying value is not an `Array` nor a `Hash`.\n    def [](index_or_key) : self\n      case object = @raw\n      when Array\n        if index_or_key.is_a?(Int)\n          object[index_or_key]\n        else\n          raise \"Expected int key for Array#[], not #{object.class}\"\n        end\n      when Hash\n        object[index_or_key]\n      else\n        raise \"Expected Array or Hash, not #{object.class}\"\n      end\n    end\n\n    # Assumes the underlying value is an `Array` or `Hash` and returns the element\n    # at the given *index_or_key*, or `nil` if out of bounds or the key is missing.\n    #\n    # Raises if the underlying value is not an `Array` nor a `Hash`.\n    def []?(index_or_key) : self?\n      case object = @raw\n      when Array\n        if index_or_key.is_a?(Int)\n          object[index_or_key]?\n        else\n          nil\n        end\n      when Hash\n        object[index_or_key]?\n      else\n        raise \"Expected Array or Hash, not #{object.class}\"\n      end\n    end\n\n    # Traverses the depth of a structure and returns the value.\n    # Returns `nil` if not found.\n    def dig?(index_or_key, *subkeys) : self?\n      self[index_or_key]?.try &.dig?(*subkeys)\n    end\n\n    # :nodoc:\n    def dig?(index_or_key) : self?\n      case @raw\n      when Hash, Array\n        self[index_or_key]?\n      else\n        nil\n      end\n    end\n\n    # Traverses the depth of a structure and returns the value, otherwise raises.\n    def dig(index_or_key, *subkeys) : self\n      self[index_or_key].dig(*subkeys)\n    end\n\n    # :nodoc:\n    def dig(index_or_key) : self\n      self[index_or_key]\n    end\n\n    def inspect(io : IO) : Nil\n      @raw.inspect(io)\n    end\n\n    def to_s(io : IO) : Nil\n      @raw.to_s(io)\n    end\n\n    # :nodoc:\n    def pretty_print(pp)\n      @raw.pretty_print(pp)\n    end\n\n    # Returns `true` if both `self` and *other*'s raw object are equal.\n    def ==(other : self)\n      raw == other.raw\n    end\n\n    # Returns `true` if the raw object is equal to *other*.\n    def ==(other)\n      raw == other\n    end\n\n    # See `Object#hash(hasher)`\n    def_hash raw\n\n    # Returns a new `{{target_type}}` instance with the `raw` value `dup`ed.\n    def dup\n      self.class.new(raw.dup)\n    end\n\n    # Returns a new `{{target_type}}` instance with the `raw` value `clone`ed.\n    def clone\n      self.class.new(raw.clone)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/digest/md5.cr",
    "content": "require \"digest/digest\"\n\n# Implements the MD5 digest algorithm.\n#\n# WARNING: MD5 is no longer a cryptographically secure hash, and should not be\n# used in security-related components, like password hashing. For passwords, see\n# `Crypto::Bcrypt::Password`. For a generic cryptographic hash, use SHA-256 via\n# `OpenSSL::Digest.new(\"SHA256\")`.\nclass Crystal::Digest::MD5 < ::Digest\n  extend ::Digest::ClassMethods\n\n  @i = StaticArray(UInt32, 2).new(0_u32)\n  @buf = StaticArray(UInt32, 4).new(0_u32)\n  @in = StaticArray(UInt8, 64).new(0_u8)\n\n  def initialize\n    reset\n  end\n\n  private def reset_impl : Nil\n    @i[0] = 0_u32\n    @i[1] = 0_u32\n    @buf[0] = 0x67452301_u32\n    @buf[1] = 0xEFCDAB89_u32\n    @buf[2] = 0x98BADCFE_u32\n    @buf[3] = 0x10325476_u32\n  end\n\n  private def update_impl(data : Bytes) : Nil\n    update(data.to_unsafe, data.bytesize.to_u32)\n  end\n\n  private def update(in_buf, in_len)\n    tmp_in = uninitialized UInt32[16]\n\n    # compute number of bytes mod 64\n    mdi = (@i[0] >> 3) & 0x3F\n\n    # update number of bits\n    @i[1] &+= 1 if (@i[0] &+ (in_len << 3)) < @i[0]\n    @i[0] &+= (in_len << 3)\n    @i[1] &+= (in_len >> 29)\n\n    in_len.times do\n      # add new character to buffer, increment mdi\n      @in[mdi] = in_buf.value\n      mdi += 1\n      in_buf += 1\n\n      # transform if necessary\n      if mdi == 0x40\n        ii = 0\n        16.times do |i|\n          tmp_in[i] = (@in[ii + 3].to_u32 << 24) |\n                      (@in[ii + 2].to_u32 << 16) |\n                      (@in[ii + 1].to_u32 << 8) |\n                      (@in[ii])\n          ii += 4\n        end\n        transform tmp_in\n        mdi = 0\n      end\n    end\n  end\n\n  S11 =  7\n  S12 = 12\n  S13 = 17\n  S14 = 22\n\n  S21 =  5\n  S22 =  9\n  S23 = 14\n  S24 = 20\n\n  S31 =  4\n  S32 = 11\n  S33 = 16\n  S34 = 23\n\n  S41 =  6\n  S42 = 10\n  S43 = 15\n  S44 = 21\n\n  private PADDING = begin\n    padding = StaticArray(UInt8, 64).new(0_u8)\n    padding[0] = 0x80_u8\n    padding\n  end\n\n  private def f(x, y, z)\n    (x & y) | ((~x) & z)\n  end\n\n  private def g(x, y, z)\n    (x & z) | (y & (~z))\n  end\n\n  private def h(x, y, z)\n    x ^ y ^ z\n  end\n\n  private def i(x, y, z)\n    y ^ (x | (~z))\n  end\n\n  private def ff(a, b, c, d, x, s, ac)\n    a &+= f(b, c, d) &+ x &+ ac.to_u32\n    a = a.rotate_left s\n    a &+ b\n  end\n\n  private def gg(a, b, c, d, x, s, ac)\n    a &+= g(b, c, d) &+ x &+ ac.to_u32\n    a = a.rotate_left s\n    a &+ b\n  end\n\n  private def hh(a, b, c, d, x, s, ac)\n    a &+= h(b, c, d) &+ x &+ ac.to_u32\n    a = a.rotate_left s\n    a &+ b\n  end\n\n  private def ii(a, b, c, d, x, s, ac)\n    a &+= i(b, c, d) &+ x &+ ac.to_u32\n    a = a.rotate_left s\n    a &+ b\n  end\n\n  private def transform(input)\n    a, b, c, d = @buf\n\n    # Round 1\n    a = ff(a, b, c, d, input[0], S11, 3614090360)  # 1\n    d = ff(d, a, b, c, input[1], S12, 3905402710)  # 2\n    c = ff(c, d, a, b, input[2], S13, 606105819)   # 3\n    b = ff(b, c, d, a, input[3], S14, 3250441966)  # 4\n    a = ff(a, b, c, d, input[4], S11, 4118548399)  # 5\n    d = ff(d, a, b, c, input[5], S12, 1200080426)  # 6\n    c = ff(c, d, a, b, input[6], S13, 2821735955)  # 7\n    b = ff(b, c, d, a, input[7], S14, 4249261313)  # 8\n    a = ff(a, b, c, d, input[8], S11, 1770035416)  # 9\n    d = ff(d, a, b, c, input[9], S12, 2336552879)  # 10\n    c = ff(c, d, a, b, input[10], S13, 4294925233) # 11\n    b = ff(b, c, d, a, input[11], S14, 2304563134) # 12\n    a = ff(a, b, c, d, input[12], S11, 1804603682) # 13\n    d = ff(d, a, b, c, input[13], S12, 4254626195) # 14\n    c = ff(c, d, a, b, input[14], S13, 2792965006) # 15\n    b = ff(b, c, d, a, input[15], S14, 1236535329) # 16\n\n    # Round 2\n    a = gg(a, b, c, d, input[1], S21, 4129170786)  # 17\n    d = gg(d, a, b, c, input[6], S22, 3225465664)  # 18\n    c = gg(c, d, a, b, input[11], S23, 643717713)  # 19\n    b = gg(b, c, d, a, input[0], S24, 3921069994)  # 20\n    a = gg(a, b, c, d, input[5], S21, 3593408605)  # 21\n    d = gg(d, a, b, c, input[10], S22, 38016083)   # 22\n    c = gg(c, d, a, b, input[15], S23, 3634488961) # 23\n    b = gg(b, c, d, a, input[4], S24, 3889429448)  # 24\n    a = gg(a, b, c, d, input[9], S21, 568446438)   # 25\n    d = gg(d, a, b, c, input[14], S22, 3275163606) # 26\n    c = gg(c, d, a, b, input[3], S23, 4107603335)  # 27\n    b = gg(b, c, d, a, input[8], S24, 1163531501)  # 28\n    a = gg(a, b, c, d, input[13], S21, 2850285829) # 29\n    d = gg(d, a, b, c, input[2], S22, 4243563512)  # 30\n    c = gg(c, d, a, b, input[7], S23, 1735328473)  # 31\n    b = gg(b, c, d, a, input[12], S24, 2368359562) # 32\n\n    # Round 3\n    a = hh(a, b, c, d, input[5], S31, 4294588738)  # 33\n    d = hh(d, a, b, c, input[8], S32, 2272392833)  # 34\n    c = hh(c, d, a, b, input[11], S33, 1839030562) # 35\n    b = hh(b, c, d, a, input[14], S34, 4259657740) # 36\n    a = hh(a, b, c, d, input[1], S31, 2763975236)  # 37\n    d = hh(d, a, b, c, input[4], S32, 1272893353)  # 38\n    c = hh(c, d, a, b, input[7], S33, 4139469664)  # 39\n    b = hh(b, c, d, a, input[10], S34, 3200236656) # 40\n    a = hh(a, b, c, d, input[13], S31, 681279174)  # 41\n    d = hh(d, a, b, c, input[0], S32, 3936430074)  # 42\n    c = hh(c, d, a, b, input[3], S33, 3572445317)  # 43\n    b = hh(b, c, d, a, input[6], S34, 76029189)    # 44\n    a = hh(a, b, c, d, input[9], S31, 3654602809)  # 45\n    d = hh(d, a, b, c, input[12], S32, 3873151461) # 46\n    c = hh(c, d, a, b, input[15], S33, 530742520)  # 47\n    b = hh(b, c, d, a, input[2], S34, 3299628645)  # 48\n\n    # Round 4\n    a = ii(a, b, c, d, input[0], S41, 4096336452)  # 49\n    d = ii(d, a, b, c, input[7], S42, 1126891415)  # 50\n    c = ii(c, d, a, b, input[14], S43, 2878612391) # 51\n    b = ii(b, c, d, a, input[5], S44, 4237533241)  # 52\n    a = ii(a, b, c, d, input[12], S41, 1700485571) # 53\n    d = ii(d, a, b, c, input[3], S42, 2399980690)  # 54\n    c = ii(c, d, a, b, input[10], S43, 4293915773) # 55\n    b = ii(b, c, d, a, input[1], S44, 2240044497)  # 56\n    a = ii(a, b, c, d, input[8], S41, 1873313359)  # 57\n    d = ii(d, a, b, c, input[15], S42, 4264355552) # 58\n    c = ii(c, d, a, b, input[6], S43, 2734768916)  # 59\n    b = ii(b, c, d, a, input[13], S44, 1309151649) # 60\n    a = ii(a, b, c, d, input[4], S41, 4149444226)  # 61\n    d = ii(d, a, b, c, input[11], S42, 3174756917) # 62\n    c = ii(c, d, a, b, input[2], S43, 718787259)   # 63\n    b = ii(b, c, d, a, input[9], S44, 3951481745)  # 64\n\n    @buf[0] &+= a\n    @buf[1] &+= b\n    @buf[2] &+= c\n    @buf[3] &+= d\n  end\n\n  private def final_impl(dst : Bytes) : Nil\n    tmp_in = uninitialized UInt32[16]\n\n    # save number of bits\n    tmp_in[14] = @i[0]\n    tmp_in[15] = @i[1]\n\n    # compute number of bytes mod 64\n    mdi = ((@i[0] >> 3) & 0x3F).to_i32\n\n    # pad out to 56 mod 64\n    pad_len = (mdi < 56) ? (56 - mdi) : (120 - mdi)\n    update PADDING.to_unsafe, pad_len\n\n    # append length in bits and transform\n    ii = 0\n    14.times do |i|\n      tmp_in[i] = (@in[ii + 3].to_u32 << 24) |\n                  (@in[ii + 2].to_u32 << 16) |\n                  (@in[ii + 1].to_u32 << 8) |\n                  (@in[ii])\n      ii += 4\n    end\n    transform tmp_in\n\n    # store buffer in digest\n    ii = 0\n    4.times do |i|\n      dst[ii] = (@buf[i] & 0xff).to_u8\n      dst[ii + 1] = ((@buf[i] >> 8) & 0xFF).to_u8\n      dst[ii + 2] = ((@buf[i] >> 16) & 0xFF).to_u8\n      dst[ii + 3] = ((@buf[i] >> 24) & 0xFF).to_u8\n      ii += 4\n    end\n  end\n\n  def digest_size : Int32\n    16\n  end\nend\n"
  },
  {
    "path": "src/crystal/digest/sha1.cr",
    "content": "require \"digest/digest\"\n\n# Implements the SHA1 digest algorithm.\n#\n# WARNING: SHA1 is no longer a cryptographically secure hash, and should not be\n# used in security-related components, like password hashing. For passwords, see\n# `Crypto::Bcrypt::Password`. For a generic cryptographic hash, use SHA-256 via\n# `OpenSSL::Digest.new(\"SHA256\")`.\nclass Crystal::Digest::SHA1 < ::Digest\n  extend ::Digest::ClassMethods\n\n  # This is a direct translation of https://tools.ietf.org/html/rfc3174#section-7\n  # but we use loop unrolling for faster execution (about 1.07x slower than OpenSSL::SHA1).\n\n  @intermediate_hash = uninitialized UInt32[5]\n  @length_low = 0_u32\n  @length_high = 0_u32\n  @message_block_index = 0\n  @message_block = StaticArray(UInt8, 64).new(0_u8) # uninitialized UInt8[64]\n\n  def initialize\n    reset\n  end\n\n  private def reset_impl : Nil\n    @length_low = 0_u32\n    @length_high = 0_u32\n    @message_block_index = 0\n    @intermediate_hash[0] = 0x67452301_u32\n    @intermediate_hash[1] = 0xEFCDAB89_u32\n    @intermediate_hash[2] = 0x98BADCFE_u32\n    @intermediate_hash[3] = 0x10325476_u32\n    @intermediate_hash[4] = 0xC3D2E1F0_u32\n  end\n\n  private def update_impl(data : Bytes) : Nil\n    data.each do |byte|\n      @message_block[@message_block_index] = byte & 0xFF_u8\n      @message_block_index += 1\n      @length_low &+= 8\n\n      if @length_low == 0\n        @length_high &+= 1\n        if @length_high == 0\n          raise ArgumentError.new \"Message too long\"\n        end\n      end\n\n      if @message_block_index == 64\n        process_message_block\n      end\n    end\n  end\n\n  private def final_impl(dst : Bytes) : Nil\n    pad_message\n\n    @length_low = 0_u32\n    @length_high = 0_u32\n    {% for i in 0...20 %}\n      dst[{{i}}] = (@intermediate_hash[{{i >> 2}}] >> 8 * (3 - ({{i & 0x03}}))).to_u8!\n    {% end %}\n  end\n\n  private def process_message_block\n    k = {0x5A827999_u32, 0x6ED9EBA1_u32, 0x8F1BBCDC_u32, 0xCA62C1D6_u32}\n\n    w = uninitialized UInt32[80]\n\n    {% for t in (0...16) %}\n      w[{{t}}] = @message_block[{{t}} * 4].to_u32 << 24\n      w[{{t}}] |= @message_block[{{t}} * 4 + 1].to_u32 << 16\n      w[{{t}}] |= @message_block[{{t}} * 4 + 2].to_u32 << 8\n      w[{{t}}] |= @message_block[{{t}} * 4 + 3].to_u32\n    {% end %}\n\n    {% for t in (16...80) %}\n      w[{{t}}] = (w[{{t - 3}}] ^ w[{{t - 8}}] ^ w[{{t - 14}}] ^ w[{{t - 16}}]).rotate_left(1)\n    {% end %}\n\n    a = @intermediate_hash[0]\n    b = @intermediate_hash[1]\n    c = @intermediate_hash[2]\n    d = @intermediate_hash[3]\n    e = @intermediate_hash[4]\n\n    {% for t in (0...20) %}\n      temp = a.rotate_left(5) &+\n        ((b & c) | ((~b) & d)) &+ e &+ w[{{t}}] &+ k[0]\n      e = d\n      d = c\n      c = b.rotate_left(30)\n      b = a\n      a = temp\n    {% end %}\n\n    {% for t in (20...40) %}\n      temp = a.rotate_left(5) &+ (b ^ c ^ d) &+ e &+ w[{{t}}] &+ k[1]\n      e = d\n      d = c\n      c = b.rotate_left(30)\n      b = a\n      a = temp\n    {% end %}\n\n    {% for t in (40...60) %}\n      temp = a.rotate_left(5) &+\n        ((b & c) | (b & d) | (c & d)) &+ e &+ w[{{t}}] &+ k[2]\n      e = d\n      d = c\n      c = b.rotate_left(30)\n      b = a\n      a = temp\n    {% end %}\n\n    {% for t in (60...80) %}\n      temp = a.rotate_left(5) &+ (b ^ c ^ d) &+ e &+ w[{{t}}] &+ k[3]\n      e = d\n      d = c\n      c = b.rotate_left(30)\n      b = a\n      a = temp\n    {% end %}\n\n    @intermediate_hash[0] &+= a\n    @intermediate_hash[1] &+= b\n    @intermediate_hash[2] &+= c\n    @intermediate_hash[3] &+= d\n    @intermediate_hash[4] &+= e\n\n    @message_block_index = 0\n  end\n\n  private def pad_message\n    if @message_block_index > 55\n      @message_block[@message_block_index] = 0x80_u8\n      @message_block_index += 1\n      while @message_block_index < 64\n        @message_block[@message_block_index] = 0_u8\n        @message_block_index += 1\n      end\n\n      process_message_block\n\n      while @message_block_index < 56\n        @message_block[@message_block_index] = 0_u8\n        @message_block_index += 1\n      end\n    else\n      @message_block[@message_block_index] = 0x80_u8\n      @message_block_index += 1\n      while @message_block_index < 56\n        @message_block[@message_block_index] = 0_u8\n        @message_block_index += 1\n      end\n    end\n\n    @message_block[56] = (@length_high >> 24).to_u8!\n    @message_block[57] = (@length_high >> 16).to_u8!\n    @message_block[58] = (@length_high >> 8).to_u8!\n    @message_block[59] = (@length_high).to_u8!\n    @message_block[60] = (@length_low >> 24).to_u8!\n    @message_block[61] = (@length_low >> 16).to_u8!\n    @message_block[62] = (@length_low >> 8).to_u8!\n    @message_block[63] = (@length_low).to_u8!\n\n    process_message_block\n  end\n\n  def digest_size : Int32\n    20\n  end\nend\n"
  },
  {
    "path": "src/crystal/dwarf/abbrev.cr",
    "content": "require \"../dwarf\"\n\nmodule Crystal\n  module DWARF\n    enum TAG : UInt32\n      ArrayType       = 0x01\n      ClassType       = 0x02\n      EntryPoint      = 0x03\n      EnumerationType = 0x04\n      FormalParameter = 0x05\n\n      ImportedDeclaration    = 0x08\n      Label                  = 0x0a\n      LexicalBlock           = 0x0b\n      Member                 = 0x0d\n      PointerType            = 0x0f\n      ReferenceType          = 0x10\n      CompileUnit            = 0x11\n      StringType             = 0x12\n      StructureType          = 0x13\n      SubroutineType         = 0x15\n      Typedef                = 0x16\n      UnionType              = 0x17\n      UnspecifiedParameters  = 0x18\n      Variant                = 0x19\n      CommonBlock            = 0x1a\n      CommonInclusion        = 0x1b\n      Inheritance            = 0x1c\n      InlinedSubroutine      = 0x1d\n      Module                 = 0x1e\n      PtrToMemberType        = 0x1f\n      SetType                = 0x20\n      SubrangeType           = 0x21\n      WithStmt               = 0x22\n      AccessDeclaration      = 0x23\n      BaseType               = 0x24\n      CatchBlock             = 0x25\n      ConstType              = 0x26\n      Constant               = 0x27\n      Enumerator             = 0x28\n      FileType               = 0x29\n      Friend                 = 0x2a\n      Namelist               = 0x2b\n      NamelistItem           = 0x2c\n      PackedType             = 0x2d\n      Subprogram             = 0x2e\n      TemplateTypeParameter  = 0x2f\n      TemplateValueParameter = 0x30\n      ThrownType             = 0x31\n      TryBlock               = 0x32\n      VariantPart            = 0x33\n      Variable               = 0x34\n      VolatileType           = 0x35\n      DwarfProcedure         = 0x36\n      RestrictType           = 0x37\n      InterfaceType          = 0x38\n      Namespace              = 0x39\n      ImportedModule         = 0x3a\n      UnspecifiedType        = 0x3b\n      PartialUnit            = 0x3c\n      ImportedUnit           = 0x3d\n      Condition              = 0x3f\n      SharedType             = 0x40\n      TypeUnit               = 0x41\n      RvalueReferenceType    = 0x42\n      TemplateAlias          = 0x43\n    end\n\n    enum AT : UInt32\n      DW_AT_sibling              = 0x01 # reference\n      DW_AT_location             = 0x02 # exprloc, loclistptr\n      DW_AT_name                 = 0x03 # string\n      DW_AT_ordering             = 0x09 # constant\n      DW_AT_byte_size            = 0x0b # constant, exprloc, reference\n      DW_AT_bit_offset           = 0x0c # constant, exprloc, reference\n      DW_AT_bit_size             = 0x0d # constant, exprloc, reference\n      DW_AT_stmt_list            = 0x10 # lineptr\n      DW_AT_low_pc               = 0x11 # address\n      DW_AT_high_pc              = 0x12 # address, constant\n      DW_AT_language             = 0x13 # constant\n      DW_AT_discr                = 0x15 # reference\n      DW_AT_discr_value          = 0x16 # constant\n      DW_AT_visibility           = 0x17 # constant\n      DW_AT_import               = 0x18 # reference\n      DW_AT_string_length        = 0x19 # exprloc, loclistptr\n      DW_AT_common_reference     = 0x1a # reference\n      DW_AT_comp_dir             = 0x1b # string\n      DW_AT_const_value          = 0x1c # block, constant, string\n      DW_AT_containing_type      = 0x1d # reference\n      DW_AT_default_value        = 0x1e # reference\n      DW_AT_inline               = 0x20 # constant\n      DW_AT_is_optional          = 0x21 # flag\n      DW_AT_lower_bound          = 0x22 # constant, exprloc, reference\n      DW_AT_producer             = 0x25 # string\n      DW_AT_prototyped           = 0x27 # flag\n      DW_AT_return_addr          = 0x2a # exprloc, loclistptr\n      DW_AT_start_scope          = 0x2c # constant, rangelistptr\n      DW_AT_bit_stride           = 0x2e # constant, exprloc, reference\n      DW_AT_upper_bound          = 0x2f # constant, exprloc, reference\n      DW_AT_abstract_origin      = 0x31 # reference\n      DW_AT_accessibility        = 0x32 # constant\n      DW_AT_address_class        = 0x33 # constant\n      DW_AT_artificial           = 0x34 # flag\n      DW_AT_base_types           = 0x35 # reference\n      DW_AT_calling_convention   = 0x36 # constant\n      DW_AT_count                = 0x37 # constant, exprloc, reference\n      DW_AT_data_member_location = 0x38 # constant, exprloc, loclistptr\n      DW_AT_decl_column          = 0x39 # constant\n      DW_AT_decl_file            = 0x3a # constant\n      DW_AT_decl_line            = 0x3b # constant\n      DW_AT_declaration          = 0x3c # flag\n      DW_AT_discr_list           = 0x3d # block\n      DW_AT_encoding             = 0x3e # constant\n      DW_AT_external             = 0x3f # flag\n      DW_AT_frame_base           = 0x40 # exprloc, loclistptr\n      DW_AT_friend               = 0x41 # reference\n      DW_AT_identifier_case      = 0x42 # constant\n      DW_AT_macro_info           = 0x43 # macptr\n      DW_AT_namelist_item        = 0x44 # reference\n      DW_AT_priority             = 0x45 # reference\n      DW_AT_segment              = 0x46 # exprloc, loclistptr\n      DW_AT_specification        = 0x47 # reference\n      DW_AT_static_link          = 0x48 # exprloc, loclistptr\n      DW_AT_type                 = 0x49 # reference\n      DW_AT_use_location         = 0x4a # exprloc, loclistptr\n      DW_AT_variable_parameter   = 0x4b # flag\n      DW_AT_virtuality           = 0x4c # constant\n      DW_AT_vtable_elem_location = 0x4d # exprloc, loclistptr\n      DW_AT_allocated            = 0x4e # constant, exprloc, reference\n      DW_AT_associated           = 0x4f # constant, exprloc, reference\n      DW_AT_data_location        = 0x50 # exprloc\n      DW_AT_byte_stride          = 0x51 # constant, exprloc, reference\n      DW_AT_entry_pc             = 0x52 # address\n      DW_AT_use_UTF8             = 0x53 # flag\n      DW_AT_extension            = 0x54 # reference\n      DW_AT_ranges               = 0x55 # rangelistptr\n      DW_AT_trampoline           = 0x56 # address, flag, reference, string\n      DW_AT_call_column          = 0x57 # constant\n      DW_AT_call_file            = 0x58 # constant\n      DW_AT_call_line            = 0x59 # constant\n      DW_AT_description          = 0x5a # string\n      DW_AT_binary_scale         = 0x5b # constant\n      DW_AT_decimal_scale        = 0x5c # constant\n      DW_AT_small                = 0x5d # reference\n      DW_AT_decimal_sign         = 0x5e # constant\n      DW_AT_digit_count          = 0x5f # constant\n      DW_AT_picture_string       = 0x60 # string\n      DW_AT_mutable              = 0x61 # flag\n      DW_AT_threads_scaled       = 0x62 # flag\n      DW_AT_explicit             = 0x63 # flag\n      DW_AT_object_pointer       = 0x64 # reference\n      DW_AT_endianity            = 0x65 # constant\n      DW_AT_elemental            = 0x66 # flag\n      DW_AT_pure                 = 0x67 # flag\n      DW_AT_recursive            = 0x68 # flag\n      DW_AT_signature            = 0x69 # reference\n      DW_AT_main_subprogram      = 0x6a # flag\n      DW_AT_data_bit_offset      = 0x6b # constant\n      DW_AT_const_expr           = 0x6c # flag\n      DW_AT_enum_class           = 0x6d # flag\n      DW_AT_linkage_name         = 0x6e # string\n\n      def unknown?\n        AT.from_value?(value).nil?\n      end\n    end\n\n    # DWARF data encoding format.\n    enum FORM : UInt32\n      # value formats\n      Addr     = 0x01 # address\n      Block2   = 0x03 # block\n      Block4   = 0x04 # block\n      Data2    = 0x05 # constant\n      Data4    = 0x06 # constant\n      Data8    = 0x07 # constant\n      String   = 0x08 # string\n      Block    = 0x09 # block\n      Block1   = 0x0a # block\n      Data1    = 0x0b # constant\n      Flag     = 0x0c # flag\n      Sdata    = 0x0d # constant\n      Strp     = 0x0e # string\n      Udata    = 0x0f # constant\n      RefAddr  = 0x10 # reference\n      Ref1     = 0x11 # reference\n      Ref2     = 0x12 # reference\n      Ref4     = 0x13 # reference\n      Ref8     = 0x14 # reference\n      RefUdata = 0x15 # reference\n      Indirect = 0x16 # (see Section 7.5.3)\n\n      # The following are new in DWARF 4.\n      SecOffset   = 0x17 # lineptr, loclistptr, macptr, rangelistptr\n      Exprloc     = 0x18 # exprloc\n      FlagPresent = 0x19 # flag\n      RefSig8     = 0x20 # reference\n\n      # The following are new in DWARF 5.\n      Strx          = 0x1a\n      Addrx         = 0x1b\n      Refsup4       = 0x1c\n      StrpSup       = 0x1d\n      Data16        = 0x1e\n      LineStrp      = 0x1f\n      ImplicitConst = 0x21\n      Loclistx      = 0x22\n      Rnglistx      = 0x23\n      Refsup8       = 0x24\n      Strx1         = 0x25\n      Strx2         = 0x26\n      Strx3         = 0x27\n      Strx4         = 0x28\n      Addrx1        = 0x29\n      Addrx2        = 0x2a\n      Addrx3        = 0x2b\n      Addrx4        = 0x2c\n\n      # Extensions for multi-file compression (.dwz)\n      # http://www.dwarfstd.org/ShowIssue.php?issue=120604.1\n      Gnurefalt  = 0x1f20\n      GnustrpAlt = 0x1f21\n    end\n\n    struct Abbrev\n      record Attribute, at : AT, form : FORM, value : Int32\n\n      property code : UInt32\n      property tag : TAG\n      property attributes : Array(Attribute)\n\n      def initialize(@code, @tag, @children : Bool)\n        @attributes = [] of Attribute\n      end\n\n      def children?\n        @children\n      end\n\n      def self.read(io : IO) : Array(Abbrev)\n        abbreviations = [] of Abbrev\n\n        loop do\n          code = DWARF.read_unsigned_leb128(io)\n          break if code == 0\n\n          tag = TAG.new(DWARF.read_unsigned_leb128(io))\n          children = io.read_byte == 1\n          abbrev = Abbrev.new(code, tag, children)\n\n          loop do\n            at = AT.new(DWARF.read_unsigned_leb128(io))\n            form = FORM.new(DWARF.read_unsigned_leb128(io))\n            break if at.value == 0 && form.value == 0\n            if form.implicit_const?\n              value = DWARF.read_signed_leb128(io)\n            else\n              value = 0\n            end\n            abbrev.attributes << Attribute.new(at, form, value)\n          end\n\n          abbreviations << abbrev\n        end\n\n        abbreviations\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/dwarf/info.cr",
    "content": "require \"../dwarf\"\nrequire \"./abbrev\"\n\nmodule Crystal\n  module DWARF\n    struct Info\n      property unit_length : UInt32 | UInt64\n      property version : UInt16\n      property unit_type : UInt8\n      property debug_abbrev_offset : UInt32 | UInt64\n      property address_size : UInt8\n      property! abbreviations : Array(Abbrev)\n\n      property dwarf64 : Bool\n      @offset : Int64\n      @ref_offset : Int64\n\n      def initialize(@io : IO::FileDescriptor, @offset)\n        @ref_offset = offset\n\n        @unit_length = @io.read_bytes(UInt32)\n        if @unit_length == 0xffffffff\n          @dwarf64 = true\n          @unit_length = @io.read_bytes(UInt64)\n        else\n          @dwarf64 = false\n        end\n\n        @offset = @io.tell\n        @version = @io.read_bytes(UInt16)\n\n        if @version < 2 || @version > 5\n          raise \"Unsupported DWARF version #{@version}\"\n        end\n\n        if @version >= 5\n          @unit_type = @io.read_bytes(UInt8)\n          @address_size = @io.read_bytes(UInt8)\n          @debug_abbrev_offset = read_ulong\n        else\n          @unit_type = 0\n          @debug_abbrev_offset = read_ulong\n          @address_size = @io.read_bytes(UInt8)\n        end\n\n        if @address_size.zero?\n          raise \"Invalid address size: 0\"\n        end\n      end\n\n      alias Value = Bool | Int32 | Int64 | Slice(UInt8) | String | UInt16 | UInt32 | UInt64 | UInt8 | UInt128\n\n      def each(&)\n        end_offset = @offset + @unit_length\n        attributes = [] of {AT, FORM, Value}\n\n        while @io.tell < end_offset\n          code = DWARF.read_unsigned_leb128(@io)\n          attributes.clear\n\n          if abbrev = @abbreviations.try &.[code &- 1]? # @abbreviations.try &.find { |a| a.code == abbrev }\n            abbrev.attributes.each do |attr|\n              value = read_attribute_value(attr.form, attr)\n              attributes << {attr.at, attr.form, value}\n            end\n            yield code, abbrev, attributes\n          else\n            yield code, nil, attributes\n          end\n        end\n      end\n\n      private def read_attribute_value(form, attr)\n        case form\n        when FORM::Addr\n          case address_size\n          when 4 then @io.read_bytes(UInt32)\n          when 8 then @io.read_bytes(UInt64)\n          else        raise \"Invalid address size: #{address_size}\"\n          end\n        when FORM::Block1\n          len = @io.read_byte.not_nil!\n          @io.read_fully(bytes = Bytes.new(len.to_i))\n          bytes\n        when FORM::Block2\n          len = @io.read_bytes(UInt16)\n          @io.read_fully(bytes = Bytes.new(len.to_i))\n          bytes\n        when FORM::Block4\n          len = @io.read_bytes(UInt32)\n          @io.read_fully(bytes = Bytes.new(len.to_i64))\n          bytes\n        when FORM::Block\n          len = DWARF.read_unsigned_leb128(@io)\n          @io.read_fully(bytes = Bytes.new(len))\n          bytes\n        when FORM::Data1\n          @io.read_byte.not_nil!\n        when FORM::Data2\n          @io.read_bytes(UInt16)\n        when FORM::Data4\n          @io.read_bytes(UInt32)\n        when FORM::Data8\n          @io.read_bytes(UInt64)\n        when FORM::Data16\n          @io.read_bytes(UInt128)\n        when FORM::Sdata\n          DWARF.read_signed_leb128(@io)\n        when FORM::Udata\n          DWARF.read_unsigned_leb128(@io)\n        when FORM::ImplicitConst\n          attr.value\n        when FORM::Exprloc\n          len = DWARF.read_unsigned_leb128(@io)\n          @io.read_fully(bytes = Bytes.new(len))\n          bytes\n        when FORM::Flag\n          @io.read_byte == 1\n        when FORM::FlagPresent\n          true\n        when FORM::SecOffset\n          read_ulong\n        when FORM::Ref1\n          @ref_offset + @io.read_byte.not_nil!.to_u64\n        when FORM::Ref2\n          @ref_offset + @io.read_bytes(UInt16).to_u64\n        when FORM::Ref4\n          @ref_offset + @io.read_bytes(UInt32).to_u64\n        when FORM::Ref8\n          @ref_offset + @io.read_bytes(UInt64).to_u64\n        when FORM::RefUdata\n          @ref_offset + DWARF.read_unsigned_leb128(@io)\n        when FORM::RefAddr\n          read_ulong\n        when FORM::RefSig8\n          @io.read_bytes(UInt64)\n        when FORM::String\n          @io.gets('\\0', chomp: true).to_s\n        when FORM::Strp, FORM::LineStrp\n          # HACK: A call to read_ulong is failing with an .ud2 / Illegal instruction: 4 error\n          #       Calling with @[AlwaysInline] makes no difference.\n          if @dwarf64\n            @io.read_bytes(UInt64)\n          else\n            @io.read_bytes(UInt32)\n          end\n        when FORM::Indirect\n          form = FORM.new(DWARF.read_unsigned_leb128(@io))\n          read_attribute_value(form, attr)\n        else\n          raise \"Unknown DW_FORM_#{form.to_s.underscore}\"\n        end\n      end\n\n      private def read_ulong\n        if @dwarf64\n          @io.read_bytes(UInt64)\n        else\n          @io.read_bytes(UInt32)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/dwarf/line_numbers.cr",
    "content": "module Crystal\n  module DWARF\n    # :nodoc:\n    #\n    # Standard Line Number opcodes.\n    enum LNS : UInt8\n      Copy             =  1\n      AdvancePc        =  2\n      AdvanceLine      =  3\n      SetFile          =  4\n      SetColumn        =  5\n      NegateStmt       =  6\n      SetBasicBlock    =  7\n      ConstAddPc       =  8\n      FixedAdvancePc   =  9\n      SetPrologueEnd   = 10\n      SetEpilogueBegin = 11\n      SetIsa           = 12\n    end\n\n    # :nodoc:\n    #\n    # Extended Line Number opcodes.\n    enum LNE : UInt8\n      EndSequence      = 1\n      SetAddress       = 2\n      DefineFile       = 3\n      SetDiscriminator = 4\n    end\n\n    # DWARF Line Numbers parser. Supports DWARF versions 2, 3 and 4.\n    #\n    # Usually located in the `.debug_line` section of ELF executables, or the\n    # `__debug_line` section of Mach-O files.\n    #\n    # Documentation:\n    # - [DWARF2](http://dwarfstd.org/doc/dwarf-2.0.0.pdf) section 6.2\n    # - [DWARF3](http://dwarfstd.org/doc/Dwarf3.pdf) section 6.2\n    # - [DWARF4](http://dwarfstd.org/doc/DWARF4.pdf) section 6.2\n    struct LineNumbers\n      # :nodoc:\n      #\n      # The state machine registers used to decompress the line number\n      # sequences.\n      struct Register\n        # The Program Counter (PC) value corresponding to a machine instruction\n        # generated by the compiler.\n        property address : UInt64\n\n        # The index of an operation inside a Very Long Instruction Word (VLIW)\n        # instruction. Together with `address` they reference an individual\n        # operation.\n        property op_index : UInt32\n\n        # Source file for the instruction.\n        property file : UInt32\n\n        # Line number within the source file. Starting at 1; the value 0 means\n        # that the instruction can't be attributed to any source line.\n        property line : UInt32\n\n        # Column number within the source file. Starting at 1; the value 0 means\n        # that a statement begins at the \"left edge\" of the line.\n        property column : UInt32\n\n        # Recommended breakpoint location.\n        property is_stmt : Bool\n\n        # Indicates that the instruction is the beginning of a basic block.\n        property basic_block : Bool\n\n        # Terminates a sequence of lines. Other information in the same row (of\n        # the decoded matrix) isn't meaningful.\n        property end_sequence : Bool\n\n        # Indicates the instruction is one where execution should be\n        # suspended (for an entry breakpoint).\n        property prologue_end : Bool\n\n        # Indicates the instruction is one where execution should be\n        # suspended (for an exit breakpoint).\n        property epilogue_begin : Bool\n\n        # Applicable Instruction Set Architecture for the instruction.\n        property isa : UInt32\n\n        # Identifies the block to which the instruction belongs.\n        property discriminator : UInt32\n\n        def initialize(@is_stmt)\n          @address = 0_u64\n          @op_index = 0_u32\n          @file = 1_u32\n          @line = 1_u32\n          @column = 0_u32\n          @basic_block = false\n          @end_sequence = false\n          @prologue_end = false\n          @epilogue_begin = false\n          @isa = 0_u32\n          @discriminator = 0_u32\n        end\n\n        def reset\n          @basic_block = false\n          @prologue_end = false\n          @epilogue_begin = false\n          @discriminator = 0_u32\n        end\n      end\n\n      # The decoded line number information for an instruction.\n      record Row,\n        address : UInt64,\n        path : String,\n        line : Int32,\n        column : Int32\n\n      # :nodoc:\n      #\n      # An individual compressed sequence.\n      record Sequence,\n        offset : Int64,\n        unit_length : UInt32,\n        minimum_instruction_length : Int32,\n        maximum_operations_per_instruction : Int32,\n        default_is_stmt : Bool,\n        line_base : Int32,\n        line_range : Int32,\n        opcode_base : Int32,\n        # An array of how many args an array. Starts at 1 because 0 means an\n        # extended opcode.\n        standard_opcode_lengths : Array(UInt8),\n        # An array of directory names. Starts at 1; 0 means that the information\n        # is missing.\n        include_directories : Array(String),\n        # An array of file names. Starts at 1; 0 means that the information is\n        # missing.\n        file_names : Array(FileEntry) do\n        record FileEntry,\n          path : String,\n          mtime : UInt64,\n          size : UInt64\n\n        # Returns the unit length, adding the size of the `unit_length`.\n        def total_length\n          unit_length + sizeof(typeof(unit_length))\n        end\n      end\n\n      # Matrix of decompressed `Row` to search line number information from the\n      # address of an instruction.\n      #\n      # The matrix contains indexed references to `directories` and `files` to\n      # reduce the memory usage of repeating a String many times.\n      getter matrix : Array(Array(Row))\n\n      @offset : Int64\n\n      def initialize(@io : IO::FileDescriptor, size, @base_address : LibC::SizeT = 0, @strings : Strings? = nil, @line_strings : Strings? = nil)\n        @offset = @io.tell\n        @matrix = Array(Array(Row)).new\n        decode_sequences(size)\n\n        @matrix.sort_by!(&.first.address)\n      end\n\n      # Returns the `Row` for the given Program Counter (PC) address if found.\n      def find(address)\n        rows = matrix.bsearch { |r| r.last.address >= address } || return\n        return unless address >= rows.first.address\n\n        next_row_index = rows.bsearch_index { |row| row.address > address } || rows.size\n        rows[next_row_index - 1]\n      end\n\n      # Decodes the compressed matrix of addresses to line numbers.\n      private def decode_sequences(size)\n        while true\n          pos = @io.tell\n          offset = pos - @offset\n          break unless offset < size\n\n          unit_length = @io.read_bytes(UInt32)\n          total_length = unit_length + sizeof(typeof(unit_length))\n          version = @io.read_bytes(UInt16)\n\n          if version < 2 || version > 5\n            raise \"Unknown line table version: #{version}\"\n          end\n\n          if version >= 5\n            _address_size = @io.read_bytes(UInt8).to_i\n            _segment_selector_size = @io.read_bytes(UInt8).to_i\n          else\n            _address_size = {{ flag?(:bits64) ? 8 : 4 }}\n            _segment_selector_size = 0\n          end\n\n          _header_length = @io.read_bytes(UInt32) # FIXME: UInt64 for DWARF64 (uncommon)\n          minimum_instruction_length = @io.read_bytes(UInt8).to_i\n\n          if version >= 4\n            maximum_operations_per_instruction = @io.read_bytes(UInt8).to_i\n          else\n            maximum_operations_per_instruction = 1\n          end\n\n          if maximum_operations_per_instruction == 0\n            raise \"Invalid maximum operations per instruction: 0\"\n          end\n\n          default_is_stmt = @io.read_byte == 1\n          line_base = @io.read_bytes(Int8).to_i\n          line_range = @io.read_bytes(UInt8).to_i\n          if line_range == 0\n            raise \"Invalid line range: 0\"\n          end\n\n          opcode_base = @io.read_bytes(UInt8).to_i\n          standard_opcode_lengths = Array(UInt8).new(opcode_base)\n          standard_opcode_lengths << 0_u8\n          (opcode_base - 1).times do\n            standard_opcode_lengths << @io.read_byte.not_nil!\n          end\n\n          if version < 5\n            include_directories = read_directory_table\n            file_names = read_filename_table(include_directories)\n          else\n            dir_format = read_lnct_format\n            count = DWARF.read_unsigned_leb128(@io)\n            include_directories = Array.new(count) { read_lnct(nil, dir_format).path }\n\n            file_format = read_lnct_format\n            count = DWARF.read_unsigned_leb128(@io)\n            file_names = Array.new(count) { read_lnct(include_directories, file_format) }\n          end\n\n          if @io.tell - @offset < offset + total_length\n            sequence = Sequence.new(\n              offset,\n              unit_length,\n              minimum_instruction_length,\n              maximum_operations_per_instruction,\n              default_is_stmt,\n              line_base,\n              line_range,\n              opcode_base,\n              standard_opcode_lengths,\n              include_directories,\n              file_names,\n            )\n            read_statement_program(sequence)\n          end\n        end\n      end\n\n      record LNCTFormat,\n        lnct : LNCT,\n        format : FORM\n\n      # :nodoc:\n      #\n      # DWARF-defined content type codes\n      # New in DWARF 5 § 6.2.4.1\n      enum LNCT : UInt32\n        PATH            = 0x01\n        DIRECTORY_INDEX = 0x02\n        TIMESTAMP       = 0x03\n        SIZE            = 0x04\n        MD5             = 0x05\n      end\n\n      private def read_lnct_format\n        count = @io.read_bytes(UInt8)\n        Array(LNCTFormat).new(count) do\n          LNCTFormat.new(\n            lnct: LNCT.new(DWARF.read_unsigned_leb128(@io)),\n            format: FORM.new(DWARF.read_unsigned_leb128(@io))\n          )\n        end\n      end\n\n      private def read_lnct(include_directories, formats)\n        dir = \"\"\n        path = \"\"\n        mtime = 0_u64\n        size = 0_u64\n\n        formats.each do |format|\n          str = nil\n          val = 0_u64\n\n          case format.format\n          when .string?\n            str = @io.gets('\\0', chomp: true) # .to_s\n          when .line_strp?\n            offset = @io.read_bytes(UInt32)\n            str = @line_strings.try &.decode(offset)\n          when .strp?\n            offset = @io.read_bytes(UInt32)\n            str = @strings.try &.decode(offset)\n          when .strp_sup?\n            @io.read_bytes(UInt32)\n          when .strx?\n            # .debug_line.dwo sections not yet supported.\n            DWARF.read_unsigned_leb128(@io)\n          when .strx1?\n            @io.read_bytes(UInt8)\n          when .strx2?\n            @io.read_bytes(UInt16)\n          when .strx3?\n            @io.skip 3\n          when .strx4?\n            @io.read_bytes(UInt32)\n          when .data1?\n            val = @io.read_bytes(UInt8).to_u64\n          when .data2?\n            val = @io.read_bytes(UInt16).to_u64\n          when .data4?\n            val = @io.read_bytes(UInt32).to_u64\n          when .data8?\n            val = @io.read_bytes(UInt64)\n          when .data16?\n            @io.skip(16)\n          when .block?\n            @io.skip(DWARF.read_unsigned_leb128(@io))\n          when .udata?\n            val = DWARF.read_unsigned_leb128(@io)\n          else\n            raise \"Unexpected encoding format: #{format.format}\"\n          end\n\n          case format.lnct\n          in .path?\n            path = str if str\n          in .directory_index?\n            if val\n              dir = include_directories.not_nil![val]\n            end\n          in .timestamp?\n            mtime = val.to_u64\n          in .size?\n            size = val.to_u64\n          in .md5?\n            # ignore\n          end\n        end\n\n        if dir != \"\" && path != \"\"\n          path = File.join(dir, path)\n        end\n\n        Sequence::FileEntry.new(path, mtime, size)\n      end\n\n      private def read_directory_table\n        ary = [\"\"]\n        loop do\n          name = @io.gets('\\0', chomp: true).to_s\n          break if name.empty?\n          ary << name\n        end\n        ary\n      end\n\n      private def read_filename_table(include_directories)\n        ary = [Sequence::FileEntry.new(\"\", 0, 0)]\n        loop do\n          name = @io.gets('\\0', chomp: true).to_s\n          break if name.empty?\n          dir = DWARF.read_unsigned_leb128(@io)\n          time = DWARF.read_unsigned_leb128(@io)\n          length = DWARF.read_unsigned_leb128(@io)\n\n          dir = include_directories[dir]\n          if (name != \"\" && dir != \"\")\n            name = File.join(dir, name)\n          end\n          ary << Sequence::FileEntry.new(name, time.to_u64, length.to_u64)\n        end\n        ary\n      end\n\n      private macro increment_address_and_op_index(operation_advance)\n        if sequence.maximum_operations_per_instruction == 1\n          registers.address += {{operation_advance}} * sequence.minimum_instruction_length\n        else\n          registers.address += sequence.minimum_instruction_length *\n            ((registers.op_index + operation_advance) // sequence.maximum_operations_per_instruction)\n          registers.op_index = (registers.op_index + operation_advance) % sequence.maximum_operations_per_instruction\n        end\n      end\n\n      # TODO: support LNE::DefineFile (manually register file, uncommon)\n      private def read_statement_program(sequence)\n        registers = Register.new(sequence.default_is_stmt)\n\n        loop do\n          opcode = @io.read_byte.not_nil!\n\n          if opcode >= sequence.opcode_base\n            # special opcode\n            adjusted_opcode = opcode - sequence.opcode_base\n            operation_advance = adjusted_opcode // sequence.line_range\n            increment_address_and_op_index(operation_advance)\n            registers.line &+= sequence.line_base + (adjusted_opcode % sequence.line_range)\n            register_to_matrix(sequence, registers)\n            registers.reset\n          elsif opcode == 0\n            # extended opcode\n            len = DWARF.read_unsigned_leb128(@io) - 1 # -1 accounts for the opcode\n            extended_opcode = LNE.new(@io.read_byte.not_nil!)\n\n            case extended_opcode\n            when LNE::EndSequence\n              registers.end_sequence = true\n              register_to_matrix(sequence, registers)\n              if (@io.tell - @offset - sequence.offset) < sequence.total_length\n                registers = Register.new(sequence.default_is_stmt)\n              else\n                break\n              end\n            when LNE::SetAddress\n              case len\n              when 8 then registers.address = @io.read_bytes(UInt64)\n              when 4 then registers.address = @io.read_bytes(UInt32).to_u64\n              else        @io.skip(len)\n              end\n              registers.op_index = 0_u32\n            when LNE::SetDiscriminator\n              registers.discriminator = DWARF.read_unsigned_leb128(@io)\n            else\n              # skip unsupported opcode\n              @io.read_fully(Bytes.new(len))\n            end\n          else\n            # standard opcode\n            standard_opcode = LNS.new(opcode)\n\n            case standard_opcode\n            when LNS::Copy\n              register_to_matrix(sequence, registers)\n              registers.reset\n            when LNS::AdvancePc\n              operation_advance = DWARF.read_unsigned_leb128(@io)\n              increment_address_and_op_index(operation_advance)\n            when LNS::AdvanceLine\n              registers.line &+= DWARF.read_signed_leb128(@io)\n            when LNS::SetFile\n              registers.file = DWARF.read_unsigned_leb128(@io)\n            when LNS::SetColumn\n              registers.column = DWARF.read_unsigned_leb128(@io)\n            when LNS::NegateStmt\n              registers.is_stmt = !registers.is_stmt\n            when LNS::SetBasicBlock\n              registers.basic_block = true\n            when LNS::ConstAddPc\n              adjusted_opcode = 255 - sequence.opcode_base\n              operation_advance = adjusted_opcode // sequence.line_range\n              increment_address_and_op_index(operation_advance)\n            when LNS::FixedAdvancePc\n              registers.address += @io.read_bytes(UInt16).not_nil!\n              registers.op_index = 0_u32\n            when LNS::SetPrologueEnd\n              registers.prologue_end = true\n            when LNS::SetEpilogueBegin\n              registers.epilogue_begin = true\n            when LNS::SetIsa\n              registers.isa = DWARF.read_unsigned_leb128(@io)\n            else\n              # consume unknown opcode args\n              n_args = sequence.standard_opcode_lengths[opcode.to_i]\n              n_args.times { DWARF.read_unsigned_leb128(@io) }\n            end\n          end\n        end\n      end\n\n      @current_sequence_matrix : Array(Row)?\n\n      private def register_to_matrix(sequence, registers)\n        # checking is_stmt should be enough to avoid \"non statement\" operations\n        # some of which have confusing line number 0.\n        # but some operations within macros seem to be useful and marked as !is_stmt\n        # so attempt to include them also\n        if registers.is_stmt || (registers.line.to_i > 0 && registers.column.to_i > 0)\n          path = sequence.file_names[registers.file].path\n\n          row = Row.new(\n            registers.address + @base_address,\n            path,\n            registers.line.to_i,\n            registers.column.to_i,\n          )\n\n          if rows = @current_sequence_matrix\n            rows << row\n          else\n            matrix << (rows = [row])\n            @current_sequence_matrix = rows\n          end\n        end\n\n        if registers.end_sequence\n          @current_sequence_matrix = nil\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/dwarf/strings.cr",
    "content": "module Crystal\n  module DWARF\n    struct Strings\n      def initialize(@io : IO::FileDescriptor, @offset : UInt32 | UInt64, size)\n        # Read a good chunk of bytes to decode strings faster\n        # (avoid seeking/reading the IO too many times)\n        @buffer = Bytes.new(Math.min(Math.max(16384, size), io.info.size - offset))\n        pos = @io.pos\n        @io.read_fully(@buffer)\n        @io.pos = pos\n      end\n\n      def decode(strp)\n        # See if we can read it from the buffer\n        if strp < @buffer.size\n          index = @buffer.index('\\0'.ord, offset: strp)\n          return String.new(@buffer[strp, index - strp]) if index\n        end\n\n        # If not, try directly from the IO\n        @io.seek(@offset + strp) do\n          @io.gets('\\0', chomp: true).to_s\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/dwarf.cr",
    "content": "require \"./dwarf/abbrev\"\nrequire \"./dwarf/info\"\nrequire \"./dwarf/line_numbers\"\nrequire \"./dwarf/strings\"\n\nmodule Crystal\n  # :nodoc:\n  #\n  # DWARF reader.\n  #\n  # Documentation:\n  # - <http://dwarfstd.org>\n  module DWARF\n    def self.read_unsigned_leb128(io : IO)\n      result = 0_u32\n      shift = 0\n\n      loop do\n        byte = io.read_byte.not_nil!.to_i\n        result |= (byte & 0x7f) << shift\n        break if byte.bit(7) == 0\n        shift += 7\n      end\n\n      result\n    end\n\n    def self.read_signed_leb128(io : IO)\n      result = 0_i32\n      shift = 0\n      size = 32\n      byte = 0_u8\n\n      loop do\n        byte = io.read_byte.not_nil!.to_i\n        result |= (byte & 0x7f) << shift\n        shift += 7\n        break if byte.bit(7) == 0\n      end\n\n      # sign bit of byte is 2nd high order bit (0x40)\n      if (shift < size) && (byte.bit(6) == 1)\n        # sign extend\n        result |= -(1 << shift)\n      end\n\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/elf.cr",
    "content": "module Crystal\n  # :nodoc:\n  #\n  # ELF reader.\n  #\n  # Documentation:\n  # - <http://www.sco.com/developers/gabi/latest/contents.html>\n  struct ELF\n    MAGIC = UInt8.slice(0x7f, 'E'.ord, 'L'.ord, 'F'.ord)\n\n    enum Klass : UInt8\n      ELF32 = 1\n      ELF64 = 2\n    end\n\n    enum OSABI : UInt8\n      SYSTEM_V = 0x00\n      HP_UX    = 0x01\n      NETBSD   = 0x02\n      LINUX    = 0x03\n      SOLARIS  = 0x06\n      AIX      = 0x07\n      IRIX     = 0x08\n      FREEBSD  = 0x09\n      OPENBSD  = 0x0C\n      OPENVMS  = 0x0D\n      NSK_OS   = 0x0E\n      AROS     = 0x0F\n      FENIX_OS = 0x10\n      CLOUDABI = 0x11\n      SORTIX   = 0x53\n    end\n\n    enum Type : UInt16\n      REL  = 1\n      EXEC = 2\n      DYN  = 3\n      CORE = 4\n    end\n\n    enum Machine : UInt16\n      UNKNOWN = 0x00\n      SPARC   = 0x02\n      X86     = 0x03\n      MIPS    = 0x08\n      POWERPC = 0x14\n      ARM     = 0x28\n      SUPERH  = 0x2A\n      IA_64   = 0x32\n      X86_64  = 0x3E\n      AARCH64 = 0xB7\n    end\n\n    enum Endianness\n      Little = 1\n      Big    = 2\n    end\n\n    struct Ident\n      property klass : Klass\n      property data : Endianness\n      property version : UInt8\n      property osabi : OSABI\n      property abiversion : UInt8\n\n      def initialize(@klass, @data, @version, @osabi, @abiversion)\n      end\n    end\n\n    struct SectionHeader\n      enum Type : UInt32\n        NULL          =  0\n        PROGBITS      =  1\n        SYMTAB        =  2\n        STRTAB        =  3\n        RELA          =  4\n        HASH          =  5\n        DYNAMIC       =  6\n        NOTE          =  7\n        NOBITS        =  8\n        REL           =  9\n        SHLIB         = 10\n        DYNSYM        = 11\n        INIT_ARRAY    = 14\n        FINI_ARRAY    = 15\n        PREINIT_ARRAY = 16\n        GROUP         = 17\n        SYMTAB_SHNDX  = 18\n      end\n\n      @[::Flags]\n      enum Flags : UInt64\n        WRITE            =        0x1\n        ALLOC            =        0x2\n        EXECINSTR        =        0x4\n        MERGE            =       0x10\n        STRINGS          =       0x20\n        INFO_LINK        =       0x40\n        LINK_ORDER       =       0x80\n        OS_NONCONFORMING =      0x100\n        GROUP            =      0x200\n        TLS              =      0x400\n        COMPRESSED       =      0x800\n        MASKOS           = 0x0ff00000\n        MASKPROC         = 0xf0000000\n\n        def short\n          String.build do |str|\n            str << 'W' if write?\n            str << 'A' if alloc?\n            str << 'X' if execinstr?\n            str << 'M' if merge?\n            str << 'S' if strings?\n            str << 'T' if tls?\n          end\n        end\n      end\n\n      property! name : UInt32\n      property! type : Type\n      property! flags : Flags\n      property! addr : UInt32 | UInt64\n      property! offset : UInt32 | UInt64\n      property! size : UInt32 | UInt64\n      property! link : UInt32\n      property! info : UInt32\n      property! addralign : UInt32 | UInt64\n      property! entsize : UInt32 | UInt64\n    end\n\n    class Error < Exception\n    end\n\n    getter! ident : Ident\n    property! type : Type\n    property! machine : Machine\n    property! version : UInt32\n    property! entry : UInt32 | UInt64\n    property! phoff : UInt32 | UInt64\n    property! shoff : UInt32 | UInt64\n    property! flags : UInt32\n    property! ehsize : UInt16\n    property! phentsize : UInt16\n    property! phnum : UInt16\n    property! shentsize : UInt16\n    property! shnum : UInt16\n    property! shstrndx : UInt16\n\n    def self.open(path, &)\n      File.open(path, \"r\") do |file|\n        yield new(file)\n      end\n    end\n\n    def initialize(@io : IO::FileDescriptor)\n      read_magic\n      read_ident\n      read_header\n    end\n\n    private def read_magic\n      @io.read(magic = Bytes.new(4))\n\n      if MAGIC.empty?\n        # If constant initialization is not working (for example when an\n        # error occurred during runtime setup or a custom main function doesn't\n        # do the initialization), continuing the ELF reader results in a\n        # seg fault. This condition detects the uninitialized constant\n        # and errors.\n        # A simple program to reproduce is:\n        # ```\n        # module Crystal\n        #   def self.main(&block)\n        #     raise \"foo\"\n        #   end\n        # end\n        # ```\n        Crystal::System.print_error \"Error: %s\\n\", \"Runtime is not initialized\"\n        LibC.exit 1\n      end\n\n      raise Error.new(\"Invalid magic number\") unless magic == MAGIC\n    end\n\n    private def read_ident\n      ei_class = Klass.new(@io.read_byte.not_nil!)\n      ei_data = Endianness.from_value(@io.read_byte.not_nil!)\n\n      ei_version = @io.read_byte.not_nil!\n      raise Error.new(\"Unsupported version number\") unless ei_version == 1\n\n      ei_osabi = OSABI.from_value(@io.read_byte.not_nil!)\n      ei_abiversion = @io.read_byte.not_nil!\n\n      # padding (unused)\n      @io.skip(7)\n\n      @ident = Ident.new(ei_class, ei_data, ei_version, ei_osabi, ei_abiversion)\n    end\n\n    # Parses and returns an Array of `SectionHeader`.\n    def section_headers\n      @sections ||= Array(SectionHeader).new(shnum.to_i) do |i|\n        @io.seek(shoff + i * shentsize)\n\n        sh = SectionHeader.new\n        sh.name = read_word\n        sh.type = SectionHeader::Type.new(read_word)\n        sh.flags = SectionHeader::Flags.new(read_ulong.to_u64)\n        sh.addr = read_ulong\n        sh.offset = read_ulong\n        sh.size = read_ulong\n        sh.link = read_word\n        sh.info = read_word\n        sh.addralign = read_ulong\n        sh.entsize = read_ulong\n        sh\n      end\n    end\n\n    # Returns the name of a section, using the `SectionHeader#name` index.\n    def sh_name(index)\n      sh = section_headers[shstrndx]\n      @io.seek(sh.offset + index) do\n        @io.gets('\\0', chomp: true).to_s\n      end\n    end\n\n    # Searches for a section then yield the `SectionHeader` and the IO object\n    # ready for parsing if the section was found. Returns the value returned by\n    # the block or nil if the section wasn't found.\n    def read_section?(name : String, &)\n      if sh = section_headers.find { |sh| sh_name(sh.name) == name }\n        @io.seek(sh.offset) do\n          yield sh, @io\n        end\n      end\n    end\n\n    private def endianness\n      ident.data == Endianness::Little ? IO::ByteFormat::LittleEndian : IO::ByteFormat::BigEndian\n    end\n\n    private def read_word\n      @io.read_bytes(UInt32, endianness)\n    end\n\n    private def read_ulong\n      case ident.klass\n      when Klass::ELF32 then @io.read_bytes(UInt32, endianness)\n      when Klass::ELF64 then @io.read_bytes(UInt64, endianness)\n      else                   raise Error.new(\"Unsupported\")\n      end\n    end\n\n    private def read_header\n      @type = Type.new(@io.read_bytes(UInt16, endianness).not_nil!)\n      @machine = Machine.new(@io.read_bytes(UInt16, endianness).not_nil!)\n\n      @version = @io.read_bytes(UInt32, endianness).not_nil!\n      raise Error.new(\"Unsupported version number\") unless version == 1\n\n      @entry = read_ulong\n      @phoff = read_ulong\n      @shoff = read_ulong\n\n      @flags = @io.read_bytes(UInt32, endianness)\n\n      @ehsize = @io.read_bytes(UInt16, endianness)\n      case ident.klass\n      when Klass::ELF32\n        raise Error.new(\"Header should be 52 bytes for ELF32\") unless ehsize == 52\n      when Klass::ELF64\n        raise Error.new(\"Header should be 64 bytes for ELF64\") unless ehsize == 64\n      end\n\n      @phentsize = @io.read_bytes(UInt16, endianness)\n      @phnum = @io.read_bytes(UInt16, endianness)\n\n      @shentsize = @io.read_bytes(UInt16, endianness)\n      @shnum = @io.read_bytes(UInt16, endianness)\n      @shstrndx = @io.read_bytes(UInt16, endianness)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/epoll.cr",
    "content": "require \"./polling\"\nrequire \"../system/unix/epoll\"\nrequire \"../system/unix/eventfd\"\nrequire \"../system/unix/timerfd\"\n\nclass Crystal::EventLoop::Epoll < Crystal::EventLoop::Polling\n  def initialize(parallelism : Int32)\n    # the epoll instance\n    @epoll = System::Epoll.new\n\n    # notification to interrupt a run\n    @interrupted = Atomic(Bool).new(false)\n    @eventfd = System::EventFD.new\n    @epoll.add(@eventfd.fd, LibC::EPOLLIN, u64: @eventfd.fd.to_u64!)\n\n    # we use timerfd to go below the millisecond precision of epoll_wait; it\n    # also allows to avoid locking timers before every epoll_wait call\n    @timerfd = System::TimerFD.new\n    @epoll.add(@timerfd.fd, LibC::EPOLLIN, u64: @timerfd.fd.to_u64!)\n  end\n\n  {% unless flag?(:preview_mt) %}\n    def after_fork : Nil\n      super\n\n      # close inherited fds\n      @epoll.close\n      @eventfd.close\n      @timerfd.close\n\n      # create new fds\n      @epoll = System::Epoll.new\n\n      @interrupted.set(false, :relaxed)\n      @eventfd = System::EventFD.new\n      @epoll.add(@eventfd.fd, LibC::EPOLLIN, u64: @eventfd.fd.to_u64!)\n\n      @timerfd = System::TimerFD.new\n      @epoll.add(@timerfd.fd, LibC::EPOLLIN, u64: @timerfd.fd.to_u64!)\n      system_set_timer(@timers.next_ready?)\n\n      # re-add all registered fds\n      Polling.arena.each_index { |fd, index| system_add(fd, index) }\n    end\n  {% end %}\n\n  private def system_run(blocking : Bool, & : Fiber ->) : Nil\n    Crystal.trace :evloop, \"run\", blocking: blocking\n\n    # wait for events (indefinitely when blocking)\n    buffer = uninitialized LibC::EpollEvent[128]\n    epoll_events = @epoll.wait(buffer.to_slice, timeout: blocking ? -1 : 0)\n\n    timer_triggered = false\n\n    # process events\n    epoll_events.size.times do |i|\n      epoll_event = epoll_events.to_unsafe + i\n\n      case epoll_event.value.data.u64\n      when @eventfd.fd\n        # TODO: panic if epoll_event.value.events != LibC::EPOLLIN (could be EPOLLERR or EPLLHUP)\n        Crystal.trace :evloop, \"interrupted\"\n        @eventfd.read\n        @interrupted.set(false, :relaxed)\n      when @timerfd.fd\n        # TODO: panic if epoll_event.value.events != LibC::EPOLLIN (could be EPOLLERR or EPLLHUP)\n        Crystal.trace :evloop, \"timer\"\n        timer_triggered = true\n      else\n        process_io(epoll_event) { |fiber| yield fiber }\n      end\n    end\n\n    # OPTIMIZE: only process timers when timer_triggered (?)\n    process_timers(timer_triggered) { |fiber| yield fiber }\n  end\n\n  private def process_io(epoll_event : LibC::EpollEvent*, &) : Nil\n    index = Polling::Arena::Index.new(epoll_event.value.data.u64)\n    events = epoll_event.value.events\n\n    Crystal.trace :evloop, \"event\", fd: index.index, index: index.to_i64, events: events\n\n    Polling.arena.get?(index) do |pd|\n      if (events & (LibC::EPOLLERR | LibC::EPOLLHUP)) != 0\n        pd.value.@readers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        pd.value.@writers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        return\n      end\n\n      if (events & LibC::EPOLLRDHUP) == LibC::EPOLLRDHUP\n        pd.value.@readers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n      elsif (events & LibC::EPOLLIN) == LibC::EPOLLIN\n        pd.value.@readers.ready_one { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n      end\n\n      if (events & LibC::EPOLLOUT) == LibC::EPOLLOUT\n        pd.value.@writers.ready_one { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n      end\n    end\n  end\n\n  def interrupt : Nil\n    # the atomic makes sure we only write once\n    @eventfd.write(1) unless @interrupted.swap(true, :relaxed)\n  end\n\n  protected def system_add(fd : Int32, index : Polling::Arena::Index) : Nil\n    Crystal.trace :evloop, \"epoll_ctl\", op: \"add\", fd: fd, index: index.to_i64\n    events = LibC::EPOLLIN | LibC::EPOLLOUT | LibC::EPOLLRDHUP | LibC::EPOLLET\n    @epoll.add(fd, events, u64: index.to_u64)\n  end\n\n  protected def system_del(fd : Int32, closing = true) : Nil\n    Crystal.trace :evloop, \"epoll_ctl\", op: \"del\", fd: fd\n    @epoll.delete(fd)\n  end\n\n  protected def system_del(fd : Int32, closing = true, &) : Nil\n    Crystal.trace :evloop, \"epoll_ctl\", op: \"del\", fd: fd\n    @epoll.delete(fd) { yield }\n  end\n\n  private def system_set_timer(time : Time::Instant?) : Nil\n    if time\n      @timerfd.set(time)\n    else\n      @timerfd.cancel\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/file_descriptor.cr",
    "content": "abstract class Crystal::EventLoop\n  module FileDescriptor\n    # Opens an unidirectional pipe.\n    #\n    # The implementation shall respect the specified blocking arguments for each\n    # end of the pipe, and follow its internal blocking requirements when a\n    # blocking arg is nil.\n    #\n    # Returns a tuple with the reader and writer IO objects.\n    abstract def pipe(read_blocking : Bool?, write_blocking : Bool?) : {IO::FileDescriptor, IO::FileDescriptor}\n\n    # Opens a file at *path*.\n    #\n    # Blocks the current fiber until the file has been opened. Avoids blocking\n    # the current thread if possible, especially when *blocking* is `false` or\n    # `nil`.\n    #\n    # Returns the system file descriptor or handle, or a system error.\n    abstract def open(path : String, flags : Int32, permissions : File::Permissions, blocking : Bool?) : {System::FileDescriptor::Handle, Bool} | Errno | WinError\n\n    # Reads at least one byte from the file descriptor into *slice*.\n    #\n    # Blocks the current fiber if no data is available for reading, continuing\n    # when available. Otherwise returns immediately.\n    #\n    # Returns the number of bytes read (up to `slice.size`).\n    # Returns 0 when EOF is reached.\n    abstract def read(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n\n    # Blocks the current fiber until the file descriptor is ready for read.\n    abstract def wait_readable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n\n    # Writes at least one byte from *slice* to the file descriptor.\n    #\n    # Blocks the current fiber if the file descriptor isn't ready for writing,\n    # continuing when ready. Otherwise returns immediately.\n    #\n    # Returns the number of bytes written (up to `slice.size`).\n    abstract def write(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n\n    # Blocks the current fiber until the file descriptor is ready for write.\n    abstract def wait_writable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n\n    # Hook to react on the file descriptor after it has been reopened. For\n    # example we might want to resume all pending operations to act on the new\n    # file descriptor.\n    abstract def reopened(file_descriptor : Crystal::System::FileDescriptor) : Nil\n\n    # Internal shutdown of the file descriptor. Called after the\n    # `IO::FileDescriptor` has been marked closed but before calling `#close` to\n    # actually close the system fd or handle.\n    #\n    # Implementations shall resume all pending waiters and let them fail because\n    # the IO has been closed.\n    abstract def shutdown(file_descriptor : Crystal::System::FileDescriptor) : Nil\n\n    # Closes the system fd or handle.\n    abstract def close(file_descriptor : Crystal::System::FileDescriptor) : Nil\n  end\n\n  # Removes the file descriptor from the event loop. Can be used to free up\n  # memory resources associated with the file descriptor, as well as removing\n  # the file descriptor from kernel data structures.\n  #\n  # Called by `::IO::FileDescriptor#finalize` before closing the file\n  # descriptor. Errors shall be silently ignored.\n  def self.remove(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    backend_class.remove_impl(file_descriptor)\n  end\n\n  # Actual implementation for `.remove`. Must be implemented on a subclass of\n  # `Crystal::EventLoop` when needed.\n  protected def self.remove_impl(file_descriptor : Crystal::System::FileDescriptor) : Nil\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/iocp/fiber_event.cr",
    "content": "class Crystal::EventLoop::IOCP::FiberEvent\n  include Crystal::EventLoop::Event\n\n  delegate type, wake_at, wake_at?, fiber, timed_out?, to: @timer\n\n  def initialize(type : Timer::Type, fiber : Fiber)\n    @timer = Timer.new(type, fiber)\n  end\n\n  # io timeout, sleep, or select timeout\n  def add(timeout : Time::Span) : Nil\n    now = Crystal::System::Time.instant\n    @timer.wake_at = now + timeout\n    EventLoop.current.add_timer(pointerof(@timer))\n  end\n\n  # select timeout has been cancelled\n  def delete : Nil\n    return unless @timer.wake_at?\n    EventLoop.current.delete_timer(pointerof(@timer))\n    clear\n  end\n\n  # fiber died\n  def free : Nil\n    delete\n  end\n\n  # the timer triggered (already dequeued from eventloop)\n  def clear : Nil\n    @timer.wake_at = nil\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/iocp/timer.cr",
    "content": "# NOTE: this struct is only needed to be able to re-use `PointerPairingHeap`\n# because EventLoop::Polling uses pointers. If `EventLoop::Polling::Event` was a\n# reference, then `PairingHeap` wouldn't need pointers, and this struct could be\n# merged into `Event`.\nstruct Crystal::EventLoop::IOCP::Timer\n  enum Type\n    Sleep\n    Timeout\n    SelectTimeout\n  end\n\n  getter type : Type\n\n  # The `Fiber` that is waiting on the event and that the `EventLoop` shall\n  # resume.\n  getter fiber : Fiber\n\n  # The absolute time, against the monotonic clock, at which a timed event shall\n  # trigger. Nil for IO events without a timeout.\n  getter! wake_at : Time::Instant\n\n  # True if an IO event has timed out (i.e. we're past `#wake_at`).\n  getter? timed_out : Bool = false\n\n  # The event can be added to the `Timers` list.\n  include PointerPairingHeap::Node\n\n  def initialize(@type : Type, @fiber, timeout : Time::Span? = nil)\n    if timeout\n      now = Crystal::System::Time.instant\n      @wake_at = now + timeout\n    end\n  end\n\n  def wake_at=(@wake_at)\n  end\n\n  def timed_out! : Bool\n    @timed_out = true\n  end\n\n  def heap_compare(other : Pointer(self)) : Bool\n    wake_at < other.value.wake_at\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/iocp.cr",
    "content": "# forward declaration for the require below to not create a module\nclass Crystal::EventLoop::IOCP < Crystal::EventLoop\nend\n\nrequire \"c/ntdll\"\nrequire \"../system/win32/iocp\"\nrequire \"../system/win32/waitable_timer\"\nrequire \"./timers\"\nrequire \"./lock\"\nrequire \"./iocp/*\"\n\n# :nodoc:\nclass Crystal::EventLoop::IOCP < Crystal::EventLoop\n  def self.default_file_blocking?\n    # here, blocking refers to setting FILE_FLAG_OVERLAPPED (non blocking) or\n    # not (blocking)\n    false\n  end\n\n  def self.default_socket_blocking?\n    # here, blocking refers to the (non)blocking mode of winsocks, it is\n    # independent from the WSA_FLAG_OVERLAPPED that we always set\n    true\n  end\n\n  @waitable_timer : System::WaitableTimer?\n  @timer_packet = LibC::HANDLE.null\n  @timer_key : System::IOCP::CompletionKey?\n\n  def initialize(parallelism : Int32)\n    @timers_mutex = Thread::Mutex.new\n    @timers = Timers(Timer).new\n\n    # the completion port\n    @iocp = System::IOCP.new\n\n    # custom completion to interrupt a blocking run\n    @interrupted = Atomic(Bool).new(false)\n    @interrupt_key = System::IOCP::CompletionKey.new(:interrupt)\n\n    # On Windows 10+ we leverage a high resolution timer with completion packet\n    # to notify a completion port; on legacy Windows we fallback to the low\n    # resolution timeout (~15.6ms)\n    if System::IOCP.wait_completion_packet_methods?\n      @waitable_timer = System::WaitableTimer.new\n      @timer_packet = @iocp.create_wait_completion_packet\n      @timer_key = System::IOCP::CompletionKey.new(:timer)\n    end\n  end\n\n  # Returns the base IO Completion Port.\n  def iocp_handle : LibC::HANDLE\n    @iocp.handle\n  end\n\n  def create_completion_port(handle : LibC::HANDLE) : LibC::HANDLE\n    iocp = LibC.CreateIoCompletionPort(handle, @iocp.handle, nil, 0)\n    raise IO::Error.from_winerror(\"CreateIoCompletionPort\") if iocp.null?\n\n    # all overlapped operations may finish synchronously, in which case we do\n    # not reschedule the running fiber; the following call tells Win32 not to\n    # queue an I/O completion packet to the associated IOCP as well, as this\n    # would be done by default\n    if LibC.SetFileCompletionNotificationModes(handle, LibC::FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) == 0\n      raise IO::Error.from_winerror(\"SetFileCompletionNotificationModes\")\n    end\n\n    iocp\n  end\n\n  # thread unsafe\n  def run(blocking : Bool) : Bool\n    enqueued = false\n\n    run_impl(blocking) do |fiber|\n      fiber.enqueue\n      enqueued = true\n    end\n\n    enqueued\n  end\n\n  {% if flag?(:execution_context) %}\n    # thread unsafe\n    def run(queue : Fiber::List*, blocking : Bool) : Nil\n      run_impl(blocking) { |fiber| queue.value.push(fiber) }\n    end\n\n    # the evloop has a single IOCP instance for the context and only one\n    # scheduler must wait on the evloop at any time\n    include EventLoop::Lock\n  {% end %}\n\n  # Runs the event loop and enqueues the fiber for the next upcoming event or\n  # completion.\n  private def run_impl(blocking : Bool, &) : Nil\n    Crystal.trace :evloop, \"run\", blocking: blocking ? 1 : 0\n\n    if @waitable_timer\n      timeout = blocking ? LibC::INFINITE : 0_i64\n    elsif blocking\n      if time = @timers_mutex.synchronize { @timers.next_ready? }\n        # convert absolute time to relative time, expressed in milliseconds,\n        # rounded up; cannot use `::Time.instant` that could be mocked\n        relative = time.duration_since(Crystal::System::Time.instant)\n        timeout = (relative.to_i * 1000 + (relative.nanoseconds + 999_999) // 1_000_000)\n      else\n        timeout = LibC::INFINITE\n      end\n    else\n      timeout = 0_i64\n    end\n\n    # the array must be at least as large as `overlapped_entries` in\n    # `System::IOCP#wait_queued_completions`\n    events = uninitialized FiberEvent[64]\n    size = 0\n\n    @iocp.wait_queued_completions(timeout) do |fiber|\n      if (event = fiber.@resume_event) && event.wake_at?\n        events[size] = event\n        size += 1\n      end\n      yield fiber\n    end\n\n    @timers_mutex.synchronize do\n      # cancel the timeout of completed operations\n      events.to_slice[0...size].each do |event|\n        @timers.delete(pointerof(event.@timer))\n        event.clear\n      end\n\n      # run expired timers\n      @timers.dequeue_ready do |timer|\n        process_timer(timer) { |fiber| yield fiber }\n      end\n\n      # update timer\n      rearm_waitable_timer(@timers.next_ready?, interruptible: false)\n    end\n\n    @interrupted.set(false, :release)\n  end\n\n  private def process_timer(timer : Pointer(Timer), &)\n    fiber = timer.value.fiber\n\n    case timer.value.type\n    in .sleep?, .timeout?\n      timer.value.timed_out!\n    in .select_timeout?\n      return unless select_action = fiber.timeout_select_action\n      fiber.timeout_select_action = nil\n      return unless select_action.time_expired?\n      fiber.@timeout_event.as(FiberEvent).clear\n    end\n\n    yield fiber\n  end\n\n  def interrupt : Nil\n    unless @interrupted.get(:acquire)\n      @iocp.post_queued_completion_status(@interrupt_key)\n    end\n  end\n\n  protected def add_timer(timer : Pointer(Timer)) : Nil\n    @timers_mutex.synchronize do\n      is_next_ready = @timers.add(timer)\n      rearm_waitable_timer(timer.value.wake_at, interruptible: true) if is_next_ready\n    end\n  end\n\n  protected def delete_timer(timer : Pointer(Timer)) : Nil\n    @timers_mutex.synchronize do\n      _, was_next_ready = @timers.delete(timer)\n      rearm_waitable_timer(@timers.next_ready?, interruptible: false) if was_next_ready\n    end\n  end\n\n  protected def rearm_waitable_timer(time : Time::Instant?, interruptible : Bool) : Nil\n    if waitable_timer = @waitable_timer\n      raise \"BUG: @timer_packet was not initialized!\" unless @timer_packet\n      status = @iocp.cancel_wait_completion_packet(@timer_packet, true)\n      if time\n        waitable_timer.set(time)\n        if status == LibC::STATUS_PENDING\n          interrupt\n        else\n          # STATUS_CANCELLED, STATUS_SUCCESS\n          @iocp.associate_wait_completion_packet(@timer_packet, waitable_timer.handle, @timer_key.not_nil!)\n        end\n      else\n        waitable_timer.cancel\n      end\n    elsif interruptible\n      interrupt\n    end\n  end\n\n  def sleep(duration : Time::Span) : Nil\n    timer = Timer.new(:sleep, Fiber.current, duration)\n    add_timer(pointerof(timer))\n    Fiber.suspend\n\n    # safety check\n    return if timer.timed_out?\n\n    # try to avoid a double resume if possible, but another thread might be\n    # running the evloop and dequeue the event in parallel, so a \"can't resume\n    # dead fiber\" can still happen in a MT execution context.\n    delete_timer(pointerof(timer))\n    raise \"BUG: #{timer.fiber} called sleep but was manually resumed before the timer expired!\"\n  end\n\n  # Suspend the current fiber for *duration* and returns true if the timer\n  # expired and false if the fiber was resumed early.\n  #\n  # Specific to IOCP to handle IO timeouts.\n  def timeout(duration : Time::Span) : Bool\n    event = Fiber.current.resume_event\n    event.add(duration)\n\n    Fiber.suspend\n\n    if event.timed_out?\n      true\n    else\n      event.delete\n      false\n    end\n  end\n\n  def create_resume_event(fiber : Fiber) : EventLoop::Event\n    FiberEvent.new(:timeout, fiber)\n  end\n\n  def create_timeout_event(fiber : Fiber) : EventLoop::Event\n    FiberEvent.new(:select_timeout, fiber)\n  end\n\n  def pipe(read_blocking : Bool?, write_blocking : Bool?) : {IO::FileDescriptor, IO::FileDescriptor}\n    r, w = System::FileDescriptor.system_pipe(!!read_blocking, !!write_blocking)\n    create_completion_port(LibC::HANDLE.new(r)) unless read_blocking\n    create_completion_port(LibC::HANDLE.new(w)) unless write_blocking\n    {\n      IO::FileDescriptor.new(handle: r, blocking: !!read_blocking),\n      IO::FileDescriptor.new(handle: w, blocking: !!write_blocking),\n    }\n  end\n\n  def open(path : String, flags : Int32, permissions : File::Permissions, blocking : Bool?) : {System::FileDescriptor::Handle, Bool} | WinError\n    access, disposition, attributes = System::File.posix_to_open_opts(flags, permissions, !!blocking)\n\n    handle = LibC.CreateFileW(\n      System.to_wstr(path),\n      access,\n      LibC::DEFAULT_SHARE_MODE, # UNIX semantics\n      nil,\n      disposition,\n      attributes,\n      LibC::HANDLE.null\n    )\n\n    if handle == LibC::INVALID_HANDLE_VALUE\n      WinError.value\n    else\n      create_completion_port(handle) unless blocking\n      {handle.address, !!blocking}\n    end\n  end\n\n  def read(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n    System::IOCP.overlapped_operation(file_descriptor, \"ReadFile\", file_descriptor.read_timeout) do |overlapped|\n      ret = LibC.ReadFile(file_descriptor.windows_handle, slice, slice.size, out byte_count, overlapped)\n      {ret, byte_count}\n    end.to_i32\n  end\n\n  def wait_readable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    raise NotImplementedError.new(\"Crystal::System::IOCP#wait_readable(FileDescriptor)\")\n  end\n\n  def write(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n    bytes_written = System::IOCP.overlapped_operation(file_descriptor, \"WriteFile\", file_descriptor.write_timeout, writing: true) do |overlapped|\n      overlapped.offset = UInt64::MAX if file_descriptor.system_append?\n\n      ret = LibC.WriteFile(file_descriptor.windows_handle, slice, slice.size, out byte_count, overlapped)\n      {ret, byte_count}\n    end.to_i32\n\n    # The overlapped offset forced a write to the end of the file, but unlike\n    # synchronous writes, an asynchronous write incorrectly updates the file\n    # pointer: it merely adds the number of written bytes to the current\n    # position, disregarding that the offset might have changed it.\n    #\n    # We could seek before the async write (it works), but a concurrent fiber or\n    # parallel thread could also seek and we'd end up overwriting instead of\n    # appending; we need both the offset + explicit seek.\n    file_descriptor.system_seek(0, IO::Seek::End) if file_descriptor.system_append?\n\n    bytes_written\n  end\n\n  def wait_writable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    raise NotImplementedError.new(\"Crystal::System::IOCP#wait_writable(FileDescriptor)\")\n  end\n\n  def reopened(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    raise NotImplementedError.new(\"Crystal::System::IOCP#reopened(FileDescriptor)\")\n  end\n\n  def shutdown(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    LibC.CancelIoEx(file_descriptor.windows_handle, nil)\n  end\n\n  def close(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.file_descriptor_close\n  end\n\n  def socket(family : ::Socket::Family, type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool?) : {::Socket::Handle, Bool}\n    blocking = true if blocking.nil?\n    fd = System::Socket.socket(family, type, protocol, blocking)\n    create_completion_port LibC::HANDLE.new(fd)\n    {fd, blocking}\n  end\n\n  def socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol) : Tuple({::Socket::Handle, ::Socket::Handle}, Bool)\n    raise NotImplementedError.new(\"Crystal::EventLoop::IOCP#socketpair\")\n  end\n\n  private def wsa_buffer(bytes)\n    wsabuf = LibC::WSABUF.new\n    wsabuf.len = bytes.size\n    wsabuf.buf = bytes.to_unsafe\n    wsabuf\n  end\n\n  def read(socket : ::Socket, slice : Bytes) : Int32\n    wsabuf = wsa_buffer(slice)\n\n    bytes_read = System::IOCP.wsa_overlapped_operation(socket, socket.fd, \"WSARecv\", socket.read_timeout, connreset_is_error: false) do |overlapped|\n      flags = 0_u32\n      ret = LibC.WSARecv(socket.fd, pointerof(wsabuf), 1, out bytes_received, pointerof(flags), overlapped, nil)\n      {ret, bytes_received}\n    end\n\n    bytes_read.to_i32\n  end\n\n  def wait_readable(socket : ::Socket) : Nil\n    # NOTE: Windows 10+ has `ProcessSocketNotifications` to associate sockets to\n    # a completion port and be notified of socket readiness. See\n    # <https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-socket-state-notifications>\n    raise NotImplementedError.new(\"Crystal::System::IOCP#wait_readable(Socket)\")\n  end\n\n  def write(socket : ::Socket, slice : Bytes) : Int32\n    wsabuf = wsa_buffer(slice)\n\n    bytes = System::IOCP.wsa_overlapped_operation(socket, socket.fd, \"WSASend\", socket.write_timeout) do |overlapped|\n      ret = LibC.WSASend(socket.fd, pointerof(wsabuf), 1, out bytes_sent, 0, overlapped, nil)\n      {ret, bytes_sent}\n    end\n\n    bytes.to_i32\n  end\n\n  def wait_writable(socket : ::Socket) : Nil\n    # NOTE: Windows 10+ has `ProcessSocketNotifications` to associate sockets to\n    # a completion port and be notified of socket readiness. See\n    # <https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-socket-state-notifications>\n    raise NotImplementedError.new(\"Crystal::System::IOCP#wait_writable(Socket)\")\n  end\n\n  def send_to(socket : ::Socket, slice : Bytes, address : ::Socket::Address) : Int32\n    wsabuf = wsa_buffer(slice)\n    bytes_written = System::IOCP.wsa_overlapped_operation(socket, socket.fd, \"WSASendTo\", socket.write_timeout) do |overlapped|\n      ret = LibC.WSASendTo(socket.fd, pointerof(wsabuf), 1, out bytes_sent, 0, address, address.size, overlapped, nil)\n      {ret, bytes_sent}\n    end\n    raise ::Socket::Error.from_wsa_error(\"Error sending datagram to #{address}\") if bytes_written == -1\n\n    # to_i32 is fine because string/slice sizes are an Int32\n    bytes_written.to_i32\n  end\n\n  def receive(socket : ::Socket, slice : Bytes) : Int32\n    receive_from(socket, slice)[0]\n  end\n\n  def receive_from(socket : ::Socket, slice : Bytes) : Tuple(Int32, ::Socket::Address)\n    sockaddr = Pointer(LibC::SOCKADDR_STORAGE).malloc.as(LibC::Sockaddr*)\n    # initialize sockaddr with the initialized family of the socket\n    copy = sockaddr.value\n    copy.sa_family = socket.family\n    sockaddr.value = copy\n\n    addrlen = sizeof(LibC::SOCKADDR_STORAGE)\n\n    wsabuf = wsa_buffer(slice)\n\n    flags = 0_u32\n    bytes_read = System::IOCP.wsa_overlapped_operation(socket, socket.fd, \"WSARecvFrom\", socket.read_timeout) do |overlapped|\n      ret = LibC.WSARecvFrom(socket.fd, pointerof(wsabuf), 1, out bytes_received, pointerof(flags), sockaddr, pointerof(addrlen), overlapped, nil)\n      {ret, bytes_received}\n    end\n\n    {bytes_read.to_i32, ::Socket::Address.from(sockaddr, addrlen)}\n  end\n\n  def connect(socket : ::Socket, address : ::Socket::Addrinfo | ::Socket::Address, timeout : ::Time::Span?) : IO::Error?\n    socket.overlapped_connect(socket.fd, \"ConnectEx\", timeout) do |overlapped|\n      # This is: LibC.ConnectEx(fd, address, address.size, nil, 0, nil, overlapped)\n      Crystal::System::Socket.connect_ex.call(socket.fd, address.to_unsafe, address.size, Pointer(Void).null, 0_u32, Pointer(UInt32).null, overlapped.to_unsafe)\n    end\n  end\n\n  def accept(socket : ::Socket) : {::Socket::Handle, Bool}?\n    socket.system_accept do |client_handle|\n      address_size = sizeof(LibC::SOCKADDR_STORAGE) + 16\n\n      # buffer_size is set to zero to only accept the connection and don't receive any data.\n      # That will be a different operation.\n      #\n      # > If dwReceiveDataLength is zero, accepting the connection will not result in a receive operation.\n      # > Instead, AcceptEx completes as soon as a connection arrives, without waiting for any data.\n      #\n      # TODO: Investigate benefits from receiving data here directly. It's hard to integrate into the event loop and socket API.\n      buffer_size = 0\n      output_buffer = Bytes.new(address_size * 2 + buffer_size)\n\n      success = socket.overlapped_accept(socket.fd, \"AcceptEx\") do |overlapped|\n        # This is: LibC.AcceptEx(fd, client_handle, output_buffer, buffer_size, address_size, address_size, out received_bytes, overlapped)\n        received_bytes = uninitialized UInt32\n        Crystal::System::Socket.accept_ex.call(socket.fd, client_handle,\n          output_buffer.to_unsafe.as(Void*), buffer_size.to_u32!,\n          address_size.to_u32!, address_size.to_u32!, pointerof(received_bytes), overlapped.to_unsafe)\n      end\n\n      if success\n        # AcceptEx does not automatically set the socket options on the accepted\n        # socket to match those of the listening socket, we need to ask for that\n        # explicitly with SO_UPDATE_ACCEPT_CONTEXT\n        System::Socket.setsockopt client_handle, LibC::SO_UPDATE_ACCEPT_CONTEXT, socket.fd\n\n        true\n      else\n        false\n      end\n    end\n  end\n\n  def shutdown(socket : ::Socket) : Nil\n    LibC.shutdown(socket.fd, LibC::SH_BOTH)\n    LibC.CancelIoEx(Pointer(Void).new(socket.fd), nil)\n  end\n\n  def close(socket : ::Socket) : Nil\n    socket.socket_close\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/kqueue.cr",
    "content": "require \"./polling\"\nrequire \"../system/unix/kqueue\"\n\nclass Crystal::EventLoop::Kqueue < Crystal::EventLoop::Polling\n  # the following are arbitrary numbers to identify specific events\n  INTERRUPT_IDENTIFIER =  9\n  TIMER_IDENTIFIER     = 10\n\n  {% unless LibC.has_constant?(:EVFILT_USER) %}\n    @pipe = uninitialized LibC::Int[2]\n  {% end %}\n\n  def initialize(parallelism : Int32)\n    # the kqueue instance\n    @kqueue = System::Kqueue.new\n\n    # notification to interrupt a run\n    @interrupted = Atomic(Bool).new(false)\n\n    {% if LibC.has_constant?(:EVFILT_USER) %}\n      @kqueue.kevent(\n        INTERRUPT_IDENTIFIER,\n        LibC::EVFILT_USER,\n        LibC::EV_ADD | LibC::EV_ENABLE | LibC::EV_CLEAR)\n    {% else %}\n      @pipe = System::FileDescriptor.system_pipe\n      @kqueue.kevent(@pipe[0], LibC::EVFILT_READ, LibC::EV_ADD)\n    {% end %}\n  end\n\n  {% unless flag?(:preview_mt) %}\n    def after_fork : Nil\n      super\n\n      # kqueue isn't inherited by fork on darwin/dragonfly, but we still close\n      @kqueue.close\n      @kqueue = System::Kqueue.new\n\n      @interrupted.set(false, :relaxed)\n\n      {% if LibC.has_constant?(:EVFILT_USER) %}\n        @kqueue.kevent(\n          INTERRUPT_IDENTIFIER,\n          LibC::EVFILT_USER,\n          LibC::EV_ADD | LibC::EV_ENABLE | LibC::EV_CLEAR)\n      {% else %}\n        @pipe.each { |fd| LibC.close(fd) }\n        @pipe = System::FileDescriptor.system_pipe\n        @kqueue.kevent(@pipe[0], LibC::EVFILT_READ, LibC::EV_ADD)\n      {% end %}\n\n      system_set_timer(@timers.next_ready?)\n\n      # re-add all registered fds\n      Polling.arena.each_index { |fd, index| system_add(fd, index) }\n    end\n  {% end %}\n\n  private def system_run(blocking : Bool, & : Fiber ->) : Nil\n    buffer = uninitialized LibC::Kevent[128]\n\n    Crystal.trace :evloop, \"run\", blocking: blocking\n    timeout = blocking ? nil : Time::Span.zero\n    kevents = @kqueue.wait(buffer.to_slice, timeout)\n\n    timer_triggered = false\n\n    # process events\n    kevents.size.times do |i|\n      kevent = kevents.to_unsafe + i\n\n      if process_interrupt?(kevent)\n        # nothing special\n      elsif kevent.value.filter == LibC::EVFILT_TIMER\n        # nothing special\n        timer_triggered = true\n      else\n        process_io(kevent) { |fiber| yield fiber }\n      end\n    end\n\n    # OPTIMIZE: only process timers when timer_triggered (?)\n    process_timers(timer_triggered) { |fiber| yield fiber }\n  end\n\n  private def process_interrupt?(kevent)\n    {% if LibC.has_constant?(:EVFILT_USER) %}\n      if kevent.value.filter == LibC::EVFILT_USER\n        @interrupted.set(false, :relaxed) if kevent.value.ident == INTERRUPT_IDENTIFIER\n        return true\n      end\n    {% else %}\n      if kevent.value.filter == LibC::EVFILT_READ && kevent.value.ident == @pipe[0]\n        ident = 0\n        ret = LibC.read(@pipe[0], pointerof(ident), sizeof(Int32))\n        raise RuntimeError.from_errno(\"read\") if ret == -1\n        @interrupted.set(false, :relaxed) if ident == INTERRUPT_IDENTIFIER\n        return true\n      end\n    {% end %}\n    false\n  end\n\n  private def process_io(kevent : LibC::Kevent*, &) : Nil\n    index =\n      {% if flag?(:bits64) %}\n        Polling::Arena::Index.new(kevent.value.udata.address)\n      {% else %}\n        # assuming 32-bit target: rebuild the arena index\n        Polling::Arena::Index.new(kevent.value.ident.to_i32!, kevent.value.udata.address.to_u32!)\n      {% end %}\n\n    Crystal.trace :evloop, \"event\", fd: kevent.value.ident, index: index.to_i64,\n      filter: kevent.value.filter, flags: kevent.value.flags, fflags: kevent.value.fflags\n\n    Polling.arena.get?(index) do |pd|\n      if (kevent.value.fflags & LibC::EV_EOF) == LibC::EV_EOF\n        # apparently some systems may report EOF on write with EVFILT_READ instead\n        # of EVFILT_WRITE, so let's wake all waiters:\n        pd.value.@readers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        pd.value.@writers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        return\n      end\n\n      case kevent.value.filter\n      when LibC::EVFILT_READ\n        if (kevent.value.fflags & LibC::EV_ERROR) == LibC::EV_ERROR\n          # OPTIMIZE: pass errno (kevent.data) through PollDescriptor\n          pd.value.@readers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        else\n          pd.value.@readers.ready_one { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        end\n      when LibC::EVFILT_WRITE\n        if (kevent.value.fflags & LibC::EV_ERROR) == LibC::EV_ERROR\n          # OPTIMIZE: pass errno (kevent.data) through PollDescriptor\n          pd.value.@writers.ready_all { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        else\n          pd.value.@writers.ready_one { |event| unsafe_resume_io(event) { |fiber| yield fiber } }\n        end\n      end\n    end\n  end\n\n  def interrupt : Nil\n    return if @interrupted.swap(true, :relaxed)\n\n    {% if LibC.has_constant?(:EVFILT_USER) %}\n      @kqueue.kevent(INTERRUPT_IDENTIFIER, LibC::EVFILT_USER, 0, LibC::NOTE_TRIGGER)\n    {% else %}\n      ident = INTERRUPT_IDENTIFIER\n      ret = LibC.write(@pipe[1], pointerof(ident), sizeof(Int32))\n      raise RuntimeError.from_errno(\"write\") if ret == -1\n    {% end %}\n  end\n\n  protected def system_add(fd : Int32, index : Polling::Arena::Index) : Nil\n    Crystal.trace :evloop, \"kevent\", op: \"add\", fd: fd, index: index.to_i64\n\n    # register both read and write events\n    kevents = uninitialized LibC::Kevent[2]\n    {LibC::EVFILT_READ, LibC::EVFILT_WRITE}.each_with_index do |filter, i|\n      kevent = kevents.to_unsafe + i\n      udata =\n        {% if flag?(:bits64) %}\n          Pointer(Void).new(index.to_u64)\n        {% else %}\n          # assuming 32-bit target: pass the generation as udata (ident is the fd/index)\n          Pointer(Void).new(index.generation)\n        {% end %}\n      System::Kqueue.set(kevent, fd, filter, LibC::EV_ADD | LibC::EV_CLEAR, udata: udata)\n    end\n\n    @kqueue.kevent(kevents.to_slice) do\n      raise RuntimeError.from_errno(\"kevent\")\n    end\n  end\n\n  protected def system_del(fd : Int32, closing = true) : Nil\n    system_del(fd, closing) do\n      raise RuntimeError.from_errno(\"kevent\")\n    end\n  end\n\n  protected def system_del(fd : Int32, closing = true, &) : Nil\n    return if closing # nothing to do: close(2) will do the cleanup\n\n    Crystal.trace :evloop, \"kevent\", op: \"del\", fd: fd\n\n    # unregister both read and write events\n    kevents = uninitialized LibC::Kevent[2]\n    {LibC::EVFILT_READ, LibC::EVFILT_WRITE}.each_with_index do |filter, i|\n      kevent = kevents.to_unsafe + i\n      System::Kqueue.set(kevent, fd, filter, LibC::EV_DELETE)\n    end\n\n    @kqueue.kevent(kevents.to_slice) do\n      raise RuntimeError.from_errno(\"kevent\")\n    end\n  end\n\n  private def system_set_timer(time : Time::Instant?) : Nil\n    if time\n      flags = LibC::EV_ADD | LibC::EV_ONESHOT | LibC::EV_CLEAR\n\n      # Cannot use `::Time.instant` here because it could be mocked.\n      t = time.duration_since(Crystal::System::Time.instant)\n\n      data = t.total_nanoseconds.to_i64!\n      {% unless LibC.has_constant?(:NOTE_NSECONDS) %}\n        # legacy BSD (and DragonFly) only have millisecond precision, so we\n        # round up to the next millisecond.\n        data = (data + 999_999) // 1_000_000\n      {% end %}\n    else\n      flags = LibC::EV_DELETE\n      data = 0_u64\n    end\n\n    fflags =\n      {% if LibC.has_constant?(:NOTE_NSECONDS) %}\n        LibC::NOTE_NSECONDS\n      {% else %}\n        0\n      {% end %}\n\n    @kqueue.kevent(TIMER_IDENTIFIER, LibC::EVFILT_TIMER, flags, fflags, data) do\n      raise RuntimeError.from_errno(\"kevent\") unless Errno.value == Errno::ENOENT\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/libevent/event.cr",
    "content": "require \"./lib_event2\"\n\n{% if flag?(:preview_mt) %}\n  LibEvent2.evthread_use_pthreads\n{% end %}\n\n# :nodoc:\nclass Crystal::EventLoop::LibEvent < Crystal::EventLoop\n  struct Event\n    include Crystal::EventLoop::Event\n\n    VERSION = String.new(LibEvent2.event_get_version)\n\n    def self.callback(&block : Int32, LibEvent2::EventFlags, Void* ->)\n      block\n    end\n\n    def initialize(@event : LibEvent2::Event)\n      @freed = false\n    end\n\n    def add(timeout : Time::Span) : Nil\n      timeval = LibC::Timeval.new(\n        tv_sec: LibC::TimeT.new(timeout.total_seconds),\n        tv_usec: timeout.nanoseconds // 1_000\n      )\n      LibEvent2.event_add(@event, pointerof(timeval))\n    end\n\n    def add(timeout : Nil) : Nil\n      LibEvent2.event_add(@event, nil)\n    end\n\n    def free : Nil\n      LibEvent2.event_free(@event) unless @freed\n      @freed = true\n    end\n\n    def delete\n      unless LibEvent2.event_del(@event) == 0\n        raise \"Error deleting event\"\n      end\n    end\n\n    # :nodoc:\n    struct Base\n      def initialize\n        @base = LibEvent2.event_base_new\n      end\n\n      def reinit : Nil\n        unless LibEvent2.event_reinit(@base) == 0\n          raise \"Error reinitializing libevent\"\n        end\n      end\n\n      def new_event(s : Int32, flags : LibEvent2::EventFlags, data, &callback : LibEvent2::Callback)\n        event = LibEvent2.event_new(@base, s, flags, callback, data.as(Void*))\n        LibEvent::Event.new(event)\n      end\n\n      # NOTE: may return `true` even if no event has been triggered (e.g.\n      #       nonblocking), but `false` means that nothing was processed.\n      def loop(flags : LibEvent2::EventLoopFlags) : Bool\n        LibEvent2.event_base_loop(@base, flags) == 0\n      end\n\n      def loop_break : Nil\n        LibEvent2.event_base_loopbreak(@base)\n      end\n\n      def loop_exit : Nil\n        LibEvent2.event_base_loopexit(@base, nil)\n      end\n\n      def new_dns_base(init = true)\n        DnsBase.new LibEvent2.evdns_base_new(@base, init ? 1 : 0)\n      end\n    end\n\n    struct DnsBase\n      def initialize(@dns_base : LibEvent2::DnsBase)\n      end\n\n      def getaddrinfo(nodename, servname, hints, data, &callback : LibEvent2::DnsGetAddrinfoCallback)\n        request = LibEvent2.evdns_getaddrinfo(@dns_base, nodename, servname, hints, callback, data.as(Void*))\n        GetAddrInfoRequest.new request if request\n      end\n\n      struct GetAddrInfoRequest\n        def initialize(@request : LibEvent2::DnsGetAddrinfoRequest)\n        end\n\n        def cancel\n          LibEvent2.evdns_getaddrinfo_cancel(@request)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/libevent/lib_event2.cr",
    "content": "require \"c/netdb\"\n\n# On musl systems, librt is empty. The entire library is already included in libc.\n# On gnu systems, it's been integrated into `glibc` since 2.34 and it's not available\n# as a shared library.\n{% if flag?(:linux) && flag?(:gnu) && !flag?(:interpreted) && !flag?(:android) %}\n  @[Link(\"rt\")]\n{% end %}\n\n# Supported library versions:\n#\n# * libevent2\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#other-runtime-libraries\n{% if flag?(:openbsd) %}\n  @[Link(\"event_core\")]\n  @[Link(\"event_extra\")]\n{% else %}\n  @[Link(\"event\", pkg_config: \"libevent\")]\n{% end %}\n{% if flag?(:preview_mt) %}\n  @[Link(\"event_pthreads\", pkg_config: \"libevent_pthreads\")]\n{% end %}\nlib LibEvent2\n  alias Int = LibC::Int\n\n  alias EvutilSocketT = Int\n\n  type EventBase = Void*\n  type Event = Void*\n\n  @[Flags]\n  enum EventLoopFlags\n    Once          = 0x01\n    NonBlock      = 0x02\n    NoExitOnEmpty = 0x04\n  end\n\n  @[Flags]\n  enum EventFlags : LibC::Short\n    Timeout = 0x01\n    Read    = 0x02\n    Write   = 0x04\n    Signal  = 0x08\n    Persist = 0x10\n    ET      = 0x20\n  end\n\n  alias Callback = (EvutilSocketT, EventFlags, Void*) ->\n\n  fun event_get_version : UInt8*\n  fun event_base_new : EventBase\n  fun event_base_dispatch(eb : EventBase) : Int\n  fun event_base_loop(eb : EventBase, flags : EventLoopFlags) : Int\n  fun event_base_loopbreak(eb : EventBase) : Int\n  fun event_base_loopexit(EventBase, LibC::Timeval*) : LibC::Int\n  fun event_set_log_callback(callback : (Int, UInt8*) -> Nil)\n  fun event_enable_debug_mode\n  fun event_reinit(eb : EventBase) : Int\n  fun event_new(eb : EventBase, s : EvutilSocketT, events : EventFlags, callback : Callback, data : Void*) : Event\n  fun event_free(event : Event)\n  fun event_add(event : Event, timeout : LibC::Timeval*) : Int\n  fun event_del(event : Event) : Int\n\n  type DnsBase = Void*\n  type DnsGetAddrinfoRequest = Void*\n\n  EVUTIL_EAI_CANCEL = -90001\n\n  alias DnsGetAddrinfoCallback = (Int32, LibC::Addrinfo*, Void*) ->\n\n  fun evdns_base_new(base : EventBase, init : Int32) : DnsBase\n  fun evdns_base_free(base : DnsBase, fail_requests : Int32)\n  fun evdns_getaddrinfo(base : DnsBase, nodename : UInt8*, servname : UInt8*, hints : LibC::Addrinfo*, cb : DnsGetAddrinfoCallback, arg : Void*) : DnsGetAddrinfoRequest\n  fun evdns_getaddrinfo_cancel(DnsGetAddrinfoRequest)\n  fun evutil_freeaddrinfo(ai : LibC::Addrinfo*)\n\n  {% if flag?(:preview_mt) %}\n    fun evthread_use_pthreads : Int\n  {% end %}\nend\n"
  },
  {
    "path": "src/crystal/event_loop/libevent.cr",
    "content": "require \"./libevent/event\"\nrequire \"./lock\"\n\n# :nodoc:\nclass Crystal::EventLoop::LibEvent < Crystal::EventLoop\n  def self.default_file_blocking?\n    false\n  end\n\n  def self.default_socket_blocking?\n    false\n  end\n\n  private getter(event_base) { Crystal::EventLoop::LibEvent::Event::Base.new }\n\n  def initialize(parallelism : Int32)\n  end\n\n  {% unless flag?(:preview_mt) %}\n    # Reinitializes the event loop after a fork.\n    def after_fork : Nil\n      event_base.reinit\n    end\n  {% end %}\n\n  def run(blocking : Bool) : Bool\n    flags = LibEvent2::EventLoopFlags::Once\n    flags |= blocking ? LibEvent2::EventLoopFlags::NoExitOnEmpty : LibEvent2::EventLoopFlags::NonBlock\n    event_base.loop(flags)\n  end\n\n  {% if flag?(:execution_context) %}\n    # the evloop has a single poll instance for the context and only one\n    # scheduler must wait on the evloop at any time\n    include Lock\n\n    def run(queue : Fiber::List*, blocking : Bool) : Nil\n      Crystal.trace :evloop, \"run\", blocking: blocking\n      @runnables = queue\n      run(blocking)\n    ensure\n      @runnables = nil\n    end\n\n    def callback_enqueue(fiber : Fiber) : Nil\n      if queue = @runnables\n        queue.value.push(fiber)\n      else\n        raise \"BUG: libevent callback executed outside of #run(queue*, blocking) call\"\n      end\n    end\n  {% end %}\n\n  def interrupt : Nil\n    event_base.loop_exit\n  end\n\n  def sleep(duration : ::Time::Span) : Nil\n    Fiber.current.resume_event.add(duration)\n    Fiber.suspend\n  end\n\n  # Create a new resume event for a fiber (sleep).\n  def create_resume_event(fiber : Fiber) : Crystal::EventLoop::LibEvent::Event\n    event_base.new_event(-1, LibEvent2::EventFlags::None, fiber) do |s, flags, data|\n      f = data.as(Fiber)\n      {% if flag?(:execution_context) %}\n        event_loop = Crystal::EventLoop.current.as(Crystal::EventLoop::LibEvent)\n        event_loop.callback_enqueue(f)\n      {% else %}\n        f.enqueue\n      {% end %}\n    end\n  end\n\n  # Creates a timeout event (timeout action of select expression).\n  def create_timeout_event(fiber) : Crystal::EventLoop::LibEvent::Event\n    event_base.new_event(-1, LibEvent2::EventFlags::None, fiber) do |s, flags, data|\n      f = data.as(Fiber)\n      if select_action = f.timeout_select_action\n        f.timeout_select_action = nil\n        if select_action.time_expired?\n          {% if flag?(:execution_context) %}\n            event_loop = Crystal::EventLoop.current.as(Crystal::EventLoop::LibEvent)\n            event_loop.callback_enqueue(f)\n          {% else %}\n            f.enqueue\n          {% end %}\n        end\n      end\n    end\n  end\n\n  # Creates a write event for a file descriptor.\n  def create_fd_write_event(io : IO::Evented, edge_triggered : Bool = false) : Crystal::EventLoop::Event\n    flags = LibEvent2::EventFlags::Write\n    flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered\n\n    event_base.new_event(io.fd, flags, io) do |s, flags, data|\n      io_ref = data.as(typeof(io))\n      if flags.includes?(LibEvent2::EventFlags::Write)\n        io_ref.resume_write\n      elsif flags.includes?(LibEvent2::EventFlags::Timeout)\n        io_ref.resume_write(timed_out: true)\n      end\n    end\n  end\n\n  # Creates a read event for a file descriptor.\n  def create_fd_read_event(io : IO::Evented, edge_triggered : Bool = false) : Crystal::EventLoop::Event\n    flags = LibEvent2::EventFlags::Read\n    flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered\n\n    event_base.new_event(io.fd, flags, io) do |s, flags, data|\n      io_ref = data.as(typeof(io))\n      if flags.includes?(LibEvent2::EventFlags::Read)\n        io_ref.resume_read\n      elsif flags.includes?(LibEvent2::EventFlags::Timeout)\n        io_ref.resume_read(timed_out: true)\n      end\n    end\n  end\n\n  def pipe(read_blocking : Bool?, write_blocking : Bool?) : {IO::FileDescriptor, IO::FileDescriptor}\n    r, w = System::FileDescriptor.system_pipe\n    System::FileDescriptor.set_blocking(r, false) unless read_blocking\n    System::FileDescriptor.set_blocking(w, false) unless write_blocking\n    {\n      IO::FileDescriptor.new(handle: r),\n      IO::FileDescriptor.new(handle: w),\n    }\n  end\n\n  def open(path : String, flags : Int32, permissions : File::Permissions, blocking : Bool?) : {System::FileDescriptor::Handle, Bool} | Errno\n    path.check_no_null_byte\n\n    fd, errno = ::Fiber.syscall do\n      ret = LibC.open(path, flags | LibC::O_CLOEXEC, permissions)\n      {ret, Errno.value}\n    end\n    return errno if fd == -1\n\n    {% if flag?(:darwin) %}\n      # FIXME: poll of non-blocking fifo fd on darwin appears to be broken, so\n      # we default to blocking for the time being\n      blocking = true if blocking.nil?\n    {% end %}\n\n    System::FileDescriptor.set_blocking(fd, false) unless blocking\n    {fd, !!blocking}\n  end\n\n  def read(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n    evented_read(file_descriptor, \"Error reading file_descriptor\") do\n      LibC.read(file_descriptor.fd, slice, slice.size).tap do |return_code|\n        if return_code == -1 && Errno.value == Errno::EBADF\n          raise IO::Error.new \"File not open for reading\", target: file_descriptor\n        end\n      end\n    end\n  end\n\n  def wait_readable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.evented_wait_readable(raise_if_closed: false) do\n      raise IO::TimeoutError.new(\"Read timed out\")\n    end\n  end\n\n  def write(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n    evented_write(file_descriptor, \"Error writing file_descriptor\") do\n      LibC.write(file_descriptor.fd, slice, slice.size).tap do |return_code|\n        if return_code == -1 && Errno.value == Errno::EBADF\n          raise IO::Error.new \"File not open for writing\", target: file_descriptor\n        end\n      end\n    end\n  end\n\n  def wait_writable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.evented_wait_writable do\n      raise IO::TimeoutError.new(\"Write timed out\")\n    end\n  end\n\n  def reopened(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.evented_close\n  end\n\n  def shutdown(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    # perform cleanup before LibC.close. Using a file descriptor after it has\n    # been closed is never defined and can always lead to undefined results\n    file_descriptor.evented_close\n  end\n\n  def close(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.file_descriptor_close\n  end\n\n  def socket(family : ::Socket::Family, type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool?) : {::Socket::Handle, Bool}\n    socket = System::Socket.socket(family, type, protocol, !!blocking)\n    {socket, !!blocking}\n  end\n\n  def socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol) : Tuple({::Socket::Handle, ::Socket::Handle}, Bool)\n    socket = System::Socket.socketpair(type, protocol, blocking: false)\n    {socket, false}\n  end\n\n  def read(socket : ::Socket, slice : Bytes) : Int32\n    evented_read(socket, \"Error reading socket\") do\n      LibC.recv(socket.fd, slice, slice.size, 0).to_i32\n    end\n  end\n\n  def wait_readable(socket : ::Socket) : Nil\n    socket.evented_wait_readable(raise_if_closed: false) do\n      raise IO::TimeoutError.new(\"Read timed out\")\n    end\n  end\n\n  def write(socket : ::Socket, slice : Bytes) : Int32\n    evented_write(socket, \"Error writing to socket\") do\n      LibC.send(socket.fd, slice, slice.size, 0).to_i32\n    end\n  end\n\n  def wait_writable(socket : ::Socket) : Nil\n    socket.evented_wait_writable do\n      raise IO::TimeoutError.new(\"Write timed out\")\n    end\n  end\n\n  def receive_from(socket : ::Socket, slice : Bytes) : Tuple(Int32, ::Socket::Address)\n    sockaddr = Pointer(LibC::SockaddrStorage).malloc.as(LibC::Sockaddr*)\n    # initialize sockaddr with the initialized family of the socket\n    copy = sockaddr.value\n    copy.sa_family = socket.family\n    sockaddr.value = copy\n\n    addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrStorage))\n\n    bytes_read = evented_read(socket, \"Error receiving datagram\") do\n      LibC.recvfrom(socket.fd, slice, slice.size, 0, sockaddr, pointerof(addrlen))\n    end\n\n    {bytes_read, ::Socket::Address.from(sockaddr, addrlen)}\n  end\n\n  def send_to(socket : ::Socket, slice : Bytes, address : ::Socket::Address) : Int32\n    bytes_sent = LibC.sendto(socket.fd, slice.to_unsafe.as(Void*), slice.size, 0, address, address.size)\n    raise ::Socket::Error.from_errno(\"Error sending datagram to #{address}\") if bytes_sent == -1\n    # to_i32 is fine because string/slice sizes are an Int32\n    bytes_sent.to_i32\n  end\n\n  def connect(socket : ::Socket, address : ::Socket::Addrinfo | ::Socket::Address, timeout : ::Time::Span?) : IO::Error?\n    loop do\n      if LibC.connect(socket.fd, address, address.size) == 0\n        return\n      end\n      case Errno.value\n      when Errno::EISCONN\n        return\n      when Errno::EINPROGRESS, Errno::EALREADY\n        socket.evented_wait_writable(timeout: timeout) do\n          return IO::TimeoutError.new(\"connect timed out\")\n        end\n      else\n        return ::Socket::ConnectError.from_errno(\"connect\")\n      end\n    end\n  end\n\n  def accept(socket : ::Socket) : {::Socket::Handle, Bool}?\n    loop do\n      client_fd =\n        {% if LibC.has_method?(:accept4) %}\n          LibC.accept4(socket.fd, nil, nil, LibC::SOCK_CLOEXEC | LibC::SOCK_NONBLOCK)\n        {% else %}\n          # we may fail to set FD_CLOEXEC between `accept` and `fcntl` but we\n          # can't call `Crystal::System::Socket.lock_read` because the socket\n          # might be in blocking mode and accept would block until the socket\n          # receives a connection.\n          #\n          # we could lock when `socket.blocking?` is false, but another thread\n          # could change the socket back to blocking mode between the condition\n          # check and the `accept` call.\n          fd = LibC.accept(socket.fd, nil, nil)\n          unless fd == -1\n            System::Socket.fcntl(fd, LibC::F_SETFD, LibC::FD_CLOEXEC)\n            System::Socket.fcntl(fd, LibC::F_SETFL, System::Socket.fcntl(fd, LibC::F_GETFL) | LibC::O_NONBLOCK)\n          end\n          fd\n        {% end %}\n\n      if client_fd == -1\n        if socket.closed?\n          return\n        elsif Errno.value == Errno::EAGAIN\n          socket.evented_wait_readable(raise_if_closed: false) do\n            raise IO::TimeoutError.new(\"Accept timed out\")\n          end\n          return if socket.closed?\n        else\n          raise ::Socket::Error.from_errno(\"accept\")\n        end\n      else\n        return {client_fd, false}\n      end\n    end\n  end\n\n  def shutdown(socket : ::Socket) : Nil\n    # perform cleanup before LibC.close. Using a file descriptor after it has\n    # been closed is never defined and can always lead to undefined results\n    socket.evented_close\n  end\n\n  def close(socket : ::Socket) : Nil\n    socket.socket_close\n  end\n\n  def evented_read(target, errno_msg : String, &) : Int32\n    loop do\n      bytes_read = yield\n      if bytes_read != -1\n        # `to_i32` is acceptable because `Slice#size` is an Int32\n        return bytes_read.to_i32\n      end\n\n      if Errno.value == Errno::EAGAIN\n        target.evented_wait_readable do\n          raise IO::TimeoutError.new(\"Read timed out\")\n        end\n      else\n        raise IO::Error.from_errno(errno_msg, target: target)\n      end\n    end\n  ensure\n    target.evented_resume_pending_readers\n  end\n\n  def evented_write(target, errno_msg : String, &) : Int32\n    loop do\n      bytes_written = yield\n      if bytes_written != -1\n        return bytes_written.to_i32\n      end\n\n      if Errno.value == Errno::EAGAIN\n        target.evented_wait_writable do\n          raise IO::TimeoutError.new(\"Write timed out\")\n        end\n      else\n        raise IO::Error.from_errno(errno_msg, target: target)\n      end\n    end\n  ensure\n    target.evented_resume_pending_writers\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/lock.cr",
    "content": "# :nodoc:\nmodule Crystal::EventLoop::Lock\n  @lock = Atomic(Bool).new(false)\n\n  def lock?(&) : Bool\n    if @lock.swap(true, :acquire) == false\n      begin\n        yield\n      ensure\n        @lock.set(false, :release)\n      end\n      true\n    else\n      false\n    end\n  end\n\n  def interrupt? : Bool\n    if @lock.get(:relaxed)\n      interrupt\n      true\n    else\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/polling/arena.cr",
    "content": "# Generational Arena.\n#\n# The arena allocates objects `T` at a predefined index. The object itself is\n# uninitialized (outside of having its memory initialized to zero). The object\n# can be allocated and later retrieved using the generation index (Arena::Index)\n# that contains both the actual index (Int32) and the generation number\n# (UInt32). Deallocating the object increases the generation number, which\n# allows the object to be reallocated later on. Trying to retrieve the\n# allocation using the generation index will fail if the generation number\n# changed (it's a new allocation).\n#\n# This arena isn't generic as it won't keep a list of free indexes. It assumes\n# that something else will maintain the uniqueness of indexes and reuse indexes\n# as much as possible instead of growing.\n#\n# For example this arena is used to hold `Crystal::EventLoop::Polling::PollDescriptor`\n# allocations for all the fd in a program, where the fd is used as the index.\n# They're unique to the process and the OS always reuses the lowest fd numbers\n# before growing.\n#\n# Thread safety: the memory region is divided in blocks of size BLOCK_BYTESIZE\n# allocated in the GC. Pointers are thus never invalidated. Mutating the blocks\n# is protected by a mutual exclusion lock. Individual (de)allocations of objects\n# are protected with a fine grained lock.\n#\n# Guarantees: blocks' memory is initialized to zero, which means `T` objects are\n# initialized to zero by default, then `#free` will also clear the memory, so\n# the next allocation shall be initialized to zero, too.\nclass Crystal::EventLoop::Polling::Arena(T, BLOCK_BYTESIZE)\n  INVALID_INDEX = Index.new(-1, 0)\n\n  struct Index\n    def initialize(index : Int32, generation : UInt32)\n      @data = (index.to_i64! << 32) | generation.to_u64!\n    end\n\n    def initialize(@data : Int64)\n    end\n\n    def initialize(data : UInt64)\n      @data = data.to_i64!\n    end\n\n    # Returns the generation number.\n    def generation : UInt32\n      @data.to_u32!\n    end\n\n    # Returns the actual index.\n    def index : Int32\n      (@data >> 32).to_i32!\n    end\n\n    def to_i64 : Int64\n      @data\n    end\n\n    def to_u64 : UInt64\n      @data.to_u64!\n    end\n\n    def valid? : Bool\n      @data >= 0\n    end\n  end\n\n  struct Entry(T)\n    @lock = SpinLock.new # protects parallel allocate/free calls\n    property? allocated = false\n    property generation = 0_u32\n    @object = uninitialized T\n\n    def pointer : Pointer(T)\n      pointerof(@object)\n    end\n\n    def free : Nil\n      @generation &+= 1_u32\n      @allocated = false\n      pointer.clear(1)\n    end\n  end\n\n  @blocks : Slice(Pointer(Entry(T)))\n  @capacity : Int32\n\n  def initialize(@capacity : Int32)\n    @blocks = Slice(Pointer(Entry(T))).new(1) { allocate_block }\n    @mutex = Thread::Mutex.new\n  end\n\n  # Allocates the object at *index* unless already allocated, then yields a\n  # pointer to the object at *index* and the current generation index to later\n  # retrieve and free the allocated object. Eventually returns the generation\n  # index.\n  #\n  # Does nothing if the object has already been allocated and returns `nil`.\n  #\n  # There are no generational checks.\n  # Raises if *index* is out of bounds.\n  def allocate_at?(index : Int32, & : (Pointer(T), Index) ->) : Index?\n    entry = at(index, grow: true)\n\n    entry.value.@lock.sync do\n      return if entry.value.allocated?\n\n      entry.value.allocated = true\n\n      gen_index = Index.new(index, entry.value.generation)\n      yield entry.value.pointer, gen_index\n\n      gen_index\n    end\n  end\n\n  # Same as `#allocate_at?` but raises when already allocated.\n  def allocate_at(index : Int32, & : (Pointer(T), Index) ->) : Index?\n    allocate_at?(index) { |ptr, idx| yield ptr, idx } ||\n      raise RuntimeError.new(\"#{self.class.name}: already allocated index=#{index}\")\n  end\n\n  # Yields a pointer to the object previously allocated at *index*.\n  #\n  # Raises if the object isn't allocated, the generation has changed (i.e. the\n  # object has been freed then reallocated) or *index* is out of bounds.\n  def get(index : Index, &) : Nil\n    at(index) do |entry|\n      yield entry.value.pointer\n    end\n  end\n\n  # Yields a pointer to the object previously allocated at *index* and returns\n  # true.\n  #\n  # Does nothing if the object isn't allocated, the generation has changed or\n  # *index* is out of bounds.\n  def get?(index : Index, &) : Bool\n    at?(index) do |entry|\n      yield entry.value.pointer\n      return true\n    end\n    false\n  end\n\n  # Yields the object previously allocated at *index* then releases it.\n  #\n  # Does nothing if the object isn't allocated, the generation has changed or\n  # *index* is out of bounds.\n  def free(index : Index, &) : Nil\n    at?(index) do |entry|\n      yield entry.value.pointer\n    ensure\n      entry.value.free\n    end\n  end\n\n  private def at(index : Index, &) : Nil\n    entry = at(index.index, grow: false)\n    entry.value.@lock.lock\n\n    unless entry.value.allocated? && entry.value.generation == index.generation\n      entry.value.@lock.unlock\n      raise RuntimeError.new(\"#{self.class.name}: invalid reference index=#{index.index}:#{index.generation} current=#{index.index}:#{entry.value.generation}\")\n    end\n\n    begin\n      yield entry\n    ensure\n      entry.value.@lock.unlock\n    end\n  end\n\n  private def at?(index : Index, &) : Nil\n    return unless entry = at?(index.index)\n\n    entry.value.@lock.sync do\n      return unless entry.value.allocated?\n      return unless entry.value.generation == index.generation\n\n      yield entry\n    end\n  end\n\n  private def at(index : Int32, grow : Bool) : Pointer(Entry(T))\n    raise IndexError.new unless 0 <= index < @capacity\n\n    n, j = index.divmod(entries_per_block)\n\n    if n >= @blocks.size\n      raise RuntimeError.new(\"#{self.class.name}: not allocated index=#{index}\") unless grow\n      @mutex.synchronize { unsafe_grow(n) if n >= @blocks.size }\n    end\n\n    @blocks.to_unsafe[n] + j\n  end\n\n  private def at?(index : Int32) : Pointer(Entry(T))?\n    return unless 0 <= index < @capacity\n\n    n, j = index.divmod(entries_per_block)\n\n    if block = @blocks[n]?\n      block + j\n    end\n  end\n\n  private def unsafe_grow(n)\n    # we manually dup instead of using realloc to avoid parallelism issues, for\n    # example fork or another thread trying to iterate after realloc but before\n    # we got the time to set @blocks or to allocate the new blocks\n    new_size = n + 1\n    new_pointer = GC.malloc(new_size * sizeof(Pointer(Entry(T)))).as(Pointer(Pointer(Entry(T))))\n    @blocks.to_unsafe.copy_to(new_pointer, @blocks.size)\n    @blocks.size.upto(n) { |j| new_pointer[j] = allocate_block }\n\n    @blocks = Slice.new(new_pointer, new_size)\n  end\n\n  private def allocate_block\n    GC.malloc(BLOCK_BYTESIZE).as(Pointer(Entry(T)))\n  end\n\n  # Iterates all allocated objects, yields the actual index as well as the\n  # generation index.\n  def each_index(&) : Nil\n    index = 0\n\n    @blocks.each do |block|\n      entries_per_block.times do |j|\n        entry = block + j\n\n        if entry.value.allocated?\n          yield index, Index.new(index, entry.value.generation)\n        end\n\n        index += 1\n      end\n    end\n  end\n\n  private def entries_per_block\n    # can't be a constant: can't access a generic when assigning a constant\n    BLOCK_BYTESIZE // sizeof(Entry(T))\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/polling/event.cr",
    "content": "require \"crystal/pointer_linked_list\"\nrequire \"crystal/pointer_pairing_heap\"\n\n# Information about the event that a `Fiber` is waiting on.\n#\n# The event can be waiting for `IO` with or without a timeout, or be a timed\n# event such as sleep or a select timeout (without IO).\n#\n# The events can be found in different queues, for example `Timers` and/or\n# `Waiters` depending on their type.\nstruct Crystal::EventLoop::Polling::Event\n  enum Type\n    IoRead\n    IoWrite\n    Sleep\n    SelectTimeout\n  end\n\n  getter type : Type\n\n  # The `Fiber` that is waiting on the event and that the `EventLoop` shall\n  # resume.\n  getter fiber : Fiber\n\n  # Arena index to access the associated `PollDescriptor` when processing an IO\n  # event. Nil for timed events (sleep, select timeout).\n  getter! index : Arena::Index?\n\n  # The absolute time, against the monotonic clock, at which a timed event shall\n  # trigger. Nil for IO events without a timeout.\n  getter! wake_at : Time::Instant\n\n  # True if an IO event has timed out (i.e. we're past `#wake_at`).\n  getter? timed_out : Bool = false\n\n  # The event can be added to `Waiters` lists.\n  include PointerLinkedList::Node\n\n  # The event can be added to the `Timers` list.\n  include PointerPairingHeap::Node\n\n  def initialize(@type : Type, @fiber, @index = nil, timeout : Time::Span? = nil)\n    if timeout\n      now = Crystal::System::Time.instant\n      @wake_at = now + timeout\n    end\n  end\n\n  # Mark the IO event as timed out.\n  def timed_out! : Bool\n    @timed_out = true\n  end\n\n  # Manually set the absolute time (against the monotonic clock). This is meant\n  # for `FiberEvent` to set and cancel its inner sleep or select timeout; these\n  # objects are allocated once per `Fiber`.\n  #\n  # NOTE: musn't be changed after registering the event into `Timers`!\n  def wake_at=(@wake_at)\n  end\n\n  def heap_compare(other : Pointer(self)) : Bool\n    wake_at < other.value.wake_at\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/polling/fiber_event.cr",
    "content": "class Crystal::EventLoop::Polling::FiberEvent\n  include Crystal::EventLoop::Event\n\n  def initialize(type : Event::Type, fiber : Fiber)\n    @event = Event.new(type, fiber)\n  end\n\n  # sleep or select timeout\n  def add(timeout : Time::Span) : Nil\n    now = Crystal::System::Time.instant\n    @event.wake_at = now + timeout\n    EventLoop.current.add_timer(pointerof(@event))\n  end\n\n  # select timeout has been cancelled\n  def delete : Nil\n    return unless @event.wake_at?\n\n    EventLoop.current.delete_timer(pointerof(@event))\n    clear\n  end\n\n  # fiber died\n  def free : Nil\n    delete\n  end\n\n  # the timer triggered (already dequeued from eventloop)\n  def clear : Nil\n    @event.wake_at = nil\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/polling/poll_descriptor.cr",
    "content": "# Information related to the evloop for a fd, such as the read and write queues\n# (waiting `Event`), as well as which evloop instance currently owns the fd.\n#\n# Thread-unsafe: parallel mutations must be protected with a lock.\nstruct Crystal::EventLoop::Polling::PollDescriptor\n  @event_loop : Polling?\n  @readers = Waiters.new\n  @writers = Waiters.new\n\n  def owned_by?(event_loop) : Bool\n    @event_loop == event_loop\n  end\n\n  # Makes *event_loop* the new owner of *fd*.\n  # Removes *fd* from the current event loop (if any).\n  def take_ownership(event_loop : EventLoop, fd : Int32, index : Arena::Index) : Nil\n    current = @event_loop\n\n    if event_loop == current\n      raise \"BUG: evloop already owns the poll-descriptor for fd=#{fd}\"\n    end\n\n    # ensure we can't have cross enqueues after we transfer the fd, so we\n    # can optimize (all enqueues are local) and we don't end up with a timer\n    # from evloop A to cancel an event from evloop B (currently unsafe)\n    if current && !empty?\n      raise RuntimeError.new(\"Can't transfer fd=#{fd} to another polling event loop with pending reader/writer fibers\")\n    end\n\n    @event_loop = event_loop\n    event_loop.system_add(fd, index)\n    current.try(&.system_del(fd, closing: false))\n  end\n\n  # Removes *fd* from its owner event loop. Raises on errors.\n  def remove(fd : Int32) : Nil\n    current, @event_loop = @event_loop, nil\n    current.try(&.system_del(fd))\n  end\n\n  # Same as `#remove` but yields on errors.\n  def remove(fd : Int32, &) : Nil\n    current, @event_loop = @event_loop, nil\n    current.try(&.system_del(fd) { yield })\n  end\n\n  # Returns true when there is at least one reader or writer. Returns false\n  # otherwise.\n  def empty? : Bool\n    @readers.@list.empty? && @writers.@list.empty?\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/polling/waiters.cr",
    "content": "# A FIFO queue of `Event` waiting on the same operation (either read or write)\n# for a fd. See `PollDescriptor`.\n#\n# Race conditions on the state of the waiting list are handled through the ready\n# always ready variables.\n#\n# Thread unsafe: parallel mutations must be protected with a lock.\nstruct Crystal::EventLoop::Polling::Waiters\n  @list = PointerLinkedList(Event).new\n  @ready = false\n  @always_ready = false\n\n  # Adds an event to the waiting list. May return false immediately if another\n  # thread marked the list as ready in parallel, returns true otherwise.\n  def add(event : Pointer(Event)) : Bool\n    if @always_ready\n      # another thread closed the fd or we received a fd error or hup event:\n      # the fd will never block again\n      return false\n    end\n\n    if @ready\n      # another thread readied the fd before the current thread got to add\n      # the event: don't block and resets @ready for the next loop\n      @ready = false\n      return false\n    end\n\n    @list.push(event)\n    true\n  end\n\n  def delete(event : Pointer(Event)) : Nil\n    @list.delete(event) if event.value.next\n  end\n\n  # Removes one pending event or marks the list as ready when there are no\n  # pending events (we got notified of readiness before a thread enqueued).\n  def ready_one(& : Pointer(Event) -> Bool) : Nil\n    # loop until the block successfully processes an event (it may have to\n    # dequeue the timeout from timers)\n    loop do\n      if event = @list.shift?\n        break if yield event\n      else\n        # no event queued but another thread may be waiting for the lock to\n        # add an event: set as ready to resolve the race condition\n        @ready = true\n        return\n      end\n    end\n  end\n\n  # Dequeues all pending events and marks the list as always ready. This must be\n  # called when a fd is closed or an error or hup event occurred.\n  def ready_all(& : Pointer(Event) ->) : Nil\n    @list.consume_each { |event| yield event }\n    @always_ready = true\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/polling.cr",
    "content": "# forward declaration for the require below to not create a module\nabstract class Crystal::EventLoop::Polling < Crystal::EventLoop; end\n\nrequire \"./polling/*\"\nrequire \"./timers\"\nrequire \"./lock\"\n\nmodule Crystal::System::FileDescriptor\n  # user data (generation index for the arena)\n  property __evloop_data : EventLoop::Polling::Arena::Index = EventLoop::Polling::Arena::INVALID_INDEX\nend\n\nmodule Crystal::System::Socket\n  # user data (generation index for the arena)\n  property __evloop_data : EventLoop::Polling::Arena::Index = EventLoop::Polling::Arena::INVALID_INDEX\nend\n\n# Polling EventLoop.\n#\n# This is the abstract interface that implements `Crystal::EventLoop` for\n# polling based UNIX targets, such as epoll (linux), kqueue (bsd), or poll\n# (posix) syscalls. This class only implements the generic parts for the\n# external world to interact with the loop. A specific implementation is\n# required to handle the actual syscalls. See `Crystal::Epoll::EventLoop` and\n# `Crystal::Kqueue::EventLoop`.\n#\n# The event loop registers the fd into the kernel data structures when an IO\n# operation would block, then keeps it there until the fd is closed.\n#\n# NOTE: the fds must have `O_NONBLOCK` set.\n#\n# It is possible to have multiple event loop instances, but an fd can only be in\n# one instance at a time. When trying to block from another loop, the fd will be\n# removed from its associated loop and added to the current one (this is\n# automatic). Trying to move a fd to another loop with pending waiters is\n# unsupported and will raise an exception. See `PollDescriptor#remove`.\n#\n# A timed event such as sleep or select timeout follows the following logic:\n#\n# 1. create an `Event` (actually reuses it, see `FiberChannel`);\n# 2. register the event in `@timers`;\n# 3. supend the current fiber.\n#\n# The timer will eventually trigger and resume the fiber.\n# When an IO operation on fd would block, the loop follows the following logic:\n#\n# 1. register the fd (once);\n# 2. create an `Event`;\n# 3. suspend the current fiber;\n#\n# When the IO operation is ready, the fiber will eventually be resumed (one\n# fiber at a time). If it's an IO operation, the operation is tried again which\n# may block again, until the operation succeeds or an error occurred (e.g.\n# closed, broken pipe).\n#\n# If the IO operation has a timeout, the event is also registered into `@timers`\n# before suspending the fiber, then after resume it will raise\n# `IO::TimeoutError` if the event timed out, and continue otherwise.\nabstract class Crystal::EventLoop::Polling < Crystal::EventLoop\n  def self.default_file_blocking?\n    false\n  end\n\n  def self.default_socket_blocking?\n    false\n  end\n\n  # The generational arena:\n  #\n  # 1. decorrelates the fd from the IO since the evloop only really cares about\n  #    the fd state and to resume pending fibers (it could monitor a fd without\n  #    an IO object);\n  #\n  # 2. permits to avoid pushing raw pointers to IO objects into kernel data\n  #    structures that are unknown to the GC, and to safely check whether the\n  #    allocation is still valid before trying to dereference the pointer. Since\n  #    `PollDescriptor` also doesn't have pointers to the actual IO object, it\n  #    won't prevent the GC from collecting lost IO objects (and spares us from\n  #    using weak references).\n  #\n  # 3. to a lesser extent, it also allows to keep the `PollDescriptor` allocated\n  #    together in the same region, and polluting the IO object itself with\n  #    specific evloop data (except for the generation index).\n  #\n  # The implementation takes advantage of the fd being unique per process and\n  # that the operating system will always reuse the lowest fd (POSIX compliance)\n  # and will only grow when the process needs that many file descriptors, so the\n  # allocated memory region won't grow larger than necessary. This assumption\n  # allows the arena to skip maintaining a list of free indexes. Some systems\n  # may deviate from the POSIX default, but all systems seem to follow it, as it\n  # allows optimizations to the OS (it can reuse already allocated resources),\n  # and either the man page explicitly says so (Linux), or they don't (BSD) and\n  # they must follow the POSIX definition.\n  #\n  # The block size is set to 64KB because it's a multiple of:\n  # - 4KB (usual page size)\n  # - 1024 (common soft limit for open files)\n  # - sizeof(Arena::Entry(PollDescriptor))\n  protected class_getter arena = Arena(PollDescriptor, 65536).new(max_fds)\n\n  private def self.max_fds : Int32\n    if LibC.getrlimit(LibC::RLIMIT_NOFILE, out rlimit) == -1\n      raise RuntimeError.from_errno(\"getrlimit(RLIMIT_NOFILE)\")\n    end\n    rlimit.rlim_max.clamp(..Int32::MAX).to_i32!\n  end\n\n  @timers_lock = SpinLock.new\n  @timers = Timers(Event).new\n\n  {% unless flag?(:preview_mt) %}\n    # no parallelism issues, but let's clean-up anyway\n    def after_fork : Nil\n      @timers_lock = SpinLock.new\n    end\n  {% end %}\n\n  # thread unsafe\n  def run(blocking : Bool) : Bool\n    system_run(blocking) do |fiber|\n      {% if flag?(:execution_context) %}\n        fiber.execution_context.enqueue(fiber)\n      {% else %}\n        Crystal::Scheduler.enqueue(fiber)\n      {% end %}\n    end\n    true\n  end\n\n  {% if flag?(:execution_context) %}\n    # the evloop has a single poll instance for the context and only one\n    # scheduler must wait on the evloop at any time\n    include EventLoop::Lock\n\n    # thread unsafe\n    def run(queue : Fiber::List*, blocking : Bool) : Nil\n      system_run(blocking) { |fiber| queue.value.push(fiber) }\n    end\n  {% end %}\n\n  # fiber interface, see Crystal::EventLoop\n\n  def sleep(duration : ::Time::Span) : Nil\n    event = Event.new(:sleep, Fiber.current, timeout: duration)\n    add_timer(pointerof(event))\n    Fiber.suspend\n\n    # safety check\n    return if event.timed_out?\n\n    # try to avoid a double resume if possible, but another thread might be\n    # running the evloop and dequeue the event in parallel, so a \"can't resume\n    # dead fiber\" can still happen in a MT execution context.\n    delete_timer(pointerof(event))\n    raise \"BUG: #{event.fiber} called sleep but was manually resumed before the timer expired!\"\n  end\n\n  def create_timeout_event(fiber : Fiber) : FiberEvent\n    FiberEvent.new(:select_timeout, fiber)\n  end\n\n  # file descriptor interface, see Crystal::EventLoop::FileDescriptor\n\n  def pipe(read_blocking : Bool?, write_blocking : Bool?) : {IO::FileDescriptor, IO::FileDescriptor}\n    r, w = System::FileDescriptor.system_pipe\n    System::FileDescriptor.set_blocking(r, false) unless read_blocking\n    System::FileDescriptor.set_blocking(w, false) unless write_blocking\n    {\n      IO::FileDescriptor.new(handle: r),\n      IO::FileDescriptor.new(handle: w),\n    }\n  end\n\n  def open(path : String, flags : Int32, permissions : File::Permissions, blocking : Bool?) : {System::FileDescriptor::Handle, Bool} | Errno\n    path.check_no_null_byte\n\n    fd, errno = ::Fiber.syscall do\n      ret = LibC.open(path, flags | LibC::O_CLOEXEC, permissions)\n      {ret, Errno.value}\n    end\n    return errno if fd == -1\n\n    {% if flag?(:darwin) %}\n      # FIXME: poll of non-blocking fifo fd on darwin appears to be broken, so\n      # we default to blocking for the time being\n      blocking = true if blocking.nil?\n    {% end %}\n\n    System::FileDescriptor.set_blocking(fd, false) unless blocking\n    {fd, !!blocking}\n  end\n\n  def read(file_descriptor : System::FileDescriptor, slice : Bytes) : Int32\n    size = evented_read(file_descriptor, slice, file_descriptor.@read_timeout)\n\n    if size == -1\n      if Errno.value == Errno::EBADF\n        raise IO::Error.new(\"File not open for reading\", target: file_descriptor)\n      else\n        raise IO::Error.from_errno(\"read\", target: file_descriptor)\n      end\n    else\n      size.to_i32\n    end\n  end\n\n  def wait_readable(file_descriptor : System::FileDescriptor) : Nil\n    wait_readable(file_descriptor, file_descriptor.@read_timeout) do\n      raise IO::TimeoutError.new\n    end\n  end\n\n  def write(file_descriptor : System::FileDescriptor, slice : Bytes) : Int32\n    size = evented_write(file_descriptor, slice, file_descriptor.@write_timeout)\n\n    if size == -1\n      if Errno.value == Errno::EBADF\n        raise IO::Error.new(\"File not open for writing\", target: file_descriptor)\n      else\n        raise IO::Error.from_errno(\"write\", target: file_descriptor)\n      end\n    else\n      size.to_i32\n    end\n  end\n\n  def wait_writable(file_descriptor : System::FileDescriptor) : Nil\n    wait_writable(file_descriptor, file_descriptor.@write_timeout) do\n      raise IO::TimeoutError.new\n    end\n  end\n\n  def reopened(file_descriptor : System::FileDescriptor) : Nil\n    resume_all(file_descriptor)\n  end\n\n  def shutdown(file_descriptor : System::FileDescriptor) : Nil\n    # perform cleanup before LibC.close. Using a file descriptor after it has\n    # been closed is never defined and can always lead to undefined results\n    resume_all(file_descriptor)\n  end\n\n  def close(file_descriptor : System::FileDescriptor) : Nil\n    file_descriptor.file_descriptor_close\n  end\n\n  protected def self.remove_impl(file_descriptor : System::FileDescriptor) : Nil\n    internal_remove(file_descriptor)\n  end\n\n  # socket interface, see Crystal::EventLoop::Socket\n\n  def socket(family : ::Socket::Family, type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool?) : {::Socket::Handle, Bool}\n    socket = System::Socket.socket(family, type, protocol, !!blocking)\n    {socket, !!blocking}\n  end\n\n  def socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol) : Tuple({::Socket::Handle, ::Socket::Handle}, Bool)\n    socket = System::Socket.socketpair(type, protocol, blocking: false)\n    {socket, false}\n  end\n\n  def read(socket : ::Socket, slice : Bytes) : Int32\n    size = evented_read(socket, slice, socket.@read_timeout)\n    raise IO::Error.from_errno(\"read\", target: socket) if size == -1\n    size\n  end\n\n  def wait_readable(socket : ::Socket) : Nil\n    wait_readable(socket, socket.@read_timeout) do\n      raise IO::TimeoutError.new\n    end\n  end\n\n  def write(socket : ::Socket, slice : Bytes) : Int32\n    size = evented_write(socket, slice, socket.@write_timeout)\n    raise IO::Error.from_errno(\"write\", target: socket) if size == -1\n    size\n  end\n\n  def wait_writable(socket : ::Socket) : Nil\n    wait_writable(socket, socket.@write_timeout) do\n      raise IO::TimeoutError.new\n    end\n  end\n\n  def accept(socket : ::Socket) : {::Socket::Handle, Bool}?\n    loop do\n      client_fd =\n        {% if LibC.has_method?(:accept4) %}\n          LibC.accept4(socket.fd, nil, nil, LibC::SOCK_CLOEXEC | LibC::SOCK_NONBLOCK)\n        {% else %}\n          # we may fail to set FD_CLOEXEC between `accept` and `fcntl` but we\n          # can't call `Crystal::System::Socket.lock_read` because the socket\n          # might be in blocking mode and accept would block until the socket\n          # receives a connection.\n          #\n          # we could lock when `socket.blocking?` is false, but another thread\n          # could change the socket back to blocking mode between the condition\n          # check and the `accept` call.\n          LibC.accept(socket.fd, nil, nil).tap do |fd|\n            unless fd == -1\n              System::Socket.fcntl(fd, LibC::F_SETFD, LibC::FD_CLOEXEC)\n              System::Socket.fcntl(fd, LibC::F_SETFL, System::Socket.fcntl(fd, LibC::F_GETFL) | LibC::O_NONBLOCK)\n            end\n          end\n        {% end %}\n\n      return {client_fd, false} unless client_fd == -1\n      return if socket.closed?\n\n      if Errno.value == Errno::EAGAIN\n        wait_readable(socket, socket.@read_timeout) do\n          raise IO::TimeoutError.new(\"Accept timed out\")\n        end\n        return if socket.closed?\n      else\n        raise ::Socket::Error.from_errno(\"accept\")\n      end\n    end\n  end\n\n  def connect(socket : ::Socket, address : ::Socket::Addrinfo | ::Socket::Address, timeout : Time::Span?) : IO::Error?\n    loop do\n      ret = LibC.connect(socket.fd, address, address.size)\n      return unless ret == -1\n\n      case Errno.value\n      when Errno::EISCONN\n        return\n      when Errno::EINPROGRESS, Errno::EALREADY\n        wait_writable(socket, timeout) do\n          return IO::TimeoutError.new(\"Connect timed out\")\n        end\n      else\n        return ::Socket::ConnectError.from_errno(\"connect\")\n      end\n    end\n  end\n\n  def send_to(socket : ::Socket, slice : Bytes, address : ::Socket::Address) : Int32\n    bytes_sent = LibC.sendto(socket.fd, slice.to_unsafe.as(Void*), slice.size, 0, address, address.size)\n    raise ::Socket::Error.from_errno(\"Error sending datagram to #{address}\") if bytes_sent == -1\n    bytes_sent.to_i32\n  end\n\n  def receive_from(socket : ::Socket, slice : Bytes) : {Int32, ::Socket::Address}\n    sockaddr = Pointer(LibC::SockaddrStorage).malloc.as(LibC::Sockaddr*)\n\n    # initialize sockaddr with the initialized family of the socket\n    copy = sockaddr.value\n    copy.sa_family = socket.family\n    sockaddr.value = copy\n    addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrStorage))\n\n    loop do\n      size = LibC.recvfrom(socket.fd, slice, slice.size, 0, sockaddr, pointerof(addrlen))\n      if size == -1\n        if Errno.value == Errno::EAGAIN\n          wait_readable(socket, socket.@read_timeout)\n          check_open(socket)\n        else\n          raise IO::Error.from_errno(\"recvfrom\", target: socket)\n        end\n      else\n        return {size.to_i32, ::Socket::Address.from(sockaddr, addrlen)}\n      end\n    end\n  end\n\n  # Extension to support Kernel TLS in OpenSSL::BIO.\n  def recvmsg(socket : ::Socket, message : Pointer(LibC::Msghdr), flags : Int32) : Int32 | Errno\n    loop do\n      ret = LibC.recvmsg(socket.fd, message, flags)\n      return ret.to_i unless ret == -1\n\n      if Errno.value == Errno::EAGAIN\n        wait_readable(socket, socket.@read_timeout) { return Errno::ETIMEDOUT }\n        return Errno::EBADF if socket.closed?\n      else\n        return Errno.value\n      end\n    end\n  end\n\n  # Extension to support Kernel TLS in OpenSSL::BIO.\n  def sendmsg(socket : ::Socket, message : Pointer(LibC::Msghdr), flags : Int32) : Int32 | Errno\n    loop do\n      ret = LibC.sendmsg(socket.fd, message, flags)\n      return ret.to_i unless ret == -1\n\n      if Errno.value == Errno::EAGAIN\n        wait_writable(socket, socket.@write_timeout) { return Errno::ETIMEDOUT }\n        return Errno::EBADF if socket.closed?\n      else\n        return Errno.value\n      end\n    end\n  end\n\n  def shutdown(socket : ::Socket) : Nil\n    # perform cleanup before LibC.close. Using a file descriptor after it has\n    # been closed is never defined and can always lead to undefined results\n    resume_all(socket)\n  end\n\n  def close(socket : ::Socket) : Nil\n    socket.socket_close\n  end\n\n  protected def self.remove_impl(socket : ::Socket) : Nil\n    internal_remove(socket)\n  end\n\n  # internals: IO\n\n  private def evented_read(io, slice : Bytes, timeout : Time::Span?) : Int32\n    loop do\n      ret = LibC.read(io.fd, slice, slice.size)\n      if ret == -1 && Errno.value == Errno::EAGAIN\n        wait_readable(io, timeout)\n        check_open(io)\n      else\n        return ret.to_i\n      end\n    end\n  end\n\n  private def evented_write(io, slice : Bytes, timeout : Time::Span?) : Int32\n    loop do\n      ret = LibC.write(io.fd, slice, slice.size)\n      if ret == -1 && Errno.value == Errno::EAGAIN\n        wait_writable(io, timeout)\n        check_open(io)\n      else\n        return ret.to_i\n      end\n    end\n  end\n\n  private def resume_all(io)\n    return unless (index = io.__evloop_data).valid?\n\n    Polling.arena.free(index) do |pd|\n      pd.value.@readers.ready_all do |event|\n        pd.value.@event_loop.try(&.unsafe_resume_io(event) do |fiber|\n          {% if flag?(:execution_context) %}\n            fiber.execution_context.enqueue(fiber)\n          {% else %}\n            Crystal::Scheduler.enqueue(fiber)\n          {% end %}\n        end)\n      end\n\n      pd.value.@writers.ready_all do |event|\n        pd.value.@event_loop.try(&.unsafe_resume_io(event) do |fiber|\n          {% if flag?(:execution_context) %}\n            fiber.execution_context.enqueue(fiber)\n          {% else %}\n            Crystal::Scheduler.enqueue(fiber)\n          {% end %}\n        end)\n      end\n\n      pd.value.remove(io.fd)\n    end\n  end\n\n  private def self.internal_remove(io)\n    return unless (index = io.__evloop_data).valid?\n\n    Polling.arena.free(index) do |pd|\n      pd.value.remove(io.fd) { } # ignore system error\n    end\n  end\n\n  private def wait_readable(io, timeout = nil) : Nil\n    wait_readable(io, timeout) do\n      raise IO::TimeoutError.new(\"Read timed out\")\n    end\n  end\n\n  private def wait_writable(io, timeout = nil) : Nil\n    wait_writable(io, timeout) do\n      raise IO::TimeoutError.new(\"Write timed out\")\n    end\n  end\n\n  private def wait_readable(io, timeout = nil, &) : Nil\n    yield if wait(:io_read, io, timeout) do |pd, event|\n               # don't wait if the waiter has already been marked ready (see Waiters#add)\n               return unless pd.value.@readers.add(event)\n             end\n  end\n\n  private def wait_writable(io, timeout = nil, &) : Nil\n    yield if wait(:io_write, io, timeout) do |pd, event|\n               # don't wait if the waiter has already been marked ready (see Waiters#add)\n               return unless pd.value.@writers.add(event)\n             end\n  end\n\n  private def wait(type : Polling::Event::Type, io, timeout, &)\n    # prepare event (on the stack); we can't initialize it properly until we get\n    # the arena index below; we also can't use a nilable since `pointerof` would\n    # point to the union, not the event\n    event = uninitialized Event\n\n    # add the event to the waiting list; in case we can't access or allocate the\n    # poll descriptor into the arena, we merely return to let the caller handle\n    # the situation (maybe the IO got closed?)\n    if (index = io.__evloop_data).valid?\n      event = Event.new(type, Fiber.current, index, timeout)\n\n      return false unless Polling.arena.get?(index) do |pd|\n                            unless pd.value.owned_by?(self)\n                              pd.value.take_ownership(self, io.fd, index)\n                            end\n                            yield pd, pointerof(event)\n                          end\n    else\n      # OPTIMIZE: failing to allocate may be a simple conflict with 2 fibers\n      # starting to read or write on the same fd, we may want to detect any\n      # error situation instead of returning and retrying a syscall\n      return false unless Polling.arena.allocate_at?(io.fd) do |pd, index|\n                            # register the fd with the event loop (once), it should usually merely add\n                            # the fd to the current evloop but may \"transfer\" the ownership from\n                            # another event loop:\n                            io.__evloop_data = index\n                            pd.value.take_ownership(self, io.fd, index)\n\n                            event = Event.new(type, Fiber.current, index, timeout)\n                            yield pd, pointerof(event)\n                          end\n    end\n\n    if event.wake_at?\n      add_timer(pointerof(event))\n\n      Fiber.suspend\n\n      # no need to delete the timer: either it triggered which means it was\n      # dequeued, or `#unsafe_resume_io` was called to resume the IO and the\n      # timer got deleted from the timers before the fiber got reenqueued.\n      return event.timed_out?\n    end\n\n    Fiber.suspend\n    false\n  end\n\n  private def check_open(io : IO)\n    raise IO::Error.new(\"Closed stream\") if io.closed?\n  end\n\n  # internals: timers\n\n  protected def add_timer(event : Event*)\n    @timers_lock.sync do\n      is_next_ready = @timers.add(event)\n      system_set_timer(event.value.wake_at) if is_next_ready\n    end\n  end\n\n  protected def delete_timer(event : Event*) : Bool\n    @timers_lock.sync do\n      dequeued, was_next_ready = @timers.delete(event)\n      # update system timer if we deleted the next timer\n      system_set_timer(@timers.next_ready?) if was_next_ready\n      dequeued\n    end\n  end\n\n  # Helper to resume the fiber associated to an IO event and remove the event\n  # from timers if applicable. Returns true if the fiber has been enqueued.\n  #\n  # Thread unsafe: we must hold the poll descriptor waiter lock for the whole\n  # duration of the dequeue/resume_io otherwise we might conflict with timers\n  # trying to cancel an IO event.\n  protected def unsafe_resume_io(event : Event*, &) : Bool\n    # we only partially own the poll descriptor; thanks to the lock we know that\n    # another thread won't dequeue it, yet it may still be in the timers queue,\n    # which at worst may be waiting on the lock to be released, so event* can be\n    # dereferenced safely.\n\n    if !event.value.wake_at? || delete_timer(event)\n      # no timeout or we canceled it: we fully own the event\n      yield event.value.fiber\n      true\n    else\n      # failed to cancel the timeout so the timer owns the event (by rule)\n      false\n    end\n  end\n\n  # Process ready timers.\n  #\n  # Shall be called after processing IO events. IO events with a timeout that\n  # have succeeded shall already have been removed from `@timers` otherwise the\n  # fiber could be resumed twice!\n  private def process_timers(timer_triggered : Bool, &) : Nil\n    # collect ready timers before processing them —this is safe— to avoids a\n    # deadlock situation when another thread tries to process a ready IO event\n    # (in poll descriptor waiters) with a timeout (same event* in timers)\n    buffer = uninitialized StaticArray(Pointer(Event), 128)\n    size = 0\n\n    @timers_lock.sync do\n      @timers.dequeue_ready do |event|\n        buffer.to_unsafe[size] = event\n        break if (size &+= 1) == buffer.size\n      end\n\n      if size > 0 || timer_triggered\n        system_set_timer(@timers.next_ready?)\n      end\n    end\n\n    buffer.to_slice[0, size].each do |event|\n      process_timer(event) { |fiber| yield fiber }\n    end\n  end\n\n  private def process_timer(event : Event*, &)\n    # we dequeued the event from timers, and by rule we own it, so event* can\n    # safely be dereferenced:\n    fiber = event.value.fiber\n\n    case event.value.type\n    when .io_read?\n      # reached read timeout: cancel io event; by rule the timer always wins,\n      # even in case of conflict with #unsafe_resume_io we must resume the fiber\n      Polling.arena.get?(event.value.index) { |pd| pd.value.@readers.delete(event) }\n      event.value.timed_out!\n    when .io_write?\n      # reached write timeout: cancel io event; by rule the timer always wins,\n      # even in case of conflict with #unsafe_resume_io we must resume the fiber\n      Polling.arena.get?(event.value.index) { |pd| pd.value.@writers.delete(event) }\n      event.value.timed_out!\n    when .select_timeout?\n      # always dequeue the event but only enqueue the fiber if we win the\n      # atomic CAS\n      return unless select_action = fiber.timeout_select_action\n      fiber.timeout_select_action = nil\n      return unless select_action.time_expired?\n      fiber.@timeout_event.as(FiberEvent).clear\n    when .sleep?\n      event.value.timed_out!\n    else\n      raise RuntimeError.new(\"BUG: unexpected event in timers: #{event.value}%s\\n\")\n    end\n\n    yield fiber\n  end\n\n  # internals: system\n\n  # Process ready events and timers.\n  #\n  # The loop must always process ready events and timers before returning. When\n  # *blocking* is `true` the loop must wait for events to become ready (possibly\n  # indefinitely); when `false` the loop shall return immediately.\n  #\n  # The `PollDescriptor` of IO events can be retrieved using the *index*\n  # from the system event's user data.\n  private abstract def system_run(blocking : Bool, & : Fiber ->) : Nil\n\n  # Add *fd* to the polling system, setting *index* as user data.\n  protected abstract def system_add(fd : Int32, index : Arena::Index) : Nil\n\n  # Remove *fd* from the polling system. Must raise a `RuntimeError` on error.\n  #\n  # If *closing* is true, then it precedes a call to `close(2)`. Some\n  # implementations may take advantage of close doing the book keeping.\n  #\n  # If *closing* is false then the fd must be deleted from the polling system.\n  protected abstract def system_del(fd : Int32, closing = true) : Nil\n\n  # Identical to `#system_del` but yields on error.\n  protected abstract def system_del(fd : Int32, closing = true, &) : Nil\n\n  # Arm a timer to interrupt a run at *time*. Set to `nil` to disarm the timer.\n  private abstract def system_set_timer(time : Time::Instant?) : Nil\nend\n"
  },
  {
    "path": "src/crystal/event_loop/socket.cr",
    "content": "# This file is only required when sockets are used (`require \"crystal/event_loop/socket\"` in `src/crystal/system/socket.cr`)\n#\n# It fills `Crystal::EventLoop::Socket` with abstract defs.\n\nabstract class Crystal::EventLoop\n  module Socket\n    # Creates a new socket file descriptor or handle and returns it, along with\n    # whether the blocking flag has been set.\n    abstract def socket(family : ::Socket::Family, type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool?) : {::Socket::Handle, Bool}\n\n    # Creates a pair of UNIX socket file descriptors or handles and returns\n    # them, along with whether the blocking mode has been set.\n    abstract def socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol) : Tuple({::Socket::Handle, ::Socket::Handle}, Bool)\n\n    # Reads at least one byte from the socket into *slice*.\n    #\n    # Blocks the current fiber if no data is available for reading, continuing\n    # when available. Otherwise returns immediately.\n    #\n    # Returns the number of bytes read (up to `slice.size`).\n    # Returns 0 when the socket is closed and no data available.\n    #\n    # Use `#receive_from` for capturing the source address of a message.\n    abstract def read(socket : ::Socket, slice : Bytes) : Int32\n\n    # Blocks the current fiber until the socket is ready for read.\n    abstract def wait_readable(socket : ::Socket) : Nil\n\n    # Writes at least one byte from *slice* to the socket.\n    #\n    # Blocks the current fiber if the socket is not ready for writing,\n    # continuing when ready. Otherwise returns immediately.\n    #\n    # Returns the number of bytes written (up to `slice.size`).\n    #\n    # Use `#send_to` for sending a message to a specific target address.\n    abstract def write(socket : ::Socket, slice : Bytes) : Int32\n\n    # Blocks the current fiber until the socket is ready for write.\n    abstract def wait_writable(socket : ::Socket) : Nil\n\n    # Accepts an incoming TCP connection on the socket.\n    #\n    # Blocks the current fiber if no connection is waiting, continuing when one\n    # becomes available. Otherwise returns immediately.\n    #\n    # Returns a handle to the socket for the new connection.\n    abstract def accept(socket : ::Socket) : {::Socket::Handle, Bool}?\n\n    # Opens a connection on *socket* to the target *address*.\n    #\n    # Blocks the current fiber and continues when the connection is established.\n    #\n    # Returns `IO::Error` in case of an error. The caller is responsible for\n    # raising it as an exception if necessary.\n    abstract def connect(socket : ::Socket, address : ::Socket::Addrinfo | ::Socket::Address, timeout : ::Time::Span?) : IO::Error?\n\n    # Sends at least one byte from *slice* to the socket with a target address\n    # *address*.\n    #\n    # Blocks the current fiber if the socket is not ready for writing,\n    # continuing when ready. Otherwise returns immediately.\n    #\n    # Returns the number of bytes sent (up to `slice.size`).\n    abstract def send_to(socket : ::Socket, slice : Bytes, address : ::Socket::Address) : Int32\n\n    # Receives at least one byte from the socket into *slice*, capturing the\n    # source address.\n    #\n    # Blocks the current fiber if no data is available for reading, continuing\n    # when available. Otherwise returns immediately.\n    #\n    # Returns a tuple containing the number of bytes received (up to `slice.size`)\n    # and the source address.\n    abstract def receive_from(socket : ::Socket, slice : Bytes) : Tuple(Int32, ::Socket::Address)\n\n    # Internal shutdown of the socket. Called after the `Socket` has been marked\n    # closed but before calling `#close` to actually close the system socket fd\n    # or handle.\n    #\n    # Implementations shall resume all pending waiters and let them fail because\n    # the IO has been closed. They don't have to call the `shutdown` syscall.\n    abstract def shutdown(socket : ::Socket) : Nil\n\n    # Closes the system socket fd or handle.\n    abstract def close(socket : ::Socket) : Nil\n  end\n\n  # Removes the socket from the event loop. Can be used to free up memory\n  # resources associated with the socket, as well as removing the socket from\n  # kernel data structures.\n  #\n  # Called by `::Socket#finalize` before closing the socket. Errors shall be\n  # silently ignored.\n  def self.remove(socket : ::Socket) : Nil\n    backend_class.remove_impl(socket)\n  end\n\n  # Actual implementation for `.remove`. Must be implemented on a subclass of\n  # `Crystal::EventLoop` when needed.\n  protected def self.remove_impl(socket : ::Socket) : Nil\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/timers.cr",
    "content": "require \"crystal/pointer_pairing_heap\"\n\n# List of `Pointer(T)` to `T` structs.\n#\n# Internally wraps a `PointerPairingHeap(T)` and thus requires that `T`\n# implements `PointerPairingHeap::Node`.\n#\n# Thread unsafe: parallel accesses must be protected!\n#\n# NOTE: this is a struct because it only wraps a const pointer to an object\n# allocated in the heap.\nstruct Crystal::EventLoop::Timers(T)\n  def initialize\n    @heap = PointerPairingHeap(T).new\n  end\n\n  def empty? : Bool\n    @heap.empty?\n  end\n\n  # Returns the time of the next ready timer (if any).\n  def next_ready? : Time::Instant?\n    if event = @heap.first?\n      event.value.wake_at\n    end\n  end\n\n  # Dequeues and yields each ready timer (their `#wake_at` is lower than\n  # `Crystal::System::Time.instant`) from the oldest to the most recent (i.e. time\n  # ascending).\n  def dequeue_ready(& : Pointer(T) -> Nil) : Nil\n    now = Crystal::System::Time.instant\n\n    while event = @heap.first?\n      break if event.value.wake_at > now\n      @heap.shift?\n      yield event\n    end\n  end\n\n  # Add a new timer into the list. Returns true if it is the next ready timer.\n  def add(event : Pointer(T)) : Bool\n    @heap.add(event)\n    @heap.first? == event\n  end\n\n  # Remove a timer from the list. Returns a tuple(dequeued, was_next_ready) of\n  # booleans. The first bool tells whether the event was dequeued, in which case\n  # the second one tells if it was the next ready event.\n  def delete(event : Pointer(T)) : {Bool, Bool}\n    if @heap.first? == event\n      @heap.shift?\n      {true, true}\n    elsif event.value.heap_previous?\n      @heap.delete(event)\n      {true, false}\n    else\n      {false, false}\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop/wasi.cr",
    "content": "# :nodoc:\nclass Crystal::EventLoop::Wasi < Crystal::EventLoop\n  def self.default_file_blocking?\n    false\n  end\n\n  def self.default_socket_blocking?\n    false\n  end\n\n  def initialize(parallelism : Int32)\n  end\n\n  # Runs the event loop.\n  def run(blocking : Bool) : Bool\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop.run\")\n  end\n\n  def interrupt : Nil\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop.interrupt\")\n  end\n\n  def sleep(duration : ::Time::Span) : Nil\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop.sleep\")\n  end\n\n  # Creates a timeout_event.\n  def create_timeout_event(fiber) : Crystal::EventLoop::Event\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop.create_timeout_event\")\n  end\n\n  # Creates a write event for a file descriptor.\n  def create_fd_write_event(io : IO::Evented, edge_triggered : Bool = false) : Crystal::EventLoop::Event\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop.create_fd_write_event\")\n  end\n\n  # Creates a read event for a file descriptor.\n  def create_fd_read_event(io : IO::Evented, edge_triggered : Bool = false) : Crystal::EventLoop::Event\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop.create_fd_read_event\")\n  end\n\n  def pipe(read_blocking : Bool?, write_blocking : Bool?) : {IO::FileDescriptor, IO::FileDescriptor}\n    raise NotImplementedError.new(\"Crystal::EventLoop::Wasi#pipe\")\n  end\n\n  def open(path : String, flags : Int32, permissions : File::Permissions, blocking : Bool?) : {System::FileDescriptor::Handle, Bool} | Errno | WinError\n    raise NotImplementedError.new(\"Crystal::Wasi::EventLoop#open\")\n  end\n\n  def read(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n    evented_read(file_descriptor, \"Error reading file_descriptor\") do\n      LibC.read(file_descriptor.fd, slice, slice.size).tap do |return_code|\n        if return_code == -1 && Errno.value == Errno::EBADF\n          raise IO::Error.new \"File not open for reading\", target: file_descriptor\n        end\n      end\n    end\n  end\n\n  def wait_readable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.evented_wait_readable(raise_if_closed: false) do\n      raise IO::TimeoutError.new(\"Read timed out\")\n    end\n  end\n\n  def write(file_descriptor : Crystal::System::FileDescriptor, slice : Bytes) : Int32\n    evented_write(file_descriptor, \"Error writing file_descriptor\") do\n      LibC.write(file_descriptor.fd, slice, slice.size).tap do |return_code|\n        if return_code == -1 && Errno.value == Errno::EBADF\n          raise IO::Error.new \"File not open for writing\", target: file_descriptor\n        end\n      end\n    end\n  end\n\n  def wait_writable(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.evented_wait_writable(raise_if_closed: false) do\n      raise IO::TimeoutError.new(\"Write timed out\")\n    end\n  end\n\n  def reopened(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    raise NotImplementedError.new(\"Crystal::EventLoop#reopened(FileDescriptor)\")\n  end\n\n  def shutdown(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.evented_close\n  end\n\n  def close(file_descriptor : Crystal::System::FileDescriptor) : Nil\n    file_descriptor.file_descriptor_close\n  end\n\n  def socket(family : ::Socket::Family, type : ::Socket::Type, protocol : ::Socket::Protocol) : {::Socket::Handle, Bool}\n    raise NotImplementedError.new(\"Crystal::EventLoop::Wasi#socket\")\n  end\n\n  def socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool) : {Handle, Handle}\n    raise NotImplementedError.new(\"Crystal::EventLoop::Wasi#socketpair\")\n  end\n\n  def read(socket : ::Socket, slice : Bytes) : Int32\n    evented_read(socket, \"Error reading socket\") do\n      LibC.recv(socket.fd, slice, slice.size, 0).to_i32\n    end\n  end\n\n  def wait_readable(socket : ::Socket) : Nil\n    socket.evented_wait_readable do\n      raise IO::TimeoutError.new(\"Read timed out\")\n    end\n  end\n\n  def write(socket : ::Socket, slice : Bytes) : Int32\n    evented_write(socket, \"Error writing to socket\") do\n      LibC.send(socket.fd, slice, slice.size, 0)\n    end\n  end\n\n  def wait_writable(socket : ::Socket) : Nil\n    socket.evented_wait_writable do\n      raise IO::TimeoutError.new(\"Write timed out\")\n    end\n  end\n\n  def receive_from(socket : ::Socket, slice : Bytes) : Tuple(Int32, ::Socket::Address)\n    raise NotImplementedError.new \"Crystal::Wasi::EventLoop#receive_from\"\n  end\n\n  def send_to(socket : ::Socket, slice : Bytes, address : ::Socket::Address) : Int32\n    raise NotImplementedError.new \"Crystal::Wasi::EventLoop#send_to\"\n  end\n\n  def connect(socket : ::Socket, address : ::Socket::Addrinfo | ::Socket::Address, timeout : ::Time::Span | ::Nil) : IO::Error?\n    raise NotImplementedError.new \"Crystal::Wasi::EventLoop#connect\"\n  end\n\n  def accept(socket : ::Socket) : ::Socket::Handle?\n    raise NotImplementedError.new \"Crystal::Wasi::EventLoop#accept\"\n  end\n\n  def shutdown(socket : ::Socket) : Nil\n    socket.evented_close\n  end\n\n  def close(socket : ::Socket) : Nil\n    socket.socket_close\n  end\n\n  def evented_read(target, errno_msg : String, &) : Int32\n    loop do\n      bytes_read = yield\n      if bytes_read != -1\n        # `to_i32` is acceptable because `Slice#size` is an Int32\n        return bytes_read.to_i32\n      end\n\n      if Errno.value == Errno::EAGAIN\n        target.evented_wait_readable do\n          raise IO::TimeoutError.new(\"Read timed out\")\n        end\n      else\n        raise IO::Error.from_errno(errno_msg, target: target)\n      end\n    end\n  ensure\n    target.evented_resume_pending_readers\n  end\n\n  def evented_write(target, errno_msg : String, &) : Int32\n    loop do\n      bytes_written = yield\n      if bytes_written != -1\n        return bytes_written.to_i32\n      end\n\n      if Errno.value == Errno::EAGAIN\n        target.evented_wait_writable do\n          raise IO::TimeoutError.new(\"Write timed out\")\n        end\n      else\n        raise IO::Error.from_errno(errno_msg, target: target)\n      end\n    end\n  ensure\n    target.evented_resume_pending_writers\n  end\nend\n\nstruct Crystal::EventLoop::Wasi::Event\n  include Crystal::EventLoop::Event\n\n  def add(timeout : Time::Span) : Nil\n  end\n\n  def add(timeout : Nil) : Nil\n  end\n\n  def free : Nil\n  end\n\n  def delete\n  end\nend\n"
  },
  {
    "path": "src/crystal/event_loop.cr",
    "content": "abstract class Crystal::EventLoop\n  def self.backend_class\n    {% if flag?(:wasi) %}\n      Crystal::EventLoop::Wasi\n    {% elsif flag?(:unix) %}\n      # TODO: enable more targets by default (need manual tests or fixes)\n      {% if flag?(\"evloop=libevent\") %}\n        Crystal::EventLoop::LibEvent\n      {% elsif flag?(\"evloop=epoll\") || flag?(:android) || flag?(:linux) %}\n        Crystal::EventLoop::Epoll\n      {% elsif flag?(\"evloop=kqueue\") || flag?(:darwin) || flag?(:freebsd) %}\n        Crystal::EventLoop::Kqueue\n      {% else %}\n        Crystal::EventLoop::LibEvent\n      {% end %}\n    {% elsif flag?(:win32) %}\n      Crystal::EventLoop::IOCP\n    {% else %}\n      {% raise \"Event loop not supported\" %}\n    {% end %}\n  end\n\n  # Creates an event loop instance.\n  #\n  # The *parallelism* arg is informational. It reports how many schedulers are\n  # expected to register with the event loop instance. Because schedulers are\n  # dynamically started and execution contexts can be resized, more or less\n  # schedulers may really register in practice.\n  def self.create(parallelism : Int32 = 1) : self\n    backend_class.new(parallelism)\n  end\n\n  def self.default_file_blocking? : Bool\n    backend_class.default_file_blocking?\n  end\n\n  def self.default_socket_blocking? : Bool\n    backend_class.default_socket_blocking?\n  end\n\n  @[AlwaysInline]\n  def self.current : self\n    {% if flag?(:execution_context) %}\n      Fiber::ExecutionContext.current.event_loop\n    {% else %}\n      Crystal::Scheduler.event_loop\n    {% end %}\n  end\n\n  @[AlwaysInline]\n  def self.current? : self | Nil\n    {% if flag?(:execution_context) %}\n      Fiber::ExecutionContext.current.event_loop\n    {% else %}\n      Crystal::Scheduler.event_loop?\n    {% end %}\n  end\n\n  # Runs the loop.\n  #\n  # Returns immediately if events are activable. Set `blocking` to false to\n  # return immediately if there are no activable events; set it to true to wait\n  # for activable events, which will block the current thread until then.\n  #\n  # Returns `true` on normal returns (e.g. has activated events, has pending\n  # events but blocking was false) and `false` when there are no registered\n  # events.\n  abstract def run(blocking : Bool) : Bool\n\n  {% if flag?(:execution_context) %}\n    # Same as `#run` but collects runnable fibers into *queue* instead of\n    # enqueueing in parallel, so the caller is responsible and in control for\n    # when and how the fibers will be enqueued.\n    abstract def run(queue : Fiber::List*, blocking : Bool) : Nil\n\n    # Tries to lock the event loop and yields if the lock was acquired. Must\n    # unlock before returning. Returns true if the lock was acquired, false\n    # otherwise.\n    #\n    # Only needed when there should be a single scheduler running the event loop\n    # at any time (e.g. epoll, kqueue and IOCP). Can be a NOOP that always\n    # yields and returns true (io_uring).\n    abstract def lock?(&) : Bool\n\n    # Same as `#interrupt` but returns true if a running event loop has likely\n    # been interrupted, and false otherwise.\n    abstract def interrupt? : Bool\n\n    # Called once before *scheduler* is started. Optional hook.\n    def register(scheduler : Fiber::ExecutionContext::Scheduler, index : Int32) : Nil\n    end\n\n    # Called once before *scheduler* is shut down. Optional hook.\n    def unregister(scheduler : Fiber::ExecutionContext::Scheduler) : Nil\n    end\n  {% end %}\n\n  # Tells a blocking run loop to no longer wait for events to activate. It may\n  # for example enqueue a NOOP event with an immediate (or past) timeout. Having\n  # activated an event, the loop shall return, allowing the blocked thread to\n  # continue.\n  #\n  # Should be a NOOP when the loop isn't running or is running in a nonblocking\n  # mode.\n  #\n  # NOTE: we assume that multiple threads won't run the event loop at the same\n  #       time in parallel, but this assumption may change in the future!\n  abstract def interrupt : Nil\n\n  # Suspend the current fiber for *duration*.\n  abstract def sleep(duration : Time::Span) : Nil\n\n  # Create a new resume event for a fiber.\n  #\n  # NOTE: optional.\n  def create_resume_event(fiber : Fiber) : Event\n    raise NotImplementedError.new(\"#{self.class.name}#create_resume_event(fiber)\")\n  end\n\n  # Creates a timeout_event.\n  abstract def create_timeout_event(fiber : Fiber) : Event\n\n  module Event\n    # Frees the event.\n    abstract def free : Nil\n\n    # Adds a new timeout to this event.\n    abstract def add(timeout : Time::Span) : Nil\n  end\nend\n\nrequire \"./event_loop/file_descriptor\"\n\nabstract class Crystal::EventLoop\n  # The FileDescriptor interface is always needed, so we include it right in\n  # the main interface.\n  include FileDescriptor\n\n  # The socket module is empty by default and filled with abstract defs when\n  # crystal/system/socket.cr is required.\n  module Socket\n  end\n\n  include Socket\nend\n\n{% if flag?(:wasi) %}\n  require \"./event_loop/wasi\"\n{% elsif flag?(:unix) %}\n  {% if flag?(\"evloop=libevent\") %}\n    require \"./event_loop/libevent\"\n  {% elsif flag?(\"evloop=epoll\") || flag?(:android) || flag?(:linux) %}\n    require \"./event_loop/epoll\"\n  {% elsif flag?(\"evloop=kqueue\") || flag?(:darwin) || flag?(:freebsd) %}\n    require \"./event_loop/kqueue\"\n  {% else %}\n    require \"./event_loop/libevent\"\n  {% end %}\n{% elsif flag?(:win32) %}\n  require \"./event_loop/iocp\"\n{% else %}\n  {% raise \"Event loop not supported\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/fd_lock.cr",
    "content": "require \"fiber/pointer_linked_list_node\"\n\n# The general design is influenced by fdMutex in Go (LICENSE: BSD 3-Clause,\n# Copyright Google):\n# https://github.com/golang/go/blob/go1.25.1/src/internal/poll/fd_mutex.go\n#\n# The internal details (spinlock, designated waker) of the locks are heavily\n# influenced by the nsync library (LICENSE: Apache-2.0, Copyright Google):\n# https://github.com/google/nsync\n\n# :nodoc:\n#\n# Tracks active references over a system file descriptor (fd) and serializes\n# reads and writes.\n#\n# Every read on the fd must lock read.\n# Every write on the fd must lock write.\n# Other operations (fcntl, setsockopt, ...) must acquire a shared reference.\n#\n# The read and write locks are exclusive but distinct: each can only be acquired\n# once, but both can be acquire at the same time. Shared references can happen\n# at any time. Both locks also acquire a shared reference while the lock is\n# acquired.\n#\n# The fdlock can be closed at any time (at which point we can't lock for read,\n# write or acquire a shared reference anymore), but the actual system close will\n# wait until there are no more references left. This avoids potential races when\n# a thread might try to read a fd that has been closed... and has been reused by\n# the OS for example.\n#\n# NOTE: since only one attempt to read (or write) can go through, it avoids\n# situations where multiple fibers are waiting, then the first fiber is resumed but\n# doesn't consume/fill everything, and... won't resume the next fiber! The lock\n# will always resume a waiting fiber (if any).\n#\n# Lock concepts\n#\n# Spinlock: slow-path for lock/unlock will spin until it acquires the spinlock\n# bit to add/remove waiters; the CPU is relaxed between each attempt.\n#\n# Designated waker: set on unlock to report that a waiter has been scheduler and\n# there's no need to wake another one. It's unset when a waiter acquires or\n# fails to acquire and adds itself again as a waiter. This leads to an\n# impressive performance boost when the lock is contended.\nstruct Crystal::FdLock\n  CLOSED = 1_u32 << 0 # the fdlock has been closed\n  RLOCK  = 1_u32 << 1 # reader lock\n  RWAIT  = 1_u32 << 2 # reader wait bit (at least one reader)\n  RSPIN  = 1_u32 << 3 # reader spinlock (protects @readers)\n  RWAKER = 1_u32 << 4 # reader designated waker (a reader is being awoken)\n  WLOCK  = 1_u32 << 5 # writer lock\n  WWAIT  = 1_u32 << 6 # writer wait bit (at least one writer)\n  WSPIN  = 1_u32 << 7 # writer spinlock (protects @writers)\n  WWAKER = 1_u32 << 8 # writer designated waker (a writer is being awoken)\n  REF    = 1_u32 << 9 # the reference counter increment\n  MASK   = ~(REF - 1) # mask for the reference counter\n\n  @m = Atomic(UInt32).new(0_u32)\n  @closing : Fiber?\n  @readers = PointerLinkedList(Fiber::PointerLinkedListNode).new\n  @writers = PointerLinkedList(Fiber::PointerLinkedListNode).new\n\n  # Locks for read and increments the references by one for the duration of the\n  # block. Raises if the fdlock is closed while trying to acquire the lock.\n  def read(& : -> F) : F forall F\n    m, success = @m.compare_and_set(0_u32, RLOCK + REF, :acquire, :relaxed)\n    lock_slow(RLOCK, RWAIT, RSPIN, RWAKER, pointerof(@readers)) unless success\n\n    begin\n      yield\n    ensure\n      m, success = @m.compare_and_set(RLOCK + REF, 0_u32, :release, :relaxed)\n      m = unlock_slow(RLOCK, RWAIT, RSPIN, RWAKER, pointerof(@readers)) unless success\n      handle_last_ref(m)\n    end\n  end\n\n  # Locks for write and increments the references by one for the duration of the\n  # block. Raises if the fdlock is closed while trying to acquire the lock.\n  def write(& : -> F) : F forall F\n    m, success = @m.compare_and_set(0_u32, WLOCK + REF, :acquire, :relaxed)\n    lock_slow(WLOCK, WWAIT, WSPIN, WWAKER, pointerof(@writers)) unless success\n\n    begin\n      yield\n    ensure\n      m, success = @m.compare_and_set(WLOCK + REF, 0_u32, :release, :relaxed)\n      m = unlock_slow(WLOCK, WWAIT, WSPIN, WWAKER, pointerof(@writers)) unless success\n      handle_last_ref(m)\n    end\n  end\n\n  @[NoInline]\n  private def lock_slow(xlock, xwait, xspin, xwaker, waiters)\n    waiter = Fiber::PointerLinkedListNode.new(Fiber.current)\n    attempts = 0\n    clear = 0_u32\n\n    while true\n      m = @m.get(:relaxed)\n\n      if (m & CLOSED) == CLOSED\n        # abort\n        raise IO::Error.new(\"Closed\")\n      elsif (m & xlock) == 0_u32\n        # acquire the lock + increment ref\n        m, success = @m.compare_and_set(m, ((m | xlock) + REF) & ~clear, :acquire, :relaxed)\n        return if success\n      elsif (m & xspin) == 0_u32\n        # acquire spinlock + forward declare pending waiter\n        m, success = @m.compare_and_set(m, (m | xspin | xwait) & ~clear, :acquire, :relaxed)\n        if success\n          # new waiters are added to the tail, while woken waiters that failed\n          # to lock again are added to the head to give them some edge\n          if (clear & xwaker) == 0_u32\n            waiters.value.push(pointerof(waiter))\n          else\n            waiters.value.unshift(pointerof(waiter))\n          end\n\n          # release spinlock before suspending the fiber\n          @m.and(~xspin, :release)\n\n          Fiber.suspend\n\n          # the designated waker has woken: clear the flag\n          clear |= xwaker\n        end\n      end\n\n      attempts = Thread.delay(attempts)\n    end\n  end\n\n  @[NoInline]\n  private def unlock_slow(xlock, xwait, xspin, xwaker, waiters)\n    attempts = 0\n\n    while true\n      m = @m.get(:relaxed)\n\n      if (m & CLOSED) == CLOSED\n        # decrement ref and abort\n        m = @m.sub(REF, :relaxed)\n        return m\n      elsif (m & xwait) == 0_u32 || (m & xwaker) != 0_u32\n        # no waiter, or there is a designated waker (no need to wake another\n        # one): unlock & decrement ref\n        m, success = @m.compare_and_set(m, (m & ~xlock) - REF, :release, :relaxed)\n        return m if success\n      elsif (m & xspin) == 0_u32\n        # there is a waiter and no designated waker: acquire spinlock + declare\n        # a designated waker + release lock & decrement ref early\n        m, success = @m.compare_and_set(m, ((m | xspin | xwaker) & ~xlock) - REF, :acquire_release, :relaxed)\n        if success\n          waiter = waiters.value.shift?\n\n          # clear flags and release spinlock\n          clear = xspin\n          clear |= xwaker unless waiter          # no designated waker\n          clear |= xwait if waiters.value.empty? # no more waiters\n          @m.and(~clear, :release)\n\n          waiter.value.enqueue if waiter\n\n          # return the m that decremented ref (for #handle_last_ref)\n          return m\n        end\n      end\n\n      attempts = Thread.delay(attempts)\n    end\n  end\n\n  # Borrows a reference for the duration of the block. Raises if the fdlock is\n  # closed while trying to borrow.\n  def reference(& : -> F) : F forall F\n    m, success = @m.compare_and_set(0_u32, REF, :acquire, :relaxed)\n    increment_slow(m) unless success\n\n    begin\n      yield\n    ensure\n      m = @m.sub(REF, :release)\n      handle_last_ref(m)\n    end\n  end\n\n  private def increment_slow(m)\n    while true\n      if (m & CLOSED) == CLOSED\n        raise IO::Error.new(\"Closed\")\n      end\n      m, success = @m.compare_and_set(m, m + REF, :acquire, :relaxed)\n      break if success\n    end\n  end\n\n  private def handle_last_ref(m)\n    return unless (m & CLOSED) == CLOSED # is closed?\n    return unless (m & MASK) == REF      # was the last ref?\n\n    # the last ref after close is responsible to resume the closing fiber\n    if fiber = @closing\n      fiber.enqueue\n    else\n      raise NilAssertionError.new(\"BUG: expected a closing fiber to resume.\")\n    end\n  end\n\n  # Closes the fdlock. Wakes waiting readers and writers. Blocks for as long as\n  # there are references.\n  #\n  # The *callback* block must cancel any external waiters (e.g. pending evloop\n  # reads or writes).\n  #\n  # Returns true if the fdlock has been closed: no fiber can lock for read,\n  # write or acquire a reference anymore, the calling fiber fully owns the fd\n  # and can safely close it.\n  #\n  # Returns false if the fdlock has already been closed: the calling fiber\n  # doesn't own the fd and musn't close it, as there might still be active\n  # references and another fiber will close anyway.\n  def try_close?(&callback : ->) : Bool\n    attempts = 0\n\n    # close + increment ref + acquire both spinlocks so we own both @readers and\n    # @writers; parallel attempts to acquire a spinlock will fail, notice that\n    # the lock is closed, and abort\n    while true\n      m = @m.get(:relaxed)\n\n      if (m & CLOSED) == CLOSED\n        # already closed: abort\n        return false\n      end\n\n      m, success = @m.compare_and_set(m, (m + REF) | CLOSED | RSPIN | WSPIN, :acquire, :relaxed)\n      break if success\n\n      attempts = Thread.delay(attempts)\n    end\n\n    # set the current fiber as the closing fiber (to be resumed by the last ref)\n    @closing = Fiber.current\n\n    # resume waiters so they can fail (the fdlock is closed); this is safe\n    # because we acquired the spinlocks above:\n    @readers.consume_each(&.value.enqueue)\n    @writers.consume_each(&.value.enqueue)\n\n    # decrement the last ref\n    m = @m.sub(REF, :release)\n\n    begin\n      yield\n    ensure\n      # wait for the last ref... unless we're the last ref!\n      Fiber.suspend unless (m & MASK) == REF\n    end\n\n    @closing = nil\n    true\n  end\n\n  # Resets the fdlock back to its pristine state so it can be used again.\n  # Assumes the caller owns the fdlock. This is required by\n  # `TCPSocket#initialize`.\n  def reset : Nil\n    @m.lazy_set(0_u32)\n    @closing = nil\n  end\n\n  def closed? : Bool\n    (@m.get(:relaxed) & CLOSED) == CLOSED\n  end\nend\n"
  },
  {
    "path": "src/crystal/hasher.cr",
    "content": "# :nodoc:\nstruct Crystal::Hasher\n  # Implementation of a Hasher to compute a fast and safe hash\n  # value for primitive and basic Crystal objects. All other\n  # hashes are computed based on these.\n  #\n  # The algorithm bases on https://github.com/funny-falcon/funny_hash\n  #\n  # It is two multiply-rotate 64bit hash functions, combined\n  # within finalizer.\n  #\n  # Both hash functions combines previous state with a block value\n  # before multiplication. One function multiplies new state\n  # as is (and then rotates state), other multiplies new state\n  # already rotated by 32 bits.\n  #\n  # This way algorithm ensures that every block bit affects at\n  # least 1 bit of every state, and certainly many bits of some\n  # state. So effect of this bit could not be easily canceled\n  # with following blocks. (Cause next blocks have to cancel\n  # bits on non-intersecting positions in both states).\n  # Rotation by 32bit with multiplication also provides good\n  # inter-block avalanche.\n  #\n  # Finalizer performs murmur-like finalization on both functions,\n  # and then combines them with addition. It greatly reduce\n  # possibility of state deduction.\n  #\n  # Note, it provides good protection from HashDos if and only if:\n  # - seed is securely random and not exposed to attacker,\n  # - hash result is also not exposed to attacker in a way other\n  #   than effect of using it in Hash implementation.\n  # Do not output calculated hash value to user's console/form/\n  # html/api response, etc. Use some from digest package instead.\n\n  # Based on https://github.com/python/cpython/blob/371c970/Python/pyhash.c#L31\n  #\n  # For numeric types, the hash of a number x is based on the reduction\n  # of x modulo the Mersen Prime P = 2**HASH_BITS - 1.  It's designed\n  # so that hash(x) == hash(y) whenever x and y are numerically equal,\n  # even if x and y have different types.\n  # A quick summary of the hashing strategy:\n  # (1) First define the 'reduction of x modulo P' for any rational\n  # number x; this is a standard extension of the usual notion of\n  # reduction modulo P for integers.  If x == p/q (written in lowest\n  # terms), the reduction is interpreted as the reduction of p times\n  # the inverse of the reduction of q, all modulo P; if q is exactly\n  # divisible by P then define the reduction to be infinity.  So we've\n  # got a well-defined map\n  #   reduce : { rational numbers } -> { 0, 1, 2, ..., P-1, infinity }.\n  # (2) Now for a rational number x, define hash(x) by:\n  #   reduce(x)   if x >= 0\n  #   -reduce(-x) if x < 0\n  # If the result of the reduction is infinity (this is impossible for\n  # integers, floats and Decimals) then use the predefined hash value\n  # HASH_INF_PLUS for x >= 0, or HASH_INF_MINUS for x < 0, instead.\n  # HASH_INF_PLUS, HASH_INF_MINUS and HASH_NAN are also used for the\n  # hashes of float and Decimal infinities and nans.\n  # A selling point for the above strategy is that it makes it possible\n  # to compute hashes of decimal and binary floating-point numbers\n  # efficiently, even if the exponent of the binary or decimal number\n  # is large.  The key point is that\n  #   reduce(x * y) == reduce(x) * reduce(y) (modulo HASH_MODULUS)\n  # provided that {reduce(x), reduce(y)} != {0, infinity}.  The reduction of a\n  # binary or decimal float is never infinity, since the denominator is a power\n  # of 2 (for binary) or a divisor of a power of 10 (for decimal).  So we have,\n  # for nonnegative x,\n  #   reduce(x * 2**e) == reduce(x) * reduce(2**e) % HASH_MODULUS\n  #   reduce(x * 10**e) == reduce(x) * reduce(10**e) % HASH_MODULUS\n  # and reduce(10**e) can be computed efficiently by the usual modular\n  # exponentiation algorithm.  For reduce(2**e) it's even better: since\n  # P is of the form 2**n-1, reduce(2**e) is 2**(e mod n), and multiplication\n  # by 2**(e mod n) modulo 2**n-1 just amounts to a rotation of bits.\n\n  private HASH_BITS    = 61\n  private HASH_MODULUS = (1_i64 << HASH_BITS) - 1\n\n  HASH_NAN       =      0_u64\n  HASH_INF_PLUS  = 314159_u64\n  HASH_INF_MINUS = (-314159_i64).to_u64!\n\n  @@seed = uninitialized UInt64[2]\n  Crystal::System::Random.random_bytes(@@seed.to_slice.to_unsafe_bytes)\n\n  def initialize(@a : UInt64 = @@seed[0], @b : UInt64 = @@seed[1])\n  end\n\n  private C1 = 0xacd5ad43274593b9_u64\n  private C2 = 0x6956abd6ed268a3d_u64\n\n  private def permute(v : UInt64)\n    @a = (@a ^ v).rotate_left(32) &* C1\n    @b = (@b.rotate_left(32) ^ v) &* C2\n    self\n  end\n\n  def result\n    a, b = @a, @b\n    a ^= (a >> 23) ^ (a >> 40)\n    b ^= (b >> 23) ^ (b >> 40)\n    a &*= C1\n    b &*= C2\n    a ^= a >> 32\n    b ^= b >> 32\n    a &+ b\n  end\n\n  def self.reduce_num(value : Int8 | Int16 | Int32)\n    value.to_u64!\n  end\n\n  def self.reduce_num(value : UInt8 | UInt16 | UInt32)\n    value.to_u64\n  end\n\n  def self.reduce_num(value : Int::Unsigned)\n    value.remainder(HASH_MODULUS).to_u64\n  end\n\n  def self.reduce_num(value : Int)\n    # The result of `remainder(HASH_MODULUS)` is a 64-bit integer,\n    # and thus guaranteed to fit into `UInt64`\n    value.remainder(HASH_MODULUS).to_u64!\n  end\n\n  # This function is for reference implementation, and it is used for `BigFloat`.\n  # For `Float64` and `Float32` all supported architectures allows more effective\n  # bitwise calculation.\n  # Arguments `frac` and `exp` are result of equivalent `Math.frexp`, though\n  # for `BigFloat` custom calculation used for more precision.\n  private def self.float_normalize_reference(value, frac, exp)\n    if value < 0\n      frac = -frac\n    end\n    # process 28 bits at a time;  this should work well both for binary\n    # and hexadecimal floating point.\n    x = 0_i64\n    while frac > 0\n      x = ((x << 28) & HASH_MODULUS) | x >> (HASH_BITS - 28)\n      frac *= 268435456.0 # 2**28\n      exp -= 28\n      y = frac.to_u32 # pull out integer part\n      frac -= y\n      x += y\n      x -= HASH_MODULUS if x >= HASH_MODULUS\n    end\n    {x, exp}\n  end\n\n  private def self.float_normalize_wrap(value, &)\n    return HASH_NAN if value.nan?\n    if value.infinite?\n      return value > 0 ? HASH_INF_PLUS : HASH_INF_MINUS\n    end\n\n    x, exp = yield value\n\n    # adjust for the exponent;  first reduce it modulo HASH_BITS\n    exp = exp >= 0 ? exp % HASH_BITS : HASH_BITS - 1 - ((-1 - exp) % HASH_BITS)\n    x = ((x << exp) & HASH_MODULUS) | x >> (HASH_BITS - exp)\n\n    (x * (value < 0 ? -1 : 1)).to_u64!\n  end\n\n  def self.reduce_num(value : Float32)\n    float_normalize_wrap(value) do |value|\n      # This optimized version works on every architecture where endianness\n      # of Float32 and Int32 matches and float is IEEE754. All supported\n      # architectures fall into this category.\n      unsafe_int = value.unsafe_as(Int32)\n      exp = (((unsafe_int >> 23) & 0xff) - 127)\n      mantissa = unsafe_int & ((1 << 23) - 1)\n      if exp > -127\n        exp -= 23\n        mantissa |= 1 << 23\n      else\n        # subnormals\n        exp -= 22\n      end\n      {mantissa.to_i64, exp}\n    end\n  end\n\n  def self.reduce_num(value : Float64)\n    float_normalize_wrap(value) do |value|\n      # This optimized version works on every architecture where endianness\n      # of Float64 and Int64 matches and float is IEEE754. All supported\n      # architectures fall into this category.\n      unsafe_int = value.unsafe_as(Int64)\n      exp = (((unsafe_int >> 52) & 0x7ff) - 1023)\n      mantissa = unsafe_int & ((1_u64 << 52) - 1)\n      if exp > -1023\n        exp -= 52\n        mantissa |= 1_u64 << 52\n      else\n        # subnormals\n        exp -= 51\n      end\n\n      {mantissa.to_i64, exp}\n    end\n  end\n\n  def self.reduce_num(value : Float)\n    float_normalize_wrap(value) do |value|\n      frac, exp = Math.frexp value\n      float_normalize_reference(value, frac, exp)\n    end\n  end\n\n  def nil\n    @a &+= @b\n    @b &+= 1\n    self\n  end\n\n  def bool(value)\n    (value ? 1 : 0).hash(self)\n  end\n\n  def number(value : Number)\n    permute(Hasher.reduce_num(value))\n  end\n\n  def char(value)\n    value.ord.hash(self)\n  end\n\n  def enum(value)\n    value.value.hash(self)\n  end\n\n  def symbol(value)\n    value.to_i.hash(self)\n  end\n\n  def reference(value)\n    permute(value.object_id.to_u64)\n  end\n\n  def string(value)\n    bytes(value.to_slice)\n  end\n\n  private def read_u24(ptr, rest)\n    ptr[0].to_u64 | (ptr[rest // 2].to_u64 << 8) | (ptr[rest - 1].to_u64 << 16)\n  end\n\n  private def read_u32(ptr)\n    # force correct unaligned read\n    t4 = uninitialized UInt32\n    pointerof(t4).as(UInt8*).copy_from(ptr, 4)\n    t4.to_u64\n  end\n\n  private def read_u64(ptr)\n    # force correct unaligned read\n    t8 = uninitialized UInt64\n    pointerof(t8).as(UInt8*).copy_from(ptr, 8)\n    t8\n  end\n\n  def bytes(value : Bytes)\n    size = value.size\n    ptr = value.to_unsafe\n    if size <= 0\n      last = 0_u64\n    elsif size <= 3\n      last = read_u24(ptr, size)\n    elsif size <= 7\n      last = read_u32(ptr)\n      last |= read_u32(ptr + (size & 3)) << 32\n    else\n      while size >= 8\n        permute(read_u64(ptr))\n        ptr += 8\n        size -= 8\n      end\n      last = read_u64(ptr - (8 - size))\n    end\n    @a ^= size\n    @b ^= size\n    permute(last)\n    self\n  end\n\n  def class(value)\n    value.crystal_type_id.hash(self)\n  end\n\n  def inspect(io : IO) : Nil\n    io << \"#{self.class}(hidden_state)\"\n  end\nend\n"
  },
  {
    "path": "src/crystal/iconv.cr",
    "content": "{% if flag?(:use_libiconv) || flag?(:win32) || (flag?(:android) && LibC::ANDROID_API < 28) %}\n  require \"./lib_iconv\"\n  private USE_LIBICONV = true\n{% else %}\n  require \"c/iconv\"\n  private USE_LIBICONV = false\n{% end %}\n\n# :nodoc:\nstruct Crystal::Iconv\n  @skip_invalid : Bool\n\n  {% if USE_LIBICONV %}\n    @iconv : LibIconv::IconvT\n  {% else %}\n    @iconv : LibC::IconvT\n  {% end %}\n\n  ERROR = LibC::SizeT::MAX # (size_t)(-1)\n\n  def initialize(from : String, to : String, invalid : Symbol? = nil)\n    original_from, original_to = from, to\n\n    @skip_invalid = invalid == :skip\n    {% unless flag?(:freebsd) || flag?(:musl) || flag?(:dragonfly) || flag?(:netbsd) || flag?(:solaris) %}\n      if @skip_invalid\n        from = \"#{from}//IGNORE\"\n        to = \"#{to}//IGNORE\"\n      end\n    {% end %}\n\n    @iconv = {{ USE_LIBICONV ? LibIconv : LibC }}.iconv_open(to, from)\n\n    if @iconv.address == ERROR\n      if Errno.value == Errno::EINVAL\n        if original_from == \"UTF-8\"\n          raise ArgumentError.new(\"Invalid encoding: #{original_to}\")\n        elsif original_to == \"UTF-8\"\n          raise ArgumentError.new(\"Invalid encoding: #{original_from}\")\n        else\n          raise ArgumentError.new(\"Invalid encoding: #{original_from} -> #{original_to}\")\n        end\n      else\n        raise RuntimeError.from_errno(\"iconv_open\")\n      end\n    end\n  end\n\n  def self.new(from : String, to : String, invalid : Symbol? = nil, &)\n    iconv = new(from, to, invalid)\n    begin\n      yield iconv\n    ensure\n      iconv.close\n    end\n  end\n\n  def convert(inbuf : UInt8**, inbytesleft : LibC::SizeT*, outbuf : UInt8**, outbytesleft : LibC::SizeT*)\n    {% if flag?(:freebsd) || flag?(:dragonfly) %}\n      if @skip_invalid\n        return LibC.__iconv(@iconv, inbuf, inbytesleft, outbuf, outbytesleft, LibC::ICONV_F_HIDE_INVALID, out invalids)\n      end\n    {% end %}\n    {{ USE_LIBICONV ? LibIconv : LibC }}.iconv(@iconv, inbuf, inbytesleft, outbuf, outbytesleft)\n  end\n\n  def handle_invalid(inbuf, inbytesleft)\n    if @skip_invalid\n      # iconv will leave inbuf right at the beginning of the invalid sequence,\n      # so we just skip that byte and later we'll try with the next one\n      if inbytesleft.value > 0\n        inbuf.value += 1\n        inbytesleft.value -= 1\n      end\n    else\n      case Errno.value\n      when Errno::EINVAL\n        raise ArgumentError.new \"Incomplete multibyte sequence\"\n      when Errno::EILSEQ\n        raise ArgumentError.new \"Invalid multibyte sequence\"\n      else\n        # All is good\n      end\n    end\n  end\n\n  def close\n    if {{ USE_LIBICONV ? LibIconv : LibC }}.iconv_close(@iconv) == -1\n      raise RuntimeError.from_errno(\"iconv_close\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/interpreter.cr",
    "content": "module Crystal\n  # :nodoc:\n  #\n  # This is a support module to implement the interpreter.\n  module Interpreter\n    # Returns the fiber associated to the currently running interpreter.\n    @[Primitive(:interpreter_current_fiber)]\n    def self.current_fiber : Void*\n    end\n\n    # Spawns a new interpreter that will run the given fiber\n    # by calling `fiber_main` (which is a `Proc(Fiber, Nil)`)\n    # and passing `fiber` to it.\n    #\n    # The spawned fiber isn't automatically enqueued in the\n    # interpreter's event loop scheduler.\n    #\n    # Returns the spawned fiber.\n    @[Primitive(:interpreter_spawn)]\n    def self.spawn(fiber : Fiber, fiber_main : Void*) : Void*\n    end\n\n    # Returns the resumable value from the interpreter's fiber.\n    @[Primitive(:interpreter_fiber_resumable)]\n    def self.fiber_resumable(context) : LibC::Long\n    end\n\n    {% if compare_versions(Crystal::VERSION, \"1.15.0-dev\") >= 0 %}\n      @[Primitive(:interpreter_signal_descriptor)]\n      def self.signal_descriptor(fd : Int32) : Nil\n      end\n\n      @[Primitive(:interpreter_signal)]\n      def self.signal(signum : Int32, handler : Int32) : Nil\n      end\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/crystal/lib_iconv.cr",
    "content": "require \"c/stddef\"\n\n{% if flag?(:without_iconv) %}\n  {% raise \"The `without_iconv` flag is preventing you to use the LibIconv module\" %}\n{% end %}\n\n# Supported library versions:\n#\n# * libiconv-gnu\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#internationalization-conversion\n@[Link(\"iconv\")]\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  @[Link(dll: \"iconv-2.dll\")]\n{% end %}\nlib LibIconv\n  type IconvT = Void*\n\n  alias Int = LibC::Int\n  alias Char = LibC::Char\n  alias SizeT = LibC::SizeT\n\n  fun iconv = libiconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close = libiconv_close(cd : IconvT) : Int\n  fun iconv_open = libiconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/crystal/mach_o.cr",
    "content": "module Crystal\n  # :nodoc:\n  #\n  # Mach-O parser.\n  #\n  # Documentation:\n  # - <http://www.idea2ic.com/File_Formats/MachORuntime.pdf>\n  # - <http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme>\n  struct MachO\n    class Error < Exception\n    end\n\n    MAGIC    = 0xfeedface\n    CIGAM    = 0xcefaedfe\n    MAGIC_64 = 0xfeedfacf\n    CIGAM_64 = 0xcffaedfe\n\n    ABI64 = 0x01000000\n\n    enum CpuType : Int32\n      ANY       = -1\n      VAX       =  1\n      MC680x0   =  6\n      X86       =  7\n      X86_64    = 7 | ABI64\n      MC98000   = 10\n      HPPA      = 11\n      ARM       = 12\n      MC88000   = 13\n      SPARC     = 14\n      I860      = 15\n      POWERPC   = 18\n      POWERPC64 = 18 | ABI64\n    end\n\n    enum FileType : UInt32\n      OBJECT      = 0x1\n      EXECUTE     = 0x2\n      FVMLIB      = 0x3\n      CORE        = 0x4\n      PRELOAD     = 0x5\n      DYLIB       = 0x6\n      DYLINKER    = 0x7\n      BUNDLE      = 0x8\n      DYLIB_STUB  = 0x9\n      DSYM        = 0xa\n      KEXT_BUNDLE = 0xb\n    end\n\n    @[::Flags]\n    enum Flags : UInt32\n      NOUNDEFS                =       0x1\n      INCRLINK                =       0x2\n      DYLDLINK                =       0x4\n      BINDATLOAD              =       0x8\n      PREBOUND                =      0x10\n      SPLIT_SEGS              =      0x20\n      LAZY_INIT               =      0x40\n      TWOLEVEL                =      0x80\n      FORCE_FLAT              =     0x100\n      NOMULTIDEFS             =     0x200\n      NOFIXPREBINDING         =     0x400\n      PREBINDABLE             =     0x800\n      ALLMODSBOUND            =    0x1000\n      SUBSECTIONS_VIA_SYMBOLS =    0x2000\n      CANONICAL               =    0x4000\n      WEAK_DEFINES            =    0x8000\n      BINDS_TO_WEAK           =   0x10000\n      ALLOW_STACK_EXECUTION   =   0x20000\n      ROOT_SAFE               =   0x40000\n      SETUID_SAFE             =   0x80000\n      NO_REEXPORTED_DYLIBS    =  0x100000\n      PIE                     =  0x200000\n      DEAD_STRIPPABLE_DYLIB   =  0x400000\n      HAS_TLV_DESCRIPTORS     =  0x800000\n      NO_HEAP_EXECUTION       = 0x1000000\n    end\n\n    property magic : UInt32\n    property cputype : CpuType\n    property cpusubtype : Int32\n    property filetype : FileType\n    property ncmds : UInt32\n    property sizeofcmds : UInt32\n    property flags : Flags\n\n    @ldoff : Int64\n    @uuid : UUID?\n    @symtab : Symtab?\n    @stabs : Array(StabEntry)?\n    @symbols : Array(Nlist64)?\n\n    def self.open(path, &)\n      File.open(path, \"r\") do |file|\n        yield new(file)\n      end\n    end\n\n    def initialize(@io : IO::FileDescriptor)\n      @magic = read_magic\n      @cputype = CpuType.new(@io.read_bytes(Int32, endianness))\n      @cpusubtype = @io.read_bytes(Int32, endianness)\n      @filetype = FileType.new(@io.read_bytes(UInt32, endianness))\n      @ncmds = @io.read_bytes(UInt32, endianness)\n      @sizeofcmds = @io.read_bytes(UInt32, endianness)\n      @flags = Flags.new(@io.read_bytes(UInt32, endianness))\n      @io.skip(4) if abi64? # reserved\n      @ldoff = @io.tell\n      @segments = [] of Segment64\n      @sections = [] of Section64\n    end\n\n    private def read_magic\n      magic = @io.read_bytes(UInt32)\n      unless magic.in?(MAGIC_64, CIGAM_64, MAGIC, CIGAM)\n        raise Error.new(\"Invalid magic number\")\n      end\n      magic\n    end\n\n    def abi64?\n      cputype.value.bits_set? ABI64\n    end\n\n    def endianness\n      if @magic.in?(MAGIC_64, MAGIC)\n        IO::ByteFormat::SystemEndian\n      elsif IO::ByteFormat::SystemEndian == IO::ByteFormat::LittleEndian\n        IO::ByteFormat::BigEndian\n      else\n        IO::ByteFormat::LittleEndian\n      end\n    end\n\n    # :nodoc:\n    REQ_DYLD = 0x80000000\n\n    enum LoadCommand : UInt32\n      SEGMENT              =  0x1\n      SYMTAB               =  0x2\n      SYMSEG               =  0x3\n      THREAD               =  0x4\n      UNIXTHREAD           =  0x5\n      LOADFVMLIB           =  0x6\n      IDFVMLIB             =  0x7\n      IDENT                =  0x8\n      FVMFILE              =  0x9\n      PREPAGE              =  0xa\n      DYSYMTAB             =  0xb\n      LOAD_DYLIB           =  0xc\n      ID_DYLIB             =  0xd\n      LOAD_DYLINKER        =  0xe\n      ID_DYLINKER          =  0xf\n      PREBOUND_DYLIB       = 0x10\n      ROUTINES             = 0x11\n      SUB_FRAMEWORK        = 0x12\n      SUB_UMBRELLA         = 0x13\n      SUB_CLIENT           = 0x14\n      SUB_LIBRARY          = 0x15\n      TWOLEVEL_HINTS       = 0x16\n      PREBIND_CKSUM        = 0x17\n      LOAD_WEAK_DYLIB      = 0x18 | REQ_DYLD\n      SEGMENT_64           = 0x19\n      ROUTINES_64          = 0x1a\n      UUID                 = 0x1b\n      RPATH                = 0x1c | REQ_DYLD\n      CODE_SIGNATURE       = 0x1d\n      SEGMENT_SPLIT_INFO   = 0x1e\n      REEXPORT_DYLIB       = 0x1f | REQ_DYLD\n      LAZY_LOAD_DYLIB      = 0x20\n      ENCRYPTION_INFO      = 0x21\n      DYLD_INFO            = 0x22\n      DYLD_INFO_ONLY       = 0x22 | REQ_DYLD\n      LOAD_UPWARD_DYLIB    = 0x23 | REQ_DYLD\n      VERSION_MIN_MACOSX   = 0x24\n      VERSION_MIN_IPHONEOS = 0x25\n      FUNCTION_STARTS      = 0x26\n      DYLD_ENVIRONMENT     = 0x27\n      MAIN                 = 0x28 | REQ_DYLD\n      DATA_IN_CODE         = 0x29\n      SOURCE_VERSION       = 0x2A\n      DYLIB_CODE_SIGN_DRS  = 0x2B\n      ENCRYPTION_INFO_64   = 0x2C\n      LINKER_OPTION        = 0x2D\n    end\n\n    struct Segment64\n      property! segname : String\n      property! vmaddr : UInt64\n      property! vmsize : UInt64\n      property! fileoff : UInt64\n      property! filesize : UInt64\n      property! maxprot : UInt32\n      property! initprot : UInt32\n      property! nsects : UInt32\n      property! flags : UInt32\n    end\n\n    struct UUID\n      property bytes : StaticArray(UInt8, 16)\n\n      def initialize(@bytes)\n      end\n\n      def ==(other : UUID)\n        bytes == other.bytes\n      end\n\n      def inspect(io : IO) : Nil\n        io << bytes.to_slice.hexstring\n      end\n    end\n\n    struct Section64\n      property segment : Segment64\n      property! sectname : String\n      property! segname : String\n      property! addr : UInt64\n      property! size : UInt64\n      property! offset : UInt32\n      property! align : UInt32\n      property! reloff : UInt32\n      property! nreloc : UInt32\n      property! flags : UInt32\n\n      def initialize(@segment)\n      end\n    end\n\n    struct Symtab\n      property! symoff : UInt32\n      property! nsyms : UInt32\n      property! stroff : UInt32\n      property! strsize : UInt32\n    end\n\n    enum Stab : UInt8\n      GSYM    = 0x20\n      FNAME   = 0x22\n      FUN     = 0x24\n      STSYM   = 0x26\n      LCSYM   = 0x28\n      BNSYM   = 0x2e\n      OPT     = 0x3c\n      RSYM    = 0x40\n      SLINE   = 0x44\n      ENSYM   = 0x4e\n      SSYM    = 0x60\n      SO      = 0x64\n      OSO     = 0x66\n      LSYM    = 0x80\n      BINCL   = 0x82\n      SOL     = 0x84\n      PARAMS  = 0x86\n      VERSION = 0x88\n      OLEVEL  = 0x8A\n      PSYM    = 0xa0\n      EINCL   = 0xa2\n      ENTRY   = 0xa4\n      LBRAC   = 0xc0\n      EXCL    = 0xc2\n      RBRAC   = 0xe0\n      BCOMM   = 0xe2\n      ECOMM   = 0xe4\n      ECOML   = 0xe8\n      LENG    = 0xfe\n    end\n\n    struct Nlist64\n      struct Type\n        STAB = 0xe0_u8 # 11100000\n        PEXT = 0x10_u8 # 00010000\n        EXT  = 0x01_u8 # 00000001\n        TYPE = 0x0e_u8 # 00001110\n        UNDF = 0x00_u8 # 00000000\n        ABS  = 0x02_u8 # 00000010\n        SECT = 0x0e_u8 # 00001110\n        PBUD = 0x0c_u8 # 00001100\n        INDR = 0x0a_u8 # 00001010\n\n        def initialize(@value : UInt8)\n        end\n\n        def value\n          @value\n        end\n\n        def to_unsafe\n          @value\n        end\n\n        def stab?\n          (value & STAB) != 0\n        end\n\n        def pext?\n          value.bits_set? PEXT\n        end\n\n        def ext?\n          value.bits_set? EXT\n        end\n\n        {% for flag in %w(UNDF ABS SECT PBUD INDR) %}\n          def {{flag.downcase.id}}?\n            (value & TYPE) == {{flag.id}}\n          end\n        {% end %}\n\n        def stab\n          Stab.new(value) if stab?\n        end\n\n        def to_s(io : IO) : Nil\n          if stab?\n            io << \"STAB(#{stab})\"\n            return\n          end\n\n          n = [] of String\n\n          n << \"PEXT\" if pext?\n          n << \"EXT\" if ext?\n\n          if undf?\n            n << \"UNDF\"\n          elsif abs?\n            n << \"ABS\"\n          elsif sect?\n            n << \"SECT\"\n          elsif pbud?\n            n << \"PBUD\"\n          elsif indr?\n            n << \"INDR\"\n          end\n\n          io << n.join('|')\n        end\n\n        def inspect(io : IO) : Nil\n          to_s(io)\n        end\n      end\n\n      property! strx : UInt32\n      property! type : Type\n      property! sect : UInt8\n      property! desc : UInt16\n      property! value : UInt64\n\n      property name : String\n      @name = \"\"\n\n      def stab?\n        type.stab\n      end\n\n      def stab\n        type.stab\n      end\n\n      def inspect(io : IO) : Nil\n        io << \"#{self.class.name}(type=#{type}, name=#{name.inspect}, sect=#{sect}, desc=#{desc}, value=#{value})\"\n      end\n    end\n\n    # Seek to the first matching load command, yields, then returns the value of\n    # the block.\n    private def seek_to(load_command : LoadCommand, &)\n      seek_to_each(load_command) do |cmd, cmdsize|\n        return yield cmdsize\n      end\n    end\n\n    # Seek to each matching load command, yielding each of them.\n    private def seek_to_each(load_command : LoadCommand, &) : Nil\n      @io.seek(@ldoff)\n\n      ncmds.times do\n        cmd = LoadCommand.new(@io.read_bytes(UInt32, endianness))\n        cmdsize = @io.read_bytes(UInt32, endianness)\n\n        if cmd == load_command\n          yield cmd, cmdsize\n        else\n          @io.skip(cmdsize - 8)\n        end\n      end\n    end\n\n    def segments\n      read_segments_and_sections if @segments.empty?\n      @segments\n    end\n\n    def sections\n      read_segments_and_sections if @sections.empty?\n      @sections\n    end\n\n    private def read_segments_and_sections\n      seek_to_each(LoadCommand::SEGMENT_64) do |cmd, cmdsize|\n        segment = Segment64.new\n        segment.segname = read_name\n        segment.vmaddr = @io.read_bytes(UInt64, endianness)\n        segment.vmsize = @io.read_bytes(UInt64, endianness)\n        segment.fileoff = @io.read_bytes(UInt64, endianness)\n        segment.filesize = @io.read_bytes(UInt64, endianness)\n        segment.maxprot = @io.read_bytes(UInt32, endianness)\n        segment.initprot = @io.read_bytes(UInt32, endianness)\n        segment.nsects = @io.read_bytes(UInt32, endianness)\n        segment.flags = @io.read_bytes(UInt32, endianness)\n        @segments << segment\n\n        segment.nsects.times do\n          section = Section64.new(segment)\n          section.sectname = read_name\n          section.segname = read_name\n          section.addr = @io.read_bytes(UInt64, endianness)\n          section.size = @io.read_bytes(UInt64, endianness)\n          section.offset = @io.read_bytes(UInt32, endianness)\n          section.align = @io.read_bytes(UInt32, endianness)\n          section.reloff = @io.read_bytes(UInt32, endianness)\n          section.nreloc = @io.read_bytes(UInt32, endianness)\n          section.flags = @io.read_bytes(UInt32, endianness)\n          @io.skip(12)\n          @sections << section\n        end\n      end\n    end\n\n    def symtab\n      @symtab ||= seek_to(LoadCommand::SYMTAB) do\n        symtab = Symtab.new\n        symtab.symoff = @io.read_bytes(UInt32, endianness)\n        symtab.nsyms = @io.read_bytes(UInt32, endianness)\n        symtab.stroff = @io.read_bytes(UInt32, endianness)\n        symtab.strsize = @io.read_bytes(UInt32, endianness)\n        symtab\n      end.not_nil!\n    end\n\n    def uuid\n      @uuid ||= begin\n        # ld has a -no_version_load_command options to suppress the LC_UUID entry.\n        # The Dwarf file generated in such scenario has a LC_UUID = 00000000-0000-0000-0000-000000000000.\n        seek_to(LoadCommand::UUID) do\n          bytes = uninitialized UInt8[16]\n          @io.read_fully(bytes.to_slice)\n          UUID.new(bytes)\n        end || UUID.new(StaticArray(UInt8, 16).new(0))\n      end\n    end\n\n    def symbols\n      @symbols ||= @io.seek(symtab.symoff) do\n        Array(Nlist64).new(symtab.nsyms) do\n          nlist = Nlist64.new\n          nlist.strx = @io.read_bytes(UInt32, endianness)\n          nlist.type = Nlist64::Type.new(@io.read_byte.not_nil!)\n          nlist.sect = @io.read_byte.not_nil!\n          nlist.desc = @io.read_bytes(UInt16, endianness)\n          nlist.value = @io.read_bytes(UInt64, endianness)\n\n          if nlist.strx > 0\n            @io.seek(symtab.stroff + nlist.strx) do\n              nlist.name = @io.gets('\\0', chomp: true).to_s\n            end\n          end\n\n          nlist\n        end\n      end\n    end\n\n    record StabEntry,\n      type : Stab,\n      name : String,\n      sect : UInt8,\n      desc : UInt16,\n      value : UInt64\n\n    def stabs\n      @stabs ||= symbols.compact_map do |nlist|\n        if stab = nlist.stab?\n          StabEntry.new(stab, nlist.name, nlist.sect, nlist.desc, nlist.value)\n        end\n      end\n    end\n\n    private def read_name\n      bytes = uninitialized StaticArray(UInt8, 16)\n      @io.read_fully(bytes.to_slice)\n      len = bytes.size\n      while len > 0 && bytes[len - 1] == 0\n        len -= 1\n      end\n      String.new(bytes.to_unsafe, len)\n    end\n\n    def read_section?(name, &)\n      if sh = sections.find { |s| s.sectname == name }\n        @io.seek(sh.offset) do\n          yield sh, @io\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/main.cr",
    "content": "require \"process/executable_path\" # Process::PATH_DELIMITER\n\nlib LibCrystalMain\n  @[Raises]\n  fun __crystal_main(argc : Int32, argv : UInt8**)\nend\n\nmodule Crystal\n  # Defines the main routine run by normal Crystal programs:\n  #\n  # - Initializes runtime requirements (GC, ...)\n  # - Invokes the given *block*\n  # - Handles unhandled exceptions\n  # - Invokes `at_exit` handlers\n  # - Flushes `STDOUT` and `STDERR`\n  #\n  # This method can be invoked if you need to define a custom\n  # main (as in C main) function, doing all the above steps.\n  #\n  # For example:\n  #\n  # ```\n  # fun main(argc : Int32, argv : UInt8**) : Int32\n  #   Crystal.main do\n  #     elapsed = Time.measure do\n  #       Crystal.main_user_code(argc, argv)\n  #     end\n  #     puts \"Time to execute program: #{elapsed}\"\n  #   end\n  # end\n  # ```\n  #\n  # Note that the above is really just an example, almost the\n  # same can be accomplished with `at_exit`. But in some cases\n  # redefinition of C's main is needed.\n  def self.main(&block)\n    {% if flag?(:tracing) %} Crystal::Tracing.init {% end %}\n    GC.init\n\n    init_runtime\n\n    status =\n      begin\n        yield\n        0\n      rescue ex\n        1\n      end\n\n    exit(status, ex)\n  end\n\n  # :nodoc:\n  def self.init_runtime : Nil\n    # `__crystal_once` directly or indirectly depends on `Fiber` and `Thread`\n    # so we explicitly initialize their class vars, then init crystal/once\n    Thread.init\n    Fiber.init\n    Crystal::Once.init\n  end\n\n  # :nodoc:\n  def self.exit(status : Int32, exception : Exception?) : Int32\n    status = Crystal::AtExitHandlers.run status, exception\n\n    if exception\n      STDERR.print \"Unhandled exception: \"\n      exception.inspect_with_backtrace(STDERR)\n    end\n\n    ignore_stdio_errors { STDOUT.flush }\n    ignore_stdio_errors { STDERR.flush }\n\n    status\n  end\n\n  # :nodoc:\n  def self.ignore_stdio_errors(&)\n    yield\n  rescue IO::Error\n  end\n\n  # Main method run by all Crystal programs at startup.\n  #\n  # This setups up the GC, invokes your program, rescuing\n  # any handled exception, and then runs `at_exit` handlers.\n  #\n  # This method is automatically invoked for you, so you\n  # don't need to invoke it.\n  #\n  # However, if you need to define a special main C function,\n  # you can redefine main and invoke `Crystal.main` from it:\n  #\n  # ```\n  # fun main(argc : Int32, argv : UInt8**) : Int32\n  #   # some setup before Crystal main\n  #   Crystal.main(argc, argv)\n  #   # some cleanup logic after Crystal main\n  # end\n  # ```\n  #\n  # The `Crystal.main` can also be passed as a callback:\n  #\n  # ```\n  # fun main(argc : Int32, argv : UInt8**) : Int32\n  #   LibFoo.init_foo_and_invoke_main(argc, argv, ->Crystal.main(Int32, UInt8**))\n  # end\n  # ```\n  #\n  # Note that before `Crystal.main` is invoked the GC\n  # is not setup yet, so nothing that allocates memory\n  # in Crystal (like `new` for classes) can be used.\n  def self.main(argc : Int32, argv : UInt8**)\n    main do\n      main_user_code(argc, argv)\n    end\n  rescue ex\n    Crystal::System.print_exception \"Unhandled exception\", ex\n    1\n  end\n\n  # Executes the main user code. This normally is executed\n  # after initializing the GC and before executing `at_exit` handlers.\n  #\n  # You should never invoke this method unless you need to\n  # redefine C's main function. See `Crystal.main` for\n  # more details.\n  def self.main_user_code(argc : Int32, argv : UInt8**)\n    LibCrystalMain.__crystal_main(argc, argv)\n  end\nend\n\n# Main function that acts as C's main function.\n# Invokes `Crystal.main`.\n#\n# Can be redefined. See `Crystal.main` for examples.\n#\n# On Windows the actual entry point is `wmain`, but there is no need to redefine\n# that. See the file required below for details.\nfun main(argc : Int32, argv : UInt8**) : Int32\n  Crystal.main(argc, argv)\nend\n\n{% if flag?(:interpreted) %}\n  # the interpreter doesn't call Crystal.main(&)\n  Crystal.init_runtime\n{% elsif flag?(:win32) %}\n  require \"./system/win32/wmain\"\n{% elsif flag?(:wasi) %}\n  require \"./system/wasi/main\"\n{% else %}\n  require \"./system/unix/main\"\n{% end %}\n"
  },
  {
    "path": "src/crystal/once.cr",
    "content": "# This file defines the `__crystal_once` functions expected by the compiler. It\n# is called each time a constant or class variable has to be initialized and is\n# its responsibility to verify the initializer is executed only once and to fail\n# on recursion.\n#\n# It also defines the `__crystal_once_init` function for backward compatibility\n# with older compiler releases. It is executed only once at the beginning of the\n# program and, for the legacy implementation, the result is passed on each call\n# to `__crystal_once`.\n\nrequire \"crystal/pointer_linked_list\"\nrequire \"crystal/spin_lock\"\n\nmodule Crystal\n  # :nodoc:\n  module Once\n    struct Operation\n      include PointerLinkedList::Node\n\n      getter fiber : Fiber\n      getter flag : Bool*\n\n      def initialize(@flag : Bool*, @fiber : Fiber)\n        @waiting = PointerLinkedList(Fiber::PointerLinkedListNode).new\n      end\n\n      def add_waiter(node) : Nil\n        @waiting.push(node)\n      end\n\n      def resume_all : Nil\n        @waiting.each(&.value.enqueue)\n      end\n    end\n\n    @@spin = uninitialized SpinLock\n    @@operations = uninitialized PointerLinkedList(Operation)\n\n    def self.init : Nil\n      @@spin = SpinLock.new\n      @@operations = PointerLinkedList(Operation).new\n    end\n\n    protected def self.exec(flag : Bool*, &)\n      @@spin.lock\n\n      if flag.value\n        @@spin.unlock\n      elsif operation = processing?(flag)\n        check_reentrancy(operation)\n        wait_initializer(operation)\n      else\n        run_initializer(flag) { yield }\n      end\n\n      # safety check, and allows to safely call `Intrinsics.unreachable` in\n      # `__crystal_once`\n      return if flag.value\n\n      System.print_error \"BUG: failed to initialize class variable or constant\\n\"\n      LibC._exit(1)\n    end\n\n    private def self.processing?(flag)\n      @@operations.each do |operation|\n        return operation if operation.value.flag == flag\n      end\n    end\n\n    private def self.check_reentrancy(operation)\n      if operation.value.fiber == Fiber.current\n        @@spin.unlock\n        raise \"Recursion while initializing class variables and/or constants\"\n      end\n    end\n\n    private def self.wait_initializer(operation)\n      waiting = Fiber::PointerLinkedListNode.new(Fiber.current)\n      operation.value.add_waiter(pointerof(waiting))\n      @@spin.unlock\n      Fiber.suspend\n    end\n\n    private def self.run_initializer(flag, &)\n      operation = Operation.new(flag, Fiber.current)\n      @@operations.push pointerof(operation)\n      @@spin.unlock\n\n      yield\n\n      @@spin.lock\n      flag.value = true\n      @@operations.delete pointerof(operation)\n      @@spin.unlock\n\n      operation.resume_all\n    end\n  end\n\n  # :nodoc:\n  #\n  # Never inlined to avoid bloating the call site with the slow-path that should\n  # usually not be taken.\n  @[NoInline]\n  def self.once(flag : Bool*, initializer : Void*)\n    Once.exec(flag, &Proc(Nil).new(initializer, Pointer(Void).null))\n  end\n\n  # :nodoc:\n  #\n  # NOTE: should also never be inlined, but that would capture the block, which\n  # would be a breaking change when we use this method to protect class getter\n  # and class property macros with lazy initialization (the block may return or\n  # break).\n  #\n  # TODO: consider a compile time flag to enable/disable the capture? returning\n  # from the block is unexpected behavior: the returned value won't be saved in\n  # the class variable.\n  def self.once(flag : Bool*, &)\n    Once.exec(flag) { yield } unless flag.value\n  end\nend\n\n{% if compare_versions(Crystal::VERSION, \"1.16.0-dev\") >= 0 %}\n  # :nodoc:\n  #\n  # We always inline this accessor to optimize for the fast-path (already\n  # initialized).\n  @[AlwaysInline]\n  fun __crystal_once(flag : Bool*, initializer : Void*)\n    return if flag.value\n    Crystal.once(flag, initializer)\n\n    # tells LLVM to assume that the flag is true, this avoids repeated access to\n    # the same constant or class variable to check the flag and try to run the\n    # initializer (only the first access will)\n    Intrinsics.unreachable unless flag.value\n  end\n{% else %}\n  # :nodoc:\n  #\n  # Unused. Kept for backward compatibility with older compilers.\n  fun __crystal_once_init : Void*\n    Pointer(Void).null\n  end\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_once(state : Void*, flag : Bool*, initializer : Void*)\n    return if flag.value\n    Crystal.once(flag, initializer)\n    Intrinsics.unreachable unless flag.value\n  end\n{% end %}\n"
  },
  {
    "path": "src/crystal/pe.cr",
    "content": "module Crystal\n  # :nodoc:\n  #\n  # Portable Executable reader.\n  #\n  # Documentation:\n  # - <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format>\n  struct PE\n    class Error < Exception\n    end\n\n    record SectionHeader, name : String, virtual_offset : UInt32, offset : UInt32, size : UInt32\n\n    record COFFSymbol, offset : UInt32, name : String\n\n    # addresses in COFF debug info are relative to this image base; used by\n    # `Exception::CallStack.read_dwarf_sections` to calculate the real relocated\n    # addresses\n    getter original_image_base : UInt64\n\n    @section_headers : Slice(SectionHeader)\n    @string_table_base : UInt32\n\n    # mapping from zero-based section index to list of symbols sorted by\n    # offsets within that section\n    getter coff_symbols = Hash(Int32, Array(COFFSymbol)).new\n\n    def self.open(path : String | ::Path, &)\n      File.open(path, \"r\") do |file|\n        yield new(file)\n      end\n    end\n\n    def initialize(@io : IO::FileDescriptor)\n      dos_header = uninitialized LibC::IMAGE_DOS_HEADER\n      io.read_fully(pointerof(dos_header).to_slice(1).to_unsafe_bytes)\n      raise Error.new(\"Invalid DOS header\") unless dos_header.e_magic == 0x5A4D # MZ\n\n      io.seek(dos_header.e_lfanew)\n      nt_header = uninitialized LibC::IMAGE_NT_HEADERS\n      io.read_fully(pointerof(nt_header).to_slice(1).to_unsafe_bytes)\n      raise Error.new(\"Invalid PE header\") unless nt_header.signature == 0x00004550 # PE\\0\\0\n\n      @original_image_base = nt_header.optionalHeader.imageBase\n      @string_table_base = nt_header.fileHeader.pointerToSymbolTable + nt_header.fileHeader.numberOfSymbols * sizeof(LibC::IMAGE_SYMBOL)\n\n      section_count = nt_header.fileHeader.numberOfSections\n      nt_section_headers = Pointer(LibC::IMAGE_SECTION_HEADER).malloc(section_count).to_slice(section_count)\n      io.read_fully(nt_section_headers.to_unsafe_bytes)\n\n      @section_headers = nt_section_headers.map do |nt_header|\n        if nt_header.name[0] === '/'\n          # section name is longer than 8 bytes; look up the COFF string table\n          name_buf = nt_header.name.to_slice + 1\n          string_offset = String.new(name_buf, truncate_at_null: true).to_i\n          io.seek(@string_table_base + string_offset)\n          name = io.gets('\\0', chomp: true).not_nil!\n        else\n          name = String.new(nt_header.name.to_slice, truncate_at_null: true)\n        end\n\n        SectionHeader.new(name: name, virtual_offset: nt_header.virtualAddress, offset: nt_header.pointerToRawData, size: nt_header.virtualSize)\n      end\n\n      io.seek(nt_header.fileHeader.pointerToSymbolTable)\n      image_symbol_count = nt_header.fileHeader.numberOfSymbols\n      image_symbols = Pointer(LibC::IMAGE_SYMBOL).malloc(image_symbol_count).to_slice(image_symbol_count)\n      io.read_fully(image_symbols.to_unsafe_bytes)\n\n      aux_count = 0\n      image_symbols.each_with_index do |sym, i|\n        if aux_count == 0\n          aux_count = sym.numberOfAuxSymbols.to_i\n        else\n          aux_count &-= 1\n        end\n\n        next unless aux_count == 0\n        next unless sym.type.bits_set?(0x20) # COFF function\n        next unless sym.sectionNumber > 0    # one-based section index\n        next unless sym.storageClass.in?(LibC::IMAGE_SYM_CLASS_EXTERNAL, LibC::IMAGE_SYM_CLASS_STATIC)\n\n        if sym.n.name.short == 0\n          io.seek(@string_table_base + sym.n.name.long)\n          name = io.gets('\\0', chomp: true).not_nil!\n        else\n          name = String.new(sym.n.shortName.to_slice, truncate_at_null: true)\n        end\n\n        # `@coff_symbols` uses zero-based indices\n        section_coff_symbols = @coff_symbols.put_if_absent(sym.sectionNumber.to_i &- 1) { [] of COFFSymbol }\n        section_coff_symbols << COFFSymbol.new(sym.value, name)\n      end\n\n      # add one sentinel symbol to ensure binary search on the offsets works\n      @coff_symbols.each_with_index do |(_, symbols), i|\n        symbols.sort_by!(&.offset)\n        symbols << COFFSymbol.new(@section_headers[i].size, \"??\")\n      end\n    end\n\n    def read_section?(name : String, &)\n      if sh = @section_headers.find(&.name.== name)\n        @io.seek(sh.offset) do\n          yield sh, @io\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/pointer_linked_list.cr",
    "content": "# :nodoc:\n#\n# Doubly linked list of `T` structs referenced as pointers.\n# `T` that must include `Crystal::PointerLinkedList::Node`.\nstruct Crystal::PointerLinkedList(T)\n  @head : Pointer(T) = Pointer(T).null\n\n  module Node\n    macro included\n      property previous : ::Pointer(self) = ::Pointer(self).null\n      property next : ::Pointer(self) = ::Pointer(self).null\n    end\n  end\n\n  @[AlwaysInline]\n  protected def self.link(p : Pointer(T), q : Pointer(T)) : Nil\n    p.value.next = q\n    q.value.previous = p\n  end\n\n  @[AlwaysInline]\n  protected def self.insert_impl(new : Pointer(T), prev : Pointer(T), _next : Pointer(T)) : Nil\n    prev.value.next = new\n    new.value.previous = prev\n    new.value.next = _next\n    _next.value.previous = new\n  end\n\n  def first?\n    if node = @head\n      node\n    end\n  end\n\n  # Returns `true` if the list is empty, otherwise false.\n  def empty? : Bool\n    @head.null?\n  end\n\n  # Prepends *node* to the head of the list.\n  def unshift(node : Pointer(T)) : Nil\n    if !empty?\n      typeof(self).insert_impl node, @head.value.previous, @head\n    else\n      node.value.previous = node\n      node.value.next = node\n    end\n    @head = node\n  end\n\n  # Appends *node* to the tail of the list.\n  def push(node : Pointer(T)) : Nil\n    if empty?\n      node.value.previous = node\n      node.value.next = node\n      @head = node\n    else\n      typeof(self).insert_impl node, @head.value.previous, @head\n    end\n  end\n\n  # Removes *node* from the list.\n  def delete(node : Pointer(T)) : Nil\n    _next = node.value.next\n\n    if node != _next\n      @head = _next if @head == node\n      typeof(self).link node.value.previous, _next\n    else\n      @head = Pointer(T).null\n    end\n\n    node.value.next = Pointer(T).null\n    node.value.previous = Pointer(T).null\n  end\n\n  # Removes and returns head from the list, yields if empty\n  def shift(&)\n    if empty?\n      yield\n    else\n      @head.tap { |t| delete(t) }\n    end\n  end\n\n  # Returns and returns head from the list, `nil` if empty.\n  def shift?\n    shift { nil }\n  end\n\n  # Removes and returns tail from the list, yields if empty.\n  def pop(&)\n    if !empty?\n      h = @head\n      t = (h.value.previous || h).tap { |t| delete(t) }\n    else\n      yield\n    end\n  end\n\n  # Removes and returns tail from the list, `nil` if empty.\n  def pop?\n    pop { nil }\n  end\n\n  # Iterates the list.\n  def each(&) : Nil\n    node = @head\n    while node\n      _next = node.value.next\n      _next = Pointer(T).null if _next == @head\n      yield node\n      node = _next\n    end\n  end\n\n  # Iterates the list before clearing it.\n  def consume_each(&) : Nil\n    each { |node| yield node }\n    @head = Pointer(T).null\n  end\nend\n"
  },
  {
    "path": "src/crystal/pointer_pairing_heap.cr",
    "content": "# :nodoc:\n#\n# Tree of `T` structs referenced as pointers.\n# `T` must include `Crystal::PointerPairingHeap::Node`.\nclass Crystal::PointerPairingHeap(T)\n  module Node\n    macro included\n      property? heap_previous : self* = Pointer(self).null\n      property? heap_next : self* = Pointer(self).null\n      property? heap_child : self* = Pointer(self).null\n    end\n\n    # Compare self with other. For example:\n    #\n    # Use `<` to create a min heap.\n    # Use `>` to create a max heap.\n    abstract def heap_compare(other : Pointer(self)) : Bool\n  end\n\n  @head : T* = Pointer(T).null\n\n  private def head=(head)\n    @head = head\n    head.value.heap_previous = null if head\n    head\n  end\n\n  def empty?\n    @head.null?\n  end\n\n  def first? : Pointer(T)\n    @head\n  end\n\n  def shift? : Pointer(T)\n    if node = @head\n      self.head = merge_pairs(node.value.heap_child?)\n      node.value.heap_child = null\n      node\n    else\n      null\n    end\n  end\n\n  def add(node : Pointer(T)) : Nil\n    if node.value.heap_previous? || node.value.heap_next? || node.value.heap_child?\n      raise ArgumentError.new(\"The node is already in a Pairing Heap tree\")\n    end\n    self.head = meld(@head, node)\n  end\n\n  def delete(node : Pointer(T)) : Nil\n    if previous_node = node.value.heap_previous?\n      next_sibling = node.value.heap_next?\n\n      if previous_node.value.heap_next? == node\n        previous_node.value.heap_next = next_sibling\n      else\n        previous_node.value.heap_child = next_sibling\n      end\n\n      if next_sibling\n        next_sibling.value.heap_previous = previous_node\n      end\n\n      subtree = merge_pairs(node.value.heap_child?)\n      clear(node)\n      self.head = meld(@head, subtree)\n    else\n      # removing head\n      self.head = merge_pairs(node.value.heap_child?)\n      node.value.heap_child = null\n    end\n  end\n\n  def clear : Nil\n    if node = @head\n      clear_recursive(node)\n      @head = null\n    end\n  end\n\n  private def clear_recursive(node)\n    child = node.value.heap_child?\n    while child\n      clear_recursive(child)\n      child = child.value.heap_next?\n    end\n    clear(node)\n  end\n\n  private def meld(a : Pointer(T), b : Pointer(T)) : Pointer(T)\n    if a && b\n      if a.value.heap_compare(b)\n        add_child(a, b)\n      else\n        add_child(b, a)\n      end\n    else\n      a || b\n    end\n  end\n\n  private def add_child(parent : Pointer(T), node : Pointer(T)) : Pointer(T)\n    first_child = parent.value.heap_child?\n    parent.value.heap_child = node\n\n    first_child.value.heap_previous = node if first_child\n    node.value.heap_previous = parent\n    node.value.heap_next = first_child\n\n    parent\n  end\n\n  private def merge_pairs(node : Pointer(T)) : Pointer(T)\n    return null unless node\n\n    # 1st pass: meld children into pairs (left to right)\n    tail = null\n\n    while a = node\n      if b = a.value.heap_next?\n        node = b.value.heap_next?\n        root = meld(a, b)\n        root.value.heap_previous = tail\n        tail = root\n      else\n        a.value.heap_previous = tail\n        tail = a\n        break\n      end\n    end\n\n    # 2nd pass: meld the pairs back into a single tree (right to left)\n    root = null\n\n    while tail\n      node = tail.value.heap_previous?\n      root = meld(root, tail)\n      tail = node\n    end\n\n    root.value.heap_next = null if root\n    root\n  end\n\n  private def clear(node) : Nil\n    node.value.heap_previous = null\n    node.value.heap_next = null\n    node.value.heap_child = null\n  end\n\n  private macro null\n    Pointer(T).null\n  end\nend\n"
  },
  {
    "path": "src/crystal/print_buffered.cr",
    "content": "module Crystal\n  # Prepares an error message, with an optional exception or backtrace, to an\n  # in-memory buffer, before writing to an IO, usually STDERR, in a single write\n  # operation.\n  #\n  # Avoids intermingled messages caused by multiple threads writing to a STDIO\n  # in parallel. This may still happen, since writes may not be atomic when the\n  # overall size is larger than PIPE_BUF, buf it should at least write 512 bytes\n  # atomically.\n  def self.print_buffered(message : String, *args, to io : IO, exception = nil, backtrace = nil) : Nil\n    buf = buffered_message(message, *args, exception: exception, backtrace: backtrace)\n    io.write(buf.to_slice)\n    io.flush unless io.sync?\n  end\n\n  # Identical to `#print_buffered` but eventually calls `System.print_error(bytes)`\n  # to write to stderr without going through the event loop.\n  def self.print_error_buffered(message : String, *args, exception = nil, backtrace = nil) : Nil\n    buf = buffered_message(message, *args, exception: exception, backtrace: backtrace)\n    System.print_error(buf.to_slice)\n  end\n\n  private def self.buffered_message(message : String, *args, exception = nil, backtrace = nil)\n    buf = IO::Memory.new(4096)\n\n    if args.empty?\n      buf << message\n    else\n      System.printf(message, *args) { |bytes| buf.write(bytes) }\n    end\n\n    if exception\n      buf << \": \"\n      exception.inspect_with_backtrace(buf)\n    else\n      buf.puts\n      backtrace.try(&.each { |line| buf << \"  from \" << line << '\\n' })\n    end\n\n    buf\n  end\nend\n"
  },
  {
    "path": "src/crystal/rw_lock.cr",
    "content": "# :nodoc:\nstruct Crystal::RWLock\n  private UNLOCKED = 0\n  private LOCKED   = 1\n\n  @writer = Atomic(Int32).new(UNLOCKED)\n  @readers = Atomic(Int32).new(0)\n\n  def read_lock : Nil\n    loop do\n      while @writer.get(:relaxed) != UNLOCKED\n        Intrinsics.pause\n      end\n\n      @readers.add(1, :acquire)\n\n      if @writer.get(:acquire) == UNLOCKED\n        return\n      end\n\n      @readers.sub(1, :release)\n    end\n  end\n\n  def read_unlock : Nil\n    @readers.sub(1, :release)\n  end\n\n  def write_lock : Nil\n    while @writer.swap(LOCKED, :acquire) != UNLOCKED\n      Intrinsics.pause\n    end\n\n    while @readers.get(:acquire) != 0\n      Intrinsics.pause\n    end\n  end\n\n  def write_unlock : Nil\n    @writer.set(UNLOCKED, :release)\n  end\nend\n"
  },
  {
    "path": "src/crystal/scheduler.cr",
    "content": "{% skip_file if flag?(:execution_context) %}\n\nrequire \"crystal/event_loop\"\nrequire \"crystal/system/print_error\"\nrequire \"fiber\"\nrequire \"fiber/stack_pool\"\nrequire \"crystal/system/thread\"\n\n# :nodoc:\n#\n# Schedulers are tied to a thread, and must only ever be accessed from within\n# this thread.\n#\n# Only the class methods are public and safe to use. Instance methods are\n# protected and must never be called directly.\nclass Crystal::Scheduler\n  @event_loop = Crystal::EventLoop.create\n  @stack_pool = Fiber::StackPool.new\n\n  def self.stack_pool : Fiber::StackPool\n    Thread.current.scheduler.@stack_pool\n  end\n\n  def self.event_loop\n    Thread.current.scheduler.@event_loop\n  end\n\n  def self.event_loop?\n    if scheduler = Thread.current?.try(&.scheduler?)\n      scheduler.@event_loop\n    end\n  end\n\n  def self.enqueue(fiber : Fiber) : Nil\n    Crystal.trace :sched, \"enqueue\", fiber: fiber do\n      thread = Thread.current\n      scheduler = thread.scheduler\n\n      {% if flag?(:preview_mt) %}\n        th = fiber.get_current_thread\n        th ||= fiber.set_current_thread(scheduler.find_target_thread)\n\n        if th == thread\n          scheduler.enqueue(fiber)\n        else\n          th.scheduler.send_fiber(fiber)\n        end\n      {% else %}\n        scheduler.enqueue(fiber)\n      {% end %}\n    end\n  end\n\n  def self.enqueue(fibers : Enumerable(Fiber)) : Nil\n    fibers.each do |fiber|\n      enqueue(fiber)\n    end\n  end\n\n  def self.reschedule : Nil\n    Crystal.trace :sched, \"reschedule\"\n    Thread.current.scheduler.reschedule\n  end\n\n  def self.resume(fiber : Fiber) : Nil\n    validate_running_thread(fiber)\n    Thread.current.scheduler.resume(fiber)\n  end\n\n  private def self.validate_running_thread(fiber : Fiber) : Nil\n    {% if flag?(:preview_mt) %}\n      if th = fiber.get_current_thread\n        unless th == Thread.current\n          raise \"BUG: tried to manually resume #{fiber} on #{Thread.current} instead of #{th}\"\n        end\n      else\n        fiber.set_current_thread\n      end\n    {% end %}\n  end\n\n  @main : Fiber\n  @lock = Crystal::SpinLock.new\n  @sleeping = false\n\n  # :nodoc:\n  def initialize(@thread : Thread)\n    @main = thread.main_fiber\n    {% if flag?(:preview_mt) %} @main.set_current_thread(thread) {% end %}\n    @runnables = Deque(Fiber).new\n  end\n\n  protected def each_scheduler(& : Scheduler ->) : Nil\n  end\n\n  protected def enqueue(fiber : Fiber) : Nil\n    @lock.sync { @runnables << fiber }\n  end\n\n  protected def enqueue(fibers : Enumerable(Fiber)) : Nil\n    @lock.sync { @runnables.concat fibers }\n  end\n\n  protected def resume(fiber : Fiber) : Nil\n    Crystal.trace :sched, \"resume\", fiber: fiber\n    validate_resumable(fiber)\n\n    {% if flag?(:preview_mt) %}\n      GC.lock_read\n    {% elsif flag?(:interpreted) %}\n      # No need to change the stack bottom!\n    {% else %}\n      GC.set_stackbottom(fiber.@stack.bottom)\n    {% end %}\n\n    current, @thread.current_fiber = @thread.current_fiber, fiber\n    Fiber.swapcontext(pointerof(current.@context), pointerof(fiber.@context))\n\n    {% if flag?(:preview_mt) %}\n      GC.unlock_read\n    {% end %}\n  end\n\n  private def validate_resumable(fiber)\n    return if fiber.resumable?\n\n    if fiber.dead?\n      fatal_resume_error(fiber, \"tried to resume a dead fiber\")\n    else\n      fatal_resume_error(fiber, \"can't resume a running fiber\")\n    end\n  end\n\n  private def fatal_resume_error(fiber, message)\n    Crystal::System.print_error \"\\nFATAL: %s: %s\\n\", message, fiber.to_s\n    caller.each { |line| Crystal::System.print_error \"  from %s\\n\", line }\n    exit 1\n  end\n\n  protected def reschedule : Nil\n    loop do\n      if runnable = @lock.sync { @runnables.shift? }\n        resume(runnable) unless runnable == @thread.current_fiber\n        break\n      else\n        Crystal.trace :sched, \"event_loop\" do\n          @event_loop.run(blocking: true)\n        end\n      end\n    end\n  end\n\n  {% if flag?(:preview_mt) %}\n    private getter! worker_fiber : Fiber\n    @rr_target = 0\n\n    protected def find_target_thread\n      if workers = @@workers\n        @rr_target &+= 1\n        workers[@rr_target % workers.size]\n      else\n        Thread.current\n      end\n    end\n\n    def run_loop\n      @worker_fiber = Fiber.current\n\n      spawn_stack_pool_collector\n\n      loop do\n        @lock.lock\n\n        if runnable = @runnables.shift?\n          @runnables << worker_fiber\n          @lock.unlock\n          resume(runnable)\n        else\n          @sleeping = true\n          @lock.unlock\n          Crystal.trace :sched, \"mt:sleeping\"\n          Crystal.trace(:sched, \"mt:slept\") { ::Fiber.suspend }\n        end\n      end\n    end\n\n    def send_fiber(fiber : Fiber)\n      @lock.lock\n      @runnables << fiber\n\n      if @sleeping\n        @sleeping = false\n        @runnables << worker_fiber\n        @event_loop.interrupt\n      end\n      @lock.unlock\n    end\n\n    def self.init : Nil\n      count = worker_count\n      pending = Atomic(Int32).new(count - 1)\n      @@workers = Array(Thread).new(count) do |i|\n        if i == 0\n          worker_loop = Fiber.new(name: \"worker-loop\") { Thread.current.scheduler.run_loop }\n          worker_loop.set_current_thread\n          Thread.current.scheduler.enqueue worker_loop\n          Thread.current\n        else\n          Thread.new(name: \"CRYSTAL-MT-#{i}\") do\n            scheduler = Thread.current.scheduler\n            pending.sub(1)\n            scheduler.run_loop\n          end\n        end\n      end\n\n      # Wait for all worker threads to be fully ready to be used\n      while pending.get > 0\n        Fiber.yield\n      end\n    end\n\n    private def self.worker_count\n      env_workers = ENV[\"CRYSTAL_WORKERS\"]?\n\n      if env_workers && !env_workers.empty?\n        workers = env_workers.to_i?\n        if !workers || workers < 1\n          Crystal::System.print_error \"FATAL: Invalid value for CRYSTAL_WORKERS: %s\\n\", env_workers\n          exit 1\n        end\n\n        workers\n      else\n        # TODO: default worker count, currently hardcoded to 4 that seems to be something\n        # that is beneficial for many scenarios without adding too much contention.\n        # In the future we could use the number of cores or something associated to it.\n        4\n      end\n    end\n  {% else %}\n    def self.init : Nil\n      {% unless flag?(:interpreted) %}\n        Thread.current.scheduler.spawn_stack_pool_collector\n      {% end %}\n    end\n  {% end %}\n\n  # Background loop to cleanup unused fiber stacks.\n  def spawn_stack_pool_collector\n    fiber = Fiber.new(name: \"stack-pool-collector\", &->@stack_pool.collect_loop)\n    {% if flag?(:preview_mt) %} fiber.set_current_thread {% end %}\n    enqueue(fiber)\n  end\nend\n"
  },
  {
    "path": "src/crystal/small_deque.cr",
    "content": "module Crystal\n  # :nodoc:\n  # An internal deque type whose storage is backed by a `StaticArray`. The deque\n  # capacity is fixed to N and storing more than N elements is an error. Only a\n  # subset of `::Deque`'s functionality is defined as needed.\n  struct SmallDeque(T, N)\n    include Indexable::Mutable(T)\n\n    @start = 0\n    @buffer = uninitialized T[N]\n    getter size : Int32 = 0\n\n    def unsafe_fetch(index : Int) : T\n      index_to_ptr(index).value\n    end\n\n    def unsafe_put(index : Int, value : T)\n      index_to_ptr(index).value = value\n    end\n\n    def <<(value : T)\n      check_capacity_for_insert\n      unsafe_put(@size, value)\n      @size += 1\n      self\n    end\n\n    def shift(&)\n      if @size == 0\n        yield\n      else\n        ptr = index_to_ptr(0)\n        value = ptr.value\n        ptr.clear\n        @size &-= 1\n        @start &+= 1\n        @start &-= N if @start >= N\n        value\n      end\n    end\n\n    # precondition: 0 <= index <= N\n    private def index_to_ptr(index)\n      index &+= @start\n      index &-= N if index >= N\n      @buffer.to_unsafe + index\n    end\n\n    private def check_capacity_for_insert\n      raise \"Out of capacity\" if @size >= N\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/spin_lock.cr",
    "content": "# :nodoc:\nstruct Crystal::SpinLock\n  private UNLOCKED = 0\n  private LOCKED   = 1\n\n  {% if flag?(:preview_mt) || flag?(:win32) %}\n    @m = Atomic(Int32).new(UNLOCKED)\n  {% end %}\n\n  def lock\n    {% if flag?(:preview_mt) || flag?(:win32) %}\n      while @m.swap(LOCKED, :acquire) == LOCKED\n        while @m.get(:relaxed) == LOCKED\n          Intrinsics.pause\n        end\n      end\n    {% end %}\n  end\n\n  def unlock\n    {% if flag?(:preview_mt) || flag?(:win32) %}\n      @m.set(UNLOCKED, :release)\n    {% end %}\n  end\n\n  def sync(&)\n    lock\n    begin\n      yield\n    ensure\n      unlock\n    end\n  end\n\n  def unsync(&)\n    unlock\n    begin\n      yield\n    ensure\n      lock\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/syntax_highlighter/colorize.cr",
    "content": "require \"colorize\"\nrequire \"../syntax_highlighter\"\n\n# A syntax highlighter that renders Crystal source code with ANSI escape codes\n# suitable for terminal highlighting.\n#\n# NOTE: To use `Crystal::SyntaxHighlighter::Colorize`, you must explicitly import it with `require \"crystal/syntax_highlighter/colorize\"`\n#\n# ```\n# require \"crystal/syntax_highlighter/colorize\"\n#\n# code = %(foo = bar(\"baz\\#{PI + 1}\") # comment)\n# colorized = Crystal::SyntaxHighlighter::Colorize.highlight(code)\n# colorized # => \"foo \\e[91m=\\e[0m bar(\\e[93m\\\"baz\\#{\\e[0;36mPI\\e[0;93m \\e[0;91m+\\e[0;93m \\e[0;35m1\\e[0;93m}\\\"\\e[0m) \\e[90m# comment\\e[0m\"\n# ```\nclass Crystal::SyntaxHighlighter::Colorize < Crystal::SyntaxHighlighter\n  # Highlights *code* and writes the result to *io*.\n  def self.highlight(io : IO, code : String)\n    new(io).highlight(code)\n  end\n\n  # Highlights *code* and returns the result.\n  def self.highlight(code : String)\n    String.build do |io|\n      highlight(io, code)\n    end\n  end\n\n  # Highlights *code* or returns unhighlighted *code* on error.\n  #\n  # Same as `.highlight(code : String)` except that any error is rescued and\n  # returns unhighlighted source code.\n  def self.highlight!(code : String)\n    highlight(code)\n  rescue\n    code\n  end\n\n  # Creates a new instance of a Colorize syntax highlighter.\n  #\n  # Appends highlighted output (when calling `#highlight`) to *io*.\n  def initialize(@io : IO, @colorize : ::Colorize::Object(String) = ::Colorize.with.toggle(true))\n  end\n\n  property colors : Hash(TokenType, ::Colorize::Color) = {\n    :comment           => :dark_gray,\n    :number            => :magenta,\n    :char              => :light_yellow,\n    :symbol            => :magenta,\n    :string            => :light_yellow,\n    :interpolation     => :light_yellow,\n    :const             => :cyan,\n    :operator          => :light_red,\n    :ident             => :light_green,\n    :keyword           => :light_red,\n    :primitive_literal => :magenta,\n    :self              => :blue,\n  } of TokenType => ::Colorize::Color\n\n  def render(type : TokenType, value : String)\n    colorize(type, value)\n  end\n\n  def render_delimiter(&)\n    @colorize.fore(colors[TokenType::STRING]).surround(@io) do\n      yield\n    end\n  end\n\n  def render_interpolation(&)\n    colorize :INTERPOLATION, \"\\#{\"\n    @colorize.fore(:default).surround(@io) do\n      yield\n    end\n    colorize :INTERPOLATION, \"}\"\n  end\n\n  def render_string_array(&)\n    @colorize.fore(colors[TokenType::STRING]).surround(@io) do\n      yield\n    end\n  end\n\n  private def colorize(type : TokenType, token)\n    if color = colors[type]?\n      @colorize.fore(color).surround(@io) do\n        @io << token\n      end\n    else\n      @io << token\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/syntax_highlighter/html.cr",
    "content": "require \"../syntax_highlighter\"\nrequire \"html\"\n\n# A syntax highlighter that renders Crystal source code with HTML markup.\n#\n# NOTE: To use `Crystal::SyntaxHighlighter::HTML`, you must explicitly import it with `require \"crystal/syntax_highlighter/html\"`\n#\n# ```\n# require \"crystal/syntax_highlighter/html\"\n#\n# code = %(foo = bar(\"baz\\#{PI + 1}\") # comment)\n# html = Crystal::SyntaxHighlighter::HTML.highlight(code)\n# html # => \"foo <span class=\\\"o\\\">=</span> bar(<span class=\\\"s\\\">&quot;baz</span><span class=\\\"i\\\">\\#{</span><span class=\\\"t\\\">PI</span> <span class=\\\"o\\\">+</span> <span class=\\\"n\\\">1</span><span class=\\\"i\\\">}</span><span class=\\\"s\\\">&quot;</span>) <span class=\\\"c\\\"># comment</span>\"\n# ```\nclass Crystal::SyntaxHighlighter::HTML < Crystal::SyntaxHighlighter\n  # Highlights *code* and writes the result to *io*.\n  def self.highlight(io : IO, code : String)\n    new(io).highlight(code)\n  end\n\n  # Highlights *code* and returns the result.\n  def self.highlight(code : String)\n    String.build do |io|\n      highlight(io, code)\n    end\n  end\n\n  # Highlights *code* or returns unhighlighted *code* on error.\n  #\n  # Same as `.highlight(code : String)` except that any error is rescued and\n  # returns unhighlighted source code.\n  def self.highlight!(code : String)\n    highlight(code)\n  rescue\n    ::HTML.escape(code)\n  end\n\n  # Creates a new instance of an HTML syntax highlighter.\n  #\n  # Appends highlighted output (when calling `#highlight`) to *io*.\n  def initialize(@io : IO)\n  end\n\n  def render(type : TokenType, value : String)\n    case type\n    when .comment?\n      span \"c\" { ::HTML.escape(value, @io) }\n    when .number?\n      span \"n\", &.print value\n    when .char?\n      span \"s\" { ::HTML.escape(value, @io) }\n    when .symbol?\n      span \"n\" { ::HTML.escape(value, @io) }\n    when .const?\n      span \"t\", &.print value\n    when .string?\n      span \"s\" { ::HTML.escape(value, @io) }\n    when .ident?\n      span \"m\" { ::HTML.escape(value, @io) }\n    when .keyword?, .self?\n      span \"k\", &.print value\n    when .primitive_literal?\n      span \"n\", &.print value\n    when .operator?\n      span \"o\" { ::HTML.escape(value, @io) }\n    else\n      ::HTML.escape(value, @io)\n    end\n  end\n\n  def render_delimiter(&)\n    span \"s\" do\n      yield\n    end\n  end\n\n  def render_interpolation(&)\n    span_end\n    span \"i\", &.print \"\\#{\"\n    yield\n    span \"i\", &.print \"}\"\n    span_start \"s\"\n  end\n\n  def render_string_array(&)\n    span \"s\" do\n      yield\n    end\n  end\n\n  private def span(klass, &)\n    span_start klass\n    yield @io\n    span_end\n  end\n\n  private def span_start(klass)\n    @io << %(<span class=\")\n    @io << klass\n    @io << %(\">)\n  end\n\n  private def span_end\n    @io << %(</span>)\n  end\nend\n"
  },
  {
    "path": "src/crystal/syntax_highlighter.cr",
    "content": "require \"html\"\n{% unless flag?(:docs) %}\n  require \"compiler/crystal/syntax\"\n{% end %}\n\nabstract class Crystal::SyntaxHighlighter\n  # Parses *code* as Crystal source code and processes it.\n  def highlight(code : String)\n    lexer = Lexer.new(code)\n    lexer.comments_enabled = true\n    lexer.count_whitespace = true\n    lexer.wants_raw = true\n\n    highlight_normal_state lexer\n  end\n\n  # Renders *token* with text *value*.\n  abstract def render(type : TokenType, value : String)\n\n  # Renders a delimiter sequence.\n  abstract def render_delimiter(&)\n\n  # Renders an interpolation sequence.\n  abstract def render_interpolation(&)\n\n  # Renders a string array sequence.\n  abstract def render_string_array(&)\n\n  # Describes the type of a highlighter token.\n  enum TokenType\n    NEWLINE\n    SPACE\n    COMMENT\n    NUMBER\n    CHAR\n    SYMBOL\n    CONST\n    STRING\n    IDENT\n    KEYWORD\n    SELF\n    PRIMITIVE_LITERAL\n    OPERATOR\n    DELIMITER_START\n    DELIMITED_TOKEN\n    DELIMITER_END\n    INTERPOLATION\n    STRING_ARRAY_START\n    STRING_ARRAY_TOKEN\n    STRING_ARRAY_END\n    UNDERSCORE\n    UNKNOWN\n  end\n\n  private def consume_space_or_newline(lexer)\n    while true\n      char = lexer.current_char\n      case char\n      when '\\n'\n        lexer.next_char\n        lexer.incr_line_number 1\n        render :NEWLINE, \"\\n\"\n      when .ascii_whitespace?\n        lexer.next_char\n        render :SPACE, char.to_s\n      else\n        break\n      end\n    end\n  end\n\n  private def slash_is_not_regex(last_token_type type, space_before)\n    case type\n    when Nil\n      false\n    when .number?, .const?, .instance_var?, .class_var?, .op_rparen?, .op_rsquare?, .op_rcurly?\n      true\n    when .op_lparen?, .op_lsquare?, .op_lcurly?\n      false\n    else\n      !space_before\n    end\n  end\n\n  private def highlight_normal_state(lexer, break_on_rcurly = false)\n    last_is_def = false\n    heredoc_stack = [] of Token\n    last_token_type = nil\n    space_before = false\n\n    while true\n      previous_delimiter_state = lexer.token.delimiter_state\n\n      token = lexer.next_token\n\n      case token.type\n      when .delimiter_start?\n        case\n        when last_is_def && token.raw == \"`\"\n          render :IDENT, token.raw # colorize 'def `'\n        when last_is_def && token.raw == \"/\"\n          render :IDENT, token.raw # colorize 'def /'\n\n          if lexer.current_char == '/'\n            render :IDENT, \"/\" # colorize 'def //'\n            lexer.reader.next_char if lexer.reader.has_next?\n          end\n        when token.raw == \"/\" && slash_is_not_regex(last_token_type, space_before)\n          render :OPERATOR, token.raw\n        when token.delimiter_state.kind.heredoc?\n          heredoc_stack << token.dup\n          highlight_token token, last_is_def\n        else\n          highlight_delimiter_state lexer, token\n          token.delimiter_state = previous_delimiter_state\n        end\n      when .string_array_start?, .symbol_array_start?\n        highlight_string_array lexer, token\n      when .op_rcurly?\n        break if break_on_rcurly\n\n        highlight_token token, last_is_def\n      when .eof?\n        break\n      else\n        highlight_token token, last_is_def\n      end\n\n      case token.type\n      when .newline?\n        heredoc_stack.each_with_index do |token, i|\n          highlight_delimiter_state lexer, token, heredoc: true\n          unless i == heredoc_stack.size - 1\n            # Next token to heredoc's end is either NEWLINE or EOF.\n            token = lexer.next_token\n\n            case token.type\n            when .eof?\n              raise \"Unterminated heredoc\"\n            else\n              highlight_token token, last_is_def\n            end\n          end\n        end\n        heredoc_stack.clear\n      when .ident?\n        if last_is_def\n          last_is_def = false\n        end\n      end\n\n      unless token.type.space?\n        last_token_type = token.type\n        last_is_def = token.keyword? :def\n      end\n      space_before = token.type.space?\n    end\n  end\n\n  private def highlight_token(token : Token, last_is_def)\n    case token.type\n    when .newline?\n      render :NEWLINE, \"\\n\"\n    when .space?\n      render :SPACE, token.value.to_s\n    when .comment?\n      render :COMMENT, token.value.to_s\n    when .number?\n      render :NUMBER, token.raw\n    when .char?\n      render :CHAR, token.raw\n    when .symbol?\n      render :SYMBOL, token.raw\n    when .delimiter_start?\n      render :STRING, token.raw\n    when .const?, .op_colon_colon?\n      render :CONST, token.to_s\n    when .ident?\n      if last_is_def\n        render :IDENT, token.to_s\n      else\n        case token.value\n        when Keyword::TRUE, Keyword::FALSE, Keyword::NIL\n          render :PRIMITIVE_LITERAL, token.to_s\n        when Keyword::SELF\n          render :SELF, token.to_s\n        when Keyword\n          render :KEYWORD, token.to_s\n        else\n          render :UNKNOWN, token.to_s\n        end\n      end\n    when .op_lparen?, .op_rparen?, .op_lsquare?, .op_rsquare?, .op_lcurly?, .op_rcurly?, .op_at_lsquare?, # ( ) { } [ ] @[\n         .op_comma?, .op_period?, .op_period_period?, .op_period_period_period?,                          # , . .. ...\n         .op_colon?, .op_semicolon?, .op_question?, .op_dollar_question?, .op_dollar_tilde?               # : ; ? $? $~\n      # Operators that should not be colorized\n      render :UNKNOWN, token.to_s\n    when .op_lsquare_rsquare?, .op_lsquare_rsquare_question?, .op_lsquare_rsquare_eq?, .op_lt_eq_gt?,        # [] []? []= <=>\n         .op_plus?, .op_minus?, .op_star?, .op_slash?, .op_slash_slash?,                                     # + - * / //\n         .op_eq_eq?, .op_lt?, .op_lt_eq?, .op_gt?, .op_gt_eq?, .op_bang_eq?, .op_eq_tilde?, .op_bang_tilde?, # == < <= > >= != =~ !~\n         .op_amp?, .op_bar?, .op_caret?, .op_tilde?, .op_star_star?, .op_gt_gt?, .op_lt_lt?, .op_percent?    # & | ^ ~ ** >> << %\n      # Operators acceptable in def\n      if last_is_def\n        render :IDENT, token.to_s\n      else\n        render :OPERATOR, token.to_s\n      end\n    when .operator?\n      # Colorize any other operator\n      render :OPERATOR, token.to_s\n    when .underscore?\n      render :UNDERSCORE, \"_\"\n    when .global_match_data_index?\n      render :UNKNOWN, \"$\" + token.value.to_s\n    else\n      render :UNKNOWN, token.to_s\n    end\n  end\n\n  private def highlight_delimiter_state(lexer, token, *, heredoc = false)\n    render_delimiter do\n      render :DELIMITER_START, token.raw unless heredoc\n      while true\n        token = lexer.next_string_token(token.delimiter_state)\n        case token.type\n        when .delimiter_end?\n          render :DELIMITER_END, token.raw\n          break\n        when .interpolation_start?\n          render_interpolation do\n            highlight_normal_state lexer, break_on_rcurly: true\n          end\n        else\n          render :DELIMITED_TOKEN, token.raw\n        end\n      end\n    end\n  end\n\n  private def highlight_string_array(lexer, token)\n    render_string_array do\n      render :STRING_ARRAY_START, token.raw\n      while true\n        consume_space_or_newline(lexer)\n        token = lexer.next_string_array_token\n        case token.type\n        when .string?\n          render :STRING_ARRAY_TOKEN, token.raw\n        when .string_array_end?\n          render :STRING_ARRAY_END, token.raw\n          break\n        when .eof?\n          if token.delimiter_state.kind.string_array?\n            raise \"Unterminated string array literal\"\n          else # .symbol_array?\n            raise \"Unterminated symbol array literal\"\n          end\n        else\n          raise \"BUG: Shouldn't happen\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/addrinfo.cr",
    "content": "module Crystal::System::Addrinfo\n  # alias Handle\n\n  # protected def initialize(addrinfo : Handle)\n\n  # def system_ip_address : ::Socket::IPAddress\n\n  # def self.getaddrinfo(domain, service, family, type, protocol, timeout) : Handle\n\n  # def self.next_addrinfo(addrinfo : Handle) : Handle\n\n  # def self.free_addrinfo(addrinfo : Handle)\n\n  def self.getaddrinfo(domain, service, family, type, protocol, timeout, flags = 0, & : ::Socket::Addrinfo ->)\n    addrinfo = root = getaddrinfo(domain, service, family, type, protocol, timeout, flags)\n\n    begin\n      while addrinfo\n        yield ::Socket::Addrinfo.new(addrinfo)\n        addrinfo = next_addrinfo(addrinfo)\n      end\n    ensure\n      free_addrinfo(root)\n    end\n  end\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/addrinfo\"\n{% elsif flag?(:unix) %}\n  require \"./unix/addrinfo\"\n{% elsif flag?(:win32) %}\n  {% if flag?(:win7) %}\n    require \"./win32/addrinfo_win7\"\n  {% else %}\n    require \"./win32/addrinfo\"\n  {% end %}\n{% else %}\n  {% raise \"No Crystal::System::Addrinfo implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/dir.cr",
    "content": "# :nodoc:\nmodule Crystal::System::Dir\n  # :nodoc:\n  #\n  # Information about a directory entry.\n  #\n  # In particular we only care about the name, whether it's a directory, and\n  # whether any hidden file attributes are set to improve the performance of\n  # `Dir.glob` by not having to call `File.info` on every directory entry.\n  # If dir is nil, the type is unknown.\n  # In the future we might change Dir's API to expose these entries\n  # with more info but right now it's not necessary.\n  struct Entry\n    getter name : String\n    getter? dir : Bool?\n    getter? native_hidden : Bool\n    getter? os_hidden : Bool\n\n    def initialize(@name, @dir, @native_hidden, @os_hidden = false)\n    end\n  end\n\n  # Returns a new handle to an iterator of entries inside *path*.\n  # def self.open(path : String) : Handle\n\n  # Returns the next directory entry name in the iterator represented by *handle*, or\n  # `nil` if iteration is complete.\n  def self.next(dir, path) : String?\n    next_entry(dir, path).try &.name\n  end\n\n  # Returns the next directory entry in the iterator represented by *handle*, or\n  # `nil` if iteration is complete.\n  # def self.next_entry(handle : Handle) : Entry?\n\n  # Rewinds the iterator to the beginning of the directory.\n  # def self.rewind(handle : Handle) : Nil\n\n  # Closes *handle*, freeing its resources.\n  # def self.close(handle : Handle) : Nil\n\n  # Returns the current working directory of the application.\n  # def self.current : String\n\n  # Sets the current working directory of the application.\n  # def self.current=(path : String)\n\n  # Creates a new directory at *path*. The UNIX-style directory mode *node*\n  # must be applied.\n  # def self.create(path : String, mode : Int32) : Nil\n\n  # Deletes the directory at *path*.\n  # def self.delete(path : String) : Nil\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/dir\"\n{% elsif flag?(:unix) %}\n  require \"./unix/dir\"\n{% elsif flag?(:win32) %}\n  require \"./win32/dir\"\n{% else %}\n  {% raise \"No implementation of Crystal::System::Dir available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/env.cr",
    "content": "module Crystal::System::Env\n  # Sets an environment variable or unsets it if *value* is `nil`.\n  # def self.set(key : String, value : String?) : Nil\n\n  # Gets an environment variable.\n  # def self.get(key : String) : String?\n\n  # Returns `true` if environment variable is set.\n  # def self.has_key?(key : String) : Bool\n\n  # Iterates all environment variables.\n  # def self.each(&block : String, String ->)\nend\n\n{% if flag?(:unix) %}\n  require \"./unix/env\"\n{% elsif flag?(:win32) %}\n  require \"./win32/env\"\n{% else %}\n  {% raise \"No Crystal::System::Env implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/fiber.cr",
    "content": "module Crystal::System::Fiber\n  # Allocates memory for a stack.\n  # def self.allocate_stack(stack_size : Int, protect : Bool) : Void*\n\n  # Prepares an existing, unused stack for use again.\n  # def self.reset_stack(stack : Void*, stack_size : Int, protect : Bool) : Nil\n\n  # Frees memory of a stack.\n  # def self.free_stack(stack : Void*, stack_size : Int) : Nil\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/fiber\"\n{% elsif flag?(:unix) %}\n  require \"./unix/fiber\"\n{% elsif flag?(:win32) %}\n  require \"./win32/fiber\"\n{% else %}\n  {% raise \"Fiber not supported\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/file.cr",
    "content": "# :nodoc:\nmodule Crystal::System::File\n  # def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions, blocking : Bool?) : FileDescriptor::Handle\n\n  # Helper method for calculating file open modes on systems with posix-y `open`\n  # calls.\n  private def self.open_flag(mode)\n    if mode.size == 0\n      raise \"No file open mode specified\"\n    end\n\n    m = 0\n    o = 0\n    case mode[0]\n    when 'r'\n      m = LibC::O_RDONLY\n    when 'w'\n      m = LibC::O_WRONLY\n      o = LibC::O_CREAT | LibC::O_TRUNC\n    when 'a'\n      m = LibC::O_WRONLY\n      o = LibC::O_CREAT | LibC::O_APPEND\n    else\n      raise \"Invalid file open mode: '#{mode}'\"\n    end\n\n    case mode.size\n    when 1\n      # Nothing\n    when 2\n      case mode[1]\n      when '+'\n        m = LibC::O_RDWR\n      when 'b'\n        # Nothing\n      else\n        raise \"Invalid file open mode: '#{mode}'\"\n      end\n    when 3\n      # POSIX allows both `+b` and `b+`: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html\n      unless mode.ends_with?(\"+b\") || mode.ends_with?(\"b+\")\n        raise \"Invalid file open mode: '#{mode}'\"\n      end\n      m = LibC::O_RDWR\n    else\n      raise \"Invalid file open mode: '#{mode}'\"\n    end\n\n    m | o\n  end\n\n  LOWER_ALPHANUM = \"0123456789abcdefghijklmnopqrstuvwxyz\".to_slice\n\n  def self.mktemp(prefix : String?, suffix : String?, dir : String, random : ::Random? = nil) : {FileDescriptor::Handle, String, Bool}\n    flags = LibC::O_RDWR | LibC::O_CREAT | LibC::O_EXCL\n    perm = ::File::Permissions.new(0o600)\n\n    prefix = ::File.join(dir, prefix || \"\")\n    bytesize = prefix.bytesize + 8 + (suffix.try(&.bytesize) || 0)\n\n    100.times do\n      path = String.build(bytesize) do |io|\n        io << prefix\n        8.times do\n          io.write_byte LOWER_ALPHANUM.sample(random)\n        end\n        io << suffix\n      end\n\n      case result = EventLoop.current.open(path, flags, perm, blocking: true)\n      when Tuple(FileDescriptor::Handle, Bool)\n        fd, blocking = result\n        return {fd, path, blocking}\n      else\n        if ::File::AlreadyExistsError.os_error?(result)\n          # retry\n        else\n          raise ::File::Error.from_os_error(\"Error creating temporary file\", result, file: path)\n        end\n      end\n    end\n\n    raise ::File::AlreadyExistsError.new(\"Error creating temporary file\", file: \"#{prefix}********#{suffix}\")\n  end\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/file\"\n{% elsif flag?(:unix) %}\n  require \"./unix/file\"\n{% elsif flag?(:win32) %}\n  require \"./win32/file\"\n{% else %}\n  {% raise \"No Crystal::System::File implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/file_descriptor.cr",
    "content": "# :nodoc:\nmodule Crystal::System::FileDescriptor\n  # Enables input echoing if *enable* is true, disables it otherwise.\n  # private def system_echo(enable : Bool)\n\n  # For the duration of the block, enables input echoing if *enable* is true,\n  # disables it otherwise.\n  # private def system_echo(enable : Bool, & : ->)\n\n  # Enables raw mode if *enable* is true, enables cooked mode otherwise.\n  # private def system_raw(enable : Bool)\n\n  # For the duration of the block, enables raw mode if *enable* is true, enables\n  # cooked mode otherwise.\n  # private def system_raw(enable : Bool, & : ->)\n\n  # Closes the internal file descriptor without notifying the event loop.\n  # This is directly used after the fork of a process to close the\n  # parent's Crystal::System::Signal.@@pipe reference before re initializing\n  # the event loop. In the case of a fork that will exec there is even\n  # no need to initialize the event loop at all.\n  # Also used in `IO::FileDescriptor#finalize`.\n  # def file_descriptor_close\n\n  # Returns `true` or `false` if this file descriptor pretends to block or not\n  # to block the caller thread regardless of the underlying internal file\n  # descriptor's implementation. Returns `nil` if nothing needs to be done, i.e.\n  # `#blocking` is identical to `#system_blocking?`.\n  #\n  # Currently used by console STDIN on Windows.\n  private def emulated_blocking? : Bool?\n  end\n\n  private def system_read(slice : Bytes) : Int32\n    event_loop.read(self, slice)\n  end\n\n  private def system_write(slice : Bytes) : Int32\n    event_loop.write(self, slice)\n  end\n\n  private def event_loop? : Crystal::EventLoop::FileDescriptor?\n    Crystal::EventLoop.current?\n  end\n\n  private def event_loop : Crystal::EventLoop::FileDescriptor\n    Crystal::EventLoop.current\n  end\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/file_descriptor\"\n{% elsif flag?(:unix) %}\n  require \"./unix/file_descriptor\"\n{% elsif flag?(:win32) %}\n  require \"./win32/file_descriptor\"\n{% else %}\n  {% raise \"No Crystal::System::FileDescriptor implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/file_info.cr",
    "content": "# :nodoc:\nmodule Crystal::System::FileInfo\n  # Size of the file, in bytes.\n  # def system_size : Int64\n\n  # The permissions of the file.\n  # def system_permissions : Permissions\n\n  # The type of the file.\n  # def system_type : Type\n\n  # The special flags this file has set.\n  # def system_flags : Flags\n\n  # The last time this file was modified.\n  # def system_modification_time : Time\n\n  # The user ID that the file belongs to.\n  # def system_owner_id : String\n\n  # The group ID that the file belongs to.\n  # def system_group_id : String\n\n  # Returns true if this `FileInfo` and *other* are of the same file.\n  # def system_same_file?(other : self) : Bool\nend\n\n{% if flag?(:unix) %}\n  require \"./unix/file_info\"\n{% elsif flag?(:win32) %}\n  require \"./win32/file_info\"\n{% else %}\n  {% raise \"No Crystal::System::FileInfo implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/group.cr",
    "content": "module Crystal::System::Group\n  # def system_name : String\n\n  # def system_id : String\n\n  # def self.from_name?(groupname : String) : ::System::Group?\n\n  # def self.from_id?(groupid : String) : ::System::Group?\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/group\"\n{% elsif flag?(:unix) %}\n  require \"./unix/group\"\n{% elsif flag?(:win32) %}\n  require \"./win32/group\"\n{% else %}\n  {% raise \"No Crystal::System::Group implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/mime.cr",
    "content": "module Crystal::System::MIME\n  # Load MIME types from operating system source.\n  # def self.load\nend\n\n{% if flag?(:unix) %}\n  require \"./unix/mime\"\n{% elsif flag?(:win32) %}\n  require \"./win32/mime\"\n{% else %}\n  {% raise \"No Crystal::System::Mime implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/panic.cr",
    "content": "module Crystal::System\n  # Prints a system error message on the standard error then exits with an error\n  # status.\n  #\n  # You should always prefer raising an exception, built with\n  # `RuntimeError.from_os_error` for example, but there are a few cases where we\n  # can't allocate any memory (e.g. stop the world) and still need to fail when\n  # reaching a system error.\n  def self.panic(syscall_name : String, error : Errno | WinError | WasiError) : NoReturn\n    System.print_error(\"%s failed with \", syscall_name)\n    error.unsafe_message { |slice| System.print_error(slice) }\n    System.print_error(\" (%s)\\n\", error.to_s)\n\n    LibC._exit(1)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/path.cr",
    "content": "module Crystal::System::Path\n  # Returns the path of the home directory of the current user.\n  # def self.home : String\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/path\"\n{% elsif flag?(:unix) %}\n  require \"./unix/path\"\n{% elsif flag?(:win32) %}\n  require \"./win32/path\"\n{% else %}\n  {% raise \"No Crystal::System::Path implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/print_error.cr",
    "content": "module Crystal::System\n  # Prints directly to stderr without going through an `IO::FileDescriptor`.\n  # This is useful for error messages from components that are required for\n  # IO to work (fibers, scheduler, event_loop).\n  def self.print_error(message, *args)\n    printf(message, *args) { |bytes| print_error(bytes) }\n  end\n\n  def self.print_error(bytes : Bytes) : Nil\n    {% if flag?(:unix) || flag?(:wasm32) %}\n      LibC.write 2, bytes, bytes.size\n    {% elsif flag?(:win32) %}\n      LibC.WriteFile(LibC.GetStdHandle(LibC::STD_ERROR_HANDLE), bytes, bytes.size, out _, nil)\n    {% end %}\n  end\n\n  # Print a UTF-16 slice as UTF-8 directly to stderr. Useful on Windows to print\n  # strings returned from the unicode variant of the Win32 API.\n  def self.print_error(bytes : Slice(UInt16)) : Nil\n    utf8 = uninitialized UInt8[512]\n    appender = utf8.to_unsafe.appender\n\n    String.each_utf16_char(bytes) do |char|\n      if appender.size > utf8.size - char.bytesize\n        # buffer is full (char won't fit)\n        print_error appender.to_slice\n        appender = utf8.to_unsafe.appender\n      end\n\n      char.each_byte do |byte|\n        appender << byte\n      end\n    end\n\n    if appender.size > 0\n      print_error appender.to_slice\n    end\n  end\n\n  def self.print(handle : FileDescriptor::Handle, bytes : Bytes) : Nil\n    {% if flag?(:unix) || flag?(:wasm32) %}\n      LibC.write handle, bytes, bytes.size\n    {% elsif flag?(:win32) %}\n      LibC.WriteFile(Pointer(FileDescriptor::Handle).new(handle), bytes, bytes.size, out _, nil)\n    {% end %}\n  end\n\n  # Minimal drop-in replacement for C `printf` function. Yields successive\n  # non-empty `Bytes` to the block, which should do the actual printing.\n  #\n  # *format* only supports the `%(l|ll)?[dpsux]` format specifiers; more should\n  # be added only when we need them or an external library passes its own format\n  # string to here. *format* must also be null-terminated.\n  #\n  # Since this method may be called under low memory conditions or even with a\n  # corrupted heap, its implementation should be as low-level as possible,\n  # avoiding memory allocations.\n  #\n  # NOTE: Crystal's `printf` only supports a subset of C's `printf` format specifiers.\n  # NOTE: MSVC uses `%X` rather than `0x%x`, we follow the latter on all platforms.\n  def self.printf(format, *args, &)\n    format = to_string_slice(format)\n    format_len = format.size\n    ptr = format.to_unsafe\n    finish = ptr + format_len\n    arg_index = 0\n\n    # The widest integer types supported by the format specifier are `%lld` and\n    # `%llu`, which do not exceed 64 bits, so we only need 20 digits maximum\n    # note that `chars` does not have to be null-terminated, since we are\n    # only yielding a `Bytes`\n    int_chars = uninitialized UInt8[20]\n\n    while ptr < finish\n      next_percent = ptr\n      while next_percent < finish && !(next_percent.value === '%')\n        next_percent += 1\n      end\n      unless next_percent == ptr\n        yield Slice.new(ptr, next_percent - ptr)\n      end\n\n      fmt_ptr = next_percent + 1\n      width = 0\n      if fmt_ptr.value === 'l'\n        width = 1\n        fmt_ptr += 1\n        if fmt_ptr.value === 'l'\n          width = 2\n          fmt_ptr += 1\n        end\n      end\n\n      break unless fmt_ptr < finish\n\n      case fmt_ptr.value\n      when 's'\n        read_arg(String | Pointer(UInt8)) do |arg|\n          yield to_string_slice(arg)\n        end\n      when 'd'\n        read_arg(Int::Primitive) do |arg|\n          yield to_int_slice(int_chars.to_slice, arg, 10, true, width)\n        end\n      when 'u'\n        read_arg(Int::Primitive) do |arg|\n          yield to_int_slice(int_chars.to_slice, arg, 10, false, width)\n        end\n      when 'x'\n        read_arg(Int::Primitive) do |arg|\n          yield to_int_slice(int_chars.to_slice, arg, 16, false, width)\n        end\n      when 'p'\n        read_arg(Pointer(Void)) do |arg|\n          yield \"0x\".to_slice\n          yield to_int_slice(int_chars.to_slice, arg.address, 16, false, 2)\n        end\n      else\n        yield Slice.new(next_percent, fmt_ptr + 1 - next_percent)\n      end\n\n      ptr = fmt_ptr + 1\n    end\n  end\n\n  private macro read_arg(type, &block)\n    {{ block.args[0] }} = args[arg_index]\n    if {{ block.args[0] }}.is_a?({{ type }})\n      {{ block.body }}\n    else\n      yield \"(???)\".to_slice\n    end\n    arg_index += 1\n  end\n\n  private def self.to_string_slice(str)\n    if str.is_a?(UInt8*)\n      if str.null?\n        \"(null)\".to_slice\n      else\n        Slice.new(str, LibC.strlen(str))\n      end\n    else\n      str.to_s.to_slice\n    end\n  end\n\n  # simplified version of `Int#internal_to_s`\n  protected def self.to_int_slice(buf, num, base, signed, width)\n    if num == 0\n      \"0\".to_slice\n    else\n      # NOTE: do not factor out `num`! it is written this way to inhibit\n      # unnecessary union dispatches\n      case {signed, width}\n      when {true, 2}  then to_int_slice_impl(buf, LibC::LongLong.new!(num), base)\n      when {true, 1}  then to_int_slice_impl(buf, LibC::Long.new!(num), base)\n      when {true, 0}  then to_int_slice_impl(buf, LibC::Int.new!(num), base)\n      when {false, 2} then to_int_slice_impl(buf, LibC::ULongLong.new!(num), base)\n      when {false, 1} then to_int_slice_impl(buf, LibC::ULong.new!(num), base)\n      else                 to_int_slice_impl(buf, LibC::UInt.new!(num), base)\n      end\n    end\n  end\n\n  private def self.to_int_slice_impl(buf, num, base)\n    ptr_end = buf.to_unsafe + buf.size\n    ptr = ptr_end\n\n    neg = num < 0\n\n    # do not assume Crystal constant initialization succeeds, hence not `DIGITS`\n    digits = \"0123456789abcdef\".to_unsafe\n\n    while num != 0\n      ptr -= 1\n      ptr.value = digits[num.remainder(base).abs]\n      num = num.tdiv(base)\n    end\n\n    if neg\n      ptr -= 1\n      ptr.value = '-'.ord.to_u8\n    end\n\n    Slice.new(ptr, ptr_end - ptr)\n  end\n\n  def self.print_exception(message, ex)\n    print_error \"%s: %s (%s)\\n\", message, ex.message || \"(no message)\", ex.class.name\n    begin\n      if bt = ex.backtrace?\n        bt.each do |frame|\n          print_error \"  from %s\\n\", frame\n        end\n      else\n        print_error \"  (no backtrace)\\n\"\n      end\n    rescue ex\n      print_error \"Error while trying to dump the backtrace: %s (%s)\\n\", ex.message || \"(no message)\", ex.class.name\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/process.cr",
    "content": "# :nodoc:\nstruct Crystal::System::Process\n  # Implementation-dependent \"conceptual\" data types (they only need to match across arg types and return types):\n  # * ProcessInformation: The system-dependent value with enough information to keep track of a running process.\n  #   Could be the PID or similar.\n  # * Args: The system-native way to specify an executable with its command line arguments.\n  #   Could be an array of strings or a single string.\n\n  # Creates a structure representing a running process based on its ID.\n  # def self.new(pi : ProcessInformation)\n\n  # Releases any resources acquired by this structure.\n  # def release\n\n  # Returns the PID of the running process.\n  # def pid : Int\n\n  # Waits until the process finishes and returns its status code\n  # def wait : Int\n\n  # Whether the process is still registered in the system.\n  # def exists? : Bool\n\n  # Asks this process to terminate.\n  # def terminate(*, graceful)\n\n  # Terminates the current process immediately.\n  # def self.exit(status : Int)\n\n  # Returns the process identifier of the current process.\n  # def self.pid : Int\n\n  # Returns the process group identifier of the current process.\n  # def self.pgid : Int\n\n  # Returns the process group identifier of the process identified by *pid*.\n  # def self.pgid(pid) : Int\n\n  # Returns the process identifier of the parent process of the current process.\n  # def self.ppid : Int\n\n  # Sends a *signal* to the processes identified by the given *pids*.\n  # def self.signal(pid : Int, signal : Int)\n\n  # Installs *handler* as the new handler for interrupt requests. Removes any\n  # previously set interrupt handler.\n  # def self.on_interrupt(&handler : ->)\n\n  # Installs *handler* as the new handler for termination signals. Removes any\n  # previously set handler.\n  # def self.on_terminate(&handler : ::Process::ExitReason ->)\n\n  # Ignores all interrupt requests. Removes any custom interrupt handler set\n  # def self.ignore_interrupts!\n\n  # Restores default handling of interrupt requests.\n  # def self.restore_interrupts!\n\n  # Returns whether a debugger is attached to the current process.\n  # def self.debugger_present? : Bool\n\n  # Spawns a fiber responsible for executing interrupt handlers on the main\n  # thread.\n  # def self.start_interrupt_loop\n\n  # Whether the process identified by *pid* is still registered in the system.\n  # def self.exists?(pid : Int) : Bool\n\n  # Measures CPU times.\n  # def self.times : ::Process::Tms\n\n  # Duplicates the current process.\n  # def self.fork : ProcessInformation\n  # def self.fork(&)\n\n  # def prepare_args(command : String, args : Enumerable(String)?, shell : Bool) : Args\n  # def prepare_args(args : Enumerable(String)) : Args\n\n  # Launches a child process with the command + args.\n  # def self.spawn(prepared_args : Args, shell : Bool, env : Env?, clear_env : Bool, input : Stdio, output : Stdio, error : Stdio, chdir : Path | String?) : ProcessInformation\n\n  # Replaces the current process with a new one.\n  # def self.replace(command : String, args : Enumerable(String)?, shell : Bool, env : Env?, clear_env : Bool, input : Stdio, output : Stdio, error : Stdio, chdir : Path | String?) : NoReturn\n\n  # Changes the root directory for the current process.\n  # def self.chroot(path : String)\nend\n\nmodule Crystal::System\n  ORIGINAL_STDIN  = IO::FileDescriptor.new(handle: Crystal::System::FileDescriptor::STDIN_HANDLE, blocking: true)\n  ORIGINAL_STDOUT = IO::FileDescriptor.new(handle: Crystal::System::FileDescriptor::STDOUT_HANDLE, blocking: true)\n  ORIGINAL_STDERR = IO::FileDescriptor.new(handle: Crystal::System::FileDescriptor::STDERR_HANDLE, blocking: true)\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/process\"\n{% elsif flag?(:unix) %}\n  require \"./unix/process\"\n{% elsif flag?(:win32) %}\n  require \"./win32/process\"\n{% else %}\n  {% raise \"No Crystal::System::Process implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/random.cr",
    "content": "module Crystal::System::Random\n  # Fills *buffer* with random bytes from a secure source.\n  # def self.random_bytes(buffer : Bytes) : Nil\n\n  # Returns a random unsigned integer from a secure source. Implementations\n  # may choose the integer size to return based on what the system source\n  # provides. They may choose to return a single byte (UInt8) in which case\n  # `::Random` will prefer `#random_bytes` to read as many bytes as required\n  # at once, avoiding multiple reads or reading too many bytes.\n  # def self.next_u\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/random\"\n{% elsif flag?(:bsd) || flag?(:darwin) %}\n  require \"./unix/arc4random\"\n{% elsif flag?(:linux) && (!flag?(:android) || LibC::ANDROID_API >= 28) %}\n  require \"./unix/getrandom\"\n{% elsif flag?(:unix) %}\n  require \"./unix/urandom\"\n{% elsif flag?(:win32) %}\n  require \"./win32/random\"\n{% else %}\n  {% raise \"No Crystal::System::Random implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/signal.cr",
    "content": "module Crystal::System::Signal\n  # Sets the handler for this signal to the passed function.\n  # def self.trap(signal, handler) : Nil\n\n  # Returns any existing handler set on the signal\n  # def self.trap_handler?(signal)\n\n  # Resets the handler for this signal to the OS default.\n  # def self.reset(signal) : Nil\n\n  # Clears the handler for this signal and prevents the OS default action.\n  # def self.ignore(signal) : Nil\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/signal\"\n{% elsif flag?(:unix) %}\n  require \"./unix/signal\"\n{% elsif flag?(:win32) %}\n  require \"./win32/signal\"\n{% else %}\n  {% raise \"No Crystal::System::Signal implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/socket.cr",
    "content": "require \"../event_loop/socket\"\n\nmodule Crystal::System::Socket\n  # Initializes a file descriptor / socket handle for use with Crystal Socket\n  # private def initialize_handle(fd)\n\n  private def system_connect(addr, timeout = nil)\n    event_loop.connect(self, addr, timeout)\n  end\n\n  # Tries to bind the socket to a local address.\n  # Returns a `Socket::BindError` on failure.\n  # private def system_bind(addr, addrstr) : ::Socket::BindError?\n\n  # Returns a `Socket::Error` on failure.\n  # private def system_listen(backlog) : ::Socket::Error?\n\n  private def system_accept : {Handle, Bool}?\n    event_loop.accept(self)\n  end\n\n  private def system_send_to(bytes : Bytes, addr : ::Socket::Address)\n    event_loop.send_to(self, bytes, addr)\n  end\n\n  private def system_receive_from(bytes : Bytes) : Tuple(Int32, ::Socket::Address)\n    event_loop.receive_from(self, bytes)\n  end\n\n  # private def system_close_read\n\n  # private def system_close_write\n\n  # private def system_send_buffer_size : Int\n\n  # private def system_send_buffer_size=(val : Int)\n\n  # private def system_recv_buffer_size : Int\n\n  # private def system_recv_buffer_size=(val : Int)\n\n  # private def system_reuse_address? : Bool\n\n  # private def system_reuse_address=(val : Bool)\n\n  # private def system_reuse_port? : Bool\n\n  # private def system_reuse_port=(val : Bool)\n\n  # private def system_broadcast? : Bool\n\n  # private def system_broadcast=(val : Bool)\n\n  # private def system_keepalive? : Bool\n\n  # private def system_keepalive=(val : Bool)\n\n  # private def system_linger\n\n  # private def system_linger=(val)\n\n  # private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET, &)\n\n  # private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET)\n\n  # private def system_setsockopt(optname, optval, level = LibC::SOL_SOCKET)\n\n  # private def system_blocking?\n\n  # private def system_blocking=(value)\n\n  # private def system_tty?\n\n  # private def system_close_on_exec?\n\n  # private def system_close_on_exec=(arg : Bool)\n\n  # private def system_fcntl(cmd, arg = 0)\n\n  # def self.fcntl(fd, cmd, arg = 0)\n\n  # def self.socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool) : {Handle, Handle}\n\n  private def system_read(slice : Bytes) : Int32\n    event_loop.read(self, slice)\n  end\n\n  private def system_write(slice : Bytes) : Int32\n    event_loop.write(self, slice)\n  end\n\n  # private def system_close\n\n  # Closes the internal handle without notifying the event loop.\n  # This is directly used after the fork of a process to close the\n  # parent's Crystal::System::Signal.@@pipe reference before re initializing\n  # the event loop. In the case of a fork that will exec there is even\n  # no need to initialize the event loop at all.\n  # Also used in `Socket#finalize`\n  # def socket_close\n\n  private def event_loop? : Crystal::EventLoop::Socket?\n    Crystal::EventLoop.current?\n  end\n\n  private def event_loop : Crystal::EventLoop::Socket\n    Crystal::EventLoop.current\n  end\n\n  # IPSocket:\n\n  # private def system_local_address\n\n  # private def system_remote_address\n\n  # TCPSocket:\n\n  # private def system_tcp_keepalive_idle\n\n  # private def system_tcp_keepalive_idle=(val : Int)\n\n  # private def system_tcp_keepalive_interval\n\n  # private def system_tcp_keepalive_interval=(val : Int)\n\n  # private def system_tcp_keepalive_count\n\n  # private def system_tcp_keepalive_count=(val : Int)\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/socket\"\n{% elsif flag?(:unix) %}\n  require \"./unix/socket\"\n{% elsif flag?(:win32) %}\n  require \"./win32/socket\"\n{% else %}\n  {% raise \"No Crystal::System::Socket implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/thread.cr",
    "content": "# :nodoc:\nmodule Crystal::System::Thread\n  # alias Handle\n\n  # def self.init : Nil\n\n  # def self.new_handle(thread_obj : ::Thread) : Handle\n\n  # def self.current_handle : Handle\n\n  # def self.yield_current : Nil\n\n  # def self.current_thread : ::Thread\n\n  # def self.current_thread? : ::Thread?\n\n  # def self.current_thread=(thread : ::Thread)\n\n  # def self.sleep(time : ::Time::Span) : Nil\n\n  # private def system_join : Exception?\n\n  # private def system_close\n\n  # private def stack_address : Void*\n\n  # private def system_name=(String) : String\n\n  # def self.init_suspend_resume : Nil\n\n  # private def system_suspend : Nil\n\n  # private def system_wait_suspended : Nil\n\n  # private def system_resume : Nil\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/thread\"\n{% elsif flag?(:unix) %}\n  require \"./unix/pthread\"\n{% elsif flag?(:win32) %}\n  require \"./win32/thread\"\n{% else %}\n  {% raise \"Thread not supported\" %}\n{% end %}\n\n# :nodoc:\nclass Thread\n  include Crystal::System::Thread\n\n  # all thread objects, so the GC can see them (it doesn't scan thread locals)\n  @@threads = uninitialized Thread::LinkedList(Thread)\n\n  protected def self.threads : Thread::LinkedList(Thread)\n    @@threads\n  end\n\n  def self.init : Nil\n    @@threads = Thread::LinkedList(Thread).new\n    Crystal::System::Thread.init\n  end\n\n  @system_handle : Crystal::System::Thread::Handle\n  @exception : Exception?\n  @detached = Atomic(Bool).new(false)\n\n  # Returns the Fiber representing the thread's main stack.\n  getter! main_fiber : Fiber\n\n  # Returns the Fiber currently running on the thread.\n  property! current_fiber : Fiber\n\n  # :nodoc:\n  property next : Thread?\n\n  # :nodoc:\n  property previous : Thread?\n\n  getter name : String?\n\n  {% if flag?(:execution_context) %}\n    # :nodoc:\n    getter! execution_context : Fiber::ExecutionContext\n\n    # :nodoc:\n    def execution_context=(@execution_context : Fiber::ExecutionContext?)\n    end\n\n    # :nodoc:\n    getter! scheduler : Fiber::ExecutionContext::Scheduler\n\n    # :nodoc:\n    def scheduler=(@scheduler : Fiber::ExecutionContext::Scheduler?)\n    end\n\n    # When a fiber terminates we can't release its stack until we swap context\n    # to another fiber. We can't free/unmap nor push it to a shared stack pool,\n    # that would result in a segfault.\n    @dead_fiber_stack : Fiber::Stack?\n\n    # :nodoc:\n    def dying_fiber(fiber : Fiber) : Fiber::Stack?\n      stack = @dead_fiber_stack\n      @dead_fiber_stack = fiber.@stack\n      stack\n    end\n\n    # :nodoc:\n    def dead_fiber_stack? : Fiber::Stack?\n      if stack = @dead_fiber_stack\n        @dead_fiber_stack = nil\n        stack\n      end\n    end\n  {% else %}\n    # :nodoc:\n    getter scheduler : Crystal::Scheduler { Crystal::Scheduler.new(self) }\n\n    # :nodoc:\n    def scheduler? : ::Crystal::Scheduler?\n      @scheduler\n    end\n  {% end %}\n\n  def self.unsafe_each(&)\n    # nothing to iterate when @@threads is nil + don't lazily allocate in a\n    # method called from a GC collection callback!\n    @@threads.try(&.unsafe_each { |thread| yield thread })\n  end\n\n  def self.each(&)\n    threads.each { |thread| yield thread }\n  end\n\n  def self.lock : Nil\n    threads.@mutex.lock\n  end\n\n  def self.unlock : Nil\n    threads.@mutex.unlock\n  end\n\n  # Creates and starts a new system thread.\n  def initialize(@name : String? = nil, &@func : Thread ->)\n    @system_handle = uninitialized Crystal::System::Thread::Handle\n    init_handle\n  end\n\n  # Used once to initialize the thread object representing the main thread of\n  # the process (that already exists).\n  def initialize\n    @func = ->(t : Thread) { }\n    @system_handle = Crystal::System::Thread.current_handle\n    @current_fiber = @main_fiber = Fiber.new(stack_address, self)\n\n    Thread.threads.push(self)\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"#<\" << self.class.name << \":0x\"\n    object_id.to_s(io, 16)\n    io << \" @system_handle=\"\n    @system_handle.inspect io\n    io << ','\n    io << \" @name=\"\n    @name.inspect io\n    io << '>'\n  end\n\n  private def detach(&)\n    unless @detached.swap(true, :relaxed)\n      yield\n    end\n  end\n\n  # Suspends the current thread until this thread terminates.\n  def join : Nil\n    detach do\n      if ex = system_join\n        @exception ||= ex\n      end\n    end\n\n    if exception = @exception\n      raise exception\n    end\n  end\n\n  # Blocks the current thread for the duration of *time*. Clock precision is\n  # dependent on the operating system and hardware.\n  def self.sleep(time : Time::Span) : Nil\n    Crystal::System::Thread.sleep(time)\n  end\n\n  # Delays execution for a brief moment.\n  @[NoInline]\n  def self.delay(backoff : Int32) : Int32\n    if backoff < 7\n      backoff.times { Intrinsics.pause }\n      backoff &+ 1\n    else\n      Thread.yield\n      0\n    end\n  end\n\n  # Returns the Thread object associated to the running system thread.\n  def self.current : Thread\n    Crystal::System::Thread.current_thread\n  end\n\n  # :nodoc:\n  def self.current? : Thread?\n    Crystal::System::Thread.current_thread?\n  end\n\n  # Associates the Thread object to the running system thread.\n  protected def self.current=(current : Thread) : Thread\n    Crystal::System::Thread.current_thread = current\n    current\n  end\n\n  # Yields the currently running thread.\n  def self.yield : Nil\n    Crystal::System::Thread.yield_current\n  end\n\n  # Changes the name of the current thread.\n  def self.name=(name : String) : String\n    thread = Thread.current\n    thread.name = name\n  end\n\n  protected def start\n    Thread.threads.push(self)\n    Thread.current = self\n    @current_fiber = @main_fiber = fiber = Fiber.new(stack_address, self)\n\n    if name = @name\n      self.system_name = name\n    end\n\n    begin\n      @func.call(self)\n    rescue ex\n      @exception = ex\n    ensure\n      Thread.threads.delete(self)\n      Fiber.inactive(fiber)\n      detach { system_close }\n    end\n  end\n\n  protected def name=(@name : String)\n    self.system_name = name\n  end\n\n  # Changes the Thread#name property but doesn't update the system name. Useful\n  # on the main thread where we'd change the process name (e.g. top, ps, ...).\n  def internal_name=(@name : String)\n  end\n\n  # Holds the GC thread handler\n  property gc_thread_handler : Void* = Pointer(Void).null\n\n  def suspend : Nil\n    system_suspend\n  end\n\n  def wait_suspended : Nil\n    system_wait_suspended\n  end\n\n  def resume : Nil\n    system_resume\n  end\n\n  def self.stop_world : Nil\n    GC.stop_world\n  end\n\n  def self.start_world : Nil\n    GC.start_world\n  end\nend\n\nrequire \"./thread_linked_list\"\nrequire \"./thread_condition_variable\"\n"
  },
  {
    "path": "src/crystal/system/thread_condition_variable.cr",
    "content": "class Thread\n  class ConditionVariable\n    # Creates a new condition variable.\n    # def initialize\n\n    # Unblocks one thread that is waiting on `self`.\n    # def signal : Nil\n\n    # Unblocks all threads that are waiting on `self`.\n    # def broadcast : Nil\n\n    # Causes the calling thread to wait on `self` and unlock the given *mutex*\n    # atomically.\n    # def wait(mutex : Thread::Mutex) : Nil\n\n    # Causes the calling thread to wait on `self` and unlock the given *mutex*\n    # atomically within the given *time* span. Yields to the given block if a\n    # timeout occurs.\n    # def wait(mutex : Thread::Mutex, time : Time::Span, & : ->)\n  end\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/thread_condition_variable\"\n{% elsif flag?(:unix) %}\n  require \"./unix/pthread_condition_variable\"\n{% elsif flag?(:win32) %}\n  require \"./win32/thread_condition_variable\"\n{% else %}\n  {% raise \"Thread condition variable not supported\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/thread_linked_list.cr",
    "content": "require \"./thread_mutex\"\n\n# :nodoc:\nclass Thread\n  # :nodoc:\n  #\n  # Thread-safe doubly linked list of `T` objects that must implement\n  # `#previous : T?` and `#next : T?` methods.\n  class LinkedList(T)\n    @mutex = Thread::Mutex.new\n    @head : T?\n    @tail : T?\n\n    # Iterates the list without acquiring the lock, to avoid a deadlock in\n    # stop-the-world situations, where a paused thread could have acquired the\n    # lock to push/delete a node, while still being \"safe\" to iterate (but only\n    # during a stop-the-world).\n    def unsafe_each(&) : Nil\n      node = @head\n\n      while node\n        yield node\n        node = node.next\n      end\n    end\n\n    # Safely iterates the list.\n    def each(&) : Nil\n      @mutex.synchronize do\n        unsafe_each { |node| yield node }\n      end\n    end\n\n    # Appends a node to the tail of the list. The operation is thread-safe.\n    #\n    # There are no guarantees that a node being pushed will be iterated by\n    # `#unsafe_each` until the method has returned.\n    def push(node : T) : Nil\n      @mutex.synchronize do\n        node.previous = nil\n\n        if tail = @tail\n          node.previous = tail\n          @tail = tail.next = node\n        else\n          @head = @tail = node\n        end\n      end\n    end\n\n    # Removes a node from the list. The operation is thread-safe.\n    #\n    # There are no guarantees that a node being deleted won't be iterated by\n    # `#unsafe_each` until the method has returned.\n    def delete(node : T) : Nil\n      @mutex.synchronize do\n        previous = node.previous\n        _next = node.next\n\n        if previous\n          node.previous = nil\n          previous.next = _next\n        else\n          @head = _next\n        end\n\n        if _next\n          node.next = nil\n          _next.previous = previous\n        else\n          @tail = previous\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/thread_mutex.cr",
    "content": "class Thread\n  class Mutex\n    # Creates a new mutex.\n    # def initialize\n\n    # Locks the mutex from the current thread.\n    # def lock : Nil\n\n    # Tries to lock the mutex and returns `false` if failed.\n    # def try_lock : Bool\n\n    # Unlocks the mutex from the current thread.\n    # def unlock : Nil\n\n    # Locks the mutex, yields to the block and ensures it unlocks afterwards.\n    # def synchronize(&block)\n  end\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/thread_mutex\"\n{% elsif flag?(:unix) %}\n  require \"./unix/pthread_mutex\"\n{% elsif flag?(:win32) %}\n  require \"./win32/thread_mutex\"\n{% else %}\n  {% raise \"Thread mutex not supported\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/thread_wait_group.cr",
    "content": "# :nodoc:\nclass Thread::WaitGroup\n  def initialize(@count : Int32)\n    @mutex = Thread::Mutex.new\n    @condition = Thread::ConditionVariable.new\n  end\n\n  def done : Nil\n    @mutex.synchronize do\n      @count -= 1\n      @condition.broadcast if @count == 0\n    end\n  end\n\n  def wait : Nil\n    @mutex.synchronize do\n      @condition.wait(@mutex) unless @count == 0\n    end\n  end\n\n  def wait(time : Time::Span, &) : Nil\n    @mutex.synchronize do\n      unless @count == 0\n        @condition.wait(@mutex, time) { yield }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/time.cr",
    "content": "module Crystal::System::Time\n  # Returns the current UTC time measured in `{seconds, nanoseconds}`\n  # since `0001-01-01 00:00:00`.\n  # def self.compute_utc_seconds_and_nanoseconds : {Int64, Int32}\n\n  # Returns the current time from the monotonic clock in `{seconds,\n  # nanoseconds}`.\n  # def self.monotonic : {Int64, Int32}\n\n  # Returns the current time from the monotonic clock in nanoseconds.\n  # Doesn't raise nor allocates GC HEAP memory.\n  # def self.ticks : UInt64\n\n  # Returns a list of paths where time zone data should be looked up.\n  # def self.zone_sources : Enumerable(String)\n\n  # Loads a time zone by its IANA zone identifier directly. May return `nil` on\n  # systems where tzdata is assumed to be available.\n  # def self.load_iana_zone(iana_name : String) : ::Time::Location?\n\n  # Returns the system's current local time zone\n  # def self.load_localtime : ::Time::Location?\n\n  def self.instant\n    seconds, nanoseconds = Crystal::System::Time.monotonic\n    ::Time::Instant.new(seconds: seconds, nanoseconds: nanoseconds)\n  end\nend\n\n{% if flag?(:unix) %}\n  require \"./unix/time\"\n{% elsif flag?(:win32) %}\n  require \"./win32/time\"\n{% else %}\n  {% raise \"No Crystal::System::Time implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/unix/addrinfo.cr",
    "content": "module Crystal::System::Addrinfo\n  alias Handle = LibC::Addrinfo*\n\n  @addr : LibC::SockaddrIn6\n\n  protected def initialize(addrinfo : Handle)\n    @family = ::Socket::Family.from_value(addrinfo.value.ai_family)\n    @type = ::Socket::Type.from_value(addrinfo.value.ai_socktype)\n    @protocol = ::Socket::Protocol.from_value(addrinfo.value.ai_protocol)\n    @size = addrinfo.value.ai_addrlen.to_i\n\n    @addr = uninitialized LibC::SockaddrIn6\n\n    case @family\n    when ::Socket::Family::INET6\n      addrinfo.value.ai_addr.as(LibC::SockaddrIn6*).copy_to(pointerof(@addr).as(LibC::SockaddrIn6*), 1)\n    when ::Socket::Family::INET\n      addrinfo.value.ai_addr.as(LibC::SockaddrIn*).copy_to(pointerof(@addr).as(LibC::SockaddrIn*), 1)\n    else\n      # TODO: (asterite) UNSPEC and UNIX unsupported?\n    end\n  end\n\n  def system_ip_address : ::Socket::IPAddress\n    ::Socket::IPAddress.from(to_unsafe, size)\n  end\n\n  def to_unsafe\n    pointerof(@addr).as(LibC::Sockaddr*)\n  end\n\n  def self.getaddrinfo(domain, service, family, type, protocol, timeout, flags = 0) : Handle\n    hints = LibC::Addrinfo.new\n    hints.ai_family = (family || ::Socket::Family::UNSPEC).to_i32\n    hints.ai_socktype = type\n    hints.ai_protocol = protocol\n\n    flags |= LibC::AI_NUMERICSERV if service.is_a?(Int)\n\n    # On OS X < 10.12, the libsystem implementation of getaddrinfo segfaults\n    # if AI_NUMERICSERV is set, and servname is NULL or 0.\n    {% if flag?(:darwin) %}\n      if service.in?(0, nil) && (flags & LibC::AI_NUMERICSERV)\n        flags |= LibC::AI_NUMERICSERV\n        service = \"00\"\n      end\n    {% end %}\n\n    hints.ai_flags = flags\n\n    ptr = Pointer(LibC::Addrinfo).null\n    ret = ::Fiber.syscall do\n      LibC.getaddrinfo(domain, service.to_s, pointerof(hints), pointerof(ptr))\n    end\n    unless ret.zero?\n      if ret == LibC::EAI_SYSTEM\n        raise ::Socket::Addrinfo::Error.from_os_error nil, Errno.value, domain: domain\n      end\n\n      error = Errno.new(ret)\n      raise ::Socket::Addrinfo::Error.from_os_error(nil, error, domain: domain, type: type, protocol: protocol, service: service)\n    end\n    ptr\n  end\n\n  def self.next_addrinfo(addrinfo : Handle) : Handle\n    addrinfo.value.ai_next\n  end\n\n  def self.free_addrinfo(addrinfo : Handle)\n    LibC.freeaddrinfo(addrinfo)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/arc4random.cr",
    "content": "{% skip_file unless flag?(:bsd) || flag?(:darwin) %}\n\nrequire \"c/stdlib\"\n\nmodule Crystal::System::Random\n  # Fills *buffer* with random bytes using arc4random.\n  def self.random_bytes(buffer : Bytes) : Nil\n    LibC.arc4random_buf(buffer.to_unsafe.as(Void*), buffer.size)\n  end\n\n  def self.next_u : UInt32\n    LibC.arc4random\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/dir.cr",
    "content": "require \"c/dirent\"\n\nmodule Crystal::System::Dir\n  def self.open(path : String) : LibC::DIR*\n    dir = LibC.opendir(path.check_no_null_byte)\n    raise ::File::Error.from_errno(\"Error opening directory\", file: path) unless dir\n    dir\n  end\n\n  def self.next_entry(dir, path) : Entry?\n    # LibC.readdir returns NULL and sets errno for failure or returns NULL for EOF but leaves errno as is.\n    # This means we need to reset `Errno` before calling `readdir`.\n    Errno.value = Errno::NONE\n    if entry = LibC.readdir(dir)\n      name = String.new(entry.value.d_name.to_unsafe)\n\n      dir =\n        {% if flag?(:solaris) %}\n          # `d_type` is a Linux / BSD extension\n          nil\n        {% else %}\n          case entry.value.d_type\n          when LibC::DT_DIR                   then true\n          when LibC::DT_UNKNOWN, LibC::DT_LNK then nil\n          else                                     false\n          end\n        {% end %}\n\n      # TODO: support `st_flags & UF_HIDDEN` on BSD-like systems: https://man.freebsd.org/cgi/man.cgi?query=stat&sektion=2\n      # TODO: support hidden file attributes on macOS / HFS+: https://stackoverflow.com/a/15236292\n      # (are these the same?)\n      Entry.new(name, dir, false)\n    elsif Errno.value != Errno::NONE\n      raise ::File::Error.from_errno(\"Error reading directory entries\", file: path)\n    else\n      nil\n    end\n  end\n\n  def self.rewind(dir) : Nil\n    LibC.rewinddir(dir)\n  end\n\n  def self.info(dir, path) : ::File::Info\n    fd = {% if flag?(:netbsd) %}\n           dir.value.dd_fd\n         {% else %}\n           LibC.dirfd(dir)\n         {% end %}\n    Crystal::System::FileDescriptor.system_info(fd)\n  end\n\n  def self.close(dir, path) : Nil\n    if LibC.closedir(dir) != 0\n      raise ::File::Error.from_errno(\"Error closing directory\", file: path)\n    end\n  end\n\n  def self.current : String\n    # If $PWD is set and it matches the current path, use that.\n    # This helps telling apart symlinked paths.\n    if (pwd = ENV[\"PWD\"]?) && pwd.starts_with?(\"/\") &&\n       (pwd_info = ::Crystal::System::File.info?(pwd, follow_symlinks: true)) &&\n       (dot_info = ::Crystal::System::File.info?(\".\", follow_symlinks: true)) &&\n       pwd_info.same_file?(dot_info)\n      return pwd\n    end\n\n    unless dir = LibC.getcwd(nil, 0)\n      raise ::File::Error.from_errno(\"Error getting current directory\", file: \"./\")\n    end\n\n    dir_str = String.new(dir)\n    LibC.free(dir.as(Void*))\n    dir_str\n  end\n\n  def self.current=(path : String)\n    if LibC.chdir(path.check_no_null_byte) != 0\n      raise ::File::Error.from_errno(\"Error while changing directory\", file: path)\n    end\n\n    path\n  end\n\n  def self.tempdir\n    tmpdir = ENV[\"TMPDIR\"]? || \"/tmp\"\n    tmpdir.rchop(::File::SEPARATOR)\n  end\n\n  def self.create(path : String, mode : Int32) : Nil\n    if LibC.mkdir(path.check_no_null_byte, mode) == -1\n      raise ::File::Error.from_errno(\"Unable to create directory\", file: path)\n    end\n  end\n\n  def self.delete(path : String, *, raise_on_missing : Bool) : Bool\n    return true if LibC.rmdir(path.check_no_null_byte) == 0\n\n    if !raise_on_missing && Errno.value == Errno::ENOENT\n      false\n    else\n      raise ::File::Error.from_errno(\"Unable to remove directory\", file: path)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/env.cr",
    "content": "require \"c/stdlib\"\nrequire \"sync/rw_lock\"\n\nmodule Crystal::System::Env\n  @@lock = Sync::RWLock.new(:unchecked)\n\n  # Sets an environment variable.\n  def self.set(key : String, value : String) : Nil\n    key.check_no_null_byte(\"key\")\n    value.check_no_null_byte(\"value\")\n\n    if @@lock.write { LibC.setenv(key, value, 1) } != 0\n      raise RuntimeError.from_errno(\"setenv\")\n    end\n  end\n\n  # Unsets an environment variable.\n  def self.set(key : String, value : Nil) : Nil\n    key.check_no_null_byte(\"key\")\n\n    if @@lock.write { LibC.unsetenv(key) } != 0\n      raise RuntimeError.from_errno(\"unsetenv\")\n    end\n  end\n\n  # Gets an environment variable.\n  def self.get(key : String) : String?\n    key.check_no_null_byte(\"key\")\n\n    @@lock.read do\n      if value = LibC.getenv(key)\n        String.new(value)\n      end\n    end\n  end\n\n  # Returns `true` if environment variable is set.\n  def self.has_key?(key : String) : Bool\n    key.check_no_null_byte(\"key\")\n\n    !!@@lock.read { LibC.getenv(key) }\n  end\n\n  # Iterates all environment variables.\n  def self.each(&block : String, String ->)\n    # Collect variables while holding the lock because we can't trust\n    # LibC.environ to be stable and don't control what &block does: it might\n    # yield the current fiber while holding the lock, deadlock if it calls\n    # Env.set, ...\n    env = Array({String, String}).new\n\n    @@lock.read do\n      each_pointer do |kv_pointer|\n        # this does `String.new(kv_pointer).partition('=')` without an intermediary string\n        key_value = Slice.new(kv_pointer, LibC.strlen(kv_pointer))\n        split_index = key_value.index!(0x3d_u8) # '='\n        key = key_value[0, split_index]\n        value = key_value[split_index + 1..]\n        env << {String.new(key), String.new(value)}\n      end\n    end\n\n    # now we can safely iterate\n    env.each do |key, value|\n      yield key, value\n    end\n  end\n\n  # Iterates all environment variables as a char pointer to a \"KEY=VALUE\"\n  # pointer.\n  private def self.each_pointer(&block : LibC::Char* ->)\n    environ_ptr = LibC.environ\n    while environ_ptr\n      environ_value = environ_ptr.value\n      if environ_value\n        yield environ_value\n        environ_ptr += 1\n      else\n        break\n      end\n    end\n  end\n\n  # Creates an environment pointer for use with `execve` and similar functions.\n  #\n  # The behaviour is pretty straight-forward and most system lib\n  # implementations generally agree on it, so there's not much controversy about\n  # different flavours.\n  #\n  # OPTIMIZE: We could further optimize this by using the existing entry\n  # pointers from `.each_pointer` instead of `.each`. And potentially we could\n  # store all strings in a single buffer, and calculate the sizes for that\n  # buffer and `envp` upfront to reduce overall allocations.\n  def self.make_envp(env, clear_env) : LibC::Char**\n    # When there are no adjustments in `env`, we can take a short cut and return\n    # an empty pointer.\n    if clear_env && (env.nil? || env.empty?)\n      return Pointer(LibC::Char*).malloc(1)\n    end\n\n    envp = Array(LibC::Char*).new\n\n    unless clear_env\n      # Dup LibC.environ, skipping overrides in env.\n      each do |key, value|\n        next if env.try(&.has_key?(key))\n\n        envp << \"#{key}=#{value}\".to_unsafe\n      end\n    end\n\n    env.try(&.each do |key, value|\n      # `nil` value means deleting the key from the inherited environment\n      next unless value\n\n      raise ArgumentError.new(\"Invalid env key #{key.inspect}\") if key.empty? || key.includes?('=')\n      envp << \"#{key.check_no_null_byte(\"key\")}=#{value.check_no_null_byte(\"value\")}\".to_unsafe\n    end)\n\n    envp << Pointer(LibC::Char).null\n\n    envp.to_unsafe\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/epoll.cr",
    "content": "require \"c/sys/epoll\"\n\nstruct Crystal::System::Epoll\n  def initialize\n    @epfd = LibC.epoll_create1(LibC::EPOLL_CLOEXEC)\n    raise RuntimeError.from_errno(\"epoll_create1\") if @epfd == -1\n  end\n\n  def fd : Int32\n    @epfd\n  end\n\n  def add(fd : Int32, epoll_event : LibC::EpollEvent*) : Nil\n    if LibC.epoll_ctl(@epfd, LibC::EPOLL_CTL_ADD, fd, epoll_event) == -1\n      raise RuntimeError.from_errno(\"epoll_ctl(EPOLL_CTL_ADD)\") unless Errno.value == Errno::EPERM\n    end\n  end\n\n  def add(fd : Int32, events : UInt32, u64 : UInt64) : Nil\n    epoll_event = uninitialized LibC::EpollEvent\n    epoll_event.events = events\n    epoll_event.data.u64 = u64\n    add(fd, pointerof(epoll_event))\n  end\n\n  def modify(fd : Int32, epoll_event : LibC::EpollEvent*) : Nil\n    if LibC.epoll_ctl(@epfd, LibC::EPOLL_CTL_MOD, fd, epoll_event) == -1\n      raise RuntimeError.from_errno(\"epoll_ctl(EPOLL_CTL_MOD)\")\n    end\n  end\n\n  def delete(fd : Int32) : Nil\n    delete(fd) do\n      raise RuntimeError.from_errno(\"epoll_ctl(EPOLL_CTL_DEL)\")\n    end\n  end\n\n  def delete(fd : Int32, &) : Nil\n    if LibC.epoll_ctl(@epfd, LibC::EPOLL_CTL_DEL, fd, nil) == -1\n      yield\n    end\n  end\n\n  # `timeout` is in milliseconds; -1 will wait indefinitely; 0 will never wait.\n  def wait(events : Slice(LibC::EpollEvent), timeout : Int32) : Slice(LibC::EpollEvent)\n    count = 0\n\n    loop do\n      count = LibC.epoll_wait(@epfd, events.to_unsafe, events.size, timeout)\n      break unless count == -1\n\n      if Errno.value == Errno::EINTR\n        # retry when waiting indefinitely, return otherwise\n        break unless timeout == -1\n      else\n        raise RuntimeError.from_errno(\"epoll_wait\")\n      end\n    end\n\n    events[0, count.clamp(0..)]\n  end\n\n  def close : Nil\n    LibC.close(@epfd)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/eventfd.cr",
    "content": "require \"c/sys/eventfd\"\n\nstruct Crystal::System::EventFD\n  # NOTE: no need to concern ourselves with endianness: we interpret the bytes\n  # in the system order and eventfd can only be used locally (no cross system\n  # issues).\n\n  getter fd : Int32\n\n  def initialize(value = 0)\n    @fd = LibC.eventfd(value, LibC::EFD_CLOEXEC)\n    raise RuntimeError.from_errno(\"eventfd\") if @fd == -1\n  end\n\n  def read : UInt64\n    buf = uninitialized UInt8[8]\n    bytes_read = LibC.read(@fd, buf.to_unsafe, buf.size)\n    raise RuntimeError.from_errno(\"eventfd_read\") unless bytes_read == 8\n    buf.unsafe_as(UInt64)\n  end\n\n  def write(value : UInt64) : Nil\n    buf = value.unsafe_as(StaticArray(UInt8, 8))\n    bytes_written = LibC.write(@fd, buf.to_unsafe, buf.size)\n    raise RuntimeError.from_errno(\"eventfd_write\") unless bytes_written == 8\n  end\n\n  def close : Nil\n    LibC.close(@fd)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/fiber.cr",
    "content": "require \"c/sys/mman\"\n\nmodule Crystal::System::Fiber\n  def self.allocate_stack(stack_size, protect) : Void*\n    flags = LibC::MAP_PRIVATE | LibC::MAP_ANON\n    {% if flag?(:openbsd) %}\n      flags |= LibC::MAP_STACK\n    {% end %}\n\n    pointer = LibC.mmap(nil, stack_size, LibC::PROT_READ | LibC::PROT_WRITE, flags, -1, 0)\n    raise RuntimeError.from_errno(\"Cannot allocate new fiber stack\") if pointer == LibC::MAP_FAILED\n\n    {% if flag?(:linux) %}\n      LibC.madvise(pointer, stack_size, LibC::MADV_NOHUGEPAGE)\n    {% end %}\n\n    if protect\n      LibC.mprotect(pointer, 4096, LibC::PROT_NONE)\n    end\n\n    pointer\n  end\n\n  def self.reset_stack(stack : Void*, stack_size : Int, protect : Bool) : Nil\n  end\n\n  def self.free_stack(stack : Void*, stack_size) : Nil\n    LibC.munmap(stack, stack_size)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/file.cr",
    "content": "require \"c/sys/file\"\nrequire \"file/error\"\n\n# :nodoc:\nmodule Crystal::System::File\n  def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions, blocking : Bool?) : {FileDescriptor::Handle, Bool}\n    perm = ::File::Permissions.new(perm) if perm.is_a? Int32\n\n    case result = EventLoop.current.open(filename, open_flag(mode), perm, blocking)\n    in Tuple(FileDescriptor::Handle, Bool)\n      result\n    in Errno\n      raise ::File::Error.from_os_error(\"Error opening file with mode '#{mode}'\", result, file: filename)\n    end\n  end\n\n  protected def system_init(mode : String, blocking : Bool) : Nil\n  end\n\n  def self.special_type?(fd)\n    stat = uninitialized LibC::Stat\n    ret = fstat(fd, pointerof(stat))\n    # not checking for S_IFSOCK because we can't open(2) a socket\n    ret != -1 && (stat.st_mode & LibC::S_IFMT).in?(LibC::S_IFCHR, LibC::S_IFIFO)\n  end\n\n  def self.info?(path : String, follow_symlinks : Bool) : ::File::Info?\n    stat = uninitialized LibC::Stat\n    if follow_symlinks\n      ret = stat(path.check_no_null_byte, pointerof(stat))\n    else\n      ret = lstat(path.check_no_null_byte, pointerof(stat))\n    end\n\n    if ret == 0\n      ::File::Info.new(stat)\n    else\n      if ::File::NotFoundError.os_error?(Errno.value)\n        nil\n      else\n        raise ::File::Error.from_errno(\"Unable to get file info\", file: path)\n      end\n    end\n  end\n\n  # On some systems, the symbols `stat`, `fstat` and `lstat` are not part of the GNU\n  # shared library `libc.so` and instead provided by `libc_noshared.a`.\n  # That makes them unavailable for dynamic runtime symbol lookup via `dlsym`\n  # which we use for interpreted mode.\n  # See https://github.com/crystal-lang/crystal/issues/11157#issuecomment-949640034 for details.\n  # Linking against the internal counterparts `__xstat`, `__fxstat` and `__lxstat` directly\n  # should work in both interpreted and compiled mode.\n\n  def self.stat(path, stat)\n    {% if LibC.has_method?(:__xstat) %}\n      LibC.__xstat(LibC::STAT_VER, path, stat)\n    {% else %}\n      LibC.stat(path, stat)\n    {% end %}\n  end\n\n  def self.fstat(path, stat)\n    {% if LibC.has_method?(:__fxstat) %}\n      LibC.__fxstat(LibC::STAT_VER, path, stat)\n    {% else %}\n      LibC.fstat(path, stat)\n    {% end %}\n  end\n\n  def self.lstat(path, stat)\n    {% if LibC.has_method?(:__lxstat) %}\n      LibC.__lxstat(LibC::STAT_VER, path, stat)\n    {% else %}\n      LibC.lstat(path, stat)\n    {% end %}\n  end\n\n  def self.info(path, follow_symlinks)\n    info?(path, follow_symlinks) || raise ::File::Error.from_errno(\"Unable to get file info\", file: path)\n  end\n\n  def self.exists?(path)\n    accessible?(path, LibC::F_OK)\n  end\n\n  def self.readable?(path) : Bool\n    accessible?(path, LibC::R_OK)\n  end\n\n  def self.writable?(path) : Bool\n    accessible?(path, LibC::W_OK)\n  end\n\n  def self.executable?(path) : Bool\n    accessible?(path, LibC::X_OK)\n  end\n\n  private def self.accessible?(path, flag)\n    LibC.access(path.check_no_null_byte, flag) == 0\n  end\n\n  def self.chown(path, uid : Int, gid : Int, follow_symlinks)\n    ret = if follow_symlinks\n            LibC.chown(path, uid, gid)\n          else\n            LibC.lchown(path, uid, gid)\n          end\n    raise ::File::Error.from_errno(\"Error changing owner\", file: path) if ret == -1\n  end\n\n  private def system_chown(uid : Int, gid : Int)\n    ret = LibC.fchown(fd, uid, gid)\n    raise ::File::Error.from_errno(\"Error changing owner\", file: path) if ret == -1\n  end\n\n  def self.chmod(path, mode)\n    if LibC.chmod(path, mode) == -1\n      raise ::File::Error.from_errno(\"Error changing permissions\", file: path)\n    end\n  end\n\n  private def system_chmod(mode)\n    if LibC.fchmod(fd, mode) == -1\n      raise ::File::Error.from_errno(\"Error changing permissions\", file: path)\n    end\n  end\n\n  def self.delete(path, *, raise_on_missing : Bool) : Bool\n    err = LibC.unlink(path.check_no_null_byte)\n    if err != -1\n      true\n    elsif !raise_on_missing && ::File::NotFoundError.os_error?(Errno.value)\n      false\n    else\n      raise ::File::Error.from_errno(\"Error deleting file\", file: path)\n    end\n  end\n\n  def self.realpath(path)\n    realpath_ptr = LibC.realpath(path, nil)\n    raise ::File::Error.from_errno(\"Error resolving real path\", file: path) unless realpath_ptr\n    String.new(realpath_ptr).tap { LibC.free(realpath_ptr.as(Void*)) }\n  end\n\n  def self.link(old_path, new_path)\n    ret = LibC.link(old_path.check_no_null_byte, new_path.check_no_null_byte)\n    raise ::File::Error.from_errno(\"Error creating link\", file: old_path, other: new_path) if ret != 0\n    ret\n  end\n\n  def self.symlink(old_path, new_path)\n    ret = LibC.symlink(old_path.check_no_null_byte, new_path.check_no_null_byte)\n    raise ::File::Error.from_errno(\"Error creating symlink\", file: old_path, other: new_path) if ret != 0\n    ret\n  end\n\n  def self.readlink(path, &) : String\n    buf = uninitialized UInt8[4096]\n    bytesize = LibC.readlink(path, buf, buf.size)\n    if bytesize == -1\n      if ::File::NotFoundError.os_error?(Errno.value) || Errno.value == Errno::EINVAL\n        yield\n      end\n\n      raise ::File::Error.from_errno(\"Cannot read link\", file: path)\n    elsif bytesize == buf.size\n      raise ::File::Error.from_os_error(\"Cannot read link\", Errno::ENAMETOOLONG, file: path)\n    else\n      return String.new(buf.to_unsafe, bytesize)\n    end\n  end\n\n  def self.rename(old_filename, new_filename) : ::File::Error?\n    code = LibC.rename(old_filename.check_no_null_byte, new_filename.check_no_null_byte)\n    if code != 0\n      ::File::Error.from_errno(\"Error renaming file\", file: old_filename, other: new_filename)\n    end\n  end\n\n  def self.utime(atime : ::Time, mtime : ::Time, filename : String) : Nil\n    ret =\n      {% if LibC.has_method?(\"utimensat\") %}\n        timespecs = uninitialized LibC::Timespec[2]\n        timespecs[0] = Crystal::System::Time.to_timespec(atime)\n        timespecs[1] = Crystal::System::Time.to_timespec(mtime)\n        LibC.utimensat(LibC::AT_FDCWD, filename, timespecs, 0)\n      {% else %}\n        timevals = uninitialized LibC::Timeval[2]\n        timevals[0] = Crystal::System::Time.to_timeval(atime)\n        timevals[1] = Crystal::System::Time.to_timeval(mtime)\n        LibC.utimes(filename, timevals)\n      {% end %}\n\n    if ret != 0\n      raise ::File::Error.from_errno(\"Error setting time on file\", file: filename)\n    end\n  end\n\n  private def system_utime(atime : ::Time, mtime : ::Time) : Nil\n    ret = {% if LibC.has_method?(\"futimens\") %}\n            timespecs = uninitialized LibC::Timespec[2]\n            timespecs[0] = Crystal::System::Time.to_timespec(atime)\n            timespecs[1] = Crystal::System::Time.to_timespec(mtime)\n            LibC.futimens(fd, timespecs)\n          {% elsif LibC.has_method?(\"futimes\") %}\n            timevals = uninitialized LibC::Timeval[2]\n            timevals[0] = Crystal::System::Time.to_timeval(atime)\n            timevals[1] = Crystal::System::Time.to_timeval(mtime)\n            LibC.futimes(fd, timevals)\n          {% else %}\n            {% raise \"Missing futimens & futimes\" %}\n          {% end %}\n\n    if ret != 0\n      raise ::File::Error.from_errno(\"Error setting time on file\", file: path)\n    end\n  end\n\n  private def system_truncate(size) : Nil\n    code = LibC.ftruncate(fd, size)\n    if code != 0\n      raise ::File::Error.from_errno(\"Error truncating file\", file: path)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/file_descriptor.cr",
    "content": "require \"c/fcntl\"\nrequire \"termios\"\n{% if flag?(:android) && LibC::ANDROID_API < 28 %}\n  require \"c/sys/ioctl\"\n{% end %}\n\n# :nodoc:\nmodule Crystal::System::FileDescriptor\n  {% if IO.has_constant?(:Evented) %}\n    include IO::Evented\n  {% end %}\n\n  # Platform-specific type to represent a file descriptor handle to the operating\n  # system.\n  alias Handle = Int32\n\n  STDIN_HANDLE  = 0\n  STDOUT_HANDLE = 1\n  STDERR_HANDLE = 2\n\n  private def system_blocking?\n    flags = FileDescriptor.fcntl(fd, LibC::F_GETFL)\n    !flags.bits_set? LibC::O_NONBLOCK\n  end\n\n  private def system_blocking=(value)\n    FileDescriptor.set_blocking(fd, value)\n  end\n\n  protected def self.get_blocking(fd : Handle)\n    fcntl(fd, LibC::F_GETFL) & LibC::O_NONBLOCK == 0\n  end\n\n  protected def self.set_blocking(fd : Handle, value : Bool)\n    current_flags = fcntl(fd, LibC::F_GETFL)\n    new_flags = current_flags\n    if value\n      new_flags &= ~LibC::O_NONBLOCK\n    else\n      new_flags |= LibC::O_NONBLOCK\n    end\n    fcntl(fd, LibC::F_SETFL, new_flags) unless new_flags == current_flags\n  end\n\n  protected def system_blocking_init(blocking : Bool?)\n    if blocking.nil?\n      blocking = EventLoop.default_file_blocking? ||\n                 case system_info.type\n                 when .pipe?, .socket?, .character_device?\n                   false\n                 else\n                   true\n                 end\n    end\n    self.system_blocking = blocking\n  end\n\n  private def system_close_on_exec?\n    flags = FileDescriptor.fcntl(fd, LibC::F_GETFD)\n    flags.bits_set? LibC::FD_CLOEXEC\n  end\n\n  private def system_close_on_exec=(arg : Bool)\n    system_fcntl(LibC::F_SETFD, arg ? LibC::FD_CLOEXEC : 0)\n    arg\n  end\n\n  private def system_closed?\n    LibC.fcntl(fd, LibC::F_GETFL) == -1\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    r = LibC.fcntl(fd, cmd, arg)\n    raise IO::Error.from_errno(\"fcntl() failed\") if r == -1\n    r\n  end\n\n  private def system_fcntl(cmd, arg = 0)\n    FileDescriptor.fcntl(fd, cmd, arg)\n  end\n\n  def self.system_info(fd)\n    stat = uninitialized LibC::Stat\n    ret = File.fstat(fd, pointerof(stat))\n\n    if ret != 0\n      raise IO::Error.from_errno(\"Unable to get info\")\n    end\n\n    ::File::Info.new(stat)\n  end\n\n  private def system_info\n    FileDescriptor.system_info(fd)\n  end\n\n  private def system_seek(offset, whence : IO::Seek) : Nil\n    seek_value = LibC.lseek(fd, offset, whence)\n\n    if seek_value == -1\n      raise IO::Error.from_errno \"Unable to seek\", target: self\n    end\n  end\n\n  private def system_pos\n    pos = LibC.lseek(fd, 0, IO::Seek::Current).to_i64\n    raise IO::Error.from_errno(\"Unable to tell\", target: self) if pos == -1\n    pos\n  end\n\n  private def system_tty?\n    LibC.isatty(fd) == 1\n  end\n\n  private def system_reopen(other : IO::FileDescriptor)\n    {% if LibC.has_method?(:dup3) %}\n      flags = other.close_on_exec? ? LibC::O_CLOEXEC : 0\n      if LibC.dup3(other.fd, fd, flags) == -1\n        raise IO::Error.from_errno(\"Could not reopen file descriptor\")\n      end\n    {% else %}\n      Process.lock_read do\n        if LibC.dup2(other.fd, fd) == -1\n          raise IO::Error.from_errno(\"Could not reopen file descriptor\")\n        end\n        self.close_on_exec = other.close_on_exec?\n      end\n    {% end %}\n\n    # Mark the handle open, since we had to have dup'd a live handle.\n    @closed = false\n\n    event_loop.reopened(self)\n  end\n\n  def file_descriptor_close(&) : Nil\n    # It would usually be set by IO::Buffered#unbuffered_close but we sometimes\n    # close file descriptors directly (i.e. signal/process pipes) and the IO\n    # object wouldn't be marked as closed, leading IO::FileDescriptor#finalize\n    # to try to close the fd again (pointless) and lead to other issues if we\n    # try to do more cleanup in the finalizer (error)\n    @closed = true\n\n    # Clear the @volatile_fd before actually closing it in order to\n    # reduce the chance of reading an outdated fd value\n    return unless fd = close_volatile_fd?\n\n    if LibC.close(fd) != 0\n      case Errno.value\n      when Errno::EINTR, Errno::EINPROGRESS\n        # ignore\n      else\n        yield\n      end\n    end\n  end\n\n  def file_descriptor_close\n    file_descriptor_close do\n      raise IO::Error.from_errno(\"Error closing file\", target: self)\n    end\n  end\n\n  def close_volatile_fd? : Int32?\n    fd = @volatile_fd.swap(-1)\n    fd unless fd == -1\n  end\n\n  private def system_flock_shared(blocking)\n    flock LibC::FlockOp::SH, blocking\n  end\n\n  private def system_flock_exclusive(blocking)\n    flock LibC::FlockOp::EX, blocking\n  end\n\n  private def system_flock_unlock\n    flock LibC::FlockOp::UN\n  end\n\n  private def flock(op : LibC::FlockOp, retry : Bool) : Nil\n    op |= LibC::FlockOp::NB\n\n    if retry\n      until flock(op)\n        sleep 0.1.seconds\n      end\n    else\n      flock(op) || raise IO::Error.from_errno(\"Error applying file lock: file is already locked\", target: self)\n    end\n  end\n\n  private def flock(op) : Bool\n    if 0 == LibC.flock(fd, op)\n      true\n    else\n      errno = Errno.value\n      if errno.in?(Errno::EAGAIN, Errno::EWOULDBLOCK)\n        false\n      else\n        raise IO::Error.from_os_error(\"Error applying or removing file lock\", errno, target: self)\n      end\n    end\n  end\n\n  private def system_fsync(flush_metadata = true) : Nil\n    ret =\n      if flush_metadata\n        LibC.fsync(fd)\n      else\n        {% if flag?(:dragonfly) %}\n          LibC.fsync(fd)\n        {% else %}\n          LibC.fdatasync(fd)\n        {% end %}\n      end\n\n    if ret != 0\n      raise IO::Error.from_errno(\"Error syncing file\", target: self)\n    end\n  end\n\n  def self.system_pipe : StaticArray(LibC::Int, 2)\n    pipe_fds = uninitialized StaticArray(LibC::Int, 2)\n\n    {% if LibC.has_method?(:pipe2) %}\n      if LibC.pipe2(pipe_fds, LibC::O_CLOEXEC) != 0\n        raise IO::Error.from_errno(\"Could not create pipe\")\n      end\n    {% else %}\n      Process.lock_read do\n        if LibC.pipe(pipe_fds) != 0\n          raise IO::Error.from_errno(\"Could not create pipe\")\n        end\n        fcntl(pipe_fds[0], LibC::F_SETFD, LibC::FD_CLOEXEC)\n        fcntl(pipe_fds[1], LibC::F_SETFD, LibC::FD_CLOEXEC)\n      end\n    {% end %}\n\n    pipe_fds\n  end\n\n  def self.pread(file, buffer, offset)\n    bytes_read = LibC.pread(file.fd, buffer, buffer.size, offset).to_i64\n\n    if bytes_read == -1\n      raise IO::Error.from_errno(\"Error reading file\", target: file)\n    end\n\n    bytes_read\n  end\n\n  def self.from_stdio(fd)\n    # If we have a TTY for stdin/out/err, it is possibly a shared terminal.\n    # We need to reopen it to use O_NONBLOCK without causing other programs to break\n\n    # Figure out the terminal TTY name. If ttyname fails we have a non-tty, or something strange.\n    # For non-tty we set flush_on_newline to true for reasons described in STDOUT and STDERR docs.\n    path = uninitialized UInt8[256]\n    ret = LibC.ttyname_r(fd, path, 256)\n    return IO::FileDescriptor.new(fd).tap(&.flush_on_newline=(true)) unless ret == 0\n\n    clone_fd = LibC.open(path, LibC::O_RDWR | LibC::O_CLOEXEC)\n    return IO::FileDescriptor.new(fd).tap(&.flush_on_newline=(true)) if clone_fd == -1\n\n    # We don't buffer output for TTY devices to see their output right away\n    io = IO::FileDescriptor.new(clone_fd)\n    io.sync = true\n    io\n  end\n\n  # Helper to write *size* values at *pointer* to a given *fd*.\n  def self.write_fully(fd : LibC::Int, pointer : Pointer, size : Int32 = 1) : Nil\n    write_fully(fd, Slice.new(pointer, size).unsafe_slice_of(UInt8))\n  end\n\n  # Helper to fully write a slice to a given *fd*.\n  def self.write_fully(fd : LibC::Int, slice : Slice(UInt8)) : Nil\n    until slice.size == 0\n      size = LibC.write(fd, slice, slice.size)\n      break if size == -1\n      slice += size\n    end\n  end\n\n  private def system_echo(enable : Bool, mode = nil)\n    new_mode = mode || system_tcgetattr\n    flags = LibC::ECHO | LibC::ECHOE | LibC::ECHOK | LibC::ECHONL\n    new_mode.c_lflag = enable ? (new_mode.c_lflag | flags) : (new_mode.c_lflag & ~flags)\n    if system_tcsetattr(LibC::TCSANOW, pointerof(new_mode)) != 0\n      raise IO::Error.from_errno(\"tcsetattr\")\n    end\n  end\n\n  private def system_echo(enable : Bool, & : ->)\n    system_console_mode do |mode|\n      system_echo(enable, mode)\n      yield\n    end\n  end\n\n  private def system_raw(enable : Bool, mode = nil)\n    new_mode = mode || system_tcgetattr\n    if enable\n      new_mode = FileDescriptor.cfmakeraw(new_mode)\n    else\n      new_mode.c_iflag |= LibC::BRKINT | LibC::ISTRIP | LibC::ICRNL | LibC::IXON\n      new_mode.c_oflag |= LibC::OPOST\n      new_mode.c_lflag |= LibC::ECHO | LibC::ECHOE | LibC::ECHOK | LibC::ECHONL | LibC::ICANON | LibC::ISIG | LibC::IEXTEN\n    end\n    if system_tcsetattr(LibC::TCSANOW, pointerof(new_mode)) != 0\n      raise IO::Error.from_errno(\"tcsetattr\")\n    end\n  end\n\n  private def system_raw(enable : Bool, & : ->)\n    system_console_mode do |mode|\n      system_raw(enable, mode)\n      yield\n    end\n  end\n\n  @[AlwaysInline]\n  private def system_console_mode(&)\n    before = system_tcgetattr\n    begin\n      yield before\n    ensure\n      system_tcsetattr(LibC::TCSANOW, pointerof(before))\n    end\n  end\n\n  @[AlwaysInline]\n  private def system_tcgetattr\n    termios = uninitialized LibC::Termios\n    {% if LibC.has_method?(:tcgetattr) %}\n      ret = LibC.tcgetattr(fd, pointerof(termios))\n      raise IO::Error.from_errno(\"tcgetattr\") if ret == -1\n    {% else %}\n      ret = LibC.ioctl(fd, LibC::TCGETS, pointerof(termios))\n      raise IO::Error.from_errno(\"ioctl\") if ret == -1\n    {% end %}\n    termios\n  end\n\n  @[AlwaysInline]\n  private def system_tcsetattr(optional_actions, termios_p)\n    {% if LibC.has_method?(:tcsetattr) %}\n      @fd_lock.reference { LibC.tcsetattr(fd, optional_actions, termios_p) }\n    {% else %}\n      optional_actions = optional_actions.value if optional_actions.is_a?(Termios::LineControl)\n      cmd = case optional_actions\n            when LibC::TCSANOW\n              LibC::TCSETS\n            when LibC::TCSADRAIN\n              LibC::TCSETSW\n            when LibC::TCSAFLUSH\n              LibC::TCSETSF\n            else\n              Errno.value = Errno::EINVAL\n              return LibC::Int.new(-1)\n            end\n\n      @fd_lock.reference { LibC.ioctl(fd, cmd, termios_p) }\n    {% end %}\n  end\n\n  @[AlwaysInline]\n  def self.cfmakeraw(termios)\n    {% if LibC.has_method?(:cfmakeraw) %}\n      LibC.cfmakeraw(pointerof(termios))\n    {% else %}\n      termios.c_iflag &= ~(LibC::IGNBRK | LibC::BRKINT | LibC::PARMRK | LibC::ISTRIP | LibC::INLCR | LibC::IGNCR | LibC::ICRNL | LibC::IXON)\n      termios.c_oflag &= ~LibC::OPOST\n      termios.c_lflag &= ~(LibC::ECHO | LibC::ECHONL | LibC::ICANON | LibC::ISIG | LibC::IEXTEN)\n      termios.c_cflag &= ~(LibC::CSIZE | LibC::PARENB)\n      termios.c_cflag |= LibC::CS8\n      termios.c_cc[LibC::VMIN] = 1\n      termios.c_cc[LibC::VTIME] = 0\n    {% end %}\n    termios\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/file_info.cr",
    "content": "module Crystal::System::FileInfo\n  def initialize(@stat : LibC::Stat)\n  end\n\n  def system_size : Int64\n    @stat.st_size.to_i64\n  end\n\n  def system_permissions : ::File::Permissions\n    ::File::Permissions.new((@stat.st_mode & 0o777).to_i16)\n  end\n\n  def system_type : ::File::Type\n    case @stat.st_mode & LibC::S_IFMT\n    when LibC::S_IFBLK\n      ::File::Type::BlockDevice\n    when LibC::S_IFCHR\n      ::File::Type::CharacterDevice\n    when LibC::S_IFDIR\n      ::File::Type::Directory\n    when LibC::S_IFIFO\n      ::File::Type::Pipe\n    when LibC::S_IFLNK\n      ::File::Type::Symlink\n    when LibC::S_IFREG\n      ::File::Type::File\n    when LibC::S_IFSOCK\n      ::File::Type::Socket\n    else\n      ::File::Type::Unknown\n    end\n  end\n\n  def system_flags : ::File::Flags\n    flags = ::File::Flags::None\n    flags |= ::File::Flags::SetUser if @stat.st_mode.bits_set? LibC::S_ISUID\n    flags |= ::File::Flags::SetGroup if @stat.st_mode.bits_set? LibC::S_ISGID\n    flags |= ::File::Flags::Sticky if @stat.st_mode.bits_set? LibC::S_ISVTX\n    flags\n  end\n\n  def system_modification_time : ::Time\n    {% if flag?(:darwin) %}\n      ::Time.new(@stat.st_mtimespec, ::Time::Location::UTC)\n    {% else %}\n      ::Time.new(@stat.st_mtim, ::Time::Location::UTC)\n    {% end %}\n  end\n\n  def system_owner_id : String\n    @stat.st_uid.to_s\n  end\n\n  def system_group_id : String\n    @stat.st_gid.to_s\n  end\n\n  def system_same_file?(other : self) : Bool\n    @stat.st_dev == other.@stat.st_dev && @stat.st_ino == other.@stat.st_ino\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/getrandom.cr",
    "content": "require \"c/sys/random\"\n\nmodule Crystal::System::Random\n  # Reads n random bytes using the Linux `getrandom(2)` syscall.\n  def self.random_bytes(buffer : Bytes) : Nil\n    getrandom(buffer)\n  end\n\n  def self.next_u : UInt8\n    buffer = uninitialized UInt8\n    getrandom(pointerof(buffer).to_slice(1))\n    buffer\n  end\n\n  # Reads n random bytes using the Linux `getrandom(2)` syscall.\n  private def self.getrandom(buffer)\n    # getrandom(2) may only read up to 256 bytes at once without being\n    # interrupted or returning early\n    chunk_size = 256\n\n    while buffer.size > 0\n      read_bytes = 0\n\n      loop do\n        # pass GRND_NONBLOCK flag so that it fails with EAGAIN if the requested\n        # entropy was not available\n        read_bytes = LibC.getrandom(buffer, buffer.size.clamp(..chunk_size), LibC::GRND_NONBLOCK)\n        break unless read_bytes == -1\n\n        err = Errno.value\n        raise RuntimeError.from_os_error(\"getrandom\", err) unless err.in?(Errno::EINTR, Errno::EAGAIN)\n\n        ::Fiber.yield\n      end\n\n      buffer += read_bytes\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/group.cr",
    "content": "require \"c/grp\"\nrequire \"../unix\"\n\nmodule Crystal::System::Group\n  private GETGR_R_SIZE_MAX = 1024 * 16\n\n  def initialize(@name : String, @id : String)\n  end\n\n  def system_name\n    @name\n  end\n\n  def system_id\n    @id\n  end\n\n  private def self.from_struct(grp)\n    ::System::Group.new(String.new(grp.gr_name), grp.gr_gid.to_s)\n  end\n\n  def self.from_name?(groupname : String)\n    groupname.check_no_null_byte\n\n    grp = uninitialized LibC::Group\n    grp_pointer = Pointer(LibC::Group).null\n    System.retry_with_buffer(\"getgrnam_r\", GETGR_R_SIZE_MAX) do |buf|\n      LibC.getgrnam_r(groupname, pointerof(grp), buf, buf.size, pointerof(grp_pointer)).tap do\n        # It's not necessary to check success with `ret == 0` because `grp_pointer` will be NULL on failure\n        return from_struct(grp) if grp_pointer\n      end\n    end\n  end\n\n  def self.from_id?(groupid : String)\n    groupid = groupid.to_u32?\n    return unless groupid\n\n    grp = uninitialized LibC::Group\n    grp_pointer = Pointer(LibC::Group).null\n    System.retry_with_buffer(\"getgrgid_r\", GETGR_R_SIZE_MAX) do |buf|\n      LibC.getgrgid_r(groupid, pointerof(grp), buf, buf.size, pointerof(grp_pointer)).tap do\n        # It's not necessary to check success with `ret == 0` because `grp_pointer` will be NULL on failure\n        return from_struct(grp) if grp_pointer\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/hostname.cr",
    "content": "require \"c/unistd\"\n\nmodule Crystal::System\n  def self.hostname\n    String.new(255) do |buffer|\n      unless LibC.gethostname(buffer, LibC::SizeT.new(255)) == 0\n        raise RuntimeError.from_errno(\"Could not get hostname\")\n      end\n      len = LibC.strlen(buffer)\n      {len, len}\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/kqueue.cr",
    "content": "require \"c/sys/event\"\n\nstruct Crystal::System::Kqueue\n  @kq : LibC::Int\n\n  def initialize\n    @kq =\n      {% if LibC.has_method?(:kqueue1) %}\n        LibC.kqueue1(LibC::O_CLOEXEC)\n      {% else %}\n        LibC.kqueue\n      {% end %}\n    if @kq == -1\n      function_name = {% if LibC.has_method?(:kqueue1) %} \"kqueue1\" {% else %} \"kqueue\" {% end %}\n      raise RuntimeError.from_errno(function_name)\n    end\n  end\n\n  # Helper to register a single event. Returns immediately.\n  def kevent(ident, filter, flags, fflags = 0, data = 0, udata = nil, &) : Nil\n    kevent = uninitialized LibC::Kevent\n    Kqueue.set pointerof(kevent), ident, filter, flags, fflags, data, udata\n    ret = LibC.kevent(@kq, pointerof(kevent), 1, nil, 0, nil)\n    yield if ret == -1\n  end\n\n  # Helper to register a single event. Returns immediately.\n  def kevent(ident, filter, flags, fflags = 0, data = 0, udata = nil) : Nil\n    kevent(ident, filter, flags, fflags, data, udata) do\n      raise RuntimeError.from_errno(\"kevent\")\n    end\n  end\n\n  # Helper to register multiple *changes*. Returns immediately.\n  def kevent(changes : Slice(LibC::Kevent), &) : Nil\n    ret = LibC.kevent(@kq, changes.to_unsafe, changes.size, nil, 0, nil)\n    yield if ret == -1\n  end\n\n  # Waits for registered events to become active. Returns a subslice to\n  # *events*.\n  #\n  # Timeout is relative to now; blocks indefinitely if `nil`; returns\n  # immediately if zero.\n  def wait(events : Slice(LibC::Kevent), timeout : ::Time::Span? = nil) : Slice(LibC::Kevent)\n    if timeout\n      ts = uninitialized LibC::Timespec\n      ts.tv_sec = typeof(ts.tv_sec).new!(timeout.@seconds)\n      ts.tv_nsec = typeof(ts.tv_nsec).new!(timeout.@nanoseconds)\n      tsp = pointerof(ts)\n    else\n      tsp = Pointer(LibC::Timespec).null\n    end\n\n    changes = Slice(LibC::Kevent).empty\n    count = 0\n\n    loop do\n      count = LibC.kevent(@kq, changes.to_unsafe, changes.size, events.to_unsafe, events.size, tsp)\n      break unless count == -1\n\n      if Errno.value == Errno::EINTR\n        # retry when waiting indefinitely, return otherwise\n        break if timeout\n      else\n        raise RuntimeError.from_errno(\"kevent\")\n      end\n    end\n\n    events[0, count.clamp(0..)]\n  end\n\n  def close : Nil\n    LibC.close(@kq)\n  end\n\n  @[AlwaysInline]\n  def self.set(kevent : LibC::Kevent*, ident, filter, flags, fflags = 0, data = 0, udata = nil) : Nil\n    kevent.value.ident = ident\n    kevent.value.filter = filter\n    kevent.value.flags = flags\n    kevent.value.fflags = fflags\n    kevent.value.data = data\n    kevent.value.udata = udata ? udata.as(Void*) : Pointer(Void).null\n    {% if LibC::Kevent.has_method?(:ext) %}\n      kevent.value.ext.fill(0)\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/linux_cpucount.cr",
    "content": "{% skip_file unless flag?(:linux) %}\n\nrequire \"./syscall\"\n\nmodule Crystal::System\n  def self.effective_cpu_count\n    {% unless flag?(:interpreted) %}\n      # we use the syscall because it returns the number of bytes to check in\n      # the set, while glibc always returns 0 and would require to zero the\n      # buffer and check every byte\n      set = uninitialized UInt8[8192] # allows up to 65536 logical cpus\n      byte_count = Syscall.sched_getaffinity(0, LibC::SizeT.new(8192), set.to_unsafe)\n      if byte_count > 0\n        count = set.to_slice[0, byte_count].sum(&.popcount)\n        return count if count > 0\n      end\n    {% end %}\n\n    -1\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/main.cr",
    "content": "require \"c/stdlib\"\n\n# Prefer explicit exit over returning the status, so we are free to resume the\n# main thread's fiber on any thread, without occurring a weird behavior where\n# another thread returns from main when the caller might expect the main thread\n# to be the one returning.\n\nfun main(argc : Int32, argv : UInt8**) : Int32\n  status = Crystal.main(argc, argv)\n  LibC.exit(status)\nend\n"
  },
  {
    "path": "src/crystal/system/unix/mime.cr",
    "content": "module Crystal::System::MIME\n  MIME_SOURCES = {\n    \"/etc/mime.types\",                      # Linux\n    \"/etc/httpd/mime.types\",                # Apache on Mac OS X\n    \"/usr/local/etc/mime.types\",            # FreeBSD\n    \"/usr/share/misc/mime.types\",           # OpenBSD\n    \"/etc/httpd/conf/mime.types\",           # Apache\n    \"/etc/apache/mime.types\",               # Apache 1\n    \"/etc/apache2/mime.types\",              # Apache 2\n    \"/usr/local/lib/netscape/mime.types\",   # Netscape\n    \"/usr/local/etc/httpd/conf/mime.types\", # Apache 1.2\n  }\n\n  # Load MIME types from operating system source.\n  def self.load\n    MIME_SOURCES.each do |path|\n      next unless ::File.exists?(path)\n      ::File.open(path) do |file|\n        ::MIME.load_mime_database file\n      end\n    rescue\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/path.cr",
    "content": "require \"./user\"\n\nmodule Crystal::System::Path\n  def self.home : String\n    if home_path = ENV[\"HOME\"]?.presence\n      home_path\n    else\n      id = LibC.getuid\n\n      pwd = uninitialized LibC::Passwd\n      pwd_pointer = Pointer(LibC::Passwd).null\n      ret = LibC::Int.new(0)\n      System.retry_with_buffer(\"getpwuid_r\", User::GETPW_R_SIZE_MAX) do |buf|\n        ret = LibC.getpwuid_r(id, pointerof(pwd), buf, buf.size, pointerof(pwd_pointer)).tap do\n          # It's not necessary to check success with `ret == 0` because `pwd_pointer` will be NULL on failure\n          return String.new(pwd.pw_dir) if pwd_pointer\n        end\n      end\n\n      raise RuntimeError.from_os_error(\"getpwuid_r\", Errno.new(ret))\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/process.cr",
    "content": "require \"c/signal\"\nrequire \"c/stdlib\"\nrequire \"c/sys/resource\"\nrequire \"c/unistd\"\nrequire \"c/limits\"\nrequire \"crystal/rw_lock\"\nrequire \"file/error\"\nrequire \"./spawn\"\n\nstruct Crystal::System::Process\n  getter pid : LibC::PidT\n\n  def initialize(@pid : LibC::PidT)\n    @channel = Crystal::System::SignalChildHandler.wait(@pid)\n  end\n\n  def release\n  end\n\n  def wait\n    @channel.receive\n  end\n\n  def exists?\n    !@channel.closed? && Crystal::System::Process.exists?(@pid)\n  end\n\n  def terminate(*, graceful)\n    Crystal::System::Process.signal(@pid, graceful ? LibC::SIGTERM : LibC::SIGKILL)\n  end\n\n  def self.exit(status : Int32)\n    LibC.exit(status)\n  end\n\n  def self.exit(status : ::Process::Status)\n    if signal = status.exit_signal?\n      signal(self.pid, signal)\n\n      # The same signal that killed the child process might not kill the parent.\n      # We have to exit explicitly. The exist status adding 128 is a convention.\n      exit 128 + signal.value\n    else\n      exit status.exit_code\n    end\n  end\n\n  def self.pid\n    LibC.getpid\n  end\n\n  def self.pgid\n    ret = LibC.getpgid(0)\n    raise RuntimeError.from_errno(\"getpgid\") if ret < 0\n    ret\n  end\n\n  def self.pgid(pid)\n    # Disallow users from depending on ppid(0) instead of `pgid`\n    raise RuntimeError.from_os_error(\"getpgid\", Errno::EINVAL) if pid == 0\n\n    ret = LibC.getpgid(pid)\n    raise RuntimeError.from_errno(\"getpgid\") if ret < 0\n    ret\n  end\n\n  def self.ppid\n    LibC.getppid\n  end\n\n  def self.signal(pid, signal)\n    ret = LibC.kill(pid, signal)\n    raise RuntimeError.from_errno(\"kill\") if ret < 0\n  end\n\n  @[Deprecated(\"Use `#on_terminate` instead\")]\n  def self.on_interrupt(&handler : ->) : Nil\n    ::Signal::INT.trap { |_signal| handler.call }\n  end\n\n  def self.on_terminate(&handler : ::Process::ExitReason ->) : Nil\n    sig_handler = Proc(::Signal, Nil).new do |signal|\n      int_type = case signal\n                 when .int?\n                   ::Process::ExitReason::Interrupted\n                 when .hup?\n                   ::Process::ExitReason::TerminalDisconnected\n                 when .term?\n                   ::Process::ExitReason::SessionEnded\n                 else\n                   ::Process::ExitReason::Interrupted\n                 end\n      handler.call int_type\n\n      # ignore prevents system defaults and clears registered interrupts\n      # hence we need to re-register\n      signal.ignore\n      Process.on_terminate &handler\n    end\n    ::Signal::INT.trap &sig_handler\n    ::Signal::HUP.trap &sig_handler\n    ::Signal::TERM.trap &sig_handler\n  end\n\n  def self.ignore_interrupts! : Nil\n    ::Signal::INT.ignore\n  end\n\n  def self.restore_interrupts! : Nil\n    ::Signal::INT.reset\n  end\n\n  def self.start_interrupt_loop : Nil\n    # do nothing; `Crystal::System::Signal.start_loop` takes care of this\n  end\n\n  def self.debugger_present? : Bool\n    {% if flag?(:linux) %}\n      ::File.each_line(\"/proc/self/status\") do |line|\n        if tracer_pid = line.lchop?(\"TracerPid:\").try(&.to_i?)\n          return true if tracer_pid != 0\n        end\n      end\n    {% end %}\n\n    # TODO: [Darwin](https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x)\n    # TODO: other BSDs\n    # TODO: [Solaris](https://docs.oracle.com/cd/E23824_01/html/821-1473/proc-4.html)\n    false\n  end\n\n  def self.exists?(pid)\n    ret = LibC.kill(pid, 0)\n    if ret == 0\n      true\n    else\n      case Errno.value\n      when Errno::EPERM\n        true\n      when Errno::ESRCH\n        false\n      else\n        raise RuntimeError.from_errno(\"kill\")\n      end\n    end\n  end\n\n  def self.times\n    LibC.getrusage(LibC::RUSAGE_SELF, out usage)\n    LibC.getrusage(LibC::RUSAGE_CHILDREN, out child)\n\n    ::Process::Tms.new(\n      usage.ru_utime.tv_sec.to_f64 + usage.ru_utime.tv_usec.to_f64 / 1e6,\n      usage.ru_stime.tv_sec.to_f64 + usage.ru_stime.tv_usec.to_f64 / 1e6,\n      child.ru_utime.tv_sec.to_f64 + child.ru_utime.tv_usec.to_f64 / 1e6,\n      child.ru_stime.tv_sec.to_f64 + child.ru_stime.tv_usec.to_f64 / 1e6,\n    )\n  end\n\n  # The RWLock is trying to protect against file descriptors leaking to\n  # sub-processes.\n  #\n  # There is a race condition in the POSIX standard between the creation of a\n  # file descriptor (`accept`, `dup`, `open`, `pipe`, `socket`) and setting the\n  # `FD_CLOEXEC` flag with `fcntl`. During the time window between those two\n  # syscalls, another thread may fork the process and exec another process,\n  # which will leak the file descriptor to that process.\n  #\n  # Most systems have long implemented non standard syscalls that prevent the\n  # race condition, except for Darwin that implements `O_CLOEXEC` but doesn't\n  # implement `SOCK_CLOEXEC` nor `accept4`, `dup3` or `pipe2`.\n  #\n  # NOTE: there may still be some potential leaks (e.g. calling `accept` on a\n  #       blocking socket).\n  #\n  # `SOCK_CLOEXEC` and `accept4` are defined in `c/sys/socket.cr` which is only\n  # included when using sockets. The absence of `LibC.socket` indicates that\n  # we're not using sockets.\n  @@rwlock = Crystal::RWLock.new\n\n  def self.lock_read(&)\n    {% if (LibC.has_constant?(:SOCK_CLOEXEC) && (LibC.has_method?(:accept4)) || !LibC.has_method?(:socket)) && LibC.has_method?(:dup3) && LibC.has_method?(:pipe2) %}\n      {% raise \"BUG: Crystal::System::Process.lock_read is only for targets without accept4, dup3 or pipe2\" %}\n    {% else %}\n      @@rwlock.read_lock\n      begin\n        yield\n      ensure\n        @@rwlock.read_unlock\n      end\n    {% end %}\n  end\n\n  def self.lock_write(&)\n    {% if (LibC.has_constant?(:SOCK_CLOEXEC) && (LibC.has_method?(:accept4)) || !LibC.has_method?(:socket)) && LibC.has_method?(:dup3) && LibC.has_method?(:pipe2) %}\n      yield\n    {% else %}\n      @@rwlock.write_lock\n      begin\n        yield\n      ensure\n        @@rwlock.write_unlock\n      end\n    {% end %}\n  end\n\n  # Only used by deprecated `::Process.fork`\n  def self.fork\n    {% raise(\"Process fork is unsupported with multithreaded mode\") if flag?(:preview_mt) %}\n\n    pid, errno = lock_write do\n      pthread_disable_cancelstate do\n        block_signals do\n          pid = LibC.fork\n          {pid, Errno.value}\n        end\n      end\n    end\n\n    case pid\n    when 0\n      # forked process\n      ::Process.after_fork_child_callbacks.each(&.call)\n\n      nil\n    when -1\n      # forking process: error\n      raise RuntimeError.from_os_error(\"fork\", errno)\n    else\n      # forking process: success\n      pid\n    end\n  end\n\n  private def self.block_signals(&)\n    newmask = uninitialized LibC::SigsetT\n    oldmask = uninitialized LibC::SigsetT\n\n    # block signals while we fork, so the child process won't forward signals it\n    # may receive to the parent through the signal pipe, but make sure to not\n    # block stop-the-world signals as it appears to create deadlocks in glibc\n    # for example; this is safe because these signal handlers musn't be\n    # registered through `Signal.trap` but directly through `sigaction`.\n    LibC.sigfillset(pointerof(newmask))\n    LibC.sigdelset(pointerof(newmask), System::Thread.sig_suspend)\n    LibC.sigdelset(pointerof(newmask), System::Thread.sig_resume)\n    ret = LibC.pthread_sigmask(LibC::SIG_SETMASK, pointerof(newmask), pointerof(oldmask))\n    raise RuntimeError.from_errno(\"Failed to disable signals\") unless ret == 0\n\n    begin\n      yield pointerof(oldmask)\n    ensure\n      LibC.pthread_sigmask(LibC::SIG_SETMASK, pointerof(oldmask), nil)\n    end\n  end\n\n  private def self.pthread_disable_cancelstate(&)\n    # No thread cancellation on android\n    {% if flag?(:android) %}\n      yield\n    {% else %}\n      LibC.pthread_setcancelstate(LibC::PTHREAD_CANCEL_DISABLE, out cancel_state)\n\n      begin\n        yield\n      ensure\n        LibC.pthread_setcancelstate(cancel_state, nil)\n      end\n    {% end %}\n  end\n\n  # Duplicates the current process.\n  # Returns a `Process` representing the new child process in the current process\n  # and `nil` inside the new child process.\n  # Only used by deprecated `::Process.fork(&)` and compiler `fork_codegen`\n  def self.fork(&)\n    pid = fork\n    return pid if pid\n\n    begin\n      yield\n      LibC._exit 0\n    rescue ex\n      ex.inspect_with_backtrace STDERR\n      STDERR.flush\n      LibC._exit 1\n    ensure\n      LibC._exit 254 # not reached\n    end\n  end\n\n  def self.prepare_args(command : String, args : Enumerable(String)?, shell : Bool) : {String, LibC::Char**}\n    if shell\n      command = %(#{command} \"${@}\") unless command.includes?(' ')\n      command_args = [\"/bin/sh\", \"-c\", command, \"sh\"]\n\n      if args\n        unless command.includes?(%(\"${@}\"))\n          raise ArgumentError.new(%(Can't specify arguments in both command and args without including \"${@}\" into your command))\n        end\n      end\n    else\n      command_args = [command]\n    end\n\n    command_args.concat(args) if args\n\n    prepare_args(command_args)\n  end\n\n  def self.prepare_args(args : Enumerable(String)) : {String, LibC::Char**}\n    pathname = args.first\n    argv = args.map(&.check_no_null_byte.to_unsafe)\n    {pathname, argv.to_unsafe}\n  end\n\n  private def self.execvpe(file, argv, envp)\n    {% if LibC.has_method?(\"execvpe\") && !flag?(\"execvpe_impl\") %}\n      lock_write { LibC.execvpe(file, argv, envp) }\n    {% else %}\n      execvpe_impl(file, argv, envp)\n    {% end %}\n  end\n\n  DEFAULT_PATH = \"/usr/bin:/bin\"\n\n  # Darwin, DragonflyBSD, and FreeBSD < 14 don't have an `execvpe` function, so\n  # we need to implement it ourselves.\n  # This method runs between `fork` and `exec` and must be very cautious, such\n  # as no memory allocations.\n  private def self.execvpe_impl(file : String, argv : LibC::Char**, envp : LibC::Char**)\n    if file.empty?\n      Errno.value = Errno::ENOENT\n      return\n    end\n\n    # When file contains a slash, it's already a pathname that we should execute.\n    if file.includes?(\"/\")\n      lock_write { LibC.execve(file, argv, envp) }\n\n      # Glibc implements a fallback if execve fails with ENOEXEC which tries\n      # executing `file` with `/bin/sh`. This is a legacy compatibility feature and\n      # has security concerns. We implement the behaviour of `execvpex`.\n      return\n    end\n\n    path = if path_ptr = LibC.getenv(\"PATH\")\n             Slice.new(path_ptr, LibC.strlen(path_ptr))\n           else\n             DEFAULT_PATH.to_slice\n           end\n\n    if file.bytesize > LibC::NAME_MAX\n      Errno.value = Errno::ENAMETOOLONG\n      return\n    end\n\n    buffer = uninitialized UInt8[LibC::PATH_MAX]\n\n    seen_eaccess = false\n\n    while path.size > 0\n      if index = path.index(':'.ord.to_u8!)\n        path_entry = path[0, index]\n        path += index\n\n        # Do not advance a trailing `:` so that we read it as an empty path in\n        # the next iteration\n        path += 1 unless path.size == 1\n      else\n        path_entry = path\n        path += path_entry.size\n      end\n\n      # When the full pathname would be too long, simply skip it.\n      # This is an edge case. BSD implementations usually also print a warning.\n      # Cosmopolitan libc even errors.\n      if path_entry.size + file.bytesize + 2 >= buffer.size\n        next\n      end\n\n      builder = buffer.to_slice\n\n      if path_entry.empty?\n        # empty path means current directory\n        builder[0] = '.'.ord.to_u8!\n        builder += 1\n      else\n        path_entry.copy_to(builder)\n        builder += path_entry.size\n      end\n      builder[0] = '/'.ord.to_u8!\n      builder += 1\n      file.to_slice.copy_to(builder)\n      builder += file.size\n      builder[0] = 0\n\n      lock_write { LibC.execve(buffer.to_slice[0, buffer.size - builder.size], argv, envp) }\n\n      case Errno.value\n      when Errno::EACCES\n        # Non-terminal condition. Take note that we encountered EACCES and error\n        # with that if no other candidate is found.\n        seen_eaccess = true\n      when Errno::ENOENT, Errno::ENOTDIR\n        # Non terminal condition. Skip.\n      else\n        # Terminal condition. Return immediately. We found a file that exists\n        # is accessible, but it wouldn't execute.\n        return\n      end\n    end\n\n    Errno.value = if seen_eaccess\n                    # Erroring with ENOENT would be misleading if we found a candidate but\n                    # couldn't access it and thus skipped it.\n                    Errno::EACCES\n                  else\n                    # Make sure to set an error in case we never tried any path (e.g. `PATH=`)\n                    Errno::ENOENT\n                  end\n  end\n\n  def self.replace(command, args, shell, env, clear_env, input, output, error, chdir)\n    prepared_args = prepare_args(command, args, shell)\n    envp = Env.make_envp(env, clear_env)\n\n    # The following steps are similar to `.try_replace` (used for `fork`/`exec`)\n    # with some differences because we're not spawning a new process.\n    reopen_io(input, ORIGINAL_STDIN)\n    reopen_io(output, ORIGINAL_STDOUT)\n    reopen_io(error, ORIGINAL_STDERR)\n\n    if chdir\n      ::Dir.cd(chdir) do\n        execvpe(*prepared_args, envp)\n      end\n    else\n      execvpe(*prepared_args, envp)\n    end\n\n    raise_exception_from_errno(command)\n  end\n\n  private def self.raise_exception_from_errno(command, errno = Errno.value, &)\n    if ::File::NotFoundError.os_error?(errno) || ::File::AccessDeniedError.os_error?(errno) || errno == Errno::ENOEXEC\n      yield errno, command\n    else\n      raise IO::Error.from_os_error(\"Error executing process: '#{command}'\", errno)\n    end\n  end\n\n  private def self.raise_exception_from_errno(command, errno = Errno.value)\n    raise_exception_from_errno(command, errno) do |errno, command|\n      raise ::File::Error.from_os_error(\"Error executing process\", errno, file: command)\n    end\n  end\n\n  private def self.reopen_io(src_io : IO::FileDescriptor, dst_io : IO::FileDescriptor)\n    if src_io.closed?\n      # Do not use FileDescriptor.file_descriptor_close here because it\n      # mutates the memory of `dst_id.fd` in `close_volatile_fd?` which causes\n      # problems with `vfork` behaviour.\n      # We can ignore any errors from `LibC.close`.\n      LibC.close(dst_io.fd)\n    else\n      src_io = to_real_fd(src_io)\n\n      # dst_io.reopen(src_io)\n      ret = LibC.dup2(src_io.fd, dst_io.fd)\n      raise IO::Error.from_errno(\"dup2\") if ret == -1\n\n      FileDescriptor.set_blocking(dst_io.fd, true)\n      dst_io.close_on_exec = false\n    end\n  end\n\n  private def self.to_real_fd(fd : IO::FileDescriptor)\n    case fd\n    when STDIN  then ORIGINAL_STDIN\n    when STDOUT then ORIGINAL_STDOUT\n    when STDERR then ORIGINAL_STDERR\n    else             fd\n    end\n  end\n\n  def self.chroot(path)\n    path.check_no_null_byte\n    if LibC.chroot(path) != 0\n      raise RuntimeError.from_errno(\"Failed to chroot\")\n    end\n\n    if LibC.chdir(\"/\") != 0\n      errno = RuntimeError.from_errno(\"chdir after chroot failed\")\n      errno.callstack = Exception::CallStack.new\n      errno.inspect_with_backtrace(STDERR)\n      abort(\"Unresolvable state, exiting...\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/pthread.cr",
    "content": "require \"c/pthread\"\nrequire \"c/sched\"\nrequire \"../panic\"\n\nmodule Crystal::System::Thread\n  alias Handle = LibC::PthreadT\n\n  def to_unsafe\n    @system_handle\n  end\n\n  protected setter system_handle\n\n  private def init_handle\n    # NOTE: `@system_handle` needs to be set here too, not just in\n    # `.thread_proc`, since the current thread might progress first; the value\n    # of `LibC.pthread_self` inside the new thread must be equal to this\n    # `@system_handle` after `pthread_create` returns\n    ret = GC.pthread_create(\n      thread: pointerof(@system_handle),\n      attr: Pointer(LibC::PthreadAttrT).null,\n      start: ->Thread.thread_proc(Void*),\n      arg: self.as(Void*),\n    )\n\n    raise RuntimeError.from_os_error(\"pthread_create\", Errno.new(ret)) unless ret == 0\n  end\n\n  def self.init : Nil\n    {% if flag?(:musl) %}\n      @@main_handle = current_handle\n    {% elsif flag?(:openbsd) || flag?(:android) %}\n      ret = LibC.pthread_key_create(out current_key, nil)\n      raise RuntimeError.from_os_error(\"pthread_key_create\", Errno.new(ret)) unless ret == 0\n      @@current_key = current_key\n    {% end %}\n  end\n\n  def self.thread_proc(data : Void*) : Void*\n    th = data.as(::Thread)\n\n    # `#start` calls `#stack_address`, which might read `@system_handle` before\n    # `GC.pthread_create` updates it in the original thread that spawned the\n    # current one, so we also assign to it here\n    th.system_handle = current_handle\n\n    th.start\n    Pointer(Void).null\n  end\n\n  def self.current_handle : Handle\n    LibC.pthread_self\n  end\n\n  def self.yield_current : Nil\n    ret = LibC.sched_yield\n    raise RuntimeError.from_errno(\"sched_yield\") unless ret == 0\n  end\n\n  # no thread local storage (TLS) for OpenBSD,\n  # we use pthread's specific storage (TSS) instead\n  #\n  # Android appears to support TLS to some degree, but executables fail with\n  # an underaligned TLS segment, see https://github.com/crystal-lang/crystal/issues/13951\n  {% if flag?(:openbsd) || flag?(:android) %}\n    @@current_key = uninitialized LibC::PthreadKeyT\n\n    def self.current_thread : ::Thread\n      if ptr = LibC.pthread_getspecific(@@current_key)\n        ptr.as(::Thread)\n      else\n        # Thread#start sets `Thread.current` as soon it starts. Thus we know\n        # that if `Thread.current` is not set then we are in the main thread\n        self.current_thread = ::Thread.new\n      end\n    end\n\n    def self.current_thread? : ::Thread?\n      if ptr = LibC.pthread_getspecific(@@current_key)\n        ptr.as(::Thread)\n      end\n    end\n\n    def self.current_thread=(thread : ::Thread)\n      ret = LibC.pthread_setspecific(@@current_key, thread.as(Void*))\n      raise RuntimeError.from_os_error(\"pthread_setspecific\", Errno.new(ret)) unless ret == 0\n      thread\n    end\n  {% else %}\n    @[ThreadLocal]\n    @@current_thread : ::Thread?\n\n    def self.current_thread : ::Thread\n      @@current_thread ||= ::Thread.new\n    end\n\n    def self.current_thread? : ::Thread?\n      @@current_thread\n    end\n\n    def self.current_thread=(@@current_thread : ::Thread)\n    end\n  {% end %}\n\n  def self.sleep(time : ::Time::Span) : Nil\n    req = uninitialized LibC::Timespec\n    req.tv_sec = typeof(req.tv_sec).new(time.@seconds)\n    req.tv_nsec = typeof(req.tv_nsec).new(time.@nanoseconds)\n\n    loop do\n      return if LibC.nanosleep(pointerof(req), out rem) == 0\n      raise RuntimeError.from_errno(\"nanosleep() failed\") unless Errno.value == Errno::EINTR\n      req = rem\n    end\n  end\n\n  private def system_join : Exception?\n    ret = GC.pthread_join(@system_handle)\n    RuntimeError.from_os_error(\"pthread_join\", Errno.new(ret)) unless ret == 0\n  end\n\n  private def system_close\n    GC.pthread_detach(@system_handle)\n  end\n\n  private def stack_address : Void*\n    address = Pointer(Void).null\n\n    {% if flag?(:darwin) %}\n      # FIXME: pthread_get_stacksize_np returns bogus value on macOS X 10.9.0:\n      address = LibC.pthread_get_stackaddr_np(@system_handle) - LibC.pthread_get_stacksize_np(@system_handle)\n    {% elsif (flag?(:bsd) && !flag?(:openbsd)) || flag?(:solaris) %}\n      ret = LibC.pthread_attr_init(out attr)\n      unless ret == 0\n        LibC.pthread_attr_destroy(pointerof(attr))\n        raise RuntimeError.from_os_error(\"pthread_attr_init\", Errno.new(ret))\n      end\n\n      if LibC.pthread_attr_get_np(@system_handle, pointerof(attr)) == 0\n        LibC.pthread_attr_getstack(pointerof(attr), pointerof(address), out _)\n      end\n      ret = LibC.pthread_attr_destroy(pointerof(attr))\n      raise RuntimeError.from_os_error(\"pthread_attr_destroy\", Errno.new(ret)) unless ret == 0\n    {% elsif flag?(:linux) %}\n      ret = LibC.pthread_getattr_np(@system_handle, out attr)\n      raise RuntimeError.from_os_error(\"pthread_getattr_np\", Errno.new(ret)) unless ret == 0\n\n      LibC.pthread_attr_getstack(pointerof(attr), pointerof(address), out stack_size)\n\n      ret = LibC.pthread_attr_destroy(pointerof(attr))\n      raise RuntimeError.from_os_error(\"pthread_attr_destroy\", Errno.new(ret)) unless ret == 0\n\n      # with musl-libc, the main thread does not respect `rlimit -Ss` and\n      # instead returns the same default stack size as non-default threads, so\n      # we obtain the rlimit to correct the stack address manually\n      {% if flag?(:musl) %}\n        if Thread.current_is_main?\n          if LibC.getrlimit(LibC::RLIMIT_STACK, out rlim) == 0\n            address = address + stack_size - rlim.rlim_cur\n          else\n            raise RuntimeError.from_errno(\"getrlimit\")\n          end\n        end\n      {% end %}\n    {% elsif flag?(:openbsd) %}\n      ret = LibC.pthread_stackseg_np(@system_handle, out stack)\n      raise RuntimeError.from_os_error(\"pthread_stackseg_np\", Errno.new(ret)) unless ret == 0\n\n      address =\n        if LibC.pthread_main_np == 1\n          stack.ss_sp - stack.ss_size + LibC.sysconf(LibC::SC_PAGESIZE)\n        else\n          stack.ss_sp - stack.ss_size\n        end\n    {% else %}\n      {% raise \"No `Crystal::System::Thread#stack_address` implementation available\" %}\n    {% end %}\n\n    address\n  end\n\n  {% if flag?(:musl) %}\n    @@main_handle = uninitialized Handle\n\n    def self.current_is_main?\n      current_handle == @@main_handle\n    end\n  {% end %}\n\n  # Warning: must be called from the current thread itself, because Darwin\n  # doesn't allow to set the name of any thread but the current one!\n  private def system_name=(name : String) : String\n    {% if flag?(:darwin) %}\n      LibC.pthread_setname_np(name)\n    {% elsif flag?(:netbsd) %}\n      LibC.pthread_setname_np(@system_handle, name, nil)\n    {% elsif LibC.has_method?(:pthread_setname_np) %}\n      LibC.pthread_setname_np(@system_handle, name)\n    {% elsif LibC.has_method?(:pthread_set_name_np) %}\n      LibC.pthread_set_name_np(@system_handle, name)\n    {% else %}\n      {% raise \"No `Crystal::System::Thread#system_name` implementation available\" %}\n    {% end %}\n    name\n  end\n\n  @suspended = Atomic(Bool).new(false)\n\n  def self.init_suspend_resume : Nil\n    install_sig_suspend_signal_handler\n    install_sig_resume_signal_handler\n  end\n\n  private def self.install_sig_suspend_signal_handler\n    action = LibC::Sigaction.new\n    action.sa_flags = LibC::SA_SIGINFO\n    action.sa_sigaction = LibC::SigactionHandlerT.new do |_, _, _|\n      # notify that the thread has been interrupted\n      Thread.current_thread.@suspended.set(true)\n\n      # block all signals but SIG_RESUME\n      mask = uninitialized LibC::SigsetT\n      LibC.sigfillset(pointerof(mask))\n      LibC.sigdelset(pointerof(mask), SIG_RESUME)\n\n      # suspend the thread until it receives the SIG_RESUME signal\n      LibC.sigsuspend(pointerof(mask))\n    end\n    LibC.sigemptyset(pointerof(action.@sa_mask))\n    LibC.sigaction(SIG_SUSPEND, pointerof(action), nil)\n  end\n\n  private def self.install_sig_resume_signal_handler\n    action = LibC::Sigaction.new\n    action.sa_flags = 0\n    action.sa_sigaction = LibC::SigactionHandlerT.new do |_, _, _|\n      # do nothing (a handler is still required to receive the signal)\n    end\n    LibC.sigemptyset(pointerof(action.@sa_mask))\n    LibC.sigaction(SIG_RESUME, pointerof(action), nil)\n  end\n\n  private def system_suspend : Nil\n    @suspended.set(false)\n\n    if LibC.pthread_kill(@system_handle, SIG_SUSPEND) == -1\n      System.panic(\"pthread_kill()\", Errno.value)\n    end\n  end\n\n  private def system_wait_suspended : Nil\n    until @suspended.get\n      Thread.yield_current\n    end\n  end\n\n  private def system_resume : Nil\n    if LibC.pthread_kill(@system_handle, SIG_RESUME) == -1\n      System.panic(\"pthread_kill()\", Errno.value)\n    end\n  end\n\n  # the suspend/resume signals try to follow BDWGC but aren't exact (e.g. it may\n  # use SIGUSR1 and SIGUSR2 on FreeBSD instead of SIGRT).\n\n  private SIG_SUSPEND =\n    {% if flag?(:linux) %}\n      LibC::SIGPWR\n    {% elsif LibC.has_constant?(:SIGRTMIN) %}\n      LibC::SIGRTMIN + 6\n    {% else %}\n      LibC::SIGXFSZ\n    {% end %}\n\n  private SIG_RESUME =\n    {% if LibC.has_constant?(:SIGRTMIN) %}\n      LibC::SIGRTMIN + 5\n    {% else %}\n      LibC::SIGXCPU\n    {% end %}\n\n  def self.sig_suspend : ::Signal\n    if (gc = GC).responds_to?(:sig_suspend)\n      gc.sig_suspend\n    else\n      ::Signal.new(SIG_SUSPEND)\n    end\n  end\n\n  def self.sig_resume : ::Signal\n    if (gc = GC).responds_to?(:sig_resume)\n      gc.sig_resume\n    else\n      ::Signal.new(SIG_RESUME)\n    end\n  end\nend\n\n# In musl (alpine) the calls to unwind API segfaults\n# when the binary is statically linked. This is because\n# some symbols like `pthread_once` are defined as \"weak\"\n# and, for some reason, not linked into the final binary.\n# Adding an explicit reference to the symbol ensures it's\n# included in the statically linked binary.\n{% if flag?(:musl) && flag?(:static) %}\n  lib LibC\n    fun pthread_once(Void*, Void*)\n  end\n\n  fun __crystal_static_musl_workaround\n    LibC.pthread_once(nil, nil)\n  end\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/unix/pthread_condition_variable.cr",
    "content": "require \"c/pthread\"\n\n# :nodoc:\nclass Thread\n  # :nodoc:\n  class ConditionVariable\n    def initialize\n      attributes = uninitialized LibC::PthreadCondattrT\n      LibC.pthread_condattr_init(pointerof(attributes))\n\n      {% unless flag?(:darwin) %}\n        LibC.pthread_condattr_setclock(pointerof(attributes), LibC::CLOCK_MONOTONIC)\n      {% end %}\n\n      ret = LibC.pthread_cond_init(out @cond, pointerof(attributes))\n      raise RuntimeError.from_os_error(\"pthread_cond_init\", Errno.new(ret)) unless ret == 0\n\n      LibC.pthread_condattr_destroy(pointerof(attributes))\n    end\n\n    def signal : Nil\n      ret = LibC.pthread_cond_signal(self)\n      raise RuntimeError.from_os_error(\"pthread_cond_signal\", Errno.new(ret)) unless ret == 0\n    end\n\n    def broadcast : Nil\n      ret = LibC.pthread_cond_broadcast(self)\n      raise RuntimeError.from_os_error(\"pthread_cond_broadcast\", Errno.new(ret)) unless ret == 0\n    end\n\n    def wait(mutex : Thread::Mutex) : Nil\n      ret = LibC.pthread_cond_wait(self, mutex)\n      raise RuntimeError.from_os_error(\"pthread_cond_wait\", Errno.new(ret)) unless ret == 0\n    end\n\n    def wait(mutex : Thread::Mutex, time : Time::Span, & : ->)\n      ret =\n        {% if flag?(:darwin) %}\n          ts = uninitialized LibC::Timespec\n          ts.tv_sec = time.to_i\n          ts.tv_nsec = time.nanoseconds\n\n          LibC.pthread_cond_timedwait_relative_np(self, mutex, pointerof(ts))\n        {% else %}\n          LibC.clock_gettime(LibC::CLOCK_MONOTONIC, out ts)\n          ts.tv_sec += time.to_i\n          ts.tv_nsec += time.nanoseconds\n\n          if ts.tv_nsec >= 1_000_000_000\n            ts.tv_sec += 1\n            ts.tv_nsec -= 1_000_000_000\n          end\n\n          LibC.pthread_cond_timedwait(self, mutex, pointerof(ts))\n        {% end %}\n\n      case errno = Errno.new(ret)\n      when .none?\n        # normal resume from #signal or #broadcast\n      when Errno::ETIMEDOUT\n        yield\n      else\n        raise RuntimeError.from_os_error(\"pthread_cond_timedwait\", errno)\n      end\n    end\n\n    def finalize\n      ret = LibC.pthread_cond_destroy(self)\n      raise RuntimeError.from_os_error(\"pthread_cond_broadcast\", Errno.new(ret)) unless ret == 0\n    end\n\n    def to_unsafe\n      pointerof(@cond)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/pthread_mutex.cr",
    "content": "require \"c/pthread\"\n\n# :nodoc:\nclass Thread\n  # :nodoc:\n  class Mutex\n    def initialize\n      attributes = uninitialized LibC::PthreadMutexattrT\n      LibC.pthread_mutexattr_init(pointerof(attributes))\n      LibC.pthread_mutexattr_settype(pointerof(attributes), LibC::PTHREAD_MUTEX_ERRORCHECK)\n\n      ret = LibC.pthread_mutex_init(out @mutex, pointerof(attributes))\n      raise RuntimeError.from_os_error(\"pthread_mutex_init\", Errno.new(ret)) unless ret == 0\n\n      LibC.pthread_mutexattr_destroy(pointerof(attributes))\n    end\n\n    def lock : Nil\n      ret = LibC.pthread_mutex_lock(self)\n      raise RuntimeError.from_os_error(\"pthread_mutex_lock\", Errno.new(ret)) unless ret == 0\n    end\n\n    def try_lock : Bool\n      case ret = Errno.new(LibC.pthread_mutex_trylock(self))\n      when .none?\n        true\n      when Errno::EBUSY\n        false\n      else\n        raise RuntimeError.from_os_error(\"pthread_mutex_trylock\", ret)\n      end\n    end\n\n    def unlock : Nil\n      ret = LibC.pthread_mutex_unlock(self)\n      raise RuntimeError.from_os_error(\"pthread_mutex_unlock\", Errno.new(ret)) unless ret == 0\n    end\n\n    def synchronize(&)\n      lock\n      yield self\n    ensure\n      unlock\n    end\n\n    def finalize\n      ret = LibC.pthread_mutex_destroy(self)\n      raise RuntimeError.from_os_error(\"pthread_mutex_destroy\", Errno.new(ret)) unless ret == 0\n    end\n\n    def to_unsafe\n      pointerof(@mutex)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/signal.cr",
    "content": "require \"c/signal\"\nrequire \"c/stdio\"\nrequire \"c/sys/wait\"\nrequire \"c/unistd\"\n\nmodule Crystal::System::Signal\n  # The number of libc functions that can be called safely from a signal(2)\n  # handler is very limited. An usual safe solution is to use a pipe(2) and\n  # just write the signal to the file descriptor and nothing more. A loop in\n  # the main program is responsible for reading the signals back from the\n  # pipe(2) and handle the signal there.\n\n  alias Handler = ::Signal ->\n\n  @@pipe : {IO::FileDescriptor, IO::FileDescriptor} = begin\n    IO.pipe(read_blocking: false, write_blocking: true).tap do |reader, writer|\n      # This avoids accidentally closing the pipe from the interpreter.\n      # See https://github.com/crystal-lang/crystal/issues/16040 for details.\n      writer.close_on_finalize = false\n    end\n  end\n\n  @@handlers = {} of ::Signal => Handler\n  @@sigset = Sigset.new\n  class_property child_handler : Handler?\n  @@mutex = Mutex.new(:unchecked)\n\n  def self.trap(signal, handler) : Nil\n    @@mutex.synchronize do\n      unless @@handlers[signal]?\n        @@sigset << signal\n        {% if flag?(:interpreted) && Crystal::Interpreter.class.has_method?(:signal) %}\n          Crystal::Interpreter.signal(signal.value, 2)\n        {% else %}\n          action = LibC::Sigaction.new\n\n          # restart some interrupted syscalls (read, write, accept, ...) instead\n          # of returning EINTR:\n          action.sa_flags = LibC::SA_RESTART\n\n          action.sa_sigaction = LibC::SigactionHandlerT.new do |value, _, _|\n            FileDescriptor.write_fully(writer.fd, pointerof(value)) unless writer.closed?\n          end\n          LibC.sigemptyset(pointerof(action.@sa_mask))\n          LibC.sigaction(signal, pointerof(action), nil)\n        {% end %}\n      end\n      @@handlers[signal] = handler\n    end\n  end\n\n  def self.trap_handler?(signal)\n    @@mutex.synchronize { @@handlers[signal]? }\n  end\n\n  def self.reset(signal) : Nil\n    set(signal, LibC::SIG_DFL)\n  end\n\n  def self.ignore(signal) : Nil\n    set(signal, LibC::SIG_IGN)\n  end\n\n  private def self.set(signal, handler)\n    if signal == ::Signal::CHLD\n      # Clear any existing signal child handler\n      @@child_handler = nil\n      # But keep a default SIGCHLD, Process#wait requires it\n      trap(signal, ->(signal : ::Signal) {\n        SignalChildHandler.call\n        @@child_handler.try(&.call(signal))\n      })\n    else\n      @@mutex.synchronize do\n        @@handlers.delete(signal)\n        {% if flag?(:interpreted) && Crystal::Interpreter.class.has_method?(:signal) %}\n          h = case handler\n              when LibC::SIG_DFL then 0\n              when LibC::SIG_IGN then 1\n              else                    2\n              end\n          Crystal::Interpreter.signal(signal.value, h)\n        {% else %}\n          LibC.signal(signal, handler)\n        {% end %}\n        @@sigset.delete(signal)\n      end\n    end\n  end\n\n  private def self.start_loop\n    spawn(name: \"signal-loop\") do\n      loop do\n        value = reader.read_bytes(Int32)\n      rescue IO::Error\n        next\n      else\n        process(::Signal.new(value))\n      end\n    end\n  end\n\n  private def self.process(signal) : Nil\n    if handler = @@handlers[signal]?\n      non_nil_handler = handler # if handler is closured it will also have the Nil type\n      spawn do\n        non_nil_handler.call(signal)\n      rescue ex\n        ex.inspect_with_backtrace(STDERR)\n        fatal(\"uncaught exception while processing handler for #{signal}\")\n      end\n    else\n      fatal(\"missing handler for #{signal}\")\n    end\n  end\n\n  # Replaces the signal pipe so the child process won't share the file\n  # descriptors of the parent process and send it received signals.\n  def self.after_fork\n    @@pipe.each do |pipe_io|\n      Crystal::EventLoop.remove(pipe_io)\n      pipe_io.file_descriptor_close { }\n    end\n  ensure\n    @@pipe = IO.pipe(read_blocking: false, write_blocking: true)\n  end\n\n  # Resets signal handlers to `SIG_DFL`. This avoids the child to receive\n  # signals that would be sent to the parent process through the signal\n  # pipe.\n  #\n  # We keep a signal set to because accessing @@handlers isn't thread safe —a\n  # thread could be mutating the hash while another one forked. This allows to\n  # only reset a few signals (fast) rather than all (very slow).\n  #\n  # We eventually close the pipe anyway to avoid a potential race where a sigset\n  # wouldn't exactly reflect actual signal state. This avoids sending a children\n  # signal to the parent. Exec will reset the signals properly for the\n  # sub-process.\n  def self.after_fork_before_exec\n    ::Signal.each do |signal|\n      next unless @@sigset.includes?(signal)\n\n      {% if flag?(:interpreted) && Crystal::Interpreter.class.has_method?(:signal) %}\n        Crystal::Interpreter.signal(signal.value, 0)\n      {% else %}\n        LibC.signal(signal, LibC::SIG_DFL)\n      {% end %}\n    end\n  ensure\n    {% unless flag?(:preview_mt) %}\n      @@pipe.each(&.file_descriptor_close)\n    {% end %}\n  end\n\n  private def self.reader\n    @@pipe[0]\n  end\n\n  private def self.writer\n    @@pipe[1]\n  end\n\n  {% unless flag?(:interpreted) %}\n    # :nodoc:\n    def self.writer=(writer : IO::FileDescriptor)\n      @@pipe = {@@pipe[0], writer}\n      writer\n    end\n  {% end %}\n\n  private def self.fatal(message : String)\n    STDERR.puts(\"FATAL: #{message}, exiting\")\n    STDERR.flush\n    LibC._exit(1)\n  end\n\n  @@setup_default_handlers = Atomic(Bool).new(false)\n  @@setup_segfault_handler = Atomic(Bool).new(false)\n  @@segfault_handler = LibC::SigactionHandlerT.new { |sig, info, data|\n    # Capture fault signals (SEGV, BUS) and finish the process printing a backtrace first\n\n    # This handler must not allocate memory via the GC! Expanding the heap or\n    # triggering a GC cycle here could generate another SEGV\n\n    # Determine if the SEGV was inside or 'near' the top of the stack\n    # to check for potential stack overflow. 'Near' is a small\n    # amount larger than a typical stack frame, 4096 bytes here.\n    addr = info.value.si_addr\n\n    current_fiber = ::Fiber.current?\n\n    is_stack_overflow = false\n\n    if current_fiber\n      is_stack_overflow =\n        begin\n          stack_top = current_fiber.@stack.pointer - 4096\n          stack_bottom = current_fiber.@stack.bottom\n          stack_top <= addr < stack_bottom\n        rescue e\n          Crystal::System.print_error \"Error while trying to determine if a stack overflow has occurred. Probable memory corruption\\n\"\n          false\n        end\n    end\n\n    if is_stack_overflow\n      Crystal::System.print_error \"Stack overflow (e.g., infinite or very deep recursion)\\n\"\n    else\n      Crystal::System.print_error \"Invalid memory access (signal %d) at address %p\\n\", sig, addr\n    end\n\n    Exception::CallStack.print_backtrace\n    LibC._exit(sig)\n  }\n\n  def self.setup_default_handlers : Nil\n    return if @@setup_default_handlers.swap(true, :relaxed)\n    @@sigset.clear\n    start_loop\n\n    {% if flag?(:interpreted) && Interpreter.class.has_method?(:signal_descriptor) %}\n      # replace the interpreter's writer pipe with the interpreted, so signals\n      # will be received by the interpreter, but handled by the interpreted\n      # signal loop\n      Crystal::Interpreter.signal_descriptor(@@pipe[1].fd)\n    {% else %}\n      ::Signal::PIPE.ignore\n    {% end %}\n\n    ::Signal::CHLD.reset\n  end\n\n  def self.setup_segfault_handler\n    return if @@setup_segfault_handler.swap(true, :relaxed)\n\n    altstack = LibC::StackT.new\n    altstack.ss_sp = LibC.malloc(LibC::SIGSTKSZ)\n    altstack.ss_size = LibC::SIGSTKSZ\n    altstack.ss_flags = 0\n    LibC.sigaltstack(pointerof(altstack), nil)\n\n    action = LibC::Sigaction.new\n    action.sa_flags = LibC::SA_ONSTACK | LibC::SA_SIGINFO\n    action.sa_sigaction = @@segfault_handler\n    LibC.sigemptyset(pointerof(action.@sa_mask))\n\n    LibC.sigaction(::Signal::SEGV, pointerof(action), nil)\n    LibC.sigaction(::Signal::BUS, pointerof(action), nil)\n  end\nend\n\nstruct Crystal::System::Sigset\n  {% if flag?(:darwin) || flag?(:openbsd) %}\n    @set = LibC::SigsetT.new(0)\n  {% else %}\n    @set = LibC::SigsetT.new\n  {% end %}\n\n  def to_unsafe\n    pointerof(@set)\n  end\n\n  def <<(signal) : Nil\n    LibC.sigaddset(pointerof(@set), signal)\n  end\n\n  def delete(signal) : Nil\n    LibC.sigdelset(pointerof(@set), signal)\n  end\n\n  def includes?(signal) : Bool\n    LibC.sigismember(pointerof(@set), signal) == 1\n  end\n\n  def clear : Nil\n    LibC.sigemptyset(pointerof(@set))\n  end\nend\n\nmodule Crystal::System::SignalChildHandler\n  # Process#wait will block until the sub-process has terminated. On POSIX\n  # systems, the SIGCHLD signal is triggered. We thus always trap SIGCHLD then\n  # reap/memorize terminated child processes and eventually notify\n  # Process#wait through a channel, that may be created before or after the\n  # child process exited.\n\n  @@pending = {} of LibC::PidT => Int32\n  @@waiting = {} of LibC::PidT => Channel(Int32)\n  @@mutex = Mutex.new(:unchecked)\n\n  def self.wait(pid : LibC::PidT) : Channel(Int32)\n    channel = Channel(Int32).new(1)\n\n    @@mutex.lock\n    if exit_code = @@pending.delete(pid)\n      @@mutex.unlock\n      channel.send(exit_code)\n      channel.close\n    else\n      @@waiting[pid] = channel\n      @@mutex.unlock\n    end\n\n    channel\n  end\n\n  def self.call : Nil\n    loop do\n      pid = LibC.waitpid(-1, out exit_code, LibC::WNOHANG)\n\n      case pid\n      when 0\n        return\n      when -1\n        return if Errno.value == Errno::ECHILD\n        raise RuntimeError.from_errno(\"waitpid\")\n      else\n        @@mutex.lock\n        if channel = @@waiting.delete(pid)\n          @@mutex.unlock\n          channel.send(exit_code)\n          channel.close\n        else\n          @@pending[pid] = exit_code\n          @@mutex.unlock\n        end\n      end\n    end\n  end\n\n  def self.after_fork\n    @@pending.clear\n    @@waiting.each_value(&.close)\n    @@waiting.clear\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/socket.cr",
    "content": "require \"c/netdb\"\nrequire \"c/netinet/tcp\"\nrequire \"c/sys/socket\"\n\nmodule Crystal::System::Socket\n  {% if IO.has_constant?(:Evented) %}\n    include IO::Evented\n  {% end %}\n\n  alias Handle = Int32\n\n  def self.socket(family, type, protocol, blocking) : Handle\n    {% if LibC.has_constant?(:SOCK_CLOEXEC) %}\n      flags = type.value | LibC::SOCK_CLOEXEC\n      flags |= LibC::SOCK_NONBLOCK unless blocking\n      fd = LibC.socket(family, flags, protocol)\n      raise ::Socket::Error.from_errno(\"Failed to create socket\") if fd == -1\n      fd\n    {% else %}\n      Process.lock_read do\n        fd = LibC.socket(family, type, protocol)\n        raise ::Socket::Error.from_errno(\"Failed to create socket\") if fd == -1\n        FileDescriptor.fcntl(fd, LibC::F_SETFD, LibC::FD_CLOEXEC)\n        FileDescriptor.fcntl(fd, LibC::F_SETFL, FileDescriptor.fcntl(fd, LibC::F_GETFL) | LibC::O_NONBLOCK) unless blocking\n        fd\n      end\n    {% end %}\n  end\n\n  private def initialize_handle(fd, blocking = nil)\n    {% if Crystal::EventLoop.has_constant?(:Polling) %}\n      @__evloop_data = Crystal::EventLoop::Polling::Arena::INVALID_INDEX\n    {% end %}\n  end\n\n  private def system_bind(addr, addrstr)\n    unless LibC.bind(fd, addr, addr.size) == 0\n      ::Socket::BindError.from_errno(\"Could not bind to '#{addrstr}'\")\n    end\n  end\n\n  private def system_listen(backlog)\n    unless LibC.listen(fd, backlog) == 0\n      ::Socket::Error.from_errno(\"Listen failed\")\n    end\n  end\n\n  private def system_accept : {Handle, Bool}?\n    event_loop.accept(self)\n  end\n\n  private def system_close_read\n    if LibC.shutdown(fd, LibC::SHUT_RD) != 0\n      raise ::Socket::Error.from_errno(\"shutdown read\")\n    end\n  end\n\n  private def system_close_write\n    if LibC.shutdown(fd, LibC::SHUT_WR) != 0\n      raise ::Socket::Error.from_errno(\"shutdown write\")\n    end\n  end\n\n  private def system_send_buffer_size : Int\n    getsockopt LibC::SO_SNDBUF, 0\n  end\n\n  private def system_send_buffer_size=(val : Int)\n    setsockopt LibC::SO_SNDBUF, val\n  end\n\n  private def system_recv_buffer_size : Int\n    getsockopt LibC::SO_RCVBUF, 0\n  end\n\n  private def system_recv_buffer_size=(val : Int)\n    setsockopt LibC::SO_RCVBUF, val\n  end\n\n  private def system_reuse_address? : Bool\n    getsockopt_bool LibC::SO_REUSEADDR\n  end\n\n  private def system_reuse_address=(val : Bool)\n    setsockopt_bool LibC::SO_REUSEADDR, val\n  end\n\n  private def system_reuse_port? : Bool\n    system_getsockopt(LibC::SO_REUSEPORT, 0) do |value|\n      return value != 0\n    end\n\n    if Errno.value == Errno::ENOPROTOOPT\n      false\n    else\n      raise ::Socket::Error.from_errno(\"getsockopt\")\n    end\n  end\n\n  private def system_reuse_port=(val : Bool)\n    setsockopt_bool LibC::SO_REUSEPORT, val\n  end\n\n  private def system_broadcast? : Bool\n    getsockopt_bool LibC::SO_BROADCAST\n  end\n\n  private def system_broadcast=(val : Bool)\n    setsockopt_bool LibC::SO_BROADCAST, val\n  end\n\n  private def system_keepalive? : Bool\n    getsockopt_bool LibC::SO_KEEPALIVE\n  end\n\n  private def system_keepalive=(val : Bool)\n    setsockopt_bool LibC::SO_KEEPALIVE, val\n  end\n\n  private def system_linger\n    v = LibC::Linger.new\n    ret = getsockopt LibC::SO_LINGER, v\n    ret.l_onoff == 0 ? nil : ret.l_linger\n  end\n\n  private def system_linger=(val)\n    v = LibC::Linger.new\n    case val\n    when Int\n      v.l_onoff = 1\n      v.l_linger = val\n    when nil\n      v.l_onoff = 0\n    end\n\n    setsockopt LibC::SO_LINGER, v\n    val\n  end\n\n  private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET, &)\n    optsize = LibC::SocklenT.new(sizeof(typeof(optval)))\n    ret = LibC.getsockopt(fd, level, optname, pointerof(optval), pointerof(optsize))\n    yield optval if ret == 0\n    ret\n  end\n\n  private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    system_getsockopt(optname, optval, level) { |value| return value }\n    raise ::Socket::Error.from_errno(\"getsockopt #{optname}\")\n  end\n\n  private def system_setsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    optsize = LibC::SocklenT.new(sizeof(typeof(optval)))\n\n    ret = LibC.setsockopt(fd, level, optname, pointerof(optval), optsize)\n    raise ::Socket::Error.from_errno(\"setsockopt #{optname}\") if ret == -1\n    ret\n  end\n\n  private def system_blocking?\n    FileDescriptor.get_blocking(fd)\n  end\n\n  private def system_blocking=(value)\n    FileDescriptor.set_blocking(fd, value)\n  end\n\n  def self.get_blocking(fd : Handle)\n    FileDescriptor.get_blocking(fd)\n  end\n\n  def self.set_blocking(fd : Handle, value : Bool)\n    FileDescriptor.set_blocking(fd, value)\n  end\n\n  private def system_close_on_exec?\n    flags = FileDescriptor.fcntl(fd, LibC::F_GETFD)\n    (flags & LibC::FD_CLOEXEC) == LibC::FD_CLOEXEC\n  end\n\n  private def system_close_on_exec=(arg : Bool)\n    system_fcntl(LibC::F_SETFD, arg ? LibC::FD_CLOEXEC : 0)\n    arg\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    FileDescriptor.fcntl(fd, cmd, arg)\n  end\n\n  private def system_fcntl(cmd, arg = 0)\n    FileDescriptor.fcntl(fd, cmd, arg)\n  end\n\n  def self.socketpair(type : ::Socket::Type, protocol : ::Socket::Protocol, blocking : Bool) : {Handle, Handle}\n    fds = uninitialized Handle[2]\n\n    {% if LibC.has_constant?(:SOCK_CLOEXEC) %}\n      flags = type.value | LibC::SOCK_CLOEXEC\n      flags |= LibC::SOCK_NONBLOCK unless blocking\n      if LibC.socketpair(::Socket::Family::UNIX, flags, protocol, fds) == -1\n        raise ::Socket::Error.new(\"socketpair() failed\")\n      end\n    {% else %}\n      Process.lock_read do\n        if LibC.socketpair(::Socket::Family::UNIX, type, protocol, fds) == -1\n          raise ::Socket::Error.new(\"socketpair() failed\")\n        end\n        FileDescriptor.fcntl(fds[0], LibC::F_SETFD, LibC::FD_CLOEXEC)\n        FileDescriptor.fcntl(fds[1], LibC::F_SETFD, LibC::FD_CLOEXEC)\n        unless blocking\n          FileDescriptor.fcntl(fds[0], LibC::F_SETFL, FileDescriptor.fcntl(fds[0], LibC::F_GETFL) | LibC::O_NONBLOCK)\n          FileDescriptor.fcntl(fds[1], LibC::F_SETFL, FileDescriptor.fcntl(fds[1], LibC::F_GETFL) | LibC::O_NONBLOCK)\n        end\n      end\n    {% end %}\n\n    {fds[0], fds[1]}\n  end\n\n  private def system_tty?\n    LibC.isatty(fd) == 1\n  end\n\n  def socket_close(&)\n    # Clear the @volatile_fd before actually closing it in order to\n    # reduce the chance of reading an outdated fd value\n    return unless fd = close_volatile_fd?\n\n    ret = LibC.close(fd)\n\n    if ret != 0\n      case Errno.value\n      when Errno::EINTR, Errno::EINPROGRESS\n        # ignore\n      else\n        yield\n      end\n    end\n  end\n\n  def close_volatile_fd? : Int32?\n    fd = @volatile_fd.swap(-1)\n    fd unless fd == -1\n  end\n\n  def socket_close\n    socket_close do\n      raise ::Socket::Error.from_errno(\"Error closing socket\")\n    end\n  end\n\n  private def system_local_address\n    sockaddr6 = uninitialized LibC::SockaddrIn6\n    sockaddr = pointerof(sockaddr6).as(LibC::Sockaddr*)\n    addrlen = sizeof(LibC::SockaddrIn6).to_u32!\n\n    if LibC.getsockname(fd, sockaddr, pointerof(addrlen)) != 0\n      raise ::Socket::Error.from_errno(\"getsockname\")\n    end\n\n    ::Socket::IPAddress.from(sockaddr, addrlen)\n  end\n\n  private def system_remote_address\n    sockaddr6 = uninitialized LibC::SockaddrIn6\n    sockaddr = pointerof(sockaddr6).as(LibC::Sockaddr*)\n    addrlen = sizeof(LibC::SockaddrIn6).to_u32!\n\n    if LibC.getpeername(fd, sockaddr, pointerof(addrlen)) != 0\n      raise ::Socket::Error.from_errno(\"getpeername\")\n    end\n\n    ::Socket::IPAddress.from(sockaddr, addrlen)\n  end\n\n  {% if flag?(:openbsd) %}\n    private def system_tcp_keepalive_idle\n      raise NotImplementedError.new(\"system_tcp_keepalive_idle\")\n    end\n\n    private def system_tcp_keepalive_idle=(val : Int)\n      raise NotImplementedError.new(\"system_tcp_keepalive_idle=\")\n    end\n\n    private def system_tcp_keepalive_interval\n      raise NotImplementedError.new(\"system_tcp_keepalive_interval\")\n    end\n\n    private def system_tcp_keepalive_interval=(val : Int)\n      raise NotImplementedError.new(\"system_tcp_keepalive_interval=\")\n    end\n\n    private def system_tcp_keepalive_count\n      raise NotImplementedError.new(\"system_tcp_keepalive_count\")\n    end\n\n    private def system_tcp_keepalive_count=(val : Int)\n      raise NotImplementedError.new(\"system_tcp_keepalive_count=\")\n    end\n  {% else %}\n    private def system_tcp_keepalive_idle\n      optname = {% if flag?(:darwin) %}\n        LibC::TCP_KEEPALIVE\n      {% elsif flag?(:netbsd) %}\n        LibC::SO_KEEPALIVE\n      {% else %}\n        LibC::TCP_KEEPIDLE\n      {% end %}\n      getsockopt optname, 0, level: ::Socket::Protocol::TCP\n    end\n\n    private def system_tcp_keepalive_idle=(val : Int)\n      optname = {% if flag?(:darwin) %}\n        LibC::TCP_KEEPALIVE\n      {% elsif flag?(:netbsd) %}\n        LibC::SO_KEEPALIVE\n      {% else %}\n        LibC::TCP_KEEPIDLE\n      {% end %}\n      setsockopt optname, val, level: ::Socket::Protocol::TCP\n      val\n    end\n\n    # The amount of time in seconds between keepalive probes.\n    private def system_tcp_keepalive_interval\n      getsockopt LibC::TCP_KEEPINTVL, 0, level: ::Socket::Protocol::TCP\n    end\n\n    private def system_tcp_keepalive_interval=(val : Int)\n      setsockopt LibC::TCP_KEEPINTVL, val, level: ::Socket::Protocol::TCP\n      val\n    end\n\n    # The number of probes sent, without response before dropping the connection.\n    private def system_tcp_keepalive_count\n      getsockopt LibC::TCP_KEEPCNT, 0, level: ::Socket::Protocol::TCP\n    end\n\n    private def system_tcp_keepalive_count=(val : Int)\n      setsockopt LibC::TCP_KEEPCNT, val, level: ::Socket::Protocol::TCP\n      val\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/crystal/system/unix/spawn.cr",
    "content": "require \"c/signal\"\nrequire \"c/unistd\"\n\nstruct Crystal::System::Process\n  def self.spawn(prepared_args, shell, env, clear_env, input, output, error, chdir, &)\n    r, w = FileDescriptor.system_pipe\n\n    envp = Env.make_envp(env, clear_env)\n\n    pid = self.fork_for_exec\n    if !pid\n      LibC.close(r)\n      begin\n        self.try_replace(prepared_args, envp, input, output, error, chdir)\n        byte = 1_u8\n        errno = Errno.value.to_i32\n        FileDescriptor.write_fully(w, pointerof(byte))\n        FileDescriptor.write_fully(w, pointerof(errno))\n      rescue ex\n        byte = 0_u8\n        message = ex.inspect_with_backtrace\n        FileDescriptor.write_fully(w, pointerof(byte))\n        FileDescriptor.write_fully(w, message.to_slice)\n      ensure\n        LibC.close(w)\n        LibC._exit 127\n      end\n    end\n\n    LibC.close(w)\n    reader_pipe = IO::FileDescriptor.new(r)\n\n    begin\n      case reader_pipe.read_byte\n      when nil\n        # Pipe was closed, no error\n      when 0\n        # Error message coming\n        message = reader_pipe.gets_to_end\n        raise RuntimeError.new(\"Error executing process: '#{prepared_args[0]}': #{message}\")\n      when 1\n        # Errno coming\n        # can't use IO#read_bytes(Int32) because we skipped system/network\n        # endianness check when writing the integer while read_bytes would;\n        # we thus read it in the same as order as written\n        buf = uninitialized StaticArray(UInt8, 4)\n        reader_pipe.read_fully(buf.to_slice)\n        raise_exception_from_errno(prepared_args[0], Errno.new(buf.unsafe_as(Int32))) do |errno, command|\n          yield errno, command\n        end\n      else\n        raise RuntimeError.new(\"BUG: Invalid error response received from subprocess\")\n      end\n    ensure\n      reader_pipe.close\n    end\n\n    pid\n  end\n\n  private def self.fork_for_exec\n    pid, errno = lock_write do\n      pthread_disable_cancelstate do\n        block_signals do |sigmask|\n          pid = LibC.fork\n          if pid == 0\n            # forked process\n\n            Crystal::System::Signal.after_fork_before_exec\n\n            # reset sigmask (inherited on exec)\n            LibC.sigemptyset(sigmask)\n          end\n          {pid, Errno.value}\n        end\n      end\n    end\n\n    case pid\n    when 0\n      # forked process\n      nil\n    when -1\n      # forking process: error\n      raise RuntimeError.from_os_error(\"fork\", errno)\n    else\n      # forking process: success\n      pid\n    end\n  end\n\n  # This method is similar to `.replace` (used for `Process.exec`) with some\n  # differences because we're limited in what we can do in the pre-exec phase\n  # between `fork` and `exec`.\n  private def self.try_replace(prepared_args, envp, input, output, error, chdir)\n    reopen_io(input, ORIGINAL_STDIN)\n    reopen_io(output, ORIGINAL_STDOUT)\n    reopen_io(error, ORIGINAL_STDERR)\n\n    if chdir\n      if 0 != LibC.chdir(chdir)\n        return\n      end\n    end\n\n    execvpe(*prepared_args, envp)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/syscall.cr",
    "content": "{% skip_file unless flag?(:linux) && !flag?(:interpreted) %}\n\nrequire \"c/unistd\"\nrequire \"syscall\"\n\nmodule Crystal::System::Syscall\n  GRND_NONBLOCK = 1u32\n\n  ::Syscall.def_syscall getrandom, LibC::SSizeT, buf : UInt8*, buflen : LibC::SizeT, flags : UInt32\n  ::Syscall.def_syscall sched_getaffinity, LibC::Int, pid : LibC::PidT, cpusetsize : LibC::SizeT, mask : Pointer(UInt8)\nend\n"
  },
  {
    "path": "src/crystal/system/unix/sysconf_cpucount.cr",
    "content": "{% skip_file if flag?(:bsd) %}\n\nrequire \"c/unistd\"\n\nmodule Crystal::System\n  def self.cpu_count\n    LibC.sysconf(LibC::SC_NPROCESSORS_ONLN)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/sysctl_cpucount.cr",
    "content": "{% skip_file unless flag?(:bsd) %}\n\nrequire \"c/sysctl\"\n\n{% if flag?(:freebsd) %}\n  require \"c/sys/cpuset\"\n{% end %}\n\nmodule Crystal::System\n  def self.cpu_count\n    mib = Int32[LibC::CTL_HW, LibC::HW_NCPU]\n    ncpus = 0\n    size = LibC::SizeT.new(sizeof(Int32))\n\n    if LibC.sysctl(mib, 2, pointerof(ncpus), pointerof(size), nil, 0) == 0\n      ncpus\n    else\n      -1\n    end\n  end\n\n  def self.effective_cpu_count\n    {% if flag?(:freebsd) %}\n      buffer = uninitialized UInt8[8192]\n      maxcpus = 0\n      size = LibC::SizeT.new(sizeof(Int32))\n\n      if LibC.sysctlbyname(\"kern.smp.maxcpus\", pointerof(maxcpus), pointerof(size), nil, 0) == 0\n        len = ((maxcpus + 7) // 8).clamp(..buffer.size)\n        set = buffer.to_slice[0, len]\n\n        if LibC.cpuset_getaffinity(LibC::CPU_LEVEL_WHICH, LibC::CPU_WHICH_PID, -1, len, set) == 0\n          return set.sum(&.popcount)\n        end\n      end\n    {% elsif flag?(:netbsd) || flag?(:openbsd) %}\n      mib = Int32[LibC::CTL_HW, LibC::HW_NCPUONLINE]\n      ncpus = 0\n      size = LibC::SizeT.new(sizeof(Int32))\n\n      if LibC.sysctl(mib, 2, pointerof(ncpus), pointerof(size), nil, 0) == 0\n        return ncpus\n      end\n    {% end %}\n\n    -1\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/time.cr",
    "content": "require \"c/sys/time\"\nrequire \"c/time\"\n\n{% if flag?(:android) %}\n  # needed for accessing local timezone\n  require \"c/sys/system_properties\"\n{% end %}\n\nmodule Crystal::System::Time\n  UNIX_EPOCH_IN_SECONDS  = 62135596800_i64\n  NANOSECONDS_PER_SECOND =   1_000_000_000\n\n  def self.compute_utc_seconds_and_nanoseconds : {Int64, Int32}\n    ret = LibC.clock_gettime(LibC::CLOCK_REALTIME, out timespec)\n    raise RuntimeError.from_errno(\"clock_gettime\") unless ret == 0\n    {timespec.tv_sec.to_i64 + UNIX_EPOCH_IN_SECONDS, timespec.tv_nsec.to_i}\n  end\n\n  private def self.clock_gettime(&)\n    # Chose the best monotonic steady clock with nanosecond precision that ticks\n    # while the system is suspended. See https://github.com/crystal-lang/rfcs/pull/15\n    clock = {% if flag?(:darwin) %}\n              # CLOCK_MONOTONIC on Darwin has 1 microsecond resolution, but\n              # CLOCK_MONOTONIC_RAW has a higher resolution.\n              LibC::CLOCK_MONOTONIC_RAW\n            {% elsif flag?(:linux) %}\n              # On Linux, `CLOCK_MONOTONIC` does not count suspended time, but\n              # but `CLOCK_BOOTTIME` does.\n              LibC::CLOCK_BOOTTIME\n            {% else %}\n              # On all other systems, `CLOCK_MONOTONIC` includes suspended time.\n              LibC::CLOCK_MONOTONIC\n            {% end %}\n\n    ret = LibC.clock_gettime(clock, out tp)\n    yield unless ret == 0\n    tp\n  end\n\n  def self.monotonic : {Int64, Int32}\n    tp = clock_gettime do\n      raise RuntimeError.from_errno(\"clock_gettime()\")\n    end\n    {tp.tv_sec.to_i64, tp.tv_nsec.to_i32}\n  end\n\n  def self.ticks : UInt64\n    tp = clock_gettime { }\n    tp.tv_sec.to_u64! &* NANOSECONDS_PER_SECOND &+ tp.tv_nsec.to_u64!\n  end\n\n  def self.to_timespec(time : ::Time)\n    t = uninitialized LibC::Timespec\n    t.tv_sec = typeof(t.tv_sec).new(time.to_unix)\n    t.tv_nsec = typeof(t.tv_nsec).new(time.nanosecond)\n    t\n  end\n\n  def self.to_timeval(time : ::Time)\n    t = uninitialized LibC::Timeval\n    t.tv_sec = typeof(t.tv_sec).new(time.to_unix)\n    t.tv_usec = typeof(t.tv_usec).new(time.nanosecond // ::Time::NANOSECONDS_PER_MICROSECOND)\n    t\n  end\n\n  # Many systems use /usr/share/zoneinfo, Solaris 2 has\n  # /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ,\n  # NixOS has /etc/zoneinfo.\n  ZONE_SOURCES = {\n    \"/usr/share/zoneinfo/\",\n    \"/usr/share/lib/zoneinfo/\",\n    \"/usr/lib/locale/TZ/\",\n    \"/etc/zoneinfo/\",\n  }\n\n  # Android Bionic C-specific locations. These are files rather than directories\n  # and use a different format (see `Time::Location.read_android_tzdata`).\n  ANDROID_TZDATA_SOURCES = {\n    \"/apex/com.android.tzdata/etc/tz/tzdata\",\n    \"/system/usr/share/zoneinfo/tzdata\",\n  }\n\n  def self.zone_sources : Enumerable(String)\n    ZONE_SOURCES\n  end\n\n  def self.android_tzdata_sources : Enumerable(String)\n    ANDROID_TZDATA_SOURCES\n  end\n\n  def self.load_iana_zone(iana_name : String) : ::Time::Location?\n    nil\n  end\n\n  {% if flag?(:android) %}\n    def self.load_localtime : ::Time::Location?\n      # NOTE: although reading a system property is expensive, we don't cache it\n      # here since it is expected that most code should only ever be calling\n      # `Time::Location.load`, which is already a cached class property, rather\n      # than `.load_local`. Bionic itself caches the property like this:\n      # https://android.googlesource.com/platform/bionic/+/master/libc/private/CachedProperty.h\n      return nil unless timezone = getprop(\"persist.sys.timezone\")\n      return nil unless path = ::Time::Location.find_android_tzdata_file(android_tzdata_sources)\n\n      ::File.open(path) do |file|\n        ::Time::Location.read_android_tzdata(file, true) do |name, location|\n          return location if name == timezone\n        end\n      end\n    end\n\n    private def self.getprop(key : String) : String?\n      {% if LibC.has_method?(\"__system_property_read_callback\") %}\n        pi = LibC.__system_property_find(key)\n        value = \"\"\n        LibC.__system_property_read_callback(pi, ->(data, _name, value, _serial) do\n          data.as(String*).value = String.new(value)\n        end, pointerof(value))\n        value.presence\n      {% else %}\n        buf = uninitialized LibC::Char[LibC::PROP_VALUE_MAX]\n        len = LibC.__system_property_get(key, buf)\n        String.new(buf.to_slice[0, len]) if len > 0\n      {% end %}\n    end\n  {% else %}\n    private LOCALTIME = \"/etc/localtime\"\n\n    def self.load_localtime : ::Time::Location?\n      # Try to defer the name of the zoneinfo file from the link target (e.g.\n      # `/usr/share/zoneinfo/Europe/Berlin`) and load the corresponding\n      # location.\n      # We do not load the actual target file, only extract the name so the\n      # resulting location is exactly the same as when loading it explicitly\n      # as `Time::Location.load(\"Europe/Berlin\")`.\n      if ::File.symlink?(\"/etc/localtime\") && (realpath = ::File.readlink?(\"/etc/localtime\"))\n        if pos = realpath.rindex(\"zoneinfo/\")\n          name = realpath[(pos + \"zoneinfo/\".size)..]\n          return ::Time::Location.load(name)\n        end\n      end\n\n      # Only when /etc/localtime is not a symlink or doesn't point to a\n      # zoneinfo/ directory, we read the TZif data from the actual target file\n      # as a fallback.\n      if ::File.file?(LOCALTIME) && ::File::Info.readable?(LOCALTIME)\n        ::File.open(LOCALTIME) do |file|\n          begin\n            ::Time::Location.read_zoneinfo(\"Local\", file)\n          rescue ::Time::Location::InvalidTZDataError\n            nil\n          end\n        end\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/crystal/system/unix/timerfd.cr",
    "content": "{% skip_file unless flag?(:linux) %}\nrequire \"c/sys/timerfd\"\n\nstruct Crystal::System::TimerFD\n  getter fd : Int32\n\n  # Create a `timerfd` instance set to the monotonic clock.\n  def initialize\n    # We must use the same clock as in `Crystal::System::Time.clock_gettime` in\n    # order to accept absolute timers with `Time::Instant` values.\n    # Since `TimerFD` is only used on Linux, we do not have to differentiate\n    # between different targets.\n    @fd = LibC.timerfd_create(LibC::CLOCK_BOOTTIME, LibC::TFD_CLOEXEC)\n    raise RuntimeError.from_errno(\"timerfd_settime\") if @fd == -1\n  end\n\n  # Arm (start) the timer to run at *time* (absolute time).\n  def set(time : ::Time::Instant) : Nil\n    itimerspec = uninitialized LibC::Itimerspec\n    itimerspec.it_interval.tv_sec = 0\n    itimerspec.it_interval.tv_nsec = 0\n    itimerspec.it_value.tv_sec = typeof(itimerspec.it_value.tv_sec).new!(time.@seconds)\n    itimerspec.it_value.tv_nsec = typeof(itimerspec.it_value.tv_nsec).new!(time.@nanoseconds)\n    ret = LibC.timerfd_settime(@fd, LibC::TFD_TIMER_ABSTIME, pointerof(itimerspec), nil)\n    raise RuntimeError.from_errno(\"timerfd_settime\") if ret == -1\n  end\n\n  # Disarm (stop) the timer.\n  def cancel : Nil\n    itimerspec = LibC::Itimerspec.new\n    ret = LibC.timerfd_settime(@fd, LibC::TFD_TIMER_ABSTIME, pointerof(itimerspec), nil)\n    raise RuntimeError.from_errno(\"timerfd_settime\") if ret == -1\n  end\n\n  def close\n    LibC.close(@fd)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/urandom.cr",
    "content": "require \"crystal/once\"\n\nmodule Crystal::System::Random\n  @@initialized = false\n  @@urandom : LibC::Int?\n\n  @[NoInline]\n  private def self.init\n    Crystal.once(pointerof(@@initialized)) do\n      # Directly open the urandom device without going through the event loop;\n      # we never set O_NONBLOCK because it should never block (by definition)...\n      # but we witnessed a case where Linux would fail with EAGAIN then never\n      # trigger a ready event to epoll, blocking a fiber forever.\n      fd = LibC.open(\"/dev/urandom\", LibC::O_RDONLY | LibC::O_CLOEXEC)\n\n      # safety check: urandom can't be a regular disk file or anything, it must\n      # be a character device; if not it's a system security issue\n      return unless FileDescriptor.system_info(fd).type.character_device?\n\n      @@urandom = fd\n    end\n  end\n\n  def self.random_bytes(buf : Bytes) : Nil\n    init unless @@initialized\n\n    unless fd = @@urandom\n      raise \"Failed to access secure source to generate random bytes!\"\n    end\n\n    until buf.empty?\n      case read_bytes = LibC.read(fd, buf.to_unsafe, buf.size)\n      when -1\n        raise ::File::Error.from_errno(\"Failed to read from secure source to generate random bytes!\", file: \"/dev/urandom\")\n      when 0\n        raise IO::EOFError.new\n      else\n        buf += read_bytes\n      end\n    end\n  end\n\n  def self.next_u : UInt8\n    buf = uninitialized UInt8[1]\n    random_bytes(buf.to_slice)\n    buf.to_unsafe.value\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix/user.cr",
    "content": "require \"c/pwd\"\nrequire \"../unix\"\n\nmodule Crystal::System::User\n  GETPW_R_SIZE_MAX = 1024 * 16\n\n  def initialize(@username : String, @id : String, @group_id : String, @name : String, @home_directory : String, @shell : String)\n  end\n\n  def system_username\n    @username\n  end\n\n  def system_id\n    @id\n  end\n\n  def system_group_id\n    @group_id\n  end\n\n  def system_name\n    @name\n  end\n\n  def system_home_directory\n    @home_directory\n  end\n\n  def system_shell\n    @shell\n  end\n\n  private def self.from_struct(pwd)\n    username = String.new(pwd.pw_name)\n    # `pw_gecos` is not part of POSIX and bionic for example always leaves it null\n    user = pwd.pw_gecos ? String.new(pwd.pw_gecos).partition(',')[0] : username\n    ::System::User.new(username, pwd.pw_uid.to_s, pwd.pw_gid.to_s, user, String.new(pwd.pw_dir), String.new(pwd.pw_shell))\n  end\n\n  def self.from_username?(username : String)\n    username.check_no_null_byte\n\n    pwd = uninitialized LibC::Passwd\n    pwd_pointer = Pointer(LibC::Passwd).null\n    System.retry_with_buffer(\"getpwnam_r\", GETPW_R_SIZE_MAX) do |buf|\n      LibC.getpwnam_r(username, pointerof(pwd), buf, buf.size, pointerof(pwd_pointer)).tap do\n        # It's not necessary to check success with `ret == 0` because `pwd_pointer` will be NULL on failure\n        return from_struct(pwd) if pwd_pointer\n      end\n    end\n  end\n\n  def self.from_id?(id : String)\n    id = id.to_u32?\n    return unless id\n\n    pwd = uninitialized LibC::Passwd\n    pwd_pointer = Pointer(LibC::Passwd).null\n    System.retry_with_buffer(\"getpwuid_r\", GETPW_R_SIZE_MAX) do |buf|\n      LibC.getpwuid_r(id, pointerof(pwd), buf, buf.size, pointerof(pwd_pointer)).tap do\n        # It's not necessary to check success with `ret == 0` because `pwd_pointer` will be NULL on failure\n        return from_struct(pwd) if pwd_pointer\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/unix.cr",
    "content": "# :nodoc:\nmodule Crystal::System\n  def self.retry_with_buffer(function_name, max_buffer, &)\n    initial_buf = uninitialized UInt8[1024]\n    buf = initial_buf.to_slice\n\n    while (ret = yield buf) != 0\n      case ret\n      when LibC::ENOENT, LibC::ESRCH, LibC::EBADF, LibC::EPERM\n        return nil\n      when LibC::ERANGE\n        raise RuntimeError.from_errno(function_name) if buf.size >= max_buffer\n        buf = Bytes.new(buf.size * 2)\n      else\n        raise RuntimeError.from_errno(function_name)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/user.cr",
    "content": "module Crystal::System::User\n  # def system_username : String\n\n  # def system_id : String\n\n  # def system_group_id : String\n\n  # def system_name : String\n\n  # def system_home_directory : String\n\n  # def system_shell : String\n\n  # def self.from_username?(username : String) : ::System::User?\n\n  # def self.from_id?(id : String) : ::System::User?\nend\n\n{% if flag?(:wasi) %}\n  require \"./wasi/user\"\n{% elsif flag?(:unix) %}\n  require \"./unix/user\"\n{% elsif flag?(:win32) %}\n  require \"./win32/user\"\n{% else %}\n  {% raise \"No Crystal::System::User implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/system/wasi/addrinfo.cr",
    "content": "module Crystal::System::Addrinfo\n  alias Handle = NoReturn\n\n  protected def initialize(addrinfo : Handle)\n    raise NotImplementedError.new(\"Crystal::System::Addrinfo#initialize\")\n  end\n\n  def system_ip_address : ::Socket::IPAddress\n    raise NotImplementedError.new(\"Crystal::System::Addrinfo#system_ip_address\")\n  end\n\n  def to_unsafe\n    raise NotImplementedError.new(\"Crystal::System::Addrinfo#to_unsafe\")\n  end\n\n  def self.getaddrinfo(domain, service, family, type, protocol, timeout, flags = 0) : Handle\n    raise NotImplementedError.new(\"Crystal::System::Addrinfo.getaddrinfo\")\n  end\n\n  def self.next_addrinfo(addrinfo : Handle) : Handle\n    raise NotImplementedError.new(\"Crystal::System::Addrinfo.next_addrinfo\")\n  end\n\n  def self.free_addrinfo(addrinfo : Handle)\n    raise NotImplementedError.new(\"Crystal::System::Addrinfo.free_addrinfo\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/cpucount.cr",
    "content": "module Crystal::System\n  def self.cpu_count\n    # TODO: There isn't a good way to get the number of CPUs on WebAssembly\n    1\n  end\n\n  def self.effective_cpu_count\n    -1\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/dir.cr",
    "content": "require \"./wasi\"\n\nmodule Crystal::System::Dir\n  private class DirHandle\n    property fd : LibWasi::Fd\n    property buf = Bytes.new(4096)\n    property pos = 4096u32\n    property end_pos = 4096u32\n    property cookie = 0u64\n\n    def initialize(@fd)\n    end\n\n    def fill_buffer(path)\n      err = LibWasi.fd_readdir(@fd, @buf, @buf.size, @cookie, pointerof(@end_pos))\n      raise ::File::Error.from_os_error(\"Error reading directory entries\", err, file: path) unless err.success?\n      @pos = 0\n    end\n  end\n\n  def self.open(path : String) : DirHandle\n    parent_fd, relative_path = Wasi.find_path_preopen(path)\n\n    err = LibWasi.path_open(parent_fd, LibWasi::LookupFlags::SymlinkFollow, relative_path, LibWasi::OpenFlags::Directory, LibWasi::Rights::FdReaddir, LibWasi::Rights::None, LibWasi::FdFlags::None, out fd)\n    raise ::File::Error.from_os_error(\"Error opening directory\", err, file: path) unless err.success?\n\n    DirHandle.new(fd)\n  end\n\n  def self.next_entry(dir, path) : Entry?\n    if dir.end_pos < dir.buf.size && dir.pos >= dir.end_pos\n      return nil\n    end\n\n    if dir.pos + sizeof(LibWasi::DirEnt) > dir.buf.size\n      dir.fill_buffer(path)\n    end\n\n    dirent = Pointer(LibWasi::DirEnt).new(dir.buf.to_unsafe.address + dir.pos).value\n\n    if dir.pos + sizeof(LibWasi::DirEnt) + dirent.d_namlen > dir.buf.size\n      if dir.pos == 0\n        dir.buf = Bytes.new(dir.buf.size * 2)\n      end\n      dir.fill_buffer(path)\n      return next_entry(dir, path)\n    end\n\n    name = String.new(dir.buf[dir.pos + sizeof(LibWasi::DirEnt), dirent.d_namlen])\n    dir.pos += sizeof(LibWasi::DirEnt) + dirent.d_namlen\n    dir.cookie = dirent.d_next\n\n    is_dir = case dirent.d_type\n             when .directory?                then true\n             when .unknown?, .symbolic_link? then nil\n             else                                 false\n             end\n\n    Entry.new(name, is_dir, false)\n  end\n\n  def self.rewind(dir) : Nil\n    dir.cookie = 0\n    dir.end_pos = dir.pos = dir.buf.size.to_u32\n  end\n\n  def self.info(dir, path) : ::File::Info\n    Crystal::System::FileDescriptor.system_info dir.fd\n  end\n\n  def self.close(dir, path) : Nil\n    err = LibWasi.fd_close(dir.fd)\n    raise ::File::Error.from_os_error(\"Error closing directory\", err, file: path) unless err.success?\n  end\n\n  def self.current : String\n    unless dir = LibC.getcwd(nil, 0)\n      raise ::File::Error.from_errno(\"Error getting current directory\", file: \"./\")\n    end\n\n    dir_str = String.new(dir)\n    LibC.free(dir.as(Void*))\n    dir_str\n  end\n\n  def self.current=(path : String)\n    if LibC.chdir(path.check_no_null_byte) != 0\n      raise ::File::Error.from_errno(\"Error while changing directory\", file: path)\n    end\n\n    path\n  end\n\n  def self.tempdir\n    tmpdir = ENV[\"TMPDIR\"]? || \"/tmp\"\n    tmpdir.rchop(::File::SEPARATOR)\n  end\n\n  def self.create(path : String, mode : Int32) : Nil\n    if LibC.mkdir(path.check_no_null_byte, mode) == -1\n      raise ::File::Error.from_errno(\"Unable to create directory\", file: path)\n    end\n  end\n\n  def self.delete(path : String, raise_on_missing : Bool) : Bool\n    return true if LibC.rmdir(path.check_no_null_byte) == 0\n\n    if !raise_on_missing && Errno.value == Errno::ENOENT\n      false\n    else\n      raise ::File::Error.from_errno(\"Unable to remove directory\", file: path)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/fiber.cr",
    "content": "module Crystal::System::Fiber\n  def self.allocate_stack(stack_size, protect) : Void*\n    LibC.malloc(stack_size)\n  end\n\n  def self.reset_stack(stack : Void*, stack_size : Int, protect : Bool) : Nil\n  end\n\n  def self.free_stack(stack : Void*, stack_size) : Nil\n    LibC.free(stack)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/file.cr",
    "content": "require \"../unix/file\"\n\n# :nodoc:\nmodule Crystal::System::File\n  protected def system_init(mode : String, blocking : Bool) : Nil\n  end\n\n  def self.chmod(path, mode)\n    raise NotImplementedError.new \"Crystal::System::File.chmod\"\n  end\n\n  def self.chown(path, uid : Int, gid : Int, follow_symlinks)\n    raise NotImplementedError.new \"Crystal::System::File.chown\"\n  end\n\n  private def system_chown(uid : Int, gid : Int)\n    raise NotImplementedError.new \"Crystal::System::File#system_chown\"\n  end\n\n  def self.realpath(path)\n    raise NotImplementedError.new \"Crystal::System::File.realpath\"\n  end\n\n  def self.utime(atime : ::Time, mtime : ::Time, filename : String) : Nil\n    raise NotImplementedError.new \"Crystal::System::File.utime\"\n  end\n\n  def self.delete(path : String, *, raise_on_missing : Bool) : Bool\n    raise NotImplementedError.new \"Crystal::System::File.delete\"\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/file_descriptor.cr",
    "content": "require \"../unix/file_descriptor\"\n\n# :nodoc:\nmodule Crystal::System::FileDescriptor\n  def self.from_stdio(fd)\n    # TODO: WASI doesn't offer a way to detect if a 'fd' is a TTY.\n    IO::FileDescriptor.new(fd).tap(&.flush_on_newline=(true))\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    FileDescriptor.fcntl(fd, cmd, arg)\n  end\n\n  private def system_fcntl(cmd, arg = 0)\n    FileDescriptor.fcntl(fd, cmd, arg)\n  end\n\n  def self.get_blocking(fd : Handle)\n    raise NotImplementedError.new(\"Crystal::System::FileDescriptor.get_blocking\")\n  end\n\n  def self.set_blocking(fd : Handle, value : Bool)\n    raise NotImplementedError.new(\"Crystal::System::FileDescriptor.set_blocking\")\n  end\n\n  protected def system_blocking_init(blocking : Bool?)\n  end\n\n  private def system_reopen(other : IO::FileDescriptor)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor#system_reopen\"\n  end\n\n  private def system_flock_shared(blocking)\n    raise NotImplementedError.new \"Crystal::System::File#system_flock_shared\"\n  end\n\n  private def system_flock_exclusive(blocking)\n    raise NotImplementedError.new \"Crystal::System::File#system_flock_exclusive\"\n  end\n\n  private def system_flock_unlock\n    raise NotImplementedError.new \"Crystal::System::File#system_flock_unlock\"\n  end\n\n  private def flock(op : LibC::FlockOp, blocking : Bool = true)\n    raise NotImplementedError.new \"Crystal::System::File#flock\"\n  end\n\n  private def system_echo(enable : Bool)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor#system_echo\"\n  end\n\n  private def system_echo(enable : Bool, & : ->)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor#system_echo\"\n  end\n\n  private def system_raw(enable : Bool)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor#system_raw\"\n  end\n\n  private def system_raw(enable : Bool, & : ->)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor#system_raw\"\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/group.cr",
    "content": "module Crystal::System::Group\n  def system_name\n    raise NotImplementedError.new(\"Crystal::System::Group#system_name\")\n  end\n\n  def system_id\n    raise NotImplementedError.new(\"Crystal::System::Group#system_id\")\n  end\n\n  def self.from_name?(groupname : String)\n    raise NotImplementedError.new(\"Crystal::System::Group.from_name?\")\n  end\n\n  def self.from_id?(groupid : String)\n    raise NotImplementedError.new(\"Crystal::System::Group.from_id?\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/hostname.cr",
    "content": "module Crystal::System\n  def self.hostname\n    raise NotImplementedError.new(\"Crystal::System.hostname\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/lib_wasi.cr",
    "content": "lib LibWasi\n  alias Fd = Int32\n  alias Size = UInt32\n  alias FileSize = UInt64\n\n  struct PrestatDir\n    pr_name_len : Size\n  end\n\n  union PrestatUnion\n    dir : PrestatDir\n  end\n\n  enum PrestatTag : UInt8\n    Dir\n  end\n\n  struct Prestat\n    tag : PrestatTag\n    value : PrestatUnion\n  end\n\n  @[Flags]\n  enum LookupFlags : UInt32\n    SymlinkFollow\n  end\n\n  @[Flags]\n  enum OpenFlags : UInt16\n    Creat\n    Directory\n    Excl\n    Trunc\n  end\n\n  @[Flags]\n  enum FdFlags : UInt16\n    Append   # Append mode: Data written to the file is always appended to the file's end.\n    Dsync    # Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized.\n    NonBlock # Non-blocking mode.\n    RSync    # Synchronized read I/O operations.\n    Sync     # Write according to synchronized I/O file integrity completion. In addition to synchronizing the data stored in the file, the implementation may also synchronously update the file's metadata.\n  end\n\n  @[Flags]\n  enum Rights : UInt64\n    FdDatasync           # The right to invoke fd_datasync. If path_open is set, includes the right to invoke path_open with fdflags::dsync.\n    FdRead               # The right to invoke fd_read and sock_recv. If rights::fd_seek is set, includes the right to invoke fd_pread.\n    FdSeek               # The right to invoke fd_seek. This flag implies rights::fd_tell.\n    FdFdstatSetFlags     # The right to invoke fd_fdstat_set_flags.\n    FdSync               # The right to invoke fd_sync. If path_open is set, includes the right to invoke path_open with fdflags::rsync and fdflags::dsync.\n    FdTell               # The right to invoke fd_seek in such a way that the file offset remains unaltered (i.e., whence::cur with offset zero), or to invoke fd_tell.\n    FdWrite              # The right to invoke fd_write and sock_send. If rights::fd_seek is set, includes the right to invoke fd_pwrite.\n    FdAdvise             # The right to invoke fd_advise.\n    FdAllocate           # The right to invoke fd_allocate.\n    PathCreateDirectory  # The right to invoke path_create_directory.\n    PathCreateFile       # If path_open is set, the right to invoke path_open with oflags::creat.\n    PathLinkSource       # The right to invoke path_link with the file descriptor as the source directory.\n    PathLinkTarget       # The right to invoke path_link with the file descriptor as the target directory.\n    PathOpen             # The right to invoke path_open.\n    FdReaddir            # The right to invoke fd_readdir.\n    PathReadlink         # The right to invoke path_readlink.\n    PathRenameSource     # The right to invoke path_rename with the file descriptor as the source directory.\n    PathRenameTarget     # The right to invoke path_rename with the file descriptor as the target directory.\n    PathFilestatGet      # The right to invoke path_filestat_get.\n    PathFilestatSetSize  # The right to change a file's size (there is no path_filestat_set_size). If path_open is set, includes the right to invoke path_open with oflags::trunc.\n    PathFilestatSetTimes # The right to invoke path_filestat_set_times.\n    FdFilestatGet        # The right to invoke fd_filestat_get.\n    FdFilestatSetSize    # The right to invoke fd_filestat_set_size.\n    FdFilestatSetTimes   # The right to invoke fd_filestat_set_times.\n    PathSymlink          # The right to invoke path_symlink.\n    PathRemoveDirectory  # The right to invoke path_remove_directory.\n    PathUnlinkFile       # The right to invoke path_unlink_file.\n    PollFdReadwrite      # If rights::fd_read is set, includes the right to invoke poll_oneoff to subscribe to eventtype::fd_read. If rights::fd_write is set, includes the right to invoke poll_oneoff to subscribe to eventtype::fd_write.\n    SockShutdown         # The right to invoke sock_shutdown.\n  end\n\n  enum FileType : UInt8\n    Unknown         # The type of the file descriptor or file is unknown or is different from any of the other types specified.\n    BlockDevice     # The file descriptor or file refers to a block device inode.\n    CharacterDevice # The file descriptor or file refers to a character device inode.\n    Directory       # The file descriptor or file refers to a directory inode.\n    RegularFile     # The file descriptor or file refers to a regular file inode.\n    SocketDgram     # The file descriptor or file refers to a datagram socket.\n    SocketStream    # The file descriptor or file refers to a byte-stream socket.\n    SymbolicLink    # The file refers to a symbolic link inode.\n  end\n\n  struct DirEnt\n    d_next : UInt64\n    d_ino : UInt64\n    d_namlen : UInt32\n    d_type : FileType\n  end\n\n  fun fd_prestat_get = __wasi_fd_prestat_get(fd : Fd, stat : Prestat*) : WasiError\n  fun fd_prestat_dir_name = __wasi_fd_prestat_dir_name(fd : Fd, path : UInt8*, len : Size) : WasiError\n  fun path_open = __wasi_path_open(fd : Fd, dirflags : LookupFlags, path : UInt8*, oflags : OpenFlags, fs_rights_base : Rights, fs_rights_inheriting : Rights, fdflags : FdFlags, ret : Fd*) : WasiError\n  fun fd_readdir = __wasi_fd_readdir(fd : Fd, buf : UInt8*, len : Size, cookie : UInt64, ret : Size*) : WasiError\n  fun fd_close = __wasi_fd_close(fd : Fd) : WasiError\n  fun random_get = __wasi_random_get(buf : UInt8*, len : Size) : WasiError\n  fun proc_exit = __wasi_proc_exit(code : Int32) : NoReturn\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/main.cr",
    "content": "require \"./lib_wasi\"\n\n# This file serve as the entrypoint for WebAssembly applications compliant to the WASI spec.\n# See https://github.com/WebAssembly/WASI/blob/snapshot-01/design/application-abi.md.\n\nlib LibC\n  # `__wasm_call_ctors` and `__wasm_call_dtors` are generated by the WebAssembly linker and they execute\n  # functions marked with `attribute(constructor)` or `attribute(destructor)`, commonly used for global\n  # initialization before/after main. LibC has constructor functions, for example.\n  fun __wasm_call_ctors\n  fun __wasm_call_dtors\n\n  # Provided by wasi-libc to obtain argc/argv and call into `__main_argc_argv`.\n  fun __main_void : Int32\nend\n\n# As part of the WASI Application ABI, a \"command\" program must export a `_start` function that takes no\n# arguments and returns nothing. This function will be called from the environment once and returning from\n# it signals the command finished successfully.\n# TODO: \"reactor\" programs must export a `_initialize` function that is called once by the environment at\n# load time. After that any other exported function can be called any number of times. These programs remain\n# alive and ready until they are unloaded from memory.\nfun _start\n  LibC.__wasm_call_ctors\n  status = LibC.__main_void\n  LibC.__wasm_call_dtors\n  LibWasi.proc_exit(status) if status != 0\nend\n\n# `__main_argc_argv` is called by wasi-libc's `__main_void` with the program\n# arguments.\nfun __main_argc_argv(argc : Int32, argv : UInt8**) : Int32\n  main(argc, argv)\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/path.cr",
    "content": "module Crystal::System::Path\n  def self.home : String\n    ENV[\"HOME\"]\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/process.cr",
    "content": "require \"c/stdlib\"\nrequire \"c/unistd\"\n\nstruct Crystal::System::Process\n  getter pid : LibC::PidT\n\n  def initialize(@pid : LibC::PidT)\n  end\n\n  def release\n    raise NotImplementedError.new(\"Process#release\")\n  end\n\n  def wait\n    raise NotImplementedError.new(\"Process#wait\")\n  end\n\n  def exists?\n    raise NotImplementedError.new(\"Process#exists?\")\n  end\n\n  def terminate(*, graceful)\n    raise NotImplementedError.new(\"Process#terminate\")\n  end\n\n  def self.exit(status)\n    LibC.exit(status)\n  end\n\n  def self.pid\n    # TODO: WebAssembly doesn't have the concept of processes.\n    1\n  end\n\n  def self.pgid\n    raise NotImplementedError.new(\"Process.pgid\")\n  end\n\n  def self.pgid(pid)\n    raise NotImplementedError.new(\"Process.pgid\")\n  end\n\n  def self.ppid\n    raise NotImplementedError.new(\"Process.ppid\")\n  end\n\n  def self.signal(pid, signal)\n    raise NotImplementedError.new(\"Process.signal\")\n  end\n\n  @[Deprecated(\"Use `#on_terminate` instead\")]\n  def self.on_interrupt(&handler : ->) : Nil\n    raise NotImplementedError.new(\"Process.on_interrupt\")\n  end\n\n  def self.on_terminate(&handler : ::Process::ExitReason ->) : Nil\n    raise NotImplementedError.new(\"Process.on_terminate\")\n  end\n\n  def self.ignore_interrupts! : Nil\n    raise NotImplementedError.new(\"Process.ignore_interrupts!\")\n  end\n\n  def self.restore_interrupts! : Nil\n    raise NotImplementedError.new(\"Process.restore_interrupts!\")\n  end\n\n  def self.start_interrupt_loop : Nil\n  end\n\n  def self.debugger_present? : Bool\n    false\n  end\n\n  def self.exists?(pid)\n    raise NotImplementedError.new(\"Process.exists?\")\n  end\n\n  def self.times\n    raise NotImplementedError.new(\"Process.times\")\n  end\n\n  def self.fork\n    raise NotImplementedError.new(\"Process.fork\")\n  end\n\n  def self.fork(&)\n    raise NotImplementedError.new(\"Process.fork\")\n  end\n\n  def self.spawn(command, args, shell, env, clear_env, input, output, error, chdir)\n    raise NotImplementedError.new(\"Process.spawn\")\n  end\n\n  def self.replace(command, args, shell, env, clear_env, input, output, error, chdir)\n    raise NotImplementedError.new(\"Process.replace\")\n  end\n\n  def self.chroot(path)\n    raise NotImplementedError.new(\"Process.chroot\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/random.cr",
    "content": "require \"./lib_wasi\"\n\nmodule Crystal::System::Random\n  def self.random_bytes(buf : Bytes) : Nil\n    err = LibWasi.random_get(buf, buf.size)\n    raise RuntimeError.from_os_error(\"random_get\", err) unless err.success?\n  end\n\n  def self.next_u : UInt8\n    buf = uninitialized UInt8\n    random_bytes(pointerof(buf).to_slice(1))\n    buf\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/signal.cr",
    "content": "module Crystal::System::Signal\n  def self.trap(signal, handler) : Nil\n    raise NotImplementedError.new(\"Crystal::System::Signal.trap\")\n  end\n\n  def self.trap_handler?(signal)\n    raise NotImplementedError.new(\"Crystal::System::Signal.trap_handler?\")\n  end\n\n  def self.reset(signal) : Nil\n    raise NotImplementedError.new(\"Crystal::System::Signal.reset\")\n  end\n\n  def self.ignore(signal) : Nil\n    raise NotImplementedError.new(\"Crystal::System::Signal.ignore\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/socket.cr",
    "content": "require \"c/netdb\"\nrequire \"c/netinet/tcp\"\nrequire \"c/sys/socket\"\nrequire \"io/evented\"\n\nmodule Crystal::System::Socket\n  include IO::Evented\n\n  alias Handle = Int32\n\n  private def initialize_handle(fd, blocking = nil)\n  end\n\n  private def system_bind(addr, addrstr)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_bind\"\n  end\n\n  private def system_listen(backlog)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_listen\"\n  end\n\n  private def system_accept : {Handle, Bool}?\n    raise NotImplementedError.new \"Crystal::System::Socket#system_accept\"\n  end\n\n  private def system_close_read\n    if LibC.shutdown(fd, LibC::SHUT_RD) != 0\n      raise ::Socket::Error.from_errno(\"shutdown read\")\n    end\n  end\n\n  private def system_close_write\n    if LibC.shutdown(fd, LibC::SHUT_WR) != 0\n      raise ::Socket::Error.from_errno(\"shutdown write\")\n    end\n  end\n\n  private def system_accept : {::Socket::Handle, Bool}?\n    event_loop.accept(self)\n  end\n\n  private def system_send_buffer_size : Int\n    raise NotImplementedError.new \"Crystal::System::Socket#system_send_buffer_size\"\n  end\n\n  private def system_send_buffer_size=(val : Int)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_send_buffer_size=\"\n  end\n\n  private def system_recv_buffer_size : Int\n    raise NotImplementedError.new \"Crystal::System::Socket#system_recv_buffer_size\"\n  end\n\n  private def system_recv_buffer_size=(val : Int)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_recv_buffer_size=\"\n  end\n\n  private def system_reuse_address? : Bool\n    raise NotImplementedError.new \"Crystal::System::Socket#system_reuse_address?\"\n  end\n\n  private def system_reuse_address=(val : Bool)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_reuse_address=\"\n  end\n\n  private def system_reuse_port?\n    raise NotImplementedError.new \"Crystal::System::Socket#system_reuse_port?\"\n  end\n\n  private def system_reuse_port=(val : Bool)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_reuse_port=\"\n  end\n\n  private def system_broadcast? : Bool\n    raise NotImplementedError.new \"Crystal::System::Socket#system_broadcast?\"\n  end\n\n  private def system_broadcast=(val : Bool)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_broadcast=\"\n  end\n\n  private def system_keepalive? : Bool\n    raise NotImplementedError.new \"Crystal::System::Socket#system_keepalive?\"\n  end\n\n  private def system_keepalive=(val : Bool)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_keepalive=\"\n  end\n\n  private def system_linger\n    raise NotImplementedError.new \"Crystal::System::Socket#system_linger\"\n  end\n\n  private def system_linger=(val)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_linge=\"\n  end\n\n  private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET, &)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_getsockopt\"\n  end\n\n  private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_getsockopt\"\n  end\n\n  private def system_setsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_setsockopt\"\n  end\n\n  private def system_blocking?\n    Socket.get_blocking(fd)\n  end\n\n  private def system_blocking=(value)\n    Socket.set_blocking(fd, value)\n  end\n\n  def self.get_blocking(fd : Handle)\n    fcntl(fd, LibC::F_GETFL) & LibC::O_NONBLOCK == 0\n  end\n\n  def self.set_blocking(fd : Handle, value : Bool)\n    flags = fcntl(fd, LibC::F_GETFL)\n    if value\n      flags &= ~LibC::O_NONBLOCK\n    else\n      flags |= LibC::O_NONBLOCK\n    end\n    fcntl(fd, LibC::F_SETFL, flags)\n  end\n\n  private def system_close_on_exec?\n    flags = fcntl(LibC::F_GETFD)\n    (flags & LibC::FD_CLOEXEC) == LibC::FD_CLOEXEC\n  end\n\n  private def system_close_on_exec=(arg : Bool)\n    fcntl(LibC::F_SETFD, arg ? LibC::FD_CLOEXEC : 0)\n    arg\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    r = LibC.fcntl fd, cmd, arg\n    raise ::Socket::Error.from_errno(\"fcntl() failed\") if r == -1\n    r\n  end\n\n  private def system_fcntl(cmd, arg = 0)\n    FileDescriptor.system_fcntl(fd, cmd, arg)\n  end\n\n  private def system_tty?\n    LibC.isatty(fd) == 1\n  end\n\n  def socket_close\n    # Clear the @volatile_fd before actually closing it in order to\n    # reduce the chance of reading an outdated fd value\n    return unless fd = close_volatile_fd?\n\n    ret = LibC.close(fd)\n\n    if ret != 0\n      case Errno.value\n      when Errno::EINTR, Errno::EINPROGRESS\n        # ignore\n      else\n        raise ::Socket::Error.from_errno(\"Error closing socket\")\n      end\n    end\n  end\n\n  def close_volatile_fd? : Int32?\n    fd = @volatile_fd.swap(-1)\n    fd unless fd == -1\n  end\n\n  private def system_local_address\n    raise NotImplementedError.new \"Crystal::System::Socket#system_local_address\"\n  end\n\n  private def system_remote_address\n    raise NotImplementedError.new \"Crystal::System::Socket#system_remote_address\"\n  end\n\n  private def system_tcp_keepalive_idle\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_tcp_keepalive_idle\")\n  end\n\n  private def system_tcp_keepalive_idle=(val : Int)\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_tcp_keepalive_idle=\")\n  end\n\n  private def system_tcp_keepalive_interval\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_tcp_keepalive_interval\")\n  end\n\n  private def system_tcp_keepalive_interval=(val : Int)\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_tcp_keepalive_interval=\")\n  end\n\n  private def system_tcp_keepalive_count\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_tcp_keepalive_count\")\n  end\n\n  private def system_tcp_keepalive_count=(val : Int)\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_tcp_keepalive_count=\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/thread.cr",
    "content": "module Crystal::System::Thread\n  alias Handle = Nil\n\n  def self.init : Nil\n  end\n\n  def self.new_handle(thread_obj : ::Thread) : Handle\n    raise NotImplementedError.new(\"Crystal::System::Thread.new_handle\")\n  end\n\n  def self.current_handle : Handle\n    nil\n  end\n\n  def self.yield_current : Nil\n    raise NotImplementedError.new(\"Crystal::System::Thread.yield_current\")\n  end\n\n  def self.current_thread : ::Thread\n    @@current_thread ||= ::Thread.new\n  end\n\n  def self.current_thread? : ::Thread?\n    @@current_thread\n  end\n\n  def self.current_thread=(@@current_thread : ::Thread)\n  end\n\n  def self.sleep(time : ::Time::Span) : Nil\n    req = uninitialized LibC::Timespec\n    req.tv_sec = typeof(req.tv_sec).new(time.@seconds)\n    req.tv_nsec = typeof(req.tv_nsec).new(time.@nanoseconds)\n\n    loop do\n      return if LibC.nanosleep(pointerof(req), out rem) == 0\n      raise RuntimeError.from_errno(\"nanosleep() failed\") unless Errno.value == Errno::EINTR\n      req = rem\n    end\n  end\n\n  private def system_join : Exception?\n    NotImplementedError.new(\"Crystal::System::Thread#system_join\")\n  end\n\n  private def system_close\n  end\n\n  private def stack_address : Void*\n    # TODO: Implement\n    Pointer(Void).null\n  end\n\n  def self.init_suspend_resume : Nil\n  end\n\n  private def system_suspend : Nil\n    raise NotImplementedError.new(\"Crystal::System::Thread.system_suspend\")\n  end\n\n  private def system_wait_suspended : Nil\n    raise NotImplementedError.new(\"Crystal::System::Thread.system_wait_suspended\")\n  end\n\n  private def system_resume : Nil\n    raise NotImplementedError.new(\"Crystal::System::Thread.system_resume\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/thread_condition_variable.cr",
    "content": "# TODO: Implement\nclass Thread\n  class ConditionVariable\n    def signal : Nil\n    end\n\n    def broadcast : Nil\n    end\n\n    def wait(mutex : Thread::Mutex) : Nil\n    end\n\n    def wait(mutex : Thread::Mutex, time : Time::Span, &)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/thread_mutex.cr",
    "content": "# TODO: Implement\nclass Thread\n  class Mutex\n    def lock\n    end\n\n    def try_lock\n    end\n\n    def unlock\n    end\n\n    def synchronize(&)\n      yield\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/user.cr",
    "content": "module Crystal::System::User\n  def system_username\n    raise NotImplementedError.new(\"Crystal::System::User#system_username\")\n  end\n\n  def system_id\n    raise NotImplementedError.new(\"Crystal::System::User#system_id\")\n  end\n\n  def system_group_id\n    raise NotImplementedError.new(\"Crystal::System::User#system_group_id\")\n  end\n\n  def system_name\n    raise NotImplementedError.new(\"Crystal::System::User#system_name\")\n  end\n\n  def system_home_directory\n    raise NotImplementedError.new(\"Crystal::System::User#system_home_directory\")\n  end\n\n  def system_shell\n    raise NotImplementedError.new(\"Crystal::System::User#system_shell\")\n  end\n\n  def self.from_username?(username : String)\n    raise NotImplementedError.new(\"Crystal::System::User.from_username?\")\n  end\n\n  def self.from_id?(id : String)\n    raise NotImplementedError.new(\"Crystal::System::User.from_id?\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/wasi/wasi.cr",
    "content": "require \"./lib_wasi\"\n\nmodule Crystal::System::Wasi\n  PREOPENS = begin\n    preopens = [] of {String, String, LibWasi::Fd}\n\n    # Skip stdin, stdout, and stderr, and count up until we reach an invalid file descriptor.\n    (3..).each do |fd|\n      stat = uninitialized LibWasi::Prestat\n      err = LibWasi.fd_prestat_get(fd, pointerof(stat))\n\n      break unless err.success?\n\n      next unless stat.tag.dir?\n\n      len = stat.value.dir.pr_name_len\n\n      name = String.new(len + 1) do |buffer|\n        err = LibWasi.fd_prestat_dir_name(fd, buffer, len)\n        raise RuntimeError.from_os_error(\"fd_prestat_dir_name\", err) unless err.success?\n        buffer[len] = 0\n        len = LibC.strlen(buffer)\n        {len, 0}\n      end\n\n      path = ::Path[name].expand.to_s\n      preopens << {path, path.ensure_suffix(\"/\"), fd}\n    end\n\n    # Preopens added later take priority over preopens added earlier.\n    preopens.reverse!\n    # Preopens of longer prefix take priority over shorter prefixes.\n    preopens.sort_by! { |entry| -entry[0].size }\n\n    preopens\n  end\n\n  def self.find_path_preopen(path)\n    path = ::Path[path].expand.to_s\n    PREOPENS.each do |preopen|\n      case path\n      when preopen[0]\n        return {preopen[2], \".\"}\n      when .starts_with? preopen[1]\n        return {preopen[2], path[preopen[1].size..-1]}\n      end\n    end\n\n    # If we can't find a preopen for it, indicate that we lack capabilities.\n    raise RuntimeError.from_os_error(nil, WasiError::NOTCAPABLE)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/addrinfo.cr",
    "content": "module Crystal::System::Addrinfo\n  alias Handle = LibC::ADDRINFOEXW*\n\n  @addr : LibC::SockaddrIn6\n\n  protected def initialize(addrinfo : Handle)\n    @family = ::Socket::Family.from_value(addrinfo.value.ai_family)\n    @type = ::Socket::Type.from_value(addrinfo.value.ai_socktype)\n    @protocol = ::Socket::Protocol.from_value(addrinfo.value.ai_protocol)\n    @size = addrinfo.value.ai_addrlen.to_i\n\n    @addr = uninitialized LibC::SockaddrIn6\n\n    case @family\n    when ::Socket::Family::INET6\n      addrinfo.value.ai_addr.as(LibC::SockaddrIn6*).copy_to(pointerof(@addr).as(LibC::SockaddrIn6*), 1)\n    when ::Socket::Family::INET\n      addrinfo.value.ai_addr.as(LibC::SockaddrIn*).copy_to(pointerof(@addr).as(LibC::SockaddrIn*), 1)\n    else\n      # TODO: (asterite) UNSPEC and UNIX unsupported?\n    end\n  end\n\n  def system_ip_address : ::Socket::IPAddress\n    ::Socket::IPAddress.from(to_unsafe, size)\n  end\n\n  def to_unsafe\n    pointerof(@addr).as(LibC::Sockaddr*)\n  end\n\n  def self.getaddrinfo(domain, service, family, type, protocol, timeout, flags = 0) : Handle\n    hints = LibC::ADDRINFOEXW.new\n    hints.ai_family = (family || ::Socket::Family::UNSPEC).to_i32\n    hints.ai_socktype = type\n    hints.ai_protocol = protocol\n\n    if service.is_a?(Int)\n      flags |= LibC::AI_NUMERICSERV\n      if service < 0\n        raise ::Socket::Addrinfo::Error.from_os_error(nil, WinError::WSATYPE_NOT_FOUND, domain: domain, type: type, protocol: protocol, service: service)\n      end\n    end\n\n    hints.ai_flags = flags\n    IOCP::GetAddrInfoOverlappedOperation.run(Crystal::EventLoop.current.iocp_handle) do |operation|\n      completion_routine = LibC::LPLOOKUPSERVICE_COMPLETION_ROUTINE.new do |dwError, dwBytes, lpOverlapped|\n        orig_operation = IOCP::GetAddrInfoOverlappedOperation.unbox(lpOverlapped)\n        LibC.PostQueuedCompletionStatus(orig_operation.iocp, 0, 0, lpOverlapped)\n      end\n\n      # NOTE: we handle the timeout ourselves so we don't pass a `LibC::Timeval`\n      # to Win32 here\n      result = LibC.GetAddrInfoExW(\n        Crystal::System.to_wstr(domain), Crystal::System.to_wstr(service.to_s), LibC::NS_DNS, nil, pointerof(hints),\n        out addrinfos, nil, operation, completion_routine, out cancel_handle)\n\n      if result == 0\n        return addrinfos\n      else\n        case error = WinError.new(result.to_u32!)\n        when .wsa_io_pending?\n          # used in `IOCP::OverlappedOperation#try_cancel_getaddrinfo`\n          operation.cancel_handle = cancel_handle\n        else\n          raise ::Socket::Addrinfo::Error.from_os_error(\"GetAddrInfoExW\", error, domain: domain, type: type, protocol: protocol, service: service)\n        end\n      end\n\n      operation.wait_for_result(timeout) do |error|\n        case error\n        when .wsa_e_cancelled?\n          raise IO::TimeoutError.new(\"GetAddrInfoExW timed out\")\n        else\n          raise ::Socket::Addrinfo::Error.from_os_error(\"GetAddrInfoExW\", error, domain: domain, type: type, protocol: protocol, service: service)\n        end\n      end\n    end\n  end\n\n  def self.next_addrinfo(addrinfo : Handle) : Handle\n    addrinfo.value.ai_next\n  end\n\n  def self.free_addrinfo(addrinfo : Handle)\n    LibC.FreeAddrInfoExW(addrinfo)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/addrinfo_win7.cr",
    "content": "module Crystal::System::Addrinfo\n  alias Handle = LibC::Addrinfo*\n\n  @addr : LibC::SockaddrIn6\n\n  protected def initialize(addrinfo : Handle)\n    @family = ::Socket::Family.from_value(addrinfo.value.ai_family)\n    @type = ::Socket::Type.from_value(addrinfo.value.ai_socktype)\n    @protocol = ::Socket::Protocol.from_value(addrinfo.value.ai_protocol)\n    @size = addrinfo.value.ai_addrlen.to_i\n\n    @addr = uninitialized LibC::SockaddrIn6\n\n    case @family\n    when ::Socket::Family::INET6\n      addrinfo.value.ai_addr.as(LibC::SockaddrIn6*).copy_to(pointerof(@addr).as(LibC::SockaddrIn6*), 1)\n    when ::Socket::Family::INET\n      addrinfo.value.ai_addr.as(LibC::SockaddrIn*).copy_to(pointerof(@addr).as(LibC::SockaddrIn*), 1)\n    else\n      # TODO: (asterite) UNSPEC and UNIX unsupported?\n    end\n  end\n\n  def system_ip_address : ::Socket::IPAddress\n    ::Socket::IPAddress.from(to_unsafe, size)\n  end\n\n  def to_unsafe\n    pointerof(@addr).as(LibC::Sockaddr*)\n  end\n\n  def self.getaddrinfo(domain, service, family, type, protocol, timeout, flags = 0) : Handle\n    hints = LibC::Addrinfo.new\n    hints.ai_family = (family || ::Socket::Family::UNSPEC).to_i32\n    hints.ai_socktype = type\n    hints.ai_protocol = protocol\n\n    if service.is_a?(Int)\n      flags |= LibC::AI_NUMERICSERV\n      if service < 0\n        raise ::Socket::Addrinfo::Error.from_os_error(nil, WinError::WSATYPE_NOT_FOUND, domain: domain, type: type, protocol: protocol, service: service)\n      end\n    end\n\n    hints.ai_flags = flags\n\n    ptr = Pointer(LibC::Addrinfo).null\n    ret = ::Fiber.syscall do\n      LibC.getaddrinfo(domain, service.to_s, pointerof(hints), pointerof(ptr))\n    end\n    unless ret.zero?\n      error = WinError.new(ret.to_u32!)\n      raise ::Socket::Addrinfo::Error.from_os_error(nil, error, domain: domain, type: type, protocol: protocol, service: service)\n    end\n    ptr\n  end\n\n  def self.next_addrinfo(addrinfo : Handle) : Handle\n    addrinfo.value.ai_next\n  end\n\n  def self.free_addrinfo(addrinfo : Handle)\n    LibC.freeaddrinfo(addrinfo)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/cpucount.cr",
    "content": "require \"c/sysinfoapi\"\n\nmodule Crystal::System\n  def self.cpu_count\n    LibC.GetNativeSystemInfo(out system_info)\n    system_info.dwNumberOfProcessors\n  end\n\n  def self.effective_cpu_count\n    if LibC.GetProcessAffinityMask(LibC.GetCurrentProcess, out process_affinity, out _) == 0\n      -1\n    else\n      process_affinity.popcount\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/crypto.cr",
    "content": "require \"c/wincrypt\"\nrequire \"openssl\"\n\nmodule Crystal::System::Crypto\n  private ServerAuthOID = \"1.3.6.1.5.5.7.3.1\"\n\n  # heavily based on cURL's code for importing system certificates on Windows:\n  # https://github.com/curl/curl/blob/2f17a9b654121dd1ecf4fc043c6d08a9da3522db/lib/vtls/openssl.c#L3015-L3157\n  private def self.each_system_certificate(store_name : String, &)\n    now = ::Time.utc\n\n    return unless cert_store = LibC.CertOpenSystemStoreW(nil, System.to_wstr(store_name))\n\n    eku = Pointer(LibC::CERT_USAGE).null\n    cert_context = Pointer(LibC::CERT_CONTEXT).null\n    while cert_context = LibC.CertEnumCertificatesInStore(cert_store, cert_context)\n      next unless cert_context.value.dwCertEncodingType == LibC::X509_ASN_ENCODING\n\n      next if cert_context.value.pbCertEncoded.nil?\n\n      not_before = Crystal::System::Time.from_filetime(cert_context.value.pCertInfo.value.notBefore)\n      not_after = Crystal::System::Time.from_filetime(cert_context.value.pCertInfo.value.notAfter)\n      next unless not_before <= now <= not_after\n\n      # look for the serverAuth OID if extended key usage exists\n      if LibC.CertGetEnhancedKeyUsage(cert_context, 0, nil, out eku_size) != 0\n        eku = eku.as(UInt8*).realloc(eku_size).as(LibC::CERT_USAGE*)\n        next unless LibC.CertGetEnhancedKeyUsage(cert_context, 0, eku, pointerof(eku_size)) != 0\n        next unless (0...eku.value.cUsageIdentifier).any? do |i|\n                      LibC.strcmp(eku.value.rgpszUsageIdentifier[i], ServerAuthOID) == 0\n                    end\n      end\n\n      encoded = Slice.new(cert_context.value.pbCertEncoded, cert_context.value.cbCertEncoded)\n      until encoded.empty?\n        cert, encoded = OpenSSL::X509::Certificate.from_der?(encoded)\n        break unless cert\n        yield cert\n      end\n    end\n  ensure\n    LibC.CertCloseStore(cert_store, 0) if cert_store\n  end\n\n  private class_getter system_root_certificates : Array(OpenSSL::X509::Certificate) do\n    certs = [] of OpenSSL::X509::Certificate\n    each_system_certificate(\"ROOT\") { |cert| certs << cert }\n    certs\n  end\n\n  def self.populate_system_root_certificates(ssl_context)\n    cert_store = LibSSL.ssl_ctx_get_cert_store(ssl_context)\n    system_root_certificates.each do |cert|\n      LibCrypto.x509_store_add_cert(cert_store, cert)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/dir.cr",
    "content": "require \"crystal/system/windows\"\nrequire \"c/winbase\"\nrequire \"c/direct\"\nrequire \"c/handleapi\"\nrequire \"c/processenv\"\n\nmodule Crystal::System::Dir\n  private class DirHandle\n    property iter_handle : LibC::HANDLE\n    property file_handle : LibC::HANDLE = LibC::INVALID_HANDLE_VALUE\n    getter query : LibC::LPWSTR\n\n    def initialize(@iter_handle, @query)\n    end\n  end\n\n  def self.open(path : String) : DirHandle\n    unless ::Dir.exists? path\n      raise ::File::Error.from_os_error(\"Error opening directory\", Errno::ENOENT, file: path)\n    end\n\n    DirHandle.new(LibC::INVALID_HANDLE_VALUE, System.to_wstr(path + \"\\\\*\"))\n  end\n\n  def self.next_entry(dir : DirHandle, path : String) : Entry?\n    if dir.iter_handle == LibC::INVALID_HANDLE_VALUE\n      # Directory is at start, use FindFirstFile\n      handle = LibC.FindFirstFileW(dir.query, out data)\n      if handle != LibC::INVALID_HANDLE_VALUE\n        dir.iter_handle = handle\n        data_to_entry(data)\n      else\n        error = WinError.value\n        if error == WinError::ERROR_FILE_NOT_FOUND\n          nil\n        else\n          raise ::File::Error.from_os_error(\"Error reading directory entries\", error, file: path)\n        end\n      end\n    else\n      # Use FindNextFile\n      if LibC.FindNextFileW(dir.iter_handle, out data_) != 0\n        data_to_entry(data_)\n      else\n        error = WinError.value\n        if error == WinError::ERROR_NO_MORE_FILES\n          nil\n        else\n          raise ::File::Error.from_os_error(\"Error reading directory entries\", error, file: path)\n        end\n      end\n    end\n  end\n\n  def self.data_to_entry(data)\n    name = String.from_utf16(data.cFileName.to_unsafe)[0]\n    unless data.dwFileAttributes.bits_set?(LibC::FILE_ATTRIBUTE_REPARSE_POINT) && data.dwReserved0 == LibC::IO_REPARSE_TAG_SYMLINK\n      dir = data.dwFileAttributes.bits_set?(LibC::FILE_ATTRIBUTE_DIRECTORY)\n    end\n    native_hidden = data.dwFileAttributes.bits_set?(LibC::FILE_ATTRIBUTE_HIDDEN)\n    os_hidden = native_hidden && data.dwFileAttributes.bits_set?(LibC::FILE_ATTRIBUTE_SYSTEM)\n    Entry.new(name, dir, native_hidden, os_hidden)\n  end\n\n  def self.rewind(dir : DirHandle) : Nil\n    close(dir)\n  end\n\n  def self.info(dir : DirHandle, path) : ::File::Info\n    if dir.file_handle == LibC::INVALID_HANDLE_VALUE\n      handle = LibC.CreateFileW(\n        System.to_wstr(path),\n        LibC::FILE_READ_ATTRIBUTES,\n        LibC::FILE_SHARE_READ | LibC::FILE_SHARE_WRITE | LibC::FILE_SHARE_DELETE,\n        nil,\n        LibC::OPEN_EXISTING,\n        LibC::FILE_FLAG_BACKUP_SEMANTICS,\n        LibC::HANDLE.null,\n      )\n\n      if handle == LibC::INVALID_HANDLE_VALUE\n        raise ::File::Error.from_winerror(\"Unable to get directory info\", file: path)\n      end\n\n      dir.file_handle = handle\n    end\n\n    Crystal::System::FileDescriptor.system_info dir.file_handle, LibC::FILE_TYPE_DISK\n  end\n\n  def self.close(dir : DirHandle, path : String) : Nil\n    close_iter(dir.iter_handle, path)\n    close_file(dir.file_handle, path)\n    dir.iter_handle = dir.file_handle = LibC::INVALID_HANDLE_VALUE\n  end\n\n  private def self.close_iter(handle : LibC::HANDLE, path : String) : Nil\n    return if handle == LibC::INVALID_HANDLE_VALUE\n\n    if LibC.FindClose(handle) == 0\n      raise ::File::Error.from_winerror(\"Error closing directory\", file: path)\n    end\n  end\n\n  private def self.close_file(handle : LibC::HANDLE, path : String) : Nil\n    return if handle == LibC::INVALID_HANDLE_VALUE\n\n    if LibC.CloseHandle(handle) == 0\n      raise ::File::Error.from_winerror(\"CloseHandle\", file: path)\n    end\n  end\n\n  def self.current : String\n    System.retry_wstr_buffer do |buffer, small_buf|\n      len = LibC.GetCurrentDirectoryW(buffer.size, buffer)\n      if 0 < len < buffer.size\n        return String.from_utf16(buffer[0, len])\n      elsif small_buf && len > 0\n        next len\n      else\n        raise ::File::Error.from_winerror(\"Error getting current directory\", file: \"./\")\n      end\n    end\n  end\n\n  def self.current=(path : String) : String\n    if LibC.SetCurrentDirectoryW(System.to_wstr(path)) == 0\n      raise ::File::Error.from_winerror(\"Error while changing directory\", file: path)\n    end\n\n    path\n  end\n\n  def self.tempdir : String\n    tmpdir = System.retry_wstr_buffer do |buffer, small_buf|\n      len = LibC.GetTempPathW(buffer.size, buffer)\n      if 0 < len < buffer.size\n        break String.from_utf16(buffer[0, len])\n      elsif small_buf && len > 0\n        next len\n      else\n        raise RuntimeError.from_winerror(\"Error getting temporary directory\")\n      end\n    end\n\n    tmpdir.rchop(\"\\\\\")\n  end\n\n  def self.create(path : String, mode : Int32) : Nil\n    if LibC.CreateDirectoryW(System.to_wstr(path), nil) == 0\n      raise ::File::Error.from_winerror(\"Unable to create directory\", file: path)\n    end\n  end\n\n  def self.delete(path : String, *, raise_on_missing : Bool) : Bool\n    win_path = System.to_wstr(path)\n\n    attributes = LibC.GetFileAttributesW(win_path)\n    if attributes == LibC::INVALID_FILE_ATTRIBUTES\n      File.check_not_found_error(\"Unable to remove directory\", path)\n      raise ::File::Error.from_os_error(\"Unable to remove directory\", Errno::ENOENT, file: path) if raise_on_missing\n      return false\n    end\n\n    # all reparse point directories should be deleted like a directory, not just\n    # symbolic links, so we don't care about the reparse tag here\n    if attributes.bits_set?(LibC::FILE_ATTRIBUTE_REPARSE_POINT) && attributes.bits_set?(LibC::FILE_ATTRIBUTE_DIRECTORY)\n      # maintain consistency with POSIX, and treat all reparse points (including\n      # symbolic links) as non-directories\n      raise ::File::Error.new(\"Cannot remove directory that is a reparse point: '#{path.inspect_unquoted}'\", file: path)\n    end\n\n    # Windows cannot delete read-only files, so we unset the attribute here, but\n    # restore it afterwards if deletion still failed\n    read_only_removed = false\n    if attributes.bits_set?(LibC::FILE_ATTRIBUTE_READONLY)\n      if LibC.SetFileAttributesW(win_path, attributes & ~LibC::FILE_ATTRIBUTE_READONLY) != 0\n        read_only_removed = true\n      end\n    end\n\n    return true if LibC.RemoveDirectoryW(win_path) != 0\n    LibC.SetFileAttributesW(win_path, attributes) if read_only_removed\n    raise ::File::Error.from_winerror(\"Unable to remove directory\", file: path)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/env.cr",
    "content": "require \"crystal/system/windows\"\nrequire \"c/winbase\"\nrequire \"c/processenv\"\n\nmodule Crystal::System::Env\n  # Sets an environment variable or unsets it if *value* is `nil`.\n  def self.set(key : String, value : String) : Nil\n    check_valid_key(key)\n    key = System.to_wstr(key, \"key\")\n    value = System.to_wstr(value, \"value\")\n\n    if LibC.SetEnvironmentVariableW(key, value) == 0\n      raise RuntimeError.from_winerror(\"SetEnvironmentVariableW\")\n    end\n  end\n\n  # Unsets an environment variable.\n  def self.set(key : String, value : Nil) : Nil\n    check_valid_key(key)\n    key = System.to_wstr(key, \"key\")\n\n    if LibC.SetEnvironmentVariableW(key, nil) == 0\n      raise RuntimeError.from_winerror(\"SetEnvironmentVariableW\")\n    end\n  end\n\n  # Gets an environment variable.\n  def self.get(key : String) : String?\n    return nil unless valid_key?(key)\n    key = System.to_wstr(key, \"key\")\n\n    System.retry_wstr_buffer do |buffer, small_buf|\n      # `GetEnvironmentVariableW` doesn't set last error on success but we need\n      # a success message in order to identify if length == 0 means not found or\n      # the value is an empty string.\n      LibC.SetLastError(WinError::ERROR_SUCCESS)\n      length = LibC.GetEnvironmentVariableW(key, buffer, buffer.size)\n\n      if 0 < length < buffer.size\n        return String.from_utf16(buffer[0, length])\n      elsif small_buf && length > 0\n        next length\n      else\n        case last_error = WinError.value\n        when WinError::ERROR_SUCCESS\n          return \"\"\n        when WinError::ERROR_ENVVAR_NOT_FOUND\n          return\n        else\n          raise RuntimeError.from_os_error(\"GetEnvironmentVariableW\", last_error)\n        end\n      end\n    end\n  end\n\n  # Returns `true` if environment variable is set.\n  def self.has_key?(key : String) : Bool\n    return false unless valid_key?(key)\n    key = System.to_wstr(key, \"key\")\n\n    buffer = uninitialized UInt16[1]\n    LibC.GetEnvironmentVariableW(key, buffer, buffer.size) != 0\n  end\n\n  # Iterates all environment variables.\n  def self.each(&block : String, String ->)\n    orig_pointer = pointer = LibC.GetEnvironmentStringsW\n    raise RuntimeError.from_winerror(\"GetEnvironmentStringsW\") if pointer.null?\n\n    begin\n      while !pointer.value.zero?\n        string, pointer = String.from_utf16(pointer)\n        # Skip internal environment variables that are reserved by `cmd.exe`\n        # (`%=ExitCode%`, `%=ExitCodeAscii%`, `%=::%`, `%=C:%` ...)\n        next if string.starts_with?('=')\n        key, _, value = string.partition('=')\n        yield key, value\n      end\n    ensure\n      LibC.FreeEnvironmentStringsW(orig_pointer)\n    end\n  end\n\n  # Used internally to create an input for `CreateProcessW` `lpEnvironment`.\n  def self.make_env_block(env, clear_env)\n    # If neither clearing nor adding anything, use the default behavior of\n    # inheriting everything.\n    return Pointer(UInt16).null if !env && !clear_env\n\n    # NOTE: the entire string contains embedded null bytes so we can't use\n    # `System.to_wstr` here\n    String.build do |io|\n      unless clear_env\n        each do |key, value|\n          # skip override\n          next if env.try(&.any? { |k, _| k.compare(key, case_insensitive: true) == 0 })\n\n          io << key.check_no_null_byte(\"key\")\n          io << '='\n          io << value.check_no_null_byte(\"value\")\n          io << '\\0'\n        end\n      end\n\n      env.try(&.each do |key, value|\n        check_valid_key(key)\n\n        # skip deletion\n        next if value.nil?\n\n        io << key.check_no_null_byte(\"key\")\n        io << '='\n        io << value.check_no_null_byte(\"value\")\n        io << '\\0'\n      end)\n\n      # terminate the block\n      io << '\\0'\n    end.to_utf16.to_unsafe\n  end\n\n  private def self.valid_key?(key : String)\n    !(key.empty? || key.includes?('='))\n  end\n\n  private def self.check_valid_key(key : String)\n    raise ArgumentError.new(\"Invalid env key #{key.inspect}\") unless valid_key?(key)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/fiber.cr",
    "content": "require \"c/memoryapi\"\nrequire \"c/sysinfoapi\"\nrequire \"c/winnt\"\n\nmodule Crystal::System::Fiber\n  # stack size in bytes needed for last-minute error handling in case of a stack\n  # overflow\n  # FIXME: use `LibC::DWORD` explicitly (#15820)\n  RESERVED_STACK_SIZE = 0x10000_u32\n\n  def self.allocate_stack(stack_size, protect) : Void*\n    if stack_top = LibC.VirtualAlloc(nil, stack_size, LibC::MEM_RESERVE, LibC::PAGE_READWRITE)\n      if protect\n        if commit_and_guard(stack_top, stack_size)\n          return stack_top\n        end\n      else\n        # for the interpreter, the stack is just ordinary memory so the entire\n        # range is committed\n        if LibC.VirtualAlloc(stack_top, stack_size, LibC::MEM_COMMIT, LibC::PAGE_READWRITE)\n          return stack_top\n        end\n      end\n\n      # failure\n      LibC.VirtualFree(stack_top, 0, LibC::MEM_RELEASE)\n    end\n\n    raise RuntimeError.from_winerror(\"VirtualAlloc\")\n  end\n\n  def self.reset_stack(stack : Void*, stack_size : Int, protect : Bool) : Nil\n    if protect\n      if LibC.VirtualFree(stack, 0, LibC::MEM_DECOMMIT) == 0\n        raise RuntimeError.from_winerror(\"VirtualFree\")\n      end\n      unless commit_and_guard(stack, stack_size)\n        raise RuntimeError.from_winerror(\"VirtualAlloc\")\n      end\n    end\n  end\n\n  # Commits the bottommost page and sets up the guard pages above it, in the\n  # same manner as each thread's main stack. When the stack hits a guard page\n  # for the first time, a page fault is generated, the page's guard status is\n  # reset, and Windows checks if a reserved page is available above. On success,\n  # a new guard page is committed, and on failure, a stack overflow exception is\n  # triggered after the `RESERVED_STACK_SIZE` portion is made available.\n  private def self.commit_and_guard(stack_top, stack_size)\n    stack_bottom = stack_top + stack_size\n\n    LibC.GetNativeSystemInfo(out system_info)\n    stack_commit_size = system_info.dwPageSize\n    stack_commit_top = stack_bottom - stack_commit_size\n    unless LibC.VirtualAlloc(stack_commit_top, stack_commit_size, LibC::MEM_COMMIT, LibC::PAGE_READWRITE)\n      return false\n    end\n\n    # the reserved stack size, plus a final guard page for when the stack\n    # overflow handler itself overflows the stack\n    stack_guard_size = system_info.dwPageSize + RESERVED_STACK_SIZE\n    stack_guard_top = stack_commit_top - stack_guard_size\n    unless LibC.VirtualAlloc(stack_guard_top, stack_guard_size, LibC::MEM_COMMIT, LibC::PAGE_READWRITE | LibC::PAGE_GUARD)\n      return false\n    end\n\n    true\n  end\n\n  def self.free_stack(stack : Void*, stack_size) : Nil\n    if LibC.VirtualFree(stack, 0, LibC::MEM_RELEASE) == 0\n      raise RuntimeError.from_winerror(\"VirtualFree\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/file.cr",
    "content": "require \"c/io\"\nrequire \"c/fcntl\"\nrequire \"c/fileapi\"\nrequire \"c/sys/utime\"\nrequire \"c/sys/stat\"\nrequire \"c/winbase\"\nrequire \"c/handleapi\"\nrequire \"c/ntifs\"\nrequire \"c/winioctl\"\n\nmodule Crystal::System::File\n  # On Windows we cannot rely on the system mode `FILE_APPEND_DATA` and\n  # keep track of append mode explicitly. When writing data, this ensures to only\n  # write at the end of the file.\n  getter? system_append = false\n\n  def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions, blocking : Bool?) : {FileDescriptor::Handle, Bool}\n    perm = ::File::Permissions.new(perm) if perm.is_a? Int32\n    # Only the owner writable bit is used, since windows only supports\n    # the read only attribute.\n    if perm.owner_write?\n      perm = LibC::S_IREAD | LibC::S_IWRITE\n    else\n      perm = LibC::S_IREAD\n    end\n\n    case result = EventLoop.current.open(filename, open_flag(mode), ::File::Permissions.new(perm), blocking != false)\n    in Tuple(FileDescriptor::Handle, Bool)\n      result\n    in WinError\n      raise ::File::Error.from_os_error(\"Error opening file with mode '#{mode}'\", result, file: filename)\n    end\n  end\n\n  def self.posix_to_open_opts(flags : Int32, perm : ::File::Permissions, blocking : Bool)\n    access = if flags.bits_set? LibC::O_WRONLY\n               LibC::FILE_GENERIC_WRITE\n             elsif flags.bits_set? LibC::O_RDWR\n               LibC::FILE_GENERIC_READ | LibC::FILE_GENERIC_WRITE\n             else\n               LibC::FILE_GENERIC_READ\n             end\n\n    # do not handle `O_APPEND`, because Win32 append mode relies on removing\n    # `FILE_WRITE_DATA` which breaks file truncation and locking; instead,\n    # simply set the end of the file as the write offset in `#write_blocking`\n\n    if flags.bits_set? LibC::O_TRUNC\n      if flags.bits_set? LibC::O_CREAT\n        disposition = LibC::CREATE_ALWAYS\n      else\n        disposition = LibC::TRUNCATE_EXISTING\n      end\n    elsif flags.bits_set? LibC::O_CREAT\n      if flags.bits_set? LibC::O_EXCL\n        disposition = LibC::CREATE_NEW\n      else\n        disposition = LibC::OPEN_ALWAYS\n      end\n    else\n      disposition = LibC::OPEN_EXISTING\n    end\n\n    attributes = 0\n    unless perm.owner_write?\n      attributes |= LibC::FILE_ATTRIBUTE_READONLY\n    end\n\n    if flags.bits_set? LibC::O_TEMPORARY\n      attributes |= LibC::FILE_FLAG_DELETE_ON_CLOSE | LibC::FILE_ATTRIBUTE_TEMPORARY\n      access |= LibC::DELETE\n    end\n\n    if flags.bits_set? LibC::O_SHORT_LIVED\n      attributes |= LibC::FILE_ATTRIBUTE_TEMPORARY\n    end\n\n    if flags.bits_set? LibC::O_SEQUENTIAL\n      attributes |= LibC::FILE_FLAG_SEQUENTIAL_SCAN\n    elsif flags.bits_set? LibC::O_RANDOM\n      attributes |= LibC::FILE_FLAG_RANDOM_ACCESS\n    end\n\n    unless blocking\n      attributes |= LibC::FILE_FLAG_OVERLAPPED\n    end\n\n    {access, disposition, attributes}\n  end\n\n  protected def system_init(mode : String, blocking : Bool) : Nil\n    @system_append = true if mode.starts_with?('a')\n    @system_blocking = blocking\n  end\n\n  private def write_blocking(handle, slice)\n    write_blocking(handle, slice, pos: @system_append ? UInt64::MAX : nil)\n  end\n\n  def self.check_not_found_error(message, path)\n    error = WinError.value\n    if ::File::NotFoundError.os_error?(error)\n      nil\n    else\n      raise ::File::Error.from_os_error(message, error, file: path)\n    end\n  end\n\n  def self.info?(path : String, follow_symlinks : Bool) : ::File::Info?\n    winpath = System.to_wstr(path)\n\n    # First try using GetFileAttributes to check if it's a reparse point\n    file_attributes = uninitialized LibC::WIN32_FILE_ATTRIBUTE_DATA\n    ret = LibC.GetFileAttributesExW(\n      winpath,\n      LibC::GET_FILEEX_INFO_LEVELS::GetFileExInfoStandard,\n      pointerof(file_attributes)\n    )\n    if ret != 0\n      if file_attributes.dwFileAttributes.bits_set? LibC::FILE_ATTRIBUTE_REPARSE_POINT\n        # Could be a symlink, retrieve its reparse tag with FindFirstFile\n        handle = LibC.FindFirstFileW(winpath, out find_data)\n        return check_not_found_error(\"Unable to get file info\", path) if handle == LibC::INVALID_HANDLE_VALUE\n\n        if LibC.FindClose(handle) == 0\n          raise RuntimeError.from_winerror(\"FindClose\")\n        end\n\n        case find_data.dwReserved0\n        when LibC::IO_REPARSE_TAG_SYMLINK\n          return ::File::Info.new(find_data) unless follow_symlinks\n        when LibC::IO_REPARSE_TAG_AF_UNIX\n          return ::File::Info.new(find_data)\n        end\n      end\n    end\n\n    handle = LibC.CreateFileW(\n      System.to_wstr(path),\n      LibC::FILE_READ_ATTRIBUTES,\n      LibC::FILE_SHARE_READ | LibC::FILE_SHARE_WRITE | LibC::FILE_SHARE_DELETE,\n      nil,\n      LibC::OPEN_EXISTING,\n      LibC::FILE_FLAG_BACKUP_SEMANTICS,\n      LibC::HANDLE.null\n    )\n\n    return check_not_found_error(\"Unable to get file info\", path) if handle == LibC::INVALID_HANDLE_VALUE\n\n    begin\n      FileDescriptor.system_info(handle)\n    ensure\n      LibC.CloseHandle(handle)\n    end\n  end\n\n  def self.info(path, follow_symlinks)\n    info?(path, follow_symlinks) || raise ::File::Error.from_winerror(\"Unable to get file info\", file: path)\n  end\n\n  def self.exists?(path, *, follow_symlinks = true)\n    accessible?(path, check_writable: false, follow_symlinks: follow_symlinks)\n  end\n\n  def self.readable?(path) : Bool\n    accessible?(path, check_writable: false, follow_symlinks: true)\n  end\n\n  def self.writable?(path) : Bool\n    accessible?(path, check_writable: true, follow_symlinks: true)\n  end\n\n  def self.executable?(path) : Bool\n    # NOTE: this always follows symlinks:\n    # https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getbinarytypew#remarks\n    LibC.GetBinaryTypeW(System.to_wstr(path), out result) != 0\n  end\n\n  private def self.accessible?(path, *, check_writable, follow_symlinks)\n    if follow_symlinks\n      path = realpath?(path, check_exists: false) || return false\n    end\n\n    handle = LibC.CreateFileW(\n      System.to_wstr(path),\n      LibC::FILE_READ_ATTRIBUTES,\n      LibC::DEFAULT_SHARE_MODE,\n      nil,\n      LibC::OPEN_EXISTING,\n      LibC::FILE_FLAG_BACKUP_SEMANTICS | LibC::FILE_FLAG_OPEN_REPARSE_POINT,\n      LibC::HANDLE.null,\n    )\n    return false if handle == LibC::INVALID_HANDLE_VALUE\n\n    begin\n      info = uninitialized LibC::FILE_ATTRIBUTE_TAG_INFO\n      if LibC.GetFileInformationByHandleEx(handle, LibC::FILE_INFO_BY_HANDLE_CLASS::FileAttributeTagInfo, pointerof(info), sizeof(typeof(info))) == 0\n        # this can happen to special files like NUL and COM1, but we managed to\n        # open them anyway, so assume they are readable and writable\n        return true\n      end\n      attributes = info.fileAttributes\n    ensure\n      LibC.CloseHandle(handle)\n    end\n\n    return false if attributes == LibC::INVALID_FILE_ATTRIBUTES\n    return true if attributes.bits_set?(LibC::FILE_ATTRIBUTE_DIRECTORY)\n    return false if check_writable && attributes.bits_set?(LibC::FILE_ATTRIBUTE_READONLY)\n\n    true\n  end\n\n  def self.chown(path : String, uid : Int32, gid : Int32, follow_symlinks : Bool) : Nil\n    raise NotImplementedError.new(\"File.chown\")\n  end\n\n  private def system_chown(uid : Int, gid : Int) : Nil\n    raise NotImplementedError.new(\"File#chown\")\n  end\n\n  def self.chmod(path : String, mode : Int32 | ::File::Permissions) : Nil\n    mode = ::File::Permissions.new(mode) unless mode.is_a? ::File::Permissions\n\n    unless exists?(path, follow_symlinks: false)\n      raise ::File::Error.from_os_error(\"Error changing permissions\", Errno::ENOENT, file: path)\n    end\n\n    path = realpath(path)\n\n    attributes = LibC.GetFileAttributesW(System.to_wstr(path))\n    if attributes == LibC::INVALID_FILE_ATTRIBUTES\n      raise ::File::Error.from_winerror(\"Error changing permissions\", file: path)\n    end\n\n    # Only the owner writable bit is used, since windows only supports\n    # the read only attribute.\n    if mode.owner_write?\n      attributes &= ~LibC::FILE_ATTRIBUTE_READONLY\n    else\n      attributes |= LibC::FILE_ATTRIBUTE_READONLY\n    end\n\n    if LibC.SetFileAttributesW(System.to_wstr(path), attributes) == 0\n      raise ::File::Error.from_winerror(\"Error changing permissions\", file: path)\n    end\n  end\n\n  private def system_chmod(mode : Int32 | ::File::Permissions) : Nil\n    mode = ::File::Permissions.new(mode) unless mode.is_a? ::File::Permissions\n    handle = windows_handle\n\n    basic_info = uninitialized LibC::FILE_BASIC_INFO\n    if LibC.GetFileInformationByHandleEx(handle, LibC::FILE_INFO_BY_HANDLE_CLASS::FileBasicInfo, pointerof(basic_info), sizeof(typeof(basic_info))) == 0\n      raise ::File::Error.from_winerror(\"Error changing permissions\", file: path)\n    end\n\n    # Only the owner writable bit is used, since windows only supports\n    # the read only attribute.\n    if mode.owner_write?\n      basic_info.fileAttributes &= ~LibC::FILE_ATTRIBUTE_READONLY\n    else\n      basic_info.fileAttributes |= LibC::FILE_ATTRIBUTE_READONLY\n    end\n\n    if LibC.SetFileInformationByHandle(handle, LibC::FILE_INFO_BY_HANDLE_CLASS::FileBasicInfo, pointerof(basic_info), sizeof(typeof(basic_info))) == 0\n      raise ::File::Error.from_winerror(\"Error changing permissions\", file: path)\n    end\n  end\n\n  def self.delete(path : String, *, raise_on_missing : Bool) : Bool\n    win_path = System.to_wstr(path)\n\n    attributes = LibC.GetFileAttributesW(win_path)\n    if attributes == LibC::INVALID_FILE_ATTRIBUTES\n      check_not_found_error(\"Error deleting file\", path)\n      raise ::File::Error.from_os_error(\"Error deleting file\", Errno::ENOENT, file: path) if raise_on_missing\n      return false\n    end\n\n    # Windows cannot delete read-only files, so we unset the attribute here, but\n    # restore it afterwards if deletion still failed\n    read_only_removed = false\n    if attributes.bits_set?(LibC::FILE_ATTRIBUTE_READONLY)\n      if LibC.SetFileAttributesW(win_path, attributes & ~LibC::FILE_ATTRIBUTE_READONLY) != 0\n        read_only_removed = true\n      end\n    end\n\n    # all reparse point directories should be deleted like a directory, not just\n    # symbolic links, so we don't care about the reparse tag here\n    is_reparse_dir = attributes.bits_set?(LibC::FILE_ATTRIBUTE_REPARSE_POINT) && attributes.bits_set?(LibC::FILE_ATTRIBUTE_DIRECTORY)\n    result = is_reparse_dir ? LibC.RemoveDirectoryW(win_path) : LibC.DeleteFileW(win_path)\n    return true if result != 0\n    LibC.SetFileAttributesW(win_path, attributes) if read_only_removed\n    raise ::File::Error.from_winerror(\"Error deleting file\", file: path)\n  end\n\n  private REALPATH_SYMLINK_LIMIT = 100\n\n  private def self.realpath?(path : String, *, check_exists : Bool = true) : String?\n    REALPATH_SYMLINK_LIMIT.times do\n      win_path = System.to_wstr(path)\n\n      realpath = System.retry_wstr_buffer do |buffer, small_buf|\n        len = LibC.GetFullPathNameW(win_path, buffer.size, buffer, nil)\n        if 0 < len < buffer.size\n          break String.from_utf16(buffer[0, len])\n        elsif small_buf && len > 0\n          next len\n        else\n          raise ::File::Error.from_winerror(\"Error resolving real path\", file: path)\n        end\n      end\n\n      if symlink_info = symlink_info?(realpath)\n        new_path, is_relative = symlink_info\n        path = is_relative ? ::File.expand_path(new_path, ::File.dirname(realpath)) : new_path\n        next\n      end\n\n      return !check_exists || exists?(realpath, follow_symlinks: false) ? realpath : nil\n    end\n\n    raise ::File::Error.from_os_error(\"Too many symbolic links\", Errno::ELOOP, file: path)\n  end\n\n  def self.realpath(path : String) : String\n    realpath?(path) || raise ::File::Error.from_os_error(\"Error resolving real path\", Errno::ENOENT, file: path)\n  end\n\n  def self.link(old_path : String, new_path : String) : Nil\n    if LibC.CreateHardLinkW(System.to_wstr(new_path), System.to_wstr(old_path), nil) == 0\n      raise ::File::Error.from_winerror(\"Error creating link\", file: old_path, other: new_path)\n    end\n  end\n\n  def self.symlink(old_path : String, new_path : String) : Nil\n    win_old_path = System.to_wstr(old_path.gsub('/', '\\\\'))\n    win_new_path = System.to_wstr(new_path)\n    info = info?(old_path, true)\n    flags = info.try(&.type.directory?) ? LibC::SYMBOLIC_LINK_FLAG_DIRECTORY : 0\n\n    # Symlink on Windows required the SeCreateSymbolicLink privilege. But in the Windows 10\n    # Creators Update (1703), Microsoft added the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE\n    # flag, that allows creation symlink without SeCreateSymbolicLink privilege if the computer\n    # is in Developer Mode.\n    result = LibC.CreateSymbolicLinkW(win_new_path, win_old_path, flags | LibC::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)\n\n    # If we get an error like ERROR_INVALID_PARAMETER, it means that we have an\n    # older Windows. Retry without SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE\n    # flag.\n    if result == 0 && WinError.value == WinError::ERROR_INVALID_PARAMETER\n      result = LibC.CreateSymbolicLinkW(win_new_path, win_old_path, flags)\n    end\n\n    if result == 0\n      raise ::File::Error.from_winerror(\"Error creating symlink\", file: old_path, other: new_path)\n    end\n  end\n\n  private def self.symlink_info?(path)\n    handle = LibC.CreateFileW(\n      System.to_wstr(path),\n      LibC::FILE_READ_ATTRIBUTES,\n      LibC::DEFAULT_SHARE_MODE,\n      nil,\n      LibC::OPEN_EXISTING,\n      LibC::FILE_FLAG_BACKUP_SEMANTICS | LibC::FILE_FLAG_OPEN_REPARSE_POINT,\n      LibC::HANDLE.null\n    )\n\n    return nil if handle == LibC::INVALID_HANDLE_VALUE\n\n    begin\n      size = 0x40\n      buf = Pointer(UInt8).malloc(size)\n\n      while true\n        if LibC.DeviceIoControl(handle, LibC::FSCTL_GET_REPARSE_POINT, nil, 0, buf, size, out _, nil) != 0\n          reparse_data = buf.as(LibC::REPARSE_DATA_BUFFER*)\n          if reparse_data.value.reparseTag == LibC::IO_REPARSE_TAG_SYMLINK\n            symlink_data = reparse_data.value.dummyUnionName.symbolicLinkReparseBuffer\n            path_buffer = reparse_data.value.dummyUnionName.symbolicLinkReparseBuffer.pathBuffer.to_unsafe.as(UInt8*)\n            is_relative = symlink_data.flags.bits_set?(LibC::SYMLINK_FLAG_RELATIVE)\n\n            # the print name is not necessarily set; fall back to substitute\n            # name if unavailable\n            if (name_len = symlink_data.printNameLength) > 0\n              name_ptr = path_buffer + symlink_data.printNameOffset\n              name = String.from_utf16(Slice.new(name_ptr, name_len).unsafe_slice_of(UInt16))\n              return {name, is_relative}\n            end\n\n            name_len = symlink_data.substituteNameLength\n            name_ptr = path_buffer + symlink_data.substituteNameOffset\n            name = String.from_utf16(Slice.new(name_ptr, name_len).unsafe_slice_of(UInt16))\n            # remove the internal prefix for NT paths which shows up when  e.g.\n            # creating a symbolic link with an absolute source\n            # TODO: support the other possible paths, for example see\n            # https://github.com/golang/go/blob/ab28b834c4a38bd2295ee43eca4f9e38c28d54a2/src/os/file_windows.go#L362\n            if name.starts_with?(%q(\\??\\)) && name[5]? == ':'\n              name = name[4..]\n            end\n            return {name, is_relative}\n          else\n            # not a symlink (e.g. IO_REPARSE_TAG_AF_UNIX)\n            return nil\n          end\n        end\n\n        return nil if WinError.value != WinError::ERROR_MORE_DATA || size == LibC::MAXIMUM_REPARSE_DATA_BUFFER_SIZE\n        size *= 2\n        buf = buf.realloc(size)\n      end\n    ensure\n      LibC.CloseHandle(handle)\n    end\n  end\n\n  def self.readlink(path, &) : String\n    info = symlink_info?(path)\n    unless info\n      if ::File::NotFoundError.os_error?(WinError.value) || WinError.value == WinError::ERROR_NOT_A_REPARSE_POINT\n        yield\n      end\n\n      raise ::File::Error.from_winerror(\"Cannot read link\", file: path)\n    end\n    path, _is_relative = info\n    path\n  end\n\n  def self.rename(old_path : String, new_path : String) : ::File::Error?\n    if LibC.MoveFileExW(System.to_wstr(old_path), System.to_wstr(new_path), LibC::MOVEFILE_REPLACE_EXISTING) == 0\n      ::File::Error.from_winerror(\"Error renaming file\", file: old_path, other: new_path)\n    end\n  end\n\n  def self.utime(access_time : ::Time, modification_time : ::Time, path : String) : Nil\n    handle = LibC.CreateFileW(\n      System.to_wstr(path),\n      LibC::FILE_WRITE_ATTRIBUTES,\n      LibC::DEFAULT_SHARE_MODE,\n      nil,\n      LibC::OPEN_EXISTING,\n      LibC::FILE_FLAG_BACKUP_SEMANTICS,\n      LibC::HANDLE.null\n    )\n    if handle == LibC::INVALID_HANDLE_VALUE\n      raise ::File::Error.from_winerror(\"Error setting time on file\", file: path)\n    end\n\n    begin\n      utime(handle, access_time, modification_time, path)\n    ensure\n      LibC.CloseHandle(handle)\n    end\n  end\n\n  def self.utime(handle : LibC::HANDLE, access_time : ::Time, modification_time : ::Time, path : String) : Nil\n    atime = Crystal::System::Time.to_filetime(access_time)\n    mtime = Crystal::System::Time.to_filetime(modification_time)\n    if LibC.SetFileTime(handle, nil, pointerof(atime), pointerof(mtime)) == 0\n      raise ::File::Error.from_winerror(\"Error setting time on file\", file: path)\n    end\n  end\n\n  private def system_utime(access_time : ::Time, modification_time : ::Time) : Nil\n    Crystal::System::File.utime(windows_handle, access_time, modification_time, path)\n  end\n\n  private def system_truncate(size : Int) : Nil\n    handle = windows_handle\n    if LibC.SetFilePointerEx(handle, size.to_i64, out old_pos, IO::Seek::Set) == 0\n      raise ::File::Error.from_winerror(\"Error truncating file\", file: path)\n    end\n    begin\n      if LibC.SetEndOfFile(handle) == 0\n        raise ::File::Error.from_winerror(\"Error truncating file\", file: path)\n      end\n    ensure\n      LibC.SetFilePointerEx(handle, old_pos, nil, IO::Seek::Set)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/file_descriptor.cr",
    "content": "require \"c/io\"\nrequire \"c/consoleapi\"\nrequire \"c/consoleapi2\"\nrequire \"c/ntifs\"\nrequire \"c/winnls\"\nrequire \"crystal/system/win32/iocp\"\nrequire \"crystal/system/thread\"\n\nmodule Crystal::System::FileDescriptor\n  # Platform-specific type to represent a file descriptor handle to the operating\n  # system.\n  # NOTE: this should really be `LibC::HANDLE`, here it is an integer type of\n  # the same size so that `IO::FileDescriptor#fd` continues to return an `Int`\n  alias Handle = ::LibC::UIntPtrT\n\n  STDIN_HANDLE  = LibC.GetStdHandle(LibC::STD_INPUT_HANDLE).address\n  STDOUT_HANDLE = LibC.GetStdHandle(LibC::STD_OUTPUT_HANDLE).address\n  STDERR_HANDLE = LibC.GetStdHandle(LibC::STD_ERROR_HANDLE).address\n\n  @system_blocking = true\n\n  def system_append?\n    false\n  end\n\n  private def system_read(slice : Bytes) : Int32\n    handle = windows_handle\n    if ConsoleUtils.console?(handle)\n      ConsoleUtils.read(handle, slice)\n    elsif system_blocking?\n      read_blocking(handle, slice)\n    else\n      event_loop.read(self, slice)\n    end\n  end\n\n  private def read_blocking(handle, slice)\n    ret = LibC.ReadFile(handle, slice, slice.size, out bytes_read, nil)\n    if ret.zero?\n      case error = WinError.value\n      when .error_access_denied?\n        raise IO::Error.new \"File not open for reading\", target: self\n      when .error_broken_pipe?\n        return 0_i32\n      else\n        raise IO::Error.from_os_error(\"Error reading file\", error, target: self)\n      end\n    end\n    bytes_read.to_i32\n  end\n\n  private def system_write(slice : Bytes) : Int32\n    handle = windows_handle\n    if system_blocking?\n      write_blocking(handle, slice).to_i32\n    else\n      event_loop.write(self, slice)\n    end\n  end\n\n  private def write_blocking(handle, slice, pos = nil)\n    overlapped = LibC::OVERLAPPED.new\n    if pos\n      overlapped.union.offset.offset = LibC::DWORD.new!(pos)\n      overlapped.union.offset.offsetHigh = LibC::DWORD.new!(pos >> 32)\n      overlapped_ptr = pointerof(overlapped)\n    else\n      overlapped_ptr = Pointer(LibC::OVERLAPPED).null\n    end\n\n    ret = LibC.WriteFile(handle, slice, slice.size, out bytes_written, overlapped_ptr)\n    if ret.zero?\n      case error = WinError.value\n      when .error_access_denied?\n        raise IO::Error.new \"File not open for writing\", target: self\n      when .error_broken_pipe?\n        return 0_u32\n      else\n        raise IO::Error.from_os_error(\"Error writing file\", error, target: self)\n      end\n    end\n    bytes_written\n  end\n\n  def emulated_blocking? : Bool?\n    # reading from STDIN is done via a separate thread (see\n    # `ConsoleUtils.read_console` below)\n    handle = windows_handle\n    if LibC.GetConsoleMode(handle, out _) != 0\n      if handle == LibC.GetStdHandle(LibC::STD_INPUT_HANDLE)\n        return false\n      end\n    end\n  end\n\n  # :nodoc:\n  def system_blocking?\n    @system_blocking\n  end\n\n  def self.get_blocking(fd : Handle)\n    raise NotImplementedError.new(\"Cannot query the blocking mode of an `IO::FileDescriptor`\")\n  end\n\n  def self.set_blocking(fd : Handle, value : Bool)\n    raise NotImplementedError.new(\"Cannot change the blocking mode of an `IO::FileDescriptor` after creation\")\n  end\n\n  private def system_blocking=(blocking)\n    unless blocking == system_blocking?\n      raise IO::Error.new(\"Cannot reconfigure `IO::FileDescriptor#blocking` after creation\")\n    end\n  end\n\n  protected def system_blocking_init(blocking : Bool?)\n    if blocking.nil?\n      # there are no official API to know whether a handle has been opened with\n      # the OVERLAPPED flag, but the following call is supposed to leak the\n      # information: if neither of the SYNCHRONOUS_IO flags are set then the\n      # OVERLAPPED flag has been set\n      info = LibC::FILE_MODE_INFORMATION.new\n      status = LibC.NtQueryInformationFile(windows_handle, out _, pointerof(info), sizeof(LibC::FILE_MODE_INFORMATION), LibC::FILE_INFORMATION_CLASS::FileModeInformation)\n      blocking = status != LibC::STATUS_SUCCESS || (info.mode & (LibC::FILE_SYNCHRONOUS_IO_ALERT | LibC::FILE_SYNCHRONOUS_IO_NONALERT)) != 0\n    end\n    @system_blocking = blocking\n    Crystal::EventLoop.current.create_completion_port(windows_handle) unless blocking\n  end\n\n  private def system_close_on_exec?\n    false\n  end\n\n  private def system_close_on_exec=(close_on_exec)\n    raise NotImplementedError.new(\"Crystal::System::FileDescriptor#system_close_on_exec=\") if close_on_exec\n    false\n  end\n\n  private def system_closed? : Bool\n    file_type = LibC.GetFileType(windows_handle)\n\n    if file_type == LibC::FILE_TYPE_UNKNOWN\n      case error = WinError.value\n      when .error_invalid_handle?\n        return true\n      else\n        raise IO::Error.from_os_error(\"Unable to get info\", error, target: self)\n      end\n    else\n      false\n    end\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor.fcntl\"\n  end\n\n  private def system_fcntl(cmd, arg = 0)\n    raise NotImplementedError.new \"Crystal::System::FileDescriptor#system_fcntl\"\n  end\n\n  protected def windows_handle\n    LibC::HANDLE.new(fd)\n  end\n\n  def self.system_info(handle, file_type = nil)\n    unless file_type\n      file_type = LibC.GetFileType(handle)\n\n      if file_type == LibC::FILE_TYPE_UNKNOWN\n        error = WinError.value\n        raise IO::Error.from_os_error(\"Unable to get info\", error, target: self) unless error == WinError::ERROR_SUCCESS\n      end\n    end\n\n    if file_type == LibC::FILE_TYPE_DISK\n      if LibC.GetFileInformationByHandle(handle, out file_info) == 0\n        raise IO::Error.from_winerror(\"Unable to get info\")\n      end\n\n      ::File::Info.new(file_info, file_type)\n    else\n      ::File::Info.new(file_type)\n    end\n  end\n\n  private def system_info\n    FileDescriptor.system_info windows_handle\n  end\n\n  def system_seek(offset, whence : IO::Seek) : Nil\n    if LibC.SetFilePointerEx(windows_handle, offset, nil, whence) == 0\n      raise IO::Error.from_winerror(\"Unable to seek\", target: self)\n    end\n  end\n\n  private def system_pos\n    if LibC.SetFilePointerEx(windows_handle, 0, out pos, IO::Seek::Current) == 0\n      raise IO::Error.from_winerror(\"Unable to tell\", target: self)\n    end\n    pos\n  end\n\n  private def system_tty?\n    LibC.GetConsoleMode(windows_handle, out _) != 0\n  end\n\n  private def system_reopen(other : IO::FileDescriptor)\n    cur_proc = LibC.GetCurrentProcess\n    if LibC.DuplicateHandle(cur_proc, other.windows_handle, cur_proc, out new_handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0\n      raise IO::Error.from_winerror(\"Could not reopen file descriptor\")\n    end\n    @volatile_fd.set(new_handle.address)\n\n    # Mark the handle open, since we had to have dup'd a live handle.\n    @closed = false\n  end\n\n  def file_descriptor_close(&)\n    # Clear the @volatile_fd before actually closing it in order to\n    # reduce the chance of reading an outdated handle value\n    handle = LibC::HANDLE.new(@volatile_fd.swap(LibC::INVALID_HANDLE_VALUE.address))\n\n    if LibC.CloseHandle(handle) == 0\n      yield\n    end\n  end\n\n  def file_descriptor_close\n    file_descriptor_close do\n      raise IO::Error.from_winerror(\"Error closing file\", target: self)\n    end\n  end\n\n  private def system_flock_shared(blocking : Bool) : Nil\n    flock(false, blocking)\n  end\n\n  private def system_flock_exclusive(blocking : Bool) : Nil\n    flock(true, blocking)\n  end\n\n  private def system_flock_unlock : Nil\n    unlock_file(windows_handle)\n  end\n\n  private def flock(exclusive, retry)\n    flags = 0_u32\n    flags |= LibC::LOCKFILE_FAIL_IMMEDIATELY if !retry || system_blocking?\n    flags |= LibC::LOCKFILE_EXCLUSIVE_LOCK if exclusive\n\n    handle = windows_handle\n    if retry && system_blocking?\n      until lock_file(handle, flags)\n        sleep 0.1.seconds\n      end\n    else\n      lock_file(handle, flags) || raise IO::Error.from_winerror(\"Error applying file lock: file is already locked\", target: self)\n    end\n  end\n\n  private def lock_file(handle, flags)\n    IOCP::IOOverlappedOperation.run(handle) do |operation|\n      result = LibC.LockFileEx(handle, flags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, operation)\n\n      if result == 0\n        case error = WinError.value\n        when .error_io_pending?\n          # the operation is running asynchronously; do nothing\n        when .error_lock_violation?\n          # synchronous failure\n          return false\n        else\n          raise IO::Error.from_os_error(\"LockFileEx\", error, target: self)\n        end\n      else\n        return true\n      end\n\n      operation.wait_for_result(nil) do |error|\n        raise IO::Error.from_os_error(\"LockFileEx\", error, target: self)\n      end\n\n      true\n    end\n  end\n\n  private def unlock_file(handle)\n    IOCP::IOOverlappedOperation.run(handle) do |operation|\n      result = LibC.UnlockFileEx(handle, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, operation)\n\n      if result == 0\n        error = WinError.value\n        unless error.error_io_pending?\n          raise IO::Error.from_os_error(\"UnlockFileEx\", error, target: self)\n        end\n      else\n        return\n      end\n\n      operation.wait_for_result(nil) do |error|\n        raise IO::Error.from_os_error(\"UnlockFileEx\", error, target: self)\n      end\n    end\n  end\n\n  private def system_fsync(flush_metadata = true) : Nil\n    if LibC.FlushFileBuffers(windows_handle) == 0\n      raise IO::Error.from_winerror(\"Error syncing file\", target: self)\n    end\n  end\n\n  private PIPE_BUFFER_SIZE = 8192\n\n  def self.system_pipe(read_blocking, write_blocking)\n    pipe_name = ::Path.windows(::File.tempname(\"crystal\", nil, dir: %q(\\\\.\\pipe))).normalize.to_s\n    pipe_mode = 0 # PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT\n\n    w_pipe_flags = LibC::PIPE_ACCESS_OUTBOUND | LibC::FILE_FLAG_FIRST_PIPE_INSTANCE\n    w_pipe_flags |= LibC::FILE_FLAG_OVERLAPPED unless write_blocking\n    w_pipe = LibC.CreateNamedPipeA(pipe_name, w_pipe_flags, pipe_mode, 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, nil)\n    raise IO::Error.from_winerror(\"CreateNamedPipeA\") if w_pipe == LibC::INVALID_HANDLE_VALUE\n\n    r_pipe_flags = LibC::FILE_FLAG_NO_BUFFERING\n    r_pipe_flags |= LibC::FILE_FLAG_OVERLAPPED unless read_blocking\n    r_pipe = LibC.CreateFileW(System.to_wstr(pipe_name), LibC::GENERIC_READ | LibC::FILE_WRITE_ATTRIBUTES, 0, nil, LibC::OPEN_EXISTING, r_pipe_flags, nil)\n    raise IO::Error.from_winerror(\"CreateFileW\") if r_pipe == LibC::INVALID_HANDLE_VALUE\n\n    {r_pipe.address, w_pipe.address}\n  end\n\n  def self.pread(file, buffer, offset)\n    handle = file.windows_handle\n\n    if file.system_blocking?\n      overlapped = LibC::OVERLAPPED.new\n      overlapped.union.offset.offset = LibC::DWORD.new!(offset)\n      overlapped.union.offset.offsetHigh = LibC::DWORD.new!(offset >> 32)\n      if LibC.ReadFile(handle, buffer, buffer.size, out bytes_read, pointerof(overlapped)) == 0\n        error = WinError.value\n        return 0_i64 if error == WinError::ERROR_HANDLE_EOF\n        raise IO::Error.from_os_error \"Error reading file\", error, target: file\n      end\n\n      bytes_read.to_i64\n    else\n      IOCP.overlapped_operation(file, \"ReadFile\", file.read_timeout, offset: offset) do |overlapped|\n        ret = LibC.ReadFile(handle, buffer, buffer.size, out byte_count, overlapped)\n        {ret, byte_count}\n      end.to_i64\n    end\n  end\n\n  def self.from_stdio(fd)\n    handle = case fd\n             when 0 then LibC.GetStdHandle(LibC::STD_INPUT_HANDLE)\n             when 1 then LibC.GetStdHandle(LibC::STD_OUTPUT_HANDLE)\n             when 2 then LibC.GetStdHandle(LibC::STD_ERROR_HANDLE)\n             else        LibC::INVALID_HANDLE_VALUE\n             end\n\n    console_handle = false\n    if handle != LibC::INVALID_HANDLE_VALUE\n      # TODO: use `out old_mode` after implementing interpreter out closured var\n      old_mode = uninitialized LibC::DWORD\n      if LibC.GetConsoleMode(handle, pointerof(old_mode)) != 0\n        console_handle = true\n        if fd == 1 || fd == 2 # STDOUT or STDERR\n          if LibC.SetConsoleMode(handle, old_mode | LibC::ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0\n            at_exit { LibC.SetConsoleMode(handle, old_mode) }\n          end\n        end\n      end\n    end\n\n    # `blocking` must be set to `true` because the underlying handles never\n    # support overlapped I/O; instead, `#emulated_blocking?` should return\n    # `false` for `STDIN` as it uses a separate thread\n    io = IO::FileDescriptor.new(handle: handle.address, blocking: true)\n\n    # Set sync or flush_on_newline as described in STDOUT and STDERR docs.\n    # See https://crystal-lang.org/api/toplevel.html#STDERR\n    if console_handle\n      io.sync = true\n    else\n      io.flush_on_newline = true\n    end\n    io\n  end\n\n  private def system_echo(enable : Bool)\n    system_console_mode(enable, LibC::ENABLE_ECHO_INPUT, 0)\n  end\n\n  private def system_echo(enable : Bool, & : ->)\n    system_console_mode(enable, LibC::ENABLE_ECHO_INPUT, 0) { yield }\n  end\n\n  private def system_raw(enable : Bool)\n    system_console_mode(enable, LibC::ENABLE_VIRTUAL_TERMINAL_INPUT, LibC::ENABLE_PROCESSED_INPUT | LibC::ENABLE_LINE_INPUT | LibC::ENABLE_ECHO_INPUT)\n  end\n\n  private def system_raw(enable : Bool, & : ->)\n    system_console_mode(enable, LibC::ENABLE_VIRTUAL_TERMINAL_INPUT, LibC::ENABLE_PROCESSED_INPUT | LibC::ENABLE_LINE_INPUT | LibC::ENABLE_ECHO_INPUT) { yield }\n  end\n\n  @[AlwaysInline]\n  private def system_console_mode(enable, on_mask, off_mask, old_mode = nil)\n    windows_handle = self.windows_handle\n    unless old_mode\n      if LibC.GetConsoleMode(windows_handle, out mode) == 0\n        raise IO::Error.from_winerror(\"GetConsoleMode\")\n      end\n      old_mode = mode\n    end\n\n    old_on_bits = old_mode & on_mask\n    old_off_bits = old_mode & off_mask\n    if enable\n      return if old_on_bits == on_mask && old_off_bits == 0\n      new_mode = (old_mode | on_mask) & ~off_mask\n    else\n      return if old_on_bits == 0 && old_off_bits == off_mask\n      new_mode = (old_mode | off_mask) & ~on_mask\n    end\n\n    if @fd_lock.reference { LibC.SetConsoleMode(windows_handle, new_mode) } == 0\n      raise IO::Error.from_winerror(\"SetConsoleMode\")\n    end\n  end\n\n  @[AlwaysInline]\n  private def system_console_mode(enable, on_mask, off_mask, &)\n    windows_handle = self.windows_handle\n    if LibC.GetConsoleMode(windows_handle, out old_mode) == 0\n      raise IO::Error.from_winerror(\"GetConsoleMode\")\n    end\n\n    begin\n      system_console_mode(enable, on_mask, off_mask, old_mode)\n      yield\n    ensure\n      @fd_lock.reference { LibC.SetConsoleMode(windows_handle, old_mode) }\n    end\n  end\nend\n\nprivate module ConsoleUtils\n  # N UTF-16 code units correspond to no more than 3*N UTF-8 code units.\n  # NOTE: For very large buffers, `ReadConsoleW` may fail.\n  private BUFFER_SIZE = 10000\n  @@utf8_buffer = Slice(UInt8).new(3 * BUFFER_SIZE)\n\n  # `@@buffer` points to part of `@@utf8_buffer`.\n  # It represents data that has not been read yet.\n  @@buffer : Bytes = @@utf8_buffer[0, 0]\n\n  # Remaining UTF-16 code unit.\n  @@remaining_unit : UInt16?\n\n  # Determines if *handle* is a console.\n  def self.console?(handle : LibC::HANDLE) : Bool\n    LibC.GetConsoleMode(handle, out _) != 0\n  end\n\n  # Reads to *slice* from the console specified by *handle*,\n  # and return the actual number of bytes read.\n  def self.read(handle : LibC::HANDLE, slice : Bytes) : Int32\n    return 0 if slice.empty?\n    fill_buffer(handle) if @@buffer.empty?\n\n    bytes_read = {slice.size, @@buffer.size}.min\n    @@buffer[0, bytes_read].copy_to(slice)\n    @@buffer += bytes_read\n    bytes_read\n  end\n\n  private def self.fill_buffer(handle : LibC::HANDLE) : Nil\n    utf16_buffer = uninitialized UInt16[BUFFER_SIZE]\n    remaining_unit = @@remaining_unit\n    if remaining_unit\n      utf16_buffer[0] = remaining_unit\n      index = read_console(handle, utf16_buffer.to_slice + 1)\n    else\n      index = read_console(handle, utf16_buffer.to_slice) - 1\n    end\n\n    if index >= 0 && utf16_buffer[index] & 0xFC00 == 0xD800\n      @@remaining_unit = utf16_buffer[index]\n      index -= 1\n    else\n      @@remaining_unit = nil\n    end\n    return if index < 0\n\n    appender = @@utf8_buffer.to_unsafe.appender\n    String.each_utf16_char(utf16_buffer.to_slice[..index]) do |char|\n      char.each_byte do |byte|\n        appender << byte\n      end\n    end\n    @@buffer = appender.to_slice\n  end\n\n  private def self.read_console(handle : LibC::HANDLE, slice : Slice(UInt16)) : Int32\n    @@mtx.synchronize do\n      @@read_requests << ReadRequest.new(\n        handle: handle,\n        slice: slice,\n        iocp: Crystal::EventLoop.current.iocp_handle,\n        completion_key: Crystal::System::IOCP::CompletionKey.new(:stdin_read, ::Fiber.current),\n      )\n      @@read_cv.signal\n    end\n\n    ::Fiber.suspend\n\n    @@mtx.synchronize do\n      @@bytes_read.shift\n    end\n  end\n\n  private def self.read_console_blocking(handle : LibC::HANDLE, slice : Slice(UInt16)) : Int32\n    if 0 == LibC.ReadConsoleW(handle, slice, slice.size, out units_read, nil)\n      raise IO::Error.from_winerror(\"ReadConsoleW\")\n    end\n    units_read.to_i32\n  end\n\n  record ReadRequest,\n    handle : LibC::HANDLE,\n    slice : Slice(UInt16),\n    iocp : LibC::HANDLE,\n    completion_key : Crystal::System::IOCP::CompletionKey\n\n  @@read_cv = ::Thread::ConditionVariable.new\n  @@read_requests = Deque(ReadRequest).new\n  @@bytes_read = Deque(Int32).new\n  @@mtx = ::Thread::Mutex.new\n\n  # Start a dedicated thread to block on reads from the console. Doesn't need an\n  # isolated execution context because there's no fiber communication (only\n  # thread communication) and only blocking I/O within the thread.\n  @@reader_thread = ::Thread.new { reader_loop }\n\n  private def self.reader_loop\n    while true\n      request = @@mtx.synchronize do\n        loop do\n          if entry = @@read_requests.shift?\n            break entry\n          end\n          @@read_cv.wait(@@mtx)\n        end\n      end\n\n      bytes = read_console_blocking(request.handle, request.slice)\n\n      @@mtx.synchronize do\n        @@bytes_read << bytes\n        LibC.PostQueuedCompletionStatus(request.iocp, LibC::JOB_OBJECT_MSG_EXIT_PROCESS, request.completion_key.object_id, nil)\n      end\n    end\n  end\nend\n\n# Enable UTF-8 console I/O for the duration of program execution\nif LibC.IsValidCodePage(LibC::CP_UTF8) != 0\n  old_input_cp = LibC.GetConsoleCP\n  if LibC.SetConsoleCP(LibC::CP_UTF8) != 0\n    at_exit { LibC.SetConsoleCP(old_input_cp) }\n  end\n\n  old_output_cp = LibC.GetConsoleOutputCP\n  if LibC.SetConsoleOutputCP(LibC::CP_UTF8) != 0\n    at_exit { LibC.SetConsoleOutputCP(old_output_cp) }\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/file_info.cr",
    "content": "module Crystal::System::FileInfo\n  protected getter file_attributes\n\n  def initialize(@file_attributes : LibC::BY_HANDLE_FILE_INFORMATION, @file_type : LibC::DWORD)\n    @reparse_tag = LibC::DWORD.new(0)\n  end\n\n  def initialize(file_attributes : LibC::WIN32_FIND_DATAW)\n    @file_attributes = LibC::BY_HANDLE_FILE_INFORMATION.new(\n      dwFileAttributes: file_attributes.dwFileAttributes,\n      ftCreationTime: file_attributes.ftCreationTime,\n      ftLastAccessTime: file_attributes.ftLastAccessTime,\n      ftLastWriteTime: file_attributes.ftLastWriteTime,\n      dwVolumeSerialNumber: 0,\n      nFileSizeHigh: file_attributes.nFileSizeHigh,\n      nFileSizeLow: file_attributes.nFileSizeLow,\n      nNumberOfLinks: 1,\n      nFileIndexHigh: 0,\n      nFileIndexLow: 0\n    )\n    @file_type = LibC::FILE_TYPE_DISK\n    @reparse_tag = file_attributes.dwReserved0\n  end\n\n  def initialize(@file_type : LibC::DWORD)\n    @file_attributes = LibC::BY_HANDLE_FILE_INFORMATION.new\n    @reparse_tag = LibC::DWORD.new(0)\n  end\n\n  def system_size : Int64\n    ((@file_attributes.nFileSizeHigh.to_u64 << 32) | @file_attributes.nFileSizeLow.to_u64).to_i64\n  end\n\n  def system_permissions : ::File::Permissions\n    if @file_attributes.dwFileAttributes.bits_set? LibC::FILE_ATTRIBUTE_READONLY\n      permissions = ::File::Permissions.new(0o444)\n    else\n      permissions = ::File::Permissions.new(0o666)\n    end\n\n    if @file_attributes.dwFileAttributes.bits_set? LibC::FILE_ATTRIBUTE_DIRECTORY\n      permissions | ::File::Permissions.new(0o111)\n    else\n      permissions\n    end\n  end\n\n  def system_type : ::File::Type\n    case @file_type\n    when LibC::FILE_TYPE_PIPE\n      ::File::Type::Pipe\n    when LibC::FILE_TYPE_CHAR\n      ::File::Type::CharacterDevice\n    when LibC::FILE_TYPE_DISK\n      # See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx\n      if @file_attributes.dwFileAttributes.bits_set?(LibC::FILE_ATTRIBUTE_REPARSE_POINT)\n        case @reparse_tag\n        when LibC::IO_REPARSE_TAG_SYMLINK\n          ::File::Type::Symlink\n        when LibC::IO_REPARSE_TAG_AF_UNIX\n          ::File::Type::Socket\n        else\n          ::File::Type::Unknown\n        end\n      elsif @file_attributes.dwFileAttributes.bits_set? LibC::FILE_ATTRIBUTE_DIRECTORY\n        ::File::Type::Directory\n      else\n        ::File::Type::File\n      end\n    else\n      ::File::Type::Unknown\n    end\n  end\n\n  def system_flags : ::File::Flags\n    ::File::Flags::None\n  end\n\n  def system_modification_time : ::Time\n    Time.from_filetime(@file_attributes.ftLastWriteTime)\n  end\n\n  def system_owner_id : String\n    \"0\"\n  end\n\n  def system_group_id : String\n    \"0\"\n  end\n\n  def system_same_file?(other : self) : Bool\n    return false if type.symlink? || type.pipe? || type.character_device?\n\n    @file_attributes.dwVolumeSerialNumber == other.file_attributes.dwVolumeSerialNumber &&\n      @file_attributes.nFileIndexHigh == other.file_attributes.nFileIndexHigh &&\n      @file_attributes.nFileIndexLow == other.file_attributes.nFileIndexLow\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/group.cr",
    "content": "require \"crystal/system/windows\"\n\n# This file contains source code derived from the following:\n#\n# * https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/os/user/lookup_windows.go\n# * https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/syscall/security_windows.go\n#\n# The following is their license:\n#\n# Copyright 2009 The Go Authors.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are\n# met:\n#\n#    * Redistributions of source code must retain the above copyright\n# notice, this list of conditions and the following disclaimer.\n#    * Redistributions in binary form must reproduce the above\n# copyright notice, this list of conditions and the following disclaimer\n# in the documentation and/or other materials provided with the\n# distribution.\n#    * Neither the name of Google LLC nor the names of its\n# contributors may be used to endorse or promote products derived from\n# this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nmodule Crystal::System::Group\n  def initialize(@name : String, @id : String)\n  end\n\n  def system_name : String\n    @name\n  end\n\n  def system_id : String\n    @id\n  end\n\n  def self.from_name?(groupname : String) : ::System::Group?\n    if found = Crystal::System.name_to_sid(groupname)\n      from_sid(found.sid)\n    end\n  end\n\n  def self.from_id?(groupid : String) : ::System::Group?\n    if sid = Crystal::System.sid_from_s(groupid)\n      begin\n        from_sid(sid)\n      ensure\n        LibC.LocalFree(sid)\n      end\n    end\n  end\n\n  private def self.from_sid(sid : LibC::SID*) : ::System::Group?\n    canonical = Crystal::System.sid_to_name(sid) || return\n\n    # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/7b2aeb27-92fc-41f6-8437-deb65d950921#gt_0387e636-5654-4910-9519-1f8326cf5ec0\n    # SidTypeAlias should also be treated as a group type next to SidTypeGroup\n    # and SidTypeWellKnownGroup:\n    # \"alias object -> resource group: A group object...\"\n    #\n    # Tests show that \"Administrators\" can be considered of type SidTypeAlias.\n    case canonical.type\n    when .sid_type_group?, .sid_type_well_known_group?, .sid_type_alias?\n      domain_and_group = canonical.domain.empty? ? canonical.name : \"#{canonical.domain}\\\\#{canonical.name}\"\n      gid = Crystal::System.sid_to_s(sid)\n      ::System::Group.new(domain_and_group, gid)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/hostname.cr",
    "content": "require \"c/sysinfoapi\"\n\nmodule Crystal::System\n  def self.hostname\n    retry_wstr_buffer do |buffer, small_buf|\n      name_size = LibC::DWORD.new(buffer.size)\n      if LibC.GetComputerNameExW(LibC::COMPUTER_NAME_FORMAT::ComputerNameDnsHostname, buffer, pointerof(name_size)) != 0\n        break String.from_utf16(buffer[0, name_size])\n      elsif small_buf && name_size > 0\n        next name_size\n      else\n        raise RuntimeError.from_winerror(\"Could not get hostname\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/iocp.cr",
    "content": "{% skip_file unless flag?(:win32) %}\nrequire \"c/handleapi\"\nrequire \"c/ioapiset\"\nrequire \"c/ntdll\"\nrequire \"crystal/system/thread_linked_list\"\n\n# :nodoc:\nstruct Crystal::System::IOCP\n  @@wait_completion_packet_methods : Bool? = nil\n\n  @@_NtCreateWaitCompletionPacket = uninitialized LibNTDLL::NtCreateWaitCompletionPacketProc\n  @@_NtAssociateWaitCompletionPacket = uninitialized LibNTDLL::NtAssociateWaitCompletionPacketProc\n  @@_NtCancelWaitCompletionPacket = uninitialized LibNTDLL::NtCancelWaitCompletionPacketProc\n\n  class_getter?(wait_completion_packet_methods : Bool) do\n    load_wait_completion_packet_methods\n  end\n\n  private def self.load_wait_completion_packet_methods : Bool\n    handle = LibC.LoadLibraryExW(Crystal::System.to_wstr(\"ntdll.dll\"), nil, 0)\n    return false if handle.null?\n\n    pointer = LibC.GetProcAddress(handle, \"NtCreateWaitCompletionPacket\")\n    return false if pointer.null?\n    @@_NtCreateWaitCompletionPacket = LibNTDLL::NtCreateWaitCompletionPacketProc.new(pointer, Pointer(Void).null)\n\n    pointer = LibC.GetProcAddress(handle, \"NtAssociateWaitCompletionPacket\")\n    @@_NtAssociateWaitCompletionPacket = LibNTDLL::NtAssociateWaitCompletionPacketProc.new(pointer, Pointer(Void).null)\n\n    pointer = LibC.GetProcAddress(handle, \"NtCancelWaitCompletionPacket\")\n    @@_NtCancelWaitCompletionPacket = LibNTDLL::NtCancelWaitCompletionPacketProc.new(pointer, Pointer(Void).null)\n\n    true\n  end\n\n  # :nodoc:\n  class CompletionKey\n    enum Tag\n      ProcessRun\n      StdinRead\n      Interrupt\n      Timer\n    end\n\n    property fiber : ::Fiber?\n    getter tag : Tag\n\n    property next : CompletionKey?\n    property previous : CompletionKey?\n\n    # Data structure to extend the lifetime of completion keys, in particular\n    # those created by `Process.new` without an associated `#wait` call\n    @@pending = ::Thread::LinkedList(CompletionKey).new\n\n    def self.unregister(key : self) : Nil\n      @@pending.delete(key)\n    end\n\n    def initialize(@tag : Tag, @fiber : ::Fiber? = nil)\n      @@pending.push(self)\n    end\n\n    def valid?(number_of_bytes_transferred)\n      case tag\n      in .process_run?\n        number_of_bytes_transferred.in?(LibC::JOB_OBJECT_MSG_EXIT_PROCESS, LibC::JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS)\n      in .stdin_read?, .interrupt?, .timer?\n        true\n      end\n    end\n\n    def inspect(io : IO) : Nil\n      to_s(io)\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"#<\" << self.class.name << \":0x\"\n      object_id.to_s(io, 16)\n      io << \" @fiber=\"\n      @fiber.inspect io\n      io << ','\n      io << \" @tag=\"\n      @tag.inspect io\n      io << '>'\n    end\n  end\n\n  getter handle : LibC::HANDLE\n\n  def initialize\n    @handle = LibC.CreateIoCompletionPort(LibC::INVALID_HANDLE_VALUE, nil, nil, 0)\n    raise IO::Error.from_winerror(\"CreateIoCompletionPort\") if @handle.null?\n  end\n\n  def wait_queued_completions(timeout, alertable = false, &)\n    overlapped_entries = uninitialized LibC::OVERLAPPED_ENTRY[64]\n\n    if timeout > UInt64::MAX\n      timeout = LibC::INFINITE\n    else\n      timeout = timeout.to_u64\n    end\n\n    result = LibC.GetQueuedCompletionStatusEx(@handle, overlapped_entries, overlapped_entries.size, out removed, timeout, alertable)\n\n    if result == 0\n      error = WinError.value\n      if timeout && error.wait_timeout?\n        return true\n      elsif alertable && error.value == LibC::WAIT_IO_COMPLETION\n        return true\n      else\n        raise IO::Error.from_os_error(\"GetQueuedCompletionStatusEx\", error)\n      end\n    end\n\n    if removed == 0\n      raise IO::Error.new(\"GetQueuedCompletionStatusEx returned 0\")\n    end\n\n    # TODO: wouldn't the processing fit better in `EventLoop::IOCP#run`?\n    removed.times do |i|\n      entry = overlapped_entries[i]\n\n      # See `CompletionKey` for the operations that use a non-nil completion\n      # key. All IO operations (include File, Socket) do not set this field.\n      case completion_key = Pointer(Void).new(entry.lpCompletionKey).as(CompletionKey?)\n      in Nil\n        operation = OverlappedOperation.unbox(entry.lpOverlapped)\n        Crystal.trace :evloop, \"operation\", op: operation.class.name, fiber: operation.@fiber\n        operation.schedule { |fiber| yield fiber }\n      in CompletionKey\n        Crystal.trace :evloop, \"completion\", tag: completion_key.tag.to_s, bytes: entry.dwNumberOfBytesTransferred, fiber: completion_key.fiber\n\n        CompletionKey.unregister(completion_key)\n        if completion_key.valid?(entry.dwNumberOfBytesTransferred)\n          # if `Process` exits before a call to `#wait`, this fiber will be\n          # reset already\n          if fiber = completion_key.fiber\n            # this ensures existing references to `completion_key` do not keep\n            # an indirect reference to `::Thread.current`, as that leads to a\n            # finalization cycle\n            completion_key.fiber = nil\n            yield fiber\n          end\n        end\n      end\n    end\n\n    false\n  end\n\n  def post_queued_completion_status(completion_key : CompletionKey, number_of_bytes_transferred = 0)\n    result = LibC.PostQueuedCompletionStatus(@handle, number_of_bytes_transferred, completion_key.as(Void*).address, nil)\n    raise RuntimeError.from_winerror(\"PostQueuedCompletionStatus\") if result == 0\n  end\n\n  def create_wait_completion_packet : LibC::HANDLE\n    packet_handle = LibC::HANDLE.null\n    object_attributes = Pointer(LibC::OBJECT_ATTRIBUTES).null\n    status = @@_NtCreateWaitCompletionPacket.call(pointerof(packet_handle), LibNTDLL::GENERIC_ALL, object_attributes)\n    raise RuntimeError.from_os_error(\"NtCreateWaitCompletionPacket\", WinError.from_ntstatus(status)) unless status == 0\n    packet_handle\n  end\n\n  def associate_wait_completion_packet(wait_handle : LibC::HANDLE, target_handle : LibC::HANDLE, completion_key : CompletionKey) : Bool\n    signaled = 0_u8\n    status = @@_NtAssociateWaitCompletionPacket.call(wait_handle, @handle,\n      target_handle, completion_key.as(Void*), Pointer(Void).null,\n      LibNTDLL::NTSTATUS.new!(0), Pointer(LibC::ULONG).null,\n      pointerof(signaled))\n    raise RuntimeError.from_os_error(\"NtAssociateWaitCompletionPacket\", WinError.from_ntstatus(status)) unless status == 0\n    signaled == 1\n  end\n\n  def cancel_wait_completion_packet(wait_handle : LibC::HANDLE, remove_signaled : Bool) : LibNTDLL::NTSTATUS\n    status = @@_NtCancelWaitCompletionPacket.call(wait_handle, remove_signaled ? 1_u8 : 0_u8)\n    case status\n    when LibC::STATUS_CANCELLED, LibC::STATUS_SUCCESS, LibC::STATUS_PENDING\n      status\n    else\n      raise RuntimeError.from_os_error(\"NtCancelWaitCompletionPacket\", WinError.from_ntstatus(status))\n    end\n  end\n\n  abstract class OverlappedOperation\n    enum State\n      STARTED\n      DONE\n    end\n\n    abstract def wait_for_result(timeout, & : WinError ->)\n    private abstract def try_cancel : Bool\n\n    @overlapped = LibC::OVERLAPPED.new\n    @fiber = ::Fiber.current\n    @state : State = :started\n\n    def self.run(*args, **opts, &)\n      operation_storage = uninitialized ReferenceStorage(self)\n      operation = unsafe_construct(pointerof(operation_storage), *args, **opts)\n      yield operation\n    end\n\n    def self.unbox(overlapped : LibC::OVERLAPPED*) : self\n      start = overlapped.as(Pointer(UInt8)) - offsetof(self, @overlapped)\n      Box(self).unbox(start.as(Pointer(Void)))\n    end\n\n    def to_unsafe\n      pointerof(@overlapped)\n    end\n\n    protected def schedule(&)\n      done!\n      yield @fiber\n    end\n\n    private def done!\n      @state = :done\n    end\n\n    private def wait_for_completion(timeout)\n      if timeout\n        evloop = EventLoop.current.as(EventLoop::IOCP)\n\n        if evloop.timeout(timeout)\n          # By the time the fiber was resumed, the operation may have completed\n          # concurrently.\n          return if @state.done?\n          return unless try_cancel\n\n          # We cancelled the operation or failed to cancel it (e.g. race\n          # condition), we must suspend the fiber again until the completion\n          # port is notified of the actual result.\n          ::Fiber.suspend\n        end\n      else\n        ::Fiber.suspend\n      end\n    end\n  end\n\n  class IOOverlappedOperation < OverlappedOperation\n    def initialize(@handle : LibC::HANDLE)\n    end\n\n    def offset=(value : UInt64)\n      @overlapped.union.offset.offset = LibC::DWORD.new!(value)\n      @overlapped.union.offset.offsetHigh = LibC::DWORD.new!(value >> 32)\n    end\n\n    def wait_for_result(timeout, & : WinError ->)\n      wait_for_completion(timeout)\n\n      result = LibC.GetOverlappedResult(@handle, self, out bytes, 0)\n      if result.zero?\n        error = WinError.value\n        yield error\n\n        raise IO::Error.from_os_error(\"GetOverlappedResult\", error)\n      end\n\n      bytes\n    end\n\n    private def try_cancel : Bool\n      # Microsoft documentation:\n      # The application must not free or reuse the OVERLAPPED structure\n      # associated with the canceled I/O operations until they have completed\n      # (this does not apply to asynchronous operations that finished\n      # synchronously, as nothing would be queued to the IOCP)\n      ret = LibC.CancelIoEx(@handle, self)\n      if ret.zero?\n        case error = WinError.value\n        when .error_not_found?\n          # Operation has already completed, do nothing\n          return false\n        else\n          raise RuntimeError.from_os_error(\"CancelIoEx\", os_error: error)\n        end\n      end\n      true\n    end\n  end\n\n  class WSAOverlappedOperation < OverlappedOperation\n    def initialize(@handle : LibC::SOCKET)\n    end\n\n    def wait_for_result(timeout, & : WinError ->)\n      wait_for_completion(timeout)\n\n      flags = 0_u32\n      result = LibC.WSAGetOverlappedResult(@handle, self, out bytes, false, pointerof(flags))\n      if result.zero?\n        error = WinError.wsa_value\n        yield error\n\n        raise IO::Error.from_os_error(\"WSAGetOverlappedResult\", error)\n      end\n\n      bytes\n    end\n\n    private def try_cancel : Bool\n      # Microsoft documentation:\n      # The application must not free or reuse the OVERLAPPED structure\n      # associated with the canceled I/O operations until they have completed\n      # (this does not apply to asynchronous operations that finished\n      # synchronously, as nothing would be queued to the IOCP)\n      ret = LibC.CancelIoEx(Pointer(Void).new(@handle), self)\n      if ret.zero?\n        case error = WinError.value\n        when .error_not_found?\n          # Operation has already completed, do nothing\n          return false\n        else\n          raise RuntimeError.from_os_error(\"CancelIoEx\", os_error: error)\n        end\n      end\n      true\n    end\n  end\n\n  class GetAddrInfoOverlappedOperation < OverlappedOperation\n    getter iocp\n    setter cancel_handle : LibC::HANDLE = LibC::INVALID_HANDLE_VALUE\n\n    def initialize(@iocp : LibC::HANDLE)\n    end\n\n    def wait_for_result(timeout, & : WinError ->)\n      wait_for_completion(timeout)\n\n      result = LibC.GetAddrInfoExOverlappedResult(self)\n      unless result.zero?\n        error = WinError.new(result.to_u32!)\n        yield error\n\n        raise ::Socket::Addrinfo::Error.from_os_error(\"GetAddrInfoExOverlappedResult\", error)\n      end\n\n      @overlapped.union.pointer.as(LibC::ADDRINFOEXW**).value\n    end\n\n    private def try_cancel : Bool\n      ret = LibC.GetAddrInfoExCancel(pointerof(@cancel_handle))\n      unless ret.zero?\n        case error = WinError.new(ret.to_u32!)\n        when .wsa_invalid_handle?\n          # Operation has already completed, do nothing\n          return false\n        else\n          raise ::Socket::Addrinfo::Error.from_os_error(\"GetAddrInfoExCancel\", error)\n        end\n      end\n      true\n    end\n  end\n\n  def self.overlapped_operation(file_descriptor, method, timeout, *, offset = nil, writing = false, &)\n    handle = file_descriptor.windows_handle\n    seekable = LibC.SetFilePointerEx(handle, 0, out original_offset, IO::Seek::Current) != 0\n\n    IOOverlappedOperation.run(handle) do |operation|\n      overlapped = operation.to_unsafe\n      if seekable\n        start_offset = offset || original_offset\n        overlapped.value.union.offset.offset = LibC::DWORD.new!(start_offset)\n        overlapped.value.union.offset.offsetHigh = LibC::DWORD.new!(start_offset >> 32)\n      end\n      result, value = yield operation\n\n      if result == 0\n        case error = WinError.value\n        when .error_handle_eof?\n          return 0_u32\n        when .error_broken_pipe?\n          return 0_u32\n        when .error_io_pending?\n          # the operation is running asynchronously; do nothing\n        when .error_access_denied?\n          raise IO::Error.new \"File not open for #{writing ? \"writing\" : \"reading\"}\", target: file_descriptor\n        else\n          raise IO::Error.from_os_error(method, error, target: file_descriptor)\n        end\n      else\n        # operation completed synchronously; seek forward by number of bytes\n        # read or written if handle is seekable, since overlapped I/O doesn't do\n        # it automatically\n        LibC.SetFilePointerEx(handle, value, nil, IO::Seek::Current) if seekable\n        return value\n      end\n\n      byte_count = operation.wait_for_result(timeout) do |error|\n        case error\n        when .error_io_incomplete?, .error_operation_aborted?\n          raise IO::TimeoutError.new(\"#{method} timed out\")\n        when .error_handle_eof?\n          return 0_u32\n        when .error_broken_pipe?\n          # TODO: this is needed for `Process.run`, can we do without it?\n          return 0_u32\n        end\n      end\n\n      # operation completed asynchronously; seek to the original file position\n      # plus the number of bytes read or written (other operations might have\n      # moved the file pointer so we don't use `IO::Seek::Current` here), unless\n      # we are calling `Crystal::System::FileDescriptor.pread`\n      if seekable && !offset\n        LibC.SetFilePointerEx(handle, original_offset + byte_count, nil, IO::Seek::Set)\n      end\n      byte_count\n    end\n  end\n\n  def self.wsa_overlapped_operation(target, socket, method, timeout, connreset_is_error = true, &)\n    WSAOverlappedOperation.run(socket) do |operation|\n      result, value = yield operation\n\n      if result == LibC::SOCKET_ERROR\n        case error = WinError.wsa_value\n        when .wsa_io_pending?\n          # the operation is running asynchronously; do nothing\n        else\n          raise IO::Error.from_os_error(method, error, target: target)\n        end\n      else\n        return value\n      end\n\n      operation.wait_for_result(timeout) do |error|\n        case error\n        when .wsa_io_incomplete?, .error_operation_aborted?\n          raise IO::Error.new(\"Closed\") if target.closed?\n          raise IO::TimeoutError.new(\"#{method} timed out\")\n        when .wsaeconnreset?\n          return 0_u32 unless connreset_is_error\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/library_archive.cr",
    "content": "# This module provides functions for operating with Windows library archives.\n# Implementation based on: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format\nmodule Crystal::System::LibraryArchive\n  # Returns the list of DLL filenames imported by the .lib archive at the given\n  # *path*.\n  def self.imported_dlls(path : ::Path | String) : Set(String)\n    return Set(String).new unless ::File.file?(path)\n    ::File.open(path, \"r\") do |f|\n      reader = COFFReader.new(f)\n      reader.run\n      reader.dlls\n    end\n  end\n\n  # Minimal implementation of a Microsoft COFF archive reader. All unused fields\n  # are ignored.\n  private struct COFFReader\n    getter dlls = Set(String).new\n\n    # MSVC-style import libraries include the `__NULL_IMPORT_DESCRIPTOR` symbol,\n    # MinGW-style ones do not\n    getter? msvc = false\n\n    def initialize(@ar : ::File)\n    end\n\n    # Attempts to collect all DLL imports found in an import library. Should not\n    # raise if the library is not an import library. Might raise if `@ar` is not\n    # a library at all.\n    def run\n      file_size = @ar.size\n\n      # magic number\n      return unless @ar.read_string(8) == \"!<arch>\\n\"\n\n      # first linker member's filename is `/`\n      # second linker member's filename is also `/` (apparently not all linkers generate this?)\n      # longnames member's filename is `//` (optional)\n      # the rest are standard members\n      first = true\n      until @ar.pos == file_size\n        read_member do |filename, io|\n          if first\n            first = false\n            return unless filename == \"/\"\n            handle_first_member(io)\n          elsif !filename.in?(\"/\", \"//\")\n            handle_standard_member(io)\n          end\n        end\n      end\n    end\n\n    private def read_member(& : String ->)\n      filename = @ar.read_string(16).rstrip(' ')\n\n      # time(12) + uid(6) + gid(6) + mode(8)\n      @ar.skip(32)\n\n      size = @ar.read_string(10).rstrip(' ').to_u32\n\n      # end of header\n      return unless @ar.read_string(2) == \"`\\n\"\n\n      new_pos = @ar.pos + size + (size.odd? ? 1 : 0)\n      yield filename, IO::Sized.new(@ar, read_size: size)\n      @ar.seek(new_pos)\n    end\n\n    private def handle_first_member(io)\n      symbol_count = io.read_bytes(UInt32, IO::ByteFormat::BigEndian)\n\n      # 4-byte offset per symbol\n      io.skip(symbol_count * 4)\n\n      symbol_count.times do\n        symbol = io.gets('\\0', chomp: true)\n        if symbol == \"__NULL_IMPORT_DESCRIPTOR\"\n          @msvc = true\n          break\n        end\n      end\n    end\n\n    private def handle_standard_member(io)\n      machine = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)\n      section_count = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)\n\n      if machine == 0x0000 && section_count == 0xFFFF\n        # short import library\n        version = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)\n        return unless version == 0 # 1 and 2 are used by object files (ANON_OBJECT_HEADER)\n\n        # machine(2) + time(4) + size(4) + ordinal/hint(2) + flags(2)\n        io.skip(14)\n\n        # TODO: is there a way to do this without constructing a temporary string,\n        # but with the optimizations present in `IO#gets`?\n        return unless io.gets('\\0') # symbol name\n\n        if dll_name = io.gets('\\0', chomp: true)\n          @dlls << dll_name if valid_dll?(dll_name)\n        end\n      else\n        # long import library, code based on GNU binutils `dlltool -I`:\n        # https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=binutils/dlltool.c;hb=967dc35c78adb85ee1e2e596047d9dc69107a9db#l3231\n\n        # timeDateStamp(4) + pointerToSymbolTable(4) + numberOfSymbols(4) + sizeOfOptionalHeader(2) + characteristics(2)\n        io.skip(16)\n\n        section_count.times do |i|\n          section_header = uninitialized LibC::IMAGE_SECTION_HEADER\n          return unless io.read_fully?(pointerof(section_header).to_slice(1).to_unsafe_bytes)\n\n          name = String.new(section_header.name.to_slice, truncate_at_null: true)\n          next unless name == (msvc? ? \".idata$6\" : \".idata$7\")\n\n          if msvc? ? section_header.characteristics.bits_set?(LibC::IMAGE_SCN_CNT_INITIALIZED_DATA) : section_header.pointerToRelocations == 0\n            bytes_read = sizeof(LibC::IMAGE_FILE_HEADER) + sizeof(LibC::IMAGE_SECTION_HEADER) * (i + 1)\n            io.skip(section_header.pointerToRawData - bytes_read)\n            if dll_name = io.gets('\\0', chomp: true, limit: section_header.sizeOfRawData)\n              @dlls << dll_name if valid_dll?(dll_name)\n            end\n          end\n\n          return\n        end\n      end\n    end\n\n    private def valid_dll?(name)\n      name.size >= 5 && name[-4..].compare(\".dll\", case_insensitive: true) == 0\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/mime.cr",
    "content": "require \"./windows_registry\"\n\nmodule Crystal::System::MIME\n  CONTENT_TYPE = System.wstr_literal \"Content Type\"\n\n  # Load MIME types from operating system source.\n  def self.load\n    WindowsRegistry.each_name(LibC::HKEY_CLASSES_ROOT) do |name|\n      # skip anything that is not a file extension\n      next if name.size < 2 || !(name[0] === '.')\n\n      WindowsRegistry.open?(LibC::HKEY_CLASSES_ROOT, name) do |sub_handle|\n        content_type = WindowsRegistry.get_string(sub_handle, CONTENT_TYPE)\n        if content_type\n          ::MIME.register String.from_utf16(name), content_type\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/path.cr",
    "content": "require \"c/combaseapi\"\nrequire \"c/knownfolders\"\nrequire \"c/shlobj_core\"\n\nmodule Crystal::System::Path\n  def self.home : String\n    ENV[\"USERPROFILE\"]?.presence || known_folder_path(LibC::FOLDERID_Profile)\n  end\n\n  def self.known_folder_path(guid : LibC::GUID) : String\n    if LibC.SHGetKnownFolderPath(pointerof(guid), 0, nil, out path_ptr) == 0\n      path, _ = String.from_utf16(path_ptr)\n      LibC.CoTaskMemFree(path_ptr)\n      path\n    else\n      raise RuntimeError.from_winerror(\"SHGetKnownFolderPath\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/process.cr",
    "content": "require \"c/processthreadsapi\"\nrequire \"c/handleapi\"\nrequire \"c/jobapi2\"\nrequire \"c/synchapi\"\nrequire \"c/tlhelp32\"\nrequire \"c/debugapi\"\nrequire \"process/shell\"\nrequire \"crystal/atomic_semaphore\"\n\nstruct Crystal::System::Process\n  {% if host_flag?(:windows) %}\n    HOST_PATH_DELIMITER = ';'\n  {% else %}\n    HOST_PATH_DELIMITER = ':'\n  {% end %}\n\n  getter pid : LibC::DWORD\n  @thread_id : LibC::DWORD\n  @process_handle : LibC::HANDLE\n  @job_object : LibC::HANDLE\n  @completion_key = IOCP::CompletionKey.new(:process_run)\n\n  @@interrupt_handler : Proc(::Process::ExitReason, Nil)?\n  @@interrupt_count = Crystal::AtomicSemaphore.new\n  @@win32_interrupt_handler : LibC::PHANDLER_ROUTINE?\n  @@setup_interrupt_handler = Atomic(Bool).new(false)\n  @@last_interrupt = ::Process::ExitReason::Interrupted\n\n  def initialize(process_info)\n    @pid = process_info.dwProcessId\n    @thread_id = process_info.dwThreadId\n    @process_handle = process_info.hProcess\n\n    @job_object = LibC.CreateJobObjectW(nil, nil)\n\n    # enable IOCP notifications\n    config_job_object(\n      LibC::JOBOBJECTINFOCLASS::AssociateCompletionPortInformation,\n      LibC::JOBOBJECT_ASSOCIATE_COMPLETION_PORT.new(\n        completionKey: @completion_key.as(Void*),\n        completionPort: Crystal::EventLoop.current.iocp_handle,\n      ),\n    )\n\n    # but not for any child processes\n    config_job_object(\n      LibC::JOBOBJECTINFOCLASS::ExtendedLimitInformation,\n      LibC::JOBOBJECT_EXTENDED_LIMIT_INFORMATION.new(\n        basicLimitInformation: LibC::JOBOBJECT_BASIC_LIMIT_INFORMATION.new(\n          limitFlags: LibC::JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK,\n        ),\n      ),\n    )\n\n    if LibC.AssignProcessToJobObject(@job_object, @process_handle) == 0\n      raise RuntimeError.from_winerror(\"AssignProcessToJobObject\")\n    end\n\n    if LibC.ResumeThread(process_info.hThread) == 0xFFFFFFFF_u32\n      raise RuntimeError.from_winerror(\"ResumeThread\")\n    end\n\n    close_handle(process_info.hThread)\n  end\n\n  private def config_job_object(kind, info)\n    if LibC.SetInformationJobObject(@job_object, kind, pointerof(info), sizeof(typeof(info))) == 0\n      raise RuntimeError.from_winerror(\"SetInformationJobObject\")\n    end\n  end\n\n  def release\n    return if @process_handle == LibC::HANDLE.null\n    close_handle(@process_handle)\n    @process_handle = LibC::HANDLE.null\n    close_handle(@job_object)\n    @job_object = LibC::HANDLE.null\n  end\n\n  def wait\n    if LibC.GetExitCodeProcess(@process_handle, out exit_code) == 0\n      raise RuntimeError.from_winerror(\"GetExitCodeProcess\")\n    end\n    return exit_code unless exit_code == LibC::STILL_ACTIVE\n\n    # let `@job_object` do its job\n    # TODO: message delivery is \"not guaranteed\"; does it ever happen? Are we\n    # stuck forever in that case?\n    # (https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port)\n    @completion_key.fiber = ::Fiber.current\n    ::Fiber.suspend\n\n    # If the IOCP notification is delivered before the process fully exits,\n    # wait for it\n    if LibC.WaitForSingleObject(@process_handle, LibC::INFINITE) != LibC::WAIT_OBJECT_0\n      raise RuntimeError.from_winerror(\"WaitForSingleObject\")\n    end\n\n    # WaitForSingleObject returns immediately once ExitProcess is called in the child, but\n    # the process still has yet to be destructed by the OS and have it's memory unmapped.\n    # Since the semantics on unix are that the resources of a process have been released once\n    # waitpid returns, we wait 5 milliseconds to attempt to replicate this behaviour.\n    sleep 5.milliseconds\n\n    if LibC.GetExitCodeProcess(@process_handle, pointerof(exit_code)) == 0\n      raise RuntimeError.from_winerror(\"GetExitCodeProcess\")\n    end\n    if exit_code == LibC::STILL_ACTIVE\n      raise \"BUG: Process still active\"\n    end\n    exit_code\n  end\n\n  def exists?\n    Crystal::System::Process.exists?(@pid)\n  end\n\n  def terminate(*, graceful)\n    LibC.TerminateProcess(@process_handle, 1)\n  end\n\n  def self.exit(status : Int32)\n    LibC.exit(status)\n  end\n\n  def self.exit(status : ::Process::Status)\n    exit status.system_exit_status.to_i32!\n  end\n\n  def self.pid\n    LibC.GetCurrentProcessId\n  end\n\n  def self.pgid\n    raise NotImplementedError.new(\"Process.pgid\")\n  end\n\n  def self.pgid(pid)\n    raise NotImplementedError.new(\"Process.pgid\")\n  end\n\n  def self.ppid\n    pid = self.pid\n    each_process_entry do |pe|\n      return pe.th32ParentProcessID if pe.th32ProcessID == pid\n    end\n    raise RuntimeError.new(\"Cannot locate current process\")\n  end\n\n  private def self.each_process_entry(&)\n    h = LibC.CreateToolhelp32Snapshot(LibC::TH32CS_SNAPPROCESS, 0)\n    raise RuntimeError.from_winerror(\"CreateToolhelp32Snapshot\") if h == LibC::INVALID_HANDLE_VALUE\n\n    begin\n      pe = LibC::PROCESSENTRY32W.new(dwSize: sizeof(LibC::PROCESSENTRY32W))\n      if LibC.Process32FirstW(h, pointerof(pe)) != 0\n        while true\n          yield pe\n          break if LibC.Process32NextW(h, pointerof(pe)) == 0\n        end\n      end\n    ensure\n      LibC.CloseHandle(h)\n    end\n  end\n\n  def self.signal(pid, signal)\n    raise NotImplementedError.new(\"Process.signal\")\n  end\n\n  @[Deprecated(\"Use `#on_terminate` instead\")]\n  def self.on_interrupt(&handler : ->) : Nil\n    on_terminate do |reason|\n      handler.call if reason.interrupted?\n    end\n  end\n\n  def self.on_terminate(&@@interrupt_handler : ::Process::ExitReason ->) : Nil\n    restore_interrupts!\n    @@win32_interrupt_handler = handler = LibC::PHANDLER_ROUTINE.new do |event_type|\n      @@last_interrupt = case event_type\n                         when LibC::CTRL_C_EVENT, LibC::CTRL_BREAK_EVENT\n                           ::Process::ExitReason::Interrupted\n                         when LibC::CTRL_CLOSE_EVENT\n                           ::Process::ExitReason::TerminalDisconnected\n                         when LibC::CTRL_LOGOFF_EVENT, LibC::CTRL_SHUTDOWN_EVENT\n                           ::Process::ExitReason::SessionEnded\n                         else\n                           next 0\n                         end\n      @@interrupt_count.signal\n      1\n    end\n    LibC.SetConsoleCtrlHandler(handler, 1)\n  end\n\n  def self.ignore_interrupts! : Nil\n    remove_interrupt_handler\n    LibC.SetConsoleCtrlHandler(nil, 1)\n  end\n\n  def self.restore_interrupts! : Nil\n    remove_interrupt_handler\n    LibC.SetConsoleCtrlHandler(nil, 0)\n  end\n\n  private def self.remove_interrupt_handler\n    if old = @@win32_interrupt_handler\n      LibC.SetConsoleCtrlHandler(old, 0)\n      @@win32_interrupt_handler = nil\n    end\n  end\n\n  def self.start_interrupt_loop : Nil\n    return if @@setup_interrupt_handler.swap(true, :relaxed)\n\n    spawn(name: \"interrupt-signal-loop\") do\n      while true\n        @@interrupt_count.wait { sleep 50.milliseconds }\n\n        if handler = @@interrupt_handler\n          non_nil_handler = handler # if handler is closured it will also have the Nil type\n          int_type = @@last_interrupt\n          spawn do\n            non_nil_handler.call int_type\n          rescue ex\n            ex.inspect_with_backtrace(STDERR)\n            STDERR.puts(\"FATAL: uncaught exception while processing interrupt handler, exiting\")\n            STDERR.flush\n            LibC._exit(1)\n          end\n        end\n      end\n    end\n  end\n\n  def self.debugger_present? : Bool\n    LibC.IsDebuggerPresent != 0\n  end\n\n  def self.exists?(pid)\n    handle = LibC.OpenProcess(LibC::PROCESS_QUERY_INFORMATION, 0, pid)\n    return false unless handle\n    begin\n      if LibC.GetExitCodeProcess(handle, out exit_code) == 0\n        raise RuntimeError.from_winerror(\"GetExitCodeProcess\")\n      end\n      exit_code == LibC::STILL_ACTIVE\n    ensure\n      close_handle(handle)\n    end\n  end\n\n  def self.times\n    if LibC.GetProcessTimes(LibC.GetCurrentProcess, out create, out exit, out kernel, out user) == 0\n      raise RuntimeError.from_winerror(\"GetProcessTimes\")\n    end\n    ::Process::Tms.new(\n      Crystal::System::Time.filetime_to_f64secs(user),\n      Crystal::System::Time.filetime_to_f64secs(kernel),\n      0,\n      0)\n  end\n\n  def self.fork\n    raise NotImplementedError.new(\"Process.fork\")\n  end\n\n  def self.fork(&)\n    raise NotImplementedError.new(\"Process.fork\")\n  end\n\n  private def self.handle_from_io(io : IO::FileDescriptor, parent_io)\n    source_handle =\n      if io.is_a?(File) && !io.system_blocking? && !io.closed?\n        dup_handle = reopen_file_as_blocking(io, parent_io == STDIN, \"Process.run\")\n      else\n        io.windows_handle\n      end\n\n    cur_proc = LibC.GetCurrentProcess\n    if LibC.DuplicateHandle(cur_proc, source_handle, cur_proc, out new_handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0\n      raise RuntimeError.from_winerror(\"DuplicateHandle\")\n    end\n\n    {new_handle, dup_handle}\n  end\n\n  def self.spawn(prepared_args, shell, env, clear_env, input, output, error, chdir, &)\n    startup_info = LibC::STARTUPINFOW.new\n    startup_info.cb = sizeof(LibC::STARTUPINFOW)\n    startup_info.dwFlags = LibC::STARTF_USESTDHANDLES\n\n    startup_info.hStdInput, dup_input = handle_from_io(input, STDIN)\n    startup_info.hStdOutput, dup_output = handle_from_io(output, STDOUT)\n    startup_info.hStdError, dup_error = handle_from_io(error, STDERR)\n\n    process_info = LibC::PROCESS_INFORMATION.new\n\n    prepared_args = ::Process.quote_windows(prepared_args) unless prepared_args.is_a?(String)\n\n    if LibC.CreateProcessW(\n         nil, System.to_wstr(prepared_args), nil, nil, true, LibC::CREATE_SUSPENDED | LibC::CREATE_UNICODE_ENVIRONMENT,\n         Env.make_env_block(env, clear_env), chdir.try { |str| System.to_wstr(str) } || Pointer(UInt16).null,\n         pointerof(startup_info), pointerof(process_info)\n       ) == 0\n      error = WinError.value\n      if ::File::NotFoundError.os_error?(error) || ::File::AccessDeniedError.os_error?(error) || error.in?(WinError::ERROR_BAD_EXE_FORMAT, WinError::ERROR_INVALID_PARAMETER)\n        yield error, prepared_args\n      else\n        raise IO::Error.from_os_error(\"Error executing process: '#{prepared_args}'\", error)\n      end\n    end\n\n    close_handle(startup_info.hStdInput)\n    close_handle(startup_info.hStdOutput)\n    close_handle(startup_info.hStdError)\n\n    close_handle(dup_input) if dup_input\n    close_handle(dup_output) if dup_output\n    close_handle(dup_error) if dup_error\n\n    process_info\n  end\n\n  def self.prepare_args(command : String, args : Enumerable(String)?, shell : Bool)\n    if shell\n      if args\n        raise NotImplementedError.new(\"Process with args and shell: true is not supported on Windows\")\n      end\n      command\n    else\n      command_args = [command]\n      command_args.concat(args) if args\n      prepare_args(command_args)\n    end\n  end\n\n  def self.prepare_args(args : Enumerable(String))\n    # Disable implicit execution of batch files (https://github.com/crystal-lang/crystal/issues/14536)\n    #\n    # > `CreateProcessW()` implicitly spawns `cmd.exe` when executing batch files (`.bat`, `.cmd`, etc.), even if the application didn’t specify them in the command line.\n    # > The problem is that the `cmd.exe` has complicated parsing rules for the command arguments, and programming language runtimes fail to escape the command arguments properly.\n    # > Because of this, it’s possible to inject commands if someone can control the part of command arguments of the batch file.\n    # https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/\n    command = args.first\n    if command.rstrip(\". \").byte_slice?(-4, 4).try(&.downcase).in?(\".bat\", \".cmd\")\n      raise ::File::Error.from_os_error(\"Error executing process\", WinError::ERROR_BAD_EXE_FORMAT, file: command)\n    end\n\n    args\n  end\n\n  private def self.try_replace(command, prepared_args, env, clear_env, input, output, error, chdir)\n    old_input_fd = reopen_io(input, ORIGINAL_STDIN)\n    old_output_fd = reopen_io(output, ORIGINAL_STDOUT)\n    old_error_fd = reopen_io(error, ORIGINAL_STDERR)\n\n    ENV.clear if clear_env\n    env.try &.each do |key, val|\n      if val\n        ENV[key] = val\n      else\n        ENV.delete key\n      end\n    end\n\n    if prepared_args.is_a?(String)\n      command = System.to_wstr(prepared_args)\n      argv = [command]\n    else\n      command = System.to_wstr(prepared_args[0])\n      argv = prepared_args.map { |arg| System.to_wstr(arg) }\n    end\n\n    argv << Pointer(LibC::WCHAR).null\n\n    if chdir\n      ::Dir.cd(chdir) do\n        LibC._wexecvp(command, argv)\n      end\n    else\n      LibC._wexecvp(command, argv)\n    end\n\n    # exec failed; restore the original C runtime file descriptors\n    errno = Errno.value\n    LibC._dup2(old_input_fd, 0)\n    LibC._dup2(old_output_fd, 1)\n    LibC._dup2(old_error_fd, 2)\n    errno\n  end\n\n  def self.replace(command, args, shell, env, clear_env, input, output, error, chdir) : NoReturn\n    prepared_args = prepare_args(command, args, shell)\n    errno = try_replace(command, prepared_args, env, clear_env, input, output, error, chdir)\n    raise_exception_from_errno(command, errno)\n  end\n\n  private def self.raise_exception_from_errno(command, errno = Errno.value)\n    if ::File::NotFoundError.os_error?(errno) || ::File::AccessDeniedError.os_error?(errno)\n      raise ::File::Error.from_os_error(\"Error executing process\", errno, file: command)\n    else\n      raise IO::Error.from_os_error(\"Error executing process: '#{command}'\", errno)\n    end\n  end\n\n  # Replaces the C standard streams' file descriptors, not Win32's, since\n  # `try_replace` uses the C `LibC._wexecvp` and only cares about the former.\n  # Returns a duplicate of the original file descriptor\n  private def self.reopen_io(src_io : IO::FileDescriptor, dst_io : IO::FileDescriptor)\n    dst_fd =\n      case dst_io\n      when ORIGINAL_STDIN  then 0\n      when ORIGINAL_STDOUT then 1\n      when ORIGINAL_STDERR then 2\n      else\n        raise \"BUG: Invalid destination IO\"\n      end\n\n    src_fd =\n      case src_io\n      when STDIN  then 0\n      when STDOUT then 1\n      when STDERR then 2\n      else\n        handle =\n          case src_io\n          when .system_blocking?, .closed?\n            src_io.windows_handle\n          when File\n            reopen_file_as_blocking(src_io, dst_fd == 0, \"Process.exec\")\n          else\n            raise IO::Error.new(\"Non-blocking streams are not supported in `Process.exec`\", target: src_io)\n          end\n        LibC._open_osfhandle(handle, 0)\n      end\n\n    return src_fd if dst_fd == src_fd\n\n    orig_src_fd = LibC._dup(src_fd)\n\n    if LibC._dup2(src_fd, dst_fd) == -1\n      raise IO::Error.from_errno(\"Failed to replace C file descriptor\", target: dst_io)\n    end\n\n    orig_src_fd\n  end\n\n  # The arguments for input, output and error must be handles without\n  # FILE_FLAG_OVERLAPPED.\n  private def self.reopen_file_as_blocking(file, for_stdin, method_name)\n    access = for_stdin ? LibC::FILE_GENERIC_READ : LibC::FILE_GENERIC_WRITE\n    handle = LibC.ReOpenFile(file.windows_handle, access, LibC::DEFAULT_SHARE_MODE, 0)\n    if handle == LibC::INVALID_HANDLE_VALUE\n      raise IO::Error.from_winerror(\"Failed to reopen non-blocking File as blocking\", target: file)\n    end\n    handle\n  end\n\n  def self.chroot(path)\n    raise NotImplementedError.new(\"Process.chroot\")\n  end\nend\n\nprivate def close_handle(handle)\n  if LibC.CloseHandle(handle) == 0\n    raise RuntimeError.from_winerror(\"CloseHandle\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/random.cr",
    "content": "require \"c/ntsecapi\"\n\nmodule Crystal::System::Random\n  def self.random_bytes(buf : Bytes) : Nil\n    if LibC.RtlGenRandom(buf, buf.size) == 0\n      raise RuntimeError.from_winerror(\"RtlGenRandom\")\n    end\n  end\n\n  def self.next_u : UInt8\n    buf = uninitialized UInt8\n    random_bytes(pointerof(buf).to_slice(1))\n    buf\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/signal.cr",
    "content": "require \"c/signal\"\nrequire \"c/malloc\"\n\nmodule Crystal::System::Signal\n  def self.trap(signal, handler) : Nil\n    raise NotImplementedError.new(\"Crystal::System::Signal.trap\")\n  end\n\n  def self.trap_handler?(signal)\n    raise NotImplementedError.new(\"Crystal::System::Signal.trap_handler?\")\n  end\n\n  def self.reset(signal) : Nil\n    raise NotImplementedError.new(\"Crystal::System::Signal.reset\")\n  end\n\n  def self.ignore(signal) : Nil\n    raise NotImplementedError.new(\"Crystal::System::Signal.ignore\")\n  end\n\n  def self.setup_seh_handler\n    LibC.AddVectoredExceptionHandler(1, ->(exception_info) do\n      case exception_info.value.exceptionRecord.value.exceptionCode\n      when LibC::EXCEPTION_ACCESS_VIOLATION\n        addr = exception_info.value.exceptionRecord.value.exceptionInformation[1]\n        Crystal::System.print_error \"Invalid memory access (C0000005) at address %p\\n\", Pointer(Void).new(addr)\n        {% if flag?(:gnu) %}\n          Exception::CallStack.print_backtrace\n        {% else %}\n          Exception::CallStack.print_backtrace(exception_info)\n        {% end %}\n        LibC._exit(1)\n      when LibC::EXCEPTION_STACK_OVERFLOW\n        LibC._resetstkoflw\n        Crystal::System.print_error \"Stack overflow (e.g., infinite or very deep recursion)\\n\"\n        {% if flag?(:gnu) %}\n          Exception::CallStack.print_backtrace\n        {% else %}\n          Exception::CallStack.print_backtrace(exception_info)\n        {% end %}\n        LibC._exit(1)\n      else\n        LibC::EXCEPTION_CONTINUE_SEARCH\n      end\n    end)\n\n    # ensure that even in the case of stack overflow there is enough reserved\n    # stack space for recovery (for other threads this is done in\n    # `Crystal::System::Thread.thread_proc`)\n    stack_size = Crystal::System::Fiber::RESERVED_STACK_SIZE\n    LibC.SetThreadStackGuarantee(pointerof(stack_size))\n\n    # this catches invalid argument checks inside the C runtime library\n    LibC._set_invalid_parameter_handler(->(expression, _function, _file, _line, _pReserved) do\n      message = expression ? String.from_utf16(expression)[0] : \"(no message)\"\n      Crystal::System.print_error \"CRT invalid parameter handler invoked: %s\\n\", message\n      caller.each do |frame|\n        Crystal::System.print_error \"  from %s\\n\", frame\n      end\n      LibC._exit(1)\n    end)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/socket.cr",
    "content": "require \"c/mswsock\"\nrequire \"c/ioapiset\"\nrequire \"crystal/system/win32/iocp\"\n\nmodule Crystal::System::Socket\n  alias Handle = LibC::SOCKET\n\n  # Initialize WSA\n  def self.initialize_wsa\n    # version 2.2\n    wsa_version = 0x202\n    err = LibC.WSAStartup(wsa_version, out wsadata)\n    unless err.zero?\n      raise IO::Error.from_os_error(\"WSAStartup\", WinError.new(err.to_u32))\n    end\n\n    if wsadata.wVersion != wsa_version\n      raise IO::Error.new(\"Unsuitable version of Winsock.dll: 0x#{wsadata.wVersion.to_s(16)}\")\n    end\n  end\n\n  def self.load_extension_function(socket, guid, proc_type)\n    function_pointer = uninitialized Pointer(Void)\n    result = LibC.WSAIoctl(\n      socket,\n      LibC::SIO_GET_EXTENSION_FUNCTION_POINTER,\n      pointerof(guid),\n      sizeof(LibC::GUID),\n      pointerof(function_pointer),\n      sizeof(Pointer(Void)),\n      out bytes,\n      nil,\n      nil\n    )\n    if result == LibC::SOCKET_ERROR\n      raise ::Socket::Error.from_wsa_error(\"WSAIoctl\")\n    end\n    proc_type.new(function_pointer, Pointer(Void).null)\n  end\n\n  class_getter connect_ex\n  class_getter accept_ex\n  @@connect_ex = uninitialized LibC::ConnectEx\n  @@accept_ex = uninitialized LibC::AcceptEx\n\n  # Some overlapped socket functions are not part of the Winsock specification.\n  # The implementation is provider-specific and needs to be queried at runtime\n  # with WSAIoctl.\n  # Crystal's socket implementation only uses Microsoft's default provider,\n  # so the same function can be shared across all sockets because they all use\n  # the same provider.\n  #\n  # https://stackoverflow.com/questions/37355397/why-is-the-wsarecvmsg-function-implemented-as-a-function-pointer-and-can-this-po/37356935#37356935\n  def self.initialize_extension_functions\n    initialize_wsa\n\n    # Create dummy socket for WSAIoctl\n    socket = LibC.socket(LibC::AF_INET, LibC::SOCK_STREAM, 0)\n    if socket == LibC::INVALID_SOCKET\n      raise ::Socket::Error.from_wsa_error(\"socket\")\n    end\n\n    @@connect_ex = load_extension_function(socket, LibC::WSAID_CONNECTEX, LibC::ConnectEx)\n    @@accept_ex = load_extension_function(socket, LibC::WSAID_ACCEPTEX, LibC::AcceptEx)\n\n    result = LibC.closesocket(socket)\n    unless result.zero?\n      raise ::Socket::Error.from_wsa_error(\"closesocket\")\n    end\n  end\n\n  initialize_extension_functions\n\n  def self.socket(family, type, protocol, blocking) : Handle\n    # the overlapped flag is distinct from the blocking mode in winsock (the\n    # latter acts like non-blocking BSD sockets); there's no downside to set the\n    # overlapped flag, we can do sync or async calls, and we can still change\n    # the blocking mode\n    socket = LibC.WSASocketW(family, type, protocol, nil, 0, LibC::WSA_FLAG_OVERLAPPED)\n    raise ::Socket::Error.from_wsa_error(\"WSASocketW\") if socket == LibC::INVALID_SOCKET\n    set_blocking(socket, blocking) unless blocking\n    socket\n  end\n\n  private def initialize_handle(handle, blocking = nil)\n    @blocking = blocking unless blocking.nil?\n\n    unless @family.unix?\n      Socket.getsockopt(handle, LibC::SO_REUSEADDR, 0) do |value|\n        if value == 0\n          Socket.setsockopt(handle, LibC::SO_EXCLUSIVEADDRUSE, 1)\n        end\n      end\n    end\n  end\n\n  private def system_connect(addr, timeout = nil)\n    if type.stream?\n      system_connect_stream(addr, timeout)\n    else\n      system_connect_connectionless(addr, timeout)\n    end\n  end\n\n  private def system_connect_stream(addr, timeout)\n    address = LibC::SockaddrIn6.new\n    address.sin6_family = family\n    address.sin6_port = 0\n    unless LibC.bind(fd, pointerof(address).as(LibC::Sockaddr*), sizeof(LibC::SockaddrIn6)) == 0\n      return ::Socket::BindError.from_wsa_error(\"Could not bind to '*'\")\n    end\n\n    error = event_loop.connect(self, addr, timeout)\n\n    if error\n      return error\n    end\n\n    # from https://learn.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options:\n    #\n    # > This option is used with the ConnectEx, WSAConnectByList, and\n    # > WSAConnectByName functions. This option updates the properties of the\n    # > socket after the connection is established. This option should be set\n    # > if the getpeername, getsockname, getsockopt, setsockopt, or shutdown\n    # > functions are to be used on the connected socket.\n    optname = LibC::SO_UPDATE_CONNECT_CONTEXT\n    if LibC.setsockopt(fd, LibC::SOL_SOCKET, optname, nil, 0) == LibC::SOCKET_ERROR\n      return ::Socket::Error.from_wsa_error(\"setsockopt #{optname}\")\n    end\n  end\n\n  # :nodoc:\n  def overlapped_connect(socket, method, timeout, &)\n    IOCP::WSAOverlappedOperation.run(socket) do |operation|\n      result = yield operation\n\n      if result == 0\n        case error = WinError.wsa_value\n        when .wsa_io_pending?\n          # the operation is running asynchronously; do nothing\n        when .wsaeaddrnotavail?\n          return ::Socket::ConnectError.from_os_error(\"ConnectEx\", error)\n        else\n          return ::Socket::Error.from_os_error(\"ConnectEx\", error)\n        end\n      else\n        return nil\n      end\n\n      operation.wait_for_result(timeout) do |error|\n        case error\n        when .wsa_io_incomplete?, .wsaeconnrefused?\n          return ::Socket::ConnectError.from_os_error(method, error)\n        when .error_operation_aborted?\n          # FIXME: Not sure why this is necessary\n          return ::Socket::ConnectError.from_os_error(method, error)\n        end\n      end\n\n      nil\n    end\n  end\n\n  private def system_connect_connectionless(addr, timeout)\n    ret = LibC.connect(fd, addr, addr.size)\n    if ret == LibC::SOCKET_ERROR\n      ::Socket::Error.from_wsa_error(\"connect\")\n    end\n  end\n\n  private def system_bind(addr, addrstr)\n    unless LibC.bind(fd, addr, addr.size) == 0\n      ::Socket::BindError.from_wsa_error(\"Could not bind to '#{addrstr}'\")\n    end\n  end\n\n  private def system_listen(backlog)\n    unless LibC.listen(fd, backlog) == 0\n      ::Socket::Error.from_wsa_error(\"Listen failed\")\n    end\n  end\n\n  private def system_accept : {Handle, Bool}?\n    event_loop.accept(self)\n  end\n\n  def system_accept(& : Handle -> Bool) : {Handle, Bool}?\n    client_socket, blocking = Crystal::EventLoop.current.socket(family, type, protocol, nil)\n    initialize_handle(client_socket)\n\n    if yield client_socket\n      {client_socket, blocking}\n    else\n      LibC.closesocket(client_socket)\n\n      nil\n    end\n  end\n\n  def overlapped_accept(socket, method, &)\n    IOCP::WSAOverlappedOperation.run(socket) do |operation|\n      result = yield operation\n\n      if result == 0\n        case WinError.wsa_value\n        when .wsa_io_pending?\n          # the operation is running asynchronously; do nothing\n        else\n          return false\n        end\n      else\n        return true\n      end\n\n      operation.wait_for_result(read_timeout) do |error|\n        case error\n        when .wsa_io_incomplete?, .wsaenotsock?\n          return false\n        when .error_operation_aborted?\n          # if the socket is closed then accept was aborted by an explicit\n          # shutdown before close, otherwise we manually canceled because of a\n          # timeout\n          return false if closed?\n          raise IO::TimeoutError.new(\"#{method} timed out (overlapped_accept)\")\n        end\n      end\n\n      true\n    end\n  end\n\n  private def system_close_read\n    if LibC.shutdown(fd, LibC::SH_RECEIVE) != 0\n      raise ::Socket::Error.from_wsa_error(\"shutdown read\")\n    end\n  end\n\n  private def system_close_write\n    if LibC.shutdown(fd, LibC::SH_SEND) != 0\n      raise ::Socket::Error.from_wsa_error(\"shutdown write\")\n    end\n  end\n\n  private def system_send_buffer_size : Int\n    getsockopt LibC::SO_SNDBUF, 0\n  end\n\n  private def system_send_buffer_size=(val : Int)\n    setsockopt LibC::SO_SNDBUF, val\n  end\n\n  private def system_recv_buffer_size : Int\n    getsockopt LibC::SO_RCVBUF, 0\n  end\n\n  private def system_recv_buffer_size=(val : Int)\n    setsockopt LibC::SO_RCVBUF, val\n  end\n\n  # SO_REUSEADDR, as used in posix, is always assumed on windows\n  # the SO_REUSEADDR flag on windows is the equivalent of SO_REUSEPORT on linux\n  # https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse#application-strategies\n  private def system_reuse_address? : Bool\n    true\n  end\n\n  private def system_reuse_address=(val : Bool)\n    raise NotImplementedError.new(\"Crystal::System::Socket#system_reuse_address=\") unless val\n  end\n\n  private def system_reuse_port?\n    getsockopt_bool LibC::SO_REUSEADDR\n  end\n\n  private def system_reuse_port=(val : Bool)\n    if val\n      setsockopt_bool LibC::SO_EXCLUSIVEADDRUSE, false\n      setsockopt_bool LibC::SO_REUSEADDR, true\n    else\n      setsockopt_bool LibC::SO_REUSEADDR, false\n      setsockopt_bool LibC::SO_EXCLUSIVEADDRUSE, true\n    end\n  end\n\n  private def system_broadcast? : Bool\n    getsockopt_bool LibC::SO_BROADCAST\n  end\n\n  private def system_broadcast=(val : Bool)\n    setsockopt_bool LibC::SO_BROADCAST, val\n  end\n\n  private def system_keepalive? : Bool\n    getsockopt_bool LibC::SO_KEEPALIVE\n  end\n\n  private def system_keepalive=(val : Bool)\n    setsockopt_bool LibC::SO_KEEPALIVE, val\n  end\n\n  private def system_linger\n    v = LibC::Linger.new\n    ret = getsockopt LibC::SO_LINGER, v\n    ret.l_onoff == 0 ? nil : ret.l_linger\n  end\n\n  private def system_linger=(val)\n    v = LibC::Linger.new\n    case val\n    when Int\n      v.l_onoff = 1\n      v.l_linger = val\n    when nil\n      v.l_onoff = 0\n    end\n\n    setsockopt LibC::SO_LINGER, v\n    val\n  end\n\n  private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET, &)\n    Socket.getsockopt(fd, optname, optval, level) { |value| yield value }\n  end\n\n  private def system_getsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    Socket.getsockopt(fd, optname, optval, level) { |value| return value }\n    raise ::Socket::Error.from_wsa_error(\"getsockopt #{optname}\")\n  end\n\n  private def system_setsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    Socket.setsockopt(fd, optname, optval, level)\n  end\n\n  protected def self.getsockopt(handle, optname, optval, level = LibC::SOL_SOCKET, &)\n    optsize = sizeof(typeof(optval))\n    ret = LibC.getsockopt(handle, level, optname, pointerof(optval).as(UInt8*), pointerof(optsize))\n    yield optval if ret == 0\n    ret\n  end\n\n  # :nodoc:\n  protected def self.setsockopt(handle, optname, optval, level = LibC::SOL_SOCKET)\n    optsize = sizeof(typeof(optval))\n\n    ret = LibC.setsockopt(handle, level, optname, pointerof(optval).as(UInt8*), optsize)\n    raise ::Socket::Error.from_wsa_error(\"setsockopt #{optname}\") if ret == LibC::SOCKET_ERROR\n    ret\n  end\n\n  @blocking : Bool = true\n\n  # WSA does not provide a direct way to query the blocking mode of a file descriptor.\n  # The best option seems to be just keeping track in an instance variable.\n  # This becomes invalid if the blocking mode was changed directly on the\n  # socket handle without going through `Socket#blocking=`.\n  private def system_blocking?\n    @blocking\n  end\n\n  private def system_blocking=(@blocking)\n    Socket.set_blocking(fd, blocking)\n  end\n\n  def self.get_blocking(fd : Handle)\n    raise NotImplementedError.new(\"Cannot query the blocking mode of a `Socket`\")\n  end\n\n  # Changes the blocking mode as per BSD sockets, has no effect on the\n  # overlapped flag.\n  def self.set_blocking(fd : Handle, value : Bool)\n    mode = value ? 1_u32 : 0_u32\n    ret = LibC.WSAIoctl(fd, LibC::FIONBIO, pointerof(mode), sizeof(UInt32), nil, 0, out _, nil, nil)\n    raise ::Socket::Error.from_wsa_error(\"WSAIoctl\") unless ret.zero?\n  end\n\n  private def system_close_on_exec?\n    false\n  end\n\n  private def system_close_on_exec=(arg : Bool)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_close_on_exec=\" if arg\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    raise NotImplementedError.new \"Crystal::System::Socket.fcntl\"\n  end\n\n  private def system_fcntl(cmd, arg = 0)\n    raise NotImplementedError.new \"Crystal::System::Socket#system_fcntl\"\n  end\n\n  private def system_tty?\n    LibC.GetConsoleMode(LibC::HANDLE.new(fd), out _) != 0\n  end\n\n  private def socket_close(&)\n    handle = @volatile_fd.swap(LibC::INVALID_SOCKET)\n\n    ret = LibC.closesocket(handle)\n\n    if ret != 0\n      case err = WinError.wsa_value\n      when WinError::WSAEINTR, WinError::WSAEINPROGRESS\n        # ignore\n      else\n        yield err\n      end\n    end\n  end\n\n  def socket_close\n    socket_close do |err|\n      raise ::Socket::Error.from_os_error(\"Error closing socket\", err)\n    end\n  end\n\n  private def system_local_address\n    sockaddr6 = uninitialized LibC::SockaddrIn6\n    sockaddr = pointerof(sockaddr6).as(LibC::Sockaddr*)\n    addrlen = sizeof(LibC::SockaddrIn6)\n\n    ret = LibC.getsockname(fd, sockaddr, pointerof(addrlen))\n    if ret == LibC::SOCKET_ERROR\n      raise ::Socket::Error.from_wsa_error(\"getsockname\")\n    end\n\n    ::Socket::IPAddress.from(sockaddr, addrlen)\n  end\n\n  private def system_remote_address\n    sockaddr6 = uninitialized LibC::SockaddrIn6\n    sockaddr = pointerof(sockaddr6).as(LibC::Sockaddr*)\n    addrlen = sizeof(LibC::SockaddrIn6)\n\n    ret = LibC.getpeername(fd, sockaddr, pointerof(addrlen))\n    if ret == LibC::SOCKET_ERROR\n      raise ::Socket::Error.from_wsa_error(\"getpeername\")\n    end\n\n    ::Socket::IPAddress.from(sockaddr, addrlen)\n  end\n\n  private def system_tcp_keepalive_idle\n    getsockopt LibC::TCP_KEEPIDLE, 0, level: ::Socket::Protocol::TCP\n  end\n\n  private def system_tcp_keepalive_idle=(val : Int)\n    setsockopt LibC::TCP_KEEPIDLE, val, level: ::Socket::Protocol::TCP\n    val\n  end\n\n  # The amount of time in seconds between keepalive probes.\n  private def system_tcp_keepalive_interval\n    getsockopt LibC::TCP_KEEPINTVL, 0, level: ::Socket::Protocol::TCP\n  end\n\n  private def system_tcp_keepalive_interval=(val : Int)\n    setsockopt LibC::TCP_KEEPINTVL, val, level: ::Socket::Protocol::TCP\n    val\n  end\n\n  # The number of probes sent, without response before dropping the connection.\n  private def system_tcp_keepalive_count\n    getsockopt LibC::TCP_KEEPCNT, 0, level: ::Socket::Protocol::TCP\n  end\n\n  private def system_tcp_keepalive_count=(val : Int)\n    setsockopt LibC::TCP_KEEPCNT, val, level: ::Socket::Protocol::TCP\n    val\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/thread.cr",
    "content": "require \"c/processthreadsapi\"\nrequire \"c/synchapi\"\nrequire \"../panic\"\n\nmodule Crystal::System::Thread\n  alias Handle = LibC::HANDLE\n\n  def to_unsafe\n    @system_handle\n  end\n\n  private def init_handle\n    @system_handle = GC.beginthreadex(\n      security: Pointer(Void).null,\n      stack_size: LibC::UInt.zero,\n      start_address: ->Thread.thread_proc(Void*),\n      arglist: self.as(Void*),\n      initflag: LibC::UInt.zero,\n      thrdaddr: Pointer(LibC::UInt).null,\n    )\n  end\n\n  def self.init : Nil\n    {% if flag?(:gnu) %}\n      current_key = LibC.TlsAlloc\n      if current_key == LibC::TLS_OUT_OF_INDEXES\n        Crystal::System.panic(\"TlsAlloc()\", WinError.value)\n      end\n      @@current_key = current_key\n    {% end %}\n  end\n\n  def self.thread_proc(data : Void*) : LibC::UInt\n    # ensure that even in the case of stack overflow there is enough reserved\n    # stack space for recovery (for the main thread this is done in\n    # `Exception::CallStack.setup_crash_handler`)\n    stack_size = Crystal::System::Fiber::RESERVED_STACK_SIZE\n    LibC.SetThreadStackGuarantee(pointerof(stack_size))\n\n    data.as(::Thread).start\n    LibC::UInt.zero\n  end\n\n  def self.current_handle : Handle\n    # `GetCurrentThread` returns a _constant_ and is only meaningful as an\n    # argument to Win32 APIs; to uniquely identify it we must duplicate the handle\n    cur_proc = LibC.GetCurrentProcess\n    if LibC.DuplicateHandle(cur_proc, LibC.GetCurrentThread, cur_proc, out handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0\n      raise RuntimeError.from_winerror(\"DuplicateHandle\")\n    end\n    handle\n  end\n\n  def self.yield_current : Nil\n    LibC.SwitchToThread\n  end\n\n  # MinGW does not support TLS correctly\n  {% if flag?(:gnu) %}\n    @@current_key = uninitialized LibC::DWORD\n\n    def self.current_thread : ::Thread\n      th = current_thread?\n      return th if th\n\n      # Thread#start sets `Thread.current` as soon it starts. Thus we know\n      # that if `Thread.current` is not set then we are in the main thread\n      self.current_thread = ::Thread.new\n    end\n\n    def self.current_thread? : ::Thread?\n      ptr = LibC.TlsGetValue(@@current_key)\n      err = WinError.value\n      unless err == WinError::ERROR_SUCCESS\n        Crystal::System.panic(\"TlsGetValue()\", err)\n      end\n\n      ptr.as(::Thread?)\n    end\n\n    def self.current_thread=(thread : ::Thread)\n      if LibC.TlsSetValue(@@current_key, thread.as(Void*)) == 0\n        Crystal::System.panic(\"TlsSetValue()\", WinError.value)\n      end\n      thread\n    end\n  {% else %}\n    @[ThreadLocal]\n    @@current_thread : ::Thread?\n\n    def self.current_thread : ::Thread\n      @@current_thread ||= ::Thread.new\n    end\n\n    def self.current_thread? : ::Thread?\n      @@current_thread\n    end\n\n    def self.current_thread=(@@current_thread : ::Thread)\n    end\n  {% end %}\n\n  def self.sleep(time : ::Time::Span) : Nil\n    LibC.Sleep(time.total_milliseconds.to_i.clamp(1..))\n  end\n\n  private def system_join : Exception?\n    if LibC.WaitForSingleObject(@system_handle, LibC::INFINITE) != LibC::WAIT_OBJECT_0\n      return RuntimeError.from_winerror(\"WaitForSingleObject\")\n    end\n    if LibC.CloseHandle(@system_handle) == 0\n      return RuntimeError.from_winerror(\"CloseHandle\")\n    end\n  end\n\n  private def system_close\n    LibC.CloseHandle(@system_handle)\n  end\n\n  private def stack_address : Void*\n    {% if LibC.has_method?(\"GetCurrentThreadStackLimits\") %}\n      LibC.GetCurrentThreadStackLimits(out low_limit, out high_limit)\n      Pointer(Void).new(low_limit)\n    {% else %}\n      tib = LibC.NtCurrentTeb\n      high_limit = tib.value.stackBase\n      if LibC.VirtualQuery(tib.value.stackLimit, out mbi, sizeof(LibC::MEMORY_BASIC_INFORMATION)) == 0\n        raise RuntimeError.from_winerror(\"VirtualQuery\")\n      end\n      low_limit = mbi.allocationBase\n      low_limit\n    {% end %}\n  end\n\n  private def system_name=(name : String) : String\n    {% if LibC.has_method?(:SetThreadDescription) %}\n      LibC.SetThreadDescription(@system_handle, System.to_wstr(name))\n    {% end %}\n    name\n  end\n\n  def self.init_suspend_resume : Nil\n  end\n\n  private def system_suspend : Nil\n    if LibC.SuspendThread(@system_handle) == -1\n      Crystal::System.panic(\"SuspendThread()\", WinError.value)\n    end\n  end\n\n  private def system_wait_suspended : Nil\n    # context must be aligned on 16 bytes but we lack a mean to force the\n    # alignment on the struct, so we overallocate then realign the pointer:\n    local = uninitialized UInt8[sizeof(Tuple(LibC::CONTEXT, UInt8[15]))]\n    thread_context = local.to_unsafe.align_up(16).as(LibC::CONTEXT*)\n    thread_context.value.contextFlags = LibC::CONTEXT_FULL\n\n    if LibC.GetThreadContext(@system_handle, thread_context) == -1\n      Crystal::System.panic(\"GetThreadContext()\", WinError.value)\n    end\n  end\n\n  private def system_resume : Nil\n    if LibC.ResumeThread(@system_handle) == -1\n      Crystal::System.panic(\"ResumeThread()\", WinError.value)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/thread_condition_variable.cr",
    "content": "require \"c/synchapi\"\n\n# :nodoc:\nclass Thread\n  # :nodoc:\n  class ConditionVariable\n    def initialize\n      @cond = uninitialized LibC::CONDITION_VARIABLE\n      LibC.InitializeConditionVariable(self)\n    end\n\n    def signal : Nil\n      LibC.WakeConditionVariable(self)\n    end\n\n    def broadcast : Nil\n      LibC.WakeAllConditionVariable(self)\n    end\n\n    def wait(mutex : Thread::Mutex) : Nil\n      ret = LibC.SleepConditionVariableCS(self, mutex, LibC::INFINITE)\n      raise RuntimeError.from_winerror(\"SleepConditionVariableCS\") if ret == 0\n    end\n\n    def wait(mutex : Thread::Mutex, time : Time::Span, & : ->)\n      ret = LibC.SleepConditionVariableCS(self, mutex, time.total_milliseconds)\n      return if ret != 0\n\n      error = WinError.value\n      if error == WinError::ERROR_TIMEOUT\n        yield\n      else\n        raise RuntimeError.from_os_error(\"SleepConditionVariableCS\", error)\n      end\n    end\n\n    def to_unsafe\n      pointerof(@cond)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/thread_mutex.cr",
    "content": "require \"c/synchapi\"\n\n# :nodoc:\nclass Thread\n  # :nodoc:\n  # for Win32 condition variable interop we must use either a critical section\n  # or a slim reader/writer lock, not a Win32 mutex\n  # also note critical sections are reentrant; to match the behaviour in\n  # `../unix/pthread_mutex.cr` we must do extra housekeeping ourselves\n  class Mutex\n    def initialize\n      @cs = uninitialized LibC::CRITICAL_SECTION\n      LibC.InitializeCriticalSectionAndSpinCount(self, 1000)\n    end\n\n    def lock : Nil\n      LibC.EnterCriticalSection(self)\n      if @cs.recursionCount > 1\n        LibC.LeaveCriticalSection(self)\n        raise RuntimeError.new \"Attempt to lock a mutex recursively (deadlock)\"\n      end\n    end\n\n    def try_lock : Bool\n      if LibC.TryEnterCriticalSection(self) != 0\n        if @cs.recursionCount > 1\n          LibC.LeaveCriticalSection(self)\n          false\n        else\n          true\n        end\n      else\n        false\n      end\n    end\n\n    def unlock : Nil\n      # `owningThread` is declared as `LibC::HANDLE` for historical reasons, so\n      # the following comparison is correct\n      unless @cs.owningThread == LibC::HANDLE.new(LibC.GetCurrentThreadId.to_u64!)\n        raise RuntimeError.new \"Attempt to unlock a mutex locked by another thread\"\n      end\n      LibC.LeaveCriticalSection(self)\n    end\n\n    def synchronize(&)\n      lock\n      yield self\n    ensure\n      unlock\n    end\n\n    def finalize\n      LibC.DeleteCriticalSection(self)\n    end\n\n    def to_unsafe\n      pointerof(@cs)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/time.cr",
    "content": "require \"c/winbase\"\nrequire \"c/timezoneapi\"\nrequire \"c/windows\"\nrequire \"./zone_names\"\nrequire \"./windows_registry\"\n\nmodule Crystal::System::Time\n  # Win32 epoch is 1601-01-01 00:00:00 UTC\n  WINDOWS_EPOCH_IN_SECONDS = 50_491_123_200_i64\n\n  # Resolution of FILETIME is 100 nanoseconds\n  NANOSECONDS_PER_FILETIME_TICK = 100\n\n  NANOSECONDS_PER_SECOND    = 1_000_000_000\n  FILETIME_TICKS_PER_SECOND = NANOSECONDS_PER_SECOND // NANOSECONDS_PER_FILETIME_TICK\n\n  BIAS_TO_OFFSET_FACTOR = -60\n\n  def self.compute_utc_seconds_and_nanoseconds : {Int64, Int32}\n    {% if LibC.has_method?(\"GetSystemTimePreciseAsFileTime\") %}\n      LibC.GetSystemTimePreciseAsFileTime(out filetime)\n      filetime_to_seconds_and_nanoseconds(filetime)\n    {% else %}\n      LibC.GetSystemTimeAsFileTime(out filetime)\n      filetime_to_seconds_and_nanoseconds(filetime)\n    {% end %}\n  end\n\n  def self.filetime_to_seconds_and_nanoseconds(filetime) : {Int64, Int32}\n    since_epoch = (filetime.dwHighDateTime.to_u64 << 32) | filetime.dwLowDateTime.to_u64\n\n    seconds = (since_epoch / FILETIME_TICKS_PER_SECOND).to_i64 + WINDOWS_EPOCH_IN_SECONDS\n    nanoseconds = since_epoch.remainder(FILETIME_TICKS_PER_SECOND).to_i32 * NANOSECONDS_PER_FILETIME_TICK\n\n    {seconds, nanoseconds}\n  end\n\n  def self.from_filetime(filetime) : ::Time\n    seconds, nanoseconds = filetime_to_seconds_and_nanoseconds(filetime)\n    ::Time.utc(seconds: seconds, nanoseconds: nanoseconds)\n  end\n\n  def self.to_filetime(time : ::Time) : LibC::FILETIME\n    span = time - ::Time.utc(seconds: WINDOWS_EPOCH_IN_SECONDS, nanoseconds: 0)\n    ticks = span.to_i.to_u64 * FILETIME_TICKS_PER_SECOND + span.nanoseconds // NANOSECONDS_PER_FILETIME_TICK\n    filetime = uninitialized LibC::FILETIME\n    filetime.dwHighDateTime = (ticks >> 32).to_u32\n    filetime.dwLowDateTime = ticks.to_u32!\n    filetime\n  end\n\n  def self.filetime_to_f64secs(filetime) : Float64\n    ((filetime.dwHighDateTime.to_u64 << 32) | filetime.dwLowDateTime.to_u64).to_f64 / FILETIME_TICKS_PER_SECOND.to_f64\n  end\n\n  private def self.performance_frequency\n    LibC.QueryPerformanceFrequency(out frequency)\n    frequency\n  end\n\n  def self.monotonic : {Int64, Int32}\n    LibC.QueryPerformanceCounter(out ticks)\n    frequency = performance_frequency\n    divmod = ticks.tdivmod(frequency)\n    {divmod[0], (divmod[1] &* NANOSECONDS_PER_SECOND // frequency).to_i32!}\n  end\n\n  def self.ticks : UInt64\n    LibC.QueryPerformanceCounter(out ticks)\n    ticks.to_u64! &* (NANOSECONDS_PER_SECOND // performance_frequency)\n  end\n\n  def self.load_localtime : ::Time::Location?\n    if LibC.GetDynamicTimeZoneInformation(out info) != LibC::TIME_ZONE_ID_INVALID\n      windows_name = String.from_utf16(info.timeZoneKeyName.to_slice, truncate_at_null: true)\n\n      return unless canonical_iana_name = windows_to_iana[windows_name]?\n      return unless windows_info = iana_to_windows[canonical_iana_name]?\n      _, stdname, dstname = windows_info\n\n      # In Crystal, we use only `UTC` as name for the UTC time zone\n      canonical_iana_name = \"UTC\" if canonical_iana_name == \"Etc/UTC\"\n\n      initialize_location_from_TZI(pointerof(info).as(LibC::TIME_ZONE_INFORMATION*).value, canonical_iana_name, windows_name, stdname, dstname)\n    end\n  end\n\n  def self.zone_sources : Enumerable(String)\n    [] of String\n  end\n\n  # https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information#remarks\n  @[Extern]\n  private record REG_TZI_FORMAT,\n    bias : LibC::LONG,\n    standardBias : LibC::LONG,\n    daylightBias : LibC::LONG,\n    standardDate : LibC::SYSTEMTIME,\n    daylightDate : LibC::SYSTEMTIME\n\n  def self.load_iana_zone(iana_name : String) : ::Time::Location?\n    return unless windows_info = iana_to_windows[iana_name]?\n    windows_name, stdname, dstname = windows_info\n\n    WindowsRegistry.open?(LibC::HKEY_LOCAL_MACHINE, REGISTRY_TIME_ZONES) do |key_handle|\n      WindowsRegistry.open?(key_handle, windows_name.to_utf16) do |sub_handle|\n        reg_tzi = uninitialized REG_TZI_FORMAT\n        WindowsRegistry.get_raw(sub_handle, TZI, Slice.new(pointerof(reg_tzi), 1).to_unsafe_bytes)\n\n        tzi = LibC::TIME_ZONE_INFORMATION.new(\n          bias: reg_tzi.bias,\n          standardDate: reg_tzi.standardDate,\n          standardBias: reg_tzi.standardBias,\n          daylightDate: reg_tzi.daylightDate,\n          daylightBias: reg_tzi.daylightBias,\n        )\n        WindowsRegistry.get_raw(sub_handle, Std, tzi.standardName.to_slice.to_unsafe_bytes)\n        WindowsRegistry.get_raw(sub_handle, Dlt, tzi.daylightName.to_slice.to_unsafe_bytes)\n        initialize_location_from_TZI(tzi, iana_name, windows_name, stdname, dstname)\n      end\n    end\n  end\n\n  private def self.initialize_location_from_TZI(info, name, windows_name, stdname, dstname)\n    if info.standardDate.wMonth == 0_u16 || info.daylightDate.wMonth == 0_u16\n      # No DST\n      zone = ::Time::Location::Zone.new(stdname, info.bias * BIAS_TO_OFFSET_FACTOR, false)\n      default_tz_args = {0, 0, ::Time::TZ::MonthWeekDay.default, ::Time::TZ::MonthWeekDay.default}\n      return ::Time::WindowsLocation.new(name, [zone], windows_name, default_tz_args)\n    end\n\n    zones = [\n      ::Time::Location::Zone.new(stdname, (info.bias + info.standardBias) * BIAS_TO_OFFSET_FACTOR, false),\n      ::Time::Location::Zone.new(dstname, (info.bias + info.daylightBias) * BIAS_TO_OFFSET_FACTOR, true),\n    ]\n\n    std_index = 0\n    dst_index = 1\n    transition1 = systemtime_to_mwd(info.daylightDate)\n    transition2 = systemtime_to_mwd(info.standardDate)\n    tz_args = {std_index, dst_index, transition1, transition2}\n\n    ::Time::WindowsLocation.new(name, zones, windows_name, tz_args)\n  end\n\n  private def self.systemtime_to_mwd(time)\n    seconds = 3600 * time.wHour + 60 * time.wMinute + time.wSecond\n    ::Time::TZ::MonthWeekDay.new(time.wMonth.to_i8, time.wDay.to_i8, time.wDayOfWeek.to_i8, seconds)\n  end\n\n  REGISTRY_TIME_ZONES = System.wstr_literal %q(SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones)\n  Std                 = System.wstr_literal \"Std\"\n  Dlt                 = System.wstr_literal \"Dlt\"\n  TZI                 = System.wstr_literal \"TZI\"\nend\n"
  },
  {
    "path": "src/crystal/system/win32/user.cr",
    "content": "require \"crystal/system/windows\"\nrequire \"c/lm\"\nrequire \"c/userenv\"\nrequire \"c/security\"\n\n# This file contains source code derived from the following:\n#\n# * https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/os/user/lookup_windows.go\n# * https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/syscall/security_windows.go\n#\n# The following is their license:\n#\n# Copyright 2009 The Go Authors.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are\n# met:\n#\n#    * Redistributions of source code must retain the above copyright\n# notice, this list of conditions and the following disclaimer.\n#    * Redistributions in binary form must reproduce the above\n# copyright notice, this list of conditions and the following disclaimer\n# in the documentation and/or other materials provided with the\n# distribution.\n#    * Neither the name of Google LLC nor the names of its\n# contributors may be used to endorse or promote products derived from\n# this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nmodule Crystal::System::User\n  def initialize(@username : String, @id : String, @group_id : String, @name : String, @home_directory : String)\n  end\n\n  def system_username\n    @username\n  end\n\n  def system_id\n    @id\n  end\n\n  def system_group_id\n    @group_id\n  end\n\n  def system_name\n    @name\n  end\n\n  def system_home_directory\n    @home_directory\n  end\n\n  def system_shell\n    Crystal::System::User.cmd_path\n  end\n\n  class_getter(cmd_path : String) do\n    \"#{Crystal::System::Path.known_folder_path(LibC::FOLDERID_System)}\\\\cmd.exe\"\n  end\n\n  def self.from_username?(username : String) : ::System::User?\n    if found = Crystal::System.name_to_sid(username)\n      if found.type.sid_type_user?\n        from_sid(found.sid)\n      end\n    end\n  end\n\n  def self.from_id?(id : String) : ::System::User?\n    if sid = Crystal::System.sid_from_s(id)\n      begin\n        from_sid(sid)\n      ensure\n        LibC.LocalFree(sid)\n      end\n    end\n  end\n\n  private def self.from_sid(sid : LibC::SID*) : ::System::User?\n    canonical = Crystal::System.sid_to_name(sid) || return\n    return unless canonical.type.sid_type_user?\n\n    domain_and_user = \"#{canonical.domain}\\\\#{canonical.name}\"\n    full_name = lookup_full_name(canonical.name, canonical.domain, domain_and_user) || return\n    pgid = lookup_primary_group_id(canonical.name, canonical.domain) || return\n    uid = Crystal::System.sid_to_s(sid)\n    home_dir = lookup_home_directory(uid, canonical.name) || return\n\n    ::System::User.new(domain_and_user, uid, pgid, full_name, home_dir)\n  end\n\n  private def self.lookup_full_name(name : String, domain : String, domain_and_user : String) : String?\n    if domain_joined?\n      domain_and_user = Crystal::System.to_wstr(domain_and_user)\n      Crystal::System.retry_wstr_buffer do |buffer, small_buf|\n        len = LibC::ULong.new(buffer.size)\n        if LibC.TranslateNameW(domain_and_user, LibC::EXTENDED_NAME_FORMAT::NameSamCompatible, LibC::EXTENDED_NAME_FORMAT::NameDisplay, buffer, pointerof(len)) != 0\n          return String.from_utf16(buffer[0, len - 1])\n        elsif small_buf && len > 0\n          next len\n        else\n          break\n        end\n      end\n    end\n\n    info = uninitialized LibC::USER_INFO_10*\n    if LibC.NetUserGetInfo(Crystal::System.to_wstr(domain), Crystal::System.to_wstr(name), 10, pointerof(info).as(LibC::BYTE**)) == LibC::NERR_Success\n      begin\n        str, _ = String.from_utf16(info.value.usri10_full_name)\n        return str\n      ensure\n        LibC.NetApiBufferFree(info)\n      end\n    end\n\n    # domain worked neither as a domain nor as a server\n    # could be domain server unavailable\n    # pretend username is fullname\n    name\n  end\n\n  # obtains the primary group SID for a user using this method:\n  # https://support.microsoft.com/en-us/help/297951/how-to-use-the-primarygroupid-attribute-to-find-the-primary-group-for\n  # The method follows this formula: domainRID + \"-\" + primaryGroupRID\n  private def self.lookup_primary_group_id(name : String, domain : String) : String?\n    domain_sid = Crystal::System.name_to_sid(domain) || return\n    return unless domain_sid.type.sid_type_domain?\n\n    domain_sid_str = Crystal::System.sid_to_s(domain_sid.sid)\n\n    # If the user has joined a domain use the RID of the default primary group\n    # called \"Domain Users\":\n    # https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems\n    # SID: S-1-5-21domain-513\n    #\n    # The correct way to obtain the primary group of a domain user is\n    # probing the user primaryGroupID attribute in the server Active Directory:\n    # https://learn.microsoft.com/en-us/windows/win32/adschema/a-primarygroupid\n    #\n    # Note that the primary group of domain users should not be modified\n    # on Windows for performance reasons, even if it's possible to do that.\n    # The .NET Developer's Guide to Directory Services Programming - Page 409\n    # https://books.google.bg/books?id=kGApqjobEfsC&lpg=PA410&ots=p7oo-eOQL7&dq=primary%20group%20RID&hl=bg&pg=PA409#v=onepage&q&f=false\n    return \"#{domain_sid_str}-513\" if domain_joined?\n\n    # For non-domain users call NetUserGetInfo() with level 4, which\n    # in this case would not have any network overhead.\n    # The primary group should not change from RID 513 here either\n    # but the group will be called \"None\" instead:\n    # https://www.adampalmer.me/iodigitalsec/2013/08/10/windows-null-session-enumeration/\n    # \"Group 'None' (RID: 513)\"\n    info = uninitialized LibC::USER_INFO_4*\n    if LibC.NetUserGetInfo(Crystal::System.to_wstr(domain), Crystal::System.to_wstr(name), 4, pointerof(info).as(LibC::BYTE**)) == LibC::NERR_Success\n      begin\n        \"#{domain_sid_str}-#{info.value.usri4_primary_group_id}\"\n      ensure\n        LibC.NetApiBufferFree(info)\n      end\n    end\n  end\n\n  private REGISTRY_PROFILE_LIST = System.wstr_literal %q(SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList)\n  private ProfileImagePath      = System.wstr_literal \"ProfileImagePath\"\n\n  private def self.lookup_home_directory(uid : String, username : String) : String?\n    # If this user has logged in at least once their home path should be stored\n    # in the registry under the specified SID. References:\n    # https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx\n    # https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles\n    #\n    # The registry is the most reliable way to find the home path as the user\n    # might have decided to move it outside of the default location,\n    # (e.g. C:\\users). Reference:\n    # https://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-do-i-set-a-home-directory-outside-cusers-for-a/aed68262-1bf4-4a4d-93dc-7495193a440f\n    reg_home_dir = WindowsRegistry.open?(LibC::HKEY_LOCAL_MACHINE, REGISTRY_PROFILE_LIST) do |key_handle|\n      WindowsRegistry.open?(key_handle, uid.to_utf16) do |sub_handle|\n        WindowsRegistry.get_string(sub_handle, ProfileImagePath)\n      end\n    end\n    return reg_home_dir if reg_home_dir\n\n    # If the home path does not exist in the registry, the user might\n    # have not logged in yet; fall back to using getProfilesDirectory().\n    # Find the username based on a SID and append that to the result of\n    # getProfilesDirectory(). The domain is not relevant here.\n    # NOTE: the user has not logged in so this directory might not exist\n    profile_dir = Crystal::System.retry_wstr_buffer do |buffer, small_buf|\n      len = LibC::DWORD.new(buffer.size)\n      if LibC.GetProfilesDirectoryW(buffer, pointerof(len)) != 0\n        break String.from_utf16(buffer[0, len - 1])\n      elsif small_buf && len > 0\n        next len\n      else\n        break nil\n      end\n    end\n    return \"#{profile_dir}\\\\#{username}\" if profile_dir\n  end\n\n  private def self.domain_joined? : Bool\n    status = LibC.NetGetJoinInformation(nil, out domain, out type)\n    if status != LibC::NERR_Success\n      raise RuntimeError.from_os_error(\"NetGetJoinInformation\", WinError.new(status))\n    end\n    is_domain = type.net_setup_domain_name?\n    LibC.NetApiBufferFree(domain)\n    is_domain\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/visual_studio.cr",
    "content": "require \"json\"\n\nmodule Crystal::System::VisualStudio\n  struct Installation\n    include JSON::Serializable\n\n    @[JSON::Field(key: \"installationPath\")]\n    getter directory : String\n\n    @[JSON::Field(key: \"installationVersion\")]\n    getter version : String\n\n    # unused fields not mapped\n  end\n\n  @@found_msvc = false\n\n  @@msvc_path : ::Path?\n\n  def self.find_latest_msvc_path : ::Path?\n    if !@@found_msvc\n      @@found_msvc = true\n      @@msvc_path = find_latest_msvc_path_impl\n    end\n\n    @@msvc_path\n  end\n\n  private def self.find_latest_msvc_path_impl\n    # ported from https://github.com/microsoft/vswhere/wiki/Find-VC\n    # Copyright (C) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n    if vs_installations = get_vs_installations\n      vs_installations.each do |installation|\n        version_path = ::File.join(installation.directory, \"VC\", \"Auxiliary\", \"Build\", \"Microsoft.VCToolsVersion.default.txt\")\n        next unless ::File.file?(version_path)\n\n        version = ::File.read(version_path).chomp\n        next if version.empty?\n\n        return ::Path.new(installation.directory, \"VC\", \"Tools\", \"MSVC\", version)\n      end\n    end\n  end\n\n  private def self.get_vs_installations : Array(Installation)?\n    if vswhere_path = find_vswhere\n      vc_install_json = `#{::Process.quote(vswhere_path)} -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -products * -sort -format json`.chomp\n      return if !$?.success? || vc_install_json.empty?\n\n      Array(Installation).from_json(vc_install_json)\n    end\n  end\n\n  private def self.find_vswhere\n    # standard path for VS2017 15.2 and later\n    if program_files = ENV[\"ProgramFiles(x86)\"]?\n      vswhere_path = ::File.join(program_files, \"Microsoft Visual Studio\", \"Installer\", \"vswhere.exe\")\n      return vswhere_path if ::File.file?(vswhere_path)\n    end\n\n    ::Process.find_executable(\"vswhere.exe\")\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/waitable_timer.cr",
    "content": "require \"c/ntdll\"\nrequire \"c/synchapi\"\nrequire \"c/winternl\"\n\nclass Crystal::System::WaitableTimer\n  getter handle : LibC::HANDLE\n\n  def initialize\n    flags = LibC::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION\n    desired_access = LibC::SYNCHRONIZE | LibC::TIMER_QUERY_STATE | LibC::TIMER_MODIFY_STATE\n    @handle = LibC.CreateWaitableTimerExW(nil, nil, flags, desired_access)\n    raise RuntimeError.from_winerror(\"CreateWaitableTimerExW\") if @handle.null?\n  end\n\n  def set(time : ::Time::Instant) : Nil\n    # convert absolute time to relative time, expressed in 100ns interval,\n    # rounded up; cannot use `::Time.instant` that could be mocked\n    relative = time.duration_since(Crystal::System::Time.instant)\n    ticks = (relative.to_i * 10_000_000 + (relative.nanoseconds + 99) // 100)\n\n    # negative duration means relative time (positive would mean absolute\n    # realtime clock)\n    duration = -ticks\n\n    ret = LibC.SetWaitableTimer(@handle, pointerof(duration), 0, nil, nil, 0)\n    raise RuntimeError.from_winerror(\"SetWaitableTimer\") if ret == 0\n  end\n\n  def cancel : Nil\n    ret = LibC.CancelWaitableTimer(@handle)\n    raise RuntimeError.from_winerror(\"CancelWaitableTimer\") if ret == 0\n  end\n\n  def close : Nil\n    LibC.CloseHandle(@handle)\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/windows_registry.cr",
    "content": "require \"c/winreg\"\nrequire \"c/regapix\"\n\nmodule Crystal::System::WindowsRegistry\n  # Opens a subkey at path *name* and returns the new key handle or `nil` if it\n  # does not exist.\n  #\n  # Users need to ensure the opened key will be closed after usage (see `#close`).\n  # *sam* specifies desired access rights to the key to be opened.\n  def self.open?(handle : LibC::HKEY, name : Slice(UInt16), sam = LibC::REGSAM::READ)\n    status = LibC.RegOpenKeyExW(handle, name, 0, sam, out sub_handle)\n    status = WinError.new(status)\n\n    case status\n    when .error_success?\n      sub_handle\n    when .error_file_not_found?\n      # key does not exist\n      nil\n    else\n      raise RuntimeError.from_os_error(\"RegOpenKeyExW\", status)\n    end\n  end\n\n  def self.open?(handle : LibC::HKEY, name : Slice(UInt16), sam = LibC::REGSAM::READ, &)\n    key_handle = open?(handle, name, sam)\n\n    return unless key_handle\n\n    begin\n      yield key_handle\n    ensure\n      close key_handle\n    end\n  end\n\n  # Closes the handle.\n  def self.close(handle : LibC::HKEY) : Nil\n    status = LibC.RegCloseKey(handle)\n    status = WinError.new(status)\n\n    unless status.error_success? || status.error_invalid_handle?\n      raise RuntimeError.from_os_error(\"RegCloseKey\", status)\n    end\n  end\n\n  # Iterates all value names in this key and yields them to the block.\n  def self.each_name(handle : LibC::HKEY, &block : Slice(UInt16) -> Nil) : Nil\n    status = LibC.RegQueryInfoKeyW(handle, nil, nil, nil, out sub_key_count, out max_sub_key_length, nil, nil, nil, nil, nil, nil)\n    status = WinError.new(status)\n\n    return unless status.error_success?\n\n    buffer = Slice(UInt16).new(max_sub_key_length + 1)\n\n    sub_key_count.times do |i|\n      length = buffer.size.to_u32\n      status = LibC.RegEnumKeyExW(handle, i, buffer, pointerof(length), nil, nil, nil, nil)\n      status = WinError.new(status)\n\n      case status\n      when .error_success?\n        yield buffer[0, length]\n      when .error_no_more_items?\n        break\n      else\n        raise RuntimeError.from_os_error(\"RegEnumValueW\", status)\n      end\n    end\n  end\n\n  # Reads a raw value into a buffer and creates a string from it.\n  def self.get_string(handle : LibC::HKEY, name : Slice(UInt16))\n    Crystal::System.retry_wstr_buffer do |buffer, small_buf|\n      raw = get_raw(handle, name, buffer.to_unsafe_bytes) || return\n      _, length = raw\n\n      if 0 <= length <= buffer.size\n        return String.from_utf16(buffer[0, length // 2 - 1])\n      elsif small_buf && length > 0\n        next length\n      else\n        raise RuntimeError.new(\"RegQueryValueExW retry buffer\")\n      end\n    end\n  end\n\n  # Reads a raw value into a buffer.\n  def self.get_raw(handle : LibC::HKEY, name : Slice(UInt16), buffer : Slice(UInt8))\n    length = buffer.size.to_u32\n    status = LibC.RegQueryValueExW(handle, name, nil, out valtype, buffer, pointerof(length))\n    status = WinError.new(status)\n    case status\n    when .error_success?\n      {valtype, length}\n    when .error_file_not_found?\n      nil\n    when .error_more_data?\n      {valtype, length}\n    else\n      raise RuntimeError.from_os_error(\"RegQueryValueExW\", status)\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/windows_sdk.cr",
    "content": "require \"./windows_registry\"\n\nmodule Crystal::System::WindowsSDK\n  REGISTRY_WIN10_SDK_64 = System.wstr_literal %q(SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0)\n  REGISTRY_WIN10_SDK_32 = System.wstr_literal %q(SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0)\n\n  InstallationFolder = System.wstr_literal \"InstallationFolder\"\n  ProductVersion     = System.wstr_literal \"ProductVersion\"\n\n  def self.find_win10_sdk_libpath : ::Path?\n    # ported from Common7\\Tools\\vsdevcmd\\core\\winsdk.bat (loaded by the MSVC\n    # developer command prompt)\n    {REGISTRY_WIN10_SDK_64, REGISTRY_WIN10_SDK_32}.each do |name|\n      {LibC::HKEY_LOCAL_MACHINE, LibC::HKEY_CURRENT_USER}.each do |hkey|\n        WindowsRegistry.open?(hkey, name) do |handle|\n          installation_folder = WindowsRegistry.get_string(handle, InstallationFolder)\n          product_version = WindowsRegistry.get_string(handle, ProductVersion)\n          next unless installation_folder && product_version\n          product_version = \"#{product_version}.0\"\n\n          if ::File.file?(::File.join(installation_folder, \"Include\", product_version, \"um\", \"winsdkver.h\"))\n            return ::Path.new(installation_folder, \"Lib\", product_version)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/win32/wmain.cr",
    "content": "require \"c/stringapiset\"\nrequire \"c/winnls\"\nrequire \"c/stdlib\"\n\n# we have both `main` and `wmain`, so we must choose an unambiguous entry point\n{% if flag?(:msvc) %}\n  @[Link({{ flag?(:static) ? \"libcmt\" : \"msvcrt\" }})]\n  @[Link(ldflags: \"/ENTRY:wmainCRTStartup\")]\n{% elsif flag?(:gnu) && !flag?(:interpreted) %}\n  @[Link(ldflags: \"-municode\")]\n{% end %}\nlib LibCrystalMain\nend\n\n# The actual entry point for Windows executables.\n#\n# This is necessary because *argv* (and Win32's `GetCommandLineA`) mistranslate\n# non-ASCII characters to Windows-1252, so `PROGRAM_NAME` and `ARGV` would be\n# garbled; to avoid that, we use this Windows-exclusive entry point which\n# contains the correctly encoded UTF-16 *argv*, convert it to UTF-8, and then\n# forward it to the original `main`.\n#\n# NOTE: we cannot use anything from the standard library here, including the GC.\nfun wmain(argc : Int32, argv : UInt16**) : Int32\n  utf8_argv = LibC.malloc(sizeof(UInt8*) &* argc).as(UInt8**)\n  i = 0_i64\n  while i < argc\n    arg = (argv + i).value\n    utf8_size = LibC.WideCharToMultiByte(LibC::CP_UTF8, 0, arg, -1, nil, 0, nil, nil)\n    utf8_arg = LibC.malloc(utf8_size).as(UInt8*)\n    LibC.WideCharToMultiByte(LibC::CP_UTF8, 0, arg, -1, utf8_arg, utf8_size, nil, nil)\n    (utf8_argv + i).value = utf8_arg\n    i &+= 1\n  end\n\n  status = main(argc, utf8_argv)\n\n  i = 0_i64\n  while i < argc\n    LibC.free((utf8_argv + i).value)\n    i &+= 1\n  end\n  LibC.free(utf8_argv)\n\n  # prefer explicit exit over returning the status, so we are free to resume the\n  # main thread's fiber on any thread, without occurring a weird behavior where\n  # another thread returns from main when the caller might expect the main\n  # thread to be the one returning.\n  LibC.exit(status)\nend\n"
  },
  {
    "path": "src/crystal/system/win32/zone_names.cr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_windows_zone_names.cr\n#\n# DO NOT EDIT\n\nmodule Crystal::System::Time\n  # These mappings from IANA to Windows time zone names and tzdata abbreviations\n  # are based on\n  # https://raw.githubusercontent.com/unicode-org/cldr/f8369ba0795c79f3bac8eb89967eea359f77835e/common/supplemental/windowsZones.xml\n  private class_getter iana_to_windows : Hash(String, {String, String, String}) do\n    data = Hash(String, {String, String, String}).new(initial_capacity: 445)\n    put(data, \"Africa/Abidjan\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Accra\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Addis_Ababa\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Algiers\", \"W. Central Africa Standard Time\", \"CET\", \"CET\")\n    put(data, \"Africa/Asmera\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Bamako\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Bangui\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Banjul\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Bissau\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Blantyre\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Brazzaville\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Bujumbura\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Cairo\", \"Egypt Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Africa/Casablanca\", \"Morocco Standard Time\", \"+01\", \"+01\")\n    put(data, \"Africa/Ceuta\", \"Romance Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Africa/Conakry\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Dakar\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Dar_es_Salaam\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Djibouti\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Douala\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/El_Aaiun\", \"Morocco Standard Time\", \"+01\", \"+01\")\n    put(data, \"Africa/Freetown\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Gaborone\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Harare\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Johannesburg\", \"South Africa Standard Time\", \"SAST\", \"SAST\")\n    put(data, \"Africa/Juba\", \"South Sudan Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Kampala\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Khartoum\", \"Sudan Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Kigali\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Kinshasa\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Lagos\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Libreville\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Lome\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Luanda\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Lubumbashi\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Lusaka\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Malabo\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Maputo\", \"South Africa Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"Africa/Maseru\", \"South Africa Standard Time\", \"SAST\", \"SAST\")\n    put(data, \"Africa/Mbabane\", \"South Africa Standard Time\", \"SAST\", \"SAST\")\n    put(data, \"Africa/Mogadishu\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Monrovia\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Nairobi\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Africa/Ndjamena\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Niamey\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Nouakchott\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Ouagadougou\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Porto-Novo\", \"W. Central Africa Standard Time\", \"WAT\", \"WAT\")\n    put(data, \"Africa/Sao_Tome\", \"Sao Tome Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Africa/Tripoli\", \"Libya Standard Time\", \"EET\", \"EET\")\n    put(data, \"Africa/Tunis\", \"W. Central Africa Standard Time\", \"CET\", \"CET\")\n    put(data, \"Africa/Windhoek\", \"Namibia Standard Time\", \"CAT\", \"CAT\")\n    put(data, \"America/Adak\", \"Aleutian Standard Time\", \"HST\", \"HDT\")\n    put(data, \"America/Anchorage\", \"Alaskan Standard Time\", \"AKST\", \"AKDT\")\n    put(data, \"America/Anguilla\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Antigua\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Araguaina\", \"Tocantins Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/La_Rioja\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/Rio_Gallegos\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/Salta\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/San_Juan\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/San_Luis\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/Tucuman\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Argentina/Ushuaia\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Aruba\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Asuncion\", \"Paraguay Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Bahia\", \"Bahia Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Bahia_Banderas\", \"Central Standard Time (Mexico)\", \"CST\", \"CST\")\n    put(data, \"America/Barbados\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Belem\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Belize\", \"Central America Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Blanc-Sablon\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Boa_Vista\", \"SA Western Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Bogota\", \"SA Pacific Standard Time\", \"-05\", \"-05\")\n    put(data, \"America/Boise\", \"Mountain Standard Time\", \"MST\", \"MDT\")\n    put(data, \"America/Buenos_Aires\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Cambridge_Bay\", \"Mountain Standard Time\", \"MST\", \"MDT\")\n    put(data, \"America/Campo_Grande\", \"Central Brazilian Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Cancun\", \"Eastern Standard Time (Mexico)\", \"EST\", \"EST\")\n    put(data, \"America/Caracas\", \"Venezuela Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Catamarca\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Cayenne\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Cayman\", \"SA Pacific Standard Time\", \"EST\", \"EST\")\n    put(data, \"America/Chicago\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Chihuahua\", \"Central Standard Time (Mexico)\", \"CST\", \"CST\")\n    put(data, \"America/Ciudad_Juarez\", \"Mountain Standard Time\", \"MST\", \"MDT\")\n    put(data, \"America/Coral_Harbour\", \"SA Pacific Standard Time\", \"EST\", \"EST\")\n    put(data, \"America/Cordoba\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Costa_Rica\", \"Central America Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Coyhaique\", \"Magallanes Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Creston\", \"US Mountain Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Cuiaba\", \"Central Brazilian Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Curacao\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Danmarkshavn\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"America/Dawson\", \"Yukon Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Dawson_Creek\", \"US Mountain Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Denver\", \"Mountain Standard Time\", \"MST\", \"MDT\")\n    put(data, \"America/Detroit\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Dominica\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Edmonton\", \"Mountain Standard Time\", \"MST\", \"MDT\")\n    put(data, \"America/Eirunepe\", \"SA Pacific Standard Time\", \"-05\", \"-05\")\n    put(data, \"America/El_Salvador\", \"Central America Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Fort_Nelson\", \"US Mountain Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Fortaleza\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Glace_Bay\", \"Atlantic Standard Time\", \"AST\", \"ADT\")\n    put(data, \"America/Godthab\", \"Greenland Standard Time\", \"-02\", \"-01\")\n    put(data, \"America/Goose_Bay\", \"Atlantic Standard Time\", \"AST\", \"ADT\")\n    put(data, \"America/Grand_Turk\", \"Turks And Caicos Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Grenada\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Guadeloupe\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Guatemala\", \"Central America Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Guayaquil\", \"SA Pacific Standard Time\", \"-05\", \"-05\")\n    put(data, \"America/Guyana\", \"SA Western Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Halifax\", \"Atlantic Standard Time\", \"AST\", \"ADT\")\n    put(data, \"America/Havana\", \"Cuba Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Hermosillo\", \"US Mountain Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Indiana/Knox\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Indiana/Marengo\", \"US Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Indiana/Petersburg\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Indiana/Tell_City\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Indiana/Vevay\", \"US Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Indiana/Vincennes\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Indiana/Winamac\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Indianapolis\", \"US Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Inuvik\", \"Mountain Standard Time\", \"MST\", \"MDT\")\n    put(data, \"America/Iqaluit\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Jamaica\", \"SA Pacific Standard Time\", \"EST\", \"EST\")\n    put(data, \"America/Jujuy\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Juneau\", \"Alaskan Standard Time\", \"AKST\", \"AKDT\")\n    put(data, \"America/Kentucky/Monticello\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Kralendijk\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/La_Paz\", \"SA Western Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Lima\", \"SA Pacific Standard Time\", \"-05\", \"-05\")\n    put(data, \"America/Los_Angeles\", \"Pacific Standard Time\", \"PST\", \"PDT\")\n    put(data, \"America/Louisville\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Lower_Princes\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Maceio\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Managua\", \"Central America Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Manaus\", \"SA Western Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Marigot\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Martinique\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Matamoros\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Mazatlan\", \"Mountain Standard Time (Mexico)\", \"MST\", \"MST\")\n    put(data, \"America/Mendoza\", \"Argentina Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Menominee\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Merida\", \"Central Standard Time (Mexico)\", \"CST\", \"CST\")\n    put(data, \"America/Metlakatla\", \"Alaskan Standard Time\", \"AKST\", \"AKDT\")\n    put(data, \"America/Mexico_City\", \"Central Standard Time (Mexico)\", \"CST\", \"CST\")\n    put(data, \"America/Miquelon\", \"Saint Pierre Standard Time\", \"-03\", \"-02\")\n    put(data, \"America/Moncton\", \"Atlantic Standard Time\", \"AST\", \"ADT\")\n    put(data, \"America/Monterrey\", \"Central Standard Time (Mexico)\", \"CST\", \"CST\")\n    put(data, \"America/Montevideo\", \"Montevideo Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Montserrat\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Nassau\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/New_York\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Nome\", \"Alaskan Standard Time\", \"AKST\", \"AKDT\")\n    put(data, \"America/Noronha\", \"UTC-02\", \"-02\", \"-02\")\n    put(data, \"America/North_Dakota/Beulah\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/North_Dakota/Center\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/North_Dakota/New_Salem\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Ojinaga\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Panama\", \"SA Pacific Standard Time\", \"EST\", \"EST\")\n    put(data, \"America/Paramaribo\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Phoenix\", \"US Mountain Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Port-au-Prince\", \"Haiti Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Port_of_Spain\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Porto_Velho\", \"SA Western Standard Time\", \"-04\", \"-04\")\n    put(data, \"America/Puerto_Rico\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Punta_Arenas\", \"Magallanes Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Rankin_Inlet\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Recife\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Regina\", \"Canada Central Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Resolute\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Rio_Branco\", \"SA Pacific Standard Time\", \"-05\", \"-05\")\n    put(data, \"America/Santarem\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Santiago\", \"Pacific SA Standard Time\", \"-04\", \"-03\")\n    put(data, \"America/Santo_Domingo\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Sao_Paulo\", \"E. South America Standard Time\", \"-03\", \"-03\")\n    put(data, \"America/Scoresbysund\", \"Azores Standard Time\", \"-02\", \"-01\")\n    put(data, \"America/Sitka\", \"Alaskan Standard Time\", \"AKST\", \"AKDT\")\n    put(data, \"America/St_Barthelemy\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/St_Johns\", \"Newfoundland Standard Time\", \"NST\", \"NDT\")\n    put(data, \"America/St_Kitts\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/St_Lucia\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/St_Thomas\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/St_Vincent\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Swift_Current\", \"Canada Central Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Tegucigalpa\", \"Central America Standard Time\", \"CST\", \"CST\")\n    put(data, \"America/Thule\", \"Atlantic Standard Time\", \"AST\", \"ADT\")\n    put(data, \"America/Tijuana\", \"Pacific Standard Time (Mexico)\", \"PST\", \"PDT\")\n    put(data, \"America/Toronto\", \"Eastern Standard Time\", \"EST\", \"EDT\")\n    put(data, \"America/Tortola\", \"SA Western Standard Time\", \"AST\", \"AST\")\n    put(data, \"America/Vancouver\", \"Pacific Standard Time\", \"PST\", \"PDT\")\n    put(data, \"America/Whitehorse\", \"Yukon Standard Time\", \"MST\", \"MST\")\n    put(data, \"America/Winnipeg\", \"Central Standard Time\", \"CST\", \"CDT\")\n    put(data, \"America/Yakutat\", \"Alaskan Standard Time\", \"AKST\", \"AKDT\")\n    put(data, \"Antarctica/Casey\", \"Central Pacific Standard Time\", \"+08\", \"+08\")\n    put(data, \"Antarctica/Davis\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Antarctica/DumontDUrville\", \"West Pacific Standard Time\", \"+10\", \"+10\")\n    put(data, \"Antarctica/Macquarie\", \"Tasmania Standard Time\", \"AEST\", \"AEDT\")\n    put(data, \"Antarctica/Mawson\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Antarctica/McMurdo\", \"New Zealand Standard Time\", \"NZST\", \"NZDT\")\n    put(data, \"Antarctica/Palmer\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"Antarctica/Rothera\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"Antarctica/Syowa\", \"E. Africa Standard Time\", \"+03\", \"+03\")\n    put(data, \"Antarctica/Vostok\", \"Central Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Arctic/Longyearbyen\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Asia/Aden\", \"Arab Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Almaty\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Amman\", \"Jordan Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Anadyr\", \"Russia Time Zone 11\", \"+12\", \"+12\")\n    put(data, \"Asia/Aqtau\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Aqtobe\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Ashgabat\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Atyrau\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Baghdad\", \"Arabic Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Bahrain\", \"Arab Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Baku\", \"Azerbaijan Standard Time\", \"+04\", \"+04\")\n    put(data, \"Asia/Bangkok\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Barnaul\", \"Altai Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Beirut\", \"Middle East Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Asia/Bishkek\", \"Central Asia Standard Time\", \"+06\", \"+06\")\n    put(data, \"Asia/Brunei\", \"Singapore Standard Time\", \"+08\", \"+08\")\n    put(data, \"Asia/Calcutta\", \"India Standard Time\", \"IST\", \"IST\")\n    put(data, \"Asia/Chita\", \"Transbaikal Standard Time\", \"+09\", \"+09\")\n    put(data, \"Asia/Colombo\", \"Sri Lanka Standard Time\", \"+0530\", \"+0530\")\n    put(data, \"Asia/Damascus\", \"Syria Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Dhaka\", \"Bangladesh Standard Time\", \"+06\", \"+06\")\n    put(data, \"Asia/Dili\", \"Tokyo Standard Time\", \"+09\", \"+09\")\n    put(data, \"Asia/Dubai\", \"Arabian Standard Time\", \"+04\", \"+04\")\n    put(data, \"Asia/Dushanbe\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Famagusta\", \"GTB Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Asia/Gaza\", \"West Bank Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Asia/Hebron\", \"West Bank Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Asia/Hong_Kong\", \"China Standard Time\", \"HKT\", \"HKT\")\n    put(data, \"Asia/Hovd\", \"W. Mongolia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Irkutsk\", \"North Asia East Standard Time\", \"+08\", \"+08\")\n    put(data, \"Asia/Jakarta\", \"SE Asia Standard Time\", \"WIB\", \"WIB\")\n    put(data, \"Asia/Jayapura\", \"Tokyo Standard Time\", \"WIT\", \"WIT\")\n    put(data, \"Asia/Jerusalem\", \"Israel Standard Time\", \"IST\", \"IDT\")\n    put(data, \"Asia/Kabul\", \"Afghanistan Standard Time\", \"+0430\", \"+0430\")\n    put(data, \"Asia/Kamchatka\", \"Russia Time Zone 11\", \"+12\", \"+12\")\n    put(data, \"Asia/Karachi\", \"Pakistan Standard Time\", \"PKT\", \"PKT\")\n    put(data, \"Asia/Katmandu\", \"Nepal Standard Time\", \"+0545\", \"+0545\")\n    put(data, \"Asia/Khandyga\", \"Yakutsk Standard Time\", \"+09\", \"+09\")\n    put(data, \"Asia/Krasnoyarsk\", \"North Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Kuala_Lumpur\", \"Singapore Standard Time\", \"+08\", \"+08\")\n    put(data, \"Asia/Kuching\", \"Singapore Standard Time\", \"+08\", \"+08\")\n    put(data, \"Asia/Kuwait\", \"Arab Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Macau\", \"China Standard Time\", \"CST\", \"CST\")\n    put(data, \"Asia/Magadan\", \"Magadan Standard Time\", \"+11\", \"+11\")\n    put(data, \"Asia/Makassar\", \"Singapore Standard Time\", \"WITA\", \"WITA\")\n    put(data, \"Asia/Manila\", \"Singapore Standard Time\", \"PST\", \"PST\")\n    put(data, \"Asia/Muscat\", \"Arabian Standard Time\", \"+04\", \"+04\")\n    put(data, \"Asia/Nicosia\", \"GTB Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Asia/Novokuznetsk\", \"North Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Novosibirsk\", \"N. Central Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Omsk\", \"Omsk Standard Time\", \"+06\", \"+06\")\n    put(data, \"Asia/Oral\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Phnom_Penh\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Pontianak\", \"SE Asia Standard Time\", \"WIB\", \"WIB\")\n    put(data, \"Asia/Pyongyang\", \"North Korea Standard Time\", \"KST\", \"KST\")\n    put(data, \"Asia/Qatar\", \"Arab Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Qostanay\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Qyzylorda\", \"Qyzylorda Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Rangoon\", \"Myanmar Standard Time\", \"+0630\", \"+0630\")\n    put(data, \"Asia/Riyadh\", \"Arab Standard Time\", \"+03\", \"+03\")\n    put(data, \"Asia/Saigon\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Sakhalin\", \"Sakhalin Standard Time\", \"+11\", \"+11\")\n    put(data, \"Asia/Samarkand\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Seoul\", \"Korea Standard Time\", \"KST\", \"KST\")\n    put(data, \"Asia/Shanghai\", \"China Standard Time\", \"CST\", \"CST\")\n    put(data, \"Asia/Singapore\", \"Singapore Standard Time\", \"+08\", \"+08\")\n    put(data, \"Asia/Srednekolymsk\", \"Russia Time Zone 10\", \"+11\", \"+11\")\n    put(data, \"Asia/Taipei\", \"Taipei Standard Time\", \"CST\", \"CST\")\n    put(data, \"Asia/Tashkent\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Tbilisi\", \"Georgian Standard Time\", \"+04\", \"+04\")\n    put(data, \"Asia/Tehran\", \"Iran Standard Time\", \"+0330\", \"+0330\")\n    put(data, \"Asia/Thimphu\", \"Bangladesh Standard Time\", \"+06\", \"+06\")\n    put(data, \"Asia/Tokyo\", \"Tokyo Standard Time\", \"JST\", \"JST\")\n    put(data, \"Asia/Tomsk\", \"Tomsk Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Ulaanbaatar\", \"Ulaanbaatar Standard Time\", \"+08\", \"+08\")\n    put(data, \"Asia/Urumqi\", \"Central Asia Standard Time\", \"+06\", \"+06\")\n    put(data, \"Asia/Ust-Nera\", \"Vladivostok Standard Time\", \"+10\", \"+10\")\n    put(data, \"Asia/Vientiane\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Asia/Vladivostok\", \"Vladivostok Standard Time\", \"+10\", \"+10\")\n    put(data, \"Asia/Yakutsk\", \"Yakutsk Standard Time\", \"+09\", \"+09\")\n    put(data, \"Asia/Yekaterinburg\", \"Ekaterinburg Standard Time\", \"+05\", \"+05\")\n    put(data, \"Asia/Yerevan\", \"Caucasus Standard Time\", \"+04\", \"+04\")\n    put(data, \"Atlantic/Azores\", \"Azores Standard Time\", \"-01\", \"+00\")\n    put(data, \"Atlantic/Bermuda\", \"Atlantic Standard Time\", \"AST\", \"ADT\")\n    put(data, \"Atlantic/Canary\", \"GMT Standard Time\", \"WET\", \"WEST\")\n    put(data, \"Atlantic/Cape_Verde\", \"Cape Verde Standard Time\", \"-01\", \"-01\")\n    put(data, \"Atlantic/Faeroe\", \"GMT Standard Time\", \"WET\", \"WEST\")\n    put(data, \"Atlantic/Madeira\", \"GMT Standard Time\", \"WET\", \"WEST\")\n    put(data, \"Atlantic/Reykjavik\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Atlantic/South_Georgia\", \"UTC-02\", \"-02\", \"-02\")\n    put(data, \"Atlantic/St_Helena\", \"Greenwich Standard Time\", \"GMT\", \"GMT\")\n    put(data, \"Atlantic/Stanley\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"Australia/Adelaide\", \"Cen. Australia Standard Time\", \"ACST\", \"ACDT\")\n    put(data, \"Australia/Brisbane\", \"E. Australia Standard Time\", \"AEST\", \"AEST\")\n    put(data, \"Australia/Broken_Hill\", \"Cen. Australia Standard Time\", \"ACST\", \"ACDT\")\n    put(data, \"Australia/Darwin\", \"AUS Central Standard Time\", \"ACST\", \"ACST\")\n    put(data, \"Australia/Eucla\", \"Aus Central W. Standard Time\", \"+0845\", \"+0845\")\n    put(data, \"Australia/Hobart\", \"Tasmania Standard Time\", \"AEST\", \"AEDT\")\n    put(data, \"Australia/Lindeman\", \"E. Australia Standard Time\", \"AEST\", \"AEST\")\n    put(data, \"Australia/Lord_Howe\", \"Lord Howe Standard Time\", \"+1030\", \"+11\")\n    put(data, \"Australia/Melbourne\", \"AUS Eastern Standard Time\", \"AEST\", \"AEDT\")\n    put(data, \"Australia/Perth\", \"W. Australia Standard Time\", \"AWST\", \"AWST\")\n    put(data, \"Australia/Sydney\", \"AUS Eastern Standard Time\", \"AEST\", \"AEDT\")\n    put(data, \"Etc/GMT\", \"UTC\", \"GMT\", \"GMT\")\n    put(data, \"Etc/GMT+1\", \"Cape Verde Standard Time\", \"-01\", \"-01\")\n    put(data, \"Etc/GMT+10\", \"Hawaiian Standard Time\", \"-10\", \"-10\")\n    put(data, \"Etc/GMT+11\", \"UTC-11\", \"-11\", \"-11\")\n    put(data, \"Etc/GMT+12\", \"Dateline Standard Time\", \"-12\", \"-12\")\n    put(data, \"Etc/GMT+2\", \"UTC-02\", \"-02\", \"-02\")\n    put(data, \"Etc/GMT+3\", \"SA Eastern Standard Time\", \"-03\", \"-03\")\n    put(data, \"Etc/GMT+4\", \"SA Western Standard Time\", \"-04\", \"-04\")\n    put(data, \"Etc/GMT+5\", \"SA Pacific Standard Time\", \"-05\", \"-05\")\n    put(data, \"Etc/GMT+6\", \"Central America Standard Time\", \"-06\", \"-06\")\n    put(data, \"Etc/GMT+7\", \"US Mountain Standard Time\", \"-07\", \"-07\")\n    put(data, \"Etc/GMT+8\", \"UTC-08\", \"-08\", \"-08\")\n    put(data, \"Etc/GMT+9\", \"UTC-09\", \"-09\", \"-09\")\n    put(data, \"Etc/GMT-1\", \"W. Central Africa Standard Time\", \"+01\", \"+01\")\n    put(data, \"Etc/GMT-10\", \"West Pacific Standard Time\", \"+10\", \"+10\")\n    put(data, \"Etc/GMT-11\", \"Central Pacific Standard Time\", \"+11\", \"+11\")\n    put(data, \"Etc/GMT-12\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Etc/GMT-13\", \"UTC+13\", \"+13\", \"+13\")\n    put(data, \"Etc/GMT-14\", \"Line Islands Standard Time\", \"+14\", \"+14\")\n    put(data, \"Etc/GMT-2\", \"South Africa Standard Time\", \"+02\", \"+02\")\n    put(data, \"Etc/GMT-3\", \"E. Africa Standard Time\", \"+03\", \"+03\")\n    put(data, \"Etc/GMT-4\", \"Arabian Standard Time\", \"+04\", \"+04\")\n    put(data, \"Etc/GMT-5\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Etc/GMT-6\", \"Central Asia Standard Time\", \"+06\", \"+06\")\n    put(data, \"Etc/GMT-7\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Etc/GMT-8\", \"Singapore Standard Time\", \"+08\", \"+08\")\n    put(data, \"Etc/GMT-9\", \"Tokyo Standard Time\", \"+09\", \"+09\")\n    put(data, \"Etc/UTC\", \"UTC\", \"UTC\", \"UTC\")\n    put(data, \"Europe/Amsterdam\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Andorra\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Astrakhan\", \"Astrakhan Standard Time\", \"+04\", \"+04\")\n    put(data, \"Europe/Athens\", \"GTB Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Belgrade\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Berlin\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Bratislava\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Brussels\", \"Romance Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Bucharest\", \"GTB Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Budapest\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Busingen\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Chisinau\", \"E. Europe Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Copenhagen\", \"Romance Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Dublin\", \"GMT Standard Time\", \"GMT\", \"IST\")\n    put(data, \"Europe/Gibraltar\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Guernsey\", \"GMT Standard Time\", \"GMT\", \"BST\")\n    put(data, \"Europe/Helsinki\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Isle_of_Man\", \"GMT Standard Time\", \"GMT\", \"BST\")\n    put(data, \"Europe/Istanbul\", \"Turkey Standard Time\", \"+03\", \"+03\")\n    put(data, \"Europe/Jersey\", \"GMT Standard Time\", \"GMT\", \"BST\")\n    put(data, \"Europe/Kaliningrad\", \"Kaliningrad Standard Time\", \"EET\", \"EET\")\n    put(data, \"Europe/Kiev\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Kirov\", \"Russian Standard Time\", \"MSK\", \"MSK\")\n    put(data, \"Europe/Lisbon\", \"GMT Standard Time\", \"WET\", \"WEST\")\n    put(data, \"Europe/Ljubljana\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/London\", \"GMT Standard Time\", \"GMT\", \"BST\")\n    put(data, \"Europe/Luxembourg\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Madrid\", \"Romance Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Malta\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Mariehamn\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Minsk\", \"Belarus Standard Time\", \"+03\", \"+03\")\n    put(data, \"Europe/Monaco\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Moscow\", \"Russian Standard Time\", \"MSK\", \"MSK\")\n    put(data, \"Europe/Oslo\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Paris\", \"Romance Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Podgorica\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Prague\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Riga\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Rome\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Samara\", \"Russia Time Zone 3\", \"+04\", \"+04\")\n    put(data, \"Europe/San_Marino\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Sarajevo\", \"Central European Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Saratov\", \"Saratov Standard Time\", \"+04\", \"+04\")\n    put(data, \"Europe/Simferopol\", \"Russian Standard Time\", \"MSK\", \"MSK\")\n    put(data, \"Europe/Skopje\", \"Central European Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Sofia\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Stockholm\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Tallinn\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Tirane\", \"Central Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Ulyanovsk\", \"Astrakhan Standard Time\", \"+04\", \"+04\")\n    put(data, \"Europe/Vaduz\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Vatican\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Vienna\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Vilnius\", \"FLE Standard Time\", \"EET\", \"EEST\")\n    put(data, \"Europe/Volgograd\", \"Volgograd Standard Time\", \"MSK\", \"MSK\")\n    put(data, \"Europe/Warsaw\", \"Central European Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Zagreb\", \"Central European Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Europe/Zurich\", \"W. Europe Standard Time\", \"CET\", \"CEST\")\n    put(data, \"Indian/Antananarivo\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Indian/Chagos\", \"Central Asia Standard Time\", \"+06\", \"+06\")\n    put(data, \"Indian/Christmas\", \"SE Asia Standard Time\", \"+07\", \"+07\")\n    put(data, \"Indian/Cocos\", \"Myanmar Standard Time\", \"+0630\", \"+0630\")\n    put(data, \"Indian/Comoro\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Indian/Kerguelen\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Indian/Mahe\", \"Mauritius Standard Time\", \"+04\", \"+04\")\n    put(data, \"Indian/Maldives\", \"West Asia Standard Time\", \"+05\", \"+05\")\n    put(data, \"Indian/Mauritius\", \"Mauritius Standard Time\", \"+04\", \"+04\")\n    put(data, \"Indian/Mayotte\", \"E. Africa Standard Time\", \"EAT\", \"EAT\")\n    put(data, \"Indian/Reunion\", \"Mauritius Standard Time\", \"+04\", \"+04\")\n    put(data, \"Pacific/Apia\", \"Samoa Standard Time\", \"+13\", \"+13\")\n    put(data, \"Pacific/Auckland\", \"New Zealand Standard Time\", \"NZST\", \"NZDT\")\n    put(data, \"Pacific/Bougainville\", \"Bougainville Standard Time\", \"+11\", \"+11\")\n    put(data, \"Pacific/Chatham\", \"Chatham Islands Standard Time\", \"+1245\", \"+1345\")\n    put(data, \"Pacific/Easter\", \"Easter Island Standard Time\", \"-06\", \"-05\")\n    put(data, \"Pacific/Efate\", \"Central Pacific Standard Time\", \"+11\", \"+11\")\n    put(data, \"Pacific/Enderbury\", \"UTC+13\", \"+13\", \"+13\")\n    put(data, \"Pacific/Fakaofo\", \"UTC+13\", \"+13\", \"+13\")\n    put(data, \"Pacific/Fiji\", \"Fiji Standard Time\", \"+12\", \"+12\")\n    put(data, \"Pacific/Funafuti\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Pacific/Galapagos\", \"Central America Standard Time\", \"-06\", \"-06\")\n    put(data, \"Pacific/Gambier\", \"UTC-09\", \"-09\", \"-09\")\n    put(data, \"Pacific/Guadalcanal\", \"Central Pacific Standard Time\", \"+11\", \"+11\")\n    put(data, \"Pacific/Guam\", \"West Pacific Standard Time\", \"ChST\", \"ChST\")\n    put(data, \"Pacific/Honolulu\", \"Hawaiian Standard Time\", \"HST\", \"HST\")\n    put(data, \"Pacific/Kiritimati\", \"Line Islands Standard Time\", \"+14\", \"+14\")\n    put(data, \"Pacific/Kosrae\", \"Central Pacific Standard Time\", \"+11\", \"+11\")\n    put(data, \"Pacific/Kwajalein\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Pacific/Majuro\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Pacific/Marquesas\", \"Marquesas Standard Time\", \"-0930\", \"-0930\")\n    put(data, \"Pacific/Midway\", \"UTC-11\", \"SST\", \"SST\")\n    put(data, \"Pacific/Nauru\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Pacific/Niue\", \"UTC-11\", \"-11\", \"-11\")\n    put(data, \"Pacific/Norfolk\", \"Norfolk Standard Time\", \"+11\", \"+12\")\n    put(data, \"Pacific/Noumea\", \"Central Pacific Standard Time\", \"+11\", \"+11\")\n    put(data, \"Pacific/Pago_Pago\", \"UTC-11\", \"SST\", \"SST\")\n    put(data, \"Pacific/Palau\", \"Tokyo Standard Time\", \"+09\", \"+09\")\n    put(data, \"Pacific/Pitcairn\", \"UTC-08\", \"-08\", \"-08\")\n    put(data, \"Pacific/Ponape\", \"Central Pacific Standard Time\", \"+11\", \"+11\")\n    put(data, \"Pacific/Port_Moresby\", \"West Pacific Standard Time\", \"+10\", \"+10\")\n    put(data, \"Pacific/Rarotonga\", \"Hawaiian Standard Time\", \"-10\", \"-10\")\n    put(data, \"Pacific/Saipan\", \"West Pacific Standard Time\", \"ChST\", \"ChST\")\n    put(data, \"Pacific/Tahiti\", \"Hawaiian Standard Time\", \"-10\", \"-10\")\n    put(data, \"Pacific/Tarawa\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Pacific/Tongatapu\", \"Tonga Standard Time\", \"+13\", \"+13\")\n    put(data, \"Pacific/Truk\", \"West Pacific Standard Time\", \"+10\", \"+10\")\n    put(data, \"Pacific/Wake\", \"UTC+12\", \"+12\", \"+12\")\n    put(data, \"Pacific/Wallis\", \"UTC+12\", \"+12\", \"+12\")\n    data\n  end\n\n  # These canonical mappings from Windows to IANA time zone names, used for the\n  # local time zone, are based on\n  # https://raw.githubusercontent.com/unicode-org/cldr/f8369ba0795c79f3bac8eb89967eea359f77835e/common/supplemental/windowsZones.xml\n  private class_getter windows_to_iana : Hash(String, String) do\n    data = Hash(String, String).new(initial_capacity: 139)\n    put(data, \"Egypt Standard Time\", \"Africa/Cairo\")\n    put(data, \"Morocco Standard Time\", \"Africa/Casablanca\")\n    put(data, \"South Africa Standard Time\", \"Africa/Johannesburg\")\n    put(data, \"South Sudan Standard Time\", \"Africa/Juba\")\n    put(data, \"Sudan Standard Time\", \"Africa/Khartoum\")\n    put(data, \"W. Central Africa Standard Time\", \"Africa/Lagos\")\n    put(data, \"E. Africa Standard Time\", \"Africa/Nairobi\")\n    put(data, \"Sao Tome Standard Time\", \"Africa/Sao_Tome\")\n    put(data, \"Libya Standard Time\", \"Africa/Tripoli\")\n    put(data, \"Namibia Standard Time\", \"Africa/Windhoek\")\n    put(data, \"Aleutian Standard Time\", \"America/Adak\")\n    put(data, \"Alaskan Standard Time\", \"America/Anchorage\")\n    put(data, \"Tocantins Standard Time\", \"America/Araguaina\")\n    put(data, \"Paraguay Standard Time\", \"America/Asuncion\")\n    put(data, \"Bahia Standard Time\", \"America/Bahia\")\n    put(data, \"SA Pacific Standard Time\", \"America/Bogota\")\n    put(data, \"Argentina Standard Time\", \"America/Buenos_Aires\")\n    put(data, \"Eastern Standard Time (Mexico)\", \"America/Cancun\")\n    put(data, \"Venezuela Standard Time\", \"America/Caracas\")\n    put(data, \"SA Eastern Standard Time\", \"America/Cayenne\")\n    put(data, \"Central Standard Time\", \"America/Chicago\")\n    put(data, \"Central Brazilian Standard Time\", \"America/Cuiaba\")\n    put(data, \"Mountain Standard Time\", \"America/Denver\")\n    put(data, \"Greenland Standard Time\", \"America/Godthab\")\n    put(data, \"Turks And Caicos Standard Time\", \"America/Grand_Turk\")\n    put(data, \"Central America Standard Time\", \"America/Guatemala\")\n    put(data, \"Atlantic Standard Time\", \"America/Halifax\")\n    put(data, \"Cuba Standard Time\", \"America/Havana\")\n    put(data, \"US Eastern Standard Time\", \"America/Indianapolis\")\n    put(data, \"SA Western Standard Time\", \"America/La_Paz\")\n    put(data, \"Pacific Standard Time\", \"America/Los_Angeles\")\n    put(data, \"Mountain Standard Time (Mexico)\", \"America/Mazatlan\")\n    put(data, \"Central Standard Time (Mexico)\", \"America/Mexico_City\")\n    put(data, \"Saint Pierre Standard Time\", \"America/Miquelon\")\n    put(data, \"Montevideo Standard Time\", \"America/Montevideo\")\n    put(data, \"Eastern Standard Time\", \"America/New_York\")\n    put(data, \"US Mountain Standard Time\", \"America/Phoenix\")\n    put(data, \"Haiti Standard Time\", \"America/Port-au-Prince\")\n    put(data, \"Magallanes Standard Time\", \"America/Punta_Arenas\")\n    put(data, \"Canada Central Standard Time\", \"America/Regina\")\n    put(data, \"Pacific SA Standard Time\", \"America/Santiago\")\n    put(data, \"E. South America Standard Time\", \"America/Sao_Paulo\")\n    put(data, \"Newfoundland Standard Time\", \"America/St_Johns\")\n    put(data, \"Pacific Standard Time (Mexico)\", \"America/Tijuana\")\n    put(data, \"Yukon Standard Time\", \"America/Whitehorse\")\n    put(data, \"Jordan Standard Time\", \"Asia/Amman\")\n    put(data, \"Arabic Standard Time\", \"Asia/Baghdad\")\n    put(data, \"Azerbaijan Standard Time\", \"Asia/Baku\")\n    put(data, \"SE Asia Standard Time\", \"Asia/Bangkok\")\n    put(data, \"Altai Standard Time\", \"Asia/Barnaul\")\n    put(data, \"Middle East Standard Time\", \"Asia/Beirut\")\n    put(data, \"Central Asia Standard Time\", \"Asia/Bishkek\")\n    put(data, \"India Standard Time\", \"Asia/Calcutta\")\n    put(data, \"Transbaikal Standard Time\", \"Asia/Chita\")\n    put(data, \"Sri Lanka Standard Time\", \"Asia/Colombo\")\n    put(data, \"Syria Standard Time\", \"Asia/Damascus\")\n    put(data, \"Bangladesh Standard Time\", \"Asia/Dhaka\")\n    put(data, \"Arabian Standard Time\", \"Asia/Dubai\")\n    put(data, \"West Bank Standard Time\", \"Asia/Hebron\")\n    put(data, \"W. Mongolia Standard Time\", \"Asia/Hovd\")\n    put(data, \"North Asia East Standard Time\", \"Asia/Irkutsk\")\n    put(data, \"Israel Standard Time\", \"Asia/Jerusalem\")\n    put(data, \"Afghanistan Standard Time\", \"Asia/Kabul\")\n    put(data, \"Russia Time Zone 11\", \"Asia/Kamchatka\")\n    put(data, \"Pakistan Standard Time\", \"Asia/Karachi\")\n    put(data, \"Nepal Standard Time\", \"Asia/Katmandu\")\n    put(data, \"North Asia Standard Time\", \"Asia/Krasnoyarsk\")\n    put(data, \"Magadan Standard Time\", \"Asia/Magadan\")\n    put(data, \"N. Central Asia Standard Time\", \"Asia/Novosibirsk\")\n    put(data, \"Omsk Standard Time\", \"Asia/Omsk\")\n    put(data, \"North Korea Standard Time\", \"Asia/Pyongyang\")\n    put(data, \"Qyzylorda Standard Time\", \"Asia/Qyzylorda\")\n    put(data, \"Myanmar Standard Time\", \"Asia/Rangoon\")\n    put(data, \"Arab Standard Time\", \"Asia/Riyadh\")\n    put(data, \"Sakhalin Standard Time\", \"Asia/Sakhalin\")\n    put(data, \"Korea Standard Time\", \"Asia/Seoul\")\n    put(data, \"China Standard Time\", \"Asia/Shanghai\")\n    put(data, \"Singapore Standard Time\", \"Asia/Singapore\")\n    put(data, \"Russia Time Zone 10\", \"Asia/Srednekolymsk\")\n    put(data, \"Taipei Standard Time\", \"Asia/Taipei\")\n    put(data, \"West Asia Standard Time\", \"Asia/Tashkent\")\n    put(data, \"Georgian Standard Time\", \"Asia/Tbilisi\")\n    put(data, \"Iran Standard Time\", \"Asia/Tehran\")\n    put(data, \"Tokyo Standard Time\", \"Asia/Tokyo\")\n    put(data, \"Tomsk Standard Time\", \"Asia/Tomsk\")\n    put(data, \"Ulaanbaatar Standard Time\", \"Asia/Ulaanbaatar\")\n    put(data, \"Vladivostok Standard Time\", \"Asia/Vladivostok\")\n    put(data, \"Yakutsk Standard Time\", \"Asia/Yakutsk\")\n    put(data, \"Ekaterinburg Standard Time\", \"Asia/Yekaterinburg\")\n    put(data, \"Caucasus Standard Time\", \"Asia/Yerevan\")\n    put(data, \"Azores Standard Time\", \"Atlantic/Azores\")\n    put(data, \"Cape Verde Standard Time\", \"Atlantic/Cape_Verde\")\n    put(data, \"Greenwich Standard Time\", \"Atlantic/Reykjavik\")\n    put(data, \"Cen. Australia Standard Time\", \"Australia/Adelaide\")\n    put(data, \"E. Australia Standard Time\", \"Australia/Brisbane\")\n    put(data, \"AUS Central Standard Time\", \"Australia/Darwin\")\n    put(data, \"Aus Central W. Standard Time\", \"Australia/Eucla\")\n    put(data, \"Tasmania Standard Time\", \"Australia/Hobart\")\n    put(data, \"Lord Howe Standard Time\", \"Australia/Lord_Howe\")\n    put(data, \"W. Australia Standard Time\", \"Australia/Perth\")\n    put(data, \"AUS Eastern Standard Time\", \"Australia/Sydney\")\n    put(data, \"UTC-11\", \"Etc/GMT+11\")\n    put(data, \"Dateline Standard Time\", \"Etc/GMT+12\")\n    put(data, \"UTC-02\", \"Etc/GMT+2\")\n    put(data, \"UTC-08\", \"Etc/GMT+8\")\n    put(data, \"UTC-09\", \"Etc/GMT+9\")\n    put(data, \"UTC+12\", \"Etc/GMT-12\")\n    put(data, \"UTC+13\", \"Etc/GMT-13\")\n    put(data, \"UTC\", \"Etc/UTC\")\n    put(data, \"Astrakhan Standard Time\", \"Europe/Astrakhan\")\n    put(data, \"W. Europe Standard Time\", \"Europe/Berlin\")\n    put(data, \"GTB Standard Time\", \"Europe/Bucharest\")\n    put(data, \"Central Europe Standard Time\", \"Europe/Budapest\")\n    put(data, \"E. Europe Standard Time\", \"Europe/Chisinau\")\n    put(data, \"Turkey Standard Time\", \"Europe/Istanbul\")\n    put(data, \"Kaliningrad Standard Time\", \"Europe/Kaliningrad\")\n    put(data, \"FLE Standard Time\", \"Europe/Kiev\")\n    put(data, \"GMT Standard Time\", \"Europe/London\")\n    put(data, \"Belarus Standard Time\", \"Europe/Minsk\")\n    put(data, \"Russian Standard Time\", \"Europe/Moscow\")\n    put(data, \"Romance Standard Time\", \"Europe/Paris\")\n    put(data, \"Russia Time Zone 3\", \"Europe/Samara\")\n    put(data, \"Saratov Standard Time\", \"Europe/Saratov\")\n    put(data, \"Volgograd Standard Time\", \"Europe/Volgograd\")\n    put(data, \"Central European Standard Time\", \"Europe/Warsaw\")\n    put(data, \"Mauritius Standard Time\", \"Indian/Mauritius\")\n    put(data, \"Samoa Standard Time\", \"Pacific/Apia\")\n    put(data, \"New Zealand Standard Time\", \"Pacific/Auckland\")\n    put(data, \"Bougainville Standard Time\", \"Pacific/Bougainville\")\n    put(data, \"Chatham Islands Standard Time\", \"Pacific/Chatham\")\n    put(data, \"Easter Island Standard Time\", \"Pacific/Easter\")\n    put(data, \"Fiji Standard Time\", \"Pacific/Fiji\")\n    put(data, \"Central Pacific Standard Time\", \"Pacific/Guadalcanal\")\n    put(data, \"Hawaiian Standard Time\", \"Pacific/Honolulu\")\n    put(data, \"Line Islands Standard Time\", \"Pacific/Kiritimati\")\n    put(data, \"Marquesas Standard Time\", \"Pacific/Marquesas\")\n    put(data, \"Norfolk Standard Time\", \"Pacific/Norfolk\")\n    put(data, \"West Pacific Standard Time\", \"Pacific/Port_Moresby\")\n    put(data, \"Tonga Standard Time\", \"Pacific/Tongatapu\")\n    data\n  end\n\n  # TODO: this is needed to avoid generating lots of allocas\n  # in LLVM, which makes LLVM really slow. The compiler should\n  # try to avoid/reuse temporary allocas.\n  # Explanation: https://github.com/crystal-lang/crystal/issues/4516#issuecomment-306226171\n  private def self.put(hash : Hash, key, value) : Nil\n    hash[key] = value\n  end\n\n  private def self.put(hash : Hash, key, *values) : Nil\n    hash[key] = values\n  end\nend\n"
  },
  {
    "path": "src/crystal/system/windows.cr",
    "content": "require \"c/sddl\"\n\n# :nodoc:\nmodule Crystal::System\n  def self.retry_wstr_buffer(&)\n    buffer_arr = uninitialized LibC::WCHAR[256]\n\n    buffer_size = yield buffer_arr.to_slice, true\n    buffer = Slice(LibC::WCHAR).new(buffer_size)\n\n    yield buffer, false\n    raise \"BUG: retry_wstr_buffer returned\"\n  end\n\n  def self.to_wstr(str : String, name : String? = nil) : LibC::LPWSTR\n    str.check_no_null_byte(name).to_utf16.to_unsafe\n  end\n\n  # Converts the string literal *str* into a `Slice` of UTF-16 code points.\n  # *str* must not contain embedded null characters.\n  #\n  # This is equivalent to the macro `StringLiteral#to_utf16` if slice literals\n  # are available, and the normal `String#to_utf16` otherwise. This is useful\n  # for passing constant string arguments to Win32 functions.\n  {% if compare_versions(Crystal::VERSION, \"1.16.0\") >= 0 %}\n    macro wstr_literal(str)\n      \\{{ str.to_utf16 }}\n    end\n  {% else %}\n    macro wstr_literal(str)\n      (\\{{ str }}).check_no_null_byte.to_utf16\n    end\n  {% end %}\n\n  def self.sid_to_s(sid : LibC::SID*) : String\n    if LibC.ConvertSidToStringSidW(sid, out ptr) == 0\n      raise RuntimeError.from_winerror(\"ConvertSidToStringSidW\")\n    end\n    str, _ = String.from_utf16(ptr)\n    LibC.LocalFree(ptr)\n    str\n  end\n\n  def self.sid_from_s(str : String) : LibC::SID*\n    status = LibC.ConvertStringSidToSidW(to_wstr(str), out sid)\n    status != 0 ? sid : Pointer(LibC::SID).null\n  end\n\n  record SIDLookupResult, sid : LibC::SID*, domain : String, type : LibC::SID_NAME_USE\n\n  def self.name_to_sid(name : String) : SIDLookupResult?\n    utf16_name = to_wstr(name)\n\n    sid_size = LibC::DWORD.zero\n    domain_buf_size = LibC::DWORD.zero\n    LibC.LookupAccountNameW(nil, utf16_name, nil, pointerof(sid_size), nil, pointerof(domain_buf_size), out _)\n\n    unless WinError.value.error_none_mapped?\n      sid = Pointer(UInt8).malloc(sid_size).as(LibC::SID*)\n      domain_buf = Slice(LibC::WCHAR).new(domain_buf_size)\n      if LibC.LookupAccountNameW(nil, utf16_name, sid, pointerof(sid_size), domain_buf, pointerof(domain_buf_size), out sid_type) != 0\n        domain = String.from_utf16(domain_buf[..-2])\n        SIDLookupResult.new(sid, domain, sid_type)\n      end\n    end\n  end\n\n  record NameLookupResult, name : String, domain : String, type : LibC::SID_NAME_USE\n\n  def self.sid_to_name(sid : LibC::SID*) : NameLookupResult?\n    name_buf_size = LibC::DWORD.zero\n    domain_buf_size = LibC::DWORD.zero\n    LibC.LookupAccountSidW(nil, sid, nil, pointerof(name_buf_size), nil, pointerof(domain_buf_size), out _)\n\n    unless WinError.value.error_none_mapped?\n      name_buf = Slice(LibC::WCHAR).new(name_buf_size)\n      domain_buf = Slice(LibC::WCHAR).new(domain_buf_size)\n      if LibC.LookupAccountSidW(nil, sid, name_buf, pointerof(name_buf_size), domain_buf, pointerof(domain_buf_size), out sid_type) != 0\n        name = String.from_utf16(name_buf[..-2])\n        domain = String.from_utf16(domain_buf[..-2])\n        NameLookupResult.new(name, domain, sid_type)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/system.cr",
    "content": "# :nodoc:\nmodule Crystal::System\n  # Returns the hostname\n  # def self.hostname\n\n  # Returns the number of logical processors available to the system. Returns -1\n  # on errors or when unknown.\n  # def self.cpu_count\n\n  # Returns the number of logical processors available to the process. Should be\n  # less than or equal to `.cpu_count`. Returns -1 on errors or when unknown.\n  def self.effective_cpu_count\n    -1\n  end\nend\n\n{% if flag?(:wasi) %}\n  require \"./system/wasi/hostname\"\n  require \"./system/wasi/cpucount\"\n{% elsif flag?(:unix) %}\n  require \"./system/unix/hostname\"\n  {% if flag?(:bsd) %}\n    require \"./system/unix/sysctl_cpucount\"\n  {% else %}\n    require \"./system/unix/sysconf_cpucount\"\n    {% if flag?(:linux) %}\n      require \"./system/unix/linux_cpucount\"\n    {% end %}\n  {% end %}\n{% elsif flag?(:win32) %}\n  require \"./system/win32/hostname\"\n  require \"./system/win32/cpucount\"\n{% else %}\n  {% raise \"No Crystal::System implementation available\" %}\n{% end %}\n"
  },
  {
    "path": "src/crystal/thread_local_value.cr",
    "content": "# :nodoc:\nstruct Crystal::ThreadLocalValue(T)\n  @values = Hash(Thread, T).new\n  @mutex = Crystal::SpinLock.new\n\n  def get(&block : -> T)\n    th = Thread.current\n    @mutex.sync do\n      @values.fetch(th) do\n        @values[th] = yield\n      end\n    end\n  end\n\n  def get?\n    @mutex.sync do\n      @values[Thread.current]?\n    end\n  end\n\n  def set(value : T)\n    @mutex.sync do\n      @values[Thread.current] = value\n    end\n  end\n\n  def consume_each(&)\n    @mutex.sync do\n      @values.each_value { |t| yield t }\n      @values.clear\n    end\n  end\nend\n"
  },
  {
    "path": "src/crystal/tracing.cr",
    "content": "require \"./system/panic\"\n\nmodule Crystal\n  # :nodoc:\n  module Tracing\n    @[Flags]\n    enum Section\n      GC\n      Sched\n      Evloop\n\n      def self.from_id(slice) : self\n        {% begin %}\n          case slice\n            {% for name in @type.constants %}\n              when {{name.underscore.stringify}}.to_slice\n                {{name}}\n            {% end %}\n          else\n            None\n          end\n        {% end %}\n      end\n\n      def to_id : String\n        {% begin %}\n          case self\n          {% for name in @type.constants %}\n            when {{name}}\n              {{name.underscore.stringify}}\n          {% end %}\n          else\n            \"???\"\n          end\n        {% end %}\n      end\n    end\n  end\n\n  {% if flag?(:tracing) %}\n    # :nodoc:\n    module Tracing\n      # IO-like object with a fixed capacity but dynamic size within the\n      # buffer's capacity (i.e. `0 <= size <= N`). Stops writing to the internal\n      # buffer when capacity is reached; further writes are skipped.\n      struct BufferIO(N)\n        getter size : Int32\n\n        def initialize\n          @buf = uninitialized UInt8[N]\n          @int_buf = uninitialized UInt8[20] # max 64-bit integers\n          @size = 0\n        end\n\n        def write(value : Bytes) : Nil\n          pos = @size\n          remaining = N - pos\n          return if remaining == 0\n\n          n = value.size.clamp(..remaining)\n          value.to_unsafe.copy_to(@buf.to_unsafe + pos, n)\n          @size = pos + n\n        end\n\n        def write(value : String) : Nil\n          write value.to_slice\n        end\n\n        def write(value : Char) : Nil\n          chars = uninitialized UInt8[4]\n          i = 0\n          value.each_byte do |byte|\n            chars[i] = byte\n            i += 1\n          end\n          write chars.to_slice[0, i]\n        end\n\n        def write(value : Fiber) : Nil\n          write value.as(Void*)\n          write ':'\n          write value.name || '?'\n        end\n\n        def write(value : Thread) : Nil\n          {% if flag?(:linux) %}\n            write Pointer(Void).new(value.@system_handle)\n          {% else %}\n            write value.@system_handle\n          {% end %}\n        end\n\n        {% if flag?(:execution_context) %}\n          def write(value : Fiber::ExecutionContext) : Nil\n            write value.name\n          end\n\n          def write(value : Fiber::ExecutionContext::Scheduler) : Nil\n            write value.name\n          end\n        {% end %}\n\n        def write(value : Pointer) : Nil\n          write \"0x\"\n          write System.to_int_slice(@int_buf.to_slice, value.address, 16, true, 2)\n        end\n\n        def write(value : Int::Signed) : Nil\n          write System.to_int_slice(@int_buf.to_slice, value, 10, true, 2)\n        end\n\n        def write(value : Int::Unsigned) : Nil\n          write System.to_int_slice(@int_buf.to_slice, value, 10, false, 2)\n        end\n\n        def write(value : Time::Span) : Nil\n          write(value.seconds &* Time::NANOSECONDS_PER_SECOND &+ value.nanoseconds)\n        end\n\n        def write(value : Bool) : Nil\n          write value ? '1' : '0'\n        end\n\n        def write(value : Nil) : Nil\n        end\n\n        def to_slice : Bytes\n          Bytes.new(@buf.to_unsafe, @size)\n        end\n      end\n\n      @@sections = Section::None\n      @@handle = uninitialized System::FileDescriptor::Handle\n\n      @[AlwaysInline]\n      def self.enabled?(section : Section) : Bool\n        @@sections.includes?(section)\n      end\n\n      # Setup tracing.\n      #\n      # Parses the `CRYSTAL_TRACE` environment variable to enable the sections\n      # to trace. See `Section`. By default no sections are enabled.\n      #\n      # Parses the `CRYSTAL_TRACE_FILE` environment variable to open the trace\n      # file to write to. Exits with an error message when the file can't be\n      # opened, created or truncated. Uses the standard error when unspecified.\n      #\n      # This should be the first thing called in main, maybe even before the GC\n      # itself is initialized. The function assumes neither the GC nor ENV nor\n      # anything is available and musn't allocate into the GC HEAP.\n      def self.init : Nil\n        @@sections = Section::None\n\n        {% if flag?(:win32) %}\n          buf = uninitialized UInt16[256]\n\n          name = System.wstr_literal \"CRYSTAL_TRACE\"\n          len = LibC.GetEnvironmentVariableW(name, buf, buf.size)\n          parse_sections(buf.to_slice[0...len]) if len > 0\n\n          name = System.wstr_literal \"CRYSTAL_TRACE_FILE\"\n          len = LibC.GetEnvironmentVariableW(name, buf, buf.size)\n          if len > 0\n            @@handle = open_trace_file(buf.to_slice[0...len])\n          else\n            @@handle = LibC.GetStdHandle(LibC::STD_ERROR_HANDLE).address\n          end\n        {% else %}\n          if ptr = LibC.getenv(\"CRYSTAL_TRACE\")\n            len = LibC.strlen(ptr)\n            parse_sections(Slice.new(ptr, len)) if len > 0\n          end\n\n          if (ptr = LibC.getenv(\"CRYSTAL_TRACE_FILE\")) && (LibC.strlen(ptr) > 0)\n            @@handle = open_trace_file(ptr)\n          else\n            @@handle = 2\n          end\n        {% end %}\n      end\n\n      private def self.open_trace_file(filename)\n        {% if flag?(:win32) %}\n          handle = LibC.CreateFileW(filename, LibC::FILE_GENERIC_WRITE, LibC::DEFAULT_SHARE_MODE, nil, LibC::CREATE_ALWAYS, LibC::FILE_ATTRIBUTE_NORMAL, LibC::HANDLE.null)\n          # not using LibC::INVALID_HANDLE_VALUE because it doesn't exist (yet)\n          return handle.address unless handle == LibC::HANDLE.new(-1.to_u64!)\n\n          syscall_name = \"CreateFileW\"\n          error = WinError.value\n        {% else %}\n          fd = LibC.open(filename, LibC::O_CREAT | LibC::O_WRONLY | LibC::O_TRUNC | LibC::O_CLOEXEC, 0o644)\n          return fd unless fd < 0\n\n          syscall_name = \"open\"\n          error = Errno.value\n        {% end %}\n\n        System.print_error \"ERROR: failed to open \"\n        System.print_error filename\n        System.print_error \" for writing\\n\"\n\n        System.panic(syscall_name, Errno.value)\n      end\n\n      private def self.parse_sections(slice)\n        each_token(slice) do |token|\n          @@sections |= Section.from_id(token)\n        end\n      end\n\n      private def self.each_token(slice, delim = ',', &)\n        while e = slice.index(delim.ord)\n          yield slice[0, e]\n          slice = slice[(e + 1)..]\n        end\n        yield slice[0..] unless slice.size == 0\n      end\n\n      # :nodoc:\n      #\n      # Formats and prints a log message to stderr. The generated message is\n      # limited to 512 bytes (PIPE_BUF) after which it will be truncated. Being\n      # below PIPE_BUF the message shall be written atomically to stderr,\n      # avoiding interleaved or smashed traces from multiple threads.\n      #\n      # Windows may not have the same guarantees but the buffering should limit\n      # these from happening.\n      def self.log(section : String, operation : String, time : UInt64, **metadata) : Nil\n        buf = BufferIO(512).new\n        buf.write section\n        buf.write \".\"\n        buf.write operation\n        buf.write \" \"\n        buf.write time\n\n        {% unless flag?(:wasm32) %}\n          # WASM doesn't have threads (and fibers aren't implemented either)\n          #\n          # We also start to trace *before* Thread.current and other objects have\n          # been allocated, they're lazily allocated and since we trace GC.malloc we\n          # must skip the objects until they're allocated (otherwise we hit infinite\n          # recursion): malloc -> trace -> malloc -> trace -> ...\n          thread = ::Thread.current?\n\n          buf.write \" thread=\"\n          {% if flag?(:linux) %}\n            buf.write Pointer(Void).new(thread ? thread.@system_handle : System::Thread.current_handle)\n          {% else %}\n            buf.write thread ? thread.@system_handle : System::Thread.current_handle\n          {% end %}\n          buf.write \":\"\n          buf.write thread.try(&.name) || \"?\"\n\n          if thread && (fiber = thread.current_fiber?)\n            buf.write \" fiber=\"\n            buf.write fiber\n          end\n        {% end %}\n\n        metadata.each do |key, value|\n          buf.write \" \"\n          buf.write key.to_s\n          buf.write \"=\"\n          buf.write value\n        end\n\n        buf.write \"\\n\"\n        System.print(@@handle, buf.to_slice)\n      end\n    end\n\n    def self.trace(section : Tracing::Section, operation : String, time : UInt64? = nil, **metadata, &)\n      if Tracing.enabled?(section)\n        time ||= System::Time.ticks\n        begin\n          yield\n        ensure\n          duration = System::Time.ticks - time\n          Tracing.log(section.to_id, operation, time, **metadata, duration: duration)\n        end\n      else\n        yield\n      end\n    end\n\n    def self.trace(section : Tracing::Section, operation : String, time : UInt64? = nil, **metadata) : Nil\n      if Tracing.enabled?(section)\n        Tracing.log(section.to_id, operation, time || System::Time.ticks, **metadata)\n      end\n    end\n  {% else %}\n    # :nodoc:\n    module Tracing\n      def self.init\n      end\n\n      def self.enabled?(section)\n        false\n      end\n\n      def self.log(section : String, operation : String, time : UInt64, **metadata)\n      end\n    end\n\n    def self.trace(section : Tracing::Section, operation : String, time : UInt64? = nil, **metadata, &)\n      yield\n    end\n\n    def self.trace(section : Tracing::Section, operation : String, time : UInt64? = nil, **metadata)\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/crystal/value_with_finalizer.cr",
    "content": "# :nodoc:\nclass Crystal::ValueWithFinalizer(T)\n  getter value : T\n\n  def initialize(@value : T, @finalizer : T ->)\n  end\n\n  def finalize\n    @finalizer.call(@value)\n  end\nend\n"
  },
  {
    "path": "src/csv/builder.cr",
    "content": "require \"csv\"\n\n# A CSV Builder writes CSV to an `IO`.\n#\n# ```\n# require \"csv\"\n#\n# result = CSV.build do |csv|\n#   # A row can be written by specifying several values\n#   csv.row \"Hello\", 1, 'a', \"String with \\\"quotes\\\"\", '\"', :sym\n#\n#   # Or an enumerable\n#   csv.row [1, 2, 3]\n#\n#   # Or using a block, and appending to the row\n#   csv.row do |row|\n#     # Appending a single value\n#     row << 4\n#\n#     # Or multiple values\n#     row.concat 5, 6\n#\n#     # Or an enumerable\n#     row.concat [7, 8]\n#   end\n# end\n# puts result\n# ```\n#\n# Output:\n#\n# ```text\n# Hello,1,a,\"String with \"\"quotes\"\"\",\"\"\"\",sym\n# 1,2,3\n# 4,5,6,7,8\n# ```\nclass CSV::Builder\n  enum Quoting\n    # No quotes\n    NONE\n\n    # Quotes according to RFC 4180 (default)\n    RFC\n\n    # Always quote\n    ALL\n  end\n\n  # Creates a builder that will write to the given `IO`.\n  def initialize(@io : IO, @separator : Char = DEFAULT_SEPARATOR, @quote_char : Char = DEFAULT_QUOTE_CHAR, @quoting : Quoting = Quoting::RFC)\n    @first_cell_in_row = true\n  end\n\n  # Yields a `CSV::Builder::Row` to append a row. A newline is appended\n  # to `IO` after the block exits.\n  def row(&)\n    yield Row.new(self, @separator, @quote_char, @quoting)\n    @io << '\\n'\n    @first_cell_in_row = true\n  end\n\n  # Appends the given values as a single row, and then a newline.\n  def row(values : Enumerable) : Nil\n    row do |row|\n      values.each do |value|\n        row << value\n      end\n    end\n  end\n\n  # :ditto:\n  def row(*values) : Nil\n    row values\n  end\n\n  # :nodoc:\n  def cell(&)\n    append_cell do\n      yield @io\n    end\n  end\n\n  # :nodoc:\n  def quote_cell(value : String) : Nil\n    append_cell do\n      @io << @quote_char\n      value.each_char do |char|\n        case char\n        when @quote_char\n          @io << @quote_char << @quote_char\n        else\n          @io << char\n        end\n      end\n      @io << @quote_char\n    end\n  end\n\n  private def append_cell(&)\n    @io << @separator unless @first_cell_in_row\n    yield\n    @first_cell_in_row = false\n  end\n\n  # A CSV Row being built.\n  struct Row\n    @builder : Builder\n\n    # :nodoc:\n    def initialize(@builder, @separator : Char = DEFAULT_SEPARATOR, @quote_char : Char = DEFAULT_QUOTE_CHAR, @quoting : Quoting = Quoting::RFC)\n    end\n\n    # Appends the given value to this row.\n    def <<(value : String) : Nil\n      if needs_quotes?(value)\n        @builder.quote_cell value\n      else\n        @builder.cell { |io| io << value }\n      end\n    end\n\n    # :ditto:\n    def <<(value : Nil | Bool | Number) : Nil\n      case @quoting\n      when .all?\n        @builder.cell { |io|\n          io << @quote_char\n          io << value\n          io << @quote_char\n        }\n      else\n        @builder.cell { |io| io << value }\n      end\n    end\n\n    # :ditto:\n    def <<(value) : Nil\n      self << value.to_s\n    end\n\n    # Appends the given values to this row.\n    def concat(values : Enumerable) : Nil\n      values.each do |value|\n        self << value\n      end\n    end\n\n    # :ditto:\n    def concat(*values) : Nil\n      concat values\n    end\n\n    # Appends a comma, thus skipping a cell.\n    def skip_cell : Nil\n      self << nil\n    end\n\n    private def needs_quotes?(value : String)\n      case @quoting\n      when .rfc?\n        value.each_byte do |byte|\n          case byte.unsafe_chr\n          when @separator, @quote_char, '\\n'\n            return true\n          else\n            # keep scanning\n          end\n        end\n        false\n      when .all?\n        true\n      else\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/csv/error.cr",
    "content": "require \"csv\"\n\nclass CSV\n  # Raises when an error related to a CSV is found.\n  class Error < Exception\n  end\n\n  # Raised when an error is encountered during CSV parsing.\n  class MalformedCSVError < Error\n    getter line_number : Int32\n    getter column_number : Int32\n\n    def initialize(message : String, @line_number : Int32, @column_number : Int32)\n      super(\"#{message} at line #{@line_number}, column #{@column_number}\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/csv/lexer/io_based.cr",
    "content": "require \"csv\"\n\n# :nodoc:\nclass CSV::Lexer::IOBased < CSV::Lexer\n  def initialize(@io : IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR)\n    super(separator, quote_char)\n    @current_char = @io.read_char || '\\0'\n  end\n\n  def rewind : Nil\n    super\n    @io.rewind\n    @current_char = @io.read_char || '\\0'\n  end\n\n  private def consume_unquoted_cell\n    @buffer.clear\n    while true\n      case current_char\n      when @separator\n        check_last_empty_column\n        break\n      when '\\r', '\\n', '\\0'\n        break\n      when @quote_char\n        raise \"Unexpected quote\"\n      else\n        @buffer << current_char\n        next_char\n      end\n    end\n    @buffer.to_s\n  end\n\n  private getter current_char\n\n  private def next_char_no_column_increment\n    @current_char = @io.read_char || '\\0'\n  end\n\n  private def consume_string\n    consume_string_with_buffer\n  end\nend\n"
  },
  {
    "path": "src/csv/lexer/string_based.cr",
    "content": "require \"csv\"\n\n# :nodoc:\nclass CSV::Lexer::StringBased < CSV::Lexer\n  def initialize(string : String, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR)\n    super(separator, quote_char)\n    @reader = Char::Reader.new(string)\n    if @reader.current_char == '\\n'\n      @line_number += 1\n      @column_number = 0\n    end\n  end\n\n  def rewind : Nil\n    super\n    @reader.pos = 0\n    if @reader.current_char == '\\n'\n      @line_number += 1\n      @column_number = 0\n    end\n  end\n\n  private def consume_unquoted_cell\n    start_pos = @reader.pos\n    end_pos = start_pos\n    while true\n      case next_char\n      when @separator\n        end_pos = @reader.pos\n        check_last_empty_column\n        break\n      when '\\r', '\\n', '\\0'\n        end_pos = @reader.pos\n        break\n      when @quote_char\n        raise \"Unexpected quote\"\n      end\n    end\n    @reader.string.byte_slice(start_pos, end_pos - start_pos)\n  end\n\n  private def next_char_no_column_increment\n    @reader.next_char\n  end\n\n  private def current_char\n    @reader.current_char\n  end\nend\n"
  },
  {
    "path": "src/csv/lexer.cr",
    "content": "require \"csv\"\n\n# A CSV lexer lets you consume a CSV token by token. You can use this to efficiently\n# parse a CSV without the need to allocate intermediate arrays.\n#\n# ```\n# require \"csv\"\n#\n# lexer = CSV::Lexer.new \"one,two\\nthree\"\n# lexer.next_token # => CSV::Token(@kind=Cell, @value=\"one\")\n# lexer.next_token # => CSV::Token(@kind=Cell, @value=\"two\")\n# lexer.next_token # => CSV::Token(@kind=Newline, @value=\"two\")\n# lexer.next_token # => CSV::Token(@kind=Cell, @value=\"three\")\n# lexer.next_token # => CSV::Token(@kind=Eof, @value=\"three\")\n# ```\nabstract class CSV::Lexer\n  # Creates a CSV lexer from a `String`.\n  def self.new(string : String, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR) : self\n    StringBased.new(string, separator, quote_char)\n  end\n\n  # Creates a CSV lexer from an `IO`.\n  def self.new(io : IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR) : self\n    IOBased.new(io, separator, quote_char)\n  end\n\n  # Returns the current `Token`.\n  getter token : Token\n  getter separator : Char\n  getter quote_char : Char\n\n  # :nodoc:\n  def initialize(@separator : Char = DEFAULT_SEPARATOR, @quote_char : Char = DEFAULT_QUOTE_CHAR)\n    @token = Token.new\n    @buffer = IO::Memory.new\n    @column_number = 1\n    @line_number = 1\n    @last_empty_column = false\n\n    # When the lexer finds \\n or \\r it produces a newline token\n    # but it doesn't eagerly consume the next token. It does this\n    # so that if a CSV is streamed from STDIN or from a socket\n    # the parser will produce a row as soon as a newline is reached,\n    # without having to wait for more content.\n    @last_was_slash_r = false\n    @last_was_slash_n = false\n  end\n\n  # Rewinds this lexer to the beginning\n  def rewind : Nil\n    @column_number = 1\n    @line_number = 1\n    @last_empty_column = false\n    @last_was_slash_r = false\n    @last_was_slash_n = false\n  end\n\n  private abstract def consume_unquoted_cell\n  private abstract def next_char_no_column_increment\n  private abstract def current_char\n\n  # Returns the next `Token` in this CSV.\n  def next_token : CSV::Token\n    if @last_empty_column\n      @last_empty_column = false\n      @token.kind = Token::Kind::Cell\n      @token.value = \"\"\n      return @token\n    end\n\n    if @last_was_slash_r\n      if next_char == '\\n'\n        next_char\n      end\n      @last_was_slash_r = false\n    elsif @last_was_slash_n\n      next_char\n      @last_was_slash_n = false\n    end\n\n    case current_char\n    when '\\0'\n      @token.kind = Token::Kind::Eof\n    when @separator\n      @token.kind = Token::Kind::Cell\n      @token.value = \"\"\n      check_last_empty_column\n    when '\\r'\n      @token.kind = Token::Kind::Newline\n      @last_was_slash_r = true\n    when '\\n'\n      @token.kind = Token::Kind::Newline\n      @last_was_slash_n = true\n    when @quote_char\n      @token.kind = Token::Kind::Cell\n      @token.value = consume_quoted_cell\n    else\n      @token.kind = Token::Kind::Cell\n      @token.value = consume_unquoted_cell\n    end\n    @token\n  end\n\n  private def consume_quoted_cell\n    @buffer.clear\n    while true\n      case char = next_char\n      when '\\0'\n        raise \"Unclosed quote\"\n      when @quote_char\n        case next_char\n        when @separator\n          check_last_empty_column\n          break\n        when '\\r', '\\n', '\\0'\n          break\n        when @quote_char\n          @buffer << @quote_char\n        else\n          raise \"Expecting comma, newline or end, not #{current_char.inspect}\"\n        end\n      else\n        @buffer << char\n      end\n    end\n    @buffer.to_s\n  end\n\n  private def check_last_empty_column\n    case next_char\n    when '\\r', '\\n', '\\0'\n      @last_empty_column = true\n    else\n      # not empty\n    end\n  end\n\n  private def next_char\n    @column_number += 1\n    char = next_char_no_column_increment\n    if char.in?('\\n', '\\r')\n      @column_number = 0\n      @line_number += 1\n    end\n    char\n  end\n\n  private def raise(msg)\n    ::raise CSV::MalformedCSVError.new(msg, @line_number, @column_number)\n  end\nend\n"
  },
  {
    "path": "src/csv/parser.cr",
    "content": "require \"csv\"\n\n# A CSV parser. It lets you consume a CSV row by row.\n#\n# Most of the time `CSV#parse` and `CSV#each_row` are more convenient.\nclass CSV::Parser\n  # Creates a parser from a `String` or `IO`.\n  # Optionally takes the optional *separator* and *quote_char* arguments for\n  # specifying non-standard cell separators and quote characters\n  def initialize(string_or_io : String | IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR)\n    @lexer = CSV::Lexer.new(string_or_io, separator, quote_char)\n    @max_row_size = 3\n  end\n\n  # Returns the remaining rows.\n  def parse : Array(Array(String))\n    rows = [] of Array(String)\n    each_row { |row| rows << row }\n    rows\n  end\n\n  # Yields each of the remaining rows as an `Array(String)`.\n  def each_row(&) : Nil\n    while row = next_row\n      yield row\n    end\n  end\n\n  # Returns an `Iterator` of `Array(String)` for the remaining rows.\n  def each_row : Iterator(Array(String))\n    RowIterator.new(self)\n  end\n\n  # Returns the next row in the CSV, if any, or `nil`.\n  def next_row : Array(String) | Nil\n    token = @lexer.next_token\n    if token.kind == Token::Kind::Eof\n      return nil\n    end\n\n    row = Array(String).new(@max_row_size)\n    next_row_internal(token, row)\n  end\n\n  # Reads the next row into the given *array*.\n  # Returns that same array, if a row was found, or `nil`.\n  def next_row(array : Array(String)) : Array(String) | Nil\n    token = @lexer.next_token\n    if token.kind == Token::Kind::Eof\n      return nil\n    end\n\n    next_row_internal(token, array)\n  end\n\n  # Rewinds this parser to the beginning.\n  def rewind : Nil\n    @lexer.rewind\n  end\n\n  private def next_row_internal(token, row)\n    while true\n      case token.kind\n      when Token::Kind::Cell\n        row << token.value\n        token = @lexer.next_token\n      else # :newline, :eof\n        @max_row_size = row.size if row.size > @max_row_size\n        return row\n      end\n    end\n  end\n\n  private struct RowIterator\n    include Iterator(Array(String))\n\n    @parser : Parser\n\n    def initialize(@parser)\n    end\n\n    def next\n      @parser.next_row || stop\n    end\n  end\nend\n"
  },
  {
    "path": "src/csv/token.cr",
    "content": "require \"csv\"\n\n# A token in a CSV. It consists of a `Kind` and a value.\n# The value only makes sense when the *kind* is `Cell`.\nstruct CSV::Token\n  # Token kinds.\n  enum Kind\n    Cell\n    Newline\n    Eof\n  end\n\n  # The `Kind`.\n  property kind : Kind\n\n  # The string value. Only makes sense for a `Cell`.\n  property value : String\n\n  # :nodoc:\n  def initialize\n    @kind = Kind::Cell\n    @value = \"\"\n  end\nend\n"
  },
  {
    "path": "src/csv.cr",
    "content": "# Provides methods and classes for parsing and generating CSV\n# (comma-separated values) strings.\n#\n# This module conforms to [RFC 4180](https://tools.ietf.org/html/rfc4180).\n#\n# NOTE: To use `CSV` or its children, you must explicitly import it with `require \"csv\"`\n#\n# ### Parsing\n#\n# Several ways of parsing CSV are provided. The most straight-forward, but\n# slow or inefficient for some scenarios, is `CSV#parse`, which returns\n# an array of arrays of all data.\n#\n# Rows can be traversed in a linear fashion with `CSV#each_row`, or\n# using an `Iterator`.\n#\n# To parse a CSV in an efficient way, optionally being able to access\n# row values from header names, create an instance of a `CSV`.\n#\n# ### Parsing with `CSV#new`\n#\n# A CSV instance holds a cursor to the current row in the CSV. The cursor\n# is advanced by invoking `#next`, which returns `true` if a next row was found,\n# and `false` otherwise. A first call to `#next` is required to position the\n# CSV parser in the first row.\n#\n# Once positioned in a row, values can be obtained with the several `#[]` methods,\n# which can accept a header name, column position, or header name pattern as a `Regex`.\n#\n# Additionally, a `Row` object can be obtained with the `#row` method which\n# provides similar methods and can be converted to an `Array` or `Hash`.\n#\n# ### Example\n#\n# ```\n# require \"csv\"\n#\n# csv = CSV.new(\"Name, Age\\nJohn, 20\\nPeter, 30\", headers: true)\n# csv.next # => true\n#\n# csv[\"Name\"]  # => \"John\"\n# csv[0]       # => \"John\"\n# csv[-2]      # => \"John\"\n# csv[/name/i] # => \"John\"\n#\n# csv[\"Age\"] # => \" 20\"\n#\n# csv.row.to_a # => [\"John\", \" 20\"]\n# csv.row.to_h # => {\"Name\" => \"John\", \"Age\" => \" 20\"}\n#\n# csv.next    # => true\n# csv[\"Name\"] # => \"Peter\"\n#\n# csv.next # => false\n# ```\n#\n# ### Building\n#\n# To create CSV data, check `CSV#build` and the `CSV::Builder` class.\nclass CSV\n  DEFAULT_SEPARATOR  = ','\n  DEFAULT_QUOTE_CHAR = '\"'\n\n  # Parses a CSV or `IO` into an array.\n  #\n  # Takes optional *separator* and *quote_char* arguments for defining\n  # non-standard csv cell separators and quote characters.\n  #\n  # ```\n  # require \"csv\"\n  #\n  # CSV.parse(\"one,two\\nthree\")\n  # # => [[\"one\", \"two\"], [\"three\"]]\n  # CSV.parse(\"one;two\\n'three;'\", separator: ';', quote_char: '\\'')\n  # # => [[\"one\", \"two\"], [\"three;\"]]\n  # ```\n  def self.parse(string_or_io : String | IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR) : Array(Array(String))\n    Parser.new(string_or_io, separator, quote_char).parse\n  end\n\n  # Yields each of a CSV's rows as an `Array(String)`.\n  #\n  # See `CSV.parse` about the *separator* and *quote_char* arguments.\n  #\n  # ```\n  # require \"csv\"\n  #\n  # CSV.each_row(\"one,two\\nthree\") do |row|\n  #   puts row\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```\n  # [\"one\", \"two\"]\n  # [\"three\"]\n  # ```\n  def self.each_row(string_or_io : String | IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR, &)\n    Parser.new(string_or_io, separator, quote_char).each_row do |row|\n      yield row\n    end\n  end\n\n  # Returns an `Iterator` of `Array(String)` over a CSV's rows.\n  #\n  # See `CSV.parse` about the *separator* and *quote_char* arguments.\n  #\n  # ```\n  # require \"csv\"\n  #\n  # rows = CSV.each_row(\"one,two\\nthree\")\n  # rows.next # => [\"one\", \"two\"]\n  # rows.next # => [\"three\"]\n  # ```\n  def self.each_row(string_or_io : String | IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR) : Iterator(Array(String))\n    Parser.new(string_or_io, separator, quote_char).each_row\n  end\n\n  # Builds a CSV. This yields a `CSV::Builder` to the given block.\n  #\n  # Takes optional *quoting* argument to define quote behavior.\n  #\n  # ```\n  # require \"csv\"\n  #\n  # result = CSV.build do |csv|\n  #   csv.row \"one\", \"two\"\n  #   csv.row \"three\"\n  # end\n  # result # => \"one,two\\nthree\\n\"\n  # result = CSV.build(quoting: CSV::Builder::Quoting::ALL) do |csv|\n  #   csv.row \"one\", \"two\"\n  #   csv.row \"three\"\n  # end\n  # result # => \"\\\"one\\\",\\\"two\\\"\\n\\\"three\\\"\\n\"\n  # ```\n  #\n  # See: `CSV::Builder::Quoting`\n  def self.build(separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR, quoting : Builder::Quoting = Builder::Quoting::RFC, &) : String\n    String.build do |io|\n      build(io, separator, quote_char, quoting) { |builder| yield builder }\n    end\n  end\n\n  # Appends CSV data to the given `IO`. This yields a `CSV::Builder`\n  # that writes to the given `IO`.\n  #\n  # ```\n  # require \"csv\"\n  #\n  # io = IO::Memory.new\n  # io.puts \"HEADER\"\n  # CSV.build(io) do |csv|\n  #   csv.row \"one\", \"two\"\n  #   csv.row \"three\"\n  # end\n  # io.to_s # => \"HEADER\\none,two\\nthree\\n\"\n  # ```\n  def self.build(io : IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR, quoting : Builder::Quoting = Builder::Quoting::RFC, &) : Nil\n    builder = Builder.new(io, separator, quote_char, quoting)\n    yield builder\n    io.flush\n  end\n\n  @headers : Array(String)?\n  @indices : Hash(String, Int32)?\n\n  # Creates a new instance from the given `String` or `IO`.\n  #\n  # * If *strip* is `true`, row values are stripped with `String#strip` before being\n  # returned from methods.\n  # * If *headers* is `true`, row values can be accessed with header names or patterns.\n  # Headers are always stripped.\n  #\n  # See `CSV.parse` about the *separator* and *quote_char* arguments.\n  def initialize(string_or_io : String | IO, headers : Bool = false, @strip : Bool = false, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR)\n    @parser = Parser.new(string_or_io, separator, quote_char)\n    if headers\n      headers = @parser.next_row || ([] of String)\n      headers = @headers = headers.map &.strip\n      indices = @indices = {} of String => Int32\n      headers.each_with_index do |header, index|\n        indices.put_if_absent(header, index)\n      end\n    end\n    @traversed = false\n  end\n\n  # Creates a new instance from the given `String` or `IO`, and yields it to\n  # the given block once for each row in the CSV.\n  #\n  # * If *strip* is `true`, row values are stripped with `String#strip` before being\n  # returned from methods.\n  # * If *headers* is `true`, row values can be accessed with header names or patterns.\n  # Headers are always stripped.\n  #\n  # See `CSV.parse` about the *separator* and *quote_char* arguments.\n  def self.new(string_or_io : String | IO, headers = false, strip = false, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR, &)\n    csv = new(string_or_io, headers, strip, separator, quote_char)\n    csv.each do\n      yield csv\n    end\n    csv\n  end\n\n  # Returns this CSV headers. Their values are always stripped.\n  # Raises `CSV::Error` if headers were not requested.\n  def headers : Array(String)\n    @headers || raise(Error.new(\"Headers not requested\"))\n  end\n\n  # Invokes the block once for each row in this CSV, yielding `self`.\n  def each(&) : Nil\n    while self.next\n      yield self\n    end\n  end\n\n  # Advanced the cursor to the next row. Must be called once to position\n  # the cursor in the first row. Returns `true` if a next row was found,\n  # `false` otherwise.\n  def next : Bool\n    return false if @traversed\n\n    row = @row ||= [] of String\n    row.clear\n    @row = @parser.next_row(row)\n    if @row\n      true\n    else\n      @traversed = true\n      false\n    end\n  end\n\n  # Returns the current row's value corresponding to the given *header* name.\n  # Raises `KeyError` if no such header exists.\n  # Raises `CSV::Error` if headers were not requested.\n  def [](header : String) : String\n    row_internal[header]\n  end\n\n  # Returns the current row's value corresponding to the given *header* name.\n  # Returns `nil` if no such header exists.\n  # Raises `CSV::Error` if headers were not requested.\n  def []?(header : String) : String?\n    row_internal[header]?\n  end\n\n  # Returns the current row's value at the given column index.\n  # A negative index counts from the end.\n  # Raises `IndexError` if no such column exists.\n  def [](column : Int) : String\n    row_internal[column]\n  end\n\n  # Returns the current row's value at the given column index.\n  # A negative index counts from the end.\n  # Returns `nil` if no such column exists.\n  def []?(column : Int) : String?\n    row_internal[column]?\n  end\n\n  # Returns the current row's value corresponding to the given *header_pattern*.\n  # Raises `KeyError` if no such header exists.\n  # Raises `CSV::Error` if headers were not requested.\n  def [](header_pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    row_internal[header_pattern, options: options]\n  end\n\n  # Returns the current row's value corresponding to the given *header_pattern*.\n  # Returns `nil` if no such header exists.\n  # Raises `CSV::Error` if headers were not requested.\n  def []?(header_pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String?\n    row_internal[header_pattern, options: options]?\n  end\n\n  # Returns a tuple of the current row's values at given indices\n  # A negative index counts from the end.\n  # Raises `IndexError` if any column doesn't exist\n  # The behavior of returning a tuple is similar to `Hash#values_at`\n  def values_at(*columns : Int)\n    columns.map { |column| row_internal[column] }\n  end\n\n  # Returns a tuple of the current row's values corresponding to the given *headers*\n  # Raises `KeyError` if any header doesn't exist.\n  # Raises `CSV::Error` if headers were not requested\n  # The behavior of returning a tuple is similar to `Hash#values_at`\n  def values_at(*headers : String)\n    headers.map { |header| row_internal[header] }\n  end\n\n  # Returns the current row as a `Row` instance.\n  def row : Row\n    Row.new(self, current_row.dup)\n  end\n\n  # Rewinds this CSV to the beginning, rewinding the underlying IO if any.\n  def rewind : Nil\n    @parser.rewind\n    @parser.next_row if @headers\n    @traversed = false\n  end\n\n  private def row_internal\n    Row.new(self, current_row)\n  end\n\n  private def current_row\n    row = @row\n    return row if row\n\n    if @traversed\n      raise Error.new(\"After last row\")\n    else\n      raise Error.new(\"Before first row\")\n    end\n  end\n\n  # :nodoc:\n  def indices : Hash(String, Int32)\n    @indices || raise(Error.new(\"Headers not requested\"))\n  end\n\n  # :nodoc:\n  getter? strip\n\n  # :nodoc:\n  def headers? : Array(String)?\n    @headers\n  end\n\n  # A Row of a `CSV::WithHeaders` instance.\n  struct Row\n    private getter csv : CSV\n    @row : Array(String)\n\n    # :nodoc:\n    def initialize(@csv, @row)\n    end\n\n    # Returns the current row's value corresponding to the given *header* name.\n    # Raises `KeyError` if no such header exists.\n    # Raises `CSV::Error` if headers were not requested.\n    def [](header : String) : String\n      value = self.[]?(header)\n      raise KeyError.new(\"Missing header: #{header}\") unless value\n      value\n    end\n\n    # Returns this row's value corresponding to the given *header* name.\n    # Returns `nil` if no such header exists.\n    # Raises `CSV::Error` if headers were not requested.\n    def []?(header : String) : String?\n      index = csv.indices[header]?\n      if index\n        maybe_strip(@row[index]? || \"\")\n      else\n        nil\n      end\n    end\n\n    # Returns this row's value at the given column index.\n    # A negative index counts from the end.\n    # Raises `IndexError` if no such column exists.\n    def [](column : Int) : String\n      value = self.[]?(column)\n      raise IndexError.new(\"Missing column index: #{column}\") unless value\n      value\n    end\n\n    # Returns this row's value at the given column index.\n    # A negative index counts from the end.\n    # Returns `nil` if no such column exists.\n    def []?(column : Int) : String?\n      size = csv.headers?.try(&.size) || @row.size\n\n      column += size if column < 0\n\n      value = @row[column]?\n      value ||= \"\" if 0 <= column < size\n      value ? maybe_strip(value) : nil\n    end\n\n    # Returns this row's value corresponding to the given *header_pattern*.\n    # Raises `KeyError` if no such header exists.\n    # Raises `CSV::Error` if headers were not requested.\n    def [](header_pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n      value = self.[]?(header_pattern, options: options)\n      raise KeyError.new(\"Missing header pattern: #{header_pattern}\") unless value\n      value\n    end\n\n    # Returns this row's value corresponding to the given *header_pattern*.\n    # Returns `nil` if no such header exists.\n    # Raises `CSV::Error` if headers were not requested.\n    def []?(header_pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String?\n      csv.headers.each_with_index do |header, i|\n        if header.matches?(header_pattern, options: options)\n          return maybe_strip(@row[i]? || \"\")\n        end\n      end\n      nil\n    end\n\n    # Returns the number of columns in this row, regardless of the number\n    # of headers (if requested).\n    def size : Int32\n      @row.size\n    end\n\n    # Converts this row to an `Array`.\n    def to_a : Array(String)\n      @row.map!(&.strip) if csv.strip?\n      @row\n    end\n\n    # Converts this row to a `Hash`.\n    def to_h : Hash(String, String)\n      h = {} of String => String\n      csv.headers.each_with_index do |header, i|\n        h[header] = maybe_strip(@row[i]? || \"\")\n      end\n      h\n    end\n\n    # Returns a tuple of this row's values at given indices\n    # A negative index counts from the end.\n    # Raises `IndexError` if any column doesn't exist\n    # The behavior of returning a tuple is similar to `Hash#values_at`\n    def values_at(*columns : Int)\n      columns.map { |column| self[column] }\n    end\n\n    # Returns a tuple of this row's values corresponding to the given *headers*\n    # Raises `KeyError` if any header doesn't exist.\n    # Raises `CSV::Error` if headers were not requested\n    # The behavior of returning a tuple is similar to `Hash#values_at`\n    def values_at(*headers : String)\n      headers.map { |header| self[header] }\n    end\n\n    private def maybe_strip(value)\n      csv.strip? ? value.strip : value\n    end\n  end\nend\n\nrequire \"./csv/**\"\n"
  },
  {
    "path": "src/deque.cr",
    "content": "# A Deque (\"[double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue)\") is a collection of objects of type\n# T that behaves much like an Array.\n#\n# Deque has a subset of Array's API. It performs better than an `Array` when there are frequent insertions or deletions\n# of items near the beginning or the end.\n#\n# The most typical use case of a Deque is a queue: use `push` to add items to the end of the queue and `shift` to get\n# and remove the item at the beginning of the queue.\n#\n# This Deque is implemented with a [dynamic array](http://en.wikipedia.org/wiki/Dynamic_array) used as a\n# [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer).\nclass Deque(T)\n  include Indexable::Mutable(T)\n\n  # This Deque is based on a circular buffer. It works like a normal array, but when an item is removed from the left\n  # side, instead of shifting all the items, only the start position is shifted. This can lead to configurations like:\n  # [234---01] @start = 6, size = 5, @capacity = 8\n  # (this Deque has 5 items, each equal to their index)\n\n  @start = 0\n  protected setter size\n  protected getter buffer\n  protected getter capacity\n\n  # Creates a new empty Deque\n  def initialize\n    @size = 0\n    @capacity = 0\n    @buffer = Pointer(T).null\n  end\n\n  # Creates a new empty `Deque` backed by a buffer that is initially `initial_capacity` big.\n  #\n  # The `initial_capacity` is useful to avoid unnecessary reallocations of the internal buffer in case of growth. If you\n  # have an estimate of the maximum number of elements a deque will hold, you should initialize it with that capacity\n  # for improved execution performance.\n  #\n  # ```\n  # deq = Deque(Int32).new(5)\n  # deq.size # => 0\n  # ```\n  def initialize(initial_capacity : Int)\n    if initial_capacity < 0\n      raise ArgumentError.new(\"Negative deque capacity: #{initial_capacity}\")\n    end\n    @size = 0\n    @capacity = initial_capacity.to_i\n\n    if @capacity == 0\n      @buffer = Pointer(T).null\n    else\n      @buffer = Pointer(T).malloc(@capacity)\n    end\n  end\n\n  # Creates a new `Deque` of the given size filled with the same value in each position.\n  #\n  # ```\n  # Deque.new(3, 'a') # => Deque{'a', 'a', 'a'}\n  # ```\n  def initialize(size : Int, value : T)\n    if size < 0\n      raise ArgumentError.new(\"Negative deque size: #{size}\")\n    end\n    @size = size.to_i\n    @capacity = size.to_i\n\n    if @capacity == 0\n      @buffer = Pointer(T).null\n    else\n      @buffer = Pointer(T).malloc(@capacity, value)\n    end\n  end\n\n  # Creates a new `Deque` of the given size and invokes the block once for\n  # each index of the deque, assigning the block's value in that index.\n  #\n  # ```\n  # Deque.new(3) { |i| (i + 1) ** 2 } # => Deque{1, 4, 9}\n  # ```\n  def self.new(size : Int, & : Int32 -> T)\n    if size < 0\n      raise ArgumentError.new(\"Negative deque size: #{size}\")\n    end\n\n    deque = Deque(T).new(size)\n    deque.size = size\n    size.to_i.times do |i|\n      deque.buffer[i] = yield i\n    end\n    deque\n  end\n\n  # Creates a new `Deque` that copies its items from an Array.\n  #\n  # ```\n  # Deque.new([1, 2, 3]) # => Deque{1, 2, 3}\n  # ```\n  def self.new(array : Array(T))\n    Deque(T).new(array.size) { |i| array[i] }\n  end\n\n  # Returns `true` if it is passed a `Deque` and `equals?` returns `true`\n  # for both deques, the caller and the argument.\n  #\n  # ```\n  # deq = Deque{2, 3}\n  # deq.unshift 1\n  # deq == Deque{1, 2, 3} # => true\n  # deq == Deque{2, 3}    # => false\n  # ```\n  def ==(other : Deque)\n    equals?(other) { |x, y| x == y }\n  end\n\n  # Concatenation. Returns a new `Deque` built by concatenating\n  # two deques together to create a third. The type of the new deque\n  # is the union of the types of both the other deques.\n  def +(other : Deque(U)) forall U\n    Deque(T | U).new.concat(self).concat(other)\n  end\n\n  # :nodoc:\n  def +(other : Deque(T))\n    dup.concat other\n  end\n\n  # Returns the additive identity of this type.\n  #\n  # This is an empty deque.\n  def self.additive_identity : self\n    self.new\n  end\n\n  # Alias for `push`.\n  def <<(value : T)\n    push(value)\n  end\n\n  def unsafe_fetch(index : Int) : T\n    index += @start\n    index -= @capacity if index >= @capacity\n    @buffer[index]\n  end\n\n  def unsafe_put(index : Int, value : T)\n    index += @start\n    index -= @capacity if index >= @capacity\n    @buffer[index] = value\n  end\n\n  # Removes all elements from `self`.\n  def clear\n    Deque.half_slices(self) do |slice|\n      slice.to_unsafe.clear(slice.size)\n    end\n    @size = 0\n    @start = 0\n    self\n  end\n\n  # Returns a new `Deque` that has this deque's elements cloned.\n  # That is, it returns a deep copy of this deque.\n  #\n  # Use `#dup` if you want a shallow copy.\n  def clone\n    {% if T == ::Bool || T == ::Char || T == ::String || T == ::Symbol || T < ::Number::Primitive %}\n      Deque(T).new(size) { |i| self[i].clone.as(T) }\n    {% else %}\n      exec_recursive_clone do |hash|\n        clone = Deque(T).new(size)\n        hash[object_id] = clone.object_id\n        each do |element|\n          clone << element.clone\n        end\n        clone\n      end\n    {% end %}\n  end\n\n  # Appends the elements of *other* to `self`, and returns `self`.\n  #\n  # ```\n  # deq = Deque{\"a\", \"b\"}\n  # deq.concat(Deque{\"c\", \"d\"})\n  # deq # => Deque{\"a\", \"b\", \"c\", \"d\"}\n  # ```\n  def concat(other : Indexable) : self\n    other_size = other.size\n\n    resize_if_cant_insert(other_size)\n\n    index = @start + @size\n    index -= @capacity if index >= @capacity\n    concat_indexable(other, index)\n\n    @size += other_size\n\n    self\n  end\n\n  private def concat_indexable(other : Deque, index)\n    Deque.half_slices(other) do |slice|\n      index = concat_indexable(slice, index)\n    end\n  end\n\n  private def concat_indexable(other : Array | StaticArray, index)\n    concat_indexable(Slice.new(other.to_unsafe, other.size), index)\n  end\n\n  private def concat_indexable(other : Slice, index)\n    if index + other.size <= @capacity\n      # there is enough space after the last element; one copy will suffice\n      (@buffer + index).copy_from(other.to_unsafe, other.size)\n      index += other.size\n      index == @capacity ? 0 : index\n    else\n      # copy the first half of *other* to the end of the buffer, and then the\n      # remaining half to the start, which must be available after a call to\n      # `#resize_if_cant_insert`\n      first_half_size = @capacity - index\n      second_half_size = other.size - first_half_size\n      (@buffer + index).copy_from(other.to_unsafe, first_half_size)\n      @buffer.copy_from(other.to_unsafe + first_half_size, second_half_size)\n      second_half_size\n    end\n  end\n\n  private def concat_indexable(other, index)\n    appender = (@buffer + index).appender\n    buffer_end = @buffer + @capacity\n    other.each do |elem|\n      appender << elem\n      appender = @buffer.appender if appender.pointer == buffer_end\n    end\n  end\n\n  # :ditto:\n  def concat(other : Enumerable(T)) : self\n    other.each do |x|\n      push x\n    end\n    self\n  end\n\n  # Removes all items from `self` that are equal to *obj*.\n  #\n  # ```\n  # a = Deque{\"a\", \"b\", \"b\", \"b\", \"c\"}\n  # a.delete(\"b\") # => true\n  # a             # => Deque{\"a\", \"c\"}\n  # ```\n  def delete(obj) : Bool\n    match = internal_delete { |i| i == obj }\n    !match.nil?\n  end\n\n  # Modifies `self`, keeping only the elements in the collection for which the\n  # passed block is truthy. Returns `self`.\n  #\n  # ```\n  # a = Deque{1, 6, 2, 4, 8}\n  # a.select! { |x| x > 3 }\n  # a # => Deque{6, 4, 8}\n  # ```\n  #\n  # See also: `Deque#select`.\n  def select!(& : T ->) : self\n    reject! { |elem| !yield(elem) }\n  end\n\n  # Modifies `self`, keeping only the elements in the collection for which\n  # `pattern === element`.\n  #\n  # ```\n  # ary = [1, 6, 2, 4, 8]\n  # ary.select!(3..7)\n  # ary # => [6, 4]\n  # ```\n  #\n  # See also: `Deque#select`.\n  def select!(pattern) : self\n    self.select! { |elem| pattern === elem }\n  end\n\n  # Modifies `self`, deleting the elements in the collection for which the\n  # passed block is truthy. Returns `self`.\n  #\n  # ```\n  # a = Deque{1, 6, 2, 4, 8}\n  # a.reject! { |x| x > 3 }\n  # a # => Deque{1, 2}\n  # ```\n  #\n  # See also: `Deque#reject`.\n  def reject!(& : T ->) : self\n    internal_delete { |e| yield e }\n    self\n  end\n\n  # Modifies `self`, deleting the elements in the collection for which\n  # `pattern === element`.\n  #\n  # ```\n  # a = Deque{1, 6, 2, 4, 8}\n  # a.reject!(3..7)\n  # a # => Deque{1, 2, 8}\n  # ```\n  #\n  # See also: `Deque#reject`.\n  def reject!(pattern) : self\n    reject! { |elem| pattern === elem }\n    self\n  end\n\n  # `reject!` and `delete` implementation:\n  # returns the last matching element, or nil\n  private def internal_delete(&)\n    match = nil\n    i = 0\n    while i < @size\n      e = self[i]\n      if yield e\n        match = e\n        delete_at(i)\n      else\n        i += 1\n      end\n    end\n    match\n  end\n\n  # Deletes the item that is present at the *index*. Items to the right\n  # of this one will have their indices decremented.\n  # Raises `IndexError` if trying to delete an element outside the deque's range.\n  #\n  # ```\n  # a = Deque{1, 2, 3}\n  # a.delete_at(1) # => 2\n  # a              # => Deque{1, 3}\n  # ```\n  def delete_at(index : Int) : T\n    if index < 0\n      index += @size\n    end\n    unless 0 <= index < @size\n      raise IndexError.new\n    end\n    return shift if index == 0\n    return pop if index == @size - 1\n\n    rindex = @start + index\n    rindex -= @capacity if rindex >= @capacity\n    value = @buffer[rindex]\n\n    if index > @size // 2\n      # Move following items to the left, starting with the first one\n      # [56-01234] -> [6x-01235]\n      dst = rindex\n      finish = (@start + @size - 1) % @capacity\n      loop do\n        src = dst + 1\n        src -= @capacity if src >= @capacity\n        @buffer[dst] = @buffer[src]\n        break if src == finish\n        dst = src\n      end\n      (@buffer + finish).clear\n    else\n      # Move preceding items to the right, starting with the last one\n      # [012345--] -> [x01345--]\n      dst = rindex\n      finish = @start\n      @start += 1\n      @start -= @capacity if @start >= @capacity\n      loop do\n        src = dst - 1\n        src += @capacity if src < 0\n        @buffer[dst] = @buffer[src]\n        break if src == finish\n        dst = src\n      end\n      (@buffer + finish).clear\n    end\n\n    @size -= 1\n    value\n  end\n\n  # Returns a new `Deque` that has exactly this deque's elements.\n  # That is, it returns a shallow copy of this deque.\n  def dup\n    Deque(T).new(size) { |i| self[i].as(T) }\n  end\n\n  # Yields each item in this deque, from first to last.\n  #\n  # Do not modify the deque while using this variant of `each`!\n  def each(& : T ->) : Nil\n    Deque.half_slices(self) do |slice|\n      slice.each do |elem|\n        yield elem\n      end\n    end\n  end\n\n  # Insert a new item before the item at *index*. Items to the right\n  # of this one will have their indices incremented.\n  #\n  # ```\n  # a = Deque{0, 1, 2}\n  # a.insert(1, 7) # => Deque{0, 7, 1, 2}\n  # ```\n  def insert(index : Int, value : T) : self\n    if index < 0\n      index += @size + 1\n    end\n    unless 0 <= index <= @size\n      raise IndexError.new\n    end\n    return unshift(value) if index == 0\n    return push(value) if index == @size\n\n    resize_if_cant_insert\n    rindex = @start + index\n    rindex -= @capacity if rindex >= @capacity\n\n    if index > @size // 2\n      # Move following items to the right, starting with the last one\n      # [56-01234] -> [4560123^]\n      dst = @start + @size\n      dst -= @capacity if dst >= @capacity\n      loop do\n        src = dst - 1\n        src += @capacity if src < 0\n        @buffer[dst] = @buffer[src]\n        break if src == rindex\n        dst = src\n      end\n    else\n      # Move preceding items to the left, starting with the first one\n      # [01234---] -> [1^234--0]\n      @start -= 1\n      @start += @capacity if @start < 0\n      rindex -= 1\n      rindex += @capacity if rindex < 0\n      dst = @start\n      loop do\n        src = dst + 1\n        src -= @capacity if src >= @capacity\n        @buffer[dst] = @buffer[src]\n        break if src == rindex\n        dst = src\n      end\n    end\n\n    @size += 1\n    @buffer[rindex] = value\n    self\n  end\n\n  def inspect(io : IO) : Nil\n    executed = exec_recursive(:inspect) do\n      io << \"Deque{\"\n      join io, \", \", &.inspect(io)\n      io << '}'\n    end\n    io << \"Deque{...}\" unless executed\n  end\n\n  def pretty_print(pp)\n    executed = exec_recursive(:inspect) do\n      pp.list(\"Deque{\", self, \"}\")\n    end\n    pp.text \"Deque{...}\" unless executed\n  end\n\n  # Returns the number of elements in the deque.\n  #\n  # ```\n  # Deque{:foo, :bar}.size # => 2\n  # ```\n  def size : Int32\n    @size\n  end\n\n  # Removes and returns the last item. Raises `IndexError` if empty.\n  #\n  # ```\n  # a = Deque{1, 2, 3}\n  # a.pop # => 3\n  # a     # => Deque{1, 2}\n  # ```\n  def pop : T\n    pop { raise IndexError.new }\n  end\n\n  # Removes and returns the last item, if not empty, otherwise executes\n  # the given block and returns its value.\n  def pop(&)\n    if @size == 0\n      yield\n    else\n      @size -= 1\n      index = @start + @size\n      index -= @capacity if index >= @capacity\n      value = @buffer[index]\n      (@buffer + index).clear\n      value\n    end\n  end\n\n  # Removes and returns the last item, if not empty, otherwise `nil`.\n  def pop? : T?\n    pop { nil }\n  end\n\n  # Removes the last *n* (at most) items in the deque.\n  def pop(n : Int) : Nil\n    if n < 0\n      raise ArgumentError.new(\"Can't pop negative count\")\n    end\n    n = Math.min(n, @size)\n    n.times { pop }\n    nil\n  end\n\n  # Adds an item to the end of the deque.\n  #\n  # ```\n  # a = Deque{1, 2}\n  # a.push 3 # => Deque{1, 2, 3}\n  # ```\n  def push(value : T)\n    resize_if_cant_insert\n    index = @start + @size\n    index -= @capacity if index >= @capacity\n    @buffer[index] = value\n    @size += 1\n    self\n  end\n\n  # :inherit:\n  def rotate!(n : Int = 1) : Nil\n    return if @size <= 1\n    if @size == @capacity\n      @start = (@start + n) % @capacity\n    else\n      # Turn *n* into an equivalent index in range -size/2 .. size/2\n      half = @size // 2\n      if n.abs >= half\n        n = (n + half) % @size - half\n      end\n      while n > 0\n        push(shift)\n        n -= 1\n      end\n      while n < 0\n        n += 1\n        unshift(pop)\n      end\n    end\n  end\n\n  # Removes and returns the first item. Raises `IndexError` if empty.\n  #\n  # ```\n  # a = Deque{1, 2, 3}\n  # a.shift # => 1\n  # a       # => Deque{2, 3}\n  # ```\n  def shift\n    shift { raise IndexError.new }\n  end\n\n  # Removes and returns the first item, if not empty, otherwise executes\n  # the given block and returns its value.\n  def shift(&)\n    if @size == 0\n      yield\n    else\n      value = @buffer[@start]\n      (@buffer + @start).clear\n      @size -= 1\n      @start += 1\n      @start -= @capacity if @start >= @capacity\n      value\n    end\n  end\n\n  # Removes and returns the first item, if not empty, otherwise `nil`.\n  def shift?\n    shift { nil }\n  end\n\n  # Removes the first *n* (at most) items in the deque.\n  def shift(n : Int) : Nil\n    if n < 0\n      raise ArgumentError.new(\"Can't shift negative count\")\n    end\n    n = Math.min(n, @size)\n    n.times { shift }\n    nil\n  end\n\n  def to_s(io : IO) : Nil\n    inspect(io)\n  end\n\n  # Adds an item to the beginning of the deque.\n  #\n  # ```\n  # a = Deque{1, 2}\n  # a.unshift 0 # => Deque{0, 1, 2}\n  # ```\n  def unshift(value : T) : self\n    resize_if_cant_insert\n    @start -= 1\n    @start += @capacity if @start < 0\n    @buffer[@start] = value\n    @size += 1\n    self\n  end\n\n  # :nodoc:\n  def self.half_slices(deque : Deque, &)\n    # For [----] yields nothing\n    # For contiguous [-012] yields @buffer[1...4]\n    # For separated [234---01] yields @buffer[6...8], @buffer[0...3]\n\n    return if deque.empty?\n    a = deque.@start\n    b = deque.@start + deque.size\n    b -= deque.capacity if b > deque.capacity\n    if a < b\n      # TODO: this `typeof` is a workaround for 1.0.0; remove it eventually\n      yield Slice(typeof(deque.buffer.value)).new(deque.buffer + a, deque.size)\n    else\n      yield Slice(typeof(deque.buffer.value)).new(deque.buffer + a, deque.capacity - a)\n      yield Slice(typeof(deque.buffer.value)).new(deque.buffer, b)\n    end\n  end\n\n  private INITIAL_CAPACITY = 4\n\n  # behaves like `calculate_new_capacity(@capacity + 1)`\n  private def calculate_new_capacity\n    return INITIAL_CAPACITY if @capacity == 0\n\n    @capacity * 2\n  end\n\n  private def calculate_new_capacity(new_size)\n    new_capacity = @capacity == 0 ? INITIAL_CAPACITY : @capacity\n    while new_capacity < new_size\n      new_capacity *= 2\n    end\n    new_capacity\n  end\n\n  # behaves like `resize_if_cant_insert(1)`\n  private def resize_if_cant_insert\n    if @size >= @capacity\n      resize_to_capacity(calculate_new_capacity)\n    end\n  end\n\n  private def resize_if_cant_insert(insert_size)\n    new_capacity = calculate_new_capacity(@size + insert_size)\n    if new_capacity > @capacity\n      resize_to_capacity(new_capacity)\n    end\n  end\n\n  private def resize_to_capacity(capacity)\n    old_capacity, @capacity = @capacity, capacity\n\n    unless @buffer\n      @buffer = Pointer(T).malloc(@capacity)\n      return\n    end\n\n    @buffer = @buffer.realloc(@capacity)\n\n    finish = @start + @size\n    if finish > old_capacity\n      # If the deque is separated into two parts, we get something like [2301----] after resize, so additional action is\n      # needed, to turn it into [23----01] or [--0123--].\n      # To do the moving we can use `copy_from` because the old and new locations will never overlap (assuming we're\n      # multiplying the capacity by 2 or more). Due to the same assumption, we can clear all of the old locations.\n      finish -= old_capacity\n      if old_capacity - @start >= @start\n        # [3012----] -> [-0123---]\n        (@buffer + old_capacity).copy_from(@buffer, finish)\n        @buffer.clear(finish)\n      else\n        # [1230----] -> [123----0]\n        to_move = old_capacity - @start\n        new_start = @capacity - to_move\n        (@buffer + new_start).copy_from(@buffer + @start, to_move)\n        (@buffer + @start).clear(to_move)\n        @start = new_start\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/digest/adler32.cr",
    "content": "require \"lib_z\"\nrequire \"./digest\"\n\n# Implements the Adler32 checksum algorithm.\n#\n# NOTE: To use `Adler32`, you must explicitly import it with `require \"digest/adler32\"`\nclass Digest::Adler32 < ::Digest\n  extend ClassMethods\n\n  @digest : UInt32\n\n  def initialize\n    @digest = Adler32.initial\n  end\n\n  def self.initial : UInt32\n    LibZ.adler32(0, nil, 0).to_u32\n  end\n\n  def self.checksum(data) : UInt32\n    update(data, initial)\n  end\n\n  def self.update(data, adler32 : UInt32) : UInt32\n    update data.to_slice, adler32\n  end\n\n  def self.update(data : Bytes, adler32 : UInt32) : UInt32\n    LibZ.adler32(adler32, data, data.size).to_u32\n  end\n\n  def self.combine(adler1 : UInt32, adler2 : UInt32, len : Int32) : UInt32\n    LibZ.adler32_combine(adler1, adler2, len).to_u32\n  end\n\n  # :nodoc:\n  def update_impl(data : Bytes) : Nil\n    @digest = Adler32.update(data, @digest)\n  end\n\n  # :nodoc:\n  def final_impl(dst : Bytes) : Nil\n    dst[0] = (@digest >> 24).to_u8!\n    dst[1] = (@digest >> 16).to_u8!\n    dst[2] = (@digest >> 8).to_u8!\n    dst[3] = (@digest).to_u8!\n  end\n\n  # :nodoc:\n  def reset_impl : Nil\n    @digest = Adler32.initial\n  end\n\n  # :nodoc:\n  def digest_size : Int32\n    4\n  end\nend\n"
  },
  {
    "path": "src/digest/crc32.cr",
    "content": "require \"lib_z\"\nrequire \"./digest\"\n\n# Implements the CRC32 checksum algorithm.\n#\n# NOTE: To use `CRC32`, you must explicitly import it with `require \"digest/crc32\"`\nclass Digest::CRC32 < ::Digest\n  extend ClassMethods\n\n  @digest : UInt32\n\n  def initialize\n    @digest = CRC32.initial\n  end\n\n  def self.initial : UInt32\n    LibZ.crc32(0, nil, 0).to_u32\n  end\n\n  def self.checksum(data) : UInt32\n    update(data, initial)\n  end\n\n  def self.update(data, crc32 : UInt32) : UInt32\n    update data.to_slice, crc32\n  end\n\n  def self.update(data : Bytes, crc32 : UInt32) : UInt32\n    LibZ.crc32(crc32, data, data.size).to_u32\n  end\n\n  def self.combine(crc1 : UInt32, crc2 : UInt32, len : Int32) : UInt32\n    LibZ.crc32_combine(crc1, crc2, len).to_u32\n  end\n\n  # :nodoc:\n  def update_impl(data : Bytes) : Nil\n    @digest = CRC32.update(data, @digest)\n  end\n\n  # :nodoc:\n  def final_impl(dst : Bytes) : Nil\n    dst[0] = (@digest >> 24).to_u8!\n    dst[1] = (@digest >> 16).to_u8!\n    dst[2] = (@digest >> 8).to_u8!\n    dst[3] = (@digest).to_u8!\n  end\n\n  # :nodoc:\n  def reset_impl : Nil\n    @digest = CRC32.initial\n  end\n\n  # :nodoc:\n  def digest_size : Int32\n    4\n  end\nend\n"
  },
  {
    "path": "src/digest/digest.cr",
    "content": "require \"base64\"\n\n# `Digest` is the base type of hashing algorithms like `Digest::MD5`,\n# `Digest::SHA1`, `Digest::SHA256`, or `Digest::SHA512`.\n#\n# A `Digest` instance holds the state of an ongoing hash calculation.\n# It can receive new data to include in the hash via `#update`, `#<<`, or `#file`.\n# Once all data is included, use `#final` or `#hexfinal` to get the hash. This will mark the\n# ongoing calculation as finished. A finished calculation can't receive new data.\n#\n# A `digest.dup.final` call may be used to get an intermediate hash value.\n#\n# Use `#reset` to reuse the `Digest` instance for a new calculation.\nabstract class Digest\n  class FinalizedError < Exception\n  end\n\n  # The `Digest::ClassMethods` module is used in the concrete subclass of `Digest`\n  # that does not require arguments in its construction.\n  #\n  # The modules adds convenient class methods as `Digest::MD5.digest`, `Digest::MD5.hexdigest`.\n  module ClassMethods\n    # Returns the hash of *data*. *data* must respond to `#to_slice`.\n    def digest(data) : Bytes\n      digest do |ctx|\n        ctx.update(data.to_slice)\n      end\n    end\n\n    # Yields an instance of `self` which can receive calls to `#update(data : String | Bytes)`\n    # and returns the finalized digest afterwards.\n    #\n    # ```\n    # require \"digest/md5\"\n    #\n    # digest = Digest::MD5.digest do |ctx|\n    #   ctx.update \"f\"\n    #   ctx.update \"oo\"\n    # end\n    # digest.hexstring # => \"acbd18db4cc2f85cedef654fccc4a4d8\"\n    # ```\n    def digest(& : self ->) : Bytes\n      context = new\n      yield context\n      context.final\n    end\n\n    # Returns the hexadecimal representation of the hash of *data*.\n    #\n    # ```\n    # require \"digest/md5\"\n    #\n    # Digest::MD5.hexdigest(\"foo\") # => \"acbd18db4cc2f85cedef654fccc4a4d8\"\n    # ```\n    def hexdigest(data) : String\n      hexdigest &.update(data)\n    end\n\n    # Yields a context object with an `#update(data : String | Bytes)`\n    # method available. Returns the resulting digest in hexadecimal representation\n    # afterwards.\n    #\n    # ```\n    # require \"digest/md5\"\n    #\n    # Digest::MD5.hexdigest(\"foo\") # => \"acbd18db4cc2f85cedef654fccc4a4d8\"\n    # Digest::MD5.hexdigest do |ctx|\n    #   ctx.update \"f\"\n    #   ctx.update \"oo\"\n    # end\n    # # => \"acbd18db4cc2f85cedef654fccc4a4d8\"\n    # ```\n    def hexdigest(& : self ->) : String\n      hashsum = digest do |ctx|\n        yield ctx\n      end\n\n      hashsum.to_slice.hexstring\n    end\n\n    # Returns the base64-encoded hash of *data*.\n    #\n    # ```\n    # require \"digest/sha1\"\n    #\n    # Digest::SHA1.base64digest(\"foo\") # => \"C+7Hteo/D9vJXQ3UfzxbwnXaijM=\"\n    # ```\n    def base64digest(data) : String\n      base64digest &.update(data)\n    end\n\n    # Yields a context object with an `#update(data : String | Bytes)`\n    # method available. Returns the resulting digest in base64 representation\n    # afterwards.\n    #\n    # ```\n    # require \"digest/sha1\"\n    #\n    # Digest::SHA1.base64digest do |ctx|\n    #   ctx.update \"f\"\n    #   ctx.update \"oo\"\n    # end\n    # # => \"C+7Hteo/D9vJXQ3UfzxbwnXaijM=\"\n    # ```\n    def base64digest(& : self -> _) : String\n      hashsum = digest do |ctx|\n        yield ctx\n      end\n\n      Base64.strict_encode(hashsum)\n    end\n  end\n\n  @finished = false\n\n  def update(data) : self\n    update data.to_slice\n  end\n\n  def update(data : Bytes) : self\n    check_finished\n    update_impl data\n    self\n  end\n\n  # Returns the final digest output.\n  #\n  # `final` or `hexfinal` can only be called once and raises `FinalizedError` on subsequent calls.\n  #\n  # NOTE: `.dup.final` call may be used to get an intermediate hash value.\n  def final : Bytes\n    dst = Bytes.new digest_size\n    final dst\n  end\n\n  # Puts the final digest output into `dst`.\n  #\n  # Faster than the `Bytes` allocating version.\n  # Use when hashing in loops.\n  #\n  # `final` or `hexfinal` can only be called once and raises `FinalizedError` on subsequent calls.\n  #\n  # NOTE: `.dup.final(dst)` call may be used to get an intermediate hash value.\n  def final(dst : Bytes) : Bytes\n    check_finished\n    @finished = true\n    final_impl dst\n    dst\n  end\n\n  # Returns a hexadecimal-encoded digest in a new `String`.\n  #\n  # `final` or `hexfinal` can only be called once and raises `FinalizedError` on subsequent calls.\n  #\n  # NOTE: `.dup.hexfinal` call may be used to get an intermediate hash value.\n  def hexfinal : String\n    dsize = digest_size\n    string_size = dsize * 2\n    String.new(string_size) do |buffer|\n      tmp = Slice.new(buffer + dsize, dsize)\n      final tmp\n      tmp.hexstring buffer\n      {string_size, string_size}\n    end\n  end\n\n  # Writes a hexadecimal-encoded digest to `dst`.\n  #\n  # Faster than the `String` allocating version.\n  # Use when hashing in loops.\n  #\n  # `final` or `hexfinal` can only be called once and raises `FinalizedError` on subsequent calls.\n  #\n  # NOTE: `.dup.final` call may be used to get an intermediate hash value.\n  def hexfinal(dst : Bytes) : Nil\n    dsize = digest_size\n    unless dst.bytesize == dsize * 2\n      raise ArgumentError.new(\"Incorrect dst size: #{dst.bytesize}, expected: #{dsize * 2}\")\n    end\n\n    tmp = dst[dsize, dsize]\n    final tmp\n    tmp.hexstring dst\n  end\n\n  # Writes a hexadecimal-encoded digest to `IO`.\n  #\n  # Faster than the `String` allocating version.\n  #\n  # `final` or `hexfinal` can only be called once and raises `FinalizedError` on subsequent calls.\n  #\n  # NOTE: `.dup.final` call may be used to get an intermediate hash value.\n  #\n  # This method is restricted to a maximum digest size of 64 bits. Implementations that allow\n  # a larger digest size should override this method to use a larger buffer.\n  def hexfinal(io : IO) : Nil\n    if digest_size > 64\n      raise \"Digest#hexfinal(IO) can't handle digest_size over 64 bits\"\n    end\n    sary = uninitialized StaticArray(UInt8, 128)\n    tmp = sary.to_slice[0, digest_size * 2]\n    hexfinal tmp\n    io << tmp\n  end\n\n  # Resets the state of this object.  Use to get another hash value without creating a new object.\n  def reset : self\n    reset_impl\n    @finished = false\n    self\n  end\n\n  # Reads the file's content and updates the digest with it.\n  def file(file_name : Path | String) : self\n    File.open(file_name) do |io|\n      # `#update` works with big buffers so there's no need for additional read buffering in the file\n      io.read_buffering = false\n      self << io\n    end\n  end\n\n  # Reads the io's data and updates the digest with it.\n  def update(io : IO) : self\n    buffer = uninitialized UInt8[IO::DEFAULT_BUFFER_SIZE]\n    while (read_bytes = io.read(buffer.to_slice)) > 0\n      self << buffer.to_slice[0, read_bytes]\n    end\n    self\n  end\n\n  # :ditto:\n  def <<(data) : self\n    update(data)\n  end\n\n  private def check_finished : Nil\n    raise FinalizedError.new(\"finish already called\") if @finished\n  end\n\n  # Hashes data incrementally.\n  abstract def update_impl(data : Bytes) : Nil\n  # Stores the output digest of #digest_size bytes in dst.\n  abstract def final_impl(dst : Bytes) : Nil\n  # Resets the object to it's initial state.\n  abstract def reset_impl : Nil\n  # Returns the digest output size in bytes.\n  abstract def digest_size : Int32\nend\n"
  },
  {
    "path": "src/digest/io_digest.cr",
    "content": "require \"./digest\"\nrequire \"openssl/digest\"\n\n# Wraps an `IO` by calculating a specified `Digest` on read or write operations.\n#\n# ### Example\n#\n# ```\n# require \"digest\"\n#\n# underlying_io = IO::Memory.new(\"foo\")\n# io = IO::Digest.new(underlying_io, Digest::SHA256.new)\n# buffer = Bytes.new(256)\n# io.read(buffer)\n# io.final.hexstring # => \"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\"\n# ```\nclass IO::Digest < IO\n  getter io : IO\n  getter digest_algorithm : ::Digest\n  getter mode : DigestMode\n\n  delegate close, closed?, flush, peek, tty?, rewind, to: @io\n  delegate final, to: @digest_algorithm\n\n  enum DigestMode\n    Read\n    Write\n  end\n\n  def initialize(@io : IO, @digest_algorithm : ::Digest, @mode = DigestMode::Read)\n  end\n\n  def read(slice : Bytes) : Int32\n    read_bytes = io.read(slice).to_i32\n    if @mode.read?\n      digest_algorithm.update(slice[0, read_bytes])\n    end\n    read_bytes\n  end\n\n  def write(slice : Bytes) : Nil\n    return if slice.empty?\n\n    if @mode.write?\n      digest_algorithm.update(slice)\n    end\n    io.write(slice)\n  end\nend\n"
  },
  {
    "path": "src/digest/md5.cr",
    "content": "require \"./digest\"\nrequire \"openssl/digest\"\n\n# Implements the MD5 digest algorithm.\n#\n# NOTE: To use `MD5`, you must explicitly import it with `require \"digest/md5\"`\n#\n# WARNING: MD5 is no longer a cryptographically secure hash, and should not be\n# used in security-related components, like password hashing. For passwords, see\n# `Crypto::Bcrypt::Password`. For a generic cryptographic hash, use SHA-256 via\n# `Digest::SHA256`.\nclass Digest::MD5 < ::OpenSSL::Digest\n  extend ClassMethods\n\n  def initialize\n    super(\"MD5\")\n  end\n\n  protected def initialize(ctx : LibCrypto::EVP_MD_CTX)\n    super(\"MD5\", ctx)\n  end\n\n  def dup\n    self.class.new(dup_ctx)\n  end\nend\n"
  },
  {
    "path": "src/digest/sha1.cr",
    "content": "require \"./digest\"\nrequire \"openssl/digest\"\n\n# Implements the SHA1 digest algorithm.\n#\n# NOTE: To use `SHA1`, you must explicitly import it with `require \"digest/sha1\"`\n#\n# WARNING: SHA1 is no longer a cryptographically secure hash, and should not be\n# used in security-related components, like password hashing. For passwords, see\n# `Crypto::Bcrypt::Password`. For a generic cryptographic hash, use SHA-256 via\n# `Digest::SHA256`.\nclass Digest::SHA1 < ::OpenSSL::Digest\n  extend ClassMethods\n\n  def initialize\n    super(\"SHA1\")\n  end\n\n  protected def initialize(ctx : LibCrypto::EVP_MD_CTX)\n    super(\"SHA1\", ctx)\n  end\n\n  def dup\n    self.class.new(dup_ctx)\n  end\nend\n"
  },
  {
    "path": "src/digest/sha256.cr",
    "content": "require \"./digest\"\nrequire \"openssl/digest\"\n\n# Implements the SHA256 digest algorithm.\n#\n# NOTE: To use `SHA256`, you must explicitly import it with `require \"digest/sha256\"`\nclass Digest::SHA256 < ::OpenSSL::Digest\n  extend ClassMethods\n\n  def initialize\n    super(\"SHA256\")\n  end\n\n  protected def initialize(ctx : LibCrypto::EVP_MD_CTX)\n    super(\"SHA256\", ctx)\n  end\n\n  def dup\n    self.class.new(dup_ctx)\n  end\nend\n"
  },
  {
    "path": "src/digest/sha512.cr",
    "content": "require \"./digest\"\nrequire \"openssl/digest\"\n\n# Implements the SHA512 digest algorithm.\n#\n# NOTE: To use `SHA512`, you must explicitly import it with `require \"digest/sha512\"`\nclass Digest::SHA512 < ::OpenSSL::Digest\n  extend ClassMethods\n\n  def initialize\n    super(\"SHA512\")\n  end\n\n  protected def initialize(ctx : LibCrypto::EVP_MD_CTX)\n    super(\"SHA512\", ctx)\n  end\n\n  def dup\n    self.class.new(dup_ctx)\n  end\nend\n"
  },
  {
    "path": "src/digest.cr",
    "content": "require \"./digest/digest\"\nrequire \"./digest/*\"\n"
  },
  {
    "path": "src/dir/glob.cr",
    "content": "class Dir\n  # Returns an array of all files that match against any of *patterns*.\n  #\n  # The pattern syntax is similar to shell filename globbing, see `File.match?` for details.\n  #\n  # NOTE: Path separator in patterns needs to be always `/`. The returned file names use system-specific path separators.\n  def self.[](*patterns : Path | String, match : File::MatchOptions = File::MatchOptions.glob_default, follow_symlinks : Bool = false) : Array(String)\n    glob(patterns, match: match, follow_symlinks: follow_symlinks)\n  end\n\n  # :ditto:\n  def self.[](patterns : Enumerable, match : File::MatchOptions = File::MatchOptions.glob_default, follow_symlinks : Bool = false) : Array(String)\n    glob(patterns, match: match, follow_symlinks: follow_symlinks)\n  end\n\n  # :ditto:\n  #\n  # For compatibility, a falsey *match_hidden* argument is equivalent to passing\n  # `match: File::MatchOptions.glob_default`, and a truthy *match_hidden* is\n  # equivalent to\n  # `match: File::MatchOptions.glob_default | File::MatchOptions::DotFiles`.\n  @[Deprecated(\"Use the overload with a `match` parameter instead\")]\n  def self.[](*patterns : Path | String, match_hidden, follow_symlinks = false) : Array(String)\n    glob(patterns, match: match_hidden_to_options(match_hidden), follow_symlinks: follow_symlinks)\n  end\n\n  # :ditto:\n  #\n  # For compatibility, a falsey *match_hidden* argument is equivalent to passing\n  # `match: File::MatchOptions.glob_default`, and a truthy *match_hidden* is\n  # equivalent to\n  # `match: File::MatchOptions.glob_default | File::MatchOptions::DotFiles`.\n  @[Deprecated(\"Use the overload with a `match` parameter instead\")]\n  def self.[](patterns : Enumerable, match_hidden, follow_symlinks = false) : Array(String)\n    glob(patterns, match: match_hidden_to_options(match_hidden), follow_symlinks: follow_symlinks)\n  end\n\n  # Returns an array of all files that match against any of *patterns*.\n  #\n  # ```\n  # Dir.glob \"path/to/folder/*.txt\" # Returns all files in the target folder that end in \".txt\".\n  # Dir.glob \"path/to/folder/**/*\"  # Returns all files in the target folder and its subfolders.\n  # ```\n  # The pattern syntax is similar to shell filename globbing, see `File.match?` for details.\n  #\n  # NOTE: Path separator in patterns needs to be always `/`. The returned file names use system-specific path separators.\n  def self.glob(*patterns : Path | String, match : File::MatchOptions = File::MatchOptions.glob_default, follow_symlinks : Bool = false) : Array(String)\n    glob(patterns, match: match, follow_symlinks: follow_symlinks)\n  end\n\n  # :ditto:\n  def self.glob(patterns : Enumerable, match : File::MatchOptions = File::MatchOptions.glob_default, follow_symlinks : Bool = false) : Array(String)\n    paths = [] of String\n    glob(patterns, match: match, follow_symlinks: follow_symlinks) do |path|\n      paths << path\n    end\n    paths\n  end\n\n  # :ditto:\n  #\n  # For compatibility, a falsey *match_hidden* argument is equivalent to passing\n  # `match: File::MatchOptions.glob_default`, and a truthy *match_hidden* is\n  # equivalent to\n  # `match: File::MatchOptions.glob_default | File::MatchOptions::DotFiles`.\n  @[Deprecated(\"Use the overload with a `match` parameter instead\")]\n  def self.glob(*patterns : Path | String, match_hidden : Bool, follow_symlinks : Bool = false) : Array(String)\n    glob(patterns, match: match_hidden_to_options(match_hidden), follow_symlinks: follow_symlinks)\n  end\n\n  # :ditto:\n  #\n  # For compatibility, a falsey *match_hidden* argument is equivalent to passing\n  # `match: File::MatchOptions.glob_default`, and a truthy *match_hidden* is\n  # equivalent to\n  # `match: File::MatchOptions.glob_default | File::MatchOptions::DotFiles`.\n  @[Deprecated(\"Use the overload with a `match` parameter instead\")]\n  def self.glob(patterns : Enumerable, match_hidden, follow_symlinks = false) : Array(String)\n    paths = [] of String\n    glob(patterns, match: match_hidden_to_options(match_hidden), follow_symlinks: follow_symlinks) do |path|\n      paths << path\n    end\n    paths\n  end\n\n  # Yields all files that match against any of *patterns*.\n  #\n  # The pattern syntax is similar to shell filename globbing, see `File.match?` for details.\n  #\n  # NOTE: Path separator in patterns needs to be always `/`. The returned file names use system-specific path separators.\n  def self.glob(*patterns : Path | String, match : File::MatchOptions = File::MatchOptions.glob_default, follow_symlinks : Bool = false, &block : String -> _)\n    glob(patterns, match: match, follow_symlinks: follow_symlinks) do |path|\n      yield path\n    end\n  end\n\n  # :ditto:\n  def self.glob(patterns : Enumerable, match : File::MatchOptions = File::MatchOptions.glob_default, follow_symlinks : Bool = false, &block : String -> _)\n    Globber.glob(patterns, match: match, follow_symlinks: follow_symlinks) do |path|\n      yield path\n    end\n  end\n\n  # :ditto:\n  #\n  # For compatibility, a falsey *match_hidden* argument is equivalent to passing\n  # `match: File::MatchOptions.glob_default`, and a truthy *match_hidden* is\n  # equivalent to\n  # `match: File::MatchOptions.glob_default | File::MatchOptions::DotFiles`.\n  @[Deprecated(\"Use the overload with a `match` parameter instead\")]\n  def self.glob(*patterns : Path | String, match_hidden, follow_symlinks = false, &block : String -> _)\n    glob(patterns, match: match_hidden_to_options(match_hidden), follow_symlinks: follow_symlinks) do |path|\n      yield path\n    end\n  end\n\n  # :ditto:\n  #\n  # For compatibility, a falsey *match_hidden* argument is equivalent to passing\n  # `match: File::MatchOptions.glob_default`, and a truthy *match_hidden* is\n  # equivalent to\n  # `match: File::MatchOptions.glob_default | File::MatchOptions::DotFiles`.\n  @[Deprecated(\"Use the overload with a `match` parameter instead\")]\n  def self.glob(patterns : Enumerable, match_hidden, follow_symlinks = false, &block : String -> _)\n    Globber.glob(patterns, match: match_hidden_to_options(match_hidden), follow_symlinks: follow_symlinks) do |path|\n      yield path\n    end\n  end\n\n  private def self.match_hidden_to_options(match_hidden)\n    options = File::MatchOptions.glob_default\n    options |= File::MatchOptions::DotFiles if match_hidden\n    options\n  end\n\n  # :nodoc:\n  module Globber\n    record DirectoriesOnly\n    record ConstantEntry, path : String, merged : Bool\n    record EntryMatch, pattern : String do\n      def matches?(string) : Bool\n        File.match?(pattern, string)\n      end\n    end\n    record RecursiveDirectories\n    record ConstantDirectory, path : String\n    record RootDirectory\n    record DirectoryMatch, pattern : String do\n      def matches?(string) : Bool\n        File.match?(pattern, string)\n      end\n    end\n    alias PatternType = DirectoriesOnly | ConstantEntry | EntryMatch | RecursiveDirectories | ConstantDirectory | RootDirectory | DirectoryMatch\n\n    def self.glob(patterns : Enumerable, *, match, follow_symlinks, &block : String -> _)\n      patterns.each do |pattern|\n        if pattern.is_a?(Path)\n          pattern = pattern.to_posix.to_s\n        end\n        sequences = compile(pattern)\n\n        sequences.each do |sequence|\n          if sequence.count(&.is_a?(RecursiveDirectories)) > 1\n            run_tracking(sequence, match: match, follow_symlinks: follow_symlinks) do |match|\n              yield match\n            end\n          else\n            run(sequence, match: match, follow_symlinks: follow_symlinks) do |match|\n              yield match\n            end\n          end\n        end\n      end\n    end\n\n    private def self.compile(pattern)\n      expanded_patterns = [] of String\n      expand_brace_pattern(pattern, expanded_patterns)\n\n      expanded_patterns.map do |expanded_pattern|\n        single_compile expanded_pattern\n      end\n    end\n\n    private def self.single_compile(glob)\n      list = [] of PatternType\n      return list if glob.empty?\n\n      parts = glob.split('/', remove_empty: true)\n\n      if glob.ends_with?('/')\n        list << DirectoriesOnly.new\n      else\n        file = parts.pop\n        if constant_entry?(file)\n          list << ConstantEntry.new file, false\n        elsif !file.empty?\n          list << EntryMatch.new file\n        end\n      end\n\n      parts.reverse_each do |dir|\n        case\n        when dir == \"**\"\n          list << RecursiveDirectories.new\n        when dir.empty?\n        when constant_entry?(dir)\n          case last = list[-1]\n          when ConstantDirectory\n            list[-1] = ConstantDirectory.new File.join(dir, last.path)\n          when ConstantEntry\n            list[-1] = ConstantEntry.new File.join(dir, last.path), true\n          else\n            list << ConstantDirectory.new dir\n          end\n        else\n          list << DirectoryMatch.new dir\n        end\n      end\n\n      if glob.starts_with?('/')\n        list << RootDirectory.new\n      end\n\n      list\n    end\n\n    private def self.constant_entry?(file)\n      file.each_char do |char|\n        return false if char.in?('*', '?', '[', '\\\\')\n      end\n\n      true\n    end\n\n    private def self.run_tracking(sequence, match, follow_symlinks, &block : String -> _)\n      result_tracker = Set(String).new\n\n      run(sequence, match, follow_symlinks) do |result|\n        if result_tracker.add?(result)\n          yield result\n        end\n      end\n    end\n\n    private def self.run(sequence, match, follow_symlinks, &block : String -> _)\n      return if sequence.empty?\n\n      path_stack = [] of Tuple(Int32, String?, Crystal::System::Dir::Entry?)\n      path_stack << {sequence.size - 1, nil, nil}\n\n      while !path_stack.empty?\n        pos, path, dir_entry = path_stack.pop\n        cmd = sequence[pos]\n\n        next_pos = pos - 1\n        case cmd\n        in RootDirectory\n          raise \"Unreachable\" if path\n          path_stack << {next_pos, root, nil}\n        in DirectoriesOnly\n          raise \"Unreachable\" unless path\n          # FIXME: [win32] File::SEPARATOR_STRING comparison is not sufficient for Windows paths.\n          if path == File::SEPARATOR_STRING\n            fullpath = path\n          else\n            fullpath = Path[path].join(\"\").to_s\n          end\n\n          if dir_entry && !dir_entry.dir?.nil?\n            yield fullpath\n          elsif dir?(fullpath, follow_symlinks)\n            yield fullpath\n          end\n        in EntryMatch\n          next if sequence[pos + 1]?.is_a?(RecursiveDirectories)\n          each_child(path) do |entry|\n            next unless matches_file?(entry, match)\n            yield join(path, entry.name) if cmd.matches?(entry.name)\n          end\n        in DirectoryMatch\n          next_cmd = sequence[next_pos]?\n\n          each_child(path) do |entry|\n            next unless matches_file?(entry, match)\n            if cmd.matches?(entry.name)\n              is_dir = entry.dir?\n              fullpath = join(path, entry.name)\n              if is_dir.nil?\n                is_dir = dir?(fullpath, follow_symlinks)\n              end\n              if is_dir\n                path_stack << {next_pos, fullpath, entry}\n              end\n            end\n          end\n        in ConstantEntry\n          unless cmd.merged\n            next if sequence[pos + 1]?.is_a?(RecursiveDirectories)\n          end\n          full = join(path, cmd.path)\n          yield full if File.exists?(full) || File.symlink?(full)\n        in ConstantDirectory\n          path_stack << {next_pos, join(path, cmd.path), nil}\n          # Don't check if full exists. It just costs us time\n          # and the downstream node will be able to check properly.\n        in RecursiveDirectories\n          path_stack << {next_pos, path, nil}\n          next_cmd = sequence[next_pos]?\n\n          dir_path = path || \"\"\n          dir_stack = [] of Dir\n          dir_path_stack = [dir_path]\n          begin\n            dir = Dir.new(path || \".\")\n            dir_stack << dir\n          rescue File::Error\n            next\n          end\n          recurse = false\n\n          until dir_path_stack.empty?\n            if recurse\n              begin\n                dir = Dir.new(dir_path)\n              rescue File::Error\n                dir_path_stack.pop\n                break if dir_path_stack.empty?\n                dir_path = dir_path_stack.last\n                next\n              ensure\n                recurse = false\n              end\n              dir_stack.push dir\n            end\n\n            if entry = read_entry(dir)\n              next if entry.name.in?(\".\", \"..\")\n              next unless matches_file?(entry, match)\n\n              if dir_path.bytesize == 0\n                fullpath = entry.name\n              else\n                fullpath = File.join(dir_path, entry.name)\n              end\n\n              case next_cmd\n              when ConstantEntry\n                unless next_cmd.merged\n                  yield fullpath if next_cmd.path == entry.name\n                end\n              when EntryMatch\n                yield fullpath if next_cmd.matches?(entry.name)\n              end\n\n              is_dir = entry.dir?\n              if is_dir.nil?\n                is_dir = dir?(fullpath, follow_symlinks)\n              end\n\n              if is_dir\n                path_stack << {next_pos, fullpath, entry}\n\n                dir_path_stack.push fullpath\n                dir_path = dir_path_stack.last\n                recurse = true\n                next\n              end\n            else\n              dir.try(&.close)\n              dir_path_stack.pop\n              dir_stack.pop\n              break if dir_path_stack.empty?\n              dir_path = dir_path_stack.last\n              dir = dir_stack.last\n            end\n          end\n        end\n      end\n    end\n\n    private def self.root\n      Path[Dir.current].anchor.not_nil!.to_s\n    end\n\n    private def self.dir?(path, follow_symlinks)\n      if info = File.info?(path, follow_symlinks: follow_symlinks)\n        info.type.directory?\n      else\n        false\n      end\n    end\n\n    private def self.join(path, entry)\n      return entry unless path\n      return \"#{root}#{entry}\" if path == File::SEPARATOR_STRING\n\n      File.join(path, entry)\n    end\n\n    private def self.each_child(path, &)\n      Dir.open(path || Dir.current) do |dir|\n        while entry = read_entry(dir)\n          next if entry.name.in?(\".\", \"..\")\n          yield entry\n        end\n      end\n    rescue exc : File::NotFoundError\n    end\n\n    private def self.read_entry(dir)\n      return unless dir\n\n      # By doing this we get an Entry struct which already tells us\n      # whether something is a directory or not, avoiding having to\n      # call File.info? which is really expensive.\n      Crystal::System::Dir.next_entry(dir.@dir, dir.path)\n    end\n\n    private def self.matches_file?(entry, match)\n      return false if entry.name.starts_with?('.') && !match.dot_files?\n      return false if entry.native_hidden? && !match.native_hidden?\n      return false if entry.os_hidden? && !match.os_hidden?\n      true\n    end\n\n    # :nodoc:\n    # FIXME: The expansion mechanism does not work for complex brace patterns.\n    private def self.expand_brace_pattern(pattern : String, expanded) : Array(String)?\n      reader = Char::Reader.new(pattern)\n\n      lbrace = nil\n      rbrace = nil\n      alt_start = nil\n\n      alternatives = [] of String\n\n      nest = 0\n      escaped = false\n      reader.each do |char|\n        case {char, escaped}\n        when {'{', false}\n          lbrace = reader.pos if nest == 0\n          nest += 1\n        when {'}', false}\n          nest -= 1\n\n          if nest == 0\n            rbrace = reader.pos\n            start = (alt_start || lbrace).not_nil! + 1\n            alternatives << pattern.byte_slice(start, reader.pos - start)\n            break\n          end\n        when {',', false}\n          if nest == 1\n            start = (alt_start || lbrace).not_nil! + 1\n            alternatives << pattern.byte_slice(start, reader.pos - start)\n            alt_start = reader.pos\n          end\n        when {'\\\\', false}\n          escaped = true\n        else\n          escaped = false\n        end\n      end\n\n      if lbrace && rbrace\n        front = pattern.byte_slice(0, lbrace)\n        back = pattern.byte_slice(rbrace + 1)\n\n        alternatives.each do |alt|\n          brace_pattern = {front, alt, back}.join\n\n          expand_brace_pattern brace_pattern, expanded\n        end\n      else\n        expanded << pattern\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/dir.cr",
    "content": "require \"crystal/system/dir\"\n\n# Objects of class `Dir` are directory streams representing directories in the underlying file system.\n# They provide a variety of ways to list directories and their contents.\n#\n# The directory used in these examples contains the two regular files (`config.h` and `main.rb`),\n# the parent directory (`..`), and the directory itself (`.`).\n#\n# See also: `File`.\nclass Dir\n  include Enumerable(String)\n  include Iterable(String)\n\n  # Returns the path of this directory.\n  #\n  # ```\n  # Dir.mkdir(\"testdir\")\n  # dir = Dir.new(\"testdir\")\n  # Dir.mkdir(\"testdir/extendeddir\")\n  # dir2 = Dir.new(\"testdir/extendeddir\")\n  #\n  # dir.path  # => \"testdir\"\n  # dir2.path # => \"testdir/extendeddir\"\n  # ```\n  getter path : String\n\n  # Returns a new directory object for the named directory.\n  def initialize(path : Path | String)\n    @path = path.to_s\n    @dir = Crystal::System::Dir.open(@path)\n    @closed = false\n  end\n\n  # Alias for `new(path)`\n  def self.open(path : Path | String) : self\n    new path\n  end\n\n  # Opens a directory and yields it, closing it at the end of the block.\n  # Returns the value of the block.\n  def self.open(path : Path | String, & : self ->)\n    dir = new path\n    begin\n      yield dir\n    ensure\n      dir.close\n    end\n  end\n\n  # Calls the block once for each entry in this directory,\n  # passing the filename of each entry as a parameter to the block.\n  #\n  # ```\n  # Dir.mkdir(\"testdir\")\n  # File.write(\"testdir/config.h\", \"\")\n  #\n  # d = Dir.new(\"testdir\")\n  # d.each { |x| puts \"Got #{x}\" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # Got .\n  # Got ..\n  # Got config.h\n  # ```\n  def each(& : String ->) : Nil\n    while entry = read\n      yield entry\n    end\n  end\n\n  def each : Iterator(String)\n    EntryIterator.new(self)\n  end\n\n  # Returns an array containing all of entries in the given directory including \".\" and \"..\".\n  #\n  # ```\n  # Dir.mkdir(\"testdir\")\n  # File.touch(\"testdir/file_1\")\n  # File.touch(\"testdir/file_2\")\n  #\n  # Dir.new(\"testdir\").entries # => [\"..\", \"file_1\", \"file_2\", \".\"]\n  # ```\n  def entries : Array(String)\n    entries = [] of String\n    each do |filename|\n      entries << filename\n    end\n    entries\n  end\n\n  # Calls the block once for each entry except for `.` and `..` in this directory,\n  # passing the filename of each entry as a parameter to the block.\n  #\n  # ```\n  # Dir.mkdir(\"testdir\")\n  # File.write(\"testdir/config.h\", \"\")\n  #\n  # d = Dir.new(\"testdir\")\n  # d.each_child { |x| puts \"Got #{x}\" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # Got config.h\n  # ```\n  def each_child(& : String ->) : Nil\n    excluded = {\".\", \"..\"}\n    while entry = read\n      yield entry unless excluded.includes?(entry)\n    end\n  end\n\n  # Returns an iterator over of the all entries in this directory except for `.` and `..`.\n  #\n  # See `#each_child(&)`\n  #\n  # ```\n  # Dir.mkdir(\"test\")\n  # File.touch(\"test/foo\")\n  # File.touch(\"test/bar\")\n  #\n  # dir = Dir.new(\"test\")\n  # iter = d.each_child\n  #\n  # iter.next # => \"foo\"\n  # iter.next # => \"bar\"\n  # ```\n  def each_child : Iterator(String)\n    ChildIterator.new(self)\n  end\n\n  # Returns an array containing all of the filenames except for `.` and `..`\n  # in the given directory.\n  def children : Array(String)\n    entries = [] of String\n    each_child do |filename|\n      entries << filename\n    end\n    entries\n  end\n\n  # Reads the next entry from dir and returns it as a string. Returns `nil` at the end of the stream.\n  #\n  # ```\n  # d = Dir.new(\"testdir\")\n  # array = [] of String\n  # while file = d.read\n  #   array << file\n  # end\n  # array.sort # => [\".\", \"..\", \"config.h\"]\n  # ```\n  def read : String?\n    Crystal::System::Dir.next(@dir, path)\n  end\n\n  # Repositions this directory to the first entry.\n  def rewind : self\n    Crystal::System::Dir.rewind(@dir)\n    self\n  end\n\n  # This method is faster than `.info` and avoids race conditions if a `Dir` is already open on POSIX systems, but not necessarily on windows.\n  def info : File::Info\n    Crystal::System::Dir.info(@dir, path)\n  end\n\n  # Closes the directory stream.\n  def close : Nil\n    return if @closed\n    Crystal::System::Dir.close(@dir, path)\n    @closed = true\n  end\n\n  # Returns an absolute path to the current working directory.\n  #\n  # The result is similar to the shell commands `pwd` (POSIX) and `cd` (Windows).\n  #\n  # On POSIX systems, it respects the environment value `$PWD` if available and\n  # if it points to the current working directory.\n  def self.current : String\n    Crystal::System::Dir.current\n  end\n\n  # Changes the current working directory of the process to the given string.\n  def self.cd(path : Path | String) : String\n    Crystal::System::Dir.current = path.to_s\n  end\n\n  # Changes the current working directory of the process to the given string\n  # and invokes the block, restoring the original working directory\n  # when the block exits.\n  def self.cd(path : Path | String, &)\n    old = current\n    begin\n      cd(path)\n      yield\n    ensure\n      cd(old)\n    end\n  end\n\n  # Returns the tmp dir used for tempfile.\n  #\n  # ```\n  # Dir.tempdir # => \"/tmp\"\n  # ```\n  def self.tempdir : String\n    Crystal::System::Dir.tempdir\n  end\n\n  # See `#each`.\n  def self.each(dirname : Path | String, & : String ->)\n    Dir.open(dirname) do |dir|\n      dir.each do |filename|\n        yield filename\n      end\n    end\n  end\n\n  # See `#entries`.\n  def self.entries(dirname : Path | String) : Array(String)\n    Dir.open(dirname) do |dir|\n      return dir.entries\n    end\n  end\n\n  # See `#each_child`.\n  def self.each_child(dirname : Path | String, & : String ->)\n    Dir.open(dirname) do |dir|\n      dir.each_child do |filename|\n        yield filename\n      end\n    end\n  end\n\n  # See `#children`.\n  def self.children(dirname : Path | String) : Array(String)\n    Dir.open(dirname) do |dir|\n      return dir.children\n    end\n  end\n\n  # Returns `true` if the given path exists and is a directory\n  #\n  # ```\n  # Dir.mkdir(\"testdir\")\n  # Dir.exists?(\"testdir\") # => true\n  # ```\n  def self.exists?(path : Path | String) : Bool\n    if info = File.info?(path)\n      info.type.directory?\n    else\n      false\n    end\n  end\n\n  # Returns `true` if the directory at *path* is empty, otherwise returns `false`.\n  # Raises `File::NotFoundError` if the directory at *path* does not exist.\n  #\n  # ```\n  # Dir.mkdir(\"bar\")\n  # Dir.empty?(\"bar\") # => true\n  # File.write(\"bar/a_file\", \"The content\")\n  # Dir.empty?(\"bar\") # => false\n  # ```\n  def self.empty?(path : Path | String) : Bool\n    each_child(path) do |f|\n      return false\n    end\n    true\n  end\n\n  # Creates a new directory at the given path. The linux-style permission mode\n  # can be specified, with a default of 777 (0o777).\n  #\n  # NOTE: *mode* is ignored on windows.\n  #\n  # ```\n  # Dir.mkdir(\"testdir\")\n  # Dir.exists?(\"testdir\") # => true\n  # ```\n  def self.mkdir(path : Path | String, mode : Int32 = 0o777) : Nil\n    Crystal::System::Dir.create(path.to_s, mode)\n  end\n\n  # Creates a new directory at the given path, including any non-existing\n  # intermediate directories. The linux-style permission mode can be specified,\n  # with a default of 777 (0o777).\n  def self.mkdir_p(path : Path | String, mode : Int32 = 0o777) : Nil\n    return if Dir.exists?(path)\n\n    path = Path.new path\n\n    path.each_parent do |parent|\n      mkdir(parent, mode) unless Dir.exists?(parent)\n    end\n    mkdir(path, mode) unless Dir.exists?(path)\n  end\n\n  # Removes the directory at *path*. Raises `File::Error` on failure.\n  #\n  # On Windows, also raises `File::Error` if *path* points to a directory that\n  # is a reparse point, such as a symbolic link. Those directories can be\n  # deleted using `File.delete` instead.\n  def self.delete(path : Path | String) : Nil\n    Crystal::System::Dir.delete(path.to_s, raise_on_missing: true)\n  end\n\n  # Removes the directory at *path*, or returns `false` if the directory does\n  # not exist. Raises `File::Error` on other kinds of failure.\n  #\n  # On Windows, also raises `File::Error` if *path* points to a directory that\n  # is a reparse point, such as a symbolic link. Those directories can be\n  # deleted using `File.delete?` instead.\n  def self.delete?(path : Path | String) : Bool\n    Crystal::System::Dir.delete(path.to_s, raise_on_missing: false)\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"#<Dir:\" << @path << '>'\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def pretty_print(pp)\n    pp.text inspect\n  end\n\n  private struct EntryIterator\n    include Iterator(String)\n\n    def initialize(@dir : Dir)\n    end\n\n    def next\n      @dir.read || stop\n    end\n  end\n\n  private struct ChildIterator\n    include Iterator(String)\n\n    def initialize(@dir : Dir)\n    end\n\n    def next\n      excluded = {\".\", \"..\"}\n      while entry = @dir.read\n        return entry unless excluded.includes?(entry)\n      end\n      stop\n    end\n  end\nend\n\nrequire \"./dir/*\"\n"
  },
  {
    "path": "src/docs_main.cr",
    "content": "# This is the main file used for generating docs for the standard library.\n# It, for example, doesn't include API for the compiler, but does include\n# the fictitious API for the Crystal::Macros module.\n\nrequire \"./annotations\"\nrequire \"./compiler/crystal/macros\"\n\nrequire \"./*\"\nrequire \"./big/**\"\nrequire \"./compress/**\"\nrequire \"./crypto/**\"\nrequire \"./crystal/syntax_highlighter/*\"\nrequire \"./digest/**\"\nrequire \"./fiber/**\"\nrequire \"./gc/**\"\nrequire \"./http/**\"\nrequire \"./io/**\"\nrequire \"./log/**\"\nrequire \"./math/**\"\nrequire \"./random/**\"\nrequire \"./spec/helpers/**\"\nrequire \"./string/**\"\nrequire \"./sync/**\"\nrequire \"./system/**\"\nrequire \"./uri/**\"\nrequire \"./uuid/**\"\n"
  },
  {
    "path": "src/docs_pseudo_methods.cr",
    "content": "{% skip_file unless flag?(:docs) %}\n\n# This file documents pseudo-methods that are implemented by the compiler\n# and can't be redefined.\n# For documentation purposes, they are declared as regular methods with\n# their names prefixed by `__crystal_pseudo`. This prefix is removed by\n# the docs generator making it appear as the pseudo-method.\n\n# Returns the type of an expression.\n#\n# ```\n# typeof(1) # => Int32\n# ```\n#\n# It accepts multiple arguments, and the result is the union of the expression types:\n#\n# ```\n# typeof(1, \"a\", 'a') # => (Int32 | String | Char)\n# ```\n#\n# The expressions passed as arguments to `typeof` do not evaluate. The compiler\n# only analyzes their return type.\n#\n# * See [`typeof`](https://crystal-lang.org/reference/syntax_and_semantics/typeof.html) in the language specification.\ndef __crystal_pseudo_typeof(*expression) : Class\nend\n\n# Returns the size of the given type as number of bytes.\n#\n# *type* must be a constant or `typeof()` expression. It cannot be evaluated\n# at runtime.\n#\n# ```\n# sizeof(Int32)        # => 4\n# sizeof(Int64)        # => 8\n# sizeof(typeof(true)) # => 1\n# ```\n#\n# For `Reference` types, the size is the same as the size of a pointer:\n#\n# ```\n# # On a 64 bits machine\n# sizeof(Pointer(Int32)) # => 8\n# sizeof(String)         # => 8\n# ```\n#\n# This is because a `Reference`'s memory is allocated on the heap and a pointer\n# to it is passed around. The size of a class on the heap can be determined\n# using `#instance_sizeof`.\n#\n# * See [`sizeof`](https://crystal-lang.org/reference/syntax_and_semantics/sizeof.html) in the language specification.\ndef __crystal_pseudo_sizeof(type : Class) : Int32\nend\n\n# Returns the instance size of the given class as number of bytes.\n#\n# *type* must be a constant or `typeof()` expression. It cannot be evaluated at runtime.\n#\n# ```\n# instance_sizeof(String)    # => 16\n# instance_sizeof(Exception) # => 48\n# ```\n#\n# See `sizeof` for determining the size of value types.\n#\n# * See [`instance_sizeof`](https://crystal-lang.org/reference/syntax_and_semantics/instance_sizeof.html) in the language specification.\ndef __crystal_pseudo_instance_sizeof(type : Class) : Int32\nend\n\n# Returns the alignment of the given type as number of bytes.\n#\n# *type* must be a constant or `typeof()` expression. It cannot be evaluated\n# at runtime.\n#\n# ```\n# alignof(Int32)        # => 4\n# alignof(Float64)      # usually 4 or 8\n# alignof(typeof(true)) # => 1\n# ```\n#\n# For `Reference` types, the alignment is the same as the alignment of a pointer:\n#\n# ```\n# # On a 64 bits machine\n# alignof(Pointer(Int32)) # => 8\n# alignof(String)         # => 8\n# ```\n#\n# This is because a `Reference`'s memory is allocated on the heap and a pointer\n# to it is passed around. The alignment of a class on the heap can be determined\n# using `#instance_alignof`.\n#\n# * See [`alignof`](https://crystal-lang.org/reference/syntax_and_semantics/alignof.html) in the language specification.\ndef __crystal_pseudo_alignof(type : Class) : Int32\nend\n\n# Returns the instance alignment of the given class as number of bytes.\n#\n# *type* must be a constant or `typeof()` expression. It cannot be evaluated at runtime.\n#\n# ```\n# instance_alignof(String)    # => 4\n# instance_alignof(Exception) # => 8\n# ```\n#\n# See `alignof` for determining the size of value types.\n#\n# * See [`instance_alignof`](https://crystal-lang.org/reference/syntax_and_semantics/instance_alignof.html) in the language specification.\ndef __crystal_pseudo_instance_alignof(type : Class) : Int32\nend\n\n# Returns a `Pointer` to the contents of a variable.\n#\n# *variable* must be a variable (local, instance, class or library).\n#\n# ```\n# a = 1\n# ptr = pointerof(a)\n# ptr.value = 2\n#\n# a # => 2\n# ```\n#\n# * See [`pointerof`](https://crystal-lang.org/reference/syntax_and_semantics/pointerof.html) in the language specification.\ndef __crystal_pseudo_pointerof(variable : T) : Pointer(T) forall T\nend\n\n# Returns the byte offset of an instance variable in a struct or class type.\n#\n# *type* must be a constant or `typeof()` expression. It cannot be evaluated at runtime.\n# *offset*  must be the name of an instance variable of *type*, prefixed by `@`,\n# or the index of an element in a Tuple, starting from 0, if *type* is a `Tuple`.\n# ```\n# offsetof(String, @bytesize)       # => 4\n# offsetof(Exception, @message)     # => 8\n# offsetof(Time, @location)         # => 16\n# offsetof({Int32, Int8, Int32}, 0) # => 0\n# offsetof({Int32, Int8, Int32}, 1) # => 4\n# offsetof({Int32, Int8, Int32}, 2) # => 8\n# ```\n#\n# * See [`offsetof`](https://crystal-lang.org/reference/syntax_and_semantics/offsetof.html) in the language specification.\ndef __crystal_pseudo_offsetof(type : Class, offset) : Int32\nend\n\nclass Object\n  # Returns the boolean negation of `self`.\n  #\n  # ```\n  # !true  # => false\n  # !false # => true\n  # !nil   # => true\n  # !1     # => false\n  # !\"foo\" # => false\n  # ```\n  #\n  # This method is a unary operator and usually written in prefix notation\n  # (`!foo`) but it can also be written as a regular method call (`foo.!`).\n  def __crystal_pseudo_! : Bool\n  end\n\n  # Returns `true` if `self` inherits or includes *type*.\n  # *type* must be a constant or `typeof()`expression. It cannot be evaluated at runtime.\n  #\n  # ```\n  # a = 1\n  # a.class                 # => Int32\n  # a.is_a?(Int32)          # => true\n  # a.is_a?(String)         # => false\n  # a.is_a?(Number)         # => true\n  # a.is_a?(Int32 | String) # => true\n  # ```\n  #\n  # * See [`is_a?`](https://crystal-lang.org/reference/syntax_and_semantics/is_a.html) in the language specification.\n  def __crystal_pseudo_is_a?(type : Class) : Bool\n  end\n\n  # Returns `true` if `self` is `Nil`.\n  #\n  # ```\n  # 1.nil?   # => false\n  # nil.nil? # => true\n  # ```\n  #\n  # This method is equivalent to `is_a?(Nil)`.\n  #\n  # * See [`nil?`](https://crystal-lang.org/reference/syntax_and_semantics/nil_question.html) in the language specification.\n  def __crystal_pseudo_nil? : Bool\n  end\n\n  # Returns `self`.\n  #\n  # The type of this expression is restricted to *type* by the compiler.\n  # *type* must be a constant or `typeof()` expression. It cannot be evaluated at runtime.\n  #\n  # If *type* is not a valid restriction for the expression type, it\n  # is a compile-time error.\n  # If *type*  is a valid restriction for the expression, but `self` can't\n  # be restricted to *type*, it raises at runtime.\n  # *type* may be a wider restriction than the expression type, the resulting\n  # type is narrowed to the minimal restriction.\n  #\n  # ```\n  # a = [1, \"foo\"][0]\n  # typeof(a) # => Int32 | String\n  #\n  # typeof(a.as(Int32)) # => Int32\n  # a.as(Int32)         # => 1\n  #\n  # typeof(a.as(Bool)) # Compile Error: can't cast (Int32 | String) to Bool\n  #\n  # typeof(a.as(String)) # => String\n  # a.as(String)         # Runtime Error: Cast from Int32 to String failed\n  #\n  # typeof(a.as(Int32 | Bool)) # => Int32\n  # a.as(Int32 | Bool)         # => 1\n  # ```\n  #\n  # * See [`as`](https://crystal-lang.org/reference/syntax_and_semantics/as.html) in the language specification.\n  def __crystal_pseudo_as(type : Class)\n  end\n\n  # Returns `self` or `nil` if can't be restricted to *type*.\n  #\n  # The type of this expression is restricted to *type* by the compiler.\n  # If *type* is not a valid type restriction for the expression type, then\n  # it is restricted to `Nil`.\n  # *type* must be a constant or `typeof()` expression. It cannot be evaluated at runtime.\n  #\n  # ```\n  # a = [1, \"foo\"][0]\n  # typeof(a) # => Int32 | String\n  #\n  # typeof(a.as?(Int32)) # => Int32 | Nil\n  # a.as?(Int32)         # => 1\n  #\n  # typeof(a.as?(Bool)) # => Bool | Nil\n  # a.as?(Bool)         # => nil\n  #\n  # typeof(a.as?(String)) # => String | Nil\n  # a.as?(String)         # nil\n  #\n  # typeof(a.as?(Int32 | Bool)) # => Int32 | Nil\n  # a.as?(Int32 | Bool)         # => 1\n  # ```\n  #\n  # * See [`#as?`](https://crystal-lang.org/reference/syntax_and_semantics/as_question.html) in the language specification.\n  def __crystal_pseudo_as?(type : Class)\n  end\n\n  # Returns `true` if method *name* can be called on `self`.\n  #\n  # *name* must be a symbol literal, it cannot be evaluated at runtime.\n  #\n  # ```\n  # a = 1\n  # a.responds_to?(:abs)  # => true\n  # a.responds_to?(:size) # => false\n  # ```\n  #\n  # * See [`#responds_to?`](https://crystal-lang.org/reference/syntax_and_semantics/responds_to.html) in the language specification.\n  def __crystal_pseudo_responds_to?(name : Symbol) : Bool\n  end\nend\n\n# Some expressions won't return to the current scope and therefore have no return type.\n# This is expressed as the special return type `NoReturn`.\n#\n# Typical examples for non-returning methods and keywords are `return`, `exit`, `raise`, `next`, and `break`.\n#\n# This is for example useful for deconstructing union types:\n#\n# ```\n# string = STDIN.gets\n# typeof(string)                        # => String?\n# typeof(raise \"Empty input\")           # => NoReturn\n# typeof(string || raise \"Empty input\") # => String\n# ```\n#\n# The compiler recognizes that in case string is Nil, the right hand side of the expression `string || raise` will be evaluated.\n# Since `typeof(raise \"Empty input\")` is `NoReturn` the execution would not return to the current scope in that case.\n# That leaves only `String` as resulting type of the expression.\n#\n# Every expression whose code paths all result in `NoReturn` will be `NoReturn` as well.\n# `NoReturn` does not show up in a union type because it would essentially be included in every expression's type.\n# It is only used when an expression will never return to the current scope.\n#\n# `NoReturn` can be explicitly set as return type of a method or function definition but will usually be inferred by the compiler.\nstruct CRYSTAL_PSEUDO__NoReturn\nend\n\n# Similar in usage to `Nil`. `Void` is preferred for C lib bindings.\nstruct CRYSTAL_PSEUDO__Void\nend\n"
  },
  {
    "path": "src/ecr/lexer.cr",
    "content": "# :nodoc:\nclass ECR::Lexer\n  class Token\n    enum Type\n      String\n      Output\n      Control\n      EOF\n    end\n\n    property type : Type\n    property value : String\n    property line_number : Int32\n    property column_number : Int32\n    property? suppress_leading : Bool\n    property? suppress_trailing : Bool\n\n    def initialize\n      @type = :EOF\n      @value = \"\"\n      @line_number = 0\n      @column_number = 0\n      @suppress_leading = false\n      @suppress_trailing = false\n    end\n  end\n\n  class SyntaxException < Exception\n    getter line_number : Int32\n    getter column_number : Int32\n\n    def initialize(message, @line_number, @column_number)\n      super(message)\n    end\n  end\n\n  def initialize(string)\n    @reader = Char::Reader.new(string)\n    @token = Token.new\n    @line_number = 1\n    @column_number = 1\n  end\n\n  def next_token : Token\n    copy_location_info_to_token\n\n    case current_char\n    when '\\0'\n      @token.type = :EOF\n      return @token\n    when '<'\n      if peek_next_char == '%'\n        next_char\n        next_char\n\n        suppress_leading = current_char == '-'\n        next_char if suppress_leading\n\n        case current_char\n        when '='\n          next_char\n          copy_location_info_to_token\n          is_output = true\n        when '%'\n          next_char\n          copy_location_info_to_token\n          is_escape = true\n        else\n          copy_location_info_to_token\n        end\n\n        return consume_control(is_output, is_escape, suppress_leading)\n      end\n    else\n      # consume string\n    end\n\n    consume_string\n  end\n\n  private def consume_string\n    start_pos = current_pos\n    while true\n      case current_char\n      when '\\0'\n        break\n      when '\\n'\n        @line_number += 1\n        @column_number = 0\n      when '<'\n        if peek_next_char == '%'\n          break\n        end\n      else\n        # keep going\n      end\n      next_char\n    end\n\n    @token.type = :string\n    @token.value = string_range(start_pos)\n    @token\n  end\n\n  private def consume_control(is_output, is_escape, suppress_leading)\n    start_pos = current_pos\n    while true\n      case current_char\n      when '\\0'\n        if is_output\n          raise \"Unexpected end of file inside <%= ...\"\n        elsif is_escape\n          raise \"Unexpected end of file inside <%% ...\"\n        else\n          raise \"Unexpected end of file inside <% ...\"\n        end\n      when '\\n'\n        @line_number += 1\n        @column_number = 0\n      when '-'\n        if peek_next_char == '%'\n          # We need to peek another char, so we remember\n          # where we are, check that, and then go back\n          pos = @reader.pos\n          column_number = @column_number\n\n          next_char\n\n          is_end = peek_next_char == '>'\n          @reader.pos = pos\n          @column_number = column_number\n\n          if is_end\n            setup_control_token(start_pos, is_escape, suppress_leading, true)\n            raise \"Expecting '>' after '-%'\" if current_char != '>'\n            next_char\n            break\n          end\n        end\n      when '%'\n        if peek_next_char == '>'\n          setup_control_token(start_pos, is_escape, suppress_leading, false)\n          break\n        end\n      else\n        # keep going\n      end\n      next_char\n    end\n\n    if is_escape\n      @token.type = :string\n    elsif is_output\n      @token.type = :output\n    else\n      @token.type = :control\n    end\n    @token\n  end\n\n  private def setup_control_token(start_pos, is_escape, suppress_leading, suppress_trailing)\n    @token.suppress_leading = !is_escape && suppress_leading\n    @token.suppress_trailing = !is_escape && suppress_trailing\n    @token.value =\n      if is_escape\n        head = suppress_leading ? \"<%-\" : \"<%\"\n        tail = string_range(start_pos, current_pos + (suppress_trailing ? 3 : 2))\n        head + tail\n      else\n        string_range(start_pos)\n      end\n\n    next_char\n    next_char\n  end\n\n  private def copy_location_info_to_token\n    @token.line_number = @line_number\n    @token.column_number = @column_number\n  end\n\n  private def current_char\n    @reader.current_char\n  end\n\n  private def next_char\n    @column_number += 1\n    next_char_no_column_increment\n  end\n\n  private def next_char_no_column_increment\n    @reader.next_char\n  end\n\n  private def peek_next_char\n    @reader.peek_next_char\n  end\n\n  private def current_pos\n    @reader.pos\n  end\n\n  private def string_range(start_pos)\n    string_range(start_pos, current_pos)\n  end\n\n  private def string_range(start_pos, end_pos)\n    @reader.string.byte_slice(start_pos, end_pos - start_pos)\n  end\n\n  private def raise(message : String)\n    raise SyntaxException.new(message, @line_number, @column_number)\n  end\nend\n"
  },
  {
    "path": "src/ecr/macros.cr",
    "content": "module ECR\n  # Defines a `to_s(io)` method whose body is the ECR contained\n  # in *filename*, translated to Crystal code.\n  #\n  # ```text\n  # # greeting.ecr\n  # Hello <%= @name %>!\n  # ```\n  #\n  # ```\n  # require \"ecr/macros\"\n  #\n  # class Greeting\n  #   def initialize(@name : String)\n  #   end\n  #\n  #   ECR.def_to_s \"greeting.ecr\"\n  # end\n  #\n  # Greeting.new(\"World\").to_s # => \"Hello World!\"\n  # ```\n  #\n  # The macro basically translates the text inside the given file\n  # to Crystal code that appends to the IO:\n  #\n  # ```\n  # class Greeting\n  #   def to_s(io)\n  #     io << \"Hello \"\n  #     io << @name\n  #     io << '!'\n  #   end\n  # end\n  # ```\n  macro def_to_s(filename)\n    def to_s(__io__ : IO) : Nil\n      ::ECR.embed {{filename}}, \"__io__\"\n    end\n  end\n\n  # Embeds an ECR file *filename* into the program and appends the content to\n  # an IO in the variable *io_name*.\n  #\n  # The generated code is the result of translating the contents of\n  # the ECR file to Crystal, a program that appends to an IO.\n  #\n  # ```text\n  # # greeting.ecr\n  # Hello <%= name %>!\n  # ```\n  #\n  # ```\n  # require \"ecr/macros\"\n  #\n  # name = \"World\"\n  #\n  # io = IO::Memory.new\n  # ECR.embed \"greeting.ecr\", io\n  # io.to_s # => \"Hello World!\"\n  # ```\n  #\n  # The `ECR.embed` line basically generates this Crystal code:\n  #\n  # ```\n  # io << \"Hello \"\n  # io << name\n  # io << '!'\n  # ```\n  macro embed(filename, io_name)\n    \\{{ run(\"ecr/process\", {{filename}}, {{io_name.id.stringify}}) }}\n  end\n\n  # Embeds an ECR file *filename* into the program and renders it to a string.\n  #\n  # The generated code is the result of translating the contents of\n  # the ECR file to Crystal, a program that appends to an IO and returns a string.\n  #\n  # ```text\n  # # greeting.ecr\n  # Hello <%= name %>!\n  # ```\n  #\n  # ```\n  # require \"ecr/macros\"\n  #\n  # name = \"World\"\n  #\n  # rendered = ECR.render \"greeting.ecr\"\n  # rendered # => \"Hello World!\"\n  # ```\n  #\n  # The `ECR.render` basically generates this Crystal code:\n  #\n  # ```\n  # String.build do |io|\n  #   io << \"Hello \"\n  #   io << name\n  #   io << '!'\n  # end\n  # ```\n  macro render(filename)\n    ::String.build do |%io|\n      ::ECR.embed({{filename}}, %io)\n    end\n  end\nend\n"
  },
  {
    "path": "src/ecr/process.cr",
    "content": "require \"ecr/processor\"\n\nfilename = ARGV[0]\nbuffer_name = ARGV[1]\n\nbegin\n  puts ECR.process_file(filename, buffer_name)\nrescue ex : File::Error\n  STDERR.puts ex.message\n  exit 1\nend\n"
  },
  {
    "path": "src/ecr/processor.cr",
    "content": "require \"./lexer\"\n\nmodule ECR\n  extend self\n\n  DefaultBufferName = \"__str__\"\n\n  # :nodoc:\n  def process_file(filename, buffer_name = DefaultBufferName) : String\n    process_string File.read(filename), filename, buffer_name\n  end\n\n  # :nodoc:\n  def process_string(string, filename, buffer_name = DefaultBufferName) : String\n    lexer = Lexer.new string\n    token = lexer.next_token\n\n    String.build do |str|\n      while true\n        case token.type\n        when .string?\n          string = token.value\n          token = lexer.next_token\n\n          string = suppress_leading_indentation(token, string)\n\n          str << buffer_name\n          str << \" << \"\n          string.inspect(str)\n          str << '\\n'\n        when .output?\n          string = token.value\n          line_number = token.line_number\n          column_number = token.column_number\n          suppress_trailing = token.suppress_trailing?\n          token = lexer.next_token\n\n          suppress_trailing_whitespace(token, suppress_trailing)\n\n          str << \"#<loc:push>(\"\n          append_loc(str, filename, line_number, column_number)\n          str << string\n          str << \")#<loc:pop>.to_s \"\n          str << buffer_name\n          str << '\\n'\n        when .control?\n          string = token.value\n          line_number = token.line_number\n          column_number = token.column_number\n          suppress_trailing = token.suppress_trailing?\n          token = lexer.next_token\n\n          suppress_trailing_whitespace(token, suppress_trailing)\n\n          str << \"#<loc:push>\"\n          append_loc(str, filename, line_number, column_number)\n          str << ' ' unless string.starts_with?(' ')\n          str << string\n          str << \"#<loc:pop>\"\n          str << '\\n'\n        when .eof?\n          break\n        end\n      end\n    end\n  end\n\n  private def suppress_leading_indentation(token, string)\n    # To suppress leading indentation we find the last index of a newline and\n    # then check if all chars after that are whitespace.\n    # We use a Char::Reader for this for maximum efficiency.\n    if (token.type.output? || token.type.control?) && token.suppress_leading?\n      char_index = string.rindex('\\n')\n      char_index = char_index ? char_index + 1 : 0\n      byte_index = string.char_index_to_byte_index(char_index).not_nil!\n      reader = Char::Reader.new(string)\n      reader.pos = byte_index\n      while reader.current_char.ascii_whitespace? && reader.has_next?\n        reader.next_char\n      end\n      if reader.pos == string.bytesize\n        string = string.byte_slice(0, byte_index)\n      end\n    end\n    string\n  end\n\n  private def suppress_trailing_whitespace(token, suppress_trailing)\n    if suppress_trailing && token.type.string?\n      newline_index = token.value.index('\\n')\n      token.value = token.value[newline_index + 1..-1] if newline_index\n    end\n  end\n\n  private def append_loc(str, filename, line_number, column_number)\n    str << \"#<loc:\"\n    filename.inspect(str)\n    str << ','\n    str << line_number\n    str << ','\n    str << column_number\n    str << '>'\n  end\nend\n"
  },
  {
    "path": "src/ecr.cr",
    "content": "# Embedded Crystal (ECR) is a template language for embedding Crystal code into other text,\n# that includes but is not limited to HTML. The template is read and transformed\n# at compile time and then embedded into the binary.\n#\n# There are `<%= %>` and `<% %>` syntax. The former will render returned values.\n# The latter will not, but instead serve to control the structure as we do in Crystal.\n#\n# Using a dash inside `<...>` either eliminates previous indentation or removes the next newline:\n#\n# * `<%- ... %>`: removes previous indentation\n# * `<% ... -%>`: removes next newline\n# * `<%-= ... %>`: removes previous indentation\n# * `<%= ... -%>`: removes next newline\n#\n# A comment can be created the same as normal code: `<% # hello %>` or by the special\n# tag: `<%# hello %>`. An ECR tag can be inserted directly (i.e. the tag itself may be\n# escaped) by using a second `%` like so: `<%% a = b %>` or `<%%= foo %>`. Dashes may\n# also be present in those escaped tags and have no effect on the surrounding text.\n#\n# NOTE: To use `ECR`, you must explicitly import it with `require \"ecr\"`\n#\n# Quick Example:\n#\n# Create a simple ECR file named `greeter.ecr`:\n#\n# ```\n# Greetings, <%= @name %>!\n# ```\n#\n# and then use it like so with the `#def_to_s` macro:\n#\n# ```\n# require \"ecr\"\n#\n# class Greeter\n#   def initialize(@name : String)\n#   end\n#\n#   ECR.def_to_s \"greeter.ecr\"\n# end\n#\n# Greeter.new(\"John\").to_s # => \"Greetings, John!\\n\"\n# ```\n#\n# Using logical statements:\n#\n# ```\n# # greeter.ecr\n# <%- if @name -%>\n# Greetings, <%= @name %>!\n# <%- else -%>\n# Greetings!\n# <%- end -%>\n# ```\n#\n# ```\n# require \"ecr\"\n#\n# class Greeter\n#   def initialize(@name : String | Nil)\n#   end\n#\n#   ECR.def_to_s \"greeter.ecr\"\n# end\n#\n# Greeter.new(nil).to_s    # => \"Greetings!\\n\"\n# Greeter.new(\"Jill\").to_s # => \"Greetings, Jill!\\n\"\n# ```\n#\n# Using loops:\n#\n# ```\n# # greeter.ecr\n# <%- @names.each do |name| -%>\n# Hi, <%= name %>!\n# <%- end -%>\n# ```\n#\n# ```\n# require \"ecr\"\n#\n# class Greeter\n#   @names : Array(String)\n#\n#   def initialize(*names)\n#     @names = names.to_a\n#   end\n#\n#   ECR.def_to_s \"greeter.ecr\"\n# end\n#\n# Greeter.new(\"John\", \"Zoe\", \"Ben\").to_s # => \"Hi, John!\\nHi, Zoe!\\nHi, Ben!\\n\"\n# ```\n#\n# Comments and Escapes:\n#\n# ```\n# # demo.ecr\n# <%# Demonstrate use of ECR tag -%>\n# A valid ECR tag looks like this: <%%= foo %>\n# ```\n#\n# ```\n# require \"ecr\"\n# foo = 2\n# ECR.render(\"demo.ecr\") # => \"A valid ECR tag looks like this: <%= foo %>\\n\"\n# ```\n#\n# Likewise, other Crystal logic can be implemented in ECR text.\nmodule ECR\nend\n\nrequire \"./ecr/macros\"\n"
  },
  {
    "path": "src/empty.cr",
    "content": "{% skip_file if flag?(:docs) %}\nrequire \"primitives\"\n\n{% if flag?(:msvc) %}\n  @[Link({{ flag?(:static) ? \"libcmt\" : \"msvcrt\" }})] # For `mainCRTStartup`\n{% end %}\nlib LibCrystalMain\n  @[Raises]\n  fun __crystal_main(argc : Int32, argv : UInt8**)\nend\n\nfun main(argc : Int32, argv : UInt8**) : Int32\n  LibCrystalMain.__crystal_main(argc, argv)\n  0\nend\n"
  },
  {
    "path": "src/enum.cr",
    "content": "# Enum is the base type of all enums.\n#\n# An enum is a set of integer values, where each value has an associated name. For example:\n#\n# ```\n# enum Color\n#   Red   # 0\n#   Green # 1\n#   Blue  # 2\n# end\n# ```\n#\n# Values start with the value `0` and are incremented by one, but can be overwritten.\n#\n# To get the underlying value you invoke value on it:\n#\n# ```\n# Color::Green.value # => 1\n# ```\n#\n# Each constant (member) in the enum has the type of the enum:\n#\n# ```\n# typeof(Color::Red) # => Color\n# ```\n#\n# ### Flags enum\n#\n# An enum can be marked with the `@[Flags]` annotation. This changes the default values:\n#\n# ```\n# @[Flags]\n# enum IOMode\n#   Read  # 1\n#   Write # 2\n#   Async # 4\n# end\n# ```\n#\n# Additionally, some methods change their behaviour.\n#\n# ### Enums from integers\n#\n# An enum can be created from an integer:\n#\n# ```\n# Color.new(1).to_s # => \"Green\"\n# ```\n#\n# Values that don't correspond to enum's constants are allowed: the value\n# will still be of type Color, but when printed you will get the underlying value:\n#\n# ```\n# Color.new(10).to_s # => \"10\"\n# ```\n#\n# This method is mainly intended to convert integers from C to enums in Crystal.\n#\n# ### Question methods\n#\n# An enum automatically defines question methods for each member, using\n# `String#underscore` for the method name.\n# * In the case of regular enums, this compares by equality (`==`).\n# * In the case of flags enums, this invokes `includes?`.\n#\n# For example:\n#\n# ```\n# color = Color::Blue\n# color.red?  # => false\n# color.blue? # => true\n#\n# mode = IOMode::Read | IOMode::Async\n# mode.read?  # => true\n# mode.write? # => false\n# mode.async? # => true\n# ```\n#\n# This is very convenient in `case` expressions:\n#\n# ```\n# case color\n# when .red?\n#   puts \"Got red\"\n# when .blue?\n#   puts \"Got blue\"\n# end\n# ```\n#\n# ### Changing the Base Type\n#\n# The type of the underlying enum value is `Int32` by default, but it can be changed to any type in `Int::Primitive`.\n#\n# ```\n# enum Color : UInt8\n#   Red\n#   Green\n#   Blue\n# end\n#\n# Color::Red.value # : UInt8\n# ```\nabstract struct Enum\n  include Comparable(self)\n\n  # Returns *value*.\n  def self.new(value : self)\n    value\n  end\n\n  # Returns the underlying value held by the enum instance.\n  #\n  # ```\n  # enum Color\n  #   Red\n  #   Green\n  #   Blue\n  # end\n  #\n  # Color::Red.value   # => 0\n  # Color::Green.value # => 1\n  # Color::Blue.value  # => 2\n  # ```\n  def value : Int\n    previous_def\n  end\n\n  # Appends a `String` representation of this enum member to the given *io*.\n  #\n  # See also: `to_s`.\n  def to_s(io : IO) : Nil\n    {% if @type.annotation(Flags) %}\n      if value == 0\n        io << \"None\"\n      elsif name = member_name\n        io << name\n      else\n        stringify_names(io, \" | \")\n      end\n    {% else %}\n      io << to_s\n    {% end %}\n  end\n\n  # Returns a `String` representation of this enum member.\n  # In the case of regular enums, this is just the name of the member.\n  # In the case of flag enums, it's the names joined by vertical bars, or \"None\",\n  # if the value is zero.\n  #\n  # If an enum's value doesn't match a member's value, the raw value\n  # is returned as a string.\n  #\n  # ```\n  # Color::Red.to_s                     # => \"Red\"\n  # IOMode::None.to_s                   # => \"None\"\n  # (IOMode::Read | IOMode::Write).to_s # => \"Read | Write\"\n  #\n  # Color.new(10).to_s # => \"10\"\n  # ```\n  def to_s : String\n    {% if @type.annotation(Flags) %}\n      String.build { |io| to_s(io) }\n    {% else %}\n      member_name || value.to_s\n    {% end %}\n  end\n\n  # Returns an unambiguous `String` representation of this enum member.\n  # In the case of a single member value, this is the fully qualified name of\n  # the member (equivalent to `#to_s` with the enum name as prefix).\n  # In the case of multiple members (for a flags enum), it's a call to `Enum.[]`\n  # for recreating the same value.\n  #\n  # If the value can't be represented fully by named members, the remaining value\n  # is appended.\n  #\n  # ```\n  # Color::Red                     # => Color:Red\n  # IOMode::None                   # => IOMode::None\n  # (IOMode::Read | IOMode::Write) # => IOMode[Read, Write]\n  #\n  # Color.new(10) # => Color[10]\n  # ```\n  def inspect(io : IO) : Nil\n    {% if @type.annotation(Flags) %}\n      if value == 0\n        io << {{ \"#{@type}::None\" }}\n      elsif name = member_name\n        io << {{ \"#{@type}::\" }} << name\n      else\n        io << {{ \"#{@type}[\" }}\n        stringify_names(io, \", \")\n        io << \"]\"\n      end\n    {% else %}\n      inspect_single(io)\n    {% end %}\n  end\n\n  private def stringify_names(io, separator) : Nil\n    remaining_value = self.value\n    {% for member in @type.constants %}\n      {% if member.stringify != \"All\" %}\n        if {{@type.constant(member)}} != 0 && remaining_value.bits_set? {{@type.constant(member)}}\n          unless remaining_value == self.value\n            io << separator\n          end\n          io << {{member.stringify}}\n          remaining_value &= ~{{@type.constant(member)}}\n        end\n      {% end %}\n    {% end %}\n\n    unless remaining_value.zero?\n      io << separator unless remaining_value == self.value\n      io << remaining_value\n    end\n  end\n\n  private def inspect_single(io) : Nil\n    if name = member_name\n      io << {{ \"#{@type}::\" }} << name\n    else\n      io << {{ \"#{@type}[\" }} << value << \"]\"\n    end\n  end\n\n  # :nodoc:\n  def member_name : String?\n    # Can't use `case` here because case with duplicate values do\n    # not compile, but enums can have duplicates (such as `enum Foo; FOO = 1; BAR = 1; end`).\n    {% for member in @type.constants %}\n      if value == {{@type.constant(member)}}\n        return {{member.stringify}}\n      end\n    {% end %}\n  end\n\n  # Returns the value of this enum member as an `Int32`.\n  #\n  # ```\n  # Color::Blue.to_i                    # => 2\n  # (IOMode::Read | IOMode::Write).to_i # => 3\n  #\n  # Color.new(10).to_i # => 10\n  # ```\n  def to_i : Int32\n    value.to_i32\n  end\n\n  {% for name in %w(i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64) %}\n    {% prefix = name.starts_with?('i') ? \"Int\".id : (name.starts_with?('u') ? \"UInt\".id : \"Float\".id) %}\n    {% type = \"#{prefix}#{name[1..-1].id}\".id %}\n    # Returns the value of this enum member as a `{{type}}`\n    def to_{{name.id}} : {{type}}\n      value.to_{{name.id}}\n    end\n\n    # Returns the value of this enum member as a `{{type}}`\n    def to_{{name.id}}! : {{type}}\n      value.to_{{name.id}}!\n    end\n  {% end %}\n\n  # Returns the enum member that results from adding *other*\n  # to this enum member's value.\n  #\n  # ```\n  # Color::Red + 1 # => Color::Green\n  # Color::Red + 2 # => Color::Blue\n  # Color::Red + 3 # => Color.new(3)\n  # ```\n  def +(other : Int) : self\n    self.class.new(value + other)\n  end\n\n  # Returns the enum member that results from subtracting *other*\n  # to this enum member's value.\n  #\n  # ```\n  # Color::Blue - 1 # => Color::Green\n  # Color::Blue - 2 # => Color::Red\n  # Color::Blue - 3 # => Color.new(-1)\n  # ```\n  def -(other : Int) : self\n    self.class.new(value - other)\n  end\n\n  # Returns the enum member that results from applying a logical\n  # \"or\" operation between this enum member's value and *other*.\n  # This is mostly useful with flag enums.\n  #\n  # ```\n  # (IOMode::Read | IOMode::Async) # => IOMode::Read | IOMode::Async\n  # ```\n  def |(other : self) : self\n    self.class.new(value | other.value)\n  end\n\n  # Returns the enum member that results from applying a logical\n  # \"and\" operation between this enum member's value and *other*.\n  # This is mostly useful with flag enums.\n  #\n  # ```\n  # (IOMode::Read | IOMode::Async) & IOMode::Read # => IOMode::Read\n  # ```\n  def &(other : self) : self\n    self.class.new(value & other.value)\n  end\n\n  # Returns the enum member that results from applying a logical\n  # \"xor\" operation between this enum member's value and *other*.\n  # This is mostly useful with flag enums.\n  def ^(other : self) : self\n    self.class.new(value ^ other.value)\n  end\n\n  # Returns the enum member that results from applying a logical\n  # \"not\" operation of this enum member's value.\n  def ~ : self\n    self.class.new(~value)\n  end\n\n  # Compares this enum member against another, according to their underlying\n  # value.\n  #\n  # ```\n  # Color::Red <=> Color::Blue  # => -1\n  # Color::Blue <=> Color::Red  # => 1\n  # Color::Blue <=> Color::Blue # => 0\n  # ```\n  def <=>(other : self)\n    value <=> other.value\n  end\n\n  def ==(other)\n    false\n  end\n\n  # Returns `true` if this enum member's value includes *other*. This\n  # performs a logical \"and\" between this enum member's value and *other*'s.\n  #\n  # This is mostly useful for flag enums.\n  #\n  # For example:\n  #\n  # ```\n  # mode = IOMode::Read | IOMode::Write\n  # mode.includes?(IOMode::Read)  # => true\n  # mode.includes?(IOMode::Async) # => false\n  # ```\n  def includes?(other : self) : Bool\n    value.bits_set?(other.value)\n  end\n\n  # Returns `true` if this enum member and *other* have the same underlying value.\n  #\n  # ```\n  # Color::Red == Color::Red  # => true\n  # Color::Red == Color::Blue # => false\n  # ```\n  def ==(other : self)\n    value == other.value\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.enum(self)\n  end\n\n  # Iterates each values in a Flags Enum.\n  #\n  # ```\n  # (IOMode::Read | IOMode::Async).each do |member, value|\n  #   # yield IOMode::Read, 1\n  #   # yield IOMode::Async, 3\n  # end\n  # ```\n  def each(& : self ->)\n    {% if @type.annotation(Flags) %}\n      return if value == 0\n      {% for member in @type.constants %}\n        {% if member.stringify != \"All\" && member.stringify != \"None\" %}\n          if includes?(self.class.new({{@type.constant(member)}}))\n            yield self.class.new({{@type.constant(member)}}), {{@type.constant(member)}}\n          end\n        {% end %}\n      {% end %}\n    {% else %}\n      {% raise \"Can't iterate #{@type}: only Flags Enum can be iterated\" %}\n    {% end %}\n  end\n\n  # Returns all enum members as an `Array(String)`.\n  #\n  # ```\n  # Color.names # => [\"Red\", \"Green\", \"Blue\"]\n  # ```\n  def self.names : Array(String)\n    {% if @type.annotation(Flags) %}\n      {{ @type.constants.select { |e| e.stringify != \"None\" && e.stringify != \"All\" }.map &.stringify }}\n    {% else %}\n      {{ @type.constants.map &.stringify }}\n    {% end %}\n  end\n\n  # Returns all enum members as an `Array(self)`.\n  #\n  # ```\n  # Color.values # => [Color::Red, Color::Green, Color::Blue]\n  # ```\n  def self.values : Array(self)\n    {% if @type.annotation(Flags) %}\n      {{ @type.constants.select { |e| e.stringify != \"None\" && e.stringify != \"All\" }.map { |e| \"#{@type}::#{e.id}\".id } }}\n    {% else %}\n      {{ @type.constants.map { |e| \"#{@type}::#{e.id}\".id } }}\n    {% end %}\n  end\n\n  # Returns the enum member that has the given value, or `nil` if\n  # no such member exists.\n  #\n  # ```\n  # Color.from_value?(0) # => Color::Red\n  # Color.from_value?(1) # => Color::Green\n  # Color.from_value?(2) # => Color::Blue\n  # Color.from_value?(3) # => nil\n  # ```\n  def self.from_value?(value : Int) : self?\n    {% if @type.annotation(Flags) %}\n      all_mask = {{@type}}::All.value\n      return if all_mask & value != value\n      return new(all_mask.class.new(value))\n    {% else %}\n      {% for member in @type.constants %}\n        return new({{@type.constant(member)}}) if {{@type.constant(member)}} == value\n      {% end %}\n    {% end %}\n    nil\n  end\n\n  # Returns the enum member that has the given value, or raises\n  # if no such member exists.\n  #\n  # ```\n  # Color.from_value(0) # => Color::Red\n  # Color.from_value(1) # => Color::Green\n  # Color.from_value(2) # => Color::Blue\n  # Color.from_value(3) # raises ArgumentError\n  # ```\n  def self.from_value(value : Int) : self\n    from_value?(value) || raise ArgumentError.new(\"Unknown enum #{self} value: #{value}\")\n  end\n\n  # Returns `true` if the given *value* is an enum member, otherwise `false`.\n  # `false` if not member.\n  #\n  # ```\n  # Color.valid?(Color::Red)   # => true\n  # Color.valid?(Color.new(4)) # => false\n  # ```\n  #\n  # NOTE: This is a class method, not an instance method because\n  # an instance method `valid?` is defined by the language when a user\n  # defines an enum member named `Valid`.\n  def self.valid?(value : self) : Bool\n    !!from_value?(value.value)\n  end\n\n  # def self.to_h : Hash(String, self)\n  #   {\n  #     {% for member in @type.constants %}\n  #       {{member.stringify}} => {{member}},\n  #     {% end %}\n  #   }\n  # end\n\n  # Returns the enum member that has the given name, or\n  # raises `ArgumentError` if no such member exists. The comparison is made by using\n  # `String#camelcase` and `String#downcase` between *string* and\n  # the enum members names. Dashes (`-`) in *string* have the same meaning as an underscore (`_`).\n  # A member named \"FortyTwo\" or \"FORTY_TWO\"\n  # is found with any of these strings: \"forty_two\", \"FortyTwo\", \"FORTY_TWO\",\n  # \"Forty-Two\", \"FORTYTWO\", \"fortytwo\".\n  #\n  # ```\n  # Color.parse(\"Red\")    # => Color::Red\n  # Color.parse(\"BLUE\")   # => Color::Blue\n  # Color.parse(\"Yellow\") # raises ArgumentError\n  # ```\n  def self.parse(string : String) : self\n    parse?(string) || raise ArgumentError.new(\"Unknown enum #{self} value: #{string}\")\n  end\n\n  # Returns the enum member that has the given name, or\n  # `nil` if no such member exists. The comparison is made by using\n  # `String#camelcase` and `String#downcase` between *string* and\n  # the enum members names. Dashes (`-`) in *string* have the same meaning as an underscore (`_`).\n  # A member named \"FortyTwo\", or \"FORTY_TWO\"\n  # is found with any of these strings: \"forty_two\", \"FortyTwo\", \"FORTY_TWO\",\n  # \"Forty-Two\", \"FORTYTWO\", \"fortytwo\".\n  #\n  # ```\n  # Color.parse?(\"Red\")    # => Color::Red\n  # Color.parse?(\"BLUE\")   # => Color::Blue\n  # Color.parse?(\"Yellow\") # => nil\n  # ```\n  #\n  # If multiple members match the same normalized string, the first one is returned.\n  def self.parse?(string : String) : self?\n    {% begin %}\n      # FIXME: There is no `StringLiteral#bytesize` or any other adequate means\n      # to figure out how much space we actually need. Maybe some regex could\n      # work. For now just play it safe.\n      # FIXME: We might want to establish some upper limit in case a member name\n      # is exorbitantly long.\n\n      # The following is an optimized normalization. It is equivalent to\n      # `string.gsub('-', '_').camelcase.downcase` but does not allocate.\n      {% max_size = @type.constants.map(&.size).sort.last %}\n      buffer = uninitialized UInt8[{{ max_size * 4 + 1 }}]\n      appender = buffer.to_unsafe.appender\n      char_counter = 0\n      string.each_char do |char|\n        next if char == '-' || char == '_'\n        char_counter += 1\n        return nil if char_counter > {{max_size}}\n        char.downcase &.each_byte do |byte|\n          appender << byte\n        end\n      end\n      # Temporarily map all constants to their normalized value in order to\n      # avoid duplicates in the `case` conditions.\n      # `FOO` and `Foo` members would both generate `when \"foo\"` which creates a compile time error.\n      # The first matching member is chosen, like with symbol autocasting.\n      # That's different from the predicate methods which return true for the last matching member.\n      {% constants = {} of _ => _ %}\n      {% for member in @type.constants %}\n        {% key = member.stringify.camelcase.downcase %}\n        {% constants[key] = member unless constants[key] %}\n      {% end %}\n\n      case appender.to_slice\n      {% for name, member in constants %}\n        when {{name}}.to_slice\n          new({{@type.constant(member)}})\n      {% end %}\n      else\n        nil\n      end\n    {% end %}\n  end\n\n  def clone\n    self\n  end\n\n  # Convenience macro to create a combined enum (combines given members using `|` (or) logical operator)\n  #\n  # ```\n  # IOMode.flags(Read, Write) # => IOMode[Read, Write]\n  # ```\n  #\n  # * `Enum.[]` is a more advanced alternative which also allows int and symbol parameters.\n  macro flags(*values)\n    {% for value, i in values %}\\\n      {% if i != 0 %} | {% end %}\\\n      {{ @type }}::{{ value }}{% end %}\\\n  end\n\n  # Convenience macro to create a combined enum (combines given members using `|` (or) logical operator).\n  #\n  # Arguments can be the name of a member, a symbol representing a member name or a numerical value.\n  #\n  # ```\n  # IOMode[Read]             # => IOMode[Read]\n  # IOMode[1]                # => IOMode[Read]\n  # IOMode[Read, Write]      # => IOMode[Read, Write]\n  # IOMode[Read, 64]         # => IOMode[Read, 64]\n  # IOMode[Read, :write, 64] # => IOMode[Read, Write, 64]\n  # ```\n  macro [](*values)\n    {% for value, i in values %}\\\n      {% if i != 0 %} | {% end %}\\\n      {% if value.is_a?(Path) %} \\\n        {{ @type }}::{{ value }} \\\n      {% else %} \\\n        {{ @type }}.new({{value}}) \\\n      {% end %} \\\n    {% end %}\\\n  end\n\n  # Iterates each member of the enum.\n  # It won't iterate the `None` and `All` members of flags enums.\n  #\n  # ```\n  # IOMode.each do |member, value|\n  #   # yield IOMode::Read, 1\n  #   # yield IOMode::Write, 2\n  #   # yield IOMode::Async, 3\n  # end\n  # ```\n  def self.each(& : self ->)\n    {% for member in @type.constants %}\n      {% unless @type.annotation(Flags) && %w(none all).includes?(member.stringify.downcase) %}\n        yield new({{@type.constant(member)}}), {{@type.constant(member)}}\n      {% end %}\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/enumerable.cr",
    "content": "# The `Enumerable` mixin provides collection classes with several traversal, searching,\n# filtering and querying methods.\n#\n# Including types must provide an `each` method, which yields successive members\n# of the collection.\n#\n# For example:\n#\n# ```\n# class Three\n#   include Enumerable(Int32)\n#\n#   def each(&)\n#     yield 1\n#     yield 2\n#     yield 3\n#   end\n# end\n#\n# three = Three.new\n# three.to_a                # => [1, 2, 3]\n# three.select &.odd?       # => [1, 3]\n# three.all? { |x| x < 10 } # => true\n# ```\n#\n# Note that most search and filter methods traverse an Enumerable eagerly,\n# producing an `Array` as the result. For a lazy alternative refer to\n# the `Iterator` and `Iterable` modules.\nmodule Enumerable(T)\n  class EmptyError < Exception\n    def initialize(message = \"Empty enumerable\")\n      super(message)\n    end\n  end\n\n  class NotFoundError < Exception\n    def initialize(message = \"Element not found\")\n      super(message)\n    end\n  end\n\n  # Must yield this collection's elements to the block.\n  abstract def each(& : T ->)\n\n  # Returns `true` if the passed block is truthy\n  # for all elements of the collection.\n  #\n  # ```\n  # [\"ant\", \"bear\", \"cat\"].all? { |word| word.size >= 3 } # => true\n  # [\"ant\", \"bear\", \"cat\"].all? { |word| word.size >= 4 } # => false\n  # ```\n  def all?(& : T ->) : Bool\n    each { |e| return false unless yield e }\n    true\n  end\n\n  # Returns `true` if `pattern === element` for all elements in\n  # this enumerable.\n  #\n  # ```\n  # [2, 3, 4].all?(1..5)        # => true\n  # [2, 3, 4].all?(Int32)       # => true\n  # [2, \"a\", 3].all?(String)    # => false\n  # %w[foo bar baz].all?(/o|a/) # => true\n  # ```\n  def all?(pattern) : Bool\n    all? { |e| pattern === e }\n  end\n\n  # Returns `true` if all of the elements of the collection are truthy.\n  #\n  # ```\n  # [nil, true, 99].all? # => false\n  # [15].all?            # => true\n  # ```\n  def all? : Bool\n    all? &.itself\n  end\n\n  # Returns `true` if the passed block is truthy\n  # for at least one element of the collection.\n  #\n  # ```\n  # [\"ant\", \"bear\", \"cat\"].any? { |word| word.size >= 4 } # => true\n  # [\"ant\", \"bear\", \"cat\"].any? { |word| word.size > 4 }  # => false\n  # ```\n  def any?(& : T ->) : Bool\n    each { |e| return true if yield e }\n    false\n  end\n\n  # Returns `true` if `pattern === element` for at least one\n  # element in this enumerable.\n  #\n  # ```\n  # [2, 3, 4].any?(1..3)      # => true\n  # [2, 3, 4].any?(5..10)     # => false\n  # [2, \"a\", 3].any?(String)  # => true\n  # %w[foo bar baz].any?(/a/) # => true\n  # ```\n  def any?(pattern) : Bool\n    any? { |e| pattern === e }\n  end\n\n  # Returns `true` if at least one of the collection's members is truthy.\n  #\n  # ```\n  # [nil, true, 99].any? # => true\n  # [nil, false].any?    # => false\n  # ([] of Int32).any?   # => false\n  # ```\n  #\n  # * `#present?` does not consider truthiness of elements.\n  # * `#any?(&)` and `#any(pattern)` allow custom conditions.\n  #\n  # NOTE: `#any?` usually has the same semantics as `#present?`. They only\n  # differ if the element type can be falsey (i.e. `T <= Nil || T <= Pointer || T <= Bool`).\n  # It's typically advised to prefer `#present?` unless these specific truthiness\n  # semantics are required.\n  def any? : Bool\n    any? &.itself\n  end\n\n  # Enumerates over the items, chunking them together based on\n  # the return value of the block.\n  #\n  # Consecutive elements which return the same block value are chunked together.\n  #\n  # For example, consecutive even numbers and odd numbers can be chunked as follows.\n  #\n  # ```\n  # ary = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunks { |n| n.even? }\n  # ary # => [{false, [3, 1]}, {true, [4]}, {false, [1, 5, 9]}, {true, [2, 6]}, {false, [5, 3, 5]}]\n  # ```\n  #\n  # The following key values have special meaning:\n  #\n  # * `Enumerable::Chunk::Drop` specifies that the elements should be dropped\n  # * `Enumerable::Chunk::Alone` specifies that the element should be chunked by itself\n  #\n  # See also: `Iterator#chunk`.\n  def chunks(&block : T -> U) forall U\n    res = [] of Tuple(typeof(Chunk.key_type(self, block)), Array(T))\n    chunks_internal(block) { |*kv| res << kv }\n    res\n  end\n\n  module Chunk\n    # Can be used in `Enumerable#chunks` and specifies that the elements should be dropped.\n    record Drop\n\n    # Can be used in `Enumerable#chunks` and specifies that the element should be chunked by itself.\n    record Alone\n\n    # :nodoc:\n    struct Accumulator(T, U)\n      @data : Array(T)\n      @reuse : Bool\n\n      def initialize(reuse = false)\n        @key = uninitialized U\n        @initialized = false\n\n        if reuse\n          if reuse.is_a?(Array)\n            @data = reuse\n          else\n            @data = [] of T\n          end\n          @reuse = true\n        else\n          @data = [] of T\n          @reuse = false\n        end\n      end\n\n      def init(key, val)\n        @key = key\n\n        if @reuse\n          @data.clear\n          @data << val\n        else\n          @data = [val]\n        end\n        @initialized = true\n      end\n\n      def add(d)\n        @data << d\n      end\n\n      def fetch\n        if @initialized\n          {@key, @data}.tap { @initialized = false }\n        end\n      end\n\n      def same_as?(key) : Bool\n        return false unless @initialized\n        return false if key.is_a?(Alone.class) || key.is_a?(Drop.class)\n        @key == key\n      end\n\n      def acc(key, val, &)\n        if same_as?(key)\n          add(val)\n        else\n          if tuple = fetch\n            yield *tuple\n          end\n\n          init(key, val) unless key.is_a?(Drop.class)\n        end\n      end\n    end\n\n    def self.key_type(ary, block)\n      ary.each do |item|\n        key = block.call(item)\n        ::raise \"\" if key.is_a?(Drop.class)\n        return key\n      end\n      ::raise \"\"\n    end\n  end\n\n  private def chunks_internal(original_block : T -> U, &) forall U\n    acc = Chunk::Accumulator(T, typeof(Chunk.key_type(self, original_block))).new\n    each do |val|\n      key = original_block.call(val)\n      acc.acc(key, val) do |*tuple|\n        yield *tuple\n      end\n    end\n\n    if tuple = acc.fetch\n      yield *tuple\n    end\n  end\n\n  # Returns an `Array` with the results of running the block against each element\n  # of the collection, removing `nil` values.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].map { |name| name.match(/^A./) }         # => [Regex::MatchData(\"Al\"), nil]\n  # [\"Alice\", \"Bob\"].compact_map { |name| name.match(/^A./) } # => [Regex::MatchData(\"Al\")]\n  # ```\n  def compact_map(& : T -> _)\n    ary = [] of typeof((yield Enumerable.element_type(self)).not_nil!)\n    each do |e|\n      v = yield e\n      unless v.nil?\n        ary << v\n      end\n    end\n    ary\n  end\n\n  # Returns the number of elements in the collection for which\n  # the passed block is truthy.\n  #\n  # ```\n  # [1, 2, 3, 4].count { |i| i % 2 == 0 } # => 2\n  # ```\n  def count(& : T ->) : Int32\n    count = 0\n    each { |e| count += 1 if yield e }\n    count\n  end\n\n  # Returns the number of times that the passed item is present in the collection.\n  #\n  # ```\n  # [1, 2, 3, 4].count(3) # => 1\n  # ```\n  def count(item) : Int32\n    count { |e| e == item }\n  end\n\n  # Calls the given block for each element in this enumerable forever.\n  def cycle(& : T ->) : Nil\n    loop { each { |x| yield x } }\n  end\n\n  # Calls the given block for each element in this enumerable *n* times.\n  def cycle(n, & : T ->) : Nil\n    n.times { each { |x| yield x } }\n  end\n\n  # Iterates over the collection yielding chunks of size *count*,\n  # but advancing one by one.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].each_cons(2) do |cons|\n  #   puts cons\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # [1, 2]\n  # [2, 3]\n  # [3, 4]\n  # [4, 5]\n  # ```\n  #\n  # By default, a new array is created and yielded for each consecutive slice of elements.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse*  is an instance of `Array`, `Deque` or a similar collection type (implementing `#<<`, `#shift` and `#size`) it will be used.\n  # * If *reuse* is falsey, the array will not be reused.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  #\n  # Chunks of two items can be iterated using `#each_cons_pair`, an optimized\n  # implementation for the special case of `count == 2` which avoids heap\n  # allocations.\n  def each_cons(count : Int, reuse = false, &)\n    raise ArgumentError.new \"Invalid cons size: #{count}\" if count <= 0\n    if reuse.nil? || reuse.is_a?(Bool)\n      # we use an initial capacity of double the count, because a second\n      # iteration would have reallocated the array to that capacity anyway\n      each_cons_internal(count, reuse, Array(T).new(count * 2)) { |slice| yield slice }\n    else\n      each_cons_internal(count, true, reuse) { |slice| yield slice }\n    end\n  end\n\n  private def each_cons_internal(count : Int, reuse, cons, &)\n    each do |elem|\n      cons << elem\n      cons.shift if cons.size > count\n      if cons.size == count\n        if reuse\n          yield cons\n        else\n          yield cons.dup\n        end\n      end\n    end\n    nil\n  end\n\n  # Iterates over the collection yielding pairs of adjacent items,\n  # but advancing one by one.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].each_cons_pair do |a, b|\n  #   puts \"#{a}, #{b}\"\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # 1, 2\n  # 2, 3\n  # 3, 4\n  # 4, 5\n  # ```\n  #\n  # Chunks of more than two items can be iterated using `#each_cons`.\n  # This method is just an optimized implementation for the special case of\n  # `count == 2` to avoid heap allocations.\n  def each_cons_pair(& : (T, T) ->) : Nil\n    last_elem = uninitialized T\n    first_iteration = true\n    each do |elem|\n      if first_iteration\n        first_iteration = false\n      else\n        yield last_elem, elem\n      end\n      last_elem = elem\n    end\n  end\n\n  # Iterates over the collection in slices of size *count*,\n  # and runs the block for each of those.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].each_slice(2) do |slice|\n  #   puts slice\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # [1, 2]\n  # [3, 4]\n  # [5]\n  # ```\n  #\n  # Note that the last one can be smaller.\n  #\n  # By default, a new array is created and yielded for each slice.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is an `Array`, this array will be reused\n  # * If *reuse* is truthy, the method will create a new array and reuse it.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_slice(count : Int, reuse = false, &)\n    each_slice_internal(count, Array(T), reuse) { |slice| yield slice }\n  end\n\n  private def each_slice_internal(count : Int, type, reuse, &)\n    if reuse\n      unless reuse.is_a?(Array)\n        reuse = type.new(count)\n      end\n      reuse.clear\n      slice = reuse\n    else\n      slice = type.new(count)\n    end\n\n    each do |elem|\n      slice << elem\n      if slice.size == count\n        yield slice\n\n        if reuse\n          slice.clear\n        else\n          slice = type.new(count)\n        end\n      end\n    end\n    yield slice unless slice.empty?\n    nil\n  end\n\n  # Iterates over the collection, yielding every *n*th element, starting with the first.\n  #\n  # ```\n  # %w[Alice Bob Charlie David].each_step(2) do |user|\n  #   puts \"User: #{user}\"\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # User: Alice\n  # User: Charlie\n  # ```\n  #\n  # Accepts an optional *offset* parameter\n  #\n  # ```\n  # %w[Alice Bob Charlie David].each_step(2, offset: 1) do |user|\n  #   puts \"User: #{user}\"\n  # end\n  # ```\n  #\n  # Which would print:\n  #\n  # ```text\n  # User: Bob\n  # User: David\n  # ```\n  def each_step(n : Int, *, offset : Int = 0, & : T ->) : Nil\n    raise ArgumentError.new(\"Invalid n size: #{n}\") if n <= 0\n    raise ArgumentError.new(\"Invalid offset size: #{offset}\") if offset < 0\n    offset_mod = offset % n\n    each_with_index do |elem, i|\n      yield elem if i >= offset && i % n == offset_mod\n    end\n  end\n\n  # Iterates over the collection, yielding both the elements and their index.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].each_with_index do |user, i|\n  #   puts \"User ##{i}: #{user}\"\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # User # 0: Alice\n  # User # 1: Bob\n  # ```\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there. So, a more human friendly version of the previous snippet would be:\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].each_with_index(1) do |user, i|\n  #   puts \"User ##{i}: #{user}\"\n  # end\n  # ```\n  #\n  # Which would print:\n  #\n  # ```text\n  # User # 1: Alice\n  # User # 2: Bob\n  # ```\n  def each_with_index(offset = 0, &)\n    i = offset\n    each do |elem|\n      yield elem, i\n      i += 1\n    end\n  end\n\n  # Iterates over the collection, passing each element and the initial object *obj*.\n  # Returns that object.\n  #\n  # ```\n  # hash = [\"Alice\", \"Bob\"].each_with_object({} of String => Int32) do |user, sizes|\n  #   sizes[user] = user.size\n  # end\n  # hash # => {\"Alice\" => 5, \"Bob\" => 3}\n  # ```\n  def each_with_object(obj : U, & : T, U ->) : U forall U\n    each do |elem|\n      yield elem, obj\n    end\n    obj\n  end\n\n  # Returns the first element in the collection for which the passed block is truthy.\n  #\n  # Accepts an optional parameter *if_none*, to set what gets returned if\n  # no element is found (defaults to `nil`).\n  #\n  # ```\n  # [1, 2, 3, 4].find { |i| i > 2 }     # => 3\n  # [1, 2, 3, 4].find { |i| i > 8 }     # => nil\n  # [1, 2, 3, 4].find(-1) { |i| i > 8 } # => -1\n  # ```\n  def find(if_none = nil, & : T ->)\n    each do |elem|\n      return elem if yield elem\n    end\n    if_none\n  end\n\n  # Returns the first element in the collection for which the passed block is truthy.\n  # Raises `Enumerable::NotFoundError` if there is no element for which the block is truthy.\n  #\n  # ```\n  # [1, 2, 3, 4].find! { |i| i > 2 } # => 3\n  # [1, 2, 3, 4].find! { |i| i > 8 } # => raises Enumerable::NotFoundError\n  # ```\n  def find!(& : T ->) : T\n    each do |elem|\n      return elem if yield elem\n    end\n    raise Enumerable::NotFoundError.new\n  end\n\n  # Yields each value until the first truthy block result and returns that result.\n  #\n  # Accepts an optional parameter `if_none`, to set what gets returned if\n  # no element is found (defaults to `nil`).\n  #\n  # ```\n  # [1, 2, 3, 4].find_value { |i| i > 2 }     # => true\n  # [1, 2, 3, 4].find_value { |i| i > 8 }     # => nil\n  # [1, 2, 3, 4].find_value(-1) { |i| i > 8 } # => -1\n  # ```\n  def find_value(if_none = nil, & : T ->)\n    each do |i|\n      if result = yield i\n        return result\n      end\n    end\n    if_none\n  end\n\n  # Returns the first element in the collection,\n  # If the collection is empty, calls the block and returns its value.\n  #\n  # ```\n  # ([1, 2, 3]).first { 4 }   # => 1\n  # ([] of Int32).first { 4 } # => 4\n  # ```\n  def first(& : ->)\n    each { |e| return e }\n    yield\n  end\n\n  # Returns the first element in the collection. Raises `Enumerable::EmptyError`\n  # if the collection is empty.\n  #\n  # ```\n  # ([1, 2, 3]).first   # => 1\n  # ([] of Int32).first # raises Enumerable::EmptyError\n  # ```\n  def first : T\n    first { raise Enumerable::EmptyError.new }\n  end\n\n  # Returns the first element in the collection.\n  # When the collection is empty, returns `nil`.\n  #\n  # ```\n  # ([1, 2, 3]).first?   # => 1\n  # ([] of Int32).first? # => nil\n  # ```\n  def first? : T?\n    first { nil }\n  end\n\n  # Returns a new array with the concatenated results of running the block\n  # once for every element in the collection.\n  # Only `Array` and `Iterator` results are concatenated; every other value is\n  # directly appended to the new array.\n  #\n  # ```\n  # array = [\"Alice\", \"Bob\"].flat_map do |user|\n  #   user.chars\n  # end\n  # array # => ['A', 'l', 'i', 'c', 'e', 'B', 'o', 'b']\n  # ```\n  def flat_map(& : T -> _)\n    ary = [] of typeof(flat_map_type(yield Enumerable.element_type(self)))\n    each do |e|\n      case v = yield e\n      when Array, Iterator\n        ary.concat(v)\n      else\n        ary.push(v)\n      end\n    end\n    ary\n  end\n\n  private def flat_map_type(elem)\n    case elem\n    when Array, Iterator\n      elem.first\n    else\n      elem\n    end\n  end\n\n  # Returns a `Hash` whose keys are each different value that the passed block\n  # returned when run for each element in the collection, and which values are\n  # an `Array` of the elements for which the block returned that value.\n  #\n  # ```\n  # [\"Alice\", \"Bob\", \"Ary\"].group_by { |name| name.size } # => {5 => [\"Alice\"], 3 => [\"Bob\", \"Ary\"]}\n  # ```\n  def group_by(& : T -> U) forall U\n    h = Hash(U, Array(T)).new\n    each do |e|\n      v = yield e\n      h.put_if_absent(v) { Array(T).new } << e\n    end\n    h\n  end\n\n  # Returns an `Array` with chunks in the given size, eventually filled up\n  # with given value or `nil`.\n  #\n  # ```\n  # [1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]\n  # [1, 2, 3].in_groups_of(2)    # => [[1, 2], [3, nil]]\n  # ```\n  def in_groups_of(size : Int, filled_up_with : U = nil) forall U\n    raise ArgumentError.new(\"Size must be positive\") if size <= 0\n\n    ary = Array(Array(T | U)).new\n    in_groups_of(size, filled_up_with) do |group|\n      ary << group\n    end\n    ary\n  end\n\n  # Yields a block with the chunks in the given size.\n  #\n  # ```\n  # [1, 2, 4].in_groups_of(2, 0) { |e| p e.sum }\n  # # => 3\n  # # => 4\n  # ```\n  #\n  # By default, a new array is created and yielded for each group.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is an `Array`, this array will be reused\n  # * If *reuse* is truthy, the method will create a new array and reuse it.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def in_groups_of(size : Int, filled_up_with : U = nil, reuse = false, &) forall U\n    raise ArgumentError.new(\"Size must be positive\") if size <= 0\n\n    each_slice_internal(size, Array(T | U), reuse) do |slice|\n      (size - slice.size).times { slice << filled_up_with }\n      yield slice\n    end\n  end\n\n  # Returns an `Array` with chunks in the given size.\n  # Last chunk can be smaller depending on the number of remaining items.\n  #\n  # ```\n  # [1, 2, 3].in_slices_of(2) # => [[1, 2], [3]]\n  # ```\n  def in_slices_of(size : Int) : Array(Array(T))\n    raise ArgumentError.new(\"Size must be positive\") if size <= 0\n\n    ary = Array(Array(T)).new\n    each_slice_internal(size, Array(T), false) do |slice|\n      ary << slice\n    end\n    ary\n  end\n\n  # Returns `true` if the collection contains *obj*, `false` otherwise.\n  #\n  # ```\n  # [1, 2, 3].includes?(2) # => true\n  # [1, 2, 3].includes?(5) # => false\n  # ```\n  def includes?(obj) : Bool\n    any? { |e| e == obj }\n  end\n\n  # Returns the index of the first element for which the passed block is truthy.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].index { |name| name.size < 4 } # => 1 (Bob's index)\n  # ```\n  #\n  # Returns `nil` if the block is not truthy for any element.\n  def index(& : T ->) : Int32?\n    each_with_index do |e, i|\n      return i if yield e\n    end\n    nil\n  end\n\n  # Returns the first index of *obj* in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].index(\"Alice\") # => 0\n  # ```\n  #\n  # Returns `nil` if *obj* is not in the collection.\n  def index(obj) : Int32?\n    index { |e| e == obj }\n  end\n\n  # Returns the index of the first element for which the passed block is truthy.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].index! { |name| name.size < 4 } # => 1 (Bob's index)\n  # ```\n  #\n  # Raises `Enumerable::NotFoundError` if there is no element for which the block is truthy.\n  def index!(& : T ->) : Int32\n    index { |e| yield e } || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the first index of *obj* in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].index!(\"Alice\") # => 0\n  # ```\n  #\n  # Raises `Enumerable::NotFoundError` if *obj* is not in the collection.\n  def index!(obj) : Int32\n    index! { |e| e == obj }\n  end\n\n  # Converts an `Enumerable` to a `Hash` by using the value returned by the block\n  # as the hash key.\n  # Be aware, if two elements return the same value as a key one will override\n  # the other. If you want to keep all values, then you should probably use\n  # `group_by` instead.\n  #\n  # ```\n  # [\"Anna\", \"Ary\", \"Alice\"].index_by { |e| e.size }\n  # # => {4 => \"Anna\", 3 => \"Ary\", 5 => \"Alice\"}\n  # [\"Anna\", \"Ary\", \"Alice\", \"Bob\"].index_by { |e| e.size }\n  # # => {4 => \"Anna\", 3 => \"Bob\", 5 => \"Alice\"}\n  # ```\n  def index_by(& : T -> U) : Hash(U, T) forall U\n    hash = {} of U => T\n    each do |elem|\n      hash[yield elem] = elem\n    end\n    hash\n  end\n\n  # Combines all elements in the collection by applying a binary operation, specified by a block, so as\n  # to reduce them to a single value.\n  #\n  # For each element in the collection the block is passed an accumulator value (*memo*) and the element. The\n  # result becomes the new value for *memo*. At the end of the iteration, the final value of *memo* is\n  # the return value for the method. The initial value for the accumulator is the first element in the collection.\n  # If the collection has only one element, that element is returned.\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].reduce { |acc, i| acc + i } # => 15\n  # [1].reduce { |acc, i| acc + i }             # => 1\n  # ([] of Int32).reduce { |acc, i| acc + i }   # raises Enumerable::EmptyError\n  # ```\n  #\n  # The block is not required to return a `T`, in which case the accumulator's\n  # type includes whatever the block returns.\n  #\n  # ```\n  # # `acc` is an `Int32 | String`\n  # [1, 2, 3, 4, 5].reduce { |acc, i| \"#{acc}-#{i}\" } # => \"1-2-3-4-5\"\n  # [1].reduce { |acc, i| \"#{acc}-#{i}\" }             # => 1\n  # ```\n  def reduce(&)\n    memo = uninitialized typeof(reduce(Enumerable.element_type(self)) { |acc, i| yield acc, i })\n    found = false\n\n    each do |elem|\n      memo = found ? (yield memo, elem) : elem\n      found = true\n    end\n\n    found ? memo : raise Enumerable::EmptyError.new\n  end\n\n  # Just like the other variant, but you can set the initial value of the accumulator.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].reduce(10) { |acc, i| acc + i }             # => 25\n  # [1, 2, 3].reduce([] of Int32) { |memo, i| memo.unshift(i) } # => [3, 2, 1]\n  # ```\n  def reduce(memo, &)\n    each do |elem|\n      memo = yield memo, elem\n    end\n    memo\n  end\n\n  # Similar to `reduce`, but instead of raising when the input is empty,\n  # return `nil`\n  #\n  # ```\n  # ([] of Int32).reduce? { |acc, i| acc + i } # => nil\n  # ```\n  def reduce?(&)\n    memo = uninitialized typeof(reduce(Enumerable.element_type(self)) { |acc, i| yield acc, i })\n    found = false\n\n    each do |elem|\n      memo = found ? (yield memo, elem) : elem\n      found = true\n    end\n\n    found ? memo : nil\n  end\n\n  # Returns an array of the prefix sums of the elements in this collection. The\n  # first element of the returned array is same as the first element of `self`.\n  #\n  # Expects all element types to respond to the `#+` method.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].accumulate # => [1, 3, 6, 10, 15, 21]\n  # ```\n  def accumulate : Array(T)\n    accumulate { |x, y| x + y }\n  end\n\n  # Returns an array containing *initial* and its prefix sums with the elements\n  # in this collection.\n  #\n  # Expects `U` to respond to the `#+` method.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].accumulate(6) # => [6, 7, 9, 12, 16, 21]\n  # ```\n  def accumulate(initial : U) : Array(U) forall U\n    accumulate(initial) { |x, y| x + y }\n  end\n\n  # Returns an array containing the successive values of applying a binary\n  # operation, specified by the given *block*, to this collection's elements.\n  #\n  # For each element in the collection the block is passed an accumulator value\n  # and the element. The result becomes the new value for the accumulator and is\n  # also appended to the returned array. The initial value for the accumulator\n  # is the first element in the collection.\n  #\n  # ```\n  # [2, 3, 4, 5].accumulate { |x, y| x * y } # => [2, 6, 24, 120]\n  # ```\n  def accumulate(&block : T, T -> T) : Array(T)\n    values = [] of T\n    memo = uninitialized T\n\n    each do |elem|\n      memo = values.empty? ? elem : (yield memo, elem)\n      values << memo\n    end\n\n    values\n  end\n\n  # Returns an array containing *initial* and the successive values of applying\n  # a binary operation, specified by the given *block*, to this collection's\n  # elements.\n  #\n  # Similar to `#accumulate(&block : T, T -> T)`, except the initial value is\n  # provided by an argument and needs not have the same type as the elements in\n  # the collection. This initial value is always present in the returned array.\n  #\n  # ```\n  # [1, 3, 5, 7].accumulate(9) { |x, y| x * y } # => [9, 9, 27, 135, 945]\n  # ```\n  def accumulate(initial : U, &block : U, T -> U) : Array(U) forall U\n    values = [initial]\n\n    each do |elem|\n      initial = yield initial, elem\n      values << initial\n    end\n\n    values\n  end\n\n  # Returns a `String` created by concatenating the elements in the collection,\n  # separated by *separator* (defaults to none).\n  #\n  # ```\n  # [1, 2, 3, 4, 5].join(\", \") # => \"1, 2, 3, 4, 5\"\n  # ```\n  def join(separator = \"\") : String\n    String.build do |io|\n      join io, separator\n    end\n  end\n\n  # Returns a `String` created by concatenating the results of passing the elements\n  # in the collection to the passed block, separated by *separator* (defaults to none).\n  #\n  # ```\n  # [1, 2, 3, 4, 5].join(\", \") { |i| -i } # => \"-1, -2, -3, -4, -5\"\n  # ```\n  def join(separator = \"\", & : T ->)\n    String.build do |io|\n      join(io, separator) do |elem|\n        io << yield elem\n      end\n    end\n  end\n\n  # Prints to *io* all the elements in the collection, separated by *separator*.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].join(STDOUT, \", \")\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # 1, 2, 3, 4, 5\n  # ```\n  def join(io : IO, separator = \"\") : Nil\n    join(io, separator) do |elem|\n      elem.to_s(io)\n    end\n  end\n\n  # Prints to *io* all the elements in the collection, separated by *separator*.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].join(STDOUT, \", \")\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # 1, 2, 3, 4, 5\n  # ```\n  @[Deprecated(%(Use `#join(io : IO, separator = \"\") instead`))]\n  def join(separator, io : IO) : Nil\n    join(io, separator)\n  end\n\n  # Prints to *io* the concatenation of the elements, with the possibility of\n  # controlling how the printing is done via a block.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].join(STDOUT, \", \") { |i, io| io << \"(#{i})\" }\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # (1), (2), (3), (4), (5)\n  # ```\n  def join(io : IO, separator = \"\", & : T, IO ->)\n    each_with_index do |elem, i|\n      io << separator if i > 0\n      yield elem, io\n    end\n  end\n\n  # Prints to *io* the concatenation of the elements, with the possibility of\n  # controlling how the printing is done via a block.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].join(STDOUT, \", \") { |i, io| io << \"(#{i})\" }\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # (1), (2), (3), (4), (5)\n  # ```\n  @[Deprecated(%(Use `#join(io : IO, separator = \"\", & : T, IO ->) instead`))]\n  def join(separator, io : IO, &)\n    join(io, separator) do |elem, io|\n      yield elem, io\n    end\n  end\n\n  # Returns an `Array` with the results of running the block against each element of the collection.\n  #\n  # ```\n  # [1, 2, 3].map { |i| i * 10 } # => [10, 20, 30]\n  # ```\n  def map(& : T -> U) : Array(U) forall U\n    map_with_index do |e|\n      yield e\n    end\n  end\n\n  # Like `map`, but the block gets passed both the element and its index.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].map_with_index { |name, i| \"User ##{i}: #{name}\" }\n  # # => [\"User #0: Alice\", \"User #1: Bob\"]\n  # ```\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there.\n  def map_with_index(offset = 0, & : T, Int32 -> U) : Array(U) forall U\n    ary = [] of U\n    each_with_index(offset) { |e, i| ary << yield e, i }\n    ary\n  end\n\n  private def quickselect_internal(data : Array(T), left : Int, right : Int, k : Int) : T\n    loop do\n      return data[left] if left == right\n      pivot_index = left + (right - left)//2\n      pivot_index = quickselect_partition_internal(data, left, right, pivot_index)\n      if k == pivot_index\n        return data[k]\n      elsif k < pivot_index\n        right = pivot_index - 1\n      else\n        left = pivot_index + 1\n      end\n    end\n  end\n\n  private def quickselect_partition_internal(data : Array(T), left : Int, right : Int, pivot_index : Int) : Int\n    pivot_value = data[pivot_index]\n    data.swap(pivot_index, right)\n    store_index = left\n    (left...right).each do |i|\n      if compare_or_raise(data[i], pivot_value) < 0\n        data.swap(store_index, i)\n        store_index += 1\n      end\n    end\n    data.swap(right, store_index)\n    store_index\n  end\n\n  # Returns the element with the maximum value in the collection.\n  #\n  # It compares using `>` so it will work for any type that supports that method.\n  #\n  # ```\n  # [1, 2, 3].max        # => 3\n  # [\"Alice\", \"Bob\"].max # => \"Bob\"\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def max : T\n    max_by &.itself\n  end\n\n  # Like `max` but returns `nil` if the collection is empty.\n  def max? : T?\n    max_by? &.itself\n  end\n\n  # Returns an array of the maximum *count* elements, sorted descending.\n  #\n  # It compares using `<=>` so it will work for any type that supports that method.\n  #\n  # ```\n  # [7, 5, 2, 4, 9].max(3)                 # => [9, 7, 5]\n  # %w[Eve Alice Bob Mallory Carol].max(2) # => [\"Mallory\", \"Eve\"]\n  # ```\n  #\n  # Returns all elements sorted descending if *count* is greater than the number\n  # of elements in the source.\n  #\n  # Raises `Enumerable::ArgumentError` if *count* is negative or if any elements\n  # are not comparable.\n  def max(count : Int) : Array(T)\n    raise ArgumentError.new(\"Count must be positive\") if count < 0\n    data = self.is_a?(Array) ? self.dup : self.to_a\n    n = data.size\n    count = n if count > n\n    (0...count).map do |i|\n      quickselect_internal(data, 0, n - 1, n - 1 - i)\n    end\n  end\n\n  # Returns the element for which the passed block returns with the maximum value.\n  #\n  # It compares using `>` so the block must return a type that supports that method\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].max_by { |name| name.size } # => \"Alice\"\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def max_by(& : T -> U) : T forall U\n    found, value = max_by_internal { |value| yield value }\n    raise Enumerable::EmptyError.new unless found\n    value\n  end\n\n  # Like `max_by` but returns `nil` if the collection is empty.\n  def max_by?(& : T -> U) : T? forall U\n    found, value = max_by_internal { |value| yield value }\n    found ? value : nil\n  end\n\n  private def max_by_internal(& : T -> U) forall U\n    max = uninitialized U\n    obj = uninitialized T\n    found = false\n\n    each_with_index do |elem, i|\n      value = yield elem\n      if i == 0 || compare_or_raise(value, max) > 0\n        max = value\n        obj = elem\n      end\n      found = true\n    end\n\n    {found, obj}\n  end\n\n  # Like `max_by` but instead of the element, returns the value returned by the block.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].max_of { |name| name.size } # => 5 (Alice's size)\n  # ```\n  def max_of(& : T -> U) : U forall U\n    found, value = max_of_internal { |value| yield value }\n    raise Enumerable::EmptyError.new unless found\n    value\n  end\n\n  # Like `max_of` but returns `nil` if the collection is empty.\n  def max_of?(& : T -> U) : U? forall U\n    found, value = max_of_internal { |value| yield value }\n    found ? value : nil\n  end\n\n  private def max_of_internal(& : T -> U) forall U\n    max = uninitialized U\n    found = false\n\n    each_with_index do |elem, i|\n      value = yield elem\n      if i == 0 || compare_or_raise(value, max) > 0\n        max = value\n      end\n      found = true\n    end\n\n    {found, max}\n  end\n\n  # Returns the element with the minimum value in the collection.\n  #\n  # It compares using `<` so it will work for any type that supports that method.\n  #\n  # ```\n  # [1, 2, 3].min        # => 1\n  # [\"Alice\", \"Bob\"].min # => \"Alice\"\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def min : T\n    min_by &.itself\n  end\n\n  # Like `min` but returns `nil` if the collection is empty.\n  def min? : T?\n    min_by? &.itself\n  end\n\n  # Returns an array of the minimum *count* elements, sorted ascending.\n  #\n  # It compares using `<=>` so it will work for any type that supports that method.\n  #\n  # ```\n  # [7, 5, 2, 4, 9].min(3)                 # => [2, 4, 5]\n  # %w[Eve Alice Bob Mallory Carol].min(2) # => [\"Alice\", \"Bob\"]\n  # ```\n  #\n  # Returns all elements sorted ascending if *count* is greater than the number\n  # of elements in the source.\n  #\n  # Raises `Enumerable::ArgumentError` if *count* is negative or if any elements\n  # are not comparable.\n  def min(count : Int) : Array(T)\n    raise ArgumentError.new(\"Count must be positive\") if count < 0\n    data = self.is_a?(Array) ? self.dup : self.to_a\n    n = data.size\n    count = n if count > n\n    (0...count).map do |i|\n      quickselect_internal(data, 0, n - 1, i)\n    end\n  end\n\n  # Returns the element for which the passed block returns with the minimum value.\n  #\n  # It compares using `<` so the block must return a type that supports that method\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].min_by { |name| name.size } # => \"Bob\"\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def min_by(& : T -> U) : T forall U\n    found, value = min_by_internal { |value| yield value }\n    raise Enumerable::EmptyError.new unless found\n    value\n  end\n\n  # Like `min_by` but returns `nil` if the collection is empty.\n  def min_by?(& : T -> U) : T? forall U\n    found, value = min_by_internal { |value| yield value }\n    found ? value : nil\n  end\n\n  private def min_by_internal(& : T -> U) forall U\n    min = uninitialized U\n    obj = uninitialized T\n    found = false\n\n    each_with_index do |elem, i|\n      value = yield elem\n      if i == 0 || compare_or_raise(value, min) < 0\n        min = value\n        obj = elem\n      end\n      found = true\n    end\n\n    {found, obj}\n  end\n\n  # Like `min_by` but instead of the element, returns the value returned by the block.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].min_of { |name| name.size } # => 3 (Bob's size)\n  # ```\n  def min_of(& : T -> U) : U forall U\n    found, value = min_of_internal { |value| yield value }\n    raise Enumerable::EmptyError.new unless found\n    value\n  end\n\n  # Like `min_of` but returns `nil` if the collection is empty.\n  def min_of?(& : T -> U) : U? forall U\n    found, value = min_of_internal { |value| yield value }\n    found ? value : nil\n  end\n\n  private def min_of_internal(& : T -> U) forall U\n    min = uninitialized U\n    found = false\n\n    each_with_index do |elem, i|\n      value = yield elem\n      if i == 0 || compare_or_raise(value, min) < 0\n        min = value\n      end\n      found = true\n    end\n\n    {found, min}\n  end\n\n  # Returns a `Tuple` with both the minimum and maximum value.\n  #\n  # ```\n  # [1, 2, 3].minmax # => {1, 3}\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def minmax : {T, T}\n    minmax_by &.itself\n  end\n\n  # Like `minmax` but returns `{nil, nil}` if the collection is empty.\n  def minmax? : {T?, T?}\n    minmax_by? &.itself\n  end\n\n  # Returns a `Tuple` with both the minimum and maximum values according to the passed block.\n  #\n  # ```\n  # [\"Alice\", \"Bob\", \"Carl\"].minmax_by { |name| name.size } # => {\"Bob\", \"Alice\"}\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def minmax_by(& : T -> U) : {T, T} forall U\n    found, value = minmax_by_internal { |value| yield value }\n    raise Enumerable::EmptyError.new unless found\n    value\n  end\n\n  # Like `minmax_by` but returns `{nil, nil}` if the collection is empty.\n  def minmax_by?(& : T -> U) : {T, T} | {Nil, Nil} forall U\n    found, value = minmax_by_internal { |value| yield value }\n    found ? value : {nil, nil}\n  end\n\n  private def minmax_by_internal(& : T -> U) forall U\n    min = uninitialized U\n    max = uninitialized U\n    objmin = uninitialized T\n    objmax = uninitialized T\n    found = false\n\n    each_with_index do |elem, i|\n      value = yield elem\n      if i == 0 || compare_or_raise(value, min) < 0\n        min = value\n        objmin = elem\n      end\n      if i == 0 || compare_or_raise(value, max) > 0\n        max = value\n        objmax = elem\n      end\n      found = true\n    end\n\n    {found, {objmin, objmax}}\n  end\n\n  # Returns a `Tuple` with both the minimum and maximum value\n  # the block returns when passed the elements in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\", \"Carl\"].minmax_of { |name| name.size } # => {3, 5}\n  # ```\n  #\n  # Raises `Enumerable::EmptyError` if the collection is empty.\n  def minmax_of(& : T -> U) : {U, U} forall U\n    found, value = minmax_of_internal { |value| yield value }\n    raise Enumerable::EmptyError.new unless found\n    value\n  end\n\n  # Like `minmax_of` but returns `{nil, nil}` if the collection is empty.\n  def minmax_of?(& : T -> U) : {U, U} | {Nil, Nil} forall U\n    found, value = minmax_of_internal { |value| yield value }\n    found ? value : {nil, nil}\n  end\n\n  private def minmax_of_internal(& : T -> U) forall U\n    min = uninitialized U\n    max = uninitialized U\n    found = false\n\n    each_with_index do |elem, i|\n      value = yield elem\n      if i == 0 || compare_or_raise(value, min) < 0\n        min = value\n      end\n      if i == 0 || compare_or_raise(value, max) > 0\n        max = value\n      end\n      found = true\n    end\n\n    {found, {min, max}}\n  end\n\n  private def compare_or_raise(value, memo)\n    value <=> memo || raise ArgumentError.new(\"Comparison of #{value} and #{memo} failed\")\n  end\n\n  # Returns `true` if the passed block is truthy\n  # for none of the elements of the collection.\n  #\n  # ```\n  # [1, 2, 3].none? { |i| i > 5 } # => true\n  # ```\n  #\n  # It's the opposite of `all?`.\n  def none?(& : T ->) : Bool\n    each { |e| return false if yield(e) }\n    true\n  end\n\n  # Returns `true` if `pattern === element` for no element in\n  # this enumerable.\n  #\n  # ```\n  # [2, 3, 4].none?(5..7)      # => true\n  # [2, \"a\", 3].none?(String)  # => false\n  # %w[foo bar baz].none?(/e/) # => true\n  # ```\n  def none?(pattern) : Bool\n    none? { |e| pattern === e }\n  end\n\n  # Returns `true` if all of the elements of the collection are falsey.\n  #\n  # ```\n  # [nil, false].none?       # => true\n  # [nil, false, true].none? # => false\n  # ```\n  #\n  # It's the opposite of `all?`.\n  def none? : Bool\n    none? &.itself\n  end\n\n  # Returns `true` if the passed block is truthy\n  # for exactly one of the elements of the collection.\n  #\n  # ```\n  # [1, 2, 3].one? { |i| i > 2 } # => true\n  # [1, 2, 3].one? { |i| i > 1 } # => false\n  # ```\n  def one?(& : T ->) : Bool\n    c = 0\n    each do |e|\n      c += 1 if yield(e)\n      return false if c > 1\n    end\n    c == 1\n  end\n\n  # Returns `true` if `pattern === element` for just one element\n  # in this enumerable.\n  #\n  # ```\n  # [1, 10, 100].one?(7..14)   # => true\n  # [2, \"a\", 3].one?(Int32)    # => false\n  # %w[foo bar baz].one?(/oo/) # => true\n  # ```\n  def one?(pattern) : Bool\n    one? { |e| pattern === e }\n  end\n\n  # Returns `true` if only one element in this enumerable\n  # is truthy.\n  #\n  # ```\n  # [1, false, false].one? # => true\n  # [1, false, 3].one?     # => false\n  # [1].one?               # => true\n  # [false].one?           # => false\n  # ```\n  def one? : Bool\n    one? &.itself\n  end\n\n  # Returns a `Tuple` with two arrays. The first one contains the elements\n  # in the collection for which the passed block is truthy,\n  # and the second one those for which the block is falsey.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].partition { |i| i % 2 == 0 } # => {[2, 4, 6], [1, 3, 5]}\n  # ```\n  def partition(& : T ->) : {Array(T), Array(T)}\n    a, b = [] of T, [] of T\n    each do |e|\n      value = yield(e)\n      value ? a.push(e) : b.push(e)\n    end\n    {a, b}\n  end\n\n  # Returns a `Tuple` with two arrays. The first one contains the elements\n  # in the collection that are of the given *type*,\n  # and the second one that are **not** of the given *type*.\n  #\n  # ```\n  # ints, others = [1, true, nil, 3, false, \"string\", 'c'].partition(Int32)\n  # ints           # => [1, 3]\n  # others         # => [true, nil, false, \"string\", 'c']\n  # typeof(ints)   # => Array(Int32)\n  # typeof(others) # => Array(String | Char | Nil)\n  # ```\n  def partition(type : U.class) forall U\n    a = [] of U\n    b = [] of typeof(begin\n      e = Enumerable.element_type(self)\n      e.is_a?(U) ? raise(\"\") : e\n    end)\n    each do |e|\n      e.is_a?(U) ? a.push(e) : b.push(e)\n    end\n    {a, b}\n  end\n\n  # Returns an `Array` with all the elements in the collection for which\n  # the passed block is falsey.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].reject { |i| i % 2 == 0 } # => [1, 3, 5]\n  # ```\n  def reject(& : T ->)\n    ary = [] of T\n    each { |e| ary << e unless yield e }\n    ary\n  end\n\n  # Returns an `Array` with all the elements in the collection\n  # that are **not** of the given *type*.\n  #\n  # ```\n  # ints = [1, true, 3, false].reject(Bool)\n  # ints         # => [1, 3]\n  # typeof(ints) # => Array(Int32)\n  # ```\n  def reject(type : U.class) forall U\n    ary = [] of typeof(begin\n      e = Enumerable.element_type(self)\n      e.is_a?(U) ? raise(\"\") : e\n    end)\n    each { |e| ary << e unless e.is_a?(U) }\n    ary\n  end\n\n  # Returns an `Array` with all the elements in the collection for which\n  # `pattern === element` is false.\n  #\n  # ```\n  # [1, 3, 2, 5, 4, 6].reject(3..5) # => [1, 2, 6]\n  # ```\n  def reject(pattern) : Array(T)\n    reject { |e| pattern === e }\n  end\n\n  # Returns an `Array` of *n* random elements from `self`. All elements have\n  # equal probability of being drawn. Sampling is done without replacement; if\n  # *n* is larger than the size of this collection, the returned `Array` has the\n  # same size as `self`.\n  #\n  # Raises `ArgumentError` if *n* is negative.\n  #\n  # ```\n  # [1, 2, 3, 4, 5].sample(2) # => [3, 5]\n  # {1, 2, 3, 4, 5}.sample(2) # => [3, 4]\n  # ```\n  #\n  # Uses the *random* instance when provided if the randomness needs to be\n  # controlled or to follow some traits. For example the following calls use a\n  # custom seed or a secure random source:\n  #\n  # ```\n  # {1, 2, 3, 4, 5}.sample(2, Random.new(1))  # => [1, 5]\n  # {1, 2, 3, 4, 5}.sample(2, Random::Secure) # => [2, 5]\n  # ```\n  def sample(n : Int, random : Random? = nil) : Array(T)\n    raise ArgumentError.new(\"Can't sample negative number of elements\") if n < 0\n\n    # Unweighted reservoir sampling:\n    # https://en.wikipedia.org/wiki/Reservoir_sampling#Simple_algorithm\n    # \"Algorithm L\" does not provide any performance improvements on Enumerable,\n    # because it is not possible to discard multiple elements at once\n\n    ary = Array(T).new(n)\n    return ary if n == 0\n\n    # must split the default random instance (thread local) because #each might\n    # yield the current fiber that may be resumed by another thread\n    rng = random || Random.split_on_stack\n\n    each_with_index do |elem, i|\n      if i < n\n        ary << elem\n      else\n        j = rng.rand(i + 1)\n        if j < n\n          ary.to_unsafe[j] = elem\n        end\n      end\n    end\n\n    ary.shuffle!(rng)\n  end\n\n  # Returns a random element from `self`. All elements have equal probability of\n  # being drawn.\n  #\n  # Raises `IndexError` if `self` is empty.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.sample # => 2\n  # a.sample # => 1\n  # ```\n  #\n  # Uses the *random* instance when provided if the randomness needs to be\n  # controlled or to follow some traits. For example the following calls use a\n  # custom seed or a secure random source:\n  #\n  # ```\n  # a.sample(Random.new(1))  # => 3\n  # a.sample(Random::Secure) # => 1\n  # ```\n  def sample(random : Random? = nil) : T\n    value = uninitialized T\n    found = false\n\n    # must split the default random instance (thread local) because #each might\n    # yield the current fiber that may be resumed by another thread\n    rng = random || Random.split_on_stack\n\n    each_with_index do |elem, i|\n      if !found\n        value = elem\n        found = true\n      elsif rng.rand(i + 1) == 0\n        value = elem\n      end\n    end\n\n    found ? value : raise IndexError.new(\"Can't sample empty collection\")\n  end\n\n  # Returns an `Array` with all the elements in the collection for which\n  # the passed block is truthy.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].select { |i| i % 2 == 0 } # => [2, 4, 6]\n  # ```\n  def select(& : T ->)\n    ary = [] of T\n    each { |e| ary << e if yield e }\n    ary\n  end\n\n  # Returns an `Array` with all the elements in the collection\n  # that are of the given *type*.\n  #\n  # ```\n  # ints = [1, true, nil, 3, false].select(Int32)\n  # ints         # => [1, 3]\n  # typeof(ints) # => Array(Int32)\n  # ```\n  def select(type : U.class) : Array(U) forall U\n    ary = [] of U\n    each { |e| ary << e if e.is_a?(U) }\n    ary\n  end\n\n  # Returns an `Array` with all the elements in the collection for which\n  # `pattern === element`.\n  #\n  # ```\n  # [1, 3, 2, 5, 4, 6].select(3..5) # => [3, 5, 4]\n  # [\"Alice\", \"Bob\"].select(/^A/)   # => [\"Alice\"]\n  # ```\n  def select(pattern) : Array(T)\n    self.select { |elem| pattern === elem }\n  end\n\n  # Returns the number of elements in the collection.\n  #\n  # ```\n  # [1, 2, 3, 4].size # => 4\n  # ```\n  def size : Int32\n    count { true }\n  end\n\n  # Returns `true` if `self` does not contain any element.\n  #\n  # ```\n  # ([] of Int32).empty? # => true\n  # ([1]).empty?         # => false\n  # [nil, false].empty?  # => false\n  # ```\n  #\n  # * `#present?` returns the inverse.\n  def empty? : Bool\n    each { return false }\n    true\n  end\n\n  # Returns `true` if `self` contains at least one element.\n  #\n  # ```\n  # ([] of Int32).present? # => false\n  # ([1]).present?         # => true\n  # [nil, false].present?  # => true\n  # ```\n  #\n  # * `#empty?` returns the inverse.\n  # * `#any?` considers only truthy elements.\n  # * `#any?(&)` and `#any(pattern)` allow custom conditions.\n  def present? : Bool\n    !empty?\n  end\n\n  # Returns an `Array` with the first *count* elements removed\n  # from the original collection.\n  #\n  # If *count* is bigger than the number of elements in the collection, returns an empty array.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].skip(3) # => [4, 5, 6]\n  # ```\n  def skip(count : Int)\n    raise ArgumentError.new(\"Attempt to skip negative size\") if count < 0\n\n    array = Array(T).new\n    each_with_index do |e, i|\n      array << e if i >= count\n    end\n    array\n  end\n\n  # Skips elements up to, but not including, the first element for which\n  # the block is falsey, and returns an `Array`\n  # containing the remaining elements.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 0].skip_while { |i| i < 3 } # => [3, 4, 5, 0]\n  # ```\n  def skip_while(& : T ->) : Array(T)\n    result = Array(T).new\n    block_returned_false = false\n    each do |x|\n      block_returned_false = true unless block_returned_false || yield x\n      result << x if block_returned_false\n    end\n    result\n  end\n\n  # Adds all the elements in the collection together.\n  #\n  # Expects all element types to respond to `#+` method.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].sum # => 21\n  # ```\n  #\n  # This method calls `.additive_identity` on the yielded type to determine the\n  # type of the sum value.\n  #\n  # If the collection is empty, returns `additive_identity`.\n  #\n  # ```\n  # ([] of Int32).sum # => 0\n  # ```\n  def sum\n    {% if T == String %}\n      # optimize for string\n      join\n    {% elsif T < Array %}\n      # optimize for array\n      flat_map &.itself\n    {% elsif T < Slice && @type < Indexable %}\n      # optimize for slice\n      Slice.join(self)\n    {% else %}\n      sum additive_identity(Reflect(T))\n    {% end %}\n  end\n\n  private def additive_identity(reflect)\n    type = reflect.type\n    if type.responds_to? :additive_identity\n      type.additive_identity\n    else\n      type.zero\n    end\n  end\n\n  # Adds *initial* and all the elements in the collection together.\n  # The type of *initial* will be the type of the sum, so use this if\n  # (for instance) you need to specify a large enough type to avoid\n  # overflow.\n  #\n  # Expects all element types to respond to `#+` method.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].sum(7) # => 28\n  # ```\n  #\n  # If the collection is empty, returns *initial*.\n  #\n  # ```\n  # ([] of Int32).sum(7) # => 7\n  # ```\n  def sum(initial)\n    sum initial, &.itself\n  end\n\n  # Adds all results of the passed block for each element in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].sum { |name| name.size } # => 8 (5 + 3)\n  # ```\n  #\n  # Expects all types returned from the block to respond to `#+` method.\n  #\n  # This method calls `.additive_identity` on the yielded type to determine the\n  # type of the sum value.  Hence, it can fail to compile if\n  # `.additive_identity` fails to determine a safe type, e.g., in case of\n  # union types.  In such cases, use `sum(initial)` with an initial value of\n  # the expected type of the sum value.\n  #\n  # If the collection is empty, returns `additive_identity`.\n  #\n  # ```\n  # ([] of Int32).sum { |x| x + 1 } # => 0\n  # ```\n  def sum(& : T ->)\n    sum(additive_identity(Reflect(typeof(yield Enumerable.element_type(self))))) do |value|\n      yield value\n    end\n  end\n\n  # Adds *initial* and all results of the passed block for each element in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].sum(1) { |name| name.size } # => 9 (1 + 5 + 3)\n  # ```\n  #\n  # Expects all types returned from the block to respond to `#+` method.\n  #\n  # If the collection is empty, returns *initial*.\n  #\n  # ```\n  # ([] of String).sum(1) { |name| name.size } # => 1\n  # ```\n  def sum(initial, & : T ->)\n    reduce(initial) { |memo, e| memo + (yield e) }\n  end\n\n  # Multiplies all the elements in the collection together.\n  #\n  # Expects all element types to respond to `#*` method.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].product # => 720\n  # ```\n  #\n  # This method calls `.multiplicative_identity` on the element type to determine the\n  # type of the product value.\n  #\n  # If the collection is empty, returns `multiplicative_identity`.\n  #\n  # ```\n  # ([] of Int32).product # => 1\n  # ```\n  def product\n    product Reflect(T).type.multiplicative_identity\n  end\n\n  # Multiplies *initial* and all the elements in the collection\n  # together.  The type of *initial* will be the type of the product,\n  # so use this if (for instance) you need to specify a large enough\n  # type to avoid overflow.\n  #\n  # Expects all element types to respond to `#*` method.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 6].product(7) # => 5040\n  # ```\n  #\n  # If the collection is empty, returns *initial*.\n  #\n  # ```\n  # ([] of Int32).product(7) # => 7\n  # ```\n  def product(initial : Number)\n    product initial, &.itself\n  end\n\n  # Multiplies all results of the passed block for each element in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].product { |name| name.size } # => 15 (5 * 3)\n  # ```\n  #\n  # Expects all types returned from the block to respond to `#*` method.\n  #\n  # This method calls `.multiplicative_identity` on the element type to\n  # determine the type of the product value.  Hence, it can fail to compile if\n  # `.multiplicative_identity` fails to determine a safe type, e.g., in case\n  # of union types.  In such cases, use `product(initial)` with an initial\n  # value of the expected type of the product value.\n  #\n  # If the collection is empty, returns `multiplicative_identity`.\n  #\n  # ```\n  # ([] of Int32).product { |x| x + 1 } # => 1\n  # ```\n  def product(& : T -> _)\n    product(Reflect(typeof(yield Enumerable.element_type(self))).type.multiplicative_identity) do |value|\n      yield value\n    end\n  end\n\n  # Multiplies *initial* and all results of the passed block for each element\n  # in the collection.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].product(2) { |name| name.size } # => 30 (2 * 5 * 3)\n  # ```\n  #\n  # Expects all types returned from the block to respond to `#*` method.\n  #\n  # If the collection is empty, returns `1`.\n  #\n  # ```\n  # ([] of String).product(1) { |name| name.size } # => 1\n  # ```\n  def product(initial : Number, & : T ->)\n    reduce(initial) { |memo, e| memo * (yield e) }\n  end\n\n  # Returns an `Array` with the first *count* elements in the collection.\n  #\n  # If *count* is bigger than the number of elements in the collection,\n  # returns as many as possible. This include the case of calling it over\n  # an empty collection, in which case it returns an empty array.\n  def first(count : Int) : Array(T)\n    raise ArgumentError.new(\"Attempt to take negative size\") if count < 0\n\n    ary = Array(T).new(count)\n    each_with_index do |e, i|\n      break if i == count\n      ary << e\n    end\n    ary\n  end\n\n  # Passes elements to the block until the block returns a falsey value,\n  # then stops iterating and returns an `Array` of all prior elements.\n  #\n  # ```\n  # [1, 2, 3, 4, 5, 0].take_while { |i| i < 3 } # => [1, 2]\n  # ```\n  def take_while(& : T ->) : Array(T)\n    result = Array(T).new\n    each do |x|\n      break unless yield x\n      result << x\n    end\n    result\n  end\n\n  # Tallies the collection.  Returns a hash where the keys are the\n  # elements and the values are numbers of elements in the collection\n  # that correspond to the key after transformation by the given block.\n  #\n  # ```\n  # [\"a\", \"A\", \"b\", \"B\"].tally_by(&.downcase) # => {\"a\" => 2, \"b\" => 2}\n  # ```\n  def tally_by(&block : T -> U) : Hash(U, Int32) forall U\n    tally_by(Hash(U, Int32).new, &block)\n  end\n\n  # Tallies the collection. Accepts a *hash* to count occurrences.\n  # The value corresponding to each element must be an integer.\n  # Returns *hash* where the keys are the\n  # elements and the values are numbers of elements in the collection\n  # that correspond to the key after transformation by the given block.\n  #\n  # ```\n  # hash = {} of Char => Int32\n  # words = [\"Crystal\", \"Ruby\"]\n  # words.each { |word| word.chars.tally_by(hash, &.downcase) }\n  # hash # => {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}\n  # ```\n  def tally_by(hash, &)\n    each_with_object(hash) do |item, hash|\n      value = yield item\n\n      count = hash.fetch(value) { typeof(hash[value]).zero }\n      hash[value] = count + 1\n    end\n  end\n\n  # Tallies the collection.  Returns a hash where the keys are the\n  # elements and the values are numbers of elements in the collection\n  # that correspond to the key.\n  #\n  # ```\n  # [\"a\", \"b\", \"c\", \"b\"].tally # => {\"a\"=>1, \"b\"=>2, \"c\"=>1}\n  # ```\n  def tally : Hash(T, Int32)\n    tally_by(Hash(T, Int32).new, &.itself)\n  end\n\n  # Tallies the collection. Accepts a *hash* to count occurrences.\n  # The value corresponding to each element must be an integer.\n  # The number of occurrences is added to each value in *hash*,\n  # and *hash* is returned.\n  #\n  # ```\n  # hash = {} of Char => Int32\n  # words = [\"crystal\", \"ruby\"]\n  # words.each { |word| word.chars.tally(hash) }\n  # hash # => {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}\n  # ```\n  def tally(hash)\n    tally_by(hash, &.itself)\n  end\n\n  # Returns an `Array` with all the elements in the collection.\n  #\n  # ```\n  # (1..5).to_a # => [1, 2, 3, 4, 5]\n  # ```\n  def to_a : Array(T)\n    to_a(&.as(T))\n  end\n\n  # Returns an `Array` with the results of running *block* against each element of the collection.\n  #\n  # ```\n  # (1..5).to_a { |i| i * 2 } # => [2, 4, 6, 8, 10]\n  # ```\n  def to_a(& : T -> U) : Array(U) forall U\n    ary = [] of U\n    each { |e| ary << yield e }\n    ary\n  end\n\n  # Creates a `Hash` out of an Enumerable where each element is a\n  # 2 element structure (for instance a `Tuple` or an `Array`).\n  #\n  # ```\n  # [[:a, :b], [:c, :d]].to_h        # => {:a => :b, :c => :d}\n  # Tuple.new({:a, 1}, {:c, 2}).to_h # => {:a => 1, :c => 2}\n  # ```\n  def to_h\n    each_with_object(Hash(typeof(Enumerable.element_type(self)[0]), typeof(Enumerable.element_type(self)[1])).new) do |item, hash|\n      hash[item[0]] = item[1]\n    end\n  end\n\n  # Creates a `Hash` out of `Tuple` pairs (key, value) returned from the *block*.\n  #\n  # ```\n  # (1..3).to_h { |i| {i, i ** 2} } # => {1 => 1, 2 => 4, 3 => 9}\n  # ```\n  def to_h(& : T -> Tuple(K, V)) forall K, V\n    each_with_object({} of K => V) do |item, hash|\n      key, value = yield item\n      hash[key] = value\n    end\n  end\n\n  # Yields elements of `self` and *others* in tandem to the given block.\n  #\n  # Raises an `IndexError` if any of *others* doesn't have as many elements\n  # as `self`. See `zip?` for a version that yields `nil` instead of raising.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = [\"a\", \"b\", \"c\"]\n  #\n  # a.zip(b) { |x, y| puts \"#{x} -- #{y}\" }\n  # ```\n  #\n  # The above produces:\n  #\n  # ```text\n  # 1 -- a\n  # 2 -- b\n  # 3 -- c\n  # ```\n  #\n  # An example with multiple arguments:\n  #\n  # ```\n  # (1..3).zip(4..6, 7..9) do |x, y, z|\n  #   puts \"#{x} -- #{y} -- #{z}\"\n  # end\n  # ```\n  #\n  # The above produces:\n  #\n  # ```text\n  # 1 -- 4 -- 7\n  # 2 -- 5 -- 8\n  # 3 -- 6 -- 9\n  # ```\n  def zip(*others : Indexable | Iterable | Iterator, &)\n    Enumerable.zip(self, others) do |elems|\n      yield elems\n    end\n  end\n\n  # Returns an `Array` of tuples populated with the elements of `self` and\n  # *others* traversed in tandem.\n  #\n  # Raises an `IndexError` if any of *others* doesn't have as many elements\n  # as `self`. See `zip?` for a version that yields `nil` instead of raising.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = [\"a\", \"b\", \"c\"]\n  #\n  # a.zip(b) # => [{1, \"a\"}, {2, \"b\"}, {3, \"c\"}]\n  # ```\n  #\n  # An example with multiple arguments:\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = (4..6)\n  # c = 8.downto(3)\n  #\n  # a.zip(b, c) # => [{1, 4, 8}, {2, 5, 7}, {3, 6, 6}]\n  # ```\n  def zip(*others : Indexable | Iterable | Iterator)\n    size = self.is_a?(Indexable) ? self.size : 0\n    pairs = Array(typeof(zip(*others) { |e| break e }.not_nil!)).new(size)\n    zip(*others) { |e| pairs << e }\n    pairs\n  end\n\n  # Yields elements of `self` and *others* in tandem to the given block.\n  #\n  # All of the elements in `self` will be yielded: if *others* don't have\n  # that many elements they will be returned as `nil`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = [\"a\", \"b\"]\n  #\n  # a.zip?(b) { |x, y| puts \"#{x.inspect} -- #{y.inspect}\" }\n  # ```\n  #\n  # The above produces:\n  #\n  # ```text\n  # 1 -- \"a\"\n  # 2 -- \"b\"\n  # 3 -- nil\n  # ```\n  #\n  # An example with multiple arguments:\n  #\n  # ```\n  # (1..3).zip?(4..5, 7..8) do |x, y, z|\n  #   puts \"#{x.inspect} -- #{y.inspect} -- #{z.inspect}\"\n  # end\n  # ```\n  #\n  # The above produces:\n  #\n  # ```text\n  # 1 -- 4 -- 7\n  # 2 -- 5 -- 8\n  # 3 -- nil -- nil\n  # ```\n  def zip?(*others : Indexable | Iterable | Iterator, &)\n    Enumerable.zip?(self, others) do |elems|\n      yield elems\n    end\n  end\n\n  # Returns an `Array` of tuples populated with the elements of `self` and\n  # *others* traversed in tandem.\n  #\n  # All elements in `self` are returned in the Array. If matching elements\n  # in *others* are missing (because they don't have that many elements)\n  # `nil` is returned inside that tuple index.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = [\"a\", \"b\"]\n  #\n  # a.zip?(b) # => [{1, \"a\"}, {2, \"b\"}, {3, nil}]\n  # ```\n  #\n  # An example with multiple arguments:\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = (4..5)\n  # c = 8.downto(7)\n  #\n  # a.zip?(b, c) # => [{1, 4, 8}, {2, 5, 7}, {3, nil, nil}]\n  # ```\n  def zip?(*others : Indexable | Iterable | Iterator)\n    size = self.is_a?(Indexable) ? self.size : 0\n    pairs = Array(typeof(zip?(*others) { |e| break e }.not_nil!)).new(size)\n    zip?(*others) { |e| pairs << e }\n    pairs\n  end\n\n  # :nodoc:\n  def self.zip(main, others : U, &) forall U\n    {% begin %}\n      {% for type, type_index in U %}\n        other{{type_index}} = others[{{type_index}}]\n      {% end %}\n\n      # Try to see if we need to create iterators (or treat as iterators)\n      # for every element in `others`.\n      {% for type, type_index in U %}\n        case other{{type_index}}\n        when Indexable\n          # Nothing to do, but needed because many Indexables are Iterable/Iterator\n        when Iterable\n          iter{{type_index}} = other{{type_index}}.each\n        else\n          iter{{type_index}} = other{{type_index}}\n        end\n      {% end %}\n\n      main.each_with_index do |elem, i|\n        {% for type, type_index in U %}\n          if other{{type_index}}.is_a?(Indexable)\n            # Index into those we can\n            other_elem{{type_index}} = other{{type_index}}[i]\n          else\n            # Otherwise advance the iterator\n            other_elem{{type_index}} = iter{{type_index}}.not_nil!.next\n            if other_elem{{type_index}}.is_a?(Iterator::Stop)\n              raise IndexError.new\n            end\n          end\n        {% end %}\n\n        # Yield all elements as a tuple\n        yield({\n          elem,\n          {% for _t, type_index in U %}\n            other_elem{{type_index}},\n          {% end %}\n        })\n      end\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.zip?(main, others : U, &) forall U\n    {% begin %}\n      {% for type, type_index in U %}\n        other{{type_index}} = others[{{type_index}}]\n      {% end %}\n\n      # Try to see if we need to create iterators (or treat as iterators)\n      # for every element in `others`.\n      {% for type, type_index in U %}\n        case other{{type_index}}\n        when Indexable\n          # Nothing to do, but needed because many Indexables are Iterable/Iterator\n        when Iterable\n          iter{{type_index}} = other{{type_index}}.each\n        else\n          iter{{type_index}} = other{{type_index}}\n        end\n      {% end %}\n\n      main.each_with_index do |elem, i|\n        {% for type, type_index in U %}\n          if other{{type_index}}.is_a?(Indexable)\n            # Index into those we can\n            other_elem{{type_index}} = other{{type_index}}[i]?\n          else\n            # Otherwise advance the iterator\n            other_elem{{type_index}} = iter{{type_index}}.not_nil!.next\n            if other_elem{{type_index}}.is_a?(Iterator::Stop)\n              other_elem{{type_index}} = nil\n            end\n          end\n        {% end %}\n\n        # Yield all elements as a tuple\n        yield({\n          elem,\n          {% for _t, type_index in U %}\n            other_elem{{type_index}},\n          {% end %}\n        })\n      end\n    {% end %}\n  end\n\n  # :nodoc:\n  private struct Reflect(X)\n    # For now, Reflect is used to reject union types in `#sum()` and\n    # `#product()` methods.\n    def self.type\n      {% if X.union? %}\n        {{\n          raise(\"`Enumerable#sum` and `#product` do not support Union \" +\n                \"types. Instead, use `Enumerable#sum(initial)` and \" +\n                \"`#product(initial)`, respectively, with an initial value \" +\n                \"of the intended type of the call.\")\n        }}\n      {% else %}\n        X\n      {% end %}\n    end\n  end\n\n  # Returns a value with the same type as an element of *x*, even if *x* is not\n  # an `Enumerable`.\n  #\n  # Used by splat expansion inside array literals. For example, this code\n  #\n  # ```\n  # [1, *{2, 3.5}, 4]\n  # ```\n  #\n  # will end up calling `typeof(1, ::Enumerable.element_type({2, 3.5}), 4)`.\n  #\n  # NOTE: there should never be a need to call this method outside the standard library.\n  def self.element_type(x)\n    x.each { |elem| return elem }\n    ::raise \"\"\n  end\nend\n"
  },
  {
    "path": "src/env.cr",
    "content": "require \"crystal/system/env\"\n\n# `ENV` is a hash-like accessor for environment variables.\n#\n# ### Example\n#\n# Assuming the following example is invoked with the `HOST=localhost` and\n# `PORT=5000` environment variables:\n#\n# ```\n# p ENV[\"HOST\"]                       # => \"localhost\"\n# p ENV[\"PORT\"].to_i                  # => 5000\n# p ENV.fetch(\"TLS_PORT\", \"443\").to_i # => 443\n# ```\n#\n# NOTE: All keys and values are strings. You must take care to cast other types\n# at runtime, e.g. integer port numbers.\n#\n# ### Safety\n#\n# Modifying the environment in single-threaded programs is safe. Modifying the\n# environment is also always safe on Windows.\n#\n# Modifying the environment in multi-threaded programs on other targets is\n# always unsafe, and can cause a mere read to segfault! At best, memory will be\n# leaked every time the environment is modified.\n#\n# The problem is that POSIX systems don't guarantee a thread safe implementation\n# of the `getenv`, `setenv` and `putenv` libc functions. Any thread that gets an\n# environment variable while another thread sets an environment variable may\n# segfault. The Crystal runtime implementation of `ENV` itself is protected by a\n# readers-writer lock, but we can't protect against external libraries,\n# including libc calls made by the stdlib, to call `getenv` internally without\n# holding the read lock while a crystal fiber with the write lock calls\n# `setenv`.\n#\n# The only safe solution is to consider `ENV` to be immutable, and to never call\n# `ENV.[]=`, `ENV.delete` or `ENV.clear` in your programs. If you really need\n# to, you must make sure that no other thread has been started (beware of\n# libraries that may start threads without your knowledge).\n#\n# NOTE: Passing environment variables to a child process should use the `env`\n# arg of `Process.run` and `Process.new`.\nmodule ENV\n  extend Enumerable({String, String})\n\n  # Retrieves the value for environment variable named *key* as a `String`.\n  # Raises `KeyError` if the named variable does not exist.\n  def self.[](key : String) : String\n    fetch(key)\n  end\n\n  # Retrieves the value for environment variable named *key* as a `String?`.\n  # Returns `nil` if the named variable does not exist.\n  def self.[]?(key : String) : String?\n    fetch(key, nil)\n  end\n\n  # Sets the value for environment variable named *key* as *value*.\n  # Overwrites existing environment variable if already present.\n  # Returns *value* if successful, otherwise raises an exception.\n  # If *value* is `nil`, the environment variable is deleted.\n  #\n  # If *key* or *value* contains a null-byte an `ArgumentError` is raised.\n  #\n  # WARNING: It is recommended to never set environment variables. See the\n  # Safety section of `ENV` for details.\n  def self.[]=(key : String, value : String?)\n    Crystal::System::Env.set(key, value)\n\n    value\n  end\n\n  # Returns `true` if the environment variable named *key* exists and `false` if it doesn't.\n  #\n  # ```\n  # ENV.has_key?(\"NOT_A_REAL_KEY\") # => false\n  # ENV.has_key?(\"PATH\")           # => true\n  # ```\n  def self.has_key?(key : String) : Bool\n    Crystal::System::Env.has_key?(key)\n  end\n\n  # Retrieves a value corresponding to the given *key*. Raises a `KeyError` exception if the\n  # key does not exist.\n  def self.fetch(key) : String\n    fetch(key) do\n      raise KeyError.new \"Missing ENV key: #{key.inspect}\"\n    end\n  end\n\n  # Retrieves a value corresponding to the given *key*. Return the second argument's value\n  # if the *key* does not exist.\n  def self.fetch(key, default : T) : String | T forall T\n    fetch(key) { default }\n  end\n\n  # Retrieves a value corresponding to a given *key*. Return the value of the block if\n  # the *key* does not exist.\n  def self.fetch(key : String, &block : String -> T) : String | T forall T\n    if value = Crystal::System::Env.get(key)\n      value\n    else\n      yield key\n    end\n  end\n\n  # Returns an array of all the environment variable names.\n  def self.keys : Array(String)\n    keys = [] of String\n    each { |key, _| keys << key }\n    keys\n  end\n\n  # Returns an array of all the environment variable values.\n  def self.values : Array(String)\n    values = [] of String\n    each { |_, value| values << value }\n    values\n  end\n\n  # Removes the environment variable named *key*. Returns the previous value if\n  # the environment variable existed, otherwise returns `nil`.\n  #\n  # WARNING: It is recommended to never delete environment variables. See the\n  # Safety section of `ENV` for details.\n  def self.delete(key : String) : String?\n    if value = self[key]?\n      Crystal::System::Env.set(key, nil)\n      value\n    else\n      nil\n    end\n  end\n\n  # Iterates over all `KEY=VALUE` pairs of environment variables, yielding both\n  # the *key* and *value*.\n  #\n  # ```\n  # ENV.each do |key, value|\n  #   puts \"#{key} => #{value}\"\n  # end\n  # ```\n  def self.each(& : {String, String} ->)\n    Crystal::System::Env.each do |key, value|\n      yield({key, value})\n    end\n  end\n\n  # WARNING: It is recommended to never delete environment variables. See the\n  # Safety section of `ENV` for details.\n  def self.clear : Nil\n    keys.each { |k| delete k }\n  end\n\n  # Writes the contents of the environment to *io*.\n  def self.inspect(io)\n    io << '{'\n    found_one = false\n    each do |key, value|\n      io << \", \" if found_one\n      key.inspect(io)\n      io << \" => \"\n      value.inspect(io)\n      found_one = true\n    end\n    io << '}'\n  end\n\n  def self.pretty_print(pp)\n    pp.list(\"{\", keys.sort!, \"}\") do |key|\n      pp.group do\n        key.pretty_print(pp)\n        pp.text \" =>\"\n        pp.nest do\n          pp.breakable\n          self[key].pretty_print(pp)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/errno.cr",
    "content": "require \"c/errno\"\nrequire \"c/string\"\n\n# Errno wraps and gives access to libc's errno. This is mostly useful when\n# dealing with C libraries.\nenum Errno\n  NONE = 0\n\n  {% for value in %w(E2BIG EPERM ENOENT ESRCH EINTR EIO ENXIO ENOEXEC EBADF ECHILD EDEADLK ENOMEM\n                    EACCES EFAULT ENOTBLK EBUSY EEXIST EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE\n                    EMFILE ENOTTY ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM ERANGE EAGAIN\n                    EWOULDBLOCK EINPROGRESS EALREADY ENOTSOCK EDESTADDRREQ EMSGSIZE EPROTOTYPE ENOPROTOOPT\n                    EPROTONOSUPPORT ESOCKTNOSUPPORT EPFNOSUPPORT EAFNOSUPPORT EADDRINUSE EADDRNOTAVAIL\n                    ENETDOWN ENETUNREACH ENETRESET ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN\n                    ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED ELOOP ENAMETOOLONG EHOSTDOWN\n                    EHOSTUNREACH ENOTEMPTY EUSERS EDQUOT ESTALE EREMOTE ENOLCK ENOSYS EOVERFLOW\n                    ECANCELED EIDRM ENOMSG EILSEQ EBADMSG EMULTIHOP ENODATA ENOLINK ENOSR ENOSTR\n                    EPROTO ETIME EOPNOTSUPP ENOTRECOVERABLE EOWNERDEAD) %}\n    {% if LibC.has_constant?(value) %}\n      {{value.id}} = LibC::{{value.id}}\n    {% end %}\n  {% end %}\n\n  # Returns the system error message associated with this errno.\n  #\n  # NOTE: The result may depend on the current system locale. Specs and\n  # comparisons should use `#value` instead of this method.\n  def message : String\n    unsafe_message { |slice| String.new(slice) }\n  end\n\n  # :nodoc:\n  def unsafe_message(&)\n    {% if LibC.has_method?(:strerror_r) %}\n      buffer = uninitialized UInt8[256]\n      if LibC.strerror_r(value, buffer, buffer.size) == 0\n        yield Bytes.new(buffer.to_unsafe, LibC.strlen(buffer))\n      else\n        yield \"(???)\".to_slice\n      end\n    {% else %}\n      pointer = LibC.strerror(value)\n      yield Bytes.new(pointer, LibC.strlen(pointer))\n    {% end %}\n  end\n\n  # returns the value of libc's errno.\n  def self.value : self\n    {% if LibC.has_method?(:__errno_location) %}\n      Errno.new LibC.__errno_location.value\n    {% elsif LibC.has_method?(:__errno) %}\n      Errno.new LibC.__errno.value\n    {% elsif LibC.has_method?(:__error) %}\n      Errno.new LibC.__error.value\n    {% elsif LibC.has_method?(:___errno) %}\n      Errno.new LibC.___errno.value\n    {% elsif flag?(:wasi) %}\n      Errno.new LibC.errno\n    {% elsif flag?(:win32) %}\n      ret = LibC._get_errno(out errno)\n      raise RuntimeError.from_os_error(\"_get_errno\", Errno.new(ret)) unless ret == 0\n      Errno.new errno\n    {% else %}\n      {% raise \"ERROR: no errno definition for target\" %}\n    {% end %}\n  end\n\n  # Sets the value of libc's errno.\n  def self.value=(errno : Errno)\n    {% if LibC.has_method?(:__errno_location) %}\n      LibC.__errno_location.value = errno.value\n    {% elsif LibC.has_method?(:__errno) %}\n      LibC.__errno.value = errno.value\n    {% elsif LibC.has_method?(:__error) %}\n      LibC.__error.value = errno.value\n    {% elsif LibC.has_method?(:___errno) %}\n      LibC.___errno.value = errno.value\n    {% elsif flag?(:wasi) %}\n      LibC.errno = errno.value\n    {% elsif flag?(:win32) %}\n      ret = LibC._set_errno(errno.value)\n      raise RuntimeError.from_os_error(\"_set_errno\", Errno.new(ret)) unless ret == 0\n    {% else %}\n      {% raise \"ERROR: no errno definition for target\" %}\n    {% end %}\n    errno\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack/dwarf.cr",
    "content": "require \"crystal/dwarf\"\n{% if flag?(:darwin) %}\n  require \"./mach_o\"\n{% else %}\n  require \"./elf\"\n{% end %}\n\nstruct Exception::CallStack\n  @@dwarf_line_numbers : Crystal::DWARF::LineNumbers?\n  @@dwarf_function_names : Array(Tuple(LibC::SizeT, LibC::SizeT, String))?\n\n  {% if flag?(:win32) %}\n    @@coff_symbols : Hash(Int32, Array(Crystal::PE::COFFSymbol))?\n  {% end %}\n\n  # :nodoc:\n\n  protected def self.decode_line_number(pc)\n    if ln = @@dwarf_line_numbers\n      if row = ln.find(pc)\n        return {row.path, row.line, row.column}\n      end\n    end\n    {\"??\", 0, 0}\n  end\n\n  protected def self.decode_function_name(pc)\n    if fn = @@dwarf_function_names\n      fn.each do |(low_pc, high_pc, function_name)|\n        return function_name if low_pc <= pc <= high_pc\n      end\n    end\n  end\n\n  protected def self.parse_function_names_from_dwarf(info, strings, line_strings, &)\n    info.each do |code, abbrev, attributes|\n      next unless abbrev && abbrev.tag.subprogram?\n      name = low_pc = high_pc = nil\n\n      attributes.each do |(at, form, value)|\n        case at\n        when Crystal::DWARF::AT::DW_AT_name\n          value = strings.try(&.decode(value.as(UInt32 | UInt64))) if form.strp?\n          value = line_strings.try(&.decode(value.as(UInt32 | UInt64))) if form.line_strp?\n          name = value.as(String)\n        when Crystal::DWARF::AT::DW_AT_low_pc\n          low_pc = value.as(LibC::SizeT)\n        when Crystal::DWARF::AT::DW_AT_high_pc\n          if form.addr?\n            high_pc = value.as(LibC::SizeT)\n          elsif value.responds_to?(:to_i)\n            high_pc = low_pc.as(LibC::SizeT) + value.to_i\n          end\n        else\n          # Not an attribute we care\n        end\n      end\n\n      if low_pc && high_pc && name\n        yield low_pc, high_pc, name\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack/elf.cr",
    "content": "{% if flag?(:win32) %}\n  require \"crystal/pe\"\n{% else %}\n  require \"crystal/elf\"\n  {% unless flag?(:wasm32) %}\n    require \"c/link\"\n  {% end %}\n{% end %}\n\nstruct Exception::CallStack\n  {% unless flag?(:win32) %}\n    private struct DlPhdrData\n      getter program : String\n      property base_address : LibC::Elf_Addr = 0\n\n      def initialize(@program : String)\n      end\n    end\n  {% end %}\n\n  protected def self.load_debug_info_impl : Nil\n    program = Process.executable_path\n    return unless program && File::Info.readable? program\n\n    {% if flag?(:win32) %}\n      if LibC.GetModuleHandleExW(LibC::GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nil, out hmodule) != 0\n        self.read_dwarf_sections(program, hmodule.address)\n      end\n    {% else %}\n      data = DlPhdrData.new(program)\n\n      phdr_callback = LibC::DlPhdrCallback.new do |info, size, data|\n        # `dl_iterate_phdr` does not always visit the current program first; on\n        # Android the first object is `/system/bin/linker64`, the second is the\n        # full program path (not the empty string), so we check both here\n        name_c_str = info.value.name\n        if name_c_str && (name_c_str.value == 0 || LibC.strcmp(name_c_str, data.as(DlPhdrData*).value.program) == 0)\n          # The first entry is the header for the current program.\n          # Note that we avoid allocating here and just store the base address\n          # to be passed to self.read_dwarf_sections when dl_iterate_phdr returns.\n          # Calling self.read_dwarf_sections from this callback may lead to reallocations\n          # and deadlocks due to the internal lock held by dl_iterate_phdr (#10084).\n          data.as(DlPhdrData*).value.base_address = info.value.addr\n          1\n        else\n          0\n        end\n      end\n\n      LibC.dl_iterate_phdr(phdr_callback, pointerof(data))\n      self.read_dwarf_sections(data.program, data.base_address)\n    {% end %}\n  rescue ex\n    @@dwarf_line_numbers = nil\n    @@dwarf_function_names = nil\n  end\n\n  protected def self.read_dwarf_sections(program, base_address = 0)\n    {{ flag?(:win32) ? Crystal::PE : Crystal::ELF }}.open(program) do |image|\n      {% if flag?(:win32) %}\n        base_address -= image.original_image_base\n        @@coff_symbols = image.coff_symbols\n      {% end %}\n\n      line_strings = image.read_section?(\".debug_line_str\") do |sh, io|\n        Crystal::DWARF::Strings.new(io, sh.offset, sh.size)\n      end\n\n      strings = image.read_section?(\".debug_str\") do |sh, io|\n        Crystal::DWARF::Strings.new(io, sh.offset, sh.size)\n      end\n\n      image.read_section?(\".debug_line\") do |sh, io|\n        @@dwarf_line_numbers = Crystal::DWARF::LineNumbers.new(io, sh.size, base_address, strings, line_strings)\n      end\n\n      abbrevs_tables = image.read_section?(\".debug_abbrev\") do |sh, io|\n        all = {} of Int64 => Array(Crystal::DWARF::Abbrev)\n        while (offset = io.pos - sh.offset) < sh.size\n          all[offset] = Crystal::DWARF::Abbrev.read(io)\n        end\n        all\n      end\n\n      image.read_section?(\".debug_info\") do |sh, io|\n        names = [] of {LibC::SizeT, LibC::SizeT, String}\n\n        while (offset = io.pos - sh.offset) < sh.size\n          info = Crystal::DWARF::Info.new(io, offset)\n\n          if abbrevs_tables\n            if abbreviations = abbrevs_tables[info.debug_abbrev_offset]?\n              info.abbreviations = abbreviations\n            end\n          end\n\n          parse_function_names_from_dwarf(info, strings, line_strings) do |low_pc, high_pc, name|\n            names << {low_pc + base_address, high_pc + base_address, name}\n          end\n        end\n\n        @@dwarf_function_names = names\n      end\n    end\n  end\n\n  protected def self.decode_address(ip)\n    ip.address\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack/interpreter.cr",
    "content": "require \"../../crystal/system/print_error\"\n\n# :nodoc:\nstruct Exception::CallStack\n  skip(__FILE__)\n\n  def self.load_debug_info_impl : Nil\n  end\n\n  @[Primitive(:interpreter_call_stack_unwind)]\n  protected def self.unwind : Array(Void*)\n  end\n\n  def self.decode_address(ip)\n    ip.unsafe_as(String).split(\"|\", 4)\n  end\n\n  def self.decode_line_number(pc)\n    _, line, column, file = pc\n    {file, line, column}\n  end\n\n  def self.decode_function_name(pc)\n    pc[0]\n  end\n\n  def self.decode_frame(pc)\n    pc[0]\n  end\n\n  def self.print_backtrace : Nil\n    unwind.each do |frame|\n      Crystal::System.print_error \"%s\\n\", frame.unsafe_as(String)\n    end\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack/libunwind.cr",
    "content": "{% unless flag?(:win32) %}\n  require \"c/dlfcn\"\n{% end %}\nrequire \"c/stdio\"\nrequire \"c/string\"\nrequire \"../lib_unwind\"\n\n{% if flag?(:darwin) || flag?(:bsd) || flag?(:linux) || flag?(:solaris) || flag?(:win32) %}\n  require \"./dwarf\"\n{% else %}\n  require \"./null\"\n{% end %}\n\nstruct Exception::CallStack\n  skip(__FILE__)\n\n  {% if flag?(:gnu) && flag?(:i386) %}\n    # This is only used for the workaround described in `Exception.unwind`\n    @@makecontext_range : Range(Void*, Void*)?\n\n    def self.makecontext_range\n      @@makecontext_range ||= begin\n        makecontext_start = makecontext_end = LibC.dlsym(LibC::RTLD_DEFAULT, \"makecontext\")\n\n        while true\n          ret = LibC.dladdr(makecontext_end, out info)\n          break if ret == 0 || info.dli_sname.null?\n          break unless LibC.strcmp(info.dli_sname, \"makecontext\") == 0\n          makecontext_end += 1\n        end\n\n        (makecontext_start...makecontext_end)\n      end\n    end\n  {% end %}\n\n  def self.setup_crash_handler\n    {% if flag?(:win32) %}\n      Crystal::System::Signal.setup_seh_handler\n    {% else %}\n      Crystal::System::Signal.setup_segfault_handler\n    {% end %}\n  end\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_call_stack_unwind)] {% end %}\n  protected def self.unwind : Array(Void*)\n    callstack = Array(Void*).new(32)\n    backtrace_fn = ->(context : LibUnwind::Context, data : Void*) do\n      bt = data.as(typeof(callstack))\n\n      ip = {% if flag?(:arm) %}\n             Pointer(Void).new(__crystal_unwind_get_ip(context))\n           {% else %}\n             Pointer(Void).new(LibUnwind.get_ip(context))\n           {% end %}\n      bt << ip\n\n      {% if flag?(:gnu) && flag?(:i386) %}\n        # This is a workaround for glibc bug: https://sourceware.org/bugzilla/show_bug.cgi?id=18635\n        # The unwind info is corrupted when `makecontext` is used.\n        # Stop the backtrace here. There is nothing interest beyond this point anyway.\n        if CallStack.makecontext_range.includes?(ip)\n          return LibUnwind::ReasonCode::END_OF_STACK\n        end\n      {% end %}\n\n      LibUnwind::ReasonCode::NO_REASON\n    end\n\n    LibUnwind.backtrace(backtrace_fn, callstack.as(Void*))\n    callstack\n  end\n\n  struct RepeatedFrame\n    getter ip : Void*, count : Int32\n\n    def initialize(@ip : Void*)\n      @count = 0\n    end\n\n    def incr\n      @count += 1\n    end\n  end\n\n  def self.print_backtrace : Nil\n    backtrace_fn = ->(context : LibUnwind::Context, data : Void*) do\n      last_frame = data.as(RepeatedFrame*)\n\n      ip = {% if flag?(:arm) %}\n             Pointer(Void).new(__crystal_unwind_get_ip(context))\n           {% else %}\n             Pointer(Void).new(LibUnwind.get_ip(context))\n           {% end %}\n\n      if last_frame.value.ip == ip\n        last_frame.value.incr\n      else\n        print_frame(last_frame.value) unless last_frame.value.ip.address == 0\n        last_frame.value = RepeatedFrame.new ip\n      end\n      LibUnwind::ReasonCode::NO_REASON\n    end\n\n    rf = RepeatedFrame.new(Pointer(Void).null)\n    LibUnwind.backtrace(backtrace_fn, pointerof(rf).as(Void*))\n    print_frame(rf)\n  end\n\n  private def self.print_frame(repeated_frame)\n    Crystal::System.print_error \"[%p] \", repeated_frame.ip\n    print_frame_location(repeated_frame)\n    Crystal::System.print_error \" (%d times)\", repeated_frame.count + 1 unless repeated_frame.count == 0\n    Crystal::System.print_error \"\\n\"\n  end\n\n  private def self.print_frame_location(repeated_frame)\n    {% if flag?(:debug) %}\n      if @@loaded\n        pc = CallStack.decode_address(repeated_frame.ip)\n        if name = decode_function_name(pc)\n          file, line, column = Exception::CallStack.decode_line_number(pc)\n          if file && file != \"??\"\n            Crystal::System.print_error \"%s at %s:%d:%d\", name, file, line, column\n            return\n          end\n        end\n      end\n    {% end %}\n\n    unsafe_decode_frame(repeated_frame.ip) do |offset, sname, fname|\n      Crystal::System.print_error \"%s +%lld in %s\", sname, offset.to_i64, fname\n      return\n    end\n\n    Crystal::System.print_error \"???\"\n  end\n\n  protected def self.decode_frame(ip)\n    decode_frame(ip) do |offset, symbol, file|\n      symbol = symbol ? String.new(symbol) : \"??\"\n      file = file ? String.new(file) : \"??\"\n      {offset, symbol, file}\n    end\n  end\n\n  # variant of `.decode_frame` that returns the C strings directly instead of\n  # wrapping them in `String.new`, since the SIGSEGV handler cannot allocate\n  # memory via the GC\n  protected def self.unsafe_decode_frame(ip, &)\n    decode_frame(ip) do |offset, symbol, file|\n      symbol ||= \"??\".to_unsafe\n      file ||= \"??\".to_unsafe\n      yield offset, symbol, file\n    end\n  end\n\n  private def self.decode_frame(ip, &)\n    original_ip = ip\n    while true\n      retry = dladdr(ip) do |file, symbol, address|\n        offset = original_ip - address\n        if offset == 0\n          ip -= 1\n          true\n        elsif symbol.null? && file.null?\n          false\n        else\n          return yield offset, symbol, file\n        end\n      end\n      break unless retry\n    end\n  end\n\n  {% if flag?(:win32) %}\n    def self.dladdr(ip, &)\n      if LibC.GetModuleHandleExW(LibC::GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | LibC::GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, ip.as(LibC::LPWSTR), out hmodule) != 0\n        symbol, address = internal_symbol(hmodule, ip) || external_symbol(hmodule, ip) || return\n\n        utf16_file = uninitialized LibC::WCHAR[LibC::MAX_PATH]\n        len = LibC.GetModuleFileNameW(hmodule, utf16_file, utf16_file.size)\n        if 0 < len < utf16_file.size\n          utf8_file = uninitialized UInt8[sizeof(UInt8[LibC::MAX_PATH][3])]\n          file = utf8_file.to_unsafe\n          appender = file.appender\n          String.each_utf16_char(utf16_file.to_slice[0, len + 1]) do |ch|\n            ch.each_byte { |b| appender << b }\n          end\n        else\n          file = Pointer(UInt8).null\n        end\n\n        yield file, symbol, address\n      end\n    end\n\n    private def self.internal_symbol(hmodule, ip)\n      if coff_symbols = @@coff_symbols\n        if LibC.GetModuleHandleExW(LibC::GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nil, out this_hmodule) != 0 && this_hmodule == hmodule\n          section_base, section_index = lookup_section(hmodule, ip) || return\n          offset = ip - section_base\n          section_coff_symbols = coff_symbols[section_index]? || return\n          next_sym = section_coff_symbols.bsearch_index { |sym| offset < sym.offset } || return\n          sym = section_coff_symbols[next_sym - 1]? || return\n\n          {sym.name.to_unsafe, section_base + sym.offset}\n        end\n      end\n    end\n\n    private def self.external_symbol(hmodule, ip)\n      if dir = data_directory(hmodule, LibC::IMAGE_DIRECTORY_ENTRY_EXPORT)\n        exports = dir.to_unsafe.as(LibC::IMAGE_EXPORT_DIRECTORY*).value\n\n        found_address = Pointer(Void).null\n        found_index = -1\n\n        func_address_offsets = (hmodule + exports.addressOfFunctions).as(LibC::DWORD*).to_slice(exports.numberOfFunctions)\n        func_address_offsets.each_with_index do |offset, i|\n          address = hmodule + offset\n          if found_address < address <= ip\n            found_address, found_index = address, i\n          end\n        end\n\n        return unless found_address\n\n        func_name_ordinals = (hmodule + exports.addressOfNameOrdinals).as(LibC::WORD*).to_slice(exports.numberOfNames)\n        if ordinal_index = func_name_ordinals.index(&.== found_index)\n          symbol = (hmodule + (hmodule + exports.addressOfNames).as(LibC::DWORD*)[ordinal_index]).as(UInt8*)\n          {symbol, found_address}\n        end\n      end\n    end\n\n    private def self.lookup_section(hmodule, ip)\n      dos_header = hmodule.as(LibC::IMAGE_DOS_HEADER*)\n      return unless dos_header.value.e_magic == 0x5A4D # MZ\n\n      nt_header = (hmodule + dos_header.value.e_lfanew).as(LibC::IMAGE_NT_HEADERS*)\n      return unless nt_header.value.signature == 0x00004550 # PE\\0\\0\n\n      section_headers = (nt_header + 1).as(LibC::IMAGE_SECTION_HEADER*).to_slice(nt_header.value.fileHeader.numberOfSections)\n      section_headers.each_with_index do |header, i|\n        base = hmodule + header.virtualAddress\n        if base <= ip < base + header.virtualSize\n          return base, i\n        end\n      end\n    end\n\n    private def self.data_directory(hmodule, index)\n      dos_header = hmodule.as(LibC::IMAGE_DOS_HEADER*)\n      return unless dos_header.value.e_magic == 0x5A4D # MZ\n\n      nt_header = (hmodule + dos_header.value.e_lfanew).as(LibC::IMAGE_NT_HEADERS*)\n      return unless nt_header.value.signature == 0x00004550 # PE\\0\\0\n      return unless nt_header.value.optionalHeader.magic == {{ flag?(:bits64) ? 0x20b : 0x10b }}\n      return unless index.in?(0...{16, nt_header.value.optionalHeader.numberOfRvaAndSizes}.min)\n\n      directory = nt_header.value.optionalHeader.dataDirectory.to_unsafe[index]\n      if directory.virtualAddress != 0\n        Bytes.new(hmodule.as(UInt8*) + directory.virtualAddress, directory.size, read_only: true)\n      end\n    end\n  {% else %}\n    private def self.dladdr(ip, &)\n      if LibC.dladdr(ip, out info) != 0\n        yield info.dli_fname, info.dli_sname, info.dli_saddr\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/exception/call_stack/mach_o.cr",
    "content": "require \"crystal/mach_o\"\n\nlib LibC\n  fun _dyld_image_count : UInt32\n  fun _dyld_get_image_name(image_index : UInt32) : Char*\n  fun _dyld_get_image_vmaddr_slide(image_index : UInt32) : Long\nend\n\nstruct Exception::CallStack\n  @@image_slide : LibC::Long?\n\n  protected def self.load_debug_info_impl : Nil\n    read_dwarf_sections\n  rescue ex\n    @@dwarf_line_numbers = nil\n    @@dwarf_function_names = nil\n  end\n\n  protected def self.read_dwarf_sections : Nil\n    locate_dsym_bundle do |mach_o|\n      line_strings = mach_o.read_section?(\"__debug_line_str\") do |sh, io|\n        Crystal::DWARF::Strings.new(io, sh.offset, sh.size)\n      end\n\n      strings = mach_o.read_section?(\"__debug_str\") do |sh, io|\n        Crystal::DWARF::Strings.new(io, sh.offset, sh.size)\n      end\n\n      mach_o.read_section?(\"__debug_line\") do |sh, io|\n        @@dwarf_line_numbers = Crystal::DWARF::LineNumbers.new(io, sh.size, strings: strings, line_strings: line_strings)\n      end\n\n      abbrevs_tables = mach_o.read_section?(\"__debug_abbrev\") do |sh, io|\n        all = {} of Int64 => Array(Crystal::DWARF::Abbrev)\n        while (offset = io.pos - sh.offset) < sh.size\n          all[offset] = Crystal::DWARF::Abbrev.read(io)\n        end\n        all\n      end\n\n      mach_o.read_section?(\"__debug_info\") do |sh, io|\n        names = [] of {LibC::SizeT, LibC::SizeT, String}\n\n        while (offset = io.pos - sh.offset) < sh.size\n          info = Crystal::DWARF::Info.new(io, offset)\n\n          if abbrevs_tables\n            if abbreviations = abbrevs_tables[info.debug_abbrev_offset]?\n              info.abbreviations = abbreviations\n            end\n          end\n\n          parse_function_names_from_dwarf(info, strings, line_strings) do |low_pc, high_pc, name|\n            names << {low_pc, high_pc, name}\n          end\n        end\n\n        @@dwarf_function_names = names\n      end\n    end\n  end\n\n  # DWARF uses fixed addresses but Darwin loads executables at a random\n  # address, so we must remove the load offset from the IP to match the\n  # addresses in DWARF sections.\n  #\n  # See https://en.wikipedia.org/wiki/Address_space_layout_randomization\n  protected def self.decode_address(ip)\n    ip.address - image_slide\n  end\n\n  # Searches the companion dSYM bundle with the DWARF sections for the\n  # current program as generated by `dsymutil`. It may be a `foo.dwarf` file\n  # or within a `foo.dSYM` bundle for a program named `foo`.\n  #\n  # See <http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme> for details.\n  private def self.locate_dsym_bundle(&)\n    program = Process.executable_path\n    return unless program\n\n    files = {\n      \"#{program}.dSYM/Contents/Resources/DWARF/#{File.basename(program)}\",\n      \"#{program}.dwarf\",\n    }\n\n    files.each do |dwarf|\n      next unless File.exists?(dwarf)\n\n      Crystal::MachO.open(program) do |mach_o|\n        Crystal::MachO.open(dwarf) do |dsym|\n          if dsym.uuid == mach_o.uuid\n            return yield dsym\n          end\n        end\n      end\n    end\n\n    nil\n  end\n\n  # The address offset at which the program was loaded at.\n  private def self.image_slide\n    @@image_slide ||= search_image_slide\n  end\n\n  private def self.search_image_slide\n    buffer = GC.malloc_atomic(LibC::PATH_MAX).as(UInt8*)\n    size = LibC::PATH_MAX.to_u32\n\n    if LibC._NSGetExecutablePath(buffer, pointerof(size)) == -1\n      buffer = GC.malloc_atomic(size).as(UInt8*)\n      if LibC._NSGetExecutablePath(buffer, pointerof(size)) == -1\n        return LibC::Long.new(0)\n      end\n    end\n\n    program = File.realpath(String.new(buffer))\n\n    LibC._dyld_image_count.times do |i|\n      if program == File.realpath(String.new(LibC._dyld_get_image_name(i)))\n        return LibC._dyld_get_image_vmaddr_slide(i)\n      end\n    end\n\n    LibC::Long.new(0)\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack/null.cr",
    "content": "struct Exception::CallStack\n  def self.load_debug_info_impl : Nil\n  end\n\n  def self.decode_address(ip)\n    ip\n  end\n\n  def self.decode_line_number(pc)\n    {\"??\", 0, 0}\n  end\n\n  def self.decode_function_name(pc)\n    nil\n  end\n\n  protected def self.decode_frame(pc)\n    nil\n  end\n\n  protected def self.unwind : Array(Void*)\n    [] of Void*\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack/stackwalk.cr",
    "content": "require \"c/dbghelp\"\n\n# :nodoc:\nstruct Exception::CallStack\n  skip(__FILE__)\n\n  @@mutex = Thread::Mutex.new\n\n  private def decode_backtrace\n    # must grab the mutex because DbgHelp isn't thread safe\n    @@mutex.synchronize { previous_def }\n  end\n\n  private def self.load_debug_info_impl : Nil\n    # TODO: figure out if and when to call SymCleanup (it cannot be done in\n    # `at_exit` because unhandled exceptions in `main_user_code` are printed\n    # after those handlers)\n    executable_path = Process.executable_path\n    executable_path_ptr = executable_path ? Crystal::System.to_wstr(File.dirname(executable_path)) : Pointer(LibC::WCHAR).null\n    if LibC.SymInitializeW(LibC.GetCurrentProcess, executable_path_ptr, 1) == 0\n      raise RuntimeError.from_winerror(\"SymInitializeW\")\n    end\n    LibC.SymSetOptions(LibC.SymGetOptions | LibC::SYMOPT_UNDNAME | LibC::SYMOPT_LOAD_LINES | LibC::SYMOPT_FAIL_CRITICAL_ERRORS | LibC::SYMOPT_NO_PROMPTS)\n  end\n\n  def self.setup_crash_handler\n    Crystal::System::Signal.setup_seh_handler\n  end\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_call_stack_unwind)] {% end %}\n  protected def self.unwind : Array(Void*)\n    # unlike DWARF, this is required on Windows to even be able to produce\n    # correct stack traces, so we do it here but not in `libunwind.cr`\n    load_debug_info\n\n    # TODO: use stack if possible (must be 16-byte aligned)\n    context = Pointer(LibC::CONTEXT).malloc(1)\n    context.value.contextFlags = LibC::CONTEXT_FULL\n    LibC.RtlCaptureContext(context)\n\n    stack = [] of Void*\n\n    # DbgHelp is thread unsafe so we'd theoretically need to grab the mutex, but\n    # unwinding alone seems fine, only decoding the backtrace seems unsafe\n    each_frame(context) do |frame|\n      (frame.count + 1).times do\n        stack << frame.ip\n      end\n    end\n\n    stack\n  end\n\n  private def self.each_frame(context, &)\n    machine_type = {% if flag?(:x86_64) %}\n                     LibC::IMAGE_FILE_MACHINE_AMD64\n                   {% elsif flag?(:i386) %}\n                     # TODO: use WOW64_CONTEXT in place of CONTEXT\n                     {% raise \"x86 not supported\" %}\n                   {% elsif flag?(:aarch64) %}\n                     LibC::IMAGE_FILE_MACHINE_ARM64\n                   {% else %}\n                     {% raise \"Architecture not supported\" %}\n                   {% end %}\n\n    stack_frame = LibC::STACKFRAME64.new\n    stack_frame.addrPC.mode = LibC::ADDRESS_MODE::AddrModeFlat\n    stack_frame.addrFrame.mode = LibC::ADDRESS_MODE::AddrModeFlat\n    stack_frame.addrStack.mode = LibC::ADDRESS_MODE::AddrModeFlat\n\n    {% if flag?(:x86_64) %}\n      stack_frame.addrPC.offset = context.value.rip\n      stack_frame.addrFrame.offset = context.value.rbp\n      stack_frame.addrStack.offset = context.value.rsp\n    {% elsif flag?(:aarch64) %}\n      stack_frame.addrPC.offset = context.value.pc\n      stack_frame.addrFrame.offset = context.value.x[29]\n      stack_frame.addrStack.offset = context.value.sp\n    {% end %}\n\n    last_frame = nil\n    cur_proc = LibC.GetCurrentProcess\n    cur_thread = LibC.GetCurrentThread\n\n    while true\n      ret = LibC.StackWalk64(\n        machine_type,\n        cur_proc,\n        cur_thread,\n        pointerof(stack_frame),\n        context,\n        nil,\n        nil, # ->LibC.SymFunctionTableAccess64,\n        nil, # ->LibC.SymGetModuleBase64,\n        nil\n      )\n      break if ret == 0\n\n      ip = Pointer(Void).new(stack_frame.addrPC.offset)\n      if last_frame\n        if ip != last_frame.ip\n          yield last_frame\n          last_frame = RepeatedFrame.new(ip)\n        else\n          last_frame.incr\n        end\n      else\n        last_frame = RepeatedFrame.new(ip)\n      end\n    end\n\n    yield last_frame if last_frame\n  end\n\n  struct RepeatedFrame\n    getter ip : Void*, count : Int32\n\n    def initialize(@ip : Void*)\n      @count = 0\n    end\n\n    def incr\n      @count += 1\n    end\n  end\n\n  private record StackContext, context : LibC::CONTEXT*, thread : LibC::HANDLE\n\n  def self.print_backtrace(exception_info) : Nil\n    load_debug_info\n\n    # must grab the mutex because we decode the backtrace (thread unsafe) as we\n    # unwind the stack (apparently thread safe)\n    @@mutex.synchronize do\n      each_frame(exception_info.value.contextRecord) do |frame|\n        print_frame(frame)\n      end\n    end\n  end\n\n  private def self.print_frame(repeated_frame)\n    Crystal::System.print_error \"[%p] \", repeated_frame.ip\n    print_frame_location(repeated_frame)\n    Crystal::System.print_error \" (%d times)\", repeated_frame.count + 1 unless repeated_frame.count == 0\n    Crystal::System.print_error \"\\n\"\n  end\n\n  private def self.print_frame_location(repeated_frame)\n    if name = decode_function_name(repeated_frame.ip.address)\n      file, line, _ = decode_line_number(repeated_frame.ip.address)\n      if file != \"??\" && line != 0\n        Crystal::System.print_error \"%s at %s:%d\", name, file, line\n        return\n      end\n    end\n\n    if frame = decode_frame(repeated_frame.ip)\n      offset, sname, fname = frame\n      Crystal::System.print_error \"%s +%lld in %s\", sname, offset.to_i64, fname\n    else\n      Crystal::System.print_error \"???\"\n    end\n  end\n\n  # WARNING: caller must own the @@mutex lock!\n  protected def self.decode_line_number(pc)\n    line_info = uninitialized LibC::IMAGEHLP_LINEW64\n    line_info.sizeOfStruct = sizeof(LibC::IMAGEHLP_LINEW64)\n\n    if LibC.SymGetLineFromAddrW64(LibC.GetCurrentProcess, pc, out displacement, pointerof(line_info)) != 0\n      file_name = String.from_utf16(line_info.fileName)[0]\n      line_number = line_info.lineNumber.to_i32\n    else\n      line_number = 0\n    end\n\n    unless file_name\n      if m_info = sym_get_module_info(pc)\n        offset, image_name = m_info\n        file_name = \"#{image_name} +#{offset}\"\n      else\n        file_name = \"??\"\n      end\n    end\n\n    {file_name, line_number, 0}\n  end\n\n  # WARNING: caller must own the @@mutex lock!\n  protected def self.decode_function_name(pc)\n    if sym = sym_from_addr(pc)\n      _, sname = sym\n      sname\n    end\n  end\n\n  # WARNING: caller must own the @@mutex lock!\n  protected def self.decode_frame(ip)\n    pc = decode_address(ip)\n    if sym = sym_from_addr(pc)\n      if m_info = sym_get_module_info(pc)\n        offset, sname = sym\n        _, fname = m_info\n        {offset, sname, fname}\n      end\n    end\n  end\n\n  private def self.sym_get_module_info(pc)\n    module_info = Pointer(LibC::IMAGEHLP_MODULEW64).malloc(1)\n    module_info.value.sizeOfStruct = sizeof(LibC::IMAGEHLP_MODULEW64)\n\n    if LibC.SymGetModuleInfoW64(LibC.GetCurrentProcess, pc, module_info) != 0\n      mod_displacement = pc - LibC.SymGetModuleBase64(LibC.GetCurrentProcess, pc)\n      image_name = String.from_utf16(module_info.value.loadedImageName.to_unsafe)[0]\n      {mod_displacement, image_name}\n    end\n  end\n\n  private def self.sym_from_addr(pc)\n    symbol_size = sizeof(LibC::SYMBOL_INFOW) + (LibC::MAX_SYM_NAME - 1) * sizeof(LibC::WCHAR)\n    symbol = Pointer(UInt8).malloc(symbol_size).as(LibC::SYMBOL_INFOW*)\n    symbol.value.sizeOfStruct = sizeof(LibC::SYMBOL_INFOW)\n    symbol.value.maxNameLen = LibC::MAX_SYM_NAME\n\n    sym_displacement = LibC::DWORD64.zero\n    if LibC.SymFromAddrW(LibC.GetCurrentProcess, pc, pointerof(sym_displacement), symbol) != 0\n      symbol_str = String.from_utf16(symbol.value.name.to_unsafe.to_slice(symbol.value.nameLen))\n      {sym_displacement, symbol_str}\n    end\n  end\n\n  protected def self.decode_address(ip)\n    ip.address\n  end\nend\n"
  },
  {
    "path": "src/exception/call_stack.cr",
    "content": "# Returns the current execution stack as an array containing strings\n# usually in the form file:line:column or file:line:column in 'method'.\ndef caller : Array(String)\n  Exception::CallStack.new.printable_backtrace\nend\n\n# :nodoc:\nstruct Exception::CallStack\n  # Compute current directory at the beginning so filenames\n  # are always shown relative to the *starting* working directory.\n  private CURRENT_DIR = Process::INITIAL_PWD.try { |dir| Path[dir] }\n\n  @@skip = [] of String\n\n  def self.skip(filename) : Nil\n    @@skip << filename\n  end\n\n  skip(__FILE__)\n\n  @@loaded = false\n\n  # :nodoc:\n  def self.load_debug_info : Nil\n    Crystal.once(pointerof(@@loaded)) do\n      return if ENV[\"CRYSTAL_LOAD_DEBUG_INFO\"]? == \"0\"\n\n      begin\n        load_debug_info_impl\n      rescue ex\n        Crystal::System.print_exception \"Unable to load debug information\", ex\n      end\n    end\n  end\n\n  @callstack : Array(Void*)\n  @backtrace : Array(String)?\n\n  def initialize(@callstack : Array(Void*) = CallStack.unwind)\n  end\n\n  class_getter empty = new([] of Void*)\n\n  def printable_backtrace : Array(String)\n    @backtrace ||= decode_backtrace\n  end\n\n  private def decode_backtrace\n    {% if flag?(:wasm32) %}\n      [] of String\n    {% else %}\n      CallStack.load_debug_info\n      show_full_info = ENV[\"CRYSTAL_CALLSTACK_FULL_INFO\"]? == \"1\"\n\n      @callstack.compact_map do |ip|\n        CallStack.decode_backtrace_frame(ip, show_full_info)\n      end\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.decode_backtrace_frame(ip, show_full_info) : String?\n    pc = decode_address(ip)\n\n    file, line_number, column_number = decode_line_number(pc)\n\n    if file && file != \"??\"\n      return if @@skip.includes?(file)\n\n      # Turn to relative to the current dir, if possible\n      if current_dir = CURRENT_DIR\n        if rel = Path[file].relative_to?(current_dir)\n          rel = rel.to_s\n          file = rel unless rel.starts_with?(\"..\")\n        end\n      end\n\n      file_line_column = file\n      unless line_number == 0\n        file_line_column = \"#{file_line_column}:#{line_number}\"\n        file_line_column = \"#{file_line_column}:#{column_number}\" unless column_number == 0\n      end\n    end\n\n    if name = decode_function_name(pc)\n      function = name\n    elsif frame = decode_frame(ip)\n      _, function, file = frame\n      # Crystal methods (their mangled name) start with `*`, so\n      # we remove that to have less clutter in the output.\n      function = function.lchop('*')\n    else\n      function = \"??\"\n    end\n\n    if file_line_column\n      if show_full_info && (frame = decode_frame(ip))\n        _, sname, _ = frame\n        line = \"#{file_line_column} in '#{sname}'\"\n      else\n        line = \"#{file_line_column} in '#{function}'\"\n      end\n    else\n      if file == \"??\" && function == \"??\"\n        line = \"???\"\n      else\n        line = \"#{file} in '#{function}'\"\n      end\n    end\n\n    if show_full_info\n      line = \"#{line} at 0x#{ip.address.to_s(16)}\"\n    end\n\n    line\n  end\nend\n\n{% if flag?(:interpreted) %}\n  require \"./call_stack/interpreter\"\n{% elsif flag?(:win32) && !flag?(:gnu) %}\n  require \"./call_stack/stackwalk\"\n{% elsif flag?(:wasm32) %}\n  require \"./call_stack/null\"\n{% else %}\n  require \"./call_stack/libunwind\"\n{% end %}\n"
  },
  {
    "path": "src/exception/lib_unwind.cr",
    "content": "{% if flag?(:openbsd) %}\n  @[Link(\"c++abi\")]\n{% end %}\nlib LibUnwind\n  @[Flags]\n  enum Action\n    SEARCH_PHASE  =  1\n    CLEANUP_PHASE =  2\n    HANDLER_FRAME =  4\n    FORCE_UNWIND  =  8\n    END_OF_STACK  = 16\n  end\n\n  enum ReasonCode\n    NO_REASON                = 0\n    FOREIGN_EXCEPTION_CAUGHT = 1\n    FATAL_PHASE2_ERROR       = 2\n    FATAL_PHASE1_ERROR       = 3\n    NORMAL_STOP              = 4\n    END_OF_STACK             = 5\n    HANDLER_FOUND            = 6\n    INSTALL_CONTEXT          = 7\n    CONTINUE_UNWIND          = 8\n    {% if flag?(:arm) %}\n      FAILURE = 9\n    {% end %}\n  end\n\n  alias Context = Void*\n\n  {% if flag?(:arm) %}\n    @[Flags]\n    enum State\n      VIRTUAL_UNWIND_FRAME  =  0\n      UNWIND_FRAME_STARTING =  1\n      UNWIND_FRAME_RESUME   =  2\n      ACTION_MASK           =  3\n      FORCE_UNWIND          =  8\n      END_OF_STACK          = 16\n    end\n\n    enum UVRSC\n      CORE  = 0\n      VFP   = 1\n      WMMXD = 3\n      WMMXC = 4\n    end\n\n    enum UVRSD\n      UINT32 = 0\n      VFPX   = 1\n      UINT64 = 3\n      FLOAT  = 4\n      DOUBLE = 5\n    end\n\n    enum UVRSR\n      OK              = 0\n      NOT_IMPLEMENTED = 1\n      FAILED          = 2\n    end\n\n    # Unwinder cache, private fields for the unwinder's use\n    struct ControlBlock_UnwinderCache\n      reserved1 : UInt32 # init reserved1 to 0, then don't touch\n      reserved2 : UInt32\n      reserved3 : UInt32\n      reserved4 : UInt32\n      reserved5 : UInt32\n    end\n\n    # Propagation barrier cache (valid after phase 1):\n    struct ControlBlock_BarrierCache\n      sp : UInt32\n      bitpattern : StaticArray(UInt32, 5)\n    end\n\n    # Cleanup cache (preserved over cleanup):\n    struct ControlBlock_CleanupCache\n      bitpattern : StaticArray(UInt32, 4)\n    end\n\n    # Personality routine cache (for personality routine's benefit):\n    struct ControlBlock_PrCache\n      fnstart : UInt32    # function start address\n      ehtp : UInt32*      # pointer to EHT entry header word\n      additional : UInt32 # additional data\n      reserved1 : UInt32\n    end\n\n    struct ControlBlock\n      exception_class : UInt64 # StaticArray(UInt8, 8)\n      exception_cleanup : UInt32\n      unwinder_cache : ControlBlock_UnwinderCache\n      barrier_cache : ControlBlock_BarrierCache\n      cleanup_cache : ControlBlock_CleanupCache\n      pr_cache : ControlBlock_PrCache\n      # __align : LibC::LongLong # Force alignment of next item to 8-byte boundary\n\n      exception_object : Void*\n      exception_type_id : Int32\n      __align : StaticArray(UInt8, 4)\n    end\n\n    alias Exception = ControlBlock\n\n    fun backtrace = _Unwind_Backtrace((Context, Void*) -> ReasonCode, Void*) : Int32\n    fun raise_exception = _Unwind_RaiseException(ucb : ControlBlock*) : ReasonCode\n    fun vrs_get = _Unwind_VRS_Get(context : Context, regclass : UVRSC, regno : UInt32, representation : UVRSD, valuep : Void*) : UVRSR\n    fun vrs_set = _Unwind_VRS_Set(context : Context, regclass : UVRSC, regno : UInt32, representation : UVRSD, valuep : Void*) : UVRSR\n    fun __gnu_unwind_frame(ucb : ControlBlock*, context : Context) : ReasonCode\n  {% else %}\n    struct Exception\n      exception_class : LibC::SizeT\n      exception_cleanup : LibC::SizeT\n      {% if flag?(:win32) && flag?(:gnu) %}\n        private_ : UInt64[6]\n      {% else %}\n        private1 : UInt64\n        private2 : UInt64\n      {% end %}\n      exception_object : Void*\n      exception_type_id : Int32\n    end\n\n    fun backtrace = _Unwind_Backtrace((Context, Void*) -> ReasonCode, Void*) : Int32\n    fun get_language_specific_data = _Unwind_GetLanguageSpecificData(Context) : UInt8*\n    fun get_region_start = _Unwind_GetRegionStart(Context) : LibC::SizeT\n    fun get_ip = _Unwind_GetIP(context : Context) : LibC::SizeT\n    fun set_ip = _Unwind_SetIP(context : Context, ip : LibC::SizeT) : LibC::SizeT\n    fun set_gr = _Unwind_SetGR(context : Context, index : Int32, value : LibC::SizeT)\n    fun raise_exception = _Unwind_RaiseException(ex : Exception*) : ReasonCode\n  {% end %}\n\n  {% if flag?(:x86_64) || flag?(:arm) || flag?(:aarch64) %}\n    EH_REGISTER_0 = 0\n    EH_REGISTER_1 = 1\n  {% else %}\n    EH_REGISTER_0 = 0\n    EH_REGISTER_1 = 2\n  {% end %}\nend\n\n{% if flag?(:arm) %}\n  # ARM EHABI uses Virtual Scratch Register to snapshot registers. The following\n  # methods emulate the `_Unwind_{Get|Set}IP` and `_Unwind_{Get|Set}GR` that can\n  # be found on the X86 architectures.\n  #\n  # ARM EHABI also stores the function start and language specific data into the\n  # exception object (`_Unwind_Control_Block`) whereas on X86 they are store on\n  # the context instead. `libgcc` expects a pointer to the control block to be\n  # stored by the personality routine in the R12 snapshot register for the\n  # `_Unwind_GetRegionStart` and `_Unwind_GetLanguageSpecificData` to return the\n  # correct data. Since extracting the data from the control block directly is\n  # easy, we just do it.\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_unwind_get_gr(context : LibUnwind::Context, index : Int32) : UInt32\n    value = 0_u32\n    LibUnwind.vrs_get(context, LibUnwind::UVRSC::CORE, index.to_u32, LibUnwind::UVRSD::UINT32, pointerof(value).as(Void*))\n    value\n  end\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_unwind_set_gr(context : LibUnwind::Context, index : Int32, value : UInt32) : Void\n    LibUnwind.vrs_set(context, LibUnwind::UVRSC::CORE, index.to_u32, LibUnwind::UVRSD::UINT32, pointerof(value).as(Void*))\n  end\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_unwind_get_ip(context : LibUnwind::Context) : LibC::UInt\n    # remove the thumb-bit before returning\n    __crystal_unwind_get_gr(context, 15) & (~0x1_u32)\n  end\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_unwind_set_ip(context : LibUnwind::Context, ip : UInt32) : Void\n    thumb_bit = __crystal_unwind_get_gr(context, 15) & (0x1_u32)\n    __crystal_unwind_set_gr(context, 15, ip | thumb_bit)\n  end\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_get_region_start(ucb : LibUnwind::ControlBlock*) : UInt32\n    ucb.value.pr_cache.fnstart\n  end\n\n  # :nodoc:\n  @[AlwaysInline]\n  fun __crystal_get_language_specific_data(ucb : LibUnwind::ControlBlock*) : UInt8*\n    lsd = ucb.value.pr_cache.ehtp\n    lsd += 1                                # skip personality routine address\n    lsd += (((lsd.value) >> 24) & 0xff) + 1 # skip unwind opcodes\n    lsd.as(UInt8*)\n  end\n{% end %}\n"
  },
  {
    "path": "src/exception.cr",
    "content": "require \"./exception/call_stack\"\nrequire \"system_error\"\n\nException::CallStack.skip(__FILE__)\n\n# Represents errors that occur during application execution.\n#\n# Exception and its descendants are used to communicate between raise and\n# rescue statements in `begin ... end` blocks.\n# Exception objects carry information about the exception – its type (the\n# exception’s class name), an optional descriptive string, and\n# optional traceback information.\n# Exception subclasses may add additional information.\nclass Exception\n  getter message : String?\n  # Returns the previous exception at the time this exception was raised.\n  # This is useful for wrapping exceptions and retaining the original\n  # exception information.\n  getter cause : Exception?\n\n  # :nodoc:\n  property callstack : CallStack?\n\n  def initialize(@message : String? = nil, @cause : Exception? = nil)\n  end\n\n  # Returns any backtrace associated with the exception.\n  # The backtrace is an array of strings, each containing\n  # “0xAddress: Function at File Line Column”.\n  def backtrace : Array(String)\n    self.backtrace?.not_nil!\n  end\n\n  # Returns any backtrace associated with the exception if the call stack exists.\n  # The backtrace is an array of strings, each containing\n  # “0xAddress: Function at File Line Column”.\n  def backtrace?\n    @callstack.try &.printable_backtrace\n  end\n\n  def to_s(io : IO) : Nil\n    io << message\n  end\n\n  def inspect(io : IO) : Nil\n    io << \"#<\" << self.class.name << ':' << message << '>'\n  end\n\n  def inspect_with_backtrace : String\n    String.build do |io|\n      inspect_with_backtrace io\n    end\n  end\n\n  def inspect_with_backtrace(io : IO) : Nil\n    io << message << \" (\" << self.class << \")\\n\"\n\n    backtrace?.try &.each do |frame|\n      io.print \"  from \"\n      io.puts frame\n    end\n\n    if cause = @cause\n      io << \"Caused by: \"\n      cause.inspect_with_backtrace(io)\n    end\n\n    io.flush\n  end\nend\n\n# Raised when the given index is invalid.\n#\n# ```\n# a = [:foo, :bar]\n# a[2] # raises IndexError\n# ```\nclass IndexError < Exception\n  def initialize(message = \"Index out of bounds\")\n    super(message)\n  end\nend\n\n# Raised when the arguments are wrong and there isn't a more specific `Exception` class.\n#\n# ```\n# [1, 2, 3].first(-4) # raises ArgumentError (attempt to take negative size)\n# ```\nclass ArgumentError < Exception\n  def initialize(message = \"Argument error\")\n    super(message)\n  end\nend\n\n# Raised when the type cast failed.\n#\n# ```\n# [1, \"hi\"][1].as(Int32) # raises TypeCastError (cast to Int32 failed)\n# ```\nclass TypeCastError < Exception\n  def initialize(message = \"Type Cast error\")\n    super(message)\n  end\nend\n\nclass InvalidByteSequenceError < Exception\n  def initialize(message = \"Invalid byte sequence in UTF-8 string\")\n    super(message)\n  end\nend\n\n# Raised when the specified key is not found.\n#\n# ```\n# h = {\"foo\" => \"bar\"}\n# h[\"baz\"] # raises KeyError (Missing hash key: \"baz\")\n# ```\nclass KeyError < Exception\nend\n\n# Raised when attempting to divide an integer by 0.\n#\n# ```\n# 1 // 0 # raises DivisionByZeroError (Division by 0)\n# ```\nclass DivisionByZeroError < Exception\n  def initialize(message = \"Division by 0\")\n    super(message)\n  end\nend\n\n# Raised when the result of an arithmetic operation is outside of the range\n# that can be represented within the given operands types.\n#\n# ```\n# Int32::MAX + 1      # raises OverflowError (Arithmetic overflow)\n# Int32::MIN - 1      # raises OverflowError (Arithmetic overflow)\n# Float64::MAX.to_f32 # raises OverflowError (Arithmetic overflow)\n# ```\nclass OverflowError < Exception\n  def initialize(message = \"Arithmetic overflow\")\n    super(message)\n  end\nend\n\n# Raised when a method is not implemented.\n#\n# This can be used either to stub out method bodies, or when the method is not\n# implemented on the current platform.\nclass NotImplementedError < Exception\n  def initialize(item)\n    super(\"Not Implemented: #{item}\")\n  end\nend\n\n# Raised when a `not_nil!` assertion fails.\n#\n# ```\n# \"hello\".index('x').not_nil! # raises NilAssertionError (\"hello\" does not contain 'x')\n# ```\nclass NilAssertionError < Exception\n  def initialize(message = \"Nil assertion failed\")\n    super(message)\n  end\nend\n\n# Raised when there is an internal runtime error\nclass RuntimeError < Exception\n  include SystemError\nend\n"
  },
  {
    "path": "src/fiber/context/aarch64-generic.cr",
    "content": "{% skip_file unless flag?(:aarch64) && !flag?(:win32) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main) : Nil\n    # in ARMv8, the context switch push/pop 12 registers and 8 FPU registers,\n    # and one more to store the argument of `fiber_main` (+ alignment), we thus\n    # reserve space for 22 pointers:\n    @context.stack_top = (stack_ptr - 22).as(Void*)\n    @context.resumable = 1\n\n    stack_ptr[-2] = self.as(Void*)      # x0 (r0): puts `self` as first argument for `fiber_main`\n    stack_ptr[-14] = fiber_main.pointer # x30 (lr): initial `resume` will `ret` to this address\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    #                  x0             , x1\n\n    # adapted from https://github.com/ldc-developers/druntime/blob/ldc/src/core/threadasm.S\n    #\n    # preserve/restore AAPCS64 registers:\n    # x19-x28   5.1.1 64-bit callee saved\n    # x29       fp, or possibly callee saved reg - depends on platform choice 5.2.3)\n    # x30       lr\n    # x0        self argument (initial call)\n    # d8-d15    5.1.2 says callee only must save bottom 64-bits (the \"d\" regs)\n    #\n    # ARM assembly requires integer literals to be moved to a register before\n    # being stored at an address; we use x19 as a scratch register that will be\n    # overwritten by the new context.\n    #\n    # AArch64 assembly also requires a register to load/store the stack top\n    # pointer. We use x19 as a scratch register again.\n    #\n    # Eventually reset LR to zero to avoid the ARM unwinder to mistake the\n    # context switch as a regular call.\n\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") >= 0 %}\n      asm(\"\n      stp     d15, d14, [sp, #-22*8]!\n      stp     d13, d12, [sp, #2*8]\n      stp     d11, d10, [sp, #4*8]\n      stp     d9,  d8,  [sp, #6*8]\n      stp     x30, x29, [sp, #8*8]  // lr, fp\n      stp     x28, x27, [sp, #10*8]\n      stp     x26, x25, [sp, #12*8]\n      stp     x24, x23, [sp, #14*8]\n      stp     x22, x21, [sp, #16*8]\n      stp     x20, x19, [sp, #18*8]\n      stp     x0,  x1,  [sp, #20*8] // push 1st argument (+ alignment)\n\n      mov     x19, sp               // current_context.stack_top = sp\n      str     x19, [x0, #0]\n      {% if flag?(:execution_context) %}\n      dmb     ish                   // barrier: ensure registers are stored\n      {% end %}\n      mov     x19, #1               // current_context.resumable = 1\n      str     x19, [x0, #8]\n\n      mov     x19, #0               // new_context.resumable = 0\n      str     x19, [x1, #8]\n      ldr     x19, [x1, #0]         // sp = new_context.stack_top (x19)\n      mov     sp, x19\n\n      ldp     x0,  x1,  [sp, #20*8] // pop 1st argument (+ alignment)\n      ldp     x20, x19, [sp, #18*8]\n      ldp     x22, x21, [sp, #16*8]\n      ldp     x24, x23, [sp, #14*8]\n      ldp     x26, x25, [sp, #12*8]\n      ldp     x28, x27, [sp, #10*8]\n      ldp     x30, x29, [sp, #8*8]  // lr, fp\n      ldp     d9,  d8,  [sp, #6*8]\n      ldp     d11, d10, [sp, #4*8]\n      ldp     d13, d12, [sp, #2*8]\n      ldp     d15, d14, [sp], #22*8\n\n      // avoid a stack corruption that will confuse the unwinder\n      mov     x16, x30 // save lr\n      mov     x30, #0  // reset lr\n      br      x16      // jump to new pc value\n      \")\n    {% else %}\n      # On LLVM < 9.0 using the previous code emits some additional\n      # instructions that breaks the context switching.\n      asm(\"\n      stp     d15, d14, [sp, #-22*8]!\n      stp     d13, d12, [sp, #2*8]\n      stp     d11, d10, [sp, #4*8]\n      stp     d9,  d8,  [sp, #6*8]\n      stp     x30, x29, [sp, #8*8]  // lr, fp\n      stp     x28, x27, [sp, #10*8]\n      stp     x26, x25, [sp, #12*8]\n      stp     x24, x23, [sp, #14*8]\n      stp     x22, x21, [sp, #16*8]\n      stp     x20, x19, [sp, #18*8]\n      stp     x0,  x1,  [sp, #20*8] // push 1st argument (+ alignment)\n\n      mov     x19, sp               // current_context.stack_top = sp\n      str     x19, [$0, #0]\n      {% if flag?(:execution_context) %}\n      dmb     ish                   // barrier: ensure registers are stored\n      {% end %}\n      mov     x19, #1               // current_context.resumable = 1\n      str     x19, [$0, #8]\n\n      mov     x19, #0               // new_context.resumable = 0\n      str     x19, [$1, #8]\n      ldr     x19, [$1, #0]         // sp = new_context.stack_top (x19)\n      mov     sp, x19\n\n      ldp     x0,  x1,  [sp, #20*8] // pop 1st argument (+ alignment)\n      ldp     x20, x19, [sp, #18*8]\n      ldp     x22, x21, [sp, #16*8]\n      ldp     x24, x23, [sp, #14*8]\n      ldp     x26, x25, [sp, #12*8]\n      ldp     x28, x27, [sp, #10*8]\n      ldp     x30, x29, [sp, #8*8]  // lr, fp\n      ldp     d9,  d8,  [sp, #6*8]\n      ldp     d11, d10, [sp, #4*8]\n      ldp     d13, d12, [sp, #2*8]\n      ldp     d15, d14, [sp], #22*8\n\n      // avoid a stack corruption that will confuse the unwinder\n      mov     x16, x30 // save lr\n      mov     x30, #0  // reset lr\n      br      x16      // jump to new pc value\n      \" :: \"r\"(current_context), \"r\"(new_context))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/aarch64-microsoft.cr",
    "content": "{% skip_file unless flag?(:aarch64) && flag?(:win32) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main) : Nil\n    # ARM64 Windows also follows the AAPCS64 for the most part, except extra\n    # bookkeeping information needs to be kept in the Thread Information Block,\n    # referenceable from the x18 register\n\n    # 12 general-purpose registers + 8 FPU registers + 1 parameter + 3 qwords for NT_TIB\n    @context.stack_top = (stack_ptr - 24).as(Void*)\n    @context.resumable = 1\n\n    # actual stack top, not including guard pages and reserved pages\n    LibC.GetNativeSystemInfo(out system_info)\n    stack_top = @stack.bottom - system_info.dwPageSize\n\n    stack_ptr[-4] = self.as(Void*)      # x0 (r0): puts `self` as first argument for `fiber_main`\n    stack_ptr[-16] = fiber_main.pointer # x30 (lr): initial `resume` will `ret` to this address\n\n    # The following three values are stored in the Thread Information Block (NT_TIB)\n    # and are used by Windows to track the current stack limits\n    stack_ptr[-3] = @stack.pointer # [x18, #0x1478]: Win32 DeallocationStack\n    stack_ptr[-2] = stack_top      # [x18, #16]: Stack Limit\n    stack_ptr[-1] = @stack.bottom  # [x18, #8]: Stack Base\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    #                  x0             , x1\n\n    # see also `./aarch64-generic.cr`\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") >= 0 %}\n      asm(\"\n      stp     d15, d14, [sp, #-24*8]!\n      stp     d13, d12, [sp, #2*8]\n      stp     d11, d10, [sp, #4*8]\n      stp     d9,  d8,  [sp, #6*8]\n      stp     x30, x29, [sp, #8*8]  // lr, fp\n      stp     x28, x27, [sp, #10*8]\n      stp     x26, x25, [sp, #12*8]\n      stp     x24, x23, [sp, #14*8]\n      stp     x22, x21, [sp, #16*8]\n      stp     x20, x19, [sp, #18*8]\n      str     x0,       [sp, #20*8] // push 1st argument\n\n      ldr     x19, [x18, #0x1478]   // Thread Information Block: Win32 DeallocationStack\n      str     x19, [sp, #21*8]\n      ldr     x19, [x18, #16]       // Thread Information Block: Stack Limit\n      str     x19, [sp, #22*8]\n      ldr     x19, [x18, #8]        // Thread Information Block: Stack Base\n      str     x19, [sp, #23*8]\n\n      mov     x19, sp               // current_context.stack_top = sp\n      str     x19, [x0, #0]\n      {% if flag?(:execution_context) %}\n      dmb     ish                   // barrier: ensure registers are stored\n      {% end %}\n      mov     x19, #1               // current_context.resumable = 1\n      str     x19, [x0, #8]\n\n      mov     x19, #0               // new_context.resumable = 0\n      str     x19, [x1, #8]\n      ldr     x19, [x1, #0]         // sp = new_context.stack_top (x19)\n      mov     sp, x19\n\n      ldr     x19, [sp, #23*8]\n      str     x19, [x18, #8]\n      ldr     x19, [sp, #22*8]\n      str     x19, [x18, #16]\n      ldr     x19, [sp, #21*8]\n      str     x19, [x18, #0x1478]\n\n      ldr     x0,       [sp, #20*8] // pop 1st argument (+ alignment)\n      ldp     x20, x19, [sp, #18*8]\n      ldp     x22, x21, [sp, #16*8]\n      ldp     x24, x23, [sp, #14*8]\n      ldp     x26, x25, [sp, #12*8]\n      ldp     x28, x27, [sp, #10*8]\n      ldp     x30, x29, [sp, #8*8]  // lr, fp\n      ldp     d9,  d8,  [sp, #6*8]\n      ldp     d11, d10, [sp, #4*8]\n      ldp     d13, d12, [sp, #2*8]\n      ldp     d15, d14, [sp], #24*8\n\n      // avoid a stack corruption that will confuse the unwinder\n      mov     x16, x30 // save lr\n      mov     x30, #0  // reset lr\n      br      x16      // jump to new pc value\n      \")\n    {% else %}\n      # On LLVM < 9.0 using the previous code emits some additional\n      # instructions that breaks the context switching.\n      asm(\"\n      stp     d15, d14, [sp, #-24*8]!\n      stp     d13, d12, [sp, #2*8]\n      stp     d11, d10, [sp, #4*8]\n      stp     d9,  d8,  [sp, #6*8]\n      stp     x30, x29, [sp, #8*8]  // lr, fp\n      stp     x28, x27, [sp, #10*8]\n      stp     x26, x25, [sp, #12*8]\n      stp     x24, x23, [sp, #14*8]\n      stp     x22, x21, [sp, #16*8]\n      stp     x20, x19, [sp, #18*8]\n      str     x0,       [sp, #20*8] // push 1st argument\n\n      ldr     x19, [x18, #0x1478]   // Thread Information Block: Win32 DeallocationStack\n      str     x19, [sp, #21*8]\n      ldr     x19, [x18, #16]       // Thread Information Block: Stack Limit\n      str     x19, [sp, #22*8]\n      ldr     x19, [x18, #8]        // Thread Information Block: Stack Base\n      str     x19, [sp, #23*8]\n\n      mov     x19, sp               // current_context.stack_top = sp\n      str     x19, [$0, #0]\n      {% if flag?(:execution_context) %}\n      dmb     ish                   // barrier: ensure registers are stored\n      {% end %}\n      mov     x19, #1               // current_context.resumable = 1\n      str     x19, [$0, #8]\n\n      mov     x19, #0               // new_context.resumable = 0\n      str     x19, [$1, #8]\n      ldr     x19, [$1, #0]         // sp = new_context.stack_top (x19)\n      mov     sp, x19\n\n      ldr     x19, [sp, #23*8]\n      str     x19, [x18, #8]\n      ldr     x19, [sp, #22*8]\n      str     x19, [x18, #16]\n      ldr     x19, [sp, #21*8]\n      str     x19, [x18, #0x1478]\n\n      ldr     x0,       [sp, #20*8] // pop 1st argument (+ alignment)\n      ldp     x20, x19, [sp, #18*8]\n      ldp     x22, x21, [sp, #16*8]\n      ldp     x24, x23, [sp, #14*8]\n      ldp     x26, x25, [sp, #12*8]\n      ldp     x28, x27, [sp, #10*8]\n      ldp     x30, x29, [sp, #8*8]  // lr, fp\n      ldp     d9,  d8,  [sp, #6*8]\n      ldp     d11, d10, [sp, #4*8]\n      ldp     d13, d12, [sp, #2*8]\n      ldp     d15, d14, [sp], #24*8\n\n      // avoid a stack corruption that will confuse the unwinder\n      mov     x16, x30 // save lr\n      mov     x30, #0  // reset lr\n      br      x16      // jump to new pc value\n      \" :: \"r\"(current_context), \"r\"(new_context))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/arm.cr",
    "content": "{% skip_file unless flag?(:arm) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main) : Void*\n    # in ARMv6 / ARVMv7, the context switch push/pop 8 registers, add one more\n    # to store the argument of `fiber_main`, and 8 64-bit FPU registers if a FPU\n    # is present, we thus reserve space for 9 or 25 pointers:\n    {% if flag?(:armhf) %}\n      @context.stack_top = (stack_ptr - 25).as(Void*)\n    {% else %}\n      @context.stack_top = (stack_ptr - 9).as(Void*)\n    {% end %}\n    @context.resumable = 1\n\n    stack_ptr[0] = fiber_main.pointer # lr: initial `resume` will `ret` to this address\n    stack_ptr[-9] = self.as(Void*)    # r0: puts `self` as first argument for `fiber_main`\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    #                  r0             , r1\n\n    # ARM assembly requires integer literals to be moved to a register before\n    # being stored at an address; we use r4 as a scratch register that will be\n    # overwritten by the new context.\n    #\n    # Eventually reset LR to zero to avoid the ARM unwinder to mistake the\n    # context switch as a regular call.\n    #\n    # NOTE: depending on the ARM architecture (v7, v6 or older) LLVM uses\n    # different strategies for atomics. By default it uses the \"older\"\n    # architecture that relies on the libgcc __sync_* symbols; when an armv6 CPU\n    # or +v6 feature is specified it uses the coprocessor instruction as used\n    # below, unless the +db (data barrier) feature is set, in which case it\n    # uses the `dmb ish` instruction. The +db feature is always enabled since\n    # armv7 / +v7.\n    #\n    # TODO: we should do the same, but we don't know the list of CPU features\n    # for the current target machine (and LLVM won't tell us).\n\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") >= 0 %}\n      asm(\"\n        {% if flag?(:armhf) %}\n        // declare the presence of a conservative FPU to the ASM compiler\n        .fpu vfp\n        {% end %}\n\n        stmdb  sp!, {r0, r4-r11, lr}  // push 1st argument + callee-saved registers\n        {% if flag?(:armhf) %}\n        vstmdb sp!, {d8-d15}          // push FPU registers\n        {% end %}\n        str    sp, [r0, #0]           // current_context.stack_top = sp\n        {% if flag?(:execution_context) %}\n        mov    r4, #0                 // barrier: ensure registers are stored\n        mcr    p15, #0, r4, c7, c10, #5\n        {% end %}\n        mov    r4, #1                 // current_context.resumable = 1\n        str    r4, [r0, #4]\n\n        mov    r4, #0                 // new_context.resumable = 0\n        str    r4, [r1, #4]\n        ldr    sp, [r1, #0]           // sp = new_context.stack_top\n        {% if flag?(:armhf) %}\n        vldmia sp!, {d8-d15}          // pop FPU registers\n        {% end %}\n        ldmia  sp!, {r0, r4-r11, lr}  // pop 1st argument + callee-saved registers\n\n        // avoid a stack corruption that will confuse the unwinder\n        mov    r1, lr\n        mov    lr, #0\n        mov    pc, r1\n        \")\n    {% else %}\n      # On LLVM < 9.0 using the previous code emits some additional\n      # instructions that breaks the context switching.\n      asm(\"\n        {% if flag?(:armhf) %}\n        // declare the presence of a conservative FPU to the ASM compiler\n        .fpu vfp\n        {% end %}\n\n        stmdb  sp!, {r0, r4-r11, lr}  // push 1st argument + callee-saved registers\n        {% if flag?(:armhf) %}\n        vstmdb sp!, {d8-d15}          // push FPU registers\n        {% end %}\n        str    sp, [$0, #0]           // current_context.stack_top = sp\n        {% if flag?(:execution_context) %}\n        mov    r4, #0                 // barrier: ensure registers are stored\n        mcr    p15, #0, r4, c7, c10, #5\n        {% end %}\n        mov    r4, #1                 // current_context.resumable = 1\n        str    r4, [$0, #4]\n\n        mov    r4, #0                 // new_context.resumable = 0\n        str    r4, [$1, #4]\n        ldr    sp, [$1, #0]           // sp = new_context.stack_top\n        {% if flag?(:armhf) %}\n        vldmia sp!, {d8-d15}          // pop FPU registers\n        {% end %}\n        ldmia  sp!, {r0, r4-r11, lr}  // pop 1st argument + callee-saved registers\n\n        // avoid a stack corruption that will confuse the unwinder\n        mov    r1, lr\n        mov    lr, #0\n        mov    pc, r1\n        \" :: \"r\"(current_context), \"r\"(new_context))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/i386.cr",
    "content": "{% skip_file unless flag?(:i386) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main)\n    # in IA32 (x86), the context switch push/pop 4 registers, and we need two\n    # more to store the argument for `fiber_main` and keep the stack aligned on\n    # 16 bytes, we thus reserve space for 6 pointers:\n    @context.stack_top = (stack_ptr - 6).as(Void*)\n    @context.resumable = 1\n\n    stack_ptr[0] = self.as(Void*)      # first argument passed on the stack\n    stack_ptr[-1] = Pointer(Void).null # empty space to keep the stack alignment (16 bytes)\n    stack_ptr[-2] = fiber_main.pointer # initial `resume` will `ret` to this address\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") >= 0 %}\n      #                %ecx           , %eax\n      asm(\"\n      movl 8(%esp), %eax\n      movl 4(%esp), %ecx\n      pushl %edi        // push 1st argument (because of initial resume)\n      pushl %ebx        // push callee-saved registers on the stack\n      pushl %ebp\n      pushl %esi\n      movl %esp, 0(%ecx)  // current_context.stack_top = %esp\n      movl $$1, 4(%ecx)   // current_context.resumable = 1\n\n      movl $$0, 4(%eax)   // new_context.resumable = 0\n      movl 0(%eax), %esp  // %esp = new_context.stack_top\n      popl %esi         // pop callee-saved registers from the stack\n      popl %ebp\n      popl %ebx\n      popl %edi         // pop first argument (for initial resume)\n      \")\n    {% else %}\n      # On LLVM < 9.0 using the previous code emits some additional\n      # instructions that breaks the context switching.\n      asm(\"\n      pushl %edi        // push 1st argument (because of initial resume)\n      pushl %ebx        // push callee-saved registers on the stack\n      pushl %ebp\n      pushl %esi\n      movl %esp, 0($0)  // current_context.stack_top = %esp\n      movl $$1, 4($0)   // current_context.resumable = 1\n\n      movl $$0, 4($1)   // new_context.resumable = 0\n      movl 0($1), %esp  // %esp = new_context.stack_top\n      popl %esi         // pop callee-saved registers from the stack\n      popl %ebp\n      popl %ebx\n      popl %edi         // pop first argument (for initial resume)\n      \" :: \"r\"(current_context), \"r\"(new_context))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/interpreted.cr",
    "content": "{% skip_file unless flag?(:interpreted) %}\n\nrequire \"crystal/interpreter\"\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main) : Nil\n    # In interpreted mode the stack_top variable actually points to the actual\n    # fiber running on the interpreter\n    @context.stack_top = Crystal::Interpreter.spawn(self, fiber_main.pointer)\n  end\n\n  # :nodoc:\n  @[Primitive(:interpreter_fiber_swapcontext)]\n  def self.swapcontext(current_context, new_context) : Nil\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/wasm32.cr",
    "content": "{% skip_file unless flag?(:wasm32) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main)\n    # TODO: Implement this using Binaryen Asyncify\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    # TODO: Implement this using Binaryen Asyncify\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/x86_64-microsoft.cr",
    "content": "{% skip_file unless flag?(:x86_64) && flag?(:win32) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main) : Nil\n    # A great explanation on stack contexts for win32:\n    # https://web.archive.org/web/20220527113808/https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/supporting-windows\n\n    # 4 shadow space + (8 registers + 3 qwords for NT_TIB + 1 parameter) + 10 128bit XMM registers\n    @context.stack_top = (stack_ptr - (4 + 12 + 10*2)).as(Void*)\n    @context.resumable = 1\n\n    # actual stack top, not including guard pages and reserved pages\n    LibC.GetNativeSystemInfo(out system_info)\n    stack_top = @stack.bottom - system_info.dwPageSize\n\n    stack_ptr -= 4                    # shadow space (or home space) before return address\n    stack_ptr[0] = fiber_main.pointer # %rbx: Initial `resume` will `ret` to this address\n    stack_ptr[-1] = self.as(Void*)    # %rcx: puts `self` as first argument for `fiber_main`\n\n    # The following three values are stored in the Thread Information Block (NT_TIB)\n    # and are used by Windows to track the current stack limits\n    stack_ptr[-2] = @stack.pointer # %gs:0x1478: Win32 DeallocationStack\n    stack_ptr[-3] = stack_top      # %gs:0x10: Stack Limit\n    stack_ptr[-4] = @stack.bottom  # %gs:0x08: Stack Base\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") >= 0 %}\n      #                %rcx           , %rdx\n      asm(\"\n          pushq %rcx\n          pushq %gs:0x1478  // Thread Information Block: Win32 DeallocationStack\n          pushq %gs:0x10    // Thread Information Block: Stack Limit\n          pushq %gs:0x08    // Thread Information Block: Stack Base\n          pushq %rdi        // push 1st argument (because of initial resume)\n          pushq %rbx        // push callee-saved registers on the stack\n          pushq %rbp\n          pushq %rsi\n          pushq %r12\n          pushq %r13\n          pushq %r14\n          pushq %r15\n          subq $$160, %rsp  // push XMM registers\n          movups %xmm6, 0x00(%rsp)\n          movups %xmm7, 0x10(%rsp)\n          movups %xmm8, 0x20(%rsp)\n          movups %xmm9, 0x30(%rsp)\n          movups %xmm10, 0x40(%rsp)\n          movups %xmm11, 0x50(%rsp)\n          movups %xmm12, 0x60(%rsp)\n          movups %xmm13, 0x70(%rsp)\n          movups %xmm14, 0x80(%rsp)\n          movups %xmm15, 0x90(%rsp)\n          movq %rsp, 0(%rcx)  // current_context.stack_top = %rsp\n          movl $$1, 8(%rcx)   // current_context.resumable = 1\n          movl $$0, 8(%rdx)   // new_context.resumable = 0\n          movq 0(%rdx), %rsp  // %rsp = new_context.stack_top\n          movups 0x00(%rsp), %xmm6 // pop XMM registers\n          movups 0x10(%rsp), %xmm7\n          movups 0x20(%rsp), %xmm8\n          movups 0x30(%rsp), %xmm9\n          movups 0x40(%rsp), %xmm10\n          movups 0x50(%rsp), %xmm11\n          movups 0x60(%rsp), %xmm12\n          movups 0x70(%rsp), %xmm13\n          movups 0x80(%rsp), %xmm14\n          movups 0x90(%rsp), %xmm15\n          addq $$160, %rsp\n          popq %r15         // pop callee-saved registers from the stack\n          popq %r14\n          popq %r13\n          popq %r12\n          popq %rsi\n          popq %rbp\n          popq %rbx\n          popq %rdi         // pop 1st argument (for initial resume)\n          popq %gs:0x08\n          popq %gs:0x10\n          popq %gs:0x1478\n          popq %rcx\n          \")\n    {% else %}\n      # On LLVM < 9.0 using the previous code emits some additional\n      # instructions that breaks the context switching.\n      asm(\"\n          pushq %rcx\n          pushq %gs:0x1478  // Thread Information Block: Win32 DeallocationStack\n          pushq %gs:0x10    // Thread Information Block: Stack Limit\n          pushq %gs:0x08    // Thread Information Block: Stack Base\n          pushq %rdi        // push 1st argument (because of initial resume)\n          pushq %rbx        // push callee-saved registers on the stack\n          pushq %rbp\n          pushq %rsi\n          pushq %r12\n          pushq %r13\n          pushq %r14\n          pushq %r15\n          subq $$160, %rsp  // push XMM registers\n          movups %xmm6, 0x00(%rsp)\n          movups %xmm7, 0x10(%rsp)\n          movups %xmm8, 0x20(%rsp)\n          movups %xmm9, 0x30(%rsp)\n          movups %xmm10, 0x40(%rsp)\n          movups %xmm11, 0x50(%rsp)\n          movups %xmm12, 0x60(%rsp)\n          movups %xmm13, 0x70(%rsp)\n          movups %xmm14, 0x80(%rsp)\n          movups %xmm15, 0x90(%rsp)\n          movq %rsp, 0($0)  // current_context.stack_top = %rsp\n          movl $$1, 8($0)   // current_context.resumable = 1\n          movl $$0, 8($1)   // new_context.resumable = 0\n          movq 0($1), %rsp  // %rsp = new_context.stack_top\n          movups 0x00(%rsp), %xmm6 // pop XMM registers\n          movups 0x10(%rsp), %xmm7\n          movups 0x20(%rsp), %xmm8\n          movups 0x30(%rsp), %xmm9\n          movups 0x40(%rsp), %xmm10\n          movups 0x50(%rsp), %xmm11\n          movups 0x60(%rsp), %xmm12\n          movups 0x70(%rsp), %xmm13\n          movups 0x80(%rsp), %xmm14\n          movups 0x90(%rsp), %xmm15\n          addq $$160, %rsp\n          popq %r15         // pop callee-saved registers from the stack\n          popq %r14\n          popq %r13\n          popq %r12\n          popq %rsi\n          popq %rbp\n          popq %rbx\n          popq %rdi         // pop 1st argument (for initial resume)\n          popq %gs:0x08\n          popq %gs:0x10\n          popq %gs:0x1478\n          popq %rcx\n          \" :: \"r\"(current_context), \"r\"(new_context))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/fiber/context/x86_64-sysv.cr",
    "content": "{% skip_file unless flag?(:x86_64) && flag?(:unix) %}\n\nclass Fiber\n  # :nodoc:\n  def makecontext(stack_ptr, fiber_main) : Nil\n    # in x86-64, the context switch push/pop 6 registers + the return address\n    # that is left on the stack, we thus reserve space for 7 pointers:\n    @context.stack_top = (stack_ptr - 7).as(Void*)\n    @context.resumable = 1\n\n    stack_ptr[0] = fiber_main.pointer # %rbx: initial `resume` will `ret` to this address\n    stack_ptr[-1] = self.as(Void*)    # %rdi: puts `self` as first argument for `fiber_main`\n  end\n\n  # :nodoc:\n  @[NoInline]\n  @[Naked]\n  def self.swapcontext(current_context, new_context) : Nil\n    {% if compare_versions(Crystal::LLVM_VERSION, \"9.0.0\") >= 0 %}\n      #                %rdi           , %rsi\n      asm(\"\n      pushq %rdi        // push 1st argument (because of initial resume)\n      pushq %rbx        // push callee-saved registers on the stack\n      pushq %rbp\n      pushq %r12\n      pushq %r13\n      pushq %r14\n      pushq %r15\n      movq %rsp, 0(%rdi)  // current_context.stack_top = %rsp\n      movl $$1, 8(%rdi)   // current_context.resumable = 1\n\n      movl $$0, 8(%rsi)   // new_context.resumable = 0\n      movq 0(%rsi), %rsp  // %rsp = new_context.stack_top\n      popq %r15         // pop callee-saved registers from the stack\n      popq %r14\n      popq %r13\n      popq %r12\n      popq %rbp\n      popq %rbx\n      popq %rdi         // pop 1st argument (for initial resume)\n      \")\n    {% else %}\n      # On LLVM < 9.0 using the previous code emits some additional\n      # instructions that breaks the context switching.\n      asm(\"\n      pushq %rdi        // push 1st argument (because of initial resume)\n      pushq %rbx        // push callee-saved registers on the stack\n      pushq %rbp\n      pushq %r12\n      pushq %r13\n      pushq %r14\n      pushq %r15\n      movq %rsp, 0($0)  // current_context.stack_top = %rsp\n      movl $$1, 8($0)   // current_context.resumable = 1\n\n      movl $$0, 8($1)   // new_context.resumable = 0\n      movq 0($1), %rsp  // %rsp = new_context.stack_top\n      popq %r15         // pop callee-saved registers from the stack\n      popq %r14\n      popq %r13\n      popq %r12\n      popq %rbp\n      popq %rbx\n      popq %rdi         // pop 1st argument (for initial resume)\n      \" :: \"r\"(current_context), \"r\"(new_context))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/fiber/context.cr",
    "content": "class Fiber\n  # :nodoc:\n  #\n  # The arch-specific make/swapcontext assembly relies on the Context struct and\n  # expects the following layout. Avoid moving the struct properties as it would\n  # require to update all the make/swapcontext implementations.\n  @[Extern]\n  struct Context\n    property stack_top : Void*\n\n    {% if flag?(:interpreted) %}\n      # In interpreted mode, the interpreted fibers will be backed by a real\n      # fiber run by the interpreter. The fiber context is thus fake.\n      #\n      # The `stack_top` property is actually a pointer to the real Fiber\n      # running in the interpreter.\n      #\n      # The `resumable` property is also delegated to the real fiber. Only the\n      # getter is defined (so we know the real state of the fiber); we don't\n      # declare a setter because only the interpreter can manipulate it (in the\n      # `makecontext` and `swapcontext` primitives).\n      def resumable : LibC::Long\n        Crystal::Interpreter.fiber_resumable(pointerof(@stack_top))\n      end\n    {% else %}\n      property resumable : LibC::Long = 0\n    {% end %}\n\n    def initialize(@stack_top = Pointer(Void).null)\n    end\n  end\n\n  # :nodoc:\n  #\n  # A fiber context switch in Crystal is achieved by calling a symbol (which\n  # must never be inlined) that will push the callee-saved registers (sometimes\n  # FPU registers and others) on the stack, saving the current stack pointer at\n  # location pointed by `current_stack_ptr` (the current fiber is now paused)\n  # then loading the `dest_stack_ptr` pointer into the stack pointer register\n  # and popping previously saved registers from the stack. Upon return from the\n  # symbol the new fiber is resumed since we returned/jumped to the calling\n  # symbol.\n  #\n  # Details are arch-specific. For example:\n  # - which registers must be saved, the callee-saved are sometimes enough (X86)\n  #   but some archs need to save the FPU register too (ARMHF);\n  # - a simple return may be enough (X86), but sometimes an explicit jump is\n  #   required to not confuse the stack unwinder (ARM);\n  # - and more.\n  #\n  # For the initial resume, the register holding the first parameter must be set\n  # (see makecontext below) and thus must also be saved/restored when swapping\n  # the context.\n  #\n  # def self.swapcontext(current_context : Context*, new_context : Context*) : Nil\n  # end\n\n  # :nodoc:\n  #\n  # Initializes `@context`, reserves and initializes space on the stack for the\n  # initial context that must call the *fiber_main* proc, passing `self` as its\n  # first argument.\n  #\n  # def makecontext(stack_ptr : Void*, fiber_main : Fiber ->) : Nil\n  # end\nend\n\n# Load the arch-specific methods to create a context and to swap from one\n# context to another one. There are two methods: `Fiber#makecontext` and\n# `Fiber.swapcontext`.\n{% if flag?(:interpreted) %}\n  require \"./context/interpreted\"\n{% else %}\n  require \"./context/*\"\n{% end %}\n"
  },
  {
    "path": "src/fiber/execution_context/concurrent.cr",
    "content": "require \"./parallel\"\n\nmodule Fiber::ExecutionContext\n  # Concurrent-only execution context.\n  #\n  # Fibers running in the same context can only run concurrently and never in\n  # parallel to each others. However, they still run in parallel to fibers\n  # running in other execution contexts.\n  #\n  # Fibers in this context can use simpler and faster synchronization primitives\n  # between themselves (for example no atomics or thread safety required), but\n  # data shared with other contexts needs to be protected (see `Sync`), and\n  # communication with fibers in other contexts requires safe primitives, for\n  # example `Channel`.\n  #\n  # A blocking fiber blocks the entire context, and thus all the other fibers in\n  # the context.\n  #\n  # For example: we can start a concurrent context to run consumer fibers, while\n  # the default context produces values. Because the consumer fibers will never\n  # run in parallel and don't yield between reading *result* then writing it, we\n  # are not required to synchronize accesses to the value:\n  #\n  # ```\n  # require \"wait_group\"\n  #\n  # consumers = Fiber::ExecutionContext::Concurrent.new(\"consumers\")\n  # channel = Channel(Int32).new(64)\n  # wg = WaitGroup.new(32)\n  #\n  # result = 0\n  #\n  # 32.times do\n  #   consumers.spawn do\n  #     while value = channel.receive?\n  #       # safe, but only for this example:\n  #       result = result + value\n  #     end\n  #   ensure\n  #     wg.done\n  #   end\n  # end\n  #\n  # 1024.times { |i| channel.send(i) }\n  # channel.close\n  #\n  # # wait for all workers to be done\n  # wg.wait\n  #\n  # p result # => 523776\n  # ```\n  #\n  # In practice, we still recommended to always protect shared accesses to a\n  # variable, for example using `Atomic#add` to increment *result* or a `Sync`\n  # primitive for more complex operations.\n  #\n  # NOTE: The `Concurrent` execution context isn't tied to a system thread, and\n  # may switch to another system thread, for example when a fiber is blocked on\n  # a syscall.\n  class Concurrent < Parallel\n    # :nodoc:\n    def self.default : self\n      new(\"DEFAULT\", capacity: 1, hijack: true)\n    end\n\n    # Creates a `Concurrent` context. The context will only really start when a\n    # fiber is spawned into it.\n    def self.new(name : String) : self\n      new(name, capacity: 1, hijack: false)\n    end\n\n    # Always raises an `ArgumentError` exception because a concurrent context\n    # cannot be resized.\n    def resize(maximum : Int32) : Nil\n      raise ArgumentError.new(\"Can't resize a concurrent context\")\n    end\n  end\n\n  @[Deprecated(\"Use Fiber::ExecutionContext::Concurrent instead.\")]\n  alias SingleThreaded = Concurrent\nend\n"
  },
  {
    "path": "src/fiber/execution_context/global_queue.cr",
    "content": "# The queue is a port of Go's `globrunq*` functions, distributed under a\n# BSD-like license:\n# <https://cs.opensource.google/go/go/+/release-branch.go1.23:LICENSE>\n\nrequire \"../list\"\nrequire \"./runnables\"\n\nmodule Fiber::ExecutionContext\n  # :nodoc:\n  #\n  # Global queue of runnable fibers.\n  # Unbounded.\n  # Shared by all schedulers in an execution context.\n  #\n  # Basically a `Fiber::List` protected by a `Thread::Mutex`, at the exception of\n  # the `#grab?` method that tries to grab 1/Nth of the queue at once.\n  class GlobalQueue\n    def initialize(@mutex : Thread::Mutex)\n      @list = Fiber::List.new\n    end\n\n    # Grabs the lock and enqueues a runnable fiber on the global runnable queue.\n    def push(fiber : Fiber) : Nil\n      @mutex.synchronize { unsafe_push(fiber) }\n    end\n\n    # Enqueues a runnable fiber on the global runnable queue. Assumes the lock\n    # is currently held.\n    def unsafe_push(fiber : Fiber) : Nil\n      @list.push(fiber)\n    end\n\n    # Grabs the lock and puts a runnable fiber on the global runnable queue.\n    def bulk_push(list : Fiber::List*) : Nil\n      @mutex.synchronize { unsafe_bulk_push(list) }\n    end\n\n    # Puts a runnable fiber on the global runnable queue. Assumes the lock is\n    # currently held.\n    def unsafe_bulk_push(list : Fiber::List*) : Nil\n      @list.bulk_unshift(list)\n    end\n\n    # Grabs the lock and dequeues one runnable fiber from the global runnable\n    # queue.\n    def pop? : Fiber?\n      @mutex.synchronize { unsafe_pop? }\n    end\n\n    # Dequeues one runnable fiber from the global runnable queue. Assumes the\n    # lock is currently held.\n    def unsafe_pop? : Fiber?\n      @list.pop?\n    end\n\n    # Grabs the lock then tries to grab a batch of fibers from the global\n    # runnable queue. Returns the next runnable fiber or `nil` if the queue was\n    # empty.\n    #\n    # `divisor` is meant for fair distribution of fibers across threads in the\n    # execution context; it should be the number of threads.\n    def grab?(runnables : Runnables, divisor : Int32) : Fiber?\n      @mutex.synchronize { unsafe_grab?(runnables, divisor) }\n    end\n\n    # Try to grab a batch of fibers from the global runnable queue. Returns the\n    # next runnable fiber or `nil` if the queue was empty. Assumes the lock is\n    # currently held.\n    #\n    # `divisor` is meant for fair distribution of fibers across threads in the\n    # execution context; it should be the number of threads.\n    def unsafe_grab?(runnables : Runnables, divisor : Int32) : Fiber?\n      # ported from Go: globrunqget\n      return if @list.empty?\n\n      divisor = 1 if divisor < 1\n      size = @list.size\n\n      n = {\n        size,                    # can't grab more than available\n        size // divisor + 1,     # divide + try to take at least 1 fiber\n        runnables.capacity // 2, # refill half the destination queue\n      }.min\n\n      fiber = @list.pop?\n\n      # OPTIMIZE: list = @list.split(n - 1) then `runnables.push(pointerof(list))` (?)\n      (n - 1).times do\n        break unless f = @list.pop?\n        runnables.push(f)\n      end\n\n      fiber\n    end\n\n    @[AlwaysInline]\n    def empty? : Bool\n      @list.empty?\n    end\n\n    @[AlwaysInline]\n    def size : Int32\n      @list.size\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context/isolated.cr",
    "content": "require \"./scheduler\"\nrequire \"../list\"\n\nmodule Fiber::ExecutionContext\n  # Isolated execution context to run a single fiber.\n  #\n  # Concurrency and parallelism are disabled. The context guarantees that the\n  # fiber will always run on the same system thread until it terminates; the\n  # fiber owns the system thread for its whole lifetime.\n  #\n  # Keep in mind that the fiber will still run in parallel to other fibers\n  # running in other execution contexts at the same time.\n  #\n  # Concurrency is disabled, so an isolated fiber can't spawn fibers into the\n  # context, but it can spawn fibers into other execution contexts. Since it can\n  # be inconvenient to pass an execution context around, calls to `::spawn` will\n  # spawn a fiber into the specified *spawn_context* during initialization,\n  # which defaults to `Fiber::ExecutionContext.default`.\n  #\n  # Isolated fibers can normally communicate with other fibers running in other\n  # execution contexts using `Channel`, `WaitGroup` or `Sync` for example. They\n  # can also execute `IO` operations or `sleep` just like any other fiber.\n  #\n  # Calls that result in waiting (e.g. sleep, or socket read/write) will block\n  # the thread since there are no other fibers to switch to. This in turn allows\n  # to call anything that would block the thread without blocking any other\n  # fiber.\n  #\n  # For example you can start an isolated fiber to run a blocking GUI loop,\n  # transparently forward `::spawn` to the default context, then keep the main\n  # fiber to wait until the GUI application quit:\n  #\n  # ```\n  # gtk = Fiber::ExecutionContext::Isolated.new(\"Gtk\") do\n  #   Gtk.main\n  # end\n  # gtk.wait\n  # ```\n  class Isolated\n    include ExecutionContext\n    include ExecutionContext::Scheduler\n\n    getter name : String\n\n    @mutex = Thread::Mutex.new\n    @condition = Thread::ConditionVariable.new\n    protected getter thread : Thread\n    protected getter main_fiber : Fiber\n\n    # :nodoc:\n    getter(event_loop : Crystal::EventLoop) do\n      evloop = Crystal::EventLoop.create(parallelism: 1)\n      evloop.register(self, index: 0)\n      evloop\n    end\n\n    getter? running : Bool = true\n    @enqueued = false\n    @waiting = false\n\n    @wait_list = Crystal::PointerLinkedList(Fiber::PointerLinkedListNode).new\n    @exception : Exception?\n\n    # Starts a new thread named *name* to execute *func*. Once *func* returns\n    # the thread will terminate.\n    def initialize(@name : String, @spawn_context : ExecutionContext = ExecutionContext.default, &@func : ->)\n      @thread = uninitialized Thread\n      @main_fiber = uninitialized Fiber\n      @main_fiber = Fiber.new(@name, allocate_stack, self) { run }\n      @thread = start_thread\n      ExecutionContext.execution_contexts.push(self)\n    end\n\n    private def allocate_stack : Stack\n      # no stack pool: we directly allocate a stack; it will be automatically\n      # released when the thread is returned to the thread pool\n      pointer = Crystal::System::Fiber.allocate_stack(StackPool::STACK_SIZE, protect: true)\n      Stack.new(pointer, StackPool::STACK_SIZE, reusable: true)\n    end\n\n    private def start_thread : Thread\n      ExecutionContext.thread_pool.checkout(self)\n    end\n\n    protected def thread=(@thread)\n    end\n\n    # :nodoc:\n    def execution_context : Isolated\n      self\n    end\n\n    protected def each_scheduler(& : Scheduler ->) : Nil\n      yield self\n    end\n\n    # :nodoc:\n    def stack_pool : Fiber::StackPool\n      raise RuntimeError.new(\"No stack pool for isolated contexts\")\n    end\n\n    # :nodoc:\n    def stack_pool? : Fiber::StackPool?\n    end\n\n    def spawn(*, name : String? = nil, &block : ->) : Fiber\n      @spawn_context.spawn(name: name, &block)\n    end\n\n    # :nodoc:\n    def spawn(*, name : String? = nil, same_thread : Bool, &block : ->) : Fiber\n      raise ArgumentError.new(\"#{self.class.name}#spawn doesn't support same_thread:true\") if same_thread\n      @spawn_context.spawn(name: name, &block)\n    end\n\n    # :nodoc:\n    def enqueue(fiber : Fiber) : Nil\n      enqueue_impl(fiber)\n    end\n\n    # :nodoc:\n    def external_enqueue(fiber : Fiber) : Nil\n      enqueue_impl(fiber)\n    end\n\n    private def enqueue_impl(fiber)\n      Crystal.trace :sched, \"enqueue\", fiber: fiber, context: self\n\n      unless fiber == @main_fiber\n        raise RuntimeError.new(\"Concurrency is disabled in isolated contexts\")\n      end\n\n      @mutex.synchronize do\n        raise RuntimeError.new(\"Can't resume dead fiber\") unless @running\n\n        @enqueued = true\n\n        if @waiting\n          # wake up the blocked thread\n          @waiting = false\n\n          if event_loop = @event_loop\n            event_loop.interrupt\n          else\n            @condition.signal\n          end\n        else\n          # race: enqueued before the other thread started waiting\n        end\n      end\n    end\n\n    protected def reschedule : Nil\n      Crystal.trace :sched, \"reschedule\"\n\n      if @main_fiber.dead?\n        ExecutionContext.thread_pool.checkin\n        return # actually unreachable\n      end\n\n      if event_loop = @event_loop\n        wait_for(event_loop)\n      else\n        park_thread\n      end\n\n      Crystal.trace :sched, \"resume\"\n    end\n\n    private def park_thread\n      @mutex.synchronize do\n        loop do\n          return if check_enqueued?\n          @waiting = true\n          @condition.wait(@mutex)\n        end\n      end\n    end\n\n    private def wait_for(event_loop) : Nil\n      loop do\n        @mutex.synchronize do\n          return if check_enqueued?\n          @waiting = true\n        end\n\n        # wait on the event loop\n        list = Fiber::List.new\n        event_loop.run(pointerof(list), blocking: true)\n\n        # restart if the evloop got interrupted\n        next unless fiber = list.pop?\n\n        # sanity check\n        unless fiber == @main_fiber && list.empty?\n          raise RuntimeError.new(\"Concurrency is disabled in isolated contexts\")\n        end\n\n        break\n      end\n\n      # cleanup\n      @mutex.synchronize do\n        @waiting = false\n        @enqueued = false\n      end\n    end\n\n    private def check_enqueued?\n      if @enqueued\n        @enqueued = false\n        @waiting = false\n        true\n      else\n        false\n      end\n    end\n\n    protected def resume(fiber : Fiber) : Nil\n      # in theory we could resume @main_fiber, but this method may only be\n      # called from the current execution context, which is @main_fiber; it\n      # doesn't make any sense for a fiber to resume itself\n      raise RuntimeError.new(\"Can't resume #{fiber} in #{self}\")\n    end\n\n    private def run\n      Crystal.trace :sched, \"started\"\n      @func.call\n    rescue exception\n      @exception = exception\n    ensure\n      @mutex.synchronize do\n        @running = false\n        @wait_list.consume_each(&.value.enqueue)\n      end\n\n      begin\n        @event_loop.try(&.unregister(self))\n      ensure\n        ExecutionContext.execution_contexts.delete(self)\n      end\n    end\n\n    # Blocks the calling fiber until the isolated context fiber terminates.\n    # Returns immediately if the isolated fiber has already terminated.\n    # Re-raises unhandled exceptions raised by the fiber.\n    #\n    # For example:\n    #\n    # ```\n    # ctx = Fiber::ExecutionContext::Isolated.new(\"test\") do\n    #   raise \"fail\"\n    # end\n    # ctx.wait # => re-raises \"fail\"\n    # ```\n    def wait : Nil\n      if @running\n        node = Fiber::PointerLinkedListNode.new(Fiber.current)\n        running = @mutex.synchronize do\n          @wait_list.push(pointerof(node)) if @running\n          @running\n        end\n        Fiber.suspend if running\n      end\n\n      if exception = @exception\n        raise exception\n      end\n    end\n\n    # :nodoc:\n    #\n    # Simple alias to ease the transition from Thread.\n    def join\n      wait\n    end\n\n    def inspect(io : IO) : Nil\n      to_s(io)\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"#<\" << self.class.name << \":0x\"\n      object_id.to_s(io, 16)\n      io << ' ' << name << '>'\n    end\n\n    def status : String\n      if @waiting\n        \"event-loop\"\n      elsif @syscall == SYSCALL_FLAG\n        \"syscall\"\n      elsif @running\n        \"running\"\n      else\n        \"shutdown\"\n      end\n    end\n\n    # :nodoc:\n    #\n    # An isolated fiber is locked to its system thread and we expect blocking\n    # syscalls to block the fiber and the thread.\n    def syscall(& : -> U) : U forall U\n      yield\n    end\n\n    protected def enter_syscall : UInt32\n      @syscall.lazy_set(SYSCALL_FLAG)\n    end\n\n    protected def leave_syscall?(value : UInt32) : Bool\n      @syscall.lazy_set(0_u32)\n      true\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context/monitor.cr",
    "content": "module Fiber::ExecutionContext\n  # :nodoc:\n  class Monitor\n    DEFAULT_EVERY              = 10.milliseconds\n    INCREASE_PARALLELISM_EVERY = 100.milliseconds\n    COLLECT_STACKS_EVERY       = 5.seconds\n\n    @thread : Thread?\n\n    def initialize(@every = DEFAULT_EVERY)\n      @collect_stacks_next = Crystal::System::Time.instant + COLLECT_STACKS_EVERY\n      @increase_parallelism_next = Crystal::System::Time.instant + INCREASE_PARALLELISM_EVERY\n      @thread = Thread.new(name: \"SYSMON\") { run_loop }\n    end\n\n    # TODO: maybe yield (ST/MT): detect schedulers that have been stuck running\n    # the same fiber since the previous iteration (check current fiber &\n    # scheduler tick to avoid ABA issues), then mark the fiber to trigger a\n    # cooperative yield, for example, `Fiber.maybe_yield` could be called at\n    # potential cancellation points that would otherwise not need to block now\n    # (IO, mutexes, schedulers, manually called in loops, ...); this could lead\n    # fiber execution time be more fair, and we could also warn when a fiber has\n    # been asked to yield but still hasn't after N iterations.\n    #\n    # TODO: event loop starvation: if an execution context didn't have the\n    # opportunity to run its event-loop since N iterations, then the monitor\n    # thread could run it; it would avoid a set of fibers to always resume\n    # themselves at the expense of pending events.\n    #\n    # TODO: run GC collections on \"low\" application activity? when we don't\n    # allocate the GC won't try to collect memory by itself, which after a peak\n    # usage can lead to keep memory allocated when it could be released to the\n    # OS.\n    private def run_loop : Nil\n      every do |now|\n        transfer_schedulers_blocked_on_syscall\n        increase_parallelism(now)\n        collect_stacks(now)\n      end\n    end\n\n    # Executes the block at exact intervals (depending on the OS scheduler\n    # precision and overall OS load), without counting the time to execute the\n    # block.\n    private def every(&)\n      remaining = @every\n\n      loop do\n        Thread.sleep(remaining)\n\n        start = Crystal::System::Time.instant\n        yield(start)\n        stop = Crystal::System::Time.instant\n\n        # calculate remaining time for more steady wakeups (minimize exponential\n        # delays)\n        remaining = (start + @every - stop).clamp(Time::Span.zero..)\n      rescue exception\n        Crystal.print_error_buffered(\"BUG: %s#every crashed\", self.class.name, exception: exception)\n      end\n    end\n\n    # Iterates each ExecutionContext::Scheduler and transfers the Scheduler for\n    # any Thread currently blocked on a syscall.\n    #\n    # OPTIMIZE: a scheduler in a MT context might not need to be transferred if\n    # its queue is empty and another scheduler in the context is blocked on the\n    # event loop.\n    private def transfer_schedulers_blocked_on_syscall : Nil\n      ExecutionContext.each do |execution_context|\n        execution_context.each_scheduler do |scheduler|\n          next unless scheduler.detach_syscall?\n\n          Crystal.trace :sched, \"reassociate\",\n            scheduler: scheduler,\n            syscall: scheduler.thread.current_fiber\n\n          pool = ExecutionContext.thread_pool\n          pool.detach(scheduler.thread)\n          pool.checkout(scheduler)\n        end\n      end\n    end\n\n    # Iterates parallel execution contexts and increases their parallelism when\n    # there are pending fibers waiting to run.\n    #\n    # At most, the parallelism will double on each iteration. This is to avoid\n    # waking too many schedulers at once which can waste resources to wake the\n    # schedulers that might just fight to dequeue/steal fibers instead of\n    # running them.\n    private def increase_parallelism(now) : Nil\n      return unless @increase_parallelism_next <= now\n      @increase_parallelism_next = now + INCREASE_PARALLELISM_EVERY\n\n      ExecutionContext.each do |execution_context|\n        next unless execution_context.is_a?(Parallel)\n        next if (capacity = execution_context.capacity) == 1\n\n        # how many schedulers are active (running, spinning)?\n        active = execution_context.size\n        return if active == 0\n\n        # how many schedulers can be woken?\n        available = capacity - active\n        return if available == 0\n\n        # don't wake more schedulers than currently active (scale active\n        # parallelism to a factor of 2 max)\n        available = active if available > active\n\n        # fibers from the global queue are divided evenly across active\n        # schedulers, so wake as many schedulers as needed\n        count = execution_context.@global_queue.size\n\n        if count < available\n          # split work: wake one scheduler for every scheduler with fibers in\n          # its local queue\n          execution_context.each_scheduler do |scheduler|\n            count += 1 unless scheduler.@runnables.empty?\n            break if count == available\n          end\n        else\n          count = available\n        end\n\n        execution_context.wake_scheduler(count)\n      end\n    end\n\n    # Iterates each execution context and collects unused fiber stacks.\n    #\n    # OPTIMIZE: should maybe happen during GC collections (?)\n    private def collect_stacks(now)\n      return unless @collect_stacks_next <= now\n      @collect_stacks_next = now + COLLECT_STACKS_EVERY\n\n      Crystal.trace :sched, \"collect_stacks\" do\n        ExecutionContext.each(&.stack_pool?.try(&.collect))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context/parallel/scheduler.cr",
    "content": "require \"crystal/pointer_linked_list\"\nrequire \"../scheduler\"\nrequire \"../runnables\"\n\nmodule Fiber::ExecutionContext\n  class Parallel\n    # Individual scheduler for the parallel execution context.\n    #\n    # The execution context itself doesn't run the fibers. The fibers actually\n    # run in the schedulers. Each scheduler in the context increases the\n    # parallelism by one. For example a parallel context with 8 schedulers means\n    # that a maximum of 8 fibers may run at the same time in different system\n    # threads.\n    class Scheduler\n      include ExecutionContext::Scheduler\n\n      getter name : String\n\n      # :nodoc:\n      property execution_context : Parallel\n      protected property! thread : Thread\n      protected property main_fiber : Fiber\n\n      @global_queue : GlobalQueue\n      @runnables : Runnables(256)\n      @event_loop : Crystal::EventLoop\n\n      @tick : UInt32 = 0\n      @spinning = false\n      @waiting = false\n      @parked = false\n      @shutdown = false\n\n      protected def initialize(@execution_context, @name)\n        @global_queue = @execution_context.global_queue\n        @runnables = Runnables(256).new(@global_queue)\n        @event_loop = @execution_context.event_loop\n        @main_fiber = Fiber.new(\"#{@name}:loop\", @execution_context) { run_loop }\n      end\n\n      protected def shutdown! : Nil\n        @shutdown = true\n      end\n\n      # :nodoc:\n      def spawn(*, name : String? = nil, same_thread : Bool, &block : ->) : Fiber\n        raise RuntimeError.new(\"#{self.class.name}#spawn doesn't support same_thread:true\") if same_thread\n        self.spawn(name: name, &block)\n      end\n\n      # Unlike `Parallel#enqueue` this method is only safe to call on\n      # `ExecutionContext.current` which should always be the case, since cross\n      # context enqueues must call `Parallel#enqueue` through `Fiber#enqueue`.\n      protected def enqueue(fiber : Fiber) : Nil\n        Crystal.trace :sched, \"enqueue\", fiber: fiber\n        @runnables.push(fiber)\n      end\n\n      protected def reschedule : Nil\n        Crystal.trace :sched, \"reschedule\"\n        if fiber = quick_dequeue?\n          resume fiber unless fiber == thread.current_fiber\n        else\n          # nothing to do: switch back to the main loop to spin/wait/park\n          resume main_fiber\n        end\n      end\n\n      protected def resume(fiber : Fiber) : Nil\n        Crystal.trace :sched, \"resume\", fiber: fiber\n\n        # in a multithreaded environment the fiber may be dequeued before its\n        # running context has been saved on the stack (thread A tries to resume\n        # fiber but thread B didn't saved its context yet); we must wait until\n        # the context switch assembly saved all registers on the stack and set\n        # the fiber as resumable.\n        attempts = 0\n\n        until fiber.resumable?\n          if fiber.dead?\n            raise \"BUG: tried to resume dead fiber #{fiber} (#{inspect})\"\n          end\n\n          # OPTIMIZE: if the thread saving the fiber context has been preempted,\n          # this will block the current thread from progressing... shall we\n          # abort and reenqueue the fiber after MAX attempts?\n          attempts = Thread.delay(attempts)\n        end\n\n        swapcontext(fiber)\n      end\n\n      private def quick_dequeue? : Fiber?\n        return if @shutdown\n\n        # every once in a while: dequeue from global queue to avoid two fibers\n        # constantly respawing each other to completely occupy the local queue\n        if (@tick &+= 1) % 61 == 0\n          if fiber = @global_queue.pop?\n            return fiber\n          end\n        end\n\n        # dequeue from local queue\n        if fiber = @runnables.shift?\n          return fiber\n        end\n\n        # the following dequeues ain't so quick and will block the current fiber\n        # (may have already been stolen and waiting for resumable), but that's\n        # not a problem with only one scheduler, so let's spare a switch to the\n        # run loop\n        if @execution_context.capacity == 1\n          # try to refill local queue\n          if fiber = @global_queue.grab?(@runnables, divisor: @execution_context.size)\n            return fiber\n          end\n\n          # run the event loop to see if any event is activable\n          list = Fiber::List.new\n          if @event_loop.lock? { @event_loop.run(pointerof(list), blocking: false) }\n            return enqueue_many(pointerof(list))\n          end\n        end\n      end\n\n      protected def run_loop : Nil\n        Crystal.trace :sched, \"started\"\n\n        loop do\n          if @shutdown\n            spin_stop\n            @runnables.drain\n\n            # we may have been the last running scheduler, waiting on the event\n            # loop while there are pending events for example; let's resume a\n            # scheduler to take our place\n            @execution_context.wake_scheduler\n\n            Crystal.trace :sched, \"shutdown\"\n            break\n          end\n\n          if fiber = find_next_runnable\n            spin_stop\n            resume fiber\n          else\n            # the event loop enqueued a fiber (or was interrupted) or the\n            # scheduler was unparked: go for the next iteration\n          end\n        rescue exception\n          Crystal.print_error_buffered(\"BUG: %s#run_loop [%s] crashed\",\n            self.class.name, @name, exception: exception)\n        end\n      ensure\n        @event_loop.unregister(self)\n      end\n\n      private def find_next_runnable : Fiber?\n        find_next_runnable do |fiber|\n          return fiber if fiber\n        end\n      end\n\n      private def find_next_runnable(&) : Nil\n        list = Fiber::List.new\n\n        # nothing to do: start spinning\n        spinning do\n          return if @shutdown\n\n          # usually empty but the scheduler may have been transferred to another\n          # thread with queued fibers\n          yield @runnables.shift?\n\n          yield @global_queue.grab?(@runnables, divisor: @execution_context.size)\n\n          if @event_loop.lock? { @event_loop.run(pointerof(list), blocking: false) }\n            unless list.empty?\n              # must stop spinning before calling enqueue_many that may call\n              # wake_scheduler which returns immediately if a thread is\n              # spinning... but we're spinning, so that would always fail to\n              # wake sleeping schedulers despite having runnable fibers\n              spin_stop\n              yield enqueue_many(pointerof(list))\n            end\n          end\n\n          yield try_steal?\n        end\n\n        # wait on the event loop for events and timers to activate\n        evloop_ran = @event_loop.lock? do\n          @waiting = true\n\n          # there is a time window between stop spinning and start waiting\n          # during which another context may have enqueued a fiber, check again\n          # before blocking on the event loop to avoid missing a runnable fiber,\n          # which may block for a long time:\n          yield @global_queue.grab?(@runnables, divisor: @execution_context.size)\n\n          # block on the event loop until an event is ready or the loop is\n          # interrupted\n          @event_loop.run(pointerof(list), blocking: true)\n        ensure\n          @waiting = false\n        end\n\n        if evloop_ran\n          yield enqueue_many(pointerof(list))\n\n          # the event loop was interrupted: restart the loop\n          return\n        end\n\n        # no runnable fiber and another thread is already running the event\n        # loop: park the thread until another scheduler or another context\n        # enqueues a fiber\n        @execution_context.park_thread do\n          # don't park the thread when told to shutdown\n          return if @shutdown\n\n          # by the time we acquire the lock, another thread may have enqueued\n          # fiber(s) and already tried to wakeup a thread (race) so we must\n          # check again; we don't check the scheduler's local queue (it's empty)\n          yield @global_queue.unsafe_grab?(@runnables, divisor: @execution_context.size)\n          yield try_steal?\n\n          @parked = true\n          nil\n        end\n        @parked = false\n\n        # immediately mark the scheduler as spinning (we just unparked); we\n        # don't increment the number of spinning threads since\n        # `Parallel#wake_scheduler` already did\n        @spinning = true\n      end\n\n      private def enqueue_many(list : Fiber::List*) : Fiber?\n        if fiber = list.value.pop?\n          Crystal.trace :sched, \"enqueue\", size: list.value.size, fiber: fiber\n          @runnables.bulk_push(list) unless list.value.empty?\n          fiber\n        end\n      end\n\n      # This method always runs in parallel!\n      private def try_steal? : Fiber?\n        @execution_context.steal do |other|\n          if other == self\n            # no need to steal from ourselves\n            next\n          end\n\n          if fiber = @runnables.steal_from(other.@runnables)\n            Crystal.trace :sched, \"stole\", from: other, size: @runnables.size, fiber: fiber\n            return fiber\n          end\n        end\n      end\n\n      # OPTIMIZE: skip spinning if there are enough threads spinning already\n      private def spinning(&)\n        spin_start\n\n        4.times do |attempt|\n          Thread.yield unless attempt == 0\n          yield\n        end\n\n        spin_stop\n      end\n\n      private def spin_start : Nil\n        return if @spinning\n\n        @spinning = true\n        @execution_context.@spinning.add(1, :acquire_release)\n      end\n\n      private def spin_stop : Nil\n        return unless @spinning\n\n        @execution_context.@spinning.sub(1, :acquire_release)\n        @spinning = false\n      end\n\n      def inspect(io : IO) : Nil\n        to_s(io)\n      end\n\n      def to_s(io : IO) : Nil\n        io << \"#<\" << self.class.name << \":0x\"\n        object_id.to_s(io, 16)\n        io << ' ' << @name << '>'\n      end\n\n      protected def active? : Bool\n        !(@waiting || @parked || @syscall.lazy_get.bits_set?(SYSCALL_FLAG))\n      end\n\n      def status : String\n        if @spinning\n          \"spinning\"\n        elsif @waiting\n          \"event-loop\"\n        elsif @parked\n          \"parked\"\n        elsif @syscall.get(:relaxed).bits_set?(SYSCALL_FLAG)\n          \"syscall\"\n        else\n          \"running\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context/parallel.cr",
    "content": "require \"./global_queue\"\nrequire \"./parallel/scheduler\"\n\nmodule Fiber::ExecutionContext\n  # Parallel execution context.\n  #\n  # Fibers running in the same context run both concurrently and in parallel to\n  # each others, in addition to the other fibers running in other execution\n  # contexts.\n  #\n  # The context internally keeps a number of fiber schedulers, each scheduler\n  # runs on a system thread, so multiple schedulers can run in parallel. The\n  # fibers are resumable by any scheduler in the context, and can thus move from\n  # one system thread to another at any time.\n  #\n  # The actual parallelism is dynamic. As the need for parallelism increases,\n  # for example more fibers running longer, the more schedulers will start (and\n  # thus system threads), as the need decreases, for example not enough fibers,\n  # the schedulers will pause themselves and parallelism will decrease.\n  #\n  # The parallelism can be as low as 1, in which case the context becomes a\n  # concurrent context (no parallelism) until resized.\n  #\n  # For example: we can start a parallel context to run consumer fibers, while\n  # the default context produces values. Because the consumer fibers can run in\n  # parallel, we must protect accesses to the shared *value* variable. Running\n  # the example without `Atomic#add` would produce a different result every\n  # time!\n  #\n  # ```\n  # require \"wait_group\"\n  #\n  # consumers = Fiber::ExecutionContext::Parallel.new(\"consumers\", 8)\n  # channel = Channel(Int32).new(64)\n  # wg = WaitGroup.new(32)\n  #\n  # result = Atomic.new(0)\n  #\n  # 32.times do\n  #   consumers.spawn do\n  #     while value = channel.receive?\n  #       result.add(value)\n  #     end\n  #   ensure\n  #     wg.done\n  #   end\n  # end\n  #\n  # 1024.times { |i| channel.send(i) }\n  # channel.close\n  #\n  # # wait for all workers to be done\n  # wg.wait\n  #\n  # p result.get # => 523776\n  # ```\n  #\n  # NOTE: The `Parallel` execution context isn't tied to a fixed set of system\n  # threads, and execution can switch to other system threads, for example when\n  # a fiber is blocked on a syscall.\n  class Parallel\n    include ExecutionContext\n\n    getter name : String\n\n    @mutex : Thread::Mutex\n    @condition : Thread::ConditionVariable\n    protected getter global_queue : GlobalQueue\n\n    # :nodoc:\n    getter stack_pool : Fiber::StackPool = Fiber::StackPool.new\n\n    # :nodoc:\n    getter event_loop : Crystal::EventLoop\n    @event_loop_lock = Atomic(Bool).new(false)\n\n    @parked = Atomic(Int32).new(0)\n    @spinning = Atomic(Int32).new(0)\n\n    # :nodoc:\n    #\n    # Starts the default execution context. There can be only one for the whole\n    # process. Must be called from the main thread's main fiber; associates the\n    # current thread and fiber to the created execution context.\n    protected def self.default(maximum : Int32) : self\n      Fiber.current.execution_context = new(\"DEFAULT\", maximum, hijack: true)\n    end\n\n    # Starts a `Parallel` context with a *maximum* parallelism. The context\n    # starts with an initial parallelism of zero. It will grow to one when a\n    # fiber is spawned, then the actual parallelism will keep increasing and\n    # decreasing as needed, but will never go past the configured *maximum*.\n    def self.new(name : String, maximum : Int32) : self\n      new(name, maximum, hijack: false)\n    end\n\n    @[Deprecated(\"Use Fiber::ExecutionContext::Parallel.new(String, Int32) instead.\")]\n    def self.new(name : String, size : Range(Nil, Int32)) : self\n      new(name, size.exclusive? ? size.end - 1 : size.end, hijack: false)\n    end\n\n    @[Deprecated(\"Use Fiber::ExecutionContext::Parallel.new(String, Int32) instead.\")]\n    def self.new(name : String, size : Range(Int32, Int32)) : self\n      raise ArgumentError.new(\"invalid range\") if size.begin > size.end\n      new(name, size.exclusive? ? size.end - 1 : size.end, hijack: false)\n    end\n\n    protected def initialize(@name : String, capacity : Int32, hijack : Bool)\n      raise ArgumentError.new(\"Parallelism can't be less than one.\") if capacity < 1\n\n      @mutex = Thread::Mutex.new\n      @condition = Thread::ConditionVariable.new\n\n      @global_queue = GlobalQueue.new(@mutex)\n      @schedulers = Array(Scheduler).new(capacity)\n      @event_loop = Crystal::EventLoop.create(capacity)\n      @started = hijack ? 1 : 0\n      @rng = Random::PCG32.new\n\n      start_schedulers(capacity)\n      hijack_current_thread(@schedulers.first) if hijack\n\n      ExecutionContext.execution_contexts.push(self)\n    end\n\n    # :nodoc:\n    #\n    # TODO: must report how many schedulers are running (count spinning\n    # schedulers but don't count waiting/parked ones).\n    def size : Int32\n      count = 0\n      @schedulers.each_with_index do |scheduler, index|\n        break if index == @started\n        count += 1 if scheduler.active?\n      end\n      count\n    end\n\n    # The maximum number of schedulers that can be started, aka how many fibers\n    # can run in parallel or maximum parallelism of the context.\n    def capacity : Int32\n      @schedulers.size\n    end\n\n    # :nodoc:\n    def stack_pool? : Fiber::StackPool?\n      @stack_pool\n    end\n\n    # Starts all schedulers at once.\n    #\n    # We could lazily initialize them as needed, would be safe as long as we\n    # only mutate when the mutex is locked, but we iterate the schedulers in\n    # #steal without locking the mutex (for obvious reasons) and there are no\n    # guarantees that the new schedulers.@size will be written after the\n    # scheduler has been written to the array's buffer.\n    #\n    # OPTIMIZE: consider storing schedulers to an array-like object that would\n    # use an atomic/fence to make sure that @size can only be incremented\n    # *after* the value has been written to @buffer.\n    private def start_schedulers(capacity)\n      capacity.times { |index| @schedulers << start_scheduler(index) }\n    end\n\n    private def start_scheduler(index)\n      scheduler = Scheduler.new(self, \"#{@name}-#{index}\")\n      @event_loop.register(scheduler, index)\n      scheduler\n    end\n\n    # Attaches *scheduler* to the current `Thread`, usually the process' main\n    # thread. Starts a `Fiber` to run the scheduler loop.\n    private def hijack_current_thread(scheduler) : Nil\n      thread = Thread.current\n      thread.internal_name = scheduler.name\n      thread.execution_context = self\n      thread.scheduler = scheduler\n      scheduler.thread = thread\n    end\n\n    # Starts a new `Thread` and attaches *scheduler*. Runs the scheduler loop\n    # directly in the thread's main `Fiber`.\n    private def start_thread(scheduler) : Nil\n      ExecutionContext.thread_pool.checkout(scheduler)\n    end\n\n    protected def each_scheduler(& : Scheduler ->) : Nil\n      @schedulers.each { |scheduler| yield scheduler }\n    end\n\n    # Resizes the context to the new *maximum* parallelism.\n    #\n    # The new *maximum* can grow, in which case more schedulers are created to\n    # eventually increase the parallelism.\n    #\n    # The new *maximum* can also shrink, in which case the overflow schedulers\n    # are removed and told to shutdown immediately. The actual shutdown is\n    # cooperative, so running schedulers won't stop until their current fiber\n    # tries to switch to another fiber.\n    def resize(maximum : Int32) : Nil\n      raise ArgumentError.new(\"Parallelism can't be less than one.\") if maximum < 1\n      removed_schedulers = nil\n\n      @mutex.synchronize do\n        # can run in parallel to #steal that dereferences @schedulers (once)\n        # without locking the mutex, so we dup the schedulers, mutate the copy,\n        # and eventually assign the copy as @schedulers; this way #steal can\n        # safely access the array (never mutated).\n        new_capacity = maximum\n        old_schedulers = @schedulers\n        old_capacity = capacity\n\n        if new_capacity > old_capacity\n          @schedulers = Array(Scheduler).new(new_capacity) do |index|\n            old_schedulers[index]? || start_scheduler(index)\n          end\n        elsif new_capacity < old_capacity\n          # tell the overflow schedulers to shutdown\n          removed_schedulers = old_schedulers[new_capacity..]\n          removed_schedulers.each(&.shutdown!)\n\n          # resize\n          @schedulers = old_schedulers[0...new_capacity]\n          @started = new_capacity if @started > new_capacity\n\n          # reset @parked counter (we wake all parked threads) so they can\n          # shutdown (if told to):\n          woken_threads = @parked.get(:relaxed)\n          @parked.set(0, :relaxed)\n\n          # update @spinning prior to unpark threads; we use acquire release\n          # semantics to make sure that all the above stores are visible before\n          # the following wakeup calls (maybe not needed, but let's err on the\n          # safe side)\n          @spinning.add(woken_threads, :acquire_release)\n\n          # wake every waiting thread:\n          @condition.broadcast\n          @event_loop.interrupt\n        end\n      end\n\n      return unless removed_schedulers\n\n      # drain the local queues of removed schedulers since they're no longer\n      # available for stealing\n      removed_schedulers.each do |scheduler|\n        scheduler.@runnables.drain\n      end\n    end\n\n    # :nodoc:\n    def spawn(*, name : String? = nil, same_thread : Bool, &block : ->) : Fiber\n      raise ArgumentError.new(\"#{self.class.name}#spawn doesn't support same_thread:true\") if same_thread\n      self.spawn(name: name, &block)\n    end\n\n    # :nodoc:\n    def enqueue(fiber : Fiber) : Nil\n      if ExecutionContext.current? == self\n        # local enqueue: push to local queue of current scheduler\n        ExecutionContext::Scheduler.current.enqueue(fiber)\n      else\n        # cross context: push to global queue\n        external_enqueue(fiber)\n      end\n    end\n\n    # :nodoc:\n    def external_enqueue(fiber : Fiber) : Nil\n      Crystal.trace :sched, \"enqueue\", fiber: fiber, to_context: self\n      @global_queue.push(fiber)\n\n      # always try to wake a scheduler on external enqueues, they may all be\n      # parked/waiting on evloop:\n      wake_scheduler\n    end\n\n    # Picks a scheduler at random then iterates all schedulers to try to steal\n    # fibers from.\n    protected def steal(& : Scheduler ->) : Nil\n      return if capacity == 1\n\n      schedulers = @schedulers\n      i = @rng.next_int\n      n = schedulers.size\n\n      n.times do |j|\n        if scheduler = schedulers[(i &+ j) % n]?\n          yield scheduler\n        end\n      end\n    end\n\n    protected def park_thread(&) : Fiber?\n      @mutex.synchronize do\n        # avoid races by checking queues again\n        if fiber = yield\n          return fiber\n        end\n\n        Crystal.trace :sched, \"park\"\n        @parked.add(1, :acquire_release)\n\n        # NOTE: we could detach the scheduler and return the thread back into\n        # ThreadPool... but it would need synchronization with #wake_scheduler\n        # that wouldn't only start a thread for the next scheduler (@started)\n        # but have to pick a non running scheduler\n        @condition.wait(@mutex)\n\n        # we don't decrement @parked because #wake_scheduler did\n        Crystal.trace :sched, \"wakeup\"\n      end\n\n      nil\n    end\n\n    # This method always runs in parallel!\n    #\n    # This can be called from any thread in the context but can also be called\n    # from external execution contexts, in which case the context may have its\n    # last thread about to park itself, and we must prevent the last thread from\n    # parking when there is a parallel cross context enqueue!\n    protected def wake_scheduler(count = 1) : Nil\n      # another thread is spinning: nothing to do\n      count -= @spinning.get(:relaxed)\n      return if count < 1\n\n      # interrupt a thread waiting on the event loop\n      # FIXME: what if every scheduler can wait on evloop? (i.e. io_uring)\n      if @event_loop.interrupt?\n        count -= 1\n        return if count == 0\n      end\n\n      # we can check @parked without locking the mutex because we can't push to\n      # the global queue _and_ park the thread at the same time, so either the\n      # thread is already parked (and we must awake it) or it noticed (or will\n      # notice) the fiber in the global queue;\n      #\n      # we still rely on an atomic to make sure the actual value is visible by\n      # the current thread\n      if @parked.get(:acquire) > 0\n        @mutex.synchronize do\n          # avoid race conditions\n          parked = @parked.get(:relaxed)\n          spinning = @spinning.get(:relaxed)\n\n          if parked > 0 && spinning < count\n            wake = count > parked ? parked : count\n\n            # increase the number of spinning threads _now_ to avoid multiple\n            # threads from trying to wakeup multiple threads at the same time\n            #\n            # we must also decrement the number of parked threads because another\n            # thread could lock the mutex and increment @spinning again before the\n            # signaled thread is resumed\n            @spinning.add(wake, :acquire_release)\n            @parked.sub(wake, :acquire_release)\n\n            if wake == parked\n              @condition.broadcast\n            else\n              wake.times { @condition.signal }\n            end\n\n            count -= wake\n          end\n        end\n        return if count == 0\n      end\n\n      # check if we can start another scheduler; no need for atomics, the value\n      # shall be rather stable over time and we check again inside the mutex\n      return if @started >= capacity\n\n      @mutex.synchronize do\n        start = @started\n        limit = (start + count).clamp(..capacity)\n\n        (start...limit).each do |index|\n          start_thread(@schedulers[index])\n          @started += 1\n        end\n      end\n    end\n\n    @[AlwaysInline]\n    def inspect(io : IO) : Nil\n      to_s(io)\n    end\n\n    def to_s(io : IO) : Nil\n      io << \"#<\" << self.class.name << \":0x\"\n      object_id.to_s(io, 16)\n      io << ' ' << name << '>'\n    end\n  end\n\n  @[Deprecated(\"Use Fiber::ExecutionContext::Parallel instead.\")]\n  alias MultiThreaded = Parallel\nend\n"
  },
  {
    "path": "src/fiber/execution_context/runnables.cr",
    "content": "# The queue is a port of Go's `runq*` functions, distributed under a BSD-like\n# license: <https://cs.opensource.google/go/go/+/release-branch.go1.23:LICENSE>\n#\n# The queue derives from the chase-lev lock-free queue with adaptations:\n#\n# - single ring buffer (per scheduler);\n# - on overflow: bulk push half the ring to `GlobalQueue`;\n# - on empty: bulk grab up to half the ring from `GlobalQueue`;\n# - bulk push operation;\n\nrequire \"../list\"\nrequire \"./global_queue\"\n\nmodule Fiber::ExecutionContext\n  # :nodoc:\n  #\n  # Local queue or runnable fibers for schedulers.\n  # Bounded.\n  # First-in, first-out semantics (FIFO).\n  # Single producer, multiple consumers thread safety.\n  #\n  # Private to an execution context scheduler, except for stealing methods that\n  # can be called from any thread in the execution context.\n  class Runnables(N)\n    def initialize(@global_queue : GlobalQueue)\n      # head is an index to the buffer where the next fiber to dequeue is.\n      #\n      # tail is an index to the buffer where the next fiber to enqueue will be\n      # (on the next push).\n      #\n      # head is always behind tail (not empty) or equal (empty) but never after\n      # tail (the queue would have a negative size => bug).\n      @head = Atomic(UInt32).new(0)\n      @tail = Atomic(UInt32).new(0)\n      @buffer = uninitialized Fiber[N]\n    end\n\n    @[AlwaysInline]\n    def capacity : Int32\n      N\n    end\n\n    # Tries to push fiber on the local runnable queue. If the run queue is full,\n    # pushes fiber on the global queue, which will grab the global lock.\n    #\n    # Executed only by the owner.\n    def push(fiber : Fiber) : Nil\n      # ported from Go: runqput\n      loop do\n        head = @head.get(:acquire) # sync with consumers\n        tail = @tail.get(:relaxed)\n\n        if (tail &- head) < N\n          # put fiber to local queue\n          @buffer.to_unsafe[tail % N] = fiber\n\n          # make the fiber available for consumption\n          @tail.set(tail &+ 1, :release)\n          return\n        end\n\n        if push_slow(fiber, head, tail)\n          return\n        end\n\n        # failed to advance head (another scheduler stole fibers),\n        # the queue isn't full, now the push above must succeed\n      end\n    end\n\n    private def push_slow(fiber : Fiber, head : UInt32, tail : UInt32) : Bool\n      # ported from Go: runqputslow\n      n = (tail &- head) // 2\n      raise \"BUG: queue is not full\" if n != N // 2\n\n      # first, try to grab half of the fibers from local queue\n      batch = uninitialized Fiber[N] # actually N // 2 + 1 but that doesn't compile\n      _, success = try_grab(batch.to_unsafe, head, n)\n      return false unless success\n\n      # append fiber to the batch and push to global queue\n      batch.to_unsafe[n] = fiber\n      push_to_global_queue(batch.to_unsafe, n &+ 1)\n      true\n    end\n\n    # Transfers every fiber in the local runnables queue to the global queue.\n    # This will grab the global lock.\n    #\n    # Can be executed by any scheduler.\n    def drain : Nil\n      batch = uninitialized Fiber[N]\n      n = 0\n\n      head = @head.get(:acquire) # sync with other consumers\n      loop do\n        tail = @tail.get(:acquire) # sync with the producer\n\n        n = (tail &- head)\n        return if n == 0 # queue is empty\n\n        # try to grab everything from local queue\n        head, success = try_grab(batch.to_unsafe, head, n)\n        break if success\n      end\n\n      push_to_global_queue(batch.to_unsafe, n)\n    end\n\n    private def try_grab(batch, head, n)\n      n.times do |i|\n        batch[i] = @buffer.to_unsafe[(head &+ i) % N]\n      end\n      @head.compare_and_set(head, head &+ n, :acquire_release, :acquire)\n    end\n\n    private def push_to_global_queue(batch, n)\n      # link the fibers\n      (n &- 1).times do |i|\n        batch[i].list_next = batch[i &+ 1]\n      end\n      list = Fiber::List.new(batch[0], batch[n &- 1], size: n.to_i32)\n\n      # and put the batch on global queue (grabs the global lock)\n      @global_queue.bulk_push(pointerof(list))\n    end\n\n    # Tries to enqueue all the fibers in *list* into the local queue. If the\n    # local queue is full, the overflow will be pushed to the global queue; in\n    # that case this will temporarily acquire the global queue lock.\n    #\n    # Executed only by the owner.\n    def bulk_push(list : Fiber::List*) : Nil\n      # ported from Go: runqputbatch\n      head = @head.get(:acquire) # sync with other consumers\n      tail = @tail.get(:relaxed)\n\n      while !list.value.empty? && (tail &- head) < N\n        fiber = list.value.pop\n        @buffer.to_unsafe[tail % N] = fiber\n        tail &+= 1\n      end\n\n      # make the fibers available for consumption\n      @tail.set(tail, :release)\n\n      # put any overflow on global queue\n      @global_queue.bulk_push(list) unless list.value.empty?\n    end\n\n    # Dequeues the next runnable fiber from the local queue.\n    #\n    # Executed only by the owner.\n    def shift? : Fiber?\n      # ported from Go: runqget\n\n      head = @head.get(:acquire) # sync with other consumers\n      loop do\n        tail = @tail.get(:relaxed)\n        return if tail == head\n\n        fiber = @buffer.to_unsafe[head % N]\n        head, success = @head.compare_and_set(head, head &+ 1, :acquire_release, :acquire)\n        return fiber if success\n      end\n    end\n\n    # Steals half the fibers from the local queue of `src` and puts them onto\n    # the local queue. Returns one of the stolen fibers, or `nil` on failure.\n    #\n    # Executed only by the owner (when the local queue is empty).\n    def steal_from(src : Runnables(N)) : Fiber?\n      # ported from Go: runqsteal\n\n      tail = @tail.get(:relaxed)\n      n = src.grab(@buffer.to_unsafe, tail)\n      return if n == 0\n\n      # 'dequeue' last fiber from @buffer\n      n &-= 1\n      fiber = @buffer.to_unsafe[(tail &+ n) % N]\n      return fiber if n == 0\n\n      head = @head.get(:acquire) # sync with consumers\n      if tail &- head &+ n >= N\n        raise \"BUG: local queue overflow\"\n      end\n\n      # make the fibers available for consumption\n      @tail.set(tail &+ n, :release)\n\n      fiber\n    end\n\n    # Grabs a batch of fibers from local queue into `buffer` of size N (normally\n    # the ring buffer of another `Runnables`) starting at `buffer_head`. Returns\n    # number of grabbed fibers.\n    #\n    # Can be executed by any scheduler.\n    protected def grab(buffer : Fiber*, buffer_head : UInt32) : UInt32\n      # ported from Go: runqgrab\n\n      head = @head.get(:acquire) # sync with other consumers\n      loop do\n        tail = @tail.get(:acquire) # sync with the producer\n\n        n = tail &- head\n        n -= n // 2\n        return 0_u32 if n == 0 # queue is empty\n\n        if n > N // 2\n          # read inconsistent head and tail\n          head = @head.get(:acquire)\n          next\n        end\n\n        n.times do |i|\n          fiber = @buffer.to_unsafe[(head &+ i) % N]\n          buffer[(buffer_head &+ i) % N] = fiber\n        end\n\n        # try to mark the fiber as consumed\n        head, success = @head.compare_and_set(head, head &+ n, :acquire_release, :acquire)\n        return n if success\n      end\n    end\n\n    @[AlwaysInline]\n    def empty? : Bool\n      @head.get(:relaxed) == @tail.get(:relaxed)\n    end\n\n    @[AlwaysInline]\n    def size : UInt32\n      @tail.get(:relaxed) &- @head.get(:relaxed)\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context/scheduler.cr",
    "content": "module Fiber::ExecutionContext\n  # :nodoc:\n  module Scheduler\n    @[AlwaysInline]\n    def self.current : Scheduler\n      Thread.current.scheduler\n    end\n\n    @[AlwaysInline]\n    def self.current? : Scheduler?\n      Thread.current.scheduler?\n    end\n\n    protected abstract def thread : Thread\n    protected abstract def execution_context : ExecutionContext\n\n    # Instantiates a fiber and enqueues it into the scheduler's local queue.\n    def spawn(*, name : String? = nil, &block : ->) : Fiber\n      fiber = Fiber.new(name, execution_context, &block)\n      enqueue(fiber)\n      fiber\n    end\n\n    # Legacy support for the *same_thread* argument. Each execution context may\n    # decide to support it or not (e.g. a single threaded context can accept it).\n    abstract def spawn(*, name : String? = nil, same_thread : Bool, &block : ->) : Fiber\n\n    # Suspends the current fiber and resumes *fiber* instead.\n    # The current fiber will never be resumed; you're responsible to reenqueue it.\n    #\n    # Unsafe. Must only be called on `ExecutionContext.current`. Prefer\n    # `ExecutionContext.enqueue` instead.\n    protected abstract def enqueue(fiber : Fiber) : Nil\n\n    # Suspends the execution of the current fiber and resumes the next runnable\n    # fiber.\n    #\n    # Unsafe. Must only be called on `ExecutionContext.current`. Prefer\n    # `ExecutionContext.reschedule` instead.\n    protected abstract def reschedule : Nil\n\n    # Suspends the execution of the current fiber and resumes *fiber*.\n    #\n    # The current fiber will never be resumed; you're responsible to reenqueue\n    # it.\n    #\n    # Unsafe. Must only be called on `ExecutionContext.current`. Prefer\n    # `ExecutionContext.resume` instead.\n    protected abstract def resume(fiber : Fiber) : Nil\n\n    # Switches the thread from running the current fiber to run *fiber* instead.\n    #\n    # Handles thread safety around fiber stacks by locking the GC, so it won't\n    # start a GC collection while we're switching context.\n    #\n    # Unsafe. Must only be called by the current scheduler. The caller must\n    # ensure that the fiber indeed belongs to the current execution context and\n    # that the fiber can indeed be resumed.\n    #\n    # WARNING: after switching context you can't trust *self* anymore (it is the\n    # scheduler that resumed *fiber* which may not be the one that suspended\n    # *fiber*) or instance variables; local variables however are the ones from\n    # before swapping context.\n    protected def swapcontext(fiber : Fiber) : Nil\n      current_fiber = thread.current_fiber\n\n      GC.lock_read\n      thread.current_fiber = fiber\n      Fiber.swapcontext(pointerof(current_fiber.@context), pointerof(fiber.@context))\n      GC.unlock_read\n    end\n\n    # Returns the current status of the scheduler. For example `\"running\"`,\n    # `\"event-loop\"` or `\"parked\"`.\n    abstract def status : String\n\n    # :nodoc:\n    SYSCALL_FLAG = 1_u32\n\n    # :nodoc:\n    # use to increment the counter to avoid ABA issues\n    SYSCALL_INCREMENT = 2_u32\n\n    @syscall = Atomic(UInt32).new(0_u32)\n\n    # :nodoc:\n    #\n    # Marks the current scheduler as doing a syscall that may block the current\n    # thread for an unknown time. This allows the monitor thread to move the\n    # scheduler to another thread, without waiting for the syscall to complete.\n    #\n    # When the syscall eventually completes, the thread will check if the\n    # scheduler has been detached and either continue running the current fiber\n    # if the scheduler is still attached, or enqueue the current fiber back into\n    # its execution context, then returns itself back into the thread pool.\n    def syscall(& : -> U) : U forall U\n      scheduler = Scheduler.current\n      fiber = Fiber.current\n      value = scheduler.enter_syscall\n\n      ret = yield\n\n      unless scheduler.leave_syscall?(value)\n        # the scheduler has been detached (or is being detached): enqueue the\n        # current fiber back into its execution context, and return the thread\n        # back into the thread pool\n        scheduler.execution_context.external_enqueue(fiber)\n        ExecutionContext.thread_pool.checkin\n      end\n\n      # the scheduler wasn't detached, or the fiber has been resume: we can\n      # continue normally\n      ret\n    end\n\n    protected def enter_syscall : UInt32\n      old_value = @syscall.add(SYSCALL_FLAG | SYSCALL_INCREMENT, :acquire_release)\n      old_value + SYSCALL_FLAG | SYSCALL_INCREMENT\n    end\n\n    protected def leave_syscall?(value : UInt32) : Bool\n      new_value = (value & ~SYSCALL_FLAG) &+ SYSCALL_INCREMENT\n      _, success = @syscall.compare_and_set(value, new_value, :acquire_release, :relaxed)\n      success\n    end\n\n    protected def detach_syscall? : Bool\n      value = @syscall.get(:relaxed)\n      value.bits_set?(SYSCALL_FLAG) && leave_syscall?(value)\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context/thread_pool.cr",
    "content": "class Fiber\n  module ExecutionContext\n    # How long a parked thread will be kept waiting in the thread pool.\n    # Defaults to 5 minutes.\n    class_property thread_keepalive : Time::Span = 5.minutes\n\n    # :nodoc:\n    class ThreadPool\n      # :nodoc:\n      struct Parked\n        include Crystal::PointerLinkedList::Node\n\n        getter thread : Thread\n\n        def initialize(@thread : Thread)\n          @mutex = Thread::Mutex.new\n          @condition_variable = Thread::ConditionVariable.new\n        end\n\n        def synchronize(&)\n          @mutex.synchronize { yield }\n        end\n\n        def wake\n          @condition_variable.signal\n        end\n\n        def wait\n          @condition_variable.wait(@mutex)\n        end\n\n        def wait(timeout, &)\n          @condition_variable.wait(@mutex, timeout) { yield }\n        end\n\n        def linked?\n          !@previous.null?\n        end\n      end\n\n      def initialize\n        @mutex = Thread::Mutex.new\n        @condition_variable = Thread::ConditionVariable.new\n        @pool = Crystal::PointerLinkedList(Parked).new\n        @main_thread = Thread.current\n      end\n\n      protected def checkout(scheduler)\n        thread =\n          if parked = @mutex.synchronize { @pool.shift? }\n            parked.value.synchronize do\n              attach(parked.value.thread, scheduler)\n              parked.value.wake\n            end\n            parked.value.thread\n          else\n            # OPTIMIZE: start thread with minimum stack size\n            Thread.new do |thread|\n              attach(thread, scheduler)\n              enter_thread_loop(thread)\n            end\n          end\n        Crystal.trace :sched, \"thread.checkout\", thread: thread\n        thread\n      end\n\n      protected def attach(thread, scheduler) : Nil\n        thread.execution_context = scheduler.execution_context\n        thread.scheduler = scheduler\n        scheduler.thread = thread\n      end\n\n      protected def detach(thread) : Nil\n        thread.execution_context = nil\n        thread.scheduler = nil\n      end\n\n      protected def checkin : Nil\n        Crystal.trace :sched, \"thread.checkin\"\n        thread = Thread.current\n        detach(thread)\n\n        if thread == @main_thread\n          resume(main_thread_loop)\n        else\n          Thread.name = \"\"\n          resume(thread.main_fiber)\n        end\n      end\n\n      private def main_thread_loop\n        @main_thread_loop ||= begin\n          # OPTIMIZE: allocate minimum stack size\n          pointer = Crystal::System::Fiber.allocate_stack(StackPool::STACK_SIZE, protect: true)\n          stack = Stack.new(pointer, StackPool::STACK_SIZE, reusable: true)\n          Fiber.new(execution_context: ExecutionContext.default) { enter_thread_loop(@main_thread) }\n        end\n      end\n\n      # Each thread has a general loop, which is used to park the thread while\n      # it's in the thread pool. On startup then on wakeup it will resume the\n      # associated scheduler's main fiber, which itself is running the\n      # scheduler's run loop.\n      #\n      # Upon checkout the thread pool will merely resume the thread's main loop,\n      # leaving the scheduler's main fiber available for resume by another\n      # thread if needed, or left dead if the scheduler has shut down (e.g.\n      # isolated context).\n      private def enter_thread_loop(thread)\n        parked = Parked.new(thread)\n        parked.synchronize do\n          loop do\n            if scheduler = thread.scheduler?\n              unless thread == @main_thread\n                Thread.name = scheduler.name\n              end\n\n              resume(scheduler.main_fiber)\n\n              {% unless flag?(:interpreted) %}\n                if (stack = Thread.current.dead_fiber_stack?) && stack.reusable?\n                  # release pending fiber stack left after swapcontext; we don't\n                  # know which stack pool to return it to, and it may not even\n                  # have one (e.g. isolated fiber stack)\n                  Crystal::System::Fiber.free_stack(stack.pointer, stack.size)\n                end\n              {% end %}\n            end\n\n            @mutex.synchronize do\n              @pool.push pointerof(parked)\n            end\n\n            if thread == @main_thread || {% flag?(:win32) && Crystal::EventLoop.has_constant?(:IOCP) %}\n              # never shutdown the main thread: the main fiber is running on its\n              # original stack, terminating the main thread would invalidate the\n              # main fiber stack (oops)\n              #\n              # FIXME: never shutdown a thread on windows: it would abort pending I/O\n              # operations (for example `Socket#accept`) that the thread started\n              parked.wait\n            else\n              parked.wait(ExecutionContext.thread_keepalive) do\n                # reached timeout: try to shutdown thread, but another thread\n                # might dequeue from @pool in parallel: run checks to avoid any\n                # race condition:\n                if !thread.scheduler? && parked.linked?\n                  deleted = false\n\n                  @mutex.synchronize do\n                    if parked.linked?\n                      @pool.delete pointerof(parked)\n                      deleted = true\n                    end\n                  end\n\n                  if deleted\n                    # no attached scheduler and we removed ourselves from the\n                    # pool: we can safely shutdown (no races)\n                    Crystal.trace :sched, \"thread.shutdown\"\n                    return\n                  end\n\n                  # no attached scheduler but another thread removed ourselves\n                  # from the pool and is waiting to acquire parked.mutex to\n                  # handoff a scheduler: unsync so it can progress\n                  parked.wait\n                end\n              end\n            end\n          rescue exception\n            Crystal.trace :sched, \"thread.exception\",\n              class: exception.class.name,\n              message: exception.message\n\n            Crystal.print_error_buffered(\"BUG: %s#enter_thread_loop crashed\",\n              self.class.name, exception: exception)\n          end\n        end\n      end\n\n      private def resume(fiber) : Nil\n        Crystal.trace :sched, \"thread.resume\", fiber: fiber\n\n        # FIXME: duplicates Fiber::ExecutionContext::MultiThreaded::Scheduler#resume:\n        attempts = 0\n        until fiber.resumable?\n          raise \"BUG: tried to resume dead fiber #{fiber} (#{inspect})\" if fiber.dead?\n          attempts = Thread.delay(attempts)\n        end\n\n        # FIXME: duplicates Fiber::ExecutionContext::Scheduler#swapcontext:\n        thread = Thread.current\n        current_fiber = thread.current_fiber\n\n        GC.lock_read\n        thread.current_fiber = fiber\n        Fiber.swapcontext(pointerof(current_fiber.@context), pointerof(fiber.@context))\n        GC.unlock_read\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/execution_context.cr",
    "content": "require \"../crystal/event_loop\"\nrequire \"../crystal/system/thread\"\nrequire \"../crystal/system/thread_linked_list\"\nrequire \"../fiber\"\nrequire \"./stack_pool\"\nrequire \"./execution_context/*\"\n\n{% raise \"ERROR: execution contexts require the `preview_mt` compilation flag\" unless flag?(:preview_mt) || flag?(:docs) %}\n{% raise \"ERROR: execution contexts require the `execution_context` compilation flag\" unless flag?(:execution_context) || flag?(:docs) %}\n\n# An execution context creates and manages a dedicated pool of one or more\n# schedulers where fibers will be running in. Each context manages the rules to\n# run, suspend and swap fibers internally.\n#\n# EXPERIMENTAL: Execution contexts are an experimental feature, implementing\n# [RFC 2](https://github.com/crystal-lang/rfcs/pull/2). It's opt-in and requires\n# the compiler flags `-Dpreview_mt -Dexecution_context`.\n#\n# An execution context groups fibers together. Instead of associating a fiber to\n# a specific system thread, we associate a fiber to an execution context,\n# abstracting which system thread(s) the fibers will run on.\n#\n# Applications can create any number of execution contexts in parallel. Fibers\n# running in any context can communicate and synchronize with any other fiber\n# running in any context through the usual synchronization primitives such as\n# `Channel`, `WaitGroup` or `Sync`.\n#\n# When spawning a fiber with `::spawn`, it spawns into the execution context of\n# the current fiber, so child fibers execute in the same context as their\n# parent, unless told otherwise (see `ExecutionContext#spawn`).\n#\n# Fibers are scoped to the execution context they are spawned into. Once\n# spawned, a fiber cannot _move_ to another execution context, and is always\n# resumed in the same execution context.\n#\n# ## Context types\n#\n# The standard library provides a number of execution context implementations\n# for common use cases.\n#\n# * `ExecutionContext::Concurrent`: Fully concurrent with limited parallelism.\n#\n#   Fibers run concurrently to each other, never in parallel (only one fiber at\n#   a time). They can use simpler and faster synchronization primitives\n#   internally (no atomics, limited thread safety), however communication with\n#   fibers in other contexts must be safe (e.g. `Channel,  `Sync`, ...). A\n#   blocking fiber blocks the entire thread and all other fibers in the context.\n#\n# * `ExecutionContext::Parallel`: Fully concurrent, fully parallel.\n#\n#   Fibers running in this context can be resumed by multiple system threads in\n#   this context. They run concurrently and in parallel to each other (multiple\n#   fibers at a time), in addition to running in parallel to any fibers in other\n#   contexts. Schedulers steal work from each other. The parallelism can grow\n#   and shrink dynamically.\n#\n# * `ExecutionContext::Isolated`: Single fiber in a single system thread without\n#   concurrency.\n#\n#   This is useful for tasks that can block thread execution for a long time\n#   (e.g. CPU heavy computation) or must be reactive (e.g. a GUI or game loop).\n#   The event-loop works normally and so does communication and synchronization\n#   with fibers in other contexts (`Channel`, `WaitGroup`, `Sync`, ...). When\n#   the fiber needs to wait, it pauses the thread.\n#\n# Again, any number of execution contexts can be created (as far as the computer\n# can physically allow). An advantage of starting execution contexts is that it\n# creates execution boundaries, the OS thread scheduler can for example preempt\n# a system thread, allowing fibers in other system threads to run.\n#\n# ## The default execution context\n#\n# The Crystal runtime starts a default execution context exposed as\n# `Fiber::ExecutionContext.default`. This is where the main fiber is running.\n#\n# Its parallelism is set to 1 for backwards compatibility reasons; Crystal used\n# to be single-threaded and concurrent only. You can increase the parallelism at\n# any time using `Parallel#resize`, for example:\n#\n# ```\n# count = Fiber::ExecutionContext.default_workers_count\n# Fiber::ExecutionContext.default.resize(count)\n# ```\n#\n# ## Relationship with system threads\n#\n# Execution contexts control when and how fibers run, and on which system thread\n# they execute. The term *parallelism* is the maximum number of fibers that can\n# run in parallel (maximum number of schedulers) but there can be less or more\n# system threads running in practice, for example when a fiber is blocked on a\n# syscall.\n#\n# There are no guarantees on how a fiber will run on system threads. A fiber can\n# start in thread A, then be resumed and terminated on thread A, B or C. This is\n# true for both the `Parallel` and `Concurrent` contexts.\n#\n# Notable exception: `Isolated` guarantees that its fiber will always run on the\n# same system thread. During its lifetime, the fiber owns the thread, but only\n# for the fiber's lifetime.\n#\n# Threads are kept in a thread pool: threads can be started, attached and\n# detached from any context at any time. Threads can be detached from a context\n# and reattached to the same execution context or to another one (`Concurrent`,\n# `Parallel` or `Isolated`).\n@[Experimental]\nmodule Fiber::ExecutionContext\n  @@thread_pool : ThreadPool?\n  @@default : ExecutionContext::Parallel?\n\n  # :nodoc:\n  def self.thread_pool : ThreadPool\n    @@thread_pool.not_nil!(\"expected thread pool to have been setup\")\n  end\n\n  # Returns the default `ExecutionContext` for the process, automatically\n  # started when the program started.\n  #\n  # The default execution context is currently `Parallel` but only starts with\n  # parallelism set to 1. The parallelism can be changed using\n  # `Parallel#resize`.\n  @[AlwaysInline]\n  def self.default : ExecutionContext::Parallel\n    @@default.not_nil!(\"expected default execution context to have been setup\")\n  end\n\n  # :nodoc:\n  def self.init_default_context : Nil\n    @@thread_pool = ThreadPool.new\n    @@default = Parallel.default(1)\n    @@monitor = Monitor.new\n  end\n\n  # Returns the default maximum parallelism. Can be used to resize the default\n  # parallel execution context for example.\n  #\n  # Respects the `CRYSTAL_WORKERS` environment variable if present and valid,\n  # and otherwise defaults to the number of logical CPUs available to the\n  # process or on the computer.\n  def self.default_workers_count : Int32\n    count = ENV[\"CRYSTAL_WORKERS\"]?.try(&.to_i?) || -1\n    count = Crystal::System.effective_cpu_count.to_i if count == -1\n    count = System.cpu_count.to_i if count == -1\n    # TODO: query for CPU limits (e.g. linux/cgroup, freebsd/rctl, ...)\n\n    # always report at least 1 worker\n    count.clamp(1..)\n  end\n\n  # :nodoc:\n  protected class_getter execution_contexts = Thread::LinkedList(ExecutionContext).new\n\n  # :nodoc:\n  property next : ExecutionContext?\n\n  # :nodoc:\n  property previous : ExecutionContext?\n\n  # :nodoc:\n  def self.unsafe_each(&) : Nil\n    @@execution_contexts.try(&.unsafe_each { |execution_context| yield execution_context })\n  end\n\n  # Iterates all execution contexts.\n  def self.each(&) : Nil\n    execution_contexts.each { |execution_context| yield execution_context }\n  end\n\n  # Returns the `ExecutionContext` the current fiber is running in.\n  @[AlwaysInline]\n  def self.current : ExecutionContext\n    Thread.current.execution_context\n  end\n\n  def self.current? : ExecutionContext?\n    Thread.current.execution_context?\n  end\n\n  # :nodoc:\n  #\n  # Tells the current scheduler to suspend the current fiber and resume the\n  # next runnable fiber. The current fiber will never be resumed; you're\n  # responsible to reenqueue it.\n  #\n  # This method is safe as it only operates on the current `ExecutionContext`\n  # and `Scheduler`.\n  @[AlwaysInline]\n  def self.reschedule : Nil\n    Scheduler.current.reschedule\n  end\n\n  # :nodoc:\n  #\n  # Tells the current scheduler to suspend the current fiber and to resume\n  # *fiber* instead. The current fiber will never be resumed; you're responsible\n  # to reenqueue it.\n  #\n  # Raises `RuntimeError` if the fiber doesn't belong to the current execution\n  # context.\n  #\n  # This method is safe as it only operates on the current `ExecutionContext`\n  # and `Scheduler`.\n  def self.resume(fiber : Fiber) : Nil\n    if fiber.execution_context == current\n      Scheduler.current.resume(fiber)\n    else\n      raise RuntimeError.new(\"Can't resume fiber from #{fiber.execution_context} into #{current}\")\n    end\n  end\n\n  # Creates a new fiber then enqueues it to the execution context.\n  #\n  # May be called from any `ExecutionContext` (i.e. must be thread-safe).\n  def spawn(*, name : String? = nil, &block : ->) : Fiber\n    Fiber.new(name, self, &block).tap { |fiber| enqueue(fiber) }\n  end\n\n  # :nodoc:\n  #\n  # Legacy support for the `same_thread` argument. Each execution context may\n  # decide to support it or not (e.g. a single threaded context can accept it).\n  abstract def spawn(*, name : String? = nil, same_thread : Bool, &block : ->) : Fiber\n\n  # :nodoc:\n  abstract def stack_pool : Fiber::StackPool\n\n  # :nodoc:\n  abstract def stack_pool? : Fiber::StackPool?\n\n  # :nodoc:\n  abstract def event_loop : Crystal::EventLoop\n\n  # :nodoc:\n  #\n  # Enqueues a fiber to be resumed inside the execution context. Implementations\n  # may check if *self* is the current context and assume a local scheduler\n  # enqueue.\n  #\n  # May be called from any ExecutionContext (i.e. must be thread-safe). May also\n  # be called from bare threads (outside of an ExecutionContext).\n  abstract def enqueue(fiber : Fiber) : Nil\n\n  # :nodoc:\n  #\n  # Identical to `#enqueue` but the enqueue musn't check whether *self* is the\n  # current context and always enqueue as if called by an external context or\n  # bare thread.\n  #\n  # Called for example by threads with their scheduler detached (or currently\n  # being detached in parallel) to enqueue the current fiber after a blocking\n  # syscall returned. See `Scheduler#syscall`.\n  abstract def external_enqueue(fiber : Fiber) : Nil\nend\n"
  },
  {
    "path": "src/fiber/list.cr",
    "content": "# The list is modeled after Go's `gQueue`, distributed under a BSD-like\n# license: <https://cs.opensource.google/go/go/+/release-branch.go1.23:LICENSE>\n\nclass Fiber\n  # :nodoc:\n  #\n  # Singly-linked list of `Fiber`.\n  # Last-in, first-out (LIFO) semantic.\n  # A fiber can only exist within a single `List` at any time.\n  #\n  # This list if simpler than `Crystal::PointerLinkedList` which is a doubly\n  # linked list. It's meant to maintain a queue of runnable fibers, or to\n  # quickly collect an arbitrary number of fibers; situations where we don't\n  # need arbitrary deletions from anywhere in the list.\n  #\n  # Thread unsafe! An external lock is required for concurrent accesses.\n  struct List\n    getter size : Int32\n\n    def initialize(@head : Fiber? = nil, @tail : Fiber? = nil, @size = 0)\n    end\n\n    # Appends *fiber* to the head of the list.\n    def push(fiber : Fiber) : Nil\n      fiber.list_next = @head\n      @head = fiber\n      @tail = fiber if @tail.nil?\n      @size += 1\n    end\n\n    # Appends all the fibers from *other* to the tail of the list.\n    def bulk_unshift(other : List*) : Nil\n      return unless last = other.value.@tail\n      last.list_next = nil\n\n      if tail = @tail\n        tail.list_next = other.value.@head\n      else\n        @head = other.value.@head\n      end\n      @tail = last\n\n      @size += other.value.size\n    end\n\n    # Removes a fiber from the head of the list. Raises `IndexError` when\n    # empty.\n    @[AlwaysInline]\n    def pop : Fiber\n      pop { raise IndexError.new }\n    end\n\n    # Removes a fiber from the head of the list. Returns `nil` when empty.\n    @[AlwaysInline]\n    def pop? : Fiber?\n      pop { nil }\n    end\n\n    private def pop(&)\n      if fiber = @head\n        @head = fiber.list_next\n        @tail = nil if @head.nil?\n        @size -= 1\n        fiber.list_next = nil\n        fiber\n      else\n        yield\n      end\n    end\n\n    @[AlwaysInline]\n    def empty? : Bool\n      @head == nil\n    end\n\n    def clear : Nil\n      @size = 0\n      @head = @tail = nil\n    end\n\n    def each(&) : Nil\n      cursor = @head\n      while cursor\n        yield cursor\n        cursor = cursor.list_next\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/pointer_linked_list_node.cr",
    "content": "require \"crystal/pointer_linked_list\"\n\nclass Fiber\n  # :nodoc:\n  struct PointerLinkedListNode\n    include Crystal::PointerLinkedList::Node\n\n    def initialize(@fiber : Fiber)\n    end\n\n    def enqueue : Nil\n      @fiber.enqueue\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/stack.cr",
    "content": "class Fiber\n  # :nodoc:\n  struct Stack\n    getter pointer : Void*\n    getter bottom : Void*\n    getter size : Int32\n    getter? reusable : Bool\n\n    # Constructor for thread stacks (main fibers).\n    def initialize(@pointer : Void*, @bottom : Void*, *, @reusable = false)\n      # FIXME: sometimes gc/boehm reports weird stack limits on linux (over\n      # 2GB) at least, so we always cast to i32 without overflow checks.\n      @size = (@bottom - @pointer).to_i32!\n    end\n\n    # Constructor for fiber stacks.\n    def initialize(@pointer : Void*, @size : Int32, *, @reusable = false)\n      @bottom = @pointer + @size\n    end\n\n    def first_addressable_pointer : Void**\n      ptr = @bottom                 # stacks grow down\n      ptr -= sizeof(Void*)          # point to first addressable pointer\n      ptr.align_down(16).as(Void**) # align to 16 bytes\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber/stack_pool.cr",
    "content": "require \"crystal/system/fiber\"\n\nclass Fiber\n  # :nodoc:\n  class StackPool\n    STACK_SIZE = 8 * 1024 * 1024\n\n    {% if flag?(:execution_context) %}\n      # must explicitly declare the variable because of the macro in #initialize\n      @lock = uninitialized Crystal::SpinLock\n    {% end %}\n\n    # If *protect* is true, guards all top pages (pages with the lowest address\n    # values) in the allocated stacks; accessing them triggers an error\n    # condition, allowing stack overflows on non-main fibers to be detected.\n    #\n    # Interpreter stacks grow upwards (pushing values increases the stack\n    # pointer value) rather than downwards, so *protect* must be false.\n    #\n    # Interpreter keeps an internal list of stacks and musn't consider\n    # `Thread.current.dead_fiber_stack` for recycle which is handled by the\n    # scheduler's fiber stack pool where the interpreter runs.\n    def initialize(@protect : Bool = true, @reuse_dead_fiber_stack : Bool = true)\n      @deque = Deque(Stack).new\n\n      {% if flag?(:execution_context) %}\n        @lock = Crystal::SpinLock.new\n      {% end %}\n    end\n\n    def finalize\n      @deque.each do |stack|\n        Crystal::System::Fiber.free_stack(stack.pointer, stack.size)\n      end\n    end\n\n    # Removes and frees at most *count* stacks from the top of the pool,\n    # returning memory to the operating system.\n    def collect(count = lazy_size // 2) : Nil\n      count.times do\n        if stack = shift?\n          Crystal::System::Fiber.free_stack(stack.pointer, stack.size)\n        else\n          return\n        end\n      end\n    end\n\n    def collect_loop(every = 5.seconds) : Nil\n      loop do\n        sleep every\n        collect\n      end\n    end\n\n    # Removes a stack from the bottom of the pool, or allocates a new one.\n    def checkout : Stack\n      if stack = pop?\n        Crystal::System::Fiber.reset_stack(stack.pointer, stack.size, @protect)\n        stack\n      else\n        pointer = Crystal::System::Fiber.allocate_stack(STACK_SIZE, @protect)\n        Stack.new(pointer, STACK_SIZE, reusable: true)\n      end\n    end\n\n    # Appends a stack to the bottom of the pool.\n    def release(stack : Stack) : Nil\n      return unless stack.reusable?\n\n      {% if flag?(:execution_context) %}\n        @lock.sync { @deque.push(stack) }\n      {% else %}\n        @deque.push(stack)\n      {% end %}\n    end\n\n    # Returns the approximated size of the pool. It may be equal or slightly\n    # bigger or smaller than the actual size.\n    def lazy_size : Int32\n      @deque.size\n    end\n\n    private def shift?\n      {% if flag?(:execution_context) %}\n        @lock.sync { @deque.shift? } unless @deque.empty?\n      {% else %}\n        @deque.shift?\n      {% end %}\n    end\n\n    private def pop?\n      {% if flag?(:execution_context) %}\n        if @reuse_dead_fiber_stack && (stack = Thread.current.dead_fiber_stack?) && stack.reusable?\n          stack\n        else\n          @lock.sync { @deque.pop? } unless @deque.empty?\n        end\n      {% else %}\n        @deque.pop?\n      {% end %}\n    end\n  end\nend\n"
  },
  {
    "path": "src/fiber.cr",
    "content": "require \"crystal/system/thread_linked_list\"\nrequire \"crystal/print_buffered\"\nrequire \"./fiber/context\"\nrequire \"./fiber/stack\"\n\n# :nodoc:\n@[NoInline]\nfun _fiber_get_stack_top : Void*\n  dummy = uninitialized Int32\n  pointerof(dummy).as(Void*)\nend\n\n# A `Fiber` is a light-weight execution unit managed by the Crystal runtime.\n#\n# It is conceptually similar to an operating system thread but with less\n# overhead and completely internal to the Crystal process. The runtime includes\n# a scheduler which schedules execution of fibers.\n#\n# A `Fiber` has a stack size of `8 MiB` which is usually also assigned\n# to an operating system thread. But only `4KiB` are actually allocated at first\n# so the memory footprint is very small.\n#\n# Communication between fibers is usually passed through `Channel`.\n#\n# ## Cooperative\n#\n# Fibers are cooperative. That means execution can only be drawn from a fiber\n# when it offers it. It can't be interrupted in its execution at random.\n# In order to make concurrency work, fibers must make sure to occasionally\n# provide hooks for the scheduler to swap in other fibers.\n# IO operations like reading from a file descriptor are natural implementations\n# for this and the developer does not need to take further action on that. When\n# IO access can't be served immediately by a buffer, the fiber will\n# automatically wait and yield execution. When IO is ready it's going to be\n# resumed through the event loop.\n#\n# When a computation-intensive task has none or only rare IO operations, a fiber\n# should explicitly offer to yield execution from time to time using\n# `Fiber.yield` to break up tight loops. The frequency of this call depends on\n# the application and concurrency model.\n#\n# ## Event loop\n#\n# The event loop is responsible for keeping track of sleeping fibers waiting for\n# notifications that IO is ready or a timeout reached. When a fiber can be woken,\n# the event loop enqueues it in the scheduler\nclass Fiber\n  @@fibers = uninitialized Thread::LinkedList(Fiber)\n\n  protected def self.fibers : Thread::LinkedList(Fiber)\n    @@fibers\n  end\n\n  # :nodoc:\n  def self.init : Nil\n    @@fibers = Thread::LinkedList(Fiber).new\n  end\n\n  @context : Context\n  @stack : Stack\n  @resume_event : Crystal::EventLoop::Event?\n  @timeout_event : Crystal::EventLoop::Event?\n  # :nodoc:\n  property timeout_select_action : Channel::TimeoutAction?\n\n  # The name of the fiber, used as internal reference.\n  property name : String?\n\n  @alive = true\n\n  {% if flag?(:preview_mt) && !flag?(:execution_context) %}\n    @current_thread = Atomic(Thread?).new(nil)\n  {% end %}\n\n  # :nodoc:\n  property next : Fiber?\n\n  # :nodoc:\n  property previous : Fiber?\n\n  {% if flag?(:execution_context) %}\n    property! execution_context : ExecutionContext\n  {% end %}\n\n  # :nodoc:\n  property list_next : Fiber?\n\n  # :nodoc:\n  def self.inactive(fiber : Fiber)\n    fibers.delete(fiber)\n  end\n\n  # :nodoc:\n  def self.unsafe_each(&)\n    # nothing to iterate when @@fibers is nil + don't lazily allocate in a\n    # method called from a GC collection callback!\n    @@fibers.try(&.unsafe_each { |fiber| yield fiber })\n  end\n\n  # :nodoc:\n  def self.each(&)\n    fibers.each { |fiber| yield fiber }\n  end\n\n  {% begin %}\n  # Creates a new `Fiber` instance.\n  #\n  # When the fiber is executed, it runs *proc* in its context.\n  #\n  # *name* is an optional and used only as an internal reference.\n  def self.new(name : String? = nil, {% if flag?(:execution_context) %}execution_context : ExecutionContext = ExecutionContext.current,{% end %} &proc : ->) : self\n    stack =\n      {% if flag?(:interpreted) %}\n        # the interpreter is managing the stacks\n        Stack.new(Pointer(Void).null, Pointer(Void).null)\n      {% elsif flag?(:execution_context) %}\n        execution_context.stack_pool.checkout\n      {% else %}\n        Crystal::Scheduler.stack_pool.checkout\n      {% end %}\n    new(name, stack, {% if flag?(:execution_context) %}execution_context,{% end %} &proc)\n  end\n\n  # :nodoc:\n  def initialize(@name : String?, @stack : Stack, {% if flag?(:execution_context) %}@execution_context : ExecutionContext = ExecutionContext.current,{% end %} &@proc : ->)\n    @context = Context.new\n\n    fiber_main = ->(f : Fiber) { f.run }\n    stack_ptr = @stack.first_addressable_pointer\n    makecontext(stack_ptr, fiber_main)\n\n    Fiber.fibers.push(self)\n  end\n  {% end %}\n\n  # :nodoc:\n  def initialize(stack : Void*, thread)\n    @proc = Proc(Void).new { }\n\n    # TODO: should creating a new context for the main fiber also be platform specific?\n    # It's the same for all platforms except for the interpreted mode.\n    @context =\n      {% if flag?(:interpreted) %}\n        # In interpreted mode the stack_top variable actually points to a fiber\n        Context.new(Crystal::Interpreter.current_fiber)\n      {% else %}\n        Context.new(_fiber_get_stack_top)\n      {% end %}\n\n    thread.gc_thread_handler, stack_bottom = GC.current_thread_stack_bottom\n    @stack = Stack.new(stack, stack_bottom)\n\n    @name = \"main\"\n\n    {% if flag?(:preview_mt) && !flag?(:execution_context) %}\n      @current_thread.set(thread)\n    {% end %}\n\n    Fiber.fibers.push(self)\n\n    # we don't initialize @execution_context here (we may not have an execution\n    # context yet), and we can't detect ExecutionContext.current (we may reach\n    # an infinite recursion).\n  end\n\n  # :nodoc:\n  def run\n    GC.unlock_read\n\n    @proc.call\n  rescue ex\n    if name = @name\n      Crystal.print_buffered(\"Unhandled exception in spawn(name: %s)\", name, exception: ex, to: STDERR)\n    else\n      Crystal.print_buffered(\"Unhandled exception in spawn\", exception: ex, to: STDERR)\n    end\n  ensure\n    # Remove the current fiber from the linked list\n    Fiber.inactive(self)\n\n    # Delete the resume event if it was used by `yield` or `sleep`\n    @resume_event.try &.free\n    @timeout_event.try &.free\n    @timeout_select_action = nil\n\n    # Additional cleanup (avoid stale references)\n    @exec_recursive_hash = nil\n    @exec_recursive_clone_hash = nil\n\n    @alive = false\n\n    # the interpreter is managing the stacks\n    {% unless flag?(:interpreted) %}\n      {% if flag?(:execution_context) %}\n        # do not prematurely release the stack before we switch to another fiber\n        if stack = Thread.current.dying_fiber(self)\n          # we can however release the stack of a previously dying fiber (we\n          # since swapped context)\n          execution_context.stack_pool.release(stack)\n        end\n      {% else %}\n        Crystal::Scheduler.stack_pool.release(@stack)\n      {% end %}\n    {% end %}\n\n    Fiber.suspend\n  end\n\n  # Returns the current fiber.\n  def self.current : Fiber\n    Thread.current.current_fiber\n  end\n\n  # :nodoc:\n  def self.current? : Fiber?\n    Thread.current?.try &.current_fiber?\n  end\n\n  # The fiber's proc is currently running or didn't fully save its context. The\n  # fiber can't be resumed.\n  def running? : Bool\n    @context.resumable == 0\n  end\n\n  # The fiber's proc is currently not running and fully saved its context. The\n  # fiber can be resumed safely.\n  def resumable? : Bool\n    @context.resumable == 1\n  end\n\n  # The fiber's proc has terminated, and the fiber is now considered dead. The\n  # fiber is impossible to resume, ever.\n  def dead? : Bool\n    !@alive\n  end\n\n  # Immediately resumes execution of this fiber.\n  #\n  # There are no provisions for resuming the current fiber (where this\n  # method is called). Unless it is explicitly added for rescheduling (for\n  # example using `#enqueue`) the current fiber won't ever reach any instructions\n  # after the call to this method.\n  #\n  # ```\n  # fiber = Fiber.new do\n  #   puts \"in fiber\"\n  # end\n  # fiber.resume\n  # puts \"never reached\"\n  # ```\n  def resume : Nil\n    {% if flag?(:execution_context) %}\n      ExecutionContext.resume(self)\n    {% else %}\n      Crystal::Scheduler.resume(self)\n    {% end %}\n  end\n\n  # Adds this fiber to the scheduler's runnables queue for the current thread.\n  #\n  # This signals to the scheduler that the fiber is eligible for being resumed\n  # the next time it has the opportunity to reschedule to another fiber. There\n  # are no guarantees when that will happen.\n  def enqueue : Nil\n    {% if flag?(:execution_context) %}\n      execution_context.enqueue(self)\n    {% else %}\n      Crystal::Scheduler.enqueue(self)\n    {% end %}\n  end\n\n  # :nodoc:\n  def resume_event : Crystal::EventLoop::Event\n    @resume_event ||= Crystal::EventLoop.current.create_resume_event(self)\n  end\n\n  # :nodoc:\n  def timeout_event : Crystal::EventLoop::Event\n    @timeout_event ||= Crystal::EventLoop.current.create_timeout_event(self)\n  end\n\n  # :nodoc:\n  def timeout(timeout : Time::Span, select_action : Channel::TimeoutAction) : Nil\n    @timeout_select_action = select_action\n    timeout_event.add(timeout)\n  end\n\n  # :nodoc:\n  def cancel_timeout : Nil\n    return unless @timeout_select_action\n    @timeout_select_action = nil\n    @timeout_event.try &.delete\n  end\n\n  # :nodoc:\n  #\n  # The current fiber will resume after a period of time.\n  # The timeout can be cancelled with `cancel_timeout`\n  def self.timeout(timeout : Time::Span, select_action : Channel::TimeoutAction) : Nil\n    Fiber.current.timeout(timeout, select_action)\n  end\n\n  # :nodoc:\n  def self.cancel_timeout : Nil\n    Fiber.current.cancel_timeout\n  end\n\n  # Yields to the scheduler and allows it to swap execution to other\n  # waiting fibers.\n  #\n  # This is equivalent to `sleep 0.seconds`. It gives the scheduler an option\n  # to interrupt the current fiber's execution. If no other fibers are ready to\n  # be resumed, it immediately resumes the current fiber.\n  #\n  # This method is particularly useful to break up tight loops which are only\n  # computation intensive and don't offer natural opportunities for swapping\n  # fibers as with IO operations.\n  #\n  # ```\n  # counter = 0\n  # spawn name: \"status\" do\n  #   loop do\n  #     puts \"Status: #{counter}\"\n  #     sleep(2.seconds)\n  #   end\n  # end\n  #\n  # while counter < Int32::MAX\n  #   counter += 1\n  #   if counter % 1_000_000 == 0\n  #     # Without this, there would never be an opportunity to resume the status fiber\n  #     Fiber.yield\n  #   end\n  # end\n  # ```\n  def self.yield : Nil\n    Crystal.trace :sched, \"yield\"\n\n    # TODO: Fiber switching and evloop for wasm32\n    {% unless flag?(:wasi) %}\n      Crystal::EventLoop.current.sleep(0.seconds)\n    {% end %}\n  end\n\n  # Suspends execution of the current fiber indefinitely.\n  #\n  # Unlike `Fiber.yield` the current fiber is not automatically\n  # reenqueued and can only be resumed with an explicit call to `#enqueue`.\n  #\n  # This is equivalent to `sleep` without a time.\n  #\n  # This method is meant to be used in concurrency primitives. It's particularly\n  # useful if the fiber needs to wait  for something to happen (for example an IO\n  # event, a message is ready in a channel, etc.) which triggers a re-enqueue.\n  def self.suspend : Nil\n    {% if flag?(:execution_context) %}\n      ExecutionContext.reschedule\n    {% else %}\n      Crystal::Scheduler.reschedule\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.syscall(&)\n    {% if flag?(:execution_context) %}\n      ExecutionContext::Scheduler.current.syscall { yield }\n    {% else %}\n      yield\n    {% end %}\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"#<\" << self.class.name << \":0x\"\n    object_id.to_s(io, 16)\n    if name = @name\n      io << \": \" << name\n    end\n    io << '>'\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  # :nodoc:\n  def push_gc_roots : Nil\n    # Push the used section of the stack\n    GC.push_stack @context.stack_top, @stack.bottom\n  end\n\n  {% if flag?(:preview_mt) && !flag?(:execution_context) %}\n    # :nodoc:\n    def set_current_thread(thread = Thread.current) : Thread\n      @current_thread.set(thread)\n    end\n\n    # :nodoc:\n    def get_current_thread : Thread?\n      @current_thread.lazy_get\n    end\n  {% end %}\n\n  # :nodoc:\n  #\n  # See `Reference#exec_recursive` for details.\n  def exec_recursive_hash\n    @exec_recursive_hash ||= Hash({UInt64, Symbol}, Nil).new\n  end\n\n  # :nodoc:\n  #\n  # See `Reference#exec_recursive_clone` for details.\n  def exec_recursive_clone_hash\n    @exec_recursive_clone_hash ||= Hash(UInt64, UInt64).new\n  end\nend\n"
  },
  {
    "path": "src/file/error.cr",
    "content": "class File < IO::FileDescriptor\nend\n\nclass File::Error < IO::Error\n  getter other : String?\n\n  def file : String\n    target.not_nil!\n  end\n\n  private def self.new_from_os_error(message, os_error, **opts)\n    case\n    when File::NotFoundError.os_error?(os_error)\n      File::NotFoundError.new(message, **opts)\n    when File::AlreadyExistsError.os_error?(os_error)\n      File::AlreadyExistsError.new(message, **opts)\n    when File::AccessDeniedError.os_error?(os_error)\n      File::AccessDeniedError.new(message, **opts)\n    when File::BadExecutableError.os_error?(os_error)\n      File::BadExecutableError.new(message, **opts)\n    else\n      super message, os_error, **opts\n    end\n  end\n\n  def initialize(message, *, file : String | Path, @other : String? = nil)\n    super message, target: file\n  end\n\n  protected def self.build_message(message, *, file : String) : String\n    \"#{message}: '#{file.inspect_unquoted}'\"\n  end\n\n  protected def self.build_message(message, *, file : String, other : String) : String\n    \"#{message}: '#{file.inspect_unquoted}' -> '#{other.inspect_unquoted}'\"\n  end\n\n  {% if flag?(:win32) %}\n    protected def self.os_error_message(os_error : WinError, *, file : String) : String?\n      case os_error\n      when WinError::ERROR_BAD_EXE_FORMAT\n        os_error.formatted_message(file)\n      else\n        super\n      end\n    end\n  {% end %}\nend\n\nclass File::NotFoundError < File::Error\n  # :nodoc:\n  # See https://github.com/crystal-lang/crystal/issues/15905#issuecomment-2975820840\n  def self.os_error?(error)\n    error.in?(\n      Errno::ENAMETOOLONG,\n      Errno::ENOENT,\n      Errno::ENOTDIR,\n      WinError::ERROR_BAD_NETPATH,\n      WinError::ERROR_BAD_NET_NAME,\n      WinError::ERROR_BAD_PATHNAME,\n      WinError::ERROR_DIRECTORY,\n      WinError::ERROR_FILE_NOT_FOUND,\n      WinError::ERROR_FILENAME_EXCED_RANGE,\n      WinError::ERROR_INVALID_DRIVE,\n      WinError::ERROR_INVALID_NAME,\n      WinError::ERROR_PATH_NOT_FOUND,\n      WinError::WSAENAMETOOLONG,\n    )\n  end\nend\n\nclass File::AlreadyExistsError < File::Error\n  # :nodoc:\n  def self.os_error?(error)\n    error.in?(\n      Errno::EEXIST,\n      WinError::ERROR_ALREADY_EXISTS,\n      WinError::ERROR_FILE_EXISTS,\n    )\n  end\nend\n\nclass File::AccessDeniedError < File::Error\n  # :nodoc:\n  def self.os_error?(error)\n    error.in?(\n      Errno::EACCES,\n      WinError::ERROR_ACCESS_DENIED,\n      WinError::ERROR_PRIVILEGE_NOT_HELD,\n    )\n  end\nend\n\nclass File::BadExecutableError < File::Error\n  # :nodoc:\n  def self.os_error?(error)\n    error.in?(\n      Errno::ENOEXEC,\n      WinError::ERROR_BAD_EXE_FORMAT,\n    )\n  end\nend\n"
  },
  {
    "path": "src/file/info.cr",
    "content": "require \"crystal/system/file_info\"\n\nclass File\n  # Represents the various behaviour-altering flags which can be set on files.\n  # Not all flags will be supported on all platforms.\n  @[::Flags]\n  enum Flags : UInt8\n    SetUser\n    SetGroup\n    Sticky\n  end\n\n  # Represents the type of a file. Not all types will be supported on all\n  # platforms.\n  enum Type : UInt8\n    File\n    Directory\n    Symlink\n    Socket\n    Pipe\n    CharacterDevice\n    BlockDevice\n    Unknown\n\n    # Returns true if `self` is a `CharacterDevice` or a `BlockDevice`.\n    def device?\n      character_device? || block_device?\n    end\n  end\n\n  # Represents a set of access permissions for a file. Not all permission sets\n  # will be supported on all platforms.\n  #\n  # The binary representation of this enum is defined to be same representation\n  # as the permission bits of a unix `st_mode` field. `File::Permissions`\n  # can also be compared to its underlying bitset, for example\n  # `File::Permissions::All == 0o777` will always be `true`.\n  #\n  # On windows, only the `OwnerWrite` bit is effective. All file permissions\n  # will either be `0o444` for read-only files or `0o666` for read-write files.\n  # Directories are always mode `0o555` for read-only or `0o777`.\n  @[::Flags]\n  enum Permissions : Int16\n    OtherExecute = 0o001\n    OtherWrite   = 0o002\n    OtherRead    = 0o004\n    OtherAll     = 0o007\n\n    GroupExecute = 0o010\n    GroupWrite   = 0o020\n    GroupRead    = 0o040\n    GroupAll     = 0o070\n\n    OwnerExecute = 0o100\n    OwnerWrite   = 0o200\n    OwnerRead    = 0o400\n    OwnerAll     = 0o700\n\n    def self.new(int : Int)\n      new(int.to_i16)\n    end\n\n    def to_s(io : IO) : Nil\n      io << (owner_read? ? 'r' : '-')\n      io << (owner_write? ? 'w' : '-')\n      io << (owner_execute? ? 'x' : '-')\n\n      io << (group_read? ? 'r' : '-')\n      io << (group_write? ? 'w' : '-')\n      io << (group_execute? ? 'x' : '-')\n\n      io << (other_read? ? 'r' : '-')\n      io << (other_write? ? 'w' : '-')\n      io << (other_execute? ? 'x' : '-')\n\n      io << \" (0o\" << self.to_i.to_s(8) << ')'\n    end\n  end\n\n  # A `File::Info` contains metadata regarding a file.\n  # It is returned by `File.info`, `File#info` and `File.info?`.\n  struct Info\n    include Crystal::System::FileInfo\n\n    # Size of the file, in bytes.\n    def size : Int64\n      system_size\n    end\n\n    # The permissions of the file.\n    def permissions : Permissions\n      system_permissions\n    end\n\n    # The type of the file.\n    def type : Type\n      system_type\n    end\n\n    # The special flags this file has set.\n    def flags : Flags\n      system_flags\n    end\n\n    # The last time this file was modified.\n    def modification_time : Time\n      system_modification_time\n    end\n\n    # The user ID that the file belongs to.\n    def owner_id : String\n      system_owner_id\n    end\n\n    # The group ID that the file belongs to.\n    def group_id : String\n      system_group_id\n    end\n\n    # Returns true if this `Info` and *other* are of the same file.\n    #\n    # On Unix-like systems, this compares device and inode fields, and will\n    # compare equal for hard linked files.\n    def same_file?(other : self) : Bool\n      system_same_file?(other)\n    end\n\n    # Returns true if this `Info` represents a standard file. Shortcut for\n    # `type.file?`.\n    def file?\n      type.file?\n    end\n\n    # Returns true if this `Info` represents a directory. Shortcut for\n    # `type.directory?`.\n    def directory?\n      type.directory?\n    end\n\n    # Returns true if this `Info` represents a symbolic link to another file.\n    # Shortcut for `type.symlink?`.\n    def symlink?\n      type.symlink?\n    end\n\n    # Returns `true` if *path* is readable by the real user id of this process else returns `false`.\n    #\n    # ```\n    # File.write(\"foo\", \"foo\")\n    # File::Info.readable?(\"foo\") # => true\n    # ```\n    #\n    # This method returns the readable property as reported by the file system\n    # which provides no indication of whether `File.read` would be a valid\n    # operation because it applies to all file types, including directories.\n    def self.readable?(path : Path | String) : Bool\n      Crystal::System::File.readable?(path.to_s)\n    end\n\n    # Returns `true` if *path* is writable by the real user id of this process else returns `false`.\n    #\n    # ```\n    # File.write(\"foo\", \"foo\")\n    # File::Info.writable?(\"foo\") # => true\n    # ```\n    #\n    # This method returns the readable property as reported by the file system\n    # which provides no indication of whether `File.write` would be a valid\n    # operation because it applies to all file types, including directories.\n    def self.writable?(path : Path | String) : Bool\n      Crystal::System::File.writable?(path.to_s)\n    end\n\n    # Returns `true` if *path* is executable by the real user id of this process else returns `false`.\n    #\n    # ```\n    # File.write(\"foo\", \"foo\")\n    # File::Info.executable?(\"foo\") # => false\n    # ```\n    #\n    # This method returns the readable property as reported by the file system\n    # which provides no indication of whether `Process.execute` would be a valid\n    # operation because it applies to all file types, including directories\n    # (which typically *are* executable to signal it's allowed to list their\n    # contents).\n    def self.executable?(path : Path | String) : Bool\n      Crystal::System::File.executable?(path.to_s)\n    end\n  end\nend\n"
  },
  {
    "path": "src/file/match.cr",
    "content": "# This implementation of glob matching for `File.match?` is a port from the Rust\n# crate https://github.com/devongovett/glob-match, which is adapted from the\n# linear-time algorithm described in https://research.swtch.com/glob\n\nclass File < IO::FileDescriptor\n  class BadPatternError < Exception\n  end\n\n  # Matches *path* against *pattern*.\n  #\n  # The pattern syntax is similar to shell filename globbing. It may contain the following metacharacters:\n  #\n  # * `*`: Wildcard matches zero or more characters, except for directory separators.\n  # * `**`: Globstar matches zero or more characters, including directory separators.\n  #   It must match a complete path segment, i.e. it must be wrapped in `/`\n  #   except for the beginning and end of the pattern.\n  # * `?`: Matches a single Unicode character, except for directory separators.\n  # * Character classes:\n  #   * `[abc]`: Character set matches one of the Unicode characters contained in the brackets.\n  #   * `[^abc]`: Negated character set matches any Unicode character _except_ those contained in the brackets.\n  #   * `[a-z]`: Character range matches one Unicode character contained in the character range.\n  #   * `[^a-z]`: Negated character range matches one Unicode character _except_ those contained in the character range.\n  # * `{a,b}`: Branches matches one of the subpatterns contained in the braces. Subpatterns\n  #   may contain any other pattern feature, including nested branches (max nesting depth is 10 levels deep).\n  # * `\\\\`: Backslash escapes the next character.\n  #\n  # Multiple character pattern can be combined in the same brackets to define a\n  # character class (for example: `[0-9a-f]`).\n  #\n  # If *path* is a `Path`, all directory separators supported by *path* are\n  # recognized, according to the path's kind. If *path* is a `String`, only `/`\n  # is considered a directory separator.\n  #\n  # NOTE: Only `/` in *pattern* matches directory separators in *path*.\n  def self.match?(pattern : String, path : Path | String) : Bool\n    if path.is_a?(Path)\n      separators = Path.separators(path.@kind)\n      path = path.to_s\n    else\n      separators = Path.separators(Path::Kind::POSIX)\n    end\n\n    match_internal(pattern, path, separators)\n  end\n\n  private def self.match_internal(glob_str, path_str, separators)\n    state = State.new(separators: separators.to_static_array.to_slice)\n\n    # Store the state when we see an opening '{' brace in a stack.\n    # Up to 10 nested braces are supported.\n    brace_stack_data = uninitialized StaticArray({UInt32, UInt32}, 10)\n    brace_stack = BraceStack.new(brace_stack_data.to_slice)\n\n    # First, check if the pattern is negated with a leading '!' character.\n    # Multiple negations can occur.\n    negated = false\n\n    # TODO: Enable negation\n    # while state.glob_index < glob.size && glob[state.glob_index] === '!'\n    #   negated = !negated\n    #   state.glob_index += 1\n    # end\n\n    matched = state.match_from(glob_str, path_str, 0, brace_stack)\n\n    matched != negated\n  end\n\n  private record State,\n    separators : Slice(Char),\n    path_index = 0_u64,\n    glob_index = 0_u64,\n    brace_depth = 0_u64,\n    wildcard = Wildcard.new,\n    globstar = Wildcard.new\n\n  private record Wildcard,\n    glob_index = 0_u32,\n    path_index = 0_u32,\n    brace_depth = 0_u32 do\n    setter path_index\n  end\n\n  struct BraceStack(T)\n    def initialize(@slice : Slice(T), @size = 0)\n    end\n\n    getter size\n\n    @[AlwaysInline]\n    def push(item : T)\n      if @size >= @slice.size\n        raise BadPatternError.new(\"Brace nesting too deep: must not exceed 10 levels\")\n      end\n      @slice.unsafe_put(@size, item)\n      @size += 1\n    end\n\n    @[AlwaysInline]\n    def pop : T\n      @size -= 1\n      @slice[@size]\n    end\n\n    def to_slice\n      @slice[0, @size]\n    end\n  end\n\n  struct State\n    @[AlwaysInline]\n    def backtrack\n      @glob_index = @wildcard.glob_index.to_u64\n      @path_index = @wildcard.path_index.to_u64\n      @brace_depth = @wildcard.brace_depth.to_u64\n    end\n\n    # Coalesce multiple ** segments into one.\n    @[AlwaysInline]\n    private def skip_globstars(glob)\n      glob_index = @glob_index + 2\n      while glob_index + 4 <= glob.size && glob[glob_index, 4] == \"/**/\".to_slice\n        glob_index += 3\n      end\n\n      if glob[glob_index..] == \"/**\".to_slice\n        glob_index += 3\n      end\n\n      @glob_index = glob_index - 2\n    end\n\n    @[AlwaysInline]\n    def skip_to_separator(path, is_end_invalid)\n      if @path_index == path.size\n        @wildcard.path_index += 1\n        return\n      end\n\n      path_index = @path_index\n      while path_index < path.size && !separators.includes?(path[path_index].unsafe_chr)\n        path_index += 1\n      end\n\n      if is_end_invalid || path_index != path.size\n        path_index += 1\n      end\n\n      @wildcard.path_index = path_index.to_u32!\n      @globstar = @wildcard\n    end\n\n    @[AlwaysInline]\n    def skip_branch(glob)\n      in_brackets = false\n      end_brace_depth = @brace_depth - 1\n\n      while @glob_index < glob.size\n        c = glob[@glob_index]\n        # Skip nested braces.\n        if c === '{' && !in_brackets\n          @brace_depth += 1\n        elsif c === '}' && !in_brackets\n          @brace_depth -= 1\n          if @brace_depth == end_brace_depth\n            @glob_index += 1\n            return\n          end\n        elsif c === '[' && !in_brackets\n          in_brackets = true\n        elsif c === ']'\n          in_brackets = false\n        elsif c === '\\\\'\n          @glob_index += 1\n        end\n        @glob_index += 1\n      end\n    end\n\n    def match_brace_branch(\n      glob : String,\n      path : String,\n      open_brace_index,\n      branch_index,\n      brace_stack,\n    )\n      brace_stack.push({open_brace_index.to_u32!, branch_index})\n\n      branch_state = self.copy_with(\n        glob_index: branch_index.to_u64,\n        brace_depth: brace_stack.size.to_u64\n      )\n\n      matched = branch_state.match_from(glob, path, branch_index, brace_stack)\n\n      brace_stack.pop\n\n      matched\n    end\n\n    def match_brace(glob : String, path : String, brace_stack)\n      brace_depth = 0\n      in_brackets = false\n      open_brace_index = @glob_index\n      branch_index = 0_u32\n\n      while @glob_index < glob.bytesize\n        c = glob.to_slice[@glob_index]\n        # Skip nested braces.\n        if c === '{' && !in_brackets\n          brace_depth += 1\n          if brace_depth == 1\n            branch_index = (@glob_index + 1).to_u32!\n          end\n        elsif c === '}' && !in_brackets\n          brace_depth -= 1\n          if brace_depth == 0\n            return true if match_brace_branch(glob, path, open_brace_index, branch_index, brace_stack)\n            break\n          end\n        elsif c === ',' && brace_depth == 1\n          return true if match_brace_branch(glob, path, open_brace_index, branch_index, brace_stack)\n          branch_index = (@glob_index + 1).to_u32!\n        elsif c === '[' && !in_brackets\n          in_brackets = true\n        elsif c === ']'\n          in_brackets = false\n        elsif c === '\\\\'\n          @glob_index += 1\n        end\n        @glob_index += 1\n      end\n      false\n    end\n\n    def match_from(glob_str, path_str, match_start, brace_stack)\n      glob = glob_str.to_slice\n      path = path_str.to_slice\n\n      while @glob_index < glob.size || @path_index < path.size\n        if @glob_index < glob.size\n          g = glob[@glob_index]\n          if '*' === g\n            is_globstar = @glob_index + 1 < glob.size && glob[@glob_index + 1] === '*'\n\n            if is_globstar\n              skip_globstars(glob)\n            end\n\n            @wildcard = Wildcard.new(\n              @glob_index.to_u32!,\n              @path_index.to_u32! + 1,\n              @brace_depth.to_u32!\n            )\n\n            in_globstar = false\n            # `**` allows path separators, whereas `*` does not.\n            # However, `**` must be a full path component, i.e. `a/**/b` not `a**b`.\n            if is_globstar\n              @glob_index += 2\n              is_end_invalid = @glob_index != glob.size\n\n              if (@glob_index.to_i64 - match_start < 3 || glob[@glob_index - 3] === '/') && (!is_end_invalid || glob[@glob_index] === '/')\n                if is_end_invalid\n                  @glob_index += 1\n                end\n\n                skip_to_separator(path, is_end_invalid)\n                in_globstar = true\n              end\n            else\n              @glob_index += 1\n            end\n\n            if !in_globstar && @path_index < path.size && separators.includes?(path[@path_index].unsafe_chr)\n              @wildcard = @globstar\n            end\n\n            next\n          elsif '?' === g && @path_index < path.size\n            if !separators.includes?(path[@path_index].unsafe_chr)\n              @glob_index += 1\n              _, @path_index = consume_unicode_character(path_str, @path_index)\n              next\n            end\n          elsif '[' === g && @path_index < path.size\n            @glob_index += 1\n\n            # Check if the character class is negated.\n            negated_class = false\n            if @glob_index < glob.size && glob[@glob_index] === '^'\n              negated_class = true\n              @glob_index += 1\n            end\n\n            # Try each range.\n            first = true\n            is_match = false\n\n            c, new_path_index = consume_unicode_glob_character(path_str, @path_index)\n\n            while @glob_index < glob.size && (first || !(']' === glob[@glob_index]))\n              low, @glob_index = consume_unicode_glob_character(glob_str, @glob_index)\n\n              # If there is a - and the following character is not ], read the range end character.\n              if @glob_index + 1 < glob.size &&\n                 glob[@glob_index] === '-' &&\n                 !(glob[@glob_index + 1] === ']')\n                @glob_index += 1\n                high, @glob_index = consume_unicode_glob_character(glob_str, @glob_index)\n              else\n                high = low\n              end\n\n              if low <= c <= high\n                is_match = true\n              end\n\n              first = false\n            end\n            if @glob_index >= glob.size\n              raise BadPatternError.new \"unterminated character set\"\n            end\n            @glob_index += 1\n            if is_match != negated_class\n              @path_index = new_path_index\n              next\n            end\n          elsif g === '{'\n            if brace_stack_entry = brace_stack.to_slice.find { |open_brace_index, _| open_brace_index == @glob_index }\n              _, branch_index = brace_stack_entry\n              @glob_index = branch_index.to_u64\n              @brace_depth += 1\n              next\n            end\n\n            return match_brace(glob_str, path_str, brace_stack)\n          elsif (g === '}' || g === ',') && @brace_depth > 0\n            skip_branch(glob)\n            next\n          elsif @path_index < path.size\n            c = g\n            # Match escaped characters as literals.\n            if !unescape(pointerof(c), glob, pointerof(@glob_index))\n              raise BadPatternError.new \"Empty escape character\"\n            end\n\n            is_match = if c === '/'\n                         separators.includes?(path[@path_index].unsafe_chr)\n                       else\n                         path[@path_index] == c\n                       end\n\n            if is_match\n              @glob_index += 1\n              @path_index += 1\n\n              if c === '/'\n                @wildcard = @globstar\n              end\n\n              next\n            end\n          end\n        end\n\n        # If we didn't match, restore state to the previous star pattern.\n        if @wildcard.path_index > 0 && @wildcard.path_index.to_u32! <= path.size\n          backtrack\n          next\n        end\n\n        return false\n      end\n\n      true\n    end\n  end\nend\n\n@[AlwaysInline]\nprivate def unescape(c, glob, glob_index) : Bool\n  if '\\\\' === c.value\n    glob_index.value += 1\n    if glob_index.value >= glob.size\n      # Invalid pattern!\n      return false\n    end\n    c.value = case escaped_char = glob[glob_index.value]\n              when 'a' then 0x61_u8\n              when 'b' then 0x08_u8\n              when 'n' then '\\n'.ord.to_u8!\n              when 'r' then '\\r'.ord.to_u8!\n              when 't' then '\\t'.ord.to_u8!\n              else          escaped_char\n              end\n  end\n\n  true\nend\n\n@[AlwaysInline]\nprivate def consume_unicode_character(string, index) : {Char, UInt64}\n  reader = Char::Reader.new(string, index)\n  {reader.current_char, index + reader.current_char_width}\nend\n\n@[AlwaysInline]\nprivate def consume_unicode_glob_character(string, index) : {Char, UInt64}\n  c = string.to_unsafe[index]\n\n  if !unescape(pointerof(c), string.to_slice, pointerof(index))\n    raise File::BadPatternError.new(\"Invalid pattern\")\n  end\n\n  if c < 0x80\n    {c.unsafe_chr, index + 1}\n  else\n    consume_unicode_character(string, index)\n  end\nend\n"
  },
  {
    "path": "src/file/preader.cr",
    "content": "# :nodoc:\nclass File::PReader < IO\n  include IO::Buffered\n\n  getter? closed = false\n\n  @offset : Int64\n  @bytesize : Int64\n  @pos : Int64\n\n  def initialize(@file : File, offset : Int, bytesize : Int)\n    @offset = offset.to_i64\n    @bytesize = bytesize.to_i64\n    @pos = 0\n  end\n\n  def unbuffered_read(slice : Bytes) : Int32\n    check_open\n\n    count = slice.size\n    count = Math.min(count, @bytesize - @pos)\n\n    bytes_read = @file.@fd_lock.reference do\n      Crystal::System::FileDescriptor.pread(@file, slice[0, count], @offset + @pos)\n    end\n\n    @pos += bytes_read\n\n    bytes_read.to_i32\n  end\n\n  def unbuffered_write(slice : Bytes) : NoReturn\n    raise IO::Error.new(\"Can't write to read-only IO\")\n  end\n\n  def unbuffered_flush : NoReturn\n    raise IO::Error.new(\"Can't flush read-only IO\")\n  end\n\n  def unbuffered_rewind : Nil\n    @pos = 0\n  end\n\n  def unbuffered_close : Nil\n    @closed = true\n  end\nend\n"
  },
  {
    "path": "src/file/tempfile.cr",
    "content": "class File\n  # Returns a fully-qualified path to a temporary file.\n  # The file is not actually created on the file system.\n  #\n  # ```\n  # File.tempname(\"foo\", \".sock\") # => \"/tmp/foo20171206-1234-449386.sock\"\n  # ```\n  #\n  # *prefix* and *suffix* are appended to the front and end of the file name, respectively.\n  #\n  # NOTE: These path values may contain directory separators. It's the caller's\n  # responsibility to ensure they are used safely. For example by rejecting\n  # user-provided values that would result in navigating the directory tree.\n  #\n  # The path will be placed in *dir* which defaults to the standard temporary directory `Dir.tempdir`.\n  def self.tempname(prefix : String?, suffix : String?, *, dir : String = Dir.tempdir)\n    name = String.build do |io|\n      if prefix\n        io << prefix\n        io << '-'\n      end\n\n      io << Time.local.to_s(\"%Y%m%d\")\n      io << '-'\n\n      io << Process.pid\n      io << '-'\n\n      io << Random.rand(0x100000000).to_s(36)\n\n      io << suffix\n    end\n\n    File.join(dir, name)\n  end\n\n  # Returns a fully-qualified path to a temporary file.\n  # The optional *suffix* is appended to the file name.\n  #\n  # ```\n  # File.tempname          # => \"/tmp/20171206-1234-449386\"\n  # File.tempname(\".sock\") # => \"/tmp/20171206-1234-449386.sock\"\n  # ```\n  def self.tempname(suffix : String? = nil, *, dir : String = Dir.tempdir)\n    tempname(prefix: nil, suffix: suffix, dir: dir)\n  end\n\n  # Creates a temporary file.\n  #\n  # ```\n  # tempfile = File.tempfile(\"foo\", \".bar\")\n  # tempfile.delete\n  # ```\n  #\n  # *prefix* and *suffix* are appended to the front and end of the file name, respectively.\n  #\n  # NOTE: These path values may contain directory separators. It's the caller's\n  # responsibility to ensure they are used safely. For example by rejecting\n  # user-provided values that would result in navigating the directory tree.\n  #\n  # The file will be placed in *dir* which defaults to the standard temporary directory `Dir.tempdir`.\n  #\n  # *encoding* and *invalid* are passed to `IO#set_encoding`.\n  #\n  # It is the caller's responsibility to remove the file when no longer needed.\n  def self.tempfile(prefix : String?, suffix : String?, *, dir : String = Dir.tempdir, encoding = nil, invalid = nil)\n    fileno, path, blocking = Crystal::System::File.mktemp(prefix, suffix, dir)\n    new(path, fileno, blocking: blocking, encoding: encoding, invalid: invalid)\n  end\n\n  # Creates a temporary file.\n  #\n  # ```\n  # tempfile = File.tempfile(\".bar\")\n  # tempfile.delete\n  # ```\n  #\n  # *prefix* and *suffix* are appended to the front and end of the file name, respectively.\n  #\n  # NOTE: These path values may contain directory separators. It's the caller's\n  # responsibility to ensure they are used safely. For example by rejecting\n  # user-provided values that would result in navigating the directory tree.\n  #\n  # The file will be placed in *dir* which defaults to the standard temporary directory `Dir.tempdir`.\n  #\n  # *encoding* and *invalid* are passed to `IO#set_encoding`.\n  #\n  # It is the caller's responsibility to remove the file when no longer needed.\n  def self.tempfile(suffix : String? = nil, *, dir : String = Dir.tempdir, encoding = nil, invalid = nil)\n    tempfile(prefix: nil, suffix: suffix, dir: dir, encoding: encoding, invalid: invalid)\n  end\n\n  # Creates a temporary file and yields it to the given block. It is closed and returned at the end of this method call.\n  #\n  # ```\n  # tempfile = File.tempfile(\"foo\", \".bar\") do |file|\n  #   file.print(\"bar\")\n  # end\n  # File.read(tempfile.path) # => \"bar\"\n  # tempfile.delete\n  # ```\n  #\n  # *prefix* and *suffix* are appended to the front and end of the file name, respectively.\n  # These values may contain directory separators.\n  #\n  # The file will be placed in *dir* which defaults to the standard temporary directory `Dir.tempdir`.\n  #\n  # *encoding* and *invalid* are passed to `IO#set_encoding`.\n  #\n  # It is the caller's responsibility to remove the file when no longer needed.\n  def self.tempfile(prefix : String?, suffix : String?, *, dir : String = Dir.tempdir, encoding = nil, invalid = nil, &)\n    tempfile = tempfile(prefix: prefix, suffix: suffix, dir: dir, encoding: encoding, invalid: invalid)\n    begin\n      yield tempfile\n    ensure\n      tempfile.close\n    end\n    tempfile\n  end\n\n  # Creates a temporary file and yields it to the given block. It is closed and returned at the end of this method call.\n  #\n  # ```\n  # tempfile = File.tempfile(\".bar\") do |file|\n  #   file.print(\"bar\")\n  # end\n  # File.read(tempfile.path) # => \"bar\"\n  # tempfile.delete\n  # ```\n  #\n  # *prefix* and *suffix* are appended to the front and end of the file name, respectively.\n  #\n  # NOTE: These path values may contain directory separators. It's the caller's\n  # responsibility to ensure they are used safely. For example by rejecting\n  # user-provided values that would result in navigating the directory tree.\n  #\n  # The file will be placed in *dir* which defaults to the standard temporary directory `Dir.tempdir`.\n  #\n  # *encoding* and *invalid* are passed to `IO#set_encoding`.\n  #\n  # It is the caller's responsibility to remove the file when no longer needed.\n  def self.tempfile(suffix : String? = nil, *, dir : String = Dir.tempdir, encoding = nil, invalid = nil, &)\n    tempfile(prefix: nil, suffix: suffix, dir: dir, encoding: encoding, invalid: invalid) do |tempfile|\n      yield tempfile\n    end\n  end\nend\n"
  },
  {
    "path": "src/file.cr",
    "content": "class File < IO::FileDescriptor\nend\n\nrequire \"./file/error\"\nrequire \"./file/match\"\nrequire \"crystal/system/file\"\n\n# A `File` instance represents a file entry in the local file system and allows using it as an `IO`.\n#\n# ```\n# file = File.new(\"path/to/file\")\n# content = file.gets_to_end\n# file.close\n#\n# # Implicit close with `open` and a block:\n# content = File.open(\"path/to/file\") do |file|\n#   file.gets_to_end\n# end\n#\n# # Shortcut of the above:\n# content = File.read(\"path/to/file\")\n#\n# # Write to a file by opening with a \"write mode\" specified.\n# File.open(\"path/to/file\", \"w\") do |file|\n#   file.print \"hello\"\n# end\n# # Content of file on disk will now be \"hello\".\n#\n# # Shortcut of the above:\n# File.write(\"path/to/file\", \"hello\")\n# ```\n#\n# See `new` for various options *mode* can be.\n#\n# ## Temporary Files\n#\n# Every tempfile is operated as a `File`, including initializing, reading and writing.\n#\n# ```\n# tempfile = File.tempfile(\"foo\")\n#\n# File.size(tempfile.path)                   # => 6\n# File.info(tempfile.path).modification_time # => 2015-10-20 13:11:12Z\n# File.exists?(tempfile.path)                # => true\n# File.read_lines(tempfile.path)             # => [\"foobar\"]\n# ```\n#\n# Files created from `tempfile` are stored in a directory that handles\n# temporary files (`Dir.tempdir`):\n#\n# ```\n# File.tempfile(\"foo\").path # => \"/tmp/foo.ulBCPS\"\n# ```\n#\n# It is encouraged to delete a tempfile after using it, which\n# ensures they are not left behind in your filesystem until garbage collected.\n#\n# ```\n# tempfile = File.tempfile(\"foo\")\n# tempfile.delete\n# ```\nclass File < IO::FileDescriptor\n  # The file/directory separator character. `'/'` on all platforms.\n  SEPARATOR = '/'\n\n  # The file/directory separator string. `\"/\"` on all platforms.\n  SEPARATOR_STRING = \"/\"\n\n  # :nodoc:\n  DEFAULT_CREATE_PERMISSIONS = File::Permissions.new(0o644)\n\n  # The name of the null device on the host platform. `/dev/null` on UNIX and `NUL`\n  # on win32.\n  #\n  # When this device is opened using `File.open`, read operations will always\n  # return EOF, and any data written will be immediately discarded.\n  #\n  # ```\n  # File.open(File::NULL, \"w\") do |file|\n  #   file.puts \"this is discarded\"\n  # end\n  # ```\n  NULL = {% if flag?(:win32) %}\n           \"NUL\"\n         {% else %}\n           \"/dev/null\"\n         {% end %}\n\n  # Options used to control the behavior of `Dir.glob`.\n  @[Flags]\n  enum MatchOptions\n    # Includes files whose name begins with a period (`.`).\n    DotFiles\n\n    # Includes files which have a hidden attribute backed by the native\n    # filesystem.\n    #\n    # On Windows, this matches files that have the NTFS hidden attribute set.\n    # This option alone doesn't match files with _both_ the hidden and the\n    # system attributes, `OSHidden` must also be used.\n    #\n    # On other systems, this has no effect.\n    NativeHidden\n\n    # Includes files which are considered hidden by operating system\n    # conventions (apart from `DotFiles`), but not by the filesystem.\n    #\n    # On Windows, this option alone has no effect. However, combining it with\n    # `NativeHidden` matches files that have both the NTFS hidden and system\n    # attributes set. Note that files with just the system attribute, but not\n    # the hidden attribute, are always matched regardless of this option or\n    # `NativeHidden`.\n    #\n    # On other systems, this has no effect.\n    OSHidden\n\n    # Returns a suitable platform-specific default set of options for\n    # `Dir.glob` and `Dir.[]`.\n    #\n    # Currently this is always `NativeHidden | OSHidden`.\n    def self.glob_default\n      NativeHidden | OSHidden\n    end\n  end\n\n  include Crystal::System::File\n\n  # This constructor is for constructors to be able to initialize a `File` with\n  # a *path* and *fd*. The *blocking* param is informational and must reflect\n  # the non/blocking state of the underlying fd.\n  private def initialize(@path, fd : Int, mode = \"\", blocking = nil, encoding = nil, invalid = nil)\n    super(handle: fd)\n    system_init(mode, blocking)\n    set_encoding(encoding, invalid: invalid) if encoding\n  end\n\n  {% begin %}\n    # Opens the file named by *filename*.\n    #\n    # *mode* must be one of the following file open modes:\n    #\n    # ```text\n    # Mode       | Description\n    # -----------+------------------------------------------------------\n    # r rb       | Read-only, starts at the beginning of the file.\n    # r+ r+b rb+ | Read-write, starts at the beginning of the file.\n    # w wb       | Write-only, truncates existing file to zero length or\n    #            | creates a new file if the file doesn't exist.\n    # w+ w+b wb+ | Read-write, truncates existing file to zero length or\n    #            | creates a new file if the file doesn't exist.\n    # a ab       | Write-only, all writes seek to the end of the file,\n    #            | creates a new file if the file doesn't exist.\n    # a+ a+b ab+ | Read-write, all writes seek to the end of the file,\n    #            | creates a new file if the file doesn't exist.\n    # ```\n    #\n    # Line endings are preserved on all platforms. The `b` mode flag has no\n    # effect; it is provided only for POSIX compatibility.\n    #\n    # NOTE: The *blocking* arg is deprecated since Crystal 1.17. It used to be\n    # true by default to denote a regular disk file (always ready in system event\n    # loops) and could be set to false when the file was known to be a fifo, pipe,\n    # or character device (for example `/dev/tty`). The event loop now chooses\n    # the appropriate blocking mode automatically and there are no reasons to\n    # change it anymore.\n    #\n    # NOTE: On macOS files are always opened in blocking mode because non-blocking\n    # FIFO files don't work — the OS exhibits issues with readiness notifications.\n    def self.new(filename : Path | String, mode = \"r\", perm = DEFAULT_CREATE_PERMISSIONS, encoding = nil, invalid = nil, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil)\n      new_internal filename, mode, perm, encoding, invalid, blocking\n    end\n  {% end %}\n\n  protected def self.new_internal(filename, mode = \"r\", perm = DEFAULT_CREATE_PERMISSIONS, encoding = nil, invalid = nil, blocking = nil)\n    filename = filename.to_s\n    fd, blocking = Crystal::System::File.open(filename, mode, perm: perm, blocking: blocking)\n    new(filename, fd, mode, blocking, encoding, invalid)\n  end\n\n  getter path : String\n\n  # Returns a `File::Info` object for the file given by *path* or returns `nil`\n  # if the file does not exist.\n  #\n  # If *follow_symlinks* is set (the default), symbolic links are followed. Otherwise,\n  # symbolic links return information on the symlink itself.\n  #\n  # ```\n  # File.write(\"foo\", \"foo\")\n  # File.info?(\"foo\").try(&.size) # => 3\n  # File.info?(\"non_existent\")    # => nil\n  #\n  # File.symlink(\"foo\", \"bar\")\n  # File.info?(\"bar\", follow_symlinks: false).try(&.type.symlink?) # => true\n  # ```\n  #\n  # Use `IO::FileDescriptor#info` if the file is already open.\n  def self.info?(path : Path | String, follow_symlinks = true) : Info?\n    Crystal::System::File.info?(path.to_s, follow_symlinks)\n  end\n\n  # Returns a `File::Info` object for the file given by *path* or raises\n  # `File::Error` in case of an error.\n  #\n  # If *follow_symlinks* is set (the default), symbolic links are followed. Otherwise,\n  # symbolic links return information on the symlink itself.\n  #\n  # ```\n  # File.write(\"foo\", \"foo\")\n  # File.info(\"foo\").size              # => 3\n  # File.info(\"foo\").modification_time # => 2015-09-23 06:24:19Z\n  #\n  # File.symlink(\"foo\", \"bar\")\n  # File.info(\"bar\", follow_symlinks: false).type.symlink? # => true\n  # ```\n  #\n  # Use `IO::FileDescriptor#info` if the file is already open.\n  def self.info(path : Path | String, follow_symlinks = true) : Info\n    Crystal::System::File.info(path.to_s, follow_symlinks)\n  end\n\n  # Returns whether the file given by *path* exists.\n  #\n  # Symbolic links are dereferenced, possibly recursively. Returns `false` if a\n  # symbolic link refers to a non-existent file.\n  #\n  # ```\n  # File.delete(\"foo\") if File.exists?(\"foo\")\n  # File.exists?(\"foo\") # => false\n  # File.write(\"foo\", \"foo\")\n  # File.exists?(\"foo\") # => true\n  # ```\n  def self.exists?(path : Path | String) : Bool\n    Crystal::System::File.exists?(path.to_s)\n  end\n\n  # Returns `true` if *path1* and *path2* represents the same file.\n  # The comparison take symlinks in consideration if *follow_symlinks* is `true`.\n  def self.same?(path1 : Path | String, path2 : Path | String, follow_symlinks = false) : Bool\n    info(path1.to_s, follow_symlinks).same_file? info(path2.to_s, follow_symlinks)\n  end\n\n  # Compares two files *filename1* to *filename2* to determine if they are identical.\n  # Returns `true` if content are the same, `false` otherwise.\n  #\n  # ```\n  # File.write(\"file.cr\", \"1\")\n  # File.write(\"bar.cr\", \"1\")\n  # File.same_content?(\"file.cr\", \"bar.cr\") # => true\n  # ```\n  def self.same_content?(path1 : Path | String, path2 : Path | String) : Bool\n    open_internal(path1, \"rb\") do |file1|\n      open_internal(path2, \"rb\") do |file2|\n        return false unless file1.size == file2.size\n\n        same_content?(file1, file2)\n      end\n    end\n  end\n\n  # Returns the size of the file at *filename* in bytes.\n  # Raises `File::NotFoundError` if the file at *filename* does not exist.\n  #\n  # ```\n  # File.size(\"foo\") # raises File::NotFoundError\n  # File.write(\"foo\", \"foo\")\n  # File.size(\"foo\") # => 3\n  # ```\n  def self.size(filename : Path | String) : Int64\n    info(filename).size\n  end\n\n  # Returns `true` if the file at *path* is empty, otherwise returns `false`.\n  # Raises `File::NotFoundError` if the file at *path* does not exist.\n  #\n  # ```\n  # File.write(\"foo\", \"\")\n  # File.empty?(\"foo\") # => true\n  # File.write(\"foo\", \"foo\")\n  # File.empty?(\"foo\") # => false\n  # ```\n  def self.empty?(path : Path | String) : Bool\n    size(path) == 0\n  end\n\n  # Returns `true` if *path* is readable by the real user id of this process else returns `false`.\n  #\n  # ```\n  # File.write(\"foo\", \"foo\")\n  # File.readable?(\"foo\") # => true\n  # ```\n  @[Deprecated(\"Use `File::Info.readable?` instead\")]\n  def self.readable?(path : Path | String) : Bool\n    Crystal::System::File.readable?(path.to_s)\n  end\n\n  # Returns `true` if *path* is writable by the real user id of this process else returns `false`.\n  #\n  # ```\n  # File.write(\"foo\", \"foo\")\n  # File.writable?(\"foo\") # => true\n  # ```\n  @[Deprecated(\"Use `File::Info.writable?` instead\")]\n  def self.writable?(path : Path | String) : Bool\n    Crystal::System::File.writable?(path.to_s)\n  end\n\n  # Returns `true` if *path* is executable by the real user id of this process else returns `false`.\n  #\n  # ```\n  # File.write(\"foo\", \"foo\")\n  # File.executable?(\"foo\") # => false\n  # ```\n  @[Deprecated(\"Use `File::Info.executable?` instead\")]\n  def self.executable?(path : Path | String) : Bool\n    Crystal::System::File.executable?(path.to_s)\n  end\n\n  # Returns `true` if given *path* exists and is a file.\n  #\n  # ```\n  # File.write(\"foo\", \"\")\n  # Dir.mkdir(\"dir1\")\n  # File.file?(\"foo\")    # => true\n  # File.file?(\"dir1\")   # => false\n  # File.file?(\"foobar\") # => false\n  # ```\n  def self.file?(path : Path | String) : Bool\n    if info = info?(path)\n      info.type.file?\n    else\n      false\n    end\n  end\n\n  # Returns `true` if the given *path* exists and is a directory.\n  #\n  # ```\n  # File.write(\"foo\", \"\")\n  # Dir.mkdir(\"dir2\")\n  # File.directory?(\"foo\")    # => false\n  # File.directory?(\"dir2\")   # => true\n  # File.directory?(\"foobar\") # => false\n  # ```\n  def self.directory?(path : Path | String) : Bool\n    Dir.exists?(path)\n  end\n\n  # Returns all components of the given *path* except the last one.\n  #\n  # ```\n  # File.dirname(\"/foo/bar/file.cr\") # => \"/foo/bar\"\n  # ```\n  def self.dirname(path : Path | String) : String\n    Path.new(path).dirname\n  end\n\n  # Returns the last component of the given *path*.\n  #\n  # ```\n  # File.basename(\"/foo/bar/file.cr\") # => \"file.cr\"\n  # ```\n  def self.basename(path : Path | String) : String\n    Path.new(path).basename\n  end\n\n  # Returns the last component of the given *path*.\n  #\n  # If *suffix* is present at the end of *path*, it is removed.\n  #\n  # ```\n  # File.basename(\"/foo/bar/file.cr\", \".cr\") # => \"file\"\n  # ```\n  def self.basename(path : Path | String, suffix : String) : String\n    Path.new(path).basename(suffix.check_no_null_byte)\n  end\n\n  # Changes the owner of the specified file.\n  #\n  # ```\n  # File.chown(\"/foo/bar/baz.cr\", 1001, 100)\n  # File.chown(\"/foo/bar\", gid: 100)\n  # ```\n  #\n  # Unless *follow_symlinks* is set to `true`, then the owner symlink itself will\n  # be changed, otherwise the owner of the symlink destination file will be\n  # changed. For example, assuming symlinks as `foo -> bar -> baz`:\n  #\n  # ```\n  # File.chown(\"foo\", gid: 100)                        # changes foo's gid\n  # File.chown(\"foo\", gid: 100, follow_symlinks: true) # changes baz's gid\n  # ```\n  #\n  # Use `#chown` if the `File` is already open.\n  def self.chown(path : Path | String, uid : Int = -1, gid : Int = -1, follow_symlinks = false) : Nil\n    Crystal::System::File.chown(path.to_s, uid, gid, follow_symlinks)\n  end\n\n  # Changes the permissions of the specified file.\n  #\n  # Symlinks are dereferenced, so that only the permissions of the symlink\n  # destination are changed, never the permissions of the symlink itself.\n  #\n  # ```\n  # File.chmod(\"foo\", 0o755)\n  # File.info(\"foo\").permissions.value # => 0o755\n  #\n  # File.chmod(\"foo\", 0o700)\n  # File.info(\"foo\").permissions.value # => 0o700\n  # ```\n  #\n  # Use `#chmod` if the `File` is already open.\n  def self.chmod(path : Path | String, permissions : Int | Permissions) : Nil\n    Crystal::System::File.chmod(path.to_s, permissions)\n  end\n\n  # Deletes the file at *path*. Raises `File::Error` on failure.\n  #\n  # On Windows, this also deletes reparse points, including symbolic links,\n  # regardless of whether the reparse point is a directory.\n  #\n  # ```\n  # File.write(\"foo\", \"\")\n  # File.delete(\"./foo\")\n  # File.delete(\"./bar\") # raises File::NotFoundError (No such file or directory)\n  # ```\n  def self.delete(path : Path | String) : Nil\n    Crystal::System::File.delete(path.to_s, raise_on_missing: true)\n  end\n\n  # Deletes the file at *path*, or returns `false` if the file does not exist.\n  # Raises `File::Error` on other kinds of failure.\n  #\n  # On Windows, this also deletes reparse points, including symbolic links,\n  # regardless of whether the reparse point is a directory.\n  #\n  # ```\n  # File.write(\"foo\", \"\")\n  # File.delete?(\"./foo\") # => true\n  # File.delete?(\"./bar\") # => false\n  # ```\n  def self.delete?(path : Path | String) : Bool\n    Crystal::System::File.delete(path.to_s, raise_on_missing: false)\n  end\n\n  # Returns *filename*'s extension, or an empty string if it has no extension.\n  #\n  # ```\n  # File.extname(\"foo.cr\") # => \".cr\"\n  # ```\n  def self.extname(filename : Path | String) : String\n    Path.new(filename).extension\n  end\n\n  # Converts *path* to an absolute path. Relative paths are\n  # referenced from the current working directory of the process unless\n  # *dir* is given, in which case it will be used as the starting point.\n  # \"~\" is expanded to the value passed to *home*.\n  # If it is `false` (default), home is not expanded.\n  # If `true`, it is expanded to the user's home directory (`Path.home`).\n  #\n  # ```\n  # File.expand_path(\"foo\")                 # => \"/home/.../foo\"\n  # File.expand_path(\"~/foo\", home: \"/bar\") # => \"/bar/foo\"\n  # File.expand_path(\"baz\", \"/foo/bar\")     # => \"/foo/bar/baz\"\n  # ```\n  def self.expand_path(path : Path | String, dir = nil, *, home = false) : String\n    Path.new(path).expand(dir || Dir.current, home: home).to_s\n  end\n\n  # Resolves the real path of *path* by following symbolic links.\n  def self.realpath(path : Path | String) : String\n    Crystal::System::File.realpath(path.to_s)\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `.realpath` instead.\")]\n  def self.real_path(path : Path | String) : String\n    realpath(path)\n  end\n\n  # Creates a new link (also known as a hard link) at *new_path* to an existing file\n  # given by *old_path*.\n  def self.link(old_path : Path | String, new_path : Path | String) : Nil\n    Crystal::System::File.link(old_path.to_s, new_path.to_s)\n  end\n\n  # Creates a symbolic link at *new_path* to an existing file given by *old_path*.\n  def self.symlink(old_path : Path | String, new_path : Path | String) : Nil\n    Crystal::System::File.symlink(old_path.to_s, new_path.to_s)\n  end\n\n  # Returns `true` if the *path* is a symbolic link.\n  def self.symlink?(path : Path | String) : Bool\n    if info = info?(path, follow_symlinks: false)\n      info.type.symlink?\n    else\n      false\n    end\n  end\n\n  # Returns the target of a symbolic link.\n  def self.readlink(path : Path | String) : String\n    Crystal::System::File.readlink(path.to_s) { }\n  end\n\n  # Returns the target of a symbolic link.\n  #\n  # Returns `nil` if *path* does not exist or is not a symbolic link.\n  def self.readlink?(path : Path | String) : String?\n    Crystal::System::File.readlink(path.to_s) { return nil }\n  end\n\n  {% begin %}\n    # Opens the file named by *filename*. If a file is being created, its initial\n    # permissions may be set using the *perm* parameter.\n    #\n    # See `self.new` for what *mode* can be.\n    def self.open(filename : Path | String, mode = \"r\", perm = DEFAULT_CREATE_PERMISSIONS, encoding = nil, invalid = nil, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil) : self\n      new_internal(filename.to_s, mode, perm, encoding, invalid, blocking)\n    end\n  {% end %}\n\n  {% begin %}\n    # Opens the file named by *filename*. If a file is being created, its initial\n    # permissions may be set using the *perm* parameter. Then given block will be passed the opened\n    # file as an argument, the file will be automatically closed when the block returns.\n    #\n    # See `self.new` for what *mode* can be.\n    def self.open(filename : Path | String, mode = \"r\", perm = DEFAULT_CREATE_PERMISSIONS, encoding = nil, invalid = nil, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil, &)\n      open_internal(filename.to_s, mode, perm, encoding, invalid, blocking) { |file| yield file }\n    end\n  {% end %}\n\n  protected def self.open_internal(filename, mode = \"r\", perm = DEFAULT_CREATE_PERMISSIONS, encoding = nil, invalid = nil, blocking = nil, &)\n    file = new_internal(filename, mode, perm, encoding, invalid, blocking)\n    begin\n      yield file\n    ensure\n      file.close\n    end\n  end\n\n  {% begin %}\n    # Returns the content of *filename* as a string.\n    #\n    # Raises `File::Error` if the file cannot be read.\n    #\n    # ```\n    # File.write(\"bar\", \"foo\")\n    # File.read(\"bar\") # => \"foo\"\n    #\n    # File.read(\"non-existent\") # raises File::NotFoundError\n    # ```\n    def self.read(filename : Path | String, encoding = nil, invalid = nil, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil) : String\n      open_internal(filename, \"r\", blocking: blocking) do |file|\n        if encoding\n          file.set_encoding(encoding, invalid: invalid)\n          file.gets_to_end\n        else\n          # We try to read a string with an initialize capacity\n          # equal to the file's size, but the size might not be\n          # correct or even be zero (for example for /proc files)\n          size = file.size.to_i\n          size = 256 if size == 0\n          String.build(size) do |io|\n            IO.copy(file, io)\n          end\n        end\n      end\n    end\n  {% end %}\n\n  {% begin %}\n    # Yields each line in *filename* to the given block.\n    #\n    # ```\n    # File.write(\"foobar\", \"foo\\nbar\")\n    #\n    # array = [] of String\n    # File.each_line(\"foobar\") do |line|\n    #   array << line\n    # end\n    # array # => [\"foo\", \"bar\"]\n    # ```\n    def self.each_line(filename : Path | String, encoding = nil, invalid = nil, chomp = true, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil, &)\n      open_internal(filename, \"r\", encoding: encoding, invalid: invalid, blocking: blocking) do |file|\n        file.each_line(chomp: chomp) do |line|\n          yield line\n        end\n      end\n    end\n  {% end %}\n\n  {% begin %}\n    # Returns all lines in *filename* as an array of strings.\n    #\n    # ```\n    # File.write(\"foobar\", \"foo\\nbar\")\n    # File.read_lines(\"foobar\") # => [\"foo\", \"bar\"]\n    # ```\n    def self.read_lines(filename : Path | String, encoding = nil, invalid = nil, chomp = true, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil) : Array(String)\n      lines = [] of String\n      open_internal(filename, \"r\", encoding: encoding, invalid: invalid, blocking: blocking) do |file|\n        file.each_line(chomp: chomp) do |line|\n          lines << line\n        end\n      end\n      lines\n    end\n  {% end %}\n\n  {% begin %}\n    # Writes the given *content* to *filename*.\n    #\n    # By default, an existing file will be overwritten.\n    #\n    # *filename* will be created if it does not already exist.\n    #\n    # ```\n    # File.write(\"foo\", \"bar\")\n    # File.write(\"foo\", \"baz\", mode: \"a\")\n    # ```\n    #\n    # NOTE: If the content is a `Slice(UInt8)`, those bytes will be written.\n    # If it's an `IO`, all bytes from the `IO` will be written.\n    # Otherwise, the string representation of *content* will be written\n    # (the result of invoking `to_s` on *content*).\n    #\n    # See `self.new` for what *mode* can be.\n    def self.write(filename : Path | String, content, perm = DEFAULT_CREATE_PERMISSIONS, encoding = nil, invalid = nil, mode = \"w\", {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated] {% end %} blocking = nil)\n      open_internal(filename, mode, perm, encoding: encoding, invalid: invalid, blocking: blocking) do |file|\n        case content\n        when Bytes\n          file.sync = true\n          file.write(content)\n        when IO\n          file.sync = true\n          IO.copy(content, file)\n        else\n          file.print(content)\n        end\n      end\n    end\n  {% end %}\n\n  # Copies the file *src* to the file *dst*.\n  # Permission bits are copied too.\n  #\n  # ```\n  # File.touch(\"afile\")\n  # File.chmod(\"afile\", 0o600)\n  # File.copy(\"afile\", \"afile_copy\")\n  # File.info(\"afile_copy\").permissions.value # => 0o600\n  # ```\n  def self.copy(src : String | Path, dst : String | Path) : Nil\n    open_internal(src) do |s|\n      permissions = s.info.permissions\n      open_internal(dst, \"wb\", perm: permissions) do |d|\n        # If permissions don't match, we opened a pre-existing file with\n        # different permissions and need to change them explicitly.\n        # The permission change does not have any effect on the open file descriptor d.\n        if d.info.permissions != permissions\n          d.chmod(permissions)\n        end\n\n        # TODO use sendfile or copy_file_range syscall. See #8926, #8919\n        IO.copy(s, d)\n      end\n    end\n  end\n\n  # Returns a new string formed by joining the strings using `File::SEPARATOR`.\n  #\n  # ```\n  # File.join(\"foo\", \"bar\", \"baz\")       # => \"foo/bar/baz\"\n  # File.join(\"foo/\", \"/bar/\", \"/baz\")   # => \"foo/bar/baz\"\n  # File.join(\"/foo/\", \"/bar/\", \"/baz/\") # => \"/foo/bar/baz/\"\n  # ```\n  def self.join(*parts : String | Path) : String\n    Path.new(*parts).to_s\n  end\n\n  # Returns a new string formed by joining the strings using `File::SEPARATOR`.\n  #\n  # ```\n  # File.join({\"foo\", \"bar\", \"baz\"})       # => \"foo/bar/baz\"\n  # File.join({\"foo/\", \"/bar/\", \"/baz\"})   # => \"foo/bar/baz\"\n  # File.join([\"/foo/\", \"/bar/\", \"/baz/\"]) # => \"/foo/bar/baz/\"\n  # ```\n  def self.join(parts : Enumerable) : String\n    Path.new(parts).to_s\n  end\n\n  # Moves *old_filename* to *new_filename*.\n  #\n  # ```\n  # File.write(\"afile\", \"foo\")\n  # File.exists?(\"afile\") # => true\n  #\n  # File.rename(\"afile\", \"afile.cr\")\n  # File.exists?(\"afile\")    # => false\n  # File.exists?(\"afile.cr\") # => true\n  # ```\n  def self.rename(old_filename : Path | String, new_filename : Path | String) : Nil\n    if error = Crystal::System::File.rename(old_filename.to_s, new_filename.to_s)\n      raise error\n    end\n  end\n\n  # Rename the current `File`\n  def rename(new_filename : Path | String) : Nil\n    File.rename(@path, new_filename)\n    @path = new_filename.to_s\n  end\n\n  # Sets the access and modification times of *filename*.\n  #\n  # Use `#utime` if the `File` is already open.\n  def self.utime(atime : Time, mtime : Time, filename : Path | String) : Nil\n    Crystal::System::File.utime(atime, mtime, filename.to_s)\n  end\n\n  # Attempts to set the access and modification times of the file named\n  # in the *filename* parameter to the value given in *time*.\n  #\n  # If the file does not exist, it will be created.\n  #\n  # Use `#touch` if the `File` is already open.\n  def self.touch(filename : Path | String, time : Time = Time.utc) : Nil\n    open_internal(filename, \"a\") { } unless exists?(filename)\n    utime time, time, filename\n  end\n\n  # Returns the size in bytes of the currently opened file.\n  def size : Int64\n    info.size\n  end\n\n  # Truncates the file to the specified *size*. Requires that the current file is opened\n  # for writing.\n  def truncate(size = 0) : Nil\n    flush\n    @fd_lock.reference { system_truncate(size) }\n  end\n\n  # Yields an `IO` to read a section inside this file.\n  # Multiple sections can be read concurrently.\n  def read_at(offset, bytesize, & : IO ->)\n    self_bytesize = self.size\n\n    unless 0 <= offset <= self_bytesize\n      raise ArgumentError.new(\"Offset out of bounds\")\n    end\n\n    if bytesize < 0\n      raise ArgumentError.new(\"Negative bytesize\")\n    end\n\n    unless 0 <= offset + bytesize <= self_bytesize\n      raise ArgumentError.new(\"Bytesize out of bounds\")\n    end\n\n    io = PReader.new(self, offset, bytesize)\n    yield io ensure io.close\n  end\n\n  def inspect(io : IO) : Nil\n    io << \"#<File:\" << @path\n    io << \" (closed)\" if closed?\n    io << '>'\n  end\n\n  # Changes the owner of the specified file.\n  #\n  # ```\n  # file.chown(1001, 100)\n  # file.chown(gid: 100)\n  # ```\n  def chown(uid : Int = -1, gid : Int = -1) : Nil\n    @fd_lock.reference { system_chown(uid, gid) }\n  end\n\n  # Changes the permissions of the specified file.\n  #\n  # ```\n  # file.chmod(0o755)\n  # file.info.permissions.value # => 0o755\n  #\n  # file.chmod(0o700)\n  # file.info.permissions.value # => 0o700\n  # ```\n  def chmod(permissions : Int | Permissions) : Nil\n    @fd_lock.reference { system_chmod(permissions) }\n  end\n\n  # Sets the access and modification times\n  def utime(atime : Time, mtime : Time) : Nil\n    @fd_lock.reference { system_utime(atime, mtime) }\n  end\n\n  # Attempts to set the access and modification times\n  # to the value given in *time*.\n  def touch(time : Time = Time.utc) : Nil\n    utime time, time\n  end\n\n  # Deletes this file.\n  def delete : Nil\n    File.delete(@path)\n  end\nend\n\nrequire \"./file/*\"\n"
  },
  {
    "path": "src/file_utils.cr",
    "content": "# NOTE: To use `FileUtils`, you must explicitly import it with `require \"file_utils\"`\nmodule FileUtils\n  extend self\n\n  # Changes the current working directory of the process to the given string *path*.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.cd(\"/tmp\")\n  # ```\n  #\n  # NOTE: Alias of `Dir.cd`\n  def cd(path : Path | String) : Nil\n    Dir.cd(path)\n  end\n\n  # Changes the current working directory of the process to the given string *path*\n  # and invoked the block, restoring the original working directory when the block exits.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.cd(\"/tmp\") { Dir.current } # => \"/tmp\"\n  # ```\n  #\n  # NOTE: Alias of `Dir.cd` with block\n  def cd(path : Path | String, &)\n    Dir.cd(path) { yield }\n  end\n\n  # Compares two files *filename1* to *filename2* to determine if they are identical.\n  # Returns `true` if content are the same, `false` otherwise.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # File.write(\"file.cr\", \"1\")\n  # File.write(\"bar.cr\", \"1\")\n  # FileUtils.cmp(\"file.cr\", \"bar.cr\") # => true\n  # ```\n  #\n  # NOTE: Alias of `File.same_content?`\n  def cmp(filename1 : Path | String, filename2 : Path | String) : Bool\n    File.same_content?(filename1, filename2)\n  end\n\n  # Attempts to set the access and modification times of the file named\n  # in the *path* parameter to the value given in *time*.\n  #\n  # If the file does not exist, it will be created.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.touch(\"afile.cr\")\n  # ```\n  #\n  # NOTE: Alias of `File.touch`\n  def touch(path : Path | String, time : Time = Time.utc) : Nil\n    File.touch(path, time)\n  end\n\n  # Attempts to set the access and modification times of each file given\n  # in the *paths* parameter to the value given in *time*.\n  #\n  # If the file does not exist, it will be created.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.touch([\"foo\", \"bar\"])\n  # ```\n  def touch(paths : Enumerable(Path | String), time : Time = Time.utc) : Nil\n    paths.each do |path|\n      touch(path, time)\n    end\n  end\n\n  # Copies the file *src_path* to the file or directory *dest*.\n  # If *dest* is a directory, a file with the same basename as *src_path* is created in *dest*\n  # Permission bits are copied too.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # File.touch(\"afile\")\n  # File.chmod(\"afile\", 0o600)\n  # FileUtils.cp(\"afile\", \"afile_copy\")\n  # File.info(\"afile_copy\").permissions.value # => 0o600\n  # ```\n  def cp(src_path : Path | String, dest : Path | String) : Nil\n    dest = Path[dest, File.basename(src_path)] if Dir.exists?(dest)\n    File.copy(src_path, dest)\n  end\n\n  # Copies a list of files *src* to *dest*.\n  # *dest* must be an existing directory.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # Dir.mkdir(\"files\")\n  # FileUtils.cp({\"bar.cr\", \"afile\"}, \"files\")\n  # ```\n  def cp(srcs : Enumerable(Path | String), dest : Path | String) : Nil\n    raise ArgumentError.new(\"No such directory : #{dest}\") unless Dir.exists?(dest)\n    srcs.each do |src|\n      cp(src, dest)\n    end\n  end\n\n  # Copies a file or directory *src_path* to *dest_path*.\n  # If *src_path* is a directory, this method copies all its contents recursively.\n  # If *dest* is a directory, copies src to dest/src.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.cp_r(\"files\", \"dir\")\n  # ```\n  def cp_r(src_path : Path | String, dest_path : Path | String) : Nil\n    dest_path = File.join(dest_path, File.basename(src_path)) if File.directory?(dest_path)\n    cp_recursive(src_path, dest_path)\n  end\n\n  private def cp_recursive(src_path : Path | String, dest_path : Path | String)\n    if Dir.exists?(src_path)\n      Dir.mkdir(dest_path) unless Dir.exists?(dest_path)\n      Dir.each_child(src_path) do |entry|\n        src = File.join(src_path, entry)\n        dest = File.join(dest_path, entry)\n        cp_recursive(src, dest)\n      end\n    else\n      cp(src_path, dest_path)\n    end\n  end\n\n  # Creates a hard link *dest_path* which points to *src_path*.\n  # If *dest_path* already exists and is a directory, creates a link *dest_path/src_path*.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # # Create a hard link, pointing from /usr/bin/emacs to /usr/bin/vim\n  # FileUtils.ln(\"/usr/bin/vim\", \"/usr/bin/emacs\")\n  # # Create a hard link, pointing from /tmp/foo.c to foo.c\n  # FileUtils.ln(\"foo.c\", \"/tmp\")\n  # ```\n  def ln(src_path : Path | String, dest_path : Path | String) : Nil\n    if Dir.exists?(dest_path)\n      File.link(src_path, File.join(dest_path, File.basename(src_path)))\n    else\n      File.link(src_path, dest_path)\n    end\n  end\n\n  # Creates a hard link to each path in *src_paths* inside the *dest_dir* directory.\n  # If *dest_dir* is not a directory, raises an `ArgumentError`.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # # Create /usr/bin/vim, /usr/bin/emacs, and /usr/bin/nano as hard links\n  # FileUtils.ln([\"vim\", \"emacs\", \"nano\"], \"/usr/bin\")\n  # ```\n  def ln(src_paths : Enumerable(Path | String), dest_dir : Path | String) : Nil\n    raise ArgumentError.new(\"No such directory : #{dest_dir}\") unless Dir.exists?(dest_dir)\n\n    src_paths.each do |path|\n      ln(path, dest_dir)\n    end\n  end\n\n  # Creates a symbolic link *dest_path* which points to *src_path*.\n  # If *dest_path* already exists and is a directory, creates a link *dest_path/src_path*.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # # Create a symbolic link pointing from logs to /var/log\n  # FileUtils.ln_s(\"/var/log\", \"logs\")\n  # # Create a symbolic link pointing from /tmp/src to src\n  # FileUtils.ln_s(\"src\", \"/tmp\")\n  # ```\n  def ln_s(src_path : Path | String, dest_path : Path | String) : Nil\n    if Dir.exists?(dest_path)\n      File.symlink(src_path, File.join(dest_path, File.basename(src_path)))\n    else\n      File.symlink(src_path, dest_path)\n    end\n  end\n\n  # Creates a symbolic link to each path in *src_paths* inside the *dest_dir* directory.\n  # If *dest_dir* is not a directory, raises an `ArgumentError`.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # # Create symbolic links in src/ pointing to every .c file in the current directory\n  # FileUtils.ln_s(Dir[\"*.c\"], \"src\")\n  # ```\n  def ln_s(src_paths : Enumerable(Path | String), dest_dir : Path | String) : Nil\n    raise ArgumentError.new(\"No such directory : #{dest_dir}\") unless Dir.exists?(dest_dir)\n\n    src_paths.each do |path|\n      ln_s(path, dest_dir)\n    end\n  end\n\n  # Like `#ln_s(Path | String, Path | String)`, but overwrites `dest_path` if it exists and is not a directory\n  # or if `dest_path/src_path` exists.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # # Create a symbolic link pointing from bar.c to foo.c, even if bar.c already exists\n  # FileUtils.ln_sf(\"foo.c\", \"bar.c\")\n  # ```\n  def ln_sf(src_path : Path | String, dest_path : Path | String) : Nil\n    if File.directory?(dest_path)\n      dest_path = File.join(dest_path, File.basename(src_path))\n    end\n\n    rm_rf(dest_path) if File.exists?(dest_path)\n    File.symlink(src_path, dest_path)\n  end\n\n  # Creates a symbolic link to each path in *src_paths* inside the *dest_dir* directory,\n  # ignoring any overwritten paths.\n  #\n  # If *dest_dir* is not a directory, raises an `ArgumentError`.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # # Create symbolic links in src/ pointing to every .c file in the current directory,\n  # # even if it means overwriting files in src/\n  # FileUtils.ln_sf(Dir[\"*.c\"], \"src\")\n  # ```\n  def ln_sf(src_paths : Enumerable(Path | String), dest_dir : Path | String) : Nil\n    raise ArgumentError.new(\"No such directory : #{dest_dir}\") unless Dir.exists?(dest_dir)\n\n    src_paths.each do |path|\n      ln_sf(path, dest_dir)\n    end\n  end\n\n  # Creates a new directory at the given *path*. The linux-style permission *mode*\n  # can be specified, with a default of 777 (0o777).\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.mkdir(\"src\")\n  # ```\n  #\n  # NOTE: Alias of `Dir.mkdir`\n  def mkdir(path : Path | String, mode = 0o777) : Nil\n    Dir.mkdir(path, mode)\n  end\n\n  # Creates a new directory at the given *paths*. The linux-style permission *mode*\n  # can be specified, with a default of 777 (0o777).\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.mkdir([\"foo\", \"bar\"])\n  # ```\n  def mkdir(paths : Enumerable(Path | String), mode = 0o777) : Nil\n    paths.each do |path|\n      Dir.mkdir(path, mode)\n    end\n  end\n\n  # Creates a new directory at the given *path*, including any non-existing\n  # intermediate directories. The linux-style permission *mode* can be specified,\n  # with a default of 777 (0o777).\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.mkdir_p(\"foo\")\n  # ```\n  #\n  # NOTE: Alias of `Dir.mkdir_p`\n  def mkdir_p(path : Path | String, mode = 0o777) : Nil\n    Dir.mkdir_p(path, mode)\n  end\n\n  # Creates a new directory at the given *paths*, including any non-existing\n  # intermediate directories. The linux-style permission *mode* can be specified,\n  # with a default of 777 (0o777).\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.mkdir_p([\"foo\", \"bar\", \"baz\", \"dir1\", \"dir2\", \"dir3\"])\n  # ```\n  def mkdir_p(paths : Enumerable(Path | String), mode = 0o777) : Nil\n    paths.each do |path|\n      Dir.mkdir_p(path, mode)\n    end\n  end\n\n  # Moves *src_path* to *dest_path*.\n  #\n  # NOTE: If *src_path* and *dest_path* exist on different mounted filesystems,\n  # the file at *src_path* is copied to *dest_path* and then removed.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.mv(\"afile\", \"afile.cr\")\n  # ```\n  def mv(src_path : Path | String, dest_path : Path | String) : Nil\n    if error = Crystal::System::File.rename(src_path.to_s, dest_path.to_s)\n      raise error unless error.os_error.in?(Errno::EXDEV, Errno::EPERM, WinError::ERROR_NOT_SAME_DEVICE)\n      cp_r(src_path, dest_path)\n      rm_r(src_path)\n    end\n  end\n\n  # Moves every *srcs* to *dest*.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.mv([\"foo\", \"bar\"], \"src\")\n  # ```\n  def mv(srcs : Enumerable(Path | String), dest : Path | String) : Nil\n    raise ArgumentError.new(\"No such directory : #{dest}\") unless Dir.exists?(dest)\n    srcs.each do |src|\n      mv(src, File.join(dest, File.basename(src)))\n    rescue File::Error\n    end\n  end\n\n  # Returns the current working directory.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.pwd\n  # ```\n  #\n  # NOTE: Alias of `Dir.current`\n  def pwd : String\n    Dir.current\n  end\n\n  # Deletes the *path* file given, raises if the path does not exist.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm(\"afile.cr\")\n  # ```\n  #\n  # NOTE: Alias of `File.delete`\n  def rm(path : Path | String) : Nil\n    File.delete(path)\n  end\n\n  # Deletes all *paths* files given, raises if any of the paths doesn't exist.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm([\"dir/afile\", \"afile_copy\"])\n  # ```\n  def rm(paths : Enumerable(Path | String)) : Nil\n    paths.each do |path|\n      File.delete(path)\n    end\n  end\n\n  # Deletes the given *path* file, ignoring it if it does not exist.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm_f(\"afile.cr\")\n  # ```\n  #\n  # NOTE: Alias of `File.delete?`\n  def rm_f(path : Path | String) : Nil\n    File.delete?(path)\n  end\n\n  # Deletes all *paths* files given, ignoring non-existing ones.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm_f([\"dir/afile\", \"afile_copy\"])\n  # ```\n  def rm_f(paths : Enumerable(Path | String)) : Nil\n    paths.each do |path|\n      File.delete?(path)\n    end\n  end\n\n  # Deletes a file or directory *path*.\n  # If *path* is a directory, this method removes all its contents recursively.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm_r(\"dir\")\n  # FileUtils.rm_r(\"file.cr\")\n  # ```\n  def rm_r(path : Path | String) : Nil\n    if Dir.exists?(path) && !File.symlink?(path)\n      Dir.each_child(path) do |entry|\n        src = File.join(path, entry)\n        rm_r(src)\n      end\n      Dir.delete(path)\n    else\n      File.delete(path)\n    end\n  end\n\n  # Deletes a list of files or directories *paths*.\n  # If one path is a directory, this method removes all its contents recursively.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm_r([\"files\", \"bar.cr\"])\n  # ```\n  def rm_r(paths : Enumerable(Path | String)) : Nil\n    paths.each do |path|\n      rm_r(path)\n    end\n  end\n\n  # Deletes a file or directory *path*.\n  # If *path* is a directory, this method removes all its contents recursively.\n  # Ignores all errors.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm_rf(\"dir\")\n  # FileUtils.rm_rf(\"file.cr\")\n  # FileUtils.rm_rf(\"non_existent_file\")\n  # ```\n  def rm_rf(path : Path | String) : Nil\n    rm_r(path)\n  rescue File::Error\n  end\n\n  # Deletes a list of files or directories *paths*.\n  # If one path is a directory, this method removes all its contents recursively.\n  # Ignores all errors.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rm_rf([\"dir\", \"file.cr\", \"non_existent_file\"])\n  # ```\n  def rm_rf(paths : Enumerable(Path | String)) : Nil\n    paths.each do |path|\n      rm_r(path)\n    rescue File::Error\n    end\n  end\n\n  # Removes the directory at the given *path*.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rmdir(\"baz\")\n  # ```\n  #\n  # NOTE: Alias of `Dir.delete`\n  def rmdir(path : Path | String) : Nil\n    Dir.delete(path)\n  end\n\n  # Removes all directories at the given *paths*.\n  #\n  # ```\n  # require \"file_utils\"\n  #\n  # FileUtils.rmdir([\"dir1\", \"dir2\", \"dir3\"])\n  # ```\n  def rmdir(paths : Enumerable(Path | String)) : Nil\n    paths.each do |path|\n      Dir.delete(path)\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/ascii_number.cr",
    "content": "require \"./float_common\"\n\nmodule Float::FastFloat\n  # Next function can be micro-optimized, but compilers are entirely able to\n  # optimize it well.\n  def self.is_integer?(c : UC) : Bool forall UC\n    !(c > '9'.ord || c < '0'.ord)\n  end\n\n  # Read 8 UC into a u64. Truncates UC if not char.\n  def self.read8_to_u64(chars : UC*) : UInt64 forall UC\n    val = uninitialized UInt64\n    chars.as(UInt8*).copy_to(pointerof(val).as(UInt8*), sizeof(UInt64))\n    {% if IO::ByteFormat::SystemEndian == IO::ByteFormat::BigEndian %}\n      val.byte_swap\n    {% else %}\n      val\n    {% end %}\n  end\n\n  # credit  @aqrit\n  def self.parse_eight_digits_unrolled(val : UInt64) : UInt32\n    mask = 0x000000FF000000FF_u64\n    mul1 = 0x000F424000000064_u64 # 100 + (1000000ULL << 32)\n    mul2 = 0x0000271000000001_u64 # 1 + (10000ULL << 32)\n    val &-= 0x3030303030303030\n    val = (val &* 10) &+ val.unsafe_shr(8) # val = (val * 2561) >> 8\n    val = (((val & mask) &* mul1) &+ ((val.unsafe_shr(16) & mask) &* mul2)).unsafe_shr(32)\n    val.to_u32!\n  end\n\n  # Call this if chars are definitely 8 digits.\n  def self.parse_eight_digits_unrolled(chars : UC*) : UInt32 forall UC\n    parse_eight_digits_unrolled(read8_to_u64(chars))\n  end\n\n  # credit @aqrit\n  def self.is_made_of_eight_digits_fast?(val : UInt64) : Bool\n    ((val &+ 0x4646464646464646_u64) | (val &- 0x3030303030303030_u64)) & 0x8080808080808080_u64 == 0\n  end\n\n  # NOTE(crystal): returns {p, i}\n  def self.loop_parse_if_eight_digits(p : UInt8*, pend : UInt8*, i : UInt64) : {UInt8*, UInt64}\n    # optimizes better than parse_if_eight_digits_unrolled() for UC = char.\n    while pend - p >= 8 && is_made_of_eight_digits_fast?(read8_to_u64(p))\n      i = i &* 100000000 &+ parse_eight_digits_unrolled(read8_to_u64(p)) # in rare cases, this will overflow, but that's ok\n      p += 8\n    end\n    {p, i}\n  end\n\n  enum ParseError\n    NoError\n\n    # [JSON-only] The minus sign must be followed by an integer.\n    MissingIntegerAfterSign\n\n    # A sign must be followed by an integer or dot.\n    MissingIntegerOrDotAfterSign\n\n    # [JSON-only] The integer part must not have leading zeros.\n    LeadingZerosInIntegerPart\n\n    # [JSON-only] The integer part must have at least one digit.\n    NoDigitsInIntegerPart\n\n    # [JSON-only] If there is a decimal point, there must be digits in the\n    # fractional part.\n    NoDigitsInFractionalPart\n\n    # The mantissa must have at least one digit.\n    NoDigitsInMantissa\n\n    # Scientific notation requires an exponential part.\n    MissingExponentialPart\n  end\n\n  struct ParsedNumberStringT(UC)\n    property exponent : Int64 = 0\n    property mantissa : UInt64 = 0\n    property lastmatch : UC* = Pointer(UC).null\n    property negative : Bool = false\n    property valid : Bool = false\n    property too_many_digits : Bool = false\n    # contains the range of the significant digits\n    property integer : Slice(UC) = Slice(UC).empty  # non-nullable\n    property fraction : Slice(UC) = Slice(UC).empty # nullable\n    property error : ParseError = :no_error\n  end\n\n  alias ByteSpan = ::Bytes\n  alias ParsedNumberString = ParsedNumberStringT(UInt8)\n\n  def self.report_parse_error(p : UC*, error : ParseError) : ParsedNumberStringT(UC) forall UC\n    answer = ParsedNumberStringT(UC).new\n    answer.valid = false\n    answer.lastmatch = p\n    answer.error = error\n    answer\n  end\n\n  # Assuming that you use no more than 19 digits, this will parse an ASCII\n  # string.\n  def self.parse_number_string(p : UC*, pend : UC*, options : ParseOptionsT(UC)) : ParsedNumberStringT(UC) forall UC\n    fmt = options.format\n    decimal_point = options.decimal_point\n\n    answer = ParsedNumberStringT(UInt8).new\n    answer.valid = false\n    answer.too_many_digits = false\n    answer.negative = p.value === '-'\n\n    if p.value === '-' || (!fmt.json_fmt? && p.value === '+')\n      p += 1\n      if p == pend\n        return report_parse_error(p, :missing_integer_or_dot_after_sign)\n      end\n      if fmt.json_fmt?\n        if !is_integer?(p.value) # a sign must be followed by an integer\n          return report_parse_error(p, :missing_integer_after_sign)\n        end\n      else\n        if !is_integer?(p.value) && p.value != decimal_point # a sign must be followed by an integer or the dot\n          return report_parse_error(p, :missing_integer_or_dot_after_sign)\n        end\n      end\n    end\n    start_digits = p\n\n    i = 0_u64 # an unsigned int avoids signed overflows (which are bad)\n\n    while p != pend && is_integer?(p.value)\n      # a multiplication by 10 is cheaper than an arbitrary integer multiplication\n      i = i &* 10 &+ (p.value &- '0'.ord).to_u64! # might overflow, we will handle the overflow later\n      p += 1\n    end\n    end_of_integer_part = p\n    digit_count = (end_of_integer_part - start_digits).to_i32!\n    answer.integer = Slice.new(start_digits, digit_count)\n    if fmt.json_fmt?\n      # at least 1 digit in integer part, without leading zeros\n      if digit_count == 0\n        return report_parse_error(p, :no_digits_in_integer_part)\n      end\n      if start_digits[0] === '0' && digit_count > 1\n        return report_parse_error(p, :leading_zeros_in_integer_part)\n      end\n    end\n\n    exponent = 0_i64\n    has_decimal_point = p != pend && p.value == decimal_point\n    if has_decimal_point\n      p += 1\n      before = p\n      # can occur at most twice without overflowing, but let it occur more, since\n      # for integers with many digits, digit parsing is the primary bottleneck.\n      p, i = loop_parse_if_eight_digits(p, pend, i)\n\n      while p != pend && is_integer?(p.value)\n        digit = (p.value &- '0'.ord).to_u8!\n        p += 1\n        i = i &* 10 &+ digit # in rare cases, this will overflow, but that's ok\n      end\n      exponent = before - p\n      answer.fraction = Slice.new(before, (p - before).to_i32!)\n      digit_count &-= exponent\n    end\n    if fmt.json_fmt?\n      # at least 1 digit in fractional part\n      if has_decimal_point && exponent == 0\n        return report_parse_error(p, :no_digits_in_fractional_part)\n      end\n    elsif digit_count == 0 # we must have encountered at least one integer!\n      return report_parse_error(p, :no_digits_in_mantissa)\n    end\n    exp_number = 0_i64 # explicit exponential part\n    if (fmt.scientific? && p != pend && p.value.unsafe_chr.in?('e', 'E')) ||\n       (fmt.fortran_fmt? && p != pend && p.value.unsafe_chr.in?('+', '-', 'd', 'D'))\n      location_of_e = p\n      if p.value.unsafe_chr.in?('e', 'E', 'd', 'D')\n        p += 1\n      end\n      neg_exp = false\n      if p != pend && p.value === '-'\n        neg_exp = true\n        p += 1\n      elsif p != pend && p.value === '+' # '+' on exponent is allowed by C++17 20.19.3.(7.1)\n        p += 1\n      end\n      if p == pend || !is_integer?(p.value)\n        if !fmt.fixed?\n          # The exponential part is invalid for scientific notation, so it must\n          # be a trailing token for fixed notation. However, fixed notation is\n          # disabled, so report a scientific notation error.\n          return report_parse_error(p, :missing_exponential_part)\n        end\n        # Otherwise, we will be ignoring the 'e'.\n        p = location_of_e\n      else\n        while p != pend && is_integer?(p.value)\n          digit = (p.value &- '0'.ord).to_u8!\n          if exp_number < 0x10000000\n            exp_number = exp_number &* 10 &+ digit\n          end\n          p += 1\n        end\n        if neg_exp\n          exp_number = 0_i64 &- exp_number\n        end\n        exponent &+= exp_number\n      end\n    else\n      # If it scientific and not fixed, we have to bail out.\n      if fmt.scientific? && !fmt.fixed?\n        return report_parse_error(p, :missing_exponential_part)\n      end\n    end\n    answer.lastmatch = p\n    answer.valid = true\n\n    # If we frequently had to deal with long strings of digits,\n    # we could extend our code by using a 128-bit integer instead\n    # of a 64-bit integer. However, this is uncommon.\n    #\n    # We can deal with up to 19 digits.\n    if digit_count > 19 # this is uncommon\n      # It is possible that the integer had an overflow.\n      # We have to handle the case where we have 0.0000somenumber.\n      # We need to be mindful of the case where we only have zeroes...\n      # E.g., 0.000000000...000.\n      start = start_digits\n      while start != pend && (start.value === '0' || start.value == decimal_point)\n        if start.value === '0'\n          digit_count &-= 1\n        end\n        start += 1\n      end\n\n      if digit_count > 19\n        answer.too_many_digits = true\n        # Let us start again, this time, avoiding overflows.\n        # We don't need to check if is_integer, since we use the\n        # pre-tokenized spans from above.\n        i = 0_u64\n        p = answer.integer.to_unsafe\n        int_end = p + answer.integer.size\n        minimal_nineteen_digit_integer = 1000000000000000000_u64\n        while i < minimal_nineteen_digit_integer && p != int_end\n          i = i &* 10 &+ (p.value &- '0'.ord).to_u64!\n          p += 1\n        end\n        if i >= minimal_nineteen_digit_integer # We have a big integers\n          exponent = (end_of_integer_part - p) &+ exp_number\n        else # We have a value with a fractional component.\n          p = answer.fraction.to_unsafe\n          frac_end = p + answer.fraction.size\n          while i < minimal_nineteen_digit_integer && p != frac_end\n            i = i &* 10 &+ (p.value &- '0'.ord).to_u64!\n            p += 1\n          end\n          exponent = (answer.fraction.to_unsafe - p) &+ exp_number\n        end\n        # We have now corrected both exponent and i, to a truncated value\n      end\n    end\n    answer.exponent = exponent\n    answer.mantissa = i\n    answer\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/bigint.cr",
    "content": "require \"./float_common\"\n\nmodule Float::FastFloat\n  # the limb width: we want efficient multiplication of double the bits in\n  # limb, or for 64-bit limbs, at least 64-bit multiplication where we can\n  # extract the high and low parts efficiently. this is every 64-bit\n  # architecture except for sparc, which emulates 128-bit multiplication.\n  # we might have platforms where `CHAR_BIT` is not 8, so let's avoid\n  # doing `8 * sizeof(limb)`.\n  {% if flag?(:bits64) %}\n    alias Limb = UInt64\n    LIMB_BITS = 64\n  {% else %}\n    alias Limb = UInt32\n    LIMB_BITS = 32\n  {% end %}\n\n  alias LimbSpan = Slice(Limb)\n\n  # number of bits in a bigint. this needs to be at least the number\n  # of bits required to store the largest bigint, which is\n  # `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or\n  # ~3600 bits, so we round to 4000.\n  BIGINT_BITS = 4000\n  {% begin %}\n    BIGINT_LIMBS = {{ BIGINT_BITS // LIMB_BITS }}\n  {% end %}\n\n  # vector-like type that is allocated on the stack. the entire\n  # buffer is pre-allocated, and only the length changes.\n  # NOTE(crystal): Deviates a lot from the original implementation to reuse\n  # `Indexable` as much as possible. Contrast with `Crystal::SmallDeque` and\n  # `Crystal::Tracing::BufferIO`\n  struct Stackvec(Size)\n    include Indexable::Mutable(Limb)\n\n    @data = uninitialized Limb[Size]\n\n    # we never need more than 150 limbs\n    @length = 0_u16\n\n    def unsafe_fetch(index : Int) : Limb\n      @data.to_unsafe[index]\n    end\n\n    def unsafe_put(index : Int, value : Limb) : Limb\n      @data.to_unsafe[index] = value\n    end\n\n    def size : Int32\n      @length.to_i32!\n    end\n\n    def to_unsafe : Limb*\n      @data.to_unsafe\n    end\n\n    def to_slice : LimbSpan\n      LimbSpan.new(@data.to_unsafe, @length)\n    end\n\n    def initialize\n    end\n\n    # create stack vector from existing limb span.\n    def initialize(s : LimbSpan)\n      try_extend(s)\n    end\n\n    # index from the end of the container\n    def rindex(index : Int) : Limb\n      rindex = @length &- index &- 1\n      @data.to_unsafe[rindex]\n    end\n\n    # set the length, without bounds checking.\n    def size=(@length : UInt16) : UInt16\n      length\n    end\n\n    def capacity : Int32\n      Size.to_i32!\n    end\n\n    # append item to vector, without bounds checking.\n    def push_unchecked(value : Limb) : Nil\n      @data.to_unsafe[@length] = value\n      @length &+= 1\n    end\n\n    # append item to vector, returning if item was added\n    def try_push(value : Limb) : Bool\n      if size < capacity\n        push_unchecked(value)\n        true\n      else\n        false\n      end\n    end\n\n    # add items to the vector, from a span, without bounds checking\n    def extend_unchecked(s : LimbSpan) : Nil\n      ptr = @data.to_unsafe + @length\n      s.to_unsafe.copy_to(ptr, s.size)\n      @length &+= s.size\n    end\n\n    # try to add items to the vector, returning if items were added\n    def try_extend(s : LimbSpan) : Bool\n      if size &+ s.size <= capacity\n        extend_unchecked(s)\n        true\n      else\n        false\n      end\n    end\n\n    # resize the vector, without bounds checking\n    # if the new size is longer than the vector, assign value to each\n    # appended item.\n    def resize_unchecked(new_len : UInt16, value : Limb) : Nil\n      if new_len > @length\n        count = new_len &- @length\n        first = @data.to_unsafe + @length\n        count.times { |i| first[i] = value }\n        @length = new_len\n      else\n        @length = new_len\n      end\n    end\n\n    # try to resize the vector, returning if the vector was resized.\n    def try_resize(new_len : UInt16, value : Limb) : Bool\n      if new_len > capacity\n        false\n      else\n        resize_unchecked(new_len, value)\n        true\n      end\n    end\n\n    # check if any limbs are non-zero after the given index.\n    # this needs to be done in reverse order, since the index\n    # is relative to the most significant limbs.\n    def nonzero?(index : Int) : Bool\n      while index < size\n        if rindex(index) != 0\n          return true\n        end\n        index &+= 1\n      end\n      false\n    end\n\n    # normalize the big integer, so most-significant zero limbs are removed.\n    def normalize : Nil\n      while @length > 0 && rindex(0) == 0\n        @length &-= 1\n      end\n    end\n  end\n\n  # NOTE(crystal): returns also *truncated* by value (ditto below)\n  def self.empty_hi64 : {UInt64, Bool}\n    truncated = false\n    {0_u64, truncated}\n  end\n\n  def self.uint64_hi64(r0 : UInt64) : {UInt64, Bool}\n    truncated = false\n    shl = r0.leading_zeros_count\n    {r0.unsafe_shl(shl), truncated}\n  end\n\n  def self.uint64_hi64(r0 : UInt64, r1 : UInt64) : {UInt64, Bool}\n    shl = r0.leading_zeros_count\n    if shl == 0\n      truncated = r1 != 0\n      {r0, truncated}\n    else\n      shr = 64 &- shl\n      truncated = r1.unsafe_shl(shl) != 0\n      {r0.unsafe_shl(shl) | r1.unsafe_shr(shr), truncated}\n    end\n  end\n\n  def self.uint32_hi64(r0 : UInt32) : {UInt64, Bool}\n    uint64_hi64(r0.to_u64!)\n  end\n\n  def self.uint32_hi64(r0 : UInt32, r1 : UInt32) : {UInt64, Bool}\n    x0 = r0.to_u64!\n    x1 = r1.to_u64!\n    uint64_hi64(x0.unsafe_shl(32) | x1)\n  end\n\n  def self.uint32_hi64(r0 : UInt32, r1 : UInt32, r2 : UInt32) : {UInt64, Bool}\n    x0 = r0.to_u64!\n    x1 = r1.to_u64!\n    x2 = r2.to_u64!\n    uint64_hi64(x0, x1.unsafe_shl(32) | x2)\n  end\n\n  # add two small integers, checking for overflow.\n  # we want an efficient operation.\n  # NOTE(crystal): returns also *overflow* by value\n  def self.scalar_add(x : Limb, y : Limb) : {Limb, Bool}\n    z = x &+ y\n    overflow = z < x\n    {z, overflow}\n  end\n\n  # multiply two small integers, getting both the high and low bits.\n  # NOTE(crystal): passes *carry* in and out by value\n  def self.scalar_mul(x : Limb, y : Limb, carry : Limb) : {Limb, Limb}\n    {% if Limb == UInt64 %}\n      z = x.to_u128! &* y.to_u128! &+ carry\n      carry = z.unsafe_shr(LIMB_BITS).to_u64!\n      {z.to_u64!, carry}\n    {% else %}\n      z = x.to_u64! &* y.to_u64! &+ carry\n      carry = z.unsafe_shr(LIMB_BITS).to_u32!\n      {z.to_u32!, carry}\n    {% end %}\n  end\n\n  # add scalar value to bigint starting from offset.\n  # used in grade school multiplication\n  def self.small_add_from(vec : Stackvec(Size)*, y : Limb, start : Int) : Bool forall Size\n    index = start\n    carry = y\n\n    while carry != 0 && index < vec.value.size\n      x, overflow = scalar_add(vec.value.unsafe_fetch(index), carry)\n      vec.value.unsafe_put(index, x)\n      carry = Limb.new!(overflow ? 1 : 0)\n      index &+= 1\n    end\n    if carry != 0\n      fastfloat_try vec.value.try_push(carry)\n    end\n    true\n  end\n\n  # add scalar value to bigint.\n  def self.small_add(vec : Stackvec(Size)*, y : Limb) : Bool forall Size\n    small_add_from(vec, y, 0)\n  end\n\n  # multiply bigint by scalar value.\n  def self.small_mul(vec : Stackvec(Size)*, y : Limb) : Bool forall Size\n    carry = Limb.zero\n    i = 0\n    while i < vec.value.size\n      xi = vec.value.unsafe_fetch(i)\n      z, carry = scalar_mul(xi, y, carry)\n      vec.value.unsafe_put(i, z)\n      i &+= 1\n    end\n    if carry != 0\n      fastfloat_try vec.value.try_push(carry)\n    end\n    true\n  end\n\n  # add bigint to bigint starting from index.\n  # used in grade school multiplication\n  def self.large_add_from(x : Stackvec(Size)*, y : LimbSpan, start : Int) : Bool forall Size\n    # the effective x buffer is from `xstart..x.len()`, so exit early\n    # if we can't get that current range.\n    if x.value.size < start || y.size > x.value.size &- start\n      fastfloat_try x.value.try_resize((y.size &+ start).to_u16!, 0)\n    end\n\n    carry = false\n    index = 0\n    while index < y.size\n      xi = x.value.unsafe_fetch(index &+ start)\n      yi = y.unsafe_fetch(index)\n      c2 = false\n      xi, c1 = scalar_add(xi, yi)\n      if carry\n        xi, c2 = scalar_add(xi, 1)\n      end\n      x.value.unsafe_put(index &+ start, xi)\n      carry = c1 || c2\n      index &+= 1\n    end\n\n    # handle overflow\n    if carry\n      fastfloat_try small_add_from(x, 1, y.size &+ start)\n    end\n    true\n  end\n\n  # add bigint to bigint.\n  def self.large_add_from(x : Stackvec(Size)*, y : LimbSpan) : Bool forall Size\n    large_add_from(x, y, 0)\n  end\n\n  # grade-school multiplication algorithm\n  def self.long_mul(x : Stackvec(Size)*, y : LimbSpan) : Bool forall Size\n    xs = x.value.to_slice\n    z = Stackvec(Size).new(xs)\n    zs = z.to_slice\n\n    if y.size != 0\n      y0 = y.unsafe_fetch(0)\n      fastfloat_try small_mul(x, y0)\n      (1...y.size).each do |index|\n        yi = y.unsafe_fetch(index)\n        zi = Stackvec(Size).new\n        if yi != 0\n          # re-use the same buffer throughout\n          zi.size = 0\n          fastfloat_try zi.try_extend(zs)\n          fastfloat_try small_mul(pointerof(zi), yi)\n          zis = zi.to_slice\n          fastfloat_try large_add_from(x, zis, index)\n        end\n      end\n    end\n\n    x.value.normalize\n    true\n  end\n\n  # grade-school multiplication algorithm\n  def self.large_mul(x : Stackvec(Size)*, y : LimbSpan) : Bool forall Size\n    if y.size == 1\n      fastfloat_try small_mul(x, y.unsafe_fetch(0))\n    else\n      fastfloat_try long_mul(x, y)\n    end\n    true\n  end\n\n  module Pow5Tables\n    LARGE_STEP = 135_u32\n\n    {% if compare_versions(Crystal::VERSION, \"1.16.0\") < 0 %}\n      SMALL_POWER_OF_5 = [\n        1_u64,\n        5_u64,\n        25_u64,\n        125_u64,\n        625_u64,\n        3125_u64,\n        15625_u64,\n        78125_u64,\n        390625_u64,\n        1953125_u64,\n        9765625_u64,\n        48828125_u64,\n        244140625_u64,\n        1220703125_u64,\n        6103515625_u64,\n        30517578125_u64,\n        152587890625_u64,\n        762939453125_u64,\n        3814697265625_u64,\n        19073486328125_u64,\n        95367431640625_u64,\n        476837158203125_u64,\n        2384185791015625_u64,\n        11920928955078125_u64,\n        59604644775390625_u64,\n        298023223876953125_u64,\n        1490116119384765625_u64,\n        7450580596923828125_u64,\n      ]\n\n      {% if Limb == UInt64 %}\n        LARGE_POWER_OF_5 = Slice[\n          1414648277510068013_u64, 9180637584431281687_u64, 4539964771860779200_u64,\n          10482974169319127550_u64, 198276706040285095_u64,\n        ]\n      {% else %}\n        LARGE_POWER_OF_5 = Slice[\n          4279965485_u32, 329373468_u32, 4020270615_u32, 2137533757_u32, 4287402176_u32,\n          1057042919_u32, 1071430142_u32, 2440757623_u32, 381945767_u32, 46164893_u32,\n        ]\n      {% end %}\n    {% else %}\n      SMALL_POWER_OF_5 = Slice(UInt64).literal(\n        1_u64,\n        5_u64,\n        25_u64,\n        125_u64,\n        625_u64,\n        3125_u64,\n        15625_u64,\n        78125_u64,\n        390625_u64,\n        1953125_u64,\n        9765625_u64,\n        48828125_u64,\n        244140625_u64,\n        1220703125_u64,\n        6103515625_u64,\n        30517578125_u64,\n        152587890625_u64,\n        762939453125_u64,\n        3814697265625_u64,\n        19073486328125_u64,\n        95367431640625_u64,\n        476837158203125_u64,\n        2384185791015625_u64,\n        11920928955078125_u64,\n        59604644775390625_u64,\n        298023223876953125_u64,\n        1490116119384765625_u64,\n        7450580596923828125_u64,\n      )\n\n      {% if Limb == UInt64 %}\n        LARGE_POWER_OF_5 = Slice(UInt64).literal(\n          1414648277510068013_u64, 9180637584431281687_u64, 4539964771860779200_u64,\n          10482974169319127550_u64, 198276706040285095_u64,\n        )\n      {% else %}\n        LARGE_POWER_OF_5 = Slice(UInt32).literal(\n          4279965485_u32, 329373468_u32, 4020270615_u32, 2137533757_u32, 4287402176_u32,\n          1057042919_u32, 1071430142_u32, 2440757623_u32, 381945767_u32, 46164893_u32,\n        )\n      {% end %}\n    {% end %}\n  end\n\n  # big integer type. implements a small subset of big integer\n  # arithmetic, using simple algorithms since asymptotically\n  # faster algorithms are slower for a small number of limbs.\n  # all operations assume the big-integer is normalized.\n  # NOTE(crystal): contrast with ::BigInt\n  struct Bigint\n    # storage of the limbs, in little-endian order.\n    @vec = Stackvec(BIGINT_LIMBS).new\n\n    def initialize\n    end\n\n    def initialize(value : UInt64)\n      {% if Limb == UInt64 %}\n        @vec.push_unchecked(value)\n      {% else %}\n        @vec.push_unchecked(value.to_u32!)\n        @vec.push_unchecked(value.unsafe_shr(32).to_u32!)\n      {% end %}\n      @vec.normalize\n    end\n\n    # get the high 64 bits from the vector, and if bits were truncated.\n    # this is to get the significant digits for the float.\n    # NOTE(crystal): returns also *truncated* by value\n    def hi64 : {UInt64, Bool}\n      {% if Limb == UInt64 %}\n        if @vec.empty?\n          FastFloat.empty_hi64\n        elsif @vec.size == 1\n          FastFloat.uint64_hi64(@vec.rindex(0))\n        else\n          result, truncated = FastFloat.uint64_hi64(@vec.rindex(0), @vec.rindex(1))\n          truncated ||= @vec.nonzero?(2)\n          {result, truncated}\n        end\n      {% else %}\n        if @vec.empty?\n          FastFloat.empty_hi64\n        elsif @vec.size == 1\n          FastFloat.uint32_hi64(@vec.rindex(0))\n        elsif @vec.size == 2\n          FastFloat.uint32_hi64(@vec.rindex(0), @vec.rindex(1))\n        else\n          result, truncated = FastFloat.uint32_hi64(@vec.rindex(0), @vec.rindex(1), @vec.rindex(2))\n          truncated ||= @vec.nonzero?(3)\n          {result, truncated}\n        end\n      {% end %}\n    end\n\n    # compare two big integers, returning the large value.\n    # assumes both are normalized. if the return value is\n    # negative, other is larger, if the return value is\n    # positive, this is larger, otherwise they are equal.\n    # the limbs are stored in little-endian order, so we\n    # must compare the limbs in ever order.\n    def compare(other : Bigint*) : Int32\n      if @vec.size > other.value.@vec.size\n        1\n      elsif @vec.size < other.value.@vec.size\n        -1\n      else\n        index = @vec.size\n        while index > 0\n          xi = @vec.unsafe_fetch(index &- 1)\n          yi = other.value.@vec.unsafe_fetch(index &- 1)\n          if xi > yi\n            return 1\n          elsif xi < yi\n            return -1\n          end\n          index &-= 1\n        end\n        0\n      end\n    end\n\n    # shift left each limb n bits, carrying over to the new limb\n    # returns true if we were able to shift all the digits.\n    def shl_bits(n : Int) : Bool\n      # Internally, for each item, we shift left by n, and add the previous\n      # right shifted limb-bits.\n      # For example, we transform (for u8) shifted left 2, to:\n      #      b10100100 b01000010\n      #      b10 b10010001 b00001000\n      shl = n\n      shr = LIMB_BITS &- n\n      prev = Limb.zero\n      index = 0\n      while index < @vec.size\n        xi = @vec.unsafe_fetch(index)\n        @vec.unsafe_put(index, xi.unsafe_shl(shl) | prev.unsafe_shr(shr))\n        prev = xi\n        index &+= 1\n      end\n\n      carry = prev.unsafe_shr(shr)\n      if carry != 0\n        return @vec.try_push(carry)\n      end\n      true\n    end\n\n    # move the limbs left by `n` limbs.\n    def shl_limbs(n : Int) : Bool\n      if n &+ @vec.size > @vec.capacity\n        false\n      elsif !@vec.empty?\n        # move limbs\n        dst = @vec.to_unsafe + n\n        src = @vec.to_unsafe\n        src.move_to(dst, @vec.size)\n        # fill in empty limbs\n        first = @vec.to_unsafe\n        n.times { |i| first[i] = 0 }\n        @vec.size = (@vec.size &+ n).to_u16!\n        true\n      else\n        true\n      end\n    end\n\n    # move the limbs left by `n` bits.\n    def shl(n : Int) : Bool\n      rem = n.unsafe_mod(LIMB_BITS)\n      div = n.unsafe_div(LIMB_BITS)\n      if rem != 0\n        FastFloat.fastfloat_try shl_bits(rem)\n      end\n      if div != 0\n        FastFloat.fastfloat_try shl_limbs(div)\n      end\n      true\n    end\n\n    # get the number of leading zeros in the bigint.\n    def ctlz : Int32\n      if @vec.empty?\n        0\n      else\n        @vec.rindex(0).leading_zeros_count.to_i32!\n      end\n    end\n\n    # get the number of bits in the bigint.\n    def bit_length : Int32\n      lz = ctlz\n      (LIMB_BITS &* @vec.size &- lz).to_i32!\n    end\n\n    def mul(y : Limb) : Bool\n      FastFloat.small_mul(pointerof(@vec), y)\n    end\n\n    def add(y : Limb) : Bool\n      FastFloat.small_add(pointerof(@vec), y)\n    end\n\n    # multiply as if by 2 raised to a power.\n    def pow2(exp : UInt32) : Bool\n      shl(exp)\n    end\n\n    # multiply as if by 5 raised to a power.\n    def pow5(exp : UInt32) : Bool\n      # multiply by a power of 5\n      large = Pow5Tables::LARGE_POWER_OF_5\n      while exp >= Pow5Tables::LARGE_STEP\n        FastFloat.fastfloat_try FastFloat.large_mul(pointerof(@vec), large)\n        exp &-= Pow5Tables::LARGE_STEP\n      end\n      small_step = {{ Limb == UInt64 ? 27_u32 : 13_u32 }}\n      max_native = {{ Limb == UInt64 ? 7450580596923828125_u64 : 1220703125_u32 }}\n      while exp >= small_step\n        FastFloat.fastfloat_try FastFloat.small_mul(pointerof(@vec), max_native)\n        exp &-= small_step\n      end\n      if exp != 0\n        FastFloat.fastfloat_try FastFloat.small_mul(pointerof(@vec), Limb.new!(Pow5Tables::SMALL_POWER_OF_5.unsafe_fetch(exp)))\n      end\n\n      true\n    end\n\n    # multiply as if by 10 raised to a power.\n    def pow10(exp : UInt32) : Bool\n      FastFloat.fastfloat_try pow5(exp)\n      pow2(exp)\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/decimal_to_binary.cr",
    "content": "require \"./float_common\"\nrequire \"./fast_table\"\n\nmodule Float::FastFloat\n  # This will compute or rather approximate w * 5**q and return a pair of 64-bit\n  # words approximating the result, with the \"high\" part corresponding to the\n  # most significant bits and the low part corresponding to the least significant\n  # bits.\n  def self.compute_product_approximation(q : Int64, w : UInt64, bit_precision : Int) : Value128\n    power_of_five_128 = Powers::POWER_OF_FIVE_128.to_unsafe\n\n    index = 2 &* (q &- Powers::SMALLEST_POWER_OF_FIVE)\n    # For small values of q, e.g., q in [0,27], the answer is always exact\n    # because The line value128 firstproduct = full_multiplication(w,\n    # power_of_five_128[index]); gives the exact answer.\n    firstproduct = w.to_u128! &* power_of_five_128[index]\n\n    precision_mask = bit_precision < 64 ? 0xFFFFFFFFFFFFFFFF_u64.unsafe_shr(bit_precision) : 0xFFFFFFFFFFFFFFFF_u64\n    if firstproduct.unsafe_shr(64).bits_set?(precision_mask) # could further guard with  (lower + w < lower)\n      # regarding the second product, we only need secondproduct.high, but our\n      # expectation is that the compiler will optimize this extra work away if\n      # needed.\n      secondproduct = w.to_u128! &* power_of_five_128[index &+ 1]\n      firstproduct &+= secondproduct.unsafe_shr(64)\n    end\n    Value128.new(firstproduct)\n  end\n\n  module Detail\n    # For q in (0,350), we have that\n    #  f = (((152170 + 65536) * q ) >> 16);\n    # is equal to\n    #   floor(p) + q\n    # where\n    #   p = log(5**q)/log(2) = q * log(5)/log(2)\n    #\n    # For negative values of q in (-400,0), we have that\n    #  f = (((152170 + 65536) * q ) >> 16);\n    # is equal to\n    #   -ceil(p) + q\n    # where\n    #   p = log(5**-q)/log(2) = -q * log(5)/log(2)\n    def self.power(q : Int32) : Int32\n      ((152170 &+ 65536) &* q).unsafe_shr(16) &+ 63\n    end\n  end\n\n  module BinaryFormat(T, EquivUint)\n    # create an adjusted mantissa, biased by the invalid power2\n    # for significant digits already multiplied by 10 ** q.\n    def compute_error_scaled(q : Int64, w : UInt64, lz : Int) : AdjustedMantissa\n      hilz = w.unsafe_shr(63).to_i32! ^ 1\n      bias = mantissa_explicit_bits &- minimum_exponent\n\n      AdjustedMantissa.new(\n        mantissa: w.unsafe_shl(hilz),\n        power2: Detail.power(q.to_i32!) &+ bias &- hilz &- lz &- 62 &+ INVALID_AM_BIAS,\n      )\n    end\n\n    # w * 10 ** q, without rounding the representation up.\n    # the power2 in the exponent will be adjusted by invalid_am_bias.\n    def compute_error(q : Int64, w : UInt64) : AdjustedMantissa\n      lz = w.leading_zeros_count.to_i32!\n      w = w.unsafe_shl(lz)\n      product = FastFloat.compute_product_approximation(q, w, mantissa_explicit_bits &+ 3)\n      compute_error_scaled(q, product.high, lz)\n    end\n\n    # w * 10 ** q\n    # The returned value should be a valid ieee64 number that simply need to be\n    # packed. However, in some very rare cases, the computation will fail. In such\n    # cases, we return an adjusted_mantissa with a negative power of 2: the caller\n    # should recompute in such cases.\n    def compute_float(q : Int64, w : UInt64) : AdjustedMantissa\n      if w == 0 || q < smallest_power_of_ten\n        # result should be zero\n        return AdjustedMantissa.new(\n          power2: 0,\n          mantissa: 0,\n        )\n      end\n      if q > largest_power_of_ten\n        # we want to get infinity:\n        return AdjustedMantissa.new(\n          power2: infinite_power,\n          mantissa: 0,\n        )\n      end\n      # At this point in time q is in [powers::smallest_power_of_five,\n      # powers::largest_power_of_five].\n\n      # We want the most significant bit of i to be 1. Shift if needed.\n      lz = w.leading_zeros_count\n      w = w.unsafe_shl(lz)\n\n      # The required precision is binary::mantissa_explicit_bits() + 3 because\n      # 1. We need the implicit bit\n      # 2. We need an extra bit for rounding purposes\n      # 3. We might lose a bit due to the \"upperbit\" routine (result too small,\n      # requiring a shift)\n\n      product = FastFloat.compute_product_approximation(q, w, mantissa_explicit_bits &+ 3)\n      # The computed 'product' is always sufficient.\n      # Mathematical proof:\n      # Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to\n      # appear) See script/mushtak_lemire.py\n\n      # The \"compute_product_approximation\" function can be slightly slower than a\n      # branchless approach: value128 product = compute_product(q, w); but in\n      # practice, we can win big with the compute_product_approximation if its\n      # additional branch is easily predicted. Which is best is data specific.\n      upperbit = product.high.unsafe_shr(63).to_i32!\n      shift = upperbit &+ 64 &- mantissa_explicit_bits &- 3\n\n      mantissa = product.high.unsafe_shr(shift)\n\n      power2 = (Detail.power(q.to_i32!) &+ upperbit &- lz &- minimum_exponent).to_i32!\n      if power2 <= 0 # we have a subnormal?\n        # Here have that answer.power2 <= 0 so -answer.power2 >= 0\n        if 1 &- power2 >= 64 # if we have more than 64 bits below the minimum exponent, you have a zero for sure.\n          # result should be zero\n          return AdjustedMantissa.new(\n            power2: 0,\n            mantissa: 0,\n          )\n        end\n        # next line is safe because -answer.power2 + 1 < 64\n        mantissa = mantissa.unsafe_shr(1 &- power2)\n        # Thankfully, we can't have both \"round-to-even\" and subnormals because\n        # \"round-to-even\" only occurs for powers close to 0.\n        mantissa &+= mantissa & 1\n        mantissa = mantissa.unsafe_shr(1)\n        # There is a weird scenario where we don't have a subnormal but just.\n        # Suppose we start with 2.2250738585072013e-308, we end up\n        # with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal\n        # whereas 0x40000000000000 x 2^-1023-53  is normal. Now, we need to round\n        # up 0x3fffffffffffff x 2^-1023-53  and once we do, we are no longer\n        # subnormal, but we can only know this after rounding.\n        # So we only declare a subnormal if we are smaller than the threshold.\n        power2 = mantissa < 1_u64.unsafe_shl(mantissa_explicit_bits) ? 0 : 1\n        return AdjustedMantissa.new(power2: power2, mantissa: mantissa)\n      end\n\n      # usually, we round *up*, but if we fall right in between and and we have an\n      # even basis, we need to round down\n      # We are only concerned with the cases where 5**q fits in single 64-bit word.\n      if product.low <= 1 && q >= min_exponent_round_to_even && q <= max_exponent_round_to_even && mantissa & 3 == 1\n        # we may fall between two floats!\n        # To be in-between two floats we need that in doing\n        #   answer.mantissa = product.high >> (upperbit + 64 -\n        #   binary::mantissa_explicit_bits() - 3);\n        # ... we dropped out only zeroes. But if this happened, then we can go\n        # back!!!\n        if mantissa.unsafe_shl(shift) == product.high\n          mantissa &= ~1_u64 # flip it so that we do not round up\n        end\n      end\n\n      mantissa &+= mantissa & 1 # round up\n      mantissa = mantissa.unsafe_shr(1)\n      if mantissa >= 2_u64.unsafe_shl(mantissa_explicit_bits)\n        mantissa = 1_u64.unsafe_shl(mantissa_explicit_bits)\n        power2 &+= 1 # undo previous addition\n      end\n\n      mantissa &= ~(1_u64.unsafe_shl(mantissa_explicit_bits))\n      if power2 >= infinite_power # infinity\n        return AdjustedMantissa.new(\n          power2: infinite_power,\n          mantissa: 0,\n        )\n      end\n      AdjustedMantissa.new(power2: power2, mantissa: mantissa)\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/digit_comparison.cr",
    "content": "require \"./float_common\"\nrequire \"./bigint\"\nrequire \"./ascii_number\"\n\nmodule Float::FastFloat\n  # 1e0 to 1e19\n  {% if compare_versions(Crystal::VERSION, \"1.16.0\") < 0 %}\n    POWERS_OF_TEN_UINT64 = [\n      1_u64,\n      10_u64,\n      100_u64,\n      1000_u64,\n      10000_u64,\n      100000_u64,\n      1000000_u64,\n      10000000_u64,\n      100000000_u64,\n      1000000000_u64,\n      10000000000_u64,\n      100000000000_u64,\n      1000000000000_u64,\n      10000000000000_u64,\n      100000000000000_u64,\n      1000000000000000_u64,\n      10000000000000000_u64,\n      100000000000000000_u64,\n      1000000000000000000_u64,\n      10000000000000000000_u64,\n    ]\n  {% else %}\n    POWERS_OF_TEN_UINT64 = Slice(UInt64).literal(\n      1_u64,\n      10_u64,\n      100_u64,\n      1000_u64,\n      10000_u64,\n      100000_u64,\n      1000000_u64,\n      10000000_u64,\n      100000000_u64,\n      1000000000_u64,\n      10000000000_u64,\n      100000000000_u64,\n      1000000000000_u64,\n      10000000000000_u64,\n      100000000000000_u64,\n      1000000000000000_u64,\n      10000000000000000_u64,\n      100000000000000000_u64,\n      1000000000000000000_u64,\n      10000000000000000000_u64,\n    )\n  {% end %}\n\n  # calculate the exponent, in scientific notation, of the number.\n  # this algorithm is not even close to optimized, but it has no practical\n  # effect on performance: in order to have a faster algorithm, we'd need\n  # to slow down performance for faster algorithms, and this is still fast.\n  def self.scientific_exponent(num : ParsedNumberStringT(UC)) : Int32 forall UC\n    mantissa = num.mantissa\n    exponent = num.exponent.to_i32!\n    while mantissa >= 10000\n      mantissa = mantissa.unsafe_div(10000)\n      exponent &+= 4\n    end\n    while mantissa >= 100\n      mantissa = mantissa.unsafe_div(100)\n      exponent &+= 2\n    end\n    while mantissa >= 10\n      mantissa = mantissa.unsafe_div(10)\n      exponent &+= 1\n    end\n    exponent\n  end\n\n  module BinaryFormat(T, EquivUint)\n    # this converts a native floating-point number to an extended-precision float.\n    def to_extended(value : T) : AdjustedMantissa\n      exponent_mask = self.exponent_mask\n      mantissa_mask = self.mantissa_mask\n      hidden_bit_mask = self.hidden_bit_mask\n\n      bias = mantissa_explicit_bits &- minimum_exponent\n      bits = value.unsafe_as(EquivUint)\n      if bits & exponent_mask == 0\n        # denormal\n        power2 = 1 &- bias\n        mantissa = bits & mantissa_mask\n      else\n        # normal\n        power2 = (bits & exponent_mask).unsafe_shr(mantissa_explicit_bits).to_i32!\n        power2 &-= bias\n        mantissa = (bits & mantissa_mask) | hidden_bit_mask\n      end\n\n      AdjustedMantissa.new(power2: power2, mantissa: mantissa.to_u64!)\n    end\n\n    # get the extended precision value of the halfway point between b and b+u.\n    # we are given a native float that represents b, so we need to adjust it\n    # halfway between b and b+u.\n    def to_extended_halfway(value : T) : AdjustedMantissa\n      am = to_extended(value)\n      am.mantissa = am.mantissa.unsafe_shl(1)\n      am.mantissa &+= 1\n      am.power2 &-= 1\n      am\n    end\n\n    # round an extended-precision float to the nearest machine float.\n    # NOTE(crystal): passes *am* in and out by value\n    def round(am : AdjustedMantissa, & : AdjustedMantissa, Int32 -> AdjustedMantissa) : AdjustedMantissa\n      mantissa_shift = 64 &- mantissa_explicit_bits &- 1\n      if 0 &- am.power2 >= mantissa_shift\n        # have a denormal float\n        shift = 1 &- am.power2\n        am = yield am, {shift, 64}.min\n        # check for round-up: if rounding-nearest carried us to the hidden bit.\n        am.power2 = am.mantissa < 1_u64.unsafe_shl(mantissa_explicit_bits) ? 0 : 1\n        return am\n      end\n\n      # have a normal float, use the default shift.\n      am = yield am, mantissa_shift\n\n      # check for carry\n      if am.mantissa >= 2_u64.unsafe_shl(mantissa_explicit_bits)\n        am.mantissa = 1_u64.unsafe_shl(mantissa_explicit_bits)\n        am.power2 &+= 1\n      end\n\n      # check for infinite: we could have carried to an infinite power\n      am.mantissa &= ~(1_u64.unsafe_shl(mantissa_explicit_bits))\n      if am.power2 >= infinite_power\n        am.power2 = infinite_power\n        am.mantissa = 0\n      end\n\n      am\n    end\n\n    # NOTE(crystal): passes *am* in and out by value\n    def round_nearest_tie_even(am : AdjustedMantissa, shift : Int32, & : Bool, Bool, Bool -> Bool) : AdjustedMantissa\n      mask = shift == 64 ? UInt64::MAX : 1_u64.unsafe_shl(shift) &- 1\n      halfway = shift == 0 ? 0_u64 : 1_u64.unsafe_shl(shift &- 1)\n      truncated_bits = am.mantissa & mask\n      is_above = truncated_bits > halfway\n      is_halfway = truncated_bits == halfway\n\n      # shift digits into position\n      if shift == 64\n        am.mantissa = 0\n      else\n        am.mantissa = am.mantissa.unsafe_shr(shift)\n      end\n      am.power2 &+= shift\n\n      is_odd = am.mantissa.bits_set?(1)\n      am.mantissa &+= (yield is_odd, is_halfway, is_above) ? 1 : 0\n      am\n    end\n\n    # NOTE(crystal): passes *am* in and out by value\n    def round_down(am : AdjustedMantissa, shift : Int32) : AdjustedMantissa\n      if shift == 64\n        am.mantissa = 0\n      else\n        am.mantissa = am.mantissa.unsafe_shr(shift)\n      end\n      am.power2 &+= shift\n      am\n    end\n\n    # NOTE(crystal): returns the new *first* by value\n    def skip_zeros(first : UC*, last : UC*) : UC* forall UC\n      int_cmp_len = FastFloat.int_cmp_len(UC)\n      int_cmp_zeros = FastFloat.int_cmp_zeros(UC)\n\n      val = uninitialized UInt64\n      while last - first >= int_cmp_len\n        first.copy_to(pointerof(val).as(UC*), int_cmp_len)\n        if val != int_cmp_zeros\n          break\n        end\n        first += int_cmp_len\n      end\n      while first != last\n        unless first.value === '0'\n          break\n        end\n        first += 1\n      end\n      first\n    end\n\n    # determine if any non-zero digits were truncated.\n    # all characters must be valid digits.\n    def is_truncated?(first : UC*, last : UC*) : Bool forall UC\n      int_cmp_len = FastFloat.int_cmp_len(UC)\n      int_cmp_zeros = FastFloat.int_cmp_zeros(UC)\n\n      # do 8-bit optimizations, can just compare to 8 literal 0s.\n\n      val = uninitialized UInt64\n      while last - first >= int_cmp_len\n        first.copy_to(pointerof(val).as(UC*), int_cmp_len)\n        if val != int_cmp_zeros\n          return true\n        end\n        first += int_cmp_len\n      end\n      while first != last\n        unless first.value === '0'\n          return true\n        end\n        first += 1\n      end\n      false\n    end\n\n    def is_truncated?(s : Slice(UC)) : Bool forall UC\n      is_truncated?(s.to_unsafe, s.to_unsafe + s.size)\n    end\n\n    macro parse_eight_digits(p, value, counter, count)\n      {{ value }} = {{ value }} &* 100000000 &+ FastFloat.parse_eight_digits_unrolled({{ p }})\n      {{ p }} += 8\n      {{ counter }} &+= 8\n      {{ count }} &+= 8\n    end\n\n    macro parse_one_digit(p, value, counter, count)\n      {{ value }} = {{ value }} &* 10 &+ {{ p }}.value &- '0'.ord\n      {{ p }} += 1\n      {{ counter }} &+= 1\n      {{ count }} &+= 1\n    end\n\n    macro add_native(big, power, value)\n      {{ big }}.value.mul({{ power }})\n      {{ big }}.value.add({{ value }})\n    end\n\n    macro round_up_bigint(big, count)\n      # need to round-up the digits, but need to avoid rounding\n      # ....9999 to ...10000, which could cause a false halfway point.\n      add_native({{ big }}, 10, 1)\n      {{ count }} &+= 1\n    end\n\n    # parse the significant digits into a big integer\n    # NOTE(crystal): returns the new *digits* by value\n    def parse_mantissa(result : Bigint*, num : ParsedNumberStringT(UC), max_digits : Int) : Int forall UC\n      # try to minimize the number of big integer and scalar multiplication.\n      # therefore, try to parse 8 digits at a time, and multiply by the largest\n      # scalar value (9 or 19 digits) for each step.\n      counter = 0\n      digits = 0\n      value = Limb.zero\n      step = {{ Limb == UInt64 ? 19 : 9 }}\n\n      # process all integer digits.\n      p = num.integer.to_unsafe\n      pend = p + num.integer.size\n      p = skip_zeros(p, pend)\n      # process all digits, in increments of step per loop\n      while p != pend\n        while pend - p >= 8 && step &- counter >= 8 && max_digits &- digits >= 8\n          parse_eight_digits(p, value, counter, digits)\n        end\n        while counter < step && p != pend && digits < max_digits\n          parse_one_digit(p, value, counter, digits)\n        end\n        if digits == max_digits\n          # add the temporary value, then check if we've truncated any digits\n          add_native(result, Limb.new!(POWERS_OF_TEN_UINT64.unsafe_fetch(counter)), value)\n          truncated = is_truncated?(p, pend)\n          unless num.fraction.empty?\n            truncated ||= is_truncated?(num.fraction)\n          end\n          if truncated\n            round_up_bigint(result, digits)\n          end\n          return digits\n        else\n          add_native(result, Limb.new!(POWERS_OF_TEN_UINT64.unsafe_fetch(counter)), value)\n          counter = 0\n          value = Limb.zero\n        end\n      end\n\n      # add our fraction digits, if they're available.\n      unless num.fraction.empty?\n        p = num.fraction.to_unsafe\n        pend = p + num.fraction.size\n        if digits == 0\n          p = skip_zeros(p, pend)\n        end\n        # process all digits, in increments of step per loop\n        while p != pend\n          while pend - p >= 8 && step &- counter >= 8 && max_digits &- digits >= 8\n            parse_eight_digits(p, value, counter, digits)\n          end\n          while counter < step && p != pend && digits < max_digits\n            parse_one_digit(p, value, counter, digits)\n          end\n          if digits == max_digits\n            # add the temporary value, then check if we've truncated any digits\n            add_native(result, Limb.new!(POWERS_OF_TEN_UINT64.unsafe_fetch(counter)), value)\n            truncated = is_truncated?(p, pend)\n            if truncated\n              round_up_bigint(result, digits)\n            end\n            return digits\n          else\n            add_native(result, Limb.new!(POWERS_OF_TEN_UINT64.unsafe_fetch(counter)), value)\n            counter = 0\n            value = Limb.zero\n          end\n        end\n      end\n\n      if counter != 0\n        add_native(result, Limb.new!(POWERS_OF_TEN_UINT64.unsafe_fetch(counter)), value)\n      end\n\n      digits\n    end\n\n    def positive_digit_comp(bigmant : Bigint*, exponent : Int32) : AdjustedMantissa\n      bigmant.value.pow10(exponent.to_u32!)\n      mantissa, truncated = bigmant.value.hi64\n      bias = mantissa_explicit_bits &- minimum_exponent\n      power2 = bigmant.value.bit_length &- 64 &+ bias\n      answer = AdjustedMantissa.new(power2: power2, mantissa: mantissa)\n\n      answer = round(answer) do |a, shift|\n        round_nearest_tie_even(a, shift) do |is_odd, is_halfway, is_above|\n          is_above || (is_halfway && truncated) || (is_odd && is_halfway)\n        end\n      end\n\n      answer\n    end\n\n    # the scaling here is quite simple: we have, for the real digits `m * 10^e`,\n    # and for the theoretical digits `n * 2^f`. Since `e` is always negative,\n    # to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.\n    # we then need to scale by `2^(f- e)`, and then the two significant digits\n    # are of the same magnitude.\n    def negative_digit_comp(bigmant : Bigint*, am : AdjustedMantissa, exponent : Int32) : AdjustedMantissa\n      real_digits = bigmant\n      real_exp = exponent\n\n      # get the value of `b`, rounded down, and get a bigint representation of b+h\n      am_b = round(am) do |a, shift|\n        round_down(a, shift)\n      end\n      b = to_float(false, am_b)\n      theor = to_extended_halfway(b)\n      theor_digits = Bigint.new(theor.mantissa)\n      theor_exp = theor.power2\n\n      # scale real digits and theor digits to be same power.\n      pow2_exp = theor_exp &- real_exp\n      pow5_exp = 0_u32 &- real_exp\n      if pow5_exp != 0\n        theor_digits.pow5(pow5_exp)\n      end\n      if pow2_exp > 0\n        theor_digits.pow2(pow2_exp.to_u32!)\n      elsif pow2_exp < 0\n        real_digits.value.pow2(0_u32 &- pow2_exp)\n      end\n\n      # compare digits, and use it to director rounding\n      ord = real_digits.value.compare(pointerof(theor_digits))\n      answer = round(am) do |a, shift|\n        round_nearest_tie_even(a, shift) do |is_odd, _, _|\n          if ord > 0\n            true\n          elsif ord < 0\n            false\n          else\n            is_odd\n          end\n        end\n      end\n\n      answer\n    end\n\n    # parse the significant digits as a big integer to unambiguously round the\n    # the significant digits. here, we are trying to determine how to round\n    # an extended float representation close to `b+h`, halfway between `b`\n    # (the float rounded-down) and `b+u`, the next positive float. this\n    # algorithm is always correct, and uses one of two approaches. when\n    # the exponent is positive relative to the significant digits (such as\n    # 1234), we create a big-integer representation, get the high 64-bits,\n    # determine if any lower bits are truncated, and use that to direct\n    # rounding. in case of a negative exponent relative to the significant\n    # digits (such as 1.2345), we create a theoretical representation of\n    # `b` as a big-integer type, scaled to the same binary exponent as\n    # the actual digits. we then compare the big integer representations\n    # of both, and use that to direct rounding.\n    def digit_comp(num : ParsedNumberStringT(UC), am : AdjustedMantissa) : AdjustedMantissa forall UC\n      # remove the invalid exponent bias\n      am.power2 &-= INVALID_AM_BIAS\n\n      sci_exp = FastFloat.scientific_exponent(num)\n      max_digits = self.max_digits\n      bigmant = Bigint.new\n      digits = parse_mantissa(pointerof(bigmant), num, max_digits)\n      # can't underflow, since digits is at most max_digits.\n      exponent = sci_exp &+ 1 &- digits\n      if exponent >= 0\n        positive_digit_comp(pointerof(bigmant), exponent)\n      else\n        negative_digit_comp(pointerof(bigmant), am, exponent)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/fast_table.cr",
    "content": "module Float::FastFloat\n  # When mapping numbers from decimal to binary,\n  # we go from w * 10^q to m * 2^p but we have\n  # 10^q = 5^q * 2^q, so effectively\n  # we are trying to match\n  # w * 2^q * 5^q to m * 2^p. Thus the powers of two\n  # are not a concern since they can be represented\n  # exactly using the binary notation, only the powers of five\n  # affect the binary significand.\n\n  # The smallest non-zero float (binary64) is 2^-1074.\n  # We take as input numbers of the form w x 10^q where w < 2^64.\n  # We have that w * 10^-343  <  2^(64-344) 5^-343 < 2^-1076.\n  # However, we have that\n  # (2^64-1) * 10^-342 =  (2^64-1) * 2^-342 * 5^-342 > 2^-1074.\n  # Thus it is possible for a number of the form w * 10^-342 where\n  # w is a 64-bit value to be a non-zero floating-point number.\n  #\n  # Any number of form w * 10^309 where w>= 1 is going to be\n  # infinite in binary64 so we never need to worry about powers\n  # of 5 greater than 308.\n  module Powers\n    SMALLEST_POWER_OF_FIVE = -342\n    LARGEST_POWER_OF_FIVE  =  308\n    NUMBER_OF_ENTRIES      = {{ 2 * (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) }}\n\n    # Powers of five from 5^-342 all the way to 5^308 rounded toward one.\n    # NOTE(crystal): this is very similar to\n    # `Float::Printer::Dragonbox::ImplInfo_Float64::CACHE`, except the endpoints\n    # are different and the rounding is in a different direction\n    {% if compare_versions(Crystal::VERSION, \"1.16.0\") < 0 %}\n      # TODO: this is needed to avoid generating lots of allocas\n      # in LLVM, which makes LLVM really slow. The compiler should\n      # try to avoid/reuse temporary allocas.\n      # Explanation: https://github.com/crystal-lang/crystal/issues/4516#issuecomment-306226171\n      private def self.put(array, value) : Nil\n        array << value\n      end\n\n      POWER_OF_FIVE_128 = begin\n        array = Array(UInt64).new(NUMBER_OF_ENTRIES)\n        put(array, 0xeef453d6923bd65a_u64); put(array, 0x113faa2906a13b3f_u64)\n        put(array, 0x9558b4661b6565f8_u64); put(array, 0x4ac7ca59a424c507_u64)\n        put(array, 0xbaaee17fa23ebf76_u64); put(array, 0x5d79bcf00d2df649_u64)\n        put(array, 0xe95a99df8ace6f53_u64); put(array, 0xf4d82c2c107973dc_u64)\n        put(array, 0x91d8a02bb6c10594_u64); put(array, 0x79071b9b8a4be869_u64)\n        put(array, 0xb64ec836a47146f9_u64); put(array, 0x9748e2826cdee284_u64)\n        put(array, 0xe3e27a444d8d98b7_u64); put(array, 0xfd1b1b2308169b25_u64)\n        put(array, 0x8e6d8c6ab0787f72_u64); put(array, 0xfe30f0f5e50e20f7_u64)\n        put(array, 0xb208ef855c969f4f_u64); put(array, 0xbdbd2d335e51a935_u64)\n        put(array, 0xde8b2b66b3bc4723_u64); put(array, 0xad2c788035e61382_u64)\n        put(array, 0x8b16fb203055ac76_u64); put(array, 0x4c3bcb5021afcc31_u64)\n        put(array, 0xaddcb9e83c6b1793_u64); put(array, 0xdf4abe242a1bbf3d_u64)\n        put(array, 0xd953e8624b85dd78_u64); put(array, 0xd71d6dad34a2af0d_u64)\n        put(array, 0x87d4713d6f33aa6b_u64); put(array, 0x8672648c40e5ad68_u64)\n        put(array, 0xa9c98d8ccb009506_u64); put(array, 0x680efdaf511f18c2_u64)\n        put(array, 0xd43bf0effdc0ba48_u64); put(array, 0x212bd1b2566def2_u64)\n        put(array, 0x84a57695fe98746d_u64); put(array, 0x14bb630f7604b57_u64)\n        put(array, 0xa5ced43b7e3e9188_u64); put(array, 0x419ea3bd35385e2d_u64)\n        put(array, 0xcf42894a5dce35ea_u64); put(array, 0x52064cac828675b9_u64)\n        put(array, 0x818995ce7aa0e1b2_u64); put(array, 0x7343efebd1940993_u64)\n        put(array, 0xa1ebfb4219491a1f_u64); put(array, 0x1014ebe6c5f90bf8_u64)\n        put(array, 0xca66fa129f9b60a6_u64); put(array, 0xd41a26e077774ef6_u64)\n        put(array, 0xfd00b897478238d0_u64); put(array, 0x8920b098955522b4_u64)\n        put(array, 0x9e20735e8cb16382_u64); put(array, 0x55b46e5f5d5535b0_u64)\n        put(array, 0xc5a890362fddbc62_u64); put(array, 0xeb2189f734aa831d_u64)\n        put(array, 0xf712b443bbd52b7b_u64); put(array, 0xa5e9ec7501d523e4_u64)\n        put(array, 0x9a6bb0aa55653b2d_u64); put(array, 0x47b233c92125366e_u64)\n        put(array, 0xc1069cd4eabe89f8_u64); put(array, 0x999ec0bb696e840a_u64)\n        put(array, 0xf148440a256e2c76_u64); put(array, 0xc00670ea43ca250d_u64)\n        put(array, 0x96cd2a865764dbca_u64); put(array, 0x380406926a5e5728_u64)\n        put(array, 0xbc807527ed3e12bc_u64); put(array, 0xc605083704f5ecf2_u64)\n        put(array, 0xeba09271e88d976b_u64); put(array, 0xf7864a44c633682e_u64)\n        put(array, 0x93445b8731587ea3_u64); put(array, 0x7ab3ee6afbe0211d_u64)\n        put(array, 0xb8157268fdae9e4c_u64); put(array, 0x5960ea05bad82964_u64)\n        put(array, 0xe61acf033d1a45df_u64); put(array, 0x6fb92487298e33bd_u64)\n        put(array, 0x8fd0c16206306bab_u64); put(array, 0xa5d3b6d479f8e056_u64)\n        put(array, 0xb3c4f1ba87bc8696_u64); put(array, 0x8f48a4899877186c_u64)\n        put(array, 0xe0b62e2929aba83c_u64); put(array, 0x331acdabfe94de87_u64)\n        put(array, 0x8c71dcd9ba0b4925_u64); put(array, 0x9ff0c08b7f1d0b14_u64)\n        put(array, 0xaf8e5410288e1b6f_u64); put(array, 0x7ecf0ae5ee44dd9_u64)\n        put(array, 0xdb71e91432b1a24a_u64); put(array, 0xc9e82cd9f69d6150_u64)\n        put(array, 0x892731ac9faf056e_u64); put(array, 0xbe311c083a225cd2_u64)\n        put(array, 0xab70fe17c79ac6ca_u64); put(array, 0x6dbd630a48aaf406_u64)\n        put(array, 0xd64d3d9db981787d_u64); put(array, 0x92cbbccdad5b108_u64)\n        put(array, 0x85f0468293f0eb4e_u64); put(array, 0x25bbf56008c58ea5_u64)\n        put(array, 0xa76c582338ed2621_u64); put(array, 0xaf2af2b80af6f24e_u64)\n        put(array, 0xd1476e2c07286faa_u64); put(array, 0x1af5af660db4aee1_u64)\n        put(array, 0x82cca4db847945ca_u64); put(array, 0x50d98d9fc890ed4d_u64)\n        put(array, 0xa37fce126597973c_u64); put(array, 0xe50ff107bab528a0_u64)\n        put(array, 0xcc5fc196fefd7d0c_u64); put(array, 0x1e53ed49a96272c8_u64)\n        put(array, 0xff77b1fcbebcdc4f_u64); put(array, 0x25e8e89c13bb0f7a_u64)\n        put(array, 0x9faacf3df73609b1_u64); put(array, 0x77b191618c54e9ac_u64)\n        put(array, 0xc795830d75038c1d_u64); put(array, 0xd59df5b9ef6a2417_u64)\n        put(array, 0xf97ae3d0d2446f25_u64); put(array, 0x4b0573286b44ad1d_u64)\n        put(array, 0x9becce62836ac577_u64); put(array, 0x4ee367f9430aec32_u64)\n        put(array, 0xc2e801fb244576d5_u64); put(array, 0x229c41f793cda73f_u64)\n        put(array, 0xf3a20279ed56d48a_u64); put(array, 0x6b43527578c1110f_u64)\n        put(array, 0x9845418c345644d6_u64); put(array, 0x830a13896b78aaa9_u64)\n        put(array, 0xbe5691ef416bd60c_u64); put(array, 0x23cc986bc656d553_u64)\n        put(array, 0xedec366b11c6cb8f_u64); put(array, 0x2cbfbe86b7ec8aa8_u64)\n        put(array, 0x94b3a202eb1c3f39_u64); put(array, 0x7bf7d71432f3d6a9_u64)\n        put(array, 0xb9e08a83a5e34f07_u64); put(array, 0xdaf5ccd93fb0cc53_u64)\n        put(array, 0xe858ad248f5c22c9_u64); put(array, 0xd1b3400f8f9cff68_u64)\n        put(array, 0x91376c36d99995be_u64); put(array, 0x23100809b9c21fa1_u64)\n        put(array, 0xb58547448ffffb2d_u64); put(array, 0xabd40a0c2832a78a_u64)\n        put(array, 0xe2e69915b3fff9f9_u64); put(array, 0x16c90c8f323f516c_u64)\n        put(array, 0x8dd01fad907ffc3b_u64); put(array, 0xae3da7d97f6792e3_u64)\n        put(array, 0xb1442798f49ffb4a_u64); put(array, 0x99cd11cfdf41779c_u64)\n        put(array, 0xdd95317f31c7fa1d_u64); put(array, 0x40405643d711d583_u64)\n        put(array, 0x8a7d3eef7f1cfc52_u64); put(array, 0x482835ea666b2572_u64)\n        put(array, 0xad1c8eab5ee43b66_u64); put(array, 0xda3243650005eecf_u64)\n        put(array, 0xd863b256369d4a40_u64); put(array, 0x90bed43e40076a82_u64)\n        put(array, 0x873e4f75e2224e68_u64); put(array, 0x5a7744a6e804a291_u64)\n        put(array, 0xa90de3535aaae202_u64); put(array, 0x711515d0a205cb36_u64)\n        put(array, 0xd3515c2831559a83_u64); put(array, 0xd5a5b44ca873e03_u64)\n        put(array, 0x8412d9991ed58091_u64); put(array, 0xe858790afe9486c2_u64)\n        put(array, 0xa5178fff668ae0b6_u64); put(array, 0x626e974dbe39a872_u64)\n        put(array, 0xce5d73ff402d98e3_u64); put(array, 0xfb0a3d212dc8128f_u64)\n        put(array, 0x80fa687f881c7f8e_u64); put(array, 0x7ce66634bc9d0b99_u64)\n        put(array, 0xa139029f6a239f72_u64); put(array, 0x1c1fffc1ebc44e80_u64)\n        put(array, 0xc987434744ac874e_u64); put(array, 0xa327ffb266b56220_u64)\n        put(array, 0xfbe9141915d7a922_u64); put(array, 0x4bf1ff9f0062baa8_u64)\n        put(array, 0x9d71ac8fada6c9b5_u64); put(array, 0x6f773fc3603db4a9_u64)\n        put(array, 0xc4ce17b399107c22_u64); put(array, 0xcb550fb4384d21d3_u64)\n        put(array, 0xf6019da07f549b2b_u64); put(array, 0x7e2a53a146606a48_u64)\n        put(array, 0x99c102844f94e0fb_u64); put(array, 0x2eda7444cbfc426d_u64)\n        put(array, 0xc0314325637a1939_u64); put(array, 0xfa911155fefb5308_u64)\n        put(array, 0xf03d93eebc589f88_u64); put(array, 0x793555ab7eba27ca_u64)\n        put(array, 0x96267c7535b763b5_u64); put(array, 0x4bc1558b2f3458de_u64)\n        put(array, 0xbbb01b9283253ca2_u64); put(array, 0x9eb1aaedfb016f16_u64)\n        put(array, 0xea9c227723ee8bcb_u64); put(array, 0x465e15a979c1cadc_u64)\n        put(array, 0x92a1958a7675175f_u64); put(array, 0xbfacd89ec191ec9_u64)\n        put(array, 0xb749faed14125d36_u64); put(array, 0xcef980ec671f667b_u64)\n        put(array, 0xe51c79a85916f484_u64); put(array, 0x82b7e12780e7401a_u64)\n        put(array, 0x8f31cc0937ae58d2_u64); put(array, 0xd1b2ecb8b0908810_u64)\n        put(array, 0xb2fe3f0b8599ef07_u64); put(array, 0x861fa7e6dcb4aa15_u64)\n        put(array, 0xdfbdcece67006ac9_u64); put(array, 0x67a791e093e1d49a_u64)\n        put(array, 0x8bd6a141006042bd_u64); put(array, 0xe0c8bb2c5c6d24e0_u64)\n        put(array, 0xaecc49914078536d_u64); put(array, 0x58fae9f773886e18_u64)\n        put(array, 0xda7f5bf590966848_u64); put(array, 0xaf39a475506a899e_u64)\n        put(array, 0x888f99797a5e012d_u64); put(array, 0x6d8406c952429603_u64)\n        put(array, 0xaab37fd7d8f58178_u64); put(array, 0xc8e5087ba6d33b83_u64)\n        put(array, 0xd5605fcdcf32e1d6_u64); put(array, 0xfb1e4a9a90880a64_u64)\n        put(array, 0x855c3be0a17fcd26_u64); put(array, 0x5cf2eea09a55067f_u64)\n        put(array, 0xa6b34ad8c9dfc06f_u64); put(array, 0xf42faa48c0ea481e_u64)\n        put(array, 0xd0601d8efc57b08b_u64); put(array, 0xf13b94daf124da26_u64)\n        put(array, 0x823c12795db6ce57_u64); put(array, 0x76c53d08d6b70858_u64)\n        put(array, 0xa2cb1717b52481ed_u64); put(array, 0x54768c4b0c64ca6e_u64)\n        put(array, 0xcb7ddcdda26da268_u64); put(array, 0xa9942f5dcf7dfd09_u64)\n        put(array, 0xfe5d54150b090b02_u64); put(array, 0xd3f93b35435d7c4c_u64)\n        put(array, 0x9efa548d26e5a6e1_u64); put(array, 0xc47bc5014a1a6daf_u64)\n        put(array, 0xc6b8e9b0709f109a_u64); put(array, 0x359ab6419ca1091b_u64)\n        put(array, 0xf867241c8cc6d4c0_u64); put(array, 0xc30163d203c94b62_u64)\n        put(array, 0x9b407691d7fc44f8_u64); put(array, 0x79e0de63425dcf1d_u64)\n        put(array, 0xc21094364dfb5636_u64); put(array, 0x985915fc12f542e4_u64)\n        put(array, 0xf294b943e17a2bc4_u64); put(array, 0x3e6f5b7b17b2939d_u64)\n        put(array, 0x979cf3ca6cec5b5a_u64); put(array, 0xa705992ceecf9c42_u64)\n        put(array, 0xbd8430bd08277231_u64); put(array, 0x50c6ff782a838353_u64)\n        put(array, 0xece53cec4a314ebd_u64); put(array, 0xa4f8bf5635246428_u64)\n        put(array, 0x940f4613ae5ed136_u64); put(array, 0x871b7795e136be99_u64)\n        put(array, 0xb913179899f68584_u64); put(array, 0x28e2557b59846e3f_u64)\n        put(array, 0xe757dd7ec07426e5_u64); put(array, 0x331aeada2fe589cf_u64)\n        put(array, 0x9096ea6f3848984f_u64); put(array, 0x3ff0d2c85def7621_u64)\n        put(array, 0xb4bca50b065abe63_u64); put(array, 0xfed077a756b53a9_u64)\n        put(array, 0xe1ebce4dc7f16dfb_u64); put(array, 0xd3e8495912c62894_u64)\n        put(array, 0x8d3360f09cf6e4bd_u64); put(array, 0x64712dd7abbbd95c_u64)\n        put(array, 0xb080392cc4349dec_u64); put(array, 0xbd8d794d96aacfb3_u64)\n        put(array, 0xdca04777f541c567_u64); put(array, 0xecf0d7a0fc5583a0_u64)\n        put(array, 0x89e42caaf9491b60_u64); put(array, 0xf41686c49db57244_u64)\n        put(array, 0xac5d37d5b79b6239_u64); put(array, 0x311c2875c522ced5_u64)\n        put(array, 0xd77485cb25823ac7_u64); put(array, 0x7d633293366b828b_u64)\n        put(array, 0x86a8d39ef77164bc_u64); put(array, 0xae5dff9c02033197_u64)\n        put(array, 0xa8530886b54dbdeb_u64); put(array, 0xd9f57f830283fdfc_u64)\n        put(array, 0xd267caa862a12d66_u64); put(array, 0xd072df63c324fd7b_u64)\n        put(array, 0x8380dea93da4bc60_u64); put(array, 0x4247cb9e59f71e6d_u64)\n        put(array, 0xa46116538d0deb78_u64); put(array, 0x52d9be85f074e608_u64)\n        put(array, 0xcd795be870516656_u64); put(array, 0x67902e276c921f8b_u64)\n        put(array, 0x806bd9714632dff6_u64); put(array, 0xba1cd8a3db53b6_u64)\n        put(array, 0xa086cfcd97bf97f3_u64); put(array, 0x80e8a40eccd228a4_u64)\n        put(array, 0xc8a883c0fdaf7df0_u64); put(array, 0x6122cd128006b2cd_u64)\n        put(array, 0xfad2a4b13d1b5d6c_u64); put(array, 0x796b805720085f81_u64)\n        put(array, 0x9cc3a6eec6311a63_u64); put(array, 0xcbe3303674053bb0_u64)\n        put(array, 0xc3f490aa77bd60fc_u64); put(array, 0xbedbfc4411068a9c_u64)\n        put(array, 0xf4f1b4d515acb93b_u64); put(array, 0xee92fb5515482d44_u64)\n        put(array, 0x991711052d8bf3c5_u64); put(array, 0x751bdd152d4d1c4a_u64)\n        put(array, 0xbf5cd54678eef0b6_u64); put(array, 0xd262d45a78a0635d_u64)\n        put(array, 0xef340a98172aace4_u64); put(array, 0x86fb897116c87c34_u64)\n        put(array, 0x9580869f0e7aac0e_u64); put(array, 0xd45d35e6ae3d4da0_u64)\n        put(array, 0xbae0a846d2195712_u64); put(array, 0x8974836059cca109_u64)\n        put(array, 0xe998d258869facd7_u64); put(array, 0x2bd1a438703fc94b_u64)\n        put(array, 0x91ff83775423cc06_u64); put(array, 0x7b6306a34627ddcf_u64)\n        put(array, 0xb67f6455292cbf08_u64); put(array, 0x1a3bc84c17b1d542_u64)\n        put(array, 0xe41f3d6a7377eeca_u64); put(array, 0x20caba5f1d9e4a93_u64)\n        put(array, 0x8e938662882af53e_u64); put(array, 0x547eb47b7282ee9c_u64)\n        put(array, 0xb23867fb2a35b28d_u64); put(array, 0xe99e619a4f23aa43_u64)\n        put(array, 0xdec681f9f4c31f31_u64); put(array, 0x6405fa00e2ec94d4_u64)\n        put(array, 0x8b3c113c38f9f37e_u64); put(array, 0xde83bc408dd3dd04_u64)\n        put(array, 0xae0b158b4738705e_u64); put(array, 0x9624ab50b148d445_u64)\n        put(array, 0xd98ddaee19068c76_u64); put(array, 0x3badd624dd9b0957_u64)\n        put(array, 0x87f8a8d4cfa417c9_u64); put(array, 0xe54ca5d70a80e5d6_u64)\n        put(array, 0xa9f6d30a038d1dbc_u64); put(array, 0x5e9fcf4ccd211f4c_u64)\n        put(array, 0xd47487cc8470652b_u64); put(array, 0x7647c3200069671f_u64)\n        put(array, 0x84c8d4dfd2c63f3b_u64); put(array, 0x29ecd9f40041e073_u64)\n        put(array, 0xa5fb0a17c777cf09_u64); put(array, 0xf468107100525890_u64)\n        put(array, 0xcf79cc9db955c2cc_u64); put(array, 0x7182148d4066eeb4_u64)\n        put(array, 0x81ac1fe293d599bf_u64); put(array, 0xc6f14cd848405530_u64)\n        put(array, 0xa21727db38cb002f_u64); put(array, 0xb8ada00e5a506a7c_u64)\n        put(array, 0xca9cf1d206fdc03b_u64); put(array, 0xa6d90811f0e4851c_u64)\n        put(array, 0xfd442e4688bd304a_u64); put(array, 0x908f4a166d1da663_u64)\n        put(array, 0x9e4a9cec15763e2e_u64); put(array, 0x9a598e4e043287fe_u64)\n        put(array, 0xc5dd44271ad3cdba_u64); put(array, 0x40eff1e1853f29fd_u64)\n        put(array, 0xf7549530e188c128_u64); put(array, 0xd12bee59e68ef47c_u64)\n        put(array, 0x9a94dd3e8cf578b9_u64); put(array, 0x82bb74f8301958ce_u64)\n        put(array, 0xc13a148e3032d6e7_u64); put(array, 0xe36a52363c1faf01_u64)\n        put(array, 0xf18899b1bc3f8ca1_u64); put(array, 0xdc44e6c3cb279ac1_u64)\n        put(array, 0x96f5600f15a7b7e5_u64); put(array, 0x29ab103a5ef8c0b9_u64)\n        put(array, 0xbcb2b812db11a5de_u64); put(array, 0x7415d448f6b6f0e7_u64)\n        put(array, 0xebdf661791d60f56_u64); put(array, 0x111b495b3464ad21_u64)\n        put(array, 0x936b9fcebb25c995_u64); put(array, 0xcab10dd900beec34_u64)\n        put(array, 0xb84687c269ef3bfb_u64); put(array, 0x3d5d514f40eea742_u64)\n        put(array, 0xe65829b3046b0afa_u64); put(array, 0xcb4a5a3112a5112_u64)\n        put(array, 0x8ff71a0fe2c2e6dc_u64); put(array, 0x47f0e785eaba72ab_u64)\n        put(array, 0xb3f4e093db73a093_u64); put(array, 0x59ed216765690f56_u64)\n        put(array, 0xe0f218b8d25088b8_u64); put(array, 0x306869c13ec3532c_u64)\n        put(array, 0x8c974f7383725573_u64); put(array, 0x1e414218c73a13fb_u64)\n        put(array, 0xafbd2350644eeacf_u64); put(array, 0xe5d1929ef90898fa_u64)\n        put(array, 0xdbac6c247d62a583_u64); put(array, 0xdf45f746b74abf39_u64)\n        put(array, 0x894bc396ce5da772_u64); put(array, 0x6b8bba8c328eb783_u64)\n        put(array, 0xab9eb47c81f5114f_u64); put(array, 0x66ea92f3f326564_u64)\n        put(array, 0xd686619ba27255a2_u64); put(array, 0xc80a537b0efefebd_u64)\n        put(array, 0x8613fd0145877585_u64); put(array, 0xbd06742ce95f5f36_u64)\n        put(array, 0xa798fc4196e952e7_u64); put(array, 0x2c48113823b73704_u64)\n        put(array, 0xd17f3b51fca3a7a0_u64); put(array, 0xf75a15862ca504c5_u64)\n        put(array, 0x82ef85133de648c4_u64); put(array, 0x9a984d73dbe722fb_u64)\n        put(array, 0xa3ab66580d5fdaf5_u64); put(array, 0xc13e60d0d2e0ebba_u64)\n        put(array, 0xcc963fee10b7d1b3_u64); put(array, 0x318df905079926a8_u64)\n        put(array, 0xffbbcfe994e5c61f_u64); put(array, 0xfdf17746497f7052_u64)\n        put(array, 0x9fd561f1fd0f9bd3_u64); put(array, 0xfeb6ea8bedefa633_u64)\n        put(array, 0xc7caba6e7c5382c8_u64); put(array, 0xfe64a52ee96b8fc0_u64)\n        put(array, 0xf9bd690a1b68637b_u64); put(array, 0x3dfdce7aa3c673b0_u64)\n        put(array, 0x9c1661a651213e2d_u64); put(array, 0x6bea10ca65c084e_u64)\n        put(array, 0xc31bfa0fe5698db8_u64); put(array, 0x486e494fcff30a62_u64)\n        put(array, 0xf3e2f893dec3f126_u64); put(array, 0x5a89dba3c3efccfa_u64)\n        put(array, 0x986ddb5c6b3a76b7_u64); put(array, 0xf89629465a75e01c_u64)\n        put(array, 0xbe89523386091465_u64); put(array, 0xf6bbb397f1135823_u64)\n        put(array, 0xee2ba6c0678b597f_u64); put(array, 0x746aa07ded582e2c_u64)\n        put(array, 0x94db483840b717ef_u64); put(array, 0xa8c2a44eb4571cdc_u64)\n        put(array, 0xba121a4650e4ddeb_u64); put(array, 0x92f34d62616ce413_u64)\n        put(array, 0xe896a0d7e51e1566_u64); put(array, 0x77b020baf9c81d17_u64)\n        put(array, 0x915e2486ef32cd60_u64); put(array, 0xace1474dc1d122e_u64)\n        put(array, 0xb5b5ada8aaff80b8_u64); put(array, 0xd819992132456ba_u64)\n        put(array, 0xe3231912d5bf60e6_u64); put(array, 0x10e1fff697ed6c69_u64)\n        put(array, 0x8df5efabc5979c8f_u64); put(array, 0xca8d3ffa1ef463c1_u64)\n        put(array, 0xb1736b96b6fd83b3_u64); put(array, 0xbd308ff8a6b17cb2_u64)\n        put(array, 0xddd0467c64bce4a0_u64); put(array, 0xac7cb3f6d05ddbde_u64)\n        put(array, 0x8aa22c0dbef60ee4_u64); put(array, 0x6bcdf07a423aa96b_u64)\n        put(array, 0xad4ab7112eb3929d_u64); put(array, 0x86c16c98d2c953c6_u64)\n        put(array, 0xd89d64d57a607744_u64); put(array, 0xe871c7bf077ba8b7_u64)\n        put(array, 0x87625f056c7c4a8b_u64); put(array, 0x11471cd764ad4972_u64)\n        put(array, 0xa93af6c6c79b5d2d_u64); put(array, 0xd598e40d3dd89bcf_u64)\n        put(array, 0xd389b47879823479_u64); put(array, 0x4aff1d108d4ec2c3_u64)\n        put(array, 0x843610cb4bf160cb_u64); put(array, 0xcedf722a585139ba_u64)\n        put(array, 0xa54394fe1eedb8fe_u64); put(array, 0xc2974eb4ee658828_u64)\n        put(array, 0xce947a3da6a9273e_u64); put(array, 0x733d226229feea32_u64)\n        put(array, 0x811ccc668829b887_u64); put(array, 0x806357d5a3f525f_u64)\n        put(array, 0xa163ff802a3426a8_u64); put(array, 0xca07c2dcb0cf26f7_u64)\n        put(array, 0xc9bcff6034c13052_u64); put(array, 0xfc89b393dd02f0b5_u64)\n        put(array, 0xfc2c3f3841f17c67_u64); put(array, 0xbbac2078d443ace2_u64)\n        put(array, 0x9d9ba7832936edc0_u64); put(array, 0xd54b944b84aa4c0d_u64)\n        put(array, 0xc5029163f384a931_u64); put(array, 0xa9e795e65d4df11_u64)\n        put(array, 0xf64335bcf065d37d_u64); put(array, 0x4d4617b5ff4a16d5_u64)\n        put(array, 0x99ea0196163fa42e_u64); put(array, 0x504bced1bf8e4e45_u64)\n        put(array, 0xc06481fb9bcf8d39_u64); put(array, 0xe45ec2862f71e1d6_u64)\n        put(array, 0xf07da27a82c37088_u64); put(array, 0x5d767327bb4e5a4c_u64)\n        put(array, 0x964e858c91ba2655_u64); put(array, 0x3a6a07f8d510f86f_u64)\n        put(array, 0xbbe226efb628afea_u64); put(array, 0x890489f70a55368b_u64)\n        put(array, 0xeadab0aba3b2dbe5_u64); put(array, 0x2b45ac74ccea842e_u64)\n        put(array, 0x92c8ae6b464fc96f_u64); put(array, 0x3b0b8bc90012929d_u64)\n        put(array, 0xb77ada0617e3bbcb_u64); put(array, 0x9ce6ebb40173744_u64)\n        put(array, 0xe55990879ddcaabd_u64); put(array, 0xcc420a6a101d0515_u64)\n        put(array, 0x8f57fa54c2a9eab6_u64); put(array, 0x9fa946824a12232d_u64)\n        put(array, 0xb32df8e9f3546564_u64); put(array, 0x47939822dc96abf9_u64)\n        put(array, 0xdff9772470297ebd_u64); put(array, 0x59787e2b93bc56f7_u64)\n        put(array, 0x8bfbea76c619ef36_u64); put(array, 0x57eb4edb3c55b65a_u64)\n        put(array, 0xaefae51477a06b03_u64); put(array, 0xede622920b6b23f1_u64)\n        put(array, 0xdab99e59958885c4_u64); put(array, 0xe95fab368e45eced_u64)\n        put(array, 0x88b402f7fd75539b_u64); put(array, 0x11dbcb0218ebb414_u64)\n        put(array, 0xaae103b5fcd2a881_u64); put(array, 0xd652bdc29f26a119_u64)\n        put(array, 0xd59944a37c0752a2_u64); put(array, 0x4be76d3346f0495f_u64)\n        put(array, 0x857fcae62d8493a5_u64); put(array, 0x6f70a4400c562ddb_u64)\n        put(array, 0xa6dfbd9fb8e5b88e_u64); put(array, 0xcb4ccd500f6bb952_u64)\n        put(array, 0xd097ad07a71f26b2_u64); put(array, 0x7e2000a41346a7a7_u64)\n        put(array, 0x825ecc24c873782f_u64); put(array, 0x8ed400668c0c28c8_u64)\n        put(array, 0xa2f67f2dfa90563b_u64); put(array, 0x728900802f0f32fa_u64)\n        put(array, 0xcbb41ef979346bca_u64); put(array, 0x4f2b40a03ad2ffb9_u64)\n        put(array, 0xfea126b7d78186bc_u64); put(array, 0xe2f610c84987bfa8_u64)\n        put(array, 0x9f24b832e6b0f436_u64); put(array, 0xdd9ca7d2df4d7c9_u64)\n        put(array, 0xc6ede63fa05d3143_u64); put(array, 0x91503d1c79720dbb_u64)\n        put(array, 0xf8a95fcf88747d94_u64); put(array, 0x75a44c6397ce912a_u64)\n        put(array, 0x9b69dbe1b548ce7c_u64); put(array, 0xc986afbe3ee11aba_u64)\n        put(array, 0xc24452da229b021b_u64); put(array, 0xfbe85badce996168_u64)\n        put(array, 0xf2d56790ab41c2a2_u64); put(array, 0xfae27299423fb9c3_u64)\n        put(array, 0x97c560ba6b0919a5_u64); put(array, 0xdccd879fc967d41a_u64)\n        put(array, 0xbdb6b8e905cb600f_u64); put(array, 0x5400e987bbc1c920_u64)\n        put(array, 0xed246723473e3813_u64); put(array, 0x290123e9aab23b68_u64)\n        put(array, 0x9436c0760c86e30b_u64); put(array, 0xf9a0b6720aaf6521_u64)\n        put(array, 0xb94470938fa89bce_u64); put(array, 0xf808e40e8d5b3e69_u64)\n        put(array, 0xe7958cb87392c2c2_u64); put(array, 0xb60b1d1230b20e04_u64)\n        put(array, 0x90bd77f3483bb9b9_u64); put(array, 0xb1c6f22b5e6f48c2_u64)\n        put(array, 0xb4ecd5f01a4aa828_u64); put(array, 0x1e38aeb6360b1af3_u64)\n        put(array, 0xe2280b6c20dd5232_u64); put(array, 0x25c6da63c38de1b0_u64)\n        put(array, 0x8d590723948a535f_u64); put(array, 0x579c487e5a38ad0e_u64)\n        put(array, 0xb0af48ec79ace837_u64); put(array, 0x2d835a9df0c6d851_u64)\n        put(array, 0xdcdb1b2798182244_u64); put(array, 0xf8e431456cf88e65_u64)\n        put(array, 0x8a08f0f8bf0f156b_u64); put(array, 0x1b8e9ecb641b58ff_u64)\n        put(array, 0xac8b2d36eed2dac5_u64); put(array, 0xe272467e3d222f3f_u64)\n        put(array, 0xd7adf884aa879177_u64); put(array, 0x5b0ed81dcc6abb0f_u64)\n        put(array, 0x86ccbb52ea94baea_u64); put(array, 0x98e947129fc2b4e9_u64)\n        put(array, 0xa87fea27a539e9a5_u64); put(array, 0x3f2398d747b36224_u64)\n        put(array, 0xd29fe4b18e88640e_u64); put(array, 0x8eec7f0d19a03aad_u64)\n        put(array, 0x83a3eeeef9153e89_u64); put(array, 0x1953cf68300424ac_u64)\n        put(array, 0xa48ceaaab75a8e2b_u64); put(array, 0x5fa8c3423c052dd7_u64)\n        put(array, 0xcdb02555653131b6_u64); put(array, 0x3792f412cb06794d_u64)\n        put(array, 0x808e17555f3ebf11_u64); put(array, 0xe2bbd88bbee40bd0_u64)\n        put(array, 0xa0b19d2ab70e6ed6_u64); put(array, 0x5b6aceaeae9d0ec4_u64)\n        put(array, 0xc8de047564d20a8b_u64); put(array, 0xf245825a5a445275_u64)\n        put(array, 0xfb158592be068d2e_u64); put(array, 0xeed6e2f0f0d56712_u64)\n        put(array, 0x9ced737bb6c4183d_u64); put(array, 0x55464dd69685606b_u64)\n        put(array, 0xc428d05aa4751e4c_u64); put(array, 0xaa97e14c3c26b886_u64)\n        put(array, 0xf53304714d9265df_u64); put(array, 0xd53dd99f4b3066a8_u64)\n        put(array, 0x993fe2c6d07b7fab_u64); put(array, 0xe546a8038efe4029_u64)\n        put(array, 0xbf8fdb78849a5f96_u64); put(array, 0xde98520472bdd033_u64)\n        put(array, 0xef73d256a5c0f77c_u64); put(array, 0x963e66858f6d4440_u64)\n        put(array, 0x95a8637627989aad_u64); put(array, 0xdde7001379a44aa8_u64)\n        put(array, 0xbb127c53b17ec159_u64); put(array, 0x5560c018580d5d52_u64)\n        put(array, 0xe9d71b689dde71af_u64); put(array, 0xaab8f01e6e10b4a6_u64)\n        put(array, 0x9226712162ab070d_u64); put(array, 0xcab3961304ca70e8_u64)\n        put(array, 0xb6b00d69bb55c8d1_u64); put(array, 0x3d607b97c5fd0d22_u64)\n        put(array, 0xe45c10c42a2b3b05_u64); put(array, 0x8cb89a7db77c506a_u64)\n        put(array, 0x8eb98a7a9a5b04e3_u64); put(array, 0x77f3608e92adb242_u64)\n        put(array, 0xb267ed1940f1c61c_u64); put(array, 0x55f038b237591ed3_u64)\n        put(array, 0xdf01e85f912e37a3_u64); put(array, 0x6b6c46dec52f6688_u64)\n        put(array, 0x8b61313bbabce2c6_u64); put(array, 0x2323ac4b3b3da015_u64)\n        put(array, 0xae397d8aa96c1b77_u64); put(array, 0xabec975e0a0d081a_u64)\n        put(array, 0xd9c7dced53c72255_u64); put(array, 0x96e7bd358c904a21_u64)\n        put(array, 0x881cea14545c7575_u64); put(array, 0x7e50d64177da2e54_u64)\n        put(array, 0xaa242499697392d2_u64); put(array, 0xdde50bd1d5d0b9e9_u64)\n        put(array, 0xd4ad2dbfc3d07787_u64); put(array, 0x955e4ec64b44e864_u64)\n        put(array, 0x84ec3c97da624ab4_u64); put(array, 0xbd5af13bef0b113e_u64)\n        put(array, 0xa6274bbdd0fadd61_u64); put(array, 0xecb1ad8aeacdd58e_u64)\n        put(array, 0xcfb11ead453994ba_u64); put(array, 0x67de18eda5814af2_u64)\n        put(array, 0x81ceb32c4b43fcf4_u64); put(array, 0x80eacf948770ced7_u64)\n        put(array, 0xa2425ff75e14fc31_u64); put(array, 0xa1258379a94d028d_u64)\n        put(array, 0xcad2f7f5359a3b3e_u64); put(array, 0x96ee45813a04330_u64)\n        put(array, 0xfd87b5f28300ca0d_u64); put(array, 0x8bca9d6e188853fc_u64)\n        put(array, 0x9e74d1b791e07e48_u64); put(array, 0x775ea264cf55347e_u64)\n        put(array, 0xc612062576589dda_u64); put(array, 0x95364afe032a819e_u64)\n        put(array, 0xf79687aed3eec551_u64); put(array, 0x3a83ddbd83f52205_u64)\n        put(array, 0x9abe14cd44753b52_u64); put(array, 0xc4926a9672793543_u64)\n        put(array, 0xc16d9a0095928a27_u64); put(array, 0x75b7053c0f178294_u64)\n        put(array, 0xf1c90080baf72cb1_u64); put(array, 0x5324c68b12dd6339_u64)\n        put(array, 0x971da05074da7bee_u64); put(array, 0xd3f6fc16ebca5e04_u64)\n        put(array, 0xbce5086492111aea_u64); put(array, 0x88f4bb1ca6bcf585_u64)\n        put(array, 0xec1e4a7db69561a5_u64); put(array, 0x2b31e9e3d06c32e6_u64)\n        put(array, 0x9392ee8e921d5d07_u64); put(array, 0x3aff322e62439fd0_u64)\n        put(array, 0xb877aa3236a4b449_u64); put(array, 0x9befeb9fad487c3_u64)\n        put(array, 0xe69594bec44de15b_u64); put(array, 0x4c2ebe687989a9b4_u64)\n        put(array, 0x901d7cf73ab0acd9_u64); put(array, 0xf9d37014bf60a11_u64)\n        put(array, 0xb424dc35095cd80f_u64); put(array, 0x538484c19ef38c95_u64)\n        put(array, 0xe12e13424bb40e13_u64); put(array, 0x2865a5f206b06fba_u64)\n        put(array, 0x8cbccc096f5088cb_u64); put(array, 0xf93f87b7442e45d4_u64)\n        put(array, 0xafebff0bcb24aafe_u64); put(array, 0xf78f69a51539d749_u64)\n        put(array, 0xdbe6fecebdedd5be_u64); put(array, 0xb573440e5a884d1c_u64)\n        put(array, 0x89705f4136b4a597_u64); put(array, 0x31680a88f8953031_u64)\n        put(array, 0xabcc77118461cefc_u64); put(array, 0xfdc20d2b36ba7c3e_u64)\n        put(array, 0xd6bf94d5e57a42bc_u64); put(array, 0x3d32907604691b4d_u64)\n        put(array, 0x8637bd05af6c69b5_u64); put(array, 0xa63f9a49c2c1b110_u64)\n        put(array, 0xa7c5ac471b478423_u64); put(array, 0xfcf80dc33721d54_u64)\n        put(array, 0xd1b71758e219652b_u64); put(array, 0xd3c36113404ea4a9_u64)\n        put(array, 0x83126e978d4fdf3b_u64); put(array, 0x645a1cac083126ea_u64)\n        put(array, 0xa3d70a3d70a3d70a_u64); put(array, 0x3d70a3d70a3d70a4_u64)\n        put(array, 0xcccccccccccccccc_u64); put(array, 0xcccccccccccccccd_u64)\n        put(array, 0x8000000000000000_u64); put(array, 0x0_u64)\n        put(array, 0xa000000000000000_u64); put(array, 0x0_u64)\n        put(array, 0xc800000000000000_u64); put(array, 0x0_u64)\n        put(array, 0xfa00000000000000_u64); put(array, 0x0_u64)\n        put(array, 0x9c40000000000000_u64); put(array, 0x0_u64)\n        put(array, 0xc350000000000000_u64); put(array, 0x0_u64)\n        put(array, 0xf424000000000000_u64); put(array, 0x0_u64)\n        put(array, 0x9896800000000000_u64); put(array, 0x0_u64)\n        put(array, 0xbebc200000000000_u64); put(array, 0x0_u64)\n        put(array, 0xee6b280000000000_u64); put(array, 0x0_u64)\n        put(array, 0x9502f90000000000_u64); put(array, 0x0_u64)\n        put(array, 0xba43b74000000000_u64); put(array, 0x0_u64)\n        put(array, 0xe8d4a51000000000_u64); put(array, 0x0_u64)\n        put(array, 0x9184e72a00000000_u64); put(array, 0x0_u64)\n        put(array, 0xb5e620f480000000_u64); put(array, 0x0_u64)\n        put(array, 0xe35fa931a0000000_u64); put(array, 0x0_u64)\n        put(array, 0x8e1bc9bf04000000_u64); put(array, 0x0_u64)\n        put(array, 0xb1a2bc2ec5000000_u64); put(array, 0x0_u64)\n        put(array, 0xde0b6b3a76400000_u64); put(array, 0x0_u64)\n        put(array, 0x8ac7230489e80000_u64); put(array, 0x0_u64)\n        put(array, 0xad78ebc5ac620000_u64); put(array, 0x0_u64)\n        put(array, 0xd8d726b7177a8000_u64); put(array, 0x0_u64)\n        put(array, 0x878678326eac9000_u64); put(array, 0x0_u64)\n        put(array, 0xa968163f0a57b400_u64); put(array, 0x0_u64)\n        put(array, 0xd3c21bcecceda100_u64); put(array, 0x0_u64)\n        put(array, 0x84595161401484a0_u64); put(array, 0x0_u64)\n        put(array, 0xa56fa5b99019a5c8_u64); put(array, 0x0_u64)\n        put(array, 0xcecb8f27f4200f3a_u64); put(array, 0x0_u64)\n        put(array, 0x813f3978f8940984_u64); put(array, 0x4000000000000000_u64)\n        put(array, 0xa18f07d736b90be5_u64); put(array, 0x5000000000000000_u64)\n        put(array, 0xc9f2c9cd04674ede_u64); put(array, 0xa400000000000000_u64)\n        put(array, 0xfc6f7c4045812296_u64); put(array, 0x4d00000000000000_u64)\n        put(array, 0x9dc5ada82b70b59d_u64); put(array, 0xf020000000000000_u64)\n        put(array, 0xc5371912364ce305_u64); put(array, 0x6c28000000000000_u64)\n        put(array, 0xf684df56c3e01bc6_u64); put(array, 0xc732000000000000_u64)\n        put(array, 0x9a130b963a6c115c_u64); put(array, 0x3c7f400000000000_u64)\n        put(array, 0xc097ce7bc90715b3_u64); put(array, 0x4b9f100000000000_u64)\n        put(array, 0xf0bdc21abb48db20_u64); put(array, 0x1e86d40000000000_u64)\n        put(array, 0x96769950b50d88f4_u64); put(array, 0x1314448000000000_u64)\n        put(array, 0xbc143fa4e250eb31_u64); put(array, 0x17d955a000000000_u64)\n        put(array, 0xeb194f8e1ae525fd_u64); put(array, 0x5dcfab0800000000_u64)\n        put(array, 0x92efd1b8d0cf37be_u64); put(array, 0x5aa1cae500000000_u64)\n        put(array, 0xb7abc627050305ad_u64); put(array, 0xf14a3d9e40000000_u64)\n        put(array, 0xe596b7b0c643c719_u64); put(array, 0x6d9ccd05d0000000_u64)\n        put(array, 0x8f7e32ce7bea5c6f_u64); put(array, 0xe4820023a2000000_u64)\n        put(array, 0xb35dbf821ae4f38b_u64); put(array, 0xdda2802c8a800000_u64)\n        put(array, 0xe0352f62a19e306e_u64); put(array, 0xd50b2037ad200000_u64)\n        put(array, 0x8c213d9da502de45_u64); put(array, 0x4526f422cc340000_u64)\n        put(array, 0xaf298d050e4395d6_u64); put(array, 0x9670b12b7f410000_u64)\n        put(array, 0xdaf3f04651d47b4c_u64); put(array, 0x3c0cdd765f114000_u64)\n        put(array, 0x88d8762bf324cd0f_u64); put(array, 0xa5880a69fb6ac800_u64)\n        put(array, 0xab0e93b6efee0053_u64); put(array, 0x8eea0d047a457a00_u64)\n        put(array, 0xd5d238a4abe98068_u64); put(array, 0x72a4904598d6d880_u64)\n        put(array, 0x85a36366eb71f041_u64); put(array, 0x47a6da2b7f864750_u64)\n        put(array, 0xa70c3c40a64e6c51_u64); put(array, 0x999090b65f67d924_u64)\n        put(array, 0xd0cf4b50cfe20765_u64); put(array, 0xfff4b4e3f741cf6d_u64)\n        put(array, 0x82818f1281ed449f_u64); put(array, 0xbff8f10e7a8921a4_u64)\n        put(array, 0xa321f2d7226895c7_u64); put(array, 0xaff72d52192b6a0d_u64)\n        put(array, 0xcbea6f8ceb02bb39_u64); put(array, 0x9bf4f8a69f764490_u64)\n        put(array, 0xfee50b7025c36a08_u64); put(array, 0x2f236d04753d5b4_u64)\n        put(array, 0x9f4f2726179a2245_u64); put(array, 0x1d762422c946590_u64)\n        put(array, 0xc722f0ef9d80aad6_u64); put(array, 0x424d3ad2b7b97ef5_u64)\n        put(array, 0xf8ebad2b84e0d58b_u64); put(array, 0xd2e0898765a7deb2_u64)\n        put(array, 0x9b934c3b330c8577_u64); put(array, 0x63cc55f49f88eb2f_u64)\n        put(array, 0xc2781f49ffcfa6d5_u64); put(array, 0x3cbf6b71c76b25fb_u64)\n        put(array, 0xf316271c7fc3908a_u64); put(array, 0x8bef464e3945ef7a_u64)\n        put(array, 0x97edd871cfda3a56_u64); put(array, 0x97758bf0e3cbb5ac_u64)\n        put(array, 0xbde94e8e43d0c8ec_u64); put(array, 0x3d52eeed1cbea317_u64)\n        put(array, 0xed63a231d4c4fb27_u64); put(array, 0x4ca7aaa863ee4bdd_u64)\n        put(array, 0x945e455f24fb1cf8_u64); put(array, 0x8fe8caa93e74ef6a_u64)\n        put(array, 0xb975d6b6ee39e436_u64); put(array, 0xb3e2fd538e122b44_u64)\n        put(array, 0xe7d34c64a9c85d44_u64); put(array, 0x60dbbca87196b616_u64)\n        put(array, 0x90e40fbeea1d3a4a_u64); put(array, 0xbc8955e946fe31cd_u64)\n        put(array, 0xb51d13aea4a488dd_u64); put(array, 0x6babab6398bdbe41_u64)\n        put(array, 0xe264589a4dcdab14_u64); put(array, 0xc696963c7eed2dd1_u64)\n        put(array, 0x8d7eb76070a08aec_u64); put(array, 0xfc1e1de5cf543ca2_u64)\n        put(array, 0xb0de65388cc8ada8_u64); put(array, 0x3b25a55f43294bcb_u64)\n        put(array, 0xdd15fe86affad912_u64); put(array, 0x49ef0eb713f39ebe_u64)\n        put(array, 0x8a2dbf142dfcc7ab_u64); put(array, 0x6e3569326c784337_u64)\n        put(array, 0xacb92ed9397bf996_u64); put(array, 0x49c2c37f07965404_u64)\n        put(array, 0xd7e77a8f87daf7fb_u64); put(array, 0xdc33745ec97be906_u64)\n        put(array, 0x86f0ac99b4e8dafd_u64); put(array, 0x69a028bb3ded71a3_u64)\n        put(array, 0xa8acd7c0222311bc_u64); put(array, 0xc40832ea0d68ce0c_u64)\n        put(array, 0xd2d80db02aabd62b_u64); put(array, 0xf50a3fa490c30190_u64)\n        put(array, 0x83c7088e1aab65db_u64); put(array, 0x792667c6da79e0fa_u64)\n        put(array, 0xa4b8cab1a1563f52_u64); put(array, 0x577001b891185938_u64)\n        put(array, 0xcde6fd5e09abcf26_u64); put(array, 0xed4c0226b55e6f86_u64)\n        put(array, 0x80b05e5ac60b6178_u64); put(array, 0x544f8158315b05b4_u64)\n        put(array, 0xa0dc75f1778e39d6_u64); put(array, 0x696361ae3db1c721_u64)\n        put(array, 0xc913936dd571c84c_u64); put(array, 0x3bc3a19cd1e38e9_u64)\n        put(array, 0xfb5878494ace3a5f_u64); put(array, 0x4ab48a04065c723_u64)\n        put(array, 0x9d174b2dcec0e47b_u64); put(array, 0x62eb0d64283f9c76_u64)\n        put(array, 0xc45d1df942711d9a_u64); put(array, 0x3ba5d0bd324f8394_u64)\n        put(array, 0xf5746577930d6500_u64); put(array, 0xca8f44ec7ee36479_u64)\n        put(array, 0x9968bf6abbe85f20_u64); put(array, 0x7e998b13cf4e1ecb_u64)\n        put(array, 0xbfc2ef456ae276e8_u64); put(array, 0x9e3fedd8c321a67e_u64)\n        put(array, 0xefb3ab16c59b14a2_u64); put(array, 0xc5cfe94ef3ea101e_u64)\n        put(array, 0x95d04aee3b80ece5_u64); put(array, 0xbba1f1d158724a12_u64)\n        put(array, 0xbb445da9ca61281f_u64); put(array, 0x2a8a6e45ae8edc97_u64)\n        put(array, 0xea1575143cf97226_u64); put(array, 0xf52d09d71a3293bd_u64)\n        put(array, 0x924d692ca61be758_u64); put(array, 0x593c2626705f9c56_u64)\n        put(array, 0xb6e0c377cfa2e12e_u64); put(array, 0x6f8b2fb00c77836c_u64)\n        put(array, 0xe498f455c38b997a_u64); put(array, 0xb6dfb9c0f956447_u64)\n        put(array, 0x8edf98b59a373fec_u64); put(array, 0x4724bd4189bd5eac_u64)\n        put(array, 0xb2977ee300c50fe7_u64); put(array, 0x58edec91ec2cb657_u64)\n        put(array, 0xdf3d5e9bc0f653e1_u64); put(array, 0x2f2967b66737e3ed_u64)\n        put(array, 0x8b865b215899f46c_u64); put(array, 0xbd79e0d20082ee74_u64)\n        put(array, 0xae67f1e9aec07187_u64); put(array, 0xecd8590680a3aa11_u64)\n        put(array, 0xda01ee641a708de9_u64); put(array, 0xe80e6f4820cc9495_u64)\n        put(array, 0x884134fe908658b2_u64); put(array, 0x3109058d147fdcdd_u64)\n        put(array, 0xaa51823e34a7eede_u64); put(array, 0xbd4b46f0599fd415_u64)\n        put(array, 0xd4e5e2cdc1d1ea96_u64); put(array, 0x6c9e18ac7007c91a_u64)\n        put(array, 0x850fadc09923329e_u64); put(array, 0x3e2cf6bc604ddb0_u64)\n        put(array, 0xa6539930bf6bff45_u64); put(array, 0x84db8346b786151c_u64)\n        put(array, 0xcfe87f7cef46ff16_u64); put(array, 0xe612641865679a63_u64)\n        put(array, 0x81f14fae158c5f6e_u64); put(array, 0x4fcb7e8f3f60c07e_u64)\n        put(array, 0xa26da3999aef7749_u64); put(array, 0xe3be5e330f38f09d_u64)\n        put(array, 0xcb090c8001ab551c_u64); put(array, 0x5cadf5bfd3072cc5_u64)\n        put(array, 0xfdcb4fa002162a63_u64); put(array, 0x73d9732fc7c8f7f6_u64)\n        put(array, 0x9e9f11c4014dda7e_u64); put(array, 0x2867e7fddcdd9afa_u64)\n        put(array, 0xc646d63501a1511d_u64); put(array, 0xb281e1fd541501b8_u64)\n        put(array, 0xf7d88bc24209a565_u64); put(array, 0x1f225a7ca91a4226_u64)\n        put(array, 0x9ae757596946075f_u64); put(array, 0x3375788de9b06958_u64)\n        put(array, 0xc1a12d2fc3978937_u64); put(array, 0x52d6b1641c83ae_u64)\n        put(array, 0xf209787bb47d6b84_u64); put(array, 0xc0678c5dbd23a49a_u64)\n        put(array, 0x9745eb4d50ce6332_u64); put(array, 0xf840b7ba963646e0_u64)\n        put(array, 0xbd176620a501fbff_u64); put(array, 0xb650e5a93bc3d898_u64)\n        put(array, 0xec5d3fa8ce427aff_u64); put(array, 0xa3e51f138ab4cebe_u64)\n        put(array, 0x93ba47c980e98cdf_u64); put(array, 0xc66f336c36b10137_u64)\n        put(array, 0xb8a8d9bbe123f017_u64); put(array, 0xb80b0047445d4184_u64)\n        put(array, 0xe6d3102ad96cec1d_u64); put(array, 0xa60dc059157491e5_u64)\n        put(array, 0x9043ea1ac7e41392_u64); put(array, 0x87c89837ad68db2f_u64)\n        put(array, 0xb454e4a179dd1877_u64); put(array, 0x29babe4598c311fb_u64)\n        put(array, 0xe16a1dc9d8545e94_u64); put(array, 0xf4296dd6fef3d67a_u64)\n        put(array, 0x8ce2529e2734bb1d_u64); put(array, 0x1899e4a65f58660c_u64)\n        put(array, 0xb01ae745b101e9e4_u64); put(array, 0x5ec05dcff72e7f8f_u64)\n        put(array, 0xdc21a1171d42645d_u64); put(array, 0x76707543f4fa1f73_u64)\n        put(array, 0x899504ae72497eba_u64); put(array, 0x6a06494a791c53a8_u64)\n        put(array, 0xabfa45da0edbde69_u64); put(array, 0x487db9d17636892_u64)\n        put(array, 0xd6f8d7509292d603_u64); put(array, 0x45a9d2845d3c42b6_u64)\n        put(array, 0x865b86925b9bc5c2_u64); put(array, 0xb8a2392ba45a9b2_u64)\n        put(array, 0xa7f26836f282b732_u64); put(array, 0x8e6cac7768d7141e_u64)\n        put(array, 0xd1ef0244af2364ff_u64); put(array, 0x3207d795430cd926_u64)\n        put(array, 0x8335616aed761f1f_u64); put(array, 0x7f44e6bd49e807b8_u64)\n        put(array, 0xa402b9c5a8d3a6e7_u64); put(array, 0x5f16206c9c6209a6_u64)\n        put(array, 0xcd036837130890a1_u64); put(array, 0x36dba887c37a8c0f_u64)\n        put(array, 0x802221226be55a64_u64); put(array, 0xc2494954da2c9789_u64)\n        put(array, 0xa02aa96b06deb0fd_u64); put(array, 0xf2db9baa10b7bd6c_u64)\n        put(array, 0xc83553c5c8965d3d_u64); put(array, 0x6f92829494e5acc7_u64)\n        put(array, 0xfa42a8b73abbf48c_u64); put(array, 0xcb772339ba1f17f9_u64)\n        put(array, 0x9c69a97284b578d7_u64); put(array, 0xff2a760414536efb_u64)\n        put(array, 0xc38413cf25e2d70d_u64); put(array, 0xfef5138519684aba_u64)\n        put(array, 0xf46518c2ef5b8cd1_u64); put(array, 0x7eb258665fc25d69_u64)\n        put(array, 0x98bf2f79d5993802_u64); put(array, 0xef2f773ffbd97a61_u64)\n        put(array, 0xbeeefb584aff8603_u64); put(array, 0xaafb550ffacfd8fa_u64)\n        put(array, 0xeeaaba2e5dbf6784_u64); put(array, 0x95ba2a53f983cf38_u64)\n        put(array, 0x952ab45cfa97a0b2_u64); put(array, 0xdd945a747bf26183_u64)\n        put(array, 0xba756174393d88df_u64); put(array, 0x94f971119aeef9e4_u64)\n        put(array, 0xe912b9d1478ceb17_u64); put(array, 0x7a37cd5601aab85d_u64)\n        put(array, 0x91abb422ccb812ee_u64); put(array, 0xac62e055c10ab33a_u64)\n        put(array, 0xb616a12b7fe617aa_u64); put(array, 0x577b986b314d6009_u64)\n        put(array, 0xe39c49765fdf9d94_u64); put(array, 0xed5a7e85fda0b80b_u64)\n        put(array, 0x8e41ade9fbebc27d_u64); put(array, 0x14588f13be847307_u64)\n        put(array, 0xb1d219647ae6b31c_u64); put(array, 0x596eb2d8ae258fc8_u64)\n        put(array, 0xde469fbd99a05fe3_u64); put(array, 0x6fca5f8ed9aef3bb_u64)\n        put(array, 0x8aec23d680043bee_u64); put(array, 0x25de7bb9480d5854_u64)\n        put(array, 0xada72ccc20054ae9_u64); put(array, 0xaf561aa79a10ae6a_u64)\n        put(array, 0xd910f7ff28069da4_u64); put(array, 0x1b2ba1518094da04_u64)\n        put(array, 0x87aa9aff79042286_u64); put(array, 0x90fb44d2f05d0842_u64)\n        put(array, 0xa99541bf57452b28_u64); put(array, 0x353a1607ac744a53_u64)\n        put(array, 0xd3fa922f2d1675f2_u64); put(array, 0x42889b8997915ce8_u64)\n        put(array, 0x847c9b5d7c2e09b7_u64); put(array, 0x69956135febada11_u64)\n        put(array, 0xa59bc234db398c25_u64); put(array, 0x43fab9837e699095_u64)\n        put(array, 0xcf02b2c21207ef2e_u64); put(array, 0x94f967e45e03f4bb_u64)\n        put(array, 0x8161afb94b44f57d_u64); put(array, 0x1d1be0eebac278f5_u64)\n        put(array, 0xa1ba1ba79e1632dc_u64); put(array, 0x6462d92a69731732_u64)\n        put(array, 0xca28a291859bbf93_u64); put(array, 0x7d7b8f7503cfdcfe_u64)\n        put(array, 0xfcb2cb35e702af78_u64); put(array, 0x5cda735244c3d43e_u64)\n        put(array, 0x9defbf01b061adab_u64); put(array, 0x3a0888136afa64a7_u64)\n        put(array, 0xc56baec21c7a1916_u64); put(array, 0x88aaa1845b8fdd0_u64)\n        put(array, 0xf6c69a72a3989f5b_u64); put(array, 0x8aad549e57273d45_u64)\n        put(array, 0x9a3c2087a63f6399_u64); put(array, 0x36ac54e2f678864b_u64)\n        put(array, 0xc0cb28a98fcf3c7f_u64); put(array, 0x84576a1bb416a7dd_u64)\n        put(array, 0xf0fdf2d3f3c30b9f_u64); put(array, 0x656d44a2a11c51d5_u64)\n        put(array, 0x969eb7c47859e743_u64); put(array, 0x9f644ae5a4b1b325_u64)\n        put(array, 0xbc4665b596706114_u64); put(array, 0x873d5d9f0dde1fee_u64)\n        put(array, 0xeb57ff22fc0c7959_u64); put(array, 0xa90cb506d155a7ea_u64)\n        put(array, 0x9316ff75dd87cbd8_u64); put(array, 0x9a7f12442d588f2_u64)\n        put(array, 0xb7dcbf5354e9bece_u64); put(array, 0xc11ed6d538aeb2f_u64)\n        put(array, 0xe5d3ef282a242e81_u64); put(array, 0x8f1668c8a86da5fa_u64)\n        put(array, 0x8fa475791a569d10_u64); put(array, 0xf96e017d694487bc_u64)\n        put(array, 0xb38d92d760ec4455_u64); put(array, 0x37c981dcc395a9ac_u64)\n        put(array, 0xe070f78d3927556a_u64); put(array, 0x85bbe253f47b1417_u64)\n        put(array, 0x8c469ab843b89562_u64); put(array, 0x93956d7478ccec8e_u64)\n        put(array, 0xaf58416654a6babb_u64); put(array, 0x387ac8d1970027b2_u64)\n        put(array, 0xdb2e51bfe9d0696a_u64); put(array, 0x6997b05fcc0319e_u64)\n        put(array, 0x88fcf317f22241e2_u64); put(array, 0x441fece3bdf81f03_u64)\n        put(array, 0xab3c2fddeeaad25a_u64); put(array, 0xd527e81cad7626c3_u64)\n        put(array, 0xd60b3bd56a5586f1_u64); put(array, 0x8a71e223d8d3b074_u64)\n        put(array, 0x85c7056562757456_u64); put(array, 0xf6872d5667844e49_u64)\n        put(array, 0xa738c6bebb12d16c_u64); put(array, 0xb428f8ac016561db_u64)\n        put(array, 0xd106f86e69d785c7_u64); put(array, 0xe13336d701beba52_u64)\n        put(array, 0x82a45b450226b39c_u64); put(array, 0xecc0024661173473_u64)\n        put(array, 0xa34d721642b06084_u64); put(array, 0x27f002d7f95d0190_u64)\n        put(array, 0xcc20ce9bd35c78a5_u64); put(array, 0x31ec038df7b441f4_u64)\n        put(array, 0xff290242c83396ce_u64); put(array, 0x7e67047175a15271_u64)\n        put(array, 0x9f79a169bd203e41_u64); put(array, 0xf0062c6e984d386_u64)\n        put(array, 0xc75809c42c684dd1_u64); put(array, 0x52c07b78a3e60868_u64)\n        put(array, 0xf92e0c3537826145_u64); put(array, 0xa7709a56ccdf8a82_u64)\n        put(array, 0x9bbcc7a142b17ccb_u64); put(array, 0x88a66076400bb691_u64)\n        put(array, 0xc2abf989935ddbfe_u64); put(array, 0x6acff893d00ea435_u64)\n        put(array, 0xf356f7ebf83552fe_u64); put(array, 0x583f6b8c4124d43_u64)\n        put(array, 0x98165af37b2153de_u64); put(array, 0xc3727a337a8b704a_u64)\n        put(array, 0xbe1bf1b059e9a8d6_u64); put(array, 0x744f18c0592e4c5c_u64)\n        put(array, 0xeda2ee1c7064130c_u64); put(array, 0x1162def06f79df73_u64)\n        put(array, 0x9485d4d1c63e8be7_u64); put(array, 0x8addcb5645ac2ba8_u64)\n        put(array, 0xb9a74a0637ce2ee1_u64); put(array, 0x6d953e2bd7173692_u64)\n        put(array, 0xe8111c87c5c1ba99_u64); put(array, 0xc8fa8db6ccdd0437_u64)\n        put(array, 0x910ab1d4db9914a0_u64); put(array, 0x1d9c9892400a22a2_u64)\n        put(array, 0xb54d5e4a127f59c8_u64); put(array, 0x2503beb6d00cab4b_u64)\n        put(array, 0xe2a0b5dc971f303a_u64); put(array, 0x2e44ae64840fd61d_u64)\n        put(array, 0x8da471a9de737e24_u64); put(array, 0x5ceaecfed289e5d2_u64)\n        put(array, 0xb10d8e1456105dad_u64); put(array, 0x7425a83e872c5f47_u64)\n        put(array, 0xdd50f1996b947518_u64); put(array, 0xd12f124e28f77719_u64)\n        put(array, 0x8a5296ffe33cc92f_u64); put(array, 0x82bd6b70d99aaa6f_u64)\n        put(array, 0xace73cbfdc0bfb7b_u64); put(array, 0x636cc64d1001550b_u64)\n        put(array, 0xd8210befd30efa5a_u64); put(array, 0x3c47f7e05401aa4e_u64)\n        put(array, 0x8714a775e3e95c78_u64); put(array, 0x65acfaec34810a71_u64)\n        put(array, 0xa8d9d1535ce3b396_u64); put(array, 0x7f1839a741a14d0d_u64)\n        put(array, 0xd31045a8341ca07c_u64); put(array, 0x1ede48111209a050_u64)\n        put(array, 0x83ea2b892091e44d_u64); put(array, 0x934aed0aab460432_u64)\n        put(array, 0xa4e4b66b68b65d60_u64); put(array, 0xf81da84d5617853f_u64)\n        put(array, 0xce1de40642e3f4b9_u64); put(array, 0x36251260ab9d668e_u64)\n        put(array, 0x80d2ae83e9ce78f3_u64); put(array, 0xc1d72b7c6b426019_u64)\n        put(array, 0xa1075a24e4421730_u64); put(array, 0xb24cf65b8612f81f_u64)\n        put(array, 0xc94930ae1d529cfc_u64); put(array, 0xdee033f26797b627_u64)\n        put(array, 0xfb9b7cd9a4a7443c_u64); put(array, 0x169840ef017da3b1_u64)\n        put(array, 0x9d412e0806e88aa5_u64); put(array, 0x8e1f289560ee864e_u64)\n        put(array, 0xc491798a08a2ad4e_u64); put(array, 0xf1a6f2bab92a27e2_u64)\n        put(array, 0xf5b5d7ec8acb58a2_u64); put(array, 0xae10af696774b1db_u64)\n        put(array, 0x9991a6f3d6bf1765_u64); put(array, 0xacca6da1e0a8ef29_u64)\n        put(array, 0xbff610b0cc6edd3f_u64); put(array, 0x17fd090a58d32af3_u64)\n        put(array, 0xeff394dcff8a948e_u64); put(array, 0xddfc4b4cef07f5b0_u64)\n        put(array, 0x95f83d0a1fb69cd9_u64); put(array, 0x4abdaf101564f98e_u64)\n        put(array, 0xbb764c4ca7a4440f_u64); put(array, 0x9d6d1ad41abe37f1_u64)\n        put(array, 0xea53df5fd18d5513_u64); put(array, 0x84c86189216dc5ed_u64)\n        put(array, 0x92746b9be2f8552c_u64); put(array, 0x32fd3cf5b4e49bb4_u64)\n        put(array, 0xb7118682dbb66a77_u64); put(array, 0x3fbc8c33221dc2a1_u64)\n        put(array, 0xe4d5e82392a40515_u64); put(array, 0xfabaf3feaa5334a_u64)\n        put(array, 0x8f05b1163ba6832d_u64); put(array, 0x29cb4d87f2a7400e_u64)\n        put(array, 0xb2c71d5bca9023f8_u64); put(array, 0x743e20e9ef511012_u64)\n        put(array, 0xdf78e4b2bd342cf6_u64); put(array, 0x914da9246b255416_u64)\n        put(array, 0x8bab8eefb6409c1a_u64); put(array, 0x1ad089b6c2f7548e_u64)\n        put(array, 0xae9672aba3d0c320_u64); put(array, 0xa184ac2473b529b1_u64)\n        put(array, 0xda3c0f568cc4f3e8_u64); put(array, 0xc9e5d72d90a2741e_u64)\n        put(array, 0x8865899617fb1871_u64); put(array, 0x7e2fa67c7a658892_u64)\n        put(array, 0xaa7eebfb9df9de8d_u64); put(array, 0xddbb901b98feeab7_u64)\n        put(array, 0xd51ea6fa85785631_u64); put(array, 0x552a74227f3ea565_u64)\n        put(array, 0x8533285c936b35de_u64); put(array, 0xd53a88958f87275f_u64)\n        put(array, 0xa67ff273b8460356_u64); put(array, 0x8a892abaf368f137_u64)\n        put(array, 0xd01fef10a657842c_u64); put(array, 0x2d2b7569b0432d85_u64)\n        put(array, 0x8213f56a67f6b29b_u64); put(array, 0x9c3b29620e29fc73_u64)\n        put(array, 0xa298f2c501f45f42_u64); put(array, 0x8349f3ba91b47b8f_u64)\n        put(array, 0xcb3f2f7642717713_u64); put(array, 0x241c70a936219a73_u64)\n        put(array, 0xfe0efb53d30dd4d7_u64); put(array, 0xed238cd383aa0110_u64)\n        put(array, 0x9ec95d1463e8a506_u64); put(array, 0xf4363804324a40aa_u64)\n        put(array, 0xc67bb4597ce2ce48_u64); put(array, 0xb143c6053edcd0d5_u64)\n        put(array, 0xf81aa16fdc1b81da_u64); put(array, 0xdd94b7868e94050a_u64)\n        put(array, 0x9b10a4e5e9913128_u64); put(array, 0xca7cf2b4191c8326_u64)\n        put(array, 0xc1d4ce1f63f57d72_u64); put(array, 0xfd1c2f611f63a3f0_u64)\n        put(array, 0xf24a01a73cf2dccf_u64); put(array, 0xbc633b39673c8cec_u64)\n        put(array, 0x976e41088617ca01_u64); put(array, 0xd5be0503e085d813_u64)\n        put(array, 0xbd49d14aa79dbc82_u64); put(array, 0x4b2d8644d8a74e18_u64)\n        put(array, 0xec9c459d51852ba2_u64); put(array, 0xddf8e7d60ed1219e_u64)\n        put(array, 0x93e1ab8252f33b45_u64); put(array, 0xcabb90e5c942b503_u64)\n        put(array, 0xb8da1662e7b00a17_u64); put(array, 0x3d6a751f3b936243_u64)\n        put(array, 0xe7109bfba19c0c9d_u64); put(array, 0xcc512670a783ad4_u64)\n        put(array, 0x906a617d450187e2_u64); put(array, 0x27fb2b80668b24c5_u64)\n        put(array, 0xb484f9dc9641e9da_u64); put(array, 0xb1f9f660802dedf6_u64)\n        put(array, 0xe1a63853bbd26451_u64); put(array, 0x5e7873f8a0396973_u64)\n        put(array, 0x8d07e33455637eb2_u64); put(array, 0xdb0b487b6423e1e8_u64)\n        put(array, 0xb049dc016abc5e5f_u64); put(array, 0x91ce1a9a3d2cda62_u64)\n        put(array, 0xdc5c5301c56b75f7_u64); put(array, 0x7641a140cc7810fb_u64)\n        put(array, 0x89b9b3e11b6329ba_u64); put(array, 0xa9e904c87fcb0a9d_u64)\n        put(array, 0xac2820d9623bf429_u64); put(array, 0x546345fa9fbdcd44_u64)\n        put(array, 0xd732290fbacaf133_u64); put(array, 0xa97c177947ad4095_u64)\n        put(array, 0x867f59a9d4bed6c0_u64); put(array, 0x49ed8eabcccc485d_u64)\n        put(array, 0xa81f301449ee8c70_u64); put(array, 0x5c68f256bfff5a74_u64)\n        put(array, 0xd226fc195c6a2f8c_u64); put(array, 0x73832eec6fff3111_u64)\n        put(array, 0x83585d8fd9c25db7_u64); put(array, 0xc831fd53c5ff7eab_u64)\n        put(array, 0xa42e74f3d032f525_u64); put(array, 0xba3e7ca8b77f5e55_u64)\n        put(array, 0xcd3a1230c43fb26f_u64); put(array, 0x28ce1bd2e55f35eb_u64)\n        put(array, 0x80444b5e7aa7cf85_u64); put(array, 0x7980d163cf5b81b3_u64)\n        put(array, 0xa0555e361951c366_u64); put(array, 0xd7e105bcc332621f_u64)\n        put(array, 0xc86ab5c39fa63440_u64); put(array, 0x8dd9472bf3fefaa7_u64)\n        put(array, 0xfa856334878fc150_u64); put(array, 0xb14f98f6f0feb951_u64)\n        put(array, 0x9c935e00d4b9d8d2_u64); put(array, 0x6ed1bf9a569f33d3_u64)\n        put(array, 0xc3b8358109e84f07_u64); put(array, 0xa862f80ec4700c8_u64)\n        put(array, 0xf4a642e14c6262c8_u64); put(array, 0xcd27bb612758c0fa_u64)\n        put(array, 0x98e7e9cccfbd7dbd_u64); put(array, 0x8038d51cb897789c_u64)\n        put(array, 0xbf21e44003acdd2c_u64); put(array, 0xe0470a63e6bd56c3_u64)\n        put(array, 0xeeea5d5004981478_u64); put(array, 0x1858ccfce06cac74_u64)\n        put(array, 0x95527a5202df0ccb_u64); put(array, 0xf37801e0c43ebc8_u64)\n        put(array, 0xbaa718e68396cffd_u64); put(array, 0xd30560258f54e6ba_u64)\n        put(array, 0xe950df20247c83fd_u64); put(array, 0x47c6b82ef32a2069_u64)\n        put(array, 0x91d28b7416cdd27e_u64); put(array, 0x4cdc331d57fa5441_u64)\n        put(array, 0xb6472e511c81471d_u64); put(array, 0xe0133fe4adf8e952_u64)\n        put(array, 0xe3d8f9e563a198e5_u64); put(array, 0x58180fddd97723a6_u64)\n        put(array, 0x8e679c2f5e44ff8f_u64); put(array, 0x570f09eaa7ea7648_u64)\n        array\n      end\n    {% end %}\n  end\nend\n\n{% if compare_versions(Crystal::VERSION, \"1.16.0\") >= 0 %}\n  require \"./fast_table_slice_literal\"\n{% end %}\n"
  },
  {
    "path": "src/float/fast_float/fast_table_slice_literal.cr",
    "content": "module Float::FastFloat\n  module Powers\n    POWER_OF_FIVE_128 = Slice(UInt64).literal(\n      0xeef453d6923bd65a_u64, 0x113faa2906a13b3f_u64,\n      0x9558b4661b6565f8_u64, 0x4ac7ca59a424c507_u64,\n      0xbaaee17fa23ebf76_u64, 0x5d79bcf00d2df649_u64,\n      0xe95a99df8ace6f53_u64, 0xf4d82c2c107973dc_u64,\n      0x91d8a02bb6c10594_u64, 0x79071b9b8a4be869_u64,\n      0xb64ec836a47146f9_u64, 0x9748e2826cdee284_u64,\n      0xe3e27a444d8d98b7_u64, 0xfd1b1b2308169b25_u64,\n      0x8e6d8c6ab0787f72_u64, 0xfe30f0f5e50e20f7_u64,\n      0xb208ef855c969f4f_u64, 0xbdbd2d335e51a935_u64,\n      0xde8b2b66b3bc4723_u64, 0xad2c788035e61382_u64,\n      0x8b16fb203055ac76_u64, 0x4c3bcb5021afcc31_u64,\n      0xaddcb9e83c6b1793_u64, 0xdf4abe242a1bbf3d_u64,\n      0xd953e8624b85dd78_u64, 0xd71d6dad34a2af0d_u64,\n      0x87d4713d6f33aa6b_u64, 0x8672648c40e5ad68_u64,\n      0xa9c98d8ccb009506_u64, 0x680efdaf511f18c2_u64,\n      0xd43bf0effdc0ba48_u64, 0x212bd1b2566def2_u64,\n      0x84a57695fe98746d_u64, 0x14bb630f7604b57_u64,\n      0xa5ced43b7e3e9188_u64, 0x419ea3bd35385e2d_u64,\n      0xcf42894a5dce35ea_u64, 0x52064cac828675b9_u64,\n      0x818995ce7aa0e1b2_u64, 0x7343efebd1940993_u64,\n      0xa1ebfb4219491a1f_u64, 0x1014ebe6c5f90bf8_u64,\n      0xca66fa129f9b60a6_u64, 0xd41a26e077774ef6_u64,\n      0xfd00b897478238d0_u64, 0x8920b098955522b4_u64,\n      0x9e20735e8cb16382_u64, 0x55b46e5f5d5535b0_u64,\n      0xc5a890362fddbc62_u64, 0xeb2189f734aa831d_u64,\n      0xf712b443bbd52b7b_u64, 0xa5e9ec7501d523e4_u64,\n      0x9a6bb0aa55653b2d_u64, 0x47b233c92125366e_u64,\n      0xc1069cd4eabe89f8_u64, 0x999ec0bb696e840a_u64,\n      0xf148440a256e2c76_u64, 0xc00670ea43ca250d_u64,\n      0x96cd2a865764dbca_u64, 0x380406926a5e5728_u64,\n      0xbc807527ed3e12bc_u64, 0xc605083704f5ecf2_u64,\n      0xeba09271e88d976b_u64, 0xf7864a44c633682e_u64,\n      0x93445b8731587ea3_u64, 0x7ab3ee6afbe0211d_u64,\n      0xb8157268fdae9e4c_u64, 0x5960ea05bad82964_u64,\n      0xe61acf033d1a45df_u64, 0x6fb92487298e33bd_u64,\n      0x8fd0c16206306bab_u64, 0xa5d3b6d479f8e056_u64,\n      0xb3c4f1ba87bc8696_u64, 0x8f48a4899877186c_u64,\n      0xe0b62e2929aba83c_u64, 0x331acdabfe94de87_u64,\n      0x8c71dcd9ba0b4925_u64, 0x9ff0c08b7f1d0b14_u64,\n      0xaf8e5410288e1b6f_u64, 0x7ecf0ae5ee44dd9_u64,\n      0xdb71e91432b1a24a_u64, 0xc9e82cd9f69d6150_u64,\n      0x892731ac9faf056e_u64, 0xbe311c083a225cd2_u64,\n      0xab70fe17c79ac6ca_u64, 0x6dbd630a48aaf406_u64,\n      0xd64d3d9db981787d_u64, 0x92cbbccdad5b108_u64,\n      0x85f0468293f0eb4e_u64, 0x25bbf56008c58ea5_u64,\n      0xa76c582338ed2621_u64, 0xaf2af2b80af6f24e_u64,\n      0xd1476e2c07286faa_u64, 0x1af5af660db4aee1_u64,\n      0x82cca4db847945ca_u64, 0x50d98d9fc890ed4d_u64,\n      0xa37fce126597973c_u64, 0xe50ff107bab528a0_u64,\n      0xcc5fc196fefd7d0c_u64, 0x1e53ed49a96272c8_u64,\n      0xff77b1fcbebcdc4f_u64, 0x25e8e89c13bb0f7a_u64,\n      0x9faacf3df73609b1_u64, 0x77b191618c54e9ac_u64,\n      0xc795830d75038c1d_u64, 0xd59df5b9ef6a2417_u64,\n      0xf97ae3d0d2446f25_u64, 0x4b0573286b44ad1d_u64,\n      0x9becce62836ac577_u64, 0x4ee367f9430aec32_u64,\n      0xc2e801fb244576d5_u64, 0x229c41f793cda73f_u64,\n      0xf3a20279ed56d48a_u64, 0x6b43527578c1110f_u64,\n      0x9845418c345644d6_u64, 0x830a13896b78aaa9_u64,\n      0xbe5691ef416bd60c_u64, 0x23cc986bc656d553_u64,\n      0xedec366b11c6cb8f_u64, 0x2cbfbe86b7ec8aa8_u64,\n      0x94b3a202eb1c3f39_u64, 0x7bf7d71432f3d6a9_u64,\n      0xb9e08a83a5e34f07_u64, 0xdaf5ccd93fb0cc53_u64,\n      0xe858ad248f5c22c9_u64, 0xd1b3400f8f9cff68_u64,\n      0x91376c36d99995be_u64, 0x23100809b9c21fa1_u64,\n      0xb58547448ffffb2d_u64, 0xabd40a0c2832a78a_u64,\n      0xe2e69915b3fff9f9_u64, 0x16c90c8f323f516c_u64,\n      0x8dd01fad907ffc3b_u64, 0xae3da7d97f6792e3_u64,\n      0xb1442798f49ffb4a_u64, 0x99cd11cfdf41779c_u64,\n      0xdd95317f31c7fa1d_u64, 0x40405643d711d583_u64,\n      0x8a7d3eef7f1cfc52_u64, 0x482835ea666b2572_u64,\n      0xad1c8eab5ee43b66_u64, 0xda3243650005eecf_u64,\n      0xd863b256369d4a40_u64, 0x90bed43e40076a82_u64,\n      0x873e4f75e2224e68_u64, 0x5a7744a6e804a291_u64,\n      0xa90de3535aaae202_u64, 0x711515d0a205cb36_u64,\n      0xd3515c2831559a83_u64, 0xd5a5b44ca873e03_u64,\n      0x8412d9991ed58091_u64, 0xe858790afe9486c2_u64,\n      0xa5178fff668ae0b6_u64, 0x626e974dbe39a872_u64,\n      0xce5d73ff402d98e3_u64, 0xfb0a3d212dc8128f_u64,\n      0x80fa687f881c7f8e_u64, 0x7ce66634bc9d0b99_u64,\n      0xa139029f6a239f72_u64, 0x1c1fffc1ebc44e80_u64,\n      0xc987434744ac874e_u64, 0xa327ffb266b56220_u64,\n      0xfbe9141915d7a922_u64, 0x4bf1ff9f0062baa8_u64,\n      0x9d71ac8fada6c9b5_u64, 0x6f773fc3603db4a9_u64,\n      0xc4ce17b399107c22_u64, 0xcb550fb4384d21d3_u64,\n      0xf6019da07f549b2b_u64, 0x7e2a53a146606a48_u64,\n      0x99c102844f94e0fb_u64, 0x2eda7444cbfc426d_u64,\n      0xc0314325637a1939_u64, 0xfa911155fefb5308_u64,\n      0xf03d93eebc589f88_u64, 0x793555ab7eba27ca_u64,\n      0x96267c7535b763b5_u64, 0x4bc1558b2f3458de_u64,\n      0xbbb01b9283253ca2_u64, 0x9eb1aaedfb016f16_u64,\n      0xea9c227723ee8bcb_u64, 0x465e15a979c1cadc_u64,\n      0x92a1958a7675175f_u64, 0xbfacd89ec191ec9_u64,\n      0xb749faed14125d36_u64, 0xcef980ec671f667b_u64,\n      0xe51c79a85916f484_u64, 0x82b7e12780e7401a_u64,\n      0x8f31cc0937ae58d2_u64, 0xd1b2ecb8b0908810_u64,\n      0xb2fe3f0b8599ef07_u64, 0x861fa7e6dcb4aa15_u64,\n      0xdfbdcece67006ac9_u64, 0x67a791e093e1d49a_u64,\n      0x8bd6a141006042bd_u64, 0xe0c8bb2c5c6d24e0_u64,\n      0xaecc49914078536d_u64, 0x58fae9f773886e18_u64,\n      0xda7f5bf590966848_u64, 0xaf39a475506a899e_u64,\n      0x888f99797a5e012d_u64, 0x6d8406c952429603_u64,\n      0xaab37fd7d8f58178_u64, 0xc8e5087ba6d33b83_u64,\n      0xd5605fcdcf32e1d6_u64, 0xfb1e4a9a90880a64_u64,\n      0x855c3be0a17fcd26_u64, 0x5cf2eea09a55067f_u64,\n      0xa6b34ad8c9dfc06f_u64, 0xf42faa48c0ea481e_u64,\n      0xd0601d8efc57b08b_u64, 0xf13b94daf124da26_u64,\n      0x823c12795db6ce57_u64, 0x76c53d08d6b70858_u64,\n      0xa2cb1717b52481ed_u64, 0x54768c4b0c64ca6e_u64,\n      0xcb7ddcdda26da268_u64, 0xa9942f5dcf7dfd09_u64,\n      0xfe5d54150b090b02_u64, 0xd3f93b35435d7c4c_u64,\n      0x9efa548d26e5a6e1_u64, 0xc47bc5014a1a6daf_u64,\n      0xc6b8e9b0709f109a_u64, 0x359ab6419ca1091b_u64,\n      0xf867241c8cc6d4c0_u64, 0xc30163d203c94b62_u64,\n      0x9b407691d7fc44f8_u64, 0x79e0de63425dcf1d_u64,\n      0xc21094364dfb5636_u64, 0x985915fc12f542e4_u64,\n      0xf294b943e17a2bc4_u64, 0x3e6f5b7b17b2939d_u64,\n      0x979cf3ca6cec5b5a_u64, 0xa705992ceecf9c42_u64,\n      0xbd8430bd08277231_u64, 0x50c6ff782a838353_u64,\n      0xece53cec4a314ebd_u64, 0xa4f8bf5635246428_u64,\n      0x940f4613ae5ed136_u64, 0x871b7795e136be99_u64,\n      0xb913179899f68584_u64, 0x28e2557b59846e3f_u64,\n      0xe757dd7ec07426e5_u64, 0x331aeada2fe589cf_u64,\n      0x9096ea6f3848984f_u64, 0x3ff0d2c85def7621_u64,\n      0xb4bca50b065abe63_u64, 0xfed077a756b53a9_u64,\n      0xe1ebce4dc7f16dfb_u64, 0xd3e8495912c62894_u64,\n      0x8d3360f09cf6e4bd_u64, 0x64712dd7abbbd95c_u64,\n      0xb080392cc4349dec_u64, 0xbd8d794d96aacfb3_u64,\n      0xdca04777f541c567_u64, 0xecf0d7a0fc5583a0_u64,\n      0x89e42caaf9491b60_u64, 0xf41686c49db57244_u64,\n      0xac5d37d5b79b6239_u64, 0x311c2875c522ced5_u64,\n      0xd77485cb25823ac7_u64, 0x7d633293366b828b_u64,\n      0x86a8d39ef77164bc_u64, 0xae5dff9c02033197_u64,\n      0xa8530886b54dbdeb_u64, 0xd9f57f830283fdfc_u64,\n      0xd267caa862a12d66_u64, 0xd072df63c324fd7b_u64,\n      0x8380dea93da4bc60_u64, 0x4247cb9e59f71e6d_u64,\n      0xa46116538d0deb78_u64, 0x52d9be85f074e608_u64,\n      0xcd795be870516656_u64, 0x67902e276c921f8b_u64,\n      0x806bd9714632dff6_u64, 0xba1cd8a3db53b6_u64,\n      0xa086cfcd97bf97f3_u64, 0x80e8a40eccd228a4_u64,\n      0xc8a883c0fdaf7df0_u64, 0x6122cd128006b2cd_u64,\n      0xfad2a4b13d1b5d6c_u64, 0x796b805720085f81_u64,\n      0x9cc3a6eec6311a63_u64, 0xcbe3303674053bb0_u64,\n      0xc3f490aa77bd60fc_u64, 0xbedbfc4411068a9c_u64,\n      0xf4f1b4d515acb93b_u64, 0xee92fb5515482d44_u64,\n      0x991711052d8bf3c5_u64, 0x751bdd152d4d1c4a_u64,\n      0xbf5cd54678eef0b6_u64, 0xd262d45a78a0635d_u64,\n      0xef340a98172aace4_u64, 0x86fb897116c87c34_u64,\n      0x9580869f0e7aac0e_u64, 0xd45d35e6ae3d4da0_u64,\n      0xbae0a846d2195712_u64, 0x8974836059cca109_u64,\n      0xe998d258869facd7_u64, 0x2bd1a438703fc94b_u64,\n      0x91ff83775423cc06_u64, 0x7b6306a34627ddcf_u64,\n      0xb67f6455292cbf08_u64, 0x1a3bc84c17b1d542_u64,\n      0xe41f3d6a7377eeca_u64, 0x20caba5f1d9e4a93_u64,\n      0x8e938662882af53e_u64, 0x547eb47b7282ee9c_u64,\n      0xb23867fb2a35b28d_u64, 0xe99e619a4f23aa43_u64,\n      0xdec681f9f4c31f31_u64, 0x6405fa00e2ec94d4_u64,\n      0x8b3c113c38f9f37e_u64, 0xde83bc408dd3dd04_u64,\n      0xae0b158b4738705e_u64, 0x9624ab50b148d445_u64,\n      0xd98ddaee19068c76_u64, 0x3badd624dd9b0957_u64,\n      0x87f8a8d4cfa417c9_u64, 0xe54ca5d70a80e5d6_u64,\n      0xa9f6d30a038d1dbc_u64, 0x5e9fcf4ccd211f4c_u64,\n      0xd47487cc8470652b_u64, 0x7647c3200069671f_u64,\n      0x84c8d4dfd2c63f3b_u64, 0x29ecd9f40041e073_u64,\n      0xa5fb0a17c777cf09_u64, 0xf468107100525890_u64,\n      0xcf79cc9db955c2cc_u64, 0x7182148d4066eeb4_u64,\n      0x81ac1fe293d599bf_u64, 0xc6f14cd848405530_u64,\n      0xa21727db38cb002f_u64, 0xb8ada00e5a506a7c_u64,\n      0xca9cf1d206fdc03b_u64, 0xa6d90811f0e4851c_u64,\n      0xfd442e4688bd304a_u64, 0x908f4a166d1da663_u64,\n      0x9e4a9cec15763e2e_u64, 0x9a598e4e043287fe_u64,\n      0xc5dd44271ad3cdba_u64, 0x40eff1e1853f29fd_u64,\n      0xf7549530e188c128_u64, 0xd12bee59e68ef47c_u64,\n      0x9a94dd3e8cf578b9_u64, 0x82bb74f8301958ce_u64,\n      0xc13a148e3032d6e7_u64, 0xe36a52363c1faf01_u64,\n      0xf18899b1bc3f8ca1_u64, 0xdc44e6c3cb279ac1_u64,\n      0x96f5600f15a7b7e5_u64, 0x29ab103a5ef8c0b9_u64,\n      0xbcb2b812db11a5de_u64, 0x7415d448f6b6f0e7_u64,\n      0xebdf661791d60f56_u64, 0x111b495b3464ad21_u64,\n      0x936b9fcebb25c995_u64, 0xcab10dd900beec34_u64,\n      0xb84687c269ef3bfb_u64, 0x3d5d514f40eea742_u64,\n      0xe65829b3046b0afa_u64, 0xcb4a5a3112a5112_u64,\n      0x8ff71a0fe2c2e6dc_u64, 0x47f0e785eaba72ab_u64,\n      0xb3f4e093db73a093_u64, 0x59ed216765690f56_u64,\n      0xe0f218b8d25088b8_u64, 0x306869c13ec3532c_u64,\n      0x8c974f7383725573_u64, 0x1e414218c73a13fb_u64,\n      0xafbd2350644eeacf_u64, 0xe5d1929ef90898fa_u64,\n      0xdbac6c247d62a583_u64, 0xdf45f746b74abf39_u64,\n      0x894bc396ce5da772_u64, 0x6b8bba8c328eb783_u64,\n      0xab9eb47c81f5114f_u64, 0x66ea92f3f326564_u64,\n      0xd686619ba27255a2_u64, 0xc80a537b0efefebd_u64,\n      0x8613fd0145877585_u64, 0xbd06742ce95f5f36_u64,\n      0xa798fc4196e952e7_u64, 0x2c48113823b73704_u64,\n      0xd17f3b51fca3a7a0_u64, 0xf75a15862ca504c5_u64,\n      0x82ef85133de648c4_u64, 0x9a984d73dbe722fb_u64,\n      0xa3ab66580d5fdaf5_u64, 0xc13e60d0d2e0ebba_u64,\n      0xcc963fee10b7d1b3_u64, 0x318df905079926a8_u64,\n      0xffbbcfe994e5c61f_u64, 0xfdf17746497f7052_u64,\n      0x9fd561f1fd0f9bd3_u64, 0xfeb6ea8bedefa633_u64,\n      0xc7caba6e7c5382c8_u64, 0xfe64a52ee96b8fc0_u64,\n      0xf9bd690a1b68637b_u64, 0x3dfdce7aa3c673b0_u64,\n      0x9c1661a651213e2d_u64, 0x6bea10ca65c084e_u64,\n      0xc31bfa0fe5698db8_u64, 0x486e494fcff30a62_u64,\n      0xf3e2f893dec3f126_u64, 0x5a89dba3c3efccfa_u64,\n      0x986ddb5c6b3a76b7_u64, 0xf89629465a75e01c_u64,\n      0xbe89523386091465_u64, 0xf6bbb397f1135823_u64,\n      0xee2ba6c0678b597f_u64, 0x746aa07ded582e2c_u64,\n      0x94db483840b717ef_u64, 0xa8c2a44eb4571cdc_u64,\n      0xba121a4650e4ddeb_u64, 0x92f34d62616ce413_u64,\n      0xe896a0d7e51e1566_u64, 0x77b020baf9c81d17_u64,\n      0x915e2486ef32cd60_u64, 0xace1474dc1d122e_u64,\n      0xb5b5ada8aaff80b8_u64, 0xd819992132456ba_u64,\n      0xe3231912d5bf60e6_u64, 0x10e1fff697ed6c69_u64,\n      0x8df5efabc5979c8f_u64, 0xca8d3ffa1ef463c1_u64,\n      0xb1736b96b6fd83b3_u64, 0xbd308ff8a6b17cb2_u64,\n      0xddd0467c64bce4a0_u64, 0xac7cb3f6d05ddbde_u64,\n      0x8aa22c0dbef60ee4_u64, 0x6bcdf07a423aa96b_u64,\n      0xad4ab7112eb3929d_u64, 0x86c16c98d2c953c6_u64,\n      0xd89d64d57a607744_u64, 0xe871c7bf077ba8b7_u64,\n      0x87625f056c7c4a8b_u64, 0x11471cd764ad4972_u64,\n      0xa93af6c6c79b5d2d_u64, 0xd598e40d3dd89bcf_u64,\n      0xd389b47879823479_u64, 0x4aff1d108d4ec2c3_u64,\n      0x843610cb4bf160cb_u64, 0xcedf722a585139ba_u64,\n      0xa54394fe1eedb8fe_u64, 0xc2974eb4ee658828_u64,\n      0xce947a3da6a9273e_u64, 0x733d226229feea32_u64,\n      0x811ccc668829b887_u64, 0x806357d5a3f525f_u64,\n      0xa163ff802a3426a8_u64, 0xca07c2dcb0cf26f7_u64,\n      0xc9bcff6034c13052_u64, 0xfc89b393dd02f0b5_u64,\n      0xfc2c3f3841f17c67_u64, 0xbbac2078d443ace2_u64,\n      0x9d9ba7832936edc0_u64, 0xd54b944b84aa4c0d_u64,\n      0xc5029163f384a931_u64, 0xa9e795e65d4df11_u64,\n      0xf64335bcf065d37d_u64, 0x4d4617b5ff4a16d5_u64,\n      0x99ea0196163fa42e_u64, 0x504bced1bf8e4e45_u64,\n      0xc06481fb9bcf8d39_u64, 0xe45ec2862f71e1d6_u64,\n      0xf07da27a82c37088_u64, 0x5d767327bb4e5a4c_u64,\n      0x964e858c91ba2655_u64, 0x3a6a07f8d510f86f_u64,\n      0xbbe226efb628afea_u64, 0x890489f70a55368b_u64,\n      0xeadab0aba3b2dbe5_u64, 0x2b45ac74ccea842e_u64,\n      0x92c8ae6b464fc96f_u64, 0x3b0b8bc90012929d_u64,\n      0xb77ada0617e3bbcb_u64, 0x9ce6ebb40173744_u64,\n      0xe55990879ddcaabd_u64, 0xcc420a6a101d0515_u64,\n      0x8f57fa54c2a9eab6_u64, 0x9fa946824a12232d_u64,\n      0xb32df8e9f3546564_u64, 0x47939822dc96abf9_u64,\n      0xdff9772470297ebd_u64, 0x59787e2b93bc56f7_u64,\n      0x8bfbea76c619ef36_u64, 0x57eb4edb3c55b65a_u64,\n      0xaefae51477a06b03_u64, 0xede622920b6b23f1_u64,\n      0xdab99e59958885c4_u64, 0xe95fab368e45eced_u64,\n      0x88b402f7fd75539b_u64, 0x11dbcb0218ebb414_u64,\n      0xaae103b5fcd2a881_u64, 0xd652bdc29f26a119_u64,\n      0xd59944a37c0752a2_u64, 0x4be76d3346f0495f_u64,\n      0x857fcae62d8493a5_u64, 0x6f70a4400c562ddb_u64,\n      0xa6dfbd9fb8e5b88e_u64, 0xcb4ccd500f6bb952_u64,\n      0xd097ad07a71f26b2_u64, 0x7e2000a41346a7a7_u64,\n      0x825ecc24c873782f_u64, 0x8ed400668c0c28c8_u64,\n      0xa2f67f2dfa90563b_u64, 0x728900802f0f32fa_u64,\n      0xcbb41ef979346bca_u64, 0x4f2b40a03ad2ffb9_u64,\n      0xfea126b7d78186bc_u64, 0xe2f610c84987bfa8_u64,\n      0x9f24b832e6b0f436_u64, 0xdd9ca7d2df4d7c9_u64,\n      0xc6ede63fa05d3143_u64, 0x91503d1c79720dbb_u64,\n      0xf8a95fcf88747d94_u64, 0x75a44c6397ce912a_u64,\n      0x9b69dbe1b548ce7c_u64, 0xc986afbe3ee11aba_u64,\n      0xc24452da229b021b_u64, 0xfbe85badce996168_u64,\n      0xf2d56790ab41c2a2_u64, 0xfae27299423fb9c3_u64,\n      0x97c560ba6b0919a5_u64, 0xdccd879fc967d41a_u64,\n      0xbdb6b8e905cb600f_u64, 0x5400e987bbc1c920_u64,\n      0xed246723473e3813_u64, 0x290123e9aab23b68_u64,\n      0x9436c0760c86e30b_u64, 0xf9a0b6720aaf6521_u64,\n      0xb94470938fa89bce_u64, 0xf808e40e8d5b3e69_u64,\n      0xe7958cb87392c2c2_u64, 0xb60b1d1230b20e04_u64,\n      0x90bd77f3483bb9b9_u64, 0xb1c6f22b5e6f48c2_u64,\n      0xb4ecd5f01a4aa828_u64, 0x1e38aeb6360b1af3_u64,\n      0xe2280b6c20dd5232_u64, 0x25c6da63c38de1b0_u64,\n      0x8d590723948a535f_u64, 0x579c487e5a38ad0e_u64,\n      0xb0af48ec79ace837_u64, 0x2d835a9df0c6d851_u64,\n      0xdcdb1b2798182244_u64, 0xf8e431456cf88e65_u64,\n      0x8a08f0f8bf0f156b_u64, 0x1b8e9ecb641b58ff_u64,\n      0xac8b2d36eed2dac5_u64, 0xe272467e3d222f3f_u64,\n      0xd7adf884aa879177_u64, 0x5b0ed81dcc6abb0f_u64,\n      0x86ccbb52ea94baea_u64, 0x98e947129fc2b4e9_u64,\n      0xa87fea27a539e9a5_u64, 0x3f2398d747b36224_u64,\n      0xd29fe4b18e88640e_u64, 0x8eec7f0d19a03aad_u64,\n      0x83a3eeeef9153e89_u64, 0x1953cf68300424ac_u64,\n      0xa48ceaaab75a8e2b_u64, 0x5fa8c3423c052dd7_u64,\n      0xcdb02555653131b6_u64, 0x3792f412cb06794d_u64,\n      0x808e17555f3ebf11_u64, 0xe2bbd88bbee40bd0_u64,\n      0xa0b19d2ab70e6ed6_u64, 0x5b6aceaeae9d0ec4_u64,\n      0xc8de047564d20a8b_u64, 0xf245825a5a445275_u64,\n      0xfb158592be068d2e_u64, 0xeed6e2f0f0d56712_u64,\n      0x9ced737bb6c4183d_u64, 0x55464dd69685606b_u64,\n      0xc428d05aa4751e4c_u64, 0xaa97e14c3c26b886_u64,\n      0xf53304714d9265df_u64, 0xd53dd99f4b3066a8_u64,\n      0x993fe2c6d07b7fab_u64, 0xe546a8038efe4029_u64,\n      0xbf8fdb78849a5f96_u64, 0xde98520472bdd033_u64,\n      0xef73d256a5c0f77c_u64, 0x963e66858f6d4440_u64,\n      0x95a8637627989aad_u64, 0xdde7001379a44aa8_u64,\n      0xbb127c53b17ec159_u64, 0x5560c018580d5d52_u64,\n      0xe9d71b689dde71af_u64, 0xaab8f01e6e10b4a6_u64,\n      0x9226712162ab070d_u64, 0xcab3961304ca70e8_u64,\n      0xb6b00d69bb55c8d1_u64, 0x3d607b97c5fd0d22_u64,\n      0xe45c10c42a2b3b05_u64, 0x8cb89a7db77c506a_u64,\n      0x8eb98a7a9a5b04e3_u64, 0x77f3608e92adb242_u64,\n      0xb267ed1940f1c61c_u64, 0x55f038b237591ed3_u64,\n      0xdf01e85f912e37a3_u64, 0x6b6c46dec52f6688_u64,\n      0x8b61313bbabce2c6_u64, 0x2323ac4b3b3da015_u64,\n      0xae397d8aa96c1b77_u64, 0xabec975e0a0d081a_u64,\n      0xd9c7dced53c72255_u64, 0x96e7bd358c904a21_u64,\n      0x881cea14545c7575_u64, 0x7e50d64177da2e54_u64,\n      0xaa242499697392d2_u64, 0xdde50bd1d5d0b9e9_u64,\n      0xd4ad2dbfc3d07787_u64, 0x955e4ec64b44e864_u64,\n      0x84ec3c97da624ab4_u64, 0xbd5af13bef0b113e_u64,\n      0xa6274bbdd0fadd61_u64, 0xecb1ad8aeacdd58e_u64,\n      0xcfb11ead453994ba_u64, 0x67de18eda5814af2_u64,\n      0x81ceb32c4b43fcf4_u64, 0x80eacf948770ced7_u64,\n      0xa2425ff75e14fc31_u64, 0xa1258379a94d028d_u64,\n      0xcad2f7f5359a3b3e_u64, 0x96ee45813a04330_u64,\n      0xfd87b5f28300ca0d_u64, 0x8bca9d6e188853fc_u64,\n      0x9e74d1b791e07e48_u64, 0x775ea264cf55347e_u64,\n      0xc612062576589dda_u64, 0x95364afe032a819e_u64,\n      0xf79687aed3eec551_u64, 0x3a83ddbd83f52205_u64,\n      0x9abe14cd44753b52_u64, 0xc4926a9672793543_u64,\n      0xc16d9a0095928a27_u64, 0x75b7053c0f178294_u64,\n      0xf1c90080baf72cb1_u64, 0x5324c68b12dd6339_u64,\n      0x971da05074da7bee_u64, 0xd3f6fc16ebca5e04_u64,\n      0xbce5086492111aea_u64, 0x88f4bb1ca6bcf585_u64,\n      0xec1e4a7db69561a5_u64, 0x2b31e9e3d06c32e6_u64,\n      0x9392ee8e921d5d07_u64, 0x3aff322e62439fd0_u64,\n      0xb877aa3236a4b449_u64, 0x9befeb9fad487c3_u64,\n      0xe69594bec44de15b_u64, 0x4c2ebe687989a9b4_u64,\n      0x901d7cf73ab0acd9_u64, 0xf9d37014bf60a11_u64,\n      0xb424dc35095cd80f_u64, 0x538484c19ef38c95_u64,\n      0xe12e13424bb40e13_u64, 0x2865a5f206b06fba_u64,\n      0x8cbccc096f5088cb_u64, 0xf93f87b7442e45d4_u64,\n      0xafebff0bcb24aafe_u64, 0xf78f69a51539d749_u64,\n      0xdbe6fecebdedd5be_u64, 0xb573440e5a884d1c_u64,\n      0x89705f4136b4a597_u64, 0x31680a88f8953031_u64,\n      0xabcc77118461cefc_u64, 0xfdc20d2b36ba7c3e_u64,\n      0xd6bf94d5e57a42bc_u64, 0x3d32907604691b4d_u64,\n      0x8637bd05af6c69b5_u64, 0xa63f9a49c2c1b110_u64,\n      0xa7c5ac471b478423_u64, 0xfcf80dc33721d54_u64,\n      0xd1b71758e219652b_u64, 0xd3c36113404ea4a9_u64,\n      0x83126e978d4fdf3b_u64, 0x645a1cac083126ea_u64,\n      0xa3d70a3d70a3d70a_u64, 0x3d70a3d70a3d70a4_u64,\n      0xcccccccccccccccc_u64, 0xcccccccccccccccd_u64,\n      0x8000000000000000_u64, 0x0_u64,\n      0xa000000000000000_u64, 0x0_u64,\n      0xc800000000000000_u64, 0x0_u64,\n      0xfa00000000000000_u64, 0x0_u64,\n      0x9c40000000000000_u64, 0x0_u64,\n      0xc350000000000000_u64, 0x0_u64,\n      0xf424000000000000_u64, 0x0_u64,\n      0x9896800000000000_u64, 0x0_u64,\n      0xbebc200000000000_u64, 0x0_u64,\n      0xee6b280000000000_u64, 0x0_u64,\n      0x9502f90000000000_u64, 0x0_u64,\n      0xba43b74000000000_u64, 0x0_u64,\n      0xe8d4a51000000000_u64, 0x0_u64,\n      0x9184e72a00000000_u64, 0x0_u64,\n      0xb5e620f480000000_u64, 0x0_u64,\n      0xe35fa931a0000000_u64, 0x0_u64,\n      0x8e1bc9bf04000000_u64, 0x0_u64,\n      0xb1a2bc2ec5000000_u64, 0x0_u64,\n      0xde0b6b3a76400000_u64, 0x0_u64,\n      0x8ac7230489e80000_u64, 0x0_u64,\n      0xad78ebc5ac620000_u64, 0x0_u64,\n      0xd8d726b7177a8000_u64, 0x0_u64,\n      0x878678326eac9000_u64, 0x0_u64,\n      0xa968163f0a57b400_u64, 0x0_u64,\n      0xd3c21bcecceda100_u64, 0x0_u64,\n      0x84595161401484a0_u64, 0x0_u64,\n      0xa56fa5b99019a5c8_u64, 0x0_u64,\n      0xcecb8f27f4200f3a_u64, 0x0_u64,\n      0x813f3978f8940984_u64, 0x4000000000000000_u64,\n      0xa18f07d736b90be5_u64, 0x5000000000000000_u64,\n      0xc9f2c9cd04674ede_u64, 0xa400000000000000_u64,\n      0xfc6f7c4045812296_u64, 0x4d00000000000000_u64,\n      0x9dc5ada82b70b59d_u64, 0xf020000000000000_u64,\n      0xc5371912364ce305_u64, 0x6c28000000000000_u64,\n      0xf684df56c3e01bc6_u64, 0xc732000000000000_u64,\n      0x9a130b963a6c115c_u64, 0x3c7f400000000000_u64,\n      0xc097ce7bc90715b3_u64, 0x4b9f100000000000_u64,\n      0xf0bdc21abb48db20_u64, 0x1e86d40000000000_u64,\n      0x96769950b50d88f4_u64, 0x1314448000000000_u64,\n      0xbc143fa4e250eb31_u64, 0x17d955a000000000_u64,\n      0xeb194f8e1ae525fd_u64, 0x5dcfab0800000000_u64,\n      0x92efd1b8d0cf37be_u64, 0x5aa1cae500000000_u64,\n      0xb7abc627050305ad_u64, 0xf14a3d9e40000000_u64,\n      0xe596b7b0c643c719_u64, 0x6d9ccd05d0000000_u64,\n      0x8f7e32ce7bea5c6f_u64, 0xe4820023a2000000_u64,\n      0xb35dbf821ae4f38b_u64, 0xdda2802c8a800000_u64,\n      0xe0352f62a19e306e_u64, 0xd50b2037ad200000_u64,\n      0x8c213d9da502de45_u64, 0x4526f422cc340000_u64,\n      0xaf298d050e4395d6_u64, 0x9670b12b7f410000_u64,\n      0xdaf3f04651d47b4c_u64, 0x3c0cdd765f114000_u64,\n      0x88d8762bf324cd0f_u64, 0xa5880a69fb6ac800_u64,\n      0xab0e93b6efee0053_u64, 0x8eea0d047a457a00_u64,\n      0xd5d238a4abe98068_u64, 0x72a4904598d6d880_u64,\n      0x85a36366eb71f041_u64, 0x47a6da2b7f864750_u64,\n      0xa70c3c40a64e6c51_u64, 0x999090b65f67d924_u64,\n      0xd0cf4b50cfe20765_u64, 0xfff4b4e3f741cf6d_u64,\n      0x82818f1281ed449f_u64, 0xbff8f10e7a8921a4_u64,\n      0xa321f2d7226895c7_u64, 0xaff72d52192b6a0d_u64,\n      0xcbea6f8ceb02bb39_u64, 0x9bf4f8a69f764490_u64,\n      0xfee50b7025c36a08_u64, 0x2f236d04753d5b4_u64,\n      0x9f4f2726179a2245_u64, 0x1d762422c946590_u64,\n      0xc722f0ef9d80aad6_u64, 0x424d3ad2b7b97ef5_u64,\n      0xf8ebad2b84e0d58b_u64, 0xd2e0898765a7deb2_u64,\n      0x9b934c3b330c8577_u64, 0x63cc55f49f88eb2f_u64,\n      0xc2781f49ffcfa6d5_u64, 0x3cbf6b71c76b25fb_u64,\n      0xf316271c7fc3908a_u64, 0x8bef464e3945ef7a_u64,\n      0x97edd871cfda3a56_u64, 0x97758bf0e3cbb5ac_u64,\n      0xbde94e8e43d0c8ec_u64, 0x3d52eeed1cbea317_u64,\n      0xed63a231d4c4fb27_u64, 0x4ca7aaa863ee4bdd_u64,\n      0x945e455f24fb1cf8_u64, 0x8fe8caa93e74ef6a_u64,\n      0xb975d6b6ee39e436_u64, 0xb3e2fd538e122b44_u64,\n      0xe7d34c64a9c85d44_u64, 0x60dbbca87196b616_u64,\n      0x90e40fbeea1d3a4a_u64, 0xbc8955e946fe31cd_u64,\n      0xb51d13aea4a488dd_u64, 0x6babab6398bdbe41_u64,\n      0xe264589a4dcdab14_u64, 0xc696963c7eed2dd1_u64,\n      0x8d7eb76070a08aec_u64, 0xfc1e1de5cf543ca2_u64,\n      0xb0de65388cc8ada8_u64, 0x3b25a55f43294bcb_u64,\n      0xdd15fe86affad912_u64, 0x49ef0eb713f39ebe_u64,\n      0x8a2dbf142dfcc7ab_u64, 0x6e3569326c784337_u64,\n      0xacb92ed9397bf996_u64, 0x49c2c37f07965404_u64,\n      0xd7e77a8f87daf7fb_u64, 0xdc33745ec97be906_u64,\n      0x86f0ac99b4e8dafd_u64, 0x69a028bb3ded71a3_u64,\n      0xa8acd7c0222311bc_u64, 0xc40832ea0d68ce0c_u64,\n      0xd2d80db02aabd62b_u64, 0xf50a3fa490c30190_u64,\n      0x83c7088e1aab65db_u64, 0x792667c6da79e0fa_u64,\n      0xa4b8cab1a1563f52_u64, 0x577001b891185938_u64,\n      0xcde6fd5e09abcf26_u64, 0xed4c0226b55e6f86_u64,\n      0x80b05e5ac60b6178_u64, 0x544f8158315b05b4_u64,\n      0xa0dc75f1778e39d6_u64, 0x696361ae3db1c721_u64,\n      0xc913936dd571c84c_u64, 0x3bc3a19cd1e38e9_u64,\n      0xfb5878494ace3a5f_u64, 0x4ab48a04065c723_u64,\n      0x9d174b2dcec0e47b_u64, 0x62eb0d64283f9c76_u64,\n      0xc45d1df942711d9a_u64, 0x3ba5d0bd324f8394_u64,\n      0xf5746577930d6500_u64, 0xca8f44ec7ee36479_u64,\n      0x9968bf6abbe85f20_u64, 0x7e998b13cf4e1ecb_u64,\n      0xbfc2ef456ae276e8_u64, 0x9e3fedd8c321a67e_u64,\n      0xefb3ab16c59b14a2_u64, 0xc5cfe94ef3ea101e_u64,\n      0x95d04aee3b80ece5_u64, 0xbba1f1d158724a12_u64,\n      0xbb445da9ca61281f_u64, 0x2a8a6e45ae8edc97_u64,\n      0xea1575143cf97226_u64, 0xf52d09d71a3293bd_u64,\n      0x924d692ca61be758_u64, 0x593c2626705f9c56_u64,\n      0xb6e0c377cfa2e12e_u64, 0x6f8b2fb00c77836c_u64,\n      0xe498f455c38b997a_u64, 0xb6dfb9c0f956447_u64,\n      0x8edf98b59a373fec_u64, 0x4724bd4189bd5eac_u64,\n      0xb2977ee300c50fe7_u64, 0x58edec91ec2cb657_u64,\n      0xdf3d5e9bc0f653e1_u64, 0x2f2967b66737e3ed_u64,\n      0x8b865b215899f46c_u64, 0xbd79e0d20082ee74_u64,\n      0xae67f1e9aec07187_u64, 0xecd8590680a3aa11_u64,\n      0xda01ee641a708de9_u64, 0xe80e6f4820cc9495_u64,\n      0x884134fe908658b2_u64, 0x3109058d147fdcdd_u64,\n      0xaa51823e34a7eede_u64, 0xbd4b46f0599fd415_u64,\n      0xd4e5e2cdc1d1ea96_u64, 0x6c9e18ac7007c91a_u64,\n      0x850fadc09923329e_u64, 0x3e2cf6bc604ddb0_u64,\n      0xa6539930bf6bff45_u64, 0x84db8346b786151c_u64,\n      0xcfe87f7cef46ff16_u64, 0xe612641865679a63_u64,\n      0x81f14fae158c5f6e_u64, 0x4fcb7e8f3f60c07e_u64,\n      0xa26da3999aef7749_u64, 0xe3be5e330f38f09d_u64,\n      0xcb090c8001ab551c_u64, 0x5cadf5bfd3072cc5_u64,\n      0xfdcb4fa002162a63_u64, 0x73d9732fc7c8f7f6_u64,\n      0x9e9f11c4014dda7e_u64, 0x2867e7fddcdd9afa_u64,\n      0xc646d63501a1511d_u64, 0xb281e1fd541501b8_u64,\n      0xf7d88bc24209a565_u64, 0x1f225a7ca91a4226_u64,\n      0x9ae757596946075f_u64, 0x3375788de9b06958_u64,\n      0xc1a12d2fc3978937_u64, 0x52d6b1641c83ae_u64,\n      0xf209787bb47d6b84_u64, 0xc0678c5dbd23a49a_u64,\n      0x9745eb4d50ce6332_u64, 0xf840b7ba963646e0_u64,\n      0xbd176620a501fbff_u64, 0xb650e5a93bc3d898_u64,\n      0xec5d3fa8ce427aff_u64, 0xa3e51f138ab4cebe_u64,\n      0x93ba47c980e98cdf_u64, 0xc66f336c36b10137_u64,\n      0xb8a8d9bbe123f017_u64, 0xb80b0047445d4184_u64,\n      0xe6d3102ad96cec1d_u64, 0xa60dc059157491e5_u64,\n      0x9043ea1ac7e41392_u64, 0x87c89837ad68db2f_u64,\n      0xb454e4a179dd1877_u64, 0x29babe4598c311fb_u64,\n      0xe16a1dc9d8545e94_u64, 0xf4296dd6fef3d67a_u64,\n      0x8ce2529e2734bb1d_u64, 0x1899e4a65f58660c_u64,\n      0xb01ae745b101e9e4_u64, 0x5ec05dcff72e7f8f_u64,\n      0xdc21a1171d42645d_u64, 0x76707543f4fa1f73_u64,\n      0x899504ae72497eba_u64, 0x6a06494a791c53a8_u64,\n      0xabfa45da0edbde69_u64, 0x487db9d17636892_u64,\n      0xd6f8d7509292d603_u64, 0x45a9d2845d3c42b6_u64,\n      0x865b86925b9bc5c2_u64, 0xb8a2392ba45a9b2_u64,\n      0xa7f26836f282b732_u64, 0x8e6cac7768d7141e_u64,\n      0xd1ef0244af2364ff_u64, 0x3207d795430cd926_u64,\n      0x8335616aed761f1f_u64, 0x7f44e6bd49e807b8_u64,\n      0xa402b9c5a8d3a6e7_u64, 0x5f16206c9c6209a6_u64,\n      0xcd036837130890a1_u64, 0x36dba887c37a8c0f_u64,\n      0x802221226be55a64_u64, 0xc2494954da2c9789_u64,\n      0xa02aa96b06deb0fd_u64, 0xf2db9baa10b7bd6c_u64,\n      0xc83553c5c8965d3d_u64, 0x6f92829494e5acc7_u64,\n      0xfa42a8b73abbf48c_u64, 0xcb772339ba1f17f9_u64,\n      0x9c69a97284b578d7_u64, 0xff2a760414536efb_u64,\n      0xc38413cf25e2d70d_u64, 0xfef5138519684aba_u64,\n      0xf46518c2ef5b8cd1_u64, 0x7eb258665fc25d69_u64,\n      0x98bf2f79d5993802_u64, 0xef2f773ffbd97a61_u64,\n      0xbeeefb584aff8603_u64, 0xaafb550ffacfd8fa_u64,\n      0xeeaaba2e5dbf6784_u64, 0x95ba2a53f983cf38_u64,\n      0x952ab45cfa97a0b2_u64, 0xdd945a747bf26183_u64,\n      0xba756174393d88df_u64, 0x94f971119aeef9e4_u64,\n      0xe912b9d1478ceb17_u64, 0x7a37cd5601aab85d_u64,\n      0x91abb422ccb812ee_u64, 0xac62e055c10ab33a_u64,\n      0xb616a12b7fe617aa_u64, 0x577b986b314d6009_u64,\n      0xe39c49765fdf9d94_u64, 0xed5a7e85fda0b80b_u64,\n      0x8e41ade9fbebc27d_u64, 0x14588f13be847307_u64,\n      0xb1d219647ae6b31c_u64, 0x596eb2d8ae258fc8_u64,\n      0xde469fbd99a05fe3_u64, 0x6fca5f8ed9aef3bb_u64,\n      0x8aec23d680043bee_u64, 0x25de7bb9480d5854_u64,\n      0xada72ccc20054ae9_u64, 0xaf561aa79a10ae6a_u64,\n      0xd910f7ff28069da4_u64, 0x1b2ba1518094da04_u64,\n      0x87aa9aff79042286_u64, 0x90fb44d2f05d0842_u64,\n      0xa99541bf57452b28_u64, 0x353a1607ac744a53_u64,\n      0xd3fa922f2d1675f2_u64, 0x42889b8997915ce8_u64,\n      0x847c9b5d7c2e09b7_u64, 0x69956135febada11_u64,\n      0xa59bc234db398c25_u64, 0x43fab9837e699095_u64,\n      0xcf02b2c21207ef2e_u64, 0x94f967e45e03f4bb_u64,\n      0x8161afb94b44f57d_u64, 0x1d1be0eebac278f5_u64,\n      0xa1ba1ba79e1632dc_u64, 0x6462d92a69731732_u64,\n      0xca28a291859bbf93_u64, 0x7d7b8f7503cfdcfe_u64,\n      0xfcb2cb35e702af78_u64, 0x5cda735244c3d43e_u64,\n      0x9defbf01b061adab_u64, 0x3a0888136afa64a7_u64,\n      0xc56baec21c7a1916_u64, 0x88aaa1845b8fdd0_u64,\n      0xf6c69a72a3989f5b_u64, 0x8aad549e57273d45_u64,\n      0x9a3c2087a63f6399_u64, 0x36ac54e2f678864b_u64,\n      0xc0cb28a98fcf3c7f_u64, 0x84576a1bb416a7dd_u64,\n      0xf0fdf2d3f3c30b9f_u64, 0x656d44a2a11c51d5_u64,\n      0x969eb7c47859e743_u64, 0x9f644ae5a4b1b325_u64,\n      0xbc4665b596706114_u64, 0x873d5d9f0dde1fee_u64,\n      0xeb57ff22fc0c7959_u64, 0xa90cb506d155a7ea_u64,\n      0x9316ff75dd87cbd8_u64, 0x9a7f12442d588f2_u64,\n      0xb7dcbf5354e9bece_u64, 0xc11ed6d538aeb2f_u64,\n      0xe5d3ef282a242e81_u64, 0x8f1668c8a86da5fa_u64,\n      0x8fa475791a569d10_u64, 0xf96e017d694487bc_u64,\n      0xb38d92d760ec4455_u64, 0x37c981dcc395a9ac_u64,\n      0xe070f78d3927556a_u64, 0x85bbe253f47b1417_u64,\n      0x8c469ab843b89562_u64, 0x93956d7478ccec8e_u64,\n      0xaf58416654a6babb_u64, 0x387ac8d1970027b2_u64,\n      0xdb2e51bfe9d0696a_u64, 0x6997b05fcc0319e_u64,\n      0x88fcf317f22241e2_u64, 0x441fece3bdf81f03_u64,\n      0xab3c2fddeeaad25a_u64, 0xd527e81cad7626c3_u64,\n      0xd60b3bd56a5586f1_u64, 0x8a71e223d8d3b074_u64,\n      0x85c7056562757456_u64, 0xf6872d5667844e49_u64,\n      0xa738c6bebb12d16c_u64, 0xb428f8ac016561db_u64,\n      0xd106f86e69d785c7_u64, 0xe13336d701beba52_u64,\n      0x82a45b450226b39c_u64, 0xecc0024661173473_u64,\n      0xa34d721642b06084_u64, 0x27f002d7f95d0190_u64,\n      0xcc20ce9bd35c78a5_u64, 0x31ec038df7b441f4_u64,\n      0xff290242c83396ce_u64, 0x7e67047175a15271_u64,\n      0x9f79a169bd203e41_u64, 0xf0062c6e984d386_u64,\n      0xc75809c42c684dd1_u64, 0x52c07b78a3e60868_u64,\n      0xf92e0c3537826145_u64, 0xa7709a56ccdf8a82_u64,\n      0x9bbcc7a142b17ccb_u64, 0x88a66076400bb691_u64,\n      0xc2abf989935ddbfe_u64, 0x6acff893d00ea435_u64,\n      0xf356f7ebf83552fe_u64, 0x583f6b8c4124d43_u64,\n      0x98165af37b2153de_u64, 0xc3727a337a8b704a_u64,\n      0xbe1bf1b059e9a8d6_u64, 0x744f18c0592e4c5c_u64,\n      0xeda2ee1c7064130c_u64, 0x1162def06f79df73_u64,\n      0x9485d4d1c63e8be7_u64, 0x8addcb5645ac2ba8_u64,\n      0xb9a74a0637ce2ee1_u64, 0x6d953e2bd7173692_u64,\n      0xe8111c87c5c1ba99_u64, 0xc8fa8db6ccdd0437_u64,\n      0x910ab1d4db9914a0_u64, 0x1d9c9892400a22a2_u64,\n      0xb54d5e4a127f59c8_u64, 0x2503beb6d00cab4b_u64,\n      0xe2a0b5dc971f303a_u64, 0x2e44ae64840fd61d_u64,\n      0x8da471a9de737e24_u64, 0x5ceaecfed289e5d2_u64,\n      0xb10d8e1456105dad_u64, 0x7425a83e872c5f47_u64,\n      0xdd50f1996b947518_u64, 0xd12f124e28f77719_u64,\n      0x8a5296ffe33cc92f_u64, 0x82bd6b70d99aaa6f_u64,\n      0xace73cbfdc0bfb7b_u64, 0x636cc64d1001550b_u64,\n      0xd8210befd30efa5a_u64, 0x3c47f7e05401aa4e_u64,\n      0x8714a775e3e95c78_u64, 0x65acfaec34810a71_u64,\n      0xa8d9d1535ce3b396_u64, 0x7f1839a741a14d0d_u64,\n      0xd31045a8341ca07c_u64, 0x1ede48111209a050_u64,\n      0x83ea2b892091e44d_u64, 0x934aed0aab460432_u64,\n      0xa4e4b66b68b65d60_u64, 0xf81da84d5617853f_u64,\n      0xce1de40642e3f4b9_u64, 0x36251260ab9d668e_u64,\n      0x80d2ae83e9ce78f3_u64, 0xc1d72b7c6b426019_u64,\n      0xa1075a24e4421730_u64, 0xb24cf65b8612f81f_u64,\n      0xc94930ae1d529cfc_u64, 0xdee033f26797b627_u64,\n      0xfb9b7cd9a4a7443c_u64, 0x169840ef017da3b1_u64,\n      0x9d412e0806e88aa5_u64, 0x8e1f289560ee864e_u64,\n      0xc491798a08a2ad4e_u64, 0xf1a6f2bab92a27e2_u64,\n      0xf5b5d7ec8acb58a2_u64, 0xae10af696774b1db_u64,\n      0x9991a6f3d6bf1765_u64, 0xacca6da1e0a8ef29_u64,\n      0xbff610b0cc6edd3f_u64, 0x17fd090a58d32af3_u64,\n      0xeff394dcff8a948e_u64, 0xddfc4b4cef07f5b0_u64,\n      0x95f83d0a1fb69cd9_u64, 0x4abdaf101564f98e_u64,\n      0xbb764c4ca7a4440f_u64, 0x9d6d1ad41abe37f1_u64,\n      0xea53df5fd18d5513_u64, 0x84c86189216dc5ed_u64,\n      0x92746b9be2f8552c_u64, 0x32fd3cf5b4e49bb4_u64,\n      0xb7118682dbb66a77_u64, 0x3fbc8c33221dc2a1_u64,\n      0xe4d5e82392a40515_u64, 0xfabaf3feaa5334a_u64,\n      0x8f05b1163ba6832d_u64, 0x29cb4d87f2a7400e_u64,\n      0xb2c71d5bca9023f8_u64, 0x743e20e9ef511012_u64,\n      0xdf78e4b2bd342cf6_u64, 0x914da9246b255416_u64,\n      0x8bab8eefb6409c1a_u64, 0x1ad089b6c2f7548e_u64,\n      0xae9672aba3d0c320_u64, 0xa184ac2473b529b1_u64,\n      0xda3c0f568cc4f3e8_u64, 0xc9e5d72d90a2741e_u64,\n      0x8865899617fb1871_u64, 0x7e2fa67c7a658892_u64,\n      0xaa7eebfb9df9de8d_u64, 0xddbb901b98feeab7_u64,\n      0xd51ea6fa85785631_u64, 0x552a74227f3ea565_u64,\n      0x8533285c936b35de_u64, 0xd53a88958f87275f_u64,\n      0xa67ff273b8460356_u64, 0x8a892abaf368f137_u64,\n      0xd01fef10a657842c_u64, 0x2d2b7569b0432d85_u64,\n      0x8213f56a67f6b29b_u64, 0x9c3b29620e29fc73_u64,\n      0xa298f2c501f45f42_u64, 0x8349f3ba91b47b8f_u64,\n      0xcb3f2f7642717713_u64, 0x241c70a936219a73_u64,\n      0xfe0efb53d30dd4d7_u64, 0xed238cd383aa0110_u64,\n      0x9ec95d1463e8a506_u64, 0xf4363804324a40aa_u64,\n      0xc67bb4597ce2ce48_u64, 0xb143c6053edcd0d5_u64,\n      0xf81aa16fdc1b81da_u64, 0xdd94b7868e94050a_u64,\n      0x9b10a4e5e9913128_u64, 0xca7cf2b4191c8326_u64,\n      0xc1d4ce1f63f57d72_u64, 0xfd1c2f611f63a3f0_u64,\n      0xf24a01a73cf2dccf_u64, 0xbc633b39673c8cec_u64,\n      0x976e41088617ca01_u64, 0xd5be0503e085d813_u64,\n      0xbd49d14aa79dbc82_u64, 0x4b2d8644d8a74e18_u64,\n      0xec9c459d51852ba2_u64, 0xddf8e7d60ed1219e_u64,\n      0x93e1ab8252f33b45_u64, 0xcabb90e5c942b503_u64,\n      0xb8da1662e7b00a17_u64, 0x3d6a751f3b936243_u64,\n      0xe7109bfba19c0c9d_u64, 0xcc512670a783ad4_u64,\n      0x906a617d450187e2_u64, 0x27fb2b80668b24c5_u64,\n      0xb484f9dc9641e9da_u64, 0xb1f9f660802dedf6_u64,\n      0xe1a63853bbd26451_u64, 0x5e7873f8a0396973_u64,\n      0x8d07e33455637eb2_u64, 0xdb0b487b6423e1e8_u64,\n      0xb049dc016abc5e5f_u64, 0x91ce1a9a3d2cda62_u64,\n      0xdc5c5301c56b75f7_u64, 0x7641a140cc7810fb_u64,\n      0x89b9b3e11b6329ba_u64, 0xa9e904c87fcb0a9d_u64,\n      0xac2820d9623bf429_u64, 0x546345fa9fbdcd44_u64,\n      0xd732290fbacaf133_u64, 0xa97c177947ad4095_u64,\n      0x867f59a9d4bed6c0_u64, 0x49ed8eabcccc485d_u64,\n      0xa81f301449ee8c70_u64, 0x5c68f256bfff5a74_u64,\n      0xd226fc195c6a2f8c_u64, 0x73832eec6fff3111_u64,\n      0x83585d8fd9c25db7_u64, 0xc831fd53c5ff7eab_u64,\n      0xa42e74f3d032f525_u64, 0xba3e7ca8b77f5e55_u64,\n      0xcd3a1230c43fb26f_u64, 0x28ce1bd2e55f35eb_u64,\n      0x80444b5e7aa7cf85_u64, 0x7980d163cf5b81b3_u64,\n      0xa0555e361951c366_u64, 0xd7e105bcc332621f_u64,\n      0xc86ab5c39fa63440_u64, 0x8dd9472bf3fefaa7_u64,\n      0xfa856334878fc150_u64, 0xb14f98f6f0feb951_u64,\n      0x9c935e00d4b9d8d2_u64, 0x6ed1bf9a569f33d3_u64,\n      0xc3b8358109e84f07_u64, 0xa862f80ec4700c8_u64,\n      0xf4a642e14c6262c8_u64, 0xcd27bb612758c0fa_u64,\n      0x98e7e9cccfbd7dbd_u64, 0x8038d51cb897789c_u64,\n      0xbf21e44003acdd2c_u64, 0xe0470a63e6bd56c3_u64,\n      0xeeea5d5004981478_u64, 0x1858ccfce06cac74_u64,\n      0x95527a5202df0ccb_u64, 0xf37801e0c43ebc8_u64,\n      0xbaa718e68396cffd_u64, 0xd30560258f54e6ba_u64,\n      0xe950df20247c83fd_u64, 0x47c6b82ef32a2069_u64,\n      0x91d28b7416cdd27e_u64, 0x4cdc331d57fa5441_u64,\n      0xb6472e511c81471d_u64, 0xe0133fe4adf8e952_u64,\n      0xe3d8f9e563a198e5_u64, 0x58180fddd97723a6_u64,\n      0x8e679c2f5e44ff8f_u64, 0x570f09eaa7ea7648_u64,\n    )\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/float_common.cr",
    "content": "module Float::FastFloat\n  @[Flags]\n  enum CharsFormat\n    Scientific = 1 << 0\n    Fixed      = 1 << 2\n    Hex        = 1 << 3\n    NoInfnan   = 1 << 4\n    JsonFmt    = 1 << 5\n    FortranFmt = 1 << 6\n\n    # RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6\n    Json = JsonFmt | Fixed | Scientific | NoInfnan\n\n    # Extension of RFC 8259 where, e.g., \"inf\" and \"nan\" are allowed.\n    JsonOrInfnan = JsonFmt | Fixed | Scientific\n\n    Fortran = FortranFmt | Fixed | Scientific\n    General = Fixed | Scientific\n  end\n\n  # NOTE(crystal): uses `Errno` to represent C++'s `std::errc`\n  record FromCharsResultT(UC), ptr : UC*, ec : Errno\n\n  alias FromCharsResult = FromCharsResultT(UInt8)\n\n  record ParseOptionsT(UC), format : CharsFormat = :general, decimal_point : UC = 0x2E # '.'.ord\n\n  alias ParseOptions = ParseOptionsT(UInt8)\n\n  # rust style `try!()` macro, or `?` operator\n  macro fastfloat_try(x)\n    unless {{ x }}\n      return false\n    end\n  end\n\n  # Compares two ASCII strings in a case insensitive manner.\n  def self.fastfloat_strncasecmp(input1 : UC*, input2 : UC*, length : Int) : Bool forall UC\n    running_diff = 0_u8\n    length.times do |i|\n      running_diff |= input1[i].to_u8! ^ input2[i].to_u8!\n    end\n    running_diff.in?(0_u8, 32_u8)\n  end\n\n  record Value128, low : UInt64, high : UInt64 do\n    def self.new(x : UInt128) : self\n      new(low: x.to_u64!, high: x.unsafe_shr(64).to_u64!)\n    end\n  end\n\n  struct AdjustedMantissa\n    property mantissa : UInt64\n    property power2 : Int32\n\n    def initialize(@mantissa : UInt64 = 0, @power2 : Int32 = 0)\n    end\n  end\n\n  INVALID_AM_BIAS = -0x8000\n\n  CONSTANT_55555 = 3125_u64\n\n  module BinaryFormat(T, EquivUint)\n  end\n\n  struct BinaryFormat_Float64\n    include BinaryFormat(Float64, UInt64)\n\n    {% if compare_versions(Crystal::VERSION, \"1.16.0\") < 0 %}\n      POWERS_OF_TEN = [\n        1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,\n        1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22,\n      ]\n\n      # Largest integer value v so that (5**index * v) <= 1<<53.\n      # 0x20000000000000 == 1 << 53\n      MAX_MANTISSA = [\n        0x20000000000000_u64,\n        0x20000000000000_u64.unsafe_div(5),\n        0x20000000000000_u64.unsafe_div(5 * 5),\n        0x20000000000000_u64.unsafe_div(5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(5 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * 5 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5),\n        0x20000000000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5 * 5),\n      ]\n    {% else %}\n      POWERS_OF_TEN = Slice(Float64).literal(\n        1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,\n        1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22,\n      )\n\n      # Largest integer value v so that (5**index * v) <= 1<<53.\n      # 0x20000000000000 == 1 << 53\n      MAX_MANTISSA = Slice(UInt64).literal(\n        {{ 0x20000000000000_u64 }},\n        {{ 0x20000000000000_u64 // (5) }},\n        {{ 0x20000000000000_u64 // (5 * 5) }},\n        {{ 0x20000000000000_u64 // (5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (5 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * 5 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5) }},\n        {{ 0x20000000000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * CONSTANT_55555 * 5 * 5 * 5 * 5) }},\n      )\n    {% end %}\n\n    def min_exponent_fast_path : Int32\n      -22\n    end\n\n    def mantissa_explicit_bits : Int32\n      52\n    end\n\n    def max_exponent_round_to_even : Int32\n      23\n    end\n\n    def min_exponent_round_to_even : Int32\n      -4\n    end\n\n    def minimum_exponent : Int32\n      -1023\n    end\n\n    def infinite_power : Int32\n      0x7FF\n    end\n\n    def sign_index : Int32\n      63\n    end\n\n    def max_exponent_fast_path : Int32\n      22\n    end\n\n    def max_mantissa_fast_path : UInt64\n      0x20000000000000_u64\n    end\n\n    def max_mantissa_fast_path(power : Int64) : UInt64\n      # caller is responsible to ensure that\n      # power >= 0 && power <= 22\n      MAX_MANTISSA.unsafe_fetch(power)\n    end\n\n    def exact_power_of_ten(power : Int64) : Float64\n      POWERS_OF_TEN.unsafe_fetch(power)\n    end\n\n    def largest_power_of_ten : Int32\n      308\n    end\n\n    def smallest_power_of_ten : Int32\n      -342\n    end\n\n    def max_digits : Int32\n      769\n    end\n\n    def exponent_mask : EquivUint\n      0x7FF0000000000000_u64\n    end\n\n    def mantissa_mask : EquivUint\n      0x000FFFFFFFFFFFFF_u64\n    end\n\n    def hidden_bit_mask : EquivUint\n      0x0010000000000000_u64\n    end\n  end\n\n  struct BinaryFormat_Float32\n    include BinaryFormat(Float32, UInt32)\n\n    {% if compare_versions(Crystal::VERSION, \"1.16.0\") < 0 %}\n      POWERS_OF_TEN = [\n        1e0f32, 1e1f32, 1e2f32, 1e3f32, 1e4f32, 1e5f32, 1e6f32, 1e7f32, 1e8f32, 1e9f32, 1e10f32,\n      ]\n\n      # Largest integer value v so that (5**index * v) <= 1<<24.\n      # 0x1000000 == 1<<24\n      MAX_MANTISSA = [\n        0x1000000_u64,\n        0x1000000_u64.unsafe_div(5),\n        0x1000000_u64.unsafe_div(5 * 5),\n        0x1000000_u64.unsafe_div(5 * 5 * 5),\n        0x1000000_u64.unsafe_div(5 * 5 * 5 * 5),\n        0x1000000_u64.unsafe_div(CONSTANT_55555),\n        0x1000000_u64.unsafe_div(CONSTANT_55555 * 5),\n        0x1000000_u64.unsafe_div(CONSTANT_55555 * 5 * 5),\n        0x1000000_u64.unsafe_div(CONSTANT_55555 * 5 * 5 * 5),\n        0x1000000_u64.unsafe_div(CONSTANT_55555 * 5 * 5 * 5 * 5),\n        0x1000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555),\n        0x1000000_u64.unsafe_div(CONSTANT_55555 * CONSTANT_55555 * 5),\n      ]\n    {% else %}\n      POWERS_OF_TEN = Slice(Float32).literal(\n        1e0f32, 1e1f32, 1e2f32, 1e3f32, 1e4f32, 1e5f32, 1e6f32, 1e7f32, 1e8f32, 1e9f32, 1e10f32,\n      )\n\n      # Largest integer value v so that (5**index * v) <= 1<<24.\n      # 0x1000000 == 1<<24\n      MAX_MANTISSA = Slice(UInt64).literal(\n        {{ 0x1000000_u64 }},\n        {{ 0x1000000_u64 // (5) }},\n        {{ 0x1000000_u64 // (5 * 5) }},\n        {{ 0x1000000_u64 // (5 * 5 * 5) }},\n        {{ 0x1000000_u64 // (5 * 5 * 5 * 5) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555 * 5) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555 * 5 * 5) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555 * 5 * 5 * 5) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555 * 5 * 5 * 5 * 5) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555 * CONSTANT_55555) }},\n        {{ 0x1000000_u64 // (CONSTANT_55555 * CONSTANT_55555 * 5) }},\n      )\n    {% end %}\n\n    def min_exponent_fast_path : Int32\n      -10\n    end\n\n    def mantissa_explicit_bits : Int32\n      23\n    end\n\n    def max_exponent_round_to_even : Int32\n      10\n    end\n\n    def min_exponent_round_to_even : Int32\n      -17\n    end\n\n    def minimum_exponent : Int32\n      -127\n    end\n\n    def infinite_power : Int32\n      0xFF\n    end\n\n    def sign_index : Int32\n      31\n    end\n\n    def max_exponent_fast_path : Int32\n      10\n    end\n\n    def max_mantissa_fast_path : UInt64\n      0x1000000_u64\n    end\n\n    def max_mantissa_fast_path(power : Int64) : UInt64\n      # caller is responsible to ensure that\n      # power >= 0 && power <= 10\n      MAX_MANTISSA.unsafe_fetch(power)\n    end\n\n    def exact_power_of_ten(power : Int64) : Float32\n      POWERS_OF_TEN.unsafe_fetch(power)\n    end\n\n    def largest_power_of_ten : Int32\n      38\n    end\n\n    def smallest_power_of_ten : Int32\n      -64\n    end\n\n    def max_digits : Int32\n      114\n    end\n\n    def exponent_mask : EquivUint\n      0x7F800000_u32\n    end\n\n    def mantissa_mask : EquivUint\n      0x007FFFFF_u32\n    end\n\n    def hidden_bit_mask : EquivUint\n      0x00800000_u32\n    end\n  end\n\n  module BinaryFormat(T, EquivUint)\n    # NOTE(crystal): returns the new *value* by value\n    def to_float(negative : Bool, am : AdjustedMantissa) : T\n      word = EquivUint.new!(am.mantissa)\n      word |= EquivUint.new!(am.power2).unsafe_shl(mantissa_explicit_bits)\n      word |= EquivUint.new!(negative ? 1 : 0).unsafe_shl(sign_index)\n      word.unsafe_as(T)\n    end\n  end\n\n  def self.int_cmp_zeros(uc : UC.class) : UInt64 forall UC\n    case sizeof(UC)\n    when 1\n      0x3030303030303030_u64\n    when 2\n      0x0030003000300030_u64\n    else\n      0x0000003000000030_u64\n    end\n  end\n\n  def self.int_cmp_len(uc : UC.class) : Int32 forall UC\n    sizeof(UInt64).unsafe_div(sizeof(UC))\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float/parse_number.cr",
    "content": "require \"./ascii_number\"\nrequire \"./decimal_to_binary\"\nrequire \"./digit_comparison\"\nrequire \"./float_common\"\n\nmodule Float::FastFloat\n  module Detail\n    def self.parse_infnan(first : UC*, last : UC*, value : T*) : FromCharsResultT(UC) forall T, UC\n      ptr = first\n      ec = Errno::NONE # be optimistic\n      minus_sign = false\n      if first.value === '-' # assume first < last, so dereference without checks\n        minus_sign = true\n        first += 1\n      elsif first.value === '+'\n        first += 1\n      end\n\n      if last - first >= 3\n        if FastFloat.fastfloat_strncasecmp(first, \"nan\".to_unsafe, 3)\n          first += 3\n          ptr = first\n          value.value = minus_sign ? -T::NAN : T::NAN\n          # Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,\n          # C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).\n          if first != last && first.value === '('\n            ptr2 = first + 1\n            while ptr2 != last\n              case ptr2.value.unsafe_chr\n              when ')'\n                ptr = ptr2 + 1 # valid nan(n-char-seq-opt)\n                break\n              when 'a'..'z', 'A'..'Z', '0'..'9', '_'\n                # Do nothing\n              else\n                break # forbidden char, not nan(n-char-seq-opt)\n              end\n              ptr2 += 1\n            end\n          end\n          return FromCharsResultT(UC).new(ptr, ec)\n        end\n      end\n      if FastFloat.fastfloat_strncasecmp(first, \"inf\".to_unsafe, 3)\n        if last - first >= 8 && FastFloat.fastfloat_strncasecmp(first + 3, \"inity\".to_unsafe, 5)\n          ptr = first + 8\n        else\n          ptr = first + 3\n        end\n        value.value = minus_sign ? -T::INFINITY : T::INFINITY\n        return FromCharsResultT(UC).new(ptr, ec)\n      end\n\n      ec = Errno::EINVAL\n      FromCharsResultT(UC).new(ptr, ec)\n    end\n\n    # See\n    # A fast function to check your floating-point rounding mode\n    # https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/\n    #\n    # This function is meant to be equivalent to :\n    # prior: #include <cfenv>\n    #  return fegetround() == FE_TONEAREST;\n    # However, it is expected to be much faster than the fegetround()\n    # function call.\n    #\n    # NOTE(crystal): uses a pointer instead of a volatile variable to prevent\n    # LLVM optimization\n    @@fmin : Float32* = Pointer(Float32).malloc(1, Float32::MIN_POSITIVE)\n\n    # Returns true if the floating-pointing rounding mode is to 'nearest'.\n    # It is the default on most system. This function is meant to be inexpensive.\n    # Credit : @mwalcott3\n    def self.rounds_to_nearest? : Bool\n      fmin = @@fmin.value # we copy it so that it gets loaded at most once.\n\n      # Explanation:\n      # Only when fegetround() == FE_TONEAREST do we have that\n      # fmin + 1.0f == 1.0f - fmin.\n      #\n      # FE_UPWARD:\n      #  fmin + 1.0f > 1\n      #  1.0f - fmin == 1\n      #\n      # FE_DOWNWARD or  FE_TOWARDZERO:\n      #  fmin + 1.0f == 1\n      #  1.0f - fmin < 1\n      #\n      # Note: This may fail to be accurate if fast-math has been\n      # enabled, as rounding conventions may not apply.\n      fmin + 1.0_f32 == 1.0_f32 - fmin\n    end\n  end\n\n  module BinaryFormat(T, EquivUint)\n    def from_chars_advanced(pns : ParsedNumberStringT(UC), value : T*) : FromCharsResultT(UC) forall UC\n      {% raise \"only some floating-point types are supported\" unless T == Float32 || T == Float64 %}\n\n      # TODO(crystal): support UInt16 and UInt32\n      {% raise \"only UInt8 is supported\" unless UC == UInt8 %}\n\n      ec = Errno::NONE # be optimistic\n      ptr = pns.lastmatch\n      # The implementation of the Clinger's fast path is convoluted because\n      # we want round-to-nearest in all cases, irrespective of the rounding mode\n      # selected on the thread.\n      # We proceed optimistically, assuming that detail::rounds_to_nearest()\n      # returns true.\n      if (min_exponent_fast_path <= pns.exponent <= max_exponent_fast_path) && !pns.too_many_digits\n        # Unfortunately, the conventional Clinger's fast path is only possible\n        # when the system rounds to the nearest float.\n        #\n        # We expect the next branch to almost always be selected.\n        # We could check it first (before the previous branch), but\n        # there might be performance advantages at having the check\n        # be last.\n        if Detail.rounds_to_nearest?\n          # We have that fegetround() == FE_TONEAREST.\n          # Next is Clinger's fast path.\n          if pns.mantissa <= max_mantissa_fast_path\n            if pns.mantissa == 0\n              value.value = pns.negative ? T.new(-0.0) : T.new(0.0)\n              return FromCharsResultT(UC).new(ptr, ec)\n            end\n            value.value = T.new(pns.mantissa)\n            if pns.exponent < 0\n              value.value /= exact_power_of_ten(0_i64 &- pns.exponent)\n            else\n              value.value *= exact_power_of_ten(pns.exponent)\n            end\n            if pns.negative\n              value.value = -value.value\n            end\n            return FromCharsResultT(UC).new(ptr, ec)\n          end\n        else\n          # We do not have that fegetround() == FE_TONEAREST.\n          # Next is a modified Clinger's fast path, inspired by Jakub Jelínek's\n          # proposal\n          if pns.exponent >= 0 && pns.mantissa <= max_mantissa_fast_path(pns.exponent)\n            # Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD\n            if pns.mantissa == 0\n              value.value = pns.negative ? T.new(-0.0) : T.new(0.0)\n              return FromCharsResultT(UC).new(ptr, ec)\n            end\n            value.value = T.new(pns.mantissa) * exact_power_of_ten(pns.exponent)\n            if pns.negative\n              value.value = -value.value\n            end\n            return FromCharsResultT(UC).new(ptr, ec)\n          end\n        end\n      end\n      am = compute_float(pns.exponent, pns.mantissa)\n      if pns.too_many_digits && am.power2 >= 0\n        if am != compute_float(pns.exponent, pns.mantissa &+ 1)\n          am = compute_error(pns.exponent, pns.mantissa)\n        end\n      end\n      # If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)\n      # and we have an invalid power (am.power2 < 0), then we need to go the long\n      # way around again. This is very uncommon.\n      if am.power2 < 0\n        am = digit_comp(pns, am)\n      end\n      value.value = to_float(pns.negative, am)\n      # Test for over/underflow.\n      if (pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == infinite_power\n        ec = Errno::ERANGE\n      end\n      FromCharsResultT(UC).new(ptr, ec)\n    end\n\n    def from_chars_advanced(first : UC*, last : UC*, value : T*, options : ParseOptionsT(UC)) : FromCharsResultT(UC) forall UC\n      {% raise \"only some floating-point types are supported\" unless T == Float32 || T == Float64 %}\n\n      # TODO(crystal): support UInt16 and UInt32\n      {% raise \"only UInt8 is supported\" unless UC == UInt8 %}\n\n      if first == last\n        return FromCharsResultT(UC).new(first, Errno::EINVAL)\n      end\n      pns = FastFloat.parse_number_string(first, last, options)\n      if !pns.valid\n        if options.format.no_infnan?\n          return FromCharsResultT(UC).new(first, Errno::EINVAL)\n        else\n          return Detail.parse_infnan(first, last, value)\n        end\n      end\n\n      # call overload that takes parsed_number_string_t directly.\n      from_chars_advanced(pns, value)\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/fast_float.cr",
    "content": "struct Float\n  # :nodoc:\n  # Source port of the floating-point part of fast_float for C++:\n  # https://github.com/fastfloat/fast_float\n  #\n  # fast_float implements the C++17 `std::from_chars`, which accepts a subset of\n  # the C `strtod` / `strtof`'s string format:\n  #\n  # - a leading plus sign is disallowed, but both fast_float and this port\n  #   accept it;\n  # - the exponent may be required or disallowed, depending on the format\n  #   argument (this port always allows both);\n  # - hexfloats are not enabled by default, and fast_float doesn't implement it;\n  #   (https://github.com/fastfloat/fast_float/issues/124)\n  # - hexfloats cannot start with `0x` or `0X`.\n  #\n  # The following is their license:\n  #\n  #   Licensed under either of Apache License, Version 2.0 or MIT license or\n  #   BOOST license.\n  #\n  #   Unless you explicitly state otherwise, any contribution intentionally\n  #   submitted for inclusion in this repository by you, as defined in the\n  #   Apache-2.0 license, shall be triple licensed as above, without any\n  #   additional terms or conditions.\n  #\n  # Main differences from the original fast_float:\n  #\n  # - Only `UC == UInt8` is implemented and tested, not the other wide chars;\n  # - No explicit SIMD (the original mainly uses this for wide char strings).\n  #\n  # The following compile-time configuration is assumed:\n  #\n  # - #define FASTFLOAT_ALLOWS_LEADING_PLUS\n  # - #define FLT_EVAL_METHOD 0\n  module FastFloat\n    # Current revision: https://github.com/fastfloat/fast_float/tree/v6.1.6\n\n    def self.to_f64?(str : String, whitespace : Bool, strict : Bool) : Float64?\n      value = uninitialized Float64\n      start = str.to_unsafe\n      finish = start + str.bytesize\n      options = ParseOptionsT(typeof(str.to_unsafe.value)).new(format: :general)\n\n      if whitespace\n        start += str.calc_excess_left\n        finish -= str.calc_excess_right\n      end\n\n      ret = BinaryFormat_Float64.new.from_chars_advanced(start, finish, pointerof(value), options)\n      if ret.ec == Errno::NONE && (!strict || ret.ptr == finish)\n        value\n      end\n    end\n\n    def self.to_f32?(str : String, whitespace : Bool, strict : Bool) : Float32?\n      value = uninitialized Float32\n      start = str.to_unsafe\n      finish = start + str.bytesize\n      options = ParseOptionsT(typeof(str.to_unsafe.value)).new(format: :general)\n\n      if whitespace\n        start += str.calc_excess_left\n        finish -= str.calc_excess_right\n      end\n\n      ret = BinaryFormat_Float32.new.from_chars_advanced(start, finish, pointerof(value), options)\n      if ret.ec == Errno::NONE && (!strict || ret.ptr == finish)\n        value\n      end\n    end\n  end\nend\n\nrequire \"./fast_float/parse_number\"\n"
  },
  {
    "path": "src/float/printer/cached_powers.cr",
    "content": "# CachedPowers is ported from the C++ \"double-conversions\" library.\n# The following is their license:\n#   Copyright 2006-2008 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n@[Deprecated]\nmodule Float::Printer::CachedPowers\n  record Power, significand : UInt64, binary_exp : Int16, decimal_exp : Int16\n  # The minimal and maximal target exponent define the range of w's binary\n  # exponent, where 'w' is the result of multiplying the input by a cached power\n  # of ten.\n  #\n  # A different range might be chosen on a different platform, to optimize digit\n  # generation, but a smaller range requires more powers of ten to be cached.\n  MIN_TARGET_EXP = -60\n  MAX_TARGET_EXP = -32\n\n  CACHED_POWER_OFFSET = 348 # -1 * the first decimal_exp\n\n  # Not all powers of ten are cached. The decimal exponent of two neighboring\n  # cached numbers will differ by `CACHED_EXP_STEP`\n  CACHED_EXP_STEP =    8\n  MIN_CACHED_EXP  = -348\n  MAX_CACHED_EXP  =  340\n\n  D_1_LOG2_10 = 0.30102999566398114 # 1 / lg(10)\n\n  PowCache = [\n    {0xfa8fd5a0081c0288_u64, -1220_i16, -348_i16},\n    {0xbaaee17fa23ebf76_u64, -1193_i16, -340_i16},\n    {0x8b16fb203055ac76_u64, -1166_i16, -332_i16},\n    {0xcf42894a5dce35ea_u64, -1140_i16, -324_i16},\n    {0x9a6bb0aa55653b2d_u64, -1113_i16, -316_i16},\n    {0xe61acf033d1a45df_u64, -1087_i16, -308_i16},\n    {0xab70fe17c79ac6ca_u64, -1060_i16, -300_i16},\n    {0xff77b1fcbebcdc4f_u64, -1034_i16, -292_i16},\n    {0xbe5691ef416bd60c_u64, -1007_i16, -284_i16},\n    {0x8dd01fad907ffc3c_u64, -980_i16, -276_i16},\n    {0xd3515c2831559a83_u64, -954_i16, -268_i16},\n    {0x9d71ac8fada6c9b5_u64, -927_i16, -260_i16},\n    {0xea9c227723ee8bcb_u64, -901_i16, -252_i16},\n    {0xaecc49914078536d_u64, -874_i16, -244_i16},\n    {0x823c12795db6ce57_u64, -847_i16, -236_i16},\n    {0xc21094364dfb5637_u64, -821_i16, -228_i16},\n    {0x9096ea6f3848984f_u64, -794_i16, -220_i16},\n    {0xd77485cb25823ac7_u64, -768_i16, -212_i16},\n    {0xa086cfcd97bf97f4_u64, -741_i16, -204_i16},\n    {0xef340a98172aace5_u64, -715_i16, -196_i16},\n    {0xb23867fb2a35b28e_u64, -688_i16, -188_i16},\n    {0x84c8d4dfd2c63f3b_u64, -661_i16, -180_i16},\n    {0xc5dd44271ad3cdba_u64, -635_i16, -172_i16},\n    {0x936b9fcebb25c996_u64, -608_i16, -164_i16},\n    {0xdbac6c247d62a584_u64, -582_i16, -156_i16},\n    {0xa3ab66580d5fdaf6_u64, -555_i16, -148_i16},\n    {0xf3e2f893dec3f126_u64, -529_i16, -140_i16},\n    {0xb5b5ada8aaff80b8_u64, -502_i16, -132_i16},\n    {0x87625f056c7c4a8b_u64, -475_i16, -124_i16},\n    {0xc9bcff6034c13053_u64, -449_i16, -116_i16},\n    {0x964e858c91ba2655_u64, -422_i16, -108_i16},\n    {0xdff9772470297ebd_u64, -396_i16, -100_i16},\n    {0xa6dfbd9fb8e5b88f_u64, -369_i16, -92_i16},\n    {0xf8a95fcf88747d94_u64, -343_i16, -84_i16},\n    {0xb94470938fa89bcf_u64, -316_i16, -76_i16},\n    {0x8a08f0f8bf0f156b_u64, -289_i16, -68_i16},\n    {0xcdb02555653131b6_u64, -263_i16, -60_i16},\n    {0x993fe2c6d07b7fac_u64, -236_i16, -52_i16},\n    {0xe45c10c42a2b3b06_u64, -210_i16, -44_i16},\n    {0xaa242499697392d3_u64, -183_i16, -36_i16},\n    {0xfd87b5f28300ca0e_u64, -157_i16, -28_i16},\n    {0xbce5086492111aeb_u64, -130_i16, -20_i16},\n    {0x8cbccc096f5088cc_u64, -103_i16, -12_i16},\n    {0xd1b71758e219652c_u64, -77_i16, -4_i16},\n    {0x9c40000000000000_u64, -50_i16, 4_i16},\n    {0xe8d4a51000000000_u64, -24_i16, 12_i16},\n    {0xad78ebc5ac620000_u64, 3_i16, 20_i16},\n    {0x813f3978f8940984_u64, 30_i16, 28_i16},\n    {0xc097ce7bc90715b3_u64, 56_i16, 36_i16},\n    {0x8f7e32ce7bea5c70_u64, 83_i16, 44_i16},\n    {0xd5d238a4abe98068_u64, 109_i16, 52_i16},\n    {0x9f4f2726179a2245_u64, 136_i16, 60_i16},\n    {0xed63a231d4c4fb27_u64, 162_i16, 68_i16},\n    {0xb0de65388cc8ada8_u64, 189_i16, 76_i16},\n    {0x83c7088e1aab65db_u64, 216_i16, 84_i16},\n    {0xc45d1df942711d9a_u64, 242_i16, 92_i16},\n    {0x924d692ca61be758_u64, 269_i16, 100_i16},\n    {0xda01ee641a708dea_u64, 295_i16, 108_i16},\n    {0xa26da3999aef774a_u64, 322_i16, 116_i16},\n    {0xf209787bb47d6b85_u64, 348_i16, 124_i16},\n    {0xb454e4a179dd1877_u64, 375_i16, 132_i16},\n    {0x865b86925b9bc5c2_u64, 402_i16, 140_i16},\n    {0xc83553c5c8965d3d_u64, 428_i16, 148_i16},\n    {0x952ab45cfa97a0b3_u64, 455_i16, 156_i16},\n    {0xde469fbd99a05fe3_u64, 481_i16, 164_i16},\n    {0xa59bc234db398c25_u64, 508_i16, 172_i16},\n    {0xf6c69a72a3989f5c_u64, 534_i16, 180_i16},\n    {0xb7dcbf5354e9bece_u64, 561_i16, 188_i16},\n    {0x88fcf317f22241e2_u64, 588_i16, 196_i16},\n    {0xcc20ce9bd35c78a5_u64, 614_i16, 204_i16},\n    {0x98165af37b2153df_u64, 641_i16, 212_i16},\n    {0xe2a0b5dc971f303a_u64, 667_i16, 220_i16},\n    {0xa8d9d1535ce3b396_u64, 694_i16, 228_i16},\n    {0xfb9b7cd9a4a7443c_u64, 720_i16, 236_i16},\n    {0xbb764c4ca7a44410_u64, 747_i16, 244_i16},\n    {0x8bab8eefb6409c1a_u64, 774_i16, 252_i16},\n    {0xd01fef10a657842c_u64, 800_i16, 260_i16},\n    {0x9b10a4e5e9913129_u64, 827_i16, 268_i16},\n    {0xe7109bfba19c0c9d_u64, 853_i16, 276_i16},\n    {0xac2820d9623bf429_u64, 880_i16, 284_i16},\n    {0x80444b5e7aa7cf85_u64, 907_i16, 292_i16},\n    {0xbf21e44003acdd2d_u64, 933_i16, 300_i16},\n    {0x8e679c2f5e44ff8f_u64, 960_i16, 308_i16},\n    {0xd433179d9c8cb841_u64, 986_i16, 316_i16},\n    {0x9e19db92b4e31ba9_u64, 1013_i16, 324_i16},\n    {0xeb96bf6ebadf77d9_u64, 1039_i16, 332_i16},\n    {0xaf87023b9bf0ee6b_u64, 1066_i16, 340_i16},\n  ].map { |t| Power.new t[0], t[1], t[2] }\n\n  Pow10Cache = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}\n\n  def self.largest_pow10(n, n_bits) : {Int32, Int32}\n    # 1233/4096 is approximately 1/lg(10).\n    #  We increment to skip over the first entry in the powers cache.\n    guess = ((n_bits + 1) * 1233 >> 12) + 1\n\n    # We don't have any guarantees that 2^number_bits <= number.<Paste>\n    guess -= 1 if n < Pow10Cache[guess]\n\n    return Pow10Cache[guess], guess\n  end\n\n  # Returns a cached power-of-ten with a binary exponent in the range\n  # around *exp* (boundaries included).\n  def self.get_cached_power_for_binary_exponent(exp) : {DiyFP, Int32}\n    min_exp = MIN_TARGET_EXP - (exp + DiyFP::SIGNIFICAND_SIZE)\n    k = ((min_exp + DiyFP::SIGNIFICAND_SIZE - 1) * D_1_LOG2_10).ceil\n    index = ((CACHED_POWER_OFFSET + k.to_i - 1) // CACHED_EXP_STEP) + 1\n    pow = PowCache[index]\n    return DiyFP.new(pow.significand, pow.binary_exp), pow.decimal_exp.to_i\n  end\nend\n"
  },
  {
    "path": "src/float/printer/diy_fp.cr",
    "content": "# DiyFP is ported from the C++ \"double-conversions\" library.\n#\n# The following is their license:\n#   Copyright 2010 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nrequire \"./ieee\"\n\n# This \"Do It Yourself Floating Point\" struct implements a floating-point number\n# with a `UInt64` significand and an `Int32` exponent. Normalized `DiyFP` numbers will\n# have the most significant bit of the significand set.\n# Multiplication and Subtraction do not normalize their results.\n#\n# NOTE: `DiyFP` is not designed to contain special Floats (*NaN* and *Infinity*).\n@[Deprecated]\nstruct Float::Printer::DiyFP\n  SIGNIFICAND_SIZE = 64\n\n  # Also known as the significand.\n  property frac : UInt64\n\n  # Exponent.\n  property exp : Int32\n\n  def initialize(@frac, @exp)\n  end\n\n  def initialize(@frac, exp : Int16)\n    @exp = exp.to_i32\n  end\n\n  def new(frac : Int32, exp)\n    new frac.to_u64, exp\n  end\n\n  # Returns a new `DiyFP` calculated as `self - other`.\n  #\n  # The exponents of both numbers must be the same and the `frac` of `self`\n  # must be greater than the *other*.\n  #\n  # NOTE: This result is not normalized.\n  def -(other : DiyFP) : self\n    self.class.new(frac - other.frac, exp)\n  end\n\n  MASK32 = 0xFFFFFFFF_u32\n\n  # Returns a new `DiyFP` calculated as `self * other`.\n  #\n  # Simply \"emulates\" a 128 bit multiplication.\n  # However: the resulting number only contains 64 bits. The least\n  # significant 64 bits are only used for rounding the most significant 64\n  # bits.\n  #\n  # NOTE: This result is not normalized.\n  def *(other : DiyFP) : self\n    a = frac >> 32\n    b = frac & MASK32\n    c = other.frac >> 32\n    d = other.frac & MASK32\n    ac = a*c\n    bc = b*c\n    ad = a*d\n    bd = b*d\n    tmp = (bd >> 32) + (ad & MASK32) + (bc & MASK32)\n    # By adding 1U << 31 to tmp we round the final result.\n    # Halfway cases will be round up.\n    tmp += 1_u32 << 31\n    f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32)\n    e = exp + other.exp + 64\n\n    self.class.new(f, e)\n  end\n\n  def normalize : DiyFP\n    f = frac\n    e = exp\n\n    # This method is mainly called for normalizing boundaries. In general\n    # boundaries need to be shifted by 10 bits. We thus optimize for this case.\n    k10MSBits = 0xFFC0000000000000_u64\n    kUint64MSB = 0x8000000000000000_u64\n    while (f & k10MSBits) == 0\n      # puts \"  sig: #{f}\"\n      #  puts \"  exp: #{e}\"\n      f <<= 10_u64\n      e -= 10\n    end\n    while (f & kUint64MSB) == 0\n      # puts \"  sig: #{f}\"\n      # puts \"  exp: #{e}\"\n      f <<= 1_u64\n      e -= 1\n    end\n    DiyFP.new(f, e)\n  end\n\n  def self.from_f(d : Float64 | Float32) : self\n    frac, exp = IEEE.frac_and_exp(d)\n    new(frac, exp)\n  end\n\n  # Normalize such that the most significant bit of `frac` is set.\n  def self.from_f_normalized(v : Float64 | Float32) : self\n    pre_normalized = from_f(v)\n    f = pre_normalized.frac\n    e = pre_normalized.exp\n\n    # could be a denormal\n    while (f & IEEE::HIDDEN_BIT_64) == 0\n      f <<= 1\n      e -= 1\n    end\n\n    # do the final shifts in one go\n    f <<= DiyFP::SIGNIFICAND_SIZE - IEEE::SIGNIFICAND_SIZE_64\n    e -= DiyFP::SIGNIFICAND_SIZE - IEEE::SIGNIFICAND_SIZE_64\n    DiyFP.new(f, e)\n  end\nend\n"
  },
  {
    "path": "src/float/printer/dragonbox.cr",
    "content": "# Source port of Dragonbox's reference implementation in C++.\n#\n# The following is their license:\n#\n#   Copyright 2020-2021 Junekey Jeon\n#\n#   The contents of this file may be used under the terms of\n#   the Apache License v2.0 with LLVM Exceptions.\n#\n#      (See accompanying file LICENSE-Apache or copy at\n#       https://llvm.org/foundation/relicensing/LICENSE.txt)\n#\n#   Alternatively, the contents of this file may be used under the terms of\n#   the Boost Software License, Version 1.0.\n#      (See accompanying file LICENSE-Boost or copy at\n#       https://www.boost.org/LICENSE_1_0.txt)\n#\n#   Unless required by applicable law or agreed to in writing, this software\n#   is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n#   KIND, either express or implied.\nmodule Float::Printer::Dragonbox\n  # Current revision: https://github.com/jk-jeon/dragonbox/tree/33a9e021290d529bcb41773be2c7c3c91726a9cb\n  #\n  # Assumes the following policies:\n  #\n  # * `jkj::dragonbox::policy::sign::ignore`\n  # * `jkj::dragonbox::policy::trailing_zero::ignore`\n  # * `jkj::dragonbox::policy::decimal_to_binary_rounding::nearest_to_even` (default)\n  # * `jkj::dragonbox::policy::binary_to_decimal_rounding::to_even` (default)\n  # * `jkj::dragonbox::policy::cache::full` (default)\n\n  # Utilities for wide unsigned integer arithmetic.\n  private module WUInt\n    # TODO: use built-in integer type\n    record UInt128, high : UInt64, low : UInt64 do\n      def unsafe_add!(n : UInt64) : self\n        sum = @low &+ n\n        @high &+= (sum < @low ? 1 : 0)\n        @low = sum\n        self\n      end\n    end\n\n    def self.umul64(x : UInt32, y : UInt32) : UInt64\n      x.to_u64 &* y\n    end\n\n    # Get 128-bit result of multiplication of two 64-bit unsigned integers.\n    def self.umul128(x : UInt64, y : UInt64) : UInt128\n      a = (x >> 32).to_u32!\n      b = x.to_u32!\n      c = (y >> 32).to_u32!\n      d = y.to_u32!\n\n      ac = umul64(a, c)\n      bc = umul64(b, c)\n      ad = umul64(a, d)\n      bd = umul64(b, d)\n\n      intermediate = (bd >> 32) &+ ad.to_u32! &+ bc.to_u32!\n\n      UInt128.new(\n        high: ac &+ (intermediate >> 32) &+ (ad >> 32) &+ (bc >> 32),\n        low: (intermediate << 32) &+ bd.to_u32!,\n      )\n    end\n\n    def self.umul128_upper64(x : UInt64, y : UInt64) : UInt64\n      a = (x >> 32).to_u32!\n      b = x.to_u32!\n      c = (y >> 32).to_u32!\n      d = y.to_u32!\n\n      ac = umul64(a, c)\n      bc = umul64(b, c)\n      ad = umul64(a, d)\n      bd = umul64(b, d)\n\n      intermediate = (bd >> 32) &+ ad.to_u32! &+ bc.to_u32!\n      ac &+ (intermediate >> 32) &+ (ad >> 32) &+ (bc >> 32)\n    end\n\n    # Get upper 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit unsigned integer.\n    def self.umul192_upper128(x : UInt64, y : UInt128) : UInt128\n      r = umul128(x, y.high)\n      r.unsafe_add!(umul128_upper64(x, y.low))\n      r\n    end\n\n    # Get upper 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit unsigned integer.\n    def self.umul96_upper64(x : UInt32, y : UInt64) : UInt64\n      yh = (y >> 32).to_u32!\n      yl = y.to_u32!\n\n      xyh = umul64(x, yh)\n      xyl = umul64(x, yl)\n\n      xyh &+ (xyl >> 32)\n    end\n\n    # Get lower 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit unsigned integer.\n    def self.umul192_lower128(x : UInt64, y : UInt128) : UInt128\n      high = x &* y.high\n      high_low = umul128(x, y.low)\n      UInt128.new(\n        high: high &+ high_low.high,\n        low: high_low.low,\n      )\n    end\n\n    # Get lower 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit unsigned integer.\n    def self.umul96_lower64(x : UInt32, y : UInt64) : UInt64\n      y &* x\n    end\n  end\n\n  # Utilities for fast log computation.\n  private module Log\n    def self.floor_log10_pow2(e : Int)\n      # Precondition: `-2620 <= e <= 2620`\n      (e &* 315653) >> 20\n    end\n\n    def self.floor_log2_pow10(e : Int)\n      # Precondition: `-1233 <= e <= 1233`\n      (e &* 1741647) >> 19\n    end\n\n    def self.floor_log10_pow2_minus_log10_4_over_3(e : Int)\n      # Precondition: `-2985 <= e <= 2936`\n      (e &* 631305 &- 261663) >> 21\n    end\n  end\n\n  # Utilities for fast divisibility tests.\n  private module Div\n    CACHED_POWERS_OF_5_TABLE_U32 = [\n      {0x00000001_u32, 0xffffffff_u32},\n      {0xcccccccd_u32, 0x33333333_u32},\n      {0xc28f5c29_u32, 0x0a3d70a3_u32},\n      {0x26e978d5_u32, 0x020c49ba_u32},\n      {0x3afb7e91_u32, 0x0068db8b_u32},\n      {0x0bcbe61d_u32, 0x0014f8b5_u32},\n      {0x68c26139_u32, 0x000431bd_u32},\n      {0xae8d46a5_u32, 0x0000d6bf_u32},\n      {0x22e90e21_u32, 0x00002af3_u32},\n      {0x3a2e9c6d_u32, 0x00000897_u32},\n      {0x3ed61f49_u32, 0x000001b7_u32},\n      {0x0c913975_u32, 0x00000057_u32},\n      {0xcf503eb1_u32, 0x00000011_u32},\n      {0xf6433fbd_u32, 0x00000003_u32},\n      {0x3140a659_u32, 0x00000002_u32},\n      {0x70402145_u32, 0x00000009_u32},\n      {0x7cd9a041_u32, 0x00000001_u32},\n      {0xe5c5200d_u32, 0x00000001_u32},\n      {0xfac10669_u32, 0x00000005_u32},\n      {0x6559ce15_u32, 0x00000001_u32},\n      {0xaddec2d1_u32, 0x00000002_u32},\n      {0x892c8d5d_u32, 0x00000003_u32},\n      {0x1b6f4f79_u32, 0x00000001_u32},\n      {0x6be30fe5_u32, 0x00000001_u32},\n    ]\n\n    CACHED_POWERS_OF_5_TABLE_U64 = [\n      {0x0000000000000001_u64, 0xffffffffffffffff_u64},\n      {0xcccccccccccccccd_u64, 0x3333333333333333_u64},\n      {0x8f5c28f5c28f5c29_u64, 0x0a3d70a3d70a3d70_u64},\n      {0x1cac083126e978d5_u64, 0x020c49ba5e353f7c_u64},\n      {0xd288ce703afb7e91_u64, 0x0068db8bac710cb2_u64},\n      {0x5d4e8fb00bcbe61d_u64, 0x0014f8b588e368f0_u64},\n      {0x790fb65668c26139_u64, 0x000431bde82d7b63_u64},\n      {0xe5032477ae8d46a5_u64, 0x0000d6bf94d5e57a_u64},\n      {0xc767074b22e90e21_u64, 0x00002af31dc46118_u64},\n      {0x8e47ce423a2e9c6d_u64, 0x0000089705f4136b_u64},\n      {0x4fa7f60d3ed61f49_u64, 0x000001b7cdfd9d7b_u64},\n      {0x0fee64690c913975_u64, 0x00000057f5ff85e5_u64},\n      {0x3662e0e1cf503eb1_u64, 0x000000119799812d_u64},\n      {0xa47a2cf9f6433fbd_u64, 0x0000000384b84d09_u64},\n      {0x54186f653140a659_u64, 0x00000000b424dc35_u64},\n      {0x7738164770402145_u64, 0x0000000024075f3d_u64},\n      {0xe4a4d1417cd9a041_u64, 0x000000000734aca5_u64},\n      {0xc75429d9e5c5200d_u64, 0x000000000170ef54_u64},\n      {0xc1773b91fac10669_u64, 0x000000000049c977_u64},\n      {0x26b172506559ce15_u64, 0x00000000000ec1e4_u64},\n      {0xd489e3a9addec2d1_u64, 0x000000000002f394_u64},\n      {0x90e860bb892c8d5d_u64, 0x000000000000971d_u64},\n      {0x502e79bf1b6f4f79_u64, 0x0000000000001e39_u64},\n      {0xdcd618596be30fe5_u64, 0x000000000000060b_u64},\n    ]\n\n    module DIVIDE_BY_POW10_INFO_F32\n      MAGIC_NUMBER = 6554_u32\n      SHIFT_AMOUNT =       16\n    end\n\n    module DIVIDE_BY_POW10_INFO_F64\n      MAGIC_NUMBER = 656_u32\n      SHIFT_AMOUNT =      16\n    end\n\n    # N == 1\n    def self.check_divisibility_and_divide_by_pow10_k1(n : UInt32)\n      n &*= DIVIDE_BY_POW10_INFO_F32::MAGIC_NUMBER\n\n      # Mask for the lowest (divisibility_check_bits)-bits.\n      divisibility_check_bits = DIVIDE_BY_POW10_INFO_F32::SHIFT_AMOUNT\n      comparison_mask = ~(UInt32::MAX << divisibility_check_bits)\n      result = n & comparison_mask < DIVIDE_BY_POW10_INFO_F32::MAGIC_NUMBER\n\n      n >>= divisibility_check_bits\n      {n, result}\n    end\n\n    # N == 2\n    def self.check_divisibility_and_divide_by_pow10_k2(n : UInt32)\n      n &*= DIVIDE_BY_POW10_INFO_F64::MAGIC_NUMBER\n\n      # Mask for the lowest (divisibility_check_bits)-bits.\n      divisibility_check_bits = DIVIDE_BY_POW10_INFO_F64::SHIFT_AMOUNT\n      comparison_mask = ~(UInt32::MAX << divisibility_check_bits)\n      result = n & comparison_mask < DIVIDE_BY_POW10_INFO_F64::MAGIC_NUMBER\n\n      n >>= divisibility_check_bits\n      {n, result}\n    end\n  end\n\n  private module ImplInfoMethods(D)\n    def extract_exponent_bits(u : D::CarrierUInt)\n      exponent_bits_mask = ~(UInt32::MAX << D::EXPONENT_BITS)\n      ((u >> D::SIGNIFICAND_BITS) & exponent_bits_mask).to_u32!\n    end\n\n    def remove_exponent_bits(u : D::CarrierUInt, exponent_bits)\n      u ^ (D::CarrierUInt.new!(exponent_bits) << D::SIGNIFICAND_BITS)\n    end\n\n    def remove_sign_bit_and_shift(u : D::CarrierUInt)\n      u << 1\n    end\n\n    def check_divisibility_and_divide_by_pow10(n : UInt32)\n      {% if D::KAPPA == 1 %}\n        Div.check_divisibility_and_divide_by_pow10_k1(n)\n      {% elsif D::KAPPA == 2 %}\n        Div.check_divisibility_and_divide_by_pow10_k2(n)\n      {% else %}\n        {% raise \"Expected kappa == 1 or kappa == 2\" %}\n      {% end %}\n    end\n  end\n\n  private module ImplInfo_Float32\n    extend ImplInfoMethods(self)\n\n    SIGNIFICAND_BITS =   23\n    EXPONENT_BITS    =    8\n    MIN_EXPONENT     = -126\n    MAX_EXPONENT     =  127\n    EXPONENT_BIAS    = -127\n    DECIMAL_DIGITS   =    9\n\n    alias CarrierUInt = UInt32\n    CARRIER_BITS = 32\n\n    KAPPA =   1\n    MIN_K = -31\n    # MAX_K = 46\n    CACHE_BITS = 64\n\n    SHORTER_INTERVAL_TIE_LOWER_THRESHOLD                = -35\n    SHORTER_INTERVAL_TIE_UPPER_THRESHOLD                = -35\n    CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_LOWER_THRESHOLD =   2\n    CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_UPPER_THRESHOLD =   3\n\n    BIG_DIVISOR   = 10_u32 ** (KAPPA + 1)\n    SMALL_DIVISOR = 10_u32 ** KAPPA\n  end\n\n  private module ImplInfo_Float64\n    extend ImplInfoMethods(self)\n\n    SIGNIFICAND_BITS =    52\n    EXPONENT_BITS    =    11\n    MIN_EXPONENT     = -1022\n    MAX_EXPONENT     =  1023\n    EXPONENT_BIAS    = -1023\n    DECIMAL_DIGITS   =    17\n\n    alias CarrierUInt = UInt64\n    CARRIER_BITS = 64\n\n    KAPPA =    2\n    MIN_K = -292\n    # MAX_K = 326\n    CACHE_BITS = 128\n\n    SHORTER_INTERVAL_TIE_LOWER_THRESHOLD                = -77\n    SHORTER_INTERVAL_TIE_UPPER_THRESHOLD                = -77\n    CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_LOWER_THRESHOLD =   2\n    CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_UPPER_THRESHOLD =   3\n\n    BIG_DIVISOR   = 10_u32 ** (KAPPA + 1)\n    SMALL_DIVISOR = 10_u32 ** KAPPA\n  end\n\n  private module Impl(F, ImplInfo)\n    def self.prefer_round_down?(significand)\n      significand % 2 != 0\n    end\n\n    def self.compute_nearest_normal(two_fc, exponent, is_closed)\n      # Step 1: Schubfach multiplier calculation\n\n      # Compute k and beta.\n      minus_k = Log.floor_log10_pow2(exponent) - ImplInfo::KAPPA\n      cache = ImplInfo.get_cache(-minus_k)\n      beta = exponent + Log.floor_log2_pow10(-minus_k)\n\n      # Compute zi and deltai.\n      # 10^kappa <= deltai < 10^(kappa + 1)\n      deltai = compute_delta(cache, beta)\n      # For the case of binary32, the result of integer check is not correct for\n      # 29711844 * 2^-82\n      # = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18\n      # and 29711844 * 2^-81\n      # = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,\n      # and they are the unique counterexamples. However, since 29711844 is even,\n      # this does not cause any problem for the endpoints calculations; it can only\n      # cause a problem when we need to perform integer check for the center.\n      # Fortunately, with these inputs, that branch is never executed, so we are fine.\n      zi, is_z_integer = compute_mul((two_fc | 1) << beta, cache)\n\n      # Step 2: Try larger divisor\n      big_divisor = ImplInfo::BIG_DIVISOR\n      small_divisor = ImplInfo::SMALL_DIVISOR\n\n      significand = zi // big_divisor\n      r = (zi - significand * big_divisor).to_u32!\n\n      case r\n      when .>(deltai)\n        # do nothing\n      when .<(deltai)\n        # Exclude the right endpoint if necessary.\n        if r == 0 && is_z_integer && !is_closed\n          significand -= 1\n          r = big_divisor\n        else\n          ret_exponent = minus_k + ImplInfo::KAPPA + 1\n          return {significand, ret_exponent}\n        end\n      else\n        # r == deltai; compare fractional parts.\n        xi_parity, x_is_integer = compute_mul_parity(two_fc - 1, cache, beta)\n\n        if xi_parity || (x_is_integer && is_closed)\n          ret_exponent = minus_k + ImplInfo::KAPPA + 1\n          return {significand, ret_exponent}\n        end\n      end\n\n      # Step 3: Find the significand with the smaller divisor\n      significand *= 10\n      ret_exponent = minus_k + ImplInfo::KAPPA\n\n      dist = r - deltai // 2 + small_divisor // 2\n      approx_y_parity = ((dist ^ (small_divisor // 2)) & 1) != 0\n\n      # Is dist divisible by 10^kappa?\n      dist, divisible_by_small_divisor = ImplInfo.check_divisibility_and_divide_by_pow10(dist)\n\n      # Add dist / 10^kappa to the significand.\n      significand += dist\n\n      if divisible_by_small_divisor\n        # Check z^(f) >= epsilon^(f).\n        # We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,\n        # where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).\n        # Since there are only 2 possibilities, we only need to care about the parity.\n        # Also, zi and r should have the same parity since the divisor\n        # is an even number.\n        yi_parity, is_y_integer = compute_mul_parity(two_fc, cache, beta)\n        if yi_parity != approx_y_parity\n          significand -= 1\n        elsif prefer_round_down?(significand) && is_y_integer\n          # If z^(f) >= epsilon^(f), we might have a tie\n          # when z^(f) == epsilon^(f), or equivalently, when y is an integer.\n          # For tie-to-up case, we can just choose the upper one.\n          significand -= 1\n        end\n      end\n\n      {significand, ret_exponent}\n    end\n\n    def self.compute_nearest_shorter(exponent)\n      # Compute k and beta.\n      minus_k = Log.floor_log10_pow2_minus_log10_4_over_3(exponent)\n      beta = exponent + Log.floor_log2_pow10(-minus_k)\n\n      # Compute xi and zi.\n      cache = ImplInfo.get_cache(-minus_k)\n\n      xi = compute_left_endpoint_for_shorter_interval_case(cache, beta)\n      zi = compute_right_endpoint_for_shorter_interval_case(cache, beta)\n\n      # If we don't accept the left endpoint or\n      # if the left endpoint is not an integer, increase it.\n      xi += 1 if !is_left_endpoint_integer_shorter_interval?(exponent)\n\n      # Try bigger divisor.\n      significand = zi // 10\n\n      # If succeed, return.\n      if significand * 10 >= xi\n        ret_exponent = minus_k + 1\n        return {significand, ret_exponent}\n      end\n\n      # Otherwise, compute the round-up of y\n      significand = compute_round_up_for_shorter_interval_case(cache, beta)\n      ret_exponent = minus_k\n\n      # When tie occurs, choose one of them according to the rule.\n      if prefer_round_down?(significand) && (ImplInfo::SHORTER_INTERVAL_TIE_LOWER_THRESHOLD <= exponent <= ImplInfo::SHORTER_INTERVAL_TIE_UPPER_THRESHOLD)\n        significand -= 1\n      elsif significand < xi\n        significand += 1\n      end\n\n      {significand, ret_exponent}\n    end\n\n    def self.compute_mul(u, cache) # : {result: ImplInfo::CarrierUInt, is_integer: Bool}\n      {% if F == Float32 %}\n        r = WUInt.umul96_upper64(u, cache)\n        {\n          ImplInfo::CarrierUInt.new!(r >> 32),\n          ImplInfo::CarrierUInt.new!(r) == 0,\n        }\n      {% else %}\n        # F == Float64\n        r = WUInt.umul192_upper128(u, cache)\n        {r.high, r.low == 0}\n      {% end %}\n    end\n\n    def self.compute_delta(cache, beta) : UInt32\n      {% if F == Float32 %}\n        (cache >> (ImplInfo::CACHE_BITS - 1 - beta)).to_u32!\n      {% else %}\n        # F == Float64\n        (cache.high >> (ImplInfo::CARRIER_BITS - 1 - beta)).to_u32!\n      {% end %}\n    end\n\n    def self.compute_mul_parity(two_f, cache, beta) # : {parity: Bool, is_integer: Bool}\n      {% if F == Float32 %}\n        r = WUInt.umul96_lower64(two_f, cache)\n        {\n          ((r >> (64 - beta)) & 1) != 0,\n          UInt32.new!(r >> (32 - beta)) == 0,\n        }\n      {% else %}\n        # F == Float64\n        r = WUInt.umul192_lower128(two_f, cache)\n        {\n          ((r.high >> (64 - beta)) & 1) != 0,\n          (r.high << beta) | (r.low >> (64 - beta)) == 0,\n        }\n      {% end %}\n    end\n\n    def self.compute_left_endpoint_for_shorter_interval_case(cache, beta)\n      significand_bits = ImplInfo::SIGNIFICAND_BITS\n\n      ImplInfo::CarrierUInt.new!(\n        {% if F == Float32 %}\n          (cache - (cache >> (significand_bits + 2))) >> (ImplInfo::CACHE_BITS - significand_bits - 1 - beta)\n        {% else %}\n          # F == Float64\n          (cache.high - (cache.high >> (significand_bits + 2))) >> (ImplInfo::CARRIER_BITS - significand_bits - 1 - beta)\n        {% end %}\n      )\n    end\n\n    def self.compute_right_endpoint_for_shorter_interval_case(cache, beta)\n      significand_bits = ImplInfo::SIGNIFICAND_BITS\n\n      ImplInfo::CarrierUInt.new!(\n        {% if F == Float32 %}\n          (cache + (cache >> (significand_bits + 1))) >> (ImplInfo::CACHE_BITS - significand_bits - 1 - beta)\n        {% else %}\n          # F == Float64\n          (cache.high + (cache.high >> (significand_bits + 1))) >> (ImplInfo::CARRIER_BITS - significand_bits - 1 - beta)\n        {% end %}\n      )\n    end\n\n    def self.compute_round_up_for_shorter_interval_case(cache, beta)\n      significand_bits = ImplInfo::SIGNIFICAND_BITS\n\n      {% if F == Float32 %}\n        (ImplInfo::CarrierUInt.new!(cache >> (ImplInfo::CACHE_BITS - significand_bits - 2 - beta)) + 1) // 2\n      {% else %}\n        # F == Float64\n        ((cache.high >> (ImplInfo::CARRIER_BITS - significand_bits - 2 - beta)) + 1) // 2\n      {% end %}\n    end\n\n    def self.is_left_endpoint_integer_shorter_interval?(exponent)\n      ImplInfo::CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_LOWER_THRESHOLD <=\n        exponent <= ImplInfo::CASE_SHORTER_INTERVAL_LEFT_ENDPOINT_UPPER_THRESHOLD\n    end\n\n    def self.to_decimal(signed_significand_bits, exponent_bits)\n      two_fc = ImplInfo.remove_sign_bit_and_shift(signed_significand_bits)\n      exponent = exponent_bits.to_i\n\n      # Is the input a normal number?\n      if exponent != 0\n        exponent += ImplInfo::EXPONENT_BIAS - ImplInfo::SIGNIFICAND_BITS\n\n        # Shorter interval case; proceed like Schubfach.\n        # One might think this condition is wrong, since when exponent_bits == 1\n        # and two_fc == 0, the interval is actually regular. However, it turns out\n        # that this seemingly wrong condition is actually fine, because the end\n        # result is anyway the same.\n        #\n        # [binary32]\n        # (fc-1/2) * 2^e = 1.175'494'28... * 10^-38\n        # (fc-1/4) * 2^e = 1.175'494'31... * 10^-38\n        #    fc    * 2^e = 1.175'494'35... * 10^-38\n        # (fc+1/2) * 2^e = 1.175'494'42... * 10^-38\n        #\n        # Hence, shorter_interval_case will return 1.175'494'4 * 10^-38.\n        # 1.175'494'3 * 10^-38 is also a correct shortest representation that will\n        # be rejected if we assume shorter interval, but 1.175'494'4 * 10^-38 is\n        # closer to the true value so it doesn't matter.\n        #\n        # [binary64]\n        # (fc-1/2) * 2^e = 2.225'073'858'507'201'13... * 10^-308\n        # (fc-1/4) * 2^e = 2.225'073'858'507'201'25... * 10^-308\n        #    fc    * 2^e = 2.225'073'858'507'201'38... * 10^-308\n        # (fc+1/2) * 2^e = 2.225'073'858'507'201'63... * 10^-308\n        #\n        # Hence, shorter_interval_case will return 2.225'073'858'507'201'4 * 10^-308.\n        # This is indeed of the shortest length, and it is the unique one\n        # closest to the true value among valid representations of the same length.\n        return compute_nearest_shorter(exponent) if two_fc == 0\n\n        two_fc |= two_fc.class.new(1) << (ImplInfo::SIGNIFICAND_BITS + 1)\n      else # Is the input a subnormal number?\n        exponent = ImplInfo::MIN_EXPONENT - ImplInfo::SIGNIFICAND_BITS\n      end\n\n      compute_nearest_normal(two_fc, exponent, signed_significand_bits % 2 == 0)\n    end\n  end\n\n  {% for f, uint in {Float32 => UInt32, Float64 => UInt64} %}\n    # Provides a decimal representation of *x*.\n    #\n    # Returns a `Tuple` of `{significand, decimal_exponent}` such that\n    # `x == significand * 10.0 ** decimal_exponent`. This decimal representation\n    # is the shortest possible while still maintaining the round-trip guarantee.\n    # There may be trailing zeros in `significand`.\n    def self.to_decimal(x : {{ f }}) : Tuple({{ uint }}, Int32)\n      br = x.unsafe_as({{ uint }})\n      exponent_bits = ImplInfo_{{ f }}.extract_exponent_bits(br)\n      s = ImplInfo_{{ f }}.remove_exponent_bits(br, exponent_bits)\n      Impl({{ f }}, ImplInfo_{{ f }}).to_decimal(s, exponent_bits)\n    end\n  {% end %}\nend\n\nrequire \"./dragonbox_cache\"\n"
  },
  {
    "path": "src/float/printer/dragonbox_cache.cr",
    "content": "module Float::Printer::Dragonbox\n  private module ImplInfoMethods(D)\n    def get_cache(k : Int)\n      # Precondition: `D::MIN_K <= k <= D::MAX_K`\n      D::CACHE.unsafe_fetch(k - D::MIN_K)\n    end\n  end\n\n  private module ImplInfo_Float32\n    CACHE = [\n      0x81ceb32c4b43fcf5_u64,\n      0xa2425ff75e14fc32_u64,\n      0xcad2f7f5359a3b3f_u64,\n      0xfd87b5f28300ca0e_u64,\n      0x9e74d1b791e07e49_u64,\n      0xc612062576589ddb_u64,\n      0xf79687aed3eec552_u64,\n      0x9abe14cd44753b53_u64,\n      0xc16d9a0095928a28_u64,\n      0xf1c90080baf72cb2_u64,\n      0x971da05074da7bef_u64,\n      0xbce5086492111aeb_u64,\n      0xec1e4a7db69561a6_u64,\n      0x9392ee8e921d5d08_u64,\n      0xb877aa3236a4b44a_u64,\n      0xe69594bec44de15c_u64,\n      0x901d7cf73ab0acda_u64,\n      0xb424dc35095cd810_u64,\n      0xe12e13424bb40e14_u64,\n      0x8cbccc096f5088cc_u64,\n      0xafebff0bcb24aaff_u64,\n      0xdbe6fecebdedd5bf_u64,\n      0x89705f4136b4a598_u64,\n      0xabcc77118461cefd_u64,\n      0xd6bf94d5e57a42bd_u64,\n      0x8637bd05af6c69b6_u64,\n      0xa7c5ac471b478424_u64,\n      0xd1b71758e219652c_u64,\n      0x83126e978d4fdf3c_u64,\n      0xa3d70a3d70a3d70b_u64,\n      0xcccccccccccccccd_u64,\n      0x8000000000000000_u64,\n      0xa000000000000000_u64,\n      0xc800000000000000_u64,\n      0xfa00000000000000_u64,\n      0x9c40000000000000_u64,\n      0xc350000000000000_u64,\n      0xf424000000000000_u64,\n      0x9896800000000000_u64,\n      0xbebc200000000000_u64,\n      0xee6b280000000000_u64,\n      0x9502f90000000000_u64,\n      0xba43b74000000000_u64,\n      0xe8d4a51000000000_u64,\n      0x9184e72a00000000_u64,\n      0xb5e620f480000000_u64,\n      0xe35fa931a0000000_u64,\n      0x8e1bc9bf04000000_u64,\n      0xb1a2bc2ec5000000_u64,\n      0xde0b6b3a76400000_u64,\n      0x8ac7230489e80000_u64,\n      0xad78ebc5ac620000_u64,\n      0xd8d726b7177a8000_u64,\n      0x878678326eac9000_u64,\n      0xa968163f0a57b400_u64,\n      0xd3c21bcecceda100_u64,\n      0x84595161401484a0_u64,\n      0xa56fa5b99019a5c8_u64,\n      0xcecb8f27f4200f3a_u64,\n      0x813f3978f8940985_u64,\n      0xa18f07d736b90be6_u64,\n      0xc9f2c9cd04674edf_u64,\n      0xfc6f7c4045812297_u64,\n      0x9dc5ada82b70b59e_u64,\n      0xc5371912364ce306_u64,\n      0xf684df56c3e01bc7_u64,\n      0x9a130b963a6c115d_u64,\n      0xc097ce7bc90715b4_u64,\n      0xf0bdc21abb48db21_u64,\n      0x96769950b50d88f5_u64,\n      0xbc143fa4e250eb32_u64,\n      0xeb194f8e1ae525fe_u64,\n      0x92efd1b8d0cf37bf_u64,\n      0xb7abc627050305ae_u64,\n      0xe596b7b0c643c71a_u64,\n      0x8f7e32ce7bea5c70_u64,\n      0xb35dbf821ae4f38c_u64,\n      0xe0352f62a19e306f_u64,\n    ]\n  end\n\n  module ImplInfo_Float64\n    # TODO: this is needed to avoid generating lots of allocas\n    # in LLVM, which makes LLVM really slow. The compiler should\n    # try to avoid/reuse temporary allocas.\n    # Explanation: https://github.com/crystal-lang/crystal/issues/4516#issuecomment-306226171\n    private def self.put(array, high, low) : Nil\n      array << WUInt::UInt128.new(high: high, low: low)\n    end\n\n    CACHE = begin\n      cache = Array(WUInt::UInt128).new(619)\n      put(cache, 0xff77b1fcbebcdc4f_u64, 0x25e8e89c13bb0f7b_u64)\n      put(cache, 0x9faacf3df73609b1_u64, 0x77b191618c54e9ad_u64)\n      put(cache, 0xc795830d75038c1d_u64, 0xd59df5b9ef6a2418_u64)\n      put(cache, 0xf97ae3d0d2446f25_u64, 0x4b0573286b44ad1e_u64)\n      put(cache, 0x9becce62836ac577_u64, 0x4ee367f9430aec33_u64)\n      put(cache, 0xc2e801fb244576d5_u64, 0x229c41f793cda740_u64)\n      put(cache, 0xf3a20279ed56d48a_u64, 0x6b43527578c11110_u64)\n      put(cache, 0x9845418c345644d6_u64, 0x830a13896b78aaaa_u64)\n      put(cache, 0xbe5691ef416bd60c_u64, 0x23cc986bc656d554_u64)\n      put(cache, 0xedec366b11c6cb8f_u64, 0x2cbfbe86b7ec8aa9_u64)\n      put(cache, 0x94b3a202eb1c3f39_u64, 0x7bf7d71432f3d6aa_u64)\n      put(cache, 0xb9e08a83a5e34f07_u64, 0xdaf5ccd93fb0cc54_u64)\n      put(cache, 0xe858ad248f5c22c9_u64, 0xd1b3400f8f9cff69_u64)\n      put(cache, 0x91376c36d99995be_u64, 0x23100809b9c21fa2_u64)\n      put(cache, 0xb58547448ffffb2d_u64, 0xabd40a0c2832a78b_u64)\n      put(cache, 0xe2e69915b3fff9f9_u64, 0x16c90c8f323f516d_u64)\n      put(cache, 0x8dd01fad907ffc3b_u64, 0xae3da7d97f6792e4_u64)\n      put(cache, 0xb1442798f49ffb4a_u64, 0x99cd11cfdf41779d_u64)\n      put(cache, 0xdd95317f31c7fa1d_u64, 0x40405643d711d584_u64)\n      put(cache, 0x8a7d3eef7f1cfc52_u64, 0x482835ea666b2573_u64)\n      put(cache, 0xad1c8eab5ee43b66_u64, 0xda3243650005eed0_u64)\n      put(cache, 0xd863b256369d4a40_u64, 0x90bed43e40076a83_u64)\n      put(cache, 0x873e4f75e2224e68_u64, 0x5a7744a6e804a292_u64)\n      put(cache, 0xa90de3535aaae202_u64, 0x711515d0a205cb37_u64)\n      put(cache, 0xd3515c2831559a83_u64, 0x0d5a5b44ca873e04_u64)\n      put(cache, 0x8412d9991ed58091_u64, 0xe858790afe9486c3_u64)\n      put(cache, 0xa5178fff668ae0b6_u64, 0x626e974dbe39a873_u64)\n      put(cache, 0xce5d73ff402d98e3_u64, 0xfb0a3d212dc81290_u64)\n      put(cache, 0x80fa687f881c7f8e_u64, 0x7ce66634bc9d0b9a_u64)\n      put(cache, 0xa139029f6a239f72_u64, 0x1c1fffc1ebc44e81_u64)\n      put(cache, 0xc987434744ac874e_u64, 0xa327ffb266b56221_u64)\n      put(cache, 0xfbe9141915d7a922_u64, 0x4bf1ff9f0062baa9_u64)\n      put(cache, 0x9d71ac8fada6c9b5_u64, 0x6f773fc3603db4aa_u64)\n      put(cache, 0xc4ce17b399107c22_u64, 0xcb550fb4384d21d4_u64)\n      put(cache, 0xf6019da07f549b2b_u64, 0x7e2a53a146606a49_u64)\n      put(cache, 0x99c102844f94e0fb_u64, 0x2eda7444cbfc426e_u64)\n      put(cache, 0xc0314325637a1939_u64, 0xfa911155fefb5309_u64)\n      put(cache, 0xf03d93eebc589f88_u64, 0x793555ab7eba27cb_u64)\n      put(cache, 0x96267c7535b763b5_u64, 0x4bc1558b2f3458df_u64)\n      put(cache, 0xbbb01b9283253ca2_u64, 0x9eb1aaedfb016f17_u64)\n      put(cache, 0xea9c227723ee8bcb_u64, 0x465e15a979c1cadd_u64)\n      put(cache, 0x92a1958a7675175f_u64, 0x0bfacd89ec191eca_u64)\n      put(cache, 0xb749faed14125d36_u64, 0xcef980ec671f667c_u64)\n      put(cache, 0xe51c79a85916f484_u64, 0x82b7e12780e7401b_u64)\n      put(cache, 0x8f31cc0937ae58d2_u64, 0xd1b2ecb8b0908811_u64)\n      put(cache, 0xb2fe3f0b8599ef07_u64, 0x861fa7e6dcb4aa16_u64)\n      put(cache, 0xdfbdcece67006ac9_u64, 0x67a791e093e1d49b_u64)\n      put(cache, 0x8bd6a141006042bd_u64, 0xe0c8bb2c5c6d24e1_u64)\n      put(cache, 0xaecc49914078536d_u64, 0x58fae9f773886e19_u64)\n      put(cache, 0xda7f5bf590966848_u64, 0xaf39a475506a899f_u64)\n      put(cache, 0x888f99797a5e012d_u64, 0x6d8406c952429604_u64)\n      put(cache, 0xaab37fd7d8f58178_u64, 0xc8e5087ba6d33b84_u64)\n      put(cache, 0xd5605fcdcf32e1d6_u64, 0xfb1e4a9a90880a65_u64)\n      put(cache, 0x855c3be0a17fcd26_u64, 0x5cf2eea09a550680_u64)\n      put(cache, 0xa6b34ad8c9dfc06f_u64, 0xf42faa48c0ea481f_u64)\n      put(cache, 0xd0601d8efc57b08b_u64, 0xf13b94daf124da27_u64)\n      put(cache, 0x823c12795db6ce57_u64, 0x76c53d08d6b70859_u64)\n      put(cache, 0xa2cb1717b52481ed_u64, 0x54768c4b0c64ca6f_u64)\n      put(cache, 0xcb7ddcdda26da268_u64, 0xa9942f5dcf7dfd0a_u64)\n      put(cache, 0xfe5d54150b090b02_u64, 0xd3f93b35435d7c4d_u64)\n      put(cache, 0x9efa548d26e5a6e1_u64, 0xc47bc5014a1a6db0_u64)\n      put(cache, 0xc6b8e9b0709f109a_u64, 0x359ab6419ca1091c_u64)\n      put(cache, 0xf867241c8cc6d4c0_u64, 0xc30163d203c94b63_u64)\n      put(cache, 0x9b407691d7fc44f8_u64, 0x79e0de63425dcf1e_u64)\n      put(cache, 0xc21094364dfb5636_u64, 0x985915fc12f542e5_u64)\n      put(cache, 0xf294b943e17a2bc4_u64, 0x3e6f5b7b17b2939e_u64)\n      put(cache, 0x979cf3ca6cec5b5a_u64, 0xa705992ceecf9c43_u64)\n      put(cache, 0xbd8430bd08277231_u64, 0x50c6ff782a838354_u64)\n      put(cache, 0xece53cec4a314ebd_u64, 0xa4f8bf5635246429_u64)\n      put(cache, 0x940f4613ae5ed136_u64, 0x871b7795e136be9a_u64)\n      put(cache, 0xb913179899f68584_u64, 0x28e2557b59846e40_u64)\n      put(cache, 0xe757dd7ec07426e5_u64, 0x331aeada2fe589d0_u64)\n      put(cache, 0x9096ea6f3848984f_u64, 0x3ff0d2c85def7622_u64)\n      put(cache, 0xb4bca50b065abe63_u64, 0x0fed077a756b53aa_u64)\n      put(cache, 0xe1ebce4dc7f16dfb_u64, 0xd3e8495912c62895_u64)\n      put(cache, 0x8d3360f09cf6e4bd_u64, 0x64712dd7abbbd95d_u64)\n      put(cache, 0xb080392cc4349dec_u64, 0xbd8d794d96aacfb4_u64)\n      put(cache, 0xdca04777f541c567_u64, 0xecf0d7a0fc5583a1_u64)\n      put(cache, 0x89e42caaf9491b60_u64, 0xf41686c49db57245_u64)\n      put(cache, 0xac5d37d5b79b6239_u64, 0x311c2875c522ced6_u64)\n      put(cache, 0xd77485cb25823ac7_u64, 0x7d633293366b828c_u64)\n      put(cache, 0x86a8d39ef77164bc_u64, 0xae5dff9c02033198_u64)\n      put(cache, 0xa8530886b54dbdeb_u64, 0xd9f57f830283fdfd_u64)\n      put(cache, 0xd267caa862a12d66_u64, 0xd072df63c324fd7c_u64)\n      put(cache, 0x8380dea93da4bc60_u64, 0x4247cb9e59f71e6e_u64)\n      put(cache, 0xa46116538d0deb78_u64, 0x52d9be85f074e609_u64)\n      put(cache, 0xcd795be870516656_u64, 0x67902e276c921f8c_u64)\n      put(cache, 0x806bd9714632dff6_u64, 0x00ba1cd8a3db53b7_u64)\n      put(cache, 0xa086cfcd97bf97f3_u64, 0x80e8a40eccd228a5_u64)\n      put(cache, 0xc8a883c0fdaf7df0_u64, 0x6122cd128006b2ce_u64)\n      put(cache, 0xfad2a4b13d1b5d6c_u64, 0x796b805720085f82_u64)\n      put(cache, 0x9cc3a6eec6311a63_u64, 0xcbe3303674053bb1_u64)\n      put(cache, 0xc3f490aa77bd60fc_u64, 0xbedbfc4411068a9d_u64)\n      put(cache, 0xf4f1b4d515acb93b_u64, 0xee92fb5515482d45_u64)\n      put(cache, 0x991711052d8bf3c5_u64, 0x751bdd152d4d1c4b_u64)\n      put(cache, 0xbf5cd54678eef0b6_u64, 0xd262d45a78a0635e_u64)\n      put(cache, 0xef340a98172aace4_u64, 0x86fb897116c87c35_u64)\n      put(cache, 0x9580869f0e7aac0e_u64, 0xd45d35e6ae3d4da1_u64)\n      put(cache, 0xbae0a846d2195712_u64, 0x8974836059cca10a_u64)\n      put(cache, 0xe998d258869facd7_u64, 0x2bd1a438703fc94c_u64)\n      put(cache, 0x91ff83775423cc06_u64, 0x7b6306a34627ddd0_u64)\n      put(cache, 0xb67f6455292cbf08_u64, 0x1a3bc84c17b1d543_u64)\n      put(cache, 0xe41f3d6a7377eeca_u64, 0x20caba5f1d9e4a94_u64)\n      put(cache, 0x8e938662882af53e_u64, 0x547eb47b7282ee9d_u64)\n      put(cache, 0xb23867fb2a35b28d_u64, 0xe99e619a4f23aa44_u64)\n      put(cache, 0xdec681f9f4c31f31_u64, 0x6405fa00e2ec94d5_u64)\n      put(cache, 0x8b3c113c38f9f37e_u64, 0xde83bc408dd3dd05_u64)\n      put(cache, 0xae0b158b4738705e_u64, 0x9624ab50b148d446_u64)\n      put(cache, 0xd98ddaee19068c76_u64, 0x3badd624dd9b0958_u64)\n      put(cache, 0x87f8a8d4cfa417c9_u64, 0xe54ca5d70a80e5d7_u64)\n      put(cache, 0xa9f6d30a038d1dbc_u64, 0x5e9fcf4ccd211f4d_u64)\n      put(cache, 0xd47487cc8470652b_u64, 0x7647c32000696720_u64)\n      put(cache, 0x84c8d4dfd2c63f3b_u64, 0x29ecd9f40041e074_u64)\n      put(cache, 0xa5fb0a17c777cf09_u64, 0xf468107100525891_u64)\n      put(cache, 0xcf79cc9db955c2cc_u64, 0x7182148d4066eeb5_u64)\n      put(cache, 0x81ac1fe293d599bf_u64, 0xc6f14cd848405531_u64)\n      put(cache, 0xa21727db38cb002f_u64, 0xb8ada00e5a506a7d_u64)\n      put(cache, 0xca9cf1d206fdc03b_u64, 0xa6d90811f0e4851d_u64)\n      put(cache, 0xfd442e4688bd304a_u64, 0x908f4a166d1da664_u64)\n      put(cache, 0x9e4a9cec15763e2e_u64, 0x9a598e4e043287ff_u64)\n      put(cache, 0xc5dd44271ad3cdba_u64, 0x40eff1e1853f29fe_u64)\n      put(cache, 0xf7549530e188c128_u64, 0xd12bee59e68ef47d_u64)\n      put(cache, 0x9a94dd3e8cf578b9_u64, 0x82bb74f8301958cf_u64)\n      put(cache, 0xc13a148e3032d6e7_u64, 0xe36a52363c1faf02_u64)\n      put(cache, 0xf18899b1bc3f8ca1_u64, 0xdc44e6c3cb279ac2_u64)\n      put(cache, 0x96f5600f15a7b7e5_u64, 0x29ab103a5ef8c0ba_u64)\n      put(cache, 0xbcb2b812db11a5de_u64, 0x7415d448f6b6f0e8_u64)\n      put(cache, 0xebdf661791d60f56_u64, 0x111b495b3464ad22_u64)\n      put(cache, 0x936b9fcebb25c995_u64, 0xcab10dd900beec35_u64)\n      put(cache, 0xb84687c269ef3bfb_u64, 0x3d5d514f40eea743_u64)\n      put(cache, 0xe65829b3046b0afa_u64, 0x0cb4a5a3112a5113_u64)\n      put(cache, 0x8ff71a0fe2c2e6dc_u64, 0x47f0e785eaba72ac_u64)\n      put(cache, 0xb3f4e093db73a093_u64, 0x59ed216765690f57_u64)\n      put(cache, 0xe0f218b8d25088b8_u64, 0x306869c13ec3532d_u64)\n      put(cache, 0x8c974f7383725573_u64, 0x1e414218c73a13fc_u64)\n      put(cache, 0xafbd2350644eeacf_u64, 0xe5d1929ef90898fb_u64)\n      put(cache, 0xdbac6c247d62a583_u64, 0xdf45f746b74abf3a_u64)\n      put(cache, 0x894bc396ce5da772_u64, 0x6b8bba8c328eb784_u64)\n      put(cache, 0xab9eb47c81f5114f_u64, 0x066ea92f3f326565_u64)\n      put(cache, 0xd686619ba27255a2_u64, 0xc80a537b0efefebe_u64)\n      put(cache, 0x8613fd0145877585_u64, 0xbd06742ce95f5f37_u64)\n      put(cache, 0xa798fc4196e952e7_u64, 0x2c48113823b73705_u64)\n      put(cache, 0xd17f3b51fca3a7a0_u64, 0xf75a15862ca504c6_u64)\n      put(cache, 0x82ef85133de648c4_u64, 0x9a984d73dbe722fc_u64)\n      put(cache, 0xa3ab66580d5fdaf5_u64, 0xc13e60d0d2e0ebbb_u64)\n      put(cache, 0xcc963fee10b7d1b3_u64, 0x318df905079926a9_u64)\n      put(cache, 0xffbbcfe994e5c61f_u64, 0xfdf17746497f7053_u64)\n      put(cache, 0x9fd561f1fd0f9bd3_u64, 0xfeb6ea8bedefa634_u64)\n      put(cache, 0xc7caba6e7c5382c8_u64, 0xfe64a52ee96b8fc1_u64)\n      put(cache, 0xf9bd690a1b68637b_u64, 0x3dfdce7aa3c673b1_u64)\n      put(cache, 0x9c1661a651213e2d_u64, 0x06bea10ca65c084f_u64)\n      put(cache, 0xc31bfa0fe5698db8_u64, 0x486e494fcff30a63_u64)\n      put(cache, 0xf3e2f893dec3f126_u64, 0x5a89dba3c3efccfb_u64)\n      put(cache, 0x986ddb5c6b3a76b7_u64, 0xf89629465a75e01d_u64)\n      put(cache, 0xbe89523386091465_u64, 0xf6bbb397f1135824_u64)\n      put(cache, 0xee2ba6c0678b597f_u64, 0x746aa07ded582e2d_u64)\n      put(cache, 0x94db483840b717ef_u64, 0xa8c2a44eb4571cdd_u64)\n      put(cache, 0xba121a4650e4ddeb_u64, 0x92f34d62616ce414_u64)\n      put(cache, 0xe896a0d7e51e1566_u64, 0x77b020baf9c81d18_u64)\n      put(cache, 0x915e2486ef32cd60_u64, 0x0ace1474dc1d122f_u64)\n      put(cache, 0xb5b5ada8aaff80b8_u64, 0x0d819992132456bb_u64)\n      put(cache, 0xe3231912d5bf60e6_u64, 0x10e1fff697ed6c6a_u64)\n      put(cache, 0x8df5efabc5979c8f_u64, 0xca8d3ffa1ef463c2_u64)\n      put(cache, 0xb1736b96b6fd83b3_u64, 0xbd308ff8a6b17cb3_u64)\n      put(cache, 0xddd0467c64bce4a0_u64, 0xac7cb3f6d05ddbdf_u64)\n      put(cache, 0x8aa22c0dbef60ee4_u64, 0x6bcdf07a423aa96c_u64)\n      put(cache, 0xad4ab7112eb3929d_u64, 0x86c16c98d2c953c7_u64)\n      put(cache, 0xd89d64d57a607744_u64, 0xe871c7bf077ba8b8_u64)\n      put(cache, 0x87625f056c7c4a8b_u64, 0x11471cd764ad4973_u64)\n      put(cache, 0xa93af6c6c79b5d2d_u64, 0xd598e40d3dd89bd0_u64)\n      put(cache, 0xd389b47879823479_u64, 0x4aff1d108d4ec2c4_u64)\n      put(cache, 0x843610cb4bf160cb_u64, 0xcedf722a585139bb_u64)\n      put(cache, 0xa54394fe1eedb8fe_u64, 0xc2974eb4ee658829_u64)\n      put(cache, 0xce947a3da6a9273e_u64, 0x733d226229feea33_u64)\n      put(cache, 0x811ccc668829b887_u64, 0x0806357d5a3f5260_u64)\n      put(cache, 0xa163ff802a3426a8_u64, 0xca07c2dcb0cf26f8_u64)\n      put(cache, 0xc9bcff6034c13052_u64, 0xfc89b393dd02f0b6_u64)\n      put(cache, 0xfc2c3f3841f17c67_u64, 0xbbac2078d443ace3_u64)\n      put(cache, 0x9d9ba7832936edc0_u64, 0xd54b944b84aa4c0e_u64)\n      put(cache, 0xc5029163f384a931_u64, 0x0a9e795e65d4df12_u64)\n      put(cache, 0xf64335bcf065d37d_u64, 0x4d4617b5ff4a16d6_u64)\n      put(cache, 0x99ea0196163fa42e_u64, 0x504bced1bf8e4e46_u64)\n      put(cache, 0xc06481fb9bcf8d39_u64, 0xe45ec2862f71e1d7_u64)\n      put(cache, 0xf07da27a82c37088_u64, 0x5d767327bb4e5a4d_u64)\n      put(cache, 0x964e858c91ba2655_u64, 0x3a6a07f8d510f870_u64)\n      put(cache, 0xbbe226efb628afea_u64, 0x890489f70a55368c_u64)\n      put(cache, 0xeadab0aba3b2dbe5_u64, 0x2b45ac74ccea842f_u64)\n      put(cache, 0x92c8ae6b464fc96f_u64, 0x3b0b8bc90012929e_u64)\n      put(cache, 0xb77ada0617e3bbcb_u64, 0x09ce6ebb40173745_u64)\n      put(cache, 0xe55990879ddcaabd_u64, 0xcc420a6a101d0516_u64)\n      put(cache, 0x8f57fa54c2a9eab6_u64, 0x9fa946824a12232e_u64)\n      put(cache, 0xb32df8e9f3546564_u64, 0x47939822dc96abfa_u64)\n      put(cache, 0xdff9772470297ebd_u64, 0x59787e2b93bc56f8_u64)\n      put(cache, 0x8bfbea76c619ef36_u64, 0x57eb4edb3c55b65b_u64)\n      put(cache, 0xaefae51477a06b03_u64, 0xede622920b6b23f2_u64)\n      put(cache, 0xdab99e59958885c4_u64, 0xe95fab368e45ecee_u64)\n      put(cache, 0x88b402f7fd75539b_u64, 0x11dbcb0218ebb415_u64)\n      put(cache, 0xaae103b5fcd2a881_u64, 0xd652bdc29f26a11a_u64)\n      put(cache, 0xd59944a37c0752a2_u64, 0x4be76d3346f04960_u64)\n      put(cache, 0x857fcae62d8493a5_u64, 0x6f70a4400c562ddc_u64)\n      put(cache, 0xa6dfbd9fb8e5b88e_u64, 0xcb4ccd500f6bb953_u64)\n      put(cache, 0xd097ad07a71f26b2_u64, 0x7e2000a41346a7a8_u64)\n      put(cache, 0x825ecc24c873782f_u64, 0x8ed400668c0c28c9_u64)\n      put(cache, 0xa2f67f2dfa90563b_u64, 0x728900802f0f32fb_u64)\n      put(cache, 0xcbb41ef979346bca_u64, 0x4f2b40a03ad2ffba_u64)\n      put(cache, 0xfea126b7d78186bc_u64, 0xe2f610c84987bfa9_u64)\n      put(cache, 0x9f24b832e6b0f436_u64, 0x0dd9ca7d2df4d7ca_u64)\n      put(cache, 0xc6ede63fa05d3143_u64, 0x91503d1c79720dbc_u64)\n      put(cache, 0xf8a95fcf88747d94_u64, 0x75a44c6397ce912b_u64)\n      put(cache, 0x9b69dbe1b548ce7c_u64, 0xc986afbe3ee11abb_u64)\n      put(cache, 0xc24452da229b021b_u64, 0xfbe85badce996169_u64)\n      put(cache, 0xf2d56790ab41c2a2_u64, 0xfae27299423fb9c4_u64)\n      put(cache, 0x97c560ba6b0919a5_u64, 0xdccd879fc967d41b_u64)\n      put(cache, 0xbdb6b8e905cb600f_u64, 0x5400e987bbc1c921_u64)\n      put(cache, 0xed246723473e3813_u64, 0x290123e9aab23b69_u64)\n      put(cache, 0x9436c0760c86e30b_u64, 0xf9a0b6720aaf6522_u64)\n      put(cache, 0xb94470938fa89bce_u64, 0xf808e40e8d5b3e6a_u64)\n      put(cache, 0xe7958cb87392c2c2_u64, 0xb60b1d1230b20e05_u64)\n      put(cache, 0x90bd77f3483bb9b9_u64, 0xb1c6f22b5e6f48c3_u64)\n      put(cache, 0xb4ecd5f01a4aa828_u64, 0x1e38aeb6360b1af4_u64)\n      put(cache, 0xe2280b6c20dd5232_u64, 0x25c6da63c38de1b1_u64)\n      put(cache, 0x8d590723948a535f_u64, 0x579c487e5a38ad0f_u64)\n      put(cache, 0xb0af48ec79ace837_u64, 0x2d835a9df0c6d852_u64)\n      put(cache, 0xdcdb1b2798182244_u64, 0xf8e431456cf88e66_u64)\n      put(cache, 0x8a08f0f8bf0f156b_u64, 0x1b8e9ecb641b5900_u64)\n      put(cache, 0xac8b2d36eed2dac5_u64, 0xe272467e3d222f40_u64)\n      put(cache, 0xd7adf884aa879177_u64, 0x5b0ed81dcc6abb10_u64)\n      put(cache, 0x86ccbb52ea94baea_u64, 0x98e947129fc2b4ea_u64)\n      put(cache, 0xa87fea27a539e9a5_u64, 0x3f2398d747b36225_u64)\n      put(cache, 0xd29fe4b18e88640e_u64, 0x8eec7f0d19a03aae_u64)\n      put(cache, 0x83a3eeeef9153e89_u64, 0x1953cf68300424ad_u64)\n      put(cache, 0xa48ceaaab75a8e2b_u64, 0x5fa8c3423c052dd8_u64)\n      put(cache, 0xcdb02555653131b6_u64, 0x3792f412cb06794e_u64)\n      put(cache, 0x808e17555f3ebf11_u64, 0xe2bbd88bbee40bd1_u64)\n      put(cache, 0xa0b19d2ab70e6ed6_u64, 0x5b6aceaeae9d0ec5_u64)\n      put(cache, 0xc8de047564d20a8b_u64, 0xf245825a5a445276_u64)\n      put(cache, 0xfb158592be068d2e_u64, 0xeed6e2f0f0d56713_u64)\n      put(cache, 0x9ced737bb6c4183d_u64, 0x55464dd69685606c_u64)\n      put(cache, 0xc428d05aa4751e4c_u64, 0xaa97e14c3c26b887_u64)\n      put(cache, 0xf53304714d9265df_u64, 0xd53dd99f4b3066a9_u64)\n      put(cache, 0x993fe2c6d07b7fab_u64, 0xe546a8038efe402a_u64)\n      put(cache, 0xbf8fdb78849a5f96_u64, 0xde98520472bdd034_u64)\n      put(cache, 0xef73d256a5c0f77c_u64, 0x963e66858f6d4441_u64)\n      put(cache, 0x95a8637627989aad_u64, 0xdde7001379a44aa9_u64)\n      put(cache, 0xbb127c53b17ec159_u64, 0x5560c018580d5d53_u64)\n      put(cache, 0xe9d71b689dde71af_u64, 0xaab8f01e6e10b4a7_u64)\n      put(cache, 0x9226712162ab070d_u64, 0xcab3961304ca70e9_u64)\n      put(cache, 0xb6b00d69bb55c8d1_u64, 0x3d607b97c5fd0d23_u64)\n      put(cache, 0xe45c10c42a2b3b05_u64, 0x8cb89a7db77c506b_u64)\n      put(cache, 0x8eb98a7a9a5b04e3_u64, 0x77f3608e92adb243_u64)\n      put(cache, 0xb267ed1940f1c61c_u64, 0x55f038b237591ed4_u64)\n      put(cache, 0xdf01e85f912e37a3_u64, 0x6b6c46dec52f6689_u64)\n      put(cache, 0x8b61313bbabce2c6_u64, 0x2323ac4b3b3da016_u64)\n      put(cache, 0xae397d8aa96c1b77_u64, 0xabec975e0a0d081b_u64)\n      put(cache, 0xd9c7dced53c72255_u64, 0x96e7bd358c904a22_u64)\n      put(cache, 0x881cea14545c7575_u64, 0x7e50d64177da2e55_u64)\n      put(cache, 0xaa242499697392d2_u64, 0xdde50bd1d5d0b9ea_u64)\n      put(cache, 0xd4ad2dbfc3d07787_u64, 0x955e4ec64b44e865_u64)\n      put(cache, 0x84ec3c97da624ab4_u64, 0xbd5af13bef0b113f_u64)\n      put(cache, 0xa6274bbdd0fadd61_u64, 0xecb1ad8aeacdd58f_u64)\n      put(cache, 0xcfb11ead453994ba_u64, 0x67de18eda5814af3_u64)\n      put(cache, 0x81ceb32c4b43fcf4_u64, 0x80eacf948770ced8_u64)\n      put(cache, 0xa2425ff75e14fc31_u64, 0xa1258379a94d028e_u64)\n      put(cache, 0xcad2f7f5359a3b3e_u64, 0x096ee45813a04331_u64)\n      put(cache, 0xfd87b5f28300ca0d_u64, 0x8bca9d6e188853fd_u64)\n      put(cache, 0x9e74d1b791e07e48_u64, 0x775ea264cf55347e_u64)\n      put(cache, 0xc612062576589dda_u64, 0x95364afe032a819e_u64)\n      put(cache, 0xf79687aed3eec551_u64, 0x3a83ddbd83f52205_u64)\n      put(cache, 0x9abe14cd44753b52_u64, 0xc4926a9672793543_u64)\n      put(cache, 0xc16d9a0095928a27_u64, 0x75b7053c0f178294_u64)\n      put(cache, 0xf1c90080baf72cb1_u64, 0x5324c68b12dd6339_u64)\n      put(cache, 0x971da05074da7bee_u64, 0xd3f6fc16ebca5e04_u64)\n      put(cache, 0xbce5086492111aea_u64, 0x88f4bb1ca6bcf585_u64)\n      put(cache, 0xec1e4a7db69561a5_u64, 0x2b31e9e3d06c32e6_u64)\n      put(cache, 0x9392ee8e921d5d07_u64, 0x3aff322e62439fd0_u64)\n      put(cache, 0xb877aa3236a4b449_u64, 0x09befeb9fad487c3_u64)\n      put(cache, 0xe69594bec44de15b_u64, 0x4c2ebe687989a9b4_u64)\n      put(cache, 0x901d7cf73ab0acd9_u64, 0x0f9d37014bf60a11_u64)\n      put(cache, 0xb424dc35095cd80f_u64, 0x538484c19ef38c95_u64)\n      put(cache, 0xe12e13424bb40e13_u64, 0x2865a5f206b06fba_u64)\n      put(cache, 0x8cbccc096f5088cb_u64, 0xf93f87b7442e45d4_u64)\n      put(cache, 0xafebff0bcb24aafe_u64, 0xf78f69a51539d749_u64)\n      put(cache, 0xdbe6fecebdedd5be_u64, 0xb573440e5a884d1c_u64)\n      put(cache, 0x89705f4136b4a597_u64, 0x31680a88f8953031_u64)\n      put(cache, 0xabcc77118461cefc_u64, 0xfdc20d2b36ba7c3e_u64)\n      put(cache, 0xd6bf94d5e57a42bc_u64, 0x3d32907604691b4d_u64)\n      put(cache, 0x8637bd05af6c69b5_u64, 0xa63f9a49c2c1b110_u64)\n      put(cache, 0xa7c5ac471b478423_u64, 0x0fcf80dc33721d54_u64)\n      put(cache, 0xd1b71758e219652b_u64, 0xd3c36113404ea4a9_u64)\n      put(cache, 0x83126e978d4fdf3b_u64, 0x645a1cac083126ea_u64)\n      put(cache, 0xa3d70a3d70a3d70a_u64, 0x3d70a3d70a3d70a4_u64)\n      put(cache, 0xcccccccccccccccc_u64, 0xcccccccccccccccd_u64)\n      put(cache, 0x8000000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xa000000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xc800000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xfa00000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0x9c40000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xc350000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xf424000000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0x9896800000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xbebc200000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xee6b280000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0x9502f90000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xba43b74000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xe8d4a51000000000_u64, 0x0000000000000000_u64)\n      put(cache, 0x9184e72a00000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xb5e620f480000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xe35fa931a0000000_u64, 0x0000000000000000_u64)\n      put(cache, 0x8e1bc9bf04000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xb1a2bc2ec5000000_u64, 0x0000000000000000_u64)\n      put(cache, 0xde0b6b3a76400000_u64, 0x0000000000000000_u64)\n      put(cache, 0x8ac7230489e80000_u64, 0x0000000000000000_u64)\n      put(cache, 0xad78ebc5ac620000_u64, 0x0000000000000000_u64)\n      put(cache, 0xd8d726b7177a8000_u64, 0x0000000000000000_u64)\n      put(cache, 0x878678326eac9000_u64, 0x0000000000000000_u64)\n      put(cache, 0xa968163f0a57b400_u64, 0x0000000000000000_u64)\n      put(cache, 0xd3c21bcecceda100_u64, 0x0000000000000000_u64)\n      put(cache, 0x84595161401484a0_u64, 0x0000000000000000_u64)\n      put(cache, 0xa56fa5b99019a5c8_u64, 0x0000000000000000_u64)\n      put(cache, 0xcecb8f27f4200f3a_u64, 0x0000000000000000_u64)\n      put(cache, 0x813f3978f8940984_u64, 0x4000000000000000_u64)\n      put(cache, 0xa18f07d736b90be5_u64, 0x5000000000000000_u64)\n      put(cache, 0xc9f2c9cd04674ede_u64, 0xa400000000000000_u64)\n      put(cache, 0xfc6f7c4045812296_u64, 0x4d00000000000000_u64)\n      put(cache, 0x9dc5ada82b70b59d_u64, 0xf020000000000000_u64)\n      put(cache, 0xc5371912364ce305_u64, 0x6c28000000000000_u64)\n      put(cache, 0xf684df56c3e01bc6_u64, 0xc732000000000000_u64)\n      put(cache, 0x9a130b963a6c115c_u64, 0x3c7f400000000000_u64)\n      put(cache, 0xc097ce7bc90715b3_u64, 0x4b9f100000000000_u64)\n      put(cache, 0xf0bdc21abb48db20_u64, 0x1e86d40000000000_u64)\n      put(cache, 0x96769950b50d88f4_u64, 0x1314448000000000_u64)\n      put(cache, 0xbc143fa4e250eb31_u64, 0x17d955a000000000_u64)\n      put(cache, 0xeb194f8e1ae525fd_u64, 0x5dcfab0800000000_u64)\n      put(cache, 0x92efd1b8d0cf37be_u64, 0x5aa1cae500000000_u64)\n      put(cache, 0xb7abc627050305ad_u64, 0xf14a3d9e40000000_u64)\n      put(cache, 0xe596b7b0c643c719_u64, 0x6d9ccd05d0000000_u64)\n      put(cache, 0x8f7e32ce7bea5c6f_u64, 0xe4820023a2000000_u64)\n      put(cache, 0xb35dbf821ae4f38b_u64, 0xdda2802c8a800000_u64)\n      put(cache, 0xe0352f62a19e306e_u64, 0xd50b2037ad200000_u64)\n      put(cache, 0x8c213d9da502de45_u64, 0x4526f422cc340000_u64)\n      put(cache, 0xaf298d050e4395d6_u64, 0x9670b12b7f410000_u64)\n      put(cache, 0xdaf3f04651d47b4c_u64, 0x3c0cdd765f114000_u64)\n      put(cache, 0x88d8762bf324cd0f_u64, 0xa5880a69fb6ac800_u64)\n      put(cache, 0xab0e93b6efee0053_u64, 0x8eea0d047a457a00_u64)\n      put(cache, 0xd5d238a4abe98068_u64, 0x72a4904598d6d880_u64)\n      put(cache, 0x85a36366eb71f041_u64, 0x47a6da2b7f864750_u64)\n      put(cache, 0xa70c3c40a64e6c51_u64, 0x999090b65f67d924_u64)\n      put(cache, 0xd0cf4b50cfe20765_u64, 0xfff4b4e3f741cf6d_u64)\n      put(cache, 0x82818f1281ed449f_u64, 0xbff8f10e7a8921a5_u64)\n      put(cache, 0xa321f2d7226895c7_u64, 0xaff72d52192b6a0e_u64)\n      put(cache, 0xcbea6f8ceb02bb39_u64, 0x9bf4f8a69f764491_u64)\n      put(cache, 0xfee50b7025c36a08_u64, 0x02f236d04753d5b5_u64)\n      put(cache, 0x9f4f2726179a2245_u64, 0x01d762422c946591_u64)\n      put(cache, 0xc722f0ef9d80aad6_u64, 0x424d3ad2b7b97ef6_u64)\n      put(cache, 0xf8ebad2b84e0d58b_u64, 0xd2e0898765a7deb3_u64)\n      put(cache, 0x9b934c3b330c8577_u64, 0x63cc55f49f88eb30_u64)\n      put(cache, 0xc2781f49ffcfa6d5_u64, 0x3cbf6b71c76b25fc_u64)\n      put(cache, 0xf316271c7fc3908a_u64, 0x8bef464e3945ef7b_u64)\n      put(cache, 0x97edd871cfda3a56_u64, 0x97758bf0e3cbb5ad_u64)\n      put(cache, 0xbde94e8e43d0c8ec_u64, 0x3d52eeed1cbea318_u64)\n      put(cache, 0xed63a231d4c4fb27_u64, 0x4ca7aaa863ee4bde_u64)\n      put(cache, 0x945e455f24fb1cf8_u64, 0x8fe8caa93e74ef6b_u64)\n      put(cache, 0xb975d6b6ee39e436_u64, 0xb3e2fd538e122b45_u64)\n      put(cache, 0xe7d34c64a9c85d44_u64, 0x60dbbca87196b617_u64)\n      put(cache, 0x90e40fbeea1d3a4a_u64, 0xbc8955e946fe31ce_u64)\n      put(cache, 0xb51d13aea4a488dd_u64, 0x6babab6398bdbe42_u64)\n      put(cache, 0xe264589a4dcdab14_u64, 0xc696963c7eed2dd2_u64)\n      put(cache, 0x8d7eb76070a08aec_u64, 0xfc1e1de5cf543ca3_u64)\n      put(cache, 0xb0de65388cc8ada8_u64, 0x3b25a55f43294bcc_u64)\n      put(cache, 0xdd15fe86affad912_u64, 0x49ef0eb713f39ebf_u64)\n      put(cache, 0x8a2dbf142dfcc7ab_u64, 0x6e3569326c784338_u64)\n      put(cache, 0xacb92ed9397bf996_u64, 0x49c2c37f07965405_u64)\n      put(cache, 0xd7e77a8f87daf7fb_u64, 0xdc33745ec97be907_u64)\n      put(cache, 0x86f0ac99b4e8dafd_u64, 0x69a028bb3ded71a4_u64)\n      put(cache, 0xa8acd7c0222311bc_u64, 0xc40832ea0d68ce0d_u64)\n      put(cache, 0xd2d80db02aabd62b_u64, 0xf50a3fa490c30191_u64)\n      put(cache, 0x83c7088e1aab65db_u64, 0x792667c6da79e0fb_u64)\n      put(cache, 0xa4b8cab1a1563f52_u64, 0x577001b891185939_u64)\n      put(cache, 0xcde6fd5e09abcf26_u64, 0xed4c0226b55e6f87_u64)\n      put(cache, 0x80b05e5ac60b6178_u64, 0x544f8158315b05b5_u64)\n      put(cache, 0xa0dc75f1778e39d6_u64, 0x696361ae3db1c722_u64)\n      put(cache, 0xc913936dd571c84c_u64, 0x03bc3a19cd1e38ea_u64)\n      put(cache, 0xfb5878494ace3a5f_u64, 0x04ab48a04065c724_u64)\n      put(cache, 0x9d174b2dcec0e47b_u64, 0x62eb0d64283f9c77_u64)\n      put(cache, 0xc45d1df942711d9a_u64, 0x3ba5d0bd324f8395_u64)\n      put(cache, 0xf5746577930d6500_u64, 0xca8f44ec7ee3647a_u64)\n      put(cache, 0x9968bf6abbe85f20_u64, 0x7e998b13cf4e1ecc_u64)\n      put(cache, 0xbfc2ef456ae276e8_u64, 0x9e3fedd8c321a67f_u64)\n      put(cache, 0xefb3ab16c59b14a2_u64, 0xc5cfe94ef3ea101f_u64)\n      put(cache, 0x95d04aee3b80ece5_u64, 0xbba1f1d158724a13_u64)\n      put(cache, 0xbb445da9ca61281f_u64, 0x2a8a6e45ae8edc98_u64)\n      put(cache, 0xea1575143cf97226_u64, 0xf52d09d71a3293be_u64)\n      put(cache, 0x924d692ca61be758_u64, 0x593c2626705f9c57_u64)\n      put(cache, 0xb6e0c377cfa2e12e_u64, 0x6f8b2fb00c77836d_u64)\n      put(cache, 0xe498f455c38b997a_u64, 0x0b6dfb9c0f956448_u64)\n      put(cache, 0x8edf98b59a373fec_u64, 0x4724bd4189bd5ead_u64)\n      put(cache, 0xb2977ee300c50fe7_u64, 0x58edec91ec2cb658_u64)\n      put(cache, 0xdf3d5e9bc0f653e1_u64, 0x2f2967b66737e3ee_u64)\n      put(cache, 0x8b865b215899f46c_u64, 0xbd79e0d20082ee75_u64)\n      put(cache, 0xae67f1e9aec07187_u64, 0xecd8590680a3aa12_u64)\n      put(cache, 0xda01ee641a708de9_u64, 0xe80e6f4820cc9496_u64)\n      put(cache, 0x884134fe908658b2_u64, 0x3109058d147fdcde_u64)\n      put(cache, 0xaa51823e34a7eede_u64, 0xbd4b46f0599fd416_u64)\n      put(cache, 0xd4e5e2cdc1d1ea96_u64, 0x6c9e18ac7007c91b_u64)\n      put(cache, 0x850fadc09923329e_u64, 0x03e2cf6bc604ddb1_u64)\n      put(cache, 0xa6539930bf6bff45_u64, 0x84db8346b786151d_u64)\n      put(cache, 0xcfe87f7cef46ff16_u64, 0xe612641865679a64_u64)\n      put(cache, 0x81f14fae158c5f6e_u64, 0x4fcb7e8f3f60c07f_u64)\n      put(cache, 0xa26da3999aef7749_u64, 0xe3be5e330f38f09e_u64)\n      put(cache, 0xcb090c8001ab551c_u64, 0x5cadf5bfd3072cc6_u64)\n      put(cache, 0xfdcb4fa002162a63_u64, 0x73d9732fc7c8f7f7_u64)\n      put(cache, 0x9e9f11c4014dda7e_u64, 0x2867e7fddcdd9afb_u64)\n      put(cache, 0xc646d63501a1511d_u64, 0xb281e1fd541501b9_u64)\n      put(cache, 0xf7d88bc24209a565_u64, 0x1f225a7ca91a4227_u64)\n      put(cache, 0x9ae757596946075f_u64, 0x3375788de9b06959_u64)\n      put(cache, 0xc1a12d2fc3978937_u64, 0x0052d6b1641c83af_u64)\n      put(cache, 0xf209787bb47d6b84_u64, 0xc0678c5dbd23a49b_u64)\n      put(cache, 0x9745eb4d50ce6332_u64, 0xf840b7ba963646e1_u64)\n      put(cache, 0xbd176620a501fbff_u64, 0xb650e5a93bc3d899_u64)\n      put(cache, 0xec5d3fa8ce427aff_u64, 0xa3e51f138ab4cebf_u64)\n      put(cache, 0x93ba47c980e98cdf_u64, 0xc66f336c36b10138_u64)\n      put(cache, 0xb8a8d9bbe123f017_u64, 0xb80b0047445d4185_u64)\n      put(cache, 0xe6d3102ad96cec1d_u64, 0xa60dc059157491e6_u64)\n      put(cache, 0x9043ea1ac7e41392_u64, 0x87c89837ad68db30_u64)\n      put(cache, 0xb454e4a179dd1877_u64, 0x29babe4598c311fc_u64)\n      put(cache, 0xe16a1dc9d8545e94_u64, 0xf4296dd6fef3d67b_u64)\n      put(cache, 0x8ce2529e2734bb1d_u64, 0x1899e4a65f58660d_u64)\n      put(cache, 0xb01ae745b101e9e4_u64, 0x5ec05dcff72e7f90_u64)\n      put(cache, 0xdc21a1171d42645d_u64, 0x76707543f4fa1f74_u64)\n      put(cache, 0x899504ae72497eba_u64, 0x6a06494a791c53a9_u64)\n      put(cache, 0xabfa45da0edbde69_u64, 0x0487db9d17636893_u64)\n      put(cache, 0xd6f8d7509292d603_u64, 0x45a9d2845d3c42b7_u64)\n      put(cache, 0x865b86925b9bc5c2_u64, 0x0b8a2392ba45a9b3_u64)\n      put(cache, 0xa7f26836f282b732_u64, 0x8e6cac7768d7141f_u64)\n      put(cache, 0xd1ef0244af2364ff_u64, 0x3207d795430cd927_u64)\n      put(cache, 0x8335616aed761f1f_u64, 0x7f44e6bd49e807b9_u64)\n      put(cache, 0xa402b9c5a8d3a6e7_u64, 0x5f16206c9c6209a7_u64)\n      put(cache, 0xcd036837130890a1_u64, 0x36dba887c37a8c10_u64)\n      put(cache, 0x802221226be55a64_u64, 0xc2494954da2c978a_u64)\n      put(cache, 0xa02aa96b06deb0fd_u64, 0xf2db9baa10b7bd6d_u64)\n      put(cache, 0xc83553c5c8965d3d_u64, 0x6f92829494e5acc8_u64)\n      put(cache, 0xfa42a8b73abbf48c_u64, 0xcb772339ba1f17fa_u64)\n      put(cache, 0x9c69a97284b578d7_u64, 0xff2a760414536efc_u64)\n      put(cache, 0xc38413cf25e2d70d_u64, 0xfef5138519684abb_u64)\n      put(cache, 0xf46518c2ef5b8cd1_u64, 0x7eb258665fc25d6a_u64)\n      put(cache, 0x98bf2f79d5993802_u64, 0xef2f773ffbd97a62_u64)\n      put(cache, 0xbeeefb584aff8603_u64, 0xaafb550ffacfd8fb_u64)\n      put(cache, 0xeeaaba2e5dbf6784_u64, 0x95ba2a53f983cf39_u64)\n      put(cache, 0x952ab45cfa97a0b2_u64, 0xdd945a747bf26184_u64)\n      put(cache, 0xba756174393d88df_u64, 0x94f971119aeef9e5_u64)\n      put(cache, 0xe912b9d1478ceb17_u64, 0x7a37cd5601aab85e_u64)\n      put(cache, 0x91abb422ccb812ee_u64, 0xac62e055c10ab33b_u64)\n      put(cache, 0xb616a12b7fe617aa_u64, 0x577b986b314d600a_u64)\n      put(cache, 0xe39c49765fdf9d94_u64, 0xed5a7e85fda0b80c_u64)\n      put(cache, 0x8e41ade9fbebc27d_u64, 0x14588f13be847308_u64)\n      put(cache, 0xb1d219647ae6b31c_u64, 0x596eb2d8ae258fc9_u64)\n      put(cache, 0xde469fbd99a05fe3_u64, 0x6fca5f8ed9aef3bc_u64)\n      put(cache, 0x8aec23d680043bee_u64, 0x25de7bb9480d5855_u64)\n      put(cache, 0xada72ccc20054ae9_u64, 0xaf561aa79a10ae6b_u64)\n      put(cache, 0xd910f7ff28069da4_u64, 0x1b2ba1518094da05_u64)\n      put(cache, 0x87aa9aff79042286_u64, 0x90fb44d2f05d0843_u64)\n      put(cache, 0xa99541bf57452b28_u64, 0x353a1607ac744a54_u64)\n      put(cache, 0xd3fa922f2d1675f2_u64, 0x42889b8997915ce9_u64)\n      put(cache, 0x847c9b5d7c2e09b7_u64, 0x69956135febada12_u64)\n      put(cache, 0xa59bc234db398c25_u64, 0x43fab9837e699096_u64)\n      put(cache, 0xcf02b2c21207ef2e_u64, 0x94f967e45e03f4bc_u64)\n      put(cache, 0x8161afb94b44f57d_u64, 0x1d1be0eebac278f6_u64)\n      put(cache, 0xa1ba1ba79e1632dc_u64, 0x6462d92a69731733_u64)\n      put(cache, 0xca28a291859bbf93_u64, 0x7d7b8f7503cfdcff_u64)\n      put(cache, 0xfcb2cb35e702af78_u64, 0x5cda735244c3d43f_u64)\n      put(cache, 0x9defbf01b061adab_u64, 0x3a0888136afa64a8_u64)\n      put(cache, 0xc56baec21c7a1916_u64, 0x088aaa1845b8fdd1_u64)\n      put(cache, 0xf6c69a72a3989f5b_u64, 0x8aad549e57273d46_u64)\n      put(cache, 0x9a3c2087a63f6399_u64, 0x36ac54e2f678864c_u64)\n      put(cache, 0xc0cb28a98fcf3c7f_u64, 0x84576a1bb416a7de_u64)\n      put(cache, 0xf0fdf2d3f3c30b9f_u64, 0x656d44a2a11c51d6_u64)\n      put(cache, 0x969eb7c47859e743_u64, 0x9f644ae5a4b1b326_u64)\n      put(cache, 0xbc4665b596706114_u64, 0x873d5d9f0dde1fef_u64)\n      put(cache, 0xeb57ff22fc0c7959_u64, 0xa90cb506d155a7eb_u64)\n      put(cache, 0x9316ff75dd87cbd8_u64, 0x09a7f12442d588f3_u64)\n      put(cache, 0xb7dcbf5354e9bece_u64, 0x0c11ed6d538aeb30_u64)\n      put(cache, 0xe5d3ef282a242e81_u64, 0x8f1668c8a86da5fb_u64)\n      put(cache, 0x8fa475791a569d10_u64, 0xf96e017d694487bd_u64)\n      put(cache, 0xb38d92d760ec4455_u64, 0x37c981dcc395a9ad_u64)\n      put(cache, 0xe070f78d3927556a_u64, 0x85bbe253f47b1418_u64)\n      put(cache, 0x8c469ab843b89562_u64, 0x93956d7478ccec8f_u64)\n      put(cache, 0xaf58416654a6babb_u64, 0x387ac8d1970027b3_u64)\n      put(cache, 0xdb2e51bfe9d0696a_u64, 0x06997b05fcc0319f_u64)\n      put(cache, 0x88fcf317f22241e2_u64, 0x441fece3bdf81f04_u64)\n      put(cache, 0xab3c2fddeeaad25a_u64, 0xd527e81cad7626c4_u64)\n      put(cache, 0xd60b3bd56a5586f1_u64, 0x8a71e223d8d3b075_u64)\n      put(cache, 0x85c7056562757456_u64, 0xf6872d5667844e4a_u64)\n      put(cache, 0xa738c6bebb12d16c_u64, 0xb428f8ac016561dc_u64)\n      put(cache, 0xd106f86e69d785c7_u64, 0xe13336d701beba53_u64)\n      put(cache, 0x82a45b450226b39c_u64, 0xecc0024661173474_u64)\n      put(cache, 0xa34d721642b06084_u64, 0x27f002d7f95d0191_u64)\n      put(cache, 0xcc20ce9bd35c78a5_u64, 0x31ec038df7b441f5_u64)\n      put(cache, 0xff290242c83396ce_u64, 0x7e67047175a15272_u64)\n      put(cache, 0x9f79a169bd203e41_u64, 0x0f0062c6e984d387_u64)\n      put(cache, 0xc75809c42c684dd1_u64, 0x52c07b78a3e60869_u64)\n      put(cache, 0xf92e0c3537826145_u64, 0xa7709a56ccdf8a83_u64)\n      put(cache, 0x9bbcc7a142b17ccb_u64, 0x88a66076400bb692_u64)\n      put(cache, 0xc2abf989935ddbfe_u64, 0x6acff893d00ea436_u64)\n      put(cache, 0xf356f7ebf83552fe_u64, 0x0583f6b8c4124d44_u64)\n      put(cache, 0x98165af37b2153de_u64, 0xc3727a337a8b704b_u64)\n      put(cache, 0xbe1bf1b059e9a8d6_u64, 0x744f18c0592e4c5d_u64)\n      put(cache, 0xeda2ee1c7064130c_u64, 0x1162def06f79df74_u64)\n      put(cache, 0x9485d4d1c63e8be7_u64, 0x8addcb5645ac2ba9_u64)\n      put(cache, 0xb9a74a0637ce2ee1_u64, 0x6d953e2bd7173693_u64)\n      put(cache, 0xe8111c87c5c1ba99_u64, 0xc8fa8db6ccdd0438_u64)\n      put(cache, 0x910ab1d4db9914a0_u64, 0x1d9c9892400a22a3_u64)\n      put(cache, 0xb54d5e4a127f59c8_u64, 0x2503beb6d00cab4c_u64)\n      put(cache, 0xe2a0b5dc971f303a_u64, 0x2e44ae64840fd61e_u64)\n      put(cache, 0x8da471a9de737e24_u64, 0x5ceaecfed289e5d3_u64)\n      put(cache, 0xb10d8e1456105dad_u64, 0x7425a83e872c5f48_u64)\n      put(cache, 0xdd50f1996b947518_u64, 0xd12f124e28f7771a_u64)\n      put(cache, 0x8a5296ffe33cc92f_u64, 0x82bd6b70d99aaa70_u64)\n      put(cache, 0xace73cbfdc0bfb7b_u64, 0x636cc64d1001550c_u64)\n      put(cache, 0xd8210befd30efa5a_u64, 0x3c47f7e05401aa4f_u64)\n      put(cache, 0x8714a775e3e95c78_u64, 0x65acfaec34810a72_u64)\n      put(cache, 0xa8d9d1535ce3b396_u64, 0x7f1839a741a14d0e_u64)\n      put(cache, 0xd31045a8341ca07c_u64, 0x1ede48111209a051_u64)\n      put(cache, 0x83ea2b892091e44d_u64, 0x934aed0aab460433_u64)\n      put(cache, 0xa4e4b66b68b65d60_u64, 0xf81da84d56178540_u64)\n      put(cache, 0xce1de40642e3f4b9_u64, 0x36251260ab9d668f_u64)\n      put(cache, 0x80d2ae83e9ce78f3_u64, 0xc1d72b7c6b42601a_u64)\n      put(cache, 0xa1075a24e4421730_u64, 0xb24cf65b8612f820_u64)\n      put(cache, 0xc94930ae1d529cfc_u64, 0xdee033f26797b628_u64)\n      put(cache, 0xfb9b7cd9a4a7443c_u64, 0x169840ef017da3b2_u64)\n      put(cache, 0x9d412e0806e88aa5_u64, 0x8e1f289560ee864f_u64)\n      put(cache, 0xc491798a08a2ad4e_u64, 0xf1a6f2bab92a27e3_u64)\n      put(cache, 0xf5b5d7ec8acb58a2_u64, 0xae10af696774b1dc_u64)\n      put(cache, 0x9991a6f3d6bf1765_u64, 0xacca6da1e0a8ef2a_u64)\n      put(cache, 0xbff610b0cc6edd3f_u64, 0x17fd090a58d32af4_u64)\n      put(cache, 0xeff394dcff8a948e_u64, 0xddfc4b4cef07f5b1_u64)\n      put(cache, 0x95f83d0a1fb69cd9_u64, 0x4abdaf101564f98f_u64)\n      put(cache, 0xbb764c4ca7a4440f_u64, 0x9d6d1ad41abe37f2_u64)\n      put(cache, 0xea53df5fd18d5513_u64, 0x84c86189216dc5ee_u64)\n      put(cache, 0x92746b9be2f8552c_u64, 0x32fd3cf5b4e49bb5_u64)\n      put(cache, 0xb7118682dbb66a77_u64, 0x3fbc8c33221dc2a2_u64)\n      put(cache, 0xe4d5e82392a40515_u64, 0x0fabaf3feaa5334b_u64)\n      put(cache, 0x8f05b1163ba6832d_u64, 0x29cb4d87f2a7400f_u64)\n      put(cache, 0xb2c71d5bca9023f8_u64, 0x743e20e9ef511013_u64)\n      put(cache, 0xdf78e4b2bd342cf6_u64, 0x914da9246b255417_u64)\n      put(cache, 0x8bab8eefb6409c1a_u64, 0x1ad089b6c2f7548f_u64)\n      put(cache, 0xae9672aba3d0c320_u64, 0xa184ac2473b529b2_u64)\n      put(cache, 0xda3c0f568cc4f3e8_u64, 0xc9e5d72d90a2741f_u64)\n      put(cache, 0x8865899617fb1871_u64, 0x7e2fa67c7a658893_u64)\n      put(cache, 0xaa7eebfb9df9de8d_u64, 0xddbb901b98feeab8_u64)\n      put(cache, 0xd51ea6fa85785631_u64, 0x552a74227f3ea566_u64)\n      put(cache, 0x8533285c936b35de_u64, 0xd53a88958f872760_u64)\n      put(cache, 0xa67ff273b8460356_u64, 0x8a892abaf368f138_u64)\n      put(cache, 0xd01fef10a657842c_u64, 0x2d2b7569b0432d86_u64)\n      put(cache, 0x8213f56a67f6b29b_u64, 0x9c3b29620e29fc74_u64)\n      put(cache, 0xa298f2c501f45f42_u64, 0x8349f3ba91b47b90_u64)\n      put(cache, 0xcb3f2f7642717713_u64, 0x241c70a936219a74_u64)\n      put(cache, 0xfe0efb53d30dd4d7_u64, 0xed238cd383aa0111_u64)\n      put(cache, 0x9ec95d1463e8a506_u64, 0xf4363804324a40ab_u64)\n      put(cache, 0xc67bb4597ce2ce48_u64, 0xb143c6053edcd0d6_u64)\n      put(cache, 0xf81aa16fdc1b81da_u64, 0xdd94b7868e94050b_u64)\n      put(cache, 0x9b10a4e5e9913128_u64, 0xca7cf2b4191c8327_u64)\n      put(cache, 0xc1d4ce1f63f57d72_u64, 0xfd1c2f611f63a3f1_u64)\n      put(cache, 0xf24a01a73cf2dccf_u64, 0xbc633b39673c8ced_u64)\n      put(cache, 0x976e41088617ca01_u64, 0xd5be0503e085d814_u64)\n      put(cache, 0xbd49d14aa79dbc82_u64, 0x4b2d8644d8a74e19_u64)\n      put(cache, 0xec9c459d51852ba2_u64, 0xddf8e7d60ed1219f_u64)\n      put(cache, 0x93e1ab8252f33b45_u64, 0xcabb90e5c942b504_u64)\n      put(cache, 0xb8da1662e7b00a17_u64, 0x3d6a751f3b936244_u64)\n      put(cache, 0xe7109bfba19c0c9d_u64, 0x0cc512670a783ad5_u64)\n      put(cache, 0x906a617d450187e2_u64, 0x27fb2b80668b24c6_u64)\n      put(cache, 0xb484f9dc9641e9da_u64, 0xb1f9f660802dedf7_u64)\n      put(cache, 0xe1a63853bbd26451_u64, 0x5e7873f8a0396974_u64)\n      put(cache, 0x8d07e33455637eb2_u64, 0xdb0b487b6423e1e9_u64)\n      put(cache, 0xb049dc016abc5e5f_u64, 0x91ce1a9a3d2cda63_u64)\n      put(cache, 0xdc5c5301c56b75f7_u64, 0x7641a140cc7810fc_u64)\n      put(cache, 0x89b9b3e11b6329ba_u64, 0xa9e904c87fcb0a9e_u64)\n      put(cache, 0xac2820d9623bf429_u64, 0x546345fa9fbdcd45_u64)\n      put(cache, 0xd732290fbacaf133_u64, 0xa97c177947ad4096_u64)\n      put(cache, 0x867f59a9d4bed6c0_u64, 0x49ed8eabcccc485e_u64)\n      put(cache, 0xa81f301449ee8c70_u64, 0x5c68f256bfff5a75_u64)\n      put(cache, 0xd226fc195c6a2f8c_u64, 0x73832eec6fff3112_u64)\n      put(cache, 0x83585d8fd9c25db7_u64, 0xc831fd53c5ff7eac_u64)\n      put(cache, 0xa42e74f3d032f525_u64, 0xba3e7ca8b77f5e56_u64)\n      put(cache, 0xcd3a1230c43fb26f_u64, 0x28ce1bd2e55f35ec_u64)\n      put(cache, 0x80444b5e7aa7cf85_u64, 0x7980d163cf5b81b4_u64)\n      put(cache, 0xa0555e361951c366_u64, 0xd7e105bcc3326220_u64)\n      put(cache, 0xc86ab5c39fa63440_u64, 0x8dd9472bf3fefaa8_u64)\n      put(cache, 0xfa856334878fc150_u64, 0xb14f98f6f0feb952_u64)\n      put(cache, 0x9c935e00d4b9d8d2_u64, 0x6ed1bf9a569f33d4_u64)\n      put(cache, 0xc3b8358109e84f07_u64, 0x0a862f80ec4700c9_u64)\n      put(cache, 0xf4a642e14c6262c8_u64, 0xcd27bb612758c0fb_u64)\n      put(cache, 0x98e7e9cccfbd7dbd_u64, 0x8038d51cb897789d_u64)\n      put(cache, 0xbf21e44003acdd2c_u64, 0xe0470a63e6bd56c4_u64)\n      put(cache, 0xeeea5d5004981478_u64, 0x1858ccfce06cac75_u64)\n      put(cache, 0x95527a5202df0ccb_u64, 0x0f37801e0c43ebc9_u64)\n      put(cache, 0xbaa718e68396cffd_u64, 0xd30560258f54e6bb_u64)\n      put(cache, 0xe950df20247c83fd_u64, 0x47c6b82ef32a206a_u64)\n      put(cache, 0x91d28b7416cdd27e_u64, 0x4cdc331d57fa5442_u64)\n      put(cache, 0xb6472e511c81471d_u64, 0xe0133fe4adf8e953_u64)\n      put(cache, 0xe3d8f9e563a198e5_u64, 0x58180fddd97723a7_u64)\n      put(cache, 0x8e679c2f5e44ff8f_u64, 0x570f09eaa7ea7649_u64)\n      put(cache, 0xb201833b35d63f73_u64, 0x2cd2cc6551e513db_u64)\n      put(cache, 0xde81e40a034bcf4f_u64, 0xf8077f7ea65e58d2_u64)\n      put(cache, 0x8b112e86420f6191_u64, 0xfb04afaf27faf783_u64)\n      put(cache, 0xadd57a27d29339f6_u64, 0x79c5db9af1f9b564_u64)\n      put(cache, 0xd94ad8b1c7380874_u64, 0x18375281ae7822bd_u64)\n      put(cache, 0x87cec76f1c830548_u64, 0x8f2293910d0b15b6_u64)\n      put(cache, 0xa9c2794ae3a3c69a_u64, 0xb2eb3875504ddb23_u64)\n      put(cache, 0xd433179d9c8cb841_u64, 0x5fa60692a46151ec_u64)\n      put(cache, 0x849feec281d7f328_u64, 0xdbc7c41ba6bcd334_u64)\n      put(cache, 0xa5c7ea73224deff3_u64, 0x12b9b522906c0801_u64)\n      put(cache, 0xcf39e50feae16bef_u64, 0xd768226b34870a01_u64)\n      put(cache, 0x81842f29f2cce375_u64, 0xe6a1158300d46641_u64)\n      put(cache, 0xa1e53af46f801c53_u64, 0x60495ae3c1097fd1_u64)\n      put(cache, 0xca5e89b18b602368_u64, 0x385bb19cb14bdfc5_u64)\n      put(cache, 0xfcf62c1dee382c42_u64, 0x46729e03dd9ed7b6_u64)\n      put(cache, 0x9e19db92b4e31ba9_u64, 0x6c07a2c26a8346d2_u64)\n      put(cache, 0xc5a05277621be293_u64, 0xc7098b7305241886_u64)\n      put(cache, 0xf70867153aa2db38_u64, 0xb8cbee4fc66d1ea8_u64)\n      cache\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/printer/grisu3.cr",
    "content": "# Grisu3 is ported from the C++ \"double-conversions\" library.\n#\n# The following is their license:\n#   Copyright 2012 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nrequire \"./diy_fp\"\nrequire \"./ieee\"\nrequire \"./cached_powers\"\n\n@[Deprecated(\"Use `Float::Printer::Dragonbox` instead\")]\nmodule Float::Printer::Grisu3\n  extend self\n\n  # Adjusts the last digit of the generated number, and screens out generated\n  # solutions that may be inaccurate. A solution may be inaccurate if it is\n  # outside the safe interval, or if we cannot prove that it is closer to the\n  # input than a neighboring representation of the same length.\n  #\n  # Input:\n  # * *buffer_ptr*: buffer pointer containing the digits of `too_high / 10^kappa`\n  # * *length*: the buffer's length\n  # * *distance_too_high_w*: `(too_high - w).frac * unit`\n  # * *unsafe_interval*: `(too_high - too_low).frac * unit`\n  # * *rest*: `(too_high - buffer * 10^kappa).frac * unit`\n  # * *ten_kappa*: `10^kappa * unit`\n  # * *unit*: the common multiplier\n  #\n  # Output: returns `true` if the buffer is guaranteed to contain the closest\n  # representable number to the input.\n  #\n  # Modifies the generated digits in the buffer to approach (round towards) *w*.\n  def round_weed(buffer_ptr, length, distance_too_high_w, unsafe_interval, rest, ten_kappa, unit) : Bool\n    buffer = buffer_ptr.to_slice(128)\n    small_distance = distance_too_high_w - unit\n    big_distance = distance_too_high_w + unit\n\n    # Let w_low  = too_high - big_distance, and\n    #     w_high = too_high - small_distance.\n    # Note: w_low < w < w_high\n    #\n    # The real w (* unit) must lie somewhere inside the interval\n    # ]w_low; w_high[ (often written as \"(w_low; w_high)\")\n    #\n    # Basically the buffer currently contains a number in the unsafe interval\n    # ]too_low; too_high[ with too_low < w < too_high\n    #\n    #  too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n    #                     ^v 1 unit            ^      ^                 ^      ^\n    #  boundary_high ---------------------     .      .                 .      .\n    #                     ^v 1 unit            .      .                 .      .\n    #   - - - - - - - - - - - - - - - - - - -  +  - - + - - - - - -     .      .\n    #                                          .      .         ^       .      .\n    #                                          .  big_distance  .       .      .\n    #                                          .      .         .       .    rest\n    #                              small_distance     .         .       .      .\n    #                                          v      .         .       .      .\n    #  w_high - - - - - - - - - - - - - - - - - -     .         .       .      .\n    #                     ^v 1 unit                   .         .       .      .\n    #  w ----------------------------------------     .         .       .      .\n    #                     ^v 1 unit                   v         .       .      .\n    #  w_low  - - - - - - - - - - - - - - - - - - - - -         .       .      .\n    #                                                           .       .      v\n    #  buffer --------------------------------------------------+-------+--------\n    #                                                           .       .\n    #                                                  safe_interval    .\n    #                                                           v       .\n    #   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -     .\n    #                     ^v 1 unit                                     .\n    #  boundary_low -------------------------                     unsafe_interval\n    #                     ^v 1 unit                                     v\n    #  too_low  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n    #\n    #\n    # Note that the value of buffer could lie anywhere inside the range too_low\n    # to too_high.\n    #\n    # boundary_low, boundary_high and w are approximations of the real boundaries\n    # and v (the input number). They are guaranteed to be precise up to one unit.\n    # In fact the error is guaranteed to be strictly less than one unit.\n    #\n    # Anything that lies outside the unsafe interval is guaranteed not to round\n    # to v when read again.\n    # Anything that lies inside the safe interval is guaranteed to round to v\n    # when read again.\n    # If the number inside the buffer lies inside the unsafe interval but not\n    # inside the safe interval then we simply do not know and bail out (returning\n    # false).\n    #\n    # Similarly we have to take into account the imprecision of 'w' when finding\n    # the closest representation of 'w'. If we have two potential\n    # representations, and one is closer to both w_low and w_high, then we know\n    # it is closer to the actual value v.\n    #\n    # By generating the digits of too_high we got the largest (closest to\n    # too_high) buffer that is still in the unsafe interval. In the case where\n    # w_high < buffer < too_high we try to decrement the buffer.\n    # This way the buffer approaches (rounds towards) w.\n    # There are 3 conditions that stop the decrementation process:\n    #   1) the buffer is already below w_high\n    #   2) decrementing the buffer would make it leave the unsafe interval\n    #   3) decrementing the buffer would yield a number below w_high and farther\n    #      away than the current number. In other words:\n    #              (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high\n    # Instead of using the buffer directly we use its distance to too_high.\n    # Conceptually rest ~= too_high - buffer\n    # We need to do the following tests in this order to avoid over- and\n    # underflows.\n    while (\n            rest < small_distance &&               # Negated condition 1\n            unsafe_interval - rest >= ten_kappa && # Negated condition 2\n            (rest + ten_kappa < small_distance ||  # buffer{-1} > w_high\n            small_distance - rest >= rest + ten_kappa - small_distance)\n          )\n      buffer[length - 1] -= 1\n      rest += ten_kappa\n    end\n\n    # We have approached w+ as much as possible. We now test if approaching w-\n    # would require changing the buffer. If yes, then we have two possible\n    # representations close to w, but we cannot decide which one is closer.\n    if (\n         rest < big_distance &&\n         unsafe_interval - rest >= ten_kappa &&\n         (rest + ten_kappa < big_distance || big_distance - rest > rest + ten_kappa - big_distance)\n       )\n      return false\n    end\n\n    # Weeding test.\n    #   The safe interval is [too_low + 2 ulp; too_high - 2 ulp]\n    #   Since too_low = too_high - unsafe_interval this is equivalent to\n    #      [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]\n    #   Conceptually we have: rest ~= too_high - buffer\n    (2 &* unit <= rest) && (rest <= unsafe_interval &- 4 &* unit)\n  end\n\n  # Generates the digits of input number *w*.\n  #\n  # *w* is a floating-point number (`DiyFp`), consisting of a significand and an\n  # exponent. Its exponent is bounded by `kMinimalTargetExponent` and\n  # `kMaximalTargetExponent`. Hence:\n  #     -60 <= w.e() <= -32\n  #\n  # Returns `false` if it fails, in which case the generated digits in the buffer\n  # should not be used.\n  #\n  # Preconditions:\n  #  * *low*, *w* and *high* are correct up to 1 ulp (unit in the last place).\n  #    That is, their error must be less than a unit of their last digits.\n  #  * `low.e() == w.e() == high.e()`\n  #  * `low < w < high`, and taking into account their error: `low~ <= high~`\n  #  * `kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent`\n  #\n  # Postconditions: returns `false` if procedure fails, otherwise:\n  # * buffer is not null-terminated, but len contains the number of digits.\n  # * buffer contains the shortest possible decimal digit-sequence\n  #   such that `LOW < buffer * 10^kappa < HIGH`, where LOW and HIGH are the\n  #   correct values of low and high (without their error).\n  # * if more than one decimal representation gives the minimal number of\n  #   decimal digits then the one closest to W (where W is the correct value\n  #   of w) is chosen.\n  #\n  # NOTE: This procedure takes into account the imprecision of its input\n  #   numbers. If the precision is not enough to guarantee all the postconditions\n  #   then `false` is returned. This usually happens rarely (~0.5%).\n  #\n  # Say, for the sake of example, that:\n  #     w.e() == -48 && w.f() == 0x1234567890abcdef\n  #\n  # w's value can be computed by `w.f() * 2^w.e()`\n  #\n  # We can obtain w's integral digits by simply shifting `w.f()` by `-w.e()`.\n  #\n  # * -> w's integral part is `0x1234`\n  # * w's fractional part is therefore `0x567890abcdef`.\n  #\n  # Printing w's integral part is easy (simply print `0x1234` in decimal).\n  # In order to print its fraction we repeatedly multiply the fraction by 10 and\n  # get each digit. Example the first digit after the point would be computed by\n  #     (0x567890abcdef * 10) >> 48. -> 3\n  #\n  # The whole thing becomes slightly more complicated because we want to stop\n  # once we have enough digits. That is, once the digits inside the buffer\n  # represent 'w' we can stop. Everything inside the interval low - high\n  # represents w. However we have to pay attention to low, high and w's\n  # imprecision.\n  def digit_gen(low : DiyFP, w : DiyFP, high : DiyFP, buffer_p) : {Bool, Int32, Int32}\n    buffer = buffer_p.to_slice(128)\n    # low, w and high are imprecise, but by less than one ulp (unit in the last\n    # place).\n    # If we remove (resp. add) 1 ulp from low (resp. high) we are certain that\n    # the new numbers are outside of the interval we want the final\n    # representation to lie in.\n    # Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield\n    # numbers that are certain to lie in the interval. We will use this fact\n    # later on.\n    # We will now start by generating the digits within the uncertain\n    # interval. Later we will weed out representations that lie outside the safe\n    # interval and thus _might_ lie outside the correct interval.\n    unit = 1_u64\n    too_low = DiyFP.new(low.frac - unit, low.exp)\n    too_high = DiyFP.new(high.frac + unit, low.exp)\n    # too_low and too_high are guaranteed to lie outside the interval we want the\n    # generated number in.\n    unsafe_interval = too_high - too_low\n    # We now cut the input number into two parts: the integral digits and the\n    # fractionals. We will not write any decimal separator though, but adapt\n    # kappa instead.\n    # Reminder: we are currently computing the digits (stored inside the buffer)\n    # such that:   too_low < buffer * 10^kappa < too_high\n    # We use too_high for the digit_generation and stop as soon as possible.\n    # If we stop early we effectively round down.\n    one = DiyFP.new(1_u64 << -w.exp, w.exp)\n    # Division by one is a shift.\n    integrals = (too_high.frac >> -one.exp).to_u32\n    # Modulo by one is an and.\n    fractionals = too_high.frac & (one.frac - 1)\n\n    # note: In the C++ version this was: SignificandSize - (-one.e())\n    divisor, kappa = CachedPowers.largest_pow10(integrals, DiyFP::SIGNIFICAND_SIZE + one.exp)\n    length = 0\n    # pp kappa\n    # pp divisor\n\n    # Loop invariant: buffer = too_high / 10^kappa  (integer division)\n    # The invariant holds for the first iteration: kappa has been initialized\n    # with the divisor exponent + 1. And the divisor is the biggest power of ten\n    # that is smaller than integrals.\n    while kappa > 0\n      digit = integrals // divisor\n      # pp [digit, kappa]\n      buffer[length] = 48_u8 + digit\n      length += 1\n      integrals %= divisor\n      kappa -= 1\n\n      # Note that kappa now equals the exponent of the divisor and that the\n      # invariant thus holds again.\n      rest = (integrals.to_u64 << -one.exp) + fractionals\n\n      # Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())\n      # Reminder: unsafe_interval.e() == one.e()\n      if rest < unsafe_interval.frac\n        # Rounding down (by not emitting the remaining digits) yields a number\n        # that lies within the unsafe interval.\n        weeded = round_weed(buffer_p, length, (too_high - w).frac, unsafe_interval.frac, rest, divisor.to_u64 << -one.exp, unit)\n        return weeded, kappa, length\n      end\n\n      divisor //= 10\n    end\n\n    # The integrals have been generated. We are at the point of the decimal\n    # separator. In the following loop we simply multiply the remaining digits by\n    # 10 and divide by one. We just need to pay attention to multiply associated\n    # data (like the interval or 'unit'), too.\n    # Note that the multiplication by 10 does not overflow, because w.e >= -60\n    # and thus one.e >= -60.\n    loop do\n      fractionals *= 10\n      unit *= 10\n      unsafe_interval = DiyFP.new(unsafe_interval.frac * 10, unsafe_interval.exp)\n      digit = (fractionals >> -one.exp).to_i\n      buffer[length] = 48_u8 + digit\n      length += 1\n      fractionals &= one.frac - 1\n      kappa -= 1\n      if fractionals < unsafe_interval.frac\n        weeded = round_weed(buffer_p, length, (too_high - w).frac * unit, unsafe_interval.frac, fractionals, one.frac, unit)\n        return weeded, kappa, length\n      end\n    end\n  end\n\n  # Provides a decimal representation of *v*.\n  #\n  # Returns a `Tuple` of `{status, decimal_exponent, length}`\n  # *status* will be `true` if it succeeds, otherwise the result cannot be\n  # trusted.\n  #\n  # There will be *length* digits inside the buffer (not null-terminated).\n  # If the function returns status as `true` then\n  #     v == (buffer * 10^decimal_exponent).to_f\n  #\n  # The digits in the buffer are the shortest representation possible:\n  # no `0.09999999999999999` instead of `0.1`. The shorter representation will\n  # even be chosen even if the longer one would be closer to *v*.\n  #\n  # The last digit will be closest to the actual *v*. That is, even if several\n  # digits might correctly yield *v* when read again, the closest will be\n  # computed.\n  def grisu3(v : Float64 | Float32, buffer_p) : {Bool, Int32, Int32}\n    w = DiyFP.from_f_normalized(v)\n\n    # boundary_minus and boundary_plus are the boundaries between v and its\n    # closest floating-point neighbors. Any number strictly between\n    # boundary_minus and boundary_plus will round to v when convert to a float.\n    # Grisu3 will never output representations that lie exactly on a boundary.\n    boundaries = IEEE.normalized_boundaries(v)\n\n    ten_mk, mk = CachedPowers.get_cached_power_for_binary_exponent(w.exp)\n\n    # Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a\n    # 64 bit significand and ten_mk is thus only precise up to 64 bits.\n    #\n    # The DiyFp::Times procedure rounds its result, and ten_mk is approximated\n    # too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now\n    # off by a small amount.\n    # In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.\n    # In other words: let f = scaled_w.f() and e = scaled_w.e(), then\n    #           (f-1) * 2^e < w*10^k < (f+1) * 2^e\n    scaled_w = w * ten_mk\n\n    # In theory it would be possible to avoid some recomputations by computing\n    # the difference between w and boundary_minus/plus (a power of 2) and to\n    # compute scaled_boundary_minus/plus by subtracting/adding from\n    # scaled_w. However the code becomes much less readable and the speed\n    # enhancements are not terrific.\n    scaled_boundary_minus = boundaries[:minus] * ten_mk\n    scaled_boundary_plus = boundaries[:plus] * ten_mk\n\n    # #digit_gen will generate the digits of scaled_w. Therefore we have\n    # v == (scaled_w * 10^-mk).to_f\n    # Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an\n    # integer than it will be updated. For instance if scaled_w == 1.23 then\n    # the buffer will be filled with \"123\" and the decimal_exponent will be\n    # decreased by 2.\n    result, kappa, length = digit_gen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, buffer_p)\n\n    decimal_exponent = -mk + kappa\n    return result, decimal_exponent, length\n  end\nend\n"
  },
  {
    "path": "src/float/printer/hexfloat.cr",
    "content": "module Float::Printer::Hexfloat(F, U)\n  # IEEE defines the following grammar:\n  #\n  # ```\n  # sign            = /[+−]/\n  # digit           = /[0123456789]/\n  # hexDigit        = /[0123456789abcdefABCDEF]/\n  # hexExpIndicator = /[Pp]/\n  # hexIndicator    = /0[Xx]/\n  # hexSignificand  = /#{hexDigit}*\\.#{hexDigit}+|#{hexDigit}+\\.|#{hexDigit}+/\n  # decExponent     = /#{hexExpIndicator}#{sign}?#{digit}+/\n  # hexSequence     = /#{sign}?#{hexIndicator}#{hexSignificand}#{decExponent}/\n  #                 = /[+−]?0[Xx](?:[0-9A-Fa-f]*\\.[0-9A-Fa-f]+|[0-9A-Fa-f]+\\.?)[Pp][+−]?[0-9]+/\n  # ```\n  def self.to_f(str : String, &)\n    ptr = str.to_unsafe\n    finish = ptr + str.bytesize\n\n    # TODO: this portion is probably common with other float parsers too\n    ptr, negative = parse_sign(ptr, finish)\n    str_bytes = Slice.new(ptr, finish - ptr)\n    if eq_case_insensitive?(str_bytes, \"inf\") || eq_case_insensitive?(str_bytes, \"infinity\")\n      return F::INFINITY * (negative ? -1 : 1)\n    elsif eq_case_insensitive?(str_bytes, \"nan\")\n      return F::NAN\n    end\n\n    check_ch '0'\n    check_ch 'x', 'X'\n\n    mantissa = U.zero\n    mantissa_max = ~(U::MAX << (F::MANT_DIGITS + 1))\n    trailing_nonzero = false\n    exp_shift = 0\n\n    has_int_digits = false\n    while true\n      ptr, digit = parse_hex(ptr, finish)\n      break unless digit\n      has_int_digits = true\n\n      if mantissa != 0\n        exp_shift += 4\n      elsif digit != 0\n        exp_shift = 8 - digit.leading_zeros_count\n      end\n\n      mix_digit\n    end\n\n    if ptr < finish && ptr.value === '.'\n      ptr += 1\n      has_frac_digits = false\n      while true\n        ptr, digit = parse_hex(ptr, finish)\n        break unless digit\n        has_frac_digits = true\n\n        if mantissa == 0\n          exp_shift -= 4\n          if digit != 0\n            exp_shift += 8 - digit.leading_zeros_count\n          end\n        end\n\n        mix_digit\n      end\n    end\n\n    return yield \"expected at least one digit\" unless has_int_digits || has_frac_digits\n\n    check_ch 'p', 'P'\n\n    ptr, exp_negative = parse_sign(ptr, finish)\n\n    exp_add = 0\n    has_exp_digits = false\n    while true\n      ptr, digit = parse_dec(ptr, finish)\n      break unless digit\n      has_exp_digits = true\n\n      return yield \"exponent overflow\" if exp_add > Int32::MAX // 10\n      exp_add &*= 10\n      return yield \"exponent overflow\" if exp_add > Int32::MAX &- digit\n      exp_add &+= digit\n    end\n\n    return yield \"empty exponent\" unless has_exp_digits\n    return yield \"trailing characters\" unless ptr == finish\n\n    return make_float(negative, 0, 0) if mantissa == 0\n\n    exp_shift += F::MAX_EXP - 2\n    if exp_negative\n      exp_shift -= exp_add\n    else\n      exp_shift += exp_add\n    end\n\n    if mantissa <= (mantissa_max >> 1)\n      mantissa <<= F::MANT_DIGITS - (sizeof(U) * 8 - mantissa.leading_zeros_count) + 1\n    end\n\n    if exp_shift <= 0\n      trailing_nonzero ||= mantissa & ~(U::MAX << (1 - exp_shift)) != 0\n      mantissa >>= 1 - exp_shift\n      round_up = (mantissa & 0b1) != 0 && ((mantissa & 0b10) != 0 || trailing_nonzero)\n      mantissa >>= 1\n      mantissa &+= 1 if round_up\n      exp_shift = mantissa > (mantissa_max >> 2) ? 1 : 0\n    elsif mantissa > (mantissa_max >> 1)\n      round_up = (mantissa & 0b1) != 0 && ((mantissa & 0b10) != 0 || trailing_nonzero)\n      mantissa >>= 1\n      mantissa &+= 1 if round_up\n      exp_shift += 1 if mantissa > (mantissa_max >> 1)\n    end\n\n    return make_float(negative, 0, F::MAX_EXP * 2 - 1) if exp_shift >= F::MAX_EXP * 2 - 1\n\n    make_float(negative, mantissa, exp_shift)\n  end\n\n  private def self.make_float(negative, mantissa, exponent) : F\n    u = negative ? U.new!(1) << (sizeof(U) * 8 - 1) : U.zero\n    u |= mantissa & ~(U::MAX << (F::MANT_DIGITS - 1))\n    u |= U.new!(exponent) << (F::MANT_DIGITS - 1)\n\n    u.unsafe_as(F)\n  end\n\n  private macro check_ch(*ch)\n    unless ptr < finish && ptr.value.unsafe_chr.in?({{ ch }})\n      return yield \"expected {{ ch.map(&.stringify).join(%( or )).id }}\"\n    end\n    ptr += 1\n  end\n\n  # `/[+-]?/`\n  private def self.parse_sign(ptr, finish)\n    if ptr < finish\n      case ptr.value\n      when '+'\n        return {ptr + 1, false}\n      when '-'\n        return {ptr + 1, true}\n      end\n    end\n\n    {ptr, false}\n  end\n\n  # `/[0-9]?/`\n  private def self.parse_dec(ptr, finish)\n    if ptr < finish\n      case ch = ptr.value\n      when 0x30..0x39\n        return {ptr + 1, ch &- 0x30}\n      end\n    end\n\n    {ptr, nil}\n  end\n\n  # `/[0-9A-Fa-f]?/`\n  private def self.parse_hex(ptr, finish)\n    if ptr < finish\n      case ch = ptr.value\n      when 0x30..0x39\n        return {ptr + 1, ch &- 0x30}\n      when 0x41..0x46\n        return {ptr + 1, ch &- 0x37}\n      when 0x61..0x66\n        return {ptr + 1, ch &- 0x57}\n      end\n    end\n\n    {ptr, nil}\n  end\n\n  # Precondition: `str.each_char.all?(&.ascii_lowercase?)`\n  private def self.eq_case_insensitive?(bytes : Bytes, str : String) : Bool\n    return false unless bytes.size == str.bytesize\n    ptr = str.to_unsafe\n\n    bytes.each_with_index do |b, i|\n      b |= 0x20 if 0x41 <= b <= 0x5A\n      return false unless b == ptr[i]\n    end\n\n    true\n  end\n\n  private macro mix_digit\n    if mantissa > (mantissa_max >> 1)\n      trailing_nonzero ||= digit != 0\n    elsif mantissa > (mantissa_max >> 2)\n      # 00000000 000[.... ........ .......]\n      mantissa <<= 1\n      mantissa |= digit >> 3\n      trailing_nonzero ||= digit & 0b0111 != 0\n      # 00000000 00[..... ........ ......]? ???\n    elsif mantissa > (mantissa_max >> 3)\n      # 00000000 0000[... ........ .......]\n      mantissa <<= 2\n      mantissa |= digit >> 2\n      trailing_nonzero ||= digit & 0b0011 != 0\n      # 00000000 00[..... ........ .....]?? ??\n    elsif mantissa > (mantissa_max >> 4)\n      # 00000000 00000[.. ........ .......]\n      mantissa <<= 3\n      mantissa |= digit >> 1\n      trailing_nonzero ||= digit & 0b0001 != 0\n      # 00000000 00[..... ........ ....]??? ?\n    else\n      mantissa <<= 4\n      mantissa |= digit\n    end\n  end\n\n  # sign and special values are handled in `Float::Printer.check_finite_float`\n  @[AlwaysInline]\n  def self.to_s(io : IO, num : F, *, prefix : Bool = true, upcase : Bool = false, precision : Int? = nil, alternative : Bool = false) : Nil\n    u = num.unsafe_as(U)\n    exponent = ((u >> (F::MANT_DIGITS - 1)) & (F::MAX_EXP * 2 - 1)).to_i\n    mantissa = u & ~(U::MAX << (F::MANT_DIGITS - 1))\n\n    if exponent < 1\n      exponent += 1\n    else\n      mantissa |= U.new!(1) << (F::MANT_DIGITS - 1)\n    end\n\n    if precision\n      trailing_zeros = {(precision * 4 + 1 - F::MANT_DIGITS) // 4, 0}.max\n      precision -= trailing_zeros\n\n      one_bit = mantissa.bits_set?(U.new!(1) << (F::MANT_DIGITS - 4 * precision - 1))\n      half_bit = mantissa.bits_set?(U.new!(1) << (F::MANT_DIGITS - 4 * precision - 2))\n      trailing_nonzero = (mantissa & ~(U::MAX << (F::MANT_DIGITS - 4 * precision - 2))) != 0\n      if half_bit && (one_bit || trailing_nonzero)\n        mantissa &+= U.new!(1) << (F::MANT_DIGITS - 4 * precision - 2)\n      end\n    else\n      trailing_zeros = 0\n    end\n\n    io << (upcase ? \"0X\" : \"0x\") if prefix\n    io << (mantissa >> (F::MANT_DIGITS - 1))\n    mantissa &= ~(U::MAX << (F::MANT_DIGITS - 1))\n\n    io << '.' if (precision || mantissa) != 0 || alternative\n\n    if precision\n      precision.times do\n        digit = mantissa >> (F::MANT_DIGITS - 5)\n        digit.to_s(io, base: 16, upcase: upcase)\n        mantissa <<= 4\n        mantissa &= ~(U::MAX << (F::MANT_DIGITS - 1))\n      end\n    else\n      while mantissa != 0\n        digit = mantissa >> (F::MANT_DIGITS - 5)\n        digit.to_s(io, base: 16, upcase: upcase)\n        mantissa <<= 4\n        mantissa &= ~(U::MAX << (F::MANT_DIGITS - 1))\n      end\n    end\n\n    trailing_zeros.times { io << '0' }\n\n    if num == 0\n      io << (upcase ? \"P+0\" : \"p+0\")\n    else\n      exponent -= F::MAX_EXP - 1\n\n      io << (upcase ? 'P' : 'p')\n      io << (exponent >= 0 ? '+' : '-')\n      io << exponent.abs\n    end\n  end\n\n  def self.to_s_size(num : F, *, precision : Int? = nil, alternative : Bool = false)\n    u = num.unsafe_as(U)\n    exponent = ((u >> (F::MANT_DIGITS - 1)) & (F::MAX_EXP * 2 - 1)).to_i\n    mantissa = u & ~(U::MAX << (F::MANT_DIGITS - 1))\n\n    if exponent < 1\n      exponent += 1\n    end\n\n    size = 6 # 0x0p+0 (integral part cannot be greater than 2)\n\n    if precision\n      size += 1 if precision != 0 || alternative # .\n      size += precision\n    else\n      size += 1 if mantissa != 0 || alternative # .\n      while mantissa != 0\n        size += 1\n        mantissa <<= 4\n        mantissa &= ~(U::MAX << (F::MANT_DIGITS - 1))\n      end\n    end\n\n    if num != 0\n      exponent = (exponent - F::MAX_EXP - 1).abs\n      while exponent >= 10\n        exponent //= 10\n        size += 1\n      end\n    end\n\n    size\n  end\nend\n"
  },
  {
    "path": "src/float/printer/ieee.cr",
    "content": "# IEEE is ported from the C++ \"double-conversions\" library.\n#\n# The following is their license:\n#   Copyright 2012 the V8 project authors. All rights reserved.\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions are\n#   met:\n#\n#       * Redistributions of source code must retain the above copyright\n#         notice, this list of conditions and the following disclaimer.\n#       * Redistributions in binary form must reproduce the above\n#         copyright notice, this list of conditions and the following\n#         disclaimer in the documentation and/or other materials provided\n#         with the distribution.\n#       * Neither the name of Google Inc. nor the names of its\n#         contributors may be used to endorse or promote products derived\n#         from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n@[Deprecated]\nmodule Float::Printer::IEEE\n  extend self\n\n  EXPONENT_MASK_64    = 0x7FF0000000000000_u64\n  SIGNIFICAND_MASK_64 = 0x000FFFFFFFFFFFFF_u64\n  HIDDEN_BIT_64       = 0x0010000000000000_u64\n  # Excludes the hidden bit\n  PHYSICAL_SIGNIFICAND_SIZE_64 = 52\n  SIGNIFICAND_SIZE_64          = 53\n  EXPONENT_BIAS_64             = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE_64\n  DENORMAL_EXPONENT_64         = -EXPONENT_BIAS_64 + 1\n  SIGN_MASK_64                 = 0x8000000000000000_u64\n  EXPONENT_MASK_32             =         0x7F800000_u32\n  SIGNIFICAND_MASK_32          =         0x007FFFFF_u32\n  HIDDEN_BIT_32                =         0x00800000_u32\n  # Excludes the hidden bit\n  PHYSICAL_SIGNIFICAND_SIZE_32 = 23\n  SIGNIFICAND_SIZE_32          = 24\n  EXPONENT_BIAS_32             = 0x7F + PHYSICAL_SIGNIFICAND_SIZE_32\n  DENORMAL_EXPONENT_32         = -EXPONENT_BIAS_32 + 1\n  SIGN_MASK_32                 = 0x80000000_u32\n\n  def to_uint(v : Float64) : UInt64\n    v.unsafe_as(UInt64)\n  end\n\n  def to_uint(v : Float32) : UInt32\n    v.unsafe_as(UInt32)\n  end\n\n  def sign(d64 : UInt64) : Int32\n    (d64 & SIGN_MASK_64) == 0 ? 1 : -1\n  end\n\n  def sign(d32 : UInt32) : Int32\n    (d32 & SIGN_MASK_32) == 0 ? 1 : -1\n  end\n\n  def special?(d64 : UInt64) : Bool\n    (d64 & EXPONENT_MASK_64) == EXPONENT_MASK_64\n  end\n\n  def special?(d32 : UInt32) : Bool\n    (d32 & EXPONENT_MASK_32) == EXPONENT_MASK_32\n  end\n\n  def inf?(d64 : UInt64) : Bool\n    special?(d64) && (d64 & SIGNIFICAND_MASK_64 == 0)\n  end\n\n  def inf?(d32 : UInt32) : Bool\n    special?(d32) && (d32 & SIGNIFICAND_MASK_32 == 0)\n  end\n\n  def nan?(d64 : UInt64)\n    special?(d64) && (d64 & SIGNIFICAND_MASK_64 != 0)\n  end\n\n  def nan?(d32 : UInt32)\n    special?(d32) && (d32 & SIGNIFICAND_MASK_32 != 0)\n  end\n\n  # Computes the two boundaries of *v*.\n  #\n  # The bigger boundary (*m_plus*) is normalized. The lower boundary has the same\n  # exponent as *m_plus*.\n  #\n  # Precondition: the value encoded by this `Float` must be greater than 0.\n  def normalized_boundaries(v : Float64) : {minus: DiyFP, plus: DiyFP}\n    w = DiyFP.from_f(v)\n    m_plus = DiyFP.new((w.frac << 1) + 1, w.exp - 1).normalize\n\n    d64 = to_uint(v)\n\n    # The boundary is closer if the significand is of the form f == 2^p-1 then\n    # the lower boundary is closer.\n    # Think of v = 1000e10 and v- = 9999e9.\n    # Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but\n    # at a distance of 1e8.\n    # The only exception is for the smallest normal: the largest denormal is\n    # at the same distance as its successor.\n    # Note: denormals have the same exponent as the smallest normals.\n    physical_significand_is_zero = (d64 & SIGNIFICAND_MASK_64) == 0\n\n    lower_bound_closer = physical_significand_is_zero && (exponent(d64) != DENORMAL_EXPONENT_64)\n    f, e = if lower_bound_closer\n             {(w.frac << 2) - 1, w.exp - 2}\n           else\n             {(w.frac << 1) - 1, w.exp - 1}\n           end\n    m_minus = DiyFP.new(f << (e - m_plus.exp), m_plus.exp)\n    {minus: m_minus, plus: m_plus}\n  end\n\n  def normalized_boundaries(v : Float32) : {minus: DiyFP, plus: DiyFP}\n    w = DiyFP.from_f(v)\n    m_plus = DiyFP.new((w.frac << 1) + 1, w.exp - 1).normalize\n\n    d32 = to_uint(v)\n\n    physical_significand_is_zero = (d32 & SIGNIFICAND_MASK_32) == 0\n\n    lower_bound_closer = physical_significand_is_zero && (exponent(d32) != DENORMAL_EXPONENT_32)\n    f, e = if lower_bound_closer\n             {(w.frac << 2) - 1, w.exp - 2}\n           else\n             {(w.frac << 1) - 1, w.exp - 1}\n           end\n    m_minus = DiyFP.new(f << (e - m_plus.exp), m_plus.exp)\n    {minus: m_minus, plus: m_plus}\n  end\n\n  def frac_and_exp(v : Float64) : {UInt64, Int32}\n    d64 = to_uint(v)\n\n    if (d64 & EXPONENT_MASK_64) == 0 # denormal float\n      frac = d64 & SIGNIFICAND_MASK_64\n      exp = 1 - EXPONENT_BIAS_64\n    else\n      frac = (d64 & SIGNIFICAND_MASK_64) + HIDDEN_BIT_64\n      exp = (((d64 & EXPONENT_MASK_64) >> PHYSICAL_SIGNIFICAND_SIZE_64) &- EXPONENT_BIAS_64).to_i!\n    end\n\n    {frac, exp}\n  end\n\n  def frac_and_exp(v : Float32) : {UInt64, Int32}\n    d32 = to_uint(v)\n\n    if (d32 & EXPONENT_MASK_32) == 0 # denormal float\n      frac = d32 & SIGNIFICAND_MASK_32\n      exp = 1 - EXPONENT_BIAS_32\n    else\n      frac = (d32 & SIGNIFICAND_MASK_32) + HIDDEN_BIT_32\n      exp = (((d32 & EXPONENT_MASK_32) >> PHYSICAL_SIGNIFICAND_SIZE_32) &- EXPONENT_BIAS_32).to_i!\n    end\n\n    {frac.to_u64, exp}\n  end\n\n  private def denormal?(d64 : UInt64) : Bool\n    (d64 & EXPONENT_MASK_64) == 0\n  end\n\n  private def denormal?(d32 : UInt32) : Bool\n    (d32 & EXPONENT_MASK_32) == 0\n  end\n\n  private def exponent(d64 : UInt64)\n    return DENORMAL_EXPONENT_64 if denormal?(d64)\n    biased_e = ((d64 & EXPONENT_MASK_64) >> PHYSICAL_SIGNIFICAND_SIZE_64).to_i\n    biased_e - EXPONENT_BIAS_64\n  end\n\n  private def exponent(d32 : UInt32)\n    return DENORMAL_EXPONENT_32 if denormal?(d32)\n    biased_e = ((d32 & EXPONENT_MASK_32) >> PHYSICAL_SIGNIFICAND_SIZE_32).to_i\n    biased_e - EXPONENT_BIAS_32\n  end\nend\n"
  },
  {
    "path": "src/float/printer/ryu_printf.cr",
    "content": "{% skip_file unless String::Formatter::HAS_RYU_PRINTF %}\n\nrequire \"./ryu_printf_table\"\n\n# Source port of Ryu Printf's reference implementation in C.\n#\n# The following is their license:\n#\n#   Copyright 2018 Ulf Adams\n#\n#   The contents of this file may be used under the terms of the Apache License,\n#   Version 2.0.\n#\n#      (See accompanying file LICENSE-Apache or copy at\n#       http://www.apache.org/licenses/LICENSE-2.0)\n#\n#   Alternatively, the contents of this file may be used under the terms of\n#   the Boost Software License, Version 1.0.\n#      (See accompanying file LICENSE-Boost or copy at\n#       https://www.boost.org/LICENSE_1_0.txt)\n#\n#   Unless required by applicable law or agreed to in writing, this software\n#   is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n#   KIND, either express or implied.\nmodule Float::Printer::RyuPrintf\n  # Current revision: https://github.com/ulfjack/ryu/tree/75d5a85440ed356ad7b23e9e6002d71f62a6255c\n\n  # Returns the number of decimal digits in v, which must not contain more than 9 digits.\n  private def self.decimal_length9(v : UInt32) : UInt32\n    # Function precondition: v is not a 10-digit number.\n    # (f2s: 9 digits are sufficient for round-tripping.)\n    # (d2fixed: We print 9-digit blocks.)\n    case v\n    when .>=(100000000) then 9_u32\n    when .>=(10000000)  then 8_u32\n    when .>=(1000000)   then 7_u32\n    when .>=(100000)    then 6_u32\n    when .>=(10000)     then 5_u32\n    when .>=(1000)      then 4_u32\n    when .>=(100)       then 3_u32\n    when .>=(10)        then 2_u32\n    else\n      1_u32\n    end\n  end\n\n  private def self.log10_pow2(e : Int32) : UInt32\n    # The first value this approximation fails for is 2^1651 which is just greater than 10^297.\n    (e.to_u32! &* 78913) >> 18\n  end\n\n  M_INV_5 = 14757395258967641293u64 # 5 * m_inv_5 = 1 (mod 2^64)\n  N_DIV_5 =  3689348814741910323u64 # #{ n | n = 0 (mod 2^64) } = 2^64 / 5\n\n  private def self.pow5_factor(value : UInt64) : UInt32\n    count = 0_u32\n    while true\n      value &*= M_INV_5\n      break if value > N_DIV_5\n      count &+= 1\n    end\n    count\n  end\n\n  # Returns true if value is divisible by 5^p.\n  private def self.multiple_of_power_of_5?(value : UInt64, p : UInt32)\n    pow5_factor(value) >= p\n  end\n\n  # Returns true if value is divisible by 2^p.\n  private def self.multiple_of_power_of_2?(value : UInt64, p : UInt32)\n    value & ~(UInt64::MAX << p) == 0\n  end\n\n  private def self.umul128(a : UInt64, b : UInt64) : {UInt64, UInt64}\n    a_lo = a.to_u32!.to_u64!\n    a_hi = (a >> 32).to_u32!.to_u64!\n    b_lo = b.to_u32!\n    b_hi = (b >> 32).to_u32!\n\n    b00 = a_lo &* b_lo\n    b01 = a_lo &* b_hi\n    b10 = a_hi &* b_lo\n    b11 = a_hi &* b_hi\n\n    b00_lo = b00.to_u32!\n    b00_hi = (b00 >> 32).to_u32!\n\n    mid1 = b10 &+ b00_hi\n    mid1_lo = mid1.to_u32!\n    mid1_hi = (mid1 >> 32).to_u32!\n\n    mid2 = b01 &+ mid1_lo\n    mid2_lo = mid2.to_u32!\n    mid2_hi = (mid2 >> 32).to_u32!\n\n    {b11 &+ mid1_hi &+ mid2_hi, (mid2_lo.to_u64! << 32) | b00_lo}\n  end\n\n  private def self.shiftright128(lo : UInt64, hi : UInt64, dist : UInt32) : UInt64\n    # We don't need to handle the case dist >= 64 here\n    (hi << (64 &- dist)) | (lo >> dist)\n  end\n\n  # Returns the low 64 bits of the high 128 bits of the 256-bit product of a and b.\n  private def self.umul256_hi128_lo64(a_hi : UInt64, a_lo : UInt64, b_hi : UInt64, b_lo : UInt64)\n    b00_hi, _ = umul128(a_lo, b_lo)\n    b01_hi, b01_lo = umul128(a_lo, b_hi)\n    b10_hi, b10_lo = umul128(a_hi, b_lo)\n    _, b11_lo = umul128(a_hi, b_hi)\n\n    temp1_lo = b10_lo &+ b00_hi\n    temp1_hi = b10_hi &+ (temp1_lo < b10_lo ? 1 : 0)\n    temp2_lo = b01_lo &+ temp1_lo\n    temp2_hi = b01_hi &+ (temp2_lo < b01_lo ? 1 : 0)\n    b11_lo &+ temp1_hi &+ temp2_hi\n  end\n\n  private def self.uint128_mod1e9(v_hi : UInt64, v_lo : UInt64) : UInt32\n    # After multiplying, we're going to shift right by 29, then truncate to uint32_t.\n    # This means that we need only 29 + 32 = 61 bits, so we can truncate to uint64_t before shifting.\n    multiplied = umul256_hi128_lo64(v_hi, v_lo, 0x89705F4136B4A597u64, 0x31680A88F8953031u64)\n\n    # For uint32_t truncation, see the mod1e9() comment in d2s_intrinsics.h.\n    shifted = (multiplied >> 29).to_u32!\n\n    v_lo.to_u32! &- 1000000000_u32 &* shifted\n  end\n\n  private def self.mulshift_mod1e9(m : UInt64, mul : {UInt64, UInt64, UInt64}, j : Int32) : UInt32\n    high0, _ = umul128(m, mul[0])\n    high1, low1 = umul128(m, mul[1])\n    high2, low2 = umul128(m, mul[2])\n\n    s0high = low1 &+ high0 # 64\n    c1 = s0high < low1 ? 1 : 0\n    s1low = low2 &+ high1 &+ c1 # 128\n    c2 = s1low < low2 ? 1 : 0   # high1 + c1 can't overflow, so compare against low2\n    s1high = high2 &+ c2        # 192\n    dist = (j &- 128).to_u32!   # dist: [0, 52]\n    shiftedhigh = s1high >> dist\n    shiftedlow = shiftright128(s1low, s1high, dist)\n    uint128_mod1e9(shiftedhigh, shiftedlow)\n  end\n\n  private def self.index_for_exponent(e : UInt32)\n    (e &+ 15) // 16\n  end\n\n  private ADDITIONAL_BITS_2 = 120\n\n  private def self.pow10_bits_for_index(idx : UInt32)\n    idx &* 16 &+ ADDITIONAL_BITS_2\n  end\n\n  private def self.length_for_index(idx : UInt32)\n    # +1 for ceil, +16 for mantissa, +8 to round up when dividing by 9\n    (log10_pow2(16 &* idx.to_i32!) &+ 25) // 9\n  end\n\n  # Convert `digits` to a sequence of decimal digits. Append the digits to the result.\n  # The caller has to guarantee that:\n  #   10^(olength-1) <= digits < 10^olength\n  # e.g., by passing `olength` as `decimalLength9(digits)`.\n  private def self.append_n_digits(olength : UInt32, digits : UInt32, result : UInt8*)\n    i = 0_u32\n    while digits >= 10000\n      c = digits &- 10000 &* (digits // 10000)\n      digits //= 10000\n      c0 = (c % 100) << 1\n      c1 = (c // 100) << 1\n      (DIGIT_TABLE + c0).copy_to(result + olength - i - 2, 2)\n      (DIGIT_TABLE + c1).copy_to(result + olength - i - 4, 2)\n      i &+= 4\n    end\n    if digits >= 100\n      c = (digits % 100) << 1\n      digits //= 100\n      (DIGIT_TABLE + c).copy_to(result + olength - i - 2, 2)\n      i &+= 2\n    end\n    if digits >= 10\n      c = digits << 1\n      (DIGIT_TABLE + c).copy_to(result + olength - i - 2, 2)\n    else\n      result.value = '0'.ord.to_u8! &+ digits\n    end\n  end\n\n  # Convert `digits` to a sequence of decimal digits. Print the first digit, followed by a decimal\n  # dot '.' followed by the remaining digits. The caller has to guarantee that:\n  #   10^(olength-1) <= digits < 10^olength\n  # e.g., by passing `olength` as `decimalLength9(digits)`.\n  private def self.append_d_digits(olength : UInt32, digits : UInt32, result : UInt8*)\n    i = 0_u32\n    while digits >= 10000\n      c = digits &- 10000 &* (digits // 10000)\n      digits //= 10000\n      c0 = (c % 100) << 1\n      c1 = (c // 100) << 1\n      (DIGIT_TABLE + c0).copy_to(result + olength + 1 - i - 2, 2)\n      (DIGIT_TABLE + c1).copy_to(result + olength + 1 - i - 4, 2)\n      i &+= 4\n    end\n    if digits >= 100\n      c = (digits % 100) << 1\n      digits //= 100\n      (DIGIT_TABLE + c).copy_to(result + olength + 1 - i - 2, 2)\n      i &+= 2\n    end\n    if digits >= 10\n      c = digits << 1\n      result[2] = DIGIT_TABLE[c &+ 1]\n      result[1] = '.'.ord.to_u8!\n      result[0] = DIGIT_TABLE[c]\n    else\n      result[1] = '.'.ord.to_u8!\n      result[0] = '0'.ord.to_u8! &+ digits\n    end\n  end\n\n  # Convert `digits` to decimal and write the last `count` decimal digits to result.\n  # If `digits` contains additional digits, then those are silently ignored.\n  private def self.append_c_digits(count : UInt32, digits : UInt32, result : UInt8*)\n    i = 0_u32\n\n    # Copy pairs of digits from DIGIT_TABLE.\n    while i < count &- 1\n      c = (digits % 100) << 1\n      digits //= 100\n      (DIGIT_TABLE + c).copy_to(result + count - i - 2, 2)\n      i &+= 2\n    end\n\n    # Generate the last digit if count is odd.\n    if i < count\n      c = '0'.ord.to_u8! &+ (digits % 10)\n      result[count - i - 1] = c\n    end\n  end\n\n  # Convert `digits` to decimal and write the last 9 decimal digits to result.\n  # If `digits` contains additional digits, then those are silently ignored.\n  private def self.append_nine_digits(digits : UInt32, result : UInt8*)\n    if digits == 0\n      Slice.new(result, 9).fill('0'.ord.to_u8!)\n      return\n    end\n\n    c = digits &- 10000 &* (digits // 10000)\n    digits //= 10000\n    c0 = (c % 100) << 1\n    c1 = (c // 100) << 1\n    (DIGIT_TABLE + c0).copy_to(result + 7, 2)\n    (DIGIT_TABLE + c1).copy_to(result + 5, 2)\n\n    c = digits &- 10000 &* (digits // 10000)\n    digits //= 10000\n    c0 = (c % 100) << 1\n    c1 = (c // 100) << 1\n    (DIGIT_TABLE + c0).copy_to(result + 3, 2)\n    (DIGIT_TABLE + c1).copy_to(result + 1, 2)\n\n    result.value = '0'.ord.to_u8! &+ digits\n  end\n\n  MANTISSA_BITS = Float64::MANT_DIGITS - 1\n  EXPONENT_BITS = 11\n\n  # NOTE: in Crystal *d* must be positive and finite\n  private def self.extract_float(d : Float64)\n    bits = d.unsafe_as(UInt64)\n\n    ieee_mantissa = bits & ~(UInt64::MAX << MANTISSA_BITS)\n    ieee_exponent = (bits >> MANTISSA_BITS) & ~(UInt64::MAX << EXPONENT_BITS)\n\n    if ieee_exponent == 0\n      e2 = 1 &- 1023 &- MANTISSA_BITS\n      m2 = ieee_mantissa\n    else\n      e2 = ieee_exponent.to_i32! &- 1023 &- MANTISSA_BITS\n      m2 = (1_u64 << MANTISSA_BITS) | ieee_mantissa\n    end\n\n    {e2, m2}\n  end\n\n  def self.d2fixed_buffered_n(d : Float64, precision : UInt32, result : UInt8*)\n    e2, m2 = extract_float(d)\n    index = 0\n    nonzero = false\n\n    if e2 >= -MANTISSA_BITS\n      idx = e2 < 0 ? 0_u32 : index_for_exponent(e2.to_u32!)\n      p10bits = pow10_bits_for_index(idx)\n      len = length_for_index(idx).to_i32!\n\n      (len - 1).downto(0) do |i|\n        j = p10bits &- e2\n        # Temporary: j is usually around 128, and by shifting a bit, we push it to 128 or above, which is\n        # a slightly faster code path in mulshift_mod1e9. Instead, we can just increase the multipliers.\n        digits = mulshift_mod1e9(m2 << 8, POW10_SPLIT[POW10_OFFSET[idx] &+ i], (j &+ 8).to_i32!)\n        if nonzero\n          append_nine_digits(digits, result + index)\n          index &+= 9\n        elsif digits != 0\n          olength = decimal_length9(digits)\n          append_n_digits(olength, digits, result + index)\n          index &+= olength\n          nonzero = true\n        end\n      end\n    end\n\n    unless nonzero\n      result[index] = '0'.ord.to_u8!\n      index &+= 1\n    end\n\n    if precision > 0\n      result[index] = '.'.ord.to_u8!\n      index &+= 1\n    end\n\n    if e2 >= 0\n      Slice.new(result + index, precision).fill('0'.ord.to_u8!)\n      index &+= precision\n      return index\n    end\n\n    idx = (0 &- e2) // 16\n    blocks = precision // 9 &+ 1\n    # 0 = don't round up; 1 = round up unconditionally; 2 = round up if odd.\n    round_up = 0\n    i = 0_u32\n\n    if blocks <= MIN_BLOCK_2[idx]\n      i = blocks\n      Slice.new(result + index, precision).fill('0'.ord.to_u8!)\n      index &+= precision\n    elsif i < MIN_BLOCK_2[idx]\n      i = MIN_BLOCK_2[idx].to_u32!\n      Slice.new(result + index, i &* 9).fill('0'.ord.to_u8!)\n      index &+= i &* 9\n    end\n\n    while i < blocks\n      j = ADDITIONAL_BITS_2 &- e2 &- 16 &* idx\n      p = POW10_OFFSET_2[idx] &+ i &- MIN_BLOCK_2[idx]\n\n      if p >= POW10_OFFSET_2[idx + 1]\n        # If the remaining digits are all 0, then we might as well use memset.\n        # No rounding required in this case.\n        fill = precision &- 9 &* i\n        Slice.new(result + index, fill).fill('0'.ord.to_u8!)\n        index &+= fill\n        break\n      end\n\n      # Temporary: j is usually around 128, and by shifting a bit, we push it to 128 or above, which is\n      # a slightly faster code path in mulShift_mod1e9. Instead, we can just increase the multipliers.\n      digits = mulshift_mod1e9(m2 << 8, POW10_SPLIT_2[p], j &+ 8)\n      if i < blocks &- 1\n        append_nine_digits(digits, result + index)\n        index &+= 9\n      else\n        maximum = precision &- 9 &* i\n        last_digit = 0_u32\n        (9 &- maximum).times do\n          last_digit = digits % 10\n          digits //= 10\n        end\n\n        if last_digit != 5\n          round_up = last_digit > 5 ? 1 : 0\n        else\n          # Is m * 10^(additionalDigits + 1) / 2^(-e2) integer?\n          required_twos = 0 &- e2 &- precision.to_i32! &- 1\n          trailing_zeros = required_twos <= 0 || (required_twos < 60 && multiple_of_power_of_2?(m2, required_twos.to_u32!))\n          round_up = trailing_zeros ? 2 : 1\n        end\n\n        if maximum > 0\n          append_c_digits(maximum, digits, result + index)\n          index &+= maximum\n        end\n        break\n      end\n\n      i &+= 1\n    end\n\n    if round_up != 0\n      round_index = index\n      dot_index = 0\n      while true\n        round_index &-= 1\n        c = result[round_index]\n        if round_index == -1 || c === '-'\n          result[round_index &+ 1] = '1'.ord.to_u8!\n          if dot_index > 0\n            result[dot_index] = '0'.ord.to_u8!\n            result[dot_index &+ 1] = '.'.ord.to_u8!\n          end\n          result[index] = '0'.ord.to_u8!\n          index &+= 1\n          break\n        end\n\n        if c === '.'\n          dot_index = round_index\n          next\n        elsif c === '9'\n          result[round_index] = '0'.ord.to_u8!\n          round_up = 1\n          next\n        else\n          result[round_index] = c &+ 1 unless round_up == 2 && c % 2 == 0\n          break\n        end\n      end\n    end\n\n    index\n  end\n\n  def self.d2exp_buffered_n(d : Float64, precision : UInt32, result : UInt8*)\n    if d == 0\n      result[0] = '0'.ord.to_u8!\n      index = 1\n      if precision > 0\n        result[index] = '.'.ord.to_u8!\n        index &+= 1\n        Slice.new(result + index, precision).fill('0'.ord.to_u8!)\n        index &+= precision\n      end\n      result[index] = 'e'.ord.to_u8!\n      result[index + 1] = '+'.ord.to_u8!\n      result[index + 2] = '0'.ord.to_u8!\n      return index &+ 3\n    end\n\n    e2, m2 = extract_float(d)\n    print_decimal_digit = precision > 0\n    precision &+= 1\n    index = 0\n    digits = 0_u32\n    printed_digits = 0_u32\n    available_digits = 0_u32\n    exp = 0\n\n    if e2 >= -MANTISSA_BITS\n      idx = e2 < 0 ? 0_u32 : index_for_exponent(e2.to_u32!)\n      p10bits = pow10_bits_for_index(idx)\n      len = length_for_index(idx).to_i32!\n\n      (len - 1).downto(0) do |i|\n        j = p10bits &- e2\n        # Temporary: j is usually around 128, and by shifting a bit, we push it to 128 or above, which is\n        # a slightly faster code path in mulshift_mod1e9. Instead, we can just increase the multipliers.\n        digits = mulshift_mod1e9(m2 << 8, POW10_SPLIT[POW10_OFFSET[idx] &+ i], (j &+ 8).to_i32!)\n        if printed_digits != 0\n          if printed_digits &+ 9 > precision\n            available_digits = 9_u32\n            break\n          end\n          append_nine_digits(digits, result + index)\n          index &+= 9\n          printed_digits &+= 9\n        elsif digits != 0\n          available_digits = decimal_length9(digits)\n          exp = i &* 9 &+ available_digits.to_i32! &- 1\n          break if available_digits > precision\n          if print_decimal_digit\n            append_d_digits(available_digits, digits, result + index)\n            index &+= available_digits &+ 1 # +1 for decimal point\n          else\n            result[index] = '0'.ord.to_u8! &+ digits\n            index &+= 1\n          end\n          printed_digits = available_digits\n          available_digits = 0_u32\n        end\n      end\n    end\n\n    if e2 < 0 && available_digits == 0\n      idx = (0 &- e2) // 16\n      (MIN_BLOCK_2[idx]..200).each do |i|\n        j = ADDITIONAL_BITS_2 &- e2 &- 16 &* idx\n        p = POW10_OFFSET_2[idx] &+ i &- MIN_BLOCK_2[idx]\n        # Temporary: j is usually around 128, and by shifting a bit, we push it to 128 or above, which is\n        # a slightly faster code path in mulShift_mod1e9. Instead, we can just increase the multipliers.\n        digits = p >= POW10_OFFSET_2[idx &+ 1] ? 0_u32 : mulshift_mod1e9(m2 << 8, POW10_SPLIT_2[p], j &+ 8)\n\n        if printed_digits != 0\n          if printed_digits &+ 9 > precision\n            available_digits = 9_u32\n            break\n          end\n          append_nine_digits(digits, result + index)\n          index &+= 9\n          printed_digits &+= 9\n        elsif digits != 0\n          available_digits = decimal_length9(digits)\n          exp = (-1 &- i) &* 9 &+ available_digits.to_i32! &- 1\n          break if available_digits > precision\n          if print_decimal_digit\n            append_d_digits(available_digits, digits, result + index)\n            index &+= available_digits &+ 1 # +1 for decimal point\n          else\n            result[index] = '0'.ord.to_u8! &+ digits\n            index &+= 1\n          end\n          printed_digits = available_digits\n          available_digits = 0_u32\n        end\n      end\n    end\n\n    maximum = precision &- printed_digits\n    digits = 0_u32 if available_digits == 0\n    last_digit = 0_u32\n    if available_digits > maximum\n      (available_digits &- maximum).times do\n        last_digit = digits % 10\n        digits //= 10\n      end\n    end\n\n    # 0 = don't round up; 1 = round up unconditionally; 2 = round up if odd.\n    round_up = 0\n    if last_digit != 5\n      round_up = last_digit > 5 ? 1 : 0\n    else\n      # Is m * 2^e2 * 10^(precision + 1 - exp) integer?\n      # precision was already increased by 1, so we don't need to write + 1 here.\n      rexp = precision.to_i32! &- exp\n      required_twos = 0 &- e2 &- rexp\n      trailing_zeros = required_twos <= 0 || (required_twos < 60 && multiple_of_power_of_2?(m2, required_twos.to_u32!))\n      if rexp < 0\n        required_fives = 0 &- rexp\n        trailing_zeros = trailing_zeros && multiple_of_power_of_5?(m2, required_fives.to_u32!)\n      end\n      round_up = trailing_zeros ? 2 : 1\n    end\n\n    if printed_digits != 0\n      if digits == 0\n        Slice.new(result + index, maximum).fill('0'.ord.to_u8!)\n      else\n        append_c_digits(maximum, digits, result + index)\n      end\n      index &+= maximum\n    else\n      if print_decimal_digit\n        append_d_digits(maximum, digits, result + index)\n        index &+= maximum &+ 1 # +1 for decimal point\n      else\n        result[index] = '0'.ord.to_u8! &+ digits\n        index &+= 1\n      end\n    end\n\n    if round_up != 0\n      round_index = index\n      while true\n        round_index &-= 1\n        c = result[round_index]\n        if round_index == -1 || c === '-'\n          result[round_index &+ 1] = '1'.ord.to_u8!\n          exp &+= 1\n          break\n        end\n\n        if c === '.'\n          next\n        elsif c === '9'\n          result[round_index] = '0'.ord.to_u8!\n          round_up = 1\n          next\n        else\n          result[round_index] = c &+ 1 unless round_up == 2 && c % 2 == 0\n          break\n        end\n      end\n    end\n\n    result[index] = 'e'.ord.to_u8!\n    index &+= 1\n    if exp < 0\n      result[index] = '-'.ord.to_u8!\n      exp = 0 &- exp\n    else\n      result[index] = '+'.ord.to_u8!\n    end\n    index &+= 1\n\n    if exp >= 100\n      c = exp % 10\n      (DIGIT_TABLE + ((exp // 10) << 1)).copy_to(result + index, 2)\n      result[index &+ 2] = '0'.ord.to_u8! &+ c\n      index &+= 3\n    elsif exp >= 10\n      (DIGIT_TABLE + (exp << 1)).copy_to(result + index, 2)\n      index &+= 2\n    else\n      result[index] = '0'.ord.to_u8! &+ exp\n      index &+= 1\n    end\n\n    index\n  end\n\n  private MAX_FIXED_PRECISION =  66_u32\n  private MAX_SCI_PRECISION   = 766_u32\n\n  # Source port of Microsoft STL's `std::to_chars` based on:\n  #\n  # * https://github.com/ulfjack/ryu/pull/185/files\n  # * https://github.com/microsoft/STL/blob/a8888806c6960f1687590ffd4244794c753aa819/stl/inc/charconv#L2324\n  # * https://github.com/llvm/llvm-project/blob/701f64790520790f75b1f948a752472d421ddaa3/libcxx/src/include/to_chars_floating_point.h#L836\n  def self.d2gen_buffered_n(d : Float64, precision : UInt32, result : UInt8*, alternative : Bool = false)\n    if d == 0\n      result[0] = '0'.ord.to_u8!\n      return {1, 0}\n    end\n\n    precision = precision.clamp(1_u32, 1000000_u32)\n    if precision <= MAX_SPECIAL_P\n      table_begin = SPECIAL_X.to_unsafe + (precision &- 1) * (precision &+ 10) // 2\n      table_length = precision.to_i32! &+ 5\n    else\n      table_begin = ORDINARY_X.to_unsafe\n      table_length = {precision, MAX_ORDINARY_P}.min.to_i32! &+ 5\n    end\n\n    bits = d.unsafe_as(UInt64)\n    index = 0\n    while index < table_length\n      break if bits <= table_begin[index]\n      index &+= 1\n    end\n\n    sci_exp_x = index &- 5\n    use_fixed_notation = precision > sci_exp_x && sci_exp_x >= -4\n\n    significand_last = exponent_first = exponent_last = Pointer(UInt8).null\n\n    # Write into the local buffer.\n    if use_fixed_notation\n      effective_precision = precision &- 1 &- sci_exp_x\n      max_precision = MAX_FIXED_PRECISION\n      len = d2fixed_buffered_n(d, {effective_precision, max_precision}.min, result)\n      significand_last = result + len\n    else\n      effective_precision = precision &- 1\n      max_precision = MAX_SCI_PRECISION\n      len = d2exp_buffered_n(d, {effective_precision, max_precision}.min, result)\n      exponent_first = result + Slice.new(result, len).fast_index('e'.ord.to_u8!, 0).not_nil!\n      significand_last = exponent_first\n      exponent_last = result + len\n    end\n\n    # If we printed a decimal point followed by digits, perform zero-trimming.\n    if effective_precision > 0 && !alternative\n      while significand_last[-1] === '0' # will stop at '.' or a nonzero digit\n        significand_last -= 1\n      end\n\n      if significand_last[-1] === '.'\n        significand_last -= 1\n      end\n    end\n\n    # Copy the exponent to the output range.\n    unless use_fixed_notation\n      exponent_first.move_to(significand_last, exponent_last - exponent_first)\n    end\n\n    extra_zeros = effective_precision > max_precision ? effective_precision &- max_precision : 0_u32\n    {(significand_last - result + (exponent_last - exponent_first)).to_i32!, extra_zeros.to_i32!}\n  end\n\n  def self.d2fixed(d : Float64, precision : Int)\n    String.new(2000) do |buffer|\n      len = d2fixed_buffered_n(d, precision.to_u32, buffer)\n      {len, len}\n    end\n  end\n\n  def self.d2exp(d : Float64, precision : Int)\n    String.new(2000) do |buffer|\n      len = d2exp_buffered_n(d, precision.to_u32, buffer)\n      {len, len}\n    end\n  end\n\n  def self.d2gen(d : Float64, precision : Int)\n    String.new(773) do |buffer|\n      len, _ = d2gen_buffered_n(d, precision.to_u32, buffer)\n      {len, len}\n    end\n  end\nend\n"
  },
  {
    "path": "src/float/printer/ryu_printf_table.cr",
    "content": "{% skip_file unless String::Formatter::HAS_RYU_PRINTF %}\n\nmodule Float::Printer::RyuPrintf\n  {% begin %}\n    # A table of all two-digit numbers. This is used to speed up decimal digit\n    # generation by copying pairs of digits into the final output.\n    DIGIT_TABLE = \"{% for i in 0..9 %}0{{ i }}{% end %}{% for i in 10..99 %}{{ i }}{% end %}\".to_unsafe\n  {% end %}\n\n  # TODO: this is needed to avoid generating lots of allocas\n  # in LLVM, which makes LLVM really slow. The compiler should\n  # try to avoid/reuse temporary allocas.\n  # Explanation: https://github.com/crystal-lang/crystal/issues/4516#issuecomment-306226171\n  private def self.put(array : Array, values) : Nil\n    array << values\n  end\n\n  private def self.put(array : Array, *values) : Nil\n    array << values\n  end\n\n  # Special constants for `%g` formatting; for details refer to\n  # https://github.com/ulfjack/ryu/pull/185/files or\n  # https://github.com/microsoft/STL/blob/a8888806c6960f1687590ffd4244794c753aa819/stl/inc/xcharconv_tables.h\n\n  private MAX_SPECIAL_P = 15_u32\n\n  private SPECIAL_X = begin\n    data = Array(UInt64).new(195)\n    put(data, 0x3F18E757928E0C9Du64)\n    put(data, 0x3F4F212D77318FC5u64)\n    put(data, 0x3F8374BC6A7EF9DBu64)\n    put(data, 0x3FB851EB851EB851u64)\n    put(data, 0x3FEE666666666666u64)\n    put(data, 0x4022FFFFFFFFFFFFu64)\n    put(data, 0x3F1A1554FBDAD751u64)\n    put(data, 0x3F504D551D68C692u64)\n    put(data, 0x3F8460AA64C2F837u64)\n    put(data, 0x3FB978D4FDF3B645u64)\n    put(data, 0x3FEFD70A3D70A3D7u64)\n    put(data, 0x4023E66666666666u64)\n    put(data, 0x4058DFFFFFFFFFFFu64)\n    put(data, 0x3F1A3387ECC8EB96u64)\n    put(data, 0x3F506034F3FD933Eu64)\n    put(data, 0x3F84784230FCF80Du64)\n    put(data, 0x3FB99652BD3C3611u64)\n    put(data, 0x3FEFFBE76C8B4395u64)\n    put(data, 0x4023FD70A3D70A3Du64)\n    put(data, 0x4058FCCCCCCCCCCCu64)\n    put(data, 0x408F3BFFFFFFFFFFu64)\n    put(data, 0x3F1A368D04E0BA6Au64)\n    put(data, 0x3F506218230C7482u64)\n    put(data, 0x3F847A9E2BCF91A3u64)\n    put(data, 0x3FB99945B6C3760Bu64)\n    put(data, 0x3FEFFF972474538Eu64)\n    put(data, 0x4023FFBE76C8B439u64)\n    put(data, 0x4058FFAE147AE147u64)\n    put(data, 0x408F3F9999999999u64)\n    put(data, 0x40C387BFFFFFFFFFu64)\n    put(data, 0x3F1A36DA54164F19u64)\n    put(data, 0x3F506248748DF16Fu64)\n    put(data, 0x3F847ADA91B16DCBu64)\n    put(data, 0x3FB99991361DC93Eu64)\n    put(data, 0x3FEFFFF583A53B8Eu64)\n    put(data, 0x4023FFF972474538u64)\n    put(data, 0x4058FFF7CED91687u64)\n    put(data, 0x408F3FF5C28F5C28u64)\n    put(data, 0x40C387F999999999u64)\n    put(data, 0x40F869F7FFFFFFFFu64)\n    put(data, 0x3F1A36E20F35445Du64)\n    put(data, 0x3F50624D49814ABAu64)\n    put(data, 0x3F847AE09BE19D69u64)\n    put(data, 0x3FB99998C2DA04C3u64)\n    put(data, 0x3FEFFFFEF39085F4u64)\n    put(data, 0x4023FFFF583A53B8u64)\n    put(data, 0x4058FFFF2E48E8A7u64)\n    put(data, 0x408F3FFEF9DB22D0u64)\n    put(data, 0x40C387FF5C28F5C2u64)\n    put(data, 0x40F869FF33333333u64)\n    put(data, 0x412E847EFFFFFFFFu64)\n    put(data, 0x3F1A36E2D51EC34Bu64)\n    put(data, 0x3F50624DC5333A0Eu64)\n    put(data, 0x3F847AE136800892u64)\n    put(data, 0x3FB9999984200AB7u64)\n    put(data, 0x3FEFFFFFE5280D65u64)\n    put(data, 0x4023FFFFEF39085Fu64)\n    put(data, 0x4058FFFFEB074A77u64)\n    put(data, 0x408F3FFFE5C91D14u64)\n    put(data, 0x40C387FFEF9DB22Du64)\n    put(data, 0x40F869FFEB851EB8u64)\n    put(data, 0x412E847FE6666666u64)\n    put(data, 0x416312CFEFFFFFFFu64)\n    put(data, 0x3F1A36E2E8E94FFCu64)\n    put(data, 0x3F50624DD191D1FDu64)\n    put(data, 0x3F847AE145F6467Du64)\n    put(data, 0x3FB999999773D81Cu64)\n    put(data, 0x3FEFFFFFFD50CE23u64)\n    put(data, 0x4023FFFFFE5280D6u64)\n    put(data, 0x4058FFFFFDE7210Bu64)\n    put(data, 0x408F3FFFFD60E94Eu64)\n    put(data, 0x40C387FFFE5C91D1u64)\n    put(data, 0x40F869FFFDF3B645u64)\n    put(data, 0x412E847FFD70A3D7u64)\n    put(data, 0x416312CFFE666666u64)\n    put(data, 0x4197D783FDFFFFFFu64)\n    put(data, 0x3F1A36E2EAE3F7A7u64)\n    put(data, 0x3F50624DD2CE7AC8u64)\n    put(data, 0x3F847AE14782197Bu64)\n    put(data, 0x3FB9999999629FD9u64)\n    put(data, 0x3FEFFFFFFFBB47D0u64)\n    put(data, 0x4023FFFFFFD50CE2u64)\n    put(data, 0x4058FFFFFFCA501Au64)\n    put(data, 0x408F3FFFFFBCE421u64)\n    put(data, 0x40C387FFFFD60E94u64)\n    put(data, 0x40F869FFFFCB923Au64)\n    put(data, 0x412E847FFFBE76C8u64)\n    put(data, 0x416312CFFFD70A3Du64)\n    put(data, 0x4197D783FFCCCCCCu64)\n    put(data, 0x41CDCD64FFBFFFFFu64)\n    put(data, 0x3F1A36E2EB16A205u64)\n    put(data, 0x3F50624DD2EE2543u64)\n    put(data, 0x3F847AE147A9AE94u64)\n    put(data, 0x3FB9999999941A39u64)\n    put(data, 0x3FEFFFFFFFF920C8u64)\n    put(data, 0x4023FFFFFFFBB47Du64)\n    put(data, 0x4058FFFFFFFAA19Cu64)\n    put(data, 0x408F3FFFFFF94A03u64)\n    put(data, 0x40C387FFFFFBCE42u64)\n    put(data, 0x40F869FFFFFAC1D2u64)\n    put(data, 0x412E847FFFF97247u64)\n    put(data, 0x416312CFFFFBE76Cu64)\n    put(data, 0x4197D783FFFAE147u64)\n    put(data, 0x41CDCD64FFF99999u64)\n    put(data, 0x4202A05F1FFBFFFFu64)\n    put(data, 0x3F1A36E2EB1BB30Fu64)\n    put(data, 0x3F50624DD2F14FE9u64)\n    put(data, 0x3F847AE147ADA3E3u64)\n    put(data, 0x3FB9999999990CDCu64)\n    put(data, 0x3FEFFFFFFFFF5014u64)\n    put(data, 0x4023FFFFFFFF920Cu64)\n    put(data, 0x4058FFFFFFFF768Fu64)\n    put(data, 0x408F3FFFFFFF5433u64)\n    put(data, 0x40C387FFFFFF94A0u64)\n    put(data, 0x40F869FFFFFF79C8u64)\n    put(data, 0x412E847FFFFF583Au64)\n    put(data, 0x416312CFFFFF9724u64)\n    put(data, 0x4197D783FFFF7CEDu64)\n    put(data, 0x41CDCD64FFFF5C28u64)\n    put(data, 0x4202A05F1FFF9999u64)\n    put(data, 0x42374876E7FF7FFFu64)\n    put(data, 0x3F1A36E2EB1C34C3u64)\n    put(data, 0x3F50624DD2F1A0FAu64)\n    put(data, 0x3F847AE147AE0938u64)\n    put(data, 0x3FB9999999998B86u64)\n    put(data, 0x3FEFFFFFFFFFEE68u64)\n    put(data, 0x4023FFFFFFFFF501u64)\n    put(data, 0x4058FFFFFFFFF241u64)\n    put(data, 0x408F3FFFFFFFEED1u64)\n    put(data, 0x40C387FFFFFFF543u64)\n    put(data, 0x40F869FFFFFFF294u64)\n    put(data, 0x412E847FFFFFEF39u64)\n    put(data, 0x416312CFFFFFF583u64)\n    put(data, 0x4197D783FFFFF2E4u64)\n    put(data, 0x41CDCD64FFFFEF9Du64)\n    put(data, 0x4202A05F1FFFF5C2u64)\n    put(data, 0x42374876E7FFF333u64)\n    put(data, 0x426D1A94A1FFEFFFu64)\n    put(data, 0x3F1A36E2EB1C41BBu64)\n    put(data, 0x3F50624DD2F1A915u64)\n    put(data, 0x3F847AE147AE135Au64)\n    put(data, 0x3FB9999999999831u64)\n    put(data, 0x3FEFFFFFFFFFFE3Du64)\n    put(data, 0x4023FFFFFFFFFEE6u64)\n    put(data, 0x4058FFFFFFFFFEA0u64)\n    put(data, 0x408F3FFFFFFFFE48u64)\n    put(data, 0x40C387FFFFFFFEEDu64)\n    put(data, 0x40F869FFFFFFFEA8u64)\n    put(data, 0x412E847FFFFFFE52u64)\n    put(data, 0x416312CFFFFFFEF3u64)\n    put(data, 0x4197D783FFFFFEB0u64)\n    put(data, 0x41CDCD64FFFFFE5Cu64)\n    put(data, 0x4202A05F1FFFFEF9u64)\n    put(data, 0x42374876E7FFFEB8u64)\n    put(data, 0x426D1A94A1FFFE66u64)\n    put(data, 0x42A2309CE53FFEFFu64)\n    put(data, 0x3F1A36E2EB1C4307u64)\n    put(data, 0x3F50624DD2F1A9E4u64)\n    put(data, 0x3F847AE147AE145Eu64)\n    put(data, 0x3FB9999999999975u64)\n    put(data, 0x3FEFFFFFFFFFFFD2u64)\n    put(data, 0x4023FFFFFFFFFFE3u64)\n    put(data, 0x4058FFFFFFFFFFDCu64)\n    put(data, 0x408F3FFFFFFFFFD4u64)\n    put(data, 0x40C387FFFFFFFFE4u64)\n    put(data, 0x40F869FFFFFFFFDDu64)\n    put(data, 0x412E847FFFFFFFD5u64)\n    put(data, 0x416312CFFFFFFFE5u64)\n    put(data, 0x4197D783FFFFFFDEu64)\n    put(data, 0x41CDCD64FFFFFFD6u64)\n    put(data, 0x4202A05F1FFFFFE5u64)\n    put(data, 0x42374876E7FFFFDFu64)\n    put(data, 0x426D1A94A1FFFFD7u64)\n    put(data, 0x42A2309CE53FFFE6u64)\n    put(data, 0x42D6BCC41E8FFFDFu64)\n    put(data, 0x3F1A36E2EB1C4328u64)\n    put(data, 0x3F50624DD2F1A9F9u64)\n    put(data, 0x3F847AE147AE1477u64)\n    put(data, 0x3FB9999999999995u64)\n    put(data, 0x3FEFFFFFFFFFFFFBu64)\n    put(data, 0x4023FFFFFFFFFFFDu64)\n    put(data, 0x4058FFFFFFFFFFFCu64)\n    put(data, 0x408F3FFFFFFFFFFBu64)\n    put(data, 0x40C387FFFFFFFFFDu64)\n    put(data, 0x40F869FFFFFFFFFCu64)\n    put(data, 0x412E847FFFFFFFFBu64)\n    put(data, 0x416312CFFFFFFFFDu64)\n    put(data, 0x4197D783FFFFFFFCu64)\n    put(data, 0x41CDCD64FFFFFFFBu64)\n    put(data, 0x4202A05F1FFFFFFDu64)\n    put(data, 0x42374876E7FFFFFCu64)\n    put(data, 0x426D1A94A1FFFFFBu64)\n    put(data, 0x42A2309CE53FFFFDu64)\n    put(data, 0x42D6BCC41E8FFFFCu64)\n    put(data, 0x430C6BF52633FFFBu64)\n    data\n  end\n\n  private MAX_ORDINARY_P = 309_u32\n\n  private ORDINARY_X = begin\n    data = Array(UInt64).new(314)\n    put(data, 0x3F1A36E2EB1C432Cu64)\n    put(data, 0x3F50624DD2F1A9FBu64)\n    put(data, 0x3F847AE147AE147Au64)\n    put(data, 0x3FB9999999999999u64)\n    put(data, 0x3FEFFFFFFFFFFFFFu64)\n    put(data, 0x4023FFFFFFFFFFFFu64)\n    put(data, 0x4058FFFFFFFFFFFFu64)\n    put(data, 0x408F3FFFFFFFFFFFu64)\n    put(data, 0x40C387FFFFFFFFFFu64)\n    put(data, 0x40F869FFFFFFFFFFu64)\n    put(data, 0x412E847FFFFFFFFFu64)\n    put(data, 0x416312CFFFFFFFFFu64)\n    put(data, 0x4197D783FFFFFFFFu64)\n    put(data, 0x41CDCD64FFFFFFFFu64)\n    put(data, 0x4202A05F1FFFFFFFu64)\n    put(data, 0x42374876E7FFFFFFu64)\n    put(data, 0x426D1A94A1FFFFFFu64)\n    put(data, 0x42A2309CE53FFFFFu64)\n    put(data, 0x42D6BCC41E8FFFFFu64)\n    put(data, 0x430C6BF52633FFFFu64)\n    put(data, 0x4341C37937E07FFFu64)\n    put(data, 0x4376345785D89FFFu64)\n    put(data, 0x43ABC16D674EC7FFu64)\n    put(data, 0x43E158E460913CFFu64)\n    put(data, 0x4415AF1D78B58C3Fu64)\n    put(data, 0x444B1AE4D6E2EF4Fu64)\n    put(data, 0x4480F0CF064DD591u64)\n    put(data, 0x44B52D02C7E14AF6u64)\n    put(data, 0x44EA784379D99DB4u64)\n    put(data, 0x45208B2A2C280290u64)\n    put(data, 0x4554ADF4B7320334u64)\n    put(data, 0x4589D971E4FE8401u64)\n    put(data, 0x45C027E72F1F1281u64)\n    put(data, 0x45F431E0FAE6D721u64)\n    put(data, 0x46293E5939A08CE9u64)\n    put(data, 0x465F8DEF8808B024u64)\n    put(data, 0x4693B8B5B5056E16u64)\n    put(data, 0x46C8A6E32246C99Cu64)\n    put(data, 0x46FED09BEAD87C03u64)\n    put(data, 0x4733426172C74D82u64)\n    put(data, 0x476812F9CF7920E2u64)\n    put(data, 0x479E17B84357691Bu64)\n    put(data, 0x47D2CED32A16A1B1u64)\n    put(data, 0x48078287F49C4A1Du64)\n    put(data, 0x483D6329F1C35CA4u64)\n    put(data, 0x48725DFA371A19E6u64)\n    put(data, 0x48A6F578C4E0A060u64)\n    put(data, 0x48DCB2D6F618C878u64)\n    put(data, 0x4911EFC659CF7D4Bu64)\n    put(data, 0x49466BB7F0435C9Eu64)\n    put(data, 0x497C06A5EC5433C6u64)\n    put(data, 0x49B18427B3B4A05Bu64)\n    put(data, 0x49E5E531A0A1C872u64)\n    put(data, 0x4A1B5E7E08CA3A8Fu64)\n    put(data, 0x4A511B0EC57E6499u64)\n    put(data, 0x4A8561D276DDFDC0u64)\n    put(data, 0x4ABABA4714957D30u64)\n    put(data, 0x4AF0B46C6CDD6E3Eu64)\n    put(data, 0x4B24E1878814C9CDu64)\n    put(data, 0x4B5A19E96A19FC40u64)\n    put(data, 0x4B905031E2503DA8u64)\n    put(data, 0x4BC4643E5AE44D12u64)\n    put(data, 0x4BF97D4DF19D6057u64)\n    put(data, 0x4C2FDCA16E04B86Du64)\n    put(data, 0x4C63E9E4E4C2F344u64)\n    put(data, 0x4C98E45E1DF3B015u64)\n    put(data, 0x4CCF1D75A5709C1Au64)\n    put(data, 0x4D03726987666190u64)\n    put(data, 0x4D384F03E93FF9F4u64)\n    put(data, 0x4D6E62C4E38FF872u64)\n    put(data, 0x4DA2FDBB0E39FB47u64)\n    put(data, 0x4DD7BD29D1C87A19u64)\n    put(data, 0x4E0DAC74463A989Fu64)\n    put(data, 0x4E428BC8ABE49F63u64)\n    put(data, 0x4E772EBAD6DDC73Cu64)\n    put(data, 0x4EACFA698C95390Bu64)\n    put(data, 0x4EE21C81F7DD43A7u64)\n    put(data, 0x4F16A3A275D49491u64)\n    put(data, 0x4F4C4C8B1349B9B5u64)\n    put(data, 0x4F81AFD6EC0E1411u64)\n    put(data, 0x4FB61BCCA7119915u64)\n    put(data, 0x4FEBA2BFD0D5FF5Bu64)\n    put(data, 0x502145B7E285BF98u64)\n    put(data, 0x50559725DB272F7Fu64)\n    put(data, 0x508AFCEF51F0FB5Eu64)\n    put(data, 0x50C0DE1593369D1Bu64)\n    put(data, 0x50F5159AF8044462u64)\n    put(data, 0x512A5B01B605557Au64)\n    put(data, 0x516078E111C3556Cu64)\n    put(data, 0x5194971956342AC7u64)\n    put(data, 0x51C9BCDFABC13579u64)\n    put(data, 0x5200160BCB58C16Cu64)\n    put(data, 0x52341B8EBE2EF1C7u64)\n    put(data, 0x526922726DBAAE39u64)\n    put(data, 0x529F6B0F092959C7u64)\n    put(data, 0x52D3A2E965B9D81Cu64)\n    put(data, 0x53088BA3BF284E23u64)\n    put(data, 0x533EAE8CAEF261ACu64)\n    put(data, 0x53732D17ED577D0Bu64)\n    put(data, 0x53A7F85DE8AD5C4Eu64)\n    put(data, 0x53DDF67562D8B362u64)\n    put(data, 0x5412BA095DC7701Du64)\n    put(data, 0x5447688BB5394C25u64)\n    put(data, 0x547D42AEA2879F2Eu64)\n    put(data, 0x54B249AD2594C37Cu64)\n    put(data, 0x54E6DC186EF9F45Cu64)\n    put(data, 0x551C931E8AB87173u64)\n    put(data, 0x5551DBF316B346E7u64)\n    put(data, 0x558652EFDC6018A1u64)\n    put(data, 0x55BBE7ABD3781ECAu64)\n    put(data, 0x55F170CB642B133Eu64)\n    put(data, 0x5625CCFE3D35D80Eu64)\n    put(data, 0x565B403DCC834E11u64)\n    put(data, 0x569108269FD210CBu64)\n    put(data, 0x56C54A3047C694FDu64)\n    put(data, 0x56FA9CBC59B83A3Du64)\n    put(data, 0x5730A1F5B8132466u64)\n    put(data, 0x5764CA732617ED7Fu64)\n    put(data, 0x5799FD0FEF9DE8DFu64)\n    put(data, 0x57D03E29F5C2B18Bu64)\n    put(data, 0x58044DB473335DEEu64)\n    put(data, 0x583961219000356Au64)\n    put(data, 0x586FB969F40042C5u64)\n    put(data, 0x58A3D3E2388029BBu64)\n    put(data, 0x58D8C8DAC6A0342Au64)\n    put(data, 0x590EFB1178484134u64)\n    put(data, 0x59435CEAEB2D28C0u64)\n    put(data, 0x59783425A5F872F1u64)\n    put(data, 0x59AE412F0F768FADu64)\n    put(data, 0x59E2E8BD69AA19CCu64)\n    put(data, 0x5A17A2ECC414A03Fu64)\n    put(data, 0x5A4D8BA7F519C84Fu64)\n    put(data, 0x5A827748F9301D31u64)\n    put(data, 0x5AB7151B377C247Eu64)\n    put(data, 0x5AECDA62055B2D9Du64)\n    put(data, 0x5B22087D4358FC82u64)\n    put(data, 0x5B568A9C942F3BA3u64)\n    put(data, 0x5B8C2D43B93B0A8Bu64)\n    put(data, 0x5BC19C4A53C4E697u64)\n    put(data, 0x5BF6035CE8B6203Du64)\n    put(data, 0x5C2B843422E3A84Cu64)\n    put(data, 0x5C6132A095CE492Fu64)\n    put(data, 0x5C957F48BB41DB7Bu64)\n    put(data, 0x5CCADF1AEA12525Au64)\n    put(data, 0x5D00CB70D24B7378u64)\n    put(data, 0x5D34FE4D06DE5056u64)\n    put(data, 0x5D6A3DE04895E46Cu64)\n    put(data, 0x5DA066AC2D5DAEC3u64)\n    put(data, 0x5DD4805738B51A74u64)\n    put(data, 0x5E09A06D06E26112u64)\n    put(data, 0x5E400444244D7CABu64)\n    put(data, 0x5E7405552D60DBD6u64)\n    put(data, 0x5EA906AA78B912CBu64)\n    put(data, 0x5EDF485516E7577Eu64)\n    put(data, 0x5F138D352E5096AFu64)\n    put(data, 0x5F48708279E4BC5Au64)\n    put(data, 0x5F7E8CA3185DEB71u64)\n    put(data, 0x5FB317E5EF3AB327u64)\n    put(data, 0x5FE7DDDF6B095FF0u64)\n    put(data, 0x601DD55745CBB7ECu64)\n    put(data, 0x6052A5568B9F52F4u64)\n    put(data, 0x60874EAC2E8727B1u64)\n    put(data, 0x60BD22573A28F19Du64)\n    put(data, 0x60F2357684599702u64)\n    put(data, 0x6126C2D4256FFCC2u64)\n    put(data, 0x615C73892ECBFBF3u64)\n    put(data, 0x6191C835BD3F7D78u64)\n    put(data, 0x61C63A432C8F5CD6u64)\n    put(data, 0x61FBC8D3F7B3340Bu64)\n    put(data, 0x62315D847AD00087u64)\n    put(data, 0x6265B4E5998400A9u64)\n    put(data, 0x629B221EFFE500D3u64)\n    put(data, 0x62D0F5535FEF2084u64)\n    put(data, 0x630532A837EAE8A5u64)\n    put(data, 0x633A7F5245E5A2CEu64)\n    put(data, 0x63708F936BAF85C1u64)\n    put(data, 0x63A4B378469B6731u64)\n    put(data, 0x63D9E056584240FDu64)\n    put(data, 0x64102C35F729689Eu64)\n    put(data, 0x6444374374F3C2C6u64)\n    put(data, 0x647945145230B377u64)\n    put(data, 0x64AF965966BCE055u64)\n    put(data, 0x64E3BDF7E0360C35u64)\n    put(data, 0x6518AD75D8438F43u64)\n    put(data, 0x654ED8D34E547313u64)\n    put(data, 0x6583478410F4C7ECu64)\n    put(data, 0x65B819651531F9E7u64)\n    put(data, 0x65EE1FBE5A7E7861u64)\n    put(data, 0x6622D3D6F88F0B3Cu64)\n    put(data, 0x665788CCB6B2CE0Cu64)\n    put(data, 0x668D6AFFE45F818Fu64)\n    put(data, 0x66C262DFEEBBB0F9u64)\n    put(data, 0x66F6FB97EA6A9D37u64)\n    put(data, 0x672CBA7DE5054485u64)\n    put(data, 0x6761F48EAF234AD3u64)\n    put(data, 0x679671B25AEC1D88u64)\n    put(data, 0x67CC0E1EF1A724EAu64)\n    put(data, 0x680188D357087712u64)\n    put(data, 0x6835EB082CCA94D7u64)\n    put(data, 0x686B65CA37FD3A0Du64)\n    put(data, 0x68A11F9E62FE4448u64)\n    put(data, 0x68D56785FBBDD55Au64)\n    put(data, 0x690AC1677AAD4AB0u64)\n    put(data, 0x6940B8E0ACAC4EAEu64)\n    put(data, 0x6974E718D7D7625Au64)\n    put(data, 0x69AA20DF0DCD3AF0u64)\n    put(data, 0x69E0548B68A044D6u64)\n    put(data, 0x6A1469AE42C8560Cu64)\n    put(data, 0x6A498419D37A6B8Fu64)\n    put(data, 0x6A7FE52048590672u64)\n    put(data, 0x6AB3EF342D37A407u64)\n    put(data, 0x6AE8EB0138858D09u64)\n    put(data, 0x6B1F25C186A6F04Cu64)\n    put(data, 0x6B537798F428562Fu64)\n    put(data, 0x6B88557F31326BBBu64)\n    put(data, 0x6BBE6ADEFD7F06AAu64)\n    put(data, 0x6BF302CB5E6F642Au64)\n    put(data, 0x6C27C37E360B3D35u64)\n    put(data, 0x6C5DB45DC38E0C82u64)\n    put(data, 0x6C9290BA9A38C7D1u64)\n    put(data, 0x6CC734E940C6F9C5u64)\n    put(data, 0x6CFD022390F8B837u64)\n    put(data, 0x6D3221563A9B7322u64)\n    put(data, 0x6D66A9ABC9424FEBu64)\n    put(data, 0x6D9C5416BB92E3E6u64)\n    put(data, 0x6DD1B48E353BCE6Fu64)\n    put(data, 0x6E0621B1C28AC20Bu64)\n    put(data, 0x6E3BAA1E332D728Eu64)\n    put(data, 0x6E714A52DFFC6799u64)\n    put(data, 0x6EA59CE797FB817Fu64)\n    put(data, 0x6EDB04217DFA61DFu64)\n    put(data, 0x6F10E294EEBC7D2Bu64)\n    put(data, 0x6F451B3A2A6B9C76u64)\n    put(data, 0x6F7A6208B5068394u64)\n    put(data, 0x6FB07D457124123Cu64)\n    put(data, 0x6FE49C96CD6D16CBu64)\n    put(data, 0x7019C3BC80C85C7Eu64)\n    put(data, 0x70501A55D07D39CFu64)\n    put(data, 0x708420EB449C8842u64)\n    put(data, 0x70B9292615C3AA53u64)\n    put(data, 0x70EF736F9B3494E8u64)\n    put(data, 0x7123A825C100DD11u64)\n    put(data, 0x7158922F31411455u64)\n    put(data, 0x718EB6BAFD91596Bu64)\n    put(data, 0x71C33234DE7AD7E2u64)\n    put(data, 0x71F7FEC216198DDBu64)\n    put(data, 0x722DFE729B9FF152u64)\n    put(data, 0x7262BF07A143F6D3u64)\n    put(data, 0x72976EC98994F488u64)\n    put(data, 0x72CD4A7BEBFA31AAu64)\n    put(data, 0x73024E8D737C5F0Au64)\n    put(data, 0x7336E230D05B76CDu64)\n    put(data, 0x736C9ABD04725480u64)\n    put(data, 0x73A1E0B622C774D0u64)\n    put(data, 0x73D658E3AB795204u64)\n    put(data, 0x740BEF1C9657A685u64)\n    put(data, 0x74417571DDF6C813u64)\n    put(data, 0x7475D2CE55747A18u64)\n    put(data, 0x74AB4781EAD1989Eu64)\n    put(data, 0x74E10CB132C2FF63u64)\n    put(data, 0x75154FDD7F73BF3Bu64)\n    put(data, 0x754AA3D4DF50AF0Au64)\n    put(data, 0x7580A6650B926D66u64)\n    put(data, 0x75B4CFFE4E7708C0u64)\n    put(data, 0x75EA03FDE214CAF0u64)\n    put(data, 0x7620427EAD4CFED6u64)\n    put(data, 0x7654531E58A03E8Bu64)\n    put(data, 0x768967E5EEC84E2Eu64)\n    put(data, 0x76BFC1DF6A7A61BAu64)\n    put(data, 0x76F3D92BA28C7D14u64)\n    put(data, 0x7728CF768B2F9C59u64)\n    put(data, 0x775F03542DFB8370u64)\n    put(data, 0x779362149CBD3226u64)\n    put(data, 0x77C83A99C3EC7EAFu64)\n    put(data, 0x77FE494034E79E5Bu64)\n    put(data, 0x7832EDC82110C2F9u64)\n    put(data, 0x7867A93A2954F3B7u64)\n    put(data, 0x789D9388B3AA30A5u64)\n    put(data, 0x78D27C35704A5E67u64)\n    put(data, 0x79071B42CC5CF601u64)\n    put(data, 0x793CE2137F743381u64)\n    put(data, 0x79720D4C2FA8A030u64)\n    put(data, 0x79A6909F3B92C83Du64)\n    put(data, 0x79DC34C70A777A4Cu64)\n    put(data, 0x7A11A0FC668AAC6Fu64)\n    put(data, 0x7A46093B802D578Bu64)\n    put(data, 0x7A7B8B8A6038AD6Eu64)\n    put(data, 0x7AB137367C236C65u64)\n    put(data, 0x7AE585041B2C477Eu64)\n    put(data, 0x7B1AE64521F7595Eu64)\n    put(data, 0x7B50CFEB353A97DAu64)\n    put(data, 0x7B8503E602893DD1u64)\n    put(data, 0x7BBA44DF832B8D45u64)\n    put(data, 0x7BF06B0BB1FB384Bu64)\n    put(data, 0x7C2485CE9E7A065Eu64)\n    put(data, 0x7C59A742461887F6u64)\n    put(data, 0x7C9008896BCF54F9u64)\n    put(data, 0x7CC40AABC6C32A38u64)\n    put(data, 0x7CF90D56B873F4C6u64)\n    put(data, 0x7D2F50AC6690F1F8u64)\n    put(data, 0x7D63926BC01A973Bu64)\n    put(data, 0x7D987706B0213D09u64)\n    put(data, 0x7DCE94C85C298C4Cu64)\n    put(data, 0x7E031CFD3999F7AFu64)\n    put(data, 0x7E37E43C8800759Bu64)\n    put(data, 0x7E6DDD4BAA009302u64)\n    put(data, 0x7EA2AA4F4A405BE1u64)\n    put(data, 0x7ED754E31CD072D9u64)\n    put(data, 0x7F0D2A1BE4048F90u64)\n    put(data, 0x7F423A516E82D9BAu64)\n    put(data, 0x7F76C8E5CA239028u64)\n    put(data, 0x7FAC7B1F3CAC7433u64)\n    put(data, 0x7FE1CCF385EBC89Fu64)\n    put(data, 0x7FEFFFFFFFFFFFFFu64)\n    data\n  end\n\n  private POW10_OFFSET = [\n    0, 2, 5, 8, 12, 16, 21, 26, 32, 39,\n    46, 54, 62, 71, 80, 90, 100, 111, 122, 134,\n    146, 159, 173, 187, 202, 217, 233, 249, 266, 283,\n    301, 319, 338, 357, 377, 397, 418, 440, 462, 485,\n    508, 532, 556, 581, 606, 632, 658, 685, 712, 740,\n    769, 798, 828, 858, 889, 920, 952, 984, 1017, 1050,\n    1084, 1118, 1153, 1188,\n  ] of UInt16\n\n  private POW10_SPLIT = begin\n    data = Array({UInt64, UInt64, UInt64}).new(1224)\n    put(data, 1u64, 72057594037927936u64, 0u64)\n    put(data, 699646928636035157u64, 72057594u64, 0u64)\n    put(data, 1u64, 0u64, 256u64)\n    put(data, 11902091922964236229u64, 4722366482869u64, 0u64)\n    put(data, 6760415703743915872u64, 4722u64, 0u64)\n    put(data, 1u64, 0u64, 16777216u64)\n    put(data, 13369850649504950658u64, 309485009821345068u64, 0u64)\n    put(data, 15151142278969419334u64, 309485009u64, 0u64)\n    put(data, 1u64, 0u64, 75511627776u64)\n    put(data, 4635408826454083567u64, 9437866644873197963u64, 1099u64)\n    put(data, 12367138975830625353u64, 20282409603651u64, 0u64)\n    put(data, 7555853734021184432u64, 20282u64, 0u64)\n    put(data, 1u64, 0u64, 250037927936u64)\n    put(data, 5171444645924616995u64, 699646928636035156u64, 72057594u64)\n    put(data, 16672297533003297786u64, 1329227995784915872u64, 0u64)\n    put(data, 14479142226848862515u64, 1329227995u64, 0u64)\n    put(data, 1u64, 0u64, 181645213696u64)\n    put(data, 12214193123817091081u64, 11902091922964236228u64, 114366482869u64)\n    put(data, 16592893013671929435u64, 6760415703743915871u64, 4722u64)\n    put(data, 4549827147718617003u64, 87112285931760u64, 0u64)\n    put(data, 5274510003815168971u64, 87112u64, 0u64)\n    put(data, 1u64, 0u64, 44724781056u64)\n    put(data, 9794971998307800535u64, 13369850649504950657u64, 209821345068u64)\n    put(data, 14720142899209240169u64, 15151142278969419333u64, 309485009u64)\n    put(data, 4300745446091561535u64, 5708990770823839524u64, 0u64)\n    put(data, 15197156861335443364u64, 5708990770u64, 0u64)\n    put(data, 1u64, 0u64, 139251286016u64)\n    put(data, 13484604155038683037u64, 4635408826454083566u64, 67670423947u64)\n    put(data, 8356963862052375699u64, 12367138975830625352u64, 58409603651u64)\n    put(data, 5850852848337610021u64, 7555853734021184431u64, 20282u64)\n    put(data, 2712780827214982050u64, 374144419156711u64, 0u64)\n    put(data, 7732076577307618052u64, 374144u64, 0u64)\n    put(data, 1u64, 0u64, 84280344576u64)\n    put(data, 17296309485351745867u64, 5171444645924616994u64, 160903807060u64)\n    put(data, 16598859101615853088u64, 16672297533003297785u64, 219784915872u64)\n    put(data, 7469952526870444257u64, 14479142226848862514u64, 1329227995u64)\n    put(data, 13531654022114669524u64, 6073184580144670117u64, 1u64)\n    put(data, 15757609704383306943u64, 24519928653u64, 0u64)\n    put(data, 9590990814237149590u64, 24u64, 0u64)\n    put(data, 1u64, 0u64, 196662132736u64)\n    put(data, 15408590707489433890u64, 12214193123817091080u64, 95899502532u64)\n    put(data, 18332056844289122710u64, 16592893013671929434u64, 240246646623u64)\n    put(data, 11114572877353986193u64, 4549827147718617002u64, 72285931760u64)\n    put(data, 1703393793997526525u64, 5274510003815168970u64, 87112u64)\n    put(data, 5082852056285196265u64, 1606938044258990u64, 0u64)\n    put(data, 816434266573722365u64, 1606938u64, 0u64)\n    put(data, 1u64, 0u64, 129530986496u64)\n    put(data, 5736523019264798742u64, 9794971998307800534u64, 69797980545u64)\n    put(data, 10129314776268243339u64, 14720142899209240168u64, 36233143877u64)\n    put(data, 16511595775483995364u64, 4300745446091561534u64, 50823839524u64)\n    put(data, 12367293405401453325u64, 15197156861335443363u64, 5708990770u64)\n    put(data, 16934621733248854291u64, 13078571300009428617u64, 5u64)\n    put(data, 10278280417769171336u64, 105312291668u64, 0u64)\n    put(data, 5760764486226151240u64, 105u64, 0u64)\n    put(data, 1u64, 0u64, 238731001856u64)\n    put(data, 4128368337188369761u64, 13484604155038683036u64, 72453031918u64)\n    put(data, 10240941003671005056u64, 8356963862052375698u64, 175317175368u64)\n    put(data, 17933378316822368251u64, 5850852848337610020u64, 231147060143u64)\n    put(data, 8346249813075698616u64, 2712780827214982049u64, 128419156711u64)\n    put(data, 15906203609160902695u64, 7732076577307618051u64, 374144u64)\n    put(data, 14525607416135386328u64, 6901746346790563u64, 0u64)\n    put(data, 6397156777364256320u64, 6901746u64, 0u64)\n    put(data, 1u64, 0u64, 34937634816u64)\n    put(data, 16798760952716600048u64, 17296309485351745866u64, 249899825954u64)\n    put(data, 2419982808370854967u64, 16598859101615853087u64, 50404946937u64)\n    put(data, 2922947087773078956u64, 7469952526870444256u64, 165733552434u64)\n    put(data, 15419220167069510190u64, 13531654022114669523u64, 77854221733u64)\n    put(data, 3452124642157173416u64, 15757609704383306942u64, 24519928653u64)\n    put(data, 5979700067267186899u64, 9590990814237149589u64, 24u64)\n    put(data, 4913998146922579597u64, 452312848583u64, 0u64)\n    put(data, 5771037749337678924u64, 452u64, 0u64)\n    put(data, 1u64, 0u64, 8835301376u64)\n    put(data, 3464734175350698519u64, 15408590707489433889u64, 90993782792u64)\n    put(data, 9334527711335850125u64, 18332056844289122709u64, 170602522202u64)\n    put(data, 7269882896518450106u64, 11114572877353986192u64, 202092341162u64)\n    put(data, 1372511258182263196u64, 1703393793997526524u64, 174275541962u64)\n    put(data, 7571228438575951046u64, 5082852056285196264u64, 26044258990u64)\n    put(data, 2992506536646070406u64, 816434266573722364u64, 1606938u64)\n    put(data, 524517896824344606u64, 29642774844752946u64, 0u64)\n    put(data, 15582941400898702773u64, 29642774u64, 0u64)\n    put(data, 1u64, 0u64, 214310977536u64)\n    put(data, 3846112492507251066u64, 5736523019264798741u64, 104549111254u64)\n    put(data, 16681117750123089487u64, 10129314776268243338u64, 62895095400u64)\n    put(data, 14986314536556547267u64, 16511595775483995363u64, 163670432318u64)\n    put(data, 2573712825027107389u64, 12367293405401453324u64, 137918027683u64)\n    put(data, 7504855874008324928u64, 16934621733248854290u64, 84557186697u64)\n    put(data, 9572138030626879787u64, 10278280417769171335u64, 105312291668u64)\n    put(data, 8520676959353394843u64, 5760764486226151239u64, 105u64)\n    put(data, 13448984662897903496u64, 1942668892225u64, 0u64)\n    put(data, 12338883700918130648u64, 1942u64, 0u64)\n    put(data, 1u64, 0u64, 156223799296u64)\n    put(data, 2517285787892561600u64, 4128368337188369760u64, 146555162524u64)\n    put(data, 4338831817635138103u64, 10240941003671005055u64, 36972170386u64)\n    put(data, 1561495325934523196u64, 17933378316822368250u64, 161452451108u64)\n    put(data, 12262635050079398786u64, 8346249813075698615u64, 3862277025u64)\n    put(data, 11144065765517284188u64, 15906203609160902694u64, 163787434755u64)\n    put(data, 1212260522471875711u64, 14525607416135386327u64, 242346790563u64)\n    put(data, 9695352922247418869u64, 6397156777364256319u64, 6901746u64)\n    put(data, 7227025834627242948u64, 127314748520905380u64, 0u64)\n    put(data, 9609008238705447829u64, 127314748u64, 0u64)\n    put(data, 1u64, 0u64, 74910662656u64)\n    put(data, 3609144142396852269u64, 16798760952716600047u64, 31131187530u64)\n    put(data, 11568848377382068865u64, 2419982808370854966u64, 224158453279u64)\n    put(data, 10068303578029323957u64, 2922947087773078955u64, 211835877600u64)\n    put(data, 11645070846862630231u64, 15419220167069510189u64, 190187140051u64)\n    put(data, 12449386705878485055u64, 3452124642157173415u64, 149324160190u64)\n    put(data, 15025619323517318418u64, 5979700067267186898u64, 199266388373u64)\n    put(data, 14996237555047131272u64, 4913998146922579596u64, 196312848583u64)\n    put(data, 10211005638256058413u64, 5771037749337678923u64, 452u64)\n    put(data, 1014743503555840530u64, 8343699359066u64, 0u64)\n    put(data, 12900897707145290678u64, 8343u64, 0u64)\n    put(data, 1u64, 0u64, 33187823616u64)\n    put(data, 4718003016239473662u64, 3464734175350698518u64, 149506025761u64)\n    put(data, 14865830648693666725u64, 9334527711335850124u64, 144394101141u64)\n    put(data, 14754517212823091778u64, 7269882896518450105u64, 252074403984u64)\n    put(data, 11113946551474911901u64, 1372511258182263195u64, 232410437116u64)\n    put(data, 1963520352638130630u64, 7571228438575951045u64, 252162224104u64)\n    put(data, 13342587341404964200u64, 2992506536646070405u64, 50028434172u64)\n    put(data, 6240392545013573291u64, 524517896824344605u64, 22844752946u64)\n    put(data, 14377490861349714758u64, 15582941400898702772u64, 29642774u64)\n    put(data, 1717863312631397839u64, 546812681195752981u64, 0u64)\n    put(data, 3611005143890591770u64, 546812681u64, 0u64)\n    put(data, 1u64, 0u64, 21208498176u64)\n    put(data, 13168252824351245504u64, 3846112492507251065u64, 138904285205u64)\n    put(data, 735883891883379688u64, 16681117750123089486u64, 227812409738u64)\n    put(data, 10609203866866106404u64, 14986314536556547266u64, 12139521251u64)\n    put(data, 12358191111890306470u64, 2573712825027107388u64, 18406839052u64)\n    put(data, 15229916368406413528u64, 7504855874008324927u64, 135518906642u64)\n    put(data, 7241424335568075942u64, 9572138030626879786u64, 71461906823u64)\n    put(data, 6049715868779871913u64, 8520676959353394842u64, 65729070919u64)\n    put(data, 2000548404719336762u64, 13448984662897903495u64, 150668892225u64)\n    put(data, 1410974761895205301u64, 12338883700918130647u64, 1942u64)\n    put(data, 16000132467694084868u64, 35835915874844u64, 0u64)\n    put(data, 16894908866816792556u64, 35835u64, 0u64)\n    put(data, 1u64, 0u64, 96136462336u64)\n    put(data, 589096329272056762u64, 2517285787892561599u64, 127235208544u64)\n    put(data, 7097729792403256904u64, 4338831817635138102u64, 250084648831u64)\n    put(data, 8553736750439287020u64, 1561495325934523195u64, 183664758778u64)\n    put(data, 2114152625261065696u64, 12262635050079398785u64, 38604121015u64)\n    put(data, 9817523680007641224u64, 11144065765517284187u64, 215065716774u64)\n    put(data, 13047215537500048015u64, 1212260522471875710u64, 63525586135u64)\n    put(data, 16755544192002345880u64, 9695352922247418868u64, 164391777855u64)\n    put(data, 6930119832670648356u64, 7227025834627242947u64, 60520905380u64)\n    put(data, 14560698131901886167u64, 9609008238705447828u64, 127314748u64)\n    put(data, 16408020927503338035u64, 2348542582773833227u64, 0u64)\n    put(data, 14274703510609809116u64, 2348542582u64, 0u64)\n    put(data, 1u64, 0u64, 239195652096u64)\n    put(data, 16428432973129962470u64, 3609144142396852268u64, 54627148527u64)\n    put(data, 3721112279790863774u64, 11568848377382068864u64, 171545803830u64)\n    put(data, 18032764903259620753u64, 10068303578029323956u64, 45631280555u64)\n    put(data, 18058455550468776079u64, 11645070846862630230u64, 167674882605u64)\n    put(data, 15692090139033993190u64, 12449386705878485054u64, 210814540455u64)\n    put(data, 389416944300619393u64, 15025619323517318417u64, 140812947666u64)\n    put(data, 12009691357260487293u64, 14996237555047131271u64, 75553539724u64)\n    put(data, 13494259174449809900u64, 10211005638256058412u64, 90055009355u64)\n    put(data, 18288583400616279877u64, 1014743503555840529u64, 151699359066u64)\n    put(data, 7216107869057472u64, 12900897707145290677u64, 8343u64)\n    put(data, 17237061291959073878u64, 153914086704665u64, 0u64)\n    put(data, 1599418782488783273u64, 153914u64, 0u64)\n    put(data, 1u64, 0u64, 22255763456u64)\n    put(data, 9565464987240335777u64, 4718003016239473661u64, 140805878294u64)\n    put(data, 857713933775880687u64, 14865830648693666724u64, 185799843980u64)\n    put(data, 4621617820081363356u64, 14754517212823091777u64, 155602488249u64)\n    put(data, 9630162611715632528u64, 11113946551474911900u64, 197106442651u64)\n    put(data, 9283986497984645815u64, 1963520352638130629u64, 133723303109u64)\n    put(data, 8981807745082630996u64, 13342587341404964199u64, 29338292357u64)\n    put(data, 18350140531565934622u64, 6240392545013573290u64, 180779405341u64)\n    put(data, 4411619033127524143u64, 14377490861349714757u64, 21093125556u64)\n    put(data, 1852297584111266889u64, 1717863312631397838u64, 9195752981u64)\n    put(data, 11746243463811666096u64, 3611005143890591769u64, 546812681u64)\n    put(data, 6335244004343789147u64, 10086913586276986678u64, 0u64)\n    put(data, 5109502367228239844u64, 10086913586u64, 0u64)\n    put(data, 1603272682579847821u64, 10u64, 0u64)\n    put(data, 1u64, 0u64, 121713852416u64)\n    put(data, 6609546910952910052u64, 13168252824351245503u64, 78039892345u64)\n    put(data, 3911171343112928288u64, 735883891883379687u64, 194575126094u64)\n    put(data, 5254510615100863555u64, 10609203866866106403u64, 60669938882u64)\n    put(data, 3881927570803887650u64, 12358191111890306469u64, 63825615420u64)\n    put(data, 6379348759607163190u64, 15229916368406413527u64, 42392558399u64)\n    put(data, 14595733737222406466u64, 7241424335568075941u64, 154327955754u64)\n    put(data, 14670223432002373542u64, 6049715868779871912u64, 135108449946u64)\n    put(data, 4045087795619708513u64, 2000548404719336761u64, 215076489095u64)\n    put(data, 12598467307137142718u64, 1410974761895205300u64, 28867368919u64)\n    put(data, 734704388050777108u64, 16000132467694084867u64, 251915874844u64)\n    put(data, 5682201693687285822u64, 16894908866816792555u64, 35835u64)\n    put(data, 11048712694145438788u64, 661055968790248u64, 0u64)\n    put(data, 17871025777010319485u64, 661055u64, 0u64)\n    put(data, 1u64, 0u64, 191031934976u64)\n    put(data, 15268761435931663695u64, 589096329272056761u64, 54384768703u64)\n    put(data, 5016238054648555438u64, 7097729792403256903u64, 59463698998u64)\n    put(data, 14236047313993899750u64, 8553736750439287019u64, 129114608443u64)\n    put(data, 6957759675154690848u64, 2114152625261065695u64, 91532209025u64)\n    put(data, 18439367135478514473u64, 9817523680007641223u64, 126707290971u64)\n    put(data, 8539004472540641041u64, 13047215537500048014u64, 244908319870u64)\n    put(data, 1908462039431738399u64, 16755544192002345879u64, 195375682548u64)\n    put(data, 714690453250792146u64, 6930119832670648355u64, 148789337027u64)\n    put(data, 13782189447673929633u64, 14560698131901886166u64, 11889480596u64)\n    put(data, 3584742913798803164u64, 16408020927503338034u64, 118773833227u64)\n    put(data, 4347581515245125291u64, 14274703510609809115u64, 2348542582u64)\n    put(data, 16836742268156371392u64, 6429475823218628948u64, 2u64)\n    put(data, 11764082328865615308u64, 43322963970u64, 0u64)\n    put(data, 5957633711383291746u64, 43u64, 0u64)\n    put(data, 1u64, 0u64, 44890587136u64)\n    put(data, 9917186842884466953u64, 16428432973129962469u64, 128201721900u64)\n    put(data, 4751011869809829335u64, 3721112279790863773u64, 180977558144u64)\n    put(data, 11068497969931435029u64, 18032764903259620752u64, 86978950836u64)\n    put(data, 17118056985122509954u64, 18058455550468776078u64, 62850669910u64)\n    put(data, 14607066080907684459u64, 15692090139033993189u64, 17021110334u64)\n    put(data, 11768892370493391107u64, 389416944300619392u64, 135651046673u64)\n    put(data, 4043396447647747170u64, 12009691357260487292u64, 44731525255u64)\n    put(data, 1670341095362518057u64, 13494259174449809899u64, 17991426092u64)\n    put(data, 3190817644167043165u64, 18288583400616279876u64, 181000391185u64)\n    put(data, 10425820027224322486u64, 7216107869057471u64, 25934422965u64)\n    put(data, 13139964660506311565u64, 17237061291959073877u64, 58086704665u64)\n    put(data, 2297772885416059937u64, 1599418782488783272u64, 153914u64)\n    put(data, 7677687919964523763u64, 2839213766779714u64, 0u64)\n    put(data, 14144589152747892828u64, 2839213u64, 0u64)\n    put(data, 1u64, 0u64, 253518544896u64)\n    put(data, 17069730341503660290u64, 9565464987240335776u64, 164046496765u64)\n    put(data, 18167423787163077107u64, 857713933775880686u64, 65250538404u64)\n    put(data, 3765746945827805904u64, 4621617820081363355u64, 156522052161u64)\n    put(data, 10241734342430761691u64, 9630162611715632527u64, 197503285916u64)\n    put(data, 13345717282537140784u64, 9283986497984645814u64, 103486904773u64)\n    put(data, 9313926784816939953u64, 8981807745082630995u64, 170994763111u64)\n    put(data, 550974205049535019u64, 18350140531565934621u64, 69239154346u64)\n    put(data, 4494692285504086222u64, 4411619033127524142u64, 206100413253u64)\n    put(data, 1134308559863725587u64, 1852297584111266888u64, 25636765134u64)\n    put(data, 17587558045116130233u64, 11746243463811666095u64, 54343434265u64)\n    put(data, 9817142032346161594u64, 6335244004343789146u64, 50276986678u64)\n    put(data, 6071944935834172568u64, 5109502367228239843u64, 10086913586u64)\n    put(data, 11564168293299416955u64, 1603272682579847820u64, 10u64)\n    put(data, 12458266507226064437u64, 186070713419u64, 0u64)\n    put(data, 1304432355328256915u64, 186u64, 0u64)\n    put(data, 1u64, 0u64, 191358304256u64)\n    put(data, 15946798815542087355u64, 6609546910952910051u64, 231212025023u64)\n    put(data, 12082566083831286138u64, 3911171343112928287u64, 35284847591u64)\n    put(data, 11449623684706196411u64, 5254510615100863554u64, 165210439715u64)\n    put(data, 17518743620362604446u64, 3881927570803887649u64, 215345825189u64)\n    put(data, 9451061563087633805u64, 6379348759607163189u64, 165791236311u64)\n    put(data, 13191114787623314926u64, 14595733737222406465u64, 168795274405u64)\n    put(data, 8367349876734474799u64, 14670223432002373541u64, 57219284648u64)\n    put(data, 6544253801674393507u64, 4045087795619708512u64, 180682964281u64)\n    put(data, 16113906253336597498u64, 12598467307137142717u64, 3039828404u64)\n    put(data, 10294087136797312392u64, 734704388050777107u64, 235308032771u64)\n    put(data, 9127173070014462803u64, 5682201693687285821u64, 232598951915u64)\n    put(data, 16266900839595484952u64, 11048712694145438787u64, 63968790248u64)\n    put(data, 3299745387370952632u64, 17871025777010319484u64, 661055u64)\n    put(data, 12061115182604399189u64, 12194330274671844u64, 0u64)\n    put(data, 5066801222582989646u64, 12194330u64, 0u64)\n    put(data, 1u64, 0u64, 185827721216u64)\n    put(data, 7568423425299591513u64, 15268761435931663694u64, 71271930809u64)\n    put(data, 16561505984665207377u64, 5016238054648555437u64, 235771737671u64)\n    put(data, 4329114621856906245u64, 14236047313993899749u64, 223377180907u64)\n    put(data, 1477500474861899139u64, 6957759675154690847u64, 135999600095u64)\n    put(data, 16891579639263969684u64, 18439367135478514472u64, 142462900359u64)\n    put(data, 4684451357140027420u64, 8539004472540641040u64, 151103457934u64)\n    put(data, 14727186580409080709u64, 1908462039431738398u64, 35038743447u64)\n    put(data, 15864176859687308834u64, 714690453250792145u64, 214747133987u64)\n    put(data, 1755486942842684438u64, 13782189447673929632u64, 50194329302u64)\n    put(data, 17417077516652710041u64, 3584742913798803163u64, 219235682866u64)\n    put(data, 4290982361913532783u64, 4347581515245125290u64, 84912721627u64)\n    put(data, 11826659981004351409u64, 16836742268156371391u64, 2637732180u64)\n    put(data, 932930645678090820u64, 11764082328865615307u64, 43322963970u64)\n    put(data, 12707792781328052617u64, 5957633711383291745u64, 43u64)\n    put(data, 16491596426880311906u64, 799167628880u64, 0u64)\n    put(data, 3092207065214166010u64, 799u64, 0u64)\n    put(data, 1u64, 0u64, 229537611776u64)\n    put(data, 8142946531605512550u64, 9917186842884466952u64, 157257552869u64)\n    put(data, 5328402096432654515u64, 4751011869809829334u64, 144600024477u64)\n    put(data, 1932004361303814512u64, 11068497969931435028u64, 142927971728u64)\n    put(data, 2511477647985517771u64, 17118056985122509953u64, 229791850638u64)\n    put(data, 17451375493324716694u64, 14607066080907684458u64, 128637992933u64)\n    put(data, 9489266854478998489u64, 11768892370493391106u64, 124219192960u64)\n    put(data, 8803053132063235169u64, 4043396447647747169u64, 235090549372u64)\n    put(data, 16198682197142616773u64, 1670341095362518056u64, 68172974571u64)\n    put(data, 13696242485403414202u64, 3190817644167043164u64, 191565184836u64)\n    put(data, 16409082426079859931u64, 10425820027224322485u64, 85712318911u64)\n    put(data, 11653410736879597610u64, 13139964660506311564u64, 168124562517u64)\n    put(data, 13589514120653213261u64, 2297772885416059936u64, 66416208296u64)\n    put(data, 8032934885905905774u64, 7677687919964523762u64, 173766779714u64)\n    put(data, 2753021350129449273u64, 14144589152747892827u64, 2839213u64)\n    put(data, 16974897459201404133u64, 52374249726338269u64, 0u64)\n    put(data, 13398576176159101589u64, 52374249u64, 0u64)\n    put(data, 1u64, 0u64, 160925351936u64)\n    put(data, 10284586955251725351u64, 17069730341503660289u64, 238984858016u64)\n    put(data, 5294476488634150891u64, 18167423787163077106u64, 155204141550u64)\n    put(data, 15833244538135063323u64, 3765746945827805903u64, 143555205531u64)\n    put(data, 10348512742273116664u64, 10241734342430761690u64, 182723472783u64)\n    put(data, 13658504610142595663u64, 13345717282537140783u64, 83504908982u64)\n    put(data, 11956362239240850266u64, 9313926784816939952u64, 29029868371u64)\n    put(data, 13415901703662731781u64, 550974205049535018u64, 46243657757u64)\n    put(data, 5161774027546852762u64, 4494692285504086221u64, 72061490990u64)\n    put(data, 15274384838790587711u64, 1134308559863725586u64, 175953423432u64)\n    put(data, 14233354597679374929u64, 17587558045116130232u64, 90532188335u64)\n    put(data, 4274656492162486921u64, 9817142032346161593u64, 227329160794u64)\n    put(data, 12040276505541795046u64, 6071944935834172567u64, 140626894819u64)\n    put(data, 13238307206256765457u64, 11564168293299416954u64, 75675363980u64)\n    put(data, 12850161204172713271u64, 12458266507226064436u64, 186070713419u64)\n    put(data, 17531777095001445154u64, 1304432355328256914u64, 186u64)\n    put(data, 5623628114515245990u64, 3432398830065u64, 0u64)\n    put(data, 7357116143579573377u64, 3432u64, 0u64)\n    put(data, 1u64, 0u64, 227864477696u64)\n    put(data, 3555734177475596582u64, 15946798815542087354u64, 31654997219u64)\n    put(data, 14001876724756424382u64, 12082566083831286137u64, 66620685343u64)\n    put(data, 18159905057231476140u64, 11449623684706196410u64, 33949692994u64)\n    put(data, 5585207679308509467u64, 17518743620362604445u64, 53512343073u64)\n    put(data, 13948697622866724672u64, 9451061563087633804u64, 65715091765u64)\n    put(data, 9807691927739036432u64, 13191114787623314925u64, 165453594945u64)\n    put(data, 15818010096140820918u64, 8367349876734474798u64, 96354764709u64)\n    put(data, 5629845624785010943u64, 6544253801674393506u64, 189873536608u64)\n    put(data, 9517635131137734707u64, 16113906253336597497u64, 19558043581u64)\n    put(data, 619338244618780585u64, 10294087136797312391u64, 61494785043u64)\n    put(data, 11632367007491958899u64, 9127173070014462802u64, 67881830461u64)\n    put(data, 12083314261009739916u64, 16266900839595484951u64, 124178879555u64)\n    put(data, 16880538609458881650u64, 3299745387370952631u64, 228653834364u64)\n    put(data, 17404223674486504228u64, 12061115182604399188u64, 26274671844u64)\n    put(data, 7089067015287185433u64, 5066801222582989645u64, 12194330u64)\n    put(data, 2592264228029443648u64, 224945689727159819u64, 0u64)\n    put(data, 13413731084370224440u64, 224945689u64, 0u64)\n    put(data, 1u64, 0u64, 78410285056u64)\n    put(data, 9323915941641553425u64, 7568423425299591512u64, 173897801038u64)\n    put(data, 12155831029092699564u64, 16561505984665207376u64, 229234681773u64)\n    put(data, 17397171276588232676u64, 4329114621856906244u64, 31080095461u64)\n    put(data, 11874560617553253769u64, 1477500474861899138u64, 40915694367u64)\n    put(data, 13444839516837727954u64, 16891579639263969683u64, 16253944616u64)\n    put(data, 16994416043584590671u64, 4684451357140027419u64, 30798362384u64)\n    put(data, 15879694502877015730u64, 14727186580409080708u64, 209859998750u64)\n    put(data, 4234647645735263359u64, 15864176859687308833u64, 160095165137u64)\n    put(data, 7978589901512919496u64, 1755486942842684437u64, 219944181664u64)\n    put(data, 6114237175390859894u64, 17417077516652710040u64, 170232614619u64)\n    put(data, 8658612872088282708u64, 4290982361913532782u64, 191641124522u64)\n    put(data, 10253813330683324853u64, 11826659981004351408u64, 203050574271u64)\n    put(data, 13289465061747830991u64, 932930645678090819u64, 97688890827u64)\n    put(data, 4123165538545565412u64, 12707792781328052616u64, 80894011233u64)\n    put(data, 7846417485927038481u64, 16491596426880311905u64, 31167628880u64)\n    put(data, 10562273346358018864u64, 3092207065214166009u64, 799u64)\n    put(data, 2691512658346619120u64, 14742040721959u64, 0u64)\n    put(data, 751187558544605998u64, 14742u64, 0u64)\n    put(data, 1u64, 0u64, 8441430016u64)\n    put(data, 3757709791947931308u64, 8142946531605512549u64, 214288853256u64)\n    put(data, 3452755398462519465u64, 5328402096432654514u64, 20104734166u64)\n    put(data, 3105818720159874523u64, 1932004361303814511u64, 129136147476u64)\n    put(data, 16859138458894499364u64, 2511477647985517770u64, 106946040961u64)\n    put(data, 12271894740606233755u64, 17451375493324716693u64, 2514414186u64)\n    put(data, 5429638071845793701u64, 9489266854478998488u64, 97477214466u64)\n    put(data, 145278150038876889u64, 8803053132063235168u64, 40878132321u64)\n    put(data, 9050266019724932450u64, 16198682197142616772u64, 92742474792u64)\n    put(data, 11907016253451490866u64, 13696242485403414201u64, 181889538140u64)\n    put(data, 2472757296513770735u64, 16409082426079859930u64, 140631732661u64)\n    put(data, 10558733798178239360u64, 11653410736879597609u64, 32736689036u64)\n    put(data, 15917322570831255850u64, 13589514120653213260u64, 242435466272u64)\n    put(data, 12254334656791355238u64, 8032934885905905773u64, 91149241586u64)\n    put(data, 7869542424662730262u64, 2753021350129449272u64, 221920211035u64)\n    put(data, 1378558986933000253u64, 16974897459201404132u64, 233726338269u64)\n    put(data, 13521405041909411105u64, 13398576176159101588u64, 52374249u64)\n    put(data, 3206744593298092012u64, 966134380754314586u64, 0u64)\n    put(data, 13914648122214918505u64, 966134380u64, 0u64)\n    put(data, 1u64, 0u64, 1557528576u64)\n    put(data, 1235541077112082496u64, 10284586955251725350u64, 242287014145u64)\n    put(data, 12014985518315533846u64, 5294476488634150890u64, 207858321906u64)\n    put(data, 1561535086344155741u64, 15833244538135063322u64, 218560993999u64)\n    put(data, 12761747276316224577u64, 10348512742273116663u64, 47740429018u64)\n    put(data, 9745594781103966137u64, 13658504610142595662u64, 176648155695u64)\n    put(data, 17514238702394846785u64, 11956362239240850265u64, 42727277488u64)\n    put(data, 2428898913707151713u64, 13415901703662731780u64, 205279820330u64)\n    put(data, 71666709959904945u64, 5161774027546852761u64, 18828026061u64)\n    put(data, 4049380135452919193u64, 15274384838790587710u64, 184771591698u64)\n    put(data, 18422240861777453733u64, 14233354597679374928u64, 185231729592u64)\n    put(data, 2914504416394425696u64, 4274656492162486920u64, 151652704697u64)\n    put(data, 12721377795748989418u64, 12040276505541795045u64, 122717650071u64)\n    put(data, 2626074459217717422u64, 13238307206256765456u64, 52696608634u64)\n    put(data, 4261529925046307655u64, 12850161204172713270u64, 146950399540u64)\n    put(data, 11536038685430305586u64, 17531777095001445153u64, 241304857490u64)\n    put(data, 12555757789435162768u64, 5623628114515245989u64, 104398830065u64)\n    put(data, 11905178684546080059u64, 7357116143579573376u64, 3432u64)\n    put(data, 14032797718924543051u64, 63316582777114u64, 0u64)\n    put(data, 10750340288005853484u64, 63316u64, 0u64)\n    put(data, 1u64, 0u64, 186192756736u64)\n    put(data, 9660290106216358253u64, 3555734177475596581u64, 121759043258u64)\n    put(data, 14820142034615351103u64, 14001876724756424381u64, 186984450425u64)\n    put(data, 12674041783707777619u64, 18159905057231476139u64, 157302774714u64)\n    put(data, 15386686816442679994u64, 5585207679308509466u64, 140756160413u64)\n    put(data, 5679510383719146248u64, 13948697622866724671u64, 237531676044u64)\n    put(data, 1391101719248678506u64, 9807691927739036431u64, 46857496045u64)\n    put(data, 3364596672173710517u64, 15818010096140820917u64, 162305194542u64)\n    put(data, 11276509210104319732u64, 5629845624785010942u64, 249515952034u64)\n    put(data, 5316312656902630164u64, 9517635131137734706u64, 135033574393u64)\n    put(data, 17470981304473644647u64, 619338244618780584u64, 82630591879u64)\n    put(data, 7373293636384920591u64, 11632367007491958898u64, 23655037778u64)\n    put(data, 7616810902585191937u64, 12083314261009739915u64, 183915095831u64)\n    put(data, 12740295655921903924u64, 16880538609458881649u64, 84943484855u64)\n    put(data, 18366635945916526940u64, 17404223674486504227u64, 77384299092u64)\n    put(data, 4472171448243407067u64, 7089067015287185432u64, 11140526925u64)\n    put(data, 229592460858185629u64, 2592264228029443647u64, 25727159819u64)\n    put(data, 12749672866417114996u64, 13413731084370224439u64, 224945689u64)\n    put(data, 9452256722867098693u64, 4149515568880992958u64, 0u64)\n    put(data, 16251451636418604634u64, 4149515568u64, 0u64)\n    put(data, 1u64, 0u64, 88505450496u64)\n    put(data, 4515791283442995454u64, 9323915941641553424u64, 80658968920u64)\n    put(data, 13306155670047701346u64, 12155831029092699563u64, 4943102544u64)\n    put(data, 4456930152933417601u64, 17397171276588232675u64, 130643721220u64)\n    put(data, 9089157128546489637u64, 11874560617553253768u64, 147728846210u64)\n    put(data, 12437332180345515840u64, 13444839516837727953u64, 27921269139u64)\n    put(data, 3433060408790452524u64, 16994416043584590670u64, 132860839963u64)\n    put(data, 8275594526021936172u64, 15879694502877015729u64, 33229560708u64)\n    put(data, 3846512444641107689u64, 4234647645735263358u64, 21432520225u64)\n    put(data, 6210962618469046250u64, 7978589901512919495u64, 152331453461u64)\n    put(data, 7272858906616296575u64, 6114237175390859893u64, 110469384344u64)\n    put(data, 3710743300451225347u64, 8658612872088282707u64, 176555860334u64)\n    put(data, 6424677242672030600u64, 10253813330683324852u64, 67720423344u64)\n    put(data, 11485842256170301862u64, 13289465061747830990u64, 136223517251u64)\n    put(data, 7355797963557024308u64, 4123165538545565411u64, 97425355144u64)\n    put(data, 6358188982569427273u64, 7846417485927038480u64, 249572581985u64)\n    put(data, 12475094728768767402u64, 10562273346358018863u64, 39145907193u64)\n    put(data, 17288154837907896183u64, 2691512658346619119u64, 150040721959u64)\n    put(data, 2983850577727105262u64, 751187558544605997u64, 14742u64)\n    put(data, 13918604635001185935u64, 271942652322184u64, 0u64)\n    put(data, 12033220395769876327u64, 271942u64, 0u64)\n    put(data, 1u64, 0u64, 101203705856u64)\n    put(data, 5782377197813462997u64, 3757709791947931307u64, 178187174245u64)\n    put(data, 17732139848231399226u64, 3452755398462519464u64, 111168366770u64)\n    put(data, 3628839527415562921u64, 3105818720159874522u64, 202913935727u64)\n    put(data, 3188692267613601004u64, 16859138458894499363u64, 149665260746u64)\n    put(data, 5168130193478377352u64, 12271894740606233754u64, 216294341269u64)\n    put(data, 12556227529405091290u64, 5429638071845793700u64, 96007875544u64)\n    put(data, 15087090312791441192u64, 145278150038876888u64, 196490615904u64)\n    put(data, 10281804758610642494u64, 9050266019724932449u64, 185645480644u64)\n    put(data, 14238177586158586580u64, 11907016253451490865u64, 218134048441u64)\n    put(data, 7107927498217678128u64, 2472757296513770734u64, 41572390106u64)\n    put(data, 3845814658485364450u64, 10558733798178239359u64, 76862879785u64)\n    put(data, 714293333681725946u64, 15917322570831255849u64, 109664308812u64)\n    put(data, 16766172658649116982u64, 12254334656791355237u64, 56426608749u64)\n    put(data, 812461421432632215u64, 7869542424662730261u64, 228074731832u64)\n    put(data, 15218024718633799196u64, 1378558986933000252u64, 148732996836u64)\n    put(data, 8110797782612805146u64, 13521405041909411104u64, 90173837972u64)\n    put(data, 15941193964933529227u64, 3206744593298092011u64, 108754314586u64)\n    put(data, 14144280602323277933u64, 13914648122214918504u64, 966134380u64)\n    put(data, 15072402647813125245u64, 17822033662586700072u64, 0u64)\n    put(data, 10822706091283369889u64, 17822033662u64, 0u64)\n    put(data, 15163844593710966731u64, 17u64, 0u64)\n    put(data, 1u64, 0u64, 38066978816u64)\n    put(data, 2408529687792073670u64, 1235541077112082495u64, 234651333670u64)\n    put(data, 3980682212356510808u64, 12014985518315533845u64, 26084650986u64)\n    put(data, 4202670442792148519u64, 1561535086344155740u64, 247691815706u64)\n    put(data, 9419583343154651922u64, 12761747276316224576u64, 78528309751u64)\n    put(data, 16359166491570434575u64, 9745594781103966136u64, 89949448782u64)\n    put(data, 12567727056384237385u64, 17514238702394846784u64, 4131670873u64)\n    put(data, 2068388267923286639u64, 2428898913707151712u64, 153003885060u64)\n    put(data, 5689135844565021196u64, 71666709959904944u64, 62219517337u64)\n    put(data, 3104061965171139313u64, 4049380135452919192u64, 80998671678u64)\n    put(data, 7955173880156328016u64, 18422240861777453732u64, 136157995600u64)\n    put(data, 1445179403240833754u64, 2914504416394425695u64, 229689627272u64)\n    put(data, 12538201164459126715u64, 12721377795748989417u64, 16142359781u64)\n    put(data, 7580606719088482667u64, 2626074459217717421u64, 54231018000u64)\n    put(data, 8168318283218819755u64, 4261529925046307654u64, 33625369910u64)\n    put(data, 5249615277755961676u64, 11536038685430305585u64, 165680648993u64)\n    put(data, 6312997372068219831u64, 12555757789435162767u64, 128645381029u64)\n    put(data, 9183815417025176703u64, 11905178684546080058u64, 26760719488u64)\n    put(data, 10683849953373876937u64, 14032797718924543050u64, 84582777114u64)\n    put(data, 17175012155615667568u64, 10750340288005853483u64, 63316u64)\n    put(data, 18003508288378896912u64, 1167984798111281u64, 0u64)\n    put(data, 14722554560950996951u64, 1167984u64, 0u64)\n    put(data, 1u64, 0u64, 37523685376u64)\n    put(data, 15059324482416394930u64, 9660290106216358252u64, 189803401509u64)\n    put(data, 4134778595813308312u64, 14820142034615351102u64, 171687061181u64)\n    put(data, 16321118342639660948u64, 12674041783707777618u64, 26834113963u64)\n    put(data, 1523550293123468805u64, 15386686816442679993u64, 63307886874u64)\n    put(data, 8016371634569878509u64, 5679510383719146247u64, 15075411775u64)\n    put(data, 9884220139611134110u64, 1391101719248678505u64, 181182395151u64)\n    put(data, 7218073002727840414u64, 3364596672173710516u64, 254611300789u64)\n    put(data, 16062235669481359233u64, 11276509210104319731u64, 50288197886u64)\n    put(data, 15558048660560338002u64, 5316312656902630163u64, 168947103794u64)\n    put(data, 8394398745765058609u64, 17470981304473644646u64, 114399707048u64)\n    put(data, 5693296366442904274u64, 7373293636384920590u64, 139412908146u64)\n    put(data, 11783494675061161358u64, 7616810902585191936u64, 113690652811u64)\n    put(data, 13377293110865447894u64, 12740295655921903923u64, 35995657329u64)\n    put(data, 12840734051093062130u64, 18366635945916526939u64, 24242436899u64)\n    put(data, 7009868331566697505u64, 4472171448243407066u64, 63012446232u64)\n    put(data, 5019690705031194477u64, 229592460858185628u64, 55691161151u64)\n    put(data, 8608277240439804984u64, 12749672866417114995u64, 190512407863u64)\n    put(data, 12172482590657749222u64, 9452256722867098692u64, 48880992958u64)\n    put(data, 16613484892678771990u64, 16251451636418604633u64, 4149515568u64)\n    put(data, 5721488662757049244u64, 2758075434182769113u64, 4u64)\n    put(data, 386931106438877039u64, 76545051729u64, 0u64)\n    put(data, 10054429752182825659u64, 76u64, 0u64)\n    put(data, 1u64, 0u64, 16244801536u64)\n    put(data, 8634592106137071313u64, 4515791283442995453u64, 171721328144u64)\n    put(data, 12626356501369830731u64, 13306155670047701345u64, 227241610667u64)\n    put(data, 4803333258178976933u64, 4456930152933417600u64, 136492724195u64)\n    put(data, 13613083223558209297u64, 9089157128546489636u64, 209674229128u64)\n    put(data, 16106967997237446989u64, 12437332180345515839u64, 78186106577u64)\n    put(data, 14832921244380020170u64, 3433060408790452523u64, 177448620878u64)\n    put(data, 13774024637717231397u64, 8275594526021936171u64, 126208519857u64)\n    put(data, 9673012968505228885u64, 3846512444641107688u64, 199336696958u64)\n    put(data, 5391832334264815667u64, 6210962618469046249u64, 117394262471u64)\n    put(data, 16514436292632703088u64, 7272858906616296574u64, 83201159797u64)\n    put(data, 12025036352783454153u64, 3710743300451225346u64, 180348282451u64)\n    put(data, 7059867105311401050u64, 6424677242672030599u64, 206622648756u64)\n    put(data, 12769210631552594670u64, 11485842256170301861u64, 227398758606u64)\n    put(data, 8328873878884556145u64, 7355797963557024307u64, 16344678115u64)\n    put(data, 1016565892414238685u64, 6358188982569427272u64, 47676276240u64)\n    put(data, 9662978461927250281u64, 12475094728768767401u64, 239937192751u64)\n    put(data, 13729967277551868112u64, 17288154837907896182u64, 45161754863u64)\n    put(data, 6371593776693359475u64, 2983850577727105261u64, 136754529069u64)\n    put(data, 17617208110845643245u64, 13918604635001185934u64, 70652322184u64)\n    put(data, 14960960225633086797u64, 12033220395769876326u64, 271942u64)\n    put(data, 12090634301321662558u64, 5016456510113118u64, 0u64)\n    put(data, 9409926148478635503u64, 5016456u64, 0u64)\n    put(data, 1u64, 0u64, 171313463296u64)\n    put(data, 4307062684900157136u64, 5782377197813462996u64, 168961261227u64)\n    put(data, 15300759383869911853u64, 17732139848231399225u64, 218196719784u64)\n    put(data, 16007534237643445447u64, 3628839527415562920u64, 35172859354u64)\n    put(data, 7138502295759677634u64, 3188692267613601003u64, 154280164899u64)\n    put(data, 8218537071653683708u64, 5168130193478377351u64, 164680674458u64)\n    put(data, 2254219416760329296u64, 12556227529405091289u64, 216817872804u64)\n    put(data, 3057410459568460683u64, 15087090312791441191u64, 97557377752u64)\n    put(data, 8217810929938874370u64, 10281804758610642493u64, 49771853153u64)\n    put(data, 11741126472498340929u64, 14238177586158586579u64, 238385321521u64)\n    put(data, 1175325363726654805u64, 7107927498217678127u64, 127208482030u64)\n    put(data, 9428843070696730900u64, 3845814658485364449u64, 41038721919u64)\n    put(data, 12662500978715131896u64, 714293333681725945u64, 101908896041u64)\n    put(data, 6443045597035184564u64, 16766172658649116981u64, 21044043621u64)\n    put(data, 1921385512639171183u64, 812461421432632214u64, 60824970773u64)\n    put(data, 10469475094355551399u64, 15218024718633799195u64, 32439687228u64)\n    put(data, 14679174489076953574u64, 8110797782612805145u64, 235864173856u64)\n    put(data, 11853074234719825644u64, 15941193964933529226u64, 104766762987u64)\n    put(data, 8270896886596139124u64, 14144280602323277932u64, 40817076584u64)\n    put(data, 16532667046659118126u64, 15072402647813125244u64, 254586700072u64)\n    put(data, 148341279888833483u64, 10822706091283369888u64, 17822033662u64)\n    put(data, 10364629296397276041u64, 15163844593710966730u64, 17u64)\n    put(data, 14265682585545771671u64, 328758493846u64, 0u64)\n    put(data, 13991741872911347878u64, 328u64, 0u64)\n    put(data, 1u64, 0u64, 63130566656u64)\n    put(data, 14029045786848724433u64, 2408529687792073669u64, 21215793215u64)\n    put(data, 4005878521026842341u64, 3980682212356510807u64, 92227827221u64)\n    put(data, 3428326338640386488u64, 4202670442792148518u64, 64510636636u64)\n    put(data, 1010001558294829380u64, 9419583343154651921u64, 184886832192u64)\n    put(data, 2012063724327403418u64, 16359166491570434574u64, 64681297848u64)\n    put(data, 10997154538851372612u64, 12567727056384237384u64, 96112127552u64)\n    put(data, 1917749645489607898u64, 2068388267923286638u64, 176308408672u64)\n    put(data, 9763872523711218805u64, 5689135844565021195u64, 152168271536u64)\n    put(data, 15875699078454059311u64, 3104061965171139312u64, 164431250840u64)\n    put(data, 10966529452671276106u64, 7955173880156328015u64, 95078343332u64)\n    put(data, 18073244132105736913u64, 1445179403240833753u64, 233679697247u64)\n    put(data, 4435241176994913173u64, 12538201164459126714u64, 173410945513u64)\n    put(data, 5464400086219074323u64, 7580606719088482666u64, 70442805421u64)\n    put(data, 2445909179323258812u64, 8168318283218819754u64, 49284582214u64)\n    put(data, 873962058644121211u64, 5249615277755961675u64, 143342228273u64)\n    put(data, 16675872194112650857u64, 6312997372068219830u64, 58497855631u64)\n    put(data, 10680102689274800355u64, 9183815417025176702u64, 74579172666u64)\n    put(data, 2370498083108897524u64, 10683849953373876936u64, 43931059274u64)\n    put(data, 15354400521451334666u64, 17175012155615667567u64, 49975972139u64)\n    put(data, 259991949657381021u64, 18003508288378896911u64, 112798111281u64)\n    put(data, 10335286558772966917u64, 14722554560950996950u64, 1167984u64)\n    put(data, 16337526653906757263u64, 21545516652742137u64, 0u64)\n    put(data, 12040967163702784894u64, 21545516u64, 0u64)\n    put(data, 1u64, 0u64, 108816367616u64)\n    put(data, 3373309160242342187u64, 15059324482416394929u64, 62224146796u64)\n    put(data, 13639841054510584221u64, 4134778595813308311u64, 82884769598u64)\n    put(data, 15898855427739708031u64, 16321118342639660947u64, 185082591826u64)\n    put(data, 4544387940067005419u64, 1523550293123468804u64, 7434568377u64)\n    put(data, 5281598644835398575u64, 8016371634569878508u64, 105535824647u64)\n    put(data, 13675642405083408835u64, 9884220139611134109u64, 180391292521u64)\n    put(data, 3973392623768015721u64, 7218073002727840413u64, 243870735540u64)\n    put(data, 4491285101509114191u64, 16062235669481359232u64, 19843403507u64)\n    put(data, 15002304272810270500u64, 15558048660560338001u64, 102455061267u64)\n    put(data, 17325098540619893468u64, 8394398745765058608u64, 14308634214u64)\n    put(data, 1137212864974584822u64, 5693296366442904273u64, 638784526u64)\n    put(data, 2619406097224859078u64, 11783494675061161357u64, 51725184512u64)\n    put(data, 8281347529729293732u64, 13377293110865447893u64, 91696097587u64)\n    put(data, 11344719666795450104u64, 12840734051093062129u64, 218380005723u64)\n    put(data, 17283870506679425783u64, 7009868331566697504u64, 156272117978u64)\n    put(data, 11054210518010603775u64, 5019690705031194476u64, 115466655644u64)\n    put(data, 6399455551799092885u64, 8608277240439804983u64, 68659871603u64)\n    put(data, 12930529916573967170u64, 12172482590657749221u64, 89900618820u64)\n    put(data, 14550097052337552404u64, 16613484892678771989u64, 217310162521u64)\n    put(data, 12487632712206414748u64, 5721488662757049243u64, 81020975577u64)\n    put(data, 5791017277843595715u64, 386931106438877038u64, 76545051729u64)\n    put(data, 10227264183449036113u64, 10054429752182825658u64, 76u64)\n    put(data, 2006055278511721441u64, 1412006979354u64, 0u64)\n    put(data, 128746359043876333u64, 1412u64, 0u64)\n    put(data, 1u64, 0u64, 253468082176u64)\n    put(data, 7408146306870995754u64, 8634592106137071312u64, 97684476157u64)\n    put(data, 8299024588195267962u64, 12626356501369830730u64, 128260389217u64)\n    put(data, 1497052939192040881u64, 4803333258178976932u64, 36737966720u64)\n    put(data, 16771714264265803747u64, 13613083223558209296u64, 63873160484u64)\n    put(data, 142988846654429432u64, 16106967997237446988u64, 43804094271u64)\n    put(data, 11839838367716104145u64, 14832921244380020169u64, 43746691371u64)\n    put(data, 6019646776647679765u64, 13774024637717231396u64, 232524375083u64)\n    put(data, 4611972391702034948u64, 9673012968505228884u64, 233292291816u64)\n    put(data, 16447182322205429545u64, 5391832334264815666u64, 126895249385u64)\n    put(data, 2113477168726764245u64, 16514436292632703087u64, 2651878526u64)\n    put(data, 3536261187802311516u64, 12025036352783454152u64, 135382716162u64)\n    put(data, 18444381860986709854u64, 7059867105311401049u64, 165692220295u64)\n    put(data, 4734315730275909838u64, 12769210631552594669u64, 51451509157u64)\n    put(data, 9974936316849658174u64, 8328873878884556144u64, 72055108147u64)\n    put(data, 11864423681540657642u64, 1016565892414238684u64, 169523831112u64)\n    put(data, 8207245621417902667u64, 9662978461927250280u64, 118744303017u64)\n    put(data, 7992526918695295028u64, 13729967277551868111u64, 237345404790u64)\n    put(data, 8679354522130259987u64, 6371593776693359474u64, 142955030765u64)\n    put(data, 6065763799692166461u64, 17617208110845643244u64, 102811035278u64)\n    put(data, 18143341109049024976u64, 14960960225633086796u64, 94655434598u64)\n    put(data, 15242492331283350570u64, 12090634301321662557u64, 136510113118u64)\n    put(data, 9986352353182266963u64, 9409926148478635502u64, 5016456u64)\n    put(data, 17340463289911536077u64, 92537289398950870u64, 0u64)\n    put(data, 7359344614214233035u64, 92537289u64, 0u64)\n    put(data, 1u64, 0u64, 212233486336u64)\n    put(data, 419091135888749535u64, 4307062684900157135u64, 57829455828u64)\n    put(data, 1073142712661309790u64, 15300759383869911852u64, 168867770169u64)\n    put(data, 11076438902195672286u64, 16007534237643445446u64, 235386978984u64)\n    put(data, 1820390940081322073u64, 7138502295759677633u64, 135445527787u64)\n    put(data, 18417808973944523597u64, 8218537071653683707u64, 217122201479u64)\n    put(data, 10251294197731582957u64, 2254219416760329295u64, 39165742553u64)\n    put(data, 1502394029870156428u64, 3057410459568460682u64, 61445488423u64)\n    put(data, 321014853559106075u64, 8217810929938874369u64, 211636487741u64)\n    put(data, 2390953058510591778u64, 11741126472498340928u64, 47063714515u64)\n    put(data, 10685224265907994087u64, 1175325363726654804u64, 225511138607u64)\n    put(data, 5967405799190505023u64, 9428843070696730899u64, 249686435553u64)\n    put(data, 11210723659228214761u64, 12662500978715131895u64, 53349278201u64)\n    put(data, 12327123641078462773u64, 6443045597035184563u64, 150104158517u64)\n    put(data, 1709976940107894237u64, 1921385512639171182u64, 27567551382u64)\n    put(data, 16607686590938553511u64, 10469475094355551398u64, 25795759643u64)\n    put(data, 18332088094272679457u64, 14679174489076953573u64, 138642556441u64)\n    put(data, 2946170632136780882u64, 11853074234719825643u64, 108448366218u64)\n    put(data, 4824449494694383419u64, 8270896886596139123u64, 124896237676u64)\n    put(data, 17008332258693407134u64, 16532667046659118125u64, 160008041596u64)\n    put(data, 1773419466622750661u64, 148341279888833482u64, 202561867680u64)\n    put(data, 3892343466023784379u64, 10364629296397276040u64, 150773344202u64)\n    put(data, 12001571085575422796u64, 14265682585545771670u64, 72758493846u64)\n    put(data, 12933506765500977582u64, 13991741872911347877u64, 328u64)\n    put(data, 11884830007749143734u64, 6064523798049u64, 0u64)\n    put(data, 9662368568096205337u64, 6064u64, 0u64)\n    put(data, 1u64, 0u64, 197760516096u64)\n    put(data, 16801499925276664442u64, 14029045786848724432u64, 87217159109u64)\n    put(data, 10492407990787637084u64, 4005878521026842340u64, 38185849943u64)\n    put(data, 7673849751013230269u64, 3428326338640386487u64, 17054752294u64)\n    put(data, 6046724489853072367u64, 1010001558294829379u64, 14109074193u64)\n    put(data, 3723941391207507903u64, 2012063724327403417u64, 72596156942u64)\n    put(data, 16844122108860347659u64, 10997154538851372611u64, 110103961416u64)\n    put(data, 10622020182694668027u64, 1917749645489607897u64, 11529300590u64)\n    put(data, 8741198820686854862u64, 9763872523711218804u64, 240860623371u64)\n    put(data, 6855480461211306807u64, 15875699078454059310u64, 79594496752u64)\n    put(data, 10005708458011566304u64, 10966529452671276105u64, 217979752527u64)\n    put(data, 8932093106442919061u64, 18073244132105736912u64, 186240434905u64)\n    put(data, 9062763476260756743u64, 4435241176994913172u64, 106296225722u64)\n    put(data, 13664977682032775521u64, 5464400086219074322u64, 170132593002u64)\n    put(data, 1078499125430623453u64, 2445909179323258811u64, 75047377578u64)\n    put(data, 6554586738078431161u64, 873962058644121210u64, 182904000843u64)\n    put(data, 12177313698643242883u64, 16675872194112650856u64, 126578969526u64)\n    put(data, 16615072271904633953u64, 10680102689274800354u64, 200128504958u64)\n    put(data, 16375404983106569285u64, 2370498083108897523u64, 111832363720u64)\n    put(data, 13552251831473522729u64, 15354400521451334665u64, 15014094191u64)\n    put(data, 8330500218412111874u64, 259991949657381020u64, 214560277007u64)\n    put(data, 7044338079053294004u64, 10335286558772966916u64, 249885659094u64)\n    put(data, 2688849443046530184u64, 16337526653906757262u64, 44652742137u64)\n    put(data, 855940991879596845u64, 12040967163702784893u64, 21545516u64)\n    put(data, 7344363609485825662u64, 397444631628981487u64, 0u64)\n    put(data, 11602660525134634992u64, 397444631u64, 0u64)\n    put(data, 1u64, 0u64, 177182867456u64)\n    put(data, 16945343208344873835u64, 3373309160242342186u64, 151739417265u64)\n    put(data, 9617992661337889145u64, 13639841054510584220u64, 147861878679u64)\n    put(data, 18280344933262742088u64, 15898855427739708030u64, 4246351763u64)\n    put(data, 5179975582362777795u64, 4544387940067005418u64, 236286316036u64)\n    put(data, 1798918997870037130u64, 5281598644835398574u64, 157741358060u64)\n    put(data, 6327667344756325883u64, 13675642405083408834u64, 157215398045u64)\n    put(data, 18380327574124007701u64, 3973392623768015720u64, 128243473053u64)\n    put(data, 18015447557304295289u64, 4491285101509114190u64, 81813276544u64)\n    put(data, 10315590748073249878u64, 15002304272810270499u64, 48939195473u64)\n    put(data, 7697916092577993382u64, 17325098540619893467u64, 209061648432u64)\n    put(data, 3124132817942110723u64, 1137212864974584821u64, 141141998289u64)\n    put(data, 7448238998520507049u64, 2619406097224859077u64, 213448932749u64)\n    put(data, 13892823322374205297u64, 8281347529729293731u64, 241614998485u64)\n    put(data, 11042137840046332564u64, 11344719666795450103u64, 32936960497u64)\n    put(data, 10513966307445593804u64, 17283870506679425782u64, 108599249952u64)\n    put(data, 9388437460943526958u64, 11054210518010603774u64, 55346915180u64)\n    put(data, 10967228614677896228u64, 6399455551799092884u64, 229700965431u64)\n    put(data, 2310996671540235542u64, 12930529916573967169u64, 21788762341u64)\n    put(data, 4989110555003898587u64, 14550097052337552403u64, 155676955925u64)\n    put(data, 16271452421983657679u64, 12487632712206414747u64, 110313931675u64)\n    put(data, 9523160181437090473u64, 5791017277843595714u64, 186554421102u64)\n    put(data, 13137707423765072250u64, 10227264183449036112u64, 26108748474u64)\n    put(data, 16846859744221860705u64, 2006055278511721440u64, 132006979354u64)\n    put(data, 7767140033449795569u64, 128746359043876332u64, 1412u64)\n    put(data, 17169456915721160017u64, 26046931378436u64, 0u64)\n    put(data, 17180899661833327819u64, 26046u64, 0u64)\n    put(data, 1u64, 0u64, 208401596416u64)\n    put(data, 17572520700934791416u64, 7408146306870995753u64, 74449891024u64)\n    put(data, 17968798858233825417u64, 8299024588195267961u64, 164081155402u64)\n    put(data, 15338423313945305609u64, 1497052939192040880u64, 16909196452u64)\n    put(data, 17895321323836726301u64, 16771714264265803746u64, 76007751440u64)\n    put(data, 814069333008965773u64, 142988846654429431u64, 201641838924u64)\n    put(data, 7200328959852723947u64, 11839838367716104144u64, 36326325705u64)\n    put(data, 759884557248133773u64, 6019646776647679764u64, 84250015524u64)\n    put(data, 13410165861863974851u64, 4611972391702034947u64, 50891603540u64)\n    put(data, 6278452420856351570u64, 16447182322205429544u64, 111114571826u64)\n    put(data, 9072115382556676442u64, 2113477168726764244u64, 200191701103u64)\n    put(data, 2755882551854926563u64, 3536261187802311515u64, 89999871944u64)\n    put(data, 8496072611504649269u64, 18444381860986709853u64, 237256647769u64)\n    put(data, 4122009033579215815u64, 4734315730275909837u64, 112540742381u64)\n    put(data, 10222217724450527221u64, 9974936316849658173u64, 220643171696u64)\n    put(data, 2064539481554006325u64, 11864423681540657641u64, 104444915676u64)\n    put(data, 7935605886598063693u64, 8207245621417902666u64, 207433275752u64)\n    put(data, 7805147585347548429u64, 7992526918695295027u64, 114470508751u64)\n    put(data, 5709020905457661273u64, 8679354522130259986u64, 236328825714u64)\n    put(data, 16257370307404906674u64, 6065763799692166460u64, 76983552492u64)\n    put(data, 14971258192939373646u64, 18143341109049024975u64, 93826297164u64)\n    put(data, 1133404845901376390u64, 15242492331283350569u64, 238541361245u64)\n    put(data, 9460827548162822047u64, 9986352353182266962u64, 214940028398u64)\n    put(data, 1273897659779791346u64, 17340463289911536076u64, 201398950870u64)\n    put(data, 7833262224435092783u64, 7359344614214233034u64, 92537289u64)\n    put(data, 3033420566713364587u64, 1707011694817242694u64, 0u64)\n    put(data, 15075466825360349103u64, 1707011694u64, 0u64)\n    put(data, 1u64, 0u64, 207022718976u64)\n    put(data, 2484134775182816690u64, 419091135888749534u64, 44058175183u64)\n    put(data, 18400539815335991277u64, 1073142712661309789u64, 198600454956u64)\n    put(data, 485494064952118286u64, 11076438902195672285u64, 193098683590u64)\n    put(data, 17577048805241314891u64, 1820390940081322072u64, 251998431425u64)\n    put(data, 2863946907557583807u64, 18417808973944523596u64, 79555723771u64)\n    put(data, 13045307417786230800u64, 10251294197731582956u64, 138081444943u64)\n    put(data, 12032088871615097766u64, 1502394029870156427u64, 1017402250u64)\n    put(data, 8848763446997690580u64, 321014853559106074u64, 64129613825u64)\n    put(data, 10031289150307672684u64, 2390953058510591777u64, 84579247168u64)\n    put(data, 11592215575498656563u64, 10685224265907994086u64, 19323493716u64)\n    put(data, 15894436747956898388u64, 5967405799190505022u64, 247607734547u64)\n    put(data, 2091546719588500923u64, 11210723659228214760u64, 179668254711u64)\n    put(data, 5863809244813756109u64, 12327123641078462772u64, 110092698035u64)\n    put(data, 11303008753675411245u64, 1709976940107894236u64, 166900304494u64)\n    put(data, 13238426537506910532u64, 16607686590938553510u64, 229993784486u64)\n    put(data, 17258458071023005565u64, 18332088094272679456u64, 235159712229u64)\n    put(data, 8385733444777075179u64, 2946170632136780881u64, 115261533931u64)\n    put(data, 9530757096163247300u64, 4824449494694383418u64, 45922023539u64)\n    put(data, 14423000845391072217u64, 17008332258693407133u64, 202096137261u64)\n    put(data, 10953140011159884311u64, 1773419466622750660u64, 136211004362u64)\n    put(data, 12228340237948264127u64, 3892343466023784378u64, 150650606472u64)\n    put(data, 11279134946966259189u64, 12001571085575422795u64, 165701126806u64)\n    put(data, 14640097792684582651u64, 12933506765500977581u64, 33644277925u64)\n    put(data, 6232313315128656728u64, 11884830007749143733u64, 176523798049u64)\n    put(data, 16136121832933322088u64, 9662368568096205336u64, 6064u64)\n    put(data, 15074767079673358271u64, 111870718431542u64, 0u64)\n    put(data, 13252722804829281908u64, 111870u64, 0u64)\n    put(data, 1u64, 0u64, 208910811136u64)\n    put(data, 7740175894281560509u64, 16801499925276664441u64, 228568794576u64)\n    put(data, 15670495392425593226u64, 10492407990787637083u64, 183416000228u64)\n    put(data, 15152257626756992778u64, 7673849751013230268u64, 67327793591u64)\n    put(data, 4090073428152440422u64, 6046724489853072366u64, 153201875267u64)\n    put(data, 14450327772834205584u64, 3723941391207507902u64, 67913121689u64)\n    put(data, 4466091895542494216u64, 16844122108860347658u64, 217575820867u64)\n    put(data, 10454115378553795377u64, 10622020182694668026u64, 116473861337u64)\n    put(data, 2267817233475657788u64, 8741198820686854861u64, 46371636340u64)\n    put(data, 5500455702636497521u64, 6855480461211306806u64, 73542410542u64)\n    put(data, 15178768299492252549u64, 10005708458011566303u64, 208484209737u64)\n    put(data, 7062359872332045590u64, 8932093106442919060u64, 148491293392u64)\n    put(data, 12297347290027942576u64, 9062763476260756742u64, 18740779924u64)\n    put(data, 8030124596941085588u64, 13664977682032775520u64, 187058465554u64)\n    put(data, 6526656990996654843u64, 1078499125430623452u64, 122355324859u64)\n    put(data, 6254287345256979850u64, 6554586738078431160u64, 104660133498u64)\n    put(data, 6642007136244870032u64, 12177313698643242882u64, 226900704872u64)\n    put(data, 2027592955437164718u64, 16615072271904633952u64, 243887712482u64)\n    put(data, 942718349157325567u64, 16375404983106569284u64, 9734669043u64)\n    put(data, 14617066671884002278u64, 13552251831473522728u64, 156451597321u64)\n    put(data, 6831631114396133348u64, 8330500218412111873u64, 4381874332u64)\n    put(data, 14603040013386939258u64, 7044338079053294003u64, 142145762820u64)\n    put(data, 9906106765319401103u64, 2688849443046530183u64, 125046400654u64)\n    put(data, 1396179595609933063u64, 855940991879596844u64, 239398138749u64)\n    put(data, 11524884268464976417u64, 7344363609485825661u64, 23628981487u64)\n    put(data, 382929570730827274u64, 11602660525134634991u64, 397444631u64)\n    put(data, 6109721884461301381u64, 7331559403129590068u64, 0u64)\n    put(data, 2390514825000339691u64, 7331559403u64, 0u64)\n    put(data, 6116191454763441755u64, 7u64, 0u64)\n    put(data, 1u64, 0u64, 42918608896u64)\n    put(data, 11598868771099176310u64, 16945343208344873834u64, 156521392426u64)\n    put(data, 14449966445520085105u64, 9617992661337889144u64, 126990979484u64)\n    put(data, 11675595287405614726u64, 18280344933262742087u64, 234280807038u64)\n    put(data, 15860796398550489897u64, 5179975582362777794u64, 174097519594u64)\n    put(data, 16180408435245829662u64, 1798918997870037129u64, 194343023534u64)\n    put(data, 13756992797154950706u64, 6327667344756325882u64, 104996399554u64)\n    put(data, 8830551328786758466u64, 18380327574124007700u64, 78976619368u64)\n    put(data, 16699955256560951264u64, 18015447557304295288u64, 35559209294u64)\n    put(data, 10038983627153402074u64, 10315590748073249877u64, 219417304867u64)\n    put(data, 15085100736692127346u64, 7697916092577993381u64, 245169359579u64)\n    put(data, 10007783780289711125u64, 3124132817942110722u64, 197403769845u64)\n    put(data, 17596907048353602192u64, 7448238998520507048u64, 163753131461u64)\n    put(data, 13530650344896573509u64, 13892823322374205296u64, 247598595491u64)\n    put(data, 6337724853398437005u64, 11042137840046332563u64, 246569963255u64)\n    put(data, 12768885008904063297u64, 10513966307445593803u64, 254508948214u64)\n    put(data, 2759773619512884114u64, 9388437460943526957u64, 148594534654u64)\n    put(data, 8434364600126655292u64, 10967228614677896227u64, 65125279380u64)\n    put(data, 3843827521199949338u64, 2310996671540235541u64, 19270460225u64)\n    put(data, 4661660852957808994u64, 4989110555003898586u64, 155882077203u64)\n    put(data, 15298044134177324417u64, 16271452421983657678u64, 194516251547u64)\n    put(data, 7747773274913338217u64, 9523160181437090472u64, 80712196546u64)\n    put(data, 10348785912020632966u64, 13137707423765072249u64, 224913270096u64)\n    put(data, 4175372293197190170u64, 16846859744221860704u64, 236421057504u64)\n    put(data, 11326064156813083145u64, 7767140033449795568u64, 4930758124u64)\n    put(data, 8100407170505981763u64, 17169456915721160016u64, 190931378436u64)\n    put(data, 1706556116319916846u64, 17180899661833327818u64, 26046u64)\n    put(data, 15028897280749641942u64, 480481077043500u64, 0u64)\n    put(data, 1421201742071739121u64, 480481u64, 0u64)\n    put(data, 1u64, 0u64, 41952608256u64)\n    put(data, 8480737406125178272u64, 17572520700934791415u64, 121974090537u64)\n    put(data, 10947205650755620361u64, 17968798858233825416u64, 176831497593u64)\n    put(data, 868577942165647781u64, 15338423313945305608u64, 226970107312u64)\n    put(data, 16017710019091388479u64, 17895321323836726300u64, 247044130786u64)\n    put(data, 6610879150827623375u64, 814069333008965772u64, 208390330615u64)\n    put(data, 12110095866223762092u64, 7200328959852723946u64, 20041193424u64)\n    put(data, 7756802952949470775u64, 759884557248133772u64, 3726966548u64)\n    put(data, 2941800790804618759u64, 13410165861863974850u64, 40340355587u64)\n    put(data, 11703600274199927522u64, 6278452420856351569u64, 212491800360u64)\n    put(data, 806737539257940346u64, 9072115382556676441u64, 91149396692u64)\n    put(data, 14579028397110132023u64, 2755882551854926562u64, 93460573019u64)\n    put(data, 14247808875344366934u64, 8496072611504649268u64, 205223454557u64)\n    put(data, 9713379923695279513u64, 4122009033579215814u64, 61554147533u64)\n    put(data, 2246428675703313877u64, 10222217724450527220u64, 233111918909u64)\n    put(data, 3549783776592680620u64, 2064539481554006324u64, 74430190057u64)\n    put(data, 12645029747929213033u64, 7935605886598063692u64, 51423117898u64)\n    put(data, 16279009267476580506u64, 7805147585347548428u64, 18309486643u64)\n    put(data, 343358782242907186u64, 5709020905457661272u64, 60881313810u64)\n    put(data, 10077054739085890321u64, 16257370307404906673u64, 207811593532u64)\n    put(data, 10526715404712173586u64, 14971258192939373645u64, 41061441999u64)\n    put(data, 11438715865125144243u64, 1133404845901376389u64, 82512872489u64)\n    put(data, 5040916178827294801u64, 9460827548162822046u64, 204069058130u64)\n    put(data, 16643761637275849508u64, 1273897659779791345u64, 202424641996u64)\n    put(data, 4852542977279030386u64, 7833262224435092782u64, 70164442058u64)\n    put(data, 7883373066544387129u64, 3033420566713364586u64, 110817242694u64)\n    put(data, 16699064314768500978u64, 15075466825360349102u64, 1707011694u64)\n    put(data, 6805863634444817214u64, 13042063791413317777u64, 1u64)\n    put(data, 2266540253968903500u64, 31488807865u64, 0u64)\n    put(data, 9016913589137908810u64, 31u64, 0u64)\n    put(data, 1u64, 0u64, 222134665216u64)\n    put(data, 11654451024602552034u64, 2484134775182816689u64, 93997495262u64)\n    put(data, 5299013208454526793u64, 18400539815335991276u64, 221026318685u64)\n    put(data, 14918550373926182540u64, 485494064952118285u64, 88952853725u64)\n    put(data, 6225552657491071054u64, 17577048805241314890u64, 76155254872u64)\n    put(data, 10344713496596235785u64, 2863946907557583806u64, 236707187532u64)\n    put(data, 12972405634433280209u64, 13045307417786230799u64, 139652260844u64)\n    put(data, 12911885282402784945u64, 12032088871615097765u64, 26479692427u64)\n    put(data, 6934311832970995868u64, 8848763446997690579u64, 33543797274u64)\n    put(data, 9975729197003430461u64, 10031289150307672683u64, 230628415265u64)\n    put(data, 1982857556803548935u64, 11592215575498656562u64, 62861639142u64)\n    put(data, 2095735223386298223u64, 15894436747956898387u64, 232113382974u64)\n    put(data, 7110931538347639365u64, 2091546719588500922u64, 52317877736u64)\n    put(data, 15822183724630969535u64, 5863809244813756108u64, 220612737332u64)\n    put(data, 16931982690156327501u64, 11303008753675411244u64, 166717656540u64)\n    put(data, 6740069226761666110u64, 13238426537506910531u64, 32935582886u64)\n    put(data, 3138792961008474902u64, 17258458071023005564u64, 81454591520u64)\n    put(data, 12154594426971851390u64, 8385733444777075178u64, 58516663377u64)\n    put(data, 15780127219221910902u64, 9530757096163247299u64, 157781872442u64)\n    put(data, 16421541930960194381u64, 14423000845391072216u64, 196593770909u64)\n    put(data, 7485894627196740576u64, 10953140011159884310u64, 186662899652u64)\n    put(data, 8897269432694476707u64, 12228340237948264126u64, 75611443130u64)\n    put(data, 17189823634941678805u64, 11279134946966259188u64, 173793641291u64)\n    put(data, 9585582064286255216u64, 14640097792684582650u64, 181337854381u64)\n    put(data, 12835472279575022097u64, 6232313315128656727u64, 24874740917u64)\n    put(data, 6776016669542754608u64, 16136121832933322087u64, 54817204760u64)\n    put(data, 18340015775620871027u64, 15074767079673358270u64, 254718431542u64)\n    put(data, 5254188752292365830u64, 13252722804829281907u64, 111870u64)\n    put(data, 6798802596750151183u64, 2063650512248692u64, 0u64)\n    put(data, 9449320530215272000u64, 2063650u64, 0u64)\n    put(data, 1u64, 0u64, 121419595776u64)\n    put(data, 17110720482574968811u64, 7740175894281560508u64, 91849499257u64)\n    put(data, 16172441693558688213u64, 15670495392425593225u64, 188821405531u64)\n    put(data, 6234654946353717320u64, 15152257626756992777u64, 238221723324u64)\n    put(data, 11180283100679445438u64, 4090073428152440421u64, 190783353838u64)\n    put(data, 14852260031176961272u64, 14450327772834205583u64, 10242107326u64)\n    put(data, 4481533167346438750u64, 4466091895542494215u64, 250566718730u64)\n    put(data, 4269718344362365664u64, 10454115378553795376u64, 205122938618u64)\n    put(data, 11520029752381101466u64, 2267817233475657787u64, 54298180301u64)\n    put(data, 16778682550309368417u64, 5500455702636497520u64, 223822842678u64)\n    put(data, 9687587467301363608u64, 15178768299492252548u64, 148382851295u64)\n    put(data, 10093971076828497318u64, 7062359872332045589u64, 6666640532u64)\n    put(data, 1913763026490934696u64, 12297347290027942575u64, 96435313926u64)\n    put(data, 12701450127613557000u64, 8030124596941085587u64, 220353810784u64)\n    put(data, 8974572160711134644u64, 6526656990996654842u64, 184339045596u64)\n    put(data, 9890000077336694124u64, 6254287345256979849u64, 130360063928u64)\n    put(data, 4292326716201059148u64, 6642007136244870031u64, 96109916034u64)\n    put(data, 14644519175104337420u64, 2027592955437164717u64, 68051104864u64)\n    put(data, 5051178622270136798u64, 942718349157325566u64, 40792392772u64)\n    put(data, 675983118348065839u64, 14617066671884002277u64, 1370343464u64)\n    put(data, 4431647660065117244u64, 6831631114396133347u64, 179791632385u64)\n    put(data, 8316115180008411962u64, 14603040013386939257u64, 135537011123u64)\n    put(data, 9621158095544965602u64, 9906106765319401102u64, 44075687047u64)\n    put(data, 15283478958951102072u64, 1396179595609933062u64, 125624765228u64)\n    put(data, 13981553073094447813u64, 11524884268464976416u64, 239020758653u64)\n    put(data, 4558368743929911607u64, 382929570730827273u64, 52331208687u64)\n    put(data, 15217004469858477791u64, 6109721884461301380u64, 235129590068u64)\n    put(data, 11589190369996515737u64, 2390514825000339690u64, 7331559403u64)\n    put(data, 3670624237398152929u64, 6116191454763441754u64, 7u64)\n    put(data, 13471713758418039777u64, 135243399970u64, 0u64)\n    put(data, 4489936967610296411u64, 135u64, 0u64)\n    put(data, 1u64, 0u64, 106628775936u64)\n    put(data, 9052049303222747950u64, 11598868771099176309u64, 120783334250u64)\n    put(data, 1011330006193020538u64, 14449966445520085104u64, 71632935288u64)\n    put(data, 17412075644359478612u64, 11675595287405614725u64, 194859815495u64)\n    put(data, 6358678384745980468u64, 15860796398550489896u64, 137877141698u64)\n    put(data, 15262353928842850919u64, 16180408435245829661u64, 250745768073u64)\n    put(data, 11145257686438581736u64, 13756992797154950705u64, 20478705146u64)\n    put(data, 1600562031807691890u64, 8830551328786758465u64, 120905306388u64)\n    put(data, 6775147337046626724u64, 16699955256560951263u64, 85544214392u64)\n    put(data, 15772127322106297822u64, 10038983627153402073u64, 165817764949u64)\n    put(data, 4141472200527441474u64, 15085100736692127345u64, 2542523045u64)\n    put(data, 18246007807879281267u64, 10007783780289711124u64, 168953930242u64)\n    put(data, 960746958654787123u64, 17596907048353602191u64, 112733498024u64)\n    put(data, 11355981212264408477u64, 13530650344896573508u64, 147343568752u64)\n    put(data, 1573078209576251481u64, 6337724853398437004u64, 203692202643u64)\n    put(data, 6245294478780491367u64, 12768885008904063296u64, 45149607627u64)\n    put(data, 7523292955659721510u64, 2759773619512884113u64, 35457227821u64)\n    put(data, 14454736751015226505u64, 8434364600126655291u64, 21208374307u64)\n    put(data, 7219786377781411316u64, 3843827521199949337u64, 218252709141u64)\n    put(data, 10597123082209392431u64, 4661660852957808993u64, 206829308634u64)\n    put(data, 6922353544343010714u64, 15298044134177324416u64, 168420007630u64)\n    put(data, 14317523356293377430u64, 7747773274913338216u64, 121561008808u64)\n    put(data, 4057766168681892717u64, 10348785912020632965u64, 96226347385u64)\n    put(data, 15214083611901244045u64, 4175372293197190169u64, 240613987168u64)\n    put(data, 8390569016883950721u64, 11326064156813083144u64, 80439123952u64)\n    put(data, 10680472538208175055u64, 8100407170505981762u64, 202092512592u64)\n    put(data, 12173567833130544927u64, 1706556116319916845u64, 44814718154u64)\n    put(data, 1386341248286610026u64, 15028897280749641941u64, 225077043500u64)\n    put(data, 12487300952797237352u64, 1421201742071739120u64, 480481u64)\n    put(data, 2614759871804869720u64, 8863311460481781u64, 0u64)\n    put(data, 8494389567327729477u64, 8863311u64, 0u64)\n    put(data, 1u64, 0u64, 247459741696u64)\n    put(data, 6260469580539185878u64, 8480737406125178271u64, 136593449207u64)\n    put(data, 17818573101084525841u64, 10947205650755620360u64, 8047085704u64)\n    put(data, 2201029069927307150u64, 868577942165647780u64, 28868321800u64)\n    put(data, 10397997613804897039u64, 16017710019091388478u64, 140358376476u64)\n    put(data, 14269915965770103741u64, 6610879150827623374u64, 234656489612u64)\n    put(data, 16776139909196366727u64, 12110095866223762091u64, 140420497130u64)\n    put(data, 6246513436385199720u64, 7756802952949470774u64, 194159475340u64)\n    put(data, 2926026498821554288u64, 2941800790804618758u64, 81634453442u64)\n    put(data, 15725499391028340982u64, 11703600274199927521u64, 89043733329u64)\n    put(data, 8576577277771450827u64, 806737539257940345u64, 226790330713u64)\n    put(data, 15523351176022259335u64, 14579028397110132022u64, 52772375266u64)\n    put(data, 4775158829429176134u64, 14247808875344366933u64, 198526563380u64)\n    put(data, 10141817222123532462u64, 9713379923695279512u64, 244121779142u64)\n    put(data, 12847658900242624586u64, 2246428675703313876u64, 52192434164u64)\n    put(data, 13708197964460514655u64, 3549783776592680619u64, 76685488436u64)\n    put(data, 1951540006613246932u64, 12645029747929213032u64, 12882486860u64)\n    put(data, 9979297327280092199u64, 16279009267476580505u64, 88018613516u64)\n    put(data, 15381307706282553684u64, 343358782242907185u64, 177546278232u64)\n    put(data, 10037428657543061177u64, 10077054739085890320u64, 77570654385u64)\n    put(data, 2584877324547208668u64, 10526715404712173585u64, 133620094029u64)\n    put(data, 1126624732730703576u64, 11438715865125144242u64, 158273268613u64)\n    put(data, 1501064139624981020u64, 5040916178827294800u64, 241902260126u64)\n    put(data, 5219661484955306109u64, 16643761637275849507u64, 46263056881u64)\n    put(data, 5336997298570282212u64, 4852542977279030385u64, 106427358510u64)\n    put(data, 12191131175733833362u64, 7883373066544387128u64, 174905258090u64)\n    put(data, 3707068178994436536u64, 16699064314768500977u64, 145368946606u64)\n    put(data, 5045484691732942022u64, 6805863634444817213u64, 185122869393u64)\n    put(data, 14847900542908711232u64, 2266540253968903499u64, 31488807865u64)\n    put(data, 9097257915916965135u64, 9016913589137908809u64, 31u64)\n    put(data, 2472027983230314217u64, 580865979874u64, 0u64)\n    put(data, 15974509111133272205u64, 580u64, 0u64)\n    put(data, 1u64, 0u64, 177631789056u64)\n    put(data, 12099486841948187399u64, 11654451024602552033u64, 236287260081u64)\n    put(data, 5319910566029976328u64, 5299013208454526792u64, 13808736236u64)\n    put(data, 11549214421017285864u64, 14918550373926182539u64, 74337487885u64)\n    put(data, 1998791413186046700u64, 6225552657491071053u64, 190560788042u64)\n    put(data, 17075171930090011210u64, 10344713496596235784u64, 15703235518u64)\n    put(data, 15158296003813501474u64, 12972405634433280208u64, 165699954703u64)\n    put(data, 1360083178079384115u64, 12911885282402784944u64, 211375909797u64)\n    put(data, 6167980558592741158u64, 6934311832970995867u64, 107540785363u64)\n    put(data, 3630180428124865653u64, 9975729197003430460u64, 50107490923u64)\n    put(data, 2276550099763657677u64, 1982857556803548934u64, 83113610034u64)\n    put(data, 407006713016100655u64, 2095735223386298222u64, 186385484371u64)\n    put(data, 14242579061653496002u64, 7110931538347639364u64, 204857722298u64)\n    put(data, 17944493332678643704u64, 15822183724630969534u64, 44917884620u64)\n    put(data, 987185901870869452u64, 16931982690156327500u64, 67365379884u64)\n    put(data, 5578665155415167745u64, 6740069226761666109u64, 124170154307u64)\n    put(data, 4849210377429577536u64, 3138792961008474901u64, 234658901884u64)\n    put(data, 10811995403388891862u64, 12154594426971851389u64, 195855442410u64)\n    put(data, 7051931074990177294u64, 15780127219221910901u64, 216890213571u64)\n    put(data, 2030832259446664275u64, 16421541930960194380u64, 22405811160u64)\n    put(data, 6069512651054767896u64, 7485894627196740575u64, 190482321942u64)\n    put(data, 10608701253763958799u64, 8897269432694476706u64, 244931862206u64)\n    put(data, 15700053443426906717u64, 17189823634941678804u64, 250519635444u64)\n    put(data, 17759719234725541222u64, 9585582064286255215u64, 87695812346u64)\n    put(data, 15187321568916405210u64, 12835472279575022096u64, 103367328599u64)\n    put(data, 11040156458113129594u64, 6776016669542754607u64, 190994214247u64)\n    put(data, 2800727824598008497u64, 18340015775620871026u64, 115284830142u64)\n    put(data, 2997236166375604479u64, 5254188752292365829u64, 116368563827u64)\n    put(data, 6260091886451512841u64, 6798802596750151182u64, 34512248692u64)\n    put(data, 17573059315228347474u64, 9449320530215271999u64, 2063650u64)\n    put(data, 7519453664590169251u64, 38067632857031246u64, 0u64)\n    put(data, 15809436065653866529u64, 38067632u64, 0u64)\n    put(data, 1u64, 0u64, 188927574016u64)\n    put(data, 228921437623588922u64, 17110720482574968810u64, 137876709820u64)\n    put(data, 2195862230003073884u64, 16172441693558688212u64, 9337981321u64)\n    put(data, 960207412233973688u64, 6234654946353717319u64, 101606084361u64)\n    put(data, 2464387149230492479u64, 11180283100679445437u64, 143805142629u64)\n    put(data, 3631866936444955213u64, 14852260031176961271u64, 7242944399u64)\n    put(data, 1578304441149380227u64, 4481533167346438749u64, 48231461895u64)\n    put(data, 18190538519673445181u64, 4269718344362365663u64, 59624502064u64)\n    put(data, 1271000736479934749u64, 11520029752381101465u64, 112909574203u64)\n    put(data, 18292963032817745634u64, 16778682550309368416u64, 132525165168u64)\n    put(data, 17168014021925537455u64, 9687587467301363607u64, 21547195268u64)\n    put(data, 18046757712870378949u64, 10093971076828497317u64, 175103745301u64)\n    put(data, 14857998893911743220u64, 1913763026490934695u64, 147688546991u64)\n    put(data, 11933607369968684575u64, 12701450127613556999u64, 250486512531u64)\n    put(data, 3483798509902859162u64, 8974572160711134643u64, 137536137978u64)\n    put(data, 7378828438829845831u64, 9890000077336694123u64, 143232687497u64)\n    put(data, 15791137430347699565u64, 4292326716201059147u64, 173793880975u64)\n    put(data, 17044141236829932641u64, 14644519175104337419u64, 254273824941u64)\n    put(data, 9075651910862456484u64, 5051178622270136797u64, 229036645118u64)\n    put(data, 17811207355884564095u64, 675983118348065838u64, 227240240101u64)\n    put(data, 4438638126207305937u64, 4431647660065117243u64, 121450817507u64)\n    put(data, 12507972635512950185u64, 8316115180008411961u64, 142521564025u64)\n    put(data, 14658269128098109408u64, 9621158095544965601u64, 6828519054u64)\n    put(data, 3642436268910286111u64, 15283478958951102071u64, 32757941510u64)\n    put(data, 3783099432964819561u64, 13981553073094447812u64, 9247109664u64)\n    put(data, 9497579866027539638u64, 4558368743929911606u64, 132824915465u64)\n    put(data, 3395179445046271361u64, 15217004469858477790u64, 234628251268u64)\n    put(data, 5938502732309497276u64, 11589190369996515736u64, 90198984938u64)\n    put(data, 5793671185917606255u64, 3670624237398152928u64, 34730303066u64)\n    put(data, 889272970253526588u64, 13471713758418039776u64, 135243399970u64)\n    put(data, 8594177504370135501u64, 4489936967610296410u64, 135u64)\n    put(data, 7374354721120724712u64, 2494800386918u64, 0u64)\n    put(data, 14764532643665507567u64, 2494u64, 0u64)\n    put(data, 1u64, 0u64, 117490712576u64)\n    put(data, 5392404173658087695u64, 9052049303222747949u64, 112054824309u64)\n    put(data, 4976586473237854316u64, 1011330006193020537u64, 133943910512u64)\n    put(data, 6308932742419013569u64, 17412075644359478611u64, 40344704645u64)\n    put(data, 4831846642430703059u64, 6358678384745980467u64, 29827373864u64)\n    put(data, 18139507855949846901u64, 15262353928842850918u64, 49604185629u64)\n    put(data, 4865833876326628410u64, 11145257686438581735u64, 65086766641u64)\n    put(data, 14296661839130179261u64, 1600562031807691889u64, 223367281473u64)\n    put(data, 9254773150378118248u64, 6775147337046626723u64, 217855008735u64)\n    put(data, 12174712433727875143u64, 15772127322106297821u64, 113224509657u64)\n    put(data, 705653145340915199u64, 4141472200527441473u64, 20989118065u64)\n    put(data, 17763928858962481812u64, 18246007807879281266u64, 143052082196u64)\n    put(data, 3982836567612046296u64, 960746958654787122u64, 68615608975u64)\n    put(data, 12730849277561967739u64, 11355981212264408476u64, 140085276740u64)\n    put(data, 17314488764367235908u64, 1573078209576251480u64, 64338558092u64)\n    put(data, 15951418930590301119u64, 6245294478780491366u64, 145407838528u64)\n    put(data, 7193356087283467261u64, 7523292955659721509u64, 59783592849u64)\n    put(data, 17592945625696089446u64, 14454736751015226504u64, 25391385403u64)\n    put(data, 3554461664875361428u64, 7219786377781411315u64, 97574471193u64)\n    put(data, 2213779057785318208u64, 10597123082209392430u64, 128375261537u64)\n    put(data, 3880940796082421148u64, 6922353544343010713u64, 104776154496u64)\n    put(data, 4528237545358141043u64, 14317523356293377429u64, 133219971944u64)\n    put(data, 11681196539088147363u64, 4057766168681892716u64, 25824757125u64)\n    put(data, 9835005502912643017u64, 15214083611901244044u64, 8454853657u64)\n    put(data, 4964088126040986696u64, 8390569016883950720u64, 66578989576u64)\n    put(data, 3355564873147047622u64, 10680472538208175054u64, 45659930434u64)\n    put(data, 1853093467828272927u64, 12173567833130544926u64, 213075153709u64)\n    put(data, 14755341584803008677u64, 1386341248286610025u64, 240676937941u64)\n    put(data, 4701571132542556621u64, 12487300952797237351u64, 245141746416u64)\n    put(data, 6128849686644853851u64, 2614759871804869719u64, 79460481781u64)\n    put(data, 12026867901170202094u64, 8494389567327729476u64, 8863311u64)\n    put(data, 17909760324981426303u64, 163499238157084246u64, 0u64)\n    put(data, 2897692901883393664u64, 163499238u64, 0u64)\n    put(data, 1u64, 0u64, 159339380736u64)\n    put(data, 12323704802554838154u64, 6260469580539185877u64, 8965946783u64)\n    put(data, 7135886931147821732u64, 17818573101084525840u64, 164119318024u64)\n    put(data, 15341283120292884947u64, 2201029069927307149u64, 62563676580u64)\n    put(data, 3092789040392634166u64, 10397997613804897038u64, 206773573694u64)\n    put(data, 8811761390822097865u64, 14269915965770103740u64, 171909436366u64)\n    put(data, 16870860798610218169u64, 16776139909196366726u64, 54338624171u64)\n    put(data, 17452041453591904833u64, 6246513436385199719u64, 6158620214u64)\n    put(data, 10314783684009874908u64, 2926026498821554287u64, 225852481030u64)\n    put(data, 4932636630789274903u64, 15725499391028340981u64, 121464937185u64)\n    put(data, 18143884346082124480u64, 8576577277771450826u64, 54841522553u64)\n    put(data, 2823209155405527322u64, 15523351176022259334u64, 85258861878u64)\n    put(data, 16195396106620226251u64, 4775158829429176133u64, 152549789013u64)\n    put(data, 1150544491807648944u64, 10141817222123532461u64, 212696472984u64)\n    put(data, 7767455475523884824u64, 12847658900242624585u64, 171743122900u64)\n    put(data, 15204378045683991808u64, 13708197964460514654u64, 104105793195u64)\n    put(data, 17239732561718805622u64, 1951540006613246931u64, 153540978792u64)\n    put(data, 12886430624522800062u64, 9979297327280092198u64, 49833822361u64)\n    put(data, 18162250541178258136u64, 15381307706282553683u64, 16544130097u64)\n    put(data, 17028935366700158084u64, 10037428657543061176u64, 17140126480u64)\n    put(data, 16075467823964198637u64, 2584877324547208667u64, 178061074449u64)\n    put(data, 9803858825574498304u64, 1126624732730703575u64, 80081372850u64)\n    put(data, 17464070808143041817u64, 1501064139624981019u64, 35282958416u64)\n    put(data, 17682703471239266776u64, 5219661484955306108u64, 113289319203u64)\n    put(data, 18147688354161351336u64, 5336997298570282211u64, 56660882545u64)\n    put(data, 6663423873348080051u64, 12191131175733833361u64, 241200960568u64)\n    put(data, 9417270363716235133u64, 3707068178994436535u64, 61273516273u64)\n    put(data, 9295013721571344179u64, 5045484691732942021u64, 75804906301u64)\n    put(data, 6199479138350037783u64, 14847900542908711231u64, 73493163339u64)\n    put(data, 887603005365085688u64, 9097257915916965134u64, 226134008905u64)\n    put(data, 333989628642975696u64, 2472027983230314216u64, 68865979874u64)\n    put(data, 4620735991403939439u64, 15974509111133272204u64, 580u64)\n    put(data, 12418523063962801201u64, 10715086071862u64, 0u64)\n    put(data, 1587745622680169419u64, 10715u64, 0u64)\n    put(data, 1u64, 0u64, 225655914496u64)\n    put(data, 10968905082284365638u64, 12099486841948187398u64, 72288392929u64)\n    put(data, 14076907092801977812u64, 5319910566029976327u64, 139626084168u64)\n    put(data, 3438322122816124202u64, 11549214421017285863u64, 77108354699u64)\n    put(data, 14645413324829073676u64, 1998791413186046699u64, 8925646925u64)\n    put(data, 12271281439492289999u64, 17075171930090011209u64, 208821732872u64)\n    put(data, 6233751789862708246u64, 15158296003813501473u64, 176073730256u64)\n    put(data, 1962644459455827991u64, 1360083178079384114u64, 155334366896u64)\n    put(data, 8726934184642952500u64, 6167980558592741157u64, 60196792475u64)\n    put(data, 4531087719737475147u64, 3630180428124865652u64, 6123412028u64)\n    put(data, 481513520412720775u64, 2276550099763657676u64, 110022063878u64)\n    put(data, 992149349835802669u64, 407006713016100654u64, 68772091758u64)\n    put(data, 11165474436676191361u64, 14242579061653496001u64, 190972772932u64)\n    put(data, 10240785855143707184u64, 17944493332678643703u64, 76053515454u64)\n    put(data, 10059329918238932466u64, 987185901870869451u64, 61302420044u64)\n    put(data, 14791716450947031886u64, 5578665155415167744u64, 21262876221u64)\n    put(data, 15378882314737417403u64, 4849210377429577535u64, 125586119445u64)\n    put(data, 14726970229242271128u64, 10811995403388891861u64, 117382285949u64)\n    put(data, 5090110549507128156u64, 7051931074990177293u64, 76110091637u64)\n    put(data, 17185220781106503841u64, 2030832259446664274u64, 223329028940u64)\n    put(data, 9858517691519529306u64, 6069512651054767895u64, 162575098847u64)\n    put(data, 5595905546638020703u64, 10608701253763958798u64, 212851101602u64)\n    put(data, 15555173226968030256u64, 15700053443426906716u64, 111962756308u64)\n    put(data, 10745236628845355771u64, 17759719234725541221u64, 16823306351u64)\n    put(data, 9973314042399760760u64, 15187321568916405209u64, 47598488080u64)\n    put(data, 4374506813558796576u64, 11040156458113129593u64, 114151827759u64)\n    put(data, 15960826480426749933u64, 2800727824598008496u64, 5162480498u64)\n    put(data, 9636454862798615738u64, 2997236166375604478u64, 14339360261u64)\n    put(data, 17973331528911319269u64, 6260091886451512840u64, 63952637454u64)\n    put(data, 7366495200039369602u64, 17573059315228347473u64, 78407630399u64)\n    put(data, 10505831326526933399u64, 7519453664590169250u64, 176857031246u64)\n    put(data, 2803218632575724145u64, 15809436065653866528u64, 38067632u64)\n    put(data, 8425731874431741636u64, 702223880805592151u64, 0u64)\n    put(data, 14860552245711912111u64, 702223880u64, 0u64)\n    put(data, 1u64, 0u64, 234012409856u64)\n    put(data, 6993664200669526994u64, 228921437623588921u64, 212119037930u64)\n    put(data, 4065363582031999356u64, 2195862230003073883u64, 71052052948u64)\n    put(data, 6899780515342669867u64, 960207412233973687u64, 189133594695u64)\n    put(data, 17713500890201844939u64, 2464387149230492478u64, 247196883901u64)\n    put(data, 6445781125105107086u64, 3631866936444955212u64, 93085560055u64)\n    put(data, 13563044070717478571u64, 1578304441149380226u64, 223986111069u64)\n    put(data, 13167612994149348885u64, 18190538519673445180u64, 153068901087u64)\n    put(data, 5505463469596727288u64, 1271000736479934748u64, 96991663513u64)\n    put(data, 12125446212518819372u64, 18292963032817745633u64, 151930679904u64)\n    put(data, 12537707724735421794u64, 17168014021925537454u64, 165978316695u64)\n    put(data, 15173675086703777069u64, 18046757712870378948u64, 167805453733u64)\n    put(data, 13535510174093048476u64, 14857998893911743219u64, 7646922151u64)\n    put(data, 10698912997087096629u64, 11933607369968684574u64, 179188857095u64)\n    put(data, 16952559548431933861u64, 3483798509902859161u64, 107400007091u64)\n    put(data, 13528255827744249993u64, 7378828438829845830u64, 75856039275u64)\n    put(data, 14122167436324771955u64, 15791137430347699564u64, 11923964747u64)\n    put(data, 13071007137740038297u64, 17044141236829932640u64, 221491992075u64)\n    put(data, 13011887609328904025u64, 9075651910862456483u64, 46965547485u64)\n    put(data, 3116434332871336590u64, 17811207355884564094u64, 59240619054u64)\n    put(data, 9050993820536772770u64, 4438638126207305936u64, 57678058555u64)\n    put(data, 11993719123438634238u64, 12507972635512950184u64, 225794626361u64)\n    put(data, 1414857165879849301u64, 14658269128098109407u64, 119197456865u64)\n    put(data, 13819438220812375094u64, 3642436268910286110u64, 196205082231u64)\n    put(data, 6073063033888264440u64, 3783099432964819560u64, 54514864836u64)\n    put(data, 6828883869150720294u64, 9497579866027539637u64, 222184053046u64)\n    put(data, 4548265621068768345u64, 3395179445046271360u64, 152321926878u64)\n    put(data, 10422524923581371874u64, 5938502732309497275u64, 224314075544u64)\n    put(data, 1858996082510682634u64, 5793671185917606254u64, 224048207584u64)\n    put(data, 890276727450878316u64, 889272970253526587u64, 90465891296u64)\n    put(data, 3886008133802710905u64, 8594177504370135500u64, 102399764570u64)\n    put(data, 612074409233016757u64, 7374354721120724711u64, 190800386918u64)\n    put(data, 3927020336901729264u64, 14764532643665507566u64, 2494u64)\n    put(data, 5298603480094474942u64, 46020944252475u64, 0u64)\n    put(data, 17418383752590430025u64, 46020u64, 0u64)\n    put(data, 1u64, 0u64, 45292322816u64)\n    put(data, 8973799690601597929u64, 5392404173658087694u64, 121269781293u64)\n    put(data, 1343055462055792431u64, 4976586473237854315u64, 83342007929u64)\n    put(data, 17425118728683169659u64, 6308932742419013568u64, 51261934931u64)\n    put(data, 18389781726026675967u64, 4831846642430703058u64, 102983344691u64)\n    put(data, 272526939565961561u64, 18139507855949846900u64, 231263777382u64)\n    put(data, 11293026845930963228u64, 4865833876326628409u64, 113775023591u64)\n    put(data, 13997416438903902597u64, 14296661839130179260u64, 163501702257u64)\n    put(data, 6186605805999441184u64, 9254773150378118247u64, 221659992483u64)\n    put(data, 4401776373281836138u64, 12174712433727875142u64, 65038253533u64)\n    put(data, 16338917089754547008u64, 705653145340915198u64, 114962984513u64)\n    put(data, 13337700757935003056u64, 17763928858962481811u64, 50215910002u64)\n    put(data, 14612496890816348693u64, 3982836567612046295u64, 156690140722u64)\n    put(data, 3219935399907691719u64, 12730849277561967738u64, 88938620316u64)\n    put(data, 10887238730052330387u64, 17314488764367235907u64, 102864728152u64)\n    put(data, 360256418697768294u64, 15951418930590301118u64, 37389952614u64)\n    put(data, 321440824631118565u64, 7193356087283467260u64, 136953715493u64)\n    put(data, 10069228080701402580u64, 17592945625696089445u64, 243192687752u64)\n    put(data, 9428069607611622975u64, 3554461664875361427u64, 46120009203u64)\n    put(data, 14736799017468812344u64, 2213779057785318207u64, 153210386222u64)\n    put(data, 10875332567307979280u64, 3880940796082421147u64, 149245476249u64)\n    put(data, 4611492910339012807u64, 4528237545358141042u64, 108633238933u64)\n    put(data, 10743508637597314786u64, 11681196539088147362u64, 140533156716u64)\n    put(data, 9356196315668016028u64, 9835005502912643016u64, 128269103756u64)\n    put(data, 15755598617722189347u64, 4964088126040986695u64, 206181905536u64)\n    put(data, 1275276394173375542u64, 3355564873147047621u64, 30100456398u64)\n    put(data, 12644999363867216251u64, 1853093467828272926u64, 105799888670u64)\n    put(data, 4553830511509832021u64, 14755341584803008676u64, 103254872681u64)\n    put(data, 8869400642218174412u64, 4701571132542556620u64, 87332245607u64)\n    put(data, 16570849151159054040u64, 6128849686644853850u64, 68651977815u64)\n    put(data, 16127119334101797673u64, 12026867901170202093u64, 86970890052u64)\n    put(data, 9686867250420930550u64, 17909760324981426302u64, 230157084246u64)\n    put(data, 10678226869774428035u64, 2897692901883393663u64, 163499238u64)\n    put(data, 7767227962910162068u64, 3016028602530220424u64, 0u64)\n    put(data, 9780840471948993674u64, 3016028602u64, 0u64)\n    put(data, 1u64, 0u64, 213668069376u64)\n    put(data, 6288709332106746357u64, 12323704802554838153u64, 16386837205u64)\n    put(data, 9066785620141948673u64, 7135886931147821731u64, 141831652624u64)\n    put(data, 8442375916704414909u64, 15341283120292884946u64, 14167660429u64)\n    put(data, 11604629218100425803u64, 3092789040392634165u64, 188477686542u64)\n    put(data, 3877248044010875762u64, 8811761390822097864u64, 134914571196u64)\n    put(data, 16435137704395217283u64, 16870860798610218168u64, 103946077062u64)\n    put(data, 14994442577577813271u64, 17452041453591904832u64, 111559165543u64)\n    put(data, 4410105917142436089u64, 10314783684009874907u64, 245267398767u64)\n    put(data, 4632574728444936970u64, 4932636630789274902u64, 202983581941u64)\n    put(data, 9117147535650050359u64, 18143884346082124479u64, 134153046474u64)\n    put(data, 588939301256904809u64, 2823209155405527321u64, 69877954182u64)\n    put(data, 324393982565305683u64, 16195396106620226250u64, 173062371141u64)\n    put(data, 9380909186923521175u64, 1150544491807648943u64, 73421074605u64)\n    put(data, 4463385697777230217u64, 7767455475523884823u64, 94824230985u64)\n    put(data, 16378985502426333808u64, 15204378045683991807u64, 211934567774u64)\n    put(data, 18210894922387834354u64, 17239732561718805621u64, 38698574803u64)\n    put(data, 1555748035329493205u64, 12886430624522800061u64, 83984577574u64)\n    put(data, 4277055533891898507u64, 18162250541178258135u64, 184923140435u64)\n    put(data, 11574429772510874408u64, 17028935366700158083u64, 219871452856u64)\n    put(data, 17391099253493808815u64, 16075467823964198636u64, 215531468251u64)\n    put(data, 5791212393959129882u64, 9803858825574498303u64, 27946729175u64)\n    put(data, 11254268231455680880u64, 17464070808143041816u64, 124958581275u64)\n    put(data, 16355477587312235322u64, 17682703471239266775u64, 227983788156u64)\n    put(data, 2411485149249320633u64, 18147688354161351335u64, 145361224931u64)\n    put(data, 12763114642070638360u64, 6663423873348080050u64, 183510511249u64)\n    put(data, 1147543073987366419u64, 9417270363716235132u64, 197503883703u64)\n    put(data, 8410777835225272692u64, 9295013721571344178u64, 63336074437u64)\n    put(data, 8134725822306818018u64, 6199479138350037782u64, 14048117055u64)\n    put(data, 8899607004752328377u64, 887603005365085687u64, 232018105614u64)\n    put(data, 690976506652396830u64, 333989628642975695u64, 140250490600u64)\n    put(data, 12281570945595192074u64, 4620735991403939438u64, 54673209484u64)\n    put(data, 12592957291365552899u64, 12418523063962801200u64, 219086071862u64)\n    put(data, 13595807339013970272u64, 1587745622680169418u64, 10715u64)\n    put(data, 9698096389749839992u64, 197658450495420u64, 0u64)\n    put(data, 8310173728816391804u64, 197658u64, 0u64)\n    data\n  end\n\n  private POW10_OFFSET_2 = [\n    0, 2, 6, 12, 20, 29, 40, 52, 66, 80,\n    95, 112, 130, 150, 170, 192, 215, 240, 265, 292,\n    320, 350, 381, 413, 446, 480, 516, 552, 590, 629,\n    670, 712, 755, 799, 845, 892, 940, 989, 1040, 1092,\n    1145, 1199, 1254, 1311, 1369, 1428, 1488, 1550, 1613, 1678,\n    1743, 1810, 1878, 1947, 2017, 2088, 2161, 2235, 2311, 2387,\n    2465, 2544, 2625, 2706, 2789, 2873, 2959, 3046, 3133,\n  ] of UInt16\n\n  private MIN_BLOCK_2 = [\n    0, 0, 0, 0, 0, 0, 1, 1, 2, 3,\n    3, 4, 4, 5, 5, 6, 6, 7, 7, 8,\n    8, 9, 9, 10, 11, 11, 12, 12, 13, 13,\n    14, 14, 15, 15, 16, 16, 17, 17, 18, 19,\n    19, 20, 20, 21, 21, 22, 22, 23, 23, 24,\n    24, 25, 26, 26, 27, 27, 28, 28, 29, 29,\n    30, 30, 31, 31, 32, 32, 33, 34, 0,\n  ] of UInt8\n\n  private POW10_SPLIT_2 = begin\n    data = Array({UInt64, UInt64, UInt64}).new(3133)\n    put(data, 0u64, 0u64, 3906250u64)\n    put(data, 0u64, 0u64, 202000000000u64)\n    put(data, 0u64, 11153727427136454656u64, 59u64)\n    put(data, 0u64, 7205759403792793600u64, 59604644775u64)\n    put(data, 0u64, 0u64, 167390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 0u64, 16777216000000000u64, 0u64)\n    put(data, 0u64, 12945425605062557696u64, 909494u64)\n    put(data, 0u64, 4388757836872548352u64, 182701772928u64)\n    put(data, 0u64, 1152921504606846976u64, 128237915039u64)\n    put(data, 0u64, 0u64, 159062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 0u64, 256000000000u64, 0u64)\n    put(data, 0u64, 16192327041775828992u64, 13u64)\n    put(data, 0u64, 15024075324038053888u64, 13877787807u64)\n    put(data, 0u64, 5449091666327633920u64, 159814456755u64)\n    put(data, 0u64, 2494994193563254784u64, 179295395851u64)\n    put(data, 0u64, 4611686018427387904u64, 11135253906u64)\n    put(data, 0u64, 0u64, 146250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 0u64, 3906250u64, 0u64)\n    put(data, 0u64, 3906250000000000u64, 0u64)\n    put(data, 0u64, 4368439412768899072u64, 211758u64)\n    put(data, 0u64, 1563676642168012800u64, 46236813575u64)\n    put(data, 0u64, 11532349341402398720u64, 7084767080u64)\n    put(data, 0u64, 9048364970084925440u64, 104625169910u64)\n    put(data, 0u64, 16609275425742389248u64, 246490512847u64)\n    put(data, 0u64, 0u64, 207900390625u64)\n    put(data, 0u64, 0u64, 225000000000u64)\n    put(data, 11153727427136454656u64, 59u64, 0u64)\n    put(data, 7205759403792793600u64, 59604644775u64, 0u64)\n    put(data, 0u64, 4264412554261970152u64, 3u64)\n    put(data, 0u64, 14485570586272534528u64, 3231174267u64)\n    put(data, 0u64, 17827675094632103936u64, 123785264354u64)\n    put(data, 0u64, 7347197909193981952u64, 226966440203u64)\n    put(data, 0u64, 13677404030777688064u64, 11398292396u64)\n    put(data, 0u64, 3810326759732150272u64, 172741453558u64)\n    put(data, 0u64, 9943947977234055168u64, 246206558227u64)\n    put(data, 0u64, 0u64, 19539062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 12945425605062557696u64, 909494u64, 0u64)\n    put(data, 4388757836872548352u64, 909494701772928u64, 0u64)\n    put(data, 1152921504606846976u64, 14878706826214591391u64, 49303u64)\n    put(data, 0u64, 4387341015746028192u64, 151806576313u64)\n    put(data, 0u64, 651726680428265472u64, 185237838233u64)\n    put(data, 0u64, 2570638187944738816u64, 153035330174u64)\n    put(data, 0u64, 7419175577111756800u64, 126139354575u64)\n    put(data, 0u64, 17299322326264840192u64, 207402194313u64)\n    put(data, 0u64, 7990511638862102528u64, 137937798142u64)\n    put(data, 0u64, 16717361816799281152u64, 254433166503u64)\n    put(data, 0u64, 0u64, 167906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 16192327041775828992u64, 13u64, 0u64)\n    put(data, 15024075324038053888u64, 13877787807u64, 0u64)\n    put(data, 5449091666327633920u64, 13877787807814456755u64, 0u64)\n    put(data, 2494994193563254784u64, 9707857417284919307u64, 752316384u64)\n    put(data, 4611686018427387904u64, 1844515466944871826u64, 224526264005u64)\n    put(data, 0u64, 15167599819856275072u64, 197099991383u64)\n    put(data, 0u64, 14830185305589481472u64, 87822237233u64)\n    put(data, 0u64, 6163721531743535104u64, 49803945956u64)\n    put(data, 0u64, 14122847407012052992u64, 228334136013u64)\n    put(data, 0u64, 335491783960035328u64, 205765601092u64)\n    put(data, 0u64, 941252322120433664u64, 68018187046u64)\n    put(data, 0u64, 11529215046068469760u64, 38051025390u64)\n    put(data, 0u64, 0u64, 238625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 4368439412768899072u64, 211758u64, 0u64)\n    put(data, 1563676642168012800u64, 211758236813575u64, 0u64)\n    put(data, 11532349341402398720u64, 8061591463141767016u64, 11479u64)\n    put(data, 9048364970084925440u64, 16628725344207857142u64, 215437019748u64)\n    put(data, 16609275425742389248u64, 3555541870038531535u64, 100901445007u64)\n    put(data, 0u64, 18316647450161853665u64, 143192746310u64)\n    put(data, 0u64, 16709574568378075648u64, 70992947447u64)\n    put(data, 0u64, 7696022835795591168u64, 247905827852u64)\n    put(data, 0u64, 16664449640376041472u64, 12417202233u64)\n    put(data, 0u64, 3109186955116544000u64, 57903381625u64)\n    put(data, 0u64, 10515518101817131008u64, 121168549362u64)\n    put(data, 0u64, 9961962375743537152u64, 242570047378u64)\n    put(data, 0u64, 9223372036854775808u64, 146540039062u64)\n    put(data, 0u64, 0u64, 150500000000u64)\n    put(data, 14485570586272534528u64, 3231174267u64, 0u64)\n    put(data, 17827675094632103936u64, 3231174267785264354u64, 0u64)\n    put(data, 7347197909193981952u64, 748977172262750475u64, 175162308u64)\n    put(data, 13677404030777688064u64, 15965033457315095468u64, 196040602133u64)\n    put(data, 3810326759732150272u64, 16809402149066729206u64, 21865466197u64)\n    put(data, 9943947977234055168u64, 7563769067065700371u64, 85911239516u64)\n    put(data, 0u64, 13550322810840051428u64, 92410032742u64)\n    put(data, 0u64, 8663209637545764864u64, 102734564471u64)\n    put(data, 0u64, 8969247575312957440u64, 119469633535u64)\n    put(data, 0u64, 6193172891660451840u64, 255486223885u64)\n    put(data, 0u64, 3427954273864908800u64, 13335732575u64)\n    put(data, 0u64, 10058367555266936832u64, 95185829773u64)\n    put(data, 0u64, 13907115649320091648u64, 141545265197u64)\n    put(data, 0u64, 0u64, 45753906250u64)\n    put(data, 0u64, 0u64, 74000000000u64)\n    put(data, 14878706826214591391u64, 49303u64, 0u64)\n    put(data, 4387341015746028192u64, 49303806576313u64, 0u64)\n    put(data, 651726680428265472u64, 14106411361315920281u64, 2672u64)\n    put(data, 2570638187944738816u64, 3609034283485221502u64, 112764710092u64)\n    put(data, 7419175577111756800u64, 9896072247338192335u64, 204195646140u64)\n    put(data, 17299322326264840192u64, 8889095178479228297u64, 188536467151u64)\n    put(data, 7990511638862102528u64, 3631796911038383102u64, 207481878815u64)\n    put(data, 16717361816799281152u64, 898318840772166823u64, 31196880105u64)\n    put(data, 0u64, 17293677953982795024u64, 233048697961u64)\n    put(data, 0u64, 7353628266884669440u64, 105937492160u64)\n    put(data, 0u64, 2404693032470315008u64, 192398640987u64)\n    put(data, 0u64, 9191155893041889280u64, 91130358670u64)\n    put(data, 0u64, 6353946855033798656u64, 142498253559u64)\n    put(data, 0u64, 3767824038248841216u64, 247344448149u64)\n    put(data, 0u64, 7205759403792793600u64, 149204254150u64)\n    put(data, 0u64, 0u64, 198390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 9707857417284919307u64, 752316384u64, 0u64)\n    put(data, 1844515466944871826u64, 752316384526264005u64, 0u64)\n    put(data, 15167599819856275072u64, 17063068157692817751u64, 40783152u64)\n    put(data, 14830185305589481472u64, 5385330256507239985u64, 48924990778u64)\n    put(data, 6163721531743535104u64, 3373050282752075748u64, 58291939338u64)\n    put(data, 14122847407012052992u64, 4116064001262906061u64, 10182853422u64)\n    put(data, 335491783960035328u64, 11306582046748043076u64, 46223132276u64)\n    put(data, 941252322120433664u64, 17035410946089626406u64, 116612931040u64)\n    put(data, 11529215046068469760u64, 15618595715183448558u64, 224923491477u64)\n    put(data, 0u64, 5141740092277295680u64, 149846685770u64)\n    put(data, 0u64, 16973644291514990592u64, 74278734288u64)\n    put(data, 0u64, 14625255268443750400u64, 208920143100u64)\n    put(data, 0u64, 14021170507320131584u64, 252792836676u64)\n    put(data, 0u64, 4451355232865091584u64, 68760089176u64)\n    put(data, 0u64, 12891553933348044800u64, 88241308450u64)\n    put(data, 0u64, 1152921504606846976u64, 34698852539u64)\n    put(data, 0u64, 0u64, 187062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 8061591463141767016u64, 11479u64, 0u64)\n    put(data, 16628725344207857142u64, 11479437019748u64, 0u64)\n    put(data, 3555541870038531535u64, 5562205901560339855u64, 622u64)\n    put(data, 18316647450161853665u64, 2106077949367544134u64, 110301527786u64)\n    put(data, 16709574568378075648u64, 7496855998374373623u64, 234114170714u64)\n    put(data, 7696022835795591168u64, 229183437194837004u64, 90406405378u64)\n    put(data, 16664449640376041472u64, 465169186276472889u64, 2012424059u64)\n    put(data, 3109186955116544000u64, 2152980561625316473u64, 123025216872u64)\n    put(data, 10515518101817131008u64, 2059790725449340402u64, 104116713310u64)\n    put(data, 9961962375743537152u64, 17891190926410198930u64, 94111661478u64)\n    put(data, 9223372036854775808u64, 9930696175609809814u64, 166969883403u64)\n    put(data, 0u64, 7276914261609005312u64, 11538344118u64)\n    put(data, 0u64, 10539762974036983808u64, 182394482312u64)\n    put(data, 0u64, 12851089458992250880u64, 136571361695u64)\n    put(data, 0u64, 9449311677678878720u64, 159696658955u64)\n    put(data, 0u64, 8699564697382289408u64, 11512248212u64)\n    put(data, 0u64, 4224376450473525248u64, 148471604347u64)\n    put(data, 0u64, 4611686018427387904u64, 123229003906u64)\n    put(data, 0u64, 0u64, 130250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 748977172262750475u64, 175162308u64, 0u64)\n    put(data, 15965033457315095468u64, 175162308040602133u64, 0u64)\n    put(data, 16809402149066729206u64, 13756840147955779925u64, 9495567u64)\n    put(data, 7563769067065700371u64, 13788447602092505948u64, 15745759798u64)\n    put(data, 13550322810840051428u64, 4972540435632173670u64, 54747473242u64)\n    put(data, 8663209637545764864u64, 2844874687533091959u64, 90269561957u64)\n    put(data, 8969247575312957440u64, 15377573779532804095u64, 101154220965u64)\n    put(data, 6193172891660451840u64, 17824715805091194381u64, 165833619944u64)\n    put(data, 3427954273864908800u64, 18277569135638159711u64, 232966279779u64)\n    put(data, 10058367555266936832u64, 4254645803379752845u64, 99990829008u64)\n    put(data, 13907115649320091648u64, 2933643244178200621u64, 208230644811u64)\n    put(data, 0u64, 17188148801879487562u64, 75159033118u64)\n    put(data, 0u64, 11069762501163246592u64, 30931771413u64)\n    put(data, 0u64, 11676570643941818368u64, 21600093027u64)\n    put(data, 0u64, 17840016768744030208u64, 99632988162u64)\n    put(data, 0u64, 16463817321652158464u64, 2967109246u64)\n    put(data, 0u64, 6954191143357644800u64, 126892505325u64)\n    put(data, 0u64, 5080060379673919488u64, 237376987457u64)\n    put(data, 0u64, 0u64, 65275390625u64)\n    put(data, 0u64, 0u64, 161000000000u64)\n    put(data, 14106411361315920281u64, 2672u64, 0u64)\n    put(data, 3609034283485221502u64, 2672764710092u64, 0u64)\n    put(data, 9896072247338192335u64, 16433563478020213436u64, 144u64)\n    put(data, 8889095178479228297u64, 4194750497955655375u64, 144890865261u64)\n    put(data, 3631796911038383102u64, 2691539602252904735u64, 109227397880u64)\n    put(data, 898318840772166823u64, 3775467271962795241u64, 248145908654u64)\n    put(data, 17293677953982795024u64, 16980212613224918121u64, 174204668490u64)\n    put(data, 7353628266884669440u64, 4172857038337333440u64, 74920499170u64)\n    put(data, 2404693032470315008u64, 5936867627376461659u64, 226226211033u64)\n    put(data, 9191155893041889280u64, 17856837443266866062u64, 217321838238u64)\n    put(data, 6353946855033798656u64, 8956297047799810807u64, 158968021097u64)\n    put(data, 3767824038248841216u64, 15356974049716912789u64, 105485521835u64)\n    put(data, 7205759403792793600u64, 6923608913322982854u64, 171832503231u64)\n    put(data, 0u64, 4855902993563955944u64, 191375329591u64)\n    put(data, 0u64, 13835893222288330752u64, 55263239028u64)\n    put(data, 0u64, 9114973913760137216u64, 116750045274u64)\n    put(data, 0u64, 17937099003422310400u64, 90494123725u64)\n    put(data, 0u64, 7007960010734960640u64, 205972372085u64)\n    put(data, 0u64, 7683422439270776832u64, 117379902273u64)\n    put(data, 0u64, 720575940379279360u64, 65416519165u64)\n    put(data, 0u64, 0u64, 253039062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 17063068157692817751u64, 40783152u64, 0u64)\n    put(data, 5385330256507239985u64, 40783152924990778u64, 0u64)\n    put(data, 3373050282752075748u64, 2768933352715741194u64, 2210859u64)\n    put(data, 4116064001262906061u64, 15201941611824153390u64, 43150104177u64)\n    put(data, 11306582046748043076u64, 1418128541727000180u64, 113824098906u64)\n    put(data, 17035410946089626406u64, 5353350204565757408u64, 90076876902u64)\n    put(data, 15618595715183448558u64, 1721001680354286741u64, 102290205696u64)\n    put(data, 5141740092277295680u64, 637631411660453962u64, 93295688u64)\n    put(data, 16973644291514990592u64, 1630012588870568400u64, 72034566068u64)\n    put(data, 14625255268443750400u64, 9253063571656828156u64, 180088363159u64)\n    put(data, 14021170507320131584u64, 6029146854993203780u64, 151501609581u64)\n    put(data, 4451355232865091584u64, 16987401965352759896u64, 109326840705u64)\n    put(data, 12891553933348044800u64, 14499131620542087970u64, 129920888905u64)\n    put(data, 1152921504606846976u64, 1978417255298660539u64, 73785999500u64)\n    put(data, 0u64, 5790079354402454176u64, 140107250214u64)\n    put(data, 0u64, 13748918935842078720u64, 38313880830u64)\n    put(data, 0u64, 18047438014740692992u64, 254745330388u64)\n    put(data, 0u64, 3116889656839372800u64, 212978353575u64)\n    put(data, 0u64, 15995952446606147584u64, 167168966926u64)\n    put(data, 0u64, 12530140063251562496u64, 14867142319u64)\n    put(data, 0u64, 16717361816799281152u64, 175679260253u64)\n    put(data, 0u64, 0u64, 93906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 5562205901560339855u64, 622u64, 0u64)\n    put(data, 2106077949367544134u64, 622301527786u64, 0u64)\n    put(data, 7496855998374373623u64, 13558973353698967386u64, 33u64)\n    put(data, 229183437194837004u64, 6228991722850501890u64, 33735033418u64)\n    put(data, 465169186276472889u64, 16886831391703377787u64, 74337674317u64)\n    put(data, 2152980561625316473u64, 1181713637872883048u64, 77915436964u64)\n    put(data, 2059790725449340402u64, 12393932434925221726u64, 164064060824u64)\n    put(data, 17891190926410198930u64, 10684799845419711910u64, 152671876423u64)\n    put(data, 9930696175609809814u64, 4590318792215640843u64, 71579224160u64)\n    put(data, 7276914261609005312u64, 6383712187366189238u64, 96248841680u64)\n    put(data, 10539762974036983808u64, 1904270214927675016u64, 208346061731u64)\n    put(data, 12851089458992250880u64, 3711506775113308575u64, 163103230695u64)\n    put(data, 9449311677678878720u64, 8091219444738793995u64, 231201201185u64)\n    put(data, 8699564697382289408u64, 39436684991068052u64, 33438625885u64)\n    put(data, 4224376450473525248u64, 18025182908196512891u64, 93002137866u64)\n    put(data, 4611686018427387904u64, 7853924592034603138u64, 10977147123u64)\n    put(data, 0u64, 4815749283615688320u64, 243425762105u64)\n    put(data, 0u64, 14242399906544287744u64, 57261062291u64)\n    put(data, 0u64, 76242322576113664u64, 147772082046u64)\n    put(data, 0u64, 10858088421377703936u64, 126004133104u64)\n    put(data, 0u64, 14293835879041466368u64, 240588618152u64)\n    put(data, 0u64, 12182236992037191680u64, 168774870395u64)\n    put(data, 0u64, 11529215046068469760u64, 123660400390u64)\n    put(data, 0u64, 0u64, 6625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 13756840147955779925u64, 9495567u64, 0u64)\n    put(data, 13788447602092505948u64, 9495567745759798u64, 0u64)\n    put(data, 4972540435632173670u64, 14000097438505379162u64, 514755u64)\n    put(data, 2844874687533091959u64, 16451062686452429925u64, 195758946802u64)\n    put(data, 15377573779532804095u64, 4009347599785716645u64, 242891813895u64)\n    put(data, 17824715805091194381u64, 16544162347546196456u64, 7217347168u64)\n    put(data, 18277569135638159711u64, 17674258299745817187u64, 96896860837u64)\n    put(data, 4254645803379752845u64, 5215238411201214416u64, 165958123462u64)\n    put(data, 2933643244178200621u64, 14253990228345322571u64, 198282718640u64)\n    put(data, 17188148801879487562u64, 11214836553940194590u64, 176772710358u64)\n    put(data, 11069762501163246592u64, 14620711348380590101u64, 214607957507u64)\n    put(data, 11676570643941818368u64, 6638710787931587427u64, 3792590350u64)\n    put(data, 17840016768744030208u64, 17320000343692853250u64, 14359885232u64)\n    put(data, 16463817321652158464u64, 75147386268843646u64, 176938919100u64)\n    put(data, 6954191143357644800u64, 17938801582125480173u64, 188004073747u64)\n    put(data, 5080060379673919488u64, 6573358613626446145u64, 19972464382u64)\n    put(data, 0u64, 8688505427903736481u64, 254356342484u64)\n    put(data, 0u64, 539870168696556032u64, 212471004823u64)\n    put(data, 0u64, 9002861336394465280u64, 151029266420u64)\n    put(data, 0u64, 17989846818158018560u64, 244488046090u64)\n    put(data, 0u64, 2700938287723315200u64, 10975231550u64)\n    put(data, 0u64, 17800090499088908288u64, 62146418157u64)\n    put(data, 0u64, 8809040871136690176u64, 237964944839u64)\n    put(data, 0u64, 9223372036854775808u64, 199477539062u64)\n    put(data, 0u64, 0u64, 246500000000u64)\n    put(data, 16433563478020213436u64, 144u64, 0u64)\n    put(data, 4194750497955655375u64, 144890865261u64, 0u64)\n    put(data, 2691539602252904735u64, 15763656745260536568u64, 7u64)\n    put(data, 3775467271962795241u64, 8787336846248645550u64, 7854549544u64)\n    put(data, 16980212613224918121u64, 17584084447880694346u64, 40476362484u64)\n    put(data, 4172857038337333440u64, 18041672551129683938u64, 244953235127u64)\n    put(data, 5936867627376461659u64, 14025886302294509785u64, 183978041028u64)\n    put(data, 17856837443266866062u64, 18430498103283160734u64, 196760344819u64)\n    put(data, 8956297047799810807u64, 3292348826238025833u64, 243999119304u64)\n    put(data, 15356974049716912789u64, 9211721212658275243u64, 200178478587u64)\n    put(data, 6923608913322982854u64, 10233245872666307519u64, 251499368407u64)\n    put(data, 4855902993563955944u64, 6200995035623311671u64, 215554745370u64)\n    put(data, 13835893222288330752u64, 8480542380570450804u64, 26336156614u64)\n    put(data, 9114973913760137216u64, 11870363864499900506u64, 198459731123u64)\n    put(data, 17937099003422310400u64, 9301051379839581901u64, 179643493714u64)\n    put(data, 7007960010734960640u64, 11456694803569638005u64, 82504211005u64)\n    put(data, 7683422439270776832u64, 14327208890643983169u64, 61621068669u64)\n    put(data, 720575940379279360u64, 4510081789599866365u64, 125776679550u64)\n    put(data, 0u64, 13255356976020303332u64, 126244492023u64)\n    put(data, 0u64, 9658806854127314944u64, 247718574341u64)\n    put(data, 0u64, 13708435528809971712u64, 5523604968u64)\n    put(data, 0u64, 1580190652103131136u64, 232743135779u64)\n    put(data, 0u64, 16557336970347413504u64, 35085662306u64)\n    put(data, 0u64, 12751520132434493440u64, 98897575035u64)\n    put(data, 0u64, 9295429630892703744u64, 123691261291u64)\n    put(data, 0u64, 0u64, 107503906250u64)\n    put(data, 0u64, 0u64, 202000000000u64)\n    put(data, 2768933352715741194u64, 2210859u64, 0u64)\n    put(data, 15201941611824153390u64, 2210859150104177u64, 0u64)\n    put(data, 1418128541727000180u64, 16872870088062921306u64, 119850u64)\n    put(data, 5353350204565757408u64, 5112979788807802982u64, 42914680120u64)\n    put(data, 1721001680354286741u64, 13742728082020150272u64, 56277175189u64)\n    put(data, 637631411660453962u64, 2217110934613627976u64, 149744994782u64)\n    put(data, 1630012588870568400u64, 11021433940188610484u64, 222120189824u64)\n    put(data, 9253063571656828156u64, 1713669895470733463u64, 128597473131u64)\n    put(data, 6029146854993203780u64, 3313382510572018285u64, 107092898231u64)\n    put(data, 16987401965352759896u64, 14976595232784069505u64, 183179618825u64)\n    put(data, 14499131620542087970u64, 7213172372862496841u64, 9811882854u64)\n    put(data, 1978417255298660539u64, 15836474542502248588u64, 102391026857u64)\n    put(data, 5790079354402454176u64, 3221099285878340134u64, 169858497005u64)\n    put(data, 13748918935842078720u64, 3265814602578095358u64, 237174616142u64)\n    put(data, 18047438014740692992u64, 6502528252282225364u64, 78177040164u64)\n    put(data, 3116889656839372800u64, 16392476834556790183u64, 36352502762u64)\n    put(data, 15995952446606147584u64, 15167629413417091342u64, 234888637949u64)\n    put(data, 12530140063251562496u64, 1366763272626280111u64, 253822238838u64)\n    put(data, 16717361816799281152u64, 8720523635169216093u64, 118074092385u64)\n    put(data, 0u64, 9649171375767398672u64, 97472740533u64)\n    put(data, 0u64, 7647980704001073152u64, 181523082628u64)\n    put(data, 0u64, 13286434495608651776u64, 132414597864u64)\n    put(data, 0u64, 4358271637167013888u64, 232720259057u64)\n    put(data, 0u64, 15954987941890097152u64, 241236262378u64)\n    put(data, 0u64, 7911135695429697536u64, 234864921629u64)\n    put(data, 0u64, 7205759403792793600u64, 29428863525u64)\n    put(data, 0u64, 0u64, 37390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 13558973353698967386u64, 33u64, 0u64)\n    put(data, 6228991722850501890u64, 33735033418u64, 0u64)\n    put(data, 16886831391703377787u64, 15288289344628122701u64, 1u64)\n    put(data, 1181713637872883048u64, 952589339068938148u64, 1828779826u64)\n    put(data, 12393932434925221726u64, 10058155040190817688u64, 50051639971u64)\n    put(data, 10684799845419711910u64, 5322725640026584391u64, 163545253677u64)\n    put(data, 4590318792215640843u64, 2269982385930389600u64, 45288545535u64)\n    put(data, 6383712187366189238u64, 13216683679976310224u64, 255123055991u64)\n    put(data, 1904270214927675016u64, 17417440642083494819u64, 119716477857u64)\n    put(data, 3711506775113308575u64, 3029180749090900711u64, 161944201349u64)\n    put(data, 8091219444738793995u64, 8315443826261908513u64, 133164212217u64)\n    put(data, 39436684991068052u64, 1488962797247197277u64, 249450781113u64)\n    put(data, 18025182908196512891u64, 18009099634999034122u64, 185080716834u64)\n    put(data, 7853924592034603138u64, 8092455412807497971u64, 34976275247u64)\n    put(data, 4815749283615688320u64, 17808458047236758329u64, 47438692886u64)\n    put(data, 14242399906544287744u64, 3164591817527425171u64, 22965398445u64)\n    put(data, 76242322576113664u64, 3314036340472350590u64, 173171552866u64)\n    put(data, 10858088421377703936u64, 33234902404332784u64, 98179654270u64)\n    put(data, 14293835879041466368u64, 12349284717857274280u64, 126001801667u64)\n    put(data, 12182236992037191680u64, 18209607903013119355u64, 195669456065u64)\n    put(data, 11529215046068469760u64, 7891549145984268038u64, 193987144822u64)\n    put(data, 0u64, 7703609897518594624u64, 118427801736u64)\n    put(data, 0u64, 6336912652634587136u64, 136417613529u64)\n    put(data, 0u64, 4461621834659397632u64, 217343524723u64)\n    put(data, 0u64, 5484660635557953536u64, 115241865004u64)\n    put(data, 0u64, 15142619273265938432u64, 44297324048u64)\n    put(data, 0u64, 12170977992968765440u64, 16820883035u64)\n    put(data, 0u64, 1152921504606846976u64, 91659790039u64)\n    put(data, 0u64, 0u64, 215062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 14000097438505379162u64, 514755u64, 0u64)\n    put(data, 16451062686452429925u64, 514755758946802u64, 0u64)\n    put(data, 4009347599785716645u64, 17812314011563521031u64, 27904u64)\n    put(data, 16544162347546196456u64, 7684138864490314336u64, 965607477u64)\n    put(data, 17674258299745817187u64, 9740522787420029605u64, 53416558002u64)\n    put(data, 5215238411201214416u64, 6701109407732989894u64, 178528034798u64)\n    put(data, 14253990228345322571u64, 16534886227502443952u64, 238363267868u64)\n    put(data, 11214836553940194590u64, 8908667306968317910u64, 28896357978u64)\n    put(data, 14620711348380590101u64, 7531472173477105155u64, 90482939822u64)\n    put(data, 6638710787931587427u64, 11527371604834801166u64, 174408281924u64)\n    put(data, 17320000343692853250u64, 15688593496691078576u64, 68624900066u64)\n    put(data, 75147386268843646u64, 11394944804253312188u64, 226850480357u64)\n    put(data, 17938801582125480173u64, 11182279880854372627u64, 229617721195u64)\n    put(data, 6573358613626446145u64, 150579373068361470u64, 107606192607u64)\n    put(data, 8688505427903736481u64, 3147220002440857300u64, 223008162924u64)\n    put(data, 539870168696556032u64, 3630514817795505815u64, 108170611138u64)\n    put(data, 9002861336394465280u64, 11708796588334233588u64, 194196810602u64)\n    put(data, 17989846818158018560u64, 16844495466426369546u64, 106634735134u64)\n    put(data, 2700938287723315200u64, 17636655472325475902u64, 30913141928u64)\n    put(data, 17800090499088908288u64, 17038926655686645229u64, 168956085008u64)\n    put(data, 8809040871136690176u64, 15602838456783529415u64, 16923682064u64)\n    put(data, 9223372036854775808u64, 10869815869248876790u64, 16845831567u64)\n    put(data, 0u64, 18407124180939800832u64, 143589253898u64)\n    put(data, 0u64, 5705018517251293184u64, 10997852201u64)\n    put(data, 0u64, 9660452258743058432u64, 41309269673u64)\n    put(data, 0u64, 5646292272224927744u64, 169523694166u64)\n    put(data, 0u64, 7410409304047484928u64, 86306086117u64)\n    put(data, 0u64, 5953758707383795712u64, 229401719093u64)\n    put(data, 0u64, 4611686018427387904u64, 53322753906u64)\n    put(data, 0u64, 0u64, 114250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 15763656745260536568u64, 7u64, 0u64)\n    put(data, 8787336846248645550u64, 7854549544u64, 0u64)\n    put(data, 17584084447880694346u64, 7854549544476362484u64, 0u64)\n    put(data, 18041672551129683938u64, 15035424419724983u64, 425795984u64)\n    put(data, 14025886302294509785u64, 18280822466032836292u64, 144000815071u64)\n    put(data, 18430498103283160734u64, 11524250747302615283u64, 223991005371u64)\n    put(data, 3292348826238025833u64, 15212285943691810760u64, 187624730884u64)\n    put(data, 9211721212658275243u64, 7951804027551297019u64, 4824659673u64)\n    put(data, 10233245872666307519u64, 1706416229965221847u64, 217431068160u64)\n    put(data, 6200995035623311671u64, 3406023111930700826u64, 92505009u64)\n    put(data, 8480542380570450804u64, 16132696204133391302u64, 177184640882u64)\n    put(data, 11870363864499900506u64, 11593846688794356915u64, 114874555213u64)\n    put(data, 9301051379839581901u64, 6875759884161133906u64, 77628503688u64)\n    put(data, 11456694803569638005u64, 3593593325323835965u64, 136372735690u64)\n    put(data, 14327208890643983169u64, 9542049733257388925u64, 202194809084u64)\n    put(data, 4510081789599866365u64, 9926551925937787518u64, 252517275552u64)\n    put(data, 13255356976020303332u64, 3128491553219547895u64, 160538119458u64)\n    put(data, 9658806854127314944u64, 17158408656931354885u64, 34169595866u64)\n    put(data, 13708435528809971712u64, 2065169543154992616u64, 218930159197u64)\n    put(data, 1580190652103131136u64, 4832622393556232739u64, 93111953065u64)\n    put(data, 16557336970347413504u64, 16505930714733656162u64, 169261976984u64)\n    put(data, 12751520132434493440u64, 18270988073492888699u64, 152894788296u64)\n    put(data, 9295429630892703744u64, 2525111411519708523u64, 200990472248u64)\n    put(data, 0u64, 16728989342518570442u64, 56136886563u64)\n    put(data, 0u64, 7974052022039438336u64, 35906880329u64)\n    put(data, 0u64, 5356554962386550784u64, 73432274226u64)\n    put(data, 0u64, 6693869495028547584u64, 50290379426u64)\n    put(data, 0u64, 8157517147199766528u64, 162362875392u64)\n    put(data, 0u64, 12065776720423157760u64, 442219890u64)\n    put(data, 0u64, 11997589407315001344u64, 114654087066u64)\n    put(data, 0u64, 0u64, 154650390625u64)\n    put(data, 0u64, 0u64, 97000000000u64)\n    put(data, 16872870088062921306u64, 119850u64, 0u64)\n    put(data, 5112979788807802982u64, 119850914680120u64, 0u64)\n    put(data, 13742728082020150272u64, 2418433229320326037u64, 6497u64)\n    put(data, 2217110934613627976u64, 1143911773589293534u64, 97131103528u64)\n    put(data, 11021433940188610484u64, 9276183703610924928u64, 40062011581u64)\n    put(data, 1713669895470733463u64, 3532180128827684715u64, 189502862926u64)\n    put(data, 3313382510572018285u64, 8563997501322031543u64, 78191479868u64)\n    put(data, 14976595232784069505u64, 14843890409658460681u64, 60464255234u64)\n    put(data, 7213172372862496841u64, 9489417861634552678u64, 2804688911u64)\n    put(data, 15836474542502248588u64, 1113198223322322089u64, 15514422373u64)\n    put(data, 3221099285878340134u64, 11190777557146597869u64, 101060346596u64)\n    put(data, 3265814602578095358u64, 17764553645932638286u64, 228606653266u64)\n    put(data, 6502528252282225364u64, 14900777150991234852u64, 82963018382u64)\n    put(data, 16392476834556790183u64, 17364899863357893610u64, 142807772747u64)\n    put(data, 15167629413417091342u64, 15537570181590167037u64, 75941353107u64)\n    put(data, 1366763272626280111u64, 5558052627121307766u64, 147842293367u64)\n    put(data, 8720523635169216093u64, 12095241565795232609u64, 119301302636u64)\n    put(data, 9649171375767398672u64, 2187936505958366389u64, 108655684359u64)\n    put(data, 7647980704001073152u64, 12009203621325860228u64, 7118608275u64)\n    put(data, 13286434495608651776u64, 14814842834750302952u64, 147651020232u64)\n    put(data, 4358271637167013888u64, 5965296499605198833u64, 200803114239u64)\n    put(data, 15954987941890097152u64, 4051026394962148842u64, 255323379371u64)\n    put(data, 7911135695429697536u64, 16799526299141688349u64, 171219606580u64)\n    put(data, 7205759403792793600u64, 9460214166646215205u64, 52910704145u64)\n    put(data, 0u64, 10750736995029068008u64, 17512839237u64)\n    put(data, 0u64, 5377963045376430080u64, 69582798620u64)\n    put(data, 0u64, 15996910350253424640u64, 28291539960u64)\n    put(data, 0u64, 13651157529655246848u64, 248867194247u64)\n    put(data, 0u64, 9771305410219737088u64, 135740030732u64)\n    put(data, 0u64, 12709439623416250368u64, 12529703527u64)\n    put(data, 0u64, 9943947977234055168u64, 103688980102u64)\n    put(data, 0u64, 0u64, 134539062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 952589339068938148u64, 1828779826u64, 0u64)\n    put(data, 10058155040190817688u64, 1828779826051639971u64, 0u64)\n    put(data, 5322725640026584391u64, 371564423966525229u64, 99138353u64)\n    put(data, 2269982385930389600u64, 14464859121514339583u64, 49020142547u64)\n    put(data, 13216683679976310224u64, 3913119023023056247u64, 211784141584u64)\n    put(data, 17417440642083494819u64, 5493396321716566945u64, 16212130607u64)\n    put(data, 3029180749090900711u64, 5837454566818211973u64, 47297797611u64)\n    put(data, 8315443826261908513u64, 2886670683193253881u64, 235316449046u64)\n    put(data, 1488962797247197277u64, 5504823105587173817u64, 22156486731u64)\n    put(data, 18009099634999034122u64, 9431834277334851106u64, 75298417058u64)\n    put(data, 8092455412807497971u64, 12921661346456247087u64, 162511300760u64)\n    put(data, 17808458047236758329u64, 3643076516404724246u64, 152700484665u64)\n    put(data, 3164591817527425171u64, 12559396953196866477u64, 57197491573u64)\n    put(data, 3314036340472350590u64, 1626880974916825698u64, 117680846273u64)\n    put(data, 33234902404332784u64, 6806994170946429566u64, 193088193394u64)\n    put(data, 12349284717857274280u64, 7596631230206896579u64, 114369007893u64)\n    put(data, 18209607903013119355u64, 3100480253729502401u64, 21411814204u64)\n    put(data, 7891549145984268038u64, 6310570748781063286u64, 60168077371u64)\n    put(data, 7703609897518594624u64, 14251867077375744136u64, 59342096725u64)\n    put(data, 6336912652634587136u64, 6701165793751570137u64, 85772595262u64)\n    put(data, 4461621834659397632u64, 10856833140463959923u64, 62363270925u64)\n    put(data, 5484660635557953536u64, 15867563727561248556u64, 13588550103u64)\n    put(data, 15142619273265938432u64, 5048961008671491600u64, 215860182353u64)\n    put(data, 12170977992968765440u64, 13278183119599849051u64, 81273704724u64)\n    put(data, 1152921504606846976u64, 4547591784941053655u64, 20719811749u64)\n    put(data, 0u64, 11815437715887182496u64, 165246525444u64)\n    put(data, 0u64, 398495392178782208u64, 4640516162u64)\n    put(data, 0u64, 9154841240825495552u64, 66021602478u64)\n    put(data, 0u64, 1902683298245640192u64, 174496284938u64)\n    put(data, 0u64, 5081900962138816512u64, 10103144668u64)\n    put(data, 0u64, 3234710432358858752u64, 220275490403u64)\n    put(data, 0u64, 16717361816799281152u64, 99175354003u64)\n    put(data, 0u64, 0u64, 147906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 17812314011563521031u64, 27904u64, 0u64)\n    put(data, 7684138864490314336u64, 27904965607477u64, 0u64)\n    put(data, 9740522787420029605u64, 13488568028574514610u64, 1512u64)\n    put(data, 6701109407732989894u64, 275784718433886190u64, 232731216738u64)\n    put(data, 16534886227502443952u64, 10020568880357102364u64, 98014950319u64)\n    put(data, 8908667306968317910u64, 8876397213146246746u64, 175543216127u64)\n    put(data, 7531472173477105155u64, 2155905919114811310u64, 255481190457u64)\n    put(data, 11527371604834801166u64, 1087100407155601220u64, 57116871894u64)\n    put(data, 15688593496691078576u64, 2903498381705011170u64, 214058931831u64)\n    put(data, 11394944804253312188u64, 12223476257006657765u64, 119157398962u64)\n    put(data, 11182279880854372627u64, 12148657163736735595u64, 178662635975u64)\n    put(data, 150579373068361470u64, 8951241323311673823u64, 199658580024u64)\n    put(data, 3147220002440857300u64, 8463862715901576300u64, 56485247764u64)\n    put(data, 3630514817795505815u64, 3873401978748963266u64, 20458826917u64)\n    put(data, 11708796588334233588u64, 248364795947002730u64, 165209977542u64)\n    put(data, 16844495466426369546u64, 10454378025404001822u64, 198013463882u64)\n    put(data, 17636655472325475902u64, 6574176865628265640u64, 74566732968u64)\n    put(data, 17038926655686645229u64, 16703315293848336u64, 168356386842u64)\n    put(data, 15602838456783529415u64, 9896033222450013456u64, 26000905488u64)\n    put(data, 10869815869248876790u64, 17311376269334085007u64, 16536465035u64)\n    put(data, 18407124180939800832u64, 18378511316495639306u64, 139938451587u64)\n    put(data, 5705018517251293184u64, 15120796393727584297u64, 131996301094u64)\n    put(data, 9660452258743058432u64, 18253447805740347049u64, 38819700014u64)\n    put(data, 5646292272224927744u64, 5842497225601731158u64, 46989521388u64)\n    put(data, 7410409304047484928u64, 4369968404176723173u64, 236316722409u64)\n    put(data, 5953758707383795712u64, 16142207253674488117u64, 233236896461u64)\n    put(data, 4611686018427387904u64, 12124259227391928178u64, 205875070808u64)\n    put(data, 0u64, 13019483264566077056u64, 88657257409u64)\n    put(data, 0u64, 74901376448135168u64, 193705787602u64)\n    put(data, 0u64, 13897060093813325824u64, 210004060411u64)\n    put(data, 0u64, 4495486210810052608u64, 251753361137u64)\n    put(data, 0u64, 14885496280087265280u64, 241243700795u64)\n    put(data, 0u64, 4976477588244398080u64, 59806944370u64)\n    put(data, 0u64, 11529215046068469760u64, 114269775390u64)\n    put(data, 0u64, 0u64, 30625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 15035424419724983u64, 425795984u64, 0u64)\n    put(data, 18280822466032836292u64, 425795984000815071u64, 0u64)\n    put(data, 11524250747302615283u64, 10043594327130472635u64, 23082446u64)\n    put(data, 15212285943691810760u64, 8336034337032909060u64, 206544464339u64)\n    put(data, 7951804027551297019u64, 16717215784895280857u64, 211451897326u64)\n    put(data, 1706416229965221847u64, 10968831263951212032u64, 238906242083u64)\n    put(data, 3406023111930700826u64, 5536629379734406065u64, 35594621534u64)\n    put(data, 16132696204133391302u64, 1618806894932332402u64, 94300141280u64)\n    put(data, 11593846688794356915u64, 11363331325254998861u64, 224087755697u64)\n    put(data, 6875759884161133906u64, 8775167772751754888u64, 177616007425u64)\n    put(data, 3593593325323835965u64, 2898202945316114122u64, 1475702798u64)\n    put(data, 9542049733257388925u64, 8868842714495185148u64, 14157111896u64)\n    put(data, 9926551925937787518u64, 17052094667531999136u64, 88480780926u64)\n    put(data, 3128491553219547895u64, 3658615537031138594u64, 126924395904u64)\n    put(data, 17158408656931354885u64, 12486952437987190746u64, 128198333945u64)\n    put(data, 2065169543154992616u64, 912079238520577629u64, 249676919048u64)\n    put(data, 4832622393556232739u64, 10960072898031888041u64, 8049443914u64)\n    put(data, 16505930714733656162u64, 6129550094334741912u64, 74594146742u64)\n    put(data, 18270988073492888699u64, 7965724516573729480u64, 182332283576u64)\n    put(data, 2525111411519708523u64, 5801761178810791992u64, 184431822791u64)\n    put(data, 16728989342518570442u64, 13197466483098446115u64, 199314514103u64)\n    put(data, 7974052022039438336u64, 11326268638393107273u64, 183715436091u64)\n    put(data, 5356554962386550784u64, 3597339351794947378u64, 59613998253u64)\n    put(data, 6693869495028547584u64, 353880726151383714u64, 173195012157u64)\n    put(data, 8157517147199766528u64, 11154818162602073600u64, 61019183912u64)\n    put(data, 12065776720423157760u64, 5141043976157511026u64, 40604703904u64)\n    put(data, 11997589407315001344u64, 7188225141808859034u64, 160278696552u64)\n    put(data, 0u64, 13894168943295705185u64, 104389674465u64)\n    put(data, 0u64, 12176538069834828288u64, 225753204407u64)\n    put(data, 0u64, 7994239409235165184u64, 183660091451u64)\n    put(data, 0u64, 13707777025480065024u64, 59433368586u64)\n    put(data, 0u64, 10120227247676719104u64, 10743100081u64)\n    put(data, 0u64, 7358494763030413312u64, 177548618618u64)\n    put(data, 0u64, 7656119366529843200u64, 122398904800u64)\n    put(data, 0u64, 9223372036854775808u64, 224415039062u64)\n    put(data, 0u64, 0u64, 86500000000u64)\n    put(data, 2418433229320326037u64, 6497u64, 0u64)\n    put(data, 1143911773589293534u64, 6497131103528u64, 0u64)\n    put(data, 9276183703610924928u64, 3877189582299842749u64, 352u64)\n    put(data, 3532180128827684715u64, 7625565791857948238u64, 96210182868u64)\n    put(data, 8563997501322031543u64, 16568435163612007484u64, 212413382749u64)\n    put(data, 14843890409658460681u64, 17592071940521808130u64, 93898176669u64)\n    put(data, 9489417861634552678u64, 15158637878035490831u64, 157953668130u64)\n    put(data, 1113198223322322089u64, 17789243229146401893u64, 34821751405u64)\n    put(data, 11190777557146597869u64, 14677686051252896484u64, 109964356807u64)\n    put(data, 17764553645932638286u64, 3531237481269211986u64, 199795678955u64)\n    put(data, 14900777150991234852u64, 8074435404989280910u64, 235191428767u64)\n    put(data, 17364899863357893610u64, 7086549341467684427u64, 159437716020u64)\n    put(data, 15537570181590167037u64, 10556134770918626963u64, 52384162609u64)\n    put(data, 5558052627121307766u64, 10772666134712966775u64, 49572249212u64)\n    put(data, 12095241565795232609u64, 6195173298198112620u64, 124583987401u64)\n    put(data, 2187936505958366389u64, 8144773843324250887u64, 201335841017u64)\n    put(data, 12009203621325860228u64, 14144284817150924691u64, 249441529074u64)\n    put(data, 14814842834750302952u64, 6464447844648863176u64, 242766763216u64)\n    put(data, 5965296499605198833u64, 15760468443293179135u64, 208350438419u64)\n    put(data, 4051026394962148842u64, 5172191224908322475u64, 19854376706u64)\n    put(data, 16799526299141688349u64, 2357554307308969012u64, 2280385048u64)\n    put(data, 9460214166646215205u64, 1602046917604361745u64, 24127803275u64)\n    put(data, 10750736995029068008u64, 7830970218109515845u64, 139086847137u64)\n    put(data, 5377963045376430080u64, 2899479134887821084u64, 161424517746u64)\n    put(data, 15996910350253424640u64, 15792042302392017912u64, 114157181078u64)\n    put(data, 13651157529655246848u64, 11286099112296056199u64, 150856088328u64)\n    put(data, 9771305410219737088u64, 15161477829153947404u64, 8611820658u64)\n    put(data, 12709439623416250368u64, 423831848142641767u64, 114821905360u64)\n    put(data, 9943947977234055168u64, 9707413321046312582u64, 208022975970u64)\n    put(data, 0u64, 10969483299803835620u64, 226526239930u64)\n    put(data, 0u64, 4326479556120930304u64, 186594656881u64)\n    put(data, 0u64, 12876227232041795584u64, 113234538926u64)\n    put(data, 0u64, 16967986827791171584u64, 174698021676u64)\n    put(data, 0u64, 1288146316538413056u64, 44919836409u64)\n    put(data, 0u64, 13715290452691779584u64, 249069830551u64)\n    put(data, 0u64, 4683743612465315840u64, 151743507385u64)\n    put(data, 0u64, 0u64, 185253906250u64)\n    put(data, 0u64, 0u64, 74000000000u64)\n    put(data, 371564423966525229u64, 99138353u64, 0u64)\n    put(data, 14464859121514339583u64, 99138353020142547u64, 0u64)\n    put(data, 3913119023023056247u64, 16344805304534272784u64, 5374300u64)\n    put(data, 5493396321716566945u64, 26429987091348271u64, 92886053671u64)\n    put(data, 5837454566818211973u64, 8691371289609838059u64, 39001432772u64)\n    put(data, 2886670683193253881u64, 12980168378493046550u64, 196471160181u64)\n    put(data, 5504823105587173817u64, 14010125458129496139u64, 117703656337u64)\n    put(data, 9431834277334851106u64, 17061829677031795106u64, 145759490422u64)\n    put(data, 12921661346456247087u64, 2227928323072698520u64, 118924923640u64)\n    put(data, 3643076516404724246u64, 7394752319272287289u64, 248120776236u64)\n    put(data, 12559396953196866477u64, 8805771303577744757u64, 44400870326u64)\n    put(data, 1626880974916825698u64, 16371027194302248385u64, 182477361818u64)\n    put(data, 6806994170946429566u64, 9114324123731231602u64, 154887475162u64)\n    put(data, 7596631230206896579u64, 14468189808746991893u64, 218494088500u64)\n    put(data, 3100480253729502401u64, 2376054557800684348u64, 52784322141u64)\n    put(data, 6310570748781063286u64, 12462238943546048571u64, 93128806175u64)\n    put(data, 14251867077375744136u64, 15334855370842605909u64, 31675579326u64)\n    put(data, 6701165793751570137u64, 7211347914013798462u64, 190831304175u64)\n    put(data, 10856833140463959923u64, 13763642332572548877u64, 239390927953u64)\n    put(data, 15867563727561248556u64, 16868268377740071383u64, 81746128545u64)\n    put(data, 5048961008671491600u64, 1120013377627684177u64, 161914430661u64)\n    put(data, 13278183119599849051u64, 15898107650717274388u64, 197060716046u64)\n    put(data, 4547591784941053655u64, 12281923376333274277u64, 14861838142u64)\n    put(data, 11815437715887182496u64, 6383530489286615044u64, 62665804400u64)\n    put(data, 398495392178782208u64, 4253822060257126466u64, 112346051881u64)\n    put(data, 9154841240825495552u64, 17614372438391501998u64, 41230600155u64)\n    put(data, 1902683298245640192u64, 4309951310554333450u64, 219954877043u64)\n    put(data, 5081900962138816512u64, 13106185988973773020u64, 115233642928u64)\n    put(data, 3234710432358858752u64, 2070134359761960547u64, 176710487766u64)\n    put(data, 16717361816799281152u64, 9399359914137865875u64, 214112222208u64)\n    put(data, 0u64, 17415053284723541264u64, 509540321u64)\n    put(data, 0u64, 4840502610448261120u64, 225944071930u64)\n    put(data, 0u64, 5690599259712258048u64, 250262404172u64)\n    put(data, 0u64, 114769594245185536u64, 76308488004u64)\n    put(data, 0u64, 3150620882578178048u64, 68006221672u64)\n    put(data, 0u64, 5136918324969472000u64, 104170795500u64)\n    put(data, 0u64, 7205759403792793600u64, 236278472900u64)\n    put(data, 0u64, 0u64, 196390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 13488568028574514610u64, 1512u64, 0u64)\n    put(data, 275784718433886190u64, 1512731216738u64, 0u64)\n    put(data, 10020568880357102364u64, 98202693831717807u64, 82u64)\n    put(data, 8876397213146246746u64, 12909287260170414079u64, 82005323578u64)\n    put(data, 2155905919114811310u64, 11728631949380786233u64, 58699813864u64)\n    put(data, 1087100407155601220u64, 18263701925522197718u64, 232635810411u64)\n    put(data, 2903498381705011170u64, 4868886449713321591u64, 107990077265u64)\n    put(data, 12223476257006657765u64, 5870139507184082354u64, 81263942863u64)\n    put(data, 12148657163736735595u64, 5978562500822661575u64, 207318220900u64)\n    put(data, 8951241323311673823u64, 10821136839630268472u64, 100324098522u64)\n    put(data, 8463862715901576300u64, 9490907630136752916u64, 218586615003u64)\n    put(data, 3873401978748963266u64, 10564005678001613989u64, 219514503133u64)\n    put(data, 248364795947002730u64, 5754050547468481222u64, 221572675895u64)\n    put(data, 10454378025404001822u64, 3833909949855542602u64, 55311927705u64)\n    put(data, 6574176865628265640u64, 15446538552665967784u64, 153207836674u64)\n    put(data, 16703315293848336u64, 14924837848804399130u64, 2837358532u64)\n    put(data, 9896033222450013456u64, 18140170340418344208u64, 196809077080u64)\n    put(data, 17311376269334085007u64, 11380424819825208971u64, 88983380604u64)\n    put(data, 18378511316495639306u64, 12416915664152252547u64, 124616934065u64)\n    put(data, 15120796393727584297u64, 17195282241626289958u64, 177673122346u64)\n    put(data, 18253447805740347049u64, 2649541045825281326u64, 42932158118u64)\n    put(data, 5842497225601731158u64, 16577429864268509676u64, 166143631907u64)\n    put(data, 4369968404176723173u64, 12051257060168107241u64, 35898664273u64)\n    put(data, 16142207253674488117u64, 5363884561143470797u64, 81653299954u64)\n    put(data, 12124259227391928178u64, 13054029903083620184u64, 242290776764u64)\n    put(data, 13019483264566077056u64, 566314952158634945u64, 188707660379u64)\n    put(data, 74901376448135168u64, 1329472079642345682u64, 91030699995u64)\n    put(data, 13897060093813325824u64, 15686237486658857211u64, 219072070825u64)\n    put(data, 4495486210810052608u64, 1069073549290598129u64, 169850352638u64)\n    put(data, 14885496280087265280u64, 4323599065125928507u64, 254057954593u64)\n    put(data, 4976477588244398080u64, 17861823329752681586u64, 33234382774u64)\n    put(data, 11529215046068469760u64, 17220149985412802078u64, 182968291382u64)\n    put(data, 0u64, 4344934572159429184u64, 54933506201u64)\n    put(data, 0u64, 2252927464837120000u64, 153235539375u64)\n    put(data, 0u64, 10910018171964489728u64, 175122131442u64)\n    put(data, 0u64, 3597328585515335680u64, 242591433270u64)\n    put(data, 0u64, 6972808074239148032u64, 54195011573u64)\n    put(data, 0u64, 2227030015734710272u64, 245377996683u64)\n    put(data, 0u64, 1152921504606846976u64, 139120727539u64)\n    put(data, 0u64, 0u64, 243062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 10043594327130472635u64, 23082446u64, 0u64)\n    put(data, 8336034337032909060u64, 23082446544464339u64, 0u64)\n    put(data, 16717215784895280857u64, 17238287503805244910u64, 1251301u64)\n    put(data, 10968831263951212032u64, 1434575446038410275u64, 229934489438u64)\n    put(data, 5536629379734406065u64, 14009569747841241694u64, 94077768490u64)\n    put(data, 1618806894932332402u64, 14938795732275951328u64, 42759460297u64)\n    put(data, 11363331325254998861u64, 6687653542888983473u64, 201809833739u64)\n    put(data, 8775167772751754888u64, 28238723295162625u64, 11362538425u64)\n    put(data, 2898202945316114122u64, 4745270274832691214u64, 185001530824u64)\n    put(data, 8868842714495185148u64, 926478968112308824u64, 200257241617u64)\n    put(data, 17052094667531999136u64, 9213681606604198526u64, 17050224525u64)\n    put(data, 3658615537031138594u64, 13346223820579313024u64, 141499474680u64)\n    put(data, 12486952437987190746u64, 691642518601291257u64, 248723500243u64)\n    put(data, 912079238520577629u64, 1153720150033789192u64, 211037494016u64)\n    put(data, 10960072898031888041u64, 12089015034721780810u64, 62543294u64)\n    put(data, 6129550094334741912u64, 3555868702841788854u64, 190655346818u64)\n    put(data, 7965724516573729480u64, 11708406782758214328u64, 130192764028u64)\n    put(data, 5801761178810791992u64, 9417497762905343943u64, 124634714003u64)\n    put(data, 13197466483098446115u64, 12838336066957615287u64, 147510523576u64)\n    put(data, 11326268638393107273u64, 13737708142128207419u64, 184695967592u64)\n    put(data, 3597339351794947378u64, 11683434809834695853u64, 104744722650u64)\n    put(data, 353880726151383714u64, 2689114340106315837u64, 218633360270u64)\n    put(data, 11154818162602073600u64, 8859225263374261032u64, 142145777180u64)\n    put(data, 5141043976157511026u64, 15761671984578600096u64, 28480259563u64)\n    put(data, 7188225141808859034u64, 7087267079878005352u64, 235854441950u64)\n    put(data, 13894168943295705185u64, 4601291730423121377u64, 222384201518u64)\n    put(data, 12176538069834828288u64, 9559411037059581623u64, 46249436524u64)\n    put(data, 7994239409235165184u64, 12969820289641388091u64, 108518216710u64)\n    put(data, 13707777025480065024u64, 13628239920285957130u64, 6703095366u64)\n    put(data, 10120227247676719104u64, 8049893933765800625u64, 70738788366u64)\n    put(data, 7358494763030413312u64, 10391755948840250234u64, 14436385624u64)\n    put(data, 7656119366529843200u64, 14454650777462444512u64, 88563338218u64)\n    put(data, 9223372036854775808u64, 14244638523341127254u64, 234783588188u64)\n    put(data, 0u64, 12246016810439753984u64, 92772203401u64)\n    put(data, 0u64, 9382741764551081984u64, 137663857901u64)\n    put(data, 0u64, 4608696190291148800u64, 237508639450u64)\n    put(data, 0u64, 1696483666416369664u64, 218249837921u64)\n    put(data, 0u64, 15416683541605384192u64, 97091966563u64)\n    put(data, 0u64, 7683140964294066176u64, 99835740089u64)\n    put(data, 0u64, 4611686018427387904u64, 185416503906u64)\n    put(data, 0u64, 0u64, 98250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 3877189582299842749u64, 352u64, 0u64)\n    put(data, 7625565791857948238u64, 352210182868u64, 0u64)\n    put(data, 16568435163612007484u64, 1722045467931902045u64, 19u64)\n    put(data, 17592071940521808130u64, 16095324008152856733u64, 19093352271u64)\n    put(data, 15158637878035490831u64, 15216188060094280738u64, 79872529262u64)\n    put(data, 17789243229146401893u64, 10793385929903030893u64, 110824871207u64)\n    put(data, 14677686051252896484u64, 12613277226875940039u64, 39585110623u64)\n    put(data, 3531237481269211986u64, 10644539625155600107u64, 95683767128u64)\n    put(data, 8074435404989280910u64, 6181262895644173983u64, 88577041649u64)\n    put(data, 7086549341467684427u64, 148914399627082292u64, 241335086933u64)\n    put(data, 10556134770918626963u64, 14379289774887985969u64, 85008072665u64)\n    put(data, 10772666134712966775u64, 11743339675582627452u64, 217779502860u64)\n    put(data, 6195173298198112620u64, 7841621929809463497u64, 12636607719u64)\n    put(data, 8144773843324250887u64, 11168944680251236601u64, 231425095176u64)\n    put(data, 14144284817150924691u64, 6178560202529287410u64, 8605469704u64)\n    put(data, 6464447844648863176u64, 13295243308201596112u64, 8334940419u64)\n    put(data, 15760468443293179135u64, 17040673746172470291u64, 3720736583u64)\n    put(data, 5172191224908322475u64, 14957442487039409922u64, 71923776774u64)\n    put(data, 2357554307308969012u64, 17778155426506992152u64, 6810844581u64)\n    put(data, 1602046917604361745u64, 14945404984219733899u64, 165963755736u64)\n    put(data, 7830970218109515845u64, 11590754866058681505u64, 216810192027u64)\n    put(data, 2899479134887821084u64, 6020790784469412466u64, 155628336080u64)\n    put(data, 15792042302392017912u64, 7934351824569522326u64, 208326387722u64)\n    put(data, 11286099112296056199u64, 5038361112172116744u64, 10430122074u64)\n    put(data, 15161477829153947404u64, 3305187319649924210u64, 90273130103u64)\n    put(data, 423831848142641767u64, 11470175511099161552u64, 119179174563u64)\n    put(data, 9707413321046312582u64, 7308362160352048610u64, 163621799460u64)\n    put(data, 10969483299803835620u64, 10666410671225576634u64, 36396187106u64)\n    put(data, 4326479556120930304u64, 2181639019945820785u64, 226578227281u64)\n    put(data, 12876227232041795584u64, 4615749499734847918u64, 81118266888u64)\n    put(data, 16967986827791171584u64, 14076159200958497580u64, 8250220281u64)\n    put(data, 1288146316538413056u64, 5470405257862074105u64, 249763070119u64)\n    put(data, 13715290452691779584u64, 4565741478181339543u64, 167296551263u64)\n    put(data, 4683743612465315840u64, 8901832997861862329u64, 95247509341u64)\n    put(data, 0u64, 14190141170191714122u64, 93482569333u64)\n    put(data, 0u64, 4240772322245764096u64, 117769249094u64)\n    put(data, 0u64, 4422842195340951552u64, 70229892728u64)\n    put(data, 0u64, 15448426386733137920u64, 120239762755u64)\n    put(data, 0u64, 9203504548935630848u64, 67837460872u64)\n    put(data, 0u64, 5936377627571912704u64, 136498922981u64)\n    put(data, 0u64, 468374361246531584u64, 229321811676u64)\n    put(data, 0u64, 0u64, 220025390625u64)\n    put(data, 0u64, 0u64, 33000000000u64)\n    put(data, 16344805304534272784u64, 5374300u64, 0u64)\n    put(data, 26429987091348271u64, 5374300886053671u64, 0u64)\n    put(data, 8691371289609838059u64, 8020875056524075716u64, 291341u64)\n    put(data, 12980168378493046550u64, 1400288714762747253u64, 13434812508u64)\n    put(data, 14010125458129496139u64, 6136037711314764689u64, 92075909803u64)\n    put(data, 17061829677031795106u64, 15735488086392394102u64, 171332635270u64)\n    put(data, 2227928323072698520u64, 7735094782793634552u64, 134853022518u64)\n    put(data, 7394752319272287289u64, 7273689191766726188u64, 54419320328u64)\n    put(data, 8805771303577744757u64, 3410634565056431030u64, 8394307481u64)\n    put(data, 16371027194302248385u64, 4600927904885215898u64, 153184890870u64)\n    put(data, 9114324123731231602u64, 9154871331680374746u64, 246249416801u64)\n    put(data, 14468189808746991893u64, 6117978272461042996u64, 97496286569u64)\n    put(data, 2376054557800684348u64, 13116904339287496285u64, 105331656266u64)\n    put(data, 12462238943546048571u64, 867037205615660831u64, 74711068809u64)\n    put(data, 15334855370842605909u64, 1802487145191504830u64, 137047002181u64)\n    put(data, 7211347914013798462u64, 17242009718457409007u64, 69097713023u64)\n    put(data, 13763642332572548877u64, 13620802355488468049u64, 127934691219u64)\n    put(data, 16868268377740071383u64, 4442227880594435745u64, 147738385175u64)\n    put(data, 1120013377627684177u64, 17354849212854314181u64, 23240813655u64)\n    put(data, 15898107650717274388u64, 18202319179831567886u64, 87940808260u64)\n    put(data, 12281923376333274277u64, 17568634016348874558u64, 68986749699u64)\n    put(data, 6383530489286615044u64, 7496925598312450672u64, 3952397558u64)\n    put(data, 4253822060257126466u64, 601870379496813865u64, 246406409151u64)\n    put(data, 17614372438391501998u64, 11995106565680728027u64, 191032627458u64)\n    put(data, 4309951310554333450u64, 16331071694764184179u64, 2650256029u64)\n    put(data, 13106185988973773020u64, 9665962217000524208u64, 157885309170u64)\n    put(data, 2070134359761960547u64, 13682661374415474390u64, 242523992861u64)\n    put(data, 9399359914137865875u64, 6940361789924260864u64, 29741738559u64)\n    put(data, 17415053284723541264u64, 9658039831644010465u64, 63376237766u64)\n    put(data, 4840502610448261120u64, 6843715893910236922u64, 198523563388u64)\n    put(data, 5690599259712258048u64, 47089792870595660u64, 124370998582u64)\n    put(data, 114769594245185536u64, 14510386192097156932u64, 54002552742u64)\n    put(data, 3150620882578178048u64, 12059931208360040296u64, 166786609611u64)\n    put(data, 5136918324969472000u64, 14877013468459184620u64, 203653770180u64)\n    put(data, 7205759403792793600u64, 2397668560671695044u64, 196806484516u64)\n    put(data, 0u64, 2195572305559232232u64, 36129977873u64)\n    put(data, 0u64, 3261686279425953792u64, 17119022213u64)\n    put(data, 0u64, 9333850662059900928u64, 133176816367u64)\n    put(data, 0u64, 5036522340217782272u64, 239505989058u64)\n    put(data, 0u64, 2800120215143186432u64, 194273030423u64)\n    put(data, 0u64, 441634238459019264u64, 23151794821u64)\n    put(data, 0u64, 720575940379279360u64, 133023941040u64)\n    put(data, 0u64, 0u64, 176039062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 98202693831717807u64, 82u64, 0u64)\n    put(data, 12909287260170414079u64, 82005323578u64, 0u64)\n    put(data, 11728631949380786233u64, 8218347283861607400u64, 4u64)\n    put(data, 18263701925522197718u64, 17896200385973633643u64, 4445517498u64)\n    put(data, 4868886449713321591u64, 16333242102094352209u64, 186970154966u64)\n    put(data, 5870139507184082354u64, 9981905728606788815u64, 214885426828u64)\n    put(data, 5978562500822661575u64, 15219470018924839012u64, 140541120193u64)\n    put(data, 10821136839630268472u64, 17152070168529617370u64, 193825049122u64)\n    put(data, 9490907630136752916u64, 17841343440958328027u64, 34929815586u64)\n    put(data, 10564005678001613989u64, 17291078023923990493u64, 34967181165u64)\n    put(data, 5754050547468481222u64, 16744804581790759223u64, 109937351217u64)\n    put(data, 3833909949855542602u64, 5001622214111594905u64, 49907737675u64)\n    put(data, 15446538552665967784u64, 9676746897435398146u64, 75271138483u64)\n    put(data, 14924837848804399130u64, 8109025833995118532u64, 179524577500u64)\n    put(data, 18140170340418344208u64, 5495826424046694744u64, 220439591171u64)\n    put(data, 11380424819825208971u64, 7890288164365705852u64, 3297929347u64)\n    put(data, 12416915664152252547u64, 8616438349039895217u64, 131427733378u64)\n    put(data, 17195282241626289958u64, 15787154801788760618u64, 130467098058u64)\n    put(data, 2649541045825281326u64, 12418659311480782502u64, 202855823376u64)\n    put(data, 16577429864268509676u64, 4486988874116669987u64, 16673216870u64)\n    put(data, 12051257060168107241u64, 4828971301551875409u64, 102243240154u64)\n    put(data, 5363884561143470797u64, 14769106422014442226u64, 218261779058u64)\n    put(data, 13054029903083620184u64, 7763933466423188156u64, 114800634863u64)\n    put(data, 566314952158634945u64, 10449097116253839963u64, 239420883676u64)\n    put(data, 1329472079642345682u64, 12870692502472900571u64, 220566446689u64)\n    put(data, 15686237486658857211u64, 11597479481311003817u64, 97697721638u64)\n    put(data, 1069073549290598129u64, 8294994869530047486u64, 38628700622u64)\n    put(data, 4323599065125928507u64, 16879315829924478241u64, 206449672572u64)\n    put(data, 17861823329752681586u64, 11873324837601439670u64, 124915029544u64)\n    put(data, 17220149985412802078u64, 3277599055636107318u64, 40643654229u64)\n    put(data, 4344934572159429184u64, 15363467897354242201u64, 85177679000u64)\n    put(data, 2252927464837120000u64, 10351182204479784367u64, 152832855263u64)\n    put(data, 10910018171964489728u64, 12811517584931924466u64, 223561138711u64)\n    put(data, 3597328585515335680u64, 16988930699558748726u64, 23694513759u64)\n    put(data, 6972808074239148032u64, 11683499918824718325u64, 95920971778u64)\n    put(data, 2227030015734710272u64, 13119300691281647499u64, 2633363799u64)\n    put(data, 1152921504606846976u64, 10125549106595354099u64, 87711198715u64)\n    put(data, 0u64, 17505352699870800544u64, 251548907116u64)\n    put(data, 0u64, 6756039242241163264u64, 108948967071u64)\n    put(data, 0u64, 3537338758766526464u64, 159366245621u64)\n    put(data, 0u64, 6522626374119718912u64, 245191759518u64)\n    put(data, 0u64, 4733294203482669056u64, 158353592284u64)\n    put(data, 0u64, 16997710893603094528u64, 220256592392u64)\n    put(data, 0u64, 16717361816799281152u64, 8921447753u64)\n    put(data, 0u64, 0u64, 73906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 17238287503805244910u64, 1251301u64, 0u64)\n    put(data, 1434575446038410275u64, 1251301934489438u64, 0u64)\n    put(data, 14009569747841241694u64, 3943737498063000362u64, 67833u64)\n    put(data, 14938795732275951328u64, 2870731037991212489u64, 249213790438u64)\n    put(data, 6687653542888983473u64, 7389433400402095883u64, 230155622641u64)\n    put(data, 28238723295162625u64, 5675049236146197433u64, 241400581987u64)\n    put(data, 4745270274832691214u64, 9953779846262904264u64, 99307645035u64)\n    put(data, 926478968112308824u64, 12691978937179636241u64, 107539595486u64)\n    put(data, 9213681606604198526u64, 15523327331528198029u64, 222688033556u64)\n    put(data, 13346223820579313024u64, 15722603279568118520u64, 20841521260u64)\n    put(data, 691642518601291257u64, 11838632364171816147u64, 108852324031u64)\n    put(data, 1153720150033789192u64, 7832751832367143680u64, 191641773546u64)\n    put(data, 12089015034721780810u64, 12167724027162940862u64, 234424614327u64)\n    put(data, 3555868702841788854u64, 4108211144748152962u64, 183659613641u64)\n    put(data, 11708406782758214328u64, 7530983398136343676u64, 201222706572u64)\n    put(data, 9417497762905343943u64, 1117587133956542355u64, 140408255428u64)\n    put(data, 12838336066957615287u64, 17134748625149490872u64, 196060584519u64)\n    put(data, 13737708142128207419u64, 4039918359454207848u64, 71928876584u64)\n    put(data, 11683434809834695853u64, 1830218764589441242u64, 40219004413u64)\n    put(data, 2689114340106315837u64, 637895981480825742u64, 253099216358u64)\n    put(data, 8859225263374261032u64, 8246879226348334620u64, 230034580410u64)\n    put(data, 15761671984578600096u64, 12389239568142583275u64, 186447064218u64)\n    put(data, 7087267079878005352u64, 14041257178803154398u64, 154671622022u64)\n    put(data, 4601291730423121377u64, 16312515716494630702u64, 134761178076u64)\n    put(data, 9559411037059581623u64, 17088522799596987756u64, 220884303248u64)\n    put(data, 12969820289641388091u64, 3588932524637852678u64, 144926370677u64)\n    put(data, 13628239920285957130u64, 107218049069817414u64, 117194556422u64)\n    put(data, 8049893933765800625u64, 1596707240462008334u64, 6005812302u64)\n    put(data, 10391755948840250234u64, 17461913142391587672u64, 78086557672u64)\n    put(data, 14454650777462444512u64, 4366474266651610090u64, 232946612208u64)\n    put(data, 14244638523341127254u64, 5539304013194805084u64, 240236707044u64)\n    put(data, 12246016810439753984u64, 4762470619211987849u64, 228300286272u64)\n    put(data, 9382741764551081984u64, 10835638458986644717u64, 64258174049u64)\n    put(data, 4608696190291148800u64, 16141642290510052058u64, 97587401137u64)\n    put(data, 1696483666416369664u64, 17390568670756355425u64, 177875040181u64)\n    put(data, 15416683541605384192u64, 12536768491333867107u64, 181942744616u64)\n    put(data, 7683140964294066176u64, 13145148522871947193u64, 40679619581u64)\n    put(data, 4611686018427387904u64, 5665349945233068642u64, 253712599929u64)\n    put(data, 0u64, 17074607537751066240u64, 121307119235u64)\n    put(data, 0u64, 6241525660962062336u64, 131925616329u64)\n    put(data, 0u64, 1142860629783085056u64, 201338353784u64)\n    put(data, 0u64, 16287527416870469632u64, 120061954598u64)\n    put(data, 0u64, 9028002014738513920u64, 38882948630u64)\n    put(data, 0u64, 16217462258161156096u64, 22489408969u64)\n    put(data, 0u64, 11529215046068469760u64, 201879150390u64)\n    put(data, 0u64, 0u64, 54625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 1722045467931902045u64, 19u64, 0u64)\n    put(data, 16095324008152856733u64, 19093352271u64, 0u64)\n    put(data, 15216188060094280738u64, 646608198162977646u64, 1u64)\n    put(data, 10793385929903030893u64, 12170458846894708007u64, 1035052700u64)\n    put(data, 12613277226875940039u64, 1797330480103086687u64, 156659761896u64)\n    put(data, 10644539625155600107u64, 10332188564497263448u64, 232097433480u64)\n    put(data, 6181262895644173983u64, 7524259485079594225u64, 136560109064u64)\n    put(data, 148914399627082292u64, 62681109059153749u64, 8407890924u64)\n    put(data, 14379289774887985969u64, 13480636451804037081u64, 236003397949u64)\n    put(data, 11743339675582627452u64, 6948168233012789004u64, 61730786766u64)\n    put(data, 7841621929809463497u64, 12015502974041806055u64, 206376660954u64)\n    put(data, 11168944680251236601u64, 7343801660689004040u64, 218651361721u64)\n    put(data, 6178560202529287410u64, 13670580858640731144u64, 185398108285u64)\n    put(data, 13295243308201596112u64, 5605073897566574851u64, 125741083673u64)\n    put(data, 17040673746172470291u64, 15387788940505247559u64, 25303851664u64)\n    put(data, 14957442487039409922u64, 17565181499678113030u64, 144834173709u64)\n    put(data, 17778155426506992152u64, 1893743623847493029u64, 13952210397u64)\n    put(data, 14945404984219733899u64, 10243498996716269784u64, 221102660047u64)\n    put(data, 11590754866058681505u64, 5619675836950314139u64, 207555301193u64)\n    put(data, 6020790784469412466u64, 10224869737511515088u64, 73304643237u64)\n    put(data, 7934351824569522326u64, 2574495974386198538u64, 165554291299u64)\n    put(data, 5038361112172116744u64, 7825756347302873178u64, 99139563706u64)\n    put(data, 3305187319649924210u64, 12071550103794656887u64, 186424235101u64)\n    put(data, 11470175511099161552u64, 7195875213867606691u64, 93654400042u64)\n    put(data, 7308362160352048610u64, 18271364438406891044u64, 42390089176u64)\n    put(data, 10666410671225576634u64, 16966521933952564706u64, 216990492650u64)\n    put(data, 2181639019945820785u64, 289920862029570129u64, 234919756997u64)\n    put(data, 4615749499734847918u64, 7804199568098625032u64, 197015716641u64)\n    put(data, 14076159200958497580u64, 5758118571242446585u64, 33423066506u64)\n    put(data, 5470405257862074105u64, 4030788293606375591u64, 138312148233u64)\n    put(data, 4565741478181339543u64, 4387716460037196127u64, 9218509471u64)\n    put(data, 8901832997861862329u64, 16807506478881285981u64, 159237858585u64)\n    put(data, 14190141170191714122u64, 17033060604413529717u64, 25911136751u64)\n    put(data, 4240772322245764096u64, 10498418508292170054u64, 239923364065u64)\n    put(data, 4422842195340951552u64, 13237752038744465016u64, 225569120407u64)\n    put(data, 15448426386733137920u64, 17737618428304633155u64, 151717619975u64)\n    put(data, 9203504548935630848u64, 13546183833248825736u64, 7961558221u64)\n    put(data, 5936377627571912704u64, 826778452978976229u64, 205734340097u64)\n    put(data, 468374361246531584u64, 13728076626990147292u64, 1044819749u64)\n    put(data, 0u64, 2794860281883592225u64, 37744200525u64)\n    put(data, 0u64, 8680705720425908736u64, 77151509679u64)\n    put(data, 0u64, 731520517439488000u64, 175470582000u64)\n    put(data, 0u64, 13120812320768917504u64, 240039655806u64)\n    put(data, 0u64, 2722954908557901824u64, 126711280661u64)\n    put(data, 0u64, 6860847004205973504u64, 21147611681u64)\n    put(data, 0u64, 6503197861922996224u64, 33371927261u64)\n    put(data, 0u64, 9223372036854775808u64, 221352539062u64)\n    put(data, 0u64, 0u64, 182500000000u64)\n    put(data, 8020875056524075716u64, 291341u64, 0u64)\n    put(data, 1400288714762747253u64, 291341434812508u64, 0u64)\n    put(data, 6136037711314764689u64, 12005656413127238315u64, 15793u64)\n    put(data, 15735488086392394102u64, 4821130826186787462u64, 177650827938u64)\n    put(data, 7735094782793634552u64, 14377899467066168118u64, 162261354025u64)\n    put(data, 7273689191766726188u64, 16575613239625444872u64, 41779427491u64)\n    put(data, 3410634565056431030u64, 4317827099179284377u64, 163898565794u64)\n    put(data, 4600927904885215898u64, 1242354770412171254u64, 162234069876u64)\n    put(data, 9154871331680374746u64, 994838588328896609u64, 116067348187u64)\n    put(data, 6117978272461042996u64, 17283309862013060457u64, 219053930307u64)\n    put(data, 13116904339287496285u64, 124242522249856586u64, 67936930105u64)\n    put(data, 867037205615660831u64, 11564608014666985609u64, 57006735200u64)\n    put(data, 1802487145191504830u64, 12401028575581654085u64, 96626918656u64)\n    put(data, 17242009718457409007u64, 2490725392961465727u64, 672261106u64)\n    put(data, 13620802355488468049u64, 1949482237120640915u64, 242135022494u64)\n    put(data, 4442227880594435745u64, 15410502396166200087u64, 158105681643u64)\n    put(data, 17354849212854314181u64, 15694919529799920727u64, 235835405008u64)\n    put(data, 18202319179831567886u64, 10324869370171768388u64, 208850823292u64)\n    put(data, 17568634016348874558u64, 1631866459122189059u64, 124559712290u64)\n    put(data, 7496925598312450672u64, 172020494461226230u64, 34088463658u64)\n    put(data, 601870379496813865u64, 12734610307908856767u64, 42009325249u64)\n    put(data, 11995106565680728027u64, 1467513250829340930u64, 193690344608u64)\n    put(data, 16331071694764184179u64, 13558759428494307997u64, 160079554052u64)\n    put(data, 9665962217000524208u64, 7915355143999496434u64, 4735021821u64)\n    put(data, 13682661374415474390u64, 2876370200608797469u64, 253429092262u64)\n    put(data, 6940361789924260864u64, 343685370404989503u64, 166155928341u64)\n    put(data, 9658039831644010465u64, 4837266557407634630u64, 21018631221u64)\n    put(data, 6843715893910236922u64, 9622591415747161468u64, 53262228745u64)\n    put(data, 47089792870595660u64, 16503783814424220982u64, 9521641725u64)\n    put(data, 14510386192097156932u64, 5377083431343591334u64, 253894671913u64)\n    put(data, 12059931208360040296u64, 16508482371299291595u64, 41291492276u64)\n    put(data, 14877013468459184620u64, 10515883558812249028u64, 180894926622u64)\n    put(data, 2397668560671695044u64, 63492062913405476u64, 30570067190u64)\n    put(data, 2195572305559232232u64, 11571919759617799697u64, 246003441911u64)\n    put(data, 3261686279425953792u64, 2956602334970088581u64, 247627315027u64)\n    put(data, 9333850662059900928u64, 13604736747717849839u64, 83160277733u64)\n    put(data, 5036522340217782272u64, 16573540719338151362u64, 229737514256u64)\n    put(data, 2800120215143186432u64, 12620703004601168151u64, 16898453442u64)\n    put(data, 441634238459019264u64, 14649407809089591941u64, 194684169680u64)\n    put(data, 720575940379279360u64, 11290375247898624432u64, 208794145988u64)\n    put(data, 0u64, 11020319450292874212u64, 196612052468u64)\n    put(data, 0u64, 8754634933362354176u64, 244597412714u64)\n    put(data, 0u64, 12976319450332528640u64, 106474589710u64)\n    put(data, 0u64, 17447331119627239424u64, 14703447686u64)\n    put(data, 0u64, 3665184902673858560u64, 134945821715u64)\n    put(data, 0u64, 12949678516038795264u64, 19198690071u64)\n    put(data, 0u64, 72057594037927936u64, 23702003479u64)\n    put(data, 0u64, 0u64, 23003906250u64)\n    put(data, 0u64, 0u64, 202000000000u64)\n    put(data, 8218347283861607400u64, 4u64, 0u64)\n    put(data, 17896200385973633643u64, 4445517498u64, 0u64)\n    put(data, 16333242102094352209u64, 4445517498970154966u64, 0u64)\n    put(data, 9981905728606788815u64, 9413159735776077452u64, 240991986u64)\n    put(data, 15219470018924839012u64, 14279163482889998017u64, 242510288411u64)\n    put(data, 17152070168529617370u64, 8693044629541194274u64, 27774075003u64)\n    put(data, 17841343440958328027u64, 11863110253260222498u64, 123471250893u64)\n    put(data, 17291078023923990493u64, 8319293368489531245u64, 205643100495u64)\n    put(data, 16744804581790759223u64, 3376307525676489265u64, 79450989797u64)\n    put(data, 5001622214111594905u64, 13205662254759912523u64, 229183029997u64)\n    put(data, 9676746897435398146u64, 5276250334231686323u64, 237715880385u64)\n    put(data, 8109025833995118532u64, 13790198520922745052u64, 193286026103u64)\n    put(data, 5495826424046694744u64, 14195535250150996227u64, 119747568159u64)\n    put(data, 7890288164365705852u64, 16425228796427004035u64, 31769541507u64)\n    put(data, 8616438349039895217u64, 4295900841296269186u64, 131890413437u64)\n    put(data, 15787154801788760618u64, 4533952595483946442u64, 125232881251u64)\n    put(data, 12418659311480782502u64, 12885038019373447184u64, 99245786062u64)\n    put(data, 4486988874116669987u64, 12140736240487831910u64, 206698499310u64)\n    put(data, 4828971301551875409u64, 6927124077155322074u64, 238658150630u64)\n    put(data, 14769106422014442226u64, 12477788342407819890u64, 230375520148u64)\n    put(data, 7763933466423188156u64, 7980854329409711087u64, 148676422261u64)\n    put(data, 10449097116253839963u64, 2062671021810827996u64, 117432642980u64)\n    put(data, 12870692502472900571u64, 2739521363598172769u64, 164111817620u64)\n    put(data, 11597479481311003817u64, 12897585686593465638u64, 148148509750u64)\n    put(data, 8294994869530047486u64, 1127632646629044686u64, 54699179521u64)\n    put(data, 16879315829924478241u64, 4833775019274666364u64, 1061129088u64)\n    put(data, 11873324837601439670u64, 15867662672939849256u64, 128262039468u64)\n    put(data, 3277599055636107318u64, 2092350330982953557u64, 172860187717u64)\n    put(data, 15363467897354242201u64, 13330062299842493592u64, 69113426538u64)\n    put(data, 10351182204479784367u64, 4479193352178519263u64, 106722624125u64)\n    put(data, 12811517584931924466u64, 3149393938889064983u64, 125242817558u64)\n    put(data, 16988930699558748726u64, 9736379904070620767u64, 22170728987u64)\n    put(data, 11683499918824718325u64, 3816238703055069186u64, 27527810212u64)\n    put(data, 13119300691281647499u64, 11598915938798661975u64, 164206878714u64)\n    put(data, 10125549106595354099u64, 17821633264606555643u64, 250628778492u64)\n    put(data, 17505352699870800544u64, 2514623558764574316u64, 252966112675u64)\n    put(data, 6756039242241163264u64, 4976730480406253215u64, 163136318016u64)\n    put(data, 3537338758766526464u64, 17276563697191611637u64, 64269789099u64)\n    put(data, 6522626374119718912u64, 12524734095940998814u64, 171936564394u64)\n    put(data, 4733294203482669056u64, 15331551308930355164u64, 170678967195u64)\n    put(data, 16997710893603094528u64, 15417115581125943816u64, 155831125061u64)\n    put(data, 16717361816799281152u64, 6010750237807115593u64, 69835763510u64)\n    put(data, 0u64, 5624630987553628432u64, 54325843423u64)\n    put(data, 0u64, 14881848243837640704u64, 223304911856u64)\n    put(data, 0u64, 15281613886881529856u64, 240806746609u64)\n    put(data, 0u64, 14057902358273196032u64, 241828417948u64)\n    put(data, 0u64, 16075318494433902592u64, 156762080413u64)\n    put(data, 0u64, 13891916000577716224u64, 157871444761u64)\n    put(data, 0u64, 7205759403792793600u64, 25753082275u64)\n    put(data, 0u64, 0u64, 163390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 3943737498063000362u64, 67833u64, 0u64)\n    put(data, 2870731037991212489u64, 67833213790438u64, 0u64)\n    put(data, 7389433400402095883u64, 4535831408134330609u64, 3677u64)\n    put(data, 5675049236146197433u64, 6204770794376564579u64, 93245887913u64)\n    put(data, 9953779846262904264u64, 13869812122751887467u64, 169336361298u64)\n    put(data, 12691978937179636241u64, 14253229412394467550u64, 82751884021u64)\n    put(data, 15523327331528198029u64, 12776557610216045332u64, 245772669114u64)\n    put(data, 15722603279568118520u64, 16493640728678654060u64, 186692618575u64)\n    put(data, 11838632364171816147u64, 9434398296825833151u64, 79894122055u64)\n    put(data, 7832751832367143680u64, 8773374058285327850u64, 71511439756u64)\n    put(data, 12167724027162940862u64, 12932015276748029367u64, 140475605560u64)\n    put(data, 4108211144748152962u64, 16293958583527755209u64, 56701045952u64)\n    put(data, 7530983398136343676u64, 13511893936143127948u64, 192883297264u64)\n    put(data, 1117587133956542355u64, 18409936402005226436u64, 240732481237u64)\n    put(data, 17134748625149490872u64, 2189663026458466887u64, 213998004652u64)\n    put(data, 4039918359454207848u64, 9497725274248154664u64, 172118701870u64)\n    put(data, 1830218764589441242u64, 14766925481127792125u64, 46514872718u64)\n    put(data, 637895981480825742u64, 6982373971809635814u64, 142800516634u64)\n    put(data, 8246879226348334620u64, 8616702383006884794u64, 26378515251u64)\n    put(data, 12389239568142583275u64, 3059473300040871066u64, 51467112372u64)\n    put(data, 14041257178803154398u64, 17123843157031495558u64, 180165854379u64)\n    put(data, 16312515716494630702u64, 11210627174210626524u64, 171928285397u64)\n    put(data, 17088522799596987756u64, 15868067138625928592u64, 213607729316u64)\n    put(data, 3588932524637852678u64, 4467869511636937589u64, 164860209643u64)\n    put(data, 107218049069817414u64, 10052108125844341766u64, 235242203691u64)\n    put(data, 1596707240462008334u64, 7470588003218451534u64, 43544925873u64)\n    put(data, 17461913142391587672u64, 2613527085490786280u64, 177404981387u64)\n    put(data, 4366474266651610090u64, 3632919450036549616u64, 139141679587u64)\n    put(data, 5539304013194805084u64, 179367907231218916u64, 227196940958u64)\n    put(data, 4762470619211987849u64, 13553068184555874624u64, 158009723553u64)\n    put(data, 10835638458986644717u64, 8798774862365584481u64, 161734713298u64)\n    put(data, 16141642290510052058u64, 910911255817064881u64, 210476982541u64)\n    put(data, 17390568670756355425u64, 2304331144765093813u64, 13049380598u64)\n    put(data, 12536768491333867107u64, 12248937023083640360u64, 246124918041u64)\n    put(data, 13145148522871947193u64, 10206039550662130685u64, 25664016206u64)\n    put(data, 5665349945233068642u64, 12267881323837852537u64, 78553270512u64)\n    put(data, 17074607537751066240u64, 2858642007937891971u64, 240665043179u64)\n    put(data, 6241525660962062336u64, 14171330289750320841u64, 235154967293u64)\n    put(data, 1142860629783085056u64, 6601103619749017720u64, 253768229354u64)\n    put(data, 16287527416870469632u64, 4919573414486739494u64, 234357846544u64)\n    put(data, 9028002014738513920u64, 3401998285294974486u64, 16266690609u64)\n    put(data, 16217462258161156096u64, 10799436256515532233u64, 49184422696u64)\n    put(data, 11529215046068469760u64, 10083786644665753398u64, 40585438612u64)\n    put(data, 0u64, 6481194517685688896u64, 148546643169u64)\n    put(data, 0u64, 15104161756860547072u64, 225351346258u64)\n    put(data, 0u64, 9556039274244079616u64, 82818798249u64)\n    put(data, 0u64, 1376343134954323968u64, 169518033927u64)\n    put(data, 0u64, 15682488278596976640u64, 7074611710u64)\n    put(data, 0u64, 1506454075355430912u64, 254850149393u64)\n    put(data, 0u64, 1152921504606846976u64, 17081665039u64)\n    put(data, 0u64, 0u64, 15062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 12170458846894708007u64, 1035052700u64, 0u64)\n    put(data, 1797330480103086687u64, 1035052700659761896u64, 0u64)\n    put(data, 10332188564497263448u64, 6172559441576707976u64, 56110319u64)\n    put(data, 7524259485079594225u64, 15083329738554729992u64, 239334615117u64)\n    put(data, 62681109059153749u64, 10013126833549229036u64, 77817668943u64)\n    put(data, 13480636451804037081u64, 5817156823499936061u64, 79542812693u64)\n    put(data, 6948168233012789004u64, 5282692560913632718u64, 21315348703u64)\n    put(data, 12015502974041806055u64, 10252307034225766362u64, 223286375337u64)\n    put(data, 7343801660689004040u64, 17981881283247669689u64, 169555778677u64)\n    put(data, 13670580858640731144u64, 11689290159733383293u64, 117974799737u64)\n    put(data, 5605073897566574851u64, 5530668968487988249u64, 121633677689u64)\n    put(data, 15387788940505247559u64, 10083765740821947024u64, 121299818165u64)\n    put(data, 17565181499678113030u64, 2798423656816843533u64, 181546642036u64)\n    put(data, 1893743623847493029u64, 7614494481582904797u64, 116151702850u64)\n    put(data, 10243498996716269784u64, 17811318500083423695u64, 66412782572u64)\n    put(data, 5619675836950314139u64, 11641467412200329033u64, 236965553510u64)\n    put(data, 10224869737511515088u64, 17733593025296340645u64, 102631085212u64)\n    put(data, 2574495974386198538u64, 3689424000190644835u64, 156961340004u64)\n    put(data, 7825756347302873178u64, 14966634145516728506u64, 100200004075u64)\n    put(data, 12071550103794656887u64, 14171681941562070109u64, 235811342862u64)\n    put(data, 7195875213867606691u64, 8130575762882608170u64, 14768248417u64)\n    put(data, 18271364438406891044u64, 5234550794400656856u64, 97440759395u64)\n    put(data, 16966521933952564706u64, 3020576149360486378u64, 99283765567u64)\n    put(data, 289920862029570129u64, 3038675756589057221u64, 63163745761u64)\n    put(data, 7804199568098625032u64, 15470260187120878369u64, 225164726942u64)\n    put(data, 5758118571242446585u64, 3497929414841828746u64, 158838644485u64)\n    put(data, 4030788293606375591u64, 9935840636861015305u64, 5189623133u64)\n    put(data, 4387716460037196127u64, 3647355485153741471u64, 93538623000u64)\n    put(data, 16807506478881285981u64, 766100215038272793u64, 24197723537u64)\n    put(data, 17033060604413529717u64, 16128087474216800751u64, 145041530375u64)\n    put(data, 10498418508292170054u64, 16216631732633731297u64, 7874305373u64)\n    put(data, 13237752038744465016u64, 13760220872779997335u64, 93879105367u64)\n    put(data, 17737618428304633155u64, 3826276262374222087u64, 87745943068u64)\n    put(data, 13546183833248825736u64, 14938032745839181005u64, 28207422851u64)\n    put(data, 826778452978976229u64, 14479259995009508865u64, 131809792377u64)\n    put(data, 13728076626990147292u64, 2372033248156102437u64, 121784922257u64)\n    put(data, 2794860281883592225u64, 792005346826701645u64, 145128588180u64)\n    put(data, 8680705720425908736u64, 16278924527931792559u64, 148042934695u64)\n    put(data, 731520517439488000u64, 17442516423538940144u64, 167882482266u64)\n    put(data, 13120812320768917504u64, 13844184233048446u64, 90945560710u64)\n    put(data, 2722954908557901824u64, 13486193870480782357u64, 134000750494u64)\n    put(data, 6860847004205973504u64, 11931315179184648737u64, 158731088034u64)\n    put(data, 6503197861922996224u64, 16492562205587485405u64, 162646797891u64)\n    put(data, 9223372036854775808u64, 12128987217680380854u64, 67894063588u64)\n    put(data, 0u64, 10568123814189138176u64, 228657513714u64)\n    put(data, 0u64, 17007583519117541376u64, 242572899139u64)\n    put(data, 0u64, 143791533903052800u64, 67921982950u64)\n    put(data, 0u64, 12398714235792654336u64, 230007794954u64)\n    put(data, 0u64, 9659957317919047680u64, 10672135645u64)\n    put(data, 0u64, 9412523221204336640u64, 221523667335u64)\n    put(data, 0u64, 4611686018427387904u64, 135510253906u64)\n    put(data, 0u64, 0u64, 82250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 12005656413127238315u64, 15793u64, 0u64)\n    put(data, 4821130826186787462u64, 15793650827938u64, 0u64)\n    put(data, 14377899467066168118u64, 3237900842885170729u64, 856u64)\n    put(data, 16575613239625444872u64, 7515893506498066595u64, 88175526956u64)\n    put(data, 4317827099179284377u64, 7300206309181072546u64, 44407437403u64)\n    put(data, 1242354770412171254u64, 5999737279837044u64, 91395744977u64)\n    put(data, 994838588328896609u64, 7556839307242450651u64, 209000325246u64)\n    put(data, 17283309862013060457u64, 12946035041643640643u64, 126409657079u64)\n    put(data, 124242522249856586u64, 15885877642352740665u64, 247701805965u64)\n    put(data, 11564608014666985609u64, 10770818348246089568u64, 141861175152u64)\n    put(data, 12401028575581654085u64, 11635415503599551744u64, 112583887232u64)\n    put(data, 2490725392961465727u64, 6248053924100826098u64, 128630757138u64)\n    put(data, 1949482237120640915u64, 16894170802729859998u64, 18338707681u64)\n    put(data, 15410502396166200087u64, 6143589029651889899u64, 225915834834u64)\n    put(data, 15694919529799920727u64, 11812087701837886160u64, 210333044628u64)\n    put(data, 10324869370171768388u64, 7306705080150829180u64, 148640334557u64)\n    put(data, 1631866459122189059u64, 1485332570280714274u64, 221396097276u64)\n    put(data, 172020494461226230u64, 18042602303295630634u64, 252080520039u64)\n    put(data, 12734610307908856767u64, 13397029889257074369u64, 103978091430u64)\n    put(data, 1467513250829340930u64, 9948104869613411488u64, 166726254445u64)\n    put(data, 13558759428494307997u64, 10836066241170646532u64, 109539287845u64)\n    put(data, 7915355143999496434u64, 18330574781234459389u64, 37587424327u64)\n    put(data, 2876370200608797469u64, 666297360208433062u64, 71993702450u64)\n    put(data, 343685370404989503u64, 5035352224889324309u64, 50036120052u64)\n    put(data, 4837266557407634630u64, 1341745796439923765u64, 244272966991u64)\n    put(data, 9622591415747161468u64, 6846932182653803785u64, 79072736185u64)\n    put(data, 16503783814424220982u64, 6727685027257825533u64, 185371172937u64)\n    put(data, 5377083431343591334u64, 2168538874806877737u64, 73364708536u64)\n    put(data, 16508482371299291595u64, 17694936100676971444u64, 184117556727u64)\n    put(data, 10515883558812249028u64, 2163944241059563294u64, 247959244408u64)\n    put(data, 63492062913405476u64, 6727780864524301558u64, 120117307652u64)\n    put(data, 11571919759617799697u64, 8599551977795002615u64, 4364713731u64)\n    put(data, 2956602334970088581u64, 15428264807806859091u64, 3466182646u64)\n    put(data, 13604736747717849839u64, 2126771385339683557u64, 246836367911u64)\n    put(data, 16573540719338151362u64, 15094316562082972944u64, 39115292507u64)\n    put(data, 12620703004601168151u64, 8111300598225956802u64, 91818264540u64)\n    put(data, 14649407809089591941u64, 9481215200564260304u64, 220439714486u64)\n    put(data, 11290375247898624432u64, 16836674128623424708u64, 182513977705u64)\n    put(data, 11020319450292874212u64, 7087243115299722740u64, 105912717933u64)\n    put(data, 8754634933362354176u64, 2343560867338408810u64, 109384200219u64)\n    put(data, 12976319450332528640u64, 3431385749090422286u64, 27127044689u64)\n    put(data, 17447331119627239424u64, 3504545517469224582u64, 81186015794u64)\n    put(data, 3665184902673858560u64, 3333759805712094227u64, 50189981793u64)\n    put(data, 12949678516038795264u64, 3595183476205994775u64, 97180723481u64)\n    put(data, 72057594037927936u64, 14191566632569921303u64, 25194895286u64)\n    put(data, 0u64, 12917427671358095562u64, 182769326368u64)\n    put(data, 0u64, 3883793922738316288u64, 32700255157u64)\n    put(data, 0u64, 7857281689266421760u64, 181210540890u64)\n    put(data, 0u64, 15987081651486195712u64, 90425944093u64)\n    put(data, 0u64, 16827562156399525888u64, 29866661432u64)\n    put(data, 0u64, 7012737938513461248u64, 56912223972u64)\n    put(data, 0u64, 7385903388887613440u64, 228380161285u64)\n    put(data, 0u64, 0u64, 5400390625u64)\n    put(data, 0u64, 0u64, 225000000000u64)\n    put(data, 9413159735776077452u64, 240991986u64, 0u64)\n    put(data, 14279163482889998017u64, 240991986510288411u64, 0u64)\n    put(data, 8693044629541194274u64, 14135788013842776187u64, 13064201u64)\n    put(data, 11863110253260222498u64, 13284322918167594445u64, 9766302603u64)\n    put(data, 8319293368489531245u64, 7264587765474046287u64, 139720144588u64)\n    put(data, 3376307525676489265u64, 16176482219778368741u64, 204393814091u64)\n    put(data, 13205662254759912523u64, 5401983818872095469u64, 75876928858u64)\n    put(data, 5276250334231686323u64, 11208857446851049921u64, 90292842129u64)\n    put(data, 13790198520922745052u64, 13794690008281035639u64, 145607633379u64)\n    put(data, 14195535250150996227u64, 14519782740993303071u64, 227747811643u64)\n    put(data, 16425228796427004035u64, 10885858587044789123u64, 59787118999u64)\n    put(data, 4295900841296269186u64, 8710500938899914621u64, 151590123576u64)\n    put(data, 4533952595483946442u64, 1284182587483102819u64, 56472197202u64)\n    put(data, 12885038019373447184u64, 10346074482131502030u64, 82069615677u64)\n    put(data, 12140736240487831910u64, 9429804686255246574u64, 61560861821u64)\n    put(data, 6927124077155322074u64, 6412022633845121254u64, 125511190736u64)\n    put(data, 12477788342407819890u64, 8892351297529018260u64, 208347596443u64)\n    put(data, 7980854329409711087u64, 14098160105983060597u64, 155482055329u64)\n    put(data, 2062671021810827996u64, 13793833029739474340u64, 161764262790u64)\n    put(data, 2739521363598172769u64, 16367653765996977044u64, 134747765186u64)\n    put(data, 12897585686593465638u64, 10684788343333772342u64, 194887292288u64)\n    put(data, 1127632646629044686u64, 13272681218705145345u64, 128579223536u64)\n    put(data, 4833775019274666364u64, 11093568615497829248u64, 240719513490u64)\n    put(data, 15867662672939849256u64, 12488220765137758124u64, 146601383559u64)\n    put(data, 2092350330982953557u64, 3727114642519696453u64, 135676987804u64)\n    put(data, 13330062299842493592u64, 11549865375695057514u64, 156202047289u64)\n    put(data, 4479193352178519263u64, 11292809154908783229u64, 57626119456u64)\n    put(data, 3149393938889064983u64, 17723904861837310998u64, 32612184410u64)\n    put(data, 9736379904070620767u64, 14877674388187150875u64, 90960814807u64)\n    put(data, 3816238703055069186u64, 12178961950105734308u64, 215806520344u64)\n    put(data, 11598915938798661975u64, 4540604068069253114u64, 24660222850u64)\n    put(data, 17821633264606555643u64, 13832478722153359868u64, 130246146639u64)\n    put(data, 2514623558764574316u64, 1308046668730371491u64, 79749860174u64)\n    put(data, 4976730480406253215u64, 18400531023544756800u64, 78070909351u64)\n    put(data, 17276563697191611637u64, 9789823458621466539u64, 167997494785u64)\n    put(data, 12524734095940998814u64, 1924870562610267306u64, 1530707393u64)\n    put(data, 15331551308930355164u64, 5290016144582400923u64, 193104347442u64)\n    put(data, 15417115581125943816u64, 15162883663174059077u64, 50286772349u64)\n    put(data, 6010750237807115593u64, 8078086116520046390u64, 125821981570u64)\n    put(data, 5624630987553628432u64, 15731407332173190623u64, 130437913925u64)\n    put(data, 14881848243837640704u64, 5346389182763011056u64, 69852801300u64)\n    put(data, 15281613886881529856u64, 6368422217216252401u64, 20289828338u64)\n    put(data, 14057902358273196032u64, 2961453088119116188u64, 242345232860u64)\n    put(data, 16075318494433902592u64, 10932141691610170525u64, 220160540693u64)\n    put(data, 13891916000577716224u64, 11034016191361782553u64, 21592632588u64)\n    put(data, 7205759403792793600u64, 5455325785621453219u64, 12598155216u64)\n    put(data, 0u64, 7735615202566149352u64, 208295733803u64)\n    put(data, 0u64, 7502396497775759360u64, 43419348540u64)\n    put(data, 0u64, 1601286435751591936u64, 60406705729u64)\n    put(data, 0u64, 11449383158571597824u64, 65086805911u64)\n    put(data, 0u64, 13043944595690356736u64, 151620672304u64)\n    put(data, 0u64, 7773494431818186752u64, 48707113653u64)\n    put(data, 0u64, 9943947977234055168u64, 181421401977u64)\n    put(data, 0u64, 0u64, 121539062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 4535831408134330609u64, 3677u64, 0u64)\n    put(data, 6204770794376564579u64, 3677245887913u64, 0u64)\n    put(data, 13869812122751887467u64, 6343817245135589714u64, 199u64)\n    put(data, 14253229412394467550u64, 17549323075660516085u64, 199343899021u64)\n    put(data, 12776557610216045332u64, 3948641822109421754u64, 141951350710u64)\n    put(data, 16493640728678654060u64, 1750739713693534543u64, 182214056302u64)\n    put(data, 9434398296825833151u64, 962163898128633415u64, 110094907790u64)\n    put(data, 8773374058285327850u64, 7967320249386531212u64, 142052159009u64)\n    put(data, 12932015276748029367u64, 3018466665533383224u64, 33431909296u64)\n    put(data, 16293958583527755209u64, 15076865731854945472u64, 176163631405u64)\n    put(data, 13511893936143127948u64, 691187172844604400u64, 45817318529u64)\n    put(data, 18409936402005226436u64, 13274492813370992341u64, 129037469331u64)\n    put(data, 2189663026458466887u64, 6364168818499152300u64, 147719611697u64)\n    put(data, 9497725274248154664u64, 17599380787401914158u64, 49345002282u64)\n    put(data, 14766925481127792125u64, 3782323149461692814u64, 42954064344u64)\n    put(data, 6982373971809635814u64, 14470163442442237466u64, 216205040148u64)\n    put(data, 8616702383006884794u64, 476109872130437939u64, 20784429132u64)\n    put(data, 3059473300040871066u64, 16330548844673355700u64, 76025809967u64)\n    put(data, 17123843157031495558u64, 14089158961463739563u64, 47885280826u64)\n    put(data, 11210627174210626524u64, 13385510793074798805u64, 58763774837u64)\n    put(data, 15868067138625928592u64, 1549401308746959012u64, 117725629994u64)\n    put(data, 4467869511636937589u64, 4607384943843027435u64, 42083993213u64)\n    put(data, 10052108125844341766u64, 5157353797716093483u64, 125249766838u64)\n    put(data, 7470588003218451534u64, 10846828782671550129u64, 182279580709u64)\n    put(data, 2613527085490786280u64, 9915857350819131531u64, 37588007766u64)\n    put(data, 3632919450036549616u64, 1673544973504317923u64, 86537539704u64)\n    put(data, 179367907231218916u64, 14780986291622785694u64, 120090723054u64)\n    put(data, 13553068184555874624u64, 8168111319515466401u64, 238801278872u64)\n    put(data, 8798774862365584481u64, 16345760387859734482u64, 152442794201u64)\n    put(data, 910911255817064881u64, 3177475373321281805u64, 217886105446u64)\n    put(data, 2304331144765093813u64, 2558676822419554038u64, 102172251285u64)\n    put(data, 12248937023083640360u64, 8813474062662382873u64, 149138706148u64)\n    put(data, 10206039550662130685u64, 5426294560236228430u64, 228477779386u64)\n    put(data, 12267881323837852537u64, 9919177474128333040u64, 186294160017u64)\n    put(data, 2858642007937891971u64, 6197383943089627371u64, 145537719688u64)\n    put(data, 14171330289750320841u64, 13673239314867423997u64, 136335960856u64)\n    put(data, 6601103619749017720u64, 9309584098968723946u64, 24741227788u64)\n    put(data, 4919573414486739494u64, 4647101757759615504u64, 12504673565u64)\n    put(data, 3401998285294974486u64, 1405809295505096753u64, 29251919891u64)\n    put(data, 10799436256515532233u64, 11332704079573859112u64, 19076209074u64)\n    put(data, 10083786644665753398u64, 2960072434514044308u64, 178614347119u64)\n    put(data, 6481194517685688896u64, 3887266602785432801u64, 111160465848u64)\n    put(data, 15104161756860547072u64, 14545546084687849554u64, 184210729144u64)\n    put(data, 9556039274244079616u64, 4617763804182385321u64, 184788515633u64)\n    put(data, 1376343134954323968u64, 7857823815580249095u64, 49250329477u64)\n    put(data, 15682488278596976640u64, 10939326736548364798u64, 133425973482u64)\n    put(data, 1506454075355430912u64, 12262012446566951953u64, 234593022090u64)\n    put(data, 1152921504606846976u64, 12555024338687723023u64, 138664725026u64)\n    put(data, 0u64, 3332969632922829472u64, 34680609233u64)\n    put(data, 0u64, 15535060143360327680u64, 209180680645u64)\n    put(data, 0u64, 15794322927987458048u64, 197842157297u64)\n    put(data, 0u64, 10571474314433921024u64, 241856211961u64)\n    put(data, 0u64, 16679514427547975680u64, 249573080770u64)\n    put(data, 0u64, 16925653299565166592u64, 194904198288u64)\n    put(data, 0u64, 16717361816799281152u64, 144917541503u64)\n    put(data, 0u64, 0u64, 127906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 6172559441576707976u64, 56110319u64, 0u64)\n    put(data, 15083329738554729992u64, 56110319334615117u64, 0u64)\n    put(data, 10013126833549229036u64, 9335385384027907407u64, 3041746u64)\n    put(data, 5817156823499936061u64, 13237828406194798613u64, 210506072255u64)\n    put(data, 5282692560913632718u64, 15667486867836528863u64, 191717624115u64)\n    put(data, 10252307034225766362u64, 17982325043592934313u64, 51849336164u64)\n    put(data, 17981881283247669689u64, 17159117626917379189u64, 100974823793u64)\n    put(data, 11689290159733383293u64, 8336208968408929657u64, 113930197630u64)\n    put(data, 5530668968487988249u64, 12767090573379150201u64, 126451906793u64)\n    put(data, 10083765740821947024u64, 14736070002412246709u64, 233692105366u64)\n    put(data, 2798423656816843533u64, 9697296975344560756u64, 150798843955u64)\n    put(data, 7614494481582904797u64, 7291706381199103298u64, 51525691522u64)\n    put(data, 17811318500083423695u64, 18098546597780825068u64, 130395284194u64)\n    put(data, 11641467412200329033u64, 132913902678533478u64, 226981124177u64)\n    put(data, 17733593025296340645u64, 1879347741692007580u64, 81007205277u64)\n    put(data, 3689424000190644835u64, 4056624629214083684u64, 157101879645u64)\n    put(data, 14966634145516728506u64, 14713227692042795499u64, 93219910061u64)\n    put(data, 14171681941562070109u64, 7366415124022528526u64, 173797605671u64)\n    put(data, 8130575762882608170u64, 825770353378039393u64, 39399334164u64)\n    put(data, 5234550794400656856u64, 10244023944395357795u64, 20044765100u64)\n    put(data, 3020576149360486378u64, 14302658294713551167u64, 172555329650u64)\n    put(data, 3038675756589057221u64, 14246653166206862817u64, 114775348659u64)\n    put(data, 15470260187120878369u64, 12404486258134291102u64, 179772312615u64)\n    put(data, 3497929414841828746u64, 8887442218637942533u64, 39672448547u64)\n    put(data, 9935840636861015305u64, 1186724038081863005u64, 35481789208u64)\n    put(data, 3647355485153741471u64, 211331772484951576u64, 24064332439u64)\n    put(data, 766100215038272793u64, 6311919513247413649u64, 151011456318u64)\n    put(data, 16128087474216800751u64, 8131780018703965703u64, 62342169842u64)\n    put(data, 16216631732633731297u64, 2262544347226725725u64, 242440824678u64)\n    put(data, 13760220872779997335u64, 15318188749880522583u64, 102122652774u64)\n    put(data, 3826276262374222087u64, 1073117094162650652u64, 102830400676u64)\n    put(data, 14938032745839181005u64, 4447950380665871747u64, 164058173794u64)\n    put(data, 14479259995009508865u64, 5373227185066463609u64, 98241123873u64)\n    put(data, 2372033248156102437u64, 6739731406934274193u64, 33291283229u64)\n    put(data, 792005346826701645u64, 12328812617001239444u64, 29365361571u64)\n    put(data, 16278924527931792559u64, 3246111484407310759u64, 163668346271u64)\n    put(data, 17442516423538940144u64, 3250825415176839770u64, 159175972056u64)\n    put(data, 13844184233048446u64, 16146270540000862342u64, 216176227598u64)\n    put(data, 13486193870480782357u64, 15686773375425916830u64, 14875291079u64)\n    put(data, 11931315179184648737u64, 11920791905793880226u64, 199850381688u64)\n    put(data, 16492562205587485405u64, 1853290561644080707u64, 120646227424u64)\n    put(data, 12128987217680380854u64, 12157689141506159076u64, 224100467082u64)\n    put(data, 10568123814189138176u64, 18100318838862562546u64, 138659069648u64)\n    put(data, 17007583519117541376u64, 7171257882533475139u64, 208981220250u64)\n    put(data, 143791533903052800u64, 14477550873015039462u64, 154388754668u64)\n    put(data, 12398714235792654336u64, 8109481182495403274u64, 236784829605u64)\n    put(data, 9659957317919047680u64, 14565395719337663965u64, 165439615855u64)\n    put(data, 9412523221204336640u64, 1860318978161305991u64, 111789591684u64)\n    put(data, 4611686018427387904u64, 16268646275151585618u64, 132100848093u64)\n    put(data, 0u64, 13759019338835519104u64, 221881925081u64)\n    put(data, 0u64, 17003783176010661888u64, 217745877932u64)\n    put(data, 0u64, 18357489540307877888u64, 172921776932u64)\n    put(data, 0u64, 905481790074912768u64, 36995161502u64)\n    put(data, 0u64, 3638882110636294144u64, 158049086266u64)\n    put(data, 0u64, 9011702854368362496u64, 58197264194u64)\n    put(data, 0u64, 11529215046068469760u64, 66488525390u64)\n    put(data, 0u64, 0u64, 78625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 3237900842885170729u64, 856u64, 0u64)\n    put(data, 7515893506498066595u64, 856175526956u64, 0u64)\n    put(data, 7300206309181072546u64, 7625299565768063067u64, 46u64)\n    put(data, 5999737279837044u64, 13889021769065194705u64, 46413368317u64)\n    put(data, 7556839307242450651u64, 14498170692313014398u64, 253752925378u64)\n    put(data, 12946035041643640643u64, 1541631360972245751u64, 194785947408u64)\n    put(data, 15885877642352740665u64, 9903958882920799117u64, 16083572003u64)\n    put(data, 10770818348246089568u64, 15744148547788062576u64, 35536894686u64)\n    put(data, 11635415503599551744u64, 17936061801321712000u64, 222853492002u64)\n    put(data, 6248053924100826098u64, 9986394078324430610u64, 34972315858u64)\n    put(data, 16894170802729859998u64, 13849561248103430369u64, 210541363507u64)\n    put(data, 6143589029651889899u64, 12142378807953854930u64, 51750786219u64)\n    put(data, 11812087701837886160u64, 2513847703931031444u64, 171658239674u64)\n    put(data, 7306705080150829180u64, 1752183758129038045u64, 186136275957u64)\n    put(data, 1485332570280714274u64, 15824833342220556540u64, 245094986071u64)\n    put(data, 18042602303295630634u64, 8168747198299470695u64, 87857865934u64)\n    put(data, 13397029889257074369u64, 17414799840149357478u64, 206442828672u64)\n    put(data, 9948104869613411488u64, 83147520704167789u64, 128944058191u64)\n    put(data, 10836066241170646532u64, 2383542703041471269u64, 79004507436u64)\n    put(data, 18330574781234459389u64, 15540952725549257799u64, 44129212108u64)\n    put(data, 666297360208433062u64, 6949835416232048690u64, 204842476735u64)\n    put(data, 5035352224889324309u64, 15398868937585367540u64, 191376751332u64)\n    put(data, 1341745796439923765u64, 14710915985268256079u64, 228834774357u64)\n    put(data, 6846932182653803785u64, 9665704836873335737u64, 85797480353u64)\n    put(data, 6727685027257825533u64, 2528789298740305993u64, 161523978909u64)\n    put(data, 2168538874806877737u64, 10562914675687726264u64, 157137085942u64)\n    put(data, 17694936100676971444u64, 17671658300096837111u64, 246572616751u64)\n    put(data, 2163944241059563294u64, 356471401631698552u64, 47957982516u64)\n    put(data, 6727780864524301558u64, 7450677157218003204u64, 52019324353u64)\n    put(data, 8599551977795002615u64, 317174560787152643u64, 193403902018u64)\n    put(data, 15428264807806859091u64, 7251937674440720374u64, 66017194067u64)\n    put(data, 2126771385339683557u64, 1252631516699038247u64, 83393128329u64)\n    put(data, 15094316562082972944u64, 10818009768860843867u64, 137067905290u64)\n    put(data, 8111300598225956802u64, 12330114194950162396u64, 10586445484u64)\n    put(data, 9481215200564260304u64, 15826681638261168822u64, 172668416829u64)\n    put(data, 16836674128623424708u64, 14240150078499211625u64, 61857966130u64)\n    put(data, 7087243115299722740u64, 10725372116242125421u64, 50771960082u64)\n    put(data, 2343560867338408810u64, 8434925524647833627u64, 18581423587u64)\n    put(data, 3431385749090422286u64, 17133902668520348241u64, 227457258228u64)\n    put(data, 3504545517469224582u64, 15093996047981365810u64, 244928830724u64)\n    put(data, 3333759805712094227u64, 6187974166976813153u64, 4818247165u64)\n    put(data, 3595183476205994775u64, 13946144707720259865u64, 253335450751u64)\n    put(data, 14191566632569921303u64, 9138079832881862582u64, 127756022019u64)\n    put(data, 12917427671358095562u64, 6600697628576225568u64, 3495376300u64)\n    put(data, 3883793922738316288u64, 8137099536646556597u64, 172357824535u64)\n    put(data, 7857281689266421760u64, 14169855543453903706u64, 23441113049u64)\n    put(data, 15987081651486195712u64, 3706403268650100765u64, 217768149408u64)\n    put(data, 16827562156399525888u64, 14736932266877982264u64, 160200924523u64)\n    put(data, 7012737938513461248u64, 18004795125138956004u64, 107798890698u64)\n    put(data, 7385903388887613440u64, 9068489270661002501u64, 202976041899u64)\n    put(data, 0u64, 7758835715193269217u64, 171491603788u64)\n    put(data, 0u64, 16943947811135261184u64, 76420607326u64)\n    put(data, 0u64, 6745843108403216384u64, 94918533251u64)\n    put(data, 0u64, 12338229654069444608u64, 131365692887u64)\n    put(data, 0u64, 14358176069683511296u64, 215668856769u64)\n    put(data, 0u64, 7083775185760813056u64, 193778358284u64)\n    put(data, 0u64, 5350276357316149248u64, 12384012222u64)\n    put(data, 0u64, 9223372036854775808u64, 190290039062u64)\n    put(data, 0u64, 0u64, 22500000000u64)\n    put(data, 14135788013842776187u64, 13064201u64, 0u64)\n    put(data, 13284322918167594445u64, 13064201766302603u64, 0u64)\n    put(data, 7264587765474046287u64, 14699116688460625612u64, 708211u64)\n    put(data, 16176482219778368741u64, 6684126021499623499u64, 115796840712u64)\n    put(data, 5401983818872095469u64, 12614606079692508506u64, 8362347197u64)\n    put(data, 11208857446851049921u64, 15358270276683001489u64, 189683839165u64)\n    put(data, 13794690008281035639u64, 18077126190953408995u64, 189832573499u64)\n    put(data, 14519782740993303071u64, 7864121581925945659u64, 59979962974u64)\n    put(data, 10885858587044789123u64, 3518026639210514839u64, 94426314885u64)\n    put(data, 8710500938899914621u64, 4698310163811252280u64, 133190712606u64)\n    put(data, 1284182587483102819u64, 6101155398200416338u64, 30254695904u64)\n    put(data, 10346074482131502030u64, 16049178580360033341u64, 224330744296u64)\n    put(data, 9429804686255246574u64, 3167464649127375997u64, 232870027714u64)\n    put(data, 6412022633845121254u64, 12778923935480989904u64, 194171708602u64)\n    put(data, 8892351297529018260u64, 11875553912612980379u64, 186692746854u64)\n    put(data, 14098160105983060597u64, 10628760849351697057u64, 102643775067u64)\n    put(data, 13793833029739474340u64, 3408944711673234310u64, 91576186280u64)\n    put(data, 16367653765996977044u64, 2102091496050506178u64, 168184799263u64)\n    put(data, 10684788343333772342u64, 6254611118630245760u64, 31113954608u64)\n    put(data, 13272681218705145345u64, 2647941151989776368u64, 48339063148u64)\n    put(data, 11093568615497829248u64, 8855437735410157458u64, 108143545177u64)\n    put(data, 12488220765137758124u64, 10184270603132180103u64, 89480054241u64)\n    put(data, 3727114642519696453u64, 12079083162535627164u64, 225552090415u64)\n    put(data, 11549865375695057514u64, 5952952868716156729u64, 47654808410u64)\n    put(data, 11292809154908783229u64, 11958907037815852320u64, 90322710221u64)\n    put(data, 17723904861837310998u64, 10101562137321697626u64, 205648293649u64)\n    put(data, 14877674388187150875u64, 13633527411279258327u64, 17547606780u64)\n    put(data, 12178961950105734308u64, 16555627393501768728u64, 252739075001u64)\n    put(data, 4540604068069253114u64, 6359650463500280706u64, 185897482359u64)\n    put(data, 13832478722153359868u64, 8093923611102181967u64, 119344757342u64)\n    put(data, 1308046668730371491u64, 2848827352928635726u64, 94438772478u64)\n    put(data, 18400531023544756800u64, 4686723431961561511u64, 254154435240u64)\n    put(data, 9789823458621466539u64, 6245554925867652609u64, 168254067786u64)\n    put(data, 1924870562610267306u64, 17527406820792516033u64, 74338572210u64)\n    put(data, 5290016144582400923u64, 12119966834653692210u64, 178950162627u64)\n    put(data, 15162883663174059077u64, 11606502845877928061u64, 195657024718u64)\n    put(data, 8078086116520046390u64, 424311496652297090u64, 206629189780u64)\n    put(data, 15731407332173190623u64, 5977664048034127173u64, 148023001972u64)\n    put(data, 5346389182763011056u64, 6702712461535947028u64, 116324049817u64)\n    put(data, 6368422217216252401u64, 11384349854055020018u64, 153363354770u64)\n    put(data, 2961453088119116188u64, 3782955013294836188u64, 146617146842u64)\n    put(data, 10932141691610170525u64, 3531805968821207061u64, 218205074402u64)\n    put(data, 11034016191361782553u64, 3867566898657193228u64, 226191459585u64)\n    put(data, 5455325785621453219u64, 12688734637425072080u64, 1209661221u64)\n    put(data, 7735615202566149352u64, 18435982764454619691u64, 37687857682u64)\n    put(data, 7502396497775759360u64, 4728836163964677692u64, 18999416628u64)\n    put(data, 1601286435751591936u64, 2120012917348838977u64, 52256350722u64)\n    put(data, 11449383158571597824u64, 9856965465824679831u64, 2114926130u64)\n    put(data, 13043944595690356736u64, 11217197671061248816u64, 50534347168u64)\n    put(data, 7773494431818186752u64, 3840562972677739189u64, 160608085504u64)\n    put(data, 9943947977234055168u64, 17104366978925258617u64, 208197335u64)\n    put(data, 0u64, 16177877219841993444u64, 215927229591u64)\n    put(data, 0u64, 7338522384267208704u64, 151877004481u64)\n    put(data, 0u64, 10935240458612244480u64, 193397822095u64)\n    put(data, 0u64, 1732868046462124032u64, 143592800573u64)\n    put(data, 0u64, 557965042578882560u64, 61093938965u64)\n    put(data, 0u64, 10454684322475540480u64, 21030247345u64)\n    put(data, 0u64, 13907115649320091648u64, 177566749572u64)\n    put(data, 0u64, 0u64, 132753906250u64)\n    put(data, 0u64, 0u64, 74000000000u64)\n    put(data, 6343817245135589714u64, 199u64, 0u64)\n    put(data, 17549323075660516085u64, 199343899021u64, 0u64)\n    put(data, 3948641822109421754u64, 14876458284855834550u64, 10u64)\n    put(data, 1750739713693534543u64, 10450704926982265198u64, 10806454419u64)\n    put(data, 962163898128633415u64, 5385653213018257806u64, 147566533849u64)\n    put(data, 7967320249386531212u64, 12735569669880147489u64, 217291956845u64)\n    put(data, 3018466665533383224u64, 3619762560577729456u64, 109690396615u64)\n    put(data, 15076865731854945472u64, 11123448126624084269u64, 199196227721u64)\n    put(data, 691187172844604400u64, 4072715118852885633u64, 137603003331u64)\n    put(data, 13274492813370992341u64, 18239087231420827283u64, 195220782328u64)\n    put(data, 6364168818499152300u64, 423431461216085297u64, 248988742900u64)\n    put(data, 17599380787401914158u64, 9360976716520160042u64, 244022954265u64)\n    put(data, 3782323149461692814u64, 11655927117263208920u64, 25507459564u64)\n    put(data, 14470163442442237466u64, 2646622721938364948u64, 236631869075u64)\n    put(data, 476109872130437939u64, 4496462484548171852u64, 147143473705u64)\n    put(data, 16330548844673355700u64, 13140258519803350063u64, 41243753719u64)\n    put(data, 14089158961463739563u64, 13089764333320627770u64, 247712334841u64)\n    put(data, 13385510793074798805u64, 6926286827289840501u64, 249709597546u64)\n    put(data, 1549401308746959012u64, 4985580225290866218u64, 106375474761u64)\n    put(data, 4607384943843027435u64, 10478790837359789693u64, 73270268845u64)\n    put(data, 5157353797716093483u64, 10041191967455692214u64, 173568056389u64)\n    put(data, 10846828782671550129u64, 5035461258013813797u64, 69544334107u64)\n    put(data, 9915857350819131531u64, 14208759661559249750u64, 27272972901u64)\n    put(data, 1673544973504317923u64, 12347272163241758840u64, 101770258404u64)\n    put(data, 14780986291622785694u64, 3372534174410277614u64, 228669346965u64)\n    put(data, 8168111319515466401u64, 17226704187274712984u64, 149182825443u64)\n    put(data, 16345760387859734482u64, 4250480179449852121u64, 227933861505u64)\n    put(data, 3177475373321281805u64, 4303723537755414374u64, 129230418992u64)\n    put(data, 2558676822419554038u64, 8680503847344854165u64, 48233305320u64)\n    put(data, 8813474062662382873u64, 8817608623911079652u64, 232470571056u64)\n    put(data, 5426294560236228430u64, 5692030448698539450u64, 48478003521u64)\n    put(data, 9919177474128333040u64, 16908836314686769809u64, 65308565588u64)\n    put(data, 6197383943089627371u64, 6073762347067727240u64, 84916629853u64)\n    put(data, 13673239314867423997u64, 10931066692585106200u64, 93329259316u64)\n    put(data, 9309584098968723946u64, 14466591364061539596u64, 52592574312u64)\n    put(data, 4647101757759615504u64, 4958077340960173341u64, 104784235489u64)\n    put(data, 1405809295505096753u64, 4076890037156765715u64, 225268777911u64)\n    put(data, 11332704079573859112u64, 14083973146609179058u64, 183221008651u64)\n    put(data, 2960072434514044308u64, 2565183738039805295u64, 11763493714u64)\n    put(data, 3887266602785432801u64, 1482420938751351224u64, 82139058889u64)\n    put(data, 14545546084687849554u64, 2151089495335413944u64, 201080362200u64)\n    put(data, 4617763804182385321u64, 3738604531753220913u64, 216116610795u64)\n    put(data, 7857823815580249095u64, 14195686514836005765u64, 235202670157u64)\n    put(data, 10939326736548364798u64, 17808833916231796970u64, 77769549707u64)\n    put(data, 12262012446566951953u64, 1302384553035657354u64, 139965418821u64)\n    put(data, 12555024338687723023u64, 1672033517974833698u64, 69070602408u64)\n    put(data, 3332969632922829472u64, 11673925532927662545u64, 168090641118u64)\n    put(data, 15535060143360327680u64, 3905334232240480709u64, 222632844771u64)\n    put(data, 15794322927987458048u64, 17411087320267472625u64, 227211708592u64)\n    put(data, 10571474314433921024u64, 16573305231063706617u64, 176943856934u64)\n    put(data, 16679514427547975680u64, 15481103236037148354u64, 38898440676u64)\n    put(data, 16925653299565166592u64, 907440704754420880u64, 228839232288u64)\n    put(data, 16717361816799281152u64, 3224970785139077759u64, 32049192459u64)\n    put(data, 0u64, 10560826509734608144u64, 11174826016u64)\n    put(data, 0u64, 4700940027512659968u64, 32572503552u64)\n    put(data, 0u64, 9733694683502084096u64, 254838469u64)\n    put(data, 0u64, 1995535635724632064u64, 197527664646u64)\n    put(data, 0u64, 10629833226245373952u64, 6108178203u64)\n    put(data, 0u64, 15729384648544878592u64, 27576244413u64)\n    put(data, 0u64, 7205759403792793600u64, 189852691650u64)\n    put(data, 0u64, 0u64, 194390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 9335385384027907407u64, 3041746u64, 0u64)\n    put(data, 13237828406194798613u64, 3041746506072255u64, 0u64)\n    put(data, 15667486867836528863u64, 7535526066623007027u64, 164893u64)\n    put(data, 17982325043592934313u64, 11302146918409311588u64, 29408501686u64)\n    put(data, 17159117626917379189u64, 2480833299122194801u64, 182612690612u64)\n    put(data, 8336208968408929657u64, 11513226205589330558u64, 180134486242u64)\n    put(data, 12767090573379150201u64, 4073957068281936105u64, 226624133243u64)\n    put(data, 14736070002412246709u64, 3729887061093812886u64, 123220849655u64)\n    put(data, 9697296975344560756u64, 13616911779739451443u64, 247202197582u64)\n    put(data, 7291706381199103298u64, 13039053282195777666u64, 78738174266u64)\n    put(data, 18098546597780825068u64, 14490756113210417890u64, 58706848494u64)\n    put(data, 132913902678533478u64, 17432486112977557585u64, 238785545462u64)\n    put(data, 1879347741692007580u64, 14308820825344039837u64, 246945016965u64)\n    put(data, 4056624629214083684u64, 4190949538817536349u64, 133775682731u64)\n    put(data, 14713227692042795499u64, 13616552502810964397u64, 171227191829u64)\n    put(data, 7366415124022528526u64, 4898145803694965031u64, 21738154790u64)\n    put(data, 825770353378039393u64, 1399036321001644308u64, 38265529016u64)\n    put(data, 10244023944395357795u64, 17170331128243738540u64, 184075841910u64)\n    put(data, 14302658294713551167u64, 10641321388205367410u64, 118930805515u64)\n    put(data, 14246653166206862817u64, 6648873641312572851u64, 11576867188u64)\n    put(data, 12404486258134291102u64, 5988456964560374823u64, 116360436162u64)\n    put(data, 8887442218637942533u64, 9972593758348346915u64, 194324634902u64)\n    put(data, 1186724038081863005u64, 16709668921872818968u64, 22540615390u64)\n    put(data, 211331772484951576u64, 6094829131503407767u64, 222905832967u64)\n    put(data, 6311919513247413649u64, 4892016478899926334u64, 7330401349u64)\n    put(data, 8131780018703965703u64, 13150857244079031538u64, 69265196744u64)\n    put(data, 2262544347226725725u64, 12983943395318785894u64, 200712909399u64)\n    put(data, 15318188749880522583u64, 15341644584614757478u64, 87703860981u64)\n    put(data, 1073117094162650652u64, 7507635124856644772u64, 245831672219u64)\n    put(data, 4447950380665871747u64, 11619655367084544354u64, 155406989715u64)\n    put(data, 5373227185066463609u64, 11553116952478783009u64, 147629902779u64)\n    put(data, 6739731406934274193u64, 17392150014233193245u64, 187626295724u64)\n    put(data, 12328812617001239444u64, 8877887560294980515u64, 172942830341u64)\n    put(data, 3246111484407310759u64, 18404180619915609503u64, 5481271248u64)\n    put(data, 3250825415176839770u64, 10079413095288181976u64, 208997692630u64)\n    put(data, 16146270540000862342u64, 14102802966539105550u64, 214546406078u64)\n    put(data, 15686773375425916830u64, 13333966026135891399u64, 190764514480u64)\n    put(data, 11920791905793880226u64, 12344968670173516152u64, 176722835746u64)\n    put(data, 1853290561644080707u64, 10577007819804726752u64, 34669222092u64)\n    put(data, 12157689141506159076u64, 15337041354031088010u64, 204573380742u64)\n    put(data, 18100318838862562546u64, 14333607285614673616u64, 134831422677u64)\n    put(data, 7171257882533475139u64, 17171597563219696538u64, 213777026407u64)\n    put(data, 14477550873015039462u64, 2849642930482147564u64, 103930874169u64)\n    put(data, 8109481182495403274u64, 14791248423979435173u64, 57154479452u64)\n    put(data, 14565395719337663965u64, 13882371364576310127u64, 92801835183u64)\n    put(data, 1860318978161305991u64, 11735995808941329540u64, 175752564859u64)\n    put(data, 16268646275151585618u64, 11376996674339273181u64, 123636209607u64)\n    put(data, 13759019338835519104u64, 9849638057168043481u64, 199616748225u64)\n    put(data, 17003783176010661888u64, 18241520229279361964u64, 193533949948u64)\n    put(data, 18357489540307877888u64, 1865852368526961444u64, 252988874793u64)\n    put(data, 905481790074912768u64, 10601487369276448158u64, 41101148059u64)\n    put(data, 3638882110636294144u64, 15999931310312762170u64, 155574707781u64)\n    put(data, 9011702854368362496u64, 5773775867713013570u64, 69867358014u64)\n    put(data, 11529215046068469760u64, 17726239863982547534u64, 62312997016u64)\n    put(data, 0u64, 9711316695888316992u64, 152960941388u64)\n    put(data, 0u64, 17872002620723724288u64, 76526451532u64)\n    put(data, 0u64, 7429694208660733952u64, 76968843203u64)\n    put(data, 0u64, 1782821038871019520u64, 195402764530u64)\n    put(data, 0u64, 3225250234313474048u64, 242096646922u64)\n    put(data, 0u64, 10009250171830927360u64, 10174841165u64)\n    put(data, 0u64, 1152921504606846976u64, 77542602539u64)\n    put(data, 0u64, 0u64, 43062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 7625299565768063067u64, 46u64, 0u64)\n    put(data, 13889021769065194705u64, 46413368317u64, 0u64)\n    put(data, 14498170692313014398u64, 9519880170333822146u64, 2u64)\n    put(data, 1541631360972245751u64, 2285186318012886800u64, 2516073738u64)\n    put(data, 9903958882920799117u64, 9706420951402272035u64, 10123880198u64)\n    put(data, 15744148547788062576u64, 2369632031840402142u64, 6526186134u64)\n    put(data, 17936061801321712000u64, 15599123897979399458u64, 150128458009u64)\n    put(data, 9986394078324430610u64, 17579576584023912658u64, 25845630200u64)\n    put(data, 13849561248103430369u64, 3480927339588501811u64, 248952990756u64)\n    put(data, 12142378807953854930u64, 3547346616671294635u64, 36188701449u64)\n    put(data, 2513847703931031444u64, 7705317123868384954u64, 9192302045u64)\n    put(data, 1752183758129038045u64, 4969425237478353909u64, 221417706078u64)\n    put(data, 15824833342220556540u64, 17043246700132217175u64, 94269393081u64)\n    put(data, 8168747198299470695u64, 17053788362783499470u64, 185923916254u64)\n    put(data, 17414799840149357478u64, 11102988228454224768u64, 222924487719u64)\n    put(data, 83147520704167789u64, 16944305387801685839u64, 39601894197u64)\n    put(data, 2383542703041471269u64, 11725142977459199276u64, 53918552635u64)\n    put(data, 15540952725549257799u64, 8175984171998533324u64, 59635621274u64)\n    put(data, 6949835416232048690u64, 1372352885142856895u64, 154443220990u64)\n    put(data, 15398868937585367540u64, 17975093466502888164u64, 254074395398u64)\n    put(data, 14710915985268256079u64, 6467823391459085653u64, 6974431769u64)\n    put(data, 9665704836873335737u64, 11319386883146885025u64, 25350621408u64)\n    put(data, 2528789298740305993u64, 9141999262922068637u64, 224613625192u64)\n    put(data, 10562914675687726264u64, 1587330393383478774u64, 104495588773u64)\n    put(data, 17671658300096837111u64, 884187548095712303u64, 165086049353u64)\n    put(data, 356471401631698552u64, 488841225726377268u64, 73047931903u64)\n    put(data, 7450677157218003204u64, 17462624199405856193u64, 255026500135u64)\n    put(data, 317174560787152643u64, 13183677579115583554u64, 39946650754u64)\n    put(data, 7251937674440720374u64, 11645015818917277779u64, 130714688593u64)\n    put(data, 1252631516699038247u64, 8760523002035971977u64, 81631277572u64)\n    put(data, 10818009768860843867u64, 10068817678491468042u64, 4474908903u64)\n    put(data, 12330114194950162396u64, 1273658177787418284u64, 231545831700u64)\n    put(data, 15826681638261168822u64, 3100019384328057661u64, 20069045148u64)\n    put(data, 14240150078499211625u64, 10363063568089458738u64, 156168052387u64)\n    put(data, 10725372116242125421u64, 13030756371481403666u64, 163561782801u64)\n    put(data, 8434925524647833627u64, 6538878900684195299u64, 17706398718u64)\n    put(data, 17133902668520348241u64, 8984884716779098868u64, 254354473335u64)\n    put(data, 15093996047981365810u64, 8728727397070363908u64, 119487071576u64)\n    put(data, 6187974166976813153u64, 6398650562917867005u64, 88473185260u64)\n    put(data, 13946144707720259865u64, 1190873176164938879u64, 236346871542u64)\n    put(data, 9138079832881862582u64, 4383628525805121795u64, 246064557364u64)\n    put(data, 6600697628576225568u64, 10189374699734119852u64, 52237636978u64)\n    put(data, 8137099536646556597u64, 5276291920541626391u64, 114552367109u64)\n    put(data, 14169855543453903706u64, 2692252373800386521u64, 5286028358u64)\n    put(data, 3706403268650100765u64, 11578684995169173920u64, 70145947293u64)\n    put(data, 14736932266877982264u64, 5799408022254132587u64, 157627681771u64)\n    put(data, 18004795125138956004u64, 15548569837712345290u64, 235314386538u64)\n    put(data, 9068489270661002501u64, 15763030464322902955u64, 106842889659u64)\n    put(data, 7758835715193269217u64, 13257749746581255500u64, 187854515593u64)\n    put(data, 16943947811135261184u64, 16152470009188707678u64, 137718704053u64)\n    put(data, 6745843108403216384u64, 13806790848493904003u64, 181875627153u64)\n    put(data, 12338229654069444608u64, 11981226523265951191u64, 145748467631u64)\n    put(data, 14358176069683511296u64, 5133628726077003713u64, 175649503591u64)\n    put(data, 7083775185760813056u64, 16183955741910833164u64, 103278294570u64)\n    put(data, 5350276357316149248u64, 13640425554331371454u64, 42877333998u64)\n    put(data, 9223372036854775808u64, 18108120906868035862u64, 238739448950u64)\n    put(data, 0u64, 6324011669895037184u64, 118981643201u64)\n    put(data, 0u64, 10444437689515769856u64, 193342825359u64)\n    put(data, 0u64, 12324712543665782784u64, 143566194101u64)\n    put(data, 0u64, 13928941951563857920u64, 181668124005u64)\n    put(data, 0u64, 3975288688270639104u64, 101755089456u64)\n    put(data, 0u64, 11141905478114607104u64, 48215500831u64)\n    put(data, 0u64, 4611686018427387904u64, 31604003906u64)\n    put(data, 0u64, 0u64, 66250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 14699116688460625612u64, 708211u64, 0u64)\n    put(data, 6684126021499623499u64, 708211796840712u64, 0u64)\n    put(data, 12614606079692508506u64, 4398362855256705725u64, 38392u64)\n    put(data, 15358270276683001489u64, 2812083125569302717u64, 248238435728u64)\n    put(data, 18077126190953408995u64, 12868509142973100603u64, 144152443331u64)\n    put(data, 7864121581925945659u64, 8726243776748165726u64, 195697603278u64)\n    put(data, 3518026639210514839u64, 358304413426858117u64, 206473050623u64)\n    put(data, 4698310163811252280u64, 3180720351566429470u64, 255019423721u64)\n    put(data, 6101155398200416338u64, 14053818240400098784u64, 233172427195u64)\n    put(data, 16049178580360033341u64, 7340140541492429288u64, 187761859013u64)\n    put(data, 3167464649127375997u64, 1323571167904965058u64, 197397909816u64)\n    put(data, 12778923935480989904u64, 14463851737583396026u64, 56071750936u64)\n    put(data, 11875553912612980379u64, 15122784818916048486u64, 24784086973u64)\n    put(data, 10628760849351697057u64, 13557974621377508955u64, 189819807807u64)\n    put(data, 3408944711673234310u64, 17525172074563876264u64, 63734979276u64)\n    put(data, 2102091496050506178u64, 15148880683074215967u64, 204950041481u64)\n    put(data, 6254611118630245760u64, 6744828147558597936u64, 137821222467u64)\n    put(data, 2647941151989776368u64, 9799290779647971692u64, 67365637866u64)\n    put(data, 8855437735410157458u64, 11170890203898678105u64, 234531220617u64)\n    put(data, 10184270603132180103u64, 7068779781287527905u64, 137605575171u64)\n    put(data, 12079083162535627164u64, 14474741922505540911u64, 3383199319u64)\n    put(data, 5952952868716156729u64, 17107062680405191514u64, 87784677331u64)\n    put(data, 11958907037815852320u64, 2712598571300237005u64, 211927375726u64)\n    put(data, 10101562137321697626u64, 3767556054903418641u64, 110147050263u64)\n    put(data, 13633527411279258327u64, 18158239681706277628u64, 23204239622u64)\n    put(data, 16555627393501768728u64, 10531652712128330681u64, 6984360145u64)\n    put(data, 6359650463500280706u64, 9548395326934120567u64, 209570922037u64)\n    put(data, 8093923611102181967u64, 15875647850297719390u64, 53517619547u64)\n    put(data, 2848827352928635726u64, 8215825295203192574u64, 91860620594u64)\n    put(data, 4686723431961561511u64, 12747310908260543144u64, 50445380781u64)\n    put(data, 6245554925867652609u64, 77706528053613642u64, 173691033109u64)\n    put(data, 17527406820792516033u64, 6024737704056756146u64, 21004212479u64)\n    put(data, 12119966834653692210u64, 6819452388570089667u64, 255326601685u64)\n    put(data, 11606502845877928061u64, 13695926775373186254u64, 213369683254u64)\n    put(data, 424311496652297090u64, 3746531715392682132u64, 54742457678u64)\n    put(data, 5977664048034127173u64, 4717376233154528116u64, 78203099891u64)\n    put(data, 6702712461535947028u64, 385190957950313369u64, 243255729478u64)\n    put(data, 11384349854055020018u64, 12388374310648616082u64, 70020881243u64)\n    put(data, 3782955013294836188u64, 1078067332084407770u64, 91671575117u64)\n    put(data, 3531805968821207061u64, 3257295319358714850u64, 77058442147u64)\n    put(data, 3867566898657193228u64, 1545453099660723457u64, 163176578333u64)\n    put(data, 12688734637425072080u64, 7495477664653506341u64, 29083779180u64)\n    put(data, 18435982764454619691u64, 7225503732673614354u64, 108406330658u64)\n    put(data, 4728836163964677692u64, 3935478326103643956u64, 34391695342u64)\n    put(data, 2120012917348838977u64, 10082240682742686210u64, 238213342707u64)\n    put(data, 9856965465824679831u64, 10838712705567897138u64, 243546559362u64)\n    put(data, 11217197671061248816u64, 2142546572501643680u64, 130587567793u64)\n    put(data, 3840562972677739189u64, 7893042119150331392u64, 177116147682u64)\n    put(data, 17104366978925258617u64, 12084811642251302615u64, 226427882670u64)\n    put(data, 16177877219841993444u64, 15317234482572954775u64, 174655118951u64)\n    put(data, 7338522384267208704u64, 2283226355108359361u64, 103830348945u64)\n    put(data, 10935240458612244480u64, 13359725152575722127u64, 145123773948u64)\n    put(data, 1732868046462124032u64, 13126551011491594557u64, 252724232151u64)\n    put(data, 557965042578882560u64, 3598021288691861269u64, 215711591756u64)\n    put(data, 10454684322475540480u64, 16462621795896662961u64, 76195049124u64)\n    put(data, 13907115649320091648u64, 14682112756964627332u64, 164892440515u64)\n    put(data, 0u64, 7174112100896070218u64, 195795918927u64)\n    put(data, 0u64, 5023109019590616064u64, 79388909396u64)\n    put(data, 0u64, 10765223023086141440u64, 84272303285u64)\n    put(data, 0u64, 8228137177297453056u64, 181583583909u64)\n    put(data, 0u64, 2891199497780592640u64, 165446048210u64)\n    put(data, 0u64, 15294857653247803392u64, 210156732238u64)\n    put(data, 0u64, 14303432416528695296u64, 78829135894u64)\n    put(data, 0u64, 0u64, 22775390625u64)\n    put(data, 0u64, 0u64, 161000000000u64)\n    put(data, 14876458284855834550u64, 10u64, 0u64)\n    put(data, 10450704926982265198u64, 10806454419u64, 0u64)\n    put(data, 5385653213018257806u64, 10806454419566533849u64, 0u64)\n    put(data, 12735569669880147489u64, 17118225092618494573u64, 585819067u64)\n    put(data, 3619762560577729456u64, 13385738875341807559u64, 187927980841u64)\n    put(data, 11123448126624084269u64, 8272682717439277193u64, 41725642358u64)\n    put(data, 4072715118852885633u64, 13402436483369350083u64, 118448463028u64)\n    put(data, 18239087231420827283u64, 10946328903241612536u64, 180726547537u64)\n    put(data, 423431461216085297u64, 16265808923426731252u64, 81593401678u64)\n    put(data, 9360976716520160042u64, 11080374459871185177u64, 78881771268u64)\n    put(data, 11655927117263208920u64, 1240761893433831916u64, 4600668303u64)\n    put(data, 2646622721938364948u64, 367264070493390483u64, 143067261837u64)\n    put(data, 4496462484548171852u64, 2863675693461092905u64, 141019909425u64)\n    put(data, 13140258519803350063u64, 7511929581752138999u64, 49155240170u64)\n    put(data, 13089764333320627770u64, 11154557789993845753u64, 234407222518u64)\n    put(data, 6926286827289840501u64, 8325416539745948522u64, 246604689789u64)\n    put(data, 4985580225290866218u64, 17745129874679852617u64, 125451321734u64)\n    put(data, 10478790837359789693u64, 1074820986392253357u64, 134961965418u64)\n    put(data, 10041191967455692214u64, 7820952682162838597u64, 106058266162u64)\n    put(data, 5035461258013813797u64, 8215518006273528603u64, 50423974694u64)\n    put(data, 14208759661559249750u64, 9680426791089900133u64, 38445364123u64)\n    put(data, 12347272163241758840u64, 16128495723604797412u64, 155524776987u64)\n    put(data, 3372534174410277614u64, 2264789053583348885u64, 27874327505u64)\n    put(data, 17226704187274712984u64, 11175458488686298083u64, 209122774460u64)\n    put(data, 4250480179449852121u64, 11026777810412287617u64, 188605822818u64)\n    put(data, 4303723537755414374u64, 16199890034895598640u64, 98597762822u64)\n    put(data, 8680503847344854165u64, 9094320719494763752u64, 6878197798u64)\n    put(data, 8817608623911079652u64, 1250835564687222832u64, 38493004114u64)\n    put(data, 5692030448698539450u64, 15362466642459337025u64, 82067807931u64)\n    put(data, 16908836314686769809u64, 7831109835595423828u64, 187832800985u64)\n    put(data, 6073762347067727240u64, 15426237284335022429u64, 217424525314u64)\n    put(data, 10931066692585106200u64, 15636308361455434548u64, 2836257998u64)\n    put(data, 14466591364061539596u64, 13967173875944980328u64, 206847645974u64)\n    put(data, 4958077340960173341u64, 18245979923595824097u64, 22757162012u64)\n    put(data, 4076890037156765715u64, 11335054479675278263u64, 28989116553u64)\n    put(data, 14083973146609179058u64, 11165339882630461707u64, 137614474534u64)\n    put(data, 2565183738039805295u64, 15944437408299395922u64, 38605274287u64)\n    put(data, 1482420938751351224u64, 15806416348777321161u64, 175864349683u64)\n    put(data, 2151089495335413944u64, 4201030477408556248u64, 243856867547u64)\n    put(data, 3738604531753220913u64, 9485474942554588907u64, 219227738318u64)\n    put(data, 14195686514836005765u64, 18238757647663230541u64, 206514208626u64)\n    put(data, 17808833916231796970u64, 4642199687824746379u64, 114988725033u64)\n    put(data, 1302384553035657354u64, 6134575894869364037u64, 41251654149u64)\n    put(data, 1672033517974833698u64, 11524208547121316008u64, 5332556025u64)\n    put(data, 11673925532927662545u64, 2734683241527878366u64, 249624728597u64)\n    put(data, 3905334232240480709u64, 10629223456178675171u64, 21148247475u64)\n    put(data, 17411087320267472625u64, 2788042336985254064u64, 179576211358u64)\n    put(data, 16573305231063706617u64, 17285498758066142502u64, 158151140077u64)\n    put(data, 15481103236037148354u64, 5525538192421886436u64, 237937048765u64)\n    put(data, 907440704754420880u64, 11414325503043801888u64, 189299540025u64)\n    put(data, 3224970785139077759u64, 7246608114685173259u64, 57618771825u64)\n    put(data, 10560826509734608144u64, 1007884269852184608u64, 113392839413u64)\n    put(data, 4700940027512659968u64, 13823717876510029312u64, 245054637515u64)\n    put(data, 9733694683502084096u64, 12487410768239429317u64, 203749385247u64)\n    put(data, 1995535635724632064u64, 3361062421598631942u64, 31676943894u64)\n    put(data, 10629833226245373952u64, 17853337379088328475u64, 22182203558u64)\n    put(data, 15729384648544878592u64, 11551561037491869885u64, 166967831358u64)\n    put(data, 7205759403792793600u64, 11480877996635204802u64, 62626211378u64)\n    put(data, 0u64, 5527488381934471912u64, 50622379643u64)\n    put(data, 0u64, 11143438404407726080u64, 123299645745u64)\n    put(data, 0u64, 6472279730688098304u64, 49604087006u64)\n    put(data, 0u64, 4561816853579563008u64, 222350862987u64)\n    put(data, 0u64, 2888714464062865408u64, 139247296587u64)\n    put(data, 0u64, 16258276129784201216u64, 75156597524u64)\n    put(data, 0u64, 720575940379279360u64, 20881362915u64)\n    put(data, 0u64, 0u64, 227039062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 7535526066623007027u64, 164893u64, 0u64)\n    put(data, 11302146918409311588u64, 164893408501686u64, 0u64)\n    put(data, 2480833299122194801u64, 16409970870640346804u64, 8938u64)\n    put(data, 11513226205589330558u64, 7721907286269370594u64, 234889586303u64)\n    put(data, 4073957068281936105u64, 14300743897882155131u64, 127418605432u64)\n    put(data, 3729887061093812886u64, 2068482633821123575u64, 120775244880u64)\n    put(data, 13616911779739451443u64, 4922882895416406094u64, 80112132668u64)\n    put(data, 13039053282195777666u64, 9317632875623428410u64, 60266870016u64)\n    put(data, 14490756113210417890u64, 5693844901999766254u64, 505109890u64)\n    put(data, 17432486112977557585u64, 11569484900262102262u64, 130308663950u64)\n    put(data, 14308820825344039837u64, 3138170119352085637u64, 142627183033u64)\n    put(data, 4190949538817536349u64, 950584692575235243u64, 185170120543u64)\n    put(data, 13616552502810964397u64, 8136430299747162645u64, 95051531299u64)\n    put(data, 4898145803694965031u64, 6698711700804594470u64, 35441076770u64)\n    put(data, 1399036321001644308u64, 17401191571004302008u64, 34363137888u64)\n    put(data, 17170331128243738540u64, 4721732028538188150u64, 96943320485u64)\n    put(data, 10641321388205367410u64, 2984214103553086219u64, 165255965606u64)\n    put(data, 6648873641312572851u64, 13128675202005662068u64, 166161774570u64)\n    put(data, 5988456964560374823u64, 14638512997670672834u64, 234711706908u64)\n    put(data, 9972593758348346915u64, 12942085665769692438u64, 28793555379u64)\n    put(data, 16709668921872818968u64, 14131134357119205086u64, 179701591869u64)\n    put(data, 6094829131503407767u64, 8921946894736102919u64, 61766050328u64)\n    put(data, 4892016478899926334u64, 5601522560505809989u64, 24483659710u64)\n    put(data, 13150857244079031538u64, 8602606493507716808u64, 190303659146u64)\n    put(data, 12983943395318785894u64, 8576789731078566487u64, 138466348232u64)\n    put(data, 15341644584614757478u64, 17881118138842658549u64, 200464948702u64)\n    put(data, 7507635124856644772u64, 11624372674432704923u64, 222969337356u64)\n    put(data, 11619655367084544354u64, 6826284072848095635u64, 12630158505u64)\n    put(data, 11553116952478783009u64, 1646466632033733563u64, 169370053601u64)\n    put(data, 17392150014233193245u64, 17871081657060299180u64, 225089255134u64)\n    put(data, 8877887560294980515u64, 15910893124677544709u64, 222968793277u64)\n    put(data, 18404180619915609503u64, 11031217459450580944u64, 189862531244u64)\n    put(data, 10079413095288181976u64, 13554987390037243094u64, 172598003496u64)\n    put(data, 14102802966539105550u64, 15026714590903687870u64, 40734817338u64)\n    put(data, 13333966026135891399u64, 4406379654994689200u64, 58814599830u64)\n    put(data, 12344968670173516152u64, 13596329092861950242u64, 150238870319u64)\n    put(data, 10577007819804726752u64, 284812388227373260u64, 47737058477u64)\n    put(data, 15337041354031088010u64, 9285079159392309382u64, 173015439710u64)\n    put(data, 14333607285614673616u64, 15046108141952711893u64, 94503345149u64)\n    put(data, 17171597563219696538u64, 13795366909944958311u64, 253815651156u64)\n    put(data, 2849642930482147564u64, 12909920641180059961u64, 84747848338u64)\n    put(data, 14791248423979435173u64, 5333762939889788252u64, 146699848200u64)\n    put(data, 13882371364576310127u64, 6411331390005944495u64, 8289143868u64)\n    put(data, 11735995808941329540u64, 1447104583224217723u64, 60347558971u64)\n    put(data, 11376996674339273181u64, 11940049226167932871u64, 59078447696u64)\n    put(data, 9849638057168043481u64, 9772290783590472385u64, 80647271365u64)\n    put(data, 18241520229279361964u64, 16351989577831528444u64, 197529756944u64)\n    put(data, 1865852368526961444u64, 4376738725895725097u64, 16886443131u64)\n    put(data, 10601487369276448158u64, 13851276297739812763u64, 123237263481u64)\n    put(data, 15999931310312762170u64, 12641996203470333509u64, 121750879192u64)\n    put(data, 5773775867713013570u64, 7707081716407945022u64, 216685323987u64)\n    put(data, 17726239863982547534u64, 417638323657040024u64, 211417801737u64)\n    put(data, 9711316695888316992u64, 16438047707692449100u64, 9022640218u64)\n    put(data, 17872002620723724288u64, 14850108107043306316u64, 90891108351u64)\n    put(data, 7429694208660733952u64, 10423290807904720835u64, 255805025973u64)\n    put(data, 1782821038871019520u64, 16951162310302339314u64, 181565047726u64)\n    put(data, 3225250234313474048u64, 2752437506572397322u64, 174918924350u64)\n    put(data, 10009250171830927360u64, 3925815842962784589u64, 62149209936u64)\n    put(data, 1152921504606846976u64, 5274166674003605291u64, 80212818903u64)\n    put(data, 0u64, 5538963350863452832u64, 215285913148u64)\n    put(data, 0u64, 16900671634439028736u64, 60300267804u64)\n    put(data, 0u64, 2326997710751662080u64, 28916187245u64)\n    put(data, 0u64, 12327726161625874432u64, 109126146798u64)\n    put(data, 0u64, 5756455743825903616u64, 238668287374u64)\n    put(data, 0u64, 3018537650245074944u64, 142312058091u64)\n    put(data, 0u64, 16717361816799281152u64, 235163635253u64)\n    put(data, 0u64, 0u64, 53906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 2285186318012886800u64, 2516073738u64, 0u64)\n    put(data, 9706420951402272035u64, 2516073738123880198u64, 0u64)\n    put(data, 2369632031840402142u64, 11997425759292732054u64, 136396630u64)\n    put(data, 15599123897979399458u64, 11491152661270395161u64, 86650381753u64)\n    put(data, 17579576584023912658u64, 18181063258234881272u64, 185622936633u64)\n    put(data, 3480927339588501811u64, 2466921813123869732u64, 57985597414u64)\n    put(data, 3547346616671294635u64, 8430880678232179465u64, 230133732099u64)\n    put(data, 7705317123868384954u64, 6738034873677997533u64, 3457038957u64)\n    put(data, 4969425237478353909u64, 7678250951042929246u64, 109365269602u64)\n    put(data, 17043246700132217175u64, 1853560606315563193u64, 98416238818u64)\n    put(data, 17053788362783499470u64, 14942676593409905118u64, 226100481721u64)\n    put(data, 11102988228454224768u64, 4909892170837638183u64, 185810044121u64)\n    put(data, 16944305387801685839u64, 16871149368312132405u64, 217266165787u64)\n    put(data, 11725142977459199276u64, 16096130589333770811u64, 27914586839u64)\n    put(data, 8175984171998533324u64, 12512479187631824282u64, 215872572987u64)\n    put(data, 1372352885142856895u64, 16980304980540557310u64, 59678302855u64)\n    put(data, 17975093466502888164u64, 8640919162749295366u64, 135920504177u64)\n    put(data, 6467823391459085653u64, 7862382415464063513u64, 113468425166u64)\n    put(data, 11319386883146885025u64, 14534157903009925344u64, 206426220604u64)\n    put(data, 9141999262922068637u64, 12627464554215107944u64, 60787898278u64)\n    put(data, 1587330393383478774u64, 2456849734836299173u64, 166684536225u64)\n    put(data, 884187548095712303u64, 18428252197697827913u64, 161133186090u64)\n    put(data, 488841225726377268u64, 7244734215936736255u64, 42998997553u64)\n    put(data, 17462624199405856193u64, 14756175050504770087u64, 49392737828u64)\n    put(data, 13183677579115583554u64, 6764116534566945922u64, 36799933852u64)\n    put(data, 11645015818917277779u64, 1588822142405565521u64, 156366683492u64)\n    put(data, 8760523002035971977u64, 17053265624843842052u64, 100086130220u64)\n    put(data, 10068817678491468042u64, 16996891591759999207u64, 44924459381u64)\n    put(data, 1273658177787418284u64, 8565556232370585876u64, 117921403339u64)\n    put(data, 3100019384328057661u64, 14464960359145886620u64, 203464339733u64)\n    put(data, 10363063568089458738u64, 5813189542048784035u64, 21784147072u64)\n    put(data, 13030756371481403666u64, 9739241026882027025u64, 128315133636u64)\n    put(data, 6538878900684195299u64, 18175068535675302910u64, 196527965313u64)\n    put(data, 8984884716779098868u64, 10562697212061761911u64, 129985272439u64)\n    put(data, 8728727397070363908u64, 4264834835660801368u64, 119572604963u64)\n    put(data, 6398650562917867005u64, 13019066443690126316u64, 35231197159u64)\n    put(data, 1190873176164938879u64, 1828040177823321846u64, 231705765006u64)\n    put(data, 4383628525805121795u64, 11240369830376975668u64, 142099098256u64)\n    put(data, 10189374699734119852u64, 8886938465302549874u64, 144609341669u64)\n    put(data, 5276291920541626391u64, 9985240313589688325u64, 229481761899u64)\n    put(data, 2692252373800386521u64, 722909126956573766u64, 107541300962u64)\n    put(data, 11578684995169173920u64, 5493363474638452381u64, 226039188982u64)\n    put(data, 5799408022254132587u64, 12410535279213120491u64, 246297795830u64)\n    put(data, 15548569837712345290u64, 10543108918366869098u64, 246672776465u64)\n    put(data, 15763030464322902955u64, 12953909016524823995u64, 17571543079u64)\n    put(data, 13257749746581255500u64, 16505942145872588169u64, 39702232814u64)\n    put(data, 16152470009188707678u64, 12428594380392015797u64, 238894788916u64)\n    put(data, 13806790848493904003u64, 7528259605829768337u64, 52673755451u64)\n    put(data, 11981226523265951191u64, 18147447600042811311u64, 59408107770u64)\n    put(data, 5133628726077003713u64, 12021069431116183911u64, 250983775105u64)\n    put(data, 16183955741910833164u64, 11819985069665662506u64, 129651663479u64)\n    put(data, 13640425554331371454u64, 10401877114068152814u64, 119640762674u64)\n    put(data, 18108120906868035862u64, 4611631138117837942u64, 50563886888u64)\n    put(data, 6324011669895037184u64, 17200813398607252417u64, 40249997024u64)\n    put(data, 10444437689515769856u64, 14100466137553658767u64, 224932457962u64)\n    put(data, 12324712543665782784u64, 17887776768825509301u64, 234764387800u64)\n    put(data, 13928941951563857920u64, 12632656857970087269u64, 216969698321u64)\n    put(data, 3975288688270639104u64, 8923681664054686256u64, 17684817700u64)\n    put(data, 11141905478114607104u64, 6213926103737837599u64, 36483753752u64)\n    put(data, 4611686018427387904u64, 1233118281776157762u64, 24336857609u64)\n    put(data, 0u64, 30716279628678784u64, 9066847476u64)\n    put(data, 0u64, 15775734650898546688u64, 244001665132u64)\n    put(data, 0u64, 976806005729918976u64, 108855204289u64)\n    put(data, 0u64, 12460098853279891456u64, 193052952759u64)\n    put(data, 0u64, 5635665595421687808u64, 183675463312u64)\n    put(data, 0u64, 1805943450575568896u64, 144305510044u64)\n    put(data, 0u64, 11529215046068469760u64, 156097900390u64)\n    put(data, 0u64, 0u64, 102625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 4398362855256705725u64, 38392u64, 0u64)\n    put(data, 2812083125569302717u64, 38392238435728u64, 0u64)\n    put(data, 12868509142973100603u64, 4564018338575530435u64, 2081u64)\n    put(data, 8726243776748165726u64, 16553437246451512014u64, 33247415929u64)\n    put(data, 358304413426858117u64, 4339777136957372927u64, 121897363631u64)\n    put(data, 3180720351566429470u64, 18439463366554654697u64, 175235259789u64)\n    put(data, 14053818240400098784u64, 1370067356680643003u64, 141999605312u64)\n    put(data, 7340140541492429288u64, 4210124040914115013u64, 64074271500u64)\n    put(data, 1323571167904965058u64, 10692225626142609720u64, 12228231281u64)\n    put(data, 14463851737583396026u64, 11592856673895384344u64, 113579626712u64)\n    put(data, 15122784818916048486u64, 10284479231227406269u64, 216628450019u64)\n    put(data, 13557974621377508955u64, 4961071383534266431u64, 227557522736u64)\n    put(data, 17525172074563876264u64, 10960611551445686988u64, 48268940218u64)\n    put(data, 15148880683074215967u64, 14616396723115619209u64, 186594175942u64)\n    put(data, 6744828147558597936u64, 1025604265437492803u64, 198792356454u64)\n    put(data, 9799290779647971692u64, 11711588454892179178u64, 102055598118u64)\n    put(data, 11170890203898678105u64, 5580373263251565705u64, 38634886482u64)\n    put(data, 7068779781287527905u64, 14109334653033148931u64, 82302512640u64)\n    put(data, 14474741922505540911u64, 2899414033769399895u64, 764868564u64)\n    put(data, 17107062680405191514u64, 13233457234892808147u64, 212157177549u64)\n    put(data, 2712598571300237005u64, 3287946691509034862u64, 205717387154u64)\n    put(data, 3767556054903418641u64, 5488480288717445911u64, 146178239947u64)\n    put(data, 18158239681706277628u64, 11687233053874362630u64, 203297531112u64)\n    put(data, 10531652712128330681u64, 6783772100089274577u64, 232633566173u64)\n    put(data, 9548395326934120567u64, 7898291058728402485u64, 221367749022u64)\n    put(data, 15875647850297719390u64, 4423684977486598491u64, 158428167216u64)\n    put(data, 8215825295203192574u64, 2750833684599526706u64, 48239808443u64)\n    put(data, 12747310908260543144u64, 15669689830489025709u64, 187149122992u64)\n    put(data, 77706528053613642u64, 15117307274214954517u64, 176849455587u64)\n    put(data, 6024737704056756146u64, 8148639818575698175u64, 227819510869u64)\n    put(data, 6819452388570089667u64, 13006484426078994901u64, 85441738649u64)\n    put(data, 13695926775373186254u64, 10287496057845513526u64, 153705082933u64)\n    put(data, 3746531715392682132u64, 14159876032966532430u64, 53557686278u64)\n    put(data, 4717376233154528116u64, 15742212196465548019u64, 6767608417u64)\n    put(data, 385190957950313369u64, 2892220461917134150u64, 97853387033u64)\n    put(data, 12388374310648616082u64, 7487151560715393883u64, 25156787585u64)\n    put(data, 1078067332084407770u64, 7245756744165177933u64, 129405879299u64)\n    put(data, 3257295319358714850u64, 3067122860671533987u64, 3392793260u64)\n    put(data, 1545453099660723457u64, 8135043905834122525u64, 172166269063u64)\n    put(data, 7495477664653506341u64, 14730019368921022572u64, 135441001613u64)\n    put(data, 7225503732673614354u64, 495969939682055458u64, 141798515950u64)\n    put(data, 3935478326103643956u64, 5617761407265775598u64, 238026886584u64)\n    put(data, 10082240682742686210u64, 2087044847072781811u64, 184304539456u64)\n    put(data, 10838712705567897138u64, 15929674232061203330u64, 64113138927u64)\n    put(data, 2142546572501643680u64, 8658086469608285873u64, 239863549370u64)\n    put(data, 7893042119150331392u64, 18369871790780313570u64, 186469355807u64)\n    put(data, 12084811642251302615u64, 3545648451947416750u64, 31995832745u64)\n    put(data, 15317234482572954775u64, 13347376792767929959u64, 169192209987u64)\n    put(data, 2283226355108359361u64, 14482164459838203025u64, 67723562745u64)\n    put(data, 13359725152575722127u64, 8899577765623565820u64, 249785079708u64)\n    put(data, 13126551011491594557u64, 7095320096604405719u64, 156482447077u64)\n    put(data, 3598021288691861269u64, 2968593824439315788u64, 229384638073u64)\n    put(data, 16462621795896662961u64, 12621408323612585636u64, 121160927793u64)\n    put(data, 14682112756964627332u64, 3954422936414648259u64, 49684207916u64)\n    put(data, 7174112100896070218u64, 17143730087577690191u64, 44214369696u64)\n    put(data, 5023109019590616064u64, 5033045529399041876u64, 160929363470u64)\n    put(data, 10765223023086141440u64, 15857648521994521781u64, 14272841944u64)\n    put(data, 8228137177297453056u64, 16655573486499109541u64, 216859644848u64)\n    put(data, 2891199497780592640u64, 16652154439190075858u64, 176902900447u64)\n    put(data, 15294857653247803392u64, 18016950600164130638u64, 223902715100u64)\n    put(data, 14303432416528695296u64, 2086292996072613910u64, 220976700849u64)\n    put(data, 0u64, 17324462585194799521u64, 177113098169u64)\n    put(data, 0u64, 11079151463184927232u64, 185939160998u64)\n    put(data, 0u64, 5239846817488961536u64, 166600602004u64)\n    put(data, 0u64, 2778806963520143360u64, 148284052665u64)\n    put(data, 0u64, 6240890740138835968u64, 185150639427u64)\n    put(data, 0u64, 17250651344549707776u64, 67338319364u64)\n    put(data, 0u64, 4197354852709302272u64, 4935159683u64)\n    put(data, 0u64, 9223372036854775808u64, 131227539062u64)\n    put(data, 0u64, 0u64, 118500000000u64)\n    put(data, 17118225092618494573u64, 585819067u64, 0u64)\n    put(data, 13385738875341807559u64, 585819067927980841u64, 0u64)\n    put(data, 8272682717439277193u64, 5654803392547571318u64, 31757315u64)\n    put(data, 13402436483369350083u64, 2931628102185393332u64, 3306547506u64)\n    put(data, 10946328903241612536u64, 15964697617980212305u64, 50158923877u64)\n    put(data, 16265808923426731252u64, 450380868305846606u64, 101865447992u64)\n    put(data, 11080374459871185177u64, 14631133530814566148u64, 56024415195u64)\n    put(data, 1240761893433831916u64, 31969822783742095u64, 219793155338u64)\n    put(data, 367264070493390483u64, 10437269029385743245u64, 10001733087u64)\n    put(data, 2863675693461092905u64, 15196146496377392433u64, 223565805487u64)\n    put(data, 7511929581752138999u64, 4409099735137480938u64, 175823784752u64)\n    put(data, 11154557789993845753u64, 10644987914903248118u64, 48239017775u64)\n    put(data, 8325416539745948522u64, 3154431617534062973u64, 47577065951u64)\n    put(data, 17745129874679852617u64, 11702056331247960454u64, 223171002080u64)\n    put(data, 1074820986392253357u64, 15575315065965259114u64, 224634369744u64)\n    put(data, 7820952682162838597u64, 10759747609480050226u64, 208844339521u64)\n    put(data, 8215518006273528603u64, 12538236653960743718u64, 65583287086u64)\n    put(data, 9680426791089900133u64, 17857942663978005403u64, 46679699170u64)\n    put(data, 16128495723604797412u64, 11443004154750813211u64, 226968081011u64)\n    put(data, 2264789053583348885u64, 4004313188770806737u64, 115620326498u64)\n    put(data, 11175458488686298083u64, 17134872954824183228u64, 98217074252u64)\n    put(data, 11026777810412287617u64, 2659553912986171234u64, 76928883324u64)\n    put(data, 16199890034895598640u64, 9501854300969137926u64, 124144174706u64)\n    put(data, 9094320719494763752u64, 14528169966301018150u64, 114515096553u64)\n    put(data, 1250835564687222832u64, 18172091996515901778u64, 233787573671u64)\n    put(data, 15362466642459337025u64, 1133541705604751035u64, 167985111081u64)\n    put(data, 7831109835595423828u64, 18280349987988641497u64, 41061449418u64)\n    put(data, 15426237284335022429u64, 9936015874712336386u64, 202990979758u64)\n    put(data, 15636308361455434548u64, 15876720399740689614u64, 174538632499u64)\n    put(data, 13967173875944980328u64, 8618117825152456982u64, 51860678737u64)\n    put(data, 18245979923595824097u64, 8085525680745921564u64, 81467189103u64)\n    put(data, 11335054479675278263u64, 8072355444669730953u64, 111438317225u64)\n    put(data, 11165339882630461707u64, 9395030504766848294u64, 169437603265u64)\n    put(data, 15944437408299395922u64, 3537903114058185903u64, 193509305624u64)\n    put(data, 15806416348777321161u64, 2126094743961928691u64, 24191790112u64)\n    put(data, 4201030477408556248u64, 289185362555601115u64, 32115255827u64)\n    put(data, 9485474942554588907u64, 16909937501450129614u64, 19015676769u64)\n    put(data, 18238757647663230541u64, 14449642060360499058u64, 97916689548u64)\n    put(data, 4642199687824746379u64, 12433818908498244393u64, 140783316665u64)\n    put(data, 6134575894869364037u64, 11884444034578008581u64, 185674038673u64)\n    put(data, 11524208547121316008u64, 988625838444140793u64, 145644257002u64)\n    put(data, 2734683241527878366u64, 1675370907158909973u64, 234053593514u64)\n    put(data, 10629223456178675171u64, 15920186275316934067u64, 170090822038u64)\n    put(data, 2788042336985254064u64, 5600921198503757726u64, 150863035027u64)\n    put(data, 17285498758066142502u64, 10457357161776341741u64, 147303626546u64)\n    put(data, 5525538192421886436u64, 12225356765775740093u64, 50566894467u64)\n    put(data, 11414325503043801888u64, 4486633318598164537u64, 131662737918u64)\n    put(data, 7246608114685173259u64, 10302486602879381361u64, 254243220879u64)\n    put(data, 1007884269852184608u64, 15536428611301239541u64, 143558498917u64)\n    put(data, 13823717876510029312u64, 12026126645955462603u64, 101842231482u64)\n    put(data, 12487410768239429317u64, 14877968141142123551u64, 186651937631u64)\n    put(data, 3361062421598631942u64, 734560801645383190u64, 95806536269u64)\n    put(data, 17853337379088328475u64, 15648943144911081638u64, 77039820620u64)\n    put(data, 11551561037491869885u64, 13664182862003235646u64, 76848330907u64)\n    put(data, 11480877996635204802u64, 3895127525902132786u64, 155740736837u64)\n    put(data, 5527488381934471912u64, 5249187334214137467u64, 69211155286u64)\n    put(data, 11143438404407726080u64, 10642260063359027505u64, 86284559015u64)\n    put(data, 6472279730688098304u64, 783598951897779422u64, 167576918074u64)\n    put(data, 4561816853579563008u64, 5538576558607624843u64, 58042478984u64)\n    put(data, 2888714464062865408u64, 15974581187564609611u64, 136300246836u64)\n    put(data, 16258276129784201216u64, 7474269406918257428u64, 52865983781u64)\n    put(data, 720575940379279360u64, 8045286838779138019u64, 37405180956u64)\n    put(data, 0u64, 8184246376556341732u64, 28436135873u64)\n    put(data, 0u64, 1493267152679331840u64, 193443668885u64)\n    put(data, 0u64, 10179074811222818816u64, 149080950174u64)\n    put(data, 0u64, 3892499202005008384u64, 158551808751u64)\n    put(data, 0u64, 10341173215925108736u64, 239211012804u64)\n    put(data, 0u64, 6230307872002015232u64, 196560596123u64)\n    put(data, 0u64, 9295429630892703744u64, 155337745666u64)\n    put(data, 0u64, 0u64, 2503906250u64)\n    put(data, 0u64, 0u64, 202000000000u64)\n    put(data, 16409970870640346804u64, 8938u64, 0u64)\n    put(data, 7721907286269370594u64, 8938889586303u64, 0u64)\n    put(data, 14300743897882155131u64, 10665454627995623288u64, 484u64)\n    put(data, 2068482633821123575u64, 16803537892767562832u64, 228578175453u64)\n    put(data, 4922882895416406094u64, 8099123106849104444u64, 221910921614u64)\n    put(data, 9317632875623428410u64, 7077413686679401728u64, 142439054343u64)\n    put(data, 5693844901999766254u64, 13536636358372449666u64, 7383667364u64)\n    put(data, 11569484900262102262u64, 7280632235418610318u64, 164733822527u64)\n    put(data, 3138170119352085637u64, 6187823673116858809u64, 63394683864u64)\n    put(data, 950584692575235243u64, 8624343686231740255u64, 216335442593u64)\n    put(data, 8136430299747162645u64, 806211610822132771u64, 161467526608u64)\n    put(data, 6698711700804594470u64, 18388078233202190882u64, 208043704818u64)\n    put(data, 17401191571004302008u64, 7628864426595573600u64, 242996819718u64)\n    put(data, 4721732028538188150u64, 4530799784343874981u64, 6413561569u64)\n    put(data, 2984214103553086219u64, 8561580552078486438u64, 225245615148u64)\n    put(data, 13128675202005662068u64, 13349114951221999594u64, 44464124211u64)\n    put(data, 14638512997670672834u64, 10029144738508991772u64, 51723656971u64)\n    put(data, 12942085665769692438u64, 12601907197916268979u64, 11543681025u64)\n    put(data, 14131134357119205086u64, 1329580921391066941u64, 1683150758u64)\n    put(data, 8921946894736102919u64, 3198179786356761112u64, 166072076726u64)\n    put(data, 5601522560505809989u64, 11406753413634654142u64, 182173373673u64)\n    put(data, 8602606493507716808u64, 11131812960525182090u64, 233618361341u64)\n    put(data, 8576789731078566487u64, 14299636753645227208u64, 253603456789u64)\n    put(data, 17881118138842658549u64, 12964114684643663326u64, 21775184861u64)\n    put(data, 11624372674432704923u64, 5019257593846306316u64, 221702786065u64)\n    put(data, 6826284072848095635u64, 6929086798159998121u64, 17272094499u64)\n    put(data, 1646466632033733563u64, 18359765766933703649u64, 35375626547u64)\n    put(data, 17871081657060299180u64, 9993076234752063198u64, 51995284896u64)\n    put(data, 15910893124677544709u64, 3257189215046584509u64, 160541725748u64)\n    put(data, 11031217459450580944u64, 2905234736672690348u64, 52176572581u64)\n    put(data, 13554987390037243094u64, 12064985302079670056u64, 165157493090u64)\n    put(data, 15026714590903687870u64, 14315096064942799930u64, 98654044163u64)\n    put(data, 4406379654994689200u64, 11943971043551974038u64, 3776022912u64)\n    put(data, 13596329092861950242u64, 12472773152119929647u64, 128647483967u64)\n    put(data, 284812388227373260u64, 7791259796982183085u64, 63676150387u64)\n    put(data, 9285079159392309382u64, 16866829442051086686u64, 115422365039u64)\n    put(data, 15046108141952711893u64, 3702498393844653053u64, 111914352656u64)\n    put(data, 13795366909944958311u64, 2057239613841701716u64, 16200712840u64)\n    put(data, 12909920641180059961u64, 17201969976738286226u64, 136111523182u64)\n    put(data, 5333762939889788252u64, 18271566505443461640u64, 110932520660u64)\n    put(data, 6411331390005944495u64, 18368509115417119804u64, 212990503604u64)\n    put(data, 1447104583224217723u64, 7613923684154518587u64, 180995758874u64)\n    put(data, 11940049226167932871u64, 17984805084714865232u64, 26412751629u64)\n    put(data, 9772290783590472385u64, 4220802739051410373u64, 13974958237u64)\n    put(data, 16351989577831528444u64, 17812459042810815760u64, 157228810174u64)\n    put(data, 4376738725895725097u64, 10629526089664605307u64, 190965615339u64)\n    put(data, 13851276297739812763u64, 17437443267816548473u64, 235576227763u64)\n    put(data, 12641996203470333509u64, 12506371893701049304u64, 179945285693u64)\n    put(data, 7707081716407945022u64, 15737221540003030739u64, 61677971778u64)\n    put(data, 417638323657040024u64, 2358380859011605513u64, 66853116489u64)\n    put(data, 16438047707692449100u64, 10042972713837039706u64, 73127848082u64)\n    put(data, 14850108107043306316u64, 13424397272769642495u64, 146544430641u64)\n    put(data, 10423290807904720835u64, 6867102315755663029u64, 49727738034u64)\n    put(data, 16951162310302339314u64, 8690748404825506734u64, 178372266362u64)\n    put(data, 2752437506572397322u64, 956229930815387710u64, 122471126415u64)\n    put(data, 3925815842962784589u64, 7734449506297687888u64, 143051837328u64)\n    put(data, 5274166674003605291u64, 16332184961683848151u64, 144419285347u64)\n    put(data, 5538963350863452832u64, 15580777817612768828u64, 99885369520u64)\n    put(data, 16900671634439028736u64, 17404245271944696092u64, 176844635657u64)\n    put(data, 2326997710751662080u64, 13201420160494469229u64, 9943486026u64)\n    put(data, 12327726161625874432u64, 16511717657124068078u64, 74715650420u64)\n    put(data, 5756455743825903616u64, 14131292492116594062u64, 116895102007u64)\n    put(data, 3018537650245074944u64, 18429136031865875691u64, 55766058900u64)\n    put(data, 16717361816799281152u64, 2563978348305862197u64, 148999045466u64)\n    put(data, 0u64, 14239974392147482896u64, 90138993544u64)\n    put(data, 0u64, 11164201396098998272u64, 136771950558u64)\n    put(data, 0u64, 7116971104932986880u64, 222605212570u64)\n    put(data, 0u64, 12437629862867369984u64, 154385811776u64)\n    put(data, 0u64, 16501893821638901760u64, 64674245265u64)\n    put(data, 0u64, 10649324268870959104u64, 145894569456u64)\n    put(data, 0u64, 7205759403792793600u64, 240577301025u64)\n    put(data, 0u64, 0u64, 33390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 11997425759292732054u64, 136396630u64, 0u64)\n    put(data, 11491152661270395161u64, 136396630650381753u64, 0u64)\n    put(data, 18181063258234881272u64, 3016823727048309817u64, 7394076u64)\n    put(data, 2466921813123869732u64, 17405973192644624358u64, 28163542341u64)\n    put(data, 8430880678232179465u64, 8937219978302591747u64, 69943579697u64)\n    put(data, 6738034873677997533u64, 15178463196824222317u64, 49484487665u64)\n    put(data, 7678250951042929246u64, 11979404627460330594u64, 241822826138u64)\n    put(data, 1853560606315563193u64, 2006448052689740002u64, 154649404826u64)\n    put(data, 14942676593409905118u64, 16330465320863239865u64, 154108769766u64)\n    put(data, 4909892170837638183u64, 17136208883957646553u64, 230885276298u64)\n    put(data, 16871149368312132405u64, 140455118208931867u64, 138928955745u64)\n    put(data, 16096130589333770811u64, 3964972929179372247u64, 97007614087u64)\n    put(data, 12512479187631824282u64, 3378050330022776379u64, 135214941613u64)\n    put(data, 16980304980540557310u64, 6065353437512901255u64, 173183124475u64)\n    put(data, 8640919162749295366u64, 12768753059854699889u64, 251328803468u64)\n    put(data, 7862382415464063513u64, 6848720690951013326u64, 140692195490u64)\n    put(data, 14534157903009925344u64, 10953228058585475132u64, 162371269892u64)\n    put(data, 12627464554215107944u64, 15539127852083296166u64, 4593775682u64)\n    put(data, 2456849734836299173u64, 14534853647735598497u64, 66842377808u64)\n    put(data, 18428252197697827913u64, 1506909603576368170u64, 80787935995u64)\n    put(data, 7244734215936736255u64, 5475702579938239025u64, 251081689733u64)\n    put(data, 14756175050504770087u64, 12039747373985783332u64, 133296838431u64)\n    put(data, 6764116534566945922u64, 17572399137760898460u64, 31652676012u64)\n    put(data, 1588822142405565521u64, 869552790852091236u64, 172952601666u64)\n    put(data, 17053265624843842052u64, 4549585778048181804u64, 66047138551u64)\n    put(data, 16996891591759999207u64, 4121918231767210357u64, 247246633539u64)\n    put(data, 8565556232370585876u64, 1558397953312543179u64, 67223449635u64)\n    put(data, 14464960359145886620u64, 6067524298738069781u64, 35084480922u64)\n    put(data, 5813189542048784035u64, 5811095224555517056u64, 154328921151u64)\n    put(data, 9739241026882027025u64, 6440894514158997188u64, 63315020103u64)\n    put(data, 18175068535675302910u64, 4612748874388784257u64, 71349161591u64)\n    put(data, 10562697212061761911u64, 9908101430749813367u64, 119250057617u64)\n    put(data, 4264834835660801368u64, 15150017990912190499u64, 145537119254u64)\n    put(data, 13019066443690126316u64, 17470426264690059239u64, 22821284120u64)\n    put(data, 1828040177823321846u64, 9615161096851907726u64, 24947073705u64)\n    put(data, 11240369830376975668u64, 9227932132124142224u64, 169521238927u64)\n    put(data, 8886938465302549874u64, 4794113194321211621u64, 143500247203u64)\n    put(data, 9985240313589688325u64, 391512698859146347u64, 163259889397u64)\n    put(data, 722909126956573766u64, 17209658878068655842u64, 245021223945u64)\n    put(data, 5493363474638452381u64, 3077364726606876150u64, 9932937477u64)\n    put(data, 12410535279213120491u64, 1952989567673965814u64, 5166824276u64)\n    put(data, 10543108918366869098u64, 11172860676923186449u64, 84105871776u64)\n    put(data, 12953909016524823995u64, 17338078544784947239u64, 160605681990u64)\n    put(data, 16505942145872588169u64, 4593380466519703278u64, 70939899121u64)\n    put(data, 12428594380392015797u64, 786884753602720052u64, 241249007654u64)\n    put(data, 7528259605829768337u64, 17848875822468020539u64, 38042657107u64)\n    put(data, 18147447600042811311u64, 2899664567187130618u64, 83967589497u64)\n    put(data, 12021069431116183911u64, 2973178834961857409u64, 121157191131u64)\n    put(data, 11819985069665662506u64, 11117453141176836727u64, 219161176347u64)\n    put(data, 10401877114068152814u64, 7535238370146462002u64, 27602678342u64)\n    put(data, 4611631138117837942u64, 10246175467290865448u64, 70408486090u64)\n    put(data, 17200813398607252417u64, 1203128834127050464u64, 202555446285u64)\n    put(data, 14100466137553658767u64, 14518048959078919658u64, 13065221744u64)\n    put(data, 17887776768825509301u64, 1553474987376920024u64, 112787025011u64)\n    put(data, 12632656857970087269u64, 14956572380830948369u64, 115084214047u64)\n    put(data, 8923681664054686256u64, 7594162606042048292u64, 31810797413u64)\n    put(data, 6213926103737837599u64, 14461296147288811288u64, 101411680379u64)\n    put(data, 1233118281776157762u64, 18305427728131488265u64, 123783948434u64)\n    put(data, 30716279628678784u64, 10253208939347909876u64, 146992339225u64)\n    put(data, 15775734650898546688u64, 6446028915490812012u64, 25555827570u64)\n    put(data, 976806005729918976u64, 12986063676957432257u64, 114349439927u64)\n    put(data, 12460098853279891456u64, 9769714697972762807u64, 183703975922u64)\n    put(data, 5635665595421687808u64, 97429465146664592u64, 242529617295u64)\n    put(data, 1805943450575568896u64, 16395571728207795868u64, 143005281661u64)\n    put(data, 11529215046068469760u64, 6331668478323650406u64, 125888805724u64)\n    put(data, 0u64, 18129911846294207040u64, 92343240435u64)\n    put(data, 0u64, 9890094564876124160u64, 243982824490u64)\n    put(data, 0u64, 12290856656987750400u64, 42536143100u64)\n    put(data, 0u64, 8498454992640802816u64, 252666288674u64)\n    put(data, 0u64, 5341660584200896512u64, 34460702168u64)\n    put(data, 0u64, 9288674231451648000u64, 216289572000u64)\n    put(data, 0u64, 1152921504606846976u64, 160503540039u64)\n    put(data, 0u64, 0u64, 71062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 4564018338575530435u64, 2081u64, 0u64)\n    put(data, 16553437246451512014u64, 2081247415929u64, 0u64)\n    put(data, 4339777136957372927u64, 15212079674427582639u64, 112u64)\n    put(data, 18439463366554654697u64, 10179808126814248333u64, 112824648491u64)\n    put(data, 1370067356680643003u64, 6066766544199222848u64, 43551848504u64)\n    put(data, 4210124040914115013u64, 6625308131806923532u64, 56328880073u64)\n    put(data, 10692225626142609720u64, 9122786786400665713u64, 201359158673u64)\n    put(data, 11592856673895384344u64, 11932880778639151320u64, 145494547262u64)\n    put(data, 10284479231227406269u64, 3884040911779255011u64, 62646882763u64)\n    put(data, 4961071383534266431u64, 13441817515637357872u64, 203210554279u64)\n    put(data, 10960611551445686988u64, 11628577856022352826u64, 167728682387u64)\n    put(data, 14616396723115619209u64, 13296656925520243654u64, 147630386468u64)\n    put(data, 1025604265437492803u64, 5020720704545399398u64, 36720813216u64)\n    put(data, 11711588454892179178u64, 14121973606499014694u64, 160272173814u64)\n    put(data, 5580373263251565705u64, 3642481034345420114u64, 246765553723u64)\n    put(data, 14109334653033148931u64, 9845536238569696768u64, 59197459292u64)\n    put(data, 2899414033769399895u64, 17655403572195686356u64, 92533727588u64)\n    put(data, 13233457234892808147u64, 8377495365136654029u64, 100957101345u64)\n    put(data, 3287946691509034862u64, 13713682649609025426u64, 33454144933u64)\n    put(data, 5488480288717445911u64, 1367709905452854731u64, 165743420226u64)\n    put(data, 11687233053874362630u64, 9981467701727208680u64, 66074143702u64)\n    put(data, 6783772100089274577u64, 6277920117543306205u64, 214541096448u64)\n    put(data, 7898291058728402485u64, 9344111460418701726u64, 340326731u64)\n    put(data, 4423684977486598491u64, 4918507011364617264u64, 75506545297u64)\n    put(data, 2750833684599526706u64, 6554777203830755259u64, 145266632799u64)\n    put(data, 15669689830489025709u64, 4198262173120265648u64, 95355335184u64)\n    put(data, 15117307274214954517u64, 8080325935698446819u64, 16227588248u64)\n    put(data, 8148639818575698175u64, 12797633874200091733u64, 152438035346u64)\n    put(data, 13006484426078994901u64, 8376502502208665497u64, 146693761122u64)\n    put(data, 10287496057845513526u64, 9891973386793349173u64, 98454091110u64)\n    put(data, 14159876032966532430u64, 14877430279003795462u64, 102536244951u64)\n    put(data, 15742212196465548019u64, 8759933935842067041u64, 215806507111u64)\n    put(data, 2892220461917134150u64, 3753418510388703513u64, 103474876970u64)\n    put(data, 7487151560715393883u64, 2961383332545305985u64, 42203473225u64)\n    put(data, 7245756744165177933u64, 2497674184068629507u64, 73160536912u64)\n    put(data, 3067122860671533987u64, 15244544070742305452u64, 80135399188u64)\n    put(data, 8135043905834122525u64, 45953573565810823u64, 20826408390u64)\n    put(data, 14730019368921022572u64, 3960077421351906445u64, 198002491148u64)\n    put(data, 495969939682055458u64, 3173330011013883118u64, 12214676227u64)\n    put(data, 5617761407265775598u64, 11026266219545759160u64, 3172026564u64)\n    put(data, 2087044847072781811u64, 8886757764964685632u64, 196597735089u64)\n    put(data, 15929674232061203330u64, 13952322129918090479u64, 177481752103u64)\n    put(data, 8658086469608285873u64, 4127250666614902202u64, 39756356898u64)\n    put(data, 18369871790780313570u64, 17649958504065306911u64, 34223738706u64)\n    put(data, 3545648451947416750u64, 13269305359002216873u64, 82956806167u64)\n    put(data, 13347376792767929959u64, 16236593433831947843u64, 23719330484u64)\n    put(data, 14482164459838203025u64, 13580930396682424057u64, 180880187493u64)\n    put(data, 8899577765623565820u64, 421976357197961116u64, 101736223712u64)\n    put(data, 7095320096604405719u64, 2962130818798626533u64, 224022875384u64)\n    put(data, 2968593824439315788u64, 8234383947306356345u64, 248160577433u64)\n    put(data, 12621408323612585636u64, 4380469931801381425u64, 153446386848u64)\n    put(data, 3954422936414648259u64, 15279887469027055916u64, 160237465750u64)\n    put(data, 17143730087577690191u64, 8534542821713755552u64, 150828324359u64)\n    put(data, 5033045529399041876u64, 7814613482565088782u64, 7462658493u64)\n    put(data, 15857648521994521781u64, 13771954404705323224u64, 189423631045u64)\n    put(data, 16655573486499109541u64, 4568173274762548144u64, 197746579144u64)\n    put(data, 16652154439190075858u64, 8105292616250821343u64, 200247641169u64)\n    put(data, 18016950600164130638u64, 2923678426777275612u64, 81439388793u64)\n    put(data, 2086292996072613910u64, 1808633176918384049u64, 121158492925u64)\n    put(data, 17324462585194799521u64, 18118642609460438969u64, 253098046200u64)\n    put(data, 11079151463184927232u64, 18138164175864360870u64, 248982213583u64)\n    put(data, 5239846817488961536u64, 4031433690465792404u64, 207983271850u64)\n    put(data, 2778806963520143360u64, 5012226396942308537u64, 170218544458u64)\n    put(data, 6240890740138835968u64, 7889712298793536835u64, 74271713337u64)\n    put(data, 17250651344549707776u64, 13500762396543628804u64, 57427702160u64)\n    put(data, 4197354852709302272u64, 501020624068841347u64, 144731877796u64)\n    put(data, 9223372036854775808u64, 8370653768288261750u64, 164027160382u64)\n    put(data, 0u64, 647579990023635200u64, 62453774050u64)\n    put(data, 0u64, 11106569307181154304u64, 226035105381u64)\n    put(data, 0u64, 10797461613892861952u64, 101602088328u64)\n    put(data, 0u64, 17627230675448889344u64, 136585331566u64)\n    put(data, 0u64, 12197735707942322176u64, 110955574089u64)\n    put(data, 0u64, 12871287735024877568u64, 73661240577u64)\n    put(data, 0u64, 4611686018427387904u64, 1697753906u64)\n    put(data, 0u64, 0u64, 50250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 5654803392547571318u64, 31757315u64, 0u64)\n    put(data, 2931628102185393332u64, 31757315306547506u64, 0u64)\n    put(data, 15964697617980212305u64, 9451803574512021605u64, 1721567u64)\n    put(data, 450380868305846606u64, 8662766454758138424u64, 223512383298u64)\n    put(data, 14631133530814566148u64, 9207992007314947035u64, 66469609510u64)\n    put(data, 31969822783742095u64, 17118602861291201802u64, 38499166246u64)\n    put(data, 10437269029385743245u64, 11186560605745599967u64, 38928001320u64)\n    put(data, 15196146496377392433u64, 10505549821532796847u64, 40606424665u64)\n    put(data, 4409099735137480938u64, 18133667530488679216u64, 89569506996u64)\n    put(data, 10644987914903248118u64, 10778135771244330799u64, 180983028086u64)\n    put(data, 3154431617534062973u64, 17087985777033767391u64, 118584283910u64)\n    put(data, 11702056331247960454u64, 2639185991757283040u64, 6926341565u64)\n    put(data, 15575315065965259114u64, 5401720287293896400u64, 189143070559u64)\n    put(data, 10759747609480050226u64, 9816495392633895233u64, 95292827843u64)\n    put(data, 12538236653960743718u64, 10042051500090034990u64, 195532153281u64)\n    put(data, 17857942663978005403u64, 11629689537856384738u64, 193544380702u64)\n    put(data, 11443004154750813211u64, 2099086731766010483u64, 30630446733u64)\n    put(data, 4004313188770806737u64, 13665537898516458594u64, 141113791719u64)\n    put(data, 17134872954824183228u64, 16375672064669490764u64, 231740810293u64)\n    put(data, 2659553912986171234u64, 7770550512184564348u64, 53887726961u64)\n    put(data, 9501854300969137926u64, 6197048880720627314u64, 113421242387u64)\n    put(data, 14528169966301018150u64, 17963594118523106281u64, 19335942692u64)\n    put(data, 18172091996515901778u64, 8255454642407818663u64, 36973808388u64)\n    put(data, 1133541705604751035u64, 16744201957549498409u64, 4447529092u64)\n    put(data, 18280349987988641497u64, 17442505417202859722u64, 132907705006u64)\n    put(data, 9936015874712336386u64, 6383975767786687150u64, 174945560113u64)\n    put(data, 15876720399740689614u64, 15245442964998335795u64, 49346076019u64)\n    put(data, 8618117825152456982u64, 2910016124519524433u64, 115826457119u64)\n    put(data, 8085525680745921564u64, 3847913871169988463u64, 31157752290u64)\n    put(data, 8072355444669730953u64, 17210451512590059177u64, 226208595828u64)\n    put(data, 9395030504766848294u64, 17899408909991454145u64, 116932980445u64)\n    put(data, 3537903114058185903u64, 5920601932753251608u64, 221970328901u64)\n    put(data, 2126094743961928691u64, 16521781895108979744u64, 69320956473u64)\n    put(data, 289185362555601115u64, 3697493405554698771u64, 57895647591u64)\n    put(data, 16909937501450129614u64, 2816108280295732065u64, 103200441519u64)\n    put(data, 14449642060360499058u64, 14251078772056398988u64, 175152661535u64)\n    put(data, 12433818908498244393u64, 4543066550096031417u64, 31772552528u64)\n    put(data, 11884444034578008581u64, 3099369389734296977u64, 80246280131u64)\n    put(data, 988625838444140793u64, 5243484113636490986u64, 195168017151u64)\n    put(data, 1675370907158909973u64, 6823370511605197226u64, 255284249843u64)\n    put(data, 15920186275316934067u64, 11396290277624641942u64, 243369895656u64)\n    put(data, 5600921198503757726u64, 15934361408437566099u64, 232617794133u64)\n    put(data, 10457357161776341741u64, 14939272230935131954u64, 85863803462u64)\n    put(data, 12225356765775740093u64, 7500666177940329347u64, 70809859570u64)\n    put(data, 4486633318598164537u64, 4806714453065462270u64, 242406611928u64)\n    put(data, 10302486602879381361u64, 11557851247268441487u64, 216260572512u64)\n    put(data, 15536428611301239541u64, 10655523157206817381u64, 96626552371u64)\n    put(data, 12026126645955462603u64, 14769600176490881210u64, 51577637067u64)\n    put(data, 14877968141142123551u64, 16688495540925795167u64, 203800661629u64)\n    put(data, 734560801645383190u64, 909793965395524173u64, 125904685156u64)\n    put(data, 15648943144911081638u64, 12724590949761703756u64, 100049320029u64)\n    put(data, 13664182862003235646u64, 10810739657314826395u64, 93689801457u64)\n    put(data, 3895127525902132786u64, 2431218615388671301u64, 241586051371u64)\n    put(data, 5249187334214137467u64, 4235001167959059286u64, 43131796625u64)\n    put(data, 10642260063359027505u64, 6253317787396334247u64, 145229579873u64)\n    put(data, 783598951897779422u64, 9534525563070371898u64, 97338993036u64)\n    put(data, 5538576558607624843u64, 8392783992374030728u64, 140516867666u64)\n    put(data, 15974581187564609611u64, 16356257019231647540u64, 82454973731u64)\n    put(data, 7474269406918257428u64, 12896334001521091877u64, 35886674469u64)\n    put(data, 8045286838779138019u64, 1427636373320877084u64, 37699111667u64)\n    put(data, 8184246376556341732u64, 16116755731295043521u64, 243077392322u64)\n    put(data, 1493267152679331840u64, 15945633911163986837u64, 194873691078u64)\n    put(data, 10179074811222818816u64, 7510154241072743838u64, 198864414546u64)\n    put(data, 3892499202005008384u64, 3571560509790395119u64, 82407126277u64)\n    put(data, 10341173215925108736u64, 3576991649007035076u64, 5193614683u64)\n    put(data, 6230307872002015232u64, 15509961892750732443u64, 91193909105u64)\n    put(data, 9295429630892703744u64, 17789791359353349378u64, 113840796718u64)\n    put(data, 0u64, 18331227331079738314u64, 46964386521u64)\n    put(data, 0u64, 15386712883100476416u64, 217993737824u64)\n    put(data, 0u64, 14082462055028752384u64, 96834115376u64)\n    put(data, 0u64, 12919043128765186048u64, 48763411797u64)\n    put(data, 0u64, 6125373368465096704u64, 85700342731u64)\n    put(data, 0u64, 12335992698065387520u64, 203332057155u64)\n    put(data, 0u64, 2774217370460225536u64, 67668735504u64)\n    put(data, 0u64, 0u64, 16150390625u64)\n    put(data, 0u64, 0u64, 97000000000u64)\n    put(data, 10665454627995623288u64, 484u64, 0u64)\n    put(data, 16803537892767562832u64, 484578175453u64, 0u64)\n    put(data, 8099123106849104444u64, 4962829537462579598u64, 26u64)\n    put(data, 7077413686679401728u64, 5711259460785241095u64, 26269035528u64)\n    put(data, 13536636358372449666u64, 13845894607204897444u64, 8309607995u64)\n    put(data, 7280632235418610318u64, 12116633056637003327u64, 59750587450u64)\n    put(data, 6187823673116858809u64, 2965791047992089560u64, 58656843994u64)\n    put(data, 8624343686231740255u64, 16021997451315962529u64, 218160775854u64)\n    put(data, 806211610822132771u64, 3942052271663803856u64, 174868554222u64)\n    put(data, 18388078233202190882u64, 15669876414782439922u64, 238213699081u64)\n    put(data, 7628864426595573600u64, 10594415915406145286u64, 9849465702u64)\n    put(data, 4530799784343874981u64, 10789820553031921377u64, 102574324437u64)\n    put(data, 8561580552078486438u64, 3989990218583987244u64, 213584917344u64)\n    put(data, 13349114951221999594u64, 2937341169808224563u64, 96216297803u64)\n    put(data, 10029144738508991772u64, 16267436558584536843u64, 75159233583u64)\n    put(data, 12601907197916268979u64, 16221580362814625793u64, 47881859502u64)\n    put(data, 1329580921391066941u64, 9695437602320209830u64, 174879373633u64)\n    put(data, 3198179786356761112u64, 10729753156793715126u64, 65525590725u64)\n    put(data, 11406753413634654142u64, 2609241432056861929u64, 197581661084u64)\n    put(data, 11131812960525182090u64, 8462663743997037565u64, 156141447261u64)\n    put(data, 14299636753645227208u64, 14993422143908194069u64, 93458761920u64)\n    put(data, 12964114684643663326u64, 1307443894537745373u64, 192812795043u64)\n    put(data, 5019257593846306316u64, 10017257439419829265u64, 163070876675u64)\n    put(data, 6929086798159998121u64, 16754772009970777891u64, 3543036613u64)\n    put(data, 18359765766933703649u64, 11722573031602862387u64, 197908278010u64)\n    put(data, 9993076234752063198u64, 7363764277467092384u64, 250635481957u64)\n    put(data, 3257189215046584509u64, 6733958494847390772u64, 101399190461u64)\n    put(data, 2905234736672690348u64, 8799796600227451045u64, 189365048621u64)\n    put(data, 12064985302079670056u64, 10512023194742249826u64, 45477037929u64)\n    put(data, 14315096064942799930u64, 4572542132337197059u64, 105569857919u64)\n    put(data, 11943971043551974038u64, 12600500455757416832u64, 127247878005u64)\n    put(data, 12472773152119929647u64, 7873789864743195199u64, 117683074498u64)\n    put(data, 7791259796982183085u64, 15724851676325671539u64, 194426839003u64)\n    put(data, 16866829442051086686u64, 8748017220462413167u64, 219852445917u64)\n    put(data, 3702498393844653053u64, 14172589522760466448u64, 221474230963u64)\n    put(data, 2057239613841701716u64, 9520545591489413768u64, 179768297617u64)\n    put(data, 17201969976738286226u64, 12488551088392570222u64, 145516109810u64)\n    put(data, 18271566505443461640u64, 1135798823651241684u64, 242677005711u64)\n    put(data, 18368509115417119804u64, 11168725610120161972u64, 143061571777u64)\n    put(data, 7613923684154518587u64, 9580104948718508826u64, 193605457828u64)\n    put(data, 17984805084714865232u64, 16638722716909738765u64, 164519338529u64)\n    put(data, 4220802739051410373u64, 15732724012348272797u64, 33901986965u64)\n    put(data, 17812459042810815760u64, 12269722190021214142u64, 149852872677u64)\n    put(data, 10629526089664605307u64, 13110655916311972587u64, 229665142972u64)\n    put(data, 17437443267816548473u64, 6618112997062866867u64, 188710730081u64)\n    put(data, 12506371893701049304u64, 8457936459015989309u64, 97358768624u64)\n    put(data, 15737221540003030739u64, 3329167139937134914u64, 240458505654u64)\n    put(data, 2358380859011605513u64, 5245511557216705097u64, 182180474512u64)\n    put(data, 10042972713837039706u64, 5655931353280440466u64, 144284359751u64)\n    put(data, 13424397272769642495u64, 604622132328697393u64, 71306608653u64)\n    put(data, 6867102315755663029u64, 8673282619234652338u64, 13032776631u64)\n    put(data, 8690748404825506734u64, 16929477433058445690u64, 183470179592u64)\n    put(data, 956229930815387710u64, 11036952409253549455u64, 8917748810u64)\n    put(data, 7734449506297687888u64, 18199392190170386320u64, 74598314388u64)\n    put(data, 16332184961683848151u64, 9683116091880335715u64, 148986591027u64)\n    put(data, 15580777817612768828u64, 2993913337608915120u64, 51524922775u64)\n    put(data, 17404245271944696092u64, 4490779842162392585u64, 151162300367u64)\n    put(data, 13201420160494469229u64, 946849923353644618u64, 207243445663u64)\n    put(data, 16511717657124068078u64, 3613491058474899828u64, 159051328837u64)\n    put(data, 14131292492116594062u64, 14624054199004410935u64, 69195887742u64)\n    put(data, 18429136031865875691u64, 12088470271991908244u64, 126792771566u64)\n    put(data, 2563978348305862197u64, 10071980927725011290u64, 238655317286u64)\n    put(data, 14239974392147482896u64, 2833441711428854664u64, 38546003180u64)\n    put(data, 11164201396098998272u64, 17655572411864340446u64, 236153601182u64)\n    put(data, 7116971104932986880u64, 4997642792058747802u64, 158957110498u64)\n    put(data, 12437629862867369984u64, 11489200787635734848u64, 226270922758u64)\n    put(data, 16501893821638901760u64, 12983586226429536913u64, 6622830822u64)\n    put(data, 10649324268870959104u64, 12311150768725063152u64, 230703841619u64)\n    put(data, 7205759403792793600u64, 8530052476845967905u64, 83667388820u64)\n    put(data, 0u64, 6282736361499820264u64, 148462415071u64)\n    put(data, 0u64, 11337164765929082880u64, 223340587820u64)\n    put(data, 0u64, 8343856200414134272u64, 44614588933u64)\n    put(data, 0u64, 17889330377156198400u64, 5452321350u64)\n    put(data, 0u64, 17730714064155312128u64, 70969782542u64)\n    put(data, 0u64, 7449235258647511040u64, 14961183935u64)\n    put(data, 0u64, 9943947977234055168u64, 191403823852u64)\n    put(data, 0u64, 0u64, 236539062500u64)\n    put(data, 0u64, 0u64, 228000000000u64)\n    put(data, 3016823727048309817u64, 7394076u64, 0u64)\n    put(data, 17405973192644624358u64, 7394076163542341u64, 0u64)\n    put(data, 8937219978302591747u64, 12396245121240683569u64, 400833u64)\n    put(data, 15178463196824222317u64, 10248996648596888561u64, 193672001794u64)\n    put(data, 11979404627460330594u64, 11257495103713935002u64, 2555599221u64)\n    put(data, 2006448052689740002u64, 7555396579247433114u64, 117610270032u64)\n    put(data, 16330465320863239865u64, 4805022328730367462u64, 80409578869u64)\n    put(data, 17136208883957646553u64, 7056637817080232586u64, 117260480782u64)\n    put(data, 140455118208931867u64, 10811411483818434913u64, 14382541102u64)\n    put(data, 3964972929179372247u64, 16962406704495245447u64, 46586087790u64)\n    put(data, 3378050330022776379u64, 18074517319117194669u64, 110919533909u64)\n    put(data, 6065353437512901255u64, 3702019776117654523u64, 85979821547u64)\n    put(data, 12768753059854699889u64, 3551977551381082764u64, 235200686894u64)\n    put(data, 6848720690951013326u64, 16442608985936005282u64, 46192553088u64)\n    put(data, 10953228058585475132u64, 3580046275479139588u64, 128891355619u64)\n    put(data, 15539127852083296166u64, 8737412692712715330u64, 227194074697u64)\n    put(data, 14534853647735598497u64, 3082033243045084752u64, 73473656091u64)\n    put(data, 1506909603576368170u64, 16401023756841128699u64, 27167077356u64)\n    put(data, 5475702579938239025u64, 7520296082779572869u64, 236889101279u64)\n    put(data, 12039747373985783332u64, 9854104766152464159u64, 223407676067u64)\n    put(data, 17572399137760898460u64, 14169188802648310188u64, 163534192089u64)\n    put(data, 869552790852091236u64, 2018609909210367042u64, 217768113264u64)\n    put(data, 4549585778048181804u64, 8270271948267674359u64, 112109429062u64)\n    put(data, 4121918231767210357u64, 12320338602894572099u64, 70448332340u64)\n    put(data, 1558397953312543179u64, 17538536685990080547u64, 52667886893u64)\n    put(data, 6067524298738069781u64, 15833914616956760474u64, 45950765978u64)\n    put(data, 5811095224555517056u64, 6137696141415969855u64, 154858358231u64)\n    put(data, 6440894514158997188u64, 9757490468419438919u64, 215332725174u64)\n    put(data, 4612748874388784257u64, 3566639201356598903u64, 182528954618u64)\n    put(data, 9908101430749813367u64, 9760900035773954449u64, 250193347898u64)\n    put(data, 15150017990912190499u64, 3873778773990716438u64, 58529139451u64)\n    put(data, 17470426264690059239u64, 2295668377270167832u64, 251209997968u64)\n    put(data, 9615161096851907726u64, 1791721710912807593u64, 144124448432u64)\n    put(data, 9227932132124142224u64, 10571009006922683279u64, 176097129428u64)\n    put(data, 4794113194321211621u64, 9840791932778184867u64, 212573055546u64)\n    put(data, 391512698859146347u64, 11525464956561274613u64, 58533470399u64)\n    put(data, 17209658878068655842u64, 4435781488897895433u64, 191624796707u64)\n    put(data, 3077364726606876150u64, 6395563367070996741u64, 35240464196u64)\n    put(data, 1952989567673965814u64, 15538690795135662932u64, 68346704184u64)\n    put(data, 11172860676923186449u64, 16294558813563371936u64, 56842354115u64)\n    put(data, 17338078544784947239u64, 4942096228426070342u64, 195883329803u64)\n    put(data, 4593380466519703278u64, 6910116424372647153u64, 11267911573u64)\n    put(data, 786884753602720052u64, 17923400669760829478u64, 149374598161u64)\n    put(data, 17848875822468020539u64, 4134686917293039955u64, 17971629497u64)\n    put(data, 2899664567187130618u64, 16857102463116098681u64, 185224141826u64)\n    put(data, 2973178834961857409u64, 11364321508775167451u64, 2913825355u64)\n    put(data, 11117453141176836727u64, 7966947780972783899u64, 75616061103u64)\n    put(data, 7535238370146462002u64, 11261055695926686278u64, 175431889104u64)\n    put(data, 10246175467290865448u64, 9227040437353594058u64, 208610463052u64)\n    put(data, 1203128834127050464u64, 7185344074282882061u64, 76500198864u64)\n    put(data, 14518048959078919658u64, 14197856148610578032u64, 208389518282u64)\n    put(data, 1553474987376920024u64, 885688687260429427u64, 202769667324u64)\n    put(data, 14956572380830948369u64, 17407816160380305183u64, 252048013279u64)\n    put(data, 7594162606042048292u64, 17812728703806357349u64, 223943679604u64)\n    put(data, 14461296147288811288u64, 17120198191964319867u64, 116965629957u64)\n    put(data, 18305427728131488265u64, 12091952048375408786u64, 5928087803u64)\n    put(data, 10253208939347909876u64, 405056939269888281u64, 251655506034u64)\n    put(data, 6446028915490812012u64, 12485440679452408690u64, 114021958180u64)\n    put(data, 12986063676957432257u64, 8394369900823444407u64, 36676837095u64)\n    put(data, 9769714697972762807u64, 2877421667354294258u64, 231455059704u64)\n    put(data, 97429465146664592u64, 2676980714750756239u64, 248155985341u64)\n    put(data, 16395571728207795868u64, 6119309228579057021u64, 189145119415u64)\n    put(data, 6331668478323650406u64, 18203256146533333852u64, 183331728417u64)\n    put(data, 18129911846294207040u64, 351919978865493747u64, 33986800493u64)\n    put(data, 9890094564876124160u64, 5190010931882390570u64, 109019077620u64)\n    put(data, 12290856656987750400u64, 6982466386088036604u64, 244281351056u64)\n    put(data, 8498454992640802816u64, 4707293888784996898u64, 144378520261u64)\n    put(data, 5341660584200896512u64, 690306801165964760u64, 197255182913u64)\n    put(data, 9288674231451648000u64, 12456770961278956704u64, 65037421606u64)\n    put(data, 1152921504606846976u64, 16946092489294063943u64, 38675282906u64)\n    put(data, 0u64, 11098404173866185376u64, 218918649514u64)\n    put(data, 0u64, 15152070965853306880u64, 170601645695u64)\n    put(data, 0u64, 17370091362040414208u64, 127821395412u64)\n    put(data, 0u64, 10141938552171134976u64, 212941634539u64)\n    put(data, 0u64, 10586988556645826560u64, 235549795590u64)\n    put(data, 0u64, 12169852093061922816u64, 6573921799u64)\n    put(data, 0u64, 16717361816799281152u64, 7659729003u64)\n    put(data, 0u64, 0u64, 107906250000u64)\n    put(data, 0u64, 0u64, 16000000000u64)\n    put(data, 15212079674427582639u64, 112u64, 0u64)\n    put(data, 10179808126814248333u64, 112824648491u64, 0u64)\n    put(data, 6066766544199222848u64, 2144184049294538808u64, 6u64)\n    put(data, 6625308131806923532u64, 4108002197393276873u64, 6116236450u64)\n    put(data, 9122786786400665713u64, 6446230217393892753u64, 162222695245u64)\n    put(data, 11932880778639151320u64, 5571068025259989822u64, 77349450840u64)\n    put(data, 3884040911779255011u64, 14804812668872528331u64, 88302008202u64)\n    put(data, 13441817515637357872u64, 17369928488562523047u64, 138802570502u64)\n    put(data, 11628577856022352826u64, 2967474173531035027u64, 6941625710u64)\n    put(data, 13296656925520243654u64, 5291425437992807716u64, 110160867097u64)\n    put(data, 5020720704545399398u64, 14219547193739388064u64, 25286848747u64)\n    put(data, 14121973606499014694u64, 17720313647158217462u64, 235770843197u64)\n    put(data, 3642481034345420114u64, 12334850628290578491u64, 61960620127u64)\n    put(data, 9845536238569696768u64, 7818499847417334620u64, 95668673592u64)\n    put(data, 17655403572195686356u64, 136007040922198372u64, 56423841726u64)\n    put(data, 8377495365136654029u64, 8523477092112604449u64, 190007372956u64)\n    put(data, 13713682649609025426u64, 367934822655966629u64, 156462058619u64)\n    put(data, 1367709905452854731u64, 12964987687054730050u64, 123019945786u64)\n    put(data, 9981467701727208680u64, 15267036012420885462u64, 58702833390u64)\n    put(data, 6277920117543306205u64, 11142900264750765568u64, 238827627680u64)\n    put(data, 9344111460418701726u64, 13680181547777718603u64, 160604057833u64)\n    put(data, 4918507011364617264u64, 13001922925761426065u64, 233741604127u64)\n    put(data, 6554777203830755259u64, 2397730045956515935u64, 31704835654u64)\n    put(data, 4198262173120265648u64, 4482395522588406288u64, 70129981206u64)\n    put(data, 8080325935698446819u64, 3255525722490493080u64, 22242991148u64)\n    put(data, 12797633874200091733u64, 836222287193822098u64, 44176482403u64)\n    put(data, 8376502502208665497u64, 420898743993182306u64, 99045331701u64)\n    put(data, 9891973386793349173u64, 11652649973356574054u64, 245022816966u64)\n    put(data, 14877430279003795462u64, 15058402726661910231u64, 198631691420u64)\n    put(data, 8759933935842067041u64, 9600134495208339559u64, 156816317647u64)\n    put(data, 3753418510388703513u64, 14626343323989004842u64, 207520424333u64)\n    put(data, 2961383332545305985u64, 6813981265331086665u64, 141792895660u64)\n    put(data, 2497674184068629507u64, 10281745288790487888u64, 172369386664u64)\n    put(data, 15244544070742305452u64, 17569829347075761940u64, 168557374528u64)\n    put(data, 45953573565810823u64, 7654580675237889478u64, 64952462357u64)\n    put(data, 3960077421351906445u64, 16194838649686212364u64, 21414955649u64)\n    put(data, 3173330011013883118u64, 6495102772252453635u64, 129877923962u64)\n    put(data, 11026266219545759160u64, 14935159852819761348u64, 122352100226u64)\n    put(data, 8886757764964685632u64, 17381879863441579697u64, 130809636637u64)\n    put(data, 13952322129918090479u64, 9062335510435372583u64, 29942273595u64)\n    put(data, 4127250666614902202u64, 7569219009130126626u64, 59491270192u64)\n    put(data, 17649958504065306911u64, 12652124168176193362u64, 48410328184u64)\n    put(data, 13269305359002216873u64, 8940200224697247767u64, 120685873025u64)\n    put(data, 16236593433831947843u64, 5600570701927432884u64, 129484649225u64)\n    put(data, 13580930396682424057u64, 2018432801986093157u64, 9303607546u64)\n    put(data, 421976357197961116u64, 8235849749361824736u64, 250109419461u64)\n    put(data, 2962130818798626533u64, 9705097287982370040u64, 197446466309u64)\n    put(data, 8234383947306356345u64, 3517483139049842585u64, 5526114378u64)\n    put(data, 4380469931801381425u64, 958281614186777760u64, 74190683143u64)\n    put(data, 15279887469027055916u64, 7336473432636108950u64, 7051948550u64)\n    put(data, 8534542821713755552u64, 12955383920176764423u64, 6397711021u64)\n    put(data, 7814613482565088782u64, 10735469126281273789u64, 173702312769u64)\n    put(data, 13771954404705323224u64, 8637888232514730693u64, 65581970947u64)\n    put(data, 4568173274762548144u64, 6806336737533581000u64, 3468260859u64)\n    put(data, 8105292616250821343u64, 16142569672872330321u64, 251368972253u64)\n    put(data, 2923678426777275612u64, 8141285259947963513u64, 221875090455u64)\n    put(data, 1808633176918384049u64, 5220241098754220797u64, 23441339958u64)\n    put(data, 18118642609460438969u64, 154438799943119608u64, 54282989837u64)\n    put(data, 18138164175864360870u64, 2226876628677628879u64, 13008372144u64)\n    put(data, 4031433690465792404u64, 17219557081221357482u64, 176120719223u64)\n    put(data, 5012226396942308537u64, 15401507148161015114u64, 119933474059u64)\n    put(data, 7889712298793536835u64, 8842629766613985337u64, 11834917375u64)\n    put(data, 13500762396543628804u64, 3180100571546071440u64, 255479359920u64)\n    put(data, 501020624068841347u64, 7740848704392475044u64, 176172393597u64)\n    put(data, 8370653768288261750u64, 2014314126623495998u64, 125419632249u64)\n    put(data, 647579990023635200u64, 11209566016506885858u64, 121109196187u64)\n    put(data, 11106569307181154304u64, 7117166613733441125u64, 155607671791u64)\n    put(data, 10797461613892861952u64, 4197646860931880328u64, 239385822375u64)\n    put(data, 17627230675448889344u64, 5487263271238026094u64, 167227554892u64)\n    put(data, 12197735707942322176u64, 18148076225293562697u64, 76297465137u64)\n    put(data, 12871287735024877568u64, 9127276943027950849u64, 49983809183u64)\n    put(data, 4611686018427387904u64, 9691696125379324722u64, 159494790674u64)\n    put(data, 0u64, 13102362262487705216u64, 18525387899u64)\n    put(data, 0u64, 8929385439893192704u64, 123710280481u64)\n    put(data, 0u64, 11891353410743566336u64, 33484062954u64)\n    put(data, 0u64, 1587423090877399040u64, 234644631560u64)\n    put(data, 0u64, 3489137423026225152u64, 8086054378u64)\n    put(data, 0u64, 13046928120492326912u64, 234189146518u64)\n    put(data, 0u64, 11529215046068469760u64, 150707275390u64)\n    put(data, 0u64, 0u64, 126625000000u64)\n    put(data, 0u64, 0u64, 64000000000u64)\n    put(data, 9451803574512021605u64, 1721567u64, 0u64)\n    put(data, 8662766454758138424u64, 1721567512383298u64, 0u64)\n    put(data, 9207992007314947035u64, 6674960280855494694u64, 93326u64)\n    put(data, 17118602861291201802u64, 16378845781483497510u64, 142361850321u64)\n    put(data, 11186560605745599967u64, 17606907750956804392u64, 209887899008u64)\n    put(data, 10505549821532796847u64, 13225609159240506969u64, 128954472381u64)\n    put(data, 18133667530488679216u64, 2668084873338435252u64, 189716961709u64)\n    put(data, 10778135771244330799u64, 14802814305275861366u64, 173144637170u64)\n    put(data, 17087985777033767391u64, 8005510553372365574u64, 242802462171u64)\n    put(data, 2639185991757283040u64, 12748500143273514429u64, 219433979596u64)\n    put(data, 5401720287293896400u64, 10393733905569036127u64, 204691097577u64)\n    put(data, 9816495392633895233u64, 603389089974790339u64, 233563445444u64)\n    put(data, 10042051500090034990u64, 2033494532597735873u64, 196032709788u64)\n    put(data, 11629689537856384738u64, 9204796763694620958u64, 156110235959u64)\n    put(data, 2099086731766010483u64, 7826260310402107021u64, 55498993032u64)\n    put(data, 13665537898516458594u64, 10122690201685169383u64, 136424262421u64)\n    put(data, 16375672064669490764u64, 7438455564568110133u64, 21548752135u64)\n    put(data, 7770550512184564348u64, 2805412574380520817u64, 7403239484u64)\n    put(data, 6197048880720627314u64, 7250965427231182867u64, 60152081720u64)\n    put(data, 17963594118523106281u64, 8136242944826085924u64, 56393075623u64)\n    put(data, 8255454642407818663u64, 15357191647956011780u64, 167441066613u64)\n    put(data, 16744201957549498409u64, 7369614426695395460u64, 117832515027u64)\n    put(data, 17442505417202859722u64, 10886957545142526638u64, 211399507598u64)\n    put(data, 6383975767786687150u64, 2030047207417538097u64, 142590183151u64)\n    put(data, 15245442964998335795u64, 11557093828502314355u64, 239110049079u64)\n    put(data, 2910016124519524433u64, 15201062539664128543u64, 55626511311u64)\n    put(data, 3847913871169988463u64, 8846936323343880674u64, 207824051251u64)\n    put(data, 17210451512590059177u64, 1485291750116245364u64, 51479593379u64)\n    put(data, 17899408909991454145u64, 2076024439668322013u64, 163080517827u64)\n    put(data, 5920601932753251608u64, 7029497773682748741u64, 195112541510u64)\n    put(data, 16521781895108979744u64, 16333533921668749881u64, 70381069837u64)\n    put(data, 3697493405554698771u64, 2065057316131928423u64, 13885442648u64)\n    put(data, 2816108280295732065u64, 7800502648925570223u64, 88111946981u64)\n    put(data, 14251078772056398988u64, 17011619967093802015u64, 229422866095u64)\n    put(data, 4543066550096031417u64, 5368819344429198672u64, 175922201766u64)\n    put(data, 3099369389734296977u64, 15598879366754275267u64, 166291044279u64)\n    put(data, 5243484113636490986u64, 16393893486035835647u64, 183845616944u64)\n    put(data, 6823370511605197226u64, 12042046205096920307u64, 48888714746u64)\n    put(data, 11396290277624641942u64, 15437070428008474344u64, 250652800632u64)\n    put(data, 15934361408437566099u64, 13704569163204647509u64, 120836845264u64)\n    put(data, 14939272230935131954u64, 18192483750856993350u64, 208742926182u64)\n    put(data, 7500666177940329347u64, 5152535865317963250u64, 102986216520u64)\n    put(data, 4806714453065462270u64, 17512614083933854680u64, 72279319528u64)\n    put(data, 11557851247268441487u64, 14481918350603613536u64, 232949360711u64)\n    put(data, 10655523157206817381u64, 16124419709964004915u64, 71785066366u64)\n    put(data, 14769600176490881210u64, 18088011566435813579u64, 126874106543u64)\n    put(data, 16688495540925795167u64, 15008862380698848893u64, 175980553071u64)\n    put(data, 909793965395524173u64, 18160498644611827812u64, 111813632059u64)\n    put(data, 12724590949761703756u64, 3604680497457231965u64, 59984482604u64)\n    put(data, 10810739657314826395u64, 5957615565551495921u64, 44195410121u64)\n    put(data, 2431218615388671301u64, 17528455034961565995u64, 201322962986u64)\n    put(data, 4235001167959059286u64, 8503772325120113809u64, 42950219451u64)\n    put(data, 6253317787396334247u64, 8501492578048509537u64, 187460990421u64)\n    put(data, 9534525563070371898u64, 2296237701094386060u64, 213460866836u64)\n    put(data, 8392783992374030728u64, 3753593040591076946u64, 20124479295u64)\n    put(data, 16356257019231647540u64, 8518075399775653155u64, 63203482686u64)\n    put(data, 12896334001521091877u64, 12757855675959554597u64, 62461765792u64)\n    put(data, 1427636373320877084u64, 121631169379748595u64, 160691604742u64)\n    put(data, 16116755731295043521u64, 16679062494579173314u64, 6006593638u64)\n    put(data, 15945633911163986837u64, 10739912744743898054u64, 102904173789u64)\n    put(data, 7510154241072743838u64, 9367340677776287570u64, 221582211836u64)\n    put(data, 3571560509790395119u64, 12227321512794715397u64, 252507804555u64)\n    put(data, 3576991649007035076u64, 7241061891859170651u64, 139662844427u64)\n    put(data, 15509961892750732443u64, 13148571323079237489u64, 11392538751u64)\n    put(data, 17789791359353349378u64, 12509763434355012654u64, 127712785479u64)\n    put(data, 18331227331079738314u64, 11812768946960181977u64, 71678155634u64)\n    put(data, 15386712883100476416u64, 14170358803552564832u64, 114640371487u64)\n    put(data, 14082462055028752384u64, 18179989524780635952u64, 31768176689u64)\n    put(data, 12919043128765186048u64, 17091718978514754901u64, 49985539206u64)\n    put(data, 6125373368465096704u64, 7394768384359232459u64, 134926543942u64)\n    put(data, 12335992698065387520u64, 6778628272692852803u64, 70400871197u64)\n    put(data, 2774217370460225536u64, 18193335045875234320u64, 29367470174u64)\n    put(data, 0u64, 1378519212560967521u64, 94986262669u64)\n    put(data, 0u64, 4677732610631043584u64, 141074729676u64)\n    put(data, 0u64, 17296098591070486528u64, 204253580392u64)\n    put(data, 0u64, 7343735382392963072u64, 104937623383u64)\n    put(data, 0u64, 14525996728454217728u64, 87398104692u64)\n    put(data, 0u64, 9691359370008330240u64, 116787455860u64)\n    put(data, 0u64, 3044433348102455296u64, 116525369644u64)\n    put(data, 0u64, 9223372036854775808u64, 44165039062u64)\n    put(data, 0u64, 0u64, 214500000000u64)\n    put(data, 4962829537462579598u64, 26u64, 0u64)\n    put(data, 5711259460785241095u64, 26269035528u64, 0u64)\n    put(data, 13845894607204897444u64, 7822291454600056379u64, 1u64)\n    put(data, 12116633056637003327u64, 8201586317771250746u64, 1424047269u64)\n    put(data, 2965791047992089560u64, 3278889188817135834u64, 165444608885u64)\n    put(data, 16021997451315962529u64, 1710725240251040430u64, 117177748939u64)\n    put(data, 3942052271663803856u64, 1850175733663425006u64, 203092738601u64)\n    put(data, 15669876414782439922u64, 9147599666163914249u64, 41100298227u64)\n    put(data, 10594415915406145286u64, 10221885933644344166u64, 243495892371u64)\n    put(data, 10789820553031921377u64, 14901479793736678101u64, 147554129546u64)\n    put(data, 3989990218583987244u64, 5181831442059703136u64, 138807810838u64)\n    put(data, 2937341169808224563u64, 6396246577759793483u64, 22280907645u64)\n    put(data, 16267436558584536843u64, 14167229556464870447u64, 125346741221u64)\n    put(data, 16221580362814625793u64, 2969982933326311854u64, 229768007053u64)\n    put(data, 9695437602320209830u64, 7892677766222018881u64, 141161003097u64)\n    put(data, 10729753156793715126u64, 798698968922663621u64, 89427862919u64)\n    put(data, 2609241432056861929u64, 15926812109043458972u64, 135043297557u64)\n    put(data, 8462663743997037565u64, 8663842590352697437u64, 21863394214u64)\n    put(data, 14993422143908194069u64, 17093523026636671168u64, 166469667847u64)\n    put(data, 1307443894537745373u64, 839764004742743203u64, 7926641740u64)\n    put(data, 10017257439419829265u64, 16894643909298232323u64, 76045523697u64)\n    put(data, 16754772009970777891u64, 9066702926218949317u64, 241915860481u64)\n    put(data, 11722573031602862387u64, 9119392417260546810u64, 1491506950u64)\n    put(data, 7363764277467092384u64, 9723021096578315109u64, 6494363253u64)\n    put(data, 6733958494847390772u64, 14787464248751217597u64, 117527086029u64)\n    put(data, 8799796600227451045u64, 3733434565920249133u64, 205801630043u64)\n    put(data, 10512023194742249826u64, 6643788868836820841u64, 91202389893u64)\n    put(data, 4572542132337197059u64, 4729646697422664063u64, 133360160516u64)\n    put(data, 12600500455757416832u64, 4090144564201555829u64, 4256394661u64)\n    put(data, 7873789864743195199u64, 2109480737093400002u64, 165221727181u64)\n    put(data, 15724851676325671539u64, 16577155033369419739u64, 205114355179u64)\n    put(data, 8748017220462413167u64, 745377248603805917u64, 235898649375u64)\n    put(data, 14172589522760466448u64, 11305561465807999667u64, 31040406981u64)\n    put(data, 9520545591489413768u64, 2211245518782892177u64, 197612875715u64)\n    put(data, 12488551088392570222u64, 14170095199249735666u64, 195119871859u64)\n    put(data, 1135798823651241684u64, 17849973668116118927u64, 115768162399u64)\n    put(data, 11168725610120161972u64, 9020960204585720001u64, 95967649011u64)\n    put(data, 9580104948718508826u64, 10807134002871850916u64, 243489027232u64)\n    put(data, 16638722716909738765u64, 3925122626254791201u64, 160585855908u64)\n    put(data, 15732724012348272797u64, 17208463291312718997u64, 164212781323u64)\n    put(data, 12269722190021214142u64, 5145077219589447653u64, 11932872664u64)\n    put(data, 13110655916311972587u64, 17602397765035489468u64, 216278915194u64)\n    put(data, 6618112997062866867u64, 16422643262490753377u64, 122954227894u64)\n    put(data, 8457936459015989309u64, 2902509461400906224u64, 182890273275u64)\n    put(data, 3329167139937134914u64, 3422418805967265206u64, 251157345353u64)\n    put(data, 5245511557216705097u64, 4228874576277237392u64, 73185529695u64)\n    put(data, 5655931353280440466u64, 2553488530807495751u64, 95229247750u64)\n    put(data, 604622132328697393u64, 11546099176912486413u64, 6138424890u64)\n    put(data, 8673282619234652338u64, 10460791037534167991u64, 58625915290u64)\n    put(data, 16929477433058445690u64, 8127117908566000904u64, 154567080618u64)\n    put(data, 11036952409253549455u64, 11541304458088287306u64, 170440571944u64)\n    put(data, 18199392190170386320u64, 6249718665174839700u64, 40625655368u64)\n    put(data, 9683116091880335715u64, 13102508413386290995u64, 72338797927u64)\n    put(data, 2993913337608915120u64, 6274675218640661911u64, 103710288404u64)\n    put(data, 4490779842162392585u64, 3404497118599817167u64, 20340150825u64)\n    put(data, 946849923353644618u64, 11258566093988562335u64, 41184558158u64)\n    put(data, 3613491058474899828u64, 16762592482501635397u64, 78610328090u64)\n    put(data, 14624054199004410935u64, 5550125446725071998u64, 26908701959u64)\n    put(data, 12088470271991908244u64, 6370033225258510318u64, 7300872903u64)\n    put(data, 10071980927725011290u64, 1503521728674735398u64, 199345320193u64)\n    put(data, 2833441711428854664u64, 4250415082606384364u64, 1081506076u64)\n    put(data, 17655572411864340446u64, 6020091901030562974u64, 28230415463u64)\n    put(data, 4997642792058747802u64, 16288222967151527138u64, 103326349835u64)\n    put(data, 11489200787635734848u64, 6377016228656203782u64, 11882986336u64)\n    put(data, 12983586226429536913u64, 8378856515587563750u64, 96345698742u64)\n    put(data, 12311150768725063152u64, 15812881490200838483u64, 182454218721u64)\n    put(data, 8530052476845967905u64, 4548570371183413652u64, 225857218023u64)\n    put(data, 6282736361499820264u64, 16731431495283420383u64, 231246578493u64)\n    put(data, 11337164765929082880u64, 14737727629551135532u64, 61907012718u64)\n    put(data, 8343856200414134272u64, 12413722258104293893u64, 110798933815u64)\n    put(data, 17889330377156198400u64, 800899742400762438u64, 55672949232u64)\n    put(data, 17730714064155312128u64, 603197008376033550u64, 240043416862u64)\n    put(data, 7449235258647511040u64, 6380777281587743935u64, 30032699375u64)\n    put(data, 9943947977234055168u64, 10001440249018225388u64, 239345902629u64)\n    put(data, 0u64, 5505914461980436708u64, 37542179162u64)\n    put(data, 0u64, 1105464290051876864u64, 90298476221u64)\n    put(data, 0u64, 4500443576769970176u64, 189059927339u64)\n    put(data, 0u64, 2843045143185981440u64, 43243969535u64)\n    put(data, 0u64, 660949699682893824u64, 255154121786u64)\n    put(data, 0u64, 276549164618219520u64, 58035830155u64)\n    put(data, 0u64, 4683743612465315840u64, 139014991760u64)\n    put(data, 0u64, 0u64, 144253906250u64)\n    put(data, 0u64, 0u64, 74000000000u64)\n    put(data, 12396245121240683569u64, 400833u64, 0u64)\n    put(data, 10248996648596888561u64, 400833672001794u64, 0u64)\n    put(data, 11257495103713935002u64, 4370024159708535157u64, 21729u64)\n    put(data, 7555396579247433114u64, 7166684413908503888u64, 225236899484u64)\n    put(data, 4805022328730367462u64, 10217286283215687029u64, 156388506740u64)\n    put(data, 7056637817080232586u64, 4767369911989629198u64, 116553880199u64)\n    put(data, 10811411483818434913u64, 14407999214182082862u64, 135258439640u64)\n    put(data, 16962406704495245447u64, 8472271297615317358u64, 216781059202u64)\n    put(data, 18074517319117194669u64, 6236024012584764757u64, 130459282747u64)\n    put(data, 3702019776117654523u64, 1951826556984620523u64, 59338055539u64)\n    put(data, 3551977551381082764u64, 12357130551551830830u64, 115105808729u64)\n    put(data, 16442608985936005282u64, 8927758011099278464u64, 89669881389u64)\n    put(data, 3580046275479139588u64, 10199854049407140323u64, 45483974731u64)\n    put(data, 8737412692712715330u64, 17895455027038549577u64, 75552935195u64)\n    put(data, 3082033243045084752u64, 16539200343720527131u64, 27970114560u64)\n    put(data, 16401023756841128699u64, 3536976106235802604u64, 896591847u64)\n    put(data, 7520296082779572869u64, 16980391644793590751u64, 231191739858u64)\n    put(data, 9854104766152464159u64, 10090294316609084067u64, 210920508875u64)\n    put(data, 14169188802648310188u64, 17603457857266236889u64, 203546995950u64)\n    put(data, 2018609909210367042u64, 11164962743035868272u64, 238954285362u64)\n    put(data, 8270271948267674359u64, 1585686890718568774u64, 50605253843u64)\n    put(data, 12320338602894572099u64, 10882524700472655412u64, 211085960258u64)\n    put(data, 17538536685990080547u64, 2194808754940947757u64, 66589942846u64)\n    put(data, 15833914616956760474u64, 274100791137209242u64, 62118980821u64)\n    put(data, 6137696141415969855u64, 12203404582981010903u64, 213014859033u64)\n    put(data, 9757490468419438919u64, 541940706340938166u64, 25661547888u64)\n    put(data, 3566639201356598903u64, 10305434016011833594u64, 112029378664u64)\n    put(data, 9760900035773954449u64, 7900783531944543546u64, 104558658697u64)\n    put(data, 3873778773990716438u64, 8920818625012419323u64, 137428302333u64)\n    put(data, 2295668377270167832u64, 12532363335400447632u64, 253483598546u64)\n    put(data, 1791721710912807593u64, 13483507182924762800u64, 210679380777u64)\n    put(data, 10571009006922683279u64, 415911049779278804u64, 41730942389u64)\n    put(data, 9840791932778184867u64, 3441628281170127418u64, 181022546583u64)\n    put(data, 11525464956561274613u64, 17830811568183566527u64, 151186571042u64)\n    put(data, 4435781488897895433u64, 17897295813176613411u64, 34966610231u64)\n    put(data, 6395563367070996741u64, 2086148701331574596u64, 55970214350u64)\n    put(data, 15538690795135662932u64, 13015567826878853432u64, 206113090347u64)\n    put(data, 16294558813563371936u64, 12944531121587846595u64, 43705575345u64)\n    put(data, 4942096228426070342u64, 3534180912913737995u64, 177701724438u64)\n    put(data, 6910116424372647153u64, 3447584022400118677u64, 22191588331u64)\n    put(data, 17923400669760829478u64, 6375676813770849297u64, 235186893904u64)\n    put(data, 4134686917293039955u64, 11580694081479200185u64, 80345626132u64)\n    put(data, 16857102463116098681u64, 1872134358882196482u64, 20627790684u64)\n    put(data, 11364321508775167451u64, 17602652840520938059u64, 92101488606u64)\n    put(data, 7966947780972783899u64, 10331040597716338351u64, 222954241722u64)\n    put(data, 11261055695926686278u64, 73785407041056976u64, 186560046833u64)\n    put(data, 9227040437353594058u64, 17166209109167902028u64, 241003999914u64)\n    put(data, 7185344074282882061u64, 8762475644006589904u64, 170930582060u64)\n    put(data, 14197856148610578032u64, 8839001228645872586u64, 44475014756u64)\n    put(data, 885688687260429427u64, 13558262784529110268u64, 100479163216u64)\n    put(data, 17407816160380305183u64, 5640853896420358111u64, 80734994898u64)\n    put(data, 17812728703806357349u64, 8459930353450835572u64, 210305791302u64)\n    put(data, 17120198191964319867u64, 7643830211500171269u64, 70458613743u64)\n    put(data, 12091952048375408786u64, 1308629115231236347u64, 239414372866u64)\n    put(data, 405056939269888281u64, 8957268500971669618u64, 2070940926u64)\n    put(data, 12485440679452408690u64, 7645679094277669412u64, 254485574498u64)\n    put(data, 8394369900823444407u64, 3821107497040617191u64, 98414473094u64)\n    put(data, 2877421667354294258u64, 8847137191985934072u64, 134207142652u64)\n    put(data, 2676980714750756239u64, 3531126524756088253u64, 252479604268u64)\n    put(data, 6119309228579057021u64, 8726915034124352183u64, 44191422752u64)\n    put(data, 18203256146533333852u64, 17611136727168068641u64, 32473087011u64)\n    put(data, 351919978865493747u64, 18017743272784259949u64, 35954701634u64)\n    put(data, 5190010931882390570u64, 18113575006829616116u64, 66976743819u64)\n    put(data, 6982466386088036604u64, 12805550441678740368u64, 139981938868u64)\n    put(data, 4707293888784996898u64, 8061966093393027781u64, 180694190280u64)\n    put(data, 690306801165964760u64, 11954593141554100801u64, 200437040057u64)\n    put(data, 12456770961278956704u64, 14068656112359197734u64, 185648059792u64)\n    put(data, 16946092489294063943u64, 895878255770467290u64, 144762663376u64)\n    put(data, 11098404173866185376u64, 10319906489512197802u64, 208048565657u64)\n    put(data, 15152070965853306880u64, 14551142616794302079u64, 153559443251u64)\n    put(data, 17370091362040414208u64, 15933181735739307476u64, 51788819021u64)\n    put(data, 10141938552171134976u64, 11524527334398983147u64, 77863739512u64)\n    put(data, 10586988556645826560u64, 11828012606225556742u64, 120624745878u64)\n    put(data, 12169852093061922816u64, 3556238869349799431u64, 150641197848u64)\n    put(data, 16717361816799281152u64, 7403090230513381483u64, 24192784095u64)\n    put(data, 0u64, 10172292854665622800u64, 223401322325u64)\n    put(data, 0u64, 11240746576366182400u64, 85551441100u64)\n    put(data, 0u64, 17021927826892259328u64, 204609362092u64)\n    put(data, 0u64, 9046328496309141504u64, 172922760556u64)\n    put(data, 0u64, 8038996803112140800u64, 108490402450u64)\n    put(data, 0u64, 17098478935265509376u64, 146435794889u64)\n    put(data, 0u64, 7205759403792793600u64, 201926910400u64)\n    put(data, 0u64, 0u64, 192390625000u64)\n    put(data, 0u64, 0u64, 232000000000u64)\n    put(data, 2144184049294538808u64, 6u64, 0u64)\n    put(data, 4108002197393276873u64, 6116236450u64, 0u64)\n    put(data, 6446230217393892753u64, 6116236450222695245u64, 0u64)\n    put(data, 5571068025259989822u64, 6240972538554414168u64, 331561842u64)\n    put(data, 14804812668872528331u64, 4356262642990299018u64, 114338323799u64)\n    put(data, 17369928488562523047u64, 1335108558830511366u64, 87236153471u64)\n    put(data, 2967474173531035027u64, 18435704923261947246u64, 127072376379u64)\n    put(data, 5291425437992807716u64, 8395401931972636441u64, 59999401566u64)\n    put(data, 14219547193739388064u64, 12482665946362458347u64, 94455115650u64)\n    put(data, 17720313647158217462u64, 16101242875289374781u64, 130676686676u64)\n    put(data, 12334850628290578491u64, 4708983440241068127u64, 84872850125u64)\n    put(data, 7818499847417334620u64, 14856666972541426744u64, 205255274503u64)\n    put(data, 136007040922198372u64, 6938795288315789246u64, 7805381530u64)\n    put(data, 8523477092112604449u64, 5556307628265073820u64, 154376152846u64)\n    put(data, 367934822655966629u64, 1441404248927865979u64, 14301208040u64)\n    put(data, 12964987687054730050u64, 16710378912353838906u64, 232078138680u64)\n    put(data, 15267036012420885462u64, 18289940136919312110u64, 56905871455u64)\n    put(data, 11142900264750765568u64, 10217414145292657824u64, 95991499641u64)\n    put(data, 13680181547777718603u64, 12461165826430955753u64, 121553887130u64)\n    put(data, 13001922925761426065u64, 662762458988270879u64, 154675521153u64)\n    put(data, 2397730045956515935u64, 16488546856395302470u64, 129035928424u64)\n    put(data, 4482395522588406288u64, 2612816787977180950u64, 104893845916u64)\n    put(data, 3255525722490493080u64, 16446616379327454252u64, 156141641081u64)\n    put(data, 836222287193822098u64, 7842178508581740643u64, 121891572860u64)\n    put(data, 420898743993182306u64, 14779029861369369333u64, 124425125348u64)\n    put(data, 11652649973356574054u64, 2697664446153849542u64, 228801172814u64)\n    put(data, 15058402726661910231u64, 12135106444393649308u64, 78146240682u64)\n    put(data, 9600134495208339559u64, 9550285041205189839u64, 170657845438u64)\n    put(data, 14626343323989004842u64, 8790318168586740109u64, 190517721989u64)\n    put(data, 6813981265331086665u64, 14038474217155846828u64, 133476524102u64)\n    put(data, 10281745288790487888u64, 4263144264274812072u64, 70761027212u64)\n    put(data, 17569829347075761940u64, 11940456333341715520u64, 140231105513u64)\n    put(data, 7654580675237889478u64, 15751110736831573013u64, 233647293434u64)\n    put(data, 16194838649686212364u64, 18384528705472318081u64, 250853869423u64)\n    put(data, 6495102772252453635u64, 2393654818032310394u64, 111996627298u64)\n    put(data, 14935159852819761348u64, 12812209822018626434u64, 98129760287u64)\n    put(data, 17381879863441579697u64, 3110778569433458461u64, 31694551286u64)\n    put(data, 9062335510435372583u64, 2860264756226872891u64, 246168635644u64)\n    put(data, 7569219009130126626u64, 2384146980060315184u64, 252155055263u64)\n    put(data, 12652124168176193362u64, 14117430062880324728u64, 159129244866u64)\n    put(data, 8940200224697247767u64, 3769610173216737153u64, 194765307417u64)\n    put(data, 5600570701927432884u64, 17731974340232672009u64, 25204350976u64)\n    put(data, 2018432801986093157u64, 1971479303384713466u64, 961252255u64)\n    put(data, 8235849749361824736u64, 3449462959779012549u64, 159106874107u64)\n    put(data, 9705097287982370040u64, 13743454852043766533u64, 251186995761u64)\n    put(data, 3517483139049842585u64, 7417711187131879498u64, 49745034180u64)\n    put(data, 958281614186777760u64, 3650992383501007879u64, 196402114929u64)\n    put(data, 7336473432636108950u64, 12838770342493958662u64, 113197920693u64)\n    put(data, 12955383920176764423u64, 16025068246546338477u64, 181695991134u64)\n    put(data, 10735469126281273789u64, 6579965938260177729u64, 94868720690u64)\n    put(data, 8637888232514730693u64, 4742939430174291459u64, 50356700668u64)\n    put(data, 6806336737533581000u64, 13062256857527449083u64, 252257115261u64)\n    put(data, 16142569672872330321u64, 2301174570202439645u64, 125708106363u64)\n    put(data, 8141285259947963513u64, 7638687886069412887u64, 123124746923u64)\n    put(data, 5220241098754220797u64, 936322449610274358u64, 171414094100u64)\n    put(data, 154438799943119608u64, 12926010544311283981u64, 20050758141u64)\n    put(data, 2226876628677628879u64, 12647854908989899184u64, 253700720435u64)\n    put(data, 17219557081221357482u64, 8862093163358513015u64, 51685641588u64)\n    put(data, 15401507148161015114u64, 444784343917630731u64, 116480415033u64)\n    put(data, 8842629766613985337u64, 11033952249213387263u64, 57024111807u64)\n    put(data, 3180100571546071440u64, 18168634046363183536u64, 191598151749u64)\n    put(data, 7740848704392475044u64, 3837904761417065597u64, 69984923625u64)\n    put(data, 2014314126623495998u64, 111459007020906105u64, 233208053234u64)\n    put(data, 11209566016506885858u64, 16191761957496794523u64, 242006042204u64)\n    put(data, 7117166613733441125u64, 9856250800340378607u64, 92877757174u64)\n    put(data, 4197646860931880328u64, 9491800102275105959u64, 246534308426u64)\n    put(data, 5487263271238026094u64, 10777328578953608268u64, 74514551514u64)\n    put(data, 18148076225293562697u64, 17424440628313779505u64, 218584240152u64)\n    put(data, 9127276943027950849u64, 3285814872419755679u64, 24944580819u64)\n    put(data, 9691696125379324722u64, 2824823424107240978u64, 211178124381u64)\n    put(data, 13102362262487705216u64, 12271707680713669755u64, 93153133984u64)\n    put(data, 8929385439893192704u64, 6951481875178001185u64, 160665250606u64)\n    put(data, 11891353410743566336u64, 10202522487003824362u64, 46376840587u64)\n    put(data, 1587423090877399040u64, 4834668463880990728u64, 139553079852u64)\n    put(data, 3489137423026225152u64, 10871520987687904746u64, 44262087902u64)\n    put(data, 13046928120492326912u64, 12057698794225322390u64, 222589346333u64)\n    put(data, 11529215046068469760u64, 7263351819222681214u64, 29653649161u64)\n    put(data, 0u64, 1778055686910650944u64, 9393747091u64)\n    put(data, 0u64, 17108187120491986944u64, 147096388591u64)\n    put(data, 0u64, 3067636961549221888u64, 239927436682u64)\n    put(data, 0u64, 16702141595163557888u64, 138166296932u64)\n    put(data, 0u64, 2432053749942845440u64, 100905424910u64)\n    put(data, 0u64, 17791470327927144448u64, 14131841897u64)\n    put(data, 0u64, 1152921504606846976u64, 105964477539u64)\n    put(data, 0u64, 0u64, 99062500000u64)\n    put(data, 0u64, 0u64, 160000000000u64)\n    put(data, 6674960280855494694u64, 93326u64, 0u64)\n    put(data, 16378845781483497510u64, 93326361850321u64, 0u64)\n    put(data, 17606907750956804392u64, 4283581425266273664u64, 5059u64)\n    put(data, 13225609159240506969u64, 6725911039793895357u64, 195232213414u64)\n    put(data, 2668084873338435252u64, 1188689198788975021u64, 166364612368u64)\n    put(data, 14802814305275861366u64, 10825527435847761650u64, 16064438970u64)\n    put(data, 8005510553372365574u64, 3917696829526085083u64, 186586853018u64)\n    put(data, 12748500143273514429u64, 12646861173976387276u64, 154212378770u64)\n    put(data, 10393733905569036127u64, 18398576063183996905u64, 146685587717u64)\n    put(data, 603389089974790339u64, 16919251228485834948u64, 5997388806u64)\n    put(data, 2033494532597735873u64, 17296019588687185052u64, 6917194446u64)\n    put(data, 9204796763694620958u64, 12365301604512770359u64, 206937619100u64)\n    put(data, 7826260310402107021u64, 2814271599679204744u64, 156670324343u64)\n    put(data, 10122690201685169383u64, 2154994415780170517u64, 119152561969u64)\n    put(data, 7438455564568110133u64, 6717373824370072839u64, 49116822481u64)\n    put(data, 2805412574380520817u64, 12709155755801344060u64, 209364149564u64)\n    put(data, 7250965427231182867u64, 826847911966403896u64, 60688964714u64)\n    put(data, 8136242944826085924u64, 2277322703890025383u64, 106044823515u64)\n    put(data, 15357191647956011780u64, 2774508958389496437u64, 219123453911u64)\n    put(data, 7369614426695395460u64, 245697774950120915u64, 215150406432u64)\n    put(data, 10886957545142526638u64, 1268929063431863950u64, 32013319303u64)\n    put(data, 2030047207417538097u64, 6735665673159411439u64, 135068788782u64)\n    put(data, 11557093828502314355u64, 14734771742997073207u64, 46365141167u64)\n    put(data, 15201062539664128543u64, 13683287077957612495u64, 175798773576u64)\n    put(data, 8846936323343880674u64, 15370263741354826803u64, 72741772478u64)\n    put(data, 1485291750116245364u64, 48035913070297507u64, 190833223667u64)\n    put(data, 2076024439668322013u64, 1206547475966802115u64, 243002604032u64)\n    put(data, 7029497773682748741u64, 13512340386605768006u64, 65407069u64)\n    put(data, 16333533921668749881u64, 2325760467700278797u64, 93732505440u64)\n    put(data, 2065057316131928423u64, 10848110652847753816u64, 96126079727u64)\n    put(data, 7800502648925570223u64, 15846378960784301285u64, 239588077256u64)\n    put(data, 17011619967093802015u64, 14121839924449844911u64, 200859033924u64)\n    put(data, 5368819344429198672u64, 5147613424753296550u64, 68765546476u64)\n    put(data, 15598879366754275267u64, 16817040482828810167u64, 236279052682u64)\n    put(data, 16393893486035835647u64, 5773528746119363888u64, 138911653591u64)\n    put(data, 12042046205096920307u64, 8716201595536184826u64, 215312983620u64)\n    put(data, 15437070428008474344u64, 5259122109038474872u64, 68472506235u64)\n    put(data, 13704569163204647509u64, 14744540084230155984u64, 123285097580u64)\n    put(data, 18192483750856993350u64, 10719345477982635878u64, 108799303119u64)\n    put(data, 5152535865317963250u64, 13698037261310555208u64, 207581096882u64)\n    put(data, 17512614083933854680u64, 16141171632951976936u64, 178742572087u64)\n    put(data, 14481918350603613536u64, 10060790174955808839u64, 55875014667u64)\n    put(data, 16124419709964004915u64, 4250043307981877118u64, 11545396528u64)\n    put(data, 18088011566435813579u64, 7075646198054337199u64, 48230395309u64)\n    put(data, 15008862380698848893u64, 18141738384245531503u64, 173383571548u64)\n    put(data, 18160498644611827812u64, 8174370508376809531u64, 92983465608u64)\n    put(data, 3604680497457231965u64, 3581964982731575596u64, 136443133513u64)\n    put(data, 5957615565551495921u64, 14798509948722114761u64, 73194178710u64)\n    put(data, 17528455034961565995u64, 14713923334885122090u64, 150802228831u64)\n    put(data, 8503772325120113809u64, 5042978054260414139u64, 95797643382u64)\n    put(data, 8501492578048509537u64, 2052996319372883413u64, 118273380388u64)\n    put(data, 2296237701094386060u64, 8825683007899981588u64, 36111293153u64)\n    put(data, 3753593040591076946u64, 9992196755378745151u64, 225478441234u64)\n    put(data, 8518075399775653155u64, 9301073417573669950u64, 18541678071u64)\n    put(data, 12757855675959554597u64, 5331614769144850592u64, 247504212200u64)\n    put(data, 121631169379748595u64, 14354009428310052102u64, 232289027415u64)\n    put(data, 16679062494579173314u64, 5581221063029119078u64, 87778132410u64)\n    put(data, 10739912744743898054u64, 1529260335339476189u64, 186302558600u64)\n    put(data, 9367340677776287570u64, 16483061525949201148u64, 136082901368u64)\n    put(data, 12227321512794715397u64, 14431217812333089675u64, 120893548555u64)\n    put(data, 7241061891859170651u64, 3452349151135392267u64, 11782317885u64)\n    put(data, 13148571323079237489u64, 9075317899834447999u64, 61187152222u64)\n    put(data, 12509763434355012654u64, 2764331337978901575u64, 94491973969u64)\n    put(data, 11812768946960181977u64, 1942890683708857202u64, 81149854702u64)\n    put(data, 14170358803552564832u64, 165089169728028447u64, 238105324315u64)\n    put(data, 18179989524780635952u64, 15193620741871233073u64, 27008949501u64)\n    put(data, 17091718978514754901u64, 14995000835194145926u64, 253823647830u64)\n    put(data, 7394768384359232459u64, 1788823614552255558u64, 86812880624u64)\n    put(data, 6778628272692852803u64, 8384901184618498845u64, 240096972322u64)\n    put(data, 18193335045875234320u64, 405511217862281310u64, 34454546404u64)\n    put(data, 1378519212560967521u64, 3111530463755196557u64, 228021982807u64)\n    put(data, 4677732610631043584u64, 7893558450035460812u64, 87168676404u64)\n    put(data, 17296098591070486528u64, 156573858237402216u64, 52427910661u64)\n    put(data, 7343735382392963072u64, 15915324019419451223u64, 5008487885u64)\n    put(data, 14525996728454217728u64, 16293363012778802804u64, 205862771443u64)\n    put(data, 9691359370008330240u64, 14342105318291351412u64, 243883264978u64)\n    put(data, 3044433348102455296u64, 3788398842525387052u64, 210777487087u64)\n    put(data, 9223372036854775808u64, 14118764407048307670u64, 239205369512u64)\n    put(data, 0u64, 2705021334614720768u64, 168765379752u64)\n    put(data, 0u64, 7017988973805568000u64, 168146639500u64)\n    put(data, 0u64, 10956732053634154496u64, 140380445944u64)\n    put(data, 0u64, 14657517938546835456u64, 248593965634u64)\n    put(data, 0u64, 11268868284797157376u64, 66794585639u64)\n    put(data, 0u64, 14600669991935148032u64, 39610886573u64)\n    put(data, 0u64, 4611686018427387904u64, 173791503906u64)\n    put(data, 0u64, 0u64, 34250000000u64)\n    put(data, 0u64, 0u64, 128000000000u64)\n    put(data, 8201586317771250746u64, 1424047269u64, 0u64)\n    put(data, 3278889188817135834u64, 1424047269444608885u64, 0u64)\n    put(data, 1710725240251040430u64, 3001188830946823627u64, 77197757u64)\n    put(data, 1850175733663425006u64, 9732296932705387049u64, 189162694772u64)\n    put(data, 9147599666163914249u64, 16337535782679529459u64, 116527588873u64)\n    put(data, 10221885933644344166u64, 7969742269895046547u64, 9885659589u64)\n    put(data, 14901479793736678101u64, 2923592083903829642u64, 197432040594u64)\n    put(data, 5181831442059703136u64, 8144196241160608534u64, 146158488244u64)\n    put(data, 6396246577759793483u64, 16431078457793424253u64, 180441497762u64)\n    put(data, 14167229556464870447u64, 202362949592775653u64, 162890730548u64)\n    put(data, 2969982933326311854u64, 8835125248522947981u64, 52010970117u64)\n    put(data, 7892677766222018881u64, 7959873808777345113u64, 5478953099u64)\n    put(data, 798698968922663621u64, 14929747122315126151u64, 139431505623u64)\n    put(data, 15926812109043458972u64, 4310328817360515349u64, 215809343213u64)\n    put(data, 8663842590352697437u64, 7294899422760201126u64, 237233663393u64)\n    put(data, 17093523026636671168u64, 2047461597291187207u64, 161395457290u64)\n    put(data, 839764004742743203u64, 10942374468813517900u64, 10110993115u64)\n    put(data, 16894643909298232323u64, 10364795403063433969u64, 219593187308u64)\n    put(data, 9066702926218949317u64, 12330859528790939137u64, 236561876684u64)\n    put(data, 9119392417260546810u64, 8973160144879916806u64, 204668457234u64)\n    put(data, 9723021096578315109u64, 2895354388547509877u64, 18486435986u64)\n    put(data, 14787464248751217597u64, 16766844772497556429u64, 146156957475u64)\n    put(data, 3733434565920249133u64, 7442407174620948827u64, 35908932476u64)\n    put(data, 6643788868836820841u64, 6683013428676659077u64, 124403453701u64)\n    put(data, 4729646697422664063u64, 16713703375071907588u64, 5362286883u64)\n    put(data, 4090144564201555829u64, 8791044883080637861u64, 35906051675u64)\n    put(data, 2109480737093400002u64, 602844107089214413u64, 91476563498u64)\n    put(data, 16577155033369419739u64, 9754832281172880875u64, 42032680244u64)\n    put(data, 745377248603805917u64, 10587846778003503903u64, 52528810517u64)\n    put(data, 11305561465807999667u64, 17206244172922947013u64, 21573968323u64)\n    put(data, 2211245518782892177u64, 11620628420699303875u64, 195932752365u64)\n    put(data, 14170095199249735666u64, 17864732368219338611u64, 237629955528u64)\n    put(data, 17849973668116118927u64, 4146383014621345887u64, 200968449082u64)\n    put(data, 9020960204585720001u64, 11445705075042688243u64, 58224775873u64)\n    put(data, 10807134002871850916u64, 7369147888966546592u64, 193620472915u64)\n    put(data, 3925122626254791201u64, 9762476865090597796u64, 83399482307u64)\n    put(data, 17208463291312718997u64, 5507001428194242827u64, 195529224931u64)\n    put(data, 5145077219589447653u64, 11371471148365328344u64, 227298535145u64)\n    put(data, 17602397765035489468u64, 3148788104946538618u64, 233616448686u64)\n    put(data, 16422643262490753377u64, 3762722308424507574u64, 174170696145u64)\n    put(data, 2902509461400906224u64, 1156171244825745915u64, 209203977585u64)\n    put(data, 3422418805967265206u64, 14208921674868257865u64, 113062676168u64)\n    put(data, 4228874576277237392u64, 7903080886897905503u64, 200770267187u64)\n    put(data, 2553488530807495751u64, 6367240794154270982u64, 51428426873u64)\n    put(data, 11546099176912486413u64, 1623672396662369850u64, 121345168815u64)\n    put(data, 10460791037534167991u64, 18323231215381674394u64, 175088019456u64)\n    put(data, 8127117908566000904u64, 9842279843006544554u64, 993304354u64)\n    put(data, 11541304458088287306u64, 7376839231308610600u64, 34533551059u64)\n    put(data, 6249718665174839700u64, 609751749293657672u64, 211399899256u64)\n    put(data, 13102508413386290995u64, 10386457966860989799u64, 120033054708u64)\n    put(data, 6274675218640661911u64, 11160336020836149780u64, 244563051014u64)\n    put(data, 3404497118599817167u64, 17947559933847409193u64, 6605003027u64)\n    put(data, 11258566093988562335u64, 10229787001712704590u64, 19972939173u64)\n    put(data, 16762592482501635397u64, 10441677090043619866u64, 165554557864u64)\n    put(data, 5550125446725071998u64, 4996681336392922375u64, 168566044449u64)\n    put(data, 6370033225258510318u64, 124497102381021895u64, 33270870638u64)\n    put(data, 1503521728674735398u64, 8180812057779384577u64, 110006749001u64)\n    put(data, 4250415082606384364u64, 5294232873532946716u64, 73443482710u64)\n    put(data, 6020091901030562974u64, 2885620189169448039u64, 86287000939u64)\n    put(data, 16288222967151527138u64, 16662526875008170507u64, 107156429783u64)\n    put(data, 6377016228656203782u64, 15663095032402672480u64, 215903277391u64)\n    put(data, 8378856515587563750u64, 1824281504410546614u64, 79849098083u64)\n    put(data, 15812881490200838483u64, 9506565509584809953u64, 99098894498u64)\n    put(data, 4548570371183413652u64, 16941136942345070055u64, 162515351948u64)\n    put(data, 16731431495283420383u64, 15924115693705937725u64, 140918380873u64)\n    put(data, 14737727629551135532u64, 9247807690406628462u64, 73863248041u64)\n    put(data, 12413722258104293893u64, 7993916633864834871u64, 169501324659u64)\n    put(data, 800899742400762438u64, 1018504409177639408u64, 115433351089u64)\n    put(data, 603197008376033550u64, 12097800686634130718u64, 177055213234u64)\n    put(data, 6380777281587743935u64, 6221488888422637551u64, 178655823089u64)\n    put(data, 10001440249018225388u64, 8229322865256080421u64, 241337267588u64)\n    put(data, 5505914461980436708u64, 7927745108183101786u64, 132446112486u64)\n    put(data, 1105464290051876864u64, 8488683721235326653u64, 230429763923u64)\n    put(data, 4500443576769970176u64, 11165516518170922283u64, 83460172466u64)\n    put(data, 2843045143185981440u64, 5463648141113596927u64, 178605283863u64)\n    put(data, 660949699682893824u64, 3958440403860778042u64, 23296184959u64)\n    put(data, 276549164618219520u64, 5091534813990256011u64, 127214587484u64)\n    put(data, 4683743612465315840u64, 6100166970623291280u64, 92276012655u64)\n    put(data, 0u64, 1913011027739012426u64, 111330690714u64)\n    put(data, 0u64, 11310957650604221440u64, 154103704535u64)\n    put(data, 0u64, 16303817257009020928u64, 215613168242u64)\n    put(data, 0u64, 9090406322154766336u64, 114883831704u64)\n    put(data, 0u64, 3003279315069566976u64, 152492791914u64)\n    put(data, 0u64, 16582887146675765248u64, 106162808097u64)\n    put(data, 0u64, 9691746398101307392u64, 33898960113u64)\n    put(data, 0u64, 0u64, 241525390625u64)\n    put(data, 0u64, 0u64, 33000000000u64)\n    data\n  end\nend\n"
  },
  {
    "path": "src/float/printer.cr",
    "content": "require \"./printer/*\"\n\n# :nodoc:\nmodule Float::Printer\n  extend self\n\n  BUFFER_SIZE = 17 # maximum number of decimal digits required\n\n  # Writes *v*'s shortest string representation to the given *io*.\n  #\n  # Based on the [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm\n  # developed by Junekey Jeon around 2020-2021.\n  #\n  # It is used by `Float::Primitive#to_s` and `Number#format`. It is probably\n  # not necessary to use this directly.\n  #\n  # *point_range* designates the boundaries of scientific notation which is used\n  # for all values whose decimal point position is outside that range.\n  def shortest(v : Float::Primitive, io : IO, *, point_range = -3..15) : Nil\n    check_finite_float(v, io) do |pos_v|\n      if pos_v.zero?\n        io << \"0.0\"\n        return\n      end\n\n      significand, decimal_exponent = Dragonbox.to_decimal(pos_v)\n\n      # remove trailing zeros\n      while significand.unsafe_mod(10) == 0\n        significand = significand.unsafe_div(10)\n        decimal_exponent += 1\n      end\n\n      # generate `significand.to_s` in a reasonably fast manner\n      str = uninitialized UInt8[BUFFER_SIZE]\n      ptr = str.to_unsafe + BUFFER_SIZE\n      while significand > 0\n        ptr -= 1\n        ptr.value = 48_u8 &+ significand.unsafe_mod(10).to_u8!\n        significand = significand.unsafe_div(10)\n      end\n\n      buffer = str.to_slice[ptr - str.to_unsafe..]\n      decimal(io, buffer, decimal_exponent, point_range, :write_all)\n    end\n  end\n\n  # How to output the decimal part in `#decimal`.\n  enum FractionMode\n    # Writes the decimal separator and all digits that follow.\n    # Also writes `.0` if the decimal part is empty.\n    WriteAll\n\n    # If there are two or more digits, removes all consecutive trailing zeros,\n    # except that the digit past the decimal separator is always kept.\n    # `.100` becomes `.1`, and `.00` becomes `.0`.\n    # Also writes `.0` if the decimal part is empty.\n    RemoveExtraZeros\n\n    # If all the digits are zero, does not write the decimal separator nor the\n    # digits. Otherwise writes everything.\n    # `.100` becomes `.100`, and `.00` becomes the empty string.\n    RemoveIfZero\n  end\n\n  # The general printing algorithm for decimal numbers. Writes to the given *io*\n  # the value *digits*, interpreted as an ASCII numeric string, and then\n  # multiplied by 10 to the *decimal_exponent*-th power.\n  #\n  # *digits* must not be empty or contain any leading zeros or minus signs. It\n  # may however contain redundant trailing zeros.\n  #\n  # *point_range* designates the boundaries of scientific notation which is used\n  # for all values whose decimal point position is outside that range. The\n  # scientific notation can be unconditionally enabled or disabled by passing\n  # `0...0` or `..` to this parameter.\n  #\n  # *fraction* determines if and how the decimal separator and the decimal part\n  # are written. Refer to `FractionMode` for the options available.\n  def decimal(io : IO, digits : Bytes, decimal_exponent : Int, point_range : Range, fraction : FractionMode) : Nil\n    length = digits.size\n\n    exp = decimal_exponent + length\n    exp_mode = !point_range.includes?(exp)\n    point = exp_mode ? 1 : exp\n\n    # add integer part digits\n    if decimal_exponent > 0 && !exp_mode\n      # whole number but not big enough to be exp form\n      io.write_string digits\n      digits = Bytes.empty\n      decimal_exponent.times { io << '0' }\n    elsif point > 0\n      io.write_string digits[0, point]\n      digits = digits[point..]\n    else\n      # add leading zero\n      io << '0'\n    end\n\n    unless fraction.remove_if_zero? && digits.all?(&.=== '0')\n      io << '.'\n\n      # add leading zeros after point\n      (-point).times { io << '0' }\n\n      # add fractional part digits\n      digits = remove_extra_zeros(digits) if fraction.remove_extra_zeros?\n      io.write_string(digits)\n\n      # print trailing 0 if whole number or exp notation of power of ten\n      io << '0' if digits.empty?\n    end\n\n    # exp notation\n    if exp_mode\n      io << 'e'\n      io << '+' if exp > 0\n      (exp - 1).to_s(io)\n    end\n  end\n\n  private def remove_extra_zeros(digits : Bytes) : Bytes\n    return digits if digits.empty?\n    b = digits.to_unsafe\n    e = b + digits.size - 1\n    while e > b && e.value === '0'\n      e -= 1\n    end\n    Slice.new(b, e + 1 - b)\n  end\n\n  # Writes *v*'s hexadecimal-significand representation to the given *io*.\n  #\n  # Used by `Float::Primitive#to_hexfloat` and `String::Formatter#float_hex`.\n  def hexfloat(v : Float64, io : IO, **opts) : Nil\n    check_finite_float(v, io) do\n      Hexfloat(Float64, UInt64).to_s(io, v, **opts)\n    end\n  end\n\n  # :ditto:\n  def hexfloat(v : Float32, io : IO, **opts) : Nil\n    check_finite_float(v, io) do\n      Hexfloat(Float32, UInt32).to_s(io, v, **opts)\n    end\n  end\n\n  # If *v* is finite, yields its absolute value, otherwise writes *v* to *io*.\n  private def check_finite_float(v : Float::Primitive, io : IO, &)\n    if v.nan?\n      io << \"NaN\"\n      return\n    end\n\n    if v.sign_bit < 0\n      io << '-'\n      v = -v\n    end\n\n    if v.infinite?\n      io << \"Infinity\"\n    else\n      yield v\n    end\n  end\nend\n"
  },
  {
    "path": "src/float.cr",
    "content": "require \"c/stdio\"\nrequire \"c/string\"\nrequire \"./float/printer\"\n\n# Float is the base type of all floating point numbers.\n#\n# There are two floating point types, `Float32` and `Float64`,\n# which correspond to the [binary32](http://en.wikipedia.org/wiki/Single_precision_floating-point_format)\n# and [binary64](http://en.wikipedia.org/wiki/Double_precision_floating-point_format)\n# types defined by IEEE.\n#\n# A floating point literal is an optional `+` or `-` sign, followed by\n# a sequence of numbers or underscores, followed by a dot,\n# followed by numbers or underscores, followed by an optional exponent suffix,\n# followed by an optional type suffix. If no suffix is present, the literal's type is `Float64`.\n#\n# ```\n# 1.0     # Float64\n# 1.0_f32 # Float32\n# 1_f32   # Float32\n#\n# 1e10   # Float64\n# 1.5e10 # Float64\n# 1.5e-7 # Float64\n#\n# +1.3 # Float64\n# -0.5 # Float64\n# ```\n#\n# The underscore `_` before the suffix is optional.\n#\n# Underscores can be used to make some numbers more readable:\n#\n# ```\n# 1_000_000.111_111 # better than 1000000.111111\n# ```\n#\n# See [`Float` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/floats.html) in the language reference.\nstruct Float\n  alias Primitive = Float32 | Float64\n\n  # Negates this value's sign.\n  def -\n    # fallback implementation; does not handle IEEE 754 signed zeros correctly\n    self.class.zero - self\n  end\n\n  def //(other)\n    self.fdiv(other).floor\n  end\n\n  def %(other)\n    modulo(other)\n  end\n\n  # Returns whether this value is a not-a-number.\n  #\n  # This includes both quiet and signalling NaNs from IEEE 754.\n  def nan? : Bool\n    !(self == self)\n  end\n\n  # Checks whether this value is infinite. Returns `1` if this value is positive\n  # infinity, `-1` if this value is negative infinity, or `nil` otherwise.\n  def infinite? : Int32?\n    # fallback implementation\n    # TODO: consider using https://llvm.org/docs/LangRef.html#llvm-is-fpclass-intrinsic\n    if nan? || self == 0 || self != 2 * self\n      nil\n    else\n      self > 0 ? 1 : -1\n    end\n  end\n\n  # Returns whether this value is finite, i.e. it is neither infinite nor a\n  # not-a-number.\n  def finite? : Bool\n    !nan? && !infinite?\n  end\n\n  def modulo(other)\n    if other == 0.0\n      raise DivisionByZeroError.new\n    else\n      self - other * (self // other)\n    end\n  end\n\n  def remainder(other)\n    if other == 0.0\n      raise DivisionByZeroError.new\n    else\n      mod = self % other\n      return self.class.zero if mod == 0.0\n      return mod if self > 0 && other > 0\n      return mod if self < 0 && other < 0\n\n      mod - other\n    end\n  end\n\n  # Writes this float to the given *io* in the given *format*.\n  # See also: `IO#write_bytes`.\n  def to_io(io : IO, format : IO::ByteFormat) : Nil\n    format.encode(self, io)\n  end\n\n  # Reads a float from the given *io* in the given *format*.\n  # See also: `IO#read_bytes`.\n  def self.from_io(io : IO, format : IO::ByteFormat) : self\n    format.decode(self, io)\n  end\nend\n\nstruct Float32\n  NAN      = (0_f32 / 0_f32).as Float32\n  INFINITY = (1_f32 / 0_f32).as Float32\n  # Smallest finite value\n  MIN = -3.40282347e+38_f32\n  # Largest finite value\n  MAX = 3.40282347e+38_f32\n  # The machine epsilon (difference between 1.0 and the next representable value)\n  EPSILON = 1.19209290e-07_f32\n  # The number of decimal digits that can be represented without losing precision\n  DIGITS = 6\n  # The radix or integer base used by the internal representation\n  RADIX = 2\n  # The number of digits that can be represented without losing precision (in base RADIX)\n  MANT_DIGITS = 24\n  # The minimum possible normal power of 2 exponent\n  MIN_EXP = -125\n  # The maximum possible normal power of 2 exponent\n  MAX_EXP = 128\n  # The minimum possible power of 10 exponent (such that 10**MIN_10_EXP is representable)\n  MIN_10_EXP = -37\n  # The maximum possible power of 10 exponent (such that 10**MAX_10_EXP is representable)\n  MAX_10_EXP = 38\n  # Smallest normal positive value, whose previous representable value is subnormal\n  MIN_POSITIVE = 1.17549435e-38_f32\n  # Smallest representable positive value, whose previous representable value is zero\n  MIN_SUBNORMAL = 1.0e-45_f32\n\n  # Returns a `Float32` by invoking `String#to_f32` on *value*.\n  #\n  # ```\n  # Float32.new \"20\"                        # => 20.0\n  # Float32.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Float32: \"  20  \"\n  # ```\n  def self.new(value : String, whitespace : Bool = true, strict : Bool = true) : self\n    value.to_f32 whitespace: whitespace, strict: strict\n  end\n\n  # Returns a `Float32` by invoking `to_f32` on *value*.\n  def self.new(value)\n    value.to_f32\n  end\n\n  # Returns a `Float32` by invoking `to_f32!` on *value*.\n  def self.new!(value) : self\n    value.to_f32!\n  end\n\n  # Returns a `Float32` by parsing *str* as a hexadecimal-significand string, or\n  # `nil` if parsing fails.\n  #\n  # The string format is defined in section 5.12.3 of IEEE 754-2008, and is the\n  # same as the `%a` specifier for `sprintf`. Unlike e.g. `String#to_f`,\n  # whitespace and underscores are not allowed. Non-finite values are also\n  # recognized.\n  #\n  # ```\n  # Float32.parse_hexfloat?(\"0x123.456p7\")     # => 37282.6875_f32\n  # Float32.parse_hexfloat?(\"0x1.fffffep+127\") # => Float32::MAX\n  # Float32.parse_hexfloat?(\"-inf\")            # => -Float32::INFINITY\n  # Float32.parse_hexfloat?(\"0x1\")             # => nil\n  # Float32.parse_hexfloat?(\"a.bp+3\")          # => nil\n  # ```\n  def self.parse_hexfloat?(str : String) : self?\n    Float::Printer::Hexfloat(self, UInt32).to_f(str) { nil }\n  end\n\n  # Returns a `Float32` by parsing *str* as a hexadecimal-significand string.\n  #\n  # The string format is defined in section 5.12.3 of IEEE 754-2008, and is the\n  # same as the `%a` specifier for `sprintf`. Unlike e.g. `String#to_f`,\n  # whitespace and underscores are not allowed. Non-finite values are also\n  # recognized.\n  #\n  # Raises `ArgumentError` if *str* is not a valid hexadecimal-significand\n  # string.\n  #\n  # ```\n  # Float32.parse_hexfloat(\"0x123.456p7\")     # => 37282.6875_f32\n  # Float32.parse_hexfloat(\"0x1.fffffep+127\") # => Float32::MAX\n  # Float32.parse_hexfloat(\"-inf\")            # => -Float32::INFINITY\n  # Float32.parse_hexfloat(\"0x1\")             # Invalid hexfloat: expected 'p' or 'P' (ArgumentError)\n  # Float32.parse_hexfloat(\"a.bp+3\")          # Invalid hexfloat: expected '0' (ArgumentError)\n  # ```\n  def self.parse_hexfloat(str : String) : self\n    Float::Printer::Hexfloat(self, UInt32).to_f(str) do |err|\n      raise ArgumentError.new(\"Invalid hexfloat: #{err}\")\n    end\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float32\n  Number.expand_div [Float64], Float64\n\n  # :inherit:\n  def infinite? : Int32?\n    # TODO: consider using https://llvm.org/docs/LangRef.html#llvm-is-fpclass-intrinsic\n    case self\n    when -Float32::INFINITY\n      -1\n    when Float32::INFINITY\n      1\n    end\n  end\n\n  # Negates this value's sign.\n  #\n  # Works on signed zeros and not-a-number values as well. The negation of\n  # `0.0_f32` is `-0.0_f32`, and vice versa. The negation of a not-a-number\n  # value is only observable via `#sign_bit`.\n  def - : Float32\n    # equivalent to `Math.copysign(self, -sign_bit.to_f32)`\n    # TODO: consider using the LLVM `fneg` instruction\n    (unsafe_as(UInt32) ^ 0x80000000_u32).unsafe_as(Float32)\n  end\n\n  def abs : Float32\n    Math.copysign(self, 1.0_f32)\n  end\n\n  # Returns `-1` if the sign bit of this float is set, `1` otherwise.\n  #\n  # Unlike `#sign`, this works on signed zeros and not-a-numbers as well.\n  def sign_bit : Int32\n    Math.copysign(1_f32, self).to_i\n  end\n\n  # Rounds towards positive infinity.\n  def ceil : Float32\n    LibM.ceil_f32(self)\n  end\n\n  # Rounds towards negative infinity.\n  def floor : Float32\n    LibM.floor_f32(self)\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds towards the even neighbor (Banker's rounding).\n  def round_even : Float32\n    # TODO: LLVM 11 introduced llvm.roundeven.* intrinsics which may replace\n    # rint in the future.\n    LibM.rint_f32(self)\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds away from zero.\n  def round_away : Float32\n    LibM.round_f32(self)\n  end\n\n  # Rounds towards zero.\n  def trunc : Float32\n    LibM.trunc_f32(self)\n  end\n\n  # Returns the least `Float32` that is greater than `self`.\n  def next_float : Float32\n    LibM.nextafter_f32(self, INFINITY)\n  end\n\n  # Returns the greatest `Float32` that is less than `self`.\n  def prev_float : Float32\n    LibM.nextafter_f32(self, -INFINITY)\n  end\n\n  def **(other : Int32)\n    {% if flag?(:win32) %}\n      self ** other.to_f32\n    {% else %}\n      LibM.powi_f32(self, other)\n    {% end %}\n  end\n\n  def **(other : Float32) : Float32\n    LibM.pow_f32(self, other)\n  end\n\n  def **(other) : Float32\n    self ** other.to_f32\n  end\n\n  def to_s : String\n    String.build(22) do |buffer|\n      Printer.shortest(self, buffer)\n    end\n  end\n\n  def to_s(io : IO) : Nil\n    Printer.shortest(self, io)\n  end\n\n  # Returns the hexadecimal-significand representation of `self`.\n  #\n  # The string format is defined in section 5.12.3 of IEEE 754-2008, and is the\n  # same as the `%a` specifier for `sprintf`. The integral part of the returned\n  # string is `0` if `self` is subnormal, otherwise `1`. The fractional part\n  # contains only significant digits.\n  #\n  # ```\n  # 1234.0625_f32.to_hexfloat          # => \"0x1.3484p+10\"\n  # Float32::MAX.to_hexfloat           # => \"0x1.fffffep+127\"\n  # Float32::MIN_SUBNORMAL.to_hexfloat # => \"0x0.000002p-126\"\n  # ```\n  def to_hexfloat : String\n    # the longest `Float64` strings are of the form `-0x1.234567p-127`\n    String.build(16) do |buffer|\n      Printer.hexfloat(self, buffer)\n    end\n  end\n\n  # Writes `self`'s hexadecimal-significand representation to the given *io*.\n  def to_hexfloat(io : IO) : Nil\n    Printer.hexfloat(self, io)\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct Float64\n  NAN      = (0_f64 / 0_f64).as Float64\n  INFINITY = (1_f64 / 0_f64).as Float64\n\n  # Smallest finite value\n  MIN = -1.7976931348623157e+308_f64\n  # Largest finite value\n  MAX = 1.7976931348623157e+308_f64\n  # The machine epsilon (difference between 1.0 and the next representable value)\n  EPSILON = 2.2204460492503131e-16_f64\n  # The number of decimal digits that can be represented without losing precision\n  DIGITS = 15\n  # The radix or integer base used by the internal representation\n  RADIX = 2\n  # The number of digits that can be represented without losing precision (in base RADIX)\n  MANT_DIGITS = 53\n  # The minimum possible normal power of 2 exponent\n  MIN_EXP = -1021\n  # The maximum possible normal power of 2 exponent\n  MAX_EXP = 1024\n  # The minimum possible power of 10 exponent (such that 10**MIN_10_EXP is representable)\n  MIN_10_EXP = -307\n  # The maximum possible power of 10 exponent (such that 10**MAX_10_EXP is representable)\n  MAX_10_EXP = 308\n  # Smallest normal positive value, whose previous representable value is subnormal\n  MIN_POSITIVE = 2.2250738585072014e-308_f64\n  # Smallest representable positive value, whose previous representable value is zero\n  MIN_SUBNORMAL = 5.0e-324_f64\n\n  # Returns a `Float64` by invoking `String#to_f64` on *value*.\n  #\n  # ```\n  # Float64.new \"20\"                        # => 20.0\n  # Float64.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Float64: \"  20  \"\n  # ```\n  def self.new(value : String, whitespace : Bool = true, strict : Bool = true) : self\n    value.to_f64 whitespace: whitespace, strict: strict\n  end\n\n  # Returns a `Float64` by invoking `to_f64` on *value*.\n  def Float64.new(value)\n    value.to_f64\n  end\n\n  # Returns a `Float64` by invoking `to_f64!` on *value*.\n  def Float64.new!(value) : Float64\n    value.to_f64!\n  end\n\n  # Returns a `Float32` by parsing *str* as a hexadecimal-significand string, or\n  # `nil` if parsing fails.\n  #\n  # The string format is defined in section 5.12.3 of IEEE 754-2008, and is the\n  # same as the `%a` specifier for `sprintf`. Unlike e.g. `String#to_f`,\n  # whitespace and underscores are not allowed. Non-finite values are also\n  # recognized.\n  #\n  # ```\n  # Float64.parse_hexfloat?(\"0x123.456p7\")     # => 37282.6875\n  # Float64.parse_hexfloat?(\"0x1.fffffep+127\") # => Float64::MAX\n  # Float64.parse_hexfloat?(\"-inf\")            # => -Float64::INFINITY\n  # Float64.parse_hexfloat?(\"0x1\")             # => nil\n  # Float64.parse_hexfloat?(\"a.bp+3\")          # => nil\n  # ```\n  def self.parse_hexfloat?(str : String) : self?\n    Float::Printer::Hexfloat(self, UInt64).to_f(str) { nil }\n  end\n\n  # Returns a `Float32` by parsing *str* as a hexadecimal-significand string.\n  #\n  # The string format is defined in section 5.12.3 of IEEE 754-2008, and is the\n  # same as the `%a` specifier for `sprintf`. Unlike e.g. `String#to_f`,\n  # whitespace and underscores are not allowed. Non-finite values are also\n  # recognized.\n  #\n  # Raises `ArgumentError` if *str* is not a valid hexadecimal-significand\n  # string.\n  #\n  # ```\n  # Float64.parse_hexfloat(\"0x123.456p7\")             # => 37282.6875\n  # Float64.parse_hexfloat(\"0x1.fffffffffffffp+1023\") # => Float64::MAX\n  # Float64.parse_hexfloat(\"-inf\")                    # => -Float64::INFINITY\n  # Float64.parse_hexfloat(\"0x1\")                     # Invalid hexfloat: expected 'p' or 'P' (ArgumentError)\n  # Float64.parse_hexfloat(\"a.bp+3\")                  # Invalid hexfloat: expected '0' (ArgumentError)\n  # ```\n  def self.parse_hexfloat(str : String) : self\n    Float::Printer::Hexfloat(self, UInt64).to_f(str) do |err|\n      raise ArgumentError.new(\"Invalid hexfloat: #{err}\")\n    end\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float64\n\n  # :inherit:\n  def infinite? : Int32?\n    # TODO: consider using https://llvm.org/docs/LangRef.html#llvm-is-fpclass-intrinsic\n    case self\n    when -Float64::INFINITY\n      -1\n    when Float64::INFINITY\n      1\n    end\n  end\n\n  # Negates this value's sign.\n  #\n  # Works on signed zeros and not-a-number values as well. The negation of\n  # `0.0` is `-0.0`, and vice versa. The negation of a not-a-number value is\n  # only observable via `#sign_bit`.\n  def - : Float64\n    # equivalent to `Math.copysign(self, -sign_bit.to_f64)`\n    # TODO: consider using the LLVM `fneg` instruction\n    (unsafe_as(UInt64) ^ 0x80000000_00000000_u64).unsafe_as(Float64)\n  end\n\n  def abs : Float64\n    Math.copysign(self, 1.0)\n  end\n\n  # Returns `-1` if the sign bit of this float is set, `1` otherwise.\n  #\n  # Unlike `#sign`, this works on signed zeros and not-a-numbers as well.\n  def sign_bit : Int32\n    Math.copysign(1_f64, self).to_i\n  end\n\n  def ceil : Float64\n    LibM.ceil_f64(self)\n  end\n\n  def floor : Float64\n    LibM.floor_f64(self)\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds towards the even neighbor (Banker's rounding).\n  def round_even : Float64\n    # TODO: LLVM 11 introduced llvm.roundeven.* intrinsics which may replace\n    # rint in the future.\n    LibM.rint_f64(self)\n  end\n\n  # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n  # rounds away from zero.\n  def round_away : Float64\n    LibM.round_f64(self)\n  end\n\n  def trunc : Float64\n    LibM.trunc_f64(self)\n  end\n\n  # Returns the least `Float64` that is greater than `self`.\n  def next_float : Float64\n    LibM.nextafter_f64(self, INFINITY)\n  end\n\n  # Returns the greatest `Float64` that is less than `self`.\n  def prev_float : Float64\n    LibM.nextafter_f64(self, -INFINITY)\n  end\n\n  def **(other : Int32)\n    {% if flag?(:win32) %}\n      self ** other.to_f64\n    {% else %}\n      LibM.powi_f64(self, other)\n    {% end %}\n  end\n\n  def **(other : Float64) : Float64\n    LibM.pow_f64(self, other)\n  end\n\n  def **(other) : Float64\n    self ** other.to_f64\n  end\n\n  def to_s : String\n    # the longest `Float64` strings are of the form `-1.2345678901234567e+123`\n    String.build(24) do |buffer|\n      Printer.shortest(self, buffer)\n    end\n  end\n\n  def to_s(io : IO) : Nil\n    Printer.shortest(self, io)\n  end\n\n  # Returns the hexadecimal-significand representation of `self`.\n  #\n  # The string format is defined in section 5.12.3 of IEEE 754-2008, and is the\n  # same as the `%a` specifier for `sprintf`. The integral part of the returned\n  # string is `0` if `self` is subnormal, otherwise `1`. The fractional part\n  # contains only significant digits.\n  #\n  # ```\n  # 1234.0625.to_hexfloat              # => \"0x1.3484p+10\"\n  # Float64::MAX.to_hexfloat           # => \"0x1.fffffffffffffp+1023\"\n  # Float64::MIN_SUBNORMAL.to_hexfloat # => \"0x0.0000000000001p-1022\"\n  # ```\n  def to_hexfloat : String\n    # the longest `Float64` strings are of the form `-0x1.23456789abcdep-1023`\n    String.build(24) do |buffer|\n      Printer.hexfloat(self, buffer)\n    end\n  end\n\n  # Writes `self`'s hexadecimal-significand representation to the given *io*.\n  def to_hexfloat(io : IO) : Nil\n    Printer.hexfloat(self, io)\n  end\n\n  def clone\n    self\n  end\nend\n"
  },
  {
    "path": "src/gc/boehm.cr",
    "content": "{% if flag?(:preview_mt) %}\n  require \"crystal/rw_lock\"\n{% end %}\nrequire \"crystal/tracing\"\n\n# MUSL: On musl systems, libpthread is empty. The entire library is already included in libc.\n# The empty library is only available for POSIX compatibility. We don't need to link it.\n#\n# Darwin: `libpthread` is provided as part of `libsystem`. There's no reason to link it explicitly.\n#\n# Interpreter: Starting with glibc 2.34, `pthread` is integrated into `libc`\n# and may not even be available as a separate shared library.\n# There's always a static library for compiled mode, but `Crystal::Loader` does not support\n# static libraries. So we just skip `pthread` entirely. The symbols are still\n# available in the interpreter because they are loaded in the compiler.\n#\n# OTHERS: On other systems, we add the linker annotation here to make sure libpthread is loaded\n# before libgc which looks up symbols from libpthread.\n{% unless flag?(:win32) || flag?(:musl) || flag?(:darwin) || flag?(:android) || (flag?(:interpreted) && flag?(:gnu)) %}\n  @[Link(\"pthread\")]\n{% end %}\n\n{% if flag?(:freebsd) || flag?(:dragonfly) %}\n  {% if flag?(:interpreted) %}\n    # FIXME: We're not using the pkg-config name here because that would resolve the\n    # lib flags for libgc including `-lpthread` which the interpreter is not able\n    # to load on systems with modern libc where libpthread is only available as an\n    # (empty) static library.\n    @[Link(\"gc-threaded\")]\n  {% else %}\n    @[Link(\"gc-threaded\", pkg_config: \"bdw-gc-threaded\")]\n  {% end %}\n{% elsif flag?(:interpreted) %}\n  # FIXME: We're not using the pkg-config name here because that would resolve the\n  # lib flags for libgc including `-lpthread` which the interpreter is not able\n  # to load on systems with modern libc where libpthread is only available as an\n  # (empty) static library.\n  @[Link(\"gc\")]\n{% else %}\n  @[Link(\"gc\", pkg_config: \"bdw-gc\")]\n{% end %}\n\n# Supported library versions:\n#\n# * libgc (8.2.0+; earlier versions require a patch for MT support)\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#other-runtime-libraries\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  @[Link(dll: \"gc.dll\")]\n{% end %}\nlib LibGC\n  {% unless flag?(:win32) %}\n    {% pkg_config_name = ((ann = LibGC.annotations(Link).find(&.[\"pkg_config\"])) && ann[\"pkg_config\"]) || ((ann = LibGC.annotations(Link).find(&.[0])) && ann[0]) %}\n    VERSION = {{ `pkg-config #{pkg_config_name} --silence-errors --modversion || printf \"0.0.0\"`.chomp.stringify }}\n  {% end %}\n\n  alias Int = LibC::Int\n  alias SizeT = LibC::SizeT\n  {% if flag?(:win32) && flag?(:bits64) %}\n    alias Word = LibC::ULongLong\n    alias SignedWord = LibC::LongLong\n  {% else %}\n    alias Word = LibC::ULong\n    alias SignedWord = LibC::Long\n  {% end %}\n\n  struct StackBase\n    mem_base : Void*\n    # reg_base : Void* should be used also for IA-64 when/if supported\n  end\n\n  alias ThreadHandle = Void*\n\n  struct ProfStats\n    heap_size : Word\n    free_bytes : Word\n    unmapped_bytes : Word\n    bytes_since_gc : Word\n    bytes_before_gc : Word\n    non_gc_bytes : Word\n    gc_no : Word\n    markers_m1 : Word\n    bytes_reclaimed_since_gc : Word\n    reclaimed_bytes_before_gc : Word\n    expl_freed_bytes_since_gc : Word\n    obtained_from_os_bytes : Word\n  end\n\n  fun init = GC_init\n  fun malloc = GC_malloc(size : SizeT) : Void*\n  fun malloc_atomic = GC_malloc_atomic(size : SizeT) : Void*\n  fun realloc = GC_realloc(ptr : Void*, size : SizeT) : Void*\n  fun free = GC_free(ptr : Void*)\n  fun collect_a_little = GC_collect_a_little : Int\n  fun collect = GC_gcollect\n  fun add_roots = GC_add_roots(low : Void*, high : Void*)\n  fun enable = GC_enable\n  fun disable = GC_disable\n  fun is_disabled = GC_is_disabled : Int\n  fun set_handle_fork = GC_set_handle_fork(value : Int)\n\n  fun base = GC_base(displaced_pointer : Void*) : Void*\n  fun is_heap_ptr = GC_is_heap_ptr(pointer : Void*) : Int\n  fun general_register_disappearing_link = GC_general_register_disappearing_link(link : Void**, obj : Void*) : Int\n\n  alias Finalizer = Void*, Void* ->\n  fun register_finalizer = GC_register_finalizer(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**)\n  fun register_finalizer_ignore_self = GC_register_finalizer_ignore_self(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**)\n  fun invoke_finalizers = GC_invoke_finalizers : Int\n\n  fun get_heap_usage_safe = GC_get_heap_usage_safe(heap_size : Word*, free_bytes : Word*, unmapped_bytes : Word*, bytes_since_gc : Word*, total_bytes : Word*)\n  fun set_max_heap_size = GC_set_max_heap_size(Word)\n\n  fun get_prof_stats = GC_get_prof_stats(stats : ProfStats*, size : SizeT)\n\n  fun get_start_callback = GC_get_start_callback : Void*\n  fun set_start_callback = GC_set_start_callback(callback : ->)\n\n  fun set_push_other_roots = GC_set_push_other_roots(proc : ->)\n  fun get_push_other_roots = GC_get_push_other_roots : ->\n\n  fun push_all_eager = GC_push_all_eager(bottom : Void*, top : Void*)\n\n  {% if flag?(:preview_mt) || flag?(:win32) || compare_versions(VERSION, \"8.2.0\") >= 0 %}\n    fun get_my_stackbottom = GC_get_my_stackbottom(sb : StackBase*) : ThreadHandle\n    fun set_stackbottom = GC_set_stackbottom(th : ThreadHandle, sb : StackBase*) : ThreadHandle\n  {% else %}\n    $stackbottom = GC_stackbottom : Void*\n  {% end %}\n\n  alias OnHeapResizeProc = Word ->\n  fun set_on_heap_resize = GC_set_on_heap_resize(OnHeapResizeProc)\n  fun get_on_heap_resize = GC_get_on_heap_resize : OnHeapResizeProc\n\n  enum EventType\n    START # COLLECTION\n    MARK_START\n    MARK_END\n    RECLAIM_START\n    RECLAIM_END\n    END              # COLLECTION\n    PRE_STOP_WORLD   # STOPWORLD_BEGIN\n    POST_STOP_WORLD  # STOPWORLD_END\n    PRE_START_WORLD  # STARTWORLD_BEGIN\n    POST_START_WORLD # STARTWORLD_END\n    THREAD_SUSPENDED\n    THREAD_UNSUSPENDED\n  end\n\n  alias OnCollectionEventProc = EventType ->\n  fun set_on_collection_event = GC_set_on_collection_event(cb : OnCollectionEventProc)\n  fun get_on_collection_event = GC_get_on_collection_event : OnCollectionEventProc\n\n  alias OnThreadEventProc = EventType, Void* ->\n  fun set_on_thread_event = GC_set_on_thread_event(cb : OnThreadEventProc)\n  fun get_on_thread_event = GC_get_on_thread_event : OnThreadEventProc\n\n  $gc_no = GC_gc_no : Word\n  $bytes_found = GC_bytes_found : SignedWord\n  # GC_on_collection_event isn't exported.  Can't collect totals without it.\n  # bytes_allocd, heap_size, unmapped_bytes are macros\n\n  fun size = GC_size(addr : Void*) : LibC::SizeT\n\n  # Boehm GC requires to use its own thread manipulation routines instead of pthread's or Win32's\n  {% if flag?(:win32) %}\n    fun beginthreadex = GC_beginthreadex(security : Void*, stack_size : LibC::UInt, start_address : Void* -> LibC::UInt,\n                                         arglist : Void*, initflag : LibC::UInt, thrdaddr : LibC::UInt*) : Void*\n  {% elsif !flag?(:wasm32) %}\n    fun pthread_create = GC_pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) : LibC::Int\n    fun pthread_join = GC_pthread_join(thread : LibC::PthreadT, value : Void**) : LibC::Int\n    fun pthread_detach = GC_pthread_detach(thread : LibC::PthreadT) : LibC::Int\n  {% end %}\n\n  alias WarnProc = LibC::Char*, Word ->\n  fun set_warn_proc = GC_set_warn_proc(WarnProc)\n  $warn_proc = GC_current_warn_proc : WarnProc\n\n  fun stop_world_external = GC_stop_world_external\n  fun start_world_external = GC_start_world_external\n  fun get_suspend_signal = GC_get_suspend_signal : Int\n  fun get_thr_restart_signal = GC_get_thr_restart_signal : Int\nend\n\nmodule GC\n  {% if flag?(:preview_mt) %}\n    @@lock = uninitialized Crystal::RWLock\n  {% end %}\n\n  # :nodoc:\n  def self.malloc(size : LibC::SizeT) : Void*\n    Crystal.trace :gc, \"malloc\", size: size do\n      LibGC.malloc(size)\n    end\n  end\n\n  # :nodoc:\n  def self.malloc_atomic(size : LibC::SizeT) : Void*\n    Crystal.trace :gc, \"malloc\", size: size, atomic: 1 do\n      LibGC.malloc_atomic(size)\n    end\n  end\n\n  # :nodoc:\n  def self.realloc(ptr : Void*, size : LibC::SizeT) : Void*\n    Crystal.trace :gc, \"realloc\", size: size do\n      LibGC.realloc(ptr, size)\n    end\n  end\n\n  def self.init : Nil\n    {% unless flag?(:win32) %}\n      LibGC.set_handle_fork(1)\n    {% end %}\n    LibGC.init\n\n    {% if flag?(:preview_mt) %}\n      @@lock = Crystal::RWLock.new\n    {% end %}\n\n    LibGC.set_start_callback -> do\n      GC.lock_write\n    end\n\n    # pushes the stack of pending fibers when the GC wants to collect memory:\n    {% unless flag?(:interpreted) %}\n      GC.before_collect do\n        Fiber.unsafe_each do |fiber|\n          fiber.push_gc_roots unless fiber.running?\n        end\n\n        {% if flag?(:preview_mt) %}\n          Thread.unsafe_each do |thread|\n            if fiber = thread.current_fiber?\n              GC.set_stackbottom(thread.gc_thread_handler, fiber.@stack.bottom)\n            end\n          end\n        {% end %}\n\n        GC.unlock_write\n      end\n    {% end %}\n\n    {% if flag?(:tracing) %}\n      if ::Crystal::Tracing.enabled?(:gc)\n        set_on_heap_resize_proc\n        set_on_collection_events_proc\n      end\n    {% end %}\n\n    # By default the GC warns on big allocations/reallocations. This\n    # is of limited use and pollutes program output with warnings.\n    LibGC.set_warn_proc ->(msg, v) do\n      start = \"GC Warning: Repeated allocation of very large block\"\n      # This implements `String#starts_with?` without allocating a `String` (#11728)\n      format_string = Slice.new(msg, Math.min(LibC.strlen(msg), start.bytesize))\n      unless format_string == start.to_slice\n        Crystal::System.print_error msg, v\n      end\n    end\n  end\n\n  {% if flag?(:tracing) %}\n    @@on_heap_resize : LibGC::OnHeapResizeProc?\n    @@on_collection_event : LibGC::OnCollectionEventProc?\n\n    @@collect_start = 0_u64\n    @@mark_start = 0_u64\n    @@sweep_start = 0_u64\n\n    private def self.set_on_heap_resize_proc : Nil\n      @@on_heap_resize = LibGC.get_on_heap_resize\n\n      LibGC.set_on_heap_resize(->(new_size : LibGC::Word) {\n        Crystal.trace :gc, \"heap_resize\", size: new_size\n        @@on_heap_resize.try(&.call(new_size))\n      })\n    end\n\n    private def self.set_on_collection_events_proc : Nil\n      @@on_collection_event = LibGC.get_on_collection_event\n\n      LibGC.set_on_collection_event(->(event_type : LibGC::EventType) {\n        case event_type\n        when .start?\n          @@collect_start = Crystal::System::Time.ticks\n        when .mark_start?\n          @@mark_start = Crystal::System::Time.ticks\n        when .reclaim_start?\n          @@sweep_start = Crystal::System::Time.ticks\n        when .end?\n          duration = Crystal::System::Time.ticks - @@collect_start\n          Crystal.trace :gc, \"collect\", @@collect_start, duration: duration\n        when .mark_end?\n          duration = Crystal::System::Time.ticks - @@mark_start\n          Crystal.trace :gc, \"collect:mark\", @@mark_start, duration: duration\n        when .reclaim_end?\n          duration = Crystal::System::Time.ticks - @@sweep_start\n          Crystal.trace :gc, \"collect:sweep\", @@sweep_start, duration: duration\n        end\n        @@on_collection_event.try(&.call(event_type))\n      })\n    end\n  {% end %}\n\n  def self.collect\n    Crystal.trace :gc, \"collect\" do\n      LibGC.collect\n    end\n  end\n\n  def self.enable\n    unless LibGC.is_disabled != 0\n      raise \"GC is not disabled\"\n    end\n\n    LibGC.enable\n  end\n\n  def self.disable\n    LibGC.disable\n  end\n\n  def self.free(pointer : Void*) : Nil\n    Crystal.trace :gc, \"free\" do\n      LibGC.free(pointer)\n    end\n  end\n\n  def self.add_finalizer(object : Reference) : Nil\n    add_finalizer_impl(object)\n  end\n\n  def self.add_finalizer(object)\n    # Nothing\n  end\n\n  private def self.add_finalizer_impl(object : T) forall T\n    LibGC.register_finalizer_ignore_self(object.as(Void*),\n      ->(obj, data) { obj.as(T).finalize },\n      nil, nil, nil)\n    nil\n  end\n\n  def self.add_root(object : Reference)\n    roots = @@roots ||= [] of Pointer(Void)\n    roots << Pointer(Void).new(object.object_id)\n  end\n\n  def self.register_disappearing_link(pointer : Void**)\n    base = LibGC.base(pointer.value)\n    LibGC.general_register_disappearing_link(pointer, base)\n  end\n\n  def self.is_heap_ptr(pointer : Void*)\n    LibGC.is_heap_ptr(pointer) != 0\n  end\n\n  def self.stats\n    LibGC.get_heap_usage_safe(out heap_size, out free_bytes, out unmapped_bytes, out bytes_since_gc, out total_bytes)\n    # collections = LibGC.gc_no - 1\n    # bytes_found = LibGC.bytes_found\n\n    Stats.new(\n      # collections: collections,\n      # bytes_found: bytes_found,\n      heap_size: heap_size.to_u64!,\n      free_bytes: free_bytes.to_u64!,\n      unmapped_bytes: unmapped_bytes.to_u64!,\n      bytes_since_gc: bytes_since_gc.to_u64!,\n      total_bytes: total_bytes.to_u64!\n    )\n  end\n\n  def self.prof_stats\n    LibGC.get_prof_stats(out stats, sizeof(LibGC::ProfStats))\n\n    ProfStats.new(\n      heap_size: stats.heap_size.to_u64!,\n      free_bytes: stats.free_bytes.to_u64!,\n      unmapped_bytes: stats.unmapped_bytes.to_u64!,\n      bytes_since_gc: stats.bytes_since_gc.to_u64!,\n      bytes_before_gc: stats.bytes_before_gc.to_u64!,\n      non_gc_bytes: stats.non_gc_bytes.to_u64!,\n      gc_no: stats.gc_no.to_u64!,\n      markers_m1: stats.markers_m1.to_u64!,\n      bytes_reclaimed_since_gc: stats.bytes_reclaimed_since_gc.to_u64!,\n      reclaimed_bytes_before_gc: stats.reclaimed_bytes_before_gc.to_u64!,\n      expl_freed_bytes_since_gc: stats.expl_freed_bytes_since_gc.to_u64!,\n      obtained_from_os_bytes: stats.obtained_from_os_bytes.to_u64!)\n  end\n\n  {% if flag?(:win32) %}\n    # :nodoc:\n    def self.beginthreadex(security : Void*, stack_size : LibC::UInt, start_address : Void* -> LibC::UInt, arglist : Void*, initflag : LibC::UInt, thrdaddr : LibC::UInt*) : LibC::HANDLE\n      ret = LibGC.beginthreadex(security, stack_size, start_address, arglist, initflag, thrdaddr)\n      raise RuntimeError.from_errno(\"GC_beginthreadex\") if ret.null?\n      ret.as(LibC::HANDLE)\n    end\n  {% else %}\n    # :nodoc:\n    def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*)\n      LibGC.pthread_create(thread, attr, start, arg)\n    end\n\n    # :nodoc:\n    def self.pthread_join(thread : LibC::PthreadT)\n      LibGC.pthread_join(thread, nil)\n    end\n\n    # :nodoc:\n    def self.pthread_detach(thread : LibC::PthreadT)\n      LibGC.pthread_detach(thread)\n    end\n  {% end %}\n\n  # :nodoc:\n  def self.current_thread_stack_bottom\n    {% if LibGC.has_method?(:get_my_stackbottom) %}\n      th = LibGC.get_my_stackbottom(out sb)\n      {th, sb.mem_base}\n    {% else %}\n      # support for legacy gc releases\n      {Pointer(Void).null, LibGC.stackbottom}\n    {% end %}\n  end\n\n  # :nodoc:\n  {% if flag?(:preview_mt) %}\n    def self.set_stackbottom(thread_handle : Void*, stack_bottom : Void*)\n      sb = LibGC::StackBase.new\n      sb.mem_base = stack_bottom\n      LibGC.set_stackbottom(thread_handle, pointerof(sb))\n    end\n  {% else %}\n    def self.set_stackbottom(stack_bottom : Void*)\n      \\{% if LibGC.has_method?(:set_stackbottom) %}\n        # this is necessary because Boehm GC does _not_ use `GC_stackbottom` on\n        # Windows when pushing all threads' stacks; it also started crashing on\n        # Linux with libgc after v8.2.4; instead `GC_set_stackbottom` must be used\n        # to associate the new bottom with the running thread\n        sb = LibGC::StackBase.new\n        sb.mem_base = stack_bottom\n        # `nil` represents the current thread (i.e. the only one)\n        LibGC.set_stackbottom(nil, pointerof(sb))\n      \\{% else %}\n        # support for legacy gc releases\n        LibGC.stackbottom = stack_bottom\n      \\{% end %}\n    end\n  {% end %}\n\n  # :nodoc:\n  def self.lock_read\n    {% if flag?(:preview_mt) %}\n      @@lock.read_lock\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.unlock_read\n    {% if flag?(:preview_mt) %}\n      @@lock.read_unlock\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.lock_write\n    {% if flag?(:preview_mt) %}\n      @@lock.write_lock\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.unlock_write\n    {% if flag?(:preview_mt) %}\n      @@lock.write_unlock\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.push_stack(stack_top, stack_bottom) : Nil\n    LibGC.push_all_eager(stack_top, stack_bottom)\n  end\n\n  # :nodoc:\n  def self.before_collect(&block) : Nil\n    @@curr_push_other_roots = block\n    @@prev_push_other_roots = LibGC.get_push_other_roots\n\n    LibGC.set_push_other_roots -> do\n      @@curr_push_other_roots.try(&.call)\n      @@prev_push_other_roots.try(&.call)\n    end\n  end\n\n  # :nodoc:\n  def self.stop_world : Nil\n    LibGC.stop_world_external\n  end\n\n  # :nodoc:\n  def self.start_world : Nil\n    LibGC.start_world_external\n  end\n\n  {% if flag?(:unix) %}\n    # :nodoc:\n    def self.sig_suspend : Signal\n      Signal.new(LibGC.get_suspend_signal)\n    end\n\n    # :nodoc:\n    def self.sig_resume : Signal\n      Signal.new(LibGC.get_thr_restart_signal)\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/gc/none.cr",
    "content": "{% if flag?(:win32) %}\n  require \"c/process\"\n  require \"c/heapapi\"\n{% end %}\nrequire \"crystal/tracing\"\n\nmodule GC\n  def self.init\n    Crystal::System::Thread.init_suspend_resume\n  end\n\n  # :nodoc:\n  def self.malloc(size : LibC::SizeT) : Void*\n    Crystal.trace :gc, \"malloc\", size: size\n\n    {% if flag?(:win32) %}\n      LibC.HeapAlloc(LibC.GetProcessHeap, LibC::HEAP_ZERO_MEMORY, size)\n    {% else %}\n      # libc malloc is not guaranteed to return cleared memory, so we need to\n      # explicitly clear it. Ref: https://github.com/crystal-lang/crystal/issues/14678\n      LibC.malloc(size).tap(&.clear(size))\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.malloc_atomic(size : LibC::SizeT) : Void*\n    Crystal.trace :gc, \"malloc\", size: size, atomic: 1\n\n    {% if flag?(:win32) %}\n      LibC.HeapAlloc(LibC.GetProcessHeap, 0, size)\n    {% else %}\n      LibC.malloc(size)\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.realloc(pointer : Void*, size : LibC::SizeT) : Void*\n    Crystal.trace :gc, \"realloc\", size: size\n\n    {% if flag?(:win32) %}\n      # realloc with a null pointer should behave like plain malloc, but Win32\n      # doesn't do that\n      if pointer\n        LibC.HeapReAlloc(LibC.GetProcessHeap, LibC::HEAP_ZERO_MEMORY, pointer, size)\n      else\n        LibC.HeapAlloc(LibC.GetProcessHeap, LibC::HEAP_ZERO_MEMORY, size)\n      end\n    {% else %}\n      LibC.realloc(pointer, size)\n    {% end %}\n  end\n\n  def self.collect\n  end\n\n  def self.enable\n  end\n\n  def self.disable\n  end\n\n  def self.free(pointer : Void*) : Nil\n    Crystal.trace :gc, \"free\"\n\n    {% if flag?(:win32) %}\n      LibC.HeapFree(LibC.GetProcessHeap, 0, pointer)\n    {% else %}\n      LibC.free(pointer)\n    {% end %}\n  end\n\n  def self.is_heap_ptr(pointer : Void*) : Bool\n    false\n  end\n\n  def self.add_finalizer(object)\n  end\n\n  def self.register_disappearing_link(pointer : Void**)\n  end\n\n  def self.stats : GC::Stats\n    Stats.new(\n      # collections: 0,\n      # bytes_found: 0,\n      heap_size: 0,\n      free_bytes: 0,\n      unmapped_bytes: 0,\n      bytes_since_gc: 0,\n      total_bytes: 0)\n  end\n\n  def self.prof_stats\n    ProfStats.new(\n      heap_size: 0,\n      free_bytes: 0,\n      unmapped_bytes: 0,\n      bytes_since_gc: 0,\n      bytes_before_gc: 0,\n      non_gc_bytes: 0,\n      gc_no: 0,\n      markers_m1: 0,\n      bytes_reclaimed_since_gc: 0,\n      reclaimed_bytes_before_gc: 0,\n      expl_freed_bytes_since_gc: 0,\n      obtained_from_os_bytes: 0)\n  end\n\n  {% if flag?(:win32) %}\n    # :nodoc:\n    def self.beginthreadex(security : Void*, stack_size : LibC::UInt, start_address : Void* -> LibC::UInt, arglist : Void*, initflag : LibC::UInt, thrdaddr : LibC::UInt*) : LibC::HANDLE\n      ret = LibC._beginthreadex(security, stack_size, start_address, arglist, initflag, thrdaddr)\n      raise RuntimeError.from_errno(\"_beginthreadex\") if ret.null?\n      ret.as(LibC::HANDLE)\n    end\n  {% elsif !flag?(:wasm32) %}\n    # :nodoc:\n    def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*)\n      LibC.pthread_create(thread, attr, start, arg)\n    end\n\n    # :nodoc:\n    def self.pthread_join(thread : LibC::PthreadT)\n      LibC.pthread_join(thread, nil)\n    end\n\n    # :nodoc:\n    def self.pthread_detach(thread : LibC::PthreadT)\n      LibC.pthread_detach(thread)\n    end\n  {% end %}\n\n  # :nodoc:\n  def self.current_thread_stack_bottom : {Void*, Void*}\n    {Pointer(Void).null, Pointer(Void).null}\n  end\n\n  # :nodoc:\n  {% if flag?(:preview_mt) %}\n    def self.set_stackbottom(thread : Thread, stack_bottom : Void*)\n      # NOTE we could store stack_bottom per thread,\n      #      and return it in `#current_thread_stack_bottom`,\n      #      but there is no actual use for that.\n    end\n  {% else %}\n    def self.set_stackbottom(stack_bottom : Void*)\n    end\n  {% end %}\n\n  # :nodoc:\n  def self.lock_read\n  end\n\n  # :nodoc:\n  def self.unlock_read\n  end\n\n  # :nodoc:\n  def self.lock_write\n  end\n\n  # :nodoc:\n  def self.unlock_write\n  end\n\n  # :nodoc:\n  def self.push_stack(stack_top, stack_bottom)\n  end\n\n  # Stop and start the world.\n  #\n  # This isn't a GC-safe stop-the-world implementation (it may allocate objects\n  # while stopping the world), but the guarantees are enough for the purpose of\n  # gc_none. It could be GC-safe if Thread::LinkedList(T) became a struct, and\n  # Thread::Mutex either became a struct or provide low level abstraction\n  # methods that directly interact with syscalls (without allocating).\n  #\n  # Thread safety is guaranteed by the mutex in Thread::LinkedList: either a\n  # thread is starting and hasn't added itself to the list (it will block until\n  # it can acquire the lock), or is currently adding itself (the current thread\n  # will block until it can acquire the lock).\n  #\n  # In both cases there can't be a deadlock since we won't suspend another\n  # thread until it has successfully added (or removed) itself to (from) the\n  # linked list and released the lock, and the other thread won't progress until\n  # it can add (or remove) itself from the list.\n  #\n  # Finally, we lock the mutex and keep it locked until we resume the world, so\n  # any thread waiting on the mutex will only be resumed when the world is\n  # resumed.\n\n  # :nodoc:\n  def self.stop_world : Nil\n    current_thread = Thread.current\n\n    # grab the lock (and keep it until the world is restarted)\n    Thread.lock\n\n    # tell all threads to stop (async)\n    Thread.unsafe_each do |thread|\n      thread.suspend unless thread == current_thread\n    end\n\n    # wait for all threads to have stopped\n    Thread.unsafe_each do |thread|\n      thread.wait_suspended unless thread == current_thread\n    end\n  end\n\n  # :nodoc:\n  def self.start_world : Nil\n    current_thread = Thread.current\n\n    # tell all threads to resume\n    Thread.unsafe_each do |thread|\n      thread.resume unless thread == current_thread\n    end\n\n    # finally, we can release the lock\n    Thread.unlock\n  end\nend\n"
  },
  {
    "path": "src/gc.cr",
    "content": "# :nodoc:\nfun __crystal_malloc(size : UInt32) : Void*\n  GC.malloc(LibC::SizeT.new(size))\nend\n\n# :nodoc:\nfun __crystal_malloc_atomic(size : UInt32) : Void*\n  GC.malloc_atomic(LibC::SizeT.new(size))\nend\n\n# :nodoc:\nfun __crystal_realloc(pointer : Void*, size : UInt32) : Void*\n  GC.realloc(pointer, LibC::SizeT.new(size))\nend\n\n# :nodoc:\nfun __crystal_malloc64(size : UInt64) : Void*\n  {% if flag?(:bits32) %}\n    if size > UInt32::MAX\n      raise ArgumentError.new(\"Given size is bigger than UInt32::MAX\")\n    end\n  {% end %}\n\n  GC.malloc(LibC::SizeT.new(size))\nend\n\n# :nodoc:\nfun __crystal_malloc_atomic64(size : UInt64) : Void*\n  {% if flag?(:bits32) %}\n    if size > UInt32::MAX\n      raise ArgumentError.new(\"Given size is bigger than UInt32::MAX\")\n    end\n  {% end %}\n\n  GC.malloc_atomic(LibC::SizeT.new(size))\nend\n\n# :nodoc:\nfun __crystal_realloc64(ptr : Void*, size : UInt64) : Void*\n  {% if flag?(:bits32) %}\n    if size > UInt32::MAX\n      raise ArgumentError.new(\"Given size is bigger than UInt32::MAX\")\n    end\n  {% end %}\n\n  GC.realloc(ptr, LibC::SizeT.new(size))\nend\n\nmodule GC\n  struct Stats\n    # The system memory allocated by the GC for its HEAP, in bytes. The memory\n    # may or may not have been allocated by the OS (for example some pages\n    # haven't been accessed). The number can grow and shrink as needed by the\n    # process.\n    getter heap_size : UInt64\n\n    # Approximate number of free bytes in the GC HEAP. The number is relative to\n    # the `#heap_size`. The reported value is pessimistic, there might be more\n    # free bytes in reality.\n    getter free_bytes : UInt64\n\n    # The size (in bytes) of virtual system memory that the GC returned to the\n    # OS when shrinking its HEAP. The OS may have reclaimed the memory already,\n    # reducing the resident memory usage, or may do so later (for example on\n    # memory pressure). The GC will reuse this memory when it needs to grow its\n    # HEAP again.\n    getter unmapped_bytes : UInt64\n\n    # Total memory allocated by the GC into its HEAP since the last GC\n    # collection, in bytes.\n    getter bytes_since_gc : UInt64\n\n    # Total memory allocated by the GC into its HEAP since the program started,\n    # in bytes. The number keeps growing indefinitely until the integer wraps\n    # back to zero.\n    getter total_bytes : UInt64\n\n    def initialize(@heap_size, @free_bytes, @unmapped_bytes, @bytes_since_gc, @total_bytes)\n    end\n\n    @[Deprecated]\n    def copy_with(heap_size = @heap_size, free_bytes = @free_bytes, unmapped_bytes = @unmapped_bytes, bytes_since_gc = @bytes_since_gc, total_bytes = @total_bytes)\n      self.class.new(heap_size, free_bytes, unmapped_bytes, bytes_since_gc, total_bytes)\n    end\n\n    @[Deprecated]\n    def clone\n      self.class.new(@heap_size.clone, @free_bytes.clone, @unmapped_bytes.clone, @bytes_since_gc.clone, @total_bytes.clone)\n    end\n  end\n\n  record ProfStats,\n    heap_size : UInt64,\n    free_bytes : UInt64,\n    unmapped_bytes : UInt64,\n    bytes_since_gc : UInt64,\n    bytes_before_gc : UInt64,\n    non_gc_bytes : UInt64,\n    gc_no : UInt64,\n    markers_m1 : UInt64,\n    bytes_reclaimed_since_gc : UInt64,\n    reclaimed_bytes_before_gc : UInt64,\n    expl_freed_bytes_since_gc : UInt64,\n    obtained_from_os_bytes : UInt64\n\n  # Allocates and clears *size* bytes of memory.\n  #\n  # The resulting object may contain pointers and they will be tracked by the GC.\n  #\n  # The memory will be automatically deallocated when unreferenced.\n  def self.malloc(size : Int) : Void*\n    malloc(LibC::SizeT.new(size))\n  end\n\n  # Allocates *size* bytes of pointer-free memory.\n  #\n  # The client promises that the resulting object will never contain any pointers.\n  #\n  # The memory is not cleared. It will be automatically deallocated when unreferenced.\n  def self.malloc_atomic(size : Int) : Void*\n    malloc_atomic(LibC::SizeT.new(size))\n  end\n\n  # Changes the allocated memory size of *pointer* to *size*.\n  # If this can't be done in place, it allocates *size* bytes of memory and\n  # copies the content of *pointer* to the new location.\n  #\n  # If *pointer* was allocated with `malloc_atomic`, the same constraints apply.\n  #\n  # The return value is a pointer that may be identical to *pointer* or different.\n  #\n  # WARNING: Memory allocated using `Pointer.malloc` must be reallocated using\n  # `Pointer#realloc` instead.\n  def self.realloc(pointer : T*, size : Int) : T* forall T\n    realloc(pointer.as(Void*), LibC::SizeT.new(size)).as(T*)\n  end\nend\n\n{% if flag?(:gc_none) || flag?(:wasm32) %}\n  require \"gc/none\"\n{% else %}\n  require \"gc/boehm\"\n{% end %}\n"
  },
  {
    "path": "src/hash.cr",
    "content": "require \"crystal/hasher\"\n\n# A `Hash` represents a collection of key-value mappings, similar to a dictionary.\n#\n# Main operations are storing a key-value mapping (`#[]=`) and\n# querying the value associated to a key (`#[]`). Key-value mappings can also be\n# deleted (`#delete`).\n# Keys are unique within a hash. When adding a key-value mapping with a key that\n# is already in use, the old value will be forgotten.\n#\n# ```\n# # Create a new Hash for mapping String to Int32\n# hash = Hash(String, Int32).new\n# hash[\"one\"] = 1\n# hash[\"two\"] = 2\n# hash[\"one\"] # => 1\n# ```\n#\n# [Hash literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/hash.html)\n# can also be used to create a `Hash`:\n#\n# ```\n# {\"one\" => 1, \"two\" => 2}\n# ```\n#\n# Implementation is based on an open hash table.\n# Two objects refer to the same hash key when their hash value (`Object#hash`)\n# is identical and both objects are equal to each other (`Object#==`).\n#\n# Enumeration follows the order that the corresponding keys were inserted.\n#\n# NOTE: When using mutable data types as keys, changing the value of a key after\n# it was inserted into the `Hash` may lead to undefined behaviour. This can be\n# restored by re-indexing the hash with `#rehash`.\nclass Hash(K, V)\n  include Enumerable({K, V})\n  include Iterable({K, V})\n\n  # ===========================================================================\n  # Overall explanation of the algorithm\n  # ===========================================================================\n  #\n  # Hash implements an open addressing collision resolution method:\n  # https://en.wikipedia.org/wiki/Open_addressing\n  #\n  # The collision resolution is done using Linear Probing:\n  # https://en.wikipedia.org/wiki/Linear_probing\n  #\n  # The algorithm is partially based on Ruby's one but they are not exactly the same:\n  # https://github.com/ruby/ruby/blob/a4c09342a2219a8374240ef8d0ca86abe287f715/st.c#L1-L101\n  #\n  # There are two main data structures:\n  #\n  # - @entries:\n  #     A contiguous buffer (Pointer) of hash entries (Entry) in the order\n  #     they were inserted. This makes it possible for Hash to preserve\n  #     order of insertion.\n  #     An entry holds a key-value pair together with the key's hash code.\n  #     An entry can also be marked as deleted. This is accomplished by using\n  #     0 as the hash code value. Because 0 is a valid hash code value, when\n  #     computing the key's hash code if it's 0 then it's replaced by another\n  #     value (UInt32::MAX). The alternative would be to use a boolean but\n  #     that involves more memory allocated and worse performance.\n  # - @indices:\n  #     A buffer of indices into the @entries buffer.\n  #     An index might mean it's empty. We could use -1 for this but because\n  #     of an optimization we'll explain later we use 0, and all other values\n  #     represent indices which are 1 less than their actual value (so value\n  #     3 means index 2).\n  #     When a key-value pair is inserted we first find the key's hash and\n  #     then fit it (by modulo) into the indices buffer size. For example,\n  #     assuming we are inserting a new key-value pair with key \"hello\",\n  #     if the indices size is 128, the key is \"hello\" and its hash is\n  #     987 then fitting it into 128 is (987 % 128) gives 91. Lets also\n  #     assume there are already 3 entries in @entries. We go ahead an add\n  #     a new entry at index 3, and at position 91 in @indices we store 3\n  #     (well, actually 4 because we store 1 more than the actual index\n  #     because 0 means empty, as explained above).\n  #\n  # Open addressing means that if, in the example above, we go and try to\n  # insert another key with a hash that will be placed in the same position\n  # in indices (let's say, 91 again), because it's occupied we will insert\n  # it into the next non-empty slot. We try with 92. If it's empty we again\n  # go and insert it intro `@entries` and store the index at 92 (continuing\n  # with the previous example we would store the value 4).\n  #\n  # If we keep the size of @indices the same as @entries it means that in the worse\n  # case @indices is full and when finding a match we have to traverse it all,\n  # which is bad. That's why we always make the size of @indices at least twice\n  # as big as the size of @entries, so the non-empty indices will tend to be\n  # spread apart with empty indices in the middle.\n  #\n  # Also, we always keep the sizes of `@indices` and `@entries` (`indices_size` / 2)\n  # powers of 2, with the smallest size of `@indices` being 8 (and thus of\n  # `@entries` being 4).\n  #\n  # The size of `@indices` is stored as a number that has to be powered by 2 in\n  # `@indices_size_pow2`. For example if `@indices_size_pow2` is 3 then the actual\n  # size is 2**3 = 8.\n  #\n  # Next comes the optimizations.\n  #\n  # The first one is that for an empty hash we don't allocate `@entries`\n  # nor `@indices`, and there are a few checks against these when adding\n  # and fetching elements. Sometimes hashes are created empty and remain empty\n  # for some time or for the duration of the program when not used, and this\n  # helps save some memory.\n  #\n  # The second optimization is that for small hashes (less or equal to 16 elements)\n  # we don't allocate `@indices` and just perform a linear scan on `@entries`.\n  # This is an heuristic but in practice it's faster to search linearly in small\n  # hashes. There's another heuristic here: if we have less than or equal to 8\n  # elements we just compare values when doing the linear scan. If we have between\n  # 9 and 16 we first compute the hash code of the key and compare the hash codes\n  # first (at this point computing the hash code plus comparing them might become\n  # cheaper than doing a full comparison each time). This optimization also exists\n  # in the Ruby implementation (though it seems hash values are always compared).\n  #\n  # A third optimization is in the way `@indices` is allocated: when the number\n  # of entries is less than or equal to 128 (2 ** 8 / 2) the indexes values will range between\n  # 0 and 128. That means we can use `Pointer(UInt8)` as the type of `@indices`.\n  # (we can't do it for ranges between 0 and 256 because we need a value that means\n  # \"empty\"). Similarly, for ranges between 128 and 32768 (2 ** 16 / 2) we can use\n  # `Pointer(UInt16)`. This saves some memory (and the performance difference is\n  # noticeable). We store the bytesize of the `@indices` buffer in `@indices_bytesize`\n  # with values 1 (UInt8), 2 (UInt16) or 4 (UInt32). This optimization also exists\n  # in the Ruby implementation.\n  #\n  # Another optimization is, when fitting a value inside the range of `@indices`,\n  # to use masking (value & mask) instead of `remainder` or `%`, which apparently\n  # are much slower. This optimization also exists in the Ruby implementation.\n  #\n  # We also keep track of the number of deleted entries (`@deleted_count`). When an\n  # entry is deleted we just mark it as deleted by using the special hash value 0.\n  # Only when the hash needs to be resized we do something with this instance variable:\n  # if we have many deleted entries (at least as many as the number of non-deleted\n  # entries) we compact the map and avoid a resize. Otherwise we remove the non-deleted\n  # entries but also resize both the `@entries` and `@indices` buffer. This probably\n  # avoids an edge case where one deletes and inserts an element and there is a constant\n  # shift of the buffer (expensive).\n  #\n  # There might be other optimizations to try out, like not using Linear Probing,\n  # but for now this implementation is much faster than the old one which used\n  # linked lists (closed addressing).\n  #\n  # All methods that deal with this implementation come after the constructors.\n  # Then all other methods use the internal methods, usually using other high-level\n  # methods.\n\n  # The index of the first non-deleted entry in `@entries`.\n  # This is useful to support `shift`: instead of marking an entry\n  # as deleted and then always having to ignore it we just increment this\n  # variable and always start iterating from it.\n  # The invariant of `@first` always pointing to a non-deleted entry holds\n  # (unless `@size` is 0) and is guaranteed because of how\n  # `delete_and_update_counts` is implemented.\n  @first : Int32 = 0\n\n  # The buffer of entries.\n  # Might be null if the hash is empty at the very beginning.\n  # Has always the size of `indices_size` / 2.\n  @entries : Pointer(Entry(K, V))\n\n  # The buffer of indices into entries. Its size is given by `@indices_size_pow2`.\n  # Might be null if the hash is empty at the very beginning or when the hash\n  # size is less than or equal to 16.\n  # Could be a Slice but this way we might save a few bounds checking.\n  @indices : Pointer(UInt8)\n\n  # The number of actual entries in the hash.\n  # Exposed to the user via the `size` getter.\n  @size : Int32\n\n  # The number of deleted entries.\n  # Resets to zero when the hash resizes.\n  @deleted_count : Int32\n\n  # The actual type of `@indices`:\n  # - 1 means `Pointer(UInt8)`\n  # - 2 means `Pointer(UInt16)`\n  # - 4 means `Pointer(UInt32)`\n  @indices_bytesize : Int8\n\n  # The size of `@indices` given as a power of 2.\n  # For example if it's 4 it means 2**4 so size 16.\n  # Can be zero when hash is totally empty.\n  # Otherwise guaranteed to be at least 3.\n  @indices_size_pow2 : UInt8\n\n  # Whether to compare objects using `object_id`.\n  @compare_by_identity : Bool = false\n\n  # The optional block that triggers on non-existing keys.\n  @block : (self, K -> V)?\n\n  # Creates a new empty `Hash`.\n  def initialize\n    @entries = Pointer(Entry(K, V)).null\n    @indices = Pointer(UInt8).null\n    @indices_size_pow2 = 0\n    @size = 0\n    @deleted_count = 0\n    @block = nil\n    @indices_bytesize = 1\n  end\n\n  # Creates a new empty `Hash` with a *block* for handling missing keys.\n  #\n  # ```\n  # proc = ->(hash : Hash(String, Int32), key : String) { hash[key] = key.size }\n  # hash = Hash(String, Int32).new(proc)\n  #\n  # hash.size   # => 0\n  # hash[\"foo\"] # => 3\n  # hash.size   # => 1\n  # hash[\"bar\"] = 10\n  # hash[\"bar\"] # => 10\n  # ```\n  #\n  # The *initial_capacity* is useful to avoid unnecessary reallocations\n  # of the internal buffer in case of growth. If the number of elements\n  # a hash will hold is known, the hash should be initialized with that\n  # capacity for improved performance. Otherwise, the default is 8.\n  # Inputs lower than 8 are ignored.\n  def initialize(block : (Hash(K, V), K -> V)? = nil, *, initial_capacity = nil)\n    initial_capacity = (initial_capacity || 0).to_i32\n\n    # Same as the empty hash case\n    # (but this constructor is a bit more expensive in terms of code execution).\n    if initial_capacity == 0\n      @entries = Pointer(Entry(K, V)).null\n      @indices = Pointer(UInt8).null\n      @indices_size_pow2 = 0\n      @indices_bytesize = 1\n    else\n      # Translate initial capacity to the nearest power of 2, but keep it a minimum of 8.\n      if initial_capacity < 8\n        initial_entries_size = 8\n      elsif initial_capacity > 2**30\n        initial_entries_size = Int32::MAX\n      else\n        initial_entries_size = Math.pw2ceil(initial_capacity)\n      end\n\n      # Because we always keep indice_size >= entries_size * 2\n      initial_indices_size = initial_entries_size.to_u64 * 2\n\n      @entries = malloc_entries(initial_entries_size)\n\n      # Check if we can avoid allocating the `@indices` buffer for\n      # small hashes.\n      if initial_indices_size > MAX_INDICES_SIZE_LINEAR_SCAN\n        @indices_bytesize = compute_indices_bytesize(initial_indices_size)\n        @indices = malloc_indices(initial_indices_size)\n      else\n        @indices = Pointer(UInt8).null\n        @indices_bytesize = 1\n      end\n\n      @indices_size_pow2 = initial_indices_size.bit_length.to_u8 - 1\n    end\n\n    @size = 0\n    @deleted_count = 0\n    @block = block\n  end\n\n  # Creates a new empty `Hash` with a *block* that handles missing keys.\n  #\n  # ```\n  # hash = Hash(String, String).new do |hash, key|\n  #   \"some default value\"\n  # end\n  #\n  # hash.size           # => 0\n  # hash[\"foo\"] = \"bar\" # => \"bar\"\n  # hash.size           # => 1\n  # hash[\"baz\"]         # => \"some default value\"\n  # hash.size           # => 1\n  # hash                # => {\"foo\" => \"bar\"}\n  # ```\n  #\n  # WARNING: When the default block is invoked on a missing key, its return\n  # value is *not* implicitly stored into the hash under that key. If you want\n  # that behaviour, you need to store it explicitly:\n  #\n  # ```\n  # hash = Hash(String, Int32).new do |hash, key|\n  #   hash[key] = key.size\n  # end\n  #\n  # hash.size   # => 0\n  # hash[\"foo\"] # => 3\n  # hash.size   # => 1\n  # hash[\"bar\"] = 10\n  # hash[\"bar\"] # => 10\n  # ```\n  #\n  # The *initial_capacity* is useful to avoid unnecessary reallocations\n  # of the internal buffer in case of growth. If the number of elements\n  # a hash will hold is known, the hash should be initialized with that\n  # capacity for improved performance. Otherwise, the default is 8.\n  # Inputs lower than 8 are ignored.\n  def self.new(initial_capacity = nil, &block : (Hash(K, V), K -> V))\n    new block, initial_capacity: initial_capacity\n  end\n\n  # Creates a new empty `Hash` where the *default_value* is returned if a key is missing.\n  #\n  # ```\n  # inventory = Hash(String, Int32).new(0)\n  # inventory[\"socks\"] = 3\n  # inventory[\"pickles\"] # => 0\n  # ```\n  #\n  # WARNING: When the default value gets returned on a missing key, it is *not*\n  # stored into the hash under that key. If you want that behaviour, please use\n  # the overload with a block.\n  #\n  # WARNING: The default value is returned as-is. It gets neither duplicated nor\n  # cloned. For types with reference semantics this means it will be exactly the\n  # *same* object every time.\n  #\n  # ```\n  # hash = Hash(String, Array(Int32)).new([1])\n  # hash[\"a\"][0] = 2\n  # hash[\"b\"] # => [2]\n  # ```\n  #\n  # * `.new(&block : (Hash(K, V), K -> V))` is an alternative with a block that\n  #   can return a different default value for each invocation.\n  #\n  # The *initial_capacity* is useful to avoid unnecessary reallocations\n  # of the internal buffer in case of growth. If the number of elements\n  # a hash will hold is known, the hash should be initialized with that\n  # capacity for improved performance. Otherwise, the default is 8.\n  # Inputs lower than 8 are ignored.\n  def self.new(default_value : V, initial_capacity = nil)\n    new(initial_capacity: initial_capacity) { default_value }\n  end\n\n  # ===========================================================================\n  # Internal implementation starts\n  # ===========================================================================\n\n  # Maximum number of `indices_size` for which we do a linear scan\n  # (maximum of 16 entries in `@entries`)\n  private MAX_INDICES_SIZE_LINEAR_SCAN = 32\n\n  # Maximum number of `indices_size` for which we can represent `@indices`\n  # as Pointer(UInt8).\n  private MAX_INDICES_BYTESIZE_1 = 256\n\n  # Maximum number of `indices_size` for which we can represent `@indices`\n  # as Pointer(UInt16).\n  private MAX_INDICES_BYTESIZE_2 = 65536\n\n  # Inserts or updates a key-value pair.\n  # Returns an `Entry` if it was updated, otherwise `nil`.\n  private def upsert(key, value) : Entry(K, V)?\n    # Empty hash table so only initialize entries for now\n    if @entries.null?\n      @indices_size_pow2 = 3\n      @entries = malloc_entries(4)\n    end\n\n    hash = key_hash(key)\n\n    # No indices allocated yet so try to do a linear scan\n    if @indices.null?\n      # Try to do an update by doing a linear scan\n      updated_entry = update_linear_scan(key, value, hash)\n      return updated_entry if updated_entry\n\n      # If we still have space, add an entry.\n      if !entries_full?\n        add_entry_and_increment_size(hash, key, value)\n        return nil\n      end\n\n      # No more space so we need to do a resize\n      resize\n\n      # Now, it could happen that we are still with less than 16 elements\n      # and so `@indices` will be null, in which case we only need to\n      # add the key-value pair at the end of the `@entries` buffer.\n      if @indices.null?\n        add_entry_and_increment_size(hash, key, value)\n        return nil\n      end\n\n      # Otherwise `@indices` became non-null which means we can't do\n      # a linear scan anymore.\n    end\n\n    # Fit the hash value into an index in `@indices`\n    index = fit_in_indices(hash)\n\n    while true\n      entry_index = get_index(index)\n\n      # If the index entry is empty...\n      if entry_index == -1\n        # If we reached the maximum in `@entries` it's time to resize\n        if entries_full?\n          resize\n          # We have to fit the hash into an index in `@indices` again, and try again\n          index = fit_in_indices(hash)\n          next\n        end\n\n        # We have free space: store the index and then insert the entry\n        set_index(index, entries_size)\n        add_entry_and_increment_size(hash, key, value)\n        return nil\n      end\n\n      # We found a non-empty slot, let's see if the key we have matches\n      entry = get_entry(entry_index)\n      if entry_matches?(entry, hash, key)\n        # If it does we just update the entry\n        set_entry(entry_index, Entry(K, V).new(hash, key, value))\n        return entry\n      else\n        # Otherwise we have to keep looking...\n        index = next_index(index)\n      end\n    end\n  end\n\n  # Tries to update a key-value-hash triplet by doing a linear scan.\n  # Returns an old `Entry` if it was updated, otherwise `nil`.\n  private def update_linear_scan(key, value, hash) : Entry(K, V)?\n    # Just do a linear scan...\n    each_entry_with_index do |entry, index|\n      if entry_matches?(entry, hash, key)\n        set_entry(index, Entry(K, V).new(entry.hash, entry.key, value))\n        return entry\n      end\n    end\n\n    nil\n  end\n\n  # Implementation of deleting a key.\n  # Returns the deleted Entry, if it existed, `nil` otherwise.\n  private def delete_impl(key) : Entry(K, V)?\n    # Empty hash table, nothing to do\n    if @indices_size_pow2 == 0\n      return nil\n    end\n\n    hash = key_hash(key)\n\n    # No indices allocated yet so do linear scan\n    if @indices.null?\n      return delete_linear_scan(key, hash)\n    end\n\n    # Fit hash into `@indices` size\n    index = fit_in_indices(hash)\n    while true\n      entry_index = get_index(index)\n\n      # If we find an empty index slot, there's no such key\n      if entry_index == -1\n        return nil\n      end\n\n      # We found a non-empty slot, let's see if the key we have matches\n      entry = get_entry(entry_index)\n      if entry_matches?(entry, hash, key)\n        # Mark this index slot as deleted\n        delete_index(index)\n        delete_entry_and_update_counts(entry_index)\n        return entry\n      else\n        # If it doesn't, check the next index...\n        index = next_index(index)\n      end\n    end\n  end\n\n  # Delete by doing a linear scan over `@entries`.\n  # Returns the deleted Entry, if it existed, `nil` otherwise.\n  private def delete_linear_scan(key, hash) : Entry(K, V)?\n    each_entry_with_index do |entry, index|\n      if entry_matches?(entry, hash, key)\n        delete_entry_and_update_counts(index)\n        return entry\n      end\n    end\n\n    nil\n  end\n\n  # Finds an entry with the given key.\n  protected def find_entry(key) : Entry(K, V)?\n    if entry_index = find_entry_with_index(key)\n      return entry_index[0]\n    end\n  end\n\n  # Finds an entry (and its index) with the given key.\n  protected def find_entry_with_index(key) : {Entry(K, V), Int32}?\n    # Empty hash table so there's no way it's there\n    if @indices_size_pow2 == 0\n      return nil\n    end\n\n    # No indices allocated yet so do linear scan\n    if @indices.null?\n      return find_entry_with_index_linear_scan(key)\n    end\n\n    hash = key_hash(key)\n\n    # Fit hash into `@indices` size\n    index = fit_in_indices(hash)\n    while true\n      entry_index = get_index(index)\n\n      # If we find an empty index slot, there's no such key\n      if entry_index == -1\n        return nil\n      end\n\n      # We found a non-empty slot, let's see if the key we have matches\n      entry = get_entry(entry_index)\n      if entry_matches?(entry, hash, key)\n        # It does!\n        return entry, entry_index\n      else\n        # Nope, move on to the next slot\n        index = next_index(index)\n      end\n    end\n  end\n\n  # Finds an Entry with the given key by doing a linear scan.\n  private def find_entry_with_index_linear_scan(key) : {Entry(K, V), Int32}?\n    # If we have less than 8 elements we avoid computing the hash\n    # code and directly compare the keys (might be cheaper than\n    # computing a hash code of a complex structure).\n    if entries_size <= 8\n      each_entry_with_index do |entry, index|\n        return entry, index if entry_matches?(entry, key)\n      end\n    else\n      hash = key_hash(key)\n      each_entry_with_index do |entry, index|\n        return entry, index if entry_matches?(entry, hash, key)\n      end\n    end\n\n    nil\n  end\n\n  # Tries to resize the hash table in the condition that there are\n  # no more available entries to add.\n  # Might not result in a resize if there are many entries marked as\n  # deleted. In that case the entries table is simply compacted.\n  # However, in case of a resize deleted entries are also compacted.\n  private def resize : Nil\n    # Only do an actual resize (grow `@entries` buffer) if we don't\n    # have many deleted elements.\n    if @deleted_count < @size\n      # First grow `@entries`\n      realloc_entries(indices_size)\n      double_indices_size\n\n      # If we didn't have `@indices` and we still don't have 16 entries\n      # we keep doing linear scans (not using `@indices`)\n      if @indices.null? && indices_size <= MAX_INDICES_SIZE_LINEAR_SCAN\n        return\n      end\n\n      # Otherwise, we must either start using `@indices`\n      # or grow the ones we had.\n      @indices_bytesize = compute_indices_bytesize(indices_size)\n      if @indices.null?\n        @indices = malloc_indices(indices_size)\n      else\n        @indices = realloc_indices(indices_size)\n      end\n    end\n\n    do_compaction\n\n    # After compaction we no longer have deleted entries\n    @deleted_count = 0\n\n    # And the first valid entry is the first one\n    @first = 0\n  end\n\n  # Compacts `@entries` (only keeps non-deleted ones) and rebuilds `@indices.`\n  # If `rehash` is `true` then hash values inside each `Entry` will be recomputed.\n  private def do_compaction(rehash : Bool = false) : Nil\n    # `@indices` might still be null if we are compacting in the case where\n    # we are still doing a linear scan (and we had many deleted elements)\n    if @indices.null?\n      has_indices = false\n    else\n      # If we do have indices we must clear them because we'll rebuild\n      # them from scratch\n      has_indices = true\n      clear_indices\n    end\n\n    # Here we traverse the `@entries` and compute their new index in `@indices`\n    # while moving non-deleted entries to the beginning (compaction).\n    new_entry_index = 0\n    each_entry_with_index do |entry, entry_index|\n      if rehash\n        # When rehashing we always have to copy the entry\n        entry_hash = key_hash(entry.key)\n        set_entry(new_entry_index, Entry(K, V).new(entry_hash, entry.key, entry.value))\n      else\n        # First we move the entry to its new index (if we need to do that)\n        entry_hash = entry.hash\n        set_entry(new_entry_index, entry) if entry_index != new_entry_index\n      end\n\n      if has_indices\n        # Then we try to find an empty index slot\n        # (we should find one now that we have more space)\n        index = fit_in_indices(entry_hash)\n        until get_index(index) == -1\n          index = next_index(index)\n        end\n        set_index(index, new_entry_index)\n      end\n\n      new_entry_index += 1\n    end\n\n    # Reset offset to first non-deleted entry\n    @first = 0\n\n    # We have to mark entries starting from the final new index\n    # as deleted so the GC can collect them.\n    entries_to_clear = entries_size - new_entry_index\n    if entries_to_clear > 0\n      (entries + new_entry_index).clear(entries_to_clear)\n    end\n  end\n\n  # After this it's 1 << 28, and with entries being Int32\n  # (4 bytes) it's 1 << 30 of actual bytesize and the\n  # next value would be 1 << 31 which overflows `Int32`.\n  private MAXIMUM_INDICES_SIZE = 1 << 28\n\n  # Doubles the value of `@indices_size` but first checks\n  # whether the maximum hash size is reached.\n  private def double_indices_size : Nil\n    if indices_size == MAXIMUM_INDICES_SIZE\n      raise \"Maximum Hash size reached\"\n    end\n\n    @indices_size_pow2 += 1\n  end\n\n  # Implementation of clearing the hash table.\n  private def clear_impl : Nil\n    # We _could_ set all buffers to null and start like in the\n    # empty case.\n    # However, it might happen that a user calls clear and then inserts\n    # elements in a loop. In that case each insert after clear will cause\n    # a new memory allocation and that's not good.\n    # Just clearing the buffers might retain some memory but it\n    # avoids a possible constant reallocation (which is slower).\n    clear_entries unless @entries.null?\n    clear_indices unless @indices.null?\n    @size = 0\n    @deleted_count = 0\n    @first = 0\n  end\n\n  # Initializes a `dup` copy from the contents of `other`.\n  protected def initialize_dup(other)\n    initialize_compare_by_identity(other)\n    initialize_default_block(other)\n\n    return if other.empty?\n\n    initialize_dup_entries(other)\n    initialize_copy_non_entries_vars(other)\n  end\n\n  # Initializes a `clone` copy from the contents of `other`.\n  protected def initialize_clone(other)\n    initialize_compare_by_identity(other)\n    initialize_default_block(other)\n\n    return if other.empty?\n\n    initialize_clone_entries(other)\n    initialize_copy_non_entries_vars(other)\n  end\n\n  private def initialize_compare_by_identity(other)\n    compare_by_identity if other.compare_by_identity?\n  end\n\n  private def initialize_default_block(other)\n    @block = other.@block\n  end\n\n  # Initializes `@entries` for a dup copy.\n  # Here we only need to duplicate the buffer.\n  private def initialize_dup_entries(other)\n    return if other.@entries.null?\n\n    @entries = malloc_entries(other.entries_capacity)\n\n    # Note that we only need to copy `entries_size` which\n    # are the effective entries in use.\n    @entries.copy_from(other.@entries, other.entries_size)\n  end\n\n  # Initializes `@entries` for a clone copy.\n  # Here we need to copy entries while cloning their values.\n  private def initialize_clone_entries(other)\n    return if other.@entries.null?\n\n    @entries = malloc_entries(other.entries_capacity)\n\n    other.each_entry_with_index do |entry, index|\n      set_entry(index, entry.clone)\n    end\n  end\n\n  # Initializes all variables other than `@entries` and `@block` for a copy.\n  private def initialize_copy_non_entries_vars(other)\n    @indices_bytesize = other.@indices_bytesize\n    @first = other.@first\n    @size = other.@size\n    @deleted_count = other.@deleted_count\n    @indices_size_pow2 = other.@indices_size_pow2\n\n    unless other.@indices.null?\n      @indices = malloc_indices(other.indices_size)\n      @indices.copy_from(other.@indices, indices_malloc_size(other.indices_size))\n    end\n  end\n\n  # Gets from `@indices` at the given `index`.\n  # Returns the index in `@entries` or `-1` if the slot is empty.\n  private def get_index(index : Int32) : Int32\n    # Check what we have: UInt8, Int16 or UInt32 buckets\n    value = case @indices_bytesize\n            when 1\n              @indices[index].to_i32!\n            when 2\n              @indices.as(UInt16*)[index].to_i32!\n            else\n              @indices.as(UInt32*)[index].to_i32!\n            end\n\n    # Because we increment the value by one when we store the value\n    # here we have to subtract one\n    value - 1\n  end\n\n  # Sets `@indices` at `index` with the given value.\n  private def set_index(index, value) : Nil\n    # We actually store 1 more than the value because 0 means empty.\n    value += 1\n\n    # We also have to see what we have: UInt8, UInt16 or UInt32 buckets.\n    case @indices_bytesize\n    when 1\n      @indices[index] = value.to_u8!\n    when 2\n      @indices.as(UInt16*)[index] = value.to_u16!\n    else\n      @indices.as(UInt32*)[index] = value.to_u32!\n    end\n  end\n\n  # Marks `@indices` at `index` as empty. Might also adjust subsequent index\n  # slots to ensure empty indices never appear before the natural position of\n  # any used index, in case of previous hash collisions.\n  private def delete_index(index) : Nil\n    # https://en.wikipedia.org/w/index.php?title=Open_addressing&oldid=1188919190#Example_pseudocode\n    i = index\n    set_index(i, -1)\n\n    j = i\n    while true\n      j = next_index(j)\n      entry_index = get_index(j)\n      break if entry_index == -1\n\n      entry = get_entry(entry_index)\n      k = fit_in_indices(entry.hash)\n\n      if i <= j\n        next if i < k && k <= j\n      else\n        next if k <= j || i < k\n      end\n\n      set_index(i, entry_index)\n      set_index(j, -1)\n      i = j\n    end\n  end\n\n  # Returns the capacity of `@indices`.\n  protected def indices_size\n    1 << @indices_size_pow2\n  end\n\n  # Computes what bytesize we'll store in `@indices` according to its size\n  private def compute_indices_bytesize(size) : Int8\n    case\n    when size <= MAX_INDICES_BYTESIZE_1\n      1_i8\n    when size <= MAX_INDICES_BYTESIZE_2\n      2_i8\n    else\n      4_i8\n    end\n  end\n\n  # Allocates `size` number of indices for `@indices`.\n  private def malloc_indices(size)\n    Pointer(UInt8).malloc(indices_malloc_size(size))\n  end\n\n  # The actual number of bytes needed to allocate `@indices`.\n  private def indices_malloc_size(size)\n    size.to_u64 * @indices_bytesize\n  end\n\n  # Reallocates `size` number of indices for `@indices`.\n  private def realloc_indices(size)\n    @indices.realloc(indices_malloc_size(size))\n  end\n\n  # Marks all existing indices as empty.\n  private def clear_indices : Nil\n    @indices.clear(indices_malloc_size(indices_size))\n  end\n\n  # Returns the entry in `@entries` at `index`.\n  private def get_entry(index) : Entry(K, V)\n    @entries[index]\n  end\n\n  # Sets the entry in `@entries` at `index`.\n  private def set_entry(index, value) : Nil\n    @entries[index] = value\n  end\n\n  # Returns the index into `@indices` for an existing *entry_index* into\n  # `@entries`.\n  private def index_for_entry_index(entry_index)\n    index = fit_in_indices(get_entry(entry_index).hash)\n    until get_index(index) == entry_index\n      index = next_index(index)\n    end\n    index\n  end\n\n  # Adds an entry at the end and also increments this hash's size.\n  private def add_entry_and_increment_size(hash, key, value) : Nil\n    set_entry(entries_size, Entry(K, V).new(hash, key, value))\n    @size += 1\n  end\n\n  # Marks an entry in `@entries` at `index` as deleted\n  # *without* modifying any counters (`@size` and `@deleted_count`).\n  private def delete_entry(index) : Nil\n    # sets `Entry#@hash` to 0 and removes stale references to key and value\n    (@entries + index).clear\n  end\n\n  # Marks an entry in `@entries` at `index` as deleted\n  # and updates the `@size` and `@deleted_count` counters.\n  private def delete_entry_and_update_counts(index) : Nil\n    delete_entry(index)\n    @size -= 1\n    @deleted_count += 1\n\n    # If we are deleting the first entry there are some\n    # more optimizations we can do\n    return if index != @first\n\n    # If the Hash is now empty then the first effective\n    # entry starts right after all the deleted ones.\n    if @size == 0\n      @first = @deleted_count\n    else\n      # Otherwise, we bump `@first` and keep bumping it\n      # until we find a non-deleted entry. It's guaranteed\n      # that this loop will end because `@size != 0` so\n      # there will be a non-deleted entry.\n      # It's better to skip the deleted entries once here\n      # and not every next time someone accesses the Hash.\n      # With this we also keep the invariant that `@first`\n      # always points to the first non-deleted entry.\n      @first += 1\n      while @entries[@first].deleted?\n        @first += 1\n      end\n    end\n  end\n\n  # Returns true if there's no place for new entries without doing a resize.\n  private def entries_full? : Bool\n    entries_size == entries_capacity\n  end\n\n  # Yields each non-deleted Entry with its index inside `@entries`.\n  protected def each_entry_with_index(&) : Nil\n    return if @size == 0\n\n    @first.upto(entries_size - 1) do |i|\n      entry = get_entry(i)\n      yield entry, i unless entry.deleted?\n    end\n  end\n\n  # Allocates `size` number of entries for `@entries`.\n  private def malloc_entries(size)\n    Pointer(Entry(K, V)).malloc(size)\n  end\n\n  private def realloc_entries(size)\n    @entries = @entries.realloc(size)\n  end\n\n  # Marks all existing entries as deleted\n  private def clear_entries\n    @entries.clear(entries_capacity)\n  end\n\n  # Computes the next index in `@indices`, needed when an index is not empty.\n  private def next_index(index : Int32) : Int32\n    fit_in_indices(index + 1)\n  end\n\n  # Fits a value inside the range of `@indices`\n  private def fit_in_indices(value) : Int32\n    # We avoid doing modulo (`%` or `remainder`) because it's much\n    # slower than `<<` + `-` + `&`.\n    # For example if `@indices_size_pow2` is 8 then `indices_size`\n    # will be 256 (1 << 8) and the mask we use is 0xFF, which is 256 - 1.\n    (value & ((1_u32 << @indices_size_pow2) - 1)).to_i32!\n  end\n\n  # Returns the first `Entry` or `nil` if non exists.\n  private def first_entry?\n    # We always make sure that `@first` points to the first\n    # non-deleted entry, so `@entries[@first]` is guaranteed\n    # to be non-deleted.\n    @size == 0 ? nil : @entries[@first]\n  end\n\n  # Returns the first `Entry` or `nil` if non exists.\n  private def last_entry?\n    return nil if @size == 0\n\n    (entries_size - 1).downto(@first).each do |i|\n      entry = get_entry(i)\n      return entry unless entry.deleted?\n    end\n\n    # Might happen if the Hash is modified concurrently\n    nil\n  end\n\n  protected getter entries\n\n  # Returns the total number of existing entries, including\n  # deleted and non-deleted ones.\n  protected def entries_size\n    @size + @deleted_count\n  end\n\n  # Returns the capacity of `@entries`.\n  protected def entries_capacity\n    indices_size // 2\n  end\n\n  # Computes the hash of a key.\n  private def key_hash(key)\n    if @compare_by_identity && key.responds_to?(:object_id)\n      hash = key.object_id.hash.to_u32!\n    else\n      hash = key.hash.to_u32!\n    end\n    hash == 0 ? UInt32::MAX : hash\n  end\n\n  private def entry_matches?(entry, hash, key)\n    # Tiny optimization: for these primitive types it's faster to just\n    # compare the key instead of comparing the hash and the key.\n    # We still have to skip hashes with value 0 (means deleted).\n    {% if K == Bool ||\n            K == Char ||\n            K == Symbol ||\n            K < Number::Primitive ||\n            K < Enum %}\n      entry.key == key && entry.hash != 0_u32\n    {% else %}\n      entry.hash == hash && entry_matches?(entry, key)\n    {% end %}\n  end\n\n  private def entry_matches?(entry, key)\n    entry_key = entry.key\n\n    if @compare_by_identity\n      if entry_key.responds_to?(:object_id)\n        if key.responds_to?(:object_id)\n          entry_key.object_id == key.object_id\n        else\n          false\n        end\n      elsif key.responds_to?(:object_id)\n        # because entry_key doesn't respond to :object_id\n        false\n      else\n        entry_key == key\n      end\n    else\n      entry_key == key\n    end\n  end\n\n  # ===========================================================================\n  # Internal implementation ends\n  # ===========================================================================\n\n  # Returns the number of elements in this Hash.\n  getter size : Int32\n\n  # Makes this hash compare keys using their object identity (`object_id)`\n  # for types that define such method (`Reference` types, but also structs that\n  # might wrap other `Reference` types and delegate the `object_id` method to them).\n  #\n  # ```\n  # h1 = {\"foo\" => 1, \"bar\" => 2}\n  # h1[\"fo\" + \"o\"]? # => 1\n  #\n  # h1.compare_by_identity\n  # h1.compare_by_identity? # => true\n  # h1[\"fo\" + \"o\"]?         # => nil # not the same String instance\n  # ```\n  def compare_by_identity : self\n    @compare_by_identity = true\n    rehash\n    self\n  end\n\n  # Returns `true` if this Hash is comparing keys by `object_id`.\n  #\n  # See `compare_by_identity`.\n  getter? compare_by_identity : Bool\n\n  # Sets the value of *key* to the given *value*.\n  #\n  # ```\n  # h = {} of String => String\n  # h[\"foo\"] = \"bar\"\n  # h[\"foo\"] # => \"bar\"\n  # ```\n  def []=(key : K, value : V) : V\n    upsert(key, value)\n    value\n  end\n\n  # Sets the value of *key* to the given *value*.\n  #\n  # If a value already exists for *key*, that (old) value is returned.\n  # Otherwise the given block is invoked with *key* and its value is returned.\n  #\n  # ```\n  # h = {} of Int32 => String\n  # h.put(1, \"one\") { \"didn't exist\" } # => \"didn't exist\"\n  # h.put(1, \"uno\") { \"didn't exist\" } # => \"one\"\n  # h.put(2, \"two\") { |key| key.to_s } # => \"2\"\n  # h                                  # => {1 => \"one\", 2 => \"two\"}\n  # ```\n  def put(key : K, value : V, &)\n    updated_entry = upsert(key, value)\n    updated_entry ? updated_entry.value : yield key\n  end\n\n  # Sets the value of *key* to the given *value*, unless a value for *key*\n  # already exists.\n  #\n  # If a value already exists for *key*, that (old) value is returned.\n  # Otherwise *value* is returned.\n  #\n  # ```\n  # h = {} of Int32 => String\n  # h.put_if_absent(1, \"one\") # => \"one\"\n  # h.put_if_absent(1, \"uno\") # => \"one\"\n  # h.put_if_absent(2, \"two\") # => \"two\"\n  # h                         # => {1 => \"one\", 2 => \"two\"}\n  # ```\n  def put_if_absent(key : K, value : V) : V\n    put_if_absent(key) { value }\n  end\n\n  # Sets the value of *key* to the value returned by the given block, unless a\n  # value for *key* already exists.\n  #\n  # If a value already exists for *key*, that (old) value is returned.\n  # Otherwise the given block is invoked with *key* and its value is returned.\n  #\n  # ```\n  # h = {} of Int32 => Array(String)\n  # h.put_if_absent(1) { |key| [key.to_s] } # => [\"1\"]\n  # h.put_if_absent(1) { [] of String }     # => [\"1\"]\n  # h.put_if_absent(2) { |key| [key.to_s] } # => [\"2\"]\n  # h                                       # => {1 => [\"1\"], 2 => [\"2\"]}\n  # ```\n  #\n  # `hash.put_if_absent(key) { value }` is a more performant alternative to\n  # `hash[key] ||= value` that also works correctly when the hash may contain\n  # falsey values.\n  def put_if_absent(key : K, & : K -> V) : V\n    if entry = find_entry(key)\n      entry.value\n    else\n      value = yield key\n      upsert(key, value)\n      value\n    end\n  end\n\n  # Updates the current value of *key* with the value returned by the given block\n  # (the current value is used as input for the block).\n  #\n  # If no entry for *key* is present, but there's a default value (or default block)\n  # then that default value is used as input for the given block.\n  #\n  # If no entry for *key* is present and the hash has no default value, it raises `KeyError`.\n  #\n  # It returns the value used as input for the given block\n  # (ie. the old value if key present, or the default value)\n  #\n  # ```\n  # h = {\"a\" => 0, \"b\" => 1}\n  # h.update(\"b\") { |v| v + 41 } # => 1\n  # h[\"b\"]                       # => 42\n  #\n  # h = Hash(String, Int32).new(40)\n  # h.update(\"foo\") { |v| v + 2 } # => 40\n  # h[\"foo\"]                      # => 42\n  #\n  # h = {} of String => Int32\n  # h.update(\"a\") { 42 } # raises KeyError\n  # ```\n  #\n  # See `#transform_values!` for updating *all* the values.\n  def update(key : K, & : V -> V) : V\n    if entry_index = find_entry_with_index(key)\n      entry, index = entry_index\n      set_entry(index, Entry(K, V).new(entry.hash, entry.key, yield entry.value))\n      entry.value\n    elsif block = @block\n      default_value = block.call(self, key)\n      upsert(key, yield default_value)\n      default_value\n    else\n      raise KeyError.new \"Missing hash key: #{key.inspect}\"\n    end\n  end\n\n  # Returns the value for the key given by *key*.\n  # If not found, returns the default value given by `Hash.new`, otherwise raises `KeyError`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h[\"foo\"] # => \"bar\"\n  #\n  # h = Hash(String, String).new(\"bar\")\n  # h[\"foo\"] # => \"bar\"\n  #\n  # h = Hash(String, String).new { \"bar\" }\n  # h[\"foo\"] # => \"bar\"\n  #\n  # h = Hash(String, String).new\n  # h[\"foo\"] # raises KeyError\n  # ```\n  def [](key)\n    fetch(key) do\n      if block = @block\n        unless key.is_a?(K)\n          raise KeyError.new \"Invalid key type: expected #{K}, got #{key.class}\"\n        end\n\n        block.call(self, key)\n      else\n        raise KeyError.new \"Missing hash key: #{key.inspect}\"\n      end\n    end\n  end\n\n  # Returns the value for the key given by *key*.\n  # If not found, returns `nil`. This ignores the default value set by `Hash.new`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h[\"foo\"]? # => \"bar\"\n  # h[\"bar\"]? # => nil\n  #\n  # h = Hash(String, String).new(\"bar\")\n  # h[\"foo\"]? # => nil\n  # ```\n  def []?(key)\n    fetch(key, nil)\n  end\n\n  # Traverses the depth of a structure and returns the value.\n  # Returns `nil` if not found.\n  #\n  # ```\n  # h = {\"a\" => {\"b\" => [10, 20, 30]}}\n  # h.dig? \"a\", \"b\" # => [10, 20, 30]\n  # h.dig? \"a\", \"x\" # => nil\n  # ```\n  def dig?(key : K, *subkeys)\n    if (value = self[key]?) && value.responds_to?(:dig?)\n      value.dig?(*subkeys)\n    end\n  end\n\n  # :nodoc:\n  def dig?(key : K)\n    self[key]?\n  end\n\n  # Traverses the depth of a structure and returns the value, otherwise\n  # raises `KeyError`.\n  #\n  # ```\n  # h = {\"a\" => {\"b\" => [10, 20, 30]}}\n  # h.dig \"a\", \"b\" # => [10, 20, 30]\n  # h.dig \"a\", \"x\" # raises KeyError\n  # ```\n  def dig(key : K, *subkeys)\n    if (value = self[key]) && value.responds_to?(:dig)\n      return value.dig(*subkeys)\n    end\n    raise KeyError.new \"Hash value not diggable for key: #{key.inspect}\"\n  end\n\n  # :nodoc:\n  def dig(key : K)\n    self[key]\n  end\n\n  # Returns `true` when key given by *key* exists, otherwise `false`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.has_key?(\"foo\") # => true\n  # h.has_key?(\"bar\") # => false\n  # ```\n  def has_key?(key) : Bool\n    !!find_entry(key)\n  end\n\n  # Returns `true` when value given by *value* exists, otherwise `false`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.has_value?(\"foo\") # => false\n  # h.has_value?(\"bar\") # => true\n  # ```\n  def has_value?(val) : Bool\n    each_value do |value|\n      return true if value == val\n    end\n    false\n  end\n\n  # Returns the value for the key given by *key*, or when not found the value given by *default*.\n  # This ignores the default value set by `Hash.new`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.fetch(\"foo\", \"foo\") # => \"bar\"\n  # h.fetch(\"bar\", \"foo\") # => \"foo\"\n  # ```\n  def fetch(key, default)\n    fetch(key) { default }\n  end\n\n  # Returns the value for the key given by *key*, or when not found calls the given block with the key.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.fetch(\"foo\") { \"default value\" }  # => \"bar\"\n  # h.fetch(\"bar\") { \"default value\" }  # => \"default value\"\n  # h.fetch(\"bar\") { |key| key.upcase } # => \"BAR\"\n  # ```\n  def fetch(key, &)\n    entry = find_entry(key)\n    entry ? entry.value : yield key\n  end\n\n  # Returns a tuple populated with the values of the given *keys*, with the same order.\n  # Raises if a key is not found.\n  #\n  # ```\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.values_at(\"a\", \"c\") # => {1, 3}\n  # ```\n  def values_at(*keys : K)\n    keys.map { |index| self[index] }\n  end\n\n  # Returns a key with the given *value*, else raises `KeyError`.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # hash.key_for(\"bar\")    # => \"foo\"\n  # hash.key_for(\"qux\")    # => \"baz\"\n  # hash.key_for(\"foobar\") # raises KeyError (Missing hash key for value: foobar)\n  # ```\n  def key_for(value) : K\n    key_for(value) { raise KeyError.new \"Missing hash key for value: #{value}\" }\n  end\n\n  # Returns a key with the given *value*, else `nil`.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # hash.key_for?(\"bar\")    # => \"foo\"\n  # hash.key_for?(\"qux\")    # => \"baz\"\n  # hash.key_for?(\"foobar\") # => nil\n  # ```\n  def key_for?(value) : K?\n    key_for(value) { nil }\n  end\n\n  # Returns a key with the given *value*, else yields *value* with the given block.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\"}\n  # hash.key_for(\"bar\") { |value| value.upcase } # => \"foo\"\n  # hash.key_for(\"qux\") { |value| value.upcase } # => \"QUX\"\n  # ```\n  def key_for(value, &)\n    each do |k, v|\n      return k if v == value\n    end\n    yield value\n  end\n\n  # Deletes the key-value pair and returns the value, otherwise returns `nil`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.delete(\"foo\")     # => \"bar\"\n  # h.fetch(\"foo\", nil) # => nil\n  # ```\n  def delete(key) : V?\n    delete(key) { nil }\n  end\n\n  # Deletes the key-value pair and returns the value, else yields *key* with given block.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.delete(\"foo\") { |key| \"#{key} not found\" } # => \"bar\"\n  # h.fetch(\"foo\", nil)                          # => nil\n  # h.delete(\"baz\") { |key| \"#{key} not found\" } # => \"baz not found\"\n  # ```\n  def delete(key, &)\n    entry = delete_impl(key)\n    entry ? entry.value : yield key\n  end\n\n  # Returns `true` when hash contains no key-value pairs.\n  #\n  # ```\n  # h = Hash(String, String).new\n  # h.empty? # => true\n  #\n  # h = {\"foo\" => \"bar\"}\n  # h.empty? # => false\n  # ```\n  def empty? : Bool\n    @size == 0\n  end\n\n  # Calls the given block for each key-value pair and passes in the key and the value.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  #\n  # h.each do |key, value|\n  #   key   # => \"foo\"\n  #   value # => \"bar\"\n  # end\n  #\n  # h.each do |key_and_value|\n  #   key_and_value # => {\"foo\", \"bar\"}\n  # end\n  # ```\n  #\n  # The enumeration follows the order the keys were inserted.\n  def each(& : {K, V} ->) : Nil\n    each_entry_with_index do |entry, i|\n      yield({entry.key, entry.value})\n    end\n  end\n\n  # Returns an iterator over the hash entries.\n  # Which behaves like an `Iterator` returning a `Tuple` consisting of the key and value types.\n  #\n  # ```\n  # hsh = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # iterator = hsh.each\n  #\n  # iterator.next # => {\"foo\", \"bar\"}\n  # iterator.next # => {\"baz\", \"qux\"}\n  # ```\n  #\n  # The enumeration follows the order the keys were inserted.\n  def each\n    EntryIterator(K, V).new(self)\n  end\n\n  # Calls the given block for each key-value pair and passes in the key.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.each_key do |key|\n  #   key # => \"foo\"\n  # end\n  # ```\n  #\n  # The enumeration follows the order the keys were inserted.\n  def each_key(& : K ->)\n    each do |key, value|\n      yield key\n    end\n  end\n\n  # Returns an iterator over the hash keys.\n  # Which behaves like an `Iterator` consisting of the key's types.\n  #\n  # ```\n  # hsh = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # iterator = hsh.each_key\n  #\n  # key = iterator.next\n  # key # => \"foo\"\n  #\n  # key = iterator.next\n  # key # => \"baz\"\n  # ```\n  #\n  # The enumeration follows the order the keys were inserted.\n  def each_key\n    KeyIterator(K, V).new(self)\n  end\n\n  # Calls the given block for each key-value pair and passes in the value.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.each_value do |value|\n  #   value # => \"bar\"\n  # end\n  # ```\n  #\n  # The enumeration follows the order the keys were inserted.\n  def each_value(& : V ->)\n    each do |key, value|\n      yield value\n    end\n  end\n\n  # Returns an iterator over the hash values.\n  # Which behaves like an `Iterator` consisting of the value's types.\n  #\n  # ```\n  # hsh = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # iterator = hsh.each_value\n  #\n  # value = iterator.next\n  # value # => \"bar\"\n  #\n  # value = iterator.next\n  # value # => \"qux\"\n  # ```\n  #\n  # The enumeration follows the order the keys were inserted.\n  def each_value\n    ValueIterator(K, V).new(self)\n  end\n\n  # Returns a new `Array` with all the keys.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\", \"baz\" => \"bar\"}\n  # h.keys # => [\"foo\", \"baz\"]\n  # ```\n  def keys : Array(K)\n    to_a_impl &.key\n  end\n\n  # Returns only the values as an `Array`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # h.values # => [\"bar\", \"qux\"]\n  # ```\n  def values : Array(V)\n    to_a_impl &.value\n  end\n\n  # Returns a new `Hash` with the keys and values of this hash and *other* combined.\n  # A value in *other* takes precedence over the one in this hash.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\"}\n  # hash.merge({\"baz\" => \"qux\"})\n  # # => {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # hash\n  # # => {\"foo\" => \"bar\"}\n  # ```\n  def merge(other : Hash(L, W)) : Hash(K | L, V | W) forall L, W\n    # Don't retain @block as far as key type may be changed and retained @block\n    # will not be compatible with this new type.\n    hash = Hash(K | L, V | W).new\n    hash.compare_by_identity if compare_by_identity?\n    hash.merge! self\n    hash.merge! other\n    hash\n  end\n\n  def merge(other : Hash(L, W), & : L, V, W -> V | W) : Hash(K | L, V | W) forall L, W\n    # Don't retain @block as far as key type may be changed and retained @block\n    # will not be compatible with this new type.\n    hash = Hash(K | L, V | W).new\n    hash.compare_by_identity if compare_by_identity?\n    hash.merge! self\n    hash.merge!(other) { |k, v1, v2| yield k, v1, v2 }\n    hash\n  end\n\n  # Similar to `#merge`, but the receiver is modified.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\"}\n  # hash.merge!({\"baz\" => \"qux\"})\n  # hash # => {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # ```\n  def merge!(other : Hash) : self\n    other.merge_into!(self)\n    self\n  end\n\n  # Adds the contents of *other* to this hash.\n  # If a key exists in both hashes, the given block is called to determine the value to be used.\n  # The block arguments are the key, the value in `self` and the value in *other*.\n  #\n  # ```\n  # hash = {\"a\" => 100, \"b\" => 200}\n  # other = {\"b\" => 254, \"c\" => 300}\n  # hash.merge!(other) { |key, v1, v2| v1 + v2 }\n  # hash # => {\"a\" => 100, \"b\" => 454, \"c\" => 300}\n  # ```\n  def merge!(other : Hash, &) : self\n    other.merge_into!(self) { |k, v1, v2| yield k, v1, v2 }\n    self\n  end\n\n  protected def merge_into!(other : Hash(K2, V2)) forall K2, V2\n    {% unless K2 >= K && V2 >= V %}\n      {% raise \"#{Hash(K, V)} can't be merged into #{Hash(K2, V2)}\" %}\n    {% end %}\n\n    each do |k, v|\n      other[k] = v\n    end\n  end\n\n  protected def merge_into!(other : Hash(K2, V2), & : K, V2, V -> V2) forall K2, V2\n    {% unless K2 >= K && V2 >= V %}\n      {% raise \"#{Hash(K, V)} can't be merged into #{Hash(K2, V2)}\" %}\n    {% end %}\n\n    each do |k, v|\n      entry = other.find_entry(k)\n      other[k] = entry ? yield(k, entry.value, v) : v\n    end\n  end\n\n  # Returns a new hash consisting of entries for which the block is truthy.\n  # ```\n  # h = {\"a\" => 100, \"b\" => 200, \"c\" => 300}\n  # h.select { |k, v| k > \"a\" } # => {\"b\" => 200, \"c\" => 300}\n  # h.select { |k, v| v < 200 } # => {\"a\" => 100}\n  # ```\n  def select(& : K, V ->) : Hash(K, V)\n    reject { |k, v| !yield(k, v) }\n  end\n\n  # Equivalent to `Hash#select` but makes modification on the current object rather than returning a new one. Returns `self`.\n  def select!(& : K, V ->) : self\n    reject! { |k, v| !yield(k, v) }\n  end\n\n  # Returns a new hash consisting of entries for which the block is falsey.\n  # ```\n  # h = {\"a\" => 100, \"b\" => 200, \"c\" => 300}\n  # h.reject { |k, v| k > \"a\" } # => {\"a\" => 100}\n  # h.reject { |k, v| v < 200 } # => {\"b\" => 200, \"c\" => 300}\n  # ```\n  def reject(& : K, V ->) : Hash(K, V)\n    object = {} of K => V\n    object.compare_by_identity if compare_by_identity?\n\n    each_with_object(object) do |(k, v), memo|\n      memo[k] = v unless yield k, v\n    end\n  end\n\n  # Equivalent to `Hash#reject`, but makes modification on the current object rather than returning a new one. Returns `self`.\n  def reject!(& : K, V -> _)\n    # No indices allocated yet so we won't need `DELETED_INDEX` yet\n    if @indices.null?\n      each_entry_with_index do |entry, index|\n        if yield(entry.key, entry.value)\n          delete_entry_and_update_counts(index)\n        end\n      end\n    else\n      each_entry_with_index do |entry, index|\n        if yield(entry.key, entry.value)\n          delete_index(index_for_entry_index(index))\n          delete_entry_and_update_counts(index)\n        end\n      end\n    end\n    self\n  end\n\n  # Returns a new `Hash` without the given keys.\n  #\n  # ```\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.reject([\"a\", \"c\"]) # => {\"b\" => 2, \"d\" => 4}\n  # ```\n  def reject(keys : Enumerable) : Hash(K, V)\n    object = {} of K => V\n    object.compare_by_identity if compare_by_identity?\n\n    each_entry_with_index do |entry, _|\n      object[entry.key] = entry.value unless keys.includes?(entry.key)\n    end\n\n    object\n  end\n\n  # Returns a new `Hash` without the given keys.\n  #\n  # ```\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.reject(\"a\", \"c\") # => {\"b\" => 2, \"d\" => 4}\n  # ```\n  def reject(*keys) : Hash(K, V)\n    reject(keys)\n  end\n\n  # Removes a list of keys out of hash.\n  #\n  # ```\n  # hash = {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}\n  # hash.reject!([\"a\", \"c\"]) # => {\"b\" => 2, \"d\" => 4}\n  # hash                     # => {\"b\" => 2, \"d\" => 4}\n  # ```\n  def reject!(keys : Enumerable) : self\n    keys.each { |k| delete(k) }\n    self\n  end\n\n  # Removes a list of keys out of hash.\n  #\n  # ```\n  # hash = {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}\n  # hash.reject!(\"a\", \"c\") # => {\"b\" => 2, \"d\" => 4}\n  # hash                   # => {\"b\" => 2, \"d\" => 4}\n  # ```\n  def reject!(*keys) : self\n    reject!(keys)\n  end\n\n  # Returns a new `Hash` with the given keys.\n  #\n  # ```\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select({\"a\", \"c\"})    # => {\"a\" => 1, \"c\" => 3}\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select(\"a\", \"c\")      # => {\"a\" => 1, \"c\" => 3}\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select([\"a\", \"c\"])    # => {\"a\" => 1, \"c\" => 3}\n  # {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select(Set{\"a\", \"c\"}) # => {\"a\" => 1, \"c\" => 3}\n  # ```\n  def select(keys : Enumerable) : Hash(K, V)\n    object = {} of K => V\n    object.compare_by_identity if compare_by_identity?\n\n    keys.each_with_object(object) do |k, memo|\n      entry = find_entry(k)\n      memo[k] = entry.value if entry\n    end\n  end\n\n  # :ditto:\n  def select(*keys) : Hash(K, V)\n    self.select(keys)\n  end\n\n  # Removes every element except the given ones.\n  #\n  # ```\n  # h1 = {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select!({\"a\", \"c\"})\n  # h2 = {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select!(\"a\", \"c\")\n  # h3 = {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select!([\"a\", \"c\"])\n  # h4 = {\"a\" => 1, \"b\" => 2, \"c\" => 3, \"d\" => 4}.select!(Set{\"a\", \"c\"})\n  # h1 == h2 == h3 == h4 # => true\n  # h1                   # => {\"a\" => 1, \"c\" => 3}\n  # ```\n  def select!(keys : Indexable) : self\n    each_key { |k| delete(k) unless k.in?(keys) }\n    self\n  end\n\n  # :ditto:\n  def select!(keys : Enumerable) : self\n    # Convert enumerable to a set to prevent exhaustion of elements\n    key_set = keys.to_set\n    each_key { |k| delete(k) unless k.in?(key_set) }\n    self\n  end\n\n  # :ditto:\n  def select!(*keys) : self\n    select!(keys)\n  end\n\n  # Returns new `Hash` without `nil` values.\n  #\n  # ```\n  # hash = {\"hello\" => \"world\", \"foo\" => nil}\n  # hash.compact # => {\"hello\" => \"world\"}\n  # ```\n  def compact\n    # Don't retain @block as far as #compact may change value type, e.g.\n    # (String | Nil) will become String.\n    object = {} of K => typeof(self.first_value.not_nil!)\n\n    object.compare_by_identity if compare_by_identity?\n\n    each_with_object(object) do |(key, value), memo|\n      memo[key] = value unless value.nil?\n    end\n  end\n\n  # Removes all `nil` value from `self`. Returns `self`.\n  #\n  # ```\n  # hash = {\"hello\" => \"world\", \"foo\" => nil}\n  # hash.compact! # => {\"hello\" => \"world\"}\n  # ```\n  def compact! : self\n    reject! { |key, value| value.nil? }\n  end\n\n  # Returns a new hash with all keys converted using the block operation.\n  # The block can change a type of keys.\n  # The block yields the key and value.\n  #\n  # ```\n  # hash = {:a => 1, :b => 2, :c => 3}\n  # hash.transform_keys { |key| key.to_s }                # => {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n  # hash.transform_keys { |key, value| key.to_s * value } # => {\"a\" => 1, \"bb\" => 2, \"ccc\" => 3}\n  # ```\n  def transform_keys(& : K, V -> K2) : Hash(K2, V) forall K2\n    copy = Hash(K2, V).new(initial_capacity: entries_capacity)\n    each_with_object(copy) do |(key, value), memo|\n      memo[yield(key, value)] = value\n    end\n  end\n\n  # Destructively transforms all keys using a block. Same as transform_keys but modifies in place.\n  # The block cannot change a type of keys.\n  # The block yields the key and value.\n  #\n  # ```\n  # hash = {\"a\" => 1, \"b\" => 2, \"c\" => 3}\n  # hash.transform_keys! { |key| key.upcase }\n  # hash # => {\"A\" => 1, \"B\" => 2, \"C\" => 3}\n  # hash.transform_keys! { |key, value| key * value }\n  # hash # => {\"a\" => 1, \"bb\" => 2, \"ccc\" => 3}\n  # ```\n  def transform_keys!(& : K, V -> K) : self\n    copy = transform_keys { |k, v| (yield k, v).as(K) }\n    initialize_dup_entries(copy) # we need only to copy the buffer\n    initialize_copy_non_entries_vars(copy)\n    self\n  end\n\n  # Returns a new hash with the results of running block once for every value.\n  # The block can change a type of values.\n  # The block yields the value and key.\n  #\n  # ```\n  # hash = {:a => 1, :b => 2, :c => 3}\n  # hash.transform_values { |value| value + 1 }             # => {:a => 2, :b => 3, :c => 4}\n  # hash.transform_values { |value, key| \"#{key}#{value}\" } # => {:a => \"a1\", :b => \"b2\", :c => \"c3\"}\n  # ```\n  def transform_values(& : V, K -> V2) : Hash(K, V2) forall V2\n    copy = Hash(K, V2).new(initial_capacity: entries_capacity)\n    copy.compare_by_identity if compare_by_identity?\n\n    each_with_object(copy) do |(key, value), memo|\n      memo[key] = yield(value, key)\n    end\n  end\n\n  # Destructively transforms all values using a block. Same as transform_values but modifies in place.\n  # The block cannot change a type of values.\n  # The block yields the value and key.\n  #\n  # ```\n  # hash = {:a => 1, :b => 2, :c => 3}\n  # hash.transform_values! { |value| value + 1 }\n  # hash # => {:a => 2, :b => 3, :c => 4}\n  # hash.transform_values! { |value, key| value + key.to_s[0].ord }\n  # hash # => {:a => 99, :b => 101, :c => 103}\n  # ```\n  # See `#update` for updating a *single* value.\n  def transform_values!(& : V, K -> V) : self\n    each_entry_with_index do |entry, i|\n      new_value = yield entry.value, entry.key\n      set_entry(i, Entry(K, V).new(entry.hash, entry.key, new_value))\n    end\n    self\n  end\n\n  # Zips two arrays into a `Hash`, taking keys from *ary1* and values from *ary2*.\n  #\n  # ```\n  # Hash.zip([\"key1\", \"key2\", \"key3\"], [\"value1\", \"value2\", \"value3\"])\n  # # => {\"key1\" => \"value1\", \"key2\" => \"value2\", \"key3\" => \"value3\"}\n  # ```\n  def self.zip(ary1 : Array(K), ary2 : Array(V))\n    hash = {} of K => V\n    ary1.each_with_index do |key, i|\n      hash[key] = ary2[i]\n    end\n    hash\n  end\n\n  # Returns the first key in the hash.\n  def first_key : K\n    entry = first_entry?\n    entry ? entry.key : raise \"Can't get first key of empty Hash\"\n  end\n\n  # Returns the first key if it exists, or returns `nil`.\n  #\n  # ```\n  # hash = {\"foo1\" => \"bar1\", \"foz2\" => \"baz2\"}\n  # hash.first_key? # => \"foo1\"\n  # hash.clear\n  # hash.first_key? # => nil\n  # ```\n  def first_key? : K?\n    first_entry?.try &.key\n  end\n\n  # Returns the first value in the hash.\n  def first_value : V\n    entry = first_entry?\n    entry ? entry.value : raise \"Can't get first value of empty Hash\"\n  end\n\n  # Returns the first value if it exists, or returns `nil`.\n  #\n  # ```\n  # hash = {\"foo1\" => \"bar1\", \"foz2\" => \"baz2\"}\n  # hash.first_value? # => \"bar1\"\n  # hash.clear\n  # hash.first_value? # => nil\n  # ```\n  def first_value? : V?\n    first_entry?.try &.value\n  end\n\n  # Returns the last key in the hash.\n  def last_key : K\n    entry = last_entry?\n    entry ? entry.key : raise \"Can't get last key of empty Hash\"\n  end\n\n  # Returns the last key if it exists, or returns `nil`.\n  #\n  # ```\n  # hash = {\"foo1\" => \"bar1\", \"foz2\" => \"baz2\"}\n  # hash.last_key? # => \"foz2\"\n  # hash.clear\n  # hash.last_key? # => nil\n  # ```\n  def last_key? : K?\n    last_entry?.try &.key\n  end\n\n  # Returns the last value in the hash.\n  def last_value : V\n    entry = last_entry?\n    entry ? entry.value : raise \"Can't get last value of empty Hash\"\n  end\n\n  # Returns the last value if it exists, or returns `nil`.\n  #\n  # ```\n  # hash = {\"foo1\" => \"bar1\", \"foz2\" => \"baz2\"}\n  # hash.last_value? # => \"baz2\"\n  # hash.clear\n  # hash.last_value? # => nil\n  # ```\n  def last_value? : V?\n    last_entry?.try &.value\n  end\n\n  # Deletes and returns the first key-value pair in the hash,\n  # or raises `IndexError` if the hash is empty.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # hash.shift # => {\"foo\", \"bar\"}\n  # hash       # => {\"baz\" => \"qux\"}\n  #\n  # hash = {} of String => String\n  # hash.shift # raises IndexError\n  # ```\n  def shift : {K, V}\n    shift { raise IndexError.new }\n  end\n\n  # Same as `#shift`, but returns `nil` if the hash is empty.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # hash.shift? # => {\"foo\", \"bar\"}\n  # hash        # => {\"baz\" => \"qux\"}\n  #\n  # hash = {} of String => String\n  # hash.shift? # => nil\n  # ```\n  def shift? : {K, V}?\n    shift { nil }\n  end\n\n  # Deletes and returns the first key-value pair in the hash.\n  # Yields to the given block if the hash is empty.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\", \"baz\" => \"qux\"}\n  # hash.shift { true } # => {\"foo\", \"bar\"}\n  # hash                # => {\"baz\" => \"qux\"}\n  #\n  # hash = {} of String => String\n  # hash.shift { true } # => true\n  # hash                # => {}\n  # ```\n  def shift(&)\n    first_entry = first_entry?\n    if first_entry\n      delete_index(index_for_entry_index(@first)) unless @indices.null?\n      delete_entry_and_update_counts(@first)\n      {first_entry.key, first_entry.value}\n    else\n      yield\n    end\n  end\n\n  # Empties a `Hash` and returns it.\n  #\n  # ```\n  # hash = {\"foo\" => \"bar\"}\n  # hash.clear # => {}\n  # ```\n  def clear : self\n    clear_impl\n    self\n  end\n\n  # Compares with *other*. Returns `true` if all key-value pairs are the same.\n  def ==(other : Hash) : Bool\n    return false unless size == other.size\n    each do |key, value|\n      entry = other.find_entry(key)\n      return false unless entry && entry.value == value\n    end\n    true\n  end\n\n  # Returns `true` if `self` is a subset of *other*.\n  def proper_subset_of?(other : Hash) : Bool\n    return false if other.size <= size\n    all? do |key, value|\n      other_value = other.fetch(key) { return false }\n      other_value == value\n    end\n  end\n\n  # Returns `true` if `self` is a subset of *other* or equals to *other*.\n  def subset_of?(other : Hash) : Bool\n    return false if other.size < size\n    all? do |key, value|\n      other_value = other.fetch(key) { return false }\n      other_value == value\n    end\n  end\n\n  # Returns `true` if *other* is a subset of `self`.\n  def superset_of?(other : Hash) : Bool\n    other.subset_of?(self)\n  end\n\n  # Returns `true` if *other* is a subset of `self` or equals to `self`.\n  def proper_superset_of?(other : Hash) : Bool\n    other.proper_subset_of?(self)\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    # The hash value must be the same regardless of the\n    # order of the keys.\n    result = hasher.result\n\n    each do |key, value|\n      copy = hasher\n      copy = key.hash(copy)\n      copy = value.hash(copy)\n      result &+= copy.result\n    end\n\n    result.hash(hasher)\n  end\n\n  # Duplicates a `Hash`.\n  #\n  # ```\n  # hash_a = {\"foo\" => \"bar\"}\n  # hash_b = hash_a.dup\n  # hash_b.merge!({\"baz\" => \"qux\"})\n  # hash_a # => {\"foo\" => \"bar\"}\n  # ```\n  def dup : Hash(K, V)\n    hash = Hash(K, V).new\n    hash.initialize_dup(self)\n    hash\n  end\n\n  # Similar to `#dup`, but duplicates the values as well.\n  #\n  # ```\n  # hash_a = {\"foobar\" => {\"foo\" => \"bar\"}}\n  # hash_b = hash_a.clone\n  # hash_b[\"foobar\"][\"foo\"] = \"baz\"\n  # hash_a # => {\"foobar\" => {\"foo\" => \"bar\"}}\n  # ```\n  def clone : Hash(K, V)\n    {% if V == ::Bool || V == ::Char || V == ::String || V == ::Symbol || V < ::Number::Primitive %}\n      clone = Hash(K, V).new\n      clone.initialize_clone(self)\n      clone\n    {% else %}\n      exec_recursive_clone do |hash|\n        clone = Hash(K, V).new\n        hash[object_id] = clone.object_id\n        clone.initialize_clone(self)\n        clone\n      end\n    {% end %}\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  # Converts to a `String`.\n  #\n  # ```\n  # h = {\"foo\" => \"bar\"}\n  # h.to_s       # => \"{\\\"foo\\\" => \\\"bar\\\"}\"\n  # h.to_s.class # => String\n  # ```\n  def to_s(io : IO) : Nil\n    executed = exec_recursive(:to_s) do\n      needs_padding = curly_like?(self.first_key?)\n\n      io << '{'\n      io << ' ' if needs_padding\n      found_one = false\n      each do |key, value|\n        io << \", \" if found_one\n        key.inspect(io)\n        io << \" => \"\n        value.inspect(io)\n        found_one = true\n      end\n      io << ' ' if needs_padding\n      io << '}'\n    end\n    io << \"{...}\" unless executed\n  end\n\n  private def curly_like?(object)\n    case object\n    when Hash, Tuple, NamedTuple\n      true\n    else\n      false\n    end\n  end\n\n  def pretty_print(pp) : Nil\n    executed = exec_recursive(:pretty_print) do\n      pp.list(\"{\", self, \"}\") do |key, value|\n        pp.group do\n          key.pretty_print(pp)\n          pp.text \" =>\"\n          pp.nest do\n            pp.breakable\n            value.pretty_print(pp)\n          end\n        end\n      end\n    end\n    pp.text \"{...}\" unless executed\n  end\n\n  # Returns an `Array` of `Tuple(K, V)` with key and values belonging to this Hash.\n  #\n  # ```\n  # h = {1 => 'a', 2 => 'b', 3 => 'c'}\n  # h.to_a # => [{1, 'a'}, {2, 'b'}, {3, 'c'}]\n  # ```\n  #\n  # The order of the array follows the order the keys were inserted in the Hash.\n  def to_a : Array({K, V})\n    super\n  end\n\n  # Returns an `Array` with the results of running *block* against tuples with key and values\n  # belonging to this Hash.\n  #\n  # ```\n  # h = {\"first_name\" => \"foo\", \"last_name\" => \"bar\"}\n  # h.to_a { |_k, v| v.capitalize } # => [\"Foo\", \"Bar\"]\n  # ```\n  #\n  # The order of the array follows the order the keys were inserted in the Hash.\n  def to_a(&block : {K, V} -> U) : Array(U) forall U\n    to_a_impl do |entry|\n      yield ({entry.key, entry.value})\n    end\n  end\n\n  private def to_a_impl(& : Entry(K, V) -> U) forall U\n    index = @first\n    if @first == @deleted_count\n      # If the deleted count equals the first element offset it\n      # means that the only deleted elements are in (0...@first)\n      # and so all the next ones are non-deleted.\n      Array(U).new(size) do |i|\n        value = yield get_entry(index)\n        index += 1\n        value\n      end\n    else\n      Array(U).new(size) do |i|\n        entry = get_entry(index)\n        while entry.deleted?\n          index += 1\n          entry = get_entry(index)\n        end\n        index += 1\n        yield entry\n      end\n    end\n  end\n\n  # Returns `self`.\n  def to_h : self\n    self\n  end\n\n  # Rebuilds the hash table based on the current keys.\n  #\n  # When using mutable data types as keys, modifying a key after it was inserted\n  # into the `Hash` may lead to undefined behaviour. This method re-indexes the\n  # hash using the current keys.\n  def rehash : Nil\n    do_compaction(rehash: true)\n  end\n\n  # Inverts keys and values. If there are duplicated values, the last key becomes the new value.\n  #\n  # ```\n  # {\"foo\" => \"bar\"}.invert                 # => {\"bar\" => \"foo\"}\n  # {\"foo\" => \"bar\", \"baz\" => \"bar\"}.invert # => {\"bar\" => \"baz\"}\n  # ```\n  def invert : Hash(V, K)\n    hash = Hash(V, K).new(initial_capacity: @size)\n    self.each do |k, v|\n      hash[v] = k\n    end\n    hash\n  end\n\n  # :nodoc:\n  struct Entry(K, V)\n    getter key, value, hash\n\n    def initialize(@hash : UInt32, @key : K, @value : V)\n    end\n\n    def deleted? : Bool\n      @hash == 0_u32\n    end\n\n    def clone\n      Entry(K, V).new(hash, key, value.clone)\n    end\n  end\n\n  private module BaseIterator\n    def initialize(@hash)\n      @index = @hash.@first\n    end\n\n    def base_next(&)\n      while true\n        if @index < @hash.entries_size\n          entry = @hash.entries[@index]\n          if entry.deleted?\n            @index += 1\n          else\n            value = yield entry\n            @index += 1\n            return value\n          end\n        else\n          return stop\n        end\n      end\n    end\n  end\n\n  private class EntryIterator(K, V)\n    include BaseIterator\n    include Iterator({K, V})\n\n    @hash : Hash(K, V)\n    @index : Int32\n\n    def next\n      base_next { |entry| {entry.key, entry.value} }\n    end\n  end\n\n  private class KeyIterator(K, V)\n    include BaseIterator\n    include Iterator(K)\n\n    @hash : Hash(K, V)\n    @index : Int32\n\n    def next\n      base_next &.key\n    end\n  end\n\n  private class ValueIterator(K, V)\n    include BaseIterator\n    include Iterator(V)\n\n    @hash : Hash(K, V)\n    @index : Int32\n\n    def next\n      base_next &.value\n    end\n  end\nend\n"
  },
  {
    "path": "src/html/entities.cr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_html_entities.cr\n#\n# DO NOT EDIT\n\nmodule HTML\n  # :nodoc:\n  SINGLE_CHAR_ENTITIES = {\n    \"AElig\".to_slice                           => '\\u{0000C6}',\n    \"AMP\".to_slice                             => '\\u{000026}',\n    \"Aacute\".to_slice                          => '\\u{0000C1}',\n    \"Abreve\".to_slice                          => '\\u{000102}',\n    \"Acirc\".to_slice                           => '\\u{0000C2}',\n    \"Acy\".to_slice                             => '\\u{000410}',\n    \"Afr\".to_slice                             => '\\u{01D504}',\n    \"Agrave\".to_slice                          => '\\u{0000C0}',\n    \"Alpha\".to_slice                           => '\\u{000391}',\n    \"Amacr\".to_slice                           => '\\u{000100}',\n    \"And\".to_slice                             => '\\u{002A53}',\n    \"Aogon\".to_slice                           => '\\u{000104}',\n    \"Aopf\".to_slice                            => '\\u{01D538}',\n    \"ApplyFunction\".to_slice                   => '\\u{002061}',\n    \"Aring\".to_slice                           => '\\u{0000C5}',\n    \"Ascr\".to_slice                            => '\\u{01D49C}',\n    \"Assign\".to_slice                          => '\\u{002254}',\n    \"Atilde\".to_slice                          => '\\u{0000C3}',\n    \"Auml\".to_slice                            => '\\u{0000C4}',\n    \"Backslash\".to_slice                       => '\\u{002216}',\n    \"Barv\".to_slice                            => '\\u{002AE7}',\n    \"Barwed\".to_slice                          => '\\u{002306}',\n    \"Bcy\".to_slice                             => '\\u{000411}',\n    \"Because\".to_slice                         => '\\u{002235}',\n    \"Bernoullis\".to_slice                      => '\\u{00212C}',\n    \"Beta\".to_slice                            => '\\u{000392}',\n    \"Bfr\".to_slice                             => '\\u{01D505}',\n    \"Bopf\".to_slice                            => '\\u{01D539}',\n    \"Breve\".to_slice                           => '\\u{0002D8}',\n    \"Bscr\".to_slice                            => '\\u{00212C}',\n    \"Bumpeq\".to_slice                          => '\\u{00224E}',\n    \"CHcy\".to_slice                            => '\\u{000427}',\n    \"COPY\".to_slice                            => '\\u{0000A9}',\n    \"Cacute\".to_slice                          => '\\u{000106}',\n    \"Cap\".to_slice                             => '\\u{0022D2}',\n    \"CapitalDifferentialD\".to_slice            => '\\u{002145}',\n    \"Cayleys\".to_slice                         => '\\u{00212D}',\n    \"Ccaron\".to_slice                          => '\\u{00010C}',\n    \"Ccedil\".to_slice                          => '\\u{0000C7}',\n    \"Ccirc\".to_slice                           => '\\u{000108}',\n    \"Cconint\".to_slice                         => '\\u{002230}',\n    \"Cdot\".to_slice                            => '\\u{00010A}',\n    \"Cedilla\".to_slice                         => '\\u{0000B8}',\n    \"CenterDot\".to_slice                       => '\\u{0000B7}',\n    \"Cfr\".to_slice                             => '\\u{00212D}',\n    \"Chi\".to_slice                             => '\\u{0003A7}',\n    \"CircleDot\".to_slice                       => '\\u{002299}',\n    \"CircleMinus\".to_slice                     => '\\u{002296}',\n    \"CirclePlus\".to_slice                      => '\\u{002295}',\n    \"CircleTimes\".to_slice                     => '\\u{002297}',\n    \"ClockwiseContourIntegral\".to_slice        => '\\u{002232}',\n    \"CloseCurlyDoubleQuote\".to_slice           => '\\u{00201D}',\n    \"CloseCurlyQuote\".to_slice                 => '\\u{002019}',\n    \"Colon\".to_slice                           => '\\u{002237}',\n    \"Colone\".to_slice                          => '\\u{002A74}',\n    \"Congruent\".to_slice                       => '\\u{002261}',\n    \"Conint\".to_slice                          => '\\u{00222F}',\n    \"ContourIntegral\".to_slice                 => '\\u{00222E}',\n    \"Copf\".to_slice                            => '\\u{002102}',\n    \"Coproduct\".to_slice                       => '\\u{002210}',\n    \"CounterClockwiseContourIntegral\".to_slice => '\\u{002233}',\n    \"Cross\".to_slice                           => '\\u{002A2F}',\n    \"Cscr\".to_slice                            => '\\u{01D49E}',\n    \"Cup\".to_slice                             => '\\u{0022D3}',\n    \"CupCap\".to_slice                          => '\\u{00224D}',\n    \"DD\".to_slice                              => '\\u{002145}',\n    \"DDotrahd\".to_slice                        => '\\u{002911}',\n    \"DJcy\".to_slice                            => '\\u{000402}',\n    \"DScy\".to_slice                            => '\\u{000405}',\n    \"DZcy\".to_slice                            => '\\u{00040F}',\n    \"Dagger\".to_slice                          => '\\u{002021}',\n    \"Darr\".to_slice                            => '\\u{0021A1}',\n    \"Dashv\".to_slice                           => '\\u{002AE4}',\n    \"Dcaron\".to_slice                          => '\\u{00010E}',\n    \"Dcy\".to_slice                             => '\\u{000414}',\n    \"Del\".to_slice                             => '\\u{002207}',\n    \"Delta\".to_slice                           => '\\u{000394}',\n    \"Dfr\".to_slice                             => '\\u{01D507}',\n    \"DiacriticalAcute\".to_slice                => '\\u{0000B4}',\n    \"DiacriticalDot\".to_slice                  => '\\u{0002D9}',\n    \"DiacriticalDoubleAcute\".to_slice          => '\\u{0002DD}',\n    \"DiacriticalGrave\".to_slice                => '\\u{000060}',\n    \"DiacriticalTilde\".to_slice                => '\\u{0002DC}',\n    \"Diamond\".to_slice                         => '\\u{0022C4}',\n    \"DifferentialD\".to_slice                   => '\\u{002146}',\n    \"Dopf\".to_slice                            => '\\u{01D53B}',\n    \"Dot\".to_slice                             => '\\u{0000A8}',\n    \"DotDot\".to_slice                          => '\\u{0020DC}',\n    \"DotEqual\".to_slice                        => '\\u{002250}',\n    \"DoubleContourIntegral\".to_slice           => '\\u{00222F}',\n    \"DoubleDot\".to_slice                       => '\\u{0000A8}',\n    \"DoubleDownArrow\".to_slice                 => '\\u{0021D3}',\n    \"DoubleLeftArrow\".to_slice                 => '\\u{0021D0}',\n    \"DoubleLeftRightArrow\".to_slice            => '\\u{0021D4}',\n    \"DoubleLeftTee\".to_slice                   => '\\u{002AE4}',\n    \"DoubleLongLeftArrow\".to_slice             => '\\u{0027F8}',\n    \"DoubleLongLeftRightArrow\".to_slice        => '\\u{0027FA}',\n    \"DoubleLongRightArrow\".to_slice            => '\\u{0027F9}',\n    \"DoubleRightArrow\".to_slice                => '\\u{0021D2}',\n    \"DoubleRightTee\".to_slice                  => '\\u{0022A8}',\n    \"DoubleUpArrow\".to_slice                   => '\\u{0021D1}',\n    \"DoubleUpDownArrow\".to_slice               => '\\u{0021D5}',\n    \"DoubleVerticalBar\".to_slice               => '\\u{002225}',\n    \"DownArrow\".to_slice                       => '\\u{002193}',\n    \"DownArrowBar\".to_slice                    => '\\u{002913}',\n    \"DownArrowUpArrow\".to_slice                => '\\u{0021F5}',\n    \"DownBreve\".to_slice                       => '\\u{000311}',\n    \"DownLeftRightVector\".to_slice             => '\\u{002950}',\n    \"DownLeftTeeVector\".to_slice               => '\\u{00295E}',\n    \"DownLeftVector\".to_slice                  => '\\u{0021BD}',\n    \"DownLeftVectorBar\".to_slice               => '\\u{002956}',\n    \"DownRightTeeVector\".to_slice              => '\\u{00295F}',\n    \"DownRightVector\".to_slice                 => '\\u{0021C1}',\n    \"DownRightVectorBar\".to_slice              => '\\u{002957}',\n    \"DownTee\".to_slice                         => '\\u{0022A4}',\n    \"DownTeeArrow\".to_slice                    => '\\u{0021A7}',\n    \"Downarrow\".to_slice                       => '\\u{0021D3}',\n    \"Dscr\".to_slice                            => '\\u{01D49F}',\n    \"Dstrok\".to_slice                          => '\\u{000110}',\n    \"ENG\".to_slice                             => '\\u{00014A}',\n    \"ETH\".to_slice                             => '\\u{0000D0}',\n    \"Eacute\".to_slice                          => '\\u{0000C9}',\n    \"Ecaron\".to_slice                          => '\\u{00011A}',\n    \"Ecirc\".to_slice                           => '\\u{0000CA}',\n    \"Ecy\".to_slice                             => '\\u{00042D}',\n    \"Edot\".to_slice                            => '\\u{000116}',\n    \"Efr\".to_slice                             => '\\u{01D508}',\n    \"Egrave\".to_slice                          => '\\u{0000C8}',\n    \"Element\".to_slice                         => '\\u{002208}',\n    \"Emacr\".to_slice                           => '\\u{000112}',\n    \"EmptySmallSquare\".to_slice                => '\\u{0025FB}',\n    \"EmptyVerySmallSquare\".to_slice            => '\\u{0025AB}',\n    \"Eogon\".to_slice                           => '\\u{000118}',\n    \"Eopf\".to_slice                            => '\\u{01D53C}',\n    \"Epsilon\".to_slice                         => '\\u{000395}',\n    \"Equal\".to_slice                           => '\\u{002A75}',\n    \"EqualTilde\".to_slice                      => '\\u{002242}',\n    \"Equilibrium\".to_slice                     => '\\u{0021CC}',\n    \"Escr\".to_slice                            => '\\u{002130}',\n    \"Esim\".to_slice                            => '\\u{002A73}',\n    \"Eta\".to_slice                             => '\\u{000397}',\n    \"Euml\".to_slice                            => '\\u{0000CB}',\n    \"Exists\".to_slice                          => '\\u{002203}',\n    \"ExponentialE\".to_slice                    => '\\u{002147}',\n    \"Fcy\".to_slice                             => '\\u{000424}',\n    \"Ffr\".to_slice                             => '\\u{01D509}',\n    \"FilledSmallSquare\".to_slice               => '\\u{0025FC}',\n    \"FilledVerySmallSquare\".to_slice           => '\\u{0025AA}',\n    \"Fopf\".to_slice                            => '\\u{01D53D}',\n    \"ForAll\".to_slice                          => '\\u{002200}',\n    \"Fouriertrf\".to_slice                      => '\\u{002131}',\n    \"Fscr\".to_slice                            => '\\u{002131}',\n    \"GJcy\".to_slice                            => '\\u{000403}',\n    \"GT\".to_slice                              => '\\u{00003E}',\n    \"Gamma\".to_slice                           => '\\u{000393}',\n    \"Gammad\".to_slice                          => '\\u{0003DC}',\n    \"Gbreve\".to_slice                          => '\\u{00011E}',\n    \"Gcedil\".to_slice                          => '\\u{000122}',\n    \"Gcirc\".to_slice                           => '\\u{00011C}',\n    \"Gcy\".to_slice                             => '\\u{000413}',\n    \"Gdot\".to_slice                            => '\\u{000120}',\n    \"Gfr\".to_slice                             => '\\u{01D50A}',\n    \"Gg\".to_slice                              => '\\u{0022D9}',\n    \"Gopf\".to_slice                            => '\\u{01D53E}',\n    \"GreaterEqual\".to_slice                    => '\\u{002265}',\n    \"GreaterEqualLess\".to_slice                => '\\u{0022DB}',\n    \"GreaterFullEqual\".to_slice                => '\\u{002267}',\n    \"GreaterGreater\".to_slice                  => '\\u{002AA2}',\n    \"GreaterLess\".to_slice                     => '\\u{002277}',\n    \"GreaterSlantEqual\".to_slice               => '\\u{002A7E}',\n    \"GreaterTilde\".to_slice                    => '\\u{002273}',\n    \"Gscr\".to_slice                            => '\\u{01D4A2}',\n    \"Gt\".to_slice                              => '\\u{00226B}',\n    \"HARDcy\".to_slice                          => '\\u{00042A}',\n    \"Hacek\".to_slice                           => '\\u{0002C7}',\n    \"Hat\".to_slice                             => '\\u{00005E}',\n    \"Hcirc\".to_slice                           => '\\u{000124}',\n    \"Hfr\".to_slice                             => '\\u{00210C}',\n    \"HilbertSpace\".to_slice                    => '\\u{00210B}',\n    \"Hopf\".to_slice                            => '\\u{00210D}',\n    \"HorizontalLine\".to_slice                  => '\\u{002500}',\n    \"Hscr\".to_slice                            => '\\u{00210B}',\n    \"Hstrok\".to_slice                          => '\\u{000126}',\n    \"HumpDownHump\".to_slice                    => '\\u{00224E}',\n    \"HumpEqual\".to_slice                       => '\\u{00224F}',\n    \"IEcy\".to_slice                            => '\\u{000415}',\n    \"IJlig\".to_slice                           => '\\u{000132}',\n    \"IOcy\".to_slice                            => '\\u{000401}',\n    \"Iacute\".to_slice                          => '\\u{0000CD}',\n    \"Icirc\".to_slice                           => '\\u{0000CE}',\n    \"Icy\".to_slice                             => '\\u{000418}',\n    \"Idot\".to_slice                            => '\\u{000130}',\n    \"Ifr\".to_slice                             => '\\u{002111}',\n    \"Igrave\".to_slice                          => '\\u{0000CC}',\n    \"Im\".to_slice                              => '\\u{002111}',\n    \"Imacr\".to_slice                           => '\\u{00012A}',\n    \"ImaginaryI\".to_slice                      => '\\u{002148}',\n    \"Implies\".to_slice                         => '\\u{0021D2}',\n    \"Int\".to_slice                             => '\\u{00222C}',\n    \"Integral\".to_slice                        => '\\u{00222B}',\n    \"Intersection\".to_slice                    => '\\u{0022C2}',\n    \"InvisibleComma\".to_slice                  => '\\u{002063}',\n    \"InvisibleTimes\".to_slice                  => '\\u{002062}',\n    \"Iogon\".to_slice                           => '\\u{00012E}',\n    \"Iopf\".to_slice                            => '\\u{01D540}',\n    \"Iota\".to_slice                            => '\\u{000399}',\n    \"Iscr\".to_slice                            => '\\u{002110}',\n    \"Itilde\".to_slice                          => '\\u{000128}',\n    \"Iukcy\".to_slice                           => '\\u{000406}',\n    \"Iuml\".to_slice                            => '\\u{0000CF}',\n    \"Jcirc\".to_slice                           => '\\u{000134}',\n    \"Jcy\".to_slice                             => '\\u{000419}',\n    \"Jfr\".to_slice                             => '\\u{01D50D}',\n    \"Jopf\".to_slice                            => '\\u{01D541}',\n    \"Jscr\".to_slice                            => '\\u{01D4A5}',\n    \"Jsercy\".to_slice                          => '\\u{000408}',\n    \"Jukcy\".to_slice                           => '\\u{000404}',\n    \"KHcy\".to_slice                            => '\\u{000425}',\n    \"KJcy\".to_slice                            => '\\u{00040C}',\n    \"Kappa\".to_slice                           => '\\u{00039A}',\n    \"Kcedil\".to_slice                          => '\\u{000136}',\n    \"Kcy\".to_slice                             => '\\u{00041A}',\n    \"Kfr\".to_slice                             => '\\u{01D50E}',\n    \"Kopf\".to_slice                            => '\\u{01D542}',\n    \"Kscr\".to_slice                            => '\\u{01D4A6}',\n    \"LJcy\".to_slice                            => '\\u{000409}',\n    \"LT\".to_slice                              => '\\u{00003C}',\n    \"Lacute\".to_slice                          => '\\u{000139}',\n    \"Lambda\".to_slice                          => '\\u{00039B}',\n    \"Lang\".to_slice                            => '\\u{0027EA}',\n    \"Laplacetrf\".to_slice                      => '\\u{002112}',\n    \"Larr\".to_slice                            => '\\u{00219E}',\n    \"Lcaron\".to_slice                          => '\\u{00013D}',\n    \"Lcedil\".to_slice                          => '\\u{00013B}',\n    \"Lcy\".to_slice                             => '\\u{00041B}',\n    \"LeftAngleBracket\".to_slice                => '\\u{0027E8}',\n    \"LeftArrow\".to_slice                       => '\\u{002190}',\n    \"LeftArrowBar\".to_slice                    => '\\u{0021E4}',\n    \"LeftArrowRightArrow\".to_slice             => '\\u{0021C6}',\n    \"LeftCeiling\".to_slice                     => '\\u{002308}',\n    \"LeftDoubleBracket\".to_slice               => '\\u{0027E6}',\n    \"LeftDownTeeVector\".to_slice               => '\\u{002961}',\n    \"LeftDownVector\".to_slice                  => '\\u{0021C3}',\n    \"LeftDownVectorBar\".to_slice               => '\\u{002959}',\n    \"LeftFloor\".to_slice                       => '\\u{00230A}',\n    \"LeftRightArrow\".to_slice                  => '\\u{002194}',\n    \"LeftRightVector\".to_slice                 => '\\u{00294E}',\n    \"LeftTee\".to_slice                         => '\\u{0022A3}',\n    \"LeftTeeArrow\".to_slice                    => '\\u{0021A4}',\n    \"LeftTeeVector\".to_slice                   => '\\u{00295A}',\n    \"LeftTriangle\".to_slice                    => '\\u{0022B2}',\n    \"LeftTriangleBar\".to_slice                 => '\\u{0029CF}',\n    \"LeftTriangleEqual\".to_slice               => '\\u{0022B4}',\n    \"LeftUpDownVector\".to_slice                => '\\u{002951}',\n    \"LeftUpTeeVector\".to_slice                 => '\\u{002960}',\n    \"LeftUpVector\".to_slice                    => '\\u{0021BF}',\n    \"LeftUpVectorBar\".to_slice                 => '\\u{002958}',\n    \"LeftVector\".to_slice                      => '\\u{0021BC}',\n    \"LeftVectorBar\".to_slice                   => '\\u{002952}',\n    \"Leftarrow\".to_slice                       => '\\u{0021D0}',\n    \"Leftrightarrow\".to_slice                  => '\\u{0021D4}',\n    \"LessEqualGreater\".to_slice                => '\\u{0022DA}',\n    \"LessFullEqual\".to_slice                   => '\\u{002266}',\n    \"LessGreater\".to_slice                     => '\\u{002276}',\n    \"LessLess\".to_slice                        => '\\u{002AA1}',\n    \"LessSlantEqual\".to_slice                  => '\\u{002A7D}',\n    \"LessTilde\".to_slice                       => '\\u{002272}',\n    \"Lfr\".to_slice                             => '\\u{01D50F}',\n    \"Ll\".to_slice                              => '\\u{0022D8}',\n    \"Lleftarrow\".to_slice                      => '\\u{0021DA}',\n    \"Lmidot\".to_slice                          => '\\u{00013F}',\n    \"LongLeftArrow\".to_slice                   => '\\u{0027F5}',\n    \"LongLeftRightArrow\".to_slice              => '\\u{0027F7}',\n    \"LongRightArrow\".to_slice                  => '\\u{0027F6}',\n    \"Longleftarrow\".to_slice                   => '\\u{0027F8}',\n    \"Longleftrightarrow\".to_slice              => '\\u{0027FA}',\n    \"Longrightarrow\".to_slice                  => '\\u{0027F9}',\n    \"Lopf\".to_slice                            => '\\u{01D543}',\n    \"LowerLeftArrow\".to_slice                  => '\\u{002199}',\n    \"LowerRightArrow\".to_slice                 => '\\u{002198}',\n    \"Lscr\".to_slice                            => '\\u{002112}',\n    \"Lsh\".to_slice                             => '\\u{0021B0}',\n    \"Lstrok\".to_slice                          => '\\u{000141}',\n    \"Lt\".to_slice                              => '\\u{00226A}',\n    \"Map\".to_slice                             => '\\u{002905}',\n    \"Mcy\".to_slice                             => '\\u{00041C}',\n    \"MediumSpace\".to_slice                     => '\\u{00205F}',\n    \"Mellintrf\".to_slice                       => '\\u{002133}',\n    \"Mfr\".to_slice                             => '\\u{01D510}',\n    \"MinusPlus\".to_slice                       => '\\u{002213}',\n    \"Mopf\".to_slice                            => '\\u{01D544}',\n    \"Mscr\".to_slice                            => '\\u{002133}',\n    \"Mu\".to_slice                              => '\\u{00039C}',\n    \"NJcy\".to_slice                            => '\\u{00040A}',\n    \"Nacute\".to_slice                          => '\\u{000143}',\n    \"Ncaron\".to_slice                          => '\\u{000147}',\n    \"Ncedil\".to_slice                          => '\\u{000145}',\n    \"Ncy\".to_slice                             => '\\u{00041D}',\n    \"NegativeMediumSpace\".to_slice             => '\\u{00200B}',\n    \"NegativeThickSpace\".to_slice              => '\\u{00200B}',\n    \"NegativeThinSpace\".to_slice               => '\\u{00200B}',\n    \"NegativeVeryThinSpace\".to_slice           => '\\u{00200B}',\n    \"NestedGreaterGreater\".to_slice            => '\\u{00226B}',\n    \"NestedLessLess\".to_slice                  => '\\u{00226A}',\n    \"NewLine\".to_slice                         => '\\u{00000A}',\n    \"Nfr\".to_slice                             => '\\u{01D511}',\n    \"NoBreak\".to_slice                         => '\\u{002060}',\n    \"NonBreakingSpace\".to_slice                => '\\u{0000A0}',\n    \"Nopf\".to_slice                            => '\\u{002115}',\n    \"Not\".to_slice                             => '\\u{002AEC}',\n    \"NotCongruent\".to_slice                    => '\\u{002262}',\n    \"NotCupCap\".to_slice                       => '\\u{00226D}',\n    \"NotDoubleVerticalBar\".to_slice            => '\\u{002226}',\n    \"NotElement\".to_slice                      => '\\u{002209}',\n    \"NotEqual\".to_slice                        => '\\u{002260}',\n    \"NotExists\".to_slice                       => '\\u{002204}',\n    \"NotGreater\".to_slice                      => '\\u{00226F}',\n    \"NotGreaterEqual\".to_slice                 => '\\u{002271}',\n    \"NotGreaterLess\".to_slice                  => '\\u{002279}',\n    \"NotGreaterTilde\".to_slice                 => '\\u{002275}',\n    \"NotLeftTriangle\".to_slice                 => '\\u{0022EA}',\n    \"NotLeftTriangleEqual\".to_slice            => '\\u{0022EC}',\n    \"NotLess\".to_slice                         => '\\u{00226E}',\n    \"NotLessEqual\".to_slice                    => '\\u{002270}',\n    \"NotLessGreater\".to_slice                  => '\\u{002278}',\n    \"NotLessTilde\".to_slice                    => '\\u{002274}',\n    \"NotPrecedes\".to_slice                     => '\\u{002280}',\n    \"NotPrecedesSlantEqual\".to_slice           => '\\u{0022E0}',\n    \"NotReverseElement\".to_slice               => '\\u{00220C}',\n    \"NotRightTriangle\".to_slice                => '\\u{0022EB}',\n    \"NotRightTriangleEqual\".to_slice           => '\\u{0022ED}',\n    \"NotSquareSubsetEqual\".to_slice            => '\\u{0022E2}',\n    \"NotSquareSupersetEqual\".to_slice          => '\\u{0022E3}',\n    \"NotSubsetEqual\".to_slice                  => '\\u{002288}',\n    \"NotSucceeds\".to_slice                     => '\\u{002281}',\n    \"NotSucceedsSlantEqual\".to_slice           => '\\u{0022E1}',\n    \"NotSupersetEqual\".to_slice                => '\\u{002289}',\n    \"NotTilde\".to_slice                        => '\\u{002241}',\n    \"NotTildeEqual\".to_slice                   => '\\u{002244}',\n    \"NotTildeFullEqual\".to_slice               => '\\u{002247}',\n    \"NotTildeTilde\".to_slice                   => '\\u{002249}',\n    \"NotVerticalBar\".to_slice                  => '\\u{002224}',\n    \"Nscr\".to_slice                            => '\\u{01D4A9}',\n    \"Ntilde\".to_slice                          => '\\u{0000D1}',\n    \"Nu\".to_slice                              => '\\u{00039D}',\n    \"OElig\".to_slice                           => '\\u{000152}',\n    \"Oacute\".to_slice                          => '\\u{0000D3}',\n    \"Ocirc\".to_slice                           => '\\u{0000D4}',\n    \"Ocy\".to_slice                             => '\\u{00041E}',\n    \"Odblac\".to_slice                          => '\\u{000150}',\n    \"Ofr\".to_slice                             => '\\u{01D512}',\n    \"Ograve\".to_slice                          => '\\u{0000D2}',\n    \"Omacr\".to_slice                           => '\\u{00014C}',\n    \"Omega\".to_slice                           => '\\u{0003A9}',\n    \"Omicron\".to_slice                         => '\\u{00039F}',\n    \"Oopf\".to_slice                            => '\\u{01D546}',\n    \"OpenCurlyDoubleQuote\".to_slice            => '\\u{00201C}',\n    \"OpenCurlyQuote\".to_slice                  => '\\u{002018}',\n    \"Or\".to_slice                              => '\\u{002A54}',\n    \"Oscr\".to_slice                            => '\\u{01D4AA}',\n    \"Oslash\".to_slice                          => '\\u{0000D8}',\n    \"Otilde\".to_slice                          => '\\u{0000D5}',\n    \"Otimes\".to_slice                          => '\\u{002A37}',\n    \"Ouml\".to_slice                            => '\\u{0000D6}',\n    \"OverBar\".to_slice                         => '\\u{00203E}',\n    \"OverBrace\".to_slice                       => '\\u{0023DE}',\n    \"OverBracket\".to_slice                     => '\\u{0023B4}',\n    \"OverParenthesis\".to_slice                 => '\\u{0023DC}',\n    \"PartialD\".to_slice                        => '\\u{002202}',\n    \"Pcy\".to_slice                             => '\\u{00041F}',\n    \"Pfr\".to_slice                             => '\\u{01D513}',\n    \"Phi\".to_slice                             => '\\u{0003A6}',\n    \"Pi\".to_slice                              => '\\u{0003A0}',\n    \"PlusMinus\".to_slice                       => '\\u{0000B1}',\n    \"Poincareplane\".to_slice                   => '\\u{00210C}',\n    \"Popf\".to_slice                            => '\\u{002119}',\n    \"Pr\".to_slice                              => '\\u{002ABB}',\n    \"Precedes\".to_slice                        => '\\u{00227A}',\n    \"PrecedesEqual\".to_slice                   => '\\u{002AAF}',\n    \"PrecedesSlantEqual\".to_slice              => '\\u{00227C}',\n    \"PrecedesTilde\".to_slice                   => '\\u{00227E}',\n    \"Prime\".to_slice                           => '\\u{002033}',\n    \"Product\".to_slice                         => '\\u{00220F}',\n    \"Proportion\".to_slice                      => '\\u{002237}',\n    \"Proportional\".to_slice                    => '\\u{00221D}',\n    \"Pscr\".to_slice                            => '\\u{01D4AB}',\n    \"Psi\".to_slice                             => '\\u{0003A8}',\n    \"QUOT\".to_slice                            => '\\u{000022}',\n    \"Qfr\".to_slice                             => '\\u{01D514}',\n    \"Qopf\".to_slice                            => '\\u{00211A}',\n    \"Qscr\".to_slice                            => '\\u{01D4AC}',\n    \"RBarr\".to_slice                           => '\\u{002910}',\n    \"REG\".to_slice                             => '\\u{0000AE}',\n    \"Racute\".to_slice                          => '\\u{000154}',\n    \"Rang\".to_slice                            => '\\u{0027EB}',\n    \"Rarr\".to_slice                            => '\\u{0021A0}',\n    \"Rarrtl\".to_slice                          => '\\u{002916}',\n    \"Rcaron\".to_slice                          => '\\u{000158}',\n    \"Rcedil\".to_slice                          => '\\u{000156}',\n    \"Rcy\".to_slice                             => '\\u{000420}',\n    \"Re\".to_slice                              => '\\u{00211C}',\n    \"ReverseElement\".to_slice                  => '\\u{00220B}',\n    \"ReverseEquilibrium\".to_slice              => '\\u{0021CB}',\n    \"ReverseUpEquilibrium\".to_slice            => '\\u{00296F}',\n    \"Rfr\".to_slice                             => '\\u{00211C}',\n    \"Rho\".to_slice                             => '\\u{0003A1}',\n    \"RightAngleBracket\".to_slice               => '\\u{0027E9}',\n    \"RightArrow\".to_slice                      => '\\u{002192}',\n    \"RightArrowBar\".to_slice                   => '\\u{0021E5}',\n    \"RightArrowLeftArrow\".to_slice             => '\\u{0021C4}',\n    \"RightCeiling\".to_slice                    => '\\u{002309}',\n    \"RightDoubleBracket\".to_slice              => '\\u{0027E7}',\n    \"RightDownTeeVector\".to_slice              => '\\u{00295D}',\n    \"RightDownVector\".to_slice                 => '\\u{0021C2}',\n    \"RightDownVectorBar\".to_slice              => '\\u{002955}',\n    \"RightFloor\".to_slice                      => '\\u{00230B}',\n    \"RightTee\".to_slice                        => '\\u{0022A2}',\n    \"RightTeeArrow\".to_slice                   => '\\u{0021A6}',\n    \"RightTeeVector\".to_slice                  => '\\u{00295B}',\n    \"RightTriangle\".to_slice                   => '\\u{0022B3}',\n    \"RightTriangleBar\".to_slice                => '\\u{0029D0}',\n    \"RightTriangleEqual\".to_slice              => '\\u{0022B5}',\n    \"RightUpDownVector\".to_slice               => '\\u{00294F}',\n    \"RightUpTeeVector\".to_slice                => '\\u{00295C}',\n    \"RightUpVector\".to_slice                   => '\\u{0021BE}',\n    \"RightUpVectorBar\".to_slice                => '\\u{002954}',\n    \"RightVector\".to_slice                     => '\\u{0021C0}',\n    \"RightVectorBar\".to_slice                  => '\\u{002953}',\n    \"Rightarrow\".to_slice                      => '\\u{0021D2}',\n    \"Ropf\".to_slice                            => '\\u{00211D}',\n    \"RoundImplies\".to_slice                    => '\\u{002970}',\n    \"Rrightarrow\".to_slice                     => '\\u{0021DB}',\n    \"Rscr\".to_slice                            => '\\u{00211B}',\n    \"Rsh\".to_slice                             => '\\u{0021B1}',\n    \"RuleDelayed\".to_slice                     => '\\u{0029F4}',\n    \"SHCHcy\".to_slice                          => '\\u{000429}',\n    \"SHcy\".to_slice                            => '\\u{000428}',\n    \"SOFTcy\".to_slice                          => '\\u{00042C}',\n    \"Sacute\".to_slice                          => '\\u{00015A}',\n    \"Sc\".to_slice                              => '\\u{002ABC}',\n    \"Scaron\".to_slice                          => '\\u{000160}',\n    \"Scedil\".to_slice                          => '\\u{00015E}',\n    \"Scirc\".to_slice                           => '\\u{00015C}',\n    \"Scy\".to_slice                             => '\\u{000421}',\n    \"Sfr\".to_slice                             => '\\u{01D516}',\n    \"ShortDownArrow\".to_slice                  => '\\u{002193}',\n    \"ShortLeftArrow\".to_slice                  => '\\u{002190}',\n    \"ShortRightArrow\".to_slice                 => '\\u{002192}',\n    \"ShortUpArrow\".to_slice                    => '\\u{002191}',\n    \"Sigma\".to_slice                           => '\\u{0003A3}',\n    \"SmallCircle\".to_slice                     => '\\u{002218}',\n    \"Sopf\".to_slice                            => '\\u{01D54A}',\n    \"Sqrt\".to_slice                            => '\\u{00221A}',\n    \"Square\".to_slice                          => '\\u{0025A1}',\n    \"SquareIntersection\".to_slice              => '\\u{002293}',\n    \"SquareSubset\".to_slice                    => '\\u{00228F}',\n    \"SquareSubsetEqual\".to_slice               => '\\u{002291}',\n    \"SquareSuperset\".to_slice                  => '\\u{002290}',\n    \"SquareSupersetEqual\".to_slice             => '\\u{002292}',\n    \"SquareUnion\".to_slice                     => '\\u{002294}',\n    \"Sscr\".to_slice                            => '\\u{01D4AE}',\n    \"Star\".to_slice                            => '\\u{0022C6}',\n    \"Sub\".to_slice                             => '\\u{0022D0}',\n    \"Subset\".to_slice                          => '\\u{0022D0}',\n    \"SubsetEqual\".to_slice                     => '\\u{002286}',\n    \"Succeeds\".to_slice                        => '\\u{00227B}',\n    \"SucceedsEqual\".to_slice                   => '\\u{002AB0}',\n    \"SucceedsSlantEqual\".to_slice              => '\\u{00227D}',\n    \"SucceedsTilde\".to_slice                   => '\\u{00227F}',\n    \"SuchThat\".to_slice                        => '\\u{00220B}',\n    \"Sum\".to_slice                             => '\\u{002211}',\n    \"Sup\".to_slice                             => '\\u{0022D1}',\n    \"Superset\".to_slice                        => '\\u{002283}',\n    \"SupersetEqual\".to_slice                   => '\\u{002287}',\n    \"Supset\".to_slice                          => '\\u{0022D1}',\n    \"THORN\".to_slice                           => '\\u{0000DE}',\n    \"TRADE\".to_slice                           => '\\u{002122}',\n    \"TSHcy\".to_slice                           => '\\u{00040B}',\n    \"TScy\".to_slice                            => '\\u{000426}',\n    \"Tab\".to_slice                             => '\\u{000009}',\n    \"Tau\".to_slice                             => '\\u{0003A4}',\n    \"Tcaron\".to_slice                          => '\\u{000164}',\n    \"Tcedil\".to_slice                          => '\\u{000162}',\n    \"Tcy\".to_slice                             => '\\u{000422}',\n    \"Tfr\".to_slice                             => '\\u{01D517}',\n    \"Therefore\".to_slice                       => '\\u{002234}',\n    \"Theta\".to_slice                           => '\\u{000398}',\n    \"ThinSpace\".to_slice                       => '\\u{002009}',\n    \"Tilde\".to_slice                           => '\\u{00223C}',\n    \"TildeEqual\".to_slice                      => '\\u{002243}',\n    \"TildeFullEqual\".to_slice                  => '\\u{002245}',\n    \"TildeTilde\".to_slice                      => '\\u{002248}',\n    \"Topf\".to_slice                            => '\\u{01D54B}',\n    \"TripleDot\".to_slice                       => '\\u{0020DB}',\n    \"Tscr\".to_slice                            => '\\u{01D4AF}',\n    \"Tstrok\".to_slice                          => '\\u{000166}',\n    \"Uacute\".to_slice                          => '\\u{0000DA}',\n    \"Uarr\".to_slice                            => '\\u{00219F}',\n    \"Uarrocir\".to_slice                        => '\\u{002949}',\n    \"Ubrcy\".to_slice                           => '\\u{00040E}',\n    \"Ubreve\".to_slice                          => '\\u{00016C}',\n    \"Ucirc\".to_slice                           => '\\u{0000DB}',\n    \"Ucy\".to_slice                             => '\\u{000423}',\n    \"Udblac\".to_slice                          => '\\u{000170}',\n    \"Ufr\".to_slice                             => '\\u{01D518}',\n    \"Ugrave\".to_slice                          => '\\u{0000D9}',\n    \"Umacr\".to_slice                           => '\\u{00016A}',\n    \"UnderBar\".to_slice                        => '\\u{00005F}',\n    \"UnderBrace\".to_slice                      => '\\u{0023DF}',\n    \"UnderBracket\".to_slice                    => '\\u{0023B5}',\n    \"UnderParenthesis\".to_slice                => '\\u{0023DD}',\n    \"Union\".to_slice                           => '\\u{0022C3}',\n    \"UnionPlus\".to_slice                       => '\\u{00228E}',\n    \"Uogon\".to_slice                           => '\\u{000172}',\n    \"Uopf\".to_slice                            => '\\u{01D54C}',\n    \"UpArrow\".to_slice                         => '\\u{002191}',\n    \"UpArrowBar\".to_slice                      => '\\u{002912}',\n    \"UpArrowDownArrow\".to_slice                => '\\u{0021C5}',\n    \"UpDownArrow\".to_slice                     => '\\u{002195}',\n    \"UpEquilibrium\".to_slice                   => '\\u{00296E}',\n    \"UpTee\".to_slice                           => '\\u{0022A5}',\n    \"UpTeeArrow\".to_slice                      => '\\u{0021A5}',\n    \"Uparrow\".to_slice                         => '\\u{0021D1}',\n    \"Updownarrow\".to_slice                     => '\\u{0021D5}',\n    \"UpperLeftArrow\".to_slice                  => '\\u{002196}',\n    \"UpperRightArrow\".to_slice                 => '\\u{002197}',\n    \"Upsi\".to_slice                            => '\\u{0003D2}',\n    \"Upsilon\".to_slice                         => '\\u{0003A5}',\n    \"Uring\".to_slice                           => '\\u{00016E}',\n    \"Uscr\".to_slice                            => '\\u{01D4B0}',\n    \"Utilde\".to_slice                          => '\\u{000168}',\n    \"Uuml\".to_slice                            => '\\u{0000DC}',\n    \"VDash\".to_slice                           => '\\u{0022AB}',\n    \"Vbar\".to_slice                            => '\\u{002AEB}',\n    \"Vcy\".to_slice                             => '\\u{000412}',\n    \"Vdash\".to_slice                           => '\\u{0022A9}',\n    \"Vdashl\".to_slice                          => '\\u{002AE6}',\n    \"Vee\".to_slice                             => '\\u{0022C1}',\n    \"Verbar\".to_slice                          => '\\u{002016}',\n    \"Vert\".to_slice                            => '\\u{002016}',\n    \"VerticalBar\".to_slice                     => '\\u{002223}',\n    \"VerticalLine\".to_slice                    => '\\u{00007C}',\n    \"VerticalSeparator\".to_slice               => '\\u{002758}',\n    \"VerticalTilde\".to_slice                   => '\\u{002240}',\n    \"VeryThinSpace\".to_slice                   => '\\u{00200A}',\n    \"Vfr\".to_slice                             => '\\u{01D519}',\n    \"Vopf\".to_slice                            => '\\u{01D54D}',\n    \"Vscr\".to_slice                            => '\\u{01D4B1}',\n    \"Vvdash\".to_slice                          => '\\u{0022AA}',\n    \"Wcirc\".to_slice                           => '\\u{000174}',\n    \"Wedge\".to_slice                           => '\\u{0022C0}',\n    \"Wfr\".to_slice                             => '\\u{01D51A}',\n    \"Wopf\".to_slice                            => '\\u{01D54E}',\n    \"Wscr\".to_slice                            => '\\u{01D4B2}',\n    \"Xfr\".to_slice                             => '\\u{01D51B}',\n    \"Xi\".to_slice                              => '\\u{00039E}',\n    \"Xopf\".to_slice                            => '\\u{01D54F}',\n    \"Xscr\".to_slice                            => '\\u{01D4B3}',\n    \"YAcy\".to_slice                            => '\\u{00042F}',\n    \"YIcy\".to_slice                            => '\\u{000407}',\n    \"YUcy\".to_slice                            => '\\u{00042E}',\n    \"Yacute\".to_slice                          => '\\u{0000DD}',\n    \"Ycirc\".to_slice                           => '\\u{000176}',\n    \"Ycy\".to_slice                             => '\\u{00042B}',\n    \"Yfr\".to_slice                             => '\\u{01D51C}',\n    \"Yopf\".to_slice                            => '\\u{01D550}',\n    \"Yscr\".to_slice                            => '\\u{01D4B4}',\n    \"Yuml\".to_slice                            => '\\u{000178}',\n    \"ZHcy\".to_slice                            => '\\u{000416}',\n    \"Zacute\".to_slice                          => '\\u{000179}',\n    \"Zcaron\".to_slice                          => '\\u{00017D}',\n    \"Zcy\".to_slice                             => '\\u{000417}',\n    \"Zdot\".to_slice                            => '\\u{00017B}',\n    \"ZeroWidthSpace\".to_slice                  => '\\u{00200B}',\n    \"Zeta\".to_slice                            => '\\u{000396}',\n    \"Zfr\".to_slice                             => '\\u{002128}',\n    \"Zopf\".to_slice                            => '\\u{002124}',\n    \"Zscr\".to_slice                            => '\\u{01D4B5}',\n    \"aacute\".to_slice                          => '\\u{0000E1}',\n    \"abreve\".to_slice                          => '\\u{000103}',\n    \"ac\".to_slice                              => '\\u{00223E}',\n    \"acd\".to_slice                             => '\\u{00223F}',\n    \"acirc\".to_slice                           => '\\u{0000E2}',\n    \"acute\".to_slice                           => '\\u{0000B4}',\n    \"acy\".to_slice                             => '\\u{000430}',\n    \"aelig\".to_slice                           => '\\u{0000E6}',\n    \"af\".to_slice                              => '\\u{002061}',\n    \"afr\".to_slice                             => '\\u{01D51E}',\n    \"agrave\".to_slice                          => '\\u{0000E0}',\n    \"alefsym\".to_slice                         => '\\u{002135}',\n    \"aleph\".to_slice                           => '\\u{002135}',\n    \"alpha\".to_slice                           => '\\u{0003B1}',\n    \"amacr\".to_slice                           => '\\u{000101}',\n    \"amalg\".to_slice                           => '\\u{002A3F}',\n    \"amp\".to_slice                             => '\\u{000026}',\n    \"and\".to_slice                             => '\\u{002227}',\n    \"andand\".to_slice                          => '\\u{002A55}',\n    \"andd\".to_slice                            => '\\u{002A5C}',\n    \"andslope\".to_slice                        => '\\u{002A58}',\n    \"andv\".to_slice                            => '\\u{002A5A}',\n    \"ang\".to_slice                             => '\\u{002220}',\n    \"ange\".to_slice                            => '\\u{0029A4}',\n    \"angle\".to_slice                           => '\\u{002220}',\n    \"angmsd\".to_slice                          => '\\u{002221}',\n    \"angmsdaa\".to_slice                        => '\\u{0029A8}',\n    \"angmsdab\".to_slice                        => '\\u{0029A9}',\n    \"angmsdac\".to_slice                        => '\\u{0029AA}',\n    \"angmsdad\".to_slice                        => '\\u{0029AB}',\n    \"angmsdae\".to_slice                        => '\\u{0029AC}',\n    \"angmsdaf\".to_slice                        => '\\u{0029AD}',\n    \"angmsdag\".to_slice                        => '\\u{0029AE}',\n    \"angmsdah\".to_slice                        => '\\u{0029AF}',\n    \"angrt\".to_slice                           => '\\u{00221F}',\n    \"angrtvb\".to_slice                         => '\\u{0022BE}',\n    \"angrtvbd\".to_slice                        => '\\u{00299D}',\n    \"angsph\".to_slice                          => '\\u{002222}',\n    \"angst\".to_slice                           => '\\u{0000C5}',\n    \"angzarr\".to_slice                         => '\\u{00237C}',\n    \"aogon\".to_slice                           => '\\u{000105}',\n    \"aopf\".to_slice                            => '\\u{01D552}',\n    \"ap\".to_slice                              => '\\u{002248}',\n    \"apE\".to_slice                             => '\\u{002A70}',\n    \"apacir\".to_slice                          => '\\u{002A6F}',\n    \"ape\".to_slice                             => '\\u{00224A}',\n    \"apid\".to_slice                            => '\\u{00224B}',\n    \"apos\".to_slice                            => '\\u{000027}',\n    \"approx\".to_slice                          => '\\u{002248}',\n    \"approxeq\".to_slice                        => '\\u{00224A}',\n    \"aring\".to_slice                           => '\\u{0000E5}',\n    \"ascr\".to_slice                            => '\\u{01D4B6}',\n    \"ast\".to_slice                             => '\\u{00002A}',\n    \"asymp\".to_slice                           => '\\u{002248}',\n    \"asympeq\".to_slice                         => '\\u{00224D}',\n    \"atilde\".to_slice                          => '\\u{0000E3}',\n    \"auml\".to_slice                            => '\\u{0000E4}',\n    \"awconint\".to_slice                        => '\\u{002233}',\n    \"awint\".to_slice                           => '\\u{002A11}',\n    \"bNot\".to_slice                            => '\\u{002AED}',\n    \"backcong\".to_slice                        => '\\u{00224C}',\n    \"backepsilon\".to_slice                     => '\\u{0003F6}',\n    \"backprime\".to_slice                       => '\\u{002035}',\n    \"backsim\".to_slice                         => '\\u{00223D}',\n    \"backsimeq\".to_slice                       => '\\u{0022CD}',\n    \"barvee\".to_slice                          => '\\u{0022BD}',\n    \"barwed\".to_slice                          => '\\u{002305}',\n    \"barwedge\".to_slice                        => '\\u{002305}',\n    \"bbrk\".to_slice                            => '\\u{0023B5}',\n    \"bbrktbrk\".to_slice                        => '\\u{0023B6}',\n    \"bcong\".to_slice                           => '\\u{00224C}',\n    \"bcy\".to_slice                             => '\\u{000431}',\n    \"bdquo\".to_slice                           => '\\u{00201E}',\n    \"becaus\".to_slice                          => '\\u{002235}',\n    \"because\".to_slice                         => '\\u{002235}',\n    \"bemptyv\".to_slice                         => '\\u{0029B0}',\n    \"bepsi\".to_slice                           => '\\u{0003F6}',\n    \"bernou\".to_slice                          => '\\u{00212C}',\n    \"beta\".to_slice                            => '\\u{0003B2}',\n    \"beth\".to_slice                            => '\\u{002136}',\n    \"between\".to_slice                         => '\\u{00226C}',\n    \"bfr\".to_slice                             => '\\u{01D51F}',\n    \"bigcap\".to_slice                          => '\\u{0022C2}',\n    \"bigcirc\".to_slice                         => '\\u{0025EF}',\n    \"bigcup\".to_slice                          => '\\u{0022C3}',\n    \"bigodot\".to_slice                         => '\\u{002A00}',\n    \"bigoplus\".to_slice                        => '\\u{002A01}',\n    \"bigotimes\".to_slice                       => '\\u{002A02}',\n    \"bigsqcup\".to_slice                        => '\\u{002A06}',\n    \"bigstar\".to_slice                         => '\\u{002605}',\n    \"bigtriangledown\".to_slice                 => '\\u{0025BD}',\n    \"bigtriangleup\".to_slice                   => '\\u{0025B3}',\n    \"biguplus\".to_slice                        => '\\u{002A04}',\n    \"bigvee\".to_slice                          => '\\u{0022C1}',\n    \"bigwedge\".to_slice                        => '\\u{0022C0}',\n    \"bkarow\".to_slice                          => '\\u{00290D}',\n    \"blacklozenge\".to_slice                    => '\\u{0029EB}',\n    \"blacksquare\".to_slice                     => '\\u{0025AA}',\n    \"blacktriangle\".to_slice                   => '\\u{0025B4}',\n    \"blacktriangledown\".to_slice               => '\\u{0025BE}',\n    \"blacktriangleleft\".to_slice               => '\\u{0025C2}',\n    \"blacktriangleright\".to_slice              => '\\u{0025B8}',\n    \"blank\".to_slice                           => '\\u{002423}',\n    \"blk12\".to_slice                           => '\\u{002592}',\n    \"blk14\".to_slice                           => '\\u{002591}',\n    \"blk34\".to_slice                           => '\\u{002593}',\n    \"block\".to_slice                           => '\\u{002588}',\n    \"bnot\".to_slice                            => '\\u{002310}',\n    \"bopf\".to_slice                            => '\\u{01D553}',\n    \"bot\".to_slice                             => '\\u{0022A5}',\n    \"bottom\".to_slice                          => '\\u{0022A5}',\n    \"bowtie\".to_slice                          => '\\u{0022C8}',\n    \"boxDL\".to_slice                           => '\\u{002557}',\n    \"boxDR\".to_slice                           => '\\u{002554}',\n    \"boxDl\".to_slice                           => '\\u{002556}',\n    \"boxDr\".to_slice                           => '\\u{002553}',\n    \"boxH\".to_slice                            => '\\u{002550}',\n    \"boxHD\".to_slice                           => '\\u{002566}',\n    \"boxHU\".to_slice                           => '\\u{002569}',\n    \"boxHd\".to_slice                           => '\\u{002564}',\n    \"boxHu\".to_slice                           => '\\u{002567}',\n    \"boxUL\".to_slice                           => '\\u{00255D}',\n    \"boxUR\".to_slice                           => '\\u{00255A}',\n    \"boxUl\".to_slice                           => '\\u{00255C}',\n    \"boxUr\".to_slice                           => '\\u{002559}',\n    \"boxV\".to_slice                            => '\\u{002551}',\n    \"boxVH\".to_slice                           => '\\u{00256C}',\n    \"boxVL\".to_slice                           => '\\u{002563}',\n    \"boxVR\".to_slice                           => '\\u{002560}',\n    \"boxVh\".to_slice                           => '\\u{00256B}',\n    \"boxVl\".to_slice                           => '\\u{002562}',\n    \"boxVr\".to_slice                           => '\\u{00255F}',\n    \"boxbox\".to_slice                          => '\\u{0029C9}',\n    \"boxdL\".to_slice                           => '\\u{002555}',\n    \"boxdR\".to_slice                           => '\\u{002552}',\n    \"boxdl\".to_slice                           => '\\u{002510}',\n    \"boxdr\".to_slice                           => '\\u{00250C}',\n    \"boxh\".to_slice                            => '\\u{002500}',\n    \"boxhD\".to_slice                           => '\\u{002565}',\n    \"boxhU\".to_slice                           => '\\u{002568}',\n    \"boxhd\".to_slice                           => '\\u{00252C}',\n    \"boxhu\".to_slice                           => '\\u{002534}',\n    \"boxminus\".to_slice                        => '\\u{00229F}',\n    \"boxplus\".to_slice                         => '\\u{00229E}',\n    \"boxtimes\".to_slice                        => '\\u{0022A0}',\n    \"boxuL\".to_slice                           => '\\u{00255B}',\n    \"boxuR\".to_slice                           => '\\u{002558}',\n    \"boxul\".to_slice                           => '\\u{002518}',\n    \"boxur\".to_slice                           => '\\u{002514}',\n    \"boxv\".to_slice                            => '\\u{002502}',\n    \"boxvH\".to_slice                           => '\\u{00256A}',\n    \"boxvL\".to_slice                           => '\\u{002561}',\n    \"boxvR\".to_slice                           => '\\u{00255E}',\n    \"boxvh\".to_slice                           => '\\u{00253C}',\n    \"boxvl\".to_slice                           => '\\u{002524}',\n    \"boxvr\".to_slice                           => '\\u{00251C}',\n    \"bprime\".to_slice                          => '\\u{002035}',\n    \"breve\".to_slice                           => '\\u{0002D8}',\n    \"brvbar\".to_slice                          => '\\u{0000A6}',\n    \"bscr\".to_slice                            => '\\u{01D4B7}',\n    \"bsemi\".to_slice                           => '\\u{00204F}',\n    \"bsim\".to_slice                            => '\\u{00223D}',\n    \"bsime\".to_slice                           => '\\u{0022CD}',\n    \"bsol\".to_slice                            => '\\u{00005C}',\n    \"bsolb\".to_slice                           => '\\u{0029C5}',\n    \"bsolhsub\".to_slice                        => '\\u{0027C8}',\n    \"bull\".to_slice                            => '\\u{002022}',\n    \"bullet\".to_slice                          => '\\u{002022}',\n    \"bump\".to_slice                            => '\\u{00224E}',\n    \"bumpE\".to_slice                           => '\\u{002AAE}',\n    \"bumpe\".to_slice                           => '\\u{00224F}',\n    \"bumpeq\".to_slice                          => '\\u{00224F}',\n    \"cacute\".to_slice                          => '\\u{000107}',\n    \"cap\".to_slice                             => '\\u{002229}',\n    \"capand\".to_slice                          => '\\u{002A44}',\n    \"capbrcup\".to_slice                        => '\\u{002A49}',\n    \"capcap\".to_slice                          => '\\u{002A4B}',\n    \"capcup\".to_slice                          => '\\u{002A47}',\n    \"capdot\".to_slice                          => '\\u{002A40}',\n    \"caret\".to_slice                           => '\\u{002041}',\n    \"caron\".to_slice                           => '\\u{0002C7}',\n    \"ccaps\".to_slice                           => '\\u{002A4D}',\n    \"ccaron\".to_slice                          => '\\u{00010D}',\n    \"ccedil\".to_slice                          => '\\u{0000E7}',\n    \"ccirc\".to_slice                           => '\\u{000109}',\n    \"ccups\".to_slice                           => '\\u{002A4C}',\n    \"ccupssm\".to_slice                         => '\\u{002A50}',\n    \"cdot\".to_slice                            => '\\u{00010B}',\n    \"cedil\".to_slice                           => '\\u{0000B8}',\n    \"cemptyv\".to_slice                         => '\\u{0029B2}',\n    \"cent\".to_slice                            => '\\u{0000A2}',\n    \"centerdot\".to_slice                       => '\\u{0000B7}',\n    \"cfr\".to_slice                             => '\\u{01D520}',\n    \"chcy\".to_slice                            => '\\u{000447}',\n    \"check\".to_slice                           => '\\u{002713}',\n    \"checkmark\".to_slice                       => '\\u{002713}',\n    \"chi\".to_slice                             => '\\u{0003C7}',\n    \"cir\".to_slice                             => '\\u{0025CB}',\n    \"cirE\".to_slice                            => '\\u{0029C3}',\n    \"circ\".to_slice                            => '\\u{0002C6}',\n    \"circeq\".to_slice                          => '\\u{002257}',\n    \"circlearrowleft\".to_slice                 => '\\u{0021BA}',\n    \"circlearrowright\".to_slice                => '\\u{0021BB}',\n    \"circledR\".to_slice                        => '\\u{0000AE}',\n    \"circledS\".to_slice                        => '\\u{0024C8}',\n    \"circledast\".to_slice                      => '\\u{00229B}',\n    \"circledcirc\".to_slice                     => '\\u{00229A}',\n    \"circleddash\".to_slice                     => '\\u{00229D}',\n    \"cire\".to_slice                            => '\\u{002257}',\n    \"cirfnint\".to_slice                        => '\\u{002A10}',\n    \"cirmid\".to_slice                          => '\\u{002AEF}',\n    \"cirscir\".to_slice                         => '\\u{0029C2}',\n    \"clubs\".to_slice                           => '\\u{002663}',\n    \"clubsuit\".to_slice                        => '\\u{002663}',\n    \"colon\".to_slice                           => '\\u{00003A}',\n    \"colone\".to_slice                          => '\\u{002254}',\n    \"coloneq\".to_slice                         => '\\u{002254}',\n    \"comma\".to_slice                           => '\\u{00002C}',\n    \"commat\".to_slice                          => '\\u{000040}',\n    \"comp\".to_slice                            => '\\u{002201}',\n    \"compfn\".to_slice                          => '\\u{002218}',\n    \"complement\".to_slice                      => '\\u{002201}',\n    \"complexes\".to_slice                       => '\\u{002102}',\n    \"cong\".to_slice                            => '\\u{002245}',\n    \"congdot\".to_slice                         => '\\u{002A6D}',\n    \"conint\".to_slice                          => '\\u{00222E}',\n    \"copf\".to_slice                            => '\\u{01D554}',\n    \"coprod\".to_slice                          => '\\u{002210}',\n    \"copy\".to_slice                            => '\\u{0000A9}',\n    \"copysr\".to_slice                          => '\\u{002117}',\n    \"crarr\".to_slice                           => '\\u{0021B5}',\n    \"cross\".to_slice                           => '\\u{002717}',\n    \"cscr\".to_slice                            => '\\u{01D4B8}',\n    \"csub\".to_slice                            => '\\u{002ACF}',\n    \"csube\".to_slice                           => '\\u{002AD1}',\n    \"csup\".to_slice                            => '\\u{002AD0}',\n    \"csupe\".to_slice                           => '\\u{002AD2}',\n    \"ctdot\".to_slice                           => '\\u{0022EF}',\n    \"cudarrl\".to_slice                         => '\\u{002938}',\n    \"cudarrr\".to_slice                         => '\\u{002935}',\n    \"cuepr\".to_slice                           => '\\u{0022DE}',\n    \"cuesc\".to_slice                           => '\\u{0022DF}',\n    \"cularr\".to_slice                          => '\\u{0021B6}',\n    \"cularrp\".to_slice                         => '\\u{00293D}',\n    \"cup\".to_slice                             => '\\u{00222A}',\n    \"cupbrcap\".to_slice                        => '\\u{002A48}',\n    \"cupcap\".to_slice                          => '\\u{002A46}',\n    \"cupcup\".to_slice                          => '\\u{002A4A}',\n    \"cupdot\".to_slice                          => '\\u{00228D}',\n    \"cupor\".to_slice                           => '\\u{002A45}',\n    \"curarr\".to_slice                          => '\\u{0021B7}',\n    \"curarrm\".to_slice                         => '\\u{00293C}',\n    \"curlyeqprec\".to_slice                     => '\\u{0022DE}',\n    \"curlyeqsucc\".to_slice                     => '\\u{0022DF}',\n    \"curlyvee\".to_slice                        => '\\u{0022CE}',\n    \"curlywedge\".to_slice                      => '\\u{0022CF}',\n    \"curren\".to_slice                          => '\\u{0000A4}',\n    \"curvearrowleft\".to_slice                  => '\\u{0021B6}',\n    \"curvearrowright\".to_slice                 => '\\u{0021B7}',\n    \"cuvee\".to_slice                           => '\\u{0022CE}',\n    \"cuwed\".to_slice                           => '\\u{0022CF}',\n    \"cwconint\".to_slice                        => '\\u{002232}',\n    \"cwint\".to_slice                           => '\\u{002231}',\n    \"cylcty\".to_slice                          => '\\u{00232D}',\n    \"dArr\".to_slice                            => '\\u{0021D3}',\n    \"dHar\".to_slice                            => '\\u{002965}',\n    \"dagger\".to_slice                          => '\\u{002020}',\n    \"daleth\".to_slice                          => '\\u{002138}',\n    \"darr\".to_slice                            => '\\u{002193}',\n    \"dash\".to_slice                            => '\\u{002010}',\n    \"dashv\".to_slice                           => '\\u{0022A3}',\n    \"dbkarow\".to_slice                         => '\\u{00290F}',\n    \"dblac\".to_slice                           => '\\u{0002DD}',\n    \"dcaron\".to_slice                          => '\\u{00010F}',\n    \"dcy\".to_slice                             => '\\u{000434}',\n    \"dd\".to_slice                              => '\\u{002146}',\n    \"ddagger\".to_slice                         => '\\u{002021}',\n    \"ddarr\".to_slice                           => '\\u{0021CA}',\n    \"ddotseq\".to_slice                         => '\\u{002A77}',\n    \"deg\".to_slice                             => '\\u{0000B0}',\n    \"delta\".to_slice                           => '\\u{0003B4}',\n    \"demptyv\".to_slice                         => '\\u{0029B1}',\n    \"dfisht\".to_slice                          => '\\u{00297F}',\n    \"dfr\".to_slice                             => '\\u{01D521}',\n    \"dharl\".to_slice                           => '\\u{0021C3}',\n    \"dharr\".to_slice                           => '\\u{0021C2}',\n    \"diam\".to_slice                            => '\\u{0022C4}',\n    \"diamond\".to_slice                         => '\\u{0022C4}',\n    \"diamondsuit\".to_slice                     => '\\u{002666}',\n    \"diams\".to_slice                           => '\\u{002666}',\n    \"die\".to_slice                             => '\\u{0000A8}',\n    \"digamma\".to_slice                         => '\\u{0003DD}',\n    \"disin\".to_slice                           => '\\u{0022F2}',\n    \"div\".to_slice                             => '\\u{0000F7}',\n    \"divide\".to_slice                          => '\\u{0000F7}',\n    \"divideontimes\".to_slice                   => '\\u{0022C7}',\n    \"divonx\".to_slice                          => '\\u{0022C7}',\n    \"djcy\".to_slice                            => '\\u{000452}',\n    \"dlcorn\".to_slice                          => '\\u{00231E}',\n    \"dlcrop\".to_slice                          => '\\u{00230D}',\n    \"dollar\".to_slice                          => '\\u{000024}',\n    \"dopf\".to_slice                            => '\\u{01D555}',\n    \"dot\".to_slice                             => '\\u{0002D9}',\n    \"doteq\".to_slice                           => '\\u{002250}',\n    \"doteqdot\".to_slice                        => '\\u{002251}',\n    \"dotminus\".to_slice                        => '\\u{002238}',\n    \"dotplus\".to_slice                         => '\\u{002214}',\n    \"dotsquare\".to_slice                       => '\\u{0022A1}',\n    \"doublebarwedge\".to_slice                  => '\\u{002306}',\n    \"downarrow\".to_slice                       => '\\u{002193}',\n    \"downdownarrows\".to_slice                  => '\\u{0021CA}',\n    \"downharpoonleft\".to_slice                 => '\\u{0021C3}',\n    \"downharpoonright\".to_slice                => '\\u{0021C2}',\n    \"drbkarow\".to_slice                        => '\\u{002910}',\n    \"drcorn\".to_slice                          => '\\u{00231F}',\n    \"drcrop\".to_slice                          => '\\u{00230C}',\n    \"dscr\".to_slice                            => '\\u{01D4B9}',\n    \"dscy\".to_slice                            => '\\u{000455}',\n    \"dsol\".to_slice                            => '\\u{0029F6}',\n    \"dstrok\".to_slice                          => '\\u{000111}',\n    \"dtdot\".to_slice                           => '\\u{0022F1}',\n    \"dtri\".to_slice                            => '\\u{0025BF}',\n    \"dtrif\".to_slice                           => '\\u{0025BE}',\n    \"duarr\".to_slice                           => '\\u{0021F5}',\n    \"duhar\".to_slice                           => '\\u{00296F}',\n    \"dwangle\".to_slice                         => '\\u{0029A6}',\n    \"dzcy\".to_slice                            => '\\u{00045F}',\n    \"dzigrarr\".to_slice                        => '\\u{0027FF}',\n    \"eDDot\".to_slice                           => '\\u{002A77}',\n    \"eDot\".to_slice                            => '\\u{002251}',\n    \"eacute\".to_slice                          => '\\u{0000E9}',\n    \"easter\".to_slice                          => '\\u{002A6E}',\n    \"ecaron\".to_slice                          => '\\u{00011B}',\n    \"ecir\".to_slice                            => '\\u{002256}',\n    \"ecirc\".to_slice                           => '\\u{0000EA}',\n    \"ecolon\".to_slice                          => '\\u{002255}',\n    \"ecy\".to_slice                             => '\\u{00044D}',\n    \"edot\".to_slice                            => '\\u{000117}',\n    \"ee\".to_slice                              => '\\u{002147}',\n    \"efDot\".to_slice                           => '\\u{002252}',\n    \"efr\".to_slice                             => '\\u{01D522}',\n    \"eg\".to_slice                              => '\\u{002A9A}',\n    \"egrave\".to_slice                          => '\\u{0000E8}',\n    \"egs\".to_slice                             => '\\u{002A96}',\n    \"egsdot\".to_slice                          => '\\u{002A98}',\n    \"el\".to_slice                              => '\\u{002A99}',\n    \"elinters\".to_slice                        => '\\u{0023E7}',\n    \"ell\".to_slice                             => '\\u{002113}',\n    \"els\".to_slice                             => '\\u{002A95}',\n    \"elsdot\".to_slice                          => '\\u{002A97}',\n    \"emacr\".to_slice                           => '\\u{000113}',\n    \"empty\".to_slice                           => '\\u{002205}',\n    \"emptyset\".to_slice                        => '\\u{002205}',\n    \"emptyv\".to_slice                          => '\\u{002205}',\n    \"emsp\".to_slice                            => '\\u{002003}',\n    \"emsp13\".to_slice                          => '\\u{002004}',\n    \"emsp14\".to_slice                          => '\\u{002005}',\n    \"eng\".to_slice                             => '\\u{00014B}',\n    \"ensp\".to_slice                            => '\\u{002002}',\n    \"eogon\".to_slice                           => '\\u{000119}',\n    \"eopf\".to_slice                            => '\\u{01D556}',\n    \"epar\".to_slice                            => '\\u{0022D5}',\n    \"eparsl\".to_slice                          => '\\u{0029E3}',\n    \"eplus\".to_slice                           => '\\u{002A71}',\n    \"epsi\".to_slice                            => '\\u{0003B5}',\n    \"epsilon\".to_slice                         => '\\u{0003B5}',\n    \"epsiv\".to_slice                           => '\\u{0003F5}',\n    \"eqcirc\".to_slice                          => '\\u{002256}',\n    \"eqcolon\".to_slice                         => '\\u{002255}',\n    \"eqsim\".to_slice                           => '\\u{002242}',\n    \"eqslantgtr\".to_slice                      => '\\u{002A96}',\n    \"eqslantless\".to_slice                     => '\\u{002A95}',\n    \"equals\".to_slice                          => '\\u{00003D}',\n    \"equest\".to_slice                          => '\\u{00225F}',\n    \"equiv\".to_slice                           => '\\u{002261}',\n    \"equivDD\".to_slice                         => '\\u{002A78}',\n    \"eqvparsl\".to_slice                        => '\\u{0029E5}',\n    \"erDot\".to_slice                           => '\\u{002253}',\n    \"erarr\".to_slice                           => '\\u{002971}',\n    \"escr\".to_slice                            => '\\u{00212F}',\n    \"esdot\".to_slice                           => '\\u{002250}',\n    \"esim\".to_slice                            => '\\u{002242}',\n    \"eta\".to_slice                             => '\\u{0003B7}',\n    \"eth\".to_slice                             => '\\u{0000F0}',\n    \"euml\".to_slice                            => '\\u{0000EB}',\n    \"euro\".to_slice                            => '\\u{0020AC}',\n    \"excl\".to_slice                            => '\\u{000021}',\n    \"exist\".to_slice                           => '\\u{002203}',\n    \"expectation\".to_slice                     => '\\u{002130}',\n    \"exponentiale\".to_slice                    => '\\u{002147}',\n    \"fallingdotseq\".to_slice                   => '\\u{002252}',\n    \"fcy\".to_slice                             => '\\u{000444}',\n    \"female\".to_slice                          => '\\u{002640}',\n    \"ffilig\".to_slice                          => '\\u{00FB03}',\n    \"fflig\".to_slice                           => '\\u{00FB00}',\n    \"ffllig\".to_slice                          => '\\u{00FB04}',\n    \"ffr\".to_slice                             => '\\u{01D523}',\n    \"filig\".to_slice                           => '\\u{00FB01}',\n    \"flat\".to_slice                            => '\\u{00266D}',\n    \"fllig\".to_slice                           => '\\u{00FB02}',\n    \"fltns\".to_slice                           => '\\u{0025B1}',\n    \"fnof\".to_slice                            => '\\u{000192}',\n    \"fopf\".to_slice                            => '\\u{01D557}',\n    \"forall\".to_slice                          => '\\u{002200}',\n    \"fork\".to_slice                            => '\\u{0022D4}',\n    \"forkv\".to_slice                           => '\\u{002AD9}',\n    \"fpartint\".to_slice                        => '\\u{002A0D}',\n    \"frac12\".to_slice                          => '\\u{0000BD}',\n    \"frac13\".to_slice                          => '\\u{002153}',\n    \"frac14\".to_slice                          => '\\u{0000BC}',\n    \"frac15\".to_slice                          => '\\u{002155}',\n    \"frac16\".to_slice                          => '\\u{002159}',\n    \"frac18\".to_slice                          => '\\u{00215B}',\n    \"frac23\".to_slice                          => '\\u{002154}',\n    \"frac25\".to_slice                          => '\\u{002156}',\n    \"frac34\".to_slice                          => '\\u{0000BE}',\n    \"frac35\".to_slice                          => '\\u{002157}',\n    \"frac38\".to_slice                          => '\\u{00215C}',\n    \"frac45\".to_slice                          => '\\u{002158}',\n    \"frac56\".to_slice                          => '\\u{00215A}',\n    \"frac58\".to_slice                          => '\\u{00215D}',\n    \"frac78\".to_slice                          => '\\u{00215E}',\n    \"frasl\".to_slice                           => '\\u{002044}',\n    \"frown\".to_slice                           => '\\u{002322}',\n    \"fscr\".to_slice                            => '\\u{01D4BB}',\n    \"gE\".to_slice                              => '\\u{002267}',\n    \"gEl\".to_slice                             => '\\u{002A8C}',\n    \"gacute\".to_slice                          => '\\u{0001F5}',\n    \"gamma\".to_slice                           => '\\u{0003B3}',\n    \"gammad\".to_slice                          => '\\u{0003DD}',\n    \"gap\".to_slice                             => '\\u{002A86}',\n    \"gbreve\".to_slice                          => '\\u{00011F}',\n    \"gcirc\".to_slice                           => '\\u{00011D}',\n    \"gcy\".to_slice                             => '\\u{000433}',\n    \"gdot\".to_slice                            => '\\u{000121}',\n    \"ge\".to_slice                              => '\\u{002265}',\n    \"gel\".to_slice                             => '\\u{0022DB}',\n    \"geq\".to_slice                             => '\\u{002265}',\n    \"geqq\".to_slice                            => '\\u{002267}',\n    \"geqslant\".to_slice                        => '\\u{002A7E}',\n    \"ges\".to_slice                             => '\\u{002A7E}',\n    \"gescc\".to_slice                           => '\\u{002AA9}',\n    \"gesdot\".to_slice                          => '\\u{002A80}',\n    \"gesdoto\".to_slice                         => '\\u{002A82}',\n    \"gesdotol\".to_slice                        => '\\u{002A84}',\n    \"gesles\".to_slice                          => '\\u{002A94}',\n    \"gfr\".to_slice                             => '\\u{01D524}',\n    \"gg\".to_slice                              => '\\u{00226B}',\n    \"ggg\".to_slice                             => '\\u{0022D9}',\n    \"gimel\".to_slice                           => '\\u{002137}',\n    \"gjcy\".to_slice                            => '\\u{000453}',\n    \"gl\".to_slice                              => '\\u{002277}',\n    \"glE\".to_slice                             => '\\u{002A92}',\n    \"gla\".to_slice                             => '\\u{002AA5}',\n    \"glj\".to_slice                             => '\\u{002AA4}',\n    \"gnE\".to_slice                             => '\\u{002269}',\n    \"gnap\".to_slice                            => '\\u{002A8A}',\n    \"gnapprox\".to_slice                        => '\\u{002A8A}',\n    \"gne\".to_slice                             => '\\u{002A88}',\n    \"gneq\".to_slice                            => '\\u{002A88}',\n    \"gneqq\".to_slice                           => '\\u{002269}',\n    \"gnsim\".to_slice                           => '\\u{0022E7}',\n    \"gopf\".to_slice                            => '\\u{01D558}',\n    \"grave\".to_slice                           => '\\u{000060}',\n    \"gscr\".to_slice                            => '\\u{00210A}',\n    \"gsim\".to_slice                            => '\\u{002273}',\n    \"gsime\".to_slice                           => '\\u{002A8E}',\n    \"gsiml\".to_slice                           => '\\u{002A90}',\n    \"gt\".to_slice                              => '\\u{00003E}',\n    \"gtcc\".to_slice                            => '\\u{002AA7}',\n    \"gtcir\".to_slice                           => '\\u{002A7A}',\n    \"gtdot\".to_slice                           => '\\u{0022D7}',\n    \"gtlPar\".to_slice                          => '\\u{002995}',\n    \"gtquest\".to_slice                         => '\\u{002A7C}',\n    \"gtrapprox\".to_slice                       => '\\u{002A86}',\n    \"gtrarr\".to_slice                          => '\\u{002978}',\n    \"gtrdot\".to_slice                          => '\\u{0022D7}',\n    \"gtreqless\".to_slice                       => '\\u{0022DB}',\n    \"gtreqqless\".to_slice                      => '\\u{002A8C}',\n    \"gtrless\".to_slice                         => '\\u{002277}',\n    \"gtrsim\".to_slice                          => '\\u{002273}',\n    \"hArr\".to_slice                            => '\\u{0021D4}',\n    \"hairsp\".to_slice                          => '\\u{00200A}',\n    \"half\".to_slice                            => '\\u{0000BD}',\n    \"hamilt\".to_slice                          => '\\u{00210B}',\n    \"hardcy\".to_slice                          => '\\u{00044A}',\n    \"harr\".to_slice                            => '\\u{002194}',\n    \"harrcir\".to_slice                         => '\\u{002948}',\n    \"harrw\".to_slice                           => '\\u{0021AD}',\n    \"hbar\".to_slice                            => '\\u{00210F}',\n    \"hcirc\".to_slice                           => '\\u{000125}',\n    \"hearts\".to_slice                          => '\\u{002665}',\n    \"heartsuit\".to_slice                       => '\\u{002665}',\n    \"hellip\".to_slice                          => '\\u{002026}',\n    \"hercon\".to_slice                          => '\\u{0022B9}',\n    \"hfr\".to_slice                             => '\\u{01D525}',\n    \"hksearow\".to_slice                        => '\\u{002925}',\n    \"hkswarow\".to_slice                        => '\\u{002926}',\n    \"hoarr\".to_slice                           => '\\u{0021FF}',\n    \"homtht\".to_slice                          => '\\u{00223B}',\n    \"hookleftarrow\".to_slice                   => '\\u{0021A9}',\n    \"hookrightarrow\".to_slice                  => '\\u{0021AA}',\n    \"hopf\".to_slice                            => '\\u{01D559}',\n    \"horbar\".to_slice                          => '\\u{002015}',\n    \"hscr\".to_slice                            => '\\u{01D4BD}',\n    \"hslash\".to_slice                          => '\\u{00210F}',\n    \"hstrok\".to_slice                          => '\\u{000127}',\n    \"hybull\".to_slice                          => '\\u{002043}',\n    \"hyphen\".to_slice                          => '\\u{002010}',\n    \"iacute\".to_slice                          => '\\u{0000ED}',\n    \"ic\".to_slice                              => '\\u{002063}',\n    \"icirc\".to_slice                           => '\\u{0000EE}',\n    \"icy\".to_slice                             => '\\u{000438}',\n    \"iecy\".to_slice                            => '\\u{000435}',\n    \"iexcl\".to_slice                           => '\\u{0000A1}',\n    \"iff\".to_slice                             => '\\u{0021D4}',\n    \"ifr\".to_slice                             => '\\u{01D526}',\n    \"igrave\".to_slice                          => '\\u{0000EC}',\n    \"ii\".to_slice                              => '\\u{002148}',\n    \"iiiint\".to_slice                          => '\\u{002A0C}',\n    \"iiint\".to_slice                           => '\\u{00222D}',\n    \"iinfin\".to_slice                          => '\\u{0029DC}',\n    \"iiota\".to_slice                           => '\\u{002129}',\n    \"ijlig\".to_slice                           => '\\u{000133}',\n    \"imacr\".to_slice                           => '\\u{00012B}',\n    \"image\".to_slice                           => '\\u{002111}',\n    \"imagline\".to_slice                        => '\\u{002110}',\n    \"imagpart\".to_slice                        => '\\u{002111}',\n    \"imath\".to_slice                           => '\\u{000131}',\n    \"imof\".to_slice                            => '\\u{0022B7}',\n    \"imped\".to_slice                           => '\\u{0001B5}',\n    \"in\".to_slice                              => '\\u{002208}',\n    \"incare\".to_slice                          => '\\u{002105}',\n    \"infin\".to_slice                           => '\\u{00221E}',\n    \"infintie\".to_slice                        => '\\u{0029DD}',\n    \"inodot\".to_slice                          => '\\u{000131}',\n    \"int\".to_slice                             => '\\u{00222B}',\n    \"intcal\".to_slice                          => '\\u{0022BA}',\n    \"integers\".to_slice                        => '\\u{002124}',\n    \"intercal\".to_slice                        => '\\u{0022BA}',\n    \"intlarhk\".to_slice                        => '\\u{002A17}',\n    \"intprod\".to_slice                         => '\\u{002A3C}',\n    \"iocy\".to_slice                            => '\\u{000451}',\n    \"iogon\".to_slice                           => '\\u{00012F}',\n    \"iopf\".to_slice                            => '\\u{01D55A}',\n    \"iota\".to_slice                            => '\\u{0003B9}',\n    \"iprod\".to_slice                           => '\\u{002A3C}',\n    \"iquest\".to_slice                          => '\\u{0000BF}',\n    \"iscr\".to_slice                            => '\\u{01D4BE}',\n    \"isin\".to_slice                            => '\\u{002208}',\n    \"isinE\".to_slice                           => '\\u{0022F9}',\n    \"isindot\".to_slice                         => '\\u{0022F5}',\n    \"isins\".to_slice                           => '\\u{0022F4}',\n    \"isinsv\".to_slice                          => '\\u{0022F3}',\n    \"isinv\".to_slice                           => '\\u{002208}',\n    \"it\".to_slice                              => '\\u{002062}',\n    \"itilde\".to_slice                          => '\\u{000129}',\n    \"iukcy\".to_slice                           => '\\u{000456}',\n    \"iuml\".to_slice                            => '\\u{0000EF}',\n    \"jcirc\".to_slice                           => '\\u{000135}',\n    \"jcy\".to_slice                             => '\\u{000439}',\n    \"jfr\".to_slice                             => '\\u{01D527}',\n    \"jmath\".to_slice                           => '\\u{000237}',\n    \"jopf\".to_slice                            => '\\u{01D55B}',\n    \"jscr\".to_slice                            => '\\u{01D4BF}',\n    \"jsercy\".to_slice                          => '\\u{000458}',\n    \"jukcy\".to_slice                           => '\\u{000454}',\n    \"kappa\".to_slice                           => '\\u{0003BA}',\n    \"kappav\".to_slice                          => '\\u{0003F0}',\n    \"kcedil\".to_slice                          => '\\u{000137}',\n    \"kcy\".to_slice                             => '\\u{00043A}',\n    \"kfr\".to_slice                             => '\\u{01D528}',\n    \"kgreen\".to_slice                          => '\\u{000138}',\n    \"khcy\".to_slice                            => '\\u{000445}',\n    \"kjcy\".to_slice                            => '\\u{00045C}',\n    \"kopf\".to_slice                            => '\\u{01D55C}',\n    \"kscr\".to_slice                            => '\\u{01D4C0}',\n    \"lAarr\".to_slice                           => '\\u{0021DA}',\n    \"lArr\".to_slice                            => '\\u{0021D0}',\n    \"lAtail\".to_slice                          => '\\u{00291B}',\n    \"lBarr\".to_slice                           => '\\u{00290E}',\n    \"lE\".to_slice                              => '\\u{002266}',\n    \"lEg\".to_slice                             => '\\u{002A8B}',\n    \"lHar\".to_slice                            => '\\u{002962}',\n    \"lacute\".to_slice                          => '\\u{00013A}',\n    \"laemptyv\".to_slice                        => '\\u{0029B4}',\n    \"lagran\".to_slice                          => '\\u{002112}',\n    \"lambda\".to_slice                          => '\\u{0003BB}',\n    \"lang\".to_slice                            => '\\u{0027E8}',\n    \"langd\".to_slice                           => '\\u{002991}',\n    \"langle\".to_slice                          => '\\u{0027E8}',\n    \"lap\".to_slice                             => '\\u{002A85}',\n    \"laquo\".to_slice                           => '\\u{0000AB}',\n    \"larr\".to_slice                            => '\\u{002190}',\n    \"larrb\".to_slice                           => '\\u{0021E4}',\n    \"larrbfs\".to_slice                         => '\\u{00291F}',\n    \"larrfs\".to_slice                          => '\\u{00291D}',\n    \"larrhk\".to_slice                          => '\\u{0021A9}',\n    \"larrlp\".to_slice                          => '\\u{0021AB}',\n    \"larrpl\".to_slice                          => '\\u{002939}',\n    \"larrsim\".to_slice                         => '\\u{002973}',\n    \"larrtl\".to_slice                          => '\\u{0021A2}',\n    \"lat\".to_slice                             => '\\u{002AAB}',\n    \"latail\".to_slice                          => '\\u{002919}',\n    \"late\".to_slice                            => '\\u{002AAD}',\n    \"lbarr\".to_slice                           => '\\u{00290C}',\n    \"lbbrk\".to_slice                           => '\\u{002772}',\n    \"lbrace\".to_slice                          => '\\u{00007B}',\n    \"lbrack\".to_slice                          => '\\u{00005B}',\n    \"lbrke\".to_slice                           => '\\u{00298B}',\n    \"lbrksld\".to_slice                         => '\\u{00298F}',\n    \"lbrkslu\".to_slice                         => '\\u{00298D}',\n    \"lcaron\".to_slice                          => '\\u{00013E}',\n    \"lcedil\".to_slice                          => '\\u{00013C}',\n    \"lceil\".to_slice                           => '\\u{002308}',\n    \"lcub\".to_slice                            => '\\u{00007B}',\n    \"lcy\".to_slice                             => '\\u{00043B}',\n    \"ldca\".to_slice                            => '\\u{002936}',\n    \"ldquo\".to_slice                           => '\\u{00201C}',\n    \"ldquor\".to_slice                          => '\\u{00201E}',\n    \"ldrdhar\".to_slice                         => '\\u{002967}',\n    \"ldrushar\".to_slice                        => '\\u{00294B}',\n    \"ldsh\".to_slice                            => '\\u{0021B2}',\n    \"le\".to_slice                              => '\\u{002264}',\n    \"leftarrow\".to_slice                       => '\\u{002190}',\n    \"leftarrowtail\".to_slice                   => '\\u{0021A2}',\n    \"leftharpoondown\".to_slice                 => '\\u{0021BD}',\n    \"leftharpoonup\".to_slice                   => '\\u{0021BC}',\n    \"leftleftarrows\".to_slice                  => '\\u{0021C7}',\n    \"leftrightarrow\".to_slice                  => '\\u{002194}',\n    \"leftrightarrows\".to_slice                 => '\\u{0021C6}',\n    \"leftrightharpoons\".to_slice               => '\\u{0021CB}',\n    \"leftrightsquigarrow\".to_slice             => '\\u{0021AD}',\n    \"leftthreetimes\".to_slice                  => '\\u{0022CB}',\n    \"leg\".to_slice                             => '\\u{0022DA}',\n    \"leq\".to_slice                             => '\\u{002264}',\n    \"leqq\".to_slice                            => '\\u{002266}',\n    \"leqslant\".to_slice                        => '\\u{002A7D}',\n    \"les\".to_slice                             => '\\u{002A7D}',\n    \"lescc\".to_slice                           => '\\u{002AA8}',\n    \"lesdot\".to_slice                          => '\\u{002A7F}',\n    \"lesdoto\".to_slice                         => '\\u{002A81}',\n    \"lesdotor\".to_slice                        => '\\u{002A83}',\n    \"lesges\".to_slice                          => '\\u{002A93}',\n    \"lessapprox\".to_slice                      => '\\u{002A85}',\n    \"lessdot\".to_slice                         => '\\u{0022D6}',\n    \"lesseqgtr\".to_slice                       => '\\u{0022DA}',\n    \"lesseqqgtr\".to_slice                      => '\\u{002A8B}',\n    \"lessgtr\".to_slice                         => '\\u{002276}',\n    \"lesssim\".to_slice                         => '\\u{002272}',\n    \"lfisht\".to_slice                          => '\\u{00297C}',\n    \"lfloor\".to_slice                          => '\\u{00230A}',\n    \"lfr\".to_slice                             => '\\u{01D529}',\n    \"lg\".to_slice                              => '\\u{002276}',\n    \"lgE\".to_slice                             => '\\u{002A91}',\n    \"lhard\".to_slice                           => '\\u{0021BD}',\n    \"lharu\".to_slice                           => '\\u{0021BC}',\n    \"lharul\".to_slice                          => '\\u{00296A}',\n    \"lhblk\".to_slice                           => '\\u{002584}',\n    \"ljcy\".to_slice                            => '\\u{000459}',\n    \"ll\".to_slice                              => '\\u{00226A}',\n    \"llarr\".to_slice                           => '\\u{0021C7}',\n    \"llcorner\".to_slice                        => '\\u{00231E}',\n    \"llhard\".to_slice                          => '\\u{00296B}',\n    \"lltri\".to_slice                           => '\\u{0025FA}',\n    \"lmidot\".to_slice                          => '\\u{000140}',\n    \"lmoust\".to_slice                          => '\\u{0023B0}',\n    \"lmoustache\".to_slice                      => '\\u{0023B0}',\n    \"lnE\".to_slice                             => '\\u{002268}',\n    \"lnap\".to_slice                            => '\\u{002A89}',\n    \"lnapprox\".to_slice                        => '\\u{002A89}',\n    \"lne\".to_slice                             => '\\u{002A87}',\n    \"lneq\".to_slice                            => '\\u{002A87}',\n    \"lneqq\".to_slice                           => '\\u{002268}',\n    \"lnsim\".to_slice                           => '\\u{0022E6}',\n    \"loang\".to_slice                           => '\\u{0027EC}',\n    \"loarr\".to_slice                           => '\\u{0021FD}',\n    \"lobrk\".to_slice                           => '\\u{0027E6}',\n    \"longleftarrow\".to_slice                   => '\\u{0027F5}',\n    \"longleftrightarrow\".to_slice              => '\\u{0027F7}',\n    \"longmapsto\".to_slice                      => '\\u{0027FC}',\n    \"longrightarrow\".to_slice                  => '\\u{0027F6}',\n    \"looparrowleft\".to_slice                   => '\\u{0021AB}',\n    \"looparrowright\".to_slice                  => '\\u{0021AC}',\n    \"lopar\".to_slice                           => '\\u{002985}',\n    \"lopf\".to_slice                            => '\\u{01D55D}',\n    \"loplus\".to_slice                          => '\\u{002A2D}',\n    \"lotimes\".to_slice                         => '\\u{002A34}',\n    \"lowast\".to_slice                          => '\\u{002217}',\n    \"lowbar\".to_slice                          => '\\u{00005F}',\n    \"loz\".to_slice                             => '\\u{0025CA}',\n    \"lozenge\".to_slice                         => '\\u{0025CA}',\n    \"lozf\".to_slice                            => '\\u{0029EB}',\n    \"lpar\".to_slice                            => '\\u{000028}',\n    \"lparlt\".to_slice                          => '\\u{002993}',\n    \"lrarr\".to_slice                           => '\\u{0021C6}',\n    \"lrcorner\".to_slice                        => '\\u{00231F}',\n    \"lrhar\".to_slice                           => '\\u{0021CB}',\n    \"lrhard\".to_slice                          => '\\u{00296D}',\n    \"lrm\".to_slice                             => '\\u{00200E}',\n    \"lrtri\".to_slice                           => '\\u{0022BF}',\n    \"lsaquo\".to_slice                          => '\\u{002039}',\n    \"lscr\".to_slice                            => '\\u{01D4C1}',\n    \"lsh\".to_slice                             => '\\u{0021B0}',\n    \"lsim\".to_slice                            => '\\u{002272}',\n    \"lsime\".to_slice                           => '\\u{002A8D}',\n    \"lsimg\".to_slice                           => '\\u{002A8F}',\n    \"lsqb\".to_slice                            => '\\u{00005B}',\n    \"lsquo\".to_slice                           => '\\u{002018}',\n    \"lsquor\".to_slice                          => '\\u{00201A}',\n    \"lstrok\".to_slice                          => '\\u{000142}',\n    \"lt\".to_slice                              => '\\u{00003C}',\n    \"ltcc\".to_slice                            => '\\u{002AA6}',\n    \"ltcir\".to_slice                           => '\\u{002A79}',\n    \"ltdot\".to_slice                           => '\\u{0022D6}',\n    \"lthree\".to_slice                          => '\\u{0022CB}',\n    \"ltimes\".to_slice                          => '\\u{0022C9}',\n    \"ltlarr\".to_slice                          => '\\u{002976}',\n    \"ltquest\".to_slice                         => '\\u{002A7B}',\n    \"ltrPar\".to_slice                          => '\\u{002996}',\n    \"ltri\".to_slice                            => '\\u{0025C3}',\n    \"ltrie\".to_slice                           => '\\u{0022B4}',\n    \"ltrif\".to_slice                           => '\\u{0025C2}',\n    \"lurdshar\".to_slice                        => '\\u{00294A}',\n    \"luruhar\".to_slice                         => '\\u{002966}',\n    \"mDDot\".to_slice                           => '\\u{00223A}',\n    \"macr\".to_slice                            => '\\u{0000AF}',\n    \"male\".to_slice                            => '\\u{002642}',\n    \"malt\".to_slice                            => '\\u{002720}',\n    \"maltese\".to_slice                         => '\\u{002720}',\n    \"map\".to_slice                             => '\\u{0021A6}',\n    \"mapsto\".to_slice                          => '\\u{0021A6}',\n    \"mapstodown\".to_slice                      => '\\u{0021A7}',\n    \"mapstoleft\".to_slice                      => '\\u{0021A4}',\n    \"mapstoup\".to_slice                        => '\\u{0021A5}',\n    \"marker\".to_slice                          => '\\u{0025AE}',\n    \"mcomma\".to_slice                          => '\\u{002A29}',\n    \"mcy\".to_slice                             => '\\u{00043C}',\n    \"mdash\".to_slice                           => '\\u{002014}',\n    \"measuredangle\".to_slice                   => '\\u{002221}',\n    \"mfr\".to_slice                             => '\\u{01D52A}',\n    \"mho\".to_slice                             => '\\u{002127}',\n    \"micro\".to_slice                           => '\\u{0000B5}',\n    \"mid\".to_slice                             => '\\u{002223}',\n    \"midast\".to_slice                          => '\\u{00002A}',\n    \"midcir\".to_slice                          => '\\u{002AF0}',\n    \"middot\".to_slice                          => '\\u{0000B7}',\n    \"minus\".to_slice                           => '\\u{002212}',\n    \"minusb\".to_slice                          => '\\u{00229F}',\n    \"minusd\".to_slice                          => '\\u{002238}',\n    \"minusdu\".to_slice                         => '\\u{002A2A}',\n    \"mlcp\".to_slice                            => '\\u{002ADB}',\n    \"mldr\".to_slice                            => '\\u{002026}',\n    \"mnplus\".to_slice                          => '\\u{002213}',\n    \"models\".to_slice                          => '\\u{0022A7}',\n    \"mopf\".to_slice                            => '\\u{01D55E}',\n    \"mp\".to_slice                              => '\\u{002213}',\n    \"mscr\".to_slice                            => '\\u{01D4C2}',\n    \"mstpos\".to_slice                          => '\\u{00223E}',\n    \"mu\".to_slice                              => '\\u{0003BC}',\n    \"multimap\".to_slice                        => '\\u{0022B8}',\n    \"mumap\".to_slice                           => '\\u{0022B8}',\n    \"nLeftarrow\".to_slice                      => '\\u{0021CD}',\n    \"nLeftrightarrow\".to_slice                 => '\\u{0021CE}',\n    \"nRightarrow\".to_slice                     => '\\u{0021CF}',\n    \"nVDash\".to_slice                          => '\\u{0022AF}',\n    \"nVdash\".to_slice                          => '\\u{0022AE}',\n    \"nabla\".to_slice                           => '\\u{002207}',\n    \"nacute\".to_slice                          => '\\u{000144}',\n    \"nap\".to_slice                             => '\\u{002249}',\n    \"napos\".to_slice                           => '\\u{000149}',\n    \"napprox\".to_slice                         => '\\u{002249}',\n    \"natur\".to_slice                           => '\\u{00266E}',\n    \"natural\".to_slice                         => '\\u{00266E}',\n    \"naturals\".to_slice                        => '\\u{002115}',\n    \"nbsp\".to_slice                            => '\\u{0000A0}',\n    \"ncap\".to_slice                            => '\\u{002A43}',\n    \"ncaron\".to_slice                          => '\\u{000148}',\n    \"ncedil\".to_slice                          => '\\u{000146}',\n    \"ncong\".to_slice                           => '\\u{002247}',\n    \"ncup\".to_slice                            => '\\u{002A42}',\n    \"ncy\".to_slice                             => '\\u{00043D}',\n    \"ndash\".to_slice                           => '\\u{002013}',\n    \"ne\".to_slice                              => '\\u{002260}',\n    \"neArr\".to_slice                           => '\\u{0021D7}',\n    \"nearhk\".to_slice                          => '\\u{002924}',\n    \"nearr\".to_slice                           => '\\u{002197}',\n    \"nearrow\".to_slice                         => '\\u{002197}',\n    \"nequiv\".to_slice                          => '\\u{002262}',\n    \"nesear\".to_slice                          => '\\u{002928}',\n    \"nexist\".to_slice                          => '\\u{002204}',\n    \"nexists\".to_slice                         => '\\u{002204}',\n    \"nfr\".to_slice                             => '\\u{01D52B}',\n    \"nge\".to_slice                             => '\\u{002271}',\n    \"ngeq\".to_slice                            => '\\u{002271}',\n    \"ngsim\".to_slice                           => '\\u{002275}',\n    \"ngt\".to_slice                             => '\\u{00226F}',\n    \"ngtr\".to_slice                            => '\\u{00226F}',\n    \"nhArr\".to_slice                           => '\\u{0021CE}',\n    \"nharr\".to_slice                           => '\\u{0021AE}',\n    \"nhpar\".to_slice                           => '\\u{002AF2}',\n    \"ni\".to_slice                              => '\\u{00220B}',\n    \"nis\".to_slice                             => '\\u{0022FC}',\n    \"nisd\".to_slice                            => '\\u{0022FA}',\n    \"niv\".to_slice                             => '\\u{00220B}',\n    \"njcy\".to_slice                            => '\\u{00045A}',\n    \"nlArr\".to_slice                           => '\\u{0021CD}',\n    \"nlarr\".to_slice                           => '\\u{00219A}',\n    \"nldr\".to_slice                            => '\\u{002025}',\n    \"nle\".to_slice                             => '\\u{002270}',\n    \"nleftarrow\".to_slice                      => '\\u{00219A}',\n    \"nleftrightarrow\".to_slice                 => '\\u{0021AE}',\n    \"nleq\".to_slice                            => '\\u{002270}',\n    \"nless\".to_slice                           => '\\u{00226E}',\n    \"nlsim\".to_slice                           => '\\u{002274}',\n    \"nlt\".to_slice                             => '\\u{00226E}',\n    \"nltri\".to_slice                           => '\\u{0022EA}',\n    \"nltrie\".to_slice                          => '\\u{0022EC}',\n    \"nmid\".to_slice                            => '\\u{002224}',\n    \"nopf\".to_slice                            => '\\u{01D55F}',\n    \"not\".to_slice                             => '\\u{0000AC}',\n    \"notin\".to_slice                           => '\\u{002209}',\n    \"notinva\".to_slice                         => '\\u{002209}',\n    \"notinvb\".to_slice                         => '\\u{0022F7}',\n    \"notinvc\".to_slice                         => '\\u{0022F6}',\n    \"notni\".to_slice                           => '\\u{00220C}',\n    \"notniva\".to_slice                         => '\\u{00220C}',\n    \"notnivb\".to_slice                         => '\\u{0022FE}',\n    \"notnivc\".to_slice                         => '\\u{0022FD}',\n    \"npar\".to_slice                            => '\\u{002226}',\n    \"nparallel\".to_slice                       => '\\u{002226}',\n    \"npolint\".to_slice                         => '\\u{002A14}',\n    \"npr\".to_slice                             => '\\u{002280}',\n    \"nprcue\".to_slice                          => '\\u{0022E0}',\n    \"nprec\".to_slice                           => '\\u{002280}',\n    \"nrArr\".to_slice                           => '\\u{0021CF}',\n    \"nrarr\".to_slice                           => '\\u{00219B}',\n    \"nrightarrow\".to_slice                     => '\\u{00219B}',\n    \"nrtri\".to_slice                           => '\\u{0022EB}',\n    \"nrtrie\".to_slice                          => '\\u{0022ED}',\n    \"nsc\".to_slice                             => '\\u{002281}',\n    \"nsccue\".to_slice                          => '\\u{0022E1}',\n    \"nscr\".to_slice                            => '\\u{01D4C3}',\n    \"nshortmid\".to_slice                       => '\\u{002224}',\n    \"nshortparallel\".to_slice                  => '\\u{002226}',\n    \"nsim\".to_slice                            => '\\u{002241}',\n    \"nsime\".to_slice                           => '\\u{002244}',\n    \"nsimeq\".to_slice                          => '\\u{002244}',\n    \"nsmid\".to_slice                           => '\\u{002224}',\n    \"nspar\".to_slice                           => '\\u{002226}',\n    \"nsqsube\".to_slice                         => '\\u{0022E2}',\n    \"nsqsupe\".to_slice                         => '\\u{0022E3}',\n    \"nsub\".to_slice                            => '\\u{002284}',\n    \"nsube\".to_slice                           => '\\u{002288}',\n    \"nsubseteq\".to_slice                       => '\\u{002288}',\n    \"nsucc\".to_slice                           => '\\u{002281}',\n    \"nsup\".to_slice                            => '\\u{002285}',\n    \"nsupe\".to_slice                           => '\\u{002289}',\n    \"nsupseteq\".to_slice                       => '\\u{002289}',\n    \"ntgl\".to_slice                            => '\\u{002279}',\n    \"ntilde\".to_slice                          => '\\u{0000F1}',\n    \"ntlg\".to_slice                            => '\\u{002278}',\n    \"ntriangleleft\".to_slice                   => '\\u{0022EA}',\n    \"ntrianglelefteq\".to_slice                 => '\\u{0022EC}',\n    \"ntriangleright\".to_slice                  => '\\u{0022EB}',\n    \"ntrianglerighteq\".to_slice                => '\\u{0022ED}',\n    \"nu\".to_slice                              => '\\u{0003BD}',\n    \"num\".to_slice                             => '\\u{000023}',\n    \"numero\".to_slice                          => '\\u{002116}',\n    \"numsp\".to_slice                           => '\\u{002007}',\n    \"nvDash\".to_slice                          => '\\u{0022AD}',\n    \"nvHarr\".to_slice                          => '\\u{002904}',\n    \"nvdash\".to_slice                          => '\\u{0022AC}',\n    \"nvinfin\".to_slice                         => '\\u{0029DE}',\n    \"nvlArr\".to_slice                          => '\\u{002902}',\n    \"nvrArr\".to_slice                          => '\\u{002903}',\n    \"nwArr\".to_slice                           => '\\u{0021D6}',\n    \"nwarhk\".to_slice                          => '\\u{002923}',\n    \"nwarr\".to_slice                           => '\\u{002196}',\n    \"nwarrow\".to_slice                         => '\\u{002196}',\n    \"nwnear\".to_slice                          => '\\u{002927}',\n    \"oS\".to_slice                              => '\\u{0024C8}',\n    \"oacute\".to_slice                          => '\\u{0000F3}',\n    \"oast\".to_slice                            => '\\u{00229B}',\n    \"ocir\".to_slice                            => '\\u{00229A}',\n    \"ocirc\".to_slice                           => '\\u{0000F4}',\n    \"ocy\".to_slice                             => '\\u{00043E}',\n    \"odash\".to_slice                           => '\\u{00229D}',\n    \"odblac\".to_slice                          => '\\u{000151}',\n    \"odiv\".to_slice                            => '\\u{002A38}',\n    \"odot\".to_slice                            => '\\u{002299}',\n    \"odsold\".to_slice                          => '\\u{0029BC}',\n    \"oelig\".to_slice                           => '\\u{000153}',\n    \"ofcir\".to_slice                           => '\\u{0029BF}',\n    \"ofr\".to_slice                             => '\\u{01D52C}',\n    \"ogon\".to_slice                            => '\\u{0002DB}',\n    \"ograve\".to_slice                          => '\\u{0000F2}',\n    \"ogt\".to_slice                             => '\\u{0029C1}',\n    \"ohbar\".to_slice                           => '\\u{0029B5}',\n    \"ohm\".to_slice                             => '\\u{0003A9}',\n    \"oint\".to_slice                            => '\\u{00222E}',\n    \"olarr\".to_slice                           => '\\u{0021BA}',\n    \"olcir\".to_slice                           => '\\u{0029BE}',\n    \"olcross\".to_slice                         => '\\u{0029BB}',\n    \"oline\".to_slice                           => '\\u{00203E}',\n    \"olt\".to_slice                             => '\\u{0029C0}',\n    \"omacr\".to_slice                           => '\\u{00014D}',\n    \"omega\".to_slice                           => '\\u{0003C9}',\n    \"omicron\".to_slice                         => '\\u{0003BF}',\n    \"omid\".to_slice                            => '\\u{0029B6}',\n    \"ominus\".to_slice                          => '\\u{002296}',\n    \"oopf\".to_slice                            => '\\u{01D560}',\n    \"opar\".to_slice                            => '\\u{0029B7}',\n    \"operp\".to_slice                           => '\\u{0029B9}',\n    \"oplus\".to_slice                           => '\\u{002295}',\n    \"or\".to_slice                              => '\\u{002228}',\n    \"orarr\".to_slice                           => '\\u{0021BB}',\n    \"ord\".to_slice                             => '\\u{002A5D}',\n    \"order\".to_slice                           => '\\u{002134}',\n    \"orderof\".to_slice                         => '\\u{002134}',\n    \"ordf\".to_slice                            => '\\u{0000AA}',\n    \"ordm\".to_slice                            => '\\u{0000BA}',\n    \"origof\".to_slice                          => '\\u{0022B6}',\n    \"oror\".to_slice                            => '\\u{002A56}',\n    \"orslope\".to_slice                         => '\\u{002A57}',\n    \"orv\".to_slice                             => '\\u{002A5B}',\n    \"oscr\".to_slice                            => '\\u{002134}',\n    \"oslash\".to_slice                          => '\\u{0000F8}',\n    \"osol\".to_slice                            => '\\u{002298}',\n    \"otilde\".to_slice                          => '\\u{0000F5}',\n    \"otimes\".to_slice                          => '\\u{002297}',\n    \"otimesas\".to_slice                        => '\\u{002A36}',\n    \"ouml\".to_slice                            => '\\u{0000F6}',\n    \"ovbar\".to_slice                           => '\\u{00233D}',\n    \"par\".to_slice                             => '\\u{002225}',\n    \"para\".to_slice                            => '\\u{0000B6}',\n    \"parallel\".to_slice                        => '\\u{002225}',\n    \"parsim\".to_slice                          => '\\u{002AF3}',\n    \"parsl\".to_slice                           => '\\u{002AFD}',\n    \"part\".to_slice                            => '\\u{002202}',\n    \"pcy\".to_slice                             => '\\u{00043F}',\n    \"percnt\".to_slice                          => '\\u{000025}',\n    \"period\".to_slice                          => '\\u{00002E}',\n    \"permil\".to_slice                          => '\\u{002030}',\n    \"perp\".to_slice                            => '\\u{0022A5}',\n    \"pertenk\".to_slice                         => '\\u{002031}',\n    \"pfr\".to_slice                             => '\\u{01D52D}',\n    \"phi\".to_slice                             => '\\u{0003C6}',\n    \"phiv\".to_slice                            => '\\u{0003D5}',\n    \"phmmat\".to_slice                          => '\\u{002133}',\n    \"phone\".to_slice                           => '\\u{00260E}',\n    \"pi\".to_slice                              => '\\u{0003C0}',\n    \"pitchfork\".to_slice                       => '\\u{0022D4}',\n    \"piv\".to_slice                             => '\\u{0003D6}',\n    \"planck\".to_slice                          => '\\u{00210F}',\n    \"planckh\".to_slice                         => '\\u{00210E}',\n    \"plankv\".to_slice                          => '\\u{00210F}',\n    \"plus\".to_slice                            => '\\u{00002B}',\n    \"plusacir\".to_slice                        => '\\u{002A23}',\n    \"plusb\".to_slice                           => '\\u{00229E}',\n    \"pluscir\".to_slice                         => '\\u{002A22}',\n    \"plusdo\".to_slice                          => '\\u{002214}',\n    \"plusdu\".to_slice                          => '\\u{002A25}',\n    \"pluse\".to_slice                           => '\\u{002A72}',\n    \"plusmn\".to_slice                          => '\\u{0000B1}',\n    \"plussim\".to_slice                         => '\\u{002A26}',\n    \"plustwo\".to_slice                         => '\\u{002A27}',\n    \"pm\".to_slice                              => '\\u{0000B1}',\n    \"pointint\".to_slice                        => '\\u{002A15}',\n    \"popf\".to_slice                            => '\\u{01D561}',\n    \"pound\".to_slice                           => '\\u{0000A3}',\n    \"pr\".to_slice                              => '\\u{00227A}',\n    \"prE\".to_slice                             => '\\u{002AB3}',\n    \"prap\".to_slice                            => '\\u{002AB7}',\n    \"prcue\".to_slice                           => '\\u{00227C}',\n    \"pre\".to_slice                             => '\\u{002AAF}',\n    \"prec\".to_slice                            => '\\u{00227A}',\n    \"precapprox\".to_slice                      => '\\u{002AB7}',\n    \"preccurlyeq\".to_slice                     => '\\u{00227C}',\n    \"preceq\".to_slice                          => '\\u{002AAF}',\n    \"precnapprox\".to_slice                     => '\\u{002AB9}',\n    \"precneqq\".to_slice                        => '\\u{002AB5}',\n    \"precnsim\".to_slice                        => '\\u{0022E8}',\n    \"precsim\".to_slice                         => '\\u{00227E}',\n    \"prime\".to_slice                           => '\\u{002032}',\n    \"primes\".to_slice                          => '\\u{002119}',\n    \"prnE\".to_slice                            => '\\u{002AB5}',\n    \"prnap\".to_slice                           => '\\u{002AB9}',\n    \"prnsim\".to_slice                          => '\\u{0022E8}',\n    \"prod\".to_slice                            => '\\u{00220F}',\n    \"profalar\".to_slice                        => '\\u{00232E}',\n    \"profline\".to_slice                        => '\\u{002312}',\n    \"profsurf\".to_slice                        => '\\u{002313}',\n    \"prop\".to_slice                            => '\\u{00221D}',\n    \"propto\".to_slice                          => '\\u{00221D}',\n    \"prsim\".to_slice                           => '\\u{00227E}',\n    \"prurel\".to_slice                          => '\\u{0022B0}',\n    \"pscr\".to_slice                            => '\\u{01D4C5}',\n    \"psi\".to_slice                             => '\\u{0003C8}',\n    \"puncsp\".to_slice                          => '\\u{002008}',\n    \"qfr\".to_slice                             => '\\u{01D52E}',\n    \"qint\".to_slice                            => '\\u{002A0C}',\n    \"qopf\".to_slice                            => '\\u{01D562}',\n    \"qprime\".to_slice                          => '\\u{002057}',\n    \"qscr\".to_slice                            => '\\u{01D4C6}',\n    \"quaternions\".to_slice                     => '\\u{00210D}',\n    \"quatint\".to_slice                         => '\\u{002A16}',\n    \"quest\".to_slice                           => '\\u{00003F}',\n    \"questeq\".to_slice                         => '\\u{00225F}',\n    \"quot\".to_slice                            => '\\u{000022}',\n    \"rAarr\".to_slice                           => '\\u{0021DB}',\n    \"rArr\".to_slice                            => '\\u{0021D2}',\n    \"rAtail\".to_slice                          => '\\u{00291C}',\n    \"rBarr\".to_slice                           => '\\u{00290F}',\n    \"rHar\".to_slice                            => '\\u{002964}',\n    \"racute\".to_slice                          => '\\u{000155}',\n    \"radic\".to_slice                           => '\\u{00221A}',\n    \"raemptyv\".to_slice                        => '\\u{0029B3}',\n    \"rang\".to_slice                            => '\\u{0027E9}',\n    \"rangd\".to_slice                           => '\\u{002992}',\n    \"range\".to_slice                           => '\\u{0029A5}',\n    \"rangle\".to_slice                          => '\\u{0027E9}',\n    \"raquo\".to_slice                           => '\\u{0000BB}',\n    \"rarr\".to_slice                            => '\\u{002192}',\n    \"rarrap\".to_slice                          => '\\u{002975}',\n    \"rarrb\".to_slice                           => '\\u{0021E5}',\n    \"rarrbfs\".to_slice                         => '\\u{002920}',\n    \"rarrc\".to_slice                           => '\\u{002933}',\n    \"rarrfs\".to_slice                          => '\\u{00291E}',\n    \"rarrhk\".to_slice                          => '\\u{0021AA}',\n    \"rarrlp\".to_slice                          => '\\u{0021AC}',\n    \"rarrpl\".to_slice                          => '\\u{002945}',\n    \"rarrsim\".to_slice                         => '\\u{002974}',\n    \"rarrtl\".to_slice                          => '\\u{0021A3}',\n    \"rarrw\".to_slice                           => '\\u{00219D}',\n    \"ratail\".to_slice                          => '\\u{00291A}',\n    \"ratio\".to_slice                           => '\\u{002236}',\n    \"rationals\".to_slice                       => '\\u{00211A}',\n    \"rbarr\".to_slice                           => '\\u{00290D}',\n    \"rbbrk\".to_slice                           => '\\u{002773}',\n    \"rbrace\".to_slice                          => '\\u{00007D}',\n    \"rbrack\".to_slice                          => '\\u{00005D}',\n    \"rbrke\".to_slice                           => '\\u{00298C}',\n    \"rbrksld\".to_slice                         => '\\u{00298E}',\n    \"rbrkslu\".to_slice                         => '\\u{002990}',\n    \"rcaron\".to_slice                          => '\\u{000159}',\n    \"rcedil\".to_slice                          => '\\u{000157}',\n    \"rceil\".to_slice                           => '\\u{002309}',\n    \"rcub\".to_slice                            => '\\u{00007D}',\n    \"rcy\".to_slice                             => '\\u{000440}',\n    \"rdca\".to_slice                            => '\\u{002937}',\n    \"rdldhar\".to_slice                         => '\\u{002969}',\n    \"rdquo\".to_slice                           => '\\u{00201D}',\n    \"rdquor\".to_slice                          => '\\u{00201D}',\n    \"rdsh\".to_slice                            => '\\u{0021B3}',\n    \"real\".to_slice                            => '\\u{00211C}',\n    \"realine\".to_slice                         => '\\u{00211B}',\n    \"realpart\".to_slice                        => '\\u{00211C}',\n    \"reals\".to_slice                           => '\\u{00211D}',\n    \"rect\".to_slice                            => '\\u{0025AD}',\n    \"reg\".to_slice                             => '\\u{0000AE}',\n    \"rfisht\".to_slice                          => '\\u{00297D}',\n    \"rfloor\".to_slice                          => '\\u{00230B}',\n    \"rfr\".to_slice                             => '\\u{01D52F}',\n    \"rhard\".to_slice                           => '\\u{0021C1}',\n    \"rharu\".to_slice                           => '\\u{0021C0}',\n    \"rharul\".to_slice                          => '\\u{00296C}',\n    \"rho\".to_slice                             => '\\u{0003C1}',\n    \"rhov\".to_slice                            => '\\u{0003F1}',\n    \"rightarrow\".to_slice                      => '\\u{002192}',\n    \"rightarrowtail\".to_slice                  => '\\u{0021A3}',\n    \"rightharpoondown\".to_slice                => '\\u{0021C1}',\n    \"rightharpoonup\".to_slice                  => '\\u{0021C0}',\n    \"rightleftarrows\".to_slice                 => '\\u{0021C4}',\n    \"rightleftharpoons\".to_slice               => '\\u{0021CC}',\n    \"rightrightarrows\".to_slice                => '\\u{0021C9}',\n    \"rightsquigarrow\".to_slice                 => '\\u{00219D}',\n    \"rightthreetimes\".to_slice                 => '\\u{0022CC}',\n    \"ring\".to_slice                            => '\\u{0002DA}',\n    \"risingdotseq\".to_slice                    => '\\u{002253}',\n    \"rlarr\".to_slice                           => '\\u{0021C4}',\n    \"rlhar\".to_slice                           => '\\u{0021CC}',\n    \"rlm\".to_slice                             => '\\u{00200F}',\n    \"rmoust\".to_slice                          => '\\u{0023B1}',\n    \"rmoustache\".to_slice                      => '\\u{0023B1}',\n    \"rnmid\".to_slice                           => '\\u{002AEE}',\n    \"roang\".to_slice                           => '\\u{0027ED}',\n    \"roarr\".to_slice                           => '\\u{0021FE}',\n    \"robrk\".to_slice                           => '\\u{0027E7}',\n    \"ropar\".to_slice                           => '\\u{002986}',\n    \"ropf\".to_slice                            => '\\u{01D563}',\n    \"roplus\".to_slice                          => '\\u{002A2E}',\n    \"rotimes\".to_slice                         => '\\u{002A35}',\n    \"rpar\".to_slice                            => '\\u{000029}',\n    \"rpargt\".to_slice                          => '\\u{002994}',\n    \"rppolint\".to_slice                        => '\\u{002A12}',\n    \"rrarr\".to_slice                           => '\\u{0021C9}',\n    \"rsaquo\".to_slice                          => '\\u{00203A}',\n    \"rscr\".to_slice                            => '\\u{01D4C7}',\n    \"rsh\".to_slice                             => '\\u{0021B1}',\n    \"rsqb\".to_slice                            => '\\u{00005D}',\n    \"rsquo\".to_slice                           => '\\u{002019}',\n    \"rsquor\".to_slice                          => '\\u{002019}',\n    \"rthree\".to_slice                          => '\\u{0022CC}',\n    \"rtimes\".to_slice                          => '\\u{0022CA}',\n    \"rtri\".to_slice                            => '\\u{0025B9}',\n    \"rtrie\".to_slice                           => '\\u{0022B5}',\n    \"rtrif\".to_slice                           => '\\u{0025B8}',\n    \"rtriltri\".to_slice                        => '\\u{0029CE}',\n    \"ruluhar\".to_slice                         => '\\u{002968}',\n    \"rx\".to_slice                              => '\\u{00211E}',\n    \"sacute\".to_slice                          => '\\u{00015B}',\n    \"sbquo\".to_slice                           => '\\u{00201A}',\n    \"sc\".to_slice                              => '\\u{00227B}',\n    \"scE\".to_slice                             => '\\u{002AB4}',\n    \"scap\".to_slice                            => '\\u{002AB8}',\n    \"scaron\".to_slice                          => '\\u{000161}',\n    \"sccue\".to_slice                           => '\\u{00227D}',\n    \"sce\".to_slice                             => '\\u{002AB0}',\n    \"scedil\".to_slice                          => '\\u{00015F}',\n    \"scirc\".to_slice                           => '\\u{00015D}',\n    \"scnE\".to_slice                            => '\\u{002AB6}',\n    \"scnap\".to_slice                           => '\\u{002ABA}',\n    \"scnsim\".to_slice                          => '\\u{0022E9}',\n    \"scpolint\".to_slice                        => '\\u{002A13}',\n    \"scsim\".to_slice                           => '\\u{00227F}',\n    \"scy\".to_slice                             => '\\u{000441}',\n    \"sdot\".to_slice                            => '\\u{0022C5}',\n    \"sdotb\".to_slice                           => '\\u{0022A1}',\n    \"sdote\".to_slice                           => '\\u{002A66}',\n    \"seArr\".to_slice                           => '\\u{0021D8}',\n    \"searhk\".to_slice                          => '\\u{002925}',\n    \"searr\".to_slice                           => '\\u{002198}',\n    \"searrow\".to_slice                         => '\\u{002198}',\n    \"sect\".to_slice                            => '\\u{0000A7}',\n    \"semi\".to_slice                            => '\\u{00003B}',\n    \"seswar\".to_slice                          => '\\u{002929}',\n    \"setminus\".to_slice                        => '\\u{002216}',\n    \"setmn\".to_slice                           => '\\u{002216}',\n    \"sext\".to_slice                            => '\\u{002736}',\n    \"sfr\".to_slice                             => '\\u{01D530}',\n    \"sfrown\".to_slice                          => '\\u{002322}',\n    \"sharp\".to_slice                           => '\\u{00266F}',\n    \"shchcy\".to_slice                          => '\\u{000449}',\n    \"shcy\".to_slice                            => '\\u{000448}',\n    \"shortmid\".to_slice                        => '\\u{002223}',\n    \"shortparallel\".to_slice                   => '\\u{002225}',\n    \"shy\".to_slice                             => '\\u{0000AD}',\n    \"sigma\".to_slice                           => '\\u{0003C3}',\n    \"sigmaf\".to_slice                          => '\\u{0003C2}',\n    \"sigmav\".to_slice                          => '\\u{0003C2}',\n    \"sim\".to_slice                             => '\\u{00223C}',\n    \"simdot\".to_slice                          => '\\u{002A6A}',\n    \"sime\".to_slice                            => '\\u{002243}',\n    \"simeq\".to_slice                           => '\\u{002243}',\n    \"simg\".to_slice                            => '\\u{002A9E}',\n    \"simgE\".to_slice                           => '\\u{002AA0}',\n    \"siml\".to_slice                            => '\\u{002A9D}',\n    \"simlE\".to_slice                           => '\\u{002A9F}',\n    \"simne\".to_slice                           => '\\u{002246}',\n    \"simplus\".to_slice                         => '\\u{002A24}',\n    \"simrarr\".to_slice                         => '\\u{002972}',\n    \"slarr\".to_slice                           => '\\u{002190}',\n    \"smallsetminus\".to_slice                   => '\\u{002216}',\n    \"smashp\".to_slice                          => '\\u{002A33}',\n    \"smeparsl\".to_slice                        => '\\u{0029E4}',\n    \"smid\".to_slice                            => '\\u{002223}',\n    \"smile\".to_slice                           => '\\u{002323}',\n    \"smt\".to_slice                             => '\\u{002AAA}',\n    \"smte\".to_slice                            => '\\u{002AAC}',\n    \"softcy\".to_slice                          => '\\u{00044C}',\n    \"sol\".to_slice                             => '\\u{00002F}',\n    \"solb\".to_slice                            => '\\u{0029C4}',\n    \"solbar\".to_slice                          => '\\u{00233F}',\n    \"sopf\".to_slice                            => '\\u{01D564}',\n    \"spades\".to_slice                          => '\\u{002660}',\n    \"spadesuit\".to_slice                       => '\\u{002660}',\n    \"spar\".to_slice                            => '\\u{002225}',\n    \"sqcap\".to_slice                           => '\\u{002293}',\n    \"sqcup\".to_slice                           => '\\u{002294}',\n    \"sqsub\".to_slice                           => '\\u{00228F}',\n    \"sqsube\".to_slice                          => '\\u{002291}',\n    \"sqsubset\".to_slice                        => '\\u{00228F}',\n    \"sqsubseteq\".to_slice                      => '\\u{002291}',\n    \"sqsup\".to_slice                           => '\\u{002290}',\n    \"sqsupe\".to_slice                          => '\\u{002292}',\n    \"sqsupset\".to_slice                        => '\\u{002290}',\n    \"sqsupseteq\".to_slice                      => '\\u{002292}',\n    \"squ\".to_slice                             => '\\u{0025A1}',\n    \"square\".to_slice                          => '\\u{0025A1}',\n    \"squarf\".to_slice                          => '\\u{0025AA}',\n    \"squf\".to_slice                            => '\\u{0025AA}',\n    \"srarr\".to_slice                           => '\\u{002192}',\n    \"sscr\".to_slice                            => '\\u{01D4C8}',\n    \"ssetmn\".to_slice                          => '\\u{002216}',\n    \"ssmile\".to_slice                          => '\\u{002323}',\n    \"sstarf\".to_slice                          => '\\u{0022C6}',\n    \"star\".to_slice                            => '\\u{002606}',\n    \"starf\".to_slice                           => '\\u{002605}',\n    \"straightepsilon\".to_slice                 => '\\u{0003F5}',\n    \"straightphi\".to_slice                     => '\\u{0003D5}',\n    \"strns\".to_slice                           => '\\u{0000AF}',\n    \"sub\".to_slice                             => '\\u{002282}',\n    \"subE\".to_slice                            => '\\u{002AC5}',\n    \"subdot\".to_slice                          => '\\u{002ABD}',\n    \"sube\".to_slice                            => '\\u{002286}',\n    \"subedot\".to_slice                         => '\\u{002AC3}',\n    \"submult\".to_slice                         => '\\u{002AC1}',\n    \"subnE\".to_slice                           => '\\u{002ACB}',\n    \"subne\".to_slice                           => '\\u{00228A}',\n    \"subplus\".to_slice                         => '\\u{002ABF}',\n    \"subrarr\".to_slice                         => '\\u{002979}',\n    \"subset\".to_slice                          => '\\u{002282}',\n    \"subseteq\".to_slice                        => '\\u{002286}',\n    \"subseteqq\".to_slice                       => '\\u{002AC5}',\n    \"subsetneq\".to_slice                       => '\\u{00228A}',\n    \"subsetneqq\".to_slice                      => '\\u{002ACB}',\n    \"subsim\".to_slice                          => '\\u{002AC7}',\n    \"subsub\".to_slice                          => '\\u{002AD5}',\n    \"subsup\".to_slice                          => '\\u{002AD3}',\n    \"succ\".to_slice                            => '\\u{00227B}',\n    \"succapprox\".to_slice                      => '\\u{002AB8}',\n    \"succcurlyeq\".to_slice                     => '\\u{00227D}',\n    \"succeq\".to_slice                          => '\\u{002AB0}',\n    \"succnapprox\".to_slice                     => '\\u{002ABA}',\n    \"succneqq\".to_slice                        => '\\u{002AB6}',\n    \"succnsim\".to_slice                        => '\\u{0022E9}',\n    \"succsim\".to_slice                         => '\\u{00227F}',\n    \"sum\".to_slice                             => '\\u{002211}',\n    \"sung\".to_slice                            => '\\u{00266A}',\n    \"sup\".to_slice                             => '\\u{002283}',\n    \"sup1\".to_slice                            => '\\u{0000B9}',\n    \"sup2\".to_slice                            => '\\u{0000B2}',\n    \"sup3\".to_slice                            => '\\u{0000B3}',\n    \"supE\".to_slice                            => '\\u{002AC6}',\n    \"supdot\".to_slice                          => '\\u{002ABE}',\n    \"supdsub\".to_slice                         => '\\u{002AD8}',\n    \"supe\".to_slice                            => '\\u{002287}',\n    \"supedot\".to_slice                         => '\\u{002AC4}',\n    \"suphsol\".to_slice                         => '\\u{0027C9}',\n    \"suphsub\".to_slice                         => '\\u{002AD7}',\n    \"suplarr\".to_slice                         => '\\u{00297B}',\n    \"supmult\".to_slice                         => '\\u{002AC2}',\n    \"supnE\".to_slice                           => '\\u{002ACC}',\n    \"supne\".to_slice                           => '\\u{00228B}',\n    \"supplus\".to_slice                         => '\\u{002AC0}',\n    \"supset\".to_slice                          => '\\u{002283}',\n    \"supseteq\".to_slice                        => '\\u{002287}',\n    \"supseteqq\".to_slice                       => '\\u{002AC6}',\n    \"supsetneq\".to_slice                       => '\\u{00228B}',\n    \"supsetneqq\".to_slice                      => '\\u{002ACC}',\n    \"supsim\".to_slice                          => '\\u{002AC8}',\n    \"supsub\".to_slice                          => '\\u{002AD4}',\n    \"supsup\".to_slice                          => '\\u{002AD6}',\n    \"swArr\".to_slice                           => '\\u{0021D9}',\n    \"swarhk\".to_slice                          => '\\u{002926}',\n    \"swarr\".to_slice                           => '\\u{002199}',\n    \"swarrow\".to_slice                         => '\\u{002199}',\n    \"swnwar\".to_slice                          => '\\u{00292A}',\n    \"szlig\".to_slice                           => '\\u{0000DF}',\n    \"target\".to_slice                          => '\\u{002316}',\n    \"tau\".to_slice                             => '\\u{0003C4}',\n    \"tbrk\".to_slice                            => '\\u{0023B4}',\n    \"tcaron\".to_slice                          => '\\u{000165}',\n    \"tcedil\".to_slice                          => '\\u{000163}',\n    \"tcy\".to_slice                             => '\\u{000442}',\n    \"tdot\".to_slice                            => '\\u{0020DB}',\n    \"telrec\".to_slice                          => '\\u{002315}',\n    \"tfr\".to_slice                             => '\\u{01D531}',\n    \"there4\".to_slice                          => '\\u{002234}',\n    \"therefore\".to_slice                       => '\\u{002234}',\n    \"theta\".to_slice                           => '\\u{0003B8}',\n    \"thetasym\".to_slice                        => '\\u{0003D1}',\n    \"thetav\".to_slice                          => '\\u{0003D1}',\n    \"thickapprox\".to_slice                     => '\\u{002248}',\n    \"thicksim\".to_slice                        => '\\u{00223C}',\n    \"thinsp\".to_slice                          => '\\u{002009}',\n    \"thkap\".to_slice                           => '\\u{002248}',\n    \"thksim\".to_slice                          => '\\u{00223C}',\n    \"thorn\".to_slice                           => '\\u{0000FE}',\n    \"tilde\".to_slice                           => '\\u{0002DC}',\n    \"times\".to_slice                           => '\\u{0000D7}',\n    \"timesb\".to_slice                          => '\\u{0022A0}',\n    \"timesbar\".to_slice                        => '\\u{002A31}',\n    \"timesd\".to_slice                          => '\\u{002A30}',\n    \"tint\".to_slice                            => '\\u{00222D}',\n    \"toea\".to_slice                            => '\\u{002928}',\n    \"top\".to_slice                             => '\\u{0022A4}',\n    \"topbot\".to_slice                          => '\\u{002336}',\n    \"topcir\".to_slice                          => '\\u{002AF1}',\n    \"topf\".to_slice                            => '\\u{01D565}',\n    \"topfork\".to_slice                         => '\\u{002ADA}',\n    \"tosa\".to_slice                            => '\\u{002929}',\n    \"tprime\".to_slice                          => '\\u{002034}',\n    \"trade\".to_slice                           => '\\u{002122}',\n    \"triangle\".to_slice                        => '\\u{0025B5}',\n    \"triangledown\".to_slice                    => '\\u{0025BF}',\n    \"triangleleft\".to_slice                    => '\\u{0025C3}',\n    \"trianglelefteq\".to_slice                  => '\\u{0022B4}',\n    \"triangleq\".to_slice                       => '\\u{00225C}',\n    \"triangleright\".to_slice                   => '\\u{0025B9}',\n    \"trianglerighteq\".to_slice                 => '\\u{0022B5}',\n    \"tridot\".to_slice                          => '\\u{0025EC}',\n    \"trie\".to_slice                            => '\\u{00225C}',\n    \"triminus\".to_slice                        => '\\u{002A3A}',\n    \"triplus\".to_slice                         => '\\u{002A39}',\n    \"trisb\".to_slice                           => '\\u{0029CD}',\n    \"tritime\".to_slice                         => '\\u{002A3B}',\n    \"trpezium\".to_slice                        => '\\u{0023E2}',\n    \"tscr\".to_slice                            => '\\u{01D4C9}',\n    \"tscy\".to_slice                            => '\\u{000446}',\n    \"tshcy\".to_slice                           => '\\u{00045B}',\n    \"tstrok\".to_slice                          => '\\u{000167}',\n    \"twixt\".to_slice                           => '\\u{00226C}',\n    \"twoheadleftarrow\".to_slice                => '\\u{00219E}',\n    \"twoheadrightarrow\".to_slice               => '\\u{0021A0}',\n    \"uArr\".to_slice                            => '\\u{0021D1}',\n    \"uHar\".to_slice                            => '\\u{002963}',\n    \"uacute\".to_slice                          => '\\u{0000FA}',\n    \"uarr\".to_slice                            => '\\u{002191}',\n    \"ubrcy\".to_slice                           => '\\u{00045E}',\n    \"ubreve\".to_slice                          => '\\u{00016D}',\n    \"ucirc\".to_slice                           => '\\u{0000FB}',\n    \"ucy\".to_slice                             => '\\u{000443}',\n    \"udarr\".to_slice                           => '\\u{0021C5}',\n    \"udblac\".to_slice                          => '\\u{000171}',\n    \"udhar\".to_slice                           => '\\u{00296E}',\n    \"ufisht\".to_slice                          => '\\u{00297E}',\n    \"ufr\".to_slice                             => '\\u{01D532}',\n    \"ugrave\".to_slice                          => '\\u{0000F9}',\n    \"uharl\".to_slice                           => '\\u{0021BF}',\n    \"uharr\".to_slice                           => '\\u{0021BE}',\n    \"uhblk\".to_slice                           => '\\u{002580}',\n    \"ulcorn\".to_slice                          => '\\u{00231C}',\n    \"ulcorner\".to_slice                        => '\\u{00231C}',\n    \"ulcrop\".to_slice                          => '\\u{00230F}',\n    \"ultri\".to_slice                           => '\\u{0025F8}',\n    \"umacr\".to_slice                           => '\\u{00016B}',\n    \"uml\".to_slice                             => '\\u{0000A8}',\n    \"uogon\".to_slice                           => '\\u{000173}',\n    \"uopf\".to_slice                            => '\\u{01D566}',\n    \"uparrow\".to_slice                         => '\\u{002191}',\n    \"updownarrow\".to_slice                     => '\\u{002195}',\n    \"upharpoonleft\".to_slice                   => '\\u{0021BF}',\n    \"upharpoonright\".to_slice                  => '\\u{0021BE}',\n    \"uplus\".to_slice                           => '\\u{00228E}',\n    \"upsi\".to_slice                            => '\\u{0003C5}',\n    \"upsih\".to_slice                           => '\\u{0003D2}',\n    \"upsilon\".to_slice                         => '\\u{0003C5}',\n    \"upuparrows\".to_slice                      => '\\u{0021C8}',\n    \"urcorn\".to_slice                          => '\\u{00231D}',\n    \"urcorner\".to_slice                        => '\\u{00231D}',\n    \"urcrop\".to_slice                          => '\\u{00230E}',\n    \"uring\".to_slice                           => '\\u{00016F}',\n    \"urtri\".to_slice                           => '\\u{0025F9}',\n    \"uscr\".to_slice                            => '\\u{01D4CA}',\n    \"utdot\".to_slice                           => '\\u{0022F0}',\n    \"utilde\".to_slice                          => '\\u{000169}',\n    \"utri\".to_slice                            => '\\u{0025B5}',\n    \"utrif\".to_slice                           => '\\u{0025B4}',\n    \"uuarr\".to_slice                           => '\\u{0021C8}',\n    \"uuml\".to_slice                            => '\\u{0000FC}',\n    \"uwangle\".to_slice                         => '\\u{0029A7}',\n    \"vArr\".to_slice                            => '\\u{0021D5}',\n    \"vBar\".to_slice                            => '\\u{002AE8}',\n    \"vBarv\".to_slice                           => '\\u{002AE9}',\n    \"vDash\".to_slice                           => '\\u{0022A8}',\n    \"vangrt\".to_slice                          => '\\u{00299C}',\n    \"varepsilon\".to_slice                      => '\\u{0003F5}',\n    \"varkappa\".to_slice                        => '\\u{0003F0}',\n    \"varnothing\".to_slice                      => '\\u{002205}',\n    \"varphi\".to_slice                          => '\\u{0003D5}',\n    \"varpi\".to_slice                           => '\\u{0003D6}',\n    \"varpropto\".to_slice                       => '\\u{00221D}',\n    \"varr\".to_slice                            => '\\u{002195}',\n    \"varrho\".to_slice                          => '\\u{0003F1}',\n    \"varsigma\".to_slice                        => '\\u{0003C2}',\n    \"vartheta\".to_slice                        => '\\u{0003D1}',\n    \"vartriangleleft\".to_slice                 => '\\u{0022B2}',\n    \"vartriangleright\".to_slice                => '\\u{0022B3}',\n    \"vcy\".to_slice                             => '\\u{000432}',\n    \"vdash\".to_slice                           => '\\u{0022A2}',\n    \"vee\".to_slice                             => '\\u{002228}',\n    \"veebar\".to_slice                          => '\\u{0022BB}',\n    \"veeeq\".to_slice                           => '\\u{00225A}',\n    \"vellip\".to_slice                          => '\\u{0022EE}',\n    \"verbar\".to_slice                          => '\\u{00007C}',\n    \"vert\".to_slice                            => '\\u{00007C}',\n    \"vfr\".to_slice                             => '\\u{01D533}',\n    \"vltri\".to_slice                           => '\\u{0022B2}',\n    \"vopf\".to_slice                            => '\\u{01D567}',\n    \"vprop\".to_slice                           => '\\u{00221D}',\n    \"vrtri\".to_slice                           => '\\u{0022B3}',\n    \"vscr\".to_slice                            => '\\u{01D4CB}',\n    \"vzigzag\".to_slice                         => '\\u{00299A}',\n    \"wcirc\".to_slice                           => '\\u{000175}',\n    \"wedbar\".to_slice                          => '\\u{002A5F}',\n    \"wedge\".to_slice                           => '\\u{002227}',\n    \"wedgeq\".to_slice                          => '\\u{002259}',\n    \"weierp\".to_slice                          => '\\u{002118}',\n    \"wfr\".to_slice                             => '\\u{01D534}',\n    \"wopf\".to_slice                            => '\\u{01D568}',\n    \"wp\".to_slice                              => '\\u{002118}',\n    \"wr\".to_slice                              => '\\u{002240}',\n    \"wreath\".to_slice                          => '\\u{002240}',\n    \"wscr\".to_slice                            => '\\u{01D4CC}',\n    \"xcap\".to_slice                            => '\\u{0022C2}',\n    \"xcirc\".to_slice                           => '\\u{0025EF}',\n    \"xcup\".to_slice                            => '\\u{0022C3}',\n    \"xdtri\".to_slice                           => '\\u{0025BD}',\n    \"xfr\".to_slice                             => '\\u{01D535}',\n    \"xhArr\".to_slice                           => '\\u{0027FA}',\n    \"xharr\".to_slice                           => '\\u{0027F7}',\n    \"xi\".to_slice                              => '\\u{0003BE}',\n    \"xlArr\".to_slice                           => '\\u{0027F8}',\n    \"xlarr\".to_slice                           => '\\u{0027F5}',\n    \"xmap\".to_slice                            => '\\u{0027FC}',\n    \"xnis\".to_slice                            => '\\u{0022FB}',\n    \"xodot\".to_slice                           => '\\u{002A00}',\n    \"xopf\".to_slice                            => '\\u{01D569}',\n    \"xoplus\".to_slice                          => '\\u{002A01}',\n    \"xotime\".to_slice                          => '\\u{002A02}',\n    \"xrArr\".to_slice                           => '\\u{0027F9}',\n    \"xrarr\".to_slice                           => '\\u{0027F6}',\n    \"xscr\".to_slice                            => '\\u{01D4CD}',\n    \"xsqcup\".to_slice                          => '\\u{002A06}',\n    \"xuplus\".to_slice                          => '\\u{002A04}',\n    \"xutri\".to_slice                           => '\\u{0025B3}',\n    \"xvee\".to_slice                            => '\\u{0022C1}',\n    \"xwedge\".to_slice                          => '\\u{0022C0}',\n    \"yacute\".to_slice                          => '\\u{0000FD}',\n    \"yacy\".to_slice                            => '\\u{00044F}',\n    \"ycirc\".to_slice                           => '\\u{000177}',\n    \"ycy\".to_slice                             => '\\u{00044B}',\n    \"yen\".to_slice                             => '\\u{0000A5}',\n    \"yfr\".to_slice                             => '\\u{01D536}',\n    \"yicy\".to_slice                            => '\\u{000457}',\n    \"yopf\".to_slice                            => '\\u{01D56A}',\n    \"yscr\".to_slice                            => '\\u{01D4CE}',\n    \"yucy\".to_slice                            => '\\u{00044E}',\n    \"yuml\".to_slice                            => '\\u{0000FF}',\n    \"zacute\".to_slice                          => '\\u{00017A}',\n    \"zcaron\".to_slice                          => '\\u{00017E}',\n    \"zcy\".to_slice                             => '\\u{000437}',\n    \"zdot\".to_slice                            => '\\u{00017C}',\n    \"zeetrf\".to_slice                          => '\\u{002128}',\n    \"zeta\".to_slice                            => '\\u{0003B6}',\n    \"zfr\".to_slice                             => '\\u{01D537}',\n    \"zhcy\".to_slice                            => '\\u{000436}',\n    \"zigrarr\".to_slice                         => '\\u{0021DD}',\n    \"zopf\".to_slice                            => '\\u{01D56B}',\n    \"zscr\".to_slice                            => '\\u{01D4CF}',\n    \"zwj\".to_slice                             => '\\u{00200D}',\n    \"zwnj\".to_slice                            => '\\u{00200C}',\n  } of Bytes => Char\n\n  # :nodoc:\n  DOUBLE_CHAR_ENTITIES = {\n    \"NotEqualTilde\".to_slice           => \"\\u{2242}\\u{0338}\",\n    \"NotGreaterFullEqual\".to_slice     => \"\\u{2267}\\u{0338}\",\n    \"NotGreaterGreater\".to_slice       => \"\\u{226B}\\u{0338}\",\n    \"NotGreaterSlantEqual\".to_slice    => \"\\u{2A7E}\\u{0338}\",\n    \"NotHumpDownHump\".to_slice         => \"\\u{224E}\\u{0338}\",\n    \"NotHumpEqual\".to_slice            => \"\\u{224F}\\u{0338}\",\n    \"NotLeftTriangleBar\".to_slice      => \"\\u{29CF}\\u{0338}\",\n    \"NotLessLess\".to_slice             => \"\\u{226A}\\u{0338}\",\n    \"NotLessSlantEqual\".to_slice       => \"\\u{2A7D}\\u{0338}\",\n    \"NotNestedGreaterGreater\".to_slice => \"\\u{2AA2}\\u{0338}\",\n    \"NotNestedLessLess\".to_slice       => \"\\u{2AA1}\\u{0338}\",\n    \"NotPrecedesEqual\".to_slice        => \"\\u{2AAF}\\u{0338}\",\n    \"NotRightTriangleBar\".to_slice     => \"\\u{29D0}\\u{0338}\",\n    \"NotSquareSubset\".to_slice         => \"\\u{228F}\\u{0338}\",\n    \"NotSquareSuperset\".to_slice       => \"\\u{2290}\\u{0338}\",\n    \"NotSubset\".to_slice               => \"\\u{2282}\\u{20D2}\",\n    \"NotSucceedsEqual\".to_slice        => \"\\u{2AB0}\\u{0338}\",\n    \"NotSucceedsTilde\".to_slice        => \"\\u{227F}\\u{0338}\",\n    \"NotSuperset\".to_slice             => \"\\u{2283}\\u{20D2}\",\n    \"ThickSpace\".to_slice              => \"\\u{205F}\\u{200A}\",\n    \"acE\".to_slice                     => \"\\u{223E}\\u{0333}\",\n    \"bne\".to_slice                     => \"\\u{003D}\\u{20E5}\",\n    \"bnequiv\".to_slice                 => \"\\u{2261}\\u{20E5}\",\n    \"caps\".to_slice                    => \"\\u{2229}\\u{FE00}\",\n    \"cups\".to_slice                    => \"\\u{222A}\\u{FE00}\",\n    \"fjlig\".to_slice                   => \"\\u{0066}\\u{006A}\",\n    \"gesl\".to_slice                    => \"\\u{22DB}\\u{FE00}\",\n    \"gvertneqq\".to_slice               => \"\\u{2269}\\u{FE00}\",\n    \"gvnE\".to_slice                    => \"\\u{2269}\\u{FE00}\",\n    \"lates\".to_slice                   => \"\\u{2AAD}\\u{FE00}\",\n    \"lesg\".to_slice                    => \"\\u{22DA}\\u{FE00}\",\n    \"lvertneqq\".to_slice               => \"\\u{2268}\\u{FE00}\",\n    \"lvnE\".to_slice                    => \"\\u{2268}\\u{FE00}\",\n    \"nGg\".to_slice                     => \"\\u{22D9}\\u{0338}\",\n    \"nGt\".to_slice                     => \"\\u{226B}\\u{20D2}\",\n    \"nGtv\".to_slice                    => \"\\u{226B}\\u{0338}\",\n    \"nLl\".to_slice                     => \"\\u{22D8}\\u{0338}\",\n    \"nLt\".to_slice                     => \"\\u{226A}\\u{20D2}\",\n    \"nLtv\".to_slice                    => \"\\u{226A}\\u{0338}\",\n    \"nang\".to_slice                    => \"\\u{2220}\\u{20D2}\",\n    \"napE\".to_slice                    => \"\\u{2A70}\\u{0338}\",\n    \"napid\".to_slice                   => \"\\u{224B}\\u{0338}\",\n    \"nbump\".to_slice                   => \"\\u{224E}\\u{0338}\",\n    \"nbumpe\".to_slice                  => \"\\u{224F}\\u{0338}\",\n    \"ncongdot\".to_slice                => \"\\u{2A6D}\\u{0338}\",\n    \"nedot\".to_slice                   => \"\\u{2250}\\u{0338}\",\n    \"nesim\".to_slice                   => \"\\u{2242}\\u{0338}\",\n    \"ngE\".to_slice                     => \"\\u{2267}\\u{0338}\",\n    \"ngeqq\".to_slice                   => \"\\u{2267}\\u{0338}\",\n    \"ngeqslant\".to_slice               => \"\\u{2A7E}\\u{0338}\",\n    \"nges\".to_slice                    => \"\\u{2A7E}\\u{0338}\",\n    \"nlE\".to_slice                     => \"\\u{2266}\\u{0338}\",\n    \"nleqq\".to_slice                   => \"\\u{2266}\\u{0338}\",\n    \"nleqslant\".to_slice               => \"\\u{2A7D}\\u{0338}\",\n    \"nles\".to_slice                    => \"\\u{2A7D}\\u{0338}\",\n    \"notinE\".to_slice                  => \"\\u{22F9}\\u{0338}\",\n    \"notindot\".to_slice                => \"\\u{22F5}\\u{0338}\",\n    \"nparsl\".to_slice                  => \"\\u{2AFD}\\u{20E5}\",\n    \"npart\".to_slice                   => \"\\u{2202}\\u{0338}\",\n    \"npre\".to_slice                    => \"\\u{2AAF}\\u{0338}\",\n    \"npreceq\".to_slice                 => \"\\u{2AAF}\\u{0338}\",\n    \"nrarrc\".to_slice                  => \"\\u{2933}\\u{0338}\",\n    \"nrarrw\".to_slice                  => \"\\u{219D}\\u{0338}\",\n    \"nsce\".to_slice                    => \"\\u{2AB0}\\u{0338}\",\n    \"nsubE\".to_slice                   => \"\\u{2AC5}\\u{0338}\",\n    \"nsubset\".to_slice                 => \"\\u{2282}\\u{20D2}\",\n    \"nsubseteqq\".to_slice              => \"\\u{2AC5}\\u{0338}\",\n    \"nsucceq\".to_slice                 => \"\\u{2AB0}\\u{0338}\",\n    \"nsupE\".to_slice                   => \"\\u{2AC6}\\u{0338}\",\n    \"nsupset\".to_slice                 => \"\\u{2283}\\u{20D2}\",\n    \"nsupseteqq\".to_slice              => \"\\u{2AC6}\\u{0338}\",\n    \"nvap\".to_slice                    => \"\\u{224D}\\u{20D2}\",\n    \"nvge\".to_slice                    => \"\\u{2265}\\u{20D2}\",\n    \"nvgt\".to_slice                    => \"\\u{003E}\\u{20D2}\",\n    \"nvle\".to_slice                    => \"\\u{2264}\\u{20D2}\",\n    \"nvlt\".to_slice                    => \"\\u{003C}\\u{20D2}\",\n    \"nvltrie\".to_slice                 => \"\\u{22B4}\\u{20D2}\",\n    \"nvrtrie\".to_slice                 => \"\\u{22B5}\\u{20D2}\",\n    \"nvsim\".to_slice                   => \"\\u{223C}\\u{20D2}\",\n    \"race\".to_slice                    => \"\\u{223D}\\u{0331}\",\n    \"smtes\".to_slice                   => \"\\u{2AAC}\\u{FE00}\",\n    \"sqcaps\".to_slice                  => \"\\u{2293}\\u{FE00}\",\n    \"sqcups\".to_slice                  => \"\\u{2294}\\u{FE00}\",\n    \"varsubsetneq\".to_slice            => \"\\u{228A}\\u{FE00}\",\n    \"varsubsetneqq\".to_slice           => \"\\u{2ACB}\\u{FE00}\",\n    \"varsupsetneq\".to_slice            => \"\\u{228B}\\u{FE00}\",\n    \"varsupsetneqq\".to_slice           => \"\\u{2ACC}\\u{FE00}\",\n    \"vnsub\".to_slice                   => \"\\u{2282}\\u{20D2}\",\n    \"vnsup\".to_slice                   => \"\\u{2283}\\u{20D2}\",\n    \"vsubnE\".to_slice                  => \"\\u{2ACB}\\u{FE00}\",\n    \"vsubne\".to_slice                  => \"\\u{228A}\\u{FE00}\",\n    \"vsupnE\".to_slice                  => \"\\u{2ACC}\\u{FE00}\",\n    \"vsupne\".to_slice                  => \"\\u{228B}\\u{FE00}\",\n  } of Bytes => String\n\n  # :nodoc:\n  MAX_ENTITY_NAME_SIZE = 31\nend\n"
  },
  {
    "path": "src/html.cr",
    "content": "require \"./html/entities\"\n\n# Provides HTML escaping and unescaping methods.\n#\n# For HTML *parsing* see module XML, especially `XML.parse_html`.\n#\n# NOTE: To use `HTML`, you must explicitly import it with `require \"html\"`\nmodule HTML\n  private SUBSTITUTIONS = {\n    '&'  => \"&amp;\",\n    '<'  => \"&lt;\",\n    '>'  => \"&gt;\",\n    '\"'  => \"&quot;\",\n    '\\'' => \"&#39;\",\n  }\n\n  # Escapes special characters in HTML, namely\n  # `&`, `<`, `>`, `\"` and `'`.\n  #\n  # ```\n  # require \"html\"\n  #\n  # HTML.escape(\"Crystal & You\") # => \"Crystal &amp; You\"\n  # ```\n  def self.escape(string : String) : String\n    string.gsub(SUBSTITUTIONS)\n  end\n\n  # Same as `escape(string)` but outputs the result to\n  # the given *io*.\n  #\n  # ```\n  # require \"html\"\n  #\n  # io = IO::Memory.new\n  # HTML.escape(\"Crystal & You\", io) # => nil\n  # io.to_s                          # => \"Crystal &amp; You\"\n  # ```\n  def self.escape(string : String, io : IO) : Nil\n    escape(string.to_slice, io)\n  end\n\n  # Same as `escape(String, IO)` but accepts `Bytes` instead of `String`.\n  #\n  # The slice is assumed to be valid UTF-8.\n  def self.escape(string : Bytes, io : IO) : Nil\n    last_copy_at = 0\n    string.each_with_index do |byte, index|\n      str = case byte\n            when '&'  then \"&amp;\"\n            when '<'  then \"&lt;\"\n            when '>'  then \"&gt;\"\n            when '\"'  then \"&quot;\"\n            when '\\'' then \"&#39;\"\n            else\n              next\n            end\n\n      io.write_string(string[last_copy_at, index &- last_copy_at])\n      last_copy_at = index &+ 1\n      io << str\n    end\n    io.write_string(string[last_copy_at, string.size &- last_copy_at])\n  end\n\n  # These replacements permit compatibility with old numeric entities that\n  # assumed Windows-1252 encoding.\n  # http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference\n  private CHARACTER_REPLACEMENTS = {\n    '\\u20AC', # First entry is what 0x80 should be replaced with.\n    '\\u0081',\n    '\\u201A',\n    '\\u0192',\n    '\\u201E',\n    '\\u2026',\n    '\\u2020',\n    '\\u2021',\n    '\\u02C6',\n    '\\u2030',\n    '\\u0160',\n    '\\u2039',\n    '\\u0152',\n    '\\u008D',\n    '\\u017D',\n    '\\u008F',\n    '\\u0090',\n    '\\u2018',\n    '\\u2019',\n    '\\u201C',\n    '\\u201D',\n    '\\u2022',\n    '\\u2013',\n    '\\u2014',\n    '\\u02DC',\n    '\\u2122',\n    '\\u0161',\n    '\\u203A',\n    '\\u0153',\n    '\\u009D',\n    '\\u017E',\n    '\\u0178', # Last entry is 0x9F.\n    # 0x00->'\\uFFFD' is handled programmatically.\n    # 0x0D->'\\u000D' is a no-op.\n  }\n\n  # Returns a string where named and numeric character references\n  # (e.g. &amp;gt;, &amp;#62;, &amp;#x3e;) in *string* are replaced with the corresponding\n  # unicode characters. This method decodes all HTML5 entities including those\n  # without a trailing semicolon (such as \"&amp;copy\").\n  #\n  # ```\n  # require \"html\"\n  #\n  # HTML.unescape(\"Crystal &amp; You\") # => \"Crystal & You\"\n  # ```\n  def self.unescape(string : String) : String\n    return string unless string.includes?('&')\n\n    String.build(string.bytesize) do |io|\n      unescape(string.to_slice, io)\n    end\n  end\n\n  private def self.unescape(slice, io)\n    while bytesize = slice.index('&'.ord)\n      io.write(slice[0, bytesize])\n      slice += bytesize &+ 1\n\n      ptr = unescape_entity(slice.to_unsafe, io)\n      slice += ptr - slice.to_unsafe\n    end\n\n    io.write slice\n  end\n\n  private def self.unescape_entity(ptr, io)\n    if '#' === ptr.value\n      unescape_numbered_entity(ptr, io)\n    else\n      unescape_named_entity(ptr, io)\n    end\n  end\n\n  private def self.unescape_numbered_entity(ptr, io)\n    start_ptr = ptr\n\n    ptr += 1\n\n    hex = ptr.value.unsafe_chr.in?('x', 'X')\n    if hex\n      ptr += 1\n      base = 16\n    else\n      base = 10\n    end\n\n    x = 0_u32\n\n    # skip leading zeros\n    while ptr.value === '0'\n      ptr += 1\n    end\n    number_start_ptr = ptr\n\n    while digit = ptr.value.unsafe_chr.to_i?(base)\n      # The number of consumed digits is limited to the representation of\n      # Char::MAX_CODEPOINT which is below that of UInt32::MAX\n      x &*= base\n      x &+= digit\n\n      ptr += 1\n    end\n\n    if ptr - number_start_ptr > 8\n      # size exceeds maxlength, so it can't be a valid codepoint and might have\n      # overflow.\n      x = 0_u32\n    end\n\n    size = ptr - start_ptr - (hex ? 2 : 1)\n    unless size > 0 && (char = decode_codepoint(x))\n      # No characters matched or invalid codepoint\n      io << '&'\n      return start_ptr\n    end\n\n    char.to_s(io)\n\n    if ptr.value === ';'\n      ptr += 1\n    end\n\n    return ptr\n  end\n\n  # see https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state\n  private def self.decode_codepoint(codepoint)\n    case codepoint\n    when 0x80..0x9F\n      # Replace characters from Windows-1252 with UTF-8 equivalents.\n      CHARACTER_REPLACEMENTS[codepoint - 0x80]\n    when 0,\n         .>(Char::MAX_CODEPOINT),\n         0xD800..0xDFFF # unicode surrogate characters\n      # Replace invalid characters with replacement character.\n      '\\uFFFD'\n    else\n      # don't replace disallowed codepoints\n      unless codepoint == 0x007F ||\n             # unicode noncharacters\n             (0xFDD0..0xFDEF).includes?(codepoint) ||\n             # last two of each plane (nonchars) disallowed\n             codepoint & 0xFFFF >= 0xFFFE ||\n             # unicode control characters except space\n             (codepoint < 0x0020 && !codepoint.in?(0x0009, 0x000A, 0x000C))\n        codepoint.unsafe_chr\n      end\n    end\n  end\n\n  private def self.unescape_named_entity(ptr, io)\n    # Consume the maximum number of characters possible, with the\n    # consumed characters matching one of the named references.\n    start_ptr = ptr\n\n    while ptr.value.unsafe_chr.ascii_alphanumeric?\n      ptr += 1\n    end\n\n    if ptr == start_ptr\n      io << '&'\n      return start_ptr\n    end\n\n    # The entity name cannot be longer than the longest name in the lookup tables.\n    entity_name = Slice.new(start_ptr, Math.min(ptr - start_ptr, MAX_ENTITY_NAME_SIZE))\n\n    # If we can't find an entity on the first try, we need to search each prefix\n    # of it, starting from the largest.\n    while entity_name.size >= 2\n      case\n      when x = SINGLE_CHAR_ENTITIES[entity_name]?\n        io << x\n      when x = DOUBLE_CHAR_ENTITIES[entity_name]?\n        io << x\n      else\n        entity_name = entity_name[0..-2]\n        next\n      end\n\n      ptr = start_ptr + entity_name.size\n      if ptr.value === ';'\n        ptr += 1\n      end\n\n      return ptr\n    end\n\n    # range -1 includes the leading '&'\n    start_ptr -= 1\n    io.write Slice.new(start_ptr, ptr - start_ptr)\n    ptr\n  end\nend\n"
  },
  {
    "path": "src/http/client/response.cr",
    "content": "require \"../common\"\nrequire \"mime/media_type\"\n\nclass HTTP::Client\n  class Response\n    getter version : String\n    getter status : HTTP::Status\n    getter status_message : String?\n    getter headers : Headers\n    getter! body_io : IO\n    @cookies : Cookies?\n\n    def initialize(@status : HTTP::Status, @body : String? = nil, @headers : Headers = Headers.new, status_message : String? = nil, @version = \"HTTP/1.1\", @body_io : IO? = nil)\n      @status_message = status_message || @status.description\n\n      if Response.mandatory_body?(@status)\n        @body = \"\" unless @body || @body_io\n      else\n        if (@body || @body_io) && (headers[\"Content-Length\"]? != \"0\")\n          raise ArgumentError.new(\"Status #{status.code} should not have a body\")\n        end\n      end\n    end\n\n    def self.new(status_code : Int32, body : String? = nil, headers : Headers = Headers.new, status_message : String? = nil, version : String = \"HTTP/1.1\", body_io : IO? = nil) : self\n      new(HTTP::Status.new(status_code), body, headers, status_message, version, body_io)\n    end\n\n    def body : String\n      @body || \"\"\n    end\n\n    def body? : String?\n      @body\n    end\n\n    # Returns `true` if the response status code is between 200 and 299.\n    def success? : Bool\n      @status.success?\n    end\n\n    # Returns a convenience wrapper around querying and setting cookie related\n    # headers, see `HTTP::Cookies`.\n    def cookies : HTTP::Cookies\n      @cookies ||= Cookies.from_server_headers(headers)\n    end\n\n    def keep_alive? : Bool\n      HTTP.keep_alive?(self)\n    end\n\n    def content_type : String?\n      mime_type.try &.media_type\n    end\n\n    # Convenience method to retrieve the HTTP status code.\n    def status_code : Int32\n      status.code\n    end\n\n    def charset : String?\n      mime_type.try &.[\"charset\"]?\n    end\n\n    def mime_type : MIME::MediaType?\n      if content_type = headers[\"Content-Type\"]?.presence\n        MIME::MediaType.parse(content_type)\n      end\n    end\n\n    def to_io(io : IO) : Nil\n      io << @version << ' ' << @status.code << ' ' << @status_message << \"\\r\\n\"\n      cookies = @cookies\n      headers = cookies ? cookies.add_response_headers(@headers) : @headers\n      HTTP.serialize_headers_and_body(io, headers, @body, @body_io, @version)\n    end\n\n    # :nodoc:\n    def consume_body_io : Nil\n      if io = @body_io\n        @body = io.gets_to_end\n        @body_io = nil\n      end\n    end\n\n    def self.mandatory_body?(status : HTTP::Status) : Bool\n      !(status.informational? || status.no_content? || status.not_modified?)\n    end\n\n    def self.supports_chunked?(version : String) : Bool\n      version == \"HTTP/1.1\"\n    end\n\n    def self.from_io(io : IO, ignore_body : Bool = false, decompress : Bool = true) : self\n      from_io?(io, ignore_body, decompress) ||\n        raise(\"Unexpected end of http request\")\n    end\n\n    # Parses an `HTTP::Client::Response` from the given `IO`.\n    # Might return `nil` if there's no data in the `IO`,\n    # which probably means that the connection was closed.\n    def self.from_io?(io : IO, ignore_body : Bool = false, decompress : Bool = true) : self?\n      from_io?(io, ignore_body: ignore_body, decompress: decompress) do |response|\n        if response\n          response.consume_body_io\n          return response\n        else\n          return nil\n        end\n      end\n    end\n\n    def self.from_io(io, ignore_body = false, decompress = true, &)\n      from_io?(io, ignore_body, decompress) do |response|\n        if response\n          yield response\n        else\n          raise(\"Unexpected end of http request\")\n        end\n      end\n    end\n\n    # Parses an `HTTP::Client::Response` from the given `IO` and yields\n    # it to the block. Might yield `nil` if there's no data in the `IO`,\n    # which probably means that the connection was closed.\n    def self.from_io?(io, ignore_body = false, decompress = true, &block)\n      line = io.gets(4096, chomp: true)\n      return yield nil unless line\n\n      pieces = line.split(3)\n      raise \"Invalid HTTP response\" if pieces.size < 2\n\n      http_version = pieces[0]\n      raise \"Unsupported HTTP version: #{http_version}\" unless HTTP::SUPPORTED_VERSIONS.includes?(http_version)\n\n      status_code = pieces[1].to_i?\n\n      unless status_code && 100 <= status_code < 1000\n        raise \"Invalid HTTP status code: #{pieces[1]}\"\n      end\n\n      status = HTTP::Status.new(status_code)\n      status_message = pieces[2]? ? pieces[2].chomp : \"\"\n\n      body_type = HTTP::BodyType::OnDemand\n      body_type = HTTP::BodyType::Mandatory if mandatory_body?(status)\n      body_type = HTTP::BodyType::Prohibited if ignore_body\n\n      HTTP.parse_headers_and_body(io, body_type: body_type, decompress: decompress) do |headers, body|\n        return yield new status, nil, headers, status_message, http_version, body\n      end\n\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/client.cr",
    "content": "{% if !flag?(:without_openssl) %}\n  require \"openssl\"\n{% end %}\n\n# An HTTP Client.\n#\n# NOTE: To use `Client`, you must explicitly import it with `require \"http/client\"`\n#\n# ### One-shot usage\n#\n# Without a block, an `HTTP::Client::Response` is returned and the response's body\n# is available as a `String` by invoking `HTTP::Client::Response#body`.\n#\n# ```\n# require \"http/client\"\n#\n# response = HTTP::Client.get \"http://www.example.com\"\n# response.status_code      # => 200\n# response.body.lines.first # => \"<!doctype html>\"\n# ```\n#\n# ### Parameters\n#\n# Parameters can be added to any request with the `URI::Params.encode` method, which\n# converts a `Hash` or `NamedTuple` to a URL encoded HTTP query.\n#\n# ```\n# require \"http/client\"\n#\n# params = URI::Params.encode({\"author\" => \"John Doe\", \"offset\" => \"20\"}) # => \"author=John+Doe&offset=20\"\n# response = HTTP::Client.get URI.new(\"http\", \"www.example.com\", query: params)\n# response.status_code # => 200\n# ```\n#\n# ### Streaming\n#\n# With a block, an `HTTP::Client::Response` body is returned and the response's body\n# is available as an `IO` by invoking `HTTP::Client::Response#body_io`.\n#\n# ```\n# require \"http/client\"\n#\n# HTTP::Client.get(\"http://www.example.com\") do |response|\n#   response.status_code  # => 200\n#   response.body_io.gets # => \"<!doctype html>\"\n# end\n# ```\n#\n# ### Reusing a connection\n#\n# Similar to the above cases, but creating an instance of an `HTTP::Client`.\n#\n# ```\n# require \"http/client\"\n#\n# client = HTTP::Client.new \"www.example.com\"\n# response = client.get \"/\"\n# response.status_code      # => 200\n# response.body.lines.first # => \"<!doctype html>\"\n# client.close\n# ```\n#\n# WARNING: A single `HTTP::Client` instance is not safe for concurrent use by multiple fibers.\n#\n# ### Compression\n#\n# If `compress` isn't set to `false`, and no `Accept-Encoding` header is explicitly specified,\n# an HTTP::Client will add an `\"Accept-Encoding\": \"gzip, deflate\"` header, and automatically decompress\n# the response body/body_io.\n#\n# ### Encoding\n#\n# If a response has a `Content-Type` header with a charset, that charset is set as the encoding\n# of the returned IO (or used for creating a String for the body). Invalid bytes in the given encoding\n# are silently ignored when reading text content.\nclass HTTP::Client\n  # The set of possible valid body types.\n  alias BodyType = String | Bytes | IO | Nil\n\n  # Returns the target host.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\"\n  # client.host # => \"www.example.com\"\n  # ```\n  getter host : String\n\n  # Returns the target port.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\"\n  # client.port # => 80\n  # ```\n  getter port : Int32\n\n  # If this client uses TLS, returns its `OpenSSL::SSL::Context::Client`, raises otherwise.\n  #\n  # Changes made after the initial request will have no effect.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\", tls: true\n  # client.tls # => #<OpenSSL::SSL::Context::Client ...>\n  # ```\n  {% if flag?(:without_openssl) %}\n    getter! tls : Nil\n    alias TLSContext = Bool | Nil\n  {% else %}\n    getter! tls : OpenSSL::SSL::Context::Client\n    alias TLSContext = OpenSSL::SSL::Context::Client | Bool | Nil\n  {% end %}\n\n  # Whether automatic compression/decompression is enabled.\n  property? compress : Bool = true\n\n  @io : IO?\n  @reconnect = true\n\n  # Creates a new HTTP client with the given *host*, *port* and *tls*\n  # configurations. If no port is given, the default one will\n  # be used depending on the *tls* arguments: 80 for if *tls* is `false`,\n  # 443 if *tls* is truthy. If *tls* is `true` a new `OpenSSL::SSL::Context::Client` will\n  # be used, else the given one. In any case the active context can be accessed through `tls`.\n  def initialize(@host : String, port = nil, tls : TLSContext = nil)\n    check_host_only(@host)\n\n    {% if flag?(:without_openssl) %}\n      if tls\n        raise \"HTTP::Client TLS is disabled because `-D without_openssl` was passed at compile time\"\n      end\n      @tls = nil\n    {% else %}\n      @tls = case tls\n             when true\n               OpenSSL::SSL::Context::Client.new\n             when OpenSSL::SSL::Context::Client\n               tls\n             when false, nil\n               nil\n             end\n    {% end %}\n\n    @port = (port || (@tls ? 443 : 80)).to_i\n  end\n\n  # Creates a new HTTP client bound to an existing `IO`.\n  # *host* and *port* can be specified and they will be used\n  # to conform the `Host` header on each request.\n  # Instances created with this constructor cannot be reconnected. Once\n  # `close` is called explicitly or if the connection doesn't support keep-alive,\n  # the next call to make a request will raise an exception.\n  def initialize(@io : IO, @host = \"\", @port = 80)\n    @reconnect = false\n  end\n\n  private def check_host_only(string : String)\n    # When parsing a URI with just a host\n    # we end up with a URI with just a path\n    uri = URI.parse(string)\n    if uri.scheme || uri.host || uri.port || uri.query || uri.user || uri.password || uri.path.includes?('/')\n      raise_invalid_host(string)\n    end\n  rescue URI::Error\n    raise_invalid_host(string)\n  end\n\n  private def raise_invalid_host(string : String)\n    raise ArgumentError.new(\"The string passed to create an HTTP::Client must be just a host, not #{string.inspect}\")\n  end\n\n  # Creates a new HTTP client from a URI. Parses the *host*, *port*,\n  # and *tls* configuration from the URI provided. Port defaults to\n  # 80 if not specified unless using the https protocol, which defaults\n  # to port 443 and sets tls to `true`.\n  #\n  # ```\n  # require \"http/client\"\n  # require \"uri\"\n  #\n  # uri = URI.parse(\"https://secure.example.com\")\n  # client = HTTP::Client.new(uri)\n  #\n  # client.tls? # => #<OpenSSL::SSL::Context::Client>\n  # client.get(\"/\")\n  # ```\n  # This constructor will *ignore* any path or query segments in the URI\n  # as those will need to be passed to the client when a request is made.\n  #\n  # If *tls* is given it will be used, if not a new TLS context will be created.\n  # If *tls* is given and *uri* is a HTTP URI, `ArgumentError` is raised.\n  # In any case the active context can be accessed through `tls`.\n  #\n  # This constructor will raise an exception if any scheme but HTTP or HTTPS\n  # is used.\n  def self.new(uri : URI, tls : TLSContext = nil) : self\n    tls = tls_flag(uri, tls)\n    host = validate_host(uri)\n    new(host, uri.port, tls)\n  end\n\n  # Creates a new HTTP client from a URI, yields it to the block and closes the\n  # client afterwards. Parses the *host*, *port*, and *tls* configuration from\n  # the URI provided. Port defaults to 80 if not specified unless using the\n  # https protocol, which defaults to port 443 and sets tls to `true`.\n  #\n  # ```\n  # require \"http/client\"\n  # require \"uri\"\n  #\n  # uri = URI.parse(\"https://secure.example.com\")\n  # HTTP::Client.new(uri) do |client|\n  #   client.tls? # => #<OpenSSL::SSL::Context::Client>\n  #   client.get(\"/\")\n  # end\n  # ```\n  # This constructor will *ignore* any path or query segments in the URI\n  # as those will need to be passed to the client when a request is made.\n  #\n  # If *tls* is given it will be used, if not a new TLS context will be created.\n  # If *tls* is given and *uri* is a HTTP URI, `ArgumentError` is raised.\n  # In any case the active context can be accessed through `tls`.\n  #\n  # This constructor will raise an exception if any scheme but HTTP or HTTPS\n  # is used.\n  def self.new(uri : URI, tls : TLSContext = nil, &)\n    tls = tls_flag(uri, tls)\n    host = validate_host(uri)\n    client = new(host, uri.port, tls)\n    begin\n      yield client\n    ensure\n      client.close\n    end\n  end\n\n  # Creates a new HTTP client, yields it to the block, and closes\n  # the client afterwards.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # HTTP::Client.new(\"www.example.com\") do |client|\n  #   client.get \"/\"\n  # end\n  # ```\n  def self.new(host : String, port = nil, tls : TLSContext = nil, &)\n    client = new(host, port, tls)\n    begin\n      yield client\n    ensure\n      client.close\n    end\n  end\n\n  # Configures this client to perform basic authentication in every\n  # request.\n  def basic_auth(username, password) : Nil\n    header = \"Basic #{Base64.strict_encode(\"#{username}:#{password}\")}\"\n    before_request do |request|\n      request.headers[\"Authorization\"] = header\n    end\n  end\n\n  # Sets the number of seconds to wait when reading before raising an `IO::TimeoutError`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"example.org\")\n  # client.read_timeout = 1.5\n  # begin\n  #   response = client.get(\"/\")\n  # rescue IO::TimeoutError\n  #   puts \"Timeout!\"\n  # end\n  # ```\n  @[Deprecated(\"Use `#read_timeout=(Time::Span)` instead\")]\n  def read_timeout=(read_timeout : Number)\n    self.read_timeout = read_timeout.seconds\n  end\n\n  # Sets the read timeout with a `Time::Span`, to wait when reading before raising an `IO::TimeoutError`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"example.org\")\n  # client.read_timeout = 5.minutes\n  # begin\n  #   response = client.get(\"/\")\n  # rescue IO::TimeoutError\n  #   puts \"Timeout!\"\n  # end\n  # ```\n  setter read_timeout : Time::Span?\n\n  # Sets the write timeout - if any chunk of request is not written\n  # within the number of seconds provided, `IO::TimeoutError` exception is raised.\n  @[Deprecated(\"Use `#write_timeout=(Time::Span)` instead\")]\n  def write_timeout=(write_timeout : Number)\n    self.write_timeout = write_timeout.seconds\n  end\n\n  # Sets the write timeout - if any chunk of request is not written\n  # within the provided `Time::Span`,  `IO::TimeoutError` exception is raised.\n  setter write_timeout : Time::Span?\n\n  # Sets the number of seconds to wait when connecting, before raising an `IO::TimeoutError`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"example.org\")\n  # client.connect_timeout = 1.5\n  # begin\n  #   response = client.get(\"/\")\n  # rescue IO::TimeoutError\n  #   puts \"Timeout!\"\n  # end\n  # ```\n  @[Deprecated(\"Use `#connect_timeout=(Time::Span)` instead\")]\n  def connect_timeout=(connect_timeout : Number)\n    self.connect_timeout = connect_timeout.seconds\n  end\n\n  # Sets the open timeout with a `Time::Span` to wait when connecting, before raising an `IO::TimeoutError`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"example.org\")\n  # client.connect_timeout = 5.minutes\n  # begin\n  #   response = client.get(\"/\")\n  # rescue IO::TimeoutError\n  #   puts \"Timeout!\"\n  # end\n  # ```\n  setter connect_timeout : Time::Span?\n\n  # Sets the number of seconds to wait when resolving a name, before raising an `IO::TimeoutError`.\n  #\n  # NOTE: *dns_timeout* is currently only supported on Windows.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"example.org\")\n  # client.dns_timeout = 1.5\n  # begin\n  #   response = client.get(\"/\")\n  # rescue IO::TimeoutError\n  #   puts \"Timeout!\"\n  # end\n  # ```\n  @[Deprecated(\"Use `#dns_timeout=(Time::Span)` instead\")]\n  def dns_timeout=(dns_timeout : Number)\n    self.dns_timeout = dns_timeout.seconds\n  end\n\n  # Sets the number of seconds to wait when resolving a name with a `Time::Span`, before raising an `IO::TimeoutError`.\n  #\n  # NOTE: *dns_timeout* is currently only supported on Windows.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"example.org\")\n  # client.dns_timeout = 1.5.seconds\n  # begin\n  #   response = client.get(\"/\")\n  # rescue IO::TimeoutError\n  #   puts \"Timeout!\"\n  # end\n  # ```\n  setter dns_timeout : Time::Span?\n\n  # Adds a callback to execute before each request. This is usually\n  # used to set an authorization header. Any number of callbacks\n  # can be added.\n  #\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new(\"www.example.com\")\n  # client.before_request do |request|\n  #   request.headers[\"Authorization\"] = \"XYZ123\"\n  # end\n  # client.get \"/\"\n  # ```\n  def before_request(&callback : HTTP::Request ->) : Nil\n    before_request = @before_request ||= [] of (HTTP::Request ->)\n    before_request << callback\n  end\n\n  {% for method in %w(get post put head delete patch options) %}\n    # Executes a {{method.id.upcase}} request.\n    # The response will have its body as a `String`, accessed via `HTTP::Client::Response#body`.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # client = HTTP::Client.new(\"www.example.com\")\n    # response = client.{{method.id}}(\"/\", headers: HTTP::Headers{\"User-Agent\" => \"AwesomeApp\"}, body: \"Hello!\")\n    # response.body #=> \"...\"\n    # ```\n    def {{method.id}}(path, headers : HTTP::Headers? = nil, body : BodyType = nil) : HTTP::Client::Response\n      exec {{method.upcase}}, path, headers, body\n    end\n\n    # Executes a {{method.id.upcase}} request and yields the response to the block.\n    # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # client = HTTP::Client.new(\"www.example.com\")\n    # client.{{method.id}}(\"/\", headers: HTTP::Headers{\"User-Agent\" => \"AwesomeApp\"}, body: \"Hello!\") do |response|\n    #   response.body_io.gets #=> \"...\"\n    # end\n    # ```\n    def {{method.id}}(path, headers : HTTP::Headers? = nil, body : BodyType = nil)\n      exec {{method.upcase}}, path, headers, body do |response|\n        yield response\n      end\n    end\n\n    # Executes a {{method.id.upcase}} request.\n    # The response will have its body as a `String`, accessed via `HTTP::Client::Response#body`.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # response = HTTP::Client.{{method.id}}(\"/\", headers: HTTP::Headers{\"User-Agent\" => \"AwesomeApp\"}, body: \"Hello!\")\n    # response.body #=> \"...\"\n    # ```\n    def self.{{method.id}}(url : String | URI, headers : HTTP::Headers? = nil, body : BodyType = nil, tls : TLSContext = nil) : HTTP::Client::Response\n      exec {{method.upcase}}, url, headers, body, tls\n    end\n\n    # Executes a {{method.id.upcase}} request and yields the response to the block.\n    # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # HTTP::Client.{{method.id}}(\"/\", headers: HTTP::Headers{\"User-Agent\" => \"AwesomeApp\"}, body: \"Hello!\") do |response|\n    #   response.body_io.gets #=> \"...\"\n    # end\n    # ```\n    def self.{{method.id}}(url : String | URI, headers : HTTP::Headers? = nil, body : BodyType = nil, tls : TLSContext = nil)\n      exec {{method.upcase}}, url, headers, body, tls do |response|\n        yield response\n      end\n    end\n\n    # Executes a {{method.id.upcase}} request with form data and returns a `Response`. The \"Content-Type\" header is set\n    # to \"application/x-www-form-urlencoded\".\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # client = HTTP::Client.new \"www.example.com\"\n    # response = client.{{method.id}} \"/\", form: \"foo=bar\"\n    # ```\n    def {{method.id}}(path, headers : HTTP::Headers? = nil, *, form : String | IO) : HTTP::Client::Response\n      request = new_request({{method.upcase}}, path, headers, form)\n      request.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\"\n      exec request\n    end\n\n    # Executes a {{method.id.upcase}} request with form data and yields the response to the block.\n    # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n    # The \"Content-Type\" header is set to \"application/x-www-form-urlencoded\".\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # client = HTTP::Client.new \"www.example.com\"\n    # client.{{method.id}}(\"/\", form: \"foo=bar\") do |response|\n    #   response.body_io.gets\n    # end\n    # ```\n    def {{method.id}}(path, headers : HTTP::Headers? = nil, *, form : String | IO)\n      request = new_request({{method.upcase}}, path, headers, form)\n      request.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\"\n      exec(request) do |response|\n        yield response\n      end\n    end\n\n    # Executes a {{method.id.upcase}} request with form data and returns a `Response`. The \"Content-Type\" header is set\n    # to \"application/x-www-form-urlencoded\".\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # client = HTTP::Client.new \"www.example.com\"\n    # response = client.{{method.id}} \"/\", form: {\"foo\" => \"bar\"}\n    # ```\n    def {{method.id}}(path, headers : HTTP::Headers? = nil, *, form : Hash(String, String) | NamedTuple) : HTTP::Client::Response\n      body = URI::Params.encode(form)\n      {{method.id}} path, form: body, headers: headers\n    end\n\n    # Executes a {{method.id.upcase}} request with form data and yields the response to the block.\n    # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n    # The \"Content-type\" header is set to \"application/x-www-form-urlencoded\".\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # client = HTTP::Client.new \"www.example.com\"\n    # client.{{method.id}}(\"/\", form: {\"foo\" => \"bar\"}) do |response|\n    #   response.body_io.gets\n    # end\n    # ```\n    def {{method.id}}(path, headers : HTTP::Headers? = nil, *, form : Hash(String, String) | NamedTuple)\n      body = URI::Params.encode(form)\n      {{method.id}}(path, form: body, headers: headers) do |response|\n        yield response\n      end\n    end\n\n    # Executes a {{method.id.upcase}} request with form data and returns a `Response`. The \"Content-Type\" header is set\n    # to \"application/x-www-form-urlencoded\".\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # response = HTTP::Client.{{method.id}} \"http://www.example.com\", form: \"foo=bar\"\n    # ```\n    def self.{{method.id}}(url, headers : HTTP::Headers? = nil, tls : TLSContext = nil, *, form : String | IO | Hash) : HTTP::Client::Response\n      exec(url, tls) do |client, path|\n        client.{{method.id}}(path, form: form, headers: headers)\n      end\n    end\n\n    # Executes a {{method.id.upcase}} request with form data and yields the response to the block.\n    # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n    # The \"Content-Type\" header is set to \"application/x-www-form-urlencoded\".\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # HTTP::Client.{{method.id}}(\"http://www.example.com\", form: \"foo=bar\") do |response|\n    #   response.body_io.gets\n    # end\n    # ```\n    def self.{{method.id}}(url, headers : HTTP::Headers? = nil, tls : TLSContext = nil, *, form : String | IO | Hash)\n      exec(url, tls) do |client, path|\n        client.{{method.id}}(path, form: form, headers: headers) do |response|\n          yield response\n        end\n      end\n    end\n  {% end %}\n\n  # Executes a request.\n  # The response will have its body as a `String`, accessed via `HTTP::Client::Response#body`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\"\n  # response = client.exec HTTP::Request.new(\"GET\", \"/\")\n  # response.body # => \"...\"\n  # ```\n  def exec(request : HTTP::Request) : HTTP::Client::Response\n    around_exec(request) do\n      exec_internal(request)\n    end\n  end\n\n  private def exec_internal(request)\n    implicit_compression = implicit_compression?(request)\n\n    set_defaults request\n    run_before_request_callbacks(request)\n\n    begin\n      response = exec_internal_single(request, implicit_compression: implicit_compression)\n    rescue exc : IO::Error\n      raise exc if @io.nil? # do not retry if client was closed\n      response = nil\n    end\n    return handle_response(response) if response\n\n    # Server probably closed the connection, so retry once\n    close\n    request.body.try &.rewind\n    response = exec_internal_single(request, implicit_compression: implicit_compression)\n    return handle_response(response) if response\n\n    raise IO::EOFError.new(\"Unexpected end of http response\")\n  end\n\n  private def exec_internal_single(request, implicit_compression = false)\n    send_request(request)\n    HTTP::Client::Response.from_io?(io, ignore_body: request.ignore_body?, decompress: implicit_compression)\n  end\n\n  private def handle_response(response)\n    close unless response.keep_alive?\n    response\n  end\n\n  # Executes a request and yields an `HTTP::Client::Response` to the block.\n  # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\"\n  # client.exec(HTTP::Request.new(\"GET\", \"/\")) do |response|\n  #   response.body_io.gets # => \"...\"\n  # end\n  # ```\n  def exec(request : HTTP::Request, &block)\n    around_exec(request) do\n      exec_internal(request) do |response|\n        yield response\n      end\n    end\n  end\n\n  private def exec_internal(request, &block : Response -> T) : T forall T\n    implicit_compression = implicit_compression?(request)\n\n    set_defaults request\n    run_before_request_callbacks(request)\n\n    exec_internal_single(request, ignore_io_error: true, implicit_compression: implicit_compression) do |response|\n      if response\n        return handle_response(response) { yield response }\n      end\n    end\n\n    # Server probably closed the connection, so retry once\n    close\n    request.body.try &.rewind\n    exec_internal_single(request, implicit_compression: implicit_compression) do |response|\n      if response\n        return handle_response(response) { yield response }\n      end\n    end\n    raise IO::EOFError.new(\"Unexpected end of http response\")\n  end\n\n  private def exec_internal_single(request, ignore_io_error = false, implicit_compression = false, &)\n    begin\n      send_request(request)\n    rescue ex : IO::Error\n      return yield nil if ignore_io_error && !@io.nil? # ignore_io_error only if client was not closed\n      raise ex\n    end\n    HTTP::Client::Response.from_io?(io, ignore_body: request.ignore_body?, decompress: implicit_compression) do |response|\n      yield response\n    end\n  end\n\n  private def handle_response(response, &)\n    yield\n  ensure\n    response.body_io?.try &.close\n    close unless response.keep_alive?\n  end\n\n  private def send_request(request)\n    request.to_io(io)\n    io.flush\n  end\n\n  private def set_defaults(request)\n    request.headers[\"Host\"] ||= host_header\n    request.headers[\"User-Agent\"] ||= \"Crystal\"\n\n    if implicit_compression?(request)\n      request.headers[\"Accept-Encoding\"] = \"gzip, deflate\"\n    end\n  end\n\n  private def implicit_compression?(request)\n    {% if flag?(:without_zlib) %}\n      false\n    {% else %}\n      compress? && !request.headers.has_key?(\"Accept-Encoding\")\n    {% end %}\n  end\n\n  # For one-shot headers we don't want keep-alive (might delay closing the response)\n  private def self.default_one_shot_headers(headers)\n    headers ||= HTTP::Headers.new\n    headers[\"Connection\"] ||= \"close\"\n    headers\n  end\n\n  private def run_before_request_callbacks(request)\n    @before_request.try &.each &.call(request)\n  end\n\n  # Executes a request.\n  # The response will have its body as a `String`, accessed via `HTTP::Client::Response#body`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\"\n  # response = client.exec \"GET\", \"/\"\n  # response.body # => \"...\"\n  # ```\n  def exec(method : String, path : String, headers : HTTP::Headers? = nil, body : BodyType = nil) : HTTP::Client::Response\n    exec new_request method, path, headers, body\n  end\n\n  # Executes a request.\n  # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # client = HTTP::Client.new \"www.example.com\"\n  # client.exec(\"GET\", \"/\") do |response|\n  #   response.body_io.gets # => \"...\"\n  # end\n  # ```\n  def exec(method : String, path, headers : HTTP::Headers? = nil, body : BodyType = nil, &)\n    exec(new_request(method, path, headers, body)) do |response|\n      yield response\n    end\n  end\n\n  # Executes a request.\n  # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # response = HTTP::Client.exec \"GET\", \"http://www.example.com\"\n  # response.body # => \"...\"\n  # ```\n  def self.exec(method : String, url : String | URI, headers : HTTP::Headers? = nil, body : BodyType = nil, tls : TLSContext = nil) : HTTP::Client::Response\n    headers = default_one_shot_headers(headers)\n    exec(url, tls) do |client, path|\n      client.exec method, path, headers, body\n    end\n  end\n\n  # Executes a request.\n  # The response will have its body as an `IO` accessed via `HTTP::Client::Response#body_io`.\n  #\n  # ```\n  # require \"http/client\"\n  #\n  # HTTP::Client.exec(\"GET\", \"http://www.example.com\") do |response|\n  #   response.body_io.gets # => \"...\"\n  # end\n  # ```\n  def self.exec(method, url : String | URI, headers : HTTP::Headers? = nil, body : BodyType = nil, tls : TLSContext = nil, &)\n    headers = default_one_shot_headers(headers)\n    exec(url, tls) do |client, path|\n      client.exec(method, path, headers, body) do |response|\n        yield response\n      end\n    end\n  end\n\n  # Closes this client. If used again, a new connection will be opened.\n  def close : Nil\n    @io.try &.close\n  rescue IO::Error\n    nil\n  ensure\n    @io = nil\n  end\n\n  private def new_request(method, path, headers, body : BodyType)\n    HTTP::Request.new(method, path, headers, body)\n  end\n\n  private def io\n    io = @io\n    return io if io\n    unless @reconnect\n      raise \"This HTTP::Client cannot be reconnected\"\n    end\n\n    hostname = @host.starts_with?('[') && @host.ends_with?(']') ? @host[1..-2] : @host\n    io = TCPSocket.new hostname, @port, @dns_timeout, @connect_timeout\n    io.read_timeout = @read_timeout if @read_timeout\n    io.write_timeout = @write_timeout if @write_timeout\n    io.sync = false\n\n    {% if !flag?(:without_openssl) %}\n      if tls = @tls\n        tcp_socket = io\n        begin\n          io = OpenSSL::SSL::Socket::Client.new(tcp_socket, context: tls, sync_close: true, hostname: @host.rchop('.'))\n        rescue exc\n          # don't leak the TCP socket when the SSL connection failed\n          tcp_socket.close\n          raise exc\n        end\n      end\n    {% end %}\n\n    @io = io\n  end\n\n  private def host_header\n    if (@tls && @port != 443) || (!@tls && @port != 80)\n      \"#{@host}:#{@port}\"\n    else\n      @host\n    end\n  end\n\n  private def self.exec(string : String, tls : TLSContext = nil, &)\n    uri = URI.parse(string)\n\n    unless uri.scheme && uri.host\n      # Assume http if no scheme and host are specified\n      uri = URI.parse(\"http://#{string}\")\n    end\n\n    exec(uri, tls) do |client, path|\n      yield client, path\n    end\n  end\n\n  protected def self.tls_flag(uri, context : TLSContext) : TLSContext\n    scheme = uri.scheme\n    raise ArgumentError.new(\"Missing scheme: #{uri}\") if !scheme\n    {% if flag?(:without_openssl) %}\n      case scheme\n      when \"http\"  then false\n      when \"https\" then true\n      else              raise ArgumentError.new \"Unsupported scheme: #{scheme}\"\n      end\n    {% else %}\n      case {scheme, context}\n      when {\"http\", false}, {\"http\", nil}\n        false\n      when {\"http\", OpenSSL::SSL::Context::Client}\n        raise ArgumentError.new(\"TLS context given for HTTP URI\")\n      when {\"https\", true}, {\"https\", nil}\n        true\n      when {\"https\", OpenSSL::SSL::Context::Client}\n        context\n      else\n        raise ArgumentError.new \"Unsupported scheme: #{scheme}\"\n      end\n    {% end %}\n  end\n\n  protected def self.validate_host(uri)\n    host = uri.host.presence\n    return host if host\n\n    raise ArgumentError.new \"Request URI must have host (URI is: #{uri})\"\n  end\n\n  private def self.exec(uri : URI, tls : TLSContext = nil, &)\n    tls = tls_flag(uri, tls)\n    host = validate_host(uri)\n\n    port = uri.port\n    path = uri.request_target\n    user = uri.user\n    password = uri.password\n\n    new(host, port, tls) do |client|\n      if user && password\n        client.basic_auth(user, password)\n      end\n      yield client, path\n    end\n  end\n\n  # This method is called when executing the request. Although it can be\n  # redefined, it is recommended to use the `def_around_exec` macro to be\n  # able to add new behaviors without losing prior existing ones.\n  protected def around_exec(request, &)\n    yield\n  end\n\n  # This macro allows injecting code to be run before and after the execution\n  # of the request. It should return the yielded value. It must be called with 1\n  # block argument that will be used to pass the `HTTP::Request`.\n  #\n  # ```\n  # class HTTP::Client\n  #   def_around_exec do |request|\n  #     # do something before exec\n  #     res = yield\n  #     # do something after exec\n  #     res\n  #   end\n  # end\n  # ```\n  macro def_around_exec(&block)\n    protected def around_exec(%request)\n      previous_def do\n        {% if block.args.size != 1 %}\n          {% raise \"Wrong number of block parameters for macro 'def_around_exec' (given #{block.args.size}, expected 1)\" %}\n        {% end %}\n\n        {{ block.args.first.id }} = %request\n        {{ block.body }}\n      end\n    end\n  end\nend\n\nrequire \"socket\"\nrequire \"uri\"\nrequire \"base64\"\nrequire \"./common\"\n"
  },
  {
    "path": "src/http/common.cr",
    "content": "require \"mime/media_type\"\n{% if !flag?(:without_zlib) %}\n  require \"compress/deflate\"\n  require \"compress/gzip\"\n{% end %}\n\nmodule HTTP\n  # Default maximum permitted size (in bytes) of the request line in an HTTP request.\n  MAX_REQUEST_LINE_SIZE = 8192 # 8 KB\n\n  # Default maximum permitted combined size (in bytes) of the headers in an HTTP request.\n  MAX_HEADERS_SIZE = 16_384 # 16 KB\n\n  # :nodoc:\n  enum BodyType\n    OnDemand\n    Prohibited\n    Mandatory\n  end\n\n  SUPPORTED_VERSIONS = {\"HTTP/1.0\", \"HTTP/1.1\"}\n\n  # :nodoc:\n  record EndOfRequest\n  # :nodoc:\n  record HeaderLine, name : String, value : String, bytesize : Int32\n\n  # :nodoc:\n  def self.parse_headers_and_body(io, body_type : BodyType = BodyType::OnDemand, decompress = true, *, max_headers_size : Int32 = MAX_HEADERS_SIZE, &) : HTTP::Status?\n    headers = Headers.new\n\n    max_size = max_headers_size\n    while header_line = read_header_line(io, max_size)\n      case header_line\n      when EndOfRequest\n        body = nil\n\n        if body_type.prohibited?\n          body = nil\n        elsif content_length = content_length(headers)\n          body = FixedLengthContent.new(io, content_length)\n        elsif headers[\"Transfer-Encoding\"]? == \"chunked\"\n          body = ChunkedContent.new(io)\n        elsif body_type.mandatory?\n          body = UnknownLengthContent.new(io)\n        end\n\n        if body.is_a?(Content) && expect_continue?(headers)\n          body.expects_continue = true\n        end\n\n        if decompress && body\n          encoding = headers[\"Content-Encoding\"]?\n          {% if flag?(:without_zlib) %}\n            case encoding\n            when \"gzip\", \"deflate\"\n              raise \"Can't decompress because `-D without_zlib` was passed at compile time\"\n            else\n              # not a format we support\n            end\n          {% else %}\n            case encoding\n            when \"gzip\"\n              body = Compress::Gzip::Reader.new(body, sync_close: true)\n              headers.delete(\"Content-Encoding\")\n              headers.delete(\"Content-Length\")\n            when \"deflate\"\n              body = Compress::Deflate::Reader.new(body, sync_close: true)\n              headers.delete(\"Content-Encoding\")\n              headers.delete(\"Content-Length\")\n            else\n              # not a format we support\n            end\n          {% end %}\n        end\n\n        check_content_type_charset(body, headers)\n\n        yield headers, body\n        return\n      else # HeaderLine\n        max_size -= header_line.bytesize\n        return HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE if max_size < 0\n\n        return HTTP::Status::BAD_REQUEST unless headers.add?(header_line.name, header_line.value)\n      end\n    end\n  end\n\n  private def self.read_header_line(io, max_size) : HeaderLine | EndOfRequest | Nil\n    # Optimization: check if we have a peek buffer\n    if peek = io.peek\n      # peek.empty? means EOF (so bad request)\n      return nil if peek.empty?\n\n      # See if we can find \\n\n      index = peek.index('\\n'.ord.to_u8)\n      if index\n        end_index = index\n\n        # Also check (and discard) \\r before that\n        if index > 0 && peek[index - 1] == '\\r'.ord.to_u8\n          end_index -= 1\n        end\n\n        # Check if we just have \"\\n\" or \"\\r\\n\" (so end of request)\n        if end_index == 0\n          io.skip(index + 1)\n          return EndOfRequest.new\n        end\n\n        return HeaderLine.new name: \"\", value: \"\", bytesize: index + 1 if index > max_size\n\n        name, value = parse_header(peek[0, end_index])\n        io.skip(index + 1) # Must skip until after \\n\n        return HeaderLine.new name: name, value: value, bytesize: index + 1\n      end\n    end\n\n    line = io.gets(max_size + 1, chomp: true)\n    return nil unless line\n    if line.bytesize > max_size\n      return HeaderLine.new name: \"\", value: \"\", bytesize: max_size\n    end\n\n    if line.empty?\n      return EndOfRequest.new\n    end\n\n    name, value = parse_header(line)\n    HeaderLine.new name: name, value: value, bytesize: line.bytesize\n  end\n\n  private def self.check_content_type_charset(body, headers)\n    return unless body\n\n    content_type = headers[\"Content-Type\"]?\n    return unless content_type\n\n    mime_type = MIME::MediaType.parse?(content_type)\n    return unless mime_type\n\n    charset = mime_type[\"charset\"]?\n    return if !charset || charset == \"utf-8\"\n\n    body.set_encoding(charset, invalid: :skip)\n  end\n\n  # :nodoc:\n  def self.parse_header(line : String) : {String, String}\n    parse_header(line.to_slice)\n  end\n\n  # :nodoc:\n  def self.parse_header(slice : Bytes) : {String, String}\n    # This is basically\n    #\n    # ```\n    # line = \"Server: nginx\"\n    # name, value = line.split ':', 2\n    # {name, value.lstrip} # => {\"Server\", \"nginx\"}\n    # ```\n    #\n    # except that it's faster because we only create 2 strings\n    # instead of 3 (two from the split and one for the lstrip),\n    # and there's no need for the array returned by split.\n\n    cstr = slice.to_unsafe\n    bytesize = slice.size\n\n    # Get the colon index and name\n    colon_index = slice.index(':'.ord.to_u8) || 0\n    name = header_name(slice[0, colon_index])\n\n    # Get where the header value starts (skip space)\n    middle_index = colon_index + 1\n    while middle_index < bytesize && cstr[middle_index].unsafe_chr.ascii_whitespace?\n      middle_index += 1\n    end\n\n    # Get where the header value ends (chomp line)\n    right_index = bytesize\n    if middle_index >= right_index\n      return {name, \"\"}\n    elsif right_index > 1 && cstr[right_index - 2] == '\\r'.ord.to_u8 && cstr[right_index - 1] == '\\n'.ord.to_u8\n      right_index -= 2\n    elsif right_index > 0 && cstr[right_index - 1] == '\\n'.ord.to_u8\n      right_index -= 1\n    end\n\n    value = String.new(slice[middle_index, right_index - middle_index])\n\n    {name, value}\n  end\n\n  # Important! These have to be in lexicographic order.\n  private COMMON_HEADERS = %w(\n    Accept-Encoding\n    Accept-Language\n    Accept-encoding\n    Accept-language\n    Allow\n    Cache-Control\n    Cache-control\n    Connection\n    Content-Disposition\n    Content-Encoding\n    Content-Language\n    Content-Length\n    Content-Type\n    Content-disposition\n    Content-encoding\n    Content-language\n    Content-length\n    Content-type\n    ETag\n    Etag\n    Expires\n    Host\n    Last-Modified\n    Last-modified\n    Location\n    Referer\n    User-Agent\n    User-agent\n    accept-encoding\n    accept-language\n    allow\n    cache-control\n    connection\n    content-disposition\n    content-encoding\n    content-language\n    content-length\n    content-type\n    etag\n    expires\n    host\n    last-modified\n    location\n    referer\n    user-agent\n  )\n\n  # :nodoc:\n  def self.header_name(slice : Bytes) : String\n    # Check if the header name is a common one.\n    # If so we avoid having to allocate a string for it.\n    if slice.size < 20\n      name = COMMON_HEADERS.bsearch { |string| slice <= string.to_slice }\n      return name if name && name.to_slice == slice\n    end\n\n    String.new(slice)\n  end\n\n  # :nodoc:\n  def self.serialize_headers_and_body(io : IO, headers : HTTP::Headers, body : String?, body_io : IO?, version : String) : Nil\n    if body\n      serialize_headers_and_string_body(io, headers, body)\n    elsif body_io\n      content_length = content_length(headers)\n      if content_length\n        headers.serialize(io)\n        io << \"\\r\\n\"\n        copied = IO.copy(body_io, io)\n        if copied != content_length\n          raise ArgumentError.new(\"Content-Length header is #{content_length} but body had #{copied} bytes\")\n        end\n      elsif Client::Response.supports_chunked?(version)\n        headers[\"Transfer-Encoding\"] = \"chunked\"\n        headers.serialize(io)\n        io << \"\\r\\n\"\n        serialize_chunked_body(io, body_io)\n      else\n        body = body_io.gets_to_end\n        serialize_headers_and_string_body(io, headers, body)\n      end\n    else\n      headers.serialize(io)\n      io << \"\\r\\n\"\n    end\n  end\n\n  def self.serialize_headers_and_string_body(io : IO, headers : HTTP::Headers, body : String) : Nil\n    headers[\"Content-Length\"] = body.bytesize.to_s\n    headers.serialize(io)\n    io << \"\\r\\n\"\n    io << body\n  end\n\n  @[Deprecated(\"Use `HTTP::Headers#serialize` instead.\")]\n  def self.serialize_headers(io, headers)\n    headers.serialize(io)\n    io << \"\\r\\n\"\n  end\n\n  def self.serialize_chunked_body(io : IO, body : IO) : Nil\n    buf = uninitialized UInt8[8192]\n    while (buf_length = body.read(buf.to_slice)) > 0\n      buf_length.to_s(io, 16)\n      io << \"\\r\\n\"\n      io.write(buf.to_slice[0, buf_length])\n      io << \"\\r\\n\"\n    end\n    io << \"0\\r\\n\\r\\n\"\n  end\n\n  # :nodoc:\n  def self.content_length(headers : HTTP::Headers) : UInt64?\n    length_headers = headers.get? \"Content-Length\"\n    return nil unless length_headers\n    first_header = length_headers[0]\n    if length_headers.size > 1 && length_headers.any? { |header| header != first_header }\n      raise ArgumentError.new(\"Multiple Content-Length headers received did not match: #{length_headers}\")\n    end\n    first_header.to_u64\n  end\n\n  # :nodoc:\n  def self.keep_alive?(message) : Bool\n    case message.headers[\"Connection\"]?.try &.downcase\n    when \"keep-alive\"\n      true\n    when \"close\", \"upgrade\"\n      false\n    else\n      case message.version\n      when \"HTTP/1.0\"\n        false\n      else\n        true\n      end\n    end\n  end\n\n  def self.expect_continue?(headers : HTTP::Headers) : Bool\n    headers[\"Expect\"]?.try(&.downcase) == \"100-continue\"\n  end\n\n  # Parse a time string using the formats specified by [RFC 2616](https://tools.ietf.org/html/rfc2616#section-3.3.1)\n  #\n  # ```\n  # require \"http\"\n  #\n  # HTTP.parse_time(\"Sun, 14 Feb 2016 21:00:00 GMT\")  # => \"2016-02-14 21:00:00Z\"\n  # HTTP.parse_time(\"Sunday, 14-Feb-16 21:00:00 GMT\") # => \"2016-02-14 21:00:00Z\"\n  # HTTP.parse_time(\"Sun Feb 14 21:00:00 2016\")       # => \"2016-02-14 21:00:00Z\"\n  # ```\n  #\n  # Uses `Time::Format::HTTP_DATE` as parser.\n  def self.parse_time(time_str : String) : Time?\n    Time::Format::HTTP_DATE.parse(time_str)\n  rescue Time::Format::Error\n  end\n\n  # Format a `Time` object as a `String` using the format specified as `sane-cookie-date`\n  # by [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) which is\n  # according to [RFC 2616](https://tools.ietf.org/html/rfc2616#section-3.3.1) a\n  # [RFC 1123](https://tools.ietf.org/html/rfc1123#page-55) format with explicit\n  # timezone `GMT` (interpreted as `UTC`).\n  #\n  # ```\n  # require \"http\"\n  #\n  # HTTP.format_time(Time.utc(2016, 2, 15)) # => \"Mon, 15 Feb 2016 00:00:00 GMT\"\n  # ```\n  #\n  # Uses `Time::Format::HTTP_DATE` as formatter.\n  def self.format_time(time : Time) : String\n    Time::Format::HTTP_DATE.format(time)\n  end\n\n  # Dequotes an [RFC 2616](https://tools.ietf.org/html/rfc2616#page-17)\n  # quoted-string.\n  #\n  # ```\n  # require \"http\"\n  #\n  # quoted = %q(\\\"foo\\\\bar\\\")\n  # HTTP.dequote_string(quoted) # => %q(\"foo\\bar\")\n  # ```\n  def self.dequote_string(str : String) : String\n    data = str.to_slice\n    quoted_pair_index = data.index('\\\\'.ord)\n    return str unless quoted_pair_index\n\n    String.build do |io|\n      while quoted_pair_index\n        io.write(data[0, quoted_pair_index])\n        io << data[quoted_pair_index + 1].unsafe_chr\n\n        data += quoted_pair_index + 2\n        quoted_pair_index = data.index('\\\\'.ord)\n      end\n      io.write(data)\n    end\n  end\n\n  # Encodes a string to an [RFC 2616](https://tools.ietf.org/html/rfc2616#page-17)\n  # quoted-string. Encoded string is written to *io*. May raise when *string*\n  # contains an invalid character.\n  #\n  # ```\n  # require \"http\"\n  #\n  # string = %q(\"foo\\ bar\")\n  # io = IO::Memory.new\n  # HTTP.quote_string(string, io)\n  # io.rewind\n  # io.gets_to_end # => %q(\\\"foo\\\\\\ bar\\\")\n  # ```\n  def self.quote_string(string : String, io : IO) : Nil\n    # Escaping rules: https://evolvis.org/pipermail/evolvis-platfrm-discuss/2014-November/000675.html\n\n    string.each_char do |char|\n      case char\n      when '\\t', ' ', '\"', '\\\\'\n        io << '\\\\'\n      when '\\u{00}'..'\\u{1F}', '\\u{7F}'\n        raise ArgumentError.new(\"String contained invalid character #{char.inspect}\")\n      else\n        # output byte as is\n      end\n      io << char\n    end\n  end\n\n  # Encodes a string to an [RFC 2616](https://tools.ietf.org/html/rfc2616#page-17)\n  # quoted-string. May raise when *string* contains an invalid character.\n  #\n  # ```\n  # require \"http\"\n  #\n  # string = %q(\"foo\\ bar\")\n  # HTTP.quote_string(string) # => %q(\\\"foo\\\\\\ bar\\\")\n  # ```\n  def self.quote_string(string : String) : String\n    String.build do |io|\n      quote_string(string, io)\n    end\n  end\nend\n\nrequire \"./status\"\nrequire \"./request\"\nrequire \"./client/response\"\nrequire \"./headers\"\nrequire \"./content\"\nrequire \"./cookie\"\nrequire \"./cookies\"\nrequire \"./formdata\"\n"
  },
  {
    "path": "src/http/content.cr",
    "content": "require \"./common\"\n\nmodule HTTP\n  # :nodoc:\n  module Content\n    CONTINUE = \"HTTP/1.1 100 Continue\\r\\n\\r\\n\"\n\n    @continue_sent = false\n    setter expects_continue : Bool = false\n\n    def close : Nil\n      @expects_continue = false\n      super\n    end\n\n    protected def ensure_send_continue\n      return unless @expects_continue\n      return if @continue_sent\n      @io << CONTINUE\n      @io.flush\n      @continue_sent = true\n    end\n  end\n\n  # :nodoc:\n  class FixedLengthContent < IO::Sized\n    include Content\n\n    def read(slice : Bytes) : Int32\n      ensure_send_continue\n      super\n    end\n\n    def read_byte : UInt8?\n      ensure_send_continue\n      super\n    end\n\n    def peek : Bytes?\n      ensure_send_continue\n      super\n    end\n\n    def skip(bytes_count) : Nil\n      ensure_send_continue\n      super\n    end\n\n    def write(slice : Bytes) : NoReturn\n      raise IO::Error.new \"Can't write to FixedLengthContent\"\n    end\n  end\n\n  # :nodoc:\n  class UnknownLengthContent < IO\n    include Content\n\n    def initialize(@io : IO)\n    end\n\n    def read(slice : Bytes) : Int32\n      ensure_send_continue\n      @io.read(slice).to_i32\n    end\n\n    def read_byte : UInt8?\n      ensure_send_continue\n      @io.read_byte\n    end\n\n    def peek : Bytes?\n      ensure_send_continue\n      @io.peek\n    end\n\n    def skip(bytes_count : Int) : Nil\n      ensure_send_continue\n      @io.skip(bytes_count)\n    end\n\n    def write(slice : Bytes) : NoReturn\n      raise IO::Error.new \"Can't write to UnknownLengthContent\"\n    end\n  end\n\n  # :nodoc:\n  class ChunkedContent < IO\n    include Content\n\n    # Returns trailing headers read by this chunked content.\n    #\n    # The value will only be populated once the entire content has been read,\n    # i.e. this IO is at EOF.\n    #\n    # All headers in the trailing headers section will be returned. Applications\n    # need to make sure to ignore them or fail if headers are not allowed\n    # in the chunked trailer part (see [RFC 7230 section 4.1.2](https://tools.ietf.org/html/rfc7230#section-4.1.2)).\n    getter headers : HTTP::Headers { HTTP::Headers.new }\n\n    # Returns the maximum permitted combined size for the trailing headers.\n    #\n    # When parsing the trailing headers `ChunkedContent` keeps track of the\n    # amount of total bytes consumed for all headers (including line breaks).\n    # If the combined byte size of all headers is larger than the permitted size,\n    # `IO::Error` is raised.\n    #\n    # Default: `HTTP::MAX_HEADERS_SIZE`\n    getter max_headers_size : Int32\n\n    def initialize(@io : IO, *, @max_headers_size : Int32 = HTTP::MAX_HEADERS_SIZE)\n      @chunk_remaining = -1\n      @received_final_chunk = false\n    end\n\n    def read(slice : Bytes) : Int32\n      ensure_send_continue\n      count = slice.size\n      return 0 if count == 0\n\n      next_chunk\n\n      return 0 if @received_final_chunk\n\n      to_read = Math.min(count, @chunk_remaining)\n\n      bytes_read = @io.read(slice[0, to_read]).to_i32\n\n      if bytes_read == 0\n        raise IO::EOFError.new(\"Invalid HTTP chunked content\")\n      end\n\n      @chunk_remaining -= bytes_read\n\n      bytes_read\n    end\n\n    def read_byte : UInt8?\n      ensure_send_continue\n      next_chunk\n      return super if @received_final_chunk\n\n      byte = @io.read_byte\n      if byte\n        @chunk_remaining -= 1\n        byte\n      else\n        raise IO::EOFError.new(\"Invalid HTTP chunked content\")\n      end\n    end\n\n    def peek : Bytes?\n      ensure_send_continue\n      next_chunk\n      return Bytes.empty if @received_final_chunk\n\n      peek = @io.peek || return\n\n      if @chunk_remaining < peek.size\n        peek = peek[0, @chunk_remaining]\n      elsif peek.size == 0\n        raise IO::EOFError.new(\"Invalid HTTP chunked content\")\n      end\n\n      peek\n    end\n\n    def skip(bytes_count : Int) : Nil\n      ensure_send_continue\n      if bytes_count <= @chunk_remaining\n        @io.skip(bytes_count)\n        @chunk_remaining -= bytes_count\n      else\n        super\n      end\n    end\n\n    # Checks if the last read consumed a chunk and we\n    # need to start consuming the next one.\n    private def next_chunk\n      return if @chunk_remaining > 0 || @received_final_chunk\n\n      # As soon as we finish reading a chunk we return,\n      # in case the following content is delayed (see #3270) and read the chunk\n      # delimiter and next chunk start on the next call to `read`.\n      read_crlf unless @chunk_remaining == -1 # -1 is the initial value\n\n      @chunk_remaining = read_chunk_size\n      if @chunk_remaining == 0\n        read_trailer\n        @received_final_chunk = true\n      end\n    end\n\n    private def read_crlf\n      char = @io.read_byte\n      if char === '\\r'\n        char = @io.read_byte\n      end\n      unless char === '\\n'\n        raise IO::Error.new(\"Invalid HTTP chunked content: expected CRLF\")\n      end\n    end\n\n    private def read_chunk_size\n      line = @io.read_line(@max_headers_size, chomp: true)\n\n      if index = line.byte_index(';'.ord)\n        chunk_size = line.byte_slice(0, index)\n      else\n        chunk_size = line\n      end\n\n      chunk_size.to_i?(16) || raise IO::Error.new(\"Invalid HTTP chunked content: invalid chunk size\")\n    end\n\n    private def read_trailer\n      max_size = @max_headers_size\n\n      while true\n        line = @io.read_line(max_size + 1, chomp: true)\n        break if line.empty?\n        max_size -= line.bytesize\n        if max_size < 0\n          raise IO::Error.new(\"Trailing headers too long\")\n        end\n\n        key, value = HTTP.parse_header(line)\n        break unless headers.add?(key, value)\n      end\n    end\n\n    def write(slice : Bytes) : NoReturn\n      raise IO::Error.new \"Can't write to ChunkedContent\"\n    end\n\n    def closed? : Bool\n      @received_final_chunk || super\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/cookie.cr",
    "content": "module HTTP\n  # Represents a cookie with all its attributes. Provides convenient access and modification of them.\n  #\n  # NOTE: To use `Cookie`, you must explicitly import it with `require \"http/cookie\"`\n  class Cookie\n    # Possible values for the `SameSite` cookie as described in the [Same-site Cookies Draft](https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1.1).\n    enum SameSite\n      # The browser will send cookies with both cross-site requests and same-site requests.\n      #\n      # The `None` directive requires the `secure` attribute to be `true` to mitigate risks associated with cross-site access.\n      None\n      # Prevents the cookie from being sent by the browser in all cross-site browsing contexts.\n      Strict\n      # Allows the cookie to be sent by the browser during top-level navigations that use a [safe](https://tools.ietf.org/html/rfc7231#section-4.2.1) HTTP method.\n      Lax\n    end\n\n    getter name : String\n    getter value : String\n    property path : String?\n    property expires : Time?\n    property domain : String?\n    property http_only : Bool\n    property samesite : SameSite?\n    property extension : String?\n    property max_age : Time::Span?\n    getter creation_time : Time\n\n    @secure : Bool?\n\n    def_equals_and_hash name, value, path, expires, domain, secure, http_only, samesite, extension\n\n    # Creates a new `Cookie` instance.\n    #\n    # Raises `IO::Error` if *name* or *value* are invalid as per [RFC 6265 §4.1.1](https://tools.ietf.org/html/rfc6265#section-4.1.1).\n    # Raises `ArgumentError` if *name* has a security prefix but the requirements are not met as per [RFC 6265 bis §4.1.3](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-07#section-4.1.3).\n    # Alternatively, if *name* has a security prefix, and the related properties are `nil`, the prefix will automatically be applied to the cookie.\n    def initialize(name : String, value : String, @path : String? = nil,\n                   @expires : Time? = nil, @domain : String? = nil,\n                   @secure : Bool? = nil, @http_only : Bool = false,\n                   @samesite : SameSite? = nil, @extension : String? = nil,\n                   @max_age : Time::Span? = nil, @creation_time = Time.utc)\n      validate_name(name)\n      @name = name\n      validate_value(value)\n      @value = value\n      raise IO::Error.new(\"Invalid max_age\") if @max_age.try { |max_age| max_age < Time::Span.zero }\n\n      self.check_prefix\n      self.validate!\n    end\n\n    # Returns `true` if this cookie has the *Secure* flag.\n    def secure : Bool\n      !!@secure\n    end\n\n    def secure=(@secure : Bool) : Bool\n    end\n\n    # Sets the name of this cookie.\n    #\n    # Raises `IO::Error` if the value is invalid as per [RFC 6265 §4.1.1](https://tools.ietf.org/html/rfc6265#section-4.1.1).\n    def name=(name : String) : Nil\n      validate_name(name)\n      @name = name\n\n      self.check_prefix\n    end\n\n    private def validate_name(name)\n      raise IO::Error.new(\"Invalid cookie name\") if name.empty?\n      name.each_byte do |byte|\n        # valid characters for cookie-name per https://tools.ietf.org/html/rfc6265#section-4.1.1\n        # and https://tools.ietf.org/html/rfc2616#section-2.2\n        # \"!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~\"\n        if !byte.in?(0x21...0x7f) ||                 # Non-printable ASCII character\n           byte.in?(0x22, 0x28, 0x29, 0x2c, 0x2f) || # '\"', '(', ')', ',', '/'\n           byte.in?(0x3a..0x40) ||                   # ':', ';', '<', '=', '>', '?', '@'\n           byte.in?(0x5b..0x5d) ||                   # '[', '\\\\', ']'\n           byte.in?(0x7b, 0x7d)                      # '{', '}'\n          raise IO::Error.new(\"Invalid cookie name\")\n        end\n      end\n    end\n\n    # Sets the value of this cookie.\n    #\n    # Raises `IO::Error` if the value is invalid as per [RFC 6265 §4.1.1](https://tools.ietf.org/html/rfc6265#section-4.1.1).\n    def value=(value : String) : String\n      validate_value(value)\n      @value = value\n    end\n\n    private def validate_value(value)\n      value.each_byte do |byte|\n        # valid characters for cookie-value per https://tools.ietf.org/html/rfc6265#section-4.1.1\n        # all printable ASCII characters except ',', '\"', ';' and '\\\\'\n        if !byte.in?(0x20...0x7f) || byte.in?(0x22, 0x2c, 0x3b, 0x5c)\n          raise IO::Error.new(\"Invalid cookie value\")\n        end\n      end\n    end\n\n    # Returns an unambiguous string representation of this cookie.\n    #\n    # It uses the `Set-Cookie` serialization from `#to_set_cookie_header` which\n    # represents the full state of the cookie.\n    #\n    # ```\n    # HTTP::Cookie.new(\"foo\", \"bar\").inspect                        # => HTTP::Cookie[\"foo=bar\"]\n    # HTTP::Cookie.new(\"foo\", \"bar\", domain: \"example.com\").inspect # => HTTP::Cookie[\"foo=bar; domain=example.com\"]\n    # ```\n    def inspect(io : IO) : Nil\n      io << \"HTTP::Cookie[\"\n      to_s.inspect(io)\n      io << \"]\"\n    end\n\n    # Returns a string representation of this cookie.\n    #\n    # It uses the `Set-Cookie` serialization from `#to_set_cookie_header` which\n    # represents the full state of the cookie.\n    #\n    # ```\n    # HTTP::Cookie.new(\"foo\", \"bar\").to_s                        # => \"foo=bar\"\n    # HTTP::Cookie.new(\"foo\", \"bar\", domain: \"example.com\").to_s # => \"foo=bar; domain=example.com\"\n    # ```\n    def to_s(io : IO) : Nil\n      to_set_cookie_header(io)\n    end\n\n    # Returns a string representation of this cookie in the format used by the\n    # `Set-Cookie` header of an HTTP response.\n    #\n    # ```\n    # HTTP::Cookie.new(\"foo\", \"bar\").to_set_cookie_header                        # => \"foo=bar\"\n    # HTTP::Cookie.new(\"foo\", \"bar\", domain: \"example.com\").to_set_cookie_header # => \"foo=bar; domain=example.com\"\n    # ```\n    def to_set_cookie_header : String\n      String.build do |header|\n        to_set_cookie_header(header)\n      end\n    end\n\n    # :ditto:\n    def to_set_cookie_header(io : IO) : Nil\n      path = @path\n      expires = @expires\n      max_age = @max_age\n      domain = @domain\n      samesite = @samesite\n\n      to_cookie_header(io)\n      io << \"; domain=#{domain}\" if domain\n      io << \"; path=#{path}\" if path\n      io << \"; expires=#{HTTP.format_time(expires)}\" if expires\n      io << \"; max-age=#{max_age.to_i}\" if max_age\n      io << \"; Secure\" if @secure\n      io << \"; HttpOnly\" if @http_only\n      io << \"; SameSite=#{samesite}\" if samesite\n      io << \"; #{@extension}\" if @extension\n    end\n\n    # Returns a string representation of this cookie in the format used by the\n    # `Cookie` header of an HTTP request.\n    # This includes only the `#name` and `#value`. All other attributes are left\n    # out.\n    #\n    # ```\n    # HTTP::Cookie.new(\"foo\", \"bar\").to_cookie_header                        # => \"foo=bar\"\n    # HTTP::Cookie.new(\"foo\", \"bar\", domain: \"example.com\").to_cookie_header # => \"foo=bar\n    # ```\n    def to_cookie_header : String\n      String.build(@name.bytesize + @value.bytesize + 1) do |io|\n        to_cookie_header(io)\n      end\n    end\n\n    # :ditto:\n    def to_cookie_header(io : IO) : Nil\n      io << @name\n      io << '='\n      io << @value\n    end\n\n    # Returns the expiration time of this cookie.\n    def expiration_time : Time?\n      if max_age = @max_age\n        @creation_time + max_age\n      else\n        @expires\n      end\n    end\n\n    # Returns the expiration status of this cookie as a `Bool`.\n    #\n    # *time_reference* can be passed to use a different reference time for\n    # comparison. Default is the current time (`Time.utc`).\n    def expired?(time_reference : Time = Time.utc) : Bool\n      if @max_age.try &.zero?\n        true\n      elsif expiration_time = self.expiration_time\n        expiration_time <= time_reference\n      else\n        false\n      end\n    end\n\n    # Returns `false` if `#name` has a security prefix but the requirements are not met as per\n    # [RFC 6265 bis §4.1.3](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-07#section-4.1.3),\n    # otherwise returns `true`.\n    def valid? : Bool\n      self.valid_secure_prefix? && self.valid_host_prefix?\n    end\n\n    # Raises `ArgumentError` if `self` is not `#valid?`.\n    def validate! : self\n      raise ArgumentError.new \"Invalid cookie name. Has '__Secure-' prefix, but is not secure.\" unless self.valid_secure_prefix?\n      raise ArgumentError.new \"Invalid cookie name. Does not meet '__Host-' prefix requirements of: secure: true, path: \\\"/\\\", domain: nil.\" unless self.valid_host_prefix?\n\n      self\n    end\n\n    private def valid_secure_prefix? : Bool\n      self.secure || !@name.starts_with?(\"__Secure-\")\n    end\n\n    private def valid_host_prefix? : Bool\n      !@name.starts_with?(\"__Host-\") || (self.secure && \"/\" == @path && @domain.nil?)\n    end\n\n    private def check_prefix : Nil\n      if @name.starts_with?(\"__Host-\")\n        @path = \"/\" if @path.nil?\n        @secure = true if @secure.nil?\n      end\n\n      if @name.starts_with?(\"__Secure-\")\n        @secure = true if @secure.nil?\n      end\n    end\n\n    # Expires the cookie.\n    #\n    # Causes the cookie to be destroyed. Sets the value to the empty string and\n    # expires its lifetime.\n    #\n    # ```\n    # cookie = HTTP::Cookie.new(\"hello\", \"world\")\n    # cookie.expire\n    #\n    # cookie.value    # => \"\"\n    # cookie.expired? # => true\n    # ```\n    def expire : Nil\n      self.value = \"\"\n      self.expires = Time::UNIX_EPOCH\n      self.max_age = Time::Span.zero\n    end\n\n    # :nodoc:\n    module Parser\n      module Regex\n        CookieName     = /[^()<>@,;:\\\\\"\\/\\[\\]?={} \\t\\x00-\\x1f\\x7f]+/\n        CookieOctet    = /[!#-+\\--:<-\\[\\]-~ ]/\n        CookieValue    = /(?:\"#{CookieOctet}*\"|#{CookieOctet}*)/\n        CookiePair     = /\\s*(?<name>#{CookieName})\\s*=\\s*(?<value>#{CookieValue})\\s*/\n        DomainLabel    = /[A-Za-z0-9\\-]+/\n        DomainIp       = /(?:\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})/\n        Time           = /(?:\\d{2}:\\d{2}:\\d{2})/\n        Month          = /(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/\n        Weekday        = /(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/\n        Wkday          = /(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)/\n        PathValue      = /[^\\x00-\\x1f\\x7f;]+/\n        DomainValue    = /(?:#{DomainLabel}(?:\\.#{DomainLabel})?|#{DomainIp})+/\n        Zone           = /(?:UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[+-]?\\d{4})/\n        RFC1036Date    = /#{Weekday}, \\d{2}-#{Month}-\\d{2} #{Time} GMT/\n        RFC1123Date    = /#{Wkday}, \\d{1,2} #{Month} \\d{2,4} #{Time} #{Zone}/\n        IISDate        = /#{Wkday}, \\d{1,2}-#{Month}-\\d{2,4} #{Time} GMT/\n        ANSICDate      = /#{Wkday} #{Month} (?:\\d{2}| \\d) #{Time} \\d{4}/\n        SaneCookieDate = /(?:#{RFC1123Date}|#{RFC1036Date}|#{IISDate}|#{ANSICDate})/\n        ExtensionAV    = /(?<extension>[^\\x00-\\x1f\\x7f]+)/\n        HttpOnlyAV     = /(?<http_only>HttpOnly)/i\n        SameSiteAV     = /SameSite=(?<samesite>\\w+)/i\n        SecureAV       = /(?<secure>Secure)/i\n        PathAV         = /Path=(?<path>#{PathValue})/i\n        DomainAV       = /Domain=\\.?(?<domain>#{DomainValue})/i\n        MaxAgeAV       = /Max-Age=(?<max_age>[0-9]*)/i\n        ExpiresAV      = /Expires=(?<expires>#{SaneCookieDate})/i\n        CookieAV       = /(?:#{ExpiresAV}|#{MaxAgeAV}|#{DomainAV}|#{PathAV}|#{SecureAV}|#{HttpOnlyAV}|#{SameSiteAV}|#{ExtensionAV})/\n      end\n\n      CookieString    = /(?:^|; )#{Regex::CookiePair}/\n      SetCookieString = /^#{Regex::CookiePair}(?:;\\s*#{Regex::CookieAV})*;?$/\n\n      def parse_cookies(header, &)\n        header.scan(CookieString).each do |pair|\n          value = pair[\"value\"]\n          if value.starts_with?('\"') && value.ends_with?('\"')\n            # Unwrap quoted cookie value\n            value = value.byte_slice(1, value.bytesize - 2)\n          else\n            value = value.strip\n          end\n          yield Cookie.new(pair[\"name\"], value)\n        end\n      end\n\n      def parse_cookies(header : String) : Array(Cookie)\n        cookies = [] of Cookie\n        parse_cookies(header) { |cookie| cookies << cookie }\n        cookies\n      end\n\n      def parse_set_cookie(header : String) : Cookie?\n        match = header.match(SetCookieString)\n        return unless match\n\n        expires = parse_time(match[\"expires\"]?)\n        max_age = match[\"max_age\"]?.try(&.to_i64.seconds)\n\n        # Unwrap quoted cookie value\n        cookie_value = match[\"value\"]\n        if cookie_value.starts_with?('\"') && cookie_value.ends_with?('\"')\n          cookie_value = cookie_value.byte_slice(1, cookie_value.bytesize - 2)\n        else\n          cookie_value = cookie_value.strip\n        end\n\n        Cookie.new(\n          match[\"name\"], cookie_value,\n          path: match[\"path\"]?,\n          expires: expires,\n          domain: match[\"domain\"]?,\n          secure: match[\"secure\"]? != nil,\n          http_only: match[\"http_only\"]? != nil,\n          samesite: match[\"samesite\"]?.try { |v| SameSite.parse? v },\n          extension: match[\"extension\"]?,\n          max_age: max_age,\n        )\n      end\n\n      private def parse_time(string)\n        return unless string\n        HTTP.parse_time(string)\n      end\n\n      extend self\n    end\n  end\nend\n\nrequire \"./common\"\n"
  },
  {
    "path": "src/http/cookies.cr",
    "content": "require \"./cookie\"\n\nmodule HTTP\n  # Represents a collection of cookies as it can be present inside\n  # a HTTP request or response.\n  #\n  # NOTE: To use `Cookies`, you must explicitly import it with `require \"http/cookie\"`\n  class Cookies\n    include Enumerable(Cookie)\n\n    # Creates a new instance by parsing the `Cookie` and `Set-Cookie`\n    # headers in the given `HTTP::Headers`.\n    #\n    # See `HTTP::Request#cookies` and `HTTP::Client::Response#cookies`.\n    @[Deprecated(\"Use `.from_client_headers` or `.from_server_headers` instead.\")]\n    def self.from_headers(headers) : self\n      new.tap(&.fill_from_headers(headers))\n    end\n\n    # Filling cookies by parsing the `Cookie` and `Set-Cookie`\n    # headers in the given `HTTP::Headers`.\n    @[Deprecated(\"Use `#fill_from_client_headers` or `#fill_from_server_headers` instead.\")]\n    def fill_from_headers(headers)\n      fill_from_client_headers(headers)\n      fill_from_server_headers(headers)\n      self\n    end\n\n    # Creates a new instance by parsing the `Cookie` headers in the given `HTTP::Headers`.\n    #\n    # See `HTTP::Client::Response#cookies`.\n    def self.from_client_headers(headers : HTTP::Headers) : self\n      new.tap(&.fill_from_client_headers(headers))\n    end\n\n    # Filling cookies by parsing the `Cookie` headers in the given `HTTP::Headers`.\n    def fill_from_client_headers(headers : HTTP::Headers) : self\n      if values = headers.get?(\"Cookie\")\n        values.each do |header|\n          Cookie::Parser.parse_cookies(header) { |cookie| self << cookie }\n        end\n      end\n      self\n    end\n\n    # Creates a new instance by parsing the `Set-Cookie` headers in the given `HTTP::Headers`.\n    #\n    # See `HTTP::Request#cookies`.\n    def self.from_server_headers(headers : HTTP::Headers) : self\n      new.tap(&.fill_from_server_headers(headers))\n    end\n\n    # Filling cookies by parsing the `Set-Cookie` headers in the given `HTTP::Headers`.\n    def fill_from_server_headers(headers : HTTP::Headers) : self\n      if values = headers.get?(\"Set-Cookie\")\n        values.each do |header|\n          Cookie::Parser.parse_set_cookie(header).try { |cookie| self << cookie }\n        end\n      end\n      self\n    end\n\n    # Creates a new empty instance.\n    def initialize\n      @cookies = {} of String => Cookie\n    end\n\n    def_equals_and_hash @cookies\n\n    # Sets a new cookie in the collection with a string value.\n    # This creates a never expiring, insecure, not HTTP-only cookie with\n    # no explicit domain restriction and no path.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # request = HTTP::Request.new \"GET\", \"/\"\n    # request.cookies[\"foo\"] = \"bar\"\n    # ```\n    def []=(key : String, value : String) : Cookie\n      self << Cookie.new(key, value)\n    end\n\n    # Sets a new cookie in the collection to the given `HTTP::Cookie`\n    # instance. The name attribute must match the given *key*, else\n    # `ArgumentError` is raised.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # response = HTTP::Client::Response.new(200)\n    # response.cookies[\"foo\"] = HTTP::Cookie.new(\"foo\", \"bar\", \"/admin\", Time.utc + 12.hours, secure: true)\n    # ```\n    def []=(key : String, value : Cookie) : Cookie\n      unless key == value.name\n        raise ArgumentError.new(\"Cookie name must match the given key\")\n      end\n\n      self << value\n    end\n\n    # Gets the current `HTTP::Cookie` for the given *key*.\n    #\n    # ```\n    # request.cookies[\"foo\"].value # => \"bar\"\n    # ```\n    def [](key : String) : Cookie\n      @cookies[key]\n    end\n\n    # Gets the current `HTTP::Cookie` for the given *key* or `nil` if none is set.\n    #\n    # ```\n    # require \"http/client\"\n    #\n    # request = HTTP::Request.new \"GET\", \"/\"\n    # request.cookies[\"foo\"]? # => nil\n    # request.cookies[\"foo\"] = \"bar\"\n    # request.cookies[\"foo\"]?.try &.value # > \"bar\"\n    # ```\n    def []?(key : String) : Cookie?\n      @cookies[key]?\n    end\n\n    # Returns `true` if a cookie with the given *key* exists.\n    #\n    # ```\n    # request.cookies.has_key?(\"foo\") # => true\n    # ```\n    def has_key?(key : String) : Bool\n      @cookies.has_key?(key)\n    end\n\n    # Adds the given *cookie* to this collection, overrides an existing cookie\n    # with the same name if present.\n    #\n    # ```\n    # response.cookies << HTTP::Cookie.new(\"foo\", \"bar\", http_only: true)\n    # ```\n    def <<(cookie : Cookie) : Cookie\n      @cookies[cookie.name] = cookie\n    end\n\n    # Clears the collection, removing all cookies.\n    def clear : Hash(String, Cookie)\n      @cookies.clear\n    end\n\n    # Deletes and returns the `HTTP::Cookie` for the specified *key*, or\n    # returns `nil` if *key* cannot be found in the collection. Note that\n    # *key* should match the name attribute of the desired `HTTP::Cookie`.\n    def delete(key : String) : Cookie?\n      @cookies.delete(key)\n    end\n\n    # Yields each `HTTP::Cookie` in the collection.\n    def each(& : Cookie ->)\n      @cookies.each_value do |cookie|\n        yield cookie\n      end\n    end\n\n    # Returns an iterator over the cookies of this collection.\n    def each : Iterator(Cookie)\n      @cookies.each_value\n    end\n\n    # Returns the number of cookies contained in this collection.\n    def size : Int32\n      @cookies.size\n    end\n\n    # Whether the collection contains any cookies.\n    def empty? : Bool\n      @cookies.empty?\n    end\n\n    # Adds `Cookie` headers for the cookies in this collection to the\n    # given `HTTP::Headers` instance and returns it. Removes any existing\n    # `Cookie` headers in it.\n    def add_request_headers(headers : HTTP::Headers) : HTTP::Headers\n      if empty?\n        headers.delete(\"Cookie\")\n      else\n        capacity = sum { |cookie| cookie.name.bytesize + cookie.value.bytesize + 1 }\n        capacity += (size - 1) * 2\n        headers[\"Cookie\"] = String.build(capacity) do |io|\n          join(io, \"; \", &.to_cookie_header(io))\n        end\n      end\n\n      headers\n    end\n\n    # Adds `Set-Cookie` headers for the cookies in this collection to the\n    # given `HTTP::Headers` instance and returns it. Removes any existing\n    # `Set-Cookie` headers in it.\n    def add_response_headers(headers : HTTP::Headers) : HTTP::Headers\n      headers.delete(\"Set-Cookie\")\n      each do |cookie|\n        headers.add(\"Set-Cookie\", cookie.to_set_cookie_header)\n      end\n\n      headers\n    end\n\n    # Returns this collection as a plain `Hash`.\n    def to_h : Hash(String, Cookie)\n      @cookies.dup\n    end\n\n    # Returns a string representation of this cookies list.\n    #\n    # It uses the `Set-Cookie` serialization from `Cookie#to_set_cookie_header` which\n    # represents the full state of the cookie.\n    #\n    # ```\n    # HTTP::Cookies{\n    #   HTTP::Cookie.new(\"foo\", \"bar\"),\n    #   HTTP::Cookie.new(\"foo\", \"bar\", domain: \"example.com\"),\n    # }.to_s # => \"HTTP::Cookies{\\\"foo=bar\\\", \\\"foo=bar; domain=example.com\\\"}\"\n    # ```\n    def to_s(io : IO)\n      io << \"HTTP::Cookies{\"\n      join(io, \", \") { |cookie| cookie.to_set_cookie_header.inspect(io) }\n      io << \"}\"\n    end\n\n    # :ditto:\n    def inspect(io : IO)\n      to_s(io)\n    end\n\n    # :ditto:\n    def pretty_print(pp) : Nil\n      pp.list(\"HTTP::Cookies{\", self, \"}\") { |elem| pp.text(elem.to_set_cookie_header.inspect) }\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/formdata/builder.cr",
    "content": "module HTTP::FormData\n  # Builds a multipart/form-data message.\n  #\n  # ### Example\n  #\n  # ```\n  # require \"http\"\n  #\n  # io = IO::Memory.new\n  # builder = HTTP::FormData::Builder.new(io, \"aA47\")\n  # builder.field(\"name\", \"joe\")\n  # file = IO::Memory.new(\"file contents\")\n  # builder.file(\"upload\", file, HTTP::FormData::FileMetadata.new(filename: \"test.txt\"))\n  # builder.finish\n  # io.to_s # => \"--aA47\\r\\nContent-Disposition: form-data; name=\\\"name\\\"\\r\\n\\r\\njoe\\r\\n--aA47\\r\\nContent-Disposition: form-data; name=\\\"upload\\\"; filename=\\\"test.txt\\\"\\r\\n\\r\\nfile contents\\r\\n--aA47--\"\n  # ```\n  class Builder\n    private enum State\n      START\n      FIELD\n      FINISHED\n    end\n\n    # Creates a new `FormData::Builder` which writes to *io*, using the\n    # multipart boundary *boundary*.\n    def initialize(@io : IO, @boundary = MIME::Multipart.generate_boundary)\n      @state = State::START\n    end\n\n    getter boundary\n\n    # Returns a content type header with correct boundary parameter.\n    #\n    # ```\n    # builder = HTTP::FormData::Builder.new(io, \"a4VF\")\n    # builder.content_type # => \"multipart/form-data; boundary=\\\"a4VF\\\"\"\n    # ```\n    def content_type : String\n      String.build do |str|\n        str << \"multipart/form-data; boundary=\\\"\"\n        HTTP.quote_string(@boundary, str)\n        str << '\"'\n      end\n    end\n\n    # Adds a form part with the given *name* and *value*. *Headers* can\n    # optionally be provided for the form part.\n    def field(name : String, value : _, headers : HTTP::Headers = HTTP::Headers.new) : Nil\n      file(name, IO::Memory.new(value.to_s), headers: headers)\n    end\n\n    # Adds a form part called *name*, with data from *io* as the value.\n    # *Metadata* can be provided to add extra metadata about the file to the\n    # Content-Disposition header for the form part. Other headers can be added\n    # using *headers*.\n    def file(name : String, io : IO, metadata : FileMetadata = FileMetadata.new, headers : HTTP::Headers = HTTP::Headers.new) : Nil\n      fail \"Cannot add form part: already finished\" if @state.finished?\n\n      headers[\"Content-Disposition\"] = generate_content_disposition(name, metadata)\n\n      # We don't add a crlf before the first boundary if this is the first body part.\n      @io << \"\\r\\n\" unless @state.start?\n      @io << \"--\" << @boundary\n      headers.each do |name, values|\n        values.each do |value|\n          @io << \"\\r\\n\" << name << \": \" << value\n        end\n      end\n      @io << \"\\r\\n\\r\\n\"\n      IO.copy(io, @io)\n\n      @state = State::FIELD\n    end\n\n    # Finalizes the multipart message, this method must be called before the\n    # generated multipart message written to the IO is considered valid.\n    def finish : Nil\n      case @state\n      in .start?\n        fail \"Cannot finish form-data: no body parts\"\n      in .finished?\n        fail \"Cannot finish form-data: already finished\"\n      in .field?\n        @io << \"\\r\\n--\" << @boundary << \"--\"\n        @state = State::FINISHED\n      end\n    end\n\n    private def generate_content_disposition(name, metadata)\n      String.build do |io|\n        io << \"form-data; name=\\\"\"\n        HTTP.quote_string(name, io)\n        io << '\"'\n\n        if filename = metadata.filename\n          io << \"; filename=\\\"\"\n          HTTP.quote_string(filename, io)\n          io << '\"'\n        end\n\n        if creation_time = metadata.creation_time\n          io << %(; creation-date=\")\n          creation_time.to_s(io, \"%a, %d %b %Y %H:%M:%S %z\")\n          io << '\"'\n        end\n\n        if modification_time = metadata.modification_time\n          io << %(; modification-date=\")\n          modification_time.to_s(io, \"%a, %d %b %Y %H:%M:%S %z\")\n          io << '\"'\n        end\n\n        if read_time = metadata.read_time\n          io << %(; read-date=\")\n          read_time.to_s(io, \"%a, %d %b %Y %H:%M:%S %z\")\n          io << '\"'\n        end\n\n        if size = metadata.size\n          io << %(; size=) << size\n        end\n      end\n    end\n\n    private def fail(msg)\n      raise FormData::Error.new(msg)\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/formdata/parser.cr",
    "content": "module HTTP::FormData\n  class Parser\n    # Creates a new parser which parses *io* with multipart boundary *boundary*.\n    def initialize(io, boundary)\n      @multipart = MIME::Multipart::Parser.new(io, boundary)\n    end\n\n    # Parses the next form-data part and yields field name, io, `FileMetadata`,\n    # and raw headers.\n    #\n    # This method yields once instead of returning the values, because the IO\n    # object yielded to the block is only valid while the block is executing.\n    # The IO object will be closed as soon as the block returns. To store the\n    # content of the body part for longer than the block, the IO must be read\n    # into memory.\n    #\n    # ```\n    # require \"http\"\n    #\n    # form_data = \"--aA40\\r\\nContent-Disposition: form-data; name=\\\"field1\\\"; filename=\\\"foo.txt\\\"; size=13\\r\\nContent-Type: text/plain\\r\\n\\r\\nfield data\\r\\n--aA40--\"\n    # parser = HTTP::FormData::Parser.new(IO::Memory.new(form_data), \"aA40\")\n    # parser.next do |part|\n    #   part.name                    # => \"field1\"\n    #   part.body.gets_to_end        # => \"field data\"\n    #   part.filename                # => \"foo.txt\"\n    #   part.size                    # => 13\n    #   part.headers[\"Content-Type\"] # => \"text/plain\"\n    # end\n    # ```\n    def next(&)\n      raise FormData::Error.new(\"Parser has already finished parsing\") unless has_next?\n\n      while @multipart.has_next?\n        @multipart.next do |headers, io|\n          yield HTTP::FormData::Part.new(headers, io)\n        end\n      end\n    end\n\n    # True if `#next` can be called legally.\n    def has_next? : Bool\n      @multipart.has_next?\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/formdata/part.cr",
    "content": "module HTTP::FormData\n  struct Part\n    getter name : String\n    getter body : IO\n    getter headers : HTTP::Headers\n\n    getter filename : String?\n    getter creation_time : Time?\n    getter modification_time : Time?\n    getter read_time : Time?\n    getter size : UInt64?\n\n    def initialize(@headers : HTTP::Headers, @body : IO)\n      content_disposition = headers.get?(\"Content-Disposition\").try(&.[0])\n      raise Error.new(\"Failed to parse form-data: Content-Disposition not found\") unless content_disposition\n\n      @name, content_disposition = FormData.parse_content_disposition(content_disposition)\n      @filename = content_disposition.filename\n      @creation_time = content_disposition.creation_time\n      @modification_time = content_disposition.modification_time\n      @read_time = content_disposition.read_time\n      @size = content_disposition.size\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/formdata.cr",
    "content": "require \"./formdata/**\"\nrequire \"mime/multipart\"\n\n# Contains utilities for parsing `multipart/form-data` messages, which are\n# commonly used for encoding HTML form data.\n#\n# NOTE: To use `FormData`, you must explicitly import it with `require \"http\"`\n#\n# ### Examples\n#\n# Commonly, you'll want to parse a from response from a HTTP request, and\n# process it. An example server which performs this task is shown below.\n#\n# ```\n# require \"http\"\n#\n# server = HTTP::Server.new do |context|\n#   name = nil\n#   file = nil\n#   HTTP::FormData.parse(context.request) do |part|\n#     case part.name\n#     when \"name\"\n#       name = part.body.gets_to_end\n#     when \"file\"\n#       file = File.tempfile(\"upload\") do |file|\n#         IO.copy(part.body, file)\n#       end\n#     end\n#   end\n#\n#   unless name && file\n#     context.response.respond_with_status(:bad_request)\n#     next\n#   end\n#\n#   context.response << file.path\n# end\n#\n# server.bind_tcp 8085\n# server.listen\n# ```\n#\n# To test the server, use the curl command below.\n#\n# ```console\n# $ curl http://localhost:8085/ -F name=foo -F file=@/path/to/test.file\n# /tmp/upload.Yxn7cc\n# ```\n#\n# Another common case is sending formdata to a server using HTTP::Client. Here\n# is an example showing how to upload a file to the server above in crystal.\n#\n# ```\n# require \"http\"\n#\n# IO.pipe do |reader, writer|\n#   channel = Channel(String).new(1)\n#\n#   spawn do\n#     HTTP::FormData.build(writer) do |formdata|\n#       channel.send(formdata.content_type)\n#\n#       formdata.field(\"name\", \"foo\")\n#       File.open(\"foo.png\") do |file|\n#         metadata = HTTP::FormData::FileMetadata.new(filename: \"foo.png\")\n#         headers = HTTP::Headers{\"Content-Type\" => \"image/png\"}\n#         formdata.file(\"file\", file, metadata, headers)\n#       end\n#     end\n#\n#     writer.close\n#   end\n#\n#   headers = HTTP::Headers{\"Content-Type\" => channel.receive}\n#   response = HTTP::Client.post(\"http://localhost:8085/\", body: reader, headers: headers)\n#\n#   puts \"Response code #{response.status_code}\"\n#   puts \"File path: #{response.body}\"\n# end\n# ```\nmodule HTTP::FormData\n  # Parses a multipart/form-data message, yielding a `FormData::Part`.\n  #\n  # ```\n  # require \"http\"\n  #\n  # form_data = \"--aA40\\r\\nContent-Disposition: form-data; name=\\\"field1\\\"\\r\\n\\r\\nfield data\\r\\n--aA40--\"\n  # HTTP::FormData.parse(IO::Memory.new(form_data), \"aA40\") do |part|\n  #   part.name             # => \"field1\"\n  #   part.body.gets_to_end # => \"field data\"\n  # end\n  # ```\n  #\n  # See: `FormData::Parser`\n  def self.parse(io, boundary, &)\n    parser = Parser.new(io, boundary)\n    while parser.has_next?\n      parser.next { |part| yield part }\n    end\n  end\n\n  # Parses a multipart/form-data message, yielding a `FormData::Part`.\n  #\n  # ```\n  # require \"http\"\n  #\n  # headers = HTTP::Headers{\"Content-Type\" => \"multipart/form-data; boundary=aA40\"}\n  # body = \"--aA40\\r\\nContent-Disposition: form-data; name=\\\"field1\\\"\\r\\n\\r\\nfield data\\r\\n--aA40--\"\n  # request = HTTP::Request.new(\"POST\", \"/\", headers, body)\n  #\n  # HTTP::FormData.parse(request) do |part|\n  #   part.name             # => \"field1\"\n  #   part.body.gets_to_end # => \"field data\"\n  # end\n  # ```\n  #\n  # See: `FormData::Parser`\n  def self.parse(request : HTTP::Request, &)\n    body = request.body\n    raise Error.new \"Cannot extract form-data from HTTP request: body is empty\" unless body\n\n    boundary = request.headers[\"Content-Type\"]?.try { |header| MIME::Multipart.parse_boundary(header) }\n    raise Error.new \"Cannot extract form-data from HTTP request: could not find boundary in Content-Type\" unless boundary\n\n    parse(body, boundary) { |part| yield part }\n  end\n\n  # Parses a `Content-Disposition` header string into a field name and\n  # `FileMetadata`. Please note that the `Content-Disposition` header for\n  # `multipart/form-data` is not compatible with the original definition in\n  # [RFC 2183](https://tools.ietf.org/html/rfc2183), but are instead specified\n  # in [RFC 2388](https://tools.ietf.org/html/rfc2388).\n  def self.parse_content_disposition(content_disposition : String) : {String, FileMetadata}\n    filename = nil\n    creation_time = nil\n    modification_time = nil\n    read_time = nil\n    size = nil\n    name = nil\n\n    parts = content_disposition.split(';')\n    type = parts.shift?\n    raise Error.new(\"Invalid Content-Disposition: not form-data\") unless type == \"form-data\"\n    parts.each do |part|\n      key, _, value = part.partition('=')\n      key = key.strip\n      value = value.strip\n      if value[0] == '\"'\n        value = HTTP.dequote_string(value[1...-1])\n      end\n\n      case key\n      when \"filename\"\n        filename = value\n      when \"creation-date\"\n        creation_time = HTTP.parse_time value\n      when \"modification-date\"\n        modification_time = HTTP.parse_time value\n      when \"read-date\"\n        read_time = HTTP.parse_time value\n      when \"size\"\n        size = value.to_u64\n      when \"name\"\n        name = value\n      else\n        # not a field we are interested in\n      end\n    end\n\n    raise Error.new(\"Invalid Content-Disposition: no name field\") unless name\n    {name, FileMetadata.new(filename, creation_time, modification_time, read_time, size)}\n  end\n\n  # Builds a multipart/form-data message, yielding a `FormData::Builder`\n  # object to the block which writes to *io* using *boundary*.\n  # `Builder#finish` is called on the builder when the block returns.\n  #\n  # ```\n  # require \"http\"\n  #\n  # io = IO::Memory.new\n  # HTTP::FormData.build(io, \"boundary\") do |builder|\n  #   builder.field(\"foo\", \"bar\")\n  # end\n  # io.to_s # => \"--boundary\\r\\nContent-Disposition: form-data; name=\\\"foo\\\"\\r\\n\\r\\nbar\\r\\n--boundary--\"\n  # ```\n  #\n  # See: `FormData::Builder`\n  def self.build(io, boundary = MIME::Multipart.generate_boundary, &)\n    builder = Builder.new(io, boundary)\n    yield builder\n    builder.finish\n  end\n\n  # Builds a multipart/form-data message, yielding a `FormData::Builder`\n  # object to the block which writes to *response* using *boundary.\n  # Content-Type is set on *response* and `Builder#finish` is called on the\n  # builder when the block returns.\n  #\n  # ```\n  # require \"http\"\n  #\n  # io = IO::Memory.new\n  # response = HTTP::Server::Response.new io\n  # HTTP::FormData.build(response, \"boundary\") do |builder|\n  #   builder.field(\"foo\", \"bar\")\n  # end\n  # response.close\n  #\n  # response.headers[\"Content-Type\"] # => \"multipart/form-data; boundary=\\\"boundary\\\"\"\n  # io.to_s                          # => \"HTTP/1.1 200 OK\\r\\nContent-Type: multipart/form-data; boundary=\\\"boundary\\\"\\r\\nContent-Length: 75\\r\\n\\r\\n--boundary\\r\\nContent-Disposition: form-data; name=\\\"foo\\\"\\r\\n\\r\\nbar\\r\\n--boundary--\"\n  # ```\n  #\n  # See: `FormData::Builder`\n  def self.build(response : HTTP::Server::Response, boundary = MIME::Multipart.generate_boundary, &)\n    builder = Builder.new(response, boundary)\n    yield builder\n    builder.finish\n    response.headers[\"Content-Type\"] = builder.content_type\n  end\n\n  # Metadata which may be available for uploaded files.\n  record FileMetadata,\n    filename : String? = nil,\n    creation_time : Time? = nil,\n    modification_time : Time? = nil,\n    read_time : Time? = nil,\n    size : UInt64? = nil\n\n  class Error < Exception\n  end\nend\n"
  },
  {
    "path": "src/http/headers.cr",
    "content": "# A `Hash`-like object that holds HTTP headers.\n#\n# Two headers are considered the same if their downcase representation is the same\n# (in which `_` is the downcase version of `-`).\n#\n# NOTE: To use `Headers`, you must explicitly import it with `require \"http/headers\"`\nstruct HTTP::Headers\n  include Enumerable({String, Array(String)})\n\n  # :nodoc:\n  record Key, name : String do\n    def hash(hasher)\n      name.each_byte do |c|\n        hasher = normalize_byte(c).hash(hasher)\n      end\n      hasher\n    end\n\n    def ==(key2)\n      key1 = name\n      key2 = key2.name\n\n      return false if key1.bytesize != key2.bytesize\n\n      cstr1 = key1.to_unsafe\n      cstr2 = key2.to_unsafe\n\n      key1.bytesize.times do |i|\n        next if cstr1[i] == cstr2[i] # Optimize the common case\n\n        byte1 = normalize_byte(cstr1[i])\n        byte2 = normalize_byte(cstr2[i])\n\n        return false if byte1 != byte2\n      end\n\n      true\n    end\n\n    private def normalize_byte(byte)\n      char = byte.unsafe_chr\n\n      return byte if char.ascii_lowercase? || char == '-' # Optimize the common case\n      return byte + 32 if char.ascii_uppercase?\n      return '-'.ord if char == '_'\n\n      byte\n    end\n  end\n\n  def initialize\n    # We keep a Hash with String | Array(String) values because\n    # the most common case is a single value and so we avoid allocating\n    # memory for arrays.\n    @hash = Hash(Key, String | Array(String)).new\n  end\n\n  def []=(key : String, value : String) : String\n    check_invalid_header_content(value)\n\n    @hash[wrap(key)] = value\n  end\n\n  def []=(key : String, value : Array(String)) : Array(String)\n    value.each { |val| check_invalid_header_content val }\n\n    @hash[wrap(key)] = value\n  end\n\n  def [](key : String) : String\n    values = @hash[wrap(key)]\n    concat values\n  end\n\n  def []?(key : String) : String?\n    fetch(key, nil)\n  end\n\n  # Returns if among the headers for *key* there is some that contains *word* as a value.\n  # The *word* is expected to match between word boundaries (i.e. non-alphanumeric chars).\n  #\n  # ```\n  # require \"http/headers\"\n  #\n  # headers = HTTP::Headers{\"Connection\" => \"keep-alive, Upgrade\"}\n  # headers.includes_word?(\"Connection\", \"Upgrade\") # => true\n  # ```\n  def includes_word?(key : String, word : String) : Bool\n    return false if word.empty?\n\n    values = @hash[wrap(key)]?\n    case values\n    when Nil\n      false\n    when String\n      includes_word_in_header_value?(word.downcase, values.downcase)\n    else\n      word = word.downcase\n      values.any? do |value|\n        includes_word_in_header_value?(word, value.downcase)\n      end\n    end\n  end\n\n  private def includes_word_in_header_value?(word, value)\n    offset = 0\n    while true\n      start = value.index(word, offset)\n      return false unless start\n      offset = start + word.size\n\n      # check if the match is not surrounded by alphanumeric chars\n      next if start > 0 && value[start - 1].ascii_alphanumeric?\n      next if start + word.size < value.size && value[start + word.size].ascii_alphanumeric?\n      return true\n    end\n\n    false\n  end\n\n  # Adds a header with *key* and *value* to the header set.  If a header with\n  # *key* already exists in the set, *value* is appended to the existing header.\n  #\n  # ```\n  # require \"http/headers\"\n  #\n  # headers = HTTP::Headers.new\n  # headers.add(\"Connection\", \"keep-alive\")\n  # headers[\"Connection\"] # => \"keep-alive\"\n  # headers.add(\"Connection\", \"Upgrade\")\n  # headers[\"Connection\"] # => \"keep-alive,Upgrade\"\n  # ```\n  def add(key : String, value : String) : self\n    check_invalid_header_content value\n    unsafe_add(key, value)\n    self\n  end\n\n  def add(key : String, value : Array(String)) : self\n    value.each { |val| check_invalid_header_content val }\n    unsafe_add(key, value)\n    self\n  end\n\n  def add?(key : String, value : String) : Bool\n    return false unless valid_value?(value)\n    unsafe_add(key, value)\n    true\n  end\n\n  def add?(key : String, value : Array(String)) : Bool\n    value.each { |val| return false unless valid_value?(val) }\n    unsafe_add(key, value)\n    true\n  end\n\n  def fetch(key : String, default : String?) : String?\n    fetch(wrap(key)) { default }\n  end\n\n  def fetch(key, &)\n    values = @hash[wrap(key)]?\n    values ? concat(values) : yield key\n  end\n\n  def has_key?(key : String) : Bool\n    @hash.has_key? wrap(key)\n  end\n\n  def empty? : Bool\n    @hash.empty?\n  end\n\n  def delete(key : String) : String?\n    values = @hash.delete wrap(key)\n    values ? concat(values) : nil\n  end\n\n  def merge!(other) : self\n    other.each do |key, value|\n      self[key] = value\n    end\n    self\n  end\n\n  # Equality operator.\n  #\n  # Returns `true` if *other* is equal to `self`.\n  #\n  # Keys are matched case-insensitive.\n  # String values are treated equal to an array values with the same string as\n  # single element.\n  #\n  # ```\n  # HTTP::Headers{\"Foo\" => \"bar\"} == HTTP::Headers{\"Foo\" => \"bar\"}   # => true\n  # HTTP::Headers{\"Foo\" => \"bar\"} == HTTP::Headers{\"foo\" => \"bar\"}   # => true\n  # HTTP::Headers{\"Foo\" => \"bar\"} == HTTP::Headers{\"Foo\" => [\"bar\"]} # => true\n  # HTTP::Headers{\"Foo\" => \"bar\"} == HTTP::Headers{\"Foo\" => \"baz\"}   # => false\n  # ```\n  def ==(other : self) : Bool\n    # Adapts `Hash#==` to treat string values equal to a single element array.\n\n    return false unless @hash.size == other.@hash.size\n\n    other.@hash.each do |key, value|\n      this_value = @hash.fetch(key) { return false }\n      case {value, this_value}\n      in {String, String}, {Array, Array}\n        return false unless this_value == value\n      in {String, Array}\n        return false unless this_value.size == 1 && this_value.unsafe_fetch(0) == value\n      in {Array, String}\n        return false unless value.size == 1 && value.unsafe_fetch(0) == this_value\n      end\n    end\n    true\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    # Adapts `Hash#hash` to ensure consistency with equality operator.\n\n    # The hash value must be the same regardless of the\n    # order of the keys.\n    result = hasher.result\n\n    @hash.each do |key, value|\n      copy = hasher\n      copy = key.hash(copy)\n      if value.is_a?(Array)\n        copy = value.hash(copy)\n      else\n        copy = 1.hash(copy)\n        copy = value.hash(copy)\n      end\n      result &+= copy.result\n    end\n\n    result.hash(hasher)\n  end\n\n  def each(&)\n    @hash.each do |key, value|\n      yield({key.name, cast(value)})\n    end\n  end\n\n  def get(key : String) : Array(String)\n    cast @hash[wrap(key)]\n  end\n\n  # :nodoc:\n  def get(key : HTTP::Headers::Key) : Array(String)\n    cast @hash[key]\n  end\n\n  def get?(key : String) : Array(String)?\n    @hash[wrap(key)]?.try { |value| cast(value) }\n  end\n\n  def dup\n    dup = HTTP::Headers.new\n    @hash.each do |key, value|\n      dup.@hash[key] = value\n    end\n    dup\n  end\n\n  def clone : HTTP::Headers\n    dup\n  end\n\n  def same?(other : HTTP::Headers) : Bool\n    object_id == other.object_id\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"HTTP::Headers{\"\n    @hash.each_with_index do |(key, values), index|\n      io << \", \" if index > 0\n      key.name.inspect(io)\n      io << \" => \"\n      case values\n      when Array\n        if values.size == 1\n          values.first.inspect(io)\n        else\n          values.inspect(io)\n        end\n      else\n        values.inspect(io)\n      end\n    end\n    io << '}'\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def pretty_print(pp)\n    pp.list(\"HTTP::Headers{\", @hash.keys.sort_by!(&.name), \"}\") do |key|\n      pp.group do\n        key.name.pretty_print(pp)\n        pp.text \" =>\"\n        pp.nest do\n          pp.breakable\n          values = get(key)\n          if values.size == 1\n            values.first.pretty_print(pp)\n          else\n            values.pretty_print(pp)\n          end\n        end\n      end\n    end\n  end\n\n  # Serializes headers according to the HTTP protocol.\n  #\n  # Prints a list of HTTP header fields in the format described in [RFC 7230 §3.2](https://www.rfc-editor.org/rfc/rfc7230#section-3.2),\n  # with each field terminated by a CRLF sequence (`\"\\r\\n\"`).\n  #\n  # The serialization does *not* include a double CRLF sequence at the end.\n  #\n  # ```\n  # headers = HTTP::Headers{\"foo\" => \"bar\", \"baz\" => %w[qux qox]}\n  # headers.serialize # => \"foo: bar\\r\\nbaz: qux\\r\\nbaz: qox\\r\\n\"\n  # ```\n  def serialize : String\n    String.build do |io|\n      serialize(io)\n    end\n  end\n\n  # :ditto:\n  def serialize(io : IO) : Nil\n    each do |name, values|\n      values.each do |value|\n        io << name << \": \" << value << \"\\r\\n\"\n      end\n    end\n  end\n\n  def valid_value?(value : String) : Bool\n    invalid_value_char(value).nil?\n  end\n\n  def clear\n    @hash.clear\n  end\n\n  def object_id\n    @hash.object_id\n  end\n\n  private def unsafe_add(key, value : String)\n    key = wrap(key)\n    existing = @hash[key]?\n    if existing\n      if existing.is_a?(Array)\n        existing << value\n      else\n        @hash[key] = [existing, value]\n      end\n    else\n      @hash[key] = value\n    end\n  end\n\n  private def unsafe_add(key, value : Array(String))\n    key = wrap(key)\n    existing = @hash[key]?\n    if existing\n      if existing.is_a?(Array)\n        existing.concat value\n      else\n        new_value = [existing]\n        new_value.concat(value)\n        @hash[key] = new_value\n      end\n    else\n      @hash[key] = value\n    end\n  end\n\n  private def wrap(key)\n    key.is_a?(Key) ? key : Key.new(key)\n  end\n\n  private def cast(value : String)\n    [value]\n  end\n\n  private def cast(value : Array(String))\n    value\n  end\n\n  private def concat(values : String)\n    values\n  end\n\n  private def concat(values : Array(String))\n    case values.size\n    when 0\n      \"\"\n    when 1\n      values.first\n    else\n      values.join \",\"\n    end\n  end\n\n  private def check_invalid_header_content(value)\n    if char = invalid_value_char(value)\n      raise ArgumentError.new(\"Header content contains invalid character #{char.inspect}\")\n    end\n  end\n\n  private def valid_char?(char)\n    # According to RFC 7230, characters accepted as HTTP header\n    # are '\\t', ' ', all US-ASCII printable characters and\n    # range from '\\x80' to '\\xff' (but the last is obsoleted.)\n    return true if char == '\\t'\n    if char < ' ' || char > '\\u{ff}' || char == '\\u{7f}'\n      return false\n    end\n    true\n  end\n\n  private def invalid_value_char(value)\n    value.each_byte do |byte|\n      unless valid_char?(char = byte.unsafe_chr)\n        return char\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/log.cr",
    "content": "require \"log\"\n\nclass HTTP::Client\n  Log = ::Log.for(self)\n\n  def_around_exec do |request|\n    emit_log(request)\n    yield\n  end\n\n  protected def emit_log(request)\n    Log.debug &.emit(\"Performing request\",\n      method: request.method,\n      host: host,\n      port: port,\n      resource: request.resource,\n    )\n  end\nend\n"
  },
  {
    "path": "src/http/params.cr",
    "content": "require \"uri/params\"\n\nmodule HTTP\n  alias Params = ::URI::Params\nend\n"
  },
  {
    "path": "src/http/request.cr",
    "content": "require \"./common\"\nrequire \"uri\"\nrequire \"./params\"\nrequire \"socket\"\n\n# An HTTP request.\n#\n# It serves both to perform requests by an `HTTP::Client` and to\n# represent requests received by an `HTTP::Server`.\n#\n# A request always holds an `IO` as a body.\n# When creating a request with a `String` or `Bytes` its body\n# will be a `IO::Memory` wrapping these, and the `Content-Length`\n# header will be set appropriately.\n#\n# NOTE: To use `Request`, you must explicitly import it with `require \"http/request\"`\nclass HTTP::Request\n  property method : String\n  property headers : Headers\n  getter body : IO?\n  property version : String\n  @cookies : Cookies?\n  @query_params : URI::Params?\n  @form_params : HTTP::Params?\n  @uri : URI?\n\n  # The network address that sent the request to an HTTP server.\n  #\n  # `HTTP::Server` will try to fill this property, and its value\n  # will have a format like \"IP:port\", but this format is not guaranteed.\n  # Middlewares can overwrite this value.\n  #\n  # Example:\n  #\n  # ```\n  # class ForwarderHandler\n  #   include HTTP::Handler\n  #\n  #   def call(context)\n  #     if ip = context.request.headers[\"X-Real-IP\"]? # When using a reverse proxy that guarantees this field.\n  #       context.request.remote_address = Socket::IPAddress.new(ip, 0)\n  #     end\n  #     call_next(context)\n  #   end\n  # end\n  #\n  # server = HTTP::Server.new([ForwarderHandler.new, HTTP::LogHandler.new])\n  # ```\n  #\n  # This property is not used by `HTTP::Client`.\n  property remote_address : Socket::Address?\n\n  # The network address of the HTTP server.\n  #\n  # `HTTP::Server` will try to fill this property, and its value\n  # will have a format like \"IP:port\", but this format is not guaranteed.\n  # Middlewares can overwrite this value.\n  #\n  # This property is not used by `HTTP::Client`.\n  property local_address : Socket::Address?\n\n  def self.new(method : String, resource : String, headers : Headers? = nil, body : String | Bytes | IO | Nil = nil, version : String = \"HTTP/1.1\") : self\n    # Duplicate headers to prevent the request from modifying data that the user might hold.\n    new(method, resource, headers.try(&.dup), body, version, internal: nil)\n  end\n\n  private def initialize(@method : String, @resource : String, headers : Headers? = nil, body : String | Bytes | IO | Nil = nil, @version = \"HTTP/1.1\", *, internal)\n    @headers = headers || Headers.new\n    self.body = body\n  end\n\n  # Returns a convenience wrapper around querying and setting cookie related\n  # headers, see `HTTP::Cookies`.\n  def cookies : HTTP::Cookies\n    @cookies ||= Cookies.from_client_headers(headers)\n  end\n\n  # Returns a convenience wrapper around querying and setting query params,\n  # see `URI::Params`.\n  def query_params : URI::Params\n    @query_params ||= uri.query_params\n  end\n\n  # Returns a convenience wrapper to parse form params, see `URI::Params`.\n  # Returns `nil` in case the content type `\"application/x-www-form-urlencoded\"`\n  # is not present or the body is `nil`.\n  def form_params? : HTTP::Params?\n    @form_params ||= begin\n      if headers[\"Content-Type\"]? == \"application/x-www-form-urlencoded\"\n        if body = self.body\n          HTTP::Params.parse(body.gets_to_end)\n        end\n      end\n    end\n  end\n\n  # Returns a convenience wrapper to parse form params, see `URI::Params`.\n  def form_params : HTTP::Params\n    form_params? || HTTP::Params.new\n  end\n\n  def resource : String\n    update_uri\n    @uri.try(&.request_target) || @resource\n  end\n\n  def keep_alive? : Bool\n    HTTP.keep_alive?(self)\n  end\n\n  def ignore_body? : Bool\n    @method == \"HEAD\"\n  end\n\n  def content_length=(length : Int) : String\n    headers[\"Content-Length\"] = length.to_s\n  end\n\n  def content_length\n    HTTP.content_length(headers)\n  end\n\n  def body=(body : String) : String\n    @body = IO::Memory.new(body)\n    self.content_length = body.bytesize\n  end\n\n  def body=(body : Bytes) : String\n    @body = IO::Memory.new(body)\n    self.content_length = body.size\n  end\n\n  def body=(@body : IO) : IO\n  end\n\n  def body=(@body : Nil) : Nil\n    @headers[\"Content-Length\"] = \"0\" if @method.in?(\"POST\", \"PUT\")\n  end\n\n  def to_io(io : IO) : Nil\n    io << @method << ' ' << resource << ' ' << @version << \"\\r\\n\"\n    cookies = @cookies\n    headers = cookies ? cookies.add_request_headers(@headers) : @headers\n    HTTP.serialize_headers_and_body(io, headers, nil, @body, @version)\n  end\n\n  # :nodoc:\n  record RequestLine, method : String, resource : String, http_version : String\n\n  # Returns a `HTTP::Request` instance if successfully parsed,\n  # `nil` on EOF or `HTTP::Status` otherwise.\n  def self.from_io(io : IO, *, max_request_line_size : Int32 = HTTP::MAX_REQUEST_LINE_SIZE, max_headers_size : Int32 = HTTP::MAX_HEADERS_SIZE) : HTTP::Request | HTTP::Status | Nil\n    line = parse_request_line(io, max_request_line_size)\n    return line unless line.is_a?(RequestLine)\n\n    status = HTTP.parse_headers_and_body(io, max_headers_size: max_headers_size) do |headers, body|\n      # No need to dup headers since nobody else holds them\n      request = new line.method, line.resource, headers, body, line.http_version, internal: nil\n\n      if io.responds_to?(:remote_address)\n        request.remote_address = io.remote_address\n      end\n\n      if io.responds_to?(:local_address)\n        request.local_address = io.local_address\n      end\n\n      return request\n    end\n\n    # Malformed or unexpectedly ended http request\n    status || HTTP::Status::BAD_REQUEST\n  end\n\n  private METHODS = %w(GET HEAD POST PUT DELETE CONNECT OPTIONS PATCH TRACE)\n\n  private def self.parse_request_line(io : IO, max_request_line_size) : RequestLine | HTTP::Status | Nil\n    # Optimization: see if we have a peek buffer\n    # (avoids a string allocation for the entire request line)\n    if peek = io.peek\n      # peek.empty? means there's no more input (EOF), so no more requests\n      return nil if peek.empty?\n\n      # See if we can find \\n\n      index = peek.index('\\n'.ord.to_u8)\n      if index\n        return HTTP::Status::URI_TOO_LONG if index > max_request_line_size\n\n        end_index = index\n\n        # Also check (and discard) \\r before that\n        if index > 0 && peek[index - 1] == '\\r'.ord.to_u8\n          end_index -= 1\n        end\n\n        parts = parse_request_line(peek[0, end_index])\n        io.skip(index + 1) # Must skip until after \\n\n        return parts\n      end\n    end\n\n    request_line = io.gets(max_request_line_size + 1, chomp: true)\n    return nil unless request_line\n\n    # Identify Request-URI too long\n    if request_line.bytesize > max_request_line_size\n      return HTTP::Status::URI_TOO_LONG\n    end\n\n    parse_request_line(request_line)\n  end\n\n  private def self.parse_request_line(line : String) : RequestLine | HTTP::Status\n    parse_request_line(line.to_slice)\n  end\n\n  private def self.parse_request_line(slice : Bytes) : RequestLine | HTTP::Status\n    space_index = slice.index(' '.ord.to_u8)\n\n    # Oops, only a single part (should be three)\n    return HTTP::Status::BAD_REQUEST unless space_index\n\n    subslice = slice[0...space_index]\n\n    # Optimization: see if it's one of the common methods\n    # (avoids a string allocation for these methods)\n    method = METHODS.find { |method| method.to_slice == subslice } ||\n             String.new(subslice)\n\n    # Skip spaces.\n    # The RFC just mentions a single space but most servers allow multiple.\n    while space_index < slice.size && slice[space_index] == ' '.ord.to_u8\n      space_index += 1\n    end\n\n    # Oops, we only found the \"method\" part followed by spaces\n    return HTTP::Status::BAD_REQUEST if space_index == slice.size\n\n    next_space_index = slice.index(' '.ord.to_u8, offset: space_index)\n\n    # Oops, we only found two parts (should be three)\n    return HTTP::Status::BAD_REQUEST unless next_space_index\n\n    resource = String.new(slice[space_index...next_space_index])\n\n    # Skip spaces again\n    space_index = next_space_index\n    while space_index < slice.size && slice[space_index] == ' '.ord.to_u8\n      space_index += 1\n    end\n\n    next_space_index = slice.index(' '.ord.to_u8, offset: space_index) || slice.size\n\n    subslice = slice[space_index...next_space_index]\n\n    # Optimization: avoid allocating a string for common HTTP version\n    http_version = HTTP::SUPPORTED_VERSIONS.find { |version| version.to_slice == subslice }\n    return HTTP::Status::BAD_REQUEST unless http_version\n\n    # Skip trailing spaces\n    space_index = next_space_index\n    while space_index < slice.size\n      # Oops, we find something else (more than three parts)\n      return HTTP::Status::BAD_REQUEST unless slice[space_index] == ' '.ord.to_u8\n      space_index += 1\n    end\n\n    RequestLine.new method: method, resource: resource, http_version: http_version\n  end\n\n  # Returns the request's path component.\n  def path : String\n    uri.path.presence || \"/\"\n  end\n\n  # Sets request's path component.\n  def path=(path : String) : String\n    uri.path = path\n  end\n\n  # Lazily parses and returns the request's query component.\n  def query : String?\n    update_uri\n    uri.query\n  end\n\n  # Sets request's query component.\n  def query=(value : String?) : String?\n    uri.query = value\n    update_query_params\n    value\n  end\n\n  # Extracts the hostname from `Host` header.\n  #\n  # Returns `nil` if the `Host` header is missing.\n  #\n  # If the `Host` header contains a port number, it is stripped off.\n  def hostname : String?\n    header = @headers[\"Host\"]?\n    return unless header\n\n    host, _, port = header.rpartition(\":\")\n    if host.empty?\n      # no colon in header\n      host = header\n    else\n      port = port.to_i?(whitespace: false)\n      unless port && Socket::IPAddress.valid_port?(port)\n        # what we identified as port is not valid, so use the entire header\n        host = header\n      end\n    end\n\n    URI.unwrap_ipv6(host)\n  end\n\n  # Returns request host with port from headers.\n  @[Deprecated(%q(Use `headers[\"Host\"]?` instead.))]\n  def host_with_port : String?\n    @headers[\"Host\"]?\n  end\n\n  # Returns the underlying URI object.\n  #\n  # Used internally to provide the components of the request uri.\n  def uri : URI\n    @uri ||= URI::Parser.new(@resource).parse_request_target.uri\n  end\n\n  private def update_query_params\n    return unless @query_params\n    @query_params = uri.query_params\n  end\n\n  private def update_uri\n    return unless @query_params\n    uri.query = query_params.to_s\n  end\n\n  def if_match : Array(String)?\n    parse_etags(\"If-Match\")\n  end\n\n  def if_none_match : Array(String)?\n    parse_etags(\"If-None-Match\")\n  end\n\n  private def parse_etags(header_name)\n    header = headers[header_name]?\n\n    return unless header\n    return [\"*\"] if header == \"*\"\n\n    etags = [] of String\n    reader = Char::Reader.new(header)\n\n    require_comma = false\n    while reader.has_next?\n      case reader.current_char\n      when ' ', '\\t'\n        reader.next_char\n      when ','\n        reader.next_char\n        require_comma = false\n      when '\"', 'W'\n        if require_comma\n          # return what we've got on error\n          return etags\n        end\n\n        reader, etag = consume_etag(reader)\n        if etag\n          etags << etag\n          require_comma = true\n        else\n          # return what we've got on error\n          return etags\n        end\n      else\n        # return what we've got on error\n        return etags\n      end\n    end\n\n    etags\n  end\n\n  private def consume_etag(reader)\n    start = reader.pos\n\n    if reader.current_char == 'W'\n      reader.next_char\n      return reader, nil if reader.current_char != '/' || !reader.has_next?\n      reader.next_char\n    end\n\n    return reader, nil if reader.current_char != '\"'\n    reader.next_char\n\n    while reader.has_next?\n      case reader.current_char\n      when '!', '\\u{23}'..'\\u{7E}', '\\u{80}'..'\\u{FF}'\n        reader.next_char\n      when '\"'\n        reader.next_char\n        return reader, reader.string.byte_slice(start, reader.pos - start)\n      else\n        return reader, nil\n      end\n    end\n\n    return reader, nil\n  end\nend\n"
  },
  {
    "path": "src/http/server/context.cr",
    "content": "require \"../request\"\nrequire \"./response\"\n\nclass HTTP::Server\n  # Instances of this class are passed to an `HTTP::Server` handler.\n  class Context\n    # The `HTTP::Request` to process.\n    getter request : Request\n\n    # The `HTTP::Server::Response` to configure and write to.\n    getter response : Response\n\n    # :nodoc:\n    def initialize(@request : Request, @response : Response)\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/server/handler.cr",
    "content": "require \"./context\"\n\n# A handler is a class which includes `HTTP::Handler` and implements the `call` method.\n# You can use a handler to intercept any incoming request and can modify the response.\n# These can be used for request throttling, ip-based filtering, adding custom headers e.g.\n#\n# NOTE: To use `Handler`, you must explicitly import it with `require \"http/server/handler\"`\n#\n# ### A custom handler\n#\n# ```\n# require \"http/server/handler\"\n#\n# class CustomHandler\n#   include HTTP::Handler\n#\n#   def call(context)\n#     puts \"Doing some stuff\"\n#     call_next(context)\n#   end\n# end\n# ```\nmodule HTTP::Handler\n  property next : Handler | HandlerProc | Nil\n\n  abstract def call(context : HTTP::Server::Context)\n\n  def call_next(context : HTTP::Server::Context) : Nil\n    if next_handler = @next\n      next_handler.call(context)\n    else\n      context.response.respond_with_status(:not_found)\n    end\n  end\n\n  alias HandlerProc = HTTP::Server::Context ->\nend\n\nrequire \"./handlers/*\"\n"
  },
  {
    "path": "src/http/server/handlers/compress_handler.cr",
    "content": "{% if !flag?(:without_zlib) %}\n  require \"compress/deflate\"\n  require \"compress/gzip\"\n{% end %}\n\n# A handler that configures an `HTTP::Server::Response` to compress the response\n# output, either using gzip or deflate, depending on the `Accept-Encoding` request header.\n#\n# NOTE: To use `CompressHandler`, you must explicitly import it with `require \"http\"`\nclass HTTP::CompressHandler\n  include HTTP::Handler\n\n  def call(context : HTTP::Server::Context) : Nil\n    {% if flag?(:without_zlib) %}\n      call_next(context)\n    {% else %}\n      context.response.output = CompressIO.new(context.response.output, context)\n      call_next(context)\n    {% end %}\n  end\n\n  {% unless flag?(:without_zlib) %}\n    private class CompressIO < IO\n      def initialize(@io : IO, @context : HTTP::Server::Context)\n        @checked = false\n      end\n\n      def read(slice : Bytes)\n        raise NotImplementedError.new(\"read\")\n      end\n\n      def write(slice : Bytes) : Nil\n        check_output unless @checked\n        @io.write(slice)\n      end\n\n      def flush\n        @io.flush\n      end\n\n      def close\n        @io.close\n      end\n\n      private def check_output\n        @checked = true\n\n        return if @context.response.wrote_headers?\n        return if @context.response.headers.has_key?(\"Content-Encoding\")\n\n        request_headers = @context.request.headers\n\n        if request_headers.includes_word?(\"Accept-Encoding\", \"gzip\")\n          @context.response.headers[\"Content-Encoding\"] = \"gzip\"\n          @context.response.headers.delete(\"Content-Length\")\n          @io = Compress::Gzip::Writer.new(@io, sync_close: true)\n        elsif request_headers.includes_word?(\"Accept-Encoding\", \"deflate\")\n          @context.response.headers[\"Content-Encoding\"] = \"deflate\"\n          @context.response.headers.delete(\"Content-Length\")\n          @io = Compress::Deflate::Writer.new(@io, sync_close: true)\n        end\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/http/server/handlers/error_handler.cr",
    "content": "# A handler that invokes the next handler and, if that next handler raises\n# an exception, returns with a 500 (Internal Server Error) status code.\n#\n# In verbose mode prints the exception with its backtrace to the response.\n# Otherwise a generic error message is returned to the client.\n#\n# This handler also logs the exceptions to the specified logger or\n# the logger for the source \"http.server\" by default.\n#\n# NOTE: To use `ErrorHandler`, you must explicitly import it with `require \"http\"`\nclass HTTP::ErrorHandler\n  include HTTP::Handler\n\n  def initialize(@verbose : Bool = false, @log = Log.for(\"http.server\"))\n  end\n\n  def call(context : HTTP::Server::Context) : Nil\n    call_next(context)\n  rescue ex : HTTP::Server::ClientError\n    @log.debug(exception: ex.cause) { ex.message }\n  rescue ex : Exception\n    @log.error(exception: ex) { \"Unhandled exception\" }\n    unless context.response.closed? || context.response.wrote_headers?\n      if @verbose\n        context.response.reset\n        context.response.status = :internal_server_error\n        context.response.content_type = \"text/plain\"\n        context.response.print(\"ERROR: \")\n        context.response.puts(ex.inspect_with_backtrace)\n      else\n        context.response.respond_with_status(:internal_server_error)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/server/handlers/log_handler.cr",
    "content": "require \"log\"\n\n# A handler that logs the request method, resource, status code, and\n# the time used to execute the next handler\n#\n# NOTE: To use `LogHandler`, you must explicitly import it with `require \"http\"`\nclass HTTP::LogHandler\n  include HTTP::Handler\n\n  def initialize(@log = Log.for(\"http.server\"))\n  end\n\n  def call(context : HTTP::Server::Context) : Nil\n    start = Time.instant\n\n    begin\n      call_next(context)\n    ensure\n      elapsed_text = elapsed_text(start.elapsed)\n\n      req = context.request\n      res = context.response\n\n      addr =\n        case remote_address = req.remote_address\n        when nil\n          \"-\"\n        when Socket::IPAddress\n          remote_address.address\n        else\n          remote_address\n        end\n\n      @log.info { \"#{addr} - #{req.method} #{req.resource} #{req.version} - #{res.status_code} (#{elapsed_text})\" }\n    end\n  end\n\n  private def elapsed_text(elapsed)\n    minutes = elapsed.total_minutes\n    return \"#{minutes.round(2)}m\" if minutes >= 1\n\n    \"#{elapsed.total_seconds.humanize(precision: 2, significant: false)}s\"\n  end\nend\n"
  },
  {
    "path": "src/http/server/handlers/static_file_handler.cr",
    "content": "require \"ecr/macros\"\nrequire \"html\"\nrequire \"uri\"\nrequire \"mime\"\n\n# A handler that lists directories and serves files under a given public directory.\n#\n# This handler can send precompressed content, if the client accepts it, and a file\n# with the same name and `.gz` extension appended is found in the same directory.\n# Precompressed files are only served if they are newer than the original file.\n#\n# NOTE: To use `StaticFileHandler`, you must explicitly import it with `require \"http\"`\nclass HTTP::StaticFileHandler\n  include HTTP::Handler\n\n  # In some file systems, using `gz --keep` to compress the file will keep the\n  # modification time of the original file but truncating some decimals. We\n  # serve the gzipped file nonetheless if the .gz file is modified by a duration\n  # of `TIME_DRIFT` before the original file. This value should match the\n  # granularity of the underlying file system's modification times\n  private TIME_DRIFT = 10.milliseconds\n\n  @public_dir : Path\n\n  # Creates a handler that will serve files in the given *public_dir*, after\n  # expanding it (using `File#expand_path`).\n  #\n  # If *fallthrough* is `false`, this handler does not call next handler when\n  # request method is neither GET or HEAD, then serves `405 Method Not Allowed`.\n  # Otherwise, it calls next handler.\n  #\n  # If *directory_listing* is `false`, directory listing is disabled. This means that\n  # paths matching directories are ignored and next handler is called.\n  def initialize(public_dir : String, @fallthrough : Bool = true, @directory_listing : Bool = true)\n    @public_dir = Path.new(public_dir).expand\n  end\n\n  # :ditto:\n  @[Deprecated]\n  def self.new(public_dir : String, fallthrough = true, directory_listing = true)\n    new(public_dir, fallthrough: !!fallthrough, listing: !!listing)\n  end\n\n  def call(context) : Nil\n    check_request_method!(context) || return\n\n    request_path = request_path(context)\n\n    check_request_path!(context, request_path) || return\n\n    request_path = Path.posix(request_path)\n    expanded_path = request_path.expand(\"/\")\n\n    file_info, file_path = file_info(expanded_path)\n\n    if normalized_path = normalize_request_path(context, request_path, expanded_path, file_info)\n      return redirect_to context, normalized_path\n    end\n\n    return call_next(context) unless file_info\n\n    if file_info.directory?\n      directory_index(context, request_path, file_path)\n    elsif file_info.file?\n      serve_file_with_cache(context, file_info, file_path)\n    else # Not a normal file (FIFO/device/socket)\n      call_next(context)\n    end\n  end\n\n  private def check_request_method!(context : Server::Context) : Bool\n    return true if context.request.method.in?(\"GET\", \"HEAD\")\n\n    if @fallthrough\n      call_next(context)\n    else\n      context.response.status = :method_not_allowed\n      context.response.headers.add(\"Allow\", \"GET, HEAD\")\n    end\n\n    false\n  end\n\n  private def check_request_path!(context : Server::Context, request_path : String) : Bool\n    # File path cannot contain '\\0' (NUL) because all filesystem I know\n    # don't accept '\\0' character as file name.\n    if request_path.includes? '\\0'\n      context.response.respond_with_status(:bad_request)\n      return false\n    end\n\n    true\n  end\n\n  private def normalize_request_path(context : Server::Context, request_path : Path, expanded_path : Path, file_info) : Path?\n    if @directory_listing && file_info.try(&.directory?) && !request_path.ends_with_separator?\n      # Append / to path if missing\n      expanded_path.join(\"\")\n    elsif request_path != expanded_path\n      expanded_path\n    end\n  end\n\n  private def file_info(expanded_path : Path)\n    file_path = @public_dir.join(expanded_path.to_kind(Path::Kind.native))\n\n    {File.info?(file_path), file_path}\n  end\n\n  private def serve_file_with_cache(context : Server::Context, file_info, file_path : Path)\n    last_modified = file_info.modification_time\n    add_cache_headers(context.response.headers, last_modified)\n\n    if cache_request?(context, last_modified)\n      context.response.status = :not_modified\n      return\n    end\n\n    serve_file_compressed(context, file_info, file_path, last_modified)\n  end\n\n  private def serve_file_compressed(context : Server::Context, file_info, file_path : Path, last_modified : Time)\n    original_file_path = file_path\n\n    # Checks if pre-gzipped file can be served\n    if context.request.headers.includes_word?(\"Accept-Encoding\", \"gzip\")\n      gz_file_path = Path[\"#{file_path}.gz\"]\n\n      if (gz_file_info = File.info?(gz_file_path)) &&\n         last_modified - gz_file_info.modification_time < TIME_DRIFT\n        file_path = gz_file_path\n        file_info = gz_file_info\n        context.response.headers[\"Content-Encoding\"] = \"gzip\"\n      end\n    end\n\n    serve_file(context, file_info, file_path, original_file_path, last_modified)\n  end\n\n  private def serve_file(context : Server::Context, file_info, file_path : Path, original_file_path : Path, last_modified : Time)\n    context.response.content_type = MIME.from_filename(original_file_path.to_s, \"application/octet-stream\")\n\n    begin\n      File.open(file_path) do |file|\n        if range_header = context.request.headers[\"Range\"]?\n          serve_file_range(context, file, range_header, file_info)\n        else\n          context.response.headers[\"Accept-Ranges\"] = \"bytes\"\n\n          serve_file_full(context, file, file_info)\n        end\n      end\n    rescue File::Error\n      # If there's any file error, we report the file as not existing.\n      # Even if it exists but is not readable, we don't want to disclose its\n      # existence.\n      context.response.respond_with_status(:not_found)\n      return\n    end\n  end\n\n  # *file* should be seekable, that's implement #seek method\n  private def serve_file_range(context : Server::Context, file : IO, range_header : String, file_info)\n    range_header = range_header.lchop?(\"bytes=\")\n    unless range_header\n      context.response.headers[\"Content-Range\"] = \"bytes */#{file_info.size}\"\n      context.response.status = :range_not_satisfiable\n      context.response.close\n      return\n    end\n\n    ranges = parse_ranges(range_header, file_info.size)\n    unless ranges\n      context.response.respond_with_status :bad_request\n      return\n    end\n\n    if file_info.size.zero? && ranges.size == 1 && ranges[0].begin.zero?\n      context.response.status = :ok\n      return\n    end\n\n    # If any of the ranges start beyond the end of the file, we return an\n    # HTTP 416 Range Not Satisfiable.\n    # See https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-11.1\n    if ranges.any? { |range| range.begin >= file_info.size }\n      context.response.headers[\"Content-Range\"] = \"bytes */#{file_info.size}\"\n      context.response.status = :range_not_satisfiable\n      context.response.close\n      return\n    end\n\n    ranges.map! { |range| range.begin..(Math.min(range.end, file_info.size - 1)) }\n\n    context.response.status = :partial_content\n\n    if ranges.size == 1\n      range = ranges.first\n      file.seek range.begin\n      context.response.headers[\"Content-Range\"] = \"bytes #{range.begin}-#{range.end}/#{file_info.size}\"\n      IO.copy file, context.response, range.size\n    else\n      MIME::Multipart.build(context.response) do |builder|\n        content_type = context.response.headers[\"Content-Type\"]?\n        context.response.headers[\"Content-Type\"] = builder.content_type(\"byterange\")\n\n        ranges.each do |range|\n          file.seek range.begin\n          headers = HTTP::Headers{\n            \"Content-Range\"  => \"bytes #{range.begin}-#{range.end}/#{file_info.size}\",\n            \"Content-Length\" => range.size.to_s,\n          }\n          headers[\"Content-Type\"] = content_type if content_type\n          chunk_io = IO::Sized.new(file, range.size)\n          builder.body_part headers, chunk_io\n        end\n      end\n    end\n  end\n\n  private def serve_file_full(context : Server::Context, file : IO, file_info)\n    context.response.status = :ok\n    context.response.content_length = file_info.size\n    IO.copy(file, context.response)\n  end\n\n  # TODO: Optimize without lots of intermediary strings\n  private def parse_ranges(header, file_size)\n    ranges = [] of Range(Int64, Int64)\n    header.split(\",\") do |range|\n      start_string, dash, finish_string = range.lchop(' ').partition(\"-\")\n      return if dash.empty?\n      start = start_string.to_i64?\n      return if start.nil? && !start_string.empty?\n      if finish_string.empty?\n        return if start_string.empty?\n        finish = file_size\n      else\n        finish = finish_string.to_i64? || return\n      end\n      if file_size.zero?\n        # > When a selected representation has zero length, the only satisfiable\n        # > form of range-spec in a GET request is a suffix-range with a non-zero suffix-length.\n\n        if start\n          # This return value signals an unsatisfiable range.\n          return [1_i64..0_i64]\n        elsif finish <= 0\n          return\n        else\n          start = finish = 0_i64\n        end\n      elsif !start\n        # suffix-range\n        start = {file_size - finish, 0_i64}.max\n        finish = file_size - 1\n      end\n\n      range = (start..finish)\n      return unless 0 <= range.begin <= range.end\n      ranges << range\n    end\n    ranges unless ranges.empty?\n  end\n\n  private def request_path(context : Server::Context) : String\n    original_path = context.request.path.not_nil!\n\n    request_path(URI.decode(original_path))\n  end\n\n  # given a full path of the request, returns the path\n  # of the file that should be expanded at the public_dir\n  protected def request_path(path : String) : String\n    path\n  end\n\n  private def redirect_to(context : Server::Context, path)\n    uri = context.request.uri.dup\n    uri.path = URI.encode_path(path.to_s)\n    context.response.redirect uri\n  end\n\n  private def add_cache_headers(response_headers : HTTP::Headers, last_modified : Time) : Nil\n    response_headers[\"Etag\"] = etag(last_modified)\n    response_headers[\"Last-Modified\"] = HTTP.format_time(last_modified)\n  end\n\n  private def cache_request?(context : HTTP::Server::Context, last_modified : Time) : Bool\n    # According to RFC 7232:\n    # A recipient must ignore If-Modified-Since if the request contains an If-None-Match header field\n    if if_none_match = context.request.if_none_match\n      match = {\"*\", context.response.headers[\"Etag\"]}\n      if_none_match.any? { |etag| match.includes?(etag) }\n    elsif if_modified_since = context.request.headers[\"If-Modified-Since\"]?\n      header_time = HTTP.parse_time(if_modified_since)\n      # File mtime probably has a higher resolution than the header value.\n      # An exact comparison might be slightly off, so we add 1s padding.\n      # Static files should generally not be modified in subsecond intervals, so this is perfectly safe.\n      # This might be replaced by a more sophisticated time comparison when it becomes available.\n      !!(header_time && last_modified <= header_time + 1.second)\n    else\n      false\n    end\n  end\n\n  private def etag(modification_time)\n    %{W/\"#{modification_time.to_unix}\"}\n  end\n\n  record DirectoryListing, request_path : String, path : String do\n    def each_entry(&)\n      Dir.each_child(path) do |entry|\n        yield entry\n      end\n    end\n\n    ECR.def_to_s \"#{__DIR__}/static_file_handler.html\"\n  end\n\n  private def directory_index(context : Server::Context, request_path : Path, path : Path)\n    unless @directory_listing\n      return call_next(context)\n    end\n\n    context.response.content_type = \"text/html; charset=utf-8\"\n    directory_listing(context.response, request_path, path)\n  end\n\n  private def directory_listing(io : IO, request_path : Path, path : Path)\n    DirectoryListing.new(request_path.to_s, path.to_s).to_s(io)\n  end\nend\n"
  },
  {
    "path": "src/http/server/handlers/static_file_handler.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>Directory listing for <%= HTML.escape request_path %></title>\n  </head>\n  <body>\n    <h2>Directory listing for <%= HTML.escape request_path %></h2>\n    <hr/>\n    <ul>\n      <% encoded_request_path = URI.encode_path(request_path) %>\n      <% each_entry do |entry| %>\n        <li>\n          <a href=\"<%= encoded_request_path %><%= URI.encode_path entry %>\"><%= HTML.escape entry %></a>\n        </li>\n      <% end %>\n    </ul>\n  </body>\n</html>\n"
  },
  {
    "path": "src/http/server/handlers/websocket_handler.cr",
    "content": "require \"base64\"\nrequire \"../../web_socket\"\n\n# A handler which adds websocket functionality to an `HTTP::Server`.\n#\n# NOTE: To use `WebSocketHandler`, you must explicitly import it with `require \"http\"`\n#\n# When a request can be upgraded, the associated `HTTP::WebSocket` and\n# `HTTP::Server::Context` will be yielded to the block. For example:\n#\n# ```\n# ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|\n#   ws.on_ping { ws.pong ctx.request.path }\n# end\n# server = HTTP::Server.new [ws_handler]\n# ```\nclass HTTP::WebSocketHandler\n  include HTTP::Handler\n\n  def initialize(@subprotocols : Array(String)? = nil, &@proc : WebSocket, Server::Context ->)\n  end\n\n  def call(context) : Nil\n    unless websocket_upgrade_request? context.request\n      return call_next context\n    end\n\n    response = context.response\n\n    version = context.request.headers[\"Sec-WebSocket-Version\"]?\n    unless version == WebSocket::Protocol::VERSION\n      response.status = :upgrade_required\n      response.headers[\"Sec-WebSocket-Version\"] = WebSocket::Protocol::VERSION\n      return\n    end\n\n    key = context.request.headers[\"Sec-WebSocket-Key\"]?\n\n    unless key\n      response.respond_with_status(:bad_request)\n      return\n    end\n\n    accept_code = WebSocket::Protocol.key_challenge(key)\n\n    response.status = :switching_protocols\n    response.headers[\"Upgrade\"] = \"websocket\"\n    response.headers[\"Connection\"] = \"Upgrade\"\n    response.headers[\"Sec-WebSocket-Accept\"] = accept_code\n    if protocol = sec_websocket_protocol(context.request)\n      response.headers[\"Sec-WebSocket-Protocol\"] = protocol\n    end\n    response.upgrade do |io|\n      ws_session = WebSocket.new(io, sync_close: false)\n      @proc.call(ws_session, context)\n      ws_session.run\n    end\n  end\n\n  private def websocket_upgrade_request?(request)\n    return false unless upgrade = request.headers[\"Upgrade\"]?\n    return false unless upgrade.compare(\"websocket\", case_insensitive: true) == 0\n\n    request.headers.includes_word?(\"Connection\", \"Upgrade\")\n  end\n\n  private def sec_websocket_protocol(request)\n    return unless requested_protocols = request.headers[\"Sec-WebSocket-Protocol\"]?\n    return unless supported_protocols = @subprotocols\n\n    requested_protocols.split(',') do |protocol|\n      protocol = protocol.strip\n      if supported_protocols.includes? protocol\n        return protocol\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/server/request_processor.cr",
    "content": "require \"./handler\"\nrequire \"log\"\n\nclass HTTP::Server::RequestProcessor\n  Log = ::Log.for(\"http.server\")\n\n  # Maximum permitted size of the request line in an HTTP request.\n  property max_request_line_size = HTTP::MAX_REQUEST_LINE_SIZE\n\n  # Maximum permitted combined size of the headers in an HTTP request.\n  property max_headers_size = HTTP::MAX_HEADERS_SIZE\n\n  def initialize(&@handler : HTTP::Handler::HandlerProc)\n    @wants_close = false\n  end\n\n  def initialize(@handler : HTTP::Handler | HTTP::Handler::HandlerProc)\n    @wants_close = false\n  end\n\n  def close : Nil\n    @wants_close = true\n  end\n\n  def process(input : IO, output : IO) : Nil\n    response = Response.new(output)\n\n    begin\n      until @wants_close\n        request = HTTP::Request.from_io(\n          input,\n          max_request_line_size: max_request_line_size,\n          max_headers_size: max_headers_size,\n        )\n\n        # EOF\n        break unless request\n\n        response.reset\n\n        if request.is_a?(HTTP::Status)\n          response.respond_with_status(request)\n          return\n        end\n\n        response.version = request.version\n        response.headers[\"Connection\"] = \"keep-alive\" if request.keep_alive?\n        context = Context.new(request, response)\n\n        Log.with_context do\n          @handler.call(context)\n        rescue ex : ClientError\n          Log.debug(exception: ex.cause) { ex.message }\n        rescue ex\n          Log.error(exception: ex) { \"Unhandled exception on HTTP::Handler\" }\n          unless response.closed?\n            unless response.wrote_headers?\n              response.respond_with_status(:internal_server_error)\n            end\n          end\n          return\n        ensure\n          response.output.close\n        end\n\n        output.flush\n\n        # If there is an upgrade handler, hand over\n        # the connection to it and return\n        if upgrade_handler = response.upgrade_handler\n          upgrade_handler.call(output)\n          return\n        end\n\n        break unless request.keep_alive?\n\n        # Don't continue if the handler set `Connection` header to `close`\n        break unless HTTP.keep_alive?(response)\n\n        # The request body is either FixedLengthContent or ChunkedContent.\n        # In case it has not entirely been consumed by the handler, the connection is\n        # closed the connection even if keep alive was requested.\n        case body = request.body\n        when FixedLengthContent\n          if body.read_remaining > 0\n            # Close the connection if there are bytes remaining\n            break\n          end\n        when ChunkedContent\n          # Close the connection if the IO has still bytes to read.\n          break unless body.closed?\n        end\n      end\n    rescue IO::Error\n      # IO-related error, nothing to do\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/server/response.cr",
    "content": "require \"../headers\"\nrequire \"../status\"\nrequire \"../cookie\"\n\nclass HTTP::Server\n  # The response to configure and write to in an `HTTP::Server` handler.\n  #\n  # The response `status` and `headers` must be configured before writing\n  # the response body. Once response output is written, changing the `status`\n  # and `headers` properties has no effect.\n  #\n  # The `HTTP::Server::Response` is a write-only `IO`, so all `IO` methods are available\n  # in it.\n  #\n  # A response can be upgraded with the `upgrade` method. Once invoked, headers\n  # are written and the connection `IO` (a socket) is yielded to the given block.\n  # This is useful to implement protocol upgrades, such as websockets.\n  class Response < IO\n    # The response headers (`HTTP::Headers`). These must be set before writing to the response.\n    getter headers : HTTP::Headers\n\n    # The version of the HTTP::Request that created this response.\n    getter version : String\n\n    # The `IO` to which output is written. This can be changed/wrapped to filter\n    # the response body (for example to compress the output).\n    property output : IO\n\n    # :nodoc:\n    def version=(version : String) : String\n      check_headers\n      @version = version\n    end\n\n    # The status code of this response, which must be set before writing the response\n    # body. If not set, the default value is 200 (OK).\n    getter status : HTTP::Status\n\n    def status=(status : HTTP::Status) : HTTP::Status\n      check_headers\n      @status = status\n    end\n\n    # :nodoc:\n    property upgrade_handler : (IO ->)?\n\n    @cookies : HTTP::Cookies?\n\n    # :nodoc:\n    def initialize(@io : IO, @version = \"HTTP/1.1\")\n      @headers = Headers.new\n      @status = :ok\n      @wrote_headers = false\n      @output = output = @original_output = Output.new(@io)\n      output.response = self\n    end\n\n    # :nodoc:\n    def reset : Nil\n      # This method is called by RequestProcessor to avoid allocating a new instance for each iteration.\n      @headers.clear\n      @cookies = nil\n      @status = :ok\n      @status_message = nil\n      @wrote_headers = false\n      @output = @original_output\n      @original_output.reset\n    end\n\n    # Convenience method to set the `Content-Type` header.\n    def content_type=(content_type : String) : String\n      check_headers\n      headers[\"Content-Type\"] = content_type\n    end\n\n    # Convenience method to get `Content-Type` header.\n    def content_type : String?\n      headers[\"Content-Type\"]?\n    end\n\n    # Convenience method to set the `Content-Length` header.\n    def content_length=(content_length : Int) : String\n      check_headers\n      headers[\"Content-Length\"] = content_length.to_s\n    end\n\n    # Convenience method to get the `Content-Length` header.\n    def content_length : Int64?\n      headers[\"Content-Length\"]?.try(&.to_i64)\n    end\n\n    # Convenience method to retrieve the HTTP status code.\n    def status_code : Int32\n      status.code\n    end\n\n    # Convenience method to set the HTTP status code.\n    def status_code=(status_code : Int32) : Int32\n      self.status = HTTP::Status.new(status_code)\n      status_code\n    end\n\n    # See `IO#write(slice)`.\n    def write(slice : Bytes) : Nil\n      return if slice.empty?\n\n      @output.write(slice)\n    end\n\n    # Convenience method to set cookies, see `HTTP::Cookies`.\n    def cookies : HTTP::Cookies\n      @cookies ||= HTTP::Cookies.new\n    end\n\n    # :nodoc:\n    def read(slice : Bytes) : NoReturn\n      raise \"Can't read from HTTP::Server::Response\"\n    end\n\n    # Upgrades this response, writing headers and yielding the connection `IO` (a socket) to the given block.\n    # This is useful to implement protocol upgrades, such as websockets.\n    def upgrade(&block : IO ->) : Nil\n      write_headers\n      @upgrade_handler = block\n    end\n\n    # Flushes the output. This method must be implemented if wrapping the response output.\n    def flush : Nil\n      @output.flush\n    end\n\n    # Closes this response, writing headers and body if not done yet.\n    # This method must be implemented if wrapping the response output.\n    def close : Nil\n      return if closed?\n\n      @output.close\n    end\n\n    # Returns `true` if this response has been closed.\n    def closed? : Bool\n      @output.closed?\n    end\n\n    # Sets the status message.\n    def status_message=(status_message : String?) : String\n      check_headers\n      @status_message = status_message\n    end\n\n    # Returns the status message.\n    #\n    # Defaults to description of `#status`.\n    def status_message : String?\n      @status_message || @status.description\n    end\n\n    # Sends *status* and *message* as response.\n    #\n    # This method calls `#reset` to remove any previous settings and writes the\n    # given *status* and *message* to the response IO. Finally, it closes the\n    # response.\n    #\n    # If *message* is `nil`, the default message for *status* is used provided\n    # by `HTTP::Status#description`.\n    #\n    # Raises `IO::Error` if the response is closed or headers were already\n    # sent.\n    def respond_with_status(status : HTTP::Status, message : String? = nil) : Nil\n      check_headers\n      reset\n      @status = status\n      @status_message = message ||= @status.description\n      self.content_type = \"text/plain\"\n      self << @status.code << ' ' << message << '\\n'\n      close\n    end\n\n    # :ditto:\n    def respond_with_status(status : Int, message : String? = nil) : Nil\n      respond_with_status(HTTP::Status.new(status), message)\n    end\n\n    # Sends a redirect to *location*.\n    #\n    # The value of *location* gets encoded with `URI.encode`.\n    #\n    # The *status* determines the HTTP status code which can be\n    # `HTTP::Status::FOUND` (`302`) for a temporary redirect or\n    # `HTTP::Status::MOVED_PERMANENTLY` (`301`) for a permanent redirect.\n    #\n    # The response gets closed.\n    #\n    # Raises `IO::Error` if the response is closed or headers were already\n    # sent.\n    def redirect(location : String | URI, status : HTTP::Status = :found) : Nil\n      check_headers\n\n      self.status = status\n      headers[\"Location\"] = if location.is_a? URI\n                              location.to_s\n                            else\n                              String.build do |io|\n                                URI.encode(location.to_s, io) do |byte|\n                                  URI.reserved?(byte) || URI.unreserved?(byte)\n                                end\n                              end\n                            end\n      close\n    end\n\n    private def check_headers\n      raise IO::Error.new \"Closed stream\" if @original_output.closed?\n      if wrote_headers?\n        raise IO::Error.new(\"Headers already sent\")\n      end\n    end\n\n    protected def write_headers\n      @io << @version << ' ' << @status.code << ' ' << status_message << \"\\r\\n\"\n      headers.each do |name, values|\n        values.each do |value|\n          @io << name << \": \" << value << \"\\r\\n\"\n        end\n      end\n      @io << \"\\r\\n\"\n      @wrote_headers = true\n    end\n\n    protected def wrote_headers?\n      @wrote_headers\n    end\n\n    protected def has_cookies?\n      !@cookies.nil?\n    end\n\n    # :nodoc:\n    class Output < IO\n      include IO::Buffered\n\n      property! response : Response\n\n      @chunked : Bool\n      @io : IO\n\n      def initialize(@io)\n        @chunked = false\n        @closed = false\n      end\n\n      def reset : Nil\n        @in_buffer_rem = Bytes.empty\n        @out_count = 0\n        @sync = false\n        @flush_on_newline = false\n        @chunked = false\n        @closed = false\n      end\n\n      private def unbuffered_read(slice : Bytes) : Int32\n        raise \"Can't read from HTTP::Server::Response\"\n      end\n\n      private def unbuffered_write(slice : Bytes) : Nil\n        return if slice.empty?\n\n        if response.headers[\"Transfer-Encoding\"]? == \"chunked\"\n          @chunked = true\n        elsif !response.wrote_headers?\n          if response.version != \"HTTP/1.0\" && !response.headers.has_key?(\"Content-Length\")\n            response.headers[\"Transfer-Encoding\"] = \"chunked\"\n            @chunked = true\n          end\n        end\n\n        ensure_headers_written\n\n        if @chunked\n          slice.size.to_s(@io, 16)\n          @io << \"\\r\\n\"\n          @io.write(slice)\n          @io << \"\\r\\n\"\n        else\n          @io.write(slice)\n        end\n      rescue ex : IO::Error\n        unbuffered_close\n        raise ClientError.new(\"Error while writing data to the client\", ex)\n      end\n\n      def closed? : Bool\n        @closed\n      end\n\n      def close : Nil\n        return if closed?\n\n        # Conditionally determine based on status if the `content-length` header should be added automatically.\n        # See https://tools.ietf.org/html/rfc7230#section-3.3.2.\n        status = response.status\n        set_content_length = !(status.not_modified? || status.no_content? || status.informational?)\n\n        if !response.wrote_headers? && !response.headers.has_key?(\"Transfer-Encoding\") && !response.headers.has_key?(\"Content-Length\") && set_content_length\n          response.content_length = @out_count\n        end\n\n        ensure_headers_written\n\n        super\n\n        if @chunked\n          @io << \"0\\r\\n\\r\\n\"\n          @io.flush\n        end\n      end\n\n      private def ensure_headers_written\n        unless response.wrote_headers?\n          if response.has_cookies?\n            response.cookies.add_response_headers(response.headers)\n          end\n\n          response.write_headers\n        end\n      end\n\n      private def unbuffered_close : Nil\n        @closed = true\n      end\n\n      private def unbuffered_rewind : Nil\n        raise \"Can't rewind to HTTP::Server::Response\"\n      end\n\n      private def unbuffered_flush : Nil\n        @io.flush\n      rescue ex : IO::Error\n        unbuffered_close\n        raise ClientError.new(\"Error while flushing data to the client\", ex)\n      end\n    end\n  end\n\n  class ClientError < Exception\n  end\nend\n"
  },
  {
    "path": "src/http/server.cr",
    "content": "require \"socket\"\nrequire \"uri\"\nrequire \"./server/context\"\nrequire \"./server/handler\"\nrequire \"./server/response\"\nrequire \"./server/request_processor\"\nrequire \"./common\"\nrequire \"log\"\n{% unless flag?(:without_openssl) %}\n  require \"openssl\"\n{% end %}\n\n# A concurrent HTTP server implementation.\n#\n# A server is initialized with a handler chain responsible for processing each\n# incoming request.\n#\n# NOTE: To use `Server`, you must explicitly import it with `require \"http/server\"`\n#\n# ```\n# require \"http/server\"\n#\n# server = HTTP::Server.new do |context|\n#   context.response.content_type = \"text/plain\"\n#   context.response.print \"Hello world!\"\n# end\n#\n# address = server.bind_tcp 8080\n# puts \"Listening on http://#{address}\"\n# server.listen\n# ```\n#\n# ## Request processing\n#\n# The handler chain receives an instance of `HTTP::Server::Context` that holds\n# the `HTTP::Request` to process and a `HTTP::Server::Response` which it can\n# configure and write to.\n#\n# Each connection is processed concurrently in a separate `Fiber` and can handle\n# multiple subsequent requests-response cycles with connection keep-alive.\n#\n# ### Handler chain\n#\n# The handler given to a server can simply be a block that receives an `HTTP::Server::Context`,\n# or it can be an instance of `HTTP::Handler`. An `HTTP::Handler` has a `#next`\n# method to forward processing to the next handler in the chain.\n#\n# For example, an initial handler might handle exceptions raised from subsequent\n# handlers and return a `500 Server Error` status (see `HTTP::ErrorHandler`).\n# The next handler might log all incoming requests (see `HTTP::LogHandler`).\n# And the final handler deals with routing and application logic.\n#\n# ```\n# require \"http/server\"\n#\n# server = HTTP::Server.new([\n#   HTTP::ErrorHandler.new,\n#   HTTP::LogHandler.new,\n#   HTTP::CompressHandler.new,\n#   HTTP::StaticFileHandler.new(\".\"),\n# ])\n#\n# server.bind_tcp \"127.0.0.1\", 8080\n# server.listen\n# ```\n#\n# ### Response object\n#\n# The `HTTP::Server::Response` object has `status` and `headers` properties that can be\n# configured before writing the response body. Once any response output has been\n# written, changing the `status` and `headers` properties has no effect.\n#\n# The `HTTP::Server::Response` is a write-only `IO`, so all `IO` methods are available\n# on it for sending the response body.\n#\n# ## Binding to sockets\n#\n# The server can be bound to one or more server sockets (see `#bind`)\n#\n# Supported types:\n#\n# * TCP socket: `#bind_tcp`, `#bind_unused_port`\n# * TCP socket with TLS/SSL: `#bind_tls`\n# * Unix socket `#bind_unix`\n#\n# `#bind(uri : URI)` and `#bind(uri : String)` parse socket configuration for\n# one of these types from an `URI`. This can be useful for injecting plain text\n# configuration values.\n#\n# Each of these methods returns the `Socket::Address` that was added to this\n# server.\n#\n# ```\n# require \"http/server\"\n#\n# server = HTTP::Server.new do |context|\n#   context.response.content_type = \"text/plain\"\n#   context.response.print \"Hello world!\"\n# end\n#\n# address = server.bind_tcp \"0.0.0.0\", 8080\n# puts \"Listening on http://#{address}\"\n# server.listen\n# ```\n#\n# It is also possible to bind a generic `Socket::Server` using\n# `#bind(socket : Socket::Server)` which can be used for custom network protocol\n# configurations.\n#\n# ## Server loop\n#\n# After defining all server sockets to listen to, the server can be started by\n# calling `#listen`. This call blocks until the server is closed.\n#\n# A server can be closed by calling `#close`. This closes the server sockets and\n# stops processing any new requests, even on connections with keep-alive enabled.\n# Currently processing requests are not interrupted but also not waited for.\n# In order to give them some grace period for finishing, the calling context\n# can add a timeout like `sleep 10.seconds` after `#listen` returns.\n#\n# ### Reusing connections\n#\n# The request processor supports reusing a connection for subsequent\n# requests. This is used by default for HTTP/1.1 or when requested by\n# the `Connection: keep-alive` header. This is signalled by this header being\n# set on the `HTTP::Server::Response` when it's passed into the handler chain.\n#\n# If in the handler chain this header is overridden to `Connection: close`, then\n# the connection will not be reused after the request has been processed.\n#\n# Reusing the connection also requires that the request body (if present) is\n# entirely consumed in the handler chain. Otherwise the connection will be closed.\nclass HTTP::Server\n  Log = ::Log.for(\"http.server\")\n\n  @sockets = [] of Socket::Server\n\n  # Returns `true` if this server is closed.\n  getter? closed : Bool = false\n\n  # Returns `true` if this server is listening on its sockets.\n  getter? listening : Bool = false\n\n  # Creates a new HTTP server with the given block as handler.\n  def self.new(&handler : HTTP::Handler::HandlerProc) : self\n    new(handler)\n  end\n\n  # Creates a new HTTP server with a handler chain constructed from the *handlers*\n  # array and the given block.\n  def self.new(handlers : Indexable(HTTP::Handler), &handler : HTTP::Handler::HandlerProc) : self\n    new(HTTP::Server.build_middleware(handlers, handler))\n  end\n\n  # Creates a new HTTP server with the *handlers* array as handler chain.\n  def self.new(handlers : Indexable(HTTP::Handler)) : self\n    new(HTTP::Server.build_middleware(handlers))\n  end\n\n  # Creates a new HTTP server with the given *handler*.\n  def initialize(handler : HTTP::Handler | HTTP::Handler::HandlerProc)\n    @processor = RequestProcessor.new(handler)\n  end\n\n  # Returns the maximum permitted size for the request line in an HTTP request.\n  #\n  # The request line is the first line of a request, consisting of method,\n  # resource and HTTP version and the delimiting line break.\n  # If the request line has a larger byte size than the permitted size,\n  # the server responds with the status code `414 URI Too Long` (see `HTTP::Status::URI_TOO_LONG`).\n  #\n  # Default: `HTTP::MAX_REQUEST_LINE_SIZE`\n  def max_request_line_size : Int32\n    @processor.max_request_line_size\n  end\n\n  # Sets the maximum permitted size for the request line in an HTTP request.\n  def max_request_line_size=(size : Int32) : Int32\n    @processor.max_request_line_size = size\n  end\n\n  # Returns the maximum permitted combined size for the headers in an HTTP request.\n  #\n  # When parsing a request, the server keeps track of the amount of total bytes\n  # consumed for all headers (including line breaks).\n  # If combined byte size of all headers is larger than the permitted size,\n  # the server responds with the status code `432 Request Header Fields Too Large`\n  # (see `HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE`).\n  #\n  # Default: `HTTP::MAX_HEADERS_SIZE`\n  def max_headers_size : Int32\n    @processor.max_headers_size\n  end\n\n  # Sets the maximum permitted combined size for the headers in an HTTP request.\n  def max_headers_size=(size : Int32) : Int32\n    @processor.max_headers_size = size\n  end\n\n  # Creates a `TCPServer` listening on `host:port` and adds it as a socket, returning the local address\n  # and port the server listens on.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind_tcp(\"127.0.0.100\", 8080) # => Socket::IPAddress.new(\"127.0.0.100\", 8080)\n  # ```\n  #\n  # If *reuse_port* is `true`, it enables the `SO_REUSEPORT` socket option,\n  # which allows multiple processes to bind to the same port.\n  def bind_tcp(host : String, port : Int32, reuse_port : Bool = false) : Socket::IPAddress\n    tcp_server = TCPServer.new(host, port, reuse_port: reuse_port)\n\n    begin\n      bind(tcp_server)\n    rescue exc\n      tcp_server.close\n      raise exc\n    end\n\n    tcp_server.local_address\n  end\n\n  # Creates a `TCPServer` listening on `127.0.0.1:port` and adds it as a socket,\n  # returning the local address and port the server listens on.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind_tcp(8080) # => Socket::IPAddress.new(\"127.0.0.1\", 8080)\n  # ```\n  #\n  # If *reuse_port* is `true`, it enables the `SO_REUSEPORT` socket option,\n  # which allows multiple processes to bind to the same port.\n  def bind_tcp(port : Int32, reuse_port : Bool = false) : Socket::IPAddress\n    bind_tcp Socket::IPAddress::LOOPBACK, port, reuse_port\n  end\n\n  # Creates a `TCPServer` listening on *address* and adds it as a socket, returning the local address\n  # and port the server listens on.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind_tcp(Socket::IPAddress.new(\"127.0.0.100\", 8080)) # => Socket::IPAddress.new(\"127.0.0.100\", 8080)\n  # server.bind_tcp(Socket::IPAddress.new(\"127.0.0.100\", 0))    # => Socket::IPAddress.new(\"127.0.0.100\", 35487)\n  # ```\n  #\n  # If *reuse_port* is `true`, it enables the `SO_REUSEPORT` socket option,\n  # which allows multiple processes to bind to the same port.\n  def bind_tcp(address : Socket::IPAddress, reuse_port : Bool = false) : Socket::IPAddress\n    bind_tcp(address.address, address.port, reuse_port: reuse_port)\n  end\n\n  # Creates a `TCPServer` listening on an unused port and adds it as a socket.\n  #\n  # Returns the `Socket::IPAddress` with the determined port number.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind_unused_port # => Socket::IPAddress.new(\"127.0.0.1\", 12345)\n  # ```\n  def bind_unused_port(host : String = Socket::IPAddress::LOOPBACK, reuse_port : Bool = false) : Socket::IPAddress\n    bind_tcp host, 0, reuse_port\n  end\n\n  # Creates a `UNIXServer` bound to *path* and adds it as a socket.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind_unix \"/tmp/my-socket.sock\"\n  # ```\n  def bind_unix(path : String) : Socket::UNIXAddress\n    server = UNIXServer.new(path)\n\n    begin\n      bind(server)\n    rescue exc\n      server.close\n      raise exc\n    end\n\n    server.local_address\n  end\n\n  # Creates a `UNIXServer` bound to *address* and adds it as a socket.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind_unix(Socket::UNIXAddress.new(\"/tmp/my-socket.sock\"))\n  # ```\n  def bind_unix(address : Socket::UNIXAddress) : Socket::UNIXAddress\n    bind_unix(address.path)\n  end\n\n  {% unless flag?(:without_openssl) %}\n    # Creates an `OpenSSL::SSL::Server` and adds it as a socket.\n    #\n    # The SSL server wraps a `TCPServer` listening on `host:port`.\n    #\n    # ```\n    # require \"http/server\"\n    #\n    # server = HTTP::Server.new { }\n    # context = OpenSSL::SSL::Context::Server.new\n    # context.certificate_chain = \"openssl.crt\"\n    # context.private_key = \"openssl.key\"\n    # server.bind_tls \"127.0.0.1\", 8080, context\n    # ```\n    def bind_tls(host : String, port : Int32, context : OpenSSL::SSL::Context::Server, reuse_port : Bool = false) : Socket::IPAddress\n      tcp_server = TCPServer.new(host, port, reuse_port: reuse_port)\n      server = OpenSSL::SSL::Server.new(tcp_server, context)\n      server.start_immediately = false\n\n      begin\n        bind(server)\n      rescue exc\n        server.close\n        raise exc\n      end\n\n      tcp_server.local_address\n    end\n\n    # Creates an `OpenSSL::SSL::Server` and adds it as a socket.\n    #\n    # The SSL server wraps a `TCPServer` listening on an unused port on *host*.\n    #\n    # ```\n    # require \"http/server\"\n    #\n    # server = HTTP::Server.new { }\n    # context = OpenSSL::SSL::Context::Server.new\n    # context.certificate_chain = \"openssl.crt\"\n    # context.private_key = \"openssl.key\"\n    # address = server.bind_tls \"127.0.0.1\", context\n    # ```\n    def bind_tls(host : String, context : OpenSSL::SSL::Context::Server) : Socket::IPAddress\n      bind_tls(host, 0, context)\n    end\n\n    # Creates an `OpenSSL::SSL::Server` and adds it as a socket.\n    #\n    # The SSL server wraps a `TCPServer` listening on an unused port on *host*.\n    #\n    # ```\n    # require \"http/server\"\n    #\n    # server = HTTP::Server.new { }\n    # context = OpenSSL::SSL::Context::Server.new\n    # context.certificate_chain = \"openssl.crt\"\n    # context.private_key = \"openssl.key\"\n    # address = server.bind_tls Socket::IPAddress.new(\"127.0.0.1\", 8000), context\n    # ```\n    def bind_tls(address : Socket::IPAddress, context : OpenSSL::SSL::Context::Server) : Socket::IPAddress\n      bind_tls(address.address, address.port, context)\n    end\n  {% end %}\n\n  # Parses a socket configuration from *uri* and adds it to this server.\n  # Returns the effective address it is bound to.\n  #\n  # ```\n  # require \"http/server\"\n  #\n  # server = HTTP::Server.new { }\n  # server.bind(\"tcp://localhost:80\")                                                  # => Socket::IPAddress.new(\"127.0.0.1\", 8080)\n  # server.bind(\"unix:///tmp/server.sock\")                                             # => Socket::UNIXAddress.new(\"/tmp/server.sock\")\n  # server.bind(\"tls://127.0.0.1:443?key=private.key&cert=certificate.cert&ca=ca.crt\") # => Socket::IPAddress.new(\"127.0.0.1\", 443)\n  # ```\n  def bind(uri : String) : Socket::Address\n    bind(URI.parse(uri))\n  end\n\n  # :ditto:\n  def bind(uri : URI) : Socket::Address\n    case uri.scheme\n    when \"tcp\"\n      bind_tcp(Socket::IPAddress.parse(uri))\n    when \"unix\"\n      bind_unix(Socket::UNIXAddress.parse(uri))\n    when \"tls\", \"ssl\"\n      address = Socket::IPAddress.parse(uri)\n      {% unless flag?(:without_openssl) %}\n        context = OpenSSL::SSL::Context::Server.from_hash(uri.query_params)\n\n        bind_tls(address, context)\n      {% else %}\n        raise ArgumentError.new \"Unsupported socket type: #{uri.scheme} (program was compiled without openssl support)\"\n      {% end %}\n    else\n      raise ArgumentError.new \"Unsupported socket type: #{uri.scheme}\"\n    end\n  end\n\n  # Adds a `Socket::Server` *socket* to this server.\n  def bind(socket : Socket::Server) : Nil\n    raise \"Can't add socket to running server\" if listening?\n    raise \"Can't add socket to closed server\" if closed?\n\n    @sockets << socket\n  end\n\n  # Enumerates all addresses this server is bound to.\n  def each_address(&block : Socket::Address ->)\n    @sockets.each do |socket|\n      yield socket.local_address\n    end\n  end\n\n  def addresses : Array(Socket::Address)\n    array = [] of Socket::Address\n    each_address do |address|\n      array << address\n    end\n    array\n  end\n\n  # Creates a `TCPServer` listening on `127.0.0.1:port`, adds it as a socket\n  # and starts the server. Blocks until the server is closed.\n  #\n  # See `#bind(port : Int32)` for details.\n  def listen(port : Int32, reuse_port : Bool = false)\n    bind_tcp(port, reuse_port)\n\n    listen\n  end\n\n  # Creates a `TCPServer` listening on `host:port`, adds it as a socket\n  # and starts the server. Blocks until the server is closed.\n  #\n  # See `#bind(host : String, port : Int32)` for details.\n  def listen(host : String, port : Int32, reuse_port : Bool = false)\n    bind_tcp(host, port, reuse_port)\n\n    listen\n  end\n\n  # Overwrite this method to implement an alternative concurrency handler\n  # one example could be the use of a fiber pool\n  protected def dispatch(io)\n    spawn handle_client(io)\n  end\n\n  # Starts the server. Blocks until the server is closed.\n  def listen : Nil\n    raise \"Can't re-start closed server\" if closed?\n    raise \"Can't start server with no sockets to listen to, use HTTP::Server#bind first\" if @sockets.empty?\n    raise \"Can't start running server\" if listening?\n\n    @listening = true\n    done = Channel(Nil).new\n\n    @sockets.each do |socket|\n      spawn do\n        loop do\n          io = begin\n            socket.accept?\n          rescue e\n            handle_exception(e)\n            next\n          end\n\n          if io\n            dispatch(io)\n          else\n            break\n          end\n        end\n      ensure\n        done.send nil\n      end\n    end\n\n    @sockets.size.times { done.receive }\n  end\n\n  # Gracefully terminates the server. It will process currently accepted\n  # requests, but it won't accept new connections.\n  def close : Nil\n    raise \"Can't close server, it's already closed\" if closed?\n\n    @closed = true\n    @processor.close\n\n    @sockets.each do |socket|\n      socket.close\n    rescue\n      # ignore exception on close\n    end\n\n    @listening = false\n    @sockets.clear\n  end\n\n  private def handle_client(io : IO)\n    if io.is_a?(IO::Buffered)\n      io.sync = false\n    end\n\n    {% unless flag?(:without_openssl) %}\n      if io.is_a?(OpenSSL::SSL::Socket::Server)\n        begin\n          io.accept\n        rescue ex\n          Log.debug(exception: ex) { \"Error during SSL handshake\" }\n          return\n        end\n      end\n    {% end %}\n\n    @processor.process(io, io)\n  ensure\n    {% begin %}\n      begin\n        io.close\n      rescue IO::Error{% unless flag?(:without_openssl) %} | OpenSSL::SSL::Error{% end %}\n      end\n    {% end %}\n  end\n\n  # This method handles exceptions raised at `Socket#accept?`.\n  private def handle_exception(e : Exception)\n    # TODO: This needs more refinement. Not every exception is an actual server\n    # error and should be logged as such. Client malfunction should only be informational.\n    # See https://github.com/crystal-lang/crystal/pull/9034#discussion_r407038999\n    Log.error(exception: e) { \"Error while connecting a new socket\" }\n  end\n\n  # Builds all handlers as the middleware for `HTTP::Server`.\n  def self.build_middleware(handlers : Indexable(HTTP::Handler), last_handler : (Context ->)? = nil) : HTTP::Handler\n    raise ArgumentError.new \"You must specify at least one HTTP Handler.\" if handlers.empty?\n    0.upto(handlers.size - 2) { |i| handlers[i].next = handlers[i + 1] }\n    handlers.last.next = last_handler if last_handler\n    handlers.first\n  end\nend\n"
  },
  {
    "path": "src/http/status.cr",
    "content": "# An enum that provides additional support around HTTP status codes.\n#\n# Based on [Hypertext Transfer Protocol (HTTP) Status Code Registry](https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml)\n#\n# It provides constants for the defined HTTP status codes as well as helper\n# methods to easily identify the type of response.\n#\n# NOTE: To use `Status`, you must explicitly import it with `require \"http/status\"`\nenum HTTP::Status\n  CONTINUE                        = 100\n  SWITCHING_PROTOCOLS             = 101\n  PROCESSING                      = 102\n  EARLY_HINTS                     = 103\n  OK                              = 200\n  CREATED                         = 201\n  ACCEPTED                        = 202\n  NON_AUTHORITATIVE_INFORMATION   = 203\n  NO_CONTENT                      = 204\n  RESET_CONTENT                   = 205\n  PARTIAL_CONTENT                 = 206\n  MULTI_STATUS                    = 207\n  ALREADY_REPORTED                = 208\n  IM_USED                         = 226\n  MULTIPLE_CHOICES                = 300\n  MOVED_PERMANENTLY               = 301\n  FOUND                           = 302\n  SEE_OTHER                       = 303\n  NOT_MODIFIED                    = 304\n  USE_PROXY                       = 305\n  SWITCH_PROXY                    = 306\n  TEMPORARY_REDIRECT              = 307\n  PERMANENT_REDIRECT              = 308\n  BAD_REQUEST                     = 400\n  UNAUTHORIZED                    = 401\n  PAYMENT_REQUIRED                = 402\n  FORBIDDEN                       = 403\n  NOT_FOUND                       = 404\n  METHOD_NOT_ALLOWED              = 405\n  NOT_ACCEPTABLE                  = 406\n  PROXY_AUTHENTICATION_REQUIRED   = 407\n  REQUEST_TIMEOUT                 = 408\n  CONFLICT                        = 409\n  GONE                            = 410\n  LENGTH_REQUIRED                 = 411\n  PRECONDITION_FAILED             = 412\n  PAYLOAD_TOO_LARGE               = 413\n  URI_TOO_LONG                    = 414\n  UNSUPPORTED_MEDIA_TYPE          = 415\n  RANGE_NOT_SATISFIABLE           = 416\n  EXPECTATION_FAILED              = 417\n  IM_A_TEAPOT                     = 418\n  MISDIRECTED_REQUEST             = 421\n  UNPROCESSABLE_ENTITY            = 422\n  LOCKED                          = 423\n  FAILED_DEPENDENCY               = 424\n  UPGRADE_REQUIRED                = 426\n  PRECONDITION_REQUIRED           = 428\n  TOO_MANY_REQUESTS               = 429\n  REQUEST_HEADER_FIELDS_TOO_LARGE = 431\n  UNAVAILABLE_FOR_LEGAL_REASONS   = 451\n  INTERNAL_SERVER_ERROR           = 500\n  NOT_IMPLEMENTED                 = 501\n  BAD_GATEWAY                     = 502\n  SERVICE_UNAVAILABLE             = 503\n  GATEWAY_TIMEOUT                 = 504\n  HTTP_VERSION_NOT_SUPPORTED      = 505\n  VARIANT_ALSO_NEGOTIATES         = 506\n  INSUFFICIENT_STORAGE            = 507\n  LOOP_DETECTED                   = 508\n  NOT_EXTENDED                    = 510\n  NETWORK_AUTHENTICATION_REQUIRED = 511\n\n  # Create a new status instance with the given status code, or raise an\n  # error if the status code given is not inside 100..999.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status.new(100)  # => HTTP::Status::CONTINUE\n  # HTTP::Status.new(202)  # => HTTP::Status::ACCEPTED\n  # HTTP::Status.new(123)  # => 123\n  # HTTP::Status.new(1000) # raises ArgumentError\n  # ```\n  def self.new(status_code : Int32) : self\n    raise ArgumentError.new(\"Invalid HTTP status code: #{status_code}\") unless 100 <= status_code <= 999\n    previous_def(status_code)\n  end\n\n  # Returns the number that represents the HTTP status code.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # status = HTTP::Status::NO_CONTENT\n  # status.code # => 204\n  # ```\n  def code : Int32\n    value\n  end\n\n  # Returns `true` if the response status code is between 100 and 199.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status::SWITCHING_PROTOCOLS.informational?   # => true\n  # HTTP::Status::INTERNAL_SERVER_ERROR.informational? # => false\n  # ```\n  def informational? : Bool\n    100 <= code <= 199\n  end\n\n  # Returns `true` if the response status code is between 200 and 299.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status::NO_CONTENT.success?            # => true\n  # HTTP::Status::INTERNAL_SERVER_ERROR.success? # => false\n  # ```\n  def success? : Bool\n    200 <= code <= 299\n  end\n\n  # Returns `true` if the response status code is between 300 and 399.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status::SWITCH_PROXY.redirection?          # => true\n  # HTTP::Status::INTERNAL_SERVER_ERROR.redirection? # => false\n  # ```\n  def redirection? : Bool\n    300 <= code <= 399\n  end\n\n  # Returns `true` if the response status code is between 400 and 499.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status::METHOD_NOT_ALLOWED.client_error?    # => true\n  # HTTP::Status::INTERNAL_SERVER_ERROR.client_error? # => false\n  # ```\n  def client_error? : Bool\n    400 <= code <= 499\n  end\n\n  # Returns `true` if the response status code is between 500 and 599.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status::INTERNAL_SERVER_ERROR.server_error? # => true\n  # HTTP::Status::METHOD_NOT_ALLOWED.server_error?    # => false\n  # ```\n  def server_error? : Bool\n    500 <= code <= 599\n  end\n\n  # Returns the default status description of the given HTTP status code.\n  #\n  # ```\n  # require \"http/status\"\n  #\n  # HTTP::Status.new(123).description               # => nil\n  # HTTP::Status::NO_CONTENT.description            # => \"No Content\"\n  # HTTP::Status::METHOD_NOT_ALLOWED.description    # => \"Method Not Allowed\"\n  # HTTP::Status::INTERNAL_SERVER_ERROR.description # => \"Internal Server Error\"\n  # ```\n  def description : String?\n    case code\n    when 100 then \"Continue\"\n    when 101 then \"Switching Protocols\"\n    when 102 then \"Processing\"\n    when 200 then \"OK\"\n    when 201 then \"Created\"\n    when 202 then \"Accepted\"\n    when 203 then \"Non-Authoritative Information\"\n    when 204 then \"No Content\"\n    when 205 then \"Reset Content\"\n    when 206 then \"Partial Content\"\n    when 207 then \"Multi-Status\"\n    when 208 then \"Already Reported\"\n    when 226 then \"IM Used\"\n    when 300 then \"Multiple Choices\"\n    when 301 then \"Moved Permanently\"\n    when 302 then \"Found\"\n    when 303 then \"See Other\"\n    when 304 then \"Not Modified\"\n    when 305 then \"Use Proxy\"\n    when 306 then \"Switch Proxy\"\n    when 307 then \"Temporary Redirect\"\n    when 308 then \"Permanent Redirect\"\n    when 400 then \"Bad Request\"\n    when 401 then \"Unauthorized\"\n    when 402 then \"Payment Required\"\n    when 403 then \"Forbidden\"\n    when 404 then \"Not Found\"\n    when 405 then \"Method Not Allowed\"\n    when 406 then \"Not Acceptable\"\n    when 407 then \"Proxy Authentication Required\"\n    when 408 then \"Request Timeout\"\n    when 409 then \"Conflict\"\n    when 410 then \"Gone\"\n    when 411 then \"Length Required\"\n    when 412 then \"Precondition Failed\"\n    when 413 then \"Payload Too Large\"\n    when 414 then \"URI Too Long\"\n    when 415 then \"Unsupported Media Type\"\n    when 416 then \"Range Not Satisfiable\"\n    when 417 then \"Expectation Failed\"\n    when 418 then \"I'm a teapot\"\n    when 421 then \"Misdirected Request\"\n    when 422 then \"Unprocessable Entity\"\n    when 423 then \"Locked\"\n    when 424 then \"Failed Dependency\"\n    when 426 then \"Upgrade Required\"\n    when 428 then \"Precondition Required\"\n    when 429 then \"Too Many Requests\"\n    when 431 then \"Request Header Fields Too Large\"\n    when 451 then \"Unavailable For Legal Reasons\"\n    when 500 then \"Internal Server Error\"\n    when 501 then \"Not Implemented\"\n    when 502 then \"Bad Gateway\"\n    when 503 then \"Service Unavailable\"\n    when 504 then \"Gateway Timeout\"\n    when 505 then \"HTTP Version Not Supported\"\n    when 506 then \"Variant Also Negotiates\"\n    when 507 then \"Insufficient Storage\"\n    when 508 then \"Loop Detected\"\n    when 510 then \"Not Extended\"\n    when 511 then \"Network Authentication Required\"\n    else          nil\n    end\n  end\nend\n"
  },
  {
    "path": "src/http/web_socket/close_code.cr",
    "content": "enum HTTP::WebSocket::CloseCode\n  NormalClosure           = 1000\n  GoingAway               = 1001\n  ProtocolError           = 1002\n  UnsupportedData         = 1003\n  NoStatusReceived        = 1005\n  AbnormalClosure         = 1006\n  InvalidFramePayloadData = 1007\n  PolicyViolation         = 1008\n  MessageTooBig           = 1009\n  MandatoryExtension      = 1010\n  InternalServerError     = 1011\n  ServiceRestart          = 1012\n  TryAgainLater           = 1013\n  BadGateway              = 1014\n  TLSHandshake            = 1015\n\n  # Create a new instance with the given close code, or raise an\n  # error if the close code given is not inside 0..4999.\n  def self.new(close_code : Int32) : self\n    unless 0 <= close_code <= 4999\n      raise ArgumentError.new(\"Invalid HTTP::WebSocket::CloseCode: #{close_code}\")\n    end\n    previous_def(close_code)\n  end\nend\n"
  },
  {
    "path": "src/http/web_socket/protocol.cr",
    "content": "require \"socket\"\nrequire \"../client\"\nrequire \"../headers\"\nrequire \"base64\"\n{% if flag?(:without_openssl) %}\n  require \"crystal/digest/sha1\"\n{% else %}\n  require \"openssl/sha1\"\n{% end %}\nrequire \"uri\"\n\n# :nodoc:\nclass HTTP::WebSocket::Protocol\n  GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n  @[::Flags]\n  enum Flags : UInt8\n    FINAL = 0x80\n    RSV1  = 0x40\n    RSV2  = 0x20\n    RSV3  = 0x10\n  end\n\n  enum Opcode : UInt8\n    CONTINUATION = 0x0\n    TEXT         = 0x1\n    BINARY       = 0x2\n    CLOSE        = 0x8\n    PING         = 0x9\n    PONG         = 0xA\n  end\n\n  MASK_BIT = 128_u8\n  VERSION  = \"13\"\n\n  record PacketInfo,\n    opcode : Opcode,\n    size : Int32,\n    final : Bool\n\n  getter protocol : String?\n\n  def initialize(@io : IO, masked = false, @sync_close = true, @protocol : String? = nil)\n    @header = uninitialized UInt8[2]\n    @mask = uninitialized UInt8[4]\n    @mask_offset = 0\n    @opcode = Opcode::CONTINUATION\n    @remaining = 0_u64\n    @masked = !!masked\n  end\n\n  class StreamIO < IO\n    getter? closed = false\n\n    def initialize(@websocket : Protocol, binary, frame_size)\n      @opcode = binary ? Opcode::BINARY : Opcode::TEXT\n      @buffer = Bytes.new(frame_size)\n      @pos = 0\n    end\n\n    def write(slice : Bytes) : Nil\n      check_open\n      return if slice.empty?\n\n      count = Math.min(@buffer.size - @pos, slice.size)\n      (@buffer + @pos).copy_from(slice.to_unsafe, count)\n      @pos += count\n\n      if @pos == @buffer.size\n        send_frame(final: false)\n      end\n\n      if count < slice.size\n        write(slice + count)\n      end\n    end\n\n    def read(slice : Bytes) : NoReturn\n      raise \"This IO is write-only\"\n    end\n\n    private def send_frame(final = false) : Nil\n      @websocket.send(\n        @buffer[0...@pos],\n        @opcode,\n        flags: final ? Flags::FINAL : Flags::None,\n        flush: final\n      )\n      @opcode = Opcode::CONTINUATION\n      @pos = 0\n    end\n\n    def close\n      return if closed?\n      @closed = true\n      send_frame(final: true)\n    end\n  end\n\n  def send(data : String) : Nil\n    send(data.to_slice, Opcode::TEXT)\n  end\n\n  def send(data : Bytes) : Nil\n    send(data, Opcode::BINARY)\n  end\n\n  def stream(binary = true, frame_size = 1024, &)\n    stream_io = StreamIO.new(self, binary, frame_size)\n    yield(stream_io)\n    stream_io.close\n  end\n\n  def send(data : Bytes, opcode : Opcode, flags : Flags = Flags::FINAL, flush : Bool = true) : Nil\n    write_header(data.size, opcode, flags)\n    write_payload(data)\n    @io.flush if flush\n  end\n\n  def receive(buffer : Bytes) : PacketInfo\n    if @remaining == 0\n      opcode = read_header\n    else\n      opcode = @opcode\n    end\n\n    read = read_payload(buffer)\n    @remaining -= read\n    PacketInfo.new(opcode, read.to_i, final? && @remaining == 0)\n  end\n\n  private def write_header(size, opcode, flags)\n    @io.write_byte(flags.value | opcode.value)\n\n    mask = @masked ? MASK_BIT : 0_u8\n    if size <= 125\n      @io.write_byte(size.to_u8 | mask)\n    elsif size <= UInt16::MAX\n      @io.write_byte(126_u8 | mask)\n      @io.write_bytes(size.to_u16, IO::ByteFormat::NetworkEndian)\n    else\n      @io.write_byte(127_u8 | mask)\n      @io.write_bytes(size.to_u64, IO::ByteFormat::NetworkEndian)\n    end\n  end\n\n  private def write_payload(data)\n    return @io.write(data) unless @masked\n\n    key = Random.next_int\n    mask_array = key.unsafe_as(StaticArray(UInt8, 4))\n    @io.write mask_array.to_slice\n\n    write_masked_data(data, mask_array)\n  end\n\n  private def write_masked_data(data, mask_array)\n    # We are going to write the data, masked, into a temporary buffer.\n    masked_data = uninitialized UInt8[IO::DEFAULT_BUFFER_SIZE]\n\n    # We'll do it by chunks of at most IO::DEFAULT_BUFFER_SIZE\n    remaining_data = data\n    until remaining_data.empty?\n      # How much data can we write?\n      # Either IO::DEFAULT_BUFFER_SIZE or whatever remains.\n      chunk_size = Math.min(remaining_data.size, IO::DEFAULT_BUFFER_SIZE)\n\n      # Mask the data\n      chunk = remaining_data[0, chunk_size]\n      chunk.each_with_index do |byte, index|\n        mask = mask_array[index & 0b11] # x & 0b11 == x % 4\n        masked_data[index] = byte ^ mask\n      end\n\n      # Write the masked data\n      @io.write(masked_data.to_slice[0, chunk_size])\n\n      # Discard the written data\n      remaining_data = remaining_data[chunk_size..]\n    end\n  end\n\n  private def read_header\n    # First byte: FIN (1 bit), RSV1,2,3 (3 bits), Opcode (4 bits)\n    # Second byte: MASK (1 bit), Payload Length (7 bits)\n    @io.read_fully(@header.to_slice)\n\n    opcode = read_opcode\n    @remaining = read_size\n\n    # Read mask, if needed\n    if masked?\n      @io.read_fully(@mask.to_slice)\n      @mask_offset = 0\n    end\n\n    opcode\n  end\n\n  private def read_opcode\n    raw_opcode = @header[0] & 0x0f_u8\n\n    parsed_opcode = Opcode.from_value?(raw_opcode)\n    unless parsed_opcode\n      raise \"Invalid packet opcode: #{raw_opcode}\"\n    end\n\n    if parsed_opcode == Opcode::CONTINUATION\n      @opcode\n    elsif control?\n      parsed_opcode\n    else\n      @opcode = parsed_opcode\n    end\n  end\n\n  private def read_size\n    size = (@header[1] & 0x7f_u8).to_u64\n    case size\n    when 126\n      size = 0_u64\n      2.times { size <<= 8; size += @io.read_byte.not_nil! }\n    when 127\n      size = 0_u64\n      8.times { size <<= 8; size += @io.read_byte.not_nil! }\n    else\n      # not a special case\n    end\n    size\n  end\n\n  private def read_payload(buffer)\n    count = Math.min(@remaining, buffer.size)\n    @io.read_fully(buffer[0, count])\n    if masked?\n      count.times do |i|\n        buffer[i] ^= @mask[@mask_offset & 0b11] # x & 0b11 == x % 4\n        @mask_offset += 1\n      end\n    end\n    count\n  end\n\n  private def control?\n    (@header[0] & 0x08_u8) != 0_u8\n  end\n\n  private def final?\n    (@header[0] & 0x80_u8) != 0_u8\n  end\n\n  private def masked?\n    (@header[1] & 0x80_u8) != 0_u8\n  end\n\n  def ping(message = nil)\n    if message\n      send(message.to_slice, Opcode::PING)\n    else\n      send(Bytes.empty, Opcode::PING)\n    end\n  end\n\n  def pong(message = nil) : Nil\n    if message\n      send(message.to_slice, Opcode::PONG)\n    else\n      send(Bytes.empty, Opcode::PONG)\n    end\n  end\n\n  def close(code : CloseCode? = nil, message = nil) : Nil\n    return if @io.closed?\n\n    if message\n      message = message.to_slice\n      code ||= CloseCode::NormalClosure\n\n      payload = Bytes.new(2 + message.size)\n      IO::ByteFormat::NetworkEndian.encode(code.to_u16, payload)\n      message.copy_to(payload + 2)\n    else\n      if code\n        payload = Bytes.new(2)\n        IO::ByteFormat::NetworkEndian.encode(code.to_u16, payload)\n      else\n        payload = Bytes.empty\n      end\n    end\n\n    send(payload, Opcode::CLOSE)\n\n    @io.close if @sync_close\n  end\n\n  def close(code : Int, message : String? = nil) : Nil\n    close(CloseCode.new(code), message)\n  end\n\n  def self.new(host : String, path : String, port : Int32? = nil, tls : HTTP::Client::TLSContext = nil, headers : HTTP::Headers = HTTP::Headers.new, protocols : Enumerable(String)? = nil) : self\n    {% if flag?(:without_openssl) %}\n      if tls\n        raise \"WebSocket TLS is disabled because `-D without_openssl` was passed at compile time\"\n      end\n    {% end %}\n\n    port ||= tls ? 443 : 80\n\n    socket = TCPSocket.new(host, port)\n    accepted_protocol = nil\n    begin\n      {% if !flag?(:without_openssl) %}\n        if tls\n          if tls.is_a?(Bool) # true, but we want to get rid of the union\n            context = OpenSSL::SSL::Context::Client.new\n          else\n            context = tls\n          end\n          socket = OpenSSL::SSL::Socket::Client.new(socket, context: context, sync_close: true, hostname: host)\n        end\n      {% end %}\n\n      random_key = Base64.strict_encode(StaticArray(UInt8, 16).new { rand(256).to_u8 })\n\n      headers[\"Host\"] = \"#{host}:#{port}\"\n      headers[\"Connection\"] = \"Upgrade\"\n      headers[\"Upgrade\"] = \"websocket\"\n      headers[\"Sec-WebSocket-Version\"] = VERSION\n      headers[\"Sec-WebSocket-Key\"] = random_key\n      headers[\"Sec-WebSocket-Protocol\"] = protocols.join(\",\") if protocols && !protocols.empty?\n\n      path = \"/\" if path.empty?\n      handshake = HTTP::Request.new(\"GET\", path, headers)\n      handshake.to_io(socket)\n      socket.flush\n\n      handshake_response = HTTP::Client::Response.from_io(socket, ignore_body: true)\n      unless handshake_response.status.switching_protocols?\n        raise Socket::Error.new(\"Handshake got denied. Status code was #{handshake_response.status.code}.\")\n      end\n\n      challenge_response = Protocol.key_challenge(random_key)\n      unless handshake_response.headers[\"Sec-WebSocket-Accept\"]? == challenge_response\n        raise Socket::Error.new(\"Handshake got denied. Server did not verify WebSocket challenge.\")\n      end\n\n      if protocols && !protocols.empty?\n        if server_protocol = handshake_response.headers[\"Sec-WebSocket-Protocol\"]?\n          unless protocols.includes?(server_protocol)\n            raise Socket::Error.new(\"Handshake got denied. Server responded with an invalid Sec-WebSocket-Protocol.\")\n          end\n          accepted_protocol = server_protocol\n        else\n          raise Socket::Error.new(\"Handshake got denied. Server did not respond with Sec-WebSocket-Protocol.\")\n        end\n      end\n    rescue exc\n      socket.close\n      raise exc\n    end\n\n    new(socket, masked: true, protocol: accepted_protocol)\n  end\n\n  def self.new(uri : URI | String, headers : HTTP::Headers = HTTP::Headers.new) : self\n    uri = URI.parse(uri) if uri.is_a?(String)\n\n    if (host = uri.hostname) && (path = uri.request_target)\n      tls = uri.scheme.in?(\"https\", \"wss\")\n      if (user = uri.user) && (password = uri.password)\n        headers[\"Authorization\"] ||= \"Basic #{Base64.strict_encode(\"#{user}:#{password}\")}\"\n      end\n      return new(host, path, uri.port, tls, headers)\n    end\n\n    raise ArgumentError.new(\"No host or path specified which are required.\")\n  end\n\n  def self.key_challenge(key : String) : String\n    {% if flag?(:without_openssl) %}\n      ::Crystal::Digest::SHA1.base64digest(key + GUID)\n    {% else %}\n      Base64.strict_encode(OpenSSL::SHA1.hash(key + GUID))\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/http/web_socket.cr",
    "content": "require \"./client\"\nrequire \"./headers\"\n\n# NOTE: To use `WebSocket`, you must explicitly import it with `require \"http/web_socket\"`\nclass HTTP::WebSocket\n  getter? closed = false\n\n  # :nodoc:\n  def initialize(io : IO, sync_close = true)\n    initialize(Protocol.new(io, sync_close: sync_close))\n  end\n\n  # :nodoc:\n  def initialize(@ws : Protocol)\n    @buffer = Bytes.new(4096)\n    @current_message = IO::Memory.new\n  end\n\n  # Opens a new websocket using the information provided by the URI. This will also handle the handshake\n  # and will raise an exception if the handshake did not complete successfully. This method will also raise\n  # an exception if the URI is missing the host and/or the path.\n  #\n  # Please note that the scheme will only be used to identify if TLS should be used or not. Therefore, schemes\n  # apart from `wss` and `https` will be treated as the default which is `ws`.\n  #\n  # ```\n  # require \"http/web_socket\"\n  #\n  # HTTP::WebSocket.new(URI.parse(\"ws://websocket.example.com/chat\"))        # Creates a new WebSocket to `websocket.example.com`\n  # HTTP::WebSocket.new(URI.parse(\"wss://websocket.example.com/chat\"))       # Creates a new WebSocket with TLS to `websocket.example.com`\n  # HTTP::WebSocket.new(URI.parse(\"http://websocket.example.com:8080/chat\")) # Creates a new WebSocket to `websocket.example.com` on port `8080`\n  # HTTP::WebSocket.new(URI.parse(\"ws://websocket.example.com/chat\"),        # Creates a new WebSocket to `websocket.example.com` with an Authorization header\n  #   HTTP::Headers{\"Authorization\" => \"Bearer authtoken\"})\n  # HTTP::WebSocket.new(\n  #   URI.parse(\"ws://user:password@websocket.example.com/chat\")) # Creates a new WebSocket to `websocket.example.com` with an HTTP basic auth Authorization header\n  # ```\n  def self.new(uri : URI | String, headers : HTTP::Headers = HTTP::Headers.new) : self\n    new(Protocol.new(uri, headers: headers))\n  end\n\n  # Opens a new websocket to the target host. This will also handle the handshake\n  # and will raise an exception if the handshake did not complete successfully.\n  #\n  # ```\n  # require \"http/web_socket\"\n  #\n  # HTTP::WebSocket.new(\"websocket.example.com\", \"/chat\")            # Creates a new WebSocket to `websocket.example.com`\n  # HTTP::WebSocket.new(\"websocket.example.com\", \"/chat\", tls: true) # Creates a new WebSocket with TLS to `ẁebsocket.example.com`\n  # ```\n  def self.new(host : String, path : String, port : Int32? = nil, tls : HTTP::Client::TLSContext = nil, headers : HTTP::Headers = HTTP::Headers.new) : self\n    new(Protocol.new(host, path, port, tls, headers))\n  end\n\n  # Called when a PING frame is received.\n  def on_ping(&@on_ping : String ->)\n  end\n\n  # Called when a PONG frame is received.\n  #\n  # An unsolicited PONG frame should not be responded to.\n  def on_pong(&@on_pong : String ->)\n  end\n\n  # Called when a text message is received.\n  def on_message(&@on_message : String ->) : Proc(String, Nil)\n  end\n\n  # Called when a binary message is received.\n  def on_binary(&@on_binary : Bytes ->) : Proc(Bytes, Nil)\n  end\n\n  # Called when the connection is closed by the other party.\n  def on_close(&@on_close : CloseCode, String ->) : Proc(HTTP::WebSocket::CloseCode, String, Nil)\n  end\n\n  protected def check_open\n    raise IO::Error.new \"Closed socket\" if closed?\n  end\n\n  # Sends a message payload (message).\n  def send(message) : Nil\n    check_open\n    @ws.send(message)\n  end\n\n  # Sends a PING frame. Received pings will call `#on_ping`.\n  #\n  # The receiving party must respond with a PONG.\n  def ping(message = nil)\n    check_open\n    @ws.ping(message)\n  end\n\n  # Sends a PONG frame, which must be in response to a previously received PING frame from `#on_ping`.\n  def pong(message = nil) : Nil\n    check_open\n    @ws.pong(message)\n  end\n\n  # Stream data as one message with automatically fragmentation handling\n  # with respect to given `frame_size`.\n  # When the io is closed, current data in the buffer is sent in a FIN frame.\n  # The io is closed when the block returns.\n  #\n  # The method accepts a block with an `io` argument.\n  # The io object can call on `IO#write` method.\n  # The `write` method accepts `Bytes` (`Slice(UInt8)`) and sends the data in chunks of *frame_size* bytes.\n  # For further information, see the `HTTP::WebSocket::Protocol::StreamIO` class.\n  #\n  # ```\n  # # Open websocket connection\n  # ws = HTTP::WebSocket.new(\"websocket.example.com\", \"/chat\")\n  #\n  # # Open stream\n  # ws.stream(false, frame_size: 4) do |io|\n  #   io.write \"foo\".encode(\"UTF-8\") # Nothing is sent, buffer not full\n  #   io.write \"bar\".encode(\"UTF-8\") # Will send a first frame with \"foob\"\n  # end                              # io is closed, a FIN frame with \"bar\" is sent\n  # ```\n  def stream(binary = true, frame_size = 1024, &)\n    check_open\n    @ws.stream(binary: binary, frame_size: frame_size) do |io|\n      yield io\n    end\n  end\n\n  # Sends a close frame, and closes the connection.\n  # The close frame may contain a body (message) that indicates the reason for closing.\n  def close(code : CloseCode | Int? = nil, message = nil) : Nil\n    return if closed?\n    @closed = true\n    @ws.close(code, message)\n  end\n\n  # Continuously receives messages and calls previously set callbacks until the websocket is closed.\n  # Ping and pong messages are automatically handled.\n  #\n  # ```\n  # # Open websocket connection\n  # ws = HTTP::WebSocket.new(\"websocket.example.com\", \"/chat\")\n  #\n  # # Set callback\n  # ws.on_message do |msg|\n  #   ws.send \"response\"\n  # end\n  #\n  # # Start infinite loop\n  # ws.run\n  # ```\n  def run : Nil\n    loop do\n      begin\n        info = @ws.receive(@buffer)\n      rescue\n        @on_close.try &.call(CloseCode::AbnormalClosure, \"\")\n        @closed = true\n        break\n      end\n\n      case info.opcode\n      in .ping?\n        @current_message.write @buffer[0, info.size]\n        if info.final\n          message = @current_message.to_s\n          do_ping(message)\n          @current_message.clear\n        end\n      in .pong?\n        @current_message.write @buffer[0, info.size]\n        if info.final\n          @on_pong.try &.call(@current_message.to_s)\n          @current_message.clear\n        end\n      in .text?\n        @current_message.write @buffer[0, info.size]\n        if info.final\n          @on_message.try &.call(@current_message.to_s)\n          @current_message.clear\n        end\n      in .binary?\n        @current_message.write @buffer[0, info.size]\n        if info.final\n          @on_binary.try &.call(@current_message.to_slice)\n          @current_message.clear\n        end\n      in .close?\n        @current_message.write @buffer[0, info.size]\n        if info.final\n          @current_message.rewind\n\n          if @current_message.size >= 2\n            code = @current_message.read_bytes(UInt16, IO::ByteFormat::NetworkEndian).to_i\n            code = CloseCode.new(code)\n          else\n            code = CloseCode::NoStatusReceived\n          end\n          message = @current_message.gets_to_end\n\n          do_close(code, message)\n\n          @current_message.clear\n          break\n        end\n      in .continuation?\n        # TODO: (asterite) I think this is good, but this case wasn't originally handled\n      end\n    end\n  end\n\n  private def do_close(code, message)\n    @on_close.try &.call(code, message)\n    close\n  end\n\n  private def do_ping(message)\n    @on_ping.try &.call(message)\n    pong(message) unless closed?\n  end\nend\n\nrequire \"./web_socket/*\"\n"
  },
  {
    "path": "src/http.cr",
    "content": "require \"uri\"\nrequire \"./http/client\"\nrequire \"./http/server\"\nrequire \"./http/log\"\nrequire \"./http/common\"\n\n# The HTTP module contains `HTTP::Client`, `HTTP::Server` and `HTTP::WebSocket` implementations.\n#\n# NOTE: To use `HTTP`, you must explicitly import it with `require \"http\"`\nmodule HTTP\nend\n"
  },
  {
    "path": "src/humanize.cr",
    "content": "struct Number\n  # Prints this number as a `String` using a customizable format.\n  #\n  # *separator* is used as decimal separator, *delimiter* as thousands\n  # delimiter between batches of *group* digits.\n  #\n  # If *decimal_places* is `nil`, all significant decimal places are printed\n  # (similar to `#to_s`). If the argument has a numeric value, the number of\n  # visible decimal places will be fixed to that amount.\n  #\n  # Trailing zeros are omitted if *only_significant* is `true`.\n  #\n  # ```\n  # 123_456.789.format                                            # => \"123,456.789\"\n  # 123_456.789.format(',', '.')                                  # => \"123.456,789\"\n  # 123_456.789.format(decimal_places: 2)                         # => \"123,456.79\"\n  # 123_456.789.format(decimal_places: 6)                         # => \"123,456.789000\"\n  # 123_456.789.format(decimal_places: 6, only_significant: true) # => \"123,456.789\"\n  # ```\n  def format(io : IO, separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : Nil\n    number = self\n    # TODO: Optimize implementation for Int\n    if decimal_places && (decimal_places < 0 || !number.is_a?(Float))\n      number = number.round(decimal_places)\n    end\n\n    if number.is_a?(Float)\n      if number.infinite?\n        if number < 0\n          io << '-'\n        end\n        io << \"Infinity\"\n        return\n      elsif number.nan?\n        io << \"NaN\"\n        return\n      end\n\n      if decimal_places && decimal_places >= 0\n        string = \"%.*f\" % {decimal_places, number.abs}\n        integer, _, decimals = string.partition('.')\n      else\n        string = String.build do |io|\n          # Make sure to avoid scientific notation of default Float#to_s\n          Float::Printer.shortest(number.abs, io, point_range: ..)\n        end\n        _, _, decimals = string.partition(\".\")\n        integer = \"%.0f\" % number.trunc.abs\n      end\n    elsif number.is_a?(Int)\n      integer = number.abs.to_s\n      decimals = \"\"\n    else\n      # TODO: optimize for BigDecimal\n      string = number.abs.to_s\n      integer, _, decimals = string.partition('.')\n    end\n\n    is_negative = number.responds_to?(:sign_bit) ? number.sign_bit < 0 : number < 0\n\n    format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant)\n  end\n\n  # :ditto:\n  def format(separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : String\n    String.build do |io|\n      format(io, separator, delimiter, decimal_places, group: group, only_significant: only_significant)\n    end\n  end\n\n  private def format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant) : Nil\n    int_size = integer.size\n    dec_size = decimals.size\n\n    io << '-' if is_negative\n\n    start = int_size % group\n    start += group if start == 0\n    io.write_string integer.to_slice[0, start]\n\n    while start < int_size\n      io << delimiter\n      io.write_string integer.to_slice[start, group]\n      start += group\n    end\n\n    decimal_places ||= dec_size\n\n    if decimal_places > 0\n      io << separator\n      if only_significant\n        decimals = decimals.rstrip('0')\n        if decimals.empty?\n          io << '0'\n        else\n          io << decimals\n        end\n      else\n        io << decimals\n        (decimal_places - dec_size).times do\n          io << '0'\n        end\n      end\n    end\n  end\n\n  # Default SI prefixes ordered by magnitude.\n  SI_PREFIXES = { {'q', 'r', 'y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm'}, {nil, 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q'} }\n\n  # SI prefixes used by `#humanize`. Equal to `SI_PREFIXES` but prepends the\n  # prefix with a space character.\n  SI_PREFIXES_PADDED = ->(magnitude : Int32, _number : Float64) do\n    magnitude = Number.prefix_index(magnitude)\n    {magnitude, (magnitude == 0 ? \" \" : si_prefix(magnitude))}\n  end\n\n  # Returns the SI prefix for *magnitude*.\n  #\n  # ```\n  # Number.si_prefix(3) # => 'k'\n  # ```\n  def self.si_prefix(magnitude : Int, prefixes = SI_PREFIXES) : Char?\n    index = (magnitude // 3)\n    prefixes = prefixes[magnitude < 0 ? 0 : 1]\n    prefixes[index.clamp((-prefixes.size)..(prefixes.size - 1))]\n  end\n\n  # :nodoc:\n  def self.prefix_index(i : Int32, *, group : Int32 = 3, prefixes = SI_PREFIXES) : Int32\n    prefixes = prefixes[i < 0 ? 0 : 1]\n    ((i - (i > 0 ? 1 : 0)) // group).clamp((-prefixes.size)..(prefixes.size - 1)) * group\n  end\n\n  # Pretty prints this number as a `String` in a human-readable format.\n  #\n  # This is particularly useful if a number can have a wide value range and\n  # the *exact* value is less relevant.\n  #\n  # It rounds the number to the nearest thousands magnitude with *precision*\n  # number of significant digits. The order of magnitude is expressed with an\n  # appended quantifier.\n  # By default, SI prefixes are used (see `SI_PREFIXES`).\n  #\n  # ```\n  # 1_200_000_000.humanize # => \"1.2G\"\n  # 0.000_000_012.humanize # => \"12.0n\"\n  # ```\n  #\n  # If *significant* is `false`, the number of *precision* digits is preserved\n  # after the decimal separator.\n  #\n  # ```\n  # 1_234.567_890.humanize(precision: 2)                     # => \"1.2k\"\n  # 1_234.567_890.humanize(precision: 2, significant: false) # => \"1.23k\"\n  # ```\n  #\n  # *separator* describes the decimal separator, *delimiter* the thousands\n  # delimiter (see `#format`).\n  #\n  # *unit_separator* is inserted between the value and the unit.\n  # Users are encouraged to use a non-breaking space ('\\u00A0') to prevent output being split across lines.\n  #\n  # See `Int#humanize_bytes` to format a file size.\n  def humanize(io : IO, precision = 3, separator = '.', delimiter = ',', *, base = 10 ** 3, significant = true, unit_separator = nil, prefixes : Indexable = SI_PREFIXES) : Nil\n    humanize(io, precision, separator, delimiter, base: base, significant: significant, unit_separator: unit_separator) do |magnitude, _|\n      magnitude = Number.prefix_index(magnitude, prefixes: prefixes)\n      {magnitude, Number.si_prefix(magnitude, prefixes)}\n    end\n  end\n\n  # :ditto:\n  def humanize(precision = 3, separator = '.', delimiter = ',', *, base = 10 ** 3, significant = true, unit_separator = nil, prefixes = SI_PREFIXES) : String\n    String.build do |io|\n      humanize(io, precision, separator, delimiter, base: base, significant: significant, unit_separator: unit_separator, prefixes: prefixes)\n    end\n  end\n\n  # Pretty prints this number as a `String` in a human-readable format.\n  #\n  # This is particularly useful if a number can have a wide value range and\n  # the *exact* value is less relevant.\n  #\n  # It rounds the number to the nearest thousands magnitude with *precision*\n  # number of significant digits. The order of magnitude is expressed with an\n  # appended quantifier.\n  # By default, SI prefixes are used (see `SI_PREFIXES`).\n  #\n  # ```\n  # 1_200_000_000.humanize # => \"1.2G\"\n  # 0.000_000_012.humanize # => \"12.0n\"\n  # ```\n  #\n  # If *significant* is `false`, the number of *precision* digits is preserved\n  # after the decimal separator.\n  #\n  # ```\n  # 1_234.567_890.humanize(precision: 2)                     # => \"1.2k\"\n  # 1_234.567_890.humanize(precision: 2, significant: false) # => \"1.23k\"\n  # ```\n  #\n  # *separator* describes the decimal separator, *delimiter* the thousands\n  # delimiter (see `#format`).\n  #\n  # This methods yields the order of magnitude and `self` and expects the block\n  # to return a `Tuple(Int32, _)` containing the (adjusted) magnitude and unit.\n  # The magnitude is typically adjusted to a multiple of `3`.\n  #\n  # ```\n  # def humanize_length(number)\n  #   number.humanize do |magnitude, number|\n  #     case magnitude\n  #     when -2, -1 then {-2, \" cm\"}\n  #     when .>=(4)\n  #       {3, \" km\"}\n  #     else\n  #       magnitude = Number.prefix_index(magnitude)\n  #       {magnitude, \" #{Number.si_prefix(magnitude)}m\"}\n  #     end\n  #   end\n  # end\n  #\n  # humanize_length(1_420) # => \"1.42 km\"\n  # humanize_length(0.23)  # => \"23.0 cm\"\n  # ```\n  #\n  # See `Int#humanize_bytes` to format a file size.\n  def humanize(io : IO, precision = 3, separator = '.', delimiter = ',', *, base = 10 ** 3, significant = true, unit_separator = nil, &prefixes : (Int32, Float64) -> {Int32, _} | {Int32, _, Bool}) : Nil\n    if zero? || (responds_to?(:infinite?) && self.infinite?) || (responds_to?(:nan?) && self.nan?)\n      digits = 0\n    else\n      log = Math.log10(abs)\n      digits = log.floor.to_i + 1\n    end\n\n    magnitude = digits\n\n    proper_fraction = 0 < abs < 1\n    if proper_fraction\n      magnitude -= 1\n    elsif magnitude == 0\n      magnitude = 1\n    end\n\n    yield_result = yield magnitude, self.to_f\n    magnitude, unit = yield_result[0..1]\n\n    decimal_places = precision\n    if significant\n      scrap_digits = digits - precision\n      decimal_places += magnitude - digits\n    else\n      scrap_digits = magnitude - precision\n    end\n    scrap_digits *= -1 if proper_fraction\n\n    exponent = 10 ** scrap_digits.to_f\n    if proper_fraction\n      number = (to_f * exponent).round / exponent\n    else\n      number = (to_f / exponent).round * exponent\n    end\n\n    number /= base.to_f ** (magnitude.to_f / 3.0)\n\n    # Scrap decimal places if magnitude lower bound == 0\n    # to return e.g. \"1B\" instead of \"1.0B\" for humanize_bytes.\n    decimal_places = 0 if yield_result[2]? == false\n\n    number.format(io, separator, delimiter, decimal_places: decimal_places, only_significant: significant)\n\n    io << unit_separator if unit\n    io << unit\n  end\n\n  # :ditto:\n  def humanize(precision = 3, separator = '.', delimiter = ',', *, base = 10 ** 3, significant = true, unit_separator = nil, &) : String\n    String.build do |io|\n      humanize(io, precision, separator, delimiter, base: base, significant: significant, unit_separator: unit_separator) do |magnitude, number|\n        yield magnitude, number\n      end\n    end\n  end\n\n  # :ditto:\n  def humanize(io : IO, precision = 3, separator = '.', delimiter = ',', *, base = 10 ** 3, significant = true, unit_separator = nil, prefixes : Proc) : Nil\n    humanize(io, precision, separator, delimiter, base: base, significant: significant, unit_separator: unit_separator) do |magnitude, number|\n      prefixes.call(magnitude, number)\n    end\n  end\n\n  # :ditto:\n  def humanize(precision = 3, separator = '.', delimiter = ',', *, base = 10 ** 3, significant = true, unit_separator = nil, prefixes : Proc) : String\n    String.build do |io|\n      humanize(io, precision, separator, delimiter, base: base, significant: significant, unit_separator: unit_separator, prefixes: prefixes)\n    end\n  end\nend\n\nstruct Int\n  enum BinaryPrefixFormat\n    # The IEC standard prefixes (`Ki`, `Mi`, `Gi`, `Ti`, `Pi`, `Ei`, `Zi`, `Yi`, `Ri`, `Qi`)\n    # based on powers of 1000.\n    IEC\n\n    # Extended range of the JEDEC units (`K`, `M`, `G`, `T`, `P`, `E`, `Z`, `Y`, `R`, `Q`)\n    # which equals to the prefixes of the SI system except for uppercase `K` and\n    # is based on powers of 1024.\n    JEDEC\n  end\n\n  # Prints this integer as a binary value in a human-readable format using\n  # a `BinaryPrefixFormat`.\n  #\n  # Values with binary measurements such as computer storage (e.g. RAM size) are\n  # typically expressed using unit prefixes based on 1024 (instead of multiples\n  # of 1000 as per SI standard). This method by default uses the IEC standard\n  # prefixes (`Ki`, `Mi`, `Gi`, `Ti`, `Pi`, `Ei`, `Zi`, `Yi`, `Ri`, `Qi`) based\n  # on powers of 1000 (see `BinaryPrefixFormat::IEC`).\n  #\n  # *format* can be set to use the extended range of JEDEC units (`K`, `M`, `G`,\n  # `T`, `P`, `E`, `Z`, `Y`, `R`, `Q`) which equals to the prefixes of the SI\n  # system except for uppercase `K` and is based on powers of 1024 (see\n  # `BinaryPrefixFormat::JEDEC`).\n  #\n  # ```\n  # 1.humanize_bytes                        # => \"1B\"\n  # 1024.humanize_bytes                     # => \"1.0kiB\"\n  # 1536.humanize_bytes                     # => \"1.5kiB\"\n  # 524288.humanize_bytes                   # => \"512kiB\"\n  # 1073741824.humanize_bytes(format: :IEC) # => \"1.0GiB\"\n  # ```\n  #\n  # See `Number#humanize` for more details on the behaviour and arguments.\n  def humanize_bytes(io : IO, precision : Int = 3, separator = '.', *, significant : Bool = true, unit_separator = nil, format : BinaryPrefixFormat = :IEC) : Nil\n    humanize(io, precision, separator, nil, base: 1024, significant: significant, unit_separator: unit_separator) do |magnitude|\n      magnitude = Number.prefix_index(magnitude)\n\n      prefix = Number.si_prefix(magnitude)\n      if prefix.nil?\n        unit = \"B\"\n      else\n        if format.iec?\n          unit = \"#{prefix}iB\"\n        else\n          unit = \"#{prefix.upcase}B\"\n        end\n      end\n      {magnitude, unit, magnitude > 0}\n    end\n  end\n\n  # :ditto:\n  def humanize_bytes(precision : Int = 3, separator = '.', *, significant : Bool = true, unit_separator = nil, format : BinaryPrefixFormat = :IEC) : String\n    String.build do |io|\n      humanize_bytes(io, precision, separator, significant: significant, unit_separator: unit_separator, format: format)\n    end\n  end\nend\n"
  },
  {
    "path": "src/indexable/mutable.cr",
    "content": "# An `Indexable` container that is additionally mutable.\n#\n# Including types may write values to numeric indices, apart from reading them.\n# This module does not cover cases where the container is resized.\nmodule Indexable::Mutable(T)\n  include Indexable(T)\n\n  # Sets the element at the given *index* to *value*, without doing any bounds\n  # check.\n  #\n  # `Indexable::Mutable` makes sure to invoke this method with *index* in\n  # `0...size`, so converting negative indices to positive ones is not needed\n  # here.\n  #\n  # Clients never invoke this method directly. Instead, they modify elements\n  # with `#[]=(index, value)`.\n  #\n  # This method should only be directly invoked if you are absolutely\n  # sure the index is in bounds, to avoid a bounds check for a small boost\n  # of performance.\n  abstract def unsafe_put(index : Int, value : T)\n\n  # Sets the given *value* at the given *index*. Returns *value*.\n  #\n  # Negative indices can be used to start counting from the end of the\n  # container. Raises `IndexError` if trying to set an element outside the\n  # container's range.\n  #\n  # ```\n  # ary = [1, 2, 3]\n  # ary[0] = 5\n  # ary # => [5, 2, 3]\n  #\n  # ary[3] = 5 # raises IndexError\n  # ```\n  @[AlwaysInline]\n  def []=(index : Int, value : T) : T\n    index = check_index_out_of_bounds index\n    unsafe_put(index, value)\n    value\n  end\n\n  # Yields the current element at the given *index* and updates the value\n  # at that *index* with the block's value. Returns the new value.\n  #\n  # Raises `IndexError` if trying to set an element outside the container's\n  # range.\n  #\n  # ```\n  # array = [1, 2, 3]\n  # array.update(1) { |x| x * 2 } # => 4\n  # array                         # => [1, 4, 3]\n  # array.update(5) { |x| x * 2 } # raises IndexError\n  # ```\n  def update(index : Int, & : T -> _) : T\n    index = check_index_out_of_bounds index\n    value = yield unsafe_fetch(index)\n    unsafe_put(index, value)\n    value\n  end\n\n  # Swaps the elements at *index0* and *index1*. Returns `self`.\n  #\n  # Negative indices can be used to start counting from the end of the\n  # container. Raises `IndexError` if either index is out of bounds.\n  #\n  # ```\n  # a = [\"first\", \"second\", \"third\"]\n  # a.swap(1, 2)  # => [\"first\", \"third\", \"second\"]\n  # a             # => [\"first\", \"third\", \"second\"]\n  # a.swap(0, -1) # => [\"second\", \"third\", \"first\"]\n  # a             # => [\"second\", \"third\", \"first\"]\n  # a.swap(2, 3)  # raises IndexError\n  # ```\n  def swap(index0 : Int, index1 : Int) : self\n    index0 = check_index_out_of_bounds(index0)\n    index1 = check_index_out_of_bounds(index1)\n\n    unless index0 == index1\n      tmp = unsafe_fetch(index0)\n      unsafe_put(index0, unsafe_fetch(index1))\n      unsafe_put(index1, tmp)\n    end\n\n    self\n  end\n\n  # Reverses in-place all the elements of `self`. Returns `self`.\n  def reverse! : self\n    return self if size <= 1\n\n    index0 = 0\n    index1 = size - 1\n\n    while index0 < index1\n      swap(index0, index1)\n      index0 += 1\n      index1 -= 1\n    end\n\n    self\n  end\n\n  # Replaces every element in `self` with the given *value*. Returns `self`.\n  #\n  # ```\n  # array = [1, 2, 3, 4]\n  # array.fill(2) # => [2, 2, 2, 2]\n  # array         # => [2, 2, 2, 2]\n  # ```\n  def fill(value : T) : self\n    each_index do |i|\n      unsafe_put(i, value)\n    end\n    self\n  end\n\n  # Replaces *count* or less (if there aren't enough) elements starting at the\n  # given *start* index with *value*. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the container.\n  #\n  # Raises `IndexError` if the *start* index is out of range.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  #\n  # ```\n  # array = [1, 2, 3, 4, 5]\n  # array.fill(9, 2, 2) # => [1, 2, 9, 9, 5]\n  # array               # => [1, 2, 9, 9, 5]\n  # ```\n  def fill(value : T, start : Int, count : Int) : self\n    return self if count <= 0\n    start, count = normalize_start_and_count(start, count)\n    count.times do |i|\n      unsafe_put(start + i, value)\n    end\n    self\n  end\n\n  # Replaces the elements within the given *range* with *value*. Returns `self`.\n  #\n  # Negative indices count backward from the end of the container.\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  #\n  # ```\n  # array = [1, 2, 3, 4, 5]\n  # array.fill(9, 2..3) # => [1, 2, 9, 9, 5]\n  # array               # => [1, 2, 9, 9, 5]\n  # ```\n  def fill(value : T, range : Range) : self\n    fill(value, *Indexable.range_to_index_and_count(range, size) || raise IndexError.new)\n  end\n\n  # Yields each index of `self` to the given block and then assigns\n  # the block's value in that position. Returns `self`.\n  #\n  # Accepts an optional *offset* parameter, which tells the block to start\n  # counting from there.\n  #\n  # ```\n  # array = [2, 1, 1, 1]\n  # array.fill { |i| i * i }            # => [0, 1, 4, 9]\n  # array                               # => [0, 1, 4, 9]\n  # array.fill(offset: 3) { |i| i * i } # => [9, 16, 25, 36]\n  # array                               # => [9, 16, 25, 36]\n  # ```\n  def fill(*, offset : Int = 0, & : Int32 -> T) : self\n    each_index do |i|\n      unsafe_put(i, yield offset + i)\n    end\n    self\n  end\n\n  # Yields each index of `self`, starting at *start* and for *count* times (or\n  # less if there aren't enough elements), to the given block and then assigns\n  # the block's value in that position. Returns `self`.\n  #\n  # Negative values of *start* count from the end of the container.\n  #\n  # Has no effect if *count* is zero or negative.\n  #\n  # Raises `IndexError` if *start* is outside the array range.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5, 6]\n  # a.fill(2, 3) { |i| i * i * i } # => [1, 2, 8, 27, 64, 6]\n  # ```\n  def fill(start : Int, count : Int, & : Int32 -> T) : self\n    return self if count <= 0\n    start, count = normalize_start_and_count(start, count)\n    count.times do |i|\n      unsafe_put(start + i, yield start + i)\n    end\n    self\n  end\n\n  # Yields each index of `self`, in the given *range*, to the given block and\n  # then assigns the block's value in that position. Returns `self`.\n  #\n  # Negative indices count backward from the end of the container.\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5, 6]\n  # a.fill(2..4) { |i| i * i * i } # => [1, 2, 8, 27, 64, 6]\n  # ```\n  def fill(range : Range, & : Int32 -> T) : self\n    fill(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new) do |i|\n      yield i\n    end\n  end\n\n  {% begin %}\n  # Invokes the given block for each element of `self`, replacing the element\n  # with the value returned by the block. Returns `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.map! { |x| x * x }\n  # a # => [1, 4, 9]\n  # ```\n  {% if compare_versions(Crystal::VERSION, \"1.1.1\") >= 0 %}\n  def map!(& : T -> _) : self # TODO: add as constant\n  {% else %}\n  def map!(&) # it doesn't compile with the type annotation in the 1.0.0 compiler\n  {% end %}\n    each_index do |i|\n      unsafe_put(i, yield unsafe_fetch(i))\n    end\n    self\n  end\n  {% end %}\n\n  # Like `#map!`, but the block gets passed both the element and its index.\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there.\n  #\n  # ```\n  # gems = [\"crystal\", \"pearl\", \"diamond\"]\n  # gems.map_with_index! { |gem, i| \"#{i}: #{gem}\" }\n  # gems # => [\"0: crystal\", \"1: pearl\", \"2: diamond\"]\n  # ```\n  def map_with_index!(offset = 0, & : T, Int32 -> _) : self\n    each_index do |i|\n      unsafe_put(i, yield(unsafe_fetch(i), offset + i))\n    end\n    self\n  end\n\n  # Modifies `self` by randomizing the order of elements in the collection.\n  # Returns `self`.\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a.shuffle! # => [3, 5, 2, 4, 1]\n  # a          # => [3, 5, 2, 4, 1]\n  # ```\n  #\n  # Uses the *random* instance when provided if the randomness needs to be\n  # controlled or to follow some traits. For example the following shuffle will\n  # always result in the same order:\n  #\n  # ```\n  # a = [1, 2, 3, 4, 5]\n  # a.shuffle!(Random.new(42)) # => [3, 2, 4, 5, 1]\n  # a.shuffle!(Random.new(42)) # => [3, 2, 4, 5, 1]\n  # a                          # => [3, 2, 4, 5, 1]\n  # ```\n  def shuffle!(random : Random? = nil) : self\n    rng = random || Random.thread_default\n    (size - 1).downto(1) do |i|\n      j = rng.rand(i + 1)\n      swap(i, j)\n    end\n    self\n  end\n\n  # Shifts all elements of `self` to the left *n* times. Returns `self`.\n  #\n  # ```\n  # a1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n  # a2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n  # a3 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n  #\n  # a1.rotate!\n  # a2.rotate!(1)\n  # a3.rotate!(3)\n  #\n  # a1 # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]\n  # a2 # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]\n  # a3 # => [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]\n  # ```\n  def rotate!(n : Int = 1) : self\n    return self if size <= 1\n    n %= size\n    return self if n == 0\n\n    # juggling algorithm\n    size.gcd(n).times do |i|\n      tmp = unsafe_fetch(i)\n      j = i\n\n      while true\n        k = j + n\n        k -= size if k >= size\n        break if k == i\n        unsafe_put(j, unsafe_fetch(k))\n        j = k\n      end\n\n      unsafe_put(j, tmp)\n    end\n\n    self\n  end\n\n  # Sorts all elements in `self` based on the return value of the comparison\n  # method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # a.sort!\n  # a # => [1, 2, 3]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#sort` for a non-modifying option\n  # that allocates a new instance.\n  #\n  # See `Slice#sort!` for details on the implementation.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def sort! : self\n    slice = Slice.new(size) { |i| unsafe_fetch(i) }.sort!\n    each_index do |i|\n      unsafe_put(i, slice.unsafe_fetch(i))\n    end\n    self\n  end\n\n  # Sorts all elements in `self` based on the return value of the comparison\n  # method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # a.unstable_sort!\n  # a # => [1, 2, 3]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#unstable_sort` for a non-modifying\n  # option that allocates a new instance.\n  #\n  # See `Slice#unstable_sort!` for details on the implementation.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def unstable_sort! : self\n    slice = Slice.new(size) { |i| unsafe_fetch(i) }.unstable_sort!\n    each_index do |i|\n      unsafe_put(i, slice.unsafe_fetch(i))\n    end\n    self\n  end\n\n  # Sorts all elements in `self` based on the comparator in the given block, using\n  # a stable sort algorithm.\n  #\n  # The block must implement a comparison between two elements *a* and *b*,\n  # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`.\n  # The comparison operator `<=>` can be used for this.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # # This is a reverse sort (forward sort would be `a <=> b`)\n  # a.sort! { |a, b| b <=> a }\n  # a # => [3, 2, 1]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#sort(&block : T, T -> U)` for a\n  # non-modifying option that allocates a new instance.\n  #\n  # See `Slice#sort!(&block : T, T -> U)` for details on the implementation.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def sort!(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}\" %}\n    {% end %}\n\n    slice = Slice.new(size) { |i| unsafe_fetch(i) }.sort!(&block)\n    each_index do |i|\n      unsafe_put(i, slice.unsafe_fetch(i))\n    end\n    self\n  end\n\n  # Sorts all elements in `self` based on the comparator in the given block,\n  # using an unstable sort algorithm.\n  #\n  # The block must implement a comparison between two elements *a* and *b*,\n  # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`.\n  # The comparison operator `<=>` can be used for this.\n  #\n  # ```\n  # a = [3, 1, 2]\n  # # This is a reverse sort (forward sort would be `a <=> b`)\n  # a.unstable_sort! { |a, b| b <=> a }\n  # a # => [3, 2, 1]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#unstable_sort(&block : T, T -> U)`\n  # for a non-modifying option that allocates a new instance.\n  #\n  # See `Slice#unstable_sort!(&block : T, T -> U)` for details on the implementation.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def unstable_sort!(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}\" %}\n    {% end %}\n\n    slice = Slice.new(size) { |i| unsafe_fetch(i) }.unstable_sort!(&block)\n    each_index do |i|\n      unsafe_put(i, slice.unsafe_fetch(i))\n    end\n    self\n  end\n\n  # Sorts all elements in `self` by the output value of the\n  # block. The output values are compared via the comparison method `#<=>`\n  # (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # a = %w(apple pear fig)\n  # a.sort_by! { |word| word.size }\n  # a # => [\"fig\", \"pear\", \"apple\"]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#sort_by(&block : T -> _)` for a\n  # non-modifying option that allocates a new instance.\n  #\n  # If stability is expendable, `#unstable_sort_by!(&block : T -> _)` provides a\n  # performance advantage over stable sort.\n  #\n  # See `#sort!(&block : T -> _)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`.\n  def sort_by!(&block : T -> _) : self\n    slice = Slice.new(size) do |i|\n      elem = unsafe_fetch(i)\n      {elem, (yield elem)}\n    end.sort! { |x, y| x[1] <=> y[1] }\n\n    each_index do |i|\n      unsafe_put(i, slice.unsafe_fetch(i)[0])\n    end\n    self\n  end\n\n  # Sorts all elements in `self` by the output value of the\n  # block. The output values are compared via the comparison method `#<=>`\n  # (see `Comparable#<=>`), using an unstable sort algorithm.\n  #\n  # ```\n  # a = %w(apple pear fig)\n  # a.unstable_sort_by! { |word| word.size }\n  # a # => [\"fig\", \"pear\", \"apple\"]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#unstable_sort_by(&block : T -> _)`\n  # for a non-modifying option that allocates a new instance.\n  #\n  # If stability is necessary, use  `#sort_by!(&block : T -> _)` instead.\n  #\n  # See `#unstable_sort!(&block : T -> _)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`.\n  def unstable_sort_by!(&block : T -> _) : self\n    slice = Slice.new(size) do |i|\n      elem = unsafe_fetch(i)\n      {elem, (yield elem)}\n    end.unstable_sort! { |x, y| x[1] <=> y[1] }\n\n    each_index do |i|\n      unsafe_put(i, slice.unsafe_fetch(i)[0])\n    end\n    self\n  end\nend\n"
  },
  {
    "path": "src/indexable.cr",
    "content": "# A container that allows accessing elements via a numeric index.\n#\n# Indexing starts at `0`. A negative index is assumed to be\n# relative to the end of the container: `-1` indicates the last element,\n# `-2` is the next to last element, and so on.\n#\n# Types including this module are typically `Array`-like types.\n#\n# ## Stability guarantees\n#\n# Several methods in `Indexable`, such as `#bsearch` and `#cartesian_product`,\n# require the collection to be _stable_; that is, calling `#each(&)` over and\n# over again should always yield the same elements, provided the collection is\n# not mutated between the calls. In particular, `#each(&)` itself should not\n# mutate the collection throughout the loop. Stability of an `Indexable` is\n# guaranteed if the following criteria are met:\n#\n# * `#unsafe_fetch` and `#size` do not mutate the collection\n# * `#each(&)` and `#each_index(&)` are not overridden\n#\n# The standard library assumes that all including types of `Indexable` are\n# always stable. It is undefined behavior to implement an `Indexable` that is\n# not stable or only conditionally stable.\nmodule Indexable(T)\n  include Iterable(T)\n  include Enumerable(T)\n\n  # Returns the number of elements in this container.\n  abstract def size\n\n  # Returns the element at the given *index*, without doing any bounds check.\n  #\n  # `Indexable` makes sure to invoke this method with *index* in `0...size`,\n  # so converting negative indices to positive ones is not needed here.\n  #\n  # Clients never invoke this method directly. Instead, they access\n  # elements with `#[](index)` and `#[]?(index)`.\n  #\n  # This method should only be directly invoked if you are absolutely\n  # sure the index is in bounds, to avoid a bounds check for a small boost\n  # of performance.\n  abstract def unsafe_fetch(index : Int)\n\n  # Returns the element at the given *index*, if in bounds,\n  # otherwise executes the given block with the index and returns its value.\n  #\n  # ```\n  # a = [:foo, :bar]\n  # a.fetch(0) { :default_value }    # => :foo\n  # a.fetch(2) { :default_value }    # => :default_value\n  # a.fetch(2) { |index| index * 3 } # => 6\n  # ```\n  def fetch(index : Int, &)\n    index = check_index_out_of_bounds(index) do\n      return yield index\n    end\n    unsafe_fetch(index)\n  end\n\n  # Returns the value at the index given by *index*, or when not found the value given by *default*.\n  #\n  # ```\n  # a = [:foo, :bar]\n  # a.fetch(0, :default_value) # => :foo\n  # a.fetch(2, :default_value) # => :default_value\n  # ```\n  @[AlwaysInline]\n  def fetch(index, default)\n    fetch(index) { default }\n  end\n\n  # Returns the element at the given *index*.\n  #\n  # Negative indices can be used to start counting from the end of the array.\n  # Raises `IndexError` if trying to access an element outside the array's range.\n  #\n  # ```\n  # ary = ['a', 'b', 'c']\n  # ary[0]  # => 'a'\n  # ary[2]  # => 'c'\n  # ary[-1] # => 'c'\n  # ary[-2] # => 'b'\n  #\n  # ary[3]  # raises IndexError\n  # ary[-4] # raises IndexError\n  # ```\n  @[AlwaysInline]\n  def [](index : Int)\n    fetch(index) { raise IndexError.new }\n  end\n\n  # Returns the element at the given *index*.\n  #\n  # Negative indices can be used to start counting from the end of the array.\n  # Returns `nil` if trying to access an element outside the array's range.\n  #\n  # ```\n  # ary = ['a', 'b', 'c']\n  # ary[0]?  # => 'a'\n  # ary[2]?  # => 'c'\n  # ary[-1]? # => 'c'\n  # ary[-2]? # => 'b'\n  #\n  # ary[3]?  # => nil\n  # ary[-4]? # => nil\n  # ```\n  @[AlwaysInline]\n  def []?(index : Int)\n    fetch(index, nil)\n  end\n\n  # Traverses the depth of a structure and returns the value.\n  # Returns `nil` if not found.\n  #\n  # ```\n  # ary = [{1, 2, 3, {4, 5, 6}}]\n  # ary.dig?(0, 3, 2) # => 6\n  # ary.dig?(0, 3, 3) # => nil\n  # ```\n  def dig?(index : Int, *subindexes)\n    if (value = self[index]?) && value.responds_to?(:dig?)\n      value.dig?(*subindexes)\n    end\n  end\n\n  # :nodoc:\n  def dig?(index : Int)\n    self[index]?\n  end\n\n  # Traverses the depth of a structure and returns the value, otherwise\n  # raises `IndexError`.\n  #\n  # ```\n  # ary = [{1, 2, 3, {4, 5, 6}}]\n  # ary.dig(0, 3, 2) # => 6\n  # ary.dig(0, 3, 3) # raises IndexError\n  # ```\n  def dig(index : Int, *subindexes)\n    if (value = self[index]) && value.responds_to?(:dig)\n      return value.dig(*subindexes)\n    end\n    raise IndexError.new \"Indexable value not diggable for index: #{index.inspect}\"\n  end\n\n  # :nodoc:\n  def dig(index : Int)\n    self[index]\n  end\n\n  # By using binary search, returns the first element\n  # for which the passed block returns a truthy value.\n  #\n  # If the block returns a falsey value, the element to be found lies\n  # behind. If the block returns a truthy value, the element to be found\n  # is itself or lies in front.\n  #\n  # Binary search needs the collection to be sorted in regards to the search\n  # criterion.\n  #\n  # Returns `nil` if the block didn't return a truthy value for any element.\n  #\n  # ```\n  # [2, 5, 7, 10].bsearch { |x| x >= 4 } # => 5\n  # [2, 5, 7, 10].bsearch { |x| x > 10 } # => nil\n  # ```\n  def bsearch(& : T -> _)\n    bsearch_index { |value| yield value }.try { |index| unsafe_fetch(index) }\n  end\n\n  # By using binary search, returns the index of the first element\n  # for which the passed block returns a truthy value.\n  #\n  # If the block returns a falsey value, the element to be found lies\n  # behind. If the block returns a truthy value, the element to be found\n  # is itself or lies in front.\n  #\n  # Binary search needs the collection to be sorted in regards to the search\n  # criterion.\n  #\n  # Returns `nil` if the block didn't return a truthy value for any element.\n  #\n  # ```\n  # [2, 5, 7, 10].bsearch_index { |x, i| x >= 4 } # => 1\n  # [2, 5, 7, 10].bsearch_index { |x, i| x > 10 } # => nil\n  # ```\n  def bsearch_index(& : T, Int32 -> _)\n    (0...size).bsearch { |index| yield unsafe_fetch(index), index }\n  end\n\n  # Returns an `Array` of all ordered combinations of elements taken from each\n  # of `self` and *others* as `Tuple`s.\n  # Traversal of elements starts from the last `Indexable` argument.\n  #\n  # ```\n  # [1, 2, 3].cartesian_product({'a', 'b'})                     # => [{1, 'a'}, {1, 'b'}, {2, 'a'}, {2, 'b'}, {3, 'a'}, {3, 'b'}]\n  # ['a', 'b'].cartesian_product({1, 2}, {'c', 'd'}).map &.join # => [\"a1c\", \"a1d\", \"a2c\", \"a2d\", \"b1c\", \"b1d\", \"b2c\", \"b2d\"]\n  # ```\n  def cartesian_product(*others : Indexable)\n    capacity = others.product(size, &.size)\n    result = Array(typeof(cartesian_product_type(*others))).new(capacity)\n    each_cartesian(*others) do |product|\n      result << product\n    end\n    result\n  end\n\n  private def cartesian_product_type(*others)\n    v = each_cartesian(*others).next\n    raise \"\" if v.is_a?(Iterator::Stop)\n    v\n  end\n\n  # Returns an `Array` of all ordered combinations of elements taken from each\n  # of the *indexables* as `Array`s.\n  # Traversal of elements starts from the last `Indexable`. If *indexables* is\n  # empty, the returned product contains exactly one empty `Array`.\n  #\n  # `#cartesian_product` is preferred over this class method when the quantity\n  # of *indexables* is known in advance.\n  #\n  # ```\n  # Indexable.cartesian_product([[1, 2, 3], [4, 5]]) # => [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]\n  # ```\n  def self.cartesian_product(indexables : Indexable(Indexable))\n    capacity = indexables.product(&.size)\n    result = Array(Array(typeof(Enumerable.element_type Enumerable.element_type indexables))).new(capacity)\n    each_cartesian(indexables) do |product|\n      result << product\n    end\n    result\n  end\n\n  # Yields each ordered combination of the elements taken from each of `self`\n  # and *others* as a `Tuple`.\n  # Traversal of elements starts from the last `Indexable` argument.\n  #\n  # ```\n  # [\"Alice\", \"Bob\"].each_cartesian({1, 2, 3}) do |name, n|\n  #   puts \"#{n}. #{name}\"\n  # end\n  # ```\n  #\n  # Prints\n  #\n  # ```text\n  # 1. Alice\n  # 2. Alice\n  # 3. Alice\n  # 1. Bob\n  # 2. Bob\n  # 3. Bob\n  # ```\n  def each_cartesian(*others : Indexable, &block)\n    Indexable.each_cartesian_impl(self, *others) { |v| yield v }\n  end\n\n  protected def self.each_cartesian_impl(*indexables : *U, &block) forall U\n    lens = indexables.map &.size\n    return if lens.any? &.zero?\n\n    n = indexables.size\n    indices = Array.new(n, 0)\n    indices[-1] -= 1\n\n    while true\n      i = n - 1\n      indices[i] += 1\n\n      while indices[i] >= lens[i]\n        indices[i] = 0\n        i -= 1\n        return if i < 0\n        indices[i] += 1\n      end\n\n      {% begin %}\n        yield Tuple.new(\n          {% for i in 0...U.size %}\n            indexables[{{ i }}].unsafe_fetch(indices[{{ i }}]),\n          {% end %}\n        )\n      {% end %}\n    end\n  end\n\n  # Yields each ordered combination of the elements taken from each of the\n  # *indexables* as `Array`s.\n  # Traversal of elements starts from the last `Indexable`. If *indexables* is\n  # empty, yields an empty `Array` exactly once.\n  #\n  # `#each_cartesian` is preferred over this class method when the quantity of\n  # *indexables* is known in advance.\n  #\n  # ```\n  # Indexable.each_cartesian([%w[Alice Bob Carol], [1, 2]]) do |name, n|\n  #   puts \"#{n}. #{name}\"\n  # end\n  # ```\n  #\n  # Prints\n  #\n  # ```text\n  # 1. Alice\n  # 2. Alice\n  # 1. Bob\n  # 2. Bob\n  # 1. Carol\n  # 2. Carol\n  # ```\n  #\n  # By default, a new `Array` is created and yielded for each combination.\n  #\n  # * If *reuse* is an `Array`, it will be reused\n  # * If *reuse* is truthy, the method will create a new `Array` and reuse it\n  # * If *reuse* is falsey, no `Array`s will be reused.\n  #\n  # This can be used to prevent many memory allocations when each combination of\n  # interest is to be used in a read-only fashion.\n  def self.each_cartesian(indexables : Indexable(Indexable), reuse = false, &block)\n    lens = indexables.map &.size\n    return if lens.any? &.zero?\n\n    n = indexables.size\n    pool = Array.new(n) { |i| indexables.unsafe_fetch(i).unsafe_fetch(0) }\n    indices = Array.new(n, 0)\n    reuse = Indexable(typeof(pool.first)).check_reuse(reuse, n)\n\n    while true\n      yield pool_slice(pool, n, reuse)\n\n      i = n\n\n      while true\n        i -= 1\n        return if i < 0\n        indices[i] += 1\n        if move_to_next = (indices[i] >= lens[i])\n          indices[i] = 0\n        end\n        pool[i] = indexables[i].unsafe_fetch(indices[i])\n        break unless move_to_next\n      end\n    end\n  end\n\n  # Returns an iterator that enumerates the ordered combinations of elements\n  # taken from each of `self` and *others* as `Tuple`s.\n  # Traversal of elements starts from the last `Indexable` argument.\n  #\n  # ```\n  # iter = {1, 2, 3}.each_cartesian({'a', 'b'})\n  # iter.next # => {1, 'a'}\n  # iter.next # => {1, 'b'}\n  # iter.next # => {2, 'a'}\n  # iter.next # => {2, 'b'}\n  # iter.next # => {3, 'a'}\n  # iter.next # => {3, 'b'}\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def each_cartesian(*others : Indexable)\n    Indexable.each_cartesian_impl(self, *others)\n  end\n\n  protected def self.each_cartesian_impl(*indexables : *U) forall U\n    {% begin %}\n      CartesianProductIteratorT(U, Tuple(\n        {% for i in 0...U.size %}\n          typeof(Enumerable.element_type(indexables[{{ i }}])),\n        {% end %}\n      )).new(indexables)\n    {% end %}\n  end\n\n  # Returns an iterator that enumerates the ordered combinations of elements\n  # taken from the *indexables* as `Array`s.\n  # Traversal of elements starts from the last `Indexable`. If *indexables* is\n  # empty, the returned iterator produces one empty `Array`, then stops.\n  #\n  # `#each_cartesian` is preferred over this class method when the quantity of\n  # *indexables* is known in advance.\n  #\n  # ```\n  # iter = Indexable.each_cartesian([%w[N S], %w[E W]])\n  # iter.next # => [\"N\", \"E\"]\n  # iter.next # => [\"N\", \"W\"]\n  # iter.next # => [\"S\", \"E\"]\n  # iter.next # => [\"S\", \"W\"]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new `Array` is created and returned for each combination.\n  #\n  # * If *reuse* is an `Array`, it will be reused\n  # * If *reuse* is truthy, the method will create a new `Array` and reuse it\n  # * If *reuse* is falsey, no `Array`s will be reused.\n  #\n  # This can be used to prevent many memory allocations when each combination of\n  # interest is to be used in a read-only fashion.\n  def self.each_cartesian(indexables : Indexable(Indexable), reuse = false)\n    CartesianProductIteratorN(typeof(indexables), typeof(Enumerable.element_type Enumerable.element_type indexables)).new(indexables, reuse)\n  end\n\n  private class CartesianProductIteratorT(Is, Ts)\n    include Iterator(Ts)\n\n    @indices : Array(Int32)\n\n    def initialize(@indexables : Is)\n      @indices = Array.new(@indexables.size, 0)\n      @indices[-1] -= 1\n    end\n\n    def next\n      i = @indices.size - 1\n      @indices[i] += 1\n\n      while @indices[i] >= @indexables[i].size\n        @indices[i] = 0\n        i -= 1\n        return stop if i < 0\n        @indices[i] += 1\n      end\n\n      {% begin %}\n        Ts.new(\n          {% for i in 0...Is.size %}\n            @indexables[{{ i }}].unsafe_fetch(@indices[{{ i }}]),\n          {% end %}\n        )\n      {% end %}\n    end\n  end\n\n  private class CartesianProductIteratorN(Is, T)\n    include Iterator(Array(T))\n\n    @indices : Array(Int32)\n    @pool : Array(T)\n    @reuse : Array(T)?\n\n    def initialize(@indexables : Is, reuse)\n      n = @indexables.size\n      @pool = Array.new(n) { |i| indexables.unsafe_fetch(i).unsafe_fetch(0) }\n      @indices = Array.new({1, n}.max, 0)\n      @indices[-1] -= 1\n\n      if reuse\n        if reuse.is_a?(Array(T))\n          @reuse = reuse\n        else\n          @reuse = Array(T).new(n)\n        end\n      end\n    end\n\n    def next\n      if @indexables.empty?\n        return stop if @indices[-1] == 0\n        @indices[-1] += 1\n        return pool_slice(@pool, 0, @reuse)\n      end\n\n      i = @indices.size - 1\n      @indices[i] += 1\n\n      while @indices[i] >= @indexables[i].size\n        @indices[i] = 0\n        @pool[i] = @indexables[i].unsafe_fetch(0)\n        i -= 1\n        return stop if i < 0\n        @indices[i] += 1\n      end\n\n      @pool[i] = @indexables[i].unsafe_fetch(@indices[i])\n      pool_slice(@pool, @indexables.size, @reuse)\n    end\n  end\n\n  # Calls the given block once for each element in `self`, passing that\n  # element as a parameter.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.each { |x| print x, \" -- \" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # a -- b -- c --\n  # ```\n  def each(& : T ->)\n    each_index do |i|\n      yield unsafe_fetch(i)\n    end\n  end\n\n  # Returns an `Iterator` for the elements of `self`.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # iter = a.each\n  # iter.next # => \"a\"\n  # iter.next # => \"b\"\n  # ```\n  #\n  # The returned iterator keeps a reference to `self`: if the array\n  # changes, the returned values of the iterator change as well.\n  def each\n    ItemIterator(self, T).new(self)\n  end\n\n  # Calls the given block once for `count` number of elements in `self`\n  # starting from index `start`, passing each element as a parameter.\n  #\n  # Negative indices count backward from the end of the array. (-1 is the\n  # last element).\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  # Raises `ArgumentError` if `count` is a negative number.\n  #\n  # ```\n  # array = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # array.each(start: 1, count: 3) { |x| print x, \" -- \" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # b -- c -- d --\n  # ```\n  def each(*, start : Int, count : Int, & : T ->)\n    each_index(start: start, count: count) do |i|\n      yield unsafe_fetch(i)\n    end\n  end\n\n  # Calls the given block once for all elements at indices within the given\n  # `range`, passing each element as a parameter.\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  #\n  # ```\n  # array = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # array.each(within: 1..3) { |x| print x, \" -- \" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # b -- c -- d --\n  # ```\n  def each(*, within range : Range, & : T ->)\n    start, count = Indexable.range_to_index_and_count(range, size) || raise IndexError.new\n    each(start: start, count: count) { |element| yield element }\n  end\n\n  # Calls the given block once for each index in `self`, passing that\n  # index as a parameter.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # a.each_index { |x| print x, \" -- \" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # 0 -- 1 -- 2 --\n  # ```\n  def each_index(& : Int32 ->) : Nil\n    i = 0\n    while i < size\n      yield i\n      i += 1\n    end\n  end\n\n  # Returns an `Iterator` for each index in `self`.\n  #\n  # ```\n  # a = [\"a\", \"b\", \"c\"]\n  # iter = a.each_index\n  # iter.next # => 0\n  # iter.next # => 1\n  # ```\n  #\n  # The returned iterator keeps a reference to `self`. If the array\n  # changes, the returned values of the iterator will change as well.\n  def each_index\n    IndexIterator.new(self)\n  end\n\n  # Calls the given block once for `count` number of indices in `self`\n  # starting from index `start`, passing each index as a parameter.\n  #\n  # Negative indices count backward from the end of the array. (-1 is the\n  # last element).\n  #\n  # Raises `IndexError` if the starting index is out of range.\n  # Raises `ArgumentError` if `count` is a negative number.\n  #\n  # ```\n  # array = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # array.each_index(start: -3, count: 2) { |x| print x, \" -- \" }\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # 2 -- 3 --\n  # ```\n  def each_index(*, start : Int, count : Int, &)\n    # We cannot use `normalize_start_and_count` here because `self` may be\n    # mutated to contain enough elements during iteration even if there weren't\n    # initially `count` elements.\n    raise ArgumentError.new \"Negative count: #{count}\" if count < 0\n\n    start += size if start < 0\n    raise IndexError.new unless 0 <= start <= size\n\n    i = start\n    # `count` and size comparison must be done every iteration because\n    # `self` can mutate in the block.\n    while i < Math.min(start + count, size)\n      yield i\n      i += 1\n    end\n    self\n  end\n\n  # Optimized version of `Enumerable#join` that performs better when\n  # all of the elements in this indexable are strings: the total string\n  # bytesize to return can be computed before creating the final string,\n  # which performs better because there's no need to do reallocations.\n  def join(separator : String | Char | Number = \"\") : String\n    return \"\" if empty?\n\n    {% if T == String %}\n      join_strings(separator)\n    {% elsif String < T %}\n      if all?(String)\n        join_strings(separator)\n      else\n        super(separator)\n      end\n    {% else %}\n      super(separator)\n    {% end %}\n  end\n\n  private def join_strings(separator)\n    separator = separator.to_s\n\n    # The total bytesize of the string to return is:\n    length =\n      ((self.size - 1) * separator.bytesize) + # the bytesize of all separators\n        self.sum(&.to_s.bytesize)              # the bytesize of all the elements\n\n    String.new(length) do |buffer|\n      # Also compute the UTF-8 size if we can\n      size = 0\n      size_known = true\n\n      each_with_index do |elem, i|\n        # elem is guaranteed to be a String, but the compiler doesn't know this\n        # if we enter via the all?(String) branch.\n        elem = elem.to_s\n\n        # Copy separator to buffer\n        if i != 0\n          buffer.copy_from(separator.to_unsafe, separator.bytesize)\n          buffer += separator.bytesize\n        end\n\n        # Copy element to buffer\n        buffer.copy_from(elem.to_unsafe, elem.bytesize)\n        buffer += elem.bytesize\n\n        # Check whether we'll know the final UTF-8 size\n        if elem.size_known?\n          size += elem.size\n        else\n          size_known = false\n        end\n      end\n\n      # Add size of all separators\n      size += (self.size - 1) * separator.size if size_known\n\n      {length, size_known ? size : 0}\n    end\n  end\n\n  # Returns an `Array` with the results of running *block* against each element of the collection.\n  #\n  # ```\n  # {1, 2, 3}.to_a { |i| i * 2 } # => [2, 4, 6]\n  # ```\n  def to_a(& : T -> U) : Array(U) forall U\n    ary = Array(U).new(size)\n    each { |e| ary << yield e }\n    ary\n  end\n\n  # Returns `true` if `self` is empty, `false` otherwise.\n  #\n  # ```\n  # ([] of Int32).empty? # => true\n  # ([1]).empty?         # => false\n  # ```\n  def empty? : Bool\n    size == 0\n  end\n\n  # Optimized version of `equals?` used when `other` is also an `Indexable`.\n  def equals?(other : Indexable, &) : Bool\n    return false if size != other.size\n    each_with_index do |item, i|\n      return false unless yield(item, other.unsafe_fetch(i))\n    end\n    true\n  end\n\n  # Determines if `self` equals *other* according to a comparison\n  # done by the given block.\n  #\n  # If `self`'s size is the same as *other*'s size, this method yields\n  # elements from `self` and *other* in tandem: if the block returns true\n  # for all of them, this method returns `true`. Otherwise it returns `false`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # b = [\"a\", \"ab\", \"abc\"]\n  # a.equals?(b) { |x, y| x == y.size } # => true\n  # a.equals?(b) { |x, y| x == y }      # => false\n  # ```\n  def equals?(other, &)\n    return false if size != other.size\n    each_with_index do |item, i|\n      return false unless yield(item, other[i])\n    end\n    true\n  end\n\n  # :inherit:\n  def first(&)\n    size == 0 ? yield : unsafe_fetch(0)\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher = size.hash(hasher)\n    each do |elem|\n      hasher = elem.hash(hasher)\n    end\n    hasher\n  end\n\n  # Returns the index of the first appearance of *object* in `self`\n  # starting from the given *offset*, or `nil` if *object* is not in `self`.\n  #\n  # ```\n  # [1, 2, 3, 1, 2, 3].index(2, offset: 2) # => 4\n  # ```\n  def index(object, offset : Int = 0)\n    index(offset) { |e| e == object }\n  end\n\n  # Returns the index of the first object in `self` for which the block\n  # is truthy, starting from the given *offset*, or `nil` if no match\n  # is found.\n  #\n  # ```\n  # [1, 2, 3, 1, 2, 3].index(offset: 2) { |x| x < 2 } # => 3\n  # ```\n  def index(offset : Int = 0, & : T ->)\n    offset += size if offset < 0\n    return nil if offset < 0\n\n    offset.upto(size - 1) do |i|\n      if yield unsafe_fetch(i)\n        return i\n      end\n    end\n    nil\n  end\n\n  # Returns the index of the first appearance of *obj* in `self`\n  # starting from the given *offset*. Raises `Enumerable::NotFoundError` if\n  # *obj* is not in `self`.\n  #\n  # ```\n  # [1, 2, 3, 1, 2, 3].index!(2, offset: 2) # => 4\n  # ```\n  def index!(obj, offset : Int = 0)\n    index(obj, offset) || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the index of the first object in `self` for which the block\n  # is truthy, starting from the given *offset*. Raises\n  # `Enumerable::NotFoundError` if no match is found.\n  #\n  # ```\n  # [1, 2, 3, 1, 2, 3].index!(offset: 2) { |x| x < 2 } # => 3\n  # ```\n  def index!(offset : Int = 0, & : T ->)\n    index(offset) { |e| yield e } || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the first element in the indexable for which the passed block\n  # is truthy, starting from the given *offset*.\n  #\n  # Accepts an optional parameter *if_none*, to set what gets returned if\n  # no element is found (defaults to `nil`).\n  #\n  # ```\n  # [1, 2, 3, 4].find { |i| i > 2 }        # => 3\n  # [1, 2, 3, 4].find(-1) { |i| i > 8 }    # => -1\n  # [1, 2, 3, 4].find(-1, 2) { |i| i < 2 } # => -1\n  # ```\n  def find(if_none = nil, *, offset : Int, & : T ->)\n    offset += size if offset < 0\n    return if_none if offset < 0\n\n    offset.upto(size - 1) do |i|\n      elem = unsafe_fetch(i)\n      if yield elem\n        return elem\n      end\n    end\n\n    if_none\n  end\n\n  # :ditto:\n  def find(if_none, _offset offset : Int, & : T ->)\n    find(if_none, offset: offset) { |e| yield e }\n  end\n\n  # Returns the first element in the indexable for which the passed block\n  # is truthy, starting from the given *offset*.\n  # Raises `Enumerable::NotFoundError` if there is no element for which the block is truthy.\n  #\n  # ```\n  # [1, 2, 3, 4].find! { |i| i > 2 }     # => 3\n  # [1, 2, 3, 4].find!(3) { |i| i > 2 }  # => 4\n  # [1, 2, 3, 4].find! { |i| i > 8 }     # => raises Enumerable::NotFoundError\n  # [1, 2, 3, 4].find!(-5) { |i| i > 2 } # => raises Enumerable::NotFoundError\n  # ```\n  def find!(offset : Int = 0, & : T ->)\n    find(offset: offset) { |i| yield i } || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the last element of `self` if it's not empty, or raises `IndexError`.\n  #\n  # ```\n  # ([1, 2, 3]).last   # => 3\n  # ([] of Int32).last # raises IndexError\n  # ```\n  def last : T\n    last { raise IndexError.new }\n  end\n\n  # Returns the last element of `self` if it's not empty, or the given block's value.\n  #\n  # ```\n  # ([1, 2, 3]).last { 4 }   # => 3\n  # ([] of Int32).last { 4 } # => 4\n  # ```\n  def last(&)\n    size == 0 ? yield : unsafe_fetch(size - 1)\n  end\n\n  # Returns the last element of `self` if it's not empty, or `nil`.\n  #\n  # ```\n  # ([1, 2, 3]).last?   # => 3\n  # ([] of Int32).last? # => nil\n  # ```\n  def last? : T?\n    last { nil }\n  end\n\n  # Same as `#each`, but works in reverse.\n  def reverse_each(& : T ->) : Nil\n    (size - 1).downto(0) do |i|\n      yield unsafe_fetch(i)\n    end\n  end\n\n  # Returns an `Iterator` over the elements of `self` in reverse order.\n  def reverse_each\n    ReverseItemIterator(self, T).new(self)\n  end\n\n  # Returns the index of the last appearance of *value* in `self`, or\n  # `nil` if the value is not in `self`.\n  #\n  # If *offset* is given, it defines the position to _end_ the search\n  # (elements beyond this point are ignored).\n  #\n  # ```\n  # [1, 2, 3, 2, 3].rindex(2)            # => 3\n  # [1, 2, 3, 2, 3].rindex(2, offset: 2) # => 1\n  # ```\n  def rindex(value, offset = size - 1)\n    rindex(offset) { |elem| elem == value }\n  end\n\n  # :ditto:\n  #\n  # Raises `Enumerable::NotFoundError` if *value* is not in `self`.\n  def rindex!(value, offset = size - 1)\n    rindex(value, offset) || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the index of the first object in `self` for which the block\n  # is truthy, starting from the last object, or `nil` if no match\n  # is found.\n  #\n  # If *offset* is given, the search starts from that index towards the\n  # first elements in `self`.\n  #\n  # ```\n  # [1, 2, 3, 2, 3].rindex { |x| x < 3 }            # => 3\n  # [1, 2, 3, 2, 3].rindex(offset: 2) { |x| x < 3 } # => 1\n  # ```\n  def rindex(offset = size - 1, & : T ->)\n    offset += size if offset < 0\n    return nil if offset >= size\n\n    offset.downto(0) do |i|\n      if yield unsafe_fetch(i)\n        return i\n      end\n    end\n    nil\n  end\n\n  # :ditto:\n  #\n  # Raises `Enumerable::NotFoundError` if no match is found.\n  def rindex!(offset = size - 1, & : T ->)\n    rindex(offset) { |e| yield e } || raise Enumerable::NotFoundError.new\n  end\n\n  # Optimized version of `Enumerable#sample` that runs in O(1) time.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.sample # => 3\n  # a.sample # => 1\n  # ```\n  #\n  # Uses the *random* instance when provided if the randomness needs to be\n  # controlled or to follow some traits. For example the following sample will\n  # always return the same value:\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.sample(Random.new(1)) # => 2\n  # a.sample(Random.new(1)) # => 2\n  # ```\n  def sample(random : Random? = nil)\n    raise IndexError.new(\"Can't sample empty collection\") if size == 0\n    rng = random || Random.thread_default\n    unsafe_fetch(rng.rand(size))\n  end\n\n  # :inherit:\n  #\n  # If `self` is not empty and `n` is equal to 1, calls `sample(random)` exactly\n  # once. Thus, *random* will be left in a different state compared to the\n  # implementation in `Enumerable`.\n  def sample(n : Int, random : Random? = nil) : Array(T)\n    return super unless n == 1\n\n    if empty?\n      [] of T\n    else\n      [sample(random)]\n    end\n  end\n\n  # Returns a `Tuple` populated with the elements at the given indexes.\n  # Raises `IndexError` if any index is invalid.\n  #\n  # ```\n  # [\"a\", \"b\", \"c\", \"d\"].values_at(0, 2) # => {\"a\", \"c\"}\n  # ```\n  def values_at(*indexes : Int)\n    indexes.map { |index| self[index] }\n  end\n\n  private def check_index_out_of_bounds(index)\n    check_index_out_of_bounds(index) { raise IndexError.new }\n  end\n\n  private def check_index_out_of_bounds(index, &)\n    index += size if index < 0\n    if 0 <= index < size\n      index\n    else\n      yield\n    end\n  end\n\n  private def normalize_start_and_count(start, count)\n    Indexable.normalize_start_and_count(start, count, size)\n  end\n\n  private def normalize_start_and_count(start, count, &)\n    Indexable.normalize_start_and_count(start, count, size) { yield }\n  end\n\n  # :nodoc:\n  def self.normalize_start_and_count(start, count, collection_size, &)\n    raise ArgumentError.new \"Negative count: #{count}\" if count < 0\n    start += collection_size if start < 0\n    if 0 <= start <= collection_size\n      count = {count, collection_size - start}.min\n      {start, count}\n    else\n      yield\n    end\n  end\n\n  # :nodoc:\n  def self.normalize_start_and_count(start, count, collection_size)\n    normalize_start_and_count(start, count, collection_size) { raise IndexError.new }\n  end\n\n  # :nodoc:\n  def self.range_to_index_and_count(range, collection_size)\n    start_index = range.begin\n    if start_index.nil?\n      start_index = 0\n    else\n      start_index += collection_size if start_index < 0\n\n      return nil if start_index < 0\n    end\n\n    count = 0\n\n    end_index = range.end\n    if end_index.nil?\n      if start_index <= collection_size\n        count = collection_size - start_index\n      end\n    else\n      if end_index < 0\n        end_index += collection_size\n      end\n\n      # must be careful with computation here, since we may be dealing with\n      # unsigned ints or ints close to MAX.\n      if end_index >= start_index\n        count = end_index - start_index\n        count += 1 unless range.excludes_end? || collection_size < count\n      end\n    end\n\n    {start_index, count}\n  end\n\n  # Returns an `Array` with all possible permutations of *size* of `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.permutations    # => [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]\n  # a.permutations(1) # => [[1],[2],[3]]\n  # a.permutations(2) # => [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]\n  # a.permutations(3) # => [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]\n  # a.permutations(0) # => [[]]\n  # a.permutations(4) # => []\n  # ```\n  def permutations(size : Int = self.size) : Array(Array(T))\n    ary = [] of Array(T)\n    each_permutation(size) do |a|\n      ary << a\n    end\n    ary\n  end\n\n  # Yields each possible permutation of *size* of `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # sums = [] of Int32\n  # a.each_permutation(2) { |p| sums << p.sum } # => nil\n  # sums                                        # => [3, 4, 3, 5, 4, 5]\n  # ```\n  #\n  # By default, a new array is created and yielded for each permutation.\n  # If *reuse* is given, the array can be reused: if *reuse* is\n  # an `Array`, this array will be reused; if *reuse* if truthy,\n  # the method will create a new array and reuse it. This can be\n  # used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_permutation(size : Int = self.size, reuse = false, &) : Nil\n    n = self.size\n    return if size > n\n\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    pool = dup_as_array(self)\n    reuse = Indexable(T).check_reuse(reuse, size)\n    cycles = (n - size + 1..n).to_a.reverse!\n    yield pool_slice(pool, size, reuse)\n\n    while true\n      stop = true\n      i = size - 1\n      while i >= 0\n        ci = (cycles[i] -= 1)\n        if ci == 0\n          e = pool[i]\n          (i + 1).upto(n - 1) { |j| pool[j - 1] = pool[j] }\n          pool[n - 1] = e\n          cycles[i] = n - i\n        else\n          pool.swap i, -ci\n          yield pool_slice(pool, size, reuse)\n          stop = false\n          break\n        end\n        i -= 1\n      end\n\n      return if stop\n    end\n  end\n\n  # Returns an `Iterator` over each possible permutation of *size* of `self`.\n  #\n  # ```\n  # iter = [1, 2, 3].each_permutation\n  # iter.next # => [1, 2, 3]\n  # iter.next # => [1, 3, 2]\n  # iter.next # => [2, 1, 3]\n  # iter.next # => [2, 3, 1]\n  # iter.next # => [3, 1, 2]\n  # iter.next # => [3, 2, 1]\n  # iter.next # => #<Iterator::Stop>\n  # ```\n  #\n  # By default, a new array is created and returned for each permutation.\n  # If *reuse* is given, the array can be reused: if *reuse* is\n  # an `Array`, this array will be reused; if *reuse* if truthy,\n  # the method will create a new array and reuse it. This can be\n  # used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_permutation(size : Int = self.size, reuse = false)\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    PermutationIterator(self, T).new(self, size.to_i, Indexable(T).check_reuse(reuse, size))\n  end\n\n  # Returns an `Array` with all possible combinations of *size* of `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # a.combinations    # => [[1, 2, 3]]\n  # a.combinations(1) # => [[1], [2], [3]]\n  # a.combinations(2) # => [[1, 2], [1, 3], [2, 3]]\n  # a.combinations(3) # => [[1, 2, 3]]\n  # a.combinations(0) # => [[]]\n  # a.combinations(4) # => []\n  # ```\n  def combinations(size : Int = self.size)\n    ary = [] of Array(T)\n    each_combination(size) do |a|\n      ary << a\n    end\n    ary\n  end\n\n  # Yields each possible combination of *size* of `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # sums = [] of Int32\n  # a.each_combination(2) { |p| sums << p.sum } # => nil\n  # sums                                        # => [3, 4, 5]\n  # ```\n  #\n  # By default, a new array is created and yielded for each combination.\n  # If *reuse* is given, the array can be reused: if *reuse* is\n  # an `Array`, this array will be reused; if *reuse* if truthy,\n  # the method will create a new array and reuse it. This can be\n  # used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_combination(size : Int = self.size, reuse = false, &) : Nil\n    n = self.size\n    return if size > n\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    reuse = Indexable(T).check_reuse(reuse, size)\n    copy = self.dup\n    pool = dup_as_array(self)\n\n    indices = (0...size).to_a\n\n    yield pool_slice(pool, size, reuse)\n\n    while true\n      stop = true\n      i = size - 1\n      while i >= 0\n        if indices[i] != i + n - size\n          stop = false\n          break\n        end\n        i -= 1\n      end\n\n      return if stop\n\n      indices[i] += 1\n      pool[i] = copy[indices[i]]\n\n      (i + 1).upto(size - 1) do |j|\n        indices[j] = indices[j - 1] + 1\n        pool[j] = copy[indices[j]]\n      end\n\n      yield pool_slice(pool, size, reuse)\n    end\n  end\n\n  # Returns an `Iterator` over each possible combination of *size* of `self`.\n  #\n  # ```\n  # iter = [1, 2, 3, 4].each_combination(3)\n  # iter.next # => [1, 2, 3]\n  # iter.next # => [1, 2, 4]\n  # iter.next # => [1, 3, 4]\n  # iter.next # => [2, 3, 4]\n  # iter.next # => #<Iterator::Stop>\n  # ```\n  #\n  # By default, a new array is created and returned for each combination.\n  # If *reuse* is given, the array can be reused: if *reuse* is\n  # an `Array`, this array will be reused; if *reuse* if truthy,\n  # the method will create a new array and reuse it. This can be\n  # used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_combination(size : Int = self.size, reuse = false)\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    CombinationIterator(self, T).new(self, size.to_i, Indexable(T).check_reuse(reuse, size))\n  end\n\n  # Returns an `Array` with all possible combinations with repeated elements of\n  # *size* of `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  #\n  # pp a.repeated_combinations\n  # pp a.repeated_combinations(2)\n  # ```\n  #\n  # produces:\n  #\n  # ```text\n  # [[1, 1, 1],\n  #  [1, 1, 2],\n  #  [1, 1, 3],\n  #  [1, 2, 2],\n  #  [1, 2, 3],\n  #  [1, 3, 3],\n  #  [2, 2, 2],\n  #  [2, 2, 3],\n  #  [2, 3, 3],\n  #  [3, 3, 3]]\n  # [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]\n  # ```\n  def repeated_combinations(size : Int = self.size) : Array(Array(T))\n    ary = [] of Array(T)\n    each_repeated_combination(size) do |a|\n      ary << a\n    end\n    ary\n  end\n\n  # Yields each possible combination with repeated elements of *size* of\n  # `self`.\n  #\n  # ```\n  # a = [1, 2, 3]\n  # sums = [] of Int32\n  # a.each_repeated_combination(2) { |p| sums << p.sum } # => nil\n  # sums                                                 # => [2, 3, 4, 4, 5, 6]\n  # ```\n  #\n  # By default, a new array is created and yielded for each combination.\n  # If *reuse* is given, the array can be reused: if *reuse* is\n  # an `Array`, this array will be reused; if *reuse* if truthy,\n  # the method will create a new array and reuse it. This can be\n  # used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_repeated_combination(size : Int = self.size, reuse = false, &) : Nil\n    n = self.size\n    return if size > n && n == 0\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    reuse = Indexable(T).check_reuse(reuse, size)\n    copy = self.dup\n    indices = Array.new(size, 0)\n    pool = indices.map { |i| copy[i] }\n\n    yield pool_slice(pool, size, reuse)\n\n    while true\n      stop = true\n\n      i = size - 1\n      while i >= 0\n        if indices[i] != n - 1\n          stop = false\n          break\n        end\n        i -= 1\n      end\n      return if stop\n\n      ii = indices[i] + 1\n      tmp = copy[ii]\n      indices.fill(i, size - i) { ii }\n      pool.fill(i, size - i) { tmp }\n\n      yield pool_slice(pool, size, reuse)\n    end\n  end\n\n  # Returns an `Iterator` over each possible combination with repeated elements\n  # of *size* of `self`.\n  #\n  # ```\n  # iter = [1, 2, 3].each_repeated_combination(2)\n  # iter.next # => [1, 1]\n  # iter.next # => [1, 2]\n  # iter.next # => [1, 3]\n  # iter.next # => [2, 2]\n  # iter.next # => [2, 3]\n  # iter.next # => [3, 3]\n  # iter.next # => #<Iterator::Stop>\n  # ```\n  #\n  # By default, a new array is created and returned for each combination.\n  # If *reuse* is given, the array can be reused: if *reuse* is\n  # an `Array`, this array will be reused; if *reuse* if truthy,\n  # the method will create a new array and reuse it. This can be\n  # used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_repeated_combination(size : Int = self.size, reuse = false)\n    raise ArgumentError.new(\"Size must be positive\") if size < 0\n\n    RepeatedCombinationIterator(self, T).new(self, size.to_i, Indexable(T).check_reuse(reuse, size))\n  end\n\n  protected def self.check_reuse(reuse, size)\n    if reuse\n      unless reuse.is_a?(Array)\n        reuse = Array(T).new(size)\n      end\n    else\n      reuse = nil\n    end\n    reuse\n  end\n\n  private class ItemIterator(A, T)\n    include Iterator(T)\n\n    def initialize(@array : A, @index = 0)\n    end\n\n    def next\n      if @index >= @array.size\n        stop\n      else\n        value = @array[@index]\n        @index += 1\n        value\n      end\n    end\n  end\n\n  private class ReverseItemIterator(A, T)\n    include Iterator(T)\n\n    def initialize(@array : A, @index : Int32 = array.size - 1)\n    end\n\n    def next\n      if @index < 0\n        stop\n      else\n        value = @array[@index]\n        @index -= 1\n        value\n      end\n    end\n  end\n\n  private class IndexIterator(A)\n    include Iterator(Int32)\n\n    def initialize(@array : A, @index = 0)\n    end\n\n    def next\n      if @index >= @array.size\n        stop\n      else\n        value = @index\n        @index += 1\n        value\n      end\n    end\n  end\n\n  private class PermutationIterator(A, T)\n    include Iterator(Array(T))\n\n    @size : Int32\n    @n : Int32\n    @cycles : Array(Int32)\n    @pool : Array(T)\n    @stop : Bool\n    @i : Int32\n    @first : Bool\n    @reuse : Array(T)?\n\n    def initialize(a : A, @size, @reuse : Array(T)?)\n      @n = a.size\n      @cycles = (@n - @size + 1..@n).to_a.reverse!\n      @pool = dup_as_array(a)\n      @stop = @size > @n\n      @i = @size - 1\n      @first = true\n    end\n\n    def next\n      return stop if @stop\n\n      if @first\n        @first = false\n        return pool_slice(@pool, @size, @reuse)\n      end\n\n      while @i >= 0\n        ci = (@cycles[@i] -= 1)\n        if ci == 0\n          e = @pool[@i]\n          (@i + 1).upto(@n - 1) { |j| @pool[j - 1] = @pool[j] }\n          @pool[@n - 1] = e\n          @cycles[@i] = @n - @i\n        else\n          @pool.swap @i, -ci\n          value = pool_slice(@pool, @size, @reuse)\n          @i = @size - 1\n          return value\n        end\n        @i -= 1\n      end\n\n      @stop = true\n      stop\n    end\n  end\n\n  private class CombinationIterator(A, T)\n    include Iterator(Array(T))\n\n    @size : Int32\n    @n : Int32\n    @copy : Array(T)\n    @pool : Array(T)\n    @indices : Array(Int32)\n    @stop : Bool\n    @i : Int32\n    @first : Bool\n    @reuse : Array(T)?\n\n    def initialize(a : A, @size, @reuse : Array(T)?)\n      @n = a.size\n      @copy = a.dup\n      @pool = dup_as_array(a)\n      @indices = (0...@size).to_a\n      @stop = @size > @n\n      @i = @size - 1\n      @first = true\n    end\n\n    def next\n      return stop if @stop\n\n      if @first\n        @first = false\n        return pool_slice(@pool, @size, @reuse)\n      end\n\n      while @i >= 0\n        if @indices[@i] != @i + @n - @size\n          @indices[@i] += 1\n          @pool[@i] = @copy[@indices[@i]]\n\n          (@i + 1).upto(@size - 1) do |j|\n            @indices[j] = @indices[j - 1] + 1\n            @pool[j] = @copy[@indices[j]]\n          end\n\n          value = pool_slice(@pool, @size, @reuse)\n          @i = @size - 1\n          return value\n        end\n        @i -= 1\n      end\n\n      @stop = true\n      stop\n    end\n  end\n\n  private class RepeatedCombinationIterator(A, T)\n    include Iterator(Array(T))\n\n    @size : Int32\n    @n : Int32\n    @copy : A\n    @indices : Array(Int32)\n    @pool : Array(T)\n    @stop : Bool\n    @i : Int32\n    @first : Bool\n    @reuse : Array(T)?\n\n    def initialize(array : A, @size, @reuse : Array(T)?)\n      @n = array.size\n      @copy = array.dup\n      @indices = Array.new(@size, 0)\n      @pool = @indices.map { |i| @copy[i] }\n      @stop = false\n      @i = @size - 1\n      @first = true\n    end\n\n    def next\n      return stop if @stop\n\n      if @first\n        @first = false\n        return pool_slice(@pool, @size, @reuse)\n      end\n\n      while @i >= 0\n        if @indices[@i] != @n - 1\n          ii = @indices[@i] + 1\n          tmp = @copy[ii]\n          @indices.fill(@i, @size - @i) { ii }\n          @pool.fill(@i, @size - @i) { tmp }\n\n          value = pool_slice(@pool, @size, @reuse)\n          @i = @size - 1\n          return value\n        end\n        @i -= 1\n      end\n\n      @stop = true\n      stop\n    end\n  end\nend\n\nprivate def pool_slice(pool, size, reuse)\n  if reuse\n    reuse.clear\n    size.times { |i| reuse << pool[i] }\n    reuse\n  else\n    pool[0, size]\n  end\nend\n\nprivate def dup_as_array(a)\n  a.is_a?(Array) ? a.dup : a.to_a\nend\n\nrequire \"./indexable/*\"\n"
  },
  {
    "path": "src/ini.cr",
    "content": "# NOTE: To use `INI`, you must explicitly import it with `require \"ini\"`\nmodule INI\n  # Exception thrown on an INI parse error.\n  class ParseException < Exception\n    getter line_number : Int32\n    getter column_number : Int32\n\n    def initialize(message, @line_number, @column_number)\n      super \"#{message} at line #{@line_number}, column #{@column_number}\"\n    end\n\n    def location\n      {line_number, column_number}\n    end\n  end\n\n  # Parses INI-style configuration from the given string.\n  # Raises a `ParseException` on any errors.\n  #\n  # ```\n  # require \"ini\"\n  #\n  # INI.parse(\"[foo]\\na = 1\") # => {\"foo\" => {\"a\" => \"1\"}}\n  # ```\n  def self.parse(string_or_io : String | IO) : Hash(String, Hash(String, String))\n    ini = Hash(String, Hash(String, String)).new\n    current_section = ini[\"\"] = Hash(String, String).new\n    lineno = 0\n\n    string_or_io.each_line do |line|\n      lineno += 1\n      next if line.empty?\n\n      offset = 0\n      line.each_char do |char|\n        break unless char.ascii_whitespace?\n        offset += 1\n      end\n\n      case line[offset]\n      when '#', ';'\n        next\n      when '['\n        end_idx = line.index(']', offset)\n        raise ParseException.new(\"Unterminated section\", lineno, line.size) unless end_idx\n        raise ParseException.new(\"Data after section\", lineno, end_idx + 1) unless end_idx == line.size - 1\n\n        current_section_name = line[offset + 1...end_idx]\n        current_section = ini.put_if_absent(current_section_name) { Hash(String, String).new }\n      else\n        key, eq, value = line.partition('=')\n        raise ParseException.new(\"Expected declaration\", lineno, key.size) if eq != \"=\"\n\n        current_section[key.strip] = value.strip\n      end\n    end\n\n    ini.delete(\"\") if ini[\"\"].empty?\n    ini\n  end\n\n  # Generates an INI-style configuration from a given hash.\n  #\n  # ```\n  # require \"ini\"\n  #\n  # INI.build({\"foo\" => {\"a\" => \"1\"}}, true) # => \"[foo]\\na = 1\\n\\n\"\n  # ```\n  def self.build(ini, space : Bool = false) : String\n    String.build { |str| build str, ini, space }\n  end\n\n  # Appends INI data to the given IO.\n  def self.build(io : IO, ini, space : Bool = false) : Nil\n    # An empty section has to be at first, to prevent being included in another one.\n    ini[\"\"]?.try &.each do |key, value|\n      io << key << (space ? \" = \" : '=') << value << '\\n'\n    end\n    ini.each do |section, contents|\n      next if section.to_s.empty?\n      io << '[' << section << \"]\\n\"\n      contents.each do |key, value|\n        io << key << (space ? \" = \" : '=') << value << '\\n'\n      end\n      io.puts\n    end\n    io.flush\n  end\nend\n"
  },
  {
    "path": "src/int.cr",
    "content": "# Int is the base type of all integer types.\n#\n# There are five signed integer types: `Int8`, `Int16`, `Int32`, `Int64` and\n# `Int128`, being able to represent numbers of 8, 16, 32, 64, and 128 bits respectively.\n# There are five unsigned integer types: `UInt8`, `UInt16`, `UInt32`, `UInt64`\n# and `UInt128.\n#\n# An integer literal is an optional `+` or `-` sign, followed by\n# a sequence of digits and underscores, optionally followed by a suffix.\n# If no suffix is present, the literal's type is `Int32`, or `Int64` if the\n# number doesn't fit into an `Int32`:\n#\n# ```\n# 1 # Int32\n#\n# 1_i8   # Int8\n# 1_i16  # Int16\n# 1_i32  # Int32\n# 1_i64  # Int64\n# 1_i128 # Int128\n#\n# 1_u8   # UInt8\n# 1_u16  # UInt16\n# 1_u32  # UInt32\n# 1_u64  # UInt64\n# 1_u128 # UInt128\n#\n# +10 # Int32\n# -20 # Int32\n#\n# 2147483648 # Int64\n# ```\n#\n# Literals without a suffix that are larger than `Int64::MAX` represent a\n# `UInt64` if the number fits, e.g. `9223372036854775808` and\n# `0x80000000_00000000`. This behavior is deprecated and will become an error in\n# the future.\n#\n# The underscore `_` before the suffix is optional.\n#\n# Underscores can be used to make some numbers more readable:\n#\n# ```\n# 1_000_000 # better than 1000000\n# ```\n#\n# Binary numbers start with `0b`:\n#\n# ```\n# 0b1101 # == 13\n# ```\n#\n# Octal numbers start with `0o`:\n#\n# ```\n# 0o123 # == 83\n# ```\n#\n# Hexadecimal numbers start with `0x`:\n#\n# ```\n# 0xFE012D # == 16646445\n# 0xfe012d # == 16646445\n# ```\n#\n# See [`Integer` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/integers.html) in the language reference.\nstruct Int\n  alias Signed = Int8 | Int16 | Int32 | Int64 | Int128\n  alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64 | UInt128\n  alias Primitive = Signed | Unsigned\n\n  # Returns a `Char` that has the unicode codepoint of `self`.\n  #\n  # Raises `ArgumentError` if this integer's value doesn't fit a char's range\n  # (`0..0xd7ff` and `0xe000..0x10ffff`).\n  #\n  # ```\n  # 97.chr # => 'a'\n  # ```\n  def chr : Char\n    unless 0 <= self <= 0xd7ff || 0xe000 <= self <= Char::MAX_CODEPOINT\n      raise ArgumentError.new(\"0x#{self.to_s(16)} out of char range\")\n    end\n    unsafe_chr\n  end\n\n  def ~\n    self ^ -1\n  end\n\n  # Divides `self` by *other* using floored division.\n  #\n  # In floored division, given two integers x and y:\n  # * q = x / y is rounded toward negative infinity\n  # * r = x % y has the sign of the second argument\n  # * x == q*y + r\n  #\n  # For example:\n  #\n  # ```text\n  #  x     y     x / y     x % y\n  #  5     3       1         2\n  # -5     3      -2         1\n  #  5    -3      -2        -1\n  # -5    -3       1        -2\n  # ```\n  #\n  # Raises if *other* is zero, or if *other* is -1 and\n  # `self` is signed and is the minimum value for that\n  # integer type.\n  def //(other : Int::Primitive)\n    check_div_argument other\n\n    div = unsafe_div other\n    mod = unsafe_mod other\n    div -= 1 if other > 0 ? mod < 0 : mod > 0\n    div\n  end\n\n  # Divides `self` by *other* using truncated division.\n  #\n  # In truncated division, given two integers x and y:\n  # * `q = x.tdiv(y)` is rounded toward zero\n  # * `r = x.remainder(y)` has the sign of x\n  # * `x == q*y + r`\n  #\n  # For example:\n  #\n  # ```text\n  #  x     y     x / y     x % y\n  #  5     3       1         2\n  # -5     3      -1        -2\n  #  5    -3      -1         2\n  # -5    -3       1        -2\n  # ```\n  #\n  # Raises if *other* is `0`, or if *other* is `-1` and\n  # `self` is signed and is the minimum value for that\n  # integer type.\n  def tdiv(other : Int)\n    check_div_argument other\n\n    unsafe_div other\n  end\n\n  private def check_div_argument(other)\n    if other == 0\n      raise DivisionByZeroError.new\n    end\n\n    {% begin %}\n      if self < 0 && self == {{@type}}::MIN && other == -1\n        raise ArgumentError.new \"Overflow: {{@type}}::MIN / -1\"\n      end\n    {% end %}\n  end\n\n  # Returns a `Tuple` of two elements containing the quotient\n  # and modulus obtained by dividing `self` by *number* using\n  # truncated division.\n  #\n  # ```\n  # 11.tdivmod(3)  # => {3, 2}\n  # 11.tdivmod(-3) # => {-4, -1}\n  # ```\n  #\n  # In truncated division, given two integers x and y:\n  # * `q = x.tdiv(y)` is rounded toward zero\n  # * `r = x.remainder(y)` has the sign of x\n  # * `x == q*y + r`\n  #\n  # For example:\n  #\n  # ```text\n  #  x     y     x / y     x % y\n  #  5     3       1         2\n  # -5     3      -1        -2\n  #  5    -3      -1         2\n  # -5    -3       1        -2\n  # ```\n  #\n  # Raises if *other* is `0`, or if *other* is `-1` and\n  # `self` is signed and is the minimum value for that\n  # integer type.\n  def tdivmod(other : Int)\n    check_div_argument other\n\n    {unsafe_div(other), unsafe_mod(other)}\n  end\n\n  def fdiv(other) : Float64\n    to_f / other\n  end\n\n  # Returns `self` modulo *other*.\n  #\n  # This uses floored division.\n  #\n  # See `Int#/` for more details.\n  def %(other : Int)\n    {% begin %}\n      if other == 0\n        raise DivisionByZeroError.new\n      elsif self < 0 && self == {{@type}}::MIN && other == -1\n        self.class.new(0)\n      elsif (self < 0) == (other < 0)\n        self.unsafe_mod(other)\n      else\n        me = self.unsafe_mod(other)\n        me == 0 ? me : me + other\n      end\n    {% end %}\n  end\n\n  # Returns `self` remainder *other*.\n  #\n  # This uses truncated division.\n  #\n  # See `Int#tdiv` for more details.\n  def remainder(other : Int)\n    {% begin %}\n      if other == 0\n        raise DivisionByZeroError.new\n      elsif self < 0 && self == {{@type}}::MIN && other == -1\n        self.class.new(0)\n      else\n        unsafe_mod other\n      end\n    {% end %}\n  end\n\n  # :nodoc:\n  #\n  # Computes (x * y) % z, but without intermediate overflows.\n  # Precondition: `0 <= x < z && y >= 0`\n  def self.mulmod(x, y, z)\n    result = zero\n    while y > 0\n      if y.bits_set?(1)\n        # result = (result + x) % z\n        if result >= z &- x\n          result &-= z &- x\n        else\n          result &+= x\n        end\n      end\n      # x = (x + x) % z\n      if x >= z &- x\n        x &-= z &- x\n      else\n        x = x.unsafe_shl(1)\n      end\n      y = y.unsafe_shr(1)\n    end\n    result\n  end\n\n  # Returns the result of shifting this number's bits *count* positions to the right.\n  # Also known as arithmetic right shift.\n  #\n  # * If *count* is greater than the number of bits of this integer, returns 0\n  # * If *count* is negative, a left shift is performed\n  #\n  # ```\n  # 8000 >> 1  # => 4000\n  # 8000 >> 2  # => 2000\n  # 8000 >> 32 # => 0\n  # 8000 >> -1 # => 16000\n  #\n  # -8000 >> 1 # => -4000\n  # ```\n  def >>(count : Int)\n    if count < 0\n      self << count.abs\n    elsif count < sizeof(self) * 8\n      self.unsafe_shr(count)\n    else\n      self.class.zero\n    end\n  end\n\n  # Returns the result of shifting this number's bits *count* positions to the left.\n  #\n  # * If *count* is greater than the number of bits of this integer, returns 0\n  # * If *count* is negative, a right shift is performed\n  #\n  # ```\n  # 8000 << 1  # => 16000\n  # 8000 << 2  # => 32000\n  # 8000 << 32 # => 0\n  # 8000 << -1 # => 4000\n  # ```\n  def <<(count : Int)\n    if count < 0\n      self >> count.abs\n    elsif count < sizeof(self) * 8\n      self.unsafe_shl(count)\n    else\n      self.class.zero\n    end\n  end\n\n  def <=>(other : Int) : Int32\n    # Override Number#<=> because when comparing\n    # Int vs Int there's no way we can return `nil`\n    self > other ? 1 : (self < other ? -1 : 0)\n  end\n\n  def abs : self\n    self >= 0 ? self : -self\n  end\n\n  def round(mode : RoundingMode) : self\n    self\n  end\n\n  def ceil : self\n    self\n  end\n\n  def floor : self\n    self\n  end\n\n  def trunc : self\n    self\n  end\n\n  # Returns `self`.\n  def round_even : self\n    self\n  end\n\n  # Returns `self`.\n  def round_away\n    self\n  end\n\n  # :inherit:\n  #\n  # Always returns `true` for `Int`.\n  def integer? : Bool\n    true\n  end\n\n  # Returns the value of raising `self` to the power of *exponent*.\n  #\n  # Raises `ArgumentError` if *exponent* is negative: if this is needed,\n  # either use a float base or a float exponent.\n  #\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 2 ** 3  # => 8\n  # 2 ** 0  # => 1\n  # 2 ** -1 # ArgumentError\n  # ```\n  def **(exponent : Int) : self\n    if exponent < 0\n      raise ArgumentError.new \"Cannot raise an integer to a negative integer power, use floats for that\"\n    end\n\n    result = self.class.new(1)\n    k = self\n    while exponent > 0\n      result *= k if exponent & 0b1 != 0\n      exponent = exponent.unsafe_shr(1)\n      k *= k if exponent > 0\n    end\n    result\n  end\n\n  # Returns the value of raising `self` to the power of *exponent*.\n  #\n  # Raises `ArgumentError` if *exponent* is negative: if this is needed,\n  # either use a float base or a float exponent.\n  #\n  # Intermediate multiplication will wrap around silently in case of overflow.\n  #\n  # ```\n  # 2 &** 3  # => 8\n  # 2 &** 0  # => 1\n  # 2 &** -1 # ArgumentError\n  # ```\n  def &**(exponent : Int) : self\n    if exponent < 0\n      raise ArgumentError.new \"Cannot raise an integer to a negative integer power, use floats for that\"\n    end\n\n    result = self.class.new(1)\n    k = self\n    while exponent > 0\n      result &*= k if exponent & 0b1 != 0\n      exponent = exponent.unsafe_shr(1)\n      k &*= k if exponent > 0\n    end\n    result\n  end\n\n  # Returns the value of raising `self` to the power of *exponent*.\n  #\n  # ```\n  # 2 ** 3.0  # => 8.0\n  # 2 ** 0.0  # => 1.0\n  # 2 ** -1.0 # => 0.5\n  # ```\n  def **(exponent : Float) : Float64\n    to_f ** exponent\n  end\n\n  def ===(char : Char)\n    self === char.ord\n  end\n\n  # Returns this number's *bit*th bit, starting with the least-significant.\n  #\n  # ```\n  # 11.bit(0) # => 1\n  # 11.bit(1) # => 1\n  # 11.bit(2) # => 0\n  # 11.bit(3) # => 1\n  # 11.bit(4) # => 0\n  # ```\n  def bit(bit)\n    self >> bit & 1\n  end\n\n  # Returns the requested range of bits\n  #\n  # ```\n  # 0b10011.bits(0..1) # => 0b11\n  # 0b10011.bits(0..2) # => 0b11\n  # 0b10011.bits(0..3) # => 0b11\n  # 0b10011.bits(0..4) # => 0b10011\n  # 0b10011.bits(0..5) # => 0b10011\n  # 0b10011.bits(1..4) # => 0b1001\n  # ```\n  def bits(range : Range)\n    start_index = range.begin\n    if start_index\n      raise IndexError.new(\"Start index (#{start_index}) must be positive\") if start_index < 0\n    else\n      start_index = 0\n    end\n\n    end_index = range.end\n    if end_index\n      raise IndexError.new(\"End index (#{end_index}) must be positive\") if end_index < 0\n      end_index += 1 unless range.exclusive?\n      raise IndexError.new(\"End index (#{end_index}) must be greater than start index (#{start_index})\") if end_index <= start_index\n    else\n      # if there is no end index then we only need to shift\n      return self >> start_index\n    end\n\n    # Generates a mask `count` bits long maintaining the correct type\n    count = end_index - start_index\n    mask = (self.class.new(1) << count) &- 1\n\n    if self < 0\n      # Special case for negative to ensure the shift and mask work as expected\n      # The result is always negative\n      offset = (~self) >> start_index\n      result = offset & mask\n      ~result\n    else\n      # Shifts out the bits we want to ignore before applying the mask\n      offset = self >> start_index\n      offset & mask\n    end\n  end\n\n  # Returns `true` if all bits in *mask* are set on `self`.\n  #\n  # ```\n  # 0b0110.bits_set?(0b0110) # => true\n  # 0b1101.bits_set?(0b0111) # => false\n  # 0b1101.bits_set?(0b1100) # => true\n  # ```\n  def bits_set?(mask) : Bool\n    (self & mask) == mask\n  end\n\n  # Returns the number of bits of this int value.\n  #\n  # “The number of bits” means that the bit position of the highest bit\n  # which is different to the sign bit.\n  # (The bit position of the bit 2**n is n+1.)\n  # If there is no such bit (zero or minus one), zero is returned.\n  #\n  # I.e. This method returns `ceil(log2(self < 0 ? -self : self + 1))`.\n  #\n  # ```\n  # 0.bit_length # => 0\n  # 1.bit_length # => 1\n  # 2.bit_length # => 2\n  # 3.bit_length # => 2\n  # 4.bit_length # => 3\n  # 5.bit_length # => 3\n  #\n  # # The above is the same as\n  # 0b0.bit_length   # => 0\n  # 0b1.bit_length   # => 1\n  # 0b10.bit_length  # => 2\n  # 0b11.bit_length  # => 2\n  # 0b100.bit_length # => 3\n  # 0b101.bit_length # => 3\n  # ```\n  def bit_length : Int32\n    x = self < 0 ? ~self : self\n\n    if x.is_a?(Int::Primitive)\n      Int32.new(sizeof(self) * 8 - x.leading_zeros_count)\n    else\n      # Safe fallback for any non-primitive Int type\n      to_s(2).size\n    end\n  end\n\n  # :nodoc:\n  def next_power_of_two : self\n    one = self.class.new!(1)\n\n    bits = sizeof(self) * 8\n    shift = bits &- (self &- 1).leading_zeros_count\n    if self.is_a?(Int::Signed)\n      shift = 0 if shift >= bits &- 1\n    else\n      shift = 0 if shift == bits\n    end\n\n    result = one << shift\n    result >= self ? result : raise OverflowError.new\n  end\n\n  # Returns the greatest common divisor of `self` and *other*. Signed\n  # integers may raise `OverflowError` if either has value equal to `MIN` of\n  # its type.\n  #\n  # ```\n  # 5.gcd(10) # => 5\n  # 5.gcd(7)  # => 1\n  # ```\n  def gcd(other : self) : self\n    # Implementation heavily inspired by\n    # https://en.wikipedia.org/wiki/Binary_GCD_algorithm#Iterative_version_in_C\n    u = self.abs\n    v = other.abs\n    return v if u == 0\n    return u if v == 0\n\n    # Let shift := lg K, where K is the greatest power of 2\n    # dividing both u and v.\n    shift = (u | v).trailing_zeros_count\n    u = u.unsafe_shr(u.trailing_zeros_count)\n\n    # From here on, u is always odd.\n    loop do\n      # remove all factors of 2 in v -- they are not common\n      v = v.unsafe_shr(v.trailing_zeros_count)\n\n      # Now u and v are both odd. Swap if necessary so u <= v,\n      # then set v = v - u (which is even).\n      u, v = v, u if u > v\n      v &-= u\n      break if v.zero?\n    end\n\n    # restore common factors of 2\n    u.unsafe_shl shift\n  end\n\n  # Returns the least common multiple of `self` and *other*.\n  #\n  # Raises `OverflowError` in case of overflow.\n  def lcm(other : Int)\n    (self // gcd(other) * other).abs\n  end\n\n  def divisible_by?(num) : Bool\n    remainder(num) == 0\n  end\n\n  def even? : Bool\n    divisible_by? 2\n  end\n\n  def odd? : Bool\n    !even?\n  end\n\n  def succ : self\n    self + 1\n  end\n\n  def pred : self\n    self - 1\n  end\n\n  def times(&block : self ->) : Nil\n    i = self ^ self\n    while i < self\n      yield i\n      i &+= 1\n    end\n  end\n\n  def times\n    TimesIterator(typeof(self)).new(self)\n  end\n\n  def upto(to, &block : self ->) : Nil\n    return unless self <= to\n    x = self\n    while true\n      yield x\n      return unless x < to\n      x += 1\n    end\n  end\n\n  def upto(to)\n    UptoIterator(typeof(self), typeof(to)).new(self, to)\n  end\n\n  # Calls the given block with each integer value from self down to `to`.\n  #\n  # ```\n  # 3.downto(1) do |i|\n  #   puts i\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # 3\n  # 2\n  # 1\n  # ```\n  def downto(to, &block : self ->) : Nil\n    return unless self >= to\n    x = self\n    while true\n      yield x\n      return unless x > to\n      x -= 1\n    end\n  end\n\n  # Get an iterator for counting down from self to `to`.\n  def downto(to)\n    DowntoIterator(typeof(self), typeof(to)).new(self, to)\n  end\n\n  def to(to, &block : self ->) : Nil\n    if self < to\n      upto(to) { |i| yield i }\n    elsif self > to\n      downto(to) { |i| yield i }\n    else\n      yield self\n    end\n  end\n\n  def to(to)\n    self <= to ? upto(to) : downto(to)\n  end\n\n  def modulo(other)\n    self % other\n  end\n\n  # Returns the digits of a number in a given base.\n  # The digits are returned as an array with the least significant digit as the first array element.\n  #\n  # ```\n  # 12345.digits      # => [5, 4, 3, 2, 1]\n  # 12345.digits(7)   # => [4, 6, 6, 0, 5]\n  # 12345.digits(100) # => [45, 23, 1]\n  #\n  # -12345.digits(7) # => ArgumentError\n  # ```\n  def digits(base = 10) : Array(Int32)\n    if base < 2\n      raise ArgumentError.new(\"Invalid base #{base}\")\n    end\n\n    if self < 0\n      raise ArgumentError.new(\"Can't request digits of negative number\")\n    end\n\n    if self == 0\n      return [0]\n    end\n\n    num = self\n\n    digits_count = (Math.log(self.to_f + 1) / Math.log(base)).ceil.to_i\n\n    ary = Array(Int32).new(digits_count)\n    while num != 0\n      ary << num.remainder(base).to_i\n      num = num.tdiv(base)\n    end\n    ary\n  end\n\n  private DIGITS_DOWNCASE = \"0123456789abcdefghijklmnopqrstuvwxyz\"\n  private DIGITS_UPCASE   = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n  private DIGITS_BASE62   = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\n  # Returns a string representation of this integer.\n  #\n  # *base* specifies the radix of the returned string, and must be either 62 or\n  # a number between 2 and 36. By default, digits above 9 are represented by\n  # ASCII lowercase letters (`a` for 10, `b` for 11, etc.), but uppercase\n  # letters may be used if *upcase* is `true`, unless base 62 is used. In that\n  # case, lowercase letters are used for 10 to 35, and uppercase ones for 36 to\n  # 61, and *upcase* must be `false`.\n  #\n  # *precision* specifies the minimum number of digits in the returned string.\n  # If there are fewer digits than this number, the string is left-padded by\n  # zeros. If `self` and *precision* are both zero, returns an empty string.\n  #\n  # ```\n  # 1234.to_s                   # => \"1234\"\n  # 1234.to_s(2)                # => \"10011010010\"\n  # 1234.to_s(16)               # => \"4d2\"\n  # 1234.to_s(16, upcase: true) # => \"4D2\"\n  # 1234.to_s(36)               # => \"ya\"\n  # 1234.to_s(62)               # => \"jU\"\n  # 1234.to_s(precision: 2)     # => \"1234\"\n  # 1234.to_s(precision: 6)     # => \"001234\"\n  # ```\n  def to_s(base : Int = 10, *, precision : Int = 1, upcase : Bool = false) : String\n    raise ArgumentError.new(\"Invalid base #{base}\") unless 2 <= base <= 36 || base == 62\n    raise ArgumentError.new(\"upcase must be false for base 62\") if upcase && base == 62\n    raise ArgumentError.new(\"Precision must be non-negative\") unless precision >= 0\n\n    case {self, precision}\n    when {0, 0}\n      \"\"\n    when {0, 1}\n      \"0\"\n    when {1, 1}\n      \"1\"\n    else\n      internal_to_s(base, precision, upcase) do |ptr, count, negative|\n        # reuse the `chars` buffer in `internal_to_s` if possible\n        if precision <= count || precision <= 128\n          if precision > count\n            difference = precision - count\n            ptr -= difference\n            Slice.new(ptr, difference).fill('0'.ord.to_u8)\n            count += difference\n          end\n\n          if negative\n            ptr -= 1\n            ptr.value = '-'.ord.to_u8\n            count += 1\n          end\n\n          String.new(ptr, count, count)\n        else\n          len = precision + (negative ? 1 : 0)\n          String.new(len) do |buffer|\n            if negative\n              buffer.value = '-'.ord.to_u8\n              buffer += 1\n            end\n\n            Slice.new(buffer, precision - count).fill('0'.ord.to_u8)\n            ptr.copy_to(buffer + precision - count, count)\n            {len, len}\n          end\n        end\n      end\n    end\n  end\n\n  # Appends a string representation of this integer to the given *io*.\n  #\n  # *base* specifies the radix of the written string, and must be either 62 or\n  # a number between 2 and 36. By default, digits above 9 are represented by\n  # ASCII lowercase letters (`a` for 10, `b` for 11, etc.), but uppercase\n  # letters may be used if *upcase* is `true`, unless base 62 is used. In that\n  # case, lowercase letters are used for 10 to 35, and uppercase ones for 36 to\n  # 61, and *upcase* must be `false`.\n  #\n  # *precision* specifies the minimum number of digits in the written string.\n  # If there are fewer digits than this number, the string is left-padded by\n  # zeros. If `self` and *precision* are both zero, returns an empty string.\n  def to_s(io : IO, base : Int = 10, *, precision : Int = 1, upcase : Bool = false) : Nil\n    raise ArgumentError.new(\"Invalid base #{base}\") unless 2 <= base <= 36 || base == 62\n    raise ArgumentError.new(\"upcase must be false for base 62\") if upcase && base == 62\n    raise ArgumentError.new(\"Precision must be non-negative\") unless precision >= 0\n\n    case {self, precision}\n    when {0, 0}\n      # do nothing\n    when {0, 1}\n      io << '0'\n    when {1, 1}\n      io << '1'\n    else\n      internal_to_s(base, precision, upcase) do |ptr, count, negative|\n        io << '-' if negative\n        if precision > count\n          (precision - count).times { io << '0' }\n        end\n        io.write_string Slice.new(ptr, count)\n      end\n    end\n  end\n\n  private def internal_to_s(base, precision, upcase = false, &)\n    # Given sizeof(self) <= 128 bits, we need at most 128 bytes for a base 2\n    # representation, plus one byte for the negative sign (possibly used by the\n    # string-returning overload).\n    chars = uninitialized UInt8[129]\n    ptr_end = chars.to_unsafe + 129\n    ptr = ptr_end\n    num = self\n\n    neg = num < 0\n\n    digits = (base == 62 ? DIGITS_BASE62 : (upcase ? DIGITS_UPCASE : DIGITS_DOWNCASE)).to_unsafe\n\n    while num != 0\n      ptr -= 1\n      ptr.value = digits[num.remainder(base).abs]\n      num = num.tdiv(base)\n    end\n\n    count = (ptr_end - ptr).to_i32\n    yield ptr, count, neg\n  end\n\n  # Writes this integer to the given *io* in the given *format*.\n  #\n  # See also: `IO#write_bytes`.\n  def to_io(io : IO, format : IO::ByteFormat) : Nil\n    format.encode(self, io)\n  end\n\n  # Reads an integer from the given *io* in the given *format*.\n  #\n  # See also: `IO#read_bytes`.\n  def self.from_io(io : IO, format : IO::ByteFormat) : self\n    format.decode(self, io)\n  end\n\n  # Counts `1`-bits in the binary representation of this integer.\n  #\n  # ```\n  # 5.popcount   # => 2\n  # -15.popcount # => 29\n  # ```\n  abstract def popcount\n\n  # Returns the number of trailing `0`-bits.\n  abstract def trailing_zeros_count\n\n  private class TimesIterator(T)\n    include Iterator(T)\n\n    @n : T\n    @index : T\n\n    def initialize(@n : T, @index = T.zero)\n    end\n\n    def next\n      if @index < @n\n        value = @index\n        @index &+= 1\n        value\n      else\n        stop\n      end\n    end\n  end\n\n  private class UptoIterator(T, N)\n    include Iterator(T)\n\n    @from : T\n    @to : N\n    @current : T\n    @done : Bool\n\n    def initialize(@from : T, @to : N)\n      @current = @from\n      @done = !(@from <= @to)\n    end\n\n    def next\n      return stop if @done\n      value = @current\n      @done = @current == @to\n      @current += 1 unless @done\n      value\n    end\n  end\n\n  private class DowntoIterator(T, N)\n    include Iterator(T)\n\n    @from : T\n    @to : N\n    @current : T\n    @done : Bool\n\n    def initialize(@from : T, @to : N)\n      @current = @from\n      @done = !(@from >= @to)\n    end\n\n    def next\n      return stop if @done\n      value = @current\n      @done = @current == @to\n      @current -= 1 unless @done\n      value\n    end\n  end\nend\n\nstruct Int8\n  MIN = -128_i8\n  MAX =  127_i8\n\n  # Returns an `Int8` by invoking `to_i8` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # Int8.new \"20\"                        # => 20\n  # Int8.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Int8: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_i8 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `Int8` by invoking `to_i8` on *value*.\n  def self.new(value) : self\n    value.to_i8\n  end\n\n  # Returns an `Int8` by invoking `to_i8!` on *value*.\n  def self.new!(value) : self\n    value.to_i8!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def - : Int8\n    0_i8 - self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int8\n    self\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int8\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt8\n    to_u8\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt8\n    to_u8!\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : UInt8\n    self < 0 ? 0_u8 &- self : to_u8!\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : self\n    -self\n  end\n\n  def popcount : Int8\n    Intrinsics.popcount8(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse8(self).to_i8!\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x12_i8.byte_swap # => 0x12\n  # ```\n  def byte_swap : self\n    self\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading8(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing8(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl8(self, self, n.to_i8!).to_i8!\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr8(self, self, n.to_i8!).to_i8!\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct Int16\n  MIN = -32768_i16\n  MAX =  32767_i16\n\n  # Returns an `Int16` by invoking `to_i16` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # Int16.new \"20\"                        # => 20\n  # Int16.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Int16: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_i16 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `Int16` by invoking `to_i16` on *value*.\n  def self.new(value) : self\n    value.to_i16\n  end\n\n  # Returns an `Int16` by invoking `to_i16!` on *value*.\n  def self.new!(value) : self\n    value.to_i16!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def - : Int16\n    0_i16 - self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int16\n    self\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int16\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt16\n    to_u16\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt16\n    to_u16!\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : UInt16\n    self < 0 ? 0_u16 &- self : to_u16!\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : self\n    -self\n  end\n\n  def popcount : Int16\n    Intrinsics.popcount16(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse16(self).to_i16!\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x1234_i16.byte_swap # => 0x3412\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap16(self).to_i16!\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading16(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing16(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl16(self, self, n.to_i16!).to_i16!\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr16(self, self, n.to_i16!).to_i16!\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct Int32\n  MIN = -2147483648_i32\n  MAX =  2147483647_i32\n\n  # Returns an `Int32` by invoking `to_i32` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # Int32.new \"20\"                        # => 20\n  # Int32.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Int32: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_i32 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `Int32` by invoking `to_i32` on *value*.\n  def self.new(value) : self\n    value.to_i32\n  end\n\n  # Returns an `Int32` by invoking `to_i32!` on *value*.\n  def self.new!(value) : self\n    value.to_i32!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def - : Int32\n    0 - self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int32\n    self\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int32\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt32\n    to_u32\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt32\n    to_u32!\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : UInt32\n    self < 0 ? 0_u32 &- self : to_u32!\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : self\n    -self\n  end\n\n  def popcount : Int32\n    Intrinsics.popcount32(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse32(self).to_i32!\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x12345678_i32.byte_swap # => 0x78563412\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap32(self).to_i32!\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading32(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing32(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl32(self, self, n.to_i32!).to_i32!\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr32(self, self, n.to_i32!).to_i32!\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct Int64\n  MIN = -9223372036854775808_i64\n  MAX =  9223372036854775807_i64\n\n  # Returns an `Int64` by invoking `to_i64` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # Int64.new \"20\"                        # => 20\n  # Int64.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Int64: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_i64 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `Int64` by invoking `to_i64` on *value*.\n  def self.new(value) : self\n    value.to_i64\n  end\n\n  # Returns an `Int64` by invoking `to_i64!` on *value*.\n  def self.new!(value) : self\n    value.to_i64!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def - : Int64\n    0_i64 - self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int64\n    self\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int64\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt64\n    to_u64\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt64\n    to_u64!\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : UInt64\n    self < 0 ? 0_u64 &- self : to_u64!\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : self\n    -self\n  end\n\n  def popcount : Int64\n    Intrinsics.popcount64(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse64(self).to_i64!\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x12345678_i64.byte_swap         # => 0x7856341200000000\n  # 0x123456789ABCDEF0_i64.byte_swap # => -0xf21436587a9cbee\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap64(self).to_i64!\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading64(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing64(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl64(self, self, n.to_i64!).to_i64!\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr64(self, self, n.to_i64!).to_i64!\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct Int128\n  # TODO: eventually update to literals once UInt128 bit support is finished\n  MIN = new(1) << 127\n  MAX = ~MIN\n\n  # Returns an `Int128` by invoking `to_i128` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # Int128.new \"20\"                        # => 20\n  # Int128.new \"  20  \", whitespace: false # raises ArgumentError: Invalid Int128: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_i128 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `Int128` by invoking `to_i128` on *value*.\n  def self.new(value) : self\n    value.to_i128\n  end\n\n  # Returns an `Int128` by invoking `to_i128!` on *value*.\n  def self.new!(value) : self\n    value.to_i128!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def -\n    # TODO: use 0_i128 - self\n    Int128.new(0) - self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int128\n    self\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int128\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt128\n    to_u128\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt128\n    to_u128!\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : UInt128\n    self < 0 ? UInt128.new(0) &- self : to_u128!\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : self\n    -self\n  end\n\n  def popcount\n    Intrinsics.popcount128(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse128(self).to_i128!\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x123456789_i128.byte_swap # ＝> -0x7698badcff0000000000000000000000\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap128(self).to_i128!\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading128(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing128(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl128(self, self, n.to_i128!).to_i128!\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr128(self, self, n.to_i128!).to_i128!\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct UInt8\n  MIN =   0_u8\n  MAX = 255_u8\n\n  # Returns an `UInt8` by invoking `to_u8` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # UInt8.new \"20\"                        # => 20\n  # UInt8.new \"  20  \", whitespace: false # raises ArgumentError: Invalid UInt8: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_u8 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `UInt8` by invoking `to_u8` on *value*.\n  def self.new(value) : self\n    value.to_u8\n  end\n\n  # Returns an `UInt8` by invoking `to_u8!` on *value*.\n  def self.new!(value) : self\n    value.to_u8!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def &- : UInt8\n    0_u8 &- self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int8\n    to_i8\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int8\n    to_i8!\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt8\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt8\n    self\n  end\n\n  def abs : self\n    self\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : self\n    self\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : Int8\n    0_i8 - self\n  end\n\n  def popcount : Int8\n    Intrinsics.popcount8(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse8(self)\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x12_u8.byte_swap # => 0x12\n  # ```\n  def byte_swap : self\n    self\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading8(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing8(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl8(self, self, n.to_u8!)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr8(self, self, n.to_u8!)\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct UInt16\n  MIN =     0_u16\n  MAX = 65535_u16\n\n  # Returns an `UInt16` by invoking `to_u16` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # UInt16.new \"20\"                        # => 20\n  # UInt16.new \"  20  \", whitespace: false # raises ArgumentError: Invalid UInt16: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_u16 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `UInt16` by invoking `to_u16` on *value*.\n  def self.new(value) : self\n    value.to_u16\n  end\n\n  # Returns an `UInt16` by invoking `to_u16!` on *value*.\n  def self.new!(value) : self\n    value.to_u16!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def &- : UInt16\n    0_u16 &- self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int16\n    to_i16\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int16\n    to_i16!\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt16\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt16\n    self\n  end\n\n  def abs : self\n    self\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : self\n    self\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : Int16\n    0_i16 - self\n  end\n\n  def popcount : Int16\n    Intrinsics.popcount16(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse16(self)\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x1234_u16.byte_swap # => 0x3412\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap16(self)\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading16(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing16(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl16(self, self, n.to_u16!)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr16(self, self, n.to_u16!)\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct UInt32\n  MIN =          0_u32\n  MAX = 4294967295_u32\n\n  # Returns an `UInt32` by invoking `to_u32` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # UInt32.new \"20\"                        # => 20\n  # UInt32.new \"  20  \", whitespace: false # raises ArgumentError: Invalid UInt32: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_u32 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `UInt32` by invoking `to_u32` on *value*.\n  def self.new(value) : self\n    value.to_u32\n  end\n\n  # Returns an `UInt32` by invoking `to_u32!` on *value*.\n  def self.new!(value) : self\n    value.to_u32!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def &- : UInt32\n    0_u32 &- self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int32\n    to_i32\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int32\n    to_i32!\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt32\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt32\n    self\n  end\n\n  def abs : self\n    self\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : self\n    self\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : Int32\n    0_i32 - self\n  end\n\n  def popcount : Int32\n    Intrinsics.popcount32(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse32(self)\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x12345678_u32.byte_swap # => 0x78563412\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap32(self)\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading32(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing32(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl32(self, self, n.to_u32!)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr32(self, self, n.to_u32!)\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct UInt64\n  MIN =                    0_u64\n  MAX = 18446744073709551615_u64\n\n  # Returns an `UInt64` by invoking `to_u64` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # UInt64.new \"20\"                        # => 20\n  # UInt64.new \"  20  \", whitespace: false # raises ArgumentError: Invalid UInt64: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_u64 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `UInt64` by invoking `to_u64` on *value*.\n  def self.new(value) : self\n    value.to_u64\n  end\n\n  # Returns an `UInt64` by invoking `to_u64!` on *value*.\n  def self.new!(value) : self\n    value.to_u64!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def &- : UInt64\n    0_u64 &- self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int64\n    to_i64\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int64\n    to_i64!\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt64\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt64\n    self\n  end\n\n  def abs : self\n    self\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : self\n    self\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : Int64\n    0_i64 - self\n  end\n\n  def popcount : Int64\n    Intrinsics.popcount64(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse64(self)\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x123456789ABCDEF0_u64.byte_swap # => 0xF0DEBC9A78563412\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap64(self)\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading64(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing64(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl64(self, self, n.to_u64!)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr64(self, self, n.to_u64!)\n  end\n\n  def clone\n    self\n  end\nend\n\nstruct UInt128\n  # TODO: eventually update to literals once UInt128 bit support is finished\n  MIN = new 0\n  MAX = ~MIN\n\n  # Returns an `UInt128` by invoking `to_u128` on *value*.\n  # See `String#to_i` for more details.\n  #\n  # ```\n  # UInt128.new \"20\"                        # => 20\n  # UInt128.new \"  20  \", whitespace: false # raises ArgumentError: Invalid UInt128: \"  20  \"\n  # ```\n  def self.new(value : String, base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : self\n    value.to_u128 base: base, whitespace: whitespace, underscore: underscore, prefix: prefix, strict: strict, leading_zero_is_octal: leading_zero_is_octal\n  end\n\n  # Returns an `UInt128` by invoking `to_u128` on *value*.\n  def self.new(value) : self\n    value.to_u128\n  end\n\n  # Returns an `UInt128` by invoking `to_u128!` on *value*.\n  def self.new!(value) : self\n    value.to_u128!\n  end\n\n  Number.expand_div [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128], Float64\n  Number.expand_div [Float32], Float32\n  Number.expand_div [Float64], Float64\n\n  def &-\n    # TODO: use 0_u128 &- self\n    UInt128.new(0) &- self\n  end\n\n  # Returns `self` converted to a signed value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_u32.to_signed # => 1_i32\n  # 2_u16.to_signed # => 2_i16\n  # 3_i64.to_signed # => 3_i64\n  # ```\n  def to_signed : Int128\n    to_i128\n  end\n\n  # Returns `self` converted to a signed value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Signed`.\n  #\n  # ```\n  # 1_u32.to_signed!     # => 1_i32\n  # 65530_u16.to_signed! # => -6_i16\n  # 3_i64.to_signed!     # => 3_i64\n  # ```\n  def to_signed! : Int128\n    to_i128!\n  end\n\n  # Returns `self` converted to an unsigned value of the same size.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  # Raises `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.to_unsigned # => 1_u32\n  # 2_i16.to_unsigned # => 2_u16\n  # 3_u64.to_unsigned # => 3_u64\n  # ```\n  def to_unsigned : UInt128\n    self\n  end\n\n  # Returns `self` converted to an unsigned value of the same size, wrapping in\n  # case of overflow.\n  #\n  # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.\n  #\n  # ```\n  # 1_i32.to_unsigned!    # => 1_u32\n  # (-6_i16).to_unsigned! # => 65530_u16\n  # 3_u64.to_unsigned!    # => 3_u64\n  # ```\n  def to_unsigned! : UInt128\n    self\n  end\n\n  def abs\n    self\n  end\n\n  # Returns the absolute value of `self` as an unsigned value of the same size.\n  #\n  # Returns `self` if `self` is already an `Int::Unsigned`. This method never\n  # overflows.\n  #\n  # ```\n  # 1_u32.abs_unsigned      # => 1_u32\n  # 2_i32.abs_unsigned      # => 2_u32\n  # -3_i8.abs_unsigned      # => 3_u8\n  # Int16::MIN.abs_unsigned # => 32768_u16\n  # ```\n  def abs_unsigned : self\n    self\n  end\n\n  # Returns the negative of `self` as a signed value of the same size.\n  #\n  # Returns `-self` if `self` is already an `Int::Signed`. Raises\n  # `OverflowError` in case of overflow.\n  #\n  # ```\n  # 1_i32.neg_signed      # => -1_i32\n  # 2_u16.neg_signed      # => -2_i16\n  # 128_u8.neg_signed     # => -128_i8\n  # Int16::MIN.neg_signed # raises OverflowError\n  # ```\n  def neg_signed : Int128\n    Int128.new(0) - self\n  end\n\n  def popcount\n    Intrinsics.popcount128(self)\n  end\n\n  # Reverses the bits of `self`; the least significant bit becomes the most\n  # significant, and vice-versa.\n  #\n  # ```\n  # 0b01001011_u8.bit_reverse          # => 0b11010010\n  # 0b1100100001100111_u16.bit_reverse # => 0b1110011000010011\n  # ```\n  def bit_reverse : self\n    Intrinsics.bitreverse128(self)\n  end\n\n  # Swaps the bytes of `self`; a little-endian value becomes a big-endian value,\n  # and vice-versa. The bit order within each byte is unchanged.\n  #\n  # Has no effect on 8-bit integers.\n  #\n  # ```\n  # 0x123456789ABCDEF013579BDF2468ACE0_u128.byte_swap # ＝> 0xE0AC6824DF9B5713F0DEBC9A78563412\n  # ```\n  def byte_swap : self\n    Intrinsics.bswap128(self)\n  end\n\n  # Returns the number of leading `0`-bits.\n  def leading_zeros_count\n    Intrinsics.countleading128(self, false)\n  end\n\n  def trailing_zeros_count\n    Intrinsics.counttrailing128(self, false)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the most significant\n  # bit's direction. Negative shifts are equivalent to `rotate_right(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_left(3)  # => 0b01101010\n  # 0b01001101_u8.rotate_left(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_left(11) # => 0b01101010\n  # 0b01001101_u8.rotate_left(-1) # => 0b10100110\n  # ```\n  def rotate_left(n : Int) : self\n    Intrinsics.fshl128(self, self, n.to_u128!)\n  end\n\n  # Returns the bitwise rotation of `self` *n* times in the least significant\n  # bit's direction. Negative shifts are equivalent to `rotate_left(-n)`.\n  #\n  # ```\n  # 0b01001101_u8.rotate_right(3)  # => 0b10101001\n  # 0b01001101_u8.rotate_right(8)  # => 0b01001101\n  # 0b01001101_u8.rotate_right(11) # => 0b10101001\n  # 0b01001101_u8.rotate_right(-1) # => 0b10011010\n  # ```\n  def rotate_right(n : Int) : self\n    Intrinsics.fshr128(self, self, n.to_u128!)\n  end\n\n  def clone\n    self\n  end\nend\n\n{% for type in %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128) %}\n  # Returns a number for given digits and base.\n  # The digits are expected as an `Enumerable` with the least significant digit as the first element.\n  #\n  # Base must not be less than 2.\n  #\n  # All digits must be within 0...base.\n  #\n  # ```\n  # {{type.id}}.from_digits([5, 4, 3, 2, 1])          # => 12345\n  # {{type.id}}.from_digits([4, 6, 6, 0, 5], base: 7) # => 12345\n  # {{type.id}}.from_digits([45, 23, 1], base: 100)   # => 12345\n  #\n  # {{type.id}}.from_digits([1], base: -2) # raises ArgumentError\n  # {{type.id}}.from_digits([-1])          # raises ArgumentError\n  # {{type.id}}.from_digits([3], base: 2)  # raises ArgumentError\n  # ```\n  def {{type.id}}.from_digits(digits : Enumerable(Int), base : Int = 10) : self\n    if base < 2\n      raise ArgumentError.new(\"Invalid base #{base}\")\n    end\n\n    num : {{type.id}} = 0\n    multiplier : {{type.id}} = 1\n    first_element = true\n\n    digits.each do |digit|\n      if digit < 0\n        raise ArgumentError.new(\"Invalid digit #{digit}\")\n      end\n\n      if digit >= base\n        raise ArgumentError.new(\"Invalid digit #{digit} for base #{base}\")\n      end\n\n      # don't calculate multiplier upfront for the next digit\n      # to avoid overflow at the last iteration\n      if first_element\n        first_element = false\n      else\n        multiplier *= base\n      end\n\n      num += digit * multiplier\n    end\n\n    num\n  end\n{% end %}\n"
  },
  {
    "path": "src/intrinsics.cr",
    "content": "# Intrinsics as exported by LLVM.\n# Use `Intrinsics` to have a unified API across LLVM versions.\nlib LibIntrinsics\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_debugtrap)] {% end %}\n  fun debugtrap = \"llvm.debugtrap\"\n\n  {% if flag?(:avr) %}\n    {% if compare_versions(Crystal::LLVM_VERSION, \"15.0.0\") < 0 %}\n      fun memcpy = \"llvm.memcpy.p0i8.p0i8.i16\"(dest : Void*, src : Void*, len : UInt16, is_volatile : Bool)\n      fun memmove = \"llvm.memmove.p0i8.p0i8.i16\"(dest : Void*, src : Void*, len : UInt16, is_volatile : Bool)\n      fun memset = \"llvm.memset.p0i8.i16\"(dest : Void*, val : UInt8, len : UInt16, is_volatile : Bool)\n    {% else %}\n      fun memcpy = \"llvm.memcpy.p0.p0.i16\"(dest : Void*, src : Void*, len : UInt16, is_volatile : Bool)\n      fun memmove = \"llvm.memmove.p0.p0.i16\"(dest : Void*, src : Void*, len : UInt16, is_volatile : Bool)\n      fun memset = \"llvm.memset.p0.i16\"(dest : Void*, val : UInt8, len : UInt16, is_volatile : Bool)\n    {% end %}\n  {% else %}\n    {% if flag?(:bits64) %}\n      {% if compare_versions(Crystal::LLVM_VERSION, \"15.0.0\") < 0 %}\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %}\n        fun memcpy = \"llvm.memcpy.p0i8.p0i8.i64\"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %}\n        fun memmove = \"llvm.memmove.p0i8.p0i8.i64\"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %}\n        fun memset = \"llvm.memset.p0i8.i64\"(dest : Void*, val : UInt8, len : UInt64, is_volatile : Bool)\n      {% else %}\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %}\n        fun memcpy = \"llvm.memcpy.p0.p0.i64\"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %}\n        fun memmove = \"llvm.memmove.p0.p0.i64\"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %}\n        fun memset = \"llvm.memset.p0.i64\"(dest : Void*, val : UInt8, len : UInt64, is_volatile : Bool)\n      {% end %}\n    {% else %}\n      {% if compare_versions(Crystal::LLVM_VERSION, \"15.0.0\") < 0 %}\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %}\n        fun memcpy = \"llvm.memcpy.p0i8.p0i8.i32\"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %}\n        fun memmove = \"llvm.memmove.p0i8.p0i8.i32\"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %}\n        fun memset = \"llvm.memset.p0i8.i32\"(dest : Void*, val : UInt8, len : UInt32, is_volatile : Bool)\n      {% else %}\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %}\n        fun memcpy = \"llvm.memcpy.p0.p0.i32\"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %}\n        fun memmove = \"llvm.memmove.p0.p0.i32\"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool)\n\n        {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %}\n        fun memset = \"llvm.memset.p0.i32\"(dest : Void*, val : UInt8, len : UInt32, is_volatile : Bool)\n      {% end %}\n    {% end %}\n  {% end %}\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_read_cycle_counter)] {% end %}\n  fun read_cycle_counter = \"llvm.readcyclecounter\" : UInt64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bitreverse8)] {% end %}\n  fun bitreverse8 = \"llvm.bitreverse.i8\"(id : UInt8) : UInt8\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bitreverse16)] {% end %}\n  fun bitreverse16 = \"llvm.bitreverse.i16\"(id : UInt16) : UInt16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bitreverse32)] {% end %}\n  fun bitreverse32 = \"llvm.bitreverse.i32\"(id : UInt32) : UInt32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bitreverse64)] {% end %}\n  fun bitreverse64 = \"llvm.bitreverse.i64\"(id : UInt64) : UInt64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bitreverse128)] {% end %}\n  fun bitreverse128 = \"llvm.bitreverse.i128\"(id : UInt128) : UInt128\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bswap16)] {% end %}\n  fun bswap16 = \"llvm.bswap.i16\"(id : UInt16) : UInt16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bswap32)] {% end %}\n  fun bswap32 = \"llvm.bswap.i32\"(id : UInt32) : UInt32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bswap64)] {% end %}\n  fun bswap64 = \"llvm.bswap.i64\"(id : UInt64) : UInt64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_bswap128)] {% end %}\n  fun bswap128 = \"llvm.bswap.i128\"(id : UInt128) : UInt128\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_popcount8)] {% end %}\n  fun popcount8 = \"llvm.ctpop.i8\"(src : Int8) : Int8\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_popcount16)] {% end %}\n  fun popcount16 = \"llvm.ctpop.i16\"(src : Int16) : Int16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_popcount32)] {% end %}\n  fun popcount32 = \"llvm.ctpop.i32\"(src : Int32) : Int32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_popcount64)] {% end %}\n  fun popcount64 = \"llvm.ctpop.i64\"(src : Int64) : Int64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_popcount128)] {% end %}\n  fun popcount128 = \"llvm.ctpop.i128\"(src : Int128) : Int128\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_countleading8)] {% end %}\n  fun countleading8 = \"llvm.ctlz.i8\"(src : Int8, zero_is_undef : Bool) : Int8\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_countleading16)] {% end %}\n  fun countleading16 = \"llvm.ctlz.i16\"(src : Int16, zero_is_undef : Bool) : Int16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_countleading32)] {% end %}\n  fun countleading32 = \"llvm.ctlz.i32\"(src : Int32, zero_is_undef : Bool) : Int32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_countleading64)] {% end %}\n  fun countleading64 = \"llvm.ctlz.i64\"(src : Int64, zero_is_undef : Bool) : Int64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_countleading128)] {% end %}\n  fun countleading128 = \"llvm.ctlz.i128\"(src : Int128, zero_is_undef : Bool) : Int128\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_counttrailing8)] {% end %}\n  fun counttrailing8 = \"llvm.cttz.i8\"(src : Int8, zero_is_undef : Bool) : Int8\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_counttrailing16)] {% end %}\n  fun counttrailing16 = \"llvm.cttz.i16\"(src : Int16, zero_is_undef : Bool) : Int16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_counttrailing32)] {% end %}\n  fun counttrailing32 = \"llvm.cttz.i32\"(src : Int32, zero_is_undef : Bool) : Int32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_counttrailing64)] {% end %}\n  fun counttrailing64 = \"llvm.cttz.i64\"(src : Int64, zero_is_undef : Bool) : Int64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_counttrailing128)] {% end %}\n  fun counttrailing128 = \"llvm.cttz.i128\"(src : Int128, zero_is_undef : Bool) : Int128\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshl8)] {% end %}\n  fun fshl8 = \"llvm.fshl.i8\"(a : UInt8, b : UInt8, count : UInt8) : UInt8\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshl16)] {% end %}\n  fun fshl16 = \"llvm.fshl.i16\"(a : UInt16, b : UInt16, count : UInt16) : UInt16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshl32)] {% end %}\n  fun fshl32 = \"llvm.fshl.i32\"(a : UInt32, b : UInt32, count : UInt32) : UInt32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshl64)] {% end %}\n  fun fshl64 = \"llvm.fshl.i64\"(a : UInt64, b : UInt64, count : UInt64) : UInt64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshl128)] {% end %}\n  fun fshl128 = \"llvm.fshl.i128\"(a : UInt128, b : UInt128, count : UInt128) : UInt128\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshr8)] {% end %}\n  fun fshr8 = \"llvm.fshr.i8\"(a : UInt8, b : UInt8, count : UInt8) : UInt8\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshr16)] {% end %}\n  fun fshr16 = \"llvm.fshr.i16\"(a : UInt16, b : UInt16, count : UInt16) : UInt16\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshr32)] {% end %}\n  fun fshr32 = \"llvm.fshr.i32\"(a : UInt32, b : UInt32, count : UInt32) : UInt32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshr64)] {% end %}\n  fun fshr64 = \"llvm.fshr.i64\"(a : UInt64, b : UInt64, count : UInt64) : UInt64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_fshr128)] {% end %}\n  fun fshr128 = \"llvm.fshr.i128\"(a : UInt128, b : UInt128, count : UInt128) : UInt128\n\n  {% if compare_versions(Crystal::LLVM_VERSION, \"19.1.0\") < 0 %}\n    fun va_start = \"llvm.va_start\"(ap : Void*)\n    fun va_end = \"llvm.va_end\"(ap : Void*)\n  {% else %}\n    fun va_start = \"llvm.va_start.p0\"(ap : Void*)\n    fun va_end = \"llvm.va_end.p0\"(ap : Void*)\n  {% end %}\n\n  {% if flag?(:i386) || flag?(:x86_64) %}\n    {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_pause)] {% end %}\n    fun pause = \"llvm.x86.sse2.pause\"\n  {% end %}\n\n  {% if flag?(:aarch64) %}\n    {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_pause)] {% end %}\n    fun arm_hint = \"llvm.aarch64.hint\"(hint : Int32)\n  {% end %}\nend\n\nmodule Intrinsics\n  macro debugtrap\n    ::LibIntrinsics.debugtrap\n  end\n\n  def self.pause\n    {% if flag?(:i386) || flag?(:x86_64) %}\n      LibIntrinsics.pause\n    {% elsif flag?(:aarch64) %}\n      LibIntrinsics.arm_hint(1) # YIELD\n    {% end %}\n  end\n\n  macro memcpy(dest, src, len, is_volatile)\n    ::LibIntrinsics.memcpy({{dest}}, {{src}}, {{len}}, {{is_volatile}})\n  end\n\n  macro memmove(dest, src, len, is_volatile)\n    ::LibIntrinsics.memmove({{dest}}, {{src}}, {{len}}, {{is_volatile}})\n  end\n\n  macro memset(dest, val, len, is_volatile)\n    ::LibIntrinsics.memset({{dest}}, {{val}}, {{len}}, {{is_volatile}})\n  end\n\n  def self.read_cycle_counter\n    LibIntrinsics.read_cycle_counter\n  end\n\n  def self.bitreverse8(id) : UInt8\n    LibIntrinsics.bitreverse8(id)\n  end\n\n  def self.bitreverse16(id) : UInt16\n    LibIntrinsics.bitreverse16(id)\n  end\n\n  def self.bitreverse32(id) : UInt32\n    LibIntrinsics.bitreverse32(id)\n  end\n\n  def self.bitreverse64(id) : UInt64\n    LibIntrinsics.bitreverse64(id)\n  end\n\n  def self.bitreverse128(id) : UInt128\n    LibIntrinsics.bitreverse128(id)\n  end\n\n  def self.bswap16(id) : UInt16\n    LibIntrinsics.bswap16(id)\n  end\n\n  def self.bswap32(id) : UInt32\n    LibIntrinsics.bswap32(id)\n  end\n\n  def self.bswap64(id) : UInt64\n    LibIntrinsics.bswap64(id)\n  end\n\n  def self.bswap128(id) : UInt128\n    LibIntrinsics.bswap128(id)\n  end\n\n  def self.popcount8(src) : Int8\n    LibIntrinsics.popcount8(src)\n  end\n\n  def self.popcount16(src) : Int16\n    LibIntrinsics.popcount16(src)\n  end\n\n  def self.popcount32(src) : Int32\n    LibIntrinsics.popcount32(src)\n  end\n\n  def self.popcount64(src) : Int64\n    LibIntrinsics.popcount64(src)\n  end\n\n  def self.popcount128(src)\n    LibIntrinsics.popcount128(src)\n  end\n\n  macro countleading8(src, zero_is_undef)\n    ::LibIntrinsics.countleading8({{src}}, {{zero_is_undef}})\n  end\n\n  macro countleading16(src, zero_is_undef)\n    ::LibIntrinsics.countleading16({{src}}, {{zero_is_undef}})\n  end\n\n  macro countleading32(src, zero_is_undef)\n    ::LibIntrinsics.countleading32({{src}}, {{zero_is_undef}})\n  end\n\n  macro countleading64(src, zero_is_undef)\n    ::LibIntrinsics.countleading64({{src}}, {{zero_is_undef}})\n  end\n\n  macro countleading128(src, zero_is_undef)\n    ::LibIntrinsics.countleading128({{src}}, {{zero_is_undef}})\n  end\n\n  macro counttrailing8(src, zero_is_undef)\n    ::LibIntrinsics.counttrailing8({{src}}, {{zero_is_undef}})\n  end\n\n  macro counttrailing16(src, zero_is_undef)\n    ::LibIntrinsics.counttrailing16({{src}}, {{zero_is_undef}})\n  end\n\n  macro counttrailing32(src, zero_is_undef)\n    ::LibIntrinsics.counttrailing32({{src}}, {{zero_is_undef}})\n  end\n\n  macro counttrailing64(src, zero_is_undef)\n    ::LibIntrinsics.counttrailing64({{src}}, {{zero_is_undef}})\n  end\n\n  macro counttrailing128(src, zero_is_undef)\n    ::LibIntrinsics.counttrailing128({{src}}, {{zero_is_undef}})\n  end\n\n  def self.fshl8(a, b, count) : UInt8\n    LibIntrinsics.fshl8(a, b, count)\n  end\n\n  def self.fshl16(a, b, count) : UInt16\n    LibIntrinsics.fshl16(a, b, count)\n  end\n\n  def self.fshl32(a, b, count) : UInt32\n    LibIntrinsics.fshl32(a, b, count)\n  end\n\n  def self.fshl64(a, b, count) : UInt64\n    LibIntrinsics.fshl64(a, b, count)\n  end\n\n  def self.fshl128(a, b, count) : UInt128\n    LibIntrinsics.fshl128(a, b, count)\n  end\n\n  def self.fshr8(a, b, count) : UInt8\n    LibIntrinsics.fshr8(a, b, count)\n  end\n\n  def self.fshr16(a, b, count) : UInt16\n    LibIntrinsics.fshr16(a, b, count)\n  end\n\n  def self.fshr32(a, b, count) : UInt32\n    LibIntrinsics.fshr32(a, b, count)\n  end\n\n  def self.fshr64(a, b, count) : UInt64\n    LibIntrinsics.fshr64(a, b, count)\n  end\n\n  def self.fshr128(a, b, count) : UInt128\n    LibIntrinsics.fshr128(a, b, count)\n  end\n\n  macro va_start(ap)\n    ::LibIntrinsics.va_start({{ap}})\n  end\n\n  macro va_end(ap)\n    ::LibIntrinsics.va_end({{ap}})\n  end\n\n  # Should codegen to the following LLVM IR (before being inlined):\n  # ```\n  # define internal void @\"*Intrinsics::unreachable:NoReturn\"() #12 {\n  # entry:\n  #   unreachable\n  # }\n  # ```\n  #\n  # Can be used like `@llvm.assume(i1 cond)` as `unreachable unless (assumption)`.\n  #\n  # WARNING: the behaviour of the program is undefined if the assumption is broken!\n  @[AlwaysInline]\n  def self.unreachable : NoReturn\n    x = uninitialized NoReturn\n    x\n  end\nend\n\n# Invokes an execution trap to catch the attention of a debugger. This has the\n# same effect as placing a breakpoint in debuggers or IDEs supporting them.\n#\n# Execution is allowed to continue if the debugger instructs so. If no debugger\n# is attached, usually the current process terminates with a status that\n# corresponds to `Process::ExitReason::Breakpoint`.\n#\n# Inside an interpreter session, this drops into the REPL's pry mode instead of\n# a system debugger.\nmacro debugger\n  ::Intrinsics.debugtrap\nend\n"
  },
  {
    "path": "src/io/argf.cr",
    "content": "# :nodoc:\nclass IO::ARGF < IO\n  @path : String?\n  @current_io : IO?\n\n  def initialize(@argv : Array(String), @stdin : IO)\n    @path = nil\n    @current_io = nil\n    @initialized = false\n    @read_from_stdin = false\n  end\n\n  def read(slice : Bytes) : Int32\n    first_initialize unless @initialized\n\n    if current_io = @current_io\n      read_count = read_from_current_io(current_io, slice)\n    elsif !@read_from_stdin && !@argv.empty?\n      # If there's no current_io it means we read all of ARGV.\n      # It might be the case that the user put more strings into\n      # ARGV, so in this case we need to read from that.\n      read_next_argv\n      read_count = read slice\n    else\n      read_count = 0\n    end\n\n    read_count.to_i32\n  end\n\n  # :nodoc:\n  def peek : Bytes?\n    first_initialize unless @initialized\n\n    if current_io = @current_io\n      peek = current_io.peek\n      if peek && peek.empty? # EOF\n        peek_next\n      else\n        peek\n      end\n    else\n      peek_next\n    end\n  end\n\n  private def peek_next\n    if !@read_from_stdin && !@argv.empty?\n      read_next_argv\n      self.peek\n    else\n      nil\n    end\n  end\n\n  def write(slice : Bytes) : NoReturn\n    raise IO::Error.new \"Can't write to ARGF\"\n  end\n\n  def path : String\n    @path || @argv.first? || \"-\"\n  end\n\n  private def first_initialize\n    # This is the moment where we decide\n    # whether we are going to use STDIN or ARGV\n    @initialized = true\n    if @argv.empty?\n      @read_from_stdin = true\n      @current_io = @stdin\n    else\n      read_next_argv\n    end\n  end\n\n  private def read_from_current_io(current_io, slice)\n    read_count = current_io.read slice\n    if read_count.zero?\n      unless @read_from_stdin\n        current_io.close\n        if @argv.empty?\n          @current_io = nil\n        else\n          read_next_argv\n          read_count = read slice\n        end\n      end\n    end\n    read_count\n  end\n\n  private def read_next_argv\n    path = @path = @argv.shift\n    @current_io = File.open(path)\n  end\nend\n"
  },
  {
    "path": "src/io/buffered.cr",
    "content": "# The `IO::Buffered` mixin enhances an `IO` with input/output buffering.\n#\n# The buffering behaviour can be turned on/off with the `#sync=` and\n# `#read_buffering=` methods.\n#\n# Additionally, several methods, like `#gets`, are implemented in a more\n# efficient way.\nmodule IO::Buffered\n  @in_buffer = Pointer(UInt8).null\n  @out_buffer = Pointer(UInt8).null\n  @in_buffer_rem = Bytes.empty\n  @out_count = 0\n  @sync = false\n  @read_buffering = true\n  @flush_on_newline = false\n  @buffer_size = IO::DEFAULT_BUFFER_SIZE\n\n  # Reads at most *slice.size* bytes from the wrapped `IO` into *slice*.\n  # Returns the number of bytes read.\n  #\n  # TODO: Add return type restriction `Int32`\n  abstract def unbuffered_read(slice : Bytes)\n\n  # Writes *slice* entirely into the wrapped `IO`.\n  #\n  # TODO: Add return type restriction `Nil`\n  abstract def unbuffered_write(slice : Bytes)\n\n  # Flushes the wrapped `IO`.\n  #\n  # TODO: Add return type restriction `Nil`\n  abstract def unbuffered_flush\n\n  # Closes the wrapped `IO`.\n  #\n  # TODO: Add return type restriction `Nil`\n  abstract def unbuffered_close\n\n  # Rewinds the wrapped `IO`.\n  #\n  # TODO: Add return type restriction `Nil`\n  abstract def unbuffered_rewind\n\n  # Return the buffer size used\n  def buffer_size : Int32\n    @buffer_size\n  end\n\n  # Set the buffer size of both the read and write buffer\n  # Cannot be changed after any of the buffers have been allocated\n  def buffer_size=(value)\n    if (@in_buffer || @out_buffer) && (buffer_size != value)\n      raise ArgumentError.new(\"Cannot change buffer_size after buffers have been allocated\")\n    end\n    @buffer_size = value\n  end\n\n  # :nodoc:\n  def read_byte : UInt8?\n    check_open\n\n    fill_buffer if read_buffering? && @in_buffer_rem.empty?\n    if @in_buffer_rem.empty?\n      return nil if read_buffering?\n\n      byte = uninitialized UInt8\n      if read(Slice.new(pointerof(byte), 1)) == 1\n        byte\n      else\n        nil\n      end\n    else\n      b = @in_buffer_rem[0]\n      @in_buffer_rem += 1\n      b\n    end\n  end\n\n  # Buffered implementation of `IO#read(slice)`.\n  def read(slice : Bytes) : Int32\n    check_open\n\n    count = slice.size\n    return 0 if count == 0\n\n    if @in_buffer_rem.empty?\n      # If we are asked to read more than half the buffer's size,\n      # read directly into the slice, as it's not worth the extra\n      # memory copy.\n      if !read_buffering? || count >= @buffer_size // 2\n        return unbuffered_read(slice[0, count]).to_i\n      else\n        fill_buffer\n        return 0 if @in_buffer_rem.empty?\n      end\n    end\n\n    to_read = Math.min(count, @in_buffer_rem.size)\n    slice.copy_from(@in_buffer_rem.to_unsafe, to_read)\n    @in_buffer_rem += to_read\n    to_read\n  end\n\n  # Returns the bytes hold in the read buffer.\n  #\n  # This method only performs a read to return\n  # peek data if the current buffer is empty:\n  # otherwise no read is performed and whatever\n  # is in the buffer is returned.\n  def peek : Bytes\n    check_open\n\n    if @in_buffer_rem.empty?\n      fill_buffer\n      if @in_buffer_rem.empty?\n        return Bytes.empty # EOF\n      end\n    end\n\n    @in_buffer_rem\n  end\n\n  # :nodoc:\n  def skip(bytes_count : Int) : Nil\n    check_open\n\n    if bytes_count <= @in_buffer_rem.size\n      @in_buffer_rem += bytes_count\n      return\n    end\n\n    bytes_count -= @in_buffer_rem.size\n    @in_buffer_rem = Bytes.empty\n\n    super(bytes_count)\n  end\n\n  # Buffered implementation of `IO#write(slice)`.\n  def write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    count = slice.size\n\n    if sync?\n      return unbuffered_write(slice)\n    end\n\n    if flush_on_newline?\n      index = slice[0, count.to_i32].rindex('\\n'.ord.to_u8)\n      if index\n        flush\n        index += 1\n        unbuffered_write slice[0, index]\n        slice += index\n        count -= index\n      end\n    end\n\n    if count >= @buffer_size\n      flush\n      return unbuffered_write slice[0, count]\n    end\n\n    if count > @buffer_size - @out_count\n      flush\n    end\n\n    slice.copy_to(out_buffer + @out_count, count)\n    @out_count += count\n  end\n\n  # :nodoc:\n  def write_byte(byte : UInt8) : Nil\n    check_open\n\n    if sync?\n      return super\n    end\n\n    if @out_count >= @buffer_size\n      flush\n    end\n    out_buffer[@out_count] = byte\n    @out_count += 1\n\n    if flush_on_newline? && byte === '\\n'\n      flush\n    end\n  end\n\n  # Returns the current position (in bytes) in this `IO`.\n  #\n  # ```\n  # File.write(\"testfile\", \"hello\")\n  #\n  # file = File.new(\"testfile\")\n  # file.pos     # => 0\n  # file.gets(2) # => \"he\"\n  # file.pos     # => 2\n  # ```\n  def pos : Int64\n    flush\n    in_rem = @in_buffer_rem.size\n\n    # TODO In 2.0 we should make `unbuffered_pos` an abstract method of Buffered\n    if self.responds_to?(:unbuffered_pos)\n      self.unbuffered_pos - in_rem\n    else\n      super - in_rem\n    end\n  end\n\n  # Turns on/off `IO` **write** buffering. When *sync* is set to `true`, no buffering\n  # will be done (that is, writing to this `IO` is immediately synced to the\n  # underlying `IO`).\n  def sync=(sync : Bool) : Bool\n    flush if sync && !@sync\n    @sync = sync\n  end\n\n  # Determines if this `IO` does write buffering. If `true`, no buffering is done.\n  def sync? : Bool\n    @sync\n  end\n\n  # Turns on/off `IO` **read** buffering.\n  def read_buffering=(read_buffering : Bool) : Bool\n    @read_buffering = read_buffering\n  end\n\n  # Determines whether this `IO` buffers reads.\n  def read_buffering? : Bool\n    @read_buffering\n  end\n\n  # Turns on/off flushing the underlying `IO` when a newline is written.\n  def flush_on_newline=(flush_on_newline : Bool) : Bool\n    @flush_on_newline = flush_on_newline\n  end\n\n  # Determines if this `IO` flushes automatically when a newline is written.\n  def flush_on_newline? : Bool\n    @flush_on_newline\n  end\n\n  # Flushes any buffered data and the underlying `IO`. Returns `self`.\n  def flush : self\n    unbuffered_write(Slice.new(out_buffer, @out_count)) if @out_count > 0\n    unbuffered_flush\n    @out_count = 0\n    self\n  end\n\n  # Flushes and closes the underlying `IO`.\n  def close : Nil\n    flush if @out_count > 0\n  ensure\n    unbuffered_close\n  end\n\n  # Rewinds the underlying `IO`. Returns `self`.\n  def rewind : self\n    unbuffered_rewind\n    @in_buffer_rem = Bytes.empty\n    self\n  end\n\n  private def fill_buffer\n    in_buffer = in_buffer()\n    size = unbuffered_read(Slice.new(in_buffer, @buffer_size)).to_i\n    @in_buffer_rem = Slice.new(in_buffer, size)\n  end\n\n  private def in_buffer\n    @in_buffer ||= GC.malloc_atomic(@buffer_size.to_u32).as(UInt8*)\n  end\n\n  private def out_buffer\n    @out_buffer ||= GC.malloc_atomic(@buffer_size.to_u32).as(UInt8*)\n  end\nend\n"
  },
  {
    "path": "src/io/byte_format.cr",
    "content": "# Defines a byte format to encode integers and floats\n# from/to `Bytes` and `IO`.\n#\n# ### Decode from bytes\n#\n# ```\n# bytes = Bytes[0x34, 0x12]\n# int16 = IO::ByteFormat::LittleEndian.decode(Int16, bytes)\n# int16 # => 0x1234_i16\n# ```\n#\n# ### Decode from an IO\n#\n# ```\n# io = IO::Memory.new(Bytes[0x34, 0x12])\n# int16 = io.read_bytes(Int16, IO::ByteFormat::LittleEndian)\n# int16 # => 0x1234_i16\n# ```\n#\n# ### Encode to bytes\n#\n# ```\n# raw = uninitialized UInt8[2]\n# IO::ByteFormat::LittleEndian.encode(0x1234_i16, raw.to_slice)\n# raw # => StaticArray[0x34, 0x12]\n# ```\n#\n# ### Encode to IO\n#\n# ```\n# io = IO::Memory.new\n# io.write_bytes(0x1234_i16, IO::ByteFormat::LittleEndian)\n# io.to_slice # => Bytes[0x34, 0x12]\n# ```\nmodule IO::ByteFormat\n  abstract def encode(int : Int8, io : IO)\n  abstract def encode(int : UInt8, io : IO)\n  abstract def encode(int : Int16, io : IO)\n  abstract def encode(int : UInt16, io : IO)\n  abstract def encode(int : Int32, io : IO)\n  abstract def encode(int : UInt32, io : IO)\n  abstract def encode(int : Int64, io : IO)\n  abstract def encode(int : UInt64, io : IO)\n  abstract def encode(int : Int128, io : IO)\n  abstract def encode(int : UInt128, io : IO)\n\n  abstract def encode(int : Int8, bytes : Bytes)\n  abstract def encode(int : UInt8, bytes : Bytes)\n  abstract def encode(int : Int16, bytes : Bytes)\n  abstract def encode(int : UInt16, bytes : Bytes)\n  abstract def encode(int : Int32, bytes : Bytes)\n  abstract def encode(int : UInt32, bytes : Bytes)\n  abstract def encode(int : Int64, bytes : Bytes)\n  abstract def encode(int : UInt64, bytes : Bytes)\n  abstract def encode(int : Int128, bytes : Bytes)\n  abstract def encode(int : UInt128, bytes : Bytes)\n\n  abstract def decode(int : Int8.class, io : IO)\n  abstract def decode(int : UInt8.class, io : IO)\n  abstract def decode(int : Int16.class, io : IO)\n  abstract def decode(int : UInt16.class, io : IO)\n  abstract def decode(int : Int32.class, io : IO)\n  abstract def decode(int : UInt32.class, io : IO)\n  abstract def decode(int : Int64.class, io : IO)\n  abstract def decode(int : UInt64.class, io : IO)\n  abstract def decode(int : Int128.class, io : IO)\n  abstract def decode(int : UInt128.class, io : IO)\n\n  abstract def decode(int : Int8.class, bytes : Bytes)\n  abstract def decode(int : UInt8.class, bytes : Bytes)\n  abstract def decode(int : Int16.class, bytes : Bytes)\n  abstract def decode(int : UInt16.class, bytes : Bytes)\n  abstract def decode(int : Int32.class, bytes : Bytes)\n  abstract def decode(int : UInt32.class, bytes : Bytes)\n  abstract def decode(int : Int64.class, bytes : Bytes)\n  abstract def decode(int : UInt64.class, bytes : Bytes)\n  abstract def decode(int : Int128.class, bytes : Bytes)\n  abstract def decode(int : UInt128.class, bytes : Bytes)\n\n  def encode(float : Float32, io : IO) : Nil\n    encode(float.unsafe_as(Int32), io)\n  end\n\n  def encode(float : Float32, bytes : Bytes)\n    encode(float.unsafe_as(Int32), bytes)\n  end\n\n  def decode(type : Float32.class, io : IO) : Float32\n    decode(Int32, io).unsafe_as(Float32)\n  end\n\n  def decode(type : Float32.class, bytes : Bytes) : Float32\n    decode(Int32, bytes).unsafe_as(Float32)\n  end\n\n  def encode(float : Float64, io : IO) : Nil\n    encode(float.unsafe_as(Int64), io)\n  end\n\n  def encode(float : Float64, bytes : Bytes)\n    encode(float.unsafe_as(Int64), bytes)\n  end\n\n  def decode(type : Float64.class, io : IO) : Float64\n    decode(Int64, io).unsafe_as(Float64)\n  end\n\n  def decode(type : Float64.class, bytes : Bytes) : Float64\n    decode(Int64, bytes).unsafe_as(Float64)\n  end\n\n  module LittleEndian\n    extend ByteFormat\n  end\n\n  module BigEndian\n    extend ByteFormat\n  end\n\n  alias SystemEndian = LittleEndian\n  alias NetworkEndian = BigEndian\n\n  {% for mod in %w(LittleEndian BigEndian) %}\n    module {{mod.id}}\n      {% for type, i in %w(Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64 Int128 UInt128) %}\n        {% bytesize = 2 ** (i // 2) %}\n\n        def self.encode(int : {{type.id}}, io : IO)\n          buffer = int.unsafe_as(StaticArray(UInt8, {{bytesize}}))\n          buffer.reverse! unless SystemEndian == self\n          io.write(buffer.to_slice)\n        end\n\n        def self.encode(int : {{type.id}}, bytes : Bytes)\n          buffer = int.unsafe_as(StaticArray(UInt8, {{bytesize}}))\n          buffer.reverse! unless SystemEndian == self\n          buffer.to_slice.copy_to(bytes)\n        end\n\n        def self.decode(int : {{type.id}}.class, io : IO)\n          buffer = uninitialized UInt8[{{bytesize}}]\n          io.read_fully(buffer.to_slice)\n          buffer.reverse! unless SystemEndian == self\n          buffer.unsafe_as({{type.id}})\n        end\n\n        def self.decode(int : {{type.id}}.class, bytes : Bytes)\n          buffer = uninitialized UInt8[{{bytesize}}]\n          bytes.copy_to(buffer.to_unsafe, {{bytesize}})\n          buffer.reverse! unless SystemEndian == self\n          buffer.unsafe_as({{type.id}})\n        end\n\n        @[Deprecated(\"Use `.decode(int : {{type.id}}.class, io : IO)` instead\")]\n        def self.decode(*, type : {{type.id}}.class, io : IO)\n          decode(type, io)\n        end\n\n        @[Deprecated(\"Use `.decode(int : {{type.id}}.class, bytes : Bytes)` instead\")]\n        def self.decode(*, type : {{type.id}}.class, bytes : Bytes)\n          decode(type, bytes)\n        end\n      {% end %}\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/io/console.cr",
    "content": "class IO::FileDescriptor < IO\n  # Yields `self` to the given block, disables character echoing for the\n  # duration of the block, and returns the block's value.\n  #\n  # This will prevent displaying back to the user what they enter on the terminal.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  #\n  # ```\n  # print \"Enter password: \"\n  # password = STDIN.noecho &.gets.try &.chomp\n  # puts\n  # ```\n  def noecho(& : self -> _)\n    system_echo(false) { yield self }\n  end\n\n  # Yields `self` to the given block, enables character echoing for the\n  # duration of the block, and returns the block's value.\n  #\n  # This causes user input to be displayed as they are entered on the terminal.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def echo(& : self -> _)\n    system_echo(true) { yield self }\n  end\n\n  # Disables character echoing on this `IO`.\n  #\n  # This will prevent displaying back to the user what they enter on the terminal.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def noecho! : Nil\n    system_echo(false)\n  end\n\n  # Enables character echoing on this `IO`.\n  #\n  # This causes user input to be displayed as they are entered on the terminal.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def echo! : Nil\n    system_echo(true)\n  end\n\n  # Yields `self` to the given block, enables character processing for the\n  # duration of the block, and returns the block's value.\n  #\n  # The so called cooked mode is the standard behavior of a terminal,\n  # doing line wise editing by the terminal and only sending the input to\n  # the program on a newline.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def cooked(& : self -> _)\n    system_raw(false) { yield self }\n  end\n\n  # Yields `self` to the given block, enables raw mode for the duration of the\n  # block, and returns the block's value.\n  #\n  # In raw mode every keypress is directly sent to the program, no interpretation\n  # is done by the terminal. On Windows, this also enables ANSI input escape\n  # sequences.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def raw(& : self -> _)\n    system_raw(true) { yield self }\n  end\n\n  # Enables character processing on this `IO`.\n  #\n  # The so called cooked mode is the standard behavior of a terminal,\n  # doing line wise editing by the terminal and only sending the input to\n  # the program on a newline.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def cooked! : Nil\n    system_raw(false)\n  end\n\n  # Enables raw mode on this `IO`.\n  #\n  # In raw mode every keypress is directly sent to the program, no interpretation\n  # is done by the terminal. On Windows, this also enables ANSI input escape\n  # sequences.\n  #\n  # Raises `IO::Error` if this `IO` is not a terminal device.\n  def raw! : Nil\n    system_raw(true)\n  end\n\n  @[Deprecated]\n  macro noecho_from_tc_mode!\n    mode.c_lflag &= ~(Termios::LocalMode.flags(ECHO, ECHOE, ECHOK, ECHONL).value)\n    system_tcsetattr(Termios::LineControl::TCSANOW, pointerof(mode))\n  end\n\n  @[Deprecated]\n  macro cooked_from_tc_mode!\n    mode.c_iflag |= (Termios::InputMode::BRKINT |\n                     Termios::InputMode::ISTRIP |\n                     Termios::InputMode::ICRNL |\n                     Termios::InputMode::IXON).value\n    mode.c_oflag |= Termios::OutputMode::OPOST.value\n    mode.c_lflag |= (Termios::LocalMode::ECHO |\n                     Termios::LocalMode::ECHOE |\n                     Termios::LocalMode::ECHOK |\n                     Termios::LocalMode::ECHONL |\n                     Termios::LocalMode::ICANON |\n                     Termios::LocalMode::ISIG |\n                     Termios::LocalMode::IEXTEN).value\n    system_tcsetattr(Termios::LineControl::TCSANOW, pointerof(mode))\n  end\n\n  @[Deprecated]\n  macro raw_from_tc_mode!\n    Crystal::System::FileDescriptor.cfmakeraw(pointerof(mode))\n    system_tcsetattr(Termios::LineControl::TCSANOW, pointerof(mode))\n  end\nend\n"
  },
  {
    "path": "src/io/delimited.cr",
    "content": "# An `IO` that wraps another `IO`, and only reads up to the beginning of a\n# specified delimiter.\n#\n# This is useful for exposing part of an underlying stream to a client.\n#\n# ```\n# io = IO::Memory.new \"abc||123\"\n# delimited = IO::Delimited.new(io, read_delimiter: \"||\")\n#\n# delimited.gets_to_end # => \"abc\"\n# delimited.gets_to_end # => \"\"\n# io.gets_to_end        # => \"123\"\n# ```\nclass IO::Delimited < IO\n  # If `#sync_close?` is `true`, closing this `IO` will close the underlying `IO`.\n  property? sync_close\n\n  getter read_delimiter\n  getter? closed : Bool\n\n  @delimiter_buffer : Bytes\n  @active_delimiter_buffer : Bytes\n\n  # Creates a new `IO::Delimited` which wraps *io*, and can read until the\n  # byte sequence *read_delimiter* (interpreted as UTF-8) is found. If\n  # *sync_close* is set, calling `#close` calls `#close` on the underlying\n  # `IO`.\n  def self.new(io : IO, read_delimiter : String, sync_close : Bool = false) : self\n    new(io, read_delimiter.to_slice, sync_close)\n  end\n\n  # Creates a new `IO::Delimited` which wraps *io*, and can read until the\n  # byte sequence *read_delimiter* is found. If *sync_close* is set, calling\n  # `#close` calls `#close` on the underlying `IO`.\n  def initialize(@io : IO, @read_delimiter : Bytes, @sync_close : Bool = false)\n    @closed = false\n    @finished = false\n\n    # The buffer where we do all our work.\n    @delimiter_buffer = Bytes.new(@read_delimiter.size)\n    # Slice inside delimiter buffer where bytes waiting to be read are stored.\n    @active_delimiter_buffer = Bytes.empty\n  end\n\n  def read(slice : Bytes) : Int32\n    check_open\n\n    if @finished\n      # We might still have stuff to read from @active_delimiter_buffer\n      return read_from_active_delimited_buffer(slice)\n    end\n\n    read_internal(slice)\n  end\n\n  private def read_internal(slice : Bytes) : Int32\n    if peek = @io.peek\n      read_with_peek(slice, peek)\n    else\n      read_without_peek(slice)\n    end\n  end\n\n  private def read_with_peek(slice : Bytes, peek : Bytes) : Int32\n    # If there's nothing else to peek, we reached EOF\n    if peek.empty?\n      @finished = true\n\n      if @active_delimiter_buffer.empty?\n        return 0\n      else\n        # If we have something in the active delimiter buffer,\n        # but we don't have any more data to read, that wasn't\n        # the delimiter so we must include it in the slice.\n        return read_from_active_delimited_buffer(slice)\n      end\n    end\n\n    first_byte = @read_delimiter[0]\n\n    # If we have something in the active delimiter buffer\n    unless @active_delimiter_buffer.empty?\n      # This is the rest of the delimiter we have to match\n      delimiter_remaining = @read_delimiter[@active_delimiter_buffer.size..]\n\n      # This is how much we can actually match of that (peek might not have enough data!)\n      min_size = Math.min(delimiter_remaining.size, peek.size)\n\n      # See if what remains to match in the delimiter matches whatever\n      # we have in peek, limited to what's available\n      if delimiter_remaining[0, min_size] == peek[0, min_size]\n        # If peek has enough data to match the entire rest of the delimiter...\n        if peek.size >= delimiter_remaining.size\n          # We found the delimiter!\n          @io.skip(min_size)\n          @active_delimiter_buffer = Bytes.empty\n          @finished = true\n          return 0\n        else\n          # Copy the remaining of peek to the active delimiter buffer for now\n          (@delimiter_buffer + @active_delimiter_buffer.size).copy_from(peek)\n          @active_delimiter_buffer = @delimiter_buffer[0, @active_delimiter_buffer.size + peek.size]\n\n          # Skip whatever we had in peek, and try reading more\n          @io.skip(peek.size)\n          return read_internal(slice)\n        end\n      else\n        # No match.\n        # We first need to check if the delimiter could actually start in this active buffer.\n        next_index = @active_delimiter_buffer.index(first_byte, 1)\n\n        # We read up to that new match, if any, or the entire buffer\n        read_bytes = Math.min(next_index || @active_delimiter_buffer.size, slice.size)\n\n        slice.copy_from(@active_delimiter_buffer[0, read_bytes])\n        slice += read_bytes\n        @active_delimiter_buffer += read_bytes\n\n        return read_bytes if slice.empty?\n        return read_bytes + read_internal(slice)\n      end\n    end\n\n    index =\n      if slice.size == 1\n        # For a size of 1, this is much faster\n        first_byte == peek[0] ? 0 : nil\n      elsif slice.size < peek.size\n        peek[0, slice.size].index(first_byte)\n      else\n        peek.index(first_byte)\n      end\n\n    # If we can't find the delimiter's first byte we can just read from peek\n    unless index\n      # If we have more in peek than what we need to read, read all of that\n      if peek.size >= slice.size\n        if slice.size == 1\n          # For a size of 1, this is much faster\n          slice[0] = peek[0]\n        else\n          slice.copy_from(peek[0, slice.size])\n        end\n        @io.skip(slice.size)\n        return slice.size\n      else\n        # Otherwise, read from peek for now\n        slice.copy_from(peek)\n        @io.skip(peek.size)\n        return peek.size\n      end\n    end\n\n    # If the delimiter is just a single byte, we can stop right here\n    if @delimiter_buffer.size == 1\n      slice.copy_from(peek[0, index])\n      @io.skip(index + 1)\n      @finished = true\n      return index\n    end\n\n    # If the delimiter fits the rest of the peek buffer,\n    # we can check it right now.\n    if index + @delimiter_buffer.size <= peek.size\n      # If we found the delimiter, we are done\n      if peek[index, @delimiter_buffer.size] == @read_delimiter\n        slice.copy_from(peek[0, index])\n        @io.skip(index + @delimiter_buffer.size)\n        @finished = true\n        return index\n      else\n        # Otherwise, we can read up to past that byte for now\n        slice.copy_from(peek[0, index + 1])\n        @io.skip(index + 1)\n        slice += index + 1\n        return index + 1\n      end\n    end\n\n    # If the part past in the peek buffer past the matching index\n    # doesn't match the read delimiter's portion, we can move on\n    rest = peek[index..]\n    unless rest == @read_delimiter[0, rest.size]\n      # We can read up to past that byte for now\n      safe_to_read = peek[0, index + 1]\n      slice.copy_from(safe_to_read)\n      @io.skip(safe_to_read.size)\n      slice += safe_to_read.size\n      return safe_to_read.size\n    end\n\n    # Copy up to index into slice\n    slice.copy_from(peek[0, index])\n    slice += index\n\n    # Copy the rest of the peek buffer into delimited buffer\n    @delimiter_buffer.copy_from(rest)\n    @active_delimiter_buffer = @delimiter_buffer[0, rest.size]\n\n    @io.skip(peek.size)\n\n    index + read_internal(slice)\n  end\n\n  private def read_from_active_delimited_buffer(slice : Bytes) : Int32\n    if @active_delimiter_buffer.empty?\n      return 0\n    end\n\n    available = Math.min(@active_delimiter_buffer.size, slice.size)\n    slice.copy_from(@active_delimiter_buffer[0, available])\n    @active_delimiter_buffer += available\n    available\n  end\n\n  private def read_without_peek(slice : Bytes) : Int32\n    first_byte = @read_delimiter[0]\n    read_bytes = 0\n\n    while read_bytes < slice.size\n      # Select the next byte as the head of the active delimiter buffer,\n      # or the next byte from the io if the buffer is not in use.\n      if @active_delimiter_buffer.size > 0\n        byte = @active_delimiter_buffer[0]\n        @active_delimiter_buffer += 1\n      else\n        byte = @io.read_byte\n      end\n\n      break if byte.nil?\n\n      # We know we don't need to check if the delimiter matches when the buffer\n      # has been resized, because this signals we are coming to the end of the IO.\n      if byte == first_byte && @delimiter_buffer.size == @read_delimiter.size\n        buffer = @delimiter_buffer\n        buffer[0] = byte\n        read_start = 1\n\n        # If we have an active delimiter buffer copy it in after the current\n        # character, and update where we should start our read operation.\n        if @active_delimiter_buffer.size > 0\n          (buffer + 1).move_from(@active_delimiter_buffer)\n          read_start += @active_delimiter_buffer.size\n        end\n\n        read_buffer = buffer + read_start\n        bytes = 0\n        while read_buffer.size > 0\n          partial_bytes = @io.read(read_buffer)\n          break if partial_bytes == 0\n\n          read_buffer += partial_bytes\n          bytes += partial_bytes\n        end\n\n        # If read didn't read as many bytes as we asked it to, resize the buffer\n        # to remove garbage bytes.\n        if bytes != buffer.size - read_start\n          buffer = buffer[0, read_start + bytes]\n        end\n\n        if buffer == @read_delimiter\n          @finished = true\n          @active_delimiter_buffer = Bytes.empty\n          return read_bytes\n        end\n\n        @delimiter_buffer = buffer\n        @active_delimiter_buffer = buffer + 1\n      end\n\n      slice[read_bytes] = byte\n      read_bytes += 1\n    end\n\n    read_bytes\n  end\n\n  def write(slice : Bytes) : Nil\n    raise IO::Error.new \"Can't write to IO::Delimited\"\n  end\n\n  def peek : Bytes?\n    if @finished\n      # It's fine to return this internal buffer because peek\n      # clients aren't supposed to write to that buffer.\n      return @active_delimiter_buffer\n    end\n\n    peek = @io.peek\n    return nil unless peek\n    return peek if peek.empty?\n\n    # See if we can find the first byte\n    first_byte = @read_delimiter[0]\n\n    offset = 0\n    loop do\n      index = peek.index(first_byte, offset: offset)\n\n      # If we can't find it, the entire underlying peek buffer is visible\n      unless index\n        return peek\n      end\n\n      # If the delimiter is just one byte, we found it!\n      if @read_delimiter.size == 1\n        return peek[0, index]\n      end\n\n      # If the delimiter fits the rest of the peek buffer,\n      # we can check it right now.\n      if index + @delimiter_buffer.size <= peek.size\n        # If we found the delimiter, we are done\n        if peek[index, @delimiter_buffer.size] == @read_delimiter\n          return peek[0, index]\n        else\n          offset = index + 1\n          next\n        end\n      else\n        # Otherwise we can't know without reading further,\n        # return what we have so far\n        if index == 0\n          # Check if whatever remains in peek actually matches the delimiter.\n          min_size = Math.min(@read_delimiter.size, peek.size)\n          if @read_delimiter[0, min_size] == peek[0, min_size]\n            # The entire peek buffer partially matches the delimiter,\n            # but we don't know what will happen next. We can't peek.\n            return nil\n          else\n            # It didn't fully match, so we can at least return\n            # the part up to the next match\n            next_index = peek.index(first_byte, 1)\n            return peek[0, next_index || peek.size]\n          end\n        else\n          return peek[0, index]\n        end\n      end\n    end\n  end\n\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    @io.close if @sync_close\n  end\nend\n"
  },
  {
    "path": "src/io/encoding.cr",
    "content": "{% unless flag?(:without_iconv) %}\n  require \"crystal/iconv\"\n{% end %}\n\nclass IO\n  # Has the `name` and the `invalid` option.\n  struct EncodingOptions\n    getter name : String\n    getter invalid : Symbol?\n\n    def initialize(@name : String, @invalid : Symbol?)\n      EncodingOptions.check_invalid(invalid)\n    end\n\n    def self.check_invalid(invalid : Symbol?) : Nil\n      if invalid && invalid != :skip\n        raise ArgumentError.new \"Valid values for `invalid` option are `nil` and `:skip`, not #{invalid.inspect}\"\n      end\n    end\n  end\nend\n\n{% if flag?(:without_iconv) %}\n  require \"./encoding_stubs\"\n  {% skip_file %}\n{% end %}\n\nclass IO\n  private class Encoder\n    def initialize(@encoding_options : EncodingOptions)\n      @iconv = Crystal::Iconv.new(\"UTF-8\", encoding_options.name, encoding_options.invalid)\n      @closed = false\n    end\n\n    def write(io : IO, slice : Bytes) : Nil\n      inbuf_ptr = slice.to_unsafe\n      inbytesleft = LibC::SizeT.new(slice.size)\n      outbuf = uninitialized UInt8[1024]\n      while inbytesleft > 0\n        outbuf_ptr = outbuf.to_unsafe\n        outbytesleft = LibC::SizeT.new(outbuf.size)\n        err = @iconv.convert(pointerof(inbuf_ptr), pointerof(inbytesleft), pointerof(outbuf_ptr), pointerof(outbytesleft))\n        if err == Crystal::Iconv::ERROR\n          @iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft))\n        end\n        io.write(outbuf.to_slice[0, outbuf.size - outbytesleft])\n      end\n    end\n\n    def close : Nil\n      return if @closed\n      @closed = true\n      @iconv.close\n    end\n\n    def finalize : Nil\n      close\n    end\n  end\n\n  private class Decoder\n    BUFFER_SIZE     = 4 * 1024\n    OUT_BUFFER_SIZE = 4 * 1024\n\n    property out_slice : Bytes\n\n    @in_buffer : Pointer(UInt8)\n\n    def initialize(@encoding_options : EncodingOptions)\n      @iconv = Crystal::Iconv.new(encoding_options.name, \"UTF-8\", encoding_options.invalid)\n      @buffer = Bytes.new((GC.malloc_atomic(BUFFER_SIZE).as(UInt8*)), BUFFER_SIZE)\n      @in_buffer = @buffer.to_unsafe\n      @in_buffer_left = LibC::SizeT.new(0)\n      @out_buffer = Bytes.new((GC.malloc_atomic(OUT_BUFFER_SIZE).as(UInt8*)), OUT_BUFFER_SIZE)\n      @out_slice = Bytes.empty\n      @closed = false\n    end\n\n    def read(io : IO) : Nil\n      loop do\n        return unless @out_slice.empty?\n\n        if @in_buffer_left == 0\n          @in_buffer = @buffer.to_unsafe\n          @in_buffer_left = LibC::SizeT.new(io.read(@buffer))\n        end\n\n        # If, after refilling the buffer, we couldn't read new bytes\n        # it means we reached the end\n        break if @in_buffer_left == 0\n\n        # Convert bytes using iconv\n        out_buffer = @out_buffer.to_unsafe\n        out_buffer_left = LibC::SizeT.new(OUT_BUFFER_SIZE)\n        result = @iconv.convert(pointerof(@in_buffer), pointerof(@in_buffer_left), pointerof(out_buffer), pointerof(out_buffer_left))\n        @out_slice = @out_buffer[0, OUT_BUFFER_SIZE - out_buffer_left]\n\n        # Check for errors\n        if result == Crystal::Iconv::ERROR\n          case Errno.value\n          when Errno::EILSEQ\n            # For an illegal sequence we just skip one byte and we'll continue next\n            @iconv.handle_invalid(pointerof(@in_buffer), pointerof(@in_buffer_left))\n          when Errno::EINVAL\n            # EINVAL means \"An incomplete multibyte sequence has been encountered in the input.\"\n            old_in_buffer_left = @in_buffer_left\n\n            # On invalid multibyte sequence we try to read more bytes\n            # to see if they complete the sequence\n            refill_in_buffer(io)\n\n            # If we couldn't read anything new, we raise or skip\n            if old_in_buffer_left == @in_buffer_left\n              @iconv.handle_invalid(pointerof(@in_buffer), pointerof(@in_buffer_left))\n            end\n          else\n            # Not an error we can handle\n          end\n\n          # Continue decoding after an error\n          next\n        end\n\n        break\n      end\n    end\n\n    private def refill_in_buffer(io)\n      buffer_remaining = BUFFER_SIZE - @in_buffer_left - (@in_buffer - @buffer.to_unsafe)\n      if buffer_remaining < 64\n        @buffer.copy_from(@in_buffer, @in_buffer_left)\n        @in_buffer = @buffer.to_unsafe\n        buffer_remaining = BUFFER_SIZE - @in_buffer_left\n      end\n      @in_buffer_left += LibC::SizeT.new(io.read(Slice.new(@in_buffer + @in_buffer_left, buffer_remaining)))\n    end\n\n    def read_byte(io : IO) : UInt8?\n      read(io)\n      if out_slice.empty?\n        nil\n      else\n        byte = out_slice.to_unsafe.value\n        advance 1\n        byte\n      end\n    end\n\n    def read_utf8(io : IO, slice : Bytes) : Int32\n      count = 0\n      until slice.empty?\n        read(io)\n        break if out_slice.empty?\n\n        available = Math.min(out_slice.size, slice.size)\n        out_slice[0, available].copy_to(slice.to_unsafe, available)\n        advance(available)\n        count += available\n        slice += available\n      end\n      count\n    end\n\n    def gets(io : IO, delimiter : UInt8, limit : Int, chomp : Bool) : String?\n      read(io)\n      return nil if @out_slice.empty?\n\n      index = @out_slice.index(delimiter)\n      if index\n        # If we find it past the limit, limit the result\n        if index >= limit\n          index = limit\n        else\n          index += 1\n        end\n\n        return gets_index(index, delimiter, chomp)\n      end\n\n      # Check if there's limit bytes in the out slice\n      if @out_slice.size >= limit\n        return gets_index(limit, delimiter, chomp)\n      end\n\n      # We need to read from the out_slice into a String until we find that byte,\n      # or until we consumed limit bytes\n      String.build do |str|\n        loop do\n          limit -= @out_slice.size\n          write str\n\n          read(io)\n\n          break if @out_slice.empty?\n\n          index = @out_slice.index(delimiter)\n          if index\n            if index >= limit\n              index = limit\n            else\n              index += 1\n            end\n            write str, index\n            break\n          else\n            if limit < @out_slice.size\n              write(str, limit)\n              break\n            end\n          end\n        end\n        str.chomp!(delimiter) if chomp\n      end\n    end\n\n    private def gets_index(index, delimiter, chomp)\n      advance_increment = index\n\n      if chomp && index > 0 && @out_slice[index - 1] === delimiter\n        index -= 1\n\n        if delimiter === '\\n' && index > 0 && @out_slice[index - 1] === '\\r'\n          index -= 1\n        end\n      end\n\n      string = String.new(@out_slice[0, index])\n      advance(advance_increment)\n      string\n    end\n\n    def write(io : IO) : Nil\n      io.write @out_slice\n      @out_slice = Bytes.empty\n    end\n\n    def write(io : IO, numbytes : Int) : Nil\n      io.write @out_slice[0, numbytes]\n      @out_slice += numbytes\n    end\n\n    def advance(numbytes : Int) : Nil\n      @out_slice += numbytes\n    end\n\n    def close : Nil\n      return if @closed\n      @closed = true\n\n      @iconv.close\n    end\n\n    def finalize : Nil\n      close\n    end\n  end\nend\n"
  },
  {
    "path": "src/io/encoding_stubs.cr",
    "content": "{% unless flag?(:without_iconv) %}\n  {% skip_file %}\n{% end %}\n\nabstract class IO\n  private class Encoder\n    def initialize(encoding_options : EncodingOptions)\n      raise NotImplementedError.new(\"IO::Encoder.new\")\n    end\n\n    def write(io, slice : Bytes)\n      raise NotImplementedError.new(\"IO::Encoder#write\")\n    end\n\n    def close\n      raise NotImplementedError.new(\"IO::Encoder#close\")\n    end\n  end\n\n  private class Decoder\n    def initialize(encoding_options : EncodingOptions)\n      raise NotImplementedError.new(\"IO::Decoder.new\")\n    end\n\n    def out_slice : Bytes\n      raise NotImplementedError.new(\"IO::Decoder#out_slice\")\n    end\n\n    def read(io)\n      raise NotImplementedError.new(\"IO::Decoder#read\")\n    end\n\n    def read_byte(io)\n      raise NotImplementedError.new(\"IO::Decoder#read_byte\")\n    end\n\n    def read_utf8(io, slice)\n      raise NotImplementedError.new(\"IO::Decoder#read_utf8\")\n    end\n\n    def gets(io, delimiter : UInt8, limit : Int, chomp)\n      raise NotImplementedError.new(\"IO::Decoder#gets\")\n    end\n\n    def write(io)\n      raise NotImplementedError.new(\"IO::Decoder#write\")\n    end\n\n    def write(io, numbytes)\n      raise NotImplementedError.new(\"IO::Decoder#write\")\n    end\n\n    def close\n      raise NotImplementedError.new(\"IO::Decoder#close\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/io/error.cr",
    "content": "class IO\n  class Error < Exception\n    include SystemError\n\n    getter target : String?\n\n    protected def self.build_message(message, *, target : File) : String\n      build_message(message, target: target.path)\n    end\n\n    protected def self.build_message(message, *, target : Nil) : String\n      message\n    end\n\n    protected def self.build_message(message, *, target) : String\n      \"#{message} (#{target})\"\n    end\n\n    def initialize(message : String? = nil, cause : Exception? = nil, *, target = nil)\n      @target = target.try(&.to_s)\n\n      super message, cause\n    end\n  end\n\n  # Raised when an `IO` operation times out.\n  #\n  # ```\n  # STDIN.read_timeout = 1.second\n  # STDIN.gets # raises IO::TimeoutError (after 1 second)\n  # ```\n  class TimeoutError < Error\n  end\n\n  class EOFError < Error\n    def initialize(message = \"End of file reached\")\n      super(message)\n    end\n  end\nend\n"
  },
  {
    "path": "src/io/evented.cr",
    "content": "require \"crystal/event_loop\"\n\n{% skip_file unless flag?(:wasi) || Crystal::EventLoop.has_constant?(:LibEvent) %}\n\nrequire \"crystal/thread_local_value\"\n\n# :nodoc:\nmodule IO::Evented\n  @read_timed_out = false\n  @write_timed_out = false\n\n  @readers = Crystal::ThreadLocalValue(Deque(Fiber)).new\n  @writers = Crystal::ThreadLocalValue(Deque(Fiber)).new\n\n  @read_event = Crystal::ThreadLocalValue(Crystal::EventLoop::Event).new\n  @write_event = Crystal::ThreadLocalValue(Crystal::EventLoop::Event).new\n\n  # :nodoc:\n  def resume_read(timed_out = false) : Nil\n    @read_timed_out = timed_out\n\n    if reader = @readers.get?.try &.shift?\n      {% if flag?(:execution_context) && Crystal::EventLoop.has_constant?(:LibEvent) %}\n        event_loop = Crystal::EventLoop.current.as(Crystal::EventLoop::LibEvent)\n        event_loop.callback_enqueue(reader)\n      {% else %}\n        reader.enqueue\n      {% end %}\n    end\n  end\n\n  # :nodoc:\n  def resume_write(timed_out = false) : Nil\n    @write_timed_out = timed_out\n\n    if writer = @writers.get?.try &.shift?\n      {% if flag?(:execution_context) && Crystal::EventLoop.has_constant?(:LibEvent) %}\n        event_loop = Crystal::EventLoop.current.as(Crystal::EventLoop::LibEvent)\n        event_loop.callback_enqueue(writer)\n      {% else %}\n        writer.enqueue\n      {% end %}\n    end\n  end\n\n  # :nodoc:\n  def evented_wait_readable(timeout = @read_timeout, *, raise_if_closed = true, &) : Nil\n    readers = @readers.get { Deque(Fiber).new }\n    readers << Fiber.current\n    add_read_event(timeout)\n    Fiber.suspend\n\n    if @read_timed_out\n      @read_timed_out = false\n      yield\n    end\n\n    check_open if raise_if_closed\n  end\n\n  private def add_read_event(timeout = @read_timeout) : Nil\n    event = @read_event.get { Crystal::EventLoop.current.create_fd_read_event(self) }\n    event.add timeout\n  end\n\n  # :nodoc:\n  def evented_wait_writable(timeout = @write_timeout, &) : Nil\n    writers = @writers.get { Deque(Fiber).new }\n    writers << Fiber.current\n    add_write_event(timeout)\n    Fiber.suspend\n\n    if @write_timed_out\n      @write_timed_out = false\n      yield\n    end\n\n    check_open\n  end\n\n  private def add_write_event(timeout = @write_timeout) : Nil\n    event = @write_event.get { Crystal::EventLoop.current.create_fd_write_event(self) }\n    event.add timeout\n  end\n\n  def evented_close : Nil\n    @read_event.consume_each &.free\n\n    @write_event.consume_each &.free\n\n    @readers.consume_each do |readers|\n      {% if flag?(:execution_context) %}\n        readers.each { |fiber| fiber.execution_context.enqueue fiber }\n      {% else %}\n        Crystal::Scheduler.enqueue readers\n      {% end %}\n    end\n\n    @writers.consume_each do |writers|\n      {% if flag?(:execution_context) %}\n        writers.each { |fiber| fiber.execution_context.enqueue fiber }\n      {% else %}\n        Crystal::Scheduler.enqueue writers\n      {% end %}\n    end\n  end\n\n  # :nodoc:\n  def evented_resume_pending_readers\n    if (readers = @readers.get?) && !readers.empty?\n      add_read_event\n    end\n  end\n\n  # :nodoc:\n  def evented_resume_pending_writers\n    if (writers = @writers.get?) && !writers.empty?\n      add_write_event\n    end\n  end\nend\n"
  },
  {
    "path": "src/io/file_descriptor.cr",
    "content": "require \"crystal/system/file_descriptor\"\nrequire \"crystal/fd_lock\"\n\n# An `IO` over a file descriptor.\nclass IO::FileDescriptor < IO\n  include Crystal::System::FileDescriptor\n  include IO::Buffered\n\n  @volatile_fd : Atomic(Handle)\n  @fd_lock = Crystal::FdLock.new\n\n  # Returns the raw file-descriptor handle. Its type is platform-specific.\n  #\n  # The file-descriptor handle has been configured for the IO system\n  # requirements. If it must be in a specific mode or have a specific set of\n  # flags set, then they must be applied, even when when it feels redundant,\n  # because even the same target isn't guaranteed to have the same requirements\n  # at runtime.\n  def fd : Handle\n    @volatile_fd.get\n  end\n\n  # Whether or not to close the file descriptor when this object is finalized.\n  # Disabling this is useful in order to create an IO wrapper over a file\n  # descriptor returned from a C API that keeps ownership of the descriptor. Do\n  # note that, if the fd is closed by its owner at any point, any IO operations\n  # will then fail.\n  property? close_on_finalize : Bool\n\n  # The time to wait when reading before raising an `IO::TimeoutError`.\n  property read_timeout : Time::Span?\n\n  # Sets the number of seconds to wait when reading before raising an `IO::TimeoutError`.\n  @[Deprecated(\"Use `#read_timeout=(Time::Span?)` instead.\")]\n  def read_timeout=(read_timeout : Number) : Number\n    self.read_timeout = read_timeout.seconds\n    read_timeout\n  end\n\n  # Sets the time to wait when writing before raising an `IO::TimeoutError`.\n  property write_timeout : Time::Span?\n\n  # Sets the number of seconds to wait when writing before raising an `IO::TimeoutError`.\n  @[Deprecated(\"Use `#write_timeout=(Time::Span?)` instead.\")]\n  def write_timeout=(write_timeout : Number) : Number\n    self.write_timeout = write_timeout.seconds\n    write_timeout\n  end\n\n  {% begin %}\n    # Creates an IO::FileDescriptor from an existing system file descriptor or\n    # handle.\n    #\n    # This adopts *fd* into the IO system that will reconfigure it as per the\n    # event loop runtime requirements.\n    #\n    # NOTE: On Windows, the handle should have been created with\n    # `FILE_FLAG_OVERLAPPED`.\n    def self.new(fd : Handle, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use IO::FileDescriptor.set_blocking instead.\")] {% end %} blocking = nil, *, close_on_finalize = true)\n      file_descriptor = new(handle: fd, close_on_finalize: close_on_finalize)\n      file_descriptor.system_blocking_init(blocking) unless file_descriptor.closed?\n      file_descriptor\n    end\n  {% end %}\n\n  # :nodoc:\n  #\n  # Internal constructor to wrap a system *handle*. The *blocking* arg is purely\n  # informational.\n  def initialize(*, handle : Handle, @close_on_finalize = true, blocking = nil)\n    @volatile_fd = Atomic.new(handle)\n    @closed = true # This is necessary so we can reference `self` in `system_closed?` (in case of an exception)\n    @closed = system_closed?\n    {% if flag?(:win32) %}\n      @system_blocking = !!blocking\n    {% end %}\n  end\n\n  # :nodoc:\n  def self.from_stdio(fd : Handle) : self\n    Crystal::System::FileDescriptor.from_stdio(fd)\n  end\n\n  # Returns whether I/O operations on this file descriptor block the current\n  # thread. If false, operations might opt to suspend the current fiber instead.\n  #\n  # This might be different from the internal file descriptor. For example, when\n  # `STDIN` is a terminal on Windows, this returns `false` since the underlying\n  # blocking reads are done on a completely separate thread.\n  @[Deprecated(\"Use IO::FileDescriptor.get_blocking instead.\")]\n  def blocking : Bool\n    emulated = emulated_blocking?\n    return emulated unless emulated.nil?\n    system_blocking?\n  end\n\n  # Changes the file descriptor's mode to blocking (true) or non blocking\n  # (false).\n  #\n  # WARNING: The file descriptor has been configured to behave correctly with\n  # the event loop runtime requirements. Changing the blocking mode can cause\n  # the event loop to misbehave, for example block the entire program when a\n  # fiber tries to read from this file descriptor.\n  @[Deprecated(\"Use IO::FileDescriptor.set_blocking instead.\")]\n  def blocking=(value : Bool) : Nil\n    @fd_lock.reference { self.system_blocking = value }\n  end\n\n  # Returns whether the blocking mode of *fd* is blocking (true) or non blocking\n  # (false).\n  #\n  # NOTE: Only implemented on UNIX targets. Raises on Windows.\n  def self.get_blocking(fd : Handle) : Bool\n    Crystal::System::FileDescriptor.get_blocking(fd)\n  end\n\n  # Changes the blocking mode of *fd* to be blocking (true) or non blocking\n  # (false).\n  #\n  # NOTE: Only implemented on UNIX targets. Raises on Windows.\n  def self.set_blocking(fd : Handle, value : Bool)\n    Crystal::System::FileDescriptor.set_blocking(fd, value)\n  end\n\n  def close_on_exec? : Bool\n    system_close_on_exec?\n  end\n\n  def close_on_exec=(value : Bool) : Bool\n    @fd_lock.reference { self.system_close_on_exec = value }\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    Crystal::System::FileDescriptor.fcntl(fd, cmd, arg)\n  end\n\n  def fcntl(cmd : Int, arg : Int = 0) : Int\n    @fd_lock.reference { system_fcntl(cmd, arg) }\n  end\n\n  # Returns a `File::Info` object for this file descriptor, or raises\n  # `IO::Error` in case of an error.\n  #\n  # Certain fields like the file size may not be updated until an explicit\n  # flush.\n  #\n  # ```\n  # File.write(\"testfile\", \"abc\")\n  #\n  # file = File.new(\"testfile\", \"a\")\n  # file.info.size # => 3\n  # file << \"defgh\"\n  # file.info.size # => 3\n  # file.flush\n  # file.info.size # => 8\n  # ```\n  #\n  # Use `File.info` if the file is not open and a path to the file is available.\n  def info : File::Info\n    @fd_lock.reference { system_info }\n  end\n\n  # Seeks to a given *offset* (in bytes) according to the *whence* argument.\n  # Returns `self`.\n  #\n  # ```\n  # File.write(\"testfile\", \"abc\")\n  #\n  # file = File.new(\"testfile\")\n  # file.gets(3) # => \"abc\"\n  # file.seek(1, IO::Seek::Set)\n  # file.gets(2) # => \"bc\"\n  # file.seek(-1, IO::Seek::Current)\n  # file.gets(1) # => \"c\"\n  # ```\n  def seek(offset, whence : Seek = Seek::Set)\n    check_open\n\n    flush\n    offset -= @in_buffer_rem.size if whence.current?\n\n    @fd_lock.reference { system_seek(offset, whence) }\n\n    @in_buffer_rem = Bytes.empty\n\n    self\n  end\n\n  # Same as `seek` but yields to the block after seeking and eventually seeks\n  # back to the original position when the block returns.\n  def seek(offset, whence : Seek = Seek::Set, &)\n    original_pos = tell\n    begin\n      seek(offset, whence)\n      yield\n    ensure\n      seek(original_pos)\n    end\n  end\n\n  # Returns the current position (in bytes) in this `IO`.\n  #\n  # ```\n  # File.write(\"testfile\", \"hello\")\n  #\n  # file = File.new(\"testfile\")\n  # file.pos     # => 0\n  # file.gets(2) # => \"he\"\n  # file.pos     # => 2\n  # ```\n  protected def unbuffered_pos : Int64\n    check_open\n\n    system_pos\n  end\n\n  # Sets the current position (in bytes) in this `IO`.\n  #\n  # ```\n  # File.write(\"testfile\", \"hello\")\n  #\n  # file = File.new(\"testfile\")\n  # file.pos = 3\n  # file.gets_to_end # => \"lo\"\n  # ```\n  def pos=(value)\n    seek value\n    value\n  end\n\n  # Flushes all data written to this File Descriptor to the disk device so that\n  # all changed information can be retrieved even if the system\n  # crashes or is rebooted. The call blocks until the device reports that\n  # the transfer has completed.\n  # To reduce disk activity the *flush_metadata* parameter can be set to false,\n  # then the syscall *fdatasync* will be used and only data required for\n  # subsequent data retrieval is flushed. Metadata such as modified time and\n  # access time is not written.\n  #\n  # NOTE: Metadata is flushed even when *flush_metadata* is false on Windows\n  # and DragonFly BSD.\n  def fsync(flush_metadata : Bool = true) : Nil\n    flush\n    @fd_lock.reference { system_fsync(flush_metadata) }\n  end\n\n  # TODO: use fcntl/lockf instead of flock (which doesn't lock over NFS)\n\n  def flock_shared(blocking = true, &)\n    flock_shared blocking\n    begin\n      yield\n    ensure\n      flock_unlock\n    end\n  end\n\n  # Places a shared advisory lock. More than one process may hold a shared lock for a given file descriptor at a given time.\n  # `IO::Error` is raised if *blocking* is set to `false` and an existing exclusive lock is set.\n  def flock_shared(blocking : Bool = true) : Nil\n    system_flock_shared(blocking)\n  end\n\n  def flock_exclusive(blocking = true, &)\n    flock_exclusive blocking\n    begin\n      yield\n    ensure\n      flock_unlock\n    end\n  end\n\n  # Places an exclusive advisory lock. Only one process may hold an exclusive lock for a given file descriptor at a given time.\n  # `IO::Error` is raised if *blocking* is set to `false` and any existing lock is set.\n  def flock_exclusive(blocking : Bool = true) : Nil\n    system_flock_exclusive(blocking)\n  end\n\n  # Removes an existing advisory lock held by this process.\n  def flock_unlock : Nil\n    system_flock_unlock\n  end\n\n  # Finalizes the file descriptor resource.\n  #\n  # This involves releasing the handle to the operating system, i.e. closing it.\n  # It does *not* implicitly call `#flush`, so data waiting in the buffer may be\n  # lost.\n  # It's recommended to always close the file descriptor explicitly via `#close`\n  # (or implicitly using the `.open` constructor).\n  #\n  # Resource release can be disabled with `close_on_finalize = false`.\n  #\n  # This method is a no-op if the file descriptor has already been closed.\n  def finalize : Nil\n    return if closed? || !close_on_finalize?\n\n    Crystal::EventLoop.remove(self)\n    file_descriptor_close { } # ignore error\n  end\n\n  def closed? : Bool\n    @closed\n  end\n\n  def tty? : Bool\n    system_tty?\n  end\n\n  def reopen(other : IO::FileDescriptor) : IO::FileDescriptor\n    return other if self.fd == other.fd\n\n    other.@fd_lock.reference do\n      @fd_lock.reference { system_reopen(other) }\n    end\n\n    other\n  end\n\n  def inspect(io : IO) : Nil\n    io << \"#<IO::FileDescriptor:\"\n    if closed?\n      io << \"(closed)\"\n    else\n      io << \" fd=\" << fd\n    end\n    io << '>'\n  end\n\n  def pretty_print(pp)\n    pp.text inspect\n  end\n\n  private def unbuffered_read(slice : Bytes) : Int32\n    @fd_lock.read { system_read(slice) }\n  end\n\n  private def unbuffered_write(slice : Bytes) : Nil\n    until slice.empty?\n      slice += @fd_lock.write { system_write(slice) }\n    end\n  end\n\n  private def unbuffered_rewind : Nil\n    self.pos = 0\n  end\n\n  private def unbuffered_close : Nil\n    return if @closed\n\n    # Set before the @closed state so the pending\n    # IO::Evented readers and writers can be cancelled\n    # knowing the IO is in a closed state.\n    @closed = true\n\n    if @fd_lock.try_close? { event_loop.shutdown(self) }\n      event_loop.close(self)\n    end\n  end\n\n  private def unbuffered_flush : Nil\n    # Nothing\n  end\nend\n"
  },
  {
    "path": "src/io/hexdump.cr",
    "content": "# IO object that prints an hexadecimal dump of all transferred data.\n#\n# Especially useful for debugging binary protocols on an IO, to understand\n# better when and how data is sent or received.\n#\n# By default `IO::Hexdump` won't print anything; you must specify which of\n# `read`, `write` or both you want to print.\n#\n# Example:\n# ```\n# require \"io/hexdump\"\n# socket = IO::Memory.new(\"abc\")\n# io = IO::Hexdump.new(socket, output: STDERR, read: true)\n# ```\n#\n# When data is read from *io* it will print something akin to the following on\n# STDERR:\n# ```text\n# 00000000  50 52 49 20 2a 20 48 54  54 50 2f 32 2e 30 0d 0a  PRI * HTTP/2.0..\n# 00000010  0d 0a 53 4d 0d 0a 0d 0a                           ..SM....\n# 00000000  00 00 00 04                                       ....\n# 00000000  00                                                .\n# 00000000  00 00 00 00                                       ....\n# ```\nclass IO::Hexdump < IO\n  def initialize(@io : IO, @output : IO = STDERR, @read = false, @write = false)\n  end\n\n  def read(slice : Bytes) : Int32\n    @io.read(slice).to_i32.tap do |read_bytes|\n      slice[0, read_bytes].hexdump(@output) if @read && read_bytes\n    end\n  end\n\n  def write(slice : Bytes) : Nil\n    return if slice.empty?\n\n    @io.write(slice).tap do\n      slice.hexdump(@output) if @write\n    end\n  end\n\n  @[Deprecated(\"Use `#read(slice : Bytes)` instead\")]\n  def read(*, buf : Bytes) : Int32\n    read(buf)\n  end\n\n  @[Deprecated(\"Use `#write(slice : Bytes)` instead\")]\n  def write(*, buf : Bytes) : Nil\n    write(buf)\n  end\n\n  delegate :peek, :close, :closed?, :flush, :tty?, :pos, :pos=, :seek, to: @io\nend\n"
  },
  {
    "path": "src/io/memory.cr",
    "content": "# An `IO` that reads and writes from a buffer in memory.\n#\n# The internal buffer can be resizeable and/or writeable depending\n# on how an `IO::Memory` is constructed.\nclass IO::Memory < IO\n  # Returns the internal buffer as a `Pointer(UInt8)`.\n  getter buffer : Pointer(UInt8)\n\n  # Same as `size`.\n  getter bytesize : Int32\n\n  @capacity : Int32\n\n  # Creates an empty, resizeable and writeable `IO::Memory` with the given\n  # initial *capacity* for the internal buffer.\n  #\n  # ```\n  # io = IO::Memory.new\n  # slice = Bytes.new(1)\n  # io.pos         # => 0\n  # io.read(slice) # => 0\n  # slice          # => Bytes[0]\n  # ```\n  def initialize(capacity : Int = 64)\n    String.check_capacity_in_bounds(capacity)\n\n    @buffer = GC.malloc_atomic(capacity.to_u32).as(UInt8*)\n    @bytesize = 0\n    @capacity = capacity.to_i\n    @pos = 0\n    @closed = false\n    @resizeable = true\n    @writeable = true\n  end\n\n  # Creates an `IO::Memory` that will read, and optionally write, from/to\n  # the given slice. The created `IO::Memory` is non-resizeable.\n  #\n  # The IO starts at position zero for reading.\n  #\n  # ```\n  # slice = Slice.new(6) { |i| ('a'.ord + i).to_u8 }\n  # io = IO::Memory.new slice, writeable: false\n  # io.pos            # => 0\n  # io.read(slice)    # => 6\n  # String.new(slice) # => \"abcdef\"\n  # ```\n  def initialize(slice : Bytes, writeable = true)\n    @buffer = slice.to_unsafe\n    @bytesize = @capacity = slice.size.to_i\n    @pos = 0\n    @closed = false\n    @resizeable = false\n    @writeable = !slice.read_only? && writeable\n  end\n\n  # Creates an `IO::Memory` whose contents are the exact contents of *string*.\n  # The created `IO::Memory` is non-resizeable and non-writeable.\n  #\n  # The `IO` starts at position zero for reading.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.pos        # => 0\n  # io.gets(2)    # => \"he\"\n  # io.print \"hi\" # raises IO::Error\n  # ```\n  def self.new(string : String) : self\n    new string.to_slice, writeable: false\n  end\n\n  # See `IO#read(slice)`.\n  def read(slice : Bytes) : Int32\n    check_open\n\n    count = slice.size\n    count = Math.min(count, @bytesize - @pos)\n    slice.copy_from(@buffer + @pos, count)\n    @pos += count\n    count\n  end\n\n  # See `IO#write(slice)`. Raises if this `IO::Memory` is non-writeable,\n  # or if it's non-resizeable and a resize is needed.\n  def write(slice : Bytes) : Nil\n    check_writeable\n    check_open\n\n    count = slice.size\n\n    return if count == 0\n\n    increase_capacity_by(count)\n\n    slice.copy_to(@buffer + @pos, count)\n\n    if @pos > @bytesize\n      (@buffer + @bytesize).clear(@pos - @bytesize)\n    end\n\n    @pos += count\n    @bytesize = @pos if @pos > @bytesize\n  end\n\n  # See `IO#write_byte`. Raises if this `IO::Memory` is non-writeable,\n  # or if it's non-resizeable and a resize is needed.\n  def write_byte(byte : UInt8) : Nil\n    check_writeable\n    check_open\n\n    increase_capacity_by(1)\n\n    (@buffer + @pos).value = byte\n\n    if @pos > @bytesize\n      (@buffer + @bytesize).clear(@pos - @bytesize)\n    end\n\n    @pos += 1\n    @bytesize = @pos if @pos > @bytesize\n\n    nil\n  end\n\n  # :nodoc:\n  def gets(delimiter : Char, limit : Int32, chomp : Bool = false) : String?\n    return super if @encoding || delimiter.ord >= 128\n\n    check_open\n\n    raise ArgumentError.new \"Negative limit\" if limit < 0\n\n    index = (@buffer + @pos).to_slice(@bytesize - @pos).index(delimiter.ord)\n    if index\n      if index >= limit\n        index = limit\n      else\n        index += 1\n      end\n    else\n      index = @bytesize - @pos\n      return nil if index == 0\n\n      if index >= limit\n        index = limit\n      end\n    end\n\n    advance = index\n\n    if chomp && index > 0 && (@buffer + @pos + index - 1).value === delimiter\n      index -= 1\n\n      if delimiter == '\\n' && index > 0 && (@buffer + @pos + index - 1).value === '\\r'\n        index -= 1\n      end\n    end\n\n    string = String.new(@buffer + @pos, index)\n    @pos += advance\n    string\n  end\n\n  def read_byte : UInt8?\n    check_open\n\n    pos = Math.min(@pos, @bytesize)\n\n    if pos == @bytesize\n      nil\n    else\n      byte = @buffer[@pos]\n      @pos += 1\n      byte\n    end\n  end\n\n  def peek : Bytes\n    check_open\n\n    Slice.new(@buffer + @pos, @bytesize - @pos, read_only: !@writeable)\n  end\n\n  # :nodoc:\n  def skip(bytes_count : Int) : Nil\n    check_open\n\n    available = @bytesize - @pos\n    if available >= bytes_count\n      @pos += bytes_count\n    else\n      raise IO::EOFError.new\n    end\n  end\n\n  def skip_to_end : Nil\n    check_open\n\n    @pos = @bytesize\n  end\n\n  # :inherit:\n  def gets_to_end : String\n    return super if @encoding\n\n    check_open\n\n    if @pos >= @bytesize\n      \"\"\n    else\n      str = String.new(@buffer + @pos, @bytesize - @pos)\n      @pos = @bytesize\n      str\n    end\n  end\n\n  # :inherit:\n  def getb_to_end : Bytes\n    check_open\n\n    if @pos >= @bytesize\n      Bytes[]\n    else\n      bytes = Slice.new(@buffer + @pos, @bytesize - @pos).dup\n      @pos = @bytesize\n      bytes\n    end\n  end\n\n  # Clears the internal buffer and resets the position to zero.\n  # Raises if this `IO::Memory` is non-resizeable.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io << \"abc\"\n  # io.rewind\n  # io.gets(1) # => \"a\"\n  # io.clear\n  # io.pos         # => 0\n  # io.gets_to_end # => \"\"\n  #\n  # io = IO::Memory.new \"hello\"\n  # io.clear # raises IO::Error\n  # ```\n  def clear : Nil\n    check_open\n    check_resizeable\n    @bytesize = 0\n    @pos = 0\n  end\n\n  # Returns `true` if this `IO::Memory` has no contents.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.empty? # => true\n  # io.print \"hello\"\n  # io.empty? # => false\n  # ```\n  def empty? : Bool\n    @bytesize == 0\n  end\n\n  # Rewinds this `IO` to the initial position (zero).\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.gets(2) # => \"he\"\n  # io.rewind\n  # io.gets(2) # => \"he\"\n  # ```\n  def rewind : self\n    @pos = 0\n    self\n  end\n\n  # Returns the total number of bytes in this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.size # => 5\n  # ```\n  def size : Int32\n    @bytesize\n  end\n\n  # Seeks to a given *offset* (in bytes) according to the *whence* argument.\n  #\n  # ```\n  # io = IO::Memory.new(\"abcdef\")\n  # io.gets(3) # => \"abc\"\n  # io.seek(1, IO::Seek::Set)\n  # io.gets(2) # => \"bc\"\n  # io.seek(-1, IO::Seek::Current)\n  # io.gets(1) # => \"c\"\n  # ```\n  def seek(offset, whence : Seek = Seek::Set)\n    check_open\n\n    case whence\n    when Seek::Set\n      # Nothing\n    when Seek::Current\n      offset += @pos\n    when Seek::End\n      offset += @bytesize\n    end\n\n    self.pos = offset\n  end\n\n  # Returns the current position (in bytes) of this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.pos     # => 0\n  # io.gets(2) # => \"he\"\n  # io.pos     # => 2\n  # ```\n  def pos : Int32\n    @pos\n  end\n\n  # Sets the current position (in bytes) of this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.pos = 3\n  # io.gets # => \"lo\"\n  # ```\n  def pos=(value)\n    raise ArgumentError.new(\"Negative pos\") if value < 0\n\n    @pos = value.to_i\n  end\n\n  # Yields an `IO::Memory` to read a section of this `IO`'s buffer.\n  #\n  # During the block duration `self` becomes read-only,\n  # so multiple concurrent open are allowed.\n  def read_at(offset, bytesize, & : IO ->)\n    unless 0 <= offset <= @bytesize\n      raise ArgumentError.new(\"Offset out of bounds\")\n    end\n\n    if bytesize < 0\n      raise ArgumentError.new(\"Negative bytesize\")\n    end\n\n    unless 0 <= offset + bytesize <= @bytesize\n      raise ArgumentError.new(\"Bytesize out of bounds\")\n    end\n\n    old_writeable = @writeable\n    old_resizeable = @resizeable\n    io = IO::Memory.new(to_slice[offset, bytesize], writeable: false)\n    begin\n      @writeable = false\n      @resizeable = false\n      yield io\n    ensure\n      io.close\n      @writeable = old_writeable\n      @resizeable = old_resizeable\n    end\n  end\n\n  # Closes this `IO`. Further operations on this `IO` will raise an `IO::Error`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.close\n  # io.gets_to_end # raises IO::Error (closed stream)\n  # ```\n  def close : Nil\n    @closed = true\n  end\n\n  # Determines if this `IO` is closed.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io.closed? # => false\n  # io.close\n  # io.closed? # => true\n  # ```\n  def closed? : Bool\n    @closed\n  end\n\n  # Returns a new `String` that contains the contents of the internal buffer.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.print 1, 2, 3\n  # io.to_s # => \"123\"\n  # ```\n  def to_s : String\n    if encoding = @encoding\n      {% if flag?(:without_iconv) %}\n        raise NotImplementedError.new(\"String.encode\")\n      {% else %}\n        String.new to_slice, encoding: encoding.name, invalid: encoding.invalid\n      {% end %}\n    else\n      String.new @buffer, @bytesize\n    end\n  end\n\n  # Returns the underlying bytes.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.print \"hello\"\n  #\n  # io.to_slice # => Bytes[104, 101, 108, 108, 111]\n  # ```\n  def to_slice : Bytes\n    Slice.new(@buffer, @bytesize, read_only: !@writeable)\n  end\n\n  # Appends this internal buffer to the given `IO`.\n  def to_s(io : IO) : Nil\n    if io == self\n      # When appending to itself, we need to pull the resize up before taking\n      # pointer to the buffer. It would become invalid when a resize happens during `#write`.\n      new_bytesize = bytesize * 2\n      resize_to_capacity(new_bytesize) if @capacity < new_bytesize\n    end\n    if encoding = @encoding\n      {% if flag?(:without_iconv) %}\n        raise NotImplementedError.new(\"String.encode\")\n      {% else %}\n        String.encode(to_slice, encoding.name, io.encoding, io, io.@encoding.try(&.invalid))\n      {% end %}\n    else\n      io.write(to_slice)\n    end\n  end\n\n  private def check_writeable\n    unless @writeable\n      raise IO::Error.new \"Read-only stream\"\n    end\n  end\n\n  private def check_resizeable\n    unless @resizeable\n      raise IO::Error.new \"Non-resizeable stream\"\n    end\n  end\n\n  private def increase_capacity_by(count)\n    raise IO::EOFError.new if count >= Int32::MAX - bytesize\n\n    new_bytesize = @pos + count\n    return if new_bytesize <= @capacity\n\n    check_resizeable\n\n    new_capacity = calculate_new_capacity(new_bytesize)\n    resize_to_capacity(new_capacity)\n  end\n\n  private def calculate_new_capacity(new_bytesize : Int32)\n    # If the new bytesize is bigger than 1 << 30, the next power of two would\n    # be 1 << 31, which is out of range for Int32.\n    # So we limit the capacity to Int32::MAX in order to be able to use the\n    # range (1 << 30) < new_bytesize < Int32::MAX\n    return Int32::MAX if new_bytesize > 1 << 30\n\n    Math.pw2ceil(new_bytesize)\n  end\n\n  private def resize_to_capacity(capacity)\n    @capacity = capacity\n    @buffer = GC.realloc(@buffer, @capacity)\n  end\nend\n"
  },
  {
    "path": "src/io/multi_writer.cr",
    "content": "# An `IO` which writes to a number of underlying writer IOs.\n#\n# ```\n# io1 = IO::Memory.new\n# io2 = IO::Memory.new\n# writer = IO::MultiWriter.new(io1, io2)\n# writer.puts \"foo bar\"\n# io1.to_s # => \"foo bar\\n\"\n# io2.to_s # => \"foo bar\\n\"\n# ```\nclass IO::MultiWriter < IO\n  # If `#sync_close?` is `true`, closing this `IO` will close all of the underlying\n  # IOs.\n  property? sync_close\n  getter? closed = false\n\n  @writers : Array(IO)\n\n  # Creates a new `IO::MultiWriter` which writes to *writers*. If\n  # *sync_close* is set, calling `#close` calls `#close` on all underlying\n  # writers.\n  def initialize(@writers : Array(IO), @sync_close = false)\n  end\n\n  # Creates a new `IO::MultiWriter` which writes to *writers*. If\n  # *sync_close* is set, calling `#close` calls `#close` on all underlying\n  # writers.\n  def initialize(*writers : IO, @sync_close = false)\n    @writers = writers.map(&.as(IO)).to_a\n  end\n\n  def write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    @writers.each(&.write(slice))\n  end\n\n  def read(slice : Bytes) : NoReturn\n    raise IO::Error.new(\"Can't read from IO::MultiWriter\")\n  end\n\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    @writers.each(&.close) if sync_close?\n  end\n\n  def flush : Nil\n    @writers.each(&.flush)\n  end\nend\n"
  },
  {
    "path": "src/io/sized.cr",
    "content": "# An `IO` that wraps another `IO`, setting a limit for the number of bytes that can be read.\n#\n# ```\n# io = IO::Memory.new \"abcde\"\n# sized = IO::Sized.new(io, read_size: 3)\n#\n# sized.gets_to_end # => \"abc\"\n# sized.gets_to_end # => \"\"\n# io.gets_to_end    # => \"de\"\n# ```\nclass IO::Sized < IO\n  # If `#sync_close?` is `true`, closing this `IO` will close the underlying `IO`.\n  property? sync_close : Bool\n\n  # The number of remaining bytes to be read.\n  property read_remaining : UInt64\n  getter? closed : Bool\n\n  # Creates a new `IO::Sized` which wraps *io*, and can read a maximum of\n  # *read_size* bytes. If *sync_close* is set, calling `#close` calls\n  # `#close` on the underlying `IO`.\n  def initialize(@io : IO, read_size : Int, @sync_close = false)\n    raise ArgumentError.new \"Negative read_size\" if read_size < 0\n    @closed = false\n    @read_remaining = read_size.to_u64\n  end\n\n  def read(slice : Bytes) : Int32\n    check_open\n\n    count = {slice.size.to_u64, @read_remaining}.min\n    bytes_read = @io.read(slice[0, count]).to_i32\n    @read_remaining -= bytes_read\n    bytes_read\n  end\n\n  def read_byte : UInt8?\n    check_open\n\n    if @read_remaining > 0\n      byte = @io.read_byte\n      @read_remaining -= 1 if byte\n      byte\n    else\n      nil\n    end\n  end\n\n  def peek : Bytes?\n    check_open\n\n    return Bytes.empty if @read_remaining == 0 # EOF\n\n    peek = @io.peek\n    return nil unless peek\n\n    if @read_remaining < peek.size\n      peek = peek[0, @read_remaining]\n    end\n\n    peek\n  end\n\n  def skip(bytes_count) : Nil\n    check_open\n\n    if bytes_count <= @read_remaining\n      @io.skip(bytes_count)\n      @read_remaining -= bytes_count\n    else\n      raise IO::EOFError.new\n    end\n  end\n\n  def write(slice : Bytes) : NoReturn\n    raise IO::Error.new \"Can't write to IO::Sized\"\n  end\n\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    @io.close if @sync_close\n  end\nend\n"
  },
  {
    "path": "src/io/stapled.cr",
    "content": "# This class staples together two unidirectional `IO`s to form a single,\n# bidirectional `IO`.\n#\n# Example (loopback):\n# ```\n# io = IO::Stapled.new(*IO.pipe)\n# io.puts \"linus\"\n# io.gets # => \"linus\"\n# ```\n#\n# Most methods simply delegate to the underlying `IO`s.\nclass IO::Stapled < IO\n  # If `#sync_close?` is `true`, closing this `IO` will close the underlying `IO`s.\n  property? sync_close : Bool\n\n  # Returns `true` if this `IO` is closed.\n  #\n  # Underlying `IO`s might have a different status.\n  getter? closed : Bool = false\n\n  # Creates a new `IO::Stapled` which reads from *reader* and writes to *writer*.\n  def initialize(@reader : IO, @writer : IO, @sync_close : Bool = false)\n  end\n\n  # Reads a single byte from `reader`.\n  def read_byte : UInt8?\n    check_open\n\n    @reader.read_byte\n  end\n\n  # Reads a slice from `reader`.\n  def read(slice : Bytes) : Int32\n    check_open\n\n    @reader.read(slice).to_i32\n  end\n\n  # Gets a string from `reader`.\n  def gets(delimiter : Char, limit : Int, chomp = false) : String?\n    check_open\n\n    @reader.gets(delimiter, limit, chomp)\n  end\n\n  # Peeks into `reader`.\n  def peek : Bytes?\n    check_open\n\n    @reader.peek\n  end\n\n  # Skips `reader`.\n  def skip(bytes_count : Int) : Nil\n    check_open\n\n    @reader.skip(bytes_count)\n  end\n\n  # Skips `reader`.\n  def skip_to_end : Nil\n    check_open\n\n    @reader.skip_to_end\n  end\n\n  # Writes a byte to `writer`.\n  def write_byte(byte : UInt8) : Nil\n    check_open\n\n    @writer.write_byte(byte)\n  end\n\n  # Writes a slice to `writer`.\n  def write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    @writer.write(slice)\n  end\n\n  # Flushes `writer`.\n  def flush : self\n    check_open\n\n    @writer.flush\n\n    self\n  end\n\n  # Closes this `IO`.\n  #\n  # If `sync_close?` is `true`, it will also close the underlying `IO`s.\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    if @sync_close\n      @reader.close\n      @writer.close\n    end\n  end\n\n  # Creates a pair of bidirectional pipe endpoints connected with each other\n  # and passes them to the given block.\n  #\n  # Both endpoints and the underlying `IO`s are closed after the block\n  # (even if `sync_close?` is `false`).\n  def self.pipe(read_blocking : Bool? = nil, write_blocking : Bool? = nil, &)\n    IO.pipe(read_blocking, write_blocking) do |a_read, a_write|\n      IO.pipe(read_blocking, write_blocking) do |b_read, b_write|\n        a, b = new(a_read, b_write, true), new(b_read, a_write, true)\n        begin\n          yield a, b\n        ensure\n          a.close\n          b.close\n        end\n      end\n    end\n  end\n\n  # Creates a pair of bidirectional pipe endpoints connected with each other\n  # and returns them in a `Tuple`.\n  def self.pipe(read_blocking : Bool? = nil, write_blocking : Bool? = nil) : {self, self}\n    a_read, a_write = IO.pipe(read_blocking, write_blocking)\n    b_read, b_write = IO.pipe(read_blocking, write_blocking)\n    return new(a_read, b_write, true), new(b_read, a_write, true)\n  end\nend\n"
  },
  {
    "path": "src/io.cr",
    "content": "require \"c/stdio\"\nrequire \"c/errno\"\n{% unless flag?(:win32) %}\n  require \"c/unistd\"\n{% end %}\n\n# The `IO` class is the basis for all input and output in Crystal.\n#\n# This class is inherited by types like `File`, `Socket` and `IO::Memory` and\n# provides many useful methods for reading from and writing to an IO, like `print`, `puts`,\n# `gets` and `printf`.\n#\n# The only requirement for a type including the `IO` module is to define\n# these two methods:\n#\n# * `read(slice : Bytes)`: read at most *slice.size* bytes from IO into *slice* and return the number of bytes read\n# * `write(slice : Bytes)`: write the whole *slice* into the IO\n#\n# For example, this is a simple `IO` on top of a `Bytes`:\n#\n# ```\n# class SimpleSliceIO < IO\n#   def initialize(@slice : Bytes)\n#   end\n#\n#   def read(slice : Bytes)\n#     slice.size.times { |i| slice[i] = @slice[i] }\n#     @slice += slice.size\n#     slice.size\n#   end\n#\n#   def write(slice : Bytes) : Nil\n#     slice.size.times { |i| @slice[i] = slice[i] }\n#     @slice += slice.size\n#   end\n# end\n#\n# slice = Slice.new(9) { |i| ('a'.ord + i).to_u8 }\n# String.new(slice) # => \"abcdefghi\"\n#\n# io = SimpleSliceIO.new(slice)\n# io.gets(3) # => \"abc\"\n# io.print \"xyz\"\n# String.new(slice) # => \"abcxyzghi\"\n# ```\n#\n# ### Encoding\n#\n# An `IO` can be set an encoding with the `#set_encoding` method. When this is\n# set, all string operations (`gets`, `gets_to_end`, `read_char`, `<<`, `print`, `puts`\n# `printf`) will write in the given encoding, and read from the given encoding.\n# Byte operations (`read`, `write`, `read_byte`, `write_byte`, `getb_to_end`) never do\n# encoding/decoding operations.\n#\n# If an encoding is not set, the default one is UTF-8.\n#\n# Mixing string and byte operations might not give correct results and should be\n# avoided, as string operations might need to read extra bytes in order to get characters\n# in the given encoding.\nabstract class IO\n  # Default size used for generic stream buffers.\n  DEFAULT_BUFFER_SIZE = 32768\n\n  # Argument to a `seek` operation.\n  enum Seek\n    # Seeks to an absolute location\n    Set = 0\n\n    # Seeks to a location relative to the current location\n    # in the stream\n    Current = 1\n\n    # Seeks to a location relative to the end of the stream\n    # (you probably want a negative value for the amount)\n    End = 2\n  end\n\n  @encoding : EncodingOptions?\n  @encoder : Encoder?\n  @decoder : Decoder?\n\n  # Reads at most *slice.size* bytes from this `IO` into *slice*.\n  # Returns the number of bytes read, which is 0 if and only if there is no\n  # more data to read (so checking for 0 is the way to detect end of file).\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # slice = Bytes.new(4)\n  # io.read(slice) # => 4\n  # slice          # => Bytes[104, 101, 108, 108]\n  # io.read(slice) # => 1\n  # slice          # => Bytes[111, 101, 108, 108]\n  # io.read(slice) # => 0\n  # ```\n  abstract def read(slice : Bytes)\n\n  # Writes the contents of *slice* into this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new\n  # slice = Bytes.new(4) { |i| ('a'.ord + i).to_u8 }\n  # io.write(slice)\n  # io.to_s # => \"abcd\"\n  # ```\n  abstract def write(slice : Bytes) : Nil\n\n  # Closes this `IO`.\n  #\n  # `IO` defines this is a no-op method, but including types may override.\n  def close\n  end\n\n  # Returns `true` if this `IO` is closed.\n  #\n  # `IO` defines returns `false`, but including types may override.\n  def closed? : Bool\n    false\n  end\n\n  protected def check_open\n    raise IO::Error.new \"Closed stream\" if closed?\n  end\n\n  # Flushes buffered data, if any.\n  #\n  # `IO` defines this is a no-op method, but including types may override.\n  def flush\n  end\n\n  # Creates a pair of pipe endpoints (connected to each other)\n  # and returns them as a two-element `Tuple`.\n  #\n  # ```\n  # reader, writer = IO.pipe\n  # writer.puts \"hello\"\n  # writer.puts \"world\"\n  # reader.gets # => \"hello\"\n  # reader.gets # => \"world\"\n  # ```\n  def self.pipe(read_blocking : Bool? = nil, write_blocking : Bool? = nil) : {IO::FileDescriptor, IO::FileDescriptor}\n    r, w = Crystal::EventLoop.current.pipe(read_blocking, write_blocking)\n    w.sync = true\n    {r, w}\n  end\n\n  # Creates a pair of pipe endpoints (connected to each other) and passes them\n  # to the given block. Both endpoints are closed after the block.\n  #\n  # ```\n  # IO.pipe do |reader, writer|\n  #   writer.puts \"hello\"\n  #   writer.puts \"world\"\n  #   reader.gets # => \"hello\"\n  #   reader.gets # => \"world\"\n  # end\n  # ```\n  def self.pipe(read_blocking = nil, write_blocking = nil, &)\n    r, w = IO.pipe(read_blocking, write_blocking)\n    begin\n      yield r, w\n    ensure\n      w.flush\n      r.close\n      w.close\n    end\n  end\n\n  # Writes the given object into this `IO`.\n  # This ends up calling `to_s(io)` on the object.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io << 1\n  # io << '-'\n  # io << \"Crystal\"\n  # io.to_s # => \"1-Crystal\"\n  # ```\n  def <<(obj : _) : self\n    obj.to_s self\n    self\n  end\n\n  # Same as `<<`.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.print 1\n  # io.print '-'\n  # io.print \"Crystal\"\n  # io.to_s # => \"1-Crystal\"\n  # ```\n  def print(obj : _) : Nil\n    self << obj\n    nil\n  end\n\n  # Writes the given objects into this `IO` by invoking `to_s(io)`\n  # on each of the objects.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.print 1, '-', \"Crystal\"\n  # io.to_s # => \"1-Crystal\"\n  # ```\n  def print(*objects : _) : Nil\n    objects.each do |obj|\n      print obj\n    end\n    nil\n  end\n\n  # Writes *string* to this `IO`, followed by a newline character\n  # unless the string already ends with one.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.puts \"hello\\n\"\n  # io.puts \"world\"\n  # io.to_s # => \"hello\\nworld\\n\"\n  # ```\n  def puts(string : String) : Nil\n    self << string\n    puts unless string.ends_with?('\\n')\n    nil\n  end\n\n  # Writes *obj* to this `IO`, followed by a newline character.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.puts 1\n  # io.puts \"Crystal\"\n  # io.to_s # => \"1\\nCrystal\\n\"\n  # ```\n  def puts(obj : _) : Nil\n    self << obj\n    puts\n  end\n\n  # Writes a newline character.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.puts\n  # io.to_s # => \"\\n\"\n  # ```\n  def puts : Nil\n    print '\\n'\n    nil\n  end\n\n  # Writes *objects* to this `IO`, each followed by a newline character unless\n  # the object is a `String` and already ends with a newline.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.puts 1, '-', \"Crystal\"\n  # io.to_s # => \"1\\n-\\nCrystal\\n\"\n  # ```\n  def puts(*objects : _) : Nil\n    objects.each do |obj|\n      puts obj\n    end\n    nil\n  end\n\n  # Writes a formatted string to this IO.\n  # For details on the format string, see top-level `::printf`.\n  def printf(format_string : String, *args) : Nil\n    printf format_string, args\n  end\n\n  # :ditto:\n  def printf(format_string : String, args : Array | Tuple) : Nil\n    String::Formatter(typeof(args)).new(format_string, args, self).format\n  end\n\n  # Reads a single byte from this `IO`. Returns `nil` if there is no more\n  # data to read.\n  #\n  # ```\n  # io = IO::Memory.new \"a\"\n  # io.read_byte # => 97\n  # io.read_byte # => nil\n  # ```\n  def read_byte : UInt8?\n    byte = uninitialized UInt8\n    if read(Slice.new(pointerof(byte), 1)) == 1\n      byte\n    else\n      nil\n    end\n  end\n\n  # Reads a single `Char` from this `IO`. Returns `nil` if there is no\n  # more data to read.\n  #\n  # ```\n  # io = IO::Memory.new \"あ\"\n  # io.read_char # => 'あ'\n  # io.read_char # => nil\n  # ```\n  def read_char : Char?\n    peek = self.peek unless decoder\n    info = read_char_with_bytesize(peek)\n    info ? info[0] : nil\n  end\n\n  # :nodoc:\n  # See also: `Char::Reader#decode_char_at`.\n  private def read_char_with_bytesize(peek = nil)\n    first = peek_or_read_utf8(peek, 0)\n    return nil unless first\n    first = first.to_u32\n\n    if first < 0x80\n      return first.unsafe_chr, 1\n    end\n\n    if first < 0xc2\n      raise InvalidByteSequenceError.new(\"Unexpected byte 0x#{first.to_s(16)} in UTF-8 byte sequence\")\n    end\n\n    second = peek_or_read_utf8_masked(peek, 1)\n\n    if first < 0xe0\n      return ((first << 6) &+ (second &- 0x3080)).unsafe_chr, 2\n    end\n\n    third = peek_or_read_utf8_masked(peek, 2)\n\n    if first < 0xf0\n      if first == 0xe0 && second < 0xa0\n        raise InvalidByteSequenceError.new(\"Overlong UTF-8 encoding\")\n      end\n\n      if first == 0xed && second >= 0xa0\n        raise InvalidByteSequenceError.new(\"Invalid UTF-8 codepoint\")\n      end\n\n      return ((first << 12) &+ (second << 6) &+ (third &- 0xE2080)).unsafe_chr, 3\n    end\n\n    if first < 0xf5\n      if first == 0xf0 && second < 0x90\n        raise InvalidByteSequenceError.new(\"Overlong UTF-8 encoding\")\n      end\n\n      if first == 0xf4 && second >= 0x90\n        raise InvalidByteSequenceError.new(\"Invalid UTF-8 codepoint\")\n      end\n\n      fourth = peek_or_read_utf8_masked(peek, 3)\n      return ((first << 18) &+ (second << 12) &+ (third << 6) &+ (fourth &- 0x3C82080)).unsafe_chr, 4\n    end\n\n    raise InvalidByteSequenceError.new(\"Unexpected byte 0x#{first.to_s(16)} in UTF-8 byte sequence\")\n  end\n\n  private def peek_or_read_utf8(peek, index)\n    if peek && (byte = peek[index]?)\n      skip(1)\n      byte\n    else\n      read_utf8_byte\n    end\n  end\n\n  private def peek_or_read_utf8_masked(peek, index)\n    byte = peek_or_read_utf8(peek, index) || raise InvalidByteSequenceError.new(\"Incomplete UTF-8 byte sequence\")\n    if (byte & 0xc0) != 0x80\n      raise InvalidByteSequenceError.new(\"Unexpected continuation byte 0x#{byte.to_s(16)} in UTF-8 byte sequence\")\n    end\n    byte.to_u32\n  end\n\n  # Reads a single decoded UTF-8 byte from this `IO`.\n  # Returns `nil` if there is no more data to read.\n  #\n  # If no encoding is set, this is the same as `#read_byte`.\n  #\n  # ```\n  # bytes = \"你\".encode(\"GB2312\") # => Bytes[196, 227]\n  #\n  # io = IO::Memory.new(bytes)\n  # io.set_encoding(\"GB2312\")\n  # io.read_utf8_byte # => 228\n  # io.read_utf8_byte # => 189\n  # io.read_utf8_byte # => 160\n  # io.read_utf8_byte # => nil\n  #\n  # \"你\".bytes # => [228, 189, 160]\n  # ```\n  def read_utf8_byte : UInt8?\n    if decoder = decoder()\n      decoder.read_byte(self)\n    else\n      read_byte\n    end\n  end\n\n  # Reads UTF-8 decoded bytes into the given *slice*.\n  # Returns the number of UTF-8 bytes read.\n  #\n  # If no encoding is set, this is the same as `#read(slice)`.\n  #\n  # ```\n  # bytes = \"你\".encode(\"GB2312\") # => Bytes[196, 227]\n  #\n  # io = IO::Memory.new(bytes)\n  # io.set_encoding(\"GB2312\")\n  #\n  # buffer = uninitialized UInt8[1024]\n  # bytes_read = io.read_utf8(buffer.to_slice) # => 3\n  # buffer.to_slice[0, bytes_read]             # => Bytes[228, 189, 160]\n  #\n  # \"你\".bytes # => [228, 189, 160]\n  # ```\n  def read_utf8(slice : Bytes) : Int32\n    if decoder = decoder()\n      decoder.read_utf8(self, slice)\n    else\n      read(slice)\n    end\n  end\n\n  # Reads an UTF-8 encoded string of exactly *bytesize* bytes.\n  # Raises `EOFError` if there are not enough bytes to build\n  # the string.\n  #\n  # ```\n  # io = IO::Memory.new(\"hello world\")\n  # io.read_string(5) # => \"hello\"\n  # io.read_string(1) # => \" \"\n  # io.read_string(6) # raises IO::EOFError\n  # ```\n  def read_string(bytesize : Int) : String\n    return \"\" if bytesize == 0\n\n    String.new(bytesize) do |ptr|\n      if decoder = decoder()\n        read = decoder.read_utf8(self, Slice.new(ptr, bytesize))\n        if read != bytesize\n          raise IO::EOFError.new\n        end\n      else\n        read_fully(Slice.new(ptr, bytesize))\n      end\n      {bytesize, 0}\n    end\n  end\n\n  # Peeks into this IO, if possible.\n  #\n  # It returns:\n  # - `nil` if this IO isn't peekable at this moment or at all\n  # - an empty slice if it is, but EOF was reached\n  # - a non-empty slice if some data can be peeked\n  #\n  # The returned bytes are only valid data until a next call\n  # to any method that reads from this IO is invoked.\n  #\n  # By default this method returns `nil`, but IO implementations\n  # that provide buffering or wrap other IOs should override\n  # this method.\n  def peek : Bytes?\n    nil\n  end\n\n  # Writes the contents of *slice*, interpreted as a sequence of UTF-8 or ASCII\n  # characters, into this `IO`. The contents are transcoded into this `IO`'s\n  # current encoding.\n  #\n  # ```\n  # bytes = \"你\".to_slice # => Bytes[228, 189, 160]\n  #\n  # io = IO::Memory.new\n  # io.set_encoding(\"GB2312\")\n  # io.write_string(bytes)\n  # io.to_slice # => Bytes[196, 227]\n  #\n  # \"你\".encode(\"GB2312\") # => Bytes[196, 227]\n  # ```\n  def write_string(slice : Bytes) : Nil\n    if encoder = encoder()\n      encoder.write(self, slice)\n    else\n      write(slice)\n    end\n\n    nil\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#write_string` instead.\")]\n  def write_utf8(slice : Bytes) : Nil\n    write_string(slice)\n  end\n\n  private def encoder\n    if encoding = @encoding\n      @encoder ||= Encoder.new(encoding)\n    else\n      nil\n    end\n  end\n\n  private def decoder\n    if encoding = @encoding\n      @decoder ||= Decoder.new(encoding)\n    else\n      nil\n    end\n  end\n\n  # Tries to read exactly `slice.size` bytes from this `IO` into *slice*.\n  # Raises `EOFError` if there aren't `slice.size` bytes of data.\n  #\n  # ```\n  # io = IO::Memory.new \"123451234\"\n  # slice = Bytes.new(5)\n  # io.read_fully(slice) # => 5\n  # slice                # => Bytes[49, 50, 51, 52, 53]\n  # io.read_fully(slice) # raises IO::EOFError\n  # ```\n  #\n  # `#read_greedy` also tries to fill the entire buffer if possible,\n  # but still allows the partially filled slice to be used if an early EOF was reached.\n  def read_fully(slice : Bytes) : Int32\n    read_fully?(slice) || raise(EOFError.new)\n  end\n\n  # Tries to read exactly `slice.size` bytes from this `IO` into *slice*.\n  # Returns `nil` if there aren't `slice.size` bytes of data, otherwise\n  # returns the number of bytes read.\n  #\n  # ```\n  # io = IO::Memory.new \"123451234\"\n  # slice = Bytes.new(5)\n  # io.read_fully?(slice) # => 5\n  # slice                 # => Bytes[49, 50, 51, 52, 53]\n  # io.read_fully?(slice) # => nil\n  # ```\n  #\n  # `#read_greedy` also tries to fill the entire buffer if possible,\n  # but still allows the partially filled slice to be used if an early EOF was reached.\n  def read_fully?(slice : Bytes) : Int32?\n    count = read_greedy(slice)\n    return nil if count != slice.size\n    count\n  end\n\n  # Similar to `#read`, but with the additional guarantee that either\n  # the buffer will be entirely filled or the EOF will be reached while trying.\n  #\n  # Calling this method may result in multiple calls to `#read` if necessary.\n  #\n  # ```\n  # io = IO::Memory.new \"123451234\"\n  # slice = Bytes.new(5)\n  # io.read_greedy(slice) # => 5\n  # slice                 # => Bytes[49, 50, 51, 52, 53]\n  # io.read_greedy(slice) # => 4\n  # ```\n  #\n  # `#read_fully` and `#read_fully?` also try to fill the entire buffer but error on unexpected EOF.\n  def read_greedy(slice : Bytes) : Int32\n    count = slice.size\n    while slice.size > 0\n      read_bytes = read(slice)\n      return count &- slice.size if read_bytes == 0\n      slice += read_bytes\n    end\n    count\n  end\n\n  # Reads the rest of this `IO` data as a `String`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello world\"\n  # io.gets_to_end # => \"hello world\"\n  # io.gets_to_end # => \"\"\n  # ```\n  def gets_to_end : String\n    String.build do |str|\n      if decoder = decoder()\n        while true\n          decoder.read(self)\n          break if decoder.out_slice.empty?\n\n          decoder.write(str)\n        end\n      else\n        IO.copy(self, str)\n      end\n    end\n  end\n\n  # Reads the rest of this `IO` data as a writable `Bytes`.\n  #\n  # ```\n  # io = IO::Memory.new Bytes[0, 1, 3, 6, 10, 15]\n  # io.getb_to_end # => Bytes[0, 1, 3, 6, 10, 15]\n  # io.getb_to_end # => Bytes[]\n  # ```\n  def getb_to_end : Bytes\n    io = IO::Memory.new\n    IO.copy(self, io)\n    io.to_slice\n  end\n\n  # Reads a line from this `IO`. A line is terminated by the `\\n` character.\n  # Returns `nil` if called at the end of this `IO`.\n  #\n  # By default the newline is removed from the returned string,\n  # unless *chomp* is `false`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\\nworld\\nfoo\\n\"\n  # io.gets               # => \"hello\"\n  # io.gets(chomp: false) # => \"world\\n\"\n  # io.gets               # => \"foo\"\n  # io.gets               # => nil\n  # ```\n  def gets(chomp : Bool = true) : String?\n    gets '\\n', chomp: chomp\n  end\n\n  # Reads a line of at most *limit* bytes from this `IO`.\n  # A line is terminated by the `\\n` character.\n  # Returns `nil` if called at the end of this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\\nworld\"\n  # io.gets(3) # => \"hel\"\n  # io.gets(3) # => \"lo\\n\"\n  # io.gets(3) # => \"wor\"\n  # io.gets(3) # => \"ld\"\n  # io.gets(3) # => nil\n  # ```\n  def gets(limit : Int, chomp : Bool = false) : String?\n    gets '\\n', limit: limit, chomp: chomp\n  end\n\n  # Reads until *delimiter* is found, or the end of the `IO` is reached.\n  # Returns `nil` if called at the end of this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\\nworld\"\n  # io.gets('o') # => \"hello\"\n  # io.gets('r') # => \"\\nwor\"\n  # io.gets('z') # => \"ld\"\n  # io.gets('w') # => nil\n  # ```\n  def gets(delimiter : Char, chomp : Bool = false) : String?\n    gets delimiter, Int32::MAX, chomp: chomp\n  end\n\n  # Reads until *delimiter* is found, *limit* bytes are read, or the end of the `IO` is reached.\n  # Returns `nil` if called at the end of this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\\nworld\"\n  # io.gets('o', 3)  # => \"hel\"\n  # io.gets('r', 10) # => \"lo\\nwor\"\n  # io.gets('z', 10) # => \"ld\"\n  # io.gets('w', 10) # => nil\n  # ```\n  def gets(delimiter : Char, limit : Int, chomp = false) : String?\n    raise ArgumentError.new \"Negative limit\" if limit < 0\n\n    ascii = delimiter.ascii?\n    decoder = decoder()\n\n    # If the char's representation is a single byte and we have an encoding,\n    # search the delimiter in the buffer\n    if ascii && decoder\n      return decoder.gets(self, delimiter.ord.to_u8, limit: limit, chomp: chomp)\n    end\n\n    # If there's no encoding, the delimiter is ASCII and we can peek,\n    # use a faster algorithm\n    if ascii && !decoder && (peek = self.peek)\n      if peek.empty?\n        nil\n      else\n        gets_peek(delimiter, limit, chomp, peek)\n      end\n    else\n      gets_slow(delimiter, limit, chomp)\n    end\n  end\n\n  private def gets_peek(delimiter, limit, chomp, peek)\n    limit = Int32::MAX if limit < 0\n\n    delimiter_byte = delimiter.ord.to_u8\n\n    # We first check, if the delimiter is already in the peek buffer.\n    # In that case it's much faster to create a String from a slice\n    # of the buffer instead of appending to a IO::Memory,\n    # which happens in the other case.\n    index = peek.index(delimiter_byte)\n    if index\n      # If we find it past the limit, limit the result\n      if index >= limit\n        index = limit\n      else\n        index += 1\n      end\n\n      advance = index\n\n      if chomp && index > 0 && peek[index - 1] === delimiter_byte\n        index -= 1\n\n        if delimiter == '\\n' && index > 0 && peek[index - 1] === '\\r'\n          index -= 1\n        end\n      end\n\n      string = String.new(peek[0, index])\n      skip(advance)\n      return string\n    end\n\n    # We didn't find the delimiter, so we append to a String::Builder\n    # until we find it or we reach the limit, appending what we have\n    # in the peek buffer and peeking again.\n    String.build do |buffer|\n      while peek\n        available = Math.min(peek.size, limit)\n        buffer.write peek[0, available]\n        skip(available)\n        peek += available\n        limit -= available\n\n        if limit == 0\n          break\n        end\n\n        if peek.size == 0\n          peek = self.peek\n        end\n\n        unless peek\n          # If for some reason this IO became unpeekable,\n          # default to the slow method. One example where this can\n          # happen is `IO::Delimited`.\n          gets_slow(delimiter, limit, chomp, buffer)\n          break\n        end\n\n        if peek.empty?\n          if buffer.bytesize == 0\n            return nil\n          else\n            break\n          end\n        end\n\n        index = peek.index(delimiter_byte)\n        if index\n          if index >= limit\n            index = limit\n          else\n            index += 1\n          end\n          buffer.write peek[0, index]\n          skip(index)\n          break\n        end\n      end\n      buffer.chomp!(delimiter_byte) if chomp\n    end\n  end\n\n  private def gets_slow(delimiter : Char, limit, chomp)\n    buffer = String::Builder.new\n    bytes_read = gets_slow(delimiter, limit, chomp, buffer)\n    buffer.to_s if bytes_read\n  end\n\n  private def gets_slow(delimiter : Char, limit, chomp, buffer : String::Builder) : Bool\n    bytes_read = false\n    chomp_rn = delimiter == '\\n' && chomp\n\n    while true\n      info = read_char_with_bytesize\n      unless info\n        break\n      end\n\n      char, char_bytesize = info\n      bytes_read = true\n\n      # Consider the case of \\r\\n when the delimiter is \\n and chomp = true\n      if chomp_rn && char == '\\r'\n        info2 = read_char_with_bytesize\n        unless info2\n          buffer << char\n          break\n        end\n\n        char2, char_bytesize2 = info2\n        if char2 == '\\n'\n          break\n        end\n\n        buffer << '\\r'\n\n        break if char_bytesize >= limit\n        limit -= char_bytesize\n\n        buffer << char2\n\n        break if char_bytesize2 >= limit\n        limit -= char_bytesize2\n\n        next\n      elsif char == delimiter\n        buffer << char unless chomp\n        break\n      else\n        buffer << char\n      end\n\n      break if char_bytesize >= limit\n      limit -= char_bytesize\n    end\n\n    bytes_read\n  end\n\n  # Reads until *delimiter* is found or the end of the `IO` is reached.\n  # Returns `nil` if called at the end of this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\\nworld\"\n  # io.gets(\"wo\") # => \"hello\\nwo\"\n  # io.gets(\"wo\") # => \"rld\"\n  # io.gets(\"wo\") # => nil\n  # ```\n  def gets(delimiter : String, chomp = false) : String?\n    # Empty string: read all\n    if delimiter.empty?\n      return gets_to_end\n    end\n\n    # One byte: use gets(Char)\n    if delimiter.bytesize == 1\n      return gets(delimiter.to_unsafe[0].unsafe_chr, chomp: chomp)\n    end\n\n    # One char: use gets(Char)\n    if delimiter.size == 1\n      return gets(delimiter[0], chomp: chomp)\n    end\n\n    # The 'hard' case: we read until we match the last byte,\n    # and then compare backwards\n    last_byte = delimiter.byte_at(delimiter.bytesize - 1)\n    total_bytes = 0\n\n    buffer = String::Builder.new\n    while true\n      unless byte = read_utf8_byte\n        return buffer.empty? ? nil : buffer.to_s\n      end\n      buffer.write_byte(byte)\n      total_bytes += 1\n\n      if (byte == last_byte) &&\n         (buffer.bytesize >= delimiter.bytesize) &&\n         (buffer.buffer + total_bytes - delimiter.bytesize).memcmp(delimiter.to_unsafe, delimiter.bytesize) == 0\n        buffer.back(delimiter.bytesize) if chomp\n        break\n      end\n    end\n    buffer.to_s\n  end\n\n  # Same as `gets`, but raises `EOFError` if called at the end of this `IO`.\n  def read_line(*args, **options) : String\n    gets(*args, **options) || raise EOFError.new\n  end\n\n  # Reads and discards exactly *bytes_count* bytes.\n  # Raises `IO::EOFError` if there aren't at least *bytes_count* bytes.\n  #\n  # ```\n  # io = IO::Memory.new \"hello world\"\n  # io.skip(6)\n  # io.gets    # => \"world\"\n  # io.skip(1) # raises IO::EOFError\n  # ```\n  def skip(bytes_count : Int) : Nil\n    buffer = uninitialized UInt8[DEFAULT_BUFFER_SIZE]\n    while bytes_count > 0\n      read_count = read(buffer.to_slice[0, Math.min(bytes_count, buffer.size)])\n      raise IO::EOFError.new if read_count == 0\n\n      bytes_count -= read_count\n    end\n  end\n\n  # Reads and discards bytes from `self` until there\n  # are no more bytes.\n  def skip_to_end : Nil\n    buffer = uninitialized UInt8[DEFAULT_BUFFER_SIZE]\n    while read(buffer.to_slice) > 0\n    end\n  end\n\n  # Writes a single byte into this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.write_byte 97_u8\n  # io.to_s # => \"a\"\n  # ```\n  def write_byte(byte : UInt8) : Nil\n    x = byte\n    write Slice.new(pointerof(x), 1)\n  end\n\n  # Writes the given object to this `IO` using the specified *format*.\n  #\n  # This ends up invoking `object.to_io(self, format)`, so any object defining a\n  # `to_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)`\n  # method can be written in this way.\n  #\n  # See `Int#to_io` and `Float#to_io`.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.write_bytes(0x01020304, IO::ByteFormat::LittleEndian)\n  # io.rewind\n  # io.gets(4) # => \"\\u{4}\\u{3}\\u{2}\\u{1}\"\n  # ```\n  def write_bytes(object : _, format : IO::ByteFormat = IO::ByteFormat::SystemEndian) : Nil\n    object.to_io(self, format)\n  end\n\n  # Reads an instance of the given *type* from this `IO` using the specified *format*.\n  #\n  # This ends up invoking `type.from_io(self, format)`, so any type defining a\n  # `from_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)`\n  # method can be read in this way.\n  #\n  # See `Int.from_io` and `Float.from_io`.\n  #\n  # ```\n  # io = IO::Memory.new\n  # io.puts \"\\u{4}\\u{3}\\u{2}\\u{1}\"\n  # io.rewind\n  # io.read_bytes(Int32, IO::ByteFormat::LittleEndian) # => 0x01020304\n  # ```\n  def read_bytes(type, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)\n    type.from_io(self, format)\n  end\n\n  # Returns `true` if this `IO` is associated with a terminal device (tty), `false` otherwise.\n  #\n  # IO returns `false`, but including types may override.\n  #\n  # ```\n  # STDIN.tty?          # => true\n  # IO::Memory.new.tty? # => false\n  # ```\n  def tty? : Bool\n    false\n  end\n\n  # Invokes the given block with each *line* in this `IO`, where a line\n  # is defined by the arguments passed to this method, which can be the same\n  # ones as in the `gets` methods.\n  #\n  # ```\n  # io = IO::Memory.new(\"hello\\nworld\")\n  # io.each_line do |line|\n  #   puts line\n  # end\n  # # output:\n  # # hello\n  # # world\n  # ```\n  def each_line(*args, **options, &block : String ->) : Nil\n    while line = gets(*args, **options)\n      yield line\n    end\n  end\n\n  # Returns an `Iterator` for the *lines* in this `IO`, where a line\n  # is defined by the arguments passed to this method, which can be the same\n  # ones as in the `gets` methods.\n  #\n  # ```\n  # io = IO::Memory.new(\"hello\\nworld\")\n  # iter = io.each_line\n  # iter.next # => \"hello\"\n  # iter.next # => \"world\"\n  # ```\n  def each_line(*args, **options)\n    LineIterator.new(self, args, options)\n  end\n\n  # Invokes the given block with each `Char` in this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new(\"あめ\")\n  # io.each_char do |char|\n  #   puts char\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # あ\n  # め\n  # ```\n  def each_char(&) : Nil\n    while char = read_char\n      yield char\n    end\n  end\n\n  # Returns an `Iterator` for the chars in this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new(\"あめ\")\n  # iter = io.each_char\n  # iter.next # => 'あ'\n  # iter.next # => 'め'\n  # ```\n  def each_char\n    CharIterator.new(self)\n  end\n\n  # Invokes the given block with each byte (`UInt8`) in this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new(\"aあ\")\n  # io.each_byte do |byte|\n  #   puts byte\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # 97\n  # 227\n  # 129\n  # 130\n  # ```\n  def each_byte(&) : Nil\n    while byte = read_byte\n      yield byte\n    end\n  end\n\n  # Returns an `Iterator` for the bytes in this `IO`.\n  #\n  # ```\n  # io = IO::Memory.new(\"aあ\")\n  # iter = io.each_byte\n  # iter.next # => 97\n  # iter.next # => 227\n  # iter.next # => 129\n  # iter.next # => 130\n  # ```\n  def each_byte\n    ByteIterator.new(self)\n  end\n\n  # Rewinds this `IO`. By default this method raises, but including types\n  # may implement it.\n  def rewind\n    raise IO::Error.new(\"Can't rewind\")\n  end\n\n  # Sets the encoding of this `IO`.\n  #\n  # The *invalid* argument can be:\n  # * `nil`: an exception is raised on invalid byte sequences\n  # * `:skip`: invalid byte sequences are ignored\n  #\n  # String operations (`gets`, `gets_to_end`, `read_char`, `<<`, `print`, `puts`\n  # `printf`) will use this encoding.\n  def set_encoding(encoding : String, invalid : Symbol? = nil) : Nil\n    if utf8_encoding?(encoding, invalid)\n      @encoding = nil\n    else\n      @encoding = EncodingOptions.new(encoding, invalid)\n    end\n    @encoder.try &.close\n    @decoder.try &.close\n    @encoder = nil\n    @decoder = nil\n    nil\n  end\n\n  # Returns this `IO`'s encoding. The default is `UTF-8`.\n  def encoding : String\n    @encoding.try(&.name) || \"UTF-8\"\n  end\n\n  private def utf8_encoding?(encoding : String, invalid : Symbol? = nil) : Bool\n    invalid.nil? && (\n      encoding.compare(\"UTF-8\", case_insensitive: true) == 0 ||\n        encoding.compare(\"UTF8\", case_insensitive: true) == 0\n    )\n  end\n\n  # :nodoc:\n  def has_non_utf8_encoding? : Bool\n    !!@encoding\n  end\n\n  # Seeks to a given *offset* (in bytes) according to the *whence* argument.\n  #\n  # The `IO` class raises on this method, but some subclasses, notable\n  # `IO::FileDescriptor` and `IO::Memory` implement it.\n  #\n  # Returns `self`.\n  #\n  # ```\n  # File.write(\"testfile\", \"abc\")\n  #\n  # file = File.new(\"testfile\")\n  # file.gets(3) # => \"abc\"\n  # file.seek(1, IO::Seek::Set)\n  # file.gets(2) # => \"bc\"\n  # file.seek(-1, IO::Seek::Current)\n  # file.gets(1) # => \"c\"\n  # ```\n  def seek(offset, whence : Seek = Seek::Set)\n    raise Error.new \"Unable to seek\"\n  end\n\n  # Returns the current position (in bytes) in this `IO`.\n  #\n  # The `IO` class raises on this method, but some subclasses, notable\n  # `IO::FileDescriptor` and `IO::Memory` implement it.\n  #\n  # ```\n  # File.write(\"testfile\", \"hello\")\n  #\n  # file = File.new(\"testfile\")\n  # file.pos     # => 0\n  # file.gets(2) # => \"he\"\n  # file.pos     # => 2\n  # ```\n  def pos\n    raise Error.new \"Unable to pos\"\n  end\n\n  # Sets the current position (in bytes) in this `IO`.\n  #\n  # The `IO` class raises on this method, but some subclasses, notable\n  # `IO::FileDescriptor` and `IO::Memory` implement it.\n  #\n  # ```\n  # File.write(\"testfile\", \"hello\")\n  #\n  # file = File.new(\"testfile\")\n  # file.pos = 3\n  # file.gets_to_end # => \"lo\"\n  # ```\n  def pos=(value)\n    raise Error.new \"Unable to pos=\"\n  end\n\n  # Same as `pos`.\n  def tell\n    pos\n  end\n\n  # Yields an `IO` to read a section inside this IO.\n  #\n  # The `IO` class raises on this method, but some subclasses, notable\n  # `File` and `IO::Memory` implement it.\n  #\n  # Multiple sections can be read concurrently.\n  def read_at(offset, bytesize, & : IO ->)\n    raise Error.new \"Unable to read_at\"\n  end\n\n  # Copy all contents from *src* to *dst*.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io2 = IO::Memory.new\n  #\n  # IO.copy io, io2\n  #\n  # io2.to_s # => \"hello\"\n  # ```\n  def self.copy(src : IO, dst : IO) : Int64\n    buffer = uninitialized UInt8[DEFAULT_BUFFER_SIZE]\n    count = 0_i64\n    while (len = src.read(buffer.to_slice).to_i32) > 0\n      dst.write buffer.to_slice[0, len]\n      count &+= len\n    end\n    count\n  end\n\n  # Copy at most *limit* bytes from *src* to *dst*.\n  #\n  # ```\n  # io = IO::Memory.new \"hello\"\n  # io2 = IO::Memory.new\n  #\n  # IO.copy io, io2, 3\n  #\n  # io2.to_s # => \"hel\"\n  # ```\n  def self.copy(src : IO, dst : IO, limit : Int) : Int64\n    raise ArgumentError.new(\"Negative limit\") if limit < 0\n\n    limit = limit.to_i64\n\n    buffer = uninitialized UInt8[DEFAULT_BUFFER_SIZE]\n    remaining = limit\n    while (len = src.read(buffer.to_slice[0, Math.min(buffer.size, Math.max(remaining, 0))])) > 0\n      dst.write buffer.to_slice[0, len]\n      remaining &-= len\n    end\n    limit - remaining\n  end\n\n  # Compares two streams *stream1* to *stream2* to determine if they are identical.\n  # Returns `true` if content are the same, `false` otherwise.\n  #\n  # ```\n  # File.write(\"afile\", \"123\")\n  # stream1 = File.open(\"afile\")\n  # stream2 = IO::Memory.new(\"123\")\n  # IO.same_content?(stream1, stream2) # => true\n  # ```\n  def self.same_content?(stream1 : IO, stream2 : IO) : Bool\n    buf1 = uninitialized UInt8[1024]\n    buf2 = uninitialized UInt8[1024]\n\n    while true\n      read1 = stream1.read(buf1.to_slice)\n      if read1.zero?\n        # First stream is EOF, check if the second has more.\n        return stream2.read_byte.nil?\n      end\n      read2 = stream2.read_fully?(buf2.to_slice[0, read1])\n      return false unless read2\n\n      return false if buf1.to_unsafe.memcmp(buf2.to_unsafe, read1) != 0\n    end\n  end\n\n  private struct LineIterator(I, A, N)\n    include Iterator(String)\n\n    def initialize(@io : I, @args : A, @nargs : N)\n    end\n\n    def next\n      @io.gets(*@args, **@nargs) || stop\n    end\n  end\n\n  private struct CharIterator(I)\n    include Iterator(Char)\n\n    def initialize(@io : I)\n    end\n\n    def next\n      @io.read_char || stop\n    end\n  end\n\n  private struct ByteIterator(I)\n    include Iterator(UInt8)\n\n    def initialize(@io : I)\n    end\n\n    def next\n      @io.read_byte || stop\n    end\n  end\nend\n\nrequire \"./io/*\"\n"
  },
  {
    "path": "src/iterable.cr",
    "content": "# The `Iterable` mixin provides convenience methods to collection classes\n# that provide an `each` method that returns an `Iterator` over the collection.\nmodule Iterable(T)\n  # Must return an `Iterator` over the elements in this collection.\n  abstract def each\n\n  # Same as `each.cycle`.\n  #\n  # See also: `Iterator#cycle`.\n  def cycle\n    each.cycle\n  end\n\n  # Same as `each.cycle(n)`.\n  #\n  # See also: `Iterator#cycle(n)`.\n  def cycle(n)\n    each.cycle(n)\n  end\n\n  # Returns an Iterator that enumerates over the items, chunking them together\n  # based on the return value of the block.\n  #\n  # ```\n  # (0..7).chunk(&.//(3)).to_a # => [{0, [0, 1, 2]}, {1, [3, 4, 5]}, {2, [6, 7]}]\n  # ```\n  #\n  # See also: `Iterator#chunk`.\n  def chunk(reuse = false, &block : T -> U) forall U\n    each.chunk reuse, &block\n  end\n\n  # Same as `each.slice(count, reuse)`.\n  #\n  # See also: `Iterator#slice(count, reuse)`.\n  def each_slice(count : Int, reuse = false)\n    each.slice(count, reuse)\n  end\n\n  # Same as `each.cons(count, reuse)`.\n  #\n  # See also: `Iterator#cons(count, reuse)`.\n  def each_cons(count : Int, reuse = false)\n    each.cons(count, reuse)\n  end\n\n  # Same as `each.cons_pair`.\n  #\n  # See also: `Iterator#cons_pair`.\n  def each_cons_pair\n    each.cons_pair\n  end\n\n  # Same as `each.step(n)`.\n  #\n  # See also: `Iterator#step`.\n  def each_step(n : Int)\n    each.step(n)\n  end\n\n  # Same as `each.skip(offset).step(n)`.\n  #\n  # See also: `Iterator#step`.\n  def each_step(n : Int, *, offset : Int)\n    each.skip(offset).step(n)\n  end\n\n  # Same as `each.with_index(offset)`.\n  #\n  # See also: `Iterator#with_index(offset)`.\n  def each_with_index(offset = 0)\n    each.with_index(offset)\n  end\n\n  # Same as `each.with_object(obj)`.\n  #\n  # See also: `Iterator#with_object(obj)`.\n  def each_with_object(obj)\n    each.with_object(obj)\n  end\n\n  # Same as `each.slice_after(reuse, &block)`.\n  #\n  # See also: `Iterator#slice_after(reuse, &block)`.\n  def slice_after(reuse : Bool | Array(T) = false, &block : T -> B) forall B\n    each.slice_after(reuse, &block)\n  end\n\n  # Same as `each.slice_after(pattern, reuse)`.\n  #\n  # See also: `Iterator#slice_after(pattern, reuse)`.\n  def slice_after(pattern, reuse : Bool | Array(T) = false)\n    each.slice_after(pattern, reuse)\n  end\n\n  # Same as `each.slice_before(reuse, &block)`.\n  #\n  # See also: `Iterator#slice_before(reuse, &block)`.\n  def slice_before(reuse : Bool | Array(T) = false, &block : T -> B) forall B\n    each.slice_before(reuse, &block)\n  end\n\n  # Same as `each.slice_before(pattern, reuse)`.\n  #\n  # See also: `Iterator#slice_before(pattern, reuse)`.\n  def slice_before(pattern, reuse : Bool | Array(T) = false)\n    each.slice_before(pattern, reuse)\n  end\n\n  # Same as `each.slice_when(reuse, &block)`.\n  #\n  # See also: `Iterator#slice_when`.\n  def slice_when(reuse : Bool | Array(T) = false, &block : T, T -> B) forall B\n    each.slice_when(reuse, &block)\n  end\n\n  # Same as `each.chunk_while(reuse, &block)`.\n  #\n  # See also: `Iterator#chunk_while`.\n  def chunk_while(reuse : Bool | Array(T) = false, &block : T, T -> B) forall B\n    each.chunk_while(reuse, &block)\n  end\nend\n"
  },
  {
    "path": "src/iterator.cr",
    "content": "require \"./enumerable\"\n\n# An `Iterator` allows processing sequences lazily, as opposed to `Enumerable` which processes\n# sequences eagerly and produces an `Array` in most of its methods.\n#\n# As an example, let's compute the first three numbers in the range `1..10_000_000` that are even,\n# multiplied by three. One way to do this is:\n#\n# ```\n# (1..10_000_000).select(&.even?).map { |x| x * 3 }.first(3) # => [6, 12, 18]\n# ```\n#\n# The above works, but creates many intermediate arrays: one for the `select` call,\n# one for the `map` call and one for the `first` call. A more efficient way is to invoke\n# `Range#each` without a block, which gives us an `Iterator` so we can process the operations\n# lazily:\n#\n# ```\n# (1..10_000_000).each.select(&.even?).map { |x| x * 3 }.first(3) # => #< Iterator(T)::First...\n# ```\n#\n# `Iterator` redefines many of `Enumerable`'s method in a lazy way, returning iterators\n# instead of arrays.\n#\n# At the end of the call chain we get back a new iterator: we need to consume it, either\n# using `each` or `Enumerable#to_a`:\n#\n# ```\n# (1..10_000_000).each.select(&.even?).map { |x| x * 3 }.first(3).to_a # => [6, 12, 18]\n# ```\n#\n# Because iterators only go forward, when using methods that consume it entirely or partially –\n# `to_a`, `any?`, `count`, `none?`, `one?` and `size` – subsequent calls will give a different\n# result as there will be less elements to consume.\n#\n# ```\n# iter = (0...100).each\n# iter.size # => 100\n# iter.size # => 0\n# ```\n#\n# ### Iterating step-by-step\n#\n# An iterator returns its next element on the method `#next`.\n# Its return type is a union of the iterator's element type and the `Stop` type:\n# `T | Iterator::Stop`.\n# The stop type is a sentinel value which indicates that the iterator has\n# reached its end. It usually needs to be handled and filtered out in order to\n# use the element value for anything useful.\n# Unlike `Nil` it's not an implicitly falsey type.\n#\n# ```\n# iter = (1..5).each\n#\n# # Unfiltered elements contain `Iterator::Stop` type\n# iter.next + iter.next # Error: expected argument #1 to 'Int32#+' to be Int32, not (Int32 | Iterator::Stop)\n#\n# # Type filtering eliminates the stop type\n# a = iter.next\n# b = iter.next\n# unless a.is_a?(Iterator::Stop) || b.is_a?(Iterator::Stop)\n#   a + b # => 3\n# end\n# ```\n#\n# `Iterator::Stop` is only present in the return type of `#next`. All other\n# methods remove it from their return types.\n#\n# Iterators can be used to build a loop.\n#\n# ```\n# iter = (1..5).each\n# sum = 0\n# while !(elem = iter.next).is_a?(Iterator::Stop)\n#   sum += elem\n# end\n# sum # => 15\n# ```\n#\n# ### Implementing an Iterator\n#\n# To implement an `Iterator` you need to define a `next` method that must return the next\n# element in the sequence or `Iterator::Stop::INSTANCE`, which signals the end of the sequence\n# (you can invoke `stop` inside an iterator as a shortcut).\n#\n# For example, this is an iterator that returns a sequence of `N` zeros:\n#\n# ```\n# class Zeros\n#   include Iterator(Int32)\n#\n#   def initialize(@size : Int32)\n#     @produced = 0\n#   end\n#\n#   def next\n#     if @produced < @size\n#       @produced += 1\n#       0\n#     else\n#       stop\n#     end\n#   end\n# end\n#\n# zeros = Zeros.new(5)\n# zeros.to_a # => [0, 0, 0, 0, 0]\n# ```\n#\n# The standard library provides iterators for many classes, like `Array`, `Hash`, `Range`, `String` and `IO`.\n# Usually to get an iterator you invoke a method that would usually yield elements to a block,\n# but without passing a block: `Array#each`, `Array#each_index`, `Hash#each`, `String#each_char`,\n# `IO#each_line`, etc.\nmodule Iterator(T)\n  include Enumerable(T)\n\n  # The class that signals that there are no more elements in an `Iterator`.\n  class Stop\n    INSTANCE = new\n  end\n\n  # `IteratorWrapper` eliminates some boilerplate when defining\n  # an `Iterator` that wraps another iterator.\n  #\n  # To use it, include this module in your iterator and make sure that the wrapped\n  # iterator is stored in the `@iterator` instance variable.\n  module IteratorWrapper\n    # Invokes `next` on the wrapped iterator and returns `stop` if\n    # the given value was a `Iterator::Stop`. Otherwise, returns the value.\n    macro wrapped_next\n      %value = @iterator.next\n      return stop if %value.is_a?(Stop)\n      %value\n    end\n  end\n\n  # Shortcut for `Iterator::Stop::INSTANCE`, to signal that there are no more elements in an iterator.\n  def stop\n    Iterator.stop\n  end\n\n  # :ditto:\n  def self.stop\n    Stop::INSTANCE\n  end\n\n  # Returns an empty iterator.\n  def self.empty\n    EmptyIterator(T).new\n  end\n\n  private struct EmptyIterator(T)\n    include Iterator(T)\n\n    def next\n      stop\n    end\n  end\n\n  def self.of(element : T)\n    SingletonIterator(T).new(element)\n  end\n\n  private struct SingletonIterator(T)\n    include Iterator(T)\n\n    def initialize(@element : T)\n    end\n\n    def next\n      @element\n    end\n  end\n\n  def self.of(&block : -> T)\n    SingletonProcIterator(typeof(without_stop(&block))).new(block)\n  end\n\n  private def self.without_stop(&block : -> T)\n    e = block.call\n    raise \"\" if e.is_a?(Iterator::Stop)\n    e\n  end\n\n  private struct SingletonProcIterator(T)\n    include Iterator(T)\n\n    def initialize(@proc : (-> (T | Iterator::Stop)) | (-> T))\n    end\n\n    def next\n      @proc.call\n    end\n  end\n\n  # Returns the next element in this iterator, or `Iterator::Stop::INSTANCE` if there\n  # are no more elements.\n  abstract def next\n\n  # Returns an iterator that returns the prefix sums of the original iterator's\n  # elements.\n  #\n  # Expects `T` to respond to the `#+` method.\n  #\n  # ```\n  # iter = (3..6).each.accumulate\n  # iter.next # => 3\n  # iter.next # => 7\n  # iter.next # => 12\n  # iter.next # => 18\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def accumulate\n    accumulate { |x, y| x + y }\n  end\n\n  # Returns an iterator that returns *initial* and its prefix sums with the\n  # original iterator's elements.\n  #\n  # Expects `U` to respond to the `#+` method.\n  #\n  # ```\n  # iter = (3..6).each.accumulate(7)\n  # iter.next # => 7\n  # iter.next # => 10\n  # iter.next # => 14\n  # iter.next # => 19\n  # iter.next # => 25\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def accumulate(initial : U) forall U\n    accumulate(initial) { |x, y| x + y }\n  end\n\n  # Returns an iterator that accumulates the original iterator's elements by\n  # the given *block*.\n  #\n  # For each element of the original iterator the block is passed an accumulator\n  # value and the element. The result becomes the new value for the accumulator\n  # and is then returned. The initial value for the accumulator is the first\n  # element of the original iterator.\n  #\n  # ```\n  # iter = %w(the quick brown fox).each.accumulate { |x, y| \"#{x}, #{y}\" }\n  # iter.next # => \"the\"\n  # iter.next # => \"the, quick\"\n  # iter.next # => \"the, quick, brown\"\n  # iter.next # => \"the, quick, brown, fox\"\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def accumulate(&block : T, T -> T)\n    AccumulateIterator(typeof(self), T).new(self, block)\n  end\n\n  # Returns an iterator that accumulates *initial* with the original iterator's\n  # elements by the given *block*.\n  #\n  # Similar to `#accumulate(&block : T, T -> T)`, except the initial value is\n  # provided by an argument and needs not have the same type as the elements of\n  # the original iterator. This initial value is returned first.\n  #\n  # ```\n  # iter = [4, 3, 2].each.accumulate(\"X\") { |x, y| x * y }\n  # iter.next # => \"X\"\n  # iter.next # => \"XXXX\"\n  # iter.next # => \"XXXXXXXXXXXX\"\n  # iter.next # => \"XXXXXXXXXXXXXXXXXXXXXXXX\"\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def accumulate(initial : U, &block : U, T -> U) forall U\n    AccumulateInitIterator(typeof(self), T, U).new(self, initial, block)\n  end\n\n  private class AccumulateInitIterator(I, T, U)\n    include Iterator(U)\n\n    @acc : U | Iterator::Stop\n\n    def initialize(@iterator : I, @acc : U, @func : U, T -> U)\n    end\n\n    def next\n      old_acc = @acc\n      return old_acc if old_acc.is_a?(Iterator::Stop)\n      elem = @iterator.next\n      @acc = elem.is_a?(Iterator::Stop) ? elem : @func.call(old_acc, elem)\n      old_acc\n    end\n  end\n\n  private class AccumulateIterator(I, T)\n    include Iterator(T)\n    include IteratorWrapper\n\n    @acc : T | Iterator::Stop = Iterator::Stop::INSTANCE\n\n    def initialize(@iterator : I, @func : T, T -> T)\n    end\n\n    def next\n      elem = wrapped_next\n      old_acc = @acc\n      @acc = old_acc.is_a?(Iterator::Stop) ? elem : @func.call(old_acc, elem)\n    end\n  end\n\n  # Returns an iterator that returns elements from the original iterator until\n  # it is exhausted and then returns the elements of the second iterator.\n  # Compared to `.chain(Iterator(Iter))`, it has better performance when the quantity of\n  # iterators to chain is small (usually less than 4).\n  # This method also cannot chain iterators in a loop, for that see `.chain(Iterator(Iter))`.\n  #\n  # ```\n  # iter = (1..2).each.chain(('a'..'b').each)\n  # iter.next # => 1\n  # iter.next # => 2\n  # iter.next # => 'a'\n  # iter.next # => 'b'\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def chain(other : Iterator(U)) forall U\n    ChainIterator(typeof(self), typeof(other), T, U).new(self, other)\n  end\n\n  private class ChainIterator(I1, I2, T1, T2)\n    include Iterator(T1 | T2)\n\n    def initialize(@iterator1 : I1, @iterator2 : I2)\n      @iterator1_consumed = false\n    end\n\n    def next\n      unless @iterator1_consumed\n        value = @iterator1.next\n        if value.is_a?(Stop)\n          @iterator1_consumed = true\n        else\n          return value\n        end\n      end\n      @iterator2.next\n    end\n  end\n\n  # The same as `#chain`, but have better performance when the quantity of\n  # iterators to chain is large (usually greater than 4) or undetermined.\n  #\n  # ```\n  # array_of_iters = [[1], [2, 3], [4, 5, 6]].each.map &.each\n  # iter = Iterator(Int32).chain array_of_iters\n  # iter.next # => 1\n  # iter.next # => 2\n  # iter.next # => 3\n  # iter.next # => 4\n  # ```\n  def self.chain(iters : Iterator(Iter)) forall Iter\n    ChainsAllIterator(Iter, typeof(iters.first.first)).new iters\n  end\n\n  # the same as `.chain(Iterator(Iter))`\n  def self.chain(iters : Iterable(Iter)) forall Iter\n    chain iters.each\n  end\n\n  private class ChainsAllIterator(Iter, T)\n    include Iterator(T)\n    @iterators : Iterator(Iter)\n    @current : Iter | Stop\n\n    def initialize(@iterators)\n      @current = @iterators.next\n    end\n\n    def next : T | Stop\n      return Stop::INSTANCE if (c = @current).is_a? Stop\n      ret = c.next\n      while ret.is_a? Stop\n        c = @current = @iterators.next\n        return Stop::INSTANCE if c.is_a? Stop\n        ret = c.next\n      end\n      ret\n    end\n  end\n\n  # Returns an iterator that applies the given function to the element and then\n  # returns it unless it is `nil`. If the returned value would be `nil` it instead\n  # returns the next non `nil` value.\n  #\n  # ```\n  # iter = [1, nil, 2, nil].each.compact_map { |e| e.try &.*(2) }\n  # iter.next # => 2\n  # iter.next # => 4\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def compact_map(&func : T -> _)\n    CompactMapIterator(typeof(self), T, typeof(func.call(first).not_nil!)).new(self, func)\n  end\n\n  private struct CompactMapIterator(I, T, U)\n    include Iterator(U)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> U?)\n    end\n\n    def next\n      while true\n        value = wrapped_next\n        mapped_value = @func.call(value)\n\n        return mapped_value unless mapped_value.nil?\n      end\n    end\n  end\n\n  # Returns an iterator that returns consecutive chunks of the size *n*.\n  #\n  # ```\n  # iter = (1..5).each.cons(3)\n  # iter.next # => [1, 2, 3]\n  # iter.next # => [2, 3, 4]\n  # iter.next # => [3, 4, 5]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and returned for each consecutive call of `next`.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse*  is an instance of `Array`, `Deque` or a similar collection type (implementing `#<<`, `#shift` and `#size`) it will be used.\n  # * If *reuse* is falsey, the array will not be reused.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  #\n  # Chunks of two items can be iterated using `#cons_pair`, an optimized\n  # implementation for the special case of `n == 2` which avoids heap\n  # allocations.\n  def cons(n : Int, reuse = false)\n    raise ArgumentError.new \"Invalid cons size: #{n}\" if n <= 0\n    if reuse.nil? || reuse.is_a?(Bool)\n      # we use an initial capacity of n * 2, because a second iteration would\n      # have reallocated the array to that capacity anyway\n      ConsIterator(typeof(self), T, typeof(n), Array(T)).new(self, n, Array(T).new(n * 2), reuse)\n    else\n      ConsIterator(typeof(self), T, typeof(n), typeof(reuse)).new(self, n, reuse, reuse)\n    end\n  end\n\n  private struct ConsIterator(I, T, N, V)\n    include Iterator(Array(T))\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @n : N, values : V, reuse)\n      @values = values\n      @reuse = !!reuse\n    end\n\n    def next\n      loop do\n        elem = wrapped_next\n        @values << elem\n        @values.shift if @values.size > @n\n        break if @values.size == @n\n      end\n\n      if @reuse\n        @values\n      else\n        @values.dup\n      end\n    end\n  end\n\n  # Returns an iterator that returns consecutive pairs of adjacent items.\n  #\n  # ```\n  # iter = (1..5).each.cons_pair\n  # iter.next # => {1, 2}\n  # iter.next # => {2, 3}\n  # iter.next # => {3, 4}\n  # iter.next # => {4, 5}\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # Chunks of more than two items can be iterated using `#cons`.\n  # This method is just an optimized implementation for the special case of\n  # `n == 2` to avoid heap allocations.\n  def cons_pair : Iterator({T, T})\n    ConsTupleIterator(typeof(self), T).new(self)\n  end\n\n  private struct ConsTupleIterator(I, T)\n    include Iterator({T, T})\n    include IteratorWrapper\n\n    @last_elem : T | Iterator::Stop = Iterator::Stop::INSTANCE\n\n    def initialize(@iterator : I)\n    end\n\n    def next : {T, T} | Iterator::Stop\n      elem = wrapped_next\n      last_elem = @last_elem\n\n      if last_elem.is_a?(Iterator::Stop)\n        @last_elem = elem\n        self.next\n      else\n        value = {last_elem, elem}\n        @last_elem = elem\n        value\n      end\n    end\n  end\n\n  # Returns an iterator that repeatedly returns the elements of the original\n  # iterator forever starting back at the beginning when the end was reached.\n  #\n  # ```\n  # iter = [\"a\", \"b\", \"c\"].each.cycle\n  # iter.next # => \"a\"\n  # iter.next # => \"b\"\n  # iter.next # => \"c\"\n  # iter.next # => \"a\"\n  # iter.next # => \"b\"\n  # iter.next # => \"c\"\n  # iter.next # => \"a\"\n  # # and so an and so on\n  # ```\n  def cycle\n    CycleIterator(typeof(self), T).new(self)\n  end\n\n  private struct CycleIterator(I, T)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I)\n      @values = [] of T\n      @use_values = false\n      @index = 0\n    end\n\n    def next\n      if @use_values\n        return stop if @values.empty?\n\n        if @index >= @values.size\n          @index = 1\n          return @values.first\n        end\n\n        @index += 1\n        return @values[@index - 1]\n      end\n\n      value = @iterator.next\n\n      if value.is_a?(Stop)\n        @use_values = true\n        return stop if @values.empty?\n\n        @index = 1\n        return @values.first\n      end\n\n      @values << value\n      value\n    end\n  end\n\n  # Returns an iterator that repeatedly returns the elements of the original\n  # iterator starting back at the beginning when the end was reached,\n  # but only *n* times.\n  #\n  # ```\n  # iter = [\"a\", \"b\", \"c\"].each.cycle(2)\n  # iter.next # => \"a\"\n  # iter.next # => \"b\"\n  # iter.next # => \"c\"\n  # iter.next # => \"a\"\n  # iter.next # => \"b\"\n  # iter.next # => \"c\"\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def cycle(n : Int)\n    CycleNIterator(typeof(self), T, typeof(n)).new(self, n)\n  end\n\n  private class CycleNIterator(I, T, N)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @n : N)\n      @count = 0\n      @values = [] of T\n      @use_values = false\n      @index = 0\n    end\n\n    def next\n      return stop if @count >= @n\n\n      if @count > 0\n        return stop if @values.empty?\n\n        if @index >= @values.size\n          @count += 1\n          return stop if @count >= @n\n\n          @index = 1\n          return @values.first\n        end\n\n        @index += 1\n        return @values[@index - 1]\n      end\n\n      value = @iterator.next\n\n      if value.is_a?(Stop)\n        @count += 1\n        return stop if @count >= @n\n        return stop if @values.empty?\n\n        @index = 1\n        return @values.first\n      end\n\n      @values << value\n      value\n    end\n  end\n\n  def each\n    self\n  end\n\n  # Calls the given block once for each element, passing that element\n  # as a parameter.\n  #\n  # ```\n  # iter = [\"a\", \"b\", \"c\"].each\n  # iter.each { |x| print x, \" \" } # Prints \"a b c\"\n  # ```\n  def each(& : T ->) : Nil\n    while true\n      value = self.next\n      break if value.is_a?(Stop)\n      yield value\n    end\n  end\n\n  # Returns an iterator that then returns slices of *n* elements of the initial\n  # iterator.\n  #\n  # ```\n  # iter = (1..9).each.each_slice(3)\n  # iter.next # => [1, 2, 3]\n  # iter.next # => [4, 5, 6]\n  # iter.next # => [7, 8, 9]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each consecutive when invoking `next`.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is an `Array`, this array will be reused\n  # * If *reuse* is truthy, the method will create a new array and reuse it.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def each_slice(n, reuse = false)\n    slice(n, reuse)\n  end\n\n  # Returns an iterator that flattens nested iterators and arrays into a single iterator\n  # whose type is the union of the simple types of all of the nested iterators and arrays\n  # (and their nested iterators and arrays, and so on).\n  #\n  # ```\n  # iter = [(1..2).each, ('a'..'b').each].each.flatten\n  # iter.next # => 1\n  # iter.next # => 2\n  # iter.next # => 'a'\n  # iter.next # => 'b'\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def flatten\n    FlattenIterator(typeof(FlattenIterator.iterator_type(self)), typeof(FlattenIterator.element_type(self))).new(self)\n  end\n\n  private struct FlattenIterator(I, T)\n    include Iterator(T)\n\n    @iterator : I\n    @stopped : Array(I)\n    @generators : Array(I)\n\n    def initialize(@iterator)\n      @generators = [] of I\n      @stopped = [] of I\n    end\n\n    def next\n      case value = @iterator.next\n      when Iterator\n        @generators.push @iterator\n        @iterator = value\n        self.next\n      when Array\n        @generators.push @iterator\n        @iterator = value.each\n        self.next\n      when Stop\n        @stopped << @iterator\n        if @generators.empty?\n          stop\n        else\n          @iterator = @generators.pop\n          self.next\n        end\n      else\n        value\n      end\n    end\n\n    def self.element_type(element)\n      case element\n      when Stop\n        raise \"\"\n      when Iterator\n        element_type(element.next)\n      when Array\n        element_type(element.each)\n      else\n        element\n      end\n    end\n\n    def self.iterator_type(iter)\n      case iter\n      when Iterator\n        iter || iterator_type iter.next\n      when Array\n        iterator_type iter.each\n      else\n        raise \"\"\n      end\n    end\n  end\n\n  # Returns a new iterator with the concatenated results of running the block\n  # once for every element in the collection.\n  # Only `Array` and `Iterator` results are concatenated; every other value is\n  # returned once in the new iterator.\n  #\n  # ```\n  # iter = [1, 2, 3].each.flat_map { |x| [x, x] }\n  #\n  # iter.next # => 1\n  # iter.next # => 1\n  # iter.next # => 2\n  #\n  # iter = [1, 2, 3].each.flat_map { |x| [x, x].each }\n  #\n  # iter.to_a # => [1, 1, 2, 2, 3, 3]\n  # ```\n  def flat_map(&func : T -> _)\n    FlatMapIterator(typeof(self), typeof(FlatMapIterator.element_type(self, func)), typeof(FlatMapIterator.iterator_type(self, func)), typeof(func)).new self, func\n  end\n\n  private class FlatMapIterator(I0, T, I, F)\n    include Iterator(T)\n    include IteratorWrapper\n\n    @iterator : I0\n    @func : F\n    @nest_iterator : I?\n    @stopped : Array(I)\n\n    def initialize(@iterator, @func)\n      @nest_iterator = nil\n      @stopped = [] of I\n    end\n\n    def next\n      if iter = @nest_iterator\n        value = iter.next\n        if value.is_a?(Stop)\n          @stopped << iter\n          @nest_iterator = nil\n          self.next\n        else\n          value\n        end\n      else\n        case value = @func.call wrapped_next\n        when Array\n          @nest_iterator = value.each\n          self.next\n        when Iterator\n          @nest_iterator = value\n          self.next\n        else\n          value\n        end\n      end\n    end\n\n    def self.element_type(iter, func)\n      value = iter.next\n      raise \"\" if value.is_a?(Stop)\n\n      case value = func.call value\n      when Array, Iterator\n        value.first\n      else\n        value\n      end\n    end\n\n    def self.iterator_type(iter, func)\n      value = iter.next\n      raise \"\" if value.is_a?(Stop)\n\n      case value = func.call value\n      when Array\n        value.each\n      when Iterator\n        value\n      else\n        raise \"\"\n      end\n    end\n  end\n\n  # Returns an iterator that chunks the iterator's elements in arrays of *size*\n  # filling up the remaining elements if no element remains with `nil` or a given\n  # optional parameter.\n  #\n  # ```\n  # iter = (1..3).each.in_groups_of(2)\n  # iter.next # => [1, 2]\n  # iter.next # => [3, nil]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  # ```\n  # iter = (1..3).each.in_groups_of(2, 'z')\n  # iter.next # => [1, 2]\n  # iter.next # => [3, 'z']\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each group.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is an `Array`, this array will be reused\n  # * If *reuse* is truthy, the method will create a new array and reuse it.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def in_groups_of(size : Int, filled_up_with = nil, reuse = false)\n    raise ArgumentError.new(\"Size must be positive\") if size <= 0\n    InGroupsOfIterator(typeof(self), T, typeof(size), typeof(filled_up_with)).new(self, size, filled_up_with, reuse)\n  end\n\n  private struct InGroupsOfIterator(I, T, N, U)\n    include Iterator(Array(T | U))\n    include IteratorWrapper\n\n    @reuse : Array(T | U)?\n\n    def initialize(@iterator : I, @size : N, @filled_up_with : U, reuse)\n      if reuse\n        if reuse.is_a?(Array)\n          @reuse = reuse\n        else\n          @reuse = Array(T | U).new(@size)\n        end\n      else\n        @reuse = nil\n      end\n    end\n\n    def next\n      value = wrapped_next\n\n      if reuse = @reuse\n        reuse.clear\n        array = reuse\n      else\n        array = Array(T | U).new(@size)\n      end\n\n      array << value\n      (@size - 1).times do\n        new_value = @iterator.next\n        new_value = @filled_up_with if new_value.is_a?(Stop)\n        array << new_value\n      end\n      array\n    end\n  end\n\n  # Returns an iterator that applies the given block to the next element and\n  # returns the result.\n  #\n  # ```\n  # iter = [1, 2, 3].each.map &.*(2)\n  # iter.next # => 2\n  # iter.next # => 4\n  # iter.next # => 6\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def map(&func : T -> U) forall U\n    MapIterator(typeof(self), T, U).new(self, func)\n  end\n\n  private struct MapIterator(I, T, U)\n    include Iterator(U)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> U)\n    end\n\n    def next\n      value = wrapped_next\n      @func.call(value)\n    end\n  end\n\n  # Returns an iterator that only returns elements for which the passed in\n  # block returns a falsey value.\n  #\n  # ```\n  # iter = [1, 2, 3].each.reject &.odd?\n  # iter.next # => 2\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def reject(&func : T -> U) forall U\n    RejectIterator(typeof(self), T, U).new(self, func)\n  end\n\n  # Returns an iterator that only returns elements\n  # that are **not** of the given *type*.\n  #\n  # ```\n  # iter = [1, false, 3, true].each.reject(Bool)\n  # iter.next # => 1\n  # iter.next # => 3\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def reject(type : U.class) forall U\n    SelectTypeIterator(typeof(self), typeof(begin\n      e = first\n      e.is_a?(U) ? raise(\"\") : e\n    end)).new(self)\n  end\n\n  # Returns an iterator that only returns elements\n  # where `pattern === element` does not hold.\n  #\n  # ```\n  # iter = [2, 3, 1, 5, 4, 6].each.reject(3..5)\n  # iter.next # => 2\n  # iter.next # => 1\n  # iter.next # => 6\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def reject(pattern)\n    reject { |elem| pattern === elem }\n  end\n\n  private struct RejectIterator(I, T, B)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> B)\n    end\n\n    def next\n      while true\n        value = wrapped_next\n        unless @func.call(value)\n          return value\n        end\n      end\n    end\n  end\n\n  # Returns an iterator that only returns elements for which the passed\n  # in block returns a truthy value.\n  #\n  # ```\n  # iter = [1, 2, 3].each.select &.odd?\n  # iter.next # => 1\n  # iter.next # => 3\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def select(&func : T -> U) forall U\n    SelectIterator(typeof(self), T, U).new(self, func)\n  end\n\n  # Returns an iterator that only returns elements\n  # of the given *type*.\n  #\n  # ```\n  # iter = [1, false, 3, nil].each.select(Int32)\n  # iter.next # => 1\n  # iter.next # => 3\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def select(type : U.class) forall U\n    SelectTypeIterator(typeof(self), U).new(self)\n  end\n\n  # Returns an iterator that only returns elements\n  # where `pattern === element`.\n  #\n  # ```\n  # iter = [1, 3, 2, 5, 4, 6].each.select(3..5)\n  # iter.next # => 3\n  # iter.next # => 5\n  # iter.next # => 4\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def select(pattern)\n    self.select { |elem| pattern === elem }\n  end\n\n  private struct SelectIterator(I, T, B)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> B)\n    end\n\n    def next\n      while true\n        value = wrapped_next\n        if @func.call(value)\n          return value\n        end\n      end\n    end\n  end\n\n  private struct SelectTypeIterator(I, T)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I)\n    end\n\n    def next\n      while true\n        value = wrapped_next\n        if value.is_a?(T)\n          return value\n        end\n      end\n    end\n  end\n\n  # Returns an iterator that skips the first *n* elements and only returns\n  # the elements after that.\n  #\n  # ```\n  # iter = (1..3).each.skip(2)\n  # iter.next # -> 3\n  # iter.next # -> Iterator::Stop::INSTANCE\n  # ```\n  def skip(n : Int)\n    raise ArgumentError.new \"Attempted to skip negative size: #{n}\" if n < 0\n    SkipIterator(typeof(self), T, typeof(n)).new(self, n)\n  end\n\n  private class SkipIterator(I, T, N)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @n : N)\n      @original = @n\n    end\n\n    def next\n      while @n > 0\n        @n -= 1\n        wrapped_next\n      end\n      @iterator.next\n    end\n  end\n\n  # Returns an iterator that only starts to return elements once the given block\n  # has returned falsey value for one element.\n  #\n  # ```\n  # iter = [1, 2, 3, 4, 0].each.skip_while { |i| i < 3 }\n  # iter.next # => 3\n  # iter.next # => 4\n  # iter.next # => 0\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def skip_while(&func : T -> U) forall U\n    SkipWhileIterator(typeof(self), T, U).new(self, func)\n  end\n\n  private class SkipWhileIterator(I, T, U)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> U)\n      @returned_false = false\n    end\n\n    def next\n      while true\n        value = wrapped_next\n        return value if @returned_false\n        unless @func.call(value)\n          @returned_false = true\n          return value\n        end\n      end\n    end\n  end\n\n  # Alias of `each_slice`.\n  def slice(n : Int, reuse = false)\n    raise ArgumentError.new \"Invalid slice size: #{n}\" if n <= 0\n    SliceIterator(typeof(self), T, typeof(n)).new(self, n, reuse)\n  end\n\n  private struct SliceIterator(I, T, N)\n    include Iterator(Array(T))\n    include IteratorWrapper\n\n    @reuse : Array(T)?\n\n    def initialize(@iterator : I, @n : N, reuse)\n      if reuse\n        if reuse.is_a?(Array)\n          @reuse = reuse\n        else\n          @reuse = Array(T).new(@n)\n        end\n      else\n        @reuse = nil\n      end\n    end\n\n    def next\n      if reuse = @reuse\n        reuse.clear\n        values = reuse\n      else\n        values = Array(T).new(@n)\n      end\n\n      @n.times do\n        value = @iterator.next\n        break if value.is_a?(Stop)\n\n        values << value\n      end\n\n      if values.empty?\n        stop\n      else\n        values\n      end\n    end\n  end\n\n  # Returns an iterator that only returns every *n*th element, starting with the\n  # first.\n  #\n  # ```\n  # iter = (1..6).each.step(2)\n  # iter.next # => 1\n  # iter.next # => 3\n  # iter.next # => 5\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def step(n : Int)\n    StepByIterator(self, T, typeof(n)).new(self, n)\n  end\n\n  private struct StepByIterator(I, T, N)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @by : N)\n      raise ArgumentError.new(\"n must be greater or equal 1\") if @by < 1\n    end\n\n    def next\n      value = @iterator.next\n      return stop if value.is_a?(Stop)\n\n      (@by - 1).times do\n        @iterator.next\n      end\n\n      value\n    end\n  end\n\n  # Returns an iterator that only returns the first *n* elements of the\n  # initial iterator.\n  #\n  # ```\n  # iter = [\"a\", \"b\", \"c\"].each.first 2\n  # iter.next # => \"a\"\n  # iter.next # => \"b\"\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def first(n : Int)\n    raise ArgumentError.new \"Attempted to take negative size: #{n}\" if n < 0\n    FirstIterator(typeof(self), T, typeof(n)).new(self, n)\n  end\n\n  private class FirstIterator(I, T, N)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @n : N)\n      @original = @n\n    end\n\n    def next\n      if @n > 0\n        @n -= 1\n        wrapped_next\n      else\n        stop\n      end\n    end\n  end\n\n  # Returns an iterator that returns elements while the given block returns a\n  # truthy value.\n  #\n  # ```\n  # iter = (1..5).each.take_while { |i| i < 3 }\n  # iter.next # => 1\n  # iter.next # => 2\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def take_while(&func : T -> U) forall U\n    TakeWhileIterator(typeof(self), T, U).new(self, func)\n  end\n\n  private class TakeWhileIterator(I, T, U)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> U)\n      @returned_false = false\n    end\n\n    def next\n      return stop if @returned_false\n      value = wrapped_next\n      if @func.call(value)\n        value\n      else\n        @returned_false = true\n        stop\n      end\n    end\n  end\n\n  # Returns an iterator that calls the given block with the next element of the\n  # iterator when calling `next`, still returning the original element.\n  #\n  # ```\n  # a = 0\n  # iter = (1..3).each.tap { |x| a += x }\n  # iter.next # => 1\n  # a         # => 1\n  # iter.next # => 2\n  # a         # => 3\n  # iter.next # => 3\n  # a         # => 6\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def tap(&block : T ->)\n    TapIterator(typeof(self), T).new(self, block)\n  end\n\n  private struct TapIterator(I, T)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @proc : T ->)\n    end\n\n    def next\n      value = wrapped_next\n      @proc.call(value)\n      value\n    end\n  end\n\n  # Returns an iterator that only returns unique values of the original\n  # iterator.\n  #\n  # ```\n  # iter = [1, 2, 1].each.uniq\n  # iter.next # => 1\n  # iter.next # => 2\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def uniq\n    uniq &.itself\n  end\n\n  # Returns an iterator that only returns unique values of the original\n  # iterator. The provided block is applied to the elements to determine the\n  # value to be checked for uniqueness.\n  #\n  # ```\n  # iter = [[\"a\", \"a\"], [\"b\", \"a\"], [\"a\", \"c\"]].each.uniq &.first\n  # iter.next # => [\"a\", \"a\"]\n  # iter.next # => [\"b\", \"a\"]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def uniq(&func : T -> U) forall U\n    UniqIterator(typeof(self), T, U).new(self, func)\n  end\n\n  private struct UniqIterator(I, T, U)\n    include Iterator(T)\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @func : T -> U)\n      @set = Set(U).new\n    end\n\n    def next\n      while true\n        value = wrapped_next\n        transformed = @func.call value\n        return value if @set.add?(transformed)\n      end\n    end\n  end\n\n  # Returns an iterator that returns a `Tuple` of the element and its index.\n  #\n  # ```\n  # iter = (1..3).each.with_index\n  # iter.next # => {1, 0}\n  # iter.next # => {2, 1}\n  # iter.next # => {3, 2}\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def with_index(offset : Int = 0)\n    WithIndexIterator(typeof(self), T, typeof(offset)).new(self, offset)\n  end\n\n  # Yields each element in this iterator together with its index.\n  def with_index(offset : Int = 0, &)\n    index = offset\n    each do |value|\n      yield value, index\n      index += 1\n    end\n  end\n\n  private class WithIndexIterator(I, T, O)\n    include Iterator({T, O})\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @offset : O, @index : O = offset)\n    end\n\n    def next\n      v = wrapped_next\n      value = {v, @index}\n      @index += 1\n      value\n    end\n  end\n\n  # Returns an iterator that returns a `Tuple` of the element and a given object.\n  #\n  # ```\n  # iter = (1..3).each.with_object(\"a\")\n  # iter.next # => {1, \"a\"}\n  # iter.next # => {2, \"a\"}\n  # iter.next # => {3, \"a\"}\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def with_object(obj)\n    WithObjectIterator(typeof(self), T, typeof(obj)).new(self, obj)\n  end\n\n  # Yields each element in this iterator together with *obj*. Returns that object.\n  def with_object(obj, &)\n    each do |value|\n      yield value, obj\n    end\n    obj\n  end\n\n  private struct WithObjectIterator(I, T, O)\n    include Iterator({T, O})\n    include IteratorWrapper\n\n    def initialize(@iterator : I, @object : O)\n    end\n\n    def next\n      v = wrapped_next\n      {v, @object}\n    end\n  end\n\n  # Returns an iterator that returns the elements of this iterator and *others*\n  # traversed in tandem as `Tuple`s.\n  #\n  # Iteration stops when any of the iterators runs out of elements.\n  #\n  # ```\n  # iter1 = [4, 5, 6].each\n  # iter2 = [7, 8, 9].each\n  # iter3 = ['a', 'b', 'c', 'd'].each\n  # iter = iter1.zip(iter2, iter3)\n  # iter.next # => {4, 7, 'a'}\n  # iter.next # => {5, 8, 'b'}\n  # iter.next # => {6, 9, 'c'}\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  def zip(*others : Iterator) : Iterator\n    Iterator.zip_impl(self, *others)\n  end\n\n  protected def self.zip_impl(*iterators : *U) forall U\n    {% begin %}\n      ZipIterator(U, Tuple(\n        {% for i in 0...U.size %}\n          typeof(iterators[{{ i }}].first),\n        {% end %}\n      )).new(iterators)\n    {% end %}\n  end\n\n  private struct ZipIterator(Is, Ts)\n    include Iterator(Ts)\n\n    def initialize(@iterators : Is)\n    end\n\n    def next\n      {% begin %}\n        {% for i in 0...Is.size %}\n          %value{i} = @iterators[{{ i }}].next\n          return stop if %value{i}.is_a?(Stop)\n        {% end %}\n\n        Tuple.new(\n          {% for i in 0...Is.size %}\n            %value{i},\n          {% end %}\n        )\n      {% end %}\n    end\n  end\n\n  # Returns an Iterator that enumerates over the items,\n  # chunking them together based on the return value of the block.\n  #\n  # Consecutive elements which return the same block value are chunked together.\n  #\n  # For example, consecutive even numbers and odd numbers can be chunked as follows.\n  #\n  # ```\n  # [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk(&.even?).each do |even, ary|\n  #   p [even, ary]\n  # end\n  #\n  # # => [false, [3, 1]]\n  # #    [true, [4]]\n  # #    [false, [1, 5, 9]]\n  # #    [true, [2, 6]]\n  # #    [false, [5, 3, 5]]\n  # ```\n  #\n  # The following key values have special meaning:\n  #\n  # * `Enumerable::Chunk::Drop` specifies that the elements should be dropped\n  # * `Enumerable::Chunk::Alone` specifies that the element should be chunked by itself\n  #\n  # By default, a new array is created and yielded for each chunk when invoking `next`.\n  # * If *reuse* is given, the array can be reused\n  # * If *reuse* is an `Array`, this array will be reused\n  # * If *reuse* is truthy, the method will create a new array and reuse it.\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  #\n  # See also: `Enumerable#chunks`.\n  def chunk(reuse = false, &block : T -> U) forall T, U\n    ChunkIterator(typeof(self), T, U, typeof(::Enumerable::Chunk.key_type(self, block))).new(self, reuse, &block)\n  end\n\n  private class ChunkIterator(I, T, U, V)\n    include Iterator(Tuple(V, Array(T)))\n    @iterator : I\n    @init : {V, T}?\n\n    def initialize(@iterator : Iterator(T), reuse, &@original_block : T -> U)\n      @acc = ::Enumerable::Chunk::Accumulator(T, V).new(reuse)\n    end\n\n    def next\n      if init = @init\n        k, v = init\n        @acc.init(k, v)\n        @init = nil\n      end\n\n      @iterator.each do |val|\n        key = @original_block.call(val)\n\n        if @acc.same_as?(key)\n          @acc.add(val)\n        else\n          tuple = @acc.fetch\n          if tuple\n            @init = {key, val} unless key.is_a?(::Enumerable::Chunk::Drop.class)\n            return tuple\n          else\n            @acc.init(key, val) unless key.is_a?(::Enumerable::Chunk::Drop.class)\n          end\n        end\n      end\n\n      if tuple = @acc.fetch\n        return tuple\n      end\n\n      stop\n    end\n  end\n\n  # Returns an iterator over chunks of elements, where each\n  # chunk ends right **after** the given block's value is _truthy_.\n  #\n  # For example, to get chunks that end at each uppercase letter:\n  #\n  # ```\n  # ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']\n  # #                   ^         ^    ^\n  # iter = ary.slice_after(&.uppercase?)\n  # iter.next # => ['a', 'b', 'C']\n  # iter.next # => ['d', 'E']\n  # iter.next # => ['F']\n  # iter.next # => ['g', 'h']\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each slice when invoking `next`.\n  # * If *reuse* is `false`, the method will create a new array for each chunk\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse* is an `Array`, that array will be reused\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def slice_after(reuse : Bool | Array(T) = false, &block : T -> B) forall B\n    SliceAfterIterator(typeof(self), T, B).new(self, block, reuse)\n  end\n\n  # Returns an iterator over chunks of elements, where each\n  # chunk ends right **after** the given pattern is matched\n  # with `pattern === element`.\n  #\n  # For example, to get chunks that end at each ASCII uppercase letter:\n  #\n  # ```\n  # ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']\n  # #                   ^         ^    ^\n  # iter = ary.slice_after('A'..'Z')\n  # iter.next # => ['a', 'b', 'C']\n  # iter.next # => ['d', 'E']\n  # iter.next # => ['F']\n  # iter.next # => ['g', 'h']\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each slice when invoking `next`.\n  # * If *reuse* is `false`, the method will create a new array for each chunk\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse* is an `Array`, that array will be reused\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def slice_after(pattern, reuse : Bool | Array(T) = false)\n    slice_after(reuse) { |elem| pattern === elem }\n  end\n\n  private class SliceAfterIterator(I, T, B)\n    include Iterator(Array(T))\n\n    def initialize(@iterator : I, @block : T -> B, reuse)\n      @end = false\n      @clear_on_next = false\n\n      if reuse\n        if reuse.is_a?(Array)\n          @values = reuse\n        else\n          @values = [] of T\n        end\n        @reuse = true\n      else\n        @values = [] of T\n        @reuse = false\n      end\n    end\n\n    def next\n      return stop if @end\n\n      if @clear_on_next\n        @values.clear\n        @clear_on_next = false\n      end\n\n      while true\n        value = @iterator.next\n\n        if value.is_a?(Stop)\n          @end = true\n          if @values.empty?\n            return stop\n          else\n            return @reuse ? @values : @values.dup\n          end\n        end\n\n        @values << value\n\n        if @block.call(value)\n          @clear_on_next = true\n          return @reuse ? @values : @values.dup\n        end\n      end\n    end\n  end\n\n  # Returns an iterator over chunks of elements, where each\n  # chunk ends right **before** the given block's value is _truthy_.\n  #\n  # For example, to get chunks that end just before each uppercase letter:\n  #\n  # ```\n  # ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']\n  # #              ^         ^    ^\n  # iter = ary.slice_before(&.uppercase?)\n  # iter.next # => ['a', 'b']\n  # iter.next # => ['C', 'd']\n  # iter.next # => ['E']\n  # iter.next # => ['F', 'g', 'h']\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each slice when invoking `next`.\n  # * If *reuse* is `false`, the method will create a new array for each chunk\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse* is an `Array`, that array will be reused\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def slice_before(reuse : Bool | Array(T) = false, &block : T -> B) forall B\n    SliceBeforeIterator(typeof(self), T, B).new(self, block, reuse)\n  end\n\n  # Returns an iterator over chunks of elements, where each\n  # chunk ends right **before** the given pattern is matched\n  # with `pattern === element`.\n  #\n  # For example, to get chunks that end just before each ASCII uppercase letter:\n  #\n  # ```\n  # ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']\n  # #              ^         ^    ^\n  # iter = ary.slice_before('A'..'Z')\n  # iter.next # => ['a', 'b']\n  # iter.next # => ['C', 'd']\n  # iter.next # => ['E']\n  # iter.next # => ['F', 'g', 'h']\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each slice when invoking `next`.\n  # * If *reuse* is `false`, the method will create a new array for each chunk\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse* is an `Array`, that array will be reused\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  def slice_before(pattern, reuse : Bool | Array(T) = false)\n    slice_before(reuse) { |elem| pattern === elem }\n  end\n\n  private class SliceBeforeIterator(I, T, B)\n    include Iterator(Array(T))\n\n    @has_value_to_add = false\n    @value_to_add : T?\n\n    def initialize(@iterator : I, @block : T -> B, reuse)\n      @end = false\n\n      if reuse\n        if reuse.is_a?(Array)\n          @values = reuse\n        else\n          @values = [] of T\n        end\n        @reuse = true\n      else\n        @values = [] of T\n        @reuse = false\n      end\n    end\n\n    def next\n      return stop if @end\n\n      if @has_value_to_add\n        @has_value_to_add = false\n        @values.clear\n        @values << @value_to_add.as(T)\n        @value_to_add = nil\n      end\n\n      while true\n        value = @iterator.next\n\n        if value.is_a?(Stop)\n          @end = true\n          if @values.empty?\n            return stop\n          else\n            return @reuse ? @values : @values.dup\n          end\n        end\n\n        if !@values.empty? && @block.call(value)\n          @has_value_to_add = true\n          @value_to_add = value\n          return @reuse ? @values : @values.dup\n        end\n\n        @values << value\n      end\n    end\n  end\n\n  # Returns an iterator for each chunked elements where the ends\n  # of chunks are defined by the block, when the block's value\n  # over a pair of elements is _truthy_.\n  #\n  # For example, one-by-one increasing subsequences can be chunked as follows:\n  #\n  # ```\n  # ary = [1, 2, 4, 9, 10, 11, 12, 15, 16, 19, 20, 21]\n  # iter = ary.slice_when { |i, j| i + 1 != j }\n  # iter.next # => [1, 2]\n  # iter.next # => [4]\n  # iter.next # => [9, 10, 11, 12]\n  # iter.next # => [15, 16]\n  # iter.next # => [19, 20, 21]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each slice when invoking `next`.\n  # * If *reuse* is `false`, the method will create a new array for each chunk\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse* is an `Array`, that array will be reused\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  #\n  # See also `#chunk_while`, which works similarly but the block's condition is inverted.\n  def slice_when(reuse : Bool | Array(T) = false, &block : T, T -> B) forall B\n    SliceWhenIterator(typeof(self), T, B).new(self, block, reuse)\n  end\n\n  # Returns an iterator for each chunked elements where elements\n  # are kept in a given chunk as long as the block's value over\n  # a pair of elements is _truthy_.\n  #\n  # For example, one-by-one increasing subsequences can be chunked as follows:\n  #\n  # ```\n  # ary = [1, 2, 4, 9, 10, 11, 12, 15, 16, 19, 20, 21]\n  # iter = ary.chunk_while { |i, j| i + 1 == j }\n  # iter.next # => [1, 2]\n  # iter.next # => [4]\n  # iter.next # => [9, 10, 11, 12]\n  # iter.next # => [15, 16]\n  # iter.next # => [19, 20, 21]\n  # iter.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # By default, a new array is created and yielded for each slice when invoking `next`.\n  # * If *reuse* is `false`, the method will create a new array for each chunk\n  # * If *reuse* is `true`, the method will create a new array and reuse it.\n  # * If *reuse* is an `Array`, that array will be reused\n  #\n  # This can be used to prevent many memory allocations when each slice of\n  # interest is to be used in a read-only fashion.\n  #\n  # See also `#slice_when`, which works similarly but the block's condition is inverted.\n  def chunk_while(reuse : Bool | Array(T) = false, &block : T, T -> B) forall B\n    SliceWhenIterator(typeof(self), T, B).new(self, block, reuse, negate: true)\n  end\n\n  private class SliceWhenIterator(I, T, B)\n    include Iterator(Array(T))\n\n    @has_previous_value = false\n    @previous_value : T?\n\n    def initialize(@iterator : I, @block : T, T -> B, reuse, @negate = false)\n      @end = false\n\n      if reuse\n        if reuse.is_a?(Array)\n          @values = reuse\n        else\n          @values = [] of T\n        end\n        @reuse = true\n      else\n        @values = [] of T\n        @reuse = false\n      end\n    end\n\n    def next\n      return stop if @end\n\n      if @has_previous_value\n        v1 = @previous_value.as(T)\n        @has_previous_value = false\n        @previous_value = nil\n        @values.clear\n      else\n        v1 = @iterator.next\n        return end_value if v1.is_a?(Stop)\n      end\n\n      while true\n        @values << v1\n\n        v2 = @iterator.next\n        return end_value if v2.is_a?(Stop)\n\n        cond = @block.call(v1, v2)\n        cond = !cond if @negate\n        if cond\n          @has_previous_value = true\n          @previous_value = v2\n          return @reuse ? @values : @values.dup\n        end\n\n        v1 = v2\n      end\n    end\n\n    private def end_value\n      @end = true\n      if @values.empty?\n        stop\n      else\n        @reuse ? @values : @values.dup\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/json/any.cr",
    "content": "# `JSON::Any` is a convenient wrapper around all possible JSON types (`JSON::Any::Type`)\n# and can be used for traversing dynamic or unknown JSON structures.\n#\n# ```\n# require \"json\"\n#\n# obj = JSON.parse(%({\"access\": [{\"name\": \"mapping\", \"speed\": \"fast\"}, {\"name\": \"any\", \"speed\": \"slow\"}]}))\n# obj[\"access\"][1][\"name\"].as_s  # => \"any\"\n# obj[\"access\"][1][\"speed\"].as_s # => \"slow\"\n# ```\n#\n# Note that methods used to traverse a JSON structure, `#[]` and `#[]?`,\n# always return a `JSON::Any` to allow further traversal. To convert them to `String`,\n# `Int32`, etc., use the `as_` methods, such as `#as_s`, `#as_i`, which perform\n# a type check against the raw underlying value. This means that invoking `#as_s`\n# when the underlying value is not a String will raise: the value won't automatically\n# be converted (parsed) to a `String`. There are also nil-able variants (`#as_i?`, `#as_s?`, ...),\n# which return `nil` when the underlying value type won't match.\nstruct JSON::Any\n  # All possible JSON types.\n  alias Type = Nil | Bool | Int64 | Float64 | String | Array(JSON::Any) | Hash(String, JSON::Any)\n\n  # Reads a `JSON::Any` value from the given pull parser.\n  def self.new(pull : JSON::PullParser) : self\n    case pull.kind\n    when .null?\n      new pull.read_null\n    when .bool?\n      new pull.read_bool\n    when .int?\n      new pull.read_int\n    when .float?\n      new pull.read_float\n    when .string?\n      new pull.read_string\n    when .begin_array?\n      ary = [] of JSON::Any\n      pull.read_array do\n        ary << new(pull)\n      end\n      new ary\n    when .begin_object?\n      hash = {} of String => JSON::Any\n      pull.read_object do |key|\n        hash[key] = new(pull)\n      end\n      new hash\n    else\n      raise \"Unknown pull kind: #{pull.kind}\"\n    end\n  end\n\n  # Returns the raw underlying value.\n  getter raw : Type\n\n  # Creates a `JSON::Any` that wraps the given value.\n  def initialize(@raw : Type)\n  end\n\n  # :ditto:\n  def self.new(raw : Int) : self\n    # FIXME: Workaround for https://github.com/crystal-lang/crystal/issues/11645\n    new(raw.to_i64)\n  end\n\n  # :ditto:\n  def self.new(raw : Float) : self\n    # FIXME: Workaround for https://github.com/crystal-lang/crystal/issues/11645\n    new(raw.to_f64)\n  end\n\n  # Assumes the underlying value is an `Array` or `Hash` and returns its size.\n  # Raises if the underlying value is not an `Array` or `Hash`.\n  def size : Int\n    case object = @raw\n    when Array, Hash\n      object.size\n    else\n      raise \"Expected Array or Hash for #size, not #{object.class}\"\n    end\n  end\n\n  # Assumes the underlying value is an `Array` and returns the element\n  # at the given index.\n  # Raises if the underlying value is not an `Array`.\n  def [](index : Int) : JSON::Any\n    case object = @raw\n    when Array\n      object[index]\n    else\n      raise \"Expected Array for #[](index : Int), not #{object.class}\"\n    end\n  end\n\n  # Assumes the underlying value is an `Array` and returns the element\n  # at the given index, or `nil` if out of bounds.\n  # Raises if the underlying value is not an `Array`.\n  def []?(index : Int) : JSON::Any?\n    case object = @raw\n    when Array\n      object[index]?\n    else\n      raise \"Expected Array for #[]?(index : Int), not #{object.class}\"\n    end\n  end\n\n  # Assumes the underlying value is a `Hash` and returns the element\n  # with the given key.\n  # Raises if the underlying value is not a `Hash`.\n  def [](key : String) : JSON::Any\n    case object = @raw\n    when Hash\n      object[key]\n    else\n      raise \"Expected Hash for #[](key : String), not #{object.class}\"\n    end\n  end\n\n  # Assumes the underlying value is a `Hash` and returns the element\n  # with the given key, or `nil` if the key is not present.\n  # Raises if the underlying value is not a `Hash`.\n  def []?(key : String) : JSON::Any?\n    case object = @raw\n    when Hash\n      object[key]?\n    else\n      raise \"Expected Hash for #[]?(key : String), not #{object.class}\"\n    end\n  end\n\n  # Traverses the depth of a structure and returns the value.\n  # Returns `nil` if not found.\n  def dig?(index_or_key : Int | String, *subkeys : Int | String) : JSON::Any?\n    self[index_or_key]?.try &.dig?(*subkeys)\n  end\n\n  # :nodoc:\n  def dig?(index_or_key : Int | String) : JSON::Any?\n    case @raw\n    when Hash, Array\n      self[index_or_key]?\n    else\n      nil\n    end\n  end\n\n  # Traverses the depth of a structure and returns the value, otherwise raises.\n  def dig(index_or_key : Int | String, *subkeys : Int | String) : JSON::Any\n    self[index_or_key].dig(*subkeys)\n  end\n\n  # :nodoc:\n  def dig(index_or_key : Int | String) : JSON::Any\n    self[index_or_key]\n  end\n\n  # Checks that the underlying value is `Nil`, and returns `nil`.\n  # Raises otherwise.\n  def as_nil : Nil\n    @raw.as(Nil)\n  end\n\n  # Checks that the underlying value is `Bool`, and returns its value.\n  # Raises otherwise.\n  def as_bool : Bool\n    @raw.as(Bool)\n  end\n\n  # Checks that the underlying value is `Bool`, and returns its value.\n  # Returns `nil` otherwise.\n  def as_bool? : Bool?\n    as_bool if @raw.is_a?(Bool)\n  end\n\n  # Checks that the underlying value is `Int`, and returns its value as an `Int32`.\n  # Raises otherwise.\n  def as_i : Int32\n    @raw.as(Int).to_i\n  end\n\n  # Checks that the underlying value is `Int`, and returns its value as an `Int32`.\n  # Returns `nil` otherwise.\n  def as_i? : Int32?\n    as_i if @raw.is_a?(Int)\n  end\n\n  # Checks that the underlying value is `Int`, and returns its value as an `Int64`.\n  # Raises otherwise.\n  def as_i64 : Int64\n    @raw.as(Int).to_i64\n  end\n\n  # Checks that the underlying value is `Int`, and returns its value as an `Int64`.\n  # Returns `nil` otherwise.\n  def as_i64? : Int64?\n    as_i64 if @raw.is_a?(Int64)\n  end\n\n  # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float64`.\n  # Raises otherwise.\n  def as_f : Float64\n    case raw = @raw\n    when Int\n      raw.to_f\n    else\n      raw.as(Float64)\n    end\n  end\n\n  # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float64`.\n  # Returns `nil` otherwise.\n  def as_f? : Float64?\n    case raw = @raw\n    when Int\n      raw.to_f\n    else\n      raw.as?(Float64)\n    end\n  end\n\n  # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float32`.\n  # Raises otherwise.\n  def as_f32 : Float32\n    case raw = @raw\n    when Int\n      raw.to_f32\n    else\n      raw.as(Float).to_f32\n    end\n  end\n\n  # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float32`.\n  # Returns `nil` otherwise.\n  def as_f32? : Float32?\n    case raw = @raw\n    when Int\n      raw.to_f32\n    when Float\n      raw.to_f32\n    else\n      nil\n    end\n  end\n\n  # Checks that the underlying value is `String`, and returns its value.\n  # Raises otherwise.\n  def as_s : String\n    @raw.as(String)\n  end\n\n  # Checks that the underlying value is `String`, and returns its value.\n  # Returns `nil` otherwise.\n  def as_s? : String?\n    as_s if @raw.is_a?(String)\n  end\n\n  # Checks that the underlying value is `Array`, and returns its value.\n  # Raises otherwise.\n  def as_a : Array(JSON::Any)\n    @raw.as(Array)\n  end\n\n  # Checks that the underlying value is `Array`, and returns its value.\n  # Returns `nil` otherwise.\n  def as_a? : Array(JSON::Any)?\n    as_a if @raw.is_a?(Array)\n  end\n\n  # Checks that the underlying value is `Hash`, and returns its value.\n  # Raises otherwise.\n  def as_h : Hash(String, JSON::Any)\n    @raw.as(Hash)\n  end\n\n  # Checks that the underlying value is `Hash`, and returns its value.\n  # Returns `nil` otherwise.\n  def as_h? : Hash(String, JSON::Any)?\n    as_h if @raw.is_a?(Hash)\n  end\n\n  def inspect(io : IO) : Nil\n    # == Why do we need exec_recursive_hash here? ==\n    # Our goal is to produce something like \"JSON::Any([1,2,3])\", with the tag only\n    # on the outside. A naive implementation would result in\n    # \"JSON::Any([JSON::Any(1), JSON::Any(2), JSON::Any(3)])\", because we need to\n    # do raw recursion through Array#inspect and Hash#inspect, which contain more\n    # JSON::Any objects, and there is no way to know whether we are \"at the root\".\n    # The two options we have are: set a stack-scoped flag indicating that we\n    # shouldn't wrap with \"JSON::Any(\", or re-implement Array#inspect and\n    # Hash#inspect, both of which require exec_recursive anyways.\n\n    # Once we have a more general method of setting fiber-local variables and flags,\n    # this could be significantly simplified.\n\n    hash = Fiber.current.exec_recursive_hash\n\n    # Here the key doesn't matter, we want to mask *all* invocations\n    # of JSON::Any#inspect below this stack frame\n    inspect_key = {0_u64, :__json_inspect__}\n\n    hash.put(inspect_key, nil) do\n      # here's the root case, where we wrap with JSON::Any(...)\n      io << \"JSON::Any(\"\n      @raw.inspect(io)\n      io << \")\"\n      return\n    ensure\n      hash.delete(inspect_key)\n    end\n\n    # This is the non-root case, where we don't wrap with anything.\n    @raw.inspect(io)\n  end\n\n  def to_s(io : IO) : Nil\n    @raw.to_s(io)\n  end\n\n  # :nodoc:\n  def pretty_print(pp)\n    @raw.pretty_print(pp)\n  end\n\n  # Returns `true` if both `self` and *other*'s raw object are equal.\n  def ==(other : JSON::Any)\n    raw == other.raw\n  end\n\n  # Returns `true` if the raw object is equal to *other*.\n  def ==(other)\n    raw == other\n  end\n\n  # See `Object#hash(hasher)`\n  def_hash raw\n\n  # :nodoc:\n  def to_json(json : JSON::Builder) : Nil\n    raw.to_json(json)\n  end\n\n  def to_yaml(yaml : YAML::Nodes::Builder) : Nil\n    raw.to_yaml(yaml)\n  end\n\n  # Returns a new JSON::Any instance with the `raw` value `dup`ed.\n  def dup\n    JSON::Any.new(raw.dup)\n  end\n\n  # Returns a new JSON::Any instance with the `raw` value `clone`ed.\n  def clone : JSON::Any\n    JSON::Any.new(raw.clone)\n  end\nend\n\nclass Object\n  def ===(other : JSON::Any)\n    self === other.raw\n  end\nend\n\nstruct Value\n  def ==(other : JSON::Any)\n    self == other.raw\n  end\nend\n\nclass Reference\n  def ==(other : JSON::Any)\n    self == other.raw\n  end\nend\n\nclass Array\n  def ==(other : JSON::Any)\n    self == other.raw\n  end\nend\n\nclass Hash\n  def ==(other : JSON::Any)\n    self == other.raw\n  end\nend\n\nclass Regex\n  def ===(other : JSON::Any)\n    value = self === other.raw\n    $~ = $~\n    value\n  end\nend\n"
  },
  {
    "path": "src/json/builder.cr",
    "content": "# A JSON builder generates valid JSON.\n#\n# A `JSON::Error` is raised if attempting to generate an invalid JSON\n# (for example, if invoking `end_array` without a matching `start_array`,\n# or trying to use a non-string value as an object's field name).\nclass JSON::Builder\n  private getter io\n\n  record StartState\n  record DocumentStartState\n  record ArrayState, empty : Bool\n  record ObjectState, empty : Bool, name : Bool\n  record DocumentEndState\n\n  alias State = StartState | DocumentStartState | ArrayState | ObjectState | DocumentEndState\n\n  @indent : String?\n\n  # By default the maximum nesting of arrays/objects is 99. Nesting more\n  # than this will result in a JSON::Error. Changing the value of this property\n  # allows more/less nesting.\n  property max_nesting = 99\n\n  # Creates a `JSON::Builder` that will write to the given `IO`.\n  def initialize(@io : IO)\n    @state = [StartState.new] of State\n    @current_indent = 0\n    @escape = Escape.new(io)\n  end\n\n  # Starts a document.\n  def start_document : Nil\n    case @state.last\n    when StartState\n      @state[-1] = DocumentStartState.new\n    when DocumentEndState\n      @state[-1] = DocumentStartState.new\n    else\n      raise JSON::Error.new(\"Starting document before ending previous one\")\n    end\n  end\n\n  # Signals the end of a JSON document.\n  def end_document : Nil\n    case @state.last\n    when StartState\n      raise JSON::Error.new(\"Empty JSON\")\n    when DocumentStartState\n      raise JSON::Error.new(\"Empty JSON\")\n    when ArrayState\n      raise JSON::Error.new(\"Unterminated JSON array\")\n    when ObjectState\n      raise JSON::Error.new(\"Unterminated JSON object\")\n    when DocumentEndState\n      # okay\n    end\n    flush\n  end\n\n  def document(&)\n    start_document\n    yield.tap { end_document }\n  end\n\n  # Writes a `null` value.\n  def null : Nil\n    scalar do\n      @io << \"null\"\n    end\n  end\n\n  # Writes a boolean value.\n  def bool(value : Bool) : Nil\n    scalar do\n      @io << value\n    end\n  end\n\n  # Writes an integer.\n  def number(number : Int) : Nil\n    scalar do\n      @io << number\n    end\n  end\n\n  # Writes a float.\n  def number(number : Float) : Nil\n    scalar do\n      case number\n      when .nan?\n        raise JSON::Error.new(\"NaN not allowed in JSON\")\n      when .infinite?\n        raise JSON::Error.new(\"Infinity not allowed in JSON\")\n      else\n        @io << number\n      end\n    end\n  end\n\n  # Writes a string with the content of `value`.\n  # The payload is stringified via `to_s(IO)` and escaped for JSON representation.\n  #\n  # ```\n  # JSON.build do |builder|\n  #   builder.string(\"foo\")\n  # end # => %(\"foo\")\n  # JSON.build do |builder|\n  #   builder.string([1, 2])\n  # end # => %(\"[1, 2]\")\n  # ```\n  #\n  # This method can also be used to write the name of an object field.\n  def string(value : _) : Nil\n    string do |io|\n      value.to_s(io)\n    end\n  end\n\n  # Writes a string with the contents written to the yielded `IO`.\n  # The payload gets escaped for JSON representation.\n  #\n  # ```\n  # JSON.build do |builder|\n  #   builder.string do |io|\n  #     io << \"foo\"\n  #     io << [1, 2]\n  #   end # => %(\"foo[1, 2]\")\n  # end\n  # ```\n  #\n  # This method can also be used to write the name of an object field.\n  def string(& : IO ->) : Nil\n    scalar(string: true) do\n      io << '\"'\n      yield @escape\n      io << '\"'\n    end\n  end\n\n  private class Escape < IO\n    def initialize(@io : IO)\n    end\n\n    delegate :flush, :tty?, :pos, :pos=, :seek, to: @io\n\n    def read(slice : Bytes)\n      raise \"\"\n    end\n\n    def write(slice : Bytes) : Nil\n      cursor = start = slice.to_unsafe\n      fin = cursor + slice.bytesize\n\n      while cursor < fin\n        case byte = cursor.value\n        when '\\\\' then escape = \"\\\\\\\\\"\n        when '\"'  then escape = \"\\\\\\\"\"\n        when '\\b' then escape = \"\\\\b\"\n        when '\\f' then escape = \"\\\\f\"\n        when '\\n' then escape = \"\\\\n\"\n        when '\\r' then escape = \"\\\\r\"\n        when '\\t' then escape = \"\\\\t\"\n        when .<(0x20), 0x7f # Char#ascii_control?\n          @io.write_string Slice.new(start, cursor - start)\n          @io << \"\\\\u00\"\n          @io << '0' if byte < 0x10\n          byte.to_s(@io, 16)\n          cursor += 1\n          start = cursor\n          next\n        else\n          cursor += 1\n          next\n        end\n\n        @io.write_string Slice.new(start, cursor - start)\n        @io << escape\n        cursor += 1\n        start = cursor\n      end\n\n      @io.write_string Slice.new(start, cursor - start)\n    end\n  end\n\n  # Writes a raw value, considered a scalar, directly into\n  # the IO without processing. This is the only method that\n  # might lead to invalid JSON being generated, so you must\n  # be sure that *string* contains a valid JSON string.\n  def raw(string : String) : Nil\n    scalar do\n      @io << string\n    end\n  end\n\n  # Writes the start of an array.\n  def start_array : Nil\n    start_scalar\n    increase_indent\n    @state.push ArrayState.new(empty: true)\n    @io << '['\n  end\n\n  # Writes the end of an array.\n  def end_array : Nil\n    case state = @state.last\n    when ArrayState\n      @state.pop\n    else\n      raise JSON::Error.new(\"Can't do end_array: not inside an array\")\n    end\n    write_indent state\n    @io << ']'\n    decrease_indent\n    end_scalar\n  end\n\n  # Writes the start of an array, invokes the block,\n  # and the writes the end of it.\n  def array(&)\n    start_array\n    yield.tap { end_array }\n  end\n\n  # Writes the start of an object.\n  def start_object : Nil\n    start_scalar\n    increase_indent\n    @state.push ObjectState.new(empty: true, name: true)\n    @io << '{'\n  end\n\n  # Writes the end of an object.\n  def end_object : Nil\n    case state = @state.last\n    when ObjectState\n      unless state.name\n        raise JSON::Error.new(\"Missing object value\")\n      end\n      @state.pop\n    else\n      raise JSON::Error.new(\"Can't do end_object: not inside an object\")\n    end\n    write_indent state\n    @io << '}'\n    decrease_indent\n    end_scalar\n  end\n\n  # Writes the start of an object, invokes the block,\n  # and the writes the end of it.\n  def object(&)\n    start_object\n    yield.tap { end_object }\n  end\n\n  # Writes a scalar value.\n  def scalar(value : Nil)\n    null\n  end\n\n  # :ditto:\n  def scalar(value : Bool)\n    bool(value)\n  end\n\n  # :ditto:\n  def scalar(value : Int | Float) : Nil\n    number(value)\n  end\n\n  # :ditto:\n  def scalar(value : String) : Nil\n    string(value)\n  end\n\n  # Writes an object's field and value.\n  # The field's name is first converted to a `String` by invoking\n  # `to_s` on it.\n  def field(name : _, value : _) : Nil\n    string(name)\n    value.to_json(self)\n  end\n\n  # Writes an object's field and then invokes the block.\n  # This is equivalent of invoking `string(value)` and then\n  # invoking the block.\n  def field(name, &)\n    string(name)\n    yield\n  end\n\n  # Flushes the underlying `IO`.\n  def flush : Nil\n    @io.flush\n  end\n\n  # Sets the indent *string*.\n  def indent=(string : String) : String?\n    if string.empty?\n      @indent = nil\n    else\n      @indent = string\n    end\n  end\n\n  # Sets the indent *level* (number of spaces).\n  def indent=(level : Int) : String?\n    if level < 0\n      @indent = nil\n    else\n      @indent = \" \" * level\n    end\n  end\n\n  # Returns `true` if the next thing that must pushed into this\n  # builder is an object key (so a string) or the end of an object.\n  def next_is_object_key? : Bool\n    state = @state.last\n    state.is_a?(ObjectState) && state.name\n  end\n\n  private def scalar(string = false, &)\n    start_scalar(string)\n    yield.tap { end_scalar(string) }\n  end\n\n  private def start_scalar(string = false)\n    object_value = false\n    case state = @state.last\n    when DocumentStartState\n      # okay\n    when StartState\n      raise JSON::Error.new(\"Write before start_document\")\n    when DocumentEndState\n      raise JSON::Error.new(\"Write past end_document and before start_document\")\n    when ArrayState\n      comma unless state.empty\n    when ObjectState\n      if state.name && !string\n        raise JSON::Error.new(\"Expected string for object name\")\n      end\n      comma if state.name && !state.empty\n      object_value = !state.name\n    end\n    write_indent unless object_value\n  end\n\n  private def end_scalar(string = false)\n    case state = @state.last\n    when DocumentStartState\n      @state[-1] = DocumentEndState.new\n    when ArrayState\n      @state[-1] = ArrayState.new(empty: false)\n    when ObjectState\n      colon if state.name\n      @state[-1] = ObjectState.new(empty: false, name: !state.name)\n    else\n      raise \"Bug: unexpected state: #{state.class}\"\n    end\n  end\n\n  private def comma\n    @io << ','\n  end\n\n  private def colon\n    @io << ':'\n    @io << ' ' if @indent\n  end\n\n  private def newline\n    @io << '\\n'\n  end\n\n  private def write_indent\n    indent = @indent\n    return unless indent\n\n    return if @current_indent == 0\n\n    write_indent(indent, @current_indent)\n  end\n\n  private def write_indent(state : State)\n    return if state.empty\n\n    indent = @indent\n    return unless indent\n\n    write_indent(indent, @current_indent - 1)\n  end\n\n  private def write_indent(indent, times)\n    newline\n    times.times do\n      @io << indent\n    end\n  end\n\n  private def increase_indent\n    @current_indent += 1\n    if @current_indent > @max_nesting\n      raise JSON::Error.new(\"Nesting of #{@current_indent} is too deep\")\n    end\n  end\n\n  private def decrease_indent\n    @current_indent -= 1\n  end\nend\n\nmodule JSON\n  # Returns the resulting `String` of writing JSON to the yielded `JSON::Builder`.\n  #\n  # ```\n  # require \"json\"\n  #\n  # string = JSON.build do |json|\n  #   json.object do\n  #     json.field \"name\", \"foo\"\n  #     json.field \"values\" do\n  #       json.array do\n  #         json.number 1\n  #         json.number 2\n  #         json.number 3\n  #       end\n  #     end\n  #   end\n  # end\n  # string # => %<{\"name\":\"foo\",\"values\":[1,2,3]}>\n  # ```\n  #\n  # Accepts an indent parameter which can either be an `Int` (number of spaces to indent)\n  # or a `String`, which will prefix each level with the string a corresponding amount of times.\n  def self.build(indent : String | Int | Nil = nil, &)\n    String.build do |str|\n      build(str, indent) do |json|\n        yield json\n      end\n    end\n  end\n\n  # Writes JSON into the given `IO`. A `JSON::Builder` is yielded to the block.\n  def self.build(io : IO, indent : String | Int | Nil = nil, &) : Nil\n    builder = JSON::Builder.new(io)\n    builder.indent = indent if indent\n    builder.document do\n      yield builder\n    end\n  end\nend\n"
  },
  {
    "path": "src/json/from_json.cr",
    "content": "# Deserializes the given JSON in *string_or_io* into\n# an instance of `self`. This simply creates a `parser = JSON::PullParser`\n# and invokes `new(parser)`: classes that want to provide JSON\n# deserialization must provide an `def initialize(parser : JSON::PullParser)`\n# method.\n#\n# ```\n# Int32.from_json(\"1\")                # => 1\n# Array(Int32).from_json(\"[1, 2, 3]\") # => [1, 2, 3]\n# ```\ndef Object.from_json(string_or_io : String | IO)\n  parser = JSON::PullParser.new(string_or_io)\n  new parser\nend\n\n# Deserializes the given JSON in *string_or_io* into\n# an instance of `self`, assuming the JSON consists\n# of an JSON object with key *root*, and whose value is\n# the value to deserialize.\n#\n# ```\n# Int32.from_json(%({\"main\": 1}), root: \"main\") # => 1\n# ```\ndef Object.from_json(string_or_io : String | IO, root : String)\n  parser = JSON::PullParser.new(string_or_io)\n  parser.on_key!(root) do\n    new parser\n  end\nend\n\n# Parses a `String` or `IO` denoting a JSON array, yielding\n# each of its elements to the given block. This is useful\n# for decoding an array and processing its elements without\n# creating an Array in memory, which might be expensive.\n#\n# ```\n# require \"json\"\n#\n# Array(Int32).from_json(\"[1, 2, 3]\") do |element|\n#   puts element\n# end\n# ```\n#\n# Output:\n#\n# ```text\n# 1\n# 2\n# 3\n# ```\n#\n# To parse and get an `Array`, use the block-less overload.\ndef Array.from_json(string_or_io, &) : Nil\n  parser = JSON::PullParser.new(string_or_io)\n  new(parser) do |element|\n    yield element\n  end\n  nil\nend\n\ndef Deque.from_json(string_or_io, &) : Nil\n  parser = JSON::PullParser.new(string_or_io)\n  new(parser) do |element|\n    yield element\n  end\nend\n\nmodule Iterator(T)\n  # Reads the content of a JSON array into an iterator in a lazy way.\n  # With this method it should be possible to process a huge JSON array, without\n  # the requirement that the whole array fits into memory.\n  #\n  # The following example produces a huge file, uses a lot of CPU but should not require much memory.\n  #\n  # ```\n  # File.open(\"/tmp/test.json\", \"w+\") do |f|\n  #   (0..1_000_000_000).each.to_json(f)\n  # end\n  #\n  # File.open(\"/tmp/test.json\", \"r\") do |f|\n  #   p Iterator(Int32).from_json(f).skip(1_000_000_000).to_a\n  # end\n  # ```\n  #\n  # WARNING: The `string_or_io` can't be used by anything else until the iterator is fully consumed.\n  def self.from_json(string_or_io)\n    Iterator(T).new(JSON::PullParser.new(string_or_io))\n  end\n\n  # Creates a new iterator which iterates over a JSON array. See also `Iterator#from_json`.\n  #\n  # WARNING: The `JSON::PullParser` can't be used by anything else until the iterator is fully consumed.\n  def self.new(pull : JSON::PullParser)\n    FromJson(T).new(pull)\n  end\n\n  private class FromJson(T)\n    include Iterator(T)\n\n    def initialize(@pull : JSON::PullParser)\n      @pull.read_begin_array\n      @end = false\n    end\n\n    def next\n      if @end\n        stop\n      elsif @pull.kind.end_array?\n        @pull.read_next\n        @end = true\n        stop\n      else\n        T.new(@pull)\n      end\n    end\n  end\nend\n\ndef Nil.new(pull : JSON::PullParser) : self\n  pull.read_null\nend\n\ndef Bool.new(pull : JSON::PullParser) : self\n  pull.read_bool\nend\n\n{% for type, method in {\n                         \"Int8\"    => \"i8\",\n                         \"Int16\"   => \"i16\",\n                         \"Int32\"   => \"i32\",\n                         \"Int64\"   => \"i64\",\n                         \"Int128\"  => \"i128\",\n                         \"UInt8\"   => \"u8\",\n                         \"UInt16\"  => \"u16\",\n                         \"UInt32\"  => \"u32\",\n                         \"UInt64\"  => \"u64\",\n                         \"UInt128\" => \"u128\",\n                       } %}\n  def {{type.id}}.new(pull : JSON::PullParser)\n    # TODO: use `PullParser#read?` instead\n    location = pull.location_i64\n    value =\n      {% if type == \"UInt64\" || type == \"UInt128\" || type == \"Int128\" %}\n        pull.read_raw\n      {% else %}\n        pull.read_int\n      {% end %}\n    begin\n      value.to_{{method.id}}\n    rescue ex : OverflowError | ArgumentError\n      raise JSON::ParseException.new(\"Can't read {{type.id}}\", *location, ex)\n    end\n  end\n\n  def {{type.id}}.from_json_object_key?(key : String)\n    key.to_{{method.id}}?\n  end\n{% end %}\n\ndef Float32.new(pull : JSON::PullParser) : self\n  case pull.kind\n  when .int?\n    value = pull.int_value.to_f32\n    pull.read_next\n    value\n  else\n    pull.read_float.to_f32\n  end\nend\n\ndef Float32.from_json_object_key?(key : String) : Float32?\n  key.to_f32?\nend\n\ndef Float64.new(pull : JSON::PullParser) : self\n  case pull.kind\n  when .int?\n    value = pull.int_value.to_f\n    pull.read_next\n    value\n  else\n    pull.read_float.to_f\n  end\nend\n\ndef Float64.from_json_object_key?(key : String) : Float64?\n  key.to_f64?\nend\n\ndef String.new(pull : JSON::PullParser) : self\n  pull.read_string\nend\n\ndef Path.new(pull : JSON::PullParser) : self\n  new(pull.read_string)\nend\n\ndef String.from_json_object_key?(key : String) : String\n  key\nend\n\ndef Path.from_json_object_key?(key : String) : Path\n  new(key)\nend\n\ndef Array.new(pull : JSON::PullParser)\n  ary = new\n  new(pull) do |element|\n    ary << element\n  end\n  ary\nend\n\ndef Array.new(pull : JSON::PullParser, &)\n  pull.read_array do\n    yield T.new(pull)\n  end\nend\n\ndef Deque.new(pull : JSON::PullParser)\n  ary = new\n  new(pull) do |element|\n    ary << element\n  end\n  ary\nend\n\ndef Deque.new(pull : JSON::PullParser, &)\n  pull.read_array do\n    yield T.new(pull)\n  end\nend\n\ndef Set.new(pull : JSON::PullParser)\n  set = new\n  pull.read_array do\n    set << T.new(pull)\n  end\n  set\nend\n\n# Reads a Hash from the given pull parser.\n#\n# Keys are read by invoking `from_json_object_key?` on this hash's\n# key type (`K`), which must return a value of type `K` or `nil`.\n# If `nil` is returned a `JSON::ParseException` is raised.\n#\n# Values are parsed using the regular `new(pull : JSON::PullParser)` method.\ndef Hash.new(pull : JSON::PullParser)\n  hash = new\n  pull.read_object do |key, key_location|\n    parsed_key = K.from_json_object_key?(key)\n    unless parsed_key\n      raise JSON::ParseException.new(\"Can't convert #{key.inspect} into #{K}\", *key_location)\n    end\n    hash[parsed_key] = V.new(pull)\n  end\n  hash\nend\n\ndef Tuple.new(pull : JSON::PullParser)\n  {% begin %}\n    pull.read_begin_array\n    value = Tuple.new(\n      {% for i in 0...T.size %}\n        (self[{{i}}].new(pull)),\n      {% end %}\n    )\n    pull.read_end_array\n    value\n {% end %}\nend\n\ndef NamedTuple.new(pull : JSON::PullParser)\n  {% begin %}\n    {% for key, type in T %}\n      {% if type.nilable? %}\n        %var{key.id} = nil\n      {% else %}\n        %var{key.id} = uninitialized typeof(element_type({{ key.symbolize }}))\n        %found{key.id} = false\n      {% end %}\n    {% end %}\n\n    location = pull.location_i64\n\n    pull.read_object do |key|\n      case key\n        {% for key, type in T %}\n          when {{key.stringify}}\n            %var{key.id} = self[{{ key.symbolize }}].new(pull)\n            {% unless type.nilable? %}\n              %found{key.id} = true\n            {% end %}\n        {% end %}\n      else\n        pull.skip\n      end\n    end\n\n    {% for key, type in T %}\n      {% unless type.nilable? %}\n        unless %found{key.id}\n          raise JSON::ParseException.new(\"Missing json attribute: #{ {{ key.id.stringify }} }\", *location)\n        end\n      {% end %}\n    {% end %}\n\n    NamedTuple.new(\n      {% for key in T.keys %}\n        {{ key.id.stringify }}: %var{key.id},\n      {% end %}\n    )\n  {% end %}\nend\n\n# Reads a serialized enum member by name from *pull*.\n#\n# See `#to_json` for reference.\n#\n# Raises `JSON::ParseException` if the deserialization fails.\ndef Enum.new(pull : JSON::PullParser) : self\n  {% if @type.annotation(Flags) %}\n    value = {{ @type }}::None\n    pull.read_array do\n      string = pull.read_string\n      value |= parse?(string) || pull.raise \"Unknown enum #{self} value: #{string.inspect}\"\n    end\n    value\n  {% else %}\n    string = pull.read_string\n    parse?(string) || pull.raise \"Unknown enum #{self} value: #{string.inspect}\"\n  {% end %}\nend\n\n# Converter for value-based serialization and deserialization of enum type `T`.\n#\n# The serialization format of `Enum#to_json` and `Enum.from_json` is based on\n# the member name. This converter offers an alternative based on the member value.\n#\n# This converter can be used for its standalone serialization methods as a\n# replacement of the default strategy of `Enum`. It also works as a serialization\n# converter with `JSON::Field` and `YAML::Field`\n#\n# ```\n# require \"json\"\n# require \"yaml\"\n#\n# enum MyEnum\n#   ONE = 1\n#   TWO = 2\n# end\n#\n# class Foo\n#   include JSON::Serializable\n#   include YAML::Serializable\n#\n#   @[JSON::Field(converter: Enum::ValueConverter(MyEnum))]\n#   @[YAML::Field(converter: Enum::ValueConverter(MyEnum))]\n#   property foo : MyEnum = MyEnum::ONE\n#\n#   def initialize(@foo)\n#   end\n# end\n#\n# foo = Foo.new(MyEnum::ONE)\n# foo.to_json # => %({\"foo\":1})\n# foo.to_yaml # => %(---\\nfoo: 1\\n)\n# ```\n#\n# NOTE: Automatically assigned enum values are subject to change when the order\n# of members by adding, removing or reordering them. This can affect the integrity\n# of serialized data between two instances of a program based on different code\n# versions. A way to avoid this is to explicitly assign fixed values to enum\n# members.\nmodule Enum::ValueConverter(T)\n  def self.new(pull : JSON::PullParser) : T\n    from_json(pull)\n  end\n\n  # Reads a serialized enum member by value from *pull*.\n  #\n  # See `.to_json` for reference.\n  #\n  # Raises `JSON::ParseException` if the deserialization fails.\n  def self.from_json(pull : JSON::PullParser) : T\n    T.from_value?(pull.read_int) || pull.raise \"Unknown enum #{T} value: #{pull.int_value}\"\n  end\nend\n\ndef Union.new(pull : JSON::PullParser)\n  location = pull.location_i64\n\n  {% begin %}\n    case pull.kind\n    {% if T.includes? Nil %}\n    when .null?\n      return pull.read_null\n    {% end %}\n    {% if T.includes? Bool %}\n    when .bool?\n      return pull.read_bool\n    {% end %}\n    {% if T.includes? String %}\n    when .string?\n      return pull.read_string\n    {% end %}\n    when .int?\n    {% type_order = [Int128, UInt128, Int64, UInt64, Int32, UInt32, Int16, UInt16, Int8, UInt8, Float64, Float32] %}\n    {% for type in type_order.select { |t| T.includes? t } %}\n      value = pull.read?({{type}})\n      return value unless value.nil?\n    {% end %}\n    when .float?\n    {% type_order = [Float64, Float32] %}\n    {% for type in type_order.select { |t| T.includes? t } %}\n      value = pull.read?({{type}})\n      return value unless value.nil?\n    {% end %}\n    else\n      # no priority type\n    end\n  {% end %}\n\n  {% begin %}\n    {% primitive_types = [Nil, Bool, String] + Number::Primitive.union_types %}\n    {% non_primitives = T.reject { |t| primitive_types.includes? t } %}\n\n    # If after traversing all the types we are left with just one\n    # non-primitive type, we can parse it directly (no need to use `read_raw`)\n    {% if non_primitives.size == 1 %}\n      return {{non_primitives[0]}}.new(pull)\n    {% else %}\n      string = pull.read_raw\n      {% for type in non_primitives %}\n        begin\n          return {{type}}.from_json(string)\n        rescue JSON::ParseException\n          # Ignore\n        end\n      {% end %}\n      raise JSON::ParseException.new(\"Couldn't parse #{self} from #{string}\", *location)\n    {% end %}\n  {% end %}\nend\n\ndef Union.from_json_object_key?(key : String)\n  {% begin %}\n    # String must come last because any key can be parsed into a String.\n    # So, we give a chance first to other types in the union to be parsed.\n    {% string_type = T.find { |type| type == ::String } %}\n\n    {% for type in T %}\n      {% unless type == string_type %}\n        if result = {{ type }}.from_json_object_key?(key)\n          return result\n        end\n      {% end %}\n    {% end %}\n\n    {% if string_type %}\n      if result = {{ string_type }}.from_json_object_key?(key)\n        return result\n      end\n    {% end %}\n  {% end %}\nend\n\n# Reads a string from JSON parser as a time formatted according to [RFC 3339](https://tools.ietf.org/html/rfc3339)\n# or other variations of [ISO 8601](https://web.archive.org/web/20250306154328/http://xml.coverpages.org/ISO-FDIS-8601.pdf).\n#\n# The JSON format itself does not specify a time data type, this method just\n# assumes that a string holding a ISO 8601 time format can be interpreted as a\n# time value.\n#\n# See `#to_json` for reference.\ndef Time.new(pull : JSON::PullParser) : self\n  Time::Format::ISO_8601_DATE_TIME.parse(pull.read_string)\nend\n\ndef Time::Location.new(pull : JSON::PullParser)\n  load(pull.read_string)\nend\n\ndef Time::Location.from_json_object_key?(key : String) : Time::Location\n  load(key)\nend\n\nstruct Time::Format\n  def from_json(pull : JSON::PullParser) : Time\n    string = pull.read_string\n    parse(string, Time::Location::UTC)\n  end\nend\n\nmodule JSON::ArrayConverter(Converter)\n  private struct WithInstance(T)\n    def from_json(pull : JSON::PullParser)\n      ary = Array(typeof(@converter.from_json(pull))).new\n      pull.read_array do\n        ary << @converter.from_json(pull)\n      end\n      ary\n    end\n  end\n\n  def self.from_json(pull : JSON::PullParser)\n    WithInstance.new(Converter).from_json(pull)\n  end\nend\n\nmodule JSON::HashValueConverter(Converter)\n  private struct WithInstance(T)\n    def from_json(pull : JSON::PullParser)\n      hash = Hash(String, typeof(@converter.from_json(pull))).new\n      pull.read_object do |key, key_location|\n        parsed_key = String.from_json_object_key?(key)\n        unless parsed_key\n          raise JSON::ParseException.new(\"Can't convert #{key.inspect} into String\", *key_location)\n        end\n        hash[parsed_key] = @converter.from_json(pull)\n      end\n      hash\n    end\n  end\n\n  def self.from_json(pull : JSON::PullParser)\n    WithInstance.new(Converter).from_json(pull)\n  end\nend\n\nmodule Time::EpochConverter\n  def self.from_json(value : JSON::PullParser) : Time\n    Time.unix(value.read_int)\n  end\nend\n\nmodule Time::EpochMillisConverter\n  def self.from_json(value : JSON::PullParser) : Time\n    Time.unix_ms(value.read_int)\n  end\nend\n\nmodule String::RawConverter\n  def self.from_json(value : JSON::PullParser) : String\n    value.read_raw\n  end\nend\n"
  },
  {
    "path": "src/json/lexer/io_based.cr",
    "content": "# :nodoc:\nclass JSON::Lexer::IOBased < JSON::Lexer\n  def initialize(@io : IO)\n    super()\n    @current_char = @io.read_char || '\\0'\n  end\n\n  private getter current_char\n\n  private def next_char_no_column_increment\n    @current_char = @io.read_char || '\\0'\n  end\n\n  private def consume_string\n    consume_string_with_buffer\n  end\n\n  private def number_start\n    @buffer.clear\n  end\n\n  private def append_number_char\n    @buffer << current_char\n  end\n\n  private def number_string\n    @buffer.to_s\n  end\nend\n"
  },
  {
    "path": "src/json/lexer/string_based.cr",
    "content": "# :nodoc:\nclass JSON::Lexer::StringBased < JSON::Lexer\n  def initialize(string)\n    super()\n    @reader = Char::Reader.new(string)\n    @number_start = 0\n  end\n\n  # Consumes a string by remembering the start position of it and then\n  # doing a substring of the original string.\n  # If we find an escape sequence (\\) we can't do that anymore so we\n  # go through a slow path where we accumulate everything in a buffer\n  # to build the resulting string.\n  private def consume_string\n    start_pos = current_pos\n\n    while true\n      case next_char\n      when '\\0'\n        raise \"Unterminated string\"\n      when '\\\\'\n        return consume_string_slow_path start_pos\n      when '\"'\n        next_char\n        break\n      else\n        if 0 <= current_char.ord < 32\n          unexpected_char\n        end\n      end\n    end\n\n    if @expects_object_key\n      start_pos += 1\n      end_pos = current_pos - 1\n      @token.string_value = @string_pool.get(@reader.string.to_unsafe + start_pos, end_pos - start_pos)\n    else\n      @token.string_value = string_range(start_pos + 1, current_pos - 1)\n    end\n  end\n\n  private def consume_string_slow_path(start_pos)\n    consume_string_with_buffer do\n      @buffer.write slice_range(start_pos + 1, current_pos)\n      @buffer << consume_string_escape_sequence\n    end\n  end\n\n  private def current_pos\n    @reader.pos\n  end\n\n  def string_range(start_pos : Int, end_pos : Int) : String\n    @reader.string.byte_slice(start_pos, end_pos - start_pos)\n  end\n\n  def slice_range(start_pos : Int, end_pos : Int) : Bytes\n    @reader.string.to_slice[start_pos, end_pos - start_pos]\n  end\n\n  private def next_char_no_column_increment\n    char = @reader.next_char\n    if char == '\\0' && @reader.pos != @reader.string.bytesize\n      unexpected_char\n    end\n    char\n  end\n\n  private def current_char\n    @reader.current_char\n  end\n\n  private def number_start\n    @number_start = current_pos\n  end\n\n  private def append_number_char\n    # Nothing\n  end\n\n  private def number_string\n    string_range(@number_start, current_pos)\n  end\nend\n"
  },
  {
    "path": "src/json/lexer.cr",
    "content": "require \"string_pool\"\n\nabstract class JSON::Lexer\n  def self.new(string : String) : self\n    StringBased.new(string)\n  end\n\n  def self.new(io : IO) : self\n    IOBased.new(io)\n  end\n\n  getter token : Token\n  property skip : Bool\n\n  @line_number : Int64\n  @column_number : Int64\n\n  def initialize\n    @token = Token.new\n    @line_number = 1\n    @column_number = 1\n    @buffer = IO::Memory.new\n    @string_pool = StringPool.new\n    @skip = false\n    @expects_object_key = false\n  end\n\n  private abstract def consume_string\n  private abstract def next_char_no_column_increment\n  private abstract def current_char\n  private abstract def number_start\n  private abstract def append_number_char\n  private abstract def number_string\n\n  def next_token : JSON::Token\n    skip_whitespace\n\n    @token.line_number = @line_number\n    @token.column_number = @column_number\n\n    case current_char\n    when '\\0'\n      @token.kind = :EOF\n    when '{'\n      next_char :begin_object\n    when '}'\n      next_char :end_object\n    when '['\n      next_char :begin_array\n    when ']'\n      next_char :end_array\n    when ','\n      next_char :comma\n    when ':'\n      next_char :colon\n    when 'f'\n      consume_false\n    when 'n'\n      consume_null\n    when 't'\n      consume_true\n    when '\"'\n      @token.kind = :string\n      @skip ? consume_string_skip : consume_string\n    else\n      consume_number\n    end\n\n    @token\n  end\n\n  # Requests the next token where the parser expects a json\n  # object key. In this case the lexer tries to reuse the String\n  # instances by using a StringPool.\n  def next_token_expect_object_key\n    @expects_object_key = true\n    next_token\n    @expects_object_key = false\n    @token\n  end\n\n  private def skip_whitespace\n    while whitespace?(current_char)\n      if current_char == '\\n'\n        @line_number += 1\n        @column_number = 0\n      end\n      next_char\n    end\n  end\n\n  private def whitespace?(char)\n    case char\n    when ' ', '\\t', '\\n', '\\r'\n      true\n    else\n      false\n    end\n  end\n\n  private def consume_true\n    if next_char == 'r' && next_char == 'u' && next_char == 'e'\n      next_char\n      @token.kind = :true\n    else\n      unexpected_char\n    end\n  end\n\n  private def consume_false\n    if next_char == 'a' && next_char == 'l' && next_char == 's' && next_char == 'e'\n      next_char\n      @token.kind = :false\n    else\n      unexpected_char\n    end\n  end\n\n  private def consume_null\n    if next_char == 'u' && next_char == 'l' && next_char == 'l'\n      next_char\n      @token.kind = :null\n    else\n      unexpected_char\n    end\n  end\n\n  # Since we are skipping we don't care about a\n  # string's contents, so we just move forward.\n  private def consume_string_skip\n    while true\n      case next_char\n      when '\\0'\n        raise \"Unterminated string\"\n      when '\\\\'\n        consume_string_escape_sequence\n      when '\"'\n        next_char\n        break\n      else\n        if 0 <= current_char.ord < 32\n          unexpected_char\n        end\n      end\n    end\n  end\n\n  private def consume_string_with_buffer\n    consume_string_with_buffer { }\n  end\n\n  private def consume_string_with_buffer(&)\n    @buffer.clear\n    yield\n    while true\n      case char = next_char\n      when '\\0'\n        raise \"Unterminated string\"\n      when '\\\\'\n        @buffer << consume_string_escape_sequence\n      when '\"'\n        next_char\n        break\n      else\n        if 0 <= current_char.ord < 32\n          unexpected_char\n        else\n          @buffer << char\n        end\n      end\n    end\n    if @expects_object_key\n      @token.string_value = @string_pool.get(@buffer)\n    else\n      @token.string_value = @buffer.to_s\n    end\n  end\n\n  private def consume_string_escape_sequence\n    case char = next_char\n    when '\\\\', '\"', '/'\n      char\n    when 'b'\n      '\\b'\n    when 'f'\n      '\\f'\n    when 'n'\n      '\\n'\n    when 'r'\n      '\\r'\n    when 't'\n      '\\t'\n    when 'u'\n      hexnum1 = read_hex_number\n      if hexnum1 < 0xd800 || hexnum1 >= 0xe000\n        hexnum1.unsafe_chr\n      elsif hexnum1 < 0xdc00\n        if next_char != '\\\\' || next_char != 'u'\n          raise \"Unterminated UTF-16 sequence\"\n        end\n        hexnum2 = read_hex_number\n        unless 0xdc00 <= hexnum2 <= 0xdfff\n          raise \"Invalid UTF-16 sequence\"\n        end\n        ((hexnum1 << 10) &+ hexnum2 &- 0x35fdc00).unsafe_chr\n      else\n        raise \"Invalid UTF-16 sequence\"\n      end\n    else\n      raise \"Unknown escape char: #{char}\"\n    end\n  end\n\n  private def read_hex_number\n    hexnum = 0\n    4.times do\n      char = next_char\n      hexnum = (hexnum << 4) | (char.to_i?(16) || raise \"Unexpected char in hex number: #{char.inspect}\")\n    end\n    hexnum\n  end\n\n  private def consume_number\n    number_start\n\n    if current_char == '-'\n      append_number_char\n      next_char\n    end\n\n    case current_char\n    when '0'\n      append_number_char\n      char = next_char\n      case char\n      when '.'\n        consume_float\n      when 'e', 'E'\n        consume_exponent\n      when '0'..'9'\n        unexpected_char\n      else\n        @token.kind = :int\n        number_end\n      end\n    when '1'..'9'\n      append_number_char\n      char = next_char\n      while '0' <= char <= '9'\n        append_number_char\n        char = next_char\n      end\n\n      case char\n      when '.'\n        consume_float\n      when 'e', 'E'\n        consume_exponent\n      else\n        @token.kind = :int\n        number_end\n      end\n    else\n      unexpected_char\n    end\n  end\n\n  private def consume_float\n    append_number_char\n    char = next_char\n\n    unless '0' <= char <= '9'\n      unexpected_char\n    end\n\n    while '0' <= char <= '9'\n      append_number_char\n      char = next_char\n    end\n\n    if char.in?('e', 'E')\n      consume_exponent\n    else\n      @token.kind = :float\n      number_end\n    end\n  end\n\n  private def consume_exponent\n    append_number_char\n\n    char = next_char\n    if char == '+'\n      append_number_char\n      char = next_char\n    elsif char == '-'\n      append_number_char\n      char = next_char\n    end\n\n    if '0' <= char <= '9'\n      while '0' <= char <= '9'\n        append_number_char\n        char = next_char\n      end\n    else\n      unexpected_char\n    end\n\n    @token.kind = :float\n\n    number_end\n  end\n\n  private def next_char\n    @column_number += 1\n    next_char_no_column_increment\n  end\n\n  private def next_char(kind : Token::Kind)\n    @token.kind = kind\n    next_char\n  end\n\n  private def number_end\n    @token.raw_value = number_string\n  end\n\n  private def unexpected_char(char = current_char)\n    raise \"Unexpected char '#{char}'\"\n  end\n\n  private def raise(msg)\n    ::raise ParseException.new(msg, @line_number, @column_number)\n  end\nend\n\nrequire \"./lexer/*\"\n"
  },
  {
    "path": "src/json/parser.cr",
    "content": "require \"./lexer\"\n\nclass JSON::Parser\n  property max_nesting = 512\n\n  def initialize(string_or_io : String | IO)\n    @lexer = JSON::Lexer.new(string_or_io)\n    @nest = 0\n    next_token\n  end\n\n  def parse : Any\n    json = parse_value\n    check :EOF\n    json\n  end\n\n  private def parse_value\n    case token.kind\n    when .int?\n      value_and_next_token token.int_value\n    when .float?\n      value_and_next_token token.float_value\n    when .string?\n      value_and_next_token token.string_value\n    when .null?\n      value_and_next_token nil\n    when .true?\n      value_and_next_token true\n    when .false?\n      value_and_next_token false\n    when .begin_array?\n      parse_array\n    when .begin_object?\n      parse_object\n    else\n      unexpected_token\n    end\n  end\n\n  private def parse_array\n    next_token\n\n    ary = [] of Any\n\n    nest do\n      unless token.kind.end_array?\n        while true\n          ary << parse_value\n\n          case token.kind\n          when .comma?\n            next_token\n            unexpected_token if token.kind.end_array?\n          when .end_array?\n            break\n          else\n            unexpected_token\n          end\n        end\n      end\n    end\n\n    next_token\n\n    Any.new(ary)\n  end\n\n  private def parse_object\n    next_token_expect_object_key\n\n    object = {} of String => Any\n\n    nest do\n      unless token.kind.end_object?\n        while true\n          check :string\n          key = token.string_value\n\n          next_token\n\n          check :colon\n          next_token\n\n          value = parse_value\n\n          object[key] = value\n\n          case token.kind\n          when .comma?\n            next_token_expect_object_key\n            unexpected_token if token.kind.end_object?\n          when .end_object?\n            break\n          else\n            unexpected_token\n          end\n        end\n      end\n    end\n\n    next_token\n\n    Any.new(object)\n  end\n\n  private delegate token, to: @lexer\n  private delegate next_token, to: @lexer\n  private delegate next_token_expect_object_key, to: @lexer\n\n  private def value_and_next_token(value)\n    next_token\n    Any.new(value)\n  end\n\n  private def check(kind : Token::Kind)\n    unexpected_token unless token.kind == kind\n  end\n\n  private def unexpected_token\n    parse_exception \"unexpected token '#{token}'\"\n  end\n\n  private def parse_exception(msg)\n    raise ParseException.new(msg, token.line_number, token.column_number)\n  end\n\n  private def nest(&)\n    @nest += 1\n    if @nest > @max_nesting\n      parse_exception \"Nesting of #{@nest} is too deep\"\n    end\n\n    yield\n    @nest -= 1\n  end\nend\n"
  },
  {
    "path": "src/json/pull_parser.cr",
    "content": "# This class allows you to consume JSON on demand, token by token.\n#\n# Each *read_** method consumes the next token.\n# Sometimes it consumes only one token (like `read_begin_array`), sometimes it consumes a full valid value (like `read_array`).\n#\n# You must be careful when calling those methods, as they move forward into the JSON input you are pulling.\n# Calling `read_string` twice will return the next two strings (if possible), not twice the same.\n#\n# If you try to read a token which is not the one currently under the cursor location, an exception `ParseException` will be raised.\n#\n# Example:\n# ```\n# input = %(\n#   {\n#     \"type\": \"event\",\n#     \"values\": [1, 4, \"three\", 10]\n#   }\n# )\n# pull = JSON::PullParser.new(input)\n# pull.read_begin_object\n# pull.read_object_key # => \"type\"\n# pull.read_string     # => \"event\"\n# # Actually you can also use `read_string` to read a key\n# pull.read_string # => \"values\"\n# pull.read_begin_array\n# pull.read_int    # => 1\n# pull.read_int    # => 4\n# pull.read_string # => \"three\"\n# pull.read_int    # => 10\n# pull.read_end_array\n# pull.read_end_object\n# ```\n#\n# Another example reading the same object:\n# ```\n# pull = JSON::PullParser.new(input)\n# pull.read_object do |key|\n#   case key\n#   when \"type\"\n#     pull.read_string # => \"event\"\n#   when \"values\"\n#     pull.read_array do\n#       if v = pull.read?(Int8)\n#         v\n#       else\n#         pull.read_string\n#       end\n#     end\n#   end\n# end\n# ```\n#\n# This example fails:\n# ```\n# pull = JSON::PullParser.new(input)\n# pull.read_begin_object\n# pull.read_object_key # => \"type\"\n# pull.read_string     # => \"event\"\n# pull.read_end_object # => raise an exception. The current token is a string (\"values\"), not the end of an object.\n# ```\nclass JSON::PullParser\n  enum Kind\n    Null\n    Bool\n    Int\n    Float\n    String\n    BeginArray\n    EndArray\n    BeginObject\n    EndObject\n    EOF\n  end\n\n  private enum ObjectStackKind\n    Object\n    Array\n  end\n\n  getter kind : Kind\n  getter bool_value : Bool\n\n  def int_value : Int64\n    token.int_value\n  end\n\n  def float_value : Float64\n    token.float_value\n  end\n\n  getter string_value : String\n  getter raw_value : String\n\n  property max_nesting = 512\n\n  # Creates a PullParser which will consume the JSON *input*.\n  #\n  # *input* must be a `String` or an `IO`.\n  def initialize(input)\n    @lexer = Lexer.new input\n    @kind = :EOF\n    @bool_value = false\n    @string_value = \"\"\n    @raw_value = \"\"\n    @object_stack = [] of ObjectStackKind\n    @skip_count = 0\n    @location = {0_i64, 0_i64}\n\n    next_token\n    case token.kind\n    in .null?\n      @kind = :null\n    in .false?\n      @kind = :bool\n      @bool_value = false\n    in .true?\n      @kind = :bool\n      @bool_value = true\n    in .int?\n      @kind = :int\n      @raw_value = token.raw_value\n    in .float?\n      @kind = :float\n      @raw_value = token.raw_value\n    in .string?\n      @kind = :string\n      @string_value = token.string_value\n    in .begin_array?\n      begin_array\n    in .begin_object?\n      begin_object\n    in .eof?\n      @kind = :eof\n    in .end_array?, .end_object?, .comma?, .colon?\n      unexpected_token\n    end\n  end\n\n  # Reads the beginning of an array.\n  def read_begin_array : JSON::PullParser::Kind\n    expect_kind :begin_array\n    read_next\n  end\n\n  # Reads the end of an array.\n  def read_end_array : JSON::PullParser::Kind\n    expect_kind :end_array\n    read_next\n  end\n\n  # Reads a whole array.\n  #\n  # It reads the beginning of the array, yield each value of the array, and reads the end of the array.\n  # You have to consumes the values, if any, so the pull parser does not fail when reading the end of the array.\n  #\n  # If the array is empty, it does not yield.\n  def read_array(&)\n    read_begin_array\n    until kind.end_array?\n      yield\n    end\n    read_end_array\n  end\n\n  # Reads the beginning of an object.\n  def read_begin_object : JSON::PullParser::Kind\n    expect_kind :begin_object\n    read_next\n  end\n\n  # Reads the end of an object.\n  def read_end_object : JSON::PullParser::Kind\n    expect_kind :end_object\n    read_next\n  end\n\n  # Reads an object's key and returns it.\n  def read_object_key : String\n    read_string\n  end\n\n  # Reads a whole object.\n  #\n  # It reads the beginning of the object, yield each key and key location, and reads the end of the object.\n  # You have to consumes the values, if any, so the pull parser does not fail when reading the end of the object.\n  #\n  # If the object is empty, it does not yield.\n  def read_object(&)\n    read_begin_object\n    until kind.end_object?\n      key_location = location\n      key = read_object_key\n      yield key, key_location\n    end\n    read_end_object\n  end\n\n  # Reads a null value and returns it.\n  def read_null : Nil\n    expect_kind :null\n    read_next\n    nil\n  end\n\n  # Reads a `Bool` value.\n  def read_bool : Bool\n    expect_kind :bool\n    @bool_value.tap { read_next }\n  end\n\n  # Reads an integer value.\n  def read_int : Int64\n    expect_kind :int\n    int_value.tap { read_next }\n  end\n\n  # Reads a float value.\n  #\n  # If the value is actually an integer, it is converted to float.\n  def read_float : Float64\n    case @kind\n    when .int?\n      int_value.to_f.tap { read_next }\n    when .float?\n      float_value.tap { read_next }\n    else\n      raise \"Expecting int or float but was #{@kind}\"\n    end\n  end\n\n  # Read the next value and returns it.\n  #\n  # The value is returned as a json string.\n  # If the value is an array or an object, it returns a string representing the full value.\n  # If the value in unknown, it raises a `ParseException`.\n  #\n  # ```\n  # pull = JSON::PullParser.new %([null, true, 1, \"foo\", [1, \"two\"], {\"foo\": \"bar\"}])\n  # pull.read_begin_array\n  # pull.read_raw # => \"null\"\n  # pull.read_raw # => \"true\"\n  # pull.read_raw # => \"1\"\n  # pull.read_raw # => \"\\\"foo\\\"\"\n  # pull.read_raw # => \"[1,\\\"two\\\"]\"\n  # pull.read_raw # => \"{\\\"foo\\\":\\\"bar\\\"}\"\n  # pull.read_end_array\n  # ```\n  def read_raw : String\n    case @kind\n    when .null?\n      read_next\n      \"null\"\n    when .bool?\n      @bool_value.to_s.tap { read_next }\n    when .int?, .float?\n      @raw_value.tap { read_next }\n    when .string?\n      @string_value.to_json.tap { read_next }\n    when .begin_array?\n      JSON.build { |json| read_raw(json) }\n    when .begin_object?\n      JSON.build { |json| read_raw(json) }\n    else\n      unexpected_token\n    end\n  end\n\n  # Reads the new value and fill the a JSON builder with it.\n  #\n  # Use this method with a `JSON::Builder` to read a JSON while building another one.\n  def read_raw(json : JSON::Builder) : Nil\n    case @kind\n    when .null?\n      read_next\n      json.null\n    when .bool?\n      json.bool(@bool_value)\n      read_next\n    when .int?, .float?\n      json.raw(@raw_value)\n      read_next\n    when .string?\n      json.string(@string_value)\n      read_next\n    when .begin_array?\n      json.array do\n        read_begin_array\n        until kind.end_array?\n          read_raw(json)\n        end\n        read_end_array\n      end\n    when .begin_object?\n      json.object do\n        read_begin_object\n        until kind.end_object?\n          json.string(@string_value)\n          read_object_key\n          read_raw(json)\n        end\n        read_end_object\n      end\n    else\n      unexpected_token\n    end\n  end\n\n  # Reads a string and returns it.\n  def read_string : String\n    expect_kind :string\n    @string_value.tap { read_next }\n  end\n\n  # Reads a `Bool` or a null value, and returns it.\n  def read_bool_or_null : Bool?\n    read_null_or { read_bool }\n  end\n\n  # Reads an integer or a null value, and returns it.\n  def read_int_or_null : Int64?\n    read_null_or { read_int }\n  end\n\n  # Reads a float or a null value, and returns it.\n  def read_float_or_null : Float64?\n    read_null_or { read_float }\n  end\n\n  # Reads a string or a null value, and returns it.\n  def read_string_or_null : String?\n    read_null_or { read_string }\n  end\n\n  # Reads an array or a null value, and returns it.\n  def read_array_or_null(&)\n    read_null_or { read_array { yield } }\n  end\n\n  # Reads an object or a null value, and returns it.\n  def read_object_or_null(&)\n    read_null_or { read_object { |key| yield key } }\n  end\n\n  # Reads a null value and returns it, or executes the given block if the value is not null.\n  def read_null_or(&)\n    unless read_null?\n      yield\n    end\n  end\n\n  # Reads the current token if its value is null.\n  #\n  # Returns `true` if the token was read.\n  def read_null? : Bool\n    if @kind.null?\n      read_next\n      true\n    else\n      false\n    end\n  end\n\n  # Reads an object keys and yield when *key* is found.\n  #\n  # All the other object keys are skipped.\n  #\n  # Returns the return value of the block or `Nil` if the key was not read.\n  def on_key(key, & : self -> _)\n    result = nil\n    read_object do |some_key|\n      if some_key == key\n        result = yield self\n      else\n        skip\n      end\n    end\n    result\n  end\n\n  # Reads an object keys and yield when *key* is found. If not found, raise an `Exception`.\n  #\n  # All the other object keys are skipped.\n  #\n  # Returns the return value of the block.\n  def on_key!(key, & : self -> _)\n    found = false\n    value = uninitialized typeof(yield self)\n\n    read_object do |some_key|\n      if some_key == key\n        found = true\n        value = yield self\n      else\n        skip\n      end\n    end\n\n    unless found\n      raise \"JSON key not found: #{key}\"\n    end\n\n    value\n  end\n\n  # Reads the next lexer's token.\n  #\n  # Contrary to `read_raw`, it does not read a full value.\n  # For example if the next token is the beginning of an array, it will stop there, while `read_raw` would have read the whole array.\n  def read_next : Kind\n    read_next_internal\n    @kind\n  end\n\n  # Reads a `Bool` value and returns it.\n  #\n  # If the value is not a `Bool`, returns `nil`.\n  def read?(klass : Bool.class) : Bool?\n    read_bool if kind.bool?\n  end\n\n  {% begin %}\n    # types that don't fit into `Int64` (integer type for `JSON::Any`)'s range\n    {% large_ints = [UInt64, Int128, UInt128] %}\n\n    {% for int in Int::Primitive.union_types %}\n      {% is_large_int = large_ints.includes?(int) %}\n\n      # Reads an `{{int}}` value and returns it.\n      #\n      # If the value is not an integer or does not fit in an `{{int}}`, it\n      # returns `nil`.\n      def read?(klass : {{int}}.class) : {{int}}?\n        if kind.int?\n          {{int}}.new({{ is_large_int ? \"raw_value\".id : \"int_value\".id }}).tap { read_next }\n        end\n      rescue JSON::ParseException | {{ is_large_int ? ArgumentError : OverflowError }}\n        nil\n      end\n    {% end %}\n  {% end %}\n\n  # Reads an `Float32` value and returns it.\n  #\n  # If the value is not an integer or does not fit in an `Float32`, it returns `nil`.\n  # If the value was actually an integer, it is converted to a float.\n  def read?(klass : Float32.class) : Float32?\n    return read_int.to_f32 if kind.int?\n    return raw_value.to_f32.tap { read_next } if kind.float?\n  rescue exc : JSON::ParseException | ArgumentError\n    nil\n  end\n\n  # Reads an `Float64` value and returns it.\n  #\n  # If the value is not an integer or does not fit in a `Float64` variable, it returns `nil`.\n  # If the value was actually an integer, it is converted to a float.\n  def read?(klass : Float64.class) : Float64?\n    return read_int.to_f64 if kind.int?\n    return read_float.to_f64 if kind.float?\n  rescue JSON::ParseException\n    nil\n  end\n\n  # Reads a `String` value and returns it.\n  #\n  # If the value is not a `String`, returns `nil`.\n  def read?(klass : String.class) : String?\n    read_string if kind.string?\n  end\n\n  private def read_next_internal\n    current_kind = @kind\n\n    while true\n      case token.kind\n      when .null?\n        @kind = :null\n        next_token_after_value\n        return\n      when .true?\n        @kind = :bool\n        @bool_value = true\n        next_token_after_value\n        return\n      when .false?\n        @kind = :bool\n        @bool_value = false\n        next_token_after_value\n        return\n      when .int?\n        @kind = :int\n        @raw_value = token.raw_value\n        next_token_after_value\n        return\n      when .float?\n        @kind = :float\n        @raw_value = token.raw_value\n        next_token_after_value\n        return\n      when .string?\n        @kind = :string\n        @string_value = token.string_value\n        if current_kind.begin_object?\n          unless next_token.kind.colon?\n            unexpected_token\n          end\n        else\n          next_token_after_value\n        end\n        return\n      when .begin_array?\n        begin_array\n        return\n      when .end_array?\n        @kind = :end_array\n        next_token_after_array_or_object\n        return\n      when .begin_object?\n        begin_object\n        return\n      when .end_object?\n        @kind = :end_object\n        next_token_after_array_or_object\n        return\n      when .comma?\n        obj = current_object()\n\n        @lexer.skip = false if @skip_count == 1\n\n        if obj.try(&.object?)\n          next_token_expect_object_key\n        else\n          next_token\n        end\n\n        @lexer.skip = true if @skip_count == 1\n\n        case token.kind\n        when .comma?, .end_array?, .end_object?, .eof?\n          unexpected_token\n        else\n          # okay\n        end\n\n        if obj.try(&.object?) && token.kind.string?\n          @kind = :string\n          @string_value = token.string_value\n          unless next_token.kind.colon?\n            unexpected_token\n          end\n          return\n        end\n      when .colon?\n        case next_token.kind\n        when .comma?, .colon?, .end_array?, .end_object?, .eof?\n          unexpected_token\n        else\n          # okay\n        end\n      when .eof?\n        @kind = :EOF\n        return\n      else\n        unexpected_token\n      end\n    end\n  end\n\n  # Skips the next value.\n  #\n  # It skips the whole value, not only the next lexer's token.\n  # For example if the next value is an array, the whole array will be skipped.\n  def skip : Nil\n    @lexer.skip = true\n    skip_internal\n    @lexer.skip = false\n  end\n\n  # Returns the current line number.\n  @[Experimental]\n  def line_number_i64 : Int64\n    @location[0]\n  end\n\n  # Returns the current line number.\n  def line_number : Int32\n    @location[0].to_i32\n  end\n\n  # Returns the current column number.\n  @[Experimental]\n  def column_number_i64\n    @location[1]\n  end\n\n  # Returns the current column number.\n  def column_number\n    @location[1].to_i32\n  end\n\n  # Returns the current location.\n  #\n  # The location is a tuple `{line number, column number}`.\n  def location : Tuple(Int32, Int32)\n    {line_number, column_number}\n  end\n\n  # Returns the current location.\n  #\n  # The location is a tuple `{line number, column number}`.\n  @[Experimental]\n  def location_i64 : Tuple(Int64, Int64)\n    @location\n  end\n\n  private def skip_internal\n    @skip_count += 1\n    case @kind\n    when .null?, .bool?, .int?, .float?, .string?\n      read_next\n    when .begin_array?\n      @skip_count += 1\n      read_begin_array\n      until kind.end_array?\n        skip_internal\n      end\n      @skip_count -= 1\n      read_end_array\n    when .begin_object?\n      @skip_count += 1\n      read_begin_object\n      until kind.end_object?\n        read_object_key\n        skip_internal\n      end\n      @skip_count -= 1\n      read_end_object\n    else\n      unexpected_token\n    end\n    @skip_count -= 1\n  end\n\n  private def begin_array\n    @kind = :begin_array\n    push_in_object_stack :array\n\n    case next_token.kind\n    when .comma?, .end_object?, .colon?, .eof?\n      unexpected_token\n    else\n      # okay\n    end\n  end\n\n  private def begin_object\n    @kind = :begin_object\n    push_in_object_stack :object\n\n    case next_token_expect_object_key.kind\n    when .string?, .end_object?\n      # OK\n    else\n      unexpected_token\n    end\n  end\n\n  private def current_object\n    @object_stack.last?\n  end\n\n  private def token\n    @lexer.token\n  end\n\n  private def next_token\n    @location = {@lexer.token.line_number_i64, @lexer.token.column_number_i64}\n    @lexer.next_token\n  end\n\n  private def next_token_expect_object_key\n    @location = {@lexer.token.line_number_i64, @lexer.token.column_number_i64}\n    @lexer.next_token_expect_object_key\n  end\n\n  private def next_token_after_value\n    case next_token.kind\n    when .comma?, .end_array?, .end_object?\n      # Ok\n    else\n      if @object_stack.empty?\n        @kind = :EOF\n      else\n        unexpected_token\n      end\n    end\n  end\n\n  private def next_token_after_array_or_object\n    unless @object_stack.pop?\n      unexpected_token\n    end\n    case next_token.kind\n    when .comma?, .end_array?, .end_object?\n      # OK\n    when .eof?\n      unless @object_stack.empty?\n        unexpected_token\n      end\n    else\n      unexpected_token\n    end\n  end\n\n  private def expect_kind(kind : Kind)\n    raise \"Expected #{kind} but was #{@kind}\" unless @kind == kind\n  end\n\n  private def unexpected_token\n    raise \"Unexpected token: #{token}\"\n  end\n\n  # Raises `ParseException` with *message* at current location.\n  def raise(message : String) : NoReturn\n    ::raise ParseException.new(message, token.line_number, token.column_number)\n  end\n\n  private def push_in_object_stack(kind : ObjectStackKind)\n    if @object_stack.size >= @max_nesting\n      raise \"Nesting of #{@object_stack.size + 1} is too deep\"\n    end\n\n    @object_stack.push(kind)\n  end\nend\n"
  },
  {
    "path": "src/json/serialization.cr",
    "content": "module JSON\n  annotation Field\n  end\n\n  # The `JSON::Serializable` module automatically generates methods for JSON serialization when included.\n  #\n  # ### Example\n  #\n  # ```\n  # require \"json\"\n  #\n  # class Location\n  #   include JSON::Serializable\n  #\n  #   @[JSON::Field(key: \"lat\")]\n  #   property latitude : Float64\n  #\n  #   @[JSON::Field(key: \"lng\")]\n  #   property longitude : Float64\n  # end\n  #\n  # class House\n  #   include JSON::Serializable\n  #   property address : String\n  #   property location : Location?\n  # end\n  #\n  # house = House.from_json(%({\"address\": \"Crystal Road 1234\", \"location\": {\"lat\": 12.3, \"lng\": 34.5}}))\n  # house.address  # => \"Crystal Road 1234\"\n  # house.location # => #<Location:0x10cd93d80 @latitude=12.3, @longitude=34.5>\n  # house.to_json  # => %({\"address\":\"Crystal Road 1234\",\"location\":{\"lat\":12.3,\"lng\":34.5}})\n  #\n  # houses = Array(House).from_json(%([{\"address\": \"Crystal Road 1234\", \"location\": {\"lat\": 12.3, \"lng\": 34.5}}]))\n  # houses.size    # => 1\n  # houses.to_json # => %([{\"address\":\"Crystal Road 1234\",\"location\":{\"lat\":12.3,\"lng\":34.5}}])\n  # ```\n  #\n  # ### Usage\n  #\n  # Including `JSON::Serializable` will create `#to_json` and `self.from_json` methods on the current class,\n  # and a constructor which takes a `JSON::PullParser`. By default, these methods serialize into a json\n  # object containing the value of every instance variable, the keys being the instance variable name.\n  # Most primitives and collections supported as instance variable values (string, integer, array, hash, etc.),\n  # along with objects which define to_json and a constructor taking a `JSON::PullParser`.\n  # Union types are also supported, including unions with nil. If multiple types in a union parse correctly,\n  # it is undefined which one will be chosen.\n  #\n  # To change how individual instance variables are parsed and serialized, the annotation `JSON::Field`\n  # can be placed on the instance variable. Annotating property, getter and setter macros is also allowed.\n  # ```\n  # require \"json\"\n  #\n  # class A\n  #   include JSON::Serializable\n  #\n  #   @[JSON::Field(key: \"my_key\", emit_null: true)]\n  #   getter a : Int32?\n  # end\n  # ```\n  #\n  # `JSON::Field` properties:\n  # * **ignore**: if `true` skip this field in serialization and deserialization (by default false)\n  # * **ignore_serialize**: If truthy, skip this field in serialization (default: `false`). The value can be any Crystal expression and is evaluated at runtime.\n  # * **ignore_deserialize**: if `true` skip this field in deserialization (by default false)\n  # * **key**: the value of the key in the json object (by default the name of the instance variable)\n  # * **root**: assume the value is inside a JSON object with a given key (see `Object.from_json(string_or_io, root)`)\n  # * **converter**: specify an alternate type for parsing and generation. The converter must define `from_json(JSON::PullParser)` and `to_json(value, JSON::Builder)`. Examples of converters are a `Time::Format` instance and `Time::EpochConverter` for `Time`.\n  # * **presence**: if `true`, a `@{{key}}_present` instance variable will be generated when the key was present (even if it has a `null` value), `false` by default\n  # * **emit_null**: if `true`, emits a `null` value for nilable property (by default nulls are not emitted)\n  #\n  # Deserialization also respects default values of variables:\n  # ```\n  # require \"json\"\n  #\n  # struct A\n  #   include JSON::Serializable\n  #   @a : Int32\n  #   @b : Float64 = 1.0\n  # end\n  #\n  # A.from_json(%<{\"a\":1}>) # => A(@a=1, @b=1.0)\n  # ```\n  #\n  # NOTE: `JSON::Serializable` defines an internal constructor on any including\n  # type, which means the default constructor (`def initialize; end`) is absent\n  # unless explicitly defined by the user, even when all instance variables have\n  # a default initializer.\n  #\n  # ### Extensions: `JSON::Serializable::Strict` and `JSON::Serializable::Unmapped`.\n  #\n  # If the `JSON::Serializable::Strict` module is included, unknown properties in the JSON\n  # document will raise a parse exception. By default the unknown properties\n  # are silently ignored.\n  # If the `JSON::Serializable::Unmapped` module is included, unknown properties in the JSON\n  # document will be stored in a `Hash(String, JSON::Any)`. On serialization, any keys inside json_unmapped\n  # will be serialized and appended to the current json object.\n  # ```\n  # require \"json\"\n  #\n  # struct A\n  #   include JSON::Serializable\n  #   include JSON::Serializable::Unmapped\n  #   @a : Int32\n  # end\n  #\n  # a = A.from_json(%({\"a\":1,\"b\":2})) # => A(@json_unmapped={\"b\" => 2}, @a=1)\n  # a.json_unmapped[\"b\"].raw.class    # => Int64\n  # a.to_json                         # => %({\"a\":1,\"b\":2})\n  # ```\n  #\n  #\n  # ### Class annotation `JSON::Serializable::Options`\n  #\n  # supported properties:\n  # * **emit_nulls**: if `true`, emits a `null` value for all nilable properties (by default nulls are not emitted)\n  #\n  # ```\n  # require \"json\"\n  #\n  # @[JSON::Serializable::Options(emit_nulls: true)]\n  # class A\n  #   include JSON::Serializable\n  #   @a : Int32?\n  # end\n  # ```\n  #\n  # ### Discriminator field\n  #\n  # A very common JSON serialization strategy for handling different objects\n  # under a same hierarchy is to use a discriminator field. For example in\n  # [GeoJSON](https://tools.ietf.org/html/rfc7946) each object has a \"type\"\n  # field, and the rest of the fields, and their meaning, depend on its value.\n  #\n  # You can use `JSON::Serializable.use_json_discriminator` for this use case.\n  #\n  # ### `after_initialize` method\n  #\n  # `#after_initialize` is a method that runs after an instance is deserialized\n  # from JSON. It can be used as a hook to post-process the initialized object.\n  #\n  # Example:\n  # ```\n  # require \"json\"\n  #\n  # class Person\n  #   include JSON::Serializable\n  #   getter name : String\n  #\n  #   def after_initialize\n  #     @name = @name.upcase\n  #   end\n  # end\n  #\n  # person = Person.from_json %({\"name\": \"Jane\"})\n  # person.name # => \"JANE\"\n  # ```\n  module Serializable\n    annotation Options\n    end\n\n    macro included\n      # Define a `new` directly in the included type,\n      # so it overloads well with other possible initializes\n\n      def self.new(pull : ::JSON::PullParser)\n        new_from_json_pull_parser(pull)\n      end\n\n      private def self.new_from_json_pull_parser(pull : ::JSON::PullParser)\n        instance = allocate\n        instance.initialize(__pull_for_json_serializable: pull)\n        ::GC.add_finalizer(instance) if instance.responds_to?(:finalize)\n        instance\n      end\n\n      # When the type is inherited, carry over the `new`\n      # so it can compete with other possible initializes\n\n      macro inherited\n        def self.new(pull : ::JSON::PullParser)\n          new_from_json_pull_parser(pull)\n        end\n      end\n\n      def initialize(*, __pull_for_json_serializable pull : ::JSON::PullParser)\n        {% verbatim do %}\n          {% begin %}\n            {% properties = {} of Nil => Nil %}\n            {% for ivar in @type.instance_vars %}\n              {% ann = ivar.annotation(::JSON::Field) %}\n              {% unless ann && (ann[:ignore] || ann[:ignore_deserialize]) %}\n                {%\n                  properties[ivar.id] = {\n                    key:         ((ann && ann[:key]) || ivar).id.stringify,\n                    has_default: ivar.has_default_value?,\n                    default:     ivar.default_value,\n                    nilable:     ivar.type.nilable?,\n                    root:        ann && ann[:root],\n                    converter:   ann && ann[:converter],\n                    presence:    ann && ann[:presence],\n                  }\n                %}\n              {% end %}\n            {% end %}\n\n            # `%var`'s type must be exact to avoid type inference issues with\n            # recursively defined serializable types\n            {% for name, value in properties %}\n              %var{name} = uninitialized ::Union(typeof(@{{ name }}))\n              %found{name} = false\n            {% end %}\n\n            %location = pull.location_i64\n            begin\n              pull.read_begin_object\n            rescue exc : ::JSON::ParseException\n              raise ::JSON::SerializableError.new(exc.message, self.class.to_s, nil, *%location, exc)\n            end\n            until pull.kind.end_object?\n              %key_location = pull.location_i64\n              key = pull.read_object_key\n              case key\n              {% for name, value in properties %}\n                when {{value[:key]}}\n                  begin\n                    {% if value[:has_default] || value[:nilable] || value[:root] %}\n                      if pull.read_null?\n                        {% if value[:nilable] %}\n                          %var{name} = nil\n                          %found{name} = true\n                        {% end %}\n                        next\n                      end\n                    {% end %}\n\n                    %var{name} =\n                      {% if value[:root] %} pull.on_key!({{value[:root]}}) do {% else %} begin {% end %}\n                        {% if value[:converter] %}\n                          {{value[:converter]}}.from_json(pull)\n                        {% else %}\n                          ::Union(typeof(@{{ name }})).new(pull)\n                        {% end %}\n                      end\n                    %found{name} = true\n                  rescue exc : ::JSON::ParseException\n                    raise ::JSON::SerializableError.new(exc.message, self.class.to_s, {{value[:key]}}, *%key_location, exc)\n                  end\n              {% end %}\n              else\n                on_unknown_json_attribute(pull, key, %key_location)\n              end\n            end\n            pull.read_next\n\n            {% for name, value in properties %}\n              if %found{name}\n                @{{name}} = %var{name}\n              else\n                {% unless value[:has_default] || value[:nilable] %}\n                  raise ::JSON::SerializableError.new(\"Missing JSON attribute: {{value[:key].id}}\", self.class.to_s, {{value[:key]}}, *%location, nil)\n                {% end %}\n              end\n\n              {% if value[:presence] %}\n                @{{name}}_present = %found{name}\n              {% end %}\n            {% end %}\n          {% end %}\n        {% end %}\n        after_initialize\n      end\n    end\n\n    protected def after_initialize\n    end\n\n    protected def on_unknown_json_attribute(pull, key, key_location)\n      pull.skip\n    end\n\n    protected def on_to_json(json : ::JSON::Builder)\n    end\n\n    def to_json(json : ::JSON::Builder)\n      {% begin %}\n        {% options = @type.annotation(::JSON::Serializable::Options) %}\n        {% emit_nulls = options && options[:emit_nulls] %}\n\n        {% properties = {} of Nil => Nil %}\n        {% for ivar in @type.instance_vars %}\n          {% ann = ivar.annotation(::JSON::Field) %}\n          {% unless ann && (ann[:ignore] || ann[:ignore_serialize] == true) %}\n            {%\n              properties[ivar.id] = {\n                key:              ((ann && ann[:key]) || ivar).id.stringify,\n                root:             ann && ann[:root],\n                converter:        ann && ann[:converter],\n                emit_null:        (ann && (ann[:emit_null] != nil) ? ann[:emit_null] : emit_nulls),\n                ignore_serialize: ann && ann[:ignore_serialize],\n              }\n            %}\n          {% end %}\n        {% end %}\n\n        json.object do\n          {% for name, value in properties %}\n            _{{name}} = @{{name}}\n\n            {% if value[:ignore_serialize] %}\n              unless {{ value[:ignore_serialize] }}\n            {% end %}\n\n              {% unless value[:emit_null] %}\n                unless _{{name}}.nil?\n              {% end %}\n\n                json.field({{value[:key]}}) do\n                  {% if value[:root] %}\n                    {% if value[:emit_null] %}\n                      if _{{name}}.nil?\n                        nil.to_json(json)\n                      else\n                    {% end %}\n\n                    json.object do\n                      json.field({{value[:root]}}) do\n                  {% end %}\n\n                  {% if value[:converter] %}\n                    if _{{name}}\n                      {{ value[:converter] }}.to_json(_{{name}}, json)\n                    else\n                      nil.to_json(json)\n                    end\n                  {% else %}\n                    _{{name}}.to_json(json)\n                  {% end %}\n\n                  {% if value[:root] %}\n                    {% if value[:emit_null] %}\n                      end\n                    {% end %}\n                      end\n                    end\n                  {% end %}\n                end\n\n              {% unless value[:emit_null] %}\n                end\n              {% end %}\n            {% if value[:ignore_serialize] %}\n              end\n            {% end %}\n          {% end %}\n          on_to_json(json)\n        end\n      {% end %}\n    end\n\n    module Strict\n      protected def on_unknown_json_attribute(pull, key, key_location)\n        raise ::JSON::SerializableError.new(\"Unknown JSON attribute: #{key}\", self.class.to_s, key, *key_location, nil)\n      end\n    end\n\n    module Unmapped\n      @[::JSON::Field(ignore: true)]\n      property json_unmapped = Hash(String, ::JSON::Any).new\n\n      protected def on_unknown_json_attribute(pull, key, key_location)\n        json_unmapped[key] = begin\n          ::JSON::Any.new(pull)\n        rescue exc : ::JSON::ParseException\n          raise ::JSON::SerializableError.new(exc.message, self.class.to_s, key, *key_location, exc)\n        end\n      end\n\n      protected def on_to_json(json)\n        json_unmapped.each do |key, value|\n          json.field(key) { value.to_json(json) }\n        end\n      end\n    end\n\n    # Tells this class to decode JSON by using a field as a discriminator.\n    #\n    # - *field* must be the field name to use as a discriminator\n    # - *mapping* must be a hash or named tuple where each key-value pair\n    #   maps a discriminator value to a class to deserialize\n    #\n    # For example:\n    #\n    # ```\n    # require \"json\"\n    #\n    # abstract class Shape\n    #   include JSON::Serializable\n    #\n    #   use_json_discriminator \"type\", {point: Point, circle: Circle}\n    #\n    #   property type : String\n    # end\n    #\n    # class Point < Shape\n    #   property x : Int32\n    #   property y : Int32\n    # end\n    #\n    # class Circle < Shape\n    #   property x : Int32\n    #   property y : Int32\n    #   property radius : Int32\n    # end\n    #\n    # Shape.from_json(%({\"type\": \"point\", \"x\": 1, \"y\": 2}))               # => #<Point:0x10373ae20 @type=\"point\", @x=1, @y=2>\n    # Shape.from_json(%({\"type\": \"circle\", \"x\": 1, \"y\": 2, \"radius\": 3})) # => #<Circle:0x106a4cea0 @type=\"circle\", @x=1, @y=2, @radius=3>\n    # ```\n    macro use_json_discriminator(field, mapping)\n      {% unless mapping.is_a?(HashLiteral) || mapping.is_a?(NamedTupleLiteral) %}\n        {% mapping.raise \"Mapping argument must be a HashLiteral or a NamedTupleLiteral, not #{mapping.class_name.id}\" %}\n      {% end %}\n\n      def self.new(pull : ::JSON::PullParser)\n        location = pull.location_i64\n\n        discriminator_value = nil\n\n        # Try to find the discriminator while also getting the raw\n        # string value of the parsed JSON, so then we can pass it\n        # to the final type.\n        json = ::String.build do |io|\n          ::JSON.build(io) do |builder|\n            builder.start_object\n            pull.read_object do |key|\n              if key == {{field.id.stringify}}\n                value_kind = pull.kind\n                case value_kind\n                when .string?\n                  discriminator_value = pull.string_value\n                when .int?\n                  discriminator_value = pull.int_value\n                when .bool?\n                  discriminator_value = pull.bool_value\n                else\n                  raise ::JSON::SerializableError.new(\"JSON discriminator field '{{field.id}}' has an invalid value type of #{value_kind.to_s}\", to_s, nil, *location, nil)\n                end\n                builder.field(key, discriminator_value)\n                pull.read_next\n              else\n                builder.field(key) { pull.read_raw(builder) }\n              end\n            end\n            builder.end_object\n          end\n        end\n\n        if discriminator_value.nil?\n          raise ::JSON::SerializableError.new(\"Missing JSON discriminator field '{{field.id}}'\", to_s, nil, *location, nil)\n        end\n\n        case discriminator_value\n        {% for key, value in mapping %}\n          {% if mapping.is_a?(NamedTupleLiteral) %}\n            when {{key.id.stringify}}\n          {% else %}\n            {% if key.is_a?(StringLiteral) %}\n              when {{key}}\n            {% elsif key.is_a?(NumberLiteral) || key.is_a?(BoolLiteral) %}\n              when {{key.id}}\n            {% elsif key.is_a?(Path) %}\n              when {{key.resolve}}\n            {% else %}\n              {% key.raise \"Mapping keys must be one of StringLiteral, NumberLiteral, BoolLiteral, or Path, not #{key.class_name.id}\" %}\n            {% end %}\n          {% end %}\n          {{value.id}}.from_json(json)\n        {% end %}\n        else\n          raise ::JSON::SerializableError.new(\"Unknown '{{field.id}}' discriminator value: #{discriminator_value.inspect}\", to_s, nil, *location, nil)\n        end\n      end\n    end\n  end\n\n  class SerializableError < ParseException\n    getter klass : String\n    getter attribute : String?\n\n    def initialize(message : String?, @klass : String, @attribute : String?, line_number, column_number, cause)\n      message = String.build do |io|\n        io << message\n        io << \"\\n  parsing \"\n        io << klass\n        if attribute = @attribute\n          io << '#' << attribute\n        end\n      end\n      super(message, line_number, column_number, cause)\n      if cause\n        @line_number, @column_number = cause.location_i64\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/json/to_json.cr",
    "content": "class Object\n  def to_json : String\n    String.build do |str|\n      to_json str\n    end\n  end\n\n  def to_json(io : IO) : Nil\n    JSON.build(io) do |json|\n      to_json(json)\n    end\n  end\n\n  def to_pretty_json(indent : String = \"  \") : String\n    String.build do |str|\n      to_pretty_json str, indent: indent\n    end\n  end\n\n  def to_pretty_json(io : IO, indent : String = \"  \") : Nil\n    JSON.build(io, indent: indent) do |json|\n      to_json(json)\n    end\n  end\nend\n\nstruct Nil\n  def to_json(json : JSON::Builder) : Nil\n    json.null\n  end\n\n  def to_json_object_key : String\n    \"\"\n  end\nend\n\nstruct Bool\n  def to_json(json : JSON::Builder) : Nil\n    json.bool(self)\n  end\nend\n\nstruct Int\n  def to_json(json : JSON::Builder) : Nil\n    json.number(self)\n  end\n\n  def to_json_object_key : String\n    to_s\n  end\nend\n\nstruct Float\n  def to_json(json : JSON::Builder) : Nil\n    json.number(self)\n  end\n\n  def to_json_object_key : String\n    to_s\n  end\nend\n\nclass String\n  def to_json(json : JSON::Builder) : Nil\n    json.string(self)\n  end\n\n  def to_json_object_key : String\n    self\n  end\nend\n\nstruct Path\n  def to_json(json : JSON::Builder) : Nil\n    @name.to_json(json)\n  end\n\n  def to_json_object_key\n    @name\n  end\nend\n\nstruct Symbol\n  def to_json(json : JSON::Builder) : Nil\n    json.string(to_s)\n  end\n\n  def to_json_object_key : String\n    to_s\n  end\nend\n\nclass Array\n  def to_json(json : JSON::Builder) : Nil\n    json.array do\n      each &.to_json(json)\n    end\n  end\nend\n\nstruct StaticArray\n  def to_json(json : JSON::Builder) : Nil\n    json.array do\n      each &.to_json(json)\n    end\n  end\nend\n\nclass Deque\n  def to_json(json : JSON::Builder) : Nil\n    json.array do\n      each &.to_json(json)\n    end\n  end\nend\n\nstruct Set\n  def to_json(json : JSON::Builder) : Nil\n    json.array do\n      each &.to_json(json)\n    end\n  end\nend\n\nmodule Iterator(T)\n  # Converts the content of an iterator into a JSON array in lazy way.\n  # See `Iterator#from_json` for an example.\n  def to_json(json : JSON::Builder)\n    json.array do\n      each &.to_json(json)\n    end\n  end\nend\n\nclass Hash\n  # Serializes this Hash into JSON.\n  #\n  # Keys are serialized by invoking `to_json_object_key` on them.\n  # Values are serialized with the usual `to_json(json : JSON::Builder)`\n  # method.\n  def to_json(json : JSON::Builder) : Nil\n    json.object do\n      each do |key, value|\n        json.field key.to_json_object_key do\n          value.to_json(json)\n        end\n      end\n    end\n  end\nend\n\nstruct Tuple\n  def to_json(json : JSON::Builder) : Nil\n    json.array do\n      {% for i in 0...T.size %}\n        self[{{i}}].to_json(json)\n      {% end %}\n    end\n  end\nend\n\nstruct NamedTuple\n  def to_json(json : JSON::Builder)\n    json.object do\n      {% for key in T.keys %}\n        json.field {{key.stringify}} do\n          self[{{key.symbolize}}].to_json(json)\n        end\n      {% end %}\n    end\n  end\nend\n\nclass Time::Location\n  def to_json(json : JSON::Builder) : Nil\n    json.string(to_s)\n  end\nend\n\nstruct Time::Format\n  def to_json(value : Time, json : JSON::Builder) : Nil\n    json.string do |io|\n      format(value, io)\n    end\n  end\nend\n\nstruct Enum\n  # Serializes this enum member by name.\n  #\n  # For non-flags enums, the serialization is a JSON string. The value is the\n  # member name (see `#to_s`) transformed with `String#underscore`.\n  #\n  # ```\n  # enum Stages\n  #   INITIAL\n  #   SECOND_STAGE\n  # end\n  #\n  # Stages::INITIAL.to_json      # => %(\"initial\")\n  # Stages::SECOND_STAGE.to_json # => %(\"second_stage\")\n  # ```\n  #\n  # For flags enums, the serialization is a JSON array including every flagged\n  # member individually serialized in the same way as a member of a non-flags enum.\n  # `None` is serialized as an empty array, `All` as an array containing\n  # all members.\n  #\n  # ```\n  # @[Flags]\n  # enum Sides\n  #   LEFT\n  #   RIGHT\n  # end\n  #\n  # Sides::LEFT.to_json                  # => %([\"left\"])\n  # (Sides::LEFT | Sides::RIGHT).to_json # => %([\"left\",\"right\"])\n  # Sides::All.to_json                   # => %([\"left\",\"right\"])\n  # Sides::None.to_json                  # => %([])\n  # ```\n  #\n  # `ValueConverter.to_json` offers a different serialization strategy based on the\n  # member value.\n  def to_json(json : JSON::Builder) : Nil\n    {% if @type.annotation(Flags) %}\n      json.array do\n        each do |member, _value|\n          json.string(member.to_s.underscore)\n        end\n      end\n    {% else %}\n      json.string(to_s.underscore)\n    {% end %}\n  end\nend\n\nmodule Enum::ValueConverter(T)\n  def self.to_json(value : T)\n    String.build do |io|\n      to_json(value, io)\n    end\n  end\n\n  def self.to_json(value : T, io : IO)\n    JSON.build(io) do |json|\n      to_json(value, json)\n    end\n  end\n\n  # Serializes enum member *member* by value.\n  #\n  # For both flags enums and non-flags enums, the value of the enum member is\n  # used for serialization.\n  #\n  # ```\n  # enum Stages\n  #   INITIAL\n  #   SECOND_STAGE\n  # end\n  #\n  # Enum::ValueConverter.to_json(Stages::INITIAL)      # => %(0)\n  # Enum::ValueConverter.to_json(Stages::SECOND_STAGE) # => %(1)\n  #\n  # @[Flags]\n  # enum Sides\n  #   LEFT\n  #   RIGHT\n  # end\n  #\n  # Enum::ValueConverter.to_json(Sides::LEFT)                # => %(1)\n  # Enum::ValueConverter.to_json(Sides::LEFT | Sides::RIGHT) # => %(3)\n  # Enum::ValueConverter.to_json(Sides::All)                 # => %(3)\n  # Enum::ValueConverter.to_json(Sides::None)                # => %(0)\n  # ```\n  #\n  # `Enum#to_json` offers a different serialization strategy based on the member\n  # name.\n  def self.to_json(member : T, json : JSON::Builder)\n    json.scalar(member.value)\n  end\nend\n\nstruct Time\n  # Emits a string formatted according to [RFC 3339](https://tools.ietf.org/html/rfc3339)\n  # ([ISO 8601](https://web.archive.org/web/20250306154328/http://xml.coverpages.org/ISO-FDIS-8601.pdf) profile).\n  #\n  # The JSON format itself does not specify a time data type, this method just\n  # assumes that a string holding a RFC 3339 time format will be interpreted as\n  # a time value.\n  #\n  # See `#from_json` for reference.\n  def to_json(json : JSON::Builder) : Nil\n    json.string do |io|\n      Time::Format::RFC_3339.format(self, io, fraction_digits: 0)\n    end\n  end\nend\n\n# Converter to be used with `JSON::Serializable`\n# to serialize the elements of an `Array(T)` with the custom converter.\n#\n# ```\n# require \"json\"\n#\n# class TimestampArray\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: JSON::ArrayConverter(Time::EpochConverter))]\n#   property dates : Array(Time)\n# end\n#\n# timestamp = TimestampArray.from_json(%({\"dates\":[1459859781,1567628762]}))\n# timestamp.dates   # => [2016-04-05 12:36:21Z, 2019-09-04 20:26:02Z]\n# timestamp.to_json # => %({\"dates\":[1459859781,1567628762]})\n# ```\n#\n# `JSON::ArrayConverter.new` should be used if the nested converter is also an\n# instance instead of a type.\n#\n# ```\n# require \"json\"\n#\n# class TimestampArray\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: JSON::ArrayConverter.new(Time::Format.new(\"%b %-d, %Y\")))]\n#   property dates : Array(Time)\n# end\n#\n# timestamp = TimestampArray.from_json(%({\"dates\":[\"Apr 5, 2016\",\"Sep 4, 2019\"]}))\n# timestamp.dates   # => [2016-04-05 00:00:00Z, 2019-09-04 00:00:00Z]\n# timestamp.to_json # => %({\"dates\":[\"Apr 5, 2016\",\"Sep 4, 2019\"]})\n# ```\n#\n# This implies that `JSON::ArrayConverter(T)` and\n# `JSON::ArrayConverter(T.class).new(T)` perform the same serializations.\nmodule JSON::ArrayConverter(Converter)\n  private struct WithInstance(T)\n    def initialize(@converter : T)\n    end\n\n    def to_json(values : Array, builder : JSON::Builder)\n      builder.array do\n        values.each do |value|\n          @converter.to_json(value, builder)\n        end\n      end\n    end\n  end\n\n  def self.new(converter : Converter)\n    WithInstance.new(converter)\n  end\n\n  def self.to_json(values : Array, builder : JSON::Builder)\n    WithInstance.new(Converter).to_json(values, builder)\n  end\nend\n\n# Converter to be used with `JSON::Serializable`\n# to serialize the values of a `Hash(String, V)` with the custom converter.\n#\n# ```\n# require \"json\"\n#\n# class TimestampHash\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: JSON::HashValueConverter(Time::EpochConverter))]\n#   property birthdays : Hash(String, Time)\n# end\n#\n# timestamp = TimestampHash.from_json(%({\"birthdays\":{\"foo\":1459859781,\"bar\":1567628762}}))\n# timestamp.birthdays # => {\"foo\" => 2016-04-05 12:36:21Z, \"bar\" => 2019-09-04 20:26:02Z}\n# timestamp.to_json   # => %({\"birthdays\":{\"foo\":1459859781,\"bar\":1567628762}})\n# ```\n#\n# `JSON::HashValueConverter.new` should be used if the nested converter is also\n# an instance instead of a type.\n#\n# ```\n# require \"json\"\n#\n# class TimestampHash\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: JSON::HashValueConverter.new(Time::Format.new(\"%b %-d, %Y\")))]\n#   property birthdays : Hash(String, Time)\n# end\n#\n# timestamp = TimestampHash.from_json(%({\"birthdays\":{\"foo\":\"Apr 5, 2016\",\"bar\":\"Sep 4, 2019\"}}))\n# timestamp.birthdays # => {\"foo\" => 2016-04-05 00:00:00Z, \"bar\" => 2019-09-04 00:00:00Z}\n# timestamp.to_json   # => %({\"birthdays\":{\"foo\":\"Apr 5, 2016\",\"bar\":\"Sep 4, 2019\"}})\n# ```\n#\n# This implies that `JSON::HashValueConverter(T)` and\n# `JSON::HashValueConverter(T.class).new(T)` perform the same serializations.\nmodule JSON::HashValueConverter(Converter)\n  private struct WithInstance(T)\n    def initialize(@converter : T)\n    end\n\n    def to_json(values : Hash, builder : JSON::Builder)\n      builder.object do\n        values.each do |key, value|\n          builder.field key.to_json_object_key do\n            @converter.to_json(value, builder)\n          end\n        end\n      end\n    end\n  end\n\n  def self.new(converter : Converter)\n    WithInstance.new(converter)\n  end\n\n  def self.to_json(values : Hash, builder : JSON::Builder)\n    WithInstance.new(Converter).to_json(values, builder)\n  end\nend\n\n# Converter to be used with `JSON::Serializable` and `YAML::Serializable`\n# to serialize a `Time` instance as the number of seconds\n# since the unix epoch. See `Time#to_unix`.\n#\n# ```\n# require \"json\"\n#\n# class Person\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: Time::EpochConverter)]\n#   property birth_date : Time\n# end\n#\n# person = Person.from_json(%({\"birth_date\": 1459859781}))\n# person.birth_date # => 2016-04-05 12:36:21Z\n# person.to_json    # => %({\"birth_date\":1459859781})\n# ```\nmodule Time::EpochConverter\n  def self.to_json(value : Time, json : JSON::Builder) : Nil\n    json.number(value.to_unix)\n  end\nend\n\n# Converter to be used with `JSON::Serializable` and `YAML::Serializable`\n# to serialize a `Time` instance as the number of milliseconds\n# since the unix epoch. See `Time#to_unix_ms`.\n#\n# ```\n# require \"json\"\n#\n# class Timestamp\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: Time::EpochMillisConverter)]\n#   property value : Time\n# end\n#\n# timestamp = Timestamp.from_json(%({\"value\": 1459860483856}))\n# timestamp.value   # => 2016-04-05 12:48:03.856Z\n# timestamp.to_json # => %({\"value\":1459860483856})\n# ```\nmodule Time::EpochMillisConverter\n  def self.to_json(value : Time, json : JSON::Builder) : Nil\n    json.number(value.to_unix_ms)\n  end\nend\n\n# Converter to be used with `JSON::Serializable` to read the raw\n# value of a JSON object property as a `String`.\n#\n# It can be useful to read ints and floats without losing precision,\n# or to read an object and deserialize it later based on some\n# condition.\n#\n# ```\n# require \"json\"\n#\n# class Raw\n#   include JSON::Serializable\n#\n#   @[JSON::Field(converter: String::RawConverter)]\n#   property value : String\n# end\n#\n# raw = Raw.from_json(%({\"value\": 123456789876543212345678987654321}))\n# raw.value   # => \"123456789876543212345678987654321\"\n# raw.to_json # => %({\"value\":123456789876543212345678987654321})\n# ```\nmodule String::RawConverter\n  def self.to_json(value : String, json : JSON::Builder) : Nil\n    json.raw(value)\n  end\nend\n"
  },
  {
    "path": "src/json/token.cr",
    "content": "class JSON::Token\n  enum Kind\n    Null\n    False\n    True\n    Int\n    Float\n    String\n    BeginArray\n    EndArray\n    BeginObject\n    EndObject\n    Colon\n    Comma\n    EOF\n  end\n\n  property kind : Kind\n  property string_value : String\n\n  def int_value : Int64\n    raw_value.to_i64\n  rescue exc : ArgumentError\n    raise ParseException.new(exc.message, line_number, column_number)\n  end\n\n  def float_value : Float64\n    raw_value.to_f64\n  rescue exc : ArgumentError\n    raise ParseException.new(exc.message, line_number, column_number)\n  end\n\n  @line_number : Int64\n  @column_number : Int64\n\n  def line_number : Int32\n    @line_number.to_i32\n  end\n\n  @[Experimental]\n  def line_number_i64 : Int64\n    @line_number\n  end\n\n  def line_number=(line_number)\n    @line_number = line_number.to_i64\n  end\n\n  def column_number : Int32\n    @column_number.to_i32\n  end\n\n  @[Experimental]\n  def column_number_i64\n    @column_number\n  end\n\n  def column_number=(column_number)\n    @column_number = column_number.to_i64\n  end\n\n  property raw_value : String\n\n  def initialize\n    @kind = :EOF\n    @line_number = 0\n    @column_number = 0\n    @string_value = \"\"\n    @raw_value = \"\"\n  end\n\n  def to_s(io : IO) : Nil\n    case @kind\n    when .null?\n      io << \"null\"\n    when .false?\n      io << \"false\"\n    when .true?\n      io << \"true\"\n    when .int?\n      raw_value.to_s(io)\n    when .float?\n      raw_value.to_s(io)\n    when .string?\n      string_value.to_s(io)\n    when .begin_array?\n      io << '['\n    when .end_array?\n      io << ']'\n    when .begin_object?\n      io << '{'\n    when .end_object?\n      io << '}'\n    when .colon?\n      io << ':'\n    when .comma?\n      io << ','\n    when .eof?\n      io << \"<EOF>\"\n    else\n      raise \"Unknown token kind: #{@kind}\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/json.cr",
    "content": "# The JSON module allows parsing and generating [JSON](http://json.org/) documents.\n#\n# NOTE: To use `JSON` or its children, you must explicitly import it with `require \"json\"`\n#\n# ### General type-safe interface\n#\n# The general type-safe interface for parsing JSON is to invoke `T.from_json` on a\n# target type `T` and pass either a `String` or `IO` as an argument.\n#\n# ```\n# require \"json\"\n#\n# json_text = %([1, 2, 3])\n# Array(Int32).from_json(json_text) # => [1, 2, 3]\n#\n# json_text = %({\"x\": 1, \"y\": 2})\n# Hash(String, Int32).from_json(json_text) # => {\"x\" => 1, \"y\" => 2}\n# ```\n#\n# Serializing is achieved by invoking `to_json`, which returns a `String`, or\n# `to_json(io : IO)`, which will stream the JSON to an `IO`.\n#\n# ```\n# require \"json\"\n#\n# [1, 2, 3].to_json            # => \"[1,2,3]\"\n# {\"x\" => 1, \"y\" => 2}.to_json # => \"{\\\"x\\\":1,\\\"y\\\":2}\"\n# ```\n#\n# Most types in the standard library implement these methods. For user-defined types\n# you can define a `self.new(pull : JSON::PullParser)` for parsing and\n# `to_json(builder : JSON::Builder)` for serializing. The following sections\n# show convenient ways to do this using `JSON::Serializable`.\n#\n# NOTE: JSON object keys are always strings but they can still be parsed\n# and deserialized to other types. To deserialize, define a\n# `T.from_json_object_key?(key : String) : T?` method, which can return `nil`\n# if the string can't be parsed into that type. To serialize, define a\n# `to_json_object_key : String` method can be serialized that way.\n# All integer and float types in the standard library can be deserialized that way.\n#\n# ```\n# require \"json\"\n#\n# json_text = %({\"1\": 2, \"3\": 4})\n# Hash(Int32, Int32).from_json(json_text) # => {1 => 2, 3 => 4}\n#\n# {1.5 => 2}.to_json # => \"{\\\"1.5\\\":2}\"\n# ```\n#\n# ### Parsing with `JSON.parse`\n#\n# `JSON.parse` will return an `Any`, which is a convenient wrapper around all possible JSON types,\n# making it easy to traverse a complex JSON structure but requires some casts from time to time,\n# mostly via some method invocations.\n#\n# ```\n# require \"json\"\n#\n# value = JSON.parse(\"[1, 2, 3]\") # : JSON::Any\n#\n# value[0]               # => 1\n# typeof(value[0])       # => JSON::Any\n# value[0].as_i          # => 1\n# typeof(value[0].as_i)  # => Int32\n# value[0].as_i?         # => 1\n# typeof(value[0].as_i?) # => Int32 | Nil\n# value[0].as_s?         # => nil\n# typeof(value[0].as_s?) # => String | Nil\n#\n# value[0] + 1       # Error, because value[0] is JSON::Any\n# value[0].as_i + 10 # => 11\n# ```\n#\n# `JSON.parse` can read from an `IO` directly (such as a file) which saves\n# allocating a string:\n#\n# ```\n# require \"json\"\n#\n# json = File.open(\"path/to/file.json\") do |file|\n#   JSON.parse(file)\n# end\n# ```\n#\n# Parsing with `JSON.parse` is useful for dealing with a dynamic JSON structure.\n#\n# ### Generating with `JSON.build`\n#\n# Use `JSON.build`, which uses `JSON::Builder`, to generate JSON\n# by emitting scalars, arrays and objects:\n#\n# ```\n# require \"json\"\n#\n# string = JSON.build do |json|\n#   json.object do\n#     json.field \"name\", \"foo\"\n#     json.field \"values\" do\n#       json.array do\n#         json.number 1\n#         json.number 2\n#         json.number 3\n#       end\n#     end\n#   end\n# end\n# string # => %<{\"name\":\"foo\",\"values\":[1,2,3]}>\n# ```\n#\n# ### Generating with `to_json`\n#\n# `to_json`, `to_json(IO)` and `to_json(JSON::Builder)` methods are provided\n# for primitive types, but you need to define `to_json(JSON::Builder)`\n# for custom objects, either manually or using `JSON::Serializable`.\nmodule JSON\n  # Generic JSON error.\n  class Error < Exception\n  end\n\n  # Exception thrown on a JSON parse error.\n  class ParseException < Error\n    @line_number : Int64\n    @column_number : Int64\n\n    def line_number : Int32\n      @line_number.to_i32\n    end\n\n    def column_number : Int32\n      @column_number.to_i32\n    end\n\n    @[Experimental]\n    def line_number_i64 : Int64\n      @line_number\n    end\n\n    @[Experimental]\n    def column_number_i64\n      @column_number\n    end\n\n    def initialize(message, line_number, column_number, cause = nil)\n      @line_number = line_number.to_i64\n      @column_number = column_number.to_i64\n      super \"#{message} at line #{@line_number}, column #{@column_number}\", cause\n    end\n\n    def location : {Int32, Int32}\n      {line_number, column_number}\n    end\n\n    @[Experimental]\n    def location_i64 : {Int64, Int64}\n      {line_number_i64, column_number_i64}\n    end\n  end\n\n  # Parses a JSON document as a `JSON::Any`.\n  def self.parse(input : String | IO) : Any\n    Parser.new(input).parse\n  end\nend\n\nrequire \"./json/*\"\n"
  },
  {
    "path": "src/kernel.cr",
    "content": "require \"crystal/at_exit_handlers\"\n\n{% unless flag?(:win32) %}\n  require \"c/unistd\"\n{% end %}\n\n# The standard input file descriptor. Contains data piped to the program.\n#\n# On Unix systems, if the file descriptor is a TTY, the runtime duplicates it.\n# So `STDIN.fd` might not be `0`.\n# The reason for this is to enable non-blocking reads for concurrency. Other fibers\n# can run while waiting on user input. The original file descriptor is\n# inherited from the parent process. Setting it to non-blocking mode would\n# reflect back, which can cause problems.\n#\n# On Windows, `STDIN` is always blocking.\nSTDIN = IO::FileDescriptor.from_stdio(0)\n\n# The standard output file descriptor.\n#\n# Typically used to output data and information.\n#\n# At the start of the program STDOUT is configured like this:\n# - if it's a TTY device (like the console) then `sync` is `true`,\n#   meaning that output will be outputted as soon as it is written\n#   to STDOUT. This is so users can see real time output data.\n# - if it's not a TTY device (like a file, or because the output\n#   was piped to a file) then `sync` is `false` but `flush_on_newline`\n#   is `true`. This is so that if you pipe the output to a file, and,\n#   for example, you `tail -f`, you can see data on a line-per-line basis.\n#   This is convenient but slower than with `flush_on_newline` set to `false`.\n#   If you need a bit more performance and you don't care about near real-time\n#   output you can do `STDOUT.flush_on_newline = false`.\n#\n# On Unix systems, if the file descriptor is a TTY, the runtime duplicates it.\n# So `STDOUT.fd` might not be `1`.\n# The reason for this is to enable non-blocking writes for concurrency. Other fibers\n# can run while waiting on IO. The original file descriptor is\n# inherited from the parent process. Setting it to non-blocking mode would\n# reflect back which can cause problems.\n#\n# On Windows, `STDOUT` is always blocking.\nSTDOUT = IO::FileDescriptor.from_stdio(1)\n\n# The standard error file descriptor.\n#\n# Typically used to output error messages and diagnostics.\n#\n# At the start of the program STDERR is configured like this:\n# - if it's a TTY device (like the console) then `sync` is `true`,\n#   meaning that output will be outputted as soon as it is written\n#   to STDERR. This is so users can see real time output data.\n# - if it's not a TTY device (like a file, or because the output\n#   was piped to a file) then `sync` is `false` but `flush_on_newline`\n#   is `true`. This is so that if you pipe the output to a file, and,\n#   for example, you `tail -f`, you can see data on a line-per-line basis.\n#   This is convenient but slower than with `flush_on_newline` set to `false`.\n#   If you need a bit more performance and you don't care about near real-time\n#   output you can do `STDERR.flush_on_newline = false`.\n#\n# On Unix systems, if the file descriptor is a TTY, the runtime duplicates it.\n# So `STDERR.fd` might not be `2`.\n# The reason for this is to enable non-blocking writes for concurrency. Other fibers\n# can run while waiting on IO. The original file descriptor is\n# inherited from the parent process. Setting it to non-blocking mode would\n# reflect back which can cause problems.\n#\n# On Windows, `STDERR` is always blocking.\nSTDERR = IO::FileDescriptor.from_stdio(2)\n\n# The name, the program was called with.\n#\n# The result may be a relative or absolute path (including symbolic links),\n# just the command name or the empty string.\n#\n# See `Process.executable_path` for a more convenient alternative that always\n# returns the absolute real path to the executable file (if it exists).\nPROGRAM_NAME = String.new(ARGV_UNSAFE.value)\n\n# An array of arguments passed to the program.\nARGV = Array.new(ARGC_UNSAFE - 1) { |i| String.new(ARGV_UNSAFE[1 + i]) }\n\n# An `IO` for reading files from `ARGV`.\n#\n# Usage example:\n#\n# `program.cr`:\n# ```\n# puts ARGF.gets_to_end\n# ```\n#\n# A file to read from: (`file`)\n# ```text\n# 123\n# ```\n#\n# ```text\n# $ crystal build program.cr\n# $ ./program file\n# 123\n# $ ./program file file\n# 123123\n# $ # If ARGV is empty, ARGF reads from STDIN instead:\n# $ echo \"hello\" | ./program\n# hello\n# $ ./program unknown\n# Unhandled exception: Error opening file with mode 'r': 'unknown': No such file or directory (File::NotFoundError)\n# ...\n# ```\n#\n# After a file from `ARGV` has been read, it's removed from `ARGV`.\n#\n# You can manipulate `ARGV` yourself to control what `ARGF` operates on.\n# If you remove a file from `ARGV`, it is ignored by `ARGF`; if you add files to `ARGV`, `ARGF` will read from it.\n#\n# ```\n# ARGV.replace [\"file1\"]\n# ARGF.gets_to_end # => Content of file1\n# ARGV             # => []\n# ARGV << \"file2\"\n# ARGF.gets_to_end # => Content of file2\n# ```\nARGF = IO::ARGF.new(ARGV, STDIN)\n\n# The newline constant\nEOL = {% if flag?(:windows) %}\n        \"\\r\\n\"\n      {% else %}\n        \"\\n\"\n      {% end %}\n\n# Repeatedly executes the block.\n#\n# ```\n# loop do\n#   line = gets\n#   break unless line\n#   # ...\n# end\n# ```\ndef loop(&)\n  while true\n    yield\n  end\nend\n\n# Reads a line from `STDIN`.\n#\n# See also: `IO#gets`.\ndef gets(*args, **options)\n  STDIN.gets(*args, **options)\nend\n\n# Reads a line from `STDIN`.\n#\n# See also: `IO#read_line`.\ndef read_line(*args, **options)\n  STDIN.read_line(*args, **options)\nend\n\n# Prints objects to `STDOUT` and then invokes `STDOUT.flush`.\n#\n# See also: `IO#print`.\ndef print(*objects : _) : Nil\n  STDOUT.print *objects\nend\n\n# Prints a formatted string to `STDOUT`.\n#\n# For details on the format string, see `sprintf`.\ndef printf(format_string, *args) : Nil\n  printf format_string, args\nend\n\n# :ditto:\ndef printf(format_string, args : Array | Tuple) : Nil\n  STDOUT.printf format_string, args\nend\n\n# Returns a formatted string.\n# The string is produced according to the *format_string* with format specifiers\n# being replaced by values from *args* formatted according to the specifier.\n#\n# Within the format string, any characters other than format specifiers\n# (specifiers beginning with `%`) are copied to the result. The formatter\n# supports the following kinds of format specifiers:\n#\n# * Sequential (`%.1f`). The first `%` consumes the first argument, the second\n#   consumes the second argument, and so on.\n# * Positional (`%3$.1f`). The one-based argument index is specified as part of\n#   the flags.\n# * Named substitution (`%<name>.1f`, `%{name}`). The angle bracket form accepts\n#   flags and the curly bracket form doesn't. Exactly one `Hash` or `NamedTuple`\n#   must be passed as the argument.\n#\n# Mixing of different kinds of format specifiers is disallowed, except that the\n# two named forms may be used together.\n#\n# A simple format specifier consists of a percent sign, followed by optional flags,\n# width, and precision indicators, then terminated with a field type\n# character.\n#\n# ```text\n# %[flags][width][.precision]type\n# ```\n#\n# A formatted substitution is similar but after the percent sign follows the\n# mandatory name of the substitution wrapped in angle brackets.\n#\n# ```text\n# %<name>[flags][width][.precision]type\n# ```\n#\n# The percent sign itself is escaped by `%%`. No format modifiers are allowed,\n# and no arguments are consumed.\n#\n# The field type controls how the corresponding argument value is to be\n# interpreted, while the flags modify that interpretation.\n#\n# The field type characters are:\n#\n# ```text\n# Field | Integer Format\n# ------+------------------------------------------------------------------\n#   b   | Formats argument as a binary number.\n#   d   | Formats argument as a decimal number.\n#   i   | Same as d.\n#   o   | Formats argument as an octal number.\n#   x   | Formats argument as a hexadecimal number using lowercase letters.\n#   X   | Same as x, but uses uppercase letters.\n#\n# Field | Float Format\n# ------+---------------------------------------------------------------\n#   e   | Formats floating point argument into exponential notation\n#       | with one digit before the decimal point as [-]d.dddddde[+-]dd.\n#       | The precision specifies the number of digits after the decimal\n#       | point (defaulting to six).\n#   E   | Equivalent to e, but uses an uppercase E to indicate\n#       | the exponent.\n#   f   | Formats floating point argument as [-]ddd.dddddd,\n#       | where the precision specifies the number of digits after\n#       | the decimal point.\n#   g   | Formats a floating point number using exponential form\n#       | if the exponent is less than -4 or greater than or\n#       | equal to the precision, or in dd.dddd form otherwise.\n#       | The precision specifies the number of significant digits.\n#   G   | Equivalent to g, but use an uppercase E in exponent form.\n#   a   | Formats floating point argument as [-]0xh.hhhhp[+-]dd,\n#       | which consists of an optional sign, \"0x\", fraction part\n#       | as hexadecimal, \"p\", and exponential part as decimal.\n#   A   | Equivalent to a, but uses uppercase X and P.\n#\n# Field | Other Format\n# ------+------------------------------------------------------------\n#   c   | Argument is a single character itself.\n#   s   | Argument is a string to be substituted. If the format\n#       | sequence contains a precision, at most that many characters\n#       | will be copied.\n# ```\n#\n# Flags modify the behavior of the format specifiers:\n#\n# ```text\n# Flag     | Applies to    | Meaning\n# ---------+---------------+----------------------------------------------------\n# space    | bdiouxX       | Add a leading space character to\n#          | aAeEfgG       | non-negative numbers. Has no effect if the plus\n#          | (numeric fmt) | flag is also given.\n# ---------+---------------+----------------------------------------------------\n# #        | boxX          | Use an alternative format.\n#          | aAeEfgG       | For x, X, b, prefix any non-zero result with\n#          |               | 0x, 0X, or 0b respectively.\n#          |               | For o, prefix any non-zero result with 0o. Note\n#          |               | that this prefix is different from C and Ruby.\n#          |               | For a, A, e, E, f, g, G,\n#          |               | force a decimal point to be added,\n#          |               | even if no digits follow.\n#          |               | For g and G, do not remove trailing zeros.\n# ---------+---------------+----------------------------------------------------\n# +        | bdiouxX       | Add a leading plus sign to non-negative\n#          | aAeEfgG       | numbers.\n#          | (numeric fmt) |\n# ---------+---------------+----------------------------------------------------\n# -        | all           | Left-justify the result of this conversion.\n# ---------+---------------+----------------------------------------------------\n# 0 (zero) | bdiouxX       | Pad with zeros, not spaces. Has no effect if the\n#          | aAeEfgG       | number is left-justified, or a precision is given\n#          | (numeric fmt) | to an integer field type.\n# ---------+---------------+----------------------------------------------------\n# *        | all           | Use the next argument as the field width or\n#          |               | precision. If width is negative, set the minus flag\n#          |               | and use the argument's absolute value as field\n#          |               | width. If precision is negative, it is ignored.\n# ---------+---------------+----------------------------------------------------\n# 1$ 2$ 3$ | all           | As a flag, specifies the one-based argument index\n# ...      |               | for a positional format specifier.\n#          |               | Immediately after *, use the argument at the given\n#          |               | one-based index as the width or precision, instead\n#          |               | of the next argument. The entire format string must\n#          |               | also use positional format specifiers throughout.\n# ```\n#\n# Examples of flags:\n#\n# Decimal number conversion:\n# ```\n# sprintf \"%d\", 123  # => \"123\"\n# sprintf \"%+d\", 123 # => \"+123\"\n# sprintf \"% d\", 123 # => \" 123\"\n# ```\n#\n# Octal number conversion:\n# ```\n# sprintf \"%o\", 123   # => \"173\"\n# sprintf \"%+o\", 123  # => \"+173\"\n# sprintf \"%o\", -123  # => \"-173\"\n# sprintf \"%+o\", -123 # => \"-173\"\n# ```\n#\n# Hexadecimal number conversion:\n# ```\n# sprintf \"%x\", 123   # => \"7b\"\n# sprintf \"%+x\", 123  # => \"+7b\"\n# sprintf \"%x\", -123  # => \"-7b\"\n# sprintf \"%+x\", -123 # => \"-7b\"\n# sprintf \"%#x\", 0    # => \"0\"\n# sprintf \"% x\", 123  # => \" 7b\"\n# sprintf \"% x\", -123 # => \"-7b\"\n# sprintf \"%X\", 123   # => \"7B\"\n# sprintf \"%#X\", -123 # => \"-0X7B\"\n# ```\n#\n# Binary number conversion:\n# ```\n# sprintf \"%b\", 123    # => \"1111011\"\n# sprintf \"%+b\", 123   # => \"+1111011\"\n# sprintf \"%+b\", -123  # => \"-1111011\"\n# sprintf \"%b\", -123   # => \"-1111011\"\n# sprintf \"%#b\", 0     # => \"0\"\n# sprintf \"% b\", 123   # => \" 1111011\"\n# sprintf \"%+ b\", 123  # => \"+1111011\"\n# sprintf \"% b\", -123  # => \"-1111011\"\n# sprintf \"%+ b\", -123 # => \"-1111011\"\n# sprintf \"%#b\", 123   # => \"0b1111011\"\n# ```\n#\n# Floating point conversion:\n# ```\n# sprintf \"%a\", 123 # => \"0x1.ecp+6\"\n# sprintf \"%A\", 123 # => \"0X1.ECP+6\"\n# ```\n#\n# Exponential form conversion:\n# ```\n# sprintf \"%g\", 123.4          # => \"123.4\"\n# sprintf \"%g\", 123.4567       # => \"123.457\"\n# sprintf \"%20.8g\", 1234.56789 # => \"           1234.5679\"\n# sprintf \"%20.8g\", 123456789  # => \"       1.2345679e+08\"\n# sprintf \"%20.8G\", 123456789  # => \"       1.2345679E+08\"\n# sprintf \"%20.8g\", -123456789 # => \"      -1.2345679e+08\"\n# sprintf \"%20.8G\", -123456789 # => \"      -1.2345679E+08\"\n# ```\n#\n# The field width is an optional integer, followed optionally by a\n# period and a precision. The width specifies the minimum number of\n# characters that will be written to the result for this field.\n#\n# Examples of width:\n# ```\n# sprintf \"%20d\", 123   # => \"                 123\"\n# sprintf \"%+20d\", 123  # => \"                +123\"\n# sprintf \"%020d\", 123  # => \"00000000000000000123\"\n# sprintf \"%+020d\", 123 # => \"+0000000000000000123\"\n# sprintf \"% 020d\", 123 # => \" 0000000000000000123\"\n# sprintf \"%-20d\", 123  # => \"123                 \"\n# sprintf \"%-+20d\", 123 # => \"+123                \"\n# sprintf \"%- 20d\", 123 # => \" 123                \"\n# sprintf \"%020x\", -123 # => \"-000000000000000007b\"\n# sprintf \"%020X\", -123 # => \"-000000000000000007B\"\n# ```\n#\n# For numeric fields, the precision controls the number of decimal places\n# displayed.\n#\n# For string fields, the precision determines the maximum\n# number of characters to be copied from the string.\n#\n# Examples of precisions:\n#\n# Precision for `d`, `o`, `x` and `b` is\n# minimum number of digits:\n# ```\n# sprintf \"%20.8d\", 123   # => \"            00000123\"\n# sprintf \"%020.8d\", 123  # => \"            00000123\"\n# sprintf \"%20.8o\", 123   # => \"            00000173\"\n# sprintf \"%020.8o\", 123  # => \"            00000173\"\n# sprintf \"%20.8x\", 123   # => \"            0000007b\"\n# sprintf \"%020.8x\", 123  # => \"            0000007b\"\n# sprintf \"%20.8b\", 123   # => \"            01111011\"\n# sprintf \"%20.8d\", -123  # => \"           -00000123\"\n# sprintf \"%020.8d\", -123 # => \"           -00000123\"\n# sprintf \"%20.8o\", -123  # => \"           -00000173\"\n# sprintf \"%20.8x\", -123  # => \"           -0000007b\"\n# sprintf \"%20.8b\", -11   # => \"           -00001011\"\n# ```\n#\n# Precision for `e` is number of digits after the decimal point:\n# ```\n# sprintf \"%20.8e\", 1234.56789 # => \"      1.23456789e+03\"\n# ```\n#\n# Precision for `f` is number of digits after the decimal point:\n# ```\n# sprintf \"%20.8f\", 1234.56789 # => \"       1234.56789000\"\n# ```\n#\n# Precision for `g` is number of significant digits:\n# ```\n# sprintf \"%20.8g\", 1234.56789 # => \"           1234.5679\"\n# sprintf \"%20.8g\", 123456789  # => \"       1.2345679e+08\"\n# sprintf \"%-20.8g\", 123456789 # => \"1.2345679e+08       \"\n# ```\n#\n# Precision for `s` is maximum number of characters:\n# ```\n# sprintf \"%20.8s\", \"string test\" # => \"            string t\"\n# ```\n#\n# Additional examples:\n# ```\n# sprintf \"%d %04x\", 123, 123                 # => \"123 007b\"\n# sprintf \"%08b '%4s'\", 123, 123              # => \"01111011 ' 123'\"\n# sprintf \"%+g:% g:%-g\", 1.23, 1.23, 1.23     # => \"+1.23: 1.23:1.23\"\n# sprintf \"%-3$*1$.*2$s\", 10, 5, \"abcdefghij\" # => \"abcde     \"\n# ```\ndef sprintf(format_string, *args) : String\n  sprintf format_string, args\nend\n\n# :ditto:\ndef sprintf(format_string, args : Array | Tuple) : String\n  String.build(format_string.bytesize) do |str|\n    String::Formatter(typeof(args)).new(format_string, args, str).format\n  end\nend\n\n# Prints *objects* to `STDOUT`, each followed by a newline character unless\n# the object is a `String` and already ends with a newline.\n#\n# See also: `IO#puts`.\ndef puts(*objects) : Nil\n  STDOUT.puts *objects\nend\n\n# Inspects *object* to `STDOUT` followed\n# by a newline. Returns *object*.\n#\n# See also: `Object#inspect(io)`.\ndef p(object)\n  object.inspect(STDOUT)\n  puts\n  object\nend\n\n# Inspects each object in *objects* to `STDOUT`, followed\n# by a newline. Returns *objects*.\n#\n# See also: `Object#inspect(io)`.\ndef p(*objects)\n  objects.each do |obj|\n    p obj # ameba:disable Lint/DebugCalls\n  end\n  objects\nend\n\n# Inspects *objects* to `STDOUT`, followed\n# by a newline. Returns *objects*.\n#\n# ```\n# p foo: 23, bar: 42 # => {foo: 23, bar: 42}\n# ```\n#\n# See `Object#inspect(io)`\ndef p(**objects)\n  p(objects) unless objects.empty? # ameba:disable Lint/DebugCalls\nend\n\n# Pretty prints *object* to `STDOUT` followed\n# by a newline. Returns *object*.\n#\n# See also: `Object#pretty_print(pp)`.\ndef pp(object)\n  PrettyPrint.format(object, STDOUT, 79)\n  puts\n  object\nend\n\n# Pretty prints each object in *objects* to `STDOUT`, followed\n# by a newline. Returns *objects*.\n#\n# See also: `Object#pretty_print(pp)`.\ndef pp(*objects)\n  objects.each do |obj|\n    pp obj # ameba:disable Lint/DebugCalls\n  end\n  objects\nend\n\n# Pretty prints *objects* to `STDOUT`, followed\n# by a newline. Returns *objects*.\n#\n# ```\n# p foo: 23, bar: 42 # => {foo: 23, bar: 42}\n# ```\n#\n# See `Object#pretty_print(pp)`\ndef pp(**objects)\n  pp(objects) unless objects.empty? # ameba:disable Lint/DebugCalls\nend\n\n# Registers the given `Proc` for execution when the program exits regularly.\n#\n# A regular exit happens when either\n# * the main fiber reaches the end of the program,\n# * the main fiber rescues an unhandled exception, or\n# * `::exit` is called.\n#\n# `Process.exit` does *not* trigger `at_exit` handlers, nor does external process\n# termination (see `Process.on_terminate` for handling that).\n#\n# If multiple handlers are registered, they are executed in reverse order of registration.\n#\n# ```\n# def do_at_exit(str1)\n#   at_exit { print str1 }\n# end\n#\n# at_exit { puts \"cruel world\" }\n# do_at_exit(\"goodbye \")\n# exit\n# ```\n#\n# Produces:\n#\n# ```text\n# goodbye cruel world\n# ```\n#\n# The exit status code that will be returned by this program is passed to\n# the block as its first argument. In case of any unhandled exception, it is\n# passed as the second argument to the block, if the program terminates\n# normally or `exit(status)` is called explicitly, then the second argument\n# will be `nil`.\n#\n# NOTE: If `at_exit` is called inside an `at_exit` handler, it will be called\n# right after the current `at_exit` handler ends, and then other handlers\n# will be invoked.\ndef at_exit(&handler : Int32, Exception? ->) : Nil\n  Crystal::AtExitHandlers.add(handler)\nend\n\n# Terminates execution immediately, returning the given status code\n# to the invoking environment.\n#\n# Registered `at_exit` procs are executed.\ndef exit(status : Int32 | Process::Status = 0) : NoReturn\n  status = Crystal::AtExitHandlers.run status\n  Crystal.ignore_stdio_errors { STDOUT.flush }\n  Crystal.ignore_stdio_errors { STDERR.flush }\n  Process.exit(status)\nend\n\n# Terminates execution immediately, printing *message* to `STDERR` and\n# then calling `exit(status)`.\ndef abort(message = nil, status = 1) : NoReturn\n  Crystal.ignore_stdio_errors { STDERR.puts message } if message\n  exit status\nend\n\n{% if !flag?(:preview_mt) && flag?(:unix) %}\n  class Process\n    # :nodoc:\n    #\n    # Hooks are defined here due to load order problems.\n    def self.after_fork_child_callbacks\n      @@after_fork_child_callbacks ||= [\n        # reinit event loop first:\n        -> { Crystal::EventLoop.current.after_fork },\n\n        # reinit signal handling:\n        ->Crystal::System::Signal.after_fork,\n        ->Crystal::System::SignalChildHandler.after_fork,\n\n        # additional reinitialization\n        ->Random::DEFAULT.new_seed,\n        -> { Random.thread_default.new_seed },\n      ] of -> Nil\n    end\n  end\n{% end %}\n\n{% unless flag?(:interpreted) || flag?(:wasm32) %}\n  {% if flag?(:execution_context) %}\n    Fiber::ExecutionContext.init_default_context\n  {% else %}\n    Crystal::Scheduler.init\n  {% end %}\n\n  # load debug info on start up of the program is executed with CRYSTAL_LOAD_DEBUG_INFO=1\n  # this will make debug info available on print_frame that is used by Crystal's segfault handler\n  #\n  # - CRYSTAL_LOAD_DEBUG_INFO=0 will never use debug info (See Exception::CallStack.load_debug_info)\n  # - CRYSTAL_LOAD_DEBUG_INFO=1 will load debug info on startup\n  # - Other values will load debug info on demand: when the backtrace of the first exception is generated\n  Exception::CallStack.load_debug_info if ENV[\"CRYSTAL_LOAD_DEBUG_INFO\"]? == \"1\"\n  Exception::CallStack.setup_crash_handler\n\n  {% if flag?(:win32) %}\n    Crystal::System::Process.start_interrupt_loop\n  {% else %}\n    Crystal::System::Signal.setup_default_handlers\n  {% end %}\n{% end %}\n\n{% if flag?(:interpreted) && flag?(:unix) && Crystal::Interpreter.class.has_method?(:signal_descriptor) %}\n  Crystal::System::Signal.setup_default_handlers\n{% end %}\n"
  },
  {
    "path": "src/levenshtein.cr",
    "content": "# Levenshtein distance methods.\n#\n# NOTE: To use `Levenshtein`, you must explicitly import it with `require \"levenshtein\"`\nmodule Levenshtein\n  # Computes the [levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance) of two strings.\n  #\n  # ```\n  # require \"levenshtein\"\n  #\n  # Levenshtein.distance(\"algorithm\", \"altruistic\") # => 6\n  # Levenshtein.distance(\"hello\", \"hallo\")          # => 1\n  # Levenshtein.distance(\"こんにちは\", \"こんちは\")           # => 1\n  # Levenshtein.distance(\"hey\", \"hey\")              # => 0\n  # ```\n  def self.distance(string1 : String, string2 : String) : Int32\n    return 0 if string1 == string2\n\n    s_size = string1.size\n    t_size = string2.size\n\n    return t_size if s_size == 0\n    return s_size if t_size == 0\n\n    # This is to allocate less memory\n    if t_size > s_size\n      string1, string2 = string2, string1\n      t_size, s_size = s_size, t_size\n    end\n\n    costs = Slice(Int32).new(t_size + 1) { |i| i }\n    last_cost = 0\n\n    if string1.single_byte_optimizable? && string2.single_byte_optimizable?\n      s = string1.to_unsafe\n      t = string2.to_unsafe\n\n      s_size.times do |i|\n        last_cost = i + 1\n\n        t_size.times do |j|\n          sub_cost = s[i] == t[j] ? 0 : 1\n          cost = Math.min(Math.min(last_cost + 1, costs[j + 1] + 1), costs[j] + sub_cost)\n          costs[j] = last_cost\n          last_cost = cost\n        end\n        costs[t_size] = last_cost\n      end\n\n      last_cost\n    else\n      reader = Char::Reader.new(string1)\n\n      # Use an array instead of a reader to decode the second string only once\n      chars = string2.chars\n\n      reader.each_with_index do |char1, i|\n        last_cost = i + 1\n\n        chars.each_with_index do |char2, j|\n          sub_cost = char1 == char2 ? 0 : 1\n          cost = Math.min(Math.min(last_cost + 1, costs[j + 1] + 1), costs[j] + sub_cost)\n          costs[j] = last_cost\n          last_cost = cost\n        end\n        costs[t_size] = last_cost\n      end\n\n      last_cost\n    end\n  end\n\n  # Finds the closest string to a given string amongst many strings.\n  #\n  # ```\n  # require \"levenshtein\"\n  #\n  # finder = Levenshtein::Finder.new \"hallo\"\n  # finder.test \"hay\"\n  # finder.test \"hall\"\n  # finder.test \"hallo world\"\n  #\n  # finder.best_match # => \"hall\"\n  # ```\n  class Finder\n    # :nodoc:\n    record Entry,\n      value : String,\n      distance : Int32\n\n    @tolerance : Int32\n\n    def initialize(@target : String, tolerance : Int? = nil)\n      @tolerance = tolerance || (target.size / 5.0).ceil.to_i\n    end\n\n    def test(name : String, value : String = name)\n      distance = Levenshtein.distance(@target, name)\n      if distance <= @tolerance\n        if best_entry = @best_entry\n          if distance < best_entry.distance\n            @best_entry = Entry.new(value, distance)\n          end\n        else\n          @best_entry = Entry.new(value, distance)\n        end\n      end\n    end\n\n    def best_match : String?\n      @best_entry.try &.value\n    end\n\n    def self.find(name, tolerance = nil, &)\n      sn = new name, tolerance\n      yield sn\n      sn.best_match\n    end\n\n    def self.find(name, all_names, tolerance = nil) : String?\n      find(name, tolerance) do |similar|\n        all_names.each do |a_name|\n          similar.test(a_name)\n        end\n      end\n    end\n  end\n\n  # Finds the best match for *name* among strings added within the given block.\n  # *tolerance* can be used to set maximum Levenshtein distance allowed.\n  #\n  # ```\n  # require \"levenshtein\"\n  #\n  # best_match = Levenshtein.find(\"hello\") do |l|\n  #   l.test \"hulk\"\n  #   l.test \"holk\"\n  #   l.test \"halka\"\n  #   l.test \"ello\"\n  # end\n  # best_match # => \"ello\"\n  # ```\n  def self.find(name, tolerance = nil, &) : String?\n    Finder.find(name, tolerance) do |sn|\n      yield sn\n    end\n  end\n\n  # Finds the best match for *name* among strings provided in *all_names*.\n  # *tolerance* can be used to set maximum Levenshtein distance allowed.\n  #\n  # ```\n  # require \"levenshtein\"\n  #\n  # Levenshtein.find(\"hello\", [\"hullo\", \"hel\", \"hall\", \"hell\"], 2) # => \"hullo\"\n  # Levenshtein.find(\"hello\", [\"hurlo\", \"hel\", \"hall\"], 1)         # => nil\n  # ```\n  def self.find(name, all_names, tolerance = nil) : String?\n    Finder.find(name, all_names, tolerance)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UShort) : UShort\n  fun ntohs(x0 : UShort) : UShort\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_seekoff : UInt64\n    d_reclen : UShort\n    d_namlen : UShort\n    d_type : UChar\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(name : Char*) : DIR*\n  fun readdir(dirp : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    = 0x1\n  RTLD_NOW     = 0x2\n  RTLD_GLOBAL  = 0x8\n  RTLD_LOCAL   = 0x4\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(path : Char*, mode : Int) : Void*\n  fun dlsym(handle : Void*, symbol : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/errno.cr",
    "content": "lib LibC\n  fun __error : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  48\n  EADDRNOTAVAIL   =  49\n  EAFNOSUPPORT    =  47\n  EAGAIN          =  35\n  EALREADY        =  37\n  EBADF           =   9\n  EBADMSG         =  94\n  EBUSY           =  16\n  ECANCELED       =  89\n  ECHILD          =  10\n  ECONNABORTED    =  53\n  ECONNREFUSED    =  61\n  ECONNRESET      =  54\n  EDEADLK         =  11\n  EDESTADDRREQ    =  39\n  EDOM            =  33\n  EDQUOT          =  69\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    =  65\n  EIDRM           =  90\n  EILSEQ          =  92\n  EINPROGRESS     =  36\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         =  56\n  EISDIR          =  21\n  ELOOP           =  62\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  40\n  EMULTIHOP       =  95\n  ENAMETOOLONG    =  63\n  ENETDOWN        =  50\n  ENETRESET       =  52\n  ENETUNREACH     =  51\n  ENFILE          =  23\n  ENOBUFS         =  55\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  77\n  ENOLINK         =  97\n  ENOMEM          =  12\n  ENOMSG          =  91\n  ENOPROTOOPT     =  42\n  ENOSPC          =  28\n  ENOSYS          =  78\n  ENOTCONN        =  57\n  ENOTDIR         =  20\n  ENOTEMPTY       =  66\n  ENOTRECOVERABLE = 104\n  ENOTSOCK        =  38\n  ENOTSUP         =  45\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      = 102\n  EOVERFLOW       =  84\n  EOWNERDEAD      = 105\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          = 100\n  EPROTONOSUPPORT =  43\n  EPROTOTYPE      =  41\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          =  70\n  ETIMEDOUT       =  60\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           =  18\n  ENODATA         =  96\n  ENOSR           =  98\n  ENOSTR          =  99\n  ETIME           = 101\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0x1000000\n  O_CREAT    =    0x0200\n  O_NOFOLLOW =    0x0100\n  O_TRUNC    =    0x0400\n  O_EXCL     =    0x0800\n  O_APPEND   =    0x0008\n  O_NONBLOCK =    0x0004\n  O_SYNC     =    0x0080\n  O_RDONLY   =    0x0000\n  O_RDWR     =    0x0002\n  O_WRONLY   =    0x0001\n  AT_FDCWD   =        -2\n\n  struct Flock\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n    l_type : Short\n    l_whence : Short\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/iconv.cr",
    "content": "require \"./stddef\"\n\n@[Link(\"iconv\")]\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x00000001\n  AI_CANONNAME   = 0x00000002\n  AI_NUMERICHOST = 0x00000004\n  AI_NUMERICSERV = 0x00001000\n  AI_V4MAPPED    = 0x00000800\n  AI_ALL         = 0x00000100\n  AI_ADDRCONFIG  = 0x00000400\n  EAI_AGAIN      =          2\n  EAI_BADFLAGS   =          3\n  EAI_FAIL       =          4\n  EAI_FAMILY     =          5\n  EAI_MEMORY     =          6\n  EAI_NODATA     =          7\n  EAI_NONAME     =          8\n  EAI_SERVICE    =          9\n  EAI_SOCKTYPE   =         10\n  EAI_SYSTEM     =         11\n  EAI_OVERFLOW   =         14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Sockaddr*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Sockaddr*, x1 : SocklenT, x2 : Char*, x3 : SocklenT, x4 : Char*, x5 : SocklenT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UShort\n  alias InAddrT = UInt\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrU6Addr\n    __u6_addr8 : StaticArray(SaFamilyT, 16)\n    __u6_addr16 : StaticArray(UShort, 8)\n    __u6_addr32 : StaticArray(UInt, 4)\n  end\n\n  struct In6Addr\n    __u6_addr : In6AddrU6Addr\n  end\n\n  struct SockaddrIn\n    sin_len : SaFamilyT\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_len : SaFamilyT\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt\n  end\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP   = 12\n\n  IP_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP   = 13\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  0x01\n  TCP_KEEPINTVL = 0x101\n  TCP_KEEPCNT   = 0x102\n  TCP_KEEPALIVE =  0x10\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/pthread.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 1\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait_relative_np(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_get_stackaddr_np(x0 : PthreadT) : Void*\n  fun pthread_get_stacksize_np(x0 : PthreadT) : SizeT\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(state : Int, oldstate : Int*) : Int\n  fun pthread_setname_np(Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_change : TimeT\n    pw_class : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n    pw_expire : TimeT\n    pw_fields : Int\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    = 10\n  SIGSEGV   = 11\n  SIGSYS    = 12\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 16\n  SIGSTOP   = 17\n  SIGTSTP   = 18\n  SIGCONT   = 19\n  SIGCHLD   = 20\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 23\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 30\n  SIGUSR2   = 31\n  SIGEMT    =  7\n  SIGINFO   = 29\n  SIGWINCH  = 28\n\n  SIGSTKSZ = 131072\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  alias SigsetT = UInt32\n\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  SA_ONSTACK = 0x0001\n  SA_RESTART = 0x0002\n  SA_SIGINFO = 0x0040\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_pid : PidT\n    si_uid : UidT\n    si_status : Int\n    si_addr : Void*\n    _pad : StaticArray(SizeT, 9)\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/stdarg.cr",
    "content": "lib LibC\n  alias VaList = Char*\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath = \"realpath$DARWIN_EXTSN\"(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/event.cr",
    "content": "require \"../time\"\n\nlib LibC\n  EVFILT_READ  =  -1_i16\n  EVFILT_WRITE =  -2_i16\n  EVFILT_TIMER =  -7_i16\n  EVFILT_USER  = -10_i16\n\n  EV_ADD     = 0x0001_u16\n  EV_DELETE  = 0x0002_u16\n  EV_ENABLE  = 0x0004_u16\n  EV_ONESHOT = 0x0010_u16\n  EV_CLEAR   = 0x0020_u16\n  EV_EOF     = 0x8000_u16\n  EV_ERROR   = 0x4000_u16\n\n  NOTE_NSECONDS = 0x00000004_u32\n  NOTE_TRIGGER  = 0x01000000_u32\n\n  struct Kevent\n    ident : SizeT # UintptrT\n    filter : Int16\n    flags : UInt16\n    fflags : UInt32\n    data : SSizeT # IntptrT\n    udata : Void*\n  end\n\n  fun kqueue : Int\n  fun kevent(kq : Int, changelist : Kevent*, nchanges : Int, eventlist : Kevent*, nevents : Int, timeout : Timespec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =   0x04\n  PROT_NONE             =   0x00\n  PROT_READ             =   0x01\n  PROT_WRITE            =   0x02\n  MAP_FIXED             = 0x0010\n  MAP_PRIVATE           = 0x0002\n  MAP_SHARED            = 0x0001\n  MAP_ANON              = 0x1000\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   = 4\n  POSIX_MADV_NORMAL     = 0\n  POSIX_MADV_RANDOM     = 1\n  POSIX_MADV_SEQUENTIAL = 2\n  POSIX_MADV_WILLNEED   = 3\n  MADV_DONTNEED         = LibC::POSIX_MADV_DONTNEED\n  MADV_NORMAL           = LibC::POSIX_MADV_NORMAL\n  MADV_RANDOM           = LibC::POSIX_MADV_RANDOM\n  MADV_SEQUENTIAL       = LibC::POSIX_MADV_SEQUENTIAL\n  MADV_WILLNEED         = LibC::POSIX_MADV_WILLNEED\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/resource.cr",
    "content": "lib LibC\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 8\n\n  fun getrlimit(Int, Rlimit*) : Int\n\n  RLIMIT_STACK = 3\n\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  struct FdSet\n    fds_bits : StaticArray(Int, 32)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/socket.cr",
    "content": "require \"./types\"\n\nlib LibC\n  SOCK_DGRAM     =      2\n  SOCK_RAW       =      3\n  SOCK_SEQPACKET =      5\n  SOCK_STREAM    =      1\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x0200\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =  2\n  AF_INET6       = 30\n  AF_UNIX        =  1\n  AF_UNSPEC      =  0\n  AF_LOCAL       = LibC::AF_UNIX\n  SHUT_RD        = 0\n  SHUT_RDWR      = 2\n  SHUT_WR        = 1\n\n  alias SocklenT = UInt\n  alias SaFamilyT = Char\n\n  struct Sockaddr\n    sa_len : SaFamilyT\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_len : SaFamilyT\n    ss_family : SaFamilyT\n    __ss_pad1 : StaticArray(Char, 6)\n    __ss_align : LongLong\n    __ss_pad2 : StaticArray(Char, 112)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  = 0o000400\n  S_IWUSR  = 0o000200\n  S_IXUSR  = 0o000100\n  S_IRWXU  = 0o000700\n  S_IRGRP  = 0o000040\n  S_IWGRP  = 0o000020\n  S_IXGRP  = 0o000010\n  S_IRWXG  = 0o000070\n  S_IROTH  = 0o000004\n  S_IWOTH  = 0o000002\n  S_IXOTH  = 0o000001\n  S_IRWXO  = 0o000007\n  S_ISUID  = 0o004000\n  S_ISGID  = 0o002000\n  S_ISVTX  = 0o001000\n\n  struct Stat\n    st_dev : DevT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_ino : InoT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    st_atimespec : Timespec\n    st_mtimespec : Timespec\n    st_ctimespec : Timespec\n    st_birthtimespec : Timespec\n    st_size : OffT\n    st_blocks : BlkcntT\n    st_blksize : BlksizeT\n    st_flags : UInt\n    st_gen : UInt\n    st_lspare : Int\n    st_qspare : StaticArray(LongLong, 2)\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat = fstat64(x0 : Int, x1 : Stat*) : Int\n  fun lstat = lstat64(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat = stat64(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = LongLong\n  alias BlksizeT = Int\n  alias ClockT = ULong\n  alias ClockidT = Int\n  alias DevT = Int\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = UInt64\n  alias ModeT = UShort\n  alias NlinkT = UShort\n  alias OffT = LongLong\n  alias PidT = Int\n\n  struct PthreadAttrT\n    __sig : Long\n    __opaque : StaticArray(Char, 56)\n  end\n\n  struct PthreadCondT\n    __sig : Long\n    __opaque : StaticArray(Char, 40)\n  end\n\n  struct PthreadCondattrT\n    __sig : Long\n    __opaque : StaticArray(Char, 8)\n  end\n\n  struct PthreadMutexT\n    __sig : Long\n    __opaque : StaticArray(Char, 56)\n  end\n\n  struct PthreadMutexattrT\n    __sig : Long\n    __opaque : StaticArray(Char, 8)\n  end\n\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Int\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_len : Char\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 104)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 0x00000001\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =          0\n  VEOL      =          1\n  VERASE    =          3\n  VINTR     =          8\n  VKILL     =          5\n  VMIN      =         16\n  VQUIT     =          9\n  VSTART    =         12\n  VSTOP     =         13\n  VSUSP     =         10\n  BRKINT    = 0x00000002\n  ICRNL     = 0x00000100\n  IGNBRK    = 0x00000001\n  IGNCR     = 0x00000080\n  IGNPAR    = 0x00000004\n  INLCR     = 0x00000040\n  INPCK     = 0x00000010\n  ISTRIP    = 0x00000020\n  IXANY     = 0x00000800\n  IXOFF     = 0x00000400\n  IXON      = 0x00000200\n  PARMRK    = 0x00000008\n  OPOST     = 0x00000001\n  ONLCR     = 0x00000002\n  OCRNL     = 0x00000010\n  ONOCR     = 0x00000020\n  ONLRET    = 0x00000040\n  OFDEL     = 0x00020000\n  OFILL     = 0x00000080\n  CRDLY     = 0x00003000\n  CR0       = 0x00000000\n  CR1       = 0x00001000\n  CR2       = 0x00002000\n  CR3       = 0x00003000\n  TABDLY    = 0x00000c04\n  TAB0      = 0x00000000\n  TAB1      = 0x00000400\n  TAB2      = 0x00000800\n  TAB3      = 0x00000004\n  BSDLY     = 0x00008000\n  BS0       = 0x00000000\n  BS1       = 0x00008000\n  VTDLY     = 0x00010000\n  VT0       = 0x00000000\n  VT1       = 0x00010000\n  FFDLY     = 0x00004000\n  FF0       = 0x00000000\n  FF1       = 0x00004000\n  NLDLY     = 0x00000300\n  NL0       = 0x00000000\n  NL1       = 0x00000100\n  B0        =          0\n  B50       =         50\n  B75       =         75\n  B110      =        110\n  B134      =        134\n  B150      =        150\n  B200      =        200\n  B300      =        300\n  B600      =        600\n  B1200     =       1200\n  B1800     =       1800\n  B2400     =       2400\n  B4800     =       4800\n  B9600     =       9600\n  B19200    =      19200\n  B38400    =      38400\n  CSIZE     = 0x00000300\n  CS5       = 0x00000000\n  CS6       = 0x00000100\n  CS7       = 0x00000200\n  CS8       = 0x00000300\n  CSTOPB    = 0x00000400\n  CREAD     = 0x00000800\n  PARENB    = 0x00001000\n  PARODD    = 0x00002000\n  HUPCL     = 0x00004000\n  CLOCAL    = 0x00008000\n  ECHO      = 0x00000008\n  ECHOE     = 0x00000002\n  ECHOK     = 0x00000004\n  ECHONL    = 0x00000010\n  ICANON    = 0x00000100\n  IEXTEN    = 0x00000400\n  ISIG      = 0x00000080\n  NOFLSH    = 0x80000000\n  TOSTOP    = 0x00400000\n  TCSANOW   =          0\n  TCSADRAIN =          1\n  TCSAFLUSH =          2\n  TCIFLUSH  =          1\n  TCIOFLUSH =          3\n  TCOFLUSH  =          2\n  TCIOFF    =          3\n  TCION     =          4\n  TCOOFF    =          1\n  TCOON     =          2\n\n  alias CcT = Char\n  alias SpeedT = ULong\n  alias TcflagT = ULong\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : StaticArray(CcT, 20)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME      = 0\n  CLOCK_MONOTONIC_RAW = 4\n  CLOCK_MONOTONIC     = 6\n  CLOCK_UPTIME_RAW    = 8\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime(__clock_id : ClockidT, __tp : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : Char**\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-darwin/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                = 0\n  R_OK                = 1 << 2\n  W_OK                = 1 << 1\n  X_OK                = 1 << 0\n  SC_CLK_TCK          =  3\n  SC_NPROCESSORS_ONLN = 58\n  SC_PAGESIZE         = 29\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun fdatasync(x0 : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : StaticArray(Int, 2)) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  {% if ANDROID_API >= 21 %}\n    fun htons(__x : UInt16) : UInt16\n    fun ntohs(__x : UInt16) : UInt16\n  {% end %}\n  fun inet_ntop(__af : Int, __src : Void*, __dst : Char*, __size : SocklenT) : Char*\n  fun inet_pton(__af : Int, __src : Char*, __dst : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : Long\n    d_reclen : UShort\n    d_type : UChar\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(__dir : DIR*) : Int\n  fun opendir(__path : Char*) : DIR*\n  fun readdir(__dir : DIR*) : Dirent*\n  fun rewinddir(__dir : DIR*)\n  fun dirfd(__dir : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/dlfcn.cr",
    "content": "@[Link(ldflags: \"/system/lib64/libdl.so\")]\nlib LibC\n  RTLD_LAZY    = 0x00001\n  RTLD_NOW     = 0x00002\n  RTLD_GLOBAL  = 0x00100\n  RTLD_LOCAL   =       0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(__handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(__filename : Char*, __flag : Int) : Void*\n  fun dlsym(__handle : Void*, __symbol : Char*) : Void*\n  fun dladdr(__addr : Void*, __info : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16\n  alias Elf_Word = UInt32\n  alias Elf_Sword = Int32\n  alias Elf_Xword = UInt64\n  alias Elf_Sxword = Int64\n  alias Elf_Addr = UInt64\n  alias Elf_Off = UInt64\n  alias Elf_Section = UInt16\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/errno.cr",
    "content": "lib LibC\n  fun __errno : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EADV            =  68\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADE           =  52\n  EBADF           =   9\n  EBADFD          =  77\n  EBADMSG         =  74\n  EBADR           =  53\n  EBADRQC         =  56\n  EBADSLT         =  57\n  EBFONT          =  59\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECHRNG          =  44\n  ECOMM           =  70\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDEADLOCK       = LibC::EDEADLK\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDOTDOT         =  73\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTDOWN       = 112\n  EHOSTUNREACH    = 113\n  EHWPOISON       = 133\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  EISNAM          = 120\n  EKEYEXPIRED     = 127\n  EKEYREJECTED    = 129\n  EKEYREVOKED     = 128\n  EL2HLT          =  51\n  EL2NSYNC        =  45\n  EL3HLT          =  46\n  EL3RST          =  47\n  ELIBACC         =  79\n  ELIBBAD         =  80\n  ELIBEXEC        =  83\n  ELIBMAX         =  82\n  ELIBSCN         =  81\n  ELNRNG          =  48\n  ELOOP           =  40\n  EMEDIUMTYPE     = 124\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENAVAIL         = 119\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOANO          =  55\n  ENOBUFS         = 105\n  ENOCSI          =  50\n  ENODATA         =  61\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOKEY          = 126\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEDIUM       = 123\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENONET          =  64\n  ENOPKG          =  65\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSR           =  63\n  ENOSTR          =  60\n  ENOSYS          =  38\n  ENOTBLK         =  15\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTNAM         = 118\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENOTUNIQ        =  76\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPFNOSUPPORT    =  96\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EREMCHG         =  78\n  EREMOTE         =  66\n  EREMOTEIO       = 121\n  ERESTART        =  85\n  ERFKILL         = 132\n  EROFS           =  30\n  ESHUTDOWN       = 108\n  ESOCKTNOSUPPORT =  94\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESRMNT          =  69\n  ESTALE          = 116\n  ESTRPIPE        =  86\n  ETIME           =  62\n  ETIMEDOUT       = 110\n  ETOOMANYREFS    = 109\n  ETXTBSY         =  26\n  EUCLEAN         = 117\n  EUNATCH         =  49\n  EUSERS          =  87\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  EXFULL          = 54\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =    0o0200\n  O_NOFOLLOW =  0o100000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  fun fcntl(__fd : Int, __cmd : Int, ...) : Int\n  fun open(__path : Char*, __flags : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  {% if ANDROID_API >= 24 %}\n    fun getgrnam_r(__name : Char*, __group : Group*, __buf : Char*, __n : SizeT, __result : Group**) : Int\n    fun getgrgid_r(__gid : GidT, __group : Group*, __buf : Char*, __n : SizeT, __result : Group**) : Int\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  {% if ANDROID_API >= 28 %}\n    type IconvT = Void*\n\n    fun iconv(__converter : IconvT, __src_buf : Char**, __src_bytes_left : SizeT*, __dst_buf : Char**, __dst_bytes_left : SizeT*) : SizeT\n    fun iconv_close(__converter : IconvT) : Int\n    fun iconv_open(__src_encoding : Char*, __dst_encoding : Char*) : IconvT\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n\n    # These fields were added in Android R.\n    adds : ULongLong\n    subs : ULongLong\n    tls_modid : SizeT\n    tls_data : Void*\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(__callback : DlPhdrCallback, __data : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x0001\n  AI_CANONNAME   = 0x0002\n  AI_NUMERICHOST = 0x0004\n  AI_NUMERICSERV = 0x0008\n  AI_V4MAPPED    = 0x0800\n  AI_ALL         = 0x0100\n  AI_ADDRCONFIG  = 0x0400\n  EAI_AGAIN      =      2\n  EAI_BADFLAGS   =      3\n  EAI_FAIL       =      4\n  EAI_FAMILY     =      5\n  EAI_MEMORY     =      6\n  EAI_NODATA     =      7\n  EAI_NONAME     =      8\n  EAI_SERVICE    =      9\n  EAI_SOCKTYPE   =     10\n  EAI_SYSTEM     =     11\n  EAI_OVERFLOW   =     14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Sockaddr*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(__ptr : Addrinfo*)\n  fun gai_strerror(__error : Int) : Char*\n  fun getaddrinfo(__node : Char*, __service : Char*, __hints : Addrinfo*, __result : Addrinfo**) : Int\n  fun getnameinfo(__sa : Sockaddr*, __sa_length : SocklenT, __host : Char*, __host_length : SizeT, __service : Char*, __service_length : SizeT, __flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias BE16 = UInt16\n  alias BE32 = UInt32\n  alias InAddrT = UInt32\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6U\n    __u6_addr8 : UInt8[16]\n    __u6_addr16 : BE16[8]\n    __u6_addr32 : BE32[4]\n  end\n\n  struct In6Addr\n    __in6_u : In6AddrIn6U\n  end\n\n  struct SockaddrIn\n    sin_family : UShort\n    sin_port : BE16\n    sin_addr : InAddr\n    sin_zero : UChar[8] # __SOCK_SIZE__ (16) - ...\n  end\n\n  struct SockaddrIn6\n    sin6_family : UShort\n    sin6_port : BE16\n    sin6_flowinfo : BE32\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_ifindex : Int\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/pthread.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  fun pthread_attr_destroy(__attr : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(__attr : PthreadAttrT*, __addr : Void**, __size : SizeT*) : Int\n\n  fun pthread_condattr_destroy(__attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(__attr : PthreadCondattrT*) : Int\n  {% if ANDROID_API >= 21 %}\n    fun pthread_condattr_setclock(__attr : PthreadCondattrT*, __clock : ClockidT) : Int\n  {% end %}\n\n  fun pthread_cond_broadcast(__cond : PthreadCondT*) : Int\n  fun pthread_cond_destroy(__cond : PthreadCondT*) : Int\n  fun pthread_cond_init(__cond : PthreadCondT*, __attr : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(__cond : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(__cond : PthreadCondT*, __mutex : PthreadMutexT*, __timeout : Timespec*) : Int\n  fun pthread_cond_wait(__cond : PthreadCondT*, __mutex : PthreadMutexT*) : Int\n\n  fun pthread_create(__pthread_ptr : PthreadT*, __attr : PthreadAttrT*, __start_routine : Void* -> Void*, Void*) : Int\n  fun pthread_detach(__pthread : PthreadT) : Int\n  fun pthread_getattr_np(__pthread : PthreadT, __attr : PthreadAttrT*) : Int\n  fun pthread_equal(__lhs : PthreadT, __rhs : PthreadT) : Int\n  fun pthread_getspecific(__key : PthreadKeyT) : Void*\n  fun pthread_join(__pthread : PthreadT, __return_value_ptr : Void**) : Int\n  fun pthread_key_create(__key_ptr : PthreadKeyT*, __key_destructor : Void* ->) : Int\n\n  fun pthread_mutexattr_destroy(__attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(__attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(__attr : PthreadMutexattrT*, __type : Int) : Int\n\n  fun pthread_mutex_destroy(__mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_init(__mutex : PthreadMutexT*, __attr : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(__mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(__mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(__mutex : PthreadMutexT*) : Int\n\n  fun pthread_self : PthreadT\n  fun pthread_setname_np(__key : PthreadT, __name : Char*) : Int\n\n  fun pthread_setspecific(__key : PthreadKeyT, __value : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(__name : Char*, __pwd : Passwd*, __buf : Char*, __n : SizeT, __result : Passwd**) : Int\n  fun getpwuid_r(__uid : UidT, __pwd : Passwd*, __buf : Char*, __n : SizeT, __result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    =  1\n  SIGINT    =  2\n  SIGQUIT   =  3\n  SIGILL    =  4\n  SIGTRAP   =  5\n  SIGABRT   =  6\n  SIGIOT    =  6\n  SIGBUS    =  7\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGUSR1   = 10\n  SIGSEGV   = 11\n  SIGUSR2   = 12\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGSTKFLT = 16\n  SIGCHLD   = 17\n  SIGCONT   = 18\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGURG    = 23\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGPROF   = 27\n  SIGWINCH  = 28\n  SIGIO     = 29\n  SIGPOLL   = LibC::SIGIO\n  SIGPWR    = 30\n  SIGSYS    = 31\n  SIGUNUSED = 31\n\n  SIGSTKSZ = 16384\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    val : ULong[1] # (_KERNEL__NSIG / _NSIG_BPW)\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    __pad0 : Int\n    si_addr : Void*  # Assuming the segfault form of siginfo_t\n    __pad1 : Int[26] # SI_MAX_SIZE (128) / sizeof(int) - ...\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_flags : Int\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_restorer : ->\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : ULong\n  end\n\n  fun kill(__pid : PidT, __signal : Int) : Int\n  fun pthread_sigmask(__how : Int, __new_set : SigsetT*, __old_set : SigsetT*) : Int\n  fun pthread_kill(__thread : PthreadT, __sig : Int) : Int\n  fun sigaction(__signal : Int, __new_action : Sigaction*, __old_action : Sigaction*) : Int\n  fun sigaltstack(__new_signal_stack : StackT*, __old_signal_stack : StackT*) : Int\n  {% if ANDROID_API >= 21 %}\n    # TODO: defined inline for `ANDROID_API < 21`\n    fun signal(__signal : Int, __handler : SighandlerT) : SighandlerT\n    fun sigemptyset(__set : SigsetT*) : Int\n    fun sigfillset(__set : SigsetT*) : Int\n    fun sigaddset(__set : SigsetT*, __signal : Int) : Int\n    fun sigdelset(__set : SigsetT*, __signal : Int) : Int\n    fun sigismember(__set : SigsetT*, __signal : Int) : Int\n    fun sigsuspend(__mask : SigsetT*) : Int\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/stdarg.cr",
    "content": "lib LibC\n  alias VaList = Char*\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(__fmt : Char*, ...) : Int\n\n  {% if ANDROID_API >= 21 %}\n    fun dprintf(__fd : Int, __fmt : Char*, ...) : Int\n  {% end %}\n\n  fun rename(__old_path : Char*, __new_path : Char*) : Int\n  fun snprintf(__buf : Char*, __size : SizeT, __fmt : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  fun exit(__status : Int) : NoReturn\n  fun free(__ptr : Void*)\n  fun getenv(__name : Char*) : Char*\n  fun malloc(__byte_count : SizeT) : Void*\n  fun mkstemp(__template : Char*) : Int\n  fun mkstemps(__template : Char*, __flags : Int) : Int\n  fun putenv(__assignment : Char*) : Int\n  fun realloc(__ptr : Void*, __byte_count : SizeT) : Void*\n  fun realpath(__path : Char*, __resolved : Char*) : Char*\n  fun setenv(__name : Char*, __value : Char*, __overwrite : Int) : Int\n  fun strtod(__s : Char*, __end_ptr : Char**) : Double\n  {% if ANDROID_API >= 21 %}\n    # TODO: defined inline for `ANDROID_API < 21`\n    fun strtof(__s : Char*, __end_ptr : Char**) : Float\n  {% end %}\n  fun unsetenv(__name : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(__s : Void*, __ch : Int, __n : SizeT) : Void*\n  fun memcmp(__lhs : Void*, __rhs : Void*, __n : SizeT) : Int\n  fun strcmp(__lhs : Char*, __rhs : Char*) : Int\n  fun strerror(__errno_value : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(__s : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(__fd : Int, __op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/ioctl.cr",
    "content": "lib LibC\n  TCGETS  = 0x5401\n  TCSETS  = 0x5402\n  TCSETSW = 0x5403\n  TCSETSF = 0x5404\n\n  fun ioctl(__fd : Int, __request : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =  0x4\n  PROT_NONE             =  0x0\n  PROT_READ             =  0x1\n  PROT_WRITE            =  0x2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = LibC::MAP_ANONYMOUS\n  MAP_ANONYMOUS         = 0x20\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  4\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(__addr : Void*, __size : SizeT, __prot : Int, __flags : Int, __fd : Int, __offset : OffT) : Void*\n  fun mprotect(__addr : Void*, __size : SizeT, __prot : Int) : Int\n  fun munmap(__addr : Void*, __size : SizeT) : Int\n  fun madvise(__addr : Void*, __size : SizeT, __advice : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/random.cr",
    "content": "lib LibC\n  {% if ANDROID_API >= 28 %}\n    GRND_NONBLOCK = 1_u32\n\n    fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ixrss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(__who : Int, __usage : RUsage*) : Int\n\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = Long\n\n  struct FdSet\n    fds_bits : FdMask[16] # FD_SETSIZE (1024) / NFDBITS (8 * sizeof(FdMask))\n  end\n\n  fun select(__max_fd_plus_one : Int, __read_fds : FdSet*, __write_fds : FdSet*, __exception_fds : FdSet*, __timeout : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::AF_UNIX\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =         2\n  AF_INET6       =        10\n  AF_UNIX        =         1\n  AF_UNSPEC      =         0\n  AF_LOCAL       =         1\n  SHUT_RD        =         0\n  SHUT_RDWR      =         2\n  SHUT_WR        =         1\n  SOCK_CLOEXEC   = 0o2000000\n  SOCK_NONBLOCK  = 0o0004000\n  SOL_TCP        =         6\n  SOL_TLS        =       282\n\n  alias SocklenT = UInt32\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : Char[14]\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __align : Void*\n    __data : Char[112]\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : SizeT\n    msg_control : Void*\n    msg_controllen : SizeT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SizeT\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(__fd : Int, __addr : Sockaddr*, __addr_length : SocklenT*) : Int\n  fun accept4(__fd : Int, __addr : Sockaddr*, __addr_length : SocklenT*, __flags : Int) : Int\n  fun bind(__fd : Int, __addr : Sockaddr*, __addr_length : SocklenT) : Int\n  fun connect(__fd : Int, __addr : Sockaddr*, __addr_length : SocklenT) : Int\n  fun getpeername(__fd : Int, __addr : Sockaddr*, __addr_length : SocklenT*) : Int\n  fun getsockname(__fd : Int, __addr : Sockaddr*, __addr_length : SocklenT*) : Int\n  fun getsockopt(__fd : Int, __level : Int, __option : Int, __value : Void*, __value_length : SocklenT*) : Int\n  fun listen(__fd : Int, __backlog : Int) : Int\n  fun recv(__fd : Int, __buf : Void*, __n : SizeT, __flags : Int) : SSizeT\n  fun recvfrom(__fd : Int, __buf : Void*, __n : SizeT, __flags : Int, __src_addr : Sockaddr*, __src_addr_length : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(__fd : Int, __buf : Void*, __n : SizeT, __flags : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(__fd : Int, __buf : Void*, __n : SizeT, __flags : Int, __dst_addr : Sockaddr*, __dst_addr_length : SocklenT) : SSizeT\n  fun setsockopt(__fd : Int, __level : Int, __option : Int, __value : Void*, __value_length : SocklenT) : Int\n  fun shutdown(__fd : Int, __how : Int) : Int\n  fun socket(__af : Int, __type : Int, __protocol : Int) : Int\n  fun socketpair(__af : Int, __type : Int, __protocol : Int, __fds : Int[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  = 0o400 | 0o200 | 0o100\n  S_IRGRP  = S_IRUSR >> 3\n  S_IWGRP  = S_IWUSR >> 3\n  S_IXGRP  = S_IXUSR >> 3\n  S_IRWXG  = S_IRWXU >> 3\n  S_IROTH  = S_IRGRP >> 3\n  S_IWOTH  = S_IWGRP >> 3\n  S_IXOTH  = S_IXGRP >> 3\n  S_IRWXO  = S_IRWXG >> 3\n  S_ISUID  = 0o4000\n  S_ISGID  = 0o2000\n  S_ISVTX  = 0o1000\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    __pad1 : ULong\n    st_size : OffT\n    st_blksize : Int\n    __pad2 : Int\n    st_blocks : Long\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __unused4 : UInt\n    __unused5 : UInt\n  end\n\n  fun chmod(__path : Char*, __mode : ModeT) : Int\n  fun fchmod(__fd : Int, __mode : ModeT) : Int\n  fun fstat(__fd : Int, __buf : Stat*) : Int\n  fun lstat(__path : Char*, __buf : Stat*) : Int\n  fun mkdir(__path : Char*, __mode : ModeT) : Int\n  fun stat(__path : Char*, __buf : Stat*) : Int\n  fun umask(__mask : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/syscall.cr",
    "content": "lib LibC\n  SYS_getrandom = 278\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/system_properties.cr",
    "content": "lib LibC\n  {% if ANDROID_API >= 26 %}\n    alias PropInfo = Void\n\n    fun __system_property_find(__name : Char*) : PropInfo*\n    fun __system_property_read_callback(__pi : PropInfo*, __callback : (Void*, Char*, Char*, UInt32 ->), __cookie : Void*)\n  {% else %}\n    PROP_VALUE_MAX = 92\n\n    fun __system_property_get(__name : Char*, __value : Char*) : Int\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(__tv : Timeval*, __tz : Timezone*) : Int\n  fun utimes(__path : Char*, __times : Timeval[2]) : Int\n  {% if ANDROID_API >= 19 %}\n    fun futimens(__dir_fd : Int, __times : Timespec[2]) : Int\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = ULong\n  alias BlksizeT = ULong\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = UInt64\n  alias GidT = UInt\n  alias IdT = UInt32\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = UInt32\n  alias OffT = Int64\n  alias PidT = Int\n\n  struct PthreadAttrT\n    flags : UInt32\n    stack_base : Void*\n    stack_size : SizeT\n    guard_size : SizeT\n    sched_policy : Int32\n    sched_priority : Int32\n    __reserved : Char[16]\n  end\n\n  struct PthreadCondT\n    __private : Int32[12]\n  end\n\n  alias PthreadCondattrT = Long\n  alias PthreadKeyT = Int\n\n  struct PthreadMutexT\n    __private : Int32[10]\n  end\n\n  alias PthreadMutexattrT = Long\n  alias PthreadT = Long\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(__pid : PidT, __status : Int*, __options : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  BRKINT = 0o000002\n  ISTRIP = 0o000040\n  ICRNL  = 0o000400\n  IXON   = 0o002000\n\n  OPOST = 0o000001\n\n  ISIG   = 0o000001\n  ICANON = 0o000002\n  ECHO   = 0o000010\n  ECHOE  = 0o000020\n  ECHOK  = 0o000040\n  ECHONL = 0o000100\n  IEXTEN = 0o100000\n\n  TCSANOW = 0\n\n  # the following constants would have been used by the inline version of\n  # `cfmakeraw` (see the bottom of this file)\n  VTIME = 5\n  VMIN  = 6\n\n  CSIZE  = 0o000060\n  PARENB = 0o000400\n\n  CS8 = 0o000060\n\n  IGNBRK = 0o000001\n  PARMRK = 0o000010\n  INLCR  = 0o000100\n  IGNCR  = 0o000200\n\n  # the following constants are unused and solely provided for `::Termios`\n  VINTR    =        0\n  VQUIT    =        1\n  VERASE   =        2\n  VKILL    =        3\n  VEOF     =        4\n  VSWTC    =        7\n  VSTART   =        8\n  VSTOP    =        9\n  VSUSP    =       10\n  VEOL     =       11\n  VREPRINT =       12\n  VDISCARD =       13\n  VWERASE  =       14\n  VLNEXT   =       15\n  VEOL2    =       16\n  IGNPAR   = 0o000004\n  INPCK    = 0o000020\n  IUCLC    = 0o001000\n  IXANY    = 0o004000\n  IXOFF    = 0o010000\n  IMAXBEL  = 0o020000\n  IUTF8    = 0o040000\n  OLCUC    = 0o000002\n  ONLCR    = 0o000004\n  OCRNL    = 0o000010\n  ONOCR    = 0o000020\n  ONLRET   = 0o000040\n  OFILL    = 0o000100\n  OFDEL    = 0o000200\n  NLDLY    = 0o000400\n  NL0      = 0o000000\n  NL1      = 0o000400\n  CRDLY    = 0o003000\n  CR0      = 0o000000\n  CR1      = 0o001000\n  CR2      = 0o002000\n  CR3      = 0o003000\n  TABDLY   = 0o014000\n  TAB0     = 0o000000\n  TAB1     = 0o004000\n  TAB2     = 0o010000\n  TAB3     = 0o014000\n  XTABS    = 0o014000\n  BSDLY    = 0o020000\n  BS0      = 0o000000\n  BS1      = 0o020000\n  VTDLY    = 0o040000\n  VT0      = 0o000000\n  VT1      = 0o040000\n  FFDLY    = 0o100000\n  FF0      = 0o000000\n  FF1      = 0o100000\n  CBAUD    = 0o010017\n  B0       = 0o000000\n  B50      = 0o000001\n  B75      = 0o000002\n  B110     = 0o000003\n  B134     = 0o000004\n  B150     = 0o000005\n  B200     = 0o000006\n  B300     = 0o000007\n  B600     = 0o000010\n  B1200    = 0o000011\n  B1800    = 0o000012\n  B2400    = 0o000013\n  B4800    = 0o000014\n  B9600    = 0o000015\n  B19200   = 0o000016\n  B38400   = 0o000017\n\n  EXTA = LibC::B19200\n  EXTB = LibC::B38400\n\n  CS5       =      0o000000\n  CS6       =      0o000020\n  CS7       =      0o000040\n  CSTOPB    =      0o000100\n  CREAD     =      0o000200\n  PARODD    =      0o001000\n  HUPCL     =      0o002000\n  CLOCAL    =      0o004000\n  CBAUDEX   =      0o010000\n  BOTHER    =      0o010000\n  B57600    =      0o010001\n  B115200   =      0o010002\n  B230400   =      0o010003\n  B460800   =      0o010004\n  B500000   =      0o010005\n  B576000   =      0o010006\n  B921600   =      0o010007\n  B1000000  =      0o010010\n  B1152000  =      0o010011\n  B1500000  =      0o010012\n  B2000000  =      0o010013\n  B2500000  =      0o010014\n  B3000000  =      0o010015\n  B3500000  =      0o010016\n  B4000000  =      0o010017\n  CIBAUD    = 0o02003600000\n  CMSPAR    = 0o10000000000\n  CRTSCTS   = 0o20000000000\n  IBSHIFT   =            16\n  XCASE     =      0o000004\n  NOFLSH    =      0o000200\n  TOSTOP    =      0o000400\n  ECHOCTL   =      0o001000\n  ECHOPRT   =      0o002000\n  ECHOKE    =      0o004000\n  FLUSHO    =      0o010000\n  PENDIN    =      0o040000\n  EXTPROC   =      0o200000\n  TCOOFF    =             0\n  TCOON     =             1\n  TCIOFF    =             2\n  TCION     =             3\n  TCIFLUSH  =             0\n  TCOFLUSH  =             1\n  TCIOFLUSH =             2\n  TCSADRAIN =             1\n  TCSAFLUSH =             2\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 19) # cc_t[NCCS]\n  end\n\n  {% if ANDROID_API >= 28 %}\n    fun tcgetattr(__fd : Int, __t : Termios*) : Int\n    fun tcsetattr(__fd : Int, __optional_actions : Int, __t : Termios*) : Int\n    fun cfmakeraw(__t : Termios*)\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_SGI_CYCLE          = 10\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(__clock : ClockidT, __ts : Timespec*) : Int\n  fun clock_settime(__clock : ClockidT, __ts : Timespec*) : Int\n  fun gmtime_r(__t : TimeT*, __tm : Tm*) : Tm*\n  fun localtime_r(__t : TimeT*, __tm : Tm*) : Tm*\n  fun mktime(__tm : Tm*) : TimeT\n  fun nanosleep(__req : Timespec*, __rem : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(__tm : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-android/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  6\n  SC_NPROCESSORS_ONLN = 97\n  SC_PAGESIZE         = 39\n\n  fun chroot(__path : Char*) : Int\n  fun access(__path : Char*, __mode : Int) : Int\n  fun chdir(__path : Char*) : Int\n  fun chown(__path : Char*, __owner : UidT, __group : GidT) : Int\n  fun fchown(__fd : Int, __owner : UidT, __group : GidT) : Int\n  fun close(__fd : Int) : Int\n  fun dup2(__old_fd : Int, __new_fd : Int) : Int\n  fun dup3(__old_fd : Int, __new_fd : Int, __flags : Int) : Int\n  fun _exit(__status : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(__file : Char*, __argv : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(__fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(__fd : Int) : Int\n  fun ftruncate(__fd : Int, __length : OffT) : Int\n  fun getcwd(__buf : Char*, __size : SizeT) : Char*\n  fun gethostname(__buf : Char*, __buf_size : SizeT) : Int\n  fun getpgid(__pid : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(__fd : Int) : Int\n  fun ttyname_r(__fd : Int, __buf : Char*, __buf_size : SizeT) : Int\n  fun lchown(__path : Char*, __owner : UidT, __group : GidT) : Int\n  fun link(__old_path : Char*, __new_path : Char*) : Int\n  {% if ANDROID_API >= 24 %}\n    fun lockf(__fd : Int, __cmd : Int, __length : OffT) : Int\n  {% end %}\n  fun lseek(__fd : Int, __offset : OffT, __whence : Int) : OffT\n  fun pipe(__fds : Int[2]) : Int\n  fun pipe2(__fds : Int[2], __flags : Int) : Int\n  fun read(__fd : Int, __buf : Void*, __count : SizeT) : SSizeT\n  fun pread(__fd : Int, __buf : Void*, __count : SizeT, __offset : OffT) : SSizeT\n  fun rmdir(__path : Char*) : Int\n  fun symlink(__old_path : Char*, __new_path : Char*) : Int\n  fun readlink(__path : Char*, __buf : Char*, __buf_size : SizeT) : SSizeT\n  fun syscall(__number : Long, ...) : Long\n  fun sysconf(__name : Int) : Long\n  fun unlink(__path : Char*) : Int\n  fun write(__fd : Int, __buf : Void*, __count : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(hostshort : UInt16T) : UInt16T\n  fun ntohs(netshort : UInt16T) : UInt16T\n  fun inet_ntop(af : Int, cp : Void*, buf : Char*, len : SocklenT) : Char*\n  fun inet_pton(af : Int, cp : Char*, buf : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : UChar\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(dirp : DIR*) : Int\n  fun opendir(name : Char*) : DIR*\n  fun readdir(dirp : DIR*) : Dirent*\n  fun rewinddir(dirp : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/dlfcn.cr",
    "content": "{% unless flag?(:interpreted) %}\n  @[Link(\"dl\")]\n{% end %}\n\nlib LibC\n  RTLD_LAZY    = 0x00001\n  RTLD_NOW     = 0x00002\n  RTLD_GLOBAL  = 0x00100\n  RTLD_LOCAL   =       0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(file : Char*, mode : Int) : Void*\n  fun dlsym(handle : Void*, name : Char*) : Void*\n  fun dladdr(address : Void*, info : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o100000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(fd : Int, cmd : Int, ...) : Int\n  fun open(file : Char*, oflag : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close(cd : IconvT) : Int\n  fun iconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n    adds : ULongLong\n    subs : ULongLong\n    tls_modid : SizeT\n    tls_data : Void*\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x0001\n  AI_CANONNAME   = 0x0002\n  AI_NUMERICHOST = 0x0004\n  AI_NUMERICSERV = 0x0400\n  AI_V4MAPPED    = 0x0008\n  AI_ALL         = 0x0010\n  AI_ADDRCONFIG  = 0x0020\n  EAI_AGAIN      =     -3\n  EAI_BADFLAGS   =     -1\n  EAI_FAIL       =     -4\n  EAI_NODATA     =     -5\n  EAI_FAMILY     =     -6\n  EAI_MEMORY     =    -10\n  EAI_NONAME     =     -2\n  EAI_SERVICE    =     -8\n  EAI_SOCKTYPE   =     -7\n  EAI_SYSTEM     =    -11\n  EAI_OVERFLOW   =    -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(ai : Addrinfo*) : Void\n  fun gai_strerror(ecode : Int) : Char*\n  fun getaddrinfo(name : Char*, service : Char*, req : Addrinfo*, pai : Addrinfo**) : Int\n  fun getnameinfo(sa : Sockaddr*, salen : SocklenT, host : Char*, hostlen : SocklenT, serv : Char*, servlen : SocklenT, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6U\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_u : In6AddrIn6U\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n# Starting with glibc 2.34, `pthread` is integrated into `libc` and may not even\n# be available as a separate shared library.\n# There's always a static library for compiled mode, but `Crystal::Loader` does not support\n# static libraries. So we just skip `pthread` entirely in interpreted mode.\n# The symbols are still available in the interpreter because they are loaded in the compiler.\n{% unless flag?(:interpreted) %}\n  @[Link(\"pthread\")]\n{% end %}\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(attr : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int\n  fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int\n  fun pthread_cond_broadcast(cond : PthreadCondT*) : Int\n  fun pthread_cond_destroy(cond : PthreadCondT*) : Int\n  fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(cond : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int\n  fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int\n  fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int\n  fun pthread_detach(th : PthreadT) : Int\n  fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int\n  fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int\n  fun pthread_join(th : PthreadT, thread_return : Void**) : Int\n  fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int\n  fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setname_np(PthreadT, Char*) : Int\n\n  # Set cancellability state of current thread to STATE, returning old\n  # state in *OLDSTATE if OLDSTATE is not NULL.\n  fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    =  1\n  SIGINT    =  2\n  SIGQUIT   =  3\n  SIGILL    =  4\n  SIGTRAP   =  5\n  SIGIOT    =  6\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGCLD    = LibC::SIGCHLD\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = 31\n\n  SIGSTKSZ = 16384\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    val : ULong[16] # (1024 / (8 * sizeof(ULong)))\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    __pad0 : Int\n    si_addr : Void*               # Assuming the segfault form of siginfo_t\n    __pad1 : StaticArray(Int, 26) # __SI_PAD_SIZE (28) - sizeof(void*) / sizeof(int) = 26\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : ->\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(pid : PidT, sig : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(sig : Int, handler : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/stdarg.cr",
    "content": "lib LibC\n  # based on https://github.com/llvm/llvm-project/blob/bf1cdc2c6c0460b7121ac653c796ef4995b1dfa9/clang/lib/AST/ASTContext.cpp#L7678-L7739\n  struct VaList\n    __stack : Void*\n    __gr_top : Void*\n    __vr_top : Void*\n    __gr_offs : Int32\n    __vr_offs : Int32\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(format : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(old : Char*, new : Char*) : Int\n  fun snprintf(s : Char*, maxlen : SizeT, format : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(nptr : Char*) : Double\n  fun div(numer : Int, denom : Int) : DivT\n  fun exit(status : Int) : NoReturn\n  fun free(ptr : Void*) : Void\n  fun getenv(name : Char*) : Char*\n  fun malloc(size : SizeT) : Void*\n  fun mkstemp(template : Char*) : Int\n  fun mkstemps(template : Char*, suffixlen : Int) : Int\n  fun putenv(string : Char*) : Int\n  fun realloc(ptr : Void*, size : SizeT) : Void*\n  fun realpath(name : Char*, resolved : Char*) : Char*\n  fun setenv(name : Char*, value : Char*, replace : Int) : Int\n  fun strtod(nptr : Char*, endptr : Char**) : Double\n  fun strtof(nptr : Char*, endptr : Char**) : Float\n  fun strtol(nptr : Char*, endptr : Char**, base : Int) : Long\n  fun unsetenv(name : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(s1 : Void*, s2 : Void*, n : SizeT) : Int\n  fun strcmp(s1 : Char*, s2 : Char*) : Int\n  fun strerror(errnum : Int) : Char*\n  fun strerror_r = __xpg_strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(s : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =  0x4\n  PROT_NONE             =  0x0\n  PROT_READ             =  0x1\n  PROT_WRITE            =  0x2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = LibC::MAP_ANONYMOUS\n  MAP_ANONYMOUS         = 0x20\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  4\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(addr : Void*, len : SizeT, prot : Int, flags : Int, fd : Int, offset : OffT) : Void*\n  fun mprotect(addr : Void*, len : SizeT, prot : Int) : Int\n  fun munmap(addr : Void*, len : SizeT) : Int\n  fun madvise(addr : Void*, len : SizeT, advice : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int\n\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = Long\n\n  struct FdSet\n    fds_bits : StaticArray(FdMask, 16)\n  end\n\n  fun select(nfds : Int, readfds : FdSet*, writefds : FdSet*, exceptfds : FdSet*, timeout : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::PF_UNIX\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =      0\n  SHUT_RDWR      =      2\n  SHUT_WR        =      1\n  SOCK_CLOEXEC   = 524288\n  SOCK_NONBLOCK  =   2048\n  SOL_TCP        =      6\n  SOL_TLS        =    282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_padding : StaticArray(Char, 118)\n    __ss_align : ULong\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : SizeT\n    msg_control : Void*\n    msg_controllen : SizeT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SizeT\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(fd : Int, addr : Sockaddr*, addr_len : SocklenT*) : Int\n  fun accept4(fd : Int, addr : Sockaddr*, addr_len : SocklenT*, flags : Int) : Int\n  fun bind(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun connect(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun getpeername(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockname(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT*) : Int\n  fun listen(fd : Int, n : Int) : Int\n  fun recv(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun recvfrom(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT) : SSizeT\n  fun setsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT) : Int\n  fun shutdown(fd : Int, how : Int) : Int\n  fun socket(domain : Int, type : Int, protocol : Int) : Int\n  fun socketpair(domain : Int, type : Int, protocol : Int, fds : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  = 0o400 | 0o200 | 0o100\n  S_IRGRP  = S_IRUSR >> 3\n  S_IWGRP  = S_IWUSR >> 3\n  S_IXGRP  = S_IXUSR >> 3\n  S_IRWXG  = S_IRWXU >> 3\n  S_IROTH  = S_IRGRP >> 3\n  S_IWOTH  = S_IWGRP >> 3\n  S_IXOTH  = S_IXGRP >> 3\n  S_IRWXO  = S_IRWXG >> 3\n  S_ISUID  = 0o4000\n  S_ISGID  = 0o2000\n  S_ISVTX  = 0o1000\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    __pad1 : DevT\n    st_size : OffT\n    st_blksize : BlksizeT\n    __pad2 : Int\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __glibc_reserved : StaticArray(Int, 2)\n  end\n\n  fun chmod(file : Char*, mode : ModeT) : Int\n  fun fchmod(fd : Int, mode : ModeT) : Int\n  fun fstat(fd : Int, buf : Stat*) : Int\n  fun lstat(file : Char*, buf : Stat*) : Int\n  fun mkdir(path : Char*, mode : ModeT) : Int\n  fun mkfifo(path : Char*, mode : ModeT) : Int\n  fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int\n  fun stat(file : Char*, buf : Stat*) : Int\n  fun umask(mask : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(tv : Timeval*, tz : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Int\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = Long\n  alias PidT = Int\n\n  union PthreadAttrT\n    __size : StaticArray(Char, 64)\n    __align : Long\n  end\n\n  union PthreadCondT\n    __size : StaticArray(Char, 48)\n    __align : LongLong\n  end\n\n  union PthreadCondattrT\n    __size : StaticArray(Char, 8)\n    __align : Int\n  end\n\n  union PthreadMutexT\n    __size : StaticArray(Char, 48)\n    __align : Long\n  end\n\n  union PthreadMutexattrT\n    __size : StaticArray(Char, 8)\n    __align : Int\n  end\n\n  alias PthreadT = ULong\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(pid : PidT, stat_loc : Int*, options : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(fd : Int, termios_p : Termios*) : Int\n  fun tcsetattr(fd : Int, optional_actions : Int, termios_p : Termios*) : Int\n  fun cfmakeraw(termios_p : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun clock_settime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun gmtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun localtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun mktime(tp : Tm*) : TimeT\n  fun nanosleep(req : Timespec*, rem : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(tp : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-gnu/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(name : Char*, type : Int) : Int\n  fun chdir(path : Char*) : Int\n  fun chown(file : Char*, owner : UidT, group : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(fd : Int) : Int\n  fun dup2(fd : Int, fd2 : Int) : Int\n  fun dup3(fd : Int, fd2 : Int, flags : Int) : Int\n  fun _exit(status : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(file : Char*, argv : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(fd : Int, length : OffT) : Int\n  fun getcwd(buf : Char*, size : SizeT) : Char*\n  fun gethostname(name : Char*, len : SizeT) : Int\n  fun getpgid(pid : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(fd : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(file : Char*, owner : UidT, group : GidT) : Int\n  fun link(from : Char*, to : Char*) : Int\n  fun lockf(fd : Int, cmd : Int, len : OffT) : Int\n  fun lseek(fd : Int, offset : OffT, whence : Int) : OffT\n  fun pipe(pipedes : StaticArray(Int, 2)) : Int\n  fun pipe2(pipedes : StaticArray(Int, 2), flags : Int) : Int\n  fun read(fd : Int, buf : Void*, nbytes : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(path : Char*) : Int\n  fun symlink(from : Char*, to : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(sysno : Long, ...) : Long\n  fun sysconf(name : Int) : Long\n  fun unlink(name : Char*) : Int\n  fun write(fd : Int, buf : Void*, n : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : Char\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =   1\n  RTLD_NOW     =   2\n  RTLD_GLOBAL  = 256\n  RTLD_LOCAL   =   0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o100000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     =  0x01\n  AI_CANONNAME   =  0x02\n  AI_NUMERICHOST =  0x04\n  AI_NUMERICSERV = 0x400\n  AI_V4MAPPED    =  0x08\n  AI_ALL         =  0x10\n  AI_ADDRCONFIG  =  0x20\n  EAI_AGAIN      =    -3\n  EAI_BADFLAGS   =    -1\n  EAI_FAIL       =    -4\n  EAI_NODATA     =    -5\n  EAI_FAMILY     =    -6\n  EAI_MEMORY     =   -10\n  EAI_NONAME     =    -2\n  EAI_SERVICE    =    -8\n  EAI_SOCKTYPE   =    -7\n  EAI_SYSTEM     =   -11\n  EAI_OVERFLOW   =   -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Sockaddr*, x1 : SocklenT, x2 : Char*, x3 : SocklenT, x4 : Char*, x5 : SocklenT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6Union\n    __s6_addr : StaticArray(UInt8T, 16)\n    __s6_addr16 : StaticArray(UInt16T, 8)\n    __s6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_union : In6AddrIn6Union\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(UInt8T, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/pthread.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_getattr_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setname_np(PthreadT, Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = LibC::SIGSYS\n\n  SIGSTKSZ = 12288\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    val : ULong[16] # 128 / sizeof(long)\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    __pad0 : Int\n    si_addr : Void*               # Assuming the segfault form of siginfo_t\n    __pad1 : StaticArray(Int, 20) # __SI_PAD_SIZE (28) - sizeof(void*) (8) = 20\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : Void*\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/stdarg.cr",
    "content": "lib LibC\n  # based on https://github.com/llvm/llvm-project/blob/bf1cdc2c6c0460b7121ac653c796ef4995b1dfa9/clang/lib/AST/ASTContext.cpp#L7678-L7739\n  struct VaList\n    __stack : Void*\n    __gr_top : Void*\n    __vr_top : Void*\n    __gr_offs : Int32\n    __vr_offs : Int32\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =    4\n  PROT_NONE             =    0\n  PROT_READ             =    1\n  PROT_WRITE            =    2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = 0x20\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  0\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/resource.cr",
    "content": "lib LibC\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(Int, Rlimit*) : Int\n\n  RLIMIT_STACK = 3\n\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = ULong\n\n  struct FdSet\n    fds_bits : StaticArray(ULong, 16)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::AF_LOCAL\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =         0\n  SHUT_RDWR      =         2\n  SHUT_WR        =         1\n  SOCK_CLOEXEC   = 0o2000000\n  SOCK_NONBLOCK  = 0o0004000\n  SOL_TCP        =         6\n  SOL_TLS        =       282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_align : ULong\n    __ss_padding : StaticArray(Char, 112)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : Int\n    __pad1 : Int\n    msg_control : Void*\n    msg_controllen : SocklenT\n    __pad2 : Int\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SocklenT\n    __pad1 : Int\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  =    0o700\n  S_IRGRP  =    0o040\n  S_IWGRP  =    0o020\n  S_IXGRP  =    0o010\n  S_IRWXG  =    0o070\n  S_IROTH  =    0o004\n  S_IWOTH  =    0o002\n  S_IXOTH  =    0o001\n  S_IRWXO  =    0o007\n  S_ISUID  =   0o4000\n  S_ISGID  =   0o2000\n  S_ISVTX  =   0o1000\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    __pad : ULong\n    st_size : OffT\n    st_blksize : BlksizeT\n    __pad2 : Int\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __unused : StaticArray(UInt, 2)\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Int\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = Long\n  alias PidT = Int\n\n  union PthreadAttrTU\n    __i : StaticArray(Int, 14)\n    __vi : StaticArray(Int, 14)\n    __s : StaticArray(ULong, 7)\n  end\n\n  struct PthreadAttrT\n    __u : PthreadAttrTU\n  end\n\n  union PthreadCondTU\n    __i : StaticArray(Int, 12)\n    __vi : StaticArray(Int, 12)\n    __p : StaticArray(Void*, 6)\n  end\n\n  struct PthreadCondT\n    __u : PthreadCondTU\n  end\n\n  struct PthreadCondattrT\n    __attr : UInt\n  end\n\n  union PthreadMutexTU\n    __i : StaticArray(Int, 10)\n    __vi : StaticArray(Int, 10)\n    __p : StaticArray(Void*, 5)\n  end\n\n  struct PthreadMutexT\n    __u : PthreadMutexTU\n  end\n\n  struct PthreadMutexattrT\n    __attr : UInt\n  end\n\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    __c_ispeed : SpeedT\n    __c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_SGI_CYCLE          = 10\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/aarch64-linux-musl/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : StaticArray(Int, 2)) : Int\n  fun pipe2(x0 : StaticArray(Int, 2), x1 : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(x0 : Long, ...) : Long\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(hostshort : UInt16T) : UInt16T\n  fun ntohs(netshort : UInt16T) : UInt16T\n  fun inet_ntop(af : Int, cp : Void*, buf : Char*, len : SocklenT) : Char*\n  fun inet_pton(af : Int, cp : Char*, buf : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : Long\n    d_reclen : UShort\n    d_type : Char\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(dirp : DIR*) : Int\n  fun opendir(name : Char*) : DIR*\n  fun readdir(dirp : DIR*) : Dirent*\n  fun rewinddir(dirp : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/dlfcn.cr",
    "content": "{% unless flag?(:interpreted) %}\n  @[Link(\"dl\")]\n{% end %}\n\nlib LibC\n  RTLD_LAZY    = 0x00001\n  RTLD_NOW     = 0x00002\n  RTLD_GLOBAL  = 0x00100\n  RTLD_LOCAL   =       0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(file : Char*, mode : Int) : Void*\n  fun dlsym(handle : Void*, name : Char*) : Void*\n  fun dladdr(address : Void*, info : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt32T\n  alias Elf_Off = UInt32T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word   # Segment type\n    offset : Elf_Off  # Segment file offset\n    vaddr : Elf_Addr  # Segment virtual address\n    paddr : Elf_Addr  # Segment physical address\n    filesz : Elf_Word # Segment size in file\n    memsz : Elf_Word  # Segment size in memory\n    flags : Elf_Word  # Segment flags\n    align : Elf_Word  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o100000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(fd : Int, cmd : Int, ...) : Int\n  fun open(file : Char*, oflag : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close(cd : IconvT) : Int\n  fun iconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x0001\n  AI_CANONNAME   = 0x0002\n  AI_NUMERICHOST = 0x0004\n  AI_NUMERICSERV = 0x0400\n  AI_V4MAPPED    = 0x0008\n  AI_ALL         = 0x0010\n  AI_ADDRCONFIG  = 0x0020\n  EAI_AGAIN      =     -3\n  EAI_BADFLAGS   =     -1\n  EAI_FAIL       =     -4\n  EAI_NODATA     =     -5\n  EAI_FAMILY     =     -6\n  EAI_MEMORY     =    -10\n  EAI_NONAME     =     -2\n  EAI_SERVICE    =     -8\n  EAI_SOCKTYPE   =     -7\n  EAI_SYSTEM     =    -11\n  EAI_OVERFLOW   =    -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(ai : Addrinfo*) : Void\n  fun gai_strerror(ecode : Int) : Char*\n  fun getaddrinfo(name : Char*, service : Char*, req : Addrinfo*, pai : Addrinfo**) : Int\n  fun getnameinfo(sa : Sockaddr*, salen : SocklenT, host : Char*, hostlen : SocklenT, serv : Char*, servlen : SocklenT, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6U\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_u : In6AddrIn6U\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n# Starting with glibc 2.34, `pthread` is integrated into `libc` and may not even\n# be available as a separate shared library.\n# There's always a static library for compiled mode, but `Crystal::Loader` does not support\n# static libraries. So we just skip `pthread` entirely in interpreted mode.\n# The symbols are still available in the interpreter because they are loaded in the compiler.\n{% unless flag?(:interpreted) %}\n  @[Link(\"pthread\")]\n{% end %}\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(attr : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int\n  fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int\n  fun pthread_cond_broadcast(cond : PthreadCondT*) : Int\n  fun pthread_cond_destroy(cond : PthreadCondT*) : Int\n  fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(cond : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int\n  fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int\n  fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int\n  fun pthread_detach(th : PthreadT) : Int\n  fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int\n  fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int\n  fun pthread_join(th : PthreadT, thread_return : Void**) : Int\n  fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int\n  fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setname_np(PthreadT, Char*) : Int\n\n  # Set cancellability state of current thread to STATE, returning old\n  # state in *OLDSTATE if OLDSTATE is not NULL.\n  fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    =  1\n  SIGINT    =  2\n  SIGQUIT   =  3\n  SIGILL    =  4\n  SIGTRAP   =  5\n  SIGIOT    =  6\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGCLD    = LibC::SIGCHLD\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = 31\n\n  SIGSTKSZ = 8192\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    val : ULong[32] # (1024 / (8 * sizeof(long)))\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_addr : Void*              # Assuming the segfault form of siginfo_t\n    __pad : StaticArray(Int, 25) # __SI_PAD_SIZE (29) - sizeof(void*) (4) = 25\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : Void*\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(pid : PidT, sig : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(sig : Int, handler : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/stdarg.cr",
    "content": "lib LibC\n  type VaList = Void*\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(format : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(old : Char*, new : Char*) : Int\n  fun snprintf(s : Char*, maxlen : SizeT, format : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(nptr : Char*) : Double\n  fun div(numer : Int, denom : Int) : DivT\n  fun exit(status : Int) : NoReturn\n  fun free(ptr : Void*) : Void\n  fun getenv(name : Char*) : Char*\n  fun malloc(size : SizeT) : Void*\n  fun mkstemp(template : Char*) : Int\n  fun mkstemps(template : Char*, suffixlen : Int) : Int\n  fun putenv(string : Char*) : Int\n  fun realloc(ptr : Void*, size : SizeT) : Void*\n  fun realpath(name : Char*, resolved : Char*) : Char*\n  fun setenv(name : Char*, value : Char*, replace : Int) : Int\n  fun strtod(nptr : Char*, endptr : Char**) : Double\n  fun strtof(nptr : Char*, endptr : Char**) : Float\n  fun strtol(nptr : Char*, endptr : Char**, base : Int) : Long\n  fun unsetenv(name : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(s1 : Void*, s2 : Void*, n : SizeT) : Int\n  fun strcmp(s1 : Char*, s2 : Char*) : Int\n  fun strerror(errnum : Int) : Char*\n  fun strerror_r = __xpg_strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(s : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =  0x4\n  PROT_NONE             =  0x0\n  PROT_READ             =  0x1\n  PROT_WRITE            =  0x2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = LibC::MAP_ANONYMOUS\n  MAP_ANONYMOUS         = 0x20\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  4\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(addr : Void*, len : SizeT, prot : Int, flags : Int, fd : Int, offset : OffT) : Void*\n  fun mprotect(addr : Void*, len : SizeT, prot : Int) : Int\n  fun munmap(addr : Void*, len : SizeT) : Int\n  fun madvise(addr : Void*, len : SizeT, advice : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\n\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = Long\n\n  struct FdSet\n    fds_bits : StaticArray(FdMask, 32)\n  end\n\n  fun select(nfds : Int, readfds : FdSet*, writefds : FdSet*, exceptfds : FdSet*, timeout : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::PF_UNIX\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =      0\n  SHUT_RDWR      =      2\n  SHUT_WR        =      1\n  SOCK_CLOEXEC   = 524288\n  SOCK_NONBLOCK  =   2048\n  SOL_TCP        =      6\n  SOL_TLS        =    282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_align : ULong\n    __ss_padding : StaticArray(Char, 120)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : SizeT\n    msg_control : Void*\n    msg_controllen : SizeT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SizeT\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(fd : Int, addr : Sockaddr*, addr_len : SocklenT*) : Int\n  fun accept4(fd : Int, addr : Sockaddr*, addr_len : SocklenT*, flags : Int) : Int\n  fun bind(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun connect(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun getpeername(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockname(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT*) : Int\n  fun listen(fd : Int, n : Int) : Int\n  fun recv(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun recvfrom(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT) : SSizeT\n  fun setsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT) : Int\n  fun shutdown(fd : Int, how : Int) : Int\n  fun socket(domain : Int, type : Int, protocol : Int) : Int\n  fun socketpair(domain : Int, type : Int, protocol : Int, fds : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  = 0o400 | 0o200 | 0o100\n  S_IRGRP  = S_IRUSR >> 3\n  S_IWGRP  = S_IWUSR >> 3\n  S_IXGRP  = S_IXUSR >> 3\n  S_IRWXG  = S_IRWXU >> 3\n  S_IROTH  = S_IRGRP >> 3\n  S_IWOTH  = S_IWGRP >> 3\n  S_IXOTH  = S_IXGRP >> 3\n  S_IRWXO  = S_IRWXG >> 3\n  S_ISUID  = 0o4000\n  S_ISGID  = 0o2000\n  S_ISVTX  = 0o1000\n\n  struct Stat\n    st_dev : DevT\n    __pad1 : UShort\n    st_ino : InoT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    __pad2 : UShort\n    st_size : OffT\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __glibc_reserved4 : ULong\n    __glibc_reserved5 : ULong\n  end\n\n  fun chmod(file : Char*, mode : ModeT) : Int\n  fun fchmod(fd : Int, mode : ModeT) : Int\n  fun fstat(fd : Int, buf : Stat*) : Int\n  fun lstat(file : Char*, buf : Stat*) : Int\n  fun mkdir(path : Char*, mode : ModeT) : Int\n  fun mkfifo(path : Char*, mode : ModeT) : Int\n  fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int\n  fun stat(file : Char*, buf : Stat*) : Int\n  fun umask(mask : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(tv : Timeval*, tz : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Long\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULongLong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = Long\n  alias PidT = Int\n\n  union PthreadAttrT\n    __size : StaticArray(Char, 36)\n    __align : Long\n  end\n\n  struct PthreadCondTData\n    __lock : Int\n    __futex : UInt\n    __total_seq : ULongLong\n    __wakeup_seq : ULongLong\n    __woken_seq : ULongLong\n    __mutex : Void*\n    __nwaiters : UInt\n    __broadcast_seq : UInt\n  end\n\n  union PthreadCondT\n    __data : PthreadCondTData\n    __size : StaticArray(Char, 48)\n    __align : LongLong\n  end\n\n  union PthreadCondattrT\n    __size : StaticArray(Char, 4)\n    __align : Long\n  end\n\n  union PthreadMutexT\n    __data : Void*\n    __size : StaticArray(Char, 24)\n    __align : Long\n  end\n\n  union PthreadMutexattrT\n    __size : StaticArray(Char, 4)\n    __align : Long\n  end\n\n  alias PthreadT = ULong\n  alias SSizeT = Int\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(pid : PidT, stat_loc : Int*, options : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(fd : Int, termios_p : Termios*) : Int\n  fun tcsetattr(fd : Int, optional_actions : Int, termios_p : Termios*) : Int\n  fun cfmakeraw(termios_p : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun clock_settime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun gmtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun localtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun mktime(tp : Tm*) : TimeT\n  fun nanosleep(req : Timespec*, rem : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(tp : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/arm-linux-gnueabihf/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 58\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(name : Char*, type : Int) : Int\n  fun chdir(path : Char*) : Int\n  fun chown(file : Char*, owner : UidT, group : GidT) : Int\n  fun fchown(fd : Int, owner : UidT, group : GidT) : Int\n  fun close(fd : Int) : Int\n  fun dup2(fd : Int, fd2 : Int) : Int\n  fun dup3(fd : Int, fd2 : Int, flags : Int) : Int\n  fun _exit(status : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(file : Char*, argv : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(fd : Int, length : OffT) : Int\n  fun getcwd(buf : Char*, size : SizeT) : Char*\n  fun gethostname(name : Char*, len : SizeT) : Int\n  fun getpgid(pid : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(fd : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(file : Char*, owner : UidT, group : GidT) : Int\n  fun link(from : Char*, to : Char*) : Int\n  fun lockf(fd : Int, cmd : Int, len : OffT) : Int\n  fun lseek(fd : Int, offset : OffT, whence : Int) : OffT\n  fun pipe(pipedes : StaticArray(Int, 2)) : Int\n  fun pipe2(pipedes : StaticArray(Int, 2), flags : Int) : Int\n  fun read(fd : Int, buf : Void*, nbytes : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(path : Char*) : Int\n  fun symlink(from : Char*, to : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(sysno : Long, ...) : Long\n  fun sysconf(name : Int) : Long\n  fun unlink(name : Char*) : Int\n  fun write(fd : Int, buf : Void*, n : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(hostshort : UInt16T) : UInt16T\n  fun ntohs(netshort : UInt16T) : UInt16T\n  fun inet_ntop(af : Int, cp : Void*, buf : Char*, len : SocklenT) : Char*\n  fun inet_pton(af : Int, cp : Char*, buf : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : UChar\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(dirp : DIR*) : Int\n  fun opendir(name : Char*) : DIR*\n  fun readdir = readdir64(dirp : DIR*) : Dirent*\n  fun rewinddir(dirp : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/dlfcn.cr",
    "content": "{% unless flag?(:interpreted) %}\n  @[Link(\"dl\")]\n{% end %}\n\nlib LibC\n  RTLD_LAZY    = 0x00001\n  RTLD_NOW     = 0x00002\n  RTLD_GLOBAL  = 0x00100\n  RTLD_LOCAL   =       0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(file : Char*, mode : Int) : Void*\n  fun dlsym(handle : Void*, name : Char*) : Void*\n  fun dladdr(address : Void*, info : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt32T\n  alias Elf_Off = UInt32T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word   # Segment type\n    offset : Elf_Off  # Segment file offset\n    vaddr : Elf_Addr  # Segment virtual address\n    paddr : Elf_Addr  # Segment physical address\n    filesz : Elf_Word # Segment size in file\n    memsz : Elf_Word  # Segment size in memory\n    flags : Elf_Word  # Segment flags\n    align : Elf_Word  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o400000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(fd : Int, cmd : Int, ...) : Int\n  fun open = open64(file : Char*, oflag : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close(cd : IconvT) : Int\n  fun iconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n    adds : ULongLong\n    subs : ULongLong\n    tls_modid : SizeT\n    tls_data : Void*\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x0001\n  AI_CANONNAME   = 0x0002\n  AI_NUMERICHOST = 0x0004\n  AI_NUMERICSERV = 0x0400\n  AI_V4MAPPED    = 0x0008\n  AI_ALL         = 0x0010\n  AI_ADDRCONFIG  = 0x0020\n  EAI_AGAIN      =     -3\n  EAI_BADFLAGS   =     -1\n  EAI_NODATA     =     -5\n  EAI_FAIL       =     -4\n  EAI_FAMILY     =     -6\n  EAI_MEMORY     =    -10\n  EAI_NONAME     =     -2\n  EAI_SERVICE    =     -8\n  EAI_SOCKTYPE   =     -7\n  EAI_SYSTEM     =    -11\n  EAI_OVERFLOW   =    -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(ai : Addrinfo*) : Void\n  fun gai_strerror(ecode : Int) : Char*\n  fun getaddrinfo(name : Char*, service : Char*, req : Addrinfo*, pai : Addrinfo**) : Int\n  fun getnameinfo(sa : Sockaddr*, salen : SocklenT, host : Char*, hostlen : SocklenT, serv : Char*, servlen : SocklenT, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6U\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_u : In6AddrIn6U\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n# Starting with glibc 2.34, `pthread` is integrated into `libc` and may not even\n# be available as a separate shared library.\n# There's always a static library for compiled mode, but `Crystal::Loader` does not support\n# static libraries. So we just skip `pthread` entirely in interpreted mode.\n# The symbols are still available in the interpreter because they are loaded in the compiler.\n{% unless flag?(:interpreted) %}\n  @[Link(\"pthread\")]\n{% end %}\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(attr : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int\n  fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int\n  fun pthread_cond_broadcast(cond : PthreadCondT*) : Int\n  fun pthread_cond_destroy(cond : PthreadCondT*) : Int\n  fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(cond : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int\n  fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int\n  fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int\n  fun pthread_detach(th : PthreadT) : Int\n  fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int\n  fun pthread_join(th : PthreadT, thread_return : Void**) : Int\n  fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int\n  fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setname_np(PthreadT, Char*) : Int\n\n  # Set cancellability state of current thread to STATE, returning old\n  # state in *OLDSTATE if OLDSTATE is not NULL.\n  fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    =  1\n  SIGINT    =  2\n  SIGQUIT   =  3\n  SIGILL    =  4\n  SIGTRAP   =  5\n  SIGIOT    =  6\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGCLD    = LibC::SIGCHLD\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = 31\n\n  SIGSTKSZ = 8192\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    val : ULong[32] # (1024 / (8 * sizeof(ULong)))\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_addr : Void*              # Assuming the segfault form of siginfo_t\n    __pad : StaticArray(Int, 27) # __SI_PAD_SIZE (29) - sizeof(void*) (4) = 25\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : ->\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(pid : PidT, sig : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(sig : Int, handler : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/stdarg.cr",
    "content": "lib LibC\n  type VaList = Void*\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(format : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(old : Char*, new : Char*) : Int\n  fun snprintf(s : Char*, maxlen : SizeT, format : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(nptr : Char*) : Double\n  fun div(numer : Int, denom : Int) : DivT\n  fun exit(status : Int) : NoReturn\n  fun free(ptr : Void*) : Void\n  fun getenv(name : Char*) : Char*\n  fun malloc(size : SizeT) : Void*\n  fun mkstemp = mkstemp64(template : Char*) : Int\n  fun mkstemps = mkstemps64(template : Char*, suffixlen : Int) : Int\n  fun putenv(string : Char*) : Int\n  fun realloc(ptr : Void*, size : SizeT) : Void*\n  fun realpath(name : Char*, resolved : Char*) : Char*\n  fun setenv(name : Char*, value : Char*, replace : Int) : Int\n  fun strtof(nptr : Char*, endptr : Char**) : Float\n  fun strtod(nptr : Char*, endptr : Char**) : Double\n  fun unsetenv(name : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(s1 : Void*, s2 : Void*, n : SizeT) : Int\n  fun strcmp(s1 : Char*, s2 : Char*) : Int\n  fun strerror(errnum : Int) : Char*\n  fun strerror_r = __xpg_strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(s : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =  0x4\n  PROT_NONE             =  0x0\n  PROT_READ             =  0x1\n  PROT_WRITE            =  0x2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = LibC::MAP_ANONYMOUS\n  MAP_ANONYMOUS         = 0x20\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  4\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap = mmap64(addr : Void*, len : SizeT, prot : Int, flags : Int, fd : Int, offset : OffT) : Void*\n  fun mprotect(addr : Void*, len : SizeT, prot : Int) : Int\n  fun munmap(addr : Void*, len : SizeT) : Int\n  fun madvise(addr : Void*, len : SizeT, advice : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int\n\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = Long\n\n  struct FdSet\n    fds_bits : StaticArray(FdMask, 32)\n  end\n\n  fun select(nfds : Int, readfds : FdSet*, writefds : FdSet*, exceptfds : FdSet*, timeout : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::PF_UNIX\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =      0\n  SHUT_RDWR      =      2\n  SHUT_WR        =      1\n  SOCK_CLOEXEC   = 524288\n  SOCK_NONBLOCK  =   2048\n  SOL_TCP        =      6\n  SOL_TLS        =    282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_padding : StaticArray(Char, 122)\n    __ss_align : ULong\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : SizeT\n    msg_control : Void*\n    msg_controllen : SizeT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SizeT\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(fd : Int, addr : Sockaddr*, addr_len : SocklenT*) : Int\n  fun accept4(fd : Int, addr : Sockaddr*, addr_len : SocklenT*, flags : Int) : Int\n  fun bind(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun connect(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun getpeername(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockname(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT*) : Int\n  fun listen(fd : Int, n : Int) : Int\n  fun recv(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun recvfrom(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT) : SSizeT\n  fun setsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT) : Int\n  fun shutdown(fd : Int, how : Int) : Int\n  fun socket(domain : Int, type : Int, protocol : Int) : Int\n  fun socketpair(domain : Int, type : Int, protocol : Int, fds : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  = 0o400 | 0o200 | 0o100\n  S_IRGRP  = S_IRUSR >> 3\n  S_IWGRP  = S_IWUSR >> 3\n  S_IXGRP  = S_IXUSR >> 3\n  S_IRWXG  = S_IRWXU >> 3\n  S_IROTH  = S_IRGRP >> 3\n  S_IWOTH  = S_IWGRP >> 3\n  S_IXOTH  = S_IXGRP >> 3\n  S_IRWXO  = S_IRWXG >> 3\n  S_ISUID  = 0o4000\n  S_ISGID  = 0o2000\n  S_ISVTX  = 0o1000\n\n  struct Stat # stat64\n    st_dev : DevT\n    __pad1 : UInt\n    __st_ino : ULong\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    __pad2 : UInt\n    st_size : OffT\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_ino : InoT\n  end\n\n  fun chmod(file : Char*, mode : ModeT) : Int\n  fun fchmod(fd : Int, mode : ModeT) : Int\n  fun fstat = fstat64(fd : Int, buf : Stat*) : Int\n  fun lstat = lstat64(file : Char*, buf : Stat*) : Int\n  fun mkdir(path : Char*, mode : ModeT) : Int\n  fun mkfifo(path : Char*, mode : ModeT) : Int\n  fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int\n  fun stat = stat64(file : Char*, buf : Stat*) : Int\n  fun umask(mask : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(tv : Timeval*, tz : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = LongLong\n  alias BlksizeT = Long\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULongLong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULongLong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = LongLong\n  alias PidT = Int\n\n  union PthreadAttrT\n    __size : StaticArray(Char, 36)\n    __align : Long\n  end\n\n  union PthreadCondT\n    __size : StaticArray(Char, 48)\n    __align : LongLong\n  end\n\n  union PthreadCondattrT\n    __size : StaticArray(Char, 4)\n    __align : Int\n  end\n\n  union PthreadMutexT\n    __size : StaticArray(Char, 24)\n    __align : Long\n  end\n\n  union PthreadMutexattrT\n    __size : StaticArray(Char, 4)\n    __align : Int\n  end\n\n  alias PthreadT = ULong\n  alias SSizeT = Int\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(pid : PidT, stat_loc : Int*, options : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(fd : Int, termios_p : Termios*) : Int\n  fun tcsetattr(fd : Int, optional_actions : Int, termios_p : Termios*) : Int\n  fun cfmakeraw(termios_p : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun clock_settime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun gmtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun localtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun mktime(tp : Tm*) : TimeT\n  fun nanosleep(req : Timespec*, rem : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(tp : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-gnu/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(name : Char*, type : Int) : Int\n  fun chdir(path : Char*) : Int\n  fun chown(file : Char*, owner : UidT, group : GidT) : Int\n  fun fchown(fd : Int, owner : UidT, group : GidT) : Int\n  fun close(fd : Int) : Int\n  fun dup2(fd : Int, fd2 : Int) : Int\n  fun dup3(fd : Int, fd2 : Int, flags : Int) : Int\n  fun _exit(status : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(file : Char*, argv : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate = ftruncate64(fd : Int, length : OffT) : Int\n  fun getcwd(buf : Char*, size : SizeT) : Char*\n  fun gethostname(name : Char*, len : SizeT) : Int\n  fun getpgid(pid : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(fd : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(file : Char*, owner : UidT, group : GidT) : Int\n  fun link(from : Char*, to : Char*) : Int\n  fun lockf = lockf64(fd : Int, cmd : Int, len : OffT) : Int\n  fun lseek = lseek64(fd : Int, offset : OffT, whence : Int) : OffT\n  fun pipe(pipedes : StaticArray(Int, 2)) : Int\n  fun pipe2(pipedes : StaticArray(Int, 2), flags : Int) : Int\n  fun read(fd : Int, buf : Void*, nbytes : SizeT) : SSizeT\n  fun pread = pread64(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(path : Char*) : Int\n  fun symlink(from : Char*, to : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(sysno : Long, ...) : Long\n  fun sysconf(name : Int) : Long\n  fun unlink(name : Char*) : Int\n  fun write(fd : Int, buf : Void*, n : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : Char\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =   1\n  RTLD_NOW     =   2\n  RTLD_GLOBAL  = 256\n  RTLD_LOCAL   =   0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt32T\n  alias Elf_Off = UInt32T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word   # Segment type\n    offset : Elf_Off  # Segment file offset\n    vaddr : Elf_Addr  # Segment virtual address\n    paddr : Elf_Addr  # Segment physical address\n    filesz : Elf_Word # Segment size in file\n    memsz : Elf_Word  # Segment size in memory\n    flags : Elf_Word  # Segment flags\n    align : Elf_Word  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o400000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     =  0x01\n  AI_CANONNAME   =  0x02\n  AI_NUMERICHOST =  0x04\n  AI_NUMERICSERV = 0x400\n  AI_V4MAPPED    =  0x08\n  AI_ALL         =  0x10\n  AI_ADDRCONFIG  =  0x20\n  EAI_AGAIN      =    -3\n  EAI_BADFLAGS   =    -1\n  EAI_FAIL       =    -4\n  EAI_NODATA     =    -5\n  EAI_FAMILY     =    -6\n  EAI_MEMORY     =   -10\n  EAI_NONAME     =    -2\n  EAI_SERVICE    =    -8\n  EAI_SOCKTYPE   =    -7\n  EAI_SYSTEM     =   -11\n  EAI_OVERFLOW   =   -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Sockaddr*, x1 : SocklenT, x2 : Char*, x3 : SocklenT, x4 : Char*, x5 : SocklenT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6Union\n    __s6_addr : StaticArray(UInt8T, 16)\n    __s6_addr16 : StaticArray(UInt16T, 8)\n    __s6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_union : In6AddrIn6Union\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(UInt8T, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/pthread.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_getattr_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setname_np(PthreadT, Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = LibC::SIGSYS\n\n  SIGSTKSZ = 8192\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    bits : ULong[32] # 128 / sizeof(long)\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_addr : Void*              # Assuming the segfault form of siginfo_t\n    __pad : StaticArray(Int, 25) # __SI_PAD_SIZE (29) - sizeof(void*) (4) = 25\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : Void*\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/stdarg.cr",
    "content": "lib LibC\n  type VaList = Void*\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =    4\n  PROT_NONE             =    0\n  PROT_READ             =    1\n  PROT_WRITE            =    2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = 0x20\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  0\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/resource.cr",
    "content": "lib LibC\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(Int, Rlimit*) : Int\n\n  RLIMIT_STACK = 3\n\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = ULong\n\n  struct FdSet\n    fds_bits : StaticArray(ULong, 16)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::AF_LOCAL\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =         0\n  SHUT_RDWR      =         2\n  SHUT_WR        =         1\n  SOCK_CLOEXEC   = 0o2000000\n  SOCK_NONBLOCK  = 0o0004000\n  SOL_TCP        =         6\n  SOL_TLS        =       282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_align : ULong\n    __ss_padding : StaticArray(Char, 112)\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : Int\n    msg_control : Void*\n    msg_controllen : SocklenT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SocklenT\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  =    0o700\n  S_IRGRP  =    0o040\n  S_IWGRP  =    0o020\n  S_IXGRP  =    0o010\n  S_IRWXG  =    0o070\n  S_IROTH  =    0o004\n  S_IWOTH  =    0o002\n  S_IXOTH  =    0o001\n  S_IRWXO  =    0o007\n  S_ISUID  =   0o4000\n  S_ISGID  =   0o2000\n  S_ISVTX  =   0o1000\n\n  struct Stat\n    st_dev : DevT\n    __st_dev_padding : Int\n    __st_ino_truncated : Long\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    __st_rdev_padding : Int\n    st_size : OffT\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_ino : InoT\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = LongLong\n  alias BlksizeT = Long\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULongLong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULongLong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = LongLong\n  alias PidT = Int\n\n  union PthreadAttrTU\n    __i : StaticArray(Int, 9)\n    __vi : StaticArray(Int, 9)\n    __s : StaticArray(UInt, 9)\n  end\n\n  struct PthreadAttrT\n    __u : PthreadAttrTU\n  end\n\n  union PthreadCondTU\n    __i : StaticArray(Int, 12)\n    __vi : StaticArray(Int, 12)\n    __p : StaticArray(Void*, 12)\n  end\n\n  struct PthreadCondT\n    __u : PthreadCondTU\n  end\n\n  struct PthreadCondattrT\n    __attr : UInt\n  end\n\n  union PthreadMutexTU\n    __i : StaticArray(Int, 6)\n    __vi : StaticArray(Int, 6)\n    __p : StaticArray(Void*, 6)\n  end\n\n  struct PthreadMutexT\n    __u : PthreadMutexTU\n  end\n\n  struct PthreadMutexattrT\n    __attr : UInt\n  end\n\n  type PthreadT = Void*\n  alias SSizeT = Int\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    __c_ispeed : SpeedT\n    __c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_SGI_CYCLE          = 10\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/i386-linux-musl/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(x0 : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : StaticArray(Int, 2)) : Int\n  fun pipe2(x0 : StaticArray(Int, 2), flags : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(x0 : Long, ...) : Long\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/linux/tls.cr",
    "content": "lib LibC\n  TLS_TX             = 1\n  TLS_RX             = 2\n  TLS_TX_ZEROCOPY_RO = 3\n\n  TLS_SET_RECORD_TYPE = 1\n  TLS_GET_RECORD_TYPE = 2\n\n  TLS_CIPHER_AES_GCM_128              = 51\n  TLS_CIPHER_AES_GCM_128_IV_SIZE      =  8\n  TLS_CIPHER_AES_GCM_128_KEY_SIZE     = 16\n  TLS_CIPHER_AES_GCM_128_SALT_SIZE    =  4\n  TLS_CIPHER_AES_GCM_128_TAG_SIZE     = 16\n  TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_AES_GCM_256              = 52\n  TLS_CIPHER_AES_GCM_256_IV_SIZE      =  8\n  TLS_CIPHER_AES_GCM_256_KEY_SIZE     = 32\n  TLS_CIPHER_AES_GCM_256_SALT_SIZE    =  4\n  TLS_CIPHER_AES_GCM_256_TAG_SIZE     = 16\n  TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_AES_CCM_128              = 53\n  TLS_CIPHER_AES_CCM_128_IV_SIZE      =  8\n  TLS_CIPHER_AES_CCM_128_KEY_SIZE     = 16\n  TLS_CIPHER_AES_CCM_128_SALT_SIZE    =  4\n  TLS_CIPHER_AES_CCM_128_TAG_SIZE     = 16\n  TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_CHACHA20_POLY1305              = 54\n  TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE      = 12\n  TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE     = 32\n  TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE    =  0\n  TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE     = 16\n  TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_SM4_GCM              = 55\n  TLS_CIPHER_SM4_GCM_IV_SIZE      =  8\n  TLS_CIPHER_SM4_GCM_KEY_SIZE     = 16\n  TLS_CIPHER_SM4_GCM_SALT_SIZE    =  4\n  TLS_CIPHER_SM4_GCM_TAG_SIZE     = 16\n  TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_SM4_CCM              = 56\n  TLS_CIPHER_SM4_CCM_IV_SIZE      =  8\n  TLS_CIPHER_SM4_CCM_KEY_SIZE     = 16\n  TLS_CIPHER_SM4_CCM_SALT_SIZE    =  4\n  TLS_CIPHER_SM4_CCM_TAG_SIZE     = 16\n  TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_ARIA_GCM_128              = 57\n  TLS_CIPHER_ARIA_GCM_128_IV_SIZE      =  8\n  TLS_CIPHER_ARIA_GCM_128_KEY_SIZE     = 16\n  TLS_CIPHER_ARIA_GCM_128_SALT_SIZE    =  4\n  TLS_CIPHER_ARIA_GCM_128_TAG_SIZE     = 16\n  TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE =  8\n\n  TLS_CIPHER_ARIA_GCM_256              = 58\n  TLS_CIPHER_ARIA_GCM_256_IV_SIZE      =  8\n  TLS_CIPHER_ARIA_GCM_256_KEY_SIZE     = 32\n  TLS_CIPHER_ARIA_GCM_256_SALT_SIZE    =  4\n  TLS_CIPHER_ARIA_GCM_256_TAG_SIZE     = 16\n  TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE =  8\n\n  struct Tls_crypto_info\n    version : UInt16\n    cipher_type : UInt16\n  end\n\n  struct Tls12_crypto_info_aes_gcm_128\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_AES_GCM_128_IV_SIZE]\n    key : Char[TLS_CIPHER_AES_GCM_128_KEY_SIZE]\n    salt : Char[TLS_CIPHER_AES_GCM_128_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_aes_gcm_256\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_AES_GCM_256_IV_SIZE]\n    key : Char[TLS_CIPHER_AES_GCM_256_KEY_SIZE]\n    salt : Char[TLS_CIPHER_AES_GCM_256_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_aes_ccm_128\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_AES_CCM_128_IV_SIZE]\n    key : Char[TLS_CIPHER_AES_CCM_128_KEY_SIZE]\n    salt : Char[TLS_CIPHER_AES_CCM_128_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_chacha20_poly1305\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE]\n    key : Char[TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE]\n    salt : Char[TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_sm4_gcm\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_SM4_GCM_IV_SIZE]\n    key : Char[TLS_CIPHER_SM4_GCM_KEY_SIZE]\n    salt : Char[TLS_CIPHER_SM4_GCM_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_sm4_ccm\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_SM4_CCM_IV_SIZE]\n    key : Char[TLS_CIPHER_SM4_CCM_KEY_SIZE]\n    salt : Char[TLS_CIPHER_SM4_CCM_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_aria_gcm_128\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_ARIA_GCM_128_IV_SIZE]\n    key : Char[TLS_CIPHER_ARIA_GCM_128_KEY_SIZE]\n    salt : Char[TLS_CIPHER_ARIA_GCM_128_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE]\n  end\n\n  struct Tls12_crypto_info_aria_gcm_256\n    info : Tls_crypto_info\n    iv : Char[TLS_CIPHER_ARIA_GCM_256_IV_SIZE]\n    key : Char[TLS_CIPHER_ARIA_GCM_256_KEY_SIZE]\n    salt : Char[TLS_CIPHER_ARIA_GCM_256_SALT_SIZE]\n    rec_seq : Char[TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE]\n  end\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/errno.cr",
    "content": "lib LibC\n  $errno : Int\n\n  E2BIG           =  1_u16\n  EACCES          =  2_u16\n  EADDRINUSE      =  3_u16\n  EADDRNOTAVAIL   =  4_u16\n  EAFNOSUPPORT    =  5_u16\n  EAGAIN          =  6_u16\n  EALREADY        =  7_u16\n  EBADF           =  8_u16\n  EBADMSG         =  9_u16\n  EBUSY           = 10_u16\n  ECANCELED       = 11_u16\n  ECHILD          = 12_u16\n  ECONNABORTED    = 13_u16\n  ECONNREFUSED    = 14_u16\n  ECONNRESET      = 15_u16\n  EDEADLK         = 16_u16\n  EDESTADDRREQ    = 17_u16\n  EDOM            = 18_u16\n  EDQUOT          = 19_u16\n  EEXIST          = 20_u16\n  EFAULT          = 21_u16\n  EFBIG           = 22_u16\n  EHOSTUNREACH    = 23_u16\n  EIDRM           = 24_u16\n  EILSEQ          = 25_u16\n  EINPROGRESS     = 26_u16\n  EINTR           = 27_u16\n  EINVAL          = 28_u16\n  EIO             = 29_u16\n  EISCONN         = 30_u16\n  EISDIR          = 31_u16\n  ELOOP           = 32_u16\n  EMFILE          = 33_u16\n  EMLINK          = 34_u16\n  EMSGSIZE        = 35_u16\n  EMULTIHOP       = 36_u16\n  ENAMETOOLONG    = 37_u16\n  ENETDOWN        = 38_u16\n  ENETRESET       = 39_u16\n  ENETUNREACH     = 40_u16\n  ENFILE          = 41_u16\n  ENOBUFS         = 42_u16\n  ENODEV          = 43_u16\n  ENOENT          = 44_u16\n  ENOEXEC         = 45_u16\n  ENOLCK          = 46_u16\n  ENOLINK         = 47_u16\n  ENOMEM          = 48_u16\n  ENOMSG          = 49_u16\n  ENOPROTOOPT     = 50_u16\n  ENOSPC          = 51_u16\n  ENOSYS          = 52_u16\n  ENOTCONN        = 53_u16\n  ENOTDIR         = 54_u16\n  ENOTEMPTY       = 55_u16\n  ENOTRECOVERABLE = 56_u16\n  ENOTSOCK        = 57_u16\n  ENOTSUP         = 58_u16\n  ENOTTY          = 59_u16\n  ENXIO           = 60_u16\n  EOPNOTSUPP      = LibC::ENOTSUP\n  EOVERFLOW       = 61_u16\n  EOWNERDEAD      = 62_u16\n  EPERM           = 63_u16\n  EPIPE           = 64_u16\n  EPROTO          = 65_u16\n  EPROTONOSUPPORT = 66_u16\n  EPROTOTYPE      = 67_u16\n  ERANGE          = 68_u16\n  EROFS           = 69_u16\n  ESPIPE          = 70_u16\n  ESRCH           = 71_u16\n  ESTALE          = 72_u16\n  ETIMEDOUT       = 73_u16\n  ETXTBSY         = 74_u16\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 75_u16\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    = 1\n  F_SETFD    = 2\n  F_GETFL    = 3\n  F_SETFL    = 4\n  FD_CLOEXEC = 1\n  O_CLOEXEC  = 0\n  O_CREAT    = 1_u16 << 12\n  O_EXCL     = 4_u16 << 12\n  O_NOFOLLOW = 0x01000000\n  O_TRUNC    = 8_u16 << 12\n  O_APPEND   =      1_u16\n  O_NONBLOCK =      4_u16\n  O_SYNC     =     16_u16\n  O_RDONLY   = 0x04000000\n  O_RDWR     = O_RDONLY | O_WRONLY\n  O_WRONLY   = 0x10000000\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UShort\n  alias InAddrT = UInt\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  struct In6Addr\n    s6_addr : StaticArray(UChar, 16)\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   = 1\n  TCP_KEEPIDLE  = 4\n  TCP_KEEPINTVL = 5\n  TCP_KEEPCNT   = 6\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/stdarg.cr",
    "content": "lib LibC\n  type VaList = Void*\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = UChar\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun dprintf(x0 : Int, x1 : Char*, ...) : Int\n  fun printf(x0 : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/stdlib.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32T\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(ptr : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(size : SizeT) : Void*\n  fun putenv(x0 : Char*) : Int\n  fun realloc(ptr : Void*, size : SizeT) : Void*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtol(x0 : Char*, x1 : Char**, x2 : Int) : Long\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, x1 : Int, x2 : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : ULong\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/file.cr",
    "content": ""
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/resource.cr",
    "content": "lib LibC\n  RUSAGE_SELF     = 1\n  RUSAGE_CHILDREN = 2\n\n  struct Rusage\n    ru_utime : Timeval\n    ru_stime : Timeval\n  end\n\n  fun getrusage(who : Int, usage : Rusage*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  struct FdSet\n    __nfds : SizeT\n    __fds : StaticArray(Int, 1024)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/socket.cr",
    "content": "require \"./types\"\n\nlib LibC\n  SOCK_DGRAM   =       5_u8\n  SOCK_STREAM  =       6_u8\n  SOL_SOCKET   = 0x7fffffff\n  AF_INET      =          1\n  AF_INET6     =          2\n  AF_UNIX      =          3\n  AF_UNSPEC    =          0\n  SHUT_RD      =       1_u8\n  SHUT_RDWR    = SHUT_RD | SHUT_WR\n  SHUT_WR      =       2_u8\n  SOCK_CLOEXEC = 0x00002000\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 0)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_data : StaticArray(Char, 32)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun shutdown(x0 : Int, x1 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = S_IFBLK | S_IFCHR | S_IFDIR | S_IFIFO | S_IFLNK | S_IFREG | S_IFSOCK\n  S_IFBLK  = 0x6000\n  S_IFCHR  = 0x2000\n  S_IFIFO  = 0xc000\n  S_IFREG  = 0x8000\n  S_IFDIR  = 0x4000\n  S_IFLNK  = 0xa000\n  S_IFSOCK = 0xc000\n  S_IRUSR  =  0x100\n  S_IWUSR  =   0x80\n  S_IXUSR  =   0x40\n  S_IRWXU  = S_IXUSR | S_IWUSR | S_IRUSR\n  S_IRGRP  = 0x20\n  S_IWGRP  = 0x10\n  S_IXGRP  =  0x8\n  S_IRWXG  = S_IXGRP | S_IWGRP | S_IRGRP\n  S_IROTH  = 0x4\n  S_IWOTH  = 0x2\n  S_IXOTH  = 0x1\n  S_IRWXO  = S_IXOTH | S_IWOTH | S_IROTH\n  S_ISUID  = 0x800\n  S_ISGID  = 0x400\n  S_ISVTX  = 0x200\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_mode : ModeT\n    st_uid : UidT\n    st_gid : GidT\n    __pad0 : UInt\n    st_rdev : DevT\n    st_size : OffT\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __reserved : StaticArray(LongLong, 3)\n  end\n\n  fun fchmod(fd : Int, mode : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/times.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Tms\n    tms_utime : ClockT\n    tms_stime : ClockT\n    tms_cutime : ClockT\n    tms_cstime : ClockT\n  end\n\n  fun times(x0 : Tms*) : ClockT\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = LongLong\n  alias BlksizeT = Long\n  alias ClockT = LongLong\n  alias ClockidT = Int\n  alias DevT = ULongLong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULongLong\n  alias ModeT = UInt\n  alias NlinkT = ULongLong\n  alias OffT = LongLong\n  alias PidT = Int\n\n  union PthreadAttrTU\n    __i : StaticArray(Int, 14)\n    __vi : StaticArray(Int, 14)\n    __s : StaticArray(ULong, 7)\n  end\n\n  struct PthreadAttrT\n    __u : PthreadAttrTU\n  end\n\n  union PthreadCondTU\n    __i : StaticArray(Int, 12)\n    __vi : StaticArray(Int, 12)\n    __p : StaticArray(Void*, 6)\n  end\n\n  struct PthreadCondT\n    __u : PthreadCondTU\n  end\n\n  struct PthreadCondattrT\n    __attr : UInt\n  end\n\n  union PthreadMutexTU\n    __i : StaticArray(Int, 10)\n    __vi : StaticArray(Int, 10)\n    __p : StaticArray(Void*, 5)\n  end\n\n  struct PthreadMutexT\n    __u : PthreadMutexTU\n  end\n\n  struct PthreadMutexattrT\n    __attr : UInt\n  end\n\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = LongLong\n  alias TimeT = LongLong\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = UInt8\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    __c_ispeed : SpeedT\n    __c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_MONOTONIC = 1\n  CLOCK_REALTIME  = 0\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Int\n    tm_zone : Char*\n    __tm_nsec : Int\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun timegm(x0 : Tm*) : TimeT\n  fun futimes(fd : Int, times : Timeval[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/wasm32-wasi/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  1\n  W_OK                =  2\n  X_OK                =  4\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(fd : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun fdatasync(x0 : Int) : Int\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun isatty(x0 : Int) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun readlink(x0 : Char*, x1 : Char*, x2 : SizeT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun sleep(x0 : UInt) : UInt\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UShort) : UShort\n  fun ntohs(x0 : UShort) : UShort\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_seekoff : UInt64\n    d_reclen : UShort\n    d_namlen : UShort\n    d_type : UChar\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir = \"opendir$INODE64\"(x0 : Char*) : DIR*\n  fun readdir = \"readdir$INODE64\"(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    = 0x1\n  RTLD_NOW     = 0x2\n  RTLD_GLOBAL  = 0x8\n  RTLD_LOCAL   = 0x4\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(path : Char*, mode : Int) : Void*\n  fun dlsym(handle : Void*, symbol : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/errno.cr",
    "content": "lib LibC\n  fun __error : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  48\n  EADDRNOTAVAIL   =  49\n  EAFNOSUPPORT    =  47\n  EAGAIN          =  35\n  EALREADY        =  37\n  EBADF           =   9\n  EBADMSG         =  94\n  EBUSY           =  16\n  ECANCELED       =  89\n  ECHILD          =  10\n  ECONNABORTED    =  53\n  ECONNREFUSED    =  61\n  ECONNRESET      =  54\n  EDEADLK         =  11\n  EDESTADDRREQ    =  39\n  EDOM            =  33\n  EDQUOT          =  69\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    =  65\n  EIDRM           =  90\n  EILSEQ          =  92\n  EINPROGRESS     =  36\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         =  56\n  EISDIR          =  21\n  ELOOP           =  62\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  40\n  EMULTIHOP       =  95\n  ENAMETOOLONG    =  63\n  ENETDOWN        =  50\n  ENETRESET       =  52\n  ENETUNREACH     =  51\n  ENFILE          =  23\n  ENOBUFS         =  55\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  77\n  ENOLINK         =  97\n  ENOMEM          =  12\n  ENOMSG          =  91\n  ENOPROTOOPT     =  42\n  ENOSPC          =  28\n  ENOSYS          =  78\n  ENOTCONN        =  57\n  ENOTDIR         =  20\n  ENOTEMPTY       =  66\n  ENOTRECOVERABLE = 104\n  ENOTSOCK        =  38\n  ENOTSUP         =  45\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      = 102\n  EOVERFLOW       =  84\n  EOWNERDEAD      = 105\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          = 100\n  EPROTONOSUPPORT =  43\n  EPROTOTYPE      =  41\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          =  70\n  ETIMEDOUT       =  60\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           =  18\n  ENODATA         =  96\n  ENOSR           =  98\n  ENOSTR          =  99\n  ETIME           = 101\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0x1000000\n  O_CREAT    =    0x0200\n  O_NOFOLLOW =    0x0100\n  O_TRUNC    =    0x0400\n  O_EXCL     =    0x0800\n  O_APPEND   =    0x0008\n  O_NONBLOCK =    0x0004\n  O_SYNC     =    0x0080\n  O_RDONLY   =    0x0000\n  O_RDWR     =    0x0002\n  O_WRONLY   =    0x0001\n\n  struct Flock\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n    l_type : Short\n    l_whence : Short\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/iconv.cr",
    "content": "require \"./stddef\"\n\n@[Link(\"iconv\")]\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x00000001\n  AI_CANONNAME   = 0x00000002\n  AI_NUMERICHOST = 0x00000004\n  AI_NUMERICSERV = 0x00001000\n  AI_V4MAPPED    = 0x00000800\n  AI_ALL         = 0x00000100\n  AI_ADDRCONFIG  = 0x00000400\n  EAI_AGAIN      =          2\n  EAI_BADFLAGS   =          3\n  EAI_FAIL       =          4\n  EAI_FAMILY     =          5\n  EAI_MEMORY     =          6\n  EAI_NODATA     =          7\n  EAI_NONAME     =          8\n  EAI_SERVICE    =          9\n  EAI_SOCKTYPE   =         10\n  EAI_SYSTEM     =         11\n  EAI_OVERFLOW   =         14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Sockaddr*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Sockaddr*, x1 : SocklenT, x2 : Char*, x3 : SocklenT, x4 : Char*, x5 : SocklenT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UShort\n  alias InAddrT = UInt\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrU6Addr\n    __u6_addr8 : StaticArray(SaFamilyT, 16)\n    __u6_addr16 : StaticArray(UShort, 8)\n    __u6_addr32 : StaticArray(UInt, 4)\n  end\n\n  struct In6Addr\n    __u6_addr : In6AddrU6Addr\n  end\n\n  struct SockaddrIn\n    sin_len : SaFamilyT\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_len : SaFamilyT\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt\n  end\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP   = 12\n\n  IP_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP   = 13\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  0x01\n  TCP_KEEPINTVL = 0x101\n  TCP_KEEPCNT   = 0x102\n  TCP_KEEPALIVE =  0x10\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/pthread.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 1\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait_relative_np(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_get_stackaddr_np(x0 : PthreadT) : Void*\n  fun pthread_get_stacksize_np(x0 : PthreadT) : SizeT\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(state : Int, oldstate : Int*) : Int\n  fun pthread_setname_np(Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_change : TimeT\n    pw_class : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n    pw_expire : TimeT\n    pw_fields : Int\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    = 10\n  SIGSEGV   = 11\n  SIGSYS    = 12\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 16\n  SIGSTOP   = 17\n  SIGTSTP   = 18\n  SIGCONT   = 19\n  SIGCHLD   = 20\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 23\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 30\n  SIGUSR2   = 31\n  SIGEMT    =  7\n  SIGINFO   = 29\n  SIGWINCH  = 28\n\n  SIGSTKSZ = 131072\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  alias SigsetT = UInt32\n\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  SA_ONSTACK = 0x0001\n  SA_RESTART = 0x0002\n  SA_SIGINFO = 0x0040\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_pid : PidT\n    si_uid : UidT\n    si_status : Int\n    si_addr : Void*\n    _pad : StaticArray(SizeT, 9)\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath = \"realpath$DARWIN_EXTSN\"(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/event.cr",
    "content": "require \"../time\"\n\nlib LibC\n  EVFILT_READ  =  -1_i16\n  EVFILT_WRITE =  -2_i16\n  EVFILT_TIMER =  -7_i16\n  EVFILT_USER  = -10_i16\n\n  EV_ADD     = 0x0001_u16\n  EV_DELETE  = 0x0002_u16\n  EV_ENABLE  = 0x0004_u16\n  EV_ONESHOT = 0x0010_u16\n  EV_CLEAR   = 0x0020_u16\n  EV_EOF     = 0x8000_u16\n  EV_ERROR   = 0x4000_u16\n\n  NOTE_NSECONDS = 0x00000004_u32\n  NOTE_TRIGGER  = 0x01000000_u32\n\n  struct Kevent\n    ident : SizeT # UintptrT\n    filter : Int16\n    flags : UInt16\n    fflags : UInt32\n    data : SSizeT # IntptrT\n    udata : Void*\n  end\n\n  fun kqueue : Int\n  fun kevent(kq : Int, changelist : Kevent*, nchanges : Int, eventlist : Kevent*, nevents : Int, timeout : Timespec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =   0x04\n  PROT_NONE             =   0x00\n  PROT_READ             =   0x01\n  PROT_WRITE            =   0x02\n  MAP_FIXED             = 0x0010\n  MAP_PRIVATE           = 0x0002\n  MAP_SHARED            = 0x0001\n  MAP_ANON              = 0x1000\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   = 4\n  POSIX_MADV_NORMAL     = 0\n  POSIX_MADV_RANDOM     = 1\n  POSIX_MADV_SEQUENTIAL = 2\n  POSIX_MADV_WILLNEED   = 3\n  MADV_DONTNEED         = LibC::POSIX_MADV_DONTNEED\n  MADV_NORMAL           = LibC::POSIX_MADV_NORMAL\n  MADV_RANDOM           = LibC::POSIX_MADV_RANDOM\n  MADV_SEQUENTIAL       = LibC::POSIX_MADV_SEQUENTIAL\n  MADV_WILLNEED         = LibC::POSIX_MADV_WILLNEED\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/resource.cr",
    "content": "lib LibC\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 8\n\n  fun getrlimit(Int, Rlimit*) : Int\n\n  RLIMIT_STACK = 3\n\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  struct FdSet\n    fds_bits : StaticArray(Int, 32)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/socket.cr",
    "content": "require \"./types\"\n\nlib LibC\n  SOCK_DGRAM     =      2\n  SOCK_RAW       =      3\n  SOCK_SEQPACKET =      5\n  SOCK_STREAM    =      1\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x0200\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =  2\n  AF_INET6       = 30\n  AF_UNIX        =  1\n  AF_UNSPEC      =  0\n  AF_LOCAL       = LibC::AF_UNIX\n  SHUT_RD        = 0\n  SHUT_RDWR      = 2\n  SHUT_WR        = 1\n\n  alias SocklenT = UInt\n  alias SaFamilyT = Char\n\n  struct Sockaddr\n    sa_len : SaFamilyT\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_len : SaFamilyT\n    ss_family : SaFamilyT\n    __ss_pad1 : StaticArray(Char, 6)\n    __ss_align : LongLong\n    __ss_pad2 : StaticArray(Char, 112)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  = 0o000400\n  S_IWUSR  = 0o000200\n  S_IXUSR  = 0o000100\n  S_IRWXU  = 0o000700\n  S_IRGRP  = 0o000040\n  S_IWGRP  = 0o000020\n  S_IXGRP  = 0o000010\n  S_IRWXG  = 0o000070\n  S_IROTH  = 0o000004\n  S_IWOTH  = 0o000002\n  S_IXOTH  = 0o000001\n  S_IRWXO  = 0o000007\n  S_ISUID  = 0o004000\n  S_ISGID  = 0o002000\n  S_ISVTX  = 0o001000\n\n  struct Stat\n    st_dev : DevT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_ino : InoT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    st_atimespec : Timespec\n    st_mtimespec : Timespec\n    st_ctimespec : Timespec\n    st_birthtimespec : Timespec\n    st_size : OffT\n    st_blocks : BlkcntT\n    st_blksize : BlksizeT\n    st_flags : UInt\n    st_gen : UInt\n    st_lspare : Int\n    st_qspare : StaticArray(LongLong, 2)\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat = fstat64(x0 : Int, x1 : Stat*) : Int\n  fun lstat = lstat64(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat = stat64(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\n  fun utimes(path : Char*, times : Timeval[2]) : Int\n  fun futimes(fd : Int, times : Timeval[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = LongLong\n  alias BlksizeT = Int\n  alias ClockT = ULong\n  alias ClockidT = Int\n  alias DevT = Int\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = UInt64\n  alias ModeT = UShort\n  alias NlinkT = UShort\n  alias OffT = LongLong\n  alias PidT = Int\n\n  struct PthreadAttrT\n    __sig : Long\n    __opaque : StaticArray(Char, 56)\n  end\n\n  struct PthreadCondT\n    __sig : Long\n    __opaque : StaticArray(Char, 40)\n  end\n\n  struct PthreadCondattrT\n    __sig : Long\n    __opaque : StaticArray(Char, 8)\n  end\n\n  struct PthreadMutexT\n    __sig : Long\n    __opaque : StaticArray(Char, 56)\n  end\n\n  struct PthreadMutexattrT\n    __sig : Long\n    __opaque : StaticArray(Char, 8)\n  end\n\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Int\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_len : Char\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 104)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 0x00000001\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =          0\n  VEOL      =          1\n  VERASE    =          3\n  VINTR     =          8\n  VKILL     =          5\n  VMIN      =         16\n  VQUIT     =          9\n  VSTART    =         12\n  VSTOP     =         13\n  VSUSP     =         10\n  BRKINT    = 0x00000002\n  ICRNL     = 0x00000100\n  IGNBRK    = 0x00000001\n  IGNCR     = 0x00000080\n  IGNPAR    = 0x00000004\n  INLCR     = 0x00000040\n  INPCK     = 0x00000010\n  ISTRIP    = 0x00000020\n  IXANY     = 0x00000800\n  IXOFF     = 0x00000400\n  IXON      = 0x00000200\n  PARMRK    = 0x00000008\n  OPOST     = 0x00000001\n  ONLCR     = 0x00000002\n  OCRNL     = 0x00000010\n  ONOCR     = 0x00000020\n  ONLRET    = 0x00000040\n  OFDEL     = 0x00020000\n  OFILL     = 0x00000080\n  CRDLY     = 0x00003000\n  CR0       = 0x00000000\n  CR1       = 0x00001000\n  CR2       = 0x00002000\n  CR3       = 0x00003000\n  TABDLY    = 0x00000c04\n  TAB0      = 0x00000000\n  TAB1      = 0x00000400\n  TAB2      = 0x00000800\n  TAB3      = 0x00000004\n  BSDLY     = 0x00008000\n  BS0       = 0x00000000\n  BS1       = 0x00008000\n  VTDLY     = 0x00010000\n  VT0       = 0x00000000\n  VT1       = 0x00010000\n  FFDLY     = 0x00004000\n  FF0       = 0x00000000\n  FF1       = 0x00004000\n  NLDLY     = 0x00000300\n  NL0       = 0x00000000\n  NL1       = 0x00000100\n  B0        =          0\n  B50       =         50\n  B75       =         75\n  B110      =        110\n  B134      =        134\n  B150      =        150\n  B200      =        200\n  B300      =        300\n  B600      =        600\n  B1200     =       1200\n  B1800     =       1800\n  B2400     =       2400\n  B4800     =       4800\n  B9600     =       9600\n  B19200    =      19200\n  B38400    =      38400\n  CSIZE     = 0x00000300\n  CS5       = 0x00000000\n  CS6       = 0x00000100\n  CS7       = 0x00000200\n  CS8       = 0x00000300\n  CSTOPB    = 0x00000400\n  CREAD     = 0x00000800\n  PARENB    = 0x00001000\n  PARODD    = 0x00002000\n  HUPCL     = 0x00004000\n  CLOCAL    = 0x00008000\n  ECHO      = 0x00000008\n  ECHOE     = 0x00000002\n  ECHOK     = 0x00000004\n  ECHONL    = 0x00000010\n  ICANON    = 0x00000100\n  IEXTEN    = 0x00000400\n  ISIG      = 0x00000080\n  NOFLSH    = 0x80000000\n  TOSTOP    = 0x00400000\n  TCSANOW   =          0\n  TCSADRAIN =          1\n  TCSAFLUSH =          2\n  TCIFLUSH  =          1\n  TCIOFLUSH =          3\n  TCOFLUSH  =          2\n  TCIOFF    =          3\n  TCION     =          4\n  TCOOFF    =          1\n  TCOON     =          2\n\n  alias CcT = Char\n  alias SpeedT = ULong\n  alias TcflagT = ULong\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : StaticArray(CcT, 20)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME      = 0\n  CLOCK_MONOTONIC_RAW = 4\n  CLOCK_MONOTONIC     = 6\n  CLOCK_UPTIME_RAW    = 8\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime(__clock_id : ClockidT, __tp : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : Char**\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-darwin/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                = 0\n  R_OK                = 1 << 2\n  W_OK                = 1 << 1\n  X_OK                = 1 << 0\n  SC_CLK_TCK          =  3\n  SC_NPROCESSORS_ONLN = 58\n  SC_PAGESIZE         = 29\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun fdatasync(x0 : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : StaticArray(Int, 2)) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_fileno : InoT\n    d_namlen : UShort\n    d_type : UChar\n    d_unused1 : UChar\n    d_unused2 : UInt\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =     1\n  RTLD_NOW     =     2\n  RTLD_GLOBAL  = 0x100\n  RTLD_LOCAL   =     0\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG         =  7\n  EACCES        = 13\n  EADDRINUSE    = 48\n  EADDRNOTAVAIL = 49\n  EAFNOSUPPORT  = 47\n  EAGAIN        = 35\n  EALREADY      = 37\n  EBADF         =  9\n  EBADMSG       = 89\n  EBUSY         = 16\n  ECANCELED     = 85\n  ECHILD        = 10\n  ECONNABORTED  = 53\n  ECONNREFUSED  = 61\n  ECONNRESET    = 54\n  EDEADLK       = 11\n  EDESTADDRREQ  = 39\n  EDOM          = 33\n  EDQUOT        = 69\n  EEXIST        = 17\n  EFAULT        = 14\n  EFBIG         = 27\n  EHOSTUNREACH  = 65\n  EIDRM         = 82\n  EILSEQ        = 86\n  EINPROGRESS   = 36\n  EINTR         =  4\n  EINVAL        = 22\n  EIO           =  5\n  EISCONN       = 56\n  EISDIR        = 21\n  ELOOP         = 62\n  EMFILE        = 24\n  EMLINK        = 31\n  EMSGSIZE      = 40\n  EMULTIHOP     = 90\n  ENAMETOOLONG  = 63\n  ENETDOWN      = 50\n  ENETRESET     = 52\n  ENETUNREACH   = 51\n  ENFILE        = 23\n  ENOBUFS       = 55\n  ENODEV        = 19\n  ENOENT        =  2\n  ENOEXEC       =  8\n  ENOLCK        = 77\n  ENOLINK       = 91\n  ENOMEM        = 12\n  ENOMSG        = 83\n  ENOPROTOOPT   = 42\n  ENOSPC        = 28\n  ENOSYS        = 78\n  ENOTCONN      = 57\n  ENOTDIR       = 20\n  ENOTEMPTY     = 66\n  #  ENOTRECOVERABLE = 95\n  ENOTSOCK   = 38\n  ENOTSUP    = LibC::EOPNOTSUPP\n  ENOTTY     = 25\n  ENXIO      =  6\n  EOPNOTSUPP = 45\n  EOVERFLOW  = 84\n  #  EOWNERDEAD      = 96\n  EPERM           =  1\n  EPIPE           = 32\n  EPROTO          = 92\n  EPROTONOSUPPORT = 43\n  EPROTOTYPE      = 41\n  ERANGE          = 34\n  EROFS           = 30\n  ESPIPE          = 29\n  ESRCH           =  3\n  ESTALE          = 70\n  ETIMEDOUT       = 60\n  ETXTBSY         = 26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =          1\n  F_SETFD    =          2\n  F_GETFL    =          3\n  F_SETFL    =          4\n  FD_CLOEXEC =          1\n  O_CLOEXEC  =    0x20000\n  O_EXCL     =     0x0800\n  O_TRUNC    =     0x0400\n  O_CREAT    =     0x0200\n  O_NOFOLLOW =     0x0100\n  O_SYNC     =     0x0080\n  O_APPEND   =     0x0008\n  O_NONBLOCK =     0x0004\n  O_RDWR     =     0x0002\n  O_WRONLY   =     0x0001\n  O_RDONLY   =     0x0000\n  AT_FDCWD   = 0xFFFAFDCD\n\n  struct Flock\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n    l_type : Short\n    l_whence : Short\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\n\n  ICONV_F_HIDE_INVALID = 0x0001\n  fun __iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*, flags : UInt32, invalids : SizeT*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x00000001\n  AI_CANONNAME   = 0x00000002\n  AI_NUMERICHOST = 0x00000004\n  AI_NUMERICSERV = 0x00000008\n  AI_V4MAPPED    = 0x00000800\n  AI_ALL         = 0x00000100\n  AI_ADDRCONFIG  = 0x00000400\n  EAI_AGAIN      =          2\n  EAI_BADFLAGS   =          3\n  EAI_FAIL       =          4\n  EAI_FAMILY     =          5\n  EAI_MEMORY     =          6\n  EAI_NONAME     =          8\n  EAI_NODATA     =          7\n  EAI_SERVICE    =          9\n  EAI_SOCKTYPE   =         10\n  EAI_SYSTEM     =         11\n  EAI_OVERFLOW   =         14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Void*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Void*, x1 : SocklenT, x2 : Char*, x3 : SizeT, x4 : Char*, x5 : SizeT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrU6Addr\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __u6_addr : In6AddrU6Addr\n  end\n\n  struct SockaddrIn\n    sin_len : UInt8T\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_len : UInt8T\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP   = 12\n\n  IP_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP   = 13\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =    1\n  TCP_KEEPIDLE  =  256\n  TCP_KEEPINTVL =  512\n  TCP_KEEPCNT   = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n@[Link(\"pthread\")]\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 1\n\n  # Flags for cancelling threads\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int\n  fun pthread_attr_get_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int\n  fun pthread_attr_init(x0 : PthreadAttrT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setname_np(PthreadT, Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_change : TimeT\n    pw_class : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n    pw_expire : TimeT\n    pw_fields : Int\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP      = 1\n  SIGINT      = 2\n  SIGQUIT     = 3\n  SIGILL      = 4\n  SIGTRAP     = 5\n  SIGIOT      = LibC::SIGABRT\n  SIGABRT     =  6\n  SIGEMT      =  7\n  SIGFPE      =  8\n  SIGKILL     =  9\n  SIGBUS      = 10\n  SIGSEGV     = 11\n  SIGSYS      = 12\n  SIGPIPE     = 13\n  SIGALRM     = 14\n  SIGTERM     = 15\n  SIGURG      = 16\n  SIGSTOP     = 17\n  SIGTSTP     = 18\n  SIGCONT     = 19\n  SIGCHLD     = 20\n  SIGTTIN     = 21\n  SIGTTOU     = 22\n  SIGIO       = 23\n  SIGXCPU     = 24\n  SIGXFSZ     = 25\n  SIGVTALRM   = 26\n  SIGPROF     = 27\n  SIGWINCH    = 28\n  SIGINFO     = 29\n  SIGUSR1     = 30\n  SIGUSR2     = 31\n  SIGTHR      = 32\n  SIGCKPT     = 33\n  SIGCKPTEXIT = 34\n\n  SIGSTKSZ = 40960\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    bits : UInt32[4]\n  end\n\n  SA_ONSTACK = 0x0001\n  SA_RESTART = 0x0002\n  SA_SIGINFO = 0x0040\n\n  struct Sigval\n    # Actually a union of an int and a void*\n    _sival_ptr : Void*\n  end\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_pid : PidT\n    si_uid : UidT\n    si_status : Int\n    si_addr : Void*\n    si_value : Sigval\n    _pad1 : Long\n    _pad2 : StaticArray(Int, 7)\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_flags : Int\n    sa_mask : SigsetT\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/event.cr",
    "content": "require \"../time\"\n\nlib LibC\n  EVFILT_READ  = -1_i16\n  EVFILT_WRITE = -2_i16\n  EVFILT_TIMER = -7_i16\n  EVFILT_USER  = -9_i16\n\n  EV_ADD     = 0x0001_u16\n  EV_DELETE  = 0x0002_u16\n  EV_ENABLE  = 0x0004_u16\n  EV_ONESHOT = 0x0010_u16\n  EV_CLEAR   = 0x0020_u16\n  EV_EOF     = 0x8000_u16\n  EV_ERROR   = 0x4000_u16\n\n  NOTE_TRIGGER = 0x01000000_u32\n\n  struct Kevent\n    ident : SizeT # UintptrT\n    filter : Short\n    flags : UShort\n    fflags : UInt\n    data : SSizeT # IntptrT\n    udata : Void*\n  end\n\n  fun kqueue : Int\n  fun kevent(kq : Int, changelist : Kevent*, nchanges : Int, eventlist : Kevent*, nevents : Int, timeout : Timespec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =   0x04\n  PROT_NONE             =   0x00\n  PROT_READ             =   0x01\n  PROT_WRITE            =   0x02\n  MAP_FIXED             = 0x0010\n  MAP_PRIVATE           = 0x0002\n  MAP_SHARED            = 0x0001\n  MAP_ANON              = 0x1000\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_NORMAL     = LibC::MADV_NORMAL\n  POSIX_MADV_RANDOM     = LibC::MADV_RANDOM\n  POSIX_MADV_SEQUENTIAL = LibC::MADV_SEQUENTIAL\n  POSIX_MADV_WILLNEED   = LibC::MADV_WILLNEED\n  POSIX_MADV_DONTNEED   = LibC::MADV_DONTNEED\n  MADV_NORMAL           = 0\n  MADV_RANDOM           = 1\n  MADV_SEQUENTIAL       = 2\n  MADV_WILLNEED         = 3\n  MADV_DONTNEED         = 4\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ixrss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int\n\n  alias RlimT = UInt64\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 8\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = ULong\n\n  struct FdSet\n    __fds_bits : StaticArray(FdMask, 16)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/socket.cr",
    "content": "require \"./types\"\n\nlib LibC\n  SOCK_DGRAM     =      2\n  SOCK_RAW       =      3\n  SOCK_SEQPACKET =      5\n  SOCK_STREAM    =      1\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x0200\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =  2\n  AF_INET6       = 28\n  AF_UNIX        = LibC::AF_LOCAL\n  AF_UNSPEC      =          0\n  AF_LOCAL       =          1\n  SHUT_RD        =          0\n  SHUT_RDWR      =          2\n  SHUT_WR        =          1\n  SOCK_CLOEXEC   = 0x10000000\n  SOCK_NONBLOCK  = 0x20000000\n\n  alias SocklenT = UInt\n  alias SaFamilyT = Char\n\n  struct Sockaddr\n    sa_len : Char\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_len : Char\n    ss_family : SaFamilyT\n    __ss_pad1 : StaticArray(Char, 6)\n    __ss_align : Long\n    __ss_pad2 : StaticArray(Char, 112)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFIFO  = 0o010000\n  S_IFCHR  = 0o020000\n  S_IFDIR  = 0o040000\n  S_IFBLK  = 0o060000\n  S_IFREG  = 0o100000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_ISUID  = 0o004000\n  S_ISGID  = 0o002000\n  S_ISVTX  = 0o001000\n  S_IRWXU  = 0o000700\n  S_IRUSR  = 0o000400\n  S_IWUSR  = 0o000200\n  S_IXUSR  = 0o000100\n  S_IRWXG  = 0o000070\n  S_IRGRP  = 0o000040\n  S_IWGRP  = 0o000020\n  S_IXGRP  = 0o000010\n  S_IRWXO  = 0o000007\n  S_IROTH  = 0o000004\n  S_IWOTH  = 0o000002\n  S_IXOTH  = 0o000001\n\n  alias FflagsT = UInt\n\n  struct Stat\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_dev : DevT\n    st_mode : ModeT\n    st_padding1 : UShort\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_size : OffT\n    st_blocks : BlkcntT\n    __old_st_blksize : UInt\n    st_flags : FflagsT\n    st_gen : UInt\n    st_lspare : Int\n    st_blksize : BlksizeT\n    st_qspare2 : Long\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Timezone*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Long\n  alias ClockT = ULong\n  alias ClockidT = ULong\n  alias DevT = UInt\n  alias GidT = UInt\n  alias IdT = Long\n  alias InoT = ULong\n  alias ModeT = UShort\n  alias NlinkT = UInt\n  alias OffT = Long\n  alias PidT = Int\n  type PthreadAttrT = Void*\n  type PthreadCondT = Void*\n  type PthreadCondattrT = Void*\n  type PthreadMutexT = Void*\n  type PthreadMutexattrT = Void*\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_len : Char\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 104)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/sysctl.cr",
    "content": "lib LibC\n  CTL_HW             =  6\n  CTL_KERN           =  1\n  HW_NCPU            =  3\n  KERN_PROC          = 14\n  KERN_PROC_PATHNAME =  9\n\n  fun sysctl(name : Int*, namelen : UInt, oldp : Void*, oldlenp : SizeT*, newp : Void*, newlen : SizeT) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =          0\n  VEOL      =          1\n  VERASE    =          3\n  VINTR     =          8\n  VKILL     =          5\n  VMIN      =         16\n  VQUIT     =          9\n  VSTART    =         12\n  VSTOP     =         13\n  VSUSP     =         10\n  BRKINT    = 0x00000002\n  ICRNL     = 0x00000100\n  IGNBRK    = 0x00000001\n  IGNCR     = 0x00000080\n  IGNPAR    = 0x00000004\n  INLCR     = 0x00000040\n  INPCK     = 0x00000010\n  ISTRIP    = 0x00000020\n  IXANY     = 0x00000800\n  IXOFF     = 0x00000400\n  IXON      = 0x00000200\n  PARMRK    = 0x00000008\n  OPOST     = 0x00000001\n  ONLCR     = 0x00000002\n  OCRNL     = 0x00000010\n  ONOCR     = 0x00000020\n  ONLRET    = 0x00000040\n  TABDLY    = 0x00000004\n  TAB0      = 0x00000000\n  TAB3      = 0x00000004\n  B0        =          0\n  B50       =         50\n  B75       =         75\n  B110      =        110\n  B134      =        134\n  B150      =        150\n  B200      =        200\n  B300      =        300\n  B600      =        600\n  B1200     =       1200\n  B1800     =       1800\n  B2400     =       2400\n  B4800     =       4800\n  B9600     =       9600\n  B19200    =      19200\n  B38400    =      38400\n  CSIZE     = 0x00000300\n  CS5       = 0x00000000\n  CS6       = 0x00000100\n  CS7       = 0x00000200\n  CS8       = 0x00000300\n  CSTOPB    = 0x00000400\n  CREAD     = 0x00000800\n  PARENB    = 0x00001000\n  PARODD    = 0x00002000\n  HUPCL     = 0x00004000\n  CLOCAL    = 0x00008000\n  ECHO      = 0x00000008\n  ECHOE     = 0x00000002\n  ECHOK     = 0x00000004\n  ECHONL    = 0x00000010\n  ICANON    = 0x00000100\n  IEXTEN    = 0x00000400\n  ISIG      = 0x00000080\n  NOFLSH    = 0x80000000\n  TOSTOP    = 0x00400000\n  TCSANOW   =          0\n  TCSADRAIN =          1\n  TCSAFLUSH =          2\n  TCIFLUSH  =          1\n  TCIOFLUSH =          3\n  TCOFLUSH  =          2\n  TCIOFF    =          3\n  TCION     =          4\n  TCOOFF    =          1\n  TCOON     =          2\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : StaticArray(CcT, 20)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  4\n  CLOCK_UPTIME             =  5\n  CLOCK_UPTIME_PRECISE     =  7\n  CLOCK_UPTIME_FAST        =  8\n  CLOCK_REALTIME_PRECISE   =  9\n  CLOCK_REALTIME_FAST      = 10\n  CLOCK_MONOTONIC_PRECISE  = 11\n  CLOCK_MONOTONIC_FAST     = 12\n  CLOCK_SECOND             = 13\n  CLOCK_THREAD_CPUTIME_ID  = 14\n  CLOCK_PROCESS_CPUTIME_ID = 15\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : Char**\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-dragonfly/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK        =    0\n  R_OK        = 0x04\n  W_OK        = 0x02\n  X_OK        = 0x01\n  SC_CLK_TCK  =    3\n  SC_PAGESIZE =   47\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(pid : PidT) : Int\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : Int*) : Int\n  fun pipe2(x0 : Int*, x1 : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_fileno : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : UChar\n    d_pad0 : UChar\n    d_namlen : UShort\n    d_pad1 : UShort\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =     1\n  RTLD_NOW     =     2\n  RTLD_GLOBAL  = 0x100\n  RTLD_LOCAL   =     0\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/errno.cr",
    "content": "lib LibC\n  fun __error : Int*\n\n  E2BIG           =  7\n  EACCES          = 13\n  EADDRINUSE      = 48\n  EADDRNOTAVAIL   = 49\n  EAFNOSUPPORT    = 47\n  EAGAIN          = 35\n  EALREADY        = 37\n  EBADF           =  9\n  EBADMSG         = 89\n  EBUSY           = 16\n  ECANCELED       = 85\n  ECHILD          = 10\n  ECONNABORTED    = 53\n  ECONNREFUSED    = 61\n  ECONNRESET      = 54\n  EDEADLK         = 11\n  EDESTADDRREQ    = 39\n  EDOM            = 33\n  EDQUOT          = 69\n  EEXIST          = 17\n  EFAULT          = 14\n  EFBIG           = 27\n  EHOSTUNREACH    = 65\n  EIDRM           = 82\n  EILSEQ          = 86\n  EINPROGRESS     = 36\n  EINTR           =  4\n  EINVAL          = 22\n  EIO             =  5\n  EISCONN         = 56\n  EISDIR          = 21\n  ELOOP           = 62\n  EMFILE          = 24\n  EMLINK          = 31\n  EMSGSIZE        = 40\n  EMULTIHOP       = 90\n  ENAMETOOLONG    = 63\n  ENETDOWN        = 50\n  ENETRESET       = 52\n  ENETUNREACH     = 51\n  ENFILE          = 23\n  ENOBUFS         = 55\n  ENODEV          = 19\n  ENOENT          =  2\n  ENOEXEC         =  8\n  ENOLCK          = 77\n  ENOLINK         = 91\n  ENOMEM          = 12\n  ENOMSG          = 83\n  ENOPROTOOPT     = 42\n  ENOSPC          = 28\n  ENOSYS          = 78\n  ENOTCONN        = 57\n  ENOTDIR         = 20\n  ENOTEMPTY       = 66\n  ENOTRECOVERABLE = 95\n  ENOTSOCK        = 38\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          = 25\n  ENXIO           =  6\n  EOPNOTSUPP      = 45\n  EOVERFLOW       = 84\n  EOWNERDEAD      = 96\n  EPERM           =  1\n  EPIPE           = 32\n  EPROTO          = 92\n  EPROTONOSUPPORT = 43\n  EPROTOTYPE      = 41\n  ERANGE          = 34\n  EROFS           = 30\n  ESPIPE          = 29\n  ESRCH           =  3\n  ESTALE          = 70\n  ETIMEDOUT       = 60\n  ETXTBSY         = 26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =          1\n  F_SETFD    =          2\n  F_GETFL    =          3\n  F_SETFL    =          4\n  FD_CLOEXEC =          1\n  O_CLOEXEC  = 0x00100000\n  O_CREAT    =     0x0200\n  O_NOFOLLOW =     0x0100\n  O_TRUNC    =     0x0400\n  O_EXCL     =     0x0800\n  O_APPEND   =     0x0008\n  O_NONBLOCK =     0x0004\n  O_SYNC     =     0x0080\n  O_RDONLY   =     0x0000\n  O_RDWR     =     0x0002\n  O_WRONLY   =     0x0001\n  AT_FDCWD   =       -100\n\n  struct Flock\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n    l_type : Short\n    l_whence : Short\n    l_sysid : Int\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  # Although FreeBSD libc provides iconv_XXXX functions, they are just compatibility symbols,\n  # not directly visible via dlsym(3). Use FreeBSD-specific exported symbols so iconv also\n  # works in the interpreter.\n  fun iconv = __bsd_iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close = __bsd_iconv_close(x0 : IconvT) : Int\n  fun iconv_open = __bsd_iconv_open(x0 : Char*, x1 : Char*) : IconvT\n\n  ICONV_F_HIDE_INVALID = 0x0001\n  fun __iconv = __bsd___iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*, flags : UInt32, invalids : SizeT*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x00000001\n  AI_CANONNAME   = 0x00000002\n  AI_NUMERICHOST = 0x00000004\n  AI_NUMERICSERV = 0x00000008\n  AI_V4MAPPED    = 0x00000800\n  AI_ALL         = 0x00000100\n  AI_ADDRCONFIG  = 0x00000400\n  EAI_AGAIN      =          2\n  EAI_BADFLAGS   =          3\n  EAI_FAIL       =          4\n  EAI_FAMILY     =          5\n  EAI_MEMORY     =          6\n  EAI_NODATA     =          7\n  EAI_NONAME     =          8\n  EAI_SERVICE    =          9\n  EAI_SOCKTYPE   =         10\n  EAI_SYSTEM     =         11\n  EAI_OVERFLOW   =         14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Void*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Void*, x1 : SocklenT, x2 : Char*, x3 : SizeT, x4 : Char*, x5 : SizeT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrU6Addr\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __u6_addr : In6AddrU6Addr\n  end\n\n  struct SockaddrIn\n    sin_len : UInt8T\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_len : UInt8T\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP   = 12\n\n  IP_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP   = 13\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY      =    1\n  TCP_TXTLS_ENABLE =   39\n  TCP_RXTLS_ENABLE =   41\n  TCP_KEEPIDLE     =  256\n  TCP_KEEPINTVL    =  512\n  TCP_KEEPCNT      = 1024\n\n  TLS_SET_RECORD_TYPE = 1\n  TLS_GET_RECORD      = 2\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n@[Link(\"pthread\")]\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 1\n\n  # Flags for cancelling threads\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int\n  fun pthread_attr_get_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int\n  fun pthread_attr_init(x0 : PthreadAttrT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_set_name_np(PthreadT, Char*)\n  fun pthread_setcancelstate(Int, Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_change : TimeT\n    pw_class : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n    pw_expire : TimeT\n    pw_fields : Int\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =   6\n  SIGFPE    =   8\n  SIGKILL   =   9\n  SIGBUS    =  10\n  SIGSEGV   =  11\n  SIGSYS    =  12\n  SIGPIPE   =  13\n  SIGALRM   =  14\n  SIGTERM   =  15\n  SIGURG    =  16\n  SIGSTOP   =  17\n  SIGTSTP   =  18\n  SIGCONT   =  19\n  SIGCHLD   =  20\n  SIGTTIN   =  21\n  SIGTTOU   =  22\n  SIGIO     =  23\n  SIGXCPU   =  24\n  SIGXFSZ   =  25\n  SIGVTALRM =  26\n  SIGUSR1   =  30\n  SIGUSR2   =  31\n  SIGEMT    =   7\n  SIGINFO   =  29\n  SIGWINCH  =  28\n  SIGRTMIN  =  65\n  SIGRTMAX  = 126\n  SIGSTKSZ  = 2048 + 32768 # MINSIGSTKSZ + 32768\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    bits : UInt32[4]\n  end\n\n  SA_ONSTACK = 0x0001\n  SA_RESTART = 0x0002\n  SA_SIGINFO = 0x0040\n\n  struct Sigval\n    # Actually a union of an int and a void*\n    _sival_ptr : Void*\n  end\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    si_pid : PidT\n    si_uid : UidT\n    si_status : Int\n    si_addr : Void*\n    si_value : Sigval\n    _pad1 : Long\n    _pad2 : StaticArray(Int, 7)\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_flags : Int\n    sa_mask : SigsetT\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/cpuset.cr",
    "content": "lib LibC\n  CPU_LEVEL_WHICH = 3\n  CPU_WHICH_PID   = 2\n\n  fun cpuset_getaffinity(Int32, Int32, IdT, SizeT, UInt8*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/event.cr",
    "content": "require \"../time\"\n\nlib LibC\n  EVFILT_READ  =  -1_i16\n  EVFILT_WRITE =  -2_i16\n  EVFILT_TIMER =  -7_i16\n  EVFILT_USER  = -11_i16\n\n  EV_ADD     = 0x0001_u16\n  EV_DELETE  = 0x0002_u16\n  EV_ENABLE  = 0x0004_u16\n  EV_ONESHOT = 0x0010_u16\n  EV_CLEAR   = 0x0020_u16\n  EV_EOF     = 0x8000_u16\n  EV_ERROR   = 0x4000_u16\n\n  NOTE_NSECONDS = 0x00000008_u32\n  NOTE_TRIGGER  = 0x01000000_u32\n\n  struct Kevent\n    ident : SizeT # UintptrT\n    filter : Short\n    flags : UShort\n    fflags : UInt\n    data : Int64\n    udata : Void*\n    ext : UInt64[4]\n  end\n\n  fun kqueue1(flags : Int) : Int\n  fun kevent(kq : Int, changelist : Kevent*, nchanges : Int, eventlist : Kevent*, nevents : Int, timeout : Timespec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/ktls.cr",
    "content": "lib LibC\n  struct Tls_get_record\n    tls_type : UInt8\n    tls_vmajor : UInt8\n    tls_vminor : UInt8\n    tls_length : UInt16\n  end\n\n  struct Tls_enable\n    cipher_key : UInt8*\n    iv : UInt8*\n    auth_key : UInt8*\n    cipher_algorithm : Int\n    cipher_key_len : Int\n    iv_len : Int\n    auth_algorithm : Int\n    auth_key_len : Int\n    flags : Int\n    tls_vmajor : UInt8\n    tls_vminor : UInt8\n    rec_seq : UInt8[8]\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =   0x04\n  PROT_NONE             =   0x00\n  PROT_READ             =   0x01\n  PROT_WRITE            =   0x02\n  MAP_FIXED             = 0x0010\n  MAP_PRIVATE           = 0x0002\n  MAP_SHARED            = 0x0001\n  MAP_ANON              = 0x1000\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   = 4\n  POSIX_MADV_NORMAL     = 0\n  POSIX_MADV_RANDOM     = 1\n  POSIX_MADV_SEQUENTIAL = 2\n  POSIX_MADV_WILLNEED   = 3\n  MADV_DONTNEED         = 4\n  MADV_NORMAL           = 0\n  MADV_RANDOM           = 1\n  MADV_SEQUENTIAL       = 2\n  MADV_WILLNEED         = 3\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\n\n  alias RlimT = UInt64\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 8\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = ULong\n\n  struct FdSet\n    __fds_bits : StaticArray(FdMask, 16)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =      2\n  SOCK_RAW       =      3\n  SOCK_SEQPACKET =      5\n  SOCK_STREAM    =      1\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x0200\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =  2\n  AF_INET6       = 28\n  AF_UNIX        =  1\n  AF_UNSPEC      =  0\n  AF_LOCAL       = LibC::AF_UNIX\n  SHUT_RD        =          0\n  SHUT_RDWR      =          2\n  SHUT_WR        =          1\n  SOCK_CLOEXEC   = 0x10000000\n  SOCK_NONBLOCK  = 0x20000000\n\n  alias SocklenT = UInt\n  alias SaFamilyT = Char\n\n  struct Sockaddr\n    sa_len : Char\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_len : Char\n    ss_family : SaFamilyT\n    __ss_pad1 : StaticArray(Char, 6)\n    __ss_align : Long\n    __ss_pad2 : StaticArray(Char, 112)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : Int\n    msg_control : Void*\n    msg_controllen : SocklenT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SocklenT\n    cmsg_level : Int\n    cmsg_type : Int\n    __pad1 : Int\n    cmsg_data : Char[0]\n  end\n\n  MSG_EOR    = 0x00000008\n  MSG_CTRUNC = 0x00000020\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  = 0o000400\n  S_IWUSR  = 0o000200\n  S_IXUSR  = 0o000100\n  S_IRWXU  = 0o000700\n  S_IRGRP  = 0o000040\n  S_IWGRP  = 0o000020\n  S_IXGRP  = 0o000010\n  S_IRWXG  = 0o000070\n  S_IROTH  = 0o000004\n  S_IWOTH  = 0o000002\n  S_IXOTH  = 0o000001\n  S_IRWXO  = 0o000007\n  S_ISUID  = 0o004000\n  S_ISGID  = 0o002000\n  S_ISVTX  = 0o001000\n\n  alias FflagsT = UInt\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_mode : ModeT\n    st_pad0 : UShort\n    st_uid : UidT\n    st_gid : GidT\n    st_pad1 : UInt\n    st_rdev : DevT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_birthtim : Timespec\n    st_size : OffT\n    st_blocks : BlkcntT\n    st_blksize : BlksizeT\n    st_flags : FflagsT\n    st_gen : ULong\n    st_spare : StaticArray(ULong, 10)\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Timezone*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Int\n  alias ClockT = Int\n  alias ClockidT = Int\n  alias DevT = ULong\n  alias GidT = UInt\n  alias IdT = Long\n  alias InoT = ULong\n  alias ModeT = UShort\n  alias NlinkT = ULong\n  alias OffT = Long\n  alias PidT = Int\n  type PthreadAttrT = Void*\n  type PthreadCondT = Void*\n  type PthreadCondattrT = Void*\n  type PthreadMutexT = Void*\n  type PthreadMutexattrT = Void*\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_len : UChar\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 104)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/sysctl.cr",
    "content": "lib LibC\n  CTL_HW             =  6\n  CTL_KERN           =  1\n  HW_NCPU            =  3\n  KERN_PROC          = 14\n  KERN_PROC_PATHNAME = 12\n\n  fun sysctl(name : Int*, namelen : UInt, oldp : Void*, oldlenp : SizeT*, newp : Void*, newlen : SizeT) : Int\n  fun sysctlbyname(name : UInt8*, oldp : Void*, oldlenp : SizeT*, newp : Void*, newlen : SizeT) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =          0\n  VEOL      =          1\n  VERASE    =          3\n  VINTR     =          8\n  VKILL     =          5\n  VMIN      =         16\n  VQUIT     =          9\n  VSTART    =         12\n  VSTOP     =         13\n  VSUSP     =         10\n  BRKINT    = 0x00000002\n  ICRNL     = 0x00000100\n  IGNBRK    = 0x00000001\n  IGNCR     = 0x00000080\n  IGNPAR    = 0x00000004\n  INLCR     = 0x00000040\n  INPCK     = 0x00000010\n  ISTRIP    = 0x00000020\n  IXANY     = 0x00000800\n  IXOFF     = 0x00000400\n  IXON      = 0x00000200\n  PARMRK    = 0x00000008\n  OPOST     = 0x00000001\n  ONLCR     = 0x00000002\n  OCRNL     = 0x00000010\n  ONOCR     = 0x00000020\n  ONLRET    = 0x00000040\n  TABDLY    = 0x00000004\n  TAB0      = 0x00000000\n  TAB3      = 0x00000004\n  B0        =          0\n  B50       =         50\n  B75       =         75\n  B110      =        110\n  B134      =        134\n  B150      =        150\n  B200      =        200\n  B300      =        300\n  B600      =        600\n  B1200     =       1200\n  B1800     =       1800\n  B2400     =       2400\n  B4800     =       4800\n  B9600     =       9600\n  B19200    =      19200\n  B38400    =      38400\n  CSIZE     = 0x00000300\n  CS5       = 0x00000000\n  CS6       = 0x00000100\n  CS7       = 0x00000200\n  CS8       = 0x00000300\n  CSTOPB    = 0x00000400\n  CREAD     = 0x00000800\n  PARENB    = 0x00001000\n  PARODD    = 0x00002000\n  HUPCL     = 0x00004000\n  CLOCAL    = 0x00008000\n  ECHO      = 0x00000008\n  ECHOE     = 0x00000002\n  ECHOK     = 0x00000004\n  ECHONL    = 0x00000010\n  ICANON    = 0x00000100\n  IEXTEN    = 0x00000400\n  ISIG      = 0x00000080\n  NOFLSH    = 0x80000000\n  TOSTOP    = 0x00400000\n  TCSANOW   =          0\n  TCSADRAIN =          1\n  TCSAFLUSH =          2\n  TCIFLUSH  =          1\n  TCIOFLUSH =          3\n  TCOFLUSH  =          2\n  TCIOFF    =          3\n  TCION     =          4\n  TCOOFF    =          1\n  TCOON     =          2\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : StaticArray(CcT, 20)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  4\n  CLOCK_UPTIME             =  5\n  CLOCK_UPTIME_PRECISE     =  7\n  CLOCK_UPTIME_FAST        =  8\n  CLOCK_REALTIME_PRECISE   =  9\n  CLOCK_REALTIME_FAST      = 10\n  CLOCK_MONOTONIC_PRECISE  = 11\n  CLOCK_MONOTONIC_FAST     = 12\n  CLOCK_SECOND             = 13\n  CLOCK_THREAD_CPUTIME_ID  = 14\n  CLOCK_PROCESS_CPUTIME_ID = 15\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  fun timezone(x0 : Int, x1 : Int) : Char*\n  $tzname : Char**\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-freebsd/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK        =    0\n  R_OK        = 0x04\n  W_OK        = 0x02\n  X_OK        = 0x01\n  SC_CLK_TCK  =    3\n  SC_PAGESIZE =   47\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  # `execvpe` is introduced in FreeBSD 15, but it already seems to be available in 14.\n  {% unless flag?(\"freebsd12\") || flag?(\"freebsd13\") %}\n    fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  {% end %}\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(pid : PidT) : Int\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : Int*) : Int\n  fun pipe2(x0 : Int*, x1 : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(hostshort : UInt16T) : UInt16T\n  fun ntohs(netshort : UInt16T) : UInt16T\n  fun inet_ntop(af : Int, cp : Void*, buf : Char*, len : SocklenT) : Char*\n  fun inet_pton(af : Int, cp : Char*, buf : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : UChar\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(dirp : DIR*) : Int\n  fun opendir(name : Char*) : DIR*\n  fun readdir(dirp : DIR*) : Dirent*\n  fun rewinddir(dirp : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/dlfcn.cr",
    "content": "{% unless flag?(:interpreted) %}\n  @[Link(\"dl\")]\n{% end %}\n\nlib LibC\n  RTLD_LAZY    = 0x00001\n  RTLD_NOW     = 0x00002\n  RTLD_GLOBAL  = 0x00100\n  RTLD_LOCAL   =       0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(handle : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(file : Char*, mode : Int) : Void*\n  fun dlsym(handle : Void*, name : Char*) : Void*\n  fun dladdr(address : Void*, info : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o400000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(fd : Int, cmd : Int, ...) : Int\n  fun open(file : Char*, oflag : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close(cd : IconvT) : Int\n  fun iconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n    adds : ULongLong\n    subs : ULongLong\n    tls_modid : SizeT\n    tls_data : Void*\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x0001\n  AI_CANONNAME   = 0x0002\n  AI_NUMERICHOST = 0x0004\n  AI_NUMERICSERV = 0x0400\n  AI_V4MAPPED    = 0x0008\n  AI_ALL         = 0x0010\n  AI_ADDRCONFIG  = 0x0020\n  EAI_AGAIN      =     -3\n  EAI_BADFLAGS   =     -1\n  EAI_FAIL       =     -4\n  EAI_NODATA     =     -5\n  EAI_FAMILY     =     -6\n  EAI_MEMORY     =    -10\n  EAI_NONAME     =     -2\n  EAI_SERVICE    =     -8\n  EAI_SOCKTYPE   =     -7\n  EAI_SYSTEM     =    -11\n  EAI_OVERFLOW   =    -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(ai : Addrinfo*) : Void\n  fun gai_strerror(ecode : Int) : Char*\n  fun getaddrinfo(name : Char*, service : Char*, req : Addrinfo*, pai : Addrinfo**) : Int\n  fun getnameinfo(sa : Sockaddr*, salen : SocklenT, host : Char*, hostlen : SocklenT, serv : Char*, servlen : SocklenT, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6U\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_u : In6AddrIn6U\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n# Starting with glibc 2.34, `pthread` is integrated into `libc` and may not even\n# be available as a separate shared library.\n# There's always a static library for compiled mode, but `Crystal::Loader` does not support\n# static libraries. So we just skip `pthread` entirely in interpreted mode.\n# The symbols are still available in the interpreter because they are loaded in the compiler.\n{% unless flag?(:interpreted) %}\n  @[Link(\"pthread\")]\n{% end %}\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(attr : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int\n  fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(attr : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int\n  fun pthread_cond_broadcast(cond : PthreadCondT*) : Int\n  fun pthread_cond_destroy(cond : PthreadCondT*) : Int\n  fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(cond : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int\n  fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int\n  fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int\n  fun pthread_detach(th : PthreadT) : Int\n  fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int\n  fun pthread_join(th : PthreadT, thread_return : Void**) : Int\n  fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int\n  fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(mutex : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setname_np(PthreadT, Char*) : Int\n\n  # Set cancellability state of current thread to STATE, returning old\n  # state in *OLDSTATE if OLDSTATE is not NULL.\n  fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    =  1\n  SIGINT    =  2\n  SIGQUIT   =  3\n  SIGILL    =  4\n  SIGTRAP   =  5\n  SIGIOT    =  6\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGCLD    = LibC::SIGCHLD\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = 31\n\n  SIGSTKSZ = 8192\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    val : ULong[16] # (1024 / (8 * sizeof(ULong)))\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    __pad0 : Int\n    si_addr : Void*               # Assuming the segfault form of siginfo_t\n    __pad1 : StaticArray(Int, 26) # __SI_PAD_SIZE (28) - sizeof(void*) / sizeof(int) = 26\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : ->\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(pid : PidT, sig : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(sig : Int, handler : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(format : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(old : Char*, new : Char*) : Int\n  fun snprintf(s : Char*, maxlen : SizeT, format : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(nptr : Char*) : Double\n  fun div(numer : Int, denom : Int) : DivT\n  fun exit(status : Int) : NoReturn\n  fun free(ptr : Void*) : Void\n  fun getenv(name : Char*) : Char*\n  fun malloc(size : SizeT) : Void*\n  fun mkstemp(template : Char*) : Int\n  fun mkstemps(template : Char*, suffixlen : Int) : Int\n  fun putenv(string : Char*) : Int\n  fun realloc(ptr : Void*, size : SizeT) : Void*\n  fun realpath(name : Char*, resolved : Char*) : Char*\n  fun setenv(name : Char*, value : Char*, replace : Int) : Int\n  fun strtof(nptr : Char*, endptr : Char**) : Float\n  fun strtod(nptr : Char*, endptr : Char**) : Double\n  fun unsetenv(name : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(s1 : Void*, s2 : Void*, n : SizeT) : Int\n  fun strcmp(s1 : Char*, s2 : Char*) : Int\n  fun strerror(errnum : Int) : Char*\n  fun strerror_r = __xpg_strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(s : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  @[Packed]\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =  0x4\n  PROT_NONE             =  0x0\n  PROT_READ             =  0x1\n  PROT_WRITE            =  0x2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = LibC::MAP_ANONYMOUS\n  MAP_ANONYMOUS         = 0x20\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  4\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(addr : Void*, len : SizeT, prot : Int, flags : Int, fd : Int, offset : OffT) : Void*\n  fun mprotect(addr : Void*, len : SizeT, prot : Int) : Int\n  fun munmap(addr : Void*, len : SizeT) : Int\n  fun madvise(addr : Void*, len : SizeT, advice : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int\n\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = Long\n\n  struct FdSet\n    fds_bits : StaticArray(FdMask, 16)\n  end\n\n  fun select(nfds : Int, readfds : FdSet*, writefds : FdSet*, exceptfds : FdSet*, timeout : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::PF_UNIX\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =      0\n  SHUT_RDWR      =      2\n  SHUT_WR        =      1\n  SOCK_CLOEXEC   = 524288\n  SOCK_NONBLOCK  =   2048\n  SOL_TCP        =      6\n  SOL_TLS        =    282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_padding : StaticArray(Char, 118)\n    __ss_align : ULong\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : SizeT\n    msg_control : Void*\n    msg_controllen : SizeT\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SizeT\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(fd : Int, addr : Sockaddr*, addr_len : SocklenT*) : Int\n  fun accept4(fd : Int, addr : Sockaddr*, addr_len : SocklenT*, flags : Int) : Int\n  fun bind(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun connect(fd : Int, addr : Sockaddr*, len : SocklenT) : Int\n  fun getpeername(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockname(fd : Int, addr : Sockaddr*, len : SocklenT*) : Int\n  fun getsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT*) : Int\n  fun listen(fd : Int, n : Int) : Int\n  fun recv(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun recvfrom(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(fd : Int, buf : Void*, n : SizeT, flags : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(fd : Int, buf : Void*, n : SizeT, flags : Int, addr : Sockaddr*, addr_len : SocklenT) : SSizeT\n  fun setsockopt(fd : Int, level : Int, optname : Int, optval : Void*, optlen : SocklenT) : Int\n  fun shutdown(fd : Int, how : Int) : Int\n  fun socket(domain : Int, type : Int, protocol : Int) : Int\n  fun socketpair(domain : Int, type : Int, protocol : Int, fds : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  = 0o400 | 0o200 | 0o100\n  S_IRGRP  = S_IRUSR >> 3\n  S_IWGRP  = S_IWUSR >> 3\n  S_IXGRP  = S_IXUSR >> 3\n  S_IRWXG  = S_IRWXU >> 3\n  S_IROTH  = S_IRGRP >> 3\n  S_IWOTH  = S_IWGRP >> 3\n  S_IXOTH  = S_IXGRP >> 3\n  S_IRWXO  = S_IRWXG >> 3\n  S_ISUID  = 0o4000\n  S_ISGID  = 0o2000\n  S_ISVTX  = 0o1000\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_mode : ModeT\n    st_uid : UidT\n    st_gid : GidT\n    __pad0 : Int\n    st_rdev : DevT\n    st_size : OffT\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __glibc_reserved : StaticArray(Long, 3)\n  end\n\n  STAT_VER = 1\n\n  fun chmod(file : Char*, mode : ModeT) : Int\n  fun fchmod(fd : Int, mode : ModeT) : Int\n  fun fstat(fd : Int, buf : Stat*) : Int\n  fun __fxstat(ver : Int, fd : Int, buf : Stat*) : Int\n  fun lstat(file : Char*, buf : Stat*) : Int\n  fun __lxstat(ver : Int, file : Char*, buf : Stat*) : Int\n  fun mkdir(path : Char*, mode : ModeT) : Int\n  fun mkfifo(path : Char*, mode : ModeT) : Int\n  fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int\n  fun stat(file : Char*, buf : Stat*) : Int\n  fun __xstat(ver : Int, file : Char*, buf : Stat*) : Int\n  fun umask(mask : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(tv : Timeval*, tz : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Long\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = ULong\n  alias OffT = Long\n  alias PidT = Int\n\n  union PthreadAttrT\n    __size : StaticArray(Char, 56)\n    __align : Long\n  end\n\n  union PthreadCondT\n    __size : StaticArray(Char, 48)\n    __align : LongLong\n  end\n\n  union PthreadCondattrT\n    __size : StaticArray(Char, 4)\n    __align : Int\n  end\n\n  union PthreadMutexT\n    __size : StaticArray(Char, 40)\n    __align : Long\n  end\n\n  union PthreadMutexattrT\n    __size : StaticArray(Char, 4)\n    __align : Int\n  end\n\n  alias PthreadT = ULong\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(pid : PidT, stat_loc : Int*, options : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    c_ispeed : SpeedT\n    c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(fd : Int, termios_p : Termios*) : Int\n  fun tcsetattr(fd : Int, optional_actions : Int, termios_p : Termios*) : Int\n  fun cfmakeraw(termios_p : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun clock_settime(clock_id : ClockidT, tp : Timespec*) : Int\n  fun gmtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun localtime_r(timer : TimeT*, tp : Tm*) : Tm*\n  fun mktime(tp : Tm*) : TimeT\n  fun nanosleep(req : Timespec*, rem : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(tp : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-gnu/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(name : Char*, type : Int) : Int\n  fun chdir(path : Char*) : Int\n  fun chown(file : Char*, owner : UidT, group : GidT) : Int\n  fun fchown(fd : Int, owner : UidT, group : GidT) : Int\n  fun close(fd : Int) : Int\n  fun dup2(fd : Int, fd2 : Int) : Int\n  fun dup3(fd : Int, fd2 : Int, flags : Int) : Int\n  fun _exit(status : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(file : Char*, argv : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(fd : Int, length : OffT) : Int\n  fun getcwd(buf : Char*, size : SizeT) : Char*\n  fun gethostname(name : Char*, len : SizeT) : Int\n  fun getpgid(pid : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(fd : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(file : Char*, owner : UidT, group : GidT) : Int\n  fun link(from : Char*, to : Char*) : Int\n  fun lockf(fd : Int, cmd : Int, len : OffT) : Int\n  fun lseek(fd : Int, offset : OffT, whence : Int) : OffT\n  fun pipe(pipedes : StaticArray(Int, 2)) : Int\n  fun pipe2(pipedes : StaticArray(Int, 2), flags : Int) : Int\n  fun read(fd : Int, buf : Void*, nbytes : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(path : Char*) : Int\n  fun symlink(from : Char*, to : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(sysno : Long, ...) : Long\n  fun sysconf(name : Int) : Long\n  fun unlink(name : Char*) : Int\n  fun write(fd : Int, buf : Void*, n : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : Char\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =   1\n  RTLD_NOW     =   2\n  RTLD_GLOBAL  = 256\n  RTLD_LOCAL   =   0\n  RTLD_DEFAULT = Pointer(Void).new(0_u64)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/errno.cr",
    "content": "lib LibC\n  fun __errno_location : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      =  98\n  EADDRNOTAVAIL   =  99\n  EAFNOSUPPORT    =  97\n  EAGAIN          =  11\n  EALREADY        = 114\n  EBADF           =   9\n  EBADMSG         =  74\n  EBUSY           =  16\n  ECANCELED       = 125\n  ECHILD          =  10\n  ECONNABORTED    = 103\n  ECONNREFUSED    = 111\n  ECONNRESET      = 104\n  EDEADLK         =  35\n  EDESTADDRREQ    =  89\n  EDOM            =  33\n  EDQUOT          = 122\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTUNREACH    = 113\n  EIDRM           =  43\n  EILSEQ          =  84\n  EINPROGRESS     = 115\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 106\n  EISDIR          =  21\n  ELOOP           =  40\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  90\n  EMULTIHOP       =  72\n  ENAMETOOLONG    =  36\n  ENETDOWN        = 100\n  ENETRESET       = 102\n  ENETUNREACH     = 101\n  ENFILE          =  23\n  ENOBUFS         = 105\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  37\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  42\n  ENOPROTOOPT     =  92\n  ENOSPC          =  28\n  ENOSYS          =  38\n  ENOTCONN        = 107\n  ENOTDIR         =  20\n  ENOTEMPTY       =  39\n  ENOTRECOVERABLE = 131\n  ENOTSOCK        =  88\n  ENOTSUP         = LibC::EOPNOTSUPP\n  ENOTTY          =  25\n  ENXIO           =   6\n  EOPNOTSUPP      =  95\n  EOVERFLOW       =  75\n  EOWNERDEAD      = 130\n  EPERM           =   1\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT =  93\n  EPROTOTYPE      =  91\n  ERANGE          =  34\n  EROFS           =  30\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESTALE          = 116\n  ETIMEDOUT       = 110\n  ETXTBSY         =  26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  ENODATA         = 61\n  ENOSR           = 63\n  ENOSTR          = 60\n  ETIME           = 62\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =         1\n  F_SETFD    =         2\n  F_GETFL    =         3\n  F_SETFL    =         4\n  FD_CLOEXEC =         1\n  O_CLOEXEC  = 0o2000000\n  O_CREAT    =     0o100\n  O_EXCL     =     0o200\n  O_NOFOLLOW =  0o400000\n  O_TRUNC    =    0o1000\n  O_APPEND   =    0o2000\n  O_NONBLOCK =    0o4000\n  O_SYNC     = 0o4010000\n  O_RDONLY   =       0o0\n  O_RDWR     =       0o2\n  O_WRONLY   =       0o1\n  AT_FDCWD   =      -100\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(x0 : IconvT, x1 : Char**, x2 : SizeT*, x3 : Char**, x4 : SizeT*) : SizeT\n  fun iconv_close(x0 : IconvT) : Int\n  fun iconv_open(x0 : Char*, x1 : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 4096\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     =  0x01\n  AI_CANONNAME   =  0x02\n  AI_NUMERICHOST =  0x04\n  AI_NUMERICSERV = 0x400\n  AI_V4MAPPED    =  0x08\n  AI_ALL         =  0x10\n  AI_ADDRCONFIG  =  0x20\n  EAI_AGAIN      =    -3\n  EAI_BADFLAGS   =    -1\n  EAI_FAIL       =    -4\n  EAI_NODATA     =    -5\n  EAI_FAMILY     =    -6\n  EAI_MEMORY     =   -10\n  EAI_NONAME     =    -2\n  EAI_SERVICE    =    -8\n  EAI_SOCKTYPE   =    -7\n  EAI_SYSTEM     =   -11\n  EAI_OVERFLOW   =   -12\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Sockaddr*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Sockaddr*, x1 : SocklenT, x2 : Char*, x3 : SocklenT, x4 : Char*, x5 : SocklenT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16T\n  alias InAddrT = UInt32T\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrIn6Union\n    __s6_addr : StaticArray(UInt8T, 16)\n    __s6_addr16 : StaticArray(UInt16T, 8)\n    __s6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __in6_union : In6AddrIn6Union\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(UInt8T, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 32\n  IPV6_MULTICAST_IF = 17\n\n  IP_MULTICAST_TTL    = 33\n  IPV6_MULTICAST_HOPS = 18\n\n  IP_MULTICAST_LOOP   = 34\n  IPV6_MULTICAST_LOOP = 19\n\n  IP_ADD_MEMBERSHIP = 35\n  IPV6_JOIN_GROUP   = 20\n\n  IP_DROP_MEMBERSHIP = 36\n  IPV6_LEAVE_GROUP   = 21\n\n  IPV6_V6ONLY = 26\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   =  1\n  TCP_KEEPIDLE  =  4\n  TCP_KEEPINTVL =  5\n  TCP_KEEPCNT   =  6\n  TCP_ULP       = 31\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/pthread.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 2\n\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_getattr_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setname_np(PthreadT, Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    =  7\n  SIGSEGV   = 11\n  SIGSYS    = 31\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 23\n  SIGSTOP   = 19\n  SIGTSTP   = 20\n  SIGCONT   = 18\n  SIGCHLD   = 17\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 29\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 10\n  SIGUSR2   = 12\n  SIGWINCH  = 28\n  SIGPWR    = 30\n  SIGSTKFLT = 16\n  SIGUNUSED = LibC::SIGSYS\n\n  SIGSTKSZ = 8192\n\n  SIG_SETMASK = 2\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    bits : ULong[16] # 128 / sizeof(long)\n  end\n\n  SA_ONSTACK = 0x08000000\n  SA_RESTART = 0x10000000\n  SA_SIGINFO = 0x00000004\n\n  struct SiginfoT\n    si_signo : Int\n    si_errno : Int\n    si_code : Int\n    __pad0 : Int\n    si_addr : Void*               # Assuming the segfault form of siginfo_t\n    __pad1 : StaticArray(Int, 20) # __SI_PAD_SIZE (28) - sizeof(void*) (8) = 20\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n    sa_flags : Int\n    sa_restorer : Void*\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_flags : Int\n    ss_size : SizeT\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  @[Packed]\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =    4\n  PROT_NONE             =    0\n  PROT_READ             =    1\n  PROT_WRITE            =    2\n  MAP_FIXED             = 0x10\n  MAP_PRIVATE           = 0x02\n  MAP_SHARED            = 0x01\n  MAP_ANON              = 0x20\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  POSIX_MADV_DONTNEED   =  0\n  POSIX_MADV_NORMAL     =  0\n  POSIX_MADV_RANDOM     =  1\n  POSIX_MADV_SEQUENTIAL =  2\n  POSIX_MADV_WILLNEED   =  3\n  MADV_DONTNEED         =  4\n  MADV_NORMAL           =  0\n  MADV_RANDOM           =  1\n  MADV_SEQUENTIAL       =  2\n  MADV_WILLNEED         =  3\n  MADV_HUGEPAGE         = 14\n  MADV_NOHUGEPAGE       = 15\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/random.cr",
    "content": "lib LibC\n  GRND_NONBLOCK = 1_u32\n\n  fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/resource.cr",
    "content": "lib LibC\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 7\n\n  fun getrlimit(Int, Rlimit*) : Int\n\n  RLIMIT_STACK = 3\n\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  alias FdMask = ULong\n\n  struct FdSet\n    fds_bits : StaticArray(ULong, 16)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/socket.cr",
    "content": "require \"./types\"\nrequire \"./uio\"\n\nlib LibC\n  SOCK_DGRAM     =  2\n  SOCK_RAW       =  3\n  SOCK_SEQPACKET =  5\n  SOCK_STREAM    =  1\n  SOL_SOCKET     =  1\n  SO_BROADCAST   =  6\n  SO_KEEPALIVE   =  9\n  SO_LINGER      = 13\n  SO_RCVBUF      =  8\n  SO_REUSEADDR   =  2\n  SO_REUSEPORT   = 15\n  SO_SNDBUF      =  7\n  PF_INET        =  2\n  PF_INET6       = 10\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = 0\n  PF_LOCAL       = 1\n  AF_INET        = LibC::PF_INET\n  AF_INET6       = LibC::PF_INET6\n  AF_UNIX        = LibC::AF_LOCAL\n  AF_UNSPEC      = LibC::PF_UNSPEC\n  AF_LOCAL       = LibC::PF_LOCAL\n  SHUT_RD        =         0\n  SHUT_RDWR      =         2\n  SHUT_WR        =         1\n  SOCK_CLOEXEC   = 0o2000000\n  SOCK_NONBLOCK  = 0o0004000\n  SOL_TCP        =         6\n  SOL_TLS        =       282\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UShort\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    __ss_align : ULong\n    __ss_padding : StaticArray(Char, 112)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  struct Msghdr\n    msg_name : Void*\n    msg_namelen : SocklenT\n    msg_iov : Iovec*\n    msg_iovlen : Int\n    __pad1 : Int\n    msg_control : Void*\n    msg_controllen : SocklenT\n    __pad2 : Int\n    msg_flags : Int\n  end\n\n  struct Cmsghdr\n    cmsg_len : SocklenT\n    __pad1 : Int\n    cmsg_level : Int\n    cmsg_type : Int\n    cmsg_data : Char[0]\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun recvmsg(Int, Msghdr*, Int) : Int\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendmsg(Int, Msghdr*, Int) : Int\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : StaticArray(Int, 2)) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  =    0o400\n  S_IWUSR  =    0o200\n  S_IXUSR  =    0o100\n  S_IRWXU  =    0o700\n  S_IRGRP  =    0o040\n  S_IWGRP  =    0o020\n  S_IXGRP  =    0o010\n  S_IRWXG  =    0o070\n  S_IROTH  =    0o004\n  S_IWOTH  =    0o002\n  S_IXOTH  =    0o001\n  S_IRWXO  =    0o007\n  S_ISUID  =   0o4000\n  S_ISGID  =   0o2000\n  S_ISVTX  =   0o1000\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_mode : ModeT\n    st_uid : UidT\n    st_gid : GidT\n    __pad0 : UInt\n    st_rdev : DevT\n    st_size : OffT\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    __unused : StaticArray(Long, 3)\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Long\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULong\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = ULong\n  alias OffT = Long\n  alias PidT = Int\n\n  union PthreadAttrTU\n    __i : StaticArray(Int, 14)\n    __vi : StaticArray(Int, 14)\n    __s : StaticArray(ULong, 7)\n  end\n\n  struct PthreadAttrT\n    __u : PthreadAttrTU\n  end\n\n  union PthreadCondTU\n    __i : StaticArray(Int, 12)\n    __vi : StaticArray(Int, 12)\n    __p : StaticArray(Void*, 6)\n  end\n\n  struct PthreadCondT\n    __u : PthreadCondTU\n  end\n\n  struct PthreadCondattrT\n    __attr : UInt\n  end\n\n  union PthreadMutexTU\n    __i : StaticArray(Int, 10)\n    __vi : StaticArray(Int, 10)\n    __p : StaticArray(Void*, 5)\n  end\n\n  struct PthreadMutexT\n    __u : PthreadMutexTU\n  end\n\n  struct PthreadMutexattrT\n    __attr : UInt\n  end\n\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/uio.cr",
    "content": "lib LibC\n  struct Iovec\n    iov_base : Void*\n    iov_len : SizeT\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 108)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =        4\n  VEOL      =       11\n  VERASE    =        2\n  VINTR     =        0\n  VKILL     =        3\n  VMIN      =        6\n  VQUIT     =        1\n  VSTART    =        8\n  VSTOP     =        9\n  VSUSP     =       10\n  BRKINT    = 0o000002\n  ICRNL     = 0o000400\n  IGNBRK    = 0o000001\n  IGNCR     = 0o000200\n  IGNPAR    = 0o000004\n  INLCR     = 0o000100\n  INPCK     = 0o000020\n  ISTRIP    = 0o000040\n  IXANY     = 0o004000\n  IXOFF     = 0o010000\n  IXON      = 0o002000\n  PARMRK    = 0o000010\n  OPOST     = 0o000001\n  ONLCR     = 0o000004\n  OCRNL     = 0o000010\n  ONOCR     = 0o000020\n  ONLRET    = 0o000040\n  OFDEL     = 0o000200\n  OFILL     = 0o000100\n  CRDLY     = 0o003000\n  CR0       = 0o000000\n  CR1       = 0o001000\n  CR2       = 0o002000\n  CR3       = 0o003000\n  TABDLY    = 0o014000\n  TAB0      = 0o000000\n  TAB1      = 0o004000\n  TAB2      = 0o010000\n  TAB3      = 0o014000\n  BSDLY     = 0o020000\n  BS0       = 0o000000\n  BS1       = 0o020000\n  VTDLY     = 0o040000\n  VT0       = 0o000000\n  VT1       = 0o040000\n  FFDLY     = 0o100000\n  FF0       = 0o000000\n  FF1       = 0o100000\n  NLDLY     = 0o000400\n  NL0       = 0o000000\n  NL1       = 0o000400\n  B0        = 0o000000\n  B50       = 0o000001\n  B75       = 0o000002\n  B110      = 0o000003\n  B134      = 0o000004\n  B150      = 0o000005\n  B200      = 0o000006\n  B300      = 0o000007\n  B600      = 0o000010\n  B1200     = 0o000011\n  B1800     = 0o000012\n  B2400     = 0o000013\n  B4800     = 0o000014\n  B9600     = 0o000015\n  B19200    = 0o000016\n  B38400    = 0o000017\n  CSIZE     = 0o000060\n  CS5       = 0o000000\n  CS6       = 0o000020\n  CS7       = 0o000040\n  CS8       = 0o000060\n  CSTOPB    = 0o000100\n  CREAD     = 0o000200\n  PARENB    = 0o000400\n  PARODD    = 0o001000\n  HUPCL     = 0o002000\n  CLOCAL    = 0o004000\n  ECHO      = 0o000010\n  ECHOE     = 0o000020\n  ECHOK     = 0o000040\n  ECHONL    = 0o000100\n  ICANON    = 0o000002\n  IEXTEN    = 0o100000\n  ISIG      = 0o000001\n  NOFLSH    = 0o000200\n  TOSTOP    = 0o000400\n  TCSANOW   =        0\n  TCSADRAIN =        1\n  TCSAFLUSH =        2\n  TCIFLUSH  =        0\n  TCIOFLUSH =        2\n  TCOFLUSH  =        1\n  TCIOFF    =        2\n  TCION     =        3\n  TCOOFF    =        0\n  TCOON     =        1\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_line : CcT\n    c_cc : StaticArray(CcT, 32)\n    __c_ispeed : SpeedT\n    __c_ospeed : SpeedT\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME           =  0\n  CLOCK_MONOTONIC          =  1\n  CLOCK_PROCESS_CPUTIME_ID =  2\n  CLOCK_THREAD_CPUTIME_ID  =  3\n  CLOCK_MONOTONIC_RAW      =  4\n  CLOCK_REALTIME_COARSE    =  5\n  CLOCK_MONOTONIC_COARSE   =  6\n  CLOCK_BOOTTIME           =  7\n  CLOCK_REALTIME_ALARM     =  8\n  CLOCK_BOOTTIME_ALARM     =  9\n  CLOCK_SGI_CYCLE          = 10\n  CLOCK_TAI                = 11\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-linux-musl/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK                =  0\n  R_OK                =  4\n  W_OK                =  2\n  X_OK                =  1\n  SC_CLK_TCK          =  2\n  SC_NPROCESSORS_ONLN = 84\n  SC_PAGESIZE         = 30\n\n  fun chroot(path : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(x0 : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : StaticArray(Int, 2)) : Int\n  fun pipe2(x0 : StaticArray(Int, 2), flags : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(x0 : Long, ...) : Long\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  struct DIR\n    dd_fd : Int\n    dd_loc : Long\n    dd_size : Long\n    dd_buf : Char*\n    dd_len : Int\n    dd_seek : OffT\n    dd_internal : Void*\n    dd_flags : Int\n    dd_lock : Void*\n  end\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_fileno : InoT\n    d_reclen : UInt16\n    d_namlen : UInt16\n    d_type : UInt8\n    d_name : StaticArray(Char, 512)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir = __opendir30(x0 : Char*) : DIR*\n  fun readdir = __readdir30(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =     1\n  RTLD_NOW     =     2\n  RTLD_GLOBAL  = 0x100\n  RTLD_LOCAL   = 0x200\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/errno.cr",
    "content": "lib LibC\n  fun __errno : Int*\n\n  EPERM           =  1     # Operation not permitted\n  ENOENT          =  2     # No such file or directory\n  ESRCH           =  3     # No such process\n  EINTR           =  4     # Interrupted system call\n  EIO             =  5     # Input/output error\n  ENXIO           =  6     # Device not configured\n  E2BIG           =  7     # Argument list too long\n  ENOEXEC         =  8     # Exec format error\n  EBADF           =  9     # Bad file descriptor\n  ECHILD          = 10     # No child processes\n  EDEADLK         = 11     # Resource deadlock avoided\n  ENOMEM          = 12     # Cannot allocate memory\n  EACCES          = 13     # Permission denied\n  EFAULT          = 14     # Bad address\n  ENOTBLK         = 15     # Block device required\n  EBUSY           = 16     # Device busy\n  EEXIST          = 17     # File exists\n  EXDEV           = 18     # Cross-device link\n  ENODEV          = 19     # Operation not supported by device\n  ENOTDIR         = 20     # Not a directory\n  EISDIR          = 21     # Is a directory\n  EINVAL          = 22     # Invalid argument\n  ENFILE          = 23     # Too many open files in system\n  EMFILE          = 24     # Too many open files\n  ENOTTY          = 25     # Inappropriate ioctl for device\n  ETXTBSY         = 26     # Text file busy\n  EFBIG           = 27     # File too large\n  ENOSPC          = 28     # No space left on device\n  ESPIPE          = 29     # Illegal seek\n  EROFS           = 30     # Read-only file system\n  EMLINK          = 31     # Too many links\n  EPIPE           = 32     # Broken pipe\n  EDOM            = 33     # Numerical argument out of domain\n  ERANGE          = 34     # Result too large or too small\n  EAGAIN          = 35     # Resource temporarily unavailable\n  EWOULDBLOCK     = EAGAIN # Operation would block\n  EINPROGRESS     = 36     # Operation now in progress\n  EALREADY        = 37     # Operation already in progress\n  ENOTSOCK        = 38     # Socket operation on non-socket\n  EDESTADDRREQ    = 39     # Destination address required\n  EMSGSIZE        = 40     # Message too long\n  EPROTOTYPE      = 41     # Protocol wrong type for socket\n  ENOPROTOOPT     = 42     # Protocol option not available\n  EPROTONOSUPPORT = 43     # Protocol not supported\n  ESOCKTNOSUPPORT = 44     # Socket type not supported\n  EOPNOTSUPP      = 45     # Operation not supported\n  EPFNOSUPPORT    = 46     # Protocol family not supported\n  EAFNOSUPPORT    = 47     # Address family not supported by protocol family\n  EADDRINUSE      = 48     # Address already in use\n  EADDRNOTAVAIL   = 49     # Can't assign requested address\n  ENETDOWN        = 50     # Network is down\n  ENETUNREACH     = 51     # Network is unreachable\n  ENETRESET       = 52     # Network dropped connection on reset\n  ECONNABORTED    = 53     # Software caused connection abort\n  ECONNRESET      = 54     # Connection reset by peer\n  ENOBUFS         = 55     # No buffer space available\n  EISCONN         = 56     # Socket is already connected\n  ENOTCONN        = 57     # Socket is not connected\n  ESHUTDOWN       = 58     # Can't send after socket shutdown\n  ETOOMANYREFS    = 59     # Too many references: can't splice\n  ETIMEDOUT       = 60     # Operation timed out\n  ECONNREFUSED    = 61     # Connection refused\n  ELOOP           = 62     # Too many levels of symbolic links\n  ENAMETOOLONG    = 63     # File name too long\n  EHOSTDOWN       = 64     # Host is down\n  EHOSTUNREACH    = 65     # No route to host\n  ENOTEMPTY       = 66     # Directory not empty\n  EPROCLIM        = 67     # Too many processes\n  EUSERS          = 68     # Too many users\n  EDQUOT          = 69     # Disc quota exceeded\n  ESTALE          = 70     # Stale NFS file handle\n  EREMOTE         = 71     # Too many levels of remote in path\n  EBADRPC         = 72     # RPC struct is bad\n  ERPCMISMATCH    = 73     # RPC version wrong\n  EPROGUNAVAIL    = 74     # RPC prog. not avail\n  EPROGMISMATCH   = 75     # Program version wrong\n  EPROCUNAVAIL    = 76     # Bad procedure for program\n  ENOLCK          = 77     # No locks available\n  ENOSYS          = 78     # Function not implemented\n  EFTYPE          = 79     # Inappropriate file type or format\n  EAUTH           = 80     # Authentication error\n  ENEEDAUTH       = 81     # Need authenticator\n  EIDRM           = 82     # Identifier removed\n  ENOMSG          = 83     # No message of desired type\n  EOVERFLOW       = 84     # Value too large to be stored in data type\n  EILSEQ          = 85     # Illegal byte sequence\n  ENOTSUP         = 86     # Not supported\n  ECANCELED       = 87     # Operation canceled\n  EBADMSG         = 88     # Bad or Corrupt message\n  ENODATA         = 89     # No message available\n  ENOSR           = 90     # No STREAM resources\n  ENOSTR          = 91     # Not a STREAM\n  ETIME           = 92     # STREAM ioctl timeout\n  ENOATTR         = 93     # Attribute not found\n  EMULTIHOP       = 94     # Multihop attempted\n  ENOLINK         = 95     # Link has been severed\n  EPROTO          = 96     # Protocol error\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =        1\n  F_SETFD    =        2\n  F_GETFL    =        3\n  F_SETFL    =        4\n  FD_CLOEXEC =        1\n  O_CLOEXEC  = 0x400000\n  O_CREAT    =   0x0200\n  O_NOFOLLOW =   0x0100\n  O_TRUNC    =   0x0400\n  O_EXCL     =   0x0800\n  O_APPEND   =   0x0008\n  O_NONBLOCK =   0x0004\n  O_SYNC     =   0x0080\n  O_RDONLY   =   0x0000\n  O_RDWR     =   0x0002\n  O_WRONLY   =   0x0001\n  AT_FDCWD   =     -100\n\n  struct Flock\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n    l_type : Short\n    l_whence : Short\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close(cd : IconvT) : Int\n  fun iconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n    adds : UInt64\n    subs : UInt64\n    tls_modid : SizeT\n    tls_data : Void*\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     =   0x1\n  AI_CANONNAME   =   0x2\n  AI_NUMERICHOST =   0x4\n  AI_NUMERICSERV =   0x8\n  AI_ADDRCONFIG  = 0x400\n  EAI_AGAIN      =     2\n  EAI_BADFLAGS   =     3\n  EAI_FAIL       =     4\n  EAI_FAMILY     =     5\n  EAI_MEMORY     =     6\n  EAI_NODATA     =     7\n  EAI_NONAME     =     8\n  EAI_SERVICE    =     9\n  EAI_SOCKTYPE   =    10\n  EAI_SYSTEM     =    11\n  EAI_OVERFLOW   =    14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Void*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Void*, x1 : SocklenT, x2 : Char*, x3 : SizeT, x4 : Char*, x5 : SizeT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16\n  alias InAddrT = UInt32\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrU6Addr\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __u6_addr : In6AddrU6Addr\n  end\n\n  struct SockaddrIn\n    sin_len : UInt8T\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Int8, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_len : UInt8T\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP   = 12\n\n  IP_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP   = 13\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   = 1\n  TCP_KEEPINTVL = 5\n  TCP_KEEPCNT   = 6\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n@[Link(\"pthread\")]\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 1\n\n  # Flags for cancelling threads\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_attr_destroy(attr : PthreadAttrT*) : Int\n  fun pthread_attr_get_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int\n  fun pthread_attr_init(attr : PthreadAttrT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int\n  fun pthread_equal(x0 : PthreadT, x1 : PthreadT) : Int\n  fun pthread_getspecific(PthreadKeyT) : Void*\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  alias PthreadKeyDestructor = (Void*) ->\n  fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int\n  fun pthread_main_np : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setname_np(PthreadT, Char*, Void*) : Int\n  fun pthread_setspecific(PthreadKeyT, Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_change : TimeT\n    pw_class : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n    pw_expire : TimeT\n  end\n\n  fun getpwnam_r = __getpwnam_r50(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r = __getpwuid_r50(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGEMT    =  7\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    = 10\n  SIGSEGV   = 11\n  SIGSYS    = 12\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 16\n  SIGSTOP   = 17\n  SIGTSTP   = 18\n  SIGCONT   = 19\n  SIGCHLD   = 20\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 23\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 30\n  SIGUSR2   = 31\n  SIGINFO   = 29\n  SIGWINCH  = 28\n\n  SIGSTKSZ = 40960\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    bits : UInt32[4]\n  end\n\n  SA_ONSTACK = 0x0001\n  SA_RESTART = 0x0002\n  SA_SIGINFO = 0x0040\n\n  struct SiginfoT\n    si_signo : Int\n    si_code : Int\n    si_errno : Int\n    _pad1 : Int\n    si_addr : Void*\n    _pad2 : StaticArray(UInt64, 13)\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_flags : Int\n    sa_mask : SigsetT\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction = __sigaction14(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack = __sigaltstack14(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset = __sigemptyset14(SigsetT*) : Int\n  fun sigfillset = __sigfillset14(SigsetT*) : Int\n  fun sigaddset = __sigaddset14(SigsetT*, Int) : Int\n  fun sigdelset = __sigdelset14(SigsetT*, Int) : Int\n  fun sigismember = __sigismember14(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv = __unsetenv13(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/event.cr",
    "content": "require \"../time\"\n\nlib LibC\n  EVFILT_READ  = 0_u32\n  EVFILT_WRITE = 1_u32\n  EVFILT_TIMER = 6_u32\n  EVFILT_USER  = 8_u32\n\n  EV_ADD     = 0x0001_u32\n  EV_DELETE  = 0x0002_u32\n  EV_ENABLE  = 0x0004_u16\n  EV_ONESHOT = 0x0010_u32\n  EV_CLEAR   = 0x0020_u32\n  EV_EOF     = 0x8000_u32\n  EV_ERROR   = 0x4000_u32\n\n  NOTE_NSECONDS = 0x00000003_u32\n  NOTE_TRIGGER  = 0x01000000_u32\n\n  struct Kevent\n    ident : SizeT # UintptrT\n    filter : UInt32\n    flags : UInt32\n    fflags : UInt32\n    data : Int64\n    udata : Void*\n    ext : UInt64[4]\n  end\n\n  fun kqueue1(flags : Int) : Int\n  fun kevent = __kevent50(kq : Int, changelist : Kevent*, nchanges : SizeT, eventlist : Kevent*, nevents : SizeT, timeout : Timespec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_NONE             =   0x00\n  PROT_READ             =   0x01\n  PROT_WRITE            =   0x02\n  PROT_EXEC             =   0x04\n  MAP_SHARED            = 0x0001\n  MAP_PRIVATE           = 0x0002\n  MAP_FIXED             = 0x0010\n  MAP_ANON              = 0x1000\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  MAP_STACK             = 0x2000\n  POSIX_MADV_NORMAL     =      0\n  POSIX_MADV_RANDOM     =      1\n  POSIX_MADV_SEQUENTIAL =      2\n  POSIX_MADV_WILLNEED   =      3\n  POSIX_MADV_DONTNEED   =      4\n  MADV_NORMAL           = LibC::POSIX_MADV_NORMAL\n  MADV_RANDOM           = LibC::POSIX_MADV_RANDOM\n  MADV_SEQUENTIAL       = LibC::POSIX_MADV_SEQUENTIAL\n  MADV_WILLNEED         = LibC::POSIX_MADV_WILLNEED\n  MADV_DONTNEED         = LibC::POSIX_MADV_DONTNEED\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ixrss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int\n\n  alias RlimT = UInt64\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 8\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  struct FdSet\n    fds_bits : StaticArray(UInt32T, 8)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/socket.cr",
    "content": "require \"./types\"\n\nlib LibC\n  SOCK_STREAM    =      1\n  SOCK_DGRAM     =      2\n  SOCK_RAW       =      3\n  SOCK_SEQPACKET =      5\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x0200\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =  2\n  AF_INET6       = 24\n  AF_UNIX        = LibC::AF_LOCAL\n  AF_UNSPEC      =          0\n  AF_LOCAL       =          1\n  SHUT_RD        =          0\n  SHUT_WR        =          1\n  SHUT_RDWR      =          2\n  SOCK_CLOEXEC   = 0x10000000\n  SOCK_NONBLOCK  = 0x20000000\n\n  alias SocklenT = UInt\n  alias SaFamilyT = UInt8\n\n  struct Sockaddr\n    sa_len : UInt8\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_len : UInt8\n    ss_family : SaFamilyT\n    __ss_pad1 : StaticArray(Char, 6)\n    __ss_pad2 : UInt64\n    __ss_pad3 : StaticArray(Char, 240)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket = __socket30(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  = 0o000400\n  S_IWUSR  = 0o000200\n  S_IXUSR  = 0o000100\n  S_IRWXU  = 0o000700\n  S_IRGRP  = 0o000040\n  S_IWGRP  = 0o000020\n  S_IXGRP  = 0o000010\n  S_IRWXG  = 0o000070\n  S_IROTH  = 0o000004\n  S_IWOTH  = 0o000002\n  S_IXOTH  = 0o000001\n  S_IRWXO  = 0o000007\n  S_ISUID  = 0o004000\n  S_ISGID  = 0o002000\n  S_ISVTX  = 0o001000\n\n  struct Stat\n    st_dev : DevT\n    st_mode : ModeT\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_birthtim : Timespec\n    st_size : OffT\n    st_blocks : BlkcntT\n    st_blksize : BlksizeT\n    st_flags : UInt32T\n    st_gen : UInt32T\n    st_spare : UInt32[2]\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat = __fstat50(x0 : Int, x1 : Stat*) : Int\n  fun lstat = __lstat50(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod = __mknod50(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat = __stat50(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday = __gettimeofday50(x0 : Timeval*, x1 : Timezone*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Int64\n  alias BlksizeT = Int32\n  alias ClockT = UInt\n  alias ClockidT = Int\n  alias DevT = UInt64\n  alias GidT = UInt32\n  alias IdT = UInt32\n  alias InoT = UInt64\n  alias ModeT = UInt32\n  alias NlinkT = UInt32\n  alias OffT = Int64\n  alias PidT = Int32\n\n  type PthreadT = Void*\n\n  struct PthreadAttrT\n    pta_magic : UInt\n    pta_flags : Int\n    pta_private : Void*\n  end\n\n  struct PthreadQueueT\n    ptqh_first : Void*\n    ptqh_last : Void*\n  end\n\n  struct PthreadCondT\n    ptc_magic : UInt\n    ptc_lock : UInt8\n    ptc_waiters : PthreadQueueT\n    ptc_mutex : Void*\n    ptc_private : Void*\n  end\n\n  struct PthreadCondattrT\n    ptca_magic : UInt\n    ptca_private : Void*\n  end\n\n  type PthreadKeyT = Int\n\n  struct PthreadMutexT\n    ptm_magic : UInt\n    ptm_errorcheck : UInt8\n    ptm_pad1 : UInt8[3]\n    ptm_ceiling : UInt8\n    ptm_pad2 : UInt8[2]\n    ptm_owner : PthreadT\n    ptm_waiters : PthreadT*\n    ptm_recursed : UInt\n    ptm_spare2 : Void*\n  end\n\n  struct PthreadMutexattrT\n    ptma_magic : UInt\n    ptma_private : Void*\n  end\n\n  alias SSizeT = Long\n  alias SusecondsT = UInt\n  alias TimeT = Int64\n  alias UidT = UInt32\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_len : UInt8\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 104)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/sysctl.cr",
    "content": "lib LibC\n  CTL_HW        =  6\n  HW_NCPU       =  3\n  HW_NCPUONLINE = 16\n\n  CTL_KERN           =  1\n  KERN_PROC          = 14\n  KERN_PROC_ARGS     = 48\n  KERN_PROC_PATHNAME =  5\n\n  fun sysctl(name : Int*, namelen : UInt, oldp : Void*, oldlenp : SizeT*, newp : Void*, newlen : SizeT) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =          0\n  VEOL      =          1\n  VERASE    =          3\n  VINTR     =          8\n  VKILL     =          5\n  VMIN      =         16\n  VQUIT     =          9\n  VSTART    =         12\n  VSTOP     =         13\n  VSUSP     =         10\n  BRKINT    = 0x00000002\n  ICRNL     = 0x00000100\n  IGNBRK    = 0x00000001\n  IGNCR     = 0x00000080\n  IGNPAR    = 0x00000004\n  INLCR     = 0x00000040\n  INPCK     = 0x00000010\n  ISTRIP    = 0x00000020\n  IXANY     = 0x00000800\n  IXOFF     = 0x00000400\n  IXON      = 0x00000200\n  PARMRK    = 0x00000008\n  OPOST     = 0x00000001\n  ONLCR     = 0x00000002\n  OCRNL     = 0x00000010\n  ONOCR     = 0x00000040\n  ONLRET    = 0x00000080\n  B0        =          0\n  B50       =         50\n  B75       =         75\n  B110      =        110\n  B134      =        134\n  B150      =        150\n  B200      =        200\n  B300      =        300\n  B600      =        600\n  B1200     =       1200\n  B1800     =       1800\n  B2400     =       2400\n  B4800     =       4800\n  B9600     =       9600\n  B19200    =      19200\n  B38400    =      38400\n  CSIZE     = 0x00000300\n  CS5       = 0x00000000\n  CS6       = 0x00000100\n  CS7       = 0x00000200\n  CS8       = 0x00000300\n  CSTOPB    = 0x00000400\n  CREAD     = 0x00000800\n  PARENB    = 0x00001000\n  PARODD    = 0x00002000\n  HUPCL     = 0x00004000\n  CLOCAL    = 0x00008000\n  ECHO      = 0x00000008\n  ECHOE     = 0x00000002\n  ECHOK     = 0x00000004\n  ECHONL    = 0x00000010\n  ICANON    = 0x00000100\n  IEXTEN    = 0x00000400\n  ISIG      = 0x00000080\n  NOFLSH    = 0x80000000\n  TOSTOP    = 0x00400000\n  TCSANOW   =          0\n  TCSADRAIN =          1\n  TCSAFLUSH =          2\n  TCIFLUSH  =          1\n  TCIOFLUSH =          3\n  TCOFLUSH  =          2\n  TCIOFF    =          3\n  TCION     =          4\n  TCOOFF    =          1\n  TCOON     =          2\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : StaticArray(CcT, 20)\n    c_ispeed : Int\n    c_ospeed : Int\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_REALTIME  = 0\n  CLOCK_MONOTONIC = 3\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime = __clock_gettime50(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime = __clock_settime50(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r = __gmtime_r50(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r = __localtime_r50(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime = __mktime50(x0 : Tm*) : TimeT\n  fun nanosleep = __nanosleep50(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm = __timegm50(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-netbsd/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK        =    0\n  X_OK        = 0x01\n  W_OK        = 0x02\n  R_OK        = 0x04\n  SC_CLK_TCK  =   39\n  SC_PAGESIZE =   28\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown = __posix_chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown = __posix_fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(x0 : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown = __posix_lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : Int*) : Int\n  fun pipe2(x0 : Int*, x1 : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(x0 : Int, ...) : Int\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  type DIR = Void\n\n  DT_UNKNOWN =  0\n  DT_DIR     =  4\n  DT_LNK     = 10\n\n  struct Dirent\n    d_fileno : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_type : Char\n    d_namlen : Char\n    __d_padding : StaticArray(Char, 4)\n    d_name : StaticArray(Char, 256)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    =     1\n  RTLD_NOW     =     2\n  RTLD_GLOBAL  = 0x100\n  RTLD_LOCAL   = 0x000\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Half = UInt16T\n  alias Elf_Word = UInt32T\n  alias Elf_Sword = Int32T\n  alias Elf_Xword = UInt64T\n  alias Elf_Sxword = Int64T\n  alias Elf_Addr = UInt64T\n  alias Elf_Off = UInt64T\n  alias Elf_Section = UInt16T\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/errno.cr",
    "content": "lib LibC\n  fun __errno : Int*\n\n  E2BIG           =  7\n  EACCES          = 13\n  EADDRINUSE      = 48\n  EADDRNOTAVAIL   = 49\n  EAFNOSUPPORT    = 47\n  EAGAIN          = 35\n  EALREADY        = 37\n  EBADF           =  9\n  EBUSY           = 16\n  ECANCELED       = 88\n  ECHILD          = 10\n  ECONNABORTED    = 53\n  ECONNREFUSED    = 61\n  ECONNRESET      = 54\n  EDEADLK         = 11\n  EDESTADDRREQ    = 39\n  EDOM            = 33\n  EDQUOT          = 69\n  EEXIST          = 17\n  EFAULT          = 14\n  EFBIG           = 27\n  EHOSTUNREACH    = 65\n  EIDRM           = 89\n  EILSEQ          = 84\n  EINPROGRESS     = 36\n  EINTR           =  4\n  EINVAL          = 22\n  EIO             =  5\n  EISCONN         = 56\n  EISDIR          = 21\n  ELOOP           = 62\n  EMFILE          = 24\n  EMLINK          = 31\n  EMSGSIZE        = 40\n  ENAMETOOLONG    = 63\n  ENETDOWN        = 50\n  ENETRESET       = 52\n  ENETUNREACH     = 51\n  ENFILE          = 23\n  ENOBUFS         = 55\n  ENODEV          = 19\n  ENOENT          =  2\n  ENOEXEC         =  8\n  ENOLCK          = 77\n  ENOMEM          = 12\n  ENOMSG          = 90\n  ENOPROTOOPT     = 42\n  ENOSPC          = 28\n  ENOSYS          = 78\n  ENOTCONN        = 57\n  ENOTDIR         = 20\n  ENOTEMPTY       = 66\n  ENOTSOCK        = 38\n  ENOTSUP         = 91\n  ENOTTY          = 25\n  ENXIO           =  6\n  EOPNOTSUPP      = 45\n  EOVERFLOW       = 87\n  EPERM           =  1\n  EPIPE           = 32\n  EPROTONOSUPPORT = 43\n  EPROTOTYPE      = 41\n  ERANGE          = 34\n  EROFS           = 30\n  ESPIPE          = 29\n  ESRCH           =  3\n  ESTALE          = 70\n  ETIMEDOUT       = 60\n  ETXTBSY         = 26\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =       1\n  F_SETFD    =       2\n  F_GETFL    =       3\n  F_SETFL    =       4\n  FD_CLOEXEC =       1\n  O_CLOEXEC  = 0x10000\n  O_CREAT    =  0x0200\n  O_NOFOLLOW =  0x0100\n  O_TRUNC    =  0x0400\n  O_EXCL     =  0x0800\n  O_APPEND   =  0x0008\n  O_NONBLOCK =  0x0004\n  O_SYNC     =  0x0080\n  O_RDONLY   =  0x0000\n  O_RDWR     =  0x0002\n  O_WRONLY   =  0x0001\n  AT_FDCWD   =    -100\n\n  struct Flock\n    l_start : OffT\n    l_len : OffT\n    l_pid : PidT\n    l_type : Short\n    l_whence : Short\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/iconv.cr",
    "content": "require \"./stddef\"\n\n@[Link(\"iconv\")]\nlib LibC\n  type IconvT = Void*\n\n  fun iconv = libiconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close = libiconv_close(cd : IconvT) : Int\n  fun iconv_open = libiconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     =   1\n  AI_CANONNAME   =   2\n  AI_NUMERICHOST =   4\n  AI_NUMERICSERV =  16\n  AI_ADDRCONFIG  =  64\n  EAI_AGAIN      =  -3\n  EAI_BADFLAGS   =  -1\n  EAI_FAIL       =  -4\n  EAI_FAMILY     =  -6\n  EAI_MEMORY     = -10\n  EAI_NODATA     =  -5\n  EAI_NONAME     =  -2\n  EAI_SERVICE    =  -8\n  EAI_SOCKTYPE   =  -7\n  EAI_SYSTEM     = -11\n  EAI_OVERFLOW   = -14\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_addr : Void*\n    ai_canonname : Char*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Void*, x1 : SocklenT, x2 : Char*, x3 : SizeT, x4 : Char*, x5 : SizeT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UShort\n  alias InAddrT = UInt\n\n  struct InAddr\n    s_addr : InAddrT\n  end\n\n  union In6AddrU6Addr\n    __u6_addr8 : StaticArray(UInt8T, 16)\n    __u6_addr16 : StaticArray(UInt16T, 8)\n    __u6_addr32 : StaticArray(UInt32T, 4)\n  end\n\n  struct In6Addr\n    __u6_addr : In6AddrU6Addr\n  end\n\n  struct SockaddrIn\n    sin_len : UInt8T\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : StaticArray(Char, 8)\n  end\n\n  struct SockaddrIn6\n    sin6_len : UInt8T\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32T\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32T\n  end\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP   = 12\n\n  IP_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP   = 13\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY = 0x01\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n@[Link(\"pthread\")]\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 1\n\n  # Flags for cancelling threads\n  PTHREAD_CANCEL_ENABLE  = 0\n  PTHREAD_CANCEL_DISABLE = 1\n\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_equal(x0 : PthreadT, x1 : PthreadT) : Int\n  fun pthread_getspecific(PthreadKeyT) : Void*\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  alias PthreadKeyDestructor = (Void*) ->\n  fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int\n  fun pthread_main_np : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_set_name_np(PthreadT, Char*)\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setspecific(PthreadKeyT, Void*) : Int\n  fun pthread_stackseg_np(PthreadT, StackT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_change : TimeT\n    pw_class : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n    pw_expire : TimeT\n  end\n\n  fun getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP    = 1\n  SIGINT    = 2\n  SIGQUIT   = 3\n  SIGILL    = 4\n  SIGTRAP   = 5\n  SIGIOT    = LibC::SIGABRT\n  SIGABRT   =  6\n  SIGFPE    =  8\n  SIGKILL   =  9\n  SIGBUS    = 10\n  SIGSEGV   = 11\n  SIGSYS    = 12\n  SIGPIPE   = 13\n  SIGALRM   = 14\n  SIGTERM   = 15\n  SIGURG    = 16\n  SIGSTOP   = 17\n  SIGTSTP   = 18\n  SIGCONT   = 19\n  SIGCHLD   = 20\n  SIGTTIN   = 21\n  SIGTTOU   = 22\n  SIGIO     = 23\n  SIGXCPU   = 24\n  SIGXFSZ   = 25\n  SIGVTALRM = 26\n  SIGUSR1   = 30\n  SIGUSR2   = 31\n  SIGEMT    =  7\n  SIGINFO   = 29\n  SIGWINCH  = 28\n\n  MAX_PAGE_SHIFT = 12_u32\n  MINSIGSTKSZ    = 3_u64 << LibC::MAX_PAGE_SHIFT\n  SIGSTKSZ       = LibC::MINSIGSTKSZ + (1_u64 << LibC::MAX_PAGE_SHIFT) * 4\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  alias SigsetT = UInt32\n\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  SA_ONSTACK = 0x0001\n  SA_RESTART = 0x0002\n  SA_SIGINFO = 0x0040\n\n  struct SiginfoT\n    si_signo : Int\n    si_code : Int\n    si_errno : Int\n    si_addr : Void*\n    _pad : StaticArray(UInt8, 108)\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_flags : Int\n    sa_mask : SigsetT\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = LongLong\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULongLong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/event.cr",
    "content": "require \"../time\"\n\nlib LibC\n  EVFILT_READ  = -1_i16\n  EVFILT_WRITE = -2_i16\n  EVFILT_TIMER = -7_i16\n\n  EV_ADD     = 0x0001_u16\n  EV_DELETE  = 0x0002_u16\n  EV_ONESHOT = 0x0010_u16\n  EV_CLEAR   = 0x0020_u16\n  EV_EOF     = 0x8000_u16\n  EV_ERROR   = 0x4000_u16\n\n  NOTE_NSECONDS = 0x00000003_u32\n\n  struct Kevent\n    ident : SizeT # UintptrT\n    filter : Short\n    flags : UShort\n    fflags : UInt\n    data : Int64\n    udata : Void*\n  end\n\n  fun kqueue1(flags : Int) : Int\n  fun kevent(kq : Int, changelist : Kevent*, nchanges : Int, eventlist : Kevent*, nevents : Int, timeout : Timespec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC             =   0x04\n  PROT_NONE             =   0x00\n  PROT_READ             =   0x01\n  PROT_WRITE            =   0x02\n  MAP_FIXED             = 0x0010\n  MAP_PRIVATE           = 0x0002\n  MAP_SHARED            = 0x0001\n  MAP_ANON              = 0x1000\n  MAP_ANONYMOUS         = LibC::MAP_ANON\n  MAP_FAILED            = Pointer(Void).new(-1.to_u64!)\n  MAP_STACK             = 0x4000\n  POSIX_MADV_DONTNEED   =      4\n  POSIX_MADV_NORMAL     =      0\n  POSIX_MADV_RANDOM     =      1\n  POSIX_MADV_SEQUENTIAL =      2\n  POSIX_MADV_WILLNEED   =      3\n  MADV_DONTNEED         = LibC::POSIX_MADV_DONTNEED\n  MADV_NORMAL           = LibC::POSIX_MADV_NORMAL\n  MADV_RANDOM           = LibC::POSIX_MADV_RANDOM\n  MADV_SEQUENTIAL       = LibC::POSIX_MADV_SEQUENTIAL\n  MADV_WILLNEED         = LibC::POSIX_MADV_WILLNEED\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ix_rss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int16\n\n  alias RlimT = UInt64\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 8\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  struct FdSet\n    fds_bits : StaticArray(UInt32T, 32)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/socket.cr",
    "content": "require \"./types\"\n\nlib LibC\n  SOCK_DGRAM     =      2\n  SOCK_RAW       =      3\n  SOCK_SEQPACKET =      5\n  SOCK_STREAM    =      1\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x0200\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::PF_LOCAL\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::AF_LOCAL\n  AF_INET        =  2\n  AF_INET6       = 24\n  AF_UNIX        = LibC::AF_LOCAL\n  AF_UNSPEC      =      0\n  AF_LOCAL       =      1\n  SHUT_RD        =      0\n  SHUT_RDWR      =      2\n  SHUT_WR        =      1\n  SOCK_CLOEXEC   = 0x8000\n  SOCK_NONBLOCK  = 0x4000\n\n  alias SocklenT = UInt\n  alias SaFamilyT = Char\n\n  struct Sockaddr\n    sa_len : Char\n    sa_family : SaFamilyT\n    sa_data : StaticArray(Char, 14)\n  end\n\n  struct SockaddrStorage\n    ss_len : UChar\n    ss_family : SaFamilyT\n    __ss_pad1 : StaticArray(Char, 6)\n    __ss_pad2 : ULongLong\n    __ss_pad3 : StaticArray(Char, 240)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0o170000\n  S_IFBLK  = 0o060000\n  S_IFCHR  = 0o020000\n  S_IFIFO  = 0o010000\n  S_IFREG  = 0o100000\n  S_IFDIR  = 0o040000\n  S_IFLNK  = 0o120000\n  S_IFSOCK = 0o140000\n  S_IRUSR  = 0o000400\n  S_IWUSR  = 0o000200\n  S_IXUSR  = 0o000100\n  S_IRWXU  = 0o000700\n  S_IRGRP  = 0o000040\n  S_IWGRP  = 0o000020\n  S_IXGRP  = 0o000010\n  S_IRWXG  = 0o000070\n  S_IROTH  = 0o000004\n  S_IWOTH  = 0o000002\n  S_IXOTH  = 0o000001\n  S_IRWXO  = 0o000007\n  S_ISUID  = 0o004000\n  S_ISGID  = 0o002000\n  S_ISVTX  = 0o001000\n\n  struct Stat\n    st_mode : ModeT\n    st_dev : DevT\n    st_ino : InoT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_size : OffT\n    st_blocks : BlkcntT\n    st_blksize : BlksizeT\n    st_flags : UInt32T\n    st_gen : UInt32T\n    __st_birthtim : Timespec\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Timezone*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = LongLong\n  alias BlksizeT = Int\n  alias ClockT = LongLong\n  alias ClockidT = Int\n  alias DevT = Int\n  alias GidT = UInt\n  alias IdT = UInt\n  alias InoT = ULongLong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = LongLong\n  alias PidT = Int\n  type PthreadAttrT = Void*\n  type PthreadCondT = Void*\n  type PthreadCondattrT = Void*\n  type PthreadKeyT = Int\n  type PthreadMutexT = Void*\n  type PthreadMutexattrT = Void*\n  type PthreadT = Void*\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = LongLong\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_len : Char\n    sun_family : SaFamilyT\n    sun_path : StaticArray(Char, 104)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 1\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/sysctl.cr",
    "content": "lib LibC\n  CTL_HW        =  6\n  HW_NCPU       =  3\n  HW_NCPUONLINE = 25\n\n  fun sysctl(name : Int*, namelen : UInt, oldp : Void*, oldlenp : SizeT*, newp : Void*, newlen : SizeT) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VEOF      =          0\n  VEOL      =          1\n  VERASE    =          3\n  VINTR     =          8\n  VKILL     =          5\n  VMIN      =         16\n  VQUIT     =          9\n  VSTART    =         12\n  VSTOP     =         13\n  VSUSP     =         10\n  BRKINT    = 0x00000002\n  ICRNL     = 0x00000100\n  IGNBRK    = 0x00000001\n  IGNCR     = 0x00000080\n  IGNPAR    = 0x00000004\n  INLCR     = 0x00000040\n  INPCK     = 0x00000010\n  ISTRIP    = 0x00000020\n  IXANY     = 0x00000800\n  IXOFF     = 0x00000400\n  IXON      = 0x00000200\n  PARMRK    = 0x00000008\n  OPOST     = 0x00000001\n  ONLCR     = 0x00000002\n  OCRNL     = 0x00000010\n  ONOCR     = 0x00000040\n  ONLRET    = 0x00000080\n  B0        =          0\n  B50       =         50\n  B75       =         75\n  B110      =        110\n  B134      =        134\n  B150      =        150\n  B200      =        200\n  B300      =        300\n  B600      =        600\n  B1200     =       1200\n  B1800     =       1800\n  B2400     =       2400\n  B4800     =       4800\n  B9600     =       9600\n  B19200    =      19200\n  B38400    =      38400\n  CSIZE     = 0x00000300\n  CS5       = 0x00000000\n  CS6       = 0x00000100\n  CS7       = 0x00000200\n  CS8       = 0x00000300\n  CSTOPB    = 0x00000400\n  CREAD     = 0x00000800\n  PARENB    = 0x00001000\n  PARODD    = 0x00002000\n  HUPCL     = 0x00004000\n  CLOCAL    = 0x00008000\n  ECHO      = 0x00000008\n  ECHOE     = 0x00000002\n  ECHOK     = 0x00000004\n  ECHONL    = 0x00000010\n  ICANON    = 0x00000100\n  IEXTEN    = 0x00000400\n  ISIG      = 0x00000080\n  NOFLSH    = 0x80000000\n  TOSTOP    = 0x00400000\n  TCSANOW   =          0\n  TCSADRAIN =          1\n  TCSAFLUSH =          2\n  TCIFLUSH  =          1\n  TCIOFLUSH =          3\n  TCOFLUSH  =          2\n  TCIOFF    =          3\n  TCION     =          4\n  TCOOFF    =          1\n  TCOON     =          2\n\n  alias CcT = Char\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : StaticArray(CcT, 20)\n    c_ispeed : Int\n    c_ospeed : Int\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\n  fun cfmakeraw(x0 : Termios*) : Void\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_MONOTONIC = 3\n  CLOCK_REALTIME  = 0\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n    tm_gmtoff : Long\n    tm_zone : Char*\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-openbsd/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK        =    0\n  R_OK        = 0x04\n  W_OK        = 0x02\n  X_OK        = 0x01\n  SC_CLK_TCK  =    3\n  SC_PAGESIZE =   28\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(x0 : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(x0 : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : SizeT) : Int\n  fun getpgid(x0 : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : Int*) : Int\n  fun pipe2(x0 : Int*, x1 : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun syscall(x0 : Int, ...) : Int\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/arpa/inet.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  fun htons(x0 : UInt16T) : UInt16T\n  fun ntohs(x0 : UInt16T) : UInt16T\n  fun inet_ntop(x0 : Int, x1 : Void*, x2 : Char*, x3 : SocklenT) : Char*\n  fun inet_pton(x0 : Int, x1 : Char*, x2 : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/dirent.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  struct DIR\n    d_fd : Int\n    d_loc : Int\n    d_size : Int\n    d_buf : Char*\n  end\n\n  struct Dirent\n    d_ino : InoT\n    d_off : OffT\n    d_reclen : UShort\n    d_name : StaticArray(Char, 1)\n  end\n\n  fun closedir(x0 : DIR*) : Int\n  fun opendir(x0 : Char*) : DIR*\n  fun readdir(x0 : DIR*) : Dirent*\n  fun rewinddir(x0 : DIR*) : Void\n  fun dirfd(dirp : DIR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/dlfcn.cr",
    "content": "lib LibC\n  RTLD_LAZY    = 0x00001\n  RTLD_NOW     = 0x00002\n  RTLD_GLOBAL  = 0x00100\n  RTLD_LOCAL   = 0x00000\n  RTLD_DEFAULT = Pointer(Void).new(-2.to_u64!)\n  RTLD_NEXT    = Pointer(Void).new(-1.to_u64!)\n\n  struct DlInfo\n    dli_fname : Char*\n    dli_fbase : Void*\n    dli_sname : Char*\n    dli_saddr : Void*\n  end\n\n  fun dlclose(x0 : Void*) : Int\n  fun dlerror : Char*\n  fun dlopen(x0 : Char*, x1 : Int) : Void*\n  fun dlsym(x0 : Void*, x1 : Char*) : Void*\n  fun dladdr(x0 : Void*, x1 : DlInfo*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/elf.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  alias Elf_Addr = ULong\n  alias Elf_Half = UShort\n  alias Elf_Off = ULong\n  alias Elf_Sword = Int\n  alias Elf_Sxword = Long\n  alias Elf_Word = UInt\n  alias Elf_Xword = ULong\n  alias Elf_Versym = Elf_Half\n\n  struct Elf_Phdr\n    type : Elf_Word    # Segment type\n    flags : Elf_Word   # Segment flags\n    offset : Elf_Off   # Segment file offset\n    vaddr : Elf_Addr   # Segment virtual address\n    paddr : Elf_Addr   # Segment physical address\n    filesz : Elf_Xword # Segment size in file\n    memsz : Elf_Xword  # Segment size in memory\n    align : Elf_Xword  # Segment alignment\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/errno.cr",
    "content": "lib LibC\n  fun ___errno : Int*\n\n  E2BIG           =   7\n  EACCES          =  13\n  EADDRINUSE      = 125\n  EADDRNOTAVAIL   = 126\n  EADV            =  68\n  EAFNOSUPPORT    = 124\n  EAGAIN          =  11\n  EALREADY        = 149\n  EBADE           =  50\n  EBADF           =   9\n  EBADFD          =  81\n  EBADMSG         =  77\n  EBADR           =  51\n  EBADRQC         =  54\n  EBADSLT         =  55\n  EBFONT          =  57\n  EBUSY           =  16\n  ECANCELED       =  47\n  ECHILD          =  10\n  ECHRNG          =  37\n  ECOMM           =  70\n  ECONNABORTED    = 130\n  ECONNREFUSED    = 146\n  ECONNRESET      = 131\n  EDEADLK         =  45\n  EDEADLOCK       =  56\n  EDESTADDRREQ    =  96\n  EDOM            =  33\n  EDQUOT          =  49\n  EEXIST          =  17\n  EFAULT          =  14\n  EFBIG           =  27\n  EHOSTDOWN       = 147\n  EHOSTUNREACH    = 148\n  EIDRM           =  36\n  EILSEQ          =  88\n  EINPROGRESS     = 150\n  EINTR           =   4\n  EINVAL          =  22\n  EIO             =   5\n  EISCONN         = 133\n  EISDIR          =  21\n  EL2HLT          =  44\n  EL2NSYNC        =  38\n  EL3HLT          =  39\n  EL3RST          =  40\n  ELIBACC         =  83\n  ELIBBAD         =  84\n  ELIBEXEC        =  87\n  ELIBMAX         =  86\n  ELIBSCN         =  85\n  ELNRNG          =  41\n  ELOCKUNMAPPED   =  72\n  ELOOP           =  90\n  EMFILE          =  24\n  EMLINK          =  31\n  EMSGSIZE        =  97\n  EMULTIHOP       =  74\n  ENAMETOOLONG    =  78\n  ENETDOWN        = 127\n  ENETRESET       = 129\n  ENETUNREACH     = 128\n  ENFILE          =  23\n  ENOANO          =  53\n  ENOBUFS         = 132\n  ENOCSI          =  43\n  ENODATA         =  61\n  ENODEV          =  19\n  ENOENT          =   2\n  ENOEXEC         =   8\n  ENOLCK          =  46\n  ENOLINK         =  67\n  ENOMEM          =  12\n  ENOMSG          =  35\n  ENONET          =  64\n  ENOPKG          =  65\n  ENOPROTOOPT     =  99\n  ENOSPC          =  28\n  ENOSR           =  63\n  ENOSTR          =  60\n  ENOSYS          =  89\n  ENOTACTIVE      =  73\n  ENOTBLK         =  15\n  ENOTCONN        = 134\n  ENOTDIR         =  20\n  ENOTEMPTY       =  93\n  ENOTRECOVERABLE =  59\n  ENOTSOCK        =  95\n  ENOTSUP         =  48\n  ENOTTY          =  25\n  ENOTUNIQ        =  80\n  ENXIO           =   6\n  EOPNOTSUPP      = 122\n  EOVERFLOW       =  79\n  EOWNERDEAD      =  58\n  EPERM           =   1\n  EPFNOSUPPORT    = 123\n  EPIPE           =  32\n  EPROTO          =  71\n  EPROTONOSUPPORT = 120\n  EPROTOTYPE      =  98\n  ERANGE          =  34\n  EREMCHG         =  82\n  EREMOTE         =  66\n  ERESTART        =  91\n  EROFS           =  30\n  ESHUTDOWN       = 143\n  ESOCKTNOSUPPORT = 121\n  ESPIPE          =  29\n  ESRCH           =   3\n  ESRMNT          =  69\n  ESTALE          = 151\n  ESTRPIPE        =  92\n  ETIME           =  62\n  ETIMEDOUT       = 145\n  ETOOMANYREFS    = 144\n  ETXTBSY         =  26\n  EUNATCH         =  42\n  EUSERS          =  94\n  EWOULDBLOCK     = LibC::EAGAIN\n  EXDEV           = 18\n  EXFULL          = 52\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/fcntl.cr",
    "content": "require \"./sys/types\"\nrequire \"./sys/stat\"\nrequire \"./unistd\"\n\nlib LibC\n  F_GETFD    =          1\n  F_SETFD    =          2\n  F_GETFL    =          3\n  F_SETFL    =          4\n  FD_CLOEXEC =          1\n  O_CLOEXEC  =   0x800000\n  O_CREAT    =      0x100\n  O_NOFOLLOW =    0x20000\n  O_TRUNC    =      0x200\n  O_EXCL     =      0x400\n  O_APPEND   =       0x08\n  O_NONBLOCK =       0x80\n  O_SYNC     =       0x10\n  O_RDONLY   =          0\n  O_RDWR     =          2\n  O_WRONLY   =          1\n  AT_FDCWD   = 0xffd19553\n\n  struct Flock\n    l_type : Short\n    l_whence : Short\n    l_start : OffT\n    l_len : OffT\n    l_sysid : Int\n    l_pid : PidT\n    l_pad : Long[4]\n  end\n\n  fun fcntl(x0 : Int, x1 : Int, ...) : Int\n  fun open(x0 : Char*, x1 : Int, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/grp.cr",
    "content": "lib LibC\n  struct Group\n    gr_name : Char*\n    gr_passwd : Char*\n    gr_gid : GidT\n    gr_mem : Char**\n  end\n\n  fun getgrnam_r = __posix_getgrnam_r(name : Char*, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\n  fun getgrgid_r = __posix_getgrgid_r(gid : GidT, grp : Group*, buf : Char*, bufsize : SizeT, result : Group**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/iconv.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  type IconvT = Void*\n\n  fun iconv(cd : IconvT, inbuf : Char**, inbytesleft : SizeT*, outbuf : Char**, outbytesleft : SizeT*) : SizeT\n  fun iconv_close(cd : IconvT) : Int\n  fun iconv_open(tocode : Char*, fromcode : Char*) : IconvT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/limits.cr",
    "content": "lib LibC\n  NAME_MAX =  255\n  PATH_MAX = 1024\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/link.cr",
    "content": "require \"./elf\"\n\nlib LibC\n  struct DlPhdrInfo\n    addr : Elf_Addr\n    name : Char*\n    phdr : Elf_Phdr*\n    phnum : Elf_Half\n    adds : ULongLong\n    subs : ULongLong\n  end\n\n  alias DlPhdrCallback = (DlPhdrInfo*, LibC::SizeT, Void*) -> LibC::Int\n  fun dl_iterate_phdr(callback : DlPhdrCallback, data : Void*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/net/if.cr",
    "content": "require \"../netinet/in\"\nrequire \"../stdint\"\n\nlib LibC\n  IF_NAMESIZE = 16\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/netdb.cr",
    "content": "require \"./netinet/in\"\nrequire \"./sys/socket\"\nrequire \"./stdint\"\n\nlib LibC\n  AI_PASSIVE     = 0x0008\n  AI_CANONNAME   = 0x0010\n  AI_NUMERICHOST = 0x0020\n  AI_NUMERICSERV = 0x0040\n  AI_V4MAPPED    = 0x0001\n  AI_ALL         = 0x0002\n  AI_ADDRCONFIG  = 0x0004\n  EAI_ADDRFAMILY =      1\n  EAI_AGAIN      =      2\n  EAI_BADFLAGS   =      3\n  EAI_FAIL       =      4\n  EAI_FAMILY     =      5\n  EAI_MEMORY     =      6\n  EAI_NODATA     =      7\n  EAI_NONAME     =      8\n  EAI_SERVICE    =      9\n  EAI_SOCKTYPE   =     10\n  EAI_SYSTEM     =     11\n  EAI_OVERFLOW   =     12\n  EAI_PROTOCOL   =     13\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SocklenT\n    ai_canonname : Char*\n    ai_addr : Sockaddr*\n    ai_next : Addrinfo*\n  end\n\n  fun freeaddrinfo(x0 : Addrinfo*) : Void\n  fun gai_strerror(x0 : Int) : Char*\n  fun getaddrinfo(x0 : Char*, x1 : Char*, x2 : Addrinfo*, x3 : Addrinfo**) : Int\n  fun getnameinfo(x0 : Sockaddr*, x1 : SocklenT, x2 : Char*, x3 : SocklenT, x4 : Char*, x5 : SocklenT, x6 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/netinet/in.cr",
    "content": "require \"../sys/socket\"\nrequire \"../stdint\"\n\nlib LibC\n  IPPROTO_IP   =   0\n  IPPROTO_IPV6 =  41\n  IPPROTO_ICMP =   1\n  IPPROTO_RAW  = 255\n  IPPROTO_TCP  =   6\n  IPPROTO_UDP  =  17\n\n  alias InPortT = UInt16\n  alias InAddrT = UInt32\n\n  struct InAddr\n    s_addr : InAddrT # actually a union similar to `In6AddrS6Un`\n  end\n\n  union In6AddrS6Un\n    _S6_u8 : UInt8[16]\n    _S6_u16 : UInt16[8]\n    _S6_u32 : UInt32[4]\n    __S6_align : UInt32\n  end\n\n  struct In6Addr\n    _S6_un : In6AddrS6Un\n  end\n\n  struct SockaddrIn\n    sin_family : SaFamilyT\n    sin_port : InPortT\n    sin_addr : InAddr\n    sin_zero : Char[8]\n  end\n\n  struct SockaddrIn6\n    sin6_family : SaFamilyT\n    sin6_port : InPortT\n    sin6_flowinfo : UInt32\n    sin6_addr : In6Addr\n    sin6_scope_id : UInt32\n    __sin6_src_id : UInt32\n  end\n\n  IP_MULTICAST_IF   = 0x10\n  IPV6_MULTICAST_IF =  0x6\n\n  IP_MULTICAST_TTL    = 0x11\n  IPV6_MULTICAST_HOPS =  0x7\n\n  IP_MULTICAST_LOOP   = 0x12\n  IPV6_MULTICAST_LOOP =  0x8\n\n  IP_ADD_MEMBERSHIP = 0x13\n  IPV6_JOIN_GROUP   =  0x9\n\n  IP_DROP_MEMBERSHIP = 0x14\n  IPV6_LEAVE_GROUP   =  0xa\n\n  IPV6_V6ONLY = 27\n\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : UInt\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/netinet/tcp.cr",
    "content": "lib LibC\n  TCP_NODELAY   = 0x01\n  TCP_KEEPIDLE  = 0x22\n  TCP_KEEPINTVL = 0x24\n  TCP_KEEPCNT   = 0x23\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/pthread.cr",
    "content": "require \"./sys/types\"\n\n@[Link(\"pthread\")]\nlib LibC\n  PTHREAD_MUTEX_ERRORCHECK = 0x2\n\n  # cancellation  state\n  PTHREAD_CANCEL_ENABLE  = 0x00\n  PTHREAD_CANCEL_DISABLE = 0x01\n\n  fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int\n  fun pthread_attr_get_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int\n  fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int\n  fun pthread_attr_init(x0 : PthreadAttrT*) : Int\n  fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int\n  fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int\n  fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int\n  fun pthread_cond_destroy(x0 : PthreadCondT*) : Int\n  fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int\n  fun pthread_cond_signal(x0 : PthreadCondT*) : Int\n  fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int\n  fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int\n  fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int\n  fun pthread_detach(x0 : PthreadT) : Int\n  fun pthread_join(x0 : PthreadT, x1 : Void**) : Int\n  fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int\n  fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int\n  fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int\n  fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int\n  fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int\n  fun pthread_self : PthreadT\n  fun pthread_setcancelstate(Int, Int*) : Int\n  fun pthread_setname_np(PthreadT, Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/pwd.cr",
    "content": "lib LibC\n  struct Passwd\n    pw_name : Char*\n    pw_passwd : Char*\n    pw_uid : UidT\n    pw_gid : GidT\n    pw_age : Char*\n    pw_comment : Char*\n    pw_gecos : Char*\n    pw_dir : Char*\n    pw_shell : Char*\n  end\n\n  fun getpwnam_r = __posix_getpwnam_r(login : Char*, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\n  fun getpwuid_r = __posix_getpwuid_r(uid : UidT, pwstore : Passwd*, buf : Char*, bufsize : SizeT, result : Passwd**) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sched.cr",
    "content": "lib LibC\n  fun sched_yield : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/signal.cr",
    "content": "require \"./sys/types\"\nrequire \"./time\"\n\nlib LibC\n  SIGHUP     =  1\n  SIGINT     =  2\n  SIGQUIT    =  3\n  SIGILL     =  4\n  SIGTRAP    =  5\n  SIGIOT     =  6\n  SIGABRT    =  6\n  SIGEMT     =  7\n  SIGFPE     =  8\n  SIGKILL    =  9\n  SIGBUS     = 10\n  SIGSEGV    = 11\n  SIGSYS     = 12\n  SIGPIPE    = 13\n  SIGALRM    = 14\n  SIGTERM    = 15\n  SIGUSR1    = 16\n  SIGUSR2    = 17\n  SIGCLD     = 18\n  SIGCHLD    = 18\n  SIGPWR     = 19\n  SIGWINCH   = 20\n  SIGURG     = 21\n  SIGPOLL    = 22\n  SIGIO      = LibC::SIGPOLL\n  SIGSTOP    = 23\n  SIGTSTP    = 24\n  SIGCONT    = 25\n  SIGTTIN    = 26\n  SIGTTOU    = 27\n  SIGVTALRM  = 28\n  SIGPROF    = 29\n  SIGXCPU    = 30\n  SIGXFSZ    = 31\n  SIGWAITING = 32\n  SIGLWP     = 33\n  SIGFREEZE  = 34\n  SIGTHAW    = 35\n  SIGCANCEL  = 36\n  SIGLOST    = 37\n  SIGXRES    = 38\n  SIGJVM1    = 39\n  SIGJVM2    = 40\n  SIGINFO    = 41\n\n  SIGSTKSZ = 8192\n\n  SIG_SETMASK = 3\n\n  alias SighandlerT = Int ->\n  SIG_DFL = SighandlerT.new(Pointer(Void).new(0_u64), Pointer(Void).null)\n  SIG_IGN = SighandlerT.new(Pointer(Void).new(1_u64), Pointer(Void).null)\n\n  struct SigsetT\n    bits : UInt[4]\n  end\n\n  SA_ONSTACK = 0x00000001\n  SA_RESTART = 0x00000004\n  SA_SIGINFO = 0x00000008\n\n  struct SiginfoT\n    si_signo : Int\n    si_code : Int\n    si_errno : Int\n    si_pad : Int\n    si_addr : Void*\n    __pad : Int[58] # SI_MAXSZ (256) / sizeof(int) - ...\n  end\n\n  alias SigactionHandlerT = (Int, SiginfoT*, Void*) ->\n\n  struct Sigaction\n    sa_flags : Int\n    # Technically a union, but only one can be valid and we only use sa_sigaction\n    # and not sa_handler (which would be a SighandlerT)\n    sa_sigaction : SigactionHandlerT\n    sa_mask : SigsetT\n  end\n\n  struct StackT\n    ss_sp : Void*\n    ss_size : SizeT\n    ss_flags : Int\n  end\n\n  fun kill(x0 : PidT, x1 : Int) : Int\n  fun pthread_sigmask(Int, SigsetT*, SigsetT*) : Int\n  fun pthread_kill(PthreadT, Int) : Int\n  fun signal(x0 : Int, x1 : Int -> Void) : Int -> Void\n  fun sigaction(x0 : Int, x1 : Sigaction*, x2 : Sigaction*) : Int\n  fun sigaltstack(x0 : StackT*, x1 : StackT*) : Int\n  fun sigemptyset(SigsetT*) : Int\n  fun sigfillset(SigsetT*) : Int\n  fun sigaddset(SigsetT*, Int) : Int\n  fun sigdelset(SigsetT*, Int) : Int\n  fun sigismember(SigsetT*, Int) : Int\n  fun sigsuspend(SigsetT*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/stdarg.cr",
    "content": "lib LibC\n  struct VaListTag\n    gp_offset : UInt\n    fp_offset : UInt\n    overflow_arg_area : Void*\n    reg_save_area : Void*\n  end\n\n  type VaList = VaListTag[1]\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/stdint.cr",
    "content": "lib LibC\n  alias Int8T = SChar\n  alias Int16T = Short\n  alias Int32T = Int\n  alias Int64T = Long\n  alias UInt8T = Char\n  alias UInt16T = UShort\n  alias UInt32T = UInt\n  alias UInt64T = ULong\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/stdio.cr",
    "content": "require \"./sys/types\"\nrequire \"./stddef\"\n\nlib LibC\n  fun printf(x0 : Char*, ...) : Int\n  fun dprintf(fd : Int, format : Char*, ...) : Int\n  fun rename(x0 : Char*, x1 : Char*) : Int\n  fun snprintf(x0 : Char*, x1 : SizeT, x2 : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/stdlib.cr",
    "content": "require \"./stddef\"\nrequire \"./sys/wait\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun arc4random : UInt32\n  fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void\n  fun arc4random_uniform(x0 : UInt32T) : UInt32T\n  fun atof(x0 : Char*) : Double\n  fun div(x0 : Int, x1 : Int) : DivT\n  fun exit(x0 : Int) : NoReturn\n  fun free(x0 : Void*) : Void\n  fun getenv(x0 : Char*) : Char*\n  fun malloc(x0 : SizeT) : Void*\n  fun mkstemp(x0 : Char*) : Int\n  fun mkstemps(x0 : Char*, x1 : Int) : Int\n  fun putenv(x0 : Char*) : Int\n  fun realloc(x0 : Void*, x1 : SizeT) : Void*\n  fun realpath(x0 : Char*, x1 : Char*) : Char*\n  fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int\n  fun strtof(x0 : Char*, x1 : Char**) : Float\n  fun strtod(x0 : Char*, x1 : Char**) : Double\n  fun unsetenv(x0 : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int\n  fun strcmp(x0 : Char*, x1 : Char*) : Int\n  fun strerror(x0 : Int) : Char*\n  fun strerror_r(Int, Char*, SizeT) : Int\n  fun strlen(x0 : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/epoll.cr",
    "content": "lib LibC\n  EPOLLIN    =  0x001_u32\n  EPOLLOUT   =  0x004_u32\n  EPOLLERR   =  0x008_u32\n  EPOLLHUP   =  0x010_u32\n  EPOLLRDHUP = 0x2000_u32\n\n  EPOLLEXCLUSIVE = 1_u32 << 28\n  EPOLLET        = 1_u32 << 31\n\n  EPOLL_CTL_ADD = 1\n  EPOLL_CTL_DEL = 2\n  EPOLL_CTL_MOD = 3\n\n  EPOLL_CLOEXEC = 0o2000000\n\n  union EpollDataT\n    ptr : Void*\n    fd : Int\n    u32 : UInt32\n    u64 : UInt64\n  end\n\n  @[Packed]\n  struct EpollEvent\n    events : UInt32\n    data : EpollDataT\n  end\n\n  fun epoll_create1(Int) : Int\n  fun epoll_ctl(Int, Int, Int, EpollEvent*) : Int\n  fun epoll_wait(Int, EpollEvent*, Int, Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/eventfd.cr",
    "content": "lib LibC\n  EFD_CLOEXEC = 0o2000000\n\n  fun eventfd(count : UInt, flags : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/file.cr",
    "content": "lib LibC\n  @[Flags]\n  enum FlockOp\n    SH = 0x1\n    EX = 0x2\n    NB = 0x4\n    UN = 0x8\n  end\n\n  fun flock(fd : Int, op : FlockOp) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/mman.cr",
    "content": "require \"./types\"\n\nlib LibC\n  PROT_EXEC  = 0x4\n  PROT_NONE  = 0x0\n  PROT_READ  = 0x1\n  PROT_WRITE = 0x2\n\n  MAP_FIXED     =  0x10\n  MAP_PRIVATE   =     2\n  MAP_SHARED    =     1\n  MAP_ANON      = 0x100\n  MAP_ANONYMOUS = LibC::MAP_ANON\n\n  MAP_FAILED = Pointer(Void).new(-1.to_u64!)\n\n  POSIX_MADV_DONTNEED   = 4\n  POSIX_MADV_NORMAL     = 0\n  POSIX_MADV_RANDOM     = 1\n  POSIX_MADV_SEQUENTIAL = 2\n  POSIX_MADV_WILLNEED   = 3\n  MADV_DONTNEED         = 4\n  MADV_NORMAL           = 0\n  MADV_RANDOM           = 1\n  MADV_SEQUENTIAL       = 2\n  MADV_WILLNEED         = 3\n\n  fun mmap(x0 : Void*, x1 : SizeT, x2 : Int, x3 : Int, x4 : Int, x5 : OffT) : Void*\n  fun mprotect(x0 : Void*, x1 : SizeT, x2 : Int) : Int\n  fun munmap(x0 : Void*, x1 : SizeT) : Int\n  fun madvise(x0 : Void*, x1 : SizeT, x2 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/resource.cr",
    "content": "lib LibC\n  struct RUsage\n    ru_utime : Timeval\n    ru_stime : Timeval\n    ru_maxrss : Long\n    ru_ixrss : Long\n    ru_idrss : Long\n    ru_isrss : Long\n    ru_minflt : Long\n    ru_majflt : Long\n    ru_nswap : Long\n    ru_inblock : Long\n    ru_oublock : Long\n    ru_msgsnd : Long\n    ru_msgrcv : Long\n    ru_nsignals : Long\n    ru_nvcsw : Long\n    ru_nivcsw : Long\n  end\n\n  RUSAGE_SELF     =  0\n  RUSAGE_CHILDREN = -1\n\n  fun getrusage(who : Int, usage : RUsage*) : Int\n\n  alias RlimT = ULongLong\n\n  struct Rlimit\n    rlim_cur : RlimT\n    rlim_max : RlimT\n  end\n\n  RLIMIT_NOFILE = 5\n\n  fun getrlimit(resource : Int, rlim : Rlimit*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/select.cr",
    "content": "require \"./types\"\nrequire \"./time\"\nrequire \"../time\"\nrequire \"../signal\"\n\nlib LibC\n  struct FdSet\n    fds_bits : Long[1024] # FD_SETSIZE (65536) / FD_NFDBITS (64)\n  end\n\n  fun select(x0 : Int, x1 : FdSet*, x2 : FdSet*, x3 : FdSet*, x4 : Timeval*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/socket.cr",
    "content": "require \"./types\"\n\n@[Link(\"socket\")]\nlib LibC\n  SOCK_DGRAM     =      1\n  SOCK_RAW       =      4\n  SOCK_SEQPACKET =      6\n  SOCK_STREAM    =      2\n  SOL_SOCKET     = 0xffff\n  SO_BROADCAST   = 0x0020\n  SO_KEEPALIVE   = 0x0008\n  SO_LINGER      = 0x0080\n  SO_RCVBUF      = 0x1002\n  SO_REUSEADDR   = 0x0004\n  SO_REUSEPORT   = 0x2004\n  SO_SNDBUF      = 0x1001\n  PF_INET        = LibC::AF_INET\n  PF_INET6       = LibC::AF_INET6\n  PF_UNIX        = LibC::AF_UNIX\n  PF_UNSPEC      = LibC::AF_UNSPEC\n  PF_LOCAL       = LibC::PF_UNIX\n  AF_INET        =  2\n  AF_INET6       = 26\n  AF_UNIX        =  1\n  AF_UNSPEC      =  0\n  AF_LOCAL       = LibC::AF_UNIX\n  SHUT_RD        =        0\n  SHUT_RDWR      =        2\n  SHUT_WR        =        1\n  SOCK_CLOEXEC   = 0x080000\n  SOCK_NONBLOCK  = 0x100000\n\n  alias SocklenT = UInt32\n  alias SaFamilyT = UInt16\n\n  struct Sockaddr\n    sa_family : SaFamilyT\n    sa_data : Char[14]\n  end\n\n  struct SockaddrStorage\n    ss_family : SaFamilyT\n    _ss_pad1 : Char[6] # sizeof(Double) - sizeof(SaFamilyT)\n    _ss_align : Double\n    _ss_pad2 : Char[240] # SS_MAXSIZE (256) - sizeof(SaFamilyT) - sizeof(typeof(_ss_align)) - sizeof(Double)\n  end\n\n  struct Linger\n    l_onoff : Int\n    l_linger : Int\n  end\n\n  fun accept(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun accept4(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*, x3 : Int) : Int\n  fun bind(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun connect(x0 : Int, x1 : Sockaddr*, x2 : SocklenT) : Int\n  fun getpeername(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockname(x0 : Int, x1 : Sockaddr*, x2 : SocklenT*) : Int\n  fun getsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT*) : Int\n  fun listen(x0 : Int, x1 : Int) : Int\n  fun recv(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun recvfrom(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT*) : SSizeT\n  fun send(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int) : SSizeT\n  fun sendto(x0 : Int, x1 : Void*, x2 : SizeT, x3 : Int, x4 : Sockaddr*, x5 : SocklenT) : SSizeT\n  fun setsockopt(x0 : Int, x1 : Int, x2 : Int, x3 : Void*, x4 : SocklenT) : Int\n  fun shutdown(x0 : Int, x1 : Int) : Int\n  fun socket(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun socketpair(x0 : Int, x1 : Int, x2 : Int, x3 : Int*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../time\"\n\nlib LibC\n  S_IFMT   = 0xF000\n  S_IFIFO  = 0x1000\n  S_IFCHR  = 0x2000\n  S_IFDIR  = 0x4000\n  S_IFBLK  = 0x6000\n  S_IFREG  = 0x8000\n  S_IFLNK  = 0xA000\n  S_IFSOCK = 0xC000\n  S_IFDOOR = 0xD000\n  S_IFPORT = 0xE000\n\n  S_IRWXU = 0o0700\n  S_IRUSR = 0o0400\n  S_IWUSR = 0o0200\n  S_IXUSR = 0o0100\n  S_IRWXG = 0o0070\n  S_IRGRP = 0o0040\n  S_IWGRP = 0o0020\n  S_IXGRP = 0o0010\n  S_IRWXO = 0o0007\n  S_IROTH = 0o0004\n  S_IWOTH = 0o0002\n  S_IXOTH = 0o0001\n\n  S_ISUID = 0x800\n  S_ISGID = 0x400\n  S_ISVTX = 0x200\n\n  struct Stat\n    st_dev : DevT\n    st_ino : InoT\n    st_mode : ModeT\n    st_nlink : NlinkT\n    st_uid : UidT\n    st_gid : GidT\n    st_rdev : DevT\n    st_size : OffT\n    st_atim : Timespec\n    st_mtim : Timespec\n    st_ctim : Timespec\n    st_blksize : BlksizeT\n    st_blocks : BlkcntT\n    st_fstype : Char[16]\n  end\n\n  fun chmod(x0 : Char*, x1 : ModeT) : Int\n  fun fchmod(x0 : Int, x1 : ModeT) : Int\n  fun fstat(x0 : Int, x1 : Stat*) : Int\n  fun lstat(x0 : Char*, x1 : Stat*) : Int\n  fun mkdir(x0 : Char*, x1 : ModeT) : Int\n  fun mkfifo(x0 : Char*, x1 : ModeT) : Int\n  fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int\n  fun stat(x0 : Char*, x1 : Stat*) : Int\n  fun umask(x0 : ModeT) : ModeT\n  fun utimensat(x0 : Int, x1 : Char*, x2 : Timespec[2], x3 : Int) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/time.cr",
    "content": "require \"./types\"\n\nlib LibC\n  struct Timeval\n    tv_sec : TimeT\n    tv_usec : SusecondsT\n  end\n\n  struct Timezone\n    tz_minuteswest : Int\n    tz_dsttime : Int\n  end\n\n  fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int\n  fun futimens(fd : Int, times : Timespec[2]) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/timerfd.cr",
    "content": "require \"../time\"\n\nlib LibC\n  TFD_NONBLOCK      = 0o0004000\n  TFD_CLOEXEC       = 0o2000000\n  TFD_TIMER_ABSTIME = 1 << 0\n\n  fun timerfd_create(ClockidT, Int) : Int\n  fun timerfd_settime(Int, Int, Itimerspec*, Itimerspec*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/types.cr",
    "content": "require \"../stddef\"\nrequire \"../stdint\"\n\nlib LibC\n  alias BlkcntT = Long\n  alias BlksizeT = Int\n  alias ClockT = Long\n  alias ClockidT = Int\n  alias DevT = ULong\n  alias GidT = UInt\n  alias IdT = Int\n  alias InoT = ULong\n  alias ModeT = UInt\n  alias NlinkT = UInt\n  alias OffT = Long\n  alias PidT = Int\n\n  struct PthreadAttrT\n    __pthread_attrp : Void*\n  end\n\n  struct PthreadCondT\n    __pthread_cond_flag : UInt8[4]\n    __pthread_cond_type : UInt16\n    __pthread_cond_magic : UInt16\n    __pthread_cond_data : UInt64\n  end\n\n  struct PthreadCondattrT\n    __pthread_condattrp : Void*\n  end\n\n  struct PthreadMutexT\n    __pthread_mutex_flag1 : UInt16\n    __pthread_mutex_flag2 : UInt8\n    __pthread_mutex_ceiling : UInt8\n    __pthread_mutex_type : UInt16\n    __pthread_mutex_magic : UInt16\n    __pthread_mutex_lock : UInt64 # actually a union with {UInt8[8]} and {UInt32, UInt32}\n    __pthread_mutex_data : UInt64\n  end\n\n  struct PthreadMutexattrT\n    __pthread_mutexattrp : Void*\n  end\n\n  alias PthreadT = UInt\n\n  alias SSizeT = Long\n  alias SusecondsT = Long\n  alias TimeT = Long\n  alias UidT = UInt\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/un.cr",
    "content": "require \"./socket\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : SaFamilyT\n    sun_path : Char[108]\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/sys/wait.cr",
    "content": "require \"./types\"\nrequire \"../signal\"\n\nlib LibC\n  WNOHANG = 0o100\n\n  fun waitpid(x0 : PidT, x1 : Int*, x2 : Int) : PidT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/termios.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  VINTR  =  0\n  VQUIT  =  1\n  VERASE =  2\n  VKILL  =  3\n  VEOF   =  4\n  VEOL   =  5\n  VMIN   =  4\n  VTIME  =  5\n  VSTART =  8\n  VSTOP  =  9\n  VSUSP  = 10\n\n  IGNBRK = 0o000001\n  BRKINT = 0o000002\n  IGNPAR = 0o000004\n  PARMRK = 0o000010\n  INPCK  = 0o000020\n  ISTRIP = 0o000040\n  INLCR  = 0o000100\n  IGNCR  = 0o000200\n  ICRNL  = 0o000400\n  IXON   = 0o002000\n  IXANY  = 0o004000\n  IXOFF  = 0o010000\n\n  OPOST  = 0o000001\n  ONLCR  = 0o000004\n  OCRNL  = 0o000010\n  ONOCR  = 0o000020\n  ONLRET = 0o000040\n  OFILL  = 0o000100\n  OFDEL  = 0o000200\n  NLDLY  = 0o000400\n  NL0    =        0\n  NL1    = 0o000400\n  CRDLY  = 0o003000\n  CR0    =        0\n  CR1    = 0o001000\n  CR2    = 0o002000\n  CR3    = 0o003000\n  TABDLY = 0o014000\n  TAB0   =        0\n  TAB1   = 0o004000\n  TAB2   = 0o010000\n  TAB3   = 0o014000\n  BSDLY  = 0o020000\n  BS0    =        0\n  BS1    = 0o020000\n  VTDLY  = 0o040000\n  VT0    =        0\n  VT1    = 0o040000\n  FFDLY  = 0o100000\n  FF0    =        0\n  FF1    = 0o100000\n\n  CSIZE  = 0o000060\n  CS5    =        0\n  CS6    = 0o000020\n  CS7    = 0o000040\n  CS8    = 0o000060\n  CSTOPB = 0o000100\n  CREAD  = 0o000200\n  PARENB = 0o000400\n  PARODD = 0o001000\n  HUPCL  = 0o002000\n  CLOCAL = 0o004000\n\n  B0       =  0\n  B50      =  1\n  B75      =  2\n  B110     =  3\n  B134     =  4\n  B150     =  5\n  B200     =  6\n  B300     =  7\n  B600     =  8\n  B1200    =  9\n  B1800    = 10\n  B2400    = 11\n  B4800    = 12\n  B9600    = 13\n  B19200   = 14\n  B38400   = 15\n  B57600   = 16\n  B76800   = 17\n  B115200  = 18\n  B153600  = 19\n  B230400  = 20\n  B307200  = 21\n  B460800  = 22\n  B921600  = 23\n  B1000000 = 24\n  B1152000 = 25\n  B1500000 = 26\n  B2000000 = 27\n  B2500000 = 28\n  B3000000 = 29\n  B3500000 = 30\n  B4000000 = 31\n\n  ISIG   = 0o000001\n  ICANON = 0o000002\n  ECHO   = 0o000010\n  ECHOE  = 0o000020\n  ECHOK  = 0o000040\n  ECHONL = 0o000100\n  NOFLSH = 0o000200\n  TOSTOP = 0o000400\n  IEXTEN = 0o100000\n\n  TCSANOW   = 0x540E\n  TCSADRAIN = 0x540F\n  TCSAFLUSH = 0x5410\n\n  TCIFLUSH  = 0\n  TCIOFLUSH = 2\n  TCOFLUSH  = 1\n\n  TCIOFF = 2\n  TCION  = 3\n  TCOOFF = 0\n  TCOON  = 1\n\n  alias CcT = UChar\n  alias SpeedT = UInt\n  alias TcflagT = UInt\n\n  struct Termios\n    c_iflag : TcflagT\n    c_oflag : TcflagT\n    c_cflag : TcflagT\n    c_lflag : TcflagT\n    c_cc : CcT[19]\n  end\n\n  fun tcgetattr(x0 : Int, x1 : Termios*) : Int\n  fun tcsetattr(x0 : Int, x1 : Int, x2 : Termios*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/time.cr",
    "content": "require \"./sys/types\"\n\nlib LibC\n  CLOCK_MONOTONIC = 4\n  CLOCK_REALTIME  = 3\n\n  struct Tm\n    tm_sec : Int\n    tm_min : Int\n    tm_hour : Int\n    tm_mday : Int\n    tm_mon : Int\n    tm_year : Int\n    tm_wday : Int\n    tm_yday : Int\n    tm_isdst : Int\n  end\n\n  struct Timespec\n    tv_sec : TimeT\n    tv_nsec : Long\n  end\n\n  struct Itimerspec\n    it_interval : Timespec\n    it_value : Timespec\n  end\n\n  fun clock_gettime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun clock_settime(x0 : ClockidT, x1 : Timespec*) : Int\n  fun gmtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun localtime_r(x0 : TimeT*, x1 : Tm*) : Tm*\n  fun mktime(x0 : Tm*) : TimeT\n  fun nanosleep(x0 : Timespec*, x1 : Timespec*) : Int\n  fun tzset : Void\n  fun timegm(x0 : Tm*) : TimeT\n\n  $daylight : Int\n  $timezone : Long\n  $tzname : StaticArray(Char*, 2)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-solaris/c/unistd.cr",
    "content": "require \"./sys/types\"\nrequire \"./stdint\"\n\nlib LibC\n  F_OK = 0\n  R_OK = 4\n  W_OK = 2\n  X_OK = 1\n\n  SC_CLK_TCK          =  3\n  SC_NPROCESSORS_ONLN = 15\n  SC_PAGESIZE         = 11\n\n  fun chroot(dirname : Char*) : Int\n  fun access(x0 : Char*, x1 : Int) : Int\n  fun chdir(x0 : Char*) : Int\n  fun chown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun fchown(x0 : Int, x1 : UidT, x2 : GidT) : Int\n  fun close(x0 : Int) : Int\n  fun dup2(x0 : Int, x1 : Int) : Int\n  fun dup3(x0 : Int, x1 : Int, x2 : Int) : Int\n  fun _exit(x0 : Int) : NoReturn\n  fun execve(file : Char*, argv : Char**, envp : Char**) : Int\n  fun execvp(x0 : Char*, x1 : Char**) : Int\n  fun execvpe(file : Char*, argv : Char**, envp : Char**) : Int\n  fun fdatasync(fd : Int) : Int\n  @[ReturnsTwice]\n  fun fork : PidT\n  fun fsync(fd : Int) : Int\n  fun ftruncate(x0 : Int, x1 : OffT) : Int\n  fun getcwd(x0 : Char*, x1 : SizeT) : Char*\n  fun gethostname(x0 : Char*, x1 : Int) : Int\n  fun getpgid(pid : PidT) : PidT\n  fun getpid : PidT\n  fun getppid : PidT\n  fun getuid : UidT\n  fun setuid(uid : UidT) : Int\n  fun ioctl(x0 : Int, x1 : Int, ...) : Int\n  fun isatty(x0 : Int) : Int\n  fun ttyname_r = __posix_ttyname_r(fd : Int, buf : Char*, buffersize : SizeT) : Int\n  fun lchown(x0 : Char*, x1 : UidT, x2 : GidT) : Int\n  fun link(x0 : Char*, x1 : Char*) : Int\n  fun lockf(x0 : Int, x1 : Int, x2 : OffT) : Int\n  fun lseek(x0 : Int, x1 : OffT, x2 : Int) : OffT\n  fun pipe(x0 : Int*) : Int\n  fun pipe2(x0 : Int*, flags : Int) : Int\n  fun read(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\n  fun pread(x0 : Int, x1 : Void*, x2 : SizeT, x3 : OffT) : SSizeT\n  fun rmdir(x0 : Char*) : Int\n  fun symlink(x0 : Char*, x1 : Char*) : Int\n  fun readlink(path : Char*, buf : Char*, size : SizeT) : SSizeT\n  fun sysconf(x0 : Int) : Long\n  fun unlink(x0 : Char*) : Int\n  fun write(x0 : Int, x1 : Void*, x2 : SizeT) : SSizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/afunix.cr",
    "content": "require \"./ws2def\"\n\nlib LibC\n  struct SockaddrUn\n    sun_family : ADDRESS_FAMILY\n    sun_path : Char[108]\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/basetsd.cr",
    "content": "lib LibC\n  {% if flag?(:bits64) %}\n    alias UINT_PTR = UInt64\n    alias ULONG_PTR = UInt64\n  {% else %}\n    alias UINT_PTR = UInt32\n    alias ULONG_PTR = Uint64\n  {% end %}\n\n  alias DWORD_PTR = ULONG_PTR\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/combaseapi.cr",
    "content": "@[Link(\"ole32\")]\nlib LibC\n  fun CoTaskMemFree(pv : Void*)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/consoleapi.cr",
    "content": "require \"c/winnt\"\n\nlib LibC\n  ENABLE_PROCESSED_INPUT        = 0x0001\n  ENABLE_LINE_INPUT             = 0x0002\n  ENABLE_ECHO_INPUT             = 0x0004\n  ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200\n\n  ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004\n\n  fun GetConsoleMode(hConsoleHandle : HANDLE, lpMode : DWORD*) : BOOL\n  fun SetConsoleMode(hConsoleHandle : HANDLE, dwMode : DWORD) : BOOL\n\n  fun GetConsoleCP : DWORD\n  fun GetConsoleOutputCP : DWORD\n\n  fun ReadConsoleW(\n    hConsoleInput : HANDLE,\n    lpBuffer : Void*,\n    nNumberOfCharsToRead : DWORD,\n    lpNumberOfCharsRead : DWORD*,\n    pInputControl : Void*,\n  ) : BOOL\n\n  CTRL_C_EVENT        = 0\n  CTRL_BREAK_EVENT    = 1\n  CTRL_CLOSE_EVENT    = 2\n  CTRL_LOGOFF_EVENT   = 5\n  CTRL_SHUTDOWN_EVENT = 6\n\n  alias PHANDLER_ROUTINE = DWORD -> BOOL\n\n  fun SetConsoleCtrlHandler(handlerRoutine : PHANDLER_ROUTINE, add : BOOL) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/consoleapi2.cr",
    "content": "lib LibC\n  fun SetConsoleCP(wCodePageID : DWORD) : BOOL\n  fun SetConsoleOutputCP(wCodePageID : DWORD) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/corecrt.cr",
    "content": "lib LibC\n  alias Time32T = Long\n  alias Time64T = Int64\n  alias TimeT = Time64T\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/dbghelp.cr",
    "content": "@[Link(\"DbgHelp\")]\nlib LibC\n  MAX_SYM_NAME = 2000\n\n  SYMOPT_UNDNAME              = 0x00000002\n  SYMOPT_LOAD_LINES           = 0x00000010\n  SYMOPT_FAIL_CRITICAL_ERRORS = 0x00000200\n  SYMOPT_NO_PROMPTS           = 0x00080000\n\n  struct SYMBOL_INFOW\n    sizeOfStruct : DWORD\n    typeIndex : DWORD\n    reserved : DWORD64[2]\n    index : DWORD\n    size : DWORD\n    modBase : DWORD64\n    flags : DWORD\n    value : DWORD64\n    address : DWORD64\n    register : DWORD\n    scope : DWORD\n    tag : DWORD\n    nameLen : DWORD\n    maxNameLen : DWORD\n    name : WCHAR[1] # VLA\n  end\n\n  struct IMAGEHLP_LINEW64\n    sizeOfStruct : DWORD\n    key : Void*\n    lineNumber : DWORD\n    fileName : LPWSTR\n    address : DWORD64\n  end\n\n  alias SYM_TYPE = DWORD\n\n  struct IMAGEHLP_MODULEW64\n    sizeOfStruct : DWORD\n    baseOfImage : DWORD64\n    imageSize : DWORD\n    timeDateStamp : DWORD\n    checkSum : DWORD\n    numSyms : DWORD\n    symType : SYM_TYPE\n    moduleName : WCHAR[32]\n    imageName : WCHAR[256]\n    loadedImageName : WCHAR[256]\n    loadedPdbName : WCHAR[256]\n    cVSig : DWORD\n    cVData : WCHAR[780] # MAX_PATH * 3\n    pdbSig : DWORD\n    pdbSig70 : GUID\n    pdbAge : DWORD\n    pdbUnmatched : BOOL\n    dbgUnmatched : BOOL\n    lineNumbers : BOOL\n    globalSymbols : BOOL\n    typeInfo : BOOL\n    sourceIndexed : BOOL\n    publics : BOOL\n    machineType : DWORD\n    reserved : DWORD\n  end\n\n  fun SymInitializeW(hProcess : HANDLE, userSearchPath : LPWSTR, fInvadeProcess : BOOL) : BOOL\n  fun SymCleanup(hProcess : HANDLE) : BOOL\n  fun SymGetOptions : DWORD\n  fun SymSetOptions(symOptions : DWORD) : DWORD\n  fun SymFromAddrW(hProcess : HANDLE, address : DWORD64, displacement : DWORD64*, symbol : SYMBOL_INFOW*) : BOOL\n  fun SymGetLineFromAddrW64(hProcess : HANDLE, dwAddr : DWORD64, pdwDisplacement : DWORD*, line : IMAGEHLP_LINEW64*) : BOOL\n  fun SymGetModuleInfoW64(hProcess : HANDLE, qwAddr : DWORD64, moduleInfo : IMAGEHLP_MODULEW64*) : BOOL\n\n  # fun SymFunctionTableAccess64(hProcess : HANDLE, addrBase : DWORD64) : Void*\n  fun SymGetModuleBase64(hProcess : HANDLE, qwAddr : DWORD64) : DWORD64\n\n  enum ADDRESS_MODE\n    AddrMode1616\n    AddrMode1632\n    AddrModeReal\n    AddrModeFlat\n  end\n\n  struct ADDRESS64\n    offset : DWORD64\n    segment : WORD\n    mode : ADDRESS_MODE\n  end\n\n  struct KDHELP64\n    thread : DWORD64\n    thCallbackStack : DWORD\n    thCallbackBStore : DWORD\n    nextCallback : DWORD\n    framePointer : DWORD\n    kiCallUserMode : DWORD64\n    keUserCallbackDispatcher : DWORD64\n    systemRangeStart : DWORD64\n    kiUserExceptionDispatcher : DWORD64\n    stackBase : DWORD64\n    stackLimit : DWORD64\n    buildVersion : DWORD\n    retpolineStubFunctionTableSize : DWORD\n    retpolineStubFunctionTable : DWORD64\n    retpolineStubOffset : DWORD\n    retpolineStubSize : DWORD\n    reserved0 : DWORD64[2]\n  end\n\n  struct STACKFRAME64\n    addrPC : ADDRESS64\n    addrReturn : ADDRESS64\n    addrFrame : ADDRESS64\n    addrStack : ADDRESS64\n    addrBStore : ADDRESS64\n    funcTableEntry : Void*\n    params : DWORD64[4]\n    far : BOOL\n    virtual : BOOL\n    reserved : DWORD64[3]\n    kdHelp : KDHELP64\n  end\n\n  IMAGE_FILE_MACHINE_AMD64 = DWORD.new!(0x8664)\n  IMAGE_FILE_MACHINE_ARM64 = DWORD.new!(0xAA64)\n\n  alias PREAD_PROCESS_MEMORY_ROUTINE64 = HANDLE, DWORD64, Void*, DWORD, DWORD* -> BOOL\n  alias PFUNCTION_TABLE_ACCESS_ROUTINE64 = HANDLE, DWORD64 -> Void*\n  alias PGET_MODULE_BASE_ROUTINE64 = HANDLE, DWORD64 -> DWORD64\n  alias PTRANSLATE_ADDRESS_ROUTINE64 = HANDLE, HANDLE, ADDRESS64* -> DWORD64\n\n  fun StackWalk64(\n    machineType : DWORD, hProcess : HANDLE, hThread : HANDLE, stackFrame : STACKFRAME64*, contextRecord : Void*,\n    readMemoryRoutine : PREAD_PROCESS_MEMORY_ROUTINE64, functionTableAccessRoutine : PFUNCTION_TABLE_ACCESS_ROUTINE64,\n    getModuleBaseRoutine : PGET_MODULE_BASE_ROUTINE64, translateAddress : PTRANSLATE_ADDRESS_ROUTINE64,\n  ) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/debugapi.cr",
    "content": "require \"c/win_def\"\n\nlib LibC\n  fun IsDebuggerPresent : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/delayimp.cr",
    "content": "require \"c/libloaderapi\"\nrequire \"c/winnt\"\n\nlib LibC\n  alias RVA = DWORD\n\n  struct ImgDelayDescr\n    grAttrs : DWORD     # attributes\n    rvaDLLName : RVA    # RVA to dll name\n    rvaHmod : RVA       # RVA of module handle\n    rvaIAT : RVA        # RVA of the IAT\n    rvaINT : RVA        # RVA of the INT\n    rvaBoundIAT : RVA   # RVA of the optional bound IAT\n    rvaUnloadIAT : RVA  # RVA of optional copy of original IAT\n    dwTimeStamp : DWORD # 0 if not bound, O.W. date/time stamp of DLL bound to (Old BIND)\n  end\n\n  DLAttrRva = 0x1\n\n  union DelayLoadProc_union\n    szProcName : LPSTR\n    dwOrdinal : DWORD\n  end\n\n  struct DelayLoadProc\n    fImportByName : BOOL\n    union : DelayLoadProc_union\n  end\n\n  struct DelayLoadInfo\n    cb : DWORD            # size of structure\n    pidd : ImgDelayDescr* # raw form of data (everything is there)\n    ppfn : FARPROC*       # points to address of function to load\n    szDll : LPSTR         # name of dll\n    dlp : DelayLoadProc   # name or ordinal of procedure\n    hmodCur : HMODULE     # the hInstance of the library we have loaded\n    pfnCur : FARPROC      # the actual function that will be called\n    dwLastError : DWORD   # error received (if an error notification)\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/direct.cr",
    "content": "require \"c/winnt\"\n\nlib LibC\n  # unused\n  fun _wmkdir(dirname : WCHAR*) : Int\n  fun _wrmdir(dirname : WCHAR*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/errhandlingapi.cr",
    "content": "require \"c/int_safe\"\nrequire \"c/ntstatus\"\n\nlib LibC\n  EXCEPTION_CONTINUE_SEARCH = LONG.new!(0)\n\n  EXCEPTION_ACCESS_VIOLATION = LibC::STATUS_ACCESS_VIOLATION\n  EXCEPTION_STACK_OVERFLOW   = LibC::STATUS_STACK_OVERFLOW\n\n  alias PVECTORED_EXCEPTION_HANDLER = EXCEPTION_POINTERS* -> LONG\n\n  fun GetLastError : DWORD\n  fun SetLastError(dwErrCode : DWORD)\n  fun AddVectoredExceptionHandler(first : DWORD, handler : PVECTORED_EXCEPTION_HANDLER) : Void*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/errno.cr",
    "content": "lib LibC\n  fun _get_errno(value : Int*) : ErrnoT\n  fun _set_errno(value : Int) : ErrnoT\n\n  # source https://docs.microsoft.com/en-us/cpp/c-runtime-library/errno-doserrno-sys-errlist-and-sys-nerr\n  EPERM        =   1\n  ENOENT       =   2\n  ESRCH        =   3\n  EINTR        =   4\n  EIO          =   5\n  ENXIO        =   6\n  E2BIG        =   7\n  ENOEXEC      =   8\n  EBADF        =   9\n  ECHILD       =  10\n  EAGAIN       =  11\n  ENOMEM       =  12\n  EACCES       =  13\n  EFAULT       =  14\n  EBUSY        =  16\n  EEXIST       =  17\n  EXDEV        =  18\n  ENODEV       =  19\n  ENOTDIR      =  20\n  EISDIR       =  21\n  EINVAL       =  22\n  ENFILE       =  23\n  EMFILE       =  24\n  ENOTTY       =  25\n  EFBIG        =  27\n  ENOSPC       =  28\n  ESPIPE       =  29\n  EROFS        =  30\n  EMLINK       =  31\n  EPIPE        =  32\n  EDOM         =  33\n  ERANGE       =  34\n  EDEADLK      =  36\n  ENAMETOOLONG =  38\n  ENOLCK       =  39\n  ENOSYS       =  40\n  ENOTEMPTY    =  41\n  EILSEQ       =  42\n  STRUNCATE    =  80\n  EADDRINUSE   = 100\n  EALREADY     = 103\n  ECONNABORTED = 106\n  ECONNREFUSED = 107\n  ECONNRESET   = 108\n  EINPROGRESS  = 112\n  EISCONN      = 113\n  ELOOP        = 114\n  ENOPROTOOPT  = 123\n\n  alias ErrnoT = Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/fcntl.cr",
    "content": "lib LibC\n  O_RDONLY = 0x0000\n  O_WRONLY = 0x0001\n  O_RDWR   = 0x0002\n  O_APPEND = 0x0008\n\n  O_CREAT = 0x0100\n  O_TRUNC = 0x0200\n  O_EXCL  = 0x0400\n\n  O_TEXT    =  0x4000\n  O_BINARY  =  0x8000\n  O_WTEXT   = 0x10000\n  O_U16TEXT = 0x20000\n  O_U8TEXT  = 0x40000\n\n  O_NOINHERIT   = 0x0080\n  O_TEMPORARY   = 0x0040\n  O_SHORT_LIVED = 0x1000\n  O_OBTAIN_DIR  = 0x2000\n  O_SEQUENTIAL  = 0x0020\n  O_RANDOM      = 0x0010\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/fileapi.cr",
    "content": "require \"c/winnt\"\nrequire \"c/basetsd\"\nrequire \"c/wtypesbase\"\nrequire \"c/minwinbase\"\n\nlib LibC\n  fun GetFullPathNameW(lpFileName : LPWSTR, nBufferLength : DWORD, lpBuffer : LPWSTR, lpFilePart : LPWSTR*) : DWORD\n  fun GetTempPathW(nBufferLength : DWORD, lpBuffer : LPWSTR) : DWORD\n\n  FILE_TYPE_CHAR    = DWORD.new(0x2)\n  FILE_TYPE_DISK    = DWORD.new(0x1)\n  FILE_TYPE_PIPE    = DWORD.new(0x3)\n  FILE_TYPE_UNKNOWN = DWORD.new(0x0)\n\n  fun GetFileType(hFile : HANDLE) : DWORD\n\n  struct WIN32_FILE_ATTRIBUTE_DATA\n    dwFileAttributes : DWORD\n    ftCreationTime : FILETIME\n    ftLastAccessTime : FILETIME\n    ftLastWriteTime : FILETIME\n    nFileSizeHigh : DWORD\n    nFileSizeLow : DWORD\n  end\n\n  struct BY_HANDLE_FILE_INFORMATION\n    dwFileAttributes : DWORD\n    ftCreationTime : FILETIME\n    ftLastAccessTime : FILETIME\n    ftLastWriteTime : FILETIME\n    dwVolumeSerialNumber : DWORD\n    nFileSizeHigh : DWORD\n    nFileSizeLow : DWORD\n    nNumberOfLinks : DWORD\n    nFileIndexHigh : DWORD\n    nFileIndexLow : DWORD\n  end\n\n  fun GetFileInformationByHandle(hFile : HANDLE, lpFileInformation : BY_HANDLE_FILE_INFORMATION*) : BOOL\n  fun GetFileAttributesW(lpFileName : LPWSTR) : DWORD\n  fun SetFileAttributesW(lpFileName : LPWSTR, dwFileAttributes : DWORD) : BOOL\n  fun GetFileAttributesExW(lpFileName : LPWSTR, fInfoLevelId : GET_FILEEX_INFO_LEVELS, lpFileInformation : Void*) : BOOL\n\n  CREATE_NEW        = 1\n  CREATE_ALWAYS     = 2\n  OPEN_EXISTING     = 3\n  OPEN_ALWAYS       = 4\n  TRUNCATE_EXISTING = 5\n\n  FILE_ATTRIBUTE_NORMAL    =  0x80\n  FILE_ATTRIBUTE_TEMPORARY = 0x100\n\n  FILE_FLAG_BACKUP_SEMANTICS    = 0x02000000\n  FILE_FLAG_DELETE_ON_CLOSE     = 0x04000000\n  FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000\n  FILE_FLAG_NO_BUFFERING        = 0x20000000\n  FILE_FLAG_OPEN_REPARSE_POINT  = 0x00200000\n  FILE_FLAG_OVERLAPPED          = 0x40000000\n  FILE_FLAG_RANDOM_ACCESS       = 0x10000000\n  FILE_FLAG_SEQUENTIAL_SCAN     = 0x08000000\n\n  FILE_SHARE_READ   = 0x1\n  FILE_SHARE_WRITE  = 0x2\n  FILE_SHARE_DELETE = 0x4\n\n  DEFAULT_SHARE_MODE = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE\n\n  GENERIC_READ  = 0x80000000\n  GENERIC_WRITE = 0x40000000\n\n  fun CreateFileW(lpFileName : LPWSTR, dwDesiredAccess : DWORD, dwShareMode : DWORD,\n                  lpSecurityAttributes : SECURITY_ATTRIBUTES*, dwCreationDisposition : DWORD,\n                  dwFlagsAndAttributes : DWORD, hTemplateFile : HANDLE) : HANDLE\n  fun DeleteFileW(lpFileName : LPWSTR) : BOOL\n\n  fun ReadFile(hFile : HANDLE, lpBuffer : Void*, nNumberOfBytesToRead : DWORD, lpNumberOfBytesRead : DWORD*, lpOverlapped : OVERLAPPED*) : BOOL\n  fun WriteFile(hFile : HANDLE, lpBuffer : Void*, nNumberOfBytesToWrite : DWORD, lpNumberOfBytesWritten : DWORD*, lpOverlapped : OVERLAPPED*) : BOOL\n  fun SetFilePointerEx(hFile : HANDLE, liDistanceToMove : LARGE_INTEGER, lpNewFilePointer : LARGE_INTEGER*, dwMoveMethod : DWORD) : BOOL\n  fun SetEndOfFile(hFile : HANDLE) : BOOL\n  fun FlushFileBuffers(hFile : HANDLE) : BOOL\n\n  fun CreateDirectoryW(lpPathName : LPWSTR, lpSecurityAttributes : SECURITY_ATTRIBUTES*) : BOOL\n  fun RemoveDirectoryW(lpPathName : LPWSTR) : BOOL\n\n  MAX_PATH = 260\n\n  struct WIN32_FIND_DATAW\n    dwFileAttributes : DWORD\n    ftCreationTime : FILETIME\n    ftLastAccessTime : FILETIME\n    ftLastWriteTime : FILETIME\n    nFileSizeHigh : DWORD\n    nFileSizeLow : DWORD\n    dwReserved0 : DWORD\n    dwReserved1 : DWORD\n    cFileName : WCHAR[MAX_PATH]\n    cAlternateFileName : WCHAR[14]\n  end\n\n  fun FindFirstFileW(lpFileName : LPWSTR, lpFindFileData : WIN32_FIND_DATAW*) : HANDLE\n  fun FindNextFileW(hFindFile : HANDLE, lpFindFileData : WIN32_FIND_DATAW*) : BOOL\n  fun FindClose(hFindFile : HANDLE) : BOOL\n\n  fun LockFileEx(\n    hFile : HANDLE,\n    dwFlags : DWORD,\n    dwReserved : DWORD,\n    nNumberOfBytesToLockLow : DWORD,\n    nNumberOfBytesToLockHigh : DWORD,\n    lpOverlapped : OVERLAPPED*,\n  ) : BOOL\n  fun UnlockFileEx(\n    hFile : HANDLE,\n    dwReserved : DWORD,\n    nNumberOfBytesToUnlockLow : DWORD,\n    nNumberOfBytesToUnlockHigh : DWORD,\n    lpOverlapped : OVERLAPPED*,\n  ) : BOOL\n  fun SetFileTime(hFile : HANDLE, lpCreationTime : FILETIME*,\n                  lpLastAccessTime : FILETIME*, lpLastWriteTime : FILETIME*) : BOOL\n  fun SetFileInformationByHandle(hFile : HANDLE, fileInformationClass : FILE_INFO_BY_HANDLE_CLASS, lpFileInformation : Void*, dwBufferSize : DWORD) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/guiddef.cr",
    "content": "lib LibC\n  struct GUID\n    data1 : UInt32\n    data2 : UInt16\n    data3 : UInt16\n    data4 : UInt8[8]\n  end\nend\n\nstruct LibC::GUID\n  def initialize(@data1 : UInt32, @data2 : UInt16, @data3 : UInt16, @data4 : UInt8[8])\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/handleapi.cr",
    "content": "require \"c/winnt\"\n\nlib LibC\n  INVALID_HANDLE_VALUE = HANDLE.new(-1.to_u64!)\n\n  fun CloseHandle(hObject : HANDLE) : BOOL\n\n  fun DuplicateHandle(hSourceProcessHandle : HANDLE, hSourceHandle : HANDLE,\n                      hTargetProcessHandle : HANDLE, lpTargetHandle : HANDLE*,\n                      dwDesiredAccess : DWORD, bInheritHandle : BOOL, dwOptions : DWORD) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/heapapi.cr",
    "content": "require \"c/winnt\"\n\nlib LibC\n  HEAP_ZERO_MEMORY = 0x00000008\n\n  fun GetProcessHeap : HANDLE\n  fun HeapAlloc(hHeap : HANDLE, dwFlags : DWORD, dwBytes : SizeT) : Void*\n  fun HeapReAlloc(hHeap : HANDLE, dwFlags : DWORD, lpMem : Void*, dwBytes : SizeT) : Void*\n  fun HeapFree(hHeap : HANDLE, dwFlags : DWORD, lpMem : Void*) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/in6addr.cr",
    "content": "lib LibC\n  struct In6Addr\n    u : In6AddrU\n  end\n\n  union In6AddrU\n    byte : Char[16]\n    word : WORD[8]\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/inaddr.cr",
    "content": "lib LibC\n  struct InAddr\n    s_addr : ULong\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/int_safe.cr",
    "content": "lib LibC\n  alias DWORD = UInt32\n  alias DWORD64 = UInt64\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/io.cr",
    "content": "require \"c/stdint\"\n\nlib LibC\n  fun _wexecvp(cmdname : WCHAR*, argv : WCHAR**) : IntPtrT\n  fun _open_osfhandle(osfhandle : HANDLE, flags : LibC::Int) : LibC::Int\n  fun _dup(fd : Int) : Int\n  fun _dup2(fd1 : Int, fd2 : Int) : Int\n\n  # unused\n  fun _get_osfhandle(fd : Int) : IntPtrT\n  fun _close(fd : Int) : Int\n  fun _isatty(fd : Int) : Int\n  fun _write(fd : Int, buffer : UInt8*, count : UInt) : Int\n  fun _read(fd : Int, buffer : UInt8*, count : UInt) : Int\n  fun _lseeki64(fd : Int, offset : Int64, origin : Int) : Int64\n  fun _wopen(filename : WCHAR*, oflag : Int, ...) : Int\n  fun _waccess_s(path : WCHAR*, mode : Int) : ErrnoT\n  fun _wchmod(filename : WCHAR*, pmode : Int) : Int\n  fun _wunlink(filename : WCHAR*) : Int\n  fun _wmktemp_s(template : WCHAR*, sizeInChars : SizeT) : ErrnoT\n  fun _chsize_s(fd : Int, size : Int64) : ErrnoT\n  fun _pipe(pfds : Int*, psize : UInt, textmode : Int) : Int\n  fun _commit(fd : Int) : Int\n  fun _setmode(fd : LibC::Int, mode : LibC::Int) : LibC::Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ioapiset.cr",
    "content": "lib LibC\n  fun GetOverlappedResult(\n    hFile : HANDLE,\n    lpOverlapped : OVERLAPPED*,\n    lpNumberOfBytesTransferred : DWORD*,\n    bWait : BOOL,\n  ) : BOOL\n\n  fun CreateIoCompletionPort(\n    fileHandle : HANDLE,\n    existingCompletionPort : HANDLE,\n    completionKey : ULong*,\n    numberOfConcurrentThreads : DWORD,\n  ) : HANDLE\n\n  fun GetQueuedCompletionStatusEx(\n    completionPort : HANDLE,\n    lpCompletionPortEntries : OVERLAPPED_ENTRY*,\n    ulCount : ULong,\n    ulNumEntriesRemoved : ULong*,\n    dwMilliseconds : DWORD,\n    fAlertable : BOOL,\n  ) : BOOL\n\n  fun PostQueuedCompletionStatus(\n    completionPort : HANDLE,\n    dwNumberOfBytesTransferred : DWORD,\n    dwCompletionKey : ULONG_PTR,\n    lpOverlapped : OVERLAPPED*,\n  ) : BOOL\n\n  fun CancelIoEx(\n    hFile : HANDLE,\n    lpOverlapped : OVERLAPPED*,\n  ) : BOOL\n  fun CancelIo(\n    hFile : HANDLE,\n  ) : BOOL\n\n  fun DeviceIoControl(\n    hDevice : HANDLE,\n    dwIoControlCode : DWORD,\n    lpInBuffer : Void*,\n    nInBufferSize : DWORD,\n    lpOutBuffer : Void*,\n    nOutBufferSize : DWORD,\n    lpBytesReturned : DWORD*,\n    lpOverlapped : OVERLAPPED*,\n  ) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/jobapi2.cr",
    "content": "require \"./winnt\"\n\nlib LibC\n  fun CreateJobObjectW(lpJobAttributes : SECURITY_ATTRIBUTES*, lpName : LPWSTR) : HANDLE\n  fun SetInformationJobObject(hJob : HANDLE, jobObjectInformationClass : JOBOBJECTINFOCLASS, lpJobObjectInformation : Void*, cbJobObjectInformationLength : DWORD) : BOOL\n  fun AssignProcessToJobObject(hJob : HANDLE, hProcess : HANDLE) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/knownfolders.cr",
    "content": "require \"c/guiddef\"\n\nlib LibC\n  FOLDERID_Profile = GUID.new(0x5e6c858f, 0x0e22, 0x4760, UInt8.static_array(0x9a, 0xfe, 0xea, 0x33, 0x17, 0xb6, 0x71, 0x73))\n  FOLDERID_System  = GUID.new(0x1ac14e77, 0x02e7, 0x4e5d, UInt8.static_array(0xb7, 0x44, 0x2e, 0xb1, 0xae, 0x51, 0x98, 0xb7))\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/libloaderapi.cr",
    "content": "require \"c/winnt\"\n\n@[Link(\"kernel32\")]\nlib LibC\n  alias FARPROC = Void*\n\n  LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008\n\n  fun LoadLibraryExW(lpLibFileName : LPWSTR, hFile : HANDLE, dwFlags : DWORD) : HMODULE\n  fun FreeLibrary(hLibModule : HMODULE) : BOOL\n\n  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 0x00000002\n  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS       = 0x00000004\n\n  fun GetModuleHandleExW(dwFlags : DWORD, lpModuleName : LPWSTR, phModule : HMODULE*) : BOOL\n\n  fun GetProcAddress(hModule : HMODULE, lpProcName : LPSTR) : FARPROC\n\n  fun GetModuleFileNameW(hModule : HMODULE, lpFilename : LPWSTR, nSize : DWORD) : DWORD\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/lm.cr",
    "content": "require \"c/winnt\"\n\n@[Link(\"netapi32\")]\nlib LibC\n  alias NET_API_STATUS = DWORD\n\n  NERR_Success = NET_API_STATUS.new!(0)\n\n  enum NETSETUP_JOIN_STATUS\n    NetSetupUnknownStatus = 0\n    NetSetupUnjoined\n    NetSetupWorkgroupName\n    NetSetupDomainName\n  end\n\n  fun NetGetJoinInformation(lpServer : LPWSTR, lpNameBuffer : LPWSTR*, bufferType : NETSETUP_JOIN_STATUS*) : NET_API_STATUS\n\n  struct USER_INFO_4\n    usri4_name : LPWSTR\n    usri4_password : LPWSTR\n    usri4_password_age : DWORD\n    usri4_priv : DWORD\n    usri4_home_dir : LPWSTR\n    usri4_comment : LPWSTR\n    usri4_flags : DWORD\n    usri4_script_path : LPWSTR\n    usri4_auth_flags : DWORD\n    usri4_full_name : LPWSTR\n    usri4_usr_comment : LPWSTR\n    usri4_parms : LPWSTR\n    usri4_workstations : LPWSTR\n    usri4_last_logon : DWORD\n    usri4_last_logoff : DWORD\n    usri4_acct_expires : DWORD\n    usri4_max_storage : DWORD\n    usri4_units_per_week : DWORD\n    usri4_logon_hours : BYTE*\n    usri4_bad_pw_count : DWORD\n    usri4_num_logons : DWORD\n    usri4_logon_server : LPWSTR\n    usri4_country_code : DWORD\n    usri4_code_page : DWORD\n    usri4_user_sid : SID*\n    usri4_primary_group_id : DWORD\n    usri4_profile : LPWSTR\n    usri4_home_dir_drive : LPWSTR\n    usri4_password_expired : DWORD\n  end\n\n  struct USER_INFO_10\n    usri10_name : LPWSTR\n    usri10_comment : LPWSTR\n    usri10_usr_comment : LPWSTR\n    usri10_full_name : LPWSTR\n  end\n\n  fun NetUserGetInfo(servername : LPWSTR, username : LPWSTR, level : DWORD, bufptr : BYTE**) : NET_API_STATUS\n  fun NetApiBufferFree(buffer : Void*) : NET_API_STATUS\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/malloc.cr",
    "content": "lib LibC\n  fun _resetstkoflw : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/memoryapi.cr",
    "content": "lib LibC\n  MEM_COMMIT     = 0x00001000\n  MEM_RESERVE    = 0x00002000\n  MEM_RESET      = 0x00080000\n  MEM_RESET_UNDO = 0x01000000\n\n  fun VirtualAlloc(lpAddress : Void*, dwSize : SizeT, flAllocationType : DWORD, flProtect : DWORD) : Void*\n\n  MEM_DECOMMIT = 0x4000\n  MEM_RELEASE  = 0x8000\n\n  fun VirtualFree(lpAddress : Void*, dwSize : SizeT, dwFreeType : DWORD) : BOOL\n  fun VirtualProtect(lpAddress : Void*, dwSize : SizeT, flNewProtect : DWORD, lpfOldProtect : DWORD*) : BOOL\n  fun VirtualQuery(lpAddress : Void*, lpBuffer : MEMORY_BASIC_INFORMATION*, dwLength : SizeT) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/minwinbase.cr",
    "content": "# part of winbase\nlib LibC\n  struct OVERLAPPED_OFFSET\n    offset : DWORD\n    offsetHigh : DWORD\n  end\n\n  union OVERLAPPED_UNION\n    offset : OVERLAPPED_OFFSET\n    pointer : Void*\n  end\n\n  struct OVERLAPPED\n    internal : ULONG_PTR\n    internalHigh : ULONG_PTR\n    union : OVERLAPPED_UNION\n    hEvent : HANDLE\n  end\n\n  struct OVERLAPPED_ENTRY\n    lpCompletionKey : ULONG_PTR\n    lpOverlapped : OVERLAPPED*\n    internal : ULONG_PTR\n    dwNumberOfBytesTransferred : DWORD\n  end\n\n  struct FILETIME\n    dwLowDateTime : DWORD\n    dwHighDateTime : DWORD\n  end\n\n  struct SYSTEMTIME\n    wYear : WORD\n    wMonth : WORD\n    wDayOfWeek : WORD\n    wDay : WORD\n    wHour : WORD\n    wMinute : WORD\n    wSecond : WORD\n    wMilliseconds : WORD\n  end\n\n  enum GET_FILEEX_INFO_LEVELS\n    GetFileExInfoStandard\n    GetFileExMaxInfoLevel\n  end\n\n  enum FILE_INFO_BY_HANDLE_CLASS\n    FileBasicInfo        = 0\n    FileAttributeTagInfo = 9\n  end\n\n  LOCKFILE_FAIL_IMMEDIATELY = DWORD.new(0x00000001)\n  LOCKFILE_EXCLUSIVE_LOCK   = DWORD.new(0x00000002)\n\n  STATUS_PENDING = 0x103\n  STILL_ACTIVE   = STATUS_PENDING\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/mswsock.cr",
    "content": "require \"./guiddef\"\n\n@[Link(\"mswsock\")]\nlib LibC\n  SO_UPDATE_ACCEPT_CONTEXT  = 0x700B\n  SO_UPDATE_CONNECT_CONTEXT = 0x7010\n\n  alias AcceptEx = Proc(SOCKET, SOCKET, Void*, DWORD, DWORD, DWORD, DWORD*, OVERLAPPED*, BOOL)\n  WSAID_ACCEPTEX = GUID.new(0xb5367df1, 0xcbac, 0x11cf, UInt8.static_array(0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92))\n\n  alias ConnectEx = Proc(SOCKET, Sockaddr*, Int, Void*, DWORD, DWORD*, OVERLAPPED*, BOOL)\n  WSAID_CONNECTEX = GUID.new(0x25a207b9, 0xddf3, 0x4660, UInt8.static_array(0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e))\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/netioapi.cr",
    "content": "require \"./in6addr\"\nrequire \"./inaddr\"\nrequire \"./stdint\"\n\n@[Link(\"iphlpapi\")]\nlib LibC\n  NDIS_IF_MAX_STRING_SIZE = 256\n  IF_NAMESIZE             = LibC::NDIS_IF_MAX_STRING_SIZE + 1 # need one more byte for terminating '\\0'\n\n  fun if_nametoindex(ifname : Char*) : UInt\n  fun if_indextoname(ifindex : UInt, ifname : LibC::Char*) : LibC::Char*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ntdef.cr",
    "content": "lib LibC\n  struct UNICODE_STRING\n    length : USHORT\n    maximumLength : USHORT\n    buffer : LPWSTR\n  end\n\n  struct OBJECT_ATTRIBUTES\n    length : ULONG\n    rootDirectory : HANDLE\n    objectName : UNICODE_STRING*\n    attributes : ULONG\n    securityDescriptor : Void*\n    securityQualityOfService : Void*\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ntdll.cr",
    "content": "require \"c/ntdef\"\nrequire \"c/winnt\"\nrequire \"c/ntstatus\"\n\n@[Link(\"ntdll\")]\nlib LibNTDLL\n  alias NTSTATUS = LibC::NTSTATUS\n  alias ACCESS_MASK = LibC::DWORD\n\n  GENERIC_ALL = 0x10000000_u32\n\n  alias NtCreateWaitCompletionPacketProc = Proc(LibC::HANDLE*, ACCESS_MASK, LibC::OBJECT_ATTRIBUTES*, NTSTATUS)\n  alias NtAssociateWaitCompletionPacketProc = Proc(LibC::HANDLE, LibC::HANDLE, LibC::HANDLE, Void*, Void*, NTSTATUS, LibC::ULONG*, LibC::BOOLEAN*, NTSTATUS)\n  alias NtCancelWaitCompletionPacketProc = Proc(LibC::HANDLE, LibC::BOOLEAN, NTSTATUS)\n\n  fun NtCreateWaitCompletionPacket(\n    waitCompletionPacketHandle : LibC::HANDLE*,\n    desiredAccess : ACCESS_MASK,\n    objectAttributes : LibC::OBJECT_ATTRIBUTES*,\n  ) : NTSTATUS\n\n  fun NtAssociateWaitCompletionPacket(\n    waitCompletionPacketHandle : LibC::HANDLE,\n    ioCompletionHandle : LibC::HANDLE,\n    targetObjectHandle : LibC::HANDLE,\n    keyContext : Void*,\n    apcContext : Void*,\n    ioStatus : NTSTATUS,\n    ioStatusInformation : LibC::ULONG*,\n    alreadySignaled : LibC::BOOLEAN*,\n  ) : NTSTATUS\n\n  fun NtCancelWaitCompletionPacket(\n    waitCompletionPacketHandle : LibC::HANDLE,\n    removeSignaledPacket : LibC::BOOLEAN,\n  ) : NTSTATUS\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ntifs.cr",
    "content": "require \"c/wdm\"\n\nlib LibC\n  SYMLINK_FLAG_RELATIVE = 0x00000001\n\n  struct REPARSE_DATA_BUFFER_struct1\n    substituteNameOffset : UShort\n    substituteNameLength : UShort\n    printNameOffset : UShort\n    printNameLength : UShort\n    flags : ULong\n    pathBuffer : WCHAR[1]\n  end\n\n  struct REPARSE_DATA_BUFFER_struct2\n    substituteNameOffset : UShort\n    substituteNameLength : UShort\n    printNameOffset : UShort\n    printNameLength : UShort\n    pathBuffer : WCHAR[1]\n  end\n\n  struct REPARSE_DATA_BUFFER_struct3\n    dataBuffer : UChar[1]\n  end\n\n  union REPARSE_DATA_BUFFER_union\n    symbolicLinkReparseBuffer : REPARSE_DATA_BUFFER_struct1\n    mountPointReparseBuffer : REPARSE_DATA_BUFFER_struct2\n    genericReparseBuffer : REPARSE_DATA_BUFFER_struct3\n  end\n\n  struct REPARSE_DATA_BUFFER\n    reparseTag : ULong\n    reparseDataLength : UShort\n    reserved : UShort\n    dummyUnionName : REPARSE_DATA_BUFFER_union\n  end\n\n  struct FILE_MODE_INFORMATION\n    mode : ULONG\n  end\n\n  fun NtQueryInformationFile(\n    fileHandle : HANDLE,\n    ioStatusBlock : IO_STATUS_BLOCK*,\n    fileInformation : Void*,\n    length : ULONG,\n    fileInformationClass : FILE_INFORMATION_CLASS,\n  ) : NTSTATUS\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ntsecapi.cr",
    "content": "require \"./winnt\"\n\n@[Link(\"advapi32\")]\nlib LibC\n  fun RtlGenRandom = SystemFunction036(random_buffer : Void*, random_buffer_length : ULong) : BOOLEAN\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ntstatus.cr",
    "content": "require \"lib_c\"\n\nlib LibC\n  alias NTSTATUS = ULONG\n\n  STATUS_SUCCESS                 = 0x00000000_u32\n  STATUS_FATAL_APP_EXIT          = 0x40000015_u32\n  STATUS_DATATYPE_MISALIGNMENT   = 0x80000002_u32\n  STATUS_BREAKPOINT              = 0x80000003_u32\n  STATUS_ACCESS_VIOLATION        = 0xC0000005_u32\n  STATUS_ILLEGAL_INSTRUCTION     = 0xC000001D_u32\n  STATUS_FLOAT_DIVIDE_BY_ZERO    = 0xC000008E_u32\n  STATUS_FLOAT_INEXACT_RESULT    = 0xC000008F_u32\n  STATUS_FLOAT_INVALID_OPERATION = 0xC0000090_u32\n  STATUS_FLOAT_OVERFLOW          = 0xC0000091_u32\n  STATUS_FLOAT_UNDERFLOW         = 0xC0000093_u32\n  STATUS_PRIVILEGED_INSTRUCTION  = 0xC0000096_u32\n  STATUS_STACK_OVERFLOW          = 0xC00000FD_u32\n  STATUS_CANCELLED               = 0xC0000120_u32\n  STATUS_CONTROL_C_EXIT          = 0xC000013A_u32\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/process.cr",
    "content": "require \"lib_c\"\n\nlib LibC\n  fun _beginthreadex(security : Void*, stack_size : UInt, start_address : Void* -> UInt, arglist : Void*, initflag : UInt, thrdaddr : UInt*) : Void*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/processenv.cr",
    "content": "require \"c/winnt\"\n\n@[Link(\"kernel32\")]\nlib LibC\n  fun GetStdHandle(nStdHandle : DWORD) : HANDLE\n\n  fun GetCurrentDirectoryW(nBufferLength : DWORD, lpBuffer : LPWSTR) : DWORD\n  fun SetCurrentDirectoryW(lpPathname : LPWSTR) : BOOL\n\n  fun GetEnvironmentVariableW(lpName : LPWSTR, lpBuffer : LPWSTR, nSize : DWORD) : DWORD\n  fun GetEnvironmentStringsW : LPWCH\n  fun FreeEnvironmentStringsW(lpszEnvironmentBlock : LPWCH) : BOOL\n  fun SetEnvironmentVariableW(lpName : LPWSTR, lpValue : LPWSTR) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr",
    "content": "require \"./basetsd\"\nrequire \"c/wtypesbase\"\nrequire \"c/sdkddkver\"\n\nlib LibC\n  CREATE_SUSPENDED           = 0x00000004\n  CREATE_UNICODE_ENVIRONMENT = 0x00000400\n\n  struct PROCESS_INFORMATION\n    hProcess : HANDLE\n    hThread : HANDLE\n    dwProcessId : DWORD\n    dwThreadId : DWORD\n  end\n\n  struct STARTUPINFOW\n    cb : DWORD\n    lpReserved : LPWSTR\n    lpDesktop : LPWSTR\n    lpTitle : LPWSTR\n    dwX : DWORD\n    dwY : DWORD\n    dwXSize : DWORD\n    dwYSize : DWORD\n    dwXCountChars : DWORD\n    dwYCountChars : DWORD\n    dwFillAttribute : DWORD\n    dwFlags : DWORD\n    wShowWindow : WORD\n    cbReserved2 : WORD\n    lpReserved2 : BYTE*\n    hStdInput : HANDLE\n    hStdOutput : HANDLE\n    hStdError : HANDLE\n  end\n\n  fun NtCurrentTeb : NT_TIB*\n  fun GetCurrentThread : HANDLE\n  fun GetCurrentThreadId : DWORD\n  {% if LibC::WIN32_WINNT >= LibC::WIN32_WINNT_WIN8 %}\n    fun GetCurrentThreadStackLimits(lowLimit : ULONG_PTR*, highLimit : ULONG_PTR*) : Void\n  {% end %}\n  fun GetCurrentProcess : HANDLE\n  fun GetCurrentProcessId : DWORD\n  fun OpenProcess(dwDesiredAccess : DWORD, bInheritHandle : BOOL, dwProcessId : DWORD) : HANDLE\n  fun GetExitCodeProcess(hProcess : HANDLE, lpExitCode : DWORD*) : BOOL\n  fun ExitProcess(uExitCode : UInt) : NoReturn\n  fun TerminateProcess(hProcess : HANDLE, uExitCode : UInt) : BOOL\n  fun CreateProcessW(lpApplicationName : LPWSTR, lpCommandLine : LPWSTR,\n                     lpProcessAttributes : SECURITY_ATTRIBUTES*, lpThreadAttributes : SECURITY_ATTRIBUTES*,\n                     bInheritHandles : BOOL, dwCreationFlags : DWORD,\n                     lpEnvironment : Void*, lpCurrentDirectory : LPWSTR,\n                     lpStartupInfo : STARTUPINFOW*, lpProcessInformation : PROCESS_INFORMATION*) : BOOL\n  {% if LibC::WIN32_WINNT >= LibC::WIN32_WINNT_WIN10 %}\n    fun SetThreadDescription(hThread : HANDLE, lpThreadDescription : LPWSTR) : LONG\n  {% end %}\n  fun SetThreadStackGuarantee(stackSizeInBytes : DWORD*) : BOOL\n  fun GetProcessTimes(hProcess : HANDLE, lpCreationTime : FILETIME*, lpExitTime : FILETIME*,\n                      lpKernelTime : FILETIME*, lpUserTime : FILETIME*) : BOOL\n  fun SwitchToThread : BOOL\n  fun QueueUserAPC(pfnAPC : PAPCFUNC, hThread : HANDLE, dwData : ULONG_PTR) : DWORD\n\n  fun GetThreadContext(hThread : HANDLE, lpContext : CONTEXT*) : DWORD\n  fun ResumeThread(hThread : HANDLE) : DWORD\n  fun SuspendThread(hThread : HANDLE) : DWORD\n\n  TLS_OUT_OF_INDEXES = 0xFFFFFFFF_u32\n\n  fun TlsAlloc : DWORD\n  fun TlsGetValue(dwTlsIndex : DWORD) : Void*\n  fun TlsSetValue(dwTlsIndex : DWORD, lpTlsValue : Void*) : BOOL\n\n  PROCESS_QUERY_INFORMATION = 0x0400\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/profileapi.cr",
    "content": "# part of windows\n\nlib LibC\n  fun QueryPerformanceCounter(lpPerformanceCount : LARGE_INTEGER*) : BOOL\n  fun QueryPerformanceFrequency(lpFrequency : LARGE_INTEGER*) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/regapix.cr",
    "content": "lib LibC\n  type HKEY = Void*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/sddl.cr",
    "content": "require \"c/winnt\"\n\nlib LibC\n  fun ConvertSidToStringSidW(sid : SID*, stringSid : LPWSTR*) : BOOL\n  fun ConvertStringSidToSidW(stringSid : LPWSTR, sid : SID**) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/sdkddkver.cr",
    "content": "lib LibC\n  WIN32_WINNT_WIN7  = 0x0601\n  WIN32_WINNT_WIN8  = 0x0602\n  WIN32_WINNT_WIN10 = 0x0A00 # includes Windows 11 too\n\n  # add other version flags here, or use mechanisms other than flags\n  {% if flag?(:win7) %}\n    WIN32_WINNT = {{ WIN32_WINNT_WIN7 }}\n  {% else %}\n    # TODO - detect host version using `cmd.exe /c ver`, environment variable or some other way.\n    WIN32_WINNT = {{ WIN32_WINNT_WIN10 }}\n  {% end %}\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/security.cr",
    "content": "require \"c/winnt\"\n\n@[Link(\"secur32\")]\nlib LibC\n  enum EXTENDED_NAME_FORMAT\n    NameUnknown          =  0\n    NameFullyQualifiedDN =  1\n    NameSamCompatible    =  2\n    NameDisplay          =  3\n    NameUniqueId         =  6\n    NameCanonical        =  7\n    NameUserPrincipal    =  8\n    NameCanonicalEx      =  9\n    NameServicePrincipal = 10\n    NameDnsDomain        = 12\n    NameGivenName        = 13\n    NameSurname          = 14\n  end\n\n  fun TranslateNameW(lpAccountName : LPWSTR, accountNameFormat : EXTENDED_NAME_FORMAT, desiredNameFormat : EXTENDED_NAME_FORMAT, lpTranslatedName : LPWSTR, nSize : ULong*) : BOOLEAN\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/shlobj_core.cr",
    "content": "require \"c/guiddef\"\n\n@[Link(\"shell32\")]\nlib LibC\n  fun SHGetKnownFolderPath(rfid : GUID*, dwFlags : DWORD, hToken : HANDLE, ppszPath : LPWSTR*) : DWORD\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/signal.cr",
    "content": "lib LibC\n  SIGINT   =  2\n  SIGILL   =  4\n  SIGFPE   =  8\n  SIGSEGV  = 11\n  SIGTERM  = 15\n  SIGBREAK = 21\n  SIGABRT  = 22\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/stdarg.cr",
    "content": "lib LibC\n  type VaList = Void*\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/stddef.cr",
    "content": "lib LibC\n  alias SizeT = UInt64\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/stdint.cr",
    "content": "lib LibC\n  alias IntPtrT = Int64\n  alias UIntPtrT = UInt64\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/stdio.cr",
    "content": "require \"./stddef\"\n\n{% if flag?(:msvc) %}\n  @[Link(\"legacy_stdio_definitions\")]\n{% end %}\nlib LibC\n  # unused\n  fun printf(format : Char*, ...) : Int\n  fun rename(old : Char*, new : Char*) : Int\n  fun snprintf(buffer : Char*, count : SizeT, format : Char*, ...) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/stdlib.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  struct DivT\n    quot : Int\n    rem : Int\n  end\n\n  fun exit(status : Int) : NoReturn\n  fun _exit(status : Int) : NoReturn\n  fun free(ptr : Void*) : Void\n  fun malloc(size : SizeT) : Void*\n  fun realloc(ptr : Void*, size : SizeT) : Void*\n\n  alias InvalidParameterHandler = WCHAR*, WCHAR*, WCHAR*, UInt, UIntPtrT ->\n  fun _set_invalid_parameter_handler(pNew : InvalidParameterHandler) : InvalidParameterHandler\n\n  # unused\n  fun strtof(nptr : Char*, endptr : Char**) : Float\n  fun strtod(nptr : Char*, endptr : Char**) : Double\n  fun atof(nptr : Char*) : Double\n  fun div(numer : Int, denom : Int) : DivT\n  fun putenv(string : Char*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/string.cr",
    "content": "require \"./stddef\"\n\nlib LibC\n  fun memchr(x0 : Void*, c : Int, n : SizeT) : Void*\n  fun memcmp(s1 : Void*, s2 : Void*, n : SizeT) : Int\n  fun strcmp(s1 : Char*, s2 : Char*) : Int\n  fun strerror(errnum : Int) : Char*\n  fun strlen(s : Char*) : SizeT\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/stringapiset.cr",
    "content": "require \"lib_c\"\nrequire \"c/win_def\"\nrequire \"c/winnt\"\n\nlib LibC\n  # this is only for the `wmain` entry point where Crystal's standard library is\n  # unusable, all other code should use `String.from_utf16` instead\n  fun WideCharToMultiByte(\n    codePage : UInt, dwFlags : DWORD, lpWideCharStr : LPWSTR,\n    cchWideChar : Int, lpMultiByteStr : LPSTR, cbMultiByte : Int,\n    lpDefaultChar : CHAR*, lpUsedDefaultChar : BOOL*,\n  ) : Int\n\n  # this was for the now removed delay-load helper, all other code should use\n  # `String#to_utf16` instead\n  fun MultiByteToWideChar(\n    codePage : UInt, dwFlags : DWORD, lpMultiByteStr : LPSTR,\n    cbMultiByte : Int, lpWideCharStr : LPWSTR, cchWideChar : Int,\n  ) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/synchapi.cr",
    "content": "require \"c/basetsd\"\nrequire \"c/int_safe\"\nrequire \"c/winbase\"\nrequire \"c/wtypesbase\"\n\nlib LibC\n  # the meanings of these fields are documented not in the Win32 API docs but in\n  # https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/displaying-a-critical-section\n  struct CRITICAL_SECTION\n    debugInfo : Void* # PRTL_CRITICAL_SECTION_DEBUG\n    lockCount : LONG\n    recursionCount : LONG\n    owningThread : HANDLE\n    lockSemaphore : HANDLE\n    spinCount : UInt64\n  end\n\n  struct CONDITION_VARIABLE\n    ptr : Void*\n  end\n\n  fun InitializeCriticalSectionAndSpinCount(lpCriticalSection : CRITICAL_SECTION*, dwSpinCount : DWORD) : BOOL\n  fun DeleteCriticalSection(lpCriticalSection : CRITICAL_SECTION*)\n  fun EnterCriticalSection(lpCriticalSection : CRITICAL_SECTION*)\n  fun TryEnterCriticalSection(lpCriticalSection : CRITICAL_SECTION*) : BOOL\n  fun LeaveCriticalSection(lpCriticalSection : CRITICAL_SECTION*)\n\n  fun InitializeConditionVariable(conditionVariable : CONDITION_VARIABLE*)\n  fun SleepConditionVariableCS(conditionVariable : CONDITION_VARIABLE*, criticalSection : CRITICAL_SECTION*, dwMilliseconds : DWORD) : BOOL\n  fun WakeConditionVariable(conditionVariable : CONDITION_VARIABLE*)\n  fun WakeAllConditionVariable(conditionVariable : CONDITION_VARIABLE*)\n\n  fun Sleep(dwMilliseconds : DWORD)\n  fun WaitForSingleObject(hHandle : HANDLE, dwMilliseconds : DWORD) : DWORD\n\n  alias PTIMERAPCROUTINE = (Void*, DWORD, DWORD) ->\n  CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002_u32\n\n  fun CreateWaitableTimerExW(lpTimerAttributes : SECURITY_ATTRIBUTES*, lpTimerName : LPWSTR, dwFlags : DWORD, dwDesiredAccess : DWORD) : HANDLE\n  fun SetWaitableTimer(hTimer : HANDLE, lpDueTime : LARGE_INTEGER*, lPeriod : LONG, pfnCompletionRoutine : PTIMERAPCROUTINE*, lpArgToCompletionRoutine : Void*, fResume : BOOL) : BOOL\n  fun CancelWaitableTimer(hTimer : HANDLE) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/sys/stat.cr",
    "content": "require \"./types\"\nrequire \"../corecrt\"\n\nlib LibC\n  S_IFMT  = 0xF000\n  S_IFDIR = 0x4000\n  S_IFCHR = 0x2000\n  S_IFIFO = 0x1000\n  S_IFREG = 0x8000\n\n  S_IREAD  = 0x0100\n  S_IWRITE = 0x0080\n  S_IEXEC  = 0x0040\n\n  struct Stat64\n    st_dev : DevT\n    st_ino : InoT\n    st_mode : UShort\n    st_nlink : Short\n    st_uid : Short\n    st_gid : Short\n    st_rdev : DevT\n    st_size : Int64\n    st_atime : Time64T\n    st_mtime : Time64T\n    st_ctime : Time64T\n  end\n\n  # unused\n  fun _wstat64(path : WCHAR*, buffer : Stat64*) : Int\n  fun _fstat64(fd : Int, buffer : Stat64*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/sys/types.cr",
    "content": "lib LibC\n  alias InoT = UShort\n  alias DevT = UInt\n  alias OffT = Long\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/sys/utime.cr",
    "content": "require \"c/corecrt\"\n\nlib LibC\n  struct Utimbuf64\n    actime : Time64T\n    modtime : Time64T\n  end\n\n  # unused\n  fun _wutime64(filename : WCHAR*, times : Utimbuf64*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/sysinfoapi.cr",
    "content": "require \"c/winbase\"\nrequire \"c/sdkddkver\"\n\nlib LibC\n  fun GetSystemTimeAsFileTime(time : FILETIME*)\n  {% if LibC::WIN32_WINNT >= LibC::WIN32_WINNT_WIN8 %}\n    fun GetSystemTimePreciseAsFileTime(time : FILETIME*)\n  {% end %}\n\n  fun GetNativeSystemInfo(system_info : SYSTEM_INFO*)\n\n  struct PROCESSOR_INFO\n    wProcessorArchitecture : WORD\n    wReserved : WORD\n  end\n\n  union OEM_PROCESSOR_INFO\n    dwOemId : DWORD\n    processorInfo : PROCESSOR_INFO\n  end\n\n  struct SYSTEM_INFO\n    oemProcessorInfo : OEM_PROCESSOR_INFO\n    dwPageSize : DWORD\n    lpMinimumApplicationAddress : Void*\n    lpMaximumApplicationAddress : Void*\n    dwActiveProcessorMask : DWORD*\n    dwNumberOfProcessors : DWORD\n    dwProcessorType : DWORD\n    dwAllocationGranularity : DWORD\n    wProcessorLevel : WORD\n    wProcessorRevision : WORD\n  end\n\n  fun GetComputerNameExW(computer_name_format : COMPUTER_NAME_FORMAT,\n                         machine_name : LPWSTR,\n                         machine_name_size : DWORD*) : BOOLEAN\n\n  enum COMPUTER_NAME_FORMAT\n    ComputerNameNetBIOS\n    ComputerNameDnsHostname\n    ComputerNameDnsDomain\n    ComputerNameDnsFullyQualified\n    ComputerNamePhysicalNetBIOS\n    ComputerNamePhysicalDnsHostname\n    ComputerNamePhysicalDomain\n    ComputerNamePhysicalDnsFullyQualified\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/timezoneapi.cr",
    "content": "require \"c/winnt\"\nrequire \"c/winbase\"\n\nlib LibC\n  struct TIME_ZONE_INFORMATION\n    bias : LONG\n    standardName : StaticArray(WCHAR, 32)\n    standardDate : SYSTEMTIME\n    standardBias : LONG\n    daylightName : StaticArray(WCHAR, 32)\n    daylightDate : SYSTEMTIME\n    daylightBias : LONG\n  end\n\n  struct DYNAMIC_TIME_ZONE_INFORMATION\n    bias : LONG\n    standardName : StaticArray(WCHAR, 32)\n    standardDate : SYSTEMTIME\n    standardBias : LONG\n    daylightName : StaticArray(WCHAR, 32)\n    daylightDate : SYSTEMTIME\n    daylightBias : LONG\n    timeZoneKeyName : StaticArray(WCHAR, 128)\n    dynamicDaylightTimeDisabled : BOOLEAN\n  end\n\n  TIME_ZONE_ID_INVALID  = 0xffffffff_u32\n  TIME_ZONE_ID_UNKNOWN  =          0_u32\n  TIME_ZONE_ID_STANDARD =          1_u32\n  TIME_ZONE_ID_DAYLIGHT =          2_u32\n\n  fun GetTimeZoneInformation(tz_info : TIME_ZONE_INFORMATION*) : DWORD\n  fun SetTimeZoneInformation(tz_info : TIME_ZONE_INFORMATION*) : BOOL\n\n  fun GetDynamicTimeZoneInformation(pTimeZoneInformation : DYNAMIC_TIME_ZONE_INFORMATION*) : DWORD\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/tlhelp32.cr",
    "content": "lib LibC\n  TH32CS_SNAPPROCESS = 0x00000002\n\n  struct PROCESSENTRY32W\n    dwSize : DWORD\n    cntUsage : DWORD\n    th32ProcessID : DWORD\n    th32DefaultHeapID : ULONG_PTR\n    th32ModuleID : DWORD\n    cntThreads : DWORD\n    th32ParentProcessID : DWORD\n    pcPriClassBase : LONG\n    dwFlags : DWORD\n    szExeFile : WCHAR[MAX_PATH]\n  end\n\n  fun CreateToolhelp32Snapshot(dwFlags : DWORD, th32ProcessID : DWORD) : HANDLE\n  fun Process32FirstW(hSnapshot : HANDLE, lppe : PROCESSENTRY32W*) : BOOL\n  fun Process32NextW(hSnapshot : HANDLE, lppe : PROCESSENTRY32W*) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/userenv.cr",
    "content": "require \"c/winnt\"\n\n@[Link(\"userenv\")]\nlib LibC\n  fun GetProfilesDirectoryW(lpProfileDir : LPWSTR, lpcchSize : DWORD*) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/wdm.cr",
    "content": "require \"c/ntstatus\"\n\nlib LibC\n  struct IO_STATUS_BLOCK__union\n    status : NTSTATUS\n    pointer : Void*\n  end\n\n  struct IO_STATUS_BLOCK\n    union : IO_STATUS_BLOCK__union\n    information : ULONG*\n  end\n\n  enum FILE_INFORMATION_CLASS\n    FileModeInformation = 16\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/win_def.cr",
    "content": "lib LibC\n  alias WORD = UInt16\n  alias BOOL = Int32\n  alias BYTE = UChar\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winbase.cr",
    "content": "require \"c/winnt\"\nrequire \"c/win_def\"\nrequire \"c/int_safe\"\nrequire \"c/minwinbase\"\n\nlib LibC\n  alias HLOCAL = Void*\n\n  fun LocalFree(hMem : HLOCAL)\n\n  FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100_u32\n  FORMAT_MESSAGE_IGNORE_INSERTS  = 0x00000200_u32\n  FORMAT_MESSAGE_FROM_STRING     = 0x00000400_u32\n  FORMAT_MESSAGE_FROM_HMODULE    = 0x00000800_u32\n  FORMAT_MESSAGE_FROM_SYSTEM     = 0x00001000_u32\n  FORMAT_MESSAGE_ARGUMENT_ARRAY  = 0x00002000_u32\n  FORMAT_MESSAGE_MAX_WIDTH_MASK  = 0x000000FF_u32\n\n  STD_INPUT_HANDLE  = 0xFFFFFFF6_u32\n  STD_OUTPUT_HANDLE = 0xFFFFFFF5_u32\n  STD_ERROR_HANDLE  = 0xFFFFFFF4_u32\n\n  fun FormatMessageA(dwFlags : DWORD, lpSource : Void*, dwMessageId : DWORD, dwLanguageId : DWORD,\n                     lpBuffer : LPSTR, nSize : DWORD, arguments : Void*) : DWORD\n  fun FormatMessageW(dwFlags : DWORD, lpSource : Void*, dwMessageId : DWORD, dwLanguageId : DWORD,\n                     lpBuffer : LPWSTR, nSize : DWORD, arguments : Void*) : DWORD\n\n  SYMBOLIC_LINK_FLAG_DIRECTORY                 = 0x1\n  SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2\n\n  PIPE_ACCESS_OUTBOUND = 0x00000002\n\n  fun CreateHardLinkW(lpFileName : LPWSTR, lpExistingFileName : LPWSTR, lpSecurityAttributes : SECURITY_ATTRIBUTES*) : BOOL\n  fun CreateSymbolicLinkW(lpSymlinkFileName : LPWSTR, lpTargetFileName : LPWSTR, dwFlags : DWORD) : BOOLEAN\n  fun CreateNamedPipeA(lpName : LPSTR, dwOpenMode : DWORD, dwPipeMode : DWORD, nMaxInstances : DWORD,\n                       nOutBufferSize : DWORD, nInBufferSize : DWORD, nDefaultTimeOut : DWORD, lpSecurityAttributes : SECURITY_ATTRIBUTES*) : HANDLE\n\n  FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1_u8\n\n  fun SetFileCompletionNotificationModes(fileHandle : HANDLE, flags : UChar) : BOOL\n\n  fun GetEnvironmentVariableW(lpName : LPWSTR, lpBuffer : LPWSTR, nSize : DWORD) : DWORD\n  fun GetEnvironmentStringsW : LPWCH\n  fun FreeEnvironmentStringsW(lpszEnvironmentBlock : LPWCH) : BOOL\n  fun SetEnvironmentVariableW(lpName : LPWSTR, lpValue : LPWSTR) : BOOL\n\n  INFINITE = 0xFFFFFFFF\n\n  WAIT_OBJECT_0      = 0x00000000_u32\n  WAIT_IO_COMPLETION = 0x000000C0_u32\n  WAIT_TIMEOUT       = 0x00000102_u32\n  WAIT_FAILED        = 0xFFFFFFFF_u32\n\n  STARTF_USESTDHANDLES = 0x00000100\n\n  MOVEFILE_REPLACE_EXISTING      =  0x1_u32\n  MOVEFILE_COPY_ALLOWED          =  0x2_u32\n  MOVEFILE_DELAY_UNTIL_REBOOT    =  0x4_u32\n  MOVEFILE_WRITE_THROUGH         =  0x8_u32\n  MOVEFILE_CREATE_HARDLINK       = 0x10_u32\n  MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20_u32\n\n  fun MoveFileExW(lpExistingFileName : LPWSTR, lpNewFileName : LPWSTR, dwFlags : DWORD) : BOOL\n\n  fun GetBinaryTypeW(lpApplicationName : LPWSTR, lpBinaryType : DWORD*) : BOOL\n\n  struct FILE_BASIC_INFO\n    creationTime : LARGE_INTEGER\n    lastAccessTime : LARGE_INTEGER\n    lastWriteTime : LARGE_INTEGER\n    changeTime : LARGE_INTEGER\n    fileAttributes : DWORD\n  end\n\n  struct FILE_ATTRIBUTE_TAG_INFO\n    fileAttributes : DWORD\n    reparseTag : DWORD\n  end\n\n  fun GetFileInformationByHandleEx(hFile : HANDLE, fileInformationClass : FILE_INFO_BY_HANDLE_CLASS, lpFileInformation : Void*, dwBufferSize : DWORD) : BOOL\n\n  fun ReOpenFile(hOriginalFile : HANDLE, dwDesiredAccess : DWORD, dwShareMode : DWORD, dwFlagsAndAttributes : DWORD) : HANDLE\n\n  fun LookupAccountNameW(lpSystemName : LPWSTR, lpAccountName : LPWSTR, sid : SID*, cbSid : DWORD*, referencedDomainName : LPWSTR, cchReferencedDomainName : DWORD*, peUse : SID_NAME_USE*) : BOOL\n  fun LookupAccountSidW(lpSystemName : LPWSTR, sid : SID*, name : LPWSTR, cchName : DWORD*, referencedDomainName : LPWSTR, cchReferencedDomainName : DWORD*, peUse : SID_NAME_USE*) : BOOL\n\n  fun GetProcessAffinityMask(hProcess : HANDLE, lpProcessAffinityMask : DWORD_PTR*, lpSystemAffinityMask : DWORD_PTR*) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/wincrypt.cr",
    "content": "require \"c/win_def\"\nrequire \"c/minwinbase\"\nrequire \"c/winnt\"\n\n@[Link(\"crypt32\")]\nlib LibC\n  alias HCERTSTORE = Void*\n  alias HCRYPTPROV_LEGACY = Void*\n\n  struct CERT_NAME_BLOB\n    cbData : DWORD\n    pbData : BYTE*\n  end\n\n  struct CRYPT_INTEGER_BLOB\n    cbData : DWORD\n    pbData : BYTE*\n  end\n\n  struct CRYPT_OBJID_BLOB\n    cbData : DWORD\n    pbData : BYTE*\n  end\n\n  struct CRYPT_BIT_BLOB\n    cbData : DWORD\n    pbData : BYTE*\n    cUnusedBits : DWORD\n  end\n\n  struct CRYPT_ALGORITHM_IDENTIFIER\n    pszObjId : LPSTR\n    parameters : CRYPT_OBJID_BLOB\n  end\n\n  struct CERT_PUBLIC_KEY_INFO\n    algorithm : CRYPT_ALGORITHM_IDENTIFIER\n    publicKey : CRYPT_BIT_BLOB\n  end\n\n  struct CERT_EXTENSION\n    pszObjId : LPSTR\n    fCritical : BOOL\n    value : CRYPT_OBJID_BLOB\n  end\n\n  struct CERT_INFO\n    dwVersion : DWORD\n    serialNumber : CRYPT_INTEGER_BLOB\n    signatureAlgorithm : CRYPT_ALGORITHM_IDENTIFIER\n    issuer : CERT_NAME_BLOB\n    notBefore : FILETIME\n    notAfter : FILETIME\n    subject : CERT_NAME_BLOB\n    subjectPublicKeyInfo : CERT_PUBLIC_KEY_INFO\n    issuerUniqueId : CRYPT_BIT_BLOB\n    subjectUniqueId : CRYPT_BIT_BLOB\n    cExtension : DWORD\n    rgExtension : CERT_EXTENSION*\n  end\n\n  struct CERT_USAGE\n    cUsageIdentifier : DWORD\n    rgpszUsageIdentifier : LPSTR*\n  end\n\n  X509_ASN_ENCODING   = 0x00000001\n  PKCS_7_ASN_ENCODING = 0x00010000\n\n  struct CERT_CONTEXT\n    dwCertEncodingType : DWORD\n    pbCertEncoded : BYTE*\n    cbCertEncoded : DWORD\n    pCertInfo : CERT_INFO*\n    hCertStore : HCERTSTORE\n  end\n\n  fun CertOpenSystemStoreW(hProv : HCRYPTPROV_LEGACY, szSubsystemProtocol : LPWSTR) : HCERTSTORE\n  fun CertCloseStore(hCertStore : HCERTSTORE, dwFlags : DWORD) : BOOL\n\n  fun CertEnumCertificatesInStore(hCertStore : HCERTSTORE, pPrevCertContext : CERT_CONTEXT*) : CERT_CONTEXT*\n  fun CertGetEnhancedKeyUsage(pCertContext : CERT_CONTEXT*, dwFlags : DWORD, pUsage : CERT_USAGE*, pcbUsage : DWORD*) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/windows.cr",
    "content": "require \"c/winnt\"\n\nrequire \"c/profileapi\"\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winioctl.cr",
    "content": "lib LibC\n  FSCTL_SET_REPARSE_POINT = 0x000900A4 # CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)\n  FSCTL_GET_REPARSE_POINT = 0x000900A8 # CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winnls.cr",
    "content": "lib LibC\n  CP_UTF8 = 65001\n\n  fun IsValidCodePage(codePage : DWORD) : BOOL\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winnt.cr",
    "content": "require \"c/int_safe\"\n\nlib LibC\n  alias BOOLEAN = BYTE\n  alias LONG = Int32\n  alias ULONG = UInt32\n  alias USHORT = UInt16\n  alias LARGE_INTEGER = Int64\n\n  alias CHAR = UChar\n  alias WCHAR = UInt16\n  alias LPSTR = CHAR*\n  alias LPWSTR = WCHAR*\n  alias LPWCH = WCHAR*\n\n  alias HANDLE = Void*\n  alias HMODULE = Void*\n\n  alias PAPCFUNC = ULONG_PTR ->\n\n  INVALID_FILE_ATTRIBUTES      = DWORD.new!(-1)\n  FILE_ATTRIBUTE_DIRECTORY     =  0x10\n  FILE_ATTRIBUTE_HIDDEN        =   0x2\n  FILE_ATTRIBUTE_READONLY      =   0x1\n  FILE_ATTRIBUTE_REPARSE_POINT = 0x400\n  FILE_ATTRIBUTE_SYSTEM        =   0x4\n\n  DELETE = 0x00010000_u32\n\n  FILE_WRITE_DATA       = 0x00000002_u32\n  FILE_APPEND_DATA      = 0x00000004_u32\n  FILE_READ_ATTRIBUTES  = 0x00000080_u32\n  FILE_WRITE_ATTRIBUTES = 0x00000100_u32\n  FILE_GENERIC_READ     = 0x00120089_u32\n  FILE_GENERIC_WRITE    = 0x00120116_u32\n\n  FILE_SYNCHRONOUS_IO_ALERT    = 0x00000010\n  FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020\n\n  MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000\n\n  IO_REPARSE_TAG_SYMLINK = 0xA000000C_u32\n  IO_REPARSE_TAG_AF_UNIX = 0x80000023_u32\n\n  # Memory protection constants\n  PAGE_READWRITE =  0x04\n  PAGE_GUARD     = 0x100\n\n  PROCESS_QUERY_LIMITED_INFORMATION =     0x1000\n  SYNCHRONIZE                       = 0x00100000\n\n  DUPLICATE_CLOSE_SOURCE = 0x00000001\n  DUPLICATE_SAME_ACCESS  = 0x00000002\n\n  enum REGSAM\n    # Required to query the values of a registry key.\n    QUERY_VALUE = 0x0001\n\n    # Required to create, delete, or set a registry value.\n    SET_VALUE = 0x0002\n\n    # Required to create a subkey of a registry key.\n    CREATE_SUB_KEY = 0x0004\n\n    # Required to enumerate the subkeys of a registry key.\n    ENUMERATE_SUB_KEYS = 0x0008\n\n    # Required to request change notifications for a registry key or for subkeys of a registry key.\n    NOTIFY = 0x0010\n\n    # Reserved for system use.\n    CREATE_LINK = 0x0020\n\n    # Indicates that an application on 64-bit Windows should operate on the 32-bit registry view. This flag is ignored by 32-bit Windows.\n    # This flag must be combined using the OR operator with the other flags in this table that either query or access registry values.\n    # Windows 2000: This flag is not supported.\n    WOW64_32KEY = 0x0200\n\n    # Indicates that an application on 64-bit Windows should operate on the 64-bit registry view. This flag is ignored by 32-bit Windows.\n    # This flag must be combined using the OR operator with the other flags in this table that either query or access registry values.\n    # Windows 2000: This flag is not supported.\n    WOW64_64KEY = 0x0100\n\n    WOW64_RES = 0x0300\n\n    # Combines the `STANDARD_RIGHTS_READ`, `QUERY_VALUE`, `ENUMERATE_SUB_KEYS`, and `NOTIFY` values.\n    # (STANDARD_RIGHTS_READ | QUERY_VALUE | ENUMERATE_SUB_KEYS | NOTIFY) & ~SYNCHRONIZE\n    READ = 0x20019\n\n    # Combines the `STANDARD_RIGHTS_REQUIRED`, `QUERY_VALUE`, `SET_VALUE`, `CREATE_SUB_KEY`, `ENUMERATE_SUB_KEYS`, `NOTIFY`, and `CREATE_LINK` access rights.\n    # (STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) & ~SYNCHRONIZE\n    ALL_ACCESS = 0xf003f\n\n    # Equivalent to `READ`.\n    # KEY_READ & ~SYNCHRONIZE\n    EXECUTE = 0x20019\n\n    # Combines the STANDARD_RIGHTS_WRITE, `KEY_SET_VALUE`, and `KEY_CREATE_SUB_KEY` access rights.\n    # (STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & ~SYNCHRONIZE\n    WRITE = 0x20006\n  end\n\n  struct SID_IDENTIFIER_AUTHORITY\n    value : BYTE[6]\n  end\n\n  struct SID\n    revision : BYTE\n    subAuthorityCount : BYTE\n    identifierAuthority : SID_IDENTIFIER_AUTHORITY\n    subAuthority : DWORD[1]\n  end\n\n  enum SID_NAME_USE\n    SidTypeUser           = 1\n    SidTypeGroup\n    SidTypeDomain\n    SidTypeAlias\n    SidTypeWellKnownGroup\n    SidTypeDeletedAccount\n    SidTypeInvalid\n    SidTypeUnknown\n    SidTypeComputer\n    SidTypeLabel\n    SidTypeLogonSession\n  end\n\n  enum JOBOBJECTINFOCLASS\n    AssociateCompletionPortInformation = 7\n    ExtendedLimitInformation           = 9\n  end\n\n  struct JOBOBJECT_BASIC_LIMIT_INFORMATION\n    perProcessUserTimeLimit : LARGE_INTEGER\n    perJobUserTimeLimit : LARGE_INTEGER\n    limitFlags : DWORD\n    minimumWorkingSetSize : SizeT\n    maximumWorkingSetSize : SizeT\n    activeProcessLimit : DWORD\n    affinity : ULONG_PTR\n    priorityClass : DWORD\n    schedulingClass : DWORD\n  end\n\n  struct IO_COUNTERS\n    readOperationCount : ULongLong\n    writeOperationCount : ULongLong\n    otherOperationCount : ULongLong\n    readTransferCount : ULongLong\n    writeTransferCount : ULongLong\n    otherTransferCount : ULongLong\n  end\n\n  struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION\n    basicLimitInformation : JOBOBJECT_BASIC_LIMIT_INFORMATION\n    ioInfo : IO_COUNTERS\n    processMemoryLimit : SizeT\n    jobMemoryLimit : SizeT\n    peakProcessMemoryUsed : SizeT\n    peakJobMemoryUsed : SizeT\n  end\n\n  struct JOBOBJECT_ASSOCIATE_COMPLETION_PORT\n    completionKey : Void*\n    completionPort : HANDLE\n  end\n\n  JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000\n\n  JOB_OBJECT_MSG_EXIT_PROCESS          = 7\n  JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS = 8\n\n  {% if flag?(:x86_64) %}\n    struct CONTEXT\n      p1Home : DWORD64\n      p2Home : DWORD64\n      p3Home : DWORD64\n      p4Home : DWORD64\n      p5Home : DWORD64\n      p6Home : DWORD64\n      contextFlags : DWORD\n      mxCsr : DWORD\n      segCs : WORD\n      segDs : WORD\n      segEs : WORD\n      segFs : WORD\n      segGs : WORD\n      segSs : WORD\n      eFlags : DWORD\n      dr0 : DWORD64\n      dr1 : DWORD64\n      dr2 : DWORD64\n      dr3 : DWORD64\n      dr6 : DWORD64\n      dr7 : DWORD64\n      rax : DWORD64\n      rcx : DWORD64\n      rdx : DWORD64\n      rbx : DWORD64\n      rsp : DWORD64\n      rbp : DWORD64\n      rsi : DWORD64\n      rdi : DWORD64\n      r8 : DWORD64\n      r9 : DWORD64\n      r10 : DWORD64\n      r11 : DWORD64\n      r12 : DWORD64\n      r13 : DWORD64\n      r14 : DWORD64\n      r15 : DWORD64\n      rip : DWORD64\n      fltSave : UInt8[512]           # DUMMYUNIONNAME\n      vectorRegister : UInt8[16][26] # M128A[26]\n      vectorControl : DWORD64\n      debugControl : DWORD64\n      lastBranchToRip : DWORD64\n      lastBranchFromRip : DWORD64\n      lastExceptionToRip : DWORD64\n      lastExceptionFromRip : DWORD64\n    end\n  {% elsif flag?(:aarch64) %}\n    struct ARM64_NT_NEON128_DUMMYSTRUCTNAME\n      low : ULongLong\n      high : LongLong\n    end\n\n    union ARM64_NT_NEON128\n      dummystructname : ARM64_NT_NEON128_DUMMYSTRUCTNAME\n      d : Double[2]\n      s : Float[4]\n      h : WORD[8]\n      b : BYTE[16]\n    end\n\n    struct CONTEXT\n      contextFlags : DWORD\n      cpsr : DWORD\n      x : DWORD64[31] # x29 = fp, x30 = lr\n      sp : DWORD64\n      pc : DWORD64\n      v : ARM64_NT_NEON128[32]\n      fpcr : DWORD\n      fpsr : DWORD\n      bcr : DWORD[8]\n      bvr : DWORD64[8]\n      wcr : DWORD[8]\n      wvr : DWORD64[8]\n    end\n  {% end %}\n\n  {% if flag?(:x86_64) %}\n    CONTEXT_AMD64 = DWORD.new!(0x00100000)\n\n    CONTEXT_CONTROL         = CONTEXT_AMD64 | 0x00000001\n    CONTEXT_INTEGER         = CONTEXT_AMD64 | 0x00000002\n    CONTEXT_SEGMENTS        = CONTEXT_AMD64 | 0x00000004\n    CONTEXT_FLOATING_POINT  = CONTEXT_AMD64 | 0x00000008\n    CONTEXT_DEBUG_REGISTERS = CONTEXT_AMD64 | 0x00000010\n\n    CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT\n  {% elsif flag?(:i386) %}\n    CONTEXT_i386 = DWORD.new!(0x00010000i64)\n    CONTEXT_i486 = DWORD.new!(0x00010000i64)\n\n    CONTEXT_CONTROL            = CONTEXT_i386 | 0x00000001\n    CONTEXT_INTEGER            = CONTEXT_i386 | 0x00000002\n    CONTEXT_SEGMENTS           = CONTEXT_i386 | 0x00000004\n    CONTEXT_FLOATING_POINT     = CONTEXT_i386 | 0x00000008\n    CONTEXT_DEBUG_REGISTERS    = CONTEXT_i386 | 0x00000010\n    CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x00000020\n\n    CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS\n  {% elsif flag?(:aarch64) %}\n    CONTEXT_ARM64 = DWORD.new!(0x00400000)\n\n    CONTEXT_ARM64_CONTROL        = CONTEXT_ARM64 | 0x1\n    CONTEXT_ARM64_INTEGER        = CONTEXT_ARM64 | 0x2\n    CONTEXT_ARM64_FLOATING_POINT = CONTEXT_ARM64 | 0x4\n\n    CONTEXT_FULL = CONTEXT_ARM64_CONTROL | CONTEXT_ARM64_INTEGER | CONTEXT_ARM64_FLOATING_POINT\n  {% end %}\n\n  fun RtlCaptureContext(contextRecord : CONTEXT*)\n\n  struct EXCEPTION_RECORD64\n    exceptionCode : DWORD\n    exceptionFlags : DWORD\n    exceptionRecord : DWORD64\n    exceptionAddress : DWORD64\n    numberParameters : DWORD\n    __unusedAlignment : DWORD\n    exceptionInformation : DWORD64[15]\n  end\n\n  struct EXCEPTION_POINTERS\n    exceptionRecord : EXCEPTION_RECORD64*\n    contextRecord : CONTEXT*\n  end\n\n  struct NT_TIB\n    exceptionList : Void*\n    stackBase : Void*\n    stackLimit : Void*\n    subSystemTib : Void*\n    fiberData : Void*\n    arbitraryUserPointer : Void*\n    pvSelf : NT_TIB*\n  end\n\n  struct MEMORY_BASIC_INFORMATION\n    baseAddress : Void*\n    allocationBase : Void*\n    allocationProtect : DWORD\n    partitionId : WORD\n    regionSize : SizeT\n    state : DWORD\n    protect : DWORD\n    type : DWORD\n  end\n\n  IMAGE_NT_SIGNATURE = 0x00004550 # PE00\n\n  struct IMAGE_DOS_HEADER\n    e_magic : WORD\n    e_cblp : WORD\n    e_cp : WORD\n    e_crlc : WORD\n    e_cparhdr : WORD\n    e_minalloc : WORD\n    e_maxalloc : WORD\n    e_ss : WORD\n    e_sp : WORD\n    e_csum : WORD\n    e_ip : WORD\n    e_cs : WORD\n    e_lfarlc : WORD\n    e_ovno : WORD\n    e_res : WORD[4]\n    e_oemid : WORD\n    e_oeminfo : WORD\n    e_res2 : WORD[10]\n    e_lfanew : LONG\n  end\n\n  struct IMAGE_FILE_HEADER\n    machine : WORD\n    numberOfSections : WORD\n    timeDateStamp : DWORD\n    pointerToSymbolTable : DWORD\n    numberOfSymbols : DWORD\n    sizeOfOptionalHeader : WORD\n    characteristics : WORD\n  end\n\n  struct IMAGE_DATA_DIRECTORY\n    virtualAddress : DWORD\n    size : DWORD\n  end\n\n  struct IMAGE_OPTIONAL_HEADER64\n    magic : WORD\n    majorLinkerVersion : BYTE\n    minorLinkerVersion : BYTE\n    sizeOfCode : DWORD\n    sizeOfInitializedData : DWORD\n    sizeOfUninitializedData : DWORD\n    addressOfEntryPoint : DWORD\n    baseOfCode : DWORD\n    imageBase : ULongLong\n    sectionAlignment : DWORD\n    fileAlignment : DWORD\n    majorOperatingSystemVersion : WORD\n    minorOperatingSystemVersion : WORD\n    majorImageVersion : WORD\n    minorImageVersion : WORD\n    majorSubsystemVersion : WORD\n    minorSubsystemVersion : WORD\n    win32VersionValue : DWORD\n    sizeOfImage : DWORD\n    sizeOfHeaders : DWORD\n    checkSum : DWORD\n    subsystem : WORD\n    dllCharacteristics : WORD\n    sizeOfStackReserve : ULongLong\n    sizeOfStackCommit : ULongLong\n    sizeOfHeapReserve : ULongLong\n    sizeOfHeapCommit : ULongLong\n    loaderFlags : DWORD\n    numberOfRvaAndSizes : DWORD\n    dataDirectory : IMAGE_DATA_DIRECTORY[16] # IMAGE_NUMBEROF_DIRECTORY_ENTRIES\n  end\n\n  struct IMAGE_NT_HEADERS64\n    signature : DWORD\n    fileHeader : IMAGE_FILE_HEADER\n    optionalHeader : IMAGE_OPTIONAL_HEADER64\n  end\n\n  IMAGE_DIRECTORY_ENTRY_EXPORT =  0\n  IMAGE_DIRECTORY_ENTRY_IMPORT =  1\n  IMAGE_DIRECTORY_ENTRY_IAT    = 12\n\n  IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040\n\n  struct IMAGE_SECTION_HEADER\n    name : BYTE[8]\n    virtualSize : DWORD\n    virtualAddress : DWORD\n    sizeOfRawData : DWORD\n    pointerToRawData : DWORD\n    pointerToRelocations : DWORD\n    pointerToLinenumbers : DWORD\n    numberOfRelocations : WORD\n    numberOfLinenumbers : WORD\n    characteristics : DWORD\n  end\n\n  struct IMAGE_EXPORT_DIRECTORY\n    characteristics : DWORD\n    timeDateStamp : DWORD\n    majorVersion : WORD\n    minorVersion : WORD\n    name : DWORD\n    base : DWORD\n    numberOfFunctions : DWORD\n    numberOfNames : DWORD\n    addressOfFunctions : DWORD\n    addressOfNames : DWORD\n    addressOfNameOrdinals : DWORD\n  end\n\n  struct IMAGE_IMPORT_BY_NAME\n    hint : WORD\n    name : CHAR[1]\n  end\n\n  struct IMAGE_SYMBOL_n_name\n    short : DWORD\n    long : DWORD\n  end\n\n  union IMAGE_SYMBOL_n\n    shortName : BYTE[8]\n    name : IMAGE_SYMBOL_n_name\n  end\n\n  IMAGE_SYM_CLASS_EXTERNAL = 2\n  IMAGE_SYM_CLASS_STATIC   = 3\n\n  @[Packed]\n  struct IMAGE_SYMBOL\n    n : IMAGE_SYMBOL_n\n    value : DWORD\n    sectionNumber : Short\n    type : WORD\n    storageClass : BYTE\n    numberOfAuxSymbols : BYTE\n  end\n\n  union IMAGE_THUNK_DATA64_u1\n    forwarderString : ULongLong\n    function : ULongLong\n    ordinal : ULongLong\n    addressOfData : ULongLong\n  end\n\n  struct IMAGE_THUNK_DATA64\n    u1 : IMAGE_THUNK_DATA64_u1\n  end\n\n  IMAGE_ORDINAL_FLAG64 = 0x8000000000000000_u64\n\n  alias IMAGE_NT_HEADERS = IMAGE_NT_HEADERS64\n  alias IMAGE_THUNK_DATA = IMAGE_THUNK_DATA64\n  IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG64\n\n  TIMER_QUERY_STATE  = 0x0001\n  TIMER_MODIFY_STATE = 0x0002\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winreg.cr",
    "content": "lib LibC\n  alias LSTATUS = DWORD\n\n  enum RegistryRoutineFlags\n    NONE                       = 0\n    SZ                         = 1\n    EXPAND_SZ                  = 2\n    BINARY                     = 3\n    DWORD                      = 4\n    DWORD_LITTLE_ENDIAN        = DWORD\n    DWORD_BIG_ENDIAN           =  5\n    LINK                       =  6\n    MULTI_SZ                   =  7\n    RESOURCE_LIST              =  8\n    FULL_RESOURCE_DESCRIPTOR   =  9\n    RESOURCE_REQUIREMENTS_LIST = 10\n    QWORD                      = 11\n    QWORD_LITTLE_ENDIAN        = QWORD\n  end\n\n  HKEY_CLASSES_ROOT     = Pointer(Void).new(0x80000000_u64).as(HKEY)\n  HKEY_CURRENT_USER     = Pointer(Void).new(0x80000001_u64).as(HKEY)\n  HKEY_LOCAL_MACHINE    = Pointer(Void).new(0x80000002_u64).as(HKEY)\n  HKEY_USERS            = Pointer(Void).new(0x80000003_u64).as(HKEY)\n  HKEY_PERFORMANCE_DATA = Pointer(Void).new(0x80000004_u64).as(HKEY)\n  HKEY_CURRENT_CONFIG   = Pointer(Void).new(0x80000005_u64).as(HKEY)\n  HKEY_DYN_DATA         = Pointer(Void).new(0x8000006_u64).as(HKEY)\n\n  fun RegOpenKeyExW(hKey : HKEY, lpSubKey : LPWSTR, ulOptions : DWORD, samDesired : REGSAM, phkResult : HKEY*) : LSTATUS\n  fun RegCloseKey(hKey : HKEY) : LSTATUS\n  fun RegEnumKeyExW(hKey : HKEY, dwIndex : DWORD,\n                    lpName : LPWSTR, lpcchName : DWORD*,\n                    lpReserved : DWORD*,\n                    lpClass : LPWSTR, lpcchClass : DWORD*,\n                    lpftLastWriteTime : FILETIME*) : LSTATUS\n  fun RegQueryInfoKeyW(hKey : HKEY, lpClass : LPSTR, lpcchClass : DWORD*, lpReserved : DWORD*,\n                       lpcSubKeys : DWORD*, lpcbMaxSubKeyLen : DWORD*,\n                       lpcbMaxClassLen : DWORD*,\n                       lpcValues : DWORD*, lpcbMaxValueNameLen : DWORD*, lpcbMaxValueLen : DWORD*,\n                       lpcbSecurityDescriptor : DWORD*, lpftLastWriteTime : FILETIME*) : DWORD\n  fun RegQueryValueExW(hKey : HKEY, lpValueName : LPWSTR, lpReserved : DWORD*, lpType : RegistryRoutineFlags*, lpData : BYTE*, lpcbData : DWORD*) : LSTATUS\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winsock2.cr",
    "content": "require \"./ws2def\"\nrequire \"./basetsd\"\nrequire \"./guiddef\"\nrequire \"./winbase\"\n\n@[Link(\"ws2_32\")]\nlib LibC\n  alias SOCKET = UINT_PTR\n\n  # -2147195266 is the value after conversion to long, actual value 2147772030 with type unsigned\n  FIONBIO = -2147195266\n\n  struct WSAData\n    wVersion : WORD\n    wHighVersion : WORD\n    szDescription : Char[257]\n    szSystemStatus : Char[129]\n    iMaxSockets : UInt16\n    iMaxUdpDg : UInt16\n    lpVendorInfo : Char*\n  end\n\n  NS_DNS = 12_u32\n\n  INVALID_SOCKET = ~SOCKET.new(0)\n  SOCKET_ERROR   = -1\n\n  SO_PROTOCOL_INFOW = 0x2005\n\n  SOMAXCONN = 0x7fffffff\n\n  FD_READ_BIT = 0\n  FD_READ     = (1 << FD_READ_BIT)\n\n  FD_WRITE_BIT = 1\n  FD_WRITE     = (1 << FD_WRITE_BIT)\n\n  FD_OOB_BIT = 2\n  FD_OOB     = (1 << FD_OOB_BIT)\n\n  FD_ACCEPT_BIT = 3\n  FD_ACCEPT     = (1 << FD_ACCEPT_BIT)\n\n  FD_CONNECT_BIT = 4\n  FD_CONNECT     = (1 << FD_CONNECT_BIT)\n\n  FD_CLOSE_BIT = 5\n  FD_CLOSE     = (1 << FD_CLOSE_BIT)\n\n  FD_QOS_BIT = 6\n  FD_QOS     = (1 << FD_QOS_BIT)\n\n  FD_GROUP_QOS_BIT = 7\n  FD_GROUP_QOS     = (1 << FD_GROUP_QOS_BIT)\n\n  FD_ROUTING_INTERFACE_CHANGE_BIT = 8\n  FD_ROUTING_INTERFACE_CHANGE     = (1 << FD_ROUTING_INTERFACE_CHANGE_BIT)\n\n  FD_ADDRESS_LIST_CHANGE_BIT = 9\n  FD_ADDRESS_LIST_CHANGE     = (1 << FD_ADDRESS_LIST_CHANGE_BIT)\n\n  FD_MAX_EVENTS = 10\n  FD_ALL_EVENTS = ((1 << FD_MAX_EVENTS) - 1)\n\n  alias WSAEVENT = HANDLE\n  alias WSAOVERLAPPED = OVERLAPPED\n\n  WSA_INVALID_EVENT       = Pointer(WSAEVENT).null\n  WSA_MAXIMUM_WAIT_EVENTS = MAXIMUM_WAIT_OBJECTS\n  WSA_WAIT_FAILED         = WAIT_FAILED\n  WSA_WAIT_EVENT_0        = WAIT_OBJECT_0\n  WSA_WAIT_IO_COMPLETION  = WAIT_IO_COMPLETION\n  WSA_WAIT_TIMEOUT        = WAIT_TIMEOUT\n  WSA_INFINITE            = INFINITE\n\n  alias LPQOS = Void*\n\n  SH_RECEIVE = 0x00\n  SH_SEND    = 0x01\n  SH_BOTH    = 0x02\n\n  alias GROUP = Int\n\n  struct WSAPROTOCOLCHAIN\n    chainLen : Int\n    chainEntries : DWORD*\n  end\n\n  struct WSAPROTOCOL_INFOW\n    dwServiceFlags1 : DWORD\n    dwServiceFlags2 : DWORD\n    dwServiceFlags3 : DWORD\n    dwServiceFlags4 : DWORD\n    dwProviderFlags : DWORD\n    providerId : GUID\n    dwCatalogEntryId : DWORD\n    protocolChain : WSAPROTOCOLCHAIN\n    iVersion : Int\n    iAddressFamily : Int\n    iMaxSockAddr : Int\n    iMinSockAddr : Int\n    iSocketType : Int\n    iProtocol : Int\n    iProtocolMaxOffset : Int\n    iNetworkByteOrder : Int\n    iSecurityScheme : Int\n    dwMessageSize : DWORD\n    dwProviderReserved : DWORD\n    szProtocol : WCHAR*\n  end\n\n  WSA_FLAG_OVERLAPPED = 0x01\n\n  alias WSAOVERLAPPED_COMPLETION_ROUTINE = Proc(DWORD, DWORD, WSAOVERLAPPED*, DWORD, Void)\n\n  struct Timeval\n    tv_sec : Long\n    tv_usec : Long\n  end\n\n  struct Linger\n    l_onoff : UShort\n    l_linger : UShort\n  end\n\n  fun accept(s : SOCKET, addr : Sockaddr*, addrlen : Int*) : SOCKET\n  fun bind(s : SOCKET, addr : Sockaddr*, namelen : Int) : Int\n  fun closesocket(s : SOCKET) : Int\n  fun connect(s : SOCKET, name : Sockaddr*, namelen : Int) : Int\n  fun ioctlsocket(s : SOCKET, cmd : Int, argp : UInt32*) : Int\n  fun getpeername(s : SOCKET, name : Sockaddr*, namelen : Int*) : Int\n  fun getsockname(s : SOCKET, name : Sockaddr*, namelen : Int*) : Int\n  fun getsockopt(s : SOCKET, level : Int, optname : Int, optval : UInt8*, optlen : Int*) : Int\n  fun htons(hostshort : UShort) : UShort\n  fun listen(s : SOCKET, backlog : Int) : Int\n  fun ntohs(netshort : UShort) : UShort\n  fun recv(s : SOCKET, buf : UInt8*, len : Int, flags : Int) : Int\n  fun recvfrom(s : SOCKET, buf : Char*, len : Int, flags : Int, from : Sockaddr*, fromlen : Int*) : Int\n  fun send(s : SOCKET, buf : UInt8*, len : Int, flags : Int) : Int\n  fun setsockopt(s : SOCKET, level : Int, optname : Int, optval : Char*, len : Int) : Int\n  fun shutdown(s : SOCKET, how : Int) : Int\n  fun socket(af : Int, type : Int, protocol : Int) : SOCKET\n\n  fun WSAStartup(wVersionRequired : WORD, lpWSAData : WSAData*) : Int\n  fun WSACleanup : Int\n\n  fun WSASetLastError(iError : Int) : Void\n  fun WSAGetLastError : Int\n\n  # Unused type\n  alias LPCONDITIONPROC = Void*\n  fun WSAAccept(\n    s : SOCKET,\n    addr : Sockaddr*,\n    addrlen : Int*,\n    lpfnCondition : LPCONDITIONPROC,\n    dwCallbackData : DWORD*,\n  ) : SOCKET\n\n  fun WSAConnect(\n    s : SOCKET,\n    name : Sockaddr*,\n    namelen : Int,\n    lpCallerData : WSABUF*,\n    lpCalleeData : WSABUF*,\n    lpSQOS : LPQOS,\n    lpGQOS : LPQOS,\n  )\n  fun WSACreateEvent : WSAEVENT\n\n  fun WSAEventSelect(\n    s : SOCKET,\n    hEventObject : WSAEVENT,\n    lNetworkEvents : Long,\n  ) : Int\n  fun WSAGetOverlappedResult(\n    s : SOCKET,\n    lpOverlapped : WSAOVERLAPPED*,\n    lpcbTransfer : DWORD*,\n    fWait : BOOL,\n    lpdwFlags : DWORD*,\n  ) : BOOL\n  fun WSAIoctl(\n    s : SOCKET,\n    dwIoControlCode : DWORD,\n    lpvInBuffer : Void*,\n    cbInBuffer : DWORD,\n    lpvOutBuffer : Void*,\n    cbOutBuffer : DWORD,\n    lpcbBytesReturned : DWORD*,\n    lpOverlapped : WSAOVERLAPPED*,\n    lpCompletionRoutine : WSAOVERLAPPED_COMPLETION_ROUTINE*,\n  ) : Int\n  fun WSARecv(\n    s : SOCKET,\n    lpBuffers : WSABUF*,\n    dwBufferCount : DWORD,\n    lpNumberOfBytesRecvd : DWORD*,\n    lpFlags : DWORD*,\n    lpOverlapped : WSAOVERLAPPED*,\n    lpCompletionRoutine : WSAOVERLAPPED_COMPLETION_ROUTINE*,\n  ) : Int\n  fun WSARecvFrom(\n    s : SOCKET,\n    lpBuffers : WSABUF*,\n    dwBufferCount : DWORD,\n    lpNumberOfBytesRecvd : DWORD*,\n    lpFlags : DWORD*,\n    lpFrom : Sockaddr*,\n    lpFromlen : Int*,\n    lpOverlapped : WSAOVERLAPPED*,\n    lpCompletionRoutine : WSAOVERLAPPED_COMPLETION_ROUTINE*,\n  ) : Int\n  fun WSAResetEvent(\n    hEvent : WSAEVENT,\n  ) : BOOL\n  fun WSASend(\n    s : SOCKET,\n    lpBuffers : WSABUF*,\n    dwBufferCount : DWORD,\n    lpNumberOfBytesSent : DWORD*,\n    dwFlags : DWORD,\n    lpOverlapped : WSAOVERLAPPED*,\n    lpCompletionRoutine : WSAOVERLAPPED_COMPLETION_ROUTINE*,\n  ) : Int\n  fun WSASendTo(\n    s : SOCKET,\n    lpBuffers : WSABUF*,\n    dwBufferCount : DWORD,\n    lpNumberOfBytesSent : DWORD*,\n    dwFlags : DWORD,\n    lpTo : Sockaddr*,\n    iTolen : Int,\n    lpOverlapped : WSAOVERLAPPED*,\n    lpCompletionRoutine : WSAOVERLAPPED_COMPLETION_ROUTINE*,\n  ) : Int\n  fun WSASocketW(\n    af : Int,\n    type : Int,\n    protocol : Int,\n    lpProtocolInfo : WSAPROTOCOL_INFOW*,\n    g : GROUP,\n    dwFlags : DWORD,\n  ) : SOCKET\n  fun WSAWaitForMultipleEvents(\n    cEvents : DWORD,\n    lphEvents : WSAEVENT*,\n    fWaitAll : BOOL,\n    dwTimeout : DWORD,\n    fAlertable : BOOL,\n  ) : DWORD\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/winternl.cr",
    "content": "@[Link(\"ntdll\")]\nlib LibNTDLL\n  fun RtlNtStatusToDosError(status : LibC::ULONG) : LibC::ULONG\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ws2def.cr",
    "content": "lib LibC\n  alias ADDRESS_FAMILY = UShort\n\n  AF_UNSPEC     = 0      # unspecified\n  AF_UNIX       = 1      # local to host (pipes, portals)\n  AF_INET       = 2      # internetwork: UDP, TCP, etc.\n  AF_IMPLINK    = 3      # arpanet imp addresses\n  AF_PUP        = 4      # pup protocols: e.g. BSP\n  AF_CHAOS      = 5      # mit CHAOS protocols\n  AF_NS         = 6      # XEROX NS protocols\n  AF_IPX        = AF_NS  # IPX protocols: IPX, SPX, etc.\n  AF_ISO        = 7      # ISO protocols\n  AF_OSI        = AF_ISO # OSI is ISO\n  AF_ECMA       =  8     # european computer manufacturers\n  AF_DATAKIT    =  9     # datakit protocols\n  AF_CCITT      = 10     # CCITT protocols, X.25 etc\n  AF_SNA        = 11     # IBM SNA\n  AF_DECnet     = 12     # DECnet\n  AF_DLI        = 13     # Direct data link interface\n  AF_LAT        = 14     # LAT\n  AF_HYLINK     = 15     # NSC Hyperchannel\n  AF_APPLETALK  = 16     # AppleTalk\n  AF_NETBIOS    = 17     # NetBios-style addresses\n  AF_VOICEVIEW  = 18     # VoiceView\n  AF_FIREFOX    = 19     # Protocols from Firefox\n  AF_UNKNOWN1   = 20     # Somebody is using this!\n  AF_BAN        = 21     # Banyan\n  AF_ATM        = 22     # Native ATM Services\n  AF_INET6      = 23     # Internetwork Version 6\n  AF_CLUSTER    = 24     # Microsoft Wolfpack\n  AF_12844      = 25     # IEEE 1284.4 WG AF\n  AF_IRDA       = 26     # IrDA\n  AF_NETDES     = 28     # Network Designers OSI & gateway\n  AF_TCNPROCESS = 29\n  AF_TCNMESSAGE = 30\n  AF_ICLFXBM    = 31\n  AF_BTH        = 32 # Bluetooth RFCOMM/L2CAP protocols\n  AF_LINK       = 33\n  AF_HYPERV     = 34\n  AF_MAX        = 35\n\n  SOCK_STREAM    = 1\n  SOCK_DGRAM     = 2\n  SOCK_RAW       = 3\n  SOCK_RDM       = 4\n  SOCK_SEQPACKET = 5\n\n  SOL_SOCKET = 0xffff\n\n  SO_DEBUG       = 0x0001 # turn on debugging info recording\n  SO_ACCEPTCONN  = 0x0002 # socket has had listen()\n  SO_REUSEADDR   = 0x0004 # allow local address reuse\n  SO_KEEPALIVE   = 0x0008 # keep connections alive\n  SO_DONTROUTE   = 0x0010 # just use interface addresses\n  SO_BROADCAST   = 0x0020 # permit sending of broadcast msgs\n  SO_USELOOPBACK = 0x0040 # bypass hardware when possible\n  SO_LINGER      = 0x0080 # linger on close if data present\n  SO_OOBINLINE   = 0x0100 # leave received OOB data in line\n\n  SO_DONTLINGER       = ~SO_LINGER\n  SO_EXCLUSIVEADDRUSE = ~SO_REUSEADDR # disallow local address reuse\n\n  SO_SNDBUF    = 0x1001 # send buffer size\n  SO_RCVBUF    = 0x1002 # receive buffer size\n  SO_SNDLOWAT  = 0x1003 # send low-water mark\n  SO_RCVLOWAT  = 0x1004 # receive low-water mark\n  SO_SNDTIMEO  = 0x1005 # send timeout\n  SO_RCVTIMEO  = 0x1006 # receive timeout\n  SO_ERROR     = 0x1007 # get error status and clear\n  SO_TYPE      = 0x1008 # get socket type\n  SO_BSP_STATE = 0x1009 # get socket 5-tuple state\n\n  SO_GROUP_ID       = 0x2001 # ID of a socket group\n  SO_GROUP_PRIORITY = 0x2002 # the relative priority within a group\n  SO_MAX_MSG_SIZE   = 0x2003 # maximum message size\n\n  SO_CONDITIONAL_ACCEPT = 0x3002 # enable true conditional accept: connection is not ack-ed\n  # to the other side until conditional function returns CF_ACCEPT\n  SO_PAUSE_ACCEPT   = 0x3003 # pause accepting new connections\n  SO_COMPARTMENT_ID = 0x3004 # get/set the compartment for a socket\n\n  SO_RANDOMIZE_PORT      = 0x3005 # randomize assignment of wildcard ports\n  SO_PORT_SCALABILITY    = 0x3006 # enable port scalability\n  SO_REUSE_UNICASTPORT   = 0x3007 # defer ephemeral port allocation for outbound connections\n  SO_REUSE_MULTICASTPORT = 0x3008 # enable port reuse and disable unicast reception.\n\n  TCP_NODELAY = 0x0001\n\n  struct Sockaddr\n    sa_family : UInt8\n    sa_data : Char[14]\n  end\n\n  SS_MAXSIZE   = 128\n  SS_ALIGNSIZE =   8 # sizeof(Int64)\n\n  SS_PAD1SIZE = SS_ALIGNSIZE - 2                              # sizeof(USHORT)\n  SS_PAD2SIZE = SS_MAXSIZE - (2 + SS_PAD1SIZE + SS_ALIGNSIZE) # sizeof(USHORT)\n\n  struct SOCKADDR_STORAGE\n    ss_family : Short\n    __ss_pad1 : Char[SS_PAD1SIZE]\n    __ss_align : Int64\n    __ss_pad2 : Char[SS_PAD2SIZE]\n  end\n\n  IOC_UNIX     = 0x00000000\n  IOC_WS2      = 0x08000000\n  IOC_PROTOCOL = 0x10000000\n  IOC_VENDOR   = 0x18000000\n\n  SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6\n\n  IPPROTO_IP   =  0\n  IPPROTO_IPV6 = 41\n\n  IP_MULTICAST_IF   = 9\n  IPV6_MULTICAST_IF = 9\n\n  IP_MULTICAST_TTL    = 10\n  IPV6_MULTICAST_HOPS = 10\n\n  IP_MULTICAST_LOOP   = 11\n  IPV6_MULTICAST_LOOP = 11\n\n  IP_ADD_MEMBERSHIP  = 12\n  IP_DROP_MEMBERSHIP = 13\n\n  # JOIN and LEAVE are the same as ADD and DROP\n  # https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ipv6-socket-options\n  IPV6_ADD_MEMBERSHIP = 12\n  IPV6_JOIN_GROUP     = 12\n\n  IPV6_DROP_MEMBERSHIP = 13\n  IPV6_LEAVE_GROUP     = 13\n\n  IPV6_V6ONLY = 27\n\n  enum IPPROTO\n    IPPROTO_HOPOPTS  =   0 # IPv6 Hop-by-Hop options\n    IPPROTO_ICMP     =   1\n    IPPROTO_IGMP     =   2\n    IPPROTO_GGP      =   3\n    IPPROTO_IPV4     =   4\n    IPPROTO_ST       =   5\n    IPPROTO_TCP      =   6\n    IPPROTO_CBT      =   7\n    IPPROTO_EGP      =   8\n    IPPROTO_IGP      =   9\n    IPPROTO_PUP      =  12\n    IPPROTO_UDP      =  17\n    IPPROTO_RDP      =  27\n    IPPROTO_IPV6     =  41 # IPv6 header\n    IPPROTO_ROUTING  =  43 # IPv6 Routing header\n    IPPROTO_FRAGMENT =  44 # IPv6 fragmentation header\n    IPPROTO_ESP      =  50 # encapsulating security payload\n    IPPROTO_AH       =  51 # authentication header\n    IPPROTO_ICMPV6   =  58 # ICMPv6\n    IPPROTO_NONE     =  59 # IPv6 no next header\n    IPPROTO_DSTOPTS  =  60 # IPv6 Destination options\n    IPPROTO_ND       =  77\n    IPPROTO_ICLFXBM  =  78\n    IPPROTO_PIM      = 103\n    IPPROTO_PGM      = 113\n    IPPROTO_L2TP     = 115\n    IPPROTO_SCTP     = 132\n    IPPROTO_RAW      = 255\n    IPPROTO_MAX      = 256\n  end\n\n  INADDR_ANY = 0x00000000_u64\n\n  IOC_VOID  = 0x20000000 # no parameters\n  IOC_OUT   = 0x40000000 # copy out parameters\n  IOC_IN    = 0x80000000 # copy in parameters\n  IOC_INOUT = IOC_IN | IOC_OUT\n\n  struct WSABUF\n    len : ULong\n    buf : Char*\n  end\n\n  AI_PASSIVE     = 0x00000001 # Socket address will be used in bind() call\n  AI_CANONNAME   = 0x00000002 # Return canonical name in first ai_canonname\n  AI_NUMERICHOST = 0x00000004 # Nodename must be a numeric address string\n  AI_NUMERICSERV = 0x00000008 # Servicename must be a numeric port number\n  AI_DNS_ONLY    = 0x00000010 # Restrict queries to unicast DNS only (no LLMNR, netbios, etc.)\n\n  AI_ALL        = 0x00000100 # Query both IP6 and IP4 with AI_V4MAPPED\n  AI_ADDRCONFIG = 0x00000400 # Resolution only if global address configured\n  AI_V4MAPPED   = 0x00000800 # On v6 failure, query v4 and convert to V4MAPPED format\n\n  AI_NON_AUTHORITATIVE      = 0x00004000 # LUP_NON_AUTHORITATIVE\n  AI_SECURE                 = 0x00008000 # LUP_SECURE\n  AI_RETURN_PREFERRED_NAMES = 0x00010000 # LUP_RETURN_PREFERRED_NAMES\n\n  AI_FQDN                 = 0x00020000 # Return the FQDN in ai_canonname\n  AI_FILESERVER           = 0x00040000 # Resolving fileserver name resolution\n  AI_DISABLE_IDN_ENCODING = 0x00080000 # Disable Internationalized Domain Names handling\n  AI_EXTENDED             = 0x80000000 # Indicates this is extended ADDRINFOEX(2/..) struct\n  AI_RESOLUTION_HANDLE    = 0x40000000 # Request resolution handle\n\n  struct Addrinfo\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SizeT\n    ai_canonname : Char*\n    ai_addr : Sockaddr*\n    ai_next : Addrinfo*\n  end\n\n  struct ADDRINFOEXW\n    ai_flags : Int\n    ai_family : Int\n    ai_socktype : Int\n    ai_protocol : Int\n    ai_addrlen : SizeT\n    ai_canonname : LPWSTR\n    ai_addr : Sockaddr*\n    ai_blob : Void*\n    ai_bloblen : SizeT\n    ai_provider : GUID*\n    ai_next : ADDRINFOEXW*\n  end\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ws2ipdef.cr",
    "content": "require \"./in6addr\"\nrequire \"./inaddr\"\n\nlib LibC\n  struct SockaddrIn6\n    sin6_family : Short\n    sin6_port : UShort\n    sin6_flowinfo : ULong\n    sin6_addr : In6Addr\n    sin6_scope_id : ULong\n  end\n\n  struct SockaddrIn\n    sin_family : Short\n    sin_port : UShort\n    sin_addr : InAddr\n    sin_zero : StaticArray(CHAR, 8)\n  end\n\n  # https://learn.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_mreq\n  struct IpMreq\n    imr_multiaddr : InAddr\n    imr_interface : InAddr\n  end\n\n  # https://learn.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ipv6_mreq\n  struct Ipv6Mreq\n    ipv6mr_multiaddr : In6Addr\n    ipv6mr_interface : ULong\n  end\n\n  TCP_EXPEDITED_1122       = 0x0002\n  TCP_KEEPALIVE            =      3\n  TCP_MAXSEG               =      4\n  TCP_MAXRT                =      5\n  TCP_STDURG               =      6\n  TCP_NOURG                =      7\n  TCP_ATMARK               =      8\n  TCP_NOSYNRETRIES         =      9\n  TCP_TIMESTAMPS           =     10\n  TCP_OFFLOAD_PREFERENCE   =     11\n  TCP_CONGESTION_ALGORITHM =     12\n  TCP_DELAY_FIN_ACK        =     13\n  TCP_MAXRTMS              =     14\n  TCP_FASTOPEN             =     15\n  TCP_KEEPCNT              =     16\n  TCP_KEEPIDLE             = TCP_KEEPALIVE\n  TCP_KEEPINTVL            = 17\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/ws2tcpip.cr",
    "content": "require \"./winsock2\"\nrequire \"./ws2ipdef\"\n\nlib LibC\n  EAI_AGAIN        = WinError::WSATRY_AGAIN\n  EAI_BADFLAGS     = WinError::WSAEINVAL\n  EAI_FAIL         = WinError::WSANO_RECOVERY\n  EAI_FAMILY       = WinError::WSAEAFNOSUPPORT\n  EAI_MEMORY       = WinError::WSA_NOT_ENOUGH_MEMORY\n  EAI_NOSECURENAME = WinError::WSA_SECURE_HOST_NOT_FOUND\n  EAI_NONAME       = WinError::WSAHOST_NOT_FOUND\n  EAI_SERVICE      = WinError::WSATYPE_NOT_FOUND\n  EAI_SOCKTYPE     = WinError::WSAESOCKTNOSUPPORT\n  EAI_IPSECPOLICY  = WinError::WSA_IPSEC_NAME_POLICY_ERROR\n\n  fun freeaddrinfo(pAddrInfo : Addrinfo*) : Void\n  fun getaddrinfo(pNodeName : Char*, pServiceName : Char*, pHints : Addrinfo*, ppResult : Addrinfo**) : Int\n  fun inet_ntop(family : Int, pAddr : Void*, pStringBuf : Char*, stringBufSize : SizeT) : Char*\n  fun inet_pton(family : Int, pszAddrString : Char*, pAddrBuf : Void*) : Int\n\n  fun FreeAddrInfoExW(pAddrInfoEx : ADDRINFOEXW*)\n\n  alias LPLOOKUPSERVICE_COMPLETION_ROUTINE = DWORD, DWORD, WSAOVERLAPPED* ->\n\n  fun GetAddrInfoExW(\n    pName : LPWSTR,\n    pServiceName : LPWSTR,\n    dwNameSpace : DWORD,\n    lpNspId : GUID*,\n    hints : ADDRINFOEXW*,\n    ppResult : ADDRINFOEXW**,\n    timeout : Timeval*,\n    lpOverlapped : OVERLAPPED*,\n    lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE,\n    lpHandle : HANDLE*,\n  ) : Int\n\n  fun GetAddrInfoExOverlappedResult(lpOverlapped : OVERLAPPED*) : Int\n  fun GetAddrInfoExCancel(lpHandle : HANDLE*) : Int\nend\n"
  },
  {
    "path": "src/lib_c/x86_64-windows-msvc/c/wtypesbase.cr",
    "content": "require \"c/winnt\"\n\nlib LibC\n  struct SECURITY_ATTRIBUTES\n    nLength : DWORD\n    lpSecurityDescriptor : Void*\n    bInheritHandle : BOOL\n  end\nend\n"
  },
  {
    "path": "src/lib_c.cr",
    "content": "# Supported library versions:\n#\n# * glibc (2.26+)\n# * musl libc (1.2+)\n# * system libraries of several BSDs\n# * macOS system library (11+)\n# * MSVCRT\n# * WASI\n# * bionic libc\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#system-library\n{% if flag?(:msvc) %}\n  @[Link({{ flag?(:static) ? \"libucrt\" : \"ucrt\" }})]\n{% end %}\nlib LibC\n  alias Char = UInt8\n  alias UChar = Char\n  alias SChar = Int8\n  alias Short = Int16\n  alias UShort = UInt16\n  alias Int = Int32\n  alias UInt = UInt32\n\n  {% if flag?(:bits32) || flag?(:win32) %}\n    alias Long = Int32\n    alias ULong = UInt32\n  {% elsif flag?(:bits64) %}\n    alias Long = Int64\n    alias ULong = UInt64\n  {% else %}\n    {% raise \"Architecture with unsupported word size\" %}\n  {% end %}\n\n  alias LongLong = Int64\n  alias ULongLong = UInt64\n  alias Float = Float32\n  alias Double = Float64\n\n  {% if flag?(:android) %}\n    {% default_api_version = 31 %}\n    {% min_supported_version = 24 %}\n    {% api_version_var = env(\"ANDROID_PLATFORM\") || env(\"ANDROID_NATIVE_API_LEVEL\") %}\n    {% api_version = api_version_var ? api_version_var.gsub(/^android-/, \"\").to_i : default_api_version %}\n    {% raise \"TODO: Support Android API level below #{min_supported_version}\" unless api_version >= min_supported_version %}\n    ANDROID_API = {{ api_version }}\n  {% end %}\n\n  $environ : Char**\nend\n"
  },
  {
    "path": "src/lib_z/lib_z.cr",
    "content": "# Supported library versions:\n#\n# * zlib\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#other-stdlib-libraries\n@[Link(\"z\")]\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  @[Link(dll: \"zlib1.dll\")]\n{% end %}\nlib LibZ\n  alias Char = LibC::Char\n  alias Int = LibC::Int\n  alias UInt = LibC::UInt\n  alias Long = LibC::Long\n  alias ULong = LibC::ULong\n  alias SizeT = LibC::SizeT\n\n  alias Bytef = UInt8\n\n  fun zlibVersion : Char*\n  fun adler32(adler : ULong, buf : Bytef*, len : UInt) : ULong\n  fun adler32_combine(adler1 : ULong, adler2 : ULong, len : Long) : ULong\n  fun crc32(crc : ULong, buf : Bytef*, len : UInt) : ULong\n  fun crc32_combine(crc1 : ULong, crc2 : ULong, len : Long) : ULong\n\n  alias AllocFunc = Void*, UInt, UInt -> Void*\n  alias FreeFunc = (Void*, Void*) ->\n\n  struct ZStream\n    next_in : Bytef*\n    avail_in : UInt\n    total_in : ULong\n    next_out : Bytef*\n    avail_out : UInt\n    total_out : ULong\n    msg : Char*\n    state : Void*\n    zalloc : AllocFunc\n    zfree : FreeFunc\n    opaque : Void*\n    data_type : Int\n    adler : Long\n    reserved : Long\n  end\n\n  # error codes\n  enum Error\n    OK            =  0\n    STREAM_END    =  1\n    NEED_DICT     =  2\n    ERRNO         = -1\n    STREAM_ERROR  = -2\n    DATA_ERROR    = -3\n    MEM_ERROR     = -4\n    BUF_ERROR     = -5\n    VERSION_ERROR = -6\n  end\n\n  enum Flush\n    NO_FLUSH      = 0\n    PARTIAL_FLUSH = 1\n    SYNC_FLUSH    = 2\n    FULL_FLUSH    = 3\n    FINISH        = 4\n    BLOCK         = 5\n    TREES         = 6\n  end\n\n  MAX_BITS      = 15\n  DEF_MEM_LEVEL =  8\n  Z_DEFLATED    =  8\n\n  fun deflateInit2 = deflateInit2_(stream : ZStream*, level : Int32, method : Int32,\n                                   window_bits : Int32, mem_level : Int32, strategy : Int32,\n                                   version : UInt8*, stream_size : Int32) : Error\n  fun deflate(stream : ZStream*, flush : Flush) : Error\n  fun deflateEnd(stream : ZStream*) : Error\n  fun deflateReset(stream : ZStream*) : Error\n  fun deflateSetDictionary(stream : ZStream*, dictionary : UInt8*, len : UInt) : Int\n\n  fun inflateInit2 = inflateInit2_(stream : ZStream*, window_bits : Int32, version : UInt8*, stream_size : Int32) : Error\n  fun inflate(stream : ZStream*, flush : Flush) : Error\n  fun inflateEnd(stream : ZStream*) : Error\n  fun inflateSetDictionary(stream : ZStream*, dictionary : UInt8*, len : UInt) : Error\n  fun zError(error : Error) : UInt8*\nend\n"
  },
  {
    "path": "src/llvm/abi/aarch64.cr",
    "content": "require \"../abi\"\n\n# Based on\n# https://github.com/rust-lang/rust/blob/master/src/librustc_trans/cabi_aarch64.rs\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::AArch64 < LLVM::ABI\n  def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context) : LLVM::ABI::FunctionType\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = atys.map { |aty| compute_arg_type(aty, context) }\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  def align(type : Type) : Int32\n    align(type, 8)\n  end\n\n  def size(type : Type) : Int32\n    size(type, 8)\n  end\n\n  def homogeneous_aggregate?(type)\n    homog_agg : {Type, UInt64}? = case type.kind\n    when Type::Kind::Float\n      return {type, 1_u64}\n    when Type::Kind::Double\n      return {type, 1_u64}\n    when Type::Kind::Array\n      check_array(type)\n    when Type::Kind::Struct\n      check_struct(type)\n    end\n\n    # Ensure we have at most four uniquely addressable members\n    if homog_agg\n      if 0 < homog_agg[1] <= 4\n        return homog_agg\n      end\n    end\n  end\n\n  private def check_array(type)\n    len = type.array_size.to_u64\n    return if len == 0\n    element = type.element_type\n\n    # if our element is an HFA/HVA, so are we; multiply members by our len\n    if homog_agg = homogeneous_aggregate?(element)\n      base_type, members = homog_agg\n      {base_type, len * members}\n    end\n  end\n\n  private def check_struct(type)\n    elements = type.struct_element_types\n    return if elements.empty?\n\n    base_type = nil\n    members = 0_u64\n\n    elements.each do |element|\n      opt_homog_agg = homogeneous_aggregate?(element)\n\n      # field isn't itself an HFA, so we aren't either\n      return unless opt_homog_agg\n      field_type, field_members = opt_homog_agg\n\n      if !base_type\n        # first field - store its type and number of members\n        base_type = field_type\n        members = field_members\n      else\n        # 2nd or later field - give up if it's a different type; otherwise incr. members\n        return unless base_type == field_type\n        members += field_members\n      end\n    end\n\n    return unless base_type\n\n    if size(type) == size(base_type) * members\n      {base_type, members}\n    end\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif register?(rty)\n      non_struct(rty, context)\n    elsif homog_agg = homogeneous_aggregate?(rty)\n      base_type, members = homog_agg\n      ArgType.direct(rty, base_type.array(members))\n    else\n      size = size(rty)\n      if size <= 16\n        cast = if size <= 1\n                 context.int8\n               elsif size <= 2\n                 context.int16\n               elsif size <= 4\n                 context.int32\n               elsif size <= 8\n                 context.int64\n               else\n                 context.int64.array(((size + 7) // 8).to_u64)\n               end\n        ArgType.direct(rty, cast)\n      else\n        ArgType.indirect(rty, LLVM::Attribute::StructRet)\n      end\n    end\n  end\n\n  private def compute_arg_type(aty, context)\n    if register?(aty)\n      non_struct(aty, context)\n    elsif homog_agg = homogeneous_aggregate?(aty)\n      base_type, members = homog_agg\n      ArgType.direct(aty, base_type.array(members))\n    else\n      size = size(aty)\n      if size <= 16\n        cast = if size == 0\n                 context.int64.array(0)\n               elsif size <= 1\n                 context.int8\n               elsif size <= 2\n                 context.int16\n               elsif size <= 4\n                 context.int32\n               elsif size <= 8\n                 context.int64\n               else\n                 context.int64.array(((size + 7) // 8).to_u64)\n               end\n        ArgType.direct(aty, cast)\n      else\n        ArgType.indirect(aty, nil)\n      end\n    end\n  end\n\n  def register?(type) : Bool\n    case type.kind\n    when Type::Kind::Integer,\n         Type::Kind::Float,\n         Type::Kind::Double,\n         Type::Kind::Pointer\n      true\n    else\n      false\n    end\n  end\n\n  private def non_struct(type, context)\n    attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n    ArgType.direct(type, attr: attr)\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi/arm.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/dfe8bd10fe6763e0a1d5d55fa2574ecba27d3e2e/src/librustc_trans/cabi_arm.rs\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::ARM < LLVM::ABI\n  def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context) : LLVM::ABI::FunctionType\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  def align(type : Type) : Int32\n    align(type, 4)\n  end\n\n  def size(type : Type) : Int32\n    size(type, 4)\n  end\n\n  def register?(type) : Bool\n    case type.kind\n    when Type::Kind::Integer, Type::Kind::Float, Type::Kind::Double, Type::Kind::Pointer\n      true\n    else\n      false\n    end\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif register?(rty)\n      non_struct(rty, context)\n    else\n      case size(rty)\n      when 1\n        ArgType.direct(rty, context.int8)\n      when 2\n        ArgType.direct(rty, context.int16)\n      when 3, 4\n        ArgType.direct(rty, context.int32)\n      else\n        ArgType.indirect(rty, LLVM::Attribute::StructRet)\n      end\n    end\n  end\n\n  private def compute_arg_types(atys, context)\n    atys.map do |aty|\n      if register?(aty)\n        non_struct(aty, context)\n      else\n        if align(aty) <= 4\n          ArgType.direct(aty, context.int32.array(((size(aty) + 3) // 4).to_u64))\n        else\n          ArgType.direct(aty, context.int64.array(((size(aty) + 7) // 8).to_u64))\n        end\n      end\n    end\n  end\n\n  private def non_struct(type, context)\n    attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n    ArgType.direct(type, attr: attr)\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi/avr.cr",
    "content": "require \"../abi\"\n\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::AVR < LLVM::ABI\n  AVRTINY = StaticArray[\n    \"attiny4\",\n    \"attiny5\",\n    \"attiny9\",\n    \"attiny10\",\n    \"attiny102\",\n    \"attiny104\",\n    \"attiny20\",\n    \"attiny40\",\n  ]\n\n  def initialize(target_machine : TargetMachine, mcpu : String? = nil)\n    super target_machine\n\n    # \"Reduced Tiny\" core devices only have 16 General Purpose Registers\n    if mcpu.in?(AVRTINY)\n      @rsize = 4 # values above 4 bytes are returned by memory\n      @rmin = 20 # 6 registers for call arguments (R25..R20)\n    else\n      @rsize = 8 # values above 8 bytes are returned by memory\n      @rmin = 8  # 18 registers for call arguments (R25..R8)\n    end\n  end\n\n  def align(type : Type) : Int32\n    target_data.abi_alignment(type).to_i32\n  end\n\n  def size(type : Type) : Int32\n    target_data.abi_size(type).to_i32\n  end\n\n  # Follows AVR GCC, while Clang (and Rust) are incompatible, despite LLVM\n  # itself being compliant.\n  #\n  # - <https://gcc.gnu.org/wiki/avr-gcc>\n  # - <https://bugs.llvm.org/show_bug.cgi?id=46140>\n  def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context) : LLVM::ABI::FunctionType\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  # Pass in registers unless the returned type is a struct larger than 8 bytes\n  # (4 bytes on reduced tiny cores).\n  #\n  # Rust & Clang always return a struct _indirectly_.\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif size(rty) > @rsize\n      ArgType.indirect(rty, LLVM::Attribute::StructRet)\n    else\n      # let the LLVM AVR backend handle the pw2ceil padding of structs\n      ArgType.direct(rty)\n    end\n  end\n\n  # Fill the R25 -> R8 registers (R20 on reduced tiny cores), rounding odd byte\n  # sizes to the next even number, then pass by memory (indirect), so {i8, i32}\n  # are passed as R24 then R20..R23 (LSB -> MSB) for example.\n  #\n  # Rust & Clang always pass structs _indirectly_.\n  private def compute_arg_types(atys, context)\n    rn = 26\n    atys.map do |aty|\n      bytes = size(aty)\n      bytes += 1 if bytes.odd?\n      rn -= bytes\n\n      if bytes == 0 || rn < @rmin\n        ArgType.indirect(aty, LLVM::Attribute::StructRet)\n      else\n        # let the LLVM AVR backend handle the odd to next even number padding\n        ArgType.direct(aty)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi/wasm32.cr",
    "content": "require \"../abi\"\n\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::Wasm32 < LLVM::ABI\n  def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context)\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new(arg_tys, ret_ty)\n  end\n\n  def align(type : Type)\n    target_data.abi_alignment(type).to_i32\n  end\n\n  def size(type : Type)\n    target_data.abi_size(type).to_i32\n  end\n\n  private def aggregate?(type)\n    case type.kind\n    when .struct?, .array?\n      true\n    else\n      false\n    end\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if aggregate?(rty)\n      ArgType.indirect(rty, LLVM::Attribute::ByVal)\n    else\n      ArgType.direct(rty)\n    end\n  end\n\n  private def compute_arg_types(atys, context)\n    atys.map do |t|\n      if aggregate?(t)\n        ArgType.indirect(t, LLVM::Attribute::ByVal)\n      else\n        ArgType.direct(t)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi/x86.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi_x86.rs\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::X86 < LLVM::ABI\n  def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context)\n    ret_ty = compute_return_type(rty, ret_def, context)\n    arg_tys = compute_arg_types(atys, context)\n    FunctionType.new arg_tys, ret_ty\n  end\n\n  def size(type : Type)\n    target_data.abi_size(type).to_i32\n  end\n\n  def align(type : Type)\n    target_data.abi_alignment(type).to_i32\n  end\n\n  private def compute_return_type(rty, ret_def, context)\n    if !ret_def\n      ArgType.direct(context.void)\n    elsif rty.kind == LLVM::Type::Kind::Struct\n      # Returning a structure. Most often, this will use\n      # a hidden first argument. On some platforms, though,\n      # small structs are returned as integers.\n      #\n      # Some links:\n      # http://www.angelcode.com/dev/callconv/callconv.html\n      # Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp\n\n      if osx? || windows?\n        case target_data.abi_size(rty)\n        when 1 then ret_value(rty, context.int8)\n        when 2 then ret_value(rty, context.int16)\n        when 4 then ret_value(rty, context.int32)\n        when 8 then ret_value(rty, context.int64)\n        else        ret_pointer(rty)\n        end\n      else\n        ret_pointer(rty)\n      end\n    else\n      non_struct(rty, context)\n    end\n  end\n\n  private def compute_arg_types(atys, context)\n    atys.map do |t|\n      case t.kind\n      when Type::Kind::Struct\n        size = target_data.abi_size(t)\n        if size == 0\n          ArgType.ignore(t)\n        else\n          ArgType.indirect(t, LLVM::Attribute::ByVal)\n        end\n      else\n        non_struct(t, context)\n      end\n    end\n  end\n\n  private def ret_value(type, cast)\n    ArgType.direct(type, cast)\n  end\n\n  private def ret_pointer(type)\n    ArgType.indirect(type, LLVM::Attribute::StructRet)\n  end\n\n  private def non_struct(type, context)\n    attr = type == context.int1 ? LLVM::Attribute::ZExt : nil\n    ArgType.direct(type, attr: attr)\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi/x86_64.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi_x86_64.rs\n# See also, section 3.2.3 of the System V Application Binary Interface AMD64 Architecture Processor Supplement\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::X86_64 < LLVM::ABI\n  MAX_INT_REGS = 6 # %rdi, %rsi, %rdx, %rcx, %r8, %r9\n  MAX_SSE_REGS = 8 # %xmm0-%xmm7\n\n  def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context) : LLVM::ABI::FunctionType\n    # registers available to pass arguments directly: int_regs can hold integers\n    # and pointers, sse_regs can hold floats and doubles\n    available_int_regs = MAX_INT_REGS\n    available_sse_regs = MAX_SSE_REGS\n\n    if ret_def\n      ret_ty, _, _ = x86_64_type(rty, Attribute::StructRet, context) { |cls| sret?(cls) }\n      if ret_ty.kind.indirect?\n        # return value is a caller-allocated struct which is passed in %rdi,\n        # so we have 1 less register available for passing arguments\n        available_int_regs -= 1\n      end\n    else\n      ret_ty = ArgType.direct(context.void)\n    end\n\n    arg_tys = atys.map do |arg_type|\n      abi_type, needed_int_regs, needed_sse_regs = x86_64_type(arg_type, Attribute::ByVal, context) { |cls| pass_by_val?(cls) }\n      if available_int_regs >= needed_int_regs && available_sse_regs >= needed_sse_regs\n        available_int_regs -= needed_int_regs\n        available_sse_regs -= needed_sse_regs\n        abi_type\n      elsif !register?(arg_type)\n        # no available registers to pass the argument, but only mark aggregates\n        # as indirect byval types because LLVM will automatically pass register\n        # types in the stack\n        ArgType.indirect(arg_type, Attribute::ByVal)\n      else\n        abi_type\n      end\n    end\n\n    FunctionType.new arg_tys, ret_ty\n  end\n\n  # returns the LLVM type (with attributes) and the number of integer and SSE\n  # registers needed to pass this value directly (ie. not using the stack)\n  def x86_64_type(type, ind_attr, context, &) : Tuple(ArgType, Int32, Int32)\n    if int_register?(type)\n      attr = type == context.int1 ? Attribute::ZExt : nil\n      {ArgType.direct(type, attr: attr), 1, 0}\n    elsif sse_register?(type)\n      {ArgType.direct(type), 0, 1}\n    else\n      cls = classify(type)\n      if yield cls\n        {ArgType.indirect(type, ind_attr), 0, 0}\n      else\n        needed_int_regs = cls.count(&.int?)\n        needed_sse_regs = cls.count(&.sse?)\n        {ArgType.direct(type, llreg(context, cls)), needed_int_regs, needed_sse_regs}\n      end\n    end\n  end\n\n  def register?(type) : Bool\n    int_register?(type) || sse_register?(type)\n  end\n\n  def int_register?(type) : Bool\n    case type.kind\n    when Type::Kind::Integer, Type::Kind::Pointer\n      true\n    else\n      false\n    end\n  end\n\n  def sse_register?(type) : Bool\n    case type.kind\n    when Type::Kind::Float, Type::Kind::Double\n      true\n    else\n      false\n    end\n  end\n\n  def pass_by_val?(cls) : Bool\n    return false if cls.empty?\n\n    cl = cls.first\n    cl.in?(RegClass::Memory, RegClass::X87, RegClass::ComplexX87)\n  end\n\n  def sret?(cls) : Bool\n    return false if cls.empty?\n\n    cls.first == RegClass::Memory\n  end\n\n  def classify(type)\n    words = (size(type) + 7) // 8\n    reg_classes = Array.new(words, RegClass::NoClass)\n    if words > 4 || has_misaligned_fields?(type)\n      all_mem(reg_classes)\n    else\n      classify(type, reg_classes, 0, 0)\n      fixup(type, reg_classes)\n    end\n    reg_classes\n  end\n\n  def classify(ty, cls, ix, off)\n    t_align = align(ty)\n    t_size = size(ty)\n\n    misalign = off % t_align\n    if misalign != 0\n      i = off // 8\n      e = (off + t_size + 7) // 8\n      while i < e\n        unify(cls, ix + i, RegClass::Memory)\n        i += 1\n      end\n      return\n    end\n\n    case ty.kind\n    when Type::Kind::Integer, Type::Kind::Pointer\n      unify(cls, ix + off // 8, RegClass::Int)\n    when Type::Kind::Float\n      unify(cls, ix + off // 8, (off % 8 == 4) ? RegClass::SSEFv : RegClass::SSEFs)\n    when Type::Kind::Double\n      unify(cls, ix + off // 8, RegClass::SSEDs)\n    when Type::Kind::Struct\n      classify_struct(ty.struct_element_types, cls, ix, off, ty.packed_struct?)\n    when Type::Kind::Array\n      len = ty.array_size\n      elt = ty.element_type\n      eltsz = size(elt)\n      i = 0\n      while i < len\n        classify(elt, cls, ix, off + i * eltsz)\n        i += 1\n      end\n    else\n      raise \"Unhandled Type::Kind in classify: #{ty.kind}\"\n    end\n  end\n\n  def classify_struct(tys, cls, i, off, packed) : Nil\n    field_off = off\n    tys.each do |ty|\n      field_off = align_offset(field_off, ty) unless packed\n      classify(ty, cls, i, field_off)\n      field_off += size(ty)\n    end\n  end\n\n  def fixup(ty, cls) : Nil\n    i = 0\n    ty_kind = ty.kind\n    e = cls.size\n    if e > 2 && ty_kind.in?(Type::Kind::Struct, Type::Kind::Array)\n      if cls[i].sse?\n        i += 1\n        while i < e\n          if cls[i] != RegClass::SSEUp\n            all_mem(cls)\n            return\n          end\n          i += 1\n        end\n      else\n        all_mem(cls)\n        return\n      end\n    else\n      while i < e\n        case\n        when cls[i] == RegClass::Memory\n          all_mem(cls)\n          return\n        when cls[i] == RegClass::X87Up\n          # for darwin\n          # cls[i] = RegClass::SSEDs\n          all_mem(cls)\n          return\n        when cls[i] == RegClass::SSEUp\n          cls[i] = RegClass::SSEDv\n        when cls[i].sse?\n          i += 1\n          while i != e && cls[i] == RegClass::SSEUp\n            i += 1\n          end\n        when cls[i] == RegClass::X87\n          i += 1\n          while i != e && cls[i] == RegClass::X87Up\n            i += 1\n          end\n        else\n          i += 1\n        end\n      end\n    end\n  end\n\n  def unify(cls, i, newv)\n    case\n    when cls[i] == newv\n      return\n    when cls[i] == RegClass::NoClass\n      cls[i] = newv\n    when newv == RegClass::NoClass\n      return\n    when cls[i] == RegClass::Memory, newv == RegClass::Memory\n      return\n    when cls[i] == RegClass::Int, newv == RegClass::Int\n      return\n    when cls[i] == RegClass::X87,\n         cls[i] == RegClass::X87Up,\n         cls[i] == RegClass::ComplexX87,\n         newv == RegClass::X87,\n         newv == RegClass::X87Up,\n         newv == RegClass::ComplexX87\n      cls[i] = RegClass::Memory\n    else\n      cls[i] = newv\n    end\n  end\n\n  def all_mem(reg_classes)\n    reg_classes.fill(RegClass::Memory)\n  end\n\n  def llreg(context, reg_classes) : LLVM::Type\n    types = Array(Type).new\n    i = 0\n    e = reg_classes.size\n    while i < e\n      case reg_classes[i]\n      when RegClass::Int\n        types << context.int64\n      when RegClass::SSEFv\n        vec_len = llvec_len(reg_classes[i + 1..-1])\n        vec_type = context.float.vector(vec_len * 2)\n        types << vec_type\n        i += vec_len\n        next\n      when RegClass::SSEFs\n        types << context.float\n      when RegClass::SSEDs\n        types << context.double\n      else\n        raise \"Unhandled RegClass: #{reg_classes[i]}\"\n      end\n      i += 1\n    end\n    context.struct(types)\n  end\n\n  def llvec_len(reg_classes) : Int32\n    len = 1\n    reg_classes.each do |reg_class|\n      break if reg_class != RegClass::SSEUp\n      len += 1\n    end\n    len\n  end\n\n  def align(type : Type) : Int32\n    align(type, 8)\n  end\n\n  def size(type : Type) : Int32\n    size(type, 8)\n  end\n\n  def has_misaligned_fields?(type : Type, offset : Int = 0) : Bool\n    case type.kind\n    when Type::Kind::Struct\n      type.struct_element_types.each do |elem|\n        offset = align_offset(offset, elem) unless type.packed_struct?\n        return true unless offset.divisible_by?(align(elem))\n        return true if has_misaligned_fields?(elem, offset)\n        offset += size(elem)\n      end\n      false\n    when Type::Kind::Array\n      # Given:\n      #\n      # ```\n      # @[Packed]\n      # struct Foo\n      #   x : Int16\n      #   y : Int8\n      # end\n      # ```\n      #\n      # the types `Foo` and `Foo[1]` have no misaligned fields, but `Foo[2]`\n      # does, because the field `.[1].x` has offset 3 and a natural alignment of\n      # 2. Checking for the first two elements is sufficient; if both contain no\n      # misaligned fields, then `size(elem) % align(elem) == 0` must be true,\n      # meaning array indices have no effect on element alignment.\n      elem = type.element_type\n      has_misaligned_fields?(elem, offset) || type.array_size > 1 && has_misaligned_fields?(elem, offset + size(elem))\n    else\n      false\n    end\n  end\n\n  enum RegClass\n    NoClass\n    Int\n    SSEFs\n    SSEFv\n    SSEDs\n    SSEDv\n    SSEInt\n    SSEUp\n    X87\n    X87Up\n    ComplexX87\n    Memory\n\n    def sse? : Bool\n      case self\n      when SSEFs, SSEFv, SSEDs\n        true\n      else\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi/x86_win64.cr",
    "content": "require \"../abi\"\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi_x86_win64.rs\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nclass LLVM::ABI::X86_Win64 < LLVM::ABI::X86\n  private def compute_arg_types(atys, context)\n    atys.map do |t|\n      case t.kind\n      when Type::Kind::Struct\n        size = target_data.abi_size(t)\n        case size\n        when 1 then ArgType.direct(t, context.int8)\n        when 2 then ArgType.direct(t, context.int16)\n        when 4 then ArgType.direct(t, context.int32)\n        when 8 then ArgType.direct(t, context.int64)\n        else        ArgType.indirect(t, LLVM::Attribute::ByVal)\n        end\n      else\n        non_struct(t, context)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/abi.cr",
    "content": "# LLVM::ABI is deprecated. The compiler uses Crystal::ABI instead.\n\n# Based on https://github.com/rust-lang/rust/blob/29ac04402d53d358a1f6200bea45a301ff05b2d1/src/librustc_trans/trans/cabi.rs\n@[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\nabstract class LLVM::ABI\n  getter target_data : TargetData\n  getter? osx : Bool\n  getter? windows : Bool\n\n  @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n  def initialize(target_machine : TargetMachine)\n    @target_data = target_machine.data_layout\n    triple = target_machine.triple\n    @osx = !!(triple =~ /apple/)\n    @windows = !!(triple =~ /windows/)\n  end\n\n  abstract def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context)\n  abstract def size(type : Type)\n  abstract def align(type : Type)\n\n  def size(type : Type, pointer_size) : Int32\n    case type.kind\n    when Type::Kind::Integer\n      (type.int_width + 7) // 8\n    when Type::Kind::Float\n      4\n    when Type::Kind::Double\n      8\n    when Type::Kind::Pointer\n      pointer_size\n    when Type::Kind::Struct\n      if type.packed_struct?\n        type.struct_element_types.reduce(0) do |memo, elem|\n          memo + size(elem)\n        end\n      else\n        size = type.struct_element_types.reduce(0) do |memo, elem|\n          align_offset(memo, elem) + size(elem)\n        end\n        align_offset(size, type)\n      end\n    when Type::Kind::Array\n      size(type.element_type) * type.array_size\n    else\n      raise \"Unhandled Type::Kind in size: #{type.kind}\"\n    end\n  end\n\n  def align_offset(offset, type) : Int32\n    align = align(type)\n    (offset + align - 1) // align * align\n  end\n\n  def align(type : Type, pointer_size) : Int32\n    case type.kind\n    when Type::Kind::Integer\n      (type.int_width + 7) // 8\n    when Type::Kind::Float\n      4\n    when Type::Kind::Double\n      8\n    when Type::Kind::Pointer\n      pointer_size\n    when Type::Kind::Struct\n      if type.packed_struct?\n        1\n      else\n        type.struct_element_types.reduce(1) do |memo, elem|\n          Math.max(memo, align(elem))\n        end\n      end\n    when Type::Kind::Array\n      align(type.element_type)\n    else\n      raise \"Unhandled Type::Kind in align: #{type.kind}\"\n    end\n  end\n\n  @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n  enum ArgKind\n    Direct\n    Indirect\n    Ignore\n  end\n\n  @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n  struct ArgType\n    getter kind : ArgKind\n    getter type : Type\n    getter cast : Type?\n    getter pad : Nil\n    getter attr : Attribute?\n\n    def self.direct(type, cast = nil, pad = nil, attr = nil)\n      new ArgKind::Direct, type, cast, pad, attr\n    end\n\n    def self.indirect(type, attr) : self\n      new ArgKind::Indirect, type, attr: attr\n    end\n\n    def self.ignore(type) : self\n      new ArgKind::Ignore, type\n    end\n\n    @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n    def initialize(@kind, @type, @cast = nil, @pad = nil, @attr = nil)\n    end\n  end\n\n  @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n  class FunctionType\n    getter arg_types : Array(ArgType)\n    getter return_type : ArgType\n\n    @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n    def initialize(@arg_types, @return_type)\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/basic_block.cr",
    "content": "struct LLVM::BasicBlock\n  def initialize(@unwrap : LibLLVM::BasicBlockRef)\n  end\n\n  def self.null\n    LLVM::BasicBlock.new(Pointer(::Void).null.as(LibLLVM::BasicBlockRef))\n  end\n\n  def instructions\n    InstructionCollection.new self\n  end\n\n  def delete\n    LibLLVM.delete_basic_block self\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def name\n    block_name = LibLLVM.get_basic_block_name(self)\n    block_name ? String.new(block_name) : nil\n  end\n\n  def parent : Function?\n    parent_func = LibLLVM.get_basic_block_parent(self)\n    parent_func ? Function.new(parent_func) : nil\n  end\nend\n"
  },
  {
    "path": "src/llvm/basic_block_collection.cr",
    "content": "require \"./basic_block\"\n\nstruct LLVM::BasicBlockCollection\n  include Enumerable(LLVM::BasicBlock)\n\n  def initialize(@function : Function)\n  end\n\n  def append(name = \"\")\n    context = LibLLVM.get_module_context(LibLLVM.get_global_parent(@function))\n    BasicBlock.new LibLLVM.append_basic_block_in_context(context, @function, name)\n  end\n\n  def append(name = \"\", &)\n    context = LibLLVM.get_module_context(LibLLVM.get_global_parent(@function))\n    block = append name\n    # builder = Builder.new(LibLLVM.create_builder_in_context(context), LLVM::Context.new(context, dispose_on_finalize: false))\n    builder = Builder.new(LibLLVM.create_builder_in_context(context))\n    builder.position_at_end block\n    yield builder\n    block\n  end\n\n  def each(&) : Nil\n    bb = LibLLVM.get_first_basic_block(@function)\n    while bb\n      yield LLVM::BasicBlock.new bb\n      bb = LibLLVM.get_next_basic_block(bb)\n    end\n  end\n\n  def []?(name : String)\n    find(&.name.==(name))\n  end\n\n  def [](name : String)\n    self[name]? || raise IndexError.new\n  end\n\n  def last?\n    block = nil\n    each do |current_block|\n      block = current_block\n    end\n    block\n  end\nend\n"
  },
  {
    "path": "src/llvm/builder.cr",
    "content": "class LLVM::Builder\n  @disposed = false\n\n  def initialize(@unwrap : LibLLVM::BuilderRef)\n  end\n\n  def position_at_end(block)\n    LibLLVM.position_builder_at_end(self, block)\n  end\n\n  def insert_block\n    BasicBlock.new LibLLVM.get_insert_block(self)\n  end\n\n  def ret\n    Value.new LibLLVM.build_ret_void(self)\n  end\n\n  def ret(value)\n    # check_value(value)\n\n    Value.new LibLLVM.build_ret(self, value)\n  end\n\n  def br(block)\n    Value.new LibLLVM.build_br(self, block)\n  end\n\n  def cond(cond, then_block, else_block)\n    # check_value(cond)\n\n    Value.new LibLLVM.build_cond(self, cond, then_block, else_block)\n  end\n\n  def phi(type, table : LLVM::PhiTable, name = \"\")\n    # check_type(\"phi\", type)\n\n    phi type, table.blocks, table.values, name\n  end\n\n  def phi(type, incoming_blocks : Array(LLVM::BasicBlock), incoming_values : Array(LLVM::Value), name = \"\")\n    # check_type(\"phi\", type)\n\n    phi_node = LibLLVM.build_phi self, type, name\n    LibLLVM.add_incoming phi_node,\n      (incoming_values.to_unsafe.as(LibLLVM::ValueRef*)),\n      (incoming_blocks.to_unsafe.as(LibLLVM::BasicBlockRef*)),\n      incoming_blocks.size\n    Value.new phi_node\n  end\n\n  @[Deprecated(\"Pass the function type of `func` as well (equal to `func.function_type`) in order to support LLVM 15+\")]\n  def call(func : LLVM::Function, name : String = \"\")\n    # check_func(func)\n\n    Value.new LibLLVM.build_call2(self, func.function_type, func, nil, 0, name)\n  end\n\n  def call(type : LLVM::Type, func : LLVM::Function, name : String = \"\")\n    # check_type(\"call\", type)\n    # check_func(func)\n\n    Value.new LibLLVM.build_call2(self, type, func, nil, 0, name)\n  end\n\n  @[Deprecated(\"Pass the function type of `func` as well (equal to `func.function_type`) in order to support LLVM 15+\")]\n  def call(func : LLVM::Function, arg : LLVM::Value, name : String = \"\")\n    # check_func(func)\n    # check_value(arg)\n\n    value = arg.to_unsafe\n    Value.new LibLLVM.build_call2(self, func.function_type, func, pointerof(value), 1, name)\n  end\n\n  def call(type : LLVM::Type, func : LLVM::Function, arg : LLVM::Value, name : String = \"\")\n    # check_type(\"call\", type)\n    # check_func(func)\n    # check_value(arg)\n\n    value = arg.to_unsafe\n    Value.new LibLLVM.build_call2(self, type, func, pointerof(value), 1, name)\n  end\n\n  @[Deprecated(\"Pass the function type of `func` as well (equal to `func.function_type`) in order to support LLVM 15+\")]\n  def call(func : LLVM::Function, args : Array(LLVM::Value), name : String = \"\", bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null)\n    # check_func(func)\n    # check_values(args)\n\n    bundle_ref = bundle.to_unsafe\n    bundles = bundle_ref ? pointerof(bundle_ref) : Pointer(Void).null.as(LibLLVM::OperandBundleRef*)\n    num_bundles = bundle_ref ? 1 : 0\n    Value.new {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.build_call_with_operand_bundles(self, func.function_type, func, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, bundles, num_bundles, name)\n  end\n\n  def call(type : LLVM::Type, func : LLVM::Function, args : Array(LLVM::Value), name : String = \"\")\n    # check_type(\"call\", type)\n    # check_func(func)\n    # check_values(args)\n\n    Value.new LibLLVM.build_call2(self, type, func, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, name)\n  end\n\n  def call(type : LLVM::Type, func : LLVM::Function, args : Array(LLVM::Value), name : String, bundle : LLVM::OperandBundleDef)\n    # check_type(\"call\", type)\n    # check_func(func)\n    # check_values(args)\n\n    bundle_ref = bundle.to_unsafe\n    bundles = bundle_ref ? pointerof(bundle_ref) : Pointer(Void).null.as(LibLLVM::OperandBundleRef*)\n    num_bundles = bundle_ref ? 1 : 0\n    Value.new {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.build_call_with_operand_bundles(self, type, func, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, bundles, num_bundles, name)\n  end\n\n  def call(type : LLVM::Type, func : LLVM::Function, args : Array(LLVM::Value), bundle : LLVM::OperandBundleDef)\n    call(type, func, args, \"\", bundle)\n  end\n\n  def alloca(type, name = \"\")\n    # check_type(\"alloca\", type)\n\n    Value.new LibLLVM.build_alloca(self, type, name)\n  end\n\n  def store(value, ptr)\n    # check_value(value, \"value\")\n    # check_value(ptr, \"ptr\")\n\n    Value.new LibLLVM.build_store(self, value, ptr)\n  end\n\n  @[Deprecated(\"Pass the pointee of `ptr` as well (equal to `ptr.type.element_type`) in order to support LLVM 15+\")]\n  def load(ptr : LLVM::Value, name = \"\")\n    # check_value(ptr)\n\n    Value.new LibLLVM.build_load2(self, ptr.type.element_type, ptr, name)\n  end\n\n  def load(type : LLVM::Type, ptr : LLVM::Value, name = \"\")\n    # check_type(\"load\", type)\n    # check_value(ptr)\n\n    Value.new LibLLVM.build_load2(self, type, ptr, name)\n  end\n\n  def store_volatile(value, ptr)\n    store(value, ptr).tap { |v| v.volatile = true }\n  end\n\n  def load_volatile(ptr : LLVM::Value, name = \"\")\n    load(ptr, name).tap { |v| v.volatile = true }\n  end\n\n  def load_volatile(type : LLVM::Type, ptr : LLVM::Value, name = \"\")\n    load(type, ptr, name).tap { |v| v.volatile = true }\n  end\n\n  {% for method_name in %w(gep inbounds_gep) %}\n    @[Deprecated(\"Pass the type of `value` as well (equal to `value.type`) in order to support LLVM 15+\")]\n    def {{method_name.id}}(value : LLVM::Value, indices : Array(LLVM::ValueRef), name = \"\")\n      # check_value(value)\n\n      Value.new LibLLVM.build_{{method_name.id}}2(self, value.type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), indices.size, name)\n    end\n\n    def {{method_name.id}}(type : LLVM::Type, value : LLVM::Value, indices : Array(LLVM::ValueRef), name = \"\")\n      # check_type({{method_name}}, type)\n      # check_value(value)\n\n      Value.new LibLLVM.build_{{method_name.id}}2(self, type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), indices.size, name)\n    end\n\n    @[Deprecated(\"Pass the type of `value` as well (equal to `value.type`) in order to support LLVM 15+\")]\n    def {{method_name.id}}(value : LLVM::Value, index : LLVM::Value, name = \"\")\n      # check_value(value)\n\n      indices = pointerof(index).as(LibLLVM::ValueRef*)\n      Value.new LibLLVM.build_{{method_name.id}}2(self, value.type, value, indices, 1, name)\n    end\n\n    def {{method_name.id}}(type : LLVM::Type, value : LLVM::Value, index : LLVM::Value, name = \"\")\n      # check_type({{method_name}}, type)\n      # check_value(value)\n\n      indices = pointerof(index).as(LibLLVM::ValueRef*)\n      Value.new LibLLVM.build_{{method_name.id}}2(self, type, value, indices, 1, name)\n    end\n\n    @[Deprecated(\"Pass the type of `value` as well (equal to `value.type`) in order to support LLVM 15+\")]\n    def {{method_name.id}}(value : LLVM::Value, index1 : LLVM::Value, index2 : LLVM::Value, name = \"\")\n      # check_value(value)\n\n      indices = uninitialized LLVM::Value[2]\n      indices[0] = index1\n      indices[1] = index2\n      Value.new LibLLVM.build_{{method_name.id}}2(self, value.type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), 2, name)\n    end\n\n    def {{method_name.id}}(type : LLVM::Type, value : LLVM::Value, index1 : LLVM::Value, index2 : LLVM::Value, name = \"\")\n      # check_type({{method_name}}, type)\n      # check_value(value)\n\n      indices = uninitialized LLVM::Value[2]\n      indices[0] = index1\n      indices[1] = index2\n      Value.new LibLLVM.build_{{method_name.id}}2(self, type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), 2, name)\n    end\n  {% end %}\n\n  def extract_value(value, index, name = \"\")\n    # check_value(value)\n\n    Value.new LibLLVM.build_extract_value(self, value, index, name)\n  end\n\n  {% for name in %w(bit_cast zext sext trunc fpext fptrunc fp2si fp2ui si2fp ui2fp int2ptr ptr2int) %}\n    def {{name.id}}(value, type, name = \"\")\n      # check_type({{name}}, type)\n      # check_value(value)\n\n      Value.new LibLLVM.build_{{name.id}}(self, value, type, name)\n    end\n  {% end %}\n\n  {% for name in %w(add sub mul sdiv exact_sdiv udiv srem urem shl ashr lshr or and xor fadd fsub fmul fdiv) %}\n    def {{name.id}}(lhs, rhs, name = \"\")\n      # check_value(lhs)\n      # check_value(rhs)\n\n      Value.new LibLLVM.build_{{name.id}}(self, lhs, rhs, name)\n    end\n  {% end %}\n\n  {% for name in %w(icmp fcmp) %}\n    def {{name.id}}(op, lhs, rhs, name = \"\")\n      # check_value(lhs)\n      # check_value(rhs)\n\n      Value.new LibLLVM.build_{{name.id}}(self, op, lhs, rhs, name)\n    end\n  {% end %}\n\n  {% for name in %w(not neg fneg) %}\n    def {{name.id}}(value, name = \"\")\n      # check_value(value)\n\n      Value.new LibLLVM.build_{{name.id}}(self, value, name)\n    end\n  {% end %}\n\n  def unreachable\n    Value.new LibLLVM.build_unreachable(self)\n  end\n\n  def select(cond, a_then, a_else, name = \"\")\n    # check_value(cond)\n    # check_value(a_then)\n    # check_value(a_else)\n\n    Value.new LibLLVM.build_select self, cond, a_then, a_else, name\n  end\n\n  def global_string_pointer(string, name = \"\")\n    Value.new LibLLVM.build_global_string_ptr self, string, name\n  end\n\n  def landing_pad(type, personality, clauses, name = \"\")\n    # check_type(\"landing_pad\", type)\n\n    lpad = LibLLVM.build_landing_pad self, type, personality, clauses.size, name\n    LibLLVM.set_cleanup lpad, 1\n    clauses.each do |clause|\n      LibLLVM.add_clause lpad, clause\n    end\n    Value.new lpad\n  end\n\n  def catch_switch(parent_pad, basic_block, num_handlers, name = \"\")\n    Value.new LibLLVM.build_catch_switch(self, parent_pad, basic_block, num_handlers, name)\n  end\n\n  def catch_pad(parent_pad, args : Array(LLVM::Value), name = \"\")\n    Value.new LibLLVM.build_catch_pad(self, parent_pad, args.to_unsafe.as(LibLLVM::ValueRef*), args.size, name)\n  end\n\n  def add_handler(catch_switch_ref, handler)\n    LibLLVM.add_handler catch_switch_ref, handler\n  end\n\n  def build_operand_bundle_def(name, values : Array(LLVM::Value))\n    LLVM::OperandBundleDef.new {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.create_operand_bundle(name, name.bytesize, values.to_unsafe.as(LibLLVM::ValueRef*), values.size)\n  end\n\n  def build_catch_ret(pad, basic_block)\n    LibLLVM.build_catch_ret(self, pad, basic_block)\n  end\n\n  @[Deprecated(\"Pass the function type of `fn` as well (equal to `fn.function_type`) in order to support LLVM 15+\")]\n  def invoke(fn : LLVM::Function, args : Array(LLVM::Value), a_then, a_catch, bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null, name = \"\")\n    # check_func(fn)\n\n    bundle_ref = bundle.to_unsafe\n    bundles = bundle_ref ? pointerof(bundle_ref) : Pointer(Void).null.as(LibLLVM::OperandBundleRef*)\n    num_bundles = bundle_ref ? 1 : 0\n    Value.new {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.build_invoke_with_operand_bundles(self, fn.function_type, fn, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, a_then, a_catch, bundles, num_bundles, name)\n  end\n\n  def invoke(type : LLVM::Type, fn : LLVM::Function, args : Array(LLVM::Value), a_then, a_catch, *, name = \"\")\n    # check_type(\"invoke\", type)\n    # check_func(fn)\n\n    Value.new LibLLVM.build_invoke2 self, type, fn, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, a_then, a_catch, name\n  end\n\n  def invoke(type : LLVM::Type, fn : LLVM::Function, args : Array(LLVM::Value), a_then, a_catch, bundle : LLVM::OperandBundleDef, name = \"\")\n    # check_type(\"invoke\", type)\n    # check_func(fn)\n\n    bundle_ref = bundle.to_unsafe\n    bundles = bundle_ref ? pointerof(bundle_ref) : Pointer(Void).null.as(LibLLVM::OperandBundleRef*)\n    num_bundles = bundle_ref ? 1 : 0\n    Value.new {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.build_invoke_with_operand_bundles(self, type, fn, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, a_then, a_catch, bundles, num_bundles, name)\n  end\n\n  def switch(value, otherwise, cases)\n    # check_value(value)\n\n    switch = LibLLVM.build_switch self, value, otherwise, cases.size\n    cases.each do |case_value, block|\n      LibLLVM.add_case switch, case_value, block\n    end\n    switch\n  end\n\n  def atomicrmw(op, ptr, val, ordering, singlethread)\n    Value.new LibLLVM.build_atomicrmw(self, op, ptr, val, ordering, singlethread ? 1 : 0)\n  end\n\n  def cmpxchg(pointer, cmp, new, success_ordering, failure_ordering, singlethread : Bool = false)\n    Value.new LibLLVM.build_atomic_cmp_xchg(self, pointer, cmp, new, success_ordering, failure_ordering, singlethread ? 1 : 0)\n  end\n\n  def fence(ordering, singlethread, name = \"\")\n    Value.new LibLLVM.build_fence(self, ordering, singlethread ? 1 : 0, name)\n  end\n\n  def va_arg(list, type, name = \"\")\n    Value.new LibLLVM.build_va_arg(self, list, type, name)\n  end\n\n  @[Deprecated(\"Call `#set_current_debug_location(metadata, context)` or `#clear_current_debug_location` instead\")]\n  def set_current_debug_location(line, column, scope, inlined_at = nil)\n    LibLLVMExt.set_current_debug_location(self, line, column, scope, inlined_at)\n  end\n\n  def set_current_debug_location(metadata, context : LLVM::Context)\n    {% if LibLLVM::IS_LT_90 %}\n      LibLLVM.set_current_debug_location(self, LibLLVM.metadata_as_value(context, metadata))\n    {% else %}\n      LibLLVM.set_current_debug_location2(self, metadata)\n    {% end %}\n  end\n\n  def clear_current_debug_location\n    {% if LibLLVM::IS_LT_90 %}\n      LibLLVMExt.clear_current_debug_location(self)\n    {% else %}\n      LibLLVM.set_current_debug_location2(self, nil)\n    {% end %}\n  end\n\n  def set_metadata(value, kind, node)\n    LibLLVM.set_metadata(value, kind, node)\n  end\n\n  def current_debug_location\n    Value.new LibLLVM.get_current_debug_location(self)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  protected def dispose\n    return if @disposed\n    @disposed = true\n\n    LibLLVM.dispose_builder(@unwrap)\n  end\n\n  def finalize\n    dispose\n  end\n\n  # The next lines are for ease debugging when a types/values\n  # are incorrectly used across contexts.\n\n  # private def check_type(name, type)\n  #   if @context != type.context\n  #     Context.wrong(@context, type.context, \"wrong context for #{name}\")\n  #   end\n  # end\n\n  # private def check_func(func)\n  #   # An instruction such as a bitcast to a function type can be passed\n  #   # to build, and in that case there's no need to check for context equality\n  #   return unless func.kind.function?\n\n  #   context = LibLLVM.get_module_context(LibLLVM.get_global_parent(func))\n  #   if @context.@unwrap != context\n  #     Context.wrong(@context, LLVM::Context.new(context, dispose_on_finalize: false), \"wrong context for #{func}\")\n  #   end\n  # end\n\n  # private def check_value(value, msg = nil)\n  #   type = value.type\n  #   ctx = type.context\n  #   if @context != ctx\n  #     Context.wrong(@context, ctx, \"wrong context for value #{value} #{msg ? \"(#{msg})\" : \"\"}\")\n  #   end\n  # end\n\n  # private def check_values(values)\n  #   values.each do |value|\n  #     check_value(value)\n  #   end\n  # end\nend\n"
  },
  {
    "path": "src/llvm/context.cr",
    "content": "class LLVM::Context\n  def self.new(*, dispose_on_finalize : Bool = true)\n    new(LibLLVM.create_context, dispose_on_finalize)\n  end\n\n  def initialize(@unwrap : LibLLVM::ContextRef, @dispose_on_finalize = true)\n    @disposed = false\n    @builders = [] of LLVM::Builder\n  end\n\n  def new_module(name : String) : Module\n    Module.new(LibLLVM.module_create_with_name_in_context(name, self), self)\n  end\n\n  def new_builder : Builder\n    # builder = Builder.new(LibLLVM.create_builder_in_context(self), self)\n    builder = Builder.new(LibLLVM.create_builder_in_context(self))\n    @builders << builder\n    builder\n  end\n\n  def void : Type\n    Type.new LibLLVM.void_type_in_context(self)\n  end\n\n  def int1 : Type\n    Type.new LibLLVM.int1_type_in_context(self)\n  end\n\n  def int8 : Type\n    Type.new LibLLVM.int8_type_in_context(self)\n  end\n\n  def int16 : Type\n    Type.new LibLLVM.int16_type_in_context(self)\n  end\n\n  def int32 : Type\n    Type.new LibLLVM.int32_type_in_context(self)\n  end\n\n  def int64 : Type\n    Type.new LibLLVM.int64_type_in_context(self)\n  end\n\n  def int128 : Type\n    Type.new LibLLVM.int128_type_in_context(self)\n  end\n\n  def int(bits : Int) : Type\n    Type.new LibLLVM.int_type_in_context(self, bits)\n  end\n\n  def half : Type\n    Type.new LibLLVM.half_type_in_context(self)\n  end\n\n  def float : Type\n    Type.new LibLLVM.float_type_in_context(self)\n  end\n\n  def double : Type\n    Type.new LibLLVM.double_type_in_context(self)\n  end\n\n  def x86_fp80 : Type\n    Type.new LibLLVM.x86_fp80_type_in_context(self)\n  end\n\n  def fp128 : Type\n    Type.new LibLLVM.fp128_type_in_context(self)\n  end\n\n  def ppc_fp128 : Type\n    Type.new LibLLVM.ppc_fp128_type_in_context(self)\n  end\n\n  def pointer(address_space = 0) : Type\n    {% if LibLLVM::IS_LT_150 %}\n      {% raise \"Opaque pointers are only supported on LLVM 15.0 or above\" %}\n    {% else %}\n      Type.new LibLLVM.pointer_type_in_context(self, address_space)\n    {% end %}\n  end\n\n  def void_pointer(address_space = 0) : Type\n    {% if LibLLVM::IS_LT_150 %}\n      int8.pointer(address_space)\n    {% else %}\n      pointer(address_space)\n    {% end %}\n  end\n\n  def struct(name : String, packed = false, &) : Type\n    llvm_struct = LibLLVM.struct_create_named(self, name)\n    the_struct = Type.new llvm_struct\n    element_types = (yield the_struct).as(Array(LLVM::Type))\n    LibLLVM.struct_set_body(llvm_struct, (element_types.to_unsafe.as(LibLLVM::TypeRef*)), element_types.size, packed ? 1 : 0)\n    the_struct\n  end\n\n  def struct(element_types : Array(LLVM::Type), name = nil, packed = false) : Type\n    if name\n      self.struct(name, packed) { element_types }\n    else\n      Type.new LibLLVM.struct_type_in_context(self, (element_types.to_unsafe.as(LibLLVM::TypeRef*)), element_types.size, packed ? 1 : 0)\n    end\n  end\n\n  def const_string(string : String) : Value\n    const_bytes(string.unsafe_byte_slice(0, string.bytesize + 1))\n  end\n\n  def const_bytes(bytes : Bytes) : Value\n    {% if LibLLVM::IS_LT_190 %}\n      Value.new LibLLVM.const_string_in_context(self, bytes, bytes.size, 1)\n    {% else %}\n      Value.new LibLLVM.const_string_in_context2(self, bytes, bytes.size, 1)\n    {% end %}\n  end\n\n  def const_struct(values : Array(LLVM::Value), packed = false) : Value\n    Value.new LibLLVM.const_struct_in_context(self, (values.to_unsafe.as(LibLLVM::ValueRef*)), values.size, packed ? 1 : 0)\n  end\n\n  def md_string(value : String) : Value\n    LLVM::Value.new LibLLVM.md_string_in_context2(self, value, value.bytesize)\n  end\n\n  def md_node(values : Array(Value)) : Value\n    Value.new LibLLVM.md_node_in_context2(self, (values.to_unsafe.as(LibLLVM::ValueRef*)), values.size)\n  end\n\n  def parse_ir(buf : MemoryBuffer)\n    ret = LibLLVM.parse_ir_in_context(self, buf, out mod, out msg)\n    if ret != 0 && msg\n      raise LLVM.string_and_dispose(msg)\n    end\n    Module.new(mod, self)\n  end\n\n  def ==(other : self)\n    @unwrap == other.@unwrap\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def finalize\n    return unless @dispose_on_finalize\n    return if @disposed\n    @disposed = true\n\n    @builders.each &.dispose\n\n    LibLLVM.dispose_context(self)\n  end\n\n  # The next lines are for ease debugging when a types/values\n  # are incorrectly used across contexts.\n\n  # @@info = {} of UInt64 => String\n\n  # def self.register(context : Context, name : String)\n  #   @@info[context.@unwrap.address] = name\n  # end\n\n  # def self.lookup(context : Context)\n  #   @@info[context.@unwrap.address]? || \"global\"\n  # end\n\n  # def self.wrong(expected, got, msg)\n  #   raise \"#{msg} (expected #{lookup(expected)}, got #{lookup(got)})\"\n  # end\nend\n"
  },
  {
    "path": "src/llvm/di_builder.cr",
    "content": "require \"./lib_llvm\"\n\n@[Experimental(\"The C API wrapped by this type is marked as experimental by LLVM.\")]\nstruct LLVM::DIBuilder\n  private DW_TAG_structure_type = 19\n\n  private def initialize(@unwrap : LibLLVM::DIBuilderRef, @llvm_module : Module)\n  end\n\n  def self.new(mod : LLVM::Module)\n    new(LibLLVM.create_di_builder(mod), mod)\n  end\n\n  def dispose\n    LibLLVM.dispose_di_builder(self)\n  end\n\n  def context\n    @llvm_module.context\n  end\n\n  def create_compile_unit(lang : DwarfSourceLanguage, file, dir, producer, optimized, flags, runtime_version)\n    file = create_file(file, dir)\n    {% if LibLLVM::IS_LT_110 %}\n      LibLLVM.di_builder_create_compile_unit(self,\n        lang, file, producer, producer.bytesize, optimized ? 1 : 0, flags, flags.bytesize, runtime_version,\n        split_name: nil, split_name_len: 0, kind: LibLLVM::DWARFEmissionKind::Full, dwo_id: 0,\n        split_debug_inlining: 1, debug_info_for_profiling: 0,\n      )\n    {% else %}\n      LibLLVM.di_builder_create_compile_unit(self,\n        lang, file, producer, producer.bytesize, optimized ? 1 : 0, flags, flags.bytesize, runtime_version,\n        split_name: nil, split_name_len: 0, kind: LibLLVM::DWARFEmissionKind::Full, dwo_id: 0,\n        split_debug_inlining: 1, debug_info_for_profiling: 0, sys_root: nil, sys_root_len: 0, sdk: nil, sdk_len: 0,\n      )\n    {% end %}\n  end\n\n  @[Deprecated(\"Pass an `LLVM::DwarfSourceLanguage` for `lang` instead\")]\n  def create_compile_unit(lang cpp_lang_code, file, dir, producer, optimized, flags, runtime_version)\n    # map the c++ values from `llvm::dwarf::SourceLanguage` to the c values from `LLVMDWARFSourceLanguage`\n    c_lang_code =\n      case cpp_lang_code\n      when 0x8001; DwarfSourceLanguage::Mips_Assembler\n      when 0x8e57; DwarfSourceLanguage::GOOGLE_RenderScript\n      when 0xb000; DwarfSourceLanguage::BORLAND_Delphi\n      else         DwarfSourceLanguage.new(lang - 1)\n      end\n\n    create_compile_unit(c_lang_code, file, dir, producer, optimized, flags, runtime_version)\n  end\n\n  def create_basic_type(name, size_in_bits, align_in_bits, encoding)\n    LibLLVM.di_builder_create_basic_type(self, name, name.bytesize, size_in_bits, encoding.value, DIFlags::Zero)\n  end\n\n  def get_or_create_type_array(types : Array(LibLLVM::MetadataRef))\n    LibLLVM.di_builder_get_or_create_type_array(self, types, types.size)\n  end\n\n  def create_subroutine_type(file, parameter_types)\n    LibLLVM.di_builder_create_subroutine_type(self, file, parameter_types, parameter_types.size, DIFlags::Zero)\n  end\n\n  def create_file(file, dir)\n    LibLLVM.di_builder_create_file(self, file, file.bytesize, dir, dir.bytesize)\n  end\n\n  def create_lexical_block(scope, file_scope, line, column)\n    LibLLVM.di_builder_create_lexical_block(self, scope, file_scope, line, column)\n  end\n\n  def create_lexical_block_file(scope, file_scope, discriminator = 0)\n    LibLLVM.di_builder_create_lexical_block_file(self, scope, file_scope, discriminator)\n  end\n\n  def create_function(scope, name, linkage_name, file, line, composite_type, is_local_to_unit, is_definition,\n                      scope_line, flags, is_optimized, func)\n    sub = LibLLVM.di_builder_create_function(self, scope, name, name.bytesize,\n      linkage_name, linkage_name.bytesize, file, line, composite_type, is_local_to_unit ? 1 : 0,\n      is_definition ? 1 : 0, scope_line, flags, is_optimized ? 1 : 0)\n    LibLLVM.set_subprogram(func, sub)\n    sub\n  end\n\n  def create_auto_variable(scope, name, file, line, type, align_in_bits, flags = DIFlags::Zero)\n    LibLLVM.di_builder_create_auto_variable(self, scope, name, name.bytesize, file, line, type, 1, flags, align_in_bits)\n  end\n\n  def create_parameter_variable(scope, name, argno, file, line, type, flags = DIFlags::Zero)\n    LibLLVM.di_builder_create_parameter_variable(self, scope, name, name.bytesize, argno, file, line, type, 1, flags)\n  end\n\n  def create_expression(addr, length)\n    LibLLVM.di_builder_create_expression(self, addr, length)\n  end\n\n  def insert_declare_at_end(storage, var_info, expr, dl : LibLLVM::MetadataRef, block)\n    {% if LibLLVM::IS_LT_190 %}\n      LibLLVM.di_builder_insert_declare_at_end(self, storage, var_info, expr, dl, block)\n    {% else %}\n      LibLLVM.di_builder_insert_declare_record_at_end(self, storage, var_info, expr, dl, block)\n    {% end %}\n  end\n\n  def get_or_create_array(elements : Array(LibLLVM::MetadataRef))\n    LibLLVM.di_builder_get_or_create_array(self, elements, elements.size)\n  end\n\n  def create_enumerator(name, value)\n    is_unsigned = value.is_a?(Int::Unsigned) ? 1 : 0\n\n    {% unless LibLLVM::IS_LT_210 %}\n      if value.is_a?(Int128) || value.is_a?(UInt128)\n        encoded_value = UInt64[value & UInt64::MAX, (value >> 64) & UInt64::MAX]\n        return LibLLVM.di_builder_create_enumerator_of_arbitrary_precision(\n          self, name, name.bytesize, encoded_value.size * 64, encoded_value, is_unsigned)\n      end\n    {% end %}\n\n    {{ LibLLVM::IS_LT_90 ? LibLLVMExt : LibLLVM }}.di_builder_create_enumerator(\n      self, name, name.bytesize, value.to_i64!, is_unsigned)\n  end\n\n  def create_enumeration_type(scope, name, file, line_number, size_in_bits, align_in_bits, elements, underlying_type)\n    LibLLVM.di_builder_create_enumeration_type(self, scope, name, name.bytesize, file, line_number,\n      size_in_bits, align_in_bits, elements, elements.size, underlying_type)\n  end\n\n  def create_struct_type(scope, name, file, line, size_in_bits, align_in_bits, flags, derived_from, element_types)\n    LibLLVM.di_builder_create_struct_type(self, scope, name, name.bytesize, file, line,\n      size_in_bits, align_in_bits, flags, derived_from, element_types, element_types.size, 0, nil, nil, 0)\n  end\n\n  def create_union_type(scope, name, file, line, size_in_bits, align_in_bits, flags, element_types)\n    LibLLVM.di_builder_create_union_type(self, scope, name, name.bytesize, file, line,\n      size_in_bits, align_in_bits, flags, element_types, element_types.size, 0, nil, 0)\n  end\n\n  def create_array_type(size_in_bits, align_in_bits, type, subs)\n    LibLLVM.di_builder_create_array_type(self, size_in_bits, align_in_bits, type, subs, subs.size)\n  end\n\n  def create_member_type(scope, name, file, line, size_in_bits, align_in_bits, offset_in_bits, flags, ty)\n    LibLLVM.di_builder_create_member_type(self, scope, name, name.bytesize, file, line, size_in_bits, align_in_bits,\n      offset_in_bits, flags, ty)\n  end\n\n  def create_pointer_type(pointee, size_in_bits, align_in_bits, name)\n    LibLLVM.di_builder_create_pointer_type(self, pointee, size_in_bits, align_in_bits, 0, name, name.bytesize)\n  end\n\n  def create_replaceable_composite_type(scope, name, file, line)\n    LibLLVM.di_builder_create_replaceable_composite_type(self, DW_TAG_structure_type, name, name.bytesize,\n      scope, file, line, 0, 0, 0, DIFlags::FwdDecl, nil, 0)\n  end\n\n  def replace_temporary(from, to)\n    LibLLVM.metadata_replace_all_uses_with(from, to)\n  end\n\n  def create_unspecified_type(name : String)\n    LibLLVM.di_builder_create_unspecified_type(self, name, name.bytesize)\n  end\n\n  def get_or_create_array_subrange(lo, count)\n    LibLLVM.di_builder_get_or_create_subrange(self, lo, count)\n  end\n\n  def create_debug_location(line, column, scope, inlined_at = nil)\n    LibLLVM.di_builder_create_debug_location(context, line, column, scope, inlined_at)\n  end\n\n  def end\n    LibLLVM.di_builder_finalize(self)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  @[Deprecated(\"Use a `LibLLVM::MetadataRef` for `dl` instead\")]\n  def insert_declare_at_end(storage, var_info, expr, dl : LibLLVM::ValueRef | LLVM::Value, block)\n    dl = dl.to_unsafe unless dl.is_a?(LibLLVM::ValueRef)\n    insert_declare_at_end(storage, var_info, expr, LibLLVM.value_as_metadata(dl), block)\n  end\n\n  @[Deprecated(\"Pass an array for `parameter_types` directly\")]\n  def create_subroutine_type(file, parameter_types : LibLLVM::MetadataRef)\n    create_subroutine_type(file, extract_metadata_array(parameter_types))\n  end\n\n  @[Deprecated(\"Pass an array for `elements` directly\")]\n  def create_enumeration_type(scope, name, file, line_number, size_in_bits, align_in_bits, elements : LibLLVM::MetadataRef, underlying_type)\n    create_enumeration_type(scope, name, file, line_number, size_in_bits, align_in_bits, extract_metadata_array(elements), underlying_type)\n  end\n\n  @[Deprecated(\"Pass an array for `element_types` directly\")]\n  def create_struct_type(scope, name, file, line, size_in_bits, align_in_bits, flags, derived_from, element_types : LibLLVM::MetadataRef)\n    create_struct_type(scope, name, file, line, size_in_bits, align_in_bits, flags, derived_from, extract_metadata_array(element_types))\n  end\n\n  @[Deprecated(\"Pass an array for `element_types` directly\")]\n  def create_union_type(scope, name, file, line, size_in_bits, align_in_bits, flags, element_types : LibLLVM::MetadataRef)\n    create_union_type(scope, name, file, line, size_in_bits, align_in_bits, flags, extract_metadata_array(element_types))\n  end\n\n  @[Deprecated(\"Pass an array for `subs` directly\")]\n  def create_array_type(size_in_bits, align_in_bits, type, subs : LibLLVM::MetadataRef)\n    create_array_type(size_in_bits, align_in_bits, type, extract_metadata_array(subs))\n  end\n\n  private def extract_metadata_array(metadata : LibLLVM::MetadataRef)\n    metadata_as_value = LibLLVM.metadata_as_value(context, metadata)\n    operand_count = LibLLVM.get_md_node_num_operands(metadata_as_value).to_i\n    operands = Pointer(LibLLVM::ValueRef).malloc(operand_count)\n    LibLLVM.get_md_node_operands(metadata_as_value, operands)\n    Slice.new(operand_count) { |i| LibLLVM.value_as_metadata(operands[i]) }\n  end\nend\n"
  },
  {
    "path": "src/llvm/enums/atomic.cr",
    "content": "module LLVM\n  enum AtomicOrdering\n    NotAtomic              = 0\n    Unordered              = 1\n    Monotonic              = 2\n    Acquire                = 4\n    Release                = 5\n    AcquireRelease         = 6\n    SequentiallyConsistent = 7\n  end\n\n  enum AtomicRMWBinOp\n    Xchg\n    Add\n    Sub\n    And\n    Nand\n    Or\n    Xor\n    Max\n    Min\n    Umax\n    Umin\n    Fadd\n    Fsub\n  end\nend\n"
  },
  {
    "path": "src/llvm/enums.cr",
    "content": "module LLVM\n  @[Flags]\n  enum Attribute : UInt64\n    Alignment\n    AllocSize\n    AlwaysInline\n    ArgMemOnly\n    Builtin\n    ByVal\n    Cold\n    Convergent\n    Dereferenceable\n    DereferenceableOrNull\n    InAlloca\n    InReg\n    InaccessibleMemOnly\n    InaccessibleMemOrArgMemOnly\n    InlineHint\n    JumpTable\n    MinSize\n    Naked\n    Nest\n    NoAlias\n    NoBuiltin\n    NoCapture\n    NoDuplicate\n    NoFree\n    NoImplicitFloat\n    NoInline\n    NoRecurse\n    NoRedZone\n    NoReturn\n    NoSync\n    NoUnwind\n    NonLazyBind\n    NonNull\n    OptimizeForSize\n    OptimizeNone\n    ReadNone\n    ReadOnly\n    Returned\n    ImmArg\n    ReturnsTwice\n    SExt\n    SafeStack\n    SanitizeAddress\n    SanitizeMemory\n    SanitizeThread\n    StackAlignment\n    StackProtect\n    StackProtectReq\n    StackProtectStrong\n    StructRet\n    SwiftError\n    SwiftSelf\n    UWTable\n    WillReturn\n    WriteOnly\n    ZExt\n    Captures\n\n    # NOTE: enum body does not allow `class_getter` or `TypeDeclaration`, hence\n    # the nil cast\n    @@kind_ids = nil.as(Hash(Attribute, UInt32)?)\n\n    protected def self.kind_ids\n      @@kind_ids ||= load_llvm_kinds_from_names\n    end\n\n    @@typed_attrs = nil.as(Array(Attribute)?)\n\n    private def self.typed_attrs\n      @@typed_attrs ||= load_llvm_typed_attributes\n    end\n\n    def each_kind(& : UInt32 ->)\n      kind_ids = Attribute.kind_ids\n      each do |member|\n        yield kind_ids[member]\n      end\n    end\n\n    private def self.kind_for_name(name : String)\n      LibLLVM.get_enum_attribute_kind_for_name(name, name.bytesize)\n    end\n\n    private def self.load_llvm_kinds_from_names\n      kinds = {} of Attribute => UInt32\n      kinds[Alignment] = kind_for_name(\"align\")\n      kinds[AllocSize] = kind_for_name(\"allocsize\")\n      kinds[AlwaysInline] = kind_for_name(\"alwaysinline\")\n      kinds[ArgMemOnly] = kind_for_name(\"argmemonly\")\n      kinds[Builtin] = kind_for_name(\"builtin\")\n      kinds[ByVal] = kind_for_name(\"byval\")\n      kinds[Captures] = kind_for_name(\"captures\")\n      kinds[Cold] = kind_for_name(\"cold\")\n      kinds[Convergent] = kind_for_name(\"convergent\")\n      kinds[Dereferenceable] = kind_for_name(\"dereferenceable\")\n      kinds[DereferenceableOrNull] = kind_for_name(\"dereferenceable_or_null\")\n      kinds[InAlloca] = kind_for_name(\"inalloca\")\n      kinds[InReg] = kind_for_name(\"inreg\")\n      kinds[InaccessibleMemOnly] = kind_for_name(\"inaccessiblememonly\")\n      kinds[InaccessibleMemOrArgMemOnly] = kind_for_name(\"inaccessiblemem_or_argmemonly\")\n      kinds[InlineHint] = kind_for_name(\"inlinehint\")\n      kinds[JumpTable] = kind_for_name(\"jumptable\")\n      kinds[MinSize] = kind_for_name(\"minsize\")\n      kinds[Naked] = kind_for_name(\"naked\")\n      kinds[Nest] = kind_for_name(\"nest\")\n      kinds[NoAlias] = kind_for_name(\"noalias\")\n      kinds[NoBuiltin] = kind_for_name(\"nobuiltin\")\n      kinds[NoCapture] = kind_for_name(\"nocapture\")\n      kinds[NoDuplicate] = kind_for_name(\"noduplicate\")\n      kinds[NoFree] = kind_for_name(\"nofree\")\n      kinds[NoImplicitFloat] = kind_for_name(\"noimplicitfloat\")\n      kinds[NoInline] = kind_for_name(\"noinline\")\n      kinds[NoRecurse] = kind_for_name(\"norecurse\")\n      kinds[NoRedZone] = kind_for_name(\"noredzone\")\n      kinds[NoReturn] = kind_for_name(\"noreturn\")\n      kinds[NoSync] = kind_for_name(\"nosync\")\n      kinds[NoUnwind] = kind_for_name(\"nounwind\")\n      kinds[NonLazyBind] = kind_for_name(\"nonlazybind\")\n      kinds[NonNull] = kind_for_name(\"nonnull\")\n      kinds[OptimizeForSize] = kind_for_name(\"optsize\")\n      kinds[OptimizeNone] = kind_for_name(\"optnone\")\n      kinds[ReadNone] = kind_for_name(\"readnone\")\n      kinds[ReadOnly] = kind_for_name(\"readonly\")\n      kinds[Returned] = kind_for_name(\"returned\")\n      kinds[ImmArg] = kind_for_name(\"immarg\")\n      kinds[ReturnsTwice] = kind_for_name(\"returns_twice\")\n      kinds[SExt] = kind_for_name(\"signext\")\n      kinds[SafeStack] = kind_for_name(\"safestack\")\n      kinds[SanitizeAddress] = kind_for_name(\"sanitize_address\")\n      kinds[SanitizeMemory] = kind_for_name(\"sanitize_memory\")\n      kinds[SanitizeThread] = kind_for_name(\"sanitize_thread\")\n      kinds[StackAlignment] = kind_for_name(\"alignstack\")\n      kinds[StackProtect] = kind_for_name(\"ssp\")\n      kinds[StackProtectReq] = kind_for_name(\"sspreq\")\n      kinds[StackProtectStrong] = kind_for_name(\"sspstrong\")\n      kinds[StructRet] = kind_for_name(\"sret\")\n      kinds[SwiftError] = kind_for_name(\"swifterror\")\n      kinds[SwiftSelf] = kind_for_name(\"swiftself\")\n      kinds[UWTable] = kind_for_name(\"uwtable\")\n      kinds[WillReturn] = kind_for_name(\"willreturn\")\n      kinds[WriteOnly] = kind_for_name(\"writeonly\")\n      kinds[ZExt] = kind_for_name(\"zeroext\")\n      kinds\n    end\n\n    private def self.load_llvm_typed_attributes\n      typed_attrs = [] of Attribute\n\n      unless LibLLVM::IS_LT_120\n        # LLVM 12 introduced mandatory type parameters for byval and sret\n        typed_attrs << ByVal\n        typed_attrs << StructRet\n      end\n\n      unless LibLLVM::IS_LT_130\n        # LLVM 13 mandates type params for inalloca\n        typed_attrs << InAlloca\n      end\n\n      typed_attrs\n    end\n\n    def self.kind_for(member)\n      kind_ids[member]\n    end\n\n    def self.from_kind(kind)\n      kind_ids.key_for(kind)\n    end\n\n    def self.requires_type?(kind)\n      member = from_kind(kind)\n      typed_attrs.includes?(member)\n    end\n  end\n\n  # Attribute index are either ReturnIndex (0), FunctionIndex (-1) or a\n  # parameter number ranging from 1 to N.\n  enum AttributeIndex : UInt32\n    ReturnIndex   = 0_u32\n    FunctionIndex = ~0_u32\n  end\n\n  enum Linkage\n    External\n    AvailableExternally\n    LinkOnceAny\n    LinkOnceODR\n    LinkOnceODRAutoHide\n    WeakAny\n    WeakODR\n    Appending\n    Internal\n    Private\n    DLLImport # obsolete\n    DLLExport # obsolete\n    ExternalWeak\n    Ghost\n    Common\n    LinkerPrivate\n    LinkerPrivateWeak\n  end\n\n  enum DLLStorageClass\n    Default\n\n    # Function to be imported from DLL.\n    DLLImport\n\n    # Function to be accessible from DLL.\n    DLLExport\n  end\n\n  enum IntPredicate\n    EQ  = 32\n    NE\n    UGT\n    UGE\n    ULT\n    ULE\n    SGT\n    SGE\n    SLT\n    SLE\n  end\n\n  enum RealPredicate\n    PredicateFalse\n    OEQ\n    OGT\n    OGE\n    OLT\n    OLE\n    ONE\n    ORD\n    UNO\n    UEQ\n    UGT\n    UGE\n    ULT\n    ULE\n    UNE\n    PredicateTrue\n  end\n\n  struct Type\n    enum Kind\n      Void\n      Half\n      Float\n      Double\n      X86_FP80\n      FP128\n      PPC_FP128\n      Label\n      Integer\n      Function\n      Struct\n      Array\n      Pointer\n      Vector\n      Metadata\n      X86_MMX # deleted in LLVM 20\n      Token\n      ScalableVector\n      BFloat\n      X86_AMX\n      TargetExt\n    end\n  end\n\n  enum CodeGenOptLevel\n    None\n    Less\n    Default\n    Aggressive\n  end\n\n  enum CodeGenFileType\n    AssemblyFile\n    ObjectFile\n  end\n\n  enum RelocMode\n    Default\n    Static\n    PIC\n    DynamicNoPIC\n  end\n\n  enum CodeModel\n    Default\n    JITDefault\n    Tiny\n    Small\n    Kernel\n    Medium\n    Large\n  end\n\n  enum VerifierFailureAction\n    AbortProcessAction # verifier will print to stderr and abort()\n    PrintMessageAction # verifier will print to stderr and return 1\n    ReturnStatusAction # verifier will just return 1\n  end\n\n  enum CallConvention\n    C            =  0\n    Fast         =  8\n    Cold         =  9\n    WebKit_JS    = 12\n    AnyReg       = 13\n    X86_StdCall  = 64\n    X86_FastCall = 65\n  end\n\n  enum DwarfTag\n    AutoVariable = 0x100\n  end\n\n  enum DwarfTypeEncoding\n    Address        = 0x01\n    Boolean        = 0x02\n    ComplexFloat   = 0x03\n    Float          = 0x04\n    Signed         = 0x05\n    SignedChar     = 0x06\n    Unsigned       = 0x07\n    UnsignedChar   = 0x08\n    ImaginaryFloat = 0x09\n    PackedDecimal  = 0x0a\n    NumericString  = 0x0b\n    Edited         = 0x0c\n    SignedFixed    = 0x0d\n    UnsignedFixed  = 0x0e\n    DecimalFloat   = 0x0f\n    Utf            = 0x10\n    LoUser         = 0x80\n    HiUser         = 0xff\n  end\n\n  enum DwarfSourceLanguage\n    C89\n    C\n    Ada83\n    C_plus_plus\n    Cobol74\n    Cobol85\n    Fortran77\n    Fortran90\n    Pascal83\n    Modula2\n\n    # New in DWARF v3:\n\n    Java\n    C99\n    Ada95\n    Fortran95\n    PLI\n    ObjC\n    ObjC_plus_plus\n    UPC\n    D\n\n    # New in DWARF v4:\n\n    Python\n\n    # New in DWARF v5:\n\n    OpenCL\n    Go\n    Modula3\n    Haskell\n    C_plus_plus_03\n    C_plus_plus_11\n    OCaml\n    Rust\n    C11\n    Swift\n    Julia\n    Dylan\n    C_plus_plus_14\n    Fortran03\n    Fortran08\n    RenderScript\n    BLISS\n\n    {% unless LibLLVM::IS_LT_160 %}\n      Kotlin\n      Zig\n      Crystal\n      C_plus_plus_17\n      C_plus_plus_20\n      C17\n      Fortran18\n      Ada2005\n      Ada2012\n    {% end %}\n\n    # Vendor extensions:\n\n    Mips_Assembler\n    GOOGLE_RenderScript\n    BORLAND_Delphi\n  end\n\n  enum DIFlags : UInt32\n    Zero       = 0\n    Private    = 1\n    Protected  = 2\n    Public     = 3\n    FwdDecl    = 1 << 2\n    AppleBlock = 1 << 3\n\n    {% if LibLLVM::IS_LT_100 %}\n      BlockByrefStruct = 1 << 4\n    {% else %}\n      ReservedBit4 = 1 << 4\n    {% end %}\n\n    Virtual             = 1 << 5\n    Artificial          = 1 << 6\n    Explicit            = 1 << 7\n    Prototyped          = 1 << 8\n    ObjcClassComplete   = 1 << 9\n    ObjectPointer       = 1 << 10\n    Vector              = 1 << 11\n    StaticMember        = 1 << 12\n    LValueReference     = 1 << 13\n    RValueReference     = 1 << 14\n    ExternalTypeRef     = 1 << 15\n    SingleInheritance   = 1 << 16\n    MultipleInheritance = 2 << 16\n    VirtualInheritance  = 3 << 16\n    IntroducedVirtual   = 1 << 18\n    BitField            = 1 << 19\n    NoReturn            = 1 << 20\n\n    {% if LibLLVM::IS_LT_90 %}\n      MainSubprogram = 1 << 21\n    {% end %}\n\n    PassByValue         = 1 << 22\n    TypePassByReference = 1 << 23\n    EnumClass           = 1 << 24\n    Thunk               = 1 << 25\n\n    {% if LibLLVM::IS_LT_90 %}\n      Trivial = 1 << 26\n    {% else %}\n      NonTrivial = 1 << 26\n    {% end %}\n\n    BigEndian    = 1 << 27\n    LittleEndian = 1 << 28\n  end\n\n  enum InlineAsmDialect\n    ATT\n    Intel\n  end\n\n  struct Value\n    enum Kind\n      Argument\n      BasicBlock\n      MemoryUse\n      MemoryDef\n      MemoryPhi\n\n      Function\n      GlobalAlias\n      GlobalIFunc\n      GlobalVariable\n      BlockAddress\n      ConstantExpr\n      ConstantArray\n      ConstantStruct\n      ConstantVector\n\n      UndefValue\n      ConstantAggregateZero\n      ConstantDataArray\n      ConstantDataVector\n      ConstantInt\n      ConstantFP\n      ConstantPointerNull\n      ConstantTokenNone\n\n      MetadataAsValue\n      InlineAsm\n\n      Instruction\n    end\n  end\n\n  struct Metadata\n    enum Type : UInt32\n      Dbg                   =  0 # \"dbg\"\n      Tbaa                  =  1 # \"tbaa\"\n      Prof                  =  2 # \"prof\"\n      Fpmath                =  3 # \"fpmath\"\n      Range                 =  4 # \"range\"\n      TbaaStruct            =  5 # \"tbaa.struct\"\n      InvariantLoad         =  6 # \"invariant.load\"\n      AliasScope            =  7 # \"alias.scope\"\n      Noalias               =  8 # \"noalias\"\n      Nontemporal           =  9 # \"nontemporal\"\n      MemParallelLoopAccess = 10 # \"llvm.mem.parallel_loop_access\"\n      Nonnull               = 11 # \"nonnull\"\n      Dereferenceable       = 12 # \"dereferenceable\"\n      DereferenceableOrNull = 13 # \"dereferenceable_or_null\"\n      MakeImplicit          = 14 # \"make.implicit\"\n      Unpredictable         = 15 # \"unpredictable\"\n      InvariantGroup        = 16 # \"invariant.group\"\n      Align                 = 17 # \"align\"\n      Loop                  = 18 # \"llvm.loop\"\n      Type                  = 19 # \"type\"\n      SectionPrefix         = 20 # \"section_prefix\"\n      AbsoluteSymbol        = 21 # \"absolute_symbol\"\n      Associated            = 22 # \"associated\"\n      Callees               = 23 # \"callees\"\n      IrrLoop               = 24 # \"irr_loop\"\n      AccessGroup           = 25 # \"llvm.access.group\"\n      Callback              = 26 # \"callback\"\n      PreserveAccessIndex   = 27 # \"llvm.preserve.*.access.index\"\n    end\n  end\n\n  enum UWTableKind\n    None    = 0 # No unwind table requested\n    Sync    = 1 # \"Synchronous\" unwind tables\n    Async   = 2 # \"Asynchronous\" unwind tables (instr precise)\n    Default = 2\n  end\nend\n\nrequire \"./enums/*\"\n"
  },
  {
    "path": "src/llvm/ext/find-llvm-config.sh",
    "content": "#!/bin/sh\n\nif ! LLVM_CONFIG=$(command -v \"$LLVM_CONFIG\"); then\n  llvm_config_version=$(llvm-config --version 2>/dev/null)\n  # shellcheck disable=SC2013\n  for version in $(cat \"$(dirname \"$0\")/llvm-versions.txt\"); do\n    LLVM_CONFIG=$(\n    ([ \"${llvm_config_version#\"$version\"}\" != \"$llvm_config_version\" ] && command -v llvm-config) || \\\n    command -v llvm-config-\"${version%.*}\" || \\\n    command -v llvm-config-\"$version\" || \\\n    command -v \"llvm-config${version%.*}${version#*.}\" || \\\n    command -v llvm-config\"${version%.*}\" || \\\n    command -v llvm-config\"$version\" || \\\n    command -v llvm\"${version%.*}\"-config)\n    [ \"$LLVM_CONFIG\" ] && break\n  done\nfi\n\nif [ \"$LLVM_CONFIG\" ]; then\n  case \"$(uname -s)\" in\n    MINGW32_NT*|MINGW64_NT*)\n      printf \"%s\" \"$(cygpath -w \"$LLVM_CONFIG\")\"\n      ;;\n    *)\n      printf \"%s\" \"$LLVM_CONFIG\"\n      ;;\n  esac\nelse\n  printf \"Error: Could not find location of llvm-config. Please specify path in environment variable LLVM_CONFIG.\\n\" >&2\n  printf \"Supported LLVM versions: %s\\n\" \"$(sed 's/\\.0//g' \"$(dirname \"$0\")/llvm-versions.txt\")\" >&2\n  exit 1\nfi\n"
  },
  {
    "path": "src/llvm/ext/llvm-versions.txt",
    "content": "23.0 22.1 21.1 20.1 19.1 18.1 17.0 16.0 15.0 14.0 13.0 12.0 11.1 11.0 10.0 9.0 8.0\n"
  },
  {
    "path": "src/llvm/ext/llvm_ext.cc",
    "content": "#include <llvm/Config/llvm-config.h>\n#include <llvm/IR/IRBuilder.h>\n#include <llvm/Target/TargetMachine.h>\n#include <llvm-c/TargetMachine.h>\n\nusing namespace llvm;\n\n#define LLVM_VERSION_GE(major, minor) \\\n  (LLVM_VERSION_MAJOR > (major) || LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor))\n\n#if !LLVM_VERSION_GE(9, 0)\n#include <llvm/IR/DIBuilder.h>\n#endif\n\n#if LLVM_VERSION_GE(16, 0)\n#define makeArrayRef ArrayRef\n#endif\n\n#if !LLVM_VERSION_GE(18, 0)\ntypedef struct LLVMOpaqueOperandBundle *LLVMOperandBundleRef;\nDEFINE_SIMPLE_CONVERSION_FUNCTIONS(OperandBundleDef, LLVMOperandBundleRef)\n#endif\n\nextern \"C\" {\n\n#if !LLVM_VERSION_GE(9, 0)\nLLVMMetadataRef LLVMExtDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder,\n                                                 const char *Name, size_t NameLen,\n                                                 int64_t Value,\n                                                 LLVMBool IsUnsigned) {\n  return wrap(unwrap(Builder)->createEnumerator({Name, NameLen}, Value,\n                                                IsUnsigned != 0));\n}\n\nvoid LLVMExtClearCurrentDebugLocation(LLVMBuilderRef B) {\n  unwrap(B)->SetCurrentDebugLocation(DebugLoc::get(0, 0, nullptr));\n}\n#endif\n\n#if !LLVM_VERSION_GE(18, 0)\nLLVMOperandBundleRef LLVMExtCreateOperandBundle(const char *Tag, size_t TagLen,\n                                                LLVMValueRef *Args,\n                                                unsigned NumArgs) {\n  return wrap(new OperandBundleDef(std::string(Tag, TagLen),\n                                   makeArrayRef(unwrap(Args), NumArgs)));\n}\n\nvoid LLVMExtDisposeOperandBundle(LLVMOperandBundleRef Bundle) {\n  delete unwrap(Bundle);\n}\n\nLLVMValueRef\nLLVMExtBuildCallWithOperandBundles(LLVMBuilderRef B, LLVMTypeRef Ty,\n                                   LLVMValueRef Fn, LLVMValueRef *Args,\n                                   unsigned NumArgs, LLVMOperandBundleRef *Bundles,\n                                   unsigned NumBundles, const char *Name) {\n  FunctionType *FTy = unwrap<FunctionType>(Ty);\n  SmallVector<OperandBundleDef, 8> OBs;\n  for (auto *Bundle : makeArrayRef(Bundles, NumBundles)) {\n    OperandBundleDef *OB = unwrap(Bundle);\n    OBs.push_back(*OB);\n  }\n  return wrap(unwrap(B)->CreateCall(\n      FTy, unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), OBs, Name));\n}\n\nLLVMValueRef LLVMExtBuildInvokeWithOperandBundles(\n    LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args,\n    unsigned NumArgs, LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,\n    LLVMOperandBundleRef *Bundles, unsigned NumBundles, const char *Name) {\n  SmallVector<OperandBundleDef, 8> OBs;\n  for (auto *Bundle : makeArrayRef(Bundles, NumBundles)) {\n    OperandBundleDef *OB = unwrap(Bundle);\n    OBs.push_back(*OB);\n  }\n  return wrap(unwrap(B)->CreateInvoke(\n      unwrap<FunctionType>(Ty), unwrap(Fn), unwrap(Then), unwrap(Catch),\n      makeArrayRef(unwrap(Args), NumArgs), OBs, Name));\n}\n#endif\n\n#if !LLVM_VERSION_GE(18, 0)\nstatic TargetMachine *unwrap(LLVMTargetMachineRef P) {\n  return reinterpret_cast<TargetMachine *>(P);\n}\n\nvoid LLVMExtSetTargetMachineGlobalISel(LLVMTargetMachineRef T, LLVMBool Enable) {\n  unwrap(T)->setGlobalISel(Enable);\n}\n#endif\n\n} // extern \"C\"\n"
  },
  {
    "path": "src/llvm/function.cr",
    "content": "require \"./value_methods\"\n\nstruct LLVM::Function\n  include LLVM::ValueMethods\n\n  def self.from_value(value : LLVM::ValueMethods)\n    new(value.to_unsafe)\n  end\n\n  def basic_blocks\n    BasicBlockCollection.new self\n  end\n\n  def call_convention\n    LLVM::CallConvention.new LibLLVM.get_function_call_convention(self)\n  end\n\n  def call_convention=(cc)\n    LibLLVM.set_function_call_convention(self, cc)\n  end\n\n  def add_attribute(attribute : Attribute, index = AttributeIndex::FunctionIndex, type : Type? = nil)\n    return if attribute.value == 0\n\n    context = LibLLVM.get_module_context(LibLLVM.get_global_parent(self))\n    attribute.each_kind do |kind|\n      LibLLVM.add_attribute_at_index(self, index, attribute_ref(context, kind, type))\n    end\n  end\n\n  def add_attribute(attribute : String, index = AttributeIndex::FunctionIndex, *, value : String)\n    context = LibLLVM.get_module_context(LibLLVM.get_global_parent(self))\n    attribute_ref = LibLLVM.create_string_attribute(context, attribute, attribute.bytesize,\n      value, value.bytesize)\n    LibLLVM.add_attribute_at_index(self, index, attribute_ref)\n  end\n\n  def add_attribute(attribute : Attribute, index = AttributeIndex::FunctionIndex, *, value)\n    return if attribute.value == 0\n\n    context = LibLLVM.get_module_context(LibLLVM.get_global_parent(self))\n    attribute.each_kind do |kind|\n      attribute_ref = LibLLVM.create_enum_attribute(context, kind, value.to_u64)\n      LibLLVM.add_attribute_at_index(self, index, attribute_ref)\n    end\n  end\n\n  def add_target_dependent_attribute(name, value)\n    LibLLVM.add_target_dependent_function_attr self, name, value\n  end\n\n  def attributes(index = AttributeIndex::FunctionIndex)\n    attrs = Attribute::None\n    0.upto(LibLLVM.get_last_enum_attribute_kind) do |kind|\n      if LibLLVM.get_enum_attribute_at_index(self, index, kind)\n        attrs |= Attribute.from_kind(kind)\n      end\n    end\n    attrs\n  end\n\n  @[Deprecated]\n  def function_type\n    Type.new LibLLVM.get_element_type(LibLLVM.type_of(self))\n  end\n\n  @[Deprecated]\n  def return_type\n    Type.new(LibLLVM.get_element_type(LibLLVM.type_of(self))).return_type\n  end\n\n  @[Deprecated]\n  def varargs?\n    Type.new(LibLLVM.get_element_type(LibLLVM.type_of(self))).varargs?\n  end\n\n  def params\n    ParameterCollection.new self\n  end\n\n  def personality_function=(fn)\n    LibLLVM.set_personality_fn(self, fn)\n  end\n\n  def delete\n    LibLLVM.delete_function(self)\n  end\n\n  def naked?\n    attributes.naked?\n  end\nend\n"
  },
  {
    "path": "src/llvm/function_collection.cr",
    "content": "struct LLVM::FunctionCollection\n  def initialize(@mod : Module)\n  end\n\n  def add(name, arg_types : Array(LLVM::Type), ret_type, varargs = false)\n    # check_types_context(name, arg_types, ret_type)\n    add(name, LLVM::Type.function(arg_types, ret_type, varargs))\n  end\n\n  def add(name, arg_types : Array(LLVM::Type), ret_type, varargs = false, &)\n    func = add(name, arg_types, ret_type, varargs)\n    yield func\n    func\n  end\n\n  def add(name, fun_type : LLVM::Type)\n    func = LibLLVM.add_function(@mod, name, fun_type)\n    Function.new(func)\n  end\n\n  def add(name, fun_type : LLVM::Type, &)\n    func = add(name, fun_type)\n    yield func\n    func\n  end\n\n  def [](name)\n    func = self[name]?\n    func || raise \"Undefined llvm function: #{name}\"\n  end\n\n  def []?(name)\n    func =\n      {% if LibLLVM::IS_LT_200 %}\n        LibLLVM.get_named_function(@mod, name)\n      {% else %}\n        LibLLVM.get_named_function_with_length(@mod, name, name.bytesize)\n      {% end %}\n\n    func ? Function.new(func) : nil\n  end\n\n  def each(&) : Nil\n    f = LibLLVM.get_first_function(@mod)\n    while f\n      yield LLVM::Function.new f\n      f = LibLLVM.get_next_function(f)\n    end\n  end\n\n  # The next lines are for ease debugging when a types/values\n  # are incorrectly used across contexts.\n\n  # private def check_types_context(name, arg_types, ret_type)\n  #   ctx = @mod.context\n\n  #   arg_types.each_with_index do |arg_type, index|\n  #     if arg_type.context != ctx\n  #       Context.wrong(ctx, arg_type.context, \"wrong context for function #{name} in #{@mod.name}, index #{index}, type #{arg_type}\")\n  #     end\n  #   end\n\n  #   if ret_type.context != ctx\n  #     Context.wrong(ctx, ret_type.context, \"wrong context for function #{name} in #{@mod.name}, return type #{ret_type}\")\n  #   end\n  # end\nend\n"
  },
  {
    "path": "src/llvm/function_pass_manager.cr",
    "content": "{% unless LibLLVM::IS_LT_170 %}\n  @[Deprecated(\"The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead\")]\n{% end %}\nclass LLVM::FunctionPassManager\n  def initialize(@unwrap : LibLLVM::PassManagerRef)\n  end\n\n  def run(mod : Module)\n    changed = false\n    run do |runner|\n      mod.functions.each do |func|\n        changed ||= runner.run(func)\n      end\n    end\n    changed\n  end\n\n  def run(&)\n    LibLLVM.initialize_function_pass_manager(self)\n\n    runner = Runner.new(self)\n    yield runner\n\n    LibLLVM.finalize_function_pass_manager(self)\n\n    self\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def finalize\n    LibLLVM.dispose_pass_manager(@unwrap)\n  end\n\n  {% unless LibLLVM::IS_LT_170 %}\n    @[Deprecated(\"The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead\")]\n  {% end %}\n  struct Runner\n    @fpm : FunctionPassManager\n\n    def initialize(@fpm)\n    end\n\n    def run(f : LLVM::Function)\n      LibLLVM.run_function_pass_manager(@fpm, f) != 0\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/generic_value.cr",
    "content": "class LLVM::GenericValue\n  def initialize(@unwrap : LibLLVM::GenericValueRef, @context : LLVM::Context)\n  end\n\n  def to_i : Int32\n    to_i64.to_i32!\n  end\n\n  def to_i64 : Int64\n    LibLLVM.generic_value_to_int(self, is_signed: 1).to_i64!\n  end\n\n  def to_u64 : UInt64\n    LibLLVM.generic_value_to_int(self, is_signed: 0)\n  end\n\n  def to_b : Bool\n    to_i != 0\n  end\n\n  def to_f32 : Float32\n    LibLLVM.generic_value_to_float(@context.float, self).to_f32\n  end\n\n  def to_f64 : Float64\n    LibLLVM.generic_value_to_float(@context.double, self)\n  end\n\n  def to_string : String\n    to_pointer.as(String)\n  end\n\n  def to_pointer : Void*\n    LibLLVM.generic_value_to_pointer(self)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def finalize\n    LibLLVM.dispose_generic_value(@unwrap)\n  end\nend\n"
  },
  {
    "path": "src/llvm/global_collection.cr",
    "content": "struct LLVM::GlobalCollection\n  def initialize(@mod : Module)\n  end\n\n  def add(type, name)\n    # check_type_context(type, name)\n\n    Value.new LibLLVM.add_global(@mod, type, name)\n  end\n\n  def []?(name)\n    global =\n      {% if LibLLVM::IS_LT_200 %}\n        LibLLVM.get_named_global(@mod, name)\n      {% else %}\n        LibLLVM.get_named_global_with_length(@mod, name, name.bytesize)\n      {% end %}\n\n    global ? Value.new(global) : nil\n  end\n\n  def [](name)\n    global = self[name]?\n    if global\n      global\n    else\n      raise \"Global not found: #{name}\"\n    end\n  end\n\n  # The next lines are for ease debugging when a types/values\n  # are incorrectly used across contexts.\n\n  # private def check_type_context(type, name)\n  #   if @mod.context != type.context\n  #     Context.wrong(@mod.context, type.context, \"wrong context for global #{name} in #{@mod.name}, type #{type}\")\n  #   end\n  # end\nend\n"
  },
  {
    "path": "src/llvm/instruction_collection.cr",
    "content": "struct LLVM::InstructionCollection\n  include Enumerable(LLVM::Value)\n\n  def initialize(@basic_block : BasicBlock)\n  end\n\n  def empty?\n    first?.nil?\n  end\n\n  def each(&) : Nil\n    inst = LibLLVM.get_first_instruction @basic_block\n\n    while inst\n      yield LLVM::Value.new inst\n      inst = LibLLVM.get_next_instruction(inst)\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/jit_compiler.cr",
    "content": "class LLVM::JITCompiler\n  def initialize(mod)\n    # JIT compilers own an LLVM::Module, and when they are disposed the module is disposed,\n    # so we must prevent the module from being dispose when the GC will want to free it.\n    mod.take_ownership { raise \"Can't create two JIT compilers for the same module\" }\n\n    # if LibLLVM.create_jit_compiler_for_module(out @unwrap, mod, 3, out error) != 0\n    if LibLLVM.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, out error) != 0\n      raise LLVM.string_and_dispose(error)\n    end\n\n    # FIXME: We need to disable global isel until https://reviews.llvm.org/D80898 is released,\n    # or we fixed generating values for 0 sized types.\n    # When removing this, also remove it from the ABI specs and Crystal::Codegen::Target.\n    # See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270\n    # for background info\n    target_machine = LibLLVM.get_execution_engine_target_machine(@unwrap)\n    {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.set_target_machine_global_isel(target_machine, 0)\n\n    @finalized = false\n  end\n\n  def self.new(mod, &)\n    jit = new(mod)\n    yield jit ensure jit.dispose\n  end\n\n  def run_function(func, context : Context)\n    ret = LibLLVM.run_function(self, func, 0, nil)\n    GenericValue.new(ret, context)\n  end\n\n  def run_function(func, args : Array(LLVM::GenericValue), context : Context)\n    ret = LibLLVM.run_function(self, func, args.size, (args.to_unsafe.as(LibLLVM::GenericValueRef*)))\n    GenericValue.new(ret, context)\n  end\n\n  def get_pointer_to_global(value)\n    LibLLVM.get_pointer_to_global(self, value)\n  end\n\n  def function_address(name : String) : Void*\n    Pointer(Void).new(LibLLVM.get_function_address(self, name.check_no_null_byte))\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def dispose\n    return if @finalized\n    @finalized = true\n    finalize\n  end\n\n  def finalize\n    return if @finalized\n    LibLLVM.dispose_execution_engine(@unwrap)\n  end\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/analysis.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  fun verify_module = LLVMVerifyModule(m : ModuleRef, action : LLVM::VerifierFailureAction, out_message : Char**) : Bool\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/bit_reader.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  fun parse_bitcode_in_context2 = LLVMParseBitcodeInContext2(c : ContextRef, mb : MemoryBufferRef, m : ModuleRef*) : Int\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/bit_writer.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  fun write_bitcode_to_file = LLVMWriteBitcodeToFile(m : ModuleRef, path : Char*) : Int\n  fun write_bitcode_to_fd = LLVMWriteBitcodeToFD(m : ModuleRef, fd : Int, should_close : Int, unbuffered : Int) : Int\n  fun write_bitcode_to_memory_buffer = LLVMWriteBitcodeToMemoryBuffer(mod : ModuleRef) : MemoryBufferRef\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/config.cr",
    "content": "# The list of supported targets are hardcoded in:\n# https://github.com/llvm/llvm-project/blob/main/llvm/CMakeLists.txt\n\nlib LibLLVM\n  ALL_TARGETS = [\n    # default targets (as of LLVM 21)\n    \"AArch64\",\n    \"AMDGPU\",\n    \"ARM\",\n    \"AVR\",\n    \"BPF\",\n    \"Hexagon\",\n    \"Lanai\",\n    \"LoongArch\",\n    \"MSP430\",\n    \"Mips\",\n    \"NVPTX\",\n    \"PowerPC\",\n    \"RISCV\",\n    \"SPIRV\",\n    \"Sparc\",\n    \"SystemZ\",\n    \"VE\",\n    \"WebAssembly\",\n    \"X86\",\n    \"XCore\",\n\n    # experimental targets (as of LLVM 21)\n    \"ARC\",\n    \"CSKY\",\n    \"DirectX\",\n    \"M68k\",\n    \"Xtensa\",\n  ]\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/core.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  # NOTE: the following C enums usually have different values from their C++\n  # counterparts (e.g. `LLVMModuleFlagBehavior` v.s. `LLVM::Module::ModFlagBehavior`)\n\n  enum ModuleFlagBehavior\n    Error        = 0\n    Warning      = 1\n    Require      = 2\n    Override     = 3\n    Append       = 4\n    AppendUnique = 5\n  end\n\n  alias AttributeIndex = UInt\n\n  fun dispose_message = LLVMDisposeMessage(message : Char*)\n\n  {% unless LibLLVM::IS_LT_160 %}\n    fun get_version = LLVMGetVersion(major : UInt*, minor : UInt*, patch : UInt*) : Void\n  {% end %}\n\n  fun create_context = LLVMContextCreate : ContextRef\n  fun dispose_context = LLVMContextDispose(c : ContextRef)\n\n  fun get_md_kind_id_in_context = LLVMGetMDKindIDInContext(c : ContextRef, name : Char*, s_len : UInt) : UInt\n\n  fun get_enum_attribute_kind_for_name = LLVMGetEnumAttributeKindForName(name : Char*, s_len : SizeT) : UInt\n  fun get_last_enum_attribute_kind = LLVMGetLastEnumAttributeKind : UInt\n  fun create_enum_attribute = LLVMCreateEnumAttribute(c : ContextRef, kind_id : UInt, val : UInt64) : AttributeRef\n  fun create_string_attribute = LLVMCreateStringAttribute(c : ContextRef, k : Char*, k_length : UInt, v : Char*, v_length : UInt) : AttributeRef\n  {% unless LibLLVM::IS_LT_120 %}\n    fun create_type_attribute = LLVMCreateTypeAttribute(c : ContextRef, kind_id : UInt, type_ref : TypeRef) : AttributeRef\n  {% end %}\n\n  fun module_create_with_name_in_context = LLVMModuleCreateWithNameInContext(module_id : Char*, c : ContextRef) : ModuleRef\n  fun get_module_identifier = LLVMGetModuleIdentifier(m : ModuleRef, len : SizeT*) : Char*\n  fun set_module_identifier = LLVMSetModuleIdentifier(m : ModuleRef, ident : Char*, len : SizeT)\n  fun set_target = LLVMSetTarget(m : ModuleRef, triple : Char*)\n  fun add_module_flag = LLVMAddModuleFlag(m : ModuleRef, behavior : ModuleFlagBehavior, key : Char*, key_len : SizeT, val : MetadataRef)\n  fun dump_module = LLVMDumpModule(m : ModuleRef)\n  fun print_module_to_file = LLVMPrintModuleToFile(m : ModuleRef, filename : Char*, error_message : Char**) : Bool\n  fun print_module_to_string = LLVMPrintModuleToString(m : ModuleRef) : Char*\n  {% if !LibLLVM::IS_LT_130 %}\n    fun get_inline_asm = LLVMGetInlineAsm(ty : TypeRef, asm_string : Char*, asm_string_size : SizeT, constraints : Char*, constraints_size : SizeT, has_side_effects : Bool, is_align_stack : Bool, dialect : LLVM::InlineAsmDialect, can_throw : Bool) : ValueRef\n  {% else %}\n    fun get_inline_asm = LLVMGetInlineAsm(t : TypeRef, asm_string : Char*, asm_string_size : SizeT, constraints : Char*, constraints_size : SizeT, has_side_effects : Bool, is_align_stack : Bool, dialect : LLVM::InlineAsmDialect) : ValueRef\n  {% end %}\n  fun get_module_context = LLVMGetModuleContext(m : ModuleRef) : ContextRef\n\n  fun add_function = LLVMAddFunction(m : ModuleRef, name : Char*, function_ty : TypeRef) : ValueRef\n  {% if LibLLVM::IS_LT_200 %}\n    fun get_named_function = LLVMGetNamedFunction(m : ModuleRef, name : Char*) : ValueRef\n  {% else %}\n    fun get_named_function_with_length = LLVMGetNamedFunctionWithLength(m : ModuleRef, name : Char*, length : SizeT) : ValueRef\n  {% end %}\n  fun get_first_function = LLVMGetFirstFunction(m : ModuleRef) : ValueRef\n  fun get_next_function = LLVMGetNextFunction(fn : ValueRef) : ValueRef\n\n  fun get_type_kind = LLVMGetTypeKind(ty : TypeRef) : LLVM::Type::Kind\n  fun get_type_context = LLVMGetTypeContext(ty : TypeRef) : ContextRef\n  fun print_type_to_string = LLVMPrintTypeToString(ty : TypeRef) : Char*\n\n  fun int1_type_in_context = LLVMInt1TypeInContext(c : ContextRef) : TypeRef\n  fun int8_type_in_context = LLVMInt8TypeInContext(c : ContextRef) : TypeRef\n  fun int16_type_in_context = LLVMInt16TypeInContext(c : ContextRef) : TypeRef\n  fun int32_type_in_context = LLVMInt32TypeInContext(c : ContextRef) : TypeRef\n  fun int64_type_in_context = LLVMInt64TypeInContext(c : ContextRef) : TypeRef\n  fun int128_type_in_context = LLVMInt128TypeInContext(c : ContextRef) : TypeRef\n  fun int_type_in_context = LLVMIntTypeInContext(c : ContextRef, num_bits : UInt) : TypeRef\n  fun get_int_type_width = LLVMGetIntTypeWidth(integer_ty : TypeRef) : UInt\n\n  fun half_type_in_context = LLVMHalfTypeInContext(c : ContextRef) : TypeRef\n  fun float_type_in_context = LLVMFloatTypeInContext(c : ContextRef) : TypeRef\n  fun double_type_in_context = LLVMDoubleTypeInContext(c : ContextRef) : TypeRef\n  fun x86_fp80_type_in_context = LLVMX86FP80TypeInContext(c : ContextRef) : TypeRef\n  fun fp128_type_in_context = LLVMFP128TypeInContext(c : ContextRef) : TypeRef\n  fun ppc_fp128_type_in_context = LLVMPPCFP128TypeInContext(c : ContextRef) : TypeRef\n\n  fun function_type = LLVMFunctionType(return_type : TypeRef, param_types : TypeRef*, param_count : UInt, is_var_arg : Bool) : TypeRef\n  fun is_function_var_arg = LLVMIsFunctionVarArg(function_ty : TypeRef) : Bool\n  fun get_return_type = LLVMGetReturnType(function_ty : TypeRef) : TypeRef\n  fun count_param_types = LLVMCountParamTypes(function_ty : TypeRef) : UInt\n  fun get_param_types = LLVMGetParamTypes(function_ty : TypeRef, dest : TypeRef*)\n\n  fun struct_type_in_context = LLVMStructTypeInContext(c : ContextRef, element_types : TypeRef*, element_count : UInt, packed : Bool) : TypeRef\n  fun struct_create_named = LLVMStructCreateNamed(c : ContextRef, name : Char*) : TypeRef\n  fun get_struct_name = LLVMGetStructName(ty : TypeRef) : Char*\n  fun struct_set_body = LLVMStructSetBody(struct_ty : TypeRef, element_types : TypeRef*, element_count : UInt, packed : Bool)\n  fun count_struct_element_types = LLVMCountStructElementTypes(struct_ty : TypeRef) : UInt\n  fun get_struct_element_types = LLVMGetStructElementTypes(struct_ty : TypeRef, dest : TypeRef*)\n  fun is_packed_struct = LLVMIsPackedStruct(struct_ty : TypeRef) : Bool\n\n  fun get_element_type = LLVMGetElementType(ty : TypeRef) : TypeRef\n  fun array_type = LLVMArrayType(element_type : TypeRef, element_count : UInt) : TypeRef\n  fun get_array_length = LLVMGetArrayLength(array_ty : TypeRef) : UInt\n  fun pointer_type = LLVMPointerType(element_type : TypeRef, address_space : UInt) : TypeRef\n  {% unless LibLLVM::IS_LT_150 %}\n    fun pointer_type_in_context = LLVMPointerTypeInContext(c : ContextRef, address_space : UInt) : TypeRef\n  {% end %}\n  fun vector_type = LLVMVectorType(element_type : TypeRef, element_count : UInt) : TypeRef\n  fun get_vector_size = LLVMGetVectorSize(vector_ty : TypeRef) : UInt\n\n  fun void_type_in_context = LLVMVoidTypeInContext(c : ContextRef) : TypeRef\n\n  fun type_of = LLVMTypeOf(val : ValueRef) : TypeRef\n  fun get_value_kind = LLVMGetValueKind(val : ValueRef) : LLVM::Value::Kind\n  fun get_value_name2 = LLVMGetValueName2(val : ValueRef, length : SizeT*) : Char*\n  fun set_value_name2 = LLVMSetValueName2(val : ValueRef, name : Char*, name_len : SizeT)\n  fun dump_value = LLVMDumpValue(val : ValueRef)\n  fun print_value_to_string = LLVMPrintValueToString(val : ValueRef) : Char*\n  fun is_constant = LLVMIsConstant(val : ValueRef) : Bool\n  fun get_value_name = LLVMGetValueName(val : ValueRef) : Char*\n  fun set_value_name = LLVMSetValueName(val : ValueRef, name : Char*)\n\n  fun get_operand = LLVMGetOperand(val : ValueRef, index : UInt) : ValueRef\n  fun get_num_operands = LLVMGetNumOperands(val : ValueRef) : Int\n\n  fun const_null = LLVMConstNull(ty : TypeRef) : ValueRef\n  fun get_undef = LLVMGetUndef(ty : TypeRef) : ValueRef\n  fun const_pointer_null = LLVMConstPointerNull(ty : TypeRef) : ValueRef\n\n  fun const_int = LLVMConstInt(int_ty : TypeRef, n : ULongLong, sign_extend : Bool) : ValueRef\n  fun const_int_of_arbitrary_precision = LLVMConstIntOfArbitraryPrecision(int_ty : TypeRef, num_words : UInt, words : UInt64*) : ValueRef\n  fun const_real = LLVMConstReal(real_ty : TypeRef, n : Double) : ValueRef\n  fun const_real_of_string = LLVMConstRealOfString(real_ty : TypeRef, text : Char*) : ValueRef\n  fun const_real_of_string_and_size = LLVMConstRealOfStringAndSize(real_ty : TypeRef, text : Char*, s_len : UInt) : ValueRef\n  fun const_int_get_zext_value = LLVMConstIntGetZExtValue(constant_val : ValueRef) : ULongLong\n  fun const_int_get_sext_value = LLVMConstIntGetSExtValue(constant_val : ValueRef) : LongLong\n\n  {% if LibLLVM::IS_LT_190 %}\n    fun const_string_in_context = LLVMConstStringInContext(c : ContextRef, str : Char*, length : UInt, dont_null_terminate : Bool) : ValueRef\n  {% else %}\n    fun const_string_in_context2 = LLVMConstStringInContext2(c : ContextRef, str : Char*, length : SizeT, dont_null_terminate : Bool) : ValueRef\n  {% end %}\n  fun const_struct_in_context = LLVMConstStructInContext(c : ContextRef, constant_vals : ValueRef*, count : UInt, packed : Bool) : ValueRef\n  fun const_array = LLVMConstArray(element_ty : TypeRef, constant_vals : ValueRef*, length : UInt) : ValueRef\n  {% unless LibLLVM::IS_LT_210 %}\n    fun const_data_array = LLVMConstDataArray(element_ty : TypeRef, data : Char*, size_in_bytes : SizeT) : ValueRef\n  {% end %}\n\n  fun align_of = LLVMAlignOf(ty : TypeRef) : ValueRef\n  fun size_of = LLVMSizeOf(ty : TypeRef) : ValueRef\n\n  fun get_global_parent = LLVMGetGlobalParent(global : ValueRef) : ModuleRef\n  fun get_linkage = LLVMGetLinkage(global : ValueRef) : LLVM::Linkage\n  fun set_linkage = LLVMSetLinkage(global : ValueRef, linkage : LLVM::Linkage)\n  fun set_dll_storage_class = LLVMSetDLLStorageClass(global : ValueRef, class : LLVM::DLLStorageClass)\n\n  fun set_alignment = LLVMSetAlignment(v : ValueRef, bytes : UInt)\n\n  fun add_global = LLVMAddGlobal(m : ModuleRef, ty : TypeRef, name : Char*) : ValueRef\n  {% if LibLLVM::IS_LT_200 %}\n    fun get_named_global = LLVMGetNamedGlobal(m : ModuleRef, name : Char*) : ValueRef\n  {% else %}\n    fun get_named_global_with_length = LLVMGetNamedGlobalWithLength(m : ModuleRef, name : Char*, length : SizeT) : ValueRef\n  {% end %}\n  fun get_initializer = LLVMGetInitializer(global_var : ValueRef) : ValueRef\n  fun set_initializer = LLVMSetInitializer(global_var : ValueRef, constant_val : ValueRef)\n  fun is_thread_local = LLVMIsThreadLocal(global_var : ValueRef) : Bool\n  fun set_thread_local = LLVMSetThreadLocal(global_var : ValueRef, is_thread_local : Bool)\n  fun is_global_constant = LLVMIsGlobalConstant(global_var : ValueRef) : Bool\n  fun set_global_constant = LLVMSetGlobalConstant(global_var : ValueRef, is_constant : Bool)\n\n  fun delete_function = LLVMDeleteFunction(fn : ValueRef)\n  fun set_personality_fn = LLVMSetPersonalityFn(fn : ValueRef, personality_fn : ValueRef)\n  fun get_function_call_convention = LLVMGetFunctionCallConv(fn : ValueRef) : LLVM::CallConvention\n  fun set_function_call_convention = LLVMSetFunctionCallConv(fn : ValueRef, cc : LLVM::CallConvention)\n  fun add_attribute_at_index = LLVMAddAttributeAtIndex(f : ValueRef, idx : AttributeIndex, a : AttributeRef)\n  fun get_enum_attribute_at_index = LLVMGetEnumAttributeAtIndex(f : ValueRef, idx : AttributeIndex, kind_id : UInt) : AttributeRef\n  fun add_target_dependent_function_attr = LLVMAddTargetDependentFunctionAttr(fn : ValueRef, a : Char*, v : Char*)\n\n  fun get_count_params = LLVMCountParams(fn : ValueRef) : UInt\n  fun get_params = LLVMGetParams(fn : ValueRef, params : ValueRef*)\n  fun get_param = LLVMGetParam(fn : ValueRef, index : UInt) : ValueRef\n  fun set_param_alignment = LLVMSetParamAlignment(arg : ValueRef, align : UInt)\n\n  fun md_string_in_context2 = LLVMMDStringInContext2(c : ContextRef, str : Char*, s_len : SizeT) : ValueRef\n  fun md_node_in_context2 = LLVMMDNodeInContext2(c : ContextRef, mds : ValueRef*, count : SizeT) : ValueRef\n  fun metadata_as_value = LLVMMetadataAsValue(c : ContextRef, md : MetadataRef) : ValueRef\n  fun value_as_metadata = LLVMValueAsMetadata(val : ValueRef) : MetadataRef\n  fun get_md_node_num_operands = LLVMGetMDNodeNumOperands(v : ValueRef) : UInt\n  fun get_md_node_operands = LLVMGetMDNodeOperands(v : ValueRef, dest : ValueRef*)\n  fun md_string_in_context = LLVMMDStringInContext(c : ContextRef, str : Char*, s_len : UInt) : ValueRef\n  fun md_node_in_context = LLVMMDNodeInContext(c : ContextRef, vals : ValueRef*, count : UInt) : ValueRef\n\n  {% unless LibLLVM::IS_LT_180 %}\n    fun create_operand_bundle = LLVMCreateOperandBundle(tag : Char*, tag_len : SizeT, args : ValueRef*, num_args : UInt) : OperandBundleRef\n    fun dispose_operand_bundle = LLVMDisposeOperandBundle(bundle : OperandBundleRef)\n  {% end %}\n\n  fun get_basic_block_name = LLVMGetBasicBlockName(bb : BasicBlockRef) : Char*\n  fun get_basic_block_parent = LLVMGetBasicBlockParent(bb : BasicBlockRef) : ValueRef\n  fun get_first_basic_block = LLVMGetFirstBasicBlock(fn : ValueRef) : BasicBlockRef\n  fun get_next_basic_block = LLVMGetNextBasicBlock(bb : BasicBlockRef) : BasicBlockRef\n  fun append_basic_block_in_context = LLVMAppendBasicBlockInContext(c : ContextRef, fn : ValueRef, name : Char*) : BasicBlockRef\n  fun delete_basic_block = LLVMDeleteBasicBlock(bb : BasicBlockRef)\n  fun get_first_instruction = LLVMGetFirstInstruction(bb : BasicBlockRef) : ValueRef\n\n  fun set_metadata = LLVMSetMetadata(val : ValueRef, kind_id : UInt, node : ValueRef)\n  fun get_next_instruction = LLVMGetNextInstruction(inst : ValueRef) : ValueRef\n\n  fun get_num_arg_operands = LLVMGetNumArgOperands(instr : ValueRef) : UInt\n  fun set_instruction_call_convention = LLVMSetInstructionCallConv(instr : ValueRef, cc : LLVM::CallConvention)\n  fun get_instruction_call_convention = LLVMGetInstructionCallConv(instr : ValueRef) : LLVM::CallConvention\n  fun set_instr_param_alignment = LLVMSetInstrParamAlignment(instr : ValueRef, idx : AttributeIndex, align : UInt)\n  fun add_call_site_attribute = LLVMAddCallSiteAttribute(c : ValueRef, idx : AttributeIndex, a : AttributeRef)\n\n  fun add_incoming = LLVMAddIncoming(phi_node : ValueRef, incoming_values : ValueRef*, incoming_blocks : BasicBlockRef*, count : UInt)\n\n  fun create_builder_in_context = LLVMCreateBuilderInContext(c : ContextRef) : BuilderRef\n  fun position_builder_at_end = LLVMPositionBuilderAtEnd(builder : BuilderRef, block : BasicBlockRef)\n  fun get_insert_block = LLVMGetInsertBlock(builder : BuilderRef) : BasicBlockRef\n  fun dispose_builder = LLVMDisposeBuilder(builder : BuilderRef)\n\n  {% if LibLLVM::IS_LT_90 %}\n    fun set_current_debug_location = LLVMSetCurrentDebugLocation(builder : BuilderRef, l : ValueRef)\n  {% else %}\n    fun get_current_debug_location2 = LLVMGetCurrentDebugLocation2(builder : BuilderRef) : MetadataRef\n    fun set_current_debug_location2 = LLVMSetCurrentDebugLocation2(builder : BuilderRef, loc : MetadataRef)\n  {% end %}\n  fun get_current_debug_location = LLVMGetCurrentDebugLocation(builder : BuilderRef) : ValueRef\n\n  fun build_ret_void = LLVMBuildRetVoid(BuilderRef) : ValueRef\n  fun build_ret = LLVMBuildRet(BuilderRef, v : ValueRef) : ValueRef\n  fun build_br = LLVMBuildBr(BuilderRef, dest : BasicBlockRef) : ValueRef\n  fun build_cond = LLVMBuildCondBr(BuilderRef, if : ValueRef, then : BasicBlockRef, else : BasicBlockRef) : ValueRef\n  fun build_switch = LLVMBuildSwitch(BuilderRef, v : ValueRef, else : BasicBlockRef, num_cases : UInt) : ValueRef\n  fun build_invoke2 = LLVMBuildInvoke2(BuilderRef, ty : TypeRef, fn : ValueRef, args : ValueRef*, num_args : UInt, then : BasicBlockRef, catch : BasicBlockRef, name : Char*) : ValueRef\n  {% unless LibLLVM::IS_LT_180 %}\n    fun build_invoke_with_operand_bundles = LLVMBuildInvokeWithOperandBundles(BuilderRef, ty : TypeRef, fn : ValueRef, args : ValueRef*, num_args : UInt, then : BasicBlockRef, catch : BasicBlockRef, bundles : OperandBundleRef*, num_bundles : UInt, name : Char*) : ValueRef\n  {% end %}\n  fun build_unreachable = LLVMBuildUnreachable(BuilderRef) : ValueRef\n\n  fun build_landing_pad = LLVMBuildLandingPad(b : BuilderRef, ty : TypeRef, pers_fn : ValueRef, num_clauses : UInt, name : Char*) : ValueRef\n  fun build_catch_ret = LLVMBuildCatchRet(b : BuilderRef, catch_pad : ValueRef, bb : BasicBlockRef) : ValueRef\n  fun build_catch_pad = LLVMBuildCatchPad(b : BuilderRef, parent_pad : ValueRef, args : ValueRef*, num_args : UInt, name : Char*) : ValueRef\n  fun build_catch_switch = LLVMBuildCatchSwitch(b : BuilderRef, parent_pad : ValueRef, unwind_bb : BasicBlockRef, num_handlers : UInt, name : Char*) : ValueRef\n\n  fun add_case = LLVMAddCase(switch : ValueRef, on_val : ValueRef, dest : BasicBlockRef)\n  fun add_clause = LLVMAddClause(landing_pad : ValueRef, clause_val : ValueRef)\n  fun set_cleanup = LLVMSetCleanup(landing_pad : ValueRef, val : Bool)\n  fun add_handler = LLVMAddHandler(catch_switch : ValueRef, dest : BasicBlockRef)\n\n  fun get_arg_operand = LLVMGetArgOperand(funclet : ValueRef, i : UInt) : ValueRef\n\n  fun build_add = LLVMBuildAdd(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_fadd = LLVMBuildFAdd(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_sub = LLVMBuildSub(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_fsub = LLVMBuildFSub(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_mul = LLVMBuildMul(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_fmul = LLVMBuildFMul(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_udiv = LLVMBuildUDiv(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_sdiv = LLVMBuildSDiv(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_exact_sdiv = LLVMBuildExactSDiv(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_fdiv = LLVMBuildFDiv(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_urem = LLVMBuildURem(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_srem = LLVMBuildSRem(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_shl = LLVMBuildShl(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_lshr = LLVMBuildLShr(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_ashr = LLVMBuildAShr(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_and = LLVMBuildAnd(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_or = LLVMBuildOr(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_xor = LLVMBuildXor(BuilderRef, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_not = LLVMBuildNot(BuilderRef, value : ValueRef, name : Char*) : ValueRef\n  fun build_neg = LLVMBuildNeg(BuilderRef, value : ValueRef, name : Char*) : ValueRef\n  fun build_fneg = LLVMBuildFNeg(BuilderRef, value : ValueRef, name : Char*) : ValueRef\n\n  fun build_malloc = LLVMBuildMalloc(BuilderRef, ty : TypeRef, name : Char*) : ValueRef\n  fun build_array_malloc = LLVMBuildArrayMalloc(BuilderRef, ty : TypeRef, val : ValueRef, name : Char*) : ValueRef\n  fun build_alloca = LLVMBuildAlloca(BuilderRef, ty : TypeRef, name : Char*) : ValueRef\n  fun build_load2 = LLVMBuildLoad2(BuilderRef, ty : TypeRef, pointer_val : ValueRef, name : Char*) : ValueRef\n  fun build_store = LLVMBuildStore(BuilderRef, val : ValueRef, ptr : ValueRef) : ValueRef\n  fun build_gep2 = LLVMBuildGEP2(b : BuilderRef, ty : TypeRef, pointer : ValueRef, indices : ValueRef*, num_indices : UInt, name : Char*) : ValueRef\n  fun build_inbounds_gep2 = LLVMBuildInBoundsGEP2(b : BuilderRef, ty : TypeRef, pointer : ValueRef, indices : ValueRef*, num_indices : UInt, name : Char*) : ValueRef\n  fun build_global_string_ptr = LLVMBuildGlobalStringPtr(b : BuilderRef, str : Char*, name : Char*) : ValueRef\n  fun set_volatile = LLVMSetVolatile(memory_access_inst : ValueRef, is_volatile : Bool)\n  fun set_ordering = LLVMSetOrdering(memory_access_inst : ValueRef, ordering : LLVM::AtomicOrdering)\n\n  fun build_trunc = LLVMBuildTrunc(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_zext = LLVMBuildZExt(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_sext = LLVMBuildSExt(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_fp2ui = LLVMBuildFPToUI(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_fp2si = LLVMBuildFPToSI(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_ui2fp = LLVMBuildUIToFP(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_si2fp = LLVMBuildSIToFP(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_fptrunc = LLVMBuildFPTrunc(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_fpext = LLVMBuildFPExt(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_ptr2int = LLVMBuildPtrToInt(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_int2ptr = LLVMBuildIntToPtr(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n  fun build_bit_cast = LLVMBuildBitCast(BuilderRef, val : ValueRef, dest_ty : TypeRef, name : Char*) : ValueRef\n\n  fun build_icmp = LLVMBuildICmp(BuilderRef, op : LLVM::IntPredicate, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n  fun build_fcmp = LLVMBuildFCmp(BuilderRef, op : LLVM::RealPredicate, lhs : ValueRef, rhs : ValueRef, name : Char*) : ValueRef\n\n  fun build_phi = LLVMBuildPhi(BuilderRef, ty : TypeRef, name : Char*) : ValueRef\n  fun build_call2 = LLVMBuildCall2(BuilderRef, TypeRef, fn : ValueRef, args : ValueRef*, num_args : UInt, name : Char*) : ValueRef\n  {% unless LibLLVM::IS_LT_180 %}\n    fun build_call_with_operand_bundles = LLVMBuildCallWithOperandBundles(BuilderRef, TypeRef, fn : ValueRef, args : ValueRef*, num_args : UInt, bundles : OperandBundleRef*, num_bundles : UInt, name : Char*) : ValueRef\n  {% end %}\n  fun build_select = LLVMBuildSelect(BuilderRef, if : ValueRef, then : ValueRef, else : ValueRef, name : Char*) : ValueRef\n  fun build_va_arg = LLVMBuildVAArg(BuilderRef, list : ValueRef, ty : TypeRef, name : Char*) : ValueRef\n  fun build_extract_value = LLVMBuildExtractValue(BuilderRef, agg_val : ValueRef, index : UInt, name : Char*) : ValueRef\n  fun build_fence = LLVMBuildFence(b : BuilderRef, ordering : LLVM::AtomicOrdering, single_thread : Bool, name : Char*) : ValueRef\n  fun build_atomicrmw = LLVMBuildAtomicRMW(b : BuilderRef, op : LLVM::AtomicRMWBinOp, ptr : ValueRef, val : ValueRef, ordering : LLVM::AtomicOrdering, single_thread : Bool) : ValueRef\n  fun build_atomic_cmp_xchg = LLVMBuildAtomicCmpXchg(b : BuilderRef, ptr : ValueRef, cmp : ValueRef, new : ValueRef, success_ordering : LLVM::AtomicOrdering, failure_ordering : LLVM::AtomicOrdering, single_thread : Bool) : ValueRef\n\n  fun create_memory_buffer_with_contents_of_file = LLVMCreateMemoryBufferWithContentsOfFile(path : Char*, out_mem_buf : MemoryBufferRef*, out_message : Char**) : Bool\n  fun get_buffer_start = LLVMGetBufferStart(mem_buf : MemoryBufferRef) : Char*\n  fun get_buffer_size = LLVMGetBufferSize(mem_buf : MemoryBufferRef) : SizeT\n  fun dispose_memory_buffer = LLVMDisposeMemoryBuffer(mem_buf : MemoryBufferRef)\n\n  {% if LibLLVM::IS_LT_170 %}\n    fun get_global_pass_registry = LLVMGetGlobalPassRegistry : PassRegistryRef\n\n    fun pass_manager_create = LLVMCreatePassManager : PassManagerRef\n    fun create_function_pass_manager_for_module = LLVMCreateFunctionPassManagerForModule(m : ModuleRef) : PassManagerRef\n    fun run_pass_manager = LLVMRunPassManager(pm : PassManagerRef, m : ModuleRef) : Bool\n    fun initialize_function_pass_manager = LLVMInitializeFunctionPassManager(fpm : PassManagerRef) : Bool\n    fun run_function_pass_manager = LLVMRunFunctionPassManager(fpm : PassManagerRef, f : ValueRef) : Bool\n    fun finalize_function_pass_manager = LLVMFinalizeFunctionPassManager(fpm : PassManagerRef) : Bool\n    fun dispose_pass_manager = LLVMDisposePassManager(pm : PassManagerRef)\n  {% end %}\n\n  fun start_multithreaded = LLVMStartMultithreaded : Bool\n  fun stop_multithreaded = LLVMStopMultithreaded\n  fun is_multithreaded = LLVMIsMultithreaded : Bool\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/debug_info.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  enum DWARFEmissionKind\n    Full = 1\n  end\n\n  fun create_di_builder = LLVMCreateDIBuilder(m : ModuleRef) : DIBuilderRef\n  fun dispose_di_builder = LLVMDisposeDIBuilder(builder : DIBuilderRef)\n  fun di_builder_finalize = LLVMDIBuilderFinalize(builder : DIBuilderRef)\n\n  {% if LibLLVM::IS_LT_110 %}\n    fun di_builder_create_compile_unit = LLVMDIBuilderCreateCompileUnit(\n      builder : DIBuilderRef, lang : LLVM::DwarfSourceLanguage, file_ref : MetadataRef, producer : Char*,\n      producer_len : SizeT, is_optimized : Bool, flags : Char*, flags_len : SizeT, runtime_ver : UInt,\n      split_name : Char*, split_name_len : SizeT, kind : DWARFEmissionKind, dwo_id : UInt,\n      split_debug_inlining : Bool, debug_info_for_profiling : Bool,\n    ) : MetadataRef\n  {% else %}\n    fun di_builder_create_compile_unit = LLVMDIBuilderCreateCompileUnit(\n      builder : DIBuilderRef, lang : LLVM::DwarfSourceLanguage, file_ref : MetadataRef, producer : Char*,\n      producer_len : SizeT, is_optimized : Bool, flags : Char*, flags_len : SizeT, runtime_ver : UInt,\n      split_name : Char*, split_name_len : SizeT, kind : DWARFEmissionKind, dwo_id : UInt,\n      split_debug_inlining : Bool, debug_info_for_profiling : Bool, sys_root : Char*,\n      sys_root_len : SizeT, sdk : Char*, sdk_len : SizeT,\n    ) : MetadataRef\n  {% end %}\n\n  fun di_builder_create_file = LLVMDIBuilderCreateFile(\n    builder : DIBuilderRef, filename : Char*, filename_len : SizeT,\n    directory : Char*, directory_len : SizeT,\n  ) : MetadataRef\n\n  fun di_builder_create_function = LLVMDIBuilderCreateFunction(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT,\n    linkage_name : Char*, linkage_name_len : SizeT, file : MetadataRef, line_no : UInt,\n    ty : MetadataRef, is_local_to_unit : Bool, is_definition : Bool, scope_line : UInt,\n    flags : LLVM::DIFlags, is_optimized : Bool,\n  ) : MetadataRef\n\n  fun di_builder_create_lexical_block = LLVMDIBuilderCreateLexicalBlock(\n    builder : DIBuilderRef, scope : MetadataRef, file : MetadataRef, line : UInt, column : UInt,\n  ) : MetadataRef\n  fun di_builder_create_lexical_block_file = LLVMDIBuilderCreateLexicalBlockFile(\n    builder : DIBuilderRef, scope : MetadataRef, file_scope : MetadataRef, discriminator : UInt,\n  ) : MetadataRef\n\n  fun di_builder_create_debug_location = LLVMDIBuilderCreateDebugLocation(\n    ctx : ContextRef, line : UInt, column : UInt, scope : MetadataRef, inlined_at : MetadataRef,\n  ) : MetadataRef\n\n  fun di_builder_get_or_create_type_array = LLVMDIBuilderGetOrCreateTypeArray(builder : DIBuilderRef, types : MetadataRef*, length : SizeT) : MetadataRef\n\n  fun di_builder_create_subroutine_type = LLVMDIBuilderCreateSubroutineType(\n    builder : DIBuilderRef, file : MetadataRef, parameter_types : MetadataRef*,\n    num_parameter_types : UInt, flags : LLVM::DIFlags,\n  ) : MetadataRef\n  {% unless LibLLVM::IS_LT_90 %}\n    fun di_builder_create_enumerator = LLVMDIBuilderCreateEnumerator(\n      builder : DIBuilderRef, name : Char*, name_len : SizeT, value : Int64, is_unsigned : Bool,\n    ) : MetadataRef\n  {% end %}\n  {% unless LibLLVM::IS_LT_210 %}\n    fun di_builder_create_enumerator_of_arbitrary_precision = LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision(\n      builder : DIBuilderRef, name : Char*, name_len : SizeT, size_in_bits : UInt64, words : UInt64*, is_unsigned : Bool,\n    ) : MetadataRef\n  {% end %}\n  fun di_builder_create_enumeration_type = LLVMDIBuilderCreateEnumerationType(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT, file : MetadataRef,\n    line_number : UInt, size_in_bits : UInt64, align_in_bits : UInt32,\n    elements : MetadataRef*, num_elements : UInt, class_ty : MetadataRef,\n  ) : MetadataRef\n  fun di_builder_create_union_type = LLVMDIBuilderCreateUnionType(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT, file : MetadataRef,\n    line_number : UInt, size_in_bits : UInt64, align_in_bits : UInt32, flags : LLVM::DIFlags,\n    elements : MetadataRef*, num_elements : UInt, run_time_lang : UInt, unique_id : Char*, unique_id_len : SizeT,\n  ) : MetadataRef\n  fun di_builder_create_array_type = LLVMDIBuilderCreateArrayType(\n    builder : DIBuilderRef, size : UInt64, align_in_bits : UInt32,\n    ty : MetadataRef, subscripts : MetadataRef*, num_subscripts : UInt,\n  ) : MetadataRef\n  fun di_builder_create_unspecified_type = LLVMDIBuilderCreateUnspecifiedType(builder : DIBuilderRef, name : Char*, name_len : SizeT) : MetadataRef\n  fun di_builder_create_basic_type = LLVMDIBuilderCreateBasicType(\n    builder : DIBuilderRef, name : Char*, name_len : SizeT, size_in_bits : UInt64,\n    encoding : UInt, flags : LLVM::DIFlags,\n  ) : MetadataRef\n  fun di_builder_create_pointer_type = LLVMDIBuilderCreatePointerType(\n    builder : DIBuilderRef, pointee_ty : MetadataRef, size_in_bits : UInt64, align_in_bits : UInt32,\n    address_space : UInt, name : Char*, name_len : SizeT,\n  ) : MetadataRef\n  fun di_builder_create_struct_type = LLVMDIBuilderCreateStructType(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT, file : MetadataRef,\n    line_number : UInt, size_in_bits : UInt64, align_in_bits : UInt32, flags : LLVM::DIFlags,\n    derived_from : MetadataRef, elements : MetadataRef*, num_elements : UInt,\n    run_time_lang : UInt, v_table_holder : MetadataRef, unique_id : Char*, unique_id_len : SizeT,\n  ) : MetadataRef\n  fun di_builder_create_member_type = LLVMDIBuilderCreateMemberType(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT, file : MetadataRef,\n    line_no : UInt, size_in_bits : UInt64, align_in_bits : UInt32, offset_in_bits : UInt64,\n    flags : LLVM::DIFlags, ty : MetadataRef,\n  ) : MetadataRef\n  fun di_builder_create_replaceable_composite_type = LLVMDIBuilderCreateReplaceableCompositeType(\n    builder : DIBuilderRef, tag : UInt, name : Char*, name_len : SizeT, scope : MetadataRef,\n    file : MetadataRef, line : UInt, runtime_lang : UInt, size_in_bits : UInt64, align_in_bits : UInt32,\n    flags : LLVM::DIFlags, unique_identifier : Char*, unique_identifier_len : SizeT,\n  ) : MetadataRef\n\n  fun di_builder_get_or_create_subrange = LLVMDIBuilderGetOrCreateSubrange(builder : DIBuilderRef, lo : Int64, count : Int64) : MetadataRef\n  fun di_builder_get_or_create_array = LLVMDIBuilderGetOrCreateArray(builder : DIBuilderRef, data : MetadataRef*, length : SizeT) : MetadataRef\n\n  {% if LibLLVM::IS_LT_140 %}\n    fun di_builder_create_expression = LLVMDIBuilderCreateExpression(builder : DIBuilderRef, addr : Int64*, length : SizeT) : MetadataRef\n  {% else %}\n    fun di_builder_create_expression = LLVMDIBuilderCreateExpression(builder : DIBuilderRef, addr : UInt64*, length : SizeT) : MetadataRef\n  {% end %}\n\n  fun metadata_replace_all_uses_with = LLVMMetadataReplaceAllUsesWith(target_metadata : MetadataRef, replacement : MetadataRef)\n\n  {% if LibLLVM::IS_LT_190 %}\n    fun di_builder_insert_declare_at_end = LLVMDIBuilderInsertDeclareAtEnd(\n      builder : DIBuilderRef, storage : ValueRef, var_info : MetadataRef,\n      expr : MetadataRef, debug_loc : MetadataRef, block : BasicBlockRef,\n    ) : ValueRef\n  {% else %}\n    fun di_builder_insert_declare_record_at_end = LLVMDIBuilderInsertDeclareRecordAtEnd(\n      builder : DIBuilderRef, storage : ValueRef, var_info : MetadataRef,\n      expr : MetadataRef, debug_loc : MetadataRef, block : BasicBlockRef,\n    ) : DbgRecordRef\n  {% end %}\n\n  fun di_builder_create_auto_variable = LLVMDIBuilderCreateAutoVariable(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT, file : MetadataRef,\n    line_no : UInt, ty : MetadataRef, always_preserve : Bool, flags : LLVM::DIFlags, align_in_bits : UInt32,\n  ) : MetadataRef\n  fun di_builder_create_parameter_variable = LLVMDIBuilderCreateParameterVariable(\n    builder : DIBuilderRef, scope : MetadataRef, name : Char*, name_len : SizeT, arg_no : UInt,\n    file : MetadataRef, line_no : UInt, ty : MetadataRef, always_preserve : Bool, flags : LLVM::DIFlags,\n  ) : MetadataRef\n\n  fun set_subprogram = LLVMSetSubprogram(func : ValueRef, sp : MetadataRef)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/error.cr",
    "content": "lib LibLLVM\n  type ErrorRef = Void*\n\n  fun get_error_message = LLVMGetErrorMessage(err : ErrorRef) : Char*\n  fun dispose_error_message = LLVMDisposeErrorMessage(err_msg : Char*)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/execution_engine.cr",
    "content": "require \"./target\"\nrequire \"./target_machine\"\nrequire \"./types\"\n\nlib LibLLVM\n  fun link_in_mc_jit = LLVMLinkInMCJIT\n\n  type GenericValueRef = Void*\n  type ExecutionEngineRef = Void*\n  type MCJITMemoryManagerRef = Void*\n\n  struct MCJITCompilerOptions\n    opt_level : UInt\n    code_model : LLVM::CodeModel\n    no_frame_pointer_elim : Bool\n    enable_fast_isel : Bool\n    mcjmm : MCJITMemoryManagerRef\n  end\n\n  fun create_generic_value_of_int = LLVMCreateGenericValueOfInt(ty : TypeRef, n : ULongLong, is_signed : Bool) : GenericValueRef\n  fun create_generic_value_of_pointer = LLVMCreateGenericValueOfPointer(p : Void*) : GenericValueRef\n  fun generic_value_to_int = LLVMGenericValueToInt(gen_val : GenericValueRef, is_signed : Bool) : ULongLong\n  fun generic_value_to_pointer = LLVMGenericValueToPointer(gen_val : GenericValueRef) : Void*\n  fun generic_value_to_float = LLVMGenericValueToFloat(ty_ref : TypeRef, gen_val : GenericValueRef) : Double\n  fun dispose_generic_value = LLVMDisposeGenericValue(gen_val : GenericValueRef)\n\n  fun create_jit_compiler_for_module = LLVMCreateJITCompilerForModule(out_jit : ExecutionEngineRef*, m : ModuleRef, opt_level : UInt, error : Char**) : Bool\n  fun create_mc_jit_compiler_for_module = LLVMCreateMCJITCompilerForModule(out_jit : ExecutionEngineRef*, m : ModuleRef, options : MCJITCompilerOptions*, size_of_options : SizeT, out_error : Char**) : Bool\n  fun dispose_execution_engine = LLVMDisposeExecutionEngine(ee : ExecutionEngineRef)\n  fun run_function = LLVMRunFunction(ee : ExecutionEngineRef, f : ValueRef, num_args : UInt, args : GenericValueRef*) : GenericValueRef\n  fun get_execution_engine_target_machine = LLVMGetExecutionEngineTargetMachine(ee : ExecutionEngineRef) : TargetMachineRef\n  fun get_pointer_to_global = LLVMGetPointerToGlobal(ee : ExecutionEngineRef, global : ValueRef) : Void*\n  fun get_function_address = LLVMGetFunctionAddress(ee : ExecutionEngineRef, name : Char*) : UInt64\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/initialization.cr",
    "content": "{% skip_file unless LibLLVM::IS_LT_170 %}\n\nrequire \"./types\"\n\nlib LibLLVM\n  fun initialize_core = LLVMInitializeCore(r : PassRegistryRef)\n  fun initialize_transform_utils = LLVMInitializeTransformUtils(r : PassRegistryRef)\n  fun initialize_scalar_opts = LLVMInitializeScalarOpts(r : PassRegistryRef)\n  {% if LibLLVM::IS_LT_160 %} fun initialize_obj_c_arc_opts = LLVMInitializeObjCARCOpts(r : PassRegistryRef) {% end %}\n  fun initialize_vectorization = LLVMInitializeVectorization(r : PassRegistryRef)\n  fun initialize_inst_combine = LLVMInitializeInstCombine(r : PassRegistryRef)\n  fun initialize_ipo = LLVMInitializeIPO(r : PassRegistryRef)\n  {% if LibLLVM::IS_LT_160 %} fun initialize_instrumentation = LLVMInitializeInstrumentation(r : PassRegistryRef) {% end %}\n  fun initialize_analysis = LLVMInitializeAnalysis(r : PassRegistryRef)\n  fun initialize_ipa = LLVMInitializeIPA(r : PassRegistryRef)\n  fun initialize_code_gen = LLVMInitializeCodeGen(r : PassRegistryRef)\n  fun initialize_target = LLVMInitializeTarget(r : PassRegistryRef)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/ir_reader.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  fun parse_ir_in_context = LLVMParseIRInContext(context_ref : ContextRef, mem_buf : MemoryBufferRef, out_m : ModuleRef*, out_message : Char**) : Bool\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/lljit.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\nlib LibLLVM\n  alias OrcLLJITBuilderRef = Void*\n  alias OrcLLJITRef = Void*\n\n  fun orc_create_lljit_builder = LLVMOrcCreateLLJITBuilder : OrcLLJITBuilderRef\n  fun orc_dispose_lljit_builder = LLVMOrcDisposeLLJITBuilder(builder : OrcLLJITBuilderRef)\n\n  fun orc_create_lljit = LLVMOrcCreateLLJIT(result : OrcLLJITRef*, builder : OrcLLJITBuilderRef) : ErrorRef\n  fun orc_dispose_lljit = LLVMOrcDisposeLLJIT(j : OrcLLJITRef) : ErrorRef\n\n  fun orc_lljit_get_main_jit_dylib = LLVMOrcLLJITGetMainJITDylib(j : OrcLLJITRef) : OrcJITDylibRef\n  fun orc_lljit_get_global_prefix = LLVMOrcLLJITGetGlobalPrefix(j : OrcLLJITRef) : Char\n  fun orc_lljit_add_llvm_ir_module = LLVMOrcLLJITAddLLVMIRModule(j : OrcLLJITRef, jd : OrcJITDylibRef, tsm : OrcThreadSafeModuleRef) : ErrorRef\n  fun orc_lljit_lookup = LLVMOrcLLJITLookup(j : OrcLLJITRef, result : OrcExecutorAddress*, name : Char*) : ErrorRef\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/orc.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\nlib LibLLVM\n  # OrcJITTargetAddress before LLVM 13.0 (also an alias of UInt64)\n  alias OrcExecutorAddress = UInt64\n  alias OrcSymbolStringPoolEntryRef = Void*\n  alias OrcJITDylibRef = Void*\n  alias OrcDefinitionGeneratorRef = Void*\n  alias OrcSymbolPredicate = Void*, OrcSymbolStringPoolEntryRef -> Int\n  alias OrcThreadSafeContextRef = Void*\n  alias OrcThreadSafeModuleRef = Void*\n\n  fun orc_create_dynamic_library_search_generator_for_process = LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess(\n    result : OrcDefinitionGeneratorRef*, global_prefx : Char,\n    filter : OrcSymbolPredicate, filter_ctx : Void*,\n  ) : ErrorRef\n\n  fun orc_jit_dylib_add_generator = LLVMOrcJITDylibAddGenerator(jd : OrcJITDylibRef, dg : OrcDefinitionGeneratorRef)\n\n  fun orc_create_new_thread_safe_context = LLVMOrcCreateNewThreadSafeContext : OrcThreadSafeContextRef\n  {% if LibLLVM::IS_LT_210 %}\n    fun orc_thread_safe_context_get_context = LLVMOrcThreadSafeContextGetContext(ts_ctx : OrcThreadSafeContextRef) : ContextRef\n  {% else %}\n    fun orc_create_new_thread_safe_context_from_llvm_context = LLVMOrcCreateNewThreadSafeContextFromLLVMContext(ctx : ContextRef) : OrcThreadSafeContextRef\n  {% end %}\n  fun orc_dispose_thread_safe_context = LLVMOrcDisposeThreadSafeContext(ts_ctx : OrcThreadSafeContextRef)\n\n  fun orc_create_new_thread_safe_module = LLVMOrcCreateNewThreadSafeModule(m : ModuleRef, ts_ctx : OrcThreadSafeContextRef) : OrcThreadSafeModuleRef\n  fun orc_dispose_thread_safe_module = LLVMOrcDisposeThreadSafeModule(tsm : OrcThreadSafeModuleRef)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/support.cr",
    "content": "lib LibLLVM\n  fun parse_command_line_options = LLVMParseCommandLineOptions(argc : Int, argv : Char**, overview : Char*)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/target.cr",
    "content": "require \"./types\"\n\nlib LibLLVM\n  type TargetDataRef = Void*\n\n  {% for target in ALL_TARGETS %}\n    {% name = target.downcase.id %}\n    fun initialize_{{name}}_target_info = LLVMInitialize{{target.id}}TargetInfo\n    fun initialize_{{name}}_target = LLVMInitialize{{target.id}}Target\n    fun initialize_{{name}}_target_mc = LLVMInitialize{{target.id}}TargetMC\n    fun initialize_{{name}}_asm_printer = LLVMInitialize{{target.id}}AsmPrinter\n    fun initialize_{{name}}_asm_parser = LLVMInitialize{{target.id}}AsmParser\n  {% end %}\n\n  fun set_module_data_layout = LLVMSetModuleDataLayout(m : ModuleRef, dl : TargetDataRef)\n\n  fun dispose_target_data = LLVMDisposeTargetData(td : TargetDataRef)\n  fun size_of_type_in_bits = LLVMSizeOfTypeInBits(td : TargetDataRef, ty : TypeRef) : ULongLong\n  fun abi_size_of_type = LLVMABISizeOfType(td : TargetDataRef, ty : TypeRef) : ULongLong\n  fun abi_alignment_of_type = LLVMABIAlignmentOfType(td : TargetDataRef, ty : TypeRef) : UInt\n  fun offset_of_element = LLVMOffsetOfElement(td : TargetDataRef, struct_ty : TypeRef, element : UInt) : ULongLong\n  fun copy_string_rep_of_target_data = LLVMCopyStringRepOfTargetData(td : TargetDataRef) : Char*\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/target_machine.cr",
    "content": "require \"./target\"\nrequire \"./types\"\n\nlib LibLLVM\n  type TargetMachineRef = Void*\n  type TargetRef = Void*\n\n  fun get_first_target = LLVMGetFirstTarget : TargetRef\n  fun get_next_target = LLVMGetNextTarget(t : TargetRef) : TargetRef\n  fun get_target_from_triple = LLVMGetTargetFromTriple(triple : Char*, t : TargetRef*, error_message : Char**) : Bool\n  fun get_target_name = LLVMGetTargetName(t : TargetRef) : Char*\n  fun get_target_description = LLVMGetTargetDescription(t : TargetRef) : Char*\n\n  fun create_target_machine = LLVMCreateTargetMachine(t : TargetRef, triple : Char*, cpu : Char*, features : Char*, level : LLVM::CodeGenOptLevel, reloc : LLVM::RelocMode, code_model : LLVM::CodeModel) : TargetMachineRef\n  fun dispose_target_machine = LLVMDisposeTargetMachine(t : TargetMachineRef)\n  fun get_target_machine_target = LLVMGetTargetMachineTarget(t : TargetMachineRef) : TargetRef\n  fun get_target_machine_triple = LLVMGetTargetMachineTriple(t : TargetMachineRef) : Char*\n  fun get_target_machine_cpu = LLVMGetTargetMachineCPU(t : TargetMachineRef) : Char*\n  fun create_target_data_layout = LLVMCreateTargetDataLayout(t : TargetMachineRef) : TargetDataRef\n  {% unless LibLLVM::IS_LT_180 %}\n    fun set_target_machine_global_isel = LLVMSetTargetMachineGlobalISel(t : TargetMachineRef, enable : Bool)\n  {% end %}\n  fun target_machine_emit_to_file = LLVMTargetMachineEmitToFile(t : TargetMachineRef, m : ModuleRef, filename : Char*, codegen : LLVM::CodeGenFileType, error_message : Char**) : Bool\n\n  fun get_default_target_triple = LLVMGetDefaultTargetTriple : Char*\n  fun normalize_target_triple = LLVMNormalizeTargetTriple(triple : Char*) : Char*\n  fun get_host_cpu_name = LLVMGetHostCPUName : Char*\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/transforms/pass_builder.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_130 %}\n\nrequire \"../target_machine\"\nrequire \"../types\"\n\nlib LibLLVM\n  type PassBuilderOptionsRef = Void*\n\n  fun run_passes = LLVMRunPasses(m : ModuleRef, passes : Char*, tm : TargetMachineRef, options : PassBuilderOptionsRef) : ErrorRef\n\n  fun create_pass_builder_options = LLVMCreatePassBuilderOptions : PassBuilderOptionsRef\n  fun dispose_pass_builder_options = LLVMDisposePassBuilderOptions(options : PassBuilderOptionsRef)\n  {% unless LibLLVM::IS_LT_170 %}\n    fun pass_builder_options_set_inliner_threshold = LLVMPassBuilderOptionsSetInlinerThreshold(PassBuilderOptionsRef, Int)\n  {% end %}\n  fun pass_builder_options_set_loop_unrolling = LLVMPassBuilderOptionsSetLoopUnrolling(PassBuilderOptionsRef, Bool)\n  fun pass_builder_options_set_loop_vectorization = LLVMPassBuilderOptionsSetLoopVectorization(PassBuilderOptionsRef, Bool)\n  fun pass_builder_options_set_slp_vectorization = LLVMPassBuilderOptionsSetSLPVectorization(PassBuilderOptionsRef, Bool)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/transforms/pass_manager_builder.cr",
    "content": "{% skip_file unless LibLLVM::IS_LT_170 %}\n\nrequire \"../types\"\n\nlib LibLLVM\n  type PassManagerBuilderRef = Void*\n\n  fun pass_manager_builder_create = LLVMPassManagerBuilderCreate : PassManagerBuilderRef\n  fun dispose_pass_manager_builder = LLVMPassManagerBuilderDispose(pmb : PassManagerBuilderRef)\n  fun pass_manager_builder_set_opt_level = LLVMPassManagerBuilderSetOptLevel(pmb : PassManagerBuilderRef, opt_level : UInt)\n  fun pass_manager_builder_set_size_level = LLVMPassManagerBuilderSetSizeLevel(pmb : PassManagerBuilderRef, size_level : UInt)\n  fun pass_manager_builder_set_disable_unroll_loops = LLVMPassManagerBuilderSetDisableUnrollLoops(pmb : PassManagerBuilderRef, value : Bool)\n  fun pass_manager_builder_set_disable_simplify_lib_calls = LLVMPassManagerBuilderSetDisableSimplifyLibCalls(pmb : PassManagerBuilderRef, value : Bool)\n  fun pass_manager_builder_use_inliner_with_threshold = LLVMPassManagerBuilderUseInlinerWithThreshold(pmb : PassManagerBuilderRef, threshold : UInt)\n  fun pass_manager_builder_populate_function_pass_manager = LLVMPassManagerBuilderPopulateFunctionPassManager(pmb : PassManagerBuilderRef, pm : PassManagerRef)\n  fun pass_manager_builder_populate_module_pass_manager = LLVMPassManagerBuilderPopulateModulePassManager(pmb : PassManagerBuilderRef, pm : PassManagerRef)\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm/types.cr",
    "content": "lib LibLLVM\n  # LLVMBool\n  alias Bool = LibC::Int\n\n  type MemoryBufferRef = Void*\n  type ContextRef = Void*\n  type ModuleRef = Void*\n  type TypeRef = Void*\n  type ValueRef = Void*\n  type BasicBlockRef = Void*\n  type MetadataRef = Void*\n  type BuilderRef = Void*\n  type DIBuilderRef = Void*\n  type PassManagerRef = Void*\n  {% if LibLLVM::IS_LT_170 %}\n    type PassRegistryRef = Void*\n  {% end %}\n  type OperandBundleRef = Void*\n  type AttributeRef = Void*\n  type DbgRecordRef = Void*\nend\n"
  },
  {
    "path": "src/llvm/lib_llvm.cr",
    "content": "{% begin %}\n  {% if flag?(:msvc) && !flag?(:static) %}\n    {% config = nil %}\n    {% for dir in Crystal::LIBRARY_PATH.split(Crystal::System::Process::HOST_PATH_DELIMITER) %}\n      {% config ||= read_file?(\"#{dir.id}/llvm_VERSION\") %}\n    {% end %}\n\n    {% lines = config ? config.lines.map(&.chomp) : nil %}\n    {% llvm_version = env(\"LLVM_VERSION\") || (lines && lines[0]) %}\n    {% llvm_targets = env(\"LLVM_TARGETS\") || (lines && lines[1]) %}\n    {% llvm_ldflags = env(\"LLVM_LDFLAGS\") || (lines && lines[2]) %}\n\n    @[Link(\"llvm\")]\n    {% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n      @[Link(dll: \"LLVM-C.dll\")]\n    {% end %}\n    lib LibLLVM\n    end\n  {% else %}\n    {% llvm_config = env(\"LLVM_CONFIG\") || `sh #{__DIR__}/ext/find-llvm-config.sh`.stringify %}\n    {% llvm_version = env(\"LLVM_VERSION\") || `#{llvm_config.id} --version`.stringify %}\n    {% llvm_targets = env(\"LLVM_TARGETS\") || `#{llvm_config.id} --targets-built`.stringify %}\n    {% llvm_ldflags = env(\"LLVM_LDFLAGS\") || \"`#{llvm_config.id} --libs --system-libs --ldflags#{\" --link-static\".id if flag?(:static)}#{\" 2> /dev/null\".id unless flag?(:win32)}`\" %}\n\n    {% unless flag?(:win32) %}\n      @[Link(\"stdc++\")]\n      lib LibLLVM\n      end\n    {% end %}\n  {% end %}\n\n  {% llvm_version ||= Crystal::DESCRIPTION.gsub(/.*LLVM: ([^\\n]*).*/m, \"\\\\1\") %}\n\n  {% unless llvm_targets %}\n    {% if flag?(:i386) || flag?(:x86_64) %}\n      {% llvm_targets = \"X86\" %}\n    {% elsif flag?(:arm) %}\n      {% llvm_targets = \"ARM\" %}\n    {% elsif flag?(:aarch64) %}\n      {% llvm_targets = \"AArch64\" %}\n    {% elsif flag?(:wasm32) %}\n      {% llvm_targets = \"WebAssembly\" %}\n    {% elsif flag?(:avr) %}\n      {% llvm_targets = \"AVR\" %}\n    {% end %}\n  {% end %}\n\n  {% if llvm_ldflags %}\n    @[Link(ldflags: {{ llvm_ldflags }})]\n  {% end %}\n  lib LibLLVM\n    VERSION = {{ llvm_version.strip.gsub(/git/, \"\").gsub(/-?rc.*/, \"\") }}\n    BUILT_TARGETS = {{ llvm_targets.strip.downcase.gsub(/;|,/, \" \").split(' ').map(&.id.symbolize) }}\n  end\n{% end %}\n\n# Supported library versions:\n#\n# * LLVM (8-22; aarch64 requires 13+)\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#other-stdlib-libraries\n{% begin %}\n  lib LibLLVM\n    IS_LT_90 = {{compare_versions(LibLLVM::VERSION, \"9.0.0\") < 0}}\n    IS_LT_100 = {{compare_versions(LibLLVM::VERSION, \"10.0.0\") < 0}}\n    IS_LT_110 = {{compare_versions(LibLLVM::VERSION, \"11.0.0\") < 0}}\n    IS_LT_120 = {{compare_versions(LibLLVM::VERSION, \"12.0.0\") < 0}}\n    IS_LT_130 = {{compare_versions(LibLLVM::VERSION, \"13.0.0\") < 0}}\n    IS_LT_140 = {{compare_versions(LibLLVM::VERSION, \"14.0.0\") < 0}}\n    IS_LT_150 = {{compare_versions(LibLLVM::VERSION, \"15.0.0\") < 0}}\n    IS_LT_160 = {{compare_versions(LibLLVM::VERSION, \"16.0.0\") < 0}}\n    IS_LT_170 = {{compare_versions(LibLLVM::VERSION, \"17.0.0\") < 0}}\n    IS_LT_180 = {{compare_versions(LibLLVM::VERSION, \"18.0.0\") < 0}}\n    IS_LT_190 = {{compare_versions(LibLLVM::VERSION, \"19.0.0\") < 0}}\n    IS_LT_200 = {{compare_versions(LibLLVM::VERSION, \"20.0.0\") < 0}}\n    IS_LT_210 = {{compare_versions(LibLLVM::VERSION, \"21.0.0\") < 0}}\n  end\n{% end %}\n\nlib LibLLVM\n  alias Char = LibC::Char\n  alias Int = LibC::Int\n  alias UInt = LibC::UInt\n  alias LongLong = LibC::LongLong\n  alias ULongLong = LibC::ULongLong\n  alias Double = LibC::Double\n  alias SizeT = LibC::SizeT\nend\n\nrequire \"./lib_llvm/config\"\nrequire \"./lib_llvm/**\"\n"
  },
  {
    "path": "src/llvm/lib_llvm_ext.cr",
    "content": "require \"./lib_llvm\"\n{% if flag?(:win32) %}\n  @[Link(ldflags: \"#{__DIR__}/ext/llvm_ext.obj\")]\n{% else %}\n  @[Link(ldflags: \"#{__DIR__}/ext/llvm_ext.o\")]\n{% end %}\nlib LibLLVMExt\n  alias Char = LibC::Char\n  alias Int = LibC::Int\n  alias UInt = LibC::UInt\n  alias SizeT = LibC::SizeT\n\n  {% if LibLLVM::IS_LT_90 %}\n    fun di_builder_create_enumerator = LLVMExtDIBuilderCreateEnumerator(builder : LibLLVM::DIBuilderRef, name : Char*, name_len : SizeT, value : Int64, is_unsigned : LibLLVM::Bool) : LibLLVM::MetadataRef\n    fun clear_current_debug_location = LLVMExtClearCurrentDebugLocation(b : LibLLVM::BuilderRef)\n  {% end %}\n\n  fun create_operand_bundle = LLVMExtCreateOperandBundle(tag : Char*, tag_len : SizeT,\n                                                         args : LibLLVM::ValueRef*,\n                                                         num_args : UInt) : LibLLVM::OperandBundleRef\n\n  fun dispose_operand_bundle = LLVMExtDisposeOperandBundle(bundle : LibLLVM::OperandBundleRef)\n\n  fun build_call_with_operand_bundles = LLVMExtBuildCallWithOperandBundles(LibLLVM::BuilderRef, LibLLVM::TypeRef, fn : LibLLVM::ValueRef,\n                                                                           args : LibLLVM::ValueRef*, num_args : UInt,\n                                                                           bundles : LibLLVM::OperandBundleRef*, num_bundles : UInt,\n                                                                           name : Char*) : LibLLVM::ValueRef\n\n  fun build_invoke_with_operand_bundles = LLVMExtBuildInvokeWithOperandBundles(LibLLVM::BuilderRef, ty : LibLLVM::TypeRef, fn : LibLLVM::ValueRef,\n                                                                               args : LibLLVM::ValueRef*, num_args : UInt,\n                                                                               then : LibLLVM::BasicBlockRef, catch : LibLLVM::BasicBlockRef,\n                                                                               bundles : LibLLVM::OperandBundleRef*, num_bundles : UInt,\n                                                                               name : Char*) : LibLLVM::ValueRef\n\n  fun set_target_machine_global_isel = LLVMExtSetTargetMachineGlobalISel(t : LibLLVM::TargetMachineRef, enable : LibLLVM::Bool)\nend\n"
  },
  {
    "path": "src/llvm/memory_buffer.cr",
    "content": "class LLVM::MemoryBuffer\n  def self.from_file(filename : String)\n    ret = LibLLVM.create_memory_buffer_with_contents_of_file(filename, out mem_buf, out msg)\n    if ret != 0 && msg\n      raise LLVM.string_and_dispose(msg)\n    end\n    new(mem_buf)\n  end\n\n  def initialize(@unwrap : LibLLVM::MemoryBufferRef)\n    @finalized = false\n  end\n\n  def to_slice\n    Slice.new(\n      LibLLVM.get_buffer_start(@unwrap),\n      LibLLVM.get_buffer_size(@unwrap),\n    )\n  end\n\n  def dispose\n    return if @finalized\n    @finalized = true\n    finalize\n  end\n\n  def finalize\n    return if @finalized\n\n    LibLLVM.dispose_memory_buffer(@unwrap)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\nend\n"
  },
  {
    "path": "src/llvm/module.cr",
    "content": "class LLVM::Module\n  # We let a module store a reference to the context so that if\n  # someone is still holding a reference to the module but not to\n  # the context, the context won't be disposed (if the context is disposed,\n  # the module will no longer be valid and segfaults will happen)\n\n  getter context : Context\n\n  def self.parse(memory_buffer : MemoryBuffer, context : Context) : self\n    LibLLVM.parse_bitcode_in_context2(context, memory_buffer, out module_ref)\n    raise \"BUG: failed to parse LLVM bitcode from memory buffer\" unless module_ref\n    new(module_ref, context)\n  end\n\n  def initialize(@unwrap : LibLLVM::ModuleRef, @context : Context)\n    @owned = false\n  end\n\n  def name : String\n    bytes = LibLLVM.get_module_identifier(self, out bytesize)\n    String.new(Slice.new(bytes, bytesize))\n  end\n\n  def name=(name : String)\n    LibLLVM.set_module_identifier(self, name, name.bytesize)\n  end\n\n  def target=(target)\n    LibLLVM.set_target(self, target)\n  end\n\n  def data_layout=(data : TargetData)\n    LibLLVM.set_module_data_layout(self, data)\n  end\n\n  def dump\n    LibLLVM.dump_module(self)\n  end\n\n  def functions\n    FunctionCollection.new(self)\n  end\n\n  def globals\n    GlobalCollection.new(self)\n  end\n\n  def add_flag(module_flag : LibLLVM::ModuleFlagBehavior, key : String, val : Int32)\n    add_flag(module_flag, key, @context.int32.const_int(val))\n  end\n\n  def add_flag(module_flag : LibLLVM::ModuleFlagBehavior, key : String, val : Value)\n    LibLLVM.add_module_flag(\n      self,\n      module_flag,\n      key,\n      key.bytesize,\n      LibLLVM.value_as_metadata(val.to_unsafe)\n    )\n  end\n\n  def write_bitcode_to_file(filename : String)\n    LibLLVM.write_bitcode_to_file self, filename\n  end\n\n  @[Deprecated(\"ThinLTO is no longer supported; use `#write_bitcode_to_file` instead\")]\n  def write_bitcode_with_summary_to_file(filename : String)\n    LibLLVM.write_bitcode_to_file self, filename\n  end\n\n  def write_bitcode_to_memory_buffer\n    MemoryBuffer.new(LibLLVM.write_bitcode_to_memory_buffer self)\n  end\n\n  def write_bitcode_to_fd(fd : Int, should_close = false, buffered = false)\n    LibLLVM.write_bitcode_to_fd(self, fd, should_close ? 1 : 0, buffered ? 1 : 0)\n  end\n\n  def verify\n    error = LibLLVM.verify_module(self, LLVM::VerifierFailureAction::ReturnStatusAction, out message)\n    begin\n      if error == 1\n        raise \"Module validation failed: #{String.new(message)}\"\n      end\n    ensure\n      LibLLVM.dispose_message(message)\n    end\n  end\n\n  def print_to_file(filename)\n    if LibLLVM.print_module_to_file(self, filename, out error_msg) != 0\n      raise LLVM.string_and_dispose(error_msg)\n    end\n    self\n  end\n\n  {% unless LibLLVM::IS_LT_170 %}\n    @[Deprecated(\"The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead\")]\n  {% end %}\n  def new_function_pass_manager\n    FunctionPassManager.new LibLLVM.create_function_pass_manager_for_module(self)\n  end\n\n  def ==(other : self)\n    @unwrap == other.@unwrap\n  end\n\n  def to_s(io : IO) : Nil\n    LLVM.to_io(LibLLVM.print_module_to_string(self), io)\n    self\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def take_ownership(&)\n    if @owned\n      yield\n    else\n      @owned = true\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/module_pass_manager.cr",
    "content": "{% unless LibLLVM::IS_LT_170 %}\n  @[Deprecated(\"The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead\")]\n{% end %}\nclass LLVM::ModulePassManager\n  def initialize\n    @unwrap = LibLLVM.pass_manager_create\n  end\n\n  def run(mod)\n    LibLLVM.run_pass_manager(self, mod) != 0\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def finalize\n    LibLLVM.dispose_pass_manager(@unwrap)\n  end\nend\n"
  },
  {
    "path": "src/llvm/operand_bundle_def.cr",
    "content": "struct LLVM::OperandBundleDef\n  def initialize(@unwrap : LibLLVM::OperandBundleRef)\n  end\n\n  def self.null\n    new(Pointer(::Void).null.as(LibLLVM::OperandBundleRef))\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def dispose\n    {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.dispose_operand_bundle(@unwrap) if @unwrap\n  end\nend\n"
  },
  {
    "path": "src/llvm/orc/jit_dylib.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\n@[Experimental(\"The C API wrapped by this type is marked as experimental by LLVM.\")]\nclass LLVM::Orc::JITDylib\n  protected def initialize(@unwrap : LibLLVM::OrcJITDylibRef)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def link_symbols_from_current_process(global_prefix : Char) : Nil\n    LLVM.assert LibLLVM.orc_create_dynamic_library_search_generator_for_process(out dg, global_prefix.ord.to_u8, nil, nil)\n    LibLLVM.orc_jit_dylib_add_generator(self, dg)\n  end\nend\n"
  },
  {
    "path": "src/llvm/orc/lljit.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\n@[Experimental(\"The C API wrapped by this type is marked as experimental by LLVM.\")]\nclass LLVM::Orc::LLJIT\n  protected def initialize(@unwrap : LibLLVM::OrcLLJITRef)\n  end\n\n  def self.new(builder : LLJITBuilder)\n    builder.take_ownership { raise \"Failed to take ownership of LLVM::Orc::LLJITBuilder\" }\n    LLVM.assert LibLLVM.orc_create_lljit(out unwrap, builder)\n    new(unwrap)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def dispose : Nil\n    LLVM.assert LibLLVM.orc_dispose_lljit(self)\n    @unwrap = LibLLVM::OrcLLJITRef.null\n  end\n\n  def finalize\n    if @unwrap\n      LibLLVM.orc_dispose_lljit(self)\n    end\n  end\n\n  def main_jit_dylib : JITDylib\n    JITDylib.new(LibLLVM.orc_lljit_get_main_jit_dylib(self))\n  end\n\n  def global_prefix : Char\n    LibLLVM.orc_lljit_get_global_prefix(self).unsafe_chr\n  end\n\n  def add_llvm_ir_module(dylib : JITDylib, tsm : ThreadSafeModule) : Nil\n    tsm.take_ownership { raise \"Failed to take ownership of LLVM::Orc::ThreadSafeModule\" }\n    LLVM.assert LibLLVM.orc_lljit_add_llvm_ir_module(self, dylib, tsm)\n  end\n\n  def lookup(name : String) : Void*\n    LLVM.assert LibLLVM.orc_lljit_lookup(self, out address, name.check_no_null_byte)\n    Pointer(Void).new(address)\n  end\nend\n"
  },
  {
    "path": "src/llvm/orc/lljit_builder.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\n@[Experimental(\"The C API wrapped by this type is marked as experimental by LLVM.\")]\nclass LLVM::Orc::LLJITBuilder\n  protected def initialize(@unwrap : LibLLVM::OrcLLJITBuilderRef)\n    @dispose_on_finalize = true\n  end\n\n  def self.new\n    new(LibLLVM.orc_create_lljit_builder)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def dispose : Nil\n    LibLLVM.orc_dispose_lljit_builder(self)\n    @unwrap = LibLLVM::OrcLLJITBuilderRef.null\n  end\n\n  def finalize\n    if @dispose_on_finalize && @unwrap\n      dispose\n    end\n  end\n\n  def take_ownership(&) : Nil\n    if @dispose_on_finalize\n      @dispose_on_finalize = false\n    else\n      yield\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/orc/thread_safe_context.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\n@[Experimental(\"The C API wrapped by this type is marked as experimental by LLVM.\")]\nclass LLVM::Orc::ThreadSafeContext\n  protected def initialize(@unwrap : LibLLVM::OrcThreadSafeContextRef)\n  end\n\n  def self.new\n    new(LibLLVM.orc_create_new_thread_safe_context)\n  end\n\n  # This constructor is only available with LLVM 21 and above.\n  def self.new(ctx : LLVM::Context)\n    {% if LibLLVM.has_method?(:orc_create_new_thread_safe_context_from_llvm_context) %}\n      new(LibLLVM.orc_create_new_thread_safe_context_from_llvm_context(ctx))\n    {% else %}\n      raise NotImplementedError.new(\"LLVM::Orc::ThreadSafeContext.new(LLVM::Context)\")\n    {% end %}\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def dispose : Nil\n    LibLLVM.orc_dispose_thread_safe_context(self)\n    @unwrap = LibLLVM::OrcThreadSafeContextRef.null\n  end\n\n  def finalize\n    if @unwrap\n      dispose\n    end\n  end\n\n  {% unless LibLLVM::IS_LT_210 %}\n    @[Deprecated(\"This function is removed in LLVM 21.\")]\n  {% end %}\n  def context : LLVM::Context\n    {% if LibLLVM.has_method?(:orc_thread_safe_context_get_context) %}\n      LLVM::Context.new(LibLLVM.orc_thread_safe_context_get_context(self), false)\n    {% else %}\n      raise NotImplementedError.new(\"LLVM::Orc::ThreadSafeContext#context\")\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/llvm/orc/thread_safe_module.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_110 %}\n\n@[Experimental(\"The C API wrapped by this type is marked as experimental by LLVM.\")]\nclass LLVM::Orc::ThreadSafeModule\n  protected def initialize(@unwrap : LibLLVM::OrcThreadSafeModuleRef)\n    @dispose_on_finalize = true\n  end\n\n  def self.new(llvm_mod : LLVM::Module, ts_ctx : ThreadSafeContext)\n    llvm_mod.take_ownership { raise \"Failed to take ownership of LLVM::Module\" }\n    new(LibLLVM.orc_create_new_thread_safe_module(llvm_mod, ts_ctx))\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def dispose : Nil\n    LibLLVM.orc_dispose_thread_safe_module(self)\n    @unwrap = LibLLVM::OrcThreadSafeModuleRef.null\n  end\n\n  def finalize\n    if @dispose_on_finalize && @unwrap\n      dispose\n    end\n  end\n\n  def take_ownership(&) : Nil\n    if @dispose_on_finalize\n      @dispose_on_finalize = false\n    else\n      yield\n    end\n  end\nend\n"
  },
  {
    "path": "src/llvm/parameter_collection.cr",
    "content": "struct LLVM::ParameterCollection\n  include Indexable(LLVM::Value)\n\n  def initialize(@function : Function)\n  end\n\n  def size\n    LibLLVM.get_count_params(@function).to_i\n  end\n\n  def to_a : Array(LLVM::Value)\n    param_size = size()\n    Array(LLVM::Value).build(param_size) do |buffer|\n      LibLLVM.get_params(@function, buffer.as(LibLLVM::ValueRef*))\n      param_size\n    end\n  end\n\n  def unsafe_fetch(index : Int)\n    Value.new LibLLVM.get_param(@function, index)\n  end\n\n  def types\n    to_a.map(&.type)\n  end\nend\n"
  },
  {
    "path": "src/llvm/pass_builder_options.cr",
    "content": "{% skip_file if LibLLVM::IS_LT_130 %}\n\nclass LLVM::PassBuilderOptions\n  def initialize\n    @options = LibLLVM.create_pass_builder_options\n    @disposed = false\n  end\n\n  def self.new(&)\n    options = new\n    begin\n      yield options\n    ensure\n      options.finalize\n    end\n  end\n\n  def to_unsafe\n    @options\n  end\n\n  def finalize\n    return if @disposed\n    @disposed = true\n\n    LibLLVM.dispose_pass_builder_options(self)\n  end\n\n  {% unless LibLLVM::IS_LT_170 %}\n    def set_inliner_threshold(threshold : Int)\n      LibLLVM.pass_builder_options_set_inliner_threshold(self, threshold)\n    end\n  {% end %}\n\n  def set_loop_unrolling(enabled : Bool)\n    LibLLVM.pass_builder_options_set_loop_unrolling(self, enabled)\n  end\n\n  def set_loop_vectorization(enabled : Bool)\n    LibLLVM.pass_builder_options_set_loop_vectorization(self, enabled)\n  end\n\n  def set_slp_vectorization(enabled : Bool)\n    LibLLVM.pass_builder_options_set_slp_vectorization(self, enabled)\n  end\nend\n"
  },
  {
    "path": "src/llvm/pass_manager_builder.cr",
    "content": "{% unless LibLLVM::IS_LT_170 %}\n  @[Deprecated(\"The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead\")]\n{% end %}\nclass LLVM::PassManagerBuilder\n  def initialize\n    @unwrap = LibLLVM.pass_manager_builder_create\n  end\n\n  def opt_level=(level)\n    LibLLVM.pass_manager_builder_set_opt_level self, level\n  end\n\n  def size_level=(level)\n    LibLLVM.pass_manager_builder_set_size_level self, level\n  end\n\n  def disable_unroll_loops=(value)\n    LibLLVM.pass_manager_builder_set_disable_unroll_loops self, value ? 1 : 0\n  end\n\n  def disable_simplify_lib_calls=(value)\n    LibLLVM.pass_manager_builder_set_disable_simplify_lib_calls self, value ? 1 : 0\n  end\n\n  def use_inliner_with_threshold=(threshold)\n    LibLLVM.pass_manager_builder_use_inliner_with_threshold self, threshold\n  end\n\n  def populate(pm : FunctionPassManager)\n    LibLLVM.pass_manager_builder_populate_function_pass_manager self, pm\n  end\n\n  def populate(pm : ModulePassManager)\n    LibLLVM.pass_manager_builder_populate_module_pass_manager self, pm\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def finalize\n    LibLLVM.dispose_pass_manager_builder(@unwrap)\n  end\nend\n"
  },
  {
    "path": "src/llvm/pass_registry.cr",
    "content": "{% unless LibLLVM::IS_LT_170 %}\n  @[Deprecated(\"The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead\")]\n{% end %}\nstruct LLVM::PassRegistry\n  def self.instance : self\n    new LibLLVM.get_global_pass_registry\n  end\n\n  def initialize(@unwrap : LibLLVM::PassRegistryRef)\n  end\n\n  {% begin %}\n    Inits = %w[\n      initialize_core\n      initialize_transform_utils\n      initialize_scalar_opts\n      {% if LibLLVM::IS_LT_160 %} initialize_obj_c_arc_opts {% end %}\n      initialize_vectorization\n      initialize_inst_combine\n      initialize_ipo\n      {% if LibLLVM::IS_LT_160 %} initialize_instrumentation {% end %}\n      initialize_analysis\n      initialize_ipa\n      initialize_code_gen\n      initialize_target\n    ]\n  {% end %}\n\n  {% for name in Inits %}\n    def {{name.id}}\n      LibLLVM.{{name.id}} self\n    end\n  {% end %}\n\n  def initialize_all\n    {% for name in Inits %}\n      {{name.id}}\n    {% end %}\n  end\n\n  def to_unsafe\n    @unwrap\n  end\nend\n"
  },
  {
    "path": "src/llvm/phi_table.cr",
    "content": "struct LLVM::PhiTable\n  getter blocks : Array(LLVM::BasicBlock)\n  getter values : Array(LLVM::Value)\n\n  def initialize\n    @blocks = [] of LLVM::BasicBlock\n    @values = [] of LLVM::Value\n  end\n\n  def add(block, value)\n    @blocks << block\n    @values << value.to_value\n  end\n\n  def empty?\n    @blocks.empty?\n  end\n\n  def size\n    @blocks.size\n  end\nend\n"
  },
  {
    "path": "src/llvm/target.cr",
    "content": "struct LLVM::Target\n  def self.each(&)\n    target = LibLLVM.get_first_target\n    while target\n      yield Target.new target\n      target = LibLLVM.get_next_target target\n    end\n  end\n\n  def self.first : self\n    first? || raise \"No LLVM targets available (did you forget to invoke LLVM.init_native_target?)\"\n  end\n\n  def self.first? : self?\n    target = LibLLVM.get_first_target\n    target ? Target.new(target) : nil\n  end\n\n  def self.from_triple(triple) : self\n    return_code = LibLLVM.get_target_from_triple triple, out target, out error\n    raise ArgumentError.new(LLVM.string_and_dispose(error)) unless return_code == 0\n    new target\n  end\n\n  def initialize(@unwrap : LibLLVM::TargetRef)\n  end\n\n  def name\n    String.new LibLLVM.get_target_name(self)\n  end\n\n  def description\n    String.new LibLLVM.get_target_description(self)\n  end\n\n  def create_target_machine(triple, cpu = \"\", features = \"\",\n                            opt_level = LLVM::CodeGenOptLevel::Default,\n                            reloc = LLVM::RelocMode::PIC,\n                            code_model = LLVM::CodeModel::Default) : LLVM::TargetMachine\n    target_machine = LibLLVM.create_target_machine(self, triple, cpu, features, opt_level, reloc, code_model)\n    target_machine ? TargetMachine.new(target_machine) : raise \"Couldn't create target machine\"\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"LLVM::Target(name=\"\n    name.inspect(io)\n    io << \", description=\"\n    description.inspect(io)\n    io << ')'\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\nend\n"
  },
  {
    "path": "src/llvm/target_data.cr",
    "content": "struct LLVM::TargetData\n  def initialize(@unwrap : LibLLVM::TargetDataRef)\n  end\n\n  def size_in_bits(type)\n    LibLLVM.size_of_type_in_bits(self, type)\n  end\n\n  def size_in_bytes(type)\n    size_in_bits = size_in_bits(type)\n    size_in_bits // 8 &+ (size_in_bits & 0x7 != 0 ? 1 : 0)\n  end\n\n  def abi_size(type)\n    LibLLVM.abi_size_of_type(self, type)\n  end\n\n  def abi_alignment(type)\n    LibLLVM.abi_alignment_of_type(self, type)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def offset_of_element(struct_type, element)\n    # element_count = LibLLVM.count_struct_element_types(struct_type)\n    # raise \"Invalid element idx!\" unless element < element_count\n    LibLLVM.offset_of_element(self, struct_type, element)\n  end\n\n  def to_data_layout_string\n    LLVM.string_and_dispose(LibLLVM.copy_string_rep_of_target_data(self))\n  end\nend\n"
  },
  {
    "path": "src/llvm/target_machine.cr",
    "content": "class LLVM::TargetMachine\n  def initialize(@unwrap : LibLLVM::TargetMachineRef)\n  end\n\n  def target\n    target = LibLLVM.get_target_machine_target(self)\n    target ? Target.new(target) : raise \"Couldn't get target\"\n  end\n\n  def data_layout : LLVM::TargetData\n    @layout ||= begin\n      layout = LibLLVM.create_target_data_layout(self)\n      layout ? TargetData.new(layout) : raise \"Missing layout for #{self}\"\n    end\n  end\n\n  def triple : String\n    triple_c = LibLLVM.get_target_machine_triple(self)\n    LLVM.string_and_dispose(triple_c)\n  end\n\n  def cpu : String\n    cpu_c = LibLLVM.get_target_machine_cpu(self)\n    LLVM.string_and_dispose(cpu_c)\n  end\n\n  def emit_obj_to_file(llvm_mod, filename)\n    emit_to_file llvm_mod, filename, LLVM::CodeGenFileType::ObjectFile\n  end\n\n  def emit_asm_to_file(llvm_mod, filename)\n    emit_to_file llvm_mod, filename, LLVM::CodeGenFileType::AssemblyFile\n  end\n\n  def enable_global_isel=(enable : Bool)\n    {{ LibLLVM::IS_LT_180 ? LibLLVMExt : LibLLVM }}.set_target_machine_global_isel(self, enable ? 1 : 0)\n    enable\n  end\n\n  private def emit_to_file(llvm_mod, filename, type)\n    status = LibLLVM.target_machine_emit_to_file(self, llvm_mod, filename, type, out error_msg)\n    unless status == 0\n      raise LLVM.string_and_dispose(error_msg)\n    end\n    true\n  end\n\n  @[Deprecated(\"This API is now internal to the compiler and no longer updated publicly.\")]\n  def abi\n    triple = self.triple\n    case triple\n    when /x86_64.+windows-(?:msvc|gnu)/\n      ABI::X86_Win64.new(self)\n    when /x86_64|amd64/\n      ABI::X86_64.new(self)\n    when /i386|i486|i586|i686/\n      ABI::X86.new(self)\n    when /aarch64|arm64/\n      ABI::AArch64.new(self)\n    when /arm/\n      ABI::ARM.new(self)\n    when /avr/\n      ABI::AVR.new(self, cpu)\n    when /wasm32/\n      ABI::Wasm32.new(self)\n    else\n      raise \"Unsupported ABI for target triple: #{triple}\"\n    end\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def finalize\n    LibLLVM.dispose_target_machine(@unwrap)\n  end\nend\n"
  },
  {
    "path": "src/llvm/type.cr",
    "content": "struct LLVM::Type\n  getter unwrap : LibLLVM::TypeRef\n\n  def initialize(@unwrap : LibLLVM::TypeRef)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\n\n  def self.function(arg_types : Array(LLVM::Type), return_type, varargs = false) : self\n    new LibLLVM.function_type(return_type, (arg_types.to_unsafe.as(LibLLVM::TypeRef*)), arg_types.size, varargs ? 1 : 0)\n  end\n\n  def size\n    # Asking the size of void crashes the program, we definitely don't want that\n    if void?\n      context.int64.const_int(1)\n    else\n      Value.new LibLLVM.size_of(self)\n    end\n  end\n\n  def alignment\n    # Asking the alignment of void crashes the program, we definitely don't want that\n    if void?\n      context.int64.const_int(1)\n    else\n      Value.new LibLLVM.align_of(self)\n    end\n  end\n\n  def kind : LLVM::Type::Kind\n    LibLLVM.get_type_kind(self)\n  end\n\n  def void?\n    kind == Kind::Void\n  end\n\n  def null\n    Value.new LibLLVM.const_null(self)\n  end\n\n  def null_pointer\n    Value.new LibLLVM.const_pointer_null(self)\n  end\n\n  def undef\n    Value.new LibLLVM.get_undef(self)\n  end\n\n  def pointer(address_space = 0) : LLVM::Type\n    {% if LibLLVM::IS_LT_150 %}\n      Type.new LibLLVM.pointer_type(self, address_space)\n    {% else %}\n      Type.new LibLLVM.pointer_type_in_context(LibLLVM.get_type_context(self), address_space)\n    {% end %}\n  end\n\n  def array(count) : LLVM::Type\n    Type.new LibLLVM.array_type(self, count)\n  end\n\n  def vector(count) : self\n    Type.new LibLLVM.vector_type(self, count)\n  end\n\n  def int_width : Int32\n    raise \"Not an Integer\" unless kind == Kind::Integer\n    LibLLVM.get_int_type_width(self).to_i32\n  end\n\n  def packed_struct? : Bool\n    raise \"Not a Struct\" unless kind == Kind::Struct\n    LibLLVM.is_packed_struct(self) != 0\n  end\n\n  # Assuming this type is a struct, returns its name.\n  # The name can be `nil` if the struct is anonymous.\n  # Raises if this type is not a struct.\n  def struct_name : String?\n    raise \"Not a Struct\" unless kind == Kind::Struct\n\n    name = LibLLVM.get_struct_name(self)\n    name ? String.new(name) : nil\n  end\n\n  def struct_element_types : Array(LLVM::Type)\n    raise \"Not a Struct\" unless kind == Kind::Struct\n    count = LibLLVM.count_struct_element_types(self)\n\n    Array(LLVM::Type).build(count) do |buffer|\n      LibLLVM.get_struct_element_types(self, buffer.as(LibLLVM::TypeRef*))\n      count\n    end\n  end\n\n  def element_type : LLVM::Type\n    case kind\n    when Kind::Array, Kind::Vector\n      Type.new LibLLVM.get_element_type(self)\n    when Kind::Pointer\n      {% if LibLLVM::IS_LT_150 %}\n        Type.new LibLLVM.get_element_type(self)\n      {% else %}\n        raise \"Typed pointers are unavailable on LLVM 15.0 or above\"\n      {% end %}\n    else\n      raise \"Not a sequential type\"\n    end\n  end\n\n  def array_size : Int32\n    raise \"Not an Array\" unless kind == Kind::Array\n    LibLLVM.get_array_length(self).to_i32\n  end\n\n  def vector_size\n    raise \"Not a Vector\" unless kind == Kind::Vector\n    LibLLVM.get_vector_size(self).to_i32\n  end\n\n  def return_type\n    raise \"Not a Function\" unless kind == Kind::Function\n    Type.new LibLLVM.get_return_type(self)\n  end\n\n  def params_types\n    params_size = self.params_size\n    Array(LLVM::Type).build(params_size) do |buffer|\n      LibLLVM.get_param_types(self, buffer.as(LibLLVM::TypeRef*))\n      params_size\n    end\n  end\n\n  def params_size\n    raise \"Not a Function\" unless kind == Kind::Function\n    LibLLVM.count_param_types(self).to_i\n  end\n\n  def varargs?\n    raise \"Not a Function\" unless kind == Kind::Function\n    LibLLVM.is_function_var_arg(self) != 0\n  end\n\n  def const_int(value) : Value\n    if !value.is_a?(Int128) && !value.is_a?(UInt128) && int_width != 128\n      Value.new LibLLVM.const_int(self, value, 0)\n    else\n      encoded_value = UInt64[value & UInt64::MAX, (value >> 64) & UInt64::MAX]\n      Value.new LibLLVM.const_int_of_arbitrary_precision(self, encoded_value.size, encoded_value)\n    end\n  end\n\n  def const_float(value : Float32) : Value\n    Value.new LibLLVM.const_real(self, value)\n  end\n\n  def const_float(value : String) : Value\n    Value.new LibLLVM.const_real_of_string_and_size(self, value, value.bytesize)\n  end\n\n  def const_double(value : Float64) : Value\n    Value.new LibLLVM.const_real(self, value)\n  end\n\n  def const_double(string : String) : Value\n    Value.new LibLLVM.const_real_of_string_and_size(self, string, string.bytesize)\n  end\n\n  def const_array(values : Array(LLVM::Value)) : Value\n    Value.new LibLLVM.const_array(self, (values.to_unsafe.as(LibLLVM::ValueRef*)), values.size)\n  end\n\n  def const_data_array(buffer : Bytes) : Value\n    {% if LibLLVM::IS_LT_210 %}\n      raise NotImplementedError.new(\"LLVM::Type#const_data_array\")\n    {% else %}\n      Value.new LibLLVM.const_data_array(self, buffer, buffer.bytesize)\n    {% end %}\n  end\n\n  def inline_asm(asm_string, constraints, has_side_effects = false, is_align_stack = false, can_throw = false, dialect : InlineAsmDialect = InlineAsmDialect::ATT)\n    value =\n      {% if LibLLVM::IS_LT_130 %}\n        LibLLVM.get_inline_asm(\n          self,\n          asm_string,\n          asm_string.bytesize,\n          constraints,\n          constraints.bytesize,\n          (has_side_effects ? 1 : 0),\n          (is_align_stack ? 1 : 0),\n          dialect,\n        )\n      {% else %}\n        LibLLVM.get_inline_asm(\n          self,\n          asm_string,\n          asm_string.bytesize,\n          constraints,\n          constraints.bytesize,\n          (has_side_effects ? 1 : 0),\n          (is_align_stack ? 1 : 0),\n          dialect,\n          (can_throw ? 1 : 0)\n        )\n      {% end %}\n    Value.new value\n  end\n\n  def context : Context\n    Context.new(LibLLVM.get_type_context(self), dispose_on_finalize: false)\n  end\n\n  def inspect(io : IO) : Nil\n    LLVM.to_io(LibLLVM.print_type_to_string(self), io)\n    self\n  end\nend\n"
  },
  {
    "path": "src/llvm/value.cr",
    "content": "require \"./value_methods\"\n\nstruct LLVM::Value\n  include ValueMethods\n\n  def self.null\n    LLVM::Value.new(Pointer(::Void).null.as(LibLLVM::ValueRef))\n  end\n\n  def to_value\n    self\n  end\nend\n"
  },
  {
    "path": "src/llvm/value_methods.cr",
    "content": "module LLVM::ValueMethods\n  def initialize(@unwrap : LibLLVM::ValueRef)\n  end\n\n  def name=(name)\n    LibLLVM.set_value_name2(self, name, name.bytesize)\n  end\n\n  def name\n    ptr = LibLLVM.get_value_name2(self, out len)\n    String.new(ptr, len)\n  end\n\n  def kind\n    LibLLVM.get_value_kind(self)\n  end\n\n  def add_instruction_attribute(index : Int, attribute : LLVM::Attribute, context : LLVM::Context, type : LLVM::Type? = nil)\n    return if attribute.value == 0\n\n    attribute.each_kind do |kind|\n      LibLLVM.add_call_site_attribute(self, index, attribute_ref(context, kind, type))\n    end\n  end\n\n  private def attribute_ref(context, kind, type)\n    if type.is_a?(Type) && Attribute.requires_type?(kind)\n      {% if LibLLVM::IS_LT_120 %}\n        raise \"Type arguments are only supported on LLVM 12.0 or above\"\n      {% else %}\n        LibLLVM.create_type_attribute(context, kind, type)\n      {% end %}\n    else\n      LibLLVM.create_enum_attribute(context, kind, 0)\n    end\n  end\n\n  def constant?\n    LibLLVM.is_constant(self) != 0\n  end\n\n  def type\n    Type.new LibLLVM.type_of(self)\n  end\n\n  def thread_local=(thread_local)\n    LibLLVM.set_thread_local(self, thread_local ? 1 : 0)\n  end\n\n  def thread_local?\n    LibLLVM.is_thread_local(self) != 0\n  end\n\n  def linkage=(linkage)\n    LibLLVM.set_linkage(self, linkage)\n  end\n\n  def linkage\n    LibLLVM.get_linkage(self)\n  end\n\n  def dll_storage_class=(storage_class)\n    LibLLVM.set_dll_storage_class(self, storage_class)\n  end\n\n  def call_convention=(call_convention)\n    LibLLVM.set_instruction_call_convention(self, call_convention)\n  end\n\n  def call_convention\n    LibLLVM.get_instruction_call_convention(self)\n  end\n\n  def global_constant=(global_constant)\n    LibLLVM.set_global_constant(self, global_constant ? 1 : 0)\n  end\n\n  def global_constant?\n    LibLLVM.is_global_constant(self) != 0\n  end\n\n  def initializer=(initializer)\n    LibLLVM.set_initializer(self, initializer)\n  end\n\n  def initializer\n    init = LibLLVM.get_initializer(self)\n    init ? LLVM::Value.new(init) : nil\n  end\n\n  def volatile=(volatile)\n    LibLLVM.set_volatile(self, volatile ? 1 : 0)\n  end\n\n  def ordering=(ordering)\n    LibLLVM.set_ordering(self, ordering)\n  end\n\n  def alignment=(bytes)\n    LibLLVM.set_alignment(self, bytes)\n  end\n\n  def const_int_get_sext_value\n    LibLLVM.const_int_get_sext_value(self)\n  end\n\n  def const_int_get_zext_value\n    LibLLVM.const_int_get_zext_value(self)\n  end\n\n  def to_value\n    LLVM::Value.new @unwrap\n  end\n\n  def dump\n    LibLLVM.dump_value self\n  end\n\n  def inspect(io : IO) : Nil\n    LLVM.to_io(LibLLVM.print_value_to_string(self), io)\n  end\n\n  def to_unsafe\n    @unwrap\n  end\nend\n"
  },
  {
    "path": "src/llvm.cr",
    "content": "require \"./llvm/**\"\nrequire \"c/string\"\n\nmodule LLVM\n  # Returns the runtime version of LLVM.\n  #\n  # Starting with LLVM 16, this method returns the version as reported by\n  # `LLVMGetVersion` at runtime. Older versions of LLVM do not expose this\n  # information, so the value falls back to `LibLLVM::VERSION` which is\n  # determined at compile time and might slightly be out of sync to the\n  # dynamic library loaded at runtime.\n  def self.version\n    {% if LibLLVM.has_method?(:get_version) %}\n      LibLLVM.get_version(out major, out minor, out patch)\n      \"#{major}.#{minor}.#{patch}\"\n    {% else %}\n      LibLLVM::VERSION\n    {% end %}\n  end\n\n  {% for target in LibLLVM::ALL_TARGETS %}\n    {% name = target.downcase.id %}\n    @@initialized_{{name}} = Atomic(Bool).new(false)\n\n    def self.init_{{name}} : Nil\n      return if @@initialized_{{name}}.swap(true)\n\n      \\{% if LibLLVM::BUILT_TARGETS.includes?({{name.symbolize}}) %}\n        LibLLVM.initialize_{{name}}_target_info\n        LibLLVM.initialize_{{name}}_target\n        LibLLVM.initialize_{{name}}_target_mc\n        LibLLVM.initialize_{{name}}_asm_printer\n        LibLLVM.initialize_{{name}}_asm_parser\n        LibLLVM.link_in_mc_jit\n      \\{% else %}\n        raise \"ERROR: LLVM was built without {{target.id}} target\"\n      \\{% end %}\n    end\n  {% end %}\n\n  def self.init_native_target : Nil\n    {% if flag?(:i386) || flag?(:x86_64) %}\n      init_x86\n    {% elsif flag?(:aarch64) %}\n      init_aarch64\n    {% elsif flag?(:arm) %}\n      init_arm\n    {% elsif flag?(:wasm32) %}\n      init_webassembly\n    {% elsif flag?(:avr) %}\n      init_avr\n    {% else %}\n      {% raise \"Unsupported platform\" %}\n    {% end %}\n  end\n\n  def self.init_all_targets : Nil\n    {% for target in LibLLVM::ALL_TARGETS %}\n      {% name = target.downcase.id %}\n      \\{% if LibLLVM::BUILT_TARGETS.includes?({{name.symbolize}}) %}\n        init_{{name}}\n      \\{% end %}\n    {% end %}\n  end\n\n  @[Deprecated(\"This method has no effect\")]\n  def self.start_multithreaded : Bool\n    if multithreaded?\n      true\n    else\n      LibLLVM.start_multithreaded != 0\n    end\n  end\n\n  @[Deprecated(\"This method has no effect\")]\n  def self.stop_multithreaded\n    if multithreaded?\n      LibLLVM.stop_multithreaded\n    end\n  end\n\n  def self.multithreaded? : Bool\n    LibLLVM.is_multithreaded != 0\n  end\n\n  def self.default_target_triple : String\n    chars = LibLLVM.get_default_target_triple\n    case triple = string_and_dispose(chars)\n    when .starts_with?(\"aarch64-unknown-linux-android\")\n      # remove API version\n      \"aarch64-unknown-linux-android\"\n    when .starts_with?(\"x86_64-pc-solaris\")\n      # remove API version\n      \"x86_64-pc-solaris\"\n    else\n      triple\n    end\n  end\n\n  def self.host_cpu_name : String\n    String.new LibLLVM.get_host_cpu_name\n  end\n\n  def self.normalize_triple(triple : String) : String\n    normalized = LibLLVM.normalize_target_triple(triple)\n    normalized = LLVM.string_and_dispose(normalized)\n\n    normalized\n  end\n\n  def self.to_io(chars, io) : Nil\n    io.write_string Slice.new(chars, LibC.strlen(chars))\n    LibLLVM.dispose_message(chars)\n  end\n\n  def self.string_and_dispose(chars) : String\n    string = String.new(chars)\n    LibLLVM.dispose_message(chars)\n    string\n  end\n\n  def self.parse_command_line_options(options : Enumerable(String), overview : String = \"\") : Nil\n    c_strs = options.to_a(&.to_unsafe)\n    LibLLVM.parse_command_line_options(c_strs.size, c_strs, overview)\n  end\n\n  protected def self.assert(error : LibLLVM::ErrorRef)\n    if error\n      chars = LibLLVM.get_error_message(error)\n      raise String.new(chars).tap { LibLLVM.dispose_error_message(chars) }\n    end\n  end\n\n  {% unless LibLLVM::IS_LT_130 %}\n    def self.run_passes(module mod : Module, passes : String, target_machine : TargetMachine, options : PassBuilderOptions)\n      LibLLVM.run_passes(mod, passes, target_machine, options)\n    end\n  {% end %}\n\n  DEBUG_METADATA_VERSION = 3\nend\n"
  },
  {
    "path": "src/log/backend.cr",
    "content": "require \"crystal/datum\"\n\n# Base class for all backends.\nabstract class Log::Backend\n  property dispatcher : Dispatcher\n\n  def initialize(dispatch_mode : DispatchMode = :async)\n    @dispatcher = Dispatcher.for(dispatch_mode)\n  end\n\n  def initialize(@dispatcher : Dispatcher)\n  end\n\n  # Writes the *entry* to this backend.\n  abstract def write(entry : Entry)\n\n  # Closes underlying resources used by this backend.\n  def close : Nil\n    @dispatcher.close\n  end\n\n  # :nodoc:\n  def dispatch(entry : Entry) : Nil\n    @dispatcher.dispatch entry, self\n  end\nend\n"
  },
  {
    "path": "src/log/broadcast_backend.cr",
    "content": "# A backend that broadcast to others backends.\n# Each of the referenced backends may have a different severity level filter.\n#\n# When this backend level is set that level setting takes precedence\n# over the severity filter of each referenced backend.\n#\n# This backend is not to be used explicitly. It is used by `Log::Builder` configuration\n# to allow a given source to emit to multiple backends.\nclass Log::BroadcastBackend < Log::Backend\n  property level : Severity? = nil\n\n  @backends = Hash(Log::Backend, Severity).new\n\n  def initialize\n    super(:direct)\n  end\n\n  def append(backend : Log::Backend, level : Severity) : Nil\n    @backends[backend] = level\n  end\n\n  def write(entry : Entry) : Nil\n    @backends.each do |backend, level|\n      backend.dispatch(entry) if (@level || level) <= entry.severity\n    end\n  end\n\n  def close : Nil\n    @backends.each_key &.close\n  end\n\n  # :nodoc:\n  def min_level : Severity\n    @backends.each_value.min? || Severity::None\n  end\n\n  # :nodoc:\n  def single_backend? : Tuple(Log::Backend, Log::Severity)?\n    if @backends.size == 1\n      @backends.first\n    end\n  end\n\n  # :nodoc:\n  def remove(backend : Log::Backend) : Nil\n    @backends.delete(backend)\n  end\nend\n"
  },
  {
    "path": "src/log/builder.cr",
    "content": "require \"weak_ref\"\n\n# Used in `Log.setup` methods to configure the binding to be used.\nmodule Log::Configuration\n  # Binds a *source* pattern to a *backend* for all logs that are of severity equal or higher to *level*.\n  abstract def bind(source : String, level : Severity, backend : Backend)\nend\n\n# A `Log::Builder` creates `Log` instances for a given source.\n# It allows you to bind sources and patterns to a given backend.\n# Already created `Log` will be reconfigured as needed.\nclass Log::Builder\n  include Configuration\n\n  @mutex = Mutex.new(:unchecked)\n  @logs = Hash(String, WeakRef(Log)).new\n\n  private record Binding, source : String, level : Severity, backend : Backend\n  @bindings = Array(Binding).new\n\n  # Binds a *source* pattern to a *backend* for all logs that are of severity equal or higher to *level*.\n  def bind(source : String, level : Severity, backend : Backend) : Nil\n    # TODO validate source is a valid path\n\n    @mutex.synchronize do\n      binding = Binding.new(source: source, level: level, backend: backend)\n      @bindings << binding\n\n      each_log do |log|\n        if Builder.matches(log.source, binding.source)\n          append_backend(log, binding.level, binding.backend)\n        end\n      end\n    end\n  end\n\n  # :nodoc:\n  # Removes an existing bind. It assumes there was a single bind with that backend.\n  def unbind(source : String, level : Severity, backend : Backend) : Nil\n    @mutex.synchronize do\n      binding = Binding.new(source: source, level: level, backend: backend)\n      @bindings.delete(binding) || raise ArgumentError.new(\"Non-existing binding #{source}, #{level}, #{backend}\")\n\n      each_log do |log|\n        if Builder.matches(log.source, binding.source)\n          remove_backend(log, binding.backend)\n        end\n      end\n    end\n  end\n\n  # Removes all existing bindings.\n  def clear : Nil\n    @mutex.synchronize do\n      @bindings.clear\n      each_log do |log|\n        log.backend = nil\n        log.initial_level = :none\n      end\n    end\n  end\n\n  # Returns a `Log` for the given *source* with a severity level and\n  # backend according to the bindings in `self`.\n  # If new bindings are applied, the existing `Log` instances will be\n  # reconfigured.\n  # Calling this method multiple times with the same value will return\n  # the same object.\n  def for(source : String) : Log\n    @mutex.synchronize do\n      log = @logs[source]?.try &.value\n\n      if log.nil?\n        log = Log.new(source, nil, :none)\n        @bindings.each do |binding|\n          next unless Builder.matches(log.source, binding.source)\n          append_backend(log, binding.level, binding.backend)\n        end\n        @logs[source] = WeakRef.new(log)\n      end\n\n      log\n    end\n  end\n\n  # :nodoc:\n  private def each_log(&)\n    @logs.reject! { |_, log_ref| log_ref.value.nil? }\n\n    @logs.each_value do |log_ref|\n      log = log_ref.value\n      yield log if log\n    end\n  end\n\n  # :nodoc:\n  private def append_backend(log : Log, level : Severity, backend : Backend)\n    current_backend = log.backend\n    case current_backend\n    when Nil\n      log.backend = backend\n      log.initial_level = level\n    when backend\n      # if the bind applies for the same backend, the last applied\n      # level should be used\n      log.initial_level = level\n    else\n      broadcast = current_backend.as?(BroadcastBackend)\n      # If the current backend is not a broadcast backend , we need to\n      # auto-create a broadcast backend for distributing the different log backends.\n      # A broadcast backend explicitly added as a binding, must not be mutated,\n      # so that requires to create a new one as well.\n      if !broadcast || @bindings.any? { |binding| binding.backend.same?(current_backend) }\n        broadcast = BroadcastBackend.new\n        broadcast.append(current_backend, log.initial_level)\n        log.backend = broadcast\n      end\n      broadcast.append(backend, level)\n      broadcast.level = log.changed_level\n      log.initial_level = broadcast.min_level\n    end\n  end\n\n  # :nodoc:\n  private def remove_backend(log : Log, backend : Backend)\n    current_backend = log.backend\n    case current_backend\n    when Nil\n      raise ArgumentError.new(\"Trying to remove backend of a log without one\")\n    when BroadcastBackend\n      current_backend.remove(backend)\n      if (single_backend = current_backend.single_backend?)\n        log.backend = single_backend[0]\n        log.initial_level = single_backend[1]\n      else\n        log.initial_level = current_backend.min_level\n      end\n    else\n      log.backend = nil\n      log.initial_level = :none\n    end\n  end\n\n  # :nodoc:\n  def close : Nil\n    @bindings.each &.backend.close\n  end\n\n  # :nodoc:\n  def self.matches(source : String, pattern : String) : Bool\n    return true if source == pattern\n    return true if pattern == \"*\"\n    if prefix = pattern.rchop?(\".*\")\n      return true if source == prefix\n      # do not match foobar with foo.*\n      return true if source.starts_with?(prefix) && source[prefix.size] == '.'\n    end\n    false\n  end\n\n  # NOTE: workaround for https://github.com/crystal-lang/crystal/pull/14473\n  protected def cleanup_collected_log(log : Log) : Nil\n    ref = @logs.fetch(log.source) { return }\n    @logs.delete(log.source) if ref.value.nil? || ref.value == log\n  end\nend\n"
  },
  {
    "path": "src/log/dispatch.cr",
    "content": "class Log\n  # Base interface implemented by log entry dispatchers\n  #\n  # Dispatchers are in charge of sending log entries according\n  # to different strategies.\n  module Dispatcher\n    alias Spec = Dispatcher | DispatchMode\n\n    # Dispatch a log entry to the specified backend\n    abstract def dispatch(entry : Entry, backend : Backend)\n\n    # Close the dispatcher, releasing resources\n    def close\n    end\n\n    # :nodoc:\n    def self.for(mode : DispatchMode) : self\n      case mode\n      in .sync?\n        SyncDispatcher.new\n      in .async?\n        AsyncDispatcher.new\n      in .direct?\n        DirectDispatcher\n      end\n    end\n  end\n\n  enum DispatchMode\n    Sync\n    Async\n    Direct\n  end\n\n  # Stateless dispatcher that deliver log entries immediately\n  module DirectDispatcher\n    extend Dispatcher\n\n    def self.dispatch(entry : Entry, backend : Backend) : Nil\n      backend.write(entry)\n    end\n  end\n\n  # Deliver log entries asynchronously through a channels\n  class AsyncDispatcher\n    include Dispatcher\n\n    def initialize(buffer_size = 2048)\n      @channel = Channel({Entry, Backend}).new(buffer_size)\n      @done = Channel(Nil).new\n      spawn write_logs\n    end\n\n    def dispatch(entry : Entry, backend : Backend) : Nil\n      @channel.send({entry, backend})\n    end\n\n    private def write_logs\n      while msg = @channel.receive?\n        entry, backend = msg\n        backend.write(entry)\n      end\n\n      @done.send nil\n    end\n\n    def close : Nil\n      # TODO: this might fail if being closed from different threads\n      unless @channel.closed?\n        @channel.close\n        @done.receive\n      end\n    end\n\n    def finalize : Nil\n      close\n    end\n  end\n\n  # Deliver log entries directly. It uses a mutex to guarantee\n  # one entry is delivered at a time.\n  class SyncDispatcher\n    include Dispatcher\n\n    def initialize\n      @mutex = Mutex.new(:unchecked)\n    end\n\n    def dispatch(entry : Entry, backend : Backend)\n      @mutex.synchronize do\n        backend.write(entry)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/log/entry.cr",
    "content": "# A logging severity level.\nenum Log::Severity\n  # Used for tracing the code and trying to find one part of a function specifically.\n  Trace\n  # Used for information that is diagnostically helpful to people more than just developers (IT, sysadmins, etc.).\n  Debug\n  # Used for generally useful information to log.\n  Info\n  # Used for normal but significant conditions.\n  Notice\n  # Used for conditions that can potentially cause application oddities, but that can be automatically recovered.\n  Warn\n  # Used for any error that is fatal to the operation, but not to the service or application.\n  Error\n  # Used for any error that is forcing a shutdown of the service or application\n  Fatal\n  # Used only for severity level filter.\n  None\n\n  def label : String\n    case self\n    in Trace  then \"TRACE\"\n    in Debug  then \"DEBUG\"\n    in Info   then \"INFO\"\n    in Notice then \"NOTICE\"\n    in Warn   then \"WARN\"\n    in Error  then \"ERROR\"\n    in Fatal  then \"FATAL\"\n    in None   then \"NONE\"\n    end\n  end\nend\n\nstruct Log::Entry\n  getter source : String\n  getter severity : Severity\n  getter message : String\n  getter timestamp : Time\n  getter context : Metadata = Log.context.metadata\n  getter data : Metadata\n  getter exception : Exception?\n\n  def initialize(@source : String, @severity : Severity, @message : String, @data : Log::Metadata, @exception : Exception?, *, @timestamp = Time.local)\n  end\nend\n"
  },
  {
    "path": "src/log/format.cr",
    "content": "class Log\n  # The program name used for log entries\n  #\n  # Defaults to the executable name\n  class_property progname = File.basename(PROGRAM_NAME)\n\n  # The current process PID\n  protected class_getter pid : String = Process.pid.to_s\n\n  # Base interface to convert log entries and write them to an `IO`\n  module Formatter\n    # Writes a `Log::Entry` through an `IO`\n    abstract def format(entry : Log::Entry, io : IO)\n\n    # Creates an instance of a `Log::Formatter` that calls\n    # the specified `Proc` for every entry\n    def self.new(&proc : (Log::Entry, IO) ->) : self\n      ProcFormatter.new proc\n    end\n  end\n\n  # :nodoc:\n  private struct ProcFormatter\n    include Formatter\n\n    def initialize(@proc : (Log::Entry, IO) ->)\n    end\n\n    def format(entry : Log::Entry, io : IO) : Nil\n      @proc.call(entry, io)\n    end\n  end\n\n  # Base implementation of `Log::Formatter` to convert\n  # log entries into text representation\n  #\n  # This can be used to create efficient formatters:\n  # ```\n  # require \"log\"\n  #\n  # struct MyFormat < Log::StaticFormatter\n  #   def run\n  #     string \"- \"\n  #     severity\n  #     string \": \"\n  #     message\n  #   end\n  # end\n  #\n  # Log.setup(:info, Log::IOBackend.new(formatter: MyFormat))\n  # Log.info { \"Hello\" }    # => -   INFO: Hello\n  # Log.error { \"Oh, no!\" } # => -  ERROR: Oh, no!\n  # ```\n  #\n  # There is also a helper macro to generate these formatters. Here's\n  # an example that generates the same result:\n  # ```\n  # Log.define_formatter MyFormat, \"- #{severity}: #{message}\"\n  # ```\n  abstract struct StaticFormatter\n    extend Formatter\n\n    def initialize(@entry : Log::Entry, @io : IO)\n    end\n\n    # Write the entry timestamp in RFC3339 format\n    def timestamp : Nil\n      @entry.timestamp.to_rfc3339(@io, fraction_digits: 6)\n    end\n\n    # Write a fixed string\n    def string(str : _) : Nil\n      @io << str\n    end\n\n    # Write the message of the entry\n    def message : Nil\n      @io << @entry.message\n    end\n\n    # Write the severity\n    #\n    # This writes the severity in uppercase and left padded\n    # with enough space so all the severities fit\n    def severity : Nil\n      @entry.severity.label.rjust(@io, 6)\n    end\n\n    # Write the source for non-root entries\n    #\n    # It doesn't write any output for entries generated from the root logger.\n    # Parameters `before` and `after` can be provided to be written around\n    # the value.\n    # ```\n    # Log.define_formatter TestFormatter, \"#{source(before: '[', after: \"] \")}#{message}\"\n    # Log.setup(:info, Log::IOBackend.new(formatter: TestFormatter))\n    # Log.for(\"foo.bar\").info { \"Hello\" } # => - [foo.bar] Hello\n    # ```\n    def source(*, before : _ = nil, after : _ = nil) : Nil\n      if @entry.source.size > 0\n        @io << before << @entry.source << after\n      end\n    end\n\n    # Write all the values from the entry data\n    #\n    # It doesn't write any output if the entry data is empty.\n    # Parameters `before` and `after` can be provided to be written around\n    # the value.\n    def data(*, before : _ = nil, after : _ = nil) : Nil\n      unless @entry.data.empty?\n        @io << before << @entry.data << after\n      end\n    end\n\n    # Write all the values from the context\n    #\n    # It doesn't write any output if the context is empty.\n    # Parameters `before` and `after` can be provided to be written around\n    # the value.\n    def context(*, before : _ = nil, after : _ = nil) : Nil\n      unless @entry.context.empty?\n        @io << before << @entry.context << after\n      end\n    end\n\n    # Write the exception, including backtrace\n    #\n    # It doesn't write any output unless there is an exception in the entry.\n    # Parameters `before` and `after` can be provided to be written around\n    # the value. `before` defaults to `'\\n'` so the exception is written\n    # on a separate line\n    def exception(*, before : _ = '\\n', after : _ = nil) : Nil\n      if ex = @entry.exception\n        @io << before\n        ex.inspect_with_backtrace(@io)\n        @io << after\n      end\n    end\n\n    # Write the program name. See `Log.progname`.\n    def progname : Nil\n      @io << Log.progname\n    end\n\n    # Write the current process identifier\n    def pid(*, before = '#', after = nil)\n      @io << before << Log.pid << after\n    end\n\n    # Write the `Log::Entry` to the `IO` using this pattern\n    def self.format(entry : Log::Entry, io : IO) : Nil\n      new(entry, io).run\n    end\n\n    # Subclasses must implement this method to define the output pattern\n    abstract def run\n  end\n\n  # Generate subclasses of `Log::StaticFormatter` from a string with interpolations\n  #\n  # Example:\n  # ```\n  # Log.define_formatter MyFormat, \"- #{severity}: #{message}\"\n  # ```\n  # See `Log::StaticFormatter` for the available methods that can\n  # be called within the interpolations.\n  macro define_formatter(name, pattern)\n    struct {{name}} < ::Log::StaticFormatter\n      def run\n        {% for part in pattern.expressions %}\n          {% if part.is_a?(StringLiteral) %}\n            string {{ part }}\n          {% else %}\n            {{ part }}\n          {% end %}\n        {% end %}\n      end\n    end\n  end\nend\n\n# Default short format\n#\n# It writes log entries with the following format:\n# ```\n# 2020-05-07T17:40:07.994508000Z   INFO - my.source: Initializing everything\n# ```\n#\n# When the entries have context data it's also written to the output:\n# ```\n# 2020-05-07T17:40:07.994508000Z   INFO - my.source: Initializing everything -- {\"data\" => 123}\n# ```\n#\n# Exceptions are written in a separate line:\n# ```\n# 2020-05-07T17:40:07.994508000Z  ERROR - my.source: Something failed\n# Oh, no (Exception)\n#   from ...\n# ```\nLog.define_formatter Log::ShortFormat, \"#{timestamp} #{severity} - #{source(after: \": \")}#{message}\" \\\n                                       \"#{data(before: \" -- \")}#{context(before: \" -- \")}#{exception}\"\n"
  },
  {
    "path": "src/log/io_backend.cr",
    "content": "# A `Log::Backend` that emits to an `IO` (defaults to STDOUT).\nclass Log::IOBackend < Log::Backend\n  property io : IO\n  property formatter : Formatter\n\n  {% if flag?(:wasm32) %}\n    # TODO: this constructor must go away once channels are fixed in Windows / WebAssembly\n    def initialize(@io = STDOUT, *, @formatter : Formatter = ShortFormat, dispatcher : Dispatcher::Spec = DispatchMode::Sync)\n      super(dispatcher)\n    end\n  {% else %}\n    def initialize(@io = STDOUT, *, @formatter : Formatter = ShortFormat, dispatcher : Dispatcher::Spec = DispatchMode::Async)\n      super(dispatcher)\n    end\n  {% end %}\n\n  def write(entry : Entry) : Nil\n    format(entry)\n    io.puts\n    io.flush\n  end\n\n  # Emits the *entry* to the given *io*.\n  # It uses the `#formatter` to convert.\n  def format(entry : Entry) : Nil\n    @formatter.format(entry, io)\n  end\nend\n"
  },
  {
    "path": "src/log/json.cr",
    "content": "require \"json\"\n\nclass Log::Metadata\n  # Returns `Log::Metadata` as JSON value.\n  #\n  # NOTE: `require \"log/json\"` is required to opt-in to this feature.\n  #\n  # ```\n  # log_entry.context.to_json # => \"{\\\"user_id\\\":1}\"\n  # ```\n  def to_json(builder : JSON::Builder) : Nil\n    builder.object do\n      each do |(key, value)|\n        builder.field key.to_json_object_key do\n          value.to_json(builder)\n        end\n      end\n    end\n  end\n\n  struct Value\n    # Returns `Log::Metadata::Value` as JSON value.\n    #\n    # NOTE: `require \"log/json\"` is required to opt-in to this feature.\n    def to_json(builder : JSON::Builder) : Nil\n      @raw.to_json builder\n    end\n  end\nend\n"
  },
  {
    "path": "src/log/log.cr",
    "content": "class Log\n  getter source : String\n  getter backend : Backend?\n  @level : Severity?\n  # :nodoc:\n  property initial_level : Severity\n\n  # :nodoc:\n  def initialize(@source : String, @backend : Backend?, level : Severity)\n    @initial_level = level\n  end\n\n  def finalize : Nil\n    # NOTE: workaround for https://github.com/crystal-lang/crystal/pull/14473\n    Log.builder.cleanup_collected_log(self)\n  end\n\n  # :nodoc:\n  def changed_level : Severity?\n    @level\n  end\n\n  def level : Severity\n    @level || @initial_level\n  end\n\n  # Change this log severity level filter.\n  def level=(value : Severity) : Log::Severity\n    @level = value\n    if (backend = @backend).responds_to?(:level=)\n      backend.level = value\n    end\n    value\n  end\n\n  # :nodoc:\n  def backend=(value : Backend?) : Backend?\n    @backend = value\n  end\n\n  {% for method in %w(trace debug info notice warn error fatal) %}\n    {% severity = method.id.camelcase %}\n\n    # Logs the given *exception* if the logger's current severity is lower than\n    # or equal to `Severity::{{severity}}`.\n    def {{method.id}}(*, exception : Exception) : Nil\n      severity = Severity::{{severity}}\n      if level <= severity && (backend = @backend)\n        backend.dispatch Emitter.new(@source, severity, exception).emit(\"\")\n      end\n    end\n\n    # Logs a message if the logger's current severity is lower than or equal to\n    # `Severity::{{ severity }}`.\n    #\n    # The block is not called unless the current severity level would emit a\n    # message.\n    #\n    # Blocks which return `nil` do not emit anything:\n    #\n    # ```\n    # Log.{{method.id}} do\n    #   if false\n    #     \"Nothing will be logged.\"\n    #   end\n    # end\n    # ```\n    def {{method.id}}(*, exception : Exception? = nil)\n      severity = Severity::{{severity}}\n      return unless level <= severity\n\n      return unless backend = @backend\n\n      dsl = Emitter.new(@source, severity, exception)\n      result = yield dsl\n\n      unless result.nil? && exception.nil?\n        entry =\n           case result\n           when Entry\n             result\n           else\n             dsl.emit(result.to_s)\n           end\n\n        backend.dispatch entry\n      end\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/log/main.cr",
    "content": "class Log\n  # Creates a `Log` for the given source.\n  # If *level* is given, it will override the configuration.\n  def self.for(source : String, level : Severity? = nil) : Log\n    log = builder.for(source)\n    log.level = level if level\n    log\n  end\n\n  # Creates a `Log` for the given nested source.\n  # If *level* is given, it will override the configuration.\n  def for(child_source : String, level : Severity? = nil) : Log\n    return ::Log.for(child_source) if source.blank?\n    return ::Log.for(source) if child_source.blank?\n    ::Log.for(\"#{source}.#{child_source}\", level)\n  end\n\n  # Creates a `Log` for the given type.\n  # A type `Foo::Bar(Baz)` corresponds to the source `foo.bar`.\n  # If *level* is given, it will override the configuration.\n  def self.for(type : Class, level : Severity? = nil) : Log\n    source = type.name.underscore.gsub(\"::\", \".\")\n\n    # remove generic arguments\n    paren = source.index('(')\n    source = source[0...paren] if paren\n\n    ::Log.for(source, level)\n  end\n\n  # :ditto:\n  def for(type : Class, level : Severity? = nil) : Log\n    ::Log.for(type, level)\n  end\n\n  private Top = Log.for(\"\")\n\n  {% for method in %i(trace debug info notice warn error fatal) %}\n    # See `Log#{{method.id}}`.\n    def self.{{method.id}}(*, exception : Exception) : Nil\n      Top.{{method.id}}(exception: exception)\n    end\n\n    # See `Log#{{method.id}}`.\n    def self.{{method.id}}(*, exception : Exception? = nil)\n      Top.{{method.id}}(exception: exception) do |dsl|\n        yield dsl\n      end\n    end\n  {% end %}\n\n  @@builder = Builder.new\n\n  at_exit { @@builder.close }\n\n  # Returns the default `Log::Builder` used for `Log.for` calls.\n  def self.builder : Log::Builder\n    @@builder\n  end\n\n  # Returns the current fiber logging context.\n  def self.context : Log::Context\n    Log::Context.new(Fiber.current.logging_context)\n  end\n\n  # Sets the current fiber logging context.\n  def self.context=(value : Log::Metadata) : Log::Metadata\n    Fiber.current.logging_context = value\n  end\n\n  # :ditto:\n  def self.context=(value : Log::Context) : Log::Metadata\n    # NOTE: There is a need for `Metadata` and `Context` setters in\n    # because `Log.context` returns a `Log::Context` for allowing DSL like `Log.context.set(a: 1)`\n    # but if the metadata is built manually the construct `Log.context = metadata` will be used.\n    Log.context = value.metadata\n  end\n\n  # Returns the current fiber logging context.\n  def context : Log::Context\n    Log.context\n  end\n\n  # Sets the current fiber logging context.\n  def context=(value : Log::Metadata | Log::Context)\n    Log.context = value\n  end\n\n  # Method to save and restore the current logging context.\n  # Temporary context for the duration of the block can be set via arguments.\n  #\n  # ```\n  # Log.context.set a: 1\n  # Log.info { %(message with {\"a\" => 1} context) }\n  # Log.with_context(b: 2) do\n  #   Log.context.set c: 3\n  #   Log.info { %(message with {\"a\" => 1, \"b\" => 2, \"c\" => 3} context) }\n  # end\n  # Log.info { %(message with {\"a\" => 1} context) }\n  # ```\n  def self.with_context(**kwargs, &)\n    previous = Log.context\n    Log.context.set(**kwargs) unless kwargs.empty?\n    begin\n      yield\n    ensure\n      Log.context = previous\n    end\n  end\n\n  # :ditto:\n  def self.with_context(values, &)\n    previous = Log.context\n    Log.context.set(values) unless values.empty?\n    begin\n      yield\n    ensure\n      Log.context = previous\n    end\n  end\n\n  # :ditto:\n  def with_context(**kwargs, &)\n    self.class.with_context(**kwargs) do\n      yield\n    end\n  end\n\n  # :ditto:\n  def with_context(values, &)\n    self.class.with_context(values) do\n      yield\n    end\n  end\n\n  struct Context\n    getter metadata : Metadata\n\n    def initialize(@metadata : Metadata)\n    end\n\n    # Clears the current `Fiber` logging context.\n    #\n    # ```\n    # Log.context.clear\n    # Log.info { \"message with empty context\" }\n    # ```\n    def clear : Nil\n      Fiber.current.logging_context = @metadata = Log::Metadata.empty\n    end\n\n    # Extends the current `Fiber` logging context.\n    #\n    # ```\n    # Log.context.set a: 1\n    # Log.context.set b: 2\n    # Log.info { %q(message with a: 1, b: 2 context\") }\n    # h = {:c => \"3\"}\n    # Log.context.set extra: h\n    # Log.info { %q(message with a: 1, b: 2, extra: {\"c\" => \"3\"} context) }\n    # h = {\"c\" => 3}\n    # Log.context.set extra: h\n    # Log.info { %q(message with a: 1, b: 2, extra: {\"c\" => 3} context) }\n    # ```\n    def set(**kwargs) : Log::Metadata\n      extend_fiber_context(Fiber.current, kwargs)\n    end\n\n    # :ditto:\n    def set(values : Hash | NamedTuple) : Nil\n      extend_fiber_context(Fiber.current, values)\n    end\n\n    private def extend_fiber_context(fiber : Fiber, values)\n      context = fiber.logging_context\n      fiber.logging_context = @metadata = context.extend(values)\n    end\n  end\n\n  # Helper DSL module for emitting log entries with data.\n  struct Emitter\n    # :nodoc:\n    def initialize(@source : String, @severity : Severity, @exception : Exception?)\n    end\n\n    # Emits a logs entry with a message, and data attached to\n    #\n    # ```\n    # Log.info &.emit(\"Program started\")                         # No data, same as Log.info { \"Program started\" }\n    # Log.info &.emit(\"User logged in\", user_id: 42)             # With entry data\n    # Log.info &.emit(action: \"Logged in\", user_id: 42)          # Empty string message, only data\n    # Log.error exception: ex, &.emit(\"Oops\", account: {id: 42}) # With data and exception\n    # ```\n    def emit(message : String) : Entry\n      emit(message, Metadata.empty)\n    end\n\n    def emit(message : String, **kwargs) : Entry\n      emit(message, kwargs)\n    end\n\n    def emit(message : String, data : Metadata | Hash | NamedTuple) : Entry\n      Entry.new(@source, @severity, message, Metadata.build(data), @exception)\n    end\n\n    def emit(**kwargs) : Entry\n      emit(kwargs)\n    end\n\n    def emit(data : Metadata | Hash | NamedTuple) : Entry\n      emit(\"\", Metadata.build(data))\n    end\n  end\nend\n"
  },
  {
    "path": "src/log/memory_backend.cr",
    "content": "# A `Log::Backend` suitable for testing.\n#\nclass Log::MemoryBackend < Log::Backend\n  getter entries = Array(Log::Entry).new\n\n  def initialize\n    super(:direct)\n  end\n\n  def write(entry : Log::Entry) : Nil\n    @entries << entry\n  end\nend\n"
  },
  {
    "path": "src/log/metadata.cr",
    "content": "# Immutable structured metadata information for logging.\n#\n# See `Log.context`, `Log.context=`, `Log::Context#clear`, `Log::Context#set`, `Log.with_context`, and `Log::Emitter`.\n#\n# NOTE: If you'd like to format the context as JSON, remember to `require \"log/json\"`.\nclass Log::Metadata\n  struct Value; end\n\n  include Enumerable({Symbol, Log::Metadata::Value})\n  alias Entry = {key: Symbol, value: Value}\n\n  # Returns an empty `Log::Metadata`.\n  #\n  # NOTE: Since `Log::Metadata` is immutable, it's safe to share this instance.\n  class_getter empty : Log::Metadata = Log::Metadata.new\n\n  @parent : Metadata?\n  # The maximum size this metadata would need.\n  # Initially is the parent.max_total_size + entries.size .\n  # When the metadata is defragmented max_total_size will be updated with size\n  protected getter max_total_size : Int32\n  @max_total_size = uninitialized Int32\n  # How many entries are stored from @first.\n  # When the metadata is defragmented @size will be increased up to\n  # the actual number of entries resulting from merging the parent\n  @size = uninitialized Int32\n  # Number of parent elements we've copied on defrag. Used to iterate parent\n  # entries first in #each.\n  @parent_size = uninitialized Int32\n\n  # @first needs to be the last ivar of Metadata. The entries are allocated together with self\n  @first = uninitialized Entry\n\n  def self.new(parent : Metadata? = nil, entries : NamedTuple | Hash = NamedTuple.new) : self\n    data_size = instance_sizeof(self) + sizeof(Entry) * {entries.size + (parent.try(&.max_total_size) || 0) - 1, 0}.max\n    data = GC.malloc(data_size).as(self)\n    data.setup(parent, entries)\n    data\n  end\n\n  def dup : self\n    self\n  end\n\n  protected def setup(@parent : Metadata?, entries : NamedTuple | Hash)\n    @size = entries.size\n    @parent_size = 0\n    @max_total_size = @size + (@parent.try(&.max_total_size) || 0)\n    ptr_entries = pointerof(@first)\n\n    if entries.is_a?(NamedTuple)\n      entries.each_with_index do |key, value, i|\n        ptr_entries[i] = {key: key, value: Value.to_metadata_value(value)}\n      end\n    else\n      entries.each_with_index do |(key, value), i|\n        ptr_entries[i] = {key: key, value: Value.to_metadata_value(value)}\n      end\n    end\n  end\n\n  # Returns a `Metadata` with the information of the argument.\n  # Used to handle `Log::Context#set` and `Log#Emitter.emit` overloads.\n  def self.build(value : NamedTuple | Hash) : self\n    return @@empty if value.empty?\n    Metadata.new(nil, value)\n  end\n\n  # :ditto:\n  def self.build(value : Metadata) : Metadata\n    value\n  end\n\n  # Returns a `Log::Metadata` with all the entries of `self`\n  # and *other*. If a key is defined in both, the values in *other* are used.\n  def extend(other : NamedTuple | Hash) : Metadata\n    return Metadata.build(other) if self.empty?\n    return self if other.empty?\n\n    Metadata.new(self, other)\n  end\n\n  def empty? : Bool\n    parent = @parent\n\n    @size == 0 && (parent.nil? || parent.empty?)\n  end\n\n  # Removes the reference to *parent*. Flattening the entries from it into `self`.\n  # `self` was originally allocated with enough entries to perform this action.\n  #\n  # If multiple threads execute defrag concurrently, the entries\n  # will be recomputed, but the result should be the same.\n  #\n  # * @parent.nil? signals if the defrag is needed/done\n  # * The value of pointerof(@first) never changes\n  # * @parent is set at the very end of the method\n  protected def defrag\n    parent = @parent\n    return if parent.nil?\n\n    ptr_entries = pointerof(@first)\n    next_free_entry = ptr_entries + @size\n    total_size = @size\n\n    # Copy parent entries that ain't overwritten\n    parent_size = 0\n    parent.each do |(key, value)|\n      overwritten = Slice.new(ptr_entries, @size).any? { |entry| entry[:key] == key }\n      next if overwritten\n      next_free_entry.value = {key: key, value: value}\n      parent_size += 1\n      next_free_entry += 1\n      total_size += 1\n    end\n\n    @size = total_size\n    @max_total_size = total_size\n    @parent_size = parent_size\n    @parent = nil\n  end\n\n  def each(& : {Symbol, Value} ->)\n    defrag\n    ptr_entries = pointerof(@first)\n    parent_size = @parent_size\n    local_size = @size - parent_size\n\n    Slice.new(ptr_entries + local_size, parent_size).each do |entry|\n      yield({entry[:key], entry[:value]})\n    end\n\n    Slice.new(ptr_entries, local_size).each do |entry|\n      yield({entry[:key], entry[:value]})\n    end\n  end\n\n  def [](key : Symbol) : Value\n    fetch(key) { raise KeyError.new \"Missing metadata key: #{key.inspect}\" }\n  end\n\n  def []?(key : Symbol) : Value?\n    fetch(key) { nil }\n  end\n\n  def fetch(key, &)\n    entry = find_entry(key)\n    entry ? entry[:value] : yield key\n  end\n\n  protected def find_entry(key) : Entry?\n    # checking the @parent before @size ensures that if other\n    # thread is doing defrag, the results will be consistent\n    # without locking.\n\n    parent = @parent\n\n    ptr_entries = pointerof(@first)\n    @size.times do |i|\n      return ptr_entries[i] if ptr_entries[i][:key] == key\n    end\n\n    return parent.find_entry(key) if parent\n\n    nil\n  end\n\n  def ==(other : Metadata) : Bool\n    self_kv = self.to_a\n    other_kv = other.to_a\n\n    return false if self_kv.size != other_kv.size\n\n    # sort kv tuples by key\n    self_kv.sort_by!(&.[0])\n    other_kv.sort_by!(&.[0])\n\n    self_kv.each_with_index do |(key, value), i|\n      return false unless key == other_kv[i][0] && value == other_kv[i][1]\n    end\n\n    true\n  end\n\n  def ==(other)\n    false\n  end\n\n  def hash(hasher)\n    to_a.sort_by!(&.[0]).hash(hasher)\n  end\n\n  def to_s(io : IO) : Nil\n    found_one = false\n    each do |(key, value)|\n      io << \", \" if found_one\n      io << key\n      io << \": \"\n      value.inspect(io)\n      found_one = true\n    end\n  end\n\n  struct Value\n    Crystal.datum types: {nil: Nil, bool: Bool, i: Int32, i64: Int64, u: UInt32, u64: UInt64, f: Float32, f64: Float64, s: String, time: Time}, hash_key_type: String, immutable: false, target_type: Log::Metadata::Value\n\n    # Creates `Log::Metadata` from the given *values*.\n    # All keys are converted to `String`\n    def initialize(hash : NamedTuple | Hash)\n      @raw = raw = Hash(String, Value).new\n      hash.each do |key, value|\n        raw[key.to_s] = Value.to_metadata_value(value)\n      end\n    end\n\n    # :nodoc:\n    def initialize(ary : Array)\n      @raw = ary.map { |e| Value.to_metadata_value(e) }\n    end\n\n    # :nodoc:\n    def self.to_metadata_value(value : _) : Metadata::Value\n      value.is_a?(Value) ? value : Value.new(value)\n    end\n  end\nend\n\nclass Fiber\n  # :nodoc:\n  getter logging_context : Log::Metadata { Log::Metadata.empty }\n\n  # :nodoc:\n  def logging_context=(value : Log::Metadata) : Log::Metadata\n    @logging_context = value\n  end\nend\n\nclass String\n  def ==(other : Log::Metadata::Value)\n    other == self\n  end\nend\n\nstruct Value\n  def ==(other : Log::Metadata::Value)\n    other == self\n  end\nend\n"
  },
  {
    "path": "src/log/setup.cr",
    "content": "class Log\n  # Setups logging bindings discarding all previous configurations.\n  def self.setup(*, builder : Log::Builder = Log.builder, &)\n    builder.clear\n    yield builder.as(Configuration)\n  end\n\n  # Setups logging for all sources using the specified *level*, *backend*.\n  def self.setup(level : Log::Severity = Log::Severity::Info,\n                 backend : Log::Backend = IOBackend.new,\n                 *, builder : Log::Builder = Log.builder)\n    Log.setup(\"*\", level, backend, builder: builder)\n  end\n\n  # Setups logging for *sources* using the specified *level*, *backend*.\n  def self.setup(sources : String = \"*\",\n                 level : Log::Severity = Log::Severity::Info,\n                 backend : Log::Backend = IOBackend.new,\n                 *, builder : Log::Builder = Log.builder) : Nil\n    Log.setup(builder: builder) do |c|\n      sources.split(',', remove_empty: false) do |source|\n        source = source.strip\n\n        c.bind(source, level, backend)\n      end\n    end\n  end\n\n  # Setups logging based on `LOG_LEVEL` environment variable.\n  def self.setup_from_env(*, builder : Log::Builder = Log.builder,\n                          default_level : Log::Severity = Log::Severity::Info,\n                          default_sources : String = \"*\",\n                          log_level_env : String = \"LOG_LEVEL\",\n                          backend : Log::Backend = Log::IOBackend.new)\n    level = ENV[log_level_env]?.presence.try { |v| Log::Severity.parse(v) } || default_level\n\n    Log.setup(default_sources, level, backend, builder: builder)\n  end\nend\n"
  },
  {
    "path": "src/log/spec.cr",
    "content": "require \"spec/dsl\"\n\nclass Log\n  # Returns and yields an `EntriesChecker` that allows checking specific log entries\n  # were emitted.\n  #\n  # This capture will even work if there are currently no backends configured, effectively\n  # adding a temporary backend.\n  #\n  # ```\n  # require \"spec\"\n  # require \"log\"\n  # require \"log/spec\"\n  #\n  # Log.setup(:none)\n  #\n  # def greet(name)\n  #   Log.info { \"Greeting #{name}\" }\n  # end\n  #\n  # it \"greets\" do\n  #   Log.capture do |logs|\n  #     greet(\"Harry\")\n  #     greet(\"Hermione\")\n  #     greet(\"Ron\")\n  #\n  #     logs.check(:info, /greeting harry/i)\n  #     logs.next(:info, /greeting hermione/i)\n  #   end\n  # end\n  # ```\n  #\n  # By default logs of all sources and severities  will be captured.\n  #\n  # Use *level* to only capture of the given severity or above.\n  #\n  # Use *source* to narrow which source are captured. Values that represent single pattern like `http.*` are allowed.\n  #\n  # The `EntriesChecker` will hold a list of emitted entries.\n  #\n  # `EntriesChecker#check` will find the next entry which matches the level and message.\n  # `EntriesChecker#next` will validate that the following entry in the list matches the given level and message.\n  # `EntriesChecker#clear` will clear the emitted and captured entries.\n  #\n  # With these methods it is possible to express expected traces in either a strict or loose way, while checking ordering.\n  #\n  # `EntriesChecker#entry` returns the last matched `Entry`. Useful to check additional entry properties other than the message.\n  #\n  # `EntriesChecker#empty` validates there are no pending entries to match.\n  #\n  # Using the yielded `EntriesChecker` allows clearing the entries between statements.\n  #\n  # Invocations can be nested in order to capture each source in their own `EntriesChecker`.\n  #\n  def self.capture(source : String = \"*\", level : Severity = Log::Severity::Trace, *, builder = Log.builder, &)\n    mem_backend = Log::MemoryBackend.new\n    builder.bind(source, level, mem_backend)\n    begin\n      dsl = Log::EntriesChecker.new(mem_backend.entries)\n      yield dsl\n      dsl\n    ensure\n      builder.unbind(source, level, mem_backend)\n    end\n  end\n\n  # :ditto:\n  def self.capture(level : Log::Severity = Log::Severity::Trace,\n                   *, builder : Log::Builder = Log.builder, &)\n    capture(\"*\", level, builder: builder) do |dsl|\n      yield dsl\n    end\n  end\n\n  # DSL for `Log.capture`\n  class EntriesChecker\n    def initialize(@entries : Array(Log::Entry))\n    end\n\n    # Returns the last entry matched by `#check` or `#next`\n    getter! entry : Entry\n\n    # :nodoc:\n    def check(description, file = __FILE__, line = __LINE__, & : Entry -> Bool) : self\n      fail(\"No entries found, expected #{description}\", file, line) if @entries.empty?\n      original_size = @entries.size\n\n      while entry = @entries.shift?\n        matches = yield entry\n        if matches\n          @entry = entry\n          return self\n        end\n      end\n\n      fail(\"No matching entries found, expected #{description}, skipped (#{original_size})\", file, line)\n    end\n\n    # Validates that at some point the indicated entry was emitted\n    def check(level : Severity, message : String, file : String = __FILE__, line : Int32 = __LINE__) : self\n      self.check(\"#{level} with #{message.inspect}\", file, line) { |e| e.severity == level && e.message == message }\n    end\n\n    # :ditto:\n    def check(level : Severity, pattern : Regex, file : String = __FILE__, line : Int32 = __LINE__, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : self\n      self.check(\"#{level} matching #{pattern.inspect}\", file, line) { |e| e.severity == level && e.message.matches?(pattern, options: options) }\n    end\n\n    # :nodoc:\n    def next(description, file = __FILE__, line = __LINE__, & : Entry -> Bool) : self\n      if entry = @entries.shift?\n        matches = yield entry\n        if matches\n          @entry = entry\n          self\n        else\n          fail(\"No matching entries found, expected #{description}, but got #{entry.severity} with #{entry.message.inspect}\", file, line)\n        end\n      else\n        fail(\"No entries found, expected #{description}\", file, line)\n      end\n    end\n\n    # Validates that the indicated entry was the next one to be emitted\n    def next(level : Severity, message : String, file : String = __FILE__, line : Int32 = __LINE__) : self\n      self.next(\"#{level} with #{message.inspect}\", file, line) { |e| e.severity == level && e.message == message }\n    end\n\n    # :ditto:\n    def next(level : Severity, pattern : Regex, file : String = __FILE__, line : Int32 = __LINE__, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : self\n      self.next(\"#{level} matching #{pattern.inspect}\", file, line) { |e| e.severity == level && e.message.matches?(pattern, options: options) }\n    end\n\n    # Clears the emitted entries so far\n    def clear : self\n      @entry = nil\n      @entries.clear\n      self\n    end\n\n    # Validates that there are no outstanding entries\n    def empty(file : String = __FILE__, line : Int32 = __LINE__) : self\n      @entry = nil\n      if first = @entries.first?\n        fail(\"Expected no entries, but got #{first.severity} with #{first.message.inspect} in a total of #{@entries.size} entries\", file, line)\n      else\n        self\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/log.cr",
    "content": "# The `Log` class provides a logging utility that you can use to output messages.\n#\n# The messages, or `Log::Entry` have associated levels, such as `Info` or `Error`\n# that indicate their importance. See `Log::Severity`.\n#\n# To log a message use the `#trace`, `#debug`, `#info`, `#notice`, `#warn`,\n# `#error`, and `#fatal` methods. They expect a block that will evaluate to the\n# message of the entry:\n#\n# NOTE: To use `Log`, you must explicitly import it with `require \"log\"`\n#\n# ```\n# require \"log\"\n#\n# Log.info { \"Program started\" }\n# ```\n#\n# Data can be associated with a log entry via the `Log::Emitter` yielded in the logging methods.\n#\n# ```\n# Log.info &.emit(\"User logged in\", user_id: 42)\n# ```\n#\n# If you want to log an exception, you can indicate it in the `exception:` named argument.\n#\n# ```\n# Log.warn(exception: e) { \"Oh no!\" }\n# Log.warn exception: e, &.emit(\"Oh no!\", user_id: 42)\n# ```\n#\n# The block is only evaluated if the current message is to be emitted to some `Log::Backend`.\n#\n# To add structured information to the message you can use the `Log::Context`.\n#\n# When creating log messages they belong to a _source_. If the top-level `Log` is used\n# as in the above examples its source is the empty string.\n#\n# The source can be used to identify the module or part of the application that is logging.\n# You can configure for each source a different level to filter the messages.\n#\n# A recommended pattern is to declare a `Log` constant in the namespace of your shard or module as follows:\n#\n# ```\n# module DB\n#   Log = ::Log.for(\"db\") # Log for db source\n#\n#   def do_something\n#     Log.info { \"this is logged in db source\" }\n#   end\n# end\n#\n# DB::Log.info { \"this is also logged in db source\" }\n# Log.for(\"db\").info { \"this is also logged in db source\" }\n# Log.info { \"this is logged in top-level source\" }\n# ```\n#\n# That way, any `Log.info` call within the `DB` module will use the `db` source. And not the top-level `::Log.info`.\n#\n# Sources can be nested. Continuing the last example, to declare a `Log` constant `db.pool` source you can do as follows:\n#\n# ```\n# class DB::Pool\n#   Log = DB::Log.for(\"pool\") # Log for db.pool source\n# end\n# ```\n#\n# A `Log` will emit the messages to the `Log::Backend`s attached to it as long as\n# the configured severity filter `level` permits it.\n#\n# Logs can also be created from a type directly. For the type `DB::Pool` the source `db.pool` will be used.\n# For generic types as `Foo::Bar(Baz)` the source `foo.bar` will be used (i.e. without generic arguments).\n#\n# ```\n# module DB\n#   Log = ::Log.for(self) # Log for db source\n# end\n# ```\n#\n# ### Default logging configuration\n#\n# By default entries from all sources with `Info` and above severity will\n# be logged to `STDOUT` using the `Log::IOBackend`.\n#\n# If you need to change the default level, backend or sources call `Log.setup` upon startup.\n#\n# NOTE: Calling `setup` will override previous `setup` calls.\n#\n# ```\n# Log.setup(:debug)                     # Log debug and above for all sources to STDOUT\n# Log.setup(\"myapp.*, http.*\", :notice) # Log notice and above for myapp.* and http.* sources only, and log nothing for any other source.\n# backend_with_formatter = Log::IOBackend.new(formatter: custom_formatter)\n# Log.setup(:debug, backend_with_formatter) # Log debug and above for all sources to using a custom backend\n# ```\n#\n# ### Configure logging explicitly in the code\n#\n# Use `Log.setup` methods to indicate which sources should go to which backends.\n#\n# You can indicate actual sources or patterns.\n#\n# * the empty string matches only the top-level source\n# * `*` matches all the sources\n# * `foo.bar.*` matches `foo.bar` and every nested source\n# * `foo.bar` matches `foo.bar`, but not its nested sources\n# * Any comma separated combination of the above\n#\n# The following configuration will setup for all sources to emit\n# warnings (or higher) to `STDOUT`, allow any of the `db.*` and\n# nested source to emit debug (or higher), and to also emit for all\n# sources errors (or higher) to an elasticsearch backend.\n#\n# ```\n# Log.setup do |c|\n#   backend = Log::IOBackend.new\n#\n#   c.bind \"*\", :warn, backend\n#   c.bind \"db.*\", :debug, backend\n#   c.bind \"*\", :error, ElasticSearchBackend.new(\"http://localhost:9200\")\n# end\n# ```\n#\n# ### Configure logging from environment variables\n#\n# Include the following line to allow configuration from environment variables.\n#\n# ```\n# Log.setup_from_env\n# ```\n#\n# The environment variable `LOG_LEVEL` is used to indicate which severity level to emit.\n# By default entries from all sources with `Info` and above severity will\n# be logged to `STDOUT` using the `Log::IOBackend`.\n#\n# To change the level and sources change the environment variable value:\n#\n# ```console\n# $ LOG_LEVEL=DEBUG ./bin/app\n# ```\n#\n# You can tweak the default values (used when `LOG_LEVEL` variable is not defined):\n#\n# ```\n# Log.setup_from_env(default_level: :error)\n# ```\n#\nclass Log\nend\n\n# list all files but log/config which requires yaml\nrequire \"./log/backend\"\nrequire \"./log/broadcast_backend\"\nrequire \"./log/builder\"\nrequire \"./log/metadata\"\nrequire \"./log/entry\"\nrequire \"./log/format\"\nrequire \"./log/main\"\nrequire \"./log/setup\"\nrequire \"./log/log\"\nrequire \"./log/memory_backend\"\nrequire \"./log/io_backend\"\nrequire \"./log/dispatch\"\n\nLog.setup\n"
  },
  {
    "path": "src/macros.cr",
    "content": "# Defines a **`Struct`** type called *name* with the given *properties*.\n#\n# The generated struct has a constructor with the given properties\n# in the same order as declared. The struct only provides getters,\n# not setters, making it immutable by default.\n#\n# ```\n# record Point, x : Int32, y : Int32\n#\n# p = Point.new 1, 2 # => #<Point(@x=1, @y=2)>\n# p.x                # => 1\n# p.y                # => 2\n# ```\n#\n# The *properties* are a sequence of type declarations (`x : Int32`, `x : Int32 = 0`)\n# or assigns (`x = 0`).\n# They declare instance variables and respective getter methods of their name with\n# optional type restrictions and default value.\n#\n# When passing a block to this macro its body is inserted inside\n# the struct definition. This allows to define additional methods or include modules\n# into the record type (reopening the type would work as well).\n#\n# ```\n# record Person, first_name : String, last_name : String do\n#   def full_name\n#     \"#{first_name} #{last_name}\"\n#   end\n# end\n#\n# person = Person.new \"John\", \"Doe\"\n# person.full_name # => \"John Doe\"\n# ```\n#\n# An example with type declarations and default values:\n#\n# ```\n# record Point, x : Int32 = 0, y : Int32 = 0\n#\n# Point.new      # => #<Point(@x=0, @y=0)>\n# Point.new y: 2 # => #<Point(@x=0, @y=2)>\n# ```\n#\n# An example with assignments (in this case the compiler must be able to\n# infer the types from the default values):\n#\n# ```\n# record Point, x = 0, y = 0\n#\n# Point.new      # => #<Point(@x=0, @y=0)>\n# Point.new y: 2 # => #<Point(@x=0, @y=2)>\n# ```\n#\n# This macro also provides a `#copy_with` method which returns\n# a copy of the record with the provided properties altered.\n#\n# ```\n# record Point, x = 0, y = 0\n#\n# p = Point.new y: 2 # => #<Point(@x=0, @y=2)>\n# p.copy_with x: 3   # => #<Point(@x=3, @y=2)>\n# p                  # => #<Point(@x=0, @y=2)>\n# ```\nmacro record(__name name, *properties, **kwargs)\n  {% raise <<-TXT unless kwargs.empty?\n    macro `record` does not accept named arguments\n      Did you mean:\n\n      record #{name}, #{(properties + kwargs.map { |name, value| \"#{name} : #{value}\" }).join(\", \").id}\n    TXT\n  %}\n\n  struct {{name.id}}\n    {% for property in properties %}\n      {% if property.is_a?(Assign) %}\n        {% if compare_versions(Crystal::VERSION, \"1.11.0\") >= 0 && property.doc != \"\" %}# {{property.doc.gsub(/\\n/, \"\\n# \").id}}{% end %}\n        getter {{property.target.id}}\n      {% elsif property.is_a?(TypeDeclaration) %}\n        {% if compare_versions(Crystal::VERSION, \"1.11.0\") >= 0 %}\n          {% unless property.doc == \"\" %}# {{property.doc.gsub(/\\n/, \"\\n# \").id}}{% end %}\n          getter {{property.var.id}} : {{property.type}}{% if !property.value.nil? || property.value.stringify == \"nil\" %} = {{property.value}}{% end %}\n        {% else %}\n          getter {{property}}\n        {% end %}\n      {% else %}\n        {% if compare_versions(Crystal::VERSION, \"1.11.0\") >= 0 && property.doc != \"\" %}# {{property.doc.gsub(/\\n/, \"\\n# \").id}}{% end %}\n        getter :{{property.id}}\n      {% end %}\n    {% end %}\n\n    def initialize({{\n                     properties.map do |field|\n                       \"@#{field.id}\".id\n                     end.splat\n                   }})\n    end\n\n    {{yield}}\n\n    def copy_with({{\n                    properties.map do |property|\n                      if property.is_a?(Assign)\n                        \"#{property.target.id} _#{property.target.id} = @#{property.target.id}\".id\n                      elsif property.is_a?(TypeDeclaration)\n                        \"#{property.var.id} _#{property.var.id} = @#{property.var.id}\".id\n                      else\n                        \"#{property.id} _#{property.id} = @#{property.id}\".id\n                      end\n                    end.splat\n                  }})\n      self.class.new({{\n                       properties.map do |property|\n                         if property.is_a?(Assign)\n                           \"_#{property.target.id}\".id\n                         elsif property.is_a?(TypeDeclaration)\n                           \"_#{property.var.id}\".id\n                         else\n                           \"_#{property.id}\".id\n                         end\n                       end.splat\n                     }})\n    end\n\n    def clone\n      self.class.new({{\n                       properties.map do |property|\n                         if property.is_a?(Assign)\n                           \"@#{property.target.id}.clone\".id\n                         elsif property.is_a?(TypeDeclaration)\n                           \"@#{property.var.id}.clone\".id\n                         else\n                           \"@#{property.id}.clone\".id\n                         end\n                       end.splat\n                     }})\n    end\n  end\nend\n\n# Prints a series of expressions together with their pretty printed values.\n# Useful for print style debugging.\n#\n# ```\n# a = 1\n# pp! a # => \"a # => 1\"\n#\n# pp! [1, 2, 3].map(&.to_s) # => \"[1, 2, 3].map(&.to_s) # => [\"1\", \"2\", \"3\"]\"\n# ```\n#\n# See also: `pp`, `Object#pretty_inspect`.\nmacro pp!(*exps)\n  {% if exps.size == 0 %}\n    # Nothing\n  {% elsif exps.size == 1 %}\n    {% exp = exps.first %}\n    %prefix = \"#{{{ exp.stringify }}} # => \"\n    ::print %prefix\n    ::pp({{exp}})\n  {% else %}\n    %names = { {{exps.map(&.stringify).splat}} }\n    %max_size = %names.max_of &.size\n    {\n      {% for exp, i in exps %}\n        begin\n          %prefix = \"#{%names[{{i}}].ljust(%max_size)} # => \"\n          ::print %prefix\n          ::pp({{exp}})\n        end,\n      {% end %}\n    }\n  {% end %}\nend\n\n# Prints a series of expressions together with their inspected values.\n# Useful for print style debugging.\n#\n# ```\n# a = 1\n# p! a # => \"a # => 1\"\n#\n# p! [1, 2, 3].map(&.to_s) # => \"[1, 2, 3].map(&.to_s) # => [\"1\", \"2\", \"3\"]\"\n# ```\n#\n# See also: `p`, `Object#inspect`.\nmacro p!(*exps)\n  {% if exps.size == 0 %}\n    # Nothing\n  {% elsif exps.size == 1 %}\n    {% exp = exps.first %}\n    %prefix = \"#{{{ exp.stringify }}} # => \"\n    ::print %prefix\n    ::p({{exp}})\n  {% else %}\n    %names = { {{exps.map(&.stringify).splat}} }\n    %max_size = %names.max_of &.size\n    {\n      {% for exp, i in exps %}\n        begin\n          %prefix = \"#{%names[{{i}}].ljust(%max_size)} # => \"\n          ::print %prefix\n          ::p({{exp}})\n        end,\n      {% end %}\n    }\n  {% end %}\nend\n"
  },
  {
    "path": "src/math/libm.cr",
    "content": "# MUSL: On musl systems, libm is empty. The entire library is already included in libc.\n# The empty library is only available for POSIX compatibility. We don't need to link it.\n#\n# Interpreter: On GNU systems, libm.so is typically a GNU ld script which adds\n# the actual library file to the load path. `Crystal::Loader` does not support\n# ld scripts yet. So we just skip that for now. The libm symbols are still\n# available in the interpreter.\n{% if (flag?(:linux) && !flag?(:musl) && !flag?(:interpreted)) || flag?(:bsd) || flag?(:solaris) %}\n  @[Link(\"m\")]\n{% end %}\n\nlib LibM\n  # LLVM standard C library intrinsics\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_ceil_f32)] {% end %}\n  fun ceil_f32 = \"llvm.ceil.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_ceil_f64)] {% end %}\n  fun ceil_f64 = \"llvm.ceil.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_copysign_f32)] {% end %}\n  fun copysign_f32 = \"llvm.copysign.f32\"(magnitude : Float32, sign : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_copysign_f64)] {% end %}\n  fun copysign_f64 = \"llvm.copysign.f64\"(magnitude : Float64, sign : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_cos_f32)] {% end %}\n  fun cos_f32 = \"llvm.cos.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_cos_f64)] {% end %}\n  fun cos_f64 = \"llvm.cos.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_exp_f32)] {% end %}\n  fun exp_f32 = \"llvm.exp.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_exp_f64)] {% end %}\n  fun exp_f64 = \"llvm.exp.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_exp2_f32)] {% end %}\n  fun exp2_f32 = \"llvm.exp2.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_exp2_f64)] {% end %}\n  fun exp2_f64 = \"llvm.exp2.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_floor_f32)] {% end %}\n  fun floor_f32 = \"llvm.floor.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_floor_f64)] {% end %}\n  fun floor_f64 = \"llvm.floor.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_fma_f32)] {% end %}\n  fun fma_f32 = \"llvm.fma.f32\"(value1 : Float32, value2 : Float32, value3 : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_fma_f64)] {% end %}\n  fun fma_f64 = \"llvm.fma.f64\"(value1 : Float64, value2 : Float64, value3 : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_log_f32)] {% end %}\n  fun log_f32 = \"llvm.log.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_log_f64)] {% end %}\n  fun log_f64 = \"llvm.log.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_log2_f32)] {% end %}\n  fun log2_f32 = \"llvm.log2.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_log2_f64)] {% end %}\n  fun log2_f64 = \"llvm.log2.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_log10_f32)] {% end %}\n  fun log10_f32 = \"llvm.log10.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_log10_f64)] {% end %}\n  fun log10_f64 = \"llvm.log10.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_min_f32)] {% end %}\n  fun min_f32 = \"llvm.minnum.f32\"(value1 : Float32, value2 : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_min_f64)] {% end %}\n  fun min_f64 = \"llvm.minnum.f64\"(value1 : Float64, value2 : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_max_f32)] {% end %}\n  fun max_f32 = \"llvm.maxnum.f32\"(value1 : Float32, value2 : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_max_f64)] {% end %}\n  fun max_f64 = \"llvm.maxnum.f64\"(value1 : Float64, value2 : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_pow_f32)] {% end %}\n  fun pow_f32 = \"llvm.pow.f32\"(value : Float32, power : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_pow_f64)] {% end %}\n  fun pow_f64 = \"llvm.pow.f64\"(value : Float64, power : Float64) : Float64\n\n  {% if flag?(:win32) %}\n  {% elsif flag?(:interpreted) %}\n    @[Primitive(:interpreter_libm_powi_f32)]\n    fun powi_f32 = \"llvm.powi.f32\"(value : Float32, power : Int32) : Float32\n\n    @[Primitive(:interpreter_libm_powi_f64)]\n    fun powi_f64 = \"llvm.powi.f64\"(value : Float64, power : Int32) : Float64\n  {% elsif compare_versions(Crystal::LLVM_VERSION, \"13.0.0\") < 0 %}\n    fun powi_f32 = \"llvm.powi.f32\"(value : Float32, power : Int32) : Float32\n    fun powi_f64 = \"llvm.powi.f64\"(value : Float64, power : Int32) : Float64\n  {% else %}\n    fun powi_f32 = \"llvm.powi.f32.i32\"(value : Float32, power : Int32) : Float32\n    fun powi_f64 = \"llvm.powi.f64.i32\"(value : Float64, power : Int32) : Float64\n  {% end %}\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_round_f32)] {% end %}\n  fun round_f32 = \"llvm.round.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_round_f64)] {% end %}\n  fun round_f64 = \"llvm.round.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_rint_f32)] {% end %}\n  fun rint_f32 = \"llvm.rint.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_rint_f64)] {% end %}\n  fun rint_f64 = \"llvm.rint.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_sin_f32)] {% end %}\n  fun sin_f32 = \"llvm.sin.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_sin_f64)] {% end %}\n  fun sin_f64 = \"llvm.sin.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_sqrt_f32)] {% end %}\n  fun sqrt_f32 = \"llvm.sqrt.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_sqrt_f64)] {% end %}\n  fun sqrt_f64 = \"llvm.sqrt.f64\"(value : Float64) : Float64\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_trunc_f32)] {% end %}\n  fun trunc_f32 = \"llvm.trunc.f32\"(value : Float32) : Float32\n\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_libm_trunc_f64)] {% end %}\n  fun trunc_f64 = \"llvm.trunc.f64\"(value : Float64) : Float64\n\n  # libm functions\n  fun acos_f32 = acosf(value : Float32) : Float32\n  fun acos_f64 = acos(value : Float64) : Float64\n  fun acosh_f32 = acoshf(value : Float32) : Float32\n  fun acosh_f64 = acosh(value : Float64) : Float64\n  fun asin_f32 = asinf(value : Float32) : Float32\n  fun asin_f64 = asin(value : Float64) : Float64\n  fun asinh_f32 = asinhf(value : Float32) : Float32\n  fun asinh_f64 = asinh(value : Float64) : Float64\n  fun atan2_f32 = atan2f(value1 : Float32, value2 : Float32) : Float32\n  fun atan2_f64 = atan2(value1 : Float64, value2 : Float64) : Float64\n  fun atan_f32 = atanf(value : Float32) : Float32\n  fun atan_f64 = atan(value : Float64) : Float64\n  fun atanh_f32 = atanhf(value : Float32) : Float32\n  fun atanh_f64 = atanh(value : Float64) : Float64\n  {% if flag?(:win32) %}\n    fun besselj0_f64 = _j0(value : Float64) : Float64\n    fun besselj1_f64 = _j1(value : Float64) : Float64\n    fun besselj_f64 = _jn(value1 : Int32, value2 : Float64) : Float64\n    fun bessely0_f64 = _y0(value : Float64) : Float64\n    fun bessely1_f64 = _y1(value : Float64) : Float64\n    fun bessely_f64 = _yn(value1 : Int32, value2 : Float64) : Float64\n  {% else %}\n    fun besselj0_f32 = j0f(value : Float32) : Float32\n    fun besselj0_f64 = j0(value : Float64) : Float64\n    fun besselj1_f32 = j1f(value : Float32) : Float32\n    fun besselj1_f64 = j1(value : Float64) : Float64\n    fun besselj_f32 = jnf(value1 : Int32, value2 : Float32) : Float32\n    fun besselj_f64 = jn(value1 : Int32, value2 : Float64) : Float64\n    fun bessely0_f32 = y0f(value : Float32) : Float32\n    fun bessely0_f64 = y0(value : Float64) : Float64\n    fun bessely1_f32 = y1f(value : Float32) : Float32\n    fun bessely1_f64 = y1(value : Float64) : Float64\n    fun bessely_f32 = ynf(value1 : Int32, value2 : Float32) : Float32\n    fun bessely_f64 = yn(value1 : Int32, value2 : Float64) : Float64\n  {% end %}\n  fun cbrt_f32 = cbrtf(value : Float32) : Float32\n  fun cbrt_f64 = cbrt(value : Float64) : Float64\n  fun cosh_f32 = coshf(value : Float32) : Float32\n  fun cosh_f64 = cosh(value : Float64) : Float64\n  fun erfc_f32 = erfcf(value : Float32) : Float32\n  fun erfc_f64 = erfc(value : Float64) : Float64\n  fun erf_f32 = erff(value : Float32) : Float32\n  fun erf_f64 = erf(value : Float64) : Float64\n  fun expm1_f32 = expm1f(value : Float32) : Float32\n  fun expm1_f64 = expm1(value : Float64) : Float64\n  {% unless flag?(:win32) %}\n    fun frexp_f32 = frexpf(value : Float32, exp : Int32*) : Float32\n  {% end %}\n  fun frexp_f64 = frexp(value : Float64, exp : Int32*) : Float64\n  fun gamma_f32 = lgammaf(value : Float32) : Float32\n  fun gamma_f64 = lgamma(value : Float64) : Float64\n  {% if flag?(:win32) %}\n    fun hypot_f32 = _hypotf(value1 : Float32, value2 : Float32) : Float32\n  {% else %}\n    fun hypot_f32 = hypotf(value1 : Float32, value2 : Float32) : Float32\n  {% end %}\n  fun hypot_f64 = hypot(value1 : Float64, value2 : Float64) : Float64\n  fun ilogb_f32 = ilogbf(value : Float32) : Int32\n  fun ilogb_f64 = ilogb(value : Float64) : Int32\n  {% unless flag?(:win32) %}\n    fun ldexp_f32 = ldexpf(value1 : Float32, value2 : Int32) : Float32\n  {% end %}\n  fun ldexp_f64 = ldexp(value1 : Float64, value2 : Int32) : Float64\n  fun log1p_f32 = log1pf(value : Float32) : Float32\n  fun log1p_f64 = log1p(value : Float64) : Float64\n  fun logb_f32 = logbf(value : Float32) : Float32\n  fun logb_f64 = logb(value : Float64) : Float64\n  fun nextafter_f32 = nextafterf(from : Float32, to : Float32) : Float32\n  fun nextafter_f64 = nextafter(from : Float64, to : Float64) : Float64\n  fun scalbln_f32 = scalblnf(value1 : Float32, value2 : Int64) : Float32\n  fun scalbln_f64 = scalbln(value1 : Float64, value2 : Int64) : Float64\n  fun scalbn_f32 = scalbnf(value1 : Float32, value2 : Int32) : Float32\n  fun scalbn_f64 = scalbn(value1 : Float64, value2 : Int32) : Float64\n  fun sinh_f32 = sinhf(value : Float32) : Float32\n  fun sinh_f64 = sinh(value : Float64) : Float64\n  fun tan_f32 = tanf(value : Float32) : Float32\n  fun tan_f64 = tan(value : Float64) : Float64\n  fun tanh_f32 = tanhf(value : Float32) : Float32\n  fun tanh_f64 = tanh(value : Float64) : Float64\n  fun tgamma_f32 = tgammaf(value : Float32) : Float32\n  fun tgamma_f64 = tgamma(value : Float64) : Float64\nend\n"
  },
  {
    "path": "src/math/math.cr",
    "content": "require \"./libm\"\n\nmodule Math\n  extend self\n\n  # Archimedes' constant (π).\n  PI = 3.14159265358979323846\n  # The full circle constant (τ), equal to 2π.\n  TAU = 6.283185307179586476925\n  # Euler's number (e).\n  E     = LibM.exp_f64(1.0)\n  LOG2  = LibM.log_f64(2.0)\n  LOG10 = LibM.log_f64(10.0)\n\n  # Calculates the sine of *value*, measured in radians.\n  def sin(value : Float32) : Float32\n    LibM.sin_f32(value)\n  end\n\n  # :ditto:\n  def sin(value : Float64) : Float64\n    LibM.sin_f64(value)\n  end\n\n  # :ditto:\n  def sin(value)\n    sin(value.to_f)\n  end\n\n  # Calculates the cosine of *value*, measured in radians.\n  def cos(value : Float32) : Float32\n    LibM.cos_f32(value)\n  end\n\n  # :ditto:\n  def cos(value : Float64) : Float64\n    LibM.cos_f64(value)\n  end\n\n  # :ditto:\n  def cos(value)\n    cos(value.to_f)\n  end\n\n  # Calculates the tangent of *value*, measured in radians.\n  def tan(value : Float32) : Float32\n    LibM.tan_f32(value)\n  end\n\n  # :ditto:\n  def tan(value : Float64) : Float64\n    LibM.tan_f64(value)\n  end\n\n  # :ditto:\n  def tan(value)\n    tan(value.to_f)\n  end\n\n  # Calculates the arc sine of *value*.\n  def asin(value : Float32) : Float32\n    LibM.asin_f32(value)\n  end\n\n  # :ditto:\n  def asin(value : Float64) : Float64\n    LibM.asin_f64(value)\n  end\n\n  # :ditto:\n  def asin(value)\n    asin(value.to_f)\n  end\n\n  # Calculates the arc cosine of *value*.\n  def acos(value : Float32) : Float32\n    LibM.acos_f32(value)\n  end\n\n  # :ditto:\n  def acos(value : Float64) : Float64\n    LibM.acos_f64(value)\n  end\n\n  # :ditto:\n  def acos(value)\n    acos(value.to_f)\n  end\n\n  # Calculates the arc tangent of *value*.\n  def atan(value : Float32) : Float32\n    LibM.atan_f32(value)\n  end\n\n  # :ditto:\n  def atan(value : Float64) : Float64\n    LibM.atan_f64(value)\n  end\n\n  # :ditto:\n  def atan(value)\n    atan(value.to_f)\n  end\n\n  # Calculates the two-argument arc tangent of the ray from (0, 0) to (*x*, *y*).\n  def atan2(y : Float32, x : Float32) : Float32\n    LibM.atan2_f32(y, x)\n  end\n\n  # :ditto:\n  def atan2(y : Float64, x : Float64) : Float64\n    LibM.atan2_f64(y, x)\n  end\n\n  # :ditto:\n  def atan2(y, x) : Float64\n    atan2(y.to_f, x.to_f)\n  end\n\n  # Calculates the hyperbolic sine of *value*.\n  def sinh(value : Float32) : Float32\n    LibM.sinh_f32(value)\n  end\n\n  # :ditto:\n  def sinh(value : Float64) : Float64\n    LibM.sinh_f64(value)\n  end\n\n  # :ditto:\n  def sinh(value)\n    sinh(value.to_f)\n  end\n\n  # Calculates the hyperbolic cosine of *value*.\n  def cosh(value : Float32) : Float32\n    LibM.cosh_f32(value)\n  end\n\n  # :ditto:\n  def cosh(value : Float64) : Float64\n    LibM.cosh_f64(value)\n  end\n\n  # :ditto:\n  def cosh(value)\n    cosh(value.to_f)\n  end\n\n  # Calculates the hyperbolic tangent of *value*.\n  def tanh(value : Float32) : Float32\n    LibM.tanh_f32(value)\n  end\n\n  # :ditto:\n  def tanh(value : Float64) : Float64\n    LibM.tanh_f64(value)\n  end\n\n  # :ditto:\n  def tanh(value)\n    tanh(value.to_f)\n  end\n\n  # Calculates the inverse hyperbolic sine of *value*.\n  def asinh(value : Float32) : Float32\n    LibM.asinh_f32(value)\n  end\n\n  # :ditto:\n  def asinh(value : Float64) : Float64\n    LibM.asinh_f64(value)\n  end\n\n  # :ditto:\n  def asinh(value)\n    asinh(value.to_f)\n  end\n\n  # Calculates the inverse hyperbolic cosine of *value*.\n  def acosh(value : Float32) : Float32\n    LibM.acosh_f32(value)\n  end\n\n  # :ditto:\n  def acosh(value : Float64) : Float64\n    LibM.acosh_f64(value)\n  end\n\n  # :ditto:\n  def acosh(value)\n    acosh(value.to_f)\n  end\n\n  # Calculates the inverse hyperbolic tangent of *value*.\n  def atanh(value : Float32) : Float32\n    LibM.atanh_f32(value)\n  end\n\n  # :ditto:\n  def atanh(value : Float64) : Float64\n    LibM.atanh_f64(value)\n  end\n\n  # :ditto:\n  def atanh(value)\n    atanh(value.to_f)\n  end\n\n  # Calculates the exponential of *value*.\n  def exp(value : Float32) : Float32\n    LibM.exp_f32(value)\n  end\n\n  # :ditto:\n  def exp(value : Float64) : Float64\n    LibM.exp_f64(value)\n  end\n\n  # :ditto:\n  def exp(value)\n    exp(value.to_f)\n  end\n\n  # Calculates the exponential of *value*, minus 1.\n  def expm1(value : Float32) : Float32\n    LibM.expm1_f32(value)\n  end\n\n  # :ditto:\n  def expm1(value : Float64) : Float64\n    LibM.expm1_f64(value)\n  end\n\n  # :ditto:\n  def expm1(value)\n    expm1(value.to_f)\n  end\n\n  # Calculates 2 raised to the power *value*.\n  def exp2(value : Float32) : Float32\n    LibM.exp2_f32(value)\n  end\n\n  # :ditto:\n  def exp2(value : Float64) : Float64\n    LibM.exp2_f64(value)\n  end\n\n  # :ditto:\n  def exp2(value)\n    exp2(value.to_f)\n  end\n\n  # Calculates the natural logarithm of *value*.\n  def log(value : Float32) : Float32\n    LibM.log_f32(value)\n  end\n\n  # :ditto:\n  def log(value : Float64) : Float64\n    LibM.log_f64(value)\n  end\n\n  # :ditto:\n  def log(value) : Float64\n    log(value.to_f)\n  end\n\n  # Calculates the natural logarithm of 1 plus *value*.\n  def log1p(value : Float32) : Float32\n    LibM.log1p_f32(value)\n  end\n\n  # :ditto:\n  def log1p(value : Float64) : Float64\n    LibM.log1p_f64(value)\n  end\n\n  # :ditto:\n  def log1p(value)\n    log1p(value.to_f)\n  end\n\n  # Calculates the logarithm of *value* to base 2.\n  def log2(value : Float32) : Float32\n    LibM.log2_f32(value)\n  end\n\n  # :ditto:\n  def log2(value : Float64) : Float64\n    LibM.log2_f64(value)\n  end\n\n  # :ditto:\n  def log2(value) : Float64\n    log2(value.to_f)\n  end\n\n  # Calculates the logarithm of *value* to base 10.\n  def log10(value : Float32) : Float32\n    LibM.log10_f32(value)\n  end\n\n  # :ditto:\n  def log10(value : Float64) : Float64\n    LibM.log10_f64(value)\n  end\n\n  # :ditto:\n  def log10(value)\n    log10(value.to_f)\n  end\n\n  # Calculates the logarithm of *value* to the given *base*.\n  def log(value, base)\n    log(value) / log(base)\n  end\n\n  # Calculates the square root of *value*.\n  def sqrt(value : Float32) : Float32\n    LibM.sqrt_f32(value)\n  end\n\n  # :ditto:\n  def sqrt(value : Float64) : Float64\n    LibM.sqrt_f64(value)\n  end\n\n  # :ditto:\n  def sqrt(value) : Float64\n    sqrt(value.to_f)\n  end\n\n  # Calculates the integer square root of *value*.\n  def isqrt(value : Int::Primitive)\n    raise ArgumentError.new \"Input must be non-negative integer\" if value < 0\n    return value if value < 2\n    res = value.class.zero\n    bit = res.succ << (res.leading_zeros_count - 2)\n    bit >>= value.leading_zeros_count & ~0x3\n    while (bit != 0)\n      if value >= res + bit\n        value -= res + bit\n        res = (res >> 1) + bit\n      else\n        res >>= 1\n      end\n      bit >>= 2\n    end\n    res\n  end\n\n  # Calculates the cubic root of *value*.\n  def cbrt(value : Float32) : Float32\n    LibM.cbrt_f32(value)\n  end\n\n  # :ditto:\n  def cbrt(value : Float64) : Float64\n    LibM.cbrt_f64(value)\n  end\n\n  # :ditto:\n  def cbrt(value)\n    cbrt(value.to_f)\n  end\n\n  # Calculates the error function of *value*.\n  def erf(value : Float32) : Float32\n    LibM.erf_f32(value)\n  end\n\n  # :ditto:\n  def erf(value : Float64) : Float64\n    LibM.erf_f64(value)\n  end\n\n  # :ditto:\n  def erf(value)\n    erf(value.to_f)\n  end\n\n  # Calculates 1 minus the error function of *value*.\n  def erfc(value : Float32) : Float32\n    LibM.erfc_f32(value)\n  end\n\n  # :ditto:\n  def erfc(value : Float64) : Float64\n    LibM.erfc_f64(value)\n  end\n\n  # :ditto:\n  def erfc(value)\n    erfc(value.to_f)\n  end\n\n  # Calculates the gamma function of *value*.\n  #\n  # Note that `gamma(n)` is same as `fact(n - 1)` for integer `n > 0`.\n  # However `gamma(n)` returns float and can be an approximation.\n  def gamma(value : Float32) : Float32\n    LibM.tgamma_f32(value)\n  end\n\n  # :ditto:\n  def gamma(value : Float64) : Float64\n    LibM.tgamma_f64(value)\n  end\n\n  # :ditto:\n  def gamma(value) : Float64\n    gamma(value.to_f)\n  end\n\n  # Calculates the logarithmic gamma of *value*.\n  #\n  # ```\n  # Math.lgamma(2.96)\n  # ```\n  # is equivalent to\n  # ```\n  # Math.log(Math.gamma(2.96).abs)\n  # ```\n  def lgamma(value : Float32)\n    {% if flag?(:darwin) %}\n      LibM.gamma_f64(value).to_f32\n    {% else %}\n      LibM.gamma_f32(value)\n    {% end %}\n  end\n\n  # :ditto:\n  def lgamma(value : Float64) : Float64\n    LibM.gamma_f64(value)\n  end\n\n  # :ditto:\n  def lgamma(value) : Float64\n    lgamma(value.to_f)\n  end\n\n  # Calculates the cylindrical Bessel function of the first kind of *value* for the given *order*.\n  def besselj(order : Int32, value : Float32)\n    {% if flag?(:darwin) || flag?(:win32) %}\n      LibM.besselj_f64(order, value).to_f32\n    {% else %}\n      LibM.besselj_f32(order, value)\n    {% end %}\n  end\n\n  # :ditto:\n  def besselj(order : Int32, value : Float64) : Float64\n    LibM.besselj_f64(order, value)\n  end\n\n  # :ditto:\n  def besselj(order, value)\n    besselj(order.to_i32, value.to_f)\n  end\n\n  # Calculates the cylindrical Bessel function of the first kind of *value* for order 0.\n  def besselj0(value : Float32)\n    {% if flag?(:darwin) || flag?(:win32) %}\n      LibM.besselj0_f64(value).to_f32\n    {% else %}\n      LibM.besselj0_f32(value)\n    {% end %}\n  end\n\n  # :ditto:\n  def besselj0(value : Float64) : Float64\n    LibM.besselj0_f64(value)\n  end\n\n  # :ditto:\n  def besselj0(value)\n    besselj0(value.to_f)\n  end\n\n  # Calculates the cylindrical Bessel function of the first kind of *value* for order 1.\n  def besselj1(value : Float32)\n    {% if flag?(:darwin) || flag?(:win32) %}\n      LibM.besselj1_f64(value).to_f32\n    {% else %}\n      LibM.besselj1_f32(value)\n    {% end %}\n  end\n\n  # :ditto:\n  def besselj1(value : Float64) : Float64\n    LibM.besselj1_f64(value)\n  end\n\n  # :ditto:\n  def besselj1(value)\n    besselj1(value.to_f)\n  end\n\n  # Calculates the cylindrical Bessel function of the second kind of *value* for the given *order*.\n  def bessely(order : Int32, value : Float32)\n    {% if flag?(:darwin) || flag?(:win32) %}\n      LibM.bessely_f64(order, value).to_f32\n    {% else %}\n      LibM.bessely_f32(order, value)\n    {% end %}\n  end\n\n  # :ditto:\n  def bessely(order : Int32, value : Float64) : Float64\n    LibM.bessely_f64(order, value)\n  end\n\n  # :ditto:\n  def bessely(order, value)\n    bessely(order.to_i32, value.to_f)\n  end\n\n  # Calculates the cylindrical Bessel function of the second kind of *value* for order 0.\n  def bessely0(value : Float32)\n    {% if flag?(:darwin) || flag?(:win32) %}\n      LibM.bessely0_f64(value).to_f32\n    {% else %}\n      LibM.bessely0_f32(value)\n    {% end %}\n  end\n\n  # :ditto:\n  def bessely0(value : Float64) : Float64\n    LibM.bessely0_f64(value)\n  end\n\n  # :ditto:\n  def bessely0(value)\n    bessely0(value.to_f)\n  end\n\n  # Calculates the cylindrical Bessel function of the second kind of *value* for order 1.\n  def bessely1(value : Float32)\n    {% if flag?(:darwin) || flag?(:win32) %}\n      LibM.bessely1_f64(value).to_f32\n    {% else %}\n      LibM.bessely1_f32(value)\n    {% end %}\n  end\n\n  # :ditto:\n  def bessely1(value : Float64) : Float64\n    LibM.bessely1_f64(value)\n  end\n\n  # :ditto:\n  def bessely1(value)\n    bessely1(value.to_f)\n  end\n\n  # Calculates the length of the hypotenuse from (0, 0) to (*value1*, *value2*).\n  #\n  # Equivalent to\n  # ```\n  # Math.sqrt(value1 ** 2 + value2 ** 2)\n  # ```\n  def hypot(value1 : Float32, value2 : Float32) : Float32\n    LibM.hypot_f32(value1, value2)\n  end\n\n  # :ditto:\n  def hypot(value1 : Float64, value2 : Float64) : Float64\n    LibM.hypot_f64(value1, value2)\n  end\n\n  # :ditto:\n  def hypot(value1, value2)\n    hypot(value1.to_f, value2.to_f)\n  end\n\n  # Fused multiply-add; returns `value1 * value2 + value3`, performing a single\n  # rounding instead of two.\n  #\n  # ```\n  # Math.fma(0.1, 10.0, -1.0) # => 5.551115123125783e-17\n  # 1.0 * 10.0 - 1.0          # => 0.0\n  # ```\n  def fma(value1 : Float32, value2 : Float32, value3 : Float32) : Float32\n    LibM.fma_f32(value1, value2, value3)\n  end\n\n  # :ditto:\n  def fma(value1 : Float64, value2 : Float64, value3 : Float64) : Float64\n    LibM.fma_f64(value1, value2, value3)\n  end\n\n  # :ditto:\n  def fma(value1, value2, value3)\n    fma(value1.to_f, value2.to_f, value3.to_f)\n  end\n\n  # Returns the unbiased base 2 exponent of the given floating-point *value*.\n  def ilogb(value : Float32) : Int32\n    LibM.ilogb_f32(value)\n  end\n\n  # :ditto:\n  def ilogb(value : Float64) : Int32\n    LibM.ilogb_f64(value)\n  end\n\n  # :ditto:\n  def ilogb(value)\n    ilogb(value.to_f)\n  end\n\n  # Returns the unbiased radix-independent exponent of the given floating-point *value*.\n  #\n  # For `Float32` and `Float64` this is equivalent to `ilogb`.\n  def logb(value : Float32) : Float32\n    LibM.logb_f32(value)\n  end\n\n  # :ditto:\n  def logb(value : Float64) : Float64\n    LibM.logb_f64(value)\n  end\n\n  # :ditto:\n  def logb(value)\n    logb(value.to_f)\n  end\n\n  # Multiplies the given floating-point *value* by 2 raised to the power *exp*.\n  def ldexp(value : Float32, exp : Int32) : Float32\n    {% if flag?(:win32) %}\n      # ucrt does not export `ldexpf` and instead defines it like this\n      LibM.ldexp_f64(value, exp).to_f32!\n    {% else %}\n      LibM.ldexp_f32(value, exp)\n    {% end %}\n  end\n\n  # :ditto:\n  def ldexp(value : Float64, exp : Int32) : Float64\n    LibM.ldexp_f64(value, exp)\n  end\n\n  # :ditto:\n  def ldexp(value, exp)\n    ldexp(value.to_f, exp.to_i32)\n  end\n\n  # Returns the floating-point *value* with its exponent raised by *exp*.\n  #\n  # For `Float32` and `Float64` this is equivalent to `ldexp`.\n  def scalbn(value : Float32, exp : Int32) : Float32\n    LibM.scalbn_f32(value, exp)\n  end\n\n  # :ditto:\n  def scalbn(value : Float64, exp : Int32) : Float64\n    LibM.scalbn_f64(value, exp)\n  end\n\n  # :ditto:\n  def scalbn(value, exp)\n    scalbn(value.to_f, exp.to_i32)\n  end\n\n  # :ditto:\n  def scalbln(value : Float32, exp : Int64)\n    LibM.scalbln_f32(value, exp)\n  end\n\n  # :ditto:\n  def scalbln(value : Float64, exp : Int64) : Float64\n    LibM.scalbln_f64(value, exp)\n  end\n\n  # :ditto:\n  def scalbln(value, exp) : Float64\n    scalbln(value.to_f, exp.to_i64)\n  end\n\n  # Decomposes the given floating-point *value* into a normalized fraction and an integral power of two.\n  def frexp(value : Float32) : {Float32, Int32}\n    {% if flag?(:win32) %}\n      # ucrt does not export `frexpf` and instead defines it like this\n      frac = LibM.frexp_f64(value, out exp)\n      {frac.to_f32, exp}\n    {% else %}\n      frac = LibM.frexp_f32(value, out exp)\n      {frac, exp}\n    {% end %}\n  end\n\n  # :ditto:\n  def frexp(value : Float64) : {Float64, Int32}\n    frac = LibM.frexp_f64(value, out exp)\n    {frac, exp}\n  end\n\n  # :ditto:\n  def frexp(value)\n    frexp(value.to_f)\n  end\n\n  # Returns the floating-point value with the magnitude of *value1* and the sign of *value2*.\n  def copysign(value1 : Float32, value2 : Float32)\n    LibM.copysign_f32(value1, value2)\n  end\n\n  # :ditto:\n  def copysign(value1 : Float64, value2 : Float64) : Float64\n    LibM.copysign_f64(value1, value2)\n  end\n\n  # :ditto:\n  def copysign(value1, value2)\n    copysign(value1.to_f, value2.to_f)\n  end\n\n  # Returns the greater of *value1* and *value2*.\n  def max(value1 : Float32, value2 : Float32)\n    LibM.max_f32(value1, value2)\n  end\n\n  # :ditto:\n  def max(value1 : Float64, value2 : Float64) : Float64\n    LibM.max_f64(value1, value2)\n  end\n\n  # :ditto:\n  def max(value1, value2)\n    value1 >= value2 ? value1 : value2\n  end\n\n  # Returns the smaller of *value1* and *value2*.\n  def min(value1 : Float32, value2 : Float32)\n    LibM.min_f32(value1, value2)\n  end\n\n  # :ditto:\n  def min(value1 : Float64, value2 : Float64) : Float64\n    LibM.min_f64(value1, value2)\n  end\n\n  # :ditto:\n  def min(value1, value2)\n    value1 <= value2 ? value1 : value2\n  end\n\n  # Computes the smallest nonnegative power of 2 that is greater than or equal\n  # to *v*.\n  #\n  # The returned value has the same type as the argument. Raises `OverflowError`\n  # if the result does not fit into the argument's type.\n  #\n  # ```\n  # Math.pw2ceil(33) # => 64\n  # Math.pw2ceil(64) # => 64\n  # Math.pw2ceil(-5) # => 1\n  # ```\n  def pw2ceil(v : Int::Primitive)\n    v.next_power_of_two\n  end\nend\n"
  },
  {
    "path": "src/mime/media_type.cr",
    "content": "require \"uri\"\nrequire \"http\"\nrequire \"mime\"\n\nmodule MIME\n  # A `MediaType` describes a MIME content type with optional parameters.\n  struct MediaType\n    getter media_type : String\n\n    # Creates a new `MediaType` instance.\n    def initialize(@media_type, @params = {} of String => String)\n      @params.each_key do |name|\n        raise Error.new(\"Invalid parameter name #{name.inspect}\") unless MIME::MediaType.token? name\n      end\n    end\n\n    def to_s(io : IO) : Nil\n      io << media_type\n\n      @params.each do |key, value|\n        io << \"; \"\n        io << key.downcase << '='\n\n        if MIME::MediaType.token? value\n          io << value\n        else\n          io << '\"'\n          MIME::MediaType.quote_string(value, io)\n          io << '\"'\n        end\n      end\n    end\n\n    # Returns the value for the parameter given by *key*. If not found, raises `KeyError`.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # MIME::MediaType.parse(\"text/plain; charset=UTF-8\")[\"charset\"] # => \"UTF-8\"\n    # MIME::MediaType.parse(\"text/plain; charset=UTF-8\")[\"foo\"]     # raises KeyError\n    # ```\n    def [](key : String) : String\n      @params[key]\n    end\n\n    # Returns the value for the parameter given by *key*. If not found, returns `nil`.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # MIME::MediaType.parse(\"text/plain; charset=UTF-8\")[\"charset\"]? # => \"UTF-8\"\n    # MIME::MediaType.parse(\"text/plain; charset=UTF-8\")[\"foo\"]?     # => nil\n    # ```\n    def []?(key : String) : String?\n      @params[key]?\n    end\n\n    # Sets the value of parameter *key* to the given value.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # mime_type = MIME::MediaType.parse(\"x-application/example\")\n    # mime_type[\"foo\"] = \"bar\"\n    # mime_type[\"foo\"] # => \"bar\"\n    # ```\n    def []=(key : String, value : String) : String\n      raise Error.new(\"Invalid parameter name\") unless MIME::MediaType.token? key\n      @params[key] = value\n    end\n\n    # Returns the value for the parameter given by *key*, or when not found the value given by *default*.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # MIME::MediaType.parse(\"x-application/example\").fetch(\"foo\", \"baz\")          # => \"baz\"\n    # MIME::MediaType.parse(\"x-application/example; foo=bar\").fetch(\"foo\", \"baz\") # => \"bar\"\n    # ```\n    def fetch(key : String, default : T) : String | T forall T\n      @params.fetch(key, default)\n    end\n\n    # Returns the value for the parameter given by *key*, or when not found calls the given block with the *key*.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # MIME::MediaType.parse(\"x-application/example\").fetch(\"foo\") { |key| key }          # => \"foo\"\n    # MIME::MediaType.parse(\"x-application/example; foo=bar\").fetch(\"foo\") { |key| key } # => \"bar\"\n    # ```\n    def fetch(key : String, &block : String -> _)\n      @params.fetch(key) { |key| yield key }\n    end\n\n    # Calls the given block for each parameter and passes in the key and the value.\n    def each_parameter(&block : String, String -> _) : Nil\n      @params.each do |key, value|\n        yield key, value\n      end\n    end\n\n    # Returns an iterator over the parameter which behaves like an `Iterator` returning a `Tuple` of key and value.\n    def each_parameter : Iterator(Tuple(String, String))\n      @params.each\n    end\n\n    # First component of `media_type`.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # MIME::MediaType.new(\"text/plain\").type # => \"text\"\n    # MIME::MediaType.new(\"foo\").type        # => \"foo\"\n    # ```\n    #\n    def type : String\n      index = media_type.byte_index('/'.ord) || media_type.bytesize\n      media_type.byte_slice(0, index)\n    end\n\n    # Second component of `media_type` or `nil`.\n    #\n    # ```\n    # require \"mime/media_type\"\n    #\n    # MIME::MediaType.new(\"text/plain\").sub_type # => \"plain\"\n    # MIME::MediaType.new(\"foo\").sub_type        # => nil\n    # ```\n    def sub_type : String?\n      index = media_type.byte_index('/'.ord) || return\n      media_type.byte_slice(index + 1, media_type.bytesize - index - 1)\n    end\n\n    # Parses a MIME type string representation including any optional parameters,\n    # per RFC 1521.\n    # Media types are the values in `Content-Type` and `Content-Disposition` HTTP\n    # headers (RFC 2183).\n    #\n    # Media type is lowercased and trimmed of whitespace. Param keys are lowercased.\n    #\n    # Raises `MIME::Error` on error.\n    def self.parse(string : String) : MediaType\n      parse_impl(string) { |err| raise MIME::Error.new(\"Parsing error: #{err} (#{string.dump})\") }\n    end\n\n    # Parses a MIME type string representation including any optional parameters,\n    # per RFC 1521.\n    # Media types are the values in `Content-Type` and `Content-Disposition` HTTP\n    # headers (RFC 2183).\n    #\n    # Media type is lowercased and trimmed of whitespace. Param keys are lowercased.\n    #\n    # Returns `nil` on error.\n    def self.parse?(string : String) : MediaType?\n      parse_impl(string) { return nil }\n    end\n\n    # This methods parses a MIME media type according to RFC 2231\n    private def self.parse_impl(string : String, &block : String -> NoReturn)\n      reader = Char::Reader.new(string)\n\n      # 1. Reading the type and subtype, separated by a '/'\n      #    media_type := type \"/\" sub_type\n\n      sub_type_start = -1 # invalid value to detect if sub type start was read\n      while reader.has_next?\n        char = reader.current_char\n\n        if char == ';'\n          # when ';' is reached, the media type is finished\n          # optional parameters may follow\n          break\n        elsif char == '/'\n          # Abort if there had already been a '/'\n          yield \"Invalid '/' at #{reader.pos}\" if sub_type_start > -1\n\n          # The separator between type and subtype has been reached\n          sub_type_start = reader.pos\n          reader.next_char\n        elsif token?(char)\n          reader.next_char\n        else\n          yield \"Invalid character '#{char}' at #{reader.pos}\"\n        end\n      end\n\n      # string is empty or first character was a ';'\n      yield \"Missing media type\" if reader.pos == 0\n\n      mediatype = string.byte_slice(0, reader.pos).strip.downcase\n\n      # string contained only whitespace as media type\n      yield \"Missing media type\" if mediatype.empty?\n\n      # 2. Consume parameters, consisting of attribute-value pairs\n      #     parameters := *( \";\" parameter)\n      #     parameter  := attribute \"=\" value\n\n      # Stores regular attribute-value pairs\n      params = {} of String => String\n\n      # Stores extended parameters with continuation values\n      continuation = Hash(String, Hash(String, String)).new do |hash, key|\n        hash[key] = {} of String => String\n      end\n\n      while reader.has_next?\n        # Consume optional whitespace\n        reader = consume_whitespace(reader)\n\n        break unless reader.has_next?\n\n        # ';' has not been consumed previously\n        reader.next_char\n\n        # Consume optional whitespace\n        reader = consume_whitespace(reader)\n\n        break unless reader.has_next?\n\n        key_start = reader.pos\n\n        # Consume attribute name and break at '='\n        while reader.has_next?\n          case char = reader.current_char\n          when ';'\n            yield \"Invalid ';' at #{reader.pos}, expecting '='\"\n          when '='\n            break\n          else\n            unless token?(char)\n              yield \"Invalid character '#{char}' at #{reader.pos}\"\n            end\n\n            reader.next_char\n          end\n        end\n\n        # Read the attribute name into a variable. It is case-insensitive and\n        # might be surrounded by whitespace, hence `.rstrip.downcase`.\n        key = string.byte_slice(key_start, reader.pos - key_start).rstrip.downcase\n\n        yield \"Missing attribute name at #{key_start}\" if key.empty?\n\n        # Consume '='\n        if reader.has_next?\n          reader.next_char\n        else\n          yield \"Missing attribute value at #{reader.pos}\"\n        end\n\n        # '*' designates an extended parameter name\n        # It is not yet processed but extended parameters are added to a\n        # special `continuation` map instead of `params` for later processing.\n        base_key, star, section = key.partition('*')\n        if star.empty?\n          if params.has_key?(key)\n            yield \"Duplicate key '#{key}' at #{key_start}\"\n          end\n\n          # Consume parameter value\n          reader, value = parse_parameter_value(reader) { |err| yield err }\n\n          # Add the attribute-value pair to `params`.\n          params[key] = value\n        else\n          # Remove whitespace surrounding '*'\n          base_key = base_key.rstrip\n          section = section.lstrip\n\n          section.each_char_with_index do |char, index|\n            unless char.ascii_number? || (char == '*' && index == section.bytesize - 1)\n              yield \"Invalid key '#{key}' at #{key_start}\"\n            end\n          end\n\n          # TODO: Using a different data structure than `Hash` for storing\n          # continuation sections could improve performance.\n          normalized_key = \"#{base_key}*#{section}\"\n          continuation_map = continuation[base_key]\n          if continuation_map.has_key?(normalized_key)\n            yield \"Duplicate key '#{key}' at #{key_start}\"\n          end\n\n          # Consume parameter value\n          reader, value = parse_parameter_value(reader) { |err| yield err }\n\n          # Add the attribute-value pair to `continuation` map.\n          continuation_map[normalized_key] = value\n        end\n      end\n\n      # 3. Resolve continuation parameters\n      #\n      #     extended-parameter := (extended-initial-name \"=\" extended-value) /\n      #                           (extended-other-names \"=\" extended-other-values)\n      #\n      #     initial-section := \"*0\"\n      #\n      #     other-sections := \"*\" (\"1\" / \"2\" / \"3\" / \"4\" / \"5\" /\n      #                            \"6\" / \"7\" / \"8\" / \"9\") *DIGIT)\n      #\n      #     extended-initial-name := attribute [initial-section] \"*\"\n      #\n      #     extended-other-names := attribute other-sections \"*\"\n      #\n      #     extended-initial-value := [charset] \"'\" [language] \"'\" extended-other-values\n      #\n      #     extended-other-values := *(ext-octet / attribute-char)\n      continuation.each do |base_key, pieces|\n        # `#{base_key}*` is an extended-initial-name without initial-section.\n        # This is not the start of a continuation, just a single encoded value.\n        if value = pieces[\"#{base_key}*\"]?\n          if decoded = decode_rfc2231(value)\n            params[base_key] = decoded\n          end\n          next\n        end\n\n        # All other pieces are extended-parameter and need to be sequentially\n        # ordered by section number\n        valid = false\n        composite_value = String.build do |io|\n          counter = 0\n          loop do\n            part_key = \"#{base_key}*#{counter}\"\n            if part = pieces[part_key]?\n              valid = true\n              io << part\n            elsif part = pieces[\"#{part_key}*\"]?\n              valid = true\n\n              io << decode_rfc2231(part) || next\n            else\n              break\n            end\n            counter += 1\n          end\n        end\n        if valid\n          params[base_key] = composite_value\n        else\n          # TODO: Not sure if invalid parameter should error or just be ignored.\n          # yield \"Invalid extended parameter: '#{base_key}'\"\n        end\n      end\n\n      # text types should automatically use UTF-8 charset unless specified differently.\n      if mediatype.starts_with?(\"text/\")\n        params[\"charset\"] ||= \"utf-8\"\n      end\n\n      MediaType.new mediatype, params\n    end\n\n    private def self.parse_parameter_value(reader, &)\n      reader = consume_whitespace(reader)\n\n      # Quoted value.\n      if reader.current_char == '\"'\n        reader.next_char\n\n        quoted = true\n        waiting_for_closing_quote = true\n      else\n        quoted = false\n        waiting_for_closing_quote = false\n      end\n\n      value = String.build do |io|\n        # Set positions for copying data from reader string to `io` in bulk.\n        value_start = reader.pos\n        value_end = reader.pos\n\n        while reader.has_next?\n          case char = reader.current_char\n          when ';'\n            break unless quoted\n\n            reader.next_char\n          when '\"'\n            yield \"Unexpected '\\\"' at #{reader.pos}\" unless waiting_for_closing_quote\n\n            waiting_for_closing_quote = false\n            break\n          when '\\\\'\n            reader.next_char\n\n            char = reader.current_char\n            # Escape `\\\\` and `\\\"`\n            if char == '\\\\' || (quoted && char == '\"')\n              # Write everything before the escaping backslash to io, then set\n              # `value_start` to the position of the escaped character, thus it\n              # will be included in the next write.\n              # This essentially skips writing the escaping backslash.\n              io.write reader.string.to_slice[value_start, reader.pos - value_start - 1]\n              value_start = reader.pos\n\n              reader.next_char\n            end\n          when '='\n            if quoted\n              reader.next_char\n            else\n              yield \"Unexpected '=' at #{reader.pos}\"\n            end\n          else\n            reader.next_char\n          end\n        end\n\n        if quoted\n          if waiting_for_closing_quote\n            yield \"Unclosed quote at #{reader.pos}\"\n          end\n\n          # Set position for final bulk copy.\n          value_end = reader.pos\n\n          reader.next_char\n\n          reader = consume_whitespace(reader)\n\n          if reader.has_next? && reader.current_char != ';'\n            yield \"Invalid character '#{reader.current_char}' at #{reader.pos}, expecting ';'\"\n          end\n        else\n          # remove right-hand-side whitespace when unquoted\n\n          # 1. Step one character back to check for whitespace.\n          reader.previous_char if reader.has_previous?\n          # 2. Remove whitespace.\n          while reader.has_previous? && reader.current_char.ascii_whitespace?\n            reader.previous_char\n          end\n          # 3. Step one character ahead again.\n          reader.next_char\n\n          value_end = reader.pos\n        end\n\n        io.write reader.string.to_slice[value_start, value_end - value_start]\n      end\n\n      return reader, value\n    end\n\n    private def self.decode_rfc2231(encoded : String)\n      encoding, _, rest = encoded.partition('\\'')\n      _lang, _, value = rest.partition('\\'')\n\n      return if encoding.empty? || value.empty?\n\n      encoding = encoding.downcase\n\n      IO::Memory.new.tap do |io|\n        io.set_encoding encoding\n\n        reader = Char::Reader.new(value)\n        while reader.has_next?\n          case char = reader.current_char\n          when '%'\n            first = reader.next_char.to_i?(16) || return\n            second = reader.next_char.to_i?(16) || return\n\n            num = first << 4 | second\n            io.write_byte num.to_u8\n            reader.next_char\n          else\n            io << char\n            reader.next_char\n          end\n        end\n      end.rewind.gets_to_end\n    end\n\n    private def self.consume_whitespace(reader)\n      while reader.current_char.ascii_whitespace?\n        reader.next_char\n      end\n      reader\n    end\n\n    # :nodoc:\n    def self.token?(char : Char) : Bool\n      !TSPECIAL_CHARACTERS.includes?(char) && 0x20 <= char.ord < 0x7F\n    end\n\n    # :nodoc:\n    def self.token?(string : String) : Bool\n      string.each_char.all? { |char| token? char }\n    end\n\n    # :nodoc:\n    def self.quote_string(string : String, io : IO) : Nil\n      string.each_char do |char|\n        case char\n        when '\"', '\\\\'\n          io << '\\\\'\n        when '\\u{00}'..'\\u{1F}', '\\u{7F}'\n          raise ArgumentError.new(\"String contained invalid character #{char.inspect}\")\n        else\n          # leave the byte as is\n        end\n        io << char\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/mime/multipart/builder.cr",
    "content": "module MIME::Multipart\n  # Builds a multipart MIME message.\n  #\n  # ### Example\n  #\n  # ```\n  # require \"mime/multipart\"\n  #\n  # io = IO::Memory.new # This is a stub. Actually, any IO can be used.\n  # multipart = MIME::Multipart::Builder.new(io)\n  # multipart.body_part HTTP::Headers{\"Content-Type\" => \"text/plain\"}, \"hello!\"\n  # multipart.finish\n  # io.to_s # => \"----------------------------DTf61dRTHYzprx7rwVQhTWr7\\r\\nContent-Type: text/plain\\r\\n\\r\\nhello!\\r\\n----------------------------DTf61dRTHYzprx7rwVQhTWr7--\"\n  # ```\n  class Builder\n    # Creates a new `Multipart::Builder` which writes the generated multipart\n    # message to *io*, using the multipart boundary *boundary*.\n    def initialize(@io : IO, @boundary = Multipart.generate_boundary)\n      @state = State::START\n    end\n\n    getter boundary\n\n    # Finite State Machine diagram: https://gist.github.com/RX14/221c1edfa98d1196711515d4b5c264eb\n\n    # Returns a content type header with multipart subtype *subtype*, and\n    # boundary parameter added.\n    #\n    # ```\n    # require \"mime/multipart\"\n    #\n    # io = IO::Memory.new # This is a stub. Actually, any IO can be used.\n    # builder = MIME::Multipart::Builder.new(io, \"a4VF\")\n    # builder.content_type(\"mixed\") # => \"multipart/mixed; boundary=a4VF\"\n    # ```\n    def content_type(subtype : String = \"mixed\") : String\n      MIME::MediaType.new(\"multipart/#{subtype}\", {\"boundary\" => @boundary}).to_s\n    end\n\n    # Appends *string* to the preamble segment of the multipart message. Throws\n    # if `#body_part` is called before this method.\n    #\n    # Can be called multiple times to append to the preamble multiple times.\n    def preamble(string : String) : Nil\n      preamble { |io| string.to_s(io) }\n    end\n\n    # Appends *data* to the preamble segment of the multipart message. Throws\n    # if `#body_part` is called before this method.\n    #\n    # Can be called multiple times to append to the preamble multiple times.\n    def preamble(data : Bytes) : Nil\n      preamble(&.write(data))\n    end\n\n    # Appends *preamble_io* to the preamble segment of the multipart message.\n    # Throws if `#body_part` is called before this method.\n    #\n    # Can be called multiple times to append to the preamble multiple times.\n    def preamble(preamble_io : IO) : Nil\n      preamble { |io| IO.copy(preamble_io, io) }\n    end\n\n    # Yields an IO that can be used to append to the preamble of the multipart\n    # message. Throws if `#body_part` is called before this method.\n    #\n    # Can be called multiple times to append to the preamble multiple times.\n    def preamble(&)\n      fail \"Cannot generate preamble: body already started\" unless @state.start? || @state.preamble?\n      yield @io\n      @state = State::PREAMBLE\n    end\n\n    # Appends a body part to the multipart message with the given *headers*\n    # and *string*. Throws if `#finish` or `#epilogue` is called before this\n    # method.\n    def body_part(headers : HTTP::Headers, string : String) : Nil\n      body_part_impl(headers) { |io| string.to_s(io) }\n    end\n\n    # Appends a body part to the multipart message with the given *headers*\n    # and *data*. Throws if `#finish` or `#epilogue` is called before this\n    # method.\n    def body_part(headers : HTTP::Headers, data : Bytes) : Nil\n      body_part_impl(headers, &.write(data))\n    end\n\n    # Appends a body part to the multipart message with the given *headers*\n    # and data from *body_io*. Throws if `#finish` or `#epilogue` is called\n    # before this method.\n    def body_part(headers : HTTP::Headers, body_io : IO) : Nil\n      body_part_impl(headers) { |io| IO.copy(body_io, io) }\n    end\n\n    # Yields an IO that can be used to write to a body part which is appended\n    # to the multipart message with the given *headers*. Throws if `#finish` or\n    # `#epilogue` is called before this method.\n    def body_part(headers : HTTP::Headers, &)\n      body_part_impl(headers) { |io| yield io }\n    end\n\n    # Appends a body part to the multipart message with the given *headers*\n    # and no body data. Throws is `#finish` or `#epilogue` is called before\n    # this method.\n    def body_part(headers : HTTP::Headers) : Nil\n      body_part_impl(headers, empty: true) { }\n    end\n\n    private def body_part_impl(headers, empty = false, &)\n      fail \"Cannot generate body part: already finished\" if @state.finished?\n      fail \"Cannot generate body part: after epilogue\" if @state.epilogue?\n\n      # We don't add a crlf before the first boundary if this is the first body\n      # part and there is no preamble\n      @io << \"\\r\\n\" unless @state.start?\n      @io << \"--\" << @boundary\n      headers.each do |name, values|\n        values.each do |value|\n          @io << \"\\r\\n\" << name << \": \" << value\n        end\n      end\n      @io << \"\\r\\n\\r\\n\" unless empty\n\n      yield @io\n\n      @state = State::BODY_PART\n    end\n\n    # Appends *string* to the epilogue segment of the multipart message. Throws\n    # if `#finish` is called before this method, or no body parts have been\n    # appended.\n    #\n    # Can be called multiple times to append to the epilogue multiple times.\n    def epilogue(string : String) : Nil\n      epilogue { |io| string.to_s(io) }\n    end\n\n    # Appends *data* to the epilogue segment of the multipart message. Throws\n    # if `#finish` is called before this method, or no body parts have been\n    # appended.\n    #\n    # Can be called multiple times to append to the epilogue multiple times.\n    def epilogue(data : Bytes) : Nil\n      epilogue(&.write(data))\n    end\n\n    # Appends *preamble_io* to the epilogue segment of the multipart message.\n    # Throws if `#finish` is called before this method, or no body parts have\n    # been appended.\n    #\n    # Can be called multiple times to append to the epilogue multiple times.\n    def epilogue(epilogue_io : IO) : Nil\n      epilogue { |io| IO.copy(epilogue_io, io) }\n    end\n\n    # Yields an IO that can be used to append to the epilogue of the multipart\n    # message. Throws if `#finish` is called before this method, or no body\n    # parts have been appended.\n    #\n    # Can be called multiple times to append to the preamble multiple times.\n    def epilogue(&)\n      case @state\n      in .start?, .preamble?\n        fail \"Cannot generate epilogue: no body parts\"\n      in .finished?\n        fail \"Cannot generate epilogue: already finished\"\n      in .epilogue?\n        # do nothing\n      in .body_part?\n        # We need to send the end boundary\n        @io << \"\\r\\n--\" << @boundary << \"--\\r\\n\"\n      in .errored?\n        fail \"BUG: unexpected error state\"\n      end\n\n      yield @io\n\n      @state = State::EPILOGUE\n    end\n\n    # Finalizes the multipart message, this method must be called to properly\n    # end the multipart message.\n    def finish : Nil\n      case @state\n      in .start?, .preamble?\n        fail \"Cannot finish multipart: no body parts\"\n      in .finished?\n        fail \"Cannot finish multipart: already finished\"\n      in .body_part?\n        # We need to send the end boundary\n        @io << \"\\r\\n--\" << @boundary << \"--\"\n      in .epilogue?\n        # do nothing\n      in .errored?\n        fail \"BUG: unexpected error state\"\n      end\n\n      @state = State::FINISHED\n      @io.flush\n    end\n\n    private def fail(msg)\n      raise Multipart::Error.new msg\n    end\n  end\nend\n"
  },
  {
    "path": "src/mime/multipart/parser.cr",
    "content": "module MIME::Multipart\n  # Parses multipart MIME messages.\n  #\n  # ### Example\n  #\n  # ```\n  # require \"mime/multipart\"\n  #\n  # multipart = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n  # parser = MIME::Multipart::Parser.new(IO::Memory.new(multipart), \"aA40\")\n  #\n  # while parser.has_next?\n  #   parser.next do |headers, io|\n  #     headers[\"Content-Type\"] # => \"text/plain\"\n  #     io.gets_to_end          # => \"body\"\n  #   end\n  # end\n  # ```\n  #\n  # Please note that the IO object yielded by `#next` is only valid until the\n  # block returns.\n  class Parser\n    # Creates a new `Multipart::Parser` which parses *io* with multipart\n    # boundary *boundary*.\n    def initialize(@io : IO, @boundary : String)\n      @state = State::PREAMBLE\n      @dash_boundary = \"--#{@boundary}\"\n      @delimiter = \"\\r\\n#{@dash_boundary}\"\n    end\n\n    # Parses the next body part and yields headers as `HTTP::Headers` and the\n    # body text as an `IO`.\n    #\n    # This method yields once instead of returning the values, because the IO\n    # object yielded to the block is only valid while the block is executing.\n    # The IO object will be closed as soon as the block returns. To store the\n    # content of the body part for longer than the block, the IO must be read\n    # into memory.\n    #\n    # ```\n    # require \"mime/multipart\"\n    #\n    # multipart = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n    # parser = MIME::Multipart::Parser.new(IO::Memory.new(multipart), \"aA40\")\n    # parser.next do |headers, io|\n    #   headers[\"Content-Type\"] # => \"text/plain\"\n    #   io.gets_to_end          # => \"body\"\n    # end\n    # ```\n    def next(&)\n      raise Multipart::Error.new \"Multipart parser already finished parsing\" if @state.finished?\n      raise Multipart::Error.new \"Multipart parser is in an errored state\" if @state.errored?\n\n      if @state.preamble?\n        # Discard preamble\n        preamble_io = IO::Delimited.new(@io, read_delimiter: @dash_boundary)\n        preamble_io.skip_to_end\n\n        fail(\"no parts\") if close_delimiter?\n        @state = State::BODY_PART\n      end\n\n      if @state.body_part?\n        body_io = IO::Delimited.new(@io, read_delimiter: @delimiter)\n        headers = parse_headers(body_io)\n\n        begin\n          yield headers, body_io\n        ensure\n          body_io.skip_to_end\n          body_io.close\n\n          @state = State::FINISHED if close_delimiter?\n        end\n      end\n    rescue ex\n      @state = State::ERRORED\n      raise ex\n    end\n\n    # True if `#next` can be called legally.\n    def has_next? : Bool\n      !@state.finished? && !@state.errored?\n    end\n\n    private def parse_headers(io)\n      headers = HTTP::Headers.new\n\n      while line = io.gets(chomp: false)\n        if line == \"\\r\\n\"\n          # Finished parsing\n          return headers\n        end\n\n        name, value = HTTP.parse_header(line)\n        headers.add(name, value)\n      end\n\n      headers\n    end\n\n    # This method is used directly after reading a boundary, to determine if\n    # it's a close delimiter or not.\n    #\n    # If it's not a close delimiter, it eats the transport padding and crlf\n    # after a delimiter.\n    private def close_delimiter?\n      transport_padding_crlf = @io.gets(\"\\r\\n\")\n      fail(\"EOF reading delimiter\") unless transport_padding_crlf\n\n      if transport_padding_crlf != \"\\r\\n\"\n        return true if transport_padding_crlf.starts_with?(\"--\")\n\n        fail(\"EOF reading delimiter padding\") unless transport_padding_crlf.ends_with?(\"\\r\\n\")\n\n        0.upto(transport_padding_crlf.bytesize - 3) do |i| # 3 constant to ignore \"\\r\\n\" at end\n          byte = transport_padding_crlf.to_unsafe[i]\n          fail(\"padding contained non-whitespace character\") unless byte.in?(' '.ord, '\\t'.ord)\n        end\n      end\n\n      false\n    end\n\n    private def fail(msg)\n      raise Multipart::Error.new \"Failed to parse multipart message: \" + msg\n    end\n  end\nend\n"
  },
  {
    "path": "src/mime/multipart/state.cr",
    "content": "module MIME::Multipart\n  private enum State\n    START\n    PREAMBLE\n    BODY_PART\n    EPILOGUE\n    FINISHED\n    ERRORED\n  end\nend\n"
  },
  {
    "path": "src/mime/multipart.cr",
    "content": "require \"random/secure\"\nrequire \"./multipart/*\"\nrequire \"mime/media_type\"\n\n# The `MIME::Multipart` module contains utilities for parsing MIME multipart\n# messages, which contain multiple body parts, each containing a header section\n# and binary body. The `multipart/form-data` content-type has a separate set of\n# utilities in the `HTTP::FormData` module.\nmodule MIME::Multipart\n  # Parses a MIME multipart message, yielding `HTTP::Headers` and an `IO` for\n  # each body part.\n  #\n  # Please note that the IO object yielded to the block is only valid while the\n  # block is executing. The IO is closed as soon as the supplied block returns.\n  #\n  # ```\n  # require \"mime/multipart\"\n  #\n  # multipart = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n  # MIME::Multipart.parse(IO::Memory.new(multipart), \"aA40\") do |headers, io|\n  #   headers[\"Content-Type\"] # => \"text/plain\"\n  #   io.gets_to_end          # => \"body\"\n  # end\n  # ```\n  #\n  # See: `Multipart::Parser`\n  def self.parse(io, boundary, &)\n    parser = Parser.new(io, boundary)\n    while parser.has_next?\n      parser.next { |headers, io| yield headers, io }\n    end\n  end\n\n  # Extracts the multipart boundary from the Content-Type header. May return\n  # `nil` is the boundary was not found.\n  #\n  # ```\n  # require \"mime/multipart\"\n  #\n  # MIME::Multipart.parse_boundary(\"multipart/mixed; boundary=\\\"abcde\\\"\") # => \"abcde\"\n  # ```\n  def self.parse_boundary(content_type : String) : String?\n    type = MIME::MediaType.parse?(content_type)\n\n    if type && type.type == \"multipart\"\n      type[\"boundary\"]?.presence\n    end\n  end\n\n  # Parses a MIME multipart message, yielding `HTTP::Headers` and an `IO` for\n  # each body part.\n  #\n  # Please note that the IO object yielded to the block is only valid while the\n  # block is executing. The IO is closed as soon as the supplied block returns.\n  #\n  # ```\n  # require \"http\"\n  # require \"mime/multipart\"\n  #\n  # headers = HTTP::Headers{\"Content-Type\" => \"multipart/mixed; boundary=aA40\"}\n  # body = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n  # request = HTTP::Request.new(\"POST\", \"/\", headers, body)\n  #\n  # MIME::Multipart.parse(request) do |headers, io|\n  #   headers[\"Content-Type\"] # => \"text/plain\"\n  #   io.gets_to_end          # => \"body\"\n  # end\n  # ```\n  #\n  # See: `Multipart::Parser`\n  def self.parse(request : HTTP::Request, &)\n    if content_type = request.headers[\"Content-Type\"]?\n      boundary = parse_boundary(content_type)\n    end\n    return nil unless boundary\n\n    body = request.body\n    return nil unless body\n    parse(body, boundary) { |headers, io| yield headers, io }\n  end\n\n  # Parses a MIME multipart message, yielding `HTTP::Headers` and an `IO` for\n  # each body part.\n  #\n  # Please note that the IO object yielded to the block is only valid while the\n  # block is executing. The IO is closed as soon as the supplied block returns.\n  #\n  # ```\n  # require \"http\"\n  # require \"mime/multipart\"\n  #\n  # headers = HTTP::Headers{\"Content-Type\" => \"multipart/byteranges; boundary=aA40\"}\n  # body = \"--aA40\\r\\nContent-Type: text/plain\\r\\n\\r\\nbody\\r\\n--aA40--\"\n  # response = HTTP::Client::Response.new(\n  #   status: :ok,\n  #   headers: headers,\n  #   body: body,\n  # )\n  #\n  # MIME::Multipart.parse(response) do |headers, io|\n  #   headers[\"Content-Type\"] # => \"text/plain\"\n  #   io.gets_to_end          # => \"body\"\n  # end\n  # ```\n  #\n  # See: `Multipart::Parser`\n  def self.parse(response : HTTP::Client::Response, &)\n    if content_type = response.headers[\"Content-Type\"]?\n      boundary = parse_boundary(content_type)\n    end\n    return nil unless boundary\n\n    if body = response.body.presence\n      body = IO::Memory.new(body)\n    else\n      body = response.body_io?\n    end\n    return nil unless body\n\n    parse(body, boundary) { |headers, io| yield headers, io }\n  end\n\n  # Yields a `Multipart::Builder` to the given block, writing to *io* and\n  # using *boundary*. `#finish` is automatically called on the builder.\n  def self.build(io : IO, boundary : String = Multipart.generate_boundary, &)\n    builder = Builder.new(io, boundary)\n    yield builder\n    builder.finish\n  end\n\n  # Yields a `Multipart::Builder` to the given block, returning the generated\n  # message as a `String`.\n  def self.build(boundary : String = Multipart.generate_boundary, &)\n    String.build do |io|\n      build(io, boundary) { |g| yield g }\n    end\n  end\n\n  # Returns a unique string suitable for use as a multipart boundary.\n  #\n  # ```\n  # require \"mime/multipart\"\n  #\n  # MIME::Multipart.generate_boundary # => \"---------------------------dQu6bXHYb4m5zrRC3xPTGwV\"\n  # ```\n  def self.generate_boundary : String\n    \"--------------------------#{Random::Secure.urlsafe_base64(18)}\"\n  end\n\n  class Error < Exception\n  end\nend\n"
  },
  {
    "path": "src/mime.cr",
    "content": "require \"crystal/system/mime\"\n\n# This module implements a global MIME registry.\n#\n# NOTE: To use `MIME`, you must explicitly import it with `require \"mime\"`\n#\n# ```\n# require \"mime\"\n#\n# MIME.from_extension(\".html\")         # => \"text/html\"\n# MIME.from_filename(\"path/file.html\") # => \"text/html\"\n# ```\n#\n# The registry will be populated with some default values (see `DEFAULT_TYPES`)\n# as well as the operating system's MIME database.\n#\n# Default initialization can be skipped by calling `MIME.init(false)` before the first\n# query to the MIME database.\n#\n# ## OS-provided MIME database\n#\n# On a POSIX system, the following files are tried to be read in sequential order,\n# stopping at the first existing file. These values override those from `DEFAULT_TYPES`.\n#\n# ```plain\n# /etc/mime.types\n# /etc/httpd/mime.types                    # Mac OS X\n# /etc/httpd/conf/mime.types               # Apache\n# /etc/apache/mime.types                   # Apache 1\n# /etc/apache2/mime.types                  # Apache 2\n# /usr/local/etc/httpd/conf/mime.types\n# /usr/local/lib/netscape/mime.types\n# /usr/local/etc/httpd/conf/mime.types     # Apache 1.2\n# /usr/local/etc/mime.types                # FreeBSD\n# /usr/share/misc/mime.types               # OpenBSD\n# ```\n#\n# ## Registering custom MIME types\n#\n# Applications can register their own MIME types:\n#\n# ```\n# require \"mime\"\n#\n# MIME.from_extension?(\".cr\")     # => nil\n# MIME.extensions(\"text/crystal\") # => Set(String).new\n#\n# MIME.register(\".cr\", \"text/crystal\")\n# MIME.from_extension?(\".cr\")     # => \"text/crystal\"\n# MIME.extensions(\"text/crystal\") # => Set(String){\".cr\"}\n# ```\n#\n# ## Loading a custom MIME database\n#\n# To load a custom MIME database, `load_mime_database` can be called with an\n# `IO` to read the database from.\n#\n# ```\n# require \"mime\"\n#\n# # Load user-defined MIME types\n# File.open(\"~/.mime.types\") do |io|\n#   MIME.load_mime_database(io)\n# end\n# ```\n#\n# Loaded values override previously defined mappings.\n#\n# The data format must follow the format of `mime.types`: Each line declares\n# a MIME type followed by a whitespace-separated list of extensions mapped to\n# this type. Everything following a `#` is considered a comment until the end of\n# line. Empty lines are ignored.\n#\n# ```plain\n# text/html html htm\n#\n# # comment\n# ```\nmodule MIME\n  class Error < Exception\n  end\n\n  @@initialized = false\n  @@types = {} of String => String\n  @@types_lower = {} of String => String\n  @@extensions = {} of String => Set(String)\n\n  # A limited set of default MIME types.\n  DEFAULT_TYPES = {\n    \".css\"  => \"text/css; charset=utf-8\",\n    \".gif\"  => \"image/gif\",\n    \".htm\"  => \"text/html; charset=utf-8\",\n    \".html\" => \"text/html; charset=utf-8\",\n    \".jpg\"  => \"image/jpeg\",\n    \".jpeg\" => \"image/jpeg\",\n    \".js\"   => \"text/javascript; charset=utf-8\",\n    \".json\" => \"application/json\",\n    \".pdf\"  => \"application/pdf\",\n    \".png\"  => \"image/png\",\n    \".svg\"  => \"image/svg+xml\",\n    \".txt\"  => \"text/plain; charset=utf-8\",\n    \".xml\"  => \"text/xml; charset=utf-8\",\n    \".wasm\" => \"application/wasm\",\n    \".webp\" => \"image/webp\",\n  }\n\n  # Initializes the MIME database.\n  #\n  # The default behaviour is to load the internal defaults as well as the OS-provided\n  # MIME database. This can be disabled with *load_defaults* set to `false`.\n  #\n  # This method usually doesn't need to be called explicitly when the default behaviour is expected.\n  # It will be called implicitly with `load_defaults: true` when a query method\n  # is called and the MIME database has not been initialized before.\n  #\n  # Calling this method repeatedly is allowed.\n  def self.init(load_defaults : Bool = true) : Nil\n    @@initialized = true\n\n    if load_defaults\n      DEFAULT_TYPES.each do |ext, type|\n        register ext, type\n      end\n\n      Crystal::System::MIME.load\n    end\n  end\n\n  # Initializes the MIME database loading contents from a file.\n  #\n  # This will neither load the internal defaults nor the OS-provided MIME database,\n  # only the database at *filename* (using `.load_mime_database`).\n  #\n  # Calling this method repeatedly is allowed.\n  def self.init(filename : String) : Nil\n    init(load_defaults: false)\n\n    File.open(filename, \"r\") do |file|\n      load_mime_database(file)\n    end\n  end\n\n  private def self.initialize_types\n    init unless @@initialized\n  end\n\n  # Looks up the MIME type associated with *extension*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Returns *default* if *extension* is not registered.\n  def self.from_extension(extension : String, default : String) : String\n    from_extension(extension) { default }\n  end\n\n  # Looks up the MIME type associated with *extension*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Raises `KeyError` if *extension* is not registered.\n  def self.from_extension(extension : String) : String\n    from_extension(extension) { raise KeyError.new(\"Missing MIME type for extension #{extension.inspect}\") }\n  end\n\n  # Looks up the MIME type associated with *extension*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Returns `nil` if *extension* is not registered.\n  def self.from_extension?(extension : String) : String?\n    from_extension(extension) { nil }\n  end\n\n  # Looks up the MIME type associated with *extension*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Runs the given block if *extension* is not registered.\n  def self.from_extension(extension : String, &block)\n    initialize_types\n\n    @@types.fetch(extension) { @@types_lower.fetch(extension.downcase) { yield extension } }\n  end\n\n  # Looks up the MIME type associated with the extension in *filename*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Returns *default* if extension is not registered.\n  def self.from_filename(filename : String | Path, default : String) : String\n    from_extension(File.extname(filename.to_s), default)\n  end\n\n  # Looks up the MIME type associated with the extension in *filename*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Raises `KeyError` if extension is not registered.\n  def self.from_filename(filename : String | Path) : String\n    from_extension(File.extname(filename.to_s))\n  end\n\n  # Looks up the MIME type associated with the extension in *filename*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Returns `nil` if extension is not registered.\n  def self.from_filename?(filename : String | Path) : String?\n    from_extension?(File.extname(filename.to_s))\n  end\n\n  # Looks up the MIME type associated with the extension in *filename*.\n  #\n  # A case-sensitive search is tried first, if this yields no result, it is\n  # matched case-insensitive. Runs the given block if extension is not registered.\n  def self.from_filename(filename : String | Path, &block)\n    from_extension(File.extname(filename.to_s)) { |extension| yield extension }\n  end\n\n  # Register *type* for *extension*.\n  #\n  # *extension* must start with a dot (`.`) and must not contain any null bytes.\n  def self.register(extension : String, type : String) : Nil\n    raise ArgumentError.new(\"Extension does not start with a dot: #{extension.inspect}\") unless extension.starts_with?('.')\n    extension.check_no_null_byte\n\n    initialize_types\n\n    # If the same extension had a different type registered before, it needs to\n    # be removed from the extensions list.\n    if previous_type = @@types[extension]?\n      if extensions = @@extensions[parse_media_type(previous_type)]?\n        extensions.delete(extension)\n      end\n    end\n\n    mediatype = parse_media_type(type) || raise ArgumentError.new \"Invalid media type: #{type}\"\n\n    @@types[extension] = type\n    @@types_lower[extension.downcase] = type\n\n    @@extensions.put_if_absent(mediatype) { Set(String).new } << extension\n  end\n\n  # Returns all extensions registered for *type*.\n  def self.extensions(type : String) : Set(String)\n    initialize_types\n\n    @@extensions.fetch(type) { Set(String).new }\n  end\n\n  # tspecial as defined by RFC 1521 and RFC 2045.\n  private TSPECIAL_CHARACTERS = {'(', ')', '<', '>', '@', ',', ';', ':', '\\\\', '\"', '/', '[', ']', '?', '='}\n\n  private def self.parse_media_type(type : String) : String?\n    reader = Char::Reader.new(type)\n\n    sub_type_start = -1\n    while reader.has_next?\n      case char = reader.current_char\n      when ';'\n        break\n      when '/'\n        return nil if sub_type_start > -1\n        sub_type_start = reader.pos\n        reader.next_char\n      else\n        if TSPECIAL_CHARACTERS.includes?(char) || 0x20 > char.ord > 0x7F\n          return nil\n        end\n\n        reader.next_char\n      end\n    end\n\n    if reader.pos == 0\n      return nil\n    end\n\n    type.byte_slice(0, reader.pos).strip.downcase\n  end\n\n  # Reads MIME type mappings from *io* and registers the extension-to-type\n  # relation (see `.register`).\n  #\n  # The format follows that of `mime.types`: Each line is list of MIME type and\n  # zero or more extensions, separated by whitespace.\n  def self.load_mime_database(io : IO) : Nil\n    while line = io.gets\n      fields = line.split\n\n      fields.each_with_index do |field, i|\n        extension = field\n        break if extension.starts_with?('#')\n        next if i == 0 # first index contains the media type\n\n        register \".#{extension}\", fields[0]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/mutex.cr",
    "content": "require \"./sync/mutex\"\n\n# A fiber-safe mutex.\n#\n# Provides deadlock protection by default. Attempting to re-lock the mutex from\n# the same fiber will raise an exception. Trying to unlock an unlocked mutex, or\n# a mutex locked by another fiber will raise an exception.\n#\n# The reentrant protection maintains a lock count. Attempting to re-lock the\n# mutex from the same fiber will increment the lock count. Attempting to unlock\n# the counter from the same fiber will decrement the lock count, eventually\n# releasing the lock when the lock count returns to 0. Attempting to unlock an\n# unlocked mutex, or a mutex locked by another fiber will raise an exception.\n#\n# You also disable all protections with `unchecked`. Attempting to re-lock the\n# mutex from the same fiber will deadlock. Any fiber can unlock the mutex, even\n# if it wasn't previously locked.\n#\n# WARNING: `Mutex` is deprecated as of Crystal 1.20. Use `Sync::Mutex` instead.\nalias Mutex = Sync::Mutex\n\n# WARNING: `Mutex::Protection` is deprecated as of Crystal 1.20. Use `Sync::Type` instead.\nalias Mutex::Protection = Sync::Type\n"
  },
  {
    "path": "src/named_tuple.cr",
    "content": "# A named tuple is a fixed-size, immutable, stack-allocated mapping\n# of a fixed set of keys to values.\n#\n# You can think of a `NamedTuple` as an immutable `Hash` whose keys (which\n# are of type `Symbol`), and the types for each key, are known at compile time.\n#\n# A named tuple can be created with a named tuple literal:\n#\n# ```\n# language = {name: \"Crystal\", year: 2011} # NamedTuple(name: String, year: Int32)\n#\n# language[:name]  # => \"Crystal\"\n# language[:year]  # => 2011\n# language[:other] # compile time error\n# ```\n#\n# See [`NamedTuple` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/named_tuple.html) in the language reference.\n#\n# The compiler knows what types are in each key, so when indexing a named tuple\n# with a symbol or string literal the compiler will return the value for that\n# key and with the expected type, like in the above snippet. Indexing with a\n# symbol or string literal for which there's no key will give a compile-time\n# error.\n#\n# Indexing with a symbol or string that is only known at runtime will return\n# a value whose type is the union of all the types in the named tuple,\n# and might raise `KeyError`.\n#\n# Indexing with `#[]?` does not make the return value nilable if the key is\n# known to exist:\n#\n# ```\n# language = {name: \"Crystal\", year: 2011}\n# language[:name]?         # => \"Crystal\"\n# typeof(language[:name]?) # => String\n# ```\n#\n# `NamedTuple`'s own instance classes may also be indexed in a similar manner,\n# returning their value types instead:\n#\n# ```\n# tuple = NamedTuple(name: String, year: Int32)\n# tuple[:name]   # => String\n# tuple[\"year\"]  # => Int32\n# tuple[:other]? # => nil\n# ```\nstruct NamedTuple\n  # Creates a named tuple that will contain the given arguments.\n  #\n  # With a named tuple literal you cannot create an empty named tuple.\n  # This method doesn't have this limitation, which makes it especially\n  # useful in macros and generic code.\n  #\n  # ```\n  # NamedTuple.new(name: \"Crystal\", year: 2011) #=> {name: \"Crystal\", year: 2011}\n  # NamedTuple.new # => {}\n  # {}             # syntax error\n  # ```\n  def self.new(**options : **T)\n    {% if @type.name(generic_args: false) == \"NamedTuple\" %}\n      # deduced type vars\n      options\n    {% elsif @type.name(generic_args: false) == \"NamedTuple()\" %}\n      # special case: empty named tuple\n      # TODO: check against `NamedTuple()` directly after 1.5.0\n      options\n    {% else %}\n      # explicitly provided type vars\n      # following `typeof` is needed to access private types\n      {% begin %}\n        {\n          {% for key in T %}\n            {{ key.stringify }}: options[{{ key.symbolize }}].as(typeof(element_type({{ key.symbolize }}))),\n          {% end %}\n        }\n      {% end %}\n    {% end %}\n  end\n\n  # Creates a named tuple from the given hash, with elements casted to the given types.\n  # Here the Int32 | String union is cast to Int32.\n  #\n  # ```\n  # num_or_str = 42.as(Int32 | String)\n  # NamedTuple(name: String, val: Int32).from({\"name\" => \"number\", \"val\" => num_or_str}) # => {name: \"number\", val: 42}\n  #\n  # num_or_str = \"a string\".as(Int32 | String)\n  # NamedTuple(name: String, val: Int32).from({\"name\" => \"number\", \"val\" => num_or_str}) # raises TypeCastError (Cast from String to Int32 failed)\n  # ```\n  # See also: `#from`.\n  def self.from(hash : Hash) : self\n    {% begin %}\n    NamedTuple.new(**{{T}}).from(hash)\n    {% end %}\n  end\n\n  # Expects to be called on a named tuple whose values are types, creates a tuple from the given hash,\n  # with types casted appropriately. The hash keys must be either symbols or strings.\n  #\n  # This allows you to easily pass a hash as individual named arguments to a method.\n  #\n  # ```\n  # require \"json\"\n  #\n  # def speak_about(thing : String, n : Int64)\n  #   \"I see #{n} #{thing}s\"\n  # end\n  #\n  # hash = JSON.parse(%({\"thing\": \"world\", \"n\": 2})).as_h # hash : Hash(String, JSON::Any)\n  # hash = hash.transform_values(&.raw)                   # hash : Hash(String, JSON::Any::Type)\n  #\n  # speak_about(**{thing: String, n: Int64}.from(hash)) # => \"I see 2 worlds\"\n  # ```\n  def from(hash : Hash)\n    if size != hash.size\n      raise ArgumentError.new(\"Expected a hash with #{size} keys but one with #{hash.size} keys was given.\")\n    end\n\n    {% begin %}\n      NamedTuple.new(\n      {% for key, value in T %}\n        {{key.stringify}}: self[{{key.symbolize}}].cast(hash.fetch({{key.symbolize}}) { hash[{{key.stringify}}] }),\n      {% end %}\n      )\n    {% end %}\n  end\n\n  # Returns the value for the given *key* if there is such a key, otherwise\n  # raises `KeyError`.\n  # Read the type docs to understand the difference between indexing with a\n  # literal or a variable.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  #\n  # tuple[:name]          # => \"Crystal\"\n  # typeof(tuple[:name])  # => String\n  # tuple[\"year\"]         # => 2011\n  # typeof(tuple[\"year\"]) # => Int32\n  # tuple[:other]         # Error: missing key 'other' for named tuple NamedTuple(name: String, year: Int32)\n  #\n  # key = :name\n  # tuple[key]         # => \"Crystal\"\n  # typeof(tuple[key]) # => (Int32 | String)\n  #\n  # key = \"year\"\n  # tuple[key] # => 2011\n  #\n  # key = :other\n  # tuple[key] # raises KeyError\n  # ```\n  def [](key : Symbol | String)\n    fetch(key) { raise KeyError.new \"Missing named tuple key: #{key.inspect}\" }\n  end\n\n  # Returns the value type for the given *key* if there is such a key, otherwise\n  # raises `KeyError`.\n  # Read the type docs to understand the difference between indexing with a\n  # literal or a variable.\n  #\n  # ```\n  # alias Foo = NamedTuple(name: String, year: Int32)\n  #\n  # Foo[:name]       # => String\n  # Foo[\"year\"]      # => Int32\n  # Foo[\"year\"].zero # => 0\n  # Foo[:other]      # => Error: missing key 'other' for named tuple NamedTuple(name: String, year: Int32).class\n  #\n  # key = :year\n  # Foo[key]      # => Int32\n  # Foo[key].zero # Error: undefined method 'zero' for String.class (compile-time type is (Int32.class | String.class))\n  #\n  # key = \"other\"\n  # Foo[key] # raises KeyError\n  # ```\n  def self.[](key : Symbol | String)\n    self[key]? || raise KeyError.new \"Missing named tuple key: #{key.inspect}\"\n  end\n\n  # Returns the value for the given *key* if there is such a key, otherwise\n  # returns `nil`.\n  # Read the type docs to understand the difference between indexing with a\n  # literal or a variable.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  #\n  # tuple[:name]?          # => \"Crystal\"\n  # typeof(tuple[:name]?)  # => String\n  # tuple[\"year\"]?         # => 2011\n  # typeof(tuple[\"year\"]?) # => Int32\n  # tuple[:other]?         # => nil\n  # typeof(tuple[:other]?) # => Nil\n  #\n  # key = :name\n  # tuple[key]?         # => \"Crystal\"\n  # typeof(tuple[key]?) # => (Int32 | String | Nil)\n  #\n  # key = \"year\"\n  # tuple[key]? # => 2011\n  #\n  # key = :other\n  # tuple[key]? # => nil\n  # ```\n  def []?(key : Symbol | String)\n    fetch(key, nil)\n  end\n\n  # Returns the value type for the given *key* if there is such a key, otherwise\n  # returns `nil`.\n  # Read the type docs to understand the difference between indexing with a\n  # literal or a variable.\n  #\n  # ```\n  # alias Foo = NamedTuple(name: String, year: Int32)\n  #\n  # Foo[:name]?          # => String\n  # Foo[\"year\"]?         # => Int32\n  # Foo[\"year\"]?.zero    # => 0\n  # Foo[:other]?         # => nil\n  # typeof(Foo[:other]?) # => Nil\n  #\n  # key = :year\n  # Foo[key]?      # => Int32\n  # Foo[key]?.zero # Error: undefined method 'zero' for String.class (compile-time type is (Int32.class | String.class | Nil))\n  #\n  # key = \"other\"\n  # Foo[key]? # => nil\n  # ```\n  def self.[]?(key : Symbol | String)\n    # following `typeof` is needed to access private types\n    {% begin %}\n      case key\n      {% for key in T %}\n      when {{ key.symbolize }}, {{ key.stringify }}\n        typeof(element_type({{ key.symbolize }}))\n      {% end %}\n      end\n    {% end %}\n  end\n\n  # Traverses the depth of a structure and returns the value.\n  # Returns `nil` if not found.\n  #\n  # ```\n  # h = {a: {b: {c: [10, 20]}}, x: {a: \"b\"}}\n  # h.dig? :a, :b, :c # => [10, 20]\n  # h.dig? \"a\", \"x\"   # => nil\n  # ```\n  def dig?(key : Symbol | String, *subkeys)\n    if (value = self[key]?) && value.responds_to?(:dig?)\n      value.dig?(*subkeys)\n    end\n  end\n\n  # :nodoc:\n  def dig?(key : Symbol | String)\n    self[key]?\n  end\n\n  # Traverses the depth of a structure and returns the value, otherwise\n  # raises `KeyError`.\n  #\n  # ```\n  # h = {a: {b: {c: [10, 20]}}, x: {a: \"b\"}}\n  # h.dig :a, :b, :c # => [10, 20]\n  # h.dig \"a\", \"x\"   # raises KeyError\n  # ```\n  def dig(key : Symbol | String, *subkeys)\n    if (value = self[key]) && value.responds_to?(:dig)\n      return value.dig(*subkeys)\n    end\n    raise KeyError.new \"NamedTuple value not diggable for key: #{key.inspect}\"\n  end\n\n  # :nodoc:\n  def dig(key : Symbol | String)\n    self[key]\n  end\n\n  # Returns the value for the given *key*, if there's such key, otherwise returns *default_value*.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.fetch(:name, \"Unknown\") # => \"Crystal\"\n  # tuple.fetch(\"year\", 0)        # => 2011\n  # tuple.fetch(:other, 0)        # => 0\n  # ```\n  def fetch(key : Symbol | String, default_value)\n    fetch(key) { default_value }\n  end\n\n  # Returns the value for the given *key*, if there's such key, otherwise the value returned by the block.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.fetch(:name) { \"Unknown\" } # => \"Crystal\"\n  # tuple.fetch(:other) { 0 }        # => 0\n  # ```\n  def fetch(key : Symbol, &block)\n    {% for key in T %}\n      return self[{{key.symbolize}}] if {{key.symbolize}} == key\n    {% end %}\n    yield\n  end\n\n  # Returns the value for the given *key*, if there's such key, otherwise the value returned by the block.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.fetch(\"name\") { \"Unknown\" } # => \"Crystal\"\n  # tuple.fetch(\"other\") { 0 }        # => 0\n  # ```\n  def fetch(key : String, &block)\n    {% for key in T %}\n      return self[{{key.symbolize}}] if {{key.stringify}} == key\n    {% end %}\n    yield\n  end\n\n  # Merges two named tuples into one, returning a new named tuple.\n  # If a key is defined in both tuples, the value and its type is used from *other*.\n  #\n  # ```\n  # a = {foo: \"Hello\", bar: \"Old\"}\n  # b = {bar: \"New\", baz: \"Bye\"}\n  # a.merge(b) # => {foo: \"Hello\", bar: \"New\", baz: \"Bye\"}\n  # ```\n  def merge(other : NamedTuple)\n    merge(**other)\n  end\n\n  # :ditto:\n  def merge(**other : **U) forall U\n    {% begin %}\n    NamedTuple.new(\n      {% for k in T %} {% unless U.keys.includes?(k) %} {{k.stringify}}: self[{{k.symbolize}}],{% end %} {% end %}\n      {% for k in U %} {{k.stringify}}: other[{{k.symbolize}}], {% end %}\n    )\n    {% end %}\n  end\n\n  # Merges two named tuples into one, returning a new named tuple.\n  # If a key is defined in both tuples, the value and its type are used from **self** (original tuple)\n  #\n  # ```\n  # a = {foo: \"Hello\", bar: \"Old\"}\n  # b = {bar: \"New\", baz: \"Bye\"}\n  # a.reverse_merge(b) # => {foo: \"Hello\", bar: \"Old\", baz: \"Bye\"}\n  # ```\n  def reverse_merge(other : NamedTuple)\n    other.merge(**self)\n  end\n\n  # :ditto:\n  def reverse_merge(**other)\n    reverse_merge(other)\n  end\n\n  # Returns a hash value based on this name tuple's size, keys and values.\n  #\n  # See also: `Object#hash`.\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    {% for key in T.keys.sort %}\n      hasher = {{key.symbolize}}.hash(hasher)\n      hasher = self[{{key.symbolize}}].hash(hasher)\n    {% end %}\n    hasher\n  end\n\n  # Returns the types of this named tuple type.\n  #\n  # ```\n  # tuple = {a: 1, b: \"hello\", c: 'x'}\n  # tuple.class.types # => {a: Int32, b: String, c: Char}\n  # ```\n  def self.types\n    NamedTuple.new(**{{T}})\n  end\n\n  # Same as `to_s`.\n  def inspect : String\n    to_s\n  end\n\n  # Returns a `Tuple` of symbols with the keys in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.keys # => {:name, :year}\n  # ```\n  def keys\n    {% begin %}\n      Tuple.new(\n        {% for key in T %}\n          {{key.symbolize}},\n        {% end %}\n      )\n    {% end %}\n  end\n\n  # Returns a `Tuple` of symbols with the keys in this named tuple, sorted by name.\n  #\n  # ```\n  # tuple = {foo: 1, bar: 2, baz: 3}\n  # tuple.sorted_keys # => {:bar, :baz, :foo}\n  # ```\n  def sorted_keys\n    {% begin %}\n      Tuple.new(\n        {% for key in T.keys.sort %}\n          {{key.symbolize}},\n        {% end %}\n      )\n    {% end %}\n  end\n\n  # Returns a `Tuple` with the values in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.values # => {\"Crystal\", 2011}\n  # ```\n  def values\n    {% begin %}\n      Tuple.new(\n        {% for key in T %}\n          self[{{key.symbolize}}],\n        {% end %}\n      )\n    {% end %}\n  end\n\n  # Returns `true` if this named tuple has the given *key*, `false` otherwise.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.has_key?(:name)  # => true\n  # tuple.has_key?(:other) # => false\n  # ```\n  def has_key?(key : Symbol) : Bool\n    {% for key in T %}\n      return true if {{key.symbolize}} == key\n    {% end %}\n    false\n  end\n\n  # :ditto:\n  def has_key?(key : String) : Bool\n    {% for key in T %}\n      return true if {{key.stringify}} == key\n    {% end %}\n    false\n  end\n\n  # Appends a string representation of this named tuple to the given `IO`.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.to_s # => %({name: \"Crystal\", year: 2011})\n  # ```\n  def to_s(io : IO) : Nil\n    io << '{'\n    {% for key, value, i in T %}\n      {% if i > 0 %}\n        io << \", \"\n      {% end %}\n      Symbol.quote_for_named_argument io, {{key.stringify}}\n      io << \": \"\n      self[{{key.symbolize}}].inspect(io)\n    {% end %}\n    io << '}'\n  end\n\n  def pretty_print(pp)\n    pp.surround(\"{\", \"}\", left_break: nil, right_break: nil) do\n      {% for key, value, i in T %}\n        {% if i > 0 %}\n          pp.comma\n        {% end %}\n        pp.group do\n          pp.text Symbol.quote_for_named_argument({{key.stringify}})\n          pp.text \": \"\n          pp.nest do\n            pp.breakable \"\"\n            self[{{key.symbolize}}].pretty_print(pp)\n          end\n        end\n      {% end %}\n    end\n  end\n\n  # Yields each key and value in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.each do |key, value|\n  #   puts \"#{key} = #{value}\"\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # name = Crystal\n  # year = 2011\n  # ```\n  def each(&) : Nil\n    {% for key in T %}\n      yield {{key.symbolize}}, self[{{key.symbolize}}]\n    {% end %}\n  end\n\n  # Yields each key in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.each_key do |key|\n  #   puts key\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # name\n  # year\n  # ```\n  def each_key(&) : Nil\n    {% for key in T %}\n      yield {{key.symbolize}}\n    {% end %}\n  end\n\n  # Yields each value in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.each_value do |value|\n  #   puts value\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # Crystal\n  # 2011\n  # ```\n  def each_value(&) : Nil\n    {% for key in T %}\n      yield self[{{key.symbolize}}]\n    {% end %}\n  end\n\n  # Yields each key and value, together with an index starting at *offset*, in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.each_with_index do |key, value, i|\n  #   puts \"#{i + 1}) #{key} = #{value}\"\n  # end\n  # ```\n  #\n  # Output:\n  #\n  # ```text\n  # 1) name = Crystal\n  # 2) year = 2011\n  # ```\n  def each_with_index(offset = 0, &)\n    i = offset\n    each do |key, value|\n      yield key, value, i\n      i += 1\n    end\n  end\n\n  # Returns an `Array` populated with the results of each iteration in the given block,\n  # which is given each key and value in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.map { |k, v| \"#{k}: #{v}\" } # => [\"name: Crystal\", \"year: 2011\"]\n  # ```\n  def map(&)\n    {% if T.size == 0 %}\n      [] of NoReturn\n    {% else %}\n      [\n        {% for key in T %}\n          (yield {{ key.symbolize }}, self[{{ key.symbolize }}]),\n        {% end %}\n      ]\n    {% end %}\n  end\n\n  # Returns a new `Array` of tuples populated with each key-value pair.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.to_a # => [{:name, \"Crystal\"}, {:year, 2011}]\n  # ```\n  #\n  # NOTE: `to_a` on an empty named tuple produces an `Array(Tuple(Symbol, NoReturn))`\n  def to_a\n    to_a(&.itself)\n  end\n\n  # Returns an `Array` with the results of running *block* against tuples with key and values belonging\n  # to this `NamedTuple`.\n  #\n  # ```\n  # tuple = {first_name: \"foo\", last_name: \"bar\"}\n  # tuple.to_a(&.last.capitalize) # => [\"Foo\", \"Bar\"]\n  # ```\n  #\n  # NOTE: `to_a` on an empty named tuple produces an `Array(Tuple(Symbol, NoReturn))`\n  def to_a(&)\n    {% if T.size == 0 %}\n      [] of {Symbol, NoReturn}\n    {% else %}\n      [\n        {% for key in T %}\n          yield({ {{key.symbolize}}, self[{{key.symbolize}}] }),\n        {% end %}\n      ]\n    {% end %}\n  end\n\n  # Returns a `Hash` with the keys and values in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.to_h # => {:name => \"Crystal\", :year => 2011}\n  # ```\n  #\n  # NOTE: `to_h` on an empty named tuple produces a `Hash(Symbol, NoReturn)`\n  def to_h\n    {% if T.size == 0 %}\n      {} of Symbol => NoReturn\n    {% else %}\n      {\n        {% for key in T %}\n          {{key.symbolize}} => self[{{key.symbolize}}],\n        {% end %}\n      }\n    {% end %}\n  end\n\n  # Returns the number of elements in this named tuple.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.size # => 2\n  # ```\n  def size\n    {{T.size}}\n  end\n\n  # Returns `true` if this named tuple is empty.\n  #\n  # ```\n  # tuple = {name: \"Crystal\", year: 2011}\n  # tuple.empty? # => false\n  # ```\n  def empty?\n    size == 0\n  end\n\n  # Returns `true` if this tuple has the same keys as *other*, and values\n  # for each key are the same in `self` and *other*.\n  #\n  # ```\n  # tuple1 = {name: \"Crystal\", year: 2011}\n  # tuple2 = {year: 2011, name: \"Crystal\"}\n  # tuple3 = {name: \"Crystal\", year: 2012}\n  # tuple4 = {name: \"Crystal\", year: 2011.0}\n  #\n  # tuple1 == tuple2 # => true\n  # tuple1 == tuple3 # => false\n  # tuple1 == tuple4 # => true\n  # ```\n  def ==(other : self)\n    {% for key in T %}\n      return false unless self[{{key.symbolize}}] == other[{{key.symbolize}}]\n    {% end %}\n    true\n  end\n\n  # :ditto:\n  def ==(other : NamedTuple)\n    return false unless sorted_keys == other.sorted_keys\n\n    {% for key in T %}\n      return false unless self[{{key.symbolize}}] == other[{{key.symbolize}}]?\n    {% end %}\n    true\n  end\n\n  # Returns a named tuple with the same keys but with cloned values, using the `clone` method.\n  def clone\n    {% begin %}\n      NamedTuple.new(\n        {% for key in T %}\n          {{key.stringify}}: self[{{key.symbolize}}].clone,\n        {% end %}\n      )\n    {% end %}\n  end\n\n  private def first_key_internal\n    i = 0\n    keys[i]\n  end\n\n  private def first_value_internal\n    i = 0\n    values[i]\n  end\n\n  # Returns a value with the same type as the value for the given *key* of an\n  # instance of `self`. *key* must be a symbol or string literal known at\n  # compile-time.\n  #\n  # The most common usage of this macro is to extract the appropriate element\n  # type in `NamedTuple`'s class methods. This macro works even if the\n  # corresponding element type is private.\n  #\n  # NOTE: there should never be a need to call this method outside the standard library.\n  private macro element_type(key)\n    x = uninitialized self\n    x[{{ key.id.symbolize }}]\n  end\nend\n"
  },
  {
    "path": "src/nil.cr",
    "content": "# The `Nil` type has only one possible value: `nil`.\n#\n# `nil` is commonly used to represent the absence of a value.\n# For example, `String#index` returns the position of the character or `nil` if it's not\n# in the string:\n#\n# ```\n# str = \"Hello world\"\n# str.index 'e' # => 1\n# str.index 'a' # => nil\n# ```\n#\n# In the above example, trying to invoke a method on the returned value will\n# give a compile time error unless both `Int32` and `Nil` define that method:\n#\n# ```\n# str = \"Hello world\"\n# idx = str.index 'e'\n# idx + 1 # Error: undefined method '+' for Nil\n# ```\n#\n# The language and the standard library provide short, readable, easy ways to deal with `nil`,\n# such as `Object#try` and `Object#not_nil!`:\n#\n# ```\n# str = \"Hello world\"\n#\n# # The index of 'e' in str or 0 if not found\n# idx1 = str.index('e') || 0\n#\n# idx2 = str.index('a')\n# if idx2\n#   # Compiles: idx2 can't be nil here\n#   idx2 + 1\n# end\n#\n# # Tell the compiler that we are sure the returned\n# # value is not nil: raises a runtime exception\n# # if our assumption doesn't hold.\n# idx3 = str.index('o').not_nil!\n# ```\n#\n# See [`Nil` literal](https://crystal-lang.org/reference/syntax_and_semantics/literals/nil.html) in the language reference.\nstruct Nil\n  # Returns `0_u64`. Even though `Nil` is not a `Reference` type, it is usually\n  # mixed with them to form nilable types so it's useful to have an\n  # object id for `nil`.\n  def object_id : UInt64\n    0_u64\n  end\n\n  # :nodoc:\n  def crystal_type_id\n    0\n  end\n\n  # Returns `true`: `Nil` has only one singleton value: `nil`.\n  def ==(other : Nil)\n    true\n  end\n\n  # Returns `true`: `Nil` has only one singleton value: `nil`.\n  def same?(other : Nil)\n    true\n  end\n\n  # Returns `false`.\n  def same?(other : Reference) : Bool\n    false\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.nil\n  end\n\n  # Returns an empty string.\n  def to_s : String\n    \"\"\n  end\n\n  # Doesn't write anything to the given `IO`.\n  def to_s(io : IO) : Nil\n    # Nothing to do\n  end\n\n  # Returns `\"nil\"`.\n  def inspect : String\n    \"nil\"\n  end\n\n  # Writes `\"nil\"` to the given `IO`.\n  def inspect(io : IO) : Nil\n    io << \"nil\"\n  end\n\n  # Doesn't yield to the block.\n  #\n  # See also: `Object#try`.\n  def try(&block)\n    self\n  end\n\n  # Raises `NilAssertionError`.\n  #\n  # If *message* is given, it is forwarded as error message of `NilAssertionError`.\n  #\n  # See also: `Object#not_nil!`.\n  def not_nil!(message = nil) : NoReturn\n    if message\n      raise NilAssertionError.new(message)\n    else\n      raise NilAssertionError.new\n    end\n  end\n\n  # Returns `self`.\n  # This method enables to call the `presence` method (see `String#presence`) on a union with `Nil`.\n  # The idea is to return `nil` when the value is `nil` or empty.\n  #\n  # ```\n  # config = {\"empty\" => \"\"}\n  # config[\"empty\"]?.presence   # => nil\n  # config[\"missing\"]?.presence # => nil\n  # ```\n  def presence : Nil\n    self\n  end\n\n  def clone\n    self\n  end\nend\n"
  },
  {
    "path": "src/number.cr",
    "content": "# The top-level number type.\nstruct Number\n  include Comparable(Number)\n  include Steppable\n\n  alias Primitive = Int::Primitive | Float::Primitive\n\n  # Returns the value zero in the respective type.\n  #\n  # ```\n  # Int32.zero   # => 0\n  # Float64.zero # => 0.0\n  # ```\n  def self.zero : self\n    new(0)\n  end\n\n  # Returns the additive identity of this type.\n  #\n  # For numerical types, it is the value `0` expressed in the respective type.\n  #\n  # ```\n  # Int32.additive_identity   # => 0\n  # Float64.additive_identity # => 0.0\n  # ```\n  def self.additive_identity : self\n    zero\n  end\n\n  # Returns the multiplicative identity of this type.\n  #\n  # For numerical types, it is the value `1` expressed in the respective type.\n  #\n  # ```\n  # Int32.multiplicative_identity   # => 1\n  # Float64.multiplicative_identity # => 1.0\n  # ```\n  def self.multiplicative_identity : self\n    new(1)\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.number(self)\n  end\n\n  # Returns `self`.\n  def +\n    self\n  end\n\n  # Divides `self` by *other* using floored division.\n  #\n  # The result will be of the same type as `self`.\n  def //(other)\n    self.class.new((self / other).floor)\n  end\n\n  # :nodoc:\n  macro expand_div(rhs_types, result_type)\n    {% for rhs in rhs_types %}\n      @[::AlwaysInline]\n      def /(other : {{rhs}}) : {{result_type}}\n        {{result_type}}.new(self) / {{result_type}}.new(other)\n      end\n    {% end %}\n  end\n\n  # Creates an `Array` of `self` with the given values, which will be casted\n  # to this type with the `new` method (defined in each `Number` type).\n  #\n  # ```\n  # floats = Float64[1, 2, 3, 4]\n  # floats.class # => Array(Float64)\n  #\n  # ints = Int64[1, 2, 3]\n  # ints.class # => Array(Int64)\n  # ```\n  #\n  # This is similar to an array literal of the same item type:\n  #\n  # ```\n  # Int64[1, 2, 3, 4]     # : Array(Int64)\n  # [1, 2, 3, 4] of Int64 # : Array(Int64)\n  # ```\n  macro [](*nums)\n    ::Array({{@type}}).build({{nums.size}}) do |%buffer|\n      {% for num, i in nums %}\n        %buffer[{{i}}] = {{@type}}.new({{num}})\n      {% end %}\n      {{nums.size}}\n    end\n  end\n\n  # Creates a `Slice` of `self` with the given values, which will be casted\n  # to this type with the `new` method (defined in each `Number` type).\n  #\n  # The slice is allocated on the heap.\n  #\n  # ```\n  # floats = Float64.slice(1, 2, 3, 4)\n  # floats.class # => Slice(Float64)\n  #\n  # ints = Int64.slice(1, 2, 3)\n  # ints.class # => Slice(Int64)\n  # ```\n  #\n  # This is a convenient alternative to `Slice.[]` for designating a\n  # specific item type which also considers autocasting.\n  #\n  # ```\n  # Int64.slice(1, 2, 3, 4)           # : Slice(Int64)\n  # Slice[1_i64, 2_i64, 3_i64, 4_i64] # : Slice(Int64)\n  # ```\n  macro slice(*nums, read_only = false)\n    %slice = ::Slice({{@type}}).new({{nums.size}}, read_only: {{read_only}})\n    {% for num, i in nums %}\n      %slice.to_unsafe[{{i}}] = {{@type}}.new!({{num}})\n    {% end %}\n    %slice\n  end\n\n  # Creates a `StaticArray` of `self` with the given values, which will be casted\n  # to this type with the `new` method (defined in each `Number` type).\n  #\n  # ```\n  # floats = Float64.static_array(1, 2, 3, 4)\n  # floats.class # => StaticArray(Float64, 4)\n  #\n  # ints = Int64.static_array(1, 2, 3)\n  # ints.class # => StaticArray(Int64, 3)\n  # ```\n  #\n  # This is a convenvenient alternative to `StaticArray.[]` for designating a\n  # specific item type which also considers autocasting.\n  #\n  # ```\n  # Int64.static_array(1, 2, 3, 4)          # : StaticArray(Int64)\n  # StaticArray[1_i64, 2_i64, 3_i64, 4_i64] # : StaticArray(Int64)\n  # ```\n  macro static_array(*nums)\n    %array = uninitialized ::StaticArray({{@type}}, {{nums.size}})\n    {% for num, i in nums %}\n      %array.to_unsafe[{{i}}] = {{@type}}.new!({{num}})\n    {% end %}\n    %array\n  end\n\n  # Performs a `#step` in the direction of the _limit_. For instance:\n  #\n  # ```\n  # 10.step(to: 5).to_a # => [10, 9, 8, 7, 6, 5]\n  # 5.step(to: 10).to_a # => [5, 6, 7, 8, 9, 10]\n  # ```\n  def step(*, to limit = nil, exclusive : Bool = false, &) : Nil\n    if limit\n      direction = limit <=> self\n    end\n    step = direction.try(&.sign) || 1\n\n    step(to: limit, by: step, exclusive: exclusive) do |x|\n      yield x\n    end\n  end\n\n  # :ditto:\n  def step(*, to limit = nil, exclusive : Bool = false)\n    if limit\n      direction = limit <=> self\n    end\n    step = direction.try(&.sign) || 1\n\n    step(to: limit, by: step, exclusive: exclusive)\n  end\n\n  # Returns the absolute value of this number.\n  #\n  # ```\n  # 123.abs  # => 123\n  # -123.abs # => 123\n  # ```\n  def abs : self\n    self < 0 ? -self : self\n  end\n\n  # Returns the square of `self` (`self * self`).\n  #\n  # ```\n  # 4.abs2   # => 16\n  # 1.5.abs2 # => 2.25\n  # ```\n  def abs2\n    self * self\n  end\n\n  # Returns the sign of this number as an `Int32`.\n  # * `-1` if this number is negative\n  # * `0` if this number is zero\n  # * `1` if this number is positive\n  #\n  # ```\n  # 123.sign # => 1\n  # 0.sign   # => 0\n  # -42.sign # => -1\n  # ```\n  def sign : Int32\n    self < 0 ? -1 : (self == 0 ? 0 : 1)\n  end\n\n  # Returns a `Tuple` of two elements containing the quotient\n  # and modulus obtained by dividing `self` by *number*.\n  #\n  # ```\n  # 11.divmod(3)  # => {3, 2}\n  # 11.divmod(-3) # => {-4, -1}\n  # ```\n  def divmod(number)\n    {(self // number).floor, self % number}\n  end\n\n  # The comparison operator.\n  #\n  # Returns:\n  # - `-1` if `self` is less than *other*\n  # - `0` if `self` is equal to *other*\n  # - `1` if `self` is greater than *other*\n  # - `nil` if `self` is `NaN` or *other* is `NaN`, because `NaN` values are not comparable\n  def <=>(other) : Int32?\n    # NaN can't be compared to other numbers\n    return nil if self.is_a?(Float) && self.nan?\n    return nil if other.is_a?(Float) && other.nan?\n\n    self > other ? 1 : (self < other ? -1 : 0)\n  end\n\n  # Keeps *digits* significant digits of this number in the given *base*.\n  #\n  # ```\n  # 1234.567.significant(1) # => 1000\n  # 1234.567.significant(2) # => 1200\n  # 1234.567.significant(3) # => 1230\n  # 1234.567.significant(4) # => 1235\n  # 1234.567.significant(5) # => 1234.6\n  # 1234.567.significant(6) # => 1234.57\n  # 1234.567.significant(7) # => 1234.567\n  # 1234.567.significant(8) # => 1234.567\n  #\n  # 15.159.significant(1, base = 2) # => 16\n  # ```\n  def significant(digits, base = 10)\n    if digits < 0\n      raise ArgumentError.new \"digits should be non-negative\"\n    end\n    return self if zero?\n\n    x = self.to_f\n\n    if base == 10\n      log = Math.log10(self.abs)\n    elsif base == 2\n      log = Math.log2(self.abs)\n    else\n      log = Math.log2(self.abs) / Math.log2(base)\n    end\n\n    exponent = (log - digits + 1).floor\n    if exponent < 0\n      y = base ** -exponent\n      value = (x * y).round / y\n    else\n      y = base ** exponent\n      value = (x / y).round * y\n    end\n\n    self.class.new(value)\n  end\n\n  # Rounds this number to a given precision.\n  #\n  # Rounds to the specified number of *digits* after the decimal place,\n  # (or before if negative), in base *base*.\n  #\n  # The rounding *mode* controls the direction of the rounding. The default is\n  # `RoundingMode::TIES_EVEN` which rounds to the nearest integer, with ties\n  # (fractional value of `0.5`) being rounded to the even neighbor (Banker's rounding).\n  #\n  # ```\n  # -1763.116.round(2) # => -1763.12\n  # ```\n  def round(digits : Number, base = 10, *, mode : RoundingMode = :ties_even)\n    if digits < 0\n      multiplier = base.to_f ** digits.abs\n      shifted = self / multiplier\n    else\n      multiplier = base.to_f ** digits\n      shifted = self * multiplier\n    end\n\n    rounded = shifted.round(mode)\n\n    if digits < 0\n      result = rounded * multiplier\n    else\n      result = rounded / multiplier\n    end\n\n    self.class.new result\n  end\n\n  # Specifies rounding behaviour for numerical operations capable of discarding\n  # precision.\n  enum RoundingMode\n    # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n    # rounds towards the even neighbor (Banker's rounding).\n    TIES_EVEN\n\n    # Rounds towards the nearest integer. If both neighboring integers are equidistant,\n    # rounds away from zero.\n    TIES_AWAY\n\n    # Rounds towards zero (truncate).\n    TO_ZERO\n\n    # Rounds towards positive infinity (ceil).\n    TO_POSITIVE\n\n    # Rounds towards negative infinity (floor).\n    TO_NEGATIVE\n  end\n\n  # Rounds `self` to an integer value using rounding *mode*.\n  #\n  # The rounding *mode* controls the direction of the rounding. The default is\n  # `RoundingMode::TIES_EVEN` which rounds to the nearest integer, with ties\n  # (fractional value of `0.5`) being rounded to the even neighbor (Banker's rounding).\n  def round(mode : RoundingMode = :ties_even) : self\n    case mode\n    in .to_zero?\n      trunc\n    in .to_positive?\n      ceil\n    in .to_negative?\n      floor\n    in .ties_away?\n      round_away\n    in .ties_even?\n      round_even\n    end\n  end\n\n  # Returns `true` if `self` is an integer.\n  #\n  # Non-integer types may return `true` as long as `self` denotes a finite value\n  # without any fractional parts.\n  #\n  # ```\n  # 1.integer?       # => true\n  # 1.0.integer?     # => true\n  # 1.2.integer?     # => false\n  # (1 / 0).integer? # => false\n  # (0 / 0).integer? # => false\n  # ```\n  def integer? : Bool\n    self % 1 == 0\n  end\n\n  # Returns `true` if `self` is equal to zero.\n  #\n  # ```\n  # 0.zero? # => true\n  # 5.zero? # => false\n  # ```\n  def zero? : Bool\n    self == 0\n  end\n\n  # Returns `true` if `self` is greater than zero.\n  #\n  # ```\n  # -1.positive? # => false\n  # 0.positive?  # => false\n  # 1.positive?  # => true\n  # ```\n  def positive? : Bool\n    self > 0\n  end\n\n  # Returns `true` if `self` is less than zero.\n  #\n  # ```\n  # -1.negative? # => true\n  # 0.negative?  # => false\n  # 1.negative?  # => false\n  # ```\n  def negative? : Bool\n    self < 0\n  end\nend\n"
  },
  {
    "path": "src/oauth/access_token.cr",
    "content": "class OAuth::AccessToken\n  getter token : String\n  getter secret : String\n\n  def initialize(@token : String, @secret : String, @extra : Hash(String, String)? = nil)\n  end\n\n  def authenticate(client, consumer_key, consumer_secret, extra_params = nil)\n    OAuth.authenticate(client, @token, @secret, consumer_key, consumer_secret, extra_params)\n  end\n\n  def extra : Hash(String, String)\n    @extra ||= {} of String => String\n  end\n\n  def self.from_response(response : String) : self\n    token = nil\n    secret = nil\n    extra = nil\n\n    URI::Params.parse(response) do |key, value|\n      case key\n      when \"oauth_token\"        then token = value\n      when \"oauth_token_secret\" then secret = value\n      else\n        extra ||= {} of String => String\n        extra[key] = value\n      end\n    end\n\n    new token.not_nil!, secret.not_nil!, extra\n  end\n\n  def self.new(pull : JSON::PullParser)\n    token = nil\n    secret = nil\n    extra = nil\n\n    pull.read_object do |key|\n      case key\n      when \"oauth_token\"\n        token = pull.read_string\n      when \"oauth_token_secret\"\n        secret = pull.read_string\n      else\n        if pull.kind.string?\n          extra ||= {} of String => String\n          extra[key] = pull.read_string\n        else\n          pull.skip\n        end\n      end\n    end\n\n    new token.not_nil!, secret.not_nil!, extra\n  end\n\n  def to_json(json : JSON::Builder)\n    json.object do\n      json.field \"oauth_token\", @token\n      json.field \"oauth_token_secret\", @secret\n      @extra.try &.each do |key, value|\n        json.field key, value\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/oauth/authorization_header.cr",
    "content": "# :nodoc:\nstruct OAuth::AuthorizationHeader\n  def initialize\n    @str = IO::Memory.new\n    @str << \"OAuth \"\n    @first = true\n  end\n\n  def add(key : String, value : String?) : Nil\n    return unless value\n\n    @str << \", \" unless @first\n    URI.encode_www_form key, @str\n    @str << %(=\")\n    URI.encode_www_form value, @str\n    @str << '\"'\n    @first = false\n  end\n\n  def to_s(io : IO) : Nil\n    @str.to_s(io)\n  end\n\n  def to_s : String\n    @str.to_s\n  end\nend\n"
  },
  {
    "path": "src/oauth/consumer.cr",
    "content": "# An OAuth consumer.\n#\n# For a quick example of how to authenticate an `HTTP::Client` with OAuth if\n# you already have an access token, check the `OAuth` module description.\n#\n# This class also provides methods to get request tokens, build authorize URIs\n# and get access tokens, as specified by [RFC 5849](https://tools.ietf.org/html/rfc5849).\n#\n# ### Example\n#\n# ```\n# require \"oauth\"\n#\n# consumer_key = \"some_key\"\n# consumer_secret = \"some_secret\"\n# oauth_callback = \"http://some.callback\"\n#\n# # Create consumer, optionally pass custom URIs if needed,\n# # if the request, authorize or access_token URIs are not the standard ones\n# # (they can also be absolute URLs)\n# consumer = OAuth::Consumer.new(\"api.example.com\", consumer_key, consumer_secret)\n#\n# # Get a request token.\n# # We probably need to save this somewhere to get it back in the\n# # callback URL (saving token and secret should be enough)\n# request_token = consumer.get_request_token(oauth_callback)\n#\n# # Build an authorize URI\n# authorize_uri = consumer.get_authorize_uri(request_token, oauth_callback)\n#\n# # Redirect the user to `authorize_uri`...\n# #\n# # ...\n# #\n# # When http://some.callback is hit, once the user authorized the access,\n# # we resume our logic to finally get an access token. The callback URL\n# # should receive an `oauth_verifier` parameter that we need to use.\n# oauth_verifier = request.params[\"oauth_verifier\"]\n#\n# # Get the access token\n# access_token = consumer.get_access_token(request_token, oauth_verifier)\n#\n# # Probably save the access token for reuse... This can be done\n# # with `to_json` and `from_json`.\n#\n# # Use the token to authenticate an HTTP::Client\n# client = HTTP::Client.new(\"api.example.com\", tls: true)\n# access_token.authenticate(client, consumer_key, consumer_secret)\n#\n# # And do requests as usual\n# client.get \"/some_path\"\n# ```\nclass OAuth::Consumer\n  @tls : Bool\n\n  # Creates an OAuth consumer.\n  #\n  # Any or all of the customizable URIs *request_token_uri*, *authorize_uri* and\n  # *access_token_uri* can be relative or absolute.\n  # If they are relative, the given *host*, *port* and *scheme* will be used.\n  # If they are absolute, the absolute URL will be used.\n  def initialize(@host : String, @consumer_key : String, @consumer_secret : String,\n                 @port : Int32? = nil,\n                 @scheme : String = \"https\",\n                 @request_token_uri : String = \"/oauth/request_token\",\n                 @authorize_uri : String = \"/oauth/authorize\",\n                 @access_token_uri : String = \"/oauth/access_token\")\n    @tls = @scheme == \"https\"\n  end\n\n  # Obtains a request token, also known as \"temporary credentials\", as specified by\n  # [RFC 5849, Section 2.1](https://tools.ietf.org/html/rfc5849#section-2.1).\n  #\n  # Raises `OAuth::Error` if there was an error getting the request token.\n  def get_request_token(oauth_callback : String = \"oob\") : RequestToken\n    post(nil, nil, {\"oauth_callback\" => oauth_callback}, @request_token_uri) do |response|\n      RequestToken.from_response(response.body)\n    end\n  end\n\n  # Returns an authorize URI from a given request token to redirect the user\n  # to obtain an access token, as specified by\n  # [RFC 5849, Section 2.2](https://tools.ietf.org/html/rfc5849#section-2.2).\n  def get_authorize_uri(request_token : RequestToken, oauth_callback : String? = nil) : String\n    get_authorize_uri(request_token, oauth_callback) { }\n  end\n\n  # Returns an authorize URI from a given request token to redirect the user\n  # to obtain an access token, as specified by\n  # [RFC 5849, Section 2.2](https://tools.ietf.org/html/rfc5849#section-2.2).\n  #\n  # Yields an `URI::Params::Builder` to add extra parameters other than those\n  # defined by the standard.\n  def get_authorize_uri(request_token : RequestToken, oauth_callback : String? = nil, &block : URI::Params::Builder ->) : String\n    uri = URI.parse(@authorize_uri)\n\n    # Use the default URI if it's not an absolute one\n    unless uri.host\n      uri = URI.new(@scheme, @host, @port, @authorize_uri)\n    end\n\n    uri.query = URI::Params.build do |form|\n      form.add \"oauth_token\", request_token.token\n      form.add \"oauth_callback\", oauth_callback if oauth_callback\n      uri.query_params.each do |key, value|\n        form.add key, value\n      end\n      yield form\n    end\n\n    uri.to_s\n  end\n\n  # Gets an access token from a previously obtained request token and an *oauth_verifier*\n  # obtained from an authorize URI, as specified by\n  # [RFC 5849, Section 2.3](https://tools.ietf.org/html/rfc5849#section-2.3).\n  #\n  # Raises `OAuth::Error` if there was an error getting the access token.\n  def get_access_token(request_token : RequestToken, oauth_verifier : String, extra_params : Hash(String, String)? = nil) : AccessToken\n    extra_params ||= {} of String => String\n    extra_params[\"oauth_verifier\"] = oauth_verifier\n    post(request_token.token, request_token.secret, extra_params, @access_token_uri) do |response|\n      AccessToken.from_response(response.body)\n    end\n  end\n\n  # Authenticated an `HTTP::Client` to add an OAuth authorization header, as specified by\n  # [RFC 5849, Section 3](https://tools.ietf.org/html/rfc5849#section-3).\n  def authenticate(client : HTTP::Client, token : AccessToken) : Nil\n    authenticate client, token.token, token.secret, nil\n  end\n\n  private def post(oauth_token, token_shared_secret, extra_params, target_uri, &)\n    uri = URI.parse(target_uri)\n\n    # If the target uri is absolute, we use that instead of the default values\n    if uri.host\n      client = HTTP::Client.new(uri)\n      target_uri = uri.request_target\n    else\n      client = HTTP::Client.new @host, @port, tls: @tls\n    end\n\n    authenticate client, oauth_token, token_shared_secret, extra_params\n\n    begin\n      response = client.post target_uri\n      yield response\n    ensure\n      client.close\n    end\n  end\n\n  private def authenticate(client, token, token_secret, extra_params)\n    OAuth.authenticate(client, token, token_secret, @consumer_key, @consumer_secret, extra_params)\n  end\nend\n"
  },
  {
    "path": "src/oauth/error.cr",
    "content": "class OAuth::Error < ::Exception\n  def initialize(message : String)\n    super\n  end\n\n  def initialize(response : HTTP::Client::Response)\n    super(\"OAuth:Error: #{response.body}\")\n  end\nend\n"
  },
  {
    "path": "src/oauth/oauth.cr",
    "content": "# The OAuth module provides an `OAuth::Consumer` as specified by\n# [RFC 5849](https://tools.ietf.org/html/rfc5849).\n#\n# NOTE: To use `OAuth`, you must explicitly import it with `require \"oauth\"`\n#\n# ### Performing HTTP client requests with OAuth authentication\n#\n# Assuming you have an access token, its secret, the consumer key and the consumer secret,\n# you can setup an `HTTP::Client` to be authenticated with OAuth using this code:\n#\n# ```\n# require \"http/client\"\n# require \"oauth\"\n#\n# token = \"some_token\"\n# secret = \"some_secret\"\n# consumer_key = \"some_consumer_key\"\n# consumer_secret = \"some_consumer_secret\"\n#\n# # Create an HTTP::Client as usual\n# client = HTTP::Client.new(\"api.example.com\", tls: true)\n#\n# # Prepare it for using OAuth authentication\n# OAuth.authenticate(client, token, secret, consumer_key, consumer_secret)\n#\n# # Execute requests as usual: they will be authenticated\n# client.get(\"/some_path\")\n# ```\n#\n# This is implemented with `HTTP::Client#before_request` to add an authorization\n# header to every request.\n#\n# Alternatively, you can create an `OAuth::Consumer` and then invoke its\n# `OAuth::Consumer#authenticate` method, or create an `OAuth::AccessToken`\n# and invoke its `OAuth::AccessToken#authenticate`.\n#\n# ### Obtaining access tokens\n#\n# See `OAuth::Consumer` for an example.\nmodule OAuth\n  # Sets up an `HTTP::Client` to add an OAuth authorization header to every request performed.\n  # Check this module's docs for an example usage.\n  def self.authenticate(client : HTTP::Client, token : String?, token_secret : String?, consumer_key : String, consumer_secret : String, extra_params : Hash(String, String)? = nil) : Nil\n    client.before_request do |request|\n      authenticate client, request, token, token_secret, consumer_key, consumer_secret, extra_params\n    end\n  end\n\n  private def self.authenticate(client, request, token, token_secret, consumer_key, consumer_secret, extra_params)\n    request.headers[\"Authorization\"] = oauth_header(client, request, token, token_secret, consumer_key, consumer_secret, extra_params)\n  end\n\n  private def self.oauth_header(client, request, token, token_secret, consumer_key, consumer_secret, extra_params)\n    ts = Time.utc.to_unix.to_s\n    nonce = Random::Secure.hex\n\n    signature = Signature.new consumer_key, consumer_secret, token, token_secret, extra_params\n    signature.authorization_header request, client.tls?, ts, nonce\n  end\nend\n"
  },
  {
    "path": "src/oauth/request_token.cr",
    "content": "class OAuth::RequestToken\n  getter token : String\n  getter secret : String\n\n  def initialize(@token : String, @secret : String)\n  end\n\n  def self.from_response(response : String) : self\n    token = nil\n    secret = nil\n\n    URI::Params.parse(response) do |key, value|\n      case key\n      when \"oauth_token\"        then token = value\n      when \"oauth_token_secret\" then secret = value\n      else\n        # Not a key we are interested in\n      end\n    end\n\n    unless token && secret\n      values = [] of String\n      values << \"token\" if token.nil?\n      values << \"secret\" if secret.nil?\n      raise Error.new(\"Missing #{values.join(\" and \")}\")\n    end\n\n    new token.not_nil!, secret.not_nil!\n  end\n\n  def_equals_and_hash @token, @secret\nend\n"
  },
  {
    "path": "src/oauth/signature.cr",
    "content": "# :nodoc:\nstruct OAuth::Signature\n  def initialize(@consumer_key : String, @client_shared_secret : String, @oauth_token : String? = nil, @token_shared_secret : String? = nil, @extra_params : Hash(String, String)? = nil)\n  end\n\n  def base_string(request : HTTP::Request, tls, ts : String, nonce : String) : String\n    base_string request, tls, gather_params(request, ts, nonce)\n  end\n\n  def key : String\n    String.build do |str|\n      URI.encode_www_form @client_shared_secret, str, space_to_plus: false\n      str << '&'\n      if token_shared_secret = @token_shared_secret\n        URI.encode_www_form token_shared_secret, str, space_to_plus: false\n      end\n    end\n  end\n\n  def compute(request : HTTP::Request, tls, ts : String, nonce : String) : String\n    base_string = base_string(request, tls, ts, nonce)\n    Base64.strict_encode(OpenSSL::HMAC.digest :sha1, key, base_string)\n  end\n\n  def authorization_header(request : HTTP::Request, tls, ts : String, nonce : String) : String\n    oauth_signature = compute request, tls, ts, nonce\n\n    auth_header = AuthorizationHeader.new\n    auth_header.add \"oauth_consumer_key\", @consumer_key\n    auth_header.add \"oauth_signature_method\", \"HMAC-SHA1\"\n    auth_header.add \"oauth_timestamp\", ts\n    auth_header.add \"oauth_nonce\", nonce\n    auth_header.add \"oauth_signature\", oauth_signature\n    auth_header.add \"oauth_token\", @oauth_token\n    auth_header.add \"oauth_version\", \"1.0\"\n    @extra_params.try &.each do |key, value|\n      auth_header.add key, value\n    end\n    auth_header.to_s\n  end\n\n  private def base_string(request, tls, normalized_params : String)\n    host, port = host_and_port(request, tls)\n\n    String.build do |str|\n      str << request.method\n      str << '&'\n      str << (tls ? \"https\" : \"http\")\n      str << \"%3A%2F%2F\"\n      URI.encode_www_form host, str, space_to_plus: false\n      if port\n        str << \"%3A\"\n        str << port\n      end\n      uri_path = request.path.presence || \"/\"\n      URI.encode_www_form(uri_path, str, space_to_plus: false)\n      str << '&'\n      URI.encode_www_form(normalized_params, str, space_to_plus: false)\n    end\n  end\n\n  private def gather_params(request, ts, nonce) : String\n    params = URI::Params.new\n\n    params.add \"oauth_consumer_key\", @consumer_key\n    params.add \"oauth_nonce\", nonce\n    params.add \"oauth_signature_method\", \"HMAC-SHA1\"\n    params.add \"oauth_timestamp\", ts\n    if token = @oauth_token\n      params.add \"oauth_token\", token\n    end\n    params.add \"oauth_version\", \"1.0\"\n\n    @extra_params.try &.each do |key, value|\n      params.add key, value\n    end\n\n    # Inline query parsing\n    if query = request.query\n      URI::Params.parse(query) do |k, v|\n        params.add k, v\n      end\n    end\n\n    # Inline form body parsing\n    body = request.body\n    content_type = request.headers[\"Content-type\"]?\n    if body && content_type == \"application/x-www-form-urlencoded\"\n      form = body.gets_to_end\n      URI::Params.parse(form) do |k, v|\n        params.add k, v\n      end\n      request.body = form\n    end\n\n    oauth_normalize_params(params)\n  end\n\n  private def host_and_port(request, tls)\n    host_header = request.headers[\"Host\"]\n    if colon_index = host_header.index ':'\n      host = host_header[0...colon_index]\n      port = host_header[colon_index + 1..-1].to_i\n      {host, port}\n    else\n      {host_header, nil}\n    end\n  end\n\n  private def oauth_normalize_params(params : URI::Params) : String\n    # Encode names + values first (RFC 5849 §3.4.1.3.2)\n    pairs = params.map do |key, value|\n      {\n        URI.encode_www_form(key.to_s, space_to_plus: false),\n        URI.encode_www_form(value.to_s, space_to_plus: false),\n      }\n    end\n\n    # Sort by encoded key then encoded value\n    pairs.sort_by! { |(k, v)| {k, v} }\n\n    # Build final normalized string WITHOUT re-encoding\n    String.build do |io|\n      pairs.join(io, \"&\") do |(key, value), io|\n        io << key << \"=\" << value\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/oauth.cr",
    "content": "require \"http/client\"\nrequire \"http/params\"\nrequire \"uri\"\nrequire \"random/secure\"\nrequire \"openssl/hmac\"\nrequire \"base64\"\nrequire \"./oauth/**\"\n"
  },
  {
    "path": "src/oauth2/access_token/access_token.cr",
    "content": "# Base class for the two possible access tokens: Bearer and Mac.\n#\n# Use `#authenticate` to authenticate an `HTTP::Client`.\nabstract class OAuth2::AccessToken\n  def self.new(pull : JSON::PullParser) : self\n    token_type = nil\n    access_token = nil\n    expires_in = nil\n    refresh_token = nil\n    scope = nil\n    mac_algorithm = nil\n    mac_key = nil\n    extra = nil\n\n    pull.read_object do |key|\n      case key\n      when \"token_type\"    then token_type = pull.read_string\n      when \"access_token\"  then access_token = pull.read_string\n      when \"expires_in\"    then expires_in = pull.read_int\n      when \"refresh_token\" then refresh_token = pull.read_string_or_null\n      when \"scope\"         then scope = pull.read_string_or_null\n      when \"mac_algorithm\" then mac_algorithm = pull.read_string\n      when \"mac_key\"       then mac_key = pull.read_string\n      else\n        extra ||= {} of String => String\n        extra[key] = pull.read_raw\n      end\n    end\n\n    unless access_token\n      raise ::JSON::SerializableError.new(\"Missing access token\", \"OAuth2::AccessToken\", \"access_token\", *pull.location, cause: nil)\n    end\n    access_token = access_token.not_nil!\n\n    token_type ||= \"bearer\"\n\n    case token_type.downcase\n    when \"bearer\"\n      Bearer.new(access_token, expires_in, refresh_token, scope, extra)\n    when \"mac\"\n      Mac.new(access_token, expires_in, mac_algorithm.not_nil!, mac_key.not_nil!, refresh_token, scope, Time.utc.to_unix, extra)\n    else\n      raise \"Unknown token_type in access token json: #{token_type}\"\n    end\n  end\n\n  property access_token : String\n  property expires_in : Int64?\n  property refresh_token : String?\n  property scope : String?\n\n  # JSON key-value pairs that are outside of the OAuth2 spec are\n  # stored in this property in case they are needed. Their value\n  # is the raw JSON string found in the JSON value (with possible\n  # changes in the string format, but preserving JSON semantic).\n  # For example if the value was `[1, 2, 3]` then the value in this hash\n  # will be the string \"[1,2,3]\".\n  property extra : Hash(String, String)?\n\n  def initialize(@access_token : String, expires_in : Int?, @refresh_token : String? = nil, @scope : String? = nil, @extra = nil)\n    @expires_in = expires_in.try &.to_i64\n  end\n\n  abstract def authenticate(request : HTTP::Request, tls)\n\n  def authenticate(client : HTTP::Client)\n    client.before_request do |request|\n      authenticate request, client.tls?\n    end\n  end\nend\n"
  },
  {
    "path": "src/oauth2/access_token/bearer.cr",
    "content": "require \"./access_token\"\n\nclass OAuth2::AccessToken::Bearer < OAuth2::AccessToken\n  def self.new(pull : JSON::PullParser) : self\n    OAuth2::AccessToken.new(pull).as(self)\n  end\n\n  def token_type : String\n    \"Bearer\"\n  end\n\n  def authenticate(request : HTTP::Request, tls)\n    request.headers[\"Authorization\"] = \"Bearer #{access_token}\"\n  end\n\n  def to_json(json : JSON::Builder) : Nil\n    json.object do\n      json.field \"token_type\", \"bearer\"\n      json.field \"access_token\", access_token\n      json.field \"expires_in\", expires_in\n      json.field \"refresh_token\", refresh_token if refresh_token\n      json.field \"scope\", scope if scope\n    end\n  end\n\n  def_equals_and_hash access_token, expires_in, refresh_token, scope\nend\n"
  },
  {
    "path": "src/oauth2/access_token/mac.cr",
    "content": "require \"random/secure\"\nrequire \"openssl/hmac\"\nrequire \"base64\"\nrequire \"./access_token\"\n\nclass OAuth2::AccessToken::Mac < OAuth2::AccessToken\n  def self.new(pull : JSON::PullParser) : self\n    OAuth2::AccessToken.new(pull).as(self)\n  end\n\n  property mac_algorithm : String\n  property mac_key : String\n  property issued_at : Int64\n\n  def initialize(access_token : String, expires_in, @mac_algorithm : String, @mac_key : String, refresh_token : String? = nil, scope : String? = nil, @issued_at : Int64 = Time.utc.to_unix, extra : Hash(String, String)? = nil)\n    super(access_token, expires_in, refresh_token, scope, extra)\n  end\n\n  def token_type : String\n    \"Mac\"\n  end\n\n  def authenticate(request : HTTP::Request, tls)\n    ts = Time.utc.to_unix\n    nonce = \"#{ts - @issued_at}:#{Random::Secure.hex}\"\n    method = request.method\n    uri = request.resource\n    host, port = host_and_port request, tls\n    ext = \"\"\n\n    mac = Mac.signature ts, nonce, method, uri, host, port, ext, mac_algorithm, mac_key\n\n    header = %(MAC id=\"#{access_token}\", nonce=\"#{nonce}\", ts=\"#{ts}\", mac=\"#{mac}\")\n    request.headers[\"Authorization\"] = header\n  end\n\n  def self.signature(ts, nonce : String, method : String, uri : String, host : String, port : Int32 | String, ext : String, mac_algorithm : String, mac_key : String) : String\n    normalized_request_string = \"#{ts}\\n#{nonce}\\n#{method}\\n#{uri}\\n#{host}\\n#{port}\\n#{ext}\\n\"\n\n    digest = case mac_algorithm\n             when \"hmac-sha-1\"   then OpenSSL::Algorithm::SHA1\n             when \"hmac-sha-256\" then OpenSSL::Algorithm::SHA256\n             else                     raise \"Unsupported algorithm: #{mac_algorithm}\"\n             end\n    Base64.strict_encode OpenSSL::HMAC.digest(digest, mac_key, normalized_request_string)\n  end\n\n  def to_json(json : JSON::Builder) : Nil\n    json.object do\n      json.field \"token_type\", \"mac\"\n      json.field \"access_token\", access_token\n      json.field \"expires_in\", expires_in\n      json.field \"refresh_token\", refresh_token if refresh_token\n      json.field \"scope\", scope if scope\n      json.field \"mac_algorithm\", mac_algorithm\n      json.field \"mac_key\", mac_key\n    end\n  end\n\n  def_equals_and_hash access_token, expires_in, mac_algorithm, mac_key, refresh_token, scope\n\n  private def host_and_port(request, tls)\n    host_header = request.headers[\"Host\"]\n    if colon_index = host_header.index ':'\n      host = host_header[0...colon_index]\n      port = host_header[colon_index + 1..-1].to_i\n    else\n      host = host_header\n      port = tls ? 443 : 80\n    end\n    {host, port}\n  end\nend\n"
  },
  {
    "path": "src/oauth2/auth_scheme.cr",
    "content": "# Enum of supported mechanisms used to pass credentials to the server.\n#\n# According to https://tools.ietf.org/html/rfc6749#section-2.3.1:\n#\n# > \"Including the client credentials in the request-body using the\n# > two parameters is NOT RECOMMENDED and SHOULD be limited to\n# > clients unable to directly utilize the HTTP Basic authentication\n# > scheme (or other password-based HTTP authentication schemes).\"\n#\n# Therefore, HTTP Basic is preferred, and Request Body should only\n# be used if the server does not support HTTP Basic.\nenum OAuth2::AuthScheme\n  HTTPBasic\n  RequestBody\nend\n"
  },
  {
    "path": "src/oauth2/client.cr",
    "content": "require \"./error_response\"\n\n# An OAuth2 client.\n#\n# For a quick example of how to authenticate an `HTTP::Client` with OAuth2 if\n# you already have an access token, check the `OAuth2` module description.\n#\n# This class also provides methods to build authorize URIs\n# and get access tokens with different methods, as specified by\n# [RFC 6749](https://tools.ietf.org/html/rfc6749).\n#\n# ### Example\n#\n# ```\n# require \"oauth2\"\n#\n# client_id = \"some_client_id\"\n# client_secret = \"some_client_secret\"\n# redirect_uri = \"http://some.callback\"\n#\n# # Create oauth client, optionally pass custom URIs if needed,\n# # if the authorize or token URIs are not the standard ones\n# # (they can also be absolute URLs)\n# oauth2_client = OAuth2::Client.new(\"api.example.com\", client_id, client_secret,\n#   redirect_uri: redirect_uri)\n#\n# # Build an authorize URI\n# authorize_uri = oauth2_client.get_authorize_uri\n#\n# # Redirect the user to `authorize_uri`...\n# #\n# # ...\n# #\n# # When http://some.callback is hit, once the user authorized the access,\n# # we resume our logic to finally get an access token. The callback URL\n# # should receive an `authorization_code` parameter that we need to use.\n# authorization_code = request.params[\"code\"]\n#\n# # Get the access token\n# access_token = oauth2_client.get_access_token_using_authorization_code(authorization_code)\n#\n# # Probably save the access token for reuse... This can be done\n# # with `to_json` and `from_json`.\n#\n# # Use the token to authenticate an HTTP::Client\n# client = HTTP::Client.new(\"api.example.com\", tls: true)\n# access_token.authenticate(client)\n#\n# # And do requests as usual\n# client.get \"/some_path\"\n#\n# # If the token expires, we can refresh it\n# new_access_token = oauth2_client.get_access_token_using_refresh_token(access_token.refresh_token)\n# ```\n#\n# You can also use an `OAuth2::Session` to automatically refresh expired\n# tokens before each request.\nclass OAuth2::Client\n  DEFAULT_HEADERS = HTTP::Headers{\n    \"Accept\"       => \"application/json\",\n    \"Content-Type\" => \"application/x-www-form-urlencoded\",\n  }\n\n  # Sets the `HTTP::Client` to use with this client.\n  setter http_client : HTTP::Client?\n\n  # Gets the redirect_uri\n  getter redirect_uri : String?\n\n  # Returns the `HTTP::Client` to use with this client.\n  #\n  # By default, this returns a new instance every time. To reuse the same instance,\n  # one can be assigned with `#http_client=`.\n  def http_client : HTTP::Client\n    @http_client || HTTP::Client.new(token_uri)\n  end\n\n  # Creates an OAuth client.\n  #\n  # Any or all of the customizable URIs *authorize_uri* and\n  # *token_uri* can be relative or absolute.\n  # If they are relative, the given *host*, *port* and *scheme* will be used.\n  # If they are absolute, the absolute URL will be used.\n  #\n  # As per https://tools.ietf.org/html/rfc6749#section-2.3.1,\n  # `AuthScheme::HTTPBasic` is the default *auth_scheme* (the mechanism used to\n  # transmit the client credentials to the server). `AuthScheme::RequestBody` should\n  # only be used if the server does not support HTTP Basic.\n  def initialize(@host : String, @client_id : String, @client_secret : String,\n                 @port : Int32? = nil,\n                 @scheme = \"https\",\n                 @authorize_uri = \"/oauth2/authorize\",\n                 @token_uri = \"/oauth2/token\",\n                 @redirect_uri : String? = nil,\n                 @auth_scheme : AuthScheme = :http_basic)\n  end\n\n  # Builds an authorize URI, as specified by\n  # [RFC 6749, Section 4.1.1](https://tools.ietf.org/html/rfc6749#section-4.1.1).\n  def get_authorize_uri(scope : String? = nil, state : String? = nil) : String\n    get_authorize_uri(scope, state) { }\n  end\n\n  # Builds an authorize URI, as specified by\n  # [RFC 6749, Section 4.1.1](https://tools.ietf.org/html/rfc6749#section-4.1.1).\n  #\n  # Yields an `URI::Params::Builder` to add extra parameters other than those\n  # defined by the standard.\n  def get_authorize_uri(scope = nil, state = nil, &block : URI::Params::Builder ->) : String\n    uri = URI.parse(@authorize_uri)\n\n    # Use the default URI if it's not an absolute one\n    unless uri.host\n      uri = URI.new(@scheme, @host, @port, @authorize_uri)\n    end\n\n    uri.query = URI::Params.build do |form|\n      form.add(\"client_id\", @client_id)\n      form.add(\"redirect_uri\", @redirect_uri)\n      form.add(\"response_type\", \"code\")\n      form.add(\"scope\", scope) unless scope.nil?\n      form.add(\"state\", state) unless state.nil?\n      uri.query_params.each do |key, value|\n        form.add(key, value)\n      end\n      yield form\n    end\n\n    uri.to_s\n  end\n\n  # Gets an access token using an authorization code, as specified by\n  # [RFC 6749, Section 4.1.3](https://tools.ietf.org/html/rfc6749#section-4.1.3).\n  def get_access_token_using_authorization_code(authorization_code : String) : AccessToken\n    get_access_token do |form|\n      form.add(\"redirect_uri\", @redirect_uri)\n      form.add(\"grant_type\", \"authorization_code\")\n      form.add(\"code\", authorization_code)\n    end\n  end\n\n  # Gets an access token using the resource owner credentials, as specified by\n  # [RFC 6749, Section 4.3.2](https://tools.ietf.org/html/rfc6749#section-4.3.2).\n  def get_access_token_using_resource_owner_credentials(username : String, password : String, scope : String? = nil) : AccessToken\n    get_access_token do |form|\n      form.add(\"grant_type\", \"password\")\n      form.add(\"username\", username)\n      form.add(\"password\", password)\n      form.add(\"scope\", scope) unless scope.nil?\n    end\n  end\n\n  # Gets an access token using client credentials, as specified by\n  # [RFC 6749, Section 4.4.2](https://tools.ietf.org/html/rfc6749#section-4.4.2).\n  def get_access_token_using_client_credentials(scope : String? = nil) : AccessToken\n    get_access_token do |form|\n      form.add(\"grant_type\", \"client_credentials\")\n      form.add(\"scope\", scope) unless scope.nil?\n    end\n  end\n\n  # Gets an access token using a refresh token, as specified by\n  # [RFC 6749, Section 6](https://tools.ietf.org/html/rfc6749#section-6).\n  def get_access_token_using_refresh_token(refresh_token : String?, scope : String? = nil) : AccessToken\n    get_access_token do |form|\n      form.add(\"grant_type\", \"refresh_token\")\n      form.add(\"refresh_token\", refresh_token)\n      form.add(\"scope\", scope) unless scope.nil?\n    end\n  end\n\n  # Makes a token exchange request with custom headers and form fields\n  def make_token_request(&block : URI::Params::Builder, HTTP::Headers -> _) : HTTP::Client::Response\n    headers = DEFAULT_HEADERS.dup\n    body = URI::Params.build do |form|\n      case @auth_scheme\n      when .request_body?\n        form.add(\"client_id\", @client_id)\n        form.add(\"client_secret\", @client_secret)\n      when .http_basic?\n        headers.add(\n          \"Authorization\",\n          \"Basic #{Base64.strict_encode(\"#{@client_id}:#{@client_secret}\")}\"\n        )\n      end\n      yield form, headers\n    end\n\n    http_client.post token_uri.request_target, form: body, headers: headers\n  end\n\n  private def get_access_token(&) : AccessToken\n    response = make_token_request do |form, _headers|\n      yield form\n    end\n\n    unless response.success?\n      raise Error.new(\"Unexpected response status: #{response.status.code} #{response.status}\")\n    end\n\n    begin\n      AccessToken.from_json(response.body)\n    rescue ex : JSON::SerializableError\n      raise Error.new(ErrorResponse.from_json(response.body).error, cause: ex)\n    end\n  end\n\n  private def token_uri : URI\n    uri = URI.parse(@token_uri)\n    if uri.host\n      uri\n    else\n      URI.new(@scheme, @host, @port, @token_uri)\n    end\n  end\nend\n"
  },
  {
    "path": "src/oauth2/error.cr",
    "content": "class OAuth2::Error < Exception\nend\n"
  },
  {
    "path": "src/oauth2/error_response.cr",
    "content": "require \"json\"\n\nstruct OAuth2::ErrorResponse\n  include JSON::Serializable\n\n  getter error : String\nend\n"
  },
  {
    "path": "src/oauth2/oauth2.cr",
    "content": "# The OAuth module provides an `OAuth2::Client` as specified\n# by [RFC 6749](https://tools.ietf.org/html/rfc6749).\n#\n# NOTE: To use `OAuth2`, you must explicitly import it with `require \"oauth2\"`\n#\n# ### Performing HTTP client requests with OAuth2 authentication\n#\n# Assuming you have an access token, you can setup an `HTTP::Client`\n# to be authenticated with OAuth2 using this code:\n#\n# ```\n# require \"http/client\"\n# require \"oauth2\"\n#\n# # Here we use a bearer token, but it could be a mac token. We also set the\n# # expires in value to 172,800 seconds, or 48 hours\n# access_token = OAuth2::AccessToken::Bearer.new(\"some_access_token\", 172_800)\n#\n# # Create an HTTP::Client\n# client = HTTP::Client.new(\"api.example.com\", tls: true)\n#\n# # Prepare it for using OAuth2 authentication\n# access_token.authenticate(client)\n#\n# # Execute requests as usual: they will be authenticated\n# client.get(\"/some_path\")\n# ```\n#\n# This is implemented with `HTTP::Client#before_request` to add an authorization\n# header to every request.\n#\n# ### Obtaining access tokens\n#\n# See `OAuth2::Client` for an example.\nmodule OAuth2\nend\n"
  },
  {
    "path": "src/oauth2/session.cr",
    "content": "# An OAuth2 session makes it easy to implement APIs that need to refresh\n# an access token once its expired before executing an HTTP request.\nclass OAuth2::Session\n  getter oauth2_client : Client\n  getter access_token : AccessToken\n  getter expires_at : Time?\n\n  # Creates an `OAuth2::Session`.\n  #\n  # Params:\n  #   * *oauth2_client*: the OAuth2::Client used to refresh an access token.\n  #   * *access_token*: the OAuth2::AccessToken to make requests.\n  #   * *expires_at*: the Time when the access token expires.\n  #   * *callback*: invoked when an access token is refreshed, giving you a chance to persist it.\n  def initialize(@oauth2_client : Client, @access_token : AccessToken, @expires_at = Time.utc, &@callback : OAuth2::Session ->)\n  end\n\n  # Authenticates an `HTTP::Client`, refreshing the access token if it is expired.\n  #\n  # Invoke this method on an `HTTP::Client` before executing an HTTP request.\n  def authenticate(http_client : HTTP::Client) : Nil\n    check_refresh_token\n    @access_token.authenticate http_client\n  end\n\n  private def check_refresh_token\n    if access_token_expired?\n      refresh_access_token\n\n      @callback.call(self)\n    end\n  end\n\n  private def access_token_expired?\n    if expires_at = @expires_at\n      Time.utc >= expires_at\n    else\n      false\n    end\n  end\n\n  private def refresh_access_token\n    old_access_token = @access_token\n    @access_token = @oauth2_client.get_access_token_using_refresh_token(@access_token.refresh_token)\n\n    expires_in = @access_token.expires_in\n    if expires_in\n      @expires_at = Time.utc + expires_in.seconds\n    else\n      # If there's no expires_in in the access token, we assume it never expires\n      @expires_at = nil\n    end\n\n    @access_token.refresh_token ||= old_access_token.refresh_token\n  end\nend\n"
  },
  {
    "path": "src/oauth2.cr",
    "content": "require \"http/client\"\nrequire \"http/params\"\nrequire \"json\"\nrequire \"./oauth2/**\"\n"
  },
  {
    "path": "src/object/properties.cr",
    "content": "# This file was automatically generated by running:\n#\n#   scripts/generate_object_properties.cr\n#\n# DO NOT EDIT\n\nclass Object\n  # Defines getter methods to access instance variables.\n  #\n  # Refer to [Getters](#getters) for details.\n  macro getter(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}} {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @{{var_name}}).nil?\n            @{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @{{var_name}}\n        {% end %}\n      end\n\n    {% end %}\n  end\n\n  # Identical to `getter` but defines query methods.\n  #\n  # For example, writing:\n  #\n  # ```\n  # class Robot\n  #   getter? working\n  # end\n  # ```\n  #\n  # Is equivalent to writing:\n  #\n  # ```\n  # class Robot\n  #   def working?\n  #     @working\n  #   end\n  # end\n  # ```\n  #\n  # Refer to [Getters](#getters) for general details.\n  macro getter?(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}}? {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @{{var_name}}).nil?\n            @{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @{{var_name}}\n        {% end %}\n      end\n\n    {% end %}\n  end\n\n  # Similar to `getter` but defines both raise-on-nil methods as well as query\n  # methods that return a nilable value.\n  #\n  # If a type is specified, then it will become a nilable type (union of the\n  # type and `Nil`). Unlike the other `getter` methods the value is always\n  # initialized to `nil`. There are no initial value or lazy initialization.\n  #\n  # For example, writing:\n  #\n  # ```\n  # class Robot\n  #   getter! name : String\n  # end\n  # ```\n  #\n  # Is equivalent to writing:\n  #\n  # ```\n  # class Robot\n  #   @name : String?\n  #\n  #   def name? : String?\n  #     @name\n  #   end\n  #\n  #   def name : String\n  #     @name.not_nil!(\"Robot#name cannot be nil\")\n  #   end\n  # end\n  # ```\n  #\n  # Refer to [Getters](#getters) for general details.\n  macro getter!(*names)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        @{{name}}?\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}}? {% if type %} : {{type}}? {% end %}\n        @{{var_name}}\n      end\n\n      def {{var_name}} {% if type %} : {{type}} {% end %}\n        if (%value = @{{var_name}}).nil?\n          ::raise ::NilAssertionError.new(\"{{@type.id}}{{\"#\".id}}{{var_name}} cannot be nil\")\n        else\n          %value\n        end\n      end\n\n    {% end %}\n  end\n\n  # Generates setter methods to set instance variables.\n  #\n  # Refer to [Setters](#setters) for general details.\n  macro setter(*names)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        @{{name}}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}}=(@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Generates both `getter` and `setter`\n  # methods to access instance variables.\n  #\n  # Refer to the aforementioned macros for details.\n  macro property(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}} {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @{{var_name}}).nil?\n            @{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @{{var_name}}\n        {% end %}\n      end\n\n      def {{var_name}}=(@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Generates both `getter?` and `setter`\n  # methods to access instance variables.\n  #\n  # Refer to the aforementioned macros for details.\n  macro property?(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}}? {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @{{var_name}}).nil?\n            @{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @{{var_name}}\n        {% end %}\n      end\n\n      def {{var_name}}=(@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Generates both `getter!` and `setter`\n  # methods to access instance variables.\n  #\n  # Refer to the aforementioned macros for details.\n  macro property!(*names)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        @{{name}}?\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def {{var_name}}? {% if type %} : {{type}}? {% end %}\n        @{{var_name}}\n      end\n\n      def {{var_name}} {% if type %} : {{type}} {% end %}\n        if (%value = @{{var_name}}).nil?\n          ::raise ::NilAssertionError.new(\"{{@type.id}}{{\"#\".id}}{{var_name}} cannot be nil\")\n        else\n          %value\n        end\n      end\n\n      def {{var_name}}=(@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Defines getter methods to access class variables.\n  #\n  # For example, writing:\n  #\n  # ```\n  # class Robot\n  #   class_getter backend\n  # end\n  # ```\n  #\n  # Is equivalent to writing:\n  #\n  # ```\n  # class Robot\n  #   def self.backend\n  #     @@backend\n  #   end\n  # end\n  # ```\n  #\n  # Refer to [Getters](#getters) for details.\n  macro class_getter(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @@{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @@{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @@{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}} {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @@{{var_name}}).nil?\n            @@{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @@{{var_name}}\n        {% end %}\n      end\n\n    {% end %}\n  end\n\n  # Identical to `class_getter` but defines query methods.\n  #\n  # For example, writing:\n  #\n  # ```\n  # class Robot\n  #   class_getter? backend\n  # end\n  # ```\n  #\n  # Is equivalent to writing:\n  #\n  # ```\n  # class Robot\n  #   def self.backend?\n  #     @@backend\n  #   end\n  # end\n  # ```\n  #\n  # Refer to [Getters](#getters) for general details.\n  macro class_getter?(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @@{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @@{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @@{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}}? {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @@{{var_name}}).nil?\n            @@{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @@{{var_name}}\n        {% end %}\n      end\n\n    {% end %}\n  end\n\n  # Similar to `class_getter` but defines both raise-on-nil methods as well as\n  # query methods that return a nilable value.\n  #\n  # If a type is specified, then it will become a nilable type (union of the\n  # type and `Nil`). Unlike with `class_getter` the value is always initialized\n  # to `nil`. There are no initial value or lazy initialization.\n  #\n  # For example, writing:\n  #\n  # ```\n  # class Robot\n  #   class_getter! backend : String\n  # end\n  # ```\n  #\n  # Is equivalent to writing:\n  #\n  # ```\n  # class Robot\n  #   @@backend : String?\n  #\n  #   def self.backend? : String?\n  #     @@backend\n  #   end\n  #\n  #   def backend : String\n  #     @@backend.not_nil!(\"Robot.backend cannot be nil\")\n  #   end\n  # end\n  # ```\n  #\n  # Refer to [Getters](#getters) for general details.\n  macro class_getter!(*names)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        @@{{name}}?\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}}? {% if type %} : {{type}}? {% end %}\n        @@{{var_name}}\n      end\n\n      def self.{{var_name}} {% if type %} : {{type}} {% end %}\n        if (%value = @@{{var_name}}).nil?\n          ::raise ::NilAssertionError.new(\"{{@type.id}}{{\".\".id}}{{var_name}} cannot be nil\")\n        else\n          %value\n        end\n      end\n\n    {% end %}\n  end\n\n  # Generates setter methods to set class variables.\n  #\n  # For example, writing:\n  #\n  # ```\n  # class Robot\n  #   class_setter factories\n  # end\n  # ```\n  #\n  # Is equivalent to writing:\n  #\n  # ```\n  # class Robot\n  #   @@factories\n  #\n  #   def self.factories=(@@factories)\n  #   end\n  # end\n  # ```\n  #\n  # Refer to [Setters](#setters) for general details.\n  macro class_setter(*names)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        @@{{name}}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @@{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}}=(@@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Generates both `class_getter` and `class_setter`\n  # methods to access instance variables.\n  #\n  # Refer to the aforementioned macros for details.\n  macro class_property(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @@{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @@{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @@{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}} {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @@{{var_name}}).nil?\n            @@{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @@{{var_name}}\n        {% end %}\n      end\n\n      def self.{{var_name}}=(@@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Generates both `class_getter?` and `class_setter`\n  # methods to access instance variables.\n  #\n  # Refer to the aforementioned macros for details.\n  macro class_property?(*names, &block)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        {% if block %}\n          @@{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %}\n        {% else %}\n          @@{{name}}\n        {% end %}\n      {% elsif name.is_a?(Assign) %}\n        {% var_name = name.target %}\n        {% type = nil %}\n        @@{{name}}\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}}? {% if type %} : {{type}} {% end %}\n        {% if block %}\n          if (%value = @@{{var_name}}).nil?\n            @@{{var_name}} = {{yield}}\n          else\n            %value\n          end\n        {% else %}\n          @@{{var_name}}\n        {% end %}\n      end\n\n      def self.{{var_name}}=(@@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\n\n  # Generates both `class_getter!` and `class_setter`\n  # methods to access instance variables.\n  #\n  # Refer to the aforementioned macros for details.\n  macro class_property!(*names)\n    {% for name in names %}\n      {% if name.is_a?(TypeDeclaration) %}\n        {% var_name = name.var.id %}\n        {% type = name.type %}\n        @@{{name}}?\n      {% else %}\n        {% var_name = name.id %}\n        {% type = nil %}\n      {% end %}\n\n      def self.{{var_name}}? {% if type %} : {{type}}? {% end %}\n        @@{{var_name}}\n      end\n\n      def self.{{var_name}} {% if type %} : {{type}} {% end %}\n        if (%value = @@{{var_name}}).nil?\n          ::raise ::NilAssertionError.new(\"{{@type.id}}{{\".\".id}}{{var_name}} cannot be nil\")\n        else\n          %value\n        end\n      end\n\n      def self.{{var_name}}=(@@{{var_name}}{% if type %} : {{type}} {% end %})\n      end\n\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/object.cr",
    "content": "require \"./object/properties\"\n\n# `Object` is the base type of all Crystal objects.\n#\n# ## Getters\n#\n# Multiple macros are available to easily declare, initialize and expose\n# instance variables as well as class variables on an `Object` by generating\n# simple accessor methods.\n#\n# For example writing:\n#\n# ```\n# class Person\n#   getter name\n# end\n# ```\n#\n# Is the same as writing:\n#\n# ```\n# class Person\n#   def name\n#     @name\n#   end\n# end\n# ```\n#\n# Similarly, we can write `class_getter name` to define a class variable, which\n# generates a `def self.name` class method returning `@@name`.\n#\n# We can define as many variables as necessary in a single call. For example\n# `getter name, age, city` will create a getter method for each of `name`, `age`\n# and `city`.\n#\n# ### Type and initial value\n#\n# Instead of plain arguments, we can specify a type as well as an initial value.\n# If the initial value is simple enough Crystal should be able to infer the type\n# of the instance or class variable!\n#\n# Specifying a type will also declare the instance or class variable with said\n# type and type the accessor method arguments and return type accordingly.\n#\n# For example writing:\n#\n# ```\n# class Person\n#   getter name : String\n#   getter age = 0\n#   getter city : String = \"unspecified\"\n# end\n# ```\n#\n# Is the same as writing:\n#\n# ```\n# class Person\n#   @name : String\n#   @age = 0\n#   @city : String = \"unspecified\"\n#\n#   def name : String\n#     @name\n#   end\n#\n#   def age\n#     @age\n#   end\n#\n#   def city : String\n#     @city\n#   end\n# end\n# ```\n#\n# The initial value of an instance variable is automatically set when the object\n# is constructed. The initial value of a class variable will be set when the\n# program starts up.\n#\n# ### Lazy initialization\n#\n# Instead of eagerly initializing the value, we can lazily initialize it the\n# first time the accessor method is called.\n#\n# Since the variable will be lazily initialized the type of the variable will be\n# a nilable type. The generated method however will return the specified type\n# only (not a nilable).\n#\n# For example writing:\n#\n# ```\n# class Person\n#   getter(city : City) { City.unspecified }\n# end\n# ```\n#\n# Is equivalent to writing:\n#\n# ```\n# class Person\n#   @city : City?\n#\n#   def city : City\n#     if (city = @city).nil?\n#       @city = City.unspecified\n#     else\n#       city\n#     end\n#   end\n# end\n# ```\n#\n# ### Variants\n#\n# Please refer to the different variants to understand how they differ from the\n# general overview presented above:\n#\n# - `getter`\n# - `getter?`\n# - `getter!`\n# - `class_getter`\n# - `class_getter?`\n# - `class_getter!`\n#\n# ## Setters\n#\n# The `setter` and `class_setter` macros are the write counterparts of the\n# getter macros. They declare `name=(value)` accessor methods. The arguments\n# behave just as for the getter macros. Each setter can have a type as well as\n# an initial value. There is no lazy initialization however since the macro\n# doesn't generate a getter method.\n#\n# For example writing:\n#\n# ```\n# class Person\n#   setter name\n#   setter age = 0\n#   setter city : String = \"unspecified\"\n# end\n# ```\n#\n# Is the same as writing:\n#\n# ```\n# class Person\n#   @age = 0\n#   @city : String = \"unspecified\"\n#\n#   def name=(@name)\n#   end\n#\n#   def age=(@age)\n#   end\n#\n#   def city=(@city : String) : String\n#   end\n# end\n# ```\n#\n# For class variables we'd have called `class_setter name` that would have\n# generated a `def self.name=(@@name)` class method instead.\n#\n# ## Properties\n#\n# The property macros define both getter and setter methods at once.\n#\n# For example writing:\n#\n# ```\n# class Person\n#   property name\n# end\n# ```\n#\n# Is equivalent to writing:\n#\n# ```\n# class Person\n#   getter name\n#   setter name\n# end\n# ```\n#\n# Which is the same as writing:\n#\n# ```\n# class Person\n#   def name\n#     @name\n#   end\n#\n#   def name=(@name)\n#   end\n# end\n# ```\n#\n# Refer to [Getters](#getters) and [Setters](#setters) above for details. The\n# macros take the exact same arguments.\nclass Object\n  # Returns `true` if this object is equal to *other*.\n  #\n  # Subclasses override this method to provide class-specific meaning.\n  abstract def ==(other)\n\n  # Returns `true` if this object is not equal to *other*.\n  #\n  # By default this method is implemented as `!(self == other)`\n  # so there's no need to override this unless there's a more efficient\n  # way to do it.\n  def !=(other)\n    !(self == other)\n  end\n\n  # Shortcut to `!(self =~ other)`.\n  def !~(other)\n    !(self =~ other)\n  end\n\n  # Case equality.\n  #\n  # The `===` method is used in a `case ... when ... end` expression.\n  #\n  # For example, this code:\n  #\n  # ```\n  # case value\n  # when x\n  #   # something when x\n  # when y\n  #   # something when y\n  # end\n  # ```\n  #\n  # Is equivalent to this code:\n  #\n  # ```\n  # if x === value\n  #   # something when x\n  # elsif y === value\n  #   # something when y\n  # end\n  # ```\n  #\n  # Object simply implements `===` by invoking `==`, but subclasses\n  # (notably `Regex`) can override it to provide meaningful case-equality semantics.\n  def ===(other)\n    self == other\n  end\n\n  # Pattern match.\n  #\n  # Overridden by descendants (notably `Regex` and `String`) to provide meaningful\n  # pattern-match semantics.\n  def =~(other)\n    nil\n  end\n\n  # Appends this object's value to *hasher*, and returns the modified *hasher*.\n  #\n  # Usually the macro `def_hash` can be used to generate this method.\n  # Otherwise, invoke `hash(hasher)` on each object's instance variables to\n  # accumulate the result:\n  #\n  # ```\n  # def hash(hasher)\n  #   hasher = @some_ivar.hash(hasher)\n  #   hasher = @some_other_ivar.hash(hasher)\n  #   hasher\n  # end\n  # ```\n  abstract def hash(hasher)\n\n  # Generates an `UInt64` hash value for this object.\n  #\n  # This method must have the property that `a == b` implies `a.hash == b.hash`.\n  #\n  # The hash value is used along with `==` by the `Hash` class to determine if two objects\n  # reference the same hash key.\n  #\n  # Subclasses must not override this method. Instead, they must define `hash(hasher)`,\n  # though usually the macro `def_hash` can be used to generate this method.\n  def hash\n    hash(Crystal::Hasher.new).result\n  end\n\n  # Returns a nicely readable and concise string representation of this object,\n  # typically intended for users.\n  #\n  # This method should usually **not** be overridden. It delegates to\n  # `#to_s(IO)` which can be overridden for custom implementations.\n  #\n  # Also see `#inspect`.\n  def to_s : String\n    String.build do |io|\n      to_s io\n    end\n  end\n\n  # Prints a nicely readable and concise string representation of this object,\n  # typically intended for users, to *io*.\n  #\n  # This method is called when an object is interpolated in a string literal:\n  # ```\n  # \"foo #{bar} baz\" # calls bar.to_io with the builder for this string\n  # ```\n  #\n  # `IO#<<` calls this method to append an object to itself:\n  # ```\n  # io << bar # calls bar.to_s(io)\n  # ```\n  #\n  # Thus implementations must not interpolate `self` in a string literal or call\n  # `io << self` which both would lead to an endless loop.\n  #\n  # Also see `#inspect(IO)`.\n  abstract def to_s(io : IO) : Nil\n\n  # Returns an unambiguous and information-rich string representation of this\n  # object, typically intended for developers.\n  #\n  # This method should usually **not** be overridden. It delegates to\n  # `#inspect(IO)` which can be overridden for custom implementations.\n  #\n  # Also see `#to_s`.\n  def inspect : String\n    String.build do |io|\n      inspect io\n    end\n  end\n\n  # Prints to *io* an unambiguous and information-rich string representation of this\n  # object, typically intended for developers.\n  #\n  # It is similar to `#to_s(IO)`, but often provides more information. Ideally, it should\n  # contain sufficient information to be able to recreate an object with the same value\n  # (given an identical environment).\n  #\n  # For types that don't provide a custom implementation of this method,\n  # default implementation delegates to `#to_s(IO)`. This said, it is advisable to\n  # have an appropriate `#inspect` implementation on every type. Default\n  # implementations are provided by `Struct#inspect` and `Reference#inspect`.\n  #\n  # `::p` and `::p!` use this method to print an object in `STDOUT`.\n  def inspect(io : IO) : Nil\n    to_s io\n  end\n\n  # Pretty prints `self` into the given printer.\n  #\n  # By default appends a text that is the result of invoking\n  # `#inspect` on `self`. Subclasses should override\n  # for custom pretty printing.\n  def pretty_print(pp : PrettyPrint) : Nil\n    pp.text(inspect)\n  end\n\n  # Returns a pretty printed version of `self`.\n  def pretty_inspect(width = 79, newline = \"\\n\", indent = 0) : String\n    String.build do |io|\n      PrettyPrint.format(self, io, width, newline, indent)\n    end\n  end\n\n  # Yields `self` to the block, and then returns `self`.\n  #\n  # The primary purpose of this method is to \"tap into\" a method chain,\n  # in order to perform operations on intermediate results within the chain.\n  #\n  # ```\n  # (1..10).tap { |x| puts \"original: #{x.inspect}\" }\n  #   .to_a.tap { |x| puts \"array: #{x.inspect}\" }\n  #   .select { |x| x % 2 == 0 }.tap { |x| puts \"evens: #{x.inspect}\" }\n  #   .map { |x| x*x }.tap { |x| puts \"squares: #{x.inspect}\" }\n  # ```\n  def tap(&)\n    yield self\n    self\n  end\n\n  # Yields `self`. `Nil` overrides this method and doesn't yield.\n  #\n  # This method is useful for dealing with nilable types, to safely\n  # perform operations only when the value is not `nil`.\n  #\n  # ```\n  # # First program argument in downcase, or nil\n  # ARGV[0]?.try &.downcase\n  # ```\n  def try(&)\n    yield self\n  end\n\n  # Returns `true` if `self` is included in the *collection* argument.\n  #\n  # ```\n  # 10.in?(0..100)     # => true\n  # 10.in?({0, 1, 10}) # => true\n  # 10.in?(0, 1, 10)   # => true\n  # 10.in?(:foo, :bar) # => false\n  # ```\n  def in?(collection : Object) : Bool\n    collection.includes?(self)\n  end\n\n  # :ditto:\n  def in?(*values : Object) : Bool\n    in?(values)\n  end\n\n  # Returns `self`.\n  #\n  # `Nil` overrides this method and raises `NilAssertionError`, see `Nil#not_nil!`.\n  #\n  # This method can be used to remove `Nil` from a union type.\n  # However, it should be avoided if possible and is often considered a code smell.\n  # Usually, you can write code in a way that the compiler can safely exclude `Nil` types,\n  # for example using [`if var`](https://crystal-lang.org/reference/syntax_and_semantics/if_var.html).\n  # `not_nil!` is only meant as a last resort when there's no other way to explain this to the compiler.\n  # Either way, consider instead raising a concrete exception with a descriptive message.\n  def not_nil!\n    self\n  end\n\n  # :ditto:\n  #\n  # *message* has no effect. It is only used by `Nil#not_nil!(message = nil)`.\n  def not_nil!(message)\n    # FIXME: the above param-less overload cannot be expressed as an optional\n    # parameter here, because that would copy the receiver if it is a struct;\n    # see https://github.com/crystal-lang/crystal/issues/13263#issuecomment-1492885817\n    # and also #13265\n    self\n  end\n\n  # Returns `self`.\n  #\n  # ```\n  # str = \"hello\"\n  # str.itself.object_id == str.object_id # => true\n  # ```\n  def itself\n    self\n  end\n\n  # Returns a shallow copy (“duplicate”) of this object.\n  #\n  # In order to create a new object with the same value as an existing one, there\n  # are two possible routes:\n  #\n  # * create a *shallow copy* (`#dup`): Constructs a new object with all its\n  #   properties' values identical to the original object's properties. They\n  #   are shared references. That means for mutable values that changes to\n  #   either object's values will be present in both's.\n  # * create a *deep copy* (`#clone`): Constructs a new object with all its\n  #   properties' values being recursive deep copies of the original object's\n  #   properties.\n  #   There is no shared state and the new object is a completely independent\n  #   copy, including everything inside it. This may not be available for every\n  #   type.\n  #\n  # A shallow copy is only one level deep whereas a deep copy copies everything\n  # below.\n  #\n  # This distinction is only relevant for compound values. Primitive types\n  # do not have any properties that could be shared or cloned.\n  # In that case, `dup` and `clone` are exactly the same.\n  #\n  # The `#clone` method can't be defined on `Object`. It's not\n  # generically available for every type because cycles could be involved, and\n  # the clone logic might not need to clone everything.\n  #\n  # Many types in the standard library, like `Array`, `Hash`, `Set` and\n  # `Deque`, and all primitive types, define `dup` and `clone`.\n  #\n  # Example:\n  #\n  # ```\n  # original = {\"foo\" => [1, 2, 3]}\n  # shallow_copy = original.dup\n  # deep_copy = original.clone\n  #\n  # # \"foo\" references the same array object for both original and shallow copy,\n  # # but not for a deep copy:\n  # original[\"foo\"] << 4\n  # shallow_copy[\"foo\"] # => [1, 2, 3, 4]\n  # deep_copy[\"foo\"]    # => [1, 2, 3]\n  #\n  # # Assigning new value does not share it to either copy:\n  # original[\"foo\"] = [1]\n  # shallow_copy[\"foo\"] # => [1, 2, 3, 4]\n  # deep_copy[\"foo\"]    # => [1, 2, 3]\n  # ```\n  abstract def dup\n\n  # Unsafely reinterprets the bytes of an object as being of another `type`.\n  #\n  # This method is useful to treat a type that is represented as a chunk of\n  # bytes as another type where those bytes convey useful information. As an\n  # example, you can check the individual bytes of an `Int32`:\n  #\n  # ```\n  # 0x01020304.unsafe_as(StaticArray(UInt8, 4)) # => StaticArray[4, 3, 2, 1]\n  # ```\n  #\n  # Or treat the bytes of a `Float64` as an `Int64`:\n  #\n  # ```\n  # 1.234_f64.unsafe_as(Int64) # => 4608236261112822104\n  # ```\n  #\n  # This method is **unsafe** because it behaves unpredictably when the given\n  # `type` doesn't have the same bytesize as the receiver, or when the given\n  # `type` representation doesn't semantically match the underlying bytes.\n  #\n  # Also note that because `unsafe_as` is a regular method, unlike the pseudo-method\n  # `as`, you can't specify some types in the type grammar using a short notation, so\n  # specifying a static array must always be done as `StaticArray(T, N)`, a tuple\n  # as `Tuple(...)` and so on, never as `UInt8[4]` or `{Int32, Int32}`.\n  def unsafe_as(type : T.class) forall T\n    x = self\n    pointerof(x).as(T*).value\n  end\n\n  # Delegate *methods* to *to*.\n  #\n  # Note that due to current language limitations this is only useful\n  # when no captured blocks are involved.\n  #\n  # ```\n  # class StringWrapper\n  #   def initialize(@string : String)\n  #   end\n  #\n  #   delegate downcase, to: @string\n  #   delegate gsub, to: @string\n  #   delegate empty?, capitalize, to: @string\n  #   delegate :[], to: @string\n  # end\n  #\n  # wrapper = StringWrapper.new \"HELLO\"\n  # wrapper.downcase       # => \"hello\"\n  # wrapper.gsub(/E/, \"A\") # => \"HALLO\"\n  # wrapper.empty?         # => false\n  # wrapper.capitalize     # => \"Hello\"\n  # ```\n  macro delegate(*methods, to object)\n    {% if compare_versions(::Crystal::VERSION, \"1.12.0-dev\") >= 0 %}\n      {% eq_operators = %w(<= >= == != []= ===) %}\n      {% for method in methods %}\n        {% if method.id.ends_with?('=') && !eq_operators.includes?(method.id.stringify) %}\n          def {{method.id}}(arg)\n            {{object.id}}.{{method.id}} arg\n          end\n        {% else %}\n          def {{method.id}}(*args, **options)\n            {{object.id}}.{{method.id}}(*args, **options)\n          end\n\n          def {{method.id}}(*args, **options)\n            {{object.id}}.{{method.id}}(*args, **options) do |*yield_args|\n              yield *yield_args\n            end\n          end\n        {% end %}\n      {% end %}\n    {% else %}\n      {% for method in methods %}\n        {% if method.id.ends_with?('=') && method.id != \"[]=\" %}\n          def {{method.id}}(arg)\n            {{object.id}}.{{method.id}} arg\n          end\n        {% else %}\n          def {{method.id}}(*args, **options)\n            {{object.id}}.{{method.id}}(*args, **options)\n          end\n\n          {% if method.id != \"[]=\" %}\n            def {{method.id}}(*args, **options)\n              {{object.id}}.{{method.id}}(*args, **options) do |*yield_args|\n                yield *yield_args\n              end\n            end\n          {% end %}\n        {% end %}\n      {% end %}\n    {% end %}\n  end\n\n  # Defines a `hash(hasher)` that will append a hash value for the given fields.\n  #\n  # ```\n  # class Person\n  #   def initialize(@name, @age)\n  #   end\n  #\n  #   # Define a hash(hasher) method based on @name and @age\n  #   def_hash @name, @age\n  # end\n  # ```\n  macro def_hash(*fields)\n    def hash(hasher)\n      {% for field in fields %}\n        hasher = {{field.id}}.hash(hasher)\n      {% end %}\n      hasher\n    end\n  end\n\n  # Defines an `==` method by comparing the given fields.\n  #\n  # The generated `==` method has a `self` restriction.\n  # For classes it will first compare by reference and return `true`\n  # when an object instance is compared with itself, without comparing\n  # any of the fields.\n  #\n  # ```\n  # class Person\n  #   def initialize(@name, @age)\n  #   end\n  #\n  #   # Define a `==` method that compares @name and @age\n  #   def_equals @name, @age\n  # end\n  # ```\n  macro def_equals(*fields)\n    def ==(other : self)\n      {% if @type.class? %}\n        return true if same?(other)\n      {% end %}\n      {% for field in fields %}\n        return false unless {{field.id}} == other.{{field.id}}\n      {% end %}\n      true\n    end\n  end\n\n  # Defines `hash` and `==` method from the given fields.\n  #\n  # The generated `==` method has a `self` restriction.\n  #\n  # ```\n  # class Person\n  #   def initialize(@name, @age)\n  #   end\n  #\n  #   # Define a hash method based on @name and @age\n  #   # Define a `==` method that compares @name and @age\n  #   def_equals_and_hash @name, @age\n  # end\n  # ```\n  macro def_equals_and_hash(*fields)\n    def_equals {{fields.splat}}\n    def_hash {{fields.splat}}\n  end\n\n  # Forwards missing methods to *delegate*.\n  #\n  # ```\n  # class StringWrapper\n  #   def initialize(@string : String)\n  #   end\n  #\n  #   forward_missing_to @string\n  # end\n  #\n  # wrapper = StringWrapper.new \"HELLO\"\n  # wrapper.downcase       # => \"hello\"\n  # wrapper.gsub(/E/, \"A\") # => \"HALLO\"\n  # ```\n  macro forward_missing_to(delegate)\n    macro method_missing(call)\n      {{delegate}}.\\{{call}}\n    end\n  end\n\n  # Defines a `clone` method that returns a copy of this object with all\n  # instance variables cloned (`clone` is in turn invoked on them).\n  macro def_clone\n    # Returns a copy of `self` with all instance variables cloned.\n    def clone\n      \\{% if @type < ::Reference && !@type.instance_vars.map(&.type).all? { |t| t == ::Bool || t == ::Char || t == ::Symbol || t == ::String || t < ::Number::Primitive } %}\n        exec_recursive_clone do |hash|\n          clone = \\{{@type}}.allocate\n          hash[object_id] = clone.object_id\n          clone.initialize_copy(self)\n          ::GC.add_finalizer(clone) if clone.responds_to?(:finalize)\n          clone\n        end\n      \\{% else %}\n        clone = \\{{@type}}.allocate\n        clone.initialize_copy(self)\n        ::GC.add_finalizer(clone) if clone.responds_to?(:finalize)\n        clone\n      \\{% end %}\n    end\n\n    protected def initialize_copy(other)\n      \\{% for ivar in @type.instance_vars %}\n        @\\{{ivar.id}} = other.@\\{{ivar.id}}.clone\n      \\{% end %}\n    end\n  end\n\n  protected def self.set_crystal_type_id(ptr)\n    ptr.as(Pointer(typeof(crystal_instance_type_id))).value = crystal_instance_type_id\n    ptr\n  end\n\n  # :nodoc:\n  #\n  # Generates a class getter with a lazy initializer for a thread local\n  # variable.\n  #\n  # Unlike the `ThreadLocal` annotation, the value is guaranteed to always be\n  # reachable by the GC that won't collect it until it's really unreachable. We\n  # achieve that by using the `ThreadLocal` annocation (unreachable by GC) to\n  # speedup direct accesses while still keeping a direct reference on the\n  # current `Thread` object whose lifetime outlives the actual thread.\n  #\n  # For example:\n  #\n  # ```\n  # class Foo\n  #   thread_local(instance : Foo) { Foo.new }\n  # end\n  #\n  # # thread 1:\n  # Foo.instance # => <Foo:0x76a066968000>\n  # Foo.instance # => <Foo:0x76a066968000>\n  #\n  # # thread 2:\n  # Foo.instance # => <Foo:0x76a06116ae30>\n  # ```\n  macro thread_local(decl, &constructor)\n    {% raise \"The thread_local macro expects a TypeDeclaration\" unless decl.is_a?(TypeDeclaration) %}\n    {% name = decl.var.id %}\n    {% tls_name = \"__tls_#{@type.name.gsub(/:/, \"_\")}__#{name}\".id %}\n    {% emulated_tls = flag?(:android) || flag?(:openbsd) || (flag?(:win32) && flag?(:gnu)) %}\n\n    {% unless emulated_tls %}\n      @[ThreadLocal]\n      @@{{name}} : {{decl.type}} | Nil\n    {% end %}\n\n    def self.{{name}} : {{decl.type}}\n      {% if emulated_tls %}\n        Thread.current.{{tls_name}} ||= {{yield}}\n      {% else %}\n        if (value = @@{{name}}).nil?\n          Thread.current.{{tls_name}} = @@{{name}} = {{yield}}\n        else\n          value\n        end\n      {% end %}\n    end\n\n    # :nodoc:\n    class ::Thread\n      # :nodoc:\n      property {{tls_name}} : {{decl.type}} | Nil\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/algorithm.cr",
    "content": "require \"openssl\"\n\nmodule OpenSSL\n  # Indicates the possible hash algorithms for `OpenSSL` digest operations.\n  enum Algorithm\n    MD4\n    MD5\n    RIPEMD160\n    SHA1\n    SHA224\n    SHA256\n    SHA384\n    SHA512\n\n    # Returns the appropriate equivalent hash algorithm that corresponds to the\n    # current enum value.\n    #\n    # The internal bindings to the `LibCrypto` digest operations sometimes require a hash algorithm\n    # implementation to be passed as one of the arguments.\n    def to_evp\n      case self\n      when MD4       then LibCrypto.evp_md4\n      when MD5       then LibCrypto.evp_md5\n      when RIPEMD160 then LibCrypto.evp_ripemd160\n      when SHA1      then LibCrypto.evp_sha1\n      when SHA224    then LibCrypto.evp_sha224\n      when SHA256    then LibCrypto.evp_sha256\n      when SHA384    then LibCrypto.evp_sha384\n      when SHA512    then LibCrypto.evp_sha512\n      else                raise \"Unsupported algorithm: #{self}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/bio.cr",
    "content": "require \"./lib_crypto\"\nrequire \"./ktls\"\n\n# :nodoc:\nclass OpenSSL::BIO\n  CRYSTAL_BIO = begin\n    bio_id = LibCrypto.BIO_get_new_index\n    raise OpenSSL::Error.new(\"BIO_get_new_index\") if bio_id == -1\n\n    bio_type = bio_id | LibCrypto::BIO_TYPE_SOURCE_SINK | LibCrypto::BIO_TYPE_DESCRIPTOR\n    biom = LibCrypto.BIO_meth_new(bio_type, \"Crystal BIO\")\n    raise OpenSSL::Error.new(\"BIO_meth_new\") if biom.null?\n\n    {% if LibCrypto.has_method?(:BIO_meth_set_read_ex) %}\n      LibCrypto.BIO_meth_set_read_ex(biom, ->read_ex)\n    {% else %}\n      LibCrypto.BIO_meth_set_read(biom, ->read)\n    {% end %}\n\n    {% if LibCrypto.has_method?(:BIO_meth_set_write_ex) %}\n      LibCrypto.BIO_meth_set_write_ex(biom, ->write_ex)\n    {% else %}\n      LibCrypto.BIO_meth_set_write(biom, ->write)\n    {% end %}\n\n    LibCrypto.BIO_meth_set_ctrl(biom, ->ctrl)\n    LibCrypto.BIO_meth_set_create(biom, ->create)\n    LibCrypto.BIO_meth_set_destroy(biom, ->destroy)\n\n    biom\n  end\n\n  def self.read_ex(b, buffer, len, readp)\n    size = len > Int32::MAX ? Int32::MAX : len.to_i\n    bio = Box(BIO).unbox(LibCrypto.BIO_get_data(b))\n\n    {% if OpenSSL.has_constant?(:KTLS) %}\n      if bio.ktls_recv?\n        case ret = KTLS.read_record(bio.socket, buffer, size)\n        in Int32\n          readp.value = LibC::SizeT.new(ret)\n          return 1\n        in Errno\n          readp.value = LibC::SizeT.new(0)\n          return ret.value\n        end\n      end\n    {% end %}\n\n    ret = bio.io.read(Slice.new(buffer, size))\n    readp.value = LibC::SizeT.new(ret)\n    1\n  end\n\n  def self.read(b, buffer, len)\n    bio = Box(BIO).unbox(LibCrypto.BIO_get_data(b))\n    bio.io.read(Slice.new(buffer, len)).to_i\n  end\n\n  def self.write_ex(b, data, len, writep)\n    size = len > Int32::MAX ? Int32::MAX : len.to_i\n    bio = Box(BIO).unbox(LibCrypto.BIO_get_data(b))\n\n    {% if OpenSSL.has_constant?(:KTLS) %}\n      if bio.ktls_send_ctrl_msg?\n        case ret = KTLS.send_ctrl_message(bio.socket, bio.ktls_record_type, data, size)\n        in Int32\n          LibCrypto.BIO_clear_flags(b, LibCrypto::BIO_FLAGS_KTLS_TX_CTRL_MSG)\n          writep.value = LibC::SizeT.new(ret)\n          return 1\n        in Errno\n          writep.value = LibC::SizeT.new(0)\n          return ret.value\n        end\n      end\n    {% end %}\n\n    bio.io.write Slice.new(data, size)\n    writep.value = LibC::SizeT.new(size)\n    1\n  end\n\n  def self.write(b, data, len)\n    bio = Box(BIO).unbox(LibCrypto.BIO_get_data(b))\n    bio.io.write Slice.new(data, len)\n    len\n  end\n\n  def self.ctrl(b, cmd, num, ptr)\n    bio = Box(BIO).unbox(LibCrypto.BIO_get_data(b))\n    val = {% begin %}\n            case cmd\n            when LibCrypto::CTRL_FLUSH\n              bio.io.flush\n              1\n            when LibCrypto::CTRL_PUSH, LibCrypto::CTRL_POP, LibCrypto::CTRL_EOF\n              0\n            {% if OpenSSL.has_constant?(:KTLS) %}\n              when LibCrypto::CTRL_SET_KTLS\n                socket = bio.socket\n                is_tx = num != 0\n                if KTLS.enable(socket) && KTLS.start(socket, ptr, is_tx)\n                  LibCrypto.BIO_set_flags(b, is_tx ? LibCrypto::BIO_FLAGS_KTLS_TX : LibCrypto::BIO_FLAGS_KTLS_RX)\n                  1\n                else\n                  0\n                end\n              when LibCrypto::CTRL_GET_KTLS_SEND\n                bio.ktls_send? ? 1 : 0\n              when LibCrypto::CTRL_GET_KTLS_RECV\n                bio.ktls_recv? ? 1 : 0\n              when LibCrypto::CTRL_SET_KTLS_TX_SEND_CTRL_MSG\n                LibCrypto.BIO_set_flags(b, LibCrypto::BIO_FLAGS_KTLS_TX_CTRL_MSG)\n                bio.ktls_record_type = num.to_u8\n                0\n              when LibCrypto::CTRL_CLEAR_KTLS_TX_CTRL_MSG\n                LibCrypto.BIO_clear_flags(b, LibCrypto::BIO_FLAGS_KTLS_TX_CTRL_MSG)\n                0\n              when LibCrypto::CTRL_SET_KTLS_TX_ZEROCOPY_SENDFILE\n                ret = KTLS.enable_tx_zerocopy_sendfile(bio.socket)\n                LibCrypto.BIO_set_flags(b, LibCrypto::BIO_FLAGS_KTLS_TX_ZEROCOPY_SENDFILE) if ret\n                ret ? 1 : 0\n            {% else %}\n              when LibCrypto::CTRL_SET_KTLS,\n                   LibCrypto::CTRL_GET_KTLS_SEND,\n                   LibCrypto::CTRL_GET_KTLS_RECV,\n                   LibCrypto::CTRL_SET_KTLS_TX_ZEROCOPY_SENDFILE\n                0\n            {% end %}\n            when LibCrypto::BIO_C_GET_FD\n              io = bio.io\n              if io.is_a?(Socket) || io.is_a?(IO::FileDescriptor)\n                io.fd\n              else\n                -1\n              end\n            else\n              STDERR.puts \"WARNING: Unsupported BIO ctrl call (#{cmd})\"\n              0\n            end\n          {% end %}\n    LibCrypto::Long.new(val)\n  end\n\n  def self.create(bio)\n    LibCrypto.BIO_set_shutdown(bio, 1)\n    LibCrypto.BIO_set_init(bio, 1)\n    1\n  end\n\n  def self.destroy(bio)\n    LibCrypto.BIO_set_data(bio, Pointer(Void).null)\n    1\n  end\n\n  def initialize(@io : IO)\n    @bio = LibCrypto.BIO_new(CRYSTAL_BIO)\n    raise OpenSSL::Error.new(\"BIO_new\") if @bio.null?\n\n    LibCrypto.BIO_set_data(@bio, Box.box(self))\n  end\n\n  getter io : IO\n\n  def socket\n    @io.as(Socket)\n  end\n\n  {% if OpenSSL.has_constant?(:KTLS) %}\n    property ktls_record_type : UInt8 = 0\n  {% end %}\n\n  def ktls_send?\n    @io.is_a?(Socket) && flag?(LibCrypto::BIO_FLAGS_KTLS_TX)\n  end\n\n  def ktls_send_ctrl_msg?\n    @io.is_a?(Socket) && flag?(LibCrypto::BIO_FLAGS_KTLS_TX_CTRL_MSG)\n  end\n\n  def ktls_recv?\n    @io.is_a?(Socket) && flag?(LibCrypto::BIO_FLAGS_KTLS_RX)\n  end\n\n  private def flag?(flag)\n    (LibCrypto.BIO_test_flags(@bio, flag) & flag) == flag\n  end\n\n  def to_unsafe\n    @bio\n  end\nend\n"
  },
  {
    "path": "src/openssl/cipher.cr",
    "content": "require \"random/secure\"\nrequire \"openssl\"\n\n# A class which can be used to encrypt and decrypt data using a specified cipher.\n#\n# NOTE: The ciphers available to an application are determined by the linked version of the system SSL library.\n#       A comprehensive list of ciphers can be found in the\n#       [OpenSSL Cipher documentation](https://www.openssl.org/docs/man3.0/man1/openssl-ciphers.html#CIPHER-STRINGS).\n#\n# ```\n# require \"random/secure\"\n# require \"openssl\"\n#\n# def encrypt(data, key, iv)\n#   cipher = OpenSSL::Cipher.new(\"aes-256-cbc\")\n#   cipher.encrypt\n#   cipher.key = key\n#   cipher.iv = iv\n#\n#   io = IO::Memory.new\n#   io.write(cipher.update(data))\n#   io.write(cipher.final)\n#   io.rewind\n#\n#   io.to_slice\n# end\n#\n# def decrypt(data, key, iv)\n#   cipher = OpenSSL::Cipher.new(\"aes-256-cbc\")\n#   cipher.decrypt\n#   cipher.key = key\n#   cipher.iv = iv\n#\n#   io = IO::Memory.new\n#   io.write(cipher.update(data))\n#   io.write(cipher.final)\n#   io.rewind\n#\n#   io.gets_to_end\n# end\n#\n# key = Random::Secure.random_bytes(64) # You can also use OpenSSL::Cipher#random_key to do this same thing\n# iv = Random::Secure.random_bytes(32)  # You can also use OpenSSL::Cipher#random_iv to do this same thing\n#\n# encrypted_data = encrypt(\"Encrypted\", key, iv)    # => Bytes[95, 182, 21, 86, 193, 155, 149, 164, 82, 102, 171, 182, 56, 153, 223, 33]\n# decrypted_data = decrypt(encrypted_data, key, iv) # => \"Encrypted\"\n# ```\nclass OpenSSL::Cipher\n  class Error < OpenSSL::Error\n  end\n\n  def initialize(name)\n    cipher = LibCrypto.evp_get_cipherbyname name\n    raise ArgumentError.new \"Unsupported cipher algorithm #{name.inspect}\" unless cipher\n\n    @ctx = LibCrypto.evp_cipher_ctx_new\n    # The EVP which has EVP_CIPH_RAND_KEY flag (such as DES3) allows\n    # uninitialized key, but other EVPs (such as AES) does not allow it.\n    # Calling EVP_CipherUpdate() without initializing key causes SEGV so\n    # we set the data filled with \"\\0\" as the key by default.\n    cipherinit cipher: cipher, key: \"\\0\" * LibCrypto::EVP_MAX_KEY_LENGTH\n  end\n\n  # Sets this cipher to encryption mode.\n  def encrypt : Nil\n    cipherinit enc: 1\n  end\n\n  # Sets this cipher to decryption mode.\n  def decrypt : Nil\n    cipherinit enc: 0\n  end\n\n  def key=(key)\n    raise ArgumentError.new \"Key length too short: wanted #{key_len}, got #{key.bytesize}\" if key.bytesize < key_len\n    cipherinit key: key\n    key\n  end\n\n  def iv=(iv)\n    raise ArgumentError.new \"IV length too short: wanted #{iv_len}, got #{iv.bytesize}\" if iv.bytesize < iv_len\n    cipherinit iv: iv\n    iv\n  end\n\n  # Sets the key using `Random::Secure`.\n  def random_key\n    key = Random::Secure.random_bytes key_len\n    self.key = key\n  end\n\n  # Sets the iv using `Random::Secure`.\n  def random_iv\n    iv = Random::Secure.random_bytes iv_len\n    self.iv = iv\n  end\n\n  # Resets the encrypt/decrypt mode.\n  def reset\n    cipherinit\n  end\n\n  # Add the data to be encrypted or decrypted to this cipher's buffer.\n  def update(data)\n    slice = data.to_slice\n    buffer_length = slice.size + block_size\n    buffer = Bytes.new(buffer_length)\n    if LibCrypto.evp_cipherupdate(@ctx, buffer, pointerof(buffer_length), slice, slice.size) != 1\n      raise Error.new \"EVP_CipherUpdate\"\n    end\n\n    buffer[0, buffer_length]\n  end\n\n  # Outputs the decrypted or encrypted buffer.\n  def final : Bytes\n    buffer_length = block_size\n    buffer = Bytes.new(buffer_length)\n\n    if LibCrypto.evp_cipherfinal_ex(@ctx, buffer, pointerof(buffer_length)) != 1\n      raise Error.new \"EVP_CipherFinal_ex\"\n    end\n\n    buffer[0, buffer_length]\n  end\n\n  def padding=(pad : Bool)\n    if LibCrypto.evp_cipher_ctx_set_padding(@ctx, pad ? 1 : 0) != 1\n      raise Error.new \"EVP_CIPHER_CTX_set_padding\"\n    end\n\n    pad\n  end\n\n  def name : String\n    nid = LibCrypto.evp_cipher_nid cipher\n    sn = LibCrypto.obj_nid2sn nid\n    String.new sn\n  end\n\n  def block_size : Int32\n    LibCrypto.evp_cipher_block_size cipher\n  end\n\n  # How many bytes the key should be.\n  def key_len : Int32\n    LibCrypto.evp_cipher_key_length cipher\n  end\n\n  # How many bytes the iv should be.\n  def iv_len : Int32\n    LibCrypto.evp_cipher_iv_length cipher\n  end\n\n  def finalize\n    LibCrypto.evp_cipher_ctx_free(@ctx) if @ctx\n    @ctx = typeof(@ctx).null\n  end\n\n  def authenticated? : Bool\n    LibCrypto.evp_cipher_flags(cipher).includes?(LibCrypto::CipherFlags::EVP_CIPH_FLAG_AEAD_CIPHER)\n  end\n\n  private def cipherinit(cipher = nil, engine = nil, key = nil, iv = nil, enc = -1)\n    if LibCrypto.evp_cipherinit_ex(@ctx, cipher, engine, key, iv, enc) != 1\n      raise Error.new \"EVP_CipherInit_ex\"\n    end\n\n    nil\n  end\n\n  private def cipher\n    LibCrypto.evp_cipher_ctx_cipher @ctx\n  end\nend\n"
  },
  {
    "path": "src/openssl/digest.cr",
    "content": "require \"./lib_crypto\"\nrequire \"./error\"\nrequire \"digest/digest\"\n\nmodule OpenSSL\n  class Digest < ::Digest\n    class Error < OpenSSL::Error; end\n\n    class UnsupportedError < Error; end\n\n    getter name : String\n    @ctx : LibCrypto::EVP_MD_CTX\n\n    def initialize(@name : String)\n      @ctx = new_evp_mt_ctx(name)\n    end\n\n    protected def initialize(@name : String, @ctx : LibCrypto::EVP_MD_CTX)\n    end\n\n    private def new_evp_mt_ctx(name)\n      md = LibCrypto.evp_get_digestbyname(name)\n      unless md\n        raise UnsupportedError.new(\"Unsupported digest algorithm: #{name}\")\n      end\n      ctx = LibCrypto.evp_md_ctx_new\n      unless ctx\n        raise Error.new \"Digest initialization failed.\"\n      end\n      if LibCrypto.evp_digestinit_ex(ctx, md, nil) != 1\n        raise Error.new \"Digest initialization failed.\"\n      end\n      raise Error.new(\"Invalid EVP_MD_CTX\") unless ctx\n      ctx\n    end\n\n    def finalize\n      LibCrypto.evp_md_ctx_free(self)\n    end\n\n    def dup\n      Digest.new(@name, dup_ctx)\n    end\n\n    protected def dup_ctx\n      ctx = LibCrypto.evp_md_ctx_new\n      if LibCrypto.evp_md_ctx_copy(ctx, @ctx) == 0\n        LibCrypto.evp_md_ctx_free(ctx)\n        raise Error.new(\"Unable to dup digest\")\n      end\n      ctx\n    end\n\n    private def reset_impl : Nil\n      if LibCrypto.evp_digestinit_ex(self, to_unsafe_md, nil) != 1\n        raise Error.new \"Digest initialization failed.\"\n      end\n    end\n\n    private def update_impl(data : Bytes) : Nil\n      check_finished\n      if LibCrypto.evp_digestupdate(self, data, data.bytesize) != 1\n        raise Error.new \"EVP_DigestUpdate\"\n      end\n    end\n\n    private def final_impl(dst : Bytes) : Nil\n      unless dst.bytesize == digest_size\n        raise ArgumentError.new(\"Incorrect data size: #{dst.bytesize}, expected: #{digest_size}\")\n      end\n      if LibCrypto.evp_digestfinal_ex(@ctx, dst, nil) != 1\n        raise Error.new \"EVP_DigestFinal_ex\"\n      end\n    end\n\n    def digest_size : Int32\n      LibCrypto.evp_md_size(to_unsafe_md).to_i\n    end\n\n    def block_size : Int32\n      LibCrypto.evp_md_block_size(to_unsafe_md).to_i\n    end\n\n    def to_unsafe_md\n      LibCrypto.evp_md_ctx_md(self)\n    end\n\n    def to_unsafe\n      @ctx\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/error.cr",
    "content": "require \"./lib_ssl\"\nrequire \"./lib_crypto\"\n\nmodule OpenSSL\n  class Error < Exception\n    getter! code : LibCrypto::ULong\n\n    def initialize(message = nil, fetched = false, cause : Exception? = nil)\n      @code ||= LibCrypto::ULong.new(0)\n\n      if fetched\n        super(message, cause: cause)\n      else\n        @code, error = fetch_error_details\n        super(message ? \"#{message}: #{error}\" : error, cause: cause)\n      end\n    end\n\n    protected def fetch_error_details\n      code = LibCrypto.err_get_error\n      message = String.new(LibCrypto.err_error_string(code, nil)) unless code == 0\n      {code, message || \"Unknown or no error\"}\n    end\n\n    protected def get_reason(code)\n      {% if LibCrypto.has_constant?(:ERR_REASON_MASK) %}\n        if (code & LibCrypto::ERR_SYSTEM_FLAG) != 0\n          (code & LibCrypto::ERR_SYSTEM_MASK).to_i\n        else\n          (code & LibCrypto::ERR_REASON_MASK).to_i\n        end\n      {% else %}\n        (code & 0xFFF).to_i\n      {% end %}\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/hmac.cr",
    "content": "require \"./lib_crypto\"\nrequire \"openssl/algorithm\"\n\n# Allows computing Hash-based Message Authentication Code (HMAC).\n#\n# It is a type of message authentication code (MAC)\n# involving a hash function in combination with a key.\n#\n# HMAC can be used to verify the integrity of a message as well as the authenticity.\n#\n# See also [RFC2104](https://tools.ietf.org/html/rfc2104.html).\nclass OpenSSL::HMAC\n  # Returns the HMAC digest of *data* using the secret *key*.\n  #\n  # It may contain non-ASCII bytes, including NUL bytes.\n  #\n  # *algorithm* specifies which `OpenSSL::Algorithm` is to be used.\n  def self.digest(algorithm : OpenSSL::Algorithm, key, data) : Bytes\n    evp = algorithm.to_evp\n    key_slice = key.to_slice\n    data_slice = data.to_slice\n    buffer = Bytes.new(128)\n    LibCrypto.hmac(evp, key_slice, key_slice.size, data_slice, data_slice.size, buffer, out buffer_len)\n    buffer[0, buffer_len.to_i]\n  end\n\n  # Returns the HMAC digest of *data* using the secret *key*,\n  # formatted as a hexadecimal string. This is necessary to safely transfer\n  # the digest where binary messages are not allowed.\n  #\n  # See also `#digest`.\n  def self.hexdigest(algorithm : OpenSSL::Algorithm, key, data) : String\n    digest(algorithm, key, data).hexstring\n  end\nend\n"
  },
  {
    "path": "src/openssl/ktls.cr",
    "content": "# Ported from OpenSSL include/internal/ktls.h private header to enable KTLS\n# through a custom BIO so the actual TCP communication goes through the normal\n# Crystal::EventLoop regardless of the socket's blocking mode.\n#\n# Copyright 2018-2024 The OpenSSL Project Authors. All Rights Reserved.\n#\n# Licensed under the Apache License 2.0 (the \"License\").  You may not use\n# this file except in compliance with the License.  You can obtain a copy\n# in the file LICENSE in the source distribution or at\n# https://www.openssl.org/source/license.html\n\n# libressl doesn't support Kernel TLS\n{% skip_file if compare_versions(LibSSL::LIBRESSL_VERSION, \"0.0.0\") > 0 %}\n\n# libevent evloop doesn't implement recvmsg/sendmsg\n{% skip_file if Crystal::EventLoop.has_constant?(:LibEvent) %}\n\n{% if flag?(:linux) %}\n  require \"c/linux/tls\"\n{% elsif flag?(:freebsd) %}\n  require \"c/sys/ktls\"\n{% else %}\n  {% skip_file %}\n{% end %}\n\nrequire \"c/netinet/tcp\"\nrequire \"c/sys/socket\"\n\nmodule OpenSSL\n  # :nodoc:\n  module KTLS\n    private CMSG_LEVEL =\n      {% if flag?(:linux) %}\n        LibC::SOL_TLS\n      {% elsif flag?(:freebsd) %}\n        LibC::IPPROTO_TCP\n      {% end %}\n\n    # When successful, this socket option doesn't change the behaviour of the\n    # TCP socket, except changing the TCP setsockopt handler to enable the\n    # processing of SOL_TLS socket options. All other functionality remains the\n    # same.\n    #\n    # Socket must be in TCP established state to enable KTLS. Further calls to\n    # enable ktls will return EEXIST\n    def self.enable(socket : Socket) : Bool\n      {% if flag?(:linux) %}\n        LibC.setsockopt(socket.fd, LibC::SOL_TCP, LibC::TCP_ULP, \"tls\", \"tls\".bytesize) == 0\n      {% elsif flag?(:freebsd) %}\n        true\n      {% end %}\n    end\n\n    def self.enable_tx_zerocopy_sendfile(socket : Socket) : Bool\n      {% if flag?(:linux) %}\n        enable = LibC::Int.new(1)\n        LibC.setsockopt(socket.fd, LibC::SOL_TLS, LibC::TLS_TX_ZEROCOPY_RO, pointerof(enable), sizeof(LibC::Int)) == 0\n      {% elsif flag?(:freebsd) %}\n        true\n      {% end %}\n    end\n\n    # The TLS_TX socket option changes the send/sendmsg handlers of the TCP\n    # socket. If successful, then data sent using this socket will be encrypted\n    # and encapsulated in TLS records using the crypto_info provided here.\n    #\n    # The TLS_RX socket option changes the recv/recvmsg handlers of the TCP\n    # socket. If successful, then data received using this socket will be\n    # decrypted, authenticated and decapsulated using the crypto_info provided\n    # here.\n    def self.start(socket : Socket, crypto_info : Pointer, is_tx : Bool) : Bool\n      {% if flag?(:linux) %}\n        if len = crypto_info_len(crypto_info)\n          optname = is_tx ? LibC::TLS_TX : LibC::TLS_RX\n          LibC.setsockopt(socket.fd, LibC::SOL_TLS, optname, crypto_info, len) == 0\n        else\n          return false\n        end\n      {% elsif flag?(:freebsd) %}\n        optname = is_tx ? LibC::TCP_TXTLS_ENABLE : LibC::TCP_RXTLS_ENABLE\n        LibC.setsockopt(socket.fd, LibC::IPPROTO_TCP, optname, crypto_info, sizeof(LibC::Tls_enable)) == 0\n      {% end %}\n    end\n\n    {% if flag?(:linux) %}\n      # OpenSSL uses an internal struct { union { cipher... }, len } and of\n      # course the union size depends on how and where openssl has been built,\n      # for example against which linux kernel headers.\n      #\n      # Hopefully 'struct crypto_info' puts the cipher version/type first, so\n      # we can at least handle an allow list for currently supported ciphers.\n      private def self.crypto_info_len(crypto_info)\n        case type = crypto_info.as(LibC::Tls_crypto_info*).value.cipher_type\n        when LibC::TLS_CIPHER_AES_GCM_128\n          sizeof(LibC::Tls12_crypto_info_aes_gcm_128)\n        when LibC::TLS_CIPHER_AES_GCM_256\n          sizeof(LibC::Tls12_crypto_info_aes_gcm_256)\n        when LibC::TLS_CIPHER_AES_CCM_128\n          sizeof(LibC::Tls12_crypto_info_aes_ccm_128)\n        when LibC::TLS_CIPHER_CHACHA20_POLY1305\n          sizeof(LibC::Tls12_crypto_info_chacha20_poly1305)\n        when LibC::TLS_CIPHER_SM4_GCM\n          sizeof(LibC::Tls12_crypto_info_sm4_gcm)\n        when LibC::TLS_CIPHER_SM4_CCM\n          sizeof(LibC::Tls12_crypto_info_sm4_ccm)\n        when LibC::TLS_CIPHER_ARIA_GCM_128\n          sizeof(LibC::Tls12_crypto_info_aria_gcm_128)\n        when LibC::TLS_CIPHER_ARIA_GCM_256\n          sizeof(LibC::Tls12_crypto_info_aria_gcm_256)\n        else\n          # unknown TLS cipher (Linux 6.17, OpenSSL 3.6), check linux/tls.h and\n          # 'struct tls_crypto_info_all' in openssl/include/internal/ktls.h\n          STDERR.print \"WARNING: unknown TLS cipher (skipping Kernel TLS)\\n\"\n          nil\n        end\n      end\n    {% end %}\n\n    # Send a TLS record using the crypto_info provided in ktls_start and use\n    # record_type instead of the default SSL3_RT_APPLICATION_DATA.\n    # When the socket is non-blocking, then this call either returns EAGAIN or\n    # the entire record is pushed to TCP. It is impossible to send a partial\n    # record using this control message.\n    def self.send_ctrl_message(socket : Socket, record_type : UInt8, data : UInt8*, length : Int32) : Int32 | Errno\n      buf = uninitialized UInt8[24] # CMSG_SPACE(sizeof(record_type))\n\n      cmsg = buf.to_unsafe.as(LibC::Cmsghdr*)\n      cmsg.value.cmsg_level = CMSG_LEVEL\n      cmsg.value.cmsg_type = LibC::TLS_SET_RECORD_TYPE\n      cmsg.value.cmsg_len = sizeof(LibC::Cmsghdr) + sizeof(UInt8) # CMSG_LEN(sizeof(record_type))\n      cmsg.value.cmsg_data.to_unsafe.value = record_type\n\n      msg_iov = LibC::Iovec.new\n      msg_iov.iov_base = data.as(Void*)\n      msg_iov.iov_len = length\n\n      msg = LibC::Msghdr.new\n      msg.msg_control = buf.to_unsafe.as(Void*)\n      msg.msg_controllen = cmsg.value.cmsg_len\n      msg.msg_iov = pointerof(msg_iov)\n      msg.msg_iovlen = 1\n\n      Crystal::EventLoop.current.sendmsg(socket, pointerof(msg), 0)\n    end\n\n    # Receive a TLS record using the crypto_info provided in ktls_start.\n    # The kernel strips the TLS record header, IV and authentication tag,\n    # returning only the plaintext data or an error on failure.\n    # We add the TLS record header here to satisfy routines in rec_layer_s3.c\n    def self.read_record(socket : Socket, data : UInt8*, length : Int32) : Int32 | Errno\n      buf = uninitialized UInt8[24] # CMSG_SPACE(sizeof(record_type))\n      p = data.as(UInt8*)\n      prepend_length = LibCrypto::SSL3_RT_HEADER_LENGTH\n\n      if data_too_small?(length, prepend_length)\n        return Errno::EINVAL\n      end\n\n      cdata_len =\n        {% if flag?(:linux) %}\n          sizeof(LibC::Char)\n        {% elsif flag?(:freebsd) %}\n          sizeof(LibC::Tls_get_record)\n        {% end %}\n\n      cmsg = buf.to_unsafe.as(LibC::Cmsghdr*)\n      cmsg.value.cmsg_level = CMSG_LEVEL\n      cmsg.value.cmsg_len = sizeof(LibC::Cmsghdr) + cdata_len # CMSGLEN(cdata_len)\n\n      msg_iov = LibC::Iovec.new\n      msg_iov.iov_base = (p + LibCrypto::SSL3_RT_HEADER_LENGTH).as(Void*)\n      msg_iov.iov_len = iov_len(length - prepend_length)\n\n      msg = LibC::Msghdr.new\n      msg.msg_control = buf.to_unsafe.as(Void*)\n      msg.msg_controllen = cmsg.value.cmsg_len\n      msg.msg_iov = pointerof(msg_iov)\n      msg.msg_iovlen = 1\n\n      ret = Crystal::EventLoop.current.recvmsg(socket, pointerof(msg), 0)\n      return ret if ret.is_a?(Errno)\n\n      {% if flag?(:linux) %}\n        if msg.msg_controllen > 0\n          cmsg = msg.msg_control.as(LibC::Cmsghdr*)\n\n          if cmsg.value.cmsg_type == LibC::TLS_GET_RECORD_TYPE\n            p[0] = cmsg.value.cmsg_data.to_unsafe.value # tls record type\n            p[1] = LibCrypto::TLS1_2_VERSION_MAJOR.to_u8!\n            p[2] = LibCrypto::TLS1_2_VERSION_MINOR.to_u8!\n            # returned length is limited to msg_iov.iov_len above\n            p[3] = (ret >> 8).to_u8!\n            p[4] = ret.to_u8!\n            ret += prepend_length\n          end\n        end\n      {% elsif flag?(:freebsd) %}\n        if (msg.msg_flags & (LibC::MSG_EOR | LibC::MSG_CTRUNC)) != LibC::MSG_EOR\n          return Errno::EMSGSIZE\n        end\n\n        if msg.msg_controllen == 0\n          return Errno::EBADMSG\n        end\n\n        cmsg = msg.msg_control.as(LibC::Cmsghdr*)\n\n        if cmsg.value.cmsg_level != LibC::IPPROTO_TCP ||\n           cmsg.value.cmsg_type != LibC::TLS_GET_RECORD ||\n           cmsg.value.cmsg_len != sizeof(LibC::Cmsghdr) + cdata_len # CMSGLEN(cdata_len)\n          return Errno::EBADMSG\n        end\n\n        tgr = cmsg.value.cmsg_data.to_unsafe.as(LibC::Tls_get_record*)\n        p[0] = tgr.value.tls_type\n        p[1] = tgr.value.tls_vmajor\n        p[2] = tgr.value.tls_vminor\n        p[3] = (ret >> 8).to_u8!\n        p[4] = ret.to_u8!\n\n        ret += prepend_length\n      {% end %}\n\n      ret\n    end\n\n    private def self.data_too_small?(length, prepend_length)\n      {% if flag?(:linux) %}\n        length < prepend_length + LibCrypto::EVP_GCM_TLS_TAG_LEN\n      {% elsif flag?(:freebsd) %}\n        length <= prepend_length\n      {% end %}\n    end\n\n    private def self.iov_len(length)\n      {% if flag?(:linux) %}\n        length - LibCrypto::EVP_GCM_TLS_TAG_LEN\n      {% elsif flag?(:freebsd) %}\n        length\n      {% end %}\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/lib_crypto.cr",
    "content": "# Supported library versions:\n#\n# * openssl (1.1.0–3.3+)\n# * libressl (2.0–4.0+)\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#tls\n{% begin %}\n  lib LibCrypto\n    {% if flag?(:msvc) %}\n      {% from_libressl = false %}\n      {% ssl_version = nil %}\n      {% for dir in Crystal::LIBRARY_PATH.split(Crystal::System::Process::HOST_PATH_DELIMITER) %}\n        {% unless ssl_version %}\n          {% config_path = \"#{dir.id}\\\\openssl_VERSION\" %}\n          {% if config_version = read_file?(config_path) %}\n            {% ssl_version = config_version.chomp %}\n          {% end %}\n        {% end %}\n      {% end %}\n      {% ssl_version ||= \"0.0.0\" %}\n    {% else %}\n      # these have to be wrapped in `sh -c` since for MinGW-w64 the compiler\n      # passes the command string to `LibC.CreateProcessW`\n      {% from_libressl = (`sh -c 'hash pkg-config 2> /dev/null || printf %s false'` != \"false\") &&\n                         (`sh -c 'test -f $(pkg-config --silence-errors --variable=includedir libcrypto)/openssl/opensslv.h || printf %s false'` != \"false\") &&\n                         (`sh -c 'printf \"#include <openssl/opensslv.h>\\nLIBRESSL_VERSION_NUMBER\" | ${CC:-cc} $(pkg-config --cflags --silence-errors libcrypto || true) -E -'`.chomp.split('\\n').last != \"LIBRESSL_VERSION_NUMBER\") %}\n      {% ssl_version = `sh -c 'hash pkg-config 2> /dev/null && pkg-config --silence-errors --modversion libcrypto || printf %s 0.0.0'`.split.last.gsub(/[^0-9.]/, \"\") %}\n    {% end %}\n\n    {% if from_libressl %}\n      LIBRESSL_VERSION = {{ ssl_version }}\n      OPENSSL_VERSION = \"0.0.0\"\n    {% else %}\n      LIBRESSL_VERSION = \"0.0.0\"\n      OPENSSL_VERSION = {{ ssl_version }}\n    {% end %}\n  end\n{% end %}\n\n{% if flag?(:win32) %}\n  @[Link(\"crypto\")]\n  @[Link(\"crypt32\")] # CertOpenStore, ...\n  @[Link(\"user32\")]  # GetProcessWindowStation, GetUserObjectInformationW, _MessageBoxW\n{% else %}\n  @[Link(ldflags: \"`command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libcrypto || printf %s '-lcrypto'`\")]\n{% end %}\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  # TODO: if someone brings their own OpenSSL 1.x.y on Windows, will this have a different name?\n  @[Link(dll: \"libcrypto-3-x64.dll\")]\n{% end %}\nlib LibCrypto\n  alias Char = LibC::Char\n  alias Int = LibC::Int\n  alias UInt = LibC::UInt\n  alias Long = LibC::Long\n  alias ULong = LibC::ULong\n  alias SizeT = LibC::SizeT\n\n  type X509 = Void*\n  type X509_EXTENSION = Void*\n  type X509_NAME = Void*\n  type X509_NAME_ENTRY = Void*\n  type X509_STORE = Void*\n  type X509_STORE_CTX = Void*\n\n  BIO_TYPE_DESCRIPTOR  = 0x0100\n  BIO_TYPE_SOURCE_SINK = 0x0400\n\n  BIO_FLAGS_KTLS_TX_CTRL_MSG          = 0x1000\n  BIO_FLAGS_KTLS_RX                   = 0x2000\n  BIO_FLAGS_KTLS_TX                   = 0x4000\n  BIO_FLAGS_KTLS_TX_ZEROCOPY_SENDFILE = 0x8000\n\n  struct Bio\n    method : Void*\n    callback : BIO_callback_fn\n    {% if compare_versions(LIBRESSL_VERSION, \"3.5.0\") >= 0 %}\n      callback_ex : BIO_callback_fn_ex\n    {% end %}\n    cb_arg : Char*\n    init : Int\n    shutdown : Int\n    flags : Int\n    retry_reason : Int\n    num : Int\n    ptr : Void*\n    next_bio : Void*\n    prev_bio : Void*\n    references : Int\n    num_read : ULong\n    num_write : ULong\n  end\n\n  alias BIO_callback_fn = (Bio*, Int, Char*, Int, Long, Long) -> Long\n  alias BIO_callback_fn_ex = (Bio*, Int, Char, SizeT, Int, Long, Int, SizeT*) -> Long\n\n  PKCS5_SALT_LEN      =  8\n  EVP_MAX_KEY_LENGTH  = 32\n  EVP_MAX_IV_LENGTH   = 16\n  EVP_GCM_TLS_TAG_LEN = 16\n\n  SSL3_RT_HEADER_LENGTH = 5\n\n  TLS1_2_VERSION_MAJOR = 0x03\n  TLS1_2_VERSION_MINOR = 0x03\n\n  CTRL_EOF                           =   2\n  CTRL_PUSH                          =   6\n  CTRL_POP                           =   7\n  CTRL_FLUSH                         =  11\n  CTRL_SET_KTLS                      =  72\n  CTRL_GET_KTLS_SEND                 =  73\n  CTRL_SET_KTLS_TX_SEND_CTRL_MSG     =  74\n  CTRL_CLEAR_KTLS_TX_CTRL_MSG        =  75\n  CTRL_GET_KTLS_RECV                 =  76\n  CTRL_SET_KTLS_TX_ZEROCOPY_SENDFILE =  90\n  BIO_C_GET_FD                       = 105\n\n  type BioMethod = Void\n\n  fun BIO_new(BioMethod*) : Bio*\n  fun BIO_free(Bio*) : Int\n\n  fun BIO_set_data(Bio*, Void*)\n  fun BIO_get_data(Bio*) : Void*\n  fun BIO_set_init(Bio*, Int)\n  fun BIO_set_shutdown(Bio*, Int)\n\n  fun BIO_set_flags(Bio*, Int)\n  fun BIO_test_flags(Bio*, Int) : Int\n  fun BIO_clear_flags(Bio*, Int)\n\n  fun BIO_ctrl(Bio*, Int, Long, Void*) : Long\n\n  fun BIO_get_new_index : Int\n  fun BIO_meth_new(Int, Char*) : BioMethod*\n  fun BIO_meth_set_read(BioMethod*, (Bio*, Char*, Int) -> Int)\n  fun BIO_meth_set_write(BioMethod*, (Bio*, Char*, Int) -> Int)\n\n  {% unless compare_versions(LIBRESSL_VERSION, \"0.0.0\") > 0 %}\n    # LibreSSL doesn't support the _ex functions\n    fun BIO_meth_set_read_ex(BioMethod*, (Bio*, Char*, SizeT, SizeT*) -> Int)\n    fun BIO_meth_set_write_ex(BioMethod*, (Bio*, Char*, SizeT, SizeT*) -> Int)\n  {% end %}\n\n  fun BIO_meth_set_puts(BioMethod*, (Bio*, Char*) -> Int)\n  fun BIO_meth_set_gets(BioMethod*, (Bio*, Char*, Int) -> Int)\n  fun BIO_meth_set_ctrl(BioMethod*, (Bio*, Int, Long, Void*) -> Long)\n  fun BIO_meth_set_create(BioMethod*, (Bio*) -> Int)\n  fun BIO_meth_set_destroy(BioMethod*, (Bio*) -> Int)\n  fun BIO_meth_set_callback_ctrl(BioMethod*, (Bio*, Int, Void*) -> Long)\n\n  fun sha1 = SHA1(data : Char*, length : SizeT, md : Char*) : Char*\n\n  type EVP_MD = Void*\n\n  fun evp_md4 = EVP_md4 : EVP_MD\n  fun evp_md5 = EVP_md5 : EVP_MD\n  fun evp_ripemd160 = EVP_ripemd160 : EVP_MD\n  fun evp_sha1 = EVP_sha1 : EVP_MD\n  fun evp_sha224 = EVP_sha224 : EVP_MD\n  fun evp_sha256 = EVP_sha256 : EVP_MD\n  fun evp_sha384 = EVP_sha384 : EVP_MD\n  fun evp_sha512 = EVP_sha512 : EVP_MD\n\n  alias EVP_CIPHER = Void*\n  alias EVP_CIPHER_CTX = Void*\n\n  alias ASN1_OBJECT = Void*\n  alias ASN1_STRING = Char*\n\n  fun obj_txt2obj = OBJ_txt2obj(s : Char*, no_name : Int) : ASN1_OBJECT\n  fun obj_nid2sn = OBJ_nid2sn(n : Int) : Char*\n  fun obj_obj2nid = OBJ_obj2nid(obj : ASN1_OBJECT) : Int\n  fun obj_ln2nid = OBJ_ln2nid(ln : Char*) : Int\n  fun obj_sn2nid = OBJ_sn2nid(sn : Char*) : Int\n  fun obj_find_sigid_algs = OBJ_find_sigid_algs(sigid : Int32, pdig_nid : Int32*, ppkey_nid : Int32*) : Int32\n\n  fun asn1_object_free = ASN1_OBJECT_free(obj : ASN1_OBJECT)\n  fun asn1_string_data = ASN1_STRING_data(x : ASN1_STRING) : Char*\n  fun asn1_string_length = ASN1_STRING_length(x : ASN1_STRING) : Int\n  fun asn1_string_print = ASN1_STRING_print(out : Bio*, v : ASN1_STRING) : Int\n  fun i2t_asn1_object = i2t_ASN1_OBJECT(buf : Char*, buf_len : Int, a : ASN1_OBJECT) : Int\n\n  struct EVP_MD_CTX_Struct\n    digest : EVP_MD\n    engine : Void*\n    flags : UInt32\n    pctx : Void*\n    update_fun : Void*\n  end\n\n  alias EVP_MD_CTX = EVP_MD_CTX_Struct*\n\n  struct HMAC_CTX_Struct\n    md : EVP_MD\n    md_ctx : EVP_MD_CTX_Struct\n    i_ctx : EVP_MD_CTX_Struct\n    o_ctx : EVP_MD_CTX_Struct\n    key_length : UInt32\n    key : UInt8[128]\n  end\n\n  alias HMAC_CTX = HMAC_CTX_Struct*\n\n  fun hmac_ctx_init = HMAC_CTX_init(ctx : HMAC_CTX)\n  fun hmac_ctx_cleanup = HMAC_CTX_cleanup(ctx : HMAC_CTX)\n  fun hmac_init_ex = HMAC_Init_ex(ctx : HMAC_CTX, key : Void*, len : Int32, md : EVP_MD, engine : Void*) : Int32\n  fun hmac_update = HMAC_Update(ctx : HMAC_CTX, data : UInt8*, len : LibC::SizeT) : Int32\n  fun hmac_final = HMAC_Final(ctx : HMAC_CTX, md : UInt8*, len : UInt32*) : Int32\n  fun hmac_ctx_copy = HMAC_CTX_copy(dst : HMAC_CTX, src : HMAC_CTX) : Int32\n\n  fun x509_digest = X509_digest(x509 : X509, evp_md : EVP_MD, hash : UInt8*, len : Int32*) : Int32\n  fun evp_get_digestbyname = EVP_get_digestbyname(name : UInt8*) : EVP_MD\n  fun evp_digestinit_ex = EVP_DigestInit_ex(ctx : EVP_MD_CTX, type : EVP_MD, engine : Void*) : Int32\n  fun evp_digestupdate = EVP_DigestUpdate(ctx : EVP_MD_CTX, data : UInt8*, count : LibC::SizeT) : Int32\n  fun evp_md_ctx_copy = EVP_MD_CTX_copy(dst : EVP_MD_CTX, src : EVP_MD_CTX) : Int32\n  fun evp_md_ctx_md = EVP_MD_CTX_md(ctx : EVP_MD_CTX) : EVP_MD\n\n  {% if compare_versions(OPENSSL_VERSION, \"3.0.0\") >= 0 %}\n    fun evp_md_size = EVP_MD_get_size(md : EVP_MD) : Int32\n    fun evp_md_block_size = EVP_MD_get_block_size(md : EVP_MD) : LibC::Int\n  {% else %}\n    fun evp_md_size = EVP_MD_size(md : EVP_MD) : Int32\n    fun evp_md_block_size = EVP_MD_block_size(md : EVP_MD) : LibC::Int\n  {% end %}\n\n  fun evp_digestfinal_ex = EVP_DigestFinal_ex(ctx : EVP_MD_CTX, md : UInt8*, size : UInt32*) : Int32\n\n  fun evp_md_ctx_new = EVP_MD_CTX_new : EVP_MD_CTX\n  fun evp_md_ctx_free = EVP_MD_CTX_free(ctx : EVP_MD_CTX)\n\n  fun evp_get_cipherbyname = EVP_get_cipherbyname(name : UInt8*) : EVP_CIPHER\n\n  {% if compare_versions(OPENSSL_VERSION, \"3.0.0\") >= 0 %}\n    fun evp_cipher_name = EVP_CIPHER_get_name(cipher : EVP_CIPHER) : UInt8*\n    fun evp_cipher_nid = EVP_CIPHER_get_nid(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_block_size = EVP_CIPHER_get_block_size(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_key_length = EVP_CIPHER_get_key_length(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_iv_length = EVP_CIPHER_get_iv_length(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_flags = EVP_CIPHER_get_flags(cipher : EVP_CIPHER) : CipherFlags\n  {% else %}\n    fun evp_cipher_name = EVP_CIPHER_name(cipher : EVP_CIPHER) : UInt8*\n    fun evp_cipher_nid = EVP_CIPHER_nid(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_block_size = EVP_CIPHER_block_size(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_key_length = EVP_CIPHER_key_length(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_iv_length = EVP_CIPHER_iv_length(cipher : EVP_CIPHER) : Int32\n    fun evp_cipher_flags = EVP_CIPHER_flags(cipher : EVP_CIPHER) : CipherFlags\n  {% end %}\n\n  fun evp_cipher_ctx_new = EVP_CIPHER_CTX_new : EVP_CIPHER_CTX\n  fun evp_cipher_ctx_free = EVP_CIPHER_CTX_free(ctx : EVP_CIPHER_CTX)\n  fun evp_cipherinit_ex = EVP_CipherInit_ex(ctx : EVP_CIPHER_CTX, type : EVP_CIPHER, engine : Void*, key : UInt8*, iv : UInt8*, enc : Int32) : Int32\n  fun evp_cipherupdate = EVP_CipherUpdate(ctx : EVP_CIPHER_CTX, out : UInt8*, outl : Int32*, in : UInt8*, inl : Int32) : Int32\n  fun evp_cipherfinal_ex = EVP_CipherFinal_ex(ctx : EVP_CIPHER_CTX, out : UInt8*, outl : Int32*) : Int32\n  fun evp_cipher_ctx_set_padding = EVP_CIPHER_CTX_set_padding(ctx : EVP_CIPHER_CTX, padding : Int32) : Int32\n  fun evp_cipher_ctx_cipher = EVP_CIPHER_CTX_cipher(ctx : EVP_CIPHER_CTX) : EVP_CIPHER\n\n  @[Flags]\n  enum CipherFlags : ULong\n    EVP_CIPH_FLAG_DEFAULT_ASN1      =   0x1000\n    EVP_CIPH_FLAG_LENGTH_BITS       =   0x2000\n    EVP_CIPH_FLAG_FIPS              =   0x4000\n    EVP_CIPH_FLAG_NON_FIPS_ALLOW    =   0x8000\n    EVP_CIPH_FLAG_CUSTOM_CIPHER     = 0x100000\n    EVP_CIPH_FLAG_AEAD_CIPHER       = 0x200000\n    EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK = 0x400000\n    EVP_CIPH_FLAG_PIPELINE          = 0x800000\n  end\n\n  fun hmac = HMAC(evp : EVP_MD, key : Char*, key_len : Int,\n                  d : Char*, n : SizeT, md : Char*, md_len : UInt*) : Char*\n\n  fun rand_bytes = RAND_bytes(buf : Char*, num : Int) : Int\n  fun err_get_error = ERR_get_error : ULong\n  fun err_error_string = ERR_error_string(e : ULong, buf : Char*) : Char*\n\n  {% if compare_versions(OPENSSL_VERSION, \"3.0.0\") >= 0 %}\n    ERR_SYSTEM_FLAG = Int32::MAX.to_u32 + 1\n    ERR_SYSTEM_MASK = Int32::MAX.to_u32\n    ERR_REASON_MASK = 0x7FFFFF\n  {% end %}\n\n  struct MD5Context\n    a : UInt\n    b : UInt\n    c : UInt\n    d : UInt\n    nl : UInt\n    nh : UInt\n    data : UInt[16]\n    num : UInt\n  end\n\n  fun md5_init = MD5_Init(c : MD5Context*) : Int\n  fun md5_update = MD5_Update(c : MD5Context*, data : Void*, len : LibC::SizeT) : Int\n  fun md5_final = MD5_Final(md : UInt8*, c : MD5Context*) : Int\n  fun md5_transform = MD5_Transform(c : MD5Context*, b : UInt8*)\n  fun md5 = MD5(data : UInt8*, length : LibC::SizeT, md : UInt8*) : UInt8*\n\n  fun pkcs5_pbkdf2_hmac_sha1 = PKCS5_PBKDF2_HMAC_SHA1(pass : LibC::Char*, passlen : LibC::Int, salt : UInt8*, saltlen : LibC::Int, iter : LibC::Int, keylen : LibC::Int, out : UInt8*) : LibC::Int\n  fun pkcs5_pbkdf2_hmac = PKCS5_PBKDF2_HMAC(pass : LibC::Char*, passlen : LibC::Int, salt : UInt8*, saltlen : LibC::Int, iter : LibC::Int, digest : EVP_MD, keylen : LibC::Int, out : UInt8*) : LibC::Int\n\n  NID_X9_62_prime256v1 = 415\n\n  alias EC_KEY = Void*\n  fun ec_key_new_by_curve_name = EC_KEY_new_by_curve_name(nid : Int) : EC_KEY\n  fun ec_key_free = EC_KEY_free(key : EC_KEY)\n\n  struct GENERAL_NAME\n    type : LibC::Int\n    value : ASN1_STRING\n  end\n\n  # GEN_URI  = 6\n  GEN_DNS   = 2\n  GEN_IPADD = 7\n\n  NID_undef            =  0\n  NID_commonName       = 13\n  NID_subject_alt_name = 85\n\n  {% if OPENSSL_VERSION != \"0.0.0\" %}\n    fun sk_free = OPENSSL_sk_free(st : Void*)\n    fun sk_num = OPENSSL_sk_num(x0 : Void*) : Int\n    fun sk_pop_free = OPENSSL_sk_pop_free(st : Void*, callback : (Void*) ->)\n    fun sk_value = OPENSSL_sk_value(x0 : Void*, x1 : Int) : Void*\n  {% else %}\n    fun sk_free(st : Void*)\n    fun sk_num(x0 : Void*) : Int\n    fun sk_pop_free(st : Void*, callback : (Void*) ->)\n    fun sk_value(x0 : Void*, x1 : Int) : Void*\n  {% end %}\n\n  fun d2i_X509(a : X509*, ppin : UInt8**, length : Long) : X509\n  fun x509_dup = X509_dup(a : X509) : X509\n  fun x509_free = X509_free(a : X509)\n  fun x509_get_subject_name = X509_get_subject_name(a : X509) : X509_NAME\n  fun x509_new = X509_new : X509\n  fun x509_set_subject_name = X509_set_subject_name(x : X509, name : X509_NAME) : Int\n  fun x509_store_ctx_get_current_cert = X509_STORE_CTX_get_current_cert(x : X509_STORE_CTX) : X509\n  fun x509_verify_cert = X509_verify_cert(x : X509_STORE_CTX) : Int\n  fun x509_add_ext = X509_add_ext(x : X509, ex : X509_EXTENSION, loc : Int) : X509_EXTENSION\n  fun x509_get_ext = X509_get_ext(x : X509, idx : Int) : X509_EXTENSION\n  fun x509_get_ext_count = X509_get_ext_count(x : X509) : Int\n  fun x509_get_ext_d2i = X509_get_ext_d2i(x : X509, nid : Int, crit : Int*, idx : Int*) : Void*\n  fun x509_get_signature_nid = X509_get_signature_nid(x509 : X509) : Int32\n\n  MBSTRING_UTF8 = 0x1000\n\n  fun x509_name_add_entry_by_txt = X509_NAME_add_entry_by_txt(name : X509_NAME, field : Char*, type : Int, bytes : Char*, len : Int, loc : Int, set : Int) : X509_NAME\n  fun x509_name_dup = X509_NAME_dup(a : X509_NAME) : X509_NAME\n  fun x509_name_entry_count = X509_NAME_entry_count(name : X509_NAME) : Int\n  fun x509_name_free = X509_NAME_free(a : X509_NAME)\n  fun x509_name_get_entry = X509_NAME_get_entry(name : X509_NAME, loc : Int) : X509_NAME_ENTRY\n  fun x509_name_get_index_by_nid = X509_NAME_get_index_by_NID(name : X509_NAME, nid : Int, lastpos : Int) : Int\n  fun x509_name_new = X509_NAME_new : X509_NAME\n\n  fun x509_name_entry_get_data = X509_NAME_ENTRY_get_data(ne : X509_NAME_ENTRY) : ASN1_STRING\n  fun x509_name_entry_get_object = X509_NAME_ENTRY_get_object(ne : X509_NAME_ENTRY) : ASN1_OBJECT\n\n  fun x509_extension_dup = X509_EXTENSION_dup(a : X509_EXTENSION) : X509_EXTENSION\n  fun x509_extension_free = X509_EXTENSION_free(a : X509_EXTENSION)\n  fun x509_extension_get_object = X509_EXTENSION_get_object(a : X509_EXTENSION) : ASN1_OBJECT\n  fun x509_extension_get_data = X509_EXTENSION_get_data(a : X509_EXTENSION) : ASN1_STRING\n  fun x509_extension_create_by_nid = X509_EXTENSION_create_by_NID(ex : X509_EXTENSION, nid : Int, crit : Int, data : ASN1_STRING) : X509_EXTENSION\n  fun x509v3_ext_nconf_nid = X509V3_EXT_nconf_nid(conf : Void*, ctx : Void*, ext_nid : Int, value : Char*) : X509_EXTENSION\n  fun x509v3_ext_print = X509V3_EXT_print(out : Bio*, ext : X509_EXTENSION, flag : Int, indent : Int) : Int\n\n  fun x509_store_add_cert = X509_STORE_add_cert(ctx : X509_STORE, x : X509) : Int\n\n  type X509VerifyParam = Void*\n\n  @[Flags]\n  enum X509VerifyFlags : ULong\n    CB_ISSUER_CHECK      =      0x1\n    USE_CHECK_TIME       =      0x2\n    CRL_CHECK            =      0x4\n    CRL_CHECK_ALL        =      0x8\n    IGNORE_CRITICAL      =     0x10\n    X509_STRICT          =     0x20\n    ALLOW_PROXY_CERTS    =     0x40\n    POLICY_CHECK         =     0x80\n    EXPLICIT_POLICY      =    0x100\n    INHIBIT_ANY          =    0x200\n    INHIBIT_MAP          =    0x400\n    NOTIFY_POLICY        =    0x800\n    EXTENDED_CRL_SUPPORT =   0x1000\n    USE_DELTAS           =   0x2000\n    CHECK_SS_SIGNATURE   =   0x4000\n    TRUSTED_FIRST        =   0x8000\n    SUITEB_128_LOS_ONLY  =  0x10000\n    SUITEB_192_LOS       =  0x20000\n    SUITEB_128_LOS       =  0x30000\n    PARTIAL_CHAIN        =  0x80000\n    NO_ALT_CHAINS        = 0x100000\n  end\n\n  fun x509_verify_param_lookup = X509_VERIFY_PARAM_lookup(name : UInt8*) : X509VerifyParam\n  fun x509_verify_param_set1_host = X509_VERIFY_PARAM_set1_host(param : X509VerifyParam, name : UInt8*, len : SizeT) : Int\n  fun x509_verify_param_set1_ip_asc = X509_VERIFY_PARAM_set1_ip_asc(param : X509VerifyParam, ip : UInt8*) : Int\n  fun x509_verify_param_set_flags = X509_VERIFY_PARAM_set_flags(param : X509VerifyParam, flags : X509VerifyFlags) : Int\nend\n"
  },
  {
    "path": "src/openssl/lib_ssl.cr",
    "content": "require \"./lib_crypto\"\n\n{% if flag?(:without_openssl) %}\n  {% raise \"The `without_openssl` flag is preventing you to use the LibSSL module\" %}\n{% end %}\n\n# Supported library versions:\n#\n# * openssl (1.1.0–3.3+)\n# * libressl (2.0–4.0+)\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#tls\n{% begin %}\n  lib LibSSL\n    {% if flag?(:msvc) %}\n      {% from_libressl = false %}\n      {% ssl_version = nil %}\n      {% for dir in Crystal::LIBRARY_PATH.split(Crystal::System::Process::HOST_PATH_DELIMITER) %}\n        {% unless ssl_version %}\n          {% config_path = \"#{dir.id}\\\\openssl_VERSION\" %}\n          {% if config_version = read_file?(config_path) %}\n            {% ssl_version = config_version.chomp %}\n          {% end %}\n        {% end %}\n      {% end %}\n      {% ssl_version ||= \"0.0.0\" %}\n    {% else %}\n      # these have to be wrapped in `sh -c` since for MinGW-w64 the compiler\n      # passes the command string to `LibC.CreateProcessW`\n      {% from_libressl = (`sh -c 'hash pkg-config 2> /dev/null || printf %s false'` != \"false\") &&\n                         (`sh -c 'test -f $(pkg-config --silence-errors --variable=includedir libssl)/openssl/opensslv.h || printf %s false'` != \"false\") &&\n                         (`sh -c 'printf \"#include <openssl/opensslv.h>\\nLIBRESSL_VERSION_NUMBER\" | ${CC:-cc} $(pkg-config --cflags --silence-errors libssl || true) -E -'`.chomp.split('\\n').last != \"LIBRESSL_VERSION_NUMBER\") %}\n      {% ssl_version = `sh -c 'hash pkg-config 2> /dev/null && pkg-config --silence-errors --modversion libssl || printf %s 0.0.0'`.split.last.gsub(/[^0-9.]/, \"\") %}\n    {% end %}\n\n    {% if from_libressl %}\n      LIBRESSL_VERSION = {{ ssl_version }}\n      OPENSSL_VERSION = \"0.0.0\"\n    {% else %}\n      LIBRESSL_VERSION = \"0.0.0\"\n      OPENSSL_VERSION = {{ ssl_version }}\n    {% end %}\n  end\n{% end %}\n\n{% if flag?(:win32) %}\n  @[Link(\"ssl\")]\n  @[Link(\"crypto\")]\n  @[Link(\"crypt32\")] # CertOpenStore, ...\n  @[Link(\"user32\")]  # GetProcessWindowStation, GetUserObjectInformationW, _MessageBoxW\n{% else %}\n  @[Link(ldflags: \"`command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libssl || printf %s '-lssl -lcrypto'`\")]\n{% end %}\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  # TODO: if someone brings their own OpenSSL 1.x.y on Windows, will this have a different name?\n  @[Link(dll: \"libssl-3-x64.dll\")]\n  @[Link(dll: \"libcrypto-3-x64.dll\")]\n{% end %}\nlib LibSSL\n  alias Int = LibC::Int\n  alias Char = LibC::Char\n  alias Long = LibC::Long\n  alias ULong = LibC::ULong\n\n  type SSLMethod = Void*\n  type SSLContext = Void*\n  type SSL = Void*\n  type SSLCipher = Void*\n\n  alias VerifyCallback = (Int, LibCrypto::X509_STORE_CTX) -> Int\n  alias CertVerifyCallback = (LibCrypto::X509_STORE_CTX, Void*) -> Int\n\n  enum SSLFileType\n    PEM  = 1\n    ASN1 = 2\n  end\n\n  enum SSLError : Int\n    NONE             = 0\n    SSL              = 1\n    WANT_READ        = 2\n    WANT_WRITE       = 3\n    WANT_X509_LOOKUP = 4\n    SYSCALL          = 5\n    ZERO_RETURN      = 6\n    WANT_CONNECT     = 7\n    WANT_ACCEPT      = 8\n  end\n\n  @[Flags]\n  enum VerifyMode : Int\n    NONE                 = 0\n    PEER                 = 1\n    FAIL_IF_NO_PEER_CERT = 2\n    CLIENT_ONCE          = 4\n  end\n\n  enum SSLCtrl : Int\n    SET_TLSEXT_HOSTNAME = 55\n  end\n\n  enum TLSExt : Long\n    NAMETYPE_host_name = 0\n  end\n\n  # SSL_CTRL_SET_TMP_RSA = 2\n  # SSL_CTRL_SET_TMP_DH = 3\n  SSL_CTRL_SET_TMP_ECDH =  4\n  SSL_CTRL_MODE         = 33\n  SSL_CTRL_CLEAR_MODE   = 78\n\n  # SNI callback control commands\n  # SSL_CTX_set_tlsext_servername_callback is a macro that calls SSL_CTX_callback_ctrl\n  SSL_CTRL_SET_TLSEXT_SERVERNAME_CB  = 53\n  SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54\n\n  enum Options : ULong\n    LEGACY_SERVER_CONNECT       = 0x00000004\n    ENABLE_KTLS                 = 0x00000008\n    SAFARI_ECDHE_ECDSA_BUG      = 0x00000040\n    DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800\n\n    # Various bug workarounds that should be rather harmless.\n    ALL = 0x80000BFF\n\n    NO_QUERY_MTU     = 0x00001000\n    COOKIE_EXCHANGE  = 0x00002000\n    NO_TICKET        = 0x00004000\n    CISCO_ANYCONNECT = 0x00008000\n\n    NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000\n    NO_COMPRESSION                         = 0x00020000\n    ALLOW_UNSAFE_LEGACY_RENEGOTIATION      = 0x00040000\n    CIPHER_SERVER_PREFERENCE               = 0x00400000\n    TLS_ROLLBACK_BUG                       = 0x00800000\n\n    NO_SSL_V3   = 0x02000000\n    NO_TLS_V1   = 0x04000000\n    NO_TLS_V1_3 = 0x20000000\n    NO_TLS_V1_2 = 0x08000000\n    NO_TLS_V1_1 = 0x10000000\n\n    {% if OPENSSL_VERSION != \"0.0.0\" %}\n      NO_RENEGOTIATION = 0x40000000\n    {% else %}\n      NO_RENEGOTIATION = 0x00000000\n    {% end %}\n\n    NETSCAPE_CA_DN_BUG              = 0x20000000\n    NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000\n    CRYPTOPRO_TLSEXT_BUG            = 0x80000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    MICROSOFT_SESS_ID_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    NETSCAPE_CHALLENGE_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    NETSCAPE_REUSE_CIPHER_CHANGE_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    SSLREF2_REUSE_CERT_TYPE_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    MICROSOFT_BIG_SSL_V3_BUFFER = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    SSLEAY_080_CLIENT_DH_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    TLS_D5_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    TLS_BLOCK_PADDING_BUG = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    NO_SSL_V2 = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    SINGLE_ECDH_USE = 0x00000000\n\n    @[Deprecated(\"Removed from LibSSL.\")]\n    SINGLE_DH_USE = 0x00000000\n  end\n\n  @[Flags]\n  enum Modes : ULong\n    ENABLE_PARTIAL_WRITE       = 0x00000001\n    ACCEPT_MOVING_WRITE_BUFFER = 0x00000002\n    AUTO_RETRY                 = 0x00000004\n    NO_AUTO_CHAIN              = 0x00000008\n    RELEASE_BUFFERS            = 0x00000010\n    SEND_CLIENTHELLO_TIME      = 0x00000020\n    SEND_SERVERHELLO_TIME      = 0x00000040\n    SEND_FALLBACK_SCSV         = 0x00000080\n  end\n\n  OPENSSL_NPN_UNSUPPORTED = 0\n  OPENSSL_NPN_NEGOTIATED  = 1\n  OPENSSL_NPN_NO_OVERLAP  = 2\n\n  SSL_TLSEXT_ERR_OK            = 0\n  SSL_TLSEXT_ERR_ALERT_WARNING = 1\n  SSL_TLSEXT_ERR_ALERT_FATAL   = 2\n  SSL_TLSEXT_ERR_NOACK         = 3\n\n  # TLS alert codes\n  SSL_AD_INTERNAL_ERROR = 80\n\n  fun tlsv1_method = TLSv1_method : SSLMethod\n  fun tlsv1_1_method = TLSv1_1_method : SSLMethod\n  fun tlsv1_2_method = TLSv1_2_method : SSLMethod\n\n  fun ssl_get_error = SSL_get_error(handle : SSL, ret : Int) : SSLError\n  fun ssl_get_servername = SSL_get_servername(ssl : SSL, host_type : TLSExt) : UInt8*\n  fun ssl_set_bio = SSL_set_bio(handle : SSL, rbio : LibCrypto::Bio*, wbio : LibCrypto::Bio*)\n  fun ssl_select_next_proto = SSL_select_next_proto(output : Char**, output_len : Char*, input : Char*, input_len : Int, client : Char*, client_len : Int) : Int\n  fun ssl_ctrl = SSL_ctrl(handle : SSL, cmd : Int, larg : Long, parg : Void*) : Long\n  fun ssl_free = SSL_free(handle : SSL)\n\n  fun ssl_get_current_cipher = SSL_get_current_cipher(ssl : SSL) : SSLCipher\n  fun ssl_cipher_get_name = SSL_CIPHER_get_name(cipher : SSLCipher) : UInt8*\n  fun ssl_get_version = SSL_get_version(ssl : SSL) : UInt8*\n\n  @[Raises]\n  fun ssl_new = SSL_new(context : SSLContext) : SSL\n\n  @[Raises]\n  fun ssl_connect = SSL_connect(handle : SSL) : Int\n\n  @[Raises]\n  fun ssl_accept = SSL_accept(handle : SSL) : Int\n\n  @[Raises]\n  fun ssl_write = SSL_write(handle : SSL, text : UInt8*, length : Int) : Int\n\n  @[Raises]\n  fun ssl_read = SSL_read(handle : SSL, buffer : UInt8*, read_size : Int) : Int\n\n  @[Raises]\n  fun ssl_shutdown = SSL_shutdown(handle : SSL) : Int\n\n  fun ssl_ctx_new = SSL_CTX_new(method : SSLMethod) : SSLContext\n  fun ssl_ctx_free = SSL_CTX_free(context : SSLContext)\n  fun ssl_ctx_set_cipher_list = SSL_CTX_set_cipher_list(ctx : SSLContext, ciphers : Char*) : Int\n  fun ssl_ctx_use_certificate_chain_file = SSL_CTX_use_certificate_chain_file(ctx : SSLContext, file : UInt8*) : Int\n  fun ssl_ctx_use_privatekey_file = SSL_CTX_use_PrivateKey_file(ctx : SSLContext, file : UInt8*, filetype : SSLFileType) : Int\n  fun ssl_ctx_get_verify_mode = SSL_CTX_get_verify_mode(ctx : SSLContext) : VerifyMode\n  fun ssl_ctx_set_verify = SSL_CTX_set_verify(ctx : SSLContext, mode : VerifyMode, callback : VerifyCallback)\n  fun ssl_ctx_set_default_verify_paths = SSL_CTX_set_default_verify_paths(ctx : SSLContext) : Int\n  fun ssl_ctx_get_cert_store = SSL_CTX_get_cert_store(ctx : SSLContext) : LibCrypto::X509_STORE\n  fun ssl_ctx_ctrl = SSL_CTX_ctrl(ctx : SSLContext, cmd : Int, larg : ULong, parg : Void*) : ULong\n  fun ssl_ctx_callback_ctrl = SSL_CTX_callback_ctrl(ctx : SSLContext, cmd : Int, fp : Proc(Void)) : Long\n  fun ssl_set_ssl_ctx = SSL_set_SSL_CTX(ssl : SSL, ctx : SSLContext) : SSLContext\n\n  {% if compare_versions(OPENSSL_VERSION, \"3.0.0\") >= 0 %}\n    fun ssl_get_peer_certificate = SSL_get1_peer_certificate(handle : SSL) : LibCrypto::X509\n  {% else %}\n    fun ssl_get_peer_certificate = SSL_get_peer_certificate(handle : SSL) : LibCrypto::X509\n  {% end %}\n\n  {% if LIBRESSL_VERSION != \"0.0.0\" %}\n    SSL_CTRL_OPTIONS       = 32\n    SSL_CTRL_CLEAR_OPTIONS = 77\n  {% else %}\n    fun ssl_ctx_get_options = SSL_CTX_get_options(ctx : SSLContext) : ULong\n    fun ssl_ctx_set_options = SSL_CTX_set_options(ctx : SSLContext, larg : ULong) : ULong\n    fun ssl_ctx_clear_options = SSL_CTX_clear_options(ctx : SSLContext, larg : ULong) : ULong\n    fun ssl_ctx_set_ciphersuites = SSL_CTX_set_ciphersuites(ctx : SSLContext, ciphers : Char*) : Int\n  {% end %}\n\n  @[Raises]\n  fun ssl_ctx_load_verify_locations = SSL_CTX_load_verify_locations(ctx : SSLContext, ca_file : UInt8*, ca_path : UInt8*) : Int\n\n  # Hostname validation for OpenSSL <= 1.0.1\n  fun ssl_ctx_set_cert_verify_callback = SSL_CTX_set_cert_verify_callback(ctx : SSLContext, callback : CertVerifyCallback, arg : Void*)\n\n  # control TLS 1.3 session ticket generation\n  {% if OPENSSL_VERSION != \"0.0.0\" %}\n    fun ssl_ctx_set_num_tickets = SSL_CTX_set_num_tickets(ctx : SSLContext, larg : LibC::SizeT) : Int\n    fun ssl_set_num_tickets = SSL_set_num_tickets(ctx : SSL, larg : LibC::SizeT) : Int\n  {% end %}\n\n  fun tls_method = TLS_method : SSLMethod\n\n  alias ALPNCallback = (SSL, Char**, Char*, Char*, Int, Void*) -> Int\n\n  fun ssl_get0_alpn_selected = SSL_get0_alpn_selected(handle : SSL, data : Char**, len : LibC::UInt*) : Void\n  fun ssl_ctx_set_alpn_select_cb = SSL_CTX_set_alpn_select_cb(ctx : SSLContext, cb : ALPNCallback, arg : Void*) : Void\n  fun ssl_ctx_set_alpn_protos = SSL_CTX_set_alpn_protos(ctx : SSLContext, protos : Char*, protos_len : Int) : Int\n\n  alias X509VerifyParam = LibCrypto::X509VerifyParam\n\n  fun dtls_method = DTLS_method : SSLMethod\n\n  fun ssl_get0_param = SSL_get0_param(handle : SSL) : X509VerifyParam\n  fun ssl_ctx_get0_param = SSL_CTX_get0_param(ctx : SSLContext) : X509VerifyParam\n  fun ssl_ctx_set1_param = SSL_CTX_set1_param(ctx : SSLContext, param : X509VerifyParam) : Int\n\n  {% if OPENSSL_VERSION != \"0.0.0\" || compare_versions(LIBRESSL_VERSION, \"3.6.0\") >= 0 %}\n    fun ssl_ctx_set_security_level = SSL_CTX_set_security_level(ctx : SSLContext, level : Int) : Void\n    fun ssl_ctx_get_security_level = SSL_CTX_get_security_level(ctx : SSLContext) : Int\n  {% end %}\n\n  # SSL reason codes\n  {% if compare_versions(OPENSSL_VERSION, \"3.0.0\") >= 0 %}\n    SSL_R_UNEXPECTED_EOF_WHILE_READING = 294\n  {% end %}\nend\n"
  },
  {
    "path": "src/openssl/md5.cr",
    "content": "require \"./lib_crypto\"\n\n# Binds the OpenSSL MD5 hash functions.\n#\n# WARNING: MD5 is no longer a cryptographically secure hash, and should not be\n# used in security-related components, like password hashing. For passwords, see\n# `Crypto::Bcrypt::Password`. For a generic cryptographic hash, use SHA-256 via\n# `OpenSSL::Digest.new(\"SHA256\")`.\nclass OpenSSL::MD5\n  def self.hash(data : String) : UInt8[16]\n    hash(data.to_unsafe, data.bytesize)\n  end\n\n  def self.hash(data : UInt8*, bytesize : Int) : UInt8[16]\n    buffer = uninitialized UInt8[16]\n    LibCrypto.md5(data, bytesize, buffer)\n    buffer\n  end\nend\n"
  },
  {
    "path": "src/openssl/pkcs5.cr",
    "content": "require \"openssl\"\nrequire \"openssl/algorithm\"\n\nmodule OpenSSL::PKCS5\n  def self.pbkdf2_hmac_sha1(secret, salt, iterations = 2**16, key_size = 64) : Bytes\n    buffer = Bytes.new(key_size)\n    if LibCrypto.pkcs5_pbkdf2_hmac_sha1(secret, secret.bytesize, salt, salt.bytesize, iterations, key_size, buffer) != 1\n      raise OpenSSL::Error.new \"pkcs5_pbkdf2_hmac\"\n    end\n    buffer\n  end\n\n  def self.pbkdf2_hmac(secret, salt, iterations = 2**16, algorithm : OpenSSL::Algorithm = OpenSSL::Algorithm::SHA1, key_size = 64) : Bytes\n    evp = algorithm.to_evp\n    buffer = Bytes.new(key_size)\n    if LibCrypto.pkcs5_pbkdf2_hmac(secret, secret.bytesize, salt, salt.bytesize, iterations, evp, key_size, buffer) != 1\n      raise OpenSSL::Error.new \"pkcs5_pbkdf2_hmac\"\n    end\n    buffer\n  end\nend\n"
  },
  {
    "path": "src/openssl/sha1.cr",
    "content": "require \"./lib_crypto\"\n\n# Binds the OpenSSL SHA1 hash functions.\n#\n# WARNING: SHA1 is no longer a cryptographically secure hash, and should not be\n# used in security-related components, like password hashing. For passwords, see\n# `Crypto::Bcrypt::Password`. For a generic cryptographic hash, use SHA-256 via\n# `OpenSSL::Digest.new(\"SHA256\")`.\nclass OpenSSL::SHA1\n  def self.hash(data : String)\n    hash(data.to_unsafe, LibC::SizeT.new(data.bytesize))\n  end\n\n  def self.hash(data : UInt8*, bytesize : LibC::SizeT)\n    buffer = uninitialized UInt8[20]\n    LibCrypto.sha1(data, bytesize, buffer)\n    buffer\n  end\nend\n"
  },
  {
    "path": "src/openssl/ssl/context.cr",
    "content": "require \"uri/punycode\"\nrequire \"log\"\n{% if flag?(:win32) %}\n  require \"crystal/system/win32/crypto\"\n{% end %}\n\n# An `SSL::Context` represents a generic secure socket protocol configuration.\n#\n# For both server and client applications exist more specialized subclassses\n# `SSL::Context::Server` and `SSL::Context::Client` which need to be instantiated\n# appropriately.\nabstract class OpenSSL::SSL::Context\n  # :nodoc:\n  def self.default_method\n    LibSSL.tls_method\n  end\n\n  class Client < Context\n    @hostname : String?\n\n    # Generates a new TLS client context with sane defaults for a client connection.\n    #\n    # Defaults to `TLS_method` or `SSLv23_method` (depending on OpenSSL version)\n    # which tells OpenSSL to negotiate the TLS or SSL protocol with the remote\n    # endpoint.\n    #\n    # Don't change the method unless you must restrict a specific protocol to be\n    # used (eg: TLSv1.2) and nothing else. You should specify options to disable\n    # specific protocols, yet allow to negotiate from various other ones. For\n    # example the following snippet will enable the TLSv1, TLSv1.1 and TLSv1.2\n    # protocols but disable the deprecated SSLv2 and SSLv3 protocols:\n    #\n    # ```\n    # require \"openssl\"\n    #\n    # context = OpenSSL::SSL::Context::Client.new\n    # context.add_options(OpenSSL::SSL::Options::NO_SSL_V2 | OpenSSL::SSL::Options::NO_SSL_V3)\n    # ```\n    def initialize(method : LibSSL::SSLMethod = Context.default_method)\n      super(method)\n\n      self.verify_mode = OpenSSL::SSL::VerifyMode::PEER\n      self.default_verify_param = \"ssl_server\"\n    end\n\n    # Returns a new TLS client context with only the given method set.\n    #\n    # For everything else this uses the defaults of your OpenSSL.\n    # Use this only if undoing the defaults that `new` sets is too much hassle.\n    def self.insecure(method : LibSSL::SSLMethod = Context.default_method) : self\n      super(method)\n    end\n\n    # Configures a client context from a hash-like interface.\n    #\n    # ```\n    # require \"openssl\"\n    #\n    # context = OpenSSL::SSL::Context::Client.from_hash({\"key\" => \"private.key\", \"cert\" => \"certificate.crt\", \"ca\" => \"ca.pem\"})\n    # ```\n    #\n    # Params:\n    #\n    # * `key` *(required)*: Path to private key file. See `#private_key=`.\n    # * `cert` *(required)*: Path to the file containing the public certificate chain. See `#certificate_chain=`.\n    # * `verify_mode`: Either `peer`, `force-peer`, `none` or empty (default: `peer`). See `verify_mode=`.\n    # * `ca`: Path to a file containing the CA certificate chain or a directory containing all CA certificates.\n    #    See `#ca_certificates=` and `#ca_certificates_path=`, respectively.\n    #    Required if `verify_mode` is `peer`, `force-peer` or empty.\n    def self.from_hash(params) : self\n      super(params)\n    end\n\n    # Wraps the original certificate verification to also validate the\n    # hostname against the certificate configured Subject Alternate\n    # Names or Common Name.\n    #\n    # Required for OpenSSL <= 1.0.1 only.\n    protected def set_cert_verify_callback(hostname : String)\n      # Sanitize the hostname with PunyCode\n      hostname = URI::Punycode.to_ascii hostname\n\n      # Keep a reference so the GC doesn't collect it after sending it to C land\n      @hostname = hostname\n      LibSSL.ssl_ctx_set_cert_verify_callback(@handle, ->(x509_ctx, arg) {\n        if LibCrypto.x509_verify_cert(x509_ctx) != 0\n          cert = LibCrypto.x509_store_ctx_get_current_cert(x509_ctx)\n          HostnameValidation.validate_hostname(arg.as(String), cert) == HostnameValidation::Result::MatchFound ? 1 : 0\n        else\n          0\n        end\n      }, hostname.as(Void*))\n    end\n\n    private def alpn_protocol=(protocol : Bytes)\n      LibSSL.ssl_ctx_set_alpn_protos(@handle, protocol, protocol.size)\n    end\n  end\n\n  class Server < Context\n    # Keep a reference so the GC doesn't collect it after sending it to C land\n    @sni_callback_box = Pointer(Void).null\n\n    # Generates a new TLS server context with sane defaults for a server connection.\n    #\n    # Defaults to `TLS_method` or `SSLv23_method` (depending on OpenSSL version)\n    # which tells OpenSSL to negotiate the TLS or SSL protocol with the remote\n    # endpoint.\n    #\n    # Don't change the method unless you must restrict a specific protocol to be\n    # used (eg: TLSv1.2) and nothing else. You should specify options to disable\n    # specific protocols, yet allow to negotiate from various other ones. For\n    # example the following snippet will enable the TLSv1, TLSv1.1 and TLSv1.2\n    # protocols but disable the deprecated SSLv2 and SSLv3 protocols:\n    #\n    # ```\n    # context = OpenSSL::SSL::Context::Server.new\n    # context.add_options(OpenSSL::SSL::Options::NO_SSL_V2 | OpenSSL::SSL::Options::NO_SSL_V3)\n    # ```\n    def initialize(method : LibSSL::SSLMethod = Context.default_method)\n      super(method)\n      self.default_verify_param = \"ssl_client\"\n    end\n\n    # Returns a new TLS server context with only the given method set.\n    #\n    # For everything else this uses the defaults of your OpenSSL.\n    # Use this only if undoing the defaults that `new` sets is too much hassle.\n    def self.insecure(method : LibSSL::SSLMethod = Context.default_method) : self\n      super(method)\n    end\n\n    # Configures a server from a hash-like interface.\n    #\n    # ```\n    # require \"openssl\"\n    #\n    # context = OpenSSL::SSL::Context::Client.from_hash({\"key\" => \"private.key\", \"cert\" => \"certificate.crt\", \"ca\" => \"ca.pem\"})\n    # ```\n    #\n    # Params:\n    #\n    # * `key` *(required)*: Path to private key file. See `#private_key=`.\n    # * `cert` *(required)*: Path to the file containing the public certificate chain. See `#certificate_chain=`.\n    # * `verify_mode`: Either `peer`, `force-peer`, `none` or empty (default: `none`). See `verify_mode=`.\n    # * `ca`: Path to a file containing the CA certificate chain or a directory containing all CA certificates.\n    #    See `#ca_certificates=` and `#ca_certificates_path=`, respectively.\n    #    Required if `verify_mode` is `peer` or `force-peer`.\n    def self.from_hash(params) : self\n      super(params)\n    end\n\n    # Disables all session ticket generation for this context.\n    # Tickets are used to resume earlier sessions more quickly,\n    # but in TLS 1.3 if the client connects, sends data, and closes the connection\n    # unidirectionally, the server connects, then sends a ticket\n    # after the connect handshake, the ticket send can fail with Broken Pipe.\n    # So if you have that kind of behavior (clients that never read) call this method.\n    def disable_session_resume_tickets : Nil\n      add_options(OpenSSL::SSL::Options::NO_TICKET) # TLS v1.2 and below\n      {% if LibSSL.has_method?(:ssl_ctx_set_num_tickets) %}\n        ret = LibSSL.ssl_ctx_set_num_tickets(self, 0) # TLS v1.3\n        raise OpenSSL::Error.new(\"SSL_CTX_set_num_tickets\") if ret != 1\n      {% end %}\n    end\n\n    private def alpn_protocol=(protocol : Bytes)\n      alpn_cb = ->(ssl : LibSSL::SSL, o : LibC::Char**, olen : LibC::Char*, i : LibC::Char*, ilen : LibC::Int, data : Void*) {\n        proto = Box(Bytes).unbox(data)\n        ret = LibSSL.ssl_select_next_proto(o, olen, proto, proto.size, i, ilen)\n        if ret != LibSSL::OPENSSL_NPN_NEGOTIATED\n          LibSSL::SSL_TLSEXT_ERR_NOACK\n        else\n          LibSSL::SSL_TLSEXT_ERR_OK\n        end\n      }\n      @alpn_protocol = alpn_protocol = Box.box(protocol)\n      LibSSL.ssl_ctx_set_alpn_select_cb(@handle, alpn_cb, alpn_protocol)\n    end\n\n    # Sets a Server Name Indication (SNI) callback for this server context.\n    #\n    # The callback receives the hostname from the client's SNI extension and should\n    # return an `SSL::Context::Server` configured for that hostname, or `nil` to\n    # continue using the current context.\n    #\n    # This allows a TLS server to present different certificates based on the\n    # hostname the client is connecting to, enabling virtual hosting over TLS.\n    #\n    # Example:\n    # ```\n    # default_context = OpenSSL::SSL::Context::Server.new\n    # default_context.certificate_chain = \"default.crt\"\n    # default_context.private_key = \"default.key\"\n    #\n    # example_context = OpenSSL::SSL::Context::Server.new\n    # example_context.certificate_chain = \"example.com.crt\"\n    # example_context.private_key = \"example.com.key\"\n    #\n    # default_context.on_server_name do |hostname|\n    #   case hostname\n    #   when \"example.com\", \"www.example.com\"\n    #     example_context\n    #   else\n    #     nil # use default context\n    #   end\n    # end\n    # ```\n    #\n    # See [SSL_CTX_set_tlsext_servername_callback](https://docs.openssl.org/3.5/man3/SSL_CTX_set_tlsext_servername_callback/)\n    @[Experimental]\n    def on_server_name(&block : String -> OpenSSL::SSL::Context::Server?)\n      # Create a C callback that extracts the hostname and calls our Crystal block\n      c_callback = Proc(LibSSL::SSL, LibC::Int*, Void*, LibC::Int).new do |ssl, alert_ptr, arg|\n        servername_ptr = LibSSL.ssl_get_servername(ssl, LibSSL::TLSExt::NAMETYPE_host_name)\n        if servername_ptr.null?\n          next LibSSL::SSL_TLSEXT_ERR_OK\n        end\n\n        begin\n          hostname = String.new(servername_ptr)\n\n          callback = Box(typeof(block)).unbox(arg)\n          new_context = callback.call(hostname)\n\n          if new_context\n            LibSSL.ssl_set_ssl_ctx(ssl, new_context.to_unsafe)\n          end\n\n          LibSSL::SSL_TLSEXT_ERR_OK\n        rescue\n          alert_ptr.value = LibSSL::SSL_AD_INTERNAL_ERROR\n          LibSSL::SSL_TLSEXT_ERR_ALERT_FATAL\n        end\n      end\n\n      # Box the callback to pass to C\n      callback_box = Box.box(block)\n      @sni_callback_box = callback_box\n\n      # Set the callback using SSL_CTX_callback_ctrl\n      LibSSL.ssl_ctx_callback_ctrl(@handle, LibSSL::SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, c_callback.unsafe_as(Proc(Void)))\n\n      # Set the arg that will be passed to the callback\n      LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, callback_box)\n    end\n  end\n\n  protected def initialize(method : LibSSL::SSLMethod)\n    @handle = LibSSL.ssl_ctx_new(method)\n    raise OpenSSL::Error.new(\"SSL_CTX_new\") if @handle.null?\n\n    set_default_verify_paths\n\n    add_options(OpenSSL::SSL::Options.flags(\n      ALL,\n      NO_TLS_V1,\n      NO_TLS_V1_1,\n      NO_SESSION_RESUMPTION_ON_RENEGOTIATION,\n      NO_RENEGOTIATION,\n    ))\n    add_modes(OpenSSL::SSL::Modes.flags(AUTO_RETRY, RELEASE_BUFFERS))\n\n    # OpenSSL does not support reading from the system root certificate store on\n    # Windows, so we have to import them ourselves\n    {% if flag?(:win32) %}\n      Crystal::System::Crypto.populate_system_root_certificates(self)\n    {% end %}\n  end\n\n  # Overriding initialize or new in the child classes as public methods,\n  # makes it either impossible to access the parent versions or makes the parent\n  # versions public too. So to provide insecure in the child classes, we need\n  # a second constructor that we call from there without getting the\n  # overridden ones of the children.\n  protected def _initialize_insecure(method : LibSSL::SSLMethod)\n    @handle = LibSSL.ssl_ctx_new(method)\n    raise OpenSSL::Error.new(\"SSL_CTX_new\") if @handle.null?\n\n    # since an insecure context on non-Windows systems still has access to the\n    # system certificates, we do the same for Windows\n    {% if flag?(:win32) %}\n      Crystal::System::Crypto.populate_system_root_certificates(self)\n    {% end %}\n  end\n\n  protected def self.insecure(method : LibSSL::SSLMethod)\n    obj = allocate\n    obj._initialize_insecure(method)\n    GC.add_finalizer(obj)\n    obj\n  end\n\n  def finalize\n    LibSSL.ssl_ctx_free(@handle)\n  end\n\n  # Sets the default paths for `ca_certificates=` and `ca_certificates_path=`.\n  def set_default_verify_paths\n    LibSSL.ssl_ctx_set_default_verify_paths(@handle)\n  end\n\n  # Sets the path to a file containing all CA certificates, in PEM format, used to\n  # validate the peers certificate.\n  def ca_certificates=(file_path : String)\n    ret = LibSSL.ssl_ctx_load_verify_locations(@handle, file_path, nil)\n    raise OpenSSL::Error.new(\"SSL_CTX_load_verify_locations\") unless ret == 1\n  end\n\n  # Sets the path to a directory containing all CA certificates used to\n  # validate the peers certificate. The certificates should be in PEM format\n  # and the `c_rehash(1)` utility must have been run in the directory.\n  def ca_certificates_path=(dir_path : String)\n    ret = LibSSL.ssl_ctx_load_verify_locations(@handle, nil, dir_path)\n    raise OpenSSL::Error.new(\"SSL_CTX_load_verify_locations\") unless ret == 1\n  end\n\n  # Specify the path to the certificate chain file to use. In server mode this\n  # is presented to the client, in client mode this used as client certificate.\n  def certificate_chain=(file_path : String)\n    ret = LibSSL.ssl_ctx_use_certificate_chain_file(@handle, file_path)\n    raise OpenSSL::Error.new(\"SSL_CTX_use_certificate_chain_file\") unless ret == 1\n  end\n\n  # Specify the path to the private key to use. The key must in PEM format.\n  # The key must correspond to the entity certificate set by `certificate_chain=`.\n  def private_key=(file_path : String)\n    ret = LibSSL.ssl_ctx_use_privatekey_file(@handle, file_path, LibSSL::SSLFileType::PEM)\n    raise OpenSSL::Error.new(\"SSL_CTX_use_PrivateKey_file\") unless ret == 1\n  end\n\n  # Specify a list of TLS ciphers to use or discard for TLSv1.2 and below.\n  #\n  # See `#security_level=` for some sensible system configuration.\n  #\n  # This method does not impact TLSv1.3 ciphersuites. Use `#cipher_suites=`\n  # to configure those.\n  #\n  # NOTE: The ciphers available to an application are determined by the\n  #       linked version of the system SSL library. A comprehensive list\n  #       of ciphers can be found in the\n  #       [OpenSSL Cipher documentation](https://www.openssl.org/docs/man3.0/man1/openssl-ciphers.html#CIPHER-STRINGS).\n  def ciphers=(ciphers : String)\n    ret = LibSSL.ssl_ctx_set_cipher_list(@handle, ciphers)\n    raise OpenSSL::Error.new(\"SSL_CTX_set_cipher_list\") if ret == 0\n    ciphers\n  end\n\n  # Specify a list of TLS ciphersuites to use or discard for TLSv1.3.\n  #\n  # See `#security_level=` for some sensible system configuration.\n  #\n  # NOTE: The ciphersuites available to an application are determined by the\n  #       linked version of the system SSL library. A comprehensive list\n  #       of ciphersuites can be found in the\n  #       [OpenSSL Cipher documentation](https://www.openssl.org/docs/man3.0/man1/openssl-ciphers.html#TLS-v1.3-cipher-suites).\n  def cipher_suites=(cipher_suites : String)\n    {% if LibSSL.has_method?(:ssl_ctx_set_ciphersuites) %}\n      ret = LibSSL.ssl_ctx_set_ciphersuites(@handle, cipher_suites)\n      raise OpenSSL::Error.new(\"SSL_CTX_set_ciphersuites\") if ret == 0\n    {% else %}\n      Log.warn { \"SSL_CTX_set_ciphersuites not supported\" }\n    {% end %}\n    cipher_suites\n  end\n\n  # Sets the current ciphers and ciphers suites to **modern** compatibility level as per Mozilla\n  # recommendations. See `#security_level=` for some sensible system configuration.\n  #\n  # WARNING: Does nothing as of Crystal 1.13.\n  # WARNING: Didn't work as expected as of OpenSSL 1.1 (didn't configure TLSv1.2 and below).\n  @[Deprecated(\"Deprecated with no replacement. Prefer #security_level, global system configuration or build your own from https://wiki.mozilla.org/Security/Server_Side_TLS\")]\n  def set_modern_ciphers\n  end\n\n  # Sets the current ciphers and ciphers suites to **intermediate** compatibility level as per Mozilla\n  # recommendations. See `#security_level=` for some sensible system configuration.\n  #\n  # WARNING: Does nothing as of Crystal 1.13.\n  # WARNING: Didn't work as expected as of OpenSSL 1.1 (didn't configure TLSv1.2 and below).\n  @[Deprecated(\"Deprecated with no replacement. Prefer #security_level, global system configuration or build your own from https://wiki.mozilla.org/Security/Server_Side_TLS\")]\n  def set_intermediate_ciphers\n  end\n\n  # Sets the current ciphers and ciphers suites to **old** compatibility level as per Mozilla\n  # recommendations. See `#security_level=` for some sensible system configuration.\n  #\n  # WARNING: Does nothing as of Crystal 1.13.\n  # WARNING: Didn't work as expected as of OpenSSL 1.1 (didn't configure TLSv1.2 and below).\n  @[Deprecated(\"Deprecated with no replacement. Prefer #security_level, global system configuration or build your own from https://wiki.mozilla.org/Security/Server_Side_TLS\")]\n  def set_old_ciphers\n  end\n\n  # Returns the security level used by this TLS context.\n  def security_level : Int32\n    {% if LibSSL.has_method?(:ssl_ctx_get_security_level) %}\n      LibSSL.ssl_ctx_get_security_level(@handle)\n    {% else %}\n      Log.warn { \"SSL_CTX_get_security_level not supported\" }\n      0\n    {% end %}\n  end\n\n  # Sets the security level used by this TLS context. The default system\n  # security level might disable some ciphers.\n  #\n  # * https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html\n  # * https://wiki.debian.org/ContinuousIntegration/TriagingTips/openssl-1.1.1\n  def security_level=(value : Int32)\n    {% if LibSSL.has_method?(:ssl_ctx_set_security_level) %}\n      LibSSL.ssl_ctx_set_security_level(@handle, value)\n    {% else %}\n      Log.warn { \"SSL_CTX_set_security_level not supported\" }\n    {% end %}\n    value\n  end\n\n  # Adds a temporary ECDH key curve to the TLS context. This is required to\n  # enable the EECDH cipher suites. By default the prime256 curve will be used.\n  def set_tmp_ecdh_key(curve = LibCrypto::NID_X9_62_prime256v1) : Nil\n    key = LibCrypto.ec_key_new_by_curve_name(curve)\n    raise OpenSSL::Error.new(\"ec_key_new_by_curve_name\") if key.null?\n    LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_SET_TMP_ECDH, 0, key)\n    LibCrypto.ec_key_free(key)\n  end\n\n  # Returns the current modes set on the TLS context.\n  def modes : LibSSL::Modes\n    OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_MODE, 0, nil)\n  end\n\n  # Adds modes to the TLS context.\n  def add_modes(mode : OpenSSL::SSL::Modes)\n    OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_MODE, mode, nil)\n  end\n\n  # Removes modes from the TLS context.\n  def remove_modes(mode : OpenSSL::SSL::Modes)\n    OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_CLEAR_MODE, mode, nil)\n  end\n\n  # Returns the current options set on the TLS context.\n  def options : LibSSL::Options\n    opts = {% if LibSSL.has_method?(:ssl_ctx_get_options) %}\n             LibSSL.ssl_ctx_get_options(@handle)\n           {% else %}\n             LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_OPTIONS, 0, nil)\n           {% end %}\n    OpenSSL::SSL::Options.new(opts)\n  end\n\n  # Adds options to the TLS context.\n  #\n  # Example:\n  # ```\n  # context.add_options(\n  #   OpenSSL::SSL::Options::ALL |       # various workarounds\n  #   OpenSSL::SSL::Options::NO_SSL_V2 | # disable overly deprecated SSLv2\n  #   OpenSSL::SSL::Options::NO_SSL_V3   # disable deprecated SSLv3\n  # )\n  # ```\n  def add_options(options : OpenSSL::SSL::Options)\n    opts = {% if LibSSL.has_method?(:ssl_ctx_set_options) %}\n             LibSSL.ssl_ctx_set_options(@handle, options)\n           {% else %}\n             LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_OPTIONS, options, nil)\n           {% end %}\n    OpenSSL::SSL::Options.new(opts)\n  end\n\n  # Removes options from the TLS context.\n  #\n  # Example:\n  # ```\n  # context.remove_options(OpenSSL::SSL::Options::NO_SSL_V3)\n  # ```\n  def remove_options(options : OpenSSL::SSL::Options)\n    opts = {% if LibSSL.has_method?(:ssl_ctx_clear_options) %}\n             LibSSL.ssl_ctx_clear_options(@handle, options)\n           {% else %}\n             LibSSL.ssl_ctx_ctrl(@handle, LibSSL::SSL_CTRL_CLEAR_OPTIONS, options, nil)\n           {% end %}\n    OpenSSL::SSL::Options.new(opts)\n  end\n\n  # Returns the current verify mode. See the `SSL_CTX_set_verify(3)` manpage for more details.\n  def verify_mode : LibSSL::VerifyMode\n    LibSSL.ssl_ctx_get_verify_mode(@handle)\n  end\n\n  # Sets the verify mode. See the `SSL_CTX_set_verify(3)` manpage for more details.\n  def verify_mode=(mode : OpenSSL::SSL::VerifyMode)\n    LibSSL.ssl_ctx_set_verify(@handle, mode, nil)\n  end\n\n  @alpn_protocol = Pointer(Void).null\n\n  # Specifies an ALPN protocol to negotiate with the remote endpoint. This is\n  # required to negotiate HTTP/2 with browsers, since browser vendors decided\n  # not to implement HTTP/2 over insecure connections.\n  #\n  # Example:\n  # ```\n  # context.alpn_protocol = \"h2\"\n  # ```\n  def alpn_protocol=(protocol : String)\n    proto = Bytes.new(protocol.bytesize + 1)\n    proto[0] = protocol.bytesize.to_u8\n    protocol.to_slice.copy_to(proto.to_unsafe + 1, protocol.bytesize)\n    self.alpn_protocol = proto\n  end\n\n  # Sets this context verify param to the default one of the given name.\n  #\n  # Depending on the OpenSSL version, the available defaults are\n  # `default`, `pkcs7`, `smime_sign`, `ssl_client` and `ssl_server`.\n  def default_verify_param=(name : String)\n    param = LibCrypto.x509_verify_param_lookup(name)\n    raise ArgumentError.new(\"#{name} is an unsupported default verify param\") unless param\n    ret = LibSSL.ssl_ctx_set1_param(@handle, param)\n    raise OpenSSL::Error.new(\"SSL_CTX_set1_param\") unless ret == 1\n  end\n\n  # Sets the given `OpenSSL::SSL::X509VerifyFlags` in this context, additionally to\n  # the already set ones.\n  def add_x509_verify_flags(flags : OpenSSL::SSL::X509VerifyFlags)\n    param = LibSSL.ssl_ctx_get0_param(@handle)\n    ret = LibCrypto.x509_verify_param_set_flags(param, flags)\n    raise OpenSSL::Error.new(\"X509_VERIFY_PARAM_set_flags\") unless ret == 1\n  end\n\n  def to_unsafe\n    @handle\n  end\n\n  private def self.from_hash(params)\n    context = new\n    if key = params[\"key\"]?\n      context.private_key = key\n    else\n      raise ArgumentError.new(\"Invalid SSL context: missing private key ('key=')\")\n    end\n\n    if cert = params[\"cert\"]?\n      context.certificate_chain = cert\n    else\n      raise ArgumentError.new(\"Invalid SSL context: missing certificate ('cert=')\")\n    end\n\n    case verify_mode = params[\"verify_mode\"]?\n    when \"peer\"\n      context.verify_mode = OpenSSL::SSL::VerifyMode::PEER\n    when \"force-peer\"\n      context.verify_mode = OpenSSL::SSL::VerifyMode::PEER | OpenSSL::SSL::VerifyMode::FAIL_IF_NO_PEER_CERT\n    when \"none\"\n      context.verify_mode = OpenSSL::SSL::VerifyMode::NONE\n    when nil\n      # use default\n    else\n      raise ArgumentError.new(\"Invalid SSL context: unknown verify mode #{verify_mode.inspect}\")\n    end\n\n    if ca = params[\"ca\"]?\n      if File.directory?(ca)\n        context.ca_certificates_path = ca\n      else\n        context.ca_certificates = ca\n      end\n    elsif context.verify_mode.peer? || context.verify_mode.fail_if_no_peer_cert?\n      raise ArgumentError.new(\"Invalid SSL context: missing CA certificate ('ca=')\")\n    end\n\n    context\n  end\nend\n"
  },
  {
    "path": "src/openssl/ssl/defaults.cr",
    "content": "# THIS FILE WAS AUTOMATICALLY GENERATED BY scripts/generate_ssl_server_defaults.cr\n# on 2024-06-07 09:20:33 UTC.\n\nabstract class OpenSSL::SSL::Context\n  # The list of secure ciphers on **modern** compatibility level as per Mozilla\n  # recommendations.\n  #\n  # The oldest clients supported by this configuration are:\n  # * Firefox 63\n  # * Android 10.0\n  # * Chrome 70\n  # * Edge 75\n  # * Java 11\n  # * OpenSSL 1.1.1\n  # * Opera 57\n  # * Safari 12.1\n  #\n  # This list represents version 5.7 of the modern configuration\n  # available at https://ssl-config.mozilla.org/guidelines/5.7.json.\n  #\n  # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n  @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n  CIPHERS_MODERN = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS\"\n\n  # The list of secure ciphersuites on **modern** compatibility level as per Mozilla\n  # recommendations.\n  #\n  # The oldest clients supported by this configuration are:\n  # * Firefox 63\n  # * Android 10.0\n  # * Chrome 70\n  # * Edge 75\n  # * Java 11\n  # * OpenSSL 1.1.1\n  # * Opera 57\n  # * Safari 12.1\n  #\n  # This list represents version 5.7 of the modern configuration\n  # available at https://ssl-config.mozilla.org/guidelines/5.7.json.\n  #\n  # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n  @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n  CIPHER_SUITES_MODERN = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\"\n\n  # The list of secure ciphers on **intermediate** compatibility level as per Mozilla\n  # recommendations.\n  #\n  # The oldest clients supported by this configuration are:\n  # * Firefox 27\n  # * Android 4.4.2\n  # * Chrome 31\n  # * Edge\n  # * IE 11 on Windows 7\n  # * Java 8u31\n  # * OpenSSL 1.0.1\n  # * Opera 20\n  # * Safari 9\n  #\n  # This list represents version 5.7 of the intermediate configuration\n  # available at https://ssl-config.mozilla.org/guidelines/5.7.json.\n  #\n  # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n  @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n  CIPHERS_INTERMEDIATE = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS\"\n\n  # The list of secure ciphersuites on **intermediate** compatibility level as per Mozilla\n  # recommendations.\n  #\n  # The oldest clients supported by this configuration are:\n  # * Firefox 27\n  # * Android 4.4.2\n  # * Chrome 31\n  # * Edge\n  # * IE 11 on Windows 7\n  # * Java 8u31\n  # * OpenSSL 1.0.1\n  # * Opera 20\n  # * Safari 9\n  #\n  # This list represents version 5.7 of the intermediate configuration\n  # available at https://ssl-config.mozilla.org/guidelines/5.7.json.\n  #\n  # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n  @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n  CIPHER_SUITES_INTERMEDIATE = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\"\n\n  # The list of secure ciphers on **old** compatibility level as per Mozilla\n  # recommendations.\n  #\n  # The oldest clients supported by this configuration are:\n  # * Firefox 1\n  # * Android 2.3\n  # * Chrome 1\n  # * Edge 12\n  # * IE8 on Windows XP\n  # * Java 6\n  # * OpenSSL 0.9.8\n  # * Opera 5\n  # * Safari 1\n  #\n  # This list represents version 5.7 of the old configuration\n  # available at https://ssl-config.mozilla.org/guidelines/5.7.json.\n  #\n  # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n  @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n  CIPHERS_OLD = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS\"\n\n  # The list of secure ciphersuites on **old** compatibility level as per Mozilla\n  # recommendations.\n  #\n  # The oldest clients supported by this configuration are:\n  # * Firefox 1\n  # * Android 2.3\n  # * Chrome 1\n  # * Edge 12\n  # * IE8 on Windows XP\n  # * Java 6\n  # * OpenSSL 0.9.8\n  # * Opera 5\n  # * Safari 1\n  #\n  # This list represents version 5.7 of the old configuration\n  # available at https://ssl-config.mozilla.org/guidelines/5.7.json.\n  #\n  # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.\n  @[Deprecated(\"Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org\")]\n  CIPHER_SUITES_OLD = \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\"\nend\n"
  },
  {
    "path": "src/openssl/ssl/hostname_validation.cr",
    "content": "require \"socket\"\nrequire \"openssl\"\n\n# :nodoc:\nmodule OpenSSL::SSL::HostnameValidation\n  enum Result\n    Error\n    MalformedCertificate\n    MatchFound\n    MatchNotFound\n    NoSANPresent\n  end\n\n  # Matches hostname against Subject Alternate Name (SAN) entries of the\n  # certificate.\n  #\n  # The Common Name (CN) entry will only be used if no SAN entries are present\n  # in the certificate, as per\n  # [RFC 6125, Section 6.4.4](https://tools.ietf.org/html/rfc6125#section-6.4.4).\n  def self.validate_hostname(hostname : String, server_cert : LibCrypto::X509) : Result\n    return Result::Error if server_cert.null?\n    result = matches_subject_alternative_name(hostname, server_cert)\n    result = matches_common_name(hostname, server_cert) if result.no_san_present?\n    result\n  end\n\n  # Matches hostname against Subject Alternate Name (SAN) entries of certificate.\n  #\n  # Adapted from https://wiki.openssl.org/index.php/Hostname_validation\n  def self.matches_subject_alternative_name(hostname, server_cert : LibCrypto::X509) : Result\n    san_names = LibCrypto.x509_get_ext_d2i(server_cert, LibCrypto::NID_subject_alt_name, nil, nil)\n    return Result::NoSANPresent if san_names.null?\n\n    begin\n      LibCrypto.sk_num(san_names).times do |i|\n        current_name = LibCrypto.sk_value(san_names, i).as(LibCrypto::GENERAL_NAME*).value\n\n        case current_name.type\n        when LibCrypto::GEN_DNS\n          dns_name = LibCrypto.asn1_string_data(current_name.value)\n          dns_name_len = LibCrypto.asn1_string_length(current_name.value)\n          return Result::MalformedCertificate if dns_name_len != LibC.strlen(dns_name)\n\n          pattern = String.new(dns_name, dns_name_len)\n          return Result::MatchFound if matches_hostname?(pattern, hostname)\n        when LibCrypto::GEN_IPADD\n          data = Slice.new(LibCrypto.asn1_string_data(current_name.value), LibCrypto.asn1_string_length(current_name.value))\n\n          case data.size\n          when 4\n            if v4_fields = ::Socket::IPAddress.parse_v4_fields?(hostname)\n              return Result::MatchFound if v4_fields.to_slice == data\n            end\n          when 16\n            if v6_fields = ::Socket::IPAddress.parse_v6_fields?(hostname)\n              {% if IO::ByteFormat::NetworkEndian != IO::ByteFormat::SystemEndian %}\n                v6_fields.map! &.byte_swap\n              {% end %}\n              return Result::MatchFound if v6_fields.to_slice.to_unsafe_bytes == data\n            end\n          else\n            # not a length we expect\n          end\n        else\n          # not a type we expect\n        end\n      end\n\n      Result::MatchNotFound\n    ensure\n      LibCrypto.sk_pop_free(san_names, ->(ptr : Void*) {\n        LibCrypto.sk_free(ptr)\n      })\n    end\n  end\n\n  # Matches hostname from Common Name (CN) entry of certificate. Should only be\n  # called if no SAN entries could be found in certificate.\n  #\n  # Adapted from https://wiki.openssl.org/index.php/Hostname_validation\n  def self.matches_common_name(hostname, server_cert : LibCrypto::X509) : Result\n    subject = LibCrypto.x509_get_subject_name(server_cert)\n\n    index = LibCrypto.x509_name_get_index_by_nid(subject, LibCrypto::NID_commonName, -1)\n    return Result::Error if index < 0\n\n    name_entry = LibCrypto.x509_name_get_entry(subject, index)\n    return Result::Error if name_entry.null?\n\n    asn1 = LibCrypto.x509_name_entry_get_data(name_entry)\n    return Result::Error if asn1.null?\n\n    str = LibCrypto.asn1_string_data(asn1)\n    str_len = LibCrypto.asn1_string_length(asn1)\n    return Result::MalformedCertificate if str_len != LibC.strlen(str)\n\n    common_name = String.new(str, str_len)\n    return Result::MatchFound if matches_hostname?(common_name, hostname)\n\n    Result::MatchNotFound\n  end\n\n  # Matches a hostname against a wildcard pattern.\n  #\n  # The hostname must be an exact match or use a wildcard following\n  # [RFC 6125, section 6.4.3](http://tools.ietf.org/html/rfc6125#section-6.4.3)\n  # and [RFC 6125, section 7.2](http://tools.ietf.org/html/rfc6125#section-7.2)\n  #\n  # IDNA domains must be given in their punycode encoding, and no wildcard match\n  # will be attempted if the left-most label is an IDNA label. For example\n  # `*.xn--kcry6tjko.example.org` will match `foo.xn--kcry6tjko.example.org` but\n  # `xn--*.example.org` won't match `xn--kcry6tjko.example.org`.\n  #\n  # No wildcard match is attempted for IP addresses. The hostname and patterns\n  # are normalized to skip trailing dots (like browsers do).\n  #\n  # To be compatible with OpenSSL `X509_check_host` a leading dot will match any\n  # subdomain. For example `.example.org` will match both `foo.example.com` and\n  # `bar.foo.example.com`.\n  #\n  # Adapted from cURL:\n  # Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.\n  # https://github.com/curl/curl/blob/curl-7_41_0/lib/hostcheck.c\n  def self.matches_hostname?(pattern, hostname) : Bool\n    pattern = pattern.chomp('.').downcase\n    hostname = hostname.chomp('.').downcase\n\n    # leading dot matches any subdomain (openssl 1.0.2 compatibility)\n    if pattern[0] == '.'\n      return hostname.ends_with?(pattern)\n    end\n\n    unless wildcard = pattern.index('*')\n      return pattern == hostname\n    end\n\n    # fail match when hostname is an IP address\n    if ::Socket::IPAddress.valid?(hostname)\n      return false\n    end\n\n    unless pattern_label_len = pattern.index('.')\n      return false\n    end\n\n    # only the first label should be considered for wildcard match\n    # need at least 2 dots in pattern to avoid too wide wildcard match\n    # no wildcard match in IDNA label\n    if wildcard > pattern_label_len || pattern.count('.') < 2 || pattern.starts_with?(\"xn--\")\n      return pattern == hostname\n    end\n\n    unless hostname_label_len = hostname.index('.')\n      return false\n    end\n\n    # the wildcard must match at least 1 char, so the first label must be at least\n    # the same size than pattern first label\n    if hostname_label_len < pattern_label_len\n      return false\n    end\n\n    # domains don't match\n    if pattern[pattern_label_len..-1] != hostname[hostname_label_len..-1]\n      return false\n    end\n\n    # wildcard match\n    suffix = pattern_label_len - (wildcard + 1)\n    pattern[0, wildcard] == hostname[0, wildcard] &&\n      pattern[wildcard + 1, suffix + 1] == hostname[hostname_label_len - suffix, suffix + 1]\n  end\nend\n"
  },
  {
    "path": "src/openssl/ssl/server.cr",
    "content": "require \"socket\"\n\n# This class wraps another `::Socket::Server` in an SSL layer.\n#\n# ```\n# require \"socket\"\n# require \"openssl\"\n#\n# tcp_server = TCPServer.new(0)\n#\n# ssl_context = OpenSSL::SSL::Context::Server.new\n# ssl_context.certificate_chain = \"openssl.crt\"\n# ssl_context.private_key = \"openssl.key\"\n# ssl_server = OpenSSL::SSL::Server.new(tcp_server, ssl_context)\n#\n# puts \"SSL Server listening on #{tcp_server.local_address}\"\n# while connection = ssl_server.accept?\n#   connection.puts \"secure message\"\n#   connection.close\n# end\n# ```\nclass OpenSSL::SSL::Server\n  include ::Socket::Server\n\n  # Returns the wrapped server socket.\n  getter wrapped : ::Socket::Server\n\n  # Returns the SSL context.\n  getter context : OpenSSL::SSL::Context::Server\n\n  # If `#sync_close?` is `true`, closing this server will\n  # close the wrapped server.\n  property? sync_close : Bool\n\n  # Returns `true` if this SSL server has been closed.\n  getter? closed : Bool = false\n\n  # When `true` a call to `#accept` will also initiate the SSL handshake.\n  property start_immediately : Bool = true\n\n  # Creates a new SSL server wrapping *wrapped*.\n  #\n  # *context* configures the SSL options, see `OpenSSL::SSL::Context::Server` for details\n  def initialize(@wrapped : ::Socket::Server, @context : OpenSSL::SSL::Context::Server = OpenSSL::SSL::Context::Server.new, @sync_close : Bool = true)\n  end\n\n  # Creates a new SSL server wrapping *wrapped*  and yields it to the block.\n  #\n  # *context* configures the SSL options, see `OpenSSL::SSL::Context::Server` for details\n  #\n  # The server is closed after the block returns.\n  def self.open(wrapped : ::Socket::Server, context : OpenSSL::SSL::Context::Server = OpenSSL::SSL::Context::Server.new, sync_close : Bool = true, &)\n    server = new(wrapped, context, sync_close)\n\n    begin\n      yield server\n    ensure\n      server.close\n    end\n  end\n\n  # Implements `::Socket::Server#accept`.\n  #\n  # This method calls `@wrapped.accept` and wraps the resulting IO in a SSL socket (`OpenSSL::SSL::Socket::Server`) with `context` configuration.\n  def accept : OpenSSL::SSL::Socket::Server\n    new_ssl_socket(@wrapped.accept)\n  end\n\n  # Implements `::Socket::Server#accept?`.\n  #\n  # This method calls `@wrapped.accept?` and wraps the resulting IO in a SSL socket (`OpenSSL::SSL::Socket::Server`) with `context` configuration.\n  def accept? : OpenSSL::SSL::Socket::Server?\n    if socket = @wrapped.accept?\n      new_ssl_socket(socket)\n    end\n  end\n\n  private def new_ssl_socket(io)\n    OpenSSL::SSL::Socket::Server.new(io, @context, sync_close: @sync_close, accept: @start_immediately)\n  end\n\n  # Closes this SSL server.\n  #\n  # Propagates to `wrapped` if `sync_close` is `true`.\n  def close : Nil\n    return if @closed\n    @closed = true\n\n    @wrapped.close if @sync_close\n  end\n\n  # Returns local address of `wrapped`.\n  def local_address : ::Socket::Address\n    @wrapped.local_address\n  end\nend\n"
  },
  {
    "path": "src/openssl/ssl/socket.cr",
    "content": "abstract class OpenSSL::SSL::Socket < IO\n  class Client < Socket\n    def initialize(io, context : Context::Client = Context::Client.new, sync_close : Bool = false, hostname : String? = nil)\n      super(io, context, sync_close)\n      begin\n        if hostname\n          # Macro from OpenSSL: SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name)\n          LibSSL.ssl_ctrl(\n            @ssl,\n            LibSSL::SSLCtrl::SET_TLSEXT_HOSTNAME,\n            LibSSL::TLSExt::NAMETYPE_host_name,\n            hostname.to_unsafe.as(Pointer(Void))\n          )\n\n          param = LibSSL.ssl_get0_param(@ssl)\n\n          if ::Socket::IPAddress.valid?(hostname)\n            unless LibCrypto.x509_verify_param_set1_ip_asc(param, hostname) == 1\n              raise OpenSSL::Error.new(\"X509_VERIFY_PARAM_set1_ip_asc\")\n            end\n          else\n            unless LibCrypto.x509_verify_param_set1_host(param, hostname, 0) == 1\n              raise OpenSSL::Error.new(\"X509_VERIFY_PARAM_set1_host\")\n            end\n          end\n        end\n\n        ret = LibSSL.ssl_connect(@ssl)\n        unless ret == 1\n          raise OpenSSL::SSL::Error.new(@ssl, ret, \"SSL_connect\")\n        end\n      rescue ex\n        LibSSL.ssl_free(@ssl) # GC never calls finalize, avoid mem leak\n        raise ex\n      end\n    end\n\n    def self.open(io, context : Context::Client = Context::Client.new, sync_close : Bool = false, hostname : String? = nil, &)\n      socket = new(io, context, sync_close, hostname)\n\n      begin\n        yield socket\n      ensure\n        socket.close\n      end\n    end\n\n    # Returns the `OpenSSL::X509::Certificate` the peer presented.\n    def peer_certificate : OpenSSL::X509::Certificate\n      super.not_nil!\n    end\n  end\n\n  class Server < Socket\n    def initialize(io, context : Context::Server = Context::Server.new,\n                   sync_close : Bool = false, accept : Bool = true)\n      super(io, context, sync_close)\n\n      if accept\n        begin\n          self.accept\n        rescue ex\n          LibSSL.ssl_free(@ssl) # GC never calls finalize, avoid mem leak\n          raise ex\n        end\n      end\n    end\n\n    def accept : Nil\n      ret = LibSSL.ssl_accept(@ssl)\n      unless ret == 1\n        bio.io.close if @sync_close\n        raise OpenSSL::SSL::Error.new(@ssl, ret, \"SSL_accept\")\n      end\n    end\n\n    def self.open(io, context : Context::Server = Context::Server.new, sync_close : Bool = false, &)\n      socket = new(io, context, sync_close)\n\n      begin\n        yield socket\n      ensure\n        socket.close\n      end\n    end\n  end\n\n  include IO::Buffered\n\n  # If `#sync_close?` is `true`, closing this socket will\n  # close the underlying IO.\n  property? sync_close : Bool\n\n  getter? closed : Bool\n\n  {% if compare_versions(Crystal::VERSION, \"1.12.0\") >= 0 %}\n    @bio = uninitialized ReferenceStorage(BIO)\n  {% else %}\n    @bio = uninitialized BIO\n  {% end %}\n\n  protected def initialize(io, context : Context, @sync_close : Bool = false)\n    @closed = false\n\n    @ssl = LibSSL.ssl_new(context)\n    unless @ssl\n      raise OpenSSL::Error.new(\"SSL_new\")\n    end\n\n    bio =\n      {% if compare_versions(Crystal::VERSION, \"1.12.0\") >= 0 %}\n        @bio = uninitialized ReferenceStorage(BIO)\n        BIO.unsafe_construct(pointerof(@bio), io)\n      {% else %}\n        @bio = BIO.new(io)\n      {% end %}\n\n    LibSSL.ssl_set_bio(@ssl, bio, bio)\n  end\n\n  def finalize\n    LibSSL.ssl_free(@ssl)\n  end\n\n  private def bio\n    {% if compare_versions(Crystal::VERSION, \"1.12.0\") >= 0 %}\n      @bio.to_reference\n    {% else %}\n      @bio\n    {% end %}\n  end\n\n  def unbuffered_read(slice : Bytes) : Int32\n    check_open\n\n    count = slice.size\n    return 0 if count == 0\n\n    LibSSL.ssl_read(@ssl, slice.to_unsafe, count).tap do |bytes|\n      if bytes <= 0 && !LibSSL.ssl_get_error(@ssl, bytes).zero_return?\n        ex = OpenSSL::SSL::Error.new(@ssl, bytes, \"SSL_read\")\n        if ex.underlying_eof?\n          # underlying BIO terminated gracefully, without terminating SSL aspect gracefully first\n          # some misbehaving servers \"do this\" so treat as EOF even though it's a protocol error\n          return 0\n        end\n        raise ex\n      end\n    end\n  end\n\n  def unbuffered_write(slice : Bytes) : Nil\n    check_open\n\n    return if slice.empty?\n\n    count = slice.size\n    bytes = LibSSL.ssl_write(@ssl, slice.to_unsafe, count)\n    unless bytes > 0\n      raise OpenSSL::SSL::Error.new(@ssl, bytes, \"SSL_write\")\n    end\n  end\n\n  def unbuffered_flush : Nil\n    bio.io.flush\n  end\n\n  # Returns the negotiated ALPN protocol (eg: `\"h2\"`) of `nil` if no protocol was\n  # negotiated.\n  def alpn_protocol\n    LibSSL.ssl_get0_alpn_selected(@ssl, out protocol, out len)\n    String.new(protocol, len) unless protocol.null?\n  end\n\n  def unbuffered_close : Nil\n    return if @closed\n    @closed = true\n\n    begin\n      loop do\n        ret = LibSSL.ssl_shutdown(@ssl)\n        break if ret == 1                # done bidirectional\n        break if ret == 0 && sync_close? # done unidirectional, \"this first successful call to SSL_shutdown() is sufficient\"\n        raise OpenSSL::SSL::Error.new(@ssl, ret, \"SSL_shutdown\") if ret < 0\n      rescue e : OpenSSL::SSL::Error\n        case e.error\n        when .want_read?, .want_write?\n          # Ignore, shutdown did not complete yet\n        when .syscall?\n          # OpenSSL claimed an underlying syscall failed, but that didn't set any error state,\n          # assume we're done\n          break\n        else\n          raise e\n        end\n\n        # ret == 0, retry, shutdown is not complete yet\n      end\n    rescue IO::Error\n    ensure\n      bio.io.close if @sync_close\n    end\n  end\n\n  def unbuffered_rewind : Nil\n    raise IO::Error.new(\"Can't rewind OpenSSL::SSL::Socket::Client\")\n  end\n\n  # Returns the hostname provided through Server Name Indication (SNI)\n  def hostname : String?\n    if host_name = LibSSL.ssl_get_servername(@ssl, LibSSL::TLSExt::NAMETYPE_host_name)\n      String.new(host_name)\n    end\n  end\n\n  # Returns the current cipher used by this socket.\n  def cipher : String\n    String.new(LibSSL.ssl_cipher_get_name(LibSSL.ssl_get_current_cipher(@ssl)))\n  end\n\n  # Returns the name of the TLS protocol version used by this socket.\n  def tls_version : String\n    String.new(LibSSL.ssl_get_version(@ssl))\n  end\n\n  def local_address\n    io = bio.io\n    io.responds_to?(:local_address) ? io.local_address : nil\n  end\n\n  def remote_address\n    io = bio.io\n    io.responds_to?(:remote_address) ? io.remote_address : nil\n  end\n\n  def read_timeout\n    io = bio.io\n    if io.responds_to? :read_timeout\n      io.read_timeout\n    else\n      raise NotImplementedError.new(\"#{io.class}#read_timeout\")\n    end\n  end\n\n  def read_timeout=(value)\n    io = bio.io\n    if io.responds_to? :read_timeout=\n      io.read_timeout = value\n    else\n      raise NotImplementedError.new(\"#{io.class}#read_timeout=\")\n    end\n  end\n\n  def write_timeout\n    io = bio.io\n    if io.responds_to? :write_timeout\n      io.write_timeout\n    else\n      raise NotImplementedError.new(\"#{io.class}#write_timeout\")\n    end\n  end\n\n  def write_timeout=(value)\n    io = bio.io\n    if io.responds_to? :write_timeout=\n      io.write_timeout = value\n    else\n      raise NotImplementedError.new(\"#{io.class}#write_timeout=\")\n    end\n  end\n\n  # Returns the `OpenSSL::X509::Certificate` the peer presented, if a\n  # connection was established.\n  #\n  # NOTE: Due to the protocol definition, a TLS/SSL server will always send a\n  # certificate, if present. A client will only send a certificate when\n  # explicitly requested to do so by the server (see `SSL_CTX_set_verify(3)`). If\n  # an anonymous cipher is used, no certificates are sent. That a certificate\n  # is returned does not indicate information about the verification state.\n  def peer_certificate : OpenSSL::X509::Certificate?\n    raw_cert = LibSSL.ssl_get_peer_certificate(@ssl)\n    if raw_cert\n      begin\n        OpenSSL::X509::Certificate.new(raw_cert)\n      ensure\n        LibCrypto.x509_free(raw_cert)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/x509/certificate.cr",
    "content": "require \"openssl/lib_crypto\"\nrequire \"./extension\"\nrequire \"./name\"\n\n# :nodoc:\nmodule OpenSSL::X509\n  # :nodoc:\n  class Certificate\n    def initialize\n      @cert = LibCrypto.x509_new\n      raise Error.new(\"X509_new\") if @cert.null?\n    end\n\n    def initialize(cert : LibCrypto::X509)\n      @cert = LibCrypto.x509_dup(cert)\n      raise Error.new(\"X509_dup\") if @cert.null?\n    end\n\n    def finalize\n      LibCrypto.x509_free(@cert)\n    end\n\n    def dup\n      self.class.new(@cert)\n    end\n\n    def to_unsafe\n      @cert\n    end\n\n    # Attempts to decode an ASN.1/DER-encoded certificate from *bytes*.\n    #\n    # Returns the decoded certificate and the remaining bytes on success.\n    # Returns `nil` and *bytes* unchanged on failure.\n    def self.from_der?(bytes : Bytes) : {self?, Bytes}\n      ptr = bytes.to_unsafe\n      if x509 = LibCrypto.d2i_X509(nil, pointerof(ptr), bytes.size)\n        {new(x509), bytes[ptr - bytes.to_unsafe..]}\n      else\n        {nil, bytes}\n      end\n    end\n\n    def subject : X509::Name\n      subject = LibCrypto.x509_get_subject_name(@cert)\n      raise Error.new(\"X509_get_subject_name\") if subject.null?\n      Name.new(subject)\n    end\n\n    # Sets the subject.\n    #\n    # Refer to `Name.parse` for the format.\n    def subject=(subject : String)\n      self.subject = Name.parse(subject)\n    end\n\n    def subject=(subject : Name)\n      ret = LibCrypto.x509_set_subject_name(@cert, subject)\n      raise Error.new(\"X509_set_subject_name\") if ret == 0\n      subject\n    end\n\n    def extensions : Array(X509::Extension)\n      count = LibCrypto.x509_get_ext_count(@cert)\n      Array(Extension).new(count) do |i|\n        Extension.new(LibCrypto.x509_get_ext(@cert, i))\n      end\n    end\n\n    def add_extension(extension : Extension)\n      ret = LibCrypto.x509_add_ext(@cert, extension, -1)\n      raise Error.new(\"X509_add_ext\") if ret.null?\n      extension\n    end\n\n    # Returns the name of the signature algorithm.\n    def signature_algorithm : String\n      sigid = LibCrypto.x509_get_signature_nid(@cert)\n      result = LibCrypto.obj_find_sigid_algs(sigid, out algo_nid, nil)\n      raise \"Could not determine certificate signature algorithm\" if result == 0\n\n      sn = LibCrypto.obj_nid2sn(algo_nid)\n      raise \"Unknown algo NID #{algo_nid.inspect}\" if sn.null?\n      String.new sn\n    end\n\n    # Returns the digest of the certificate using *algorithm_name*\n    #\n    # ```\n    # cert = OpenSSL::X509::Certificate.new\n    # cert.digest(\"SHA1\").hexstring   # => \"6f608752059150c9b3450a9fe0a0716b4f3fa0ca\"\n    # cert.digest(\"SHA256\").hexstring # => \"51d80c865cc717f181cd949f0b23b5e1e82c93e01db53f0836443ec908b83748\"\n    # ```\n    def digest(algorithm_name : String) : Bytes\n      algo_type = LibCrypto.evp_get_digestbyname algorithm_name\n      raise ArgumentError.new \"Could not find digest for #{algorithm_name.inspect}\" if algo_type.null?\n      hash = Bytes.new(64) # EVP_MAX_MD_SIZE for SHA512\n      result = LibCrypto.x509_digest(@cert, algo_type, hash, out size)\n      raise Error.new \"Could not generate certificate hash\" unless result == 1\n\n      hash[0, size]\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/x509/extension.cr",
    "content": "require \"openssl/lib_crypto\"\n\n# :nodoc:\nmodule OpenSSL::X509\n  # :nodoc:\n  class Extension\n    def self.new(oid : String, value : String, critical = false)\n      nid = LibCrypto.obj_ln2nid(oid)\n      nid = LibCrypto.obj_sn2nid(oid) if nid == LibCrypto::NID_undef\n      raise Error.new(\"OBJ_sn2nid\") if nid == LibCrypto::NID_undef\n      new(nid, value, critical)\n    end\n\n    def initialize(nid : Int32, value : String, critical = false)\n      valstr = String.build do |str|\n        str << \"critical,\" if critical\n        str << value\n      end\n      @ext = LibCrypto.x509v3_ext_nconf_nid(nil, nil, nid, valstr)\n      raise Error.new(\"X509V3_EXT_nconf_nid\") if @ext.null?\n    end\n\n    def initialize(ext : LibCrypto::X509_EXTENSION)\n      @ext = LibCrypto.x509_extension_dup(ext)\n      raise Error.new(\"X509_EXTENSION_dup\") if @ext.null?\n    end\n\n    def finalize\n      LibCrypto.x509_extension_free(@ext)\n    end\n\n    def dup\n      self.class.new(@ext)\n    end\n\n    def to_unsafe\n      @ext\n    end\n\n    def nid\n      obj = LibCrypto.x509_extension_get_object(@ext)\n      LibCrypto.obj_obj2nid(obj)\n    end\n\n    def oid : String\n      obj = LibCrypto.x509_extension_get_object(@ext)\n      LibCrypto.obj_obj2nid(obj)\n\n      if (nid = LibCrypto.obj_obj2nid(obj)) == LibCrypto::NID_undef\n        buf = Bytes.new(512)\n        LibCrypto.i2t_asn1_object(buf, buf.size, obj)\n      else\n        buf = LibCrypto.obj_nid2sn(nid)\n      end\n\n      String.new(buf)\n    end\n\n    def value : String\n      bio = OpenSSL::BIO.new(io = IO::Memory.new)\n\n      if LibCrypto.x509v3_ext_print(bio, @ext, 0, 0) == 0\n        LibCrypto.asn1_string_print(bio, LibCrypto.x509_extension_get_data(@ext))\n      end\n\n      io.to_s\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/x509/name.cr",
    "content": "require \"openssl/lib_crypto\"\n\n# :nodoc:\nmodule OpenSSL::X509\n  # :nodoc:\n  class Name\n    # Parses entries in string and initializes a Name object.\n    #\n    # Example:\n    # ```\n    # require \"openssl\"\n    #\n    # OpenSSL::X509::Name.parse(\"CN=nobody/DC=example\")\n    # ```\n    def self.parse(string : String) : Name\n      new.tap do |name|\n        string.split('/') do |entry|\n          oid, _, value = entry.partition('=')\n          name.add_entry(oid, value)\n        end\n      end\n    end\n\n    def initialize\n      @name = LibCrypto.x509_name_new\n      raise Error.new(\"X509_NAME_new\") if @name.null?\n    end\n\n    def initialize(name : LibCrypto::X509_NAME)\n      @name = LibCrypto.x509_name_dup(name)\n      raise Error.new(\"X509_NAME_dup\") if @name.null?\n    end\n\n    def finalize\n      LibCrypto.x509_name_free(@name)\n    end\n\n    def dup\n      self.class.new(@name)\n    end\n\n    def to_unsafe\n      @name\n    end\n\n    # Adds a new entry.\n    #\n    # Example:\n    # ```\n    # name = OpenSSL::X509::Name.new\n    # name.add_entry \"CN\", \"Nobody\"\n    # name.add_entry \"DC\", \"example\"\n    # ```\n    def add_entry(oid : String, value : String) : Nil\n      type = LibCrypto::MBSTRING_UTF8\n      ret = LibCrypto.x509_name_add_entry_by_txt(@name, oid, type, value, value.bytesize, -1, 0)\n      raise Error.new(\"X509_NAME_add_entry_by_txt\") if ret.null?\n    end\n\n    # Returns entries as an Array of oid, value pairs.\n    #\n    # Example:\n    # ```\n    # name = OpenSSL::X509::Name.parse(\"CN=Nobody/DC=example\")\n    # name.to_a # => [{\"CN\", \"Nobody\"}, {\"DC\", \"example\"}]\n    # ```\n    def to_a : Array({String, String})\n      count = LibCrypto.x509_name_entry_count(@name)\n      raise Error.new(\"X509_NAME_entry_count\") if count < 0\n\n      long_name = Bytes.new(512)\n\n      Array(Tuple(String, String)).new(count) do |i|\n        entry = LibCrypto.x509_name_get_entry(@name, i)\n        raise Error.new(\"X509_NAME_get_entry\") if entry.null?\n\n        obj = LibCrypto.x509_name_entry_get_object(entry)\n        LibCrypto.i2t_asn1_object(long_name, long_name.size, obj)\n\n        nid = LibCrypto.obj_ln2nid(long_name)\n        if nid == LibCrypto::NID_undef\n          oid = String.new(long_name)\n        else\n          short_name = LibCrypto.obj_nid2sn(nid)\n          oid = String.new(short_name)\n        end\n\n        asn1 = LibCrypto.x509_name_entry_get_data(entry)\n        str = LibCrypto.asn1_string_data(asn1)\n        str_len = LibCrypto.asn1_string_length(asn1)\n\n        {oid, String.new(str, str_len)}\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/openssl/x509/x509.cr",
    "content": "require \"./*\"\n"
  },
  {
    "path": "src/openssl.cr",
    "content": "require \"./openssl/lib_ssl\"\nrequire \"./openssl/error\"\n\n# The OpenSSL module allows for access to Secure Sockets Layer (SSL) and Transport Layer Security (TLS)\n# encryption, as well as classes for encrypting data, decrypting data, and computing hashes. It uses\n# the SSL library provided by the operating system, which may be either [OpenSSL](https://openssl.org)\n# or [LibreSSL](https://www.libressl.org).\n#\n# WARNING: This module should not be used without first reading the [Security considerations](#security-considerations).\n#\n# To create secure sockets, use either `OpenSSL::SSL::Socket::Client` for client applications, or\n# `OpenSSL::SSL::Socket::Server` for servers.  These classes use a default context, but you can provide\n# your own by supplying an `OpenSSL::SSL::Context`.  For more control, consider subclassing `OpenSSL::SSL::Socket`.\n#\n# Hashing algorithms are provided by classes such as `Digest::SHA256` and `Digest::MD5`.  If you need\n# a different option, you can initialize one using the name of the digest with `OpenSSL::Digest`.\n# A Hash-based Message Authentication Code (HMAC) can be computed using `HMAC` and specifying a digest\n# `Algorithm`.\n#\n# The `OpenSSL::Cipher` class can be used for encrypting and decrypting data.\n#\n# NOTE: To use `OpenSSL`, you must explicitly import it using the `require \"openssl\"` statement.\n#\n# ## Security Considerations\n#\n# Crystal aims to provide reasonable configuration defaults in accordance with\n# [Mozilla's recommendations](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29).\n# However, these defaults may not be suitable for your application.  It is recommended that you refer\n# to the Open Worldwide Application Security Project (OWASP) cheat sheet on\n# [implementing transport layer protection](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html)\n# to ensure the appropriate configuration for your use.\n#\n# If you come across any shortcomings or spots for improvement in Crystal's configuration options,\n# please don't hesitate to let us know by [opening an issue](https://github.com/crystal-lang/crystal/issues/new).\n#\n# ## Usage Example\n#\n# ### Server side\n#\n# An `SSL` server is created with a `TCPServer` and a `SSL::Context`.  You can then use the\n# SSL server like an ordinary TCP server.\n#\n# NOTE: For the below example to work, a certificate and private key should be attained.\n#\n# ```\n# require \"openssl\"\n# require \"socket\"\n#\n# PORT = ENV[\"PORT\"] ||= \"5555\"\n#\n# # Bind new TCPServer to PORT\n# socket = TCPServer.new(PORT.to_i)\n#\n# context = OpenSSL::SSL::Context::Server.new\n# context.private_key = \"/path/to/private.key\"\n# context.certificate_chain = \"/path/to/public.cert\"\n#\n# puts \"Server is up. Listening on port #{PORT}.\"\n#\n# socket.accept do |client|\n#   puts \"Got client\"\n#\n#   bytes = Bytes.new(20)\n#\n#   OpenSSL::SSL::Socket::Server.open(client, context) do |ssl_socket|\n#     ssl_socket.read(bytes)\n#   end\n#\n#   puts \"Client said: #{String.new(bytes)}\"\n# end\n#\n# socket.close\n# puts \"Server has stopped.\"\n# ```\n#\n# ### Client side\n#\n# An `SSL` client is created with a `TCPSocket` and a `SSL::Context`. Unlike a SSL server,\n# a client does not require a certificate or private key.\n#\n# NOTE: By default, closing an `SSL::Socket` does not close the underlying socket.  You need to\n#       set `SSL::Socket#sync_close=` to true if you want this behaviour.\n#\n# ```\n# require \"openssl\"\n# require \"socket\"\n#\n# PORT = ENV[\"PORT\"] ||= \"5555\"\n#\n# # Bind TCPSocket to PORT and open a connection\n# TCPSocket.open(\"127.0.0.1\", PORT) do |socket|\n#   context = OpenSSL::SSL::Context::Client.new\n#\n#   OpenSSL::SSL::Socket::Client.open(socket, context) do |ssl_socket|\n#     ssl_socket << \"Hello from client!\"\n#   end\n# end\n# ```\nmodule OpenSSL\n  module SSL\n    alias Modes = LibSSL::Modes\n    alias Options = LibSSL::Options\n    alias VerifyMode = LibSSL::VerifyMode\n    alias ErrorType = LibSSL::SSLError\n    {% if LibCrypto.has_constant?(:X509VerifyFlags) %}\n      alias X509VerifyFlags = LibCrypto::X509VerifyFlags\n    {% end %}\n\n    class Error < OpenSSL::Error\n      getter error : ErrorType\n      getter? underlying_eof : Bool = false\n\n      def initialize(ssl : LibSSL::SSL, return_code : LibSSL::Int, func = nil)\n        @error = LibSSL.ssl_get_error(ssl, return_code)\n\n        case @error\n        when .none?\n          message = \"Raised erroneously\"\n        when .syscall?\n          @code, message = fetch_error_details\n          {% if LibSSL.has_constant?(:SSL_R_UNEXPECTED_EOF_WHILE_READING) %}\n            if @code == 0\n              # FIXME: this isn't a per the OpenSSL documentation for how to\n              #        detect EOF, but this fixes the EOF detection spec...\n              message = \"Unexpected EOF while reading\"\n              @underlying_eof = true\n            else\n              cause = RuntimeError.from_errno(func || \"OpenSSL\")\n            end\n          {% else %}\n            case return_code\n            when 0\n              message = \"Unexpected EOF while reading\"\n              @underlying_eof = true\n            when -1\n              cause = RuntimeError.from_errno(func || \"OpenSSL\")\n              message = \"I/O error\"\n            else\n              message = \"Unknown error\"\n            end\n          {% end %}\n        when .ssl?\n          code, message = fetch_error_details\n          @code = code\n          {% if LibSSL.has_constant?(:SSL_R_UNEXPECTED_EOF_WHILE_READING) %}\n            if get_reason(code) == LibSSL::SSL_R_UNEXPECTED_EOF_WHILE_READING\n              @underlying_eof = true\n            end\n          {% end %}\n        else\n          message = @error.to_s\n        end\n\n        super(func ? \"#{func}: #{message}\" : message, true, cause: cause)\n      end\n    end\n  end\nend\n\nrequire \"./openssl/bio\"\nrequire \"./openssl/ssl/*\"\nrequire \"./openssl/digest\"\nrequire \"./openssl/md5\"\nrequire \"./openssl/x509/x509\"\nrequire \"./openssl/pkcs5\"\nrequire \"./openssl/cipher\"\n"
  },
  {
    "path": "src/option_parser.cr",
    "content": "# `OptionParser` is a class for command-line options processing. It supports:\n#\n# * Short and long modifier style options (example: `-h`, `--help`)\n# * Passing arguments to the flags (example: `-f filename.txt`)\n# * Subcommands\n# * Automatic help message generation\n#\n# Run `crystal` for an example of a CLI built with `OptionParser`.\n#\n# NOTE: To use `OptionParser`, you must explicitly import it with `require \"option_parser\"`\n#\n# Short example:\n#\n# ```\n# require \"option_parser\"\n#\n# upcase = false\n# destination = \"World\"\n#\n# OptionParser.parse do |parser|\n#   parser.banner = \"Usage: salute [arguments]\"\n#   parser.on(\"-u\", \"--upcase\", \"Upcases the salute\") { upcase = true }\n#   parser.on(\"-t NAME\", \"--to=NAME\", \"Specifies the name to salute\") { |name| destination = name }\n#   parser.on(\"-h\", \"--help\", \"Show this help\") do\n#     puts parser\n#     exit\n#   end\n#   parser.invalid_option do |flag|\n#     STDERR.puts \"ERROR: #{flag} is not a valid option.\"\n#     STDERR.puts parser\n#     exit(1)\n#   end\n# end\n#\n# destination = destination.upcase if upcase\n# puts \"Hello #{destination}!\"\n# ```\n#\n# # Subcommands\n#\n# `OptionParser` also supports subcommands.\n#\n# Short example:\n#\n# ```\n# require \"option_parser\"\n#\n# verbose = false\n# salute = false\n# welcome = false\n# name = \"World\"\n# parser = OptionParser.new do |parser|\n#   parser.banner = \"Usage: example [subcommand] [arguments]\"\n#   parser.on(\"salute\", \"Salute a name\") do\n#     salute = true\n#     parser.banner = \"Usage: example salute [arguments]\"\n#     parser.on(\"-t NAME\", \"--to=NAME\", \"Specify the name to salute\") { |_name| name = _name }\n#   end\n#   parser.on(\"welcome\", \"Print a greeting message\") do\n#     welcome = true\n#     parser.banner = \"Usage: example welcome\"\n#   end\n#   parser.on(\"-v\", \"--verbose\", \"Enabled verbose output\") { verbose = true }\n#   parser.on(\"-h\", \"--help\", \"Show this help\") do\n#     puts parser\n#     exit\n#   end\n# end\n#\n# parser.parse\n#\n# if salute\n#   STDERR.puts \"Saluting #{name}\" if verbose\n#   puts \"Hello #{name}\"\n# elsif welcome\n#   STDERR.puts \"Welcoming #{name}\" if verbose\n#   puts \"Welcome!\"\n# else\n#   puts parser\n#   exit(1)\n# end\n# ```\nclass OptionParser\n  class Exception < ::Exception\n  end\n\n  class InvalidOption < Exception\n    def initialize(option)\n      super(\"Invalid option: #{option}\")\n    end\n  end\n\n  class MissingOption < Exception\n    def initialize(option)\n      super(\"Missing option: #{option}\")\n    end\n  end\n\n  # :nodoc:\n  enum FlagValue\n    Required\n    Optional\n    None\n  end\n\n  # :nodoc:\n  record Handler,\n    value_type : FlagValue,\n    block : String ->\n\n  # Creates a new parser, with its configuration specified in the block,\n  # and uses it to parse the passed *args* (defaults to `ARGV`).\n  #\n  # Refer to `#gnu_optional_args?` for the behaviour of the named parameter.\n  def self.parse(args = ARGV, *, gnu_optional_args : Bool = false, &) : self\n    parser = OptionParser.new(gnu_optional_args: gnu_optional_args)\n    yield parser\n    parser.parse(args)\n    parser\n  end\n\n  # Creates a new parser.\n  #\n  # Refer to `#gnu_optional_args?` for the behaviour of the named parameter.\n  def initialize(*, @gnu_optional_args : Bool = false)\n    @flags = [] of String\n    @handlers = Hash(String, Handler).new\n    @stop = false\n    @missing_option = ->(option : String) { raise MissingOption.new(option) }\n    @invalid_option = ->(option : String) { raise InvalidOption.new(option) }\n  end\n\n  # Creates a new parser, with its configuration specified in the block.\n  #\n  # Refer to `#gnu_optional_args?` for the behaviour of the named parameter.\n  def self.new(*, gnu_optional_args : Bool = false, &)\n    new(gnu_optional_args: gnu_optional_args).tap { |parser| yield parser }\n  end\n\n  # Returns whether the GNU convention is followed for optional arguments.\n  #\n  # If true, any optional argument must follow the preceding flag in the same\n  # token immediately, without any space inbetween:\n  #\n  # ```\n  # require \"option_parser\"\n  #\n  # OptionParser.parse(%w(-a1 -a 2 -a --b=3 --b 4), gnu_optional_args: true) do |parser|\n  #   parser.on(\"-a\", \"--b [x]\", \"optional\") { |x| p x }\n  #   parser.unknown_args { |args, _| puts \"Remaining: #{args}\" }\n  # end\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # \"1\"\n  # \"\"\n  # \"\"\n  # \"3\"\n  # \"\"\n  # Remaining: [\"2\", \"4\"]\n  # ```\n  #\n  # Without `gnu_optional_args: true`, prints the following instead:\n  #\n  # ```text\n  # \"1\"\n  # \"2\"\n  # \"--b=3\"\n  # \"4\"\n  # Remaining: []\n  # ```\n  property? gnu_optional_args : Bool\n\n  # Establishes the initial message for the help printout.\n  # Typically, you want to write here the name of your program,\n  # and a one-line template of its invocation.\n  #\n  # Example:\n  #\n  # ```\n  # require \"option_parser\"\n  #\n  # parser = OptionParser.new\n  # parser.banner = \"Usage: crystal [command] [switches] [program file] [--] [arguments]\"\n  # ```\n  setter banner : String?\n\n  # Establishes a handler for a *flag* or subcommand.\n  #\n  # Flags must start with a dash or double dash. They can also have\n  # an optional argument, which will get passed to the block.\n  # Each flag has a description, which will be used for the help message.\n  #\n  # Subcommands are any *flag* passed which does not start with a dash. They\n  # cannot take arguments. When a subcommand is parsed, all subcommands are\n  # removed from the OptionParser, simulating a \"tree\" of subcommands. All flags\n  # remain valid. For a longer example, see the examples at the top of the page.\n  #\n  # Examples of valid flags:\n  #\n  # * `-a`, `-B`\n  # * `--something-longer`\n  # * `-f FILE`, `--file FILE`, `--file=FILE` (these will yield the passed value to the block as a string)\n  #\n  # Examples of valid subcommands:\n  #\n  # * `foo`, `run`\n  def on(flag : String, description : String, &block : String ->)\n    append_flag flag, description\n\n    flag, value_type = parse_flag_definition(flag)\n    @handlers[flag] = Handler.new(value_type, block)\n  end\n\n  # Establishes a handler for a pair of short and long flags.\n  #\n  # See the other definition of `on` for examples. This method does not support\n  # subcommands.\n  def on(short_flag : String, long_flag : String, description : String, &block : String ->)\n    check_starts_with_dash short_flag, \"short_flag\", allow_empty: true\n    check_starts_with_dash long_flag, \"long_flag\"\n\n    append_flag \"#{short_flag}, #{long_flag}\", description\n\n    short_flag, short_value_type = parse_flag_definition(short_flag)\n    long_flag, long_value_type = parse_flag_definition(long_flag)\n\n    # Pick the \"most required\" argument type between both flags\n    if short_value_type.required? || long_value_type.required?\n      value_type = FlagValue::Required\n    elsif short_value_type.optional? || long_value_type.optional?\n      value_type = FlagValue::Optional\n    else\n      value_type = FlagValue::None\n    end\n\n    handler = Handler.new(value_type, block)\n    @handlers[short_flag] = @handlers[long_flag] = handler\n  end\n\n  private def parse_flag_definition(flag : String)\n    case flag\n    when /\\A--(\\S+)\\s+\\[\\S+\\]\\z/\n      {\"--#{$1}\", FlagValue::Optional}\n    when /\\A--(\\S+)(\\s+|\\=)(\\S+)?\\z/\n      {\"--#{$1}\", FlagValue::Required}\n    when /\\A--\\S+\\z/\n      # This can't be merged with `else` otherwise /-(.)/ matches\n      {flag, FlagValue::None}\n    when /\\A-(.)\\s*\\[\\S+\\]\\z/\n      {flag[0..1], FlagValue::Optional}\n    when /\\A-(.)\\s+\\S+\\z/, /\\A-(.)\\s+\\z/, /\\A-(.)\\S+\\z/\n      {flag[0..1], FlagValue::Required}\n    else\n      # This happens for -f without argument\n      {flag, FlagValue::None}\n    end\n  end\n\n  # Adds a separator, with an optional header message, that will be used to\n  # print the help. The separator is placed between the flags registered (`#on`)\n  # before, and the flags registered after the call.\n  #\n  # This way, you can group the different options in an easier to read way.\n  def separator(message = \"\") : Nil\n    @flags << message.to_s\n  end\n\n  # Sets a handler for regular arguments that didn't match any of the setup options.\n  #\n  # You typically use this to get the main arguments (not modifiers)\n  # that your program expects (for example, filenames). The default behaviour\n  # is to do nothing. The arguments can also be extracted from the *args* array\n  # passed to `#parse` after parsing.\n  def unknown_args(&@unknown_args : Array(String), Array(String) ->)\n  end\n\n  # Sets a handler for when a option that expects an argument wasn't given any.\n  #\n  # You typically use this to display a help message.\n  # The default behaviour is to raise `MissingOption`.\n  def missing_option(&@missing_option : String ->)\n  end\n\n  # Sets a handler for option arguments that didn't match any of the setup options.\n  #\n  # You typically use this to display a help message.\n  # The default behaviour is to raise `InvalidOption`.\n  def invalid_option(&@invalid_option : String ->)\n  end\n\n  # Sets a handler which runs before each argument is parsed. This callback is\n  # not passed flag arguments. For example, `--foo=foo_arg --bar bar_arg` would\n  # pass `--foo=foo_arg` and `--bar` to the callback only.\n  #\n  # You typically use this to implement advanced option parsing behaviour such\n  # as treating all options after a filename differently (along with `#stop`).\n  def before_each(&@before_each : String ->)\n  end\n\n  # Stops the current parse and returns immediately, leaving the remaining flags\n  # unparsed. This is treated identically to `--` being inserted *behind* the\n  # current parsed flag.\n  def stop : Nil\n    @stop = true\n  end\n\n  # Returns all the setup options, formatted in a help message.\n  def to_s(io : IO) : Nil\n    if banner = @banner\n      io << banner\n      io << '\\n'\n    end\n    @flags.join io, '\\n'\n  end\n\n  # Width for option list portion of summary.\n  property summary_width : Int32 = 32\n\n  def summary_width=(width : Int32)\n    raise ArgumentError.new(\"Negative summary width: #{width}\") if width < 0\n    @summary_width = width\n  end\n\n  # Indentation for summary.\n  property summary_indent : String = \"    \"\n\n  private def append_flag(flag, description)\n    description_indent = \"#{summary_indent}#{\" \" * summary_width} \"\n    description = description.gsub(\"\\n\", \"\\n#{description_indent}\")\n\n    if flag.size >= summary_width\n      @flags << \"#{summary_indent}#{flag}\\n#{description_indent}#{description}\"\n    else\n      @flags << \"#{summary_indent}#{flag}#{\" \" * (summary_width - flag.size)} #{description}\"\n    end\n  end\n\n  private def check_starts_with_dash(arg, name, allow_empty = false)\n    return if allow_empty && arg.empty?\n\n    unless arg.starts_with?('-')\n      raise ArgumentError.new(\"Argument '#{name}' (#{arg.inspect}) must start with a dash (-)\")\n    end\n  end\n\n  private def with_preserved_state(&)\n    old_flags = @flags.clone\n    old_handlers = @handlers.clone\n    old_banner = @banner\n    old_unknown_args = @unknown_args\n    old_missing_option = @missing_option\n    old_invalid_option = @invalid_option\n    old_before_each = @before_each\n    old_summary_width = @summary_width\n    old_summary_indent = @summary_indent\n\n    begin\n      yield\n    ensure\n      @flags = old_flags\n      @handlers = old_handlers\n      @stop = false\n      @banner = old_banner\n      @unknown_args = old_unknown_args\n      @missing_option = old_missing_option\n      @invalid_option = old_invalid_option\n      @before_each = old_before_each\n      @summary_width = old_summary_width\n      @summary_indent = old_summary_indent\n    end\n  end\n\n  # Parses the passed *args* (defaults to `ARGV`), running the handlers associated to each option.\n  def parse(args = ARGV) : Nil\n    with_preserved_state do\n      # List of indexes in `args` which have been handled and must be deleted\n      handled_args = [] of Int32\n      double_dash_index = nil\n\n      arg_index = 0\n      while arg_index < args.size\n        arg = args[arg_index]\n\n        if @stop\n          double_dash_index = arg_index - 1\n          @stop = false\n          break\n        end\n\n        if before_each = @before_each\n          before_each.call(arg)\n        end\n\n        # -- means to stop parsing arguments\n        if arg == \"--\"\n          double_dash_index = arg_index\n          handled_args << arg_index\n          break\n        end\n\n        flag, value = parse_arg_to_flag_and_value(arg)\n\n        arg_index = handle_flag(flag, value, arg_index, args, handled_args)\n\n        arg_index += 1\n      end\n\n      # We're about to delete all the unhandled arguments in args so double_dash_index\n      # is about to change. Arguments are only handled before \"--\", so we're deleting\n      # nothing after \"--\", which means it's index is decremented by handled_args.size.\n      # But actually we also added \"--\" itself to handled_args so we change it's index\n      # by one less.\n      if double_dash_index\n        double_dash_index -= handled_args.size - 1\n      end\n\n      # After argument parsing, delete handled arguments from args.\n      remove_handled_args(args, handled_args)\n\n      # Since we've deleted all handled arguments, `args` is all unknown arguments\n      # which we split by the index of any double dash argument\n      if unknown_args = @unknown_args\n        if double_dash_index\n          before_dash = args[0...double_dash_index]\n          after_dash = args[double_dash_index..-1]\n        else\n          before_dash = args\n          after_dash = [] of String\n        end\n        unknown_args.call(before_dash, after_dash)\n      end\n\n      # We consider any remaining arguments which start with '-' to be invalid\n      args.each_with_index do |arg, index|\n        break if double_dash_index && index >= double_dash_index\n\n        if arg.starts_with?('-') && arg != \"-\"\n          @invalid_option.call(arg)\n        end\n      end\n    end\n  end\n\n  # Parses a command-line argument into a flag and optional inline value.\n  private def parse_arg_to_flag_and_value(arg : String) : {String, String?}\n    if arg.starts_with?(\"--\")\n      name, separator, value = arg.partition(\"=\")\n      if separator == \"=\"\n        return {name, value}\n      end\n    elsif arg.starts_with?('-') && arg.size > 2\n      return {arg[0..1], arg[2..]}\n    end\n    {arg, nil}\n  end\n\n  # Processes a single flag/subcommand. Matches original behaviour exactly.\n  private def handle_flag(flag : String, value : String?, arg_index : Int32, args : Array(String), handled_args : Array(Int32)) : Int32\n    return arg_index unless handler = @handlers[flag]?\n    return arg_index if handler.value_type.none? && value\n\n    handled_args << arg_index\n\n    if !value\n      case handler.value_type\n      in FlagValue::Required\n        value = args[arg_index + 1]?\n        if value\n          handled_args << arg_index + 1\n          arg_index += 1\n        else\n          @missing_option.call(flag)\n        end\n      in FlagValue::Optional\n        unless gnu_optional_args?\n          value = args[arg_index + 1]?\n          if value && !@handlers.has_key?(value)\n            handled_args << arg_index + 1\n            arg_index += 1\n          else\n            value = nil\n          end\n        end\n      in FlagValue::None\n        # do nothing\n      end\n    end\n\n    # If this is a subcommand (flag not starting with -), delete all\n    # subcommands since they are no longer valid.\n    unless flag.starts_with?('-')\n      @handlers.select! { |k, _| k.starts_with?('-') }\n      @flags.select!(&.starts_with?(\"#{summary_indent}-\"))\n    end\n\n    handler.block.call(value || \"\")\n\n    arg_index\n  end\n\n  # Removes handled arguments from the args array based on handled_args indexes.\n  private def remove_handled_args(args : Array(String), handled_args : Array(Int32)) : Nil\n    # After argument parsing, delete handled arguments from args.\n    # We reverse so that we delete args from the end\n    handled_args.reverse!\n    i = 0\n    args.reject! do\n      # handled_args is sorted in reverse so we know that i <= handled_args.last\n      handled = i == handled_args.last?\n\n      # Maintain the i <= handled_args.last invariant\n      handled_args.pop if handled\n\n      i += 1\n\n      handled\n    end\n  end\nend\n"
  },
  {
    "path": "src/path.cr",
    "content": "require \"crystal/system/path\"\n\n# A `Path` represents a filesystem path and allows path-handling operations\n# such as querying its components as well as semantic manipulations.\n#\n# A path is hierarchical and composed of a sequence of directory and file name\n# elements separated by a special separator or delimiter. A root component,\n# that identifies a file system hierarchy, may also be present.\n# The name element that is farthest from the root of the directory hierarchy is\n# the name of a file or directory. The other name elements are directory names.\n# A `Path` can represent a root, a root and a sequence of names, or simply one or\n# more name elements.\n# A `Path` is considered to be an empty path if it consists solely of one name\n# element that is empty or equal to `\".\"`. Accessing a file using an empty path\n# is equivalent to accessing the default directory of the process.\n#\n# # Examples\n#\n# ```\n# Path[\"foo/bar/baz.cr\"].parent    # => Path[\"foo/bar\"]\n# Path[\"foo/bar/baz.cr\"].basename  # => \"baz.cr\"\n# Path[\"./foo/../bar\"].normalize   # => Path[\"bar\"]\n# Path[\"~/bin\"].expand(home: true) # => Path[\"/home/crystal/bin\"]\n# ```\n#\n# For now, its methods are purely lexical, there is no direct filesystem access.\n#\n# Path handling comes in different kinds depending on operating system:\n#\n# * `Path.posix()` creates a new POSIX path\n# * `Path.windows()` creates a new Windows path\n# * `Path.new()` means `Path.posix` on POSIX platforms and `Path.windows()`\n#    on Windows platforms.\n#\n# ```\n# # On POSIX system:\n# Path.new(\"foo\", \"bar\", \"baz.cr\") == Path.posix(\"foo/bar/baz.cr\")\n# # On Windows system:\n# Path.new(\"foo\", \"bar\", \"baz.cr\") == Path.windows(\"foo\\\\bar\\\\baz.cr\")\n# ```\n#\n# The main differences between Windows and POSIX paths:\n# * POSIX paths use forward slash (`/`) as only path separator, Windows paths use\n#   backslash (`\\`) as default separator but also recognize forward slashes.\n# * POSIX paths are generally case-sensitive, Windows paths case-insensitive\n#   (see `#<=>`).\n# * A POSIX path is absolute if it begins with a forward slash (`/`). A Windows path\n#   is absolute if it starts with both a drive and a root (see `#absolute?`).\n#\n# ```\n# Path.posix(\"/foo/./bar\").normalize   # => Path.posix(\"/foo/bar\")\n# Path.windows(\"/foo/./bar\").normalize # => Path.windows(\"\\\\foo\\\\bar\")\n#\n# Path.posix(\"/foo\").absolute?   # => true\n# Path.windows(\"/foo\").absolute? # => false\n#\n# Path.posix(\"foo\") == Path.posix(\"FOO\")     # => false\n# Path.windows(\"foo\") == Path.windows(\"FOO\") # => true\n# ```\nstruct Path\n  include Comparable(Path)\n\n  class Error < Exception\n  end\n\n  enum Kind : UInt8\n    # TODO: Consider adding NATIVE member, see https://github.com/crystal-lang/crystal/pull/5635#issuecomment-441237811\n\n    POSIX\n    WINDOWS\n\n    def self.native : Kind\n      {% if flag?(:win32) %}\n        WINDOWS\n      {% else %}\n        POSIX\n      {% end %}\n    end\n  end\n\n  # The file/directory separator characters of the current platform.\n  # `{'/'}` on POSIX, `{'\\\\', '/'}` on Windows.\n  SEPARATORS = separators(Kind.native)\n\n  # :nodoc:\n  def self.separators(kind)\n    if kind.windows?\n      {'\\\\', '/'}\n    else\n      {'/'}\n    end\n  end\n\n  # Creates a new `Path` of native kind.\n  #\n  # When compiling for a windows target, this is equal to `Path.windows()`,\n  # otherwise `Path.posix` is used.\n  def self.new(name : String = \"\") : Path\n    new(name.check_no_null_byte, Kind.native)\n  end\n\n  # :ditto:\n  def self.new(path : Path) : Path\n    path.to_native\n  end\n\n  # :ditto:\n  def self.new(name : String | Path, *parts : String | Path) : Path\n    new(name).join(*parts)\n  end\n\n  # :ditto:\n  def self.[](name : String | Path, *parts) : Path\n    new(name, *parts)\n  end\n\n  # :ditto:\n  def self.new(parts : Enumerable) : Path\n    new(\"\").join(parts)\n  end\n\n  # :ditto:\n  def self.[](parts : Enumerable) : Path\n    new(parts)\n  end\n\n  # Creates a new `Path` of POSIX kind.\n  def self.posix(name : String = \"\") : Path\n    new(name.check_no_null_byte, Kind::POSIX)\n  end\n\n  # :ditto:\n  def self.posix(path : Path) : Path\n    path.to_posix\n  end\n\n  # :ditto:\n  def self.posix(name : String | Path, *parts : String | Path) : Path\n    posix(name).join(parts)\n  end\n\n  # :ditto:\n  def self.posix(parts : Enumerable) : Path\n    posix(\"\").join(parts)\n  end\n\n  # Creates a new `Path` of Windows kind.\n  def self.windows(name : String = \"\") : Path\n    new(name.check_no_null_byte, Kind::WINDOWS)\n  end\n\n  # :ditto:\n  def self.windows(path : Path) : Path\n    path.to_windows\n  end\n\n  # :ditto:\n  def self.windows(name : String | Path, *parts : String | Path) : Path\n    windows(name).join(parts)\n  end\n\n  # :ditto:\n  def self.windows(parts : Enumerable) : Path\n    windows(\"\").join(parts)\n  end\n\n  # :nodoc:\n  protected def initialize(@name : String, @kind : Kind)\n  end\n\n  # Internal helper method to create a new `Path` of the same kind as `self`.\n  private def new_instance(string : String, kind = @kind) : Path\n    Path.new(string, kind)\n  end\n\n  # Returns `true` if this is a Windows path.\n  def windows? : Bool\n    @kind.windows?\n  end\n\n  # Returns `true` if this is a POSIX path.\n  def posix? : Bool\n    @kind.posix?\n  end\n\n  # Returns `true` if this is a native path for the target platform.\n  def native? : Bool\n    @kind == Kind.native\n  end\n\n  # Returns all components of this path except the last one.\n  #\n  # ```\n  # Path[\"/foo/bar/file.cr\"].dirname # => \"/foo/bar\"\n  # ```\n  def dirname : String\n    return \".\" if @name.empty?\n    slice = @name.to_slice\n    sep = self.separators.map &.ord\n    pos = slice.size - 1\n    stage = 0\n\n    slice.reverse_each do |byte|\n      is_separator = byte.in? sep\n      # The stages are ordered like this to improve performance\n      # Trailing separators are possible but unlikely (stage 0)\n      # There will probably only be one separator between filename and dirname (stage 2)\n      # There will probably be multiple characters in the filename which need to be skipped (stage 1)\n      case stage\n      when 1 # Wait until separator\n        stage += 1 if is_separator\n      when 2 # Remove trailing separators\n        break unless is_separator\n      when 0 # Wait until past trailing separators\n        stage += 1 unless is_separator\n      end\n      pos -= 1\n    end\n\n    case stage\n    when 0 # Path only consists of separators\n      String.new(slice[0, 1])\n    when 1 # Path has no parent (ex. \"hello/\", \"C:/\", \"crystal\")\n      return anchor.to_s if windows? && (windows_drive? || dos_local_device_path?)\n      \".\"\n    else # Path has a parent (ex. \"a/a\", \"/home/user//\", \"C://Users/mmm\", \"\\\\wsl.localhost\\Debian\")\n      if windows? && (anchor = self.anchor) && pos < anchor.to_s.bytesize\n        anchor.to_s\n      else\n        @name.byte_slice(0, {pos, 0}.max + 1)\n      end\n    end\n  end\n\n  # Returns the parent path of this path.\n  #\n  # If the path is empty, it returns `\".\"`. If the path is rooted\n  # and in the top-most hierarchy, the root path is returned.\n  #\n  # ```\n  # Path[\"foo/bar/file.cr\"].parent # => Path[\"foo/bar\"]\n  # Path[\"foo\"].parent             # => Path[\".\"]\n  # Path[\"/foo\"].parent            # => Path[\"/\"]\n  # Path[\"/\"].parent               # => Path[\"/\"]\n  # Path[\"\"].parent                # => Path[\".\"]\n  # Path[\"foo/bar/.\"].parent       # => Path[\"foo/bar\"]\n  # ```\n  def parent : Path\n    new_instance dirname\n  end\n\n  # Returns all parent paths of this path beginning with the topmost path.\n  #\n  # ```\n  # Path[\"foo/bar/file.cr\"].parents # => [Path[\".\"], Path[\"foo\"], Path[\"foo/bar\"]]\n  # ```\n  def parents : Array(Path)\n    parents = [] of Path\n    each_parent do |parent|\n      parents << parent\n    end\n    parents\n  end\n\n  # Yields each parent of this path beginning with the topmost parent.\n  #\n  # ```\n  # Path[\"foo/bar/file.cr\"].each_parent { |parent| puts parent }\n  # # Path[\".\"]\n  # # Path[\"foo\"]\n  # # Path[\"foo/bar\"]\n  # ```\n  def each_parent(&block : Path ->)\n    return if empty?\n\n    first_char = @name.char_at(0)\n    unless separators.includes?(first_char) || (first_char == '.' && separators.includes?(@name.byte_at?(1).try &.unsafe_chr)) || (windows? && (windows_drive? || unc_share?))\n      yield new_instance(\".\")\n    end\n\n    pos_memo = nil\n    each_part_separator_index do |start_pos, length|\n      # Delay yielding for each part to avoid yielding for the last part, which\n      # means the entire path.\n      if pos_memo\n        yield new_instance(@name.byte_slice(0, pos_memo))\n      end\n\n      pos_memo = start_pos + length\n    end\n  end\n\n  # Returns the last component of this path.\n  #\n  # If *suffix* is given, it is stripped from the end.\n  #\n  # In case the last component is the empty string (i.e. the path has a trailing\n  # separator), the second to last component is returned.\n  # For a path that only consists of an anchor, or an empty path, the base name\n  # is equivalent to the full path.\n  #\n  # ```\n  # Path[\"/foo/bar/file.cr\"].basename # => \"file.cr\"\n  # Path[\"/foo/bar/\"].basename        # => \"bar\"\n  # Path[\"/foo/bar/.\"].basename       # => \".\"\n  # Path[\"/\"].basename                # => \"/\"\n  # Path[\"\"].basename                 # => \"\"\n  # ```\n  def basename(suffix : String? = nil) : String\n    suffix.try &.check_no_null_byte\n\n    return \"\" if @name.empty?\n    return @name if @name.size == 1 && separators.includes?(@name[0])\n\n    bytes = @name.to_slice\n\n    current = bytes.size - 1\n\n    separators = self.separators.map &.ord\n\n    # skip trailing separators\n    while separators.includes?(bytes[current]) && current > 0\n      current -= 1\n    end\n\n    # read suffix\n    if suffix && suffix.bytesize <= current && suffix == @name.byte_slice(current - suffix.bytesize + 1, suffix.bytesize)\n      current -= suffix.bytesize\n    end\n\n    # one character left?\n    return @name.byte_slice(0, 1) if current == 0\n\n    end_pos = {current, 1}.max\n\n    # read basename\n    while !separators.includes?(bytes[current]) && current > 0\n      current -= 1\n    end\n\n    start_pos = current + 1\n\n    if start_pos == 1 && !separators.includes?(bytes[current])\n      start_pos = 0\n    end\n\n    @name.byte_slice(start_pos, end_pos - start_pos + 1)\n  end\n\n  # Returns the extension of this path, or an empty string if it has no extension.\n  #\n  # ```\n  # Path[\"foo.cr\"].extension     # => \".cr\"\n  # Path[\"foo\"].extension        # => \"\"\n  # Path[\"foo.tar.gz\"].extension # => \".gz\"\n  # ```\n  def extension : String\n    return \"\" if @name.bytesize < 3\n    bytes = @name.to_slice\n    separators = self.separators.map &.ord\n\n    # Ignore trailing separators\n    offset = bytes.size - 1\n    while bytes.unsafe_fetch(offset).in? separators\n      return \"\" if offset == 0\n      offset -= 1\n    end\n\n    # Get the first occurrence of a separator or a '.' past the trailing separators\n    dot_index = bytes.rindex(offset: offset) { |byte| byte === '.' || byte.in? separators }\n\n    # Return \"\" if '.' is the first character (ex. \".dotfile\"),\n    # or if the '.' character follows after a separator (ex. \"pathto/.dotfile\")\n    # or if the character at the returned index is a separator (ex. \"no/extension\")\n    # or if the filename ends with a '.'\n    return \"\" unless dot_index\n    return \"\" if dot_index == 0\n    return \"\" if dot_index == offset\n    return \"\" if bytes.unsafe_fetch(dot_index - 1).in?(separators)\n    return \"\" if bytes.unsafe_fetch(dot_index).in?(separators)\n\n    String.new(bytes[dot_index, offset - dot_index + 1])\n  end\n\n  # Returns the last component of this path without the extension.\n  #\n  # This is equivalent to `self.basename(self.extension)`.\n  #\n  # ```\n  # Path[\"file.cr\"].stem     # => \"file\"\n  # Path[\"file.tar.gz\"].stem # => \"file.tar\"\n  # Path[\"foo/file.cr\"].stem # => \"file\"\n  # ```\n  def stem : String\n    basename(extension)\n  end\n\n  # Removes redundant elements from this path and returns the shortest equivalent path by purely lexical processing.\n  # It applies the following rules iteratively until no further processing can be done:\n  #\n  #   1. Replace multiple slashes with a single slash.\n  #   2. Eliminate each `.` path name element (the current directory).\n  #   3. Eliminate each `..` path name element (the parent directory) preceded\n  #      by a non-`..` element along with the latter.\n  #   4. Eliminate `..` elements that begin a rooted path:\n  #      that is, replace `\"/..\"` by `\"/\"` at the beginning of a path.\n  #\n  # If the path turns to be empty, the current directory (`\".\"`) is returned.\n  #\n  # The returned path ends in a slash if it is the root (`\"/\"`, `\\`, or `C:\\`),\n  # or if this path is a Windows local device path that also ends in a slash.\n  # This trailing slash is significant; `\\\\.\\C:` refers to the _volume_ `C:`, on\n  # which most I/O functions fail, whereas `\\\\.\\C:\\` refers to the root\n  # _directory_ of said volume.\n  #\n  # See also Rob Pike: *[Lexical File Names in Plan 9 or Getting Dot-Dot Right](https://9p.io/sys/doc/lexnames.html)*\n  def normalize(*, remove_final_separator : Bool = true) : Path\n    return new_instance \".\" if empty?\n\n    drive, root = drive_and_root\n    reader = Char::Reader.new(@name)\n    dotdot = 0\n    separators = self.separators\n    add_separator_at_end = (!remove_final_separator || (windows? && dos_local_device_path?)) && ends_with_separator?\n\n    new_name = String.build do |str|\n      if drive\n        str << drive.gsub('/', '\\\\')\n        reader.pos += drive.bytesize\n      end\n      if root\n        str << separators[0]\n        reader.next_char\n        dotdot = str.bytesize\n      end\n      anchor_pos = str.bytesize\n\n      while (char = reader.current_char) != Char::ZERO\n        curr_pos = reader.pos\n        if separators.includes?(char)\n          # empty path element\n          reader.next_char\n        elsif char == '.' && (reader.pos + 1 == @name.bytesize || separators.includes?(reader.peek_next_char))\n          # . element\n          reader.next_char\n        elsif char == '.' && reader.next_char == '.' && (reader.pos + 1 == @name.bytesize || separators.includes?(reader.peek_next_char))\n          # .. element: remove to last /\n          reader.next_char\n          if str.bytesize > dotdot\n            str.back 1\n            while str.bytesize > dotdot && !separators.includes?((str.buffer + str.bytesize).value.unsafe_chr)\n              str.back 1\n            end\n          elsif !root\n            if str.bytesize > 0\n              str << separators[0]\n            end\n            str << \"..\"\n            dotdot = str.bytesize\n          end\n        else\n          reader.pos = curr_pos # make sure to reset lookahead used in previous condition\n\n          # real path element\n          # add slash if needed\n          if str.bytesize > anchor_pos && !separators.includes?((str.buffer + str.bytesize - 1).value.unsafe_chr)\n            str << separators[0]\n          end\n\n          loop do\n            str << char\n            char = reader.next_char\n            break if separators.includes?(char) || char == Char::ZERO\n          end\n        end\n      end\n\n      if str.empty?\n        str << '.'\n      end\n\n      last_char = (str.buffer + str.bytesize - 1).value.unsafe_chr\n\n      if add_separator_at_end && !separators.includes?(last_char)\n        str << separators[0]\n      end\n    end\n\n    new_instance new_name\n  end\n\n  # Yields each component of this path as a `String`.\n  #\n  # ```\n  # Path.new(\"foo/bar/\").each_part # yields: \"foo\", \"bar\"\n  # ```\n  #\n  # See `#parts` for more examples.\n  def each_part(& : String ->)\n    each_part_separator_index do |start_pos, length|\n      yield @name.byte_slice(start_pos, length)\n    end\n  end\n\n  # Returns the components of this path as an `Array(String)`.\n  #\n  # ```\n  # Path.new(\"foo/bar/\").parts                   # => [\"foo\", \"bar\"]\n  # Path.new(\"/Users/foo/bar.cr\").parts          # => [\"/\", \"Users\", \"foo\", \"bar.cr\"]\n  # Path.windows(\"C:\\\\Users\\\\foo\\\\bar.cr\").parts # => [\"C:\\\\\", \"Users\", \"foo\", \"bar.cr\"]\n  # Path.posix(\"C:\\\\Users\\\\foo\\\\bar.cr\").parts   # => [\"C:\\\\Users\\\\foo\\\\bar.cr\"]\n  # ```\n  def parts : Array(String)\n    parts = [] of String\n    each_part do |part|\n      parts << part\n    end\n    parts\n  end\n\n  # Returns an iterator over all components of this path.\n  #\n  # ```\n  # parts = Path.new(\"foo/bar/\").each_part\n  # parts.next # => \"foo\"\n  # parts.next # => \"bar\"\n  # parts.next # => Iterator::Stop::INSTANCE\n  # ```\n  #\n  # See `#parts` for more examples.\n  def each_part : Iterator(String)\n    PartIterator.new(self)\n  end\n\n  private def each_part_separator_index(&)\n    reader = Char::Reader.new(@name)\n    start_pos = reader.pos\n\n    if anchor = self.anchor\n      reader.pos = anchor.@name.bytesize\n      # Path is absolute, consume leading separators\n      while separators.includes?(reader.current_char)\n        break unless reader.has_next?\n        reader.next_char\n      end\n\n      start_pos = reader.pos\n      yield 0, start_pos\n    end\n\n    last_was_separator = false\n    separators = self.separators\n\n    while next_part = Path.next_part_separator_index(reader, last_was_separator, separators)\n      reader, last_was_separator, start_pos = next_part\n\n      break if reader.pos == start_pos\n      yield start_pos, reader.pos - start_pos\n    end\n  end\n\n  # :nodoc:\n  def self.next_part_separator_index(reader : Char::Reader, last_was_separator, separators)\n    start_pos = reader.pos\n\n    reader.each do |char|\n      if separators.includes?(char)\n        if last_was_separator\n          next\n        end\n\n        return reader, true, start_pos\n      elsif last_was_separator\n        start_pos = reader.pos\n        last_was_separator = false\n      end\n    end\n\n    unless last_was_separator\n      {reader, false, start_pos}\n    end\n  end\n\n  # :nodoc:\n  class PartIterator\n    include Iterator(String)\n\n    def initialize(@path : Path)\n      @reader = Char::Reader.new(@path.@name)\n      @last_was_separator = false\n      @anchor_processed = false\n    end\n\n    def next\n      start_pos = next_pos\n\n      return stop unless start_pos\n      return stop if start_pos == @reader.pos\n\n      @path.@name.byte_slice(start_pos, @reader.pos - start_pos)\n    end\n\n    private def next_pos\n      unless @anchor_processed\n        @anchor_processed = true\n        if anchor_pos = process_anchor\n          return anchor_pos\n        end\n      end\n\n      next_part = Path.next_part_separator_index(@reader, @last_was_separator, @path.separators)\n      return unless next_part\n\n      @reader, @last_was_separator, start_pos = next_part\n\n      start_pos\n    end\n\n    private def process_anchor\n      anchor = @path.anchor\n      return unless anchor\n\n      reader = @reader\n      reader.pos = anchor.@name.bytesize\n      # Path is absolute, consume leading separators\n      while @path.separators.includes?(reader.current_char)\n        return unless reader.has_next?\n        reader.next_char\n      end\n\n      @reader = reader\n      0\n    end\n  end\n\n  private def windows_drive?\n    @name.byte_at?(1) === ':' && @name.char_at(0).ascii_letter?\n  end\n\n  # Converts this path to a native path.\n  #\n  # * `#to_kind` performs a configurable conversion.\n  def to_native : Path\n    to_kind(Kind.native)\n  end\n\n  # Converts this path to a Windows path.\n  #\n  # This creates a new instance with the same string representation but with\n  # `Kind::WINDOWS`. If `#windows?` is true, this is a no-op.\n  #\n  # ```\n  # Path.posix(\"foo/bar\").to_windows   # => Path.windows(\"foo/bar\")\n  # Path.windows(\"foo/bar\").to_windows # => Path.windows(\"foo/bar\")\n  # ```\n  #\n  # When *mappings* is `true` (default), forbidden characters in Windows paths are\n  # substituted by replacement characters when converting from a POSIX path.\n  # Replacements are calculated by adding `0xF000` to their codepoint.\n  # For example, the backslash character `U+005C` becomes `U+F05C`.\n  #\n  # ```\n  # Path.posix(\"foo\\\\bar\").to_windows(mappings: true)  # => Path.windows(\"foo\\uF05Cbar\")\n  # Path.posix(\"foo\\\\bar\").to_windows(mappings: false) # => Path.windows(\"foo\\\\bar\")\n  # ```\n  #\n  # * `#to_posix` performs the inverse conversion.\n  # * `#to_kind` performs a configurable conversion.\n  def to_windows(*, mappings : Bool = true) : Path\n    name = @name\n    if posix? && mappings\n      name = name.tr(WINDOWS_ESCAPE_CHARACTERS, WINDOWS_ESCAPED_CHARACTERS)\n    end\n    new_instance(name, Kind::WINDOWS)\n  end\n\n  # :nodoc:\n  WINDOWS_ESCAPE_CHARACTERS = %(\"*:<>?\\\\| )\n  # :nodoc:\n  WINDOWS_ESCAPED_CHARACTERS = \"\\uF022\\uF02A\\uF03A\\uF03C\\uF03E\\uF03F\\uF05C\\uF07C\\uF020\"\n\n  # Converts this path to a POSIX path.\n  #\n  # It returns a new instance with `Kind::POSIX` and all occurrences of Windows'\n  # backslash file separators (`\\\\`) replaced by forward slash (`/`).\n  # If `#posix?` is true, this is a no-op.\n  #\n  # ```\n  # Path.windows(\"foo/bar\\\\baz\").to_posix # => Path.posix(\"foo/bar/baz\")\n  # Path.posix(\"foo/bar\\\\baz\").to_posix   # => Path.posix(\"foo/bar\\\\baz\")\n  # ```\n  #\n  # When *mappings* is `true` (default), replacements  for forbidden characters in Windows\n  # paths are substituted by the original characters when converting to a POSIX path.\n  # Originals are calculated by subtracting `0xF000` from the replacement codepoint.\n  # For example, the `U+F05C` becomes `U+005C`, the backslash character.\n  #\n  # ```\n  # Path.windows(\"foo\\uF05Cbar\").to_posix(mappings: true)  # => Path.posix(\"foo\\\\bar\")\n  # Path.windows(\"foo\\uF05Cbar\").to_posix(mappings: false) # => Path.posix(\"foo\\uF05Cbar\")\n  # ```\n  #\n  # * `#to_windows` performs the inverse conversion.\n  # * `#to_kind` performs a configurable conversion.\n  def to_posix(*, mappings : Bool = true) : Path\n    name = @name\n    if windows?\n      name = name.gsub('\\\\', '/')\n      if mappings\n        name = name.tr(WINDOWS_ESCAPED_CHARACTERS, WINDOWS_ESCAPE_CHARACTERS)\n      end\n    end\n    new_instance(name, Kind::POSIX)\n  end\n\n  # Converts this path to the given *kind*.\n  #\n  # See `#to_windows` and `#to_posix` for details.\n  #\n  # * `#to_native` converts to the native path semantics.\n  def to_kind(kind, *, mappings : Bool = true) : Path\n    if kind.posix?\n      to_posix(mappings: mappings)\n    else\n      to_windows(mappings: mappings)\n    end\n  end\n\n  # Converts this path to an absolute path. Relative paths are\n  # referenced from the current working directory of the process (`Dir.current`)\n  # unless *base* is given, in which case it will be used as the reference path.\n  #\n  # ```\n  # Path[\"foo\"].expand                 # => Path[\"/current/path/foo\"]\n  # Path[\"~/foo\"].expand(home: \"/bar\") # => Path[\"/bar/foo\"]\n  # Path[\"baz\"].expand(\"/foo/bar\")     # => Path[\"/foo/bar/baz\"]\n  # ```\n  #\n  # *home* specifies the home directory which `~` will expand to.\n  # \"~\" is expanded to the value passed to *home*.\n  # If it is `false` (default), home is not expanded.\n  # If `true`, it is expanded to the user's home directory (`Path.home`).\n  #\n  # If *expand_base* is `true`, *base* itself will be expanded in `Dir.current`\n  # if it is not an absolute path. This guarantees the method returns an absolute\n  # path (assuming that `Dir.current` is absolute).\n  def expand(base : Path | String = Dir.current, *, home : Path | String | Bool = false, expand_base = true) : Path\n    base = Path.new(base) unless base.is_a?(Path)\n    base = base.to_kind(@kind)\n    if base == self\n      # expanding base, avoid recursion\n      return new_instance(@name).normalize(remove_final_separator: false)\n    end\n\n    name = @name\n\n    if home\n      if name == \"~\"\n        name = resolve_home(home).to_s\n      elsif name.starts_with?(\"~/\") || (windows? && name.starts_with?(\"~\\\\\"))\n        name = resolve_home(home).join(name.byte_slice(2, name.bytesize - 2)).to_s\n      end\n    end\n\n    if new_instance(name).absolute?\n      expanded = name\n    else\n      unless base.absolute? || !expand_base\n        base = base.expand\n      end\n\n      if name.empty?\n        expanded = base\n      elsif windows?\n        base_drive, base_root = base.drive_and_root\n        drive, root = new_instance(name).drive_and_root\n\n        if drive && base_root\n          base_relative = base_drive ? base.@name.lchop(base_drive) : base.@name\n          expanded = \"#{drive}#{base_relative}#{separators[0]}#{name.lchop(drive)}\"\n        elsif root\n          if base_drive\n            expanded = \"#{base_drive}#{name}\"\n          else\n            expanded = name\n          end\n        else\n          if base_root\n            expanded = base.join(name)\n          else\n            expanded = String.build do |io|\n              if drive\n                io << drive\n              elsif base_drive\n                io << base_drive\n              end\n              base_relative = base.@name\n              base_relative = base_relative.lchop(base_drive) if base_drive\n              name_relative = drive ? name.lchop(drive) : name\n\n              io << base_relative\n              io << separators[0] unless base_relative.empty?\n              io << name_relative\n            end\n          end\n        end\n      else\n        expanded = base.join(name)\n      end\n    end\n\n    expanded = new_instance(expanded) unless expanded.is_a?(Path)\n    expanded.normalize(remove_final_separator: false)\n  end\n\n  private def resolve_home(home)\n    case home\n    when String then home = Path[home]\n    when Bool   then home = Path.home\n    when Path # no transformation needed\n    end\n\n    home.to_kind(@kind).normalize\n  end\n\n  # Appends the given *part* to this path and returns the joined path.\n  #\n  # ```\n  # Path[\"foo\"].join(\"bar\")     # => Path[\"foo/bar\"]\n  # Path[\"foo/\"].join(\"/bar\")   # => Path[\"foo/bar\"]\n  # Path[\"/foo/\"].join(\"/bar/\") # => Path[\"/foo/bar/\"]\n  # ```\n  #\n  # Joining an empty string (`\"\"`) appends a trailing path separator.\n  # In case the path already ends with a trailing separator, no additional\n  # separator is added.\n  #\n  # ```\n  # Path[\"a/b\"].join(\"\")   # => Path[\"a/b/\"]\n  # Path[\"a/b/\"].join(\"\")  # => Path[\"a/b/\"]\n  # Path[\"a/b/\"].join(\"c\") # => Path[\"a/b/c\"]\n  # ```\n  def join(part) : Path\n    # If we are joining a single part we can use `String.new` instead of\n    # `String.build` which avoids an extra allocation.\n    # Given that `File.join(arg1, arg2)` is the most common usage\n    # it's good if we can optimize this case.\n\n    if part.is_a?(Path)\n      part = part.to_kind(@kind).to_s\n    else\n      part = part.to_s\n      part.check_no_null_byte\n    end\n\n    if @name.empty?\n      if part.empty?\n        # We could use `separators[0].to_s` but then we'd have to\n        # convert Char to String which involves a memory allocation\n        return new_instance(windows? ? \"\\\\\" : \"/\")\n      else\n        return new_instance(part)\n      end\n    end\n\n    bytesize = @name.bytesize + part.bytesize # bytesize of the resulting string\n    add_separator = false                     # do we need to add a separate between the parts?\n    part_ptr = part.to_unsafe                 # where do we start copying from `part`?\n    part_bytesize = part.bytesize             # how much do we copy from `part`?\n\n    case {ends_with_separator?, starts_with_separator?(part)}\n    when {true, true}\n      # There are separators on both sides so we'll just lchop from the right part\n      bytesize -= 1\n      part_ptr += 1\n      part_bytesize -= 1\n    when {false, false}\n      # No separators on any side so we need to add one\n      bytesize += 1\n      add_separator = true\n    else\n      # There's at least on separator in the middle, so nothing to do\n    end\n\n    new_name = String.new(bytesize) do |buffer|\n      # Copy name\n      buffer.copy_from(@name.to_unsafe, @name.bytesize)\n      buffer += @name.bytesize\n\n      # Add separator if needed\n      if add_separator\n        buffer.value = separators[0].ord.to_u8\n        buffer += 1\n      end\n\n      # Copy the part\n      buffer.copy_from(part_ptr, part_bytesize)\n\n      {bytesize, @name.single_byte_optimizable? && part.single_byte_optimizable? ? bytesize : 0}\n    end\n\n    new_instance new_name\n  end\n\n  # Appends the given *parts* to this path and returns the joined path.\n  #\n  # ```\n  # Path[\"foo\"].join(\"bar\", \"baz\")       # => Path[\"foo/bar/baz\"]\n  # Path[\"foo/\"].join(\"/bar/\", \"/baz\")   # => Path[\"foo/bar/baz\"]\n  # Path[\"/foo/\"].join(\"/bar/\", \"/baz/\") # => Path[\"/foo/bar/baz/\"]\n  # ```\n  #\n  # See `join(part)` for details.\n  def join(*parts) : Path\n    join parts\n  end\n\n  # Appends the given *parts* to this path and returns the joined path.\n  #\n  # ```\n  # Path[\"foo\"].join(\"bar\", \"baz\")           # => Path[\"foo/bar/baz\"]\n  # Path[\"foo/\"].join(Path[\"/bar/\", \"/baz\"]) # => Path[\"foo/bar/baz\"]\n  # Path[\"/foo/\"].join(\"/bar/\", \"/baz/\")     # => Path[\"/foo/bar/baz/\"]\n  # ```\n  #\n  # Non-matching paths are implicitly converted to this path's kind.\n  #\n  # ```\n  # Path.posix(\"foo/bar\").join(Path.windows(\"baz\\\\baq\")) # => Path.posix(\"foo/bar/baz/baq\")\n  # Path.windows(\"foo\\\\bar\").join(Path.posix(\"baz/baq\")) # => Path.windows(\"foo\\\\bar\\\\baz/baq\")\n  # ```\n  #\n  # See `join(part)` for details.\n  def join(parts : Enumerable) : Path\n    parts.reduce(self) { |path, part| path.join(part) }\n  end\n\n  # Appends the given *part* to this path and returns the joined path.\n  #\n  # ```\n  # Path[\"foo\"] / \"bar\" / \"baz\"     # => Path[\"foo/bar/baz\"]\n  # Path[\"foo/\"] / Path[\"/bar/baz\"] # => Path[\"foo/bar/baz\"]\n  # ```\n  #\n  # See `join(part)` for details.\n  def /(part : Path | String) : Path\n    join(part)\n  end\n\n  # Resolves path *name* in this path's parent directory.\n  #\n  # Raises `Path::Error` if `#parent` is `nil`.\n  def sibling(name : Path | String) : Path\n    if parent = self.parent\n      parent.join(name)\n    else\n      raise Error.new(\"Can't resolve sibling for a path without parent directory\")\n    end\n  end\n\n  private def empty?\n    @name.empty? || @name == \".\"\n  end\n\n  # Returns a relative path that is lexically equivalent to `self` when joined\n  # to *base* with an intervening separator.\n  #\n  # The returned path is in normalized form.\n  #\n  # That means with normalized paths `base.join(target.relative_to(base))` is\n  # equivalent to `target`.\n  #\n  # Returns `nil` if `self` cannot be expressed as relative to *base* or if\n  # knowing the current working directory would be necessary to resolve it. The\n  # latter can be avoided by expanding the paths first.\n  #\n  # For Windows paths, the drive and the root must be identical; relative paths\n  # between different path types are not supported, even if they would resolve\n  # to the same roots (e.g. `\\\\.\\C:\\foo` and `C:\\foo` are not equivalent, nor\n  # are `\\\\?\\UNC\\server\\share\\foo` and `\\\\server\\share\\foo`).\n  def relative_to?(base : Path) : Path?\n    # work on normalized paths otherwise we would need to backtrack on `..` parts\n    base = base.normalize\n    target = self.normalize\n\n    base_anchor = base.anchor\n    target_anchor = target.anchor\n\n    # if paths have a different anchors, there can't be a relative path between\n    # them.\n    if base_anchor != target_anchor\n      return nil\n    end\n\n    # check for trivial case of equal paths\n    if base == target\n      return new_instance(\".\")\n    end\n\n    base_iterator = base.each_part\n    target_iterator = target.each_part\n\n    if target_anchor\n      # process anchors, we have already established they're equal\n      base_iterator.next\n      target_iterator.next\n    end\n\n    # consume both paths simultaneously as long as they have identical components\n    base_part = base_iterator.next\n    target_part = target_iterator.next\n    while base_part.is_a?(String) && target_part.is_a?(String)\n      if base_part.compare(target_part, case_insensitive: windows?) != 0\n        break\n      end\n\n      base_part = base_iterator.next\n      target_part = target_iterator.next\n    end\n\n    parts = [] of String\n\n    # base_path is not consumed, so we go up before descending into target_path\n    if base_part.is_a?(String)\n      # Can't relativize upwards from current working directory without knowing\n      # its path\n      if base_part == \"..\"\n        return nil\n      end\n\n      parts << \"..\" unless base_part == \".\"\n      base_iterator.each do\n        parts << \"..\"\n      end\n    end\n\n    # target_path is not consumed, so we append what's left to the relative path\n    if target_part.is_a?(String)\n      parts << target_part\n      target_iterator.each do |part|\n        parts << part\n      end\n    end\n\n    new_instance(parts.join(separators[0]))\n  end\n\n  # :ditto:\n  def relative_to?(base : String) : Path?\n    relative_to?(new_instance(base))\n  end\n\n  # Same as `#relative_to` but returns `self` if `self` can't be expressed as\n  # relative path to *base*.\n  def relative_to(base : Path | String) : Path\n    relative_to?(base) || self\n  end\n\n  # Compares this path to *other*.\n  #\n  # The comparison is performed strictly lexically: `foo` and `./foo` are *not*\n  # treated as equal. Nor are paths of different `kind`.\n  # To compare paths semantically, they need to be normalized and converted to\n  # the same kind.\n  #\n  # ```\n  # Path[\"foo\"] <=> Path[\"foo\"]               # => 0\n  # Path[\"foo\"] <=> Path[\"./foo\"]             # => 1\n  # Path[\"foo\"] <=> Path[\"foo/\"]              # => -1\n  # Path.posix(\"foo\") <=> Path.windows(\"foo\") # => -1\n  # ```\n  #\n  # Comparison is case-sensitive for POSIX paths and case-insensitive for\n  # Windows paths.\n  #\n  # ```\n  # Path.posix(\"foo\") <=> Path.posix(\"FOO\")     # => 1\n  # Path.windows(\"foo\") <=> Path.windows(\"FOO\") # => 0\n  # ```\n  def <=>(other : Path)\n    ord = @name.compare(other.@name, case_insensitive: windows? || other.windows?)\n    return ord if ord != 0\n\n    @kind <=> other.@kind\n  end\n\n  # Returns `true` if this path is considered equivalent to *other*.\n  #\n  # The comparison is performed strictly lexically: `foo` and `./foo` are *not*\n  # treated as equal. Nor are paths of different `kind`.\n  # To compare paths semantically, they need to be normalized and converted to\n  # the same kind.\n  #\n  # ```\n  # Path[\"foo\"] == Path[\"foo\"]               # => true\n  # Path[\"foo\"] == Path[\"./foo\"]             # => false\n  # Path[\"foo\"] == Path[\"foo/\"]              # => false\n  # Path.posix(\"foo\") == Path.windows(\"foo\") # => false\n  # ```\n  #\n  # Comparison is case-sensitive for POSIX paths and case-insensitive for\n  # Windows paths.\n  #\n  # ```\n  # Path.posix(\"foo\") == Path.posix(\"FOO\")     # => false\n  # Path.windows(\"foo\") == Path.windows(\"FOO\") # => true\n  # ```\n  def ==(other : self)\n    return false if @kind != other.@kind\n\n    @name.compare(other.@name, case_insensitive: windows? || other.windows?) == 0\n  end\n\n  def hash(hasher)\n    name = @name\n    if windows?\n      name = name.downcase\n    end\n    hasher = name.hash(hasher)\n    @kind.hash(hasher)\n  end\n\n  # Returns a path representing the drive component or `nil` if this path does not contain a drive.\n  #\n  # See `#anchor` for the combination of drive and `#root`.\n  #\n  # ```\n  # Path.windows(\"C:\\\\Program Files\").drive       # => Path.windows(\"C:\")\n  # Path.windows(\"\\\\\\\\host\\\\share\\\\folder\").drive # => Path.windows(\"\\\\\\\\host\\\\share\")\n  # Path.windows(\"\\\\\\\\.\\\\NUL\").drive              # => Path.windows(\"\\\\\\\\.\")\n  # Path.windows(\"//?\").drive                     # => Path.windows(\"//?\")\n  # ```\n  #\n  # NOTE: Drives are only available for Windows paths. It can be a drive letter\n  # (`C:`), a UNC share (`\\\\host\\share`), or a root local device path (`\\\\.`,\n  # `\\\\?`).\n  def drive : Path?\n    drive_end, _ = drive_and_root_indices\n\n    if drive_end\n      drive = @name.byte_slice(0, drive_end)\n      new_instance drive\n    end\n  end\n\n  # Returns the root path component of this path or `nil` if it is not rooted.\n  #\n  # See `#anchor` for the combination of `#drive` and root.\n  #\n  # ```\n  # Path[\"/etc/\"].root                           # => Path[\"/\"]\n  # Path.windows(\"C:Program Files\").root         # => nil\n  # Path.windows(\"C:\\\\Program Files\").root       # => Path.windows(\"\\\\\")\n  # Path.windows(\"\\\\\\\\host\\\\share\\\\folder\").root # => Path.windows(\"\\\\\")\n  # Path.windows(\"//./NUL\").root                 # => Path.windows(\"/\")\n  # Path.windows(\"\\\\\\\\?\").root                   # => nil\n  # ```\n  def root : Path?\n    drive_end, root_end = drive_and_root_indices\n\n    if root_end\n      root_start = drive_end || 0\n      root = @name.byte_slice(root_start, root_end - root_start)\n      new_instance root\n    end\n  end\n\n  # Returns the concatenation of `#drive` and `#root`.\n  #\n  # ```\n  # Path[\"/etc/\"].anchor                           # => Path[\"/\"]\n  # Path.windows(\"C:Program Files\").anchor         # => Path.windows(\"C:\")\n  # Path.windows(\"C:\\\\Program Files\").anchor       # => Path.windows(\"C:\\\\\")\n  # Path.windows(\"\\\\\\\\host\\\\share\\\\folder\").anchor # => Path.windows(\"\\\\\\\\host\\\\share\\\\\")\n  # Path.windows(\"\\\\\\\\.\\\\NUL\").anchor              # => Path.windows(\"\\\\\\\\.\\\\\")\n  # Path.windows(\"//?\").anchor                     # => Path.windows(\"//?\")\n  # ```\n  def anchor : Path?\n    drive_end, root_end = drive_and_root_indices\n    anchor_end = root_end || drive_end\n\n    if anchor_end\n      new_instance(@name.byte_slice(0, anchor_end))\n    end\n  end\n\n  # Returns a tuple of `#drive` and `#root` as strings.\n  def drive_and_root : {String?, String?}\n    drive_end, root_end = drive_and_root_indices\n\n    if drive_end\n      drive = @name.byte_slice(0, drive_end)\n    end\n\n    if root_end\n      root_start = drive_end || 0\n      root = @name.byte_slice(root_start, root_end - root_start)\n    end\n\n    {drive, root}\n  end\n\n  private def drive_and_root_indices : {Int32?, Int32?}\n    if windows?\n      if windows_drive?\n        if separators.includes?(@name.byte_at?(2).try(&.chr))\n          {2, 3}\n        else\n          {2, nil}\n        end\n      elsif dos_local_device_path?\n        if separators.includes?(@name.byte_at?(3).try(&.chr))\n          {3, 4}\n        else\n          {3, nil}\n        end\n      elsif unc_share = unc_share?\n        unc_share\n      elsif starts_with_separator?\n        {nil, 1}\n      else\n        {nil, nil}\n      end\n    elsif absolute? # posix\n      {nil, 1}\n    else\n      {nil, nil}\n    end\n  end\n\n  private def dos_local_device_path?\n    # `//./`, `\\\\?` etc.\n    @name.size >= 3 &&\n      separators.includes?(@name.to_unsafe[0].unsafe_chr) &&\n      separators.includes?(@name.to_unsafe[1].unsafe_chr) &&\n      {'.', '?'}.includes?(@name.to_unsafe[2].unsafe_chr)\n  end\n\n  private def unc_share?\n    # Test for UNC share\n    # path: //share/share\n    # part: 1122222 33333\n\n    # Grammar definition: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/62e862f4-2a51-452e-8eeb-dc4ff5ee33cc?redirectedfrom=MSDN\n\n    return unless @name.size >= 5\n\n    reader = Char::Reader.new(@name)\n\n    # 1. Consume two leading separators\n    char = reader.current_char\n    return unless separators.includes?(char) && char == reader.next_char\n    reader.next_char\n\n    # 2. Consume first path component\n    # The first component is either an IPv4 address or a hostname.\n    # IPv6 addresses are converted into hostnames by replacing all `:`s with\n    # `-`s, and then appending `.ipv6-literal.net`, so raw IPv6 addresses cannot\n    # appear here.\n    # Hostname follows the grammar of `reg-name` in [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).\n    return if separators.includes?(reader.current_char)\n    while true\n      char = reader.current_char\n      break if separators.includes?(char)\n      if char == '%'\n        # percent encoded character\n        return unless reader.has_next?\n        reader.next_char\n        return unless reader.current_char.ascii_number?\n        return unless reader.has_next?\n        reader.next_char\n        return unless reader.current_char.ascii_number?\n      else\n        # unreserved / sub-delims\n        return unless char.ascii_alphanumeric? || char.in?('_', '.', '-', '~', '!', '$', ';', '=') || char.in?('&'..',')\n      end\n      return unless reader.has_next?\n      reader.next_char\n    end\n\n    # Consume separator\n    char = reader.next_char\n    return if separators.includes?(char)\n\n    return unless reader.has_next?\n    reader.next_char\n\n    # 3. Consume second path component\n    # `share-name` in UNC grammar\n    while true\n      char = reader.current_char\n      break if separators.includes?(char) || !reader.has_next?\n      return unless char.ascii_alphanumeric? || char.in?(' ', '!', '-', '.', '@', '^', '_', '`', '{', '}', '~') || char.in?('#'..')') || char.ord.in?(0x80..0xFF)\n      reader.next_char\n    end\n\n    # Consume optional trailing separators\n    share_end = reader.pos\n    while reader.has_next?\n      char = reader.next_char\n      break unless separators.includes?(char)\n    end\n\n    unless reader.pos == share_end\n      root_end = reader.pos\n    end\n\n    return share_end, root_end\n  end\n\n  # Returns `true` if this path is absolute.\n  #\n  # A POSIX path is absolute if it begins with a forward slash (`/`).\n  #\n  # A Windows path is absolute if it begins with a drive letter (`C:`), a UNC\n  # share (`\\\\server\\share`), or a root local device path (`\\\\.`, `\\\\?`), which\n  # is then followed by a root path separator. Drive-relative paths (`C:foo`),\n  # rooted paths (`\\foo`), and root local device paths (`\\\\.`) are not absolute.\n  def absolute? : Bool\n    separators = self.separators\n    if windows?\n      first_is_separator = false\n      starts_with_double_separator = false\n      found_share_name = false\n      found_dot_or_question_mark = false\n\n      @name.each_char_with_index do |char, index|\n        case index\n        when 0\n          if separators.includes?(char)\n            first_is_separator = true\n          else\n            return false unless char.ascii_letter?\n          end\n        when 1\n          if first_is_separator && separators.includes?(char)\n            starts_with_double_separator = true\n          else\n            return false unless char == ':'\n          end\n        else\n          case char\n          when .in?(separators)\n            if index == 2\n              return !starts_with_double_separator && !found_share_name\n            elsif index == 3 && found_dot_or_question_mark\n              return true\n            elsif found_share_name\n              return true\n            else\n              found_share_name = true\n            end\n          when '.', '?'\n            if index == 2\n              found_dot_or_question_mark = true\n            end\n          end\n        end\n      end\n\n      false\n    else\n      separators.includes?(@name[0]?)\n    end\n  end\n\n  # Returns `true` if this path is relative.\n  #\n  # See `#absolute?`\n  def relative? : Bool\n    !absolute?\n  end\n\n  # :nodoc:\n  def separators\n    Path.separators(@kind)\n  end\n\n  def ends_with_separator? : Bool\n    ends_with_separator?(@name)\n  end\n\n  private def ends_with_separator?(name)\n    separators.any? { |separator| name.ends_with?(separator) }\n  end\n\n  private def starts_with_separator?(name = @name)\n    separators.any? { |separator| name.starts_with?(separator) }\n  end\n\n  # Returns the string representation of this path.\n  def to_s : String\n    @name\n  end\n\n  # Appends the string representation of this path to *io*.\n  def to_s(io : IO)\n    io << to_s\n  end\n\n  # Inspects this path to *io*.\n  def inspect(io : IO)\n    if native?\n      io << \"Path[\"\n      @name.inspect(io)\n      io << ']'\n    else\n      io << \"Path.\"\n      io << (windows? ? \"windows\" : \"posix\")\n      io << '('\n      @name.inspect(io)\n      io << ')'\n    end\n  end\n\n  # Returns a new `URI` with `file` scheme from this path.\n  #\n  # A URI can only be created with an absolute path. Raises `Path::Error` if\n  # this path is not absolute.\n  def to_uri : URI\n    raise Error.new(\"Cannot create a URI from relative path\") unless absolute?\n    URI.new(scheme: \"file\", path: @name)\n  end\n\n  # Returns the path of the home directory of the current user.\n  def self.home : Path\n    new(Crystal::System::Path.home)\n  end\nend\n"
  },
  {
    "path": "src/pointer.cr",
    "content": "require \"c/string\"\n\n# A typed pointer to some memory.\n#\n# This is the only unsafe type in Crystal. If you are using a pointer, you are writing\n# unsafe code because a pointer doesn't know where it's pointing to nor how much memory\n# starting from it is valid. However, pointers make it possible to interface with C and\n# to implement efficient data structures. For example, both `Array` and `Hash` are\n# implemented using pointers.\n#\n# You can obtain pointers in four ways: `#new`, `#malloc`, `pointerof`, or by calling a C\n# function that returns a pointer.\n#\n# `pointerof(x)`, where *x* is a variable or an instance variable, returns a pointer to\n# that variable:\n#\n# ```\n# x = 1\n# ptr = pointerof(x)\n# ptr.value = 2\n# x # => 2\n# ```\n#\n# Use `#value` to dereference the pointer.\n#\n# Note that a pointer is *falsey* if it's null (if its address is zero).\n#\n# When calling a C function that expects a pointer you can also pass `nil` instead of using\n# `Pointer.null` to construct a null pointer.\n#\n# For a safe alternative, see `Slice`, which is a pointer with a size and with bounds checking.\nstruct Pointer(T)\n  # Unsafe wrapper around a `Pointer` that allows to write values to\n  # it while advancing the location and keeping track of how many elements\n  # were written.\n  #\n  # See also: `Pointer#appender`.\n  struct Appender(T)\n    def initialize(@pointer : Pointer(T))\n      @start = @pointer\n    end\n\n    def <<(value : T)\n      @pointer.value = value\n      @pointer += 1\n    end\n\n    def size : Int64\n      @pointer - @start\n    end\n\n    def pointer\n      @pointer\n    end\n\n    # Creates a slice pointing at the values appended by this instance.\n    #\n    # ```\n    # slice = Slice(Int32).new(5)\n    # appender = slice.to_unsafe.appender\n    # appender << 1\n    # appender << 2\n    # appender << 3\n    # appender.to_slice # => Slice[1, 2, 3]\n    # ```\n    def to_slice : Slice(T)\n      @start.to_slice(size)\n    end\n  end\n\n  include Comparable(self)\n\n  # Returns `true` if this pointer's address is zero.\n  #\n  # ```\n  # a = 1\n  # pointerof(a).null? # => false\n  #\n  # b = Pointer(Int32).new(0)\n  # b.null? # => true\n  # ```\n  def null? : Bool\n    address == 0\n  end\n\n  # Returns a new pointer whose address is this pointer's address incremented by `other * sizeof(T)`.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(1234)\n  # ptr.address # => 1234\n  #\n  # # An Int32 occupies four bytes\n  # ptr2 = ptr + 1\n  # ptr2.address # => 1238\n  # ```\n  def +(other : Int)\n    self + other.to_i64\n  end\n\n  # Returns a new pointer whose address is this pointer's address decremented by `other * sizeof(T)`.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(1234)\n  # ptr.address # => 1234\n  #\n  # # An Int32 occupies four bytes\n  # ptr2 = ptr - 1\n  # ptr2.address # => 1230\n  # ```\n  def -(other : Int)\n    # TODO: If throwing on overflow for integer conversion is implemented,\n    # then (here and in `Pointer#-`) for a `UInt64` argument the call to\n    # `to_i64` should become `as_unsafe`.\n    self + (-other.to_i64!)\n  end\n\n  # Returns `-1`, `0` or `1` depending on whether this pointer's address is less, equal or greater than *other*'s address,\n  # respectively.\n  def <=>(other : self)\n    address <=> other.address\n  end\n\n  # Gets the value pointed at this pointer's address plus `offset * sizeof(T)`.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| i + 10 }\n  # ptr[0] # => 10\n  # ptr[1] # => 11\n  # ptr[2] # => 12\n  # ptr[3] # => 13\n  # ```\n  def [](offset)\n    (self + offset).value\n  end\n\n  # Sets the value pointed at this pointer's address plus `offset * sizeof(T)`.\n  #\n  # ```\n  # ptr = Pointer(Int32).malloc(4)\n  # ptr.to_slice(4) # => Slice[0, 0, 0, 0]\n  # ptr[1] = 42\n  #\n  # ptr2 = ptr + 1\n  # ptr2.value       # => 42\n  # ptr1.to_slice(4) # => Slice[0, 42, 0, 0]\n  # ```\n  def []=(offset, value : T)\n    (self + offset).value = value\n  end\n\n  # Copies *count* elements from *source* into `self`.\n  # If *source* and `self` overlap, behaviour is undefined.\n  # Use `#move_from` if they overlap (slower but always works).\n  #\n  # ```\n  # ptr1 = Pointer.malloc(4) { |i| i + 1 }\n  # ptr2 = Pointer.malloc(4) { |i| i + 11 }\n  #\n  # ptr1.to_slice(4) # => Slice[1, 2, 3, 4]\n  # ptr2.to_slice(4) # => Slice[11, 12, 13, 14]\n  #\n  # # ptr2 -> [11, 12, 13, 14]\n  # #          ^---^           <- copy this\n  # # ptr1 -> [1,  2,  3,  4]\n  # #          ^---^           <- here\n  # ptr1.copy_from(ptr2, 2)\n  # ptr1.to_slice(4) # => Slice[1, 12, 3, 4]\n  # ```\n  def copy_from(source : Pointer(T), count : Int)\n    source.copy_to(self, count)\n  end\n\n  # :nodoc:\n  def copy_from(source : Pointer(NoReturn), count : Int)\n    raise ArgumentError.new(\"Negative count\") if count < 0\n\n    # We need this overload for cases when we have a pointer to unreachable\n    # data, like when doing Tuple.new.to_a\n    self\n  end\n\n  # Copies *count* elements from `self` into *target*.\n  # If `self` and *target* overlap, behaviour is undefined.\n  # Use `#move_to` if they overlap (slower but always works).\n  #\n  # ```\n  # ptr1 = Pointer.malloc(4) { |i| i + 1 }\n  # ptr2 = Pointer.malloc(4) { |i| i + 11 }\n  #\n  # ptr1.to_slice(4) # => Slice[1, 2, 3, 4]\n  # ptr2.to_slice(4) # => Slice[11, 12, 13, 14]\n  #\n  # # ptr1 -> [1,  2,  3,  4]\n  # #          ^---^           <- copy this\n  # # ptr2 -> [11, 12, 13, 14]\n  # #          ^---^           <- here\n  # ptr1.copy_to(ptr2, 2)\n  # ptr2.to_slice(4) # => Slice[1, 2, 13, 14]\n  # ```\n  def copy_to(target : Pointer, count : Int)\n    target.copy_from_impl(self, count)\n  end\n\n  # Copies *count* elements from *source* into `self`.\n  # *source* and `self` may overlap; the copy is always done in a non-destructive manner.\n  #\n  # ```\n  # ptr1 = Pointer.malloc(4) { |i| i + 1 }\n  # ptr1.to_slice(4) # => Slice[1, 2, 3, 4]\n  # #                              ^-- ptr2\n  # ptr2 = ptr1 + 1\n  #\n  # # [1, 2, 3, 4]\n  # #  ^-----^       <- copy this\n  # #     ^------^   <- here\n  # ptr2.move_from(ptr1, 3)\n  #\n  # ptr1.to_slice(4) # => Slice[1, 1, 2, 3]\n  # ```\n  def move_from(source : Pointer(T), count : Int)\n    source.move_to(self, count)\n  end\n\n  # :nodoc:\n  def move_from(source : Pointer(NoReturn), count : Int)\n    raise ArgumentError.new(\"Negative count\") if count < 0\n\n    # We need this overload for cases when we have a pointer to unreachable\n    # data, like when doing Tuple.new.to_a\n    self\n  end\n\n  # Copies *count* elements from `self` into *target*.\n  # *target* and `self` may overlap; the copy is always done in a non-destructive manner.\n  #\n  # ```\n  # ptr1 = Pointer.malloc(4) { |i| i + 1 }\n  # ptr1.to_slice(4) # => Slice[1, 2, 3, 4]\n  # #                              ^-- ptr2\n  # ptr2 = ptr1 + 1\n  #\n  # # [1, 2, 3, 4]\n  # #  ^-----^       <- copy this\n  # #     ^------^   <- here\n  # ptr1.move_to(ptr2, 3)\n  #\n  # ptr1.to_slice(4) # => Slice[1, 1, 2, 3]\n  # ```\n  def move_to(target : Pointer, count : Int)\n    target.move_from_impl(self, count)\n  end\n\n  # We use separate method in which we make sure that `source`\n  # is never a union of pointers. This is guaranteed because both\n  # copy_from/move_from/copy_to/move_to reverse self and caller,\n  # and so if either self or the arguments are unions a dispatch\n  # will happen and unions will disappear.\n  protected def copy_from_impl(source : Pointer(T), count : Int)\n    raise ArgumentError.new(\"Negative count\") if count < 0\n\n    if self.class == source.class\n      Intrinsics.memcpy(self.as(Void*), source.as(Void*), bytesize(count), false)\n    else\n      while count > 0\n        count &-= 1\n        self[count] = source[count]\n      end\n    end\n    self\n  end\n\n  protected def move_from_impl(source : Pointer(T), count : Int)\n    raise ArgumentError.new(\"Negative count\") if count < 0\n\n    if self.class == source.class\n      Intrinsics.memmove(self.as(Void*), source.as(Void*), bytesize(count), false)\n    else\n      if source.address < address\n        copy_from source, count\n      else\n        count.times do |i|\n          self[i] = source[i]\n        end\n      end\n    end\n    self\n  end\n\n  # Compares *count* elements from this pointer and *other*, lexicographically.\n  #\n  # Returns 0 if both pointers point to the same sequence of *count* bytes.\n  # Otherwise, if the first two differing bytes (treated as UInt8) from `self`\n  # and *other* are `x` and `y` respectively, returns a negative value if\n  # `x < y`, or a positive value if `x > y`.\n  #\n  # ```\n  # ptr1 = Pointer.malloc(4) { |i| i + 1 }\n  # ptr2 = Pointer.malloc(4) { |i| i + 11 }\n  #\n  # ptr1.to_slice(4) # => Slice[1, 2, 3, 4]\n  # ptr2.to_slice(4) # => Slice[11, 12, 13, 14]\n  #\n  # ptr1.memcmp(ptr2, 4) < 0  # => true\n  # ptr2.memcmp(ptr1, 4) > 0  # => true\n  # ptr1.memcmp(ptr1, 4) == 0 # => true\n  # ```\n  def memcmp(other : Pointer(T), count : Int) : Int32\n    LibC.memcmp(self.as(Void*), (other.as(Void*)), (count * sizeof(T)))\n  end\n\n  # Swaps the contents pointed at the offsets *i* and *j*.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| i + 1 }\n  # ptr[2] # => 3\n  # ptr[3] # => 4\n  # ptr.swap(2, 3)\n  # ptr[2] # => 4\n  # ptr[3] # => 3\n  # ```\n  def swap(i, j)\n    self[i], self[j] = self[j], self[i]\n  end\n\n  # Returns the address of this pointer.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(1234)\n  # ptr.hash # => 1234\n  # ```\n  def_hash address\n\n  # Appends a string representation of this pointer to the given `IO`,\n  # including its type and address in hexadecimal.\n  #\n  # ```\n  # ptr1 = Pointer(Int32).new(1234)\n  # ptr1.to_s # => \"Pointer(Int32)@0x4d2\"\n  #\n  # ptr2 = Pointer(Int32).new(0)\n  # ptr2.to_s # => \"Pointer(Int32).null\"\n  # ```\n  def to_s(io : IO) : Nil\n    io << {{ @type.name.stringify }}\n    if address == 0\n      io << \".null\"\n    else\n      io << \"@0x\"\n      address.to_s(io, 16)\n    end\n  end\n\n  # Tries to change the size of the allocation pointed to by this pointer to *size*,\n  # and returns that pointer.\n  #\n  # Since the space after the end of the block may be in use, realloc may find it\n  # necessary to copy the block to a new address where more free space is available.\n  # The value of realloc is the new address of the block.\n  # If the block needs to be moved, realloc copies the old contents.\n  #\n  # Remember to always assign the value of realloc.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| i + 1 }\n  # ptr.to_slice(4) # => Slice[1, 2, 3, 4]\n  #\n  # ptr = ptr.realloc(8)\n  # ptr.to_slice(8) # => Slice[1, 2, 3, 4, 0, 0, 0, 0]\n  # ```\n  #\n  # WARNING: Memory allocated using `GC.malloc` or `GC.malloc_atomic` must be\n  # reallocated using `GC.realloc` instead.\n  def realloc(size : Int)\n    if size < 0\n      raise ArgumentError.new(\"Negative size\")\n    end\n\n    realloc(size.to_u64)\n  end\n\n  # Shuffles *count* consecutive values pointed by this pointer.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| i + 1 }\n  # ptr.to_slice(4) # => Slice[1, 2, 3, 4]\n  # ptr.shuffle!(4)\n  # ptr.to_slice(4) # => Slice[3, 4, 1, 2]\n  # ```\n  def shuffle!(count : Int, random : Random? = nil)\n    rng = random || Random.thread_default\n    (count - 1).downto(1) do |i|\n      j = rng.rand(i + 1)\n      swap(i, j)\n    end\n    self\n  end\n\n  # Sets *count* consecutive values pointed by this pointer to the\n  # values returned by the block.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| i + 1 }\n  # ptr.to_slice(4) # => Slice[1, 2, 3, 4]\n  # ptr.map!(4) { |value| value * 2 }\n  # ptr.to_slice(4) # => Slice[2, 4, 6, 8]\n  # ```\n  def map!(count : Int, & : T -> T) : self\n    fill(count) do |i|\n      yield self[i]\n    end\n  end\n\n  # Like `map!`, but yields 2 arguments, the element and its index\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there.\n  def map_with_index!(count : Int, offset = 0, &block) : self\n    fill(count) do |i|\n      yield self[i], offset + i\n    end\n  end\n\n  # Replaces *count* elements in `self` with *value*. Returns `self`.\n  #\n  # ```\n  # ptr = Pointer(Int32).malloc(5) { |i| i }\n  # ptr.to_slice(5) # => Slice[0, 1, 2, 3, 4]\n  # ptr.fill(3, 0)\n  # ptr.to_slice(5) # => Slice[0, 0, 0, 0, 4]\n  # ```\n  def fill(count : Int, value : T) : self\n    {% if T == UInt8 %}\n      Intrinsics.memset(self.as(Void*), value, count, false)\n      self\n    {% else %}\n      {% if Number::Primitive.union_types.includes?(T) %}\n        if value == 0\n          clear(count)\n          return self\n        end\n      {% end %}\n\n      fill(count) { value }\n    {% end %}\n  end\n\n  # Yields *count* indices starting from `self` to the given block and then\n  # assigns the block's output value in that position. Returns `self`.\n  #\n  # ```\n  # ptr = Pointer(Int32).malloc(5) { |i| i }\n  # ptr.to_slice(5) # => Slice[0, 1, 2, 3, 4]\n  #\n  # (ptr + 1).fill(3) { |i| i * i }\n  # ptr.to_slice(5) # => Slice[0, 0, 1, 4, 4]\n  #\n  # (ptr + 1).fill(3, offset: 3) { |i| i * i }\n  # ptr.to_slice(5) # => Slice[0, 9, 16, 25, 4]\n  # ```\n  def fill(count : Int, *, offset : Int = 0, &) : self\n    count.times do |i|\n      self[i] = yield i + offset\n    end\n    self\n  end\n\n  # Returns a pointer with the address of this pointer\n  # aligned downwards to the next given byte *boundary*.\n  #\n  # INFO: This method requires the given parameter to be a power of 2\n  #\n  # INFO: This method aligns on byte boundaries, not sizeof(T) boundaries\n  #\n  # ```\n  # ptr = Pointer(Void).new(0x30_u64)\n  # ptr.align_down(16) # => Pointer(Void)@0x30\n  # ptr.align_down(32) # => Pointer(Void)@0x20\n  # ```\n  @[AlwaysInline]\n  def align_down(boundary : UInt64) : Pointer(T)\n    Pointer(T).new(self.address & (&-boundary))\n  end\n\n  # Returns a pointer with the address of this pointer\n  # aligned upwards to the next given byte *boundary*.\n  #\n  # INFO: This method requires the given parameter to be a power of 2\n  #\n  # INFO: This method aligns on byte boundaries, not sizeof(T) boundaries\n  #\n  # ```\n  # ptr = Pointer(Void).new(0x30_u64)\n  # ptr.align_up(16) # => Pointer(Void)@0x30\n  # ptr.align_up(32) # => Pointer(Void)@0x40\n  # ```\n  @[AlwaysInline]\n  def align_up(boundary : UInt64) : Pointer(T)\n    Pointer(T).new((self.address &+ (boundary &- 1)) & (&-boundary))\n  end\n\n  # :nodoc:\n  #\n  # This definition is required for the invalid `Pointer.new` method to not get\n  # documented. This will never be called, the compiler will fail to compile.\n  def initialize\n    raise \"can't create instance of a pointer type\"\n  end\n\n  # Returns a pointer whose memory address is zero. This doesn't allocate memory.\n  #\n  # When calling a C function you can also pass `nil` instead of constructing a\n  # null pointer with this method.\n  #\n  # ```\n  # ptr = Pointer(Int32).null\n  # ptr.address # => 0\n  # ```\n  def self.null\n    new 0_u64\n  end\n\n  # Returns a pointer that points to the given memory address. This doesn't allocate memory.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(5678)\n  # ptr.address # => 5678\n  # ```\n  @[Deprecated(\"Call `.new(UInt64)` directly instead\")]\n  def self.new(address : Int)\n    new address.to_u64!\n  end\n\n  # Allocates `size * sizeof(T)` bytes from the system's heap initialized\n  # to zero and returns a pointer to the first byte from that memory.\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # ```\n  # # Allocate memory for an Int32: 4 bytes\n  # ptr = Pointer(Int32).malloc\n  # ptr.value # => 0\n  #\n  # # Allocate memory for 10 Int32: 40 bytes\n  # ptr = Pointer(Int32).malloc(10)\n  # ptr[0] # => 0\n  # # ...\n  # ptr[9] # => 0\n  # ```\n  def self.malloc(size : Int = 1)\n    if size < 0\n      raise ArgumentError.new(\"Negative Pointer#malloc size\")\n    end\n\n    malloc(size.to_u64)\n  end\n\n  # Allocates `size * sizeof(T)` bytes from the system's heap initialized\n  # to *value* and returns a pointer to the first byte from that memory.\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # ```\n  # # An Int32 occupies 4 bytes, so here we are requesting 8 bytes\n  # # initialized to the number 42\n  # ptr = Pointer.malloc(2, 42)\n  # ptr[0] # => 42\n  # ptr[1] # => 42\n  # ```\n  def self.malloc(size : Int, value : T) : Pointer(T)\n    ptr = Pointer(T).malloc(size)\n\n    {% if Number::Primitive.union_types.includes?(T) %}\n      return ptr if value.zero?\n    {% elsif T < Pointer %}\n      return ptr if value.null?\n    {% end %}\n\n    ptr.fill(size, value)\n  end\n\n  # Allocates `size * sizeof(T)` bytes from the system's heap initialized\n  # to the value returned by the block (which is invoked once with each index in the range `0...size`)\n  # and returns a pointer to the first byte from that memory.\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # ```\n  # # An Int32 occupies 4 bytes, so here we are requesting 16 bytes.\n  # # i is an index in the range 0 .. 3\n  # ptr = Pointer.malloc(4) { |i| i + 10 }\n  # ptr[0] # => 10\n  # ptr[1] # => 11\n  # ptr[2] # => 12\n  # ptr[3] # => 13\n  # ```\n  def self.malloc(size : Int, & : Int32 -> T) : Pointer(T)\n    ptr = Pointer(T).malloc(size)\n    ptr.fill(size) { |i| yield i }\n  end\n\n  # Returns a `Pointer::Appender` for this pointer.\n  def appender : Pointer::Appender\n    Pointer::Appender.new(self)\n  end\n\n  # Returns a `Slice` that points to this pointer and is bounded by the given *size*.\n  #\n  # ```\n  # ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15]\n  # slice = ptr.to_slice(4)                # => Slice[10, 11, 12, 13]\n  # slice.class                            # => Slice(Int32)\n  # ```\n  def to_slice(size) : Slice(T)\n    Slice.new(self, size)\n  end\n\n  # Clears (sets to \"zero\" bytes) a number of values pointed by this pointer.\n  #\n  # ```\n  # ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15]\n  # ptr.clear(3)\n  # ptr.to_slice(6) # => Slice[0, 0, 0, 13, 14, 15]\n  # ```\n  def clear(count = 1)\n    Intrinsics.memset(self.as(Void*), 0_u8, bytesize(count), false)\n  end\n\n  def clone\n    self\n  end\n\n  private def bytesize(count)\n    {% if flag?(:bits64) %}\n      count.to_u64 * sizeof(T)\n    {% else %}\n      if count > UInt32::MAX\n        raise ArgumentError.new(\"Given count is bigger than UInt32::MAX\")\n      end\n\n      count.to_u32 * sizeof(T)\n    {% end %}\n  end\nend\n"
  },
  {
    "path": "src/prelude.cr",
    "content": "# Entries to this file should only be ordered if macros are involved -\n# macros need to be defined before they are used.\n# A first compiler pass gathers all classes and methods, removing the\n# requirement to place these in load order.\n#\n# When adding new files, use alpha-sort when possible. Make sure\n# to also add them to `docs_main.cr` if their content needs to\n# appear in the API docs.\n\n# This list requires ordered statements\nrequire \"lib_c\"\nrequire \"macros\"\nrequire \"object\"\nrequire \"crystal/once\"\nrequire \"comparable\"\nrequire \"exception\"\nrequire \"iterable\"\nrequire \"iterator\"\nrequire \"steppable\"\nrequire \"indexable\"\nrequire \"string\"\nrequire \"number\"\nrequire \"primitives\"\n\n# Alpha-sorted list\nrequire \"annotations\"\nrequire \"array\"\nrequire \"atomic\"\nrequire \"base64\"\nrequire \"bool\"\nrequire \"box\"\nrequire \"char\"\nrequire \"char/reader\"\nrequire \"class\"\nrequire \"concurrent\"\nrequire \"crystal/compiler_rt\"\nrequire \"crystal/main\"\nrequire \"deque\"\nrequire \"dir\"\nrequire \"enum\"\nrequire \"enumerable\"\nrequire \"env\"\nrequire \"errno\"\nrequire \"winerror\"\nrequire \"wasi_error\"\nrequire \"file\"\nrequire \"float\"\nrequire \"gc\"\nrequire \"hash\"\nrequire \"int\"\nrequire \"intrinsics\"\nrequire \"io\"\nrequire \"kernel\"\nrequire \"math/math\"\nrequire \"mutex\"\nrequire \"named_tuple\"\nrequire \"nil\"\nrequire \"humanize\"\nrequire \"path\"\nrequire \"pointer\"\nrequire \"pretty_print\"\nrequire \"proc\"\nrequire \"process\"\nrequire \"raise\"\nrequire \"random\"\nrequire \"range\"\nrequire \"reference\"\nrequire \"reference_storage\"\nrequire \"regex\"\nrequire \"set\"\n{% unless flag?(:wasm32) %}\n  require \"signal\"\n{% end %}\nrequire \"slice\"\nrequire \"static_array\"\nrequire \"struct\"\nrequire \"symbol\"\nrequire \"system\"\nrequire \"crystal/system/thread\"\nrequire \"time\"\nrequire \"tuple\"\nrequire \"unicode\"\nrequire \"union\"\nrequire \"va_list\"\nrequire \"value\"\n"
  },
  {
    "path": "src/pretty_print.cr",
    "content": "# This class implements a pretty printing algorithm.\n# It finds line breaks and nice indentations for grouped structure.\n#\n# ### References\n#\n# * [Ruby's prettyprint.rb](https://github.com/ruby/ruby/blob/master/lib/prettyprint.rb)\n# * [Christian Lindig, Strictly Pretty, March 2000](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200)\n# * [Philip Wadler, A prettier printer, March 1998](http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier)\nclass PrettyPrint\n  protected getter group_queue\n  protected getter newline\n  protected getter indent\n\n  # Creates a new pretty printer that will write to the given *output*\n  # and be capped at *maxwidth*.\n  def initialize(@output : IO, @maxwidth = 79, @newline = \"\\n\", @indent = 0)\n    @output_width = @indent\n    @buffer_width = 0\n\n    # Buffer of object that can't yet be printed to\n    # the output because we don't know if the current\n    # group overflows maxwidth or not\n    @buffer = Deque(Text | Breakable).new\n\n    root_group = Group.new(0)\n\n    # All groups being pushed by `group` calls\n    @group_stack = [] of Group\n    @group_stack << root_group\n\n    # Queue of array of groups (one array per group level)\n    # that are not yet breakable\n    @group_queue = GroupQueue.new\n    @group_queue.enq root_group\n  end\n\n  protected def current_group\n    @group_stack.last\n  end\n\n  # Checks if the current output width plus the\n  # total width accumulated in buffer objects exceeds\n  # the maximum allowed width. If so, it means that\n  # all groups until the first break must be broken\n  # into newlines, and all breakables and texts until\n  # that point can be printed.\n  protected def break_outmost_groups\n    while @maxwidth < @output_width + @buffer_width\n      return unless group = @group_queue.deq\n\n      until group.breakables.empty?\n        data = @buffer.shift\n        @output_width = data.output(@output, @output_width)\n        @buffer_width -= data.width\n      end\n\n      while !@buffer.empty? && @buffer.first.is_a?(Text)\n        text = @buffer.shift.as(Text)\n        @output_width = text.output(@output, @output_width)\n        @buffer_width -= text.width\n      end\n    end\n  end\n\n  # Appends a text element.\n  def text(obj) : Nil\n    obj = obj.to_s\n    width = obj.size\n    return if width == 0\n\n    if @buffer.empty?\n      @output << obj\n      @output_width += width\n    else\n      text = @buffer.last\n      unless text.is_a?(Text)\n        text = Text.new\n        @buffer << text\n      end\n      text.add(obj, width)\n      @buffer_width += width\n      break_outmost_groups\n    end\n  end\n\n  # Appends an element that can turn into a newline if necessary.\n  def breakable(sep = \" \") : Nil\n    width = sep.size\n    group = @group_stack.last\n    if group.break?\n      flush\n      @output << @newline\n      @indent.times { @output << ' ' }\n      @output_width = @indent\n      @buffer_width = 0\n    else\n      @buffer << Breakable.new(sep, width, self)\n      @buffer_width += width\n      break_outmost_groups\n    end\n  end\n\n  # Similar to `#breakable` except\n  # the decision to break or not is determined individually.\n  def fill_breakable(sep = \" \") : Nil\n    group { breakable sep }\n  end\n\n  # Creates a group of objects. Inside a group all breakable\n  # objects are either turned into newlines or are output\n  # as is, depending on the available width.\n  def group(indent = 0, open_obj = \"\", close_obj = \"\", &)\n    text open_obj\n    group_sub do\n      nest(indent) do\n        yield\n      end\n    end\n    text close_obj\n  end\n\n  private def group_sub(&)\n    group = Group.new(@group_stack.last.depth + 1)\n    @group_stack.push group\n    @group_queue.enq group\n    begin\n      yield\n    ensure\n      @group_stack.pop\n      if group.breakables.empty?\n        @group_queue.delete group\n      end\n    end\n  end\n\n  # Increases the indentation for breakables inside the current group.\n  def nest(indent = 1, &)\n    @indent += indent\n    begin\n      yield\n    ensure\n      @indent -= indent\n    end\n  end\n\n  # Same as:\n  #\n  # ```\n  # text \",\"\n  # breakable\n  # ```\n  def comma : Nil\n    text \",\"\n    breakable\n  end\n\n  # Appends a group that is surrounded by the given *left* and *right*\n  # objects, and optionally is surrounded by the given breakable\n  # objects.\n  def surround(left, right, left_break = \"\", right_break = \"\", &) : Nil\n    group(1, left, right) do\n      breakable left_break if left_break\n      yield\n      breakable right_break if right_break\n    end\n  end\n\n  # Appends a list of elements surrounded by *left* and *right*\n  # and separated by commas, yielding each element to the given block.\n  def list(left, elements, right, &) : Nil\n    group(1, left, right) do\n      elements.each_with_index do |elem, i|\n        comma if i > 0\n        yield elem\n      end\n    end\n  end\n\n  # Appends a list of elements surrounded by *left* and *right*\n  # and separated by commas.\n  def list(left, elements, right) : Nil\n    list(left, elements, right) do |elem|\n      elem.pretty_print(self)\n    end\n  end\n\n  # Outputs any buffered data.\n  def flush : Nil\n    @buffer.each do |data|\n      @output_width = data.output(@output, @output_width)\n    end\n    @buffer.clear\n    @buffer_width = 0\n\n    @output.flush\n  end\n\n  private class Text\n    getter width\n\n    def initialize\n      @objs = [] of String\n      @width = 0\n    end\n\n    def output(io, output_width)\n      @objs.each { |obj| io << obj }\n      output_width + @width\n    end\n\n    def add(obj, width)\n      @objs << obj.to_s\n      @width += width\n    end\n  end\n\n  private class Breakable\n    @indent : Int32\n    @group : Group\n    getter width\n\n    def initialize(@obj : String, @width : Int32, @pp : PrettyPrint)\n      @indent = @pp.indent\n      @group = @pp.current_group\n      @group.breakables.push self\n    end\n\n    def output(io, output_width)\n      @group.breakables.shift\n      if @group.break?\n        io << @pp.newline\n        @indent.times { io << ' ' }\n        @indent\n      else\n        @pp.group_queue.delete @group if @group.breakables.empty?\n        io << @obj\n        output_width + @width\n      end\n    end\n  end\n\n  private class Group\n    getter depth\n    getter breakables\n    getter? :break\n\n    def initialize(@depth : Int32)\n      @breakables = Deque(Breakable).new\n      @break = false\n    end\n\n    def break : Nil\n      @break = true\n    end\n  end\n\n  private class GroupQueue\n    def initialize\n      @queue = [] of Array(Group)\n    end\n\n    def enq(group)\n      depth = group.depth\n      until depth < @queue.size\n        @queue << [] of Group\n      end\n      @queue[depth] << group\n    end\n\n    def deq\n      @queue.each do |gs|\n        (gs.size - 1).downto(0) do |i|\n          unless gs[i].breakables.empty?\n            group = gs.delete_at(i)\n            group.break\n            return group\n          end\n        end\n        gs.each &.break\n        gs.clear\n      end\n      nil\n    end\n\n    def delete(group)\n      @queue[group.depth].delete(group)\n    end\n  end\n\n  # Pretty prints *obj* into *io* with the given\n  # *width* as a limit and starting with\n  # the given *indent*ation.\n  def self.format(obj, io : IO, width : Int32, newline = \"\\n\", indent = 0)\n    format(io, width, newline, indent) do |printer|\n      obj.pretty_print(printer)\n    end\n  end\n\n  # Creates a pretty printer and yields it to the block,\n  # appending any output to the given *io*.\n  def self.format(io : IO, width : Int32, newline = \"\\n\", indent = 0, &)\n    printer = PrettyPrint.new(io, width, newline, indent)\n    yield printer\n    printer.flush\n    io\n  end\nend\n"
  },
  {
    "path": "src/primitives.cr",
    "content": "# Methods defined here are primitives because they either:\n# * can't be expressed in Crystal (need to be expressed in LLVM). For example unary\n#   and binary math operators fall into this category.\n# * should always be inlined with an LLVM instruction for performance reasons, even\n#   in non-release builds. An example of this is `Char#ord`, which could be implemented\n#   in Crystal by assigning `self` to a variable and casting a pointer to it to `Int32`,\n#   and then reading back the value.\n\nclass Object\n  # Returns the **runtime** `Class` of an object.\n  #\n  # ```\n  # 1.class       # => Int32\n  # \"hello\".class # => String\n  # ```\n  #\n  # Compare it with `typeof`, which returns the **compile-time** type of an object:\n  #\n  # ```\n  # random_value = rand # => 0.627423\n  # value = random_value < 0.5 ? 1 : \"hello\"\n  # value         # => \"hello\"\n  # value.class   # => String\n  # typeof(value) # => Int32 | String\n  # ```\n  @[Primitive(:class)]\n  def class\n  end\n\n  # :nodoc:\n  @[Primitive(:object_crystal_type_id)]\n  def crystal_type_id : Int32\n  end\nend\n\nclass Reference\n  # Returns a `UInt64` that uniquely identifies this object.\n  #\n  # The returned value is the memory address of this object.\n  #\n  # ```\n  # string = \"hello\"\n  # string.object_id # => 4460249568\n  #\n  # pointer = Pointer(String).new(string.object_id)\n  # string2 = pointer.as(String)\n  # string2.object_id == string.object_id # => true\n  # ```\n  @[Primitive(:object_id)]\n  def object_id : UInt64\n  end\n\n  # Performs basic initialization so that the given *address* is ready for use\n  # as an object's instance data. Returns *address* cast to `self`'s type.\n  #\n  # More specifically, this is the part of object initialization that occurs\n  # after memory allocation and before calling one of the `#initialize`\n  # overloads. It zeroes the memory, sets up the type ID (necessary for dynamic\n  # dispatch), and then runs all inline instance variable initializers.\n  #\n  # *address* must point to a buffer of at least `instance_sizeof(self)` bytes\n  # with an alignment of `instance_alignof(self)` or above. `ReferenceStorage`\n  # fulfils both requirements.\n  #\n  # WARNING: The caller is responsible for managing the memory at the given\n  # *address*, in particular if the memory is not garbage-collectable.\n  #\n  # ```\n  # class Foo\n  #   getter i : Int64\n  #   getter str = \"abc\"\n  #\n  #   def initialize(@i)\n  #   end\n  #\n  #   def self.alloc_with_libc(i : Int64)\n  #     foo_buffer = LibC.malloc(instance_sizeof(Foo))\n  #     foo = Foo.pre_initialize(foo_buffer)\n  #     foo.i                  # => 0\n  #     foo.str                # => \"abc\"\n  #     (foo || \"\").is_a?(Foo) # => true\n  #\n  #     foo.initialize(i) # okay\n  #     foo\n  #   end\n  # end\n  #\n  # foo = Foo.alloc_with_libc(123_i64)\n  # foo.i # => 123\n  # ```\n  #\n  # See also: `Reference.unsafe_construct`, `Struct.pre_initialize`.\n  @[Experimental(\"This API is still under development. Join the discussion about custom reference allocation at [#13481](https://github.com/crystal-lang/crystal/issues/13481).\")]\n  @[Primitive(:pre_initialize)]\n  {% if compare_versions(Crystal::VERSION, \"1.2.0\") >= 0 %}\n    def self.pre_initialize(address : Pointer)\n      # This ensures `.pre_initialize` is instantiated for every subclass,\n      # otherwise all calls will refer to the sole instantiation in\n      # `Reference.class`. This is necessary when the receiver is a virtual\n      # metaclass type. Apparently this works even for primitives\n      \\{% @type %}\n    end\n  {% else %}\n    # Primitives cannot have a body until 1.2.0 (#11147)\n    def self.pre_initialize(address : Pointer)\n    end\n  {% end %}\nend\n\nstruct Struct\n  # Performs basic initialization so that the given *address* is ready for use\n  # as an object's instance data.\n  #\n  # More specifically, this is the part of object initialization that occurs\n  # after memory allocation and before calling one of the `#initialize`\n  # overloads. It zeroes the memory, and then runs all inline instance variable\n  # initializers.\n  #\n  # *address* must point to a buffer of at least `sizeof(self)` bytes with an\n  # alignment of `alignof(self)` or above. This can for example be a pointer to\n  # an uninitialized instance.\n  #\n  # This method only works for non-virtual constructions. Neither the struct\n  # type nor *address*'s pointee type can be an abstract struct.\n  #\n  # ```\n  # struct Foo\n  #   getter i : Int64\n  #   getter str = \"abc\"\n  #\n  #   def initialize(@i)\n  #   end\n  # end\n  #\n  # foo = uninitialized Foo\n  # pointerof(foo).to_slice(1).to_unsafe_bytes.fill(0xFF)\n  # Foo.pre_initialize(pointerof(foo))\n  # foo # => Foo(@i=0, @str=\"abc\")\n  # ```\n  #\n  # See also: `Reference.pre_initialize`.\n  @[Experimental(\"This API is still under development. Join the discussion about custom reference allocation at [#13481](https://github.com/crystal-lang/crystal/issues/13481).\")]\n  @[Primitive(:pre_initialize)]\n  {% if compare_versions(Crystal::VERSION, \"1.2.0\") >= 0 %}\n    def self.pre_initialize(address : Pointer) : Nil\n      \\{% @type %}\n    end\n  {% else %}\n    def self.pre_initialize(address : Pointer) : Nil\n    end\n  {% end %}\nend\n\nclass Class\n  # :nodoc:\n  @[Primitive(:class_crystal_instance_type_id)]\n  def crystal_instance_type_id : Int32\n  end\nend\n\nstruct Bool\n  # Returns `true` if `self` is equal to *other*.\n  @[Primitive(:binary)]\n  def ==(other : Bool) : Bool\n  end\n\n  # Returns `true` if `self` is not equal to *other*.\n  @[Primitive(:binary)]\n  def !=(other : Bool) : Bool\n  end\nend\n\nstruct Char\n  # Returns the codepoint of this char.\n  #\n  # The codepoint is the integer representation.\n  # The Universal Coded Character Set (UCS) standard, commonly known as Unicode,\n  # assigns names and meanings to numbers, these numbers are called codepoints.\n  #\n  # For values below and including 127 this matches the ASCII codes\n  # and thus its byte representation.\n  #\n  # ```\n  # 'a'.ord      # => 97\n  # '\\0'.ord     # => 0\n  # '\\u007f'.ord # => 127\n  # '☃'.ord      # => 9731\n  # ```\n  @[Primitive(:convert)]\n  def ord : Int32\n  end\n\n  {% for op, desc in {\n                       \"==\" => \"equal to\",\n                       \"!=\" => \"not equal to\",\n                       \"<\"  => \"less than\",\n                       \"<=\" => \"less than or equal to\",\n                       \">\"  => \"greater than\",\n                       \">=\" => \"greater than or equal to\",\n                     } %}\n    # Returns `true` if `self`'s codepoint is {{desc.id}} *other*'s codepoint.\n    @[Primitive(:binary)]\n    def {{op.id}}(other : Char) : Bool\n    end\n  {% end %}\nend\n\nstruct Symbol\n  # Returns `true` if `self` is equal to *other*.\n  @[Primitive(:binary)]\n  def ==(other : Symbol) : Bool\n  end\n\n  # Returns `true` if `self` is not equal to *other*.\n  @[Primitive(:binary)]\n  def !=(other : Symbol) : Bool\n  end\n\n  # Returns a unique number for this symbol.\n  @[Primitive(:convert)]\n  def to_i : Int32\n  end\n\n  # Returns the symbol's name as a String.\n  #\n  # ```\n  # :foo.to_s           # => \"foo\"\n  # :\"hello world\".to_s # => \"hello world\"\n  # ```\n  @[Primitive(:symbol_to_s)]\n  def to_s : String\n  end\nend\n\nstruct Pointer(T)\n  # Allocates `size * sizeof(T)` bytes from the system's heap initialized\n  # to zero and returns a pointer to the first byte from that memory.\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # ```\n  # # Allocate memory for an Int32: 4 bytes\n  # ptr = Pointer(Int32).malloc(1_u64)\n  # ptr.value # => 0\n  #\n  # # Allocate memory for 10 Int32: 40 bytes\n  # ptr = Pointer(Int32).malloc(10_u64)\n  # ptr[0] # => 0\n  # # ...\n  # ptr[9] # => 0\n  # ```\n  #\n  # The implementation uses `GC.malloc` if the compiler is aware that the\n  # allocated type contains inner address pointers. See\n  # `Crystal::Macros::TypeNode#has_inner_pointers?` for details.\n  #\n  # To override this implicit behaviour, `GC.malloc` and `GC.malloc_atomic`\n  # can be used directly instead.\n  @[Primitive(:pointer_malloc)]\n  def self.malloc(size : UInt64)\n  end\n\n  # Returns a pointer that points to the given memory address.\n  # This doesn't allocate memory.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(5678_u64)\n  # ptr.address # => 5678\n  # ```\n  @[Primitive(:pointer_new)]\n  def self.new(address : UInt64)\n  end\n\n  # Gets the value pointed by this pointer.\n  #\n  # ```\n  # ptr = Pointer(Int32).malloc(4)\n  # ptr.value = 42\n  # ptr.value # => 42\n  # ```\n  #\n  # WARNING: The pointer must be appropriately aligned, i.e. `address` must be\n  # a multiple of `alignof(T)`. It is undefined behavior to load from a\n  # misaligned pointer. Such reads should instead be done via a cast to\n  # `Pointer(UInt8)`, which is guaranteed to have byte alignment:\n  #\n  # ```\n  # # raises SIGSEGV on X86 if `ptr` is misaligned\n  # x = ptr.as(UInt128*).value\n  #\n  # # okay, `ptr` can have any alignment\n  # x = uninitialized UInt128\n  # ptr.as(UInt8*).copy_to(pointerof(x).as(UInt8*), sizeof(typeof(x)))\n  # ```\n  @[Primitive(:pointer_get)]\n  def value : T\n  end\n\n  # Sets the value pointed by this pointer.\n  #\n  # ```\n  # ptr = Pointer(Int32).malloc(4)\n  # ptr.value = 42\n  # ptr.value # => 42\n  # ```\n  #\n  # WARNING: The pointer must be appropriately aligned, i.e. `address` must be\n  # a multiple of `alignof(T)`. It is undefined behavior to store to a\n  # misaligned pointer. Such writes should instead be done via a cast to\n  # `Pointer(UInt8)`, which is guaranteed to have byte alignment:\n  #\n  # ```\n  # # raises SIGSEGV on X86 if `ptr` is misaligned\n  # x = 123_u128\n  # ptr.as(UInt128*).value = x\n  #\n  # # okay, `ptr` can have any alignment\n  # ptr.as(UInt8*).copy_from(pointerof(x).as(UInt8*), sizeof(typeof(x)))\n  # ```\n  @[Primitive(:pointer_set)]\n  def value=(value : T)\n  end\n\n  # Returns the address of this pointer.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(1234)\n  # ptr.address # => 1234\n  # ```\n  @[Primitive(:pointer_address)]\n  def address : UInt64\n  end\n\n  # Tries to change the size of the allocation pointed to by this pointer to *size*,\n  # and returns that pointer.\n  #\n  # Since the space after the end of the block may be in use, realloc may find it\n  # necessary to copy the block to a new address where more free space is available.\n  # The value of realloc is the new address of the block.\n  # If the block needs to be moved, realloc copies the old contents.\n  #\n  # Remember to always assign the value of realloc.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4]\n  # ptr = ptr.realloc(8_u8)\n  # ptr # [1, 2, 3, 4, 0, 0, 0, 0]\n  # ```\n  @[Primitive(:pointer_realloc)]\n  def realloc(size : UInt64) : self\n  end\n\n  # Returns a new pointer whose address is this pointer's address\n  # incremented by `offset * sizeof(T)`.\n  #\n  # ```\n  # ptr = Pointer(Int32).new(1234)\n  # ptr.address # => 1234\n  #\n  # # An Int32 occupies four bytes\n  # ptr2 = ptr + 1_u64\n  # ptr2.address # => 1238\n  # ```\n  @[Primitive(:pointer_add)]\n  def +(offset : Int64) : self\n  end\n\n  # Returns how many T elements are there between this pointer and *other*.\n  # That is, this is `(self.address - other.address) / sizeof(T)`.\n  #\n  # ```\n  # ptr1 = Pointer(Int32).malloc(4)\n  # ptr2 = ptr1 + 2\n  # ptr2 - ptr1 # => 2\n  # ```\n  @[Primitive(:pointer_diff)]\n  def -(other : self) : Int64\n  end\nend\n\nstruct Slice(T)\n  # Constructs a read-only `Slice` constant from the given *args*. The slice\n  # contents are stored in the program's read-only data section.\n  #\n  # If `T` is specified, it must be one of the `Number::Primitive` types and\n  # cannot be a union. The *args* must all be number literals that fit into\n  # `T`'s range, as if they are autocasted into `T`.\n  #\n  # If `T` is not specified, it is inferred from *args*, which must all be\n  # number literals of the same type, and cannot be empty.\n  #\n  # ```\n  # x = Slice(UInt8).literal(0, 1, 4, 9, 16, 25)\n  # x            # => Slice[0, 1, 4, 9, 16, 25]\n  # x.read_only? # => true\n  #\n  # Slice.literal(1_u8, 2_u8, 3_u8) # => Bytes[1, 2, 3]\n  # ```\n  @[Experimental(\"Slice literals are still under development. Join the discussion at [#2886](https://github.com/crystal-lang/crystal/issues/2886).\")]\n  @[Primitive(:slice_literal)]\n  def self.literal(*args)\n  end\nend\n\nstruct Proc\n  # Invokes this `Proc` and returns the result.\n  #\n  # ```\n  # add = ->(x : Int32, y : Int32) { x + y }\n  # add.call(1, 2) # => 3\n  # ```\n  @[Primitive(:proc_call)]\n  @[Raises]\n  def call(*args : *T) : R\n  end\nend\n\n# All `Number` methods are defined on concrete structs (for example `Int32`, `UInt8`, etc.),\n# never on `Number`, `Int` or `Float` because we don't want to handle a primitive for\n# other types that could extend these types (for example `BigInt`): if we do that\n# a compiler crash will happen.\n#\n# A similar logic is applied to method arguments: they are always concrete, to avoid\n# unintentionally handling a `BigInt` and have a crash. We also can't have an argument\n# be a union, because the codegen primitives always consider primitive types, never\n# unions.\n\n{% begin %}\n  {% ints = %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128) %}\n  {% floats = %w(Float32 Float64) %}\n  {% nums = %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128 Float32 Float64) %}\n  {% binaries = {\"+\" => \"adding\", \"-\" => \"subtracting\", \"*\" => \"multiplying\"} %}\n\n  {% for num in nums %}\n    struct {{num.id}}\n      {% for name, type in {\n                             to_i: Int32, to_u: UInt32, to_f: Float64,\n                             to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,\n                             to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,\n                             to_f32: Float32, to_f64: Float64,\n                           } %}\n        # Returns `self` converted to `{{type}}`.\n        # Raises `OverflowError` in case of overflow.\n        @[::Primitive(:convert)]\n        @[Raises]\n        def {{name.id}} : {{type}}\n        end\n\n        # Returns `self` converted to `{{type}}`.\n        # In case of overflow\n        # {% if ints.includes?(num) %} a wrapping is performed.\n        # {% elsif type < Int %} the result is undefined.\n        # {% else %} infinity is returned.\n        # {% end %}\n        @[::Primitive(:unchecked_convert)]\n        def {{name.id}}! : {{type}}\n        end\n      {% end %}\n\n      {% for num2 in nums %}\n        {% for op, desc in {\n                             \"==\" => \"equal to\",\n                             \"!=\" => \"not equal to\",\n                             \"<\"  => \"less than\",\n                             \"<=\" => \"less than or equal to\",\n                             \">\"  => \"greater than\",\n                             \">=\" => \"greater than or equal to\",\n                           } %}\n          # Returns `true` if `self` is {{desc.id}} *other*{% if op == \"!=\" && (!ints.includes?(num) || !ints.includes?(num2)) %}\n          # or if `self` and *other* are unordered{% end %}.\n          @[::Primitive(:binary)]\n          def {{op.id}}(other : {{num2.id}}) : Bool\n          end\n        {% end %}\n      {% end %}\n    end\n  {% end %}\n\n  {% for int in ints %}\n    struct {{int.id}}\n      # Returns a `Char` that has the unicode codepoint of `self`,\n      # without checking if this integer is in the range valid for\n      # chars (`0..0xd7ff` and `0xe000..0x10ffff`). In case of overflow\n      # a wrapping is performed.\n      #\n      # You should never use this method unless `chr` turns out to\n      # be a bottleneck.\n      #\n      # ```\n      # 97.unsafe_chr # => 'a'\n      # ```\n      @[::Primitive(:unchecked_convert)]\n      def unsafe_chr : Char\n      end\n\n      {% for int2 in ints %}\n        {% for op, desc in binaries %}\n          # Returns the result of {{desc.id}} `self` and *other*.\n          # Raises `OverflowError` in case of overflow.\n          @[::Primitive(:binary)]\n          @[Raises]\n          def {{op.id}}(other : {{int2.id}}) : self\n          end\n\n          # Returns the result of {{desc.id}} `self` and *other*.\n          # In case of overflow a wrapping is performed.\n          @[::Primitive(:binary)]\n          def &{{op.id}}(other : {{int2.id}}) : self\n          end\n        {% end %}\n\n        # Returns the result of performing a bitwise OR of `self`'s and *other*'s bits.\n        @[::Primitive(:binary)]\n        def |(other : {{int2.id}}) : self\n        end\n\n        # Returns the result of performing a bitwise AND of `self`'s and *other*'s bits.\n        @[::Primitive(:binary)]\n        def &(other : {{int2.id}}) : self\n        end\n\n        # Returns the result of performing a bitwise XOR of `self`'s and *other*'s bits.\n        @[::Primitive(:binary)]\n        def ^(other : {{int2.id}}) : self\n        end\n\n        # :nodoc:\n        @[::Primitive(:binary)]\n        def unsafe_shl(other : {{int2.id}}) : self\n        end\n\n        # :nodoc:\n        @[::Primitive(:binary)]\n        def unsafe_shr(other : {{int2.id}}) : self\n        end\n\n        # :nodoc:\n        @[::Primitive(:binary)]\n        def unsafe_div(other : {{int2.id}}) : self\n        end\n\n        # :nodoc:\n        @[::Primitive(:binary)]\n        def unsafe_mod(other : {{int2.id}}) : self\n        end\n      {% end %}\n\n      {% for float in floats %}\n        {% for op, desc in binaries %}\n          # Returns the result of {{desc.id}} `self` and *other*.\n          @[::Primitive(:binary)]\n          def {{op.id}}(other : {{float.id}}) : {{float.id}}\n          end\n        {% end %}\n      {% end %}\n    end\n  {% end %}\n\n  {% for float in floats %}\n    struct {{float.id}}\n      {% for num in nums %}\n        {% for op, desc in binaries %}\n          # Returns the result of {{desc.id}} `self` and *other*.\n          @[::Primitive(:binary)]\n          def {{op.id}}(other : {{num.id}}) : self\n          end\n        {% end %}\n\n        # Returns the float division of `self` and *other*.\n        @[::Primitive(:binary)]\n        def fdiv(other : {{num.id}}) : self\n        end\n      {% end %}\n\n      # Returns the result of division `self` and *other*.\n      @[::Primitive(:binary)]\n      def /(other : {{float.id}}) : {{float.id}}\n      end\n    end\n  {% end %}\n{% end %}\n"
  },
  {
    "path": "src/proc.cr",
    "content": "# A `Proc` represents a function pointer with an optional context (the closure data).\n# It is typically created with a proc literal:\n#\n# ```\n# # A proc without arguments\n# -> { 1 } # Proc(Int32)\n#\n# # A proc with one argument\n# ->(x : Int32) { x.to_s } # Proc(Int32, String)\n#\n# # A proc with two arguments:\n# ->(x : Int32, y : Int32) { x + y } # Proc(Int32, Int32, Int32)\n# ```\n#\n# See [`Proc` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/proc.html) in the language reference.\n#\n# The types of the arguments (`T`) are mandatory, except when directly\n# sending a proc literal to a lib fun in C bindings.\n#\n# The return type (`R`) is inferred from the proc's body.\n#\n# A special new method is provided too:\n#\n# ```\n# Proc(Int32, String).new { |x| x.to_s } # Proc(Int32, String)\n# ```\n#\n# This form allows you to specify the return type and to check it\n# against the proc's body.\n#\n# Another way to create a `Proc` is by capturing a block:\n#\n# ```\n# def capture(&block : Int32 -> Int32)\n#   # block argument is used, so block is turned into a Proc\n#   block\n# end\n#\n# proc = capture { |x| x + 1 } # Proc(Int32, Int32)\n# proc.call(1)                 # => 2\n# ```\n#\n# When capturing blocks, the type of the arguments and return type\n# must be specified in the capturing method block signature.\n#\n# ### Passing a Proc to a C function\n#\n# Passing a `Proc` to a C function, for example as a callback, is possible\n# as long as the `Proc` isn't a closure. If it is, either a compile-time or\n# runtime error will happen depending on whether the compiler can check this.\n# The reason is that a `Proc` is internally represented as two void pointers,\n# one having the function pointer and another the closure data. If just\n# the function pointer is passed, the closure data will be missing at invocation time.\n#\n# Most of the time a C function that allows setting a callback also provide\n# an argument for custom data. This custom data is then sent as an argument\n# to the callback. For example, suppose a C function that invokes a callback\n# at every tick, passing that tick:\n#\n# ```\n# lib LibTicker\n#   fun on_tick(callback : (Int32, Void* ->), data : Void*)\n# end\n# ```\n#\n# To properly define a wrapper for this function we must send the `Proc` as the\n# callback data, and then convert that callback data to the `Proc` and finally invoke it.\n#\n# ```\n# module Ticker\n#   # The callback for the user doesn't have a Void*\n#   @@box = Pointer(Void).null\n#\n#   def self.on_tick(&callback : Int32 ->)\n#     # Since Proc is a {Void*, Void*}, we can't turn that into a Void*, so we\n#     # \"box\" it: we allocate memory and store the Proc there\n#     boxed_data = Box.box(callback)\n#\n#     # We must save this in Crystal-land so the GC doesn't collect it (*)\n#     @@box = boxed_data\n#\n#     # We pass a callback that doesn't form a closure, and pass the boxed_data as\n#     # the callback data\n#     LibTicker.on_tick(->(tick, data) {\n#       # Now we turn data back into the Proc, using Box.unbox\n#       data_as_callback = Box(typeof(callback)).unbox(data)\n#       # And finally invoke the user's callback\n#       data_as_callback.call(tick)\n#     }, boxed_data)\n#   end\n# end\n#\n# Ticker.on_tick do |tick|\n#   puts tick\n# end\n# ```\n#\n# Note that we save the box in `@@box`. The reason is that if we don't do it,\n# and our code doesn't reference it anymore, the GC will collect it.\n# The C library will of course store the callback, but Crystal's GC has\n# no way of knowing that.\nstruct Proc\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_proc_new)] {% end %}\n  def self.new(pointer : Void*, closure_data : Void*) : self\n    func = {pointer, closure_data}\n    ptr = pointerof(func).as(self*)\n    ptr.value\n  end\n\n  # Creates a `Proc` by capturing the given *block*.\n  #\n  # The block argument types are inferred from the `Proc`'s type arguments. The\n  # return type of the block must match the return type specified in the `Proc`\n  # type.\n  #\n  # ```\n  # gt = Proc(Int32, Int32, Bool).new do |x, y|\n  #   x > y\n  # end\n  # gt.call(3, 1) # => true\n  # gt.call(1, 2) # => false\n  # ```\n  def self.new(&block : self)\n    block\n  end\n\n  # Returns a new `Proc` that has its first arguments fixed to\n  # the values given by *args*.\n  #\n  # See [Wikipedia, Partial application](https://en.wikipedia.org/wiki/Partial_application)\n  #\n  # ```\n  # add = ->(x : Int32, y : Int32) { x + y }\n  # add.call(1, 2) # => 3\n  #\n  # add_one = add.partial(1)\n  # add_one.call(2)  # => 3\n  # add_one.call(10) # => 11\n  #\n  # add_one_and_two = add_one.partial(2)\n  # add_one_and_two.call # => 3\n  # ```\n  def partial(*args : *U) forall U\n    {% begin %}\n      {% remaining = (T.size - U.size) %}\n      ->(\n          {% for i in 0...remaining %}\n            arg{{i}} : {{T[i + U.size]}},\n          {% end %}\n        ) {\n        call(\n          *args,\n          {% for i in 0...remaining %}\n            arg{{i}},\n          {% end %}\n        )\n      }\n    {% end %}\n  end\n\n  # Returns the number of arguments of this `Proc`.\n  #\n  # ```\n  # add = ->(x : Int32, y : Int32) { x + y }\n  # add.arity # => 2\n  #\n  # neg = ->(x : Int32) { -x }\n  # neg.arity # => 1\n  # ```\n  def arity\n    {{T.size}}\n  end\n\n  def pointer : Void*\n    internal_representation[0]\n  end\n\n  def closure_data : Void*\n    internal_representation[1]\n  end\n\n  def closure? : Bool\n    !closure_data.null?\n  end\n\n  private def internal_representation\n    func = self\n    ptr = pointerof(func).as({Void*, Void*}*)\n    ptr.value\n  end\n\n  def ==(other : self)\n    pointer == other.pointer && closure_data == other.closure_data\n  end\n\n  def ===(other : self)\n    self == other\n  end\n\n  def ===(other)\n    call(other)\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    internal_representation.hash(hasher)\n  end\n\n  def clone\n    self\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"#<\"\n    io << {{@type.name.stringify}}\n    io << \":0x\"\n    pointer.address.to_s(io, 16)\n    if closure?\n      io << \":closure\"\n    end\n    io << '>'\n    nil\n  end\n\n  # Invokes this `Proc` and returns the result.\n  #\n  # ```\n  # add = ->(x : Int32, y : Int32) { x + y }\n  # add[1, 2] # => 3\n  # ```\n  #\n  # Same as `#call`.\n  def [](*args : *T) : R\n    call(*args)\n  end\nend\n"
  },
  {
    "path": "src/process/executable_path.cr",
    "content": "# Reference:\n# - https://github.com/gpakosz/whereami/blob/master/src/whereami.c\n# - http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe\n\nclass Process\n  {% if flag?(:windows) %}\n    PATH_DELIMITER = ';'\n  {% else %}\n    PATH_DELIMITER = ':'\n  {% end %}\n\n  # :nodoc:\n  INITIAL_PATH = ENV[\"PATH\"]?\n\n  # :nodoc:\n  #\n  # Working directory at program start. Nil if working directory does not exist.\n  #\n  # Used for `Exception::CallStack::CURRENT_DIR`\n  # and `Process.executable_path_impl` on openbsd.\n  INITIAL_PWD = begin\n    Dir.current\n  rescue File::NotFoundError\n    nil\n  end\n\n  # Returns an absolute path to the executable file of the currently running\n  # program. This is in opposition to `PROGRAM_NAME` which may be a relative or\n  # absolute path, just the executable file name or a symlink.\n  #\n  # The executable path will be canonicalized (all symlinks and relative paths\n  # will be expanded).\n  #\n  # Returns `nil` if the file can't be found.\n  def self.executable_path : String?\n    if executable = executable_path_impl\n      begin\n        File.realpath(executable)\n      rescue File::Error\n      end\n    end\n  end\n\n  private def self.file_executable?(path)\n    unless File.info?(path, follow_symlinks: true).try &.file?\n      return false\n    end\n    {% if flag?(:win32) %}\n      # This is *not* a temporary stub.\n      # Windows doesn't have \"executable\" metadata for files, so it also doesn't have files that are \"not executable\".\n      true\n    {% else %}\n      File::Info.executable?(path)\n    {% end %}\n  end\n\n  # Searches an executable, checking for an absolute path, a path relative to\n  # *pwd* or absolute path, then eventually searching in directories declared\n  # in *path*.\n  def self.find_executable(name : Path | String, path : String? = ENV[\"PATH\"]?, pwd : Path | String = Dir.current) : String?\n    find_executable_possibilities(Path.new(name), path, pwd) do |p|\n      return p.to_s if file_executable?(p)\n    end\n    nil\n  end\n\n  private def self.find_executable_possibilities(name, path, pwd, &)\n    return if name.to_s.empty?\n\n    {% if flag?(:win32) %}\n      # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters\n      # > If the file name does not contain an extension, .exe is appended.\n      # See find_executable_spec.cr for cases this needs to match, based on CreateProcessW behavior.\n      basename = name.ends_with_separator? ? \"\" : name.basename\n      basename = \"\" if basename == name.anchor.to_s\n      if (basename.empty? ? !name.anchor : !basename.includes?(\".\"))\n        name = Path.new(\"#{name}.exe\")\n      end\n    {% end %}\n\n    if name.absolute?\n      yield name\n    end\n\n    # check if the name includes a separator\n    count_parts = 0\n    name.each_part do\n      count_parts += 1\n      break if count_parts > 1\n    end\n    has_separator = (count_parts > 1)\n\n    if {{ flag?(:win32) }} || has_separator\n      yield name.expand(pwd)\n    end\n\n    if path && !has_separator\n      path.split(PATH_DELIMITER) do |path_entry|\n        yield Path.new(path_entry, name)\n      end\n    end\n  end\nend\n\n{% if flag?(:darwin) %}\n  require \"c/limits\"\n\n  lib LibC\n    fun _NSGetExecutablePath(buf : Char*, bufsize : UInt32*) : Int\n  end\n\n  class Process\n    private def self.executable_path_impl\n      buf = GC.malloc_atomic(LibC::PATH_MAX).as(UInt8*)\n      size = LibC::PATH_MAX.to_u32\n\n      if LibC._NSGetExecutablePath(buf, pointerof(size)) == -1\n        buf = GC.malloc_atomic(size).as(UInt8*)\n        return nil if LibC._NSGetExecutablePath(buf, pointerof(size)) == -1\n      end\n\n      String.new(buf)\n    end\n  end\n{% elsif flag?(:freebsd) || flag?(:dragonfly) %}\n  require \"c/sysctl\"\n\n  class Process\n    private def self.executable_path_impl\n      mib = Int32[LibC::CTL_KERN, LibC::KERN_PROC, LibC::KERN_PROC_PATHNAME, -1]\n      buf = GC.malloc_atomic(LibC::PATH_MAX).as(UInt8*)\n      size = LibC::SizeT.new(LibC::PATH_MAX)\n\n      if LibC.sysctl(mib, 4, buf, pointerof(size), nil, 0) == 0\n        String.new(buf, size - 1)\n      end\n    end\n  end\n{% elsif flag?(:netbsd) %}\n  require \"c/sysctl\"\n\n  class Process\n    private def self.executable_path_impl\n      mib = Int32[LibC::CTL_KERN, LibC::KERN_PROC_ARGS, -1, LibC::KERN_PROC_PATHNAME]\n      buf = GC.malloc_atomic(LibC::PATH_MAX).as(UInt8*)\n      size = LibC::SizeT.new(LibC::PATH_MAX)\n\n      if LibC.sysctl(mib, 4, buf, pointerof(size), nil, 0) == 0\n        String.new(buf, size - 1)\n      end\n    end\n  end\n{% elsif flag?(:linux) %}\n  class Process\n    private def self.executable_path_impl\n      \"/proc/self/exe\"\n    end\n  end\n{% elsif flag?(:win32) %}\n  require \"crystal/system/windows\"\n  require \"c/libloaderapi\"\n\n  class Process\n    private def self.executable_path_impl\n      Crystal::System.retry_wstr_buffer do |buffer, small_buf|\n        len = LibC.GetModuleFileNameW(nil, buffer, buffer.size)\n        if 0 < len < buffer.size\n          break String.from_utf16(buffer[0, len])\n        elsif small_buf && len == buffer.size\n          next 32767 # big enough. 32767 is the maximum total path length of UNC path.\n        else\n          break nil\n        end\n      end\n    end\n  end\n{% else %}\n  # openbsd, ...\n  class Process\n    private def self.executable_path_impl\n      if pwd = INITIAL_PWD\n        find_executable(PROGRAM_NAME, INITIAL_PATH, pwd)\n      end\n    end\n  end\n{% end %}\n"
  },
  {
    "path": "src/process/shell.cr",
    "content": "class Process\n  # Converts a sequence of strings to one joined string with each argument shell-quoted.\n  #\n  # This is then safe to pass as part of the command when using `shell: true` or `system()`.\n  #\n  # NOTE: The actual return value is system-dependent, so it mustn't be relied on in other contexts.\n  # See also: `quote_posix`.\n  #\n  # ```\n  # files = [\"my file.txt\", \"another.txt\"]\n  # `grep -E 'fo+' -- #{Process.quote(files)}`\n  # ```\n  def self.quote(args : Enumerable(String)) : String\n    {% if flag?(:win32) %}\n      quote_windows(args)\n    {% else %}\n      quote_posix(args)\n    {% end %}\n  end\n\n  # Shell-quotes one item, same as `quote({arg})`.\n  def self.quote(arg : String) : String\n    quote({arg})\n  end\n\n  # Converts a sequence of strings to one joined string with each argument shell-quoted.\n  #\n  # This is then safe to pass to a POSIX shell.\n  #\n  # ```\n  # files = [\"my file.txt\", \"another.txt\"]\n  # Process.quote_posix(files) # => \"'my file.txt' another.txt\"\n  # ```\n  def self.quote_posix(args : Enumerable(String)) : String\n    args.join(' ') do |arg|\n      if arg.empty?\n        \"''\"\n      elsif arg.matches? %r([^a-zA-Z0-9%+,\\-./:=@_]) # not all characters are safe, needs quoting\n        \"'\" + arg.gsub(\"'\", %('\"'\"')) + \"'\"          # %(foo'ba#r) becomes %('foo'\"'\"'ba#r')\n      else\n        arg\n      end\n    end\n  end\n\n  # Shell-quotes one item, same as `quote_posix({arg})`.\n  def self.quote_posix(arg : String) : String\n    quote_posix({arg})\n  end\n\n  # :nodoc:\n  #\n  # Converts a sequence of strings to one joined string with each argument shell-quoted.\n  #\n  # This is then safe to pass Windows API CreateProcess.\n  #\n  # NOTE: This is **not** safe to pass to the CMD shell.\n  #\n  # ```\n  # files = [\"my file.txt\", \"another.txt\"]\n  # Process.quote_windows(files) # => %(\"my file.txt\" another.txt)\n  # ```\n  def self.quote_windows(args : Enumerable(String)) : String\n    String.build { |io| quote_windows(io, args) }\n  end\n\n  private def self.quote_windows(io : IO, args)\n    args.join(io, ' ') do |arg|\n      need_quotes = arg.empty? || arg.includes?(' ') || arg.includes?('\\t')\n\n      io << '\"' if need_quotes\n\n      slashes = 0\n      arg.each_char do |c|\n        case c\n        when '\\\\'\n          slashes += 1\n        when '\"'\n          (slashes + 1).times { io << '\\\\' }\n          slashes = 0\n        else\n          slashes = 0\n        end\n\n        io << c\n      end\n\n      if need_quotes\n        slashes.times { io << '\\\\' }\n        io << '\"'\n      end\n    end\n  end\n\n  # :nodoc:\n  #\n  # Shell-quotes one item, same as `quote_windows({arg})`.\n  #\n  # ```\n  # Process.quote_windows(%q(C:\\\"foo\" project.txt)) # => %q(\"C:\\\\\\\"foo\\\" project.txt\")\n  # ```\n  def self.quote_windows(arg : String) : String\n    quote_windows({arg})\n  end\n\n  # Splits the given *line* into individual command-line arguments in a\n  # platform-specific manner, unquoting tokens if necessary.\n  #\n  # Equivalent to `parse_arguments_posix` on Unix-like systems. Equivalent to\n  # `parse_arguments_windows` on Windows.\n  def self.parse_arguments(line : String) : Array(String)\n    {% if flag?(:unix) %}\n      parse_arguments_posix(line)\n    {% elsif flag?(:win32) %}\n      parse_arguments_windows(line)\n    {% else %}\n      raise NotImplementedError.new(\"Process.parse_arguments\")\n    {% end %}\n  end\n\n  # Splits the given *line* into individual command-line arguments according to\n  # POSIX shell rules, unquoting tokens if necessary.\n  #\n  # Raises `ArgumentError` if a quotation mark is unclosed.\n  #\n  # ```\n  # Process.parse_arguments_posix(%q[\"foo bar\" '\\hello/' Fizz\\ Buzz]) # => [\"foo bar\", \"\\\\hello/\", \"Fizz Buzz\"]\n  # ```\n  #\n  # See https://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_03\n  def self.parse_arguments_posix(line : String) : Array(String)\n    tokens = [] of String\n\n    reader = Char::Reader.new(line)\n\n    while reader.has_next?\n      # skip whitespace\n      while reader.current_char.ascii_whitespace?\n        reader.next_char\n      end\n      break unless reader.has_next?\n\n      token = String.build do |str|\n        while reader.has_next? && !reader.current_char.ascii_whitespace?\n          quote = nil\n          if reader.current_char.in?('\\'', '\"')\n            quote = reader.current_char\n            reader.next_char\n          end\n\n          until (char = reader.current_char) == quote || (!quote && (char.ascii_whitespace? || char.in?('\\'', '\"')))\n            break unless reader.has_next?\n            reader.next_char\n            if char == '\\\\' && quote != '\\''\n              str << char if quote == '\"'\n              char = reader.current_char\n              if reader.has_next?\n                reader.next_char\n              else\n                break if quote == '\"'\n                char = '\\\\'\n              end\n            end\n            str << char\n          end\n\n          if quote\n            raise ArgumentError.new(\"Unmatched quote\") unless reader.has_next?\n            reader.next_char\n          end\n        end\n      end\n\n      tokens << token\n    end\n\n    tokens\n  end\n\n  # Splits the given *line* into individual command-line arguments according to\n  # Microsoft's standard C runtime, unquoting tokens if necessary.\n  #\n  # Raises `ArgumentError` if a quotation mark is unclosed. Leading spaces in\n  # *line* are ignored. Otherwise, this method is equivalent to\n  # [`CommandLineToArgvW`](https://docs.microsoft.com/en-gb/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw)\n  # for some unspecified program name.\n  #\n  # NOTE: This does **not** take strings that are passed to the CMD shell or\n  # used in a batch script.\n  #\n  # ```\n  # Process.parse_arguments_windows(%q[foo\"bar \\\\\\\"hello\\\\\" Fizz\\Buzz]) # => [\"foobar \\\\\\\"hello\\\\\", \"Fizz\\\\Buzz\"]\n  # ```\n  def self.parse_arguments_windows(line : String) : Array(String)\n    tokens = [] of String\n    reader = Char::Reader.new(line)\n    quote = false\n\n    while true\n      # skip whitespace\n      while reader.current_char.in?(' ', '\\t')\n        reader.next_char\n      end\n      break unless reader.has_next?\n\n      token = String.build do |str|\n        while true\n          backslash_count = 0\n          while reader.current_char == '\\\\'\n            backslash_count += 1\n            reader.next_char\n          end\n\n          if reader.current_char == '\"'\n            (backslash_count // 2).times { str << '\\\\' }\n            if backslash_count.odd?\n              str << '\"'\n            else\n              quote = !quote\n            end\n          else\n            backslash_count.times { str << '\\\\' }\n            break unless reader.has_next?\n            # `current_char` is neither '\\\\' nor '\"'\n            char = reader.current_char\n            break if char.in?(' ', '\\t') && !quote\n            str << char\n          end\n\n          reader.next_char\n        end\n      end\n\n      tokens << token\n    end\n\n    raise ArgumentError.new(\"Unmatched quote\") if quote\n    tokens\n  end\nend\n"
  },
  {
    "path": "src/process/status.cr",
    "content": "{% if flag?(:win32) %}\n  require \"c/ntstatus\"\n{% end %}\n\n# The reason why a process terminated.\n#\n# This enum provides a platform-independent way to query any exceptions that\n# occurred upon a process's termination, via `Process::Status#exit_reason`.\nenum Process::ExitReason\n  # The process exited normally.\n  #\n  # * On Unix-like systems, this implies `Process::Status#normal_exit?` is true.\n  # * On Windows, only exit statuses less than `0x40000000` are assumed to be\n  #   reserved for normal exits.\n  Normal\n\n  # The process terminated due to an abort request.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::ABRT`, `Signal::KILL`,\n  #   and `Signal::QUIT`.\n  # * On Windows, this corresponds to the `NTSTATUS` value\n  #   `STATUS_FATAL_APP_EXIT`.\n  Aborted\n\n  # The process exited due to an interrupt request.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::INT`.\n  # * On Windows, this corresponds to the <kbd>Ctrl</kbd> + <kbd>C</kbd> and\n  #   <kbd>Ctrl</kbd> + <kbd>Break</kbd> signals for console applications.\n  Interrupted\n\n  # The process reached a debugger breakpoint, but no debugger was attached.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::TRAP`.\n  # * On Windows, this corresponds to the `NTSTATUS` value\n  #   `STATUS_BREAKPOINT`.\n  Breakpoint\n\n  # The process tried to access a memory address where a read or write was not\n  # allowed.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::SEGV`.\n  # * On Windows, this corresponds to the `NTSTATUS` values\n  #   `STATUS_ACCESS_VIOLATION` and `STATUS_STACK_OVERFLOW`.\n  AccessViolation\n\n  # The process tried to access an invalid memory address.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::BUS`.\n  # * On Windows, this corresponds to the `NTSTATUS` value\n  #   `STATUS_DATATYPE_MISALIGNMENT`.\n  BadMemoryAccess\n\n  # The process tried to execute an invalid instruction.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::ILL`.\n  # * On Windows, this corresponds to the `NTSTATUS` values\n  #   `STATUS_ILLEGAL_INSTRUCTION` and `STATUS_PRIVILEGED_INSTRUCTION`.\n  BadInstruction\n\n  # A hardware floating-point exception occurred.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::FPE`.\n  # * On Windows, this corresponds to the `NTSTATUS` values\n  #   `STATUS_FLOAT_DIVIDE_BY_ZERO`, `STATUS_FLOAT_INEXACT_RESULT`,\n  #   `STATUS_FLOAT_INVALID_OPERATION`, `STATUS_FLOAT_OVERFLOW`, and\n  #   `STATUS_FLOAT_UNDERFLOW`.\n  FloatException\n\n  # The process exited due to a POSIX signal.\n  #\n  # Only applies to signals without a more specific exit reason. Unused on\n  # Windows.\n  Signal\n\n  # The process exited in a way that cannot be represented by any other\n  # `ExitReason`s.\n  #\n  # A `Process::Status` that maps to `Unknown` may map to a different value if\n  # new enum members are added to `ExitReason`.\n  Unknown\n\n  # The process exited due to the user closing the terminal window or ending an ssh session.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::HUP`.\n  # * On Windows, this corresponds to the `CTRL_CLOSE_EVENT` message.\n  TerminalDisconnected\n\n  # The process exited due to the user logging off or shutting down the OS.\n  #\n  # * On Unix-like systems, this corresponds to `Signal::TERM`.\n  # * On Windows, this corresponds to the `CTRL_LOGOFF_EVENT` and `CTRL_SHUTDOWN_EVENT` messages.\n  SessionEnded\n\n  # Returns `true` if the process exited abnormally.\n  #\n  # This includes all values except `Normal`.\n  def abnormal? : Bool\n    !normal?\n  end\n\n  # Returns a textual description of this exit reason.\n  #\n  # ```\n  # Process::ExitReason::Normal.description  # => \"Process exited normally\"\n  # Process::ExitReason::Aborted.description # => \"Process terminated abnormally\"\n  # ```\n  #\n  # `Status#description` provides more detail for a specific process status.\n  def description : String\n    case self\n    in .normal?\n      \"Process exited normally\"\n    in .aborted?, .session_ended?, .terminal_disconnected?\n      \"Process terminated abnormally\"\n    in .interrupted?\n      \"Process was interrupted\"\n    in .breakpoint?\n      \"Process hit a breakpoint and no debugger was attached\"\n    in .access_violation?, .bad_memory_access?\n      \"Process terminated because of an invalid memory access\"\n    in .bad_instruction?\n      \"Process terminated because of an invalid instruction\"\n    in .float_exception?\n      \"Process terminated because of a floating-point system exception\"\n    in .signal?\n      \"Process terminated because of an unhandled signal\"\n    in .unknown?\n      \"Process terminated abnormally, the cause is unknown\"\n    end\n  end\nend\n\n# The status of a terminated process. Returned by `Process#wait`.\nclass Process::Status\n  # Platform-specific exit status code, which usually contains either the exit code or a termination signal.\n  # The other `Process::Status` methods extract the values from `exit_status`.\n  @[Deprecated(\"Use `#exit_reason`, `#exit_code`, or `#system_exit_status` instead\")]\n  def exit_status : Int32\n    @exit_status.to_i32!\n  end\n\n  # Returns the exit status as indicated by the operating system.\n  #\n  # It can encode exit codes and termination signals and is platform-specific.\n  def system_exit_status : UInt32\n    @exit_status.to_u32!\n  end\n\n  {% if flag?(:win32) %}\n    # :nodoc:\n    def initialize(@exit_status : UInt32)\n    end\n  {% else %}\n    # :nodoc:\n    def initialize(@exit_status : Int32)\n    end\n  {% end %}\n\n  # Returns a platform-independent reason why the process terminated.\n  def exit_reason : ExitReason\n    {% if flag?(:win32) %}\n      # TODO: perhaps this should cover everything that SEH can handle?\n      # https://learn.microsoft.com/en-us/windows/win32/debug/getexceptioncode\n      case @exit_status\n      when LibC::STATUS_FATAL_APP_EXIT\n        ExitReason::Aborted\n      when LibC::STATUS_CONTROL_C_EXIT\n        ExitReason::Interrupted\n      when LibC::STATUS_BREAKPOINT\n        ExitReason::Breakpoint\n      when LibC::STATUS_ACCESS_VIOLATION, LibC::STATUS_STACK_OVERFLOW\n        ExitReason::AccessViolation\n      when LibC::STATUS_DATATYPE_MISALIGNMENT\n        ExitReason::BadMemoryAccess\n      when LibC::STATUS_ILLEGAL_INSTRUCTION, LibC::STATUS_PRIVILEGED_INSTRUCTION\n        ExitReason::BadInstruction\n      when LibC::STATUS_FLOAT_DIVIDE_BY_ZERO, LibC::STATUS_FLOAT_INEXACT_RESULT, LibC::STATUS_FLOAT_INVALID_OPERATION, LibC::STATUS_FLOAT_OVERFLOW, LibC::STATUS_FLOAT_UNDERFLOW\n        ExitReason::FloatException\n      else\n        @exit_status & 0xC0000000_u32 == 0 ? ExitReason::Normal : ExitReason::Unknown\n      end\n    {% elsif flag?(:unix) && !flag?(:wasm32) %}\n      case exit_signal?\n      when Nil\n        ExitReason::Normal\n      when .abrt?, .kill?, .quit?\n        ExitReason::Aborted\n      when .hup?\n        ExitReason::TerminalDisconnected\n      when .term?\n        ExitReason::SessionEnded\n      when .int?\n        ExitReason::Interrupted\n      when .trap?\n        ExitReason::Breakpoint\n      when .segv?\n        ExitReason::AccessViolation\n      when .bus?\n        ExitReason::BadMemoryAccess\n      when .ill?\n        ExitReason::BadInstruction\n      when .fpe?\n        ExitReason::FloatException\n      else\n        # TODO: stop / continue\n        ExitReason::Signal\n      end\n    {% else %}\n      raise NotImplementedError.new(\"Process::Status#exit_reason\")\n    {% end %}\n  end\n\n  # Returns `true` if the process was terminated by a signal.\n  #\n  # NOTE: In contrast to `WIFSIGNALED` in glibc, the status code `0x7E` (`SIGSTOP`)\n  # is considered a signal.\n  #\n  # * `#abnormal_exit?` is a more portable alternative.\n  # * `#exit_signal?` provides more information about the signal.\n  def signal_exit? : Bool\n    !!exit_signal?\n  end\n\n  # Returns `true` if the process terminated normally.\n  #\n  # Equivalent to `ExitReason::Normal`\n  #\n  # * `#exit_reason` provides more insights into other exit reasons.\n  # * `#abnormal_exit?` returns the inverse.\n  def normal_exit? : Bool\n    exit_reason.normal?\n  end\n\n  # Returns `true` if the process terminated abnormally.\n  #\n  # Equivalent to `ExitReason#abnormal?`\n  #\n  # * `#exit_reason` provides more insights into the specific exit reason.\n  # * `#normal_exit?` returns the inverse.\n  def abnormal_exit? : Bool\n    exit_reason.abnormal?\n  end\n\n  # If `signal_exit?` is `true`, returns the *Signal* the process\n  # received and didn't handle. Will raise if `signal_exit?` is `false`.\n  #\n  # Available only on Unix-like operating systems.\n  #\n  # NOTE: `#exit_reason` is preferred over this method as a portable alternative\n  # which also works on Windows.\n  @[Deprecated(\"Use `#exit_signal?` instead.\")]\n  def exit_signal : Signal\n    {% if flag?(:unix) && !flag?(:wasm32) %}\n      Signal.new(signal_code)\n    {% else %}\n      raise NotImplementedError.new(\"Process::Status#exit_signal\")\n    {% end %}\n  end\n\n  # Returns the exit `Signal` or `nil` if there is none.\n  #\n  # On Windows returns always `nil`.\n  #\n  # * `#exit_reason` is a portable alternative.\n  def exit_signal? : Signal?\n    {% if flag?(:unix) && !flag?(:wasm32) %}\n      code = signal_code\n      unless code.zero?\n        Signal.new(code)\n      end\n    {% end %}\n  end\n\n  # Returns the exit code of the process if it exited normally (`#normal_exit?`).\n  #\n  # Raises `RuntimeError` if the status describes an abnormal exit.\n  #\n  # ```\n  # Process.run(\"true\").exit_code                                # => 0\n  # Process.run(\"exit 123\", shell: true).exit_code               # => 123\n  # Process.new(\"sleep\", [\"10\"]).tap(&.terminate).wait.exit_code # RuntimeError: Abnormal exit has no exit code\n  # ```\n  def exit_code : Int32\n    exit_code? || raise RuntimeError.new(\"Abnormal exit has no exit code\")\n  end\n\n  # Returns the exit code of the process if it exited normally.\n  #\n  # Returns `nil` if the status describes an abnormal exit.\n  #\n  # ```\n  # Process.run(\"true\").exit_code?                                # => 0\n  # Process.run(\"exit 123\", shell: true).exit_code?               # => 123\n  # Process.new(\"sleep\", [\"10\"]).tap(&.terminate).wait.exit_code? # => nil\n  # ```\n  def exit_code? : Int32?\n    return unless normal_exit?\n\n    {% if flag?(:unix) %}\n      # define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)\n      (@exit_status & 0xff00) >> 8\n    {% else %}\n      @exit_status.to_i32!\n    {% end %}\n  end\n\n  # Returns `true` if the process exited normally with an exit code of `0`.\n  def success? : Bool\n    exit_code? == 0\n  end\n\n  private def signal_code\n    # define __WTERMSIG(status) ((status) & 0x7f)\n    @exit_status & 0x7f\n  end\n\n  def_equals_and_hash @exit_status\n\n  # Prints a textual representation of the process status to *io*.\n  #\n  # The result is similar to `#to_s`, but prefixed by the type name,\n  # delimited by square brackets, and constants use full paths:\n  # `Process::Status[0]`, `Process::Status[1]`, `Process::Status[Signal::HUP]`,\n  # `Process::Status[LibC::STATUS_CONTROL_C_EXIT]`.\n  def inspect(io : IO) : Nil\n    io << \"Process::Status[\"\n    {% if flag?(:win32) %}\n      if name = name_for_win32_exit_status\n        io << \"LibC::\" << name\n      else\n        stringify_exit_status_windows(io)\n      end\n    {% else %}\n      if signal = exit_signal?\n        signal.inspect(io)\n      else\n        exit_code.inspect(io)\n      end\n    {% end %}\n    io << \"]\"\n  end\n\n  private def name_for_win32_exit_status\n    case @exit_status\n    # Ignoring LibC::STATUS_SUCCESS here because we prefer its numerical representation `0`\n    when LibC::STATUS_FATAL_APP_EXIT          then \"STATUS_FATAL_APP_EXIT\"\n    when LibC::STATUS_DATATYPE_MISALIGNMENT   then \"STATUS_DATATYPE_MISALIGNMENT\"\n    when LibC::STATUS_BREAKPOINT              then \"STATUS_BREAKPOINT\"\n    when LibC::STATUS_ACCESS_VIOLATION        then \"STATUS_ACCESS_VIOLATION\"\n    when LibC::STATUS_ILLEGAL_INSTRUCTION     then \"STATUS_ILLEGAL_INSTRUCTION\"\n    when LibC::STATUS_FLOAT_DIVIDE_BY_ZERO    then \"STATUS_FLOAT_DIVIDE_BY_ZERO\"\n    when LibC::STATUS_FLOAT_INEXACT_RESULT    then \"STATUS_FLOAT_INEXACT_RESULT\"\n    when LibC::STATUS_FLOAT_INVALID_OPERATION then \"STATUS_FLOAT_INVALID_OPERATION\"\n    when LibC::STATUS_FLOAT_OVERFLOW          then \"STATUS_FLOAT_OVERFLOW\"\n    when LibC::STATUS_FLOAT_UNDERFLOW         then \"STATUS_FLOAT_UNDERFLOW\"\n    when LibC::STATUS_PRIVILEGED_INSTRUCTION  then \"STATUS_PRIVILEGED_INSTRUCTION\"\n    when LibC::STATUS_STACK_OVERFLOW          then \"STATUS_STACK_OVERFLOW\"\n    when LibC::STATUS_CANCELLED               then \"STATUS_CANCELLED\"\n    when LibC::STATUS_CONTROL_C_EXIT          then \"STATUS_CONTROL_C_EXIT\"\n    end\n  end\n\n  # Prints a textual representation of the process status to *io*.\n  #\n  # A normal exit status prints the numerical value (`0`, `1` etc) or a named\n  # status (e.g. `STATUS_CONTROL_C_EXIT` on Windows).\n  # A signal exit status prints the name of the `Signal` member (`HUP`, `INT`, etc.).\n  def to_s(io : IO) : Nil\n    {% if flag?(:win32) %}\n      if name = name_for_win32_exit_status\n        io << name\n      else\n        stringify_exit_status_windows(io)\n      end\n    {% else %}\n      if signal = exit_signal?\n        if name = signal.member_name\n          io << name\n        else\n          signal.inspect(io)\n        end\n      else\n        io << exit_code\n      end\n    {% end %}\n  end\n\n  # Returns a textual representation of the process status.\n  #\n  # A normal exit status prints the numerical value (`0`, `1` etc) or a named\n  # status (e.g. `STATUS_CONTROL_C_EXIT` on Windows).\n  # A signal exit status prints the name of the `Signal` member (`HUP`, `INT`, etc.).\n  def to_s : String\n    {% if flag?(:win32) %}\n      name_for_win32_exit_status || String.build { |io| stringify_exit_status_windows(io) }\n    {% else %}\n      if signal = exit_signal?\n        signal.member_name || signal.inspect\n      else\n        exit_code.to_s\n      end\n    {% end %}\n  end\n\n  # Returns a textual description of this process status.\n  #\n  # ```\n  # Process::Status.new(0).description # => \"Process exited normally\"\n  # process = Process.new(\"sleep\", [\"10\"])\n  # process.terminate\n  # process.wait.description # => \"Process received and didn't handle signal TERM (15)\"\n  # ```\n  #\n  # `ExitReason#description` provides the specific messages for non-signal exits.\n  def description : String\n    if exit_reason.signal? && (signal = exit_signal?)\n      \"Process received and didn't handle signal #{signal}\"\n    else\n      exit_reason.description\n    end\n  end\n\n  private def stringify_exit_status_windows(io)\n    # On Windows large status codes are typically expressed in hexadecimal\n    if @exit_status >= UInt16::MAX\n      io << \"0x\"\n      @exit_status.to_s(base: 16, upcase: true).rjust(io, 8, '0')\n    else\n      @exit_status.to_s(io)\n    end\n  end\nend\n"
  },
  {
    "path": "src/process.cr",
    "content": "class Process\n  # A struct representing the CPU current times of the process,\n  # in fractions of seconds.\n  #\n  # * *utime*: CPU time a process spent in userland.\n  # * *stime*: CPU time a process spent in the kernel.\n  # * *cutime*: CPU time a processes terminated children (and their terminated children) spent in the userland.\n  # * *cstime*: CPU time a processes terminated children (and their terminated children) spent in the kernel.\n  record Tms, utime : Float64, stime : Float64, cutime : Float64, cstime : Float64\nend\n\nrequire \"crystal/system/process\"\n\nclass Process\n  # Terminate the current process immediately. All open files, pipes and sockets\n  # are flushed and closed, all child processes are inherited by PID 1. This does\n  # not run any handlers registered with `at_exit`, use `::exit` for that.\n  #\n  # *status* is the exit status of the current process.\n  def self.exit(status : Int32 | Process::Status = 0) : NoReturn\n    Crystal::System::Process.exit(status)\n  end\n\n  # Returns the process identifier of the current process.\n  def self.pid : Int64\n    Crystal::System::Process.pid.to_i64\n  end\n\n  # Returns the process group identifier of the current process.\n  def self.pgid : Int64\n    Crystal::System::Process.pgid.to_i64\n  end\n\n  # Returns the process group identifier of the process identified by *pid*.\n  def self.pgid(pid : Int) : Int64\n    Crystal::System::Process.pgid(pid).to_i64\n  end\n\n  # Returns the process identifier of the parent process of the current process.\n  #\n  # On Windows, the parent is associated only at process creation time, and the\n  # system does not re-parent the current process if the parent terminates; thus\n  # `Process.exists?(Process.ppid)` is not guaranteed to be true.\n  def self.ppid : Int64\n    Crystal::System::Process.ppid.to_i64\n  end\n\n  # Sends *signal* to the process identified by *pid*.\n  def self.signal(signal : Signal, pid : Int) : Nil\n    Crystal::System::Process.signal(pid, signal.value)\n  end\n\n  # Installs *handler* as the new handler for interrupt requests. Removes any\n  # previously set interrupt handler.\n  #\n  # The handler is executed on a fresh fiber every time an interrupt occurs.\n  #\n  # * On Unix-like systems, this traps `SIGINT`.\n  # * On Windows, this captures <kbd>Ctrl</kbd> + <kbd>C</kbd> and\n  #   <kbd>Ctrl</kbd> + <kbd>Break</kbd> signals sent to a console application.\n  @[Deprecated(\"Use `#on_terminate` instead\")]\n  def self.on_interrupt(&handler : ->) : Nil\n    Crystal::System::Process.on_interrupt(&handler)\n  end\n\n  # Installs *handler* as the new handler for termination requests. Removes any\n  # previously set termination handler.\n  #\n  # The handler is executed on a fresh fiber every time an interrupt occurs.\n  #\n  # * On Unix-like systems, this traps `SIGINT`, `SIGHUP` and `SIGTERM`.\n  # * On Windows, this captures <kbd>Ctrl</kbd> + <kbd>C</kbd>,\n  #   <kbd>Ctrl</kbd> + <kbd>Break</kbd>, terminal close, windows logoff\n  #   and shutdown signals sent to a console application.\n  #\n  # ```\n  # wait_channel = Channel(Nil).new\n  #\n  # Process.on_terminate do |reason|\n  #   case reason\n  #   when .interrupted?\n  #     puts \"terminating gracefully\"\n  #     wait_channel.close\n  #   when .terminal_disconnected?\n  #     puts \"reloading configuration\"\n  #   when .session_ended?\n  #     puts \"terminating forcefully\"\n  #     Process.exit\n  #   end\n  # end\n  #\n  # wait_channel.receive?\n  # puts \"bye\"\n  # ```\n  def self.on_terminate(&handler : ::Process::ExitReason ->) : Nil\n    Crystal::System::Process.on_terminate(&handler)\n  end\n\n  # Ignores all interrupt requests. Removes any custom interrupt handler set\n  # with `#on_terminate`.\n  #\n  # * On Windows, interrupts generated by <kbd>Ctrl</kbd> + <kbd>Break</kbd>\n  #   cannot be ignored in this way.\n  def self.ignore_interrupts! : Nil\n    Crystal::System::Process.ignore_interrupts!\n  end\n\n  # Restores default handling of interrupt requests.\n  def self.restore_interrupts! : Nil\n    Crystal::System::Process.restore_interrupts!\n  end\n\n  # Returns whether a debugger is attached to the current process.\n  #\n  # Currently supported on Windows and Linux. Always returns `false` on other\n  # systems.\n  @[Experimental]\n  def self.debugger_present? : Bool\n    Crystal::System::Process.debugger_present?\n  end\n\n  # Returns `true` if the process identified by *pid* is valid for\n  # a currently registered process, `false` otherwise. Note that this\n  # returns `true` for a process in the zombie or similar state.\n  def self.exists?(pid : Int) : Bool\n    Crystal::System::Process.exists?(pid)\n  end\n\n  # Returns a `Tms` for the current process. For the children times, only those\n  # of terminated children are returned on Unix; they are zero on Windows.\n  def self.times : Tms\n    Crystal::System::Process.times\n  end\n\n  # :nodoc:\n  #\n  # Runs the given block inside a new process and\n  # returns a `Process` representing the new child process.\n  #\n  # Available only on Unix-like operating systems.\n  @[Deprecated(\"Fork is no longer supported.\")]\n  def self.fork(&) : Process\n    new Crystal::System::Process.fork { yield }\n  end\n\n  # :nodoc:\n  #\n  # Duplicates the current process.\n  # Returns a `Process` representing the new child process in the current process\n  # and `nil` inside the new child process.\n  #\n  # Available only on Unix-like operating systems.\n  @[Deprecated(\"Fork is no longer supported.\")]\n  def self.fork : Process?\n    {% raise(\"Process fork is unsupported with multithread mode\") if flag?(:preview_mt) %}\n\n    if pid = Crystal::System::Process.fork\n      new pid\n    end\n  end\n\n  # How to redirect the standard input, output and error IO of a process.\n  enum Redirect\n    # Pipe the IO so the parent process can read (or write) to the process IO\n    # through `#input`, `#output` or `#error`.\n    Pipe\n\n    # Discards the IO.\n    Close\n\n    # Use the IO of the parent process.\n    Inherit\n  end\n\n  # The standard `IO` configuration of a process.\n  alias Stdio = Redirect | IO\n  alias ExecStdio = Redirect | IO::FileDescriptor\n  alias Env = Nil | Hash(String, Nil) | Hash(String, String?) | Hash(String, String)\n\n  # Executes a child process and waits for it to complete, returning its status.\n  #\n  # See `Process.new` for the meaning of the parameters.\n  #\n  # Returns a `Process::Status` representing the child process' exit status.\n  #\n  # Raises `IO::Error` if the execution itself fails (for example because the\n  # executable does not exist or is not executable).\n  #\n  # Example:\n  #\n  # ```\n  # io = IO::Memory.new\n  # status = Process.run(%w[echo hello], output: io)\n  # io.to_s # => \"hello\\n\"\n  # status  # => Process::Status[0]\n  # ```\n  def self.run(args : Enumerable(String), *, env : Env = nil, clear_env : Bool = false,\n               input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil) : Process::Status\n    new(args, env: env, clear_env: clear_env, input: input, output: output, error: error, chdir: chdir).wait\n  end\n\n  # Executes a child process and waits for it to complete, returning its status.\n  #\n  # See `Process.new` for the meaning of the parameters.\n  #\n  # Returns a `Process::Status` representing the child process' exit status.\n  # The global `$?` variable is set to the returned status.\n  #\n  # Raises `IO::Error` if the execution itself fails (for example because the\n  # executable does not exist or is not executable).\n  #\n  # Example:\n  #\n  # ```\n  # status = Process.run(\"echo\", [\"hello\"], output: Process::Redirect::Inherit)\n  # # outputs \"hello\\n\"\n  # $?     # => Process::Status[0]\n  # status # => Process::Status[0]\n  # ```\n  def self.run(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,\n               input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil) : Process::Status\n    status = new(command, args, env, clear_env, shell, input, output, error, chdir).wait\n    $? = status\n    status\n  end\n\n  # Executes a child process and waits for it to complete, returning its status.\n  #\n  # See `Process.new` for the meaning of the parameters.\n  #\n  # Returns a `Process::Status` representing the child process' exit status.\n  # The global `$?` variable is set to the returned status.\n  #\n  # Returns `nil` if the execution itself fails (for example because the\n  # executable does not exist or is not executable).\n  #\n  # Example:\n  #\n  # ```\n  # Process.run?([\"true\"])        # => Process::Status[0]\n  # Process.run?([\"nonexistent\"]) # => nil\n  # ```\n  def self.run?(args : Enumerable(String), *,\n                env : Env = nil, clear_env : Bool = false,\n                input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close,\n                chdir : Path | String? = nil) : Process::Status?\n    status = new(args, env: env, clear_env: clear_env, input: input, output: output, error: error, chdir: chdir) { return nil }.wait\n    status\n  end\n\n  # Executes a child process, yields the block, and then waits for it to finish.\n  #\n  # See `Process.new` for the meaning of the parameters.\n  #\n  # By default the process is configured to use pipes for input, output and error.\n  # These will be closed automatically at the end of the block.\n  #\n  # Returns a tuple with the process' exit status and the block's output value.\n  #\n  # Raises `IO::Error` if the execution itself fails (for example because the\n  # executable does not exist or is not executable).\n  #\n  # Example:\n  #\n  # ```\n  # status, result = Process.run(%w[echo hello]) do |process|\n  #   process.output.gets_to_end\n  # end\n  # status # => Process::Status[0]\n  # result # => \"hello\\n\"\n  # ```\n  def self.run(args : Enumerable(String), *, env : Env = nil, clear_env : Bool = false,\n               input : Stdio = Redirect::Pipe, output : Stdio = Redirect::Pipe, error : Stdio = Redirect::Pipe, chdir : Path | String? = nil, & : Process -> _)\n    process = new(args, env: env, clear_env: clear_env, input: input, output: output, error: error, chdir: chdir)\n    begin\n      value = yield process\n\n      process.close\n      status = process.wait\n      {status, value}\n    rescue ex\n      process.terminate\n      raise ex\n    end\n  end\n\n  # Executes a child process, yields the block, and then waits for it to finish.\n  #\n  # See `Process.new` for the meaning of the parameters.\n  #\n  # By default the process is configured to use pipes for input, output and error.\n  # These will be closed automatically at the end of the block.\n  #\n  # Returns the block's value.\n  #\n  # Raises `IO::Error` if the execution itself fails (for example because the\n  # executable does not exist or is not executable).\n  #\n  # Example:\n  #\n  # ```\n  # output = Process.run(\"echo\", [\"hello\"]) do |process|\n  #   process.output.gets_to_end\n  # end\n  # $?     # => Process::Status[0]\n  # output # => \"hello\\n\"\n  # ```\n  def self.run(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,\n               input : Stdio = Redirect::Pipe, output : Stdio = Redirect::Pipe, error : Stdio = Redirect::Pipe, chdir : Path | String? = nil, &)\n    process = new(command, args, env, clear_env, shell, input, output, error, chdir)\n    begin\n      value = yield process\n\n      process.close\n      $? = process.wait\n      value\n    rescue ex\n      process.terminate\n      raise ex\n    end\n  end\n\n  # Replaces the current process with a new one. This function never returns.\n  #\n  # Raises `IO::Error` if executing the command fails (for example if the executable doesn't exist).\n  def self.exec(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,\n                input : ExecStdio = Redirect::Inherit, output : ExecStdio = Redirect::Inherit, error : ExecStdio = Redirect::Inherit, chdir : Path | String? = nil) : NoReturn\n    input = exec_stdio_to_fd(input, for: STDIN)\n    output = exec_stdio_to_fd(output, for: STDOUT)\n    error = exec_stdio_to_fd(error, for: STDERR)\n\n    Crystal::System::Process.replace(command, args, shell, env, clear_env, input, output, error, chdir)\n  end\n\n  private def self.exec_stdio_to_fd(stdio : ExecStdio, for dst_io : IO::FileDescriptor) : IO::FileDescriptor\n    case stdio\n    when IO::FileDescriptor\n      stdio\n    when Redirect::Pipe\n      raise \"Cannot use Process::Redirect::Pipe for Process.exec\"\n    when Redirect::Inherit\n      dst_io\n    when Redirect::Close\n      if dst_io == STDIN\n        File.open(File::NULL, \"r\")\n      else\n        File.open(File::NULL, \"w\")\n      end\n    else\n      raise \"BUG: Impossible type in ExecStdio #{stdio.class}\"\n    end\n  end\n\n  # Returns the process identifier of this process.\n  def pid : Int64\n    @process_info.pid.to_i64\n  end\n\n  # A pipe to this process' input. Raises if a pipe wasn't asked when creating the process.\n  getter! input : IO::FileDescriptor\n\n  # A pipe to this process' output. Raises if a pipe wasn't asked when creating the process.\n  getter! output : IO::FileDescriptor\n\n  # A pipe to this process' error. Raises if a pipe wasn't asked when creating the process.\n  getter! error : IO::FileDescriptor\n\n  @process_info : Crystal::System::Process\n  @wait_count = 0\n\n  # Creates and executes a child process.\n  #\n  # This starts a new process for the command given in *args[0]*.\n  #\n  # The command is either a path to the executable to run, or the name of an\n  # executable which is then looked up by the operating system.\n  # The lookup uses the `PATH` variable of the current process environment\n  # (i.e. `ENV[\"PATH\"]).\n  # In order to resolve to a specific executable, provide a path instead of\n  # only a command name. `Process.find_executable` can help with looking up a\n  # command in a custom `PATH`.\n  #\n  # The following arguments in *args* are passed as arguments to the child process.\n  #\n  # Raises `IO::Error` if executing *args[0]* fails, for example because the\n  # executable doesn't exist or is not executable.\n  #\n  # *env* provides a mapping of environment variables for the child process.\n  # If *clear_env* is `true`, only these explicit variables are used; if `false`,\n  # the child inherits the parent's environment with *env*  merged.\n  #\n  # *input*, *output*, *error* configure the child process's standard streams.\n  # * `Redirect::Close` passes the null device\n  # * `Redirect::Pipe` creates a pipe that's accessible via `#input`, `#output`\n  #    or `#error`.\n  # * `Redirect::Inherit` to share the parent's streams (`STDIN`, `STDOUT`, `STDERR`).\n  # * An `IO` instance creates a pipe that reads/writes into the given IO.\n  #\n  # *chdir* changes the working directory of the child process. If `nil`, uses\n  # the current working directory of the parent process.\n  #\n  # Example:\n  #\n  # ```\n  # process = Process.new([\"echo\", \"Hello\"], output: Process::Redirect::Pipe)\n  # process.output.gets_to_end # => \"Hello\\n\"\n  # process.wait               # => Process::Status[0]\n  # ```\n  #\n  # Similar methods:\n  #\n  # * `Process.run` is a convenient short cut if you just want to run a command\n  #    and wait for it to finish.\n  # * `Process.exec` replaces the current process.\n  def initialize(args : Enumerable(String), *, env : Env = nil, clear_env : Bool = false,\n                 input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil)\n    raise File::NotFoundError.new(\"Error executing process: No command\", file: \"\") if args.empty?\n\n    fork_input = stdio_to_fd(input, for: STDIN)\n    fork_output = stdio_to_fd(output, for: STDOUT)\n    fork_error = stdio_to_fd(error, for: STDERR)\n\n    prepared_args = Crystal::System::Process.prepare_args(args)\n    pid = Crystal::System::Process.spawn(prepared_args, false, env, clear_env, fork_input, fork_output, fork_error, chdir.try &.to_s) do |error, command|\n      raise ::File::Error.from_os_error(\"Error executing process\", error, file: command)\n    end\n    @process_info = Crystal::System::Process.new(pid)\n\n    fork_input.close unless fork_input.in?(input, STDIN)\n    fork_output.close unless fork_output.in?(output, STDOUT)\n    fork_error.close unless fork_error.in?(error, STDERR)\n  end\n\n  # :nodoc:\n  protected def initialize(args : Enumerable(String), *, env : Env = nil, clear_env : Bool = false,\n                           input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil, &)\n    raise File::NotFoundError.new(\"Error executing process: No command\", file: \"\") if args.empty?\n\n    fork_input = stdio_to_fd(input, for: STDIN)\n    fork_output = stdio_to_fd(output, for: STDOUT)\n    fork_error = stdio_to_fd(error, for: STDERR)\n\n    prepared_args = Crystal::System::Process.prepare_args(args)\n    pid = Crystal::System::Process.spawn(prepared_args, false, env, clear_env, fork_input, fork_output, fork_error, chdir.try &.to_s) do\n      yield\n    end\n    @process_info = Crystal::System::Process.new(pid)\n\n    fork_input.close unless fork_input.in?(input, STDIN)\n    fork_output.close unless fork_output.in?(output, STDOUT)\n    fork_error.close unless fork_error.in?(error, STDERR)\n  end\n\n  # Creates and executes a child process.\n  #\n  # This starts a new process for `command`.\n  #\n  # ## `shell: false` (the default)\n  #\n  # *command* is either a path to the executable to run, or the name of an\n  # executable which is then looked up by the operating system.\n  # The lookup uses the `PATH` variable of the current process environment\n  # (i.e. `ENV[\"PATH\"]).\n  # In order to resolve to a specific executable, provide a path instead of\n  # only a command name. `Process.find_executable` can help with looking up a\n  # command in a custom `PATH`.\n  #\n  # The arguments in *args* are passed as arguments to the child process.\n  #\n  # Raises `IO::Error` if executing *command*  fails, for example because the\n  # executable doesn't exist or is not executable.\n  #\n  # ## `shell: true`\n  #\n  # *command* is a shell script executed in the system shell (`/bin/sh` on Unix\n  # systems, `cmd.exe` on Windows).\n  # Command names are looked up by the shell itself, using the `PATH` variable\n  # of the shell process (i.e. `env[\"PATH\"]`).\n  #\n  # *args* is unsupported on Windows.\n  # On Unix it's passed as additional arguments to the shell process and can be\n  # used in the shell script with `\"${@}\"` to safely insert them there. If the\n  # script is a single command (no whitespace), `\"${@}\"` is appended implicitly.\n  #\n  # The returned instance represents the shell process, not the process executed\n  # for *command*.\n  #\n  # If executing *command*  fails, for example because the executable doesn't\n  # exist or is not executable, it may raise `IO::Error` (on Windows) or return\n  # an unsuccessful exit status (on Unix).\n  #\n  # ## Shared parameters\n  #\n  # *env* provides a mapping of environment variables for the child process.\n  # If *clear_env* is `true`, only these explicit variables are used; if `false`,\n  # the child inherits the parent's environment with *env*  merged.\n  #\n  # *input*, *output*, *error* configure the child process's standard streams.\n  # * `Redirect::Close` passes the null device\n  # * `Redirect::Pipe` creates a pipe that's accessible via `#input`, `#output`\n  #    or `#error`.\n  # * `Redirect::Inherit` to share the parent's streams (`STDIN`, `STDOUT`, `STDERR`).\n  # * An `IO` instance creates a pipe that reads/writes into the given IO.\n  #\n  # *chdir* changes the working directory of the child process. If `nil`, uses\n  # the current working directory of the parent process.\n  #\n  # Example:\n  #\n  # ```\n  # process = Process.new(\"echo\", [\"Hello\"], output: Process::Redirect::Pipe)\n  # process.output.gets_to_end # => \"Hello\\n\"\n  # process.wait               # => Process::Status[0]\n  # ```\n  #\n  # Similar methods:\n  #\n  # * `Process.run` is a convenient short cut if you just want to run a command\n  #    and wait for it to finish.\n  # * `Process.exec` replaces the current process.\n  def initialize(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,\n                 input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil)\n    fork_input = stdio_to_fd(input, for: STDIN)\n    fork_output = stdio_to_fd(output, for: STDOUT)\n    fork_error = stdio_to_fd(error, for: STDERR)\n\n    prepared_args = Crystal::System::Process.prepare_args(command, args, shell)\n    pid = Crystal::System::Process.spawn(prepared_args, shell, env, clear_env, fork_input, fork_output, fork_error, chdir.try &.to_s) do |error, command|\n      raise ::File::Error.from_os_error(\"Error executing process\", error, file: command)\n    end\n    @process_info = Crystal::System::Process.new(pid)\n\n    fork_input.close unless fork_input.in?(input, STDIN)\n    fork_output.close unless fork_output.in?(output, STDOUT)\n    fork_error.close unless fork_error.in?(error, STDERR)\n  end\n\n  def finalize : Nil\n    @process_info.release\n  end\n\n  private def stdio_to_fd(stdio : Stdio, for dst_io : IO::FileDescriptor) : IO::FileDescriptor\n    case stdio\n    in IO::FileDescriptor\n      # on Windows, only async pipes can be passed to child processes, async\n      # regular files will report an error and those require a separate pipe\n      # (https://github.com/crystal-lang/crystal/pull/13362#issuecomment-1519082712)\n      {% if flag?(:win32) %}\n        unless stdio.system_blocking? || stdio.info.type.pipe?\n          return io_to_fd(stdio, for: dst_io)\n        end\n      {% end %}\n\n      stdio\n    in IO\n      io_to_fd(stdio, for: dst_io)\n    in Redirect::Pipe\n      case dst_io\n      when STDIN\n        fork_io, @input = IO.pipe(read_blocking: true)\n      when STDOUT\n        @output, fork_io = IO.pipe(write_blocking: true)\n      when STDERR\n        @error, fork_io = IO.pipe(write_blocking: true)\n      else\n        raise \"BUG: Unknown destination io #{dst_io}\"\n      end\n\n      fork_io\n    in Redirect::Inherit\n      dst_io\n    in Redirect::Close\n      if dst_io == STDIN\n        File.open(File::NULL, \"r\")\n      else\n        File.open(File::NULL, \"w\")\n      end\n    end\n  end\n\n  private def io_to_fd(stdio : Stdio, for dst_io : IO::FileDescriptor) : IO::FileDescriptor\n    if stdio.closed?\n      if dst_io == STDIN\n        return File.open(File::NULL, \"r\").tap(&.close)\n      else\n        return File.open(File::NULL, \"w\").tap(&.close)\n      end\n    end\n\n    if dst_io == STDIN\n      fork_io, process_io = IO.pipe(read_blocking: true)\n\n      @wait_count += 1\n      ensure_channel\n      spawn { copy_io(stdio, process_io, channel, close_dst: true) }\n    else\n      process_io, fork_io = IO.pipe(write_blocking: true)\n\n      @wait_count += 1\n      ensure_channel\n      spawn { copy_io(process_io, stdio, channel, close_src: true) }\n    end\n\n    fork_io\n  end\n\n  {% unless flag?(:interpreted) %}\n    # :nodoc:\n    def initialize(pid : LibC::PidT)\n      @process_info = Crystal::System::Process.new(pid)\n    end\n  {% end %}\n\n  # Sends *signal* to this process.\n  #\n  # NOTE: `#terminate` is preferred over `signal(Signal::TERM)` and\n  # `signal(Signal::KILL)` as a portable alternative which also works on\n  # Windows.\n  def signal(signal : Signal) : Nil\n    Crystal::System::Process.signal(@process_info.pid, signal)\n  end\n\n  # Waits for this process to complete and closes any pipes.\n  def wait : Process::Status\n    @wait_count.times do\n      ex = channel.receive\n      raise ex if ex\n    end\n    @wait_count = 0\n\n    Process::Status.new(@process_info.wait)\n  ensure\n    close\n    @process_info.release\n  end\n\n  # Whether the process is still registered in the system.\n  # Note that this returns `true` for processes in the zombie or similar state.\n  def exists? : Bool\n    @process_info.exists?\n  end\n\n  # Whether this process is already terminated.\n  def terminated? : Bool\n    !exists?\n  end\n\n  # Closes any system resources (e.g. pipes) held for the child process.\n  def close : Nil\n    close_io @input\n    close_io @output\n    close_io @error\n  end\n\n  # Asks this process to terminate.\n  #\n  # If *graceful* is true, prefers graceful termination over abrupt termination\n  # if supported by the system.\n  #\n  # * On Unix-like systems, this causes `Signal::TERM` to be sent to the process\n  #   instead of `Signal::KILL`.\n  # * On Windows, this parameter has no effect and graceful termination is\n  #   unavailable. The terminated process has an exit status of 1.\n  def terminate(*, graceful : Bool = true) : Nil\n    @process_info.terminate(graceful: graceful)\n  end\n\n  private def channel\n    if channel = @channel\n      channel\n    else\n      raise \"BUG: Notification channel was not initialized for this process\"\n    end\n  end\n\n  private def ensure_channel\n    @channel ||= Channel(Exception?).new\n  end\n\n  private def copy_io(src, dst, channel, close_src = false, close_dst = false)\n    return unless src.is_a?(IO) && dst.is_a?(IO)\n\n    begin\n      IO.copy(src, dst)\n\n      # close is called here to trigger exceptions\n      # close must be called before channel.send or the process may deadlock\n      src.close if close_src\n      close_src = false\n      dst.close if close_dst\n      close_dst = false\n\n      channel.send nil\n    rescue ex\n      channel.send ex\n    ensure\n      # any exceptions are silently ignored because of spawn\n      src.close if close_src\n      dst.close if close_dst\n    end\n  end\n\n  private def close_io(io)\n    io.close if io\n  end\n\n  # Changes the root directory and the current working directory for the current\n  # process.\n  #\n  # Available only on Unix-like operating systems.\n  #\n  # Security: `chroot` on its own is not an effective means of mitigation. At minimum\n  # the process needs to also drop privileges as soon as feasible after the `chroot`.\n  # Changes to the directory hierarchy or file descriptors passed via `recvmsg(2)` from\n  # outside the `chroot` jail may allow a restricted process to escape, even if it is\n  # unprivileged.\n  #\n  # ```\n  # Process.chroot(\"/var/empty\")\n  # ```\n  def self.chroot(path : String) : Nil\n    Crystal::System::Process.chroot(path)\n  end\nend\n\n# Executes the given command in a subshell.\n# Standard input, output and error are inherited.\n# Returns `true` if the command gives zero exit code, `false` otherwise.\n# The special `$?` variable is set to a `Process::Status` associated with this execution.\n#\n# If *command* contains no spaces and *args* is given, it will become\n# its argument list.\n#\n# If *command* contains spaces and *args* is given, *command* must include\n# `\"${@}\"` (including the quotes) to receive the argument list.\n#\n# No shell interpretation is done in *args*.\n#\n# Example:\n#\n# ```\n# system(\"echo *\")\n# ```\n#\n# Produces:\n#\n# ```text\n# LICENSE shard.yml Readme.md spec src\n# ```\ndef system(command : String, args : Enumerable(String)? = nil) : Bool\n  status = Process.run(command, args, shell: true, input: Process::Redirect::Inherit, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)\n  $? = status\n  status.success?\nend\n\n# Returns the standard output of executing *command* in a subshell.\n# Standard input, and error are inherited.\n# The special `$?` variable is set to a `Process::Status` associated with this execution.\n#\n# It is impossible to call this method with any regular call syntax. There is an associated literal type which calls the method with the literal content as command:\n#\n# ```\n# `echo hi`   # => \"hi\\n\"\n# $?.success? # => true\n# ```\n#\n# See [`Command` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/command.html) in the language reference.\ndef `(command : String) : String\n  process = Process.new(command, shell: true, input: Process::Redirect::Inherit, output: Process::Redirect::Pipe, error: Process::Redirect::Inherit)\n  output = process.output.gets_to_end\n  status = process.wait\n  $? = status\n  output\nend\n\nrequire \"./process/*\"\n"
  },
  {
    "path": "src/raise.cr",
    "content": "require \"c/stdio\"\nrequire \"c/stdlib\"\nrequire \"crystal/system/print_error\"\nrequire \"exception/call_stack\"\n\nException::CallStack.skip(__FILE__)\n\nprivate struct LEBReader\n  def initialize(@data : UInt8*)\n  end\n\n  def data : UInt8*\n    @data\n  end\n\n  def read_uint8 : UInt8\n    value = @data.value\n    @data += 1\n    value\n  end\n\n  def read_uint32 : UInt32\n    value = @data.as(UInt32*).value\n    @data += 4\n    value\n  end\n\n  def read_uleb128 : UInt64\n    result = 0_u64\n    shift = 0\n    while true\n      byte = read_uint8\n      result |= ((0x7f_u64 & byte) << shift)\n      break if (byte & 0x80_u8) == 0\n      shift += 7\n    end\n    result\n  end\nend\n\nprivate def traverse_eh_table(leb, start, ip, actions, &)\n  # Ref: https://chromium.googlesource.com/native_client/pnacl-libcxxabi/+/master/src/cxa_personality.cpp\n\n  throw_offset = ip - 1 - start\n  # puts \"Personality - actions : #{actions}, start: #{start}, ip: #{ip}, throw_offset: #{throw_offset}\"\n\n  lp_start_encoding = leb.read_uint8 # @LPStart encoding\n  if lp_start_encoding != 0xff_u8\n    Crystal::System.print_error \"Unexpected encoding for LPStart: 0x%x\\n\", lp_start_encoding\n    LibC.exit 1\n  end\n\n  if leb.read_uint8 != 0xff_u8 # @TType encoding\n    leb.read_uleb128           # @TType base offset\n  end\n\n  cs_encoding = leb.read_uint8 # CS Encoding (1: uleb128, 3: uint32)\n  if cs_encoding != 1 && cs_encoding != 3\n    Crystal::System.print_error \"Unexpected CS encoding: 0x%x\\n\", cs_encoding\n    LibC.exit 1\n  end\n\n  cs_table_length = leb.read_uleb128 # CS table length\n  cs_table_end = leb.data + cs_table_length\n\n  while leb.data < cs_table_end\n    cs_offset = cs_encoding == 3 ? leb.read_uint32 : leb.read_uleb128\n    cs_length = cs_encoding == 3 ? leb.read_uint32 : leb.read_uleb128\n    cs_addr = cs_encoding == 3 ? leb.read_uint32 : leb.read_uleb128\n    action = leb.read_uleb128\n    # puts \"cs_offset: #{cs_offset}, cs_length: #{cs_length}, cs_addr: #{cs_addr}, action: #{action}\"\n\n    if cs_addr != 0\n      if cs_offset <= throw_offset && throw_offset <= cs_offset + cs_length\n        if actions.includes? LibUnwind::Action::SEARCH_PHASE\n          # puts \"found\"\n          return LibUnwind::ReasonCode::HANDLER_FOUND\n        end\n\n        if actions.includes? LibUnwind::Action::HANDLER_FRAME\n          unwind_ip = start + cs_addr\n          yield unwind_ip\n          # puts \"install\"\n          return LibUnwind::ReasonCode::INSTALL_CONTEXT\n        end\n      end\n    end\n  end\n\n  nil\nend\n\n{% if flag?(:interpreted) %}\n  # interpreter does not need `__crystal_personality`\n{% elsif flag?(:win32) && !flag?(:gnu) %}\n  require \"exception/lib_unwind\"\n\n  {% begin %}\n    @[Link({{ flag?(:static) ? \"libvcruntime\" : \"vcruntime\" }})]\n  {% end %}\n  lib LibC\n    fun _CxxThrowException(ex : Void*, throw_info : Void*) : NoReturn\n  end\n\n  @[Primitive(:throw_info)]\n  def throw_info : Void*\n  end\n\n  # :nodoc:\n  @[Raises]\n  fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn\n    Crystal::System.print_error \"EXITING: __crystal_raise called\"\n    LibC.exit(1)\n  end\n{% elsif flag?(:arm) %}\n  # On ARM EHABI the personality routine is responsible for actually\n  # unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).\n  private macro __crystal_continue_unwind\n    if LibUnwind.__gnu_unwind_frame(ucb, context) != LibUnwind::ReasonCode::NO_REASON\n      return LibUnwind::ReasonCode::FAILURE\n    end\n    # puts \"continue\"\n    return LibUnwind::ReasonCode::CONTINUE_UNWIND\n  end\n\n  # :nodoc:\n  fun __crystal_personality(state : LibUnwind::State, ucb : LibUnwind::ControlBlock*, context : LibUnwind::Context) : LibUnwind::ReasonCode\n    # puts \"\\n__crystal_personality(#{state}, #{ucb}, #{context})\"\n\n    case LibUnwind::State.new(state.value & LibUnwind::State::ACTION_MASK.value)\n    when LibUnwind::State::VIRTUAL_UNWIND_FRAME\n      if state.force_unwind?\n        __crystal_continue_unwind\n      else\n        actions = LibUnwind::Action::SEARCH_PHASE\n      end\n    when LibUnwind::State::UNWIND_FRAME_STARTING\n      actions = LibUnwind::Action::HANDLER_FRAME\n    when LibUnwind::State::UNWIND_FRAME_RESUME\n      __crystal_continue_unwind\n    else\n      exit(-1)\n    end\n\n    if state.force_unwind?\n      actions |= LibUnwind::Action::FORCE_UNWIND\n    end\n\n    start = __crystal_get_region_start(ucb)\n    lsd = __crystal_get_language_specific_data(ucb)\n\n    ip = __crystal_unwind_get_ip(context)\n    leb = LEBReader.new(lsd)\n\n    reason = traverse_eh_table(leb, start, ip, actions) do |unwind_ip|\n      __crystal_unwind_set_gr(context, LibUnwind::EH_REGISTER_0, ucb.address.to_u32)\n      __crystal_unwind_set_gr(context, LibUnwind::EH_REGISTER_1, ucb.value.exception_type_id.to_u32)\n      __crystal_unwind_set_ip(context, unwind_ip)\n    end\n    return reason if reason\n\n    __crystal_continue_unwind\n  end\n{% elsif flag?(:wasm32) %}\n  # :nodoc:\n  fun __crystal_personality\n    Crystal::System.print_error \"EXITING: __crystal_personality called\"\n    LibC.exit(1)\n  end\n\n  # :nodoc:\n  @[Raises]\n  fun __crystal_raise(ex : Void*) : NoReturn\n    Crystal::System.print_error \"EXITING: __crystal_raise called\"\n    LibC.exit(1)\n  end\n\n  # :nodoc:\n  fun __crystal_get_exception(ex : Void*) : UInt64\n    Crystal::System.print_error \"EXITING: __crystal_get_exception called\"\n    LibC.exit(1)\n    0u64\n  end\n{% else %}\n  {% mingw = flag?(:win32) && flag?(:gnu) %}\n  # :nodoc:\n  fun {{ mingw ? \"__crystal_personality_imp\".id : \"__crystal_personality\".id }}(\n    version : Int32, actions : LibUnwind::Action, exception_class : UInt64, exception_object : LibUnwind::Exception*, context : Void*,\n  ) : LibUnwind::ReasonCode\n    start = LibUnwind.get_region_start(context)\n    ip = LibUnwind.get_ip(context)\n    lsd = LibUnwind.get_language_specific_data(context)\n\n    leb = LEBReader.new(lsd)\n    reason = traverse_eh_table(leb, start, ip, actions) do |unwind_ip|\n      LibUnwind.set_gr(context, LibUnwind::EH_REGISTER_0, exception_object.address)\n      LibUnwind.set_gr(context, LibUnwind::EH_REGISTER_1, exception_object.value.exception_type_id)\n      LibUnwind.set_ip(context, unwind_ip)\n    end\n    return reason if reason\n\n    return LibUnwind::ReasonCode::CONTINUE_UNWIND\n  end\n\n  {% if mingw %}\n    lib LibC\n      alias EXCEPTION_DISPOSITION = Int\n      alias DISPATCHER_CONTEXT = Void\n    end\n\n    # :nodoc:\n    lib LibUnwind\n      alias PersonalityFn = Int32, Action, UInt64, Exception*, Void* -> ReasonCode\n\n      fun _GCC_specific_handler(ms_exc : LibC::EXCEPTION_RECORD64*, this_frame : Void*, ms_orig_context : LibC::CONTEXT*, ms_disp : LibC::DISPATCHER_CONTEXT*, gcc_per : PersonalityFn) : LibC::EXCEPTION_DISPOSITION\n    end\n\n    # :nodoc:\n    fun __crystal_personality(ms_exc : LibC::EXCEPTION_RECORD64*, this_frame : Void*, ms_orig_context : LibC::CONTEXT*, ms_disp : LibC::DISPATCHER_CONTEXT*) : LibC::EXCEPTION_DISPOSITION\n      LibUnwind._GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp, ->__crystal_personality_imp)\n    end\n  {% end %}\n{% end %}\n\n{% unless flag?(:interpreted) || (flag?(:win32) && !flag?(:gnu)) || flag?(:wasm32) %}\n  # :nodoc:\n  @[Raises]\n  fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn\n    ret = LibUnwind.raise_exception(unwind_ex)\n    Crystal::System.print_error \"Failed to raise an exception: %s\\n\", ret.to_s\n    Exception::CallStack.print_backtrace\n    Crystal::System.print_exception(\"\\nTried to raise:\", unwind_ex.value.exception_object.as(Exception))\n    LibC.exit(ret)\n  end\n\n  # :nodoc:\n  fun __crystal_get_exception(unwind_ex : LibUnwind::Exception*) : UInt64\n    unwind_ex.value.exception_object.address\n  end\n{% end %}\n\n{% if flag?(:wasm32) %}\n  def raise(exception : Exception) : NoReturn\n    Crystal::System.print_error \"EXITING: Attempting to raise:\\n%s\\n\", exception.inspect_with_backtrace\n    LibIntrinsics.debugtrap\n    LibC.exit(1)\n  end\n{% else %}\n  # Raises the *exception*.\n  #\n  # This will set the exception's callstack if it hasn't been already.\n  # Re-raising a previously caught exception won't replace the callstack.\n  def raise(exception : Exception) : NoReturn\n    {% if flag?(:debug_raise) %}\n      STDERR.puts\n      STDERR.puts \"Attempting to raise: \"\n      exception.inspect_with_backtrace(STDERR)\n    {% end %}\n\n    exception.callstack ||= Exception::CallStack.new\n    raise_without_backtrace(exception)\n  end\n{% end %}\n\n# Raises an Exception with the *message*.\ndef raise(message : String) : NoReturn\n  raise Exception.new(message)\nend\n\n{% if flag?(:win32) && !flag?(:gnu) %}\n  # :nodoc:\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_raise_without_backtrace)] {% end %}\n  def raise_without_backtrace(exception : Exception) : NoReturn\n    LibC._CxxThrowException(pointerof(exception).as(Void*), throw_info)\n  end\n{% else %}\n  # :nodoc:\n  {% if flag?(:interpreted) %} @[Primitive(:interpreter_raise_without_backtrace)] {% end %}\n  def raise_without_backtrace(exception : Exception) : NoReturn\n    unwind_ex = Pointer(LibUnwind::Exception).malloc\n    unwind_ex.value.exception_class = LibC::SizeT.zero\n    unwind_ex.value.exception_cleanup = LibC::SizeT.zero\n    unwind_ex.value.exception_object = exception.as(Void*)\n    unwind_ex.value.exception_type_id = exception.crystal_type_id\n    __crystal_raise(unwind_ex)\n  end\n{% end %}\n\n# :nodoc:\nfun __crystal_raise_string(message : UInt8*)\n  raise String.new(message)\nend\n\n# :nodoc:\nfun __crystal_raise_overflow : NoReturn\n  raise OverflowError.new\nend\n\n{% if flag?(:interpreted) %}\n  # :nodoc:\n  def __crystal_raise_cast_failed(obj, type_name : String, location : String)\n    raise TypeCastError.new(\"Cast from #{obj.class} to #{type_name} failed, at #{location}\")\n  end\n{% else %}\n  # :nodoc:\n  fun __crystal_raise_cast_failed(from_type : Void*, to_type : Void*, location : Void*) : NoReturn\n    if location\n      raise TypeCastError.new(\"Cast from #{from_type.as(String)} to #{to_type.as(String)} failed, at #{location.as(String)}\")\n    else\n      raise TypeCastError.new(\"Cast from #{from_type.as(String)} to #{to_type.as(String)} failed\")\n    end\n  end\n{% end %}\n"
  },
  {
    "path": "src/random/isaac.cr",
    "content": "require \"random/secure\"\n\n# (c) Bob Jenkins, March 1996, Public Domain\n# You may use this code in any way you wish, and it is free.  No warrantee.\n# http://burtleburtle.net/bob/rand/isaacafa.html\n\nclass Random::ISAAC\n  include Random\n\n  private getter counter\n  private getter aa\n  private getter bb\n  private getter cc\n\n  private def random_seeds\n    Random::Secure.rand(StaticArray(UInt32, 8))\n  end\n\n  def initialize(seeds = random_seeds)\n    @rsl = StaticArray(UInt32, 256).new { 0_u32 }\n    @mm = StaticArray(UInt32, 256).new { 0_u32 }\n    @counter = 0\n    @aa = @bb = @cc = 0_u32\n    init_by_array(seeds)\n  end\n\n  def new_seed(seeds = random_seeds)\n    @aa = @bb = @cc = 0_u32\n    init_by_array(seeds)\n  end\n\n  def next_u : UInt32\n    if (@counter -= 1) == -1\n      isaac\n      @counter = 255\n    end\n    @rsl[counter]\n  end\n\n  private def isaac\n    @cc &+= 1\n    @bb &+= cc\n\n    256.times do |i|\n      @aa ^= case i % 4\n             when 0 then aa << 13\n             when 1 then aa >> 6\n             when 2 then aa << 2\n             else        aa >> 16\n             end\n      x = @mm[i]\n      @aa = @mm[(i + 128) % 256] &+ aa\n      @mm[i] = y = @mm[(x >> 2) % 256] &+ aa &+ bb\n      @rsl[i] = @bb = @mm[(y >> 10) % 256] &+ x\n    end\n  end\n\n  private def init_by_array(seeds)\n    seed_len = seeds.size\n    256.times { |i| @rsl[i] = i < seed_len ? seeds[i].to_u32 : 0_u32 }\n\n    a = b = c = d = e = f = g = h = 0x9e3779b9_u32\n\n    mix = -> {\n      a ^= b << 11; d &+= a; b &+= c\n      b ^= c >> 2; e &+= b; c &+= d\n      c ^= d << 8; f &+= c; d &+= e\n      d ^= e >> 16; g &+= d; e &+= f\n      e ^= f << 10; h &+= e; f &+= g\n      f ^= g >> 4; a &+= f; g &+= h\n      g ^= h << 8; b &+= g; h &+= a\n      h ^= a >> 9; c &+= h; a &+= b\n    }\n    4.times(&mix)\n\n    scramble = ->(seed : StaticArray(UInt32, 256)) {\n      0.step(to: 255, by: 8) do |i|\n        a &+= seed[i]; b &+= seed[i + 1]; c &+= seed[i + 2]; d &+= seed[i + 3]\n        e &+= seed[i + 4]; f &+= seed[i + 5]; g &+= seed[i + 6]; h &+= seed[i + 7]\n        mix.call\n        @mm[i] = a; @mm[i + 1] = b; @mm[i + 2] = c; @mm[i + 3] = d\n        @mm[i + 4] = e; @mm[i + 5] = f; @mm[i + 6] = g; @mm[i + 7] = h\n      end\n    }\n\n    scramble.call(@rsl)\n    scramble.call(@mm)\n\n    isaac\n    @counter = 256\n  end\nend\n"
  },
  {
    "path": "src/random/pcg32.cr",
    "content": "require \"random/secure\"\n\n# This is a Crystal conversion of basic C PCG implementation\n#\n#  Original file notice:\n#\n#  PCG Random Number Generation for C.\n#\n#  Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>\n#\n#  Licensed under the Apache License, Version 2.0 (the \"License\");\n#  you may not use this file except in compliance with the License.\n#  You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n#  Unless required by applicable law or agreed to in writing, software\n#  distributed under the License is distributed on an \"AS IS\" BASIS,\n#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  See the License for the specific language governing permissions and\n#  limitations under the License.\n#\n#  For additional information about the PCG random number generation scheme,\n#  including its license and other licensing options, visit\n#\n#        http://www.pcg-random.org\n#\n\n# pcg32_random_r:\n#       -  result:      32-bit unsigned int (uint32_t)\n#       -  period:      2^64   (* 2^63 streams)\n#       -  state type:  pcg32_random_t (16 bytes)\n#       -  output func: XSH-RR\nclass Random::PCG32\n  include Random\n\n  PCG_DEFAULT_MULTIPLIER_64 = 6364136223846793005_u64\n\n  @state : UInt64\n  @inc : UInt64\n\n  def self.new\n    new(Random::Secure.rand(UInt64::MIN..UInt64::MAX), Random::Secure.rand(UInt64::MIN..UInt64::MAX))\n  end\n\n  # :nodoc:\n  def initialize(other : self)\n    @state = other.@state\n    @inc = other.@inc\n  end\n\n  def initialize(initstate : UInt64, initseq = 0_u64)\n    # initialize to zeros to prevent compiler complains\n    @state = 0_u64\n    @inc = 0_u64\n    new_seed(initstate, initseq)\n  end\n\n  def new_seed : UInt32\n    new_seed(Random::Secure.rand(UInt64::MIN..UInt64::MAX), Random::Secure.rand(UInt64::MIN..UInt64::MAX))\n  end\n\n  def new_seed(initstate : UInt64, initseq = 0_u64) : UInt32\n    @state = 0_u64\n    @inc = (initseq << 1) | 1\n    next_u\n    @state &+= initstate\n    next_u\n  end\n\n  def next_u : UInt32\n    oldstate = @state\n    @state = oldstate &* PCG_DEFAULT_MULTIPLIER_64 &+ @inc\n    xorshifted = UInt32.new!(((oldstate >> 18) ^ oldstate) >> 27)\n    rot = UInt32.new!(oldstate >> 59)\n    UInt32.new!(xorshifted.rotate_right(rot))\n  end\n\n  def jump(delta)\n    deltau64 = UInt64.new!(delta)\n    acc_mult = 1u64\n    acc_plus = 0u64\n    cur_plus = @inc\n    cur_mult = PCG_DEFAULT_MULTIPLIER_64\n    while (deltau64 > 0)\n      if deltau64 & 1 > 0\n        acc_mult &*= cur_mult\n        acc_plus = acc_plus &* cur_mult &+ cur_plus\n      end\n      cur_plus = (cur_mult &+ 1) &* cur_plus\n      cur_mult &*= cur_mult\n      deltau64 //= 2\n    end\n    @state = acc_mult &* @state &+ acc_plus\n  end\n\n  def split_internal(other : self) : Nil\n    @inc = ((@inc &+ 1) << 1) | 1\n    other.next_u\n  end\nend\n"
  },
  {
    "path": "src/random/secure.cr",
    "content": "require \"crystal/system/random\"\n\n# `Random::Secure` generates random numbers from a secure source provided by the system.\n#\n# It uses a [cryptographically secure pseudorandom number generator (CSPRNG)](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator)\n# for cryptography and secure usages such as generating secret keys, or to seed\n# another pseudorandom number generator (PRNG).\n#\n# ```\n# Random::Secure.rand(6)            # => 4\n# [1, 5, 6].shuffle(Random::Secure) # => [6, 1, 5]\n# ```\n#\n# On BSD-based systems and macOS/Darwin, it uses [`arc4random`](https://man.openbsd.org/arc4random),\n# on Linux [`getrandom`](http://man7.org/linux/man-pages/man2/getrandom.2.html),\n# on Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom),\n# and falls back to reading from `/dev/urandom` on UNIX systems.\nmodule Random::Secure\n  extend Random\n  extend self\n\n  def next_u\n    Crystal::System::Random.next_u\n  end\n\n  def random_bytes(buf : Bytes) : Nil\n    Crystal::System::Random.random_bytes(buf)\n  end\n\n  {% for type in [UInt8, UInt16, UInt32, UInt64, UInt128] %}\n    # Generates a random integer of a given type. The number of bytes to\n    # generate can be limited; by default it will generate as many bytes as\n    # needed to fill the integer size.\n    private def rand_type(type : {{type}}.class, needed_parts = nil) : {{type}}\n      needed_bytes =\n        if needed_parts\n          needed_parts * sizeof(typeof(next_u))\n        else\n          sizeof({{type}})\n        end\n\n      buf = uninitialized UInt8[sizeof({{type}})]\n\n      if needed_bytes < sizeof({{type}})\n        bytes = Slice.new(buf.to_unsafe, needed_bytes)\n        random_bytes(bytes)\n\n        bytes.reduce({{type}}.new(0)) do |result, byte|\n          (result << 8) | byte\n        end\n      else\n        random_bytes(buf.to_slice)\n        buf.unsafe_as({{type}})\n      end\n    end\n  {% end %}\n\n  {% for type in [Int8, Int16, Int32, Int64, Int128] %}\n    private def rand_type(type : {{type}}.class, needed_bytes = sizeof({{type}})) : {{type}}\n      result = rand_type({{\"U#{type}\".id}}, needed_bytes)\n      {{type}}.new!(result)\n    end\n  {% end %}\nend\n"
  },
  {
    "path": "src/random.cr",
    "content": "require \"base64\"\nrequire \"random/pcg32\"\n\n# `Random` provides an interface for random values generation, using a pseudo random number generator (PRNG).\n#\n# ```\n# Random.rand    # => 0.167595\n# Random.rand(5) # => 2\n# ```\n#\n# The above methods delegate to a `Random` instance.\n#\n# ```\n# r = Random.new\n# r.rand      # => 0.0372991\n# r.next_bool # => true\n# r.next_int  # => 2223112\n# ```\n#\n# This module also defines a global method `#rand`, which `Array#sample` and `Array#shuffle` delegates.\n#\n# ```\n# rand     # => 0.293829\n# rand(10) # => 8\n# ```\n#\n# An instance of each class that includes `Random` is a random number generator with its own state.\n# Usually such RNGs can be initialized with a seed, which defines their initial state. It is\n# guaranteed that after initializing two different instances with the same seed, and then executing\n# the same calls on both of them, you will get the same results. This allows exactly reproducing the\n# same seemingly random events by just keeping the seed.\n#\n# It is possible to make a custom RNG by including `Random` and implementing `next_u` to return an\n# unsigned number of a pre-determined type (usually `UInt32`). The numbers generated by your RNG\n# must be uniformly distributed in the whole range of possible values for that type (e.g.\n# `0u32..UInt32::MAX`). This allows all other methods of `Random` to be based on this and still\n# produce uniformly distributed results. Your `Random` class should also have a way to initialize\n# it. For pseudo-random number generators that will usually be an integer seed, but there are no\n# rigid requirements for the initialization.\n#\n# The default PRNG is `Random::PCG32` which has a good overall statistical\n# distribution (low bias of generated numbers) and is fast for overall usages on\n# different platforms, but isn't cryptographically secure. If a third party has\n# access to some generated numbers, she may deduce incoming numbers, putting\n# your application at risk.\n#\n# It is recommended to use a secure source, such as `Random::Secure`,\n# `Random::ISAAC` or ChaCha20 for anything that needs security, such as online\n# games, identification tokens, salts, initializing a PRNG, ... These PRNG are\n# slower but cryptographically secure, so a third party can't deduce incoming\n# numbers.\nmodule Random\n  @[Deprecated(\"Use `#rand`, `Random.next_int` or `Random::Secure.random_bytes` for example, or create a local instance with `Random.new` instead.\")]\n  DEFAULT = PCG32.new\n\n  # :nodoc:\n  thread_local(thread_default : ::Random::PCG32) do\n    ::Random::PCG32.new\n  end\n\n  # :nodoc:\n  #\n  # Same as `Random.thread_default#split` but allocates the dup on the stack\n  # when possible (hence the macro).\n  macro split_on_stack\n    {% if compare_versions(Crystal::VERSION, \"1.12.0\") >= 0 %}\n      %thread_rng = ::Random.thread_default.as(::Random::PCG32)\n      %buf = uninitialized ::ReferenceStorage(::Random::PCG32)\n      %copy = ::Random::PCG32.unsafe_construct(pointerof(%buf), %thread_rng)\n      %thread_rng.split_internal(%copy)\n      %copy\n    {% else %}\n      ::Random.thread_default.split\n    {% end %}\n  end\n\n  # Initializes an instance with the given *seed* and *sequence*.\n  def self.new(seed, sequence = 0_u64)\n    PCG32.new(seed.to_u64, sequence)\n  end\n\n  # Initializes an instance seeded from a system source.\n  def self.new\n    PCG32.new\n  end\n\n  # Reseed the generator.\n  def new_seed\n    raise NotImplementedError.new(\"{{@type}}#new_seed\")\n  end\n\n  # Generates a random unsigned integer.\n  #\n  # The integers must be uniformly distributed between `0` and\n  # the maximal value for the chosen type.\n  abstract def next_u\n\n  # Splits the current instance into two seemingly independent instances that\n  # will return distinct sequences of random numbers. Returns a new instance.\n  #\n  # ```\n  # random = Random.new\n  # split1 = random.split\n  # split2 = random.split\n  #\n  # Array.new(5) { random.rand(99) } # => [79, 42, 54, 17, 52]\n  # Array.new(5) { split1.rand(99) } # => [90, 37, 15, 74, 61]\n  # Array.new(5) { split2.rand(99) } # => [6, 87, 5, 73, 71]\n  # ```\n  def split : self\n    copy = dup\n    split_internal(copy)\n    copy\n  end\n\n  # The internal implementation for `#split` where *self* is the original\n  # instance and *other* the duplicated instance to be returned.\n  #\n  # The default `Random` implementation in stdlib is splittable, but not every\n  # PRNG algorithm is splittable, so the method raises a `NotImplementedError`\n  # exception by default.\n  def split_internal(other : self) : Nil\n    raise NotImplementedError.new(\"#{self.class}#split\")\n  end\n\n  # Generates a random `Bool`.\n  #\n  # ```\n  # Random.new.next_bool # => true\n  # ```\n  def next_bool : Bool\n    next_u.odd?\n  end\n\n  # Same as `rand(Int32::MIN..Int32::MAX)`.\n  def next_int : Int32\n    rand_type(Int32)\n  end\n\n  # See `#rand`.\n  def next_float : Float64\n    max_prec = 1u64 << 53 # Float64, excluding mantissa, has 2^53 values\n    rand(max_prec) / (max_prec - 1).to_f64\n  end\n\n  # Generates a random `Float64` between `0` and `1`.\n  #\n  # ```\n  # r = Random.new\n  # r.rand # => 0.167595\n  # r.rand # => 0.0372991\n  # ```\n  def rand : Float64\n    next_float\n  end\n\n  # Generates a random integer which is greater than or equal to `0`\n  # and less than *max*.\n  #\n  # The return type always matches the supplied argument.\n  #\n  # ```\n  # Random.new.rand(10)   # => 5\n  # Random.new.rand(5000) # => 2243\n  # ```\n  def rand(max : Int) : Int\n    rand_int(max)\n  end\n\n  {% for size in [8, 16, 32, 64, 128] %}\n    {% utype = \"UInt#{size}\".id %}\n    {% for type in [\"Int#{size}\".id, utype] %}\n      private def rand_int(max : {{type}}) : {{type}}\n        unless max > 0\n          raise ArgumentError.new \"Invalid bound for rand: #{max}\"\n        end\n\n        # The basic ideas of the algorithm are best illustrated with examples.\n        #\n        # Let's say we have a random number generator that gives uniformly distributed random\n        # numbers between 0 and 15. We need to get a uniformly distributed random number between\n        # 0 and 5 (*max* = 6). The typical mistake made in this case is to just use `rand() % 6`,\n        # but it is clear that some results will appear more often than others. So, the surefire\n        # approach is to make the RNG spit out numbers until it gives one inside our desired range.\n        # That is really wasteful though. So the approach taken here is to discard only a small\n        # range of the possible generated numbers, and use the modulo operation on the \"valid\" ones,\n        # like this (where X means \"discard and try again\"):\n        #\n        # Generated number:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15\n        #           Result:  0  1  2  3  4  5  0  1  2  3  4  5  X  X  X  X\n        #\n        # 12 is the *limit* here - the highest number divisible by *max* while still being within\n        # bounds of what the RNG can produce.\n        #\n        # On the other side of the spectrum is the problem of generating a random number in a higher\n        # range than what the RNG can produce. Let's say we have the same mentioned RNG, but we need\n        # a uniformly distributed random number between 0 and 255. All that needs to be done is to\n        # generate two random numbers between 0 and 15, and combine their bits\n        # (i.e. `rand()*16 + rand()`).\n        #\n        # Using a combination of these tricks, any RNG can be turned into any RNG, however, there\n        # are several difficult parts about this. The code below uses as few calls to the underlying\n        # RNG as possible, meaning that (with the above example) with *max* being 257, it would call\n        # the RNG 3 times. (Of course, it doesn't actually deal with RNGs that produce numbers\n        # 0 to 15, only with the `UInt8`, `UInt16`, `UInt32` and `UInt64` ranges.\n        #\n        # Another problem is how to actually compute the *limit*. The obvious way to do it, which is\n        # `(RAND_MAX + 1) // max * max`, fails because `RAND_MAX` is usually already the highest\n        # number that an integer type can hold. And even the *limit* itself will often be\n        # `RAND_MAX + 1`, meaning that we don't have to discard anything. The ways to deal with this\n        # are described below.\n\n        # if max - 1 <= typeof(next_u)::MAX\n        if typeof(next_u).new!(max &- 1) == max &- 1\n          # One number from the RNG will be enough.\n          # All the computations will (almost) fit into `typeof(next_u)`.\n\n          # Relies on integer overflow + wraparound to find the highest number divisible by *max*.\n          limit = typeof(next_u).new(0) &- (typeof(next_u).new(0) &- max) % max\n          # *limit* might be 0, which means it would've been `typeof(next_u)::MAX + 1, but didn't\n          # fit into the integer type.\n\n          loop do\n            result = next_u\n\n            # For a uniform distribution we may need to throw away some numbers\n            if result < limit || limit == 0\n              return {{type}}.new!(result % max)\n            end\n          end\n        else\n          # We need to find out how many random numbers need to be combined to be able to generate a\n          # random number of this magnitude.\n          # All the computations will be based on `{{utype}}` as the larger type.\n\n          # `rand_max - 1` is the maximal number we can get from combining `needed_parts` random\n          # numbers.\n          # Compute *rand_max* as `(typeof(next_u)::MAX + 1) ** needed_parts)`.\n          # If *rand_max* overflows, that means it has reached `high({{utype}}) + 1`.\n          rand_max = {{utype}}.new(1) << (sizeof(typeof(next_u))*8)\n          needed_parts = 1\n          while rand_max < max && rand_max > 0\n            rand_max <<= sizeof(typeof(next_u))*8\n            needed_parts += 1\n          end\n\n          limit =\n            if rand_max > 0\n              # `rand_max` didn't overflow, so we can calculate the *limit* the straightforward way.\n              rand_max // max &* max\n            else\n              # *rand_max* is `{{utype}}::MAX + 1`, need the same wraparound trick. *limit* might\n              # overflow, which means it would've been `{{utype}}::MAX + 1`, but didn't fit into\n              # the integer type.\n              {{utype}}.new(0) &- ({{utype}}.new(0) &- max) % max\n            end\n\n          loop do\n            result = rand_type({{utype}}, needed_parts)\n\n            # For a uniform distribution we may need to throw away some numbers.\n            if result < limit || limit == 0\n              return {{type}}.new(result % max)\n            end\n          end\n        end\n      end\n\n      private def rand_range(range : Range({{type}}, {{type}})) : {{type}}\n        span = {{utype}}.new!(range.end &- range.begin)\n        if range.excludes_end?\n          unless range.begin < range.end\n            raise ArgumentError.new \"Invalid range for rand: #{range}\"\n          end\n        else\n          unless range.begin <= range.end\n            raise ArgumentError.new \"Invalid range for rand: #{range}\"\n          end\n          if range.begin == {{type}}::MIN && range.end == {{type}}::MAX\n            return rand_type({{type}})\n          end\n          span += 1\n        end\n        # this addition never overflows because `rand_int` is unsigned and\n        # `rand_int <= range.end - range.begin <= type::MAX - range.begin`\n        range.begin &+ rand_int(span)\n      end\n\n      # Generates a random integer in range `{{type}}::MIN..{{type}}::MAX`.\n      private def rand_type(type : {{type}}.class, needed_parts = sizeof({{type}}) // sizeof(typeof(next_u))) : {{type}}\n        # Build up the number combining multiple outputs from the RNG.\n        result = {{utype}}.new!(next_u)\n        (needed_parts - 1).times do\n          result <<= sizeof(typeof(next_u))*8\n          result |= {{utype}}.new!(next_u)\n        end\n        {{type}}.new!(result)\n      end\n    {% end %}\n  {% end %}\n\n  # Returns a random `Float` which is greater than or equal to `0`\n  # and less than *max*.\n  #\n  # ```\n  # Random.new.rand(3.5)    # => 2.88938\n  # Random.new.rand(10.725) # => 7.70147\n  # ```\n  def rand(max : Float64) : Float64\n    unless max > 0\n      raise ArgumentError.new \"Invalid bound for rand: #{max}\"\n    end\n    max_prec = 1u64 << 53 # Float64, excluding mantissa, has 2^53 values\n    rand(max_prec) / max_prec.to_f64 * max\n  end\n\n  # :ditto:\n  def rand(max : Float32) : Float32\n    unless max > 0\n      raise ArgumentError.new \"Invalid bound for rand: #{max}\"\n    end\n    max_prec = 1u32 << 24 # Float32, excluding mantissa, has 2^24 values\n    rand(max_prec) / max_prec.to_f32 * max\n  end\n\n  # Returns a random integer in the given *range*.\n  #\n  # The return type always matches the supplied argument.\n  #\n  # ```\n  # Random.new.rand(10..20)                 # => 14\n  # Random.new.rand(Int64::MIN..Int64::MAX) # => -5297435808626736140\n  # ```\n  def rand(range : Range(Int, Int)) : Int\n    rand_range(range)\n  end\n\n  # Returns a random `Float` in the given *range*.\n  #\n  # ```\n  # Random.new.rand(6.2..21.768) # => 15.2989\n  # ```\n  def rand(range : Range(Float, Float)) : Float\n    span = range.end - range.begin\n    if range.excludes_end?\n      unless range.begin < range.end\n        raise ArgumentError.new \"Invalid range for rand: #{range}\"\n      end\n      range.begin + rand(span)\n    else\n      unless range.begin <= range.end\n        raise ArgumentError.new \"Invalid range for rand: #{range}\"\n      end\n      range.begin + rand * span\n    end\n  end\n\n  {% for type, values in {\n                           \"Int8\".id    => %w(20 -66 89 19),\n                           \"UInt8\".id   => %w(186 221 127 245),\n                           \"Int16\".id   => %w(-32554 32169 -20152 -7686),\n                           \"UInt16\".id  => %w(39546 44091 2874 17348),\n                           \"Int32\".id   => %w(1870830079 -1043532158 -867180637 -1216773590),\n                           \"UInt32\".id  => %w(3147957137 4245108745 2207809043 3184391838),\n                           \"Int64\".id   => %w(4438449217673515190 8514493061600538358 -4874671083204037318 -7825896160729246667),\n                           \"UInt64\".id  => %w(15004487597684511003 12027825265648206103 11303949506191212698 6228566501671148658),\n                           \"Int128\".id  => %w(-33248638598154624979861619415313153263 7715345987200799268985566794637461715 51883986405785085023723116953594906714 -63505201678563022521901409748929046368),\n                           \"UInt128\".id => %w(209016375821699277802308597707088869733 168739091726124084850659068882871627438 293712757766410232411790495845165436283 15480005665598870938163293877660434201),\n                         } %}\n    # Returns a random {{type}}\n    #\n    # ```\n    # rand({{type}}) # => {{values[0].id}}\n    # ```\n    def rand(type : {{type}}.class) : {{type}}\n      rand_type_from_bytes(type)\n    end\n\n    # Returns a StaticArray filled with random {{type}} values.\n    #\n    # ```\n    # rand(StaticArray({{type}}, 4)) # => StaticArray[{{values.join(\", \").id}}]\n    # ```\n    def rand(type : StaticArray({{type}}, _).class)\n      rand_type_from_bytes(type)\n    end\n  {% end %}\n\n  private def rand_type_from_bytes(t : T.class) forall T\n    buffer = uninitialized UInt8[sizeof(T)]\n    random_bytes(buffer.to_slice)\n    buffer.unsafe_as(T)\n  end\n\n  # Fills a given slice with random bytes.\n  #\n  # ```\n  # slice = Bytes.new(4) # => [0, 0, 0, 0]\n  # Random.new.random_bytes(slice)\n  # slice # => [217, 118, 38, 196]\n  # ```\n  def random_bytes(buf : Bytes) : Nil\n    ptr = buf.to_unsafe\n    finish = buf.to_unsafe + buf.size\n\n    while ptr < finish\n      random = next_u\n      rand_ptr = pointerof(random).as(UInt8*)\n      if IO::ByteFormat::SystemEndian != IO::ByteFormat::LittleEndian\n        rand_ptr.to_slice(sizeof(typeof(next_u))).reverse!\n      end\n      rand_ptr.copy_to(ptr, {finish - ptr, sizeof(typeof(next_u))}.min)\n      ptr += sizeof(typeof(next_u))\n    end\n  end\n\n  # Generates a slice filled with *n* random bytes.\n  #\n  # ```\n  # Random.new.random_bytes    # => [145, 255, 191, 133, 132, 139, 53, 136, 93, 238, 2, 37, 138, 244, 3, 216]\n  # Random.new.random_bytes(4) # => [217, 118, 38, 196]\n  # ```\n  def random_bytes(n : Int = 16) : Bytes\n    raise ArgumentError.new \"Negative size: #{n}\" if n < 0\n    Bytes.new(n).tap { |buf| random_bytes(buf) }\n  end\n\n  # Generates *n* random bytes that are encoded into base64.\n  #\n  # The parameter *n* specifies the length, in bytes, of the random number to\n  # be generated. The length of the result string is about 4/3 of *n* due to\n  # the base64 encoding. The result receives a padding\n  # consisting of `=` characters to fill up the string size to a multiple of 4.\n  #\n  # Check `Base64#strict_encode` for details.\n  #\n  # ```\n  # Random::Secure.base64(4) # => \"fK1eYg==\"\n  # ```\n  #\n  # It is recommended to use the secure `Random::Secure` as a source or another\n  # cryptographically quality PRNG such as `Random::ISAAC` or ChaCha20.\n  def base64(n : Int = 16) : String\n    Base64.strict_encode(random_bytes(n))\n  end\n\n  # Generates *n* random bytes that are encoded as a URL-safe base64 string.\n  #\n  # The parameter *n* specifies the length, in bytes, of the random number to\n  # be generated. The length of the result string is about 4/3 of *n* due to\n  # the base64 encoding. If *padding* is `true`, the result receives a padding\n  # consisting of `=` characters to fill up the string size to a multiple of 4.\n  #\n  # Check `Base64#urlsafe_encode` for details.\n  #\n  # ```\n  # Random::Secure.urlsafe_base64                    # => \"MAD2bw8QaBdvITCveBNCrw\"\n  # Random::Secure.urlsafe_base64(8, padding: true)  # => \"vvP1kcs841I=\"\n  # Random::Secure.urlsafe_base64(16, padding: true) # => \"og2aJrELDZWSdJfVGkxNKw==\"\n  # ```\n  #\n  # It is recommended to use the secure `Random::Secure` as a source or another\n  # cryptographically quality PRNG such as `Random::ISAAC` or ChaCha20.\n  def urlsafe_base64(n : Int = 16, padding = false) : String\n    Base64.urlsafe_encode(random_bytes(n), padding)\n  end\n\n  # Generates a hexadecimal string based on *n* random bytes.\n  #\n  # The bytes are encoded into a string of two-digit hexadecimal\n  # number (00-ff) per byte.\n  #\n  # ```\n  # Random::Secure.hex    # => \"05f100a1123f6bdbb427698ab664ff5f\"\n  # Random::Secure.hex(1) # => \"1a\"\n  # ```\n  #\n  # It is recommended to use the secure `Random::Secure` as a source or another\n  # cryptographically quality PRNG such as `Random::ISAAC` or ChaCha20.\n  def hex(n : Int = 16) : String\n    random_bytes(n).hexstring\n  end\n\n  # See `#split`.\n  def self.split : Random\n    thread_default.split.as(Random)\n  end\n\n  # See `#next_bool`.\n  def self.next_bool : Bool\n    thread_default.next_bool\n  end\n\n  # See `#next_int`.\n  def self.next_int : Int32\n    thread_default.next_int\n  end\n\n  # See `#rand`.\n  def self.rand : Float64\n    thread_default.rand\n  end\n\n  # See `#rand(x)`.\n  def self.rand(x)\n    thread_default.rand(x)\n  end\nend\n\n# See `Random#rand`.\ndef rand : Float64\n  Random.rand\nend\n\n# See `Random#rand(x)`.\ndef rand(x)\n  Random.rand(x)\nend\n"
  },
  {
    "path": "src/range/bsearch.cr",
    "content": "{% for p in [64, 32] %}\n  # Cast integer as floating number value with keeping binary structure.\n  private def int_as_float(i : Int{{ p }})\n    # Integer uses two's complement to represent signed number, but\n    # floating number value uses sign bit. This difference causes a\n    # problem that it reproduce incorrect value when you sum positive\n    # number and negative number. It fixes this problem.\n    if i < 0\n      i = -i\n      -i.unsafe_as(Float{{ p }})\n    else\n      i.unsafe_as(Float{{ p }})\n    end\n  end\n\n  # Cast floating number value as integer with keeping binary structure.\n  private def float_as_int(f : Float{{ p }})\n    if f < 0\n      f = -f\n      -f.unsafe_as(Int{{ p }})\n    else\n      f.unsafe_as(Int{{ p }})\n    end\n  end\n\n  private def bsearch_internal(from : Float{{ p }}, to, exclusive, &block)\n    bsearch_internal from, to.to_f{{ p }}, exclusive do |value|\n      yield value\n    end\n  end\n\n  private def bsearch_internal(from, to : Float{{ p }}, exclusive)\n    bsearch_internal from.to_f{{ p }}, to, exclusive do |value|\n      yield value\n    end\n  end\n\n  private def bsearch_internal(from : Float{{ p }}, to : Float{{ p }}, exclusive)\n    from = float_as_int from\n    to = float_as_int to\n    to -= 1 if exclusive\n\n    bsearch_internal(from, to, false){ |i| yield int_as_float i }\n      .try{ |i| int_as_float i }\n  end\n\n  private def bsearch_internal(from : Int{{ p }}, to : Int{{ p }}, exclusive)\n    saved_to = to\n    satisfied = nil\n    while from < to\n      mid = (from < 0) == (to < 0) ? from + ((to - from) >> 1)\n          : (from < -to) ? -(((- from - to - 1) >> 1) + 1) : ((from + to) >> 1)\n\n      if yield mid\n        satisfied = mid\n        to = mid\n      else\n        from = mid + 1\n      end\n    end\n\n    if !exclusive && from == saved_to && yield from\n      satisfied = from\n    end\n\n    satisfied\n  end\n{% end %}\n\nstruct Range(B, E)\n  # By using binary search, returns the first element\n  # for which the passed block returns a truthy value.\n  #\n  # If the block returns a falsey value, the element to be found lies\n  # behind. If the block returns a truthy value, the element to be found\n  # is itself or lies in front.\n  #\n  # Returns `nil` if the block didn't return a truthy value for any element.\n  #\n  # ```\n  # (0..10).bsearch { |x| x >= 5 }                       # => 5\n  # (0..Float64::INFINITY).bsearch { |x| x ** 4 >= 256 } # => 4\n  # ```\n  def bsearch(&block : B | E -> _)\n    from = self.begin\n    to = self.end\n\n    # If the range consists of floating value,\n    # it uses specialized implementation for floating value.\n    # This implementation is very fast. For example,\n    # `(1..1e300).bsearch{ false }` loops over 2000 times in\n    # popular implementation, but in this implementation loops 65 times\n    # at most.\n    {% for v in %w(from to) %}\n      if {{ v.id }}.is_a?(Float::Primitive)\n        return bsearch_internal from, to, self.excludes_end? do |value|\n          yield value\n        end\n      end\n    {% end %}\n\n    saved_to = to\n    satisfied = nil\n    while from < to\n      mid = from + ((to - from) >> 1)\n\n      if yield mid\n        satisfied = mid\n        to = mid\n      else\n        from = mid + 1\n      end\n    end\n\n    if !self.excludes_end? && from == saved_to && yield from\n      satisfied = from\n    end\n\n    satisfied\n  end\nend\n"
  },
  {
    "path": "src/range.cr",
    "content": "# A `Range` represents an interval: a set of values with a beginning and an end.\n#\n# Ranges may be constructed using the usual `new` method or with literals:\n#\n# ```\n# x..y  # an inclusive range, in mathematics: [x, y]\n# x...y # an exclusive range, in mathematics: [x, y)\n# (x..) # an endless range, in mathematics: >= x\n# ..y   # a beginless inclusive range, in mathematics: <= y\n# ...y  # a beginless exclusive range, in mathematics: < y\n# ```\n#\n# See [`Range` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/range.html) in the language reference.\n#\n# An easy way to remember which one is inclusive and which one is exclusive it\n# to think of the extra dot as if it pushes *y* further away, thus leaving it outside of the range.\n#\n# Ranges typically involve integers, but can be created using arbitrary objects\n# as long as they define `succ` (or `pred` for `reverse_each`), to get the\n# next element in the range, and `<` and `==`, to know when the range reached the end:\n#\n# ```\n# # Represents a string of 'x's.\n# struct Xs\n#   include Comparable(Xs)\n#\n#   getter size\n#\n#   def initialize(@size : Int32)\n#   end\n#\n#   def succ\n#     Xs.new(@size + 1)\n#   end\n#\n#   def <=>(other)\n#     @size <=> other.size\n#   end\n#\n#   def inspect(io)\n#     @size.times { io << 'x' }\n#   end\n#\n#   def to_s(io)\n#     io << @size << ' '\n#     inspect(io)\n#   end\n# end\n# ```\n#\n# An example of using `Xs` to construct a range:\n#\n# ```\n# r = Xs.new(3)..Xs.new(6)\n# r.to_s                 # => \"xxx..xxxxxx\"\n# r.to_a                 # => [Xs.new(3), Xs.new(4), Xs.new(5), Xs.new(6)]\n# r.includes?(Xs.new(5)) # => true\n# ```\nstruct Range(B, E)\n  include Enumerable(B)\n  include Iterable(B)\n\n  # Returns the object that defines the beginning of this range.\n  #\n  # ```\n  # (1..10).begin  # => 1\n  # (1...10).begin # => 1\n  # ```\n  getter begin : B\n\n  # Returns the object that defines the end of the range.\n  #\n  # ```\n  # (1..10).end  # => 10\n  # (1...10).end # => 10\n  # ```\n  getter end : E\n\n  # Returns `true` if the range is exclusive.\n  # Returns `false` otherwise (default).\n  getter? exclusive : Bool\n\n  # Constructs a `Range` using the given beginning and end.\n  #\n  # ```\n  # Range.new(1, 10)                  # => 1..10\n  # Range.new(1, 10, exclusive: true) # => 1...10\n  # ```\n  def initialize(@begin : B, @end : E, @exclusive : Bool = false)\n  end\n\n  def ==(other : Range)\n    @begin == other.@begin && @end == other.@end && @exclusive == other.@exclusive\n  end\n\n  # Returns an `Iterator` that cycles over the values of this range.\n  #\n  # ```\n  # (1..3).cycle.first(5).to_a # => [1, 2, 3, 1, 2]\n  # ```\n  def cycle\n    each.cycle\n  end\n\n  # Iterates over the elements of this range, passing each in turn to the block.\n  #\n  # ```\n  # (10..15).each { |n| print n, ' ' }\n  # # prints: 10 11 12 13 14 15\n  # ```\n  def each(&) : Nil\n    current = @begin\n    if current.nil?\n      raise ArgumentError.new(\"Can't each beginless range\")\n    end\n\n    # TODO: This typeof and the macro interpolations are a workaround until #9324 is fixed.\n    typeof(yield current)\n\n    {% if E == Nil %}\n      while true\n        {{ \"yield current\".id }}\n        current = current.succ\n      end\n    {% else %}\n      end_value = @end\n      while end_value.nil? || current < end_value\n        {{ \"yield current\".id }}\n        current = current.succ\n      end\n      {{ \"yield current\".id }} if !@exclusive && current == end_value\n    {% end %}\n  end\n\n  # Returns an `Iterator` over the elements of this range.\n  #\n  # ```\n  # (1..3).each.skip(1).to_a # => [2, 3]\n  # ```\n  def each\n    if @begin.nil?\n      raise ArgumentError.new(\"Can't each beginless range\")\n    end\n\n    ItemIterator.new(self)\n  end\n\n  # Iterates over the elements of this range in reverse order,\n  # passing each in turn to the block.\n  #\n  # ```\n  # (10...15).reverse_each { |n| print n, ' ' }\n  # # prints: 14 13 12 11 10\n  # ```\n  def reverse_each(&) : Nil\n    end_value = @end\n    if end_value.nil?\n      raise ArgumentError.new(\"Can't reverse_each endless range\")\n    end\n\n    begin_value = @begin\n\n    yield end_value if !@exclusive && (begin_value.nil? || !(end_value < begin_value))\n    current = end_value\n\n    {% if B == Nil %}\n      while true\n        current = current.pred\n        yield current\n      end\n    {% else %}\n      while begin_value.nil? || begin_value < current\n        current = current.pred\n        yield current\n      end\n    {% end %}\n  end\n\n  # Returns a reverse `Iterator` over the elements of this range.\n  #\n  # ```\n  # (1..3).reverse_each.skip(1).to_a # => [2, 1]\n  # ```\n  def reverse_each\n    if @end.nil?\n      raise ArgumentError.new(\"Can't reverse_each endless range\")\n    end\n\n    ReverseIterator.new(self)\n  end\n\n  # Iterates from `begin` to `end` incrementing by the amount of *step* on each\n  # iteration.\n  #\n  # ```\n  # ary = [] of Int32\n  # (1..4).step(by: 2) do |x|\n  #   ary << x\n  # end\n  # ary                      # => [1, 3]\n  # (1..4).step(by: 2).to_a  # => [1, 3]\n  # (1..4).step(by: 1).to_a  # => [1, 2, 3, 4]\n  # (1...4).step(by: 1).to_a # => [1, 2, 3]\n  # ```\n  #\n  # If `B` is a `Steppable`, implementation is delegated to `Steppable#step`.\n  # Otherwise `#succ` method is expected to be defined on `begin` and its\n  # successors and iteration is based on calling `#succ` sequentially\n  # (*step* times per iteration).\n  #\n  # Raises `ArgumentError` if `begin` is `nil`.\n  def step(by = 1, &) : Nil\n    current = @begin\n    if current.nil?\n      raise ArgumentError.new(\"Can't step beginless range\")\n    end\n\n    {% if B < Steppable %}\n      current.step(to: @end, by: by, exclusive: @exclusive) do |x|\n        yield x\n      end\n    {% else %}\n      end_value = @end\n      while end_value.nil? || current < end_value\n        yield current\n        by.times do\n          current = current.succ\n          return if end_value && current > end_value\n        rescue exc : OverflowError\n          if current == end_value\n            return\n          else\n            raise exc\n          end\n        end\n      end\n      yield current if !@exclusive && current == @end\n    {% end %}\n  end\n\n  # :ditto:\n  def step(by = 1) : Iterator\n    start = @begin\n    if start.nil?\n      raise ArgumentError.new(\"Can't step beginless range\")\n    end\n\n    {% if B < Steppable %}\n      start.step(to: @end, by: by, exclusive: @exclusive)\n    {% else %}\n      StepIterator(self, B, typeof(by)).new(self, by)\n    {% end %}\n  end\n\n  # Returns `true` if this range excludes the *end* element.\n  #\n  # ```\n  # (1..10).excludes_end?  # => false\n  # (1...10).excludes_end? # => true\n  # ```\n  def excludes_end? : Bool\n    @exclusive\n  end\n\n  # Returns `true` if this range includes the given *value*.\n  #\n  # ```\n  # (1..10).includes?(4)  # => true\n  # (1..10).includes?(10) # => true\n  # (1..10).includes?(11) # => false\n  #\n  # (1...10).includes?(9)  # => true\n  # (1...10).includes?(10) # => false\n  # ```\n  def includes?(value) : Bool\n    begin_value = @begin\n    end_value = @end\n\n    # begin passes\n    (begin_value.nil? || value >= begin_value) &&\n      # end passes\n      (end_value.nil? ||\n        (@exclusive ? value < end_value : value <= end_value))\n  end\n\n  # Same as `includes?`.\n  def covers?(value)\n    includes?(value)\n  end\n\n  # Same as `includes?`, useful for the `case` expression.\n  #\n  # ```\n  # case 79\n  # when 1..50   then puts \"low\"\n  # when 51..75  then puts \"medium\"\n  # when 76..100 then puts \"high\"\n  # end\n  # ```\n  #\n  # Produces:\n  #\n  # ```text\n  # high\n  # ```\n  #\n  # See also: `Object#===`.\n  def ===(value)\n    includes?(value)\n  end\n\n  def to_s(io : IO) : Nil\n    @begin.try &.inspect(io)\n    io << (@exclusive ? \"...\" : \"..\")\n    @end.try &.inspect(io)\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  # Optimized version of `Enumerable#sum` that runs in O(1) time when `self` is\n  # an `Int` range.\n  def sum(initial)\n    b = self.begin\n    e = self.end\n\n    if b.is_a?(Int) && e.is_a?(Int)\n      e -= 1 if @exclusive\n      n = e - b + 1\n      if n >= 0\n        initial + n * (b + e) // 2\n      else\n        initial\n      end\n    else\n      super\n    end\n  end\n\n  # Optimized version of `Enumerable#sample` that runs in O(1) time when `self`\n  # is an `Int` or `Float` range. In these cases, this range is considered to be\n  # a distribution of numeric values rather than a collection of elements, and\n  # the method simply calls `random.rand(self)`.\n  #\n  # Raises `ArgumentError` if `self` is an open range.\n  def sample(random : Random? = nil)\n    rng = random || Random.thread_default\n\n    {% if B < Int && E < Int %}\n      rng.rand(self)\n    {% elsif B < Float && E < Float %}\n      rng.rand(self)\n    {% elsif B.nilable? || E.nilable? %}\n      b = self.begin\n      e = self.end\n\n      if b.nil? || e.nil?\n        raise ArgumentError.new(\"Can't sample an open range\")\n      end\n\n      Range.new(b, e, @exclusive).sample(rng)\n    {% else %}\n      super\n    {% end %}\n  end\n\n  # :inherit:\n  #\n  # If `self` is not empty and `n` is equal to 1, calls `sample(random)` exactly\n  # once. Thus, *random* will be left in a different state compared to the\n  # implementation in `Enumerable`.\n  def sample(n : Int, random : Random? = nil)\n    rng = random || Random.thread_default\n\n    if self.begin.nil? || self.end.nil?\n      raise ArgumentError.new(\"Can't sample an open range\")\n    end\n\n    if n < 0\n      raise ArgumentError.new \"Can't sample negative number of elements\"\n    end\n\n    # For a range of integers we can do much better\n    {% if B < Int && E < Int %}\n      min = self.begin\n      max = self.end\n\n      if exclusive? ? max <= min : max < min\n        raise ArgumentError.new \"Invalid range for rand: #{self}\"\n      end\n\n      max -= 1 if exclusive?\n\n      available = max - min + 1\n\n      # When a big chunk of elements is going to be needed, it's\n      # faster to just traverse the entire range than hitting\n      # a lot of duplicates because or random.\n      if n >= available // 4\n        return super\n      end\n\n      possible = Math.min(n, available)\n\n      # If we must return all values in the range...\n      if possible == available\n        result = Array(B).new(possible) { |i| min + i }\n        result.shuffle!(rng)\n        return result\n      end\n\n      range_sample(n, rng)\n    {% elsif B < Float && E < Float %}\n      min = self.begin\n      max = self.end\n\n      if exclusive? ? max <= min : max < min\n        raise ArgumentError.new \"Invalid range for rand: #{self}\"\n      end\n\n      if min == max\n        return [min]\n      end\n\n      range_sample(n, rng)\n    {% else %}\n      case n\n      when 0\n        [] of B\n      when 1\n        [sample(rng)]\n      else\n        super\n      end\n    {% end %}\n  end\n\n  private def range_sample(n, random)\n    if n <= 16\n      # For a small requested amount doing a linear lookup is faster\n      result = Array(B).new(n)\n      until result.size == n\n        value = sample(random)\n        result << value unless result.includes?(value)\n      end\n      result\n    else\n      # Otherwise using a Set is faster\n      result = Set(B).new(n)\n      until result.size == n\n        result << sample(random)\n      end\n      result.to_a\n    end\n  end\n\n  # Returns a new `Range` with `begin` and `end` cloned.\n  def clone\n    Range.new(@begin.clone, @end.clone, @exclusive)\n  end\n\n  def map(&block : B -> U) forall U\n    b = self.begin\n    e = self.end\n\n    # Optimized implementation for int range\n    if b.is_a?(Int) && e.is_a?(Int)\n      e -= 1 if @exclusive\n      n = e - b + 1\n      n = 0 if n < 0\n      Array(U).new(n) { |i| yield b + i }\n    else\n      super { |i| yield i }\n    end\n  end\n\n  # Returns the number of values in this range.\n  #\n  # If both the beginning and the end of this range are `Int`s, runs in constant\n  # time instead of linear.\n  #\n  # ```\n  # (3..8).size  # => 6\n  # (3...8).size # => 5\n  # ```\n  #\n  # Raises `OverflowError` if the difference is bigger than `Int32`.\n  # Raises `ArgumentError` if either `begin` or `end` are `nil`.\n  def size : Int32\n    b = self.begin\n    e = self.end\n\n    # Optimized implementation for int range\n    if b.is_a?(Int) && e.is_a?(Int)\n      return 0 if e < b\n\n      # Convert `e` to `Int32` in order to ensure that `e &- b` doesn't get\n      # truncated due to the smaller type of `e`.\n      if e.is_a?(UInt8 | Int8 | UInt16 | Int16)\n        e = e.to_i32!\n      end\n\n      diff = (e &- b).to_i32.abs\n      diff &+= 1 unless @exclusive\n      diff\n    else\n      if b.nil? || e.nil?\n        raise ArgumentError.new(\"Can't calculate size of an open range\")\n      end\n      super\n    end\n  end\n\n  private class ItemIterator(B, E)\n    include Iterator(B)\n\n    @range : Range(B, E)\n    @current : B\n    @reached_end : Bool\n\n    def initialize(@range : Range(B, E), @current = range.begin, @reached_end = false)\n    end\n\n    def next\n      return stop if @reached_end\n\n      end_value = @range.end\n\n      if end_value.nil? || @current < end_value\n        value = @current\n        @current = @current.succ\n        value\n      else\n        @reached_end = true\n\n        if !@range.excludes_end? && @current == end_value\n          @current\n        else\n          stop\n        end\n      end\n    end\n  end\n\n  private class ReverseIterator(B, E)\n    include Iterator(E)\n\n    @range : Range(B, E)\n    @current : E\n\n    def initialize(@range : Range(B, E))\n      if range.excludes_end?\n        @current = range.end.not_nil!\n      else\n        @current = range.end.not_nil!.succ\n      end\n    end\n\n    def next\n      begin_value = @range.begin\n\n      return stop if !begin_value.nil? && @current <= begin_value\n      @current = @current.pred\n    end\n  end\n\n  private class StepIterator(R, B, N)\n    include Iterator(B)\n\n    @range : R\n    @step : N\n    @current : B\n    @reached_end : Bool\n    @at_start = true\n\n    def initialize(@range, @step, @current = range.begin, @reached_end = false)\n    end\n\n    def next\n      return stop if @reached_end\n\n      end_value = @range.end\n\n      if @at_start\n        @at_start = false\n\n        if end_value\n          if @current > end_value || (@current == end_value && @range.exclusive?)\n            @reached_end = true\n            return stop\n          end\n        end\n\n        return @current\n      end\n\n      if end_value.nil? || @current < end_value\n        @step.times do\n          if end_value && @current >= end_value\n            @reached_end = true\n            return stop\n          end\n\n          @current = @current.succ\n        end\n\n        if @current == end_value && @range.exclusive?\n          @reached_end = true\n          stop\n        else\n          @current\n        end\n      else\n        @reached_end = true\n        stop\n      end\n    end\n\n    def sum(initial)\n      return super if @reached_end\n\n      b = @current\n      e = @range.end\n      d = @step\n\n      if b.is_a?(Int) && e.is_a?(Int) && d.is_a?(Int)\n        e -= 1 if @range.excludes_end?\n        n = (e - b) // d + 1\n        if n >= 0\n          e = b + (n - 1) * d\n          initial + n * (b + e) // 2\n        else\n          initial\n        end\n      else\n        super\n      end\n    end\n  end\nend\n\nrequire \"./range/*\"\n"
  },
  {
    "path": "src/reference.cr",
    "content": "# `Reference` is the base class of classes you define in your program.\n# It is set as a class' superclass when you don't specify one:\n#\n# ```\n# class MyClass # < Reference\n# end\n# ```\n#\n# A reference type is passed by reference: when you pass it to methods,\n# return it from methods or assign it to variables, a pointer is actually passed.\n#\n# Invoking `new` on a `Reference` allocates a new instance on the heap.\n# The instance's memory is automatically freed (garbage-collected) when\n# the instance is no longer referred by any other entity in the program.\nclass Reference\n  # Constructs an object in-place at the given *address*, forwarding *args* and\n  # *opts* to `#initialize`. Returns that object.\n  #\n  # This method can be used to decouple object allocation from initialization.\n  # For example, the instance data might come from a custom allocator, or it\n  # might reside on the stack using a type like `ReferenceStorage`.\n  #\n  # *address* must point to a suitably aligned buffer of at least\n  # `instance_sizeof(self)` bytes.\n  #\n  # WARNING: This method is unsafe, as it assumes the caller is responsible for\n  # managing the memory at the given *address* manually.\n  #\n  # ```\n  # class Foo\n  #   getter i : Int64\n  #   getter str = \"abc\"\n  #\n  #   def initialize(@i)\n  #   end\n  #\n  #   def finalize\n  #     puts \"bye\"\n  #   end\n  # end\n  #\n  # foo_buffer = uninitialized ReferenceStorage(Foo)\n  # foo = Foo.unsafe_construct(pointerof(foo_buffer), 123_i64)\n  # begin\n  #   foo # => #<Foo:0x... @i=123, @str=\"abc\">\n  # ensure\n  #   foo.finalize if foo.responds_to?(:finalize) # prints \"bye\"\n  # end\n  # ```\n  #\n  # See also: `Reference.pre_initialize`.\n  @[Experimental(\"This API is still under development. Join the discussion about custom reference allocation at [#13481](https://github.com/crystal-lang/crystal/issues/13481).\")]\n  def self.unsafe_construct(address : Pointer, *args, **opts) : self\n    obj = pre_initialize(address)\n    obj.initialize(*args, **opts)\n    obj\n  end\n\n  # Returns `true` if this reference is the same as *other*. Invokes `same?`.\n  def ==(other : self)\n    same?(other)\n  end\n\n  # Returns `false` (other can only be a `Value` here).\n  def ==(other)\n    false\n  end\n\n  # Returns `true` if this reference is the same as *other*. This is only\n  # `true` if this reference's `object_id` is the same as *other*'s.\n  def same?(other : Reference) : Bool\n    object_id == other.object_id\n  end\n\n  # Returns `false`: a reference is never `nil`.\n  def same?(other : Nil)\n    false\n  end\n\n  # Returns a shallow copy of this object.\n  #\n  # This allocates a new object and copies the contents of\n  # `self` into it.\n  def dup\n    {% if @type.abstract? %}\n      # This shouldn't happen, as the type is abstract,\n      # but we need to avoid the allocate invocation below\n      raise \"Can't dup {{@type}}\"\n    {% else %}\n      dup = self.class.allocate\n      dup.as(Void*).copy_from(self.as(Void*), instance_sizeof(self))\n      GC.add_finalizer(dup) if dup.responds_to?(:finalize)\n      dup\n    {% end %}\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.reference(self)\n  end\n\n  # Appends a String representation of this object\n  # which includes its class name, its object address\n  # and the values of all instance variables.\n  #\n  # ```\n  # class Person\n  #   def initialize(@name : String, @age : Int32)\n  #   end\n  # end\n  #\n  # Person.new(\"John\", 32).inspect # => #<Person:0x10fd31f20 @name=\"John\", @age=32>\n  # ```\n  def inspect(io : IO) : Nil\n    io << \"#<\" << {{@type.name.id.stringify}} << \":0x\"\n    object_id.to_s(io, 16)\n\n    executed = exec_recursive(:inspect) do\n      {% for ivar, i in @type.instance_vars %}\n        {% if i > 0 %}\n          io << ','\n        {% end %}\n        io << \" @{{ivar.id}}=\"\n        @{{ivar.id}}.inspect io\n      {% end %}\n    end\n    unless executed\n      io << \" ...\"\n    end\n    io << '>'\n  end\n\n  def pretty_print(pp) : Nil\n    {% if @type.overrides?(Reference, \"inspect\") %}\n      pp.text inspect\n    {% else %}\n      prefix = \"#<#{{{@type.name.id.stringify}}}:0x#{object_id.to_s(16)}\"\n      executed = exec_recursive(:pretty_print) do\n        pp.surround(prefix, \">\", left_break: nil, right_break: nil) do\n          {% for ivar, i in @type.instance_vars.map(&.name).sort %}\n            {% if i == 0 %}\n              pp.breakable\n            {% else %}\n              pp.comma\n            {% end %}\n            pp.group do\n              pp.text \"@{{ivar.id}}=\"\n              pp.nest do\n                pp.breakable \"\"\n                @{{ivar.id}}.pretty_print(pp)\n              end\n            end\n          {% end %}\n        end\n      end\n      unless executed\n        pp.text \"#{prefix} ...>\"\n      end\n    {% end %}\n  end\n\n  # Appends a short String representation of this object\n  # which includes its class name and its object address.\n  #\n  # ```\n  # class Person\n  #   def initialize(@name : String, @age : Int32)\n  #   end\n  # end\n  #\n  # Person.new(\"John\", 32).to_s # => #<Person:0x10a199f20>\n  # ```\n  def to_s(io : IO) : Nil\n    io << \"#<\" << self.class.name << \":0x\"\n    object_id.to_s(io, 16)\n    io << '>'\n  end\n\n  private def exec_recursive(method, &)\n    # NOTE: can't use `Set` because of prelude require order\n    hash = Fiber.current.exec_recursive_hash\n    key = {object_id, method}\n    hash.put(key, nil) do\n      yield\n      return true\n    ensure\n      hash.delete(key)\n    end\n    false\n  end\n\n  # Helper method to perform clone by also checking recursiveness.\n  # When clone is wanted, call this method. Then create the clone\n  # instance without any contents (don't fill it out yet), then\n  # put the clone's object id into the hash yielded into the block.\n  # At the end of the block return the cloned object.\n  #\n  # For example:\n  #\n  # ```\n  # def clone\n  #   exec_recursive_clone do |hash|\n  #     clone = SomeClass.new\n  #     hash[object_id] = clone.object_id\n  #     # fill out the clone object\n  #     clone\n  #   end\n  # end\n  # ```\n  private def exec_recursive_clone(&)\n    # NOTE: can't use `Set` because of prelude require order\n    hash = Fiber.current.exec_recursive_clone_hash\n    clone_object_id = hash.fetch(object_id) do\n      yield(hash).object_id\n    ensure\n      hash.delete(object_id)\n    end\n    Pointer(Void).new(clone_object_id).as(self)\n  end\nend\n"
  },
  {
    "path": "src/reference_storage.cr",
    "content": "# `ReferenceStorage(T)` provides the minimum storage for the instance data of\n# an object of type `T`. The compiler guarantees that\n# `sizeof(ReferenceStorage(T)) == instance_sizeof(T)` and\n# `alignof(ReferenceStorage(T)) == instance_alignof(T)` always hold, which means\n# `Pointer(ReferenceStorage(T))` and `T` are binary-compatible.\n#\n# `T` must be a non-union reference type.\n#\n# WARNING: `ReferenceStorage` is only necessary for manual memory management,\n# such as creating instances of `T` with a non-default allocator. Therefore,\n# this type is unsafe and no public constructors are defined.\n#\n# WARNING: `ReferenceStorage` is unsuitable when instances of `T` require more\n# than `instance_sizeof(T)` bytes, such as `String` and `Log::Metadata`.\n@[Experimental(\"This type's API is still under development. Join the discussion about custom reference allocation at [#13481](https://github.com/crystal-lang/crystal/issues/13481).\")]\n@[Primitive(:ReferenceStorageType)]\nstruct ReferenceStorage(T) < Value\n  private def initialize\n  end\n\n  # Returns whether `self` and *other* are bytewise equal.\n  #\n  # NOTE: This does not call `T#==`, so it works even if `self` or *other* does\n  # not represent a valid instance of `T`. If validity is guaranteed, call\n  # `to_reference == other.to_reference` instead to use `T#==`.\n  def ==(other : ReferenceStorage(T)) : Bool\n    to_bytes == other.to_bytes\n  end\n\n  def ==(other) : Bool\n    false\n  end\n\n  def hash(hasher)\n    to_bytes.hash(hasher)\n  end\n\n  def to_s(io : IO) : Nil\n    io << \"ReferenceStorage(#<\" << T << \":0x\"\n    pointerof(@type_id).address.to_s(io, 16)\n    io << \">)\"\n  end\n\n  # Returns a `T` whose instance data refers to `self`.\n  #\n  # WARNING: The caller is responsible for ensuring that the instance data is\n  # correctly initialized and outlives the returned `T`.\n  def to_reference : T\n    pointerof(@type_id).as(T)\n  end\n\n  protected def to_bytes\n    Slice.new(pointerof(@type_id).as(UInt8*), instance_sizeof(T))\n  end\nend\n"
  },
  {
    "path": "src/regex/engine.cr",
    "content": "# The following condition ensures that the engine selection respects `-Duse_pcre2`/`-Duse_pcre`,\n# and if none is given it tries to check for availability of `libpcre2` with `pkg-config`.\n# If `pkg-config` is unavailable, the default is PCRE2. If `pkg-config` is available but\n# has no information about a `libpcre2` package, it falls back to PCRE.\n{% if flag?(:use_pcre2) || (!flag?(:use_pcre) && (flag?(:win32) || `hash pkg-config 2> /dev/null && (pkg-config --silence-errors --modversion libpcre2-8 || printf %s false) || true` != \"false\")) %}\n  require \"./pcre2\"\n\n  # :nodoc:\n  alias Regex::Engine = PCRE2\n{% else %}\n  require \"./pcre\"\n\n  # :nodoc:\n  alias Regex::Engine = PCRE\n{% end %}\n"
  },
  {
    "path": "src/regex/lib_pcre.cr",
    "content": "# Supported library versions:\n#\n# * libpcre\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#regular-expression-engine\n@[Link(\"pcre\", pkg_config: \"libpcre\")]\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  @[Link(dll: \"pcre.dll\")]\n{% end %}\nlib LibPCRE\n  alias Int = LibC::Int\n\n  # Public options. Some are compile-time only, some are run-time only, and some\n  # are both. Most of the compile-time options are saved with the compiled regex so\n  # that they can be inspected during studying (and therefore JIT compiling). Note\n  # that pcre_study() has its own set of options. Originally, all the options\n  # defined here used distinct bits. However, almost all the bits in a 32-bit word\n  # are now used, so in order to conserve them, option bits that were previously\n  # only recognized at matching time (i.e. by pcre_exec() or pcre_dfa_exec()) may\n  # also be used for compile-time options that affect only compiling and are not\n  # relevant for studying or JIT compiling.\n\n  # Some options for pcre_compile() change its behaviour but do not affect the\n  # behaviour of the execution functions. Other options are passed through to the\n  # execution functions and affect their behaviour, with or without affecting the\n  # behaviour of pcre_compile().\n\n  # Options that can be passed to pcre_compile() are tagged Cx below, with these\n  # variants:\n\n  # C1   Affects compile only\n  # C2   Does not affect compile; affects exec, dfa_exec\n  # C3   Affects compile, exec, dfa_exec\n  # C4   Affects compile, exec, dfa_exec, study\n  # C5   Affects compile, exec, study\n\n  # Options that can be set for pcre_exec() and/or pcre_dfa_exec() are flagged with\n  # E and D, respectively. They take precedence over C3, C4, and C5 settings passed\n  # from pcre_compile(). Those that are compatible with JIT execution are flagged\n  # with J.\n\n  CASELESS       = 0x00000001\n  MULTILINE      = 0x00000002\n  DOTALL         = 0x00000004\n  EXTENDED       = 0x00000008\n  ANCHORED       = 0x00000010\n  DOLLAR_ENDONLY = 0x00000020\n\n  EXTRA           = 0x00000040 # C1\n  NOTBOL          = 0x00000080 #    E D J\n  NOTEOL          = 0x00000100 #    E D J\n  UNGREEDY        = 0x00000200 # C1\n  NOTEMPTY        = 0x00000400 #    E D J\n  UTF8            = 0x00000800 # C4        )\n  UTF16           = 0x00000800 # C4        ) Synonyms\n  UTF32           = 0x00000800 # C4        )\n  NO_AUTO_CAPTURE = 0x00001000 # C1\n  NO_UTF8_CHECK   = 0x00002000 # C1 E D J  )\n  NO_UTF16_CHECK  = 0x00002000 # C1 E D J  ) Synonyms\n  NO_UTF32_CHECK  = 0x00002000 # C1 E D J  )\n  AUTO_CALLOUT    = 0x00004000 # C1\n  PARTIAL_SOFT    = 0x00008000 #    E D J  ) Synonyms\n  PARTIAL         = 0x00008000 #    E D J  )\n\n  # This pair use the same bit.\n  NEVER_UTF    = 0x00010000 # C1        ) Overlaid\n  DFA_SHORTEST = 0x00010000 #      D    ) Overlaid\n  NOTBOS       = 0x00010000 #      D    ) Overlaid\n\n  # This pair use the same bit.\n  NO_AUTO_POSSESS = 0x00020000 # C1        ) Overlaid\n  DFA_RESTART     = 0x00020000 #      D    ) Overlaid\n  NOTEOS          = 0x00020000 #      D    ) Overlaid\n\n  FIRSTLINE         = 0x00040000 # C3\n  DUPNAMES          = 0x00080000 # C1\n  NEWLINE_CR        = 0x00100000 # C3 E D\n  NEWLINE_LF        = 0x00200000 # C3 E D\n  NEWLINE_CRLF      = 0x00300000 # C3 E D\n  NEWLINE_ANY       = 0x00400000 # C3 E D\n  NEWLINE_ANYCRLF   = 0x00500000 # C3 E D\n  BSR_ANYCRLF       = 0x00800000 # C3 E D\n  BSR_UNICODE       = 0x01000000 # C3 E D\n  JAVASCRIPT_COMPAT = 0x02000000 # C5\n  NO_START_OPTIMIZE = 0x04000000 # C2 E D    ) Synonyms\n  NO_START_OPTIMISE = 0x04000000 # C2 E D    )\n  PARTIAL_HARD      = 0x08000000 #    E D J\n  NOTEMPTY_ATSTART  = 0x10000000 #    E D J\n  UCP               = 0x20000000 # C3\n  NOTGPOS           = 0x40000000 # C3\n\n  type Pcre = Void*\n  type PcreExtra = Void*\n  fun compile = pcre_compile(pattern : UInt8*, options : Int, errptr : UInt8**, erroffset : Int*, tableptr : Void*) : Pcre\n  fun config = pcre_config(what : Int, where : Void*) : Int\n  fun exec = pcre_exec(code : Pcre, extra : PcreExtra, subject : UInt8*, length : Int, offset : Int, options : Int, ovector : Int*, ovecsize : Int) : Int\n  fun study = pcre_study(code : Pcre, options : Int, errptr : UInt8**) : PcreExtra\n  fun free_study = pcre_free_study(extra : PcreExtra) : Void\n  fun full_info = pcre_fullinfo(code : Pcre, extra : PcreExtra, what : Int, where : Int*) : Int\n  fun get_stringnumber = pcre_get_stringnumber(code : Pcre, string_name : UInt8*) : Int\n  fun get_stringtable_entries = pcre_get_stringtable_entries(code : Pcre, name : UInt8*, first : UInt8**, last : UInt8**) : Int\n  fun version = pcre_version : LibC::Char*\n\n  CONFIG_JIT = 9\n\n  STUDY_JIT_COMPILE = 0x0001\n\n  INFO_CAPTURECOUNT  = 2\n  INFO_NAMEENTRYSIZE = 7\n  INFO_NAMECOUNT     = 8\n  INFO_NAMETABLE     = 9\n\n  $free = pcre_free : Void* ->\n\n  # Exec-time and get/set-time error codes\n  enum Error\n    NOMATCH         =  -1\n    NULL            =  -2\n    BADOPTION       =  -3\n    BADMAGIC        =  -4\n    UNKNOWN_OPCODE  =  -5\n    UNKNOWN_NODE    =  -5 # For backward compatibility\n    NOMEMORY        =  -6\n    NOSUBSTRING     =  -7\n    MATCHLIMIT      =  -8\n    CALLOUT         =  -9 # Never used by PCRE itself\n    BADUTF8         = -10 # Same for 8/16/32\n    BADUTF16        = -10 # Same for 8/16/32\n    BADUTF32        = -10 # Same for 8/16/32\n    BADUTF8_OFFSET  = -11 # Same for 8/16\n    BADUTF16_OFFSET = -11 # Same for 8/16\n    PARTIAL         = -12\n    BADPARTIAL      = -13\n    INTERNAL        = -14\n    BADCOUNT        = -15\n    DFA_UITEM       = -16\n    DFA_UCOND       = -17\n    DFA_UMLIMIT     = -18\n    DFA_WSSIZE      = -19\n    DFA_RECURSE     = -20\n    RECURSIONLIMIT  = -21\n    NULLWSLIMIT     = -22 # No longer actually used\n    BADNEWLINE      = -23\n    BADOFFSET       = -24\n    SHORTUTF8       = -25\n    SHORTUTF16      = -25 # Same for 8/16\n    RECURSELOOP     = -26\n    JIT_STACKLIMIT  = -27\n    BADMODE         = -28\n    BADENDIANNESS   = -29\n    DFA_BADRESTART  = -30\n    JIT_BADOPTION   = -31\n    BADLENGTH       = -32\n    UNSET           = -33\n  end\nend\n"
  },
  {
    "path": "src/regex/lib_pcre2.cr",
    "content": "# Supported library versions:\n#\n# * libpcre2 (recommended: 10.36+)\n#\n# See https://crystal-lang.org/reference/man/required_libraries.html#regular-expression-engine\n@[Link(\"pcre2-8\", pkg_config: \"libpcre2-8\")]\n{% if compare_versions(Crystal::VERSION, \"1.11.0-dev\") >= 0 %}\n  @[Link(dll: \"pcre2-8.dll\")]\n{% end %}\nlib LibPCRE2\n  alias Int = LibC::Int\n\n  UNSET = ~LibC::SizeT.new(0)\n\n  # The following option bits can be passed to pcre2_compile(), pcre2_match(),\n  # or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it\n  # is passed. Put these bits at the most significant end of the options word so\n  # others can be added next to them\n\n  ANCHORED     = 0x80000000_u32\n  NO_UTF_CHECK = 0x40000000_u32\n  ENDANCHORED  = 0x20000000_u32\n\n  # The following option bits can be passed only to pcre2_compile(). However,\n  # they may affect compilation, JIT compilation, and/or interpretive execution.\n  # The following tags indicate which:\n\n  # C   alters what is compiled by pcre2_compile()\n  # J   alters what is compiled by pcre2_jit_compile()\n  # M   is inspected during pcre2_match() execution\n  # D   is inspected during pcre2_dfa_match() execution\n\n  ALLOW_EMPTY_CLASS   = 0x00000001_u32 # C\n  ALT_BSUX            = 0x00000002_u32 # C\n  AUTO_CALLOUT        = 0x00000004_u32 # C\n  CASELESS            = 0x00000008_u32 # C\n  DOLLAR_ENDONLY      = 0x00000010_u32 #   J M D\n  DOTALL              = 0x00000020_u32 # C\n  DUPNAMES            = 0x00000040_u32 # C\n  EXTENDED            = 0x00000080_u32 # C\n  FIRSTLINE           = 0x00000100_u32 #   J M D\n  MATCH_UNSET_BACKREF = 0x00000200_u32 # C J M\n  MULTILINE           = 0x00000400_u32 # C\n  NEVER_UCP           = 0x00000800_u32 # C\n  NEVER_UTF           = 0x00001000_u32 # C\n  NO_AUTO_CAPTURE     = 0x00002000_u32 # C\n  NO_AUTO_POSSESS     = 0x00004000_u32 # C\n  NO_DOTSTAR_ANCHOR   = 0x00008000_u32 # C\n  NO_START_OPTIMIZE   = 0x00010000_u32 #   J M D\n  UCP                 = 0x00020000_u32 # C J M D\n  UNGREEDY            = 0x00040000_u32 # C\n  UTF                 = 0x00080000_u32 # C J M D\n  NEVER_BACKSLASH_C   = 0x00100000_u32 # C\n  ALT_CIRCUMFLEX      = 0x00200000_u32 #   J M D\n  ALT_VERBNAMES       = 0x00400000_u32 # C\n  USE_OFFSET_LIMIT    = 0x00800000_u32 #   J M D\n  EXTENDED_MORE       = 0x01000000_u32 # C\n  LITERAL             = 0x02000000_u32 # C\n  MATCH_INVALID_UTF   = 0x04000000_u32 #   J M D\n\n  # An additional compile options word is available in the compile context.\n\n  EXTRA_ALLOW_SURROGATE_ESCAPES = 0x00000001_u32 # C\n  EXTRA_BAD_ESCAPE_IS_LITERAL   = 0x00000002_u32 # C\n  EXTRA_MATCH_WORD              = 0x00000004_u32 # C\n  EXTRA_MATCH_LINE              = 0x00000008_u32 # C\n  EXTRA_ESCAPED_CR_IS_LF        = 0x00000010_u32 # C\n  EXTRA_ALT_BSUX                = 0x00000020_u32 # C\n  EXTRA_ALLOW_LOOKAROUND_BSK    = 0x00000040_u32 # C\n  EXTRA_CASELESS_RESTRICT       = 0x00000080_u32 # C\n  EXTRA_ASCII_BSD               = 0x00000100_u32 # C\n  EXTRA_ASCII_BSS               = 0x00000200_u32 # C\n  EXTRA_ASCII_BSW               = 0x00000400_u32 # C\n  EXTRA_ASCII_POSIX             = 0x00000800_u32 # C\n\n  # These are for pcre2_jit_compile().\n\n  JIT_COMPLETE     = 0x00000001_u32 # For full matching\n  JIT_PARTIAL_SOFT = 0x00000002_u32\n  JIT_PARTIAL_HARD = 0x00000004_u32\n  JIT_INVALID_UTF  = 0x00000100_u32\n\n  # These are for pcre2_match(), pcre2_dfa_match(), pcre2_jit_match(), and\n  # pcre2_substitute(). Some are allowed only for one of the functions, and in\n  # these cases it is noted below. Note that PCRE2_ANCHORED, PCRE2_ENDANCHORED and\n  # PCRE2_NO_UTF_CHECK can also be passed to these functions (though\n  # pcre2_jit_match() ignores the latter since it bypasses all sanity checks).\n\n  NOTBOL                      = 0x00000001_u32\n  NOTEOL                      = 0x00000002_u32\n  NOTEMPTY                    = 0x00000004_u32 # ) These two must be kept\n  NOTEMPTY_ATSTART            = 0x00000008_u32 # ) adjacent to each other.\n  PARTIAL_SOFT                = 0x00000010_u32\n  PARTIAL_HARD                = 0x00000020_u32\n  DFA_RESTART                 = 0x00000040_u32 # pcre2_dfa_match() only\n  DFA_SHORTEST                = 0x00000080_u32 # pcre2_dfa_match() only\n  SUBSTITUTE_GLOBAL           = 0x00000100_u32 # pcre2_substitute() only\n  SUBSTITUTE_EXTENDED         = 0x00000200_u32 # pcre2_substitute() only\n  SUBSTITUTE_UNSET_EMPTY      = 0x00000400_u32 # pcre2_substitute() only\n  SUBSTITUTE_UNKNOWN_UNSET    = 0x00000800_u32 # pcre2_substitute() only\n  SUBSTITUTE_OVERFLOW_LENGTH  = 0x00001000_u32 # pcre2_substitute() only\n  NO_JIT                      = 0x00002000_u32 # Not for pcre2_dfa_match()\n  COPY_MATCHED_SUBJECT        = 0x00004000_u32\n  SUBSTITUTE_LITERAL          = 0x00008000_u32 # pcre2_substitute() only\n  SUBSTITUTE_MATCHED          = 0x00010000_u32 # pcre2_substitute() only\n  SUBSTITUTE_REPLACEMENT_ONLY = 0x00020000_u32 # pcre2_substitute() only\n\n  enum Error\n    #  \"Expected\" matching error codes: no match and partial match.\n\n    NOMATCH = -1\n    PARTIAL = -2\n\n    #  Error codes for UTF-8 validity checks\n\n    UTF8_ERR1  =  -3\n    UTF8_ERR2  =  -4\n    UTF8_ERR3  =  -5\n    UTF8_ERR4  =  -6\n    UTF8_ERR5  =  -7\n    UTF8_ERR6  =  -8\n    UTF8_ERR7  =  -9\n    UTF8_ERR8  = -10\n    UTF8_ERR9  = -11\n    UTF8_ERR10 = -12\n    UTF8_ERR11 = -13\n    UTF8_ERR12 = -14\n    UTF8_ERR13 = -15\n    UTF8_ERR14 = -16\n    UTF8_ERR15 = -17\n    UTF8_ERR16 = -18\n    UTF8_ERR17 = -19\n    UTF8_ERR18 = -20\n    UTF8_ERR19 = -21\n    UTF8_ERR20 = -22\n    UTF8_ERR21 = -23\n\n    #  Error codes for UTF-16 validity checks\n\n    UTF16_ERR1 = -24\n    UTF16_ERR2 = -25\n    UTF16_ERR3 = -26\n\n    #  Error codes for UTF-32 validity checks\n\n    UTF32_ERR1 = -27\n    UTF32_ERR2 = -28\n\n    # Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction\n    # functions, context functions, and serializing functions. They are in numerical\n    # order. Originally they were in alphabetical order too, but now that PCRE2 is\n    # released, the numbers must not be changed.\n\n    BADDATA           = -29\n    MIXEDTABLES       = -30 # Name was changed\n    BADMAGIC          = -31\n    BADMODE           = -32\n    BADOFFSET         = -33\n    BADOPTION         = -34\n    BADREPLACEMENT    = -35\n    BADUTFOFFSET      = -36\n    CALLOUT           = -37 # Never used by PCRE2 itself\n    DFA_BADRESTART    = -38\n    DFA_RECURSE       = -39\n    DFA_UCOND         = -40\n    DFA_UFUNC         = -41\n    DFA_UITEM         = -42\n    DFA_WSSIZE        = -43\n    INTERNAL          = -44\n    JIT_BADOPTION     = -45\n    JIT_STACKLIMIT    = -46\n    MATCHLIMIT        = -47\n    NOMEMORY          = -48\n    NOSUBSTRING       = -49\n    NOUNIQUESUBSTRING = -50\n    NULL              = -51\n    RECURSELOOP       = -52\n    DEPTHLIMIT        = -53\n    RECURSIONLIMIT    = -53 # Obsolete synonym\n    UNAVAILABLE       = -54\n    UNSET             = -55\n    BADOFFSETLIMIT    = -56\n    BADREPESCAPE      = -57\n    REPMISSINGBRACE   = -58\n    BADSUBSTITUTION   = -59\n    BADSUBSPATTERN    = -60\n    TOOMANYREPLACE    = -61\n    BADSERIALIZEDDATA = -62\n    HEAPLIMIT         = -63\n    CONVERT_SYNTAX    = -64\n    INTERNAL_DUPMATCH = -65\n    DFA_UINVALID_UTF  = -66\n\n    def utf8_validity?\n      in?(UTF8_ERR21..UTF8_ERR1)\n    end\n  end\n\n  INFO_ALLOPTIONS     =  0\n  INFO_ARGOPTIONS     =  1\n  INFO_BACKREFMAX     =  2\n  INFO_BSR            =  3\n  INFO_CAPTURECOUNT   =  4\n  INFO_FIRSTCODEUNIT  =  5\n  INFO_FIRSTCODETYPE  =  6\n  INFO_FIRSTBITMAP    =  7\n  INFO_HASCRORLF      =  8\n  INFO_JCHANGED       =  9\n  INFO_JITSIZE        = 10\n  INFO_LASTCODEUNIT   = 11\n  INFO_LASTCODETYPE   = 12\n  INFO_MATCHEMPTY     = 13\n  INFO_MATCHLIMIT     = 14\n  INFO_MAXLOOKBEHIND  = 15\n  INFO_MINLENGTH      = 16\n  INFO_NAMECOUNT      = 17\n  INFO_NAMEENTRYSIZE  = 18\n  INFO_NAMETABLE      = 19\n  INFO_NEWLINE        = 20\n  INFO_DEPTHLIMIT     = 21\n  INFO_RECURSIONLIMIT = 21 # Obsolete synonym\n  INFO_SIZE           = 22\n  INFO_HASBACKSLASHC  = 23\n  INFO_FRAMESIZE      = 24\n  INFO_HEAPLIMIT      = 25\n  INFO_EXTRAOPTIONS   = 26\n\n  # Request types for pcre2_config().\n\n  CONFIG_BSR               =  0\n  CONFIG_JIT               =  1\n  CONFIG_JITTARGET         =  2\n  CONFIG_LINKSIZE          =  3\n  CONFIG_MATCHLIMIT        =  4\n  CONFIG_NEWLINE           =  5\n  CONFIG_PARENSLIMIT       =  6\n  CONFIG_DEPTHLIMIT        =  7\n  CONFIG_RECURSIONLIMIT    =  7 # Obsolete synonym\n  CONFIG_STACKRECURSE      =  8 # Obsolete\n  CONFIG_UNICODE           =  9\n  CONFIG_UNICODE_VERSION   = 10\n  CONFIG_VERSION           = 11\n  CONFIG_HEAPLIMIT         = 12\n  CONFIG_NEVER_BACKSLASH_C = 13\n  CONFIG_COMPILED_WIDTHS   = 14\n  CONFIG_TABLES_LENGTH     = 15\n\n  type Code = Void\n  type CompileContext = Void\n  type MatchData = Void\n  type GeneralContext = Void\n\n  fun get_error_message = pcre2_get_error_message_8(errorcode : Int, buffer : UInt8*, bufflen : LibC::SizeT) : Int\n\n  fun compile = pcre2_compile_8(pattern : UInt8*, length : LibC::SizeT, options : UInt32, errorcode : Int*, erroroffset : LibC::SizeT*, ccontext : CompileContext*) : Code*\n  fun code_free = pcre2_code_free_8(code : Code*) : Void\n\n  type MatchContext = Void*\n  fun match_context_create = pcre2_match_context_create_8(gcontext : Void*) : MatchContext*\n\n  fun jit_compile = pcre2_jit_compile_8(code : Code*, options : UInt32) : Int\n\n  type JITStack = Void\n\n  fun jit_stack_create = pcre2_jit_stack_create_8(startsize : LibC::SizeT, maxsize : LibC::SizeT, gcontext : GeneralContext*) : JITStack*\n  fun jit_stack_assign = pcre2_jit_stack_assign_8(mcontext : MatchContext*, callable_function : Void* -> JITStack*, callable_data : Void*) : Void\n  fun jit_stack_free = pcre2_jit_stack_free_8(jit_stack : JITStack*) : Void\n\n  fun pattern_info = pcre2_pattern_info_8(code : Code*, what : UInt32, where : Void*) : Int\n\n  fun match = pcre2_match_8(code : Code*, subject : UInt8*, length : LibC::SizeT, startoffset : LibC::SizeT, options : UInt32, match_data : MatchData*, mcontext : MatchContext*) : Int\n  fun match_data_create = pcre2_match_data_create_8(ovecsize : UInt32, gcontext : GeneralContext*) : MatchData*\n  fun match_data_create_from_pattern = pcre2_match_data_create_from_pattern_8(code : Code*, gcontext : GeneralContext*) : MatchData*\n  fun match_data_free = pcre2_match_data_free_8(match_data : MatchData*) : Void\n\n  fun substring_nametable_scan = pcre2_substring_nametable_scan_8(code : Code*, name : UInt8*, first : UInt8*, last : UInt8*) : Int\n\n  fun get_ovector_pointer = pcre2_get_ovector_pointer_8(match_data : MatchData*) : LibC::SizeT*\n  fun get_ovector_count = pcre2_get_ovector_count_8(match_data : MatchData*) : UInt32\n\n  fun config = pcre2_config_8(what : UInt32, where : Void*) : Int\nend\n"
  },
  {
    "path": "src/regex/match_data.cr",
    "content": "class Regex\n  # `Regex::MatchData` is the type of the special variable `$~`, and is the type\n  # returned by `Regex#match` and `String#match`. It encapsulates all the\n  # results of a regular expression match.\n  #\n  # ```\n  # if md = \"Crystal\".match(/[p-s]/)\n  #   md.string # => \"Crystal\"\n  #   md[0]     # => \"r\"\n  #   md[1]?    # => nil\n  # end\n  # ```\n  #\n  # Many `Regex::MatchData` methods deal with capture groups, and accept an integer\n  # argument to select the desired capture group. Capture groups are numbered\n  # starting from `1`, so that `0` can be used to refer to the entire regular\n  # expression without needing to capture it explicitly.\n  struct MatchData\n    include Engine::MatchData\n\n    # Returns the original regular expression.\n    #\n    # ```\n    # \"Crystal\".match!(/[p-s]/).regex # => /[p-s]/\n    # ```\n    getter regex : Regex\n\n    # Returns the number of capture groups, including named capture groups.\n    #\n    # ```\n    # \"Crystal\".match!(/[p-s]/).group_size          # => 0\n    # \"Crystal\".match!(/r(ys)/).group_size          # => 1\n    # \"Crystal\".match!(/r(ys)(?<ok>ta)/).group_size # => 2\n    # ```\n    getter group_size : Int32\n\n    # Returns the original string.\n    #\n    # ```\n    # \"Crystal\".match!(/[p-s]/).string # => \"Crystal\"\n    # ```\n    getter string : String\n\n    # Returns the number of elements in this match object.\n    #\n    # ```\n    # \"Crystal\".match!(/[p-s]/).size          # => 1\n    # \"Crystal\".match!(/r(ys)/).size          # => 2\n    # \"Crystal\".match!(/r(ys)(?<ok>ta)/).size # => 3\n    # ```\n    def size : Int32\n      group_size + 1\n    end\n\n    # Returns the position of the first character of the *n*th match.\n    #\n    # When *n* is `0` or not given, uses the match of the entire `Regex`.\n    # Otherwise, uses the match of the *n*th capture group.\n    #\n    # Raises `IndexError` if the index is out of range or the respective\n    # subpattern is unused.\n    #\n    # ```\n    # \"Crystal\".match!(/r/).begin(0)     # => 1\n    # \"Crystal\".match!(/r(ys)/).begin(1) # => 2\n    # \"クリスタル\".match!(/リ(ス)/).begin(0)    # => 1\n    # \"Crystal\".match!(/r/).begin(1)     # IndexError: Invalid capture group index: 1\n    # \"Crystal\".match!(/r(x)?/).begin(1) # IndexError: Capture group 1 was not matched\n    # ```\n    def begin(n : Int32 = 0) : Int32\n      @string.byte_index_to_char_index(byte_begin(n)).not_nil!\n    end\n\n    # Returns the position of the next character after the match.\n    #\n    # When *n* is `0` or not given, uses the match of the entire `Regex`.\n    # Otherwise, uses the match of the *n*th capture group.\n    #\n    # Raises `IndexError` if the index is out of range or the respective\n    # subpattern is unused.\n    #\n    # ```\n    # \"Crystal\".match!(/r/).end(0)     # => 2\n    # \"Crystal\".match!(/r(ys)/).end(1) # => 4\n    # \"クリスタル\".match!(/リ(ス)/).end(0)    # => 3\n    # \"Crystal\".match!(/r/).end(1)     # IndexError: Invalid capture group index: 1\n    # \"Crystal\".match!(/r(x)?/).end(1) # IndexError: Capture group 1 was not matched\n    # ```\n    def end(n : Int32 = 0) : Int32\n      @string.byte_index_to_char_index(byte_end(n)).not_nil!\n    end\n\n    # Returns the position of the first byte of the *n*th match.\n    #\n    # When *n* is `0` or not given, uses the match of the entire `Regex`.\n    # Otherwise, uses the match of the *n*th capture group.\n    #\n    # Raises `IndexError` if the index is out of range or the respective\n    # subpattern is unused.\n    #\n    # ```\n    # \"Crystal\".match!(/r/).byte_begin(0)     # => 1\n    # \"Crystal\".match!(/r(ys)/).byte_begin(1) # => 2\n    # \"クリスタル\".match!(/リ(ス)/).byte_begin(0)    # => 3\n    # \"Crystal\".match!(/r/).byte_begin(1)     # IndexError: Invalid capture group index: 1\n    # \"Crystal\".match!(/r(x)?/).byte_begin(1) # IndexError: Capture group 1 was not matched\n    # ```\n    def byte_begin(n : Int32 = 0) : Int32\n      check_index_out_of_bounds n\n      byte_range(n) { |normalized_n| raise_capture_group_was_not_matched(normalized_n) }.begin\n    end\n\n    # Returns the position of the next byte after the match.\n    #\n    # When *n* is `0` or not given, uses the match of the entire `Regex`.\n    # Otherwise, uses the match of the *n*th capture group.\n    #\n    # Raises `IndexError` if the index is out of range or the respective\n    # subpattern is unused.\n    #\n    # ```\n    # \"Crystal\".match!(/r/).byte_end(0)     # => 2\n    # \"Crystal\".match!(/r(ys)/).byte_end(1) # => 4\n    # \"クリスタル\".match!(/リ(ス)/).byte_end(0)    # => 9\n    # \"Crystal\".match!(/r/).byte_end(1)     # IndexError: Invalid capture group index: 1\n    # \"Crystal\".match!(/r(x)?/).byte_end(1) # IndexError: Capture group 1 was not matched\n    # ```\n    def byte_end(n : Int32 = 0) : Int32\n      check_index_out_of_bounds n\n      byte_range(n) { |normalized_n| raise_capture_group_was_not_matched(normalized_n) }.end\n    end\n\n    # Returns the match of the *n*th capture group, or `nil` if there isn't\n    # an *n*th capture group.\n    #\n    # When *n* is `0`, returns the match for the entire `Regex`.\n    #\n    # ```\n    # \"Crystal\".match!(/r(ys)/)[0]? # => \"rys\"\n    # \"Crystal\".match!(/r(ys)/)[1]? # => \"ys\"\n    # \"Crystal\".match!(/r(ys)/)[2]? # => nil\n    # ```\n    def []?(n : Int) : String?\n      return unless valid_group?(n)\n\n      range = byte_range(n) { return nil }\n      @string.byte_slice(range.begin, range.end - range.begin)\n    end\n\n    # Returns the match of the *n*th capture group, or raises an `IndexError`\n    # if there is no *n*th capture group.\n    #\n    # ```\n    # \"Crystal\".match!(/r(ys)/)[1] # => \"ys\"\n    # \"Crystal\".match!(/r(ys)/)[2] # raises IndexError\n    # ```\n    def [](n : Int) : String\n      check_index_out_of_bounds n\n\n      range = byte_range(n) { |normalized_n| raise_capture_group_was_not_matched(normalized_n) }\n      @string.byte_slice(range.begin, range.end - range.begin)\n    end\n\n    # Returns the match of the capture group named by *group_name*, or\n    # `nil` if there is no such named capture group.\n    #\n    # ```\n    # \"Crystal\".match!(/r(?<ok>ys)/)[\"ok\"]? # => \"ys\"\n    # \"Crystal\".match!(/r(?<ok>ys)/)[\"ng\"]? # => nil\n    # ```\n    #\n    # When there are capture groups having same name, it returns the last\n    # matched capture group.\n    #\n    # ```\n    # \"Crystal\".match!(/(?<ok>Cr).*(?<ok>al)/)[\"ok\"]? # => \"al\"\n    # ```\n    def []?(group_name : String) : String?\n      fetch_impl(group_name) { nil }\n    end\n\n    # Returns the match of the capture group named by *group_name*, or\n    # raises an `KeyError` if there is no such named capture group.\n    #\n    # ```\n    # \"Crystal\".match!(/r(?<ok>ys)/)[\"ok\"] # => \"ys\"\n    # \"Crystal\".match!(/r(?<ok>ys)/)[\"ng\"] # raises KeyError\n    # ```\n    #\n    # When there are capture groups having same name, it returns the last\n    # matched capture group.\n    #\n    # ```\n    # \"Crystal\".match!(/(?<ok>Cr).*(?<ok>al)/)[\"ok\"] # => \"al\"\n    # ```\n    def [](group_name : String) : String\n      fetch_impl(group_name) { |exists|\n        if exists\n          raise KeyError.new(\"Capture group '#{group_name}' was not matched\")\n        else\n          raise KeyError.new(\"Capture group '#{group_name}' does not exist\")\n        end\n      }\n    end\n\n    # Returns all matches that are within the given range.\n    def [](range : Range) : Array(String)\n      self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new]\n    end\n\n    # Like `#[](Range)`, but returns `nil` if the range's start is out of range.\n    def []?(range : Range) : Array(String)?\n      self[*Indexable.range_to_index_and_count(range, size) || return nil]?\n    end\n\n    # Returns count or less (if there aren't enough) matches starting at the\n    # given start index.\n    def [](start : Int, count : Int) : Array(String)\n      self[start, count]? || raise IndexError.new\n    end\n\n    # Like `#[](Int, Int)` but returns `nil` if the *start* index is out of range.\n    def []?(start : Int, count : Int) : Array(String)?\n      start, count = Indexable.normalize_start_and_count(start, count, size) { return nil }\n\n      Array(String).new(count) { |i| self[start + i] }\n    end\n\n    # Returns the part of the original string before the match. If the match\n    # starts at the start of the string, returns the empty string.\n    #\n    # ```\n    # \"Crystal\".match!(/yst/).pre_match # => \"Cr\"\n    # ```\n    def pre_match : String\n      @string.byte_slice(0, byte_begin(0))\n    end\n\n    # Returns the part of the original string after the match. If the match ends\n    # at the end of the string, returns the empty string.\n    #\n    # ```\n    # \"Crystal\".match!(/yst/).post_match # => \"al\"\n    # ```\n    def post_match : String\n      @string.byte_slice(byte_end(0))\n    end\n\n    # Returns an array of unnamed capture groups.\n    #\n    # It is a difference from `to_a` that the result array does not contain the match for the entire `Regex` (`self[0]`).\n    #\n    # ```\n    # match = \"Crystal\".match!(/(Cr)(?<name1>y)(st)(?<name2>al)/)\n    # match.captures # => [\"Cr\", \"st\"]\n    #\n    # # When this regex has an optional group, result array may contain\n    # # a `nil` if this group is not matched.\n    # match = \"Crystal\".match!(/(Cr)(stal)?/)\n    # match.captures # => [\"Cr\", nil]\n    # ```\n    def captures : Array(String?)\n      name_table = @regex.name_table\n\n      caps = [] of String?\n      (1...size).each do |i|\n        caps << self[i]? unless name_table.has_key? i\n      end\n\n      caps\n    end\n\n    # Returns a hash of named capture groups.\n    #\n    # ```\n    # match = \"Crystal\".match!(/(Cr)(?<name1>y)(st)(?<name2>al)/)\n    # match.named_captures # => {\"name1\" => \"y\", \"name2\" => \"al\"}\n    #\n    # # When this regex has an optional group, result hash may contain\n    # # a `nil` if this group is not matched.\n    # match = \"Crystal\".match!(/(?<name1>Cr)(?<name2>stal)?/)\n    # match.named_captures # => {\"name1\" => \"Cr\", \"name2\" => nil}\n    # ```\n    def named_captures : Hash(String, String?)\n      name_table = @regex.name_table\n\n      caps = {} of String => String?\n      (1...size).each do |i|\n        if (name = name_table[i]?) && !caps.has_key?(name)\n          caps[name] = self[name]?\n        end\n      end\n\n      caps\n    end\n\n    # Convert this match data into an array.\n    #\n    # ```\n    # match = \"Crystal\".match!(/(Cr)(?<name1>y)(st)(?<name2>al)/)\n    # match.to_a # => [\"Crystal\", \"Cr\", \"y\", \"st\", \"al\"]\n    #\n    # # When this regex has an optional group, result array may contain\n    # # a `nil` if this group is not matched.\n    # match = \"Crystal\".match!(/(Cr)(?<name1>stal)?/)\n    # match.to_a # => [\"Cr\", \"Cr\", nil]\n    # ```\n    def to_a : Array(String?)\n      (0...size).map { |i| self[i]? }\n    end\n\n    # Convert this match data into a hash.\n    #\n    # ```\n    # match = \"Crystal\".match!(/(Cr)(?<name1>y)(st)(?<name2>al)/)\n    # match.to_h # => {0 => \"Crystal\", 1 => \"Cr\", \"name1\" => \"y\", 3 => \"st\", \"name2\" => \"al\"}\n    #\n    # # When this regex has an optional group, result array may contain\n    # # a `nil` if this group is not matched.\n    # match = \"Crystal\".match!(/(Cr)(?<name1>stal)?/)\n    # match.to_h # => {0 => \"Cr\", 1 => \"Cr\", \"name1\" => nil}\n    # ```\n    def to_h : Hash(Int32 | String, String?)\n      name_table = @regex.name_table\n\n      hash = {} of (String | Int32) => String?\n      (0...size).each do |i|\n        if name = name_table[i]?\n          hash[name] = self[name]? unless hash.has_key?(name)\n        else\n          hash[i] = self[i]?\n        end\n      end\n\n      hash\n    end\n\n    def inspect(io : IO) : Nil\n      name_table = @regex.name_table\n\n      io << \"Regex::MatchData(\"\n      size.times do |i|\n        io << ' ' << name_table.fetch(i, i) << ':' if i > 0\n        self[i]?.inspect(io)\n      end\n      io << ')'\n    end\n\n    # Returns the matched substring.\n    #\n    # ```\n    # \"Crystal\".match!(/yst/).to_s         # => \"yst\"\n    # \"Crystal\".match!(/(y)(s)(?=t)/).to_s # => \"ys\"\n    # ```\n    def to_s : String\n      self[0]\n    end\n\n    # Prints the matched substring to *io*.\n    def to_s(io : IO) : Nil\n      io << to_s\n    end\n\n    def pretty_print(pp) : Nil\n      name_table = @regex.name_table\n\n      pp.surround(\"Regex::MatchData(\", \")\", left_break: nil, right_break: nil) do\n        size.times do |i|\n          pp.breakable if i > 0\n          pp.group do\n            if i == 0\n              self[i].pretty_print pp\n            else\n              pp.text \"#{name_table.fetch(i, i)}:\"\n              pp.nest do\n                pp.breakable \"\"\n                self[i]?.pretty_print pp\n              end\n            end\n          end\n        end\n      end\n    end\n\n    def dup\n      self\n    end\n\n    def clone\n      self\n    end\n\n    def ==(other : Regex::MatchData) : Bool\n      return false unless size == other.size\n      return false unless regex == other.regex\n      return false unless string == other.string\n\n      @ovector.memcmp(other.@ovector, size * 2) == 0\n    end\n\n    # See `Object#hash(hasher)`\n    def hash(hasher)\n      hasher = regex.hash hasher\n      hasher = string.hash hasher\n      Slice.new(@ovector, size * 2).hash(hasher)\n    end\n\n    private def check_index_out_of_bounds(index)\n      raise_invalid_group_index(index) unless valid_group?(index)\n    end\n\n    private def valid_group?(index)\n      -size <= index < size\n    end\n\n    private def raise_invalid_group_index(index)\n      raise IndexError.new(\"Invalid capture group index: #{index}\")\n    end\n\n    private def raise_capture_group_was_not_matched(index)\n      raise IndexError.new(\"Capture group #{index} was not matched\")\n    end\n  end\nend\n"
  },
  {
    "path": "src/regex/pcre.cr",
    "content": "require \"./lib_pcre\"\n\n# :nodoc:\nmodule Regex::PCRE\n  def self.version : String\n    String.new(LibPCRE.version)\n  end\n\n  class_getter version_number : {Int32, Int32} do\n    version = self.version\n    dot = version.index('.') || raise RuntimeError.new(\"Invalid libpcre2 version\")\n    space = version.index(' ', dot) || raise RuntimeError.new(\"Invalid libpcre2 version\")\n    {version.byte_slice(0, dot).to_i, version.byte_slice(dot + 1, space - dot - 1).to_i}\n  end\n\n  private def initialize(*, _source source, _options @options)\n    # PCRE's pattern must have their null characters escaped\n    source = source.gsub('\\u{0}', \"\\\\0\")\n    @source = source\n\n    @re = LibPCRE.compile(@source, pcre_compile_options(options) | LibPCRE::UTF8 | LibPCRE::DUPNAMES | LibPCRE::UCP, out errptr, out erroffset, nil)\n    raise ArgumentError.new(\"#{String.new(errptr)} at #{erroffset}\") if @re.null?\n    @extra = LibPCRE.study(@re, LibPCRE::STUDY_JIT_COMPILE, out studyerrptr)\n    if @extra.null? && studyerrptr\n      {% unless flag?(:interpreted) %}\n        LibPCRE.free.call @re.as(Void*)\n      {% end %}\n      raise ArgumentError.new(\"#{String.new(studyerrptr)}\")\n    end\n    LibPCRE.full_info(@re, nil, LibPCRE::INFO_CAPTURECOUNT, out @captures)\n  end\n\n  private def pcre_compile_options(options)\n    flag = 0\n    Regex::CompileOptions.each do |option|\n      if options.includes?(option)\n        flag |= case option\n                when .ignore_case?       then LibPCRE::CASELESS\n                when .multiline?         then LibPCRE::MULTILINE | LibPCRE::DOTALL\n                when .multiline_only?    then LibPCRE::MULTILINE\n                when .dotall?            then LibPCRE::DOTALL\n                when .extended?          then LibPCRE::EXTENDED\n                when .anchored?          then LibPCRE::ANCHORED\n                when .dollar_endonly?    then LibPCRE::DOLLAR_ENDONLY\n                when .firstline?         then LibPCRE::FIRSTLINE\n                when .utf_8?             then LibPCRE::UTF8\n                when .no_utf_check?      then LibPCRE::NO_UTF8_CHECK\n                when .dupnames?          then LibPCRE::DUPNAMES\n                when .ucp?               then LibPCRE::UCP\n                when .endanchored?       then raise ArgumentError.new(\"Regex::Option::ENDANCHORED is not supported with PCRE\")\n                when .match_invalid_utf? then raise ArgumentError.new(\"Regex::Option::MATCH_INVALID_UTF is not supported with PCRE\")\n                else\n                  raise \"Unreachable\"\n                end\n        options &= ~option\n      end\n    end\n\n    # Unnamed values are explicitly used PCRE options, just pass them through:\n    flag |= options.value\n\n    flag\n  end\n\n  def self.supports_compile_flag?(options)\n    !options.endanchored? && !options.match_invalid_utf?\n  end\n\n  private def pcre_match_options(options)\n    flag = 0\n    Regex::Options.each do |option|\n      if options.includes?(option)\n        flag |= case option\n                when .ignore_case?    then raise ArgumentError.new(\"Invalid regex option IGNORE_CASE for `pcre_exec`\")\n                when .multiline?      then raise ArgumentError.new(\"Invalid regex option MULTILINE for `pcre_exec`\")\n                when .dotall?         then raise ArgumentError.new(\"Invalid regex option DOTALL for `pcre_exec`\")\n                when .extended?       then raise ArgumentError.new(\"Invalid regex option EXTENDED for `pcre_exec`\")\n                when .anchored?       then LibPCRE::ANCHORED\n                when .dollar_endonly? then raise ArgumentError.new(\"Invalid regex option DOLLAR_ENDONLY for `pcre_exec`\")\n                when .firstline?      then raise ArgumentError.new(\"Invalid regex option FIRSTLINE for `pcre_exec`\")\n                when .utf_8?          then raise ArgumentError.new(\"Invalid regex option UTF_8 for `pcre_exec`\")\n                when .no_utf_check?   then LibPCRE::NO_UTF8_CHECK\n                when .dupnames?       then raise ArgumentError.new(\"Invalid regex option DUPNAMES for `pcre_exec`\")\n                when .ucp?            then raise ArgumentError.new(\"Invalid regex option UCP for `pcre_exec`\")\n                when .endanchored?    then raise ArgumentError.new(\"Regex::Option::ENDANCHORED is not supported with PCRE\")\n                else\n                  raise \"Unreachable\"\n                end\n        options &= ~option\n      end\n    end\n\n    # Unnamed values are explicitly used PCRE options, just pass them through:\n    flag |= options.value\n\n    flag\n  end\n\n  private def pcre_match_options(options : Regex::MatchOptions)\n    flag = 0\n    Regex::MatchOptions.each do |option|\n      if options.includes?(option)\n        flag |= case option\n                when .anchored?     then LibPCRE::ANCHORED\n                when .endanchored?  then raise ArgumentError.new(\"Regex::Option::ENDANCHORED is not supported with PCRE\")\n                when .no_jit?       then raise ArgumentError.new(\"Regex::Option::NO_JIT is not supported with PCRE\")\n                when .no_utf_check? then LibPCRE::NO_UTF8_CHECK\n                else\n                  raise \"Unreachable\"\n                end\n        options &= ~option\n      end\n    end\n\n    # Unnamed values are explicitly used PCRE options, just pass them through:\n    flag |= options.value\n\n    flag\n  end\n\n  def self.supports_match_flag?(options)\n    !options.endanchored? && !options.no_jit?\n  end\n\n  def finalize\n    LibPCRE.free_study @extra\n    {% unless flag?(:interpreted) %}\n      LibPCRE.free.call @re.as(Void*)\n    {% end %}\n  end\n\n  protected def self.error_impl(source)\n    re = LibPCRE.compile(source, LibPCRE::UTF8 | LibPCRE::DUPNAMES, out errptr, out erroffset, nil)\n    if re\n      {% unless flag?(:interpreted) %}\n        LibPCRE.free.call re.as(Void*)\n      {% end %}\n      nil\n    else\n      \"#{String.new(errptr)} at #{erroffset}\"\n    end\n  end\n\n  private def name_table_impl\n    LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMECOUNT, out name_count)\n    LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMEENTRYSIZE, out name_entry_size)\n    table_pointer = Pointer(UInt8).null\n    LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMETABLE, pointerof(table_pointer).as(Pointer(Int32)))\n    name_table = table_pointer.to_slice(name_entry_size*name_count)\n\n    lookup = Hash(Int32, String).new\n\n    name_count.times do |i|\n      capture_offset = i * name_entry_size\n      capture_number = ((name_table[capture_offset].to_u16 << 8)).to_i32 | name_table[capture_offset + 1]\n\n      name_offset = capture_offset + 2\n      checked = name_table[name_offset, name_entry_size - 3]\n      name = String.new(checked.to_unsafe)\n\n      lookup[capture_number] = name\n    end\n\n    lookup\n  end\n\n  private def capture_count_impl\n    LibPCRE.full_info(@re, @extra, LibPCRE::INFO_CAPTURECOUNT, out capture_count)\n    capture_count\n  end\n\n  private def match_impl(str, byte_index, options)\n    ovector_size = (@captures + 1) * 3\n    ovector = Pointer(Int32).malloc(ovector_size)\n    if internal_matches?(str, byte_index, options, ovector, ovector_size)\n      Regex::MatchData.new(self, @re, str, byte_index, ovector, @captures)\n    end\n  end\n\n  private def matches_impl(str, byte_index, options)\n    internal_matches?(str, byte_index, options, nil, 0)\n  end\n\n  # Calls `pcre_exec` C function, and handles returning value.\n  private def internal_matches?(str, byte_index, options, ovector, ovector_size)\n    ret = LibPCRE.exec(@re, @extra, str, str.bytesize, byte_index, pcre_match_options(options), ovector, ovector_size)\n\n    return true if ret >= 0\n\n    case error = LibPCRE::Error.new(ret)\n    when .nomatch?\n      return false\n    when .badutf8_offset?\n      raise ArgumentError.new(\"Regex match error: bad offset into UTF string\")\n    when .badutf8?\n      raise ArgumentError.new(\"Regex match error: UTF-8 error\")\n    else\n      raise Regex::Error.new(\"Regex match error: #{error}\")\n    end\n  end\n\n  module MatchData\n    # :nodoc:\n    def initialize(@regex : ::Regex, @code : LibPCRE::Pcre, @string : String, @pos : Int32, @ovector : Int32*, @group_size : Int32)\n    end\n\n    private def byte_range(n, &)\n      n += size if n < 0\n      range = Range.new(@ovector[n * 2], @ovector[n * 2 + 1], exclusive: true)\n      if range.begin < 0 || range.end < 0\n        yield n\n      else\n        range\n      end\n    end\n\n    private def fetch_impl(group_name : String, &)\n      max_start = -1\n      match = nil\n      exists = false\n      each_named_capture_number(group_name) do |n|\n        exists = true\n        start = byte_range(n) { nil }.try(&.begin) || next\n        if start > max_start\n          max_start = start\n          match = self[n]?\n        end\n      end\n      if match\n        match\n      else\n        yield exists\n      end\n    end\n\n    private def each_named_capture_number(group_name, &)\n      name_entry_size = LibPCRE.get_stringtable_entries(@code, group_name, out first, out last)\n      return if name_entry_size < 0\n\n      while first <= last\n        capture_number = (first[0].to_u16 << 8) | first[1].to_u16\n        yield capture_number\n\n        first += name_entry_size\n      end\n\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "src/regex/pcre2.cr",
    "content": "require \"./lib_pcre2\"\nrequire \"crystal/value_with_finalizer\"\n\n# :nodoc:\nmodule Regex::PCRE2\n  @re : LibPCRE2::Code*\n  @jit : Bool\n\n  def self.version : String\n    String.new(24) do |pointer|\n      size = LibPCRE2.config(LibPCRE2::CONFIG_VERSION, pointer)\n      {size - 1, size - 1}\n    end\n  end\n\n  class_getter version_number : {Int32, Int32} do\n    version = self.version\n    dot = version.index('.') || raise RuntimeError.new(\"Invalid libpcre2 version\")\n    space = version.index(' ', dot) || raise RuntimeError.new(\"Invalid libpcre2 version\")\n    # PCRE2 versions can contain -RC{N} which would make `.to_i` fail unless strict is set to false\n    {version.byte_slice(0, dot).to_i, version.byte_slice(dot + 1, space - dot - 1).to_i(strict: false)}\n  end\n\n  # :nodoc:\n  def initialize(*, _source @source : String, _options @options)\n    options = pcre2_compile_options(options) | LibPCRE2::UTF | LibPCRE2::DUPNAMES | LibPCRE2::UCP\n    @re = PCRE2.compile(source, options) do |error_message|\n      raise ArgumentError.new(error_message)\n    end\n\n    @jit = jit_compile\n  end\n\n  private def jit_compile : Bool\n    ret = LibPCRE2.jit_compile(@re, LibPCRE2::JIT_COMPLETE)\n    if ret < 0\n      case error = LibPCRE2::Error.new(ret)\n      when .jit_badoption?\n        # okay\n        return false\n      else\n        raise ArgumentError.new(\"Regex JIT compile error: #{error}\")\n      end\n    end\n    true\n  end\n\n  protected def self.compile(source, options, &)\n    if res = LibPCRE2.compile(source, source.bytesize, options, out errorcode, out erroroffset, nil)\n      res\n    else\n      message = get_error_message(errorcode)\n      yield \"#{message} at #{erroroffset}\"\n    end\n  end\n\n  protected def self.get_error_message(errorcode)\n    String.new(256) do |buffer|\n      bytesize = LibPCRE2.get_error_message(errorcode, buffer, 256)\n      {bytesize, 0}\n    end\n  end\n\n  private def pcre2_compile_options(options)\n    flag = 0\n    Regex::CompileOptions.each do |option|\n      if options.includes?(option)\n        flag |= case option\n                when .ignore_case?       then LibPCRE2::CASELESS\n                when .multiline?         then LibPCRE2::MULTILINE | LibPCRE2::DOTALL\n                when .multiline_only?    then LibPCRE2::MULTILINE\n                when .dotall?            then LibPCRE2::DOTALL\n                when .extended?          then LibPCRE2::EXTENDED\n                when .anchored?          then LibPCRE2::ANCHORED\n                when .dollar_endonly?    then LibPCRE2::DOLLAR_ENDONLY\n                when .firstline?         then LibPCRE2::FIRSTLINE\n                when .utf_8?             then LibPCRE2::UTF\n                when .no_utf_check?      then LibPCRE2::NO_UTF_CHECK\n                when .dupnames?          then LibPCRE2::DUPNAMES\n                when .ucp?               then LibPCRE2::UCP\n                when .endanchored?       then LibPCRE2::ENDANCHORED\n                when .match_invalid_utf? then LibPCRE2::MATCH_INVALID_UTF\n                else\n                  raise \"Unreachable\"\n                end\n        options &= ~option\n      end\n    end\n    unless options.none?\n      raise ArgumentError.new(\"Unknown Regex::Option value: #{options}\")\n    end\n    flag\n  end\n\n  def self.supports_compile_flag?(options)\n    true\n  end\n\n  private def pcre2_match_options(options)\n    flag = 0\n    Regex::Options.each do |option|\n      if options.includes?(option)\n        flag |= case option\n                when .ignore_case?    then raise ArgumentError.new(\"Invalid regex option IGNORE_CASE for `pcre2_match`\")\n                when .multiline?      then raise ArgumentError.new(\"Invalid regex option MULTILINE for `pcre2_match`\")\n                when .dotall?         then raise ArgumentError.new(\"Invalid regex option DOTALL for `pcre2_match`\")\n                when .extended?       then raise ArgumentError.new(\"Invalid regex option EXTENDED for `pcre2_match`\")\n                when .anchored?       then LibPCRE2::ANCHORED\n                when .dollar_endonly? then raise ArgumentError.new(\"Invalid regex option DOLLAR_ENDONLY for `pcre2_match`\")\n                when .firstline?      then raise ArgumentError.new(\"Invalid regex option FIRSTLINE for `pcre2_match`\")\n                when .utf_8?          then raise ArgumentError.new(\"Invalid regex option UTF_8 for `pcre2_match`\")\n                when .no_utf_check?   then LibPCRE2::NO_UTF_CHECK\n                when .dupnames?       then raise ArgumentError.new(\"Invalid regex option DUPNAMES for `pcre2_match`\")\n                when .ucp?            then raise ArgumentError.new(\"Invalid regex option UCP for `pcre2_match`\")\n                when .endanchored?    then LibPCRE2::ENDANCHORED\n                else\n                  raise \"Unreachable\"\n                end\n        options &= ~option\n      end\n    end\n    unless options.none?\n      raise ArgumentError.new(\"Unknown Regex::Option value: #{options}\")\n    end\n    flag\n  end\n\n  private def pcre2_match_options(options : Regex::MatchOptions)\n    flag = 0\n    Regex::MatchOptions.each do |option|\n      if options.includes?(option)\n        flag |= case option\n                when .anchored?     then LibPCRE2::ANCHORED\n                when .endanchored?  then LibPCRE2::ENDANCHORED\n                when .no_jit?       then LibPCRE2::NO_JIT\n                when .no_utf_check? then LibPCRE2::NO_UTF_CHECK\n                else\n                  raise \"Unreachable\"\n                end\n        options &= ~option\n      end\n    end\n    unless options.none?\n      raise ArgumentError.new(\"Unknown Regex::MatchOption value: #{options}\")\n    end\n    flag\n  end\n\n  def self.supports_match_flag?(options)\n    true\n  end\n\n  protected def self.error_impl(source)\n    code = PCRE2.compile(source, LibPCRE2::UTF | LibPCRE2::DUPNAMES | LibPCRE2::UCP) do |error_message|\n      return error_message\n    end\n\n    LibPCRE2.code_free code\n\n    nil\n  end\n\n  private def pattern_info(what)\n    value = uninitialized UInt32\n    pattern_info(what, pointerof(value))\n    value\n  end\n\n  private def pattern_info(what, where)\n    ret = LibPCRE2.pattern_info(@re, what, where)\n    if ret != 0\n      raise \"Error pattern_info #{what}: #{ret}\"\n    end\n  end\n\n  private def name_table_impl\n    lookup = Hash(Int32, String).new\n\n    each_named_capture_group do |capture_number, name_entry|\n      lookup[capture_number] = String.new(name_entry.to_unsafe + 2)\n    end\n\n    lookup\n  end\n\n  # :nodoc:\n  def each_named_capture_group(&)\n    name_table = uninitialized UInt8*\n    pattern_info(LibPCRE2::INFO_NAMETABLE, pointerof(name_table))\n\n    name_entry_size = pattern_info(LibPCRE2::INFO_NAMEENTRYSIZE)\n\n    name_count = pattern_info(LibPCRE2::INFO_NAMECOUNT)\n    name_count.times do\n      capture_number = (name_table[0].to_i << 8) | name_table[1]\n\n      yield capture_number, Slice.new(name_table, name_entry_size)\n\n      name_table += name_entry_size\n    end\n  end\n\n  private def capture_count_impl\n    pattern_info(LibPCRE2::INFO_CAPTURECOUNT).to_i32\n  end\n\n  private def match_impl(str, byte_index, options)\n    match_data = match_data(str, byte_index, options) || return\n\n    # We reuse the same `match_data` allocation, so we must reimplement the\n    # behavior of pcre2_match_data_create_from_pattern (get_ovector_count always\n    # returns 65535, aka the maximum).\n    ovector_count = capture_count_impl &+ 1\n    ovector = Slice.new(LibPCRE2.get_ovector_pointer(match_data), ovector_count &* 2)\n\n    # We need to dup the ovector because `match_data` is re-used for subsequent\n    # matches. We only dup the match data (not everything).\n    ovector = ovector.dup\n\n    ::Regex::MatchData.new(self, @re, str, byte_index, ovector.to_unsafe, ovector_count.to_i32 &- 1)\n  end\n\n  private def matches_impl(str, byte_index, options)\n    if match_data(str, byte_index, options)\n      true\n    else\n      false\n    end\n  end\n\n  class_getter match_context : LibPCRE2::MatchContext* do\n    match_context = LibPCRE2.match_context_create(nil)\n    LibPCRE2.jit_stack_assign(match_context, ->(_data) { current_jit_stack.value }, nil)\n    match_context\n  end\n\n  # JIT stack is unique per thread.\n  #\n  # Only a single `match` function can run per thread at any given time, so\n  # there can't be any concurrent access to the JIT stack.\n  thread_local(current_jit_stack : ::Crystal::ValueWithFinalizer(::LibPCRE2::JITStack*)) do\n    ptr = LibPCRE2.jit_stack_create(32_768, 1_048_576, nil)\n    raise RuntimeError.new(\"Error allocating JIT stack\") if ptr.null?\n    ::Crystal::ValueWithFinalizer.new(ptr, ->(value : ::LibPCRE2::JITStack*) { LibPCRE2.jit_stack_free(value) })\n  end\n\n  # Match data is unique per thread.\n  #\n  # Match data contains a buffer for backtracking when matching in interpreted\n  # mode (non-JIT). This buffer is heap-allocated and should be re-used for\n  # subsequent matches.\n  #\n  # Only a single `match` function can run per thread at any given time, so\n  # there can't be any concurrent access to the match data buffer.\n  thread_local(current_match_data : ::Crystal::ValueWithFinalizer(::LibPCRE2::MatchData*)) do\n    # The ovector size is clamped to 65535 pairs; we declare the maximum because\n    # we allocate the match data buffer once for the thread and need to adapt to\n    # any regular expression.\n    ptr = LibPCRE2.match_data_create(65_535, nil)\n    raise RuntimeError.new(\"Error allocating match data\") if ptr.null?\n    ::Crystal::ValueWithFinalizer.new(ptr, ->(value : LibPCRE2::MatchData*) { LibPCRE2.match_data_free(value) })\n  end\n\n  def finalize\n    LibPCRE2.code_free @re\n  end\n\n  private def match_data(str, byte_index, options)\n    match_data = Regex::PCRE2.current_match_data.value\n    match_count = LibPCRE2.match(@re, str, str.bytesize, byte_index, pcre2_match_options(options), match_data, PCRE2.match_context)\n\n    if match_count < 0\n      case error = LibPCRE2::Error.new(match_count)\n      when .nomatch?\n        return\n      when .badutfoffset?, .utf8_validity?\n        error_message = PCRE2.get_error_message(error)\n        raise ArgumentError.new(\"Regex match error: #{error_message}\")\n      else\n        error_message = PCRE2.get_error_message(error)\n        raise Regex::Error.new(\"Regex match error: #{error_message}\")\n      end\n    end\n\n    match_data\n  end\n\n  module MatchData\n    # :nodoc:\n    def initialize(@regex : Regex, @code : LibPCRE2::Code*, @string : String, @pos : Int32, @ovector : LibC::SizeT*, @group_size : Int32)\n    end\n\n    private def byte_range(n, &)\n      n += size if n < 0\n      range = Range.new(@ovector[n * 2].to_i32!, @ovector[n * 2 + 1].to_i32!, exclusive: true)\n      if range.begin < 0 || range.end < 0\n        yield n\n      else\n        range\n      end\n    end\n\n    private def fetch_impl(group_name : String, &)\n      selected_range = nil\n      exists = false\n      @regex.each_named_capture_group do |number, name_entry|\n        if name_entry[2, group_name.bytesize]? == group_name.to_slice && name_entry[2 + group_name.bytesize].zero?\n          exists = true\n          range = byte_range(number) { nil }\n          if (range && selected_range && range.begin > selected_range.begin) || !selected_range\n            selected_range = range\n          end\n        end\n      end\n\n      if selected_range\n        @string.byte_slice(selected_range.begin, selected_range.end - selected_range.begin)\n      else\n        yield exists\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/regex.cr",
    "content": "require \"./regex/engine\"\nrequire \"./regex/match_data\"\n\n# A `Regex` represents a regular expression, a pattern that describes the\n# contents of strings. A `Regex` can determine whether or not a string matches\n# its description, and extract the parts of the string that match.\n#\n# A `Regex` can be created using the literal syntax, in which it is delimited by\n# forward slashes (`/`):\n#\n# ```\n# /hay/ =~ \"haystack\"   # => 0\n# /y/.match(\"haystack\") # => Regex::MatchData(\"y\")\n# ```\n#\n# See [`Regex` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/regex.html) in the language reference.\n#\n# Interpolation works in regular expression literals just as it does in string\n# literals. Be aware that using this feature will cause an exception to be\n# raised at runtime, if the resulting string would not be a valid regular\n# expression.\n#\n# ```\n# x = \"a\"\n# /#{x}/.match(\"asdf\") # => Regex::MatchData(\"a\")\n# x = \"(\"\n# /#{x}/ # raises ArgumentError\n# ```\n#\n# When we check to see if a particular regular expression describes a string,\n# we can say that we are performing a match or matching one against the other.\n# If we find that a regular expression does describe a string, we say that it\n# matches, and we can refer to a part of the string that was described as\n# a match.\n#\n# Here `\"haystack\"` does not contain the pattern `/needle/`, so it doesn't match:\n#\n# ```\n# /needle/.match(\"haystack\") # => nil\n# ```\n#\n# Here `\"haystack\"` contains the pattern `/hay/`, so it matches:\n#\n# ```\n# /hay/.match(\"haystack\") # => Regex::MatchData(\"hay\")\n# ```\n#\n# Regex methods that perform a match usually return a truthy value if there was\n# a match and `nil` if there was no match. After performing a match, the\n# special variable `$~` will be an instance of `Regex::MatchData` if it matched, `nil`\n# otherwise.\n#\n# When matching a regular expression using `=~` (either `String#=~` or\n# `Regex#=~`), the returned value will be the index of the first match in the\n# string if the expression matched, `nil` otherwise.\n#\n# ```\n# /stack/ =~ \"haystack\"  # => 3\n# \"haystack\" =~ /stack/  # => 3\n# $~                     # => Regex::MatchData(\"stack\")\n# /needle/ =~ \"haystack\" # => nil\n# \"haystack\" =~ /needle/ # => nil\n# $~                     # raises Exception\n# ```\n#\n# When matching a regular expression using `#match` (either `String#match` or\n# `Regex#match`), the returned value will be a `Regex::MatchData` if the expression\n# matched, `nil` otherwise.\n#\n# ```\n# /hay/.match(\"haystack\")    # => Regex::MatchData(\"hay\")\n# \"haystack\".match(/hay/)    # => Regex::MatchData(\"hay\")\n# $~                         # => Regex::MatchData(\"hay\")\n# /needle/.match(\"haystack\") # => nil\n# \"haystack\".match(/needle/) # => nil\n# $~                         # raises Exception\n# ```\n#\n# [Regular expressions](https://en.wikipedia.org/wiki/Regular_expression)\n# have their own language for describing strings.\n#\n# Many programming languages and tools implement their own regular expression\n# language, but Crystal uses [PCRE2](http://www.pcre.org/), a popular C library, with\n# [JIT compilation](http://www.pcre.org/current/doc/html/pcre2jit.html) enabled\n# for providing regular expressions. Here give a brief summary of the most\n# basic features of regular expressions - grouping, repetition, and\n# alternation - but the feature set of PCRE2 extends far beyond these, and we\n# don't attempt to describe it in full here. For more information, refer to\n# the PCRE2 documentation, especially the\n# [full pattern syntax](http://www.pcre.org/current/doc/html/pcre2pattern.html)\n# or\n# [syntax quick reference](http://www.pcre.org/current/doc/html/pcre2syntax.html).\n#\n# NOTE: Prior to Crystal 1.8 the compiler expected regex literals to follow the\n# original [PCRE pattern syntax](https://www.pcre.org/original/doc/html/pcrepattern.html).\n# The following summary applies to both PCRE and PCRE2.\n#\n# The regular expression language can be used to match much more than just the\n# static substrings in the above examples. Certain characters, called\n# [metacharacters](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC4),\n# are given special treatment in regular expressions, and can be used to\n# describe more complex patterns. To match metacharacters literally in a\n# regular expression, they must be escaped by being preceded with a backslash\n# (`\\`). `escape` will do this automatically for a given String.\n#\n# A group of characters (often called a capture group or\n# [subpattern](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC14))\n# can be identified by enclosing it in parentheses (`()`). The contents of\n# each capture group can be extracted on a successful match:\n#\n# ```\n# /a(sd)f/.match(\"_asdf_\")                     # => Regex::MatchData(\"asdf\" 1:\"sd\")\n# /a(sd)f/.match(\"_asdf_\").try &.[1]           # => \"sd\"\n# /a(?<grp>sd)f/.match(\"_asdf_\")               # => Regex::MatchData(\"asdf\" grp:\"sd\")\n# /a(?<grp>sd)f/.match(\"_asdf_\").try &.[\"grp\"] # => \"sd\"\n# ```\n#\n# Capture groups are indexed starting from 1. Methods that accept a capture\n# group index will usually also accept 0 to refer to the full match. Capture\n# groups can also be given names, using the `(?&lt;name&gt;...)` syntax, as in the\n# previous example.\n#\n# Following a match, the special variables $N (e.g., $1, $2, $3, ...) can be used\n# to access a capture group. Trying to access an invalid capture group will raise an\n# exception. Note that it is possible to have a successful match with a nil capture:\n#\n# ```\n# /(spice)(s)?/.match(\"spice\") # => Regex::MatchData(\"spice\" 1:\"spice\" 2:nil)\n# $1                           # => \"spice\"\n# $2                           # => raises Exception\n# ```\n#\n# This can be mitigated by using the nilable version of the above: $N?,\n# (e.g., $1? $2?, $3?, ...). Changing the above to use `$2?` instead of `$2`\n# would return `nil`. `$2?.nil?` would return `true`.\n#\n# A character or group can be\n# [repeated](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC17)\n# or made optional using an asterisk (`*` - zero or more), a plus sign\n# (`+` - one or more), integer bounds in curly braces\n# (`{n,m}`) (at least `n`, no more than `m`), or a question mark\n# (`?`) (zero or one).\n#\n# ```\n# /fo*/.match(\"_f_\")         # => Regex::MatchData(\"f\")\n# /fo+/.match(\"_f_\")         # => nil\n# /fo*/.match(\"_foo_\")       # => Regex::MatchData(\"foo\")\n# /fo{3,}/.match(\"_foo_\")    # => nil\n# /fo{1,3}/.match(\"_foo_\")   # => Regex::MatchData(\"foo\")\n# /fo*/.match(\"_foo_\")       # => Regex::MatchData(\"foo\")\n# /fo*/.match(\"_foooooooo_\") # => Regex::MatchData(\"foooooooo\")\n# /fo{,3}/.match(\"_foooo_\")  # => nil\n# /f(op)*/.match(\"fopopo\")   # => Regex::MatchData(\"fopop\" 1:\"op\")\n# /foo?bar/.match(\"foobar\")  # => Regex::MatchData(\"foobar\")\n# /foo?bar/.match(\"fobar\")   # => Regex::MatchData(\"fobar\")\n# ```\n#\n# Alternatives can be separated using a\n# [vertical bar](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC12)\n# (`|`). Any single character can be represented by\n# [dot](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC7)\n# (`.`). When matching only one character, specific\n# alternatives can be expressed as a\n# [character class](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC9),\n# enclosed in square brackets (`[]`):\n#\n# ```\n# /foo|bar/.match(\"foo\")     # => Regex::MatchData(\"foo\")\n# /foo|bar/.match(\"bar\")     # => Regex::MatchData(\"bar\")\n# /_(x|y)_/.match(\"_x_\")     # => Regex::MatchData(\"_x_\" 1:\"x\")\n# /_(x|y)_/.match(\"_y_\")     # => Regex::MatchData(\"_y_\" 1:\"y\")\n# /_(x|y)_/.match(\"_(x|y)_\") # => nil\n# /_(x|y)_/.match(\"_(x|y)_\") # => nil\n# /_._/.match(\"_x_\")         # => Regex::MatchData(\"_x_\")\n# /_[xyz]_/.match(\"_x_\")     # => Regex::MatchData(\"_x_\")\n# /_[a-z]_/.match(\"_x_\")     # => Regex::MatchData(\"_x_\")\n# /_[^a-z]_/.match(\"_x_\")    # => nil\n# /_[^a-wy-z]_/.match(\"_x_\") # => Regex::MatchData(\"_x_\")\n# ```\n#\n# Regular expressions can be defined with these 3\n# [optional flags](http://www.pcre.org/current/doc/html/pcre2pattern.html#SEC13):\n#\n# * `i`: ignore case (`Regex::Options::IGNORE_CASE`)\n# * `m`: multiline (`Regex::Options::MULTILINE`)\n# * `x`: extended (`Regex::Options::EXTENDED`)\n#\n# ```\n# /asdf/ =~ \"ASDF\"    # => nil\n# /asdf/i =~ \"ASDF\"   # => 0\n# /^z/i =~ \"ASDF\\nZ\"  # => nil\n# /^z/im =~ \"ASDF\\nZ\" # => 5\n# ```\n#\n# PCRE2 supports other encodings, but Crystal strings are UTF-8 only, so Crystal\n# regular expressions are also UTF-8 only (by default).\n# Crystal strings are expected to contain only valid UTF-8 encodings, but that's\n# not guaranteed. There's a chance that a string *can* contain invalid bytes.\n# Especially data read from external sources must not be trusted to be valid encoding.\n# The regex engine demands valid UTF-8, so it checks the encoding for every\n# match. This can be unnecessary if the string is already validated (for example\n# via `String#valid_encoding?` or because it has already been used in a previous\n# regex match).\n# If that's the case, it's profitable to skip UTF-8 validation via `MatchOptions::NO_UTF_CHECK`\n# (or `CompileOptions::NO_UTF_CHECK` for the pattern).\n#\n# PCRE2 optionally permits named capture groups (named subpatterns) to not be\n# unique. Crystal exposes the name table of a `Regex` as a\n# `Hash` of `String` => `Int32`, and therefore requires named capture groups to have\n# unique names within a single `Regex`.\nclass Regex\n  include Regex::Engine\n\n  class Error < Exception\n  end\n\n  # List of metacharacters that need to be escaped.\n  #\n  # See `Regex.needs_escape?` and `Regex.escape`.\n  SPECIAL_CHARACTERS = {\n    ' ', '.', '\\\\', '+', '*', '?', '[',\n    '^', ']', '$', '(', ')', '{', '}',\n    '=', '!', '<', '>', '|', ':', '-',\n  }\n\n  # Represents compile options passed to `Regex.new`.\n  #\n  # This type is intended to be renamed to `CompileOptions`. Please use that\n  # name.\n  @[Flags]\n  enum Options : UInt64\n    # Case insensitive match.\n    IGNORE_CASE = 0x0000_0001\n\n    # PCRE native `PCRE_MULTILINE` flag is `2`, and `PCRE_DOTALL` is `4`\n    # - `PCRE_DOTALL` changes the \"`.`\" meaning\n    # - `PCRE_MULTILINE` changes \"`^`\" and \"`$`\" meanings\n    #\n    # Crystal modifies this meaning to have essentially one unique \"`m`\"\n    # flag that activates both behaviours, so here we do the same by\n    # mapping `MULTILINE` to `PCRE_MULTILINE | PCRE_DOTALL`.\n    # The same applies for PCRE2 except that the native values are 0x200 and 0x400.\n    #\n    # For the behaviour of `PCRE_MULTILINE` use `MULTILINE_ONLY`.\n\n    # Multiline matching.\n    #\n    # Equivalent to `MULTILINE | DOTALL` in PCRE and PCRE2.\n    MULTILINE = 0x0000_0006\n\n    # Equivalent to `MULTILINE` in PCRE and PCRE2.\n    MULTILINE_ONLY = 0x0000_0004\n\n    DOTALL = 0x0000_0002\n\n    # Ignore white space and `#` comments.\n    EXTENDED = 0x0000_0008\n\n    # Force pattern anchoring at the start of the subject.\n    ANCHORED = 0x0000_0010\n\n    DOLLAR_ENDONLY = 0x0000_0020\n    FIRSTLINE      = 0x0004_0000\n\n    # :nodoc:\n    UTF_8 = 0x0000_0800\n    # :nodoc:\n    NO_UTF8_CHECK = 0x0000_2000\n    # :nodoc:\n    DUPNAMES = 0x0008_0000\n    # :nodoc:\n    UCP = 0x2000_0000\n\n    # Force pattern anchoring at the end of the subject.\n    #\n    # Unsupported with PCRE.\n    ENDANCHORED = 0x8000_0000\n\n    # Do not check the pattern for valid UTF encoding.\n    #\n    # This option is potentially dangerous and must only be used when the\n    # pattern is guaranteed to be valid (e.g. `String#valid_encoding?`).\n    # Failing to do so can lead to undefined behaviour in the regex library\n    # and may crash the entire process.\n    #\n    # NOTE: `String` is *supposed* to be valid UTF-8, but this is not guaranteed or\n    # enforced. Especially data originating from external sources should not be\n    # trusted.\n    #\n    # UTF validation is comparatively expensive, so skipping it can produce a\n    # significant performance improvement.\n    #\n    # ```\n    # pattern = \"fo+\"\n    # if pattern.valid_encoding?\n    #   regex = Regex.new(pattern, options: Regex::CompileOptions::NO_UTF_CHECK)\n    #   regex.match(foo)\n    # else\n    #   raise \"Invalid UTF in regex pattern\"\n    # end\n    # ```\n    #\n    # The standard library implicitly applies this option when it can be sure\n    # about the patterns's validity (e.g. on repeated matches in `String#gsub`).\n    NO_UTF_CHECK = NO_UTF8_CHECK\n\n    # Enable matching against subjects containing invalid UTF bytes.\n    # Invalid bytes never match anything. The entire subject string is\n    # effectively split into segments of valid UTF.\n    #\n    # Read more in the [PCRE2 documentation](https://www.pcre.org/current/doc/html/pcre2unicode.html#matchinvalid).\n    #\n    # When this option is set, `MatchOptions::NO_UTF_CHECK` is ignored at match time.\n    #\n    # Unsupported with PCRE.\n    #\n    # NOTE: This option was introduced in PCRE2 10.34 but a bug that can lead to an\n    # infinite loop is only fixed in 10.36 (https://github.com/PCRE2Project/pcre2/commit/e0c6029a62db9c2161941ecdf459205382d4d379).\n    MATCH_INVALID_UTF = 0x1_0000_0000\n  end\n\n  # Represents compile options passed to `Regex.new`.\n  #\n  # This alias is supposed to replace `Options`.\n  alias CompileOptions = Options\n\n  # Returns `true` if the regex engine supports all *options* flags when compiling a pattern.\n  def self.supports_compile_options?(options : CompileOptions) : Bool\n    options.each do |flag|\n      return false unless Engine.supports_compile_flag?(flag)\n    end\n    true\n  end\n\n  # Represents options passed to regex match methods such as `Regex#match`.\n  @[Flags]\n  enum MatchOptions\n    # Force pattern anchoring at the start of the subject.\n    ANCHORED\n\n    # Force pattern anchoring at the end of the subject.\n    #\n    # Unsupported with PCRE.\n    ENDANCHORED\n\n    # Disable JIT engine.\n    #\n    # Unsupported with PCRE.\n    NO_JIT\n\n    # Do not check subject for valid UTF encoding.\n    #\n    # This option is potentially dangerous and must only be used when the\n    # subject is guaranteed to be valid (e.g. `String#valid_encoding?`).\n    # Failing to do so can lead to undefined behaviour in the regex library\n    # and may crash the entire process.\n    #\n    # NOTE: `String` is *supposed* to be valid UTF-8, but this is not guaranteed or\n    # enforced. Especially data originating from external sources should not be\n    # trusted.\n    #\n    # UTF validation is comparatively expensive, so skipping it can produce a\n    # significant performance improvement.\n    #\n    # ```\n    # subject = \"foo\"\n    # if subject.valid_encoding?\n    #   /foo/.match(subject, options: Regex::MatchOptions::NO_UTF_CHECK)\n    # else\n    #   raise \"Invalid UTF in regex subject\"\n    # end\n    # ```\n    #\n    # A good use case is when the same subject is matched multiple times, UTF\n    # validation only needs to happen once.\n    #\n    # This option has no effect if the pattern was compiled with\n    # `CompileOptions::MATCH_INVALID_UTF` when using PCRE2 10.34+.\n    NO_UTF_CHECK\n  end\n\n  # Returns `true` if the regex engine supports all *options* flags when matching a pattern.\n  def self.supports_match_options?(options : MatchOptions) : Bool\n    options.each do |flag|\n      return false unless Engine.supports_match_flag?(flag)\n    end\n    true\n  end\n\n  # Returns a `Regex::CompileOptions` representing the optional flags applied to this `Regex`.\n  #\n  # ```\n  # /ab+c/ix.options      # => Regex::CompileOptions::IGNORE_CASE | Regex::CompileOptions::EXTENDED\n  # /ab+c/ix.options.to_s # => \"IGNORE_CASE | EXTENDED\"\n  # ```\n  getter options : Options\n\n  # Returns the original `String` representation of the `Regex` pattern.\n  #\n  # ```\n  # /ab+c/x.source # => \"ab+c\"\n  # ```\n  getter source : String\n\n  # Creates a new `Regex` out of the given source `String`.\n  #\n  # ```\n  # Regex.new(\"^a-z+:\\\\s+\\\\w+\")                          # => /^a-z+:\\s+\\w+/\n  # Regex.new(\"cat\", Regex::CompileOptions::IGNORE_CASE) # => /cat/i\n  # options = Regex::CompileOptions::IGNORE_CASE | Regex::CompileOptions::EXTENDED\n  # Regex.new(\"dog\", options) # => /dog/ix\n  # ```\n  def self.new(source : String, options : Options = Options::None) : self\n    new(_source: source, _options: options)\n  end\n\n  # Creates a new `Regex` instance from a literal consisting of a *pattern* and the named parameter modifiers.\n  def self.literal(pattern : String, *, i : Bool = false, m : Bool = false, x : Bool = false) : self\n    options = CompileOptions::None\n    options |= :ignore_case if i\n    options |= :multiline if m\n    options |= :extended if x\n    new(pattern, options: options)\n  end\n\n  # Determines Regex's source validity. If it is, `nil` is returned.\n  # If it's not, a `String` containing the error message is returned.\n  #\n  # ```\n  # Regex.error?(\"(foo|bar)\") # => nil\n  # Regex.error?(\"(foo|bar\")  # => \"missing ) at 8\"\n  # ```\n  def self.error?(source : String) : String?\n    Engine.error_impl(source)\n  end\n\n  # Returns `true` if *char* need to be escaped, `false` otherwise.\n  #\n  # ```\n  # Regex.needs_escape?('*') # => true\n  # Regex.needs_escape?('@') # => false\n  # ```\n  def self.needs_escape?(char : Char) : Bool\n    SPECIAL_CHARACTERS.includes?(char)\n  end\n\n  # Returns `true` if *str* need to be escaped, `false` otherwise.\n  #\n  # ```\n  # Regex.needs_escape?(\"10$\") # => true\n  # Regex.needs_escape?(\"foo\") # => false\n  # ```\n  def self.needs_escape?(str : String) : Bool\n    str.each_char { |char| return true if SPECIAL_CHARACTERS.includes?(char) }\n    false\n  end\n\n  # Returns a `String` constructed by escaping any metacharacters in *str*.\n  #\n  # ```\n  # string = Regex.escape(\"*?{}.\") # => \"\\\\*\\\\?\\\\{\\\\}\\\\.\"\n  # /#{string}/                    # => /\\*\\?\\{\\}\\./\n  # ```\n  def self.escape(str : String) : String\n    String.build do |result|\n      str.each_byte do |byte|\n        {% begin %}\n          case byte.unsafe_chr\n          when {{SPECIAL_CHARACTERS.splat}}\n            result << '\\\\'\n            result.write_byte byte\n          else\n            result.write_byte byte\n          end\n        {% end %}\n      end\n    end\n  end\n\n  # Union. Returns a `Regex` that matches any of *patterns*.\n  #\n  # All capture groups in the patterns after the first one will have their\n  # indexes offset.\n  #\n  # ```\n  # re = Regex.union([/skiing/i, \"sledding\"])\n  # re.match(\"Skiing\")   # => Regex::MatchData(\"Skiing\")\n  # re.match(\"sledding\") # => Regex::MatchData(\"sledding\")\n  # re = Regex.union({/skiing/i, \"sledding\"})\n  # re.match(\"Skiing\")   # => Regex::MatchData(\"Skiing\")\n  # re.match(\"sledding\") # => Regex::MatchData(\"sledding\")\n  # ```\n  def self.union(patterns : Enumerable(Regex | String)) : self\n    new patterns.map { |pattern| union_part pattern }.join('|')\n  end\n\n  # Union. Returns a `Regex` that matches any of *patterns*.\n  #\n  # All capture groups in the patterns after the first one will have their\n  # indexes offset.\n  #\n  # ```\n  # re = Regex.union(/skiing/i, \"sledding\")\n  # re.match(\"Skiing\")   # => Regex::MatchData(\"Skiing\")\n  # re.match(\"sledding\") # => Regex::MatchData(\"sledding\")\n  # ```\n  def self.union(*patterns : Regex | String) : self\n    union patterns\n  end\n\n  private def self.union_part(pattern : Regex)\n    pattern.to_s\n  end\n\n  private def self.union_part(pattern : String)\n    escape pattern\n  end\n\n  # Union. Returns a `Regex` that matches either of the operands.\n  #\n  # All capture groups in the second operand will have their indexes\n  # offset.\n  #\n  # ```\n  # re = /skiing/i + /sledding/\n  # re.match(\"Skiing\")   # => Regex::MatchData(\"Skiing\")\n  # re.match(\"sledding\") # => Regex::MatchData(\"sledding\")\n  # ```\n  def +(other) : Regex\n    Regex.union(self, other)\n  end\n\n  # Equality. Two regexes are equal if their sources and options are the same.\n  #\n  # ```\n  # /abc/ == /abc/i  # => false\n  # /abc/i == /ABC/i # => false\n  # /abc/i == /abc/i # => true\n  # ```\n  def ==(other : Regex) : Bool\n    source == other.source && options == other.options\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher = source.hash hasher\n    hasher = options.hash hasher\n    hasher\n  end\n\n  # Case equality. This is equivalent to `#match` or `#=~` but only returns\n  # `true` or `false`. Used in `case` expressions. The special variable\n  # `$~` will contain a `Regex::MatchData` if there was a match, `nil` otherwise.\n  #\n  # ```\n  # a = \"HELLO\"\n  # b = case a\n  #     when /^[a-z]*$/\n  #       \"Lower case\"\n  #     when /^[A-Z]*$/\n  #       \"Upper case\"\n  #     else\n  #       \"Mixed case\"\n  #     end\n  # b # => \"Upper case\"\n  # ```\n  def ===(other : String) : Bool\n    match = match(other)\n    $~ = match\n    !match.nil?\n  end\n\n  # Match. Matches a regular expression against *other* and returns\n  # the starting position of the match if *other* is a matching `String`,\n  # otherwise `nil`. `$~` will contain a `Regex::MatchData` if there was a match,\n  # `nil` otherwise.\n  #\n  # ```\n  # /at/ =~ \"input data\" # => 7\n  # /ax/ =~ \"input data\" # => nil\n  # ```\n  def =~(other : String) : Int32?\n    match = self.match(other)\n    $~ = match\n    match.try &.begin(0)\n  end\n\n  # Match. When the argument is not a `String`, always returns `nil`.\n  #\n  # ```\n  # /at/ =~ \"input data\" # => 7\n  # /ax/ =~ \"input data\" # => nil\n  # ```\n  def =~(other) : Nil\n    nil\n  end\n\n  # Prints to *io* an unambiguous string representation of this regular expression object.\n  #\n  # Uses the regex literal syntax with basic option flags if sufficient (i.e. no\n  # other options than `IGNORE_CASE`, `MULTILINE`, or `EXTENDED` are set).\n  # Otherwise the syntax presents a `Regex.new` call.\n  # ```\n  # /ab+c/ix.inspect                     # => \"/ab+c/ix\"\n  # Regex.new(\"ab+c\", :anchored).inspect # => Regex.new(\"ab+c\", Regex::Options::ANCHORED)\n  # ```\n  def inspect(io : IO) : Nil\n    if (options & ~CompileOptions[IGNORE_CASE, MULTILINE, EXTENDED]).none?\n      inspect_literal(io)\n    else\n      inspect_extensive(io)\n    end\n  end\n\n  # Convert to `String` in literal format. Returns the source as a `String` in\n  # Regex literal format, delimited in forward slashes (`/`), with option flags\n  # included.\n  #\n  # Only `IGNORE_CASE`, `MULTILINE`, and `EXTENDED` options can be represented.\n  # Any other option value is ignored. Use `#inspect` instead for an unambiguous\n  # and correct representation.\n  #\n  # ```\n  # /ab+c/ix.inspect_literal                     # => \"/ab+c/ix\"\n  # Regex.new(\"ab+c\", :anchored).inspect_literal # => \"/ab+c/\"\n  # ```\n  private def inspect_literal(io : IO) : Nil\n    io << '/'\n    Regex.append_source(source, io)\n    io << '/'\n    io << 'i' if options.ignore_case?\n    io << 'm' if options.multiline?\n    io << 'x' if options.extended?\n  end\n\n  # Prints to *io* an extensive string representation of this regular expression object.\n  # The result is unambiguous and mirrors a Crystal expression to recreate an equivalent\n  # instance.\n  #\n  # ```\n  # /ab+c/ix.inspect_literal                     # => Regex.new(\"ab+c\", Regex::Options[IGNORE_CASE, EXTENDED])\n  # Regex.new(\"ab+c\", :anchored).inspect_literal # => Regex.new(\"ab+c\", Regex::Options::ANCHORED)\n  # ```\n  private def inspect_extensive(io : IO) : Nil\n    io << \"Regex.new(\"\n    source.inspect(io)\n    io << \", \"\n    options.inspect(io)\n    io << \")\"\n  end\n\n  # Match at character index. Matches a regular expression against `String`\n  # *str*. Starts at the character index given by *pos* if given, otherwise at\n  # the start of *str*. Returns a `Regex::MatchData` if *str* matched, otherwise\n  # `nil`. `$~` will contain the same value that was returned.\n  #\n  # ```\n  # /(.)(.)(.)/.match(\"abc\").try &.[2]   # => \"b\"\n  # /(.)(.)/.match(\"abc\", 1).try &.[2]   # => \"c\"\n  # /(.)(.)/.match(\"クリスタル\", 3).try &.[2] # => \"ル\"\n  # ```\n  def match(str : String, pos : Int32 = 0, options : Regex::MatchOptions = :none) : MatchData?\n    if byte_index = str.char_index_to_byte_index(pos)\n      $~ = match_at_byte_index(str, byte_index, options)\n    else\n      $~ = nil\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def match(str, pos = 0, *, options) : MatchData?\n    if byte_index = str.char_index_to_byte_index(pos)\n      $~ = match_at_byte_index(str, byte_index, options)\n    else\n      $~ = nil\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def match(str, pos, _options) : MatchData?\n    match(str, pos, options: _options)\n  end\n\n  # Matches a regular expression against *str*. This starts at the character\n  # index *pos* if given, otherwise at the start of *str*. Returns a `Regex::MatchData`\n  # if *str* matched, otherwise raises `Regex::Error`. `$~` will contain the same value\n  # if matched.\n  #\n  # ```\n  # /(.)(.)(.)/.match!(\"abc\")[2]   # => \"b\"\n  # /(.)(.)/.match!(\"abc\", 1)[2]   # => \"c\"\n  # /(.)(タ)/.match!(\"クリスタル\", 3)[2] # raises Exception\n  # ```\n  def match!(str : String, pos : Int32 = 0, *, options : Regex::MatchOptions = :none) : MatchData\n    byte_index = str.char_index_to_byte_index(pos) || raise Error.new \"Match not found\"\n    $~ = match_at_byte_index(str, byte_index, options) || raise Error.new \"Match not found\"\n  end\n\n  # Match at byte index. Matches a regular expression against `String`\n  # *str*. Starts at the byte index given by *pos* if given, otherwise at\n  # the start of *str*. Returns a `Regex::MatchData` if *str* matched, otherwise\n  # `nil`. `$~` will contain the same value that was returned.\n  #\n  # ```\n  # /(.)(.)(.)/.match_at_byte_index(\"abc\").try &.[2]   # => \"b\"\n  # /(.)(.)/.match_at_byte_index(\"abc\", 1).try &.[2]   # => \"c\"\n  # /(.)(.)/.match_at_byte_index(\"クリスタル\", 3).try &.[2] # => \"ス\"\n  # ```\n  def match_at_byte_index(str : String, byte_index : Int32 = 0, options : Regex::MatchOptions = :none) : MatchData?\n    if byte_index > str.bytesize\n      $~ = nil\n    else\n      $~ = match_impl(str, byte_index, options)\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def match_at_byte_index(str, byte_index = 0, *, options) : MatchData?\n    if byte_index > str.bytesize\n      $~ = nil\n    else\n      $~ = match_impl(str, byte_index, options)\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def match_at_byte_index(str, byte_index, _options) : MatchData?\n    match_at_byte_index(str, byte_index, options: _options)\n  end\n\n  # Match at character index. It behaves like `#match`, however it returns `Bool` value.\n  # It neither returns `MatchData` nor assigns it to the `$~` variable.\n  #\n  # ```\n  # /foo/.matches?(\"bar\") # => false\n  # /foo/.matches?(\"foo\") # => true\n  #\n  # # `$~` is not set even if last match succeeds.\n  # $~ # raises Exception\n  # ```\n  def matches?(str : String, pos : Int32 = 0, options : Regex::MatchOptions = :none) : Bool\n    if byte_index = str.char_index_to_byte_index(pos)\n      matches_at_byte_index?(str, byte_index, options)\n    else\n      false\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def matches?(str, pos = 0, *, options) : Bool\n    if byte_index = str.char_index_to_byte_index(pos)\n      matches_at_byte_index?(str, byte_index, options)\n    else\n      false\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def matches?(str, pos, _options) : Bool\n    matches?(str, pos, options: _options)\n  end\n\n  # Match at byte index. It behaves like `#match_at_byte_index`, however it returns `Bool` value.\n  # It neither returns `MatchData` nor assigns it to the `$~` variable.\n  def matches_at_byte_index?(str : String, byte_index : Int32 = 0, options : Regex::MatchOptions = :none) : Bool\n    return false if byte_index > str.bytesize\n\n    matches_impl(str, byte_index, options)\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def matches_at_byte_index?(str, byte_index = 0, *, options) : Bool\n    return false if byte_index > str.bytesize\n\n    matches_impl(str, byte_index, options)\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use the overload with `Regex::MatchOptions` instead.\")]\n  def matches_at_byte_index?(str, byte_index, _options) : Bool\n    matches_at_byte_index?(str, byte_index, options: _options)\n  end\n\n  # Returns a `Hash` where the values are the names of capture groups and the\n  # keys are their indexes. Non-named capture groups will not have entries in\n  # the `Hash`. Capture groups are indexed starting from `1`.\n  #\n  # ```\n  # /(.)/.name_table                         # => {}\n  # /(?<foo>.)/.name_table                   # => {1 => \"foo\"}\n  # /(?<foo>.)(?<bar>.)/.name_table          # => {2 => \"bar\", 1 => \"foo\"}\n  # /(.)(?<foo>.)(.)(?<bar>.)(.)/.name_table # => {4 => \"bar\", 2 => \"foo\"}\n  # ```\n  def name_table : Hash(Int32, String)\n    name_table_impl\n  end\n\n  # Returns the number of (named & non-named) capture groups.\n  #\n  # ```\n  # /(?:.+)/.capture_count     # => 0\n  # /(?<foo>.+)/.capture_count # => 1\n  # /(.)/.capture_count        # => 1\n  # /(.)|(.)/.capture_count    # => 2\n  # ```\n  def capture_count : Int32\n    capture_count_impl\n  end\n\n  # Convert to `String` in subpattern format. Produces a `String` which can be\n  # embedded in another `Regex` via interpolation, where it will be interpreted\n  # as a non-capturing subexpression in another regular expression.\n  #\n  # ```\n  # re = /A*/i                 # => /A*/i\n  # re.to_s                    # => \"(?i-msx:A*)\"\n  # \"Crystal\".match(/t#{re}l/) # => Regex::MatchData(\"tal\")\n  # re = /A*/                  # => \"(?-imsx:A*)\"\n  # \"Crystal\".match(/t#{re}l/) # => nil\n  # ```\n  def to_s(io : IO) : Nil\n    io << \"(?\"\n    io << 'i' if options.ignore_case?\n    io << \"ms\" if options.multiline?\n    io << 'x' if options.extended?\n\n    io << '-'\n    io << 'i' unless options.ignore_case?\n    io << \"ms\" unless options.multiline?\n    io << 'x' unless options.extended?\n\n    io << ':'\n    Regex.append_source(source, io)\n    io << ')'\n  end\n\n  # :nodoc:\n  def self.append_source(source : String, io : IO) : Nil\n    reader = Char::Reader.new(source)\n    while reader.has_next?\n      case char = reader.current_char\n      when '\\\\'\n        io << '\\\\'\n        io << reader.next_char\n      when '/'\n        io << \"\\\\/\"\n      else\n        io << char\n      end\n      reader.next_char\n    end\n  end\n\n  def dup\n    self\n  end\n\n  def clone : Regex\n    self\n  end\nend\n"
  },
  {
    "path": "src/semantic_version.cr",
    "content": "# Conforms to Semantic Versioning 2.0.0\n#\n# See [https://semver.org/](https://semver.org/) for more information.\nstruct SemanticVersion\n  include Comparable(self)\n\n  VERSION_PATTERN = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\n                      (?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?\n                      (?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$/x\n\n  # The major version of this semantic version\n  getter major : Int32\n\n  # The minor version of this semantic version\n  getter minor : Int32\n\n  # The patch version of this semantic version\n  getter patch : Int32\n\n  # The build metadata of this semantic version\n  getter build : String?\n\n  # The pre-release version of this semantic version\n  getter prerelease : Prerelease\n\n  # Checks if *str* is a valid semantic version.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # SemanticVersion.valid?(\"1.15.0\") # => true\n  # SemanticVersion.valid?(\"1.2\")    # => false\n  def self.valid?(str : String) : Bool\n    VERSION_PATTERN.matches?(str)\n  end\n\n  # Parses a `SemanticVersion` from the given semantic version string.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # semver = SemanticVersion.parse(\"2.61.4\")\n  # semver # => #<SemanticVersion:0x55b3667c9e70 @major=2, @minor=61, @patch=4, ... >\n  # ```\n  #\n  # Raises `ArgumentError` if *str* is not a semantic version.\n  def self.parse(str : String) : self\n    parse?(str) || raise ArgumentError.new(\"Not a semantic version: #{str.inspect}\")\n  end\n\n  # Parses a `SemanticVersion` from the given semantic version string.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # semver = SemanticVersion.parse?(\"2.61.4\")\n  # semver # => #<SemanticVersion:0x55b3667c9e70 @major=2, @minor=61, @patch=4, ... >\n  # ```\n  #\n  # Returns `nil` if *str* is not a semantic version.\n  def self.parse?(str : String) : self?\n    return unless m = str.match VERSION_PATTERN\n\n    new m[1].to_i, m[2].to_i, m[3].to_i, m[4]?, m[5]?\n  end\n\n  # Creates a new `SemanticVersion` instance with the given major, minor, and patch versions\n  # and optionally build and pre-release version\n  #\n  # Raises `ArgumentError` if *prerelease* is invalid pre-release version\n  def initialize(@major : Int, @minor : Int, @patch : Int, prerelease : String | Prerelease | Nil = nil, @build : String? = nil)\n    @prerelease = case prerelease\n                  when Prerelease\n                    prerelease\n                  when String\n                    Prerelease.parse prerelease\n                  when nil\n                    Prerelease.new\n                  else\n                    raise ArgumentError.new(\"Invalid prerelease #{prerelease.inspect}\")\n                  end\n  end\n\n  def_equals_and_hash major, minor, patch, prerelease, build\n\n  # Returns the string representation of this semantic version\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # semver = SemanticVersion.parse(\"0.27.1\")\n  # semver.to_s # => \"0.27.1\"\n  # ```\n  def to_s(io : IO) : Nil\n    io << major << '.' << minor << '.' << patch\n    unless prerelease.identifiers.empty?\n      io << '-'\n      prerelease.to_s io\n    end\n    if build\n      io << '+' << build\n    end\n  end\n\n  # Returns a new `SemanticVersion` created with the specified parts. The\n  # default for each part is its current value.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # current_version = SemanticVersion.new 1, 1, 1, \"rc\"\n  # current_version.copy_with(patch: 2)        # => SemanticVersion(@build=nil, @major=1, @minor=1, @patch=2, @prerelease=SemanticVersion::Prerelease(@identifiers=[\"rc\"]))\n  # current_version.copy_with(prerelease: nil) # => SemanticVersion(@build=nil, @major=1, @minor=1, @patch=1, @prerelease=SemanticVersion::Prerelease(@identifiers=[]))\n  # ```\n  def copy_with(major : Int32 = @major, minor : Int32 = @minor, patch : Int32 = @patch, prerelease : String | Prerelease | Nil = @prerelease, build : String? = @build)\n    SemanticVersion.new major, minor, patch, prerelease, build\n  end\n\n  # Returns a copy of the current version with a major bump.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # current_version = SemanticVersion.new 1, 1, 1, \"rc\"\n  # current_version.bump_major # => SemanticVersion(@build=nil, @major=2, @minor=0, @patch=0, @prerelease=SemanticVersion::Prerelease(@identifiers=[]))\n  # ```\n  def bump_major\n    copy_with(major: major + 1, minor: 0, patch: 0, prerelease: nil, build: nil)\n  end\n\n  # Returns a copy of the current version with a minor bump.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # current_version = SemanticVersion.new 1, 1, 1, \"rc\"\n  # current_version.bump_minor # => SemanticVersion(@build=nil, @major=1, @minor=2, @patch=0, @prerelease=SemanticVersion::Prerelease(@identifiers=[]))\n  # ```\n  def bump_minor\n    copy_with(minor: minor + 1, patch: 0, prerelease: nil, build: nil)\n  end\n\n  # Returns a copy of the current version with a patch bump. Bumping a patch of\n  # a prerelease just erase the prerelease data.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # current_version = SemanticVersion.new 1, 1, 1, \"rc\"\n  # next_patch = current_version.bump_patch # => SemanticVersion(@build=nil, @major=1, @minor=1, @patch=1, @prerelease=SemanticVersion::Prerelease(@identifiers=[]))\n  # next_patch.bump_patch                   # => SemanticVersion(@build=nil, @major=1, @minor=1, @patch=2, @prerelease=SemanticVersion::Prerelease(@identifiers=[]))\n  # ```\n  def bump_patch\n    if prerelease.identifiers.empty?\n      copy_with(patch: patch + 1, prerelease: nil, build: nil)\n    else\n      copy_with(prerelease: nil, build: nil)\n    end\n  end\n\n  # The comparison operator.\n  #\n  # Returns `-1`, `0` or `1` depending on whether `self`'s version is lower than *other*'s,\n  # equal to *other*'s version or greater than *other*'s version.\n  #\n  # ```\n  # require \"semantic_version\"\n  #\n  # semver1 = SemanticVersion.new(1, 0, 0)\n  # semver2 = SemanticVersion.new(2, 0, 0)\n  #\n  # semver1 <=> semver2 # => -1\n  # semver2 <=> semver2 # => 0\n  # semver2 <=> semver1 # => 1\n  # ```\n  def <=>(other : self) : Int32\n    r = major <=> other.major\n    return r unless r.zero?\n    r = minor <=> other.minor\n    return r unless r.zero?\n    r = patch <=> other.patch\n    return r unless r.zero?\n\n    prerelease <=> other.prerelease\n  end\n\n  # Contains the pre-release version related to this semantic version\n  struct Prerelease\n    include Comparable(self)\n\n    # Parses a `Prerelease` from the given pre-release version string\n    #\n    # ```\n    # require \"semantic_version\"\n    #\n    # prerelease = SemanticVersion::Prerelease.parse(\"rc.1.3\")\n    # prerelease # => SemanticVersion::Prerelease(@identifiers=[\"rc\", 1, 3])\n    # ```\n    def self.parse(str : String) : self\n      identifiers = [] of String | Int32\n      str.split('.') do |val|\n        if number = val.to_i32?\n          identifiers << number\n        else\n          identifiers << val\n        end\n      end\n      Prerelease.new identifiers\n    end\n\n    # Array of identifiers that make up the pre-release metadata\n    getter identifiers : Array(String | Int32)\n\n    # Creates a new `Prerelease` instance with supplied array of identifiers\n    def initialize(@identifiers : Array(String | Int32) = [] of String | Int32)\n    end\n\n    # Returns the string representation of this semantic version's pre-release metadata\n    #\n    # ```\n    # require \"semantic_version\"\n    #\n    # semver = SemanticVersion.parse(\"0.27.1-rc.1\")\n    # semver.prerelease.to_s # => \"rc.1\"\n    # ```\n    def to_s(io : IO) : Nil\n      identifiers.join(io, '.')\n    end\n\n    # The comparison operator.\n    #\n    # Returns `-1`, `0` or `1` depending on whether `self`'s pre-release is lower than *other*'s,\n    # equal to *other*'s pre-release or greater than *other*'s pre-release.\n    #\n    # ```\n    # require \"semantic_version\"\n    #\n    # prerelease1 = SemanticVersion::Prerelease.new([\"rc\", 1])\n    # prerelease2 = SemanticVersion::Prerelease.new([\"rc\", 1, 2])\n    #\n    # prerelease1 <=> prerelease2 # => -1\n    # prerelease1 <=> prerelease1 # => 0\n    # prerelease2 <=> prerelease1 # => 1\n    # ```\n    def <=>(other : self) : Int32\n      if identifiers.empty?\n        if other.identifiers.empty?\n          return 0\n        else\n          return 1\n        end\n      elsif other.identifiers.empty?\n        return -1\n      end\n\n      identifiers.each_with_index do |item, i|\n        return 1 if i >= other.identifiers.size # larger = higher precedence\n\n        oitem = other.identifiers[i]\n        r = compare item, oitem\n        return r if r != 0\n      end\n\n      return -1 if identifiers.size != other.identifiers.size # larger = higher precedence\n      0\n    end\n\n    private def compare(x : Int32, y : String)\n      -1\n    end\n\n    private def compare(x : String, y : Int32)\n      1\n    end\n\n    private def compare(x : Int32, y : Int32)\n      x <=> y\n    end\n\n    private def compare(x : String, y : String)\n      x <=> y\n    end\n  end\nend\n"
  },
  {
    "path": "src/set.cr",
    "content": "# `Set` implements a collection of values with no duplicates.\n#\n# An `Enumerable` object can be converted to `Set` using the `#to_set` method.\n#\n# `Set` uses `Hash` as storage, so you must note the following points:\n#\n# * Equality of elements is determined according to `Object#==` and `Object#hash`.\n# * Enumeration follows the order that the elements were inserted.\n# * `Set` assumes that the identity of each element does not change while it is stored. Modifying an element of a set will render the set to an unreliable state.\n#\n# ### Example\n#\n# ```\n# s1 = Set{1, 2}\n# s2 = [1, 2].to_set\n# s3 = Set.new [1, 2]\n# s1 == s2 # => true\n# s1 == s3 # => true\n# s1.add(2)\n# s1.concat([6, 8])\n# s1.subset_of? s2 # => false\n# s2.subset_of? s1 # => true\n# ```\nstruct Set(T)\n  include Enumerable(T)\n  include Iterable(T)\n\n  # Creates a new, empty `Set`.\n  #\n  # ```\n  # s = Set(Int32).new\n  # s.empty? # => true\n  # ```\n  #\n  # An initial capacity can be specified, and it will be set as the initial capacity\n  # of the internal `Hash`.\n  def initialize(initial_capacity = nil)\n    @hash = Hash(T, Nil).new(initial_capacity: initial_capacity)\n  end\n\n  protected def initialize(*, using_hash @hash : Hash(T, Nil))\n  end\n\n  # Optimized version of `new` used when *other* is also an `Indexable`\n  def self.new(other : Indexable(T))\n    Set(T).new(other.size).concat(other)\n  end\n\n  # Creates a new set from the elements in *enumerable*.\n  #\n  # ```\n  # a = [1, 3, 5]\n  # s = Set.new a\n  # s.empty? # => false\n  # ```\n  def self.new(enumerable : Enumerable(T))\n    Set(T).new.concat(enumerable)\n  end\n\n  # Makes this set compare objects using their object identity (`object_id)`\n  # for types that define such method (`Reference` types, but also structs that\n  # might wrap other `Reference` types and delegate the `object_id` method to them).\n  #\n  # ```\n  # s = Set{\"foo\", \"bar\"}\n  # s.includes?(\"fo\" + \"o\") # => true\n  #\n  # s.compare_by_identity\n  # s.compare_by_identity?  # => true\n  # s.includes?(\"fo\" + \"o\") # => false # not the same String instance\n  # ```\n  def compare_by_identity : self\n    @hash.compare_by_identity\n    self\n  end\n\n  # Returns `true` if this Set is comparing objects by `object_id`.\n  #\n  # See `compare_by_identity`.\n  def compare_by_identity? : Bool\n    @hash.compare_by_identity?\n  end\n\n  # Alias for `add`\n  def <<(object : T) : self\n    add object\n  end\n\n  # Adds *object* to the set and returns `self`.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.includes? 8 # => false\n  # s.add(8)\n  # s.includes? 8 # => true\n  # ```\n  def add(object : T) : self\n    @hash[object] = nil\n    self\n  end\n\n  # Adds *object* to the set and returns `true` on success\n  # and `false` if the value was already in the set.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.add? 8 # => true\n  # s.add? 8 # => false\n  # ```\n  def add?(object : T) : Bool\n    @hash.put(object, nil) { return true }\n    false\n  end\n\n  # Adds `#each` element of *elems* to the set and returns `self`.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.concat [5, 5, 8, 9]\n  # s.size # => 4\n  # ```\n  #\n  # See also: `#|` to merge two sets and return a new one.\n  def concat(elems) : self\n    elems.each { |elem| self << elem }\n    self\n  end\n\n  # Returns `true` if *object* exists in the set.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.includes? 5 # => true\n  # s.includes? 9 # => false\n  # ```\n  def includes?(object) : Bool\n    @hash.has_key?(object)\n  end\n\n  # Removes the *object* from the set and returns `true` if it was present, otherwise returns `false`.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.includes? 5 # => true\n  # s.delete 5    # => true\n  # s.includes? 5 # => false\n  # s.delete 5    # => false\n  # ```\n  def delete(object) : Bool\n    @hash.delete(object) { return false }\n    true\n  end\n\n  # Returns the number of elements in the set.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.size # => 2\n  # ```\n  def size : Int32\n    @hash.size\n  end\n\n  # Removes all elements in the set, and returns `self`.\n  #\n  # ```\n  # s = Set{1, 5}\n  # s.size # => 2\n  # s.clear\n  # s.size # => 0\n  # ```\n  def clear : self\n    @hash.clear\n    self\n  end\n\n  # Returns `true` if the set is empty.\n  #\n  # ```\n  # s = Set(Int32).new\n  # s.empty? # => true\n  # s << 3\n  # s.empty? # => false\n  # ```\n  def empty? : Bool\n    @hash.empty?\n  end\n\n  # Yields each element of the set, and returns `nil`.\n  def each(& : T ->) : Nil\n    @hash.each_key do |key|\n      yield key\n    end\n  end\n\n  # Returns an iterator for each element of the set.\n  def each\n    @hash.each_key\n  end\n\n  # Intersection: returns a new set containing elements common to both sets.\n  #\n  # ```\n  # Set{1, 1, 3, 5} & Set{1, 2, 3}               # => Set{1, 3}\n  # Set{'a', 'b', 'b', 'z'} & Set{'a', 'b', 'c'} # => Set{'a', 'b'}\n  # ```\n  def &(other : Set) : Set(T)\n    smallest, largest = self, other\n    if largest.size < smallest.size\n      smallest, largest = largest, smallest\n    end\n\n    set = Set(T).new\n    smallest.each do |value|\n      set.add value if largest.includes?(value)\n    end\n    set\n  end\n\n  # Union: returns a new set containing all unique elements from both sets.\n  #\n  # ```\n  # Set{1, 1, 3, 5} | Set{1, 2, 3}               # => Set{1, 3, 5, 2}\n  # Set{'a', 'b', 'b', 'z'} | Set{'a', 'b', 'c'} # => Set{'a', 'b', 'z', 'c'}\n  # ```\n  #\n  # See also: `#concat` to add elements from a set to `self`.\n  def |(other : Set(U)) : Set(T | U) forall U\n    set = Set(T | U).new(Math.max(size, other.size))\n    each { |value| set.add value }\n    other.each { |value| set.add value }\n    set\n  end\n\n  # Addition: returns a new set containing the unique elements from both sets.\n  #\n  # ```\n  # Set{1, 1, 2, 3} + Set{3, 4, 5} # => Set{1, 2, 3, 4, 5}\n  # ```\n  def +(other : Set(U)) : Set(T | U) forall U\n    self | other\n  end\n\n  # Returns the additive identity of this type.\n  #\n  # This is an empty set.\n  def self.additive_identity : self\n    new\n  end\n\n  # Difference: returns a new set containing elements in this set that are not\n  # present in the other.\n  #\n  # ```\n  # Set{1, 2, 3, 4, 5} - Set{2, 4}               # => Set{1, 3, 5}\n  # Set{'a', 'b', 'b', 'z'} - Set{'a', 'b', 'c'} # => Set{'z'}\n  # ```\n  def -(other : Set) : Set(T)\n    set = Set(T).new\n    each do |value|\n      set.add value unless other.includes?(value)\n    end\n    set\n  end\n\n  # Difference: returns a new set containing elements in this set that are not\n  # present in the other enumerable.\n  #\n  # ```\n  # Set{1, 2, 3, 4, 5} - [2, 4]               # => Set{1, 3, 5}\n  # Set{'a', 'b', 'b', 'z'} - ['a', 'b', 'c'] # => Set{'z'}\n  # ```\n  def -(other : Enumerable) : Set(T)\n    dup.subtract other\n  end\n\n  # Symmetric Difference: returns a new set `(self - other) | (other - self)`.\n  # Equivalently, returns `(self | other) - (self & other)`.\n  #\n  # ```\n  # Set{1, 2, 3, 4, 5} ^ Set{2, 4, 6}            # => Set{1, 3, 5, 6}\n  # Set{'a', 'b', 'b', 'z'} ^ Set{'a', 'b', 'c'} # => Set{'z', 'c'}\n  # ```\n  def ^(other : Set(U)) : Set(T | U) forall U\n    set = Set(T | U).new\n    each do |value|\n      set.add value unless other.includes?(value)\n    end\n    other.each do |value|\n      set.add value unless includes?(value)\n    end\n    set\n  end\n\n  # Symmetric Difference: returns a new set `(self - other) | (other - self)`.\n  # Equivalently, returns `(self | other) - (self & other)`.\n  #\n  # ```\n  # Set{1, 2, 3, 4, 5} ^ [2, 4, 6]            # => Set{1, 3, 5, 6}\n  # Set{'a', 'b', 'b', 'z'} ^ ['a', 'b', 'c'] # => Set{'z', 'c'}\n  # ```\n  def ^(other : Enumerable(U)) : Set(T | U) forall U\n    set = Set(T | U).new(self)\n    other.each do |value|\n      if includes?(value)\n        set.delete value\n      else\n        set.add value\n      end\n    end\n    set\n  end\n\n  # Returns `self` after removing from it those elements that are present in\n  # the given enumerable.\n  #\n  # ```\n  # Set{'a', 'b', 'b', 'z'}.subtract Set{'a', 'b', 'c'} # => Set{'z'}\n  # Set{1, 2, 3, 4, 5}.subtract [2, 4, 6]               # => Set{1, 3, 5}\n  # ```\n  def subtract(other : Enumerable) : self\n    other.each do |value|\n      delete value\n    end\n    self\n  end\n\n  # Returns `true` if both sets have the same elements.\n  #\n  # ```\n  # Set{1, 5} == Set{1, 5} # => true\n  # ```\n  def ==(other : Set) : Bool\n    same?(other) || @hash == other.@hash\n  end\n\n  # Same as `#includes?`.\n  #\n  # It is for convenience with using on `case` statement.\n  #\n  # ```\n  # red_like = Set{\"red\", \"pink\", \"violet\"}\n  # blue_like = Set{\"blue\", \"azure\", \"violet\"}\n  #\n  # case \"violet\"\n  # when red_like & blue_like\n  #   puts \"red & blue like color!\"\n  # when red_like\n  #   puts \"red like color!\"\n  # when blue_like\n  #   puts \"blue like color!\"\n  # end\n  # ```\n  #\n  # See also: `Object#===`.\n  def ===(object : T) : Bool\n    includes? object\n  end\n\n  # Returns a new `Set` with all of the same elements.\n  def dup : Set(T)\n    set = Set(T).new(using_hash: @hash.dup)\n    set.compare_by_identity if compare_by_identity?\n    set\n  end\n\n  # Returns a new `Set` with all of the elements cloned.\n  def clone : Set(T)\n    clone = Set(T).new(self.size)\n    clone.compare_by_identity if compare_by_identity?\n    each do |element|\n      clone << element.clone\n    end\n    clone\n  end\n\n  # Returns the elements as an `Array`.\n  #\n  # ```\n  # Set{1, 5}.to_a # => [1,5]\n  # ```\n  def to_a : Array(T)\n    @hash.keys\n  end\n\n  # Returns an `Array` with the results of running *block* against each element of the collection.\n  #\n  # ```\n  # Set{1, 2, 3, 4, 5}.to_a { |i| i // 2 } # => [0, 1, 1, 2, 2]\n  # ```\n  def to_a(& : T -> U) : Array(U) forall U\n    array = Array(U).new(size)\n    @hash.each_key do |key|\n      array << yield key\n    end\n    array\n  end\n\n  # Alias of `#to_s`.\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  def pretty_print(pp) : Nil\n    pp.list(\"Set{\", self, \"}\")\n  end\n\n  # See `Object#hash(hasher)`\n  def_hash @hash\n\n  # Returns `true` if the set and the given set have at least one element in\n  # common.\n  #\n  # ```\n  # Set{1, 2, 3}.intersects? Set{4, 5} # => false\n  # Set{1, 2, 3}.intersects? Set{3, 4} # => true\n  # ```\n  def intersects?(other : Set) : Bool\n    if size < other.size\n      any? { |o| other.includes?(o) }\n    else\n      other.any? { |o| includes?(o) }\n    end\n  end\n\n  # Writes a string representation of the set to *io*.\n  def to_s(io : IO) : Nil\n    io << \"Set{\"\n    join io, \", \", &.inspect(io)\n    io << '}'\n  end\n\n  # Returns `true` if the set is a subset of the *other* set.\n  #\n  # This set must have the same or fewer elements than the *other* set, and all\n  # of elements in this set must be present in the *other* set.\n  #\n  # ```\n  # Set{1, 5}.subset_of? Set{1, 3, 5}    # => true\n  # Set{1, 3, 5}.subset_of? Set{1, 3, 5} # => true\n  # ```\n  def subset_of?(other : Set) : Bool\n    return false if other.size < size\n    all? { |value| other.includes?(value) }\n  end\n\n  # Returns `true` if the set is a proper subset of the *other* set.\n  #\n  # This set must have fewer elements than the *other* set, and all\n  # of elements in this set must be present in the *other* set.\n  #\n  # ```\n  # Set{1, 5}.proper_subset_of? Set{1, 3, 5}    # => true\n  # Set{1, 3, 5}.proper_subset_of? Set{1, 3, 5} # => false\n  # ```\n  def proper_subset_of?(other : Set) : Bool\n    return false if other.size <= size\n    all? { |value| other.includes?(value) }\n  end\n\n  # Returns `true` if the set is a superset of the *other* set.\n  #\n  # The *other* must have the same or fewer elements than this set, and all of\n  # elements in the *other* set must be present in this set.\n  #\n  # ```\n  # Set{1, 3, 5}.superset_of? Set{1, 5}    # => true\n  # Set{1, 3, 5}.superset_of? Set{1, 3, 5} # => true\n  # ```\n  def superset_of?(other : Set) : Bool\n    other.subset_of?(self)\n  end\n\n  # Returns `true` if the set is a superset of the *other* set.\n  #\n  # The *other* must have fewer elements than this set, and all of\n  # elements in the *other* set must be present in this set.\n  #\n  # ```\n  # Set{1, 3, 5}.proper_superset_of? Set{1, 5}    # => true\n  # Set{1, 3, 5}.proper_superset_of? Set{1, 3, 5} # => false\n  # ```\n  def proper_superset_of?(other : Set) : Bool\n    other.proper_subset_of?(self)\n  end\n\n  # :nodoc:\n  def object_id : UInt64\n    @hash.object_id\n  end\n\n  # :nodoc:\n  def same?(other : Set) : Bool\n    @hash.same?(other.@hash)\n  end\n\n  # Replaces every element of the set with a value returned from the block, and\n  # returns `self`.\n  def map!(& : T -> T) : self\n    @hash.transform_keys! { |k, _| yield(k) }\n    self\n  end\n\n  # Deletes every element of the set for which the block is falsey, and returns\n  # `self`.\n  def select!(& : T ->) : self\n    @hash.select! { |k, _| yield(k) }\n    self\n  end\n\n  # Deletes every element of the set for which the block is truthy, and returns\n  # `self`.\n  def reject!(& : T ->) : self\n    @hash.reject! { |k, _| yield(k) }\n    self\n  end\n\n  # Rebuilds the set based on the current elements.\n  #\n  # When using mutable data types as elements, modifying an elements after it\n  # was inserted into the `Set` may lead to undefined behaviour. This method\n  # re-indexes the set using the current elements.\n  def rehash : Nil\n    @hash.rehash\n  end\nend\n\nmodule Enumerable\n  # Returns a new `Set` with each unique element in the enumerable.\n  def to_set : Set(T)\n    Set.new(self)\n  end\n\n  # Returns a new `Set` with the unique results of running *block* against each\n  # element of the enumerable.\n  def to_set(&block : T -> U) : Set(U) forall U\n    set = Set(U).new\n    each do |elem|\n      set << yield elem\n    end\n    set\n  end\nend\n"
  },
  {
    "path": "src/signal.cr",
    "content": "require \"crystal/system/signal\"\n\n# Safely handle inter-process signals on POSIX systems.\n#\n# Signals are dispatched to the event loop and later processed in a dedicated\n# fiber. Some received signals may never be processed when the program\n# terminates.\n#\n# ```\n# puts \"Ctrl+C still has the OS default action (stops the program)\"\n# sleep 3.seconds\n#\n# Signal::INT.trap do\n#   puts \"Gotcha!\"\n# end\n# puts \"Ctrl+C will be caught from now on\"\n# sleep 3.seconds\n#\n# Signal::INT.reset\n# puts \"Ctrl+C is back to the OS default action\"\n# sleep 3.seconds\n# ```\n#\n# WARNING: An uncaught exception in a signal handler is a fatal error.\n#\n# ## Portability\n#\n# The set of available signals is platform-dependent. Only signals that exist on\n# the target platform are available as members of this enum.\n#\n# * `ABRT`, `FPE`, `ILL`, `INT`, `SEGV`, and `TERM` are guaranteed to exist\n#   on all platforms.\n# * `PWR`, `STKFLT`, and `UNUSED` only exist on Linux.\n# * `BREAK` only exists on Windows.\n# * All other signals exist on all POSIX platforms.\n#\n# The methods `#trap`, `#reset`, and `#ignore` may not be implemented at all on\n# non-POSIX systems.\n#\n# The standard library provides several platform-agnostic APIs to achieve tasks\n# that are typically solved with signals on POSIX systems:\n#\n# * The portable API for responding to a termination request is\n#   `Process.on_terminate`.\n# * The portable API for sending a `TERM` or `KILL` signal to a process is\n#   `Process#terminate`.\n# * The portable API for retrieving the exit signal of a process\n#   (`Process::Status#exit_signal`) is `Process::Status#exit_reason`.\nenum Signal : Int32\n  # Signals required by the ISO C standard. Since every supported platform must\n  # bind against a C runtime library, these constants must be defined at all\n  # times, even when the platform does not support POSIX signals\n\n  INT  = LibC::SIGINT\n  ILL  = LibC::SIGILL\n  FPE  = LibC::SIGFPE\n  SEGV = LibC::SIGSEGV\n  TERM = LibC::SIGTERM\n  ABRT = LibC::SIGABRT\n\n  {% if flag?(:win32) %}\n    BREAK = LibC::SIGBREAK\n  {% else %}\n    HUP    = LibC::SIGHUP\n    QUIT   = LibC::SIGQUIT\n    TRAP   = LibC::SIGTRAP\n    IOT    = LibC::SIGIOT\n    KILL   = LibC::SIGKILL\n    BUS    = LibC::SIGBUS\n    SYS    = LibC::SIGSYS\n    PIPE   = LibC::SIGPIPE\n    ALRM   = LibC::SIGALRM\n    URG    = LibC::SIGURG\n    STOP   = LibC::SIGSTOP\n    TSTP   = LibC::SIGTSTP\n    CONT   = LibC::SIGCONT\n    CHLD   = LibC::SIGCHLD\n    TTIN   = LibC::SIGTTIN\n    TTOU   = LibC::SIGTTOU\n    IO     = LibC::SIGIO\n    XCPU   = LibC::SIGXCPU\n    XFSZ   = LibC::SIGXFSZ\n    VTALRM = LibC::SIGVTALRM\n    USR1   = LibC::SIGUSR1\n    USR2   = LibC::SIGUSR2\n    WINCH  = LibC::SIGWINCH\n\n    {% if flag?(:linux) %}\n      PWR    = LibC::SIGPWR\n      STKFLT = LibC::SIGSTKFLT\n      UNUSED = LibC::SIGUNUSED\n    {% end %}\n  {% end %}\n\n  # Sets the handler for this signal to the passed function.\n  #\n  # After executing this, whenever the current process receives the\n  # corresponding signal, the passed function will be called (instead of the OS\n  # default). The handler will run in a signal-safe fiber throughout the event\n  # loop; there is no limit to what functions can be called, unlike raw signals\n  # that run on the sigaltstack.\n  #\n  # Note that `CHLD` is always trapped and child processes will always be reaped\n  # before the custom handler is called, hence a custom `CHLD` handler must\n  # check child processes using `Process.exists?`. Trying to use waitpid with a\n  # zero or negative value won't work.\n  #\n  # NOTE: `Process.on_terminate` is preferred over `Signal::INT.trap` as a\n  # portable alternative which also works on Windows.\n  def trap(&handler : Signal ->) : Nil\n    {% if @type.has_constant?(\"CHLD\") %}\n      if self == CHLD\n        Crystal::System::Signal.child_handler = handler\n        return\n      end\n    {% end %}\n    Crystal::System::Signal.trap(self, handler)\n  end\n\n  # Returns any existing handler for this signal\n  #\n  # ```\n  # Signal::USR1.trap { }\n  # prev_handler = Signal::USR1.trap_handler?\n  #\n  # Signal::USR1.trap do |signal|\n  #   prev_handler.try &.call(signal)\n  #   # ...\n  # end\n  # ```\n  def trap_handler?\n    {% if @type.has_constant?(\"CHLD\") %}\n      return Crystal::System::Signal.child_handler if self == CHLD\n    {% end %}\n    Crystal::System::Signal.trap_handler?(self)\n  end\n\n  # Resets the handler for this signal to the OS default.\n  #\n  # Note that trying to reset `CHLD` will actually set the default crystal\n  # handler that monitors and reaps child processes. This prevents zombie\n  # processes and is required by `Process#wait` for example.\n  def reset : Nil\n    Crystal::System::Signal.reset(self)\n  end\n\n  # Clears the handler for this signal and prevents the OS default action.\n  #\n  # Note that trying to ignore `CHLD` will actually set the default crystal\n  # handler that monitors and reaps child processes. This prevents zombie\n  # processes and is required by `Process#wait` for example.\n  def ignore : Nil\n    Crystal::System::Signal.ignore(self)\n  end\nend\n"
  },
  {
    "path": "src/slice/sort.cr",
    "content": "struct Slice(T)\n  protected def self.intro_sort!(a, n)\n    return if n < 2\n    quick_sort_for_intro_sort!(a, n, (n.bit_length - 1) * 2)\n    insertion_sort!(a, n)\n  end\n\n  protected def self.quick_sort_for_intro_sort!(a, n, d)\n    while n > 16\n      if d == 0\n        heap_sort!(a, n)\n        return\n      end\n      d -= 1\n      center_median!(a, n)\n      c = partition_for_quick_sort!(a, n)\n      quick_sort_for_intro_sort!(c, n - (c - a), d)\n      n = c - a\n    end\n  end\n\n  protected def self.heap_sort!(a, n)\n    (n // 2).downto 0 do |p|\n      heapify!(a, p, n)\n    end\n    while n > 1\n      n -= 1\n      a.value, a[n] = a[n], a.value\n      heapify!(a, 0, n)\n    end\n  end\n\n  protected def self.heapify!(a, p, n)\n    v, c = a[p], p\n    while c < (n - 1) // 2\n      c = 2 * (c + 1)\n      c -= 1 if cmp(a[c], a[c - 1]) < 0\n      break unless cmp(v, a[c]) <= 0\n      a[p] = a[c]\n      p = c\n    end\n    if n & 1 == 0 && c == n // 2 - 1\n      c = 2 * c + 1\n      if cmp(v, a[c]) < 0\n        a[p] = a[c]\n        p = c\n      end\n    end\n    a[p] = v\n  end\n\n  protected def self.center_median!(a, n)\n    b, c = a + n // 2, a + n - 1\n    if cmp(a.value, b.value) <= 0\n      if cmp(b.value, c.value) <= 0\n        return\n      elsif cmp(a.value, c.value) <= 0\n        b.value, c.value = c.value, b.value\n      else\n        a.value, b.value, c.value = c.value, a.value, b.value\n      end\n    elsif cmp(a.value, c.value) <= 0\n      a.value, b.value = b.value, a.value\n    elsif cmp(b.value, c.value) <= 0\n      a.value, b.value, c.value = b.value, c.value, a.value\n    else\n      a.value, c.value = c.value, a.value\n    end\n  end\n\n  protected def self.partition_for_quick_sort!(a, n)\n    v, l, r = a[n // 2], a + 1, a + n - 1\n    loop do\n      while cmp(l.value, v) < 0\n        l += 1\n      end\n      r -= 1\n      while cmp(v, r.value) < 0\n        r -= 1\n      end\n      return l unless l < r\n      l.value, r.value = r.value, l.value\n      l += 1\n    end\n  end\n\n  protected def self.insertion_sort!(a, n)\n    (1...n).each do |i|\n      l = a + i\n      v = l.value\n      p = l - 1\n      while l > a && cmp(v, p.value) < 0\n        l.value = p.value\n        l, p = p, p - 1\n      end\n      l.value = v\n    end\n  end\n\n  protected def self.intro_sort!(a, n, comp)\n    return if n < 2\n    quick_sort_for_intro_sort!(a, n, (n.bit_length - 1) * 2, comp)\n    insertion_sort!(a, n, comp)\n  end\n\n  protected def self.quick_sort_for_intro_sort!(a, n, d, comp)\n    while n > 16\n      if d == 0\n        heap_sort!(a, n, comp)\n        return\n      end\n      d -= 1\n      center_median!(a, n, comp)\n      c = partition_for_quick_sort!(a, n, comp)\n      quick_sort_for_intro_sort!(c, n - (c - a), d, comp)\n      n = c - a\n    end\n  end\n\n  protected def self.heap_sort!(a, n, comp)\n    (n // 2).downto 0 do |p|\n      heapify!(a, p, n, comp)\n    end\n    while n > 1\n      n -= 1\n      a.value, a[n] = a[n], a.value\n      heapify!(a, 0, n, comp)\n    end\n  end\n\n  protected def self.heapify!(a, p, n, comp)\n    v, c = a[p], p\n    while c < (n - 1) // 2\n      c = 2 * (c + 1)\n      c -= 1 if cmp(a[c], a[c - 1], comp) < 0\n      break unless cmp(v, a[c], comp) <= 0\n      a[p] = a[c]\n      p = c\n    end\n    if n & 1 == 0 && c == n // 2 - 1\n      c = 2 * c + 1\n      if cmp(v, a[c], comp) < 0\n        a[p] = a[c]\n        p = c\n      end\n    end\n    a[p] = v\n  end\n\n  protected def self.center_median!(a, n, comp)\n    b, c = a + n // 2, a + n - 1\n    if cmp(a.value, b.value, comp) <= 0\n      if cmp(b.value, c.value, comp) <= 0\n        return\n      elsif cmp(a.value, c.value, comp) <= 0\n        b.value, c.value = c.value, b.value\n      else\n        a.value, b.value, c.value = c.value, a.value, b.value\n      end\n    elsif cmp(a.value, c.value, comp) <= 0\n      a.value, b.value = b.value, a.value\n    elsif cmp(b.value, c.value, comp) <= 0\n      a.value, b.value, c.value = b.value, c.value, a.value\n    else\n      a.value, c.value = c.value, a.value\n    end\n  end\n\n  protected def self.partition_for_quick_sort!(a, n, comp)\n    v, l, r = a[n // 2], a + 1, a + n - 1\n    loop do\n      while l < a + n && cmp(l.value, v, comp) < 0\n        l += 1\n      end\n      r -= 1\n      while r >= a && cmp(v, r.value, comp) < 0\n        r -= 1\n      end\n      return l unless l < r\n      l.value, r.value = r.value, l.value\n      l += 1\n    end\n  end\n\n  protected def self.insertion_sort!(a, n, comp)\n    (1...n).each do |i|\n      l = a + i\n      v = l.value\n      p = l - 1\n      while l > a && cmp(v, p.value, comp) < 0\n        l.value = p.value\n        l, p = p, p - 1\n      end\n      l.value = v\n    end\n  end\n\n  protected def self.cmp(v1, v2)\n    v = v1 <=> v2\n    raise ArgumentError.new(\"Comparison of #{v1} and #{v2} failed\") if v.nil?\n    v\n  end\n\n  protected def self.cmp(v1, v2, block)\n    v = block.call(v1, v2)\n    raise ArgumentError.new(\"Comparison of #{v1} and #{v2} failed\") if v.nil?\n    v\n  end\n\n  # The stable sort implementation is ported from Rust.\n  # https://github.com/rust-lang/rust/blob/507bff92fadf1f25a830da5065a5a87113345163/library/alloc/src/slice.rs\n  #\n  # Rust License (MIT):\n  #\n  # Permission is hereby granted, free of charge, to any\n  # person obtaining a copy of this software and associated\n  # documentation files (the \"Software\"), to deal in the\n  # Software without restriction, including without\n  # limitation the rights to use, copy, modify, merge,\n  # publish, distribute, sublicense, and/or sell copies of\n  # the Software, and to permit persons to whom the Software\n  # is furnished to do so, subject to the following\n  # conditions:\n  #\n  # The above copyright notice and this permission notice\n  # shall be included in all copies or substantial portions\n  # of the Software.\n  #\n  # THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n  # ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n  # TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n  # SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n  # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n  # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n  # DEALINGS IN THE SOFTWARE.\n\n  # Slices of up to this length get sorted using insertion sort.\n  private MAX_INSERTION = 10\n\n  # Very short runs are extended using insertion sort to span at least this many elements.\n  private MIN_RUN = 10\n\n  # This merge sort borrows some (but not all) ideas from TimSort, which is described in detail\n  # [here](http://svn.python.org/projects/python/trunk/Objects/listsort.txt).\n  #\n  # The algorithm identifies strictly descending and non-descending subsequences, which are called\n  # natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed\n  # onto the stack, and then some pairs of adjacent runs are merged until these two invariants are\n  # satisfied:\n  #\n  # 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len`\n  # 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len`\n  #\n  # The invariants ensure that the total running time is `O(n * log(n))` worst-case.\n  protected def self.merge_sort!(v : Slice(T)) forall T\n    size = v.size\n\n    # Short arrays get sorted in-place via insertion sort to avoid allocations.\n    if size <= MAX_INSERTION\n      if size >= 2\n        (size - 1).downto(0) { |i| insert_head!(v[i..]) }\n      end\n      return\n    end\n\n    # Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it\n    # shallow copies of the contents of `v` without risking the dtors running on copies if\n    # `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run,\n    # which will always have length at most `len / 2`.\n    buf = Pointer(T).malloc(size // 2)\n\n    # In order to identify natural runs in `v`, we traverse it backwards. That might seem like a\n    # strange decision, but consider the fact that merges more often go in the opposite direction\n    # (forwards). According to benchmarks, merging forwards is slightly faster than merging\n    # backwards. To conclude, identifying runs by traversing backwards improves performance.\n    runs = [] of Range(Int32, Int32)\n    last = size\n    while last > 0\n      # Find the next natural run, and reverse it if it's strictly descending.\n      start = last - 1\n      if start > 0\n        start -= 1\n        if cmp(v[start + 1], v[start]) < 0\n          while start > 0 && cmp(v[start], v[start - 1]) < 0\n            start -= 1\n          end\n          v[start...last].reverse!\n        else\n          while start > 0 && cmp(v[start], v[start - 1]) > 0\n            start -= 1\n          end\n        end\n      end\n\n      # Insert some more elements into the run if it's too short. Insertion sort is faster than\n      # merge sort on short sequences, so this significantly improves performance.\n      while start > 0 && last - start < MIN_RUN\n        start -= 1\n        insert_head!(v[start...last])\n      end\n\n      # Push this run onto the stack.\n      runs.push(start...last)\n      last = start\n\n      # Merge some pairs of adjacent runs to satisfy the invariants.\n      while r = collapse(runs)\n        left = runs[r + 1]\n        right = runs[r]\n        merge!(v[left.begin...right.end], left.size, buf)\n        runs[r] = left.begin...right.end\n        runs.delete_at(r + 1)\n      end\n    end\n  end\n\n  # Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.\n  #\n  # This is the integral subroutine of insertion sort.\n  protected def self.insert_head!(v)\n    if v.size >= 2 && cmp(v[1], v[0]) < 0\n      x, v[0] = v[0], v[1]\n      (2...v.size).each do |i|\n        if cmp(v[i], x) < 0\n          v[i - 1] = v[i]\n        else\n          v[i - 1] = x\n          return\n        end\n      end\n      v[v.size - 1] = x\n    end\n  end\n\n  # Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and\n  # stores the result into `v[..]`.\n  protected def self.merge!(v, mid, buf)\n    size = v.size\n\n    if mid <= size - mid\n      # The left run is shorter.\n      buf.copy_from(v.to_unsafe, mid)\n\n      left = 0\n      right = mid\n      out = v.to_unsafe\n\n      while left < mid && right < size\n        # Consume the lesser side.\n        # If equal, prefer the left run to maintain stability.\n        if cmp(v[right], buf[left]) < 0\n          out.value = v[right]\n          out += 1\n          right += 1\n        else\n          out.value = buf[left]\n          out += 1\n          left += 1\n        end\n      end\n\n      out.copy_from(buf + left, mid - left)\n    else\n      # The right run is shorter.\n      buf.copy_from((v + mid).to_unsafe, size - mid)\n\n      left = mid\n      right = size - mid\n      out = v.to_unsafe + size\n\n      while left > 0 && right > 0\n        # Consume the greater side.\n        # If equal, prefer the right run to maintain stability.\n        if cmp(buf[right - 1], v[left - 1]) < 0\n          left -= 1\n          out -= 1\n          out.value = v[left]\n        else\n          right -= 1\n          out -= 1\n          out.value = buf[right]\n        end\n      end\n\n      (v + left).copy_from(buf, right)\n    end\n  end\n\n  # This merge sort borrows some (but not all) ideas from TimSort, which is described in detail\n  # [here](http://svn.python.org/projects/python/trunk/Objects/listsort.txt).\n  #\n  # The algorithm identifies strictly descending and non-descending subsequences, which are called\n  # natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed\n  # onto the stack, and then some pairs of adjacent runs are merged until these two invariants are\n  # satisfied:\n  #\n  # 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len`\n  # 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len`\n  #\n  # The invariants ensure that the total running time is `O(n * log(n))` worst-case.\n  protected def self.merge_sort!(v : Slice(T), comp) forall T\n    size = v.size\n\n    # Short arrays get sorted in-place via insertion sort to avoid allocations.\n    if size <= MAX_INSERTION\n      if size >= 2\n        (size - 1).downto(0) { |i| insert_head!(v[i..], comp) }\n      end\n      return\n    end\n\n    # Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it\n    # shallow copies of the contents of `v` without risking the dtors running on copies if\n    # `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run,\n    # which will always have length at most `len / 2`.\n    buf = Pointer(T).malloc(size // 2)\n\n    # In order to identify natural runs in `v`, we traverse it backwards. That might seem like a\n    # strange decision, but consider the fact that merges more often go in the opposite direction\n    # (forwards). According to benchmarks, merging forwards is slightly faster than merging\n    # backwards. To conclude, identifying runs by traversing backwards improves performance.\n    runs = [] of Range(Int32, Int32)\n    last = size\n    while last > 0\n      # Find the next natural run, and reverse it if it's strictly descending.\n      start = last - 1\n      if start > 0\n        start -= 1\n        if cmp(v[start + 1], v[start], comp) < 0\n          while start > 0 && cmp(v[start], v[start - 1], comp) < 0\n            start -= 1\n          end\n          v[start...last].reverse!\n        else\n          while start > 0 && cmp(v[start], v[start - 1], comp) > 0\n            start -= 1\n          end\n        end\n      end\n\n      # Insert some more elements into the run if it's too short. Insertion sort is faster than\n      # merge sort on short sequences, so this significantly improves performance.\n      while start > 0 && last - start < MIN_RUN\n        start -= 1\n        insert_head!(v[start...last], comp)\n      end\n\n      # Push this run onto the stack.\n      runs.push(start...last)\n      last = start\n\n      # Merge some pairs of adjacent runs to satisfy the invariants.\n      while r = collapse(runs)\n        left = runs[r + 1]\n        right = runs[r]\n        merge!(v[left.begin...right.end], left.size, buf, comp)\n        runs[r] = left.begin...right.end\n        runs.delete_at(r + 1)\n      end\n    end\n  end\n\n  # Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.\n  #\n  # This is the integral subroutine of insertion sort.\n  protected def self.insert_head!(v, comp)\n    if v.size >= 2 && cmp(v[1], v[0], comp) < 0\n      x, v[0] = v[0], v[1]\n      (2...v.size).each do |i|\n        if cmp(v[i], x, comp) < 0\n          v[i - 1] = v[i]\n        else\n          v[i - 1] = x\n          return\n        end\n      end\n      v[v.size - 1] = x\n    end\n  end\n\n  # Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and\n  # stores the result into `v[..]`.\n  protected def self.merge!(v, mid, buf, comp)\n    size = v.size\n\n    if mid <= size - mid\n      # The left run is shorter.\n      buf.copy_from(v.to_unsafe, mid)\n\n      left = 0\n      right = mid\n      out = v.to_unsafe\n\n      while left < mid && right < size\n        # Consume the lesser side.\n        # If equal, prefer the left run to maintain stability.\n        if cmp(v[right], buf[left], comp) < 0\n          out.value = v[right]\n          out += 1\n          right += 1\n        else\n          out.value = buf[left]\n          out += 1\n          left += 1\n        end\n      end\n\n      out.copy_from(buf + left, mid - left)\n    else\n      # The right run is shorter.\n      buf.copy_from((v + mid).to_unsafe, size - mid)\n\n      left = mid\n      right = size - mid\n      out = v.to_unsafe + size\n\n      while left > 0 && right > 0\n        # Consume the greater side.\n        # If equal, prefer the right run to maintain stability.\n        if cmp(buf[right - 1], v[left - 1], comp) < 0\n          left -= 1\n          out -= 1\n          out.value = v[left]\n        else\n          right -= 1\n          out -= 1\n          out.value = buf[right]\n        end\n      end\n\n      (v + left).copy_from(buf, right)\n    end\n  end\n\n  # Examines the stack of runs and identifies the next pair of runs to merge. More specifically,\n  # if `r` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the\n  # algorithm should continue building a new run instead, `nil` is returned.\n  #\n  # TimSort is infamous for its buggy implementations, as described here:\n  # http://envisage-project.eu/timsort-specification-and-verification/\n  #\n  # The gist of the story is: we must enforce the invariants on the top four runs on the stack.\n  # Enforcing them on just top three is not sufficient to ensure that the invariants will still\n  # hold for *all* runs in the stack.\n  #\n  # This function correctly checks invariants for the top four runs. Additionally, if the top\n  # run starts at index 0, it will always demand a merge operation until the stack is fully\n  # collapsed, in order to complete the sort.\n  @[AlwaysInline]\n  protected def self.collapse(runs)\n    n = runs.size\n    if n >= 2 &&\n       (runs[n - 1].begin == 0 ||\n       runs[n - 2].size <= runs[n - 1].size ||\n       (n >= 3 && runs[n - 3].size <= runs[n - 2].size + runs[n - 1].size) ||\n       (n >= 4 && runs[n - 4].size <= runs[n - 3].size + runs[n - 2].size))\n      n >= 3 && runs[n - 3].size < runs[n - 1].size ? n - 3 : n - 2\n    end\n  end\nend\n"
  },
  {
    "path": "src/slice.cr",
    "content": "require \"c/string\"\nrequire \"slice/sort\"\n\n# A `Slice` is a `Pointer` with an associated size.\n#\n# While a pointer is unsafe because no bound checks are performed when reading from and writing to it,\n# reading from and writing to a slice involve bound checks.\n# In this way, a slice is a safe alternative to `Pointer`.\n#\n# A Slice can be created as read-only: trying to write to it\n# will raise. For example the slice of bytes returned by\n# `String#to_slice` is read-only.\nstruct Slice(T)\n  include Indexable::Mutable(T)\n  include Comparable(Slice)\n\n  # Creates a new `Slice` with the given *args*. The type of the\n  # slice will be the union of the type of the given *args*.\n  #\n  # The slice is allocated on the heap.\n  #\n  # ```\n  # slice = Slice[1, 'a']\n  # slice[0]    # => 1\n  # slice[1]    # => 'a'\n  # slice.class # => Slice(Char | Int32)\n  # ```\n  #\n  # If `T` is a `Number` then this is equivalent to\n  # `Number.slice` (numbers will be coerced to the type `T`)\n  #\n  # * `Number.slice` is a convenient alternative for designating a\n  #   specific numerical item type.\n  macro [](*args, read_only = false)\n    # TODO: there should be a better way to check this, probably\n    # asking if @type was instantiated or if T is defined\n    {% if @type.name != \"Slice(T)\" && T < ::Number %}\n      {{T}}.slice({{args.splat(\", \")}}read_only: {{read_only}})\n    {% else %}\n      %ptr = ::Pointer(typeof({{args.splat}})).malloc({{args.size}})\n      {% for arg, i in args %}\n        %ptr[{{i}}] = {{arg}}\n      {% end %}\n      ::Slice.new(%ptr, {{args.size}}, read_only: {{read_only}})\n    {% end %}\n  end\n\n  # Returns the size of this slice.\n  #\n  # ```\n  # Slice(UInt8).new(3).size # => 3\n  # ```\n  getter size : Int32\n\n  # Returns `true` if this slice cannot be written to.\n  getter? read_only : Bool\n\n  # Creates a slice to the given *pointer*, bounded by the given *size*. This\n  # method does not allocate heap memory.\n  #\n  # ```\n  # ptr = Pointer.malloc(9) { |i| ('a'.ord + i).to_u8 }\n  #\n  # slice = Slice.new(ptr, 3)\n  # slice.size # => 3\n  # slice      # => Bytes[97, 98, 99]\n  #\n  # String.new(slice) # => \"abc\"\n  # ```\n  def initialize(@pointer : Pointer(T), size : Int, *, @read_only = false)\n    @size = size.to_i32\n  end\n\n  # Allocates `size * sizeof(T)` bytes of heap memory initialized to zero\n  # and returns a slice pointing to that memory.\n  #\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # Only works for primitive integers and floats (`UInt8`, `Int32`, `Float64`, etc.)\n  #\n  # ```\n  # slice = Slice(UInt8).new(3)\n  # slice # => Bytes[0, 0, 0]\n  # ```\n  def self.new(size : Int, *, read_only = false)\n    {% unless Number::Primitive.union_types.includes?(T) %}\n      {% raise \"Can only use primitive integers and floats with Slice.new(size), not #{T}\" %}\n    {% end %}\n\n    pointer = Pointer(T).malloc(size)\n    new(pointer, size, read_only: read_only)\n  end\n\n  # Allocates `size * sizeof(T)` bytes of heap memory initialized to the value\n  # returned by the block (which is invoked once with each index in the range `0...size`)\n  # and returns a slice pointing to that memory.\n  #\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # ```\n  # slice = Slice.new(3) { |i| i + 10 }\n  # slice # => Slice[10, 11, 12]\n  # ```\n  def self.new(size : Int, *, read_only = false, &)\n    pointer = Pointer.malloc(size) { |i| yield i }\n    new(pointer, size, read_only: read_only)\n  end\n\n  # Allocates `size * sizeof(T)` bytes of heap memory initialized to *value*\n  # and returns a slice pointing to that memory.\n  #\n  # The memory is allocated by the `GC`, so when there are\n  # no pointers to this memory, it will be automatically freed.\n  #\n  # ```\n  # slice = Slice.new(3, 10)\n  # slice # => Slice[10, 10, 10]\n  # ```\n  def self.new(size : Int, value : T, *, read_only = false)\n    pointer = Pointer(T).malloc(size, value)\n    new(pointer, size, read_only: read_only)\n  end\n\n  # Returns a deep copy of this slice.\n  #\n  # This method allocates memory for the slice copy and stores the return values\n  # from calling `#clone` on each item.\n  def clone\n    pointer = Pointer(T).malloc(size)\n    copy = self.class.new(pointer, size)\n    each_with_index do |item, i|\n      copy[i] = item.clone\n    end\n    copy\n  end\n\n  # Returns a shallow copy of this slice.\n  #\n  # This method allocates memory for the slice copy and duplicates the values.\n  def dup\n    pointer = Pointer(T).malloc(size)\n    copy = self.class.new(pointer, size)\n    copy.copy_from(self)\n    copy\n  end\n\n  # Creates an empty slice.\n  #\n  # ```\n  # slice = Slice(UInt8).empty\n  # slice.size # => 0\n  # ```\n  def self.empty : self\n    new(Pointer(T).null, 0)\n  end\n\n  # Returns a new slice that is *offset* elements apart from this slice.\n  #\n  # ```\n  # slice = Slice.new(5) { |i| i + 10 }\n  # slice # => Slice[10, 11, 12, 13, 14]\n  #\n  # slice2 = slice + 2\n  # slice2 # => Slice[12, 13, 14]\n  # ```\n  def +(offset : Int) : Slice(T)\n    check_size(offset)\n\n    Slice.new(@pointer + offset, @size - offset, read_only: @read_only)\n  end\n\n  # Returns a new slice that has `self`'s elements followed by *other*'s\n  # elements.\n  #\n  # ```\n  # Slice[1, 2] + Slice[3, 4, 5]          # => Slice[1, 2, 3, 4, 5]\n  # Slice[1, 2, 3] + Slice['a', 'b', 'c'] # => Slice[1, 2, 3, 'a', 'b', 'c']\n  # ```\n  #\n  # See also: `Slice.join` to join multiple slices at once without creating\n  # intermediate results.\n  def +(other : Slice) : Slice\n    Slice.join({self, other})\n  end\n\n  # Returns a new slice that has the elements from *slices* joined together.\n  #\n  # ```\n  # Slice.join([Slice[1, 2], Slice[3, 4, 5]])        # => Slice[1, 2, 3, 4, 5]\n  # Slice.join({Slice[1], Slice['a'], Slice[\"xyz\"]}) # => Slice[1, 'a', \"xyz\"]\n  # ```\n  #\n  # See also: `#+(other : Slice)`.\n  def self.join(slices : Indexable(Slice)) : Slice\n    total_size = slices.sum(&.size)\n    buf = Pointer(typeof(Enumerable.element_type Enumerable.element_type slices)).malloc(total_size)\n\n    ptr = buf\n    slices.each do |slice|\n      slice.to_unsafe.copy_to(ptr, slice.size)\n      ptr += slice.size\n    end\n\n    Slice.new(buf, total_size)\n  end\n\n  # Returns the additive identity of this type.\n  #\n  # This is an empty slice.\n  def self.additive_identity : self\n    self.new(0)\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  @[AlwaysInline]\n  def []=(index : Int, value : T) : T\n    check_writable\n    super\n  end\n\n  # Returns a new slice that starts at *start* elements from this slice's start,\n  # and of exactly *count* size.\n  #\n  # Negative *start* is added to `#size`, thus it's treated as index counting\n  # from the end of the array, `-1` designating the last element.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  # Returns `nil` if the new slice falls outside this slice.\n  #\n  # ```\n  # slice = Slice.new(5) { |i| i + 10 }\n  # slice # => Slice[10, 11, 12, 13, 14]\n  #\n  # slice[1, 3]?   # => Slice[11, 12, 13]\n  # slice[1, 33]?  # => nil\n  # slice[-3, 2]?  # => Slice[12, 13]\n  # slice[-3, 10]? # => nil\n  # ```\n  def []?(start : Int, count : Int) : Slice(T)?\n    # we skip the calculated count because the subslice must contain exactly\n    # *count* elements\n    start, _ = Indexable.normalize_start_and_count(start, count, size) { return }\n    return unless count <= @size - start\n\n    Slice.new(@pointer + start, count, read_only: @read_only)\n  end\n\n  # Returns a new slice that starts at *start* elements from this slice's start,\n  # and of exactly *count* size.\n  #\n  # Negative *start* is added to `#size`, thus it's treated as index counting\n  # from the end of the array, `-1` designating the last element.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  # Raises `IndexError` if the new slice falls outside this slice.\n  #\n  # ```\n  # slice = Slice.new(5) { |i| i + 10 }\n  # slice # => Slice[10, 11, 12, 13, 14]\n  #\n  # slice[1, 3]   # => Slice[11, 12, 13]\n  # slice[1, 33]  # raises IndexError\n  # slice[-3, 2]  # => Slice[12, 13]\n  # slice[-3, 10] # raises IndexError\n  # ```\n  def [](start : Int, count : Int) : Slice(T)\n    self[start, count]? || raise IndexError.new\n  end\n\n  # Returns a new slice with the elements in the given range.\n  #\n  # Negative indices count backward from the end of the slice (`-1` is the last\n  # element). Additionally, an empty slice is returned when the starting index\n  # for an element range is at the end of the slice.\n  #\n  # Returns `nil` if the new slice falls outside this slice.\n  #\n  # ```\n  # slice = Slice.new(5) { |i| i + 10 }\n  # slice # => Slice[10, 11, 12, 13, 14]\n  #\n  # slice[1..3]?  # => Slice[11, 12, 13]\n  # slice[1..33]? # => nil\n  # ```\n  def []?(range : Range)\n    start, count = Indexable.range_to_index_and_count(range, size) || return nil\n    self[start, count]?\n  end\n\n  # Returns a new slice with the elements in the given range.\n  #\n  # The first element in the returned slice is `self[range.begin]` followed\n  # by the next elements up to index `range.end` (or `self[range.end - 1]` if\n  # the range is exclusive).\n  # If there are fewer elements in `self`, the returned slice is shorter than\n  # `range.size`.\n  #\n  # ```\n  # a = Slice[\"a\", \"b\", \"c\", \"d\", \"e\"]\n  # a[1..3] # => Slice[\"b\", \"c\", \"d\"]\n  # ```\n  #\n  # Negative indices count backward from the end of the slice (`-1` is the last\n  # element). Additionally, an empty slice is returned when the starting index\n  # for an element range is at the end of the slice.\n  #\n  # Raises `IndexError` if the new slice falls outside this slice.\n  #\n  # ```\n  # slice = Slice.new(5) { |i| i + 10 }\n  # slice # => Slice[10, 11, 12, 13, 14]\n  #\n  # slice[1..3]  # => Slice[11, 12, 13]\n  # slice[1..33] # raises IndexError\n  # ```\n  def [](range : Range) : Slice(T)\n    start, count = Indexable.range_to_index_and_count(range, size) || raise IndexError.new\n    self[start, count]\n  end\n\n  @[AlwaysInline]\n  def unsafe_fetch(index : Int) : T\n    @pointer[index]\n  end\n\n  @[AlwaysInline]\n  def unsafe_put(index : Int, value : T)\n    @pointer[index] = value\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def update(index : Int, & : T -> _) : T\n    check_writable\n    super { |elem| yield elem }\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def swap(index0 : Int, index1 : Int) : self\n    check_writable\n    super\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def reverse! : self\n    check_writable\n    super\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def shuffle!(random : Random? = nil) : self\n    check_writable\n    super\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def rotate!(n : Int = 1) : self\n    check_writable\n\n    return self if size == 0\n    n %= size\n\n    if n == 0\n    elsif n == 1\n      tmp = self[0]\n      @pointer.move_from(@pointer + n, size - n)\n      self[-1] = tmp\n    elsif n == (size - 1)\n      tmp = self[-1]\n      (@pointer + size - n).move_from(@pointer, n)\n      self[0] = tmp\n    elsif n <= SMALL_SLICE_SIZE\n      tmp_buffer = uninitialized T[SMALL_SLICE_SIZE]\n      tmp_buffer.to_unsafe.copy_from(@pointer, n)\n      @pointer.move_from(@pointer + n, size - n)\n      (@pointer + size - n).copy_from(tmp_buffer.to_unsafe, n)\n    elsif size - n <= SMALL_SLICE_SIZE\n      tmp_buffer = uninitialized T[SMALL_SLICE_SIZE]\n      tmp_buffer.to_unsafe.copy_from(@pointer + n, size - n)\n      (@pointer + size - n).move_from(@pointer, n)\n      @pointer.copy_from(tmp_buffer.to_unsafe, size - n)\n    elsif n <= size // 2\n      tmp = self[...n].dup\n      @pointer.move_from(@pointer + n, size - n)\n      (@pointer + size - n).copy_from(tmp.to_unsafe, n)\n    else\n      tmp = self[n..].dup\n      (@pointer + size - n).move_from(@pointer, n)\n      @pointer.copy_from(tmp.to_unsafe, size - n)\n    end\n    self\n  end\n\n  private SMALL_SLICE_SIZE = 16 # same as Array::SMALL_ARRAY_SIZE\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def map!(& : T -> _) : self\n    check_writable\n    super { |elem| yield elem }\n  end\n\n  # Returns a new slice where elements are mapped by the given block.\n  #\n  # ```\n  # slice = Slice[1, 2.5, \"a\"]\n  # slice.map &.to_s # => Slice[\"1\", \"2.5\", \"a\"]\n  # ```\n  def map(*, read_only = false, & : T -> _)\n    Slice.new(size, read_only: read_only) { |i| yield @pointer[i] }\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def map_with_index!(offset = 0, & : T, Int32 -> _) : self\n    check_writable\n    super { |elem, i| yield elem, i }\n  end\n\n  # Like `map`, but the block gets passed both the element and its index.\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there.\n  def map_with_index(offset = 0, *, read_only = false, & : (T, Int32) -> _)\n    Slice.new(size, read_only: read_only) { |i| yield @pointer[i], offset + i }\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def fill(value : T) : self\n    check_writable\n\n    to_unsafe.fill(size, value)\n\n    self\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def fill(value : T, start : Int, count : Int) : self\n    # since `#[]` requires exactly *count* elements but we allow fewer here, we\n    # must normalize the indices beforehand\n    start, count = normalize_start_and_count(start, count)\n    self[start, count].fill(value)\n    self\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def fill(value : T, range : Range) : self\n    fill(value, *Indexable.range_to_index_and_count(range, size) || raise IndexError.new)\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def fill(*, offset : Int = 0, & : Int32 -> T) : self\n    check_writable\n    super { |i| yield i }\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def fill(start : Int, count : Int, & : Int32 -> T) : self\n    check_writable\n    super(start, count) { |i| yield i }\n  end\n\n  # :inherit:\n  #\n  # Raises if this slice is read-only.\n  def fill(range : Range, & : Int32 -> T) : self\n    check_writable\n    super(range) { |i| yield i }\n  end\n\n  def copy_from(source : Pointer(T), count) : Nil\n    check_writable\n    check_size(count)\n\n    @pointer.copy_from(source, count)\n  end\n\n  def copy_to(target : Pointer(T), count) : Nil\n    check_size(count)\n\n    @pointer.copy_to(target, count)\n  end\n\n  # Copies the contents of this slice into *target*.\n  #\n  # Raises `IndexError` if the destination slice cannot fit the data being transferred\n  # e.g. `dest.size < self.size`.\n  #\n  # ```\n  # src = Slice['a', 'a', 'a']\n  # dst = Slice['b', 'b', 'b', 'b', 'b']\n  # src.copy_to dst\n  # dst             # => Slice['a', 'a', 'a', 'b', 'b']\n  # dst.copy_to src # raises IndexError\n  # ```\n  def copy_to(target : self) : Nil\n    target.check_writable\n    raise IndexError.new if target.size < size\n\n    @pointer.copy_to(target.to_unsafe, size)\n  end\n\n  # Copies the contents of *source* into this slice.\n  #\n  # Raises `IndexError` if the destination slice cannot fit the data being transferred.\n  @[AlwaysInline]\n  def copy_from(source : self) : Nil\n    source.copy_to(self)\n  end\n\n  def move_from(source : Pointer(T), count) : Nil\n    check_writable\n    check_size(count)\n\n    @pointer.move_from(source, count)\n  end\n\n  def move_to(target : Pointer(T), count) : Nil\n    @pointer.move_to(target, count)\n  end\n\n  # Moves the contents of this slice into *target*. *target* and `self` may\n  # overlap; the copy is always done in a non-destructive manner.\n  #\n  # Raises `IndexError` if the destination slice cannot fit the data being transferred\n  # e.g. `dest.size < self.size`.\n  #\n  # ```\n  # src = Slice['a', 'a', 'a']\n  # dst = Slice['b', 'b', 'b', 'b', 'b']\n  # src.move_to dst\n  # dst             # => Slice['a', 'a', 'a', 'b', 'b']\n  # dst.move_to src # raises IndexError\n  # ```\n  #\n  # See also: `Pointer#move_to`.\n  def move_to(target : self) : Nil\n    target.check_writable\n    raise IndexError.new if target.size < size\n\n    @pointer.move_to(target.to_unsafe, size)\n  end\n\n  # Moves the contents of *source* into this slice. *source* and `self` may\n  # overlap; the copy is always done in a non-destructive manner.\n  #\n  # Raises `IndexError` if the destination slice cannot fit the data being transferred.\n  @[AlwaysInline]\n  def move_from(source : self) : Nil\n    source.move_to(self)\n  end\n\n  def inspect(io : IO) : Nil\n    to_s(io)\n  end\n\n  # Returns a new `Slice` pointing at the same contents as `self`, but\n  # reinterpreted as elements of the given *type*.\n  #\n  # The returned slice never refers to more memory than `self`; if the last\n  # bytes of `self` do not fit into a `U`, they are excluded from the returned\n  # slice.\n  #\n  # WARNING: This method is **unsafe**: elements are reinterpreted using\n  # `#unsafe_as`, and the resulting slice may not be properly aligned.\n  # Additionally, the same elements may produce different results depending on\n  # the system endianness.\n  #\n  # ```\n  # # assume little-endian system\n  # bytes = Bytes[0x01, 0x02, 0x03, 0x04, 0xFF, 0xFE]\n  # bytes.unsafe_slice_of(Int8)  # => Slice[1_i8, 2_i8, 3_i8, 4_i8, -1_i8, -2_i8]\n  # bytes.unsafe_slice_of(Int16) # => Slice[513_i16, 1027_i16, -257_i16]\n  # bytes.unsafe_slice_of(Int32) # => Slice[0x04030201]\n  # ```\n  def unsafe_slice_of(type : U.class) : Slice(U) forall U\n    Slice.new(to_unsafe.unsafe_as(Pointer(U)), bytesize // sizeof(U), read_only: @read_only)\n  end\n\n  # Returns a new `Bytes` pointing at the same contents as `self`.\n  #\n  # WARNING: This method is **unsafe**: the returned slice is writable if `self`\n  # is also writable, and modifications through the returned slice may violate\n  # the binary representations of Crystal objects. Additionally, the same\n  # elements may produce different results depending on the system endianness.\n  #\n  # ```\n  # # assume little-endian system\n  # ints = Slice[0x01020304, 0x05060708]\n  # bytes = ints.to_unsafe_bytes # => Bytes[0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05]\n  # bytes[2] = 0xAD\n  # ints # => Slice[0x01AD0304, 0x05060708]\n  # ```\n  def to_unsafe_bytes : Bytes\n    unsafe_slice_of(UInt8)\n  end\n\n  # Returns a hexstring representation of this slice.\n  #\n  # `self` must be a `Slice(UInt8)`. To call this method on other `Slice`s,\n  # `#to_unsafe_bytes` should be used first.\n  #\n  # ```\n  # UInt8.slice(97, 62, 63, 8, 255).hexstring # => \"613e3f08ff\"\n  #\n  # # assume little-endian system\n  # Int16.slice(97, 62, 1000, -2).to_unsafe_bytes.hexstring # => \"61003e00e803feff\"\n  # ```\n  def hexstring : String\n    {% unless T == UInt8 %}\n      {% raise \"Can only call `#hexstring` on Slice(UInt8), not #{@type}\" %}\n    {% end %}\n\n    str_size = size * 2\n    String.new(str_size) do |buffer|\n      hexstring(buffer)\n      {str_size, str_size}\n    end\n  end\n\n  # :nodoc:\n  def hexstring(buffer) : Nil\n    {% unless T == UInt8 %}\n      {% raise \"Can only call `#hexstring` on Slice(UInt8), not #{@type}\" %}\n    {% end %}\n\n    offset = 0\n    each do |v|\n      buffer[offset] = to_hex(v >> 4)\n      buffer[offset + 1] = to_hex(v & 0x0f)\n      offset += 2\n    end\n\n    nil\n  end\n\n  # Returns a hexdump of this slice.\n  #\n  # `self` must be a `Slice(UInt8)`. To call this method on other `Slice`s,\n  # `#to_unsafe_bytes` should be used first.\n  #\n  # This method is specially useful for debugging binary data and\n  # incoming/outgoing data in protocols.\n  #\n  # ```\n  # slice = UInt8.slice(97, 62, 63, 8, 255)\n  # slice.hexdump # => \"00000000  61 3e 3f 08 ff                                    a>?..\\n\"\n  #\n  # # assume little-endian system\n  # slice = Int16.slice(97, 62, 1000, -2)\n  # slice.to_unsafe_bytes.hexdump # => \"00000000  61 00 3e 00 e8 03 fe ff                           a.>.....\\n\"\n  # ```\n  def hexdump : String\n    {% unless T == UInt8 %}\n      {% raise \"Can only call `#hexdump` on Slice(UInt8), not #{@type}\" %}\n    {% end %}\n\n    return \"\" if empty?\n\n    full_lines, leftover = size.divmod(16)\n    if leftover == 0\n      str_size = full_lines * 77\n    else\n      str_size = (full_lines + 1) * 77 - (16 - leftover)\n    end\n\n    String.new(str_size) do |buf|\n      pos = 0\n      offset = 0\n\n      while pos < size\n        # Ensure we don't write outside the buffer:\n        # slower, but safer (speed is not very important when hexdump is used)\n        hexdump_line(Slice.new(buf + offset, {77, str_size - offset}.min), pos)\n        pos += 16\n        offset += 77\n      end\n\n      {str_size, str_size}\n    end\n  end\n\n  # Writes a hexdump of this slice to the given *io*.\n  #\n  # `self` must be a `Slice(UInt8)`. To call this method on other `Slice`s,\n  # `#to_unsafe_bytes` should be used first.\n  #\n  # This method is specially useful for debugging binary data and\n  # incoming/outgoing data in protocols.\n  #\n  # Returns the number of bytes written to *io*.\n  #\n  # ```\n  # slice = UInt8.slice(97, 62, 63, 8, 255)\n  # slice.hexdump(STDOUT)\n  # ```\n  #\n  # Prints:\n  #\n  # ```text\n  # 00000000  61 3e 3f 08 ff                                    a>?..\n  # ```\n  def hexdump(io : IO)\n    {% unless T == UInt8 %}\n      {% raise \"Can only call `#hexdump` on Slice(UInt8), not #{@type}\" %}\n    {% end %}\n\n    return 0 if empty?\n\n    line = uninitialized UInt8[77]\n    line_slice = line.to_slice\n    count = 0\n\n    pos = 0\n    while pos < size\n      line_bytes = hexdump_line(line_slice, pos)\n      io.write_string(line_slice[0, line_bytes])\n      count += line_bytes\n      pos += 16\n    end\n\n    io.flush\n    count\n  end\n\n  private def hexdump_line(line, start_pos)\n    hex_offset = 10\n    ascii_offset = 60\n\n    0.upto(7) do |j|\n      line[7 - j] = to_hex((start_pos >> (4 * j)) & 0xf)\n    end\n    line[8] = 0x20_u8\n    line[9] = 0x20_u8\n\n    pos = start_pos\n    16.times do |i|\n      break if pos >= size\n      v = unsafe_fetch(pos)\n      pos += 1\n\n      line[hex_offset] = to_hex(v >> 4)\n      line[hex_offset + 1] = to_hex(v & 0x0f)\n      line[hex_offset + 2] = 0x20_u8\n      hex_offset += 3\n\n      if i == 7\n        line[hex_offset] = 0x20_u8\n        hex_offset += 1\n      end\n\n      line[ascii_offset] = 0x20_u8 <= v <= 0x7e_u8 ? v : 0x2e_u8\n      ascii_offset += 1\n    end\n\n    while hex_offset < 60\n      line[hex_offset] = 0x20_u8\n      hex_offset += 1\n    end\n\n    if ascii_offset < line.size\n      line[ascii_offset] = 0x0a_u8\n      ascii_offset += 1\n    end\n\n    ascii_offset\n  end\n\n  private def to_hex(c)\n    ((c < 10 ? 48_u8 : 87_u8) + c)\n  end\n\n  def bytesize : Int32\n    sizeof(T) * size\n  end\n\n  # Combined comparison operator.\n  #\n  # Returns a negative number, `0`, or a positive number depending on\n  # whether `self` is less than *other*, equals *other*.\n  #\n  # It compares the elements of both slices in the same position using the\n  # `<=>` operator. As soon as one of such comparisons returns a non-zero\n  # value, that result is the return value of the comparison.\n  #\n  # If all elements are equal, the comparison is based on the size of the arrays.\n  #\n  # ```\n  # Bytes[8] <=> Bytes[1, 2, 3] # => 7\n  # Bytes[2] <=> Bytes[4, 2, 3] # => -2\n  # Bytes[1, 2] <=> Bytes[1, 2] # => 0\n  # ```\n  def <=>(other : Slice(U)) forall U\n    # If both slices are identical references, we can skip the memory comparison.\n    return 0 if same?(other)\n\n    min_size = Math.min(size, other.size)\n    {% if T == UInt8 && U == UInt8 %}\n      cmp = to_unsafe.memcmp(other.to_unsafe, min_size)\n      return cmp if cmp != 0\n    {% else %}\n      0.upto(min_size - 1) do |i|\n        n = to_unsafe[i] <=> other.to_unsafe[i]\n        return n if n != 0\n      end\n    {% end %}\n    size <=> other.size\n  end\n\n  # Returns `true` if `self` and *other* have the same size and all their\n  # elements are equal, `false` otherwise.\n  #\n  # ```\n  # Bytes[1, 2] == Bytes[1, 2]    # => true\n  # Bytes[1, 3] == Bytes[1, 2]    # => false\n  # Bytes[1, 2] == Bytes[1, 2, 3] # => false\n  # ```\n  def ==(other : Slice(U)) : Bool forall U\n    # If both slices are of different sizes, they cannot be equal.\n    return false if size != other.size\n\n    # If both slices are identical references, we can skip the memory comparison.\n    # Not using `same?` here because we have already compared sizes.\n    return true if to_unsafe == other.to_unsafe\n\n    {% if T == UInt8 && U == UInt8 %}\n      to_unsafe.memcmp(other.to_unsafe, size) == 0\n    {% else %}\n      each_with_index do |elem, i|\n        return false unless elem == other.to_unsafe[i]\n      end\n      true\n    {% end %}\n  end\n\n  # Returns `true` if `self` and *other* point to the same memory, i.e. pointer\n  # and size are identical.\n  #\n  # ```\n  # slice = Slice[1, 2, 3]\n  # slice.same?(slice)           # => true\n  # slice == Slice[1, 2, 3]      # => false\n  # slice.same?(slice + 1)       # => false\n  # (slice + 1).same?(slice + 1) # => true\n  # slice.same?(slice[0, 2])     # => false\n  # ```\n  def same?(other : self) : Bool\n    to_unsafe == other.to_unsafe && size == other.size\n  end\n\n  def to_slice : self\n    self\n  end\n\n  def to_s(io : IO) : Nil\n    if T == UInt8\n      io << \"Bytes[\"\n      # Inspect using to_s because we know this is a UInt8.\n      join io, \", \", &.to_s(io)\n      io << ']'\n    else\n      io << \"Slice[\"\n      join io, \", \", &.inspect(io)\n      io << ']'\n    end\n  end\n\n  def pretty_print(pp) : Nil\n    prefix = T == UInt8 ? \"Bytes[\" : \"Slice[\"\n    pp.list(prefix, self, \"]\")\n  end\n\n  def to_a\n    Array(T).build(@size) do |pointer|\n      pointer.copy_from(@pointer, @size)\n      @size\n    end\n  end\n\n  # Returns this slice's pointer.\n  #\n  # ```\n  # slice = Slice.new(3, 10)\n  # slice.to_unsafe[0] # => 10\n  # ```\n  def to_unsafe : Pointer(T)\n    @pointer\n  end\n\n  # Returns a new instance with all elements sorted based on the return value of\n  # their comparison method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # a = Slice[3, 1, 2]\n  # a.sort # => Slice[1, 2, 3]\n  # a      # => Slice[3, 1, 2]\n  # ```\n  #\n  # See `#sort!` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def sort : self\n    dup.sort!\n  end\n\n  # Returns a new instance with all elements sorted based on the return value of\n  # their comparison method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm.\n  #\n  # ```\n  # a = Slice[3, 1, 2]\n  # a.unstable_sort # => Slice[1, 2, 3]\n  # a               # => Slice[3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#unstable_sort!` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def unstable_sort : self\n    dup.unstable_sort!\n  end\n\n  # Returns a new instance with all elements sorted based on the comparator in the\n  # given block, using a stable sort algorithm.\n  #\n  # ```\n  # a = Slice[3, 1, 2]\n  # b = a.sort { |a, b| b <=> a }\n  #\n  # b # => Slice[3, 2, 1]\n  # a # => Slice[3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#sort!(&block : T, T -> U)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def sort(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#sort_by`?\" %}\n    {% end %}\n\n    dup.sort! &block\n  end\n\n  # Returns a new instance with all elements sorted based on the comparator in the\n  # given block, using an unstable sort algorithm.\n  #\n  # ```\n  # a = Slice[3, 1, 2]\n  # b = a.unstable_sort { |a, b| b <=> a }\n  #\n  # b # => Slice[3, 2, 1]\n  # a # => Slice[3, 1, 2]\n  # ```\n  #\n  # See `Indexable::Mutable#unstable_sort!(&block : T, T -> U)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def unstable_sort(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#unstable_sort_by`?\" %}\n    {% end %}\n\n    dup.unstable_sort!(&block)\n  end\n\n  # Sorts all elements in `self` based on the return value of the comparison\n  # method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # slice = Slice[3, 1, 2]\n  # slice.sort!\n  # slice # => Slice[1, 2, 3]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#sort` for a non-modifying option\n  # that allocates a new instance.\n  #\n  # The sort mechanism is implemented as [*merge sort*](https://en.wikipedia.org/wiki/Merge_sort).\n  # It is stable, which is typically a good default.\n  #\n  # Stability means that two elements which compare equal (i.e. `a <=> b == 0`)\n  # keep their original relation. Stable sort guarantees that `[a, b].sort!`\n  # always results in `[a, b]` (given they compare equal). With unstable sort,\n  # the result could also be `[b, a]`.\n  #\n  # If stability is expendable, `#unstable_sort!` provides a performance\n  # advantage over stable sort. As an optimization, if `T` is any primitive\n  # integer type, `Char`, any enum type, any `Pointer` instance, `Symbol`, or\n  # `Time::Span`, then an unstable sort is automatically used.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def sort! : self\n    # If two values `x, y : T` have the same binary representation whenever they\n    # compare equal, i.e. `x <=> y == 0` implies\n    # `pointerof(x).memcmp(pointerof(y), 1) == 0`, then swapping the two values\n    # is a no-op and therefore a stable sort isn't required\n    {% if T.union_types.size == 1 && (T <= Int::Primitive || T <= Char || T <= Enum || T <= Pointer || T <= Symbol || T <= Time::Span) %}\n      unstable_sort!\n    {% else %}\n      Slice.merge_sort!(self)\n\n      self\n    {% end %}\n  end\n\n  # Sorts all elements in `self` based on the return value of the comparison\n  # method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm..\n  #\n  # ```\n  # slice = Slice[3, 1, 2]\n  # slice.unstable_sort!\n  # slice # => Slice[1, 2, 3]\n  # ```\n  #\n  # This sort operation modifies `self`. See `#unstable_sort` for a non-modifying\n  # option that allocates a new instance.\n  #\n  # The sort mechanism is implemented as [*introsort*](https://en.wikipedia.org/wiki/Introsort).\n  # It does not guarantee stability between equally comparing elements.\n  # This offers higher performance but may be unexpected in some situations.\n  #\n  # Stability means that two elements which compare equal (i.e. `a <=> b == 0`)\n  # keep their original relation. Stable sort guarantees that `[a, b].sort!`\n  # always results in `[a, b]` (given they compare equal). With unstable sort,\n  # the result could also be `[b, a]`.\n  #\n  # If stability is necessary, use  `#sort!` instead.\n  #\n  # Raises `ArgumentError` if the comparison between any two elements returns `nil`.\n  def unstable_sort! : self\n    Slice.intro_sort!(to_unsafe, size)\n\n    self\n  end\n\n  # Sorts all elements in `self` based on the comparator in the given block, using\n  # a stable sort algorithm.\n  #\n  # ```\n  # slice = Slice[3, 1, 2]\n  # # This is a reverse sort (forward sort would be `a <=> b`)\n  # slice.sort! { |a, b| b <=> a }\n  # slice # => Slice[3, 2, 1]\n  # ```\n  #\n  # The block must implement a comparison between two elements *a* and *b*,\n  # where `a < b` outputs a negative value, `a == b` outputs `0`, and `a > b`\n  # outputs a positive value.\n  # The comparison operator (`Comparable#<=>`) can be used for this.\n  #\n  # The block's output type must be `<= Int32?`, but returning an actual `nil`\n  # value is an error.\n  #\n  # This sort operation modifies `self`. See `#sort(&block : T, T -> U)` for a\n  # non-modifying option that allocates a new instance.\n  #\n  # The sort mechanism is implemented as [*merge sort*](https://en.wikipedia.org/wiki/Merge_sort).\n  # It is stable, which is typically a good default.\n  #\n  # Stability means that two elements which compare equal (i.e. `a <=> b == 0`)\n  # keep their original relation. Stable sort guarantees that `[a, b].sort!`\n  # always results in `[a, b]` (given they compare equal). With unstable sort,\n  # the result could also be `[b, a]`.\n  #\n  # If stability is expendable, `#unstable_sort!(&block : T, T -> U)` provides a\n  # performance advantage over stable sort.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def sort!(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#sort_by!`?\" %}\n    {% end %}\n\n    Slice.merge_sort!(self, block)\n\n    self\n  end\n\n  # Sorts all elements in `self` based on the comparator in the given block,\n  # using an unstable sort algorithm.\n  #\n  # ```\n  # slice = Slice[3, 1, 2]\n  # # This is a reverse sort (forward sort would be `a <=> b`)\n  # slice.unstable_sort! { |a, b| b <=> a }\n  # slice # => Slice[3, 2, 1]\n  # ```\n  #\n  # The block must implement a comparison between two elements *a* and *b*,\n  # where `a < b` outputs a negative value, `a == b` outputs `0`, and `a > b`\n  # outputs a positive value.\n  # The comparison operator (`Comparable#<=>`) can be used for this.\n  #\n  # The block's output type must be `<= Int32?`, but returning an actual `nil`\n  # value is an error.\n  #\n  # This sort operation modifies `self`. See `#unstable_sort(&block : T, T -> U)`\n  # for a non-modifying option that allocates a new instance.\n  #\n  # The sort mechanism is implemented as [*introsort*](https://en.wikipedia.org/wiki/Introsort).\n  # It does not guarantee stability between equally comparing elements.\n  # This offers higher performance but may be unexpected in some situations.\n  #\n  # Stability means that two elements which compare equal (i.e. `a <=> b == 0`)\n  # keep their original relation. Stable sort guarantees that `[a, b].sort!`\n  # always results in `[a, b]` (given they compare equal). With unstable sort,\n  # the result could also be `[b, a]`.\n  #\n  # If stability is necessary, use  `#sort!(&block : T, T -> U)` instead.\n  #\n  # Raises `ArgumentError` if for any two elements the block returns `nil`.\n  def unstable_sort!(&block : T, T -> U) : self forall U\n    {% unless U <= Int32? %}\n      {% raise \"Expected block to return Int32 or Nil, not #{U}.\\nThe block is supposed to be a custom comparison operation, compatible with `Comparable#<=>`.\\nDid you mean to use `#unstable_sort_by!`?\" %}\n    {% end %}\n\n    Slice.intro_sort!(to_unsafe, size, block)\n\n    self\n  end\n\n  # Returns a new instance with all elements sorted by the output value of the\n  # block. The output values are compared via the comparison method `T#<=>`\n  # (see `Comparable#<=>`), using a stable sort algorithm.\n  #\n  # ```\n  # a = Slice[\"apple\", \"pear\", \"fig\"]\n  # b = a.sort_by { |word| word.size }\n  # b # => Slice[\"fig\", \"pear\", \"apple\"]\n  # a # => Slice[\"apple\", \"pear\", \"fig\"]\n  # ```\n  #\n  # If stability is expendable, `#unstable_sort_by(&block : T -> _)` provides a\n  # performance advantage over stable sort.\n  #\n  # See `Indexable::Mutable#sort_by!(&block : T -> _)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`.\n  def sort_by(&block : T -> _) : self\n    dup.sort_by! { |e| yield(e) }\n  end\n\n  # Returns a new instance with all elements sorted by the output value of the\n  # block. The output values are compared via the comparison method `#<=>`\n  # (see `Comparable#<=>`), using an unstable sort algorithm.\n  #\n  # ```\n  # a = Slice[\"apple\", \"pear\", \"fig\"]\n  # b = a.unstable_sort_by { |word| word.size }\n  # b # => Slice[\"fig\", \"pear\", \"apple\"]\n  # a # => Slice[\"apple\", \"pear\", \"fig\"]\n  # ```\n  #\n  # If stability is necessary, use `#sort_by(&block : T -> _)` instead.\n  #\n  # See `Indexable::Mutable#unstable_sort!(&block : T -> _)` for details on the sorting mechanism.\n  #\n  # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`.\n  def unstable_sort_by(&block : T -> _) : self\n    dup.unstable_sort_by! { |e| yield(e) }\n  end\n\n  # Modifies `self` by sorting all elements. The given block is called for\n  # each element, then the comparison method `<=>` is called on the object\n  # returned from the block to determine sort order.\n  #\n  # ```\n  # a = Slice[\"apple\", \"pear\", \"fig\"]\n  # a.sort_by! { |word| word.size }\n  # a # => Slice[\"fig\", \"pear\", \"apple\"]\n  # ```\n  def sort_by!(&block : T -> _) : Slice(T)\n    sorted = map { |e| {e, yield(e)} }.sort! { |x, y| x[1] <=> y[1] }\n    size.times do |i|\n      to_unsafe[i] = sorted.to_unsafe[i][0]\n    end\n    self\n  end\n\n  # :ditto:\n  #\n  # This method does not guarantee stability between equally sorting elements.\n  # Which results in a performance advantage over stable sort.\n  def unstable_sort_by!(&block : T -> _) : Slice(T)\n    sorted = map { |e| {e, yield(e)} }.unstable_sort! { |x, y| x[1] <=> y[1] }\n    size.times do |i|\n      to_unsafe[i] = sorted.to_unsafe[i][0]\n    end\n    self\n  end\n\n  def index(object, offset : Int = 0)\n    # Optimize for the case of looking for a byte in a byte slice\n    if T.is_a?(UInt8.class) &&\n       (object.is_a?(UInt8) || (object.is_a?(Int) && 0 <= object < 256))\n      return fast_index(object, offset)\n    end\n\n    super\n  end\n\n  # :nodoc:\n  def fast_index(object, offset) : Int32?\n    offset = check_index_out_of_bounds(offset) { return nil }\n    result = LibC.memchr(to_unsafe + offset, object, size - offset)\n    if result\n      return (result - to_unsafe.as(Void*)).to_i32\n    end\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    {% if T == UInt8 %}\n      hasher.bytes(self)\n    {% else %}\n      super hasher\n    {% end %}\n  end\n\n  protected def check_writable\n    raise \"Can't write to read-only Slice\" if @read_only\n  end\n\n  private def check_size(count : Int)\n    unless 0 <= count <= size\n      raise IndexError.new\n    end\n  end\nend\n\n# A convenient alias for the most common slice type,\n# a slice of bytes, used for example in `IO#read` and `IO#write`.\nalias Bytes = Slice(UInt8)\n"
  },
  {
    "path": "src/socket/address.cr",
    "content": "require \"./common\"\nrequire \"uri\"\n\nclass Socket\n  abstract struct Address\n    getter family : Family\n    getter size : Int32\n\n    # Returns either an `IPAddress` or `UNIXAddress` from the internal OS\n    # representation. Only INET, INET6 and UNIX families are supported.\n    def self.from(sockaddr : LibC::Sockaddr*, addrlen) : Address\n      case family = Family.new(sockaddr.value.sa_family)\n      when Family::INET6\n        IPAddress.new(sockaddr.as(LibC::SockaddrIn6*), addrlen.to_i)\n      when Family::INET\n        IPAddress.new(sockaddr.as(LibC::SockaddrIn*), addrlen.to_i)\n      when Family::UNIX\n        UNIXAddress.new(sockaddr.as(LibC::SockaddrUn*), addrlen.to_i)\n      else\n        raise \"Unsupported family type: #{family} (#{family.value})\"\n      end\n    end\n\n    # :ditto:\n    def self.from(sockaddr : LibC::Sockaddr*) : Address\n      case family = Family.new(sockaddr.value.sa_family)\n      when Family::INET6\n        sockaddr = sockaddr.as(LibC::SockaddrIn6*)\n\n        IPAddress.new(sockaddr, sizeof(typeof(sockaddr)))\n      when Family::INET\n        sockaddr = sockaddr.as(LibC::SockaddrIn*)\n\n        IPAddress.new(sockaddr, sizeof(typeof(sockaddr)))\n      when Family::UNIX\n        sockaddr = sockaddr.as(LibC::SockaddrUn*)\n\n        UNIXAddress.new(sockaddr, sizeof(typeof(sockaddr)))\n      else\n        raise \"Unsupported family type: #{family} (#{family.value})\"\n      end\n    end\n\n    # Parses a `Socket::Address` from an URI.\n    #\n    # Supported formats:\n    # * `ip://<host>:<port>`\n    # * `tcp://<host>:<port>`\n    # * `udp://<host>:<port>`\n    # * `unix://<path>`\n    #\n    # See `IPAddress.parse` and `UNIXAddress.parse` for details.\n    def self.parse(uri : URI) : self\n      case uri.scheme\n      when \"ip\", \"tcp\", \"udp\"\n        IPAddress.parse uri\n      when \"unix\"\n        UNIXAddress.parse uri\n      else\n        raise Socket::Error.new \"Unsupported address type: #{uri.scheme}\"\n      end\n    end\n\n    # :ditto:\n    def self.parse(uri : String) : self\n      parse URI.parse(uri)\n    end\n\n    def initialize(@family : Family, @size : Int32)\n    end\n\n    abstract def to_unsafe : LibC::Sockaddr*\n  end\n\n  # IP address representation.\n  #\n  # Holds a binary representation of an IP address, either translated from a\n  # `String`, or directly received from an opened connection (e.g.\n  # `Socket#local_address`, `Socket#receive`).\n  #\n  # `IPAddress` won't resolve domains, including `localhost`. If you must\n  # resolve an IP, or don't know whether a `String` contains an IP or a domain\n  # name, you should use `Addrinfo.resolve` instead.\n  struct IPAddress < Address\n    UNSPECIFIED  = \"0.0.0.0\"\n    UNSPECIFIED6 = \"::\"\n    LOOPBACK     = \"127.0.0.1\"\n    LOOPBACK6    = \"::1\"\n    BROADCAST    = \"255.255.255.255\"\n    BROADCAST6   = \"ff0X::1\"\n\n    getter port : Int32\n    getter zone_id : Int32\n\n    @addr : LibC::In6Addr | LibC::InAddr\n\n    # Creates an `IPAddress` from the given IPv4 or IPv6 *address* and *port*\n    # number.\n    #\n    # *address* is parsed using `.parse_v4_fields?` and `.parse_v6_fields?`.\n    # Raises `Socket::Error` if *address* does not contain a valid IP address or\n    # the port number is out of range.\n    #\n    # Scoped/Zoned IPv6 link-local addresses are supported per RFC4007, e.g.\n    # `fe80::abcd%eth0` but will always use their numerical interface index\n    # in the `#inspect` representation. The interface name can be retrieved later\n    # using `#link_local_interface` on the `IPAddress` object.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # Socket::IPAddress.new(\"127.0.0.1\", 8080)                 # => Socket::IPAddress(127.0.0.1:8080)\n    # Socket::IPAddress.new(\"fe80::2ab2:bdff:fe59:8e2c\", 1234) # => Socket::IPAddress([fe80::2ab2:bdff:fe59:8e2c]:1234)\n    # Socket::IPAddress.new(\"fe80::4567:8:9%eth0\", 443)        # => Socket::IPAddress([fe80::4567:8:9%2]:443)\n    # ```\n    def self.new(address : String, port : Int32)\n      raise Error.new(\"Invalid port number: #{port}\") unless IPAddress.valid_port?(port)\n\n      if v4_fields = parse_v4_fields?(address)\n        addr = v4(v4_fields, port.to_u16!)\n      else\n        v6_fields_tpl = parse_v6_fields?(address.to_slice)\n        raise Error.new(\"Invalid IP address: #{address}\") if v6_fields_tpl.nil?\n        v6_fields, zone_slice = v6_fields_tpl\n        zone_id = 0\n        unless zone_slice.nil?\n          # `zone_id` is only relevant for link-local addresses, i.e. beginning with \"fe80:\".\n          if v6_fields[0] != 0xfe80\n            raise Error.new(\"Zoned/scoped IPv6 addresses are only allowed for link-local (supplied '#{address}' is not within fe80::/10)\")\n          end\n          # Scope/Zone can be given either as a network interface name or directly as the interface index.\n          # When given a name we need to find the corresponding interface index.\n          zone = String.new(zone_slice)\n          if zone_id = zone.to_i?\n            raise Error.new(\"Invalid IPv6 link-local zone index '#{zone}' in address '#{address}'\") unless zone_id.positive?\n          else\n            zone_id = LibC.if_nametoindex(zone).to_i\n            raise Error.new(\"IPv6 link-local zone interface '#{zone}' not found (in address '#{address}')\") unless zone_id.positive?\n          end\n        end\n        addr = v6(v6_fields, port.to_u16!, zone_id)\n      end\n\n      addr\n    end\n\n    # Creates an `IPAddress` from the internal OS representation. Supports both\n    # INET and INET6 families.\n    def self.from(sockaddr : LibC::Sockaddr*, addrlen) : IPAddress\n      case family = Family.new(sockaddr.value.sa_family)\n      when Family::INET6\n        new(sockaddr.as(LibC::SockaddrIn6*), addrlen.to_i)\n      when Family::INET\n        new(sockaddr.as(LibC::SockaddrIn*), addrlen.to_i)\n      else\n        raise \"Unsupported family type: #{family} (#{family.value})\"\n      end\n    end\n\n    # :ditto:\n    def self.from(sockaddr : LibC::Sockaddr*) : IPAddress\n      case family = Family.new(sockaddr.value.sa_family)\n      when Family::INET6\n        sockaddr = sockaddr.as(LibC::SockaddrIn6*)\n\n        new(sockaddr, sizeof(typeof(sockaddr)))\n      when Family::INET\n        sockaddr = sockaddr.as(LibC::SockaddrIn*)\n\n        new(sockaddr, sizeof(typeof(sockaddr)))\n      else\n        raise \"Unsupported family type: #{family} (#{family.value})\"\n      end\n    end\n\n    # Parses a `Socket::IPAddress` from an URI.\n    #\n    # It expects the URI to include `<scheme>://<host>:<port>` where `scheme` as\n    # well as any additional URI components (such as `path` or `query`) are ignored.\n    #\n    # `host` must be an IP address (v4 or v6), otherwise `Socket::Error` will be\n    # raised. Domain names will not be resolved.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # Socket::IPAddress.parse(\"tcp://127.0.0.1:8080\") # => Socket::IPAddress.new(\"127.0.0.1\", 8080)\n    # Socket::IPAddress.parse(\"udp://[::1]:8080\")     # => Socket::IPAddress.new(\"::1\", 8080)\n    # ```\n    def self.parse(uri : URI) : IPAddress\n      host = uri.host.presence\n      raise Socket::Error.new(\"Invalid IP address: missing host\") unless host\n\n      port = uri.port\n      raise Socket::Error.new(\"Invalid IP address: missing port\") unless port\n\n      # remove ipv6 brackets\n      if host.starts_with?('[') && host.ends_with?(']')\n        host = host.byte_slice(1, host.bytesize - 2)\n      end\n\n      new(host, port)\n    end\n\n    # :ditto:\n    def self.parse(uri : String) : self\n      parse URI.parse(uri)\n    end\n\n    # Parses *str* as an IPv4 address and returns its fields, or returns `nil`\n    # if *str* does not contain a valid IPv4 address.\n    #\n    # The format of IPv4 addresses follows\n    # [RFC 3493, section 6.3](https://datatracker.ietf.org/doc/html/rfc3493#section-6.3).\n    # No extensions (e.g. octal fields, fewer than 4 fields) are supported.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # Socket::IPAddress.parse_v4_fields?(\"192.168.0.1\")     # => UInt8.static_array(192, 168, 0, 1)\n    # Socket::IPAddress.parse_v4_fields?(\"255.255.255.254\") # => UInt8.static_array(255, 255, 255, 254)\n    # Socket::IPAddress.parse_v4_fields?(\"01.2.3.4\")        # => nil\n    # ```\n    def self.parse_v4_fields?(str : String) : UInt8[4]?\n      parse_v4_fields?(str.to_slice)\n    end\n\n    private def self.parse_v4_fields?(bytes : Bytes)\n      # port of https://git.musl-libc.org/cgit/musl/tree/src/network/inet_pton.c?id=7e13e5ae69a243b90b90d2f4b79b2a150f806335\n      fields = StaticArray(UInt8, 4).new(0)\n      ptr = bytes.to_unsafe\n      finish = ptr + bytes.size\n\n      4.times do |i|\n        decimal = 0_u32\n        old_ptr = ptr\n\n        3.times do\n          break unless ptr < finish\n          ch = ptr.value &- 0x30\n          break unless ch <= 0x09\n          decimal = decimal &* 10 &+ ch\n          ptr += 1\n        end\n\n        return nil if ptr == old_ptr                             # no decimal\n        return nil if ptr - old_ptr > 1 && old_ptr.value === '0' # octal etc.\n        return nil if decimal > 0xFF                             # overflow\n\n        fields[i] = decimal.to_u8!\n\n        break if i == 3\n        return nil unless ptr < finish && ptr.value === '.'\n        ptr += 1\n      end\n\n      fields if ptr == finish\n    end\n\n    # Parses *str* as an IPv6 address and returns its fields, or returns `nil`\n    # if *str* does not contain a valid IPv6 address.\n    #\n    # The format of IPv6 addresses follows\n    # [RFC 4291, section 2.2](https://datatracker.ietf.org/doc/html/rfc4291#section-2.2).\n    # Both canonical and non-canonical forms are supported.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # Socket::IPAddress.parse_v6_fields?(\"::1\")                 # => UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1)\n    # Socket::IPAddress.parse_v6_fields?(\"a:0b:00c:000d:E:F::\") # => UInt16.static_array(10, 11, 12, 13, 14, 15, 0, 0)\n    # Socket::IPAddress.parse_v6_fields?(\"::ffff:192.168.1.1\")  # => UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0101)\n    # Socket::IPAddress.parse_v6_fields?(\"1::2::\")              # => nil\n    # Socket::IPAddress.parse_v6_fields?(\"fe80::a:b%eth0\")      # => StaticArray[65152, 0, 0, 0, 0, 0, 10, 11]\n    # ```\n    def self.parse_v6_fields?(str : String) : UInt16[8]?\n      parse_v6_fields?(str.to_slice).try &.[0]\n    end\n\n    # This private method additionally supports IPv6 scoped addresses and\n    # returns its fields plus the zone/scope subslice (if present).\n    # Invalid addresses will immediately return just `nil` while correctly\n    # formatted addresses return a tuple.\n    #\n    # The format of IPv6 scoped addresses follows\n    # [RFC 4007, section 11](https://datatracker.ietf.org/doc/html/rfc4007#section-11).\n    private def self.parse_v6_fields?(bytes : Bytes) : Tuple(UInt16[8], Slice(UInt8)?)?\n      # port of https://git.musl-libc.org/cgit/musl/tree/src/network/inet_pton.c?id=7e13e5ae69a243b90b90d2f4b79b2a150f806335\n      ptr = bytes.to_unsafe\n      finish = ptr + bytes.size\n\n      if ptr < finish && ptr.value === ':'\n        ptr += 1\n        return nil unless ptr < finish && ptr.value === ':'\n      end\n\n      fields = StaticArray(UInt16, 8).new(0)\n      brk = -1\n      need_v4 = false\n      zone_slice = nil\n\n      i = 0\n      while true\n        if ptr < finish && ptr.value === ':' && brk < 0\n          brk = i\n          fields[i] = 0\n          ptr += 1\n          break if ptr == finish\n          return nil if i == 7\n          i &+= 1\n          next\n        end\n\n        field = 0_u16\n        old_ptr = ptr\n\n        4.times do\n          break unless ptr < finish\n          ch = from_hex(ptr.value)\n          break unless ch <= 0x0F\n          field = field.unsafe_shl(4) | ch\n          ptr += 1\n        end\n\n        return nil if ptr == old_ptr # no field\n\n        fields[i] = field\n        break if ptr == finish && (brk >= 0 || i == 7)\n        return nil if i == 7\n\n        unless ptr < finish && ptr.value === ':'\n          if (ptr < finish && ptr.value === '%')\n            zone_slice = Bytes.new(ptr + 1, finish - ptr - 1)\n            break\n          end\n          return nil if !(ptr < finish && ptr.value === '.') || (i < 6 && brk < 0)\n          need_v4 = true\n          i &+= 1\n          fields[i] = 0\n          ptr = old_ptr\n          break\n        end\n\n        ptr += 1\n        i &+= 1\n      end\n\n      if brk >= 0\n        fields_brk = fields.to_unsafe + brk\n        (fields_brk + 7 - i).move_from(fields_brk, i &+ 1 &- brk)\n        fields_brk.clear(7 &- i)\n      end\n\n      if need_v4\n        x0, x1, x2, x3 = parse_v4_fields?(Slice.new(ptr, finish - ptr)) || return nil\n        fields[6] = x0.to_u16! << 8 | x1\n        fields[7] = x2.to_u16! << 8 | x3\n      end\n\n      {fields, zone_slice}\n    end\n\n    private def self.from_hex(ch : UInt8)\n      if 0x30 <= ch <= 0x39\n        ch &- 0x30\n      elsif 0x41 <= ch <= 0x46\n        ch &- 0x37\n      elsif 0x61 <= ch <= 0x66\n        ch &- 0x57\n      else\n        0xFF_u8\n      end\n    end\n\n    # Returns the IPv4 address with the given address *fields* and *port*\n    # number.\n    def self.v4(fields : UInt8[4], port : UInt16) : self\n      addr_value = UInt32.zero\n      fields.each_with_index do |field, i|\n        addr_value = (addr_value << 8) | field\n      end\n\n      addr = LibC::SockaddrIn.new(\n        sin_family: LibC::AF_INET,\n        sin_port: endian_swap(port),\n        sin_addr: LibC::InAddr.new(s_addr: endian_swap(addr_value)),\n      )\n      new(pointerof(addr), sizeof(typeof(addr)))\n    end\n\n    # Returns the IPv4 address `x0.x1.x2.x3:port`.\n    #\n    # Raises `Socket::Error` if any field or the port number is out of range.\n    def self.v4(x0 : Int, x1 : Int, x2 : Int, x3 : Int, *, port : Int) : self\n      fields = StaticArray[x0, x1, x2, x3].map { |field| to_v4_field(field) }\n      port = valid_port?(port) ? port.to_u16! : raise Error.new(\"Invalid port number: #{port}\")\n      v4(fields, port)\n    end\n\n    private def self.to_v4_field(field)\n      0 <= field <= 0xff ? field.to_u8! : raise Error.new(\"Invalid IPv4 field: #{field}\")\n    end\n\n    # Returns the IPv6 address with the given address *fields*, *port* number\n    # and scope identifier.\n    def self.v6(fields : UInt16[8], port : UInt16, zone_id : Int32 = 0) : self\n      fields.map! { |field| endian_swap(field) }\n      addr = LibC::SockaddrIn6.new(\n        sin6_family: LibC::AF_INET6,\n        sin6_port: endian_swap(port),\n        sin6_addr: ipv6_from_addr16(fields),\n        sin6_scope_id: zone_id,\n      )\n      new(pointerof(addr), sizeof(typeof(addr)))\n    end\n\n    # Returns the IPv6 address `[x0:x1:x2:x3:x4:x5:x6:x7]:port`.\n    #\n    # Raises `Socket::Error` if any field or the port number is out of range.\n    def self.v6(x0 : Int, x1 : Int, x2 : Int, x3 : Int, x4 : Int, x5 : Int, x6 : Int, x7 : Int, *, port : Int, zone_id : Int32 = 0) : self\n      fields = StaticArray[x0, x1, x2, x3, x4, x5, x6, x7].map { |field| to_v6_field(field) }\n      port = valid_port?(port) ? port.to_u16! : raise Error.new(\"Invalid port number: #{port}\")\n      v6(fields, port, zone_id)\n    end\n\n    private def self.to_v6_field(field)\n      0 <= field <= 0xffff ? field.to_u16! : raise Error.new(\"Invalid IPv6 field: #{field}\")\n    end\n\n    # Returns the IPv4-mapped IPv6 address with the given IPv4 address *fields*\n    # and *port* number.\n    def self.v4_mapped_v6(fields : UInt8[4], port : UInt16) : self\n      v6_fields = StaticArray[\n        0_u16, 0_u16, 0_u16, 0_u16, 0_u16, 0xffff_u16,\n        fields[0].to_u16! << 8 | fields[1],\n        fields[2].to_u16! << 8 | fields[3],\n      ]\n      v6(v6_fields, port)\n    end\n\n    # Returns the IPv4-mapped IPv6 address `[::ffff:x0.x1.x2.x3]:port`.\n    #\n    # Raises `Socket::Error` if any field or the port number is out of range.\n    def self.v4_mapped_v6(x0 : Int, x1 : Int, x2 : Int, x3 : Int, *, port : Int) : self\n      v4_fields = StaticArray[x0, x1, x2, x3].map { |field| to_v4_field(field) }\n      port = valid_port?(port) ? port.to_u16! : raise Error.new(\"Invalid port number: #{port}\")\n      v4_mapped_v6(v4_fields, port)\n    end\n\n    private def self.ipv6_from_addr16(bytes : UInt16[8])\n      addr = LibC::In6Addr.new\n      {% if flag?(:darwin) || flag?(:bsd) %}\n        addr.__u6_addr.__u6_addr16 = bytes\n      {% elsif flag?(:linux) && flag?(:musl) %}\n        addr.__in6_union.__s6_addr16 = bytes\n      {% elsif flag?(:wasm32) %}\n        bytes.each_with_index do |byte, i|\n          addr.s6_addr[2 * i] = byte.to_u8!\n          addr.s6_addr[2 * i + 1] = (byte >> 8).to_u8!\n        end\n      {% elsif flag?(:linux) %}\n        addr.__in6_u.__u6_addr16 = bytes\n      {% elsif flag?(:solaris) %}\n        addr._S6_un._S6_u16 = bytes\n      {% elsif flag?(:win32) %}\n        addr.u.word = bytes\n      {% else %}\n        {% raise \"Unsupported platform\" %}\n      {% end %}\n      addr\n    end\n\n    protected def initialize(sockaddr : LibC::SockaddrIn6*, @size)\n      @family = Family::INET6\n      @addr = sockaddr.value.sin6_addr\n      @zone_id = sockaddr.value.sin6_scope_id.to_i\n      @port = IPAddress.endian_swap(sockaddr.value.sin6_port).to_i\n    end\n\n    protected def initialize(sockaddr : LibC::SockaddrIn*, @size)\n      @family = Family::INET\n      @addr = sockaddr.value.sin_addr\n      @zone_id = 0\n      @port = IPAddress.endian_swap(sockaddr.value.sin_port).to_i\n    end\n\n    # Returns `true` if *address* is a valid IPv4 or IPv6 address.\n    def self.valid?(address : String) : Bool\n      valid_v4?(address) || valid_v6?(address)\n    end\n\n    # Returns `true` if *address* is a valid IPv6 address.\n    def self.valid_v6?(address : String) : Bool\n      !parse_v6_fields?(address).nil?\n    end\n\n    # Returns `true` if *address* is a valid IPv4 address.\n    def self.valid_v4?(address : String) : Bool\n      !parse_v4_fields?(address).nil?\n    end\n\n    # Returns a `String` representation of the IP address, without the port\n    # number.\n    #\n    # IPv6 addresses are canonicalized according to\n    # [RFC 5952, section 4](https://datatracker.ietf.org/doc/html/rfc5952#section-4).\n    # IPv4-mapped IPv6 addresses use the mixed notation according to RFC 5952,\n    # section 5.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # v4 = Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 8080)\n    # v4.address # => \"127.0.0.1\"\n    #\n    # v6 = Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1), 443)\n    # v6.address # => \"2001:db8::1:0:0:1\"\n    #\n    # mapped = Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 1, 15), 55001)\n    # mapped.address # => \"::ffff:192.168.1.15\"\n    # ```\n    #\n    # To obtain both the address and the port number in one string, see `#to_s`.\n    def address : String\n      case addr = @addr\n      in LibC::InAddr\n        String.build(IPV4_MAX_SIZE) do |io|\n          address_to_s(io, addr)\n        end\n      in LibC::In6Addr\n        String.build(IPV6_MAX_SIZE) do |io|\n          address_to_s(io, addr)\n        end\n      end\n    end\n\n    private IPV4_MAX_SIZE = 15 # \"255.255.255.255\".size\n\n    # NOTE: INET6_ADDRSTRLEN is 46 bytes (including the terminating null\n    # character), but it is only attainable for mixed-notation addresses that\n    # use all 24 hexadecimal digits in the IPv6 field part, which we do not\n    # support\n    private IPV6_MAX_SIZE = 39 # \"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\".size\n\n    # Returns `true` if this IP is a loopback address.\n    #\n    # In the IPv4 family, loopback addresses are all addresses in the subnet\n    # `127.0.0.0/24`. In IPv6 `::1` is the loopback address.\n    def loopback? : Bool\n      case addr = @addr\n      in LibC::InAddr\n        addr.s_addr & 0x000000ff_u32 == 0x0000007f_u32\n      in LibC::In6Addr\n        addr8 = ipv6_addr8(addr)\n        num = addr8.unsafe_as(UInt128)\n        # TODO: Use UInt128 literals\n        num == (1_u128 << 120) ||                         # \"::1\"\n          num & UInt128::MAX >> 24 == 0x7fffff_u128 << 80 # \"::ffff:127.0.0.1/104\"\n      end\n    end\n\n    # Returns `true` if this IP is an unspecified address, either the IPv4 address `0.0.0.0` or the IPv6 address `::`.\n    def unspecified? : Bool\n      case addr = @addr\n      in LibC::InAddr\n        addr.s_addr == 0_u32\n      in LibC::In6Addr\n        ipv6_addr8(addr) == StaticArray[0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8]\n      end\n    end\n\n    # Returns `true` if this IP is a private address.\n    #\n    # IPv4 addresses in `10.0.0.0/8`, `172.16.0.0/12` and `192.168.0.0/16` as defined in [RFC 1918](https://tools.ietf.org/html/rfc1918)\n    # and IPv6 Unique Local Addresses in `fc00::/7` as defined in [RFC 4193](https://tools.ietf.org/html/rfc4193) are considered private.\n    def private? : Bool\n      case addr = @addr\n      in LibC::InAddr\n        addr.s_addr & 0x000000ff_u32 == 0x00000000a_u32 ||     # 10.0.0.0/8\n          addr.s_addr & 0x000000f0ff_u32 == 0x0000010ac_u32 || # 172.16.0.0/12\n          addr.s_addr & 0x000000ffff_u32 == 0x0000a8c0_u32     # 192.168.0.0/16\n      in LibC::In6Addr\n        ipv6_addr8(addr)[0] & 0xfe_u8 == 0xfc_u8\n      end\n    end\n\n    # Returns `true` if this IP is a link-local address.\n    #\n    # IPv4 addresses in `169.254.0.0/16` reserved by [RFC 3927](https://www.rfc-editor.org/rfc/rfc3927) and Link-Local IPv6\n    # Unicast Addresses in `fe80::/10` reserved by [RFC 4291](https://tools.ietf.org/html/rfc4291) are considered link-local.\n    def link_local?\n      case addr = @addr\n      in LibC::InAddr\n        addr.s_addr & 0x000000ffff_u32 == 0x0000fea9_u32 # 169.254.0.0/16\n      in LibC::In6Addr\n        ipv6_addr8(addr).unsafe_as(UInt128) & 0xc0ff_u128 == 0x80fe_u128\n      end\n    end\n\n    private def ipv6_addr8(addr : LibC::In6Addr)\n      {% if flag?(:darwin) || flag?(:bsd) %}\n        addr.__u6_addr.__u6_addr8\n      {% elsif flag?(:linux) && flag?(:musl) %}\n        addr.__in6_union.__s6_addr\n      {% elsif flag?(:wasm32) %}\n        addr.s6_addr\n      {% elsif flag?(:linux) %}\n        addr.__in6_u.__u6_addr8\n      {% elsif flag?(:solaris) %}\n        addr._S6_un._S6_u8\n      {% elsif flag?(:win32) %}\n        addr.u.byte\n      {% else %}\n        {% raise \"Unsupported platform\" %}\n      {% end %}\n    end\n\n    def_equals_and_hash family, port, address_value\n\n    protected def address_value\n      case addr = @addr\n      in LibC::InAddr\n        addr.s_addr\n      in LibC::In6Addr\n        ipv6_addr8(addr).unsafe_as(UInt128)\n      end\n    end\n\n    # Writes the `String` representation of the IP address plus the port number\n    # to the given *io*.\n    #\n    # IPv6 addresses are canonicalized according to\n    # [RFC 5952, section 4](https://datatracker.ietf.org/doc/html/rfc5952#section-4),\n    # and surrounded within a pair of square brackets according to\n    # [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).\n    # IPv4-mapped IPv6 addresses use the mixed notation according to RFC 5952,\n    # section 5.\n    #\n    # To obtain the address alone without the port number, see `#address`.\n    def to_s(io : IO) : Nil\n      case addr = @addr\n      in LibC::InAddr\n        address_to_s(io, addr)\n        io << ':' << port\n      in LibC::In6Addr\n        io << '['\n        address_to_s(io, addr)\n        io << '%' << @zone_id if @zone_id.positive?\n        io << ']' << ':' << port\n      end\n    end\n\n    private def address_to_s(io : IO, addr : LibC::InAddr)\n      io << (addr.s_addr & 0xFF)\n      io << '.' << (addr.s_addr >> 8 & 0xFF)\n      io << '.' << (addr.s_addr >> 16 & 0xFF)\n      io << '.' << (addr.s_addr >> 24)\n    end\n\n    private def address_to_s(io : IO, addr : LibC::In6Addr)\n      bytes = ipv6_addr8(addr)\n      if Slice.new(bytes.to_unsafe, 10).all?(&.zero?) && bytes[10] == 0xFF && bytes[11] == 0xFF\n        io << \"::ffff:\" << bytes[12] << '.' << bytes[13] << '.' << bytes[14] << '.' << bytes[15]\n      else\n        fields = bytes.unsafe_as(StaticArray(UInt16, 8)).map! { |field| IPAddress.endian_swap(field) }\n\n        zeros_start = nil\n        zeros_count_max = 1\n\n        count = 0\n        fields.each_with_index do |field, i|\n          if field == 0\n            count += 1\n            if count > zeros_count_max\n              zeros_start = i - count + 1\n              zeros_count_max = count\n            end\n          else\n            count = 0\n          end\n        end\n\n        i = 0\n        while i < 8\n          if i == zeros_start\n            io << ':'\n            i += zeros_count_max\n            io << ':' if i == 8\n          else\n            io << ':' if i > 0\n            fields[i].to_s(io, base: 16)\n            i += 1\n          end\n        end\n      end\n    end\n\n    private IPV4_FULL_MAX_SIZE = IPV4_MAX_SIZE + 6 # \":65535\".size\n    private IPV6_FULL_MAX_SIZE = IPV6_MAX_SIZE + 8 # \"[\".size + \"]:65535\".size\n\n    # Returns a `String` representation of the IP address plus the port number.\n    #\n    # IPv6 addresses are canonicalized according to\n    # [RFC 5952, section 4](https://datatracker.ietf.org/doc/html/rfc5952#section-4),\n    # and surrounded within a pair of square brackets according to\n    # [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).\n    # IPv4-mapped IPv6 addresses use the mixed notation according to RFC 5952,\n    # section 5.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # v4 = Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 8080)\n    # v4.to_s # => \"127.0.0.1:8080\"\n    #\n    # v6 = Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1), 443)\n    # v6.to_s # => \"[2001:db8::1:0:0:1]:443\"\n    #\n    # mapped = Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 1, 15), 55001)\n    # mapped.to_s # => \"[::ffff:192.168.1.15]:55001\"\n    # ```\n    #\n    # To obtain the address alone without the port number, see `#address`.\n    def to_s : String\n      String.build(@addr.is_a?(LibC::InAddr) ? IPV4_FULL_MAX_SIZE : IPV6_FULL_MAX_SIZE) do |io|\n        to_s(io)\n      end\n    end\n\n    def inspect(io : IO) : Nil\n      io << \"Socket::IPAddress(\"\n      to_s(io)\n      io << \")\"\n    end\n\n    def inspect : String\n      # 19 == \"Socket::IPAddress(\".size + \")\".size\n      String.build((@addr.is_a?(LibC::InAddr) ? IPV4_FULL_MAX_SIZE : IPV6_FULL_MAX_SIZE) + 19) do |io|\n        inspect(io)\n      end\n    end\n\n    def pretty_print(pp)\n      pp.text inspect\n    end\n\n    def to_unsafe : LibC::Sockaddr*\n      case addr = @addr\n      in LibC::InAddr\n        to_sockaddr_in(addr)\n      in LibC::In6Addr\n        to_sockaddr_in6(addr)\n      end\n    end\n\n    private def to_sockaddr_in6(addr)\n      sockaddr = Pointer(LibC::SockaddrIn6).malloc\n      sockaddr.value.sin6_family = family\n      sockaddr.value.sin6_port = IPAddress.endian_swap(port.to_u16!)\n      sockaddr.value.sin6_addr = addr\n      if @family == Family::INET6 && link_local?\n        sockaddr.value.sin6_scope_id = @zone_id\n      else\n        sockaddr.value.sin6_scope_id = 0\n      end\n      sockaddr.as(LibC::Sockaddr*)\n    end\n\n    private def to_sockaddr_in(addr)\n      sockaddr = Pointer(LibC::SockaddrIn).malloc\n      sockaddr.value.sin_family = family\n      sockaddr.value.sin_port = IPAddress.endian_swap(port.to_u16!)\n      sockaddr.value.sin_addr = addr\n      sockaddr.as(LibC::Sockaddr*)\n    end\n\n    # Returns the interface name for a scoped/zoned link-local IPv6 address.\n    # This only works on properly initialized link-local IPv6 address objects.\n    # In any other case this will return nil.\n    #\n    # The OS tracks the zone via a numerical interface index as enumerated\n    # by the kernel. To keep our abstraction class in line, we also only\n    # keep the interface index around.\n    #\n    # This helper method exists to look up the interface name based on the\n    # associated zone_id property.\n    def link_local_interface : String | Nil\n      {% unless LibC.has_method?(:if_nametoindex) %}\n        raise NotImplementedError.new \"Socket::Address.link_local_interface\"\n      {% end %}\n      return nil if @zone_id.zero?\n      return nil unless (@family == Socket::Family::INET6 && link_local?)\n      buf = uninitialized StaticArray(UInt8, LibC::IF_NAMESIZE)\n      result = LibC.if_indextoname(@zone_id, buf)\n      if result.null?\n        message = \"Failed to look up interface name for index #{@zone_id}\"\n        {% if flag?(:windows) %}\n          # In windows it is not possible to determine an error code here\n          raise Error.new(message)\n        {% else %}\n          raise Error.from_errno(message)\n        {% end %}\n      end\n      String.new(buf.to_unsafe)\n    end\n\n    protected def self.endian_swap(x : Int::Primitive) : Int::Primitive\n      {% if IO::ByteFormat::NetworkEndian != IO::ByteFormat::SystemEndian %}\n        x.byte_swap\n      {% else %}\n        x\n      {% end %}\n    end\n\n    # Returns `true` if *port* is a valid port number.\n    #\n    # Valid port numbers are in the range `0..65_535`.\n    def self.valid_port?(port : Int) : Bool\n      port.in?(0..UInt16::MAX)\n    end\n  end\n\n  # UNIX address representation.\n  #\n  # Holds the local path of an UNIX address, usually coming from an opened\n  # connection (e.g. `Socket#local_address`, `Socket#receive`).\n  #\n  # Example:\n  # ```\n  # require \"socket\"\n  #\n  # Socket::UNIXAddress.new(\"/tmp/my.sock\")\n  # ```\n  struct UNIXAddress < Address\n    getter path : String\n\n    # :nodoc:\n    MAX_PATH_SIZE = {% if flag?(:wasm32) %}\n                      0\n                    {% else %}\n                      sizeof(typeof(LibC::SockaddrUn.new.sun_path)) - 1\n                    {% end %}\n\n    def initialize(path : Path | String)\n      @path = path.to_s\n      if @path.bytesize > MAX_PATH_SIZE\n        raise ArgumentError.new(\"Path size exceeds the maximum size of #{MAX_PATH_SIZE} bytes\")\n      end\n      @family = Family::UNIX\n      @size = {% if flag?(:wasm32) %}\n                1\n              {% else %}\n                sizeof(LibC::SockaddrUn)\n              {% end %}\n    end\n\n    # Creates an `UNIXSocket` from the internal OS representation.\n    def self.from(sockaddr : LibC::Sockaddr*, addrlen) : UNIXAddress\n      {% if flag?(:wasm32) %}\n        raise NotImplementedError.new \"Socket::UNIXAddress.from\"\n      {% else %}\n        new(sockaddr.as(LibC::SockaddrUn*), addrlen.to_i)\n      {% end %}\n    end\n\n    # :ditto:\n    def self.from(sockaddr : LibC::Sockaddr*) : UNIXAddress\n      {% if flag?(:wasm32) %}\n        raise NotImplementedError.new \"Socket::UNIXAddress.from\"\n      {% else %}\n        sockaddr = sockaddr.as(LibC::SockaddrUn*)\n\n        new(sockaddr, sizeof(typeof(sockaddr)))\n      {% end %}\n    end\n\n    # Parses a `Socket::UNIXAddress` from an URI.\n    #\n    # It expects the URI to include `<scheme>://<path>` where `scheme` as well\n    # as any additional URI components (such as `fragment` or `query`) are ignored.\n    #\n    # If `host` is not empty, it will be prepended to `path` to form a relative\n    # path.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # Socket::UNIXAddress.parse(\"unix:///foo.sock\") # => Socket::UNIXAddress.new(\"/foo.sock\")\n    # Socket::UNIXAddress.parse(\"unix://foo.sock\")  # => Socket::UNIXAddress.new(\"foo.sock\")\n    # ```\n    def self.parse(uri : URI) : UNIXAddress\n      unix_path = String.build do |io|\n        io << uri.host\n        if port = uri.port\n          io << ':' << port\n        end\n        if path = uri.path.presence\n          io << path\n        end\n      end\n\n      raise Socket::Error.new(\"Invalid UNIX address: missing path\") if unix_path.empty?\n\n      {% if flag?(:wasm32) %}\n        raise NotImplementedError.new \"Socket::UNIXAddress.parse\"\n      {% else %}\n        UNIXAddress.new(unix_path)\n      {% end %}\n    end\n\n    # :ditto:\n    def self.parse(uri : String) : self\n      parse URI.parse(uri)\n    end\n\n    {% unless flag?(:wasm32) %}\n      protected def initialize(sockaddr : LibC::SockaddrUn*, size)\n        @family = Family::UNIX\n        @path = String.new(sockaddr.value.sun_path.to_slice, truncate_at_null: true)\n        @size = size || sizeof(LibC::SockaddrUn)\n      end\n    {% end %}\n\n    def_equals_and_hash path\n\n    def to_s(io : IO) : Nil\n      io << path\n    end\n\n    def to_unsafe : LibC::Sockaddr*\n      {% if flag?(:wasm32) %}\n        raise NotImplementedError.new \"Socket::UNIXAddress#to_unsafe\"\n      {% else %}\n        sockaddr = Pointer(LibC::SockaddrUn).malloc\n        sockaddr.value.sun_family = family\n        sockaddr.value.sun_path.to_unsafe.copy_from(@path.to_unsafe, {@path.bytesize + 1, sockaddr.value.sun_path.size}.min)\n        sockaddr.as(LibC::Sockaddr*)\n      {% end %}\n    end\n  end\n\n  # Returns `true` if the string represents a valid IPv4 or IPv6 address.\n  @[Deprecated(\"Use `IPAddress.valid?` instead\")]\n  def self.ip?(string : String)\n    IPAddress.valid?(string)\n  end\nend\n"
  },
  {
    "path": "src/socket/addrinfo.cr",
    "content": "require \"uri/punycode\"\nrequire \"./address\"\nrequire \"crystal/system/addrinfo\"\n\nclass Socket\n  # Domain name resolver.\n  #\n  # # Query Concurrency Behaviour\n  #\n  # On most platforms, DNS queries are currently resolved synchronously.\n  # Calling a resolve method blocks the entire thread until it returns.\n  # This can cause latencies, especially in single-threaded processes.\n  #\n  # DNS queries resolve asynchronously on the following platforms:\n  #\n  # * Windows 8 and higher\n  #\n  # NOTE: Follow the discussion in [Async DNS resolution (#13619)](https://github.com/crystal-lang/crystal/issues/13619)\n  # for more details.\n  struct Addrinfo\n    include Crystal::System::Addrinfo\n\n    getter family : Family\n    getter type : Type\n    getter protocol : Protocol\n    getter size : Int32\n\n    # Resolves a domain that best matches the given options.\n    #\n    # - *domain* may be an IP address or a domain name.\n    # - *service* may be a port number or a service name. It must be specified,\n    #   because different servers may handle the `mail` or `http` services for\n    #   example.\n    # - *family* is optional and defaults to `Family::UNSPEC`\n    # - *type* is the intended socket type (e.g. `Type::STREAM`) and must be\n    #   specified.\n    # - *protocol* is the intended socket protocol (e.g. `Protocol::TCP`) and\n    #   should be specified.\n    # - *timeout* is optional and specifies the maximum time to wait before\n    #   `IO::TimeoutError` is raised. Currently this is only supported on\n    #   Windows.\n    #\n    # Example:\n    # ```\n    # require \"socket\"\n    #\n    # addrinfos = Socket::Addrinfo.resolve(\"example.org\", \"http\", type: Socket::Type::STREAM, protocol: Socket::Protocol::TCP)\n    # ```\n    def self.resolve(domain : String, service, family : Family? = nil, type : Type = nil, protocol : Protocol = Protocol::IP, timeout = nil) : Array(Addrinfo)\n      addrinfos = [] of Addrinfo\n\n      getaddrinfo(domain, service, family, type, protocol, timeout) do |addrinfo|\n        addrinfos << addrinfo\n      end\n\n      addrinfos\n    end\n\n    # Resolves a domain that best matches the given options.\n    #\n    # Yields each possible `Addrinfo` resolution since a domain may resolve to\n    # many IP. Implementations are supposed to try all the addresses until the\n    # socket is connected (or bound) or there are no addresses to try anymore.\n    #\n    # Raising is an expensive operation, so instead of raising on a connect or\n    # bind error, just to rescue it immediately after, the block is expected to\n    # return the error instead, which will be raised once there are no more\n    # addresses to try.\n    #\n    # The iteration will be stopped once the block returns something that isn't\n    # an `Exception` (e.g. a `Socket` or `nil`).\n    def self.resolve(domain : String, service, family : Family? = nil, type : Type = nil, protocol : Protocol = Protocol::IP, timeout = nil, &)\n      exception = nil\n\n      getaddrinfo(domain, service, family, type, protocol, timeout) do |addrinfo|\n        value = yield addrinfo\n\n        if value.is_a?(Exception)\n          exception = value\n        else\n          return value\n        end\n      end\n\n      case exception\n      when Socket::ConnectError\n        raise Socket::ConnectError.from_os_error(\"Error connecting to '#{domain}:#{service}'\", exception.os_error)\n      when Exception\n        {% if flag?(:win32) && compare_versions(Crystal::LLVM_VERSION, \"13.0.0\") < 0 %}\n          # FIXME: Workaround for https://github.com/crystal-lang/crystal/issues/11047\n          array = StaticArray(UInt8, 0).new(0)\n        {% end %}\n\n        raise exception\n      end\n    end\n\n    class Error < Socket::Error\n      @[Deprecated(\"Use `#os_error` instead\")]\n      def error_code : Int32\n        os_error.not_nil!.value.to_i32!\n      end\n\n      @[Deprecated(\"Use `.from_os_error` instead\")]\n      def self.new(error_code : Int32, message, domain)\n        from_os_error(message, Errno.new(error_code), domain: domain, type: nil, service: nil, protocol: nil)\n      end\n\n      @[Deprecated(\"Use `.from_os_error` instead\")]\n      def self.new(error_code : Int32, domain)\n        new error_code, nil, domain: domain\n      end\n\n      protected def self.new_from_os_error(message : String?, os_error, *, domain, type, service, protocol, **opts)\n        new(message, **opts)\n      end\n\n      protected def self.new_from_os_error(message : String?, os_error, *, domain, **opts)\n        new(message, **opts)\n      end\n\n      def self.build_message(message, *, domain, **opts)\n        \"Hostname lookup for #{domain} failed\"\n      end\n\n      def self.os_error_message(os_error : Errno | WinError, *, type, service, protocol, **opts)\n        # when `EAI_NONAME` etc. is an integer then only `os_error.value` can\n        # match; when `EAI_NONAME` is a `WinError` then `os_error` itself can\n        # match\n        case os_error.is_a?(Errno) ? os_error.value : os_error\n        when LibC::EAI_NONAME\n          \"No address found\"\n        when LibC::EAI_SOCKTYPE\n          \"The requested socket type #{type} protocol #{protocol} is not supported\"\n        when LibC::EAI_SERVICE\n          \"The requested service #{service} is not available for the requested socket type #{type}\"\n        else\n          # Win32 also has this method, but `WinError` is already sufficient\n          {% if LibC.has_method?(:gai_strerror) %}\n            if os_error.is_a?(Errno)\n              return String.new(LibC.gai_strerror(os_error))\n            end\n          {% end %}\n\n          super\n        end\n      end\n    end\n\n    private def self.getaddrinfo(domain, service, family, type, protocol, timeout, &)\n      # RFC 3986 says:\n      # > When a non-ASCII registered name represents an internationalized domain name\n      # > intended for resolution via the DNS, the name must be transformed to the IDNA\n      # > encoding [RFC3490] prior to name lookup.\n      domain = URI::Punycode.to_ascii domain\n\n      Crystal::System::Addrinfo.getaddrinfo(domain, service, family, type, protocol, timeout) do |addrinfo|\n        yield addrinfo\n      end\n    end\n\n    # Resolves *domain* for the TCP protocol and returns an `Array` of possible\n    # `Addrinfo`. See `#resolve` for details.\n    #\n    # Example:\n    # ```\n    # require \"socket\"\n    #\n    # addrinfos = Socket::Addrinfo.tcp(\"example.org\", 80)\n    # ```\n    def self.tcp(domain : String, service, family = Family::UNSPEC, timeout = nil) : Array(Addrinfo)\n      resolve(domain, service, family, Type::STREAM, Protocol::TCP, timeout)\n    end\n\n    # Resolves a domain for the TCP protocol with STREAM type, and yields each\n    # possible `Addrinfo`. See `#resolve` for details.\n    def self.tcp(domain : String, service, family = Family::UNSPEC, timeout = nil, &)\n      resolve(domain, service, family, Type::STREAM, Protocol::TCP, timeout) { |addrinfo| yield addrinfo }\n    end\n\n    # Resolves *domain* for the UDP protocol and returns an `Array` of possible\n    # `Addrinfo`. See `#resolve` for details.\n    #\n    # Example:\n    # ```\n    # require \"socket\"\n    #\n    # addrinfos = Socket::Addrinfo.udp(\"example.org\", 53)\n    # ```\n    def self.udp(domain : String, service, family = Family::UNSPEC, timeout = nil) : Array(Addrinfo)\n      resolve(domain, service, family, Type::DGRAM, Protocol::UDP, timeout)\n    end\n\n    # Resolves a domain for the UDP protocol with DGRAM type, and yields each\n    # possible `Addrinfo`. See `#resolve` for details.\n    def self.udp(domain : String, service, family = Family::UNSPEC, timeout = nil, &)\n      resolve(domain, service, family, Type::DGRAM, Protocol::UDP, timeout) { |addrinfo| yield addrinfo }\n    end\n\n    # Returns an `IPAddress` matching this addrinfo.\n    getter(ip_address : Socket::IPAddress) do\n      system_ip_address\n    end\n\n    def inspect(io : IO)\n      io << \"Socket::Addrinfo(\"\n      io << ip_address << \", \"\n      io << family << \", \"\n      io << type << \", \"\n      io << protocol\n      io << \")\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/socket/common.cr",
    "content": "{% if flag?(:win32) %}\n  require \"c/ws2tcpip\"\n  require \"c/afunix\"\n  require \"c/netioapi\"\n{% elsif flag?(:wasi) %}\n  require \"c/arpa/inet\"\n  require \"c/netinet/in\"\n{% else %}\n  require \"c/arpa/inet\"\n  require \"c/sys/un\"\n  require \"c/netinet/in\"\n  require \"c/net/if\"\n{% end %}\n\nclass Socket < IO\n  enum Protocol\n    IP = LibC::IPPROTO_IP\n    {% if flag?(:win32) %}\n      TCP  = LibC::IPPROTO::IPPROTO_TCP\n      UDP  = LibC::IPPROTO::IPPROTO_UDP\n      RAW  = LibC::IPPROTO::IPPROTO_RAW\n      ICMP = LibC::IPPROTO::IPPROTO_ICMP\n    {% else %}\n      TCP  = LibC::IPPROTO_TCP\n      UDP  = LibC::IPPROTO_UDP\n      RAW  = LibC::IPPROTO_RAW\n      ICMP = LibC::IPPROTO_ICMP\n    {% end %}\n  end\n\n  # :nodoc:\n  {% if flag?(:win32) %}\n    alias FamilyT = UInt8\n  {% else %}\n    alias FamilyT = LibC::SaFamilyT\n  {% end %}\n\n  enum Family : FamilyT\n    UNSPEC = LibC::AF_UNSPEC\n    UNIX   = LibC::AF_UNIX\n    INET   = LibC::AF_INET\n    INET6  = LibC::AF_INET6\n  end\n\n  enum Type\n    STREAM = LibC::SOCK_STREAM\n    DGRAM  = LibC::SOCK_DGRAM\n    {% unless flag?(:wasi) %}\n      RAW       = LibC::SOCK_RAW\n      SEQPACKET = LibC::SOCK_SEQPACKET\n    {% end %}\n  end\n\n  class Error < IO::Error\n    private def self.new_from_os_error(message, os_error, **opts)\n      case os_error\n      when Errno::ECONNREFUSED\n        Socket::ConnectError.new(message, **opts)\n      when Errno::EADDRINUSE\n        Socket::BindError.new(message, **opts)\n      else\n        super message, os_error, **opts\n      end\n    end\n  end\n\n  class ConnectError < Error\n  end\n\n  class BindError < Error\n  end\nend\n"
  },
  {
    "path": "src/socket/ip_socket.cr",
    "content": "# NOTE: To use `IPSocket`, you must explicitly import it with `require \"socket/ip_socket\"`\nclass IPSocket < Socket\n  # Returns the `IPAddress` for the local end of the IP socket.\n  getter local_address : Socket::IPAddress { system_local_address }\n\n  # Returns the `IPAddress` for the remote end of the IP socket.\n  getter remote_address : Socket::IPAddress { system_remote_address }\n\n  def close\n    super\n  ensure\n    @local_address = nil\n    @remote_address = nil\n  end\n\n  def connect(addr, timeout = nil, &)\n    super(addr, timeout) { |error| yield error }\n  ensure\n    @local_address = nil\n    @remote_address = nil\n  end\n\n  def bind(addr)\n    super(addr)\n  ensure\n    @local_address = nil\n    @remote_address = nil\n  end\n\n  # Reports whether IPv4 packets are accepted by the socket.\n  def ipv6_only? : Bool\n    raise Socket::Error.new(\"Unsupported IP address family: #{family}. For use with IPv6 only\") unless family.inet6?\n    getsockopt_bool(LibC::IPV6_V6ONLY, level: LibC::IPPROTO_IPV6)\n  end\n\n  # Sets whether an IPv6 socket will accept IPv4 clients / packets\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = UDPSocket.new(:inet6)\n  # # enable IPv6 dual stack, accepting IPv4 clients\n  # server.ipv6_only = false\n  # server.bind \"::1\", 1234\n  #\n  # message = Bytes.new(32)\n  # bytes_read, client_addr = server.receive(message)\n  # ```\n  def ipv6_only=(val : Bool) : Bool\n    raise Socket::Error.new(\"Unsupported IP address family: #{family}. For use with IPv6 only\") unless family.inet6?\n    setsockopt_bool LibC::IPV6_V6ONLY, val, level: LibC::IPPROTO_IPV6\n  end\nend\n"
  },
  {
    "path": "src/socket/server.cr",
    "content": "class Socket\n  module Server\n    # Accepts an incoming connection and returns the client socket.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # server = TCPServer.new(2202)\n    # while true\n    #   socket = server.accept\n    #   socket.puts Time.utc\n    #   socket.close\n    # end\n    # ```\n    #\n    # If the server is closed after invoking this method, an `IO::Error` (closed stream) exception must be raised.\n    abstract def accept : IO\n\n    # Accepts an incoming connection and returns the client socket.\n    #\n    # Returns `nil` if the server is closed after invoking this method.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # server = TCPServer.new(2202)\n    # while socket = server.accept?\n    #   socket.puts Time.utc\n    #   socket.close\n    # end\n    # ```\n    abstract def accept? : IO?\n\n    # Accepts an incoming connection and yields the client socket to the block.\n    # Eventually closes the connection when the block returns.\n    #\n    # Returns the value of the block. If the server is closed after invoking this\n    # method, an `IO::Error` (closed stream) exception will be raised.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # server = TCPServer.new(2202)\n    # server.accept do |socket|\n    #   socket.puts Time.utc\n    # end\n    # ```\n    def accept(&)\n      sock = accept\n      begin\n        yield sock\n      ensure\n        sock.close\n      end\n    end\n\n    # Accepts an incoming connection and yields the client socket to the block.\n    # Eventually closes the connection when the block returns.\n    #\n    # Returns the value of the block or `nil` if the server is closed after\n    # invoking this method.\n    #\n    # ```\n    # require \"socket\"\n    #\n    # server = UNIXServer.new(\"/tmp/service.sock\")\n    # server.accept? do |socket|\n    #   socket.puts Time.utc\n    # end\n    # ```\n    def accept?(&)\n      sock = accept?\n      return unless sock\n\n      begin\n        yield sock\n      ensure\n        sock.close\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/socket/tcp_server.cr",
    "content": "require \"./tcp_socket\"\n\n# A Transmission Control Protocol (TCP/IP) server.\n#\n# NOTE: To use `TCPServer`, you must explicitly import it with `require \"socket\"`\n#\n# Usage example:\n# ```\n# require \"socket\"\n#\n# def handle_client(client)\n#   message = client.gets\n#   client.puts message\n# end\n#\n# server = TCPServer.new(\"localhost\", 1234)\n# while client = server.accept?\n#   spawn handle_client(client)\n# end\n# ```\n#\n# Options:\n# - *host* local interface to bind on, or `::` to bind on all local interfaces.\n# - *port* specific port to bind on, or `0` to receive an \"ephemeral\" (free, assigned by kernel) port.\n# - *backlog* to specify how many pending connections are allowed.\n# - *reuse_port* to enable multiple processes to bind to the same port (`SO_REUSEPORT`).\nclass TCPServer < TCPSocket\n  include Socket::Server\n\n  # Creates a new `TCPServer`, waiting to be bound.\n  def self.new(family : Family = Family::INET)\n    super(family)\n  end\n\n  # Binds a socket to the *host* and *port* combination.\n  def initialize(host : String, port : Int, backlog : Int = SOMAXCONN, dns_timeout = nil, reuse_port : Bool = false)\n    Addrinfo.tcp(host, port, timeout: dns_timeout) do |addrinfo|\n      super(addrinfo.family, addrinfo.type, addrinfo.protocol)\n\n      self.reuse_address = true\n      self.reuse_port = true if reuse_port\n\n      if errno = @fd_lock.reference { system_bind(addrinfo, \"#{host}:#{port}\") }\n        close\n        next errno\n      end\n\n      if errno = listen(backlog) { |errno| errno }\n        close\n        next errno\n      end\n    end\n  end\n\n  # Creates a TCPServer from an existing system file descriptor or socket\n  # handle.\n  #\n  # This adopts *fd* into the IO system that will reconfigure it as per the\n  # event loop runtime requirements.\n  #\n  # NOTE: On Windows, the handle must have been created with\n  # `WSA_FLAG_OVERLAPPED`.\n  def initialize(*, fd : Handle, family : Family = Family::INET)\n    super(fd: fd, family: family)\n  end\n\n  # Creates a new TCP server, listening on all local interfaces (`::`).\n  def self.new(port : Int, backlog = SOMAXCONN, reuse_port = false)\n    new(\"::\", port, backlog, reuse_port: reuse_port)\n  end\n\n  # Creates a new TCP server and yields it to the block. Eventually closes the\n  # server socket when the block returns.\n  #\n  # Returns the value of the block.\n  def self.open(host, port, backlog = SOMAXCONN, reuse_port = false, &)\n    server = new(host, port, backlog, reuse_port: reuse_port)\n    begin\n      yield server\n    ensure\n      server.close\n    end\n  end\n\n  # Creates a new TCP server, listening on all interfaces, and yields it to the\n  # block. Eventually closes the server socket when the block returns.\n  #\n  # Returns the value of the block.\n  def self.open(port : Int, backlog = SOMAXCONN, reuse_port = false, &)\n    server = new(port, backlog, reuse_port: reuse_port)\n    begin\n      yield server\n    ensure\n      server.close\n    end\n  end\n\n  # Accepts an incoming connection.\n  #\n  # Returns the client `TCPSocket` or `nil` if the server is closed after invoking\n  # this method.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = TCPServer.new(2022)\n  # loop do\n  #   if socket = server.accept?\n  #     # handle the client in a fiber\n  #     spawn handle_connection(socket)\n  #   else\n  #     # another fiber closed the server\n  #     break\n  #   end\n  # end\n  # ```\n  def accept? : TCPSocket?\n    return if closed?\n\n    if rs = @fd_lock.read { system_accept }\n      sock = TCPSocket.new(handle: rs[0], family: family, type: type, protocol: protocol, blocking: rs[1])\n      sock.sync = sync?\n      sock\n    end\n  end\nend\n"
  },
  {
    "path": "src/socket/tcp_socket.cr",
    "content": "require \"./ip_socket\"\n\n# A Transmission Control Protocol (TCP/IP) socket.\n#\n# NOTE: To use `TCPSocket`, you must explicitly import it with `require \"socket\"`\n#\n# Usage example:\n# ```\n# require \"socket\"\n#\n# client = TCPSocket.new(\"localhost\", 1234)\n# client << \"message\\n\"\n# response = client.gets\n# client.close\n# ```\nclass TCPSocket < IPSocket\n  {% begin %}\n    # Creates a new `TCPSocket`, waiting to be connected.\n    def self.new(family : Family = Family::INET, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil)\n      super(af: family, type: Type::STREAM, protocol: Protocol::TCP, blocking: blocking)\n    end\n  {% end %}\n\n  {% begin %}\n    # Creates a new TCP connection to a remote TCP server.\n    #\n    # You may limit the DNS resolution time with `dns_timeout` and limit the\n    # connection time to the remote server with `connect_timeout`. Both values\n    # must be in seconds (integers or floats).\n    def initialize(host : String, port, dns_timeout = nil, connect_timeout = nil, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil)\n      Addrinfo.tcp(host, port, timeout: dns_timeout) do |addrinfo|\n        super(af: addrinfo.family, type: addrinfo.type, protocol: addrinfo.protocol, blocking: blocking)\n        connect(addrinfo, timeout: connect_timeout) do |error|\n          close\n          error\n        end\n      end\n    end\n  {% end %}\n\n  protected def initialize(family : Family, type : Type, protocol : Protocol = Protocol::IP)\n    super family, type, protocol\n  end\n\n  # Internal constructor for `TCPServer#accept?`.\n  # The *blocking* arg is purely informational.\n  protected def initialize(*, handle, family, type, protocol, blocking)\n    super(handle: handle, family: family, type: type, protocol: protocol, blocking: blocking)\n  end\n\n  {% begin %}\n    # Creates an UNIXSocket from an existing system file descriptor or socket\n    # handle.\n    #\n    # This adopts *fd* into the IO system that will reconfigure it as per the\n    # event loop runtime requirements.\n    #\n    # NOTE: On Windows, the handle must have been created with\n    # `WSA_FLAG_OVERLAPPED`.\n    def initialize(*, fd : Handle, family : Family = Family::INET, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil)\n      super fd, family, Type::STREAM, Protocol::TCP, blocking\n    end\n  {% end %}\n\n  # Opens a TCP socket to a remote TCP server, yields it to the block, then\n  # eventually closes the socket when the block returns.\n  #\n  # Returns the value of the block.\n  def self.open(host : String, port, &)\n    sock = new(host, port)\n    begin\n      yield sock\n    ensure\n      sock.close\n    end\n  end\n\n  # Returns `true` if the Nagle algorithm is disabled.\n  def tcp_nodelay? : Bool\n    getsockopt_bool LibC::TCP_NODELAY, level: Protocol::TCP\n  end\n\n  # Disables the Nagle algorithm when set to `true`, otherwise enables it.\n  def tcp_nodelay=(val : Bool)\n    setsockopt_bool LibC::TCP_NODELAY, val, level: Protocol::TCP\n  end\n\n  # The amount of time in seconds the connection must be idle before sending keepalive probes.\n  def tcp_keepalive_idle\n    system_tcp_keepalive_idle\n  end\n\n  def tcp_keepalive_idle=(val : Int)\n    self.system_tcp_keepalive_idle = val\n  end\n\n  # The amount of time in seconds between keepalive probes.\n  def tcp_keepalive_interval\n    system_tcp_keepalive_interval\n  end\n\n  def tcp_keepalive_interval=(val : Int)\n    self.system_tcp_keepalive_interval = val\n    val\n  end\n\n  # The number of probes sent, without response before dropping the connection.\n  def tcp_keepalive_count\n    system_tcp_keepalive_count\n  end\n\n  def tcp_keepalive_count=(val : Int)\n    self.system_tcp_keepalive_count = val\n  end\nend\n"
  },
  {
    "path": "src/socket/udp_socket.cr",
    "content": "require \"./ip_socket\"\n\n# A User Datagram Protocol (UDP) socket.\n#\n# UDP runs on top of the Internet Protocol (IP) and was developed for applications that do\n# not require reliability, acknowledgement, or flow control features at the transport layer.\n# This simple protocol provides transport layer addressing in the form of UDP ports and an\n# optional checksum capability.\n#\n# UDP is a very simple protocol. Messages, so called datagrams, are sent to other hosts on\n# an IP network without the need to set up special transmission channels or data paths\n# beforehand. The UDP socket only needs to be opened for communication. It listens for\n# incoming messages and sends outgoing messages on request.\n#\n# This implementation supports both IPv4 and IPv6 addresses. For IPv4 addresses you must use\n# `Socket::Family::INET` family (default) or `Socket::Family::INET6` for IPv6 # addresses.\n#\n# NOTE: To use `UDPSocket`, you must explicitly import it with `require \"socket\"`\n#\n# Usage example:\n#\n# ```\n# require \"socket\"\n#\n# # Create server\n# server = UDPSocket.new\n# server.bind \"localhost\", 1234\n#\n# # Create client and connect to server\n# client = UDPSocket.new\n# client.connect \"localhost\", 1234\n#\n# # Send a text message to server\n# client.send \"message\"\n#\n# # Receive text message from client\n# message, client_addr = server.receive\n#\n# # Close client and server\n# client.close\n# server.close\n# ```\n#\n# The `send` methods may sporadically fail with `Socket::ConnectError` when sending datagrams\n# to a non-listening server.\n# Wrap with an exception handler to prevent raising. Example:\n#\n# ```\n# begin\n#   client.send(message, @destination)\n# rescue ex : Socket::ConnectError\n#   p ex.inspect\n# end\n# ```\nclass UDPSocket < IPSocket\n  def initialize(family : Family = Family::INET)\n    super(family, Type::DGRAM, Protocol::UDP)\n  end\n\n  # Receives a text message from the previously bound address.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = UDPSocket.new\n  # server.bind(\"localhost\", 1234)\n  #\n  # message, client_addr = server.receive\n  # ```\n  def receive(max_message_size = 512) : {String, IPAddress}\n    address = nil\n    message = String.new(max_message_size) do |buffer|\n      bytes_read, address = @fd_lock.read do\n        system_receive_from(Slice.new(buffer, max_message_size))\n      end\n      {bytes_read, 0}\n    end\n    {message, address.as(IPAddress)}\n  end\n\n  # Receives a binary message from the previously bound address.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = UDPSocket.new\n  # server.bind \"localhost\", 1234\n  #\n  # message = Bytes.new(32)\n  # bytes_read, client_addr = server.receive(message)\n  # ```\n  def receive(message : Bytes) : {Int32, IPAddress}\n    bytes_read, address = @fd_lock.read { system_receive_from(message) }\n    {bytes_read, address.as(IPAddress)}\n  end\n\n  # Reports whether transmitted multicast packets should be copied and sent\n  # back to the originator.\n  def multicast_loopback? : Bool\n    case @family\n    when Family::INET\n      getsockopt_bool LibC::IP_MULTICAST_LOOP, LibC::IPPROTO_IP\n    when Family::INET6\n      getsockopt_bool LibC::IPV6_MULTICAST_LOOP, LibC::IPPROTO_IPV6\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n  end\n\n  # Sets whether transmitted multicast packets should be copied and sent back\n  # to the originator, if the host has joined the multicast group.\n  def multicast_loopback=(val : Bool)\n    case @family\n    when Family::INET\n      setsockopt_bool LibC::IP_MULTICAST_LOOP, val, LibC::IPPROTO_IP\n    when Family::INET6\n      setsockopt_bool LibC::IPV6_MULTICAST_LOOP, val, LibC::IPPROTO_IPV6\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n  end\n\n  # Returns the current value of the `hoplimit` field on uni-cast packets.\n  # Datagrams with a `hoplimit` of `1` are not forwarded beyond the local network.\n  # Multicast datagrams with a `hoplimit` of `0` will not be transmitted on any\n  # network, but may be delivered locally if the sending host belongs to the\n  # destination group and multicast loopback is enabled.\n  def multicast_hops : Int32\n    case @family\n    when Family::INET\n      getsockopt LibC::IP_MULTICAST_TTL, 0, LibC::IPPROTO_IP\n    when Family::INET6\n      getsockopt LibC::IPV6_MULTICAST_HOPS, 0, LibC::IPPROTO_IPV6\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n  end\n\n  # The multicast hops option controls the `hoplimit` field on uni-cast packets.\n  # If `-1` is specified, the kernel will use a default value.\n  # If a value of `0` to `255` is specified, the packet will have the specified\n  # value as `hoplimit`. Other values are considered invalid and `Socket::Error` will be raised.\n  # Datagrams with a `hoplimit` of `1` are not forwarded beyond the local network.\n  # Multicast datagrams with a `hoplimit` of `0` will not be transmitted on any\n  # network, but may be delivered locally if the sending host belongs to the\n  # destination group and multicast loopback is enabled.\n  def multicast_hops=(val : Int)\n    case @family\n    when Family::INET\n      setsockopt LibC::IP_MULTICAST_TTL, val, LibC::IPPROTO_IP\n    when Family::INET6\n      setsockopt LibC::IPV6_MULTICAST_HOPS, val, LibC::IPPROTO_IPV6\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n    val\n  end\n\n  # For hosts with multiple interfaces, each multicast transmission is sent\n  # from the primary network interface. This function overrides the default\n  # IPv4 interface address for subsequent transmissions. Setting the interface\n  # to `0.0.0.0` will select the default interface.\n  # Raises `Socket::Error` unless the socket is IPv4 and an IPv4 address is provided.\n  def multicast_interface(address : IPAddress)\n    if @family == Family::INET\n      addr = address.@addr\n      if addr.is_a?(LibC::InAddr)\n        setsockopt LibC::IP_MULTICAST_IF, addr, LibC::IPPROTO_IP\n      else\n        raise Socket::Error.new \"Expecting an IPv4 interface address. Address provided: #{address.address}\"\n      end\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}. For use with IPv4 only\"\n    end\n  end\n\n  # For hosts with multiple interfaces, each multicast transmission is sent\n  # from the primary network interface. This function overrides the default\n  # IPv6 interface for subsequent transmissions. Setting the interface to\n  # index `0` will select the default interface.\n  def multicast_interface(index : UInt32)\n    if @family == Family::INET6\n      setsockopt LibC::IPV6_MULTICAST_IF, index, LibC::IPPROTO_IPV6\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}. For use with IPv6 only\"\n    end\n  end\n\n  # A host must become a member of a multicast group before it can receive\n  # datagrams sent to the group.\n  # Raises `Socket::Error` if an incompatible address is provided.\n  def join_group(address : IPAddress)\n    case @family\n    when Family::INET\n      group_modify(address, LibC::IP_ADD_MEMBERSHIP)\n    when Family::INET6\n      group_modify(address, LibC::IPV6_JOIN_GROUP)\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n  end\n\n  # Drops membership to the specified group. Memberships are automatically\n  # dropped when the socket is closed or the process exits.\n  # Raises `Socket::Error` if an incompatible address is provided.\n  def leave_group(address : IPAddress)\n    case @family\n    when Family::INET\n      group_modify(address, LibC::IP_DROP_MEMBERSHIP)\n    when Family::INET6\n      group_modify(address, LibC::IPV6_LEAVE_GROUP)\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n  end\n\n  private def group_modify(ip, operation)\n    ip_addr = ip.@addr\n\n    case @family\n    when Family::INET\n      if ip_addr.is_a?(LibC::InAddr)\n        req = LibC::IpMreq.new\n        req.imr_multiaddr = ip_addr\n\n        setsockopt operation, req, LibC::IPPROTO_IP\n      else\n        raise Socket::Error.new \"Expecting an IPv4 multicast address. Address provided: #{ip.address}\"\n      end\n    when Family::INET6\n      if ip_addr.is_a?(LibC::In6Addr)\n        req = LibC::Ipv6Mreq.new\n        req.ipv6mr_multiaddr = ip_addr\n\n        setsockopt operation, req, LibC::IPPROTO_IPV6\n      else\n        raise Socket::Error.new \"Expecting an IPv6 multicast address. Address provided: #{ip.address}\"\n      end\n    else\n      raise Socket::Error.new \"Unsupported IP address family: #{@family}\"\n    end\n  end\nend\n"
  },
  {
    "path": "src/socket/unix_server.cr",
    "content": "require \"./unix_socket\"\n\n# A local interprocess communication server socket.\n#\n# Available on UNIX-like operating systems, and Windows 10 Build 17063 or above.\n# Not all features are supported on Windows.\n#\n# NOTE: To use `UNIXServer`, you must explicitly import it with `require \"socket\"`\n#\n# Example usage:\n# ```\n# require \"socket\"\n#\n# def handle_client(client)\n#   message = client.gets\n#   client.puts message\n# end\n#\n# server = UNIXServer.new(\"/tmp/myapp.sock\")\n# while client = server.accept?\n#   spawn handle_client(client)\n# end\n# ```\nclass UNIXServer < UNIXSocket\n  include Socket::Server\n\n  # Creates a named UNIX socket, listening on a filesystem pathname.\n  #\n  # Always deletes any existing filesystem pathname first, in order to cleanup\n  # any leftover socket file.\n  #\n  # The server is of stream type by default, but this can be changed for\n  # another type. For example datagram messages:\n  #\n  # ```\n  # UNIXServer.new(\"/tmp/dgram.sock\", Socket::Type::DGRAM)\n  # ```\n  #\n  # [Only the stream type is supported on Windows](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable).\n  def initialize(path : Path | String, type : Type = Type::STREAM, backlog : Int = 128)\n    @path = path = path.to_s\n    super(Family::UNIX, type)\n\n    if error = @fd_lock.reference { system_bind(UNIXAddress.new(path), path) }\n      close(delete: false)\n      raise error\n    end\n\n    return if type == Type::DGRAM\n\n    listen(backlog) do |error|\n      close\n      raise error\n    end\n  end\n\n  # Creates a UNIXServer from an existing system file descriptor or socket\n  # handle.\n  #\n  # This adopts *fd* into the IO system that will reconfigure it as per the\n  # event loop runtime requirements.\n  #\n  # NOTE: On Windows, the handle must have been created with\n  # `WSA_FLAG_OVERLAPPED`.\n  def initialize(*, fd : Handle, type : Type = Type::STREAM, path : Path | String? = nil)\n    @path = path = path.to_s\n    super(fd: fd, type: type, path: path)\n  end\n\n  # Creates a new UNIX server and yields it to the block. Eventually closes the\n  # server socket when the block returns.\n  #\n  # Returns the value of the block.\n  def self.open(path : Path | String, type : Type = Type::STREAM, backlog = 128, &)\n    server = new(path, type, backlog)\n    begin\n      yield server\n    ensure\n      server.close\n    end\n  end\n\n  # Accepts an incoming connection.\n  #\n  # Returns the client socket or `nil` if the server is closed after invoking\n  # this method.\n  def accept? : UNIXSocket?\n    return if closed?\n\n    if rs = @fd_lock.read { system_accept }\n      sock = UNIXSocket.new(handle: rs[0], type: type, path: @path, blocking: rs[1])\n      sock.sync = sync?\n      sock\n    end\n  end\n\n  # Closes the socket, then deletes the filesystem pathname if it exists.\n  def close(delete = true) : Nil\n    super()\n  ensure\n    if delete && (path = @path)\n      File.delete?(path)\n      @path = nil\n    end\n  end\nend\n"
  },
  {
    "path": "src/socket/unix_socket.cr",
    "content": "# A local interprocess communication clientsocket.\n#\n# Available on UNIX-like operating systems, and Windows 10 Build 17063 or above.\n# Not all features are supported on Windows.\n#\n# NOTE: To use `UNIXSocket`, you must explicitly import it with `require \"socket\"`\n#\n# Example usage:\n# ```\n# require \"socket\"\n#\n# sock = UNIXSocket.new(\"/tmp/myapp.sock\")\n# sock.puts \"message\"\n# response = sock.gets\n# sock.close\n# ```\nclass UNIXSocket < Socket\n  getter path : String?\n\n  # Connects a named UNIX socket, bound to a filesystem pathname.\n  def initialize(path : Path | String, type : Type = Type::STREAM)\n    @path = path = path.to_s\n    super(Family::UNIX, type, Protocol::IP)\n\n    connect(UNIXAddress.new(path)) do |error|\n      close\n      raise error\n    end\n  end\n\n  protected def initialize(family : Family, type : Type)\n    super family, type, Protocol::IP\n  end\n\n  # Internal constructor for `UNIXSocket#pair` and `UNIXServer#accept?`.\n  # The *blocking* arg is purely informational.\n  protected def initialize(*, handle : Handle, type : Type = Type::STREAM, path : Path | String? = nil, blocking : Bool = nil)\n    @path = path.to_s\n    super handle: handle, family: Family::UNIX, type: type, protocol: Protocol::IP, blocking: blocking\n  end\n\n  # Creates an UNIXSocket from an existing system file descriptor or socket\n  # handle.\n  #\n  # This adopts the system socket into the IO system that will reconfigure it as\n  # per the event loop runtime requirements.\n  #\n  # NOTE: On Windows, the handle must have been created with\n  # `WSA_FLAG_OVERLAPPED`.\n  def initialize(*, fd : Handle, type : Type = Type::STREAM, path : Path | String? = nil)\n    @path = path.to_s\n    super fd, Family::UNIX, type, Protocol::IP\n  end\n\n  # Opens an UNIX socket to a filesystem pathname, yields it to the block, then\n  # eventually closes the socket when the block returns.\n  #\n  # Returns the value of the block.\n  def self.open(path : Path | String, type : Type = Type::STREAM, &)\n    sock = new(path, type)\n    begin\n      yield sock\n    ensure\n      sock.close\n    end\n  end\n\n  # Returns a pair of unnamed UNIX sockets.\n  #\n  # [Not supported on Windows](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable).\n  #\n  # ```\n  # require \"socket\"\n  #\n  # left, right = UNIXSocket.pair\n  #\n  # spawn do\n  #   # echo server\n  #   message = right.gets\n  #   right.puts message\n  # end\n  #\n  # left.puts \"message\"\n  # left.gets # => \"message\"\n  # ```\n  def self.pair(type : Type = Type::STREAM) : {UNIXSocket, UNIXSocket}\n    fds, blocking = Crystal::EventLoop.current.socketpair(type, Protocol::IP)\n    fds.map do |fd|\n      sock = UNIXSocket.new(handle: fd, type: type, blocking: blocking)\n      sock.sync = true\n      sock\n    end\n  end\n\n  # Creates a pair of unnamed UNIX sockets (see `pair`) and yields them to the\n  # block. Eventually closes both sockets when the block returns.\n  #\n  # Returns the value of the block.\n  #\n  # [Not supported on Windows](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable).\n  def self.pair(type : Type = Type::STREAM, &)\n    left, right = pair(type)\n    begin\n      yield left, right\n    ensure\n      left.close\n      right.close\n    end\n  end\n\n  def local_address : Socket::UNIXAddress\n    UNIXAddress.new(path.to_s)\n  end\n\n  def remote_address : Socket::UNIXAddress\n    UNIXAddress.new(path.to_s)\n  end\n\n  def receive(max_message_size = 512) : {String, UNIXAddress}\n    message, address = super(max_message_size)\n    {message, address.as(UNIXAddress)}\n  end\nend\n"
  },
  {
    "path": "src/socket.cr",
    "content": "require \"crystal/system/socket\"\nrequire \"crystal/fd_lock\"\n\nclass Socket < IO\n  include IO::Buffered\n  include Crystal::System::Socket\n\n  # :nodoc:\n  SOMAXCONN = 128\n\n  @volatile_fd : Atomic(Handle)\n  @fd_lock = Crystal::FdLock.new\n\n  # Returns the handle associated with this socket from the operating system.\n  #\n  # * on POSIX platforms, this is a file descriptor (`Int32`)\n  # * on Windows, this is a SOCKET handle (`LibC::SOCKET`)\n  #\n  # The returned system socket has been configured as per the IO system runtime\n  # requirements. If the returned socket must be in a specific mode or have a\n  # specific set of flags set, then they must be applied, even when it feels\n  # redundant, because even the same target isn't guaranteed to have the same\n  # requirements at runtime.\n  def fd\n    @volatile_fd.get\n  end\n\n  @closed : Bool\n\n  getter family : Family\n  getter type : Type\n  getter protocol : Protocol\n\n  # The time to wait when reading before raising an `IO::TimeoutError`.\n  property read_timeout : Time::Span?\n\n  # Sets the number of seconds to wait when reading before raising an `IO::TimeoutError`.\n  @[Deprecated(\"Use `#read_timeout=(Time::Span?)` instead.\")]\n  def read_timeout=(read_timeout : Number) : Number\n    self.read_timeout = read_timeout.seconds\n    read_timeout\n  end\n\n  # Sets the time to wait when writing before raising an `IO::TimeoutError`.\n  property write_timeout : Time::Span?\n\n  # Sets the number of seconds to wait when writing before raising an `IO::TimeoutError`.\n  @[Deprecated(\"Use `#write_timeout=(Time::Span?)` instead.\")]\n  def write_timeout=(write_timeout : Number) : Number\n    self.write_timeout = write_timeout.seconds\n    write_timeout\n  end\n\n  {% begin %}\n    # Creates a TCP socket. Consider using `TCPSocket` or `TCPServer` unless you\n    # need full control over the socket.\n    def self.tcp(family : Family, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil) : self\n      new(af: family, type: Type::STREAM, protocol: Protocol::TCP, blocking: blocking)\n    end\n  {% end %}\n\n  {% begin %}\n    # Creates an UDP socket. Consider using `UDPSocket` unless you need full\n    # control over the socket.\n    def self.udp(family : Family, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil) : self\n      new(af: family, type: Type::DGRAM, protocol: Protocol::UDP, blocking: blocking)\n    end\n  {% end %}\n\n  {% begin %}\n    # Creates an UNIX socket. Consider using `UNIXSocket` or `UNIXServer` unless\n    # you need full control over the socket.\n    def self.unix(type : Type = Type::STREAM, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil) : self\n      new(af: Family::UNIX, type: type, protocol: Protocol::IP, blocking: blocking)\n    end\n  {% end %}\n\n  {% begin %}\n    # Creates a socket. Consider using `TCPSocket`, `TCPServer`, `UDPSocket`,\n    # `UNIXSocket` or `UNIXServer` unless you need full control over the socket.\n    def initialize(family : Family, type : Type, protocol : Protocol = Protocol::IP, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil)\n      # This method is `#initialize` instead of `.new` because it is used as super\n      # constructor from subclasses.\n      initialize(af: family, type: type, protocol: protocol, blocking: blocking)\n    end\n  {% end %}\n\n  # :nodoc:\n  #\n  # Internal initializer for the above constructors to avoid deprecation\n  # warnings on the blocking arg.\n  protected def initialize(*, af : Family, type : Type, protocol : Protocol, blocking)\n    fd, blocking = Crystal::EventLoop.current.socket(af, type, protocol, blocking)\n    initialize(handle: fd, family: af, type: type, protocol: protocol, blocking: blocking)\n    self.sync = true\n  end\n\n  {% begin %}\n    # Creates a Socket from an existing system file descriptor or socket handle.\n    #\n    # This adopts *fd* into the IO system that will reconfigure it as per the\n    # event loop runtime requirements.\n    #\n    # NOTE: On Windows, the handle must have been created with\n    # `WSA_FLAG_OVERLAPPED`.\n    def initialize(fd, @family : Family, @type : Type, @protocol : Protocol = Protocol::IP, {% if compare_versions(Crystal::VERSION, \"1.5.0\") >= 0 %} @[Deprecated(\"Use Socket.set_blocking instead.\")] {% end %} blocking = nil)\n      initialize(handle: fd, family: family, type: type, protocol: protocol)\n      blocking = Crystal::EventLoop.default_socket_blocking? if blocking.nil?\n      Crystal::System::Socket.set_blocking(fd, blocking) unless blocking\n      self.sync = true\n    end\n  {% end %}\n\n  # :nodoc:\n  #\n  # Internal constructor to initialize the bare socket. The *blocking* arg is\n  # purely informational.\n  def initialize(*, handle, @family, @type, @protocol, blocking = nil)\n    @volatile_fd = Atomic.new(handle)\n    @closed = false\n    initialize_handle(handle, blocking)\n  end\n\n  # Connects the socket to a remote host:port.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # sock = Socket.tcp(Socket::Family::INET)\n  # sock.connect \"crystal-lang.org\", 80\n  # ```\n  def connect(host : String, port : Int, connect_timeout = nil) : Nil\n    Addrinfo.resolve(host, port, @family, @type, @protocol) do |addrinfo|\n      connect(addrinfo, timeout: connect_timeout)\n    end\n  end\n\n  # Connects the socket to a remote address. Raises if the connection failed.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # sock = Socket.unix\n  # sock.connect Socket::UNIXAddress.new(\"/tmp/service.sock\")\n  # ```\n  def connect(addr, timeout = nil) : Nil\n    connect(addr, timeout) { |error| raise error }\n  end\n\n  # Tries to connect to a remote address. Yields an `IO::TimeoutError` or an\n  # `Socket::ConnectError` error if the connection failed.\n  def connect(addr, timeout = nil, &)\n    timeout = timeout.seconds unless timeout.is_a?(::Time::Span?)\n    result = @fd_lock.write { system_connect(addr, timeout) }\n    yield result if result.is_a?(Exception)\n  end\n\n  # Binds the socket to a local address.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # sock = Socket.tcp(Socket::Family::INET)\n  # sock.bind \"localhost\", 1234\n  # ```\n  def bind(host : String, port : Int) : Nil\n    Addrinfo.resolve(host, port, @family, @type, @protocol) do |addrinfo|\n      @fd_lock.reference { system_bind(addrinfo, \"#{host}:#{port}\") }\n    end\n  end\n\n  # Binds the socket on *port* to all local interfaces.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # sock = Socket.tcp(Socket::Family::INET6)\n  # sock.bind 1234\n  # ```\n  def bind(port : Int)\n    if family.inet?\n      address = \"0.0.0.0\"\n      address_and_port = \"0.0.0.0:#{port}\"\n    else\n      address = \"::\"\n      address_and_port = \"[::]:#{port}\"\n    end\n\n    Addrinfo.resolve(address, port, @family, @type, @protocol) do |addrinfo|\n      @fd_lock.reference { system_bind(addrinfo, address_and_port) }\n    end\n  end\n\n  # Binds the socket to a local address.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # sock = Socket.udp(Socket::Family::INET)\n  # sock.bind Socket::IPAddress.new(\"192.168.1.25\", 80)\n  # ```\n  def bind(addr : Socket::Address) : Nil\n    if error = @fd_lock.reference { system_bind(addr, addr.to_s) }\n      raise error\n    end\n  end\n\n  # Tells the previously bound socket to listen for incoming connections.\n  def listen(backlog : Int = SOMAXCONN) : Nil\n    listen(backlog) { |errno| raise errno }\n  end\n\n  # Tries to listen for connections on the previously bound socket.\n  # Yields an `Socket::Error` on failure.\n  def listen(backlog : Int = SOMAXCONN, &)\n    if error = @fd_lock.reference { system_listen(backlog) }\n      yield error\n    end\n  end\n\n  # Accepts an incoming connection.\n  #\n  # Returns the client socket. Raises an `IO::Error` (closed stream) exception\n  # if the server is closed after invoking this method.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = TCPServer.new(2202)\n  # socket = server.accept\n  # socket.puts Time.utc\n  # socket.close\n  # ```\n  def accept : Socket\n    accept? || raise Socket::Error.new(\"Closed stream\")\n  end\n\n  # Accepts an incoming connection.\n  #\n  # Returns the client `Socket` or `nil` if the server is closed after invoking\n  # this method.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = TCPServer.new(2202)\n  # if socket = server.accept?\n  #   socket.puts Time.utc\n  #   socket.close\n  # end\n  # ```\n  def accept? : Socket?\n    return if closed?\n\n    if rs = @fd_lock.read { system_accept }\n      sock = Socket.new(handle: rs[0], family: family, type: type, protocol: protocol, blocking: rs[1])\n      unless (blocking = system_blocking?) == rs[1]\n        # FIXME: unlike the overloads in TCPServer and UNIXServer, this version\n        # carries the blocking mode from the server socket to the client socket\n        Crystal::System::Socket.set_blocking(fd, blocking)\n      end\n      sock.sync = sync?\n      sock\n    end\n  end\n\n  # Sends a message to a previously connected remote address.\n  # Returns the number of bytes sent.\n  # Does not guarantee that the entire message is sent. That's only the case\n  # when the return value is equivalent to `message.bytesize`.\n  # `#write` ensures the entire message is sent.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # sock = Socket.udp(Socket::Family::INET)\n  # sock.connect(\"example.com\", 2000)\n  # sock.send(\"text message\")\n  #\n  # sock = Socket.unix(Socket::Type::DGRAM)\n  # sock.connect Socket::UNIXAddress.new(\"/tmp/service.sock\")\n  # sock.send(Bytes[0])\n  # ```\n  def send(message) : Int32\n    @fd_lock.write { system_write(message.to_slice) }\n  end\n\n  # Sends a message to the specified remote address.\n  # Returns the number of bytes sent.\n  # Does not guarantee that the entire message is sent. That's only the case\n  # when the return value is equivalent to `message.bytesize`.\n  # `#write` ensures the entire message is sent but it requires an established connection.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = Socket::IPAddress.new(\"10.0.3.1\", 2022)\n  # sock = Socket.udp(Socket::Family::INET)\n  # sock.connect(\"example.com\", 2000)\n  # sock.send(\"text query\", to: server)\n  # ```\n  def send(message, to addr : Address) : Int32\n    @fd_lock.write { system_send_to(message.to_slice, addr) }\n  end\n\n  # Receives a text message from the previously bound address.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = Socket.udp(Socket::Family::INET)\n  # server.bind(\"localhost\", 1234)\n  #\n  # message, client_addr = server.receive\n  # ```\n  def receive(max_message_size = 512) : {String, Address}\n    address = nil\n    message = String.new(max_message_size) do |buffer|\n      bytes_read, address = @fd_lock.read do\n        system_receive_from(Slice.new(buffer, max_message_size))\n      end\n      {bytes_read, 0}\n    end\n    {message, address.as(Address)}\n  end\n\n  # Receives a binary message from the previously bound address.\n  #\n  # ```\n  # require \"socket\"\n  #\n  # server = Socket.udp(Socket::Family::INET)\n  # server.bind(\"localhost\", 1234)\n  #\n  # message = Bytes.new(32)\n  # bytes_read, client_addr = server.receive(message)\n  # ```\n  def receive(message : Bytes) : {Int32, Address}\n    @fd_lock.read { system_receive_from(message) }\n  end\n\n  # Calls `shutdown(2)` with `SHUT_RD`\n  def close_read\n    @fd_lock.reference { system_close_read }\n  end\n\n  # Calls `shutdown(2)` with `SHUT_WR`\n  def close_write\n    @fd_lock.reference { system_close_write }\n  end\n\n  def inspect(io : IO) : Nil\n    io << \"#<#{self.class}:fd #{fd}>\"\n  end\n\n  def send_buffer_size : Int32\n    system_send_buffer_size\n  end\n\n  def send_buffer_size=(val : Int32)\n    self.system_send_buffer_size = val\n    val\n  end\n\n  def recv_buffer_size : Int32\n    system_recv_buffer_size\n  end\n\n  def recv_buffer_size=(val : Int32)\n    self.system_recv_buffer_size = val\n    val\n  end\n\n  def reuse_address? : Bool\n    system_reuse_address?\n  end\n\n  def reuse_address=(val : Bool)\n    self.system_reuse_address = val\n    val\n  end\n\n  def reuse_port? : Bool\n    system_reuse_port?\n  end\n\n  def reuse_port=(val : Bool)\n    self.system_reuse_port = val\n    val\n  end\n\n  def broadcast? : Bool\n    system_broadcast?\n  end\n\n  def broadcast=(val : Bool)\n    self.system_broadcast = val\n    val\n  end\n\n  def keepalive?\n    system_keepalive?\n  end\n\n  def keepalive=(val : Bool)\n    self.system_keepalive = val\n    val\n  end\n\n  def linger\n    system_linger\n  end\n\n  # WARNING: The behavior of `SO_LINGER` is platform specific.\n  # Bad things may happen especially with nonblocking sockets.\n  # See [Cross-Platform Testing of SO_LINGER by Nybek](https://www.nybek.com/blog/2015/04/29/so_linger-on-non-blocking-sockets/)\n  # for more information.\n  #\n  # * `nil`: disable `SO_LINGER`\n  # * `Int`: enable `SO_LINGER` and set timeout to `Int` seconds\n  #   * `0`: abort on close (socket buffer is discarded and RST sent to peer). Depends on platform and whether `shutdown()` was called first.\n  #   * `>=1`: abort after `Int` seconds on close. Linux and Cygwin may block on close.\n  def linger=(val : Int?)\n    self.system_linger = val\n  end\n\n  # Returns the modified *optval*.\n  protected def getsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    system_getsockopt(optname, optval, level)\n  end\n\n  protected def getsockopt(optname, optval, level = LibC::SOL_SOCKET, &)\n    system_getsockopt(optname, optval, level) { |value| yield value }\n  end\n\n  protected def setsockopt(optname, optval, level = LibC::SOL_SOCKET)\n    @fd_lock.reference { system_setsockopt(optname, optval, level) }\n  end\n\n  private def getsockopt_bool(optname, level = LibC::SOL_SOCKET)\n    ret = system_getsockopt optname, 0, level\n    ret != 0\n  end\n\n  private def setsockopt_bool(optname, optval : Bool, level = LibC::SOL_SOCKET)\n    v = optval ? 1 : 0\n    @fd_lock.reference { system_setsockopt(optname, v, level) }\n    optval\n  end\n\n  # Returns whether the socket's mode is blocking (true) or non blocking (false).\n  @[Deprecated(\"Use Socket.get_blocking instead.\")]\n  def blocking\n    system_blocking?\n  end\n\n  # Changes the socket's mode to blocking (true) or non blocking (false).\n  #\n  # WARNING: The socket has been configured to behave correctly with the event\n  # loop runtime requirements. Changing the blocking mode can cause the event\n  # loop to misbehave, for example block the entire program when a fiber tries\n  # to read from this socket.\n  @[Deprecated(\"Use Socket.set_blocking instead.\")]\n  def blocking=(value)\n    @fd_lock.reference { self.system_blocking = value }\n  end\n\n  # Returns whether the blocking mode of *fd* is blocking (true) or non blocking\n  # (false).\n  #\n  # NOTE: Only implemented on UNIX targets. Raises on Windows.\n  def self.get_blocking(fd : Handle) : Bool\n    Crystal::System::Socket.get_blocking(fd)\n  end\n\n  # Changes the blocking mode of *fd* to be blocking (true) or non blocking\n  # (false).\n  def self.set_blocking(fd : Handle, value : Bool)\n    Crystal::System::Socket.set_blocking(fd, value)\n  end\n\n  def close_on_exec?\n    system_close_on_exec?\n  end\n\n  def close_on_exec=(arg : Bool)\n    @fd_lock.reference { self.system_close_on_exec = arg }\n  end\n\n  def self.fcntl(fd, cmd, arg = 0)\n    Crystal::System::Socket.fcntl(fd, cmd, arg)\n  end\n\n  def fcntl(cmd, arg = 0)\n    @fd_lock.reference { system_fcntl(cmd, arg) }\n  end\n\n  # Finalizes the socket resource.\n  #\n  # This involves releasing the handle to the operating system, i.e. closing it.\n  # It does *not* implicitly call `#flush`, so data waiting in the buffer may be\n  # lost. By default write buffering is disabled, though (`sync? == true`).\n  # It's recommended to always close the socket explicitly via `#close`.\n  #\n  # This method is a no-op if the file descriptor has already been closed.\n  def finalize\n    return if closed?\n\n    Crystal::EventLoop.remove(self)\n    socket_close { } # ignore error\n  end\n\n  def closed? : Bool\n    @closed\n  end\n\n  def tty?\n    system_tty?\n  end\n\n  private def unbuffered_read(slice : Bytes) : Int32\n    @fd_lock.read { system_read(slice) }\n  end\n\n  private def unbuffered_write(slice : Bytes) : Nil\n    until slice.empty?\n      slice += @fd_lock.write { system_write(slice) }\n    end\n  end\n\n  private def unbuffered_rewind : Nil\n    raise Socket::Error.new(\"Can't rewind\")\n  end\n\n  private def unbuffered_close : Nil\n    return if @closed\n\n    @closed = true\n\n    if @fd_lock.try_close? { event_loop.shutdown(self) }\n      event_loop.close(self)\n      @fd_lock.reset\n    end\n  end\n\n  private def unbuffered_flush : Nil\n    # Nothing\n  end\nend\n\nrequire \"./socket/*\"\n"
  },
  {
    "path": "src/spec/cli.cr",
    "content": "require \"option_parser\"\nrequire \"colorize\"\n\n# This file is included in the compiler to add usage instructions for the\n# spec runner on `crystal spec --help`.\n\nmodule Spec\n  # :nodoc:\n  #\n  # Configuration for a spec runner. More global state is defined in `./dsl.cr`.\n  class CLI\n    getter pattern : Regex?\n    getter line : Int32?\n    getter slowest : Int32?\n    getter? fail_fast = false\n    property? focus = false\n    getter? dry_run = false\n    getter? list_tags = false\n    getter? color : Bool\n\n    getter stdout : IO\n    getter stderr : IO\n\n    def initialize(@stdout : IO = STDOUT, @stderr : IO = STDERR)\n      @color = Colorize.default_enabled?(@stdout, @stderr)\n    end\n\n    def add_location(file, line)\n      locations = @locations ||= {} of String => Array(Int32)\n      locations.put_if_absent(File.expand_path(file)) { [] of Int32 } << line\n    end\n\n    def add_tag(tag)\n      if anti_tag = tag.lchop?('~')\n        (@anti_tags ||= Set(String).new) << anti_tag\n      else\n        (@tags ||= Set(String).new) << tag\n      end\n    end\n\n    getter randomizer_seed : UInt64?\n    getter randomizer : Random::PCG32?\n\n    def order=(mode)\n      seed =\n        case mode\n        when \"default\"\n          nil\n        when \"random\"\n          Random::Secure.rand(1..99999).to_u64 # 5 digits or less for simplicity\n        when UInt64\n          mode\n        else\n          raise ArgumentError.new(\"Order must be either 'default', 'random', or a numeric seed value\")\n        end\n\n      @randomizer_seed = seed\n      @randomizer = seed ? Random::PCG32.new(seed) : nil\n    end\n\n    def option_parser : OptionParser\n      @option_parser ||= OptionParser.new do |opts|\n        opts.banner = \"crystal spec runner\"\n        opts.on(\"-e\", \"--example STRING\", \"run examples whose full nested names include STRING\") do |pattern|\n          @pattern = Regex.new(Regex.escape(pattern))\n        end\n        opts.on(\"-l\", \"--line LINE\", \"run examples whose line matches LINE\") do |line|\n          @line = line.to_i\n        end\n        opts.on(\"-p\", \"--profile\", \"Print the 10 slowest specs\") do\n          @slowest = 10\n        end\n        opts.on(\"--fail-fast\", \"abort the run on first failure\") do\n          @fail_fast = true\n        end\n        opts.on(\"--location file:line\", \"run example at line 'line' in file 'file', multiple allowed\") do |location|\n          if location =~ /\\A(.+?)\\:(\\d+)\\Z/\n            add_location $1, $2.to_i\n          else\n            abort \"location #{location} must be file:line\"\n          end\n        end\n        opts.on(\"--tag TAG\", \"run examples with the specified TAG, or exclude examples by adding ~ before the TAG.\") do |tag|\n          add_tag tag\n        end\n        opts.on(\"--list-tags\", \"lists all the tags used.\") do\n          @list_tags = true\n        end\n        opts.on(\"--order MODE\", \"run examples in random order by passing MODE as 'random' or to a specific seed by passing MODE as the seed value\") do |mode|\n          if mode.in?(\"default\", \"random\")\n            self.order = mode\n          elsif seed = mode.to_u64?\n            self.order = seed\n          else\n            abort(\"order must be either 'default', 'random', or a numeric seed value\")\n          end\n        end\n        opts.on(\"--junit_output OUTPUT_PATH\", \"generate JUnit XML output within the given OUTPUT_PATH\") do |output_path|\n          configure_formatter(\"junit\", output_path)\n        end\n        opts.on(\"-h\", \"--help\", \"show this help\") do |pattern|\n          @stdout.puts opts\n          exit\n        end\n        opts.on(\"-v\", \"--verbose\", \"verbose output\") do\n          configure_formatter(\"verbose\")\n        end\n        opts.on(\"--tap\", \"Generate TAP output (Test Anything Protocol)\") do\n          configure_formatter(\"tap\")\n        end\n        opts.on(\"--color\", \"Enabled ANSI colored output\") do\n          @color = true\n        end\n        opts.on(\"--no-color\", \"Disable ANSI colored output\") do\n          @color = false\n        end\n        opts.on(\"--dry-run\", \"Pass all tests without execution\") do\n          @dry_run = true\n        end\n        opts.unknown_args do |args|\n        end\n      end\n    end\n\n    # Blank implementation to reduce the interface of spec's option parser for\n    # inclusion in the compiler. This avoids depending on more of `Spec`\n    # module.\n    # The real implementation in `../spec.cr` overrides this for actual use.\n    def configure_formatter(formatter, output_path = nil)\n    end\n\n    private def abort(msg)\n      @stderr.puts msg\n      exit 1\n    end\n  end\n\n  @[Deprecated(\"This is an internal API.\")]\n  def self.randomizer : Random::PCG32?\n    @@cli.randomizer\n  end\nend\n"
  },
  {
    "path": "src/spec/context.cr",
    "content": "require \"./item\"\n\nmodule Spec\n  # Base type for `ExampleGroup`.\n  abstract class Context\n    # All the children, which can be `describe`/`context` or `it`\n    getter children = [] of ExampleGroup | Example\n\n    protected abstract def cli : CLI\n\n    def randomize(randomizer)\n      children.each do |child|\n        child.randomize(randomizer) if child.is_a?(ExampleGroup)\n      end\n      children.shuffle!(randomizer)\n    end\n\n    protected def internal_run\n      run_before_all_hooks\n      children.each &.run\n      run_after_all_hooks\n    end\n\n    protected def before_each(&block)\n      (@before_each ||= [] of ->) << block\n    end\n\n    protected def run_before_each_hooks\n      @before_each.try &.each &.call\n    end\n\n    protected def after_each(&block)\n      (@after_each ||= [] of ->) << block\n    end\n\n    protected def run_after_each_hooks\n      @after_each.try &.reverse_each &.call\n    end\n\n    protected def before_all(&block)\n      (@before_all ||= [] of ->) << block\n    end\n\n    protected def run_before_all_hooks\n      @before_all.try &.each &.call\n    end\n\n    protected def after_all(&block)\n      (@after_all ||= [] of ->) << block\n    end\n\n    protected def run_after_all_hooks\n      @after_all.try &.reverse_each &.call\n    end\n\n    protected def around_each(&block : Example::Procsy ->)\n      (@around_each ||= [] of Example::Procsy ->) << block\n    end\n\n    protected def run_around_each_hooks(procsy : Example::Procsy) : Bool\n      internal_run_around_each_hooks(procsy)\n    end\n\n    protected def internal_run_around_each_hooks(procsy : Example::Procsy) : Bool\n      around_each = @around_each\n      return false unless around_each\n\n      run_around_each_hook(around_each, procsy, 0)\n      true\n    end\n\n    protected def run_around_each_hook(around_each, procsy, index) : Nil\n      around_each[index].call(\n        if index == around_each.size - 1\n          # If we don't have any more hooks after this one, call the procsy\n          procsy\n        else\n          # Otherwise, create a procsy that will invoke the next hook\n          Example::Procsy.new(procsy.example) do\n            run_around_each_hook(around_each, procsy, index + 1)\n          end\n        end\n      )\n    end\n\n    protected def around_all(&block : ExampleGroup::Procsy ->)\n      (@around_all ||= [] of ExampleGroup::Procsy ->) << block\n    end\n\n    protected def run_around_all_hooks(procsy : ExampleGroup::Procsy) : Bool\n      around_all = @around_all\n      return false unless around_all\n\n      run_around_all_hook(around_all, procsy, 0)\n      true\n    end\n\n    protected def run_around_all_hook(around_all, procsy, index) : Nil\n      around_all[index].call(\n        if index == around_all.size - 1\n          # If we don't have any more hooks after this one, call the procsy\n          procsy\n        else\n          # Otherwise, create a procsy that will invoke the next hook\n          ExampleGroup::Procsy.new(procsy.example_group) do\n            run_around_all_hook(around_all, procsy, index + 1)\n          end\n        end\n      )\n    end\n  end\n\n  # :nodoc:\n  enum Status\n    Success\n    Fail\n    Error\n    Pending\n\n    def color : Colorize::Color\n      case self\n      in Success then Colorize::ColorANSI::Green\n      in Fail    then Colorize::ColorANSI::Red\n      in Error   then Colorize::ColorANSI::Red\n      in Pending then Colorize::ColorANSI::Yellow\n      end\n    end\n\n    def letter : Char\n      case self\n      in Success then '.'\n      in Fail    then 'F'\n      in Error   then 'E'\n      in Pending then '*'\n      end\n    end\n  end\n\n  # :nodoc:\n  record Result,\n    kind : Status,\n    description : String,\n    file : String,\n    line : Int32,\n    elapsed : Time::Span?,\n    exception : Exception?\n\n  # :nodoc:\n  class CLI\n    getter root_context : RootContext { RootContext.new(self) }\n    property current_context : Context { root_context }\n  end\n\n  # :nodoc:\n  #\n  # The root context is the main interface that the spec DSL interacts with.\n  class RootContext < Context\n    @results : Hash(Status, Array(Result))\n\n    protected getter cli : CLI\n\n    def results_for(status : Status)\n      @results[status]\n    end\n\n    def initialize(@cli : CLI)\n      @results = Status.values.to_h { |status| {status, [] of Result} }\n    end\n\n    def run\n      print_order_message(cli.stdout)\n\n      internal_run\n    end\n\n    def report(status : Status, full_description, file, line, elapsed = nil, ex = nil)\n      result = Result.new(status, full_description, file, line, elapsed, ex)\n\n      report_formatters result\n\n      @results[status] << result\n    end\n\n    def report_formatters(result)\n      cli.formatters.each(&.report(result))\n    end\n\n    def succeeded\n      results_for(:fail).empty? && results_for(:error).empty?\n    end\n\n    def finish(elapsed_time, aborted = false)\n      cli.formatters.each(&.finish(elapsed_time, aborted))\n      if cli.formatters.any?(&.should_print_summary?)\n        print_summary(cli.stdout, elapsed_time, aborted)\n      end\n    end\n\n    def print_summary(io : IO, elapsed_time, aborted = false)\n      pendings = results_for(:pending)\n      unless pendings.empty?\n        io.puts\n        io.puts \"Pending:\"\n        pendings.each do |pending|\n          io.puts cli.colorize(\"  #{pending.description}\", :pending)\n        end\n      end\n\n      failures = results_for(:fail)\n      errors = results_for(:error)\n\n      cwd = Dir.current\n\n      failures_and_errors = failures + errors\n      unless failures_and_errors.empty?\n        io.puts\n        io.puts \"Failures:\"\n        failures_and_errors.each_with_index do |fail, i|\n          if ex = fail.exception\n            io.puts\n            io.puts \"#{(i + 1).to_s.rjust(3, ' ')}) #{fail.description}\"\n\n            if ex.is_a?(SpecError)\n              source_line = Spec.read_line(ex.file, ex.line)\n              if source_line\n                io.puts cli.colorize(\"     Failure/Error: #{source_line.strip}\", :error)\n              end\n            end\n            io.puts\n\n            message = ex.is_a?(SpecError) ? ex.to_s : ex.inspect_with_backtrace\n            message.split('\\n') do |line|\n              io.print \"       \"\n              io.puts cli.colorize(line, :error)\n            end\n\n            if ex.is_a?(SpecError)\n              io.puts\n              io.puts cli.colorize(\"     # #{Path[ex.file].relative_to(cwd)}:#{ex.line}\", :comment)\n            end\n          end\n        end\n      end\n\n      if cli.slowest\n        io.puts\n        results = results_for(:success) + results_for(:fail)\n        top_n = results.sort_by { |res| -res.elapsed.not_nil!.to_f }[0..cli.slowest.not_nil!]\n        top_n_time = top_n.sum &.elapsed.not_nil!.total_seconds\n        percent = (top_n_time * 100) / elapsed_time.total_seconds\n        io.puts \"Top #{cli.slowest} slowest examples (#{top_n_time.humanize} seconds, #{percent.round(2)}% of total time):\"\n        top_n.each do |res|\n          io.puts \"  #{res.description}\"\n          res_elapsed = res.elapsed.not_nil!.total_seconds.humanize\n          io.puts \"    #{res_elapsed.colorize.bold.toggle(cli.color?)} seconds #{Path[res.file].relative_to(cwd)}:#{res.line}\"\n        end\n      end\n\n      io.puts\n\n      success = results_for(:success)\n      total = pendings.size + failures.size + errors.size + success.size\n\n      final_status = case\n                     when aborted                           then Status::Error\n                     when (failures.size + errors.size) > 0 then Status::Fail\n                     when pendings.size > 0                 then Status::Pending\n                     else                                        Status::Success\n                     end\n\n      io.puts \"Aborted!\".colorize.red.toggle(cli.color?) if aborted\n      io.puts \"Finished in #{Spec.to_human(elapsed_time)}\"\n      io.puts cli.colorize(\"#{total} examples, #{failures.size} failures, #{errors.size} errors, #{pendings.size} pending\", final_status)\n      io.puts cli.colorize(\"Only running `focus: true`\", :focus) if cli.focus?\n\n      unless failures_and_errors.empty?\n        io.puts\n        io.puts \"Failed examples:\"\n        io.puts\n        failures_and_errors.each do |fail|\n          io.print cli.colorize(\"crystal spec #{Path[fail.file].relative_to(cwd)}:#{fail.line}\", :error)\n          io.puts cli.colorize(\" # #{fail.description}\", :comment)\n        end\n      end\n\n      print_order_message(io)\n    end\n\n    def print_order_message(io : IO)\n      if randomizer_seed = cli.randomizer_seed\n        io.puts cli.colorize(\"Randomized with seed: #{randomizer_seed}\", :order)\n      end\n    end\n\n    def describe(description, file, line, end_line, focus, tags, &block)\n      cli.focus = true if focus\n\n      context = Spec::ExampleGroup.new(cli.current_context, description, file, line, end_line, focus, tags)\n      cli.current_context.children << context\n\n      old_context = cli.current_context\n      cli.current_context = context\n      begin\n        block.call\n      ensure\n        cli.current_context = old_context\n      end\n    end\n\n    def it(description, file, line, end_line, focus, tags, &block)\n      add_example(description, file, line, end_line, focus, tags, block)\n    end\n\n    def pending(description, file, line, end_line, focus, tags)\n      add_example(description, file, line, end_line, focus, tags, nil)\n    end\n\n    private def add_example(description, file, line, end_line, focus, tags, block)\n      check_nesting_spec(file, line) do\n        cli.focus = true if focus\n        cli.current_context.children <<\n          Example.new(cli.current_context, description, file, line, end_line, focus, tags, block)\n      end\n    end\n\n    @spec_nesting = false\n\n    def check_nesting_spec(file, line, &block)\n      raise NestingSpecError.new(\"Can't nest `it` or `pending`\", file, line) if @spec_nesting\n\n      @spec_nesting = true\n      begin\n        yield\n      ensure\n        @spec_nesting = false\n      end\n    end\n\n    protected def around_all(&block : ExampleGroup::Procsy ->)\n      raise \"Can't call `around_all` outside of a describe/context\"\n    end\n  end\n\n  # Represents a `describe` or `context`.\n  class ExampleGroup < Context\n    include Item\n\n    def initialize(@parent : Context, @description : String,\n                   @file : String, @line : Int32, @end_line : Int32,\n                   @focus : Bool, tags)\n      initialize_tags(tags)\n    end\n\n    # :nodoc:\n    def cli : CLI\n      @parent.cli\n    end\n\n    # :nodoc:\n    def run\n      cli.formatters.each(&.push(self))\n\n      ran = run_around_all_hooks(ExampleGroup::Procsy.new(self) { internal_run })\n      ran || internal_run\n\n      cli.formatters.each(&.pop)\n    end\n\n    protected def report(status : Status, description, file, line, elapsed = nil, ex = nil)\n      parent.report status, \"#{@description} #{description}\", file, line, elapsed, ex\n    end\n\n    protected def run_before_each_hooks\n      @parent.run_before_each_hooks\n      super\n    end\n\n    protected def run_after_each_hooks\n      super\n      @parent.run_after_each_hooks\n    end\n\n    protected def run_around_each_hooks(procsy : Example::Procsy) : Bool\n      ran = @parent.run_around_each_hooks(Example::Procsy.new(procsy.example) do\n        if @around_each\n          # If we have around callbacks we execute them, and it will\n          # eventually run the example\n          internal_run_around_each_hooks(procsy)\n        else\n          # Otherwise we have to run the example now, because the parent\n          # around hooks won't run it\n          procsy.run\n        end\n      end)\n      ran || internal_run_around_each_hooks(procsy)\n    end\n  end\nend\n\nrequire \"./example_group/procsy\"\n"
  },
  {
    "path": "src/spec/dsl.cr",
    "content": "require \"colorize\"\nrequire \"option_parser\"\n\nmodule Spec\n  # :nodoc:\n  # The default spec runner.\n  class_getter cli = CLI.new\n\n  # :nodoc:\n  enum InfoKind\n    Comment\n    Focus\n    Order\n\n    def color : Colorize::Color\n      Colorize::ColorANSI::Cyan\n    end\n  end\n\n  # :nodoc:\n  class SpecError < Exception\n    getter file : String\n    getter line : Int32\n\n    def initialize(message, @file, @line)\n      super(message)\n    end\n  end\n\n  # :nodoc:\n  class AssertionFailed < SpecError\n  end\n\n  # :nodoc:\n  class ExamplePending < SpecError\n  end\n\n  # :nodoc:\n  class NestingSpecError < SpecError\n  end\n\n  # :nodoc:\n  def self.to_human(span : Time::Span)\n    total_milliseconds = span.total_milliseconds\n    if total_milliseconds < 1\n      return \"#{(span.total_milliseconds * 1000).round.to_i} microseconds\"\n    end\n\n    total_seconds = span.total_seconds\n    if total_seconds < 1\n      return \"#{span.total_milliseconds.round(2)} milliseconds\"\n    end\n\n    if total_seconds < 60\n      return \"#{total_seconds.round(2)} seconds\"\n    end\n\n    minutes = span.minutes\n    seconds = span.seconds\n    \"#{minutes}:#{seconds < 10 ? \"0\" : \"\"}#{seconds} minutes\"\n  end\n\n  record SplitFilter, remainder : Int32, quotient : Int32\n\n  # Instructs the spec runner to execute the given block\n  # before each spec in the spec suite.\n  #\n  # If multiple blocks are registered they run in the order\n  # that they are given.\n  #\n  # For example:\n  #\n  # ```\n  # Spec.before_each { puts 1 }\n  # Spec.before_each { puts 2 }\n  # ```\n  #\n  # will print, just before each spec, 1 and then 2.\n  def self.before_each(&block)\n    @@cli.root_context.before_each(&block)\n  end\n\n  # Instructs the spec runner to execute the given block\n  # after each spec in the spec suite.\n  #\n  # If multiple blocks are registered they run in the reversed\n  # order that they are given.\n  #\n  # For example:\n  #\n  # ```\n  # Spec.after_each { puts 1 }\n  # Spec.after_each { puts 2 }\n  # ```\n  #\n  # will print, just after each spec, 2 and then 1.\n  def self.after_each(&block)\n    @@cli.root_context.after_each(&block)\n  end\n\n  # Instructs the spec runner to execute the given block\n  # before the entire spec suite.\n  #\n  # If multiple blocks are registered they run in the order\n  # that they are given.\n  #\n  # For example:\n  #\n  # ```\n  # Spec.before_suite { puts 1 }\n  # Spec.before_suite { puts 2 }\n  # ```\n  #\n  # will print, just before the spec suite starts, 1 and then 2.\n  def self.before_suite(&block)\n    @@cli.root_context.before_all(&block)\n  end\n\n  # Instructs the spec runner to execute the given block\n  # after the entire spec suite.\n  #\n  # If multiple blocks are registered they run in the reversed\n  # order that they are given.\n  #\n  # For example:\n  #\n  # ```\n  # Spec.after_suite { puts 1 }\n  # Spec.after_suite { puts 2 }\n  # ```\n  #\n  # will print, just after the spec suite ends, 2 and then 1.\n  def self.after_suite(&block)\n    @@cli.root_context.after_all(&block)\n  end\n\n  # Instructs the spec runner to execute the given block when each spec in the\n  # spec suite runs.\n  #\n  # The block must call `run` on the given `Example::Procsy` object.\n  #\n  # If multiple blocks are registered they run in the reversed\n  # order that they are given.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # Spec.around_each do |example|\n  #   puts \"runs before each sample\"\n  #   example.run\n  #   puts \"runs after each sample\"\n  # end\n  #\n  # it { }\n  # it { }\n  # ```\n  def self.around_each(&block : Example::Procsy ->)\n    @@cli.root_context.around_each(&block)\n  end\n\n  # :nodoc:\n  class CLI\n    @aborted = false\n\n    def abort!\n      @aborted = true\n      finish_run\n    end\n\n    @split_filter : SplitFilter? = nil\n\n    def add_split_filter(filter)\n      if filter\n        r, _, m = filter.partition('%')\n        @split_filter = SplitFilter.new(remainder: r.to_i, quotient: m.to_i)\n      else\n        @split_filter = nil\n      end\n    end\n\n    @start_time : Time::Instant? = nil\n\n    def run\n      @start_time = Time.instant\n\n      at_exit do |status|\n        # Do not run specs if the process is exiting on an error\n        next unless status == 0\n\n        begin\n          if list_tags?\n            execute_list_tags\n          else\n            execute_examples\n          end\n        rescue ex\n          @stderr.print \"Unhandled exception: \"\n          ex.inspect_with_backtrace(@stderr)\n          @stderr.flush\n          @aborted = true\n        ensure\n          finish_run unless list_tags?\n        end\n      end\n    end\n\n    def colorize(str, status : Status)\n      str.colorize(status.color).toggle(@color)\n    end\n\n    def colorize(str, kind : InfoKind)\n      str.colorize(kind.color).toggle(@color)\n    end\n\n    def execute_examples\n      log_setup\n      maybe_randomize\n      run_filters\n      root_context.run\n    end\n\n    def execute_list_tags\n      run_filters\n      tag_counts = collect_tags(root_context)\n      print_list_tags(tag_counts)\n    end\n\n    private def collect_tags(context) : Hash(String, Int32)\n      tag_counts = Hash(String, Int32).new(0)\n      collect_tags(tag_counts, context, Set(String).new)\n      tag_counts\n    end\n\n    private def collect_tags(tag_counts, context : Context, tags)\n      if context.responds_to?(:tags) && (context_tags = context.tags)\n        tags += context_tags\n      end\n      context.children.each do |child|\n        collect_tags(tag_counts, child, tags)\n      end\n    end\n\n    private def collect_tags(tag_counts, example : Example, tags)\n      if example_tags = example.tags\n        tags += example_tags\n      end\n      if tags.empty?\n        tag_counts.update(\"untagged\") { |count| count + 1 }\n      else\n        tags.tally(tag_counts)\n      end\n    end\n\n    private def print_list_tags(tag_counts : Hash(String, Int32)) : Nil\n      return if tag_counts.empty?\n      longest_name_size = tag_counts.keys.max_of(&.size)\n      tag_counts.to_a.sort_by! { |k, v| {-v, k} }.each do |tag_name, count|\n        @stdout.puts \"#{tag_name.rjust(longest_name_size)}: #{count}\"\n      end\n    end\n\n    # :nodoc:\n    #\n    # Workaround for #8914\n    private macro defined?(t)\n      {% if t.resolve? %}\n        {{ yield }}\n      {% end %}\n    end\n\n    # :nodoc:\n    def log_setup\n    end\n\n    # :nodoc:\n    macro finished\n      # :nodoc:\n      #\n      # Initialized the log module for the specs.\n      # If the \"log\" module is required it is configured to emit no entries by default.\n      def log_setup\n        defined?(::Log) do\n          if ::Log.responds_to?(:setup)\n            ::Log.setup_from_env(default_level: :none)\n          end\n        end\n      end\n    end\n\n    def finish_run\n      elapsed_time = @start_time.not_nil!.elapsed\n      root_context.finish(elapsed_time, @aborted)\n      exit 1 if !root_context.succeeded || @aborted || (focus? && ENV[\"SPEC_FOCUS_NO_FAIL\"]? != \"1\")\n    end\n\n    # :nodoc:\n    def maybe_randomize\n      if randomizer = @randomizer\n        root_context.randomize(randomizer)\n      end\n    end\n\n    # :nodoc:\n    def run_filters\n      root_context.run_filters(@pattern, @line, @locations, @split_filter, @focus, @tags, @anti_tags)\n    end\n  end\n\n  @[Deprecated(\"This is an internal API.\")]\n  def self.finish_run\n    @@cli.finish_run\n  end\n\n  @[Deprecated(\"This is an internal API.\")]\n  def self.add_split_filter(filter)\n    @@cli.add_split_filter(filter)\n  end\nend\n\nrequire \"./*\"\n"
  },
  {
    "path": "src/spec/example/procsy.cr",
    "content": "module Spec\n  class Example\n    # Wraps an `Example` and a `Proc` that will eventually execute the\n    # example.\n    struct Procsy\n      # The example that will eventually run when calling `run`.\n      getter example : Example\n\n      # :nodoc:\n      def initialize(@example : Example, &@proc : ->)\n      end\n\n      # Executes the wrapped example, possibly executing other\n      # `around_each` hooks before that.\n      def run\n        @proc.call\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/spec/example.cr",
    "content": "require \"./item\"\n\nmodule Spec\n  # Each example (`it`) in a spec suite.\n  class Example\n    include Item\n\n    # :nodoc:\n    getter block : (->) | Nil\n\n    # :nodoc:\n    def initialize(@parent : Context, @description : String,\n                   @file : String, @line : Int32, @end_line : Int32,\n                   @focus : Bool, tags,\n                   @block : (->) | Nil)\n      initialize_tags(tags)\n    end\n\n    # :nodoc:\n    def run\n      @parent.cli.root_context.check_nesting_spec(file, line) do\n        @parent.cli.formatters.each(&.before_example(description))\n\n        if @parent.cli.dry_run?\n          @parent.report(:success, description, file, line)\n          return\n        end\n\n        unless block = @block\n          @parent.report(:pending, description, file, line)\n          return\n        end\n\n        non_nil_block = block\n        start = Time.instant\n\n        ran = @parent.run_around_each_hooks(Example::Procsy.new(self) { internal_run(start, non_nil_block) })\n        ran || internal_run(start, non_nil_block)\n\n        # We do this to give a chance for signals (like CTRL+C) to be handled,\n        # which currently are only handled when there's a fiber switch\n        # (IO stuff, sleep, etc.). Without it the user might wait more than needed\n        # after pressing CTRL+C to quit the tests.\n        Fiber.yield\n      end\n    end\n\n    private def internal_run(start, block)\n      @parent.run_before_each_hooks\n      block.call\n      @parent.report(:success, description, file, line, start.elapsed)\n    rescue ex : Spec::AssertionFailed\n      @parent.report(:fail, description, file, line, start.elapsed, ex)\n      @parent.cli.abort! if @parent.cli.fail_fast?\n    rescue ex : Spec::ExamplePending\n      @parent.report(:pending, description, file, line, start.elapsed)\n    rescue ex\n      @parent.report(:error, description, file, line, start.elapsed, ex)\n      @parent.cli.abort! if @parent.cli.fail_fast?\n    ensure\n      @parent.run_after_each_hooks\n    end\n  end\nend\n\nrequire \"./example/procsy\"\n"
  },
  {
    "path": "src/spec/example_group/procsy.cr",
    "content": "module Spec\n  class ExampleGroup < Context\n    # Wraps an `ExampleGroup` and a `Proc` that will eventually execute the\n    # group.\n    struct Procsy\n      # The group that will eventually run when calling `run`.\n      getter example_group : ExampleGroup\n\n      # :nodoc:\n      def initialize(@example_group : ExampleGroup, &@proc : ->)\n      end\n\n      # Executes the wrapped example group, possibly executing other\n      # `around_all` hooks before that.\n      def run\n        @proc.call\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/spec/expectations.cr",
    "content": "module Spec\n  # :nodoc:\n  struct EqualExpectation(T)\n    def initialize(@expected_value : T)\n    end\n\n    def match(actual_value)\n      expected_value = @expected_value\n\n      # For the case of comparing strings we want to make sure that two strings\n      # are equal if their content is equal, but also their bytesize and size\n      # should be equal. Otherwise, an incorrect bytesize or size was used\n      # when creating them.\n      if actual_value.is_a?(String) && expected_value.is_a?(String)\n        actual_value == expected_value &&\n          actual_value.bytesize == expected_value.bytesize &&\n          actual_value.size == expected_value.size\n      else\n        actual_value == @expected_value\n      end\n    end\n\n    def failure_message(actual_value)\n      expected_value = @expected_value\n\n      # Check for the case of string equality when the content match\n      # but not the bytesize or size.\n      if actual_value.is_a?(String) &&\n         expected_value.is_a?(String) &&\n         actual_value == expected_value\n        if actual_value.bytesize != expected_value.bytesize\n          return <<-MSG\n            Expected bytesize: #{expected_value.bytesize}\n                 got bytesize: #{actual_value.bytesize}\n            MSG\n        end\n\n        <<-MSG\n        Expected size: #{expected_value.size}\n              got size: #{actual_value.size}\n        MSG\n      else\n        expected = expected_value.pretty_inspect\n        got = actual_value.pretty_inspect\n        if expected == got\n          expected += \" : #{@expected_value.class}\"\n          got += \" : #{actual_value.class}\"\n        end\n        \"Expected: #{expected}\\n     got: #{got}\"\n      end\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: actual_value != #{@expected_value.pretty_inspect}\\n     got: #{actual_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct BeExpectation(T)\n    def initialize(@expected_value : T)\n    end\n\n    def match(actual_value)\n      actual_value.same? @expected_value\n    end\n\n    def failure_message(actual_value)\n      \"Expected: #{@expected_value.pretty_inspect} (#{identify(@expected_value)})\\n     got: #{actual_value.pretty_inspect} (#{identify(actual_value)})\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: #{@expected_value.pretty_inspect} (#{identify(@expected_value)})\\n     got: #{actual_value.pretty_inspect} (#{identify(actual_value)})\"\n    end\n\n    private def identify(value)\n      if value.responds_to?(:to_unsafe)\n        if !value.responds_to?(:object_id)\n          return value.to_unsafe\n        end\n      end\n\n      \"object_id: #{value.object_id}\"\n    end\n  end\n\n  # :nodoc:\n  struct BeTruthyExpectation\n    def match(actual_value)\n      !!actual_value\n    end\n\n    def failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} to be truthy\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} not to be truthy\"\n    end\n  end\n\n  # :nodoc:\n  struct BeFalseyExpectation\n    def match(actual_value)\n      !actual_value\n    end\n\n    def failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} to be falsey\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} not to be falsey\"\n    end\n  end\n\n  # :nodoc:\n  struct BeNilExpectation\n    def match(actual_value)\n      actual_value.nil?\n    end\n\n    def failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} to be nil\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} not to be nil\"\n    end\n  end\n\n  # :nodoc:\n  struct CloseExpectation(T, D)\n    def initialize(@expected_value : T, @delta : D)\n    end\n\n    def match(actual_value)\n      (actual_value - @expected_value).abs <= @delta\n    end\n\n    def failure_message(actual_value)\n      \"Expected #{actual_value.pretty_inspect} to be within #{@delta} of #{@expected_value.pretty_inspect}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected #{actual_value.pretty_inspect} not to be within #{@delta} of #{@expected_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct BeAExpectation(T)\n    def match(actual_value)\n      actual_value.is_a?(T)\n    end\n\n    def failure_message(actual_value)\n      \"Expected #{actual_value.pretty_inspect} (#{actual_value.class}) to be a #{T}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected #{actual_value.pretty_inspect} (#{actual_value.class}) not to be a #{T}\"\n    end\n  end\n\n  # :nodoc:\n  struct Be(T)\n    enum Relation\n      LessThan\n      LessOrEqual\n      GreaterThan\n      GreaterOrEqual\n    end\n\n    def self.<(other)\n      Be.new(other, :less_than)\n    end\n\n    def self.<=(other)\n      Be.new(other, :less_or_equal)\n    end\n\n    def self.>(other)\n      Be.new(other, :greater_than)\n    end\n\n    def self.>=(other)\n      Be.new(other, :greater_or_equal)\n    end\n\n    def initialize(@expected_value : T, @op : Relation)\n    end\n\n    def match(actual_value)\n      case @op\n      in .less_than?\n        actual_value < @expected_value\n      in .less_or_equal?\n        actual_value <= @expected_value\n      in .greater_than?\n        actual_value > @expected_value\n      in .greater_or_equal?\n        actual_value >= @expected_value\n      end\n    end\n\n    def failure_message(actual_value)\n      \"Expected #{actual_value.pretty_inspect} to be #{@op} #{@expected_value.pretty_inspect}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected #{actual_value.pretty_inspect} not to be #{@op} #{@expected_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct MatchExpectation(T)\n    def initialize(@expected_value : T)\n    end\n\n    def match(actual_value)\n      actual_value =~ @expected_value\n    end\n\n    def failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect}\\nto match: #{@expected_value.pretty_inspect}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: value #{actual_value.pretty_inspect}\\n to not match: #{@expected_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct ContainExpectation(T)\n    def initialize(@expected_value : T)\n    end\n\n    def match(actual_value)\n      actual_value.includes?(@expected_value)\n    end\n\n    def failure_message(actual_value)\n      \"Expected:   #{actual_value.pretty_inspect}\\nto include: #{@expected_value.pretty_inspect}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: value #{actual_value.pretty_inspect}\\nto not include: #{@expected_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct StartWithExpectation(T)\n    def initialize(@expected_value : T)\n    end\n\n    def match(actual_value)\n      actual_value.starts_with?(@expected_value)\n    end\n\n    def failure_message(actual_value)\n      \"Expected:   #{actual_value.pretty_inspect}\\nto start with: #{@expected_value.pretty_inspect}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: value #{actual_value.pretty_inspect}\\nnot to start with: #{@expected_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct EndWithExpectation(T)\n    def initialize(@expected_value : T)\n    end\n\n    def match(actual_value)\n      actual_value.ends_with?(@expected_value)\n    end\n\n    def failure_message(actual_value)\n      \"Expected:   #{actual_value.pretty_inspect}\\nto end with: #{@expected_value.pretty_inspect}\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: value #{actual_value.pretty_inspect}\\nnot to end with: #{@expected_value.pretty_inspect}\"\n    end\n  end\n\n  # :nodoc:\n  struct BeEmptyExpectation\n    def match(actual_value)\n      actual_value.empty?\n    end\n\n    def failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} to be empty\"\n    end\n\n    def negative_failure_message(actual_value)\n      \"Expected: #{actual_value.pretty_inspect} not to be empty\"\n    end\n  end\n\n  # This module defines a number of methods to create expectations, which are\n  # automatically included into the top level namespace.\n  #\n  # Expectations are used by `Spec::ObjectExtensions#should` and `Spec::ObjectExtensions#should_not`.\n  module Expectations\n    # Creates an `Expectation` that passes if actual equals *value* (`==`).\n    def eq(value)\n      Spec::EqualExpectation.new value\n    end\n\n    # Creates an `Expectation` that passes if actual and *value* are identical (`.same?`).\n    def be(value)\n      Spec::BeExpectation.new value\n    end\n\n    # Creates an `Expectation` that passes if actual is true (`== true`).\n    def be_true\n      eq true\n    end\n\n    # Creates an `Expectation` that passes if actual is false (`== false`).\n    def be_false\n      eq false\n    end\n\n    # Creates an `Expectation` that passes if actual is truthy (neither `nil` nor `false`).\n    def be_truthy\n      Spec::BeTruthyExpectation.new\n    end\n\n    # Creates an `Expectation` that passes if actual is falsy (`nil` or `false`).\n    def be_falsey\n      Spec::BeFalseyExpectation.new\n    end\n\n    # Creates an `Expectation` that passes if actual is nil (`Object#nil?`).\n    def be_nil\n      Spec::BeNilExpectation.new\n    end\n\n    # Creates an `Expectation` that passes if actual is within *delta* of *expected*.\n    def be_close(expected, delta)\n      Spec::CloseExpectation.new(expected, delta)\n    end\n\n    # Returns a factory to create a comparison `Expectation` that:\n    #\n    # * passes if actual is lesser than *value*: `be < value`\n    # * passes if actual is lesser than or equal *value*: `be <= value`\n    # * passes if actual is greater than *value*: `be > value`\n    # * passes if actual is greater than or equal *value*: `be >= value`\n    def be\n      Spec::Be\n    end\n\n    # Creates an `Expectation` that passes if actual matches *value* (`=~`).\n    def match(value)\n      Spec::MatchExpectation.new(value)\n    end\n\n    # Creates an `Expectation` that passes if actual includes *expected* (`.includes?`).\n    # Works on collections and `String`.\n    def contain(expected)\n      Spec::ContainExpectation.new(expected)\n    end\n\n    # Creates an `Expectation` that passes if actual starts with *expected* (`.starts_with?`).\n    # Works on `String`.\n    def start_with(expected)\n      Spec::StartWithExpectation.new(expected)\n    end\n\n    # Creates an `Expectation` that passes if actual ends with *expected* (`.ends_with?`).\n    # Works on `String`.\n    def end_with(expected)\n      Spec::EndWithExpectation.new(expected)\n    end\n\n    # Creates an `Expectation` that passes if actual is empty (`.empty?`).\n    def be_empty\n      Spec::BeEmptyExpectation.new\n    end\n\n    # Creates an `Expectation` that passes if actual is of type *type* (`is_a?`).\n    macro be_a(type)\n      ::Spec::BeAExpectation({{type}}).new\n    end\n\n    # Runs the block and passes if it raises an exception of type *klass* and the error message matches.\n    #\n    # If *message* is a string, it matches if the exception's error message contains that string.\n    # If *message* is a regular expression, it is used to match the error message.\n    #\n    # It returns the rescued exception.\n    {% if flag?(:wasm32) %}\n      def expect_raises(klass : T.class, message : String | Regex | Nil = nil, file = __FILE__, line = __LINE__, &) forall T\n        # TODO: Enable \"expect_raises\" for wasm32 after exceptions are working.\n      end\n    {% else %}\n      def expect_raises(klass : T.class, message : String | Regex | Nil = nil, file = __FILE__, line = __LINE__, &) forall T\n        yield\n      rescue ex : T\n        # We usually bubble Spec::AssertionFailed, unless this is the expected exception\n        if ex.is_a?(Spec::AssertionFailed) && klass != Spec::AssertionFailed\n          raise ex\n        end\n\n        # `NestingSpecError` is treated as the same above.\n        if ex.is_a?(Spec::NestingSpecError) && klass != Spec::NestingSpecError\n          raise ex\n        end\n\n        exception_as_string = ex.to_s\n        case message\n        when Regex\n          unless exception_as_string =~ message\n            expectation_failed_message = build_expectation_failed_message(klass, message, ex, exception_as_string)\n            fail expectation_failed_message, file, line\n          end\n        when String\n          unless exception_as_string.includes?(message)\n            expectation_failed_message = build_expectation_failed_message(klass, message, ex, exception_as_string)\n            fail expectation_failed_message, file, line\n          end\n        when Nil\n          # No need to check the message\n        end\n\n        ex\n      rescue ex\n        expectation_failed_message = build_expectation_failed_message(klass, ex)\n        fail expectation_failed_message, file, line\n      else\n        fail \"Expected #{klass} but nothing was raised\", file, line\n      end\n\n      private def build_expectation_failed_message(klass : Class, message : String, exception : Exception, exception_as_string : String)\n        backtrace = \"  # #{exception.backtrace.join(\"\\n  # \")}\"\n\n        <<-MESSAGE\n        Expected #{klass} with message containing: #{message.inspect}\n             got #{exception.class} with message: #{exception_as_string.inspect}\n        Backtrace:\n        #{backtrace}\n        MESSAGE\n      end\n\n      private def build_expectation_failed_message(klass : Class, message : Regex, exception : Exception, exception_as_string : String)\n        backtrace = \"  # #{exception.backtrace.join(\"\\n  # \")}\"\n\n        <<-MESSAGE\n        Expected #{klass} with message matching: #{message.inspect}\n             got #{exception.class} with message: #{exception_as_string.inspect}\n        Backtrace:\n        #{backtrace}\n        MESSAGE\n      end\n\n      private def build_expectation_failed_message(klass : Class, exception : Exception)\n        exception_as_string = exception.to_s\n        backtrace = \"  # #{exception.backtrace.join(\"\\n  # \")}\"\n\n        <<-MESSAGE\n        Expected #{klass}\n             got #{exception.class} with message: #{exception_as_string.inspect}\n        Backtrace:\n        #{backtrace}\n        MESSAGE\n      end\n    {% end %}\n  end\n\n  module ObjectExtensions\n    # Validates an expectation and fails the example if it does not match.\n    #\n    # This overload returns a value whose type is restricted to the expected type. For example:\n    #\n    # ```\n    # x = 1 || 'a'\n    # typeof(x) # => Int32 | Char\n    # x = x.should be_a(Int32)\n    # typeof(x) # => Int32\n    # ```\n    #\n    # See `Spec::Expectations` for available expectations.\n    def should(expectation : BeAExpectation(T), failure_message : String? = nil, *, file = __FILE__, line = __LINE__) : T forall T\n      if expectation.match self\n        self.is_a?(T) ? self : (raise \"Bug: expected #{self} to be a #{T}\")\n      else\n        failure_message ||= expectation.failure_message(self)\n        fail(failure_message, file, line)\n      end\n    end\n\n    # Validates an expectation and fails the example if it does not match.\n    #\n    # See `Spec::Expectations` for available expectations.\n    def should(expectation, failure_message : String? = nil, *, file = __FILE__, line = __LINE__)\n      unless expectation.match self\n        failure_message ||= expectation.failure_message(self)\n        fail(failure_message, file, line)\n      end\n    end\n\n    # Validates an expectation and fails the example if it matches.\n    #\n    # This overload returns a value whose type is restricted to exclude the given\n    # type in `should_not be_a`. For example:\n    #\n    # ```\n    # x = 1 || 'a'\n    # typeof(x) # => Int32 | Char\n    # x = x.should_not be_a(Char)\n    # typeof(x) # => Int32\n    # ```\n    #\n    # See `Spec::Expectations` for available expectations.\n    def should_not(expectation : BeAExpectation(T), failure_message : String? = nil, *, file = __FILE__, line = __LINE__) forall T\n      if expectation.match self\n        failure_message ||= expectation.negative_failure_message(self)\n        fail(failure_message, file, line)\n      else\n        self.is_a?(T) ? (raise \"Bug: expected #{self} not to be a #{T}\") : self\n      end\n    end\n\n    # Validates an expectation and fails the example if it matches.\n    #\n    # This overload returns a value whose type is restricted to be not `Nil`. For example:\n    #\n    # ```\n    # x = 1 || nil\n    # typeof(x) # => Int32 | Nil\n    # x = x.should_not be_nil\n    # typeof(x) # => Int32\n    # ```\n    #\n    # See `Spec::Expectations` for available expectations.\n    def should_not(expectation : BeNilExpectation, failure_message : String? = nil, *, file = __FILE__, line = __LINE__)\n      if expectation.match self\n        failure_message ||= expectation.negative_failure_message(self)\n        fail(failure_message, file, line)\n      else\n        self.not_nil!\n      end\n    end\n\n    # Validates an expectation and fails the example if it matches.\n    #\n    # See `Spec::Expectations` for available expectations.\n    def should_not(expectation, failure_message : String? = nil, *, file = __FILE__, line = __LINE__)\n      if expectation.match self\n        failure_message ||= expectation.negative_failure_message(self)\n        fail(failure_message, file, line)\n      end\n    end\n  end\nend\n\ninclude Spec::Expectations\n\nclass Object\n  include Spec::ObjectExtensions\nend\n"
  },
  {
    "path": "src/spec/filters.cr",
    "content": "require \"./item\"\nrequire \"./example\"\nrequire \"./context\"\n\nmodule Spec\n  module Item\n    # :nodoc:\n    def matches_pattern?(pattern : Regex) : Bool\n      !!(@description =~ pattern)\n    end\n\n    # :nodoc:\n    def matches_line?(line : Int32) : Bool\n      @line == line || @line <= line <= @end_line\n    end\n\n    # :nodoc:\n    def matches_locations?(locations : Hash(String, Array(Int32))) : Bool\n      lines = locations[file]?\n      !!(lines && lines.any? { |line| matches_line?(line) })\n    end\n\n    # :nodoc:\n    def matches_tags?(tags : Set(String)) : Bool\n      if t = @tags\n        tags.intersects?(t)\n      else\n        false\n      end\n    end\n  end\n\n  class RootContext\n    # :nodoc:\n    def run_filters(pattern = nil, line = nil, locations = nil, split_filter = nil, focus = nil, tags = nil, anti_tags = nil)\n      filter_by_pattern(pattern) if pattern\n      filter_by_line(line) if line\n      filter_by_locations(locations) if locations\n      filter_by_split(split_filter) if split_filter\n      filter_by_focus if focus\n      filter_by_tags(tags) if tags\n      filter_by_anti_tags(anti_tags) if anti_tags\n    end\n\n    # :nodoc:\n    def filter_by_pattern(pattern : Regex)\n      children.select!(&.filter_by_pattern(pattern))\n    end\n\n    # :nodoc:\n    def filter_by_line(line : Int32)\n      children.select!(&.filter_by_line(line))\n    end\n\n    # :nodoc:\n    def filter_by_locations(locations : Hash(String, Array(Int32)))\n      children.select!(&.filter_by_locations(locations))\n    end\n\n    # :nodoc:\n    def filter_by_focus\n      children.select!(&.filter_by_focus)\n    end\n\n    # :nodoc:\n    def filter_by_tags(tags : Set(String))\n      children.select!(&.filter_by_tags(tags))\n    end\n\n    # :nodoc:\n    def filter_by_anti_tags(anti_tags : Set(String))\n      children.select!(&.filter_by_anti_tags(anti_tags))\n    end\n\n    # :nodoc:\n    def filter_by_split(split_filter : SplitFilter)\n      children.select!(&.filter_by_split(split_filter))\n    end\n  end\n\n  class ExampleGroup\n    # :nodoc:\n    #\n    # Filters a context and its children by pattern.\n    # Returns `true` if the context matches the pattern, `false` otherwise.\n    def filter_by_pattern(pattern : Regex) : Bool\n      return true if matches_pattern?(pattern)\n\n      children.select!(&.filter_by_pattern(pattern))\n      !children.empty?\n    end\n\n    # :nodoc:\n    #\n    # Filters a context and its children by line.\n    # Returns `true` if the context matches the line, `false` otherwise.\n    def filter_by_line(line : Int32) : Bool\n      # If any children matches then we match too, but we filter children\n      if children.any? &.matches_line?(line)\n        children.select!(&.filter_by_line(line))\n        return true\n      end\n\n      # Otherwise check if we match. If we do it means the line is inside\n      # this context but outside a nested context or example, so then we\n      # have to run all contexts and examples inside ourselves.\n      if matches_line?(line)\n        return true\n      end\n\n      false\n    end\n\n    # :nodoc:\n    #\n    # Filters a context and its children by the given locations.\n    # Returns `true` if the context matches the locations, `false` otherwise.\n    def filter_by_locations(locations : Hash(String, Array(Int32))) : Bool\n      # If any children matches then we match too, but we filter children\n      if children.any? &.matches_locations?(locations)\n        children.select!(&.filter_by_locations(locations))\n        return true\n      end\n\n      # Otherwise check if we match. If we do it means the line is inside\n      # this context but outside a nested context or example, so then we\n      # have to run all contexts and examples inside ourselves.\n      if matches_locations?(locations)\n        return true\n      end\n\n      false\n    end\n\n    # :nodoc:\n    #\n    # Filters a context and its children that are marked as focus.\n    # Returns `true` if the context or any of its children have focus,\n    # `false` otherwise.\n    def filter_by_focus : Bool\n      return true if focus?\n\n      children.select!(&.filter_by_focus)\n      !children.empty?\n    end\n\n    # :nodoc:\n    #\n    # Filters a context and its children by the given tags.\n    # Returns `true` if the context matches the tags, `false` otherwise.\n    def filter_by_tags(tags : Set(String)) : Bool\n      return true if matches_tags?(tags)\n\n      children.select!(&.filter_by_tags(tags))\n      !children.empty?\n    end\n\n    # :nodoc:\n    #\n    # Filters a context and its children by the given anti-tags.\n    # Returns `false` if the context matches the anti_tags, `true` otherwise.\n    def filter_by_anti_tags(anti_tags : Set(String)) : Bool\n      return false if matches_tags?(anti_tags)\n\n      children.select!(&.filter_by_anti_tags(anti_tags))\n      !children.empty?\n    end\n\n    # :nodoc:\n    #\n    # Filters a context and its children by the given split filter\n    # Returns `true` if the context matches the filter, `false` otherwise.\n    def filter_by_split(split_filter : SplitFilter) : Bool\n      children.select!(&.filter_by_split(split_filter))\n      !children.empty?\n    end\n  end\n\n  class Example\n    # :nodoc:\n    #\n    # Returns `true` if the example matches the pattern,\n    # `false` otherwise.\n    def filter_by_pattern(pattern : Regex) : Bool\n      matches_pattern?(pattern)\n    end\n\n    # :nodoc:\n    #\n    # Returns `true` if the example is contained in the given line,\n    # `false` otherwise.\n    def filter_by_line(line : Int32) : Bool\n      matches_line?(line)\n    end\n\n    # :nodoc:\n    #\n    # Returns `true` if the example is contained in any of the given locations,\n    # `false` otherwise.\n    def filter_by_locations(locations : Hash(String, Array(Int32))) : Bool\n      matches_locations?(locations)\n    end\n\n    # :nodoc:\n    #\n    # Returns `true` if this example is marked as focus, `false` otherwise\n    def filter_by_focus : Bool\n      @focus\n    end\n\n    # :nodoc:\n    #\n    # Returns `true` if the example is tagged with any of the given tags,\n    # `false` otherwise.\n    def filter_by_tags(tags : Set(String)) : Bool\n      matches_tags?(tags)\n    end\n\n    # :nodoc:\n    #\n    # Returns `false` if the example is tagged with any of the given anti_tags,\n    # `true` otherwise.\n    def filter_by_anti_tags(anti_tags : Set(String)) : Bool\n      !matches_tags?(anti_tags)\n    end\n\n    @@example_counter = -1\n\n    # :nodoc:\n    #\n    # Returns `true` if the example is matches the given split filter,\n    # `false` otherwise.`\n    def filter_by_split(split_filter : SplitFilter) : Bool\n      @@example_counter += 1\n      @@example_counter % split_filter.quotient == split_filter.remainder\n    end\n  end\nend\n"
  },
  {
    "path": "src/spec/formatter.cr",
    "content": "module Spec\n  # :nodoc:\n  abstract class Formatter\n    def initialize(@cli : CLI, @io : IO = cli.stdout)\n    end\n\n    def push(context)\n    end\n\n    def pop\n    end\n\n    def before_example(description)\n    end\n\n    def report(result)\n    end\n\n    def finish(elapsed_time, aborted)\n    end\n\n    def should_print_summary?\n      false\n    end\n  end\n\n  # :nodoc:\n  class DotFormatter < Formatter\n    @count = 0\n    @split = 0\n\n    def initialize(*args)\n      super\n\n      if split = ENV[\"SPEC_SPLIT_DOTS\"]?\n        @split = split.to_i\n      end\n    end\n\n    def report(result)\n      @io << @cli.colorize(result.kind.letter, result.kind)\n      split_lines\n      @io.flush\n    end\n\n    private def split_lines\n      return unless @split > 0\n      if (@count += 1) >= @split\n        @io.puts\n        @count = 0\n      end\n    end\n\n    def finish(elapsed_time, aborted)\n      @io.puts\n    end\n\n    def should_print_summary?\n      true\n    end\n  end\n\n  # :nodoc:\n  class VerboseFormatter < Formatter\n    class Item\n      def initialize(@indent : Int32, @description : String)\n        @printed = false\n      end\n\n      def print(io)\n        return if @printed\n        @printed = true\n\n        VerboseFormatter.print_indent(io, @indent)\n        io.puts @description\n      end\n    end\n\n    @indent = 0\n    @last_description = \"\"\n    @items = [] of Item\n\n    def push(context)\n      @items << Item.new(@indent, context.description)\n      @indent += 1\n    end\n\n    def pop\n      @items.pop\n      @indent -= 1\n    end\n\n    def print_indent\n      self.class.print_indent(@io, @indent)\n    end\n\n    def self.print_indent(io, indent)\n      indent.times { io << \"  \" }\n    end\n\n    def before_example(description)\n      @items.each &.print(@io)\n      print_indent\n      @io << description\n      @last_description = description\n    end\n\n    def report(result)\n      @io << '\\r'\n      print_indent\n      @io.puts @cli.colorize(@last_description, result.kind)\n    end\n\n    def should_print_summary?\n      true\n    end\n  end\n\n  # :nodoc:\n  class CLI\n    def formatters\n      @formatters ||= [Spec::DotFormatter.new(self)] of Spec::Formatter\n    end\n\n    def override_default_formatter(formatter)\n      formatters[0] = formatter\n    end\n\n    def add_formatter(formatter)\n      formatters << formatter\n    end\n  end\n\n  @[Deprecated(\"This is an internal API.\")]\n  def self.override_default_formatter(formatter)\n    @@cli.override_default_formatter(formatter)\n  end\n\n  @[Deprecated(\"This is an internal API.\")]\n  def self.add_formatter(formatter)\n    @@cli.add_formatter(formatter)\n  end\nend\n"
  },
  {
    "path": "src/spec/helpers/iterate.cr",
    "content": "module Spec::Methods\n  # Spec helper for generic iteration methods which tests both yielding and\n  # iterator overloads.\n  #\n  # This helper creates two spec examples named *description* with suffixes\n  # `\" yielding\"` and `\" iterator\"`.\n  # The yielding example calls *method* with a block and expects the iteration\n  # elements to be yielded to the block. The iterator example calls *method*\n  # without a block and expects it to return an `Iterator` which it then consumes.\n  #\n  # The iterated elements are collected in an array and compared to *expected*,\n  # ensuring type-equality of the elements.\n  #\n  # By default, both examples make sure that the iteration is finished after\n  # iterating all elements from *expected*. If the iteration is infinite,\n  # passing `infinite: true` skips that check and allows to test a finite sample\n  # of an infinite iteration.\n  #\n  # ```\n  # require \"spec/helpers/iterate\"\n  #\n  # it_iterates \"Array#each\", [1, 2, 3], (1..3).each\n  # it_iterates \"infinite #cycle\", [1, 2, 3, 1, 2, 3, 1], (1..3).cycle, infinite: true\n  # ```\n  #\n  # If the iteration elements are tuples (i.e. multiple values), the yielding\n  # variant by default only catches the first value because of the block argument\n  # mechanics. Passing `tuple: true` ensures all yielded arguments are collected\n  # using a splat.\n  #\n  # ```\n  # require \"spec/helpers/iterate\"\n  #\n  # it_iterates \"Array#each_with_index\", [{1, 0}, {2, 1}, {3, 2}], (1..3).each_with_index, tuple: true\n  # ```\n  macro it_iterates(description, expected, method, *, infinite = false, tuple = false, file = __FILE__, line = __LINE__)\n    it {{ \"#{description} yielding\" }}, file: {{ file }}, line: {{ line }} do\n      assert_iterates_yielding {{ expected }}, {{ method }}, infinite: {{ infinite }}, tuple: {{ tuple }}\n    end\n    it {{ \"#{description} iterator\" }}, file: {{ file }}, line: {{ line }} do\n      assert_iterates_iterator {{ expected }}, {{ method }}, infinite: {{ infinite }}\n    end\n  end\n\n  # Calls *method* with a block and compares yielded values with *expected*.\n  #\n  # See `.it_iterates` for details.\n  macro assert_iterates_yielding(expected, method, *, infinite = false, tuple = false)\n    %remaining = ({{expected}}).size\n    %ary = [] of typeof(::Enumerable.element_type({{ expected }}))\n    {{ method.id }} do |{% if tuple %}*{% end %}x|\n      if %remaining == 0\n        if {{ infinite }}\n          break\n        else\n          fail \"Reached iteration limit #{({{ expected }}).size} receiving value #{x.inspect}\"\n        end\n      end\n\n      %ary << x\n      %remaining -= 1\n    end\n\n    %ary.should eq({{ expected }})\n    %ary.zip({{ expected }}).each_with_index do |(actual, expected), i|\n      if actual.class != expected.class\n        fail \"Mismatching type, expected: #{expected} (#{expected.class}), got: #{actual} (#{actual.class}) at #{i}\"\n      end\n    end\n  end\n\n  # Calls *method* expecting an iterator and compares iterated values with *expected*.\n  #\n  # See `.it_iterates` for details.\n  macro assert_iterates_iterator(expected, method, *, infinite = false)\n    %ary = [] of typeof(::Enumerable.element_type({{ expected }}))\n    %iter = {{ method.id }}\n    ({{ expected }}).size.times do\n      %v = %iter.next\n      if %v.is_a?(::Iterator::Stop)\n        # Compare the actual value directly. Since there are less\n        # then expected values, the expectation will fail and raise.\n        %ary.should eq({{ expected }})\n        raise \"Unreachable\"\n      end\n      %ary << %v\n    end\n    unless {{ infinite }}\n      %iter.next.should be_a(::Iterator::Stop)\n    end\n\n    %ary.should eq({{ expected }})\n    %ary.zip({{ expected }}).each_with_index do |(actual, expected), i|\n      if actual.class != expected.class\n        fail \"Mismatching type, expected: #{expected} (#{expected.class}), got: #{actual} (#{actual.class}) at #{i}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "src/spec/helpers/string.cr",
    "content": "module Spec::Methods\n  # Asserts that the given *call* and its `IO`-accepting variant both match the\n  # given *expectation*, used to test string printing.\n  #\n  # Given a call of the form `foo.bar(*args, **opts)`, this tests the following\n  # cases:\n  #\n  # * The call itself. Additionally this call must return a `String`.\n  # * `String.build { |io| foo.bar(io, *args, **opts) }`, which constructs a\n  #   `String` via an `IO` overload.\n  # * `io = ...; foo.bar(io, *args, **opts); io.to_s`, where `io` is an `IO`\n  #   configured to use the UTF-16 encoding, and contents written to it are\n  #   decoded back into a UTF-8 `String`. This case ensures that the `IO`\n  #   overload does not produce malformed UTF-8 byte sequences via a non-default\n  #   encoding. This case is skipped if the `without_iconv` flag is set.\n  #\n  # The overload that accepts a *str* argument is usually easier to work with.\n  macro assert_prints(call, *, should expectation, file = __FILE__, line = __LINE__)\n    %expectation = {{ expectation }}\n    %file = {{ file }}\n    %line = {{ line }}\n\n    %result = {{ call }}\n    %result.should be_a(::String), file: %file, line: %line\n    %result.should(%expectation, file: %file, line: %line)\n\n    ::String.build do |io|\n      {% if call.receiver %}{{ call.receiver }}.{% end %}{{ call.name }}(\n        io,\n        {% for arg in call.args %} {{ arg }}, {% end %}\n        {% if call.named_args %} {% for narg in call.named_args %} {{ narg.name }}: {{ narg.value }}, {% end %} {% end %}\n      ) {{ call.block }}\n    end.should(%expectation, file: %file, line: %line)\n\n    {% unless flag?(:without_iconv) %}\n      %utf16_io = ::IO::Memory.new\n      %utf16_io.set_encoding(::IO::ByteFormat::SystemEndian == ::IO::ByteFormat::LittleEndian ? \"UTF-16LE\" : \"UTF-16BE\")\n      {% if call.receiver %}{{ call.receiver }}.{% end %}{{ call.name }}(\n        %utf16_io,\n        {% for arg in call.args %} {{ arg }}, {% end %}\n        {% if call.named_args %} {% for narg in call.named_args %} {{ narg.name }}: {{ narg.value }}, {% end %} {% end %}\n      ) {{ call.block }}\n      %result = ::String.from_utf16(%utf16_io.to_slice.unsafe_slice_of(::UInt16))\n      %result.should(%expectation, file: %file, line: %line)\n    {% end %}\n  end\n\n  # Asserts that the given *call* and its `IO`-accepting variant both produce\n  # the given string *str*.\n  #\n  # Equivalent to `assert_prints call, should: eq(str)`. *str* must be validly\n  # encoded in UTF-8.\n  #\n  # ```\n  # require \"spec\"\n  # require \"spec/helpers/string\"\n  #\n  # it \"prints integers with `Int#to_s`\" do\n  #   assert_prints 123.to_s, \"123\"\n  #   assert_prints 123.to_s(16), \"7b\"\n  # end\n  # ```\n  #\n  # Methods that do not follow the convention of `IO`-accepting and\n  # `String`-returning overloads can also be tested as long as suitable wrapper\n  # methods are defined:\n  #\n  # ```\n  # require \"spec\"\n  # require \"spec/helpers/string\"\n  #\n  # private def fprintf(format, *args)\n  #   sprintf(format, *args)\n  # end\n  #\n  # private def fprintf(io : IO, format, *args)\n  #   io.printf(format, *args)\n  # end\n  #\n  # it \"prints with `sprintf` and `IO#printf`\" do\n  #   assert_prints fprintf(\"%d\", 123), \"123\"\n  #   assert_prints fprintf(\"%x %b\", 123, 6), \"7b 110\"\n  # end\n  # ```\n  macro assert_prints(call, str, *, file = __FILE__, line = __LINE__)\n    %str = ({{ str }}).as(::String)\n    unless %str.valid_encoding?\n      ::fail \"`str` contains invalid UTF-8 byte sequences: #{%str.inspect}\", file: {{ file }}, line: {{ line }}\n    end\n    assert_prints({{ call }}, should: eq(%str), file: {{ file }}, line: {{ line }})\n  end\nend\n"
  },
  {
    "path": "src/spec/item.cr",
    "content": "module Spec\n  # Info that `describe`, `context` and `it` all have in common.\n  module Item\n    # The `describe`/`context` that wraps this example or example group.\n    getter parent : Context\n\n    # The example or example group's description.\n    getter description : String\n\n    # The file where the example or example group is defined.\n    getter file : String\n\n    # The line where the example or example group starts.\n    getter line : Int32\n\n    # The line where the example or example group ends.\n    getter end_line : Int32\n\n    # Does this example or example group have `focus: true` on it?\n    getter? focus : Bool\n\n    # The tags defined on this example or example group\n    getter tags : Set(String)?\n\n    private def initialize_tags(tags)\n      @tags = tags.is_a?(String) ? Set{tags} : tags.try(&.to_set)\n    end\n\n    # All tags, including tags inherited from ancestor example groups\n    def all_tags : Set(String)\n      result = tags.try(&.dup) || Set(String).new\n      ancestor = self\n      while ancestor = ancestor.parent.as?(ExampleGroup)\n        if tags = ancestor.tags\n          result.concat(tags)\n        end\n      end\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "src/spec/junit_formatter.cr",
    "content": "require \"html\"\n\nmodule Spec\n  # :nodoc:\n  class JUnitFormatter < Formatter\n    @started_at = Time.utc\n\n    @results = [] of Spec::Result\n    @summary = {} of Status => Int32\n\n    def report(result)\n      current = @summary[result.kind]? || 0\n      @summary[result.kind] = current + 1\n      @results << result\n    end\n\n    def finish(elapsed_time, aborted)\n      io = @io\n      io.puts %(<?xml version=\"1.0\"?>)\n      io << %(<testsuite tests=\") << @results.size\n      io << %(\" skipped=\") << (@summary[Status::Pending]? || 0)\n      io << %(\" errors=\") << (@summary[Status::Error]? || 0)\n      io << %(\" failures=\") << (@summary[Status::Fail]? || 0)\n      io << %(\" time=\") << elapsed_time.total_seconds\n      io << %(\" timestamp=\") << @started_at.to_rfc3339\n      io << %(\" hostname=\") << System.hostname\n      io << %(\">)\n\n      io.puts\n\n      @results.each { |r| write_report(r, io) }\n\n      io << %(</testsuite>)\n      io.close\n    end\n\n    def self.file(cli : CLI, output_path : Path)\n      if output_path.extension != \".xml\"\n        output_path = output_path.join(\"output.xml\")\n      end\n\n      Dir.mkdir_p(output_path.dirname)\n      file = File.new(output_path, \"w\")\n      JUnitFormatter.new(cli, file)\n    end\n\n    private def escape_xml_attr(value)\n      String.build do |io|\n        reader = Char::Reader.new(value)\n        while reader.has_next?\n          case current_char = reader.current_char\n          when .control?\n            current_char.to_s.inspect_unquoted(io)\n          else\n            current_char.to_s(io)\n          end\n          reader.next_char\n        end\n      end\n    end\n\n    # -------- private utility methods\n    private def write_report(result, io)\n      io << %(  <testcase file=\")\n      HTML.escape(result.file, io)\n      io << %(\" classname=\")\n      HTML.escape(classname(result), io)\n      io << %(\" name=\")\n      HTML.escape(escape_xml_attr(result.description), io)\n      io << %(\" line=\")\n      io << result.line\n\n      if elapsed = result.elapsed\n        io << %(\" time=\")\n        io << elapsed.total_seconds\n      end\n\n      if tag = inner_content_tag(result.kind)\n        io.puts %(\">)\n\n        if (exception = result.exception) && !result.kind.pending?\n          write_inner_content(tag, exception, io)\n        else\n          io << \"    <\" << tag << \"/>\\n\"\n        end\n        io.puts \"  </testcase>\"\n      else\n        io.puts %(\"/>)\n      end\n    end\n\n    private def inner_content_tag(kind)\n      case kind\n      in .error?   then \"error\"\n      in .fail?    then \"failure\"\n      in .pending? then \"skipped\"\n      in .success? then nil\n      end\n    end\n\n    private def write_inner_content(tag, exception, io)\n      io << \"    <\" << tag\n\n      if message = exception.message\n        io << %( message=\")\n        HTML.escape(message, io)\n        io << '\"'\n      end\n      if tag == \"error\"\n        io << %( type=\")\n        io << exception.class.name\n        io << '\"'\n      end\n      io << '>'\n\n      if backtrace = exception.backtrace?\n        HTML.escape(backtrace.join('\\n'), io)\n      end\n\n      io << \"</\" << tag << \">\\n\"\n    end\n\n    private def classname(result)\n      path = Path.new result.file\n      path.expand.relative_to(Dir.current).parts.join('.').rchop path.extension\n    end\n  end\nend\n"
  },
  {
    "path": "src/spec/methods.cr",
    "content": "module Spec::Methods\n  # Defines an example group that describes a unit to be tested.\n  # Inside *&block* examples are defined by `#it` or `#pending`.\n  #\n  # Several `describe` blocks can be nested.\n  #\n  # Example:\n  # ```\n  # require \"spec\"\n  #\n  # describe \"Int32\" do\n  #   describe \"+\" do\n  #     it \"adds\" { (1 + 1).should eq 2 }\n  #   end\n  # end\n  # ```\n  #\n  # If `focus` is `true`, only this `describe`, and others marked with `focus: true`, will run.\n  def describe(description = nil, file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil, &block)\n    Spec.cli.root_context.describe(description.to_s, file, line, end_line, focus, tags, &block)\n  end\n\n  # Defines an example group that establishes a specific context,\n  # like *empty array* versus *array with elements*.\n  # Inside *&block* examples are defined by `#it` or `#pending`.\n  #\n  # It is functionally equivalent to `#describe`.\n  #\n  # If `focus` is `true`, only this `context`, and others marked with `focus: true`, will run.\n  def context(description = nil, file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil, &block)\n    describe(description.to_s, file, line, end_line, focus, tags, &block)\n  end\n\n  # Defines a concrete test case.\n  #\n  # The test is performed by the block supplied to *&block*.\n  #\n  # Example:\n  # ```\n  # require \"spec\"\n  #\n  # it \"adds\" { (1 + 1).should eq 2 }\n  # ```\n  #\n  # It is usually used inside a `#describe` or `#context` section.\n  #\n  # If `focus` is `true`, only this test, and others marked with `focus: true`, will run.\n  def it(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil, &block)\n    Spec.cli.root_context.it(description.to_s, file, line, end_line, focus, tags, &block)\n  end\n\n  # Defines a pending test case.\n  #\n  # *&block* is never evaluated.\n  # It can be used to describe behaviour that is not yet implemented.\n  #\n  # Example:\n  # ```\n  # require \"spec\"\n  #\n  # pending \"check cat\" { cat.alive? }\n  # ```\n  #\n  # It is usually used inside a `#describe` or `#context` section.\n  #\n  # If `focus` is `true`, only this test, and others marked with `focus: true`, will run.\n  def pending(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil, &block)\n    pending(description, file, line, end_line, focus, tags)\n  end\n\n  # Defines a yet-to-be-implemented pending test case\n  #\n  # If `focus` is `true`, only this test, and others marked with `focus: true`, will run.\n  def pending(description = \"assert\", file = __FILE__, line = __LINE__, end_line = __END_LINE__, focus : Bool = false, tags : String | Enumerable(String) | Nil = nil)\n    Spec.cli.root_context.pending(description.to_s, file, line, end_line, focus, tags)\n  end\n\n  # Fails an example.\n  #\n  # This method can be used to manually fail an example defined in an `#it` block.\n  def fail(msg, file = __FILE__, line = __LINE__)\n    raise Spec::AssertionFailed.new(msg, file, line)\n  end\n\n  # Marks the current example pending\n  #\n  # In case an example needs to be pending on some condition that requires executing it,\n  # this allows to mark it as such  rather than letting it fail or never run.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # it \"test git\" do\n  #   cmd = Process.find_executable(\"git\")\n  #   pending!(\"git is not available\") unless cmd\n  #   cmd.should end_with(\"git\")\n  # end\n  # ```\n  def pending!(msg = \"Cannot run example\", file = __FILE__, line = __LINE__)\n    raise Spec::ExamplePending.new(msg, file, line)\n  end\n\n  # Executes the given block before each spec in the current context runs.\n  #\n  # A context is defined by `describe` or `context` blocks, or outside of them\n  # it's the root context. Nested contexts inherit the `*_each` blocks of\n  # their ancestors.\n  #\n  # If multiple blocks are registered for the same spec, the blocks defined in\n  # the outermost context go first. Blocks on the same context are executed in\n  # order of definition.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # it \"sample_a\" { }\n  #\n  # describe \"nested_context\" do\n  #   before_each do\n  #     puts \"runs before sample_b\"\n  #   end\n  #\n  #   it \"sample_b\" { }\n  # end\n  # ```\n  def before_each(&block)\n    if Spec.cli.current_context.is_a?(RootContext)\n      raise \"Can't call `before_each` outside of a describe/context\"\n    end\n    Spec.cli.current_context.before_each(&block)\n  end\n\n  # Executes the given block after each spec in the current context runs.\n  #\n  # A context is defined by `describe` or `context` blocks, or outside of them\n  # it's the root context. Nested contexts inherit the `*_each` blocks of\n  # their ancestors.\n  #\n  # If multiple blocks are registered for the same spec, the blocks defined in\n  # the outermost context go first. Blocks on the same context are executed in\n  # order of definition.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # it \"sample_a\" { }\n  #\n  # describe \"nested_context\" do\n  #   after_each do\n  #     puts \"runs after sample_b\"\n  #   end\n  #\n  #   it \"sample_b\" { }\n  # end\n  # ```\n  def after_each(&block)\n    if Spec.cli.current_context.is_a?(RootContext)\n      raise \"Can't call `after_each` outside of a describe/context\"\n    end\n    Spec.cli.current_context.after_each(&block)\n  end\n\n  # Executes the given block before the first spec in the current context runs.\n  #\n  # A context is defined by `describe` or `context` blocks, or outside of them\n  # it's the root context.\n  # This is independent of the source location the specs and this hook are\n  # defined.\n  #\n  # If multiple blocks are registered on the same context, they are executed in\n  # order of definition.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # it \"sample_a\" { }\n  #\n  # describe \"nested_context\" do\n  #   before_all do\n  #     puts \"runs at start of nested_context\"\n  #   end\n  #\n  #   it \"sample_b\" { }\n  # end\n  # ```\n  def before_all(&block)\n    if Spec.cli.current_context.is_a?(RootContext)\n      raise \"Can't call `before_all` outside of a describe/context\"\n    end\n    Spec.cli.current_context.before_all(&block)\n  end\n\n  # Executes the given block after the last spec in the current context runs.\n  #\n  # A context is defined by `describe` or `context` blocks, or outside of them\n  # it's the root context.\n  # This is independent of the source location the specs and this hook are\n  # defined.\n  #\n  # If multiple blocks are registered on the same context, they are executed in\n  # order of definition.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # it \"sample_a\" { }\n  #\n  # describe \"nested_context\" do\n  #   after_all do\n  #     puts \"runs at end of nested_context\"\n  #   end\n  #\n  #   it \"sample_b\" { }\n  # end\n  # ```\n  def after_all(&block)\n    if Spec.cli.current_context.is_a?(RootContext)\n      raise \"Can't call `after_all` outside of a describe/context\"\n    end\n    Spec.cli.current_context.after_all(&block)\n  end\n\n  # Executes the given block when each spec in the current context runs.\n  #\n  # The block must call `run` on the given `Example::Procsy`\n  # object.\n  #\n  # This is essentially a `before_each` and `after_each` hook combined into one.\n  # It is useful for example when setup and teardown steps need shared state.\n  #\n  # A context is defined by `describe` or `context` blocks, or outside of them\n  # it's the root context. Nested contexts inherit the `*_each` blocks of\n  # their ancestors.\n  #\n  # If multiple blocks are registered for the same spec, the blocks defined in\n  # the outermost context go first. Blocks on the same context are executed in\n  # order of definition.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # it \"sample_a\" { }\n  #\n  # describe \"nested_context\" do\n  #   around_each do |example|\n  #     puts \"runs before sample_b\"\n  #     example.run\n  #     puts \"runs after sample_b\"\n  #   end\n  #\n  #   it \"sample_b\" { }\n  # end\n  # ```\n  def around_each(&block : Example::Procsy ->)\n    if Spec.cli.current_context.is_a?(RootContext)\n      raise \"Can't call `around_each` outside of a describe/context\"\n    end\n    Spec.cli.current_context.around_each(&block)\n  end\n\n  # Executes the given block when the current context runs.\n  #\n  # The block must call `run` on the given `Context::Procsy`\n  # object.\n  #\n  # This is essentially a `before_all` and `after_all` hook combined into one.\n  # It is useful for example when setup and teardown steps need shared state.\n  #\n  # A context is defined by `describe` or `context` blocks. This hook does not\n  # work outside such a block (i.e. in the root context).\n  #\n  # If multiple blocks are registered for the same spec, the blocks defined in\n  # the outermost context go first. Blocks on the same context are executed in\n  # order of definition.\n  #\n  # ```\n  # require \"spec\"\n  #\n  # describe \"main_context\" do\n  #   around_each do |example|\n  #     puts \"runs at beginning of main_context\"\n  #     example.run\n  #     puts \"runs at end of main_context\"\n  #   end\n  #\n  #   it \"sample_a\" { }\n  #\n  #   describe \"nested_context\" do\n  #     around_each do |example|\n  #       puts \"runs at beginning of nested_context\"\n  #       example.run\n  #       puts \"runs at end of nested_context\"\n  #     end\n  #\n  #     it \"sample_b\" { }\n  #   end\n  # end\n  # ```\n  def around_all(&block : ExampleGroup::Procsy ->)\n    if Spec.cli.current_context.is_a?(RootContext)\n      raise \"Can't call `around_all` outside of a describe/context\"\n    end\n    Spec.cli.current_context.around_all(&block)\n  end\nend\n\ninclude Spec::Methods\n"
  },
  {
    "path": "src/spec/source.cr",
    "content": "module Spec\n  # :nodoc:\n  def self.lines_cache\n    @@lines_cache ||= {} of String => Array(String)\n  end\n\n  # :nodoc:\n  def self.read_line(file, line)\n    return nil unless File.file?(file)\n\n    lines = lines_cache.put_if_absent(file) { File.read_lines(file) }\n    lines[line - 1]?\n  end\nend\n"
  },
  {
    "path": "src/spec/tap_formatter.cr",
    "content": "# :nodoc:\nclass Spec::TAPFormatter < Spec::Formatter\n  @counter = 0\n\n  def report(result)\n    case result.kind\n    in .success?\n      @io << \"ok\"\n    in .fail?, .error?\n      @io << \"not ok\"\n    in .pending?\n      @io << \"ok\"\n    end\n\n    @counter += 1\n\n    @io << ' ' << @counter << \" -\"\n    if result.kind.pending?\n      @io << \" # SKIP\"\n    end\n    @io << ' ' << result.description\n\n    @io.puts\n  end\n\n  def finish(elapsed_time, aborted)\n    @io << \"1..\" << @counter\n    @io.puts\n  end\nend\n"
  },
  {
    "path": "src/string/builder.cr",
    "content": "require \"io\"\n\n# Similar to `IO::Memory`, but optimized for building a single string.\n#\n# You should never have to deal with this class. Instead, use `String.build`.\nclass String::Builder < IO\n  getter bytesize : Int32\n  getter capacity : Int32\n  @buffer : Pointer(UInt8)\n\n  def initialize(capacity : Int = 64)\n    String.check_capacity_in_bounds(capacity)\n\n    # Make sure to also be able to hold\n    # the header size plus the trailing zero byte\n    capacity += String::HEADER_SIZE + 1\n\n    @buffer = GC.malloc_atomic(capacity.to_u32).as(UInt8*)\n    @bytesize = 0\n    @capacity = capacity.to_i\n    @finished = false\n  end\n\n  def self.build(capacity : Int = 64, &) : String\n    builder = new(capacity)\n    yield builder\n    builder.to_s\n  end\n\n  def self.new(string : String)\n    io = new(string.bytesize)\n    io << string\n    io\n  end\n\n  def read(slice : Bytes) : NoReturn\n    raise \"Not implemented\"\n  end\n\n  def write(slice : Bytes) : Nil\n    return if slice.empty?\n\n    count = slice.size\n\n    increase_capacity_by count\n    slice.copy_to(@buffer + real_bytesize, count)\n    @bytesize += count\n  end\n\n  def write_byte(byte : UInt8) : Nil\n    increase_capacity_by 1\n    @buffer[real_bytesize] = byte\n\n    @bytesize += 1\n\n    nil\n  end\n\n  def write_string(slice : Bytes) : Nil\n    write(slice)\n  end\n\n  def set_encoding(encoding : String, invalid : Symbol? = nil) : Nil\n    unless utf8_encoding?(encoding, invalid)\n      raise \"Can't change encoding of String::Builder\"\n    end\n  end\n\n  def buffer : Pointer(UInt8)\n    @buffer + String::HEADER_SIZE\n  end\n\n  def empty? : Bool\n    @bytesize == 0\n  end\n\n  # Chomps the last byte from the string buffer.\n  # If the byte is `'\\n'` and there's a `'\\r'` before it, it is also removed.\n  def chomp!(byte : UInt8) : self\n    if bytesize > 0 && buffer[bytesize - 1] == byte\n      back(1)\n\n      if byte === '\\n' && bytesize > 0 && buffer[bytesize - 1] === '\\r'\n        back(1)\n      end\n    end\n    self\n  end\n\n  # Moves the write pointer, and the resulting string bytesize,\n  # by the given *amount*.\n  def back(amount : Int) : Int32\n    unless 0 <= amount <= @bytesize\n      raise ArgumentError.new \"Invalid back amount\"\n    end\n\n    @bytesize -= amount\n  end\n\n  def to_s : String\n    raise \"Can only invoke 'to_s' once on String::Builder\" if @finished\n    @finished = true\n\n    real_bytesize = real_bytesize()\n    @buffer[real_bytesize] = 0_u8\n    real_bytesize += 1\n\n    # Try to reclaim some memory if capacity is bigger than what we need\n    if @capacity > real_bytesize\n      resize_to_capacity(real_bytesize)\n    end\n\n    String.set_crystal_type_id(@buffer)\n    str = @buffer.as(String)\n    str.initialize_header(bytesize)\n    str\n  end\n\n  private def real_bytesize\n    @bytesize + String::HEADER_SIZE\n  end\n\n  private def increase_capacity_by(count)\n    raise IO::EOFError.new if count >= Int32::MAX - real_bytesize\n\n    new_bytesize = real_bytesize + count\n    return if new_bytesize <= @capacity\n\n    new_capacity = calculate_new_capacity(new_bytesize)\n    resize_to_capacity(new_capacity)\n  end\n\n  private def calculate_new_capacity(new_bytesize)\n    # If the new bytesize is bigger than 1 << 30, the next power of two would\n    # be 1 << 31, which is out of range for Int32.\n    # So we limit the capacity to Int32::MAX in order to be able to use the\n    # range (1 << 30) < new_bytesize < Int32::MAX\n    return Int32::MAX if new_bytesize > 1 << 30\n\n    Math.pw2ceil(new_bytesize)\n  end\n\n  private def resize_to_capacity(capacity)\n    @capacity = capacity\n    @buffer = GC.realloc(@buffer, @capacity)\n  end\nend\n"
  },
  {
    "path": "src/string/grapheme/grapheme.cr",
    "content": "require \"./properties\"\n\nclass String\n  # Iterates the grapheme boundaries in this string and yields for each grapheme\n  # cluster the byte index of the first character and the byte index of the last\n  # byte of the last character in the cluster. That is the byte span of the\n  # respective grapheme cluster in the entire string.\n  # The third input value is the last character itself, which can be used\n  # to avoid multiple decoding in single-char graphemes.\n  private def each_grapheme_boundary(& : Range(Int32, Int32), Char -> Nil) : Nil\n    state = Grapheme::Property::Start\n\n    reader = Char::Reader.new(self)\n    last_char = reader.current_char\n    # cache last_property to avoid re-calculation on the following iteration\n    last_property = Grapheme::Property.from(last_char)\n    last_boundary = 0\n\n    while reader.has_next?\n      char = reader.next_char\n      property = Grapheme::Property.from(char)\n      boundary, state = Grapheme.break?(last_property, property, state)\n\n      if boundary\n        index = reader.pos\n        yield last_boundary...index, last_char\n\n        last_boundary = index\n      end\n\n      last_char = char\n      last_property = property\n    end\n  end\n\n  # :nodoc:\n  class GraphemeIterator\n    include Iterator(Grapheme)\n\n    @last_char : Char\n    @last_property : Grapheme::Property\n\n    def initialize(str : String)\n      @reader = Char::Reader.new(str)\n      @state = Grapheme::Property::Start\n      @last_char = @reader.current_char\n      # cache last_property to avoid re-calculation on the following iteration\n      @last_property = Grapheme::Property.from(@last_char)\n      @last_boundary = 0\n    end\n\n    def next\n      return stop unless @reader.has_next?\n\n      while char = @reader.next_char\n        property = Grapheme::Property.from(char)\n        boundary, @state = Grapheme.break?(@last_property, property, @state)\n\n        last_char = @last_char\n        @last_char = char\n        @last_property = property\n\n        if boundary\n          index = @reader.pos\n          grapheme = Grapheme.new(@reader.string, @last_boundary...index, last_char)\n          @last_boundary = index\n\n          return grapheme\n        end\n      end\n\n      Grapheme.new(@reader.string, @last_boundary..@reader.string.bytesize, @last_char)\n    end\n  end\n\n  # `Grapheme` represents a Unicode grapheme cluster, which describes the smallest\n  # functional unit of a writing system. This is also called a *user-perceived character*.\n  #\n  # In the latin alphabet, most graphemes consist of a single Unicode codepoint\n  # (equivalent to `Char`). But a grapheme can also consist of a sequence of codepoints,\n  # which combine into a single unit.\n  #\n  # For example, the string `\"e\\u0301\"` consists of two characters, the latin small letter `e`\n  # and the combining acute accent `´`. Together, they form a single grapheme: `é`.\n  # That same grapheme could alternatively be described in a single codepoint, `\\u00E9` (latin small letter e with acute).\n  # But the combinatory possibilities are far bigger than the amount of directly\n  # available codepoints.\n  #\n  # ```\n  # \"e\\u0301\".size # => 2\n  # \"é\".size       # => 1\n  #\n  # \"e\\u0301\".grapheme_size # => 1\n  # \"é\".grapheme_size       # => 1\n  # ```\n  #\n  # This combination of codepoints is common in some non-latin scripts. It's also\n  # often used with emojis to create customized combination. For example, the\n  # thumbs up sign `👍` (`U+1F44D`) combined with an emoji modifier such as\n  # `U+1F3FC` assign a colour to the emoji.\n  #\n  # Instances of this type can be acquired via `String#each_grapheme` or `String#graphemes`.\n  #\n  # The algorithm to determine boundaries between grapheme clusters is specified\n  # in the [Unicode Standard Annex #29](https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries).\n  @[Experimental(\"The grapheme API is still under development. Join the discussion at [#11610](https://github.com/crystal-lang/crystal/issues/11610).\")]\n  struct Grapheme\n    # For efficiency reasons we avoid allocating a string for graphemes consisting\n    # of only a single character.\n    # As a trade-off, this leads to multi dispatch on this ivar. But that's\n    # acceptable compared to the allocation overhead.\n    #\n    # Graphemes consisting of a single character are always represented as\n    # `Char`, never as `String` to simplify comparability. This invariant is\n    # protected by the constructor.\n    @cluster : Char | String\n\n    # :nodoc:\n    def self.new(string : String, range : Range(Int32, Int32), char : Char) : self\n      if char.bytesize == range.size\n        new(char)\n      else\n        new(string.byte_slice(range.begin, range.end - range.begin))\n      end\n    end\n\n    # :nodoc:\n    def initialize(@cluster)\n    end\n\n    # Appends the characters in this grapheme cluster to *io*.\n    def to_s(io : IO) : Nil\n      io << @cluster\n    end\n\n    # Returns the characters in this grapheme cluster.\n    def to_s : String\n      case cluster = @cluster\n      in Char\n        cluster.to_s\n      in String\n        cluster\n      end\n    end\n\n    # Appends a representation of this grapheme cluster to *io*.\n    def inspect(io : IO) : Nil\n      io << \"String::Grapheme(\"\n      @cluster.inspect(io)\n      io << \")\"\n    end\n\n    # Returns the number of characters in this grapheme cluster.\n    def size : Int32\n      case cluster = @cluster\n      in Char\n        1\n      in String\n        cluster.size\n      end\n    end\n\n    # Returns the number of bytes in the UTF-8 representation of this grapheme cluster.\n    def bytesize : Int32\n      @cluster.bytesize\n    end\n\n    # Returns `true` if *other* is equivalent to `self`.\n    #\n    # Two graphemes are considered equivalent if they contain the same sequence\n    # of codepoints.\n    def ==(other : self) : Bool\n      @cluster == other.@cluster\n    end\n\n    # :nodoc:\n    def self.break?(c1 : Char, c2 : Char) : Bool\n      break?(Property.from(c1), Property.from(c2))\n    end\n\n    # :nodoc:\n    #\n    # Returns whether there is a grapheme break between boundclasses lbc and tbc.\n    #\n    # Please note that evaluation of GB10 (grapheme breaks between emoji zwj sequences)\n    # and GB 12/13 (regional indicator code points) require knowledge of previous characters\n    # which is not handled by this overload. This may result in an incorrect break before\n    # an E_Modifier class codepoint and an incorrectly missing break between two\n    # REGIONAL_INDICATOR class code points if such support does not exist in the caller.\n    #\n    # The rules are graphically displayed in a table on https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.html\n    #\n    # The implementation is inspired by https://github.com/JuliaStrings/utf8proc/blob/462093b3924c7491defc67fda4bc7a27baf9b088/utf8proc.c#L261\n    def self.break?(lbc : Property, tbc : Property) : Bool\n      return true if lbc.start?                                                   # GB1\n      return false if lbc.cr? && tbc.lf?                                          # GB3\n      return true if lbc.cr? || lbc.lf? || lbc.control?                           # GB4\n      return true if tbc.cr? || tbc.lf? || tbc.control?                           # GB5\n      return false if lbc.l? && (tbc.l? || tbc.v? || tbc.lv? || tbc.lvt?)         # GB6\n      return false if (lbc.lv? || lbc.v?) && (tbc.v? || tbc.t?)                   # GB7\n      return false if (lbc.lvt? || lbc.t?) && tbc.t?                              # GB8\n      return false if tbc.extend? || tbc.zwj?                                     # GB9\n      return false if tbc.spacing_mark?                                           # GB9a\n      return false if lbc.prepend?                                                # GB9b\n      return false if lbc.extended_plus_zero_width? && tbc.extended_pictographic? # GB11 (requires additional handling)\n      return false if lbc.regional_indicator? && tbc.regional_indicator?          # GB12/13 (requires additional handling)\n      true                                                                        # GB999\n    end\n\n    # :nodoc:\n    def self.break?(c1 : Char, c2 : Char, state : Property) : {Bool, Property}\n      break?(Property.from(c1), Property.from(c2), state)\n    end\n\n    # :nodoc:\n    #\n    # Returns whether there is a grapheme break between boundclasses lbc and tbc.\n    #\n    # Please note that evaluation of GB10 (grapheme breaks between emoji zwj sequences)\n    # and GB 12/13 (regional indicator code points) require knowledge of previous characters\n    # which is accounted for in the state argument. The caller is expected to\n    # store the returned state and pass it when calling this method the next time.\n    # The initial value is `Property::Start`.\n    #\n    # The implementation is inspired by https://github.com/JuliaStrings/utf8proc/blob/462093b3924c7491defc67fda4bc7a27baf9b088/utf8proc.c#L291\n    def self.break?(lbc : Property, tbc : Property, state : Property) : {Bool, Property}\n      if state.start?\n        state = lbc_override = lbc\n      else\n        lbc_override = state\n      end\n\n      break_permitted = break?(lbc_override, tbc)\n\n      # Special support for GB 12/13 made possible by GB999. After two RI\n      # class codepoints we want to force a break. Do this by resetting the\n      # second RI's bound class to UTF8PROC_BOUNDCLASS_OTHER, to force a break\n      # after that character according to GB999 (unless of course such a break is\n      # forbidden by a different rule such as GB9).\n      if state == tbc && tbc.regional_indicator?\n        state = Property::Any\n        # Special support for GB11 (emoji extend* zwj / emoji)\n      elsif state.extended_pictographic?\n        if tbc.extend? # fold EXTEND codepoints into emoji\n          state = Property::ExtendedPictographic\n        elsif tbc.zwj?\n          state = Property::ExtendedPlusZeroWidth # state to record emoji+zwg combo\n        else\n          state = tbc\n        end\n      else\n        state = tbc\n      end\n\n      {break_permitted, state}\n    end\n  end\nend\n"
  },
  {
    "path": "src/string/grapheme.cr",
    "content": "require \"./grapheme/grapheme\"\n\nclass String\n  # Returns this string split into Unicode extended grapheme clusters.\n  #\n  # `Grapheme` clusters correspond to \"user-perceived characters\" and are defined\n  # in [Unicode Standard Annex #29](https://unicode.org/reports/tr29/). A cluster\n  # can consist of multiple code points which together form a single glyph.\n  #\n  # ```\n  # \"a👍🏼à\".graphemes # => [String::Grapheme('a'), String::Grapheme(\"👍🏼\"), String::Grapheme(\"à\")]\n  # ```\n  #\n  # * `#each_grapheme` iterates the grapheme clusters without allocating an array\n  @[Experimental(\"The grapheme API is still under development. Join the discussion at [#11610](https://github.com/crystal-lang/crystal/issues/11610).\")]\n  def graphemes : Array(Grapheme)\n    graphemes = [] of Grapheme\n    each_grapheme do |grapheme|\n      graphemes << grapheme\n    end\n    graphemes\n  end\n\n  # Yields each Unicode extended grapheme cluster in this string.\n  #\n  # `Grapheme` clusters correspond to \"user-perceived characters\" and are defined\n  # in [Unicode Standard Annex #29](https://unicode.org/reports/tr29/). A cluster\n  # can consist of multiple code points which together form a single glyph.\n  #\n  # ```\n  # \"a👍🏼à\".each_grapheme do |cluster|\n  #   p! cluster\n  # end\n  # ```\n  #\n  # * `#graphemes` collects all grapheme clusters in an array\n  @[Experimental(\"The grapheme API is still under development. Join the discussion at [#11610](https://github.com/crystal-lang/crystal/issues/11610).\")]\n  def each_grapheme(& : Grapheme -> _) : Nil\n    each_grapheme_boundary do |range, last_char|\n      yield Grapheme.new(self, range, last_char)\n    end\n  end\n\n  # Returns the number of Unicode extended graphemes clusters in this string.\n  #\n  # * `#each_grapheme` iterates the grapheme clusters.\n  @[Experimental(\"The grapheme API is still under development. Join the discussion at [#11610](https://github.com/crystal-lang/crystal/issues/11610).\")]\n  def grapheme_size : Int32\n    size = 0\n    each_grapheme_boundary do\n      size += 1\n    end\n    size\n  end\n\n  # Returns an iterator of this string split into Unicode extended grapheme clusters.\n  #\n  # `Grapheme` clusters correspond to \"user-perceived characters\" and are defined\n  # in [Unicode Standard Annex #29](https://unicode.org/reports/tr29/). A cluster\n  # can consist of multiple code points which together form a single glyph.\n  #\n  # ```\n  # \"a👍🏼à\".each_grapheme.to_a # => [String::Grapheme('a'), String::Grapheme(\"👍🏼\"), String::Grapheme(\"à\")]\n  # ```\n  #\n  # * `#graphemes` collects all grapheme clusters in an array\n  @[Experimental(\"The grapheme API is still under development. Join the discussion at [#11610](https://github.com/crystal-lang/crystal/issues/11610).\")]\n  def each_grapheme : Iterator(Grapheme)\n    GraphemeIterator.new(self)\n  end\nend\n"
  },
  {
    "path": "src/string.cr",
    "content": "require \"c/string\"\nrequire \"crystal/small_deque\"\n{% unless flag?(:without_iconv) %}\n  require \"crystal/iconv\"\n{% end %}\nrequire \"float/fast_float\"\n\n# A `String` represents an immutable sequence of UTF-8 characters.\n#\n# A `String` is typically created with a string literal, enclosing UTF-8 characters\n# in double quotes:\n#\n# ```\n# \"hello world\"\n# ```\n#\n# See [`String` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html) in the language reference.\n#\n# A backslash can be used to denote some characters inside the string:\n#\n# ```\n# \"\\\"\" # double quote\n# \"\\\\\" # backslash\n# \"\\e\" # escape\n# \"\\f\" # form feed\n# \"\\n\" # newline\n# \"\\r\" # carriage return\n# \"\\t\" # tab\n# \"\\v\" # vertical tab\n# ```\n#\n# You can use a backslash followed by an *u* and four hexadecimal characters to denote a unicode codepoint written:\n#\n# ```\n# \"\\u0041\" # == \"A\"\n# ```\n#\n# Or you can use curly braces and specify up to six hexadecimal numbers (0 to 10FFFF):\n#\n# ```\n# \"\\u{41}\" # == \"A\"\n# ```\n#\n# A string can span multiple lines:\n#\n# ```\n# \"hello\n#       world\" # same as \"hello\\n      world\"\n# ```\n#\n# Note that in the above example trailing and leading spaces, as well as newlines,\n# end up in the resulting string. To avoid this, you can split a string into multiple lines\n# by joining multiple literals with a backslash:\n#\n# ```\n# \"hello \" \\\n# \"world, \" \\\n# \"no newlines\" # same as \"hello world, no newlines\"\n# ```\n#\n# Alternatively, a backslash followed by a newline can be inserted inside the string literal:\n#\n# ```\n# \"hello \\\n#      world, \\\n#      no newlines\" # same as \"hello world, no newlines\"\n# ```\n#\n# In this case, leading whitespace is not included in the resulting string.\n#\n# If you need to write a string that has many double quotes, parentheses, or similar\n# characters, you can use alternative literals:\n#\n# ```\n# # Supports double quotes and nested parentheses\n# %(hello (\"world\")) # same as \"hello (\\\"world\\\")\"\n#\n# # Supports double quotes and nested brackets\n# %[hello [\"world\"]] # same as \"hello [\\\"world\\\"]\"\n#\n# # Supports double quotes and nested curlies\n# %{hello {\"world\"}} # same as \"hello {\\\"world\\\"}\"\n#\n# # Supports double quotes and nested angles\n# %<hello <\"world\">> # same as \"hello <\\\"world\\\">\"\n# ```\n#\n# To create a `String` with embedded expressions, you can use string interpolation:\n#\n# ```\n# a = 1\n# b = 2\n# \"sum = #{a + b}\" # \"sum = 3\"\n# ```\n#\n# This ends up invoking `Object#to_s(IO)` on each expression enclosed by `#{...}`.\n#\n# If you need to dynamically build a string, use `String#build` or `IO::Memory`.\n#\n# ### Non UTF-8 valid strings\n#\n# A string might end up being composed of bytes which form an invalid\n# byte sequence according to UTF-8. This can happen if the string is created\n# via one of the constructors that accept bytes, or when getting a string\n# from `String.build` or `IO::Memory`. No exception will be raised, but every\n# byte that doesn't start a valid UTF-8 byte sequence is interpreted as though\n# it encodes the Unicode replacement character (U+FFFD) by itself. For example:\n#\n# ```\n# # here 255 is not a valid byte value in the UTF-8 encoding\n# string = String.new(Bytes[255, 97])\n# string.valid_encoding? # => false\n#\n# # The first char here is the unicode replacement char\n# string.chars # => ['�', 'a']\n# ```\n#\n# One can also create strings with specific byte value in them by\n# using octal and hexadecimal escape sequences:\n#\n# ```\n# # Octal escape sequences\n# \"\\101\" # # => \"A\"\n# \"\\12\"  # # => \"\\n\"\n# \"\\1\"   # string with one character with code point 1\n# \"\\377\" # string with one byte with value 255\n#\n# # Hexadecimal escape sequences\n# \"\\x41\" # # => \"A\"\n# \"\\xFF\" # string with one byte with value 255\n# ```\n#\n# The reason for allowing strings that don't have a valid UTF-8 sequence\n# is that the world is full of content that isn't properly encoded,\n# and having a program raise an exception or stop because of this\n# is not good. It's better if programs are more resilient, but\n# show a replacement character when there's an error in incoming data.\n#\n# Note that this interpretation only applies to methods inside Crystal; calling\n# `#to_slice` or `#to_unsafe`, e.g. when passing a string to a C library, will\n# expose the invalid UTF-8 byte sequences. In particular, `Regex`'s underlying\n# engine may reject strings that are not valid UTF-8, or it may invoke undefined\n# behavior on invalid strings. If this is undesired, `#scrub` could be used to\n# remove the offending byte sequences first.\nclass String\n  # :nodoc:\n  #\n  # Holds the offset to the first character byte.\n  HEADER_SIZE = offsetof(String, @c)\n\n  include Comparable(self)\n\n  macro inherited\n    {{ raise \"Cannot inherit from String\" }}\n  end\n\n  # Creates a `String` from the given *slice*. `Bytes` will be copied from the slice.\n  #\n  # This method is always safe to call, and the resulting string will have\n  # the contents and size of the slice.\n  #\n  # If *truncate_at_null* is true, only the characters up to and not including\n  # the first null character are copied.\n  #\n  # ```\n  # slice = Slice.new(4) { |i| ('a'.ord + i).to_u8 }\n  # String.new(slice) # => \"abcd\"\n  #\n  # slice = UInt8.slice(102, 111, 111, 0, 98, 97, 114)\n  # String.new(slice, truncate_at_null: true) # => \"foo\"\n  # ```\n  def self.new(slice : Bytes, *, truncate_at_null : Bool = false)\n    bytesize = slice.size\n    if truncate_at_null\n      if index = slice.index(0)\n        bytesize = index\n      end\n    end\n    new(slice.to_unsafe, bytesize)\n  end\n\n  # Creates a new `String` from the given *bytes*, which are encoded in the given *encoding*.\n  #\n  # The *invalid* argument can be:\n  # * `nil`: an exception is raised on invalid byte sequences\n  # * `:skip`: invalid byte sequences are ignored\n  #\n  # ```\n  # slice = Slice.new(2, 0_u8)\n  # slice[0] = 186_u8\n  # slice[1] = 195_u8\n  # String.new(slice, \"GB2312\") # => \"好\"\n  # ```\n  def self.new(bytes : Bytes, encoding : String, invalid : Symbol? = nil) : String\n    String.build do |str|\n      String.encode(bytes, encoding, \"UTF-8\", str, invalid)\n    end\n  end\n\n  # Creates a `String` from a pointer. `Bytes` will be copied from the pointer.\n  #\n  # This method is **unsafe**: the pointer must point to data that eventually\n  # contains a zero byte that indicates the ends of the string. Otherwise,\n  # the result of this method is undefined and might cause a segmentation fault.\n  #\n  # This method is typically used in C bindings, where you get a `char*` from a\n  # library and the library guarantees that this pointer eventually has an\n  # ending zero byte.\n  #\n  # ```\n  # ptr = Pointer.malloc(5) { |i| i == 4 ? 0_u8 : ('a'.ord + i).to_u8 }\n  # String.new(ptr) # => \"abcd\"\n  # ```\n  def self.new(chars : UInt8*)\n    raise ArgumentError.new(\"Cannot create a string with a null pointer\") if chars.null?\n\n    new(chars, LibC.strlen(chars))\n  end\n\n  # Creates a new `String` from a pointer, indicating its bytesize count\n  # and, optionally, the UTF-8 codepoints count (size). `Bytes` will be\n  # copied from the pointer.\n  #\n  # If the given size is zero, the amount of UTF-8 codepoints will be\n  # lazily computed when needed.\n  #\n  # ```\n  # ptr = Pointer.malloc(4) { |i| ('a'.ord + i).to_u8 }\n  # String.new(ptr, 2) # => \"ab\"\n  # ```\n  def self.new(chars : UInt8*, bytesize, size = 0)\n    # Avoid allocating memory for the empty string\n    return \"\" if bytesize == 0\n\n    if chars.null?\n      raise ArgumentError.new(\"Cannot create a string with a null pointer and a non-zero (#{bytesize}) bytesize\")\n    end\n\n    new(bytesize) do |buffer|\n      buffer.copy_from(chars, bytesize)\n      {bytesize, size}\n    end\n  end\n\n  # Creates a new `String` by allocating a buffer (`Pointer(UInt8)`) with the given capacity, then\n  # yielding that buffer. The block must return a tuple with the bytesize and size\n  # (UTF-8 codepoints count) of the String. If the returned size is zero, the UTF-8 codepoints\n  # count will be lazily computed.\n  #\n  # The bytesize returned by the block must be less than or equal to the\n  # capacity given to this String, otherwise `ArgumentError` is raised.\n  #\n  # If you need to build a `String` where the maximum capacity is unknown, use `String#build`.\n  #\n  # ```\n  # str = String.new(4) do |buffer|\n  #   buffer[0] = 'a'.ord.to_u8\n  #   buffer[1] = 'b'.ord.to_u8\n  #   {2, 2}\n  # end\n  # str # => \"ab\"\n  # ```\n  def self.new(capacity : Int, &)\n    check_capacity_in_bounds(capacity)\n\n    str = GC.malloc_atomic(capacity.to_u32 + HEADER_SIZE + 1).as(UInt8*)\n    buffer = str.as(String).to_unsafe\n    bytesize, size = yield buffer\n\n    unless 0 <= bytesize <= capacity\n      raise ArgumentError.new(\"Bytesize out of capacity bounds\")\n    end\n\n    buffer[bytesize] = 0_u8\n\n    # Try to reclaim some memory if capacity is bigger than what was requested\n    if bytesize < capacity\n      str = GC.realloc(str, bytesize.to_u32 + HEADER_SIZE + 1)\n    end\n\n    set_crystal_type_id(str)\n    str = str.as(String)\n    str.initialize_header(bytesize.to_i, size.to_i)\n    str\n  end\n\n  # :nodoc:\n  #\n  # Initializes the header information of a `String` instance.\n  # The actual character content at `@c` is expected to be already filled and is\n  # unaffected by this method.\n  def initialize_header(@bytesize : Int32, @length : Int32 = 0)\n  end\n\n  # Builds a `String` by creating a `String::Builder` with the given initial capacity, yielding\n  # it to the block and finally getting a `String` out of it. The `String::Builder` automatically\n  # resizes as needed.\n  #\n  # ```\n  # str = String.build do |str|\n  #   str << \"hello \"\n  #   str << 1\n  # end\n  # str # => \"hello 1\"\n  # ```\n  def self.build(capacity = 64, &) : self\n    String::Builder.build(capacity) do |builder|\n      yield builder\n    end\n  end\n\n  # Returns the number of bytes in this string.\n  #\n  # ```\n  # \"hello\".bytesize # => 5\n  # \"你好\".bytesize    # => 6\n  # ```\n  def bytesize : Int32\n    @bytesize\n  end\n\n  # Returns the result of interpreting leading characters in this string as an\n  # integer base *base* (between 2 and 36).\n  #\n  # If there is not a valid number at the start of this string,\n  # or if the resulting integer doesn't fit an `Int32`, an `ArgumentError` is raised.\n  #\n  # Options:\n  # * **whitespace**: if `true`, leading and trailing whitespaces are allowed\n  # * **underscore**: if `true`, underscores in numbers are allowed\n  # * **prefix**: if `true`, the prefixes `\"0x\"`, `\"0o\"` and `\"0b\"` override the base\n  # * **strict**: if `true`, extraneous characters past the end of the number\n  #   are disallowed, unless **whitespace** is also `true` and all the trailing\n  #   characters past the number are whitespaces\n  # * **leading_zero_is_octal**: if `true`, then a number prefixed with `\"0\"` will be treated as an octal\n  #\n  # ```\n  # \"12345\".to_i             # => 12345\n  # \"0a\".to_i                # raises ArgumentError\n  # \"hello\".to_i             # raises ArgumentError\n  # \"0a\".to_i(16)            # => 10\n  # \"1100101\".to_i(2)        # => 101\n  # \"1100101\".to_i(8)        # => 294977\n  # \"1100101\".to_i(10)       # => 1100101\n  # \"1100101\".to_i(base: 16) # => 17826049\n  #\n  # \"12_345\".to_i                   # raises ArgumentError\n  # \"12_345\".to_i(underscore: true) # => 12345\n  #\n  # \"  12345  \".to_i                    # => 12345\n  # \"  12345  \".to_i(whitespace: false) # raises ArgumentError\n  #\n  # \"0x123abc\".to_i               # raises ArgumentError\n  # \"0x123abc\".to_i(prefix: true) # => 1194684\n  #\n  # \"99 red balloons\".to_i                # raises ArgumentError\n  # \"99 red balloons\".to_i(strict: false) # => 99\n  #\n  # \"0755\".to_i                              # => 755\n  # \"0755\".to_i(leading_zero_is_octal: true) # => 493\n  # ```\n  def to_i(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false)\n    to_i32(base, whitespace, underscore, prefix, strict, leading_zero_is_octal)\n  end\n\n  # Same as `#to_i`, but returns `nil` if there is not a valid number at the start\n  # of this string, or if the resulting integer doesn't fit an `Int32`.\n  #\n  # ```\n  # \"12345\".to_i?             # => 12345\n  # \"99 red balloons\".to_i?   # => nil\n  # \"0a\".to_i?(strict: false) # => 0\n  # \"hello\".to_i?             # => nil\n  # ```\n  def to_i?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false)\n    to_i32?(base, whitespace, underscore, prefix, strict, leading_zero_is_octal)\n  end\n\n  # Same as `#to_i`, but returns the block's value if there is not a valid number at the start\n  # of this string, or if the resulting integer doesn't fit an `Int32`.\n  #\n  # ```\n  # \"12345\".to_i { 0 } # => 12345\n  # \"hello\".to_i { 0 } # => 0\n  # ```\n  def to_i(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    to_i32(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { yield }\n  end\n\n  # Same as `#to_i` but returns an `Int8`.\n  def to_i8(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int8\n    to_i8(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid Int8: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `Int8` or `nil`.\n  def to_i8?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int8?\n    to_i8(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `Int8` or the block's value.\n  def to_i8(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ Int8, UInt8, 127, 128\n  end\n\n  # Same as `#to_i` but returns an `UInt8`.\n  def to_u8(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt8\n    to_u8(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid UInt8: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `UInt8` or `nil`.\n  def to_u8?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt8?\n    to_u8(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `UInt8` or the block's value.\n  def to_u8(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ UInt8, UInt8\n  end\n\n  # Same as `#to_i` but returns an `Int16`.\n  def to_i16(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int16\n    to_i16(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid Int16: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `Int16` or `nil`.\n  def to_i16?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int16?\n    to_i16(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `Int16` or the block's value.\n  def to_i16(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ Int16, UInt16, 32767, 32768\n  end\n\n  # Same as `#to_i` but returns an `UInt16`.\n  def to_u16(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt16\n    to_u16(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid UInt16: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `UInt16` or `nil`.\n  def to_u16?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt16?\n    to_u16(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `UInt16` or the block's value.\n  def to_u16(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ UInt16, UInt16\n  end\n\n  # Same as `#to_i`.\n  def to_i32(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int32\n    to_i32(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid Int32: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i`.\n  def to_i32?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int32?\n    to_i32(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i`.\n  def to_i32(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ Int32, UInt32, 2147483647, 2147483648\n  end\n\n  # Same as `#to_i` but returns an `UInt32`.\n  def to_u32(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt32\n    to_u32(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid UInt32: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `UInt32` or `nil`.\n  def to_u32?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt32?\n    to_u32(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `UInt32` or the block's value.\n  def to_u32(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ UInt32, UInt32\n  end\n\n  # Same as `#to_i` but returns an `Int64`.\n  def to_i64(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int64\n    to_i64(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid Int64: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `Int64` or `nil`.\n  def to_i64?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int64?\n    to_i64(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `Int64` or the block's value.\n  def to_i64(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ Int64, UInt64, 9223372036854775807, 9223372036854775808u64\n  end\n\n  # Same as `#to_i` but returns an `UInt64`.\n  def to_u64(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt64\n    to_u64(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid UInt64: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `UInt64` or `nil`.\n  def to_u64?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt64?\n    to_u64(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `UInt64` or the block's value.\n  def to_u64(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ UInt64, UInt64\n  end\n\n  # Same as `#to_i` but returns an `Int128`.\n  def to_i128(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int128\n    to_i128(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid Int128: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `Int128` or `nil`.\n  def to_i128?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : Int128?\n    to_i128(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `Int128` or the block's value.\n  def to_i128(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ Int128, UInt128, Int128::MAX, (UInt128.new(Int128::MAX) + 1)\n  end\n\n  # Same as `#to_i` but returns an `UInt128`.\n  def to_u128(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt128\n    to_u128(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { raise ArgumentError.new(\"Invalid UInt128: #{self.inspect}\") }\n  end\n\n  # Same as `#to_i` but returns an `UInt128` or `nil`.\n  def to_u128?(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false) : UInt128?\n    to_u128(base, whitespace, underscore, prefix, strict, leading_zero_is_octal) { nil }\n  end\n\n  # Same as `#to_i` but returns an `UInt128` or the block's value.\n  def to_u128(base : Int = 10, whitespace : Bool = true, underscore : Bool = false, prefix : Bool = false, strict : Bool = true, leading_zero_is_octal : Bool = false, &block)\n    gen_to_ UInt128, UInt128\n  end\n\n  {% if compare_versions(Crystal::VERSION, \"1.16.0\") >= 0 %}\n    {%\n      table = (0...256).map { -1 }\n      (0...10).each do |i|\n        table[48 + i] = i\n      end\n      (0...26).each do |i|\n        table[65 + i] = i + 10\n        table[97 + i] = i + 10\n      end\n    %}\n\n    # :nodoc:\n    CHAR_TO_DIGIT = Slice(Int8).literal({{ table.splat }})\n\n    {%\n      (0...26).each do |i|\n        table[65 + i] = i + 36\n      end\n    %}\n\n    # :nodoc:\n    CHAR_TO_DIGIT62 = Slice(Int8).literal({{ table.splat }})\n  {% else %}\n    # :nodoc:\n    CHAR_TO_DIGIT = begin\n      table = StaticArray(Int8, 256).new(-1_i8)\n      10_i8.times do |i|\n        table.to_unsafe[48 + i] = i\n      end\n      26_i8.times do |i|\n        table.to_unsafe[65 + i] = i + 10\n        table.to_unsafe[97 + i] = i + 10\n      end\n      table\n    end\n\n    # :nodoc:\n    CHAR_TO_DIGIT62 = begin\n      table = CHAR_TO_DIGIT.clone\n      26_i8.times do |i|\n        table.to_unsafe[65 + i] = i + 36\n      end\n      table\n    end\n  {% end %}\n\n  # :nodoc:\n  record ToUnsignedInfo(T),\n    value : T,\n    negative : Bool,\n    invalid : Bool\n\n  private macro gen_to_(int_class, unsigned_int_class, max_positive = nil, max_negative = nil)\n    {% unsigned = int_class == unsigned_int_class %}\n    info = to_unsigned_info({{unsigned_int_class}}, base, whitespace, underscore, prefix, strict, leading_zero_is_octal, unsigned: {{unsigned}})\n\n    return yield if info.invalid\n\n    if info.negative\n      {% if max_negative %}\n        return yield if info.value > {{max_negative}}\n        (~info.value &+ 1).unsafe_as({{int_class}})\n      {% else %}\n        return yield\n      {% end %}\n    else\n      {% if max_positive %}\n        return yield if info.value > {{max_positive}}\n      {% end %}\n      {{int_class}}.new(info.value)\n    end\n  end\n\n  private def to_unsigned_info(int_class, base, whitespace, underscore, prefix, strict, leading_zero_is_octal, unsigned)\n    raise ArgumentError.new(\"Invalid base #{base}\") unless 2 <= base <= 36 || base == 62\n\n    ptr = to_unsafe\n\n    # Skip leading whitespace\n    if whitespace\n      ptr += calc_excess_left\n    end\n\n    negative = false\n\n    # Check + and -\n    case ptr.value.unsafe_chr\n    when '-'\n      if unsigned\n        return ToUnsignedInfo.new(value: int_class.new(0), negative: true, invalid: true)\n      end\n      negative = true\n      ptr += 1\n    when '+'\n      ptr += 1\n    else\n      # no sign prefix\n    end\n\n    found_digit = false\n    last_is_underscore = true\n\n    # Check leading zero\n    if ptr.value.unsafe_chr == '0'\n      ptr += 1\n      last_is_underscore = false\n      if prefix\n        case ptr.value.unsafe_chr\n        when 'b', 'B'\n          base = 2\n          ptr += 1\n        when 'x', 'X'\n          base = 16\n          ptr += 1\n        when 'o', 'O'\n          base = 8\n          ptr += 1\n        else\n          if leading_zero_is_octal\n            base = 8\n          else\n            base = 10\n            found_digit = true\n          end\n        end\n      elsif leading_zero_is_octal\n        base = 8\n      else\n        found_digit = true\n      end\n    end\n\n    value = int_class.new(0)\n    mul_overflow = ~(int_class.new(0)) // base\n    invalid = false\n\n    digits = (base == 62 ? CHAR_TO_DIGIT62 : CHAR_TO_DIGIT).to_unsafe\n    while ptr.value != 0\n      if underscore && ptr.value.unsafe_chr == '_'\n        break if last_is_underscore\n        last_is_underscore = true\n        ptr += 1\n        next\n      end\n\n      last_is_underscore = false\n      digit = digits[ptr.value]\n      if digit == -1 || digit >= base\n        break\n      end\n\n      if value > mul_overflow\n        invalid = true\n        break\n      end\n\n      value *= base\n\n      old = value\n      value &+= digit\n      if value < old\n        invalid = true\n        break\n      end\n\n      found_digit = true\n      ptr += 1\n    end\n\n    if found_digit\n      unless ptr.value == 0\n        if whitespace\n          ptr += calc_excess_right\n        end\n\n        if strict && ptr.value != 0\n          invalid = true\n        end\n      end\n    else\n      invalid = true\n    end\n\n    ToUnsignedInfo.new(value: value, negative: negative, invalid: invalid)\n  end\n\n  # Returns the result of interpreting characters in this string as a floating point number (`Float64`).\n  # This method raises an exception if the string is not a valid float representation\n  # or exceeds the range of the data type. Values representing infinity or NaN\n  # are considered valid.\n  #\n  # Options:\n  # * **whitespace**: if `true`, leading and trailing whitespaces are allowed\n  # * **strict**: if `true`, extraneous characters past the end of the number\n  #   are disallowed, unless **whitespace** is also `true` and all the trailing\n  #   characters past the number are whitespaces\n  #\n  # ```\n  # \"123.45e1\".to_f                # => 1234.5\n  # \"45.67 degrees\".to_f           # raises ArgumentError\n  # \"thx1138\".to_f(strict: false)  # raises ArgumentError\n  # \" 1.2\".to_f(whitespace: false) # raises ArgumentError\n  # \"1.2foo\".to_f(strict: false)   # => 1.2\n  # ```\n  def to_f(whitespace : Bool = true, strict : Bool = true) : Float64\n    to_f64(whitespace: whitespace, strict: strict)\n  end\n\n  # :ditto:\n  def to_f64(whitespace : Bool = true, strict : Bool = true) : Float64\n    to_f64?(whitespace: whitespace, strict: strict) || raise ArgumentError.new(\"Invalid Float64: #{self.inspect}\")\n  end\n\n  # Returns the result of interpreting characters in this string as a floating point number (`Float64`).\n  # This method returns `nil` if the string is not a valid float representation\n  # or exceeds the range of the data type. Values representing infinity or NaN\n  # are considered valid.\n  #\n  # Options:\n  # * **whitespace**: if `true`, leading and trailing whitespaces are allowed\n  # * **strict**: if `true`, extraneous characters past the end of the number\n  #   are disallowed, unless **whitespace** is also `true` and all the trailing\n  #   characters past the number are whitespaces\n  #\n  # ```\n  # \"123.45e1\".to_f?                # => 1234.5\n  # \"45.67 degrees\".to_f?           # => nil\n  # \"thx1138\".to_f?                 # => nil\n  # \" 1.2\".to_f?(whitespace: false) # => nil\n  # \"1.2foo\".to_f?(strict: false)   # => 1.2\n  # ```\n  def to_f?(whitespace : Bool = true, strict : Bool = true) : Float64?\n    to_f64?(whitespace: whitespace, strict: strict)\n  end\n\n  # :ditto:\n  def to_f64?(whitespace : Bool = true, strict : Bool = true) : Float64?\n    Float::FastFloat.to_f64?(self, whitespace, strict)\n  end\n\n  # Same as `#to_f` but returns a Float32.\n  def to_f32(whitespace : Bool = true, strict : Bool = true) : Float32\n    to_f32?(whitespace: whitespace, strict: strict) || raise ArgumentError.new(\"Invalid Float32: #{self.inspect}\")\n  end\n\n  # Same as `#to_f?` but returns a Float32.\n  def to_f32?(whitespace : Bool = true, strict : Bool = true) : Float32?\n    Float::FastFloat.to_f32?(self, whitespace, strict)\n  end\n\n  # Returns the `Char` at the given *index*.\n  #\n  # Negative indices can be used to start counting from the end of the string.\n  #\n  # Raises `IndexError` if the *index* is out of bounds.\n  #\n  # ```\n  # \"hello\"[0]  # => 'h'\n  # \"hello\"[1]  # => 'e'\n  # \"hello\"[-1] # => 'o'\n  # \"hello\"[-2] # => 'l'\n  # \"hello\"[5]  # raises IndexError\n  # ```\n  def [](index : Int) : Char\n    char_at(index) { raise IndexError.new }\n  end\n\n  # Returns the substring indicated by *range* as span of character indices.\n  #\n  # The substring ranges from `self[range.begin]` to `self[range.end]`\n  # (or `self[range.end - 1]` if the range is exclusive). It can be smaller than\n  # `range.size` if the end index is larger than `self.size`.\n  #\n  # ```\n  # s = \"abcde\"\n  # s[1..3] # => \"bcd\"\n  # # range.end > s.size\n  # s[3..7] # => \"de\"\n  # ```\n  #\n  # Open ended ranges are clamped at the start and end of `self`, respectively.\n  #\n  # ```\n  # # open ended ranges\n  # s[2..] # => \"cde\"\n  # s[..2] # => \"abc\"\n  # ```\n  #\n  # Negative range values are added to `self.size`, thus they are treated as\n  # character indices counting from the end, `-1` designating the last character.\n  #\n  # ```\n  # # negative indices, both ranges are equivalent for `s`\n  # s[1..3]   # => \"bcd\"\n  # s[-4..-2] # => \"bcd\"\n  # # Mixing negative and positive indices, both ranges are equivalent for `s`\n  # s[1..-2] # => \"bcd\"\n  # s[-4..3] # => \"bcd\"\n  # ```\n  #\n  # Raises `IndexError` if the start index it out of range (`range.begin >\n  # self.size || range.begin < -self.size). If `range.begin == self.size` an\n  # empty string is returned. If `range.begin > range.end`, an empty string is\n  # returned.\n  #\n  # ```\n  # # range.begin > array.size\n  # s[6..10] # raise IndexError\n  # # range.begin == s.size\n  # s[5..10] # => \"\"\n  # # range.begin > range.end\n  # s[3..1]   # => \"\"\n  # s[-2..-4] # => \"\"\n  # s[-2..1]  # => \"\"\n  # s[3..-4]  # => \"\"\n  # ```\n  def [](range : Range) : String\n    self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new]\n  end\n\n  # Like `#[](Range)`, but returns `nil` if `range.begin` is out of range.\n  #\n  # ```\n  # \"hello\"[6..7]? # => nil\n  # \"hello\"[6..]?  # => nil\n  # ```\n  def []?(range : Range) : String?\n    self[*Indexable.range_to_index_and_count(range, size) || return nil]?\n  end\n\n  # Returns a substring starting from the *start* character of size *count*.\n  #\n  # Negative *start* is added to `self.size`, thus it's treated as a character\n  # index counting from the end, `-1` designating the last character.\n  #\n  # Raises `IndexError` if *start* index is out of bounds.\n  # Raises `ArgumentError` if *count* is negative.\n  def [](start : Int, count : Int) : String\n    self[start, count]? || raise IndexError.new\n  end\n\n  # Like `#[](Int, Int)` but returns `nil` if the *start* index is out of bounds.\n  def []?(start : Int, count : Int) : String?\n    return byte_slice?(start, count) if single_byte_optimizable?\n\n    start, count = Indexable.normalize_start_and_count(start, count, size) { return nil }\n    return \"\" if count == 0\n    return self if count == size\n\n    start_pos, end_pos = find_start_and_end(start, count)\n    byte_count = end_pos - start_pos\n\n    String.new(byte_count) do |buffer|\n      buffer.copy_from(to_unsafe + start_pos, byte_count)\n      {byte_count, 0}\n    end\n  end\n\n  # Returns the character at *index* or `nil` if it's out of range.\n  #\n  # Negative indices can be used to start counting from the end of the string.\n  #\n  # See `#[]` for a raising alternative.\n  #\n  # ```\n  # \"hello\"[0]?  # => 'h'\n  # \"hello\"[1]?  # => 'e'\n  # \"hello\"[-1]? # => 'o'\n  # \"hello\"[-2]? # => 'l'\n  # \"hello\"[5]?  # => nil\n  # ```\n  def []?(index : Int) : Char?\n    char_at(index) { nil }\n  end\n\n  # Returns *str* if *str* is found in this string, or `nil` otherwise.\n  #\n  # ```\n  # \"crystal\"[\"cry\"]?  # => \"cry\"\n  # \"crystal\"[\"ruby\"]? # => nil\n  # ```\n  def []?(str : String | Char)\n    includes?(str) ? str : nil\n  end\n\n  def []?(regex : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String?\n    self[regex, 0, options: options]?\n  end\n\n  def []?(regex : Regex, group, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String?\n    match = match(regex, options: options)\n    match[group]? if match\n  end\n\n  # Returns *str* if *str* is found in this string.\n  #\n  # ```\n  # \"crystal\"[\"cry\"]  # => \"cry\"\n  # \"crystal\"[\"ruby\"] # raises NilAssertionError\n  # ```\n  def [](str : String | Char)\n    self[str]?.not_nil!\n  end\n\n  def [](regex : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    self[regex, options: options]?.not_nil!\n  end\n\n  def [](regex : Regex, group, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    self[regex, group, options: options]?.not_nil!\n  end\n\n  # Returns the `Char` at the given *index*.\n  #\n  # Negative indices can be used to start counting from the end of the string.\n  #\n  # Raises `IndexError` if the *index* is out of bounds.\n  #\n  # ```\n  # \"hello\".char_at(0)  # => 'h'\n  # \"hello\".char_at(1)  # => 'e'\n  # \"hello\".char_at(-1) # => 'o'\n  # \"hello\".char_at(-2) # => 'l'\n  # \"hello\".char_at(5)  # raises IndexError\n  # ```\n  def char_at(index : Int) : Char\n    char_at(index) { raise IndexError.new }\n  end\n\n  # Returns the `Char` at the given *index*, or result of running the given block if out of bounds.\n  #\n  # Negative indices can be used to start counting from the end of the string.\n  #\n  # ```\n  # \"hello\".char_at(4) { 'x' }  # => 'o'\n  # \"hello\".char_at(5) { 'x' }  # => 'x'\n  # \"hello\".char_at(-1) { 'x' } # => 'o'\n  # \"hello\".char_at(-5) { 'x' } # => 'h'\n  # \"hello\".char_at(-6) { 'x' } # => 'x'\n  # ```\n  def char_at(index : Int, &)\n    if single_byte_optimizable?\n      byte = byte_at?(index)\n      if byte\n        return byte < 0x80 ? byte.unsafe_chr : Char::REPLACEMENT\n      else\n        return yield\n      end\n    end\n\n    index += size if index < 0\n\n    byte_index = char_index_to_byte_index(index)\n    if byte_index && byte_index < @bytesize\n      Char::Reader.new(self, pos: byte_index).current_char\n    else\n      yield\n    end\n  end\n\n  # Returns a new string that results from deleting characters\n  # at the given range.\n  #\n  # ```\n  # \"abcdef\".delete_at(1..3) # => \"aef\"\n  # ```\n  #\n  # Negative indices can be used to start counting from the end of the string:\n  #\n  # ```\n  # \"abcdef\".delete_at(-3..-2) # => \"abcf\"\n  # ```\n  #\n  # Raises `IndexError` if any index is outside the bounds of this string.\n  def delete_at(range : Range) : String\n    delete_at(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new)\n  end\n\n  # Returns a new string that results from deleting the character\n  # at the given *index*.\n  #\n  # ```\n  # \"abcde\".delete_at(0) # => \"bcde\"\n  # \"abcde\".delete_at(2) # => \"abde\"\n  # \"abcde\".delete_at(4) # => \"abcd\"\n  # ```\n  #\n  # A negative *index* counts from the end of the string:\n  #\n  # ```\n  # \"abcde\".delete_at(-2) # => \"abce\"\n  # ```\n  #\n  # If *index* is outside the bounds of the string, `IndexError` is raised.\n  def delete_at(index : Int) : String\n    index += size if index < 0\n\n    byte_index = char_index_to_byte_index(index)\n    if byte_index && byte_index < @bytesize\n      char_bytesize = char_bytesize_at(byte_index)\n\n      new_bytesize = self.bytesize - char_bytesize\n      String.new(new_bytesize) do |buffer|\n        # Copy left part\n        buffer.copy_from(to_unsafe, byte_index)\n\n        # Copy right part\n        (buffer + byte_index).copy_from(\n          to_unsafe + byte_index + char_bytesize,\n          self.bytesize - byte_index - char_bytesize,\n        )\n\n        {new_bytesize, size - 1}\n      end\n    else\n      raise IndexError.new\n    end\n  end\n\n  # Returns a new string that results from deleting *count* characters\n  # starting at *start*.\n  #\n  # ```\n  # \"abcdefg\".delete_at(1, 3) # => \"aefg\"\n  # ```\n  #\n  # Deleting more characters than those in the string is valid, and just\n  # results in deleting up to the last character:\n  #\n  # ```\n  # \"abcdefg\".delete_at(3, 10) # => \"abc\"\n  # ```\n  #\n  # A negative *start* counts from the end of the string:\n  #\n  # ```\n  # \"abcdefg\".delete_at(-3, 2) # => \"abcdg\"\n  # ```\n  #\n  # If *count* is negative, `ArgumentError` is raised.\n  #\n  # If *start* is outside the bounds of the string, `ArgumentError`\n  # is raised.\n  #\n  # However, *start* can be the position that is exactly the end of the string:\n  #\n  # ```\n  # \"abcd\".delete_at(4, 3) # => \"abcd\"\n  # ```\n  def delete_at(start : Int, count : Int) : String\n    start, count = Indexable.normalize_start_and_count(start, count, size)\n\n    case count\n    when 0\n      self\n    when size\n      \"\"\n    else\n      if single_byte_optimizable?\n        byte_delete_at(start, count, count)\n      else\n        unicode_delete_at(start, count)\n      end\n    end\n  end\n\n  # :ditto:\n  @[Deprecated(\"Use `#delete_at(start, count)` instead\")]\n  def delete_at(*, index start : Int, count : Int) : String\n    delete_at(start, count)\n  end\n\n  private def byte_delete_at(start, count, byte_count)\n    new_bytesize = bytesize - byte_count\n    String.new(new_bytesize) do |buffer|\n      # Copy left part\n      buffer.copy_from(to_unsafe, start)\n\n      # Copy right part\n      (buffer + start).copy_from(\n        to_unsafe + start + byte_count,\n        bytesize - start - byte_count,\n      )\n\n      {new_bytesize, size - count}\n    end\n  end\n\n  private def unicode_delete_at(start, count)\n    start_pos, end_pos = find_start_and_end(start, count)\n    byte_delete_at(start_pos, count, end_pos - start_pos)\n  end\n\n  private def find_start_and_end(start, count)\n    start_pos = nil\n    end_pos = nil\n\n    reader = Char::Reader.new(self)\n    i = 0\n\n    reader.each do\n      if i == start\n        start_pos = reader.pos\n      elsif i == start + count\n        end_pos = reader.pos\n        i += 1\n        break\n      end\n      i += 1\n    end\n\n    end_pos = reader.pos if i == start + count\n\n    {start_pos.not_nil!, end_pos.not_nil!}\n  end\n\n  # Returns a new string built from *count* bytes starting at *start* byte.\n  #\n  # *start* can be negative to start counting\n  # from the end of the string.\n  # If *count* is bigger than the number of bytes from *start* to `#bytesize`,\n  # only remaining bytes are returned.\n  #\n  # This method should be avoided,\n  # unless the string is proven to be ASCII-only (for example `#ascii_only?`),\n  # or the byte positions are known to be at character boundaries.\n  # Otherwise, multi-byte characters may be split, leading to an invalid UTF-8 encoding.\n  #\n  # Raises `IndexError` if the *start* index is out of bounds.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  #\n  # ```\n  # \"hello\".byte_slice(0, 2)   # => \"he\"\n  # \"hello\".byte_slice(0, 100) # => \"hello\"\n  # \"hello\".byte_slice(-2, 3)  # => \"lo\"\n  # \"hello\".byte_slice(-2, 5)  # => \"lo\"\n  # \"¥hello\".byte_slice(0, 2)  # => \"¥\"\n  # \"¥hello\".byte_slice(2, 2)  # => \"he\"\n  # \"¥hello\".byte_slice(0, 1)  # => \"\\xC2\" (invalid UTF-8 character)\n  # \"¥hello\".byte_slice(1, 1)  # => \"\\xA5\" (invalid UTF-8 character)\n  # \"¥hello\".byte_slice(1, 2)  # => \"\\xA5h\" (invalid UTF-8 character)\n  # \"hello\".byte_slice(6, 2)   # raises IndexError\n  # \"hello\".byte_slice(-6, 2)  # raises IndexError\n  # \"hello\".byte_slice(0, -2)  # raises ArgumentError\n  # ```\n  def byte_slice(start : Int, count : Int) : String\n    byte_slice?(start, count) || raise IndexError.new\n  end\n\n  # Returns a new string built from byte in *range*.\n  #\n  # Byte indices can be negative to start counting from the end of the string.\n  # If the end index is bigger than `#bytesize`, only remaining bytes are returned.\n  #\n  # This method should be avoided,\n  # unless the string is proven to be ASCII-only (for example `#ascii_only?`),\n  # or the byte positions are known to be at character boundaries.\n  # Otherwise, multi-byte characters may be split, leading to an invalid UTF-8 encoding.\n  #\n  # Raises `IndexError` if the *range* begin is out of bounds.\n  #\n  # ```\n  # \"hello\".byte_slice(0..2)   # => \"hel\"\n  # \"hello\".byte_slice(0..100) # => \"hello\"\n  # \"hello\".byte_slice(-2..3)  # => \"l\"\n  # \"hello\".byte_slice(-2..5)  # => \"lo\"\n  # \"¥hello\".byte_slice(0...2) # => \"¥\"\n  # \"¥hello\".byte_slice(2...4) # => \"he\"\n  # \"¥hello\".byte_slice(0..0)  # => \"\\xC2\" (invalid UTF-8 character)\n  # \"¥hello\".byte_slice(1..1)  # => \"\\xA5\" (invalid UTF-8 character)\n  # \"¥hello\".byte_slice(1..2)  # => \"\\xA5h\" (invalid UTF-8 character)\n  # \"hello\".byte_slice(6..2)   # raises IndexError\n  # \"hello\".byte_slice(-6..2)  # raises IndexError\n  # ```\n  def byte_slice(range : Range) : String\n    byte_slice(*Indexable.range_to_index_and_count(range, bytesize) || raise IndexError.new)\n  end\n\n  # Like `byte_slice(Int, Int)` but returns `Nil` if the *start* index is out of bounds.\n  #\n  # Raises `ArgumentError` if *count* is negative.\n  #\n  # ```\n  # \"hello\".byte_slice?(0, 2)   # => \"he\"\n  # \"hello\".byte_slice?(0, 100) # => \"hello\"\n  # \"hello\".byte_slice?(6, 2)   # => nil\n  # \"hello\".byte_slice?(-6, 2)  # => nil\n  # \"hello\".byte_slice?(0, -2)  # raises ArgumentError\n  # ```\n  def byte_slice?(start : Int, count : Int) : String | Nil\n    start, count = Indexable.normalize_start_and_count(start, count, bytesize) { return nil }\n    return \"\" if count == 0\n    return self if count == bytesize\n\n    single_byte_optimizable = single_byte_optimizable?\n\n    String.new(count) do |buffer|\n      buffer.copy_from(to_unsafe + start, count)\n      slice_size = single_byte_optimizable ? count : 0\n      {count, slice_size}\n    end\n  end\n\n  # Like `byte_slice(Range)` but returns `Nil` if *range* begin is out of bounds.\n  #\n  # ```\n  # \"hello\".byte_slice?(0..2)   # => \"hel\"\n  # \"hello\".byte_slice?(0..100) # => \"hello\"\n  # \"hello\".byte_slice?(6..8)   # => nil\n  # \"hello\".byte_slice?(-6..2)  # => nil\n  # ```\n  def byte_slice?(range : Range) : String?\n    byte_slice?(*Indexable.range_to_index_and_count(range, bytesize) || return nil)\n  end\n\n  # Returns a substring starting from the *start* byte.\n  #\n  # *start* can be negative to start counting\n  # from the end of the string.\n  #\n  # This method should be avoided,\n  # unless the string is proven to be ASCII-only (for example `#ascii_only?`),\n  # or the byte positions are known to be at character boundaries.\n  # Otherwise, multi-byte characters may be split, leading to an invalid UTF-8 encoding.\n  #\n  # Raises `IndexError` if *start* index is out of bounds.\n  #\n  # ```\n  # \"hello\".byte_slice(0)  # => \"hello\"\n  # \"hello\".byte_slice(2)  # => \"llo\"\n  # \"hello\".byte_slice(-2) # => \"lo\"\n  # \"¥hello\".byte_slice(2) # => \"hello\"\n  # \"¥hello\".byte_slice(1) # => \"\\xA5hello\" (invalid UTF-8 character)\n  # \"hello\".byte_slice(6)  # raises IndexError\n  # \"hello\".byte_slice(-6) # raises IndexError\n  # ```\n  def byte_slice(start : Int) : String\n    byte_slice start, bytesize\n  end\n\n  # Returns a substring starting from the *start* byte.\n  #\n  # *start* can be negative to start counting\n  # from the end of the string.\n  #\n  # This method should be avoided,\n  # unless the string is proven to be ASCII-only (for example `#ascii_only?`),\n  # or the byte positions are known to be at character boundaries.\n  # Otherwise, multi-byte characters may be split, leading to an invalid UTF-8 encoding.\n  #\n  # Returns `nil` if *start* index is out of bounds.\n  #\n  # ```\n  # \"hello\".byte_slice?(0)  # => \"hello\"\n  # \"hello\".byte_slice?(2)  # => \"llo\"\n  # \"hello\".byte_slice?(-2) # => \"lo\"\n  # \"¥hello\".byte_slice?(2) # => \"hello\"\n  # \"¥hello\".byte_slice?(1) # => \"\\xA5hello\" (invalid UTF-8 character)\n  # \"hello\".byte_slice?(6)  # => nil\n  # \"hello\".byte_slice?(-6) # => nil\n  # ```\n  def byte_slice?(start : Int) : String?\n    byte_slice? start, bytesize\n  end\n\n  # Returns the codepoint of the character at the given *index*.\n  #\n  # Negative indices can be used to start counting from the end of the string.\n  #\n  # Raises `IndexError` if the *index* is out of bounds.\n  #\n  # See also: `Char#ord`.\n  #\n  # ```\n  # \"hello\".codepoint_at(0)  # => 104\n  # \"hello\".codepoint_at(-1) # => 111\n  # \"hello\".codepoint_at(5)  # raises IndexError\n  # ```\n  def codepoint_at(index) : Int32\n    char_at(index).ord\n  end\n\n  # Returns the byte at the given *index*.\n  #\n  # Raises `IndexError` if the *index* is out of bounds.\n  #\n  # ```\n  # \"¥hello\".byte_at(0)  # => 194\n  # \"¥hello\".byte_at(1)  # => 165\n  # \"¥hello\".byte_at(2)  # => 104\n  # \"¥hello\".byte_at(-1) # => 111\n  # \"¥hello\".byte_at(6)  # => 111\n  # \"¥hello\".byte_at(7)  # raises IndexError\n  # ```\n  def byte_at(index) : UInt8\n    byte_at(index) { raise IndexError.new }\n  end\n\n  # Returns the byte at the given *index*, or `nil` if out of bounds.\n  #\n  # ```\n  # \"¥hello\".byte_at?(0)  # => 194\n  # \"¥hello\".byte_at?(1)  # => 165\n  # \"¥hello\".byte_at?(2)  # => 104\n  # \"¥hello\".byte_at?(-1) # => 111\n  # \"¥hello\".byte_at?(6)  # => 111\n  # \"¥hello\".byte_at?(7)  # => nil\n  # ```\n  def byte_at?(index) : UInt8 | Nil\n    byte_at(index) { nil }\n  end\n\n  # Returns the byte at the given *index*, or yields if out of bounds.\n  #\n  # ```\n  # \"¥hello\".byte_at(6) { \"OUT OF BOUNDS\" } # => 111\n  # \"¥hello\".byte_at(7) { \"OUT OF BOUNDS\" } # => \"OUT OF BOUNDS\"\n  # ```\n  def byte_at(index, &)\n    index += bytesize if index < 0\n    if 0 <= index < bytesize\n      to_unsafe[index]\n    else\n      yield\n    end\n  end\n\n  # Returns the byte at the given *index* without bounds checking.\n  @[Deprecated(\"Use `to_unsafe[index]` instead.\")]\n  def unsafe_byte_at(index : Int) : UInt8\n    to_unsafe[index]\n  end\n\n  # Returns a new `String` with each uppercase letter replaced with its lowercase counterpart.\n  #\n  # ```\n  # \"hEllO\".downcase # => \"hello\"\n  # ```\n  def downcase(options : Unicode::CaseOptions = :none) : String\n    return self if empty?\n\n    if single_byte_optimizable? && (options.none? || options.ascii?)\n      return String.new(bytesize) do |buffer|\n        bytesize.times do |i|\n          byte = to_unsafe[i]\n          buffer[i] = 'A'.ord <= byte <= 'Z'.ord ? byte + 32 : byte\n        end\n        {@bytesize, @length}\n      end\n    end\n\n    String.build(bytesize) { |io| downcase io, options }\n  end\n\n  # Writes a downcased version of `self` to the given *io*.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"hEllO\".downcase io\n  # io.to_s # => \"hello\"\n  # ```\n  def downcase(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    each_char do |char|\n      char.downcase(io, options)\n    end\n  end\n\n  # Returns a new `String` with each lowercase letter replaced with its uppercase counterpart.\n  #\n  # ```\n  # \"hEllO\".upcase # => \"HELLO\"\n  # ```\n  def upcase(options : Unicode::CaseOptions = :none) : String\n    return self if empty?\n\n    if single_byte_optimizable? && (options.none? || options.ascii?)\n      return String.new(bytesize) do |buffer|\n        bytesize.times do |i|\n          byte = to_unsafe[i]\n          buffer[i] = 'a'.ord <= byte <= 'z'.ord ? byte - 32 : byte\n        end\n        {@bytesize, @length}\n      end\n    end\n\n    String.build(bytesize) { |io| upcase io, options }\n  end\n\n  # Writes a upcased version of `self` to the given *io*.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"hEllO\".upcase io\n  # io.to_s # => \"HELLO\"\n  # ```\n  def upcase(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    each_char do |char|\n      char.upcase(io, options)\n    end\n  end\n\n  # Returns a new `String` with the first letter converted to uppercase and every\n  # subsequent letter converted to lowercase.\n  #\n  # ```\n  # \"hEllO\".capitalize # => \"Hello\"\n  # ```\n  def capitalize(options : Unicode::CaseOptions = :none) : String\n    return self if empty?\n\n    if single_byte_optimizable? && (options.none? || options.ascii?)\n      return String.new(bytesize) do |buffer|\n        bytesize.times do |i|\n          byte = to_unsafe[i]\n\n          buffer[i] = if byte >= 0x80\n                        byte\n                      elsif i.zero?\n                        byte.unsafe_chr.upcase.ord.to_u8!\n                      else\n                        byte.unsafe_chr.downcase.ord.to_u8!\n                      end\n        end\n        {@bytesize, @length}\n      end\n    end\n\n    String.build(bytesize) { |io| capitalize io, options }\n  end\n\n  # Writes a capitalized version of `self` to the given *io*.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"hEllO\".capitalize io\n  # io.to_s # => \"Hello\"\n  # ```\n  def capitalize(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    each_char_with_index do |char, i|\n      if i.zero?\n        char.titlecase(io, options)\n      else\n        char.downcase(io, options)\n      end\n    end\n  end\n\n  # Returns a new `String` with the first letter after any space converted to uppercase and every other letter converted to lowercase.\n  # Optionally, if *underscore_to_space* is `true`, underscores (`_`) will be converted to a space and the following letter converted to uppercase.\n  #\n  # ```\n  # \"hEllO tAb\\tworld\".titleize                   # => \"Hello Tab\\tWorld\"\n  # \"  spaces before\".titleize                    # => \"  Spaces Before\"\n  # \"x-men: the last stand\".titleize              # => \"X-men: The Last Stand\"\n  # \"foo_bar\".titleize                            # => \"Foo_bar\"\n  # \"foo_bar\".titleize(underscore_to_space: true) # => \"Foo Bar\"\n  # ```\n  def titleize(options : Unicode::CaseOptions = :none, *, underscore_to_space : Bool = false) : String\n    return self if empty?\n\n    if single_byte_optimizable? && (options.none? || options.ascii?)\n      upcase_next = true\n\n      return String.new(bytesize) do |buffer|\n        bytesize.times do |i|\n          byte = to_unsafe[i]\n          if byte < 0x80\n            char = byte.unsafe_chr\n            replaced_char, upcase_next = if upcase_next\n                                           {char.upcase, false}\n                                         elsif underscore_to_space && '_' == char\n                                           {' ', true}\n                                         else\n                                           {char.downcase, char.ascii_whitespace?}\n                                         end\n\n            buffer[i] = replaced_char.ord.to_u8!\n          else\n            buffer[i] = byte\n            upcase_next = false\n          end\n        end\n        {@bytesize, @length}\n      end\n    end\n\n    String.build(bytesize) { |io| titleize io, options, underscore_to_space: underscore_to_space }\n  end\n\n  # Writes a titleized version of `self` to the given *io*.\n  # Optionally, if *underscore_to_space* is `true`, underscores (`_`) will be converted to a space and the following letter converted to uppercase.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"x-men: the last stand\".titleize io\n  # io.to_s # => \"X-men: The Last Stand\"\n  # ```\n  def titleize(io : IO, options : Unicode::CaseOptions = :none, *, underscore_to_space : Bool = false) : Nil\n    upcase_next = true\n\n    each_char_with_index do |char, i|\n      if upcase_next\n        upcase_next = false\n        char.titlecase(io, options)\n      elsif underscore_to_space && '_' == char\n        upcase_next = true\n        io << ' '\n      else\n        upcase_next = char.whitespace?\n        char.downcase(io, options)\n      end\n    end\n  end\n\n  # Returns the result of normalizing this `String` according to the given\n  # [Unicode normalization form](https://unicode.org/reports/tr15/).\n  #\n  # ```\n  # str = \"\\u{1E9B}\\u{0323}\"                # => \"ẛ̣\"\n  # str.unicode_normalize.codepoints        # => [0x1E9B, 0x0323]\n  # str.unicode_normalize(:nfd).codepoints  # => [0x017F, 0x0323, 0x0307]\n  # str.unicode_normalize(:nfkc).codepoints # => [0x1E69]\n  # str.unicode_normalize(:nfkd).codepoints # => [0x0073, 0x0323, 0x0307]\n  # ```\n  def unicode_normalize(form : Unicode::NormalizationForm = :nfc) : String\n    return self if Unicode.quick_check_normalized(self, form).yes?\n    String.build { |io| do_unicode_normalize(io, form) }\n  end\n\n  # Normalizes this `String` according to the given\n  # [Unicode normalization form](https://unicode.org/reports/tr15/) and\n  # writes the result to the given *io*.\n  def unicode_normalize(io : IO, form : Unicode::NormalizationForm = :nfc) : Nil\n    return io << self if Unicode.quick_check_normalized(self, form).yes?\n    do_unicode_normalize(io, form)\n  end\n\n  private def do_unicode_normalize(io, form)\n    # the maximum number of code points after any normalization is `3 * size` as\n    # required by the Unicode standard; allow a single reallocation as the\n    # majority of strings have only few characters that aren't normalized\n    codepoints = Array(Int32).new((size * 3 + 1) // 2)\n\n    each_char do |char|\n      case form\n      in .nfc?, .nfd?\n        Unicode.canonical_decompose(codepoints, char)\n      in .nfkc?, .nfkd?\n        Unicode.compatibility_decompose(codepoints, char)\n      end\n    end\n\n    Unicode.canonical_order!(codepoints)\n\n    case form\n    in .nfc?, .nfkc?\n      Unicode.canonical_compose!(codepoints) { |char| io << char }\n    in .nfd?, .nfkd?\n      codepoints.each { |codepoint| io << codepoint.unsafe_chr }\n    end\n  end\n\n  # Returns whether this `String` is in the given\n  # [Unicode normalization form](https://unicode.org/reports/tr15/).\n  #\n  # ```\n  # foo = \"\\u{00E0}\"              # => \"à\"\n  # foo.unicode_normalized?       # => true\n  # foo.unicode_normalized?(:nfd) # => false\n  #\n  # bar = \"\\u{0061}\\u{0300}\"      # => \"à\"\n  # bar.unicode_normalized?       # => false\n  # bar.unicode_normalized?(:nfd) # => true\n  # ```\n  def unicode_normalized?(form : Unicode::NormalizationForm = :nfc) : Bool\n    case Unicode.quick_check_normalized(self, form)\n    in .yes?\n      true\n    in .no?\n      false\n    in .maybe?\n      self == String.build { |io| do_unicode_normalize(io, form) }\n    end\n  end\n\n  # Returns a new `String` with the last carriage return removed (that is, it\n  # will remove \\n, \\r, and \\r\\n).\n  #\n  # ```\n  # \"string\\r\\n\".chomp # => \"string\"\n  # \"string\\n\\r\".chomp # => \"string\\n\"\n  # \"string\\n\".chomp   # => \"string\"\n  # \"string\".chomp     # => \"string\"\n  # \"x\".chomp.chomp    # => \"x\"\n  # ```\n  def chomp : String\n    return self if empty?\n\n    case to_unsafe[bytesize - 1]\n    when '\\n'\n      if bytesize > 1 && to_unsafe[bytesize - 2] === '\\r'\n        unsafe_byte_slice_string(0, bytesize - 2, @length > 0 ? @length - 2 : 0)\n      else\n        unsafe_byte_slice_string(0, bytesize - 1, @length > 0 ? @length - 1 : 0)\n      end\n    when '\\r'\n      unsafe_byte_slice_string(0, bytesize - 1, @length > 0 ? @length - 1 : 0)\n    else\n      self\n    end\n  end\n\n  # Returns a new `String` with *suffix* removed from the end of the string.\n  # If *suffix* is `'\\n'` then `\"\\r\\n\"` is also removed if the string ends with it.\n  #\n  # ```\n  # \"hello\".chomp('o') # => \"hell\"\n  # \"hello\".chomp('a') # => \"hello\"\n  # ```\n  def chomp(suffix : Char) : String\n    if suffix == '\\n'\n      chomp\n    elsif ends_with?(suffix)\n      unsafe_byte_slice_string(0, bytesize - suffix.bytesize)\n    else\n      self\n    end\n  end\n\n  # Returns a new `String` with *suffix* removed from the end of the string.\n  # If *suffix* is `\"\\n\"` then `\"\\r\\n\"` is also removed if the string ends with it.\n  #\n  # ```\n  # \"hello\".chomp(\"llo\") # => \"he\"\n  # \"hello\".chomp(\"ol\")  # => \"hello\"\n  # ```\n  def chomp(suffix : String) : String\n    if suffix.bytesize == 1\n      chomp(suffix.to_unsafe[0].unsafe_chr)\n    elsif ends_with?(suffix)\n      unsafe_byte_slice_string(0, bytesize - suffix.bytesize)\n    else\n      self\n    end\n  end\n\n  # Returns a new `String` with the first char removed from it.\n  # Applying lchop to an empty string returns an empty string.\n  #\n  # ```\n  # \"hello\".lchop # => \"ello\"\n  # \"\".lchop      # => \"\"\n  # ```\n  def lchop : String\n    lchop? || \"\"\n  end\n\n  # Returns a new `String` with *prefix* removed from the beginning of the string.\n  #\n  # ```\n  # \"hello\".lchop('h')   # => \"ello\"\n  # \"hello\".lchop('g')   # => \"hello\"\n  # \"hello\".lchop(\"hel\") # => \"lo\"\n  # \"hello\".lchop(\"eh\")  # => \"hello\"\n  # ```\n  def lchop(prefix : Char | String) : String\n    lchop?(prefix) || self\n  end\n\n  # Returns a new `String` with the first char removed from it if possible, else returns `nil`.\n  #\n  # ```\n  # \"hello\".lchop? # => \"ello\"\n  # \"\".lchop?      # => nil\n  # ```\n  def lchop? : String?\n    return if empty?\n\n    if single_byte_optimizable?\n      unsafe_byte_slice_string(1, bytesize - 1)\n    else\n      first_char_bytesize = char_bytesize_at(0)\n      unsafe_byte_slice_string(first_char_bytesize, bytesize - first_char_bytesize)\n    end\n  end\n\n  # Returns a new `String` with *prefix* removed from the beginning of the string if possible, else returns `nil`.\n  #\n  # ```\n  # \"hello\".lchop?('h')   # => \"ello\"\n  # \"hello\".lchop?('g')   # => nil\n  # \"hello\".lchop?(\"hel\") # => \"lo\"\n  # \"hello\".lchop?(\"eh\")  # => nil\n  # ```\n  def lchop?(prefix : Char | String) : String?\n    if starts_with?(prefix)\n      unsafe_byte_slice_string(prefix.bytesize, bytesize - prefix.bytesize)\n    end\n  end\n\n  # Returns a new `String` with the last character removed.\n  # Applying rchop to an empty string returns an empty string.\n  #\n  # ```\n  # \"string\\r\\n\".rchop # => \"string\\r\"\n  # \"string\\n\\r\".rchop # => \"string\\n\"\n  # \"string\\n\".rchop   # => \"string\"\n  # \"strings\".rchop    # => \"string\"\n  # \"x\".rchop.rchop    # => \"\"\n  # ```\n  def rchop : String\n    rchop? || \"\"\n  end\n\n  # Returns a new `String` with *suffix* removed from the end of the string.\n  #\n  # ```\n  # \"strings\".rchop('s')  # => \"string\"\n  # \"string\".rchop('x')   # => \"string\"\n  # \"string\".rchop(\"ing\") # => \"str\"\n  # \"string\".rchop(\"inx\") # => \"string\"\n  # ```\n  def rchop(suffix : Char | String) : String\n    rchop?(suffix) || self\n  end\n\n  # Returns a new `String` with the last character removed if possible, else returns `nil`.\n  #\n  # ```\n  # \"string\\r\\n\".rchop? # => \"string\\r\"\n  # \"string\\n\\r\".rchop? # => \"string\\n\"\n  # \"string\\n\".rchop?   # => \"string\"\n  # \"strings\".rchop?    # => \"string\"\n  # \"\".rchop?           # => nil\n  # ```\n  def rchop? : String?\n    return if empty?\n\n    unsafe_byte_slice_string(0, Char::Reader.new(at_end: self).pos, @length > 0 ? @length - 1 : 0)\n  end\n\n  # Returns a new `String` with *suffix* removed from the end of the string if possible, else returns `nil`.\n  #\n  # ```\n  # \"strings\".rchop?('s')  # => \"string\"\n  # \"string\".rchop?('x')   # => nil\n  # \"string\".rchop?(\"ing\") # => \"str\"\n  # \"string\".rchop?(\"inx\") # => nil\n  # ```\n  def rchop?(suffix : Char | String) : String?\n    if ends_with?(suffix)\n      unsafe_byte_slice_string(0, bytesize - suffix.bytesize)\n    end\n  end\n\n  # Returns a slice of bytes containing this string encoded in the given encoding.\n  #\n  # The *invalid* argument can be:\n  # * `nil`: an exception is raised on invalid byte sequences\n  # * `:skip`: invalid byte sequences are ignored\n  #\n  # ```\n  # \"好\".encode(\"GB2312\") # => Bytes[186, 195]\n  # \"好\".bytes            # => [229, 165, 189]\n  # ```\n  def encode(encoding : String, invalid : Symbol? = nil) : Bytes\n    io = IO::Memory.new\n    String.encode(to_slice, \"UTF-8\", encoding, io, invalid)\n    io.to_slice\n  end\n\n  # :nodoc:\n  def self.encode(slice, from, to, io, invalid)\n    IO::EncodingOptions.check_invalid(invalid)\n\n    inbuf_ptr = slice.to_unsafe\n    inbytesleft = LibC::SizeT.new(slice.size)\n    outbuf = uninitialized UInt8[1024]\n\n    Crystal::Iconv.new(from, to, invalid) do |iconv|\n      while inbytesleft > 0\n        outbuf_ptr = outbuf.to_unsafe\n        outbytesleft = LibC::SizeT.new(outbuf.size)\n        err = iconv.convert(pointerof(inbuf_ptr), pointerof(inbytesleft), pointerof(outbuf_ptr), pointerof(outbytesleft))\n        if err == Crystal::Iconv::ERROR\n          iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft))\n        end\n        io.write(outbuf.to_slice[0, outbuf.size - outbytesleft])\n      end\n\n      outbuf_ptr = outbuf.to_unsafe\n      outbytesleft = LibC::SizeT.new(outbuf.size)\n      err = iconv.convert(Pointer(UInt8*).null, Pointer(LibC::SizeT).null, pointerof(outbuf_ptr), pointerof(outbytesleft))\n      if err == Crystal::Iconv::ERROR\n        iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft))\n      end\n      io.write(outbuf.to_slice[0, outbuf.size - outbytesleft])\n    end\n  end\n\n  # Interprets this string as containing a sequence of hexadecimal values\n  # and decodes it as a slice of bytes. Two consecutive bytes in the string\n  # represent a byte in the returned slice.\n  #\n  # Raises `ArgumentError` if this string does not denote an hexstring.\n  #\n  # ```\n  # \"0102031aff\".hexbytes  # => Bytes[1, 2, 3, 26, 255]\n  # \"1\".hexbytes           # raises ArgumentError\n  # \"hello world\".hexbytes # raises ArgumentError\n  # ```\n  def hexbytes : Bytes\n    hexbytes? || raise(ArgumentError.new(\"#{self} is not a hexstring\"))\n  end\n\n  # Interprets this string as containing a sequence of hexadecimal values\n  # and decodes it as a slice of bytes. Two consecutive bytes in the string\n  # represent a byte in the returned slice.\n  #\n  # Returns `nil` if this string does not denote an hexstring.\n  #\n  # ```\n  # \"0102031aff\".hexbytes?  # => Bytes[1, 2, 3, 26, 255]\n  # \"1\".hexbytes?           # => nil\n  # \"hello world\".hexbytes? # => nil\n  # ```\n  def hexbytes? : Bytes?\n    return unless bytesize.divisible_by?(2)\n\n    bytes = Bytes.new(bytesize // 2)\n\n    i = 0\n    while i < bytesize\n      high_nibble = to_unsafe[i].unsafe_chr.to_u8?(16)\n      low_nibble = to_unsafe[i + 1].unsafe_chr.to_u8?(16)\n      return unless high_nibble && low_nibble\n\n      bytes[i // 2] = (high_nibble << 4) | low_nibble\n      i += 2\n    end\n\n    bytes\n  end\n\n  # Returns a new `String` that results of inserting *other* in `self` at *index*.\n  # Negative indices count from the end of the string, and insert **after**\n  # the given index.\n  #\n  # Raises `IndexError` if the index is out of bounds.\n  #\n  # ```\n  # \"abcd\".insert(0, 'X')  # => \"Xabcd\"\n  # \"abcd\".insert(3, 'X')  # => \"abcXd\"\n  # \"abcd\".insert(4, 'X')  # => \"abcdX\"\n  # \"abcd\".insert(-3, 'X') # => \"abXcd\"\n  # \"abcd\".insert(-1, 'X') # => \"abcdX\"\n  #\n  # \"abcd\".insert(5, 'X')  # raises IndexError\n  # \"abcd\".insert(-6, 'X') # raises IndexError\n  # ```\n  def insert(index : Int, other : Char) : String\n    index = index.to_i\n    index += size + 1 if index < 0\n\n    byte_index = char_index_to_byte_index(index)\n    raise IndexError.new unless byte_index\n\n    bytes, count = String.char_bytes_and_bytesize(other)\n\n    new_bytesize = bytesize + count\n    new_size = (single_byte_optimizable? && other.ascii?) ? new_bytesize : 0\n\n    insert_impl(byte_index, bytes.to_unsafe, count, new_bytesize, new_size)\n  end\n\n  # Returns a new `String` that results of inserting *other* in `self` at *index*.\n  # Negative indices count from the end of the string, and insert **after**\n  # the given index.\n  #\n  # Raises `IndexError` if the index is out of bounds.\n  #\n  # ```\n  # \"abcd\".insert(0, \"FOO\")  # => \"FOOabcd\"\n  # \"abcd\".insert(3, \"FOO\")  # => \"abcFOOd\"\n  # \"abcd\".insert(4, \"FOO\")  # => \"abcdFOO\"\n  # \"abcd\".insert(-3, \"FOO\") # => \"abFOOcd\"\n  # \"abcd\".insert(-1, \"FOO\") # => \"abcdFOO\"\n  #\n  # \"abcd\".insert(5, \"FOO\")  # raises IndexError\n  # \"abcd\".insert(-6, \"FOO\") # raises IndexError\n  # ```\n  def insert(index : Int, other : String) : String\n    index = index.to_i\n    index += size + 1 if index < 0\n\n    byte_index = char_index_to_byte_index(index)\n    raise IndexError.new unless byte_index\n\n    new_bytesize = bytesize + other.bytesize\n    new_size = single_byte_optimizable? && other.single_byte_optimizable? ? new_bytesize : 0\n\n    insert_impl(byte_index, other.to_unsafe, other.bytesize, new_bytesize, new_size)\n  end\n\n  private def insert_impl(byte_index, other, other_bytesize, new_bytesize, new_size)\n    String.new(new_bytesize) do |buffer|\n      buffer.copy_from(to_unsafe, byte_index)\n      buffer += byte_index\n      buffer.copy_from(other, other_bytesize)\n      buffer += other_bytesize\n      buffer.copy_from(to_unsafe + byte_index, bytesize - byte_index)\n      {new_bytesize, new_size}\n    end\n  end\n\n  # Returns a new `String` with leading and trailing whitespace removed.\n  #\n  # ```\n  # \"    hello    \".strip # => \"hello\"\n  # \"\\tgoodbye\\r\\n\".strip # => \"goodbye\"\n  # ```\n  def strip : String\n    excess_left = calc_excess_left\n    if excess_left == bytesize\n      return \"\"\n    end\n\n    excess_right = calc_excess_right\n    remove_excess(excess_left, excess_right)\n  end\n\n  # Returns a new string where leading and trailing occurrences of *char* are removed.\n  #\n  # ```\n  # \"aaabcdaaa\".strip('a') # => \"bcd\"\n  # ```\n  def strip(char : Char) : String\n    return self if empty?\n\n    excess_left = calc_excess_left(char)\n    if excess_left == bytesize\n      return \"\"\n    end\n\n    excess_right = calc_excess_right(char)\n    remove_excess(excess_left, excess_right)\n  end\n\n  # Returns a new string where leading and trailing occurrences of any char\n  # in *chars* are removed. The *chars* argument is not a prefix or suffix;\n  # rather; all combinations of its values are stripped.\n  #\n  # ```\n  # \"abcdefcba\".strip(\"abc\") # => \"def\"\n  # ```\n  def strip(chars : String) : String\n    return self if empty?\n\n    case chars.size\n    when 0\n      self\n    when 1\n      strip(chars[0])\n    else\n      excess_left = calc_excess_left(chars)\n      if excess_left == bytesize\n        return \"\"\n      end\n\n      excess_right = calc_excess_right(chars)\n      remove_excess(excess_left, excess_right)\n    end\n  end\n\n  # Returns a new string where leading and trailing characters for which\n  # the block returns a *truthy* value are removed.\n  #\n  # ```\n  # \"bcadefcba\".strip { |c| 'a' <= c <= 'c' } # => \"def\"\n  # ```\n  def strip(&block : Char -> _) : String\n    return self if empty?\n\n    excess_left = calc_excess_left { |c| yield c }\n    if excess_left == bytesize\n      return \"\"\n    end\n\n    excess_right = calc_excess_right { |c| yield c }\n    remove_excess(excess_left, excess_right)\n  end\n\n  # Returns a new `String` with trailing whitespace removed.\n  #\n  # ```\n  # \"    hello    \".rstrip # => \"    hello\"\n  # \"\\tgoodbye\\r\\n\".rstrip # => \"\\tgoodbye\"\n  # ```\n  def rstrip : String\n    remove_excess_right(calc_excess_right)\n  end\n\n  # Returns a new string with trailing occurrences of *char* removed.\n  #\n  # ```\n  # \"aaabcdaaa\".rstrip('a') # => \"aaabcd\"\n  # ```\n  def rstrip(char : Char) : String\n    return self if empty?\n\n    remove_excess_right(calc_excess_right(char))\n  end\n\n  # Returns a new string where trailing occurrences of any char\n  # in *chars* are removed. The *chars* argument is not a suffix;\n  # rather; all combinations of its values are stripped.\n  #\n  # ```\n  # \"abcdefcba\".rstrip(\"abc\") # => \"abcdef\"\n  # ```\n  def rstrip(chars : String) : String\n    return self if empty?\n\n    case chars.size\n    when 0\n      self\n    when 1\n      rstrip(chars[0])\n    else\n      remove_excess_right(calc_excess_right(chars))\n    end\n  end\n\n  # Returns a new string where trailing characters for which\n  # the block returns a *truthy* value are removed.\n  #\n  # ```\n  # \"bcadefcba\".rstrip { |c| 'a' <= c <= 'c' } # => \"bcadef\"\n  # ```\n  def rstrip(&block : Char -> _) : String\n    return self if empty?\n\n    excess_right = calc_excess_right { |c| yield c }\n    remove_excess_right(excess_right)\n  end\n\n  # Returns a new `String` with leading whitespace removed.\n  #\n  # ```\n  # \"    hello    \".lstrip # => \"hello    \"\n  # \"\\tgoodbye\\r\\n\".lstrip # => \"goodbye\\r\\n\"\n  # ```\n  def lstrip : String\n    remove_excess_left(calc_excess_left)\n  end\n\n  # Returns a new string with leading occurrences of *char* removed.\n  #\n  # ```\n  # \"aaabcdaaa\".lstrip('a') # => \"bcdaaa\"\n  # ```\n  def lstrip(char : Char) : String\n    return self if empty?\n\n    remove_excess_left(calc_excess_left(char))\n  end\n\n  # Returns a new string where leading occurrences of any char\n  # in *chars* are removed. The *chars* argument is not a suffix;\n  # rather; all combinations of its values are stripped.\n  #\n  # ```\n  # \"bcadefcba\".lstrip(\"abc\") # => \"defcba\"\n  # ```\n  def lstrip(chars : String) : String\n    return self if empty?\n\n    case chars.size\n    when 0\n      self\n    when 1\n      lstrip(chars[0])\n    else\n      remove_excess_left(calc_excess_left(chars))\n    end\n  end\n\n  # Returns a new string where leading characters for which\n  # the block returns a *truthy* value are removed.\n  #\n  # ```\n  # \"bcadefcba\".lstrip { |c| 'a' <= c <= 'c' } # => \"defcba\"\n  # ```\n  def lstrip(&block : Char -> _) : String\n    return self if empty?\n\n    excess_left = calc_excess_left { |c| yield c }\n    remove_excess_left(excess_left)\n  end\n\n  # :nodoc:\n  def calc_excess_right\n    if single_byte_optimizable?\n      i = bytesize - 1\n      while i >= 0 && to_unsafe[i].unsafe_chr.ascii_whitespace?\n        i -= 1\n      end\n      bytesize - 1 - i\n    else\n      calc_excess_right &.whitespace?\n    end\n  end\n\n  private def calc_excess_right(char : Char)\n    calc_excess_right do |reader_char|\n      char == reader_char\n    end\n  end\n\n  private def calc_excess_right(chars : String)\n    calc_excess_right do |reader_char|\n      chars.includes?(reader_char)\n    end\n  end\n\n  private def calc_excess_right(&block)\n    byte_index = bytesize\n    reader = Char::Reader.new(at_end: self)\n    while (yield reader.current_char)\n      byte_index = reader.pos\n      if byte_index == 0\n        return bytesize\n      else\n        reader.previous_char\n      end\n    end\n    bytesize - byte_index\n  end\n\n  # :nodoc:\n  def calc_excess_left\n    if single_byte_optimizable?\n      excess_left = 0\n      # All strings end with '\\0', and it's not a whitespace\n      # so it's safe to access past 1 byte beyond the string data\n      while to_unsafe[excess_left].unsafe_chr.ascii_whitespace?\n        excess_left += 1\n      end\n      excess_left\n    else\n      calc_excess_left &.whitespace?\n    end\n  end\n\n  private def calc_excess_left(char : Char)\n    calc_excess_left do |reader_char|\n      char == reader_char\n    end\n  end\n\n  private def calc_excess_left(chars : String)\n    calc_excess_left do |reader_char|\n      chars.includes?(reader_char)\n    end\n  end\n\n  private def calc_excess_left(&block)\n    reader = Char::Reader.new(self)\n    while (yield reader.current_char)\n      reader.next_char\n      return bytesize unless reader.has_next?\n    end\n    reader.pos\n  end\n\n  private def remove_excess(excess_left, excess_right)\n    if excess_right == 0 && excess_left == 0\n      self\n    else\n      unsafe_byte_slice_string(excess_left, bytesize - excess_right - excess_left)\n    end\n  end\n\n  private def remove_excess_right(excess_right)\n    case excess_right\n    when 0\n      self\n    when bytesize\n      \"\"\n    else\n      unsafe_byte_slice_string(0, bytesize - excess_right)\n    end\n  end\n\n  private def remove_excess_left(excess_left)\n    case excess_left\n    when 0\n      self\n    when bytesize\n      \"\"\n    else\n      unsafe_byte_slice_string(excess_left)\n    end\n  end\n\n  # Returns a new string _tr_anslating characters using *from* and *to* as a\n  # map. If *to* is shorter than *from*, the last character in *to* is used for\n  # the rest. If *to* is empty, this acts like `String#delete`.\n  #\n  # ```\n  # \"aabbcc\".tr(\"abc\", \"xyz\") # => \"xxyyzz\"\n  # \"aabbcc\".tr(\"abc\", \"x\")   # => \"xxxxxx\"\n  # \"aabbcc\".tr(\"a\", \"xyz\")   # => \"xxbbcc\"\n  # ```\n  def tr(from : String, to : String) : String\n    return delete(from) if to.empty?\n\n    if from.bytesize == 1\n      return gsub(from.to_unsafe[0].unsafe_chr, to[0])\n    end\n\n    multi = nil\n    table = StaticArray(Int32, 256).new(-1)\n    reader = Char::Reader.new(to)\n    char = reader.current_char\n    next_char = reader.next_char\n    from.each_char do |ch|\n      if ch.ord >= 256\n        multi ||= {} of Char => Char\n        multi[ch] = char\n      else\n        table[ch.ord] = char.ord\n      end\n      if next_char != Char::ZERO\n        char = next_char\n        reader.next_char\n        next_char = reader.current_char\n      end\n    end\n\n    String.build(bytesize) do |buffer|\n      each_char do |ch|\n        if ch.ord < 256\n          if (a = table[ch.ord]) >= 0\n            buffer << a.unsafe_chr\n          else\n            buffer << ch\n          end\n        else\n          if a = multi.try &.[ch]?\n            buffer << a\n          else\n            buffer << ch\n          end\n        end\n      end\n    end\n  end\n\n  # Returns a new `String` where the first character is yielded to the given\n  # block and replaced by its return value.\n  #\n  # ```\n  # \"hello\".sub { |char| char + 1 } # => \"iello\"\n  # \"hello\".sub { \"hi\" }            # => \"hiello\"\n  # ```\n  def sub(&block : Char -> _) : String\n    return self if empty?\n\n    String.build(bytesize) do |buffer|\n      reader = Char::Reader.new(self)\n      buffer << yield reader.current_char\n      reader.next_char\n      buffer.write unsafe_byte_slice(reader.pos)\n    end\n  end\n\n  # Returns a `String` where the first occurrence of *char* is replaced by\n  # *replacement*.\n  #\n  # ```\n  # \"hello\".sub('l', \"lo\")      # => \"helolo\"\n  # \"hello world\".sub('o', 'a') # => \"hella world\"\n  # ```\n  def sub(char : Char, replacement) : String\n    if includes?(char)\n      String.build(bytesize) do |buffer|\n        reader = Char::Reader.new(self)\n        while reader.has_next?\n          if reader.current_char == char\n            buffer << replacement\n            break\n          else\n            buffer << reader.current_char\n          end\n          reader.next_char\n        end\n        reader.next_char\n        buffer.write unsafe_byte_slice(reader.pos)\n      end\n    else\n      self\n    end\n  end\n\n  # Returns a `String` where the first occurrence of *pattern* is replaced by\n  # the block's return value.\n  #\n  # ```\n  # \"hello\".sub(/./) { |s| s[0].ord.to_s + ' ' } # => \"104 ello\"\n  # ```\n  def sub(pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None, &) : String\n    sub_append(pattern, options) do |str, match, buffer|\n      $~ = match\n      buffer << yield str, match\n    end\n  end\n\n  # Returns a `String` where the first occurrence of *pattern* is replaced by\n  # *replacement*\n  #\n  # ```\n  # \"hello\".sub(/[aeiou]/, \"*\") # => \"h*llo\"\n  # ```\n  #\n  # Within *replacement*, the special match variable `$~` will not refer to the\n  # current match.\n  #\n  # If *backreferences* is `true` (the default value), *replacement* can include backreferences:\n  #\n  # ```\n  # \"hello\".sub(/[aeiou]/, \"(\\\\0)\") # => \"h(e)llo\"\n  # ```\n  #\n  # When substitution is performed, any backreferences found in *replacement*\n  # will be replaced with the contents of the corresponding capture group in\n  # *pattern*. Backreferences to capture groups that were not present in\n  # *pattern* or that did not match will be skipped. See `Regex` for information\n  # about capture groups.\n  #\n  # Backreferences are expressed in the form `\"\\\\d\"`, where *d* is a group\n  # number, or `\"\\\\k&lt;name>\"` where *name* is the name of a named capture group.\n  # A sequence of literal characters resembling a backreference can be\n  # expressed by placing `\"\\\\\"` before the sequence.\n  #\n  # ```\n  # \"foo\".sub(/o/, \"x\\\\0x\")                  # => \"fxoxo\"\n  # \"foofoo\".sub(/(?<bar>oo)/, \"|\\\\k<bar>|\") # => \"f|oo|foo\"\n  # \"foo\".sub(/o/, \"\\\\\\\\0\")                  # => \"f\\\\0o\"\n  # ```\n  #\n  # Raises `ArgumentError` if an incomplete named back-reference is present in\n  # *replacement*.\n  #\n  # Raises `IndexError` if a named group referenced in *replacement* is not present\n  # in *pattern*.\n  def sub(pattern : Regex, replacement, backreferences = true, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    if backreferences && replacement.is_a?(String) && replacement.has_back_references?\n      sub_append(pattern, options) { |_, match, buffer| scan_backreferences(replacement, match, buffer) }\n    else\n      sub(pattern, options: options) { replacement }\n    end\n  end\n\n  # Returns a `String` where the first occurrences of the given *pattern* is replaced\n  # with the matching entry from the *hash* of replacements. If the first match\n  # is not included in the *hash*, nothing is replaced.\n  #\n  # ```\n  # \"hello\".sub(/(he|l|o)/, {\"he\": \"ha\", \"l\": \"la\"}) # => \"hallo\"\n  # \"hello\".sub(/(he|l|o)/, {\"l\": \"la\"})             # => \"hello\"\n  # ```\n  def sub(pattern : Regex, hash : Hash(String, _) | NamedTuple, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    # FIXME: The options parameter should be a named-only parameter, but that breaks overload ordering (fixed with -Dpreview_overload_ordering).\n\n    sub(pattern, options: options) do |match|\n      if hash.has_key?(match)\n        hash[match]\n      else\n        return self\n      end\n    end\n  end\n\n  # Returns a `String` where the first occurrences of the given *string* is replaced\n  # with the given *replacement*.\n  #\n  # ```\n  # \"hello yellow\".sub(\"ll\", \"dd\") # => \"heddo yellow\"\n  # ```\n  def sub(string : String, replacement) : String\n    sub(string) { replacement }\n  end\n\n  # Returns a `String` where the first occurrences of the given *string* is replaced\n  # with the block's value.\n  #\n  # ```\n  # \"hello yellow\".sub(\"ll\") { \"dd\" } # => \"heddo yellow\"\n  # ```\n  def sub(string : String, &block) : String\n    index = self.byte_index(string)\n    return self unless index\n\n    String.build(bytesize) do |buffer|\n      buffer.write unsafe_byte_slice(0, index)\n      buffer << yield string\n      buffer.write unsafe_byte_slice(index + string.bytesize)\n    end\n  end\n\n  # Returns a `String` where the first char in the string matching a key in the\n  # given *hash* is replaced by the corresponding hash value.\n  #\n  # ```\n  # \"hello\".sub({'a' => 'b', 'l' => 'd'}) # => \"hedlo\"\n  # ```\n  def sub(hash : Hash(Char, _)) : String\n    return self if empty?\n\n    String.build(bytesize) do |buffer|\n      reader = Char::Reader.new(self)\n      while reader.has_next?\n        if hash.has_key?(reader.current_char)\n          buffer << hash[reader.current_char]\n          reader.next_char\n          break\n        else\n          buffer << reader.current_char\n          reader.next_char\n        end\n      end\n\n      if reader.has_next?\n        buffer << reader.current_char\n        reader.next_char\n        buffer.write unsafe_byte_slice(reader.pos)\n      end\n    end\n  end\n\n  private def sub_append(pattern : Regex, options : Regex::MatchOptions, &)\n    match = pattern.match(self, options: options)\n    return self unless match\n\n    String.build(bytesize) do |buffer|\n      buffer.write unsafe_byte_slice(0, match.byte_begin)\n      str = match[0]\n      $~ = match\n      yield str, match, buffer\n      buffer.write unsafe_byte_slice(match.byte_begin + str.bytesize)\n    end\n  end\n\n  # Returns a new `String` with the character at the given index\n  # replaced by *replacement*.\n  #\n  # ```\n  # \"hello\".sub(1, 'a') # => \"hallo\"\n  # ```\n  def sub(index : Int, replacement : Char) : String\n    sub_index(index.to_i, replacement) do |buffer|\n      replacement.each_byte do |byte|\n        buffer.value = byte\n        buffer += 1\n      end\n      {buffer, @length}\n    end\n  end\n\n  # Returns a new `String` with the character at the given index\n  # replaced by *replacement*.\n  #\n  # ```\n  # \"hello\".sub(1, \"eee\") # => \"heeello\"\n  # ```\n  def sub(index : Int, replacement : String) : String\n    sub_index(index.to_i, replacement) do |buffer|\n      buffer.copy_from(replacement.to_unsafe, replacement.bytesize)\n      buffer += replacement.bytesize\n      {buffer, self.size_known? && replacement.size_known? ? self.size + replacement.size - 1 : 0}\n    end\n  end\n\n  private def sub_index(index, replacement, &)\n    index += size if index < 0\n\n    byte_index = char_index_to_byte_index(index)\n    raise IndexError.new unless byte_index && byte_index < bytesize\n\n    width = char_bytesize_at(byte_index)\n    replacement_width = replacement.bytesize\n    new_bytesize = bytesize - width + replacement_width\n\n    String.new(new_bytesize) do |buffer|\n      buffer.copy_from(to_unsafe, byte_index)\n      buffer += byte_index\n      buffer, length = yield buffer\n      buffer.copy_from(to_unsafe + byte_index + width, bytesize - byte_index - width)\n      {new_bytesize, length}\n    end\n  end\n\n  # Returns a new `String` with characters at the given range\n  # replaced by *replacement*.\n  #\n  # ```\n  # \"hello\".sub(1..2, 'a') # => \"halo\"\n  # ```\n  def sub(range : Range, replacement : Char) : String\n    sub_range(range, replacement) do |buffer, from_index, to_index|\n      replacement.each_byte do |byte|\n        buffer.value = byte\n        buffer += 1\n      end\n      {buffer, single_byte_optimizable? ? bytesize - (to_index - from_index) + 1 : 0}\n    end\n  end\n\n  # Returns a new `String` with characters at the given range\n  # replaced by *replacement*.\n  #\n  # ```\n  # \"hello\".sub(1..2, \"eee\") # => \"heeelo\"\n  # ```\n  def sub(range : Range, replacement : String) : String\n    sub_range(range, replacement) do |buffer|\n      buffer.copy_from(replacement.to_unsafe, replacement.bytesize)\n      buffer += replacement.bytesize\n      {buffer, 0}\n    end\n  end\n\n  private def sub_range(range, replacement, &)\n    start, count = Indexable.range_to_index_and_count(range, size) || raise IndexError.new\n\n    from_index = char_index_to_byte_index(start)\n    raise IndexError.new unless from_index\n\n    if count == 0\n      to_index = from_index\n    else\n      to_index = char_index_to_byte_index(start + count)\n      raise IndexError.new unless to_index\n    end\n\n    new_bytesize = bytesize - (to_index - from_index) + replacement.bytesize\n\n    String.new(new_bytesize) do |buffer|\n      buffer.copy_from(to_unsafe, from_index)\n      buffer += from_index\n      buffer, length = yield buffer, from_index, to_index\n      buffer.copy_from(to_unsafe + to_index, bytesize - to_index)\n      {new_bytesize, length}\n    end\n  end\n\n  # This returns `true` if this string has `'\\\\'` in it. It might not be a back reference,\n  # but `'\\\\'` is probably used for back references, so this check is faster than parsing\n  # the whole thing.\n  def has_back_references?\n    to_slice.index('\\\\'.ord.to_u8)\n  end\n\n  private def scan_backreferences(replacement, match_data, buffer)\n    # We only append to the buffer in chunks, so if we have \"foo\\\\1\", we remember that\n    # the chunk starts at index 0 (first_index) and when we find a \"\\\\\" we append\n    # from 0 to 3 in a single write. When we find a \"\\\\0\" or \"\\\\k<...>\", we append\n    # from the first_index, process the backreference, and then reset first_index\n    # to the new index.\n    first_index = 0\n    index = 0\n\n    while index = replacement.byte_index('\\\\'.ord.to_u8, index)\n      index += 1\n      chr = replacement.to_unsafe[index].unsafe_chr\n      case chr\n      when '\\\\'\n        buffer.write(replacement.unsafe_byte_slice(first_index, index - first_index))\n        index += 1\n        first_index = index\n      when '0'..'9'\n        buffer.write(replacement.unsafe_byte_slice(first_index, index - 1 - first_index))\n        buffer << match_data[chr - '0']?\n        index += 1\n        first_index = index\n      when 'k'\n        index += 1\n        chr = replacement.to_unsafe[index].unsafe_chr\n        next unless chr == '<'\n\n        buffer.write(replacement.unsafe_byte_slice(first_index, index - 2 - first_index))\n\n        index += 1\n        start_index = index\n        end_index = replacement.byte_index('>'.ord.to_u8, start_index)\n        raise ArgumentError.new(\"Missing ending '>' for '\\\\\\\\k<...'\") unless end_index\n\n        name = replacement.byte_slice(start_index, end_index - start_index)\n        capture = match_data[name]?\n        raise IndexError.new(\"Undefined group name reference: #{name.inspect}\") unless capture\n\n        buffer << capture\n        index = end_index + 1\n        first_index = index\n      end\n    end\n\n    if first_index != replacement.bytesize\n      buffer.write(replacement.unsafe_byte_slice(first_index))\n    end\n  end\n\n  # Returns a `String` where each character yielded to the given block\n  # is replaced by the block's return value.\n  #\n  # ```\n  # \"hello\".gsub { |char| char + 1 } # => \"ifmmp\"\n  # \"hello\".gsub { \"hi\" }            # => \"hihihihihi\"\n  # ```\n  def gsub(&block : Char -> _) : String\n    String.build(bytesize) do |buffer|\n      each_char do |my_char|\n        buffer << yield my_char\n      end\n    end\n  end\n\n  # Returns a `String` where all occurrences of the given char are\n  # replaced with the given *replacement*.\n  #\n  # ```\n  # \"hello\".gsub('l', \"lo\")      # => \"heloloo\"\n  # \"hello world\".gsub('o', 'a') # => \"hella warld\"\n  # ```\n  def gsub(char : Char, replacement) : String\n    if replacement.is_a?(String) && replacement.bytesize == 1\n      return gsub(char, replacement.to_unsafe[0].unsafe_chr)\n    end\n\n    if includes?(char)\n      if replacement.is_a?(Char) && char.ascii? && replacement.ascii?\n        return gsub_ascii_char(char, replacement)\n      end\n\n      gsub { |my_char| char == my_char ? replacement : my_char }\n    else\n      self\n    end\n  end\n\n  private def gsub_ascii_char(char, replacement)\n    String.new(bytesize) do |buffer|\n      to_slice.each_with_index do |byte, i|\n        if char.ord == byte\n          buffer[i] = replacement.ord.to_u8\n        else\n          buffer[i] = byte\n        end\n      end\n      {bytesize, @length}\n    end\n  end\n\n  # Returns a `String` where all occurrences of the given *pattern* are replaced\n  # by the block value's value.\n  #\n  # ```\n  # \"hello\".gsub(/./) { |s| s[0].ord.to_s + ' ' }                                              # => \"104 101 108 108 111 \"\n  # \"foo bar baz\".gsub(/ba./) { |match| match.upcase }                                         # => \"foo BAR BAZ\"\n  # \"Name: Alice, Name: Bob\".gsub(/Name: (\\w+)/) { |full, matches| \"User(#{matches[1]})\" }     # => \"User(Alice), User(Bob)\"\n  # \"5x10, 3x7\".gsub(/(\\d+)x(\\d+)/) { |full, matches| \"#{matches[1].to_i * matches[2].to_i}\" } # => \"50, 21\"\n  # ```\n  def gsub(pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None, &) : String\n    gsub_append(pattern, options) do |string, match, buffer|\n      $~ = match\n      buffer << yield string, match\n    end\n  end\n\n  # Returns a `String` where all occurrences of the given *pattern* are replaced\n  # with the given *replacement*.\n  #\n  # ```\n  # \"hello\".gsub(/[aeiou]/, '*') # => \"h*ll*\"\n  # ```\n  #\n  # Within *replacement*, the special match variable `$~` will not refer to the\n  # current match.\n  #\n  # If *backreferences* is `true` (the default value), *replacement* can include backreferences:\n  #\n  # ```\n  # \"hello\".gsub(/[aeiou]/, \"(\\\\0)\") # => \"h(e)ll(o)\"\n  # ```\n  #\n  # When substitution is performed, any backreferences found in *replacement*\n  # will be replaced with the contents of the corresponding capture group in\n  # *pattern*. Backreferences to capture groups that were not present in\n  # *pattern* or that did not match will be skipped. See `Regex` for information\n  # about capture groups.\n  #\n  # Backreferences are expressed in the form `\"\\\\d\"`, where *d* is a group\n  # number, or `\"\\\\k<name>\"` where *name* is the name of a named capture group.\n  # A sequence of literal characters resembling a backreference can be\n  # expressed by placing `\"\\\\\"` before the sequence.\n  #\n  # ```\n  # \"foo\".gsub(/o/, \"x\\\\0x\")                  # => \"fxoxxox\"\n  # \"foofoo\".gsub(/(?<bar>oo)/, \"|\\\\k<bar>|\") # => \"f|oo|f|oo|\"\n  # \"foo\".gsub(/o/, \"\\\\\\\\0\")                  # => \"f\\\\0\\\\0\"\n  # ```\n  #\n  # Raises `ArgumentError` if an incomplete named back-reference is present in\n  # *replacement*.\n  #\n  # Raises `IndexError` if a named group referenced in *replacement* is not present\n  # in *pattern*.\n  def gsub(pattern : Regex, replacement, backreferences = true, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    if backreferences && replacement.is_a?(String) && replacement.has_back_references?\n      gsub_append(pattern, options) { |_, match, buffer| scan_backreferences(replacement, match, buffer) }\n    else\n      gsub(pattern, options: options) { replacement }\n    end\n  end\n\n  # Returns a `String` where all occurrences of the given *pattern* are replaced\n  # with a *hash* of replacements. If the *hash* contains the matched pattern,\n  # the corresponding value is used as a replacement. Otherwise the match is\n  # not included in the returned string.\n  #\n  # ```\n  # # \"he\" and \"l\" are matched and replaced,\n  # # but \"o\" is not and so is not included\n  # \"hello\".gsub(/(he|l|o)/, {\"he\": \"ha\", \"l\": \"la\"}) # => \"halala\"\n  # ```\n  def gsub(pattern : Regex, hash : Hash(String, _) | NamedTuple, options : Regex::MatchOptions = Regex::MatchOptions::None) : String\n    # FIXME: The options parameter should be a named-only parameter, but that breaks overload ordering (fixed with -Dpreview_overload_ordering).\n\n    gsub(pattern, options: options) do |match|\n      hash[match]?\n    end\n  end\n\n  # Returns a `String` where all occurrences of the given *string* are replaced\n  # with the given *replacement*.\n  #\n  # ```\n  # \"hello yellow\".gsub(\"ll\", \"dd\") # => \"heddo yeddow\"\n  # ```\n  def gsub(string : String, replacement) : String\n    if string.bytesize == 1\n      gsub(string.to_unsafe[0].unsafe_chr, replacement)\n    else\n      gsub(string) { replacement }\n    end\n  end\n\n  # Returns a `String` where all occurrences of the given *string* are replaced\n  # with the block's value.\n  #\n  # ```\n  # \"hello yellow\".gsub(\"ll\") { \"dd\" } # => \"heddo yeddow\"\n  # ```\n  def gsub(string : String, &block) : String\n    byte_offset = 0\n    index = self.byte_index(string, byte_offset)\n    return self unless index\n\n    last_byte_offset = 0\n\n    String.build(bytesize) do |buffer|\n      while index\n        buffer.write unsafe_byte_slice(last_byte_offset, index - last_byte_offset)\n        buffer << yield string\n\n        if string.bytesize == 0\n          # The pattern matched an empty result. We must advance one character to avoid stagnation.\n          byte_offset = index + char_bytesize_at(byte_offset)\n          last_byte_offset = index\n        else\n          byte_offset = index + string.bytesize\n          last_byte_offset = byte_offset\n        end\n\n        index = self.byte_index(string, byte_offset)\n      end\n\n      if last_byte_offset < bytesize\n        buffer.write unsafe_byte_slice(last_byte_offset)\n      end\n    end\n  end\n\n  # Returns a `String` where all chars in the given hash are replaced\n  # by the corresponding *hash* values.\n  #\n  # ```\n  # \"hello\".gsub({'e' => 'a', 'l' => 'd'}) # => \"haddo\"\n  # ```\n  def gsub(hash : Hash(Char, _)) : String\n    gsub do |char|\n      hash[char]? || char\n    end\n  end\n\n  # Returns a `String` where all chars in the given named tuple are replaced\n  # by the corresponding *tuple* values.\n  #\n  # ```\n  # \"hello\".gsub({e: 'a', l: 'd'}) # => \"haddo\"\n  # ```\n  def gsub(tuple : NamedTuple) : String\n    gsub do |char|\n      tuple[char.to_s]? || char\n    end\n  end\n\n  private def gsub_append(pattern : Regex, options : Regex::MatchOptions, &)\n    byte_offset = 0\n    match = pattern.match_at_byte_index(self, byte_offset, options: options)\n    return self unless match\n\n    last_byte_offset = 0\n\n    String.build(bytesize) do |buffer|\n      while match\n        index = match.byte_begin(0)\n\n        buffer.write unsafe_byte_slice(last_byte_offset, index - last_byte_offset)\n        str = match[0]\n        $~ = match\n        yield str, match, buffer\n\n        if str.bytesize == 0\n          # The pattern matched an empty result. We must advance one character to avoid stagnation.\n          byte_offset = index + char_bytesize_at(byte_offset)\n          last_byte_offset = index\n        else\n          byte_offset = index + str.bytesize\n          last_byte_offset = byte_offset\n        end\n\n        match = pattern.match_at_byte_index(self, byte_offset, options: options | Regex::MatchOptions::NO_UTF_CHECK)\n      end\n\n      if last_byte_offset < bytesize\n        buffer.write unsafe_byte_slice(last_byte_offset)\n      end\n    end\n  end\n\n  # Yields each char in this string to the block,\n  # returns the number of times the block returned a truthy value.\n  #\n  # ```\n  # \"aabbcc\".count &.in?('a', 'b') # => 4\n  # ```\n  def count(&) : Int32\n    count = 0\n    each_char do |char|\n      count += 1 if yield char\n    end\n    count\n  end\n\n  # Counts the occurrences of *other* char in this string.\n  #\n  # ```\n  # \"aabbcc\".count('a') # => 2\n  # ```\n  def count(other : Char) : Int32\n    count { |char| char == other }\n  end\n\n  # Sets should be a list of strings following the rules\n  # described at `Char#in_set?`. Returns the number of characters\n  # in this string that match the given set.\n  def count(*sets) : Int32\n    count(&.in_set?(*sets))\n  end\n\n  # Yields each char in this string to the block.\n  # Returns a new `String` with all characters for which the\n  # block returned a truthy value removed.\n  #\n  # ```\n  # \"aabbcc\".delete &.in?('a', 'b') # => \"cc\"\n  # ```\n  def delete(&) : String\n    String.build(bytesize) do |buffer|\n      each_char do |char|\n        buffer << char unless yield char\n      end\n    end\n  end\n\n  # Returns a new `String` with all occurrences of *char* removed.\n  #\n  # ```\n  # \"aabbcc\".delete('b') # => \"aacc\"\n  # ```\n  def delete(char : Char) : String\n    delete { |my_char| my_char == char }\n  end\n\n  # Sets should be a list of strings following the rules\n  # described at `Char#in_set?`. Returns a new `String` with\n  # all characters that match the given set removed.\n  #\n  # ```\n  # \"aabbccdd\".delete(\"a-c\") # => \"dd\"\n  # ```\n  def delete(*sets) : String\n    delete(&.in_set?(*sets))\n  end\n\n  # Yields each char in this string to the block.\n  # Returns a new `String`, that has all characters removed,\n  # that were the same as the previous one and for which the given\n  # block returned a truthy value.\n  #\n  # ```\n  # \"aaabbbccc\".squeeze &.in?('a', 'b') # => \"abccc\"\n  # \"aaabbbccc\".squeeze &.in?('a', 'c') # => \"abbbc\"\n  # ```\n  def squeeze(&) : String\n    previous = nil\n    String.build(bytesize) do |buffer|\n      each_char do |char|\n        buffer << char unless yield(char) && previous == char\n        previous = char\n      end\n    end\n  end\n\n  # Returns a new `String`, with all runs of char replaced by one instance.\n  #\n  # ```\n  # \"a    bbb\".squeeze(' ') # => \"a bbb\"\n  # ```\n  def squeeze(char : Char) : String\n    squeeze { |my_char| char == my_char }\n  end\n\n  # Sets should be a list of strings following the rules\n  # described at `Char#in_set?`. Returns a new `String` with all\n  # runs of the same character replaced by one instance, if\n  # they match the given set.\n  #\n  # If no set is given, all characters are matched.\n  #\n  # ```\n  # \"aaabbbcccddd\".squeeze(\"b-d\") # => \"aaabcd\"\n  # \"a       bbb\".squeeze         # => \"a b\"\n  # ```\n  def squeeze(*sets : String) : String\n    squeeze(&.in_set?(*sets))\n  end\n\n  # Returns a new `String`, that has all characters removed,\n  # that were the same as the previous one.\n  #\n  # ```\n  # \"a       bbb\".squeeze # => \"a b\"\n  # ```\n  def squeeze : String\n    squeeze { true }\n  end\n\n  # Returns `true` if this is the empty string, `\"\"`.\n  def empty? : Bool\n    bytesize == 0\n  end\n\n  # Returns `true` if this string consists exclusively of unicode whitespace.\n  #\n  # ```\n  # \"\".blank?        # => true\n  # \"   \".blank?     # => true\n  # \"   a   \".blank? # => false\n  # ```\n  def blank? : Bool\n    each_char do |char|\n      return false unless char.whitespace?\n    end\n    true\n  end\n\n  # Returns `self` unless `#blank?` is `true` in which case it returns `nil`.\n  #\n  # ```\n  # \"a\".presence         # => \"a\"\n  # \"\".presence          # => nil\n  # \"   \".presence       # => nil\n  # \"    a    \".presence # => \"    a    \"\n  # nil.presence         # => nil\n  #\n  # config = {\"empty\" => \"\"}\n  # config[\"empty\"]?.presence || \"default\"   # => \"default\"\n  # config[\"missing\"]?.presence || \"default\" # => \"default\"\n  # ```\n  #\n  # See also: `Nil#presence`.\n  def presence : self?\n    self unless blank?\n  end\n\n  # Returns `true` if this string is equal to `*other*.\n  #\n  # Equality is checked byte-per-byte: if any byte is different from the corresponding\n  # byte, it returns `false`. This means two strings containing invalid\n  # UTF-8 byte sequences may compare unequal, even when they both produce the\n  # Unicode replacement character at the same string indices.\n  #\n  # Thus equality is case-sensitive, as it is with the comparison operator (`#<=>`).\n  # `#compare` offers a case-insensitive alternative.\n  #\n  # ```\n  # \"abcdef\" == \"abcde\"   # => false\n  # \"abcdef\" == \"abcdef\"  # => true\n  # \"abcdef\" == \"abcdefg\" # => false\n  # \"abcdef\" == \"ABCDEF\"  # => false\n  #\n  # \"abcdef\".compare(\"ABCDEF\", case_insensitive: true) == 0 # => true\n  # ```\n  def ==(other : self) : Bool\n    # Quick pointer comparison if both strings are identical references\n    return true if same?(other)\n\n    # If the bytesize differs, they cannot be equal\n    return false if bytesize != other.bytesize\n\n    # If the character size of both strings differs, they cannot be equal.\n    # We need to exclude the case that @length of either string might not have\n    # been calculated (indicated by `0`).\n    return false if @length != other.@length && @length != 0 && other.@length != 0\n\n    # All meta data matches up, so we need to compare byte-by-byte.\n    to_unsafe.memcmp(other.to_unsafe, bytesize) == 0\n  end\n\n  # The comparison operator.\n  #\n  # Compares this string with *other*, returning `-1`, `0` or `1` depending on whether\n  # this string is less, equal or greater than *other*.\n  #\n  # Comparison is done byte-per-byte: if a byte is less than the other corresponding\n  # byte, `-1` is returned and so on. This means two strings containing invalid\n  # UTF-8 byte sequences may compare unequal, even when they both produce the\n  # Unicode replacement character at the same string indices.\n  #\n  # If the strings are of different lengths, and the strings are equal when compared\n  # up to the shortest length, then the longer string is considered greater than\n  # the shorter one.\n  #\n  # ```\n  # \"abcdef\" <=> \"abcde\"   # => 1\n  # \"abcdef\" <=> \"abcdef\"  # => 0\n  # \"abcdef\" <=> \"abcdefg\" # => -1\n  # \"abcdef\" <=> \"ABCDEF\"  # => 1\n  # ```\n  #\n  # The comparison is case-sensitive. `#compare` is a case-insensitive alternative.\n  def <=>(other : self) : Int32\n    return 0 if same?(other)\n    min_bytesize = Math.min(bytesize, other.bytesize)\n\n    cmp = to_unsafe.memcmp(other.to_unsafe, min_bytesize)\n    cmp == 0 ? (bytesize <=> other.bytesize) : cmp.sign\n  end\n\n  # Compares this string with *other*, returning `-1`, `0` or `1` depending on whether\n  # this string is less, equal or greater than *other*, optionally in a *case_insensitive*\n  # manner.\n  #\n  # Case-sensitive comparisons (`case_insensitive == false`) are equivalent to\n  # `#<=>` and are always done byte-per-byte.\n  #\n  # ```\n  # \"abcdef\".compare(\"abcde\")   # => 1\n  # \"abcdef\".compare(\"abcdef\")  # => 0\n  # \"abcdef\".compare(\"abcdefg\") # => -1\n  # \"abcdef\".compare(\"ABCDEF\")  # => 1\n  #\n  # \"abcdef\".compare(\"ABCDEF\", case_insensitive: true) # => 0\n  # \"abcdef\".compare(\"ABCDEG\", case_insensitive: true) # => -1\n  #\n  # \"heIIo\".compare(\"heııo\", case_insensitive: true, options: Unicode::CaseOptions::Turkic) # => 0\n  # \"Baﬄe\".compare(\"baffle\", case_insensitive: true, options: Unicode::CaseOptions::Fold)   # => 0\n  # ```\n  #\n  # Case-sensitive only comparison is provided by the comparison operator `#<=>`.\n  def compare(other : String, case_insensitive = false, options : Unicode::CaseOptions = :none) : Int32\n    return self <=> other unless case_insensitive\n\n    if single_byte_optimizable? && other.single_byte_optimizable?\n      position = 0\n\n      while position < bytesize && position < other.bytesize\n        byte1 = to_unsafe[position]\n        byte2 = other.to_unsafe[position]\n\n        # Lowercase both bytes\n        # Also reject any invalid code units\n        if 65 <= byte1 <= 90\n          byte1 += 32\n        elsif byte1 >= 0x80\n          return 1 if byte2 < 0x80\n        end\n\n        if 65 <= byte2 <= 90\n          byte2 += 32\n        elsif byte2 >= 0x80\n          return byte1 < 0x80 ? -1 : 0\n        end\n\n        comparison = byte1 <=> byte2\n        return comparison unless comparison == 0\n\n        position += 1\n      end\n\n      bytesize <=> other.bytesize\n    else\n      reader1 = Char::Reader.new(self)\n      reader2 = Char::Reader.new(other)\n\n      # 3 chars maximum for case folding; 2 held in temporary buffers\n      chars1 = Crystal::SmallDeque(Char, 2).new\n      chars2 = Crystal::SmallDeque(Char, 2).new\n\n      while true\n        lhs = chars1.shift do\n          next unless reader1.has_next?\n          lhs_ = nil\n          reader1.current_char.downcase(options) do |char|\n            if lhs_\n              chars1 << char\n            else\n              lhs_ = char\n            end\n          end\n          reader1.next_char\n          lhs_\n        end\n\n        rhs = chars2.shift do\n          next unless reader2.has_next?\n          rhs_ = nil\n          reader2.current_char.downcase(options) do |char|\n            if rhs_\n              chars2 << char\n            else\n              rhs_ = char\n            end\n          end\n          reader2.next_char\n          rhs_\n        end\n\n        case {lhs, rhs}\n        in {Nil, Nil}\n          return 0\n        in {Nil, Char}\n          return -1\n        in {Char, Nil}\n          return 1\n        in {Char, Char}\n          comparison = lhs <=> rhs\n          return comparison.sign unless comparison == 0\n        end\n      end\n    end\n  end\n\n  # Tests whether *str* matches *regex*.\n  # If successful, it returns the position of the first match.\n  # If unsuccessful, it returns `nil`.\n  #\n  # If the argument isn't a `Regex`, it returns `nil`.\n  #\n  # ```\n  # \"Haystack\" =~ /ay/ # => 1\n  # \"Haystack\" =~ /z/  # => nil\n  #\n  # \"Haystack\" =~ 45 # => nil\n  # ```\n  def =~(regex : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Int32?\n    match = regex.match(self, options: options)\n    $~ = match\n    match.try &.begin(0)\n  end\n\n  # :ditto:\n  def =~(other, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Nil\n    nil\n  end\n\n  # Concatenates *str* and *other*.\n  #\n  # ```\n  # \"abc\" + \"def\" # => \"abcdef\"\n  # \"abc\" + 'd'   # => \"abcd\"\n  # ```\n  def +(other : self) : String\n    return self if other.empty?\n    return other if self.empty?\n\n    size = bytesize + other.bytesize\n    String.new(size) do |buffer|\n      buffer.copy_from(to_unsafe, bytesize)\n      (buffer + bytesize).copy_from(other.to_unsafe, other.bytesize)\n\n      if size_known? && other.size_known?\n        {size, @length + other.@length}\n      else\n        {size, 0}\n      end\n    end\n  end\n\n  # :ditto:\n  def +(char : Char) : String\n    bytes, count = String.char_bytes_and_bytesize(char)\n    size = bytesize + count\n    String.new(size) do |buffer|\n      buffer.copy_from(to_unsafe, bytesize)\n      (buffer + bytesize).copy_from(bytes.to_unsafe, count)\n\n      if size_known?\n        {size, @length + 1}\n      else\n        {size, 0}\n      end\n    end\n  end\n\n  # Makes a new `String` by adding *str* to itself *times* times.\n  #\n  # ```\n  # \"Developers! \" * 4\n  # # => \"Developers! Developers! Developers! Developers! \"\n  # ```\n  def *(times : Int) : String\n    raise ArgumentError.new \"Negative argument\" if times < 0\n\n    if times == 0 || bytesize == 0\n      return \"\"\n    elsif bytesize == 1\n      return String.new(times) do |buffer|\n        Slice.new(buffer, times).fill(to_unsafe[0])\n        {times, times}\n      end\n    end\n\n    total_bytesize = bytesize * times\n    String.new(total_bytesize) do |buffer|\n      buffer.copy_from(to_unsafe, bytesize)\n      n = bytesize\n\n      while n <= total_bytesize // 2\n        (buffer + n).copy_from(buffer, n)\n        n *= 2\n      end\n\n      (buffer + n).copy_from(buffer, total_bytesize - n)\n      {total_bytesize, @length * times}\n    end\n  end\n\n  # Prime number constant for Rabin-Karp algorithm `String#index`.\n  private PRIME_RK = 2097169u32\n\n  # Update rolling hash for Rabin-Karp algorithm `String#index`.\n  private macro update_hash(n)\n    {% for i in 1..n %}\n      {% if i != 1 %}\n        byte = head_pointer.value\n      {% end %}\n      hash = hash &* PRIME_RK &+ pointer.value &- pow &* byte\n      pointer += 1\n      head_pointer += 1\n    {% end %}\n  end\n\n  # Returns the index of the _first_ occurrence of *search* in the string, or `nil` if not present.\n  # If *offset* is present, it defines the position to start the search.\n  #\n  # ```\n  # \"Hello, World\".index('o')    # => 4\n  # \"Hello, World\".index('Z')    # => nil\n  # \"Hello, World\".index(\"o\", 5) # => 8\n  # \"Hello, World\".index(\"H\", 2) # => nil\n  # \"Hello, World\".index(/[ ]+/) # => 6\n  # \"Hello, World\".index(/\\d+/)  # => nil\n  # ```\n  def index(search : Char, offset = 0) : Int32?\n    # If it's ASCII we can delegate to slice\n    if single_byte_optimizable?\n      # With `single_byte_optimizable?` there are only ASCII characters and\n      # invalid UTF-8 byte sequences, and we can reject anything that is neither\n      # ASCII nor the replacement character.\n      case search\n      when .ascii?\n        return to_slice.fast_index(search.ord.to_u8!, offset)\n      when Char::REPLACEMENT\n        offset.upto(bytesize - 1) do |i|\n          if to_unsafe[i] >= 0x80\n            return i.to_i\n          end\n        end\n      end\n\n      return nil\n    end\n\n    offset += size if offset < 0\n    return nil if offset < 0\n\n    each_char_with_index do |char, i|\n      if i >= offset && char == search\n        return i\n      end\n    end\n\n    nil\n  end\n\n  # :ditto:\n  def index(search : String, offset = 0)\n    offset += size if offset < 0\n    return if offset < 0\n\n    return size < offset ? nil : offset if search.empty?\n\n    # Rabin-Karp algorithm\n    # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm\n\n    # calculate a rolling hash of search text (needle)\n    search_hash = 0u32\n    search.each_byte do |b|\n      search_hash = search_hash &* PRIME_RK &+ b\n    end\n    pow = PRIME_RK &** search.bytesize\n\n    # Find start index with offset\n    char_index = 0\n    pointer = to_unsafe\n    end_pointer = pointer + bytesize\n    while char_index < offset && pointer < end_pointer\n      char_bytesize = String.char_bytesize_at(pointer)\n      pointer += char_bytesize\n      char_index += 1\n    end\n\n    head_pointer = pointer\n\n    # calculate a rolling hash of this text (haystack)\n    hash = 0u32\n    hash_end_pointer = pointer + search.bytesize\n    return if hash_end_pointer > end_pointer\n    while pointer < hash_end_pointer\n      hash = hash &* PRIME_RK &+ pointer.value\n      pointer += 1\n    end\n\n    while true\n      # check hash equality and real string equality\n      if hash == search_hash && head_pointer.memcmp(search.to_unsafe, search.bytesize) == 0\n        return char_index\n      end\n\n      byte = head_pointer.value\n      char_bytesize = String.char_bytesize_at(head_pointer)\n      return if pointer + char_bytesize > end_pointer\n      case char_bytesize\n      when 1 then update_hash 1\n      when 2 then update_hash 2\n      when 3 then update_hash 3\n      else        update_hash 4\n      end\n\n      char_index += 1\n    end\n  end\n\n  # :ditto:\n  def index(search : Regex, offset = 0, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Int32?\n    offset += size if offset < 0\n    return nil unless 0 <= offset <= size\n\n    self.match(search, offset, options: options).try &.begin\n  end\n\n  # Returns the index of the _first_ occurrence of *search* in the string. If *offset* is present,\n  # it defines the position to start the search.\n  #\n  # Raises `Enumerable::NotFoundError` if *search* does not occur in `self`.\n  #\n  # ```\n  # \"Hello, World\".index!('o')    # => 4\n  # \"Hello, World\".index!('Z')    # raises Enumerable::NotFoundError\n  # \"Hello, World\".index!(\"o\", 5) # => 8\n  # \"Hello, World\".index!(\"H\", 2) # raises Enumerable::NotFoundError\n  # \"Hello, World\".index!(/[ ]+/) # => 6\n  # \"Hello, World\".index!(/\\d+/)  # raises Enumerable::NotFoundError\n  # ```\n  def index!(search, offset = 0) : Int32\n    index(search, offset) || raise Enumerable::NotFoundError.new\n  end\n\n  # :ditto:\n  def index!(search : Regex, offset = 0, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Int32\n    index(search, offset, options: options) || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the index of the _last_ appearance of *search* in the string,\n  # If *offset* is present, it defines the position to _end_ the search\n  # (characters beyond this point are ignored).\n  #\n  # ```\n  # \"Hello, World\".rindex('o')    # => 8\n  # \"Hello, World\".rindex('Z')    # => nil\n  # \"Hello, World\".rindex('o', 5) # => 4\n  # \"Hello, World\".rindex('W', 2) # => nil\n  # ```\n  def rindex(search : Char, offset = size - 1)\n    # If it's ASCII we can delegate to slice\n    if single_byte_optimizable?\n      # With `single_byte_optimizable?` there are only ASCII characters and\n      # invalid UTF-8 byte sequences, and we can reject anything that is neither\n      # ASCII nor the replacement character.\n      case search\n      when .ascii?\n        return to_slice.rindex(search.ord.to_u8!, offset)\n      when Char::REPLACEMENT\n        offset.downto(0) do |i|\n          if to_unsafe[i] >= 0x80\n            return i.to_i\n          end\n        end\n      end\n\n      return nil\n    end\n\n    offset += size if offset < 0\n    return nil if offset < 0\n\n    if offset == size - 1\n      reader = Char::Reader.new(at_end: self)\n    else\n      byte_index = char_index_to_byte_index(offset)\n      raise IndexError.new unless byte_index\n      reader = Char::Reader.new(self, pos: byte_index)\n    end\n\n    while true\n      if reader.current_char == search\n        return offset\n      elsif reader.has_previous?\n        reader.previous_char\n        offset -= 1\n      else\n        return nil\n      end\n    end\n  end\n\n  # Returns the index of the _last_ appearance of *search* in the string,\n  # If *offset* is present, it defines the position to _end_ the search\n  # (characters beyond this point are ignored).\n  #\n  # ```\n  # \"Hello, World\".rindex(\"orld\")    # => 8\n  # \"Hello, World\".rindex(\"snorlax\") # => nil\n  # \"Hello, World\".rindex(\"o\", 5)    # => 4\n  # \"Hello, World\".rindex(\"W\", 2)    # => nil\n  # ```\n  def rindex(search : String, offset = size - search.size) : Int32?\n    offset += size if offset < 0\n    return if offset < 0\n\n    # Rabin-Karp algorithm\n    # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm\n\n    # calculate a rolling hash of search text (needle)\n    search_hash = 0u32\n    search.to_slice.reverse_each do |b|\n      search_hash = search_hash &* PRIME_RK &+ b\n    end\n    pow = PRIME_RK &** search.bytesize\n\n    hash = 0u32\n    char_index = size\n\n    begin_pointer = to_unsafe\n    pointer = begin_pointer + bytesize\n    tail_pointer = pointer\n    hash_begin_pointer = pointer - search.bytesize\n\n    return if hash_begin_pointer < begin_pointer\n\n    # calculate a rolling hash of this text (haystack)\n    while hash_begin_pointer < pointer\n      pointer -= 1\n      byte = pointer.value\n      char_index -= 1 if (byte & 0xC0) != 0x80\n\n      hash = hash &* PRIME_RK &+ byte\n    end\n\n    while true\n      # check hash equality and real string equality\n      if hash == search_hash && char_index <= offset &&\n         pointer.memcmp(search.to_unsafe, search.bytesize) == 0\n        return char_index\n      end\n\n      return if begin_pointer == pointer\n\n      pointer -= 1\n      tail_pointer -= 1\n      byte = pointer.value\n      char_index -= 1 if (byte & 0xC0) != 0x80\n\n      # update a rolling hash of this text (haystack)\n      hash = hash &* PRIME_RK &+ byte &- pow &* tail_pointer.value\n    end\n  end\n\n  # Returns the index of the _last_ appearance of *search* in the string,\n  # If *offset* is present, it defines the position to _end_ the search\n  # (characters beyond this point are ignored).\n  #\n  # ```\n  # \"Hello, World\".rindex(/world/i) # => 7\n  # \"Hello, World\".rindex(/world/)  # => nil\n  # \"Hello, World\".rindex(/o/, 5)   # => 4\n  # \"Hello, World\".rindex(/W/, 2)   # => nil\n  # ```\n  def rindex(search : Regex, offset = size, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Int32?\n    offset += size if offset < 0\n    return nil unless 0 <= offset <= size\n\n    match_result = nil\n    scan(search, options: options) do |match_data|\n      break if (index = match_data.begin) && index > offset\n      match_result = match_data\n    end\n\n    match_result.try &.begin\n  end\n\n  # Returns the index of the _last_ appearance of *search* in the string,\n  # If *offset* is present, it defines the position to _end_ the search\n  # (characters beyond this point are ignored).\n  # Raises `Enumerable::NotFoundError` if *search* does not occur in `self`.\n  #\n  # ```\n  # \"Hello, World\".rindex!('o')    # => 8\n  # \"Hello, World\".rindex!('Z')    # raises Enumerable::NotFoundError\n  # \"Hello, World\".rindex!('o', 5) # => 4\n  # \"Hello, World\".rindex!('W', 2) # raises Enumerable::NotFoundError\n  # ```\n  def rindex!(search : Char, offset = size - 1) : Int32\n    rindex(search, offset) || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the index of the _last_ appearance of *search* in the string,\n  # If *offset* is present, it defines the position to _end_ the search\n  # (characters beyond this point are ignored).\n  # Raises `Enumerable::NotFoundError` if *search* does not occur in `self`.\n  #\n  # ```\n  # \"Hello, World\".rindex!(\"orld\")    # => 8\n  # \"Hello, World\".rindex!(\"snorlax\") # raises Enumerable::NotFoundError\n  # \"Hello, World\".rindex!(\"o\", 5)    # => 4\n  # \"Hello, World\".rindex!(\"W\", 2)    # raises Enumerable::NotFoundError\n  # ```\n  def rindex!(search : String, offset = size - search.size) : Int32\n    rindex(search, offset) || raise Enumerable::NotFoundError.new\n  end\n\n  # Returns the index of the _last_ appearance of *search* in the string,\n  # If *offset* is present, it defines the position to _end_ the search\n  # (characters beyond this point are ignored).\n  # Raises `Enumerable::NotFoundError` if *search* does not occur in `self`.\n  #\n  # ```\n  # \"Hello, World\".rindex!(/world/i) # => 7\n  # \"Hello, World\".rindex!(/world/)  # raises Enumerable::NotFoundError\n  # \"Hello, World\".rindex!(/o/, 5)   # => 4\n  # \"Hello, World\".rindex!(/W/, 2)   # raises Enumerable::NotFoundError\n  # ```\n  def rindex!(search : Regex, offset = size, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Int32\n    rindex(search, offset, options: options) || raise Enumerable::NotFoundError.new\n  end\n\n  # Searches separator or pattern (`Regex`) in the string, and returns\n  # a `Tuple` with the part before it, the match, and the part after it.\n  # If it is not found, returns str followed by two empty strings.\n  #\n  # ```\n  # \"hello\".partition(\"l\") # => {\"he\", \"l\", \"lo\"}\n  # \"hello\".partition(\"x\") # => {\"hello\", \"\", \"\"}\n  # ```\n  def partition(search : (Char | String)) : Tuple(String, String, String)\n    pre = mid = post = \"\"\n    search_size = search.is_a?(Char) ? 1 : search.size\n    case pos = self.index(search)\n    when .nil?\n      pre = self\n    when 0\n      mid = search.to_s\n      post = self[(pos + search_size)..-1]\n    else\n      pre = self[0..(pos - 1)]\n      mid = search.to_s\n      post = self[(pos + search_size)..-1]\n    end\n    {pre, mid, post}\n  end\n\n  # :ditto:\n  def partition(search : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Tuple(String, String, String)\n    pre = mid = post = \"\"\n    case m = self.match(search, options: options)\n    when .nil?\n      pre = self\n    else\n      pre = m.pre_match\n      mid = m[0]\n      post = m.post_match\n    end\n    {pre, mid, post}\n  end\n\n  # Searches separator or pattern (`Regex`) in the string from the end of the string,\n  # and returns a `Tuple` with the part before it, the match, and the part after it.\n  # If it is not found, returns two empty strings and str.\n  #\n  # ```\n  # \"hello\".rpartition(\"l\")  # => {\"hel\", \"l\", \"o\"}\n  # \"hello\".rpartition(\"x\")  # => {\"\", \"\", \"hello\"}\n  # \"hello\".rpartition(/.l/) # => {\"he\", \"ll\", \"o\"}\n  # ```\n  def rpartition(search : (Char | String)) : Tuple(String, String, String)\n    pos = self.rindex(search)\n    search_size = search.is_a?(Char) ? 1 : search.size\n\n    pre = mid = post = \"\"\n\n    case pos\n    when .nil?\n      post = self\n    when 0\n      mid = search.to_s\n      post = self[(pos + search_size)..-1]\n    else\n      pre = self[0..(pos - 1)]\n      mid = search.to_s\n      post = self[(pos + search_size)..-1]\n    end\n    {pre, mid, post}\n  end\n\n  # :ditto:\n  def rpartition(search : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Tuple(String, String, String)\n    match_result = nil\n    pos = self.size - 1\n\n    while pos >= 0\n      self[pos..-1].scan(search, options: options) do |m|\n        match_result = m\n      end\n      break unless match_result.nil?\n      pos -= 1\n    end\n\n    pre = mid = post = \"\"\n\n    case\n    when match_result.nil?\n      post = self\n    when pos == 0\n      mid = match_result[0]\n      post = self[match_result[0].size..-1]\n    else\n      pre = self[0..pos - 1]\n      mid = match_result.not_nil![0]\n      post = self[pos + match_result.not_nil![0].size..-1]\n    end\n    {pre, mid, post}\n  end\n\n  # Returns the index of the _first_ occurrence of *byte* in the string, or `nil` if not present.\n  # If *offset* is present, it defines the position to start the search.\n  #\n  # Negative *offset* can be used to start the search from the end of the string.\n  #\n  # ```\n  # \"Hello, World\".byte_index(0x6f)             # => 4\n  # \"Hello, World\".byte_index(0x5a)             # => nil\n  # \"Hello, World\".byte_index(0x6f, 5)          # => 8\n  # \"💣\".byte_index(0xA3)                        # => 3\n  # \"Dizzy Miss Lizzy\".byte_index('z'.ord)      # => 2\n  # \"Dizzy Miss Lizzy\".byte_index('z'.ord, 3)   # => 3\n  # \"Dizzy Miss Lizzy\".byte_index('z'.ord, -4)  # => 13\n  # \"Dizzy Miss Lizzy\".byte_index('z'.ord, -17) # => nil\n  # ```\n  def byte_index(byte : Int, offset : Int32 = 0) : Int32?\n    offset += bytesize if offset < 0\n    return if offset < 0\n\n    offset.upto(bytesize - 1) do |i|\n      if to_unsafe[i] == byte\n        return i\n      end\n    end\n    nil\n  end\n\n  # Returns the index of the _first_ occurrence of *char* in the string, or `nil` if not present.\n  # If *offset* is present, it defines the position to start the search.\n  #\n  # Negative *offset* can be used to start the search from the end of the string.\n  #\n  # ```\n  # \"Hello, World\".byte_index('o')          # => 4\n  # \"Hello, World\".byte_index('Z')          # => nil\n  # \"Hello, World\".byte_index('o', 5)       # => 8\n  # \"Hi, 💣\".byte_index('💣')                 # => 4\n  # \"Dizzy Miss Lizzy\".byte_index('z')      # => 2\n  # \"Dizzy Miss Lizzy\".byte_index('z', 3)   # => 3\n  # \"Dizzy Miss Lizzy\".byte_index('z', -4)  # => 13\n  # \"Dizzy Miss Lizzy\".byte_index('z', -17) # => nil\n  # ```\n  def byte_index(char : Char, offset = 0) : Int32?\n    return byte_index(char.ord, offset) if char.ascii?\n\n    offset += bytesize if offset < 0\n    return if offset < 0\n    return if offset + char.bytesize > bytesize\n\n    # Simplified \"Rabin-Karp\" algorithm\n    search_hash = 0u32\n    search_mask = 0u32\n    hash = 0u32\n    char.each_byte do |byte|\n      search_hash = (search_hash << 8) | byte\n      search_mask = (search_mask << 8) | 0xff\n      hash = (hash << 8) | to_unsafe[offset]\n      offset += 1\n    end\n\n    offset.upto(bytesize) do |i|\n      if (hash & search_mask) == search_hash\n        return i - char.bytesize\n      end\n      # rely on zero terminating byte\n      hash = (hash << 8) | to_unsafe[i]\n    end\n    nil\n  end\n\n  # Returns the byte index of *search* in the string, or `nil` if the string is not present.\n  # If *offset* is present, it defines the position to start the search.\n  #\n  # Negative *offset* can be used to start the search from the end of the string.\n  #\n  # ```\n  # \"¥hello\".byte_index(\"hello\")              # => 2\n  # \"hello\".byte_index(\"world\")               # => nil\n  # \"Dizzy Miss Lizzy\".byte_index(\"izzy\")     # => 1\n  # \"Dizzy Miss Lizzy\".byte_index(\"izzy\", 2)  # => 12\n  # \"Dizzy Miss Lizzy\".byte_index(\"izzy\", -4) # => 12\n  # \"Dizzy Miss Lizzy\".byte_index(\"izzy\", -3) # => nil\n  # ```\n  def byte_index(search : String, offset = 0) : Int32?\n    offset += bytesize if offset < 0\n    return if offset < 0\n\n    return bytesize < offset ? nil : offset if search.empty?\n\n    # Rabin-Karp algorithm\n    # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm\n\n    # calculate a rolling hash of search text (needle)\n    search_hash = 0u32\n    search.each_byte do |b|\n      search_hash = search_hash &* PRIME_RK &+ b\n    end\n    pow = PRIME_RK &** search.bytesize\n\n    # calculate a rolling hash of this text (haystack)\n    pointer = head_pointer = to_unsafe + offset\n    hash_end_pointer = pointer + search.bytesize\n    end_pointer = to_unsafe + bytesize\n    hash = 0u32\n    return if hash_end_pointer > end_pointer\n    while pointer < hash_end_pointer\n      hash = hash &* PRIME_RK &+ pointer.value\n      pointer += 1\n    end\n\n    while true\n      # check hash equality and real string equality\n      if hash == search_hash && head_pointer.memcmp(search.to_unsafe, search.bytesize) == 0\n        return offset\n      end\n\n      return if pointer >= end_pointer\n\n      # update a rolling hash of this text (haystack)\n      hash = hash &* PRIME_RK &+ pointer.value &- pow &* head_pointer.value\n      pointer += 1\n      head_pointer += 1\n      offset += 1\n    end\n\n    nil\n  end\n\n  # Returns the byte index of the regex *pattern* in the string, or `nil` if the pattern does not find a match.\n  # If *offset* is present, it defines the position to start the search.\n  #\n  # Negative *offset* can be used to start the search from the end of the string.\n  #\n  # ```\n  # \"hello world\".byte_index(/o/)             # => 4\n  # \"hello world\".byte_index(/o/, offset: 4)  # => 4\n  # \"hello world\".byte_index(/o/, offset: 5)  # => 7\n  # \"hello world\".byte_index(/o/, offset: -1) # => nil\n  # \"hello world\".byte_index(/y/)             # => nil\n  # ```\n  def byte_index(pattern : Regex, offset = 0, options : Regex::MatchOptions = Regex::MatchOptions::None) : Int32?\n    offset += bytesize if offset < 0\n    return if offset < 0\n\n    if match = pattern.match_at_byte_index(self, offset, options: options)\n      match.byte_begin\n    end\n  end\n\n  # Returns the byte index of a char index, or `nil` if out of bounds.\n  #\n  # It is valid to pass `#size` to *index*, and in this case the answer\n  # will be the bytesize of this string.\n  #\n  # ```\n  # \"hello\".char_index_to_byte_index(1) # => 1\n  # \"hello\".char_index_to_byte_index(5) # => 5\n  # \"こんにちは\".char_index_to_byte_index(1) # => 3\n  # \"こんにちは\".char_index_to_byte_index(5) # => 15\n  # ```\n  def char_index_to_byte_index(index)\n    if single_byte_optimizable?\n      return 0 <= index <= bytesize ? index : nil\n    end\n\n    size = each_byte_index_and_char_index do |byte_index, char_index|\n      return byte_index if index == char_index\n    end\n    return @bytesize if index == size\n    nil\n  end\n\n  # Returns the char index of a byte index, or `nil` if out of bounds.\n  #\n  # It is valid to pass `#bytesize` to *index*, and in this case the answer\n  # will be the size of this string.\n  def byte_index_to_char_index(index) : Int32?\n    if single_byte_optimizable?\n      return 0 <= index <= bytesize ? index : nil\n    end\n\n    size = each_byte_index_and_char_index do |byte_index, char_index|\n      return char_index if index == byte_index\n    end\n    return size if index == @bytesize\n    nil\n  end\n\n  # Returns `true` if the string contains *search*.\n  #\n  # ```\n  # \"Team\".includes?('i')            # => false\n  # \"Dysfunctional\".includes?(\"fun\") # => true\n  # ```\n  def includes?(search : Char | String) : Bool\n    !!index(search)\n  end\n\n  # Makes an array by splitting the string on any amount of ASCII whitespace\n  # characters (and removing that whitespace).\n  #\n  # If *limit* is present, up to *limit* new strings will be created, with the\n  # entire remainder added to the last string.\n  #\n  # ```\n  # old_pond = \"\n  #   Old pond\n  #   a frog leaps in\n  #   water's sound\n  # \"\n  # old_pond.split    # => [\"Old\", \"pond\", \"a\", \"frog\", \"leaps\", \"in\", \"water's\", \"sound\"]\n  # old_pond.split(3) # => [\"Old\", \"pond\", \"a frog leaps in\\n  water's sound\\n\"]\n  # ```\n  def split(limit : Int32? = nil) : Array(String)\n    ary = Array(String).new\n    split(limit) do |string|\n      ary << string\n    end\n    ary\n  end\n\n  # Splits the string after any amount of ASCII whitespace characters and yields\n  # each non-whitespace part to a block.\n  #\n  # If *limit* is present, up to *limit* new strings will be created, with the\n  # entire remainder added to the last string.\n  #\n  # ```\n  # ary = [] of String\n  # old_pond = \"\n  #   Old pond\n  #   a frog leaps in\n  #   water's sound\n  # \"\n  #\n  # old_pond.split { |s| ary << s }\n  # ary # => [\"Old\", \"pond\", \"a\", \"frog\", \"leaps\", \"in\", \"water's\", \"sound\"]\n  # ary.clear\n  #\n  # old_pond.split(3) { |s| ary << s }\n  # ary # => [\"Old\", \"pond\", \"a frog leaps in\\n  water's sound\\n\"]\n  # ```\n  def split(limit : Int32? = nil, &block : String -> _)\n    if limit && limit <= 1\n      yield self\n      return\n    end\n\n    if single_byte_optimizable?\n      split_single_byte(limit) do |piece|\n        yield piece\n      end\n      return\n    end\n\n    yielded = 0\n    start_pos = 0\n    piece_size = 0\n    looking_for_space = false\n\n    reader = Char::Reader.new(self)\n    reader.each do |char|\n      if char.whitespace?\n        if looking_for_space\n          piece_bytesize = reader.pos - start_pos\n          yield String.new(to_unsafe + start_pos, piece_bytesize, piece_size)\n          yielded += 1\n          looking_for_space = false\n        end\n      else\n        if looking_for_space\n          piece_size += 1\n        else\n          start_pos = reader.pos\n          piece_size = 1\n          looking_for_space = true\n\n          break if limit && yielded + 1 == limit\n        end\n      end\n    end\n\n    if looking_for_space\n      piece_bytesize = bytesize - start_pos\n      yield String.new(to_unsafe + start_pos, piece_bytesize, piece_size)\n    end\n  end\n\n  private def split_single_byte(limit, &)\n    yielded = 0\n    index = 0\n    i = 0\n    looking_for_space = false\n    limit_reached = false\n    while i < bytesize\n      if looking_for_space\n        while i < bytesize\n          c = to_unsafe[i]\n          i += 1\n          if c.unsafe_chr.ascii_whitespace?\n            piece_bytesize = i - 1 - index\n            yield String.new(to_unsafe + index, piece_bytesize, piece_bytesize)\n            yielded += 1\n            looking_for_space = false\n\n            if limit && yielded + 1 == limit\n              limit_reached = true\n            end\n\n            break\n          end\n        end\n      else\n        while i < bytesize\n          c = to_unsafe[i]\n          i += 1\n          unless c.unsafe_chr.ascii_whitespace?\n            index = i - 1\n            looking_for_space = true\n            break\n          end\n        end\n\n        break if limit_reached\n      end\n    end\n    if looking_for_space\n      piece_bytesize = bytesize - index\n      yield String.new(to_unsafe + index, piece_bytesize, piece_bytesize)\n    end\n  end\n\n  # Makes an `Array` by splitting the string on the given character *separator*\n  # (and removing that character).\n  #\n  # If *limit* is present, up to *limit* new strings will be created,\n  # with the entire remainder added to the last string.\n  #\n  # If *remove_empty* is `true`, any empty strings are removed from the result.\n  #\n  # ```\n  # \"foo,,bar,baz\".split(',')                     # => [\"foo\", \"\", \"bar\", \"baz\"]\n  # \"foo,,bar,baz\".split(',', remove_empty: true) # => [\"foo\", \"bar\", \"baz\"]\n  # \"foo,bar,baz\".split(',', 2)                   # => [\"foo\", \"bar,baz\"]\n  # ```\n  def split(separator : Char, limit = nil, *, remove_empty = false) : Array(String)\n    ary = Array(String).new\n    split(separator, limit, remove_empty: remove_empty) do |string|\n      ary << string\n    end\n    ary\n  end\n\n  # Splits the string after each character *separator* and yields each part to a block.\n  #\n  # If *limit* is present, up to *limit* new strings will be created,\n  # with the entire remainder added to the last string.\n  #\n  # If *remove_empty* is `true`, any empty strings are not yielded.\n  #\n  # ```\n  # ary = [] of String\n  #\n  # \"foo,,bar,baz\".split(',') { |string| ary << string }\n  # ary # => [\"foo\", \"\", \"bar\", \"baz\"]\n  # ary.clear\n  #\n  # \"foo,,bar,baz\".split(',', remove_empty: true) { |string| ary << string }\n  # ary # => [\"foo\", \"bar\", \"baz\"]\n  # ary.clear\n  #\n  # \"foo,bar,baz\".split(',', 2) { |string| ary << string }\n  # ary # => [\"foo\", \"bar,baz\"]\n  # ```\n  def split(separator : Char, limit = nil, *, remove_empty = false, &block : String -> _)\n    if empty?\n      yield \"\" unless remove_empty\n      return\n    end\n\n    if limit && limit <= 1\n      yield self\n      return\n    end\n\n    yielded = 0\n    byte_offset = 0\n\n    reader = Char::Reader.new(self)\n    reader.each do |char|\n      if char == separator\n        piece_bytesize = reader.pos - byte_offset\n        yield String.new(to_unsafe + byte_offset, piece_bytesize) unless remove_empty && piece_bytesize == 0\n        yielded += 1\n        byte_offset = reader.pos + reader.current_char_width\n        break if limit && yielded + 1 == limit\n      end\n    end\n\n    piece_bytesize = bytesize - byte_offset\n    return if remove_empty && piece_bytesize == 0\n    yield String.new(to_unsafe + byte_offset, piece_bytesize)\n  end\n\n  # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*).\n  #\n  # If *limit* is present, the array will be limited to *limit* items and\n  # the final item will contain the remainder of the string.\n  #\n  # If *separator* is an empty string (`\"\"`), the string will be separated into one-character strings.\n  #\n  # If *remove_empty* is `true`, any empty strings are removed from the result.\n  #\n  # ```\n  # long_river_name = \"Mississippi\"\n  # long_river_name.split(\"ss\")                    # => [\"Mi\", \"i\", \"ippi\"]\n  # long_river_name.split(\"i\")                     # => [\"M\", \"ss\", \"ss\", \"pp\", \"\"]\n  # long_river_name.split(\"i\", remove_empty: true) # => [\"M\", \"ss\", \"ss\", \"pp\"]\n  # long_river_name.split(\"\")                      # => [\"M\", \"i\", \"s\", \"s\", \"i\", \"s\", \"s\", \"i\", \"p\", \"p\", \"i\"]\n  # ```\n  def split(separator : String, limit = nil, *, remove_empty = false) : Array(String)\n    ary = Array(String).new\n    split(separator, limit, remove_empty: remove_empty) do |string|\n      ary << string\n    end\n    ary\n  end\n\n  # Splits the string after each string *separator* and yields each part to a block.\n  #\n  # If *limit* is present, the array will be limited to *limit* items and\n  # the final item will contain the remainder of the string.\n  #\n  # If *separator* is an empty string (`\"\"`), the string will be separated into one-character strings.\n  #\n  # If *remove_empty* is `true`, any empty strings are removed from the result.\n  #\n  # ```\n  # ary = [] of String\n  # long_river_name = \"Mississippi\"\n  #\n  # long_river_name.split(\"ss\") { |s| ary << s }\n  # ary # => [\"Mi\", \"i\", \"ippi\"]\n  # ary.clear\n  #\n  # long_river_name.split(\"i\") { |s| ary << s }\n  # ary # => [\"M\", \"ss\", \"ss\", \"pp\", \"\"]\n  # ary.clear\n  #\n  # long_river_name.split(\"i\", remove_empty: true) { |s| ary << s }\n  # ary # => [\"M\", \"ss\", \"ss\", \"pp\"]\n  # ary.clear\n  #\n  # long_river_name.split(\"\") { |s| ary << s }\n  # ary # => [\"M\", \"i\", \"s\", \"s\", \"i\", \"s\", \"s\", \"i\", \"p\", \"p\", \"i\"]\n  # ```\n  def split(separator : String, limit = nil, *, remove_empty = false, &block : String -> _)\n    if empty?\n      yield \"\" unless remove_empty\n      return\n    end\n\n    if limit && limit <= 1\n      yield self\n      return\n    end\n\n    if separator.empty?\n      split_by_empty_separator(limit) do |string|\n        yield string\n      end\n      return\n    end\n\n    yielded = 0\n    byte_offset = 0\n    separator_bytesize = separator.bytesize\n\n    single_byte_optimizable = single_byte_optimizable?\n\n    i = 0\n    stop = bytesize - separator.bytesize + 1\n    while i < stop\n      if (to_unsafe + i).memcmp(separator.to_unsafe, separator_bytesize) == 0\n        piece_bytesize = i - byte_offset\n        piece_size = single_byte_optimizable ? piece_bytesize : 0\n        unless remove_empty && piece_bytesize == 0\n          yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size)\n        end\n        yielded += 1\n        byte_offset = i + separator_bytesize\n        i += separator_bytesize - 1\n        break if limit && yielded + 1 == limit\n      end\n      i += 1\n    end\n\n    piece_bytesize = bytesize - byte_offset\n    return if remove_empty && piece_bytesize == 0\n    piece_size = single_byte_optimizable ? piece_bytesize : 0\n    yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size)\n  end\n\n  # Makes an `Array` by splitting the string on *separator* (and removing\n  # instances of *separator*).\n  #\n  # If *separator* is an empty regex (`//`), the string will be separated into\n  # one-character strings. If *separator* defines any capture groups, their\n  # matches are also included in the result.\n  #\n  # If *limit* is present, *separator* will be matched at most `limit - 1`\n  # times, and the final item will contain the remainder of the string. The\n  # array may contain more than *limit* items if capture groups are present.\n  #\n  # If *remove_empty* is `true`, any empty strings are removed from the result.\n  # This does not affect matches from *separator*'s capture groups.\n  #\n  # ```\n  # long_river_name = \"Mississippi\"\n  # long_river_name.split(/s+/)  # => [\"Mi\", \"i\", \"ippi\"]\n  # long_river_name.split(//)    # => [\"M\", \"i\", \"s\", \"s\", \"i\", \"s\", \"s\", \"i\", \"p\", \"p\", \"i\"]\n  # long_river_name.split(/(i)/) # => [\"M\", \"i\", \"ss\", \"i\", \"ss\", \"i\", \"pp\", \"i\", \"\"]\n  # ```\n  def split(separator : Regex, limit = nil, *, remove_empty = false, options : Regex::MatchOptions = Regex::MatchOptions::None) : Array(String)\n    ary = Array(String).new\n    split(separator, limit, remove_empty: remove_empty, options: options) do |string|\n      ary << string\n    end\n    ary\n  end\n\n  # Splits the string after each regex *separator* and yields each part to a\n  # block.\n  #\n  # If *separator* is an empty regex (`//`), the string will be separated into\n  # one-character strings. If *separator* defines any capture groups, their\n  # matches are also yielded in order.\n  #\n  # If *limit* is present, *separator* will be matched at most `limit - 1`\n  # times, and the final item will contain the remainder of the string. More\n  # than *limit* items may be yielded in total if capture groups are present.\n  #\n  # If *remove_empty* is `true`, any empty strings are not yielded. This does\n  # not affect matches from *separator*'s capture groups.\n  #\n  # ```\n  # ary = [] of String\n  # long_river_name = \"Mississippi\"\n  #\n  # long_river_name.split(/s+/) { |s| ary << s }\n  # ary # => [\"Mi\", \"i\", \"ippi\"]\n  # ary.clear\n  #\n  # long_river_name.split(//) { |s| ary << s }\n  # ary # => [\"M\", \"i\", \"s\", \"s\", \"i\", \"s\", \"s\", \"i\", \"p\", \"p\", \"i\"]\n  # ary.clear\n  #\n  # long_river_name.split(/(i)/) { |s| ary << s }\n  # ary # => [\"M\", \"i\", \"ss\", \"i\", \"ss\", \"i\", \"pp\", \"i\", \"\"]\n  # ```\n  def split(separator : Regex, limit = nil, *, remove_empty = false, options : Regex::MatchOptions = Regex::MatchOptions::None, &block : String -> _)\n    if empty?\n      yield \"\" unless remove_empty\n      return\n    end\n\n    if limit && limit <= 1\n      yield self\n      return\n    end\n\n    if separator.source.empty?\n      split_by_empty_separator(limit) do |string|\n        yield string\n      end\n      return\n    end\n\n    count = 0\n    match_offset = slice_offset = 0\n\n    while match = separator.match_at_byte_index(self, match_offset, options: options)\n      index = match.byte_begin(0)\n      match_bytesize = match.byte_end(0) - index\n      next_offset = index + match_bytesize\n\n      if next_offset == slice_offset\n        match_offset = next_offset + char_bytesize_at(next_offset)\n      else\n        slice_size = index - slice_offset\n\n        yield byte_slice(slice_offset, slice_size) unless remove_empty && slice_size == 0\n        count += 1\n\n        1.upto(match.size) do |i|\n          if group = match[i]?\n            yield group\n          end\n        end\n\n        slice_offset = match_offset = next_offset\n      end\n\n      break if limit && count + 1 == limit\n      break if match_offset >= bytesize\n      options |= :no_utf_check\n    end\n\n    yield byte_slice(slice_offset) unless remove_empty && slice_offset == bytesize\n  end\n\n  private def split_by_empty_separator(limit, &block : String -> _)\n    yielded = 0\n\n    each_char do |c|\n      yield c.to_s\n      yielded += 1\n      break if limit && yielded + 1 == limit\n    end\n\n    if limit && yielded != size\n      yield self[yielded..-1]\n      yielded += 1\n    end\n  end\n\n  # Returns an array of the string split into lines.\n  #\n  # Both LF (line feed, `\\n`) and CRLF (carriage return line feed, `\\r\\n`) are\n  # recognized as line delimiters.\n  #\n  # If *chomp* is true, the line separator is removed from the end of each line.\n  #\n  # ```\n  # \"hello\\nworld\\n\".lines                 # => [\"hello\", \"world\"]\n  # \"hello\\nworld\\n\".lines(chomp: false)   # => [\"hello\\n\", \"world\\n\"]\n  # \"hello\\nworld\\r\\n\".lines               # => [\"hello\", \"world\"]\n  # \"hello\\nworld\\r\\n\".lines(chomp: false) # => [\"hello\\n\", \"world\\r\\n\"]\n  # ```\n  #\n  # A trailing line feed is not considered starting a final, empty line.  The\n  # empty string does not contain any lines.\n  #\n  # ```\n  # \"hellp\\n\".lines # => [\"hellp\"]\n  # \"\\n\".lines      # => [\"\"]\n  # \"\".lines        # => [] of String\n  # ```\n  #\n  # * `#each_line` yields each line without allocating an array\n  def lines(chomp : Bool = true) : Array(String)\n    lines = [] of String\n    each_line(chomp: chomp) do |line|\n      lines << line\n    end\n    lines\n  end\n\n  # Splits the string after each newline and yields each line.\n  #\n  # Both LF (line feed, `\\n`) and CRLF (carriage return line feed, `\\r\\n`) are\n  # recognized as line delimiters.\n  #\n  # If *chomp* is true, the line separator is removed from the end of each line.\n  #\n  # ```\n  # \"hello\\nworld\".each_line { }                   # yields \"hello\", \"world\"\n  # \"hello\\nworld\".each_line(chomp: false) { }     # yields \"hello\\n\", \"world\"\n  # \"hello\\nworld\\r\\n\".each_line { }               # yields \"hello\", \"world\"\n  # \"hello\\nworld\\r\\n\".each_line(chomp: false) { } # yields \"hello\\n\", \"world\\r\\n\"\n  # ```\n  #\n  # If *remove_empty* is `true`, any empty lines are removed from the result.\n  #\n  # A trailing line feed is not considered starting a final, empty line.  The\n  # empty string does not contain any lines.\n  #\n  # ```\n  # \"hello\\n\".each_line { } # yields \"hello\"\n  # \"\\n\".each_line { }      # yields \"\"\n  # \"\".each_line { }        # does not yield\n  # ```\n  #\n  # * `#lines` returns an array of lines\n  def each_line(chomp : Bool = true, remove_empty : Bool = false, & : String ->) : Nil\n    return if empty?\n\n    offset = 0\n\n    while byte_index = byte_index('\\n'.ord.to_u8, offset)\n      count = byte_index - offset + 1\n\n      if remove_empty && (byte_index == offset || (byte_index == offset + 1 && to_unsafe[offset] === '\\r'))\n        offset = byte_index + 1\n        next\n      end\n\n      if chomp\n        count -= 1\n        if offset + count > 0 && to_unsafe[offset + count - 1] === '\\r'\n          count -= 1\n        end\n      end\n\n      yield unsafe_byte_slice_string(offset, count)\n      offset = byte_index + 1\n    end\n\n    unless offset == bytesize\n      yield unsafe_byte_slice_string(offset)\n    end\n  end\n\n  # Returns an `Iterator` which yields each line of this string (see `String#each_line`).\n  def each_line(chomp = true, *, remove_empty : Bool = false)\n    LineIterator.new(self, chomp, remove_empty)\n  end\n\n  # Converts camelcase boundaries to underscores.\n  #\n  # ```\n  # \"DoesWhatItSaysOnTheTin\".underscore                         # => \"does_what_it_says_on_the_tin\"\n  # \"PartyInTheUSA\".underscore                                  # => \"party_in_the_usa\"\n  # \"HTTP_CLIENT\".underscore                                    # => \"http_client\"\n  # \"3.14IsPi\".underscore                                       # => \"3.14_is_pi\"\n  # \"InterestingImage\".underscore(Unicode::CaseOptions::Turkic) # => \"ınteresting_ımage\"\n  # ```\n  def underscore(options : Unicode::CaseOptions = :none) : String\n    String.build(bytesize + 10) { |io| underscore io, options }\n  end\n\n  # Writes an underscored version of `self` to the given *io*.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"DoesWhatItSaysOnTheTin\".underscore io\n  # io.to_s # => \"does_what_it_says_on_the_tin\"\n  # ```\n  def underscore(io : IO, options : Unicode::CaseOptions = :none) : Nil\n    first = true\n    last_is_downcase = false\n    last_is_upcase = false\n    last_is_digit = false\n    mem : Char? = nil\n\n    each_char do |char|\n      if options.ascii?\n        digit = char.ascii_number?\n        downcase = digit || char.ascii_lowercase?\n        upcase = char.ascii_uppercase?\n      else\n        digit = char.number?\n        downcase = digit || char.lowercase?\n        upcase = char.uppercase?\n      end\n\n      if first\n        char.downcase(io, options)\n      elsif last_is_downcase && upcase\n        if mem\n          # This is the case of A1Bcd, we need to put 'mem' (not to need to convert as downcase\n          #                       ^\n          # because 'mem' is digit surely) before putting this char as downcase.\n          io << mem\n          mem = nil\n        end\n        # This is the case of AbcDe, we need to put an underscore before the 'D'\n        #                        ^\n        io << '_'\n        char.downcase(io, options)\n      elsif (last_is_upcase || last_is_digit) && (upcase || digit)\n        # This is the case of 1) A1Bcd, 2) A1BCd or 3) A1B_cd:if the next char is upcase (case 1) we need\n        #                          ^         ^           ^\n        # 1) we need to append this char as downcase\n        # 2) we need to append an underscore and then the char as downcase, so we save this char\n        #    in 'mem' and decide later\n        # 3) we need to append this char as downcase and then a single underscore\n        if mem\n          # case 2\n          mem.downcase(io, options)\n        end\n        mem = char\n      else\n        if mem\n          if char == '_'\n            # case 3\n          elsif last_is_upcase && downcase\n            # case 1\n            io << '_'\n          end\n          mem.downcase(io, options)\n          mem = nil\n        end\n\n        char.downcase(io, options)\n      end\n\n      last_is_downcase = downcase\n      last_is_upcase = upcase\n      last_is_digit = digit\n      first = false\n    end\n\n    mem.downcase(io, options) if mem\n  end\n\n  # Converts underscores to camelcase boundaries.\n  #\n  # If *lower* is true, lower camelcase will be returned (the first letter is downcased).\n  #\n  # ```\n  # \"eiffel_tower\".camelcase                                            # => \"EiffelTower\"\n  # \"empire_state_building\".camelcase(lower: true)                      # => \"empireStateBuilding\"\n  # \"isolated_integer\".camelcase(options: Unicode::CaseOptions::Turkic) # => \"İsolatedİnteger\"\n  # ```\n  def camelcase(options : Unicode::CaseOptions = Unicode::CaseOptions::None, *, lower : Bool = false) : String\n    return self if empty?\n\n    String.build(bytesize) { |io| camelcase io, options, lower: lower }\n  end\n\n  # Writes an camelcased version of `self` to the given *io*.\n  #\n  # If *lower* is true, lower camelcase will be written (the first letter is downcased).\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"eiffel_tower\".camelcase io\n  # io.to_s # => \"EiffelTower\"\n  # ```\n  def camelcase(io : IO, options : Unicode::CaseOptions = Unicode::CaseOptions::None, *, lower : Bool = false) : Nil\n    first = true\n    last_is_underscore = false\n\n    each_char do |char|\n      if first\n        if lower\n          char.downcase(io, options)\n        else\n          char.titlecase(io, options)\n        end\n      elsif char == '_'\n        last_is_underscore = true\n      elsif last_is_underscore\n        char.titlecase(io, options)\n        last_is_underscore = false\n      else\n        io << char\n      end\n      first = false\n    end\n  end\n\n  # Reverses the order of characters in the string.\n  #\n  # ```\n  # \"Argentina\".reverse # => \"anitnegrA\"\n  # \"racecar\".reverse   # => \"racecar\"\n  # ```\n  #\n  # Works on Unicode graphemes (and not codepoints) so combining characters are preserved.\n  #\n  # ```\n  # \"Noe\\u0308l\".reverse # => \"lëoN\"\n  # ```\n  def reverse : String\n    return self if bytesize <= 1\n\n    if single_byte_optimizable?\n      String.new(bytesize) do |buffer|\n        bytesize.times do |i|\n          buffer[i] = self.to_unsafe[bytesize - i - 1]\n        end\n        {@bytesize, @length}\n      end\n    else\n      # Iterate graphemes to reverse the string,\n      # so combining characters are placed correctly\n      String.new(bytesize) do |buffer|\n        buffer += bytesize\n        each_grapheme_boundary do |range|\n          buffer -= range.size\n          buffer.copy_from(to_unsafe + range.begin, range.size)\n        end\n        {@bytesize, @length}\n      end\n    end\n  end\n\n  # Adds instances of *char* to right of the string until it is at least size of *len*.\n  #\n  # ```\n  # \"Purple\".ljust(8)      # => \"Purple  \"\n  # \"Purple\".ljust(8, '-') # => \"Purple--\"\n  # \"Aubergine\".ljust(8)   # => \"Aubergine\"\n  # ```\n  def ljust(len : Int, char : Char = ' ') : String\n    just len, char, -1\n  end\n\n  # Adds instances of *char* to right of the string until it is at least size of *len*,\n  # and then appends the result to the given IO.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"Purple\".ljust(io, 8, '-')\n  # io.to_s # => \"Purple--\"\n  # ```\n  def ljust(io : IO, len : Int, char : Char = ' ') : Nil\n    io << self\n    (len - size).times { io << char }\n  end\n\n  # Adds instances of *char* to left of the string until it is at least size of *len*.\n  #\n  # ```\n  # \"Purple\".rjust(8)      # => \"  Purple\"\n  # \"Purple\".rjust(8, '-') # => \"--Purple\"\n  # \"Aubergine\".rjust(8)   # => \"Aubergine\"\n  # ```\n  def rjust(len : Int, char : Char = ' ') : String\n    just len, char, 1\n  end\n\n  # Adds instances of *char* to left of the string until it is at least size of *len*,\n  # and then appends the result to the given IO.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"Purple\".rjust(io, 8, '-')\n  # io.to_s # => \"--Purple\"\n  # ```\n  def rjust(io : IO, len : Int, char : Char = ' ') : Nil\n    (len - size).times { io << char }\n    io << self\n  end\n\n  # Adds instances of *char* to left and right of the string until it is at least size of *len*.\n  #\n  # ```\n  # \"Purple\".center(8)      # => \" Purple \"\n  # \"Purple\".center(8, '-') # => \"-Purple-\"\n  # \"Purple\".center(9, '-') # => \"-Purple--\"\n  # \"Aubergine\".center(8)   # => \"Aubergine\"\n  # ```\n  def center(len : Int, char : Char = ' ') : String\n    just len, char, 0\n  end\n\n  # Adds instances of *char* to left and right of the string until it is at least size of *len*,\n  # then appends the result to the given IO.\n  #\n  # ```\n  # io = IO::Memory.new\n  # \"Purple\".center(io, 9, '-')\n  # io.to_s # => \"-Purple--\"\n  # ```\n  def center(io : IO, len : Int, char : Char = ' ') : Nil\n    difference = len - size\n\n    if difference <= 0\n      io << self\n      return\n    end\n\n    left_padding = difference // 2\n    right_padding = difference - left_padding\n\n    left_padding.times { io << char }\n    io << self\n    right_padding.times { io << char }\n  end\n\n  private def just(len, char, justify)\n    return self if size >= len\n\n    bytes, count = String.char_bytes_and_bytesize(char)\n    padding = (len - size)\n    new_bytesize = bytesize + padding * count\n    case justify\n    when .< 0\n      leftpadding, rightpadding = 0, padding\n    when .> 0\n      leftpadding, rightpadding = padding, 0\n    else\n      leftpadding = padding // 2\n      rightpadding = padding - leftpadding\n    end\n\n    String.new(new_bytesize) do |buffer|\n      if leftpadding > 0\n        if count == 1\n          Slice.new(buffer, leftpadding).fill(char.ord.to_u8)\n          buffer += leftpadding\n        else\n          leftpadding.times do\n            buffer.copy_from(bytes.to_unsafe, count)\n            buffer += count\n          end\n        end\n      end\n      buffer.copy_from(to_unsafe, bytesize)\n      buffer += bytesize\n      if rightpadding > 0\n        if count == 1\n          Slice.new(buffer, rightpadding).fill(char.ord.to_u8)\n        else\n          rightpadding.times do\n            buffer.copy_from(bytes.to_unsafe, count)\n            buffer += count\n          end\n        end\n      end\n      {new_bytesize, len}\n    end\n  end\n\n  # Returns the successor of the string. The successor is calculated\n  # by incrementing characters starting from the rightmost alphanumeric\n  # (or the rightmost character if there are no alphanumerics) in the string.\n  # Incrementing a digit always results in another digit, and incrementing\n  # a letter results in another letter of the same case.\n  #\n  # If the increment generates a \"carry\", the character to the left of it is\n  # incremented. This process repeats until there is no carry,\n  # adding an additional character if necessary.\n  #\n  # ```\n  # \"abcd\".succ      # => \"abce\"\n  # \"THX1138\".succ   # => \"THX1139\"\n  # \"((koala))\".succ # => \"((koalb))\"\n  # \"1999zzz\".succ   # => \"2000aaa\"\n  # \"ZZZ9999\".succ   # => \"AAAA0000\"\n  # \"***\".succ       # => \"**+\"\n  # ```\n  def succ : String\n    return self if empty?\n\n    chars = self.chars\n\n    carry = nil\n    last_alnum = 0\n    index = size - 1\n\n    while index >= 0\n      s = chars[index]\n      if s.ascii_alphanumeric?\n        carry = 0\n        if ('0' <= s && s < '9') ||\n           ('a' <= s && s < 'z') ||\n           ('A' <= s && s < 'Z')\n          chars[index] = s.succ\n          break\n        elsif s == '9'\n          chars[index] = '0'\n          carry = '1'\n        elsif s == 'z'\n          chars[index] = carry = 'a'\n        elsif s == 'Z'\n          chars[index] = carry = 'A'\n        end\n\n        last_alnum = index\n      end\n      index -= 1\n    end\n\n    if carry.nil? # there were no alphanumeric chars\n      chars[size - 1] = chars[size - 1].succ\n    end\n\n    if carry.is_a?(Char) && index < 0 # we still have a carry and already reached the beginning\n      chars.insert(last_alnum, carry)\n    end\n\n    String.build(chars.size) do |str|\n      chars.each do |char|\n        str << char\n      end\n    end\n  end\n\n  # Finds matches of *regex* starting at *pos* and updates `$~` to the result.\n  #\n  # ```\n  # \"foo\".match(/foo/) # => Regex::MatchData(\"foo\")\n  # $~                 # => Regex::MatchData(\"foo\")\n  #\n  # \"foo\".match(/bar/) # => nil\n  # $~                 # raises Exception\n  # ```\n  def match(regex : Regex, pos = 0, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Regex::MatchData?\n    $~ = regex.match self, pos, options: options\n  end\n\n  # Finds matches of *regex* starting at *pos* and updates `$~` to the result.\n  # Raises `Regex::Error` if there are no matches.\n  #\n  # ```\n  # \"foo\".match!(/foo/) # => Regex::MatchData(\"foo\")\n  # $~                  # => Regex::MatchData(\"foo\")\n  #\n  # \"foo\".match!(/bar/) # => raises Exception\n  def match!(regex : Regex, pos = 0, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Regex::MatchData\n    $~ = regex.match! self, pos, options: options\n  end\n\n  # Finds match of *regex* like `#match`, but it returns `Bool` value.\n  # It neither returns `MatchData` nor assigns it to the `$~` variable.\n  #\n  # ```\n  # \"foo\".matches?(/bar/) # => false\n  # \"foo\".matches?(/foo/) # => true\n  #\n  # # `$~` is not set even if last match succeeds.\n  # $~ # raises Exception\n  # ```\n  def matches?(regex : Regex, pos = 0, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Bool\n    regex.matches? self, pos, options: options\n  end\n\n  # Matches the regular expression *regex* against the entire string and returns\n  # the resulting `MatchData`.\n  # It also updates `$~` with the result.\n  #\n  # ```\n  # \"foo\".match_full(/foo/)  # => Regex::MatchData(\"foo\")\n  # $~                       # => Regex::MatchData(\"foo\")\n  # \"fooo\".match_full(/foo/) # => nil\n  # $~                       # raises Exception\n  # ```\n  def match_full(regex : Regex) : Regex::MatchData?\n    match(regex, options: Regex::MatchOptions::ANCHORED | Regex::MatchOptions::ENDANCHORED)\n  end\n\n  # Matches the regular expression *regex* against the entire string and returns\n  # the resulting `MatchData`.\n  # It also updates `$~` with the result.\n  # Raises `Regex::Error` if there are no matches.\n  #\n  # ```\n  # \"foo\".match_full!(/foo/)  # => Regex::MatchData(\"foo\")\n  # $~                        # => Regex::MatchData(\"foo\")\n  # \"fooo\".match_full!(/foo/) # Regex::Error\n  # $~                        # raises Exception\n  # ```\n  def match_full!(regex : Regex) : Regex::MatchData\n    match!(regex, options: Regex::MatchOptions::ANCHORED | Regex::MatchOptions::ENDANCHORED)\n  end\n\n  # Returns `true` if the regular expression *regex* matches this string entirely.\n  #\n  # ```\n  # \"foo\".matches_full?(/foo/)  # => true\n  # \"fooo\".matches_full?(/foo/) # => false\n  #\n  # # `$~` is not set even if last match succeeds.\n  # $~ # raises Exception\n  # ```\n  def matches_full?(regex : Regex) : Bool\n    matches?(regex, options: Regex::MatchOptions::ANCHORED | Regex::MatchOptions::ENDANCHORED)\n  end\n\n  # Searches the string for instances of *pattern*,\n  # yielding a `Regex::MatchData` for each match.\n  def scan(pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None, &) : self\n    byte_offset = 0\n\n    while match = pattern.match_at_byte_index(self, byte_offset, options: options)\n      index = match.byte_begin(0)\n      $~ = match\n      yield match\n      match_bytesize = match.byte_end(0) - index\n      match_bytesize += char_bytesize_at(index) if match_bytesize == 0\n      byte_offset = index + match_bytesize\n      options |= :no_utf_check\n    end\n\n    self\n  end\n\n  # Searches the string for instances of *pattern*,\n  # returning an `Array` of `Regex::MatchData` for each match.\n  def scan(pattern : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Array(Regex::MatchData)\n    matches = [] of Regex::MatchData\n    scan(pattern, options: options) do |match|\n      matches << match\n    end\n    matches\n  end\n\n  # Searches the string for instances of *pattern*,\n  # yielding the matched string for each match.\n  def scan(pattern : String, &) : self\n    return self if pattern.empty?\n    index = 0\n    while index = byte_index(pattern, index)\n      yield pattern\n      index += pattern.bytesize\n    end\n    self\n  end\n\n  # Searches the string for instances of *pattern*,\n  # returning an array of the matched string for each match.\n  def scan(pattern : String) : Array(String)\n    matches = [] of String\n    scan(pattern) do |match|\n      matches << match\n    end\n    matches\n  end\n\n  # Yields each character in the string to the block.\n  #\n  # ```\n  # array = [] of Char\n  # \"ab☃\".each_char do |char|\n  #   array << char\n  # end\n  # array # => ['a', 'b', '☃']\n  # ```\n  def each_char(&) : Nil\n    if single_byte_optimizable?\n      each_byte do |byte|\n        yield (byte < 0x80 ? byte.unsafe_chr : Char::REPLACEMENT)\n      end\n    else\n      Char::Reader.new(self).each do |char|\n        yield char\n      end\n    end\n  end\n\n  # Returns an `Iterator` over each character in the string.\n  #\n  # ```\n  # chars = \"ab☃\".each_char\n  # chars.next # => 'a'\n  # chars.next # => 'b'\n  # chars.next # => '☃'\n  # ```\n  def each_char\n    CharIterator.new(Char::Reader.new(self))\n  end\n\n  # Yields each character and its index in the string to the block.\n  #\n  # ```\n  # array = [] of Tuple(Char, Int32)\n  # \"ab☃\".each_char_with_index do |char, index|\n  #   array << {char, index}\n  # end\n  # array # => [{'a', 0}, {'b', 1}, {'☃', 2}]\n  # ```\n  #\n  # Accepts an optional *offset* parameter, which tells it to start counting\n  # from there.\n  def each_char_with_index(offset = 0, &)\n    each_char do |char|\n      yield char, offset\n      offset += 1\n    end\n  end\n\n  # Returns an `Array` of all characters in the string.\n  #\n  # ```\n  # \"ab☃\".chars # => ['a', 'b', '☃']\n  # ```\n  def chars : Array(Char)\n    chars = Array(Char).new(@length > 0 ? @length : bytesize)\n    each_char do |char|\n      chars << char\n    end\n    chars\n  end\n\n  # Yields each codepoint to the block.\n  #\n  # ```\n  # array = [] of Int32\n  # \"ab☃\".each_codepoint do |codepoint|\n  #   array << codepoint\n  # end\n  # array # => [97, 98, 9731]\n  # ```\n  #\n  # See also: `Char#ord`.\n  def each_codepoint(&)\n    each_char do |char|\n      yield char.ord\n    end\n  end\n\n  # Returns an `Iterator` for each codepoint.\n  #\n  # ```\n  # codepoints = \"ab☃\".each_codepoint\n  # codepoints.next # => 97\n  # codepoints.next # => 98\n  # codepoints.next # => 9731\n  # ```\n  #\n  # See also: `Char#ord`.\n  def each_codepoint\n    each_char.map &.ord\n  end\n\n  # Returns an `Array` of the codepoints that make the string.\n  #\n  # ```\n  # \"ab☃\".codepoints # => [97, 98, 9731]\n  # ```\n  #\n  # See also: `Char#ord`.\n  def codepoints : Array(Int32)\n    codepoints = Array(Int32).new(@length > 0 ? @length : bytesize)\n    each_codepoint do |codepoint|\n      codepoints << codepoint\n    end\n    codepoints\n  end\n\n  # Yields each byte in the string to the block.\n  #\n  # ```\n  # array = [] of UInt8\n  # \"ab☃\".each_byte do |byte|\n  #   array << byte\n  # end\n  # array # => [97, 98, 226, 152, 131]\n  # ```\n  def each_byte(&)\n    to_slice.each do |byte|\n      yield byte\n    end\n    nil\n  end\n\n  # Returns an `Iterator` over each byte in the string.\n  #\n  # ```\n  # bytes = \"ab☃\".each_byte\n  # bytes.next # => 97\n  # bytes.next # => 98\n  # bytes.next # => 226\n  # bytes.next # => 152\n  # bytes.next # => 131\n  # ```\n  def each_byte\n    to_slice.each\n  end\n\n  # Returns this string's bytes as an `Array(UInt8)`.\n  #\n  # ```\n  # \"hello\".bytes # => [104, 101, 108, 108, 111]\n  # \"你好\".bytes    # => [228, 189, 160, 229, 165, 189]\n  # ```\n  def bytes : Array(UInt8)\n    Array.new(bytesize) { |i| to_unsafe[i] }\n  end\n\n  # Pretty prints `self` into the given printer.\n  def pretty_print(pp : PrettyPrint) : Nil\n    printed_bytesize = 0\n    pp.group do\n      split('\\n') do |part|\n        printed_bytesize += part.bytesize\n        if printed_bytesize != bytesize\n          printed_bytesize += 1 # == \"\\n\".bytesize\n          pp.text('\"')\n          pp.text(part.inspect_unquoted)\n          pp.text(\"\\\\n\\\"\")\n          break if printed_bytesize == bytesize\n          pp.text(\" +\")\n          pp.breakable\n        else\n          pp.text(part.inspect)\n        end\n      end\n    end\n  end\n\n  # Returns a representation of `self` as a Crystal string literal, wrapped in\n  # double quotes.\n  #\n  # Non-printable characters (see `Char#printable?`) are escaped.\n  #\n  # ```\n  # \"\\u{1f48e} - à la carte\\n\".inspect # => %(\"\\u{1F48E} - à la carte\\\\n\")\n  # ```\n  #\n  # See `Char#unicode_escape` for the format used to escape characters without a\n  # special escape sequence.\n  #\n  # * `#inspect_unquoted` omits the delimiters.\n  # * `#dump` additionally escapes all non-ASCII characters.\n  def inspect : String\n    super\n  end\n\n  # :ditto:\n  def inspect(io : IO) : Nil\n    dump_or_inspect(io) do |char, error|\n      inspect_char(char, error, io)\n    end\n  end\n\n  # Returns a representation of `self` as the content of a Crystal string literal\n  # without delimiters.\n  #\n  # Non-printable characters (see `Char#printable?`) are escaped.\n  #\n  # ```\n  # \"\\u{1f48e} - à la carte\\n\".inspect_unquoted # => %(\\u{1F48E} - à la carte\\\\n)\n  # ```\n  #\n  # See `Char#unicode_escape` for the format used to escape characters without a\n  # special escape sequence.\n  #\n  # * `#inspect` wraps the content in double quotes.\n  # * `#dump_unquoted` additionally escapes all non-ASCII characters.\n  def inspect_unquoted : String\n    String.build do |io|\n      inspect_unquoted(io)\n    end\n  end\n\n  # :ditto:\n  def inspect_unquoted(io : IO) : Nil\n    dump_or_inspect_unquoted(io) do |char, error|\n      inspect_char(char, error, io)\n    end\n  end\n\n  # Returns a representation of `self` as an ASCII-compatible Crystal string\n  # literal, wrapped in double quotes.\n  #\n  # Non-printable characters (see `Char#printable?`) and non-ASCII characters\n  # (codepoints larger `U+007F`) are escaped.\n  #\n  # ```\n  # \"\\u{1f48e} - à la carte\\n\".dump # => %(\"\\\\u{1F48E} - \\\\u00E0 la carte\\\\n\")\n  # ```\n  #\n  # See `Char#unicode_escape` for the format used to escape characters without a\n  # special escape sequence.\n  #\n  # * `#dump_unquoted` omits the delimiters.\n  # * `#inspect` only escapes non-printable characters.\n  def dump : String\n    String.build do |io|\n      dump io\n    end\n  end\n\n  # :ditto:\n  def dump(io : IO) : Nil\n    dump_or_inspect(io) do |char, error|\n      dump_char(char, error, io)\n    end\n  end\n\n  # Returns a representation of `self` as the content of an ASCII-compatible\n  # Crystal string literal without delimiters.\n  #\n  # Non-printable characters (see `Char#printable?`) and non-ASCII characters\n  # (codepoints larger `U+007F`) are escaped.\n  #\n  # ```\n  # \"\\u{1f48e} - à la carte\\n\".dump_unquoted # => %(\\\\u{1F48E} - \\\\u00E0 la carte\\\\n)\n  # ```\n  #\n  # See `Char#unicode_escape` for the format used to escape characters without a\n  # special escape sequence.\n  #\n  # * `#dump` wraps the content in double quotes.\n  # * `#inspect_unquoted` only escapes non-printable characters.\n  def dump_unquoted : String\n    String.build do |io|\n      dump_unquoted(io)\n    end\n  end\n\n  # :nodoc:\n  def dump_unquoted(io : IO) : Nil\n    dump_or_inspect_unquoted(io) do |char, error|\n      dump_char(char, error, io)\n    end\n  end\n\n  private def dump_or_inspect(io, &)\n    io << '\"'\n    dump_or_inspect_unquoted(io) do |char, error|\n      yield char, error\n    end\n    io << '\"'\n  end\n\n  private def dump_or_inspect_unquoted(io, &)\n    reader = Char::Reader.new(self)\n    while reader.has_next?\n      current_char = reader.current_char\n      case current_char\n      when '\"'  then io << \"\\\\\\\"\"\n      when '\\\\' then io << \"\\\\\\\\\"\n      when '\\a' then io << \"\\\\a\"\n      when '\\b' then io << \"\\\\b\"\n      when '\\e' then io << \"\\\\e\"\n      when '\\f' then io << \"\\\\f\"\n      when '\\n' then io << \"\\\\n\"\n      when '\\r' then io << \"\\\\r\"\n      when '\\t' then io << \"\\\\t\"\n      when '\\v' then io << \"\\\\v\"\n      when '#'\n        current_char = reader.next_char\n        if current_char == '{'\n          io << \"\\\\\\#{\"\n          reader.next_char\n          next\n        else\n          io << '#'\n          next\n        end\n      else\n        if reader.error\n          reader.current_char_width.times do |i|\n            yield '\\0', to_unsafe[reader.pos + i]\n          end\n        else\n          yield current_char, nil\n        end\n      end\n      reader.next_char\n    end\n  end\n\n  private def inspect_char(char, error, io)\n    dump_or_inspect_char char, error, io do\n      !char.printable?\n    end\n  end\n\n  private def dump_char(char, error, io)\n    dump_or_inspect_char char, error, io do\n      # Technically, the condition would be `!char.ascii? || !char.printable?` but\n      # all non-printable ASCII characters are control characters, so we can simplify.\n      !char.ascii? || char.ascii_control?\n    end\n  end\n\n  private def dump_or_inspect_char(char, error, io, &)\n    if error\n      dump_hex(error, io)\n    elsif yield\n      char.unicode_escape(io)\n    else\n      io << char\n    end\n  end\n\n  private def dump_hex(char, io)\n    io << \"\\\\x\"\n    io << '0' if char < 0x0F\n    char.to_s(io, 16, upcase: true)\n  end\n\n  # Returns `true` if this string starts with the given *str*.\n  #\n  # ```\n  # \"hello\".starts_with?(\"h\")  # => true\n  # \"hello\".starts_with?(\"he\") # => true\n  # \"hello\".starts_with?(\"hu\") # => false\n  # ```\n  def starts_with?(str : String) : Bool\n    return false if str.bytesize > bytesize\n    to_unsafe.memcmp(str.to_unsafe, str.bytesize) == 0\n  end\n\n  # Returns `true` if this string starts with the given *char*.\n  #\n  # ```\n  # \"hello\".starts_with?('h') # => true\n  # \"hello\".starts_with?('e') # => false\n  # ```\n  def starts_with?(char : Char) : Bool\n    each_char do |c|\n      return c == char\n    end\n\n    false\n  end\n\n  # Returns `true` if the regular expression *re* matches at the start of this string.\n  #\n  # ```\n  # \"22hello\".starts_with?(/[0-9]/) # => true\n  # \"22hello\".starts_with?(/[a-z]/) # => false\n  # \"h22\".starts_with?(/[a-z]/)     # => true\n  # \"h22\".starts_with?(/[A-Z]/)     # => false\n  # \"h22\".starts_with?(/[a-z]{2}/)  # => false\n  # \"hh22\".starts_with?(/[a-z]{2}/) # => true\n  # ```\n  def starts_with?(re : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Bool\n    !!($~ = re.match_at_byte_index(self, 0, options: options | Regex::MatchOptions::ANCHORED))\n  end\n\n  # Returns `true` if this string ends with the given *str*.\n  #\n  # ```\n  # \"hello\".ends_with?(\"o\")  # => true\n  # \"hello\".ends_with?(\"lo\") # => true\n  # \"hello\".ends_with?(\"ll\") # => false\n  # ```\n  def ends_with?(str : String) : Bool\n    return false if str.bytesize > bytesize\n    (to_unsafe + bytesize - str.bytesize).memcmp(str.to_unsafe, str.bytesize) == 0\n  end\n\n  # Returns `true` if this string ends with the given *char*.\n  #\n  # ```\n  # \"hello\".ends_with?('o') # => true\n  # \"hello\".ends_with?('l') # => false\n  # ```\n  def ends_with?(char : Char) : Bool\n    return false unless bytesize > 0\n\n    if char.ascii? || single_byte_optimizable?\n      byte = to_unsafe[bytesize - 1]\n      return byte < 0x80 ? byte == char.ord : char == Char::REPLACEMENT\n    end\n\n    bytes, count = String.char_bytes_and_bytesize(char)\n    return false if bytesize < count\n\n    count.times do |i|\n      return false unless to_unsafe[bytesize - count + i] == bytes[i]\n    end\n\n    true\n  end\n\n  # Returns `true` if the regular expression *re* matches at the end of this string.\n  #\n  # ```\n  # \"22hello\".ends_with?(/[0-9]/) # => false\n  # \"22hello\".ends_with?(/[a-z]/) # => true\n  # \"22h\".ends_with?(/[a-z]/)     # => true\n  # \"22h\".ends_with?(/[A-Z]/)     # => false\n  # \"22h\".ends_with?(/[a-z]{2}/)  # => false\n  # \"22hh\".ends_with?(/[a-z]{2}/) # => true\n  # ```\n  def ends_with?(re : Regex, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : Bool\n    if Regex.supports_match_options?(Regex::MatchOptions::ENDANCHORED)\n      result = re.match(self, options: options | Regex::MatchOptions::ENDANCHORED)\n    else\n      # Workaround when ENDANCHORED is unavailable (PCRE).\n      result = /#{re}\\z/.match(self, options: options)\n    end\n    $~ = result\n    !!result\n  end\n\n  # Interpolates *other* into the string using top-level `::sprintf`.\n  #\n  # ```\n  # \"I have %d apples\" % 5                                             # => \"I have 5 apples\"\n  # \"%s, %s, %s, D\" % ['A', 'B', 'C']                                  # => \"A, B, C, D\"\n  # \"sum: %{one} + %{two} = %{three}\" % {one: 1, two: 2, three: 1 + 2} # => \"sum: 1 + 2 = 3\"\n  # \"I have %<apples>s apples\" % {apples: 4}                           # => \"I have 4 apples\"\n  # ```\n  def %(other) : String\n    sprintf self, other\n  end\n\n  # See `Object#hash(hasher)`\n  def hash(hasher)\n    hasher.string(self)\n  end\n\n  # Returns the number of unicode codepoints in this string.\n  #\n  # ```\n  # \"hello\".size # => 5\n  # \"你好\".size    # => 2\n  # ```\n  def size : Int32\n    if @length > 0 || @bytesize == 0\n      return @length\n    end\n\n    @length = each_byte_index_and_char_index { }\n  end\n\n  # Returns `true` if this String is comprised in its entirety\n  # by ASCII characters.\n  #\n  # ```\n  # \"hello\".ascii_only? # => true\n  # \"你好\".ascii_only?    # => false\n  # ```\n  def ascii_only? : Bool\n    if @bytesize == size\n      each_byte do |byte|\n        return false unless byte < 0x80\n      end\n      true\n    else\n      false\n    end\n  end\n\n  # :nodoc:\n  def single_byte_optimizable? : Bool\n    @bytesize == size\n  end\n\n  # Returns `true` if this String is encoded correctly\n  # according to the UTF-8 encoding.\n  def valid_encoding? : Bool\n    Unicode.valid?(to_slice)\n  end\n\n  # Returns a String where bytes that are invalid in the\n  # UTF-8 encoding are replaced with *replacement*.\n  def scrub(replacement = Char::REPLACEMENT) : String\n    # If the string is valid we have a chance of returning self\n    # to avoid creating a new string\n    result = nil\n\n    reader = Char::Reader.new(self)\n    while reader.has_next?\n      if reader.error\n        unless result\n          result = String::Builder.new(bytesize)\n          result.write(to_slice[0, reader.pos])\n        end\n        result << replacement\n      else\n        result << reader.current_char if result\n      end\n      reader.next_char\n    end\n\n    result ? result.to_s : self\n  end\n\n  protected def char_bytesize_at(byte_index)\n    String.char_bytesize_at(to_unsafe + byte_index)\n  end\n\n  protected def self.char_bytesize_at(bytes : Pointer(UInt8))\n    first = bytes.value\n\n    if first < 0x80\n      return 1\n    end\n\n    if first < 0xc2\n      return 1 # Invalid\n    end\n\n    second = bytes[1]\n\n    if (second & 0xc0) != 0x80\n      return 1 # Invalid\n    end\n\n    if first < 0xe0\n      return 2\n    end\n\n    third = bytes[2]\n\n    if (third & 0xc0) != 0x80\n      return 1 # Invalid\n    end\n\n    if first < 0xf0\n      if first == 0xe0 && second < 0xa0\n        return 1 # Invalid\n      end\n\n      if first == 0xed && second >= 0xa0\n        return 1 # Invalid\n      end\n\n      return 3\n    end\n\n    if first == 0xf0 && second < 0x90\n      return 1 # Invalid\n    end\n\n    if first == 0xf4 && second >= 0x90\n      return 1 # Invalid\n    end\n\n    fourth = bytes[3]\n\n    if (fourth & 0xc0) != 0x80\n      return 1 # Invalid\n    end\n\n    if first < 0xf5\n      return 4\n    end\n\n    1 # Invalid\n  end\n\n  # :nodoc:\n  def size_known? : Bool\n    @bytesize == 0 || @length > 0\n  end\n\n  protected def each_byte_index_and_char_index(&)\n    byte_index = 0\n    char_index = 0\n\n    while byte_index < bytesize\n      yield byte_index, char_index\n      byte_index += char_bytesize_at(byte_index)\n      char_index += 1\n    end\n\n    char_index\n  end\n\n  # Returns `self`.\n  def clone : String\n    self\n  end\n\n  # :ditto:\n  def dup : String\n    self\n  end\n\n  # :ditto:\n  def to_s : String\n    self\n  end\n\n  # Appends `self` to *io*.\n  def to_s(io : IO) : Nil\n    io.write_string(to_slice)\n  end\n\n  # Returns the underlying bytes of this String.\n  #\n  # The returned slice is read-only.\n  #\n  # May contain invalid UTF-8 byte sequences; `#scrub` may be used to first\n  # obtain a `String` that is guaranteed to be valid UTF-8.\n  def to_slice : Bytes\n    Slice.new(to_unsafe, bytesize, read_only: true)\n  end\n\n  # Returns a pointer to the underlying bytes of this String.\n  #\n  # May contain invalid UTF-8 byte sequences; `#scrub` may be used to first\n  # obtain a `String` that is guaranteed to be valid UTF-8.\n  def to_unsafe : UInt8*\n    pointerof(@c)\n  end\n\n  # Returns *count* of underlying bytes of this String starting at given *byte_offset*.\n  #\n  # The returned slice is read-only.\n  def unsafe_byte_slice(byte_offset, count) : Slice\n    Slice.new(to_unsafe + byte_offset, count, read_only: true)\n  end\n\n  # Returns the underlying bytes of this String starting at given *byte_offset*.\n  #\n  # The returned slice is read-only.\n  def unsafe_byte_slice(byte_offset) : Slice\n    Slice.new(to_unsafe + byte_offset, bytesize - byte_offset, read_only: true)\n  end\n\n  protected def unsafe_byte_slice_string(byte_offset, *, size = 0)\n    String.new(to_unsafe + byte_offset, bytesize - byte_offset, size)\n  end\n\n  protected def unsafe_byte_slice_string(byte_offset, count, size = 0)\n    String.new(to_unsafe + byte_offset, count, size)\n  end\n\n  protected def self.char_bytes_and_bytesize(char : Char)\n    bytes = uninitialized UInt8[4]\n\n    bytesize = 0\n    char.each_byte do |byte|\n      bytes[bytesize] = byte\n      bytesize += 1\n    end\n\n    {bytes, bytesize}\n  end\n\n  # Raises an `ArgumentError` if `self` has null bytes. Returns `self` otherwise.\n  #\n  # This method should sometimes be called before passing a `String` to a C function.\n  def check_no_null_byte(name = nil) : self\n    if byte_index(0)\n      name = \"`#{name}` \" if name\n      raise ArgumentError.new(\"String #{name}contains null byte\")\n    end\n    self\n  end\n\n  # Returns `self` if it starts with the given *prefix*. Otherwise, returns a new\n  # `String` with the *prefix* prepended.\n  #\n  # ```\n  # \"llo!\".ensure_prefix(\"He\")   # => \"Hello!\"\n  # \"Hello!\".ensure_prefix(\"He\") # => \"Hello!\"\n  # \"ello\".ensure_prefix('H')    # => \"Hello!\"\n  # \"Hello!\".ensure_prefix('H')  # => \"Hello!\"\n  # ```\n  def ensure_prefix(prefix : String | Char) : self\n    return self if starts_with?(prefix)\n\n    \"#{prefix}#{self}\"\n  end\n\n  # Returns `self` if it ends with the given *suffix*. Otherwise, returns a new\n  # `String` with the *suffix* appended.\n  #\n  # ```\n  # \"Hell\".ensure_suffix(\"o!\")   # => \"Hello!\"\n  # \"Hello!\".ensure_suffix(\"o!\") # => \"Hello!\"\n  # \"Hello\".ensure_suffix('!')   # => \"Hello!\"\n  # \"Hello!\".ensure_suffix('!')  # => \"Hello!\"\n  # ```\n  def ensure_suffix(suffix : String | Char) : self\n    return self if ends_with?(suffix)\n\n    \"#{self}#{suffix}\"\n  end\n\n  # :nodoc:\n  def self.check_capacity_in_bounds(capacity) : Nil\n    if capacity < 0\n      raise ArgumentError.new(\"Negative capacity\")\n    end\n\n    if capacity.to_u64 > (UInt32::MAX - HEADER_SIZE - 1)\n      raise ArgumentError.new(\"Capacity too big\")\n    end\n  end\n\n  private class CharIterator\n    include Iterator(Char)\n\n    @reader : Char::Reader\n    @end : Bool\n\n    def initialize(@reader, @end = false)\n      check_empty\n    end\n\n    def next\n      return stop if @end\n\n      value = @reader.current_char\n      @reader.next_char\n      @end = true unless @reader.has_next?\n\n      value\n    end\n\n    private def check_empty\n      @end = true if @reader.string.bytesize == 0\n    end\n  end\n\n  private class LineIterator\n    include Iterator(String)\n\n    def initialize(@string : String, @chomp : Bool, @remove_empty : Bool)\n      @offset = 0\n      @end = false\n    end\n\n    def next\n      return stop if @end\n\n      byte_index = @string.byte_index('\\n'.ord.to_u8, @offset)\n      if byte_index\n        count = byte_index - @offset + 1\n\n        if @remove_empty && (byte_index == @offset || (byte_index == @offset + 1 && @string.to_unsafe[@offset] === '\\r'))\n          @offset = byte_index + 1\n          return self.next\n        end\n\n        if @chomp\n          count -= 1\n          if @offset + count > 0 && @string.to_unsafe[@offset + count - 1] === '\\r'\n            count -= 1\n          end\n        end\n\n        value = @string.unsafe_byte_slice_string(@offset, count)\n        @offset = byte_index + 1\n      else\n        if @offset == @string.bytesize\n          value = stop\n        else\n          value = @string.unsafe_byte_slice_string(@offset)\n        end\n        @end = true\n      end\n\n      value\n    end\n  end\n\n  # Implementation of string interpolation of a single string.\n  #\n  # For example, this code will end up invoking this method:\n  #\n  # ```\n  # value = \"hello\"\n  # \"#{value}\" # same as String.interpolation(value)\n  # ```\n  #\n  # In this case the implementation just returns the same string.\n  #\n  # NOTE: there should never be a need to call this method instead of using string interpolation.\n  def self.interpolation(value : String) : String\n    value\n  end\n\n  # Implementation of string interpolation of a single non-string value.\n  #\n  # For example, this code will end up invoking this method:\n  #\n  # ```\n  # value = 123\n  # \"#{value}\" # same as String.interpolation(value)\n  # ```\n  #\n  # In this case the implementation just returns the result of calling `value.to_s`.\n  #\n  # NOTE: there should never be a need to call this method instead of using string interpolation.\n  def self.interpolation(value) : String\n    value.to_s\n  end\n\n  # Implementation of string interpolation of a string and a char.\n  #\n  # For example, this code will end up invoking this method:\n  #\n  # ```\n  # char = '!'\n  # \"hello#{char}\" # same as String.interpolation(\"hello\", char)\n  # ```\n  #\n  # In this case the implementation just does `value + char`.\n  #\n  # NOTE: there should never be a need to call this method instead of using string interpolation.\n  def self.interpolation(value : String, char : Char) : String\n    value + char\n  end\n\n  # Implementation of string interpolation of a char and a string.\n  #\n  # For example, this code will end up invoking this method:\n  #\n  # ```\n  # char = '!'\n  # \"#{char}hello\" # same as String.interpolation(char, \"hello\")\n  # ```\n  #\n  # In this case the implementation just does `char + value`.\n  #\n  # NOTE: there should never be a need to call this method instead of using string interpolation.\n  def self.interpolation(char : Char, value : String) : String\n    char + value\n  end\n\n  # Implementation of string interpolation of multiple string values.\n  #\n  # For example, this code will end up invoking this method:\n  #\n  # ```\n  # value1 = \"hello\"\n  # value2 = \"world\"\n  # \"#{value1} #{value2}!\" # same as String.interpolation(value1, \" \", value2, \"!\")\n  # ```\n  #\n  # In this case the implementation can pre-compute the needed string bytesize and so\n  # it's a bit more performant than interpolating non-string values.\n  #\n  # NOTE: there should never be a need to call this method instead of using string interpolation.\n  def self.interpolation(*values : String) : String\n    bytesize = values.sum(&.bytesize)\n    size = if values.all?(&.size_known?)\n             values.sum(&.size)\n           else\n             0\n           end\n    String.new(bytesize) do |buffer|\n      values.each do |value|\n        buffer.copy_from(value.to_unsafe, value.bytesize)\n        buffer += value.bytesize\n      end\n      {bytesize, size}\n    end\n  end\n\n  # Implementation of string interpolation of multiple, possibly non-string values.\n  #\n  # For example, this code will end up invoking this method:\n  #\n  # ```\n  # value1 = \"hello\"\n  # value2 = 123\n  # \"#{value1} #{value2}!\" # same as String.interpolation(value1, \" \", value2, \"!\")\n  # ```\n  #\n  # In this case the implementation will call `String.build` with the given values.\n  #\n  # NOTE: there should never be a need to call this method instead of using string interpolation.\n  def self.interpolation(*values : *T) : String forall T\n    capacity = 0\n    {% for i in 0...T.size %}\n      value{{i}} = values[{{i}}]\n      if value{{i}}.is_a?(String)\n        capacity += value{{i}}.bytesize\n      else\n        capacity += 15\n      end\n    {% end %}\n    String.build(capacity) do |io|\n      {% for i in 0...T.size %}\n        if value{{i}}.is_a?(String)\n          io.write(value{{i}}.to_slice)\n        else\n          io << value{{i}}\n        end\n      {% end %}\n    end\n  end\n\n  # Returns the empty string.\n  def self.additive_identity : String\n    \"\"\n  end\nend\n\nrequire \"./string/*\"\n"
  }
]